summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-net-cdc_ncm19
-rw-r--r--Documentation/ABI/testing/sysfs-class-net-mesh4
-rw-r--r--Documentation/ABI/testing/sysfs-class-net-qmi23
-rw-r--r--Documentation/devicetree/bindings/net/dsa/dsa.txt3
-rw-r--r--Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt7
-rw-r--r--Documentation/devicetree/bindings/net/ieee802154/adf7242.txt18
-rw-r--r--Documentation/devicetree/bindings/net/macb.txt8
-rw-r--r--Documentation/devicetree/bindings/net/micrel-ksz90x1.txt17
-rw-r--r--Documentation/devicetree/bindings/net/nfc/st95hf.txt50
-rw-r--r--Documentation/devicetree/bindings/net/renesas,ravb.txt12
-rw-r--r--Documentation/devicetree/bindings/net/socfpga-dwmac.txt2
-rw-r--r--Documentation/devicetree/bindings/net/stmmac.txt25
-rw-r--r--Documentation/fault-injection/notifier-error-inject.txt25
-rw-r--r--Documentation/networking/batman-adv.txt9
-rw-r--r--Documentation/networking/ip-sysctl.txt31
-rw-r--r--Documentation/networking/switchdev.txt8
-rw-r--r--MAINTAINERS87
-rw-r--r--arch/alpha/include/uapi/asm/socket.h3
-rw-r--r--arch/arm64/net/bpf_jit_comp.c6
-rw-r--r--arch/avr32/include/uapi/asm/socket.h3
-rw-r--r--arch/frv/include/uapi/asm/socket.h3
-rw-r--r--arch/ia64/include/uapi/asm/socket.h3
-rw-r--r--arch/m32r/include/uapi/asm/socket.h3
-rw-r--r--arch/mips/bcm47xx/setup.c39
-rw-r--r--arch/mips/include/uapi/asm/socket.h3
-rw-r--r--arch/mn10300/include/uapi/asm/socket.h3
-rw-r--r--arch/parisc/include/uapi/asm/socket.h3
-rw-r--r--arch/powerpc/include/asm/hvcall.h20
-rw-r--r--arch/powerpc/include/uapi/asm/socket.h3
-rw-r--r--arch/powerpc/platforms/82xx/ep8248e.c10
-rw-r--r--arch/powerpc/platforms/pasemi/gpio_mdio.c3
-rw-r--r--arch/s390/include/uapi/asm/socket.h3
-rw-r--r--arch/s390/net/bpf_jit_comp.c13
-rw-r--r--arch/sparc/include/uapi/asm/socket.h3
-rw-r--r--arch/sparc/kernel/idprom.c7
-rw-r--r--arch/x86/net/bpf_jit_comp.c40
-rw-r--r--arch/xtensa/include/uapi/asm/socket.h3
-rw-r--r--crypto/algif_aead.c4
-rw-r--r--crypto/algif_skcipher.c4
-rw-r--r--drivers/atm/solos-pci.c34
-rw-r--r--drivers/bcma/main.c29
-rw-r--r--drivers/bluetooth/bcm203x.c4
-rw-r--r--drivers/bluetooth/bfusb.c15
-rw-r--r--drivers/bluetooth/bluecard_cs.c25
-rw-r--r--drivers/bluetooth/bpa10x.c4
-rw-r--r--drivers/bluetooth/bt3c_cs.c11
-rw-r--r--drivers/bluetooth/btintel.c129
-rw-r--r--drivers/bluetooth/btintel.h19
-rw-r--r--drivers/bluetooth/btmrvl_drv.h1
-rw-r--r--drivers/bluetooth/btmrvl_main.c21
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c10
-rw-r--r--drivers/bluetooth/btsdio.c6
-rw-r--r--drivers/bluetooth/btuart_cs.c11
-rw-r--r--drivers/bluetooth/btusb.c186
-rw-r--r--drivers/bluetooth/btwilink.c8
-rw-r--r--drivers/bluetooth/dtl1_cs.c11
-rw-r--r--drivers/bluetooth/hci_ath.c6
-rw-r--r--drivers/bluetooth/hci_bcm.c10
-rw-r--r--drivers/bluetooth/hci_bcsp.c25
-rw-r--r--drivers/bluetooth/hci_h4.c16
-rw-r--r--drivers/bluetooth/hci_h5.c28
-rw-r--r--drivers/bluetooth/hci_intel.c66
-rw-r--r--drivers/bluetooth/hci_ldisc.c13
-rw-r--r--drivers/bluetooth/hci_ll.c4
-rw-r--r--drivers/bluetooth/hci_qca.c4
-rw-r--r--drivers/bluetooth/hci_vhci.c8
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c17
-rw-r--r--drivers/infiniband/hw/mlx5/main.c463
-rw-r--r--drivers/infiniband/hw/mlx5/mlx5_ib.h45
-rw-r--r--drivers/isdn/Makefile1
-rw-r--r--drivers/isdn/act2000/module.c2
-rw-r--r--drivers/isdn/i4l/Kconfig2
-rw-r--r--drivers/isdn/sc/Kconfig8
-rw-r--r--drivers/isdn/sc/Makefile10
-rw-r--r--drivers/isdn/sc/card.h131
-rw-r--r--drivers/isdn/sc/command.c363
-rw-r--r--drivers/isdn/sc/event.c68
-rw-r--r--drivers/isdn/sc/hardware.h110
-rw-r--r--drivers/isdn/sc/includes.h16
-rw-r--r--drivers/isdn/sc/init.c549
-rw-r--r--drivers/isdn/sc/interrupt.c247
-rw-r--r--drivers/isdn/sc/ioctl.c582
-rw-r--r--drivers/isdn/sc/message.c230
-rw-r--r--drivers/isdn/sc/message.h245
-rw-r--r--drivers/isdn/sc/packet.c204
-rw-r--r--drivers/isdn/sc/scioc.h110
-rw-r--r--drivers/isdn/sc/shmem.c138
-rw-r--r--drivers/isdn/sc/timer.c122
-rw-r--r--drivers/net/bonding/bond_3ad.c11
-rw-r--r--drivers/net/bonding/bond_main.c123
-rw-r--r--drivers/net/bonding/bond_sysfs.c3
-rw-r--r--drivers/net/can/xilinx_can.c176
-rw-r--r--drivers/net/dsa/mv88e6xxx.c240
-rw-r--r--drivers/net/dsa/mv88e6xxx.h8
-rw-r--r--drivers/net/ethernet/3com/3c509.c2
-rw-r--r--drivers/net/ethernet/3com/3c59x.c2
-rw-r--r--drivers/net/ethernet/8390/ax88796.c17
-rw-r--r--drivers/net/ethernet/Kconfig1
-rw-r--r--drivers/net/ethernet/Makefile1
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.c49
-rw-r--r--drivers/net/ethernet/aeroflex/greth.c5
-rw-r--r--drivers/net/ethernet/aeroflex/greth.h1
-rw-r--r--drivers/net/ethernet/agere/et131x.c49
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c15
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.c25
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c1
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_hw.c4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_hw.h4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_main.c33
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_main.h4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h4
-rw-r--r--drivers/net/ethernet/arc/Kconfig4
-rw-r--r--drivers/net/ethernet/arc/emac_rockchip.c88
-rw-r--r--drivers/net/ethernet/atheros/alx/hw.c10
-rw-r--r--drivers/net/ethernet/atheros/alx/hw.h9
-rw-r--r--drivers/net/ethernet/atheros/alx/main.c8
-rw-r--r--drivers/net/ethernet/broadcom/b44.c21
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.c40
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c2
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c15
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h115
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c109
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h7
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c124
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h43
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c3
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c730
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h28
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c110
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h865
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c62
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c4
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c14
-rw-r--r--drivers/net/ethernet/broadcom/sb1250-mac.c22
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c46
-rw-r--r--drivers/net/ethernet/broadcom/tg3.h1
-rw-r--r--drivers/net/ethernet/cadence/macb.c67
-rw-r--r--drivers/net/ethernet/cadence/macb.h2
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic.h6
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic_main.c11
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_main.c15
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.c211
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.h12
-rw-r--r--drivers/net/ethernet/cavium/thunder/q_struct.h30
-rw-r--r--drivers/net/ethernet/chelsio/Kconfig17
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cphy.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88x201x.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/my3126.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/pm3393.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/vsc7326.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/ael1002.c12
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/aq100x.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/common.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c14
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c28
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/vsc8211.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c11
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h19
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c144
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c148
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c232
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.c179
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.h10
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c187
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c162
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h31
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_values.h3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c11
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c11
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c2
-rw-r--r--drivers/net/ethernet/dec/tulip/de4x5.c2
-rw-r--r--drivers/net/ethernet/dlink/dl2k.c314
-rw-r--r--drivers/net/ethernet/dnet.c28
-rw-r--r--drivers/net/ethernet/emulex/benet/be.h13
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c756
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h178
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c88
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c592
-rw-r--r--drivers/net/ethernet/ethoc.c18
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.c23
-rw-r--r--drivers/net/ethernet/freescale/Kconfig1
-rw-r--r--drivers/net/ethernet/freescale/Makefile2
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c36
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx.c22
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx_phy.c4
-rw-r--r--drivers/net/ethernet/freescale/fman/Kconfig9
-rw-r--r--drivers/net/ethernet/freescale/fman/Makefile7
-rw-r--r--drivers/net/ethernet/freescale/fman/fman.c2871
-rw-r--r--drivers/net/ethernet/freescale/fman/fman.h325
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.c1453
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.h59
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_mac.h278
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.c1170
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.h60
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_muram.c158
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_muram.h51
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_port.c1778
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_port.h151
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_sp.c166
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_sp.h103
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_tgec.c786
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_tgec.h55
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.c977
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.h97
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c2
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fec.c4
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c10
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-fec.c10
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.c2
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c13
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hnae.h63
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c99
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c14
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h4
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c213
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h24
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c72
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h32
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c75
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h8
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h188
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.c553
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.h12
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_ethtool.c201
-rw-r--r--drivers/net/ethernet/hisilicon/hns_mdio.c5
-rw-r--r--drivers/net/ethernet/hp/hp100.c2
-rw-r--r--drivers/net/ethernet/ibm/Kconfig10
-rw-r--r--drivers/net/ethernet/ibm/Makefile1
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c9
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.c3585
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.h1046
-rw-r--r--drivers/net/ethernet/intel/Kconfig10
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000.h7
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.c216
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c133
-rw-r--r--drivers/net/ethernet/intel/e1000e/defines.h3
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h2
-rw-r--r--drivers/net/ethernet/intel/e1000e/hw.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c45
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c92
-rw-r--r--drivers/net/ethernet/intel/fm10k/Makefile20
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k.h23
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c4
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c4
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c30
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_main.c80
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_mbx.c57
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_mbx.h8
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_netdev.c75
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pci.c211
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.c151
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.h20
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_tlv.c20
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_tlv.h11
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_type.h43
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_vf.c81
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_vf.h2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h46
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h23
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c28
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_devids.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c108
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_fcoe.c2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c1004
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c110
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.h3
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c100
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h23
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_common.c1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_devids.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.c226
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.h18
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf.h19
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c79
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_main.c435
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c86
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c21
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_defines.h6
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_hw.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.c32
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.h3
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c229
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.h16
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_regs.h4
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h2
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c38
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c26
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h50
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c8
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c13
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c250
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.h2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c7
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c6
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c70
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c19
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c553
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c3
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c720
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c237
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type.h17
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c16
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c220
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h3
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c104
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.c4
-rw-r--r--drivers/net/ethernet/jme.c2
-rw-r--r--drivers/net/ethernet/lantiq_etop.c30
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c21
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c10
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c429
-rw-r--r--drivers/net/ethernet/marvell/sky2.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_cq.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c40
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c107
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h126
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/port.c598
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c193
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Kconfig1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Makefile8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en.h57
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_clock.c287
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c907
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_fs.c1224
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c120
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tx.c85
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eq.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c1097
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h147
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/flow_table.c422
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c289
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h72
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.c1514
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.h169
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c119
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sriov.c233
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/vport.c435
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Kconfig10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c85
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.h34
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c372
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/reg.h790
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c1210
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h154
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c765
-rw-r--r--drivers/net/ethernet/microchip/encx24j600.c24
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c3
-rw-r--r--drivers/net/ethernet/netronome/Kconfig36
-rw-r--r--drivers/net/ethernet/netronome/Makefile5
-rw-r--r--drivers/net/ethernet/netronome/nfp/Makefile8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net.h748
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c2435
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h323
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c235
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c640
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c385
-rw-r--r--drivers/net/ethernet/nxp/lpc_eth.c23
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_hsi.h6
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_main.c18
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mcp.c27
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mcp.h13
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede.h5
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ethtool.c211
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_main.c7
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h2
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c4
-rw-r--r--drivers/net/ethernet/rdc/r6040.c20
-rw-r--r--drivers/net/ethernet/realtek/r8169.c29
-rw-r--r--drivers/net/ethernet/renesas/ravb.h4
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c115
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c400
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.h1
-rw-r--r--drivers/net/ethernet/rocker/rocker.c2
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c4
-rw-r--r--drivers/net/ethernet/sfc/ef10.c96
-rw-r--r--drivers/net/ethernet/sfc/efx.c18
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c250
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h10
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h5
-rw-r--r--drivers/net/ethernet/sfc/rx.c1
-rw-r--r--drivers/net/ethernet/sfc/tx.c8
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c21
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.c23
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h28
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c24
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h42
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c75
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c30
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c23
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c46
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c2
-rw-r--r--drivers/net/ethernet/synopsys/dwc_eth_qos.c29
-rw-r--r--drivers/net/ethernet/ti/cpmac.c5
-rw-r--r--drivers/net/ethernet/ti/cpsw.c9
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c5
-rw-r--r--drivers/net/ethernet/ti/davinci_mdio.c4
-rw-r--r--drivers/net/ethernet/ti/netcp.h2
-rw-r--r--drivers/net/ethernet/ti/netcp_core.c134
-rw-r--r--drivers/net/ethernet/ti/netcp_ethss.c4
-rw-r--r--drivers/net/ethernet/tile/tilepro.c3
-rw-r--r--drivers/net/ethernet/toshiba/tc35815.c40
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac.h1
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_mdio.c2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet.h2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c6
-rw-r--r--drivers/net/geneve.c153
-rw-r--r--drivers/net/hyperv/hyperv_net.h62
-rw-r--r--drivers/net/hyperv/netvsc.c111
-rw-r--r--drivers/net/hyperv/netvsc_drv.c235
-rw-r--r--drivers/net/hyperv/rndis_filter.c66
-rw-r--r--drivers/net/ieee802154/Kconfig11
-rw-r--r--drivers/net/ieee802154/Makefile1
-rw-r--r--drivers/net/ieee802154/adf7242.c1285
-rw-r--r--drivers/net/ieee802154/atusb.c3
-rw-r--r--drivers/net/ieee802154/cc2520.c145
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c2
-rw-r--r--drivers/net/irda/toim3232-sir.c10
-rw-r--r--drivers/net/loopback.c2
-rw-r--r--drivers/net/macvlan.c4
-rw-r--r--drivers/net/macvtap.c2
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/amd.c1
-rw-r--r--drivers/net/phy/aquantia.c4
-rw-r--r--drivers/net/phy/at803x.c15
-rw-r--r--drivers/net/phy/bcm-phy-lib.c8
-rw-r--r--drivers/net/phy/bcm63xx.c2
-rw-r--r--drivers/net/phy/bcm7xxx.c20
-rw-r--r--drivers/net/phy/bcm87xx.c11
-rw-r--r--drivers/net/phy/broadcom.c12
-rw-r--r--drivers/net/phy/cicada.c2
-rw-r--r--drivers/net/phy/davicom.c4
-rw-r--r--drivers/net/phy/dp83640.c25
-rw-r--r--drivers/net/phy/dp83848.c2
-rw-r--r--drivers/net/phy/dp83867.c17
-rw-r--r--drivers/net/phy/et1011c.c1
-rw-r--r--drivers/net/phy/fixed_phy.c14
-rw-r--r--drivers/net/phy/icplus.c21
-rw-r--r--drivers/net/phy/lxt.c4
-rw-r--r--drivers/net/phy/marvell.c153
-rw-r--r--drivers/net/phy/mdio-bcm-unimac.c11
-rw-r--r--drivers/net/phy/mdio-gpio.c2
-rw-r--r--drivers/net/phy/mdio-moxart.c7
-rw-r--r--drivers/net/phy/mdio-mux.c3
-rw-r--r--drivers/net/phy/mdio-octeon.c14
-rw-r--r--drivers/net/phy/mdio-sun4i.c12
-rw-r--r--drivers/net/phy/mdio_bus.c294
-rw-r--r--drivers/net/phy/mdio_device.c171
-rw-r--r--drivers/net/phy/micrel.c149
-rw-r--r--drivers/net/phy/microchip.c11
-rw-r--r--drivers/net/phy/national.c1
-rw-r--r--drivers/net/phy/phy.c62
-rw-r--r--drivers/net/phy/phy_device.c336
-rw-r--r--drivers/net/phy/qsemi.c1
-rw-r--r--drivers/net/phy/realtek.c5
-rw-r--r--drivers/net/phy/smsc.c32
-rw-r--r--drivers/net/phy/ste10Xp.c2
-rw-r--r--drivers/net/phy/teranetics.c1
-rw-r--r--drivers/net/phy/vitesse.c8
-rw-r--r--drivers/net/plip/plip.c36
-rw-r--r--drivers/net/ppp/ppp_generic.c9
-rw-r--r--drivers/net/ppp/pppoe.c28
-rw-r--r--drivers/net/ppp/pppox.c5
-rw-r--r--drivers/net/ppp/pptp.c1
-rw-r--r--drivers/net/team/team.c69
-rw-r--r--drivers/net/team/team_mode_activebackup.c1
-rw-r--r--drivers/net/team/team_mode_broadcast.c1
-rw-r--r--drivers/net/team/team_mode_loadbalance.c1
-rw-r--r--drivers/net/team/team_mode_random.c1
-rw-r--r--drivers/net/team/team_mode_roundrobin.c1
-rw-r--r--drivers/net/tun.c3
-rw-r--r--drivers/net/usb/asix_common.c2
-rw-r--r--drivers/net/usb/ax88172a.c14
-rw-r--r--drivers/net/usb/cdc_ether.c8
-rw-r--r--drivers/net/usb/cdc_ncm.c43
-rw-r--r--drivers/net/usb/lan78xx.c66
-rw-r--r--drivers/net/usb/qmi_wwan.c152
-rw-r--r--drivers/net/usb/r8152.c134
-rw-r--r--drivers/net/usb/usbnet.c5
-rw-r--r--drivers/net/virtio_net.c3
-rw-r--r--drivers/net/vrf.c70
-rw-r--r--drivers/net/vxlan.c23
-rw-r--r--drivers/net/wan/hdlc.c21
-rw-r--r--drivers/net/wan/hdlc_cisco.c1
-rw-r--r--drivers/net/wan/hdlc_fr.c1
-rw-r--r--drivers/net/wan/hdlc_ppp.c1
-rw-r--r--drivers/net/wan/hdlc_raw.c1
-rw-r--r--drivers/net/wan/hdlc_raw_eth.c1
-rw-r--r--drivers/net/wan/hdlc_x25.c1
-rw-r--r--drivers/net/wan/wanxl.c1
-rw-r--r--drivers/net/wireless/Kconfig238
-rw-r--r--drivers/net/wireless/Makefile65
-rw-r--r--drivers/net/wireless/admtek/Kconfig41
-rw-r--r--drivers/net/wireless/admtek/Makefile1
-rw-r--r--drivers/net/wireless/admtek/adm8211.c (renamed from drivers/net/wireless/adm8211.c)0
-rw-r--r--drivers/net/wireless/admtek/adm8211.h (renamed from drivers/net/wireless/adm8211.h)0
-rw-r--r--drivers/net/wireless/airo.c8236
-rw-r--r--drivers/net/wireless/ath/Kconfig17
-rw-r--r--drivers/net/wireless/ath/ath10k/Kconfig1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c16
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h38
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c199
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h4
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c33
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c130
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c210
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.h3
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c49
-rw-r--r--drivers/net/wireless/ath/ath10k/thermal.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c15
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c19
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c208
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h130
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c4
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_mbox.c5
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig11
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h23
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c56
-rw-r--r--drivers/net/wireless/ath/ath9k/common-beacon.c22
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.c74
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_4k.c76
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_9287.c68
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_def.c61
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_hst.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c11
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/rng.c107
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c23
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.c43
-rw-r--r--drivers/net/wireless/ath/wcn36xx/hal.h2
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.c27
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.h9
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c24
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c30
-rw-r--r--drivers/net/wireless/ath/wil6210/rx_reorder.c12
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c5
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h1
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_crash_dump.c3
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform.c3
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform.h38
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c4
-rw-r--r--drivers/net/wireless/atmel/Kconfig57
-rw-r--r--drivers/net/wireless/atmel/Makefile5
-rw-r--r--drivers/net/wireless/atmel/at76c50x-usb.c (renamed from drivers/net/wireless/at76c50x-usb.c)0
-rw-r--r--drivers/net/wireless/atmel/at76c50x-usb.h (renamed from drivers/net/wireless/at76c50x-usb.h)0
-rw-r--r--drivers/net/wireless/atmel/atmel.c (renamed from drivers/net/wireless/atmel.c)0
-rw-r--r--drivers/net/wireless/atmel/atmel.h (renamed from drivers/net/wireless/atmel.h)0
-rw-r--r--drivers/net/wireless/atmel/atmel_cs.c (renamed from drivers/net/wireless/atmel_cs.c)0
-rw-r--r--drivers/net/wireless/atmel/atmel_pci.c (renamed from drivers/net/wireless/atmel_pci.c)0
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/Makefile57
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcdc.c389
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c1380
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/btcoex.c497
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bus.h248
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c6357
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h504
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/chip.c1331
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/common.c198
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/common.h23
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/core.c1260
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/core.h221
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/debug.c111
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/debug.h139
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/feature.c172
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/feature.h94
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/firmware.c542
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/firmware.h47
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fweh.c478
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fweh.h289
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil.c420
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil.h106
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h637
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c2271
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c1561
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/p2p.c2401
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/p2p.h187
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/pcie.c2107
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio.c4376
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio.h377
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/usb.c1535
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/Makefile48
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.c774
-rw-r--r--drivers/net/wireless/brcm80211/brcmutil/Makefile23
-rw-r--r--drivers/net/wireless/brcm80211/include/brcm_hw_ids.h93
-rw-r--r--drivers/net/wireless/brcm80211/include/brcmu_wifi.h274
-rw-r--r--drivers/net/wireless/broadcom/Kconfig18
-rw-r--r--drivers/net/wireless/broadcom/Makefile5
-rw-r--r--drivers/net/wireless/broadcom/b43/Kconfig (renamed from drivers/net/wireless/b43/Kconfig)0
-rw-r--r--drivers/net/wireless/broadcom/b43/Makefile (renamed from drivers/net/wireless/b43/Makefile)0
-rw-r--r--drivers/net/wireless/broadcom/b43/b43.h (renamed from drivers/net/wireless/b43/b43.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/bus.c (renamed from drivers/net/wireless/b43/bus.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/bus.h (renamed from drivers/net/wireless/b43/bus.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/debugfs.c (renamed from drivers/net/wireless/b43/debugfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/debugfs.h (renamed from drivers/net/wireless/b43/debugfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/dma.c (renamed from drivers/net/wireless/b43/dma.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/dma.h (renamed from drivers/net/wireless/b43/dma.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/leds.c (renamed from drivers/net/wireless/b43/leds.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/leds.h (renamed from drivers/net/wireless/b43/leds.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/lo.c (renamed from drivers/net/wireless/b43/lo.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/lo.h (renamed from drivers/net/wireless/b43/lo.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/main.c (renamed from drivers/net/wireless/b43/main.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/main.h (renamed from drivers/net/wireless/b43/main.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_a.c (renamed from drivers/net/wireless/b43/phy_a.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_a.h (renamed from drivers/net/wireless/b43/phy_a.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ac.c (renamed from drivers/net/wireless/b43/phy_ac.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ac.h (renamed from drivers/net/wireless/b43/phy_ac.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_common.c (renamed from drivers/net/wireless/b43/phy_common.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_common.h (renamed from drivers/net/wireless/b43/phy_common.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_g.c (renamed from drivers/net/wireless/b43/phy_g.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_g.h (renamed from drivers/net/wireless/b43/phy_g.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ht.c (renamed from drivers/net/wireless/b43/phy_ht.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ht.h (renamed from drivers/net/wireless/b43/phy_ht.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lcn.c (renamed from drivers/net/wireless/b43/phy_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lcn.h (renamed from drivers/net/wireless/b43/phy_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lp.c (renamed from drivers/net/wireless/b43/phy_lp.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lp.h (renamed from drivers/net/wireless/b43/phy_lp.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_n.c (renamed from drivers/net/wireless/b43/phy_n.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_n.h (renamed from drivers/net/wireless/b43/phy_n.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/pio.c (renamed from drivers/net/wireless/b43/pio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/pio.h (renamed from drivers/net/wireless/b43/pio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/ppr.c (renamed from drivers/net/wireless/b43/ppr.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/ppr.h (renamed from drivers/net/wireless/b43/ppr.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2055.c (renamed from drivers/net/wireless/b43/radio_2055.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2055.h (renamed from drivers/net/wireless/b43/radio_2055.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2056.c (renamed from drivers/net/wireless/b43/radio_2056.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2056.h (renamed from drivers/net/wireless/b43/radio_2056.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2057.c (renamed from drivers/net/wireless/b43/radio_2057.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2057.h (renamed from drivers/net/wireless/b43/radio_2057.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2059.c (renamed from drivers/net/wireless/b43/radio_2059.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2059.h (renamed from drivers/net/wireless/b43/radio_2059.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/rfkill.c (renamed from drivers/net/wireless/b43/rfkill.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/rfkill.h (renamed from drivers/net/wireless/b43/rfkill.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sdio.c (renamed from drivers/net/wireless/b43/sdio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sdio.h (renamed from drivers/net/wireless/b43/sdio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sysfs.c (renamed from drivers/net/wireless/b43/sysfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sysfs.h (renamed from drivers/net/wireless/b43/sysfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables.c (renamed from drivers/net/wireless/b43/tables.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables.h (renamed from drivers/net/wireless/b43/tables.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_lpphy.c (renamed from drivers/net/wireless/b43/tables_lpphy.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_lpphy.h (renamed from drivers/net/wireless/b43/tables_lpphy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_nphy.c (renamed from drivers/net/wireless/b43/tables_nphy.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_nphy.h (renamed from drivers/net/wireless/b43/tables_nphy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_ht.c (renamed from drivers/net/wireless/b43/tables_phy_ht.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_ht.h (renamed from drivers/net/wireless/b43/tables_phy_ht.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_lcn.c (renamed from drivers/net/wireless/b43/tables_phy_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_lcn.h (renamed from drivers/net/wireless/b43/tables_phy_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/wa.c (renamed from drivers/net/wireless/b43/wa.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/wa.h (renamed from drivers/net/wireless/b43/wa.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/xmit.c (renamed from drivers/net/wireless/b43/xmit.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/xmit.h (renamed from drivers/net/wireless/b43/xmit.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/Kconfig (renamed from drivers/net/wireless/b43legacy/Kconfig)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/Makefile (renamed from drivers/net/wireless/b43legacy/Makefile)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/b43legacy.h (renamed from drivers/net/wireless/b43legacy/b43legacy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/debugfs.c (renamed from drivers/net/wireless/b43legacy/debugfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/debugfs.h (renamed from drivers/net/wireless/b43legacy/debugfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/dma.c (renamed from drivers/net/wireless/b43legacy/dma.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/dma.h (renamed from drivers/net/wireless/b43legacy/dma.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/ilt.c (renamed from drivers/net/wireless/b43legacy/ilt.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/ilt.h (renamed from drivers/net/wireless/b43legacy/ilt.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/leds.c (renamed from drivers/net/wireless/b43legacy/leds.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/leds.h (renamed from drivers/net/wireless/b43legacy/leds.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/main.c (renamed from drivers/net/wireless/b43legacy/main.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/main.h (renamed from drivers/net/wireless/b43legacy/main.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/phy.c (renamed from drivers/net/wireless/b43legacy/phy.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/phy.h (renamed from drivers/net/wireless/b43legacy/phy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/pio.c (renamed from drivers/net/wireless/b43legacy/pio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/pio.h (renamed from drivers/net/wireless/b43legacy/pio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/radio.c (renamed from drivers/net/wireless/b43legacy/radio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/radio.h (renamed from drivers/net/wireless/b43legacy/radio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/rfkill.c (renamed from drivers/net/wireless/b43legacy/rfkill.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/rfkill.h (renamed from drivers/net/wireless/b43legacy/rfkill.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/sysfs.c (renamed from drivers/net/wireless/b43legacy/sysfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/sysfs.h (renamed from drivers/net/wireless/b43legacy/sysfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/xmit.c (renamed from drivers/net/wireless/b43legacy/xmit.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/xmit.h (renamed from drivers/net/wireless/b43legacy/xmit.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/Kconfig (renamed from drivers/net/wireless/brcm80211/Kconfig)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/Makefile (renamed from drivers/net/wireless/brcm80211/Makefile)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile57
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c391
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/bcdc.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c1378
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c495
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/btcoex.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h248
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c6626
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h414
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c1332
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/chip.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c252
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h79
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/commonring.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/commonring.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c1356
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h224
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c111
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h141
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c190
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h101
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c581
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h87
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/flowring.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/flowring.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c478
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h289
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c421
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h107
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h790
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c2269
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c1560
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/of.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/of.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c2394
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h185
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c2007
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/pcie.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/proto.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/proto.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c4257
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h377
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c1497
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/usb.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/vendor.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/vendor.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile48
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/aiutils.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/aiutils.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/ampdu.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/ampdu.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/antsel.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/antsel.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c774
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/channel.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/d11.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/debug.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/debug.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/dma.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/dma.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/led.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/led.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/main.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/main.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/pmu.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/pmu.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/pub.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/rate.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/rate.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/scb.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/stf.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/stf.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/types.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile23
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c (renamed from drivers/net/wireless/brcm80211/brcmutil/d11.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c (renamed from drivers/net/wireless/brcm80211/brcmutil/utils.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h97
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h (renamed from drivers/net/wireless/brcm80211/include/brcmu_d11.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h (renamed from drivers/net/wireless/brcm80211/include/brcmu_utils.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h251
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h (renamed from drivers/net/wireless/brcm80211/include/chipcommon.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/defs.h (renamed from drivers/net/wireless/brcm80211/include/defs.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/soc.h (renamed from drivers/net/wireless/brcm80211/include/soc.h)0
-rw-r--r--drivers/net/wireless/cisco/Kconfig56
-rw-r--r--drivers/net/wireless/cisco/Makefile2
-rw-r--r--drivers/net/wireless/cisco/airo.c8224
-rw-r--r--drivers/net/wireless/cisco/airo.h (renamed from drivers/net/wireless/airo.h)0
-rw-r--r--drivers/net/wireless/cisco/airo_cs.c (renamed from drivers/net/wireless/airo_cs.c)0
-rw-r--r--drivers/net/wireless/cw1200/sta.c2399
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c708
-rw-r--r--drivers/net/wireless/intel/Kconfig18
-rw-r--r--drivers/net/wireless/intel/Makefile6
-rw-r--r--drivers/net/wireless/intel/ipw2x00/Kconfig (renamed from drivers/net/wireless/ipw2x00/Kconfig)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/Makefile (renamed from drivers/net/wireless/ipw2x00/Makefile)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw.h (renamed from drivers/net/wireless/ipw2x00/ipw.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.c8646
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.h (renamed from drivers/net/wireless/ipw2x00/ipw2100.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.c (renamed from drivers/net/wireless/ipw2x00/ipw2200.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.h (renamed from drivers/net/wireless/ipw2x00/ipw2200.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw.h (renamed from drivers/net/wireless/ipw2x00/libipw.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_geo.c (renamed from drivers/net/wireless/ipw2x00/libipw_geo.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_module.c (renamed from drivers/net/wireless/ipw2x00/libipw_module.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_rx.c (renamed from drivers/net/wireless/ipw2x00/libipw_rx.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_tx.c (renamed from drivers/net/wireless/ipw2x00/libipw_tx.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_wx.c (renamed from drivers/net/wireless/ipw2x00/libipw_wx.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-debug.c (renamed from drivers/net/wireless/iwlegacy/3945-debug.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c (renamed from drivers/net/wireless/iwlegacy/3945-mac.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-rs.c (renamed from drivers/net/wireless/iwlegacy/3945-rs.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945.c (renamed from drivers/net/wireless/iwlegacy/3945.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945.h (renamed from drivers/net/wireless/iwlegacy/3945.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-calib.c (renamed from drivers/net/wireless/iwlegacy/4965-calib.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-debug.c (renamed from drivers/net/wireless/iwlegacy/4965-debug.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-mac.c6868
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-rs.c (renamed from drivers/net/wireless/iwlegacy/4965-rs.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965.c (renamed from drivers/net/wireless/iwlegacy/4965.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965.h (renamed from drivers/net/wireless/iwlegacy/4965.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/Kconfig (renamed from drivers/net/wireless/iwlegacy/Kconfig)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/Makefile (renamed from drivers/net/wireless/iwlegacy/Makefile)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/commands.h (renamed from drivers/net/wireless/iwlegacy/commands.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.c5586
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.h (renamed from drivers/net/wireless/iwlegacy/common.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/csr.h (renamed from drivers/net/wireless/iwlegacy/csr.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/debug.c (renamed from drivers/net/wireless/iwlegacy/debug.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h (renamed from drivers/net/wireless/iwlegacy/iwl-spectrum.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/prph.h (renamed from drivers/net/wireless/iwlegacy/prph.h)0
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Kconfig161
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Makefile23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/Makefile (renamed from drivers/net/wireless/iwlwifi/dvm/Makefile)0
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/agn.h476
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/calib.c1113
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/calib.h74
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/commands.h4008
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c2443
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/dev.h949
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/devices.c690
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/led.c221
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/led.h55
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/lib.c1303
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c1652
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/main.c2183
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/power.c395
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/power.h47
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rs.c3338
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rs.h426
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rx.c1026
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rxon.c1572
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/scan.c1075
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/sta.c1442
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.c685
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.h128
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tx.c1413
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/ucode.c452
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-1000.c140
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-2000.c216
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-5000.c178
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-6000.c389
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-7000.c380
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-8000.c238
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-9000.c163
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h117
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h440
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-csr.h552
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-debug.c136
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-debug.h223
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h97
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h155
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h209
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h97
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h81
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c43
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h89
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c1718
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.h155
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c947
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h144
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c464
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h70
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fh.h535
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h327
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h793
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fw.h308
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.c294
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.h73
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-modparams.h138
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c193
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h139
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c842
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h97
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h282
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c471
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h82
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-prph.h407
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-scd.h143
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.c205
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h1221
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/Makefile12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/binding.c211
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/coex.c1008
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c1315
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/constants.h140
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c2284
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c1483
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c1570
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h103
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h476
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h438
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h387
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h467
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h389
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h368
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h736
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h414
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h284
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h386
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h646
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h1854
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c817
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h174
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c1037
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/led.c136
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c1464
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c3979
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h1543
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/nvm.c934
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/offloading.c255
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c1519
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c295
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/power.c1038
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/quota.c328
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.c4015
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.h392
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rx.c624
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c458
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/scan.c1585
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sf.c340
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.c1841
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.h450
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tdls.c732
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/testmode.h97
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.c885
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.h250
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tof.c306
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tof.h94
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tt.c460
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tx.c1220
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/utils.c1083
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c723
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/internal.h582
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/rx.c1556
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans.c2717
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/tx.c2295
-rw-r--r--drivers/net/wireless/intersil/Kconfig38
-rw-r--r--drivers/net/wireless/intersil/Makefile4
-rw-r--r--drivers/net/wireless/intersil/hostap/Kconfig (renamed from drivers/net/wireless/hostap/Kconfig)0
-rw-r--r--drivers/net/wireless/intersil/hostap/Makefile (renamed from drivers/net/wireless/hostap/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap.h (renamed from drivers/net/wireless/hostap/hostap.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211.h (renamed from drivers/net/wireless/hostap/hostap_80211.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_rx.c (renamed from drivers/net/wireless/hostap/hostap_80211_rx.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_tx.c (renamed from drivers/net/wireless/hostap/hostap_80211_tx.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.c (renamed from drivers/net/wireless/hostap/hostap_ap.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.h (renamed from drivers/net/wireless/hostap/hostap_ap.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_common.h (renamed from drivers/net/wireless/hostap/hostap_common.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_config.h (renamed from drivers/net/wireless/hostap/hostap_config.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_cs.c710
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_download.c (renamed from drivers/net/wireless/hostap/hostap_download.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_hw.c (renamed from drivers/net/wireless/hostap/hostap_hw.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_info.c (renamed from drivers/net/wireless/hostap/hostap_info.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ioctl.c (renamed from drivers/net/wireless/hostap/hostap_ioctl.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_main.c (renamed from drivers/net/wireless/hostap/hostap_main.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_pci.c (renamed from drivers/net/wireless/hostap/hostap_pci.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_plx.c (renamed from drivers/net/wireless/hostap/hostap_plx.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_proc.c (renamed from drivers/net/wireless/hostap/hostap_proc.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_wlan.h (renamed from drivers/net/wireless/hostap/hostap_wlan.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/Kconfig (renamed from drivers/net/wireless/orinoco/Kconfig)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/Makefile (renamed from drivers/net/wireless/orinoco/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/airport.c (renamed from drivers/net/wireless/orinoco/airport.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/cfg.c (renamed from drivers/net/wireless/orinoco/cfg.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/cfg.h (renamed from drivers/net/wireless/orinoco/cfg.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/fw.c (renamed from drivers/net/wireless/orinoco/fw.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/fw.h (renamed from drivers/net/wireless/orinoco/fw.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes.c (renamed from drivers/net/wireless/orinoco/hermes.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes.h (renamed from drivers/net/wireless/orinoco/hermes.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_dld.c (renamed from drivers/net/wireless/orinoco/hermes_dld.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_dld.h (renamed from drivers/net/wireless/orinoco/hermes_dld.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_rid.h (renamed from drivers/net/wireless/orinoco/hermes_rid.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hw.c (renamed from drivers/net/wireless/orinoco/hw.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hw.h (renamed from drivers/net/wireless/orinoco/hw.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/main.c (renamed from drivers/net/wireless/orinoco/main.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/main.h (renamed from drivers/net/wireless/orinoco/main.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/mic.c (renamed from drivers/net/wireless/orinoco/mic.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/mic.h (renamed from drivers/net/wireless/orinoco/mic.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco.h (renamed from drivers/net/wireless/orinoco/orinoco.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_cs.c (renamed from drivers/net/wireless/orinoco/orinoco_cs.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_nortel.c (renamed from drivers/net/wireless/orinoco/orinoco_nortel.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_pci.c (renamed from drivers/net/wireless/orinoco/orinoco_pci.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_pci.h (renamed from drivers/net/wireless/orinoco/orinoco_pci.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_plx.c (renamed from drivers/net/wireless/orinoco/orinoco_plx.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_tmd.c (renamed from drivers/net/wireless/orinoco/orinoco_tmd.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_usb.c (renamed from drivers/net/wireless/orinoco/orinoco_usb.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/scan.c (renamed from drivers/net/wireless/orinoco/scan.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/scan.h (renamed from drivers/net/wireless/orinoco/scan.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/spectrum_cs.c (renamed from drivers/net/wireless/orinoco/spectrum_cs.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/wext.c (renamed from drivers/net/wireless/orinoco/wext.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/wext.h (renamed from drivers/net/wireless/orinoco/wext.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/Kconfig (renamed from drivers/net/wireless/p54/Kconfig)0
-rw-r--r--drivers/net/wireless/intersil/p54/Makefile (renamed from drivers/net/wireless/p54/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/p54/eeprom.c (renamed from drivers/net/wireless/p54/eeprom.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/eeprom.h (renamed from drivers/net/wireless/p54/eeprom.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/fwio.c (renamed from drivers/net/wireless/p54/fwio.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/led.c (renamed from drivers/net/wireless/p54/led.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/lmac.h (renamed from drivers/net/wireless/p54/lmac.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/main.c (renamed from drivers/net/wireless/p54/main.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54.h (renamed from drivers/net/wireless/p54/p54.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54pci.c (renamed from drivers/net/wireless/p54/p54pci.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54pci.h (renamed from drivers/net/wireless/p54/p54pci.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54spi.c (renamed from drivers/net/wireless/p54/p54spi.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54spi.h (renamed from drivers/net/wireless/p54/p54spi.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54spi_eeprom.h (renamed from drivers/net/wireless/p54/p54spi_eeprom.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54usb.c (renamed from drivers/net/wireless/p54/p54usb.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54usb.h (renamed from drivers/net/wireless/p54/p54usb.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/txrx.c (renamed from drivers/net/wireless/p54/txrx.c)0
-rw-r--r--drivers/net/wireless/intersil/prism54/Makefile (renamed from drivers/net/wireless/prism54/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_38xx.c (renamed from drivers/net/wireless/prism54/isl_38xx.c)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_38xx.h (renamed from drivers/net/wireless/prism54/isl_38xx.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_ioctl.c2910
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_ioctl.h (renamed from drivers/net/wireless/prism54/isl_ioctl.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_oid.h (renamed from drivers/net/wireless/prism54/isl_oid.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_dev.c965
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_dev.h (renamed from drivers/net/wireless/prism54/islpci_dev.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_eth.c507
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_eth.h (renamed from drivers/net/wireless/prism54/islpci_eth.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_hotplug.c (renamed from drivers/net/wireless/prism54/islpci_hotplug.c)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_mgt.c502
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_mgt.h (renamed from drivers/net/wireless/prism54/islpci_mgt.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/oid_mgt.c901
-rw-r--r--drivers/net/wireless/intersil/prism54/oid_mgt.h (renamed from drivers/net/wireless/prism54/oid_mgt.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/prismcompat.h (renamed from drivers/net/wireless/prism54/prismcompat.h)0
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c8639
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c6868
-rw-r--r--drivers/net/wireless/iwlegacy/common.c5586
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig161
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile23
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/agn.h485
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/calib.c1113
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/calib.h74
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/commands.h4008
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/debugfs.c2441
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/dev.h949
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/devices.c690
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/led.c223
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/led.h55
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/lib.c1300
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c1655
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/main.c2077
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/power.c395
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/power.h47
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.c3338
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.h426
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rx.c1101
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rxon.c1572
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/scan.c1075
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/sta.c1442
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tt.c685
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tt.h128
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tx.c1412
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/ucode.c452
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c140
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-2000.c216
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c178
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c389
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-7000.c367
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-8000.c229
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hw.h117
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-config.h437
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h552
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.c136
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h225
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace-data.h80
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace-io.h155
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h209
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h97
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h81
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace.c43
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace.h89
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c1706
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.h155
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c947
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h144
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-read.c464
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-read.h70
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fh.h535
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h320
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h768
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h322
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.c289
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.h73
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-modparams.h129
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-notif-wait.c193
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-notif-wait.h139
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.c844
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.h97
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-op-mode.h271
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-phy-db.c471
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-phy-db.h82
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h401
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scd.h143
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.c114
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h1125
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile12
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/binding.c211
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/coex.c1005
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/coex_legacy.c1315
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/constants.h139
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c2100
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c1483
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c1516
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.h103
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h476
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h425
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h387
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h467
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h389
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h238
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h730
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h414
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h284
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h386
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h646
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h1773
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c1166
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/led.c136
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c1452
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c4265
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h1535
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/nvm.c864
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/offloading.c217
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c1434
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c295
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power.c1040
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/quota.c328
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c3983
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.h392
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c612
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/scan.c1552
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sf.c340
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c1819
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h426
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tdls.c732
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/testmode.h97
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.c872
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.h249
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tof.c306
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tof.h94
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tt.c460
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c1115
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c1083
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c702
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h569
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/rx.c1548
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c2825
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c1988
-rw-r--r--drivers/net/wireless/libertas/cfg.c2215
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c1453
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c95
-rw-r--r--drivers/net/wireless/marvell/Kconfig27
-rw-r--r--drivers/net/wireless/marvell/Makefile6
-rw-r--r--drivers/net/wireless/marvell/libertas/Kconfig (renamed from drivers/net/wireless/libertas/Kconfig)0
-rw-r--r--drivers/net/wireless/marvell/libertas/LICENSE (renamed from drivers/net/wireless/libertas/LICENSE)0
-rw-r--r--drivers/net/wireless/marvell/libertas/Makefile (renamed from drivers/net/wireless/libertas/Makefile)0
-rw-r--r--drivers/net/wireless/marvell/libertas/README (renamed from drivers/net/wireless/libertas/README)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.c2216
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.h (renamed from drivers/net/wireless/libertas/cfg.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.c (renamed from drivers/net/wireless/libertas/cmd.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.h (renamed from drivers/net/wireless/libertas/cmd.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cmdresp.c (renamed from drivers/net/wireless/libertas/cmdresp.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/debugfs.c (renamed from drivers/net/wireless/libertas/debugfs.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/debugfs.h (renamed from drivers/net/wireless/libertas/debugfs.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/decl.h (renamed from drivers/net/wireless/libertas/decl.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/defs.h (renamed from drivers/net/wireless/libertas/defs.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/dev.h (renamed from drivers/net/wireless/libertas/dev.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/ethtool.c (renamed from drivers/net/wireless/libertas/ethtool.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/firmware.c (renamed from drivers/net/wireless/libertas/firmware.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/host.h (renamed from drivers/net/wireless/libertas/host.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_cs.c (renamed from drivers/net/wireless/libertas/if_cs.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_sdio.c1453
-rw-r--r--drivers/net/wireless/marvell/libertas/if_sdio.h (renamed from drivers/net/wireless/libertas/if_sdio.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_spi.c (renamed from drivers/net/wireless/libertas/if_spi.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_spi.h (renamed from drivers/net/wireless/libertas/if_spi.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.c (renamed from drivers/net/wireless/libertas/if_usb.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.h (renamed from drivers/net/wireless/libertas/if_usb.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/main.c (renamed from drivers/net/wireless/libertas/main.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.c (renamed from drivers/net/wireless/libertas/mesh.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.h (renamed from drivers/net/wireless/libertas/mesh.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/radiotap.h (renamed from drivers/net/wireless/libertas/radiotap.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/rx.c (renamed from drivers/net/wireless/libertas/rx.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/tx.c (renamed from drivers/net/wireless/libertas/tx.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/types.h (renamed from drivers/net/wireless/libertas/types.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/Kconfig18
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/Makefile (renamed from drivers/net/wireless/libertas_tf/Makefile)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/cmd.c (renamed from drivers/net/wireless/libertas_tf/cmd.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/deb_defs.h (renamed from drivers/net/wireless/libertas_tf/deb_defs.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/if_usb.c (renamed from drivers/net/wireless/libertas_tf/if_usb.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/if_usb.h (renamed from drivers/net/wireless/libertas_tf/if_usb.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/libertas_tf.h (renamed from drivers/net/wireless/libertas_tf/libertas_tf.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/main.c (renamed from drivers/net/wireless/libertas_tf/main.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11ac.c (renamed from drivers/net/wireless/mwifiex/11ac.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11ac.h (renamed from drivers/net/wireless/mwifiex/11ac.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11h.c (renamed from drivers/net/wireless/mwifiex/11h.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.c (renamed from drivers/net/wireless/mwifiex/11n.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.h (renamed from drivers/net/wireless/mwifiex/11n.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_aggr.c316
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_aggr.h (renamed from drivers/net/wireless/mwifiex/11n_aggr.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c920
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h (renamed from drivers/net/wireless/mwifiex/11n_rxreorder.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/Kconfig (renamed from drivers/net/wireless/mwifiex/Kconfig)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/Makefile (renamed from drivers/net/wireless/mwifiex/Makefile)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/README (renamed from drivers/net/wireless/mwifiex/README)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.c3913
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.h (renamed from drivers/net/wireless/mwifiex/cfg80211.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfp.c537
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cmdevt.c1659
-rw-r--r--drivers/net/wireless/marvell/mwifiex/debugfs.c1003
-rw-r--r--drivers/net/wireless/marvell/mwifiex/decl.h273
-rw-r--r--drivers/net/wireless/marvell/mwifiex/ethtool.c (renamed from drivers/net/wireless/mwifiex/ethtool.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/fw.h2184
-rw-r--r--drivers/net/wireless/marvell/mwifiex/ie.c488
-rw-r--r--drivers/net/wireless/marvell/mwifiex/init.c782
-rw-r--r--drivers/net/wireless/marvell/mwifiex/ioctl.h (renamed from drivers/net/wireless/mwifiex/ioctl.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/join.c1531
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.c1552
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.h1605
-rw-r--r--drivers/net/wireless/marvell/mwifiex/pcie.c2753
-rw-r--r--drivers/net/wireless/marvell/mwifiex/pcie.h384
-rw-r--r--drivers/net/wireless/marvell/mwifiex/scan.c (renamed from drivers/net/wireless/mwifiex/scan.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sdio.c2695
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sdio.h (renamed from drivers/net/wireless/mwifiex/sdio.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmd.c (renamed from drivers/net/wireless/mwifiex/sta_cmd.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c (renamed from drivers/net/wireless/mwifiex/sta_cmdresp.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_event.c (renamed from drivers/net/wireless/mwifiex/sta_event.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_ioctl.c1452
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_rx.c267
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_tx.c (renamed from drivers/net/wireless/mwifiex/sta_tx.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/tdls.c (renamed from drivers/net/wireless/mwifiex/tdls.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/txrx.c (renamed from drivers/net/wireless/mwifiex/txrx.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_cmd.c885
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_event.c (renamed from drivers/net/wireless/mwifiex/uap_event.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_txrx.c436
-rw-r--r--drivers/net/wireless/marvell/mwifiex/usb.c (renamed from drivers/net/wireless/mwifiex/usb.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/usb.h (renamed from drivers/net/wireless/mwifiex/usb.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/util.c (renamed from drivers/net/wireless/mwifiex/util.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/util.h (renamed from drivers/net/wireless/mwifiex/util.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/wmm.c (renamed from drivers/net/wireless/mwifiex/wmm.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/wmm.h (renamed from drivers/net/wireless/mwifiex/wmm.h)0
-rw-r--r--drivers/net/wireless/marvell/mwl8k.c (renamed from drivers/net/wireless/mwl8k.c)0
-rw-r--r--drivers/net/wireless/mediatek/Kconfig16
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.c318
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.c910
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c3887
-rw-r--r--drivers/net/wireless/mwifiex/cfp.c537
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c1659
-rw-r--r--drivers/net/wireless/mwifiex/debugfs.c973
-rw-r--r--drivers/net/wireless/mwifiex/decl.h273
-rw-r--r--drivers/net/wireless/mwifiex/fw.h2177
-rw-r--r--drivers/net/wireless/mwifiex/ie.c488
-rw-r--r--drivers/net/wireless/mwifiex/init.c782
-rw-r--r--drivers/net/wireless/mwifiex/join.c1525
-rw-r--r--drivers/net/wireless/mwifiex/main.c1552
-rw-r--r--drivers/net/wireless/mwifiex/main.h1579
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c2742
-rw-r--r--drivers/net/wireless/mwifiex/pcie.h382
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c2684
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c1421
-rw-r--r--drivers/net/wireless/mwifiex/sta_rx.c267
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c885
-rw-r--r--drivers/net/wireless/mwifiex/uap_txrx.c437
-rw-r--r--drivers/net/wireless/prism54/isl_ioctl.c2910
-rw-r--r--drivers/net/wireless/prism54/islpci_dev.c963
-rw-r--r--drivers/net/wireless/prism54/islpci_eth.c506
-rw-r--r--drivers/net/wireless/prism54/islpci_mgt.c502
-rw-r--r--drivers/net/wireless/prism54/oid_mgt.c901
-rw-r--r--drivers/net/wireless/ralink/Kconfig16
-rw-r--r--drivers/net/wireless/ralink/Makefile1
-rw-r--r--drivers/net/wireless/ralink/rt2x00/Kconfig (renamed from drivers/net/wireless/rt2x00/Kconfig)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/Makefile (renamed from drivers/net/wireless/rt2x00/Makefile)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2400pci.c (renamed from drivers/net/wireless/rt2x00/rt2400pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2400pci.h (renamed from drivers/net/wireless/rt2x00/rt2400pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500pci.c (renamed from drivers/net/wireless/rt2x00/rt2500pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500pci.h (renamed from drivers/net/wireless/rt2x00/rt2500pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500usb.c2004
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500usb.h (renamed from drivers/net/wireless/rt2x00/rt2500usb.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800.h (renamed from drivers/net/wireless/rt2x00/rt2800.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.c (renamed from drivers/net/wireless/rt2x00/rt2800lib.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.h (renamed from drivers/net/wireless/rt2x00/rt2800lib.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800mmio.c (renamed from drivers/net/wireless/rt2x00/rt2800mmio.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800mmio.h (renamed from drivers/net/wireless/rt2x00/rt2800mmio.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.c (renamed from drivers/net/wireless/rt2x00/rt2800pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.h (renamed from drivers/net/wireless/rt2x00/rt2800pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800soc.c (renamed from drivers/net/wireless/rt2x00/rt2800soc.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800usb.c (renamed from drivers/net/wireless/rt2x00/rt2800usb.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800usb.h (renamed from drivers/net/wireless/rt2x00/rt2800usb.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00.h (renamed from drivers/net/wireless/rt2x00/rt2x00.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00config.c (renamed from drivers/net/wireless/rt2x00/rt2x00config.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c (renamed from drivers/net/wireless/rt2x00/rt2x00crypto.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00debug.c (renamed from drivers/net/wireless/rt2x00/rt2x00debug.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00debug.h (renamed from drivers/net/wireless/rt2x00/rt2x00debug.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dev.c (renamed from drivers/net/wireless/rt2x00/rt2x00dev.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dump.h (renamed from drivers/net/wireless/rt2x00/rt2x00dump.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c (renamed from drivers/net/wireless/rt2x00/rt2x00firmware.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00leds.c (renamed from drivers/net/wireless/rt2x00/rt2x00leds.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00leds.h (renamed from drivers/net/wireless/rt2x00/rt2x00leds.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00lib.h (renamed from drivers/net/wireless/rt2x00/rt2x00lib.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00link.c (renamed from drivers/net/wireless/rt2x00/rt2x00link.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00mac.c (renamed from drivers/net/wireless/rt2x00/rt2x00mac.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c (renamed from drivers/net/wireless/rt2x00/rt2x00mmio.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h (renamed from drivers/net/wireless/rt2x00/rt2x00mmio.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00pci.c223
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00pci.h (renamed from drivers/net/wireless/rt2x00/rt2x00pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00queue.c (renamed from drivers/net/wireless/rt2x00/rt2x00queue.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00queue.h (renamed from drivers/net/wireless/rt2x00/rt2x00queue.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00reg.h (renamed from drivers/net/wireless/rt2x00/rt2x00reg.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00soc.c (renamed from drivers/net/wireless/rt2x00/rt2x00soc.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00soc.h (renamed from drivers/net/wireless/rt2x00/rt2x00soc.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00usb.c (renamed from drivers/net/wireless/rt2x00/rt2x00usb.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00usb.h (renamed from drivers/net/wireless/rt2x00/rt2x00usb.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.c (renamed from drivers/net/wireless/rt2x00/rt61pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.h (renamed from drivers/net/wireless/rt2x00/rt61pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt73usb.c (renamed from drivers/net/wireless/rt2x00/rt73usb.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt73usb.h (renamed from drivers/net/wireless/rt2x00/rt73usb.h)0
-rw-r--r--drivers/net/wireless/realtek/Kconfig18
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c23
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c21
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c22
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c23
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c21
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c7
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c8
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.c2
-rw-r--r--drivers/net/wireless/rsi/Kconfig15
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c5
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c2001
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.c221
-rw-r--r--drivers/net/wireless/st/Kconfig16
-rw-r--r--drivers/net/wireless/st/Makefile1
-rw-r--r--drivers/net/wireless/st/cw1200/Kconfig (renamed from drivers/net/wireless/cw1200/Kconfig)0
-rw-r--r--drivers/net/wireless/st/cw1200/Makefile (renamed from drivers/net/wireless/cw1200/Makefile)0
-rw-r--r--drivers/net/wireless/st/cw1200/bh.c (renamed from drivers/net/wireless/cw1200/bh.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/bh.h (renamed from drivers/net/wireless/cw1200/bh.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200.h (renamed from drivers/net/wireless/cw1200/cw1200.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200_sdio.c (renamed from drivers/net/wireless/cw1200/cw1200_sdio.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200_spi.c (renamed from drivers/net/wireless/cw1200/cw1200_spi.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/debug.c (renamed from drivers/net/wireless/cw1200/debug.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/debug.h (renamed from drivers/net/wireless/cw1200/debug.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/fwio.c (renamed from drivers/net/wireless/cw1200/fwio.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/fwio.h (renamed from drivers/net/wireless/cw1200/fwio.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/hwbus.h (renamed from drivers/net/wireless/cw1200/hwbus.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/hwio.c (renamed from drivers/net/wireless/cw1200/hwio.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/hwio.h (renamed from drivers/net/wireless/cw1200/hwio.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/main.c (renamed from drivers/net/wireless/cw1200/main.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/pm.c (renamed from drivers/net/wireless/cw1200/pm.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/pm.h (renamed from drivers/net/wireless/cw1200/pm.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/queue.c (renamed from drivers/net/wireless/cw1200/queue.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/queue.h (renamed from drivers/net/wireless/cw1200/queue.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/scan.c (renamed from drivers/net/wireless/cw1200/scan.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/scan.h (renamed from drivers/net/wireless/cw1200/scan.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/sta.c2393
-rw-r--r--drivers/net/wireless/st/cw1200/sta.h (renamed from drivers/net/wireless/cw1200/sta.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/txrx.c (renamed from drivers/net/wireless/cw1200/txrx.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/txrx.h (renamed from drivers/net/wireless/cw1200/txrx.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/wsm.c (renamed from drivers/net/wireless/cw1200/wsm.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/wsm.h (renamed from drivers/net/wireless/cw1200/wsm.h)0
-rw-r--r--drivers/net/wireless/ti/Kconfig18
-rw-r--r--drivers/net/wireless/ti/wl1251/Kconfig2
-rw-r--r--drivers/net/wireless/ti/wl12xx/conf.h233
-rw-r--r--drivers/net/wireless/ti/wl12xx/main.c116
-rw-r--r--drivers/net/wireless/ti/wl18xx/conf.h90
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.c2
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.h1
-rw-r--r--drivers/net/wireless/ti/wl18xx/main.c147
-rw-r--r--drivers/net/wireless/ti/wlcore/Kconfig2
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.c4
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.h2
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.h1
-rw-r--r--drivers/net/wireless/ti/wlcore/conf.h237
-rw-r--r--drivers/net/wireless/ti/wlcore/debugfs.c60
-rw-r--r--drivers/net/wireless/ti/wlcore/event.c82
-rw-r--r--drivers/net/wireless/ti/wlcore/event.h9
-rw-r--r--drivers/net/wireless/ti/wlcore/io.c11
-rw-r--r--drivers/net/wireless/ti/wlcore/io.h14
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c96
-rw-r--r--drivers/net/wireless/ti/wlcore/rx.c1
-rw-r--r--drivers/net/wireless/ti/wlcore/spi.c10
-rw-r--r--drivers/net/wireless/ti/wlcore/sysfs.c26
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore.h3
-rw-r--r--drivers/net/wireless/zydas/Kconfig35
-rw-r--r--drivers/net/wireless/zydas/Makefile3
-rw-r--r--drivers/net/wireless/zydas/zd1201.c (renamed from drivers/net/wireless/zd1201.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1201.h (renamed from drivers/net/wireless/zd1201.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/Kconfig (renamed from drivers/net/wireless/zd1211rw/Kconfig)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/Makefile (renamed from drivers/net/wireless/zd1211rw/Makefile)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_chip.c (renamed from drivers/net/wireless/zd1211rw/zd_chip.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_chip.h (renamed from drivers/net/wireless/zd1211rw/zd_chip.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_def.h (renamed from drivers/net/wireless/zd1211rw/zd_def.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.c (renamed from drivers/net/wireless/zd1211rw/zd_mac.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.h (renamed from drivers/net/wireless/zd1211rw/zd_mac.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf.c (renamed from drivers/net/wireless/zd1211rw/zd_rf.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf.h (renamed from drivers/net/wireless/zd1211rw/zd_rf.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_al2230.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_al7230b.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_rf2959.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_uw2453.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.c (renamed from drivers/net/wireless/zd1211rw/zd_usb.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.h (renamed from drivers/net/wireless/zd1211rw/zd_usb.h)0
-rw-r--r--drivers/nfc/Kconfig1
-rw-r--r--drivers/nfc/Makefile1
-rw-r--r--drivers/nfc/fdp/i2c.c12
-rw-r--r--drivers/nfc/microread/i2c.c2
-rw-r--r--drivers/nfc/nfcsim.c10
-rw-r--r--drivers/nfc/nxp-nci/i2c.c34
-rw-r--r--drivers/nfc/pn544/i2c.c46
-rw-r--r--drivers/nfc/s3fwrn5/core.c2
-rw-r--r--drivers/nfc/s3fwrn5/i2c.c2
-rw-r--r--drivers/nfc/s3fwrn5/s3fwrn5.h4
-rw-r--r--drivers/nfc/st-nci/Kconfig18
-rw-r--r--drivers/nfc/st-nci/i2c.c80
-rw-r--r--drivers/nfc/st-nci/ndlc.c1
-rw-r--r--drivers/nfc/st-nci/se.c3
-rw-r--r--drivers/nfc/st-nci/spi.c81
-rw-r--r--drivers/nfc/st21nfca/Kconfig13
-rw-r--r--drivers/nfc/st21nfca/i2c.c80
-rw-r--r--drivers/nfc/st21nfca/se.c5
-rw-r--r--drivers/nfc/st95hf/Kconfig10
-rw-r--r--drivers/nfc/st95hf/Makefile6
-rw-r--r--drivers/nfc/st95hf/core.c1273
-rw-r--r--drivers/nfc/st95hf/spi.c167
-rw-r--r--drivers/nfc/st95hf/spi.h64
-rw-r--r--drivers/nfc/trf7970a.c8
-rw-r--r--drivers/of/of_mdio.c95
-rw-r--r--drivers/s390/net/ctcm_main.c7
-rw-r--r--drivers/s390/net/qeth_core.h4
-rw-r--r--drivers/s390/net/qeth_core_main.c11
-rw-r--r--drivers/s390/net/qeth_l2_main.c1
-rw-r--r--drivers/s390/net/qeth_l3_main.c40
-rw-r--r--drivers/scsi/fcoe/fcoe.c2
-rw-r--r--drivers/ssb/Kconfig2
-rw-r--r--drivers/ssb/host_soc.c37
-rw-r--r--drivers/ssb/main.c5
-rw-r--r--drivers/ssb/ssb_private.h3
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c2
-rw-r--r--drivers/staging/netlogic/xlr_net.c36
-rw-r--r--drivers/staging/octeon/ethernet-rgmii.c6
-rw-r--r--fs/kernfs/dir.c46
-rw-r--r--include/linux/brcmphy.h1
-rw-r--r--include/linux/cgroup-defs.h126
-rw-r--r--include/linux/cgroup.h66
-rw-r--r--include/linux/etherdevice.h3
-rw-r--r--include/linux/filter.h41
-rw-r--r--include/linux/hashtable.h4
-rw-r--r--include/linux/hdlc.h2
-rw-r--r--include/linux/if_pppox.h1
-rw-r--r--include/linux/if_team.h1
-rw-r--r--include/linux/if_vlan.h4
-rw-r--r--include/linux/inet_diag.h9
-rw-r--r--include/linux/kernfs.h12
-rw-r--r--include/linux/mdio.h78
-rw-r--r--include/linux/mlx4/driver.h5
-rw-r--r--include/linux/mlx5/device.h66
-rw-r--r--include/linux/mlx5/driver.h30
-rw-r--r--include/linux/mlx5/flow_table.h54
-rw-r--r--include/linux/mlx5/fs.h111
-rw-r--r--include/linux/mlx5/mlx5_ifc.h311
-rw-r--r--include/linux/mlx5/vport.h37
-rw-r--r--include/linux/mroute.h76
-rw-r--r--include/linux/netdev_features.h14
-rw-r--r--include/linux/netdevice.h282
-rw-r--r--include/linux/netfilter/nf_conntrack_sctp.h13
-rw-r--r--include/linux/netfilter/nfnetlink.h12
-rw-r--r--include/linux/netlink.h2
-rw-r--r--include/linux/pci_ids.h2
-rw-r--r--include/linux/phy.h80
-rw-r--r--include/linux/pim.h5
-rw-r--r--include/linux/platform_data/microread.h2
-rw-r--r--include/linux/qed/qed_if.h17
-rw-r--r--include/linux/rhashtable.h82
-rw-r--r--include/linux/rtnetlink.h5
-rw-r--r--include/linux/sched.h1
-rw-r--r--include/linux/sh_eth.h2
-rw-r--r--include/linux/skbuff.h171
-rw-r--r--include/linux/soc/ti/knav_dma.h22
-rw-r--r--include/linux/sock_diag.h2
-rw-r--r--include/linux/ssb/ssb.h10
-rw-r--r--include/linux/wait.h21
-rw-r--r--include/net/6lowpan.h10
-rw-r--r--include/net/addrconf.h3
-rw-r--r--include/net/bluetooth/bluetooth.h25
-rw-r--r--include/net/bluetooth/hci.h4
-rw-r--r--include/net/bluetooth/hci_core.h62
-rw-r--r--include/net/bluetooth/hci_mon.h2
-rw-r--r--include/net/bluetooth/hci_sock.h1
-rw-r--r--include/net/bluetooth/mgmt.h15
-rw-r--r--include/net/bonding.h39
-rw-r--r--include/net/busy_poll.h45
-rw-r--r--include/net/cfg80211.h10
-rw-r--r--include/net/checksum.h3
-rw-r--r--include/net/cls_cgroup.h11
-rw-r--r--include/net/dsa.h20
-rw-r--r--include/net/genetlink.h2
-rw-r--r--include/net/geneve.h8
-rw-r--r--include/net/ieee802154_netdev.h2
-rw-r--r--include/net/ila.h18
-rw-r--r--include/net/inet_frag.h1
-rw-r--r--include/net/inet_sock.h14
-rw-r--r--include/net/ip6_tunnel.h19
-rw-r--r--include/net/ip_fib.h3
-rw-r--r--include/net/ip_tunnels.h28
-rw-r--r--include/net/ipv6.h15
-rw-r--r--include/net/l3mdev.h23
-rw-r--r--include/net/mac80211.h56
-rw-r--r--include/net/net_namespace.h3
-rw-r--r--include/net/netfilter/ipv6/nf_defrag_ipv6.h3
-rw-r--r--include/net/netfilter/nf_conntrack_timeout.h2
-rw-r--r--include/net/netfilter/nf_dup_netdev.h6
-rw-r--r--include/net/netfilter/nf_tables.h45
-rw-r--r--include/net/netfilter/nf_tables_core.h10
-rw-r--r--include/net/netfilter/nft_meta.h3
-rw-r--r--include/net/netns/ipv4.h7
-rw-r--r--include/net/netns/sctp.h7
-rw-r--r--include/net/netprio_cgroup.h16
-rw-r--r--include/net/nfc/nfc.h1
-rw-r--r--include/net/protocol.h2
-rw-r--r--include/net/request_sock.h2
-rw-r--r--include/net/sch_generic.h9
-rw-r--r--include/net/sctp/sctp.h32
-rw-r--r--include/net/sctp/structs.h10
-rw-r--r--include/net/sock.h44
-rw-r--r--include/net/sock_reuseport.h28
-rw-r--r--include/net/switchdev.h15
-rw-r--r--include/net/tcp.h29
-rw-r--r--include/net/udp.h7
-rw-r--r--include/net/udp_tunnel.h8
-rw-r--r--include/net/vxlan.h2
-rw-r--r--include/trace/events/fib6.h76
-rw-r--r--include/uapi/asm-generic/socket.h3
-rw-r--r--include/uapi/linux/bpf.h27
-rw-r--r--include/uapi/linux/ethtool.h3
-rw-r--r--include/uapi/linux/if_link.h4
-rw-r--r--include/uapi/linux/ila.h22
-rw-r--r--include/uapi/linux/in6.h1
-rw-r--r--include/uapi/linux/mroute.h59
-rw-r--r--include/uapi/linux/netfilter/ipset/ip_set_bitmap.h2
-rw-r--r--include/uapi/linux/netfilter/ipset/ip_set_hash.h2
-rw-r--r--include/uapi/linux/netfilter/ipset/ip_set_list.h2
-rw-r--r--include/uapi/linux/netfilter/nf_conntrack_sctp.h12
-rw-r--r--include/uapi/linux/netfilter/nf_conntrack_tuple_common.h3
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h91
-rw-r--r--include/uapi/linux/netfilter/nfnetlink.h2
-rw-r--r--include/uapi/linux/netfilter/xt_HMARK.h1
-rw-r--r--include/uapi/linux/netfilter/xt_RATEEST.h1
-rw-r--r--include/uapi/linux/netfilter/xt_TEE.h2
-rw-r--r--include/uapi/linux/netfilter/xt_TPROXY.h1
-rw-r--r--include/uapi/linux/netfilter/xt_cgroup.h15
-rw-r--r--include/uapi/linux/netfilter/xt_hashlimit.h1
-rw-r--r--include/uapi/linux/netfilter/xt_ipvs.h1
-rw-r--r--include/uapi/linux/netfilter/xt_mac.h2
-rw-r--r--include/uapi/linux/netfilter/xt_osf.h2
-rw-r--r--include/uapi/linux/netfilter/xt_physdev.h2
-rw-r--r--include/uapi/linux/netfilter/xt_policy.h2
-rw-r--r--include/uapi/linux/netfilter/xt_rateest.h1
-rw-r--r--include/uapi/linux/netfilter/xt_recent.h1
-rw-r--r--include/uapi/linux/netfilter/xt_sctp.h12
-rw-r--r--include/uapi/linux/netfilter_arp/arp_tables.h1
-rw-r--r--include/uapi/linux/netfilter_bridge.h1
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_arp.h1
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_arpreply.h2
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_ip6.h1
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_nat.h2
-rw-r--r--include/uapi/linux/netfilter_bridge/ebtables.h6
-rw-r--r--include/uapi/linux/netfilter_ipv4/ip_tables.h1
-rw-r--r--include/uapi/linux/netfilter_ipv6/ip6_tables.h1
-rw-r--r--include/uapi/linux/netfilter_ipv6/ip6t_rt.h2
-rw-r--r--include/uapi/linux/nl80211.h11
-rw-r--r--include/uapi/linux/pkt_sched.h4
-rw-r--r--include/uapi/linux/rtnetlink.h1
-rw-r--r--include/uapi/linux/sock_diag.h1
-rw-r--r--include/uapi/linux/sockios.h2
-rw-r--r--kernel/bpf/core.c4
-rw-r--r--kernel/bpf/hashtab.c64
-rw-r--r--kernel/bpf/inode.c20
-rw-r--r--kernel/bpf/syscall.c22
-rw-r--r--kernel/bpf/verifier.c10
-rw-r--r--kernel/cgroup.c126
-rw-r--r--lib/Kconfig.debug23
-rw-r--r--lib/Makefile1
-rw-r--r--lib/netdev-notifier-error-inject.c55
-rw-r--r--lib/rhashtable.c3
-rw-r--r--lib/test_bpf.c120
-rw-r--r--lib/test_rhashtable.c76
-rw-r--r--net/6lowpan/6lowpan_i.h28
-rw-r--r--net/6lowpan/Kconfig47
-rw-r--r--net/6lowpan/Makefile9
-rw-r--r--net/6lowpan/core.c59
-rw-r--r--net/6lowpan/debugfs.c53
-rw-r--r--net/6lowpan/nhc_ghc_ext_dest.c27
-rw-r--r--net/6lowpan/nhc_ghc_ext_frag.c28
-rw-r--r--net/6lowpan/nhc_ghc_ext_hop.c27
-rw-r--r--net/6lowpan/nhc_ghc_ext_route.c27
-rw-r--r--net/6lowpan/nhc_ghc_icmpv6.c27
-rw-r--r--net/6lowpan/nhc_ghc_udp.c27
-rw-r--r--net/8021q/vlan_dev.c11
-rw-r--r--net/Kconfig9
-rw-r--r--net/atm/common.c4
-rw-r--r--net/atm/mpc.h4
-rw-r--r--net/atm/mpoa_caches.c4
-rw-r--r--net/batman-adv/bat_iv_ogm.c80
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c55
-rw-r--r--net/batman-adv/bridge_loop_avoidance.h2
-rw-r--r--net/batman-adv/debugfs.c9
-rw-r--r--net/batman-adv/fragmentation.c8
-rw-r--r--net/batman-adv/gateway_common.c117
-rw-r--r--net/batman-adv/hard-interface.c11
-rw-r--r--net/batman-adv/main.c23
-rw-r--r--net/batman-adv/main.h4
-rw-r--r--net/batman-adv/network-coding.c4
-rw-r--r--net/batman-adv/originator.c195
-rw-r--r--net/batman-adv/originator.h7
-rw-r--r--net/batman-adv/packet.h3
-rw-r--r--net/batman-adv/routing.c6
-rw-r--r--net/batman-adv/send.c3
-rw-r--r--net/batman-adv/sysfs.c16
-rw-r--r--net/batman-adv/translation-table.c24
-rw-r--r--net/batman-adv/types.h66
-rw-r--r--net/bluetooth/6lowpan.c8
-rw-r--r--net/bluetooth/af_bluetooth.c26
-rw-r--r--net/bluetooth/bnep/core.c7
-rw-r--r--net/bluetooth/cmtp/capi.c8
-rw-r--r--net/bluetooth/cmtp/core.c3
-rw-r--r--net/bluetooth/hci_conn.c123
-rw-r--r--net/bluetooth/hci_core.c647
-rw-r--r--net/bluetooth/hci_event.c14
-rw-r--r--net/bluetooth/hci_request.c1778
-rw-r--r--net/bluetooth/hci_request.h53
-rw-r--r--net/bluetooth/hci_sock.c209
-rw-r--r--net/bluetooth/l2cap_core.c19
-rw-r--r--net/bluetooth/mgmt.c1899
-rw-r--r--net/bluetooth/rfcomm/core.c46
-rw-r--r--net/bridge/br_fdb.c1
-rw-r--r--net/bridge/br_if.c13
-rw-r--r--net/bridge/br_mdb.c23
-rw-r--r--net/bridge/br_stp.c2
-rw-r--r--net/bridge/br_stp_if.c1
-rw-r--r--net/bridge/br_sysfs_br.c3
-rw-r--r--net/bridge/br_vlan.c31
-rw-r--r--net/bridge/netfilter/ebt_ip6.c4
-rw-r--r--net/bridge/netfilter/ebt_log.c9
-rw-r--r--net/bridge/netfilter/ebt_stp.c2
-rw-r--r--net/bridge/netfilter/ebt_vlan.c15
-rw-r--r--net/bridge/netfilter/ebtable_filter.c2
-rw-r--r--net/bridge/netfilter/ebtable_nat.c2
-rw-r--r--net/bridge/netfilter/ebtables.c139
-rw-r--r--net/bridge/netfilter/nf_tables_bridge.c2
-rw-r--r--net/bridge/netfilter/nft_meta_bridge.c1
-rw-r--r--net/core/Makefile2
-rw-r--r--net/core/datagram.c77
-rw-r--r--net/core/dev.c414
-rw-r--r--net/core/ethtool.c85
-rw-r--r--net/core/filter.c299
-rw-r--r--net/core/net-sysfs.c5
-rw-r--r--net/core/net-traces.c4
-rw-r--r--net/core/netclassid_cgroup.c11
-rw-r--r--net/core/netprio_cgroup.c19
-rw-r--r--net/core/pktgen.c8
-rw-r--r--net/core/rtnetlink.c21
-rw-r--r--net/core/scm.c4
-rw-r--r--net/core/sock.c54
-rw-r--r--net/core/sock_diag.c23
-rw-r--r--net/core/sock_reuseport.c251
-rw-r--r--net/core/stream.c2
-rw-r--r--net/dccp/output.c2
-rw-r--r--net/dsa/dsa.c80
-rw-r--r--net/dsa/dsa_priv.h1
-rw-r--r--net/dsa/slave.c33
-rw-r--r--net/ethernet/eth.c31
-rw-r--r--net/ieee802154/6lowpan/core.c6
-rw-r--r--net/ieee802154/6lowpan/reassembly.c1
-rw-r--r--net/ipv4/Kconfig13
-rw-r--r--net/ipv4/fou.c2
-rw-r--r--net/ipv4/inet_diag.c65
-rw-r--r--net/ipv4/inet_fragment.c10
-rw-r--r--net/ipv4/ip_fragment.c1
-rw-r--r--net/ipv4/ip_gre.c8
-rw-r--r--net/ipv4/ip_output.c5
-rw-r--r--net/ipv4/ip_tunnel.c8
-rw-r--r--net/ipv4/ip_tunnel_core.c26
-rw-r--r--net/ipv4/ip_vti.c3
-rw-r--r--net/ipv4/ipconfig.c62
-rw-r--r--net/ipv4/ipip.c1
-rw-r--r--net/ipv4/ipmr.c741
-rw-r--r--net/ipv4/netfilter/arp_tables.c6
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c2
-rw-r--r--net/ipv4/netfilter/nf_nat_l3proto_ipv4.c3
-rw-r--r--net/ipv4/netfilter/nf_nat_snmp_basic.c22
-rw-r--r--net/ipv4/netfilter/nf_reject_ipv4.c1
-rw-r--r--net/ipv4/netfilter/nf_tables_arp.c2
-rw-r--r--net/ipv4/netfilter/nf_tables_ipv4.c2
-rw-r--r--net/ipv4/ping.c2
-rw-r--r--net/ipv4/syncookies.c4
-rw-r--r--net/ipv4/sysctl_net_ipv4.c53
-rw-r--r--net/ipv4/tcp.c50
-rw-r--r--net/ipv4/tcp_diag.c19
-rw-r--r--net/ipv4/tcp_input.c2
-rw-r--r--net/ipv4/tcp_ipv4.c27
-rw-r--r--net/ipv4/tcp_minisocks.c7
-rw-r--r--net/ipv4/tcp_output.c14
-rw-r--r--net/ipv4/tcp_timer.c3
-rw-r--r--net/ipv4/tcp_yeah.c2
-rw-r--r--net/ipv4/udp.c144
-rw-r--r--net/ipv4/udp_diag.c4
-rw-r--r--net/ipv4/udp_offload.c15
-rw-r--r--net/ipv4/udp_tunnel.c11
-rw-r--r--net/ipv6/Kconfig1
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/addrconf.c77
-rw-r--r--net/ipv6/ila.c229
-rw-r--r--net/ipv6/ila/Makefile7
-rw-r--r--net/ipv6/ila/ila.h48
-rw-r--r--net/ipv6/ila/ila_common.c103
-rw-r--r--net/ipv6/ila/ila_lwt.c152
-rw-r--r--net/ipv6/ila/ila_xlat.c680
-rw-r--r--net/ipv6/inet6_connection_sock.c4
-rw-r--r--net/ipv6/ip6_gre.c1
-rw-r--r--net/ipv6/ip6_output.c4
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c167
-rw-r--r--net/ipv6/netfilter/nf_defrag_ipv6_hooks.c20
-rw-r--r--net/ipv6/netfilter/nf_nat_l3proto_ipv6.c3
-rw-r--r--net/ipv6/netfilter/nf_reject_ipv6.c1
-rw-r--r--net/ipv6/netfilter/nf_tables_ipv6.c2
-rw-r--r--net/ipv6/raw.c20
-rw-r--r--net/ipv6/reassembly.c1
-rw-r--r--net/ipv6/route.c20
-rw-r--r--net/ipv6/sit.c7
-rw-r--r--net/ipv6/syncookies.c4
-rw-r--r--net/ipv6/tcp_ipv6.c19
-rw-r--r--net/ipv6/udp.c70
-rw-r--r--net/iucv/af_iucv.c24
-rw-r--r--net/l2tp/l2tp_ppp.c19
-rw-r--r--net/mac80211/cfg.c532
-rw-r--r--net/mac80211/debugfs.c1
-rw-r--r--net/mac80211/ibss.c1
-rw-r--r--net/mac80211/ieee80211_i.h25
-rw-r--r--net/mac80211/key.c56
-rw-r--r--net/mac80211/main.c4
-rw-r--r--net/mac80211/mesh_pathtbl.c4
-rw-r--r--net/mac80211/mlme.c6
-rw-r--r--net/mac80211/offchannel.c829
-rw-r--r--net/mac80211/rx.c3
-rw-r--r--net/mac80211/sta_info.c163
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/trace.h25
-rw-r--r--net/mac80211/tx.c2
-rw-r--r--net/mac80211/util.c7
-rw-r--r--net/mac802154/driver-ops.h3
-rw-r--r--net/mac802154/mac_cmd.c2
-rw-r--r--net/mac802154/rx.c3
-rw-r--r--net/mac802154/tx.c9
-rw-r--r--net/mpls/af_mpls.c185
-rw-r--r--net/mpls/internal.h2
-rw-r--r--net/mpls/mpls_iptunnel.c2
-rw-r--r--net/netfilter/Kconfig22
-rw-r--r--net/netfilter/Makefile9
-rw-r--r--net/netfilter/ipset/ip_set_core.c108
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_sctp.c2
-rw-r--r--net/netfilter/nf_conntrack_expect.c7
-rw-r--r--net/netfilter/nf_conntrack_ftp.c17
-rw-r--r--net/netfilter/nf_conntrack_irc.c7
-rw-r--r--net/netfilter/nf_conntrack_netlink.c96
-rw-r--r--net/netfilter/nf_conntrack_sane.c19
-rw-r--r--net/netfilter/nf_conntrack_sip.c5
-rw-r--r--net/netfilter/nf_conntrack_standalone.c7
-rw-r--r--net/netfilter/nf_conntrack_tftp.c7
-rw-r--r--net/netfilter/nf_conntrack_timeout.c2
-rw-r--r--net/netfilter/nf_dup_netdev.c40
-rw-r--r--net/netfilter/nf_tables_api.c176
-rw-r--r--net/netfilter/nf_tables_core.c62
-rw-r--r--net/netfilter/nf_tables_inet.c2
-rw-r--r--net/netfilter/nf_tables_netdev.c47
-rw-r--r--net/netfilter/nf_tables_trace.c275
-rw-r--r--net/netfilter/nfnetlink.c26
-rw-r--r--net/netfilter/nfnetlink_acct.c21
-rw-r--r--net/netfilter/nfnetlink_cthelper.c18
-rw-r--r--net/netfilter/nfnetlink_cttimeout.c120
-rw-r--r--net/netfilter/nfnetlink_log.c40
-rw-r--r--net/netfilter/nfnetlink_queue.c123
-rw-r--r--net/netfilter/nft_byteorder.c23
-rw-r--r--net/netfilter/nft_compat.c6
-rw-r--r--net/netfilter/nft_ct.c38
-rw-r--r--net/netfilter/nft_dup_netdev.c97
-rw-r--r--net/netfilter/nft_fwd_netdev.c98
-rw-r--r--net/netfilter/nft_limit.c16
-rw-r--r--net/netfilter/nft_meta.c56
-rw-r--r--net/netfilter/nft_payload.c135
-rw-r--r--net/netfilter/x_tables.c12
-rw-r--r--net/netfilter/xt_CT.c2
-rw-r--r--net/netfilter/xt_cgroup.c108
-rw-r--r--net/netfilter/xt_osf.c7
-rw-r--r--net/netlink/af_netlink.c4
-rw-r--r--net/netlink/genetlink.c16
-rw-r--r--net/nfc/core.c13
-rw-r--r--net/nfc/digital_core.c3
-rw-r--r--net/nfc/nci/core.c6
-rw-r--r--net/nfc/nci/hci.c2
-rw-r--r--net/nfc/netlink.c37
-rw-r--r--net/nfc/nfc.h1
-rw-r--r--net/openvswitch/conntrack.c25
-rw-r--r--net/openvswitch/vport-geneve.c6
-rw-r--r--net/openvswitch/vport-netdev.c2
-rw-r--r--net/openvswitch/vport.h23
-rw-r--r--net/phonet/af_phonet.c4
-rw-r--r--net/rds/page.c31
-rw-r--r--net/rfkill/rfkill-gpio.c4
-rw-r--r--net/rxrpc/af_rxrpc.c2
-rw-r--r--net/sched/Kconfig14
-rw-r--r--net/sched/cls_bpf.c8
-rw-r--r--net/sched/cls_flower.c10
-rw-r--r--net/sched/sch_ingress.c88
-rw-r--r--net/sctp/associola.c5
-rw-r--r--net/sctp/endpointola.c52
-rw-r--r--net/sctp/input.c187
-rw-r--r--net/sctp/output.c2
-rw-r--r--net/sctp/proc.c316
-rw-r--r--net/sctp/protocol.c41
-rw-r--r--net/sctp/sm_sideeffect.c18
-rw-r--r--net/sctp/sm_statefuns.c17
-rw-r--r--net/sctp/socket.c8
-rw-r--r--net/sctp/sysctl.c9
-rw-r--r--net/socket.c2
-rw-r--r--net/switchdev/switchdev.c14
-rw-r--r--net/tipc/bcast.c126
-rw-r--r--net/tipc/bcast.h1
-rw-r--r--net/tipc/bearer.c140
-rw-r--r--net/tipc/bearer.h8
-rw-r--r--net/tipc/core.h5
-rw-r--r--net/tipc/discover.c38
-rw-r--r--net/tipc/link.c626
-rw-r--r--net/tipc/link.h175
-rw-r--r--net/tipc/name_distr.c68
-rw-r--r--net/tipc/name_distr.h1
-rw-r--r--net/tipc/name_table.c5
-rw-r--r--net/tipc/netlink.c8
-rw-r--r--net/tipc/netlink_compat.c8
-rw-r--r--net/tipc/node.c875
-rw-r--r--net/tipc/node.h127
-rw-r--r--net/tipc/socket.c4
-rw-r--r--net/tipc/udp_media.c17
-rw-r--r--net/unix/af_unix.c67
-rw-r--r--net/unix/garbage.c13
-rw-r--r--net/vmw_vsock/vmci_transport.h2
-rw-r--r--net/vmw_vsock/vmci_transport_notify.c2
-rw-r--r--net/vmw_vsock/vmci_transport_notify.h5
-rw-r--r--net/vmw_vsock/vmci_transport_notify_qstate.c2
-rw-r--r--net/wireless/core.h7
-rw-r--r--net/wireless/lib80211_crypt_ccmp.c4
-rw-r--r--net/wireless/lib80211_crypt_tkip.c4
-rw-r--r--net/wireless/nl80211.c62
-rw-r--r--net/wireless/ocb.c3
-rw-r--r--net/wireless/rdev-ops.h51
-rw-r--r--net/wireless/reg.c98
-rw-r--r--net/wireless/trace.h103
-rw-r--r--net/wireless/util.c121
-rw-r--r--tools/testing/selftests/net/.gitignore1
-rw-r--r--tools/testing/selftests/net/Makefile2
-rw-r--r--tools/testing/selftests/net/reuseport_bpf.c514
1932 files changed, 258479 insertions, 213336 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
index 5cedf72df358..f7be0e88b139 100644
--- a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
+++ b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
@@ -19,6 +19,25 @@ Description:
Set to 0 to pad all frames. Set greater than tx_max to
disable all padding.
+What: /sys/class/net/<iface>/cdc_ncm/ndp_to_end
+Date: Dec 2015
+KernelVersion: 4.5
+Contact: Bjørn Mork <bjorn@mork.no>
+Description:
+ Boolean attribute showing the status of the "NDP to
+ end" quirk. Defaults to 'N', except for devices
+ already known to need it enabled.
+
+ The "NDP to end" quirk makes the driver place the NDP
+ (the packet index table) after the payload. The NCM
+ specification does not mandate this, but some devices
+ are known to be more restrictive. Write 'Y' to this
+ attribute for temporary testing of a suspect device
+ failing to work with the default driver settings.
+
+ A device entry should be added to the driver if this
+ quirk is found to be required.
+
What: /sys/class/net/<iface>/cdc_ncm/rx_max
Date: May 2014
KernelVersion: 3.16
diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh
index c46406296631..c2b956d44a95 100644
--- a/Documentation/ABI/testing/sysfs-class-net-mesh
+++ b/Documentation/ABI/testing/sysfs-class-net-mesh
@@ -8,7 +8,7 @@ Description:
What: /sys/class/net/<mesh_iface>/mesh/<vlan_subdir>/ap_isolation
Date: May 2011
-Contact: Antonio Quartulli <antonio@meshcoding.com>
+Contact: Antonio Quartulli <a@unstable.cc>
Description:
Indicates whether the data traffic going from a
wireless client to another wireless client will be
@@ -70,7 +70,7 @@ Description:
What: /sys/class/net/<mesh_iface>/mesh/isolation_mark
Date: Nov 2013
-Contact: Antonio Quartulli <antonio@meshcoding.com>
+Contact: Antonio Quartulli <a@unstable.cc>
Description:
Defines the isolation mark (and its bitmask) which
is used to classify clients as "isolated" by the
diff --git a/Documentation/ABI/testing/sysfs-class-net-qmi b/Documentation/ABI/testing/sysfs-class-net-qmi
new file mode 100644
index 000000000000..fa5a00bb1143
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-net-qmi
@@ -0,0 +1,23 @@
+What: /sys/class/net/<iface>/qmi/raw_ip
+Date: Dec 2015
+KernelVersion: 4.4
+Contact: Bjørn Mork <bjorn@mork.no>
+Description:
+ Boolean. Default: 'N'
+
+ Set this to 'Y' to change the network device link
+ framing from '802.3' to 'raw-ip'.
+
+ The netdev will change to reflect the link framing
+ mode. The netdev is an ordinary ethernet device in
+ '802.3' mode, and the driver expects to exchange
+ frames with an ethernet header over the USB link. The
+ netdev is a headerless p-t-p device in 'raw-ip' mode,
+ and the driver expects to echange IPv4 or IPv6 packets
+ without any L2 header over the USB link.
+
+ Userspace is in full control of firmware configuration
+ through the delegation of the QMI protocol. Userspace
+ is responsible for coordination of driver and firmware
+ link framing mode, changing this setting to 'Y' if the
+ firmware is configured for 'raw-ip' mode.
diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.txt b/Documentation/devicetree/bindings/net/dsa/dsa.txt
index 04e6bef3ac3f..5fdbbcdf8c4b 100644
--- a/Documentation/devicetree/bindings/net/dsa/dsa.txt
+++ b/Documentation/devicetree/bindings/net/dsa/dsa.txt
@@ -31,6 +31,8 @@ A switch child node has the following optional property:
switch. Must be set if the switch can not detect
the presence and/or size of a connected EEPROM,
otherwise optional.
+- reset-gpios : phandle and specifier to a gpio line connected to
+ reset pin of the switch chip.
A switch may have multiple "port" children nodes
@@ -114,6 +116,7 @@ Example:
#size-cells = <0>;
reg = <17 1>; /* MDIO address 17, switch 1 in tree */
mii-bus = <&mii_bus1>;
+ reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
switch1port0: port@0 {
reg = <0>;
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
index 9c23fdf25018..4a7ede9657b0 100644
--- a/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
+++ b/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
@@ -1,7 +1,12 @@
Hisilicon MDIO bus controller
Properties:
-- compatible: "hisilicon,mdio","hisilicon,hns-mdio".
+- compatible: can be one of:
+ "hisilicon,hns-mdio"
+ "hisilicon,mdio"
+ "hisilicon,hns-mdio" is recommended to be used for hip05 and later SOCs,
+ while "hisilicon,mdio" is optional for backwards compatibility only on
+ hip04 Soc.
- reg: The base address of the MDIO bus controller register bank.
- #address-cells: Must be <1>.
- #size-cells: Must be <0>. MDIO addresses have no size component.
diff --git a/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt b/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
new file mode 100644
index 000000000000..dea5124cdc52
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
@@ -0,0 +1,18 @@
+* ADF7242 IEEE 802.15.4 *
+
+Required properties:
+ - compatible: should be "adi,adf7242"
+ - spi-max-frequency: maximal bus speed (12.5 MHz)
+ - reg: the chipselect index
+ - interrupts: the interrupt generated by the device via pin IRQ1.
+ IRQ_TYPE_LEVEL_HIGH (4) or IRQ_TYPE_EDGE_FALLING (1)
+
+Example:
+
+ adf7242@0 {
+ compatible = "adi,adf7242";
+ spi-max-frequency = <10000000>;
+ reg = <0>;
+ interrupts = <98 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&gpio3>;
+ };
diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt
index b5d79761ac97..5c397ca14cfe 100644
--- a/Documentation/devicetree/bindings/net/macb.txt
+++ b/Documentation/devicetree/bindings/net/macb.txt
@@ -4,6 +4,7 @@ Required properties:
- compatible: Should be "cdns,[<chip>-]{macb|gem}"
Use "cdns,at91sam9260-macb" for Atmel at91sam9 SoCs or the 10/100Mbit IP
available on sama5d3 SoCs.
+ Use "cdns,np4-macb" for NP4 SoC devices.
Use "cdns,at32ap7000-macb" for other 10/100 usage or use the generic form: "cdns,macb".
Use "cdns,pc302-gem" for Picochip picoXcell pc302 and later devices based on
the Cadence GEM, or the generic form: "cdns,gem".
@@ -19,6 +20,9 @@ Required properties:
Optional elements: 'tx_clk'
- clocks: Phandles to input clocks.
+Optional properties for PHY child node:
+- reset-gpios : Should specify the gpio for phy reset
+
Examples:
macb0: ethernet@fffc4000 {
@@ -29,4 +33,8 @@ Examples:
local-mac-address = [3a 0e 03 04 05 06];
clock-names = "pclk", "hclk", "tx_clk";
clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>;
+ ethernet-phy@1 {
+ reg = <0x1>;
+ reset-gpios = <&pioE 6 1>;
+ };
};
diff --git a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
index 692076fda0e5..f9c32adab5c6 100644
--- a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
+++ b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
@@ -1,8 +1,9 @@
Micrel KSZ9021/KSZ9031 Gigabit Ethernet PHY
-Some boards require special tuning values, particularly when it comes to
-clock delays. You can specify clock delay values by adding
-micrel-specific properties to an Ethernet OF device node.
+Some boards require special tuning values, particularly when it comes
+to clock delays. You can specify clock delay values in the PHY OF
+device node. Deprecated, but still supported, these properties can
+also be added to an Ethernet OF device node.
Note that these settings are applied after any phy-specific fixup from
phy_fixup_list (see phy_init_hw() from drivers/net/phy/phy_device.c),
@@ -57,16 +58,6 @@ KSZ9031:
Examples:
- /* Attach to an Ethernet device with autodetected PHY */
- &enet {
- rxc-skew-ps = <3000>;
- rxdv-skew-ps = <0>;
- txc-skew-ps = <3000>;
- txen-skew-ps = <0>;
- status = "okay";
- };
-
- /* Attach to an explicitly-specified PHY */
mdio {
phy0: ethernet-phy@0 {
rxc-skew-ps = <3000>;
diff --git a/Documentation/devicetree/bindings/net/nfc/st95hf.txt b/Documentation/devicetree/bindings/net/nfc/st95hf.txt
new file mode 100644
index 000000000000..ea3178bc9ddd
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/nfc/st95hf.txt
@@ -0,0 +1,50 @@
+* STMicroelectronics : NFC Transceiver ST95HF
+
+ST NFC Transceiver is required to attach with SPI bus.
+ST95HF node should be defined in DT as SPI slave device of SPI
+master with which ST95HF transceiver is physically connected.
+The properties defined below are required to be the part of DT
+to include ST95HF transceiver into the platform.
+
+Required properties:
+===================
+- reg: Address of SPI slave "ST95HF transceiver" on SPI master bus.
+
+- compatible: should be "st,st95hf" for ST95HF NFC transceiver
+
+- spi-max-frequency: Max. operating SPI frequency for ST95HF
+ transceiver.
+
+- enable-gpio: GPIO line to enable ST95HF transceiver.
+
+- interrupt-parent : Standard way to specify the controller to which
+ ST95HF transceiver's interrupt is routed.
+
+- interrupts : Standard way to define ST95HF transceiver's out
+ interrupt.
+
+Optional property:
+=================
+- st95hfvin-supply : This is an optional property. It contains a
+ phandle to ST95HF transceiver's regulator supply node in DT.
+
+Example:
+=======
+spi@9840000 {
+ reg = <0x9840000 0x110>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cs-gpios = <&pio0 4>;
+ status = "okay";
+
+ st95hf@0{
+ reg = <0>;
+ compatible = "st,st95hf";
+ status = "okay";
+ spi-max-frequency = <1000000>;
+ enable-gpio = <&pio4 0>;
+ interrupt-parent = <&pio0>;
+ interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+ };
+
+};
diff --git a/Documentation/devicetree/bindings/net/renesas,ravb.txt b/Documentation/devicetree/bindings/net/renesas,ravb.txt
index b486f3f5f6a3..81a9f9e6b45f 100644
--- a/Documentation/devicetree/bindings/net/renesas,ravb.txt
+++ b/Documentation/devicetree/bindings/net/renesas,ravb.txt
@@ -5,8 +5,18 @@ interface contains.
Required properties:
- compatible: "renesas,etheravb-r8a7790" if the device is a part of R8A7790 SoC.
+ "renesas,etheravb-r8a7791" if the device is a part of R8A7791 SoC.
+ "renesas,etheravb-r8a7792" if the device is a part of R8A7792 SoC.
+ "renesas,etheravb-r8a7793" if the device is a part of R8A7793 SoC.
"renesas,etheravb-r8a7794" if the device is a part of R8A7794 SoC.
"renesas,etheravb-r8a7795" if the device is a part of R8A7795 SoC.
+ "renesas,etheravb-rcar-gen2" for generic R-Car Gen 2 compatible interface.
+ "renesas,etheravb-rcar-gen3" for generic R-Car Gen 3 compatible interface.
+
+ When compatible with the generic version, nodes must list the
+ SoC-specific version corresponding to the platform first
+ followed by the generic version.
+
- reg: offset and length of (1) the register block and (2) the stream buffer.
- interrupts: A list of interrupt-specifiers, one for each entry in
interrupt-names.
@@ -37,7 +47,7 @@ Optional properties:
Example:
ethernet@e6800000 {
- compatible = "renesas,etheravb-r8a7795";
+ compatible = "renesas,etheravb-r8a7795", "renesas,etheravb-rcar-gen3";
reg = <0 0xe6800000 0 0x800>, <0 0xe6a00000 0 0x10000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
index 3a9d67951606..72d82d684342 100644
--- a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
+++ b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
@@ -11,6 +11,8 @@ Required properties:
designware version numbers documented in stmmac.txt
- altr,sysmgr-syscon : Should be the phandle to the system manager node that
encompasses the glue register, the register offset, and the register shift.
+ - altr,f2h_ptp_ref_clk use f2h_ptp_ref_clk instead of default eosc1 clock
+ for ptp ref clk. This affects all emacs as the clock is common.
Optional properties:
altr,emac-splitter: Should be the phandle to the emac splitter soft IP node if
diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
index f34fc3c81a75..e862a922bd3f 100644
--- a/Documentation/devicetree/bindings/net/stmmac.txt
+++ b/Documentation/devicetree/bindings/net/stmmac.txt
@@ -35,18 +35,18 @@ Optional properties:
- reset-names: Should contain the reset signal name "stmmaceth", if a
reset phandle is given
- max-frame-size: See ethernet.txt file in the same directory
-- clocks: If present, the first clock should be the GMAC main clock and
- the second clock should be peripheral's register interface clock. Further
- clocks may be specified in derived bindings.
-- clock-names: One name for each entry in the clocks property, the
- first one should be "stmmaceth" and the second one should be "pclk".
-- clk_ptp_ref: this is the PTP reference clock; in case of the PTP is
- available this clock is used for programming the Timestamp Addend Register.
- If not passed then the system clock will be used and this is fine on some
- platforms.
+- clocks: If present, the first clock should be the GMAC main clock
+ The optional second clock should be peripheral's register interface clock.
+ The third optional clock should be the ptp reference clock.
+ Further clocks may be specified in derived bindings.
+- clock-names: One name for each entry in the clocks property.
+ The first one should be "stmmaceth".
+ The optional second one should be "pclk".
+ The optional third one should be "clk_ptp_ref".
- snps,burst_len: The AXI burst lenth value of the AXI BUS MODE register.
- tx-fifo-depth: See ethernet.txt file in the same directory
- rx-fifo-depth: See ethernet.txt file in the same directory
+- mdio: with compatible = "snps,dwmac-mdio", create and register mdio bus.
Examples:
@@ -65,4 +65,11 @@ Examples:
tx-fifo-depth = <16384>;
clocks = <&clock>;
clock-names = "stmmaceth";
+ mdio0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,dwmac-mdio";
+ phy1: ethernet-phy@0 {
+ };
+ };
};
diff --git a/Documentation/fault-injection/notifier-error-inject.txt b/Documentation/fault-injection/notifier-error-inject.txt
index 09adabef513f..83d3f4e43e91 100644
--- a/Documentation/fault-injection/notifier-error-inject.txt
+++ b/Documentation/fault-injection/notifier-error-inject.txt
@@ -10,6 +10,7 @@ modules that can be used to test the following notifiers.
* PM notifier
* Memory hotplug notifier
* powerpc pSeries reconfig notifier
+ * Netdevice notifier
CPU notifier error injection module
-----------------------------------
@@ -87,6 +88,30 @@ Possible pSeries reconfig notifier events to be failed are:
* PSERIES_DRCONF_MEM_ADD
* PSERIES_DRCONF_MEM_REMOVE
+Netdevice notifier error injection module
+----------------------------------------------
+This feature is controlled through debugfs interface
+/sys/kernel/debug/notifier-error-inject/netdev/actions/<notifier event>/error
+
+Netdevice notifier events which can be failed are:
+
+ * NETDEV_REGISTER
+ * NETDEV_CHANGEMTU
+ * NETDEV_CHANGENAME
+ * NETDEV_PRE_UP
+ * NETDEV_PRE_TYPE_CHANGE
+ * NETDEV_POST_INIT
+ * NETDEV_PRECHANGEMTU
+ * NETDEV_PRECHANGEUPPER
+ * NETDEV_CHANGEUPPER
+
+Example: Inject netdevice mtu change error (-22 == -EINVAL)
+
+ # cd /sys/kernel/debug/notifier-error-inject/netdev
+ # echo -22 > actions/NETDEV_CHANGEMTU/error
+ # ip link set eth0 mtu 1024
+ RTNETLINK answers: Invalid argument
+
For more usage examples
-----------------------
There are tools/testing/selftests using the notifier error injection features
diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt
index 58e49042fc20..ff23b755f5e4 100644
--- a/Documentation/networking/batman-adv.txt
+++ b/Documentation/networking/batman-adv.txt
@@ -115,14 +115,17 @@ The "bat0" interface can be used like any other regular inter-
face. It needs an IP address which can be either statically con-
figured or dynamically (by using DHCP or similar services):
-# NodeA: ifconfig bat0 192.168.0.1
-# NodeB: ifconfig bat0 192.168.0.2
+# NodeA: ip link set up dev bat0
+# NodeA: ip addr add 192.168.0.1/24 dev bat0
+
+# NodeB: ip link set up dev bat0
+# NodeB: ip addr add 192.168.0.2/24 dev bat0
# NodeB: ping 192.168.0.1
Note: In order to avoid problems remove all IP addresses previ-
ously assigned to interfaces now used by batman advanced, e.g.
-# ifconfig eth0 0.0.0.0
+# ip addr flush dev eth0
LOGGING/DEBUGGING
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 2ea4c45cf1c8..ceb44a095a27 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -335,6 +335,14 @@ tcp_keepalive_intvl - INTEGER
after probes started. Default value: 75sec i.e. connection
will be aborted after ~11 minutes of retries.
+tcp_l3mdev_accept - BOOLEAN
+ Enables child sockets to inherit the L3 master device index.
+ Enabling this option allows a "global" listen socket to work
+ across L3 master domains (e.g., VRFs) with connected sockets
+ derived from the listen socket to be bound to the L3 domain in
+ which the packets originated. Only valid when the kernel was
+ compiled with CONFIG_NET_L3_MASTER_DEV.
+
tcp_low_latency - BOOLEAN
If set, the TCP stack makes decisions that prefer lower
latency as opposed to higher throughput. By default, this
@@ -1723,6 +1731,25 @@ addip_enable - BOOLEAN
Default: 0
+pf_enable - INTEGER
+ Enable or disable pf (pf is short for potentially failed) state. A value
+ of pf_retrans > path_max_retrans also disables pf state. That is, one of
+ both pf_enable and pf_retrans > path_max_retrans can disable pf state.
+ Since pf_retrans and path_max_retrans can be changed by userspace
+ application, sometimes user expects to disable pf state by the value of
+ pf_retrans > path_max_retrans, but occasionally the value of pf_retrans
+ or path_max_retrans is changed by the user application, this pf state is
+ enabled. As such, it is necessary to add this to dynamically enable
+ and disable pf state. See:
+ https://datatracker.ietf.org/doc/draft-ietf-tsvwg-sctp-failover for
+ details.
+
+ 1: Enable pf.
+
+ 0: Disable pf.
+
+ Default: 1
+
addip_noauth_enable - BOOLEAN
Dynamic Address Reconfiguration (ADD-IP) requires the use of
authentication to protect the operations of adding or removing new
@@ -1799,7 +1826,9 @@ pf_retrans - INTEGER
having to reduce path_max_retrans to a very low value. See:
http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
for details. Note also that a value of pf_retrans > path_max_retrans
- disables this feature
+ disables this feature. Since both pf_retrans and path_max_retrans can
+ be changed by userspace application, a variable pf_enable is used to
+ disable pf state.
Default: 0
diff --git a/Documentation/networking/switchdev.txt b/Documentation/networking/switchdev.txt
index 91994134efca..fad63136ee3e 100644
--- a/Documentation/networking/switchdev.txt
+++ b/Documentation/networking/switchdev.txt
@@ -304,8 +304,12 @@ certain netdevs from flooding unicast traffic for which there is no FDB entry.
IGMP Snooping
^^^^^^^^^^^^^
-XXX: complete this section
-
+In order to support IGMP snooping, the port netdevs should trap to the bridge
+driver all IGMP join and leave messages.
+The bridge multicast module will notify port netdevs on every multicast group
+changed whether it is static configured or dynamically joined/leave.
+The hardware implementation should be forwarding all registered multicast
+traffic groups only to the configured ports.
L3 Routing Offload
------------------
diff --git a/MAINTAINERS b/MAINTAINERS
index 0788854baadb..14ef6ec44ef9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -371,6 +371,15 @@ ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR)
M: Jiri Kosina <jikos@kernel.org>
S: Maintained
+ADF7242 IEEE 802.15.4 RADIO DRIVER
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: https://wiki.analog.com/ADF7242
+W: http://ez.analog.com/community/linux-device-drivers
+L: linux-wpan@vger.kernel.org
+S: Supported
+F: drivers/net/ieee802154/adf7242.c
+F: Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
+
ADM1025 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
L: lm-sensors@lm-sensors.org
@@ -388,7 +397,7 @@ ADM8211 WIRELESS DRIVER
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
S: Orphan
-F: drivers/net/wireless/adm8211.*
+F: drivers/net/wireless/admtek/adm8211.*
ADP1653 FLASH CONTROLLER DRIVER
M: Sakari Ailus <sakari.ailus@iki.fi>
@@ -1992,7 +2001,7 @@ L: linux-wireless@vger.kernel.org
W: http://www.thekelleys.org.uk/atmel
W: http://atmelwlandriver.sourceforge.net/
S: Maintained
-F: drivers/net/wireless/atmel*
+F: drivers/net/wireless/atmel/atmel*
ATMEL MAXTOUCH DRIVER
M: Nick Dyer <nick.dyer@itdev.co.uk>
@@ -2081,7 +2090,7 @@ L: linux-wireless@vger.kernel.org
L: b43-dev@lists.infradead.org
W: http://wireless.kernel.org/en/users/Drivers/b43
S: Odd Fixes
-F: drivers/net/wireless/b43/
+F: drivers/net/wireless/broadcom/b43/
B43LEGACY WIRELESS DRIVER
M: Larry Finger <Larry.Finger@lwfinger.net>
@@ -2089,7 +2098,7 @@ L: linux-wireless@vger.kernel.org
L: b43-dev@lists.infradead.org
W: http://wireless.kernel.org/en/users/Drivers/b43
S: Maintained
-F: drivers/net/wireless/b43legacy/
+F: drivers/net/wireless/broadcom/b43legacy/
BACKLIGHT CLASS/SUBSYSTEM
M: Jingoo Han <jingoohan1@gmail.com>
@@ -2101,7 +2110,7 @@ F: include/linux/backlight.h
BATMAN ADVANCED
M: Marek Lindner <mareklindner@neomailbox.ch>
M: Simon Wunderlich <sw@simonwunderlich.de>
-M: Antonio Quartulli <antonio@meshcoding.com>
+M: Antonio Quartulli <a@unstable.cc>
L: b.a.t.m.a.n@lists.open-mesh.org
W: http://www.open-mesh.org/
S: Maintained
@@ -2391,7 +2400,7 @@ M: Hante Meuleman <meuleman@broadcom.com>
L: linux-wireless@vger.kernel.org
L: brcm80211-dev-list@broadcom.com
S: Supported
-F: drivers/net/wireless/brcm80211/
+F: drivers/net/wireless/broadcom/brcm80211/
BROADCOM BNX2FC 10 GIGABIT FCOE DRIVER
M: QLogic-Storage-Upstream@qlogic.com
@@ -3113,7 +3122,7 @@ F: sound/pci/cs5535audio/
CW1200 WLAN driver
M: Solomon Peachy <pizza@shaftnet.org>
S: Maintained
-F: drivers/net/wireless/cw1200/
+F: drivers/net/wireless/st/cw1200/
CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
@@ -5035,7 +5044,7 @@ L: hostap@shmoo.com (subscribers-only)
L: linux-wireless@vger.kernel.org
W: http://hostap.epitest.fi/
S: Maintained
-F: drivers/net/wireless/hostap/
+F: drivers/net/wireless/intersil/hostap/
HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER
L: platform-driver-x86@vger.kernel.org
@@ -5273,6 +5282,13 @@ L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/ibm/ibmveth.*
+IBM Power SRIOV Virtual NIC Device Driver
+M: Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
+M: John Allen <jallen@linux.vnet.ibm.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/ibm/ibmvnic.*
+
IBM Power Virtual SCSI Device Drivers
M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
L: linux-scsi@vger.kernel.org
@@ -5603,7 +5619,7 @@ L: linux-wireless@vger.kernel.org
S: Maintained
F: Documentation/networking/README.ipw2100
F: Documentation/networking/README.ipw2200
-F: drivers/net/wireless/ipw2x00/
+F: drivers/net/wireless/intel/ipw2x00/
INTEL(R) TRACE HUB
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
@@ -5635,7 +5651,7 @@ INTEL WIRELESS 3945ABG/BG, 4965AGN (iwlegacy)
M: Stanislaw Gruszka <sgruszka@redhat.com>
L: linux-wireless@vger.kernel.org
S: Supported
-F: drivers/net/wireless/iwlegacy/
+F: drivers/net/wireless/intel/iwlegacy/
INTEL WIRELESS WIFI LINK (iwlwifi)
M: Johannes Berg <johannes.berg@intel.com>
@@ -5645,7 +5661,7 @@ L: linux-wireless@vger.kernel.org
W: http://intellinuxwireless.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
S: Supported
-F: drivers/net/wireless/iwlwifi/
+F: drivers/net/wireless/intel/iwlwifi/
INTEL MANAGEMENT ENGINE (mei)
M: Tomas Winkler <tomas.winkler@intel.com>
@@ -6697,7 +6713,7 @@ F: drivers/net/ethernet/marvell/sk*
MARVELL LIBERTAS WIRELESS DRIVER
L: libertas-dev@lists.infradead.org
S: Orphan
-F: drivers/net/wireless/libertas/
+F: drivers/net/wireless/marvell/libertas/
MARVELL MV643XX ETHERNET DRIVER
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
@@ -6717,13 +6733,13 @@ M: Amitkumar Karwar <akarwar@marvell.com>
M: Nishant Sarmukadam <nishants@marvell.com>
L: linux-wireless@vger.kernel.org
S: Maintained
-F: drivers/net/wireless/mwifiex/
+F: drivers/net/wireless/marvell/mwifiex/
MARVELL MWL8K WIRELESS DRIVER
M: Lennert Buytenhek <buytenh@wantstofly.org>
L: linux-wireless@vger.kernel.org
S: Odd Fixes
-F: drivers/net/wireless/mwl8k.c
+F: drivers/net/wireless/marvell/mwl8k.c
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
M: Nicolas Pitre <nico@fluxnic.net>
@@ -7359,6 +7375,13 @@ F: include/net/netrom.h
F: include/uapi/linux/netrom.h
F: net/netrom/
+NETRONOME ETHERNET DRIVERS
+M: Jakub Kicinski <jakub.kicinski@netronome.com>
+M: Rolf Neugebauer <rolf.neugebauer@netronome.com>
+L: oss-drivers@netronome.com
+S: Maintained
+F: drivers/net/ethernet/netronome/
+
NETWORK BLOCK DEVICE (NBD)
M: Markus Pargmann <mpa@pengutronix.de>
S: Maintained
@@ -7490,7 +7513,12 @@ F: net/nfc/
F: include/net/nfc/
F: include/uapi/linux/nfc.h
F: drivers/nfc/
+F: include/linux/platform_data/microread.h
+F: include/linux/platform_data/nfcmrvl.h
+F: include/linux/platform_data/nxp-nci.h
F: include/linux/platform_data/pn544.h
+F: include/linux/platform_data/st21nfca.h
+F: include/linux/platform_data/st-nci.h
F: Documentation/devicetree/bindings/net/nfc/
NFS, SUNRPC, AND LOCKD CLIENTS
@@ -7940,7 +7968,7 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/orinoco
W: http://www.nongnu.org/orinoco/
S: Orphan
-F: drivers/net/wireless/orinoco/
+F: drivers/net/wireless/intersil/orinoco/
OSD LIBRARY and FILESYSTEM
M: Boaz Harrosh <ooo@electrozaur.com>
@@ -7966,7 +7994,7 @@ M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/p54
S: Maintained
-F: drivers/net/wireless/p54/
+F: drivers/net/wireless/intersil/p54/
PA SEMI ETHERNET DRIVER
M: Olof Johansson <olof@lixom.net>
@@ -8517,7 +8545,7 @@ M: "Luis R. Rodriguez" <mcgrof@gmail.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/p54
S: Obsolete
-F: drivers/net/wireless/prism54/
+F: drivers/net/wireless/intersil/prism54/
PS3 NETWORK SUPPORT
M: Geoff Levand <geoff@infradead.org>
@@ -8821,7 +8849,7 @@ M: Stanislaw Gruszka <sgruszka@redhat.com>
M: Helmut Schaa <helmut.schaa@googlemail.com>
L: linux-wireless@vger.kernel.org
S: Maintained
-F: drivers/net/wireless/rt2x00/
+F: drivers/net/wireless/ralink/rt2x00/
RAMDISK RAM BLOCK DEVICE DRIVER
M: Jens Axboe <axboe@kernel.dk>
@@ -9062,7 +9090,7 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Orphan
-F: drivers/net/wireless/rtl818x/rtl8180/
+F: drivers/net/wireless/realtek/rtl818x/rtl8180/
RTL8187 WIRELESS DRIVER
M: Herton Ronaldo Krzesinski <herton@canonical.com>
@@ -9072,7 +9100,7 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Maintained
-F: drivers/net/wireless/rtl818x/rtl8187/
+F: drivers/net/wireless/realtek/rtl818x/rtl8187/
RTL8192CE WIRELESS DRIVER
M: Larry Finger <Larry.Finger@lwfinger.net>
@@ -9081,8 +9109,8 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Maintained
-F: drivers/net/wireless/rtlwifi/
-F: drivers/net/wireless/rtlwifi/rtl8192ce/
+F: drivers/net/wireless/realtek/rtlwifi/
+F: drivers/net/wireless/realtek/rtlwifi/rtl8192ce/
RTL8XXXU WIRELESS DRIVER (rtl8xxxu)
M: Jes Sorensen <Jes.Sorensen@redhat.com>
@@ -9126,7 +9154,7 @@ F: drivers/s390/block/dasd*
F: block/partitions/ibm.c
S390 NETWORK DRIVERS
-M: Ursula Braun <ursula.braun@de.ibm.com>
+M: Ursula Braun <ubraun@linux.vnet.ibm.com>
L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported
@@ -9156,7 +9184,7 @@ S: Supported
F: drivers/s390/scsi/zfcp_*
S390 IUCV NETWORK LAYER
-M: Ursula Braun <ursula.braun@de.ibm.com>
+M: Ursula Braun <ubraun@linux.vnet.ibm.com>
L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported
@@ -11185,6 +11213,13 @@ L: linux-usb@vger.kernel.org
S: Supported
F: drivers/usb/class/usblp.c
+USB QMI WWAN NETWORK DRIVER
+M: Bjørn Mork <bjorn@mork.no>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-class-net-qmi
+F: drivers/net/usb/qmi_wwan.c
+
USB RTL8150 DRIVER
M: Petko Manolov <petkan@nucleusys.com>
L: linux-usb@vger.kernel.org
@@ -11281,7 +11316,7 @@ USB ZD1201 DRIVER
L: linux-wireless@vger.kernel.org
W: http://linux-lc100020.sourceforge.net
S: Orphan
-F: drivers/net/wireless/zd1201.*
+F: drivers/net/wireless/zydas/zd1201.*
USB ZR364XX DRIVER
M: Antoine Jacquet <royale@zerezo.com>
@@ -11912,7 +11947,7 @@ W: http://zd1211.ath.cx/wiki/DriverRewrite
L: linux-wireless@vger.kernel.org
L: zd1211-devs@lists.sourceforge.net (subscribers-only)
S: Maintained
-F: drivers/net/wireless/zd1211rw/
+F: drivers/net/wireless/zydas/zd1211rw/
ZPOOL COMPRESSED PAGE STORAGE API
M: Dan Streetman <ddstreet@ieee.org>
diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index 9a20821b111c..c5fb9e6bc3a5 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h
@@ -92,4 +92,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _UAPI_ASM_SOCKET_H */
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index b162ad70effc..7658612d915c 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -152,8 +152,6 @@ static void build_prologue(struct jit_ctx *ctx)
const u8 r8 = bpf2a64[BPF_REG_8];
const u8 r9 = bpf2a64[BPF_REG_9];
const u8 fp = bpf2a64[BPF_REG_FP];
- const u8 ra = bpf2a64[BPF_REG_A];
- const u8 rx = bpf2a64[BPF_REG_X];
const u8 tmp1 = bpf2a64[TMP_REG_1];
const u8 tmp2 = bpf2a64[TMP_REG_2];
@@ -200,10 +198,6 @@ static void build_prologue(struct jit_ctx *ctx)
/* Set up function call stack */
emit(A64_SUB_I(1, A64_SP, A64_SP, STACK_SIZE), ctx);
-
- /* Clear registers A and X */
- emit_a64_mov_i64(ra, 0, ctx);
- emit_a64_mov_i64(rx, 0, ctx);
}
static void build_epilogue(struct jit_ctx *ctx)
diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h
index 2b65ed6b277c..9de0796240a0 100644
--- a/arch/avr32/include/uapi/asm/socket.h
+++ b/arch/avr32/include/uapi/asm/socket.h
@@ -85,4 +85,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _UAPI__ASM_AVR32_SOCKET_H */
diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h
index 4823ad125578..f02e4849ae83 100644
--- a/arch/frv/include/uapi/asm/socket.h
+++ b/arch/frv/include/uapi/asm/socket.h
@@ -85,5 +85,8 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_SOCKET_H */
diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h
index 59be3d87f86d..bce29166de1b 100644
--- a/arch/ia64/include/uapi/asm/socket.h
+++ b/arch/ia64/include/uapi/asm/socket.h
@@ -94,4 +94,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_IA64_SOCKET_H */
diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h
index 7bc4cb273856..14aa4a6bccf1 100644
--- a/arch/m32r/include/uapi/asm/socket.h
+++ b/arch/m32r/include/uapi/asm/socket.h
@@ -85,4 +85,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_M32R_SOCKET_H */
diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c
index 6d38948f0f1e..c807e32d6d81 100644
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -101,50 +101,13 @@ static void bcm47xx_machine_halt(void)
}
#ifdef CONFIG_BCM47XX_SSB
-static int bcm47xx_get_invariants(struct ssb_bus *bus,
- struct ssb_init_invariants *iv)
-{
- char buf[20];
- int len, err;
-
- /* Fill boardinfo structure */
- memset(&iv->boardinfo, 0 , sizeof(struct ssb_boardinfo));
-
- len = bcm47xx_nvram_getenv("boardvendor", buf, sizeof(buf));
- if (len > 0) {
- err = kstrtou16(strim(buf), 0, &iv->boardinfo.vendor);
- if (err)
- pr_warn("Couldn't parse nvram board vendor entry with value \"%s\"\n",
- buf);
- }
- if (!iv->boardinfo.vendor)
- iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM;
-
- len = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf));
- if (len > 0) {
- err = kstrtou16(strim(buf), 0, &iv->boardinfo.type);
- if (err)
- pr_warn("Couldn't parse nvram board type entry with value \"%s\"\n",
- buf);
- }
-
- memset(&iv->sprom, 0, sizeof(struct ssb_sprom));
- bcm47xx_fill_sprom(&iv->sprom, NULL, false);
-
- if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
- iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10);
-
- return 0;
-}
-
static void __init bcm47xx_register_ssb(void)
{
int err;
char buf[100];
struct ssb_mipscore *mcore;
- err = ssb_bus_ssbbus_register(&bcm47xx_bus.ssb, SSB_ENUM_BASE,
- bcm47xx_get_invariants);
+ err = ssb_bus_host_soc_register(&bcm47xx_bus.ssb, SSB_ENUM_BASE);
if (err)
panic("Failed to initialize SSB bus (err %d)", err);
diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
index dec3c850f36b..5910fe294e93 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h
@@ -103,4 +103,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _UAPI_ASM_SOCKET_H */
diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h
index cab7d6d50051..58b1aa01ab9f 100644
--- a/arch/mn10300/include/uapi/asm/socket.h
+++ b/arch/mn10300/include/uapi/asm/socket.h
@@ -85,4 +85,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_SOCKET_H */
diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
index a5cd40cd8ee1..f9cf1223422c 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h
@@ -84,4 +84,7 @@
#define SO_ATTACH_BPF 0x402B
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 0x402C
+#define SO_ATTACH_REUSEPORT_EBPF 0x402D
+
#endif /* _UAPI_ASM_SOCKET_H */
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 85bc8c0d257b..e3b54dd4f730 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -258,11 +258,16 @@
#define H_DEL_CONN 0x288
#define H_JOIN 0x298
#define H_VASI_STATE 0x2A4
+#define H_VIOCTL 0x2A8
#define H_ENABLE_CRQ 0x2B0
#define H_GET_EM_PARMS 0x2B8
#define H_SET_MPP 0x2D0
#define H_GET_MPP 0x2D4
+#define H_REG_SUB_CRQ 0x2DC
#define H_HOME_NODE_ASSOCIATIVITY 0x2EC
+#define H_FREE_SUB_CRQ 0x2E0
+#define H_SEND_SUB_CRQ 0x2E4
+#define H_SEND_SUB_CRQ_INDIRECT 0x2E8
#define H_BEST_ENERGY 0x2F4
#define H_XIRR_X 0x2FC
#define H_RANDOM 0x300
@@ -271,6 +276,21 @@
#define H_SET_MODE 0x31C
#define MAX_HCALL_OPCODE H_SET_MODE
+/* H_VIOCTL functions */
+#define H_GET_VIOA_DUMP_SIZE 0x01
+#define H_GET_VIOA_DUMP 0x02
+#define H_GET_ILLAN_NUM_VLAN_IDS 0x03
+#define H_GET_ILLAN_VLAN_ID_LIST 0x04
+#define H_GET_ILLAN_SWITCH_ID 0x05
+#define H_DISABLE_MIGRATION 0x06
+#define H_ENABLE_MIGRATION 0x07
+#define H_GET_PARTNER_INFO 0x08
+#define H_GET_PARTNER_WWPN_LIST 0x09
+#define H_DISABLE_ALL_VIO_INTS 0x0A
+#define H_DISABLE_VIO_INTERRUPT 0x0B
+#define H_ENABLE_VIO_INTERRUPT 0x0C
+
+
/* Platform specific hcalls, used by KVM */
#define H_RTAS 0xf000
diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h
index c046666038f8..dd54f28ecdec 100644
--- a/arch/powerpc/include/uapi/asm/socket.h
+++ b/arch/powerpc/include/uapi/asm/socket.h
@@ -92,4 +92,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_POWERPC_SOCKET_H */
diff --git a/arch/powerpc/platforms/82xx/ep8248e.c b/arch/powerpc/platforms/82xx/ep8248e.c
index a0cb8bd41958..6781bda117be 100644
--- a/arch/powerpc/platforms/82xx/ep8248e.c
+++ b/arch/powerpc/platforms/82xx/ep8248e.c
@@ -131,23 +131,15 @@ static int ep8248e_mdio_probe(struct platform_device *ofdev)
if (!bus)
return -ENOMEM;
- bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (bus->irq == NULL) {
- ret = -ENOMEM;
- goto err_free_bus;
- }
-
bus->name = "ep8248e-mdio-bitbang";
bus->parent = &ofdev->dev;
snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start);
ret = of_mdiobus_register(bus, ofdev->dev.of_node);
if (ret)
- goto err_free_irq;
+ goto err_free_bus;
return 0;
-err_free_irq:
- kfree(bus->irq);
err_free_bus:
free_mdio_bitbang(bus);
return ret;
diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c
index ae3f47b25b18..ddf635000c6b 100644
--- a/arch/powerpc/platforms/pasemi/gpio_mdio.c
+++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c
@@ -41,7 +41,6 @@ static void __iomem *gpio_regs;
struct gpio_priv {
int mdc_pin;
int mdio_pin;
- int mdio_irqs[PHY_MAX_ADDR];
};
#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin)
@@ -245,8 +244,6 @@ static int gpio_mdio_probe(struct platform_device *ofdev)
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
new_bus->priv = priv;
- new_bus->irq = priv->mdio_irqs;
-
prop = of_get_property(np, "mdc-pin", NULL);
priv->mdc_pin = *prop;
diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h
index 296942d56e6a..d02e89d14fef 100644
--- a/arch/s390/include/uapi/asm/socket.h
+++ b/arch/s390/include/uapi/asm/socket.h
@@ -91,4 +91,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_SOCKET_H */
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 9a0c4c22e536..3c0bfc1f2694 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -408,7 +408,7 @@ static void emit_load_skb_data_hlen(struct bpf_jit *jit)
* Save registers and create stack frame if necessary.
* See stack frame layout desription in "bpf_jit.h"!
*/
-static void bpf_jit_prologue(struct bpf_jit *jit, bool is_classic)
+static void bpf_jit_prologue(struct bpf_jit *jit)
{
if (jit->seen & SEEN_TAIL_CALL) {
/* xc STK_OFF_TCCNT(4,%r15),STK_OFF_TCCNT(%r15) */
@@ -448,15 +448,6 @@ static void bpf_jit_prologue(struct bpf_jit *jit, bool is_classic)
/* stg %b1,ST_OFF_SKBP(%r0,%r15) */
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, REG_15,
STK_OFF_SKBP);
- /* Clear A (%b0) and X (%b7) registers for converted BPF programs */
- if (is_classic) {
- if (REG_SEEN(BPF_REG_A))
- /* lghi %ba,0 */
- EMIT4_IMM(0xa7090000, BPF_REG_A, 0);
- if (REG_SEEN(BPF_REG_X))
- /* lghi %bx,0 */
- EMIT4_IMM(0xa7090000, BPF_REG_X, 0);
- }
}
/*
@@ -1245,7 +1236,7 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp)
jit->lit = jit->lit_start;
jit->prg = 0;
- bpf_jit_prologue(jit, bpf_prog_was_classic(fp));
+ bpf_jit_prologue(jit);
for (i = 0; i < fp->len; i += insn_count) {
insn_count = bpf_jit_insn(jit, fp, i);
if (insn_count < 0)
diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
index e6a16c40be5f..d270ee91968e 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h
@@ -81,6 +81,9 @@
#define SO_ATTACH_BPF 0x0034
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 0x0035
+#define SO_ATTACH_REUSEPORT_EBPF 0x0036
+
/* Security levels - as per NRL IPv6 - don't actually do anything */
#define SO_SECURITY_AUTHENTICATION 0x5001
#define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c
index 6bd75012109d..f95dd11b75ea 100644
--- a/arch/sparc/kernel/idprom.c
+++ b/arch/sparc/kernel/idprom.c
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/export.h>
+#include <linux/etherdevice.h>
#include <asm/oplib.h>
#include <asm/idprom.h>
@@ -60,6 +61,12 @@ static void __init display_system_type(unsigned char machtype)
{
}
#endif
+
+unsigned char *arch_get_platform_mac_address(void)
+{
+ return idprom->id_ethaddr;
+}
+
/* Calculate the IDPROM checksum (xor of the data bytes). */
static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
{
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 75991979f667..4286f3618bd0 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -193,7 +193,7 @@ struct jit_context {
32 /* space for rbx, r13, r14, r15 */ + \
8 /* space for skb_copy_bits() buffer */)
-#define PROLOGUE_SIZE 51
+#define PROLOGUE_SIZE 48
/* emit x64 prologue code for BPF program and check it's size.
* bpf_tail_call helper will skip it while jumping into another program
@@ -229,11 +229,15 @@ static void emit_prologue(u8 **pprog)
/* mov qword ptr [rbp-X],r15 */
EMIT3_off32(0x4C, 0x89, 0xBD, -STACKSIZE + 24);
- /* clear A and X registers */
- EMIT2(0x31, 0xc0); /* xor eax, eax */
- EMIT3(0x4D, 0x31, 0xED); /* xor r13, r13 */
+ /* Clear the tail call counter (tail_call_cnt): for eBPF tail calls
+ * we need to reset the counter to 0. It's done in two instructions,
+ * resetting rax register to 0 (xor on eax gets 0 extended), and
+ * moving it to the counter location.
+ */
- /* clear tail_cnt: mov qword ptr [rbp-X], rax */
+ /* xor eax, eax */
+ EMIT2(0x31, 0xc0);
+ /* mov qword ptr [rbp-X], rax */
EMIT3_off32(0x48, 0x89, 0x85, -STACKSIZE + 32);
BUILD_BUG_ON(cnt != PROLOGUE_SIZE);
@@ -455,6 +459,18 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
}
case BPF_ALU | BPF_MOV | BPF_K:
+ /* optimization: if imm32 is zero, use 'xor <dst>,<dst>'
+ * to save 3 bytes.
+ */
+ if (imm32 == 0) {
+ if (is_ereg(dst_reg))
+ EMIT1(add_2mod(0x40, dst_reg, dst_reg));
+ b2 = 0x31; /* xor */
+ b3 = 0xC0;
+ EMIT2(b2, add_2reg(b3, dst_reg, dst_reg));
+ break;
+ }
+
/* mov %eax, imm32 */
if (is_ereg(dst_reg))
EMIT1(add_1mod(0x40, dst_reg));
@@ -469,6 +485,20 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
return -EINVAL;
}
+ /* optimization: if imm64 is zero, use 'xor <dst>,<dst>'
+ * to save 7 bytes.
+ */
+ if (insn[0].imm == 0 && insn[1].imm == 0) {
+ b1 = add_2mod(0x48, dst_reg, dst_reg);
+ b2 = 0x31; /* xor */
+ b3 = 0xC0;
+ EMIT3(b1, b2, add_2reg(b3, dst_reg, dst_reg));
+
+ insn++;
+ i++;
+ break;
+ }
+
/* movabsq %rax, imm64 */
EMIT2(add_1mod(0x48, dst_reg), add_1reg(0xB8, dst_reg));
EMIT(insn[0].imm, 4);
diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h
index 4120af086160..fd3b96d1153f 100644
--- a/arch/xtensa/include/uapi/asm/socket.h
+++ b/arch/xtensa/include/uapi/asm/socket.h
@@ -96,4 +96,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _XTENSA_SOCKET_H */
diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c
index 4c93b8a4e81e..147069c9afd0 100644
--- a/crypto/algif_aead.c
+++ b/crypto/algif_aead.c
@@ -106,7 +106,7 @@ static void aead_wmem_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM |
POLLRDBAND);
@@ -157,7 +157,7 @@ static void aead_data_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLRDNORM |
POLLRDBAND);
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index 5c756b30e79b..eaa9f9be5b87 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -238,7 +238,7 @@ static void skcipher_wmem_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM |
POLLRDBAND);
@@ -288,7 +288,7 @@ static void skcipher_data_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLRDNORM |
POLLRDBAND);
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index 3d7fb6516f74..6ac2b2b1e8de 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -347,8 +347,8 @@ static char *next_string(struct sk_buff *skb)
*/
static int process_status(struct solos_card *card, int port, struct sk_buff *skb)
{
- char *str, *end, *state_str, *snr, *attn;
- int ver, rate_up, rate_down;
+ char *str, *state_str, *snr, *attn;
+ int ver, rate_up, rate_down, err;
if (!card->atmdev[port])
return -ENODEV;
@@ -357,7 +357,11 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb
if (!str)
return -EIO;
- ver = simple_strtol(str, NULL, 10);
+ err = kstrtoint(str, 10, &ver);
+ if (err) {
+ dev_warn(&card->dev->dev, "Unexpected status interrupt version\n");
+ return err;
+ }
if (ver < 1) {
dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n",
ver);
@@ -373,16 +377,16 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb
return 0;
}
- rate_down = simple_strtol(str, &end, 10);
- if (*end)
- return -EIO;
+ err = kstrtoint(str, 10, &rate_down);
+ if (err)
+ return err;
str = next_string(skb);
if (!str)
return -EIO;
- rate_up = simple_strtol(str, &end, 10);
- if (*end)
- return -EIO;
+ err = kstrtoint(str, 10, &rate_up);
+ if (err)
+ return err;
state_str = next_string(skb);
if (!state_str)
@@ -417,7 +421,7 @@ static int process_command(struct solos_card *card, int port, struct sk_buff *sk
struct solos_param *prm;
unsigned long flags;
int cmdpid;
- int found = 0;
+ int found = 0, err;
if (skb->len < 7)
return 0;
@@ -428,7 +432,9 @@ static int process_command(struct solos_card *card, int port, struct sk_buff *sk
skb->data[6] != '\n')
return 0;
- cmdpid = simple_strtol(&skb->data[1], NULL, 10);
+ err = kstrtoint(&skb->data[1], 10, &cmdpid);
+ if (err)
+ return err;
spin_lock_irqsave(&card->param_queue_lock, flags);
list_for_each_entry(prm, &card->param_queue, list) {
@@ -519,7 +525,7 @@ struct geos_gpio_attr {
static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
struct solos_card *card = pci_get_drvdata(pdev);
uint32_t data32;
@@ -545,7 +551,7 @@ static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr
static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
struct solos_card *card = pci_get_drvdata(pdev);
uint32_t data32;
@@ -559,7 +565,7 @@ static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
static ssize_t hardware_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
struct solos_card *card = pci_get_drvdata(pdev);
uint32_t data32;
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 59d8d0d14824..c466f752b067 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -668,11 +668,36 @@ static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env)
core->id.rev, core->id.class);
}
-static int __init bcma_modinit(void)
+static unsigned int bcma_bus_registered;
+
+/*
+ * If built-in, bus has to be registered early, before any driver calls
+ * bcma_driver_register.
+ * Otherwise registering driver would trigger BUG in driver_register.
+ */
+static int __init bcma_init_bus_register(void)
{
int err;
+ if (bcma_bus_registered)
+ return 0;
+
err = bus_register(&bcma_bus_type);
+ if (!err)
+ bcma_bus_registered = 1;
+
+ return err;
+}
+#ifndef MODULE
+fs_initcall(bcma_init_bus_register);
+#endif
+
+/* Main initialization has to be done with SPI/mtd/NAND/SPROM available */
+static int __init bcma_modinit(void)
+{
+ int err;
+
+ err = bcma_init_bus_register();
if (err)
return err;
@@ -691,7 +716,7 @@ static int __init bcma_modinit(void)
return err;
}
-fs_initcall(bcma_modinit);
+module_init(bcma_modinit);
static void __exit bcma_modexit(void)
{
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
index 364f82b34d03..5b0ef7bbe8ac 100644
--- a/drivers/bluetooth/bcm203x.c
+++ b/drivers/bluetooth/bcm203x.c
@@ -178,10 +178,8 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
return -ENODEV;
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
- if (!data) {
- BT_ERR("Can't allocate memory for data structure");
+ if (!data)
return -ENOMEM;
- }
data->udev = udev;
data->state = BCM203X_LOAD_MINIDRV;
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 616ec2ac1b22..3bf4ec60e073 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -324,7 +324,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
return -ENOMEM;
}
- bt_cb(skb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(skb) = pkt_type;
data->reassembly = skb;
} else {
@@ -469,9 +469,10 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
unsigned char buf[3];
int sent = 0, size, count;
- BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
+ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb,
+ hci_skb_pkt_type(skb), skb->len);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -484,7 +485,7 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
count = skb->len;
@@ -635,10 +636,8 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
/* Initialize control structure and load firmware */
data = devm_kzalloc(&intf->dev, sizeof(struct bfusb_data), GFP_KERNEL);
- if (!data) {
- BT_ERR("Can't allocate memory for control structure");
- goto done;
- }
+ if (!data)
+ return -ENOMEM;
data->udev = udev;
data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index 36fa1c958c74..c0b3b5576992 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -261,7 +261,7 @@ static void bluecard_write_wakeup(struct bluecard_info *info)
if (!skb)
break;
- if (bt_cb(skb)->pkt_type & 0x80) {
+ if (hci_skb_pkt_type(skb) & 0x80) {
/* Disable RTS */
info->ctrl_reg |= REG_CONTROL_RTS;
outb(info->ctrl_reg, iobase + REG_CONTROL);
@@ -279,13 +279,13 @@ static void bluecard_write_wakeup(struct bluecard_info *info)
/* Mark the buffer as dirty */
clear_bit(ready_bit, &(info->tx_state));
- if (bt_cb(skb)->pkt_type & 0x80) {
+ if (hci_skb_pkt_type(skb) & 0x80) {
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
DEFINE_WAIT(wait);
unsigned char baud_reg;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case PKT_BAUD_RATE_460800:
baud_reg = REG_CONTROL_BAUD_RATE_460800;
break;
@@ -402,9 +402,9 @@ static void bluecard_receive(struct bluecard_info *info,
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
- bt_cb(info->rx_skb)->pkt_type = buf[i];
+ hci_skb_pkt_type(info->rx_skb) = buf[i];
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case 0x00:
/* init packet */
@@ -436,7 +436,8 @@ static void bluecard_receive(struct bluecard_info *info,
default:
/* unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
@@ -578,21 +579,21 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
switch (baud) {
case 460800:
cmd[4] = 0x00;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_460800;
break;
case 230400:
cmd[4] = 0x01;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_230400;
break;
case 115200:
cmd[4] = 0x02;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_115200;
break;
case 57600:
/* Fall through... */
default:
cmd[4] = 0x03;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_57600;
break;
}
@@ -660,7 +661,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct bluecard_info *info = hci_get_drvdata(hdev);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -673,7 +674,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
bluecard_write_wakeup(info);
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 49c397e21b39..fd6b53e9bbf2 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -295,9 +295,9 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return -ENOMEM;
/* Prepend skb with frame type */
- *skb_push(skb, 1) = bt_cb(skb)->pkt_type;
+ *skb_push(skb, 1) = hci_skb_pkt_type(skb);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
if (!dr) {
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 5803aaed958f..8165ef2fe877 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -246,10 +246,10 @@ static void bt3c_receive(struct bt3c_info *info)
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
- bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
+ hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L);
inb(iobase + DATA_H);
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
@@ -268,7 +268,8 @@ static void bt3c_receive(struct bt3c_info *info)
default:
/* Unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
@@ -411,7 +412,7 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
struct bt3c_info *info = hci_get_drvdata(hdev);
unsigned long flags;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -424,7 +425,7 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
spin_lock_irqsave(&(info->lock), flags);
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 1f13e617bf56..fce154855718 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -73,6 +73,48 @@ int btintel_check_bdaddr(struct hci_dev *hdev)
}
EXPORT_SYMBOL_GPL(btintel_check_bdaddr);
+int btintel_enter_mfg(struct hci_dev *hdev)
+{
+ const u8 param[] = { 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Entering manufacturer mode failed (%ld)",
+ PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_enter_mfg);
+
+int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
+{
+ u8 param[] = { 0x00, 0x00 };
+ struct sk_buff *skb;
+
+ /* The 2nd command parameter specifies the manufacturing exit method:
+ * 0x00: Just disable the manufacturing mode (0x00).
+ * 0x01: Disable manufacturing mode and reset with patches deactivated.
+ * 0x02: Disable manufacturing mode and reset with patches activated.
+ */
+ if (reset)
+ param[1] |= patched ? 0x02 : 0x01;
+
+ skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Exiting manufacturer mode failed (%ld)",
+ PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_exit_mfg);
+
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
@@ -126,37 +168,19 @@ EXPORT_SYMBOL_GPL(btintel_set_diag);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
{
- struct sk_buff *skb;
- u8 param[2];
- int err;
-
- param[0] = 0x01;
- param[1] = 0x00;
-
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
- hdev->name, err);
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ int err, ret;
- err = btintel_set_diag(hdev, enable);
+ err = btintel_enter_mfg(hdev);
+ if (err)
+ return err;
- param[0] = 0x00;
- param[1] = 0x00;
+ ret = btintel_set_diag(hdev, enable);
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
- hdev->name, err);
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, false, false);
+ if (err)
+ return err;
- return err;
+ return ret;
}
EXPORT_SYMBOL_GPL(btintel_set_diag_mfg);
@@ -309,39 +333,46 @@ EXPORT_SYMBOL_GPL(btintel_set_event_mask);
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
{
- struct sk_buff *skb;
- u8 param[2];
- int err;
+ int err, ret;
- param[0] = 0x01;
- param[1] = 0x00;
+ err = btintel_enter_mfg(hdev);
+ if (err)
+ return err;
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
- hdev->name, err);
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ ret = btintel_set_event_mask(hdev, debug);
- err = btintel_set_event_mask(hdev, debug);
+ err = btintel_exit_mfg(hdev, false, false);
+ if (err)
+ return err;
- param[0] = 0x00;
- param[1] = 0x00;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
+int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
- hdev->name, err);
+ bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
+ PTR_ERR(skb));
return PTR_ERR(skb);
}
+
+ if (skb->len != sizeof(*ver)) {
+ bt_dev_err(hdev, "Intel version event size mismatch");
+ kfree_skb(skb);
+ return -EILSEQ;
+ }
+
+ memcpy(ver, skb->data, sizeof(*ver));
+
kfree_skb(skb);
- return err;
+ return 0;
}
-EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
+EXPORT_SYMBOL_GPL(btintel_read_version);
/* ------- REGMAP IBT SUPPORT ------- */
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index 07e58e05a7fa..1e8955aaafed 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -72,6 +72,8 @@ struct intel_secure_send_result {
#if IS_ENABLED(CONFIG_BT_INTEL)
int btintel_check_bdaddr(struct hci_dev *hdev);
+int btintel_enter_mfg(struct hci_dev *hdev);
+int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched);
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int btintel_set_diag(struct hci_dev *hdev, bool enable);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
@@ -83,6 +85,7 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
+int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver);
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
u16 opcode_write);
@@ -94,6 +97,16 @@ static inline int btintel_check_bdaddr(struct hci_dev *hdev)
return -EOPNOTSUPP;
}
+static inline int btintel_enter_mfg(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
return -EOPNOTSUPP;
@@ -140,6 +153,12 @@ static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
return -EOPNOTSUPP;
}
+static inline int btintel_read_version(struct hci_dev *hdev,
+ struct intel_version *ver)
+{
+ return -EOPNOTSUPP;
+}
+
static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
u16 opcode_read,
u16 opcode_write)
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index 27a9aac25583..05904732e6f1 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -89,6 +89,7 @@ struct btmrvl_adapter {
wait_queue_head_t event_hs_wait_q;
u8 cmd_complete;
bool is_suspended;
+ bool is_suspending;
};
struct btmrvl_private {
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 6af917331962..f25a825a693f 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -196,7 +196,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
if (len)
memcpy(skb_put(skb, len), param, len);
- bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ hci_skb_pkt_type(skb) = MRVL_VENDOR_PKT;
skb_queue_head(&priv->adapter->tx_queue, skb);
@@ -387,7 +387,7 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
skb->data[0] = (skb->len & 0x0000ff);
skb->data[1] = (skb->len & 0x00ff00) >> 8;
skb->data[2] = (skb->len & 0xff0000) >> 16;
- skb->data[3] = bt_cb(skb)->pkt_type;
+ skb->data[3] = hci_skb_pkt_type(skb);
if (priv->hw_host_to_card)
ret = priv->hw_host_to_card(priv, skb->data, skb->len);
@@ -434,9 +434,14 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
- BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
+ BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len);
- switch (bt_cb(skb)->pkt_type) {
+ if (priv->adapter->is_suspending || priv->adapter->is_suspended) {
+ BT_ERR("%s: Device is suspending or suspended", __func__);
+ return -EBUSY;
+ }
+
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -452,7 +457,8 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
skb_queue_tail(&priv->adapter->tx_queue, skb);
- wake_up_interruptible(&priv->main_thread.wait_q);
+ if (!priv->adapter->is_suspended)
+ wake_up_interruptible(&priv->main_thread.wait_q);
return 0;
}
@@ -543,7 +549,7 @@ static int btmrvl_setup(struct hci_dev *hdev)
if (ret)
return ret;
- priv->btmrvl_dev.gpio_gap = 0xffff;
+ priv->btmrvl_dev.gpio_gap = 0xfffe;
btmrvl_check_device_tree(priv);
@@ -643,7 +649,8 @@ static int btmrvl_service_main_thread(void *data)
if (adapter->ps_state == PS_SLEEP)
continue;
- if (!priv->btmrvl_dev.tx_dnld_rdy)
+ if (!priv->btmrvl_dev.tx_dnld_rdy ||
+ priv->adapter->is_suspended)
continue;
skb = skb_dequeue(&adapter->tx_queue);
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 71ea2a3af293..6ed8acfcfa9c 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -698,7 +698,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
case HCI_EVENT_PKT:
- bt_cb(skb)->pkt_type = type;
+ hci_skb_pkt_type(skb) = type;
skb_put(skb, buf_len);
skb_pull(skb, SDIO_HEADER_LEN);
@@ -713,7 +713,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
break;
case MRVL_VENDOR_PKT:
- bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+ hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
skb_put(skb, buf_len);
skb_pull(skb, SDIO_HEADER_LEN);
@@ -1112,7 +1112,8 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
*/
if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
BT_ERR("FW failed to be active in time!");
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ goto done;
}
sdio_release_host(card->func);
@@ -1544,10 +1545,10 @@ static int btmrvl_sdio_suspend(struct device *dev)
}
priv = card->priv;
+ priv->adapter->is_suspending = true;
hcidev = priv->btmrvl_dev.hcidev;
BT_DBG("%s: SDIO suspend", hcidev->name);
hci_suspend_dev(hcidev);
- skb_queue_purge(&priv->adapter->tx_queue);
if (priv->adapter->hs_state != HS_ACTIVATED) {
if (btmrvl_enable_hs(priv)) {
@@ -1556,6 +1557,7 @@ static int btmrvl_sdio_suspend(struct device *dev)
}
}
+ priv->adapter->is_suspending = false;
priv->adapter->is_suspended = true;
/* We will keep the power when hs enabled successfully */
diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
index 7b624423a7e8..2b05661e3818 100644
--- a/drivers/bluetooth/btsdio.c
+++ b/drivers/bluetooth/btsdio.c
@@ -86,7 +86,7 @@ static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
skb->data[0] = (skb->len & 0x0000ff);
skb->data[1] = (skb->len & 0x00ff00) >> 8;
skb->data[2] = (skb->len & 0xff0000) >> 16;
- skb->data[3] = bt_cb(skb)->pkt_type;
+ skb->data[3] = hci_skb_pkt_type(skb);
err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len);
if (err < 0) {
@@ -158,7 +158,7 @@ static int btsdio_rx_packet(struct btsdio_data *data)
data->hdev->stat.byte_rx += len;
- bt_cb(skb)->pkt_type = hdr[3];
+ hci_skb_pkt_type(skb) = hdr[3];
err = hci_recv_frame(data->hdev, skb);
if (err < 0)
@@ -252,7 +252,7 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s", hdev->name);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index bb8e4025fb9e..9624b29f8349 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -200,9 +200,9 @@ static void btuart_receive(struct btuart_info *info)
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
- bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX);
+ hci_skb_pkt_type(info->rx_skb) = inb(iobase + UART_RX);
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
@@ -221,7 +221,8 @@ static void btuart_receive(struct btuart_info *info)
default:
/* Unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
@@ -424,7 +425,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btuart_info *info = hci_get_drvdata(hdev);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -437,7 +438,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
btuart_write_wakeup(info);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 92f0ee388f9e..a191e318fab8 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -153,6 +153,10 @@ static const struct usb_device_id btusb_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01),
.driver_info = BTUSB_BCM_PATCHRAM },
+ /* Toshiba Corp - Broadcom based */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0930, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
+
/* Intel Bluetooth USB Bootloader (RAM module) */
{ USB_DEVICE(0x8087, 0x0a5a),
.driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC },
@@ -437,22 +441,22 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
break;
}
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
- bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ hci_skb_expect(skb) = HCI_EVENT_HDR_SIZE;
}
- len = min_t(uint, bt_cb(skb)->expect, count);
+ len = min_t(uint, hci_skb_expect(skb), count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
- bt_cb(skb)->expect -= len;
+ hci_skb_expect(skb) -= len;
if (skb->len == HCI_EVENT_HDR_SIZE) {
/* Complete event header */
- bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
+ hci_skb_expect(skb) = hci_event_hdr(skb)->plen;
- if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -461,7 +465,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
}
}
- if (bt_cb(skb)->expect == 0) {
+ if (!hci_skb_expect(skb)) {
/* Complete frame */
data->recv_event(data->hdev, skb);
skb = NULL;
@@ -492,24 +496,24 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
break;
}
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
- bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
+ hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
+ hci_skb_expect(skb) = HCI_ACL_HDR_SIZE;
}
- len = min_t(uint, bt_cb(skb)->expect, count);
+ len = min_t(uint, hci_skb_expect(skb), count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
- bt_cb(skb)->expect -= len;
+ hci_skb_expect(skb) -= len;
if (skb->len == HCI_ACL_HDR_SIZE) {
__le16 dlen = hci_acl_hdr(skb)->dlen;
/* Complete ACL header */
- bt_cb(skb)->expect = __le16_to_cpu(dlen);
+ hci_skb_expect(skb) = __le16_to_cpu(dlen);
- if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -518,7 +522,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
}
}
- if (bt_cb(skb)->expect == 0) {
+ if (!hci_skb_expect(skb)) {
/* Complete frame */
hci_recv_frame(data->hdev, skb);
skb = NULL;
@@ -549,22 +553,22 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
break;
}
- bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
- bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
+ hci_skb_pkt_type(skb) = HCI_SCODATA_PKT;
+ hci_skb_expect(skb) = HCI_SCO_HDR_SIZE;
}
- len = min_t(uint, bt_cb(skb)->expect, count);
+ len = min_t(uint, hci_skb_expect(skb), count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
- bt_cb(skb)->expect -= len;
+ hci_skb_expect(skb) -= len;
if (skb->len == HCI_SCO_HDR_SIZE) {
/* Complete SCO header */
- bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
+ hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen;
- if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -573,7 +577,7 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
}
}
- if (bt_cb(skb)->expect == 0) {
+ if (!hci_skb_expect(skb)) {
/* Complete frame */
hci_recv_frame(data->hdev, skb);
skb = NULL;
@@ -1257,7 +1261,7 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s", hdev->name);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
urb = alloc_ctrl_urb(hdev, skb);
if (IS_ERR(urb))
@@ -1642,13 +1646,8 @@ static int btusb_setup_intel(struct hci_dev *hdev)
struct sk_buff *skb;
const struct firmware *fw;
const u8 *fw_ptr;
- int disable_patch;
- struct intel_version *ver;
-
- const u8 mfg_enable[] = { 0x01, 0x00 };
- const u8 mfg_disable[] = { 0x00, 0x00 };
- const u8 mfg_reset_deactivate[] = { 0x00, 0x01 };
- const u8 mfg_reset_activate[] = { 0x00, 0x02 };
+ int disable_patch, err;
+ struct intel_version ver;
BT_DBG("%s", hdev->name);
@@ -1674,35 +1673,22 @@ static int btusb_setup_intel(struct hci_dev *hdev)
* The returned information are hardware variant and revision plus
* firmware variant, revision and build number.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s reading Intel fw version command failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- if (skb->len != sizeof(*ver)) {
- BT_ERR("%s Intel version event length mismatch", hdev->name);
- kfree_skb(skb);
- return -EIO;
- }
-
- ver = (struct intel_version *)skb->data;
+ err = btintel_read_version(hdev, &ver);
+ if (err)
+ return err;
BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
- hdev->name, ver->hw_platform, ver->hw_variant,
- ver->hw_revision, ver->fw_variant, ver->fw_revision,
- ver->fw_build_num, ver->fw_build_ww, ver->fw_build_yy,
- ver->fw_patch_num);
+ hdev->name, ver.hw_platform, ver.hw_variant, ver.hw_revision,
+ ver.fw_variant, ver.fw_revision, ver.fw_build_num,
+ ver.fw_build_ww, ver.fw_build_yy, ver.fw_patch_num);
/* fw_patch_num indicates the version of patch the device currently
* have. If there is no patch data in the device, it is always 0x00.
* So, if it is other than 0x00, no need to patch the device again.
*/
- if (ver->fw_patch_num) {
+ if (ver.fw_patch_num) {
BT_INFO("%s: Intel device is already patched. patch num: %02x",
- hdev->name, ver->fw_patch_num);
- kfree_skb(skb);
+ hdev->name, ver.fw_patch_num);
goto complete;
}
@@ -1712,31 +1698,21 @@ static int btusb_setup_intel(struct hci_dev *hdev)
* If no patch file is found, allow the device to operate without
* a patch.
*/
- fw = btusb_setup_intel_get_fw(hdev, ver);
- if (!fw) {
- kfree_skb(skb);
+ fw = btusb_setup_intel_get_fw(hdev, &ver);
+ if (!fw)
goto complete;
- }
fw_ptr = fw->data;
- kfree_skb(skb);
-
- /* This Intel specific command enables the manufacturer mode of the
- * controller.
- *
+ /* Enable the manufacturer mode of the controller.
* Only while this mode is enabled, the driver can download the
* firmware patch data and configuration parameters.
*/
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s entering Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
+ err = btintel_enter_mfg(hdev);
+ if (err) {
release_firmware(fw);
- return PTR_ERR(skb);
+ return err;
}
- kfree_skb(skb);
-
disable_patch = 1;
/* The firmware data file consists of list of Intel specific HCI
@@ -1776,14 +1752,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)
/* Patching completed successfully and disable the manufacturer mode
* with reset and activate the downloaded firmware patches.
*/
- skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate),
- mfg_reset_activate, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, true, true);
+ if (err)
+ return err;
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
hdev->name);
@@ -1792,14 +1763,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)
exit_mfg_disable:
/* Disable the manufacturer mode without reset */
- skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, false, false);
+ if (err)
+ return err;
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
@@ -1811,14 +1777,9 @@ exit_mfg_deactivate:
/* Patching failed. Disable the manufacturer mode with reset and
* deactivate the downloaded firmware patches.
*/
- skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate),
- mfg_reset_deactivate, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, true, false);
+ if (err)
+ return err;
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
hdev->name);
@@ -1853,7 +1814,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
*skb_put(skb, 1) = 0x00;
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
return hci_recv_frame(hdev, skb);
}
@@ -1945,7 +1906,7 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s", hdev->name);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
struct hci_command_hdr *cmd = (void *)skb->data;
@@ -2005,7 +1966,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
0x00, 0x08, 0x04, 0x00 };
struct btusb_data *data = hci_get_drvdata(hdev);
struct sk_buff *skb;
- struct intel_version *ver;
+ struct intel_version ver;
struct intel_boot_params *params;
const struct firmware *fw;
const u8 *fw_ptr;
@@ -2023,28 +1984,16 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* is in bootloader mode or if it already has operational firmware
* loaded.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s: Reading Intel version information failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- if (skb->len != sizeof(*ver)) {
- BT_ERR("%s: Intel version event size mismatch", hdev->name);
- kfree_skb(skb);
- return -EILSEQ;
- }
-
- ver = (struct intel_version *)skb->data;
+ err = btintel_read_version(hdev, &ver);
+ if (err)
+ return err;
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
*/
- if (ver->hw_platform != 0x37) {
+ if (ver.hw_platform != 0x37) {
BT_ERR("%s: Unsupported Intel hardware platform (%u)",
- hdev->name, ver->hw_platform);
- kfree_skb(skb);
+ hdev->name, ver.hw_platform);
return -EINVAL;
}
@@ -2053,14 +2002,13 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* put in place to ensure correct forward compatibility options
* when newer hardware variants come along.
*/
- if (ver->hw_variant != 0x0b) {
+ if (ver.hw_variant != 0x0b) {
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
- hdev->name, ver->hw_variant);
- kfree_skb(skb);
+ hdev->name, ver.hw_variant);
return -EINVAL;
}
- btintel_version_info(hdev, ver);
+ btintel_version_info(hdev, &ver);
/* The firmware variant determines if the device is in bootloader
* mode or is running operational firmware. The value 0x06 identifies
@@ -2075,8 +2023,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* It is not possible to use the Secure Boot Parameters in this
* case since that command is only available in bootloader mode.
*/
- if (ver->fw_variant == 0x23) {
- kfree_skb(skb);
+ if (ver.fw_variant == 0x23) {
clear_bit(BTUSB_BOOTLOADER, &data->flags);
btintel_check_bdaddr(hdev);
return 0;
@@ -2085,15 +2032,12 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
/* If the device is not in bootloader mode, then the only possible
* choice is to return an error and abort the device initialization.
*/
- if (ver->fw_variant != 0x06) {
+ if (ver.fw_variant != 0x06) {
BT_ERR("%s: Unsupported Intel firmware variant (%u)",
- hdev->name, ver->fw_variant);
- kfree_skb(skb);
+ hdev->name, ver.fw_variant);
return -ENODEV;
}
- kfree_skb(skb);
-
/* Read the secure boot parameters to identify the operating
* details of the bootloader.
*/
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
index 57eb935aedc7..24a652f9252b 100644
--- a/drivers/bluetooth/btwilink.c
+++ b/drivers/bluetooth/btwilink.c
@@ -249,10 +249,10 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
hst = hci_get_drvdata(hdev);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
- BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type,
- skb->len);
+ BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
+ skb->len);
/* Insert skb to shared transport layer's transmit queue.
* Freeing skb memory is taken care in shared transport layer,
@@ -268,7 +268,7 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
/* ST accepted our skb. So, Go ahead and do rest */
hdev->stat.byte_tx += len;
- ti_st_tx_complete(hst, bt_cb(skb)->pkt_type);
+ ti_st_tx_complete(hst, hci_skb_pkt_type(skb));
return 0;
}
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 5026f66fac88..6317c6f323bf 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -239,7 +239,7 @@ static void dtl1_receive(struct dtl1_info *info)
info->rx_count = nsh->len + (nsh->len & 0x0001);
break;
case RECV_WAIT_DATA:
- bt_cb(info->rx_skb)->pkt_type = nsh->type;
+ hci_skb_pkt_type(info->rx_skb) = nsh->type;
/* remove PAD byte if it exists */
if (nsh->len & 0x0001) {
@@ -250,7 +250,7 @@ static void dtl1_receive(struct dtl1_info *info)
/* remove NSH */
skb_pull(info->rx_skb, NSHL);
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case 0x80:
/* control data for the Nokia Card */
dtl1_control(info, info->rx_skb);
@@ -259,12 +259,13 @@ static void dtl1_receive(struct dtl1_info *info)
case 0x83:
case 0x84:
/* send frame to the HCI layer */
- bt_cb(info->rx_skb)->pkt_type &= 0x0f;
+ hci_skb_pkt_type(info->rx_skb) &= 0x0f;
hci_recv_frame(info->hdev, info->rx_skb);
break;
default:
/* unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
kfree_skb(info->rx_skb);
break;
}
@@ -386,7 +387,7 @@ static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
struct sk_buff *s;
struct nsh nsh;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
nsh.type = 0x81;
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index d776dfd51478..0ccf6bf01ed4 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -205,7 +205,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct ath_struct *ath = hu->priv;
- if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) == HCI_SCODATA_PKT) {
kfree_skb(skb);
return 0;
}
@@ -213,7 +213,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
/* Update power management enable flag with parameters of
* HCI sleep enable vendor specific HCI command.
*/
- if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
struct hci_command_hdr *hdr = (void *)skb->data;
if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP)
@@ -223,7 +223,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&ath->txq, skb);
set_bit(HCI_UART_SENDING, &hu->tx_state);
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index cb852cc750b7..5f3de181e744 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -472,7 +472,7 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&bcm->txq, skb);
return 0;
@@ -814,8 +814,16 @@ static const struct hci_uart_proto bcm_proto = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id bcm_acpi_match[] = {
+ { "BCM2E1A", 0 },
{ "BCM2E39", 0 },
+ { "BCM2E3A", 0 },
+ { "BCM2E3D", 0 },
+ { "BCM2E3F", 0 },
+ { "BCM2E40", 0 },
+ { "BCM2E64", 0 },
+ { "BCM2E65", 0 },
{ "BCM2E67", 0 },
+ { "BCM2E7B", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index d0b615a932d1..064f2fefad62 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -155,7 +155,7 @@ static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
skb_queue_tail(&bcsp->rel, skb);
@@ -231,7 +231,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
if (!nskb)
return NULL;
- bt_cb(nskb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(nskb) = pkt_type;
bcsp_slip_msgdelim(nskb);
@@ -291,7 +291,10 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&bcsp->unrel);
if (skb != NULL) {
- struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+ struct sk_buff *nskb;
+
+ nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
+ hci_skb_pkt_type(skb));
if (nskb) {
kfree_skb(skb);
return nskb;
@@ -310,8 +313,10 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
if (bcsp->unack.qlen < BCSP_TXWINSIZE) {
skb = skb_dequeue(&bcsp->rel);
if (skb != NULL) {
- struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
- bt_cb(skb)->pkt_type);
+ struct sk_buff *nskb;
+
+ nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
+ hci_skb_pkt_type(skb));
if (nskb) {
__skb_queue_tail(&bcsp->unack, skb);
mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
@@ -412,7 +417,7 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu)
if (!nskb)
return;
memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
- bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
+ hci_skb_pkt_type(nskb) = BCSP_LE_PKT;
skb_queue_head(&bcsp->unrel, nskb);
hci_uart_tx_wakeup(hu);
@@ -494,14 +499,14 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
bcsp_pkt_cull(bcsp);
if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
bcsp->rx_skb->data[0] & 0x80) {
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
bcsp->rx_skb->data[0] & 0x80) {
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_SCODATA_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
!(bcsp->rx_skb->data[0] & 0x80)) {
@@ -523,7 +528,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
hdr.evt = 0xff;
hdr.plen = bcsp->rx_skb->len;
memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
hci_recv_frame(hu->hdev, bcsp->rx_skb);
} else {
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index a6fce48da0fb..635597b6e168 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -108,7 +108,7 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&h4->txq, skb);
return 0;
@@ -184,8 +184,8 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
if (!skb)
return ERR_PTR(-ENOMEM);
- bt_cb(skb)->pkt_type = (&pkts[i])->type;
- bt_cb(skb)->expect = (&pkts[i])->hlen;
+ hci_skb_pkt_type(skb) = (&pkts[i])->type;
+ hci_skb_expect(skb) = (&pkts[i])->hlen;
break;
}
@@ -197,18 +197,18 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
buffer += 1;
}
- len = min_t(uint, bt_cb(skb)->expect - skb->len, count);
+ len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
/* Check for partial packet */
- if (skb->len < bt_cb(skb)->expect)
+ if (skb->len < hci_skb_expect(skb))
continue;
for (i = 0; i < pkts_count; i++) {
- if (bt_cb(skb)->pkt_type == (&pkts[i])->type)
+ if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
break;
}
@@ -228,7 +228,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
case 1:
/* Single octet variable length */
dlen = skb->data[(&pkts[i])->loff];
- bt_cb(skb)->expect += dlen;
+ hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
@@ -239,7 +239,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
/* Double octet variable length */
dlen = get_unaligned_le16(skb->data +
(&pkts[i])->loff);
- bt_cb(skb)->expect += dlen;
+ hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index abee2216fdeb..0879d64b1caf 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -51,7 +51,7 @@
#define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01)
#define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01)
#define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f)
-#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0xff) + ((hdr)[2] << 4))
+#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0x0f) + ((hdr)[2] << 4))
#define SLIP_DELIMITER 0xc0
#define SLIP_ESC 0xdb
@@ -107,7 +107,7 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
if (!nskb)
return;
- bt_cb(nskb)->pkt_type = HCI_3WIRE_LINK_PKT;
+ hci_skb_pkt_type(nskb) = HCI_3WIRE_LINK_PKT;
memcpy(skb_put(nskb, len), data, len);
@@ -116,18 +116,14 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
static u8 h5_cfg_field(struct h5 *h5)
{
- u8 field = 0;
-
/* Sliding window size (first 3 bits) */
- field |= (h5->tx_win & 7);
-
- return field;
+ return h5->tx_win & 0x07;
}
static void h5_timed_event(unsigned long arg)
{
const unsigned char sync_req[] = { 0x01, 0x7e };
- unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
+ unsigned char conf_req[3] = { 0x03, 0xfc };
struct hci_uart *hu = (struct hci_uart *)arg;
struct h5 *h5 = hu->priv;
struct sk_buff *skb;
@@ -285,7 +281,7 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
struct h5 *h5 = hu->priv;
const unsigned char sync_req[] = { 0x01, 0x7e };
const unsigned char sync_rsp[] = { 0x02, 0x7d };
- unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
+ unsigned char conf_req[3] = { 0x03, 0xfc };
const unsigned char conf_rsp[] = { 0x04, 0x7b };
const unsigned char wakeup_req[] = { 0x05, 0xfa };
const unsigned char woken_req[] = { 0x06, 0xf9 };
@@ -317,7 +313,7 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_rsp, 2) == 0) {
if (H5_HDR_LEN(hdr) > 2)
- h5->tx_win = (data[2] & 7);
+ h5->tx_win = (data[2] & 0x07);
BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
h5->state = H5_ACTIVE;
hci_uart_init_ready(hu);
@@ -360,7 +356,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
case HCI_EVENT_PKT:
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
- bt_cb(h5->rx_skb)->pkt_type = H5_HDR_PKT_TYPE(hdr);
+ hci_skb_pkt_type(h5->rx_skb) = H5_HDR_PKT_TYPE(hdr);
/* Remove Three-wire header */
skb_pull(h5->rx_skb, 4);
@@ -562,7 +558,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
skb_queue_tail(&h5->rel, skb);
@@ -573,7 +569,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
break;
default:
- BT_ERR("Unknown packet type %u", bt_cb(skb)->pkt_type);
+ BT_ERR("Unknown packet type %u", hci_skb_pkt_type(skb));
kfree_skb(skb);
break;
}
@@ -642,7 +638,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
if (!nskb)
return NULL;
- bt_cb(nskb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(nskb) = pkt_type;
h5_slip_delim(nskb);
@@ -697,7 +693,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&h5->unrel);
if (skb) {
- nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
+ nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb),
skb->data, skb->len);
if (nskb) {
kfree_skb(skb);
@@ -715,7 +711,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&h5->rel);
if (skb) {
- nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
+ nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb),
skb->data, skb->len);
if (nskb) {
__skb_queue_tail(&h5->unack, skb);
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 4a414a5a3165..3d63ea37bd4c 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -186,7 +186,7 @@ static int intel_lpm_suspend(struct hci_uart *hu)
}
memcpy(skb_put(skb, sizeof(suspend)), suspend, sizeof(suspend));
- bt_cb(skb)->pkt_type = HCI_LPM_PKT;
+ hci_skb_pkt_type(skb) = HCI_LPM_PKT;
set_bit(STATE_LPM_TRANSACTION, &intel->flags);
@@ -230,7 +230,7 @@ static int intel_lpm_resume(struct hci_uart *hu)
return -ENOMEM;
}
- bt_cb(skb)->pkt_type = HCI_LPM_WAKE_PKT;
+ hci_skb_pkt_type(skb) = HCI_LPM_WAKE_PKT;
set_bit(STATE_LPM_TRANSACTION, &intel->flags);
@@ -272,7 +272,7 @@ static int intel_lpm_host_wake(struct hci_uart *hu)
memcpy(skb_put(skb, sizeof(lpm_resume_ack)), lpm_resume_ack,
sizeof(lpm_resume_ack));
- bt_cb(skb)->pkt_type = HCI_LPM_PKT;
+ hci_skb_pkt_type(skb) = HCI_LPM_PKT;
/* LPM flow is a priority, enqueue packet at list head */
skb_queue_head(&intel->txq, skb);
@@ -467,7 +467,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
*skb_put(skb, 1) = 0x00;
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
return hci_recv_frame(hdev, skb);
}
@@ -502,7 +502,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
/* Device will not accept speed change if Intel version has not been
* previously requested.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
PTR_ERR(skb));
@@ -517,7 +517,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
}
memcpy(skb_put(skb, sizeof(speed_cmd)), speed_cmd, sizeof(speed_cmd));
- bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
hci_uart_set_flow_control(hu, true);
@@ -542,7 +542,7 @@ static int intel_setup(struct hci_uart *hu)
struct intel_device *idev = NULL;
struct hci_dev *hdev = hu->hdev;
struct sk_buff *skb;
- struct intel_version *ver;
+ struct intel_version ver;
struct intel_boot_params *params;
struct list_head *p;
const struct firmware *fw;
@@ -590,35 +590,16 @@ static int intel_setup(struct hci_uart *hu)
* is in bootloader mode or if it already has operational firmware
* loaded.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
- PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- if (skb->len != sizeof(*ver)) {
- bt_dev_err(hdev, "Intel version event size mismatch");
- kfree_skb(skb);
- return -EILSEQ;
- }
-
- ver = (struct intel_version *)skb->data;
- if (ver->status) {
- bt_dev_err(hdev, "Intel version command failure (%02x)",
- ver->status);
- err = -bt_to_errno(ver->status);
- kfree_skb(skb);
+ err = btintel_read_version(hdev, &ver);
+ if (err)
return err;
- }
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
*/
- if (ver->hw_platform != 0x37) {
+ if (ver.hw_platform != 0x37) {
bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)",
- ver->hw_platform);
- kfree_skb(skb);
+ ver.hw_platform);
return -EINVAL;
}
@@ -627,14 +608,13 @@ static int intel_setup(struct hci_uart *hu)
* put in place to ensure correct forward compatibility options
* when newer hardware variants come along.
*/
- if (ver->hw_variant != 0x0b) {
+ if (ver.hw_variant != 0x0b) {
bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
- ver->hw_variant);
- kfree_skb(skb);
+ ver.hw_variant);
return -EINVAL;
}
- btintel_version_info(hdev, ver);
+ btintel_version_info(hdev, &ver);
/* The firmware variant determines if the device is in bootloader
* mode or is running operational firmware. The value 0x06 identifies
@@ -649,8 +629,7 @@ static int intel_setup(struct hci_uart *hu)
* It is not possible to use the Secure Boot Parameters in this
* case since that command is only available in bootloader mode.
*/
- if (ver->fw_variant == 0x23) {
- kfree_skb(skb);
+ if (ver.fw_variant == 0x23) {
clear_bit(STATE_BOOTLOADER, &intel->flags);
btintel_check_bdaddr(hdev);
return 0;
@@ -659,19 +638,16 @@ static int intel_setup(struct hci_uart *hu)
/* If the device is not in bootloader mode, then the only possible
* choice is to return an error and abort the device initialization.
*/
- if (ver->fw_variant != 0x06) {
+ if (ver.fw_variant != 0x06) {
bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)",
- ver->fw_variant);
- kfree_skb(skb);
+ ver.fw_variant);
return -ENODEV;
}
- kfree_skb(skb);
-
/* Read the secure boot parameters to identify the operating
* details of the bootloader.
*/
- skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)",
PTR_ERR(skb));
@@ -881,7 +857,7 @@ done:
set_bit(STATE_BOOTING, &intel->flags);
skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(reset_param), reset_param,
- HCI_INIT_TIMEOUT);
+ HCI_CMD_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -1126,7 +1102,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu)
return skb;
if (test_bit(STATE_BOOTLOADER, &intel->flags) &&
- (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT)) {
+ (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT)) {
struct hci_command_hdr *cmd = (void *)skb->data;
__u16 opcode = le16_to_cpu(cmd->opcode);
@@ -1140,7 +1116,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
return skb;
}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 96bcec5598c2..73202624133b 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -162,7 +162,7 @@ restart:
break;
}
- hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type);
+ hci_uart_tx_complete(hu, hci_skb_pkt_type(skb));
kfree_skb(skb);
}
@@ -248,7 +248,8 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
- BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
+ BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
+ skb->len);
hu->proto->enqueue(hu, skb);
@@ -461,13 +462,7 @@ static int hci_uart_tty_open(struct tty_struct *tty)
INIT_WORK(&hu->init_ready, hci_uart_init_work);
INIT_WORK(&hu->write_work, hci_uart_write_work);
- /* Flush any pending characters in the driver and line discipline. */
-
- /* FIXME: why is this needed. Note don't use ldisc_ref here as the
- open path is before the ldisc is referencable */
-
- if (tty->ldisc->ops->flush_buffer)
- tty->ldisc->ops->flush_buffer(tty);
+ /* Flush any pending characters in the driver */
tty_driver_flush_buffer(tty);
return 0;
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 9ee24b075f79..02692fe30279 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -307,7 +307,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
@@ -493,7 +493,7 @@ static int ll_recv(struct hci_uart *hu, const void *data, int count)
return -ENOMEM;
}
- bt_cb(ll->rx_skb)->pkt_type = type;
+ hci_skb_pkt_type(ll->rx_skb) = type;
}
return count;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 71325e443e46..683c2b642057 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -678,7 +678,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
qca->tx_ibs_state);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* Don't go to sleep in middle of patch download or
* Out-Of-Band(GPIOs control) sleep is selected.
@@ -873,7 +873,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
/* Assign commands to change baudrate and packet type. */
memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
- bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
skb_queue_tail(&qca->txq, skb);
hci_uart_tx_wakeup(hu);
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index ed888e302bc3..80783dcb7f57 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -80,7 +80,7 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct vhci_data *data = hci_get_drvdata(hdev);
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&data->readq, skb);
wake_up_interruptible(&data->read_wait);
@@ -140,7 +140,7 @@ static int vhci_create_device(struct vhci_data *data, __u8 opcode)
return -EBUSY;
}
- bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+ hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
*skb_put(skb, 1) = 0xff;
*skb_put(skb, 1) = opcode;
@@ -183,7 +183,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
return -ENODEV;
}
- bt_cb(skb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(skb) = pkt_type;
ret = hci_recv_frame(data->hdev, skb);
break;
@@ -234,7 +234,7 @@ static inline ssize_t vhci_put_user(struct vhci_data *data,
data->hdev->stat.byte_tx += len;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
data->hdev->stat.cmd_tx++;
break;
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 870e56b6b25f..26833bfa639b 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -40,6 +40,7 @@
#include <linux/gfp.h>
#include <rdma/ib_pma.h>
+#include <linux/mlx4/driver.h>
#include "mlx4_ib.h"
enum {
@@ -606,8 +607,8 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port,
struct ib_mad *mad)
{
struct mlx4_ib_dev *dev = to_mdev(ibdev);
- int err;
- int slave;
+ int err, other_port;
+ int slave = -1;
u8 *slave_id;
int is_eth = 0;
@@ -625,7 +626,17 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port,
mlx4_ib_warn(ibdev, "RoCE mgmt class is not CM\n");
return -EINVAL;
}
- if (mlx4_get_slave_from_roce_gid(dev->dev, port, grh->dgid.raw, &slave)) {
+ err = mlx4_get_slave_from_roce_gid(dev->dev, port, grh->dgid.raw, &slave);
+ if (err && mlx4_is_mf_bonded(dev->dev)) {
+ other_port = (port == 1) ? 2 : 1;
+ err = mlx4_get_slave_from_roce_gid(dev->dev, other_port, grh->dgid.raw, &slave);
+ if (!err) {
+ port = other_port;
+ pr_debug("resolved slave %d from gid %pI6 wire port %d other %d\n",
+ slave, grh->dgid.raw, port, other_port);
+ }
+ }
+ if (err) {
mlx4_ib_warn(ibdev, "failed matching grh\n");
return -ENOENT;
}
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 7e97cb55a6bf..b0ec175cc6ba 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -43,6 +43,9 @@
#include <linux/mlx5/vport.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
+#include <linux/in.h>
+#include <linux/etherdevice.h>
+#include <linux/mlx5/fs.h>
#include "user.h"
#include "mlx5_ib.h"
@@ -835,6 +838,457 @@ static int mlx5_ib_dealloc_pd(struct ib_pd *pd)
return 0;
}
+static bool outer_header_zero(u32 *match_criteria)
+{
+ int size = MLX5_ST_SZ_BYTES(fte_match_param);
+ char *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_criteria,
+ outer_headers);
+
+ return outer_headers_c[0] == 0 && !memcmp(outer_headers_c,
+ outer_headers_c + 1,
+ size - 1);
+}
+
+static int parse_flow_attr(u32 *match_c, u32 *match_v,
+ union ib_flow_spec *ib_spec)
+{
+ void *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
+ outer_headers);
+ void *outer_headers_v = MLX5_ADDR_OF(fte_match_param, match_v,
+ outer_headers);
+ switch (ib_spec->type) {
+ case IB_FLOW_SPEC_ETH:
+ if (ib_spec->size != sizeof(ib_spec->eth))
+ return -EINVAL;
+
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+ dmac_47_16),
+ ib_spec->eth.mask.dst_mac);
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+ dmac_47_16),
+ ib_spec->eth.val.dst_mac);
+
+ if (ib_spec->eth.mask.vlan_tag) {
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ vlan_tag, 1);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ vlan_tag, 1);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ first_vid, ntohs(ib_spec->eth.mask.vlan_tag));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ first_vid, ntohs(ib_spec->eth.val.vlan_tag));
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ first_cfi,
+ ntohs(ib_spec->eth.mask.vlan_tag) >> 12);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ first_cfi,
+ ntohs(ib_spec->eth.val.vlan_tag) >> 12);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ first_prio,
+ ntohs(ib_spec->eth.mask.vlan_tag) >> 13);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ first_prio,
+ ntohs(ib_spec->eth.val.vlan_tag) >> 13);
+ }
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ ethertype, ntohs(ib_spec->eth.mask.ether_type));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ ethertype, ntohs(ib_spec->eth.val.ether_type));
+ break;
+ case IB_FLOW_SPEC_IPV4:
+ if (ib_spec->size != sizeof(ib_spec->ipv4))
+ return -EINVAL;
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ ethertype, 0xffff);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ ethertype, ETH_P_IP);
+
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.mask.src_ip,
+ sizeof(ib_spec->ipv4.mask.src_ip));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.val.src_ip,
+ sizeof(ib_spec->ipv4.val.src_ip));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.mask.dst_ip,
+ sizeof(ib_spec->ipv4.mask.dst_ip));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.val.dst_ip,
+ sizeof(ib_spec->ipv4.val.dst_ip));
+ break;
+ case IB_FLOW_SPEC_TCP:
+ if (ib_spec->size != sizeof(ib_spec->tcp_udp))
+ return -EINVAL;
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol,
+ 0xff);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ip_protocol,
+ IPPROTO_TCP);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, tcp_sport,
+ ntohs(ib_spec->tcp_udp.mask.src_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, tcp_sport,
+ ntohs(ib_spec->tcp_udp.val.src_port));
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, tcp_dport,
+ ntohs(ib_spec->tcp_udp.mask.dst_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, tcp_dport,
+ ntohs(ib_spec->tcp_udp.val.dst_port));
+ break;
+ case IB_FLOW_SPEC_UDP:
+ if (ib_spec->size != sizeof(ib_spec->tcp_udp))
+ return -EINVAL;
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol,
+ 0xff);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ip_protocol,
+ IPPROTO_UDP);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, udp_sport,
+ ntohs(ib_spec->tcp_udp.mask.src_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, udp_sport,
+ ntohs(ib_spec->tcp_udp.val.src_port));
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, udp_dport,
+ ntohs(ib_spec->tcp_udp.mask.dst_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, udp_dport,
+ ntohs(ib_spec->tcp_udp.val.dst_port));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* If a flow could catch both multicast and unicast packets,
+ * it won't fall into the multicast flow steering table and this rule
+ * could steal other multicast packets.
+ */
+static bool flow_is_multicast_only(struct ib_flow_attr *ib_attr)
+{
+ struct ib_flow_spec_eth *eth_spec;
+
+ if (ib_attr->type != IB_FLOW_ATTR_NORMAL ||
+ ib_attr->size < sizeof(struct ib_flow_attr) +
+ sizeof(struct ib_flow_spec_eth) ||
+ ib_attr->num_of_specs < 1)
+ return false;
+
+ eth_spec = (struct ib_flow_spec_eth *)(ib_attr + 1);
+ if (eth_spec->type != IB_FLOW_SPEC_ETH ||
+ eth_spec->size != sizeof(*eth_spec))
+ return false;
+
+ return is_multicast_ether_addr(eth_spec->mask.dst_mac) &&
+ is_multicast_ether_addr(eth_spec->val.dst_mac);
+}
+
+static bool is_valid_attr(struct ib_flow_attr *flow_attr)
+{
+ union ib_flow_spec *ib_spec = (union ib_flow_spec *)(flow_attr + 1);
+ bool has_ipv4_spec = false;
+ bool eth_type_ipv4 = true;
+ unsigned int spec_index;
+
+ /* Validate that ethertype is correct */
+ for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
+ if (ib_spec->type == IB_FLOW_SPEC_ETH &&
+ ib_spec->eth.mask.ether_type) {
+ if (!((ib_spec->eth.mask.ether_type == htons(0xffff)) &&
+ ib_spec->eth.val.ether_type == htons(ETH_P_IP)))
+ eth_type_ipv4 = false;
+ } else if (ib_spec->type == IB_FLOW_SPEC_IPV4) {
+ has_ipv4_spec = true;
+ }
+ ib_spec = (void *)ib_spec + ib_spec->size;
+ }
+ return !has_ipv4_spec || eth_type_ipv4;
+}
+
+static void put_flow_table(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_flow_prio *prio, bool ft_added)
+{
+ prio->refcount -= !!ft_added;
+ if (!prio->refcount) {
+ mlx5_destroy_flow_table(prio->flow_table);
+ prio->flow_table = NULL;
+ }
+}
+
+static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
+{
+ struct mlx5_ib_dev *dev = to_mdev(flow_id->qp->device);
+ struct mlx5_ib_flow_handler *handler = container_of(flow_id,
+ struct mlx5_ib_flow_handler,
+ ibflow);
+ struct mlx5_ib_flow_handler *iter, *tmp;
+
+ mutex_lock(&dev->flow_db.lock);
+
+ list_for_each_entry_safe(iter, tmp, &handler->list, list) {
+ mlx5_del_flow_rule(iter->rule);
+ list_del(&iter->list);
+ kfree(iter);
+ }
+
+ mlx5_del_flow_rule(handler->rule);
+ put_flow_table(dev, &dev->flow_db.prios[handler->prio], true);
+ mutex_unlock(&dev->flow_db.lock);
+
+ kfree(handler);
+
+ return 0;
+}
+
+#define MLX5_FS_MAX_TYPES 10
+#define MLX5_FS_MAX_ENTRIES 32000UL
+static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
+ struct ib_flow_attr *flow_attr)
+{
+ struct mlx5_flow_namespace *ns = NULL;
+ struct mlx5_ib_flow_prio *prio;
+ struct mlx5_flow_table *ft;
+ int num_entries;
+ int num_groups;
+ int priority;
+ int err = 0;
+
+ if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
+ if (flow_is_multicast_only(flow_attr))
+ priority = MLX5_IB_FLOW_MCAST_PRIO;
+ else
+ priority = flow_attr->priority;
+ ns = mlx5_get_flow_namespace(dev->mdev,
+ MLX5_FLOW_NAMESPACE_BYPASS);
+ num_entries = MLX5_FS_MAX_ENTRIES;
+ num_groups = MLX5_FS_MAX_TYPES;
+ prio = &dev->flow_db.prios[priority];
+ } else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
+ flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) {
+ ns = mlx5_get_flow_namespace(dev->mdev,
+ MLX5_FLOW_NAMESPACE_LEFTOVERS);
+ build_leftovers_ft_param(&priority,
+ &num_entries,
+ &num_groups);
+ prio = &dev->flow_db.prios[MLX5_IB_FLOW_LEFTOVERS_PRIO];
+ }
+
+ if (!ns)
+ return ERR_PTR(-ENOTSUPP);
+
+ ft = prio->flow_table;
+ if (!ft) {
+ ft = mlx5_create_auto_grouped_flow_table(ns, priority,
+ num_entries,
+ num_groups);
+
+ if (!IS_ERR(ft)) {
+ prio->refcount = 0;
+ prio->flow_table = ft;
+ } else {
+ err = PTR_ERR(ft);
+ }
+ }
+
+ return err ? ERR_PTR(err) : prio;
+}
+
+static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_flow_prio *ft_prio,
+ struct ib_flow_attr *flow_attr,
+ struct mlx5_flow_destination *dst)
+{
+ struct mlx5_flow_table *ft = ft_prio->flow_table;
+ struct mlx5_ib_flow_handler *handler;
+ void *ib_flow = flow_attr + 1;
+ u8 match_criteria_enable = 0;
+ unsigned int spec_index;
+ u32 *match_c;
+ u32 *match_v;
+ int err = 0;
+
+ if (!is_valid_attr(flow_attr))
+ return ERR_PTR(-EINVAL);
+
+ match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ handler = kzalloc(sizeof(*handler), GFP_KERNEL);
+ if (!handler || !match_c || !match_v) {
+ err = -ENOMEM;
+ goto free;
+ }
+
+ INIT_LIST_HEAD(&handler->list);
+
+ for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
+ err = parse_flow_attr(match_c, match_v, ib_flow);
+ if (err < 0)
+ goto free;
+
+ ib_flow += ((union ib_flow_spec *)ib_flow)->size;
+ }
+
+ /* Outer header support only */
+ match_criteria_enable = (!outer_header_zero(match_c)) << 0;
+ handler->rule = mlx5_add_flow_rule(ft, match_criteria_enable,
+ match_c, match_v,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG,
+ dst);
+
+ if (IS_ERR(handler->rule)) {
+ err = PTR_ERR(handler->rule);
+ goto free;
+ }
+
+ handler->prio = ft_prio - dev->flow_db.prios;
+
+ ft_prio->flow_table = ft;
+free:
+ if (err)
+ kfree(handler);
+ kfree(match_c);
+ kfree(match_v);
+ return err ? ERR_PTR(err) : handler;
+}
+
+enum {
+ LEFTOVERS_MC,
+ LEFTOVERS_UC,
+};
+
+static struct mlx5_ib_flow_handler *create_leftovers_rule(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_flow_prio *ft_prio,
+ struct ib_flow_attr *flow_attr,
+ struct mlx5_flow_destination *dst)
+{
+ struct mlx5_ib_flow_handler *handler_ucast = NULL;
+ struct mlx5_ib_flow_handler *handler = NULL;
+
+ static struct {
+ struct ib_flow_attr flow_attr;
+ struct ib_flow_spec_eth eth_flow;
+ } leftovers_specs[] = {
+ [LEFTOVERS_MC] = {
+ .flow_attr = {
+ .num_of_specs = 1,
+ .size = sizeof(leftovers_specs[0])
+ },
+ .eth_flow = {
+ .type = IB_FLOW_SPEC_ETH,
+ .size = sizeof(struct ib_flow_spec_eth),
+ .mask = {.dst_mac = {0x1} },
+ .val = {.dst_mac = {0x1} }
+ }
+ },
+ [LEFTOVERS_UC] = {
+ .flow_attr = {
+ .num_of_specs = 1,
+ .size = sizeof(leftovers_specs[0])
+ },
+ .eth_flow = {
+ .type = IB_FLOW_SPEC_ETH,
+ .size = sizeof(struct ib_flow_spec_eth),
+ .mask = {.dst_mac = {0x1} },
+ .val = {.dst_mac = {} }
+ }
+ }
+ };
+
+ handler = create_flow_rule(dev, ft_prio,
+ &leftovers_specs[LEFTOVERS_MC].flow_attr,
+ dst);
+ if (!IS_ERR(handler) &&
+ flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT) {
+ handler_ucast = create_flow_rule(dev, ft_prio,
+ &leftovers_specs[LEFTOVERS_UC].flow_attr,
+ dst);
+ if (IS_ERR(handler_ucast)) {
+ kfree(handler);
+ handler = handler_ucast;
+ } else {
+ list_add(&handler_ucast->list, &handler->list);
+ }
+ }
+
+ return handler;
+}
+
+static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
+ struct ib_flow_attr *flow_attr,
+ int domain)
+{
+ struct mlx5_ib_dev *dev = to_mdev(qp->device);
+ struct mlx5_ib_flow_handler *handler = NULL;
+ struct mlx5_flow_destination *dst = NULL;
+ struct mlx5_ib_flow_prio *ft_prio;
+ int err;
+
+ if (flow_attr->priority > MLX5_IB_FLOW_LAST_PRIO)
+ return ERR_PTR(-ENOSPC);
+
+ if (domain != IB_FLOW_DOMAIN_USER ||
+ flow_attr->port > MLX5_CAP_GEN(dev->mdev, num_ports) ||
+ flow_attr->flags)
+ return ERR_PTR(-EINVAL);
+
+ dst = kzalloc(sizeof(*dst), GFP_KERNEL);
+ if (!dst)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&dev->flow_db.lock);
+
+ ft_prio = get_flow_table(dev, flow_attr);
+ if (IS_ERR(ft_prio)) {
+ err = PTR_ERR(ft_prio);
+ goto unlock;
+ }
+
+ dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR;
+ dst->tir_num = to_mqp(qp)->raw_packet_qp.rq.tirn;
+
+ if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
+ handler = create_flow_rule(dev, ft_prio, flow_attr,
+ dst);
+ } else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
+ flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) {
+ handler = create_leftovers_rule(dev, ft_prio, flow_attr,
+ dst);
+ } else {
+ err = -EINVAL;
+ goto destroy_ft;
+ }
+
+ if (IS_ERR(handler)) {
+ err = PTR_ERR(handler);
+ handler = NULL;
+ goto destroy_ft;
+ }
+
+ ft_prio->refcount++;
+ mutex_unlock(&dev->flow_db.lock);
+ kfree(dst);
+
+ return &handler->ibflow;
+
+destroy_ft:
+ put_flow_table(dev, ft_prio, false);
+unlock:
+ mutex_unlock(&dev->flow_db.lock);
+ kfree(dst);
+ kfree(handler);
+ return ERR_PTR(err);
+}
+
static int mlx5_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
{
struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
@@ -1439,10 +1893,19 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
(1ull << IB_USER_VERBS_CMD_CLOSE_XRCD);
}
+ if (mlx5_ib_port_link_layer(&dev->ib_dev) ==
+ IB_LINK_LAYER_ETHERNET) {
+ dev->ib_dev.create_flow = mlx5_ib_create_flow;
+ dev->ib_dev.destroy_flow = mlx5_ib_destroy_flow;
+ dev->ib_dev.uverbs_ex_cmd_mask |=
+ (1ull << IB_USER_VERBS_EX_CMD_CREATE_FLOW) |
+ (1ull << IB_USER_VERBS_EX_CMD_DESTROY_FLOW);
+ }
err = init_node_data(dev);
if (err)
goto err_dealloc;
+ mutex_init(&dev->flow_db.lock);
mutex_init(&dev->cap_mask_mutex);
err = create_dev_resources(&dev->devr);
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index 633347260b79..1474cccd1e0f 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -105,6 +105,36 @@ struct mlx5_ib_pd {
u32 pdn;
};
+#define MLX5_IB_FLOW_MCAST_PRIO (MLX5_BY_PASS_NUM_PRIOS - 1)
+#define MLX5_IB_FLOW_LAST_PRIO (MLX5_IB_FLOW_MCAST_PRIO - 1)
+#if (MLX5_IB_FLOW_LAST_PRIO <= 0)
+#error "Invalid number of bypass priorities"
+#endif
+#define MLX5_IB_FLOW_LEFTOVERS_PRIO (MLX5_IB_FLOW_MCAST_PRIO + 1)
+
+#define MLX5_IB_NUM_FLOW_FT (MLX5_IB_FLOW_LEFTOVERS_PRIO + 1)
+struct mlx5_ib_flow_prio {
+ struct mlx5_flow_table *flow_table;
+ unsigned int refcount;
+};
+
+struct mlx5_ib_flow_handler {
+ struct list_head list;
+ struct ib_flow ibflow;
+ unsigned int prio;
+ struct mlx5_flow_rule *rule;
+};
+
+struct mlx5_ib_flow_db {
+ struct mlx5_ib_flow_prio prios[MLX5_IB_NUM_FLOW_FT];
+ /* Protect flow steering bypass flow tables
+ * when add/del flow rules.
+ * only single add/removal of flow steering rule could be done
+ * simultaneously.
+ */
+ struct mutex lock;
+};
+
/* Use macros here so that don't have to duplicate
* enum ib_send_flags and enum ib_qp_type for low-level driver
*/
@@ -171,9 +201,21 @@ struct mlx5_ib_pfault {
struct mlx5_pagefault mpfault;
};
+struct mlx5_ib_rq {
+ u32 tirn;
+};
+
+struct mlx5_ib_raw_packet_qp {
+ struct mlx5_ib_rq rq;
+};
+
struct mlx5_ib_qp {
struct ib_qp ibqp;
- struct mlx5_core_qp mqp;
+ union {
+ struct mlx5_core_qp mqp;
+ struct mlx5_ib_raw_packet_qp raw_packet_qp;
+ };
+
struct mlx5_buf buf;
struct mlx5_db db;
@@ -431,6 +473,7 @@ struct mlx5_ib_dev {
*/
struct srcu_struct mr_srcu;
#endif
+ struct mlx5_ib_flow_db flow_db;
};
static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile
index f1f777570e8e..91c81965e7ca 100644
--- a/drivers/isdn/Makefile
+++ b/drivers/isdn/Makefile
@@ -10,7 +10,6 @@ obj-$(CONFIG_ISDN_DIVERSION) += divert/
obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/
obj-$(CONFIG_ISDN_DRV_ICN) += icn/
obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit/
-obj-$(CONFIG_ISDN_DRV_SC) += sc/
obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop/
obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000/
obj-$(CONFIG_HYSDN) += hysdn/
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
index c3a1b061838d..68073d0da0e3 100644
--- a/drivers/isdn/act2000/module.c
+++ b/drivers/isdn/act2000/module.c
@@ -37,7 +37,7 @@ MODULE_DESCRIPTION("ISDN4Linux: Driver for IBM Active 2000 ISDN card");
MODULE_AUTHOR("Fritz Elfert");
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA");
-MODULE_PARM_DESC(membase, "Base port address of first card");
+MODULE_PARM_DESC(act_port, "Base port address of first card");
MODULE_PARM_DESC(act_irq, "IRQ of first card");
MODULE_PARM_DESC(act_id, "ID-String of first card");
module_param(act_bus, int, 0);
diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig
index 9c6650ea848e..f5b714cd7618 100644
--- a/drivers/isdn/i4l/Kconfig
+++ b/drivers/isdn/i4l/Kconfig
@@ -130,8 +130,6 @@ source "drivers/isdn/icn/Kconfig"
source "drivers/isdn/pcbit/Kconfig"
-source "drivers/isdn/sc/Kconfig"
-
source "drivers/isdn/act2000/Kconfig"
endmenu
diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig
deleted file mode 100644
index 7469863a7925..000000000000
--- a/drivers/isdn/sc/Kconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-config ISDN_DRV_SC
- tristate "Spellcaster support"
- depends on ISA
- help
- This enables support for the Spellcaster BRI ISDN boards. This
- driver currently builds only in a modularized version.
- To build it, choose M here: the module will be called sc.
- See <file:Documentation/isdn/README.sc> for more information.
diff --git a/drivers/isdn/sc/Makefile b/drivers/isdn/sc/Makefile
deleted file mode 100644
index 0f2b7d602ac0..000000000000
--- a/drivers/isdn/sc/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# Makefile for the sc ISDN device driver
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_ISDN_DRV_SC) += sc.o
-
-# Multipart objects.
-
-sc-y := shmem.o init.o packet.o command.o event.o \
- ioctl.o interrupt.o message.o timer.o
diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h
deleted file mode 100644
index 3da69ee43da7..000000000000
--- a/drivers/isdn/sc/card.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* $Id: card.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Driver parameters for SpellCaster ISA ISDN adapters
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#ifndef CARD_H
-#define CARD_H
-
-/*
- * We need these if they're not already included
- */
-#include <linux/timer.h>
-#include <linux/time.h>
-#include <linux/isdnif.h>
-#include <linux/irqreturn.h>
-#include "message.h"
-#include "scioc.h"
-
-/*
- * Amount of time to wait for a reset to complete
- */
-#define CHECKRESET_TIME msecs_to_jiffies(4000)
-
-/*
- * Amount of time between line status checks
- */
-#define CHECKSTAT_TIME msecs_to_jiffies(8000)
-
-/*
- * The maximum amount of time to wait for a message response
- * to arrive. Use exclusively by send_and_receive
- */
-#define SAR_TIMEOUT msecs_to_jiffies(10000)
-
-/*
- * Macro to determine is a card id is valid
- */
-#define IS_VALID_CARD(x) ((x >= 0) && (x <= cinst))
-
-/*
- * Per channel status and configuration
- */
-typedef struct {
- int l2_proto;
- int l3_proto;
- char dn[50];
- unsigned long first_sendbuf; /* Offset of first send buffer */
- unsigned int num_sendbufs; /* Number of send buffers */
- unsigned int free_sendbufs; /* Number of free sendbufs */
- unsigned int next_sendbuf; /* Next sequential buffer */
- char eazlist[50]; /* Set with SETEAZ */
- char sillist[50]; /* Set with SETSIL */
- int eazclear; /* Don't accept calls if TRUE */
-} bchan;
-
-/*
- * Everything you want to know about the adapter ...
- */
-typedef struct {
- int model;
- int driverId; /* LL Id */
- char devicename[20]; /* The device name */
- isdn_if *card; /* ISDN4Linux structure */
- bchan *channel; /* status of the B channels */
- char nChannels; /* Number of channels */
- unsigned int interrupt; /* Interrupt number */
- int iobase; /* I/O Base address */
- int ioport[MAX_IO_REGS]; /* Index to I/O ports */
- int shmem_pgport; /* port for the exp mem page reg. */
- int shmem_magic; /* adapter magic number */
- unsigned int rambase; /* Shared RAM base address */
- unsigned int ramsize; /* Size of shared memory */
- RspMessage async_msg; /* Async response message */
- int want_async_messages; /* Snoop the Q ? */
- unsigned char seq_no; /* Next send seq. number */
- struct timer_list reset_timer; /* Check reset timer */
- struct timer_list stat_timer; /* Check startproc timer */
- unsigned char nphystat; /* Latest PhyStat info */
- unsigned char phystat; /* Last PhyStat info */
- HWConfig_pl hwconfig; /* Hardware config info */
- char load_ver[11]; /* CommManage Version string */
- char proc_ver[11]; /* CommEngine Version */
- int StartOnReset; /* Indicates startproc after reset */
- int EngineUp; /* Indicates CommEngine Up */
- int trace_mode; /* Indicate if tracing is on */
- spinlock_t lock; /* local lock */
-} board;
-
-
-extern board *sc_adapter[];
-extern int cinst;
-
-void memcpy_toshmem(int card, void *dest, const void *src, size_t n);
-void memcpy_fromshmem(int card, void *dest, const void *src, size_t n);
-int get_card_from_id(int driver);
-int indicate_status(int card, int event, ulong Channel, char *Data);
-irqreturn_t interrupt_handler(int interrupt, void *cardptr);
-int sndpkt(int devId, int channel, int ack, struct sk_buff *data);
-void rcvpkt(int card, RspMessage *rcvmsg);
-int command(isdn_ctrl *cmd);
-int reset(int card);
-int startproc(int card);
-int send_and_receive(int card, unsigned int procid, unsigned char type,
- unsigned char class, unsigned char code,
- unsigned char link, unsigned char data_len,
- unsigned char *data, RspMessage *mesgdata, int timeout);
-void flushreadfifo(int card);
-int sendmessage(int card, unsigned int procid, unsigned int type,
- unsigned int class, unsigned int code, unsigned int link,
- unsigned int data_len, unsigned int *data);
-int receivemessage(int card, RspMessage *rspmsg);
-int sc_ioctl(int card, scs_ioctl *data);
-int setup_buffers(int card, int c);
-void sc_check_reset(unsigned long data);
-void check_phystat(unsigned long data);
-
-#endif /* CARD_H */
diff --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c
deleted file mode 100644
index 4a4e66152ce7..000000000000
--- a/drivers/isdn/sc/command.c
+++ /dev/null
@@ -1,363 +0,0 @@
-/* $Id: command.c,v 1.4.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include <linux/module.h>
-#include "includes.h" /* This must be first */
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-#include "scioc.h"
-
-static int dial(int card, unsigned long channel, setup_parm setup);
-static int hangup(int card, unsigned long channel);
-static int answer(int card, unsigned long channel);
-static int clreaz(int card, unsigned long channel);
-static int seteaz(int card, unsigned long channel, char *);
-static int setl2(int card, unsigned long arg);
-static int setl3(int card, unsigned long arg);
-static int acceptb(int card, unsigned long channel);
-
-#ifdef DEBUG
-/*
- * Translate command codes to strings
- */
-static char *commands[] = { "ISDN_CMD_IOCTL",
- "ISDN_CMD_DIAL",
- "ISDN_CMD_ACCEPTB",
- "ISDN_CMD_ACCEPTB",
- "ISDN_CMD_HANGUP",
- "ISDN_CMD_CLREAZ",
- "ISDN_CMD_SETEAZ",
- NULL,
- NULL,
- NULL,
- "ISDN_CMD_SETL2",
- NULL,
- "ISDN_CMD_SETL3",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL, };
-
-/*
- * Translates ISDN4Linux protocol codes to strings for debug messages
- */
-static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" };
-static char *l2protos[] = { "ISDN_PROTO_L2_X75I",
- "ISDN_PROTO_L2_X75UI",
- "ISDN_PROTO_L2_X75BUI",
- "ISDN_PROTO_L2_HDLC",
- "ISDN_PROTO_L2_TRANS" };
-#endif
-
-int get_card_from_id(int driver)
-{
- int i;
-
- for (i = 0; i < cinst; i++) {
- if (sc_adapter[i]->driverId == driver)
- return i;
- }
- return -ENODEV;
-}
-
-/*
- * command
- */
-
-int command(isdn_ctrl *cmd)
-{
- int card;
-
- card = get_card_from_id(cmd->driver);
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*
- * Dispatch the command
- */
- switch (cmd->command) {
- case ISDN_CMD_IOCTL:
- {
- unsigned long cmdptr;
- scs_ioctl ioc;
-
- memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
- if (copy_from_user(&ioc, (scs_ioctl __user *)cmdptr,
- sizeof(scs_ioctl))) {
- pr_debug("%s: Failed to verify user space 0x%lx\n",
- sc_adapter[card]->devicename, cmdptr);
- return -EFAULT;
- }
- return sc_ioctl(card, &ioc);
- }
- case ISDN_CMD_DIAL:
- return dial(card, cmd->arg, cmd->parm.setup);
- case ISDN_CMD_HANGUP:
- return hangup(card, cmd->arg);
- case ISDN_CMD_ACCEPTD:
- return answer(card, cmd->arg);
- case ISDN_CMD_ACCEPTB:
- return acceptb(card, cmd->arg);
- case ISDN_CMD_CLREAZ:
- return clreaz(card, cmd->arg);
- case ISDN_CMD_SETEAZ:
- return seteaz(card, cmd->arg, cmd->parm.num);
- case ISDN_CMD_SETL2:
- return setl2(card, cmd->arg);
- case ISDN_CMD_SETL3:
- return setl3(card, cmd->arg);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * start the onboard firmware
- */
-int startproc(int card)
-{
- int status;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*
- * send start msg
- */
- status = sendmessage(card, CMPID, cmReqType2,
- cmReqClass0,
- cmReqStartProc,
- 0, 0, NULL);
- pr_debug("%s: Sent startProc\n", sc_adapter[card]->devicename);
-
- return status;
-}
-
-
-/*
- * Dials the number passed in
- */
-static int dial(int card, unsigned long channel, setup_parm setup)
-{
- int status;
- char Phone[48];
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*extract ISDN number to dial from eaz/msn string*/
- strcpy(Phone, setup.phone);
-
- /*send the connection message*/
- status = sendmessage(card, CEPID, ceReqTypePhy,
- ceReqClass1,
- ceReqPhyConnect,
- (unsigned char)channel + 1,
- strlen(Phone),
- (unsigned int *)Phone);
-
- pr_debug("%s: Dialing %s on channel %lu\n",
- sc_adapter[card]->devicename, Phone, channel + 1);
-
- return status;
-}
-
-/*
- * Answer an incoming call
- */
-static int answer(int card, unsigned long channel)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- if (setup_buffers(card, channel + 1)) {
- hangup(card, channel + 1);
- return -ENOBUFS;
- }
-
- indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
- pr_debug("%s: Answered incoming call on channel %lu\n",
- sc_adapter[card]->devicename, channel + 1);
- return 0;
-}
-
-/*
- * Hangup up the call on specified channel
- */
-static int hangup(int card, unsigned long channel)
-{
- int status;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- status = sendmessage(card, CEPID, ceReqTypePhy,
- ceReqClass1,
- ceReqPhyDisconnect,
- (unsigned char)channel + 1,
- 0,
- NULL);
- pr_debug("%s: Sent HANGUP message to channel %lu\n",
- sc_adapter[card]->devicename, channel + 1);
- return status;
-}
-
-/*
- * Set the layer 2 protocol (X.25, HDLC, Raw)
- */
-static int setl2(int card, unsigned long arg)
-{
- int status = 0;
- int protocol, channel;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
- protocol = arg >> 8;
- channel = arg & 0xff;
- sc_adapter[card]->channel[channel].l2_proto = protocol;
-
- /*
- * check that the adapter is also set to the correct protocol
- */
- pr_debug("%s: Sending GetFrameFormat for channel %d\n",
- sc_adapter[card]->devicename, channel + 1);
- status = sendmessage(card, CEPID, ceReqTypeCall,
- ceReqClass0,
- ceReqCallGetFrameFormat,
- (unsigned char)channel + 1,
- 1,
- (unsigned int *)protocol);
- if (status)
- return status;
- return 0;
-}
-
-/*
- * Set the layer 3 protocol
- */
-static int setl3(int card, unsigned long channel)
-{
- int protocol = channel >> 8;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- sc_adapter[card]->channel[channel].l3_proto = protocol;
- return 0;
-}
-
-static int acceptb(int card, unsigned long channel)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- if (setup_buffers(card, channel + 1))
- {
- hangup(card, channel + 1);
- return -ENOBUFS;
- }
-
- pr_debug("%s: B-Channel connection accepted on channel %lu\n",
- sc_adapter[card]->devicename, channel + 1);
- indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
- return 0;
-}
-
-static int clreaz(int card, unsigned long arg)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- strcpy(sc_adapter[card]->channel[arg].eazlist, "");
- sc_adapter[card]->channel[arg].eazclear = 1;
- pr_debug("%s: EAZ List cleared for channel %lu\n",
- sc_adapter[card]->devicename, arg + 1);
- return 0;
-}
-
-static int seteaz(int card, unsigned long arg, char *num)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- strcpy(sc_adapter[card]->channel[arg].eazlist, num);
- sc_adapter[card]->channel[arg].eazclear = 0;
- pr_debug("%s: EAZ list for channel %lu set to: %s\n",
- sc_adapter[card]->devicename, arg + 1,
- sc_adapter[card]->channel[arg].eazlist);
- return 0;
-}
-
-int reset(int card)
-{
- unsigned long flags;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- indicate_status(card, ISDN_STAT_STOP, 0, NULL);
-
- if (sc_adapter[card]->EngineUp) {
- del_timer(&sc_adapter[card]->stat_timer);
- }
-
- sc_adapter[card]->EngineUp = 0;
-
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- init_timer(&sc_adapter[card]->reset_timer);
- sc_adapter[card]->reset_timer.function = sc_check_reset;
- sc_adapter[card]->reset_timer.data = card;
- sc_adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
- add_timer(&sc_adapter[card]->reset_timer);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-
- outb(0x1, sc_adapter[card]->ioport[SFT_RESET]);
-
- pr_debug("%s: Adapter Reset\n", sc_adapter[card]->devicename);
- return 0;
-}
-
-void flushreadfifo(int card)
-{
- while (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA)
- inb(sc_adapter[card]->ioport[FIFO_READ]);
-}
diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c
deleted file mode 100644
index 833d96c2cf92..000000000000
--- a/drivers/isdn/sc/event.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* $Id: event.c,v 1.4.8.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-#ifdef DEBUG
-static char *events[] = { "ISDN_STAT_STAVAIL",
- "ISDN_STAT_ICALL",
- "ISDN_STAT_RUN",
- "ISDN_STAT_STOP",
- "ISDN_STAT_DCONN",
- "ISDN_STAT_BCONN",
- "ISDN_STAT_DHUP",
- "ISDN_STAT_BHUP",
- "ISDN_STAT_CINF",
- "ISDN_STAT_LOAD",
- "ISDN_STAT_UNLOAD",
- "ISDN_STAT_BSENT",
- "ISDN_STAT_NODCH",
- "ISDN_STAT_ADDCH",
- "ISDN_STAT_CAUSE" };
-#endif
-
-int indicate_status(int card, int event, ulong Channel, char *Data)
-{
- isdn_ctrl cmd;
-
-#ifdef DEBUG
- pr_debug("%s: Indicating event %s on Channel %d\n",
- sc_adapter[card]->devicename, events[event - 256], Channel);
-#endif
- if (Data != NULL) {
- pr_debug("%s: Event data: %s\n", sc_adapter[card]->devicename,
- Data);
- switch (event) {
- case ISDN_STAT_BSENT:
- memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length));
- break;
- case ISDN_STAT_ICALL:
- memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
- break;
- default:
- strlcpy(cmd.parm.num, Data, sizeof(cmd.parm.num));
- }
- }
-
- cmd.command = event;
- cmd.driver = sc_adapter[card]->driverId;
- cmd.arg = Channel;
- return sc_adapter[card]->card->statcallb(&cmd);
-}
diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h
deleted file mode 100644
index 81fbe78701f0..000000000000
--- a/drivers/isdn/sc/hardware.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Hardware specific macros, defines and structures
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#ifndef HARDWARE_H
-#define HARDWARE_H
-
-#include <asm/param.h> /* For HZ */
-
-/*
- * General hardware parameters common to all ISA adapters
- */
-
-#define MAX_CARDS 4 /* The maximum number of cards to
- control or probe for. */
-
-#define SIGNATURE 0x87654321 /* Board reset signature */
-#define SIG_OFFSET 0x1004 /* Where to find signature in shared RAM */
-#define TRACE_OFFSET 0x1008 /* Trace enable word offset in shared RAM */
-#define BUFFER_OFFSET 0x1800 /* Beginning of buffers */
-
-/* I/O Port parameters */
-#define IOBASE_MIN 0x180 /* Lowest I/O port address */
-#define IOBASE_MAX 0x3C0 /* Highest I/O port address */
-#define IOBASE_OFFSET 0x20 /* Inter-board I/O port gap used during
- probing */
-#define FIFORD_OFFSET 0x0
-#define FIFOWR_OFFSET 0x400
-#define FIFOSTAT_OFFSET 0x1000
-#define RESET_OFFSET 0x2800
-#define PG0_OFFSET 0x3000 /* Offset from I/O Base for Page 0 register */
-#define PG1_OFFSET 0x3400 /* Offset from I/O Base for Page 1 register */
-#define PG2_OFFSET 0x3800 /* Offset from I/O Base for Page 2 register */
-#define PG3_OFFSET 0x3C00 /* Offset from I/O Base for Page 3 register */
-
-#define FIFO_READ 0 /* FIFO Read register */
-#define FIFO_WRITE 1 /* FIFO Write rgister */
-#define LO_ADDR_PTR 2 /* Extended RAM Low Addr Pointer */
-#define HI_ADDR_PTR 3 /* Extended RAM High Addr Pointer */
-#define NOT_USED_1 4
-#define FIFO_STATUS 5 /* FIFO Status Register */
-#define NOT_USED_2 6
-#define MEM_OFFSET 7
-#define SFT_RESET 10 /* Reset Register */
-#define EXP_BASE 11 /* Shared RAM Base address */
-#define EXP_PAGE0 12 /* Shared RAM Page0 register */
-#define EXP_PAGE1 13 /* Shared RAM Page1 register */
-#define EXP_PAGE2 14 /* Shared RAM Page2 register */
-#define EXP_PAGE3 15 /* Shared RAM Page3 register */
-#define IRQ_SELECT 16 /* IRQ selection register */
-#define MAX_IO_REGS 17 /* Total number of I/O ports */
-
-/* FIFO register values */
-#define RF_HAS_DATA 0x01 /* fifo has data */
-#define RF_QUART_FULL 0x02 /* fifo quarter full */
-#define RF_HALF_FULL 0x04 /* fifo half full */
-#define RF_NOT_FULL 0x08 /* fifo not full */
-#define WF_HAS_DATA 0x10 /* fifo has data */
-#define WF_QUART_FULL 0x20 /* fifo quarter full */
-#define WF_HALF_FULL 0x40 /* fifo half full */
-#define WF_NOT_FULL 0x80 /* fifo not full */
-
-/* Shared RAM parameters */
-#define SRAM_MIN 0xC0000 /* Lowest host shared RAM address */
-#define SRAM_MAX 0xEFFFF /* Highest host shared RAM address */
-#define SRAM_PAGESIZE 0x4000 /* Size of one RAM page (16K) */
-
-/* Shared RAM buffer parameters */
-#define BUFFER_SIZE 0x800 /* The size of a buffer in bytes */
-#define BUFFER_BASE BUFFER_OFFSET /* Offset from start of shared RAM
- where buffer start */
-#define BUFFERS_MAX 16 /* Maximum number of send/receive
- buffers per channel */
-#define HDLC_PROTO 0x01 /* Frame Format for Layer 2 */
-
-#define BRI_BOARD 0
-#define POTS_BOARD 1
-#define PRI_BOARD 2
-
-/*
- * Specific hardware parameters for the DataCommute/BRI
- */
-#define BRI_CHANNELS 2 /* Number of B channels */
-#define BRI_BASEPG_VAL 0x98
-#define BRI_MAGIC 0x60000 /* Magic Number */
-#define BRI_MEMSIZE 0x10000 /* Amount of RAM (64K) */
-#define BRI_PARTNO "72-029"
-#define BRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
-/*
- * Specific hardware parameters for the DataCommute/PRI
- */
-#define PRI_CHANNELS 23 /* Number of B channels */
-#define PRI_BASEPG_VAL 0x88
-#define PRI_MAGIC 0x20000 /* Magic Number */
-#define PRI_MEMSIZE 0x100000 /* Amount of RAM (1M) */
-#define PRI_PARTNO "72-030"
-#define PRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
-
-/*
- * Some handy macros
- */
-
-/* Determine if a channel number is valid for the adapter */
-#define IS_VALID_CHANNEL(y, x) ((x > 0) && (x <= sc_adapter[y]->channels))
-
-#endif
diff --git a/drivers/isdn/sc/includes.h b/drivers/isdn/sc/includes.h
deleted file mode 100644
index 4766e5b77378..000000000000
--- a/drivers/isdn/sc/includes.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/errno.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/timer.h>
-#include <linux/wait.h>
-#include <linux/isdnif.h>
diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c
deleted file mode 100644
index 3597ef47b28a..000000000000
--- a/drivers/isdn/sc/init.c
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include "includes.h"
-#include "hardware.h"
-#include "card.h"
-
-MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card");
-MODULE_AUTHOR("Spellcaster Telecommunications Inc.");
-MODULE_LICENSE("GPL");
-
-board *sc_adapter[MAX_CARDS];
-int cinst;
-
-static char devname[] = "scX";
-static const char version[] = "2.0b1";
-
-static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
-
-/* insmod set parameters */
-static unsigned int io[] = {0, 0, 0, 0};
-static unsigned char irq[] = {0, 0, 0, 0};
-static unsigned long ram[] = {0, 0, 0, 0};
-static bool do_reset;
-
-module_param_array(io, int, NULL, 0);
-module_param_array(irq, byte, NULL, 0);
-module_param_array(ram, long, NULL, 0);
-module_param(do_reset, bool, 0);
-
-static int identify_board(unsigned long, unsigned int);
-
-static int __init sc_init(void)
-{
- int b = -1;
- int i, j;
- int status = -ENODEV;
-
- unsigned long memsize = 0;
- unsigned long features = 0;
- isdn_if *interface;
- unsigned char channels;
- unsigned char pgport;
- unsigned long magic;
- int model;
- int last_base = IOBASE_MIN;
- int probe_exhasted = 0;
-
-#ifdef MODULE
- pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
-#else
- pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
-#endif
- pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
-
- while (b++ < MAX_CARDS - 1) {
- pr_debug("Probing for adapter #%d\n", b);
- /*
- * Initialize reusable variables
- */
- model = -1;
- magic = 0;
- channels = 0;
- pgport = 0;
-
- /*
- * See if we should probe for IO base
- */
- pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
- io[b] == 0 ? "will" : "won't");
- if (io[b]) {
- /*
- * No, I/O Base has been provided
- */
- for (i = 0; i < MAX_IO_REGS - 1; i++) {
- if (!request_region(io[b] + i * 0x400, 1, "sc test")) {
- pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400);
- io[b] = 0;
- break;
- } else
- release_region(io[b] + i * 0x400, 1);
- }
-
- /*
- * Confirm the I/O Address with a test
- */
- if (io[b] == 0) {
- pr_debug("I/O Address invalid.\n");
- continue;
- }
-
- outb(0x18, io[b] + 0x400 * EXP_PAGE0);
- if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
- pr_debug("I/O Base 0x%x fails test\n",
- io[b] + 0x400 * EXP_PAGE0);
- continue;
- }
- } else {
- /*
- * Yes, probe for I/O Base
- */
- if (probe_exhasted) {
- pr_debug("All probe addresses exhausted, skipping\n");
- continue;
- }
- pr_debug("Probing for I/O...\n");
- for (i = last_base; i <= IOBASE_MAX; i += IOBASE_OFFSET) {
- int found_io = 1;
- if (i == IOBASE_MAX) {
- probe_exhasted = 1; /* No more addresses to probe */
- pr_debug("End of Probes\n");
- }
- last_base = i + IOBASE_OFFSET;
- pr_debug(" checking 0x%x...", i);
- for (j = 0; j < MAX_IO_REGS - 1; j++) {
- if (!request_region(i + j * 0x400, 1, "sc test")) {
- pr_debug("Failed\n");
- found_io = 0;
- break;
- } else
- release_region(i + j * 0x400, 1);
- }
-
- if (found_io) {
- io[b] = i;
- outb(0x18, io[b] + 0x400 * EXP_PAGE0);
- if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
- pr_debug("Failed by test\n");
- continue;
- }
- pr_debug("Passed\n");
- break;
- }
- }
- if (probe_exhasted) {
- continue;
- }
- }
-
- /*
- * See if we should probe for shared RAM
- */
- if (do_reset) {
- pr_debug("Doing a SAFE probe reset\n");
- outb(0xFF, io[b] + RESET_OFFSET);
- msleep_interruptible(10000);
- }
- pr_debug("RAM Base for board %d is 0x%lx, %s probe\n", b,
- ram[b], ram[b] == 0 ? "will" : "won't");
-
- if (ram[b]) {
- /*
- * No, the RAM base has been provided
- * Just look for a signature and ID the
- * board model
- */
- if (request_region(ram[b], SRAM_PAGESIZE, "sc test")) {
- pr_debug("request_region for RAM base 0x%lx succeeded\n", ram[b]);
- model = identify_board(ram[b], io[b]);
- release_region(ram[b], SRAM_PAGESIZE);
- }
- } else {
- /*
- * Yes, probe for free RAM and look for
- * a signature and id the board model
- */
- for (i = SRAM_MIN; i < SRAM_MAX; i += SRAM_PAGESIZE) {
- pr_debug("Checking RAM address 0x%x...\n", i);
- if (request_region(i, SRAM_PAGESIZE, "sc test")) {
- pr_debug(" request_region succeeded\n");
- model = identify_board(i, io[b]);
- release_region(i, SRAM_PAGESIZE);
- if (model >= 0) {
- pr_debug(" Identified a %s\n",
- boardname[model]);
- ram[b] = i;
- break;
- }
- pr_debug(" Unidentified or inaccessible\n");
- continue;
- }
- pr_debug(" request failed\n");
- }
- }
- /*
- * See if we found free RAM and the board model
- */
- if (!ram[b] || model < 0) {
- /*
- * Nope, there was no place in RAM for the
- * board, or it couldn't be identified
- */
- pr_debug("Failed to find an adapter at 0x%lx\n", ram[b]);
- continue;
- }
-
- /*
- * Set the board's magic number, memory size and page register
- */
- switch (model) {
- case PRI_BOARD:
- channels = 23;
- magic = 0x20000;
- memsize = 0x100000;
- features = PRI_FEATURES;
- break;
-
- case BRI_BOARD:
- case POTS_BOARD:
- channels = 2;
- magic = 0x60000;
- memsize = 0x10000;
- features = BRI_FEATURES;
- break;
- }
- switch (ram[b] >> 12 & 0x0F) {
- case 0x0:
- pr_debug("RAM Page register set to EXP_PAGE0\n");
- pgport = EXP_PAGE0;
- break;
-
- case 0x4:
- pr_debug("RAM Page register set to EXP_PAGE1\n");
- pgport = EXP_PAGE1;
- break;
-
- case 0x8:
- pr_debug("RAM Page register set to EXP_PAGE2\n");
- pgport = EXP_PAGE2;
- break;
-
- case 0xC:
- pr_debug("RAM Page register set to EXP_PAGE3\n");
- pgport = EXP_PAGE3;
- break;
-
- default:
- pr_debug("RAM base address doesn't fall on 16K boundary\n");
- continue;
- }
-
- pr_debug("current IRQ: %d b: %d\n", irq[b], b);
-
- /*
- * Make sure we got an IRQ
- */
- if (!irq[b]) {
- /*
- * No interrupt could be used
- */
- pr_debug("Failed to acquire an IRQ line\n");
- continue;
- }
-
- /*
- * Horray! We found a board, Make sure we can register
- * it with ISDN4Linux
- */
- interface = kzalloc(sizeof(isdn_if), GFP_KERNEL);
- if (interface == NULL) {
- /*
- * Oops, can't malloc isdn_if
- */
- continue;
- }
-
- interface->owner = THIS_MODULE;
- interface->hl_hdrlen = 0;
- interface->channels = channels;
- interface->maxbufsize = BUFFER_SIZE;
- interface->features = features;
- interface->writebuf_skb = sndpkt;
- interface->writecmd = NULL;
- interface->command = command;
- strcpy(interface->id, devname);
- interface->id[2] = '0' + cinst;
-
- /*
- * Allocate the board structure
- */
- sc_adapter[cinst] = kzalloc(sizeof(board), GFP_KERNEL);
- if (sc_adapter[cinst] == NULL) {
- /*
- * Oops, can't alloc memory for the board
- */
- kfree(interface);
- continue;
- }
- spin_lock_init(&sc_adapter[cinst]->lock);
-
- if (!register_isdn(interface)) {
- /*
- * Oops, couldn't register for some reason
- */
- kfree(interface);
- kfree(sc_adapter[cinst]);
- continue;
- }
-
- sc_adapter[cinst]->card = interface;
- sc_adapter[cinst]->driverId = interface->channels;
- strcpy(sc_adapter[cinst]->devicename, interface->id);
- sc_adapter[cinst]->nChannels = channels;
- sc_adapter[cinst]->ramsize = memsize;
- sc_adapter[cinst]->shmem_magic = magic;
- sc_adapter[cinst]->shmem_pgport = pgport;
- sc_adapter[cinst]->StartOnReset = 1;
-
- /*
- * Allocate channels status structures
- */
- sc_adapter[cinst]->channel = kzalloc(sizeof(bchan) * channels, GFP_KERNEL);
- if (sc_adapter[cinst]->channel == NULL) {
- /*
- * Oops, can't alloc memory for the channels
- */
- indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
- kfree(interface);
- kfree(sc_adapter[cinst]);
- continue;
- }
-
- /*
- * Lock down the hardware resources
- */
- sc_adapter[cinst]->interrupt = irq[b];
- if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
- 0, interface->id,
- (void *)(unsigned long) cinst)) {
- kfree(sc_adapter[cinst]->channel);
- indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
- kfree(interface);
- kfree(sc_adapter[cinst]);
- continue;
-
- }
- sc_adapter[cinst]->iobase = io[b];
- for (i = 0; i < MAX_IO_REGS - 1; i++) {
- sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400;
- request_region(sc_adapter[cinst]->ioport[i], 1,
- interface->id);
- pr_debug("Requesting I/O Port %#x\n",
- sc_adapter[cinst]->ioport[i]);
- }
- sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
- request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1,
- interface->id);
- pr_debug("Requesting I/O Port %#x\n",
- sc_adapter[cinst]->ioport[IRQ_SELECT]);
- sc_adapter[cinst]->rambase = ram[b];
- request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE,
- interface->id);
-
- pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n",
- sc_adapter[cinst]->devicename,
- sc_adapter[cinst]->driverId,
- boardname[model], channels, irq[b], io[b], ram[b]);
-
- /*
- * reset the adapter to put things in motion
- */
- reset(cinst);
-
- cinst++;
- status = 0;
- }
- if (status)
- pr_info("Failed to find any adapters, driver unloaded\n");
- return status;
-}
-
-static void __exit sc_exit(void)
-{
- int i, j;
-
- for (i = 0; i < cinst; i++) {
- pr_debug("Cleaning up after adapter %d\n", i);
- /*
- * kill the timers
- */
- del_timer_sync(&(sc_adapter[i]->reset_timer));
- del_timer_sync(&(sc_adapter[i]->stat_timer));
-
- /*
- * Tell I4L we're toast
- */
- indicate_status(i, ISDN_STAT_STOP, 0, NULL);
- indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
-
- /*
- * Release shared RAM
- */
- release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE);
-
- /*
- * Release the IRQ
- */
- free_irq(sc_adapter[i]->interrupt, NULL);
-
- /*
- * Reset for a clean start
- */
- outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]);
-
- /*
- * Release the I/O Port regions
- */
- for (j = 0; j < MAX_IO_REGS - 1; j++) {
- release_region(sc_adapter[i]->ioport[j], 1);
- pr_debug("Releasing I/O Port %#x\n",
- sc_adapter[i]->ioport[j]);
- }
- release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1);
- pr_debug("Releasing I/O Port %#x\n",
- sc_adapter[i]->ioport[IRQ_SELECT]);
-
- /*
- * Release any memory we alloced
- */
- kfree(sc_adapter[i]->channel);
- kfree(sc_adapter[i]->card);
- kfree(sc_adapter[i]);
- }
- pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
-}
-
-static int identify_board(unsigned long rambase, unsigned int iobase)
-{
- unsigned int pgport;
- unsigned long sig;
- DualPortMemory *dpm;
- RspMessage rcvmsg;
- ReqMessage sndmsg;
- HWConfig_pl hwci;
- int x;
-
- pr_debug("Attempting to identify adapter @ 0x%lx io 0x%x\n",
- rambase, iobase);
-
- /*
- * Enable the base pointer
- */
- outb(rambase >> 12, iobase + 0x2c00);
-
- switch (rambase >> 12 & 0x0F) {
- case 0x0:
- pgport = iobase + PG0_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
- break;
-
- case 0x4:
- pgport = iobase + PG1_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
- break;
-
- case 0x8:
- pgport = iobase + PG2_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
- break;
-
- case 0xC:
- pgport = iobase + PG3_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
- break;
- default:
- pr_debug("Invalid rambase 0x%lx\n", rambase);
- return -1;
- }
-
- /*
- * Try to identify a PRI card
- */
- outb(PRI_BASEPG_VAL, pgport);
- msleep_interruptible(1000);
- sig = readl(rambase + SIG_OFFSET);
- pr_debug("Looking for a signature, got 0x%lx\n", sig);
- if (sig == SIGNATURE)
- return PRI_BOARD;
-
- /*
- * Try to identify a PRI card
- */
- outb(BRI_BASEPG_VAL, pgport);
- msleep_interruptible(1000);
- sig = readl(rambase + SIG_OFFSET);
- pr_debug("Looking for a signature, got 0x%lx\n", sig);
- if (sig == SIGNATURE)
- return BRI_BOARD;
-
- return -1;
-
- /*
- * Try to spot a card
- */
- sig = readl(rambase + SIG_OFFSET);
- pr_debug("Looking for a signature, got 0x%lx\n", sig);
- if (sig != SIGNATURE)
- return -1;
-
- dpm = (DualPortMemory *) rambase;
-
- memset(&sndmsg, 0, MSG_LEN);
- sndmsg.msg_byte_cnt = 3;
- sndmsg.type = cmReqType1;
- sndmsg.class = cmReqClass0;
- sndmsg.code = cmReqHWConfig;
- memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
- outb(0, iobase + 0x400);
- pr_debug("Sent HWConfig message\n");
- /*
- * Wait for the response
- */
- x = 0;
- while ((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
- schedule_timeout_interruptible(1);
- x++;
- }
- if (x == 100) {
- pr_debug("Timeout waiting for response\n");
- return -1;
- }
-
- memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
- pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
- memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
- pr_debug("Hardware Config: Interface: %s, RAM Size: %ld, Serial: %s\n"
- " Part: %s, Rev: %s\n",
- hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
- hwci.serial_no, hwci.part_no, hwci.rev_no);
-
- if (!strncmp(PRI_PARTNO, hwci.part_no, 6))
- return PRI_BOARD;
- if (!strncmp(BRI_PARTNO, hwci.part_no, 6))
- return BRI_BOARD;
-
- return -1;
-}
-
-module_init(sc_init);
-module_exit(sc_exit);
diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c
deleted file mode 100644
index e80cc76bc314..000000000000
--- a/drivers/isdn/sc/interrupt.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/* $Id: interrupt.c,v 1.4.8.3 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-#include <linux/interrupt.h>
-
-/*
- *
- */
-irqreturn_t interrupt_handler(int dummy, void *card_inst)
-{
-
- RspMessage rcvmsg;
- int channel;
- int card = (int)(unsigned long) card_inst;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return IRQ_NONE;
- }
-
- pr_debug("%s: Entered Interrupt handler\n",
- sc_adapter[card]->devicename);
-
- /*
- * Pull all of the waiting messages off the response queue
- */
- while (!receivemessage(card, &rcvmsg)) {
- /*
- * Push the message to the adapter structure for
- * send_and_receive to snoop
- */
- if (sc_adapter[card]->want_async_messages)
- memcpy(&(sc_adapter[card]->async_msg),
- &rcvmsg, sizeof(RspMessage));
-
- channel = (unsigned int) rcvmsg.phy_link_no;
-
- /*
- * Trap Invalid request messages
- */
- if (IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) {
- pr_debug("%s: Invalid request Message, rsp_status = %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.rsp_status);
- break;
- }
-
- /*
- * Check for a linkRead message
- */
- if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
- {
- pr_debug("%s: Received packet 0x%x bytes long at 0x%lx\n",
- sc_adapter[card]->devicename,
- rcvmsg.msg_data.response.msg_len,
- rcvmsg.msg_data.response.buff_offset);
- rcvpkt(card, &rcvmsg);
- continue;
-
- }
-
- /*
- * Handle a write acknoledgement
- */
- if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) {
- pr_debug("%s: Packet Send ACK on channel %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.phy_link_no);
- sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].free_sendbufs++;
- continue;
- }
-
- /*
- * Handle a connection message
- */
- if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect))
- {
- unsigned int callid;
- setup_parm setup;
- pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n",
- sc_adapter[card]->devicename,
- rcvmsg.phy_link_no,
- rcvmsg.rsp_status,
- rcvmsg.msg_data.byte_array[2]);
-
- memcpy(&callid, rcvmsg.msg_data.byte_array, sizeof(int));
- if (callid >= 0x8000 && callid <= 0xFFFF)
- {
- pr_debug("%s: Got Dial-Out Rsp\n",
- sc_adapter[card]->devicename);
- indicate_status(card, ISDN_STAT_DCONN,
- (unsigned long)rcvmsg.phy_link_no - 1, NULL);
-
- }
- else if (callid >= 0x0000 && callid <= 0x7FFF)
- {
- int len;
-
- pr_debug("%s: Got Incoming Call\n",
- sc_adapter[card]->devicename);
- len = strlcpy(setup.phone, &(rcvmsg.msg_data.byte_array[4]),
- sizeof(setup.phone));
- if (len >= sizeof(setup.phone))
- continue;
- len = strlcpy(setup.eazmsn,
- sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn,
- sizeof(setup.eazmsn));
- if (len >= sizeof(setup.eazmsn))
- continue;
- setup.si1 = 7;
- setup.si2 = 0;
- setup.plan = 0;
- setup.screen = 0;
-
- indicate_status(card, ISDN_STAT_ICALL, (unsigned long)rcvmsg.phy_link_no - 1, (char *)&setup);
- indicate_status(card, ISDN_STAT_DCONN, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
- }
- continue;
- }
-
- /*
- * Handle a disconnection message
- */
- if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect))
- {
- pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n",
- sc_adapter[card]->devicename,
- rcvmsg.phy_link_no,
- rcvmsg.rsp_status,
- rcvmsg.msg_data.byte_array[2]);
-
- indicate_status(card, ISDN_STAT_BHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
- indicate_status(card, ISDN_STAT_DHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
- continue;
-
- }
-
- /*
- * Handle a startProc engine up message
- */
- if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) {
- pr_debug("%s: Received EngineUp message\n",
- sc_adapter[card]->devicename);
- sc_adapter[card]->EngineUp = 1;
- sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 1, 0, NULL);
- sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 2, 0, NULL);
- init_timer(&sc_adapter[card]->stat_timer);
- sc_adapter[card]->stat_timer.function = check_phystat;
- sc_adapter[card]->stat_timer.data = card;
- sc_adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
- add_timer(&sc_adapter[card]->stat_timer);
- continue;
- }
-
- /*
- * Start proc response
- */
- if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) {
- pr_debug("%s: StartProc Response Status %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.rsp_status);
- continue;
- }
-
- /*
- * Handle a GetMyNumber Rsp
- */
- if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetMyNumber)) {
- strlcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn,
- rcvmsg.msg_data.byte_array,
- sizeof(rcvmsg.msg_data.byte_array));
- continue;
- }
-
- /*
- * PhyStatus response
- */
- if (IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) {
- unsigned int b1stat, b2stat;
-
- /*
- * Covert the message data to the adapter->phystat code
- */
- b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0];
- b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1];
-
- sc_adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */
- pr_debug("%s: PhyStat is 0x%2x\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->nphystat);
- continue;
- }
-
-
- /*
- * Handle a GetFramFormat
- */
- if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) {
- if (rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) {
- unsigned int proto = HDLC_PROTO;
- /*
- * Set board format to HDLC if it wasn't already
- */
- pr_debug("%s: current frame format: 0x%x, will change to HDLC\n",
- sc_adapter[card]->devicename,
- rcvmsg.msg_data.byte_array[0]);
- sendmessage(card, CEPID, ceReqTypeCall,
- ceReqClass0,
- ceReqCallSetFrameFormat,
- (unsigned char)channel + 1,
- 1, &proto);
- }
- continue;
- }
-
- /*
- * Hmm...
- */
- pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.type, rcvmsg.class, rcvmsg.code,
- rcvmsg.phy_link_no);
-
- } /* while */
-
- pr_debug("%s: Exiting Interrupt Handler\n",
- sc_adapter[card]->devicename);
- return IRQ_HANDLED;
-}
diff --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c
deleted file mode 100644
index e63983aa1d27..000000000000
--- a/drivers/isdn/sc/ioctl.c
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-#include "scioc.h"
-
-static int GetStatus(int card, boardInfo *);
-
-/*
- * Process private IOCTL messages (typically from scctrl)
- */
-int sc_ioctl(int card, scs_ioctl *data)
-{
- int status;
- RspMessage *rcvmsg;
- char *spid;
- char *dn;
- char switchtype;
- char speed;
-
- rcvmsg = kmalloc(sizeof(RspMessage), GFP_KERNEL);
- if (!rcvmsg)
- return -ENOMEM;
-
- switch (data->command) {
- case SCIOCRESET: /* Perform a hard reset of the adapter */
- {
- pr_debug("%s: SCIOCRESET: ioctl received\n",
- sc_adapter[card]->devicename);
- sc_adapter[card]->StartOnReset = 0;
- kfree(rcvmsg);
- return reset(card);
- }
-
- case SCIOCLOAD:
- {
- char *srec;
-
- srec = kmalloc(SCIOC_SRECSIZE, GFP_KERNEL);
- if (!srec) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
- pr_debug("%s: SCIOLOAD: ioctl received\n",
- sc_adapter[card]->devicename);
- if (sc_adapter[card]->EngineUp) {
- pr_debug("%s: SCIOCLOAD: command failed, LoadProc while engine running.\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- kfree(srec);
- return -1;
- }
-
- /*
- * Get the SRec from user space
- */
- if (copy_from_user(srec, data->dataptr, SCIOC_SRECSIZE)) {
- kfree(rcvmsg);
- kfree(srec);
- return -EFAULT;
- }
-
- status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
- 0, SCIOC_SRECSIZE, srec, rcvmsg, SAR_TIMEOUT);
- kfree(rcvmsg);
- kfree(srec);
-
- if (status) {
- pr_debug("%s: SCIOCLOAD: command failed, status = %d\n",
- sc_adapter[card]->devicename, status);
- return -1;
- }
- else {
- pr_debug("%s: SCIOCLOAD: command successful\n",
- sc_adapter[card]->devicename);
- return 0;
- }
- }
-
- case SCIOCSTART:
- {
- kfree(rcvmsg);
- pr_debug("%s: SCIOSTART: ioctl received\n",
- sc_adapter[card]->devicename);
- if (sc_adapter[card]->EngineUp) {
- pr_debug("%s: SCIOCSTART: command failed, engine already running.\n",
- sc_adapter[card]->devicename);
- return -1;
- }
-
- sc_adapter[card]->StartOnReset = 1;
- startproc(card);
- return 0;
- }
-
- case SCIOCSETSWITCH:
- {
- pr_debug("%s: SCIOSETSWITCH: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the switch type from user space
- */
- if (copy_from_user(&switchtype, data->dataptr, sizeof(char))) {
- kfree(rcvmsg);
- return -EFAULT;
- }
-
- pr_debug("%s: SCIOCSETSWITCH: setting switch type to %d\n",
- sc_adapter[card]->devicename,
- switchtype);
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType,
- 0, sizeof(char), &switchtype, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCSETSWITCH: command successful\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- return 0;
- }
- else {
- pr_debug("%s: SCIOCSETSWITCH: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
- }
-
- case SCIOCGETSWITCH:
- {
- pr_debug("%s: SCIOGETSWITCH: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the switch type from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetSwitchType, 0, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCGETSWITCH: command successful\n",
- sc_adapter[card]->devicename);
- }
- else {
- pr_debug("%s: SCIOCGETSWITCH: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
-
- switchtype = rcvmsg->msg_data.byte_array[0];
-
- /*
- * Package the switch type and send to user space
- */
- if (copy_to_user(data->dataptr, &switchtype,
- sizeof(char))) {
- kfree(rcvmsg);
- return -EFAULT;
- }
-
- kfree(rcvmsg);
- return 0;
- }
-
- case SCIOCGETSPID:
- {
- pr_debug("%s: SCIOGETSPID: ioctl received\n",
- sc_adapter[card]->devicename);
-
- spid = kzalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
- if (!spid) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
- /*
- * Get the spid from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID,
- data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status) {
- pr_debug("%s: SCIOCGETSPID: command successful\n",
- sc_adapter[card]->devicename);
- } else {
- pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(spid);
- kfree(rcvmsg);
- return status;
- }
- strlcpy(spid, rcvmsg->msg_data.byte_array, SCIOC_SPIDSIZE);
-
- /*
- * Package the switch type and send to user space
- */
- if (copy_to_user(data->dataptr, spid, SCIOC_SPIDSIZE)) {
- kfree(spid);
- kfree(rcvmsg);
- return -EFAULT;
- }
-
- kfree(spid);
- kfree(rcvmsg);
- return 0;
- }
-
- case SCIOCSETSPID:
- {
- pr_debug("%s: DCBIOSETSPID: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the spid from user space
- */
- spid = memdup_user(data->dataptr, SCIOC_SPIDSIZE);
- if (IS_ERR(spid)) {
- kfree(rcvmsg);
- return PTR_ERR(spid);
- }
-
- pr_debug("%s: SCIOCSETSPID: setting channel %d spid to %s\n",
- sc_adapter[card]->devicename, data->channel, spid);
- status = send_and_receive(card, CEPID, ceReqTypeCall,
- ceReqClass0, ceReqCallSetSPID, data->channel,
- strlen(spid), spid, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCSETSPID: command successful\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- kfree(spid);
- return 0;
- }
- else {
- pr_debug("%s: SCIOCSETSPID: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- kfree(spid);
- return status;
- }
- }
-
- case SCIOCGETDN:
- {
- pr_debug("%s: SCIOGETDN: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the dn from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber,
- data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status) {
- pr_debug("%s: SCIOCGETDN: command successful\n",
- sc_adapter[card]->devicename);
- }
- else {
- pr_debug("%s: SCIOCGETDN: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
-
- dn = kzalloc(SCIOC_DNSIZE, GFP_KERNEL);
- if (!dn) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
- strlcpy(dn, rcvmsg->msg_data.byte_array, SCIOC_DNSIZE);
- kfree(rcvmsg);
-
- /*
- * Package the dn and send to user space
- */
- if (copy_to_user(data->dataptr, dn, SCIOC_DNSIZE)) {
- kfree(dn);
- return -EFAULT;
- }
- kfree(dn);
- return 0;
- }
-
- case SCIOCSETDN:
- {
- pr_debug("%s: SCIOSETDN: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the spid from user space
- */
- dn = memdup_user(data->dataptr, SCIOC_DNSIZE);
- if (IS_ERR(dn)) {
- kfree(rcvmsg);
- return PTR_ERR(dn);
- }
-
- pr_debug("%s: SCIOCSETDN: setting channel %d dn to %s\n",
- sc_adapter[card]->devicename, data->channel, dn);
- status = send_and_receive(card, CEPID, ceReqTypeCall,
- ceReqClass0, ceReqCallSetMyNumber, data->channel,
- strlen(dn), dn, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCSETDN: command successful\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- kfree(dn);
- return 0;
- }
- else {
- pr_debug("%s: SCIOCSETDN: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- kfree(dn);
- return status;
- }
- }
-
- case SCIOCTRACE:
-
- pr_debug("%s: SCIOTRACE: ioctl received\n",
- sc_adapter[card]->devicename);
-/* sc_adapter[card]->trace = !sc_adapter[card]->trace;
- pr_debug("%s: SCIOCTRACE: tracing turned %s\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->trace ? "ON" : "OFF"); */
- break;
-
- case SCIOCSTAT:
- {
- boardInfo *bi;
-
- pr_debug("%s: SCIOSTAT: ioctl received\n",
- sc_adapter[card]->devicename);
-
- bi = kzalloc(sizeof(boardInfo), GFP_KERNEL);
- if (!bi) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
-
- kfree(rcvmsg);
- GetStatus(card, bi);
-
- if (copy_to_user(data->dataptr, bi, sizeof(boardInfo))) {
- kfree(bi);
- return -EFAULT;
- }
-
- kfree(bi);
- return 0;
- }
-
- case SCIOCGETSPEED:
- {
- pr_debug("%s: SCIOGETSPEED: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the speed from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetCallType, data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCGETSPEED: command successful\n",
- sc_adapter[card]->devicename);
- }
- else {
- pr_debug("%s: SCIOCGETSPEED: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
-
- speed = rcvmsg->msg_data.byte_array[0];
-
- kfree(rcvmsg);
-
- /*
- * Package the switch type and send to user space
- */
-
- if (copy_to_user(data->dataptr, &speed, sizeof(char)))
- return -EFAULT;
-
- return 0;
- }
-
- case SCIOCSETSPEED:
- pr_debug("%s: SCIOCSETSPEED: ioctl received\n",
- sc_adapter[card]->devicename);
- break;
-
- case SCIOCLOOPTST:
- pr_debug("%s: SCIOCLOOPTST: ioctl received\n",
- sc_adapter[card]->devicename);
- break;
-
- default:
- kfree(rcvmsg);
- return -1;
- }
-
- kfree(rcvmsg);
- return 0;
-}
-
-static int GetStatus(int card, boardInfo *bi)
-{
- RspMessage rcvmsg;
- int i, status;
-
- /*
- * Fill in some of the basic info about the board
- */
- bi->modelid = sc_adapter[card]->model;
- strcpy(bi->serial_no, sc_adapter[card]->hwconfig.serial_no);
- strcpy(bi->part_no, sc_adapter[card]->hwconfig.part_no);
- bi->iobase = sc_adapter[card]->iobase;
- bi->rambase = sc_adapter[card]->rambase;
- bi->irq = sc_adapter[card]->interrupt;
- bi->ramsize = sc_adapter[card]->hwconfig.ram_size;
- bi->interface = sc_adapter[card]->hwconfig.st_u_sense;
- strcpy(bi->load_ver, sc_adapter[card]->load_ver);
- strcpy(bi->proc_ver, sc_adapter[card]->proc_ver);
-
- /*
- * Get the current PhyStats and LnkStats
- */
- status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2,
- ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- if (sc_adapter[card]->model < PRI_BOARD) {
- bi->l1_status = rcvmsg.msg_data.byte_array[2];
- for (i = 0; i < BRI_CHANNELS; i++)
- bi->status.bristats[i].phy_stat =
- rcvmsg.msg_data.byte_array[i];
- }
- else {
- bi->l1_status = rcvmsg.msg_data.byte_array[0];
- bi->l2_status = rcvmsg.msg_data.byte_array[1];
- for (i = 0; i < PRI_CHANNELS; i++)
- bi->status.pristats[i].phy_stat =
- rcvmsg.msg_data.byte_array[i + 2];
- }
- }
-
- /*
- * Get the call types for each channel
- */
- for (i = 0; i < sc_adapter[card]->nChannels; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- if (sc_adapter[card]->model == PRI_BOARD) {
- bi->status.pristats[i].call_type =
- rcvmsg.msg_data.byte_array[0];
- }
- else {
- bi->status.bristats[i].call_type =
- rcvmsg.msg_data.byte_array[0];
- }
- }
- }
-
- /*
- * If PRI, get the call states and service states for each channel
- */
- if (sc_adapter[card]->model == PRI_BOARD) {
- /*
- * Get the call states
- */
- status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
- ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- for (i = 0; i < PRI_CHANNELS; i++)
- bi->status.pristats[i].call_state =
- rcvmsg.msg_data.byte_array[i];
- }
-
- /*
- * Get the service states
- */
- status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
- ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- for (i = 0; i < PRI_CHANNELS; i++)
- bi->status.pristats[i].serv_state =
- rcvmsg.msg_data.byte_array[i];
- }
-
- /*
- * Get the link stats for the channels
- */
- for (i = 1; i <= PRI_CHANNELS; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
- ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- bi->status.pristats[i - 1].link_stats.tx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[0];
- bi->status.pristats[i - 1].link_stats.tx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[4];
- bi->status.pristats[i - 1].link_stats.rx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[8];
- bi->status.pristats[i - 1].link_stats.rx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[12];
- }
- }
-
- /*
- * Link stats for the D channel
- */
- status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
- ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
- bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
- bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
- bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
- }
-
- return 0;
- }
-
- /*
- * If BRI or POTS, Get SPID, DN and call types for each channel
- */
-
- /*
- * Get the link stats for the channels
- */
- status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
- ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
- bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
- bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
- bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
- bi->status.bristats[0].link_stats.tx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[16];
- bi->status.bristats[0].link_stats.tx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[20];
- bi->status.bristats[0].link_stats.rx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[24];
- bi->status.bristats[0].link_stats.rx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[28];
- bi->status.bristats[1].link_stats.tx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[32];
- bi->status.bristats[1].link_stats.tx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[36];
- bi->status.bristats[1].link_stats.rx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[40];
- bi->status.bristats[1].link_stats.rx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[44];
- }
-
- /*
- * Get the SPIDs
- */
- for (i = 0; i < BRI_CHANNELS; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetSPID, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status)
- strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array);
- }
-
- /*
- * Get the DNs
- */
- for (i = 0; i < BRI_CHANNELS; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetMyNumber, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status)
- strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array);
- }
-
- return 0;
-}
diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c
deleted file mode 100644
index 9679a1902b32..000000000000
--- a/drivers/isdn/sc/message.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/* $Id: message.c,v 1.5.8.2 2001/09/23 22:24:59 kai Exp $
- *
- * functions for sending and receiving control messages
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-#include <linux/sched.h>
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-/*
- * receive a message from the board
- */
-int receivemessage(int card, RspMessage *rspmsg)
-{
- DualPortMemory *dpm;
- unsigned long flags;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -EINVAL;
- }
-
- pr_debug("%s: Entered receivemessage\n",
- sc_adapter[card]->devicename);
-
- /*
- * See if there are messages waiting
- */
- if (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) {
- /*
- * Map in the DPM to the base page and copy the message
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- dpm = (DualPortMemory *) sc_adapter[card]->rambase;
- memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]),
- MSG_LEN);
- dpm->rsp_tail = (dpm->rsp_tail + 1) % MAX_MESSAGES;
- inb(sc_adapter[card]->ioport[FIFO_READ]);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- /*
- * Tell the board that the message is received
- */
- pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d "
- "cnt:%d (type,class,code):(%d,%d,%d) "
- "link:%d stat:0x%x\n",
- sc_adapter[card]->devicename,
- rspmsg->sequence_no,
- rspmsg->process_id,
- rspmsg->time_stamp,
- rspmsg->cmd_sequence_no,
- rspmsg->msg_byte_cnt,
- rspmsg->type,
- rspmsg->class,
- rspmsg->code,
- rspmsg->phy_link_no,
- rspmsg->rsp_status);
-
- return 0;
- }
- return -ENOMSG;
-}
-
-/*
- * send a message to the board
- */
-int sendmessage(int card,
- unsigned int procid,
- unsigned int type,
- unsigned int class,
- unsigned int code,
- unsigned int link,
- unsigned int data_len,
- unsigned int *data)
-{
- DualPortMemory *dpm;
- ReqMessage sndmsg;
- unsigned long flags;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -EINVAL;
- }
-
- /*
- * Make sure we only send CEPID messages when the engine is up
- * and CMPID messages when it is down
- */
- if (sc_adapter[card]->EngineUp && procid == CMPID) {
- pr_debug("%s: Attempt to send CM message with engine up\n",
- sc_adapter[card]->devicename);
- return -ESRCH;
- }
-
- if (!sc_adapter[card]->EngineUp && procid == CEPID) {
- pr_debug("%s: Attempt to send CE message with engine down\n",
- sc_adapter[card]->devicename);
- return -ESRCH;
- }
-
- memset(&sndmsg, 0, MSG_LEN);
- sndmsg.msg_byte_cnt = 4;
- sndmsg.type = type;
- sndmsg.class = class;
- sndmsg.code = code;
- sndmsg.phy_link_no = link;
-
- if (data_len > 0) {
- if (data_len > MSG_DATA_LEN)
- data_len = MSG_DATA_LEN;
- memcpy(&(sndmsg.msg_data), data, data_len);
- sndmsg.msg_byte_cnt = data_len + 8;
- }
-
- sndmsg.process_id = procid;
- sndmsg.sequence_no = sc_adapter[card]->seq_no++ % 256;
-
- /*
- * wait for an empty slot in the queue
- */
- while (!(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
- udelay(1);
-
- /*
- * Disable interrupts and map in shared memory
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- dpm = (DualPortMemory *) sc_adapter[card]->rambase; /* Fix me */
- memcpy_toio(&(dpm->req_queue[dpm->req_head]), &sndmsg, MSG_LEN);
- dpm->req_head = (dpm->req_head + 1) % MAX_MESSAGES;
- outb(sndmsg.sequence_no, sc_adapter[card]->ioport[FIFO_WRITE]);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-
- pr_debug("%s: Sent Message seq:%d pid:%d time:%d "
- "cnt:%d (type,class,code):(%d,%d,%d) "
- "link:%d\n ",
- sc_adapter[card]->devicename,
- sndmsg.sequence_no,
- sndmsg.process_id,
- sndmsg.time_stamp,
- sndmsg.msg_byte_cnt,
- sndmsg.type,
- sndmsg.class,
- sndmsg.code,
- sndmsg.phy_link_no);
-
- return 0;
-}
-
-int send_and_receive(int card,
- unsigned int procid,
- unsigned char type,
- unsigned char class,
- unsigned char code,
- unsigned char link,
- unsigned char data_len,
- unsigned char *data,
- RspMessage *mesgdata,
- int timeout)
-{
- int retval;
- int tries;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -EINVAL;
- }
-
- sc_adapter[card]->want_async_messages = 1;
- retval = sendmessage(card, procid, type, class, code, link,
- data_len, (unsigned int *) data);
-
- if (retval) {
- pr_debug("%s: SendMessage failed in SAR\n",
- sc_adapter[card]->devicename);
- sc_adapter[card]->want_async_messages = 0;
- return -EIO;
- }
-
- tries = 0;
- /* wait for the response */
- while (tries < timeout) {
- schedule_timeout_interruptible(1);
-
- pr_debug("SAR waiting..\n");
-
- /*
- * See if we got our message back
- */
- if ((sc_adapter[card]->async_msg.type == type) &&
- (sc_adapter[card]->async_msg.class == class) &&
- (sc_adapter[card]->async_msg.code == code) &&
- (sc_adapter[card]->async_msg.phy_link_no == link)) {
-
- /*
- * Got it!
- */
- pr_debug("%s: Got ASYNC message\n",
- sc_adapter[card]->devicename);
- memcpy(mesgdata, &(sc_adapter[card]->async_msg),
- sizeof(RspMessage));
- sc_adapter[card]->want_async_messages = 0;
- return 0;
- }
-
- tries++;
- }
-
- pr_debug("%s: SAR message timeout\n", sc_adapter[card]->devicename);
- sc_adapter[card]->want_async_messages = 0;
- return -ETIME;
-}
diff --git a/drivers/isdn/sc/message.h b/drivers/isdn/sc/message.h
deleted file mode 100644
index 5e6f4a5c15f8..000000000000
--- a/drivers/isdn/sc/message.h
+++ /dev/null
@@ -1,245 +0,0 @@
-/* $Id: message.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * structures, macros and defines useful for sending
- * messages to the adapter
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-/*
- * Board message macros, defines and structures
- */
-
-#ifndef MESSAGE_H
-#define MESSAGE_H
-
-#define MAX_MESSAGES 32 /* Maximum messages that can be
- queued */
-#define MSG_DATA_LEN 48 /* Maximum size of message payload */
-#define MSG_LEN 64 /* Size of a message */
-#define CMPID 0 /* Loader message process ID */
-#define CEPID 64 /* Firmware message process ID */
-
-/*
- * Macro to determine if a message is a loader message
- */
-#define IS_CM_MESSAGE(mesg, tx, cx, dx) \
- ((mesg.type == cmRspType##tx) \
- && (mesg.class == cmRspClass##cx) \
- && (mesg.code == cmRsp##dx))
-
-/*
- * Macro to determine if a message is a firmware message
- */
-#define IS_CE_MESSAGE(mesg, tx, cx, dx) \
- ((mesg.type == ceRspType##tx) \
- && (mesg.class == ceRspClass##cx) \
- && (mesg.code == ceRsp##tx##dx))
-
-/*
- * Loader Request and Response Messages
- */
-
-/* message types */
-#define cmReqType1 1
-#define cmReqType2 2
-#define cmRspType0 0
-#define cmRspType1 1
-#define cmRspType2 2
-#define cmRspType5 5
-
-/* message classes */
-#define cmReqClass0 0
-#define cmRspClass0 0
-
-/* message codes */
-#define cmReqHWConfig 1 /* 1,0,1 */
-#define cmReqMsgLpbk 2 /* 1,0,2 */
-#define cmReqVersion 3 /* 1,0,3 */
-#define cmReqLoadProc 1 /* 2,0,1 */
-#define cmReqStartProc 2 /* 2,0,2 */
-#define cmReqReadMem 6 /* 2,0,6 */
-#define cmRspHWConfig cmReqHWConfig
-#define cmRspMsgLpbk cmReqMsgLpbk
-#define cmRspVersion cmReqVersion
-#define cmRspLoadProc cmReqLoadProc
-#define cmRspStartProc cmReqStartProc
-#define cmRspReadMem cmReqReadMem
-#define cmRspMiscEngineUp 1 /* 5,0,1 */
-#define cmRspInvalid 0 /* 0,0,0 */
-
-
-/*
- * Firmware Request and Response Messages
- */
-
-/* message types */
-#define ceReqTypePhy 1
-#define ceReqTypeLnk 2
-#define ceReqTypeCall 3
-#define ceReqTypeStat 1
-#define ceRspTypeErr 0
-#define ceRspTypePhy ceReqTypePhy
-#define ceRspTypeLnk ceReqTypeLnk
-#define ceRspTypeCall ceReqTypeCall
-#define ceRspTypeStat ceReqTypeStat
-
-/* message classes */
-#define ceReqClass0 0
-#define ceReqClass1 1
-#define ceReqClass2 2
-#define ceReqClass3 3
-#define ceRspClass0 ceReqClass0
-#define ceRspClass1 ceReqClass1
-#define ceRspClass2 ceReqClass2
-#define ceRspClass3 ceReqClass3
-
-/* message codes (B) = BRI only, (P) = PRI only, (V) = POTS only */
-#define ceReqPhyProcInfo 1 /* 1,0,1 */
-#define ceReqPhyConnect 1 /* 1,1,1 */
-#define ceReqPhyDisconnect 2 /* 1,1,2 */
-#define ceReqPhySetParams 3 /* 1,1,3 (P) */
-#define ceReqPhyGetParams 4 /* 1,1,4 (P) */
-#define ceReqPhyStatus 1 /* 1,2,1 */
-#define ceReqPhyAcfaStatus 2 /* 1,2,2 (P) */
-#define ceReqPhyChCallState 3 /* 1,2,3 (P) */
-#define ceReqPhyChServState 4 /* 1,2,4 (P) */
-#define ceReqPhyRLoopBack 1 /* 1,3,1 */
-#define ceRspPhyProcInfo ceReqPhyProcInfo
-#define ceRspPhyConnect ceReqPhyConnect
-#define ceRspPhyDisconnect ceReqPhyDisconnect
-#define ceRspPhySetParams ceReqPhySetParams
-#define ceRspPhyGetParams ceReqPhyGetParams
-#define ceRspPhyStatus ceReqPhyStatus
-#define ceRspPhyAcfaStatus ceReqPhyAcfaStatus
-#define ceRspPhyChCallState ceReqPhyChCallState
-#define ceRspPhyChServState ceReqPhyChServState
-#define ceRspPhyRLoopBack ceReqphyRLoopBack
-#define ceReqLnkSetParam 1 /* 2,0,1 */
-#define ceReqLnkGetParam 2 /* 2,0,2 */
-#define ceReqLnkGetStats 3 /* 2,0,3 */
-#define ceReqLnkWrite 1 /* 2,1,1 */
-#define ceReqLnkRead 2 /* 2,1,2 */
-#define ceReqLnkFlush 3 /* 2,1,3 */
-#define ceReqLnkWrBufTrc 4 /* 2,1,4 */
-#define ceReqLnkRdBufTrc 5 /* 2,1,5 */
-#define ceRspLnkSetParam ceReqLnkSetParam
-#define ceRspLnkGetParam ceReqLnkGetParam
-#define ceRspLnkGetStats ceReqLnkGetStats
-#define ceRspLnkWrite ceReqLnkWrite
-#define ceRspLnkRead ceReqLnkRead
-#define ceRspLnkFlush ceReqLnkFlush
-#define ceRspLnkWrBufTrc ceReqLnkWrBufTrc
-#define ceRspLnkRdBufTrc ceReqLnkRdBufTrc
-#define ceReqCallSetSwitchType 1 /* 3,0,1 */
-#define ceReqCallGetSwitchType 2 /* 3,0,2 */
-#define ceReqCallSetFrameFormat 3 /* 3,0,3 */
-#define ceReqCallGetFrameFormat 4 /* 3,0,4 */
-#define ceReqCallSetCallType 5 /* 3,0,5 */
-#define ceReqCallGetCallType 6 /* 3,0,6 */
-#define ceReqCallSetSPID 7 /* 3,0,7 (!P) */
-#define ceReqCallGetSPID 8 /* 3,0,8 (!P) */
-#define ceReqCallSetMyNumber 9 /* 3,0,9 (!P) */
-#define ceReqCallGetMyNumber 10 /* 3,0,10 (!P) */
-#define ceRspCallSetSwitchType ceReqCallSetSwitchType
-#define ceRspCallGetSwitchType ceReqCallSetSwitchType
-#define ceRspCallSetFrameFormat ceReqCallSetFrameFormat
-#define ceRspCallGetFrameFormat ceReqCallGetFrameFormat
-#define ceRspCallSetCallType ceReqCallSetCallType
-#define ceRspCallGetCallType ceReqCallGetCallType
-#define ceRspCallSetSPID ceReqCallSetSPID
-#define ceRspCallGetSPID ceReqCallGetSPID
-#define ceRspCallSetMyNumber ceReqCallSetMyNumber
-#define ceRspCallGetMyNumber ceReqCallGetMyNumber
-#define ceRspStatAcfaStatus 2
-#define ceRspStat
-#define ceRspErrError 0 /* 0,0,0 */
-
-/*
- * Call Types
- */
-#define CALLTYPE_64K 0
-#define CALLTYPE_56K 1
-#define CALLTYPE_SPEECH 2
-#define CALLTYPE_31KHZ 3
-
-/*
- * Link Level data contains a pointer to and the length of
- * a buffer in shared RAM. Used by LnkRead and LnkWrite message
- * types. Part of RspMsgStruct and ReqMsgStruct.
- */
-typedef struct {
- unsigned long buff_offset;
- unsigned short msg_len;
-} LLData;
-
-
-/*
- * Message payload template for an HWConfig message
- */
-typedef struct {
- char st_u_sense;
- char powr_sense;
- char sply_sense;
- unsigned char asic_id;
- long ram_size;
- char serial_no[13];
- char part_no[13];
- char rev_no[2];
-} HWConfig_pl;
-
-/*
- * A Message
- */
-struct message {
- unsigned char sequence_no;
- unsigned char process_id;
- unsigned char time_stamp;
- unsigned char cmd_sequence_no; /* Rsp messages only */
- unsigned char reserved1[3];
- unsigned char msg_byte_cnt;
- unsigned char type;
- unsigned char class;
- unsigned char code;
- unsigned char phy_link_no;
- unsigned char rsp_status; /* Rsp messages only */
- unsigned char reseved2[3];
- union {
- unsigned char byte_array[MSG_DATA_LEN];
- LLData response;
- HWConfig_pl HWCresponse;
- } msg_data;
-};
-
-typedef struct message ReqMessage; /* Request message */
-typedef struct message RspMessage; /* Response message */
-
-/*
- * The first 5010 bytes of shared memory contain the message queues,
- * indexes and other data. This structure is its template
- */
-typedef struct {
- volatile ReqMessage req_queue[MAX_MESSAGES];
- volatile RspMessage rsp_queue[MAX_MESSAGES];
- volatile unsigned char req_head;
- volatile unsigned char req_tail;
- volatile unsigned char rsp_head;
- volatile unsigned char rsp_tail;
- volatile unsigned long signature;
- volatile unsigned long trace_enable;
- volatile unsigned char reserved[4];
-} DualPortMemory;
-
-#endif
diff --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c
deleted file mode 100644
index 2446957085e0..000000000000
--- a/drivers/isdn/sc/packet.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/* $Id: packet.c,v 1.5.8.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-int sndpkt(int devId, int channel, int ack, struct sk_buff *data)
-{
- LLData ReqLnkWrite;
- int status;
- int card;
- unsigned long len;
-
- card = get_card_from_id(devId);
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- pr_debug("%s: sndpkt: frst = 0x%lx nxt = %d f = %d n = %d\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->channel[channel].first_sendbuf,
- sc_adapter[card]->channel[channel].next_sendbuf,
- sc_adapter[card]->channel[channel].free_sendbufs,
- sc_adapter[card]->channel[channel].num_sendbufs);
-
- if (!sc_adapter[card]->channel[channel].free_sendbufs) {
- pr_debug("%s: out of TX buffers\n",
- sc_adapter[card]->devicename);
- return -EINVAL;
- }
-
- if (data->len > BUFFER_SIZE) {
- pr_debug("%s: data overflows buffer size (data > buffer)\n",
- sc_adapter[card]->devicename);
- return -EINVAL;
- }
-
- ReqLnkWrite.buff_offset = sc_adapter[card]->channel[channel].next_sendbuf *
- BUFFER_SIZE + sc_adapter[card]->channel[channel].first_sendbuf;
- ReqLnkWrite.msg_len = data->len; /* sk_buff size */
- pr_debug("%s: writing %d bytes to buffer offset 0x%lx\n",
- sc_adapter[card]->devicename,
- ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset);
- memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len);
-
- /*
- * sendmessage
- */
- pr_debug("%s: sndpkt size=%d, buf_offset=0x%lx buf_indx=%d\n",
- sc_adapter[card]->devicename,
- ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset,
- sc_adapter[card]->channel[channel].next_sendbuf);
-
- status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
- channel + 1, sizeof(LLData), (unsigned int *)&ReqLnkWrite);
- len = data->len;
- if (status) {
- pr_debug("%s: failed to send packet, status = %d\n",
- sc_adapter[card]->devicename, status);
- return -1;
- }
- else {
- sc_adapter[card]->channel[channel].free_sendbufs--;
- sc_adapter[card]->channel[channel].next_sendbuf =
- ++sc_adapter[card]->channel[channel].next_sendbuf ==
- sc_adapter[card]->channel[channel].num_sendbufs ? 0 :
- sc_adapter[card]->channel[channel].next_sendbuf;
- pr_debug("%s: packet sent successfully\n", sc_adapter[card]->devicename);
- dev_kfree_skb(data);
- indicate_status(card, ISDN_STAT_BSENT, channel, (char *)&len);
- }
- return len;
-}
-
-void rcvpkt(int card, RspMessage *rcvmsg)
-{
- LLData newll;
- struct sk_buff *skb;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- switch (rcvmsg->rsp_status) {
- case 0x01:
- case 0x02:
- case 0x70:
- pr_debug("%s: error status code: 0x%x\n",
- sc_adapter[card]->devicename, rcvmsg->rsp_status);
- return;
- case 0x00:
- if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) {
- printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n",
- sc_adapter[card]->devicename);
- return;
- }
- skb_put(skb, rcvmsg->msg_data.response.msg_len);
- pr_debug("%s: getting data from offset: 0x%lx\n",
- sc_adapter[card]->devicename,
- rcvmsg->msg_data.response.buff_offset);
- memcpy_fromshmem(card,
- skb_put(skb, rcvmsg->msg_data.response.msg_len),
- (char *)rcvmsg->msg_data.response.buff_offset,
- rcvmsg->msg_data.response.msg_len);
- sc_adapter[card]->card->rcvcallb_skb(sc_adapter[card]->driverId,
- rcvmsg->phy_link_no - 1, skb);
-
- case 0x03:
- /*
- * Recycle the buffer
- */
- pr_debug("%s: buffer size : %d\n",
- sc_adapter[card]->devicename, BUFFER_SIZE);
-/* memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */
- newll.buff_offset = rcvmsg->msg_data.response.buff_offset;
- newll.msg_len = BUFFER_SIZE;
- pr_debug("%s: recycled buffer at offset 0x%lx size %d\n",
- sc_adapter[card]->devicename,
- newll.buff_offset, newll.msg_len);
- sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
- rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll);
- }
-
-}
-
-int setup_buffers(int card, int c)
-{
- unsigned int nBuffers, i, cBase;
- unsigned int buffer_size;
- LLData RcvBuffOffset;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*
- * Calculate the buffer offsets (send/recv/send/recv)
- */
- pr_debug("%s: setting up channel buffer space in shared RAM\n",
- sc_adapter[card]->devicename);
- buffer_size = BUFFER_SIZE;
- nBuffers = ((sc_adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2;
- nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers;
- pr_debug("%s: calculating buffer space: %d buffers, %d big\n",
- sc_adapter[card]->devicename,
- nBuffers, buffer_size);
- if (nBuffers < 2) {
- pr_debug("%s: not enough buffer space\n",
- sc_adapter[card]->devicename);
- return -1;
- }
- cBase = (nBuffers * buffer_size) * (c - 1);
- pr_debug("%s: channel buffer offset from shared RAM: 0x%x\n",
- sc_adapter[card]->devicename, cBase);
- sc_adapter[card]->channel[c - 1].first_sendbuf = BUFFER_BASE + cBase;
- sc_adapter[card]->channel[c - 1].num_sendbufs = nBuffers / 2;
- sc_adapter[card]->channel[c - 1].free_sendbufs = nBuffers / 2;
- sc_adapter[card]->channel[c - 1].next_sendbuf = 0;
- pr_debug("%s: send buffer setup complete: first=0x%lx n=%d f=%d, nxt=%d\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->channel[c - 1].first_sendbuf,
- sc_adapter[card]->channel[c - 1].num_sendbufs,
- sc_adapter[card]->channel[c - 1].free_sendbufs,
- sc_adapter[card]->channel[c - 1].next_sendbuf);
-
- /*
- * Prep the receive buffers
- */
- pr_debug("%s: adding %d RecvBuffers:\n",
- sc_adapter[card]->devicename, nBuffers / 2);
- for (i = 0; i < nBuffers / 2; i++) {
- RcvBuffOffset.buff_offset =
- ((sc_adapter[card]->channel[c - 1].first_sendbuf +
- (nBuffers / 2) * buffer_size) + (buffer_size * i));
- RcvBuffOffset.msg_len = buffer_size;
- pr_debug("%s: adding RcvBuffer #%d offset=0x%lx sz=%d bufsz:%d\n",
- sc_adapter[card]->devicename,
- i + 1, RcvBuffOffset.buff_offset,
- RcvBuffOffset.msg_len, buffer_size);
- sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
- c, sizeof(LLData), (unsigned int *)&RcvBuffOffset);
- }
- return 0;
-}
diff --git a/drivers/isdn/sc/scioc.h b/drivers/isdn/sc/scioc.h
deleted file mode 100644
index a50e143779e7..000000000000
--- a/drivers/isdn/sc/scioc.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#ifndef __ISDN_SC_SCIOC_H__
-#define __ISDN_SC_SCIOC_H__
-
-/*
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-/*
- * IOCTL Command Codes
- */
-#define SCIOCLOAD 0x01 /* Load a firmware record */
-#define SCIOCRESET 0x02 /* Perform hard reset */
-#define SCIOCDEBUG 0x03 /* Set debug level */
-#define SCIOCREV 0x04 /* Get driver revision(s) */
-#define SCIOCSTART 0x05 /* Start the firmware */
-#define SCIOCGETSWITCH 0x06 /* Get switch type */
-#define SCIOCSETSWITCH 0x07 /* Set switch type */
-#define SCIOCGETSPID 0x08 /* Get channel SPID */
-#define SCIOCSETSPID 0x09 /* Set channel SPID */
-#define SCIOCGETDN 0x0A /* Get channel DN */
-#define SCIOCSETDN 0x0B /* Set channel DN */
-#define SCIOCTRACE 0x0C /* Toggle trace mode */
-#define SCIOCSTAT 0x0D /* Get line status */
-#define SCIOCGETSPEED 0x0E /* Set channel speed */
-#define SCIOCSETSPEED 0x0F /* Set channel speed */
-#define SCIOCLOOPTST 0x10 /* Perform loopback test */
-
-typedef struct {
- int device;
- int channel;
- unsigned long command;
- void __user *dataptr;
-} scs_ioctl;
-
-/* Size of strings */
-#define SCIOC_SPIDSIZE 49
-#define SCIOC_DNSIZE SCIOC_SPIDSIZE
-#define SCIOC_REVSIZE SCIOC_SPIDSIZE
-#define SCIOC_SRECSIZE 49
-
-typedef struct {
- unsigned long tx_good;
- unsigned long tx_bad;
- unsigned long rx_good;
- unsigned long rx_bad;
-} ChLinkStats;
-
-typedef struct {
- char spid[49];
- char dn[49];
- char call_type;
- char phy_stat;
- ChLinkStats link_stats;
-} BRIStat;
-
-typedef BRIStat POTStat;
-
-typedef struct {
- char call_type;
- char call_state;
- char serv_state;
- char phy_stat;
- ChLinkStats link_stats;
-} PRIStat;
-
-typedef char PRIInfo;
-typedef char BRIInfo;
-typedef char POTInfo;
-
-
-typedef struct {
- char acfa_nos;
- char acfa_ais;
- char acfa_los;
- char acfa_rra;
- char acfa_slpp;
- char acfa_slpn;
- char acfa_fsrf;
-} ACFAStat;
-
-typedef struct {
- unsigned char modelid;
- char serial_no[13];
- char part_no[13];
- char load_ver[11];
- char proc_ver[11];
- int iobase;
- long rambase;
- char irq;
- long ramsize;
- char interface;
- char switch_type;
- char l1_status;
- char l2_status;
- ChLinkStats dch_stats;
- ACFAStat AcfaStats;
- union {
- PRIStat pristats[23];
- BRIStat bristats[2];
- POTStat potsstats[2];
- } status;
- union {
- PRIInfo priinfo;
- BRIInfo briinfo;
- POTInfo potsinfo;
- } info;
-} boardInfo;
-
-#endif /* __ISDN_SC_SCIOC_H__ */
diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c
deleted file mode 100644
index d24506ceb6e8..000000000000
--- a/drivers/isdn/sc/shmem.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/* $Id: shmem.c,v 1.2.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * Card functions implementing ISDN4Linux functionality
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h" /* This must be first */
-#include "hardware.h"
-#include "card.h"
-
-/*
- *
- */
-void memcpy_toshmem(int card, void *dest, const void *src, size_t n)
-{
- unsigned long flags;
- unsigned char ch;
- unsigned long dest_rem = ((unsigned long) dest) % 0x4000;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- if (n > SRAM_PAGESIZE)
- return;
-
- /*
- * determine the page to load from the address
- */
- ch = (unsigned long) dest / SRAM_PAGESIZE;
- pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
- /*
- * Block interrupts and load the page
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
-
- outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- memcpy_toio((void __iomem *)(sc_adapter[card]->rambase + dest_rem), src, n);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
- ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
- pr_debug("%s: copying %zu bytes from %#lx to %#lx\n",
- sc_adapter[card]->devicename, n,
- (unsigned long) src,
- sc_adapter[card]->rambase + ((unsigned long) dest % 0x4000));
-}
-
-/*
- * Reverse of above
- */
-void memcpy_fromshmem(int card, void *dest, const void *src, size_t n)
-{
- unsigned long flags;
- unsigned char ch;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- if (n > SRAM_PAGESIZE) {
- return;
- }
-
- /*
- * determine the page to load from the address
- */
- ch = (unsigned long) src / SRAM_PAGESIZE;
- pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
-
-
- /*
- * Block interrupts and load the page
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
-
- outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- memcpy_fromio(dest, (void *)(sc_adapter[card]->rambase +
- ((unsigned long) src % 0x4000)), n);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
- ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
-/* pr_debug("%s: copying %d bytes from %#x to %#x\n",
- sc_adapter[card]->devicename, n,
- sc_adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */
-}
-
-#if 0
-void memset_shmem(int card, void *dest, int c, size_t n)
-{
- unsigned long flags;
- unsigned char ch;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- if (n > SRAM_PAGESIZE) {
- return;
- }
-
- /*
- * determine the page to load from the address
- */
- ch = (unsigned long) dest / SRAM_PAGESIZE;
- pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
-
- /*
- * Block interrupts and load the page
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
-
- outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- memset_io(sc_adapter[card]->rambase +
- ((unsigned long) dest % 0x4000), c, n);
- pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
- ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-}
-#endif /* 0 */
diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c
deleted file mode 100644
index 6fbac2230d7e..000000000000
--- a/drivers/isdn/sc/timer.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/* $Id: timer.c,v 1.3.6.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-
-/*
- * Write the proper values into the I/O ports following a reset
- */
-static void setup_ports(int card)
-{
-
- outb((sc_adapter[card]->rambase >> 12), sc_adapter[card]->ioport[EXP_BASE]);
-
- /* And the IRQ */
- outb((sc_adapter[card]->interrupt | 0x80),
- sc_adapter[card]->ioport[IRQ_SELECT]);
-}
-
-/*
- * Timed function to check the status of a previous reset
- * Must be very fast as this function runs in the context of
- * an interrupt handler.
- *
- * Setup the ioports for the board that were cleared by the reset.
- * Then, check to see if the signate has been set. Next, set the
- * signature to a known value and issue a startproc if needed.
- */
-void sc_check_reset(unsigned long data)
-{
- unsigned long flags;
- unsigned long sig;
- int card = (unsigned int) data;
-
- pr_debug("%s: check_timer timer called\n",
- sc_adapter[card]->devicename);
-
- /* Setup the io ports */
- setup_ports(card);
-
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- outb(sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport],
- (sc_adapter[card]->shmem_magic >> 14) | 0x80);
- sig = (unsigned long) *((unsigned long *)(sc_adapter[card]->rambase + SIG_OFFSET));
-
- /* check the signature */
- if (sig == SIGNATURE) {
- flushreadfifo(card);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- /* See if we need to do a startproc */
- if (sc_adapter[card]->StartOnReset)
- startproc(card);
- } else {
- pr_debug("%s: No signature yet, waiting another %lu jiffies.\n",
- sc_adapter[card]->devicename, CHECKRESET_TIME);
- mod_timer(&sc_adapter[card]->reset_timer, jiffies + CHECKRESET_TIME);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- }
-}
-
-/*
- * Timed function to check the status of a previous reset
- * Must be very fast as this function runs in the context of
- * an interrupt handler.
- *
- * Send check sc_adapter->phystat to see if the channels are up
- * If they are, tell ISDN4Linux that the board is up. If not,
- * tell IADN4Linux that it is up. Always reset the timer to
- * fire again (endless loop).
- */
-void check_phystat(unsigned long data)
-{
- unsigned long flags;
- int card = (unsigned int) data;
-
- pr_debug("%s: Checking status...\n", sc_adapter[card]->devicename);
- /*
- * check the results of the last PhyStat and change only if
- * has changed drastically
- */
- if (sc_adapter[card]->nphystat && !sc_adapter[card]->phystat) { /* All is well */
- pr_debug("PhyStat transition to RUN\n");
- pr_info("%s: Switch contacted, transmitter enabled\n",
- sc_adapter[card]->devicename);
- indicate_status(card, ISDN_STAT_RUN, 0, NULL);
- }
- else if (!sc_adapter[card]->nphystat && sc_adapter[card]->phystat) { /* All is not well */
- pr_debug("PhyStat transition to STOP\n");
- pr_info("%s: Switch connection lost, transmitter disabled\n",
- sc_adapter[card]->devicename);
-
- indicate_status(card, ISDN_STAT_STOP, 0, NULL);
- }
-
- sc_adapter[card]->phystat = sc_adapter[card]->nphystat;
-
- /* Reinitialize the timer */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- mod_timer(&sc_adapter[card]->stat_timer, jiffies + CHECKSTAT_TIME);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-
- /* Send a new cePhyStatus message */
- sendmessage(card, CEPID, ceReqTypePhy, ceReqClass2,
- ceReqPhyStatus, 0, 0, NULL);
-}
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 940e2ebbdea8..4cbb8b27a891 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -93,7 +93,8 @@ enum ad_link_speed_type {
AD_LINK_SPEED_10000MBPS,
AD_LINK_SPEED_20000MBPS,
AD_LINK_SPEED_40000MBPS,
- AD_LINK_SPEED_56000MBPS
+ AD_LINK_SPEED_56000MBPS,
+ AD_LINK_SPEED_100000MBPS,
};
/* compare MAC addresses */
@@ -258,6 +259,7 @@ static inline int __check_agg_selection_timer(struct port *port)
* %AD_LINK_SPEED_20000MBPS
* %AD_LINK_SPEED_40000MBPS
* %AD_LINK_SPEED_56000MBPS
+ * %AD_LINK_SPEED_100000MBPS
*/
static u16 __get_link_speed(struct port *port)
{
@@ -305,6 +307,10 @@ static u16 __get_link_speed(struct port *port)
speed = AD_LINK_SPEED_56000MBPS;
break;
+ case SPEED_100000:
+ speed = AD_LINK_SPEED_100000MBPS;
+ break;
+
default:
/* unknown speed value from ethtool. shouldn't happen */
speed = 0;
@@ -681,6 +687,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator)
case AD_LINK_SPEED_56000MBPS:
bandwidth = aggregator->num_of_ports * 56000;
break;
+ case AD_LINK_SPEED_100000MBPS:
+ bandwidth = aggregator->num_of_ports * 100000;
+ break;
default:
bandwidth = 0; /* to silence the compiler */
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 9e0f8a7ef8b1..56b560558884 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -830,7 +830,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
}
new_active->delay = 0;
- bond_set_slave_link_state(new_active, BOND_LINK_UP);
+ bond_set_slave_link_state(new_active, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
if (BOND_MODE(bond) == BOND_MODE_8023AD)
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
@@ -1066,12 +1067,12 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
return features;
}
-#define BOND_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
+#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
NETIF_F_HIGHDMA | NETIF_F_LRO)
-#define BOND_ENC_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\
- NETIF_F_ALL_TSO)
+#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
static void bond_compute_features(struct bonding *bond)
{
@@ -1198,26 +1199,42 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
return ret;
}
-static int bond_master_upper_dev_link(struct net_device *bond_dev,
- struct net_device *slave_dev,
- struct slave *slave)
+static enum netdev_lag_tx_type bond_lag_tx_type(struct bonding *bond)
{
+ switch (BOND_MODE(bond)) {
+ case BOND_MODE_ROUNDROBIN:
+ return NETDEV_LAG_TX_TYPE_ROUNDROBIN;
+ case BOND_MODE_ACTIVEBACKUP:
+ return NETDEV_LAG_TX_TYPE_ACTIVEBACKUP;
+ case BOND_MODE_BROADCAST:
+ return NETDEV_LAG_TX_TYPE_BROADCAST;
+ case BOND_MODE_XOR:
+ case BOND_MODE_8023AD:
+ return NETDEV_LAG_TX_TYPE_HASH;
+ default:
+ return NETDEV_LAG_TX_TYPE_UNKNOWN;
+ }
+}
+
+static int bond_master_upper_dev_link(struct bonding *bond, struct slave *slave)
+{
+ struct netdev_lag_upper_info lag_upper_info;
int err;
- err = netdev_master_upper_dev_link_private(slave_dev, bond_dev, slave);
+ lag_upper_info.tx_type = bond_lag_tx_type(bond);
+ err = netdev_master_upper_dev_link(slave->dev, bond->dev, slave,
+ &lag_upper_info);
if (err)
return err;
- slave_dev->flags |= IFF_SLAVE;
- rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
+ rtmsg_ifinfo(RTM_NEWLINK, slave->dev, IFF_SLAVE, GFP_KERNEL);
return 0;
}
-static void bond_upper_dev_unlink(struct net_device *bond_dev,
- struct net_device *slave_dev)
+static void bond_upper_dev_unlink(struct bonding *bond, struct slave *slave)
{
- netdev_upper_dev_unlink(slave_dev, bond_dev);
- slave_dev->flags &= ~IFF_SLAVE;
- rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
+ netdev_upper_dev_unlink(slave->dev, bond->dev);
+ slave->dev->flags &= ~IFF_SLAVE;
+ rtmsg_ifinfo(RTM_NEWLINK, slave->dev, IFF_SLAVE, GFP_KERNEL);
}
static struct slave *bond_alloc_slave(struct bonding *bond)
@@ -1299,6 +1316,16 @@ void bond_queue_slave_event(struct slave *slave)
queue_delayed_work(slave->bond->wq, &nnw->work, 0);
}
+void bond_lower_state_changed(struct slave *slave)
+{
+ struct netdev_lag_lower_state_info info;
+
+ info.link_up = slave->link == BOND_LINK_UP ||
+ slave->link == BOND_LINK_FAIL;
+ info.tx_enabled = bond_is_active_slave(slave);
+ netdev_lower_state_changed(slave->dev, &info);
+}
+
/* enslave device <slave> to bond device <master> */
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
{
@@ -1351,7 +1378,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
* the current ifenslave will set the interface down prior to
* enslaving it; the old ifenslave will not.
*/
- if ((slave_dev->flags & IFF_UP)) {
+ if (slave_dev->flags & IFF_UP) {
netdev_err(bond_dev, "%s is up - this may be due to an out of date ifenslave\n",
slave_dev->name);
res = -EPERM;
@@ -1465,6 +1492,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
}
}
+ /* set slave flag before open to prevent IPv6 addrconf */
+ slave_dev->flags |= IFF_SLAVE;
+
/* open the slave since the application closed it */
res = dev_open(slave_dev);
if (res) {
@@ -1563,21 +1593,26 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
if (bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS) {
if (bond->params.updelay) {
bond_set_slave_link_state(new_slave,
- BOND_LINK_BACK);
+ BOND_LINK_BACK,
+ BOND_SLAVE_NOTIFY_NOW);
new_slave->delay = bond->params.updelay;
} else {
bond_set_slave_link_state(new_slave,
- BOND_LINK_UP);
+ BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
}
} else {
- bond_set_slave_link_state(new_slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(new_slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_NOW);
}
} else if (bond->params.arp_interval) {
bond_set_slave_link_state(new_slave,
(netif_carrier_ok(slave_dev) ?
- BOND_LINK_UP : BOND_LINK_DOWN));
+ BOND_LINK_UP : BOND_LINK_DOWN),
+ BOND_SLAVE_NOTIFY_NOW);
} else {
- bond_set_slave_link_state(new_slave, BOND_LINK_UP);
+ bond_set_slave_link_state(new_slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
}
if (new_slave->link != BOND_LINK_DOWN)
@@ -1662,7 +1697,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
goto err_detach;
}
- res = bond_master_upper_dev_link(bond_dev, slave_dev, new_slave);
+ res = bond_master_upper_dev_link(bond, new_slave);
if (res) {
netdev_dbg(bond_dev, "Error %d calling bond_master_upper_dev_link\n", res);
goto err_unregister;
@@ -1698,7 +1733,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* Undo stages on error */
err_upper_unlink:
- bond_upper_dev_unlink(bond_dev, slave_dev);
+ bond_upper_dev_unlink(bond, new_slave);
err_unregister:
netdev_rx_handler_unregister(slave_dev);
@@ -1725,6 +1760,7 @@ err_close:
dev_close(slave_dev);
err_restore_mac:
+ slave_dev->flags &= ~IFF_SLAVE;
if (!bond->params.fail_over_mac ||
BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
/* XXX TODO - fom follow mode needs to change master's
@@ -1799,12 +1835,14 @@ static int __bond_release_one(struct net_device *bond_dev,
return -EINVAL;
}
+ bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW);
+
bond_sysfs_slave_del(slave);
/* recompute stats just before removing the slave */
bond_get_stats(bond->dev, &bond->bond_stats);
- bond_upper_dev_unlink(bond_dev, slave_dev);
+ bond_upper_dev_unlink(bond, slave);
/* unregister rx_handler early so bond_handle_frame wouldn't be called
* for this slave anymore.
*/
@@ -1996,7 +2034,8 @@ static int bond_miimon_inspect(struct bonding *bond)
if (link_state)
continue;
- bond_set_slave_link_state(slave, BOND_LINK_FAIL);
+ bond_set_slave_link_state(slave, BOND_LINK_FAIL,
+ BOND_SLAVE_NOTIFY_LATER);
slave->delay = bond->params.downdelay;
if (slave->delay) {
netdev_info(bond->dev, "link status down for %sinterface %s, disabling it in %d ms\n",
@@ -2011,7 +2050,8 @@ static int bond_miimon_inspect(struct bonding *bond)
case BOND_LINK_FAIL:
if (link_state) {
/* recovered before downdelay expired */
- bond_set_slave_link_state(slave, BOND_LINK_UP);
+ bond_set_slave_link_state(slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_LATER);
slave->last_link_up = jiffies;
netdev_info(bond->dev, "link status up again after %d ms for interface %s\n",
(bond->params.downdelay - slave->delay) *
@@ -2033,7 +2073,8 @@ static int bond_miimon_inspect(struct bonding *bond)
if (!link_state)
continue;
- bond_set_slave_link_state(slave, BOND_LINK_BACK);
+ bond_set_slave_link_state(slave, BOND_LINK_BACK,
+ BOND_SLAVE_NOTIFY_LATER);
slave->delay = bond->params.updelay;
if (slave->delay) {
@@ -2047,7 +2088,8 @@ static int bond_miimon_inspect(struct bonding *bond)
case BOND_LINK_BACK:
if (!link_state) {
bond_set_slave_link_state(slave,
- BOND_LINK_DOWN);
+ BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_LATER);
netdev_info(bond->dev, "link status down again after %d ms for interface %s\n",
(bond->params.updelay - slave->delay) *
bond->params.miimon,
@@ -2085,7 +2127,8 @@ static void bond_miimon_commit(struct bonding *bond)
continue;
case BOND_LINK_UP:
- bond_set_slave_link_state(slave, BOND_LINK_UP);
+ bond_set_slave_link_state(slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
slave->last_link_up = jiffies;
primary = rtnl_dereference(bond->primary_slave);
@@ -2125,7 +2168,8 @@ static void bond_miimon_commit(struct bonding *bond)
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
- bond_set_slave_link_state(slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_NOW);
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
BOND_MODE(bond) == BOND_MODE_8023AD)
@@ -2708,7 +2752,8 @@ static void bond_ab_arp_commit(struct bonding *bond)
struct slave *current_arp_slave;
current_arp_slave = rtnl_dereference(bond->current_arp_slave);
- bond_set_slave_link_state(slave, BOND_LINK_UP);
+ bond_set_slave_link_state(slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
if (current_arp_slave) {
bond_set_slave_inactive_flags(
current_arp_slave,
@@ -2731,7 +2776,8 @@ static void bond_ab_arp_commit(struct bonding *bond)
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
- bond_set_slave_link_state(slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_NOW);
bond_set_slave_inactive_flags(slave,
BOND_SLAVE_NOTIFY_NOW);
@@ -2810,7 +2856,8 @@ static bool bond_ab_arp_probe(struct bonding *bond)
* up when it is actually down
*/
if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
- bond_set_slave_link_state(slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_LATER);
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
@@ -2830,7 +2877,8 @@ static bool bond_ab_arp_probe(struct bonding *bond)
if (!new_slave)
goto check_state;
- bond_set_slave_link_state(new_slave, BOND_LINK_BACK);
+ bond_set_slave_link_state(new_slave, BOND_LINK_BACK,
+ BOND_SLAVE_NOTIFY_LATER);
bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER);
bond_arp_send_all(bond, new_slave);
new_slave->last_link_up = jiffies;
@@ -2838,7 +2886,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
check_state:
bond_for_each_slave_rcu(bond, slave, iter) {
- if (slave->should_notify) {
+ if (slave->should_notify || slave->should_notify_link) {
should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW;
break;
}
@@ -2893,8 +2941,10 @@ re_arm:
if (should_notify_peers)
call_netdevice_notifiers(NETDEV_NOTIFY_PEERS,
bond->dev);
- if (should_notify_rtnl)
+ if (should_notify_rtnl) {
bond_slave_state_notify(bond);
+ bond_slave_link_notify(bond);
+ }
rtnl_unlock();
}
@@ -4135,7 +4185,6 @@ void bond_setup(struct net_device *bond_dev)
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER;
- bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM);
bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
bond_dev->features |= bond_dev->hw_features;
}
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index f4ae72086215..e23c3ed737de 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -42,7 +42,6 @@
#include <net/bonding.h>
-#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_bond(cd) ((struct bonding *)(netdev_priv(to_net_dev(cd))))
/* "show" function for the bond_masters attribute.
@@ -481,7 +480,7 @@ static ssize_t bonding_show_mii_status(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- bool active = !!rcu_access_pointer(bond->curr_active_slave);
+ bool active = netif_carrier_ok(bond->dev);
return sprintf(buf, "%s\n", active ? "up" : "down");
}
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index 51670b322409..c71a03593595 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -32,6 +32,7 @@
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/can/led.h>
+#include <linux/pm_runtime.h>
#define DRIVER_NAME "xilinx_can"
@@ -138,7 +139,7 @@ struct xcan_priv {
u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg);
void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg,
u32 val);
- struct net_device *dev;
+ struct device *dev;
void __iomem *reg_base;
unsigned long irq_flags;
struct clk *bus_clk;
@@ -840,6 +841,13 @@ static int xcan_open(struct net_device *ndev)
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags,
ndev->name, ndev);
if (ret < 0) {
@@ -847,29 +855,17 @@ static int xcan_open(struct net_device *ndev)
goto err;
}
- ret = clk_prepare_enable(priv->can_clk);
- if (ret) {
- netdev_err(ndev, "unable to enable device clock\n");
- goto err_irq;
- }
-
- ret = clk_prepare_enable(priv->bus_clk);
- if (ret) {
- netdev_err(ndev, "unable to enable bus clock\n");
- goto err_can_clk;
- }
-
/* Set chip into reset mode */
ret = set_reset_mode(ndev);
if (ret < 0) {
netdev_err(ndev, "mode resetting failed!\n");
- goto err_bus_clk;
+ goto err_irq;
}
/* Common open */
ret = open_candev(ndev);
if (ret)
- goto err_bus_clk;
+ goto err_irq;
ret = xcan_chip_start(ndev);
if (ret < 0) {
@@ -885,13 +881,11 @@ static int xcan_open(struct net_device *ndev)
err_candev:
close_candev(ndev);
-err_bus_clk:
- clk_disable_unprepare(priv->bus_clk);
-err_can_clk:
- clk_disable_unprepare(priv->can_clk);
err_irq:
free_irq(ndev->irq, ndev);
err:
+ pm_runtime_put(priv->dev);
+
return ret;
}
@@ -908,12 +902,11 @@ static int xcan_close(struct net_device *ndev)
netif_stop_queue(ndev);
napi_disable(&priv->napi);
xcan_chip_stop(ndev);
- clk_disable_unprepare(priv->bus_clk);
- clk_disable_unprepare(priv->can_clk);
free_irq(ndev->irq, ndev);
close_candev(ndev);
can_led_event(ndev, CAN_LED_EVENT_STOP);
+ pm_runtime_put(priv->dev);
return 0;
}
@@ -932,27 +925,20 @@ static int xcan_get_berr_counter(const struct net_device *ndev,
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
- ret = clk_prepare_enable(priv->can_clk);
- if (ret)
- goto err;
-
- ret = clk_prepare_enable(priv->bus_clk);
- if (ret)
- goto err_clk;
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ return ret;
+ }
bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
- clk_disable_unprepare(priv->bus_clk);
- clk_disable_unprepare(priv->can_clk);
+ pm_runtime_put(priv->dev);
return 0;
-
-err_clk:
- clk_disable_unprepare(priv->can_clk);
-err:
- return ret;
}
@@ -965,15 +951,45 @@ static const struct net_device_ops xcan_netdev_ops = {
/**
* xcan_suspend - Suspend method for the driver
- * @dev: Address of the platform_device structure
+ * @dev: Address of the device structure
*
* Put the driver into low power mode.
- * Return: 0 always
+ * Return: 0 on success and failure value on error
*/
static int __maybe_unused xcan_suspend(struct device *dev)
{
- struct platform_device *pdev = dev_get_drvdata(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ if (!device_may_wakeup(dev))
+ return pm_runtime_force_suspend(dev);
+
+ return 0;
+}
+
+/**
+ * xcan_resume - Resume from suspend
+ * @dev: Address of the device structure
+ *
+ * Resume operation after suspend.
+ * Return: 0 on success and failure value on error
+ */
+static int __maybe_unused xcan_resume(struct device *dev)
+{
+ if (!device_may_wakeup(dev))
+ return pm_runtime_force_resume(dev);
+
+ return 0;
+
+}
+
+/**
+ * xcan_runtime_suspend - Runtime suspend method for the driver
+ * @dev: Address of the device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int __maybe_unused xcan_runtime_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
struct xcan_priv *priv = netdev_priv(ndev);
if (netif_running(ndev)) {
@@ -984,43 +1000,55 @@ static int __maybe_unused xcan_suspend(struct device *dev)
priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
priv->can.state = CAN_STATE_SLEEPING;
- clk_disable(priv->bus_clk);
- clk_disable(priv->can_clk);
+ clk_disable_unprepare(priv->bus_clk);
+ clk_disable_unprepare(priv->can_clk);
return 0;
}
/**
- * xcan_resume - Resume from suspend
- * @dev: Address of the platformdevice structure
+ * xcan_runtime_resume - Runtime resume from suspend
+ * @dev: Address of the device structure
*
* Resume operation after suspend.
* Return: 0 on success and failure value on error
*/
-static int __maybe_unused xcan_resume(struct device *dev)
+static int __maybe_unused xcan_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = dev_get_drvdata(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
+ u32 isr, status;
- ret = clk_enable(priv->bus_clk);
+ ret = clk_prepare_enable(priv->bus_clk);
if (ret) {
dev_err(dev, "Cannot enable clock.\n");
return ret;
}
- ret = clk_enable(priv->can_clk);
+ ret = clk_prepare_enable(priv->can_clk);
if (ret) {
dev_err(dev, "Cannot enable clock.\n");
clk_disable_unprepare(priv->bus_clk);
return ret;
}
- priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
- priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
- priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+ isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+ status = priv->read_reg(priv, XCAN_SR_OFFSET);
if (netif_running(ndev)) {
+ if (isr & XCAN_IXR_BSOFF_MASK) {
+ priv->can.state = CAN_STATE_BUS_OFF;
+ priv->write_reg(priv, XCAN_SRR_OFFSET,
+ XCAN_SRR_RESET_MASK);
+ } else if ((status & XCAN_SR_ESTAT_MASK) ==
+ XCAN_SR_ESTAT_MASK) {
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ } else if (status & XCAN_SR_ERRWRN_MASK) {
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ } else {
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ }
netif_device_attach(ndev);
netif_start_queue(ndev);
}
@@ -1028,7 +1056,10 @@ static int __maybe_unused xcan_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
+static const struct dev_pm_ops xcan_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xcan_suspend, xcan_resume)
+ SET_RUNTIME_PM_OPS(xcan_runtime_suspend, xcan_runtime_resume, NULL)
+};
/**
* xcan_probe - Platform registration call
@@ -1069,7 +1100,7 @@ static int xcan_probe(struct platform_device *pdev)
return -ENOMEM;
priv = netdev_priv(ndev);
- priv->dev = ndev;
+ priv->dev = &pdev->dev;
priv->can.bittiming_const = &xcan_bittiming_const;
priv->can.do_set_mode = xcan_do_set_mode;
priv->can.do_get_berr_counter = xcan_get_berr_counter;
@@ -1111,21 +1142,17 @@ static int xcan_probe(struct platform_device *pdev)
}
}
- ret = clk_prepare_enable(priv->can_clk);
- if (ret) {
- dev_err(&pdev->dev, "unable to enable device clock\n");
- goto err_free;
- }
-
- ret = clk_prepare_enable(priv->bus_clk);
- if (ret) {
- dev_err(&pdev->dev, "unable to enable bus clock\n");
- goto err_unprepare_disable_dev;
- }
-
priv->write_reg = xcan_write_reg_le;
priv->read_reg = xcan_read_reg_le;
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ goto err_pmdisable;
+ }
+
if (priv->read_reg(priv, XCAN_SR_OFFSET) != XCAN_SR_CONFIG_MASK) {
priv->write_reg = xcan_write_reg_be;
priv->read_reg = xcan_read_reg_be;
@@ -1138,22 +1165,23 @@ static int xcan_probe(struct platform_device *pdev)
ret = register_candev(ndev);
if (ret) {
dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
- goto err_unprepare_disable_busclk;
+ goto err_disableclks;
}
devm_can_led_init(ndev);
- clk_disable_unprepare(priv->bus_clk);
- clk_disable_unprepare(priv->can_clk);
+
+ pm_runtime_put(&pdev->dev);
+
netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
priv->reg_base, ndev->irq, priv->can.clock.freq,
priv->tx_max);
return 0;
-err_unprepare_disable_busclk:
- clk_disable_unprepare(priv->bus_clk);
-err_unprepare_disable_dev:
- clk_disable_unprepare(priv->can_clk);
+err_disableclks:
+ pm_runtime_put(priv->dev);
+err_pmdisable:
+ pm_runtime_disable(&pdev->dev);
err_free:
free_candev(ndev);
err:
@@ -1172,10 +1200,8 @@ static int xcan_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct xcan_priv *priv = netdev_priv(ndev);
- if (set_reset_mode(ndev) < 0)
- netdev_err(ndev, "mode resetting failed!\n");
-
unregister_candev(ndev);
+ pm_runtime_disable(&pdev->dev);
netif_napi_del(&priv->napi);
free_candev(ndev);
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index b06dba05594a..9fe33fc3c2b9 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -19,6 +19,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <linux/gpio/consumer.h>
#include <linux/phy.h>
#include <net/dsa.h>
#include <net/switchdev.h>
@@ -616,98 +617,112 @@ static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
}
static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
- { "in_good_octets", 8, 0x00, },
- { "in_bad_octets", 4, 0x02, },
- { "in_unicast", 4, 0x04, },
- { "in_broadcasts", 4, 0x06, },
- { "in_multicasts", 4, 0x07, },
- { "in_pause", 4, 0x16, },
- { "in_undersize", 4, 0x18, },
- { "in_fragments", 4, 0x19, },
- { "in_oversize", 4, 0x1a, },
- { "in_jabber", 4, 0x1b, },
- { "in_rx_error", 4, 0x1c, },
- { "in_fcs_error", 4, 0x1d, },
- { "out_octets", 8, 0x0e, },
- { "out_unicast", 4, 0x10, },
- { "out_broadcasts", 4, 0x13, },
- { "out_multicasts", 4, 0x12, },
- { "out_pause", 4, 0x15, },
- { "excessive", 4, 0x11, },
- { "collisions", 4, 0x1e, },
- { "deferred", 4, 0x05, },
- { "single", 4, 0x14, },
- { "multiple", 4, 0x17, },
- { "out_fcs_error", 4, 0x03, },
- { "late", 4, 0x1f, },
- { "hist_64bytes", 4, 0x08, },
- { "hist_65_127bytes", 4, 0x09, },
- { "hist_128_255bytes", 4, 0x0a, },
- { "hist_256_511bytes", 4, 0x0b, },
- { "hist_512_1023bytes", 4, 0x0c, },
- { "hist_1024_max_bytes", 4, 0x0d, },
- /* Not all devices have the following counters */
- { "sw_in_discards", 4, 0x110, },
- { "sw_in_filtered", 2, 0x112, },
- { "sw_out_filtered", 2, 0x113, },
-
+ { "in_good_octets", 8, 0x00, BANK0, },
+ { "in_bad_octets", 4, 0x02, BANK0, },
+ { "in_unicast", 4, 0x04, BANK0, },
+ { "in_broadcasts", 4, 0x06, BANK0, },
+ { "in_multicasts", 4, 0x07, BANK0, },
+ { "in_pause", 4, 0x16, BANK0, },
+ { "in_undersize", 4, 0x18, BANK0, },
+ { "in_fragments", 4, 0x19, BANK0, },
+ { "in_oversize", 4, 0x1a, BANK0, },
+ { "in_jabber", 4, 0x1b, BANK0, },
+ { "in_rx_error", 4, 0x1c, BANK0, },
+ { "in_fcs_error", 4, 0x1d, BANK0, },
+ { "out_octets", 8, 0x0e, BANK0, },
+ { "out_unicast", 4, 0x10, BANK0, },
+ { "out_broadcasts", 4, 0x13, BANK0, },
+ { "out_multicasts", 4, 0x12, BANK0, },
+ { "out_pause", 4, 0x15, BANK0, },
+ { "excessive", 4, 0x11, BANK0, },
+ { "collisions", 4, 0x1e, BANK0, },
+ { "deferred", 4, 0x05, BANK0, },
+ { "single", 4, 0x14, BANK0, },
+ { "multiple", 4, 0x17, BANK0, },
+ { "out_fcs_error", 4, 0x03, BANK0, },
+ { "late", 4, 0x1f, BANK0, },
+ { "hist_64bytes", 4, 0x08, BANK0, },
+ { "hist_65_127bytes", 4, 0x09, BANK0, },
+ { "hist_128_255bytes", 4, 0x0a, BANK0, },
+ { "hist_256_511bytes", 4, 0x0b, BANK0, },
+ { "hist_512_1023bytes", 4, 0x0c, BANK0, },
+ { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
+ { "sw_in_discards", 4, 0x10, PORT, },
+ { "sw_in_filtered", 2, 0x12, PORT, },
+ { "sw_out_filtered", 2, 0x13, PORT, },
+ { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
};
-static bool have_sw_in_discards(struct dsa_switch *ds)
+static bool mv88e6xxx_has_stat(struct dsa_switch *ds,
+ struct mv88e6xxx_hw_stat *stat)
{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-
- switch (ps->id) {
- case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161:
- case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171:
- case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176:
- case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185:
- case PORT_SWITCH_ID_6352:
+ switch (stat->type) {
+ case BANK0:
return true;
- default:
- return false;
- }
-}
-
-static void _mv88e6xxx_get_strings(struct dsa_switch *ds,
- int nr_stats,
- struct mv88e6xxx_hw_stat *stats,
- int port, uint8_t *data)
-{
- int i;
-
- for (i = 0; i < nr_stats; i++) {
- memcpy(data + i * ETH_GSTRING_LEN,
- stats[i].string, ETH_GSTRING_LEN);
+ case BANK1:
+ return mv88e6xxx_6320_family(ds);
+ case PORT:
+ return mv88e6xxx_6095_family(ds) ||
+ mv88e6xxx_6185_family(ds) ||
+ mv88e6xxx_6097_family(ds) ||
+ mv88e6xxx_6165_family(ds) ||
+ mv88e6xxx_6351_family(ds) ||
+ mv88e6xxx_6352_family(ds);
}
+ return false;
}
static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
- int stat,
- struct mv88e6xxx_hw_stat *stats,
+ struct mv88e6xxx_hw_stat *s,
int port)
{
- struct mv88e6xxx_hw_stat *s = stats + stat;
u32 low;
u32 high = 0;
int ret;
u64 value;
- if (s->reg >= 0x100) {
- ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
- s->reg - 0x100);
+ switch (s->type) {
+ case PORT:
+ ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), s->reg);
if (ret < 0)
return UINT64_MAX;
low = ret;
if (s->sizeof_stat == 4) {
ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
- s->reg - 0x100 + 1);
+ s->reg + 1);
if (ret < 0)
return UINT64_MAX;
high = ret;
}
- } else {
+ break;
+ case BANK0:
+ case BANK1:
_mv88e6xxx_stats_read(ds, s->reg, &low);
if (s->sizeof_stat == 8)
_mv88e6xxx_stats_read(ds, s->reg + 1, &high);
@@ -716,61 +731,59 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
return value;
}
-static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
- int nr_stats,
- struct mv88e6xxx_hw_stat *stats,
- int port, uint64_t *data)
+void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int ret;
- int i;
-
- mutex_lock(&ps->smi_mutex);
+ struct mv88e6xxx_hw_stat *stat;
+ int i, j;
- ret = _mv88e6xxx_stats_snapshot(ds, port);
- if (ret < 0) {
- mutex_unlock(&ps->smi_mutex);
- return;
+ for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
+ stat = &mv88e6xxx_hw_stats[i];
+ if (mv88e6xxx_has_stat(ds, stat)) {
+ memcpy(data + j * ETH_GSTRING_LEN, stat->string,
+ ETH_GSTRING_LEN);
+ j++;
+ }
}
-
- /* Read each of the counters. */
- for (i = 0; i < nr_stats; i++)
- data[i] = _mv88e6xxx_get_ethtool_stat(ds, i, stats, port);
-
- mutex_unlock(&ps->smi_mutex);
-}
-
-/* All the statistics in the table */
-void
-mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
-{
- if (have_sw_in_discards(ds))
- _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
- mv88e6xxx_hw_stats, port, data);
- else
- _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
- mv88e6xxx_hw_stats, port, data);
}
int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
{
- if (have_sw_in_discards(ds))
- return ARRAY_SIZE(mv88e6xxx_hw_stats);
- return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
+ struct mv88e6xxx_hw_stat *stat;
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
+ stat = &mv88e6xxx_hw_stats[i];
+ if (mv88e6xxx_has_stat(ds, stat))
+ j++;
+ }
+ return j;
}
void
mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
int port, uint64_t *data)
{
- if (have_sw_in_discards(ds))
- _mv88e6xxx_get_ethtool_stats(
- ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
- mv88e6xxx_hw_stats, port, data);
- else
- _mv88e6xxx_get_ethtool_stats(
- ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
- mv88e6xxx_hw_stats, port, data);
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ struct mv88e6xxx_hw_stat *stat;
+ int ret;
+ int i, j;
+
+ mutex_lock(&ps->smi_mutex);
+
+ ret = _mv88e6xxx_stats_snapshot(ds, port);
+ if (ret < 0) {
+ mutex_unlock(&ps->smi_mutex);
+ return;
+ }
+ for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
+ stat = &mv88e6xxx_hw_stats[i];
+ if (mv88e6xxx_has_stat(ds, stat)) {
+ data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port);
+ j++;
+ }
+ }
+
+ mutex_unlock(&ps->smi_mutex);
}
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
@@ -2323,6 +2336,7 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
+ struct gpio_desc *gpiod = ds->pd->reset;
unsigned long timeout;
int ret;
int i;
@@ -2336,6 +2350,14 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
/* Wait for transmit queues to drain. */
usleep_range(2000, 4000);
+ /* If there is a gpio connected to the reset pin, toggle it */
+ if (gpiod) {
+ gpiod_set_value_cansleep(gpiod, 1);
+ usleep_range(10000, 20000);
+ gpiod_set_value_cansleep(gpiod, 0);
+ usleep_range(10000, 20000);
+ }
+
/* Reset the switch. Keep the PPU active if requested. The PPU
* needs to be active to support indirect phy register access
* through global registers 0x18 and 0x19.
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 21c8daa03f78..ca08f913d302 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -288,6 +288,7 @@
#define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_BANK_1 BIT(9)
#define GLOBAL_STATS_COUNTER_32 0x1e
#define GLOBAL_STATS_COUNTER_01 0x1f
@@ -420,10 +421,17 @@ struct mv88e6xxx_priv_state {
struct work_struct bridge_work;
};
+enum stat_type {
+ BANK0,
+ BANK1,
+ PORT,
+};
+
struct mv88e6xxx_hw_stat {
char string[ETH_GSTRING_LEN];
int sizeof_stat;
int reg;
+ enum stat_type type;
};
int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active);
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
index 4547a1b8b958..7677c745fb30 100644
--- a/drivers/net/ethernet/3com/3c509.c
+++ b/drivers/net/ethernet/3com/3c509.c
@@ -562,7 +562,7 @@ static void el3_common_remove (struct net_device *dev)
}
#ifdef CONFIG_EISA
-static int __init el3_eisa_probe (struct device *device)
+static int el3_eisa_probe(struct device *device)
{
short i;
int ioaddr, irq, if_port;
diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c
index 2839af00f20c..1c5f3b273e6a 100644
--- a/drivers/net/ethernet/3com/3c59x.c
+++ b/drivers/net/ethernet/3com/3c59x.c
@@ -907,7 +907,7 @@ static struct eisa_device_id vortex_eisa_ids[] = {
};
MODULE_DEVICE_TABLE(eisa, vortex_eisa_ids);
-static int __init vortex_eisa_probe(struct device *device)
+static int vortex_eisa_probe(struct device *device)
{
void __iomem *ioaddr;
struct eisa_device *edev;
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
index 0443654f0339..c89b9aeeceb6 100644
--- a/drivers/net/ethernet/8390/ax88796.c
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -372,7 +372,7 @@ static int ax_mii_probe(struct net_device *dev)
ax->phy_dev = phy_dev;
netdev_info(dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phy_dev->drv->name, dev_name(&phy_dev->dev), phy_dev->irq);
+ phy_dev->drv->name, phydev_name(phy_dev), phy_dev->irq);
return 0;
}
@@ -627,7 +627,7 @@ static int ax_mii_init(struct net_device *dev)
struct platform_device *pdev = to_platform_device(dev->dev.parent);
struct ei_device *ei_local = netdev_priv(dev);
struct ax_device *ax = to_ax_dev(dev);
- int err, i;
+ int err;
ax->bb_ctrl.ops = &bb_ops;
ax->addr_memr = ei_local->mem + AX_MEMR;
@@ -642,23 +642,12 @@ static int ax_mii_init(struct net_device *dev)
snprintf(ax->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
- ax->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!ax->mii_bus->irq) {
- err = -ENOMEM;
- goto out_free_mdio_bitbang;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- ax->mii_bus->irq[i] = PHY_POLL;
-
err = mdiobus_register(ax->mii_bus);
if (err)
- goto out_free_irq;
+ goto out_free_mdio_bitbang;
return 0;
- out_free_irq:
- kfree(ax->mii_bus->irq);
out_free_mdio_bitbang:
free_mdio_bitbang(ax->mii_bus);
out:
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 31c5e476fd64..0b13af8e4070 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -122,6 +122,7 @@ config FEALNX
cards. <http://www.myson.com.tw/>
source "drivers/net/ethernet/natsemi/Kconfig"
+source "drivers/net/ethernet/netronome/Kconfig"
source "drivers/net/ethernet/8390/Kconfig"
config NET_NETX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 071f84eb6f3f..38dc1a776a2b 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
+obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/
obj-$(CONFIG_NET_NETX) += netx-eth.o
obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c
index 096531a73124..74139cb7f849 100644
--- a/drivers/net/ethernet/adi/bfin_mac.c
+++ b/drivers/net/ethernet/adi/bfin_mac.c
@@ -380,9 +380,8 @@ static void bfin_mac_adjust_link(struct net_device *dev)
static int mii_probe(struct net_device *dev, int phy_mode)
{
struct bfin_mac_local *lp = netdev_priv(dev);
- struct phy_device *phydev = NULL;
+ struct phy_device *phydev;
unsigned short sysctl;
- int i;
u32 sclk, mdc_div;
/* Enable PHY output early */
@@ -396,18 +395,7 @@ static int mii_probe(struct net_device *dev, int phy_mode)
sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div);
bfin_write_EMAC_SYSCTL(sysctl);
- /* search for connected PHY device */
- for (i = 0; i < PHY_MAX_ADDR; ++i) {
- struct phy_device *const tmp_phydev = lp->mii_bus->phy_map[i];
-
- if (!tmp_phydev)
- continue; /* no PHY here... */
-
- phydev = tmp_phydev;
- break; /* found it */
- }
-
- /* now we are supposed to have a proper phydev, to attach to... */
+ phydev = phy_find_first(lp->mii_bus);
if (!phydev) {
netdev_err(dev, "no phy device found\n");
return -ENODEV;
@@ -419,7 +407,7 @@ static int mii_probe(struct net_device *dev, int phy_mode)
return -EINVAL;
}
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&bfin_mac_adjust_link, phy_mode);
if (IS_ERR(phydev)) {
@@ -444,10 +432,8 @@ static int mii_probe(struct net_device *dev, int phy_mode)
lp->old_duplex = -1;
lp->phydev = phydev;
- pr_info("attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, irq=%d, mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq,
- MDC_CLK, mdc_div, sclk/1000000);
+ phy_attached_print(phydev, "mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n",
+ MDC_CLK, mdc_div, sclk / 1000000);
return 0;
}
@@ -1840,12 +1826,6 @@ static int bfin_mii_bus_probe(struct platform_device *pdev)
snprintf(miibus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
- miibus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (!miibus->irq)
- goto out_err_irq_alloc;
-
- for (i = rc; i < PHY_MAX_ADDR; ++i)
- miibus->irq[i] = PHY_POLL;
rc = clamp(mii_bus_pd->phydev_number, 0, PHY_MAX_ADDR);
if (rc != mii_bus_pd->phydev_number)
@@ -1864,14 +1844,12 @@ static int bfin_mii_bus_probe(struct platform_device *pdev)
rc = mdiobus_register(miibus);
if (rc) {
dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
- goto out_err_mdiobus_register;
+ goto out_err_irq_alloc;
}
platform_set_drvdata(pdev, miibus);
return 0;
-out_err_mdiobus_register:
- kfree(miibus->irq);
out_err_irq_alloc:
mdiobus_free(miibus);
out_err_alloc:
@@ -1887,7 +1865,6 @@ static int bfin_mii_bus_remove(struct platform_device *pdev)
dev_get_platdata(&pdev->dev);
mdiobus_unregister(miibus);
- kfree(miibus->irq);
mdiobus_free(miibus);
peripheral_free_list(mii_bus_pd->mac_peripherals);
@@ -1912,21 +1889,21 @@ static struct platform_driver bfin_mac_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &bfin_mii_bus_driver,
+ &bfin_mac_driver,
+};
+
static int __init bfin_mac_init(void)
{
- int ret;
- ret = platform_driver_register(&bfin_mii_bus_driver);
- if (!ret)
- return platform_driver_register(&bfin_mac_driver);
- return -ENODEV;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(bfin_mac_init);
static void __exit bfin_mac_cleanup(void)
{
- platform_driver_unregister(&bfin_mac_driver);
- platform_driver_unregister(&bfin_mii_bus_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(bfin_mac_cleanup);
diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c
index 20bf55dbd76f..b873531c5575 100644
--- a/drivers/net/ethernet/aeroflex/greth.c
+++ b/drivers/net/ethernet/aeroflex/greth.c
@@ -1337,11 +1337,6 @@ static int greth_mdio_init(struct greth_private *greth)
greth->mdio->write = greth_mdio_write;
greth->mdio->priv = greth;
- greth->mdio->irq = greth->mdio_irqs;
-
- for (phy = 0; phy < PHY_MAX_ADDR; phy++)
- greth->mdio->irq[phy] = PHY_POLL;
-
ret = mdiobus_register(greth->mdio);
if (ret) {
goto error;
diff --git a/drivers/net/ethernet/aeroflex/greth.h b/drivers/net/ethernet/aeroflex/greth.h
index ae16ac94daf8..92dd918e4a83 100644
--- a/drivers/net/ethernet/aeroflex/greth.h
+++ b/drivers/net/ethernet/aeroflex/greth.h
@@ -125,7 +125,6 @@ struct greth_private {
struct phy_device *phy;
struct mii_bus *mdio;
- int mdio_irqs[PHY_MAX_ADDR];
unsigned int link;
unsigned int speed;
unsigned int duplex;
diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c
index e0f3d197e7f2..3f3bcbea15bd 100644
--- a/drivers/net/ethernet/agere/et131x.c
+++ b/drivers/net/ethernet/agere/et131x.c
@@ -1235,7 +1235,7 @@ static int et131x_mii_read(struct et131x_adapter *adapter, u8 reg, u16 *value)
if (!phydev)
return -EIO;
- return et131x_phy_mii_read(adapter, phydev->addr, reg, value);
+ return et131x_phy_mii_read(adapter, phydev->mdio.addr, reg, value);
}
static int et131x_mii_write(struct et131x_adapter *adapter, u8 addr, u8 reg,
@@ -1462,7 +1462,7 @@ static void et1310_phy_power_switch(struct et131x_adapter *adapter, bool down)
data &= ~BMCR_PDOWN;
if (down)
data |= BMCR_PDOWN;
- et131x_mii_write(adapter, phydev->addr, MII_BMCR, data);
+ et131x_mii_write(adapter, phydev->mdio.addr, MII_BMCR, data);
}
/* et131x_xcvr_init - Init the phy if we are setting it into force mode */
@@ -1490,7 +1490,7 @@ static void et131x_xcvr_init(struct et131x_adapter *adapter)
else
lcr2 |= (LED_VAL_LINKON << LED_TXRX_SHIFT);
- et131x_mii_write(adapter, phydev->addr, PHY_LED_2, lcr2);
+ et131x_mii_write(adapter, phydev->mdio.addr, PHY_LED_2, lcr2);
}
}
@@ -3192,14 +3192,14 @@ static void et131x_adjust_link(struct net_device *netdev)
et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG,
&register18);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG,
register18 | 0x4);
- et131x_mii_write(adapter, phydev->addr, PHY_INDEX_REG,
- register18 | 0x8402);
- et131x_mii_write(adapter, phydev->addr, PHY_DATA_REG,
- register18 | 511);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
+ PHY_INDEX_REG, register18 | 0x8402);
+ et131x_mii_write(adapter, phydev->mdio.addr,
+ PHY_DATA_REG, register18 | 511);
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG, register18);
}
@@ -3212,8 +3212,8 @@ static void et131x_adjust_link(struct net_device *netdev)
et131x_mii_read(adapter, PHY_CONFIG, &reg);
reg &= ~ET_PHY_CONFIG_TX_FIFO_DEPTH;
reg |= ET_PHY_CONFIG_FIFO_DEPTH_32;
- et131x_mii_write(adapter, phydev->addr, PHY_CONFIG,
- reg);
+ et131x_mii_write(adapter, phydev->mdio.addr,
+ PHY_CONFIG, reg);
}
et131x_set_rx_dma_timer(adapter);
@@ -3226,14 +3226,14 @@ static void et131x_adjust_link(struct net_device *netdev)
et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG,
&register18);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG,
register18 | 0x4);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_INDEX_REG, register18 | 0x8402);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_DATA_REG, register18 | 511);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG, register18);
}
@@ -3265,7 +3265,7 @@ static int et131x_mii_probe(struct net_device *netdev)
return -ENODEV;
}
- phydev = phy_connect(netdev, dev_name(&phydev->dev),
+ phydev = phy_connect(netdev, phydev_name(phydev),
&et131x_adjust_link, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -3289,9 +3289,7 @@ static int et131x_mii_probe(struct net_device *netdev)
phydev->autoneg = AUTONEG_ENABLE;
adapter->phydev = phydev;
- dev_info(&adapter->pdev->dev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
}
@@ -3327,7 +3325,6 @@ static void et131x_pci_remove(struct pci_dev *pdev)
netif_napi_del(&adapter->napi);
phy_disconnect(adapter->phydev);
mdiobus_unregister(adapter->mii_bus);
- kfree(adapter->mii_bus->irq);
mdiobus_free(adapter->mii_bus);
et131x_adapter_memory_free(adapter);
@@ -3946,7 +3943,6 @@ static int et131x_pci_setup(struct pci_dev *pdev,
struct net_device *netdev;
struct et131x_adapter *adapter;
int rc;
- int ii;
rc = pci_enable_device(pdev);
if (rc < 0) {
@@ -4036,18 +4032,11 @@ static int et131x_pci_setup(struct pci_dev *pdev,
adapter->mii_bus->priv = netdev;
adapter->mii_bus->read = et131x_mdio_read;
adapter->mii_bus->write = et131x_mdio_write;
- adapter->mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int),
- GFP_KERNEL);
- if (!adapter->mii_bus->irq)
- goto err_mdio_free;
-
- for (ii = 0; ii < PHY_MAX_ADDR; ii++)
- adapter->mii_bus->irq[ii] = PHY_POLL;
rc = mdiobus_register(adapter->mii_bus);
if (rc < 0) {
dev_err(&pdev->dev, "failed to register MII bus\n");
- goto err_mdio_free_irq;
+ goto err_mdio_free;
}
rc = et131x_mii_probe(netdev);
@@ -4087,8 +4076,6 @@ err_phy_disconnect:
phy_disconnect(adapter->phydev);
err_mdio_unregister:
mdiobus_unregister(adapter->mii_bus);
-err_mdio_free_irq:
- kfree(adapter->mii_bus->irq);
err_mdio_free:
mdiobus_free(adapter->mii_bus);
err_mem_free:
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index fe644823ceaf..17472851674f 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -131,7 +131,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
{
struct altera_tse_private *priv = netdev_priv(dev);
int ret;
- int i;
struct device_node *mdio_node = NULL;
struct mii_bus *mdio = NULL;
struct device_node *child_node = NULL;
@@ -161,14 +160,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
mdio->write = &altera_tse_mdio_write;
snprintf(mdio->id, MII_BUS_ID_SIZE, "%s-%u", mdio->name, id);
- mdio->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (mdio->irq == NULL) {
- ret = -ENOMEM;
- goto out_free_mdio;
- }
- for (i = 0; i < PHY_MAX_ADDR; i++)
- mdio->irq[i] = PHY_POLL;
-
mdio->priv = dev;
mdio->parent = priv->device;
@@ -176,7 +167,7 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
if (ret != 0) {
netdev_err(dev, "Cannot register MDIO bus %s\n",
mdio->id);
- goto out_free_mdio_irq;
+ goto out_free_mdio;
}
if (netif_msg_drv(priv))
@@ -184,8 +175,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
priv->mdio = mdio;
return 0;
-out_free_mdio_irq:
- kfree(mdio->irq);
out_free_mdio:
mdiobus_free(mdio);
mdio = NULL;
@@ -855,7 +844,7 @@ static int init_phy(struct net_device *dev)
}
netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n",
- phydev->addr, phydev->phy_id, phydev->link);
+ phydev->mdio.addr, phydev->phy_id, phydev->link);
priv->phydev = phydev;
return 0;
diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c
index 5330bcb8a944..d3977d032b48 100644
--- a/drivers/net/ethernet/amd/au1000_eth.c
+++ b/drivers/net/ethernet/amd/au1000_eth.c
@@ -344,9 +344,6 @@ static void au1000_mdio_write(struct net_device *dev, int phy_addr,
static int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
{
- /* WARNING: bus->phy_map[phy_addr].attached_dev == dev does
- * _NOT_ hold (e.g. when PHY is accessed through other MAC's MII bus)
- */
struct net_device *const dev = bus->priv;
/* make sure the MAC associated with this
@@ -502,7 +499,7 @@ static int au1000_mii_probe(struct net_device *dev)
BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
if (aup->phy_addr)
- phydev = aup->mii_bus->phy_map[aup->phy_addr];
+ phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr);
else
netdev_info(dev, "using PHY-less setup\n");
return 0;
@@ -512,8 +509,8 @@ static int au1000_mii_probe(struct net_device *dev)
* on the current MAC's MII bus
*/
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
- if (aup->mii_bus->phy_map[phy_addr]) {
- phydev = aup->mii_bus->phy_map[phy_addr];
+ if (mdiobus_get_phy(aup->mii_bus, aup->phy_addr)) {
+ phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr);
if (!aup->phy_search_highest_addr)
/* break out with first one found */
break;
@@ -531,7 +528,8 @@ static int au1000_mii_probe(struct net_device *dev)
*/
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
struct phy_device *const tmp_phydev =
- aup->mii_bus->phy_map[phy_addr];
+ mdiobus_get_phy(aup->mii_bus,
+ phy_addr);
if (aup->mac_id == 1)
break;
@@ -558,7 +556,7 @@ static int au1000_mii_probe(struct net_device *dev)
/* now we are supposed to have a proper phydev, to attach to... */
BUG_ON(phydev->attached_dev);
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&au1000_adjust_link, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -583,9 +581,7 @@ static int au1000_mii_probe(struct net_device *dev)
aup->old_duplex = -1;
aup->phy_dev = phydev;
- netdev_info(dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
}
@@ -1293,14 +1289,7 @@ static int au1000_probe(struct platform_device *pdev)
aup->mii_bus->name = "au1000_eth_mii";
snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, aup->mac_id);
- aup->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (aup->mii_bus->irq == NULL) {
- err = -ENOMEM;
- goto err_out;
- }
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- aup->mii_bus->irq[i] = PHY_POLL;
/* if known, set corresponding PHY IRQs */
if (aup->phy_static_config)
if (aup->phy_irq && aup->phy_busid == aup->mac_id)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 53ce1222b11d..8a9b493566c9 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -2024,7 +2024,6 @@ read_again:
skb->dev = netdev;
skb->protocol = eth_type_trans(skb, netdev);
skb_record_rx_queue(skb, channel->queue_index);
- skb_mark_napi_id(skb, napi);
napi_gro_receive(napi, skb);
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
index c31e691d11fc..db55c9f6e8e1 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -869,7 +869,7 @@ void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata)
pdata->mdio_bus = NULL;
}
-struct xgene_mac_ops xgene_gmac_ops = {
+const struct xgene_mac_ops xgene_gmac_ops = {
.init = xgene_gmac_init,
.reset = xgene_gmac_reset,
.rx_enable = xgene_gmac_rx_enable,
@@ -879,7 +879,7 @@ struct xgene_mac_ops xgene_gmac_ops = {
.set_mac_addr = xgene_gmac_set_mac_addr,
};
-struct xgene_port_ops xgene_gport_ops = {
+const struct xgene_port_ops xgene_gport_ops = {
.reset = xgene_enet_reset,
.cle_bypass = xgene_enet_cle_bypass,
.shutdown = xgene_gport_shutdown,
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
index c153a1dc5ff7..8a9091039ab4 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
@@ -340,8 +340,8 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata);
void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata);
bool xgene_ring_mgr_init(struct xgene_enet_pdata *p);
-extern struct xgene_mac_ops xgene_gmac_ops;
-extern struct xgene_port_ops xgene_gport_ops;
+extern const struct xgene_mac_ops xgene_gmac_ops;
+extern const struct xgene_port_ops xgene_gport_ops;
extern struct xgene_ring_ops xgene_ring1_ops;
#endif /* __XGENE_ENET_HW_H__ */
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
index d0ae1a6cc212..a4799c1fc7d4 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
@@ -690,7 +690,7 @@ static void xgene_enet_napi_disable(struct xgene_enet_pdata *pdata)
static int xgene_enet_open(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
- struct xgene_mac_ops *mac_ops = pdata->mac_ops;
+ const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
int ret;
mac_ops->tx_enable(pdata);
@@ -714,7 +714,7 @@ static int xgene_enet_open(struct net_device *ndev)
static int xgene_enet_close(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
- struct xgene_mac_ops *mac_ops = pdata->mac_ops;
+ const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
netif_stop_queue(ndev);
@@ -1090,7 +1090,7 @@ static const struct net_device_ops xgene_ndev_ops = {
};
#ifdef CONFIG_ACPI
-static int xgene_get_port_id_acpi(struct device *dev,
+static void xgene_get_port_id_acpi(struct device *dev,
struct xgene_enet_pdata *pdata)
{
acpi_status status;
@@ -1103,24 +1103,19 @@ static int xgene_get_port_id_acpi(struct device *dev,
pdata->port_id = temp;
}
- return 0;
+ return;
}
#endif
-static int xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pdata)
+static void xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pdata)
{
u32 id = 0;
- int ret;
- ret = of_property_read_u32(dev->of_node, "port-id", &id);
- if (ret) {
- pdata->port_id = 0;
- ret = 0;
- } else {
- pdata->port_id = id & BIT(0);
- }
+ of_property_read_u32(dev->of_node, "port-id", &id);
- return ret;
+ pdata->port_id = id & BIT(0);
+
+ return;
}
static int xgene_get_tx_delay(struct xgene_enet_pdata *pdata)
@@ -1215,13 +1210,11 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
}
if (dev->of_node)
- ret = xgene_get_port_id_dt(dev, pdata);
+ xgene_get_port_id_dt(dev, pdata);
#ifdef CONFIG_ACPI
else
- ret = xgene_get_port_id_acpi(dev, pdata);
+ xgene_get_port_id_acpi(dev, pdata);
#endif
- if (ret)
- return ret;
if (!device_get_mac_address(dev, ndev->dev_addr, ETH_ALEN))
eth_hw_addr_random(ndev);
@@ -1429,7 +1422,7 @@ static int xgene_enet_probe(struct platform_device *pdev)
struct net_device *ndev;
struct xgene_enet_pdata *pdata;
struct device *dev = &pdev->dev;
- struct xgene_mac_ops *mac_ops;
+ const struct xgene_mac_ops *mac_ops;
const struct of_device_id *of_id;
int ret;
@@ -1516,7 +1509,7 @@ err:
static int xgene_enet_remove(struct platform_device *pdev)
{
struct xgene_enet_pdata *pdata;
- struct xgene_mac_ops *mac_ops;
+ const struct xgene_mac_ops *mac_ops;
struct net_device *ndev;
pdata = platform_get_drvdata(pdev);
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
index 1aa72c787f8d..70d5b62c125a 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
@@ -174,8 +174,8 @@ struct xgene_enet_pdata {
int phy_mode;
enum xgene_enet_rm rm;
struct rtnl_link_stats64 stats;
- struct xgene_mac_ops *mac_ops;
- struct xgene_port_ops *port_ops;
+ const struct xgene_mac_ops *mac_ops;
+ const struct xgene_port_ops *port_ops;
struct xgene_ring_ops *ring_ops;
struct delayed_work link_work;
u32 port_id;
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
index 05b817e56fde..78475512b683 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
@@ -405,7 +405,7 @@ static void xgene_enet_link_state(struct work_struct *work)
schedule_delayed_work(&p->link_work, poll_interval);
}
-struct xgene_mac_ops xgene_sgmac_ops = {
+const struct xgene_mac_ops xgene_sgmac_ops = {
.init = xgene_sgmac_init,
.reset = xgene_sgmac_reset,
.rx_enable = xgene_sgmac_rx_enable,
@@ -416,7 +416,7 @@ struct xgene_mac_ops xgene_sgmac_ops = {
.link_state = xgene_enet_link_state
};
-struct xgene_port_ops xgene_sgport_ops = {
+const struct xgene_port_ops xgene_sgport_ops = {
.reset = xgene_enet_reset,
.cle_bypass = xgene_enet_cle_bypass,
.shutdown = xgene_enet_shutdown
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
index de432465009c..29a71b4dcc44 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
@@ -35,7 +35,7 @@
#define MPA_IDLE_WITH_QMI_EMPTY BIT(12)
#define SG_RX_DV_GATE_REG_0_ADDR 0x0dfc
-extern struct xgene_mac_ops xgene_sgmac_ops;
-extern struct xgene_port_ops xgene_sgport_ops;
+extern const struct xgene_mac_ops xgene_sgmac_ops;
+extern const struct xgene_port_ops xgene_sgport_ops;
#endif /* __XGENE_ENET_SGMAC_H__ */
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
index 7a28a48cb2c7..ba030dc1940b 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
@@ -326,7 +326,7 @@ static void xgene_enet_link_state(struct work_struct *work)
schedule_delayed_work(&pdata->link_work, poll_interval);
}
-struct xgene_mac_ops xgene_xgmac_ops = {
+const struct xgene_mac_ops xgene_xgmac_ops = {
.init = xgene_xgmac_init,
.reset = xgene_xgmac_reset,
.rx_enable = xgene_xgmac_rx_enable,
@@ -338,7 +338,7 @@ struct xgene_mac_ops xgene_xgmac_ops = {
.link_state = xgene_enet_link_state
};
-struct xgene_port_ops xgene_xgport_ops = {
+const struct xgene_port_ops xgene_xgport_ops = {
.reset = xgene_enet_reset,
.cle_bypass = xgene_enet_xgcle_bypass,
.shutdown = xgene_enet_shutdown,
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h
index f8f908dbf51c..0a2dca8a1725 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h
@@ -69,7 +69,7 @@
#define XG_ENET_SPARE_CFG_REG_1_ADDR 0x0410
#define XGENET_RX_DV_GATE_REG_0_ADDR 0x0804
-extern struct xgene_mac_ops xgene_xgmac_ops;
-extern struct xgene_port_ops xgene_xgport_ops;
+extern const struct xgene_mac_ops xgene_xgmac_ops;
+extern const struct xgene_port_ops xgene_xgport_ops;
#endif /* __XGENE_ENET_XGMAC_H__ */
diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig
index 52a6b16f57d2..689045186064 100644
--- a/drivers/net/ethernet/arc/Kconfig
+++ b/drivers/net/ethernet/arc/Kconfig
@@ -34,9 +34,9 @@ config EMAC_ROCKCHIP
select ARC_EMAC_CORE
depends on OF_IRQ && OF_NET && REGULATOR && HAS_DMA
---help---
- Support for Rockchip RK3066/RK3188 EMAC ethernet controllers.
+ Support for Rockchip RK3036/RK3066/RK3188 EMAC ethernet controllers.
This selects Rockchip SoC glue layer support for the
- emac device driver. This driver is used for RK3066/RK3188
+ emac device driver. This driver is used for RK3036/RK3066/RK3188
EMAC ethernet controller.
endif # NET_VENDOR_ARC
diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index c31c7407b753..85e821ccfcd2 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -25,17 +25,13 @@
#include "emac.h"
#define DRV_NAME "rockchip_emac"
-#define DRV_VERSION "1.0"
-
-#define GRF_MODE_MII (1UL << 0)
-#define GRF_MODE_RMII (0UL << 0)
-#define GRF_SPEED_10M (0UL << 1)
-#define GRF_SPEED_100M (1UL << 1)
-#define GRF_SPEED_ENABLE_BIT (1UL << 17)
-#define GRF_MODE_ENABLE_BIT (1UL << 16)
+#define DRV_VERSION "1.1"
struct emac_rockchip_soc_data {
- int grf_offset;
+ unsigned int grf_offset;
+ unsigned int grf_mode_offset;
+ unsigned int grf_speed_offset;
+ bool need_div_macclk;
};
struct rockchip_priv_data {
@@ -44,23 +40,22 @@ struct rockchip_priv_data {
const struct emac_rockchip_soc_data *soc_data;
struct regulator *regulator;
struct clk *refclk;
+ struct clk *macclk;
};
static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
{
struct rockchip_priv_data *emac = priv;
+ u32 speed_offset = emac->soc_data->grf_speed_offset;
u32 data;
int err = 0;
- /* write-enable bits */
- data = GRF_SPEED_ENABLE_BIT;
-
switch(speed) {
case 10:
- data |= GRF_SPEED_10M;
+ data = (1 << (speed_offset + 16)) | (0 << speed_offset);
break;
case 100:
- data |= GRF_SPEED_100M;
+ data = (1 << (speed_offset + 16)) | (1 << speed_offset);
break;
default:
pr_err("speed %u not supported\n", speed);
@@ -72,14 +67,25 @@ static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
pr_err("unable to apply speed %u to grf (%d)\n", speed, err);
}
-static const struct emac_rockchip_soc_data emac_rockchip_dt_data[] = {
- { .grf_offset = 0x154 }, /* rk3066 */
- { .grf_offset = 0x0a4 }, /* rk3188 */
+static const struct emac_rockchip_soc_data emac_rk3036_emac_data = {
+ .grf_offset = 0x140, .grf_mode_offset = 8,
+ .grf_speed_offset = 9, .need_div_macclk = 1,
+};
+
+static const struct emac_rockchip_soc_data emac_rk3066_emac_data = {
+ .grf_offset = 0x154, .grf_mode_offset = 0,
+ .grf_speed_offset = 1, .need_div_macclk = 0,
+};
+
+static const struct emac_rockchip_soc_data emac_rk3188_emac_data = {
+ .grf_offset = 0x0a4, .grf_mode_offset = 0,
+ .grf_speed_offset = 1, .need_div_macclk = 0,
};
static const struct of_device_id emac_rockchip_dt_ids[] = {
- { .compatible = "rockchip,rk3066-emac", .data = &emac_rockchip_dt_data[0] },
- { .compatible = "rockchip,rk3188-emac", .data = &emac_rockchip_dt_data[1] },
+ { .compatible = "rockchip,rk3036-emac", .data = &emac_rk3036_emac_data },
+ { .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data },
+ { .compatible = "rockchip,rk3188-emac", .data = &emac_rk3188_emac_data },
{ /* Sentinel */ }
};
@@ -110,7 +116,7 @@ static int emac_rockchip_probe(struct platform_device *pdev)
interface = of_get_phy_mode(dev->of_node);
- /* RK3066 and RK3188 SoCs only support RMII */
+ /* RK3036/RK3066/RK3188 SoCs only support RMII */
if (interface != PHY_INTERFACE_MODE_RMII) {
dev_err(dev, "unsupported phy interface mode %d\n", interface);
err = -ENOTSUPP;
@@ -164,15 +170,12 @@ static int emac_rockchip_probe(struct platform_device *pdev)
}
}
- err = arc_emac_probe(ndev, interface);
- if (err)
- goto out_regulator_disable;
-
- /* write-enable bits */
- data = GRF_MODE_ENABLE_BIT | GRF_SPEED_ENABLE_BIT;
-
- data |= GRF_SPEED_100M;
- data |= GRF_MODE_RMII;
+ /* Set speed 100M */
+ data = (1 << (priv->soc_data->grf_speed_offset + 16)) |
+ (1 << priv->soc_data->grf_speed_offset);
+ /* Set RMII mode */
+ data |= (1 << (priv->soc_data->grf_mode_offset + 16)) |
+ (0 << priv->soc_data->grf_mode_offset);
err = regmap_write(priv->grf, priv->soc_data->grf_offset, data);
if (err) {
@@ -184,6 +187,33 @@ static int emac_rockchip_probe(struct platform_device *pdev)
err = clk_set_rate(priv->refclk, 50000000);
if (err)
dev_err(dev, "failed to change reference clock rate (%d)\n", err);
+
+ if (priv->soc_data->need_div_macclk) {
+ priv->macclk = devm_clk_get(dev, "macclk");
+ if (IS_ERR(priv->macclk)) {
+ dev_err(dev, "failed to retrieve mac clock (%ld)\n", PTR_ERR(priv->macclk));
+ err = PTR_ERR(priv->macclk);
+ goto out_regulator_disable;
+ }
+
+ err = clk_prepare_enable(priv->macclk);
+ if (err) {
+ dev_err(dev, "failed to enable mac clock (%d)\n", err);
+ goto out_regulator_disable;
+ }
+
+ /* RMII TX/RX needs always a rate of 25MHz */
+ err = clk_set_rate(priv->macclk, 25000000);
+ if (err)
+ dev_err(dev, "failed to change mac clock rate (%d)\n", err);
+ }
+
+ err = arc_emac_probe(ndev, interface);
+ if (err) {
+ dev_err(dev, "failed to probe arc emac (%d)\n", err);
+ goto out_regulator_disable;
+ }
+
return 0;
out_regulator_disable:
diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c
index 7712f068f6d4..1fe35e453d43 100644
--- a/drivers/net/ethernet/atheros/alx/hw.c
+++ b/drivers/net/ethernet/atheros/alx/hw.c
@@ -958,13 +958,13 @@ void alx_configure_basic(struct alx_hw *hw)
alx_write_mem32(hw, ALX_TINT_TPD_THRSHLD, hw->ith_tpd);
alx_write_mem32(hw, ALX_TINT_TIMER, hw->imt);
- raw_mtu = hw->mtu + ETH_HLEN;
- alx_write_mem32(hw, ALX_MTU, raw_mtu + 8);
- if (raw_mtu > ALX_MTU_JUMBO_TH)
+ raw_mtu = ALX_RAW_MTU(hw->mtu);
+ alx_write_mem32(hw, ALX_MTU, raw_mtu);
+ if (raw_mtu > (ALX_MTU_JUMBO_TH + ETH_FCS_LEN + VLAN_HLEN))
hw->rx_ctrl &= ~ALX_MAC_CTRL_FAST_PAUSE;
- if ((raw_mtu + 8) < ALX_TXQ1_JUMBO_TSO_TH)
- val = (raw_mtu + 8 + 7) >> 3;
+ if (raw_mtu < ALX_TXQ1_JUMBO_TSO_TH)
+ val = (raw_mtu + 7) >> 3;
else
val = ALX_TXQ1_JUMBO_TSO_TH >> 3;
alx_write_mem32(hw, ALX_TXQ1, val | ALX_TXQ1_ERRLGPKT_DROP_EN);
diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h
index 15548802d6f8..f289c05f5cb4 100644
--- a/drivers/net/ethernet/atheros/alx/hw.h
+++ b/drivers/net/ethernet/atheros/alx/hw.h
@@ -37,6 +37,7 @@
#include <linux/types.h>
#include <linux/mdio.h>
#include <linux/pci.h>
+#include <linux/if_vlan.h>
#include "reg.h"
/* Transmit Packet Descriptor, contains 4 32-bit words.
@@ -343,12 +344,14 @@ struct alx_rrd {
ALX_RSS_HASH_TYPE_IPV4_TCP | \
ALX_RSS_HASH_TYPE_IPV6 | \
ALX_RSS_HASH_TYPE_IPV6_TCP)
-#define ALX_DEF_RXBUF_SIZE 1536
+#define ALX_FRAME_PAD 16
+#define ALX_RAW_MTU(_mtu) (_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN)
+#define ALX_MAX_FRAME_LEN(_mtu) (ALIGN((ALX_RAW_MTU(_mtu) + ALX_FRAME_PAD), 8))
+#define ALX_DEF_RXBUF_SIZE ALX_MAX_FRAME_LEN(1500)
#define ALX_MAX_JUMBO_PKT_SIZE (9*1024)
#define ALX_MAX_TSO_PKT_SIZE (7*1024)
#define ALX_MAX_FRAME_SIZE ALX_MAX_JUMBO_PKT_SIZE
-#define ALX_MIN_FRAME_SIZE 68
-#define ALX_RAW_MTU(_mtu) (_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN)
+#define ALX_MIN_FRAME_SIZE (ETH_ZLEN + ETH_FCS_LEN + VLAN_HLEN)
#define ALX_MAX_RX_QUEUES 8
#define ALX_MAX_TX_QUEUES 4
diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c
index bd377a6b067d..55b118e876fd 100644
--- a/drivers/net/ethernet/atheros/alx/main.c
+++ b/drivers/net/ethernet/atheros/alx/main.c
@@ -577,7 +577,6 @@ static int alx_alloc_rings(struct alx_priv *alx)
alx->int_mask &= ~ALX_ISR_ALL_QUEUES;
alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0;
- alx->tx_ringsz = alx->tx_ringsz;
netif_napi_add(alx->dev, &alx->napi, alx_poll, 64);
@@ -705,7 +704,7 @@ static int alx_init_sw(struct alx_priv *alx)
hw->smb_timer = 400;
hw->mtu = alx->dev->mtu;
- alx->rxbuf_size = ALIGN(ALX_RAW_MTU(hw->mtu), 8);
+ alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu);
alx->tx_ringsz = 256;
alx->rx_ringsz = 512;
hw->imt = 200;
@@ -806,7 +805,7 @@ static void alx_reinit(struct alx_priv *alx)
static int alx_change_mtu(struct net_device *netdev, int mtu)
{
struct alx_priv *alx = netdev_priv(netdev);
- int max_frame = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+ int max_frame = ALX_MAX_FRAME_LEN(mtu);
if ((max_frame < ALX_MIN_FRAME_SIZE) ||
(max_frame > ALX_MAX_FRAME_SIZE))
@@ -817,8 +816,7 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
netdev->mtu = mtu;
alx->hw.mtu = mtu;
- alx->rxbuf_size = mtu > ALX_DEF_RXBUF_SIZE ?
- ALIGN(max_frame, 8) : ALX_DEF_RXBUF_SIZE;
+ alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE);
netdev_update_features(netdev);
if (netif_running(netdev))
alx_reinit(alx);
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index a3b1c07ae0af..74f0a37c4eb6 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -2263,24 +2263,16 @@ static int b44_register_phy_one(struct b44 *bp)
mii_bus->parent = sdev->dev;
mii_bus->phy_mask = ~(1 << bp->phy_addr);
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%x", instance);
- mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!mii_bus->irq) {
- dev_err(sdev->dev, "mii_bus irq allocation failed\n");
- err = -ENOMEM;
- goto err_out_mdiobus;
- }
-
- memset(mii_bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR);
bp->mii_bus = mii_bus;
err = mdiobus_register(mii_bus);
if (err) {
dev_err(sdev->dev, "failed to register MII bus\n");
- goto err_out_mdiobus_irq;
+ goto err_out_mdiobus;
}
- if (!bp->mii_bus->phy_map[bp->phy_addr] &&
+ if (!mdiobus_is_registered_device(bp->mii_bus, bp->phy_addr) &&
(sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM))) {
dev_info(sdev->dev,
@@ -2313,19 +2305,15 @@ static int b44_register_phy_one(struct b44 *bp)
bp->phydev = phydev;
bp->old_link = 0;
- bp->phy_addr = phydev->addr;
+ bp->phy_addr = phydev->mdio.addr;
- dev_info(sdev->dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
err_out_mdiobus_unregister:
mdiobus_unregister(mii_bus);
-err_out_mdiobus_irq:
- kfree(mii_bus->irq);
-
err_out_mdiobus:
mdiobus_free(mii_bus);
@@ -2339,7 +2327,6 @@ static void b44_unregister_phy_one(struct b44 *bp)
phy_disconnect(bp->phydev);
mdiobus_unregister(mii_bus);
- kfree(mii_bus->irq);
mdiobus_free(mii_bus);
}
diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
index 8b1929e9f698..87c6b5bdd616 100644
--- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
@@ -908,8 +908,7 @@ static int bcm_enet_open(struct net_device *dev)
else
phydev->advertising &= ~SUPPORTED_Pause;
- dev_info(kdev, "attached PHY at address %d [%s]\n",
- phydev->addr, phydev->drv->name);
+ phy_attached_info(phydev);
priv->old_link = 0;
priv->old_duplex = -1;
@@ -1849,17 +1848,8 @@ static int bcm_enet_probe(struct platform_device *pdev)
* if a slave is not present on hw */
bus->phy_mask = ~(1 << priv->phy_id);
- bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
- GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto out_free_mdio;
- }
-
if (priv->has_phy_interrupt)
bus->irq[priv->phy_id] = priv->phy_interrupt;
- else
- bus->irq[priv->phy_id] = PHY_POLL;
ret = mdiobus_register(bus);
if (ret) {
@@ -2884,33 +2874,21 @@ struct platform_driver bcm63xx_enet_shared_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &bcm63xx_enet_shared_driver,
+ &bcm63xx_enet_driver,
+ &bcm63xx_enetsw_driver,
+};
+
/* entry point */
static int __init bcm_enet_init(void)
{
- int ret;
-
- ret = platform_driver_register(&bcm63xx_enet_shared_driver);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&bcm63xx_enet_driver);
- if (ret)
- platform_driver_unregister(&bcm63xx_enet_shared_driver);
-
- ret = platform_driver_register(&bcm63xx_enetsw_driver);
- if (ret) {
- platform_driver_unregister(&bcm63xx_enet_driver);
- platform_driver_unregister(&bcm63xx_enet_shared_driver);
- }
-
- return ret;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
static void __exit bcm_enet_exit(void)
{
- platform_driver_unregister(&bcm63xx_enet_driver);
- platform_driver_unregister(&bcm63xx_enetsw_driver);
- platform_driver_unregister(&bcm63xx_enet_shared_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 858106352ce9..993c780bdfab 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -1216,7 +1216,7 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv,
/* Initialize SW view of the ring */
spin_lock_init(&ring->lock);
ring->priv = priv;
- netif_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
+ netif_tx_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
ring->index = index;
ring->size = size;
ring->alloc_size = ring->size;
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index 28f7610b03fe..c7798d360512 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -1471,7 +1471,7 @@ static int bgmac_mii_register(struct bgmac *bgmac)
struct mii_bus *mii_bus;
struct phy_device *phy_dev;
char bus_id[MII_BUS_ID_SIZE + 3];
- int i, err = 0;
+ int err = 0;
if (ci->id == BCMA_CHIP_ID_BCM4707 ||
ci->id == BCMA_CHIP_ID_BCM53018)
@@ -1490,18 +1490,10 @@ static int bgmac_mii_register(struct bgmac *bgmac)
mii_bus->parent = &bgmac->core->dev;
mii_bus->phy_mask = ~(1 << bgmac->phyaddr);
- mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!mii_bus->irq) {
- err = -ENOMEM;
- goto err_free_bus;
- }
- for (i = 0; i < PHY_MAX_ADDR; i++)
- mii_bus->irq[i] = PHY_POLL;
-
err = mdiobus_register(mii_bus);
if (err) {
bgmac_err(bgmac, "Registration of mii bus failed\n");
- goto err_free_irq;
+ goto err_free_bus;
}
bgmac->mii_bus = mii_bus;
@@ -1522,8 +1514,6 @@ static int bgmac_mii_register(struct bgmac *bgmac)
err_unregister_bus:
mdiobus_unregister(mii_bus);
-err_free_irq:
- kfree(mii_bus->irq);
err_free_bus:
mdiobus_free(mii_bus);
return err;
@@ -1534,7 +1524,6 @@ static void bgmac_mii_unregister(struct bgmac *bgmac)
struct mii_bus *mii_bus = bgmac->mii_bus;
mdiobus_unregister(mii_bus);
- kfree(mii_bus->irq);
mdiobus_free(mii_bus);
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index b5e64b02200c..cae0956186ce 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -540,10 +540,6 @@ struct bnx2x_fastpath {
struct napi_struct napi;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned long busy_poll_state;
-#endif
-
union host_hc_status_block status_blk;
/* chip independent shortcuts into sb structure */
__le16 *sb_index_values;
@@ -594,8 +590,6 @@ struct bnx2x_fastpath {
/* The last maximal completed SGE */
u16 last_max_sge;
__le16 *rx_cons_sb;
- unsigned long rx_pkt,
- rx_calls;
/* TPA related */
struct bnx2x_agg_info *tpa_info;
@@ -617,115 +611,6 @@ struct bnx2x_fastpath {
#define bnx2x_fp_stats(bp, fp) (&((bp)->fp_stats[(fp)->index]))
#define bnx2x_fp_qstats(bp, fp) (&((bp)->fp_stats[(fp)->index].eth_q_stats))
-#ifdef CONFIG_NET_RX_BUSY_POLL
-
-enum bnx2x_fp_state {
- BNX2X_STATE_FP_NAPI = BIT(0), /* NAPI handler owns the queue */
-
- BNX2X_STATE_FP_NAPI_REQ_BIT = 1, /* NAPI would like to own the queue */
- BNX2X_STATE_FP_NAPI_REQ = BIT(1),
-
- BNX2X_STATE_FP_POLL_BIT = 2,
- BNX2X_STATE_FP_POLL = BIT(2), /* busy_poll owns the queue */
-
- BNX2X_STATE_FP_DISABLE_BIT = 3, /* queue is dismantled */
-};
-
-static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp)
-{
- WRITE_ONCE(fp->busy_poll_state, 0);
-}
-
-/* called from the device poll routine to get ownership of a FP */
-static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
-{
- unsigned long prev, old = READ_ONCE(fp->busy_poll_state);
-
- while (1) {
- switch (old) {
- case BNX2X_STATE_FP_POLL:
- /* make sure bnx2x_fp_lock_poll() wont starve us */
- set_bit(BNX2X_STATE_FP_NAPI_REQ_BIT,
- &fp->busy_poll_state);
- /* fallthrough */
- case BNX2X_STATE_FP_POLL | BNX2X_STATE_FP_NAPI_REQ:
- return false;
- default:
- break;
- }
- prev = cmpxchg(&fp->busy_poll_state, old, BNX2X_STATE_FP_NAPI);
- if (unlikely(prev != old)) {
- old = prev;
- continue;
- }
- return true;
- }
-}
-
-static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
-{
- smp_wmb();
- fp->busy_poll_state = 0;
-}
-
-/* called from bnx2x_low_latency_poll() */
-static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
-{
- return cmpxchg(&fp->busy_poll_state, 0, BNX2X_STATE_FP_POLL) == 0;
-}
-
-static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
-{
- smp_mb__before_atomic();
- clear_bit(BNX2X_STATE_FP_POLL_BIT, &fp->busy_poll_state);
-}
-
-/* true if a socket is polling */
-static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
-{
- return READ_ONCE(fp->busy_poll_state) & BNX2X_STATE_FP_POLL;
-}
-
-/* false if fp is currently owned */
-static inline bool bnx2x_fp_ll_disable(struct bnx2x_fastpath *fp)
-{
- set_bit(BNX2X_STATE_FP_DISABLE_BIT, &fp->busy_poll_state);
- return !bnx2x_fp_ll_polling(fp);
-
-}
-#else
-static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp)
-{
-}
-
-static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
-{
- return true;
-}
-
-static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
-{
-}
-
-static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
-{
- return false;
-}
-
-static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
-{
-}
-
-static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
-{
- return false;
-}
-static inline bool bnx2x_fp_ll_disable(struct bnx2x_fastpath *fp)
-{
- return true;
-}
-#endif /* CONFIG_NET_RX_BUSY_POLL */
-
/* Use 2500 as a mini-jumbo MTU for FCoE */
#define BNX2X_FCOE_MINI_JUMBO_MTU 2500
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index c82ab87fcbe8..9695a4c4a434 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -46,7 +46,6 @@ static void bnx2x_add_all_napi_cnic(struct bnx2x *bp)
for_each_rx_queue_cnic(bp, i) {
netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
bnx2x_poll, NAPI_POLL_WEIGHT);
- napi_hash_add(&bnx2x_fp(bp, i, napi));
}
}
@@ -58,7 +57,6 @@ static void bnx2x_add_all_napi(struct bnx2x *bp)
for_each_eth_queue(bp, i) {
netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
bnx2x_poll, NAPI_POLL_WEIGHT);
- napi_hash_add(&bnx2x_fp(bp, i, napi));
}
}
@@ -560,10 +558,8 @@ static int bnx2x_alloc_rx_sge(struct bnx2x *bp, struct bnx2x_fastpath *fp,
put_page(pool->page);
pool->page = alloc_pages(gfp_mask, PAGES_PER_SGE_SHIFT);
- if (unlikely(!pool->page)) {
- BNX2X_ERR("Can't alloc sge\n");
+ if (unlikely(!pool->page))
return -ENOMEM;
- }
pool->offset = 0;
}
@@ -747,7 +743,7 @@ static void bnx2x_gro_receive(struct bnx2x *bp, struct bnx2x_fastpath *fp,
bnx2x_gro_csum(bp, skb, bnx2x_gro_ipv6_csum);
break;
default:
- BNX2X_ERR("Error: FW GRO supports only IPv4/IPv6, not 0x%04x\n",
+ WARN_ONCE(1, "Error: FW GRO supports only IPv4/IPv6, not 0x%04x\n",
be16_to_cpu(skb->protocol));
}
}
@@ -1094,12 +1090,7 @@ reuse_rx:
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
le16_to_cpu(cqe_fp->vlan_tag));
- skb_mark_napi_id(skb, &fp->napi);
-
- if (bnx2x_fp_ll_polling(fp))
- netif_receive_skb(skb);
- else
- napi_gro_receive(&fp->napi, skb);
+ napi_gro_receive(&fp->napi, skb);
next_rx:
rx_buf->data = NULL;
@@ -1131,9 +1122,6 @@ next_cqe:
bnx2x_update_rx_prod(bp, fp, bd_prod_fw, sw_comp_prod,
fp->rx_sge_prod);
- fp->rx_pkt += rx_pkt;
- fp->rx_calls++;
-
return rx_pkt;
}
@@ -1869,7 +1857,6 @@ static void bnx2x_napi_enable_cnic(struct bnx2x *bp)
int i;
for_each_rx_queue_cnic(bp, i) {
- bnx2x_fp_busy_poll_init(&bp->fp[i]);
napi_enable(&bnx2x_fp(bp, i, napi));
}
}
@@ -1879,7 +1866,6 @@ static void bnx2x_napi_enable(struct bnx2x *bp)
int i;
for_each_eth_queue(bp, i) {
- bnx2x_fp_busy_poll_init(&bp->fp[i]);
napi_enable(&bnx2x_fp(bp, i, napi));
}
}
@@ -1890,8 +1876,6 @@ static void bnx2x_napi_disable_cnic(struct bnx2x *bp)
for_each_rx_queue_cnic(bp, i) {
napi_disable(&bnx2x_fp(bp, i, napi));
- while (!bnx2x_fp_ll_disable(&bp->fp[i]))
- usleep_range(1000, 2000);
}
}
@@ -1901,8 +1885,6 @@ static void bnx2x_napi_disable(struct bnx2x *bp)
for_each_eth_queue(bp, i) {
napi_disable(&bnx2x_fp(bp, i, napi));
- while (!bnx2x_fp_ll_disable(&bp->fp[i]))
- usleep_range(1000, 2000);
}
}
@@ -3219,49 +3201,32 @@ int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state)
*/
static int bnx2x_poll(struct napi_struct *napi, int budget)
{
- int work_done = 0;
- u8 cos;
struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath,
napi);
struct bnx2x *bp = fp->bp;
+ int rx_work_done;
+ u8 cos;
- while (1) {
#ifdef BNX2X_STOP_ON_ERROR
- if (unlikely(bp->panic)) {
- napi_complete(napi);
- return 0;
- }
+ if (unlikely(bp->panic)) {
+ napi_complete(napi);
+ return 0;
+ }
#endif
- if (!bnx2x_fp_lock_napi(fp))
- return budget;
-
- for_each_cos_in_tx_queue(fp, cos)
- if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos]))
- bnx2x_tx_int(bp, fp->txdata_ptr[cos]);
-
- if (bnx2x_has_rx_work(fp)) {
- work_done += bnx2x_rx_int(fp, budget - work_done);
-
- /* must not complete if we consumed full budget */
- if (work_done >= budget) {
- bnx2x_fp_unlock_napi(fp);
- break;
- }
- }
-
- bnx2x_fp_unlock_napi(fp);
+ for_each_cos_in_tx_queue(fp, cos)
+ if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos]))
+ bnx2x_tx_int(bp, fp->txdata_ptr[cos]);
- /* Fall out from the NAPI loop if needed */
- if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
+ rx_work_done = (bnx2x_has_rx_work(fp)) ? bnx2x_rx_int(fp, budget) : 0;
- /* No need to update SB for FCoE L2 ring as long as
- * it's connected to the default SB and the SB
- * has been updated when NAPI was scheduled.
- */
- if (IS_FCOE_FP(fp)) {
- napi_complete(napi);
- break;
- }
+ if (rx_work_done < budget) {
+ /* No need to update SB for FCoE L2 ring as long as
+ * it's connected to the default SB and the SB
+ * has been updated when NAPI was scheduled.
+ */
+ if (IS_FCOE_FP(fp)) {
+ napi_complete(napi);
+ } else {
bnx2x_update_fpsb_idx(fp);
/* bnx2x_has_rx_work() reads the status block,
* thus we need to ensure that status block indices
@@ -3286,40 +3251,15 @@ static int bnx2x_poll(struct napi_struct *napi, int budget)
bnx2x_ack_sb(bp, fp->igu_sb_id, USTORM_ID,
le16_to_cpu(fp->fp_hc_idx),
IGU_INT_ENABLE, 1);
- break;
+ } else {
+ rx_work_done = budget;
}
}
}
- return work_done;
+ return rx_work_done;
}
-#ifdef CONFIG_NET_RX_BUSY_POLL
-/* must be called with local_bh_disable()d */
-int bnx2x_low_latency_recv(struct napi_struct *napi)
-{
- struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath,
- napi);
- struct bnx2x *bp = fp->bp;
- int found = 0;
-
- if ((bp->state == BNX2X_STATE_CLOSED) ||
- (bp->state == BNX2X_STATE_ERROR) ||
- (bp->dev->features & (NETIF_F_LRO | NETIF_F_GRO)))
- return LL_FLUSH_FAILED;
-
- if (!bnx2x_fp_lock_poll(fp))
- return LL_FLUSH_BUSY;
-
- if (bnx2x_has_rx_work(fp))
- found = bnx2x_rx_int(fp, 4);
-
- bnx2x_fp_unlock_poll(fp);
-
- return found;
-}
-#endif
-
/* we split the first BD into headers and data BDs
* to ease the pain of our fellow microcode engineers
* we use one mapping for both BDs
@@ -4494,7 +4434,6 @@ static int bnx2x_alloc_rx_bds(struct bnx2x_fastpath *fp,
/* Limit the CQE producer by the CQE ring size */
fp->rx_comp_prod = min_t(u16, NUM_RCQ_RINGS*RCQ_DESC_CNT,
cqe_ring_prod);
- fp->rx_pkt = fp->rx_calls = 0;
bnx2x_fp_stats(bp, fp)->eth_q_stats.rx_skb_alloc_failed += failure_cnt;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index b7d32e8412f1..4cbb03f87b5a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -570,13 +570,6 @@ int bnx2x_enable_msix(struct bnx2x *bp);
int bnx2x_enable_msi(struct bnx2x *bp);
/**
- * bnx2x_low_latency_recv - LL callback
- *
- * @napi: napi structure
- */
-int bnx2x_low_latency_recv(struct napi_struct *napi);
-
-/**
* bnx2x_alloc_mem_bp - allocate memories outsize main driver structure
*
* @bp: driver handle
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index d84efcd34fac..820b7e04bb5f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -52,7 +52,7 @@ static const struct {
{ Q_STATS_OFFSET32(rx_skb_alloc_failed),
4, "[%s]: rx_skb_alloc_discard" },
{ Q_STATS_OFFSET32(hw_csum_err), 4, "[%s]: rx_csum_offload_errors" },
-
+ { Q_STATS_OFFSET32(driver_xoff), 4, "[%s]: tx_exhaustion_events" },
{ Q_STATS_OFFSET32(total_bytes_transmitted_hi), 8, "[%s]: tx_bytes" },
/* 10 */{ Q_STATS_OFFSET32(total_unicast_packets_transmitted_hi),
8, "[%s]: tx_ucast_packets" },
@@ -74,117 +74,115 @@ static const struct {
static const struct {
long offset;
int size;
- u32 flags;
-#define STATS_FLAGS_PORT 1
-#define STATS_FLAGS_FUNC 2
-#define STATS_FLAGS_BOTH (STATS_FLAGS_FUNC | STATS_FLAGS_PORT)
+ bool is_port_stat;
char string[ETH_GSTRING_LEN];
} bnx2x_stats_arr[] = {
/* 1 */ { STATS_OFFSET32(total_bytes_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_bytes" },
+ 8, false, "rx_bytes" },
{ STATS_OFFSET32(error_bytes_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_error_bytes" },
+ 8, false, "rx_error_bytes" },
{ STATS_OFFSET32(total_unicast_packets_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_ucast_packets" },
+ 8, false, "rx_ucast_packets" },
{ STATS_OFFSET32(total_multicast_packets_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_mcast_packets" },
+ 8, false, "rx_mcast_packets" },
{ STATS_OFFSET32(total_broadcast_packets_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_bcast_packets" },
+ 8, false, "rx_bcast_packets" },
{ STATS_OFFSET32(rx_stat_dot3statsfcserrors_hi),
- 8, STATS_FLAGS_PORT, "rx_crc_errors" },
+ 8, true, "rx_crc_errors" },
{ STATS_OFFSET32(rx_stat_dot3statsalignmenterrors_hi),
- 8, STATS_FLAGS_PORT, "rx_align_errors" },
+ 8, true, "rx_align_errors" },
{ STATS_OFFSET32(rx_stat_etherstatsundersizepkts_hi),
- 8, STATS_FLAGS_PORT, "rx_undersize_packets" },
+ 8, true, "rx_undersize_packets" },
{ STATS_OFFSET32(etherstatsoverrsizepkts_hi),
- 8, STATS_FLAGS_PORT, "rx_oversize_packets" },
+ 8, true, "rx_oversize_packets" },
/* 10 */{ STATS_OFFSET32(rx_stat_etherstatsfragments_hi),
- 8, STATS_FLAGS_PORT, "rx_fragments" },
+ 8, true, "rx_fragments" },
{ STATS_OFFSET32(rx_stat_etherstatsjabbers_hi),
- 8, STATS_FLAGS_PORT, "rx_jabbers" },
+ 8, true, "rx_jabbers" },
{ STATS_OFFSET32(no_buff_discard_hi),
- 8, STATS_FLAGS_BOTH, "rx_discards" },
+ 8, false, "rx_discards" },
{ STATS_OFFSET32(mac_filter_discard),
- 4, STATS_FLAGS_PORT, "rx_filtered_packets" },
+ 4, true, "rx_filtered_packets" },
{ STATS_OFFSET32(mf_tag_discard),
- 4, STATS_FLAGS_PORT, "rx_mf_tag_discard" },
+ 4, true, "rx_mf_tag_discard" },
{ STATS_OFFSET32(pfc_frames_received_hi),
- 8, STATS_FLAGS_PORT, "pfc_frames_received" },
+ 8, true, "pfc_frames_received" },
{ STATS_OFFSET32(pfc_frames_sent_hi),
- 8, STATS_FLAGS_PORT, "pfc_frames_sent" },
+ 8, true, "pfc_frames_sent" },
{ STATS_OFFSET32(brb_drop_hi),
- 8, STATS_FLAGS_PORT, "rx_brb_discard" },
+ 8, true, "rx_brb_discard" },
{ STATS_OFFSET32(brb_truncate_hi),
- 8, STATS_FLAGS_PORT, "rx_brb_truncate" },
+ 8, true, "rx_brb_truncate" },
{ STATS_OFFSET32(pause_frames_received_hi),
- 8, STATS_FLAGS_PORT, "rx_pause_frames" },
+ 8, true, "rx_pause_frames" },
{ STATS_OFFSET32(rx_stat_maccontrolframesreceived_hi),
- 8, STATS_FLAGS_PORT, "rx_mac_ctrl_frames" },
+ 8, true, "rx_mac_ctrl_frames" },
{ STATS_OFFSET32(nig_timer_max),
- 4, STATS_FLAGS_PORT, "rx_constant_pause_events" },
+ 4, true, "rx_constant_pause_events" },
/* 20 */{ STATS_OFFSET32(rx_err_discard_pkt),
- 4, STATS_FLAGS_BOTH, "rx_phy_ip_err_discards"},
+ 4, false, "rx_phy_ip_err_discards"},
{ STATS_OFFSET32(rx_skb_alloc_failed),
- 4, STATS_FLAGS_BOTH, "rx_skb_alloc_discard" },
+ 4, false, "rx_skb_alloc_discard" },
{ STATS_OFFSET32(hw_csum_err),
- 4, STATS_FLAGS_BOTH, "rx_csum_offload_errors" },
-
+ 4, false, "rx_csum_offload_errors" },
+ { STATS_OFFSET32(driver_xoff),
+ 4, false, "tx_exhaustion_events" },
{ STATS_OFFSET32(total_bytes_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_bytes" },
+ 8, false, "tx_bytes" },
{ STATS_OFFSET32(tx_stat_ifhcoutbadoctets_hi),
- 8, STATS_FLAGS_PORT, "tx_error_bytes" },
+ 8, true, "tx_error_bytes" },
{ STATS_OFFSET32(total_unicast_packets_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_ucast_packets" },
+ 8, false, "tx_ucast_packets" },
{ STATS_OFFSET32(total_multicast_packets_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_mcast_packets" },
+ 8, false, "tx_mcast_packets" },
{ STATS_OFFSET32(total_broadcast_packets_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_bcast_packets" },
+ 8, false, "tx_bcast_packets" },
{ STATS_OFFSET32(tx_stat_dot3statsinternalmactransmiterrors_hi),
- 8, STATS_FLAGS_PORT, "tx_mac_errors" },
+ 8, true, "tx_mac_errors" },
{ STATS_OFFSET32(rx_stat_dot3statscarriersenseerrors_hi),
- 8, STATS_FLAGS_PORT, "tx_carrier_errors" },
+ 8, true, "tx_carrier_errors" },
/* 30 */{ STATS_OFFSET32(tx_stat_dot3statssinglecollisionframes_hi),
- 8, STATS_FLAGS_PORT, "tx_single_collisions" },
+ 8, true, "tx_single_collisions" },
{ STATS_OFFSET32(tx_stat_dot3statsmultiplecollisionframes_hi),
- 8, STATS_FLAGS_PORT, "tx_multi_collisions" },
+ 8, true, "tx_multi_collisions" },
{ STATS_OFFSET32(tx_stat_dot3statsdeferredtransmissions_hi),
- 8, STATS_FLAGS_PORT, "tx_deferred" },
+ 8, true, "tx_deferred" },
{ STATS_OFFSET32(tx_stat_dot3statsexcessivecollisions_hi),
- 8, STATS_FLAGS_PORT, "tx_excess_collisions" },
+ 8, true, "tx_excess_collisions" },
{ STATS_OFFSET32(tx_stat_dot3statslatecollisions_hi),
- 8, STATS_FLAGS_PORT, "tx_late_collisions" },
+ 8, true, "tx_late_collisions" },
{ STATS_OFFSET32(tx_stat_etherstatscollisions_hi),
- 8, STATS_FLAGS_PORT, "tx_total_collisions" },
+ 8, true, "tx_total_collisions" },
{ STATS_OFFSET32(tx_stat_etherstatspkts64octets_hi),
- 8, STATS_FLAGS_PORT, "tx_64_byte_packets" },
+ 8, true, "tx_64_byte_packets" },
{ STATS_OFFSET32(tx_stat_etherstatspkts65octetsto127octets_hi),
- 8, STATS_FLAGS_PORT, "tx_65_to_127_byte_packets" },
+ 8, true, "tx_65_to_127_byte_packets" },
{ STATS_OFFSET32(tx_stat_etherstatspkts128octetsto255octets_hi),
- 8, STATS_FLAGS_PORT, "tx_128_to_255_byte_packets" },
+ 8, true, "tx_128_to_255_byte_packets" },
{ STATS_OFFSET32(tx_stat_etherstatspkts256octetsto511octets_hi),
- 8, STATS_FLAGS_PORT, "tx_256_to_511_byte_packets" },
+ 8, true, "tx_256_to_511_byte_packets" },
/* 40 */{ STATS_OFFSET32(tx_stat_etherstatspkts512octetsto1023octets_hi),
- 8, STATS_FLAGS_PORT, "tx_512_to_1023_byte_packets" },
+ 8, true, "tx_512_to_1023_byte_packets" },
{ STATS_OFFSET32(etherstatspkts1024octetsto1522octets_hi),
- 8, STATS_FLAGS_PORT, "tx_1024_to_1522_byte_packets" },
+ 8, true, "tx_1024_to_1522_byte_packets" },
{ STATS_OFFSET32(etherstatspktsover1522octets_hi),
- 8, STATS_FLAGS_PORT, "tx_1523_to_9022_byte_packets" },
+ 8, true, "tx_1523_to_9022_byte_packets" },
{ STATS_OFFSET32(pause_frames_sent_hi),
- 8, STATS_FLAGS_PORT, "tx_pause_frames" },
+ 8, true, "tx_pause_frames" },
{ STATS_OFFSET32(total_tpa_aggregations_hi),
- 8, STATS_FLAGS_FUNC, "tpa_aggregations" },
+ 8, false, "tpa_aggregations" },
{ STATS_OFFSET32(total_tpa_aggregated_frames_hi),
- 8, STATS_FLAGS_FUNC, "tpa_aggregated_frames"},
+ 8, false, "tpa_aggregated_frames"},
{ STATS_OFFSET32(total_tpa_bytes_hi),
- 8, STATS_FLAGS_FUNC, "tpa_bytes"},
+ 8, false, "tpa_bytes"},
{ STATS_OFFSET32(recoverable_error),
- 4, STATS_FLAGS_FUNC, "recoverable_errors" },
+ 4, false, "recoverable_errors" },
{ STATS_OFFSET32(unrecoverable_error),
- 4, STATS_FLAGS_FUNC, "unrecoverable_errors" },
+ 4, false, "unrecoverable_errors" },
{ STATS_OFFSET32(driver_filtered_tx_pkt),
- 4, STATS_FLAGS_FUNC, "driver_filtered_tx_pkt" },
+ 4, false, "driver_filtered_tx_pkt" },
{ STATS_OFFSET32(eee_tx_lpi),
- 4, STATS_FLAGS_PORT, "Tx LPI entry count"}
+ 4, true, "Tx LPI entry count"}
};
#define BNX2X_NUM_STATS ARRAY_SIZE(bnx2x_stats_arr)
@@ -3065,12 +3063,8 @@ static void bnx2x_self_test(struct net_device *dev,
}
}
-#define IS_PORT_STAT(i) \
- ((bnx2x_stats_arr[i].flags & STATS_FLAGS_BOTH) == STATS_FLAGS_PORT)
-#define IS_FUNC_STAT(i) (bnx2x_stats_arr[i].flags & STATS_FLAGS_FUNC)
-#define HIDE_PORT_STAT(bp) \
- ((IS_MF(bp) && !(bp->msg_enable & BNX2X_MSG_STATS)) || \
- IS_VF(bp))
+#define IS_PORT_STAT(i) (bnx2x_stats_arr[i].is_port_stat)
+#define HIDE_PORT_STAT(bp) IS_VF(bp)
/* ethtool statistics are displayed for all regular ethernet queues and the
* fcoe L2 queue if not disabled
@@ -3094,7 +3088,7 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset)
num_strings = 0;
if (HIDE_PORT_STAT(bp)) {
for (i = 0; i < BNX2X_NUM_STATS; i++)
- if (IS_FUNC_STAT(i))
+ if (!IS_PORT_STAT(i))
num_strings++;
} else
num_strings += BNX2X_NUM_STATS;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
index cafd5de675cf..27aa0802d87d 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
@@ -3013,8 +3013,8 @@ struct afex_stats {
};
#define BCM_5710_FW_MAJOR_VERSION 7
-#define BCM_5710_FW_MINOR_VERSION 12
-#define BCM_5710_FW_REVISION_VERSION 30
+#define BCM_5710_FW_MINOR_VERSION 13
+#define BCM_5710_FW_REVISION_VERSION 1
#define BCM_5710_FW_ENGINEERING_VERSION 0
#define BCM_5710_FW_COMPILE_FLAGS 1
@@ -3583,7 +3583,7 @@ enum classify_rule {
CLASSIFY_RULE_OPCODE_MAC,
CLASSIFY_RULE_OPCODE_VLAN,
CLASSIFY_RULE_OPCODE_PAIR,
- CLASSIFY_RULE_OPCODE_VXLAN,
+ CLASSIFY_RULE_OPCODE_IMAC_VNI,
MAX_CLASSIFY_RULE
};
@@ -3826,6 +3826,17 @@ struct eth_classify_header {
__le32 echo;
};
+/*
+ * Command for adding/removing a Inner-MAC/VNI classification rule
+ */
+struct eth_classify_imac_vni_cmd {
+ struct eth_classify_cmd_header header;
+ __le32 vni;
+ __le16 imac_lsb;
+ __le16 imac_mid;
+ __le16 imac_msb;
+ __le16 reserved1;
+};
/*
* Command for adding/removing a MAC classification rule
@@ -3869,14 +3880,6 @@ struct eth_classify_vlan_cmd {
/*
* Command for adding/removing a VXLAN classification rule
*/
-struct eth_classify_vxlan_cmd {
- struct eth_classify_cmd_header header;
- __le32 vni;
- __le16 inner_mac_lsb;
- __le16 inner_mac_mid;
- __le16 inner_mac_msb;
- __le16 reserved1;
-};
/*
* union for eth classification rule
@@ -3885,7 +3888,7 @@ union eth_classify_rule_cmd {
struct eth_classify_mac_cmd mac;
struct eth_classify_vlan_cmd vlan;
struct eth_classify_pair_cmd pair;
- struct eth_classify_vxlan_cmd vxlan;
+ struct eth_classify_imac_vni_cmd imac_vni;
};
/*
@@ -5623,6 +5626,14 @@ enum igu_mode {
MAX_IGU_MODE
};
+/*
+ * Inner Headers Classification Type
+ */
+enum inner_clss_type {
+ INNER_CLSS_DISABLED,
+ INNER_CLSS_USE_VLAN,
+ INNER_CLSS_USE_VNI,
+ MAX_INNER_CLSS_TYPE};
/*
* IP versions
@@ -5953,14 +5964,6 @@ enum ts_offset_cmd {
MAX_TS_OFFSET_CMD
};
-/* Tunnel Mode */
-enum tunnel_mode {
- TUNN_MODE_NONE,
- TUNN_MODE_VXLAN,
- TUNN_MODE_GRE,
- MAX_TUNNEL_MODE
-};
-
/* zone A per-queue data */
struct ustorm_queue_zone_data {
struct ustorm_eth_rx_producers eth_rx_producers;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 2e611dc5f162..6c4e3a69976f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -13004,9 +13004,6 @@ static const struct net_device_ops bnx2x_netdev_ops = {
.ndo_fcoe_get_wwn = bnx2x_fcoe_get_wwn,
#endif
-#ifdef CONFIG_NET_RX_BUSY_POLL
- .ndo_busy_poll = bnx2x_low_latency_recv,
-#endif
.ndo_get_phys_port_id = bnx2x_get_phys_port_id,
.ndo_set_vf_link_state = bnx2x_set_vf_link_state,
.ndo_features_check = bnx2x_features_check,
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 07f5f239cb65..df835f5e46d8 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -72,8 +72,10 @@ MODULE_VERSION(DRV_MODULE_VERSION);
#define BNXT_TX_PUSH_THRESH 92
enum board_idx {
+ BCM57301,
BCM57302,
BCM57304,
+ BCM57402,
BCM57404,
BCM57406,
BCM57304_VF,
@@ -84,17 +86,21 @@ enum board_idx {
static const struct {
char *name;
} board_info[] = {
- { "Broadcom BCM57302 NetXtreme-C Single-port 10Gb/25Gb/40Gb/50Gb Ethernet" },
+ { "Broadcom BCM57301 NetXtreme-C Single-port 10Gb Ethernet" },
+ { "Broadcom BCM57302 NetXtreme-C Dual-port 10Gb/25Gb Ethernet" },
{ "Broadcom BCM57304 NetXtreme-C Dual-port 10Gb/25Gb/40Gb/50Gb Ethernet" },
+ { "Broadcom BCM57402 NetXtreme-E Dual-port 10Gb Ethernet" },
{ "Broadcom BCM57404 NetXtreme-E Dual-port 10Gb/25Gb Ethernet" },
- { "Broadcom BCM57406 NetXtreme-E Dual-port 10Gb Ethernet" },
+ { "Broadcom BCM57406 NetXtreme-E Dual-port 10GBase-T Ethernet" },
{ "Broadcom BCM57304 NetXtreme-C Ethernet Virtual Function" },
{ "Broadcom BCM57404 NetXtreme-E Ethernet Virtual Function" },
};
static const struct pci_device_id bnxt_pci_tbl[] = {
+ { PCI_VDEVICE(BROADCOM, 0x16c8), .driver_data = BCM57301 },
{ PCI_VDEVICE(BROADCOM, 0x16c9), .driver_data = BCM57302 },
{ PCI_VDEVICE(BROADCOM, 0x16ca), .driver_data = BCM57304 },
+ { PCI_VDEVICE(BROADCOM, 0x16d0), .driver_data = BCM57402 },
{ PCI_VDEVICE(BROADCOM, 0x16d1), .driver_data = BCM57404 },
{ PCI_VDEVICE(BROADCOM, 0x16d2), .driver_data = BCM57406 },
#ifdef CONFIG_BNXT_SRIOV
@@ -173,7 +179,6 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
u32 len, free_size, vlan_tag_flags, cfa_action, flags;
u16 prod, last_frag;
struct pci_dev *pdev = bp->pdev;
- struct bnxt_napi *bnapi;
struct bnxt_tx_ring_info *txr;
struct bnxt_sw_tx_bd *tx_buf;
@@ -183,8 +188,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
- bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
+ txr = &bp->tx_ring[i];
txq = netdev_get_tx_queue(dev, i);
prod = txr->tx_prod;
@@ -417,8 +421,8 @@ tx_dma_error:
static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts)
{
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
- int index = bnapi->index;
+ struct bnxt_tx_ring_info *txr = bnapi->tx_ring;
+ int index = txr - &bp->tx_ring[0];
struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, index);
u16 cons = txr->tx_cons;
struct pci_dev *pdev = bp->pdev;
@@ -596,7 +600,7 @@ static void bnxt_reuse_rx_agg_bufs(struct bnxt_napi *bnapi, u16 cp_cons,
{
struct bnxt *bp = bnapi->bp;
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
u16 prod = rxr->rx_agg_prod;
u16 sw_prod = rxr->rx_sw_agg_prod;
u32 i;
@@ -675,7 +679,7 @@ static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi,
{
struct pci_dev *pdev = bp->pdev;
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
u16 prod = rxr->rx_agg_prod;
u32 i;
@@ -856,8 +860,13 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt_tpa_info *tpa_info,
struct tcphdr *th;
int payload_off, tcp_opt_len = 0;
int len, nw_off;
+ u16 segs;
+
+ segs = TPA_END_TPA_SEGS(tpa_end);
+ if (segs == 1)
+ return skb;
- NAPI_GRO_CB(skb)->count = TPA_END_TPA_SEGS(tpa_end);
+ NAPI_GRO_CB(skb)->count = segs;
skb_shinfo(skb)->gso_size =
le32_to_cpu(tpa_end1->rx_tpa_end_cmp_seg_len);
skb_shinfo(skb)->gso_type = tpa_info->gso_type;
@@ -929,7 +938,7 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp,
bool *agg_event)
{
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
u8 agg_id = TPA_END_AGG_ID(tpa_end);
u8 *data, agg_bufs;
u16 cp_cons = RING_CMP(*raw_cons);
@@ -1045,7 +1054,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons,
bool *agg_event)
{
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
struct net_device *dev = bp->dev;
struct rx_cmp *rxcmp;
struct rx_cmp_ext *rxcmp1;
@@ -1187,8 +1196,10 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons,
skb->csum_level = RX_CMP_ENCAP(rxcmp1);
}
} else {
- if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS)
- cpr->rx_l4_csum_errors++;
+ if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) {
+ if (dev->features & NETIF_F_RXCSUM)
+ cpr->rx_l4_csum_errors++;
+ }
}
skb_record_rx_queue(skb, bnapi->index);
@@ -1377,7 +1388,7 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget)
bnxt_tx_int(bp, bnapi, tx_pkts);
if (rx_event) {
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
@@ -1446,19 +1457,14 @@ static void bnxt_free_tx_skbs(struct bnxt *bp)
int i, max_idx;
struct pci_dev *pdev = bp->pdev;
- if (!bp->bnapi)
+ if (!bp->tx_ring)
return;
max_idx = bp->tx_nr_pages * TX_DESC_CNT;
for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
int j;
- if (!bnapi)
- continue;
-
- txr = &bnapi->tx_ring;
for (j = 0; j < max_idx;) {
struct bnxt_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j];
struct sk_buff *skb = tx_buf->skb;
@@ -1504,21 +1510,15 @@ static void bnxt_free_rx_skbs(struct bnxt *bp)
int i, max_idx, max_agg_idx;
struct pci_dev *pdev = bp->pdev;
- if (!bp->bnapi)
+ if (!bp->rx_ring)
return;
max_idx = bp->rx_nr_pages * RX_DESC_CNT;
max_agg_idx = bp->rx_agg_nr_pages * RX_DESC_CNT;
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
int j;
- if (!bnapi)
- continue;
-
- rxr = &bnapi->rx_ring;
-
if (rxr->rx_tpa) {
for (j = 0; j < MAX_TPA; j++) {
struct bnxt_tpa_info *tpa_info =
@@ -1646,19 +1646,13 @@ static void bnxt_free_rx_rings(struct bnxt *bp)
{
int i;
- if (!bp->bnapi)
+ if (!bp->rx_ring)
return;
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- rxr = &bnapi->rx_ring;
-
kfree(rxr->rx_tpa);
rxr->rx_tpa = NULL;
@@ -1677,6 +1671,9 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp)
{
int i, rc, agg_rings = 0, tpa_rings = 0;
+ if (!bp->rx_ring)
+ return -ENOMEM;
+
if (bp->flags & BNXT_FLAG_AGG_RINGS)
agg_rings = 1;
@@ -1684,14 +1681,9 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp)
tpa_rings = 1;
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- rxr = &bnapi->rx_ring;
ring = &rxr->rx_ring_struct;
rc = bnxt_alloc_ring(bp, ring);
@@ -1729,19 +1721,13 @@ static void bnxt_free_tx_rings(struct bnxt *bp)
int i;
struct pci_dev *pdev = bp->pdev;
- if (!bp->bnapi)
+ if (!bp->tx_ring)
return;
for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- txr = &bnapi->tx_ring;
-
if (txr->tx_push) {
dma_free_coherent(&pdev->dev, bp->tx_push_size,
txr->tx_push, txr->tx_push_mapping);
@@ -1775,14 +1761,9 @@ static int bnxt_alloc_tx_rings(struct bnxt *bp)
}
for (i = 0, j = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- txr = &bnapi->tx_ring;
ring = &txr->tx_ring_struct;
rc = bnxt_alloc_ring(bp, ring);
@@ -1885,7 +1866,10 @@ static void bnxt_init_ring_struct(struct bnxt *bp)
ring->dma_arr = cpr->cp_desc_mapping;
ring->vmem_size = 0;
- rxr = &bnapi->rx_ring;
+ rxr = bnapi->rx_ring;
+ if (!rxr)
+ goto skip_rx;
+
ring = &rxr->rx_ring_struct;
ring->nr_pages = bp->rx_nr_pages;
ring->page_size = HW_RXBD_RING_SIZE;
@@ -1902,7 +1886,11 @@ static void bnxt_init_ring_struct(struct bnxt *bp)
ring->vmem_size = SW_RXBD_AGG_RING_SIZE * bp->rx_agg_nr_pages;
ring->vmem = (void **)&rxr->rx_agg_ring;
- txr = &bnapi->tx_ring;
+skip_rx:
+ txr = bnapi->tx_ring;
+ if (!txr)
+ continue;
+
ring = &txr->tx_ring_struct;
ring->nr_pages = bp->tx_nr_pages;
ring->page_size = HW_RXBD_RING_SIZE;
@@ -1938,22 +1926,18 @@ static void bnxt_init_rxbd_pages(struct bnxt_ring_struct *ring, u32 type)
static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
{
struct net_device *dev = bp->dev;
- struct bnxt_napi *bnapi = bp->bnapi[ring_nr];
struct bnxt_rx_ring_info *rxr;
struct bnxt_ring_struct *ring;
u32 prod, type;
int i;
- if (!bnapi)
- return -EINVAL;
-
type = (bp->rx_buf_use_size << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_PACKET_BD | RX_BD_FLAGS_EOP;
if (NET_IP_ALIGN == 2)
type |= RX_BD_FLAGS_SOP;
- rxr = &bnapi->rx_ring;
+ rxr = &bp->rx_ring[ring_nr];
ring = &rxr->rx_ring_struct;
bnxt_init_rxbd_pages(ring, type);
@@ -1969,11 +1953,12 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
rxr->rx_prod = prod;
ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring = &rxr->rx_agg_ring_struct;
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+
if (!(bp->flags & BNXT_FLAG_AGG_RINGS))
return 0;
- ring = &rxr->rx_agg_ring_struct;
-
type = ((u32)PAGE_SIZE << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP;
@@ -1989,7 +1974,6 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
prod = NEXT_RX_AGG(prod);
}
rxr->rx_agg_prod = prod;
- ring->fw_ring_id = INVALID_HW_RING_ID;
if (bp->flags & BNXT_FLAG_TPA) {
if (rxr->rx_tpa) {
@@ -2035,8 +2019,7 @@ static int bnxt_init_tx_rings(struct bnxt *bp)
MAX_SKB_FRAGS + 1);
for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
ring->fw_ring_id = INVALID_HW_RING_ID;
@@ -2423,14 +2406,18 @@ static void bnxt_clear_ring_indices(struct bnxt *bp)
cpr = &bnapi->cp_ring;
cpr->cp_raw_cons = 0;
- txr = &bnapi->tx_ring;
- txr->tx_prod = 0;
- txr->tx_cons = 0;
+ txr = bnapi->tx_ring;
+ if (txr) {
+ txr->tx_prod = 0;
+ txr->tx_cons = 0;
+ }
- rxr = &bnapi->rx_ring;
- rxr->rx_prod = 0;
- rxr->rx_agg_prod = 0;
- rxr->rx_sw_agg_prod = 0;
+ rxr = bnapi->rx_ring;
+ if (rxr) {
+ rxr->rx_prod = 0;
+ rxr->rx_agg_prod = 0;
+ rxr->rx_sw_agg_prod = 0;
+ }
}
}
@@ -2496,6 +2483,10 @@ static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init)
bnxt_free_stats(bp);
bnxt_free_ring_grps(bp);
bnxt_free_vnics(bp);
+ kfree(bp->tx_ring);
+ bp->tx_ring = NULL;
+ kfree(bp->rx_ring);
+ bp->rx_ring = NULL;
kfree(bp->bnapi);
bp->bnapi = NULL;
} else {
@@ -2505,7 +2496,7 @@ static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init)
static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init)
{
- int i, rc, size, arr_size;
+ int i, j, rc, size, arr_size;
void *bnapi;
if (irq_re_init) {
@@ -2527,6 +2518,33 @@ static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init)
bp->bnapi[i]->bp = bp;
}
+ bp->rx_ring = kcalloc(bp->rx_nr_rings,
+ sizeof(struct bnxt_rx_ring_info),
+ GFP_KERNEL);
+ if (!bp->rx_ring)
+ return -ENOMEM;
+
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ bp->rx_ring[i].bnapi = bp->bnapi[i];
+ bp->bnapi[i]->rx_ring = &bp->rx_ring[i];
+ }
+
+ bp->tx_ring = kcalloc(bp->tx_nr_rings,
+ sizeof(struct bnxt_tx_ring_info),
+ GFP_KERNEL);
+ if (!bp->tx_ring)
+ return -ENOMEM;
+
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS)
+ j = 0;
+ else
+ j = bp->rx_nr_rings;
+
+ for (i = 0; i < bp->tx_nr_rings; i++, j++) {
+ bp->tx_ring[i].bnapi = bp->bnapi[j];
+ bp->bnapi[j]->tx_ring = &bp->tx_ring[i];
+ }
+
rc = bnxt_alloc_stats(bp);
if (rc)
goto alloc_mem_err;
@@ -2596,6 +2614,9 @@ int _hwrm_send_message(struct bnxt *bp, void *msg, u32 msg_len, int timeout)
/* Write request msg to hwrm channel */
__iowrite32_copy(bp->bar0, data, msg_len / 4);
+ for (i = msg_len; i < HWRM_MAX_REQ_LEN; i += 4)
+ writel(0, bp->bar0 + i);
+
/* currently supports only one outstanding message */
if (intr_process)
bp->hwrm_intr_seq_id = le32_to_cpu(req->target_id_seq_id) &
@@ -2710,6 +2731,14 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp)
return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
}
+static int bnxt_hwrm_func_drv_unrgtr(struct bnxt *bp)
+{
+ struct hwrm_func_drv_unrgtr_input req = {0};
+
+ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_DRV_UNRGTR, -1, -1);
+ return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
static int bnxt_hwrm_tunnel_dst_port_free(struct bnxt *bp, u8 tunnel_type)
{
u32 rc = 0;
@@ -2772,7 +2801,7 @@ static int bnxt_hwrm_cfa_l2_set_rx_mask(struct bnxt *bp, u16 vnic_id)
struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_SET_RX_MASK, -1, -1);
- req.dflt_vnic_id = cpu_to_le32(vnic->fw_vnic_id);
+ req.vnic_id = cpu_to_le32(vnic->fw_vnic_id);
req.num_mc_entries = cpu_to_le32(vnic->mc_list_count);
req.mc_tbl_addr = cpu_to_le64(vnic->mc_list_mapping);
@@ -2805,7 +2834,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_free(struct bnxt *bp,
CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK | \
CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT | \
CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK | \
- CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID)
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID)
static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
struct bnxt_ntuple_filter *fltr)
@@ -2824,7 +2853,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
req.ethertype = htons(ETH_P_IP);
memcpy(req.src_macaddr, fltr->src_mac_addr, ETH_ALEN);
- req.ipaddr_type = 4;
+ req.ip_addr_type = CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4;
req.ip_protocol = keys->basic.ip_proto;
req.src_ipaddr[0] = keys->addrs.v4addrs.src;
@@ -2837,7 +2866,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
req.dst_port = keys->ports.dst;
req.dst_port_mask = cpu_to_be16(0xffff);
- req.dst_vnic_id = cpu_to_le16(vnic->fw_vnic_id);
+ req.dst_id = cpu_to_le16(vnic->fw_vnic_id);
mutex_lock(&bp->hwrm_cmd_lock);
rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
if (!rc)
@@ -2857,10 +2886,10 @@ static int bnxt_hwrm_set_vnic_filter(struct bnxt *bp, u16 vnic_id, u16 idx,
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_FILTER_ALLOC, -1, -1);
req.flags = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX |
CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST);
- req.dst_vnic_id = cpu_to_le16(bp->vnic_info[vnic_id].fw_vnic_id);
+ req.dst_id = cpu_to_le16(bp->vnic_info[vnic_id].fw_vnic_id);
req.enables =
cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR |
- CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID |
+ CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID |
CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK);
memcpy(req.l2_addr, mac_addr, ETH_ALEN);
req.l2_addr_mask[0] = 0xff;
@@ -2930,7 +2959,8 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags)
req.enables =
cpu_to_le32(VNIC_TPA_CFG_REQ_ENABLES_MAX_AGG_SEGS |
- VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS);
+ VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS |
+ VNIC_TPA_CFG_REQ_ENABLES_MIN_AGG_LEN);
/* Number of segs are log2 units, and first packet is not
* included as part of this units.
@@ -2948,6 +2978,8 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags)
segs = ilog2(nsegs);
req.max_agg_segs = cpu_to_le16(segs);
req.max_aggs = cpu_to_le16(VNIC_TPA_CFG_REQ_MAX_AGGS_MAX);
+
+ req.min_agg_len = cpu_to_le32(512);
}
req.vnic_id = cpu_to_le16(vnic->fw_vnic_id);
@@ -3058,7 +3090,7 @@ static int bnxt_hwrm_vnic_ctx_alloc(struct bnxt *bp, u16 vnic_id)
static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
{
- int grp_idx = 0;
+ unsigned int ring = 0, grp_idx;
struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
struct hwrm_vnic_cfg_input req = {0};
@@ -3069,10 +3101,11 @@ static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
req.rss_rule = cpu_to_le16(vnic->fw_rss_cos_lb_ctx);
req.cos_rule = cpu_to_le16(0xffff);
if (vnic->flags & BNXT_VNIC_RSS_FLAG)
- grp_idx = 0;
+ ring = 0;
else if (vnic->flags & BNXT_VNIC_RFS_FLAG)
- grp_idx = vnic_id - 1;
+ ring = vnic_id - 1;
+ grp_idx = bp->rx_ring[ring].bnapi->index;
req.vnic_id = cpu_to_le16(vnic->fw_vnic_id);
req.dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id);
@@ -3113,22 +3146,25 @@ static void bnxt_hwrm_vnic_free(struct bnxt *bp)
bnxt_hwrm_vnic_free_one(bp, i);
}
-static int bnxt_hwrm_vnic_alloc(struct bnxt *bp, u16 vnic_id, u16 start_grp_id,
- u16 end_grp_id)
+static int bnxt_hwrm_vnic_alloc(struct bnxt *bp, u16 vnic_id,
+ unsigned int start_rx_ring_idx,
+ unsigned int nr_rings)
{
- u32 rc = 0, i, j;
+ int rc = 0;
+ unsigned int i, j, grp_idx, end_idx = start_rx_ring_idx + nr_rings;
struct hwrm_vnic_alloc_input req = {0};
struct hwrm_vnic_alloc_output *resp = bp->hwrm_cmd_resp_addr;
/* map ring groups to this vnic */
- for (i = start_grp_id, j = 0; i < end_grp_id; i++, j++) {
- if (bp->grp_info[i].fw_grp_id == INVALID_HW_RING_ID) {
+ for (i = start_rx_ring_idx, j = 0; i < end_idx; i++, j++) {
+ grp_idx = bp->rx_ring[i].bnapi->index;
+ if (bp->grp_info[grp_idx].fw_grp_id == INVALID_HW_RING_ID) {
netdev_err(bp->dev, "Not enough ring groups avail:%x req:%x\n",
- j, (end_grp_id - start_grp_id));
+ j, nr_rings);
break;
}
bp->vnic_info[vnic_id].fw_grp_ids[j] =
- bp->grp_info[i].fw_grp_id;
+ bp->grp_info[grp_idx].fw_grp_id;
}
bp->vnic_info[vnic_id].fw_rss_cos_lb_ctx = INVALID_HW_RING_ID;
@@ -3155,20 +3191,22 @@ static int bnxt_hwrm_ring_grp_alloc(struct bnxt *bp)
struct hwrm_ring_grp_alloc_input req = {0};
struct hwrm_ring_grp_alloc_output *resp =
bp->hwrm_cmd_resp_addr;
+ unsigned int grp_idx = bp->rx_ring[i].bnapi->index;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_GRP_ALLOC, -1, -1);
- req.cr = cpu_to_le16(bp->grp_info[i].cp_fw_ring_id);
- req.rr = cpu_to_le16(bp->grp_info[i].rx_fw_ring_id);
- req.ar = cpu_to_le16(bp->grp_info[i].agg_fw_ring_id);
- req.sc = cpu_to_le16(bp->grp_info[i].fw_stats_ctx);
+ req.cr = cpu_to_le16(bp->grp_info[grp_idx].cp_fw_ring_id);
+ req.rr = cpu_to_le16(bp->grp_info[grp_idx].rx_fw_ring_id);
+ req.ar = cpu_to_le16(bp->grp_info[grp_idx].agg_fw_ring_id);
+ req.sc = cpu_to_le16(bp->grp_info[grp_idx].fw_stats_ctx);
rc = _hwrm_send_message(bp, &req, sizeof(req),
HWRM_CMD_TIMEOUT);
if (rc)
break;
- bp->grp_info[i].fw_grp_id = le32_to_cpu(resp->ring_group_id);
+ bp->grp_info[grp_idx].fw_grp_id =
+ le32_to_cpu(resp->ring_group_id);
}
mutex_unlock(&bp->hwrm_cmd_lock);
return rc;
@@ -3293,75 +3331,66 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
{
int i, rc = 0;
- if (bp->cp_nr_rings) {
- for (i = 0; i < bp->cp_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
+ for (i = 0; i < bp->cp_nr_rings; i++) {
+ struct bnxt_napi *bnapi = bp->bnapi[i];
+ struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+ struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
- rc = hwrm_ring_alloc_send_msg(bp, ring,
- HWRM_RING_ALLOC_CMPL, i,
- INVALID_STATS_CTX_ID);
- if (rc)
- goto err_out;
- cpr->cp_doorbell = bp->bar1 + i * 0x80;
- BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
- bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id;
- }
+ rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_CMPL, i,
+ INVALID_STATS_CTX_ID);
+ if (rc)
+ goto err_out;
+ cpr->cp_doorbell = bp->bar1 + i * 0x80;
+ BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
+ bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id;
}
- if (bp->tx_nr_rings) {
- for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
- struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
- u16 fw_stats_ctx = bp->grp_info[i].fw_stats_ctx;
+ for (i = 0; i < bp->tx_nr_rings; i++) {
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
+ struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
+ u32 map_idx = txr->bnapi->index;
+ u16 fw_stats_ctx = bp->grp_info[map_idx].fw_stats_ctx;
- rc = hwrm_ring_alloc_send_msg(bp, ring,
- HWRM_RING_ALLOC_TX, i,
- fw_stats_ctx);
- if (rc)
- goto err_out;
- txr->tx_doorbell = bp->bar1 + i * 0x80;
- }
+ rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_TX,
+ map_idx, fw_stats_ctx);
+ if (rc)
+ goto err_out;
+ txr->tx_doorbell = bp->bar1 + map_idx * 0x80;
}
- if (bp->rx_nr_rings) {
- for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
- struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
+ struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+ u32 map_idx = rxr->bnapi->index;
- rc = hwrm_ring_alloc_send_msg(bp, ring,
- HWRM_RING_ALLOC_RX, i,
- INVALID_STATS_CTX_ID);
- if (rc)
- goto err_out;
- rxr->rx_doorbell = bp->bar1 + i * 0x80;
- writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
- bp->grp_info[i].rx_fw_ring_id = ring->fw_ring_id;
- }
+ rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_RX,
+ map_idx, INVALID_STATS_CTX_ID);
+ if (rc)
+ goto err_out;
+ rxr->rx_doorbell = bp->bar1 + map_idx * 0x80;
+ writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
+ bp->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id;
}
if (bp->flags & BNXT_FLAG_AGG_RINGS) {
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
struct bnxt_ring_struct *ring =
&rxr->rx_agg_ring_struct;
+ u32 grp_idx = rxr->bnapi->index;
+ u32 map_idx = grp_idx + bp->rx_nr_rings;
rc = hwrm_ring_alloc_send_msg(bp, ring,
HWRM_RING_ALLOC_AGG,
- bp->rx_nr_rings + i,
+ map_idx,
INVALID_STATS_CTX_ID);
if (rc)
goto err_out;
- rxr->rx_agg_doorbell =
- bp->bar1 + (bp->rx_nr_rings + i) * 0x80;
+ rxr->rx_agg_doorbell = bp->bar1 + map_idx * 0x80;
writel(DB_KEY_RX | rxr->rx_agg_prod,
rxr->rx_agg_doorbell);
- bp->grp_info[i].agg_fw_ring_id = ring->fw_ring_id;
+ bp->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id;
}
}
err_out:
@@ -3408,91 +3437,75 @@ static int hwrm_ring_free_send_msg(struct bnxt *bp,
return 0;
}
-static int bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path)
+static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path)
{
- int i, rc = 0;
+ int i;
if (!bp->bnapi)
- return 0;
+ return;
- if (bp->tx_nr_rings) {
- for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
- struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
- u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_TX,
- close_path ? cmpl_ring_id :
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->tx_nr_rings; i++) {
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
+ struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
+ u32 grp_idx = txr->bnapi->index;
+ u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_TX,
+ close_path ? cmpl_ring_id :
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
}
}
- if (bp->rx_nr_rings) {
- for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
- struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
- u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_RX,
- close_path ? cmpl_ring_id :
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- bp->grp_info[i].rx_fw_ring_id =
- INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
+ struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+ u32 grp_idx = rxr->bnapi->index;
+ u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_RX,
+ close_path ? cmpl_ring_id :
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+ bp->grp_info[grp_idx].rx_fw_ring_id =
+ INVALID_HW_RING_ID;
}
}
- if (bp->rx_agg_nr_pages) {
- for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
- struct bnxt_ring_struct *ring =
- &rxr->rx_agg_ring_struct;
- u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_RX,
- close_path ? cmpl_ring_id :
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- bp->grp_info[i].agg_fw_ring_id =
- INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
+ struct bnxt_ring_struct *ring = &rxr->rx_agg_ring_struct;
+ u32 grp_idx = rxr->bnapi->index;
+ u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_RX,
+ close_path ? cmpl_ring_id :
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+ bp->grp_info[grp_idx].agg_fw_ring_id =
+ INVALID_HW_RING_ID;
}
}
- if (bp->cp_nr_rings) {
- for (i = 0; i < bp->cp_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_CMPL,
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- bp->grp_info[i].cp_fw_ring_id =
- INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->cp_nr_rings; i++) {
+ struct bnxt_napi *bnapi = bp->bnapi[i];
+ struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+ struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_CMPL,
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+ bp->grp_info[i].cp_fw_ring_id = INVALID_HW_RING_ID;
}
}
-
- return rc;
}
int bnxt_hwrm_set_coal(struct bnxt *bp)
@@ -3604,7 +3617,7 @@ static int bnxt_hwrm_stat_ctx_alloc(struct bnxt *bp)
return 0;
}
-static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
+int bnxt_hwrm_func_qcaps(struct bnxt *bp)
{
int rc = 0;
struct hwrm_func_qcaps_input req = {0};
@@ -3628,9 +3641,10 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
pf->max_rsscos_ctxs = le16_to_cpu(resp->max_rsscos_ctx);
pf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings);
pf->max_tx_rings = le16_to_cpu(resp->max_tx_rings);
- pf->max_pf_tx_rings = pf->max_tx_rings;
pf->max_rx_rings = le16_to_cpu(resp->max_rx_rings);
- pf->max_pf_rx_rings = pf->max_rx_rings;
+ pf->max_hw_ring_grps = le32_to_cpu(resp->max_hw_ring_grps);
+ if (!pf->max_hw_ring_grps)
+ pf->max_hw_ring_grps = pf->max_tx_rings;
pf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs);
pf->max_vnics = le16_to_cpu(resp->max_vnics);
pf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx);
@@ -3658,6 +3672,9 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
vf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings);
vf->max_tx_rings = le16_to_cpu(resp->max_tx_rings);
vf->max_rx_rings = le16_to_cpu(resp->max_rx_rings);
+ vf->max_hw_ring_grps = le32_to_cpu(resp->max_hw_ring_grps);
+ if (!vf->max_hw_ring_grps)
+ vf->max_hw_ring_grps = vf->max_tx_rings;
vf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs);
vf->max_vnics = le16_to_cpu(resp->max_vnics);
vf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx);
@@ -3734,14 +3751,11 @@ static int bnxt_hwrm_ver_get(struct bnxt *bp)
memcpy(&bp->ver_resp, resp, sizeof(struct hwrm_ver_get_output));
- if (req.hwrm_intf_maj != resp->hwrm_intf_maj ||
- req.hwrm_intf_min != resp->hwrm_intf_min ||
- req.hwrm_intf_upd != resp->hwrm_intf_upd) {
- netdev_warn(bp->dev, "HWRM interface %d.%d.%d does not match driver interface %d.%d.%d.\n",
+ if (resp->hwrm_intf_maj < 1) {
+ netdev_warn(bp->dev, "HWRM interface %d.%d.%d is older than 1.0.0.\n",
resp->hwrm_intf_maj, resp->hwrm_intf_min,
- resp->hwrm_intf_upd, req.hwrm_intf_maj,
- req.hwrm_intf_min, req.hwrm_intf_upd);
- netdev_warn(bp->dev, "Please update driver or firmware with matching interface versions.\n");
+ resp->hwrm_intf_upd);
+ netdev_warn(bp->dev, "Please update firmware with HWRM interface 1.0.0 or newer.\n");
}
snprintf(bp->fw_ver_str, BC_HWRM_STR_LEN, "bc %d.%d.%d rm %d.%d.%d",
resp->hwrm_fw_maj, resp->hwrm_fw_min, resp->hwrm_fw_bld,
@@ -3867,7 +3881,7 @@ static int bnxt_alloc_rfs_vnics(struct bnxt *bp)
break;
bp->vnic_info[vnic_id].flags |= BNXT_VNIC_RFS_FLAG;
- rc = bnxt_hwrm_vnic_alloc(bp, vnic_id, ring_id, ring_id + 1);
+ rc = bnxt_hwrm_vnic_alloc(bp, vnic_id, ring_id, 1);
if (rc) {
netdev_err(bp->dev, "hwrm vnic %d alloc failure rc: %x\n",
vnic_id, rc);
@@ -3944,8 +3958,7 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init)
}
bp->vnic_info[0].uc_filter_count = 1;
- bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST |
- CFA_L2_SET_RX_MASK_REQ_MASK_BCAST;
+ bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_BCAST;
if ((bp->dev->flags & IFF_PROMISC) && BNXT_PF(bp))
bp->vnic_info[0].rx_mask |=
@@ -4026,20 +4039,42 @@ static int bnxt_set_real_num_queues(struct bnxt *bp)
return rc;
#ifdef CONFIG_RFS_ACCEL
- if (bp->rx_nr_rings)
+ if (bp->flags & BNXT_FLAG_RFS)
dev->rx_cpu_rmap = alloc_irq_cpu_rmap(bp->rx_nr_rings);
- if (!dev->rx_cpu_rmap)
- rc = -ENOMEM;
#endif
return rc;
}
+static int bnxt_trim_rings(struct bnxt *bp, int *rx, int *tx, int max,
+ bool shared)
+{
+ int _rx = *rx, _tx = *tx;
+
+ if (shared) {
+ *rx = min_t(int, _rx, max);
+ *tx = min_t(int, _tx, max);
+ } else {
+ if (max < 2)
+ return -ENOMEM;
+
+ while (_rx + _tx > max) {
+ if (_rx > _tx && _rx > 1)
+ _rx--;
+ else if (_tx > 1)
+ _tx--;
+ }
+ *rx = _rx;
+ *tx = _tx;
+ }
+ return 0;
+}
+
static int bnxt_setup_msix(struct bnxt *bp)
{
struct msix_entry *msix_ent;
struct net_device *dev = bp->dev;
- int i, total_vecs, rc = 0;
+ int i, total_vecs, rc = 0, min = 1;
const int len = sizeof(bp->irq_tbl[0].name);
bp->flags &= ~BNXT_FLAG_USING_MSIX;
@@ -4054,7 +4089,10 @@ static int bnxt_setup_msix(struct bnxt *bp)
msix_ent[i].vector = 0;
}
- total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, 1, total_vecs);
+ if (!(bp->flags & BNXT_FLAG_SHARED_RINGS))
+ min = 2;
+
+ total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, min, total_vecs);
if (total_vecs < 0) {
rc = -ENODEV;
goto msix_setup_exit;
@@ -4065,8 +4103,11 @@ static int bnxt_setup_msix(struct bnxt *bp)
int tcs;
/* Trim rings based upon num of vectors allocated */
- bp->rx_nr_rings = min_t(int, total_vecs, bp->rx_nr_rings);
- bp->tx_nr_rings = min_t(int, total_vecs, bp->tx_nr_rings);
+ rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings,
+ total_vecs, min == 1);
+ if (rc)
+ goto msix_setup_exit;
+
bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
tcs = netdev_get_num_tc(dev);
if (tcs > 1) {
@@ -4085,12 +4126,21 @@ static int bnxt_setup_msix(struct bnxt *bp)
}
}
}
- bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings);
+ bp->cp_nr_rings = total_vecs;
for (i = 0; i < bp->cp_nr_rings; i++) {
+ char *attr;
+
bp->irq_tbl[i].vector = msix_ent[i].vector;
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS)
+ attr = "TxRx";
+ else if (i < bp->rx_nr_rings)
+ attr = "rx";
+ else
+ attr = "tx";
+
snprintf(bp->irq_tbl[i].name, len,
- "%s-%s-%d", dev->name, "TxRx", i);
+ "%s-%s-%d", dev->name, attr, i);
bp->irq_tbl[i].handler = bnxt_msix;
}
rc = bnxt_set_real_num_queues(bp);
@@ -4128,6 +4178,7 @@ static int bnxt_setup_inta(struct bnxt *bp)
bp->tx_nr_rings = 1;
bp->cp_nr_rings = 1;
bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
+ bp->flags |= BNXT_FLAG_SHARED_RINGS;
bp->irq_tbl[0].vector = bp->pdev->irq;
snprintf(bp->irq_tbl[0].name, len,
"%s-%s-%d", bp->dev->name, "TxRx", 0);
@@ -4176,7 +4227,7 @@ static void bnxt_free_irq(struct bnxt *bp)
static int bnxt_request_irq(struct bnxt *bp)
{
- int i, rc = 0;
+ int i, j, rc = 0;
unsigned long flags = 0;
#ifdef CONFIG_RFS_ACCEL
struct cpu_rmap *rmap = bp->dev->rx_cpu_rmap;
@@ -4185,14 +4236,15 @@ static int bnxt_request_irq(struct bnxt *bp)
if (!(bp->flags & BNXT_FLAG_USING_MSIX))
flags = IRQF_SHARED;
- for (i = 0; i < bp->cp_nr_rings; i++) {
+ for (i = 0, j = 0; i < bp->cp_nr_rings; i++) {
struct bnxt_irq *irq = &bp->irq_tbl[i];
#ifdef CONFIG_RFS_ACCEL
- if (rmap && (i < bp->rx_nr_rings)) {
+ if (rmap && bp->bnapi[i]->rx_ring) {
rc = irq_cpu_rmap_add(rmap, irq->vector);
if (rc)
netdev_warn(bp->dev, "failed adding irq rmap for ring %d\n",
- i);
+ j);
+ j++;
}
#endif
rc = request_irq(irq->vector, irq->handler, flags, irq->name,
@@ -4230,12 +4282,10 @@ static void bnxt_init_napi(struct bnxt *bp)
bnapi = bp->bnapi[i];
netif_napi_add(bp->dev, &bnapi->napi,
bnxt_poll, 64);
- napi_hash_add(&bnapi->napi);
}
} else {
bnapi = bp->bnapi[0];
netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll, 64);
- napi_hash_add(&bnapi->napi);
}
}
@@ -4265,14 +4315,12 @@ static void bnxt_enable_napi(struct bnxt *bp)
static void bnxt_tx_disable(struct bnxt *bp)
{
int i;
- struct bnxt_napi *bnapi;
struct bnxt_tx_ring_info *txr;
struct netdev_queue *txq;
- if (bp->bnapi) {
+ if (bp->tx_ring) {
for (i = 0; i < bp->tx_nr_rings; i++) {
- bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
+ txr = &bp->tx_ring[i];
txq = netdev_get_tx_queue(bp->dev, i);
__netif_tx_lock(txq, smp_processor_id());
txr->dev_state = BNXT_DEV_STATE_CLOSING;
@@ -4287,13 +4335,11 @@ static void bnxt_tx_disable(struct bnxt *bp)
static void bnxt_tx_enable(struct bnxt *bp)
{
int i;
- struct bnxt_napi *bnapi;
struct bnxt_tx_ring_info *txr;
struct netdev_queue *txq;
for (i = 0; i < bp->tx_nr_rings; i++) {
- bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
+ txr = &bp->tx_ring[i];
txq = netdev_get_tx_queue(bp->dev, i);
txr->dev_state = 0;
}
@@ -4355,7 +4401,7 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state)
link_info->auto_mode = resp->auto_mode;
link_info->auto_pause_setting = resp->auto_pause;
link_info->force_pause_setting = resp->force_pause;
- link_info->duplex_setting = resp->duplex_setting;
+ link_info->duplex_setting = resp->duplex;
if (link_info->phy_link_status == BNXT_LINK_LINK)
link_info->link_speed = le16_to_cpu(resp->link_speed);
else
@@ -4932,9 +4978,32 @@ skip_uc:
return rc;
}
+static bool bnxt_rfs_capable(struct bnxt *bp)
+{
+#ifdef CONFIG_RFS_ACCEL
+ struct bnxt_pf_info *pf = &bp->pf;
+ int vnics;
+
+ if (BNXT_VF(bp) || !(bp->flags & BNXT_FLAG_MSIX_CAP))
+ return false;
+
+ vnics = 1 + bp->rx_nr_rings;
+ if (vnics > pf->max_rsscos_ctxs || vnics > pf->max_vnics)
+ return false;
+
+ return true;
+#else
+ return false;
+#endif
+}
+
static netdev_features_t bnxt_fix_features(struct net_device *dev,
netdev_features_t features)
{
+ struct bnxt *bp = netdev_priv(dev);
+
+ if (!bnxt_rfs_capable(bp))
+ features &= ~NETIF_F_NTUPLE;
return features;
}
@@ -4975,7 +5044,7 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
bp->flags = flags;
- if (!netif_running(dev)) {
+ if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
if (update_tpa)
bnxt_set_ring_params(bp);
return rc;
@@ -4999,31 +5068,53 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
return rc;
}
+static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi)
+{
+ struct bnxt_tx_ring_info *txr = bnapi->tx_ring;
+ int i = bnapi->index;
+
+ if (!txr)
+ return;
+
+ netdev_info(bnapi->bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n",
+ i, txr->tx_ring_struct.fw_ring_id, txr->tx_prod,
+ txr->tx_cons);
+}
+
+static void bnxt_dump_rx_sw_state(struct bnxt_napi *bnapi)
+{
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
+ int i = bnapi->index;
+
+ if (!rxr)
+ return;
+
+ netdev_info(bnapi->bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n",
+ i, rxr->rx_ring_struct.fw_ring_id, rxr->rx_prod,
+ rxr->rx_agg_ring_struct.fw_ring_id, rxr->rx_agg_prod,
+ rxr->rx_sw_agg_prod);
+}
+
+static void bnxt_dump_cp_sw_state(struct bnxt_napi *bnapi)
+{
+ struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+ int i = bnapi->index;
+
+ netdev_info(bnapi->bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n",
+ i, cpr->cp_ring_struct.fw_ring_id, cpr->cp_raw_cons);
+}
+
static void bnxt_dbg_dump_states(struct bnxt *bp)
{
int i;
struct bnxt_napi *bnapi;
- struct bnxt_tx_ring_info *txr;
- struct bnxt_rx_ring_info *rxr;
- struct bnxt_cp_ring_info *cpr;
for (i = 0; i < bp->cp_nr_rings; i++) {
bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
- rxr = &bnapi->rx_ring;
- cpr = &bnapi->cp_ring;
if (netif_msg_drv(bp)) {
- netdev_info(bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n",
- i, txr->tx_ring_struct.fw_ring_id,
- txr->tx_prod, txr->tx_cons);
- netdev_info(bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n",
- i, rxr->rx_ring_struct.fw_ring_id,
- rxr->rx_prod,
- rxr->rx_agg_ring_struct.fw_ring_id,
- rxr->rx_agg_prod, rxr->rx_sw_agg_prod);
- netdev_info(bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n",
- i, cpr->cp_ring_struct.fw_ring_id,
- cpr->cp_raw_cons);
+ bnxt_dump_tx_sw_state(bnapi);
+ bnxt_dump_rx_sw_state(bnapi);
+ bnxt_dump_cp_sw_state(bnapi);
}
}
}
@@ -5294,10 +5385,14 @@ static int bnxt_setup_tc(struct net_device *dev, u8 tc)
return 0;
if (tc) {
- int max_rx_rings, max_tx_rings;
+ int max_rx_rings, max_tx_rings, rc;
+ bool sh = false;
+
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS)
+ sh = true;
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
- if (bp->tx_nr_rings_per_tc * tc > max_tx_rings)
+ rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh);
+ if (rc || bp->tx_nr_rings_per_tc * tc > max_tx_rings)
return -ENOMEM;
}
@@ -5551,6 +5646,7 @@ static void bnxt_remove_one(struct pci_dev *pdev)
cancel_work_sync(&bp->sp_task);
bp->sp_event = 0;
+ bnxt_hwrm_func_drv_unrgtr(bp);
bnxt_free_hwrm_resources(bp);
pci_iounmap(pdev, bp->bar2);
pci_iounmap(pdev, bp->bar1);
@@ -5610,28 +5706,64 @@ static int bnxt_get_max_irq(struct pci_dev *pdev)
return (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
}
-void bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx)
+static void _bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx,
+ int *max_cp)
{
- int max_rings = 0;
+ int max_ring_grps = 0;
- if (BNXT_PF(bp)) {
- *max_tx = bp->pf.max_pf_tx_rings;
- *max_rx = bp->pf.max_pf_rx_rings;
- max_rings = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings);
- max_rings = min_t(int, max_rings, bp->pf.max_stat_ctxs);
- } else {
#ifdef CONFIG_BNXT_SRIOV
+ if (!BNXT_PF(bp)) {
*max_tx = bp->vf.max_tx_rings;
*max_rx = bp->vf.max_rx_rings;
- max_rings = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings);
- max_rings = min_t(int, max_rings, bp->vf.max_stat_ctxs);
+ *max_cp = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings);
+ *max_cp = min_t(int, *max_cp, bp->vf.max_stat_ctxs);
+ max_ring_grps = bp->vf.max_hw_ring_grps;
+ } else
#endif
+ {
+ *max_tx = bp->pf.max_tx_rings;
+ *max_rx = bp->pf.max_rx_rings;
+ *max_cp = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings);
+ *max_cp = min_t(int, *max_cp, bp->pf.max_stat_ctxs);
+ max_ring_grps = bp->pf.max_hw_ring_grps;
}
+
if (bp->flags & BNXT_FLAG_AGG_RINGS)
*max_rx >>= 1;
+ *max_rx = min_t(int, *max_rx, max_ring_grps);
+}
+
+int bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx, bool shared)
+{
+ int rx, tx, cp;
+
+ _bnxt_get_max_rings(bp, &rx, &tx, &cp);
+ if (!rx || !tx || !cp)
+ return -ENOMEM;
- *max_rx = min_t(int, *max_rx, max_rings);
- *max_tx = min_t(int, *max_tx, max_rings);
+ *max_rx = rx;
+ *max_tx = tx;
+ return bnxt_trim_rings(bp, max_rx, max_tx, cp, shared);
+}
+
+static int bnxt_set_dflt_rings(struct bnxt *bp)
+{
+ int dflt_rings, max_rx_rings, max_tx_rings, rc;
+ bool sh = true;
+
+ if (sh)
+ bp->flags |= BNXT_FLAG_SHARED_RINGS;
+ dflt_rings = netif_get_num_default_rss_queues();
+ rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh);
+ if (rc)
+ return rc;
+ bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings);
+ bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings);
+ bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
+ bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) :
+ bp->tx_nr_rings + bp->rx_nr_rings;
+ bp->num_stat_ctxs = bp->cp_nr_rings;
+ return rc;
}
static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -5639,7 +5771,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
static int version_printed;
struct net_device *dev;
struct bnxt *bp;
- int rc, max_rx_rings, max_tx_rings, max_irqs, dflt_rings;
+ int rc, max_irqs;
if (version_printed++ == 0)
pr_info("%s", version);
@@ -5654,11 +5786,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (bnxt_vf_pciid(ent->driver_data))
bp->flags |= BNXT_FLAG_VF;
- if (pdev->msix_cap) {
+ if (pdev->msix_cap)
bp->flags |= BNXT_FLAG_MSIX_CAP;
- if (BNXT_PF(bp))
- bp->flags |= BNXT_FLAG_RFS;
- }
rc = bnxt_init_board(pdev, dev);
if (rc < 0)
@@ -5677,9 +5806,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
NETIF_F_RXHASH |
NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO;
- if (bp->flags & BNXT_FLAG_RFS)
- dev->hw_features |= NETIF_F_NTUPLE;
-
dev->hw_enc_features =
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
NETIF_F_TSO | NETIF_F_TSO6 |
@@ -5724,19 +5850,21 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
bnxt_set_tpa_flags(bp);
bnxt_set_ring_params(bp);
- dflt_rings = netif_get_num_default_rss_queues();
if (BNXT_PF(bp))
bp->pf.max_irqs = max_irqs;
#if defined(CONFIG_BNXT_SRIOV)
else
bp->vf.max_irqs = max_irqs;
#endif
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
- bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings);
- bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings);
- bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
- bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings);
- bp->num_stat_ctxs = bp->cp_nr_rings;
+ bnxt_set_dflt_rings(bp);
+
+ if (BNXT_PF(bp)) {
+ dev->hw_features |= NETIF_F_NTUPLE;
+ if (bnxt_rfs_capable(bp)) {
+ bp->flags |= BNXT_FLAG_RFS;
+ dev->features |= NETIF_F_NTUPLE;
+ }
+ }
if (dev->hw_features & NETIF_F_HW_VLAN_CTAG_RX)
bp->flags |= BNXT_FLAG_STRIP_VLAN;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index f199f4cc8ffe..8af3ca8efcef 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -11,11 +11,11 @@
#define BNXT_H
#define DRV_MODULE_NAME "bnxt_en"
-#define DRV_MODULE_VERSION "0.1.24"
+#define DRV_MODULE_VERSION "1.0.0"
-#define DRV_VER_MAJ 0
-#define DRV_VER_MIN 1
-#define DRV_VER_UPD 24
+#define DRV_VER_MAJ 1
+#define DRV_VER_MIN 0
+#define DRV_VER_UPD 0
struct tx_bd {
__le32 tx_bd_len_flags_type;
@@ -528,6 +528,7 @@ struct tx_push_bd {
};
struct bnxt_tx_ring_info {
+ struct bnxt_napi *bnapi;
u16 tx_prod;
u16 tx_cons;
void __iomem *tx_doorbell;
@@ -558,6 +559,7 @@ struct bnxt_tpa_info {
};
struct bnxt_rx_ring_info {
+ struct bnxt_napi *bnapi;
u16 rx_prod;
u16 rx_agg_prod;
u16 rx_sw_agg_prod;
@@ -604,8 +606,8 @@ struct bnxt_napi {
int index;
struct bnxt_cp_ring_info cp_ring;
- struct bnxt_rx_ring_info rx_ring;
- struct bnxt_tx_ring_info tx_ring;
+ struct bnxt_rx_ring_info *rx_ring;
+ struct bnxt_tx_ring_info *tx_ring;
#ifdef CONFIG_NET_RX_BUSY_POLL
atomic_t poll_state;
@@ -695,6 +697,7 @@ struct bnxt_vf_info {
u16 max_cp_rings;
u16 max_tx_rings;
u16 max_rx_rings;
+ u16 max_hw_ring_grps;
u16 max_l2_ctxs;
u16 max_irqs;
u16 max_vnics;
@@ -722,9 +725,8 @@ struct bnxt_pf_info {
u16 max_rsscos_ctxs;
u16 max_cp_rings;
u16 max_tx_rings; /* HW assigned max tx rings for this PF */
- u16 max_pf_tx_rings; /* runtime max tx rings owned by PF */
u16 max_rx_rings; /* HW assigned max rx rings for this PF */
- u16 max_pf_rx_rings; /* runtime max rx rings owned by PF */
+ u16 max_hw_ring_grps;
u16 max_irqs;
u16 max_l2_ctxs;
u16 max_vnics;
@@ -875,6 +877,8 @@ struct bnxt {
#define BNXT_FLAG_USING_MSIX 0x40
#define BNXT_FLAG_MSIX_CAP 0x80
#define BNXT_FLAG_RFS 0x100
+ #define BNXT_FLAG_SHARED_RINGS 0x200
+
#define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \
BNXT_FLAG_RFS | \
BNXT_FLAG_STRIP_VLAN)
@@ -884,6 +888,9 @@ struct bnxt {
struct bnxt_napi **bnapi;
+ struct bnxt_rx_ring_info *rx_ring;
+ struct bnxt_tx_ring_info *tx_ring;
+
u32 rx_buf_size;
u32 rx_buf_use_size; /* useable size */
u32 rx_ring_size;
@@ -913,6 +920,8 @@ struct bnxt {
int cp_nr_rings;
int num_stat_ctxs;
+
+ /* grp_info indexed by completion ring index */
struct bnxt_ring_grp_info *grp_info;
struct bnxt_vnic_info *vnic_info;
int nr_vnics;
@@ -1084,9 +1093,10 @@ void bnxt_hwrm_cmd_hdr_init(struct bnxt *, void *, u16, u16, u16);
int _hwrm_send_message(struct bnxt *, void *, u32, int);
int hwrm_send_message(struct bnxt *, void *, u32, int);
int bnxt_hwrm_set_coal(struct bnxt *);
+int bnxt_hwrm_func_qcaps(struct bnxt *);
int bnxt_hwrm_set_pause(struct bnxt *);
int bnxt_hwrm_set_link_setting(struct bnxt *, bool);
int bnxt_open_nic(struct bnxt *, bool, bool);
int bnxt_close_nic(struct bnxt *, bool, bool);
-void bnxt_get_max_rings(struct bnxt *, int *, int *);
+int bnxt_get_max_rings(struct bnxt *, int *, int *, bool);
#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index 45bd628eaf3a..922b898e7a32 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -211,7 +211,10 @@ static void bnxt_get_channels(struct net_device *dev,
struct bnxt *bp = netdev_priv(dev);
int max_rx_rings, max_tx_rings, tcs;
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+ bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true);
+ channel->max_combined = max_rx_rings;
+
+ bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false);
tcs = netdev_get_num_tc(dev);
if (tcs > 1)
max_tx_rings /= tcs;
@@ -219,9 +222,12 @@ static void bnxt_get_channels(struct net_device *dev,
channel->max_rx = max_rx_rings;
channel->max_tx = max_tx_rings;
channel->max_other = 0;
- channel->max_combined = 0;
- channel->rx_count = bp->rx_nr_rings;
- channel->tx_count = bp->tx_nr_rings_per_tc;
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS) {
+ channel->combined_count = bp->rx_nr_rings;
+ } else {
+ channel->rx_count = bp->rx_nr_rings;
+ channel->tx_count = bp->tx_nr_rings_per_tc;
+ }
}
static int bnxt_set_channels(struct net_device *dev,
@@ -230,19 +236,35 @@ static int bnxt_set_channels(struct net_device *dev,
struct bnxt *bp = netdev_priv(dev);
int max_rx_rings, max_tx_rings, tcs;
u32 rc = 0;
+ bool sh = false;
+
+ if (channel->other_count)
+ return -EINVAL;
+
+ if (!channel->combined_count &&
+ (!channel->rx_count || !channel->tx_count))
+ return -EINVAL;
- if (channel->other_count || channel->combined_count ||
- !channel->rx_count || !channel->tx_count)
+ if (channel->combined_count &&
+ (channel->rx_count || channel->tx_count))
return -EINVAL;
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+ if (channel->combined_count)
+ sh = true;
+
+ bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh);
+
tcs = netdev_get_num_tc(dev);
if (tcs > 1)
max_tx_rings /= tcs;
- if (channel->rx_count > max_rx_rings ||
- channel->tx_count > max_tx_rings)
- return -EINVAL;
+ if (sh && (channel->combined_count > max_rx_rings ||
+ channel->combined_count > max_tx_rings))
+ return -ENOMEM;
+
+ if (!sh && (channel->rx_count > max_rx_rings ||
+ channel->tx_count > max_tx_rings))
+ return -ENOMEM;
if (netif_running(dev)) {
if (BNXT_PF(bp)) {
@@ -258,14 +280,27 @@ static int bnxt_set_channels(struct net_device *dev,
}
}
- bp->rx_nr_rings = channel->rx_count;
- bp->tx_nr_rings_per_tc = channel->tx_count;
+ if (sh) {
+ bp->flags |= BNXT_FLAG_SHARED_RINGS;
+ bp->rx_nr_rings = channel->combined_count;
+ bp->tx_nr_rings_per_tc = channel->combined_count;
+ } else {
+ bp->flags &= ~BNXT_FLAG_SHARED_RINGS;
+ bp->rx_nr_rings = channel->rx_count;
+ bp->tx_nr_rings_per_tc = channel->tx_count;
+ }
+
bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
if (tcs > 1)
bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs;
- bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings);
+
+ bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) :
+ bp->tx_nr_rings + bp->rx_nr_rings;
+
bp->num_stat_ctxs = bp->cp_nr_rings;
+ /* After changing number of rx channels, update NTUPLE feature. */
+ netdev_update_features(dev);
if (netif_running(dev)) {
rc = bnxt_open_nic(bp, true, false);
if ((!rc) && BNXT_PF(bp)) {
@@ -802,6 +837,45 @@ static int bnxt_flash_nvram(struct net_device *dev,
return rc;
}
+static int bnxt_firmware_reset(struct net_device *dev,
+ u16 dir_type)
+{
+ struct bnxt *bp = netdev_priv(dev);
+ struct hwrm_fw_reset_input req = {0};
+
+ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_RESET, -1, -1);
+
+ /* TODO: Support ASAP ChiMP self-reset (e.g. upon PF driver unload) */
+ /* TODO: Address self-reset of APE/KONG/BONO/TANG or ungraceful reset */
+ /* (e.g. when firmware isn't already running) */
+ switch (dir_type) {
+ case BNX_DIR_TYPE_CHIMP_PATCH:
+ case BNX_DIR_TYPE_BOOTCODE:
+ case BNX_DIR_TYPE_BOOTCODE_2:
+ req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT;
+ /* Self-reset ChiMP upon next PCIe reset: */
+ req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST;
+ break;
+ case BNX_DIR_TYPE_APE_FW:
+ case BNX_DIR_TYPE_APE_PATCH:
+ req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT;
+ break;
+ case BNX_DIR_TYPE_KONG_FW:
+ case BNX_DIR_TYPE_KONG_PATCH:
+ req.embedded_proc_type =
+ FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL;
+ break;
+ case BNX_DIR_TYPE_BONO_FW:
+ case BNX_DIR_TYPE_BONO_PATCH:
+ req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
static int bnxt_flash_firmware(struct net_device *dev,
u16 dir_type,
const u8 *fw_data,
@@ -818,6 +892,9 @@ static int bnxt_flash_firmware(struct net_device *dev,
case BNX_DIR_TYPE_BOOTCODE_2:
code_type = CODE_BOOT;
break;
+ case BNX_DIR_TYPE_APE_FW:
+ code_type = CODE_MCTP_PASSTHRU;
+ break;
default:
netdev_err(dev, "Unsupported directory entry type: %u\n",
dir_type);
@@ -856,10 +933,9 @@ static int bnxt_flash_firmware(struct net_device *dev,
/* TODO: Validate digital signature (RSA-encrypted SHA-256 hash) here */
rc = bnxt_flash_nvram(dev, dir_type, BNX_DIR_ORDINAL_FIRST,
0, 0, fw_data, fw_size);
- if (rc == 0) { /* Firmware update successful */
- /* TODO: Notify processor it needs to reset itself
- */
- }
+ if (rc == 0) /* Firmware update successful */
+ rc = bnxt_firmware_reset(dev, dir_type);
+
return rc;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
index 70fc8253c07f..4badbedcb421 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
@@ -103,19 +103,22 @@ struct hwrm_async_event_cmpl {
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE (0x2UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_DCB_CONFIG_CHANGE (0x3UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED (0x4UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_NOT_ALLOWED (0x5UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_UNLOAD (0x10UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_LOAD (0x11UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD (0x20UL << 0)
- #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD (0x20UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD (0x21UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_FLR (0x30UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_MAC_ADDR_CHANGE (0x31UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_VF_COMM_STATUS_CHANGE (0x32UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR (0xffUL << 0)
__le32 event_data2;
u8 opaque_v;
#define HWRM_ASYNC_EVENT_CMPL_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
};
@@ -132,9 +135,16 @@ struct hwrm_async_event_cmpl_link_status_change {
#define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
- #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_UP 0x1UL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE 0x1UL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_DOWN (0x0UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_UP (0x1UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_MASK 0xeUL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_SFT 1
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0UL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_SFT 4
};
/* HWRM Asynchronous Event Completion Record for link MTU change (16 bytes) */
@@ -150,7 +160,8 @@ struct hwrm_async_event_cmpl_link_mtu_change {
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_SFT 0
@@ -169,7 +180,8 @@ struct hwrm_async_event_cmpl_link_speed_change {
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_FORCE 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_MASK 0xfffeUL
@@ -200,7 +212,8 @@ struct hwrm_async_event_cmpl_dcb_config_change {
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0
@@ -219,7 +232,8 @@ struct hwrm_async_event_cmpl_port_conn_not_allowed {
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0
@@ -238,7 +252,8 @@ struct hwrm_async_event_cmpl_func_drvr_unload {
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0
@@ -257,7 +272,8 @@ struct hwrm_async_event_cmpl_func_drvr_load {
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0
@@ -276,10 +292,13 @@ struct hwrm_async_event_cmpl_pf_drvr_unload {
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_MASK 0x70000UL
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_SFT 16
};
/* HWRM Asynchronous Event Completion Record for PF Driver load (16 bytes) */
@@ -289,16 +308,19 @@ struct hwrm_async_event_cmpl_pf_drvr_load {
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_SFT 0
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
__le16 event_id;
- #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD (0x20UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD (0x21UL << 0)
__le32 event_data2;
u8 opaque_v;
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_MASK 0x70000UL
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_SFT 16
};
/* HWRM Asynchronous Event Completion Record for VF FLR (16 bytes) */
@@ -314,7 +336,8 @@ struct hwrm_async_event_cmpl_vf_flr {
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_SFT 0
@@ -333,7 +356,8 @@ struct hwrm_async_event_cmpl_vf_mac_addr_change {
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_SFT 0
@@ -357,18 +381,20 @@ struct hwrm_async_event_cmpl_hwrm_error {
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA1_TIMESTAMP 0x1UL
};
-/* HW Resource Manager Specification 0.7.8 */
-#define HWRM_VERSION_MAJOR 0
-#define HWRM_VERSION_MINOR 7
-#define HWRM_VERSION_UPDATE 8
+/* HW Resource Manager Specification 1.0.0 */
+#define HWRM_VERSION_MAJOR 1
+#define HWRM_VERSION_MINOR 0
+#define HWRM_VERSION_UPDATE 0
-#define HWRM_VERSION_STR "0.7.8"
-/* Following is the signature for HWRM message field that indicates not
+#define HWRM_VERSION_STR "1.0.0"
+/*
+ * Following is the signature for HWRM message field that indicates not
* applicable (All F's). Need to cast it the size of the field if needed.
*/
#define HWRM_NA_SIGNATURE ((__le32)(-1))
@@ -398,7 +424,9 @@ struct output {
struct cmd_nums {
__le16 req_type;
#define HWRM_VER_GET (0x0UL)
- #define HWRM_FUNC_DISABLE (0x10UL)
+ #define HWRM_FUNC_BUF_UNRGTR (0xeUL)
+ #define HWRM_FUNC_VF_CFG (0xfUL)
+ #define RESERVED1 (0x10UL)
#define HWRM_FUNC_RESET (0x11UL)
#define HWRM_FUNC_GETFID (0x12UL)
#define HWRM_FUNC_VF_ALLOC (0x13UL)
@@ -414,10 +442,9 @@ struct cmd_nums {
#define HWRM_FUNC_DRV_RGTR (0x1dUL)
#define HWRM_FUNC_DRV_QVER (0x1eUL)
#define HWRM_FUNC_BUF_RGTR (0x1fUL)
- #define HWRM_FUNC_VF_CFG (0x20UL)
#define HWRM_PORT_PHY_CFG (0x20UL)
#define HWRM_PORT_MAC_CFG (0x21UL)
- #define HWRM_PORT_ENABLE (0x22UL)
+ #define RESERVED2 (0x22UL)
#define HWRM_PORT_QSTATS (0x23UL)
#define HWRM_PORT_LPBK_QSTATS (0x24UL)
#define HWRM_PORT_CLR_STATS (0x25UL)
@@ -455,13 +482,11 @@ struct cmd_nums {
#define HWRM_RING_GRP_FREE (0x61UL)
#define HWRM_VNIC_RSS_COS_LB_CTX_ALLOC (0x70UL)
#define HWRM_VNIC_RSS_COS_LB_CTX_FREE (0x71UL)
- #define HWRM_ARB_GRP_ALLOC (0x80UL)
- #define HWRM_ARB_GRP_CFG (0x81UL)
#define HWRM_CFA_L2_FILTER_ALLOC (0x90UL)
#define HWRM_CFA_L2_FILTER_FREE (0x91UL)
#define HWRM_CFA_L2_FILTER_CFG (0x92UL)
#define HWRM_CFA_L2_SET_RX_MASK (0x93UL)
- #define HWRM_CFA_L2_SET_BCASTMCAST_MIRRORING (0x94UL)
+ #define RESERVED3 (0x94UL)
#define HWRM_CFA_TUNNEL_FILTER_ALLOC (0x95UL)
#define HWRM_CFA_TUNNEL_FILTER_FREE (0x96UL)
#define HWRM_CFA_ENCAP_RECORD_ALLOC (0x97UL)
@@ -469,6 +494,9 @@ struct cmd_nums {
#define HWRM_CFA_NTUPLE_FILTER_ALLOC (0x99UL)
#define HWRM_CFA_NTUPLE_FILTER_FREE (0x9aUL)
#define HWRM_CFA_NTUPLE_FILTER_CFG (0x9bUL)
+ #define HWRM_CFA_EM_FLOW_ALLOC (0x9cUL)
+ #define HWRM_CFA_EM_FLOW_FREE (0x9dUL)
+ #define HWRM_CFA_EM_FLOW_CFG (0x9eUL)
#define HWRM_TUNNEL_DST_PORT_QUERY (0xa0UL)
#define HWRM_TUNNEL_DST_PORT_ALLOC (0xa1UL)
#define HWRM_TUNNEL_DST_PORT_FREE (0xa2UL)
@@ -483,8 +511,6 @@ struct cmd_nums {
#define HWRM_FWD_RESP (0xd2UL)
#define HWRM_FWD_ASYNC_EVENT_CMPL (0xd3UL)
#define HWRM_TEMP_MONITOR_QUERY (0xe0UL)
- #define HWRM_MGMT_L2_FILTER_ALLOC (0x100UL)
- #define HWRM_MGMT_L2_FILTER_FREE (0x101UL)
#define HWRM_DBG_READ_DIRECT (0xff10UL)
#define HWRM_DBG_READ_INDIRECT (0xff11UL)
#define HWRM_DBG_WRITE_DIRECT (0xff12UL)
@@ -505,7 +531,6 @@ struct cmd_nums {
__le16 unused_0[3];
};
-/* Return Codes (8 bytes) */
struct ret_codes {
__le16 error_code;
#define HWRM_ERR_CODE_SUCCESS (0x0UL)
@@ -529,7 +554,7 @@ struct hwrm_err_output {
__le16 resp_len;
__le32 opaque_0;
__le16 opaque_1;
- u8 opaque_2;
+ u8 cmd_err;
u8 valid;
};
@@ -686,65 +711,38 @@ struct hwrm_ver_get_output {
u8 hwrm_fw_min;
u8 hwrm_fw_bld;
u8 hwrm_fw_rsvd;
- u8 ape_fw_maj;
- u8 ape_fw_min;
- u8 ape_fw_bld;
- u8 ape_fw_rsvd;
- u8 kong_fw_maj;
- u8 kong_fw_min;
- u8 kong_fw_bld;
- u8 kong_fw_rsvd;
- u8 tang_fw_maj;
- u8 tang_fw_min;
- u8 tang_fw_bld;
- u8 tang_fw_rsvd;
- u8 bono_fw_maj;
- u8 bono_fw_min;
- u8 bono_fw_bld;
- u8 bono_fw_rsvd;
+ u8 mgmt_fw_maj;
+ u8 mgmt_fw_min;
+ u8 mgmt_fw_bld;
+ u8 mgmt_fw_rsvd;
+ u8 netctrl_fw_maj;
+ u8 netctrl_fw_min;
+ u8 netctrl_fw_bld;
+ u8 netctrl_fw_rsvd;
+ __le32 reserved1;
+ u8 roce_fw_maj;
+ u8 roce_fw_min;
+ u8 roce_fw_bld;
+ u8 roce_fw_rsvd;
char hwrm_fw_name[16];
- char ape_fw_name[16];
- char kong_fw_name[16];
- char tang_fw_name[16];
- char bono_fw_name[16];
+ char mgmt_fw_name[16];
+ char netctrl_fw_name[16];
+ __le32 reserved2[4];
+ char roce_fw_name[16];
__le16 chip_num;
u8 chip_rev;
u8 chip_metal;
u8 chip_bond_id;
- u8 unused_0;
+ u8 chip_platform_type;
+ #define VER_GET_RESP_CHIP_PLATFORM_TYPE_ASIC (0x0UL << 0)
+ #define VER_GET_RESP_CHIP_PLATFORM_TYPE_FPGA (0x1UL << 0)
+ #define VER_GET_RESP_CHIP_PLATFORM_TYPE_PALLADIUM (0x2UL << 0)
__le16 max_req_win_len;
__le16 max_resp_len;
__le16 def_req_timeout;
+ u8 unused_0;
u8 unused_1;
u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_func_disable */
-/* Input (24 bytes) */
-struct hwrm_func_disable_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 enables;
- #define FUNC_DISABLE_REQ_ENABLES_VF_ID_VALID 0x1UL
- __le16 vf_id;
- __le16 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_func_disable_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
u8 valid;
};
@@ -759,7 +757,12 @@ struct hwrm_func_reset_input {
__le32 enables;
#define FUNC_RESET_REQ_ENABLES_VF_ID_VALID 0x1UL
__le16 vf_id;
- __le16 unused_0;
+ u8 func_reset_level;
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETALL (0x0UL << 0)
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETME (0x1UL << 0)
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETCHILDREN (0x2UL << 0)
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETVF (0x3UL << 0)
+ u8 unused_0;
};
/* Output (16 bytes) */
@@ -861,7 +864,7 @@ struct hwrm_func_vf_free_output {
};
/* hwrm_func_vf_cfg */
-/* Input (24 bytes) */
+/* Input (32 bytes) */
struct hwrm_func_vf_cfg_input {
__le16 req_type;
__le16 cmpl_ring;
@@ -871,8 +874,11 @@ struct hwrm_func_vf_cfg_input {
__le32 enables;
#define FUNC_VF_CFG_REQ_ENABLES_MTU 0x1UL
#define FUNC_VF_CFG_REQ_ENABLES_GUEST_VLAN 0x2UL
+ #define FUNC_VF_CFG_REQ_ENABLES_ASYNC_EVENT_CR 0x4UL
__le16 mtu;
__le16 guest_vlan;
+ __le16 async_event_cr;
+ __le16 unused_0[3];
};
/* Output (16 bytes) */
@@ -944,7 +950,7 @@ struct hwrm_func_cfg_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le16 vf_id;
+ __le16 fid;
u8 unused_0;
u8 unused_1;
__le32 flags;
@@ -1000,10 +1006,6 @@ struct hwrm_func_cfg_input {
#define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_IF_VLANDNE (0x2UL << 0)
#define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0)
u8 allowed_vlan_pris;
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_NOCHECK (0x0UL << 0)
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_VALIDATE_VLAN (0x1UL << 0)
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_IF_VLANDNE (0x2UL << 0)
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0)
u8 evb_mode;
#define FUNC_CFG_REQ_EVB_MODE_NO_EVB (0x0UL << 0)
#define FUNC_CFG_REQ_EVB_MODE_VEB (0x1UL << 0)
@@ -1166,6 +1168,15 @@ struct hwrm_func_drv_rgtr_input {
#define FUNC_DRV_RGTR_REQ_ENABLES_VF_REQ_FWD 0x8UL
#define FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD 0x10UL
__le16 os_type;
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_UNKNOWN (0x0UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_OTHER (0x1UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_MSDOS (0xeUL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_SOLARIS (0x1dUL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_LINUX (0x24UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_FREEBSD (0x2aUL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_ESXI (0x68UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN864 (0x73UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN2012R2 (0x74UL << 0)
u8 ver_maj;
u8 ver_min;
u8 ver_upd;
@@ -1276,9 +1287,7 @@ struct hwrm_func_drv_qver_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 enables;
- #define FUNC_DRV_QVER_REQ_ENABLES_OS_TYPE_VALID 0x1UL
- #define FUNC_DRV_QVER_REQ_ENABLES_VER_VALID 0x2UL
+ __le32 reserved;
__le16 fid;
__le16 unused_0;
};
@@ -1290,6 +1299,15 @@ struct hwrm_func_drv_qver_output {
__le16 seq_id;
__le16 resp_len;
__le16 os_type;
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_UNKNOWN (0x0UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_OTHER (0x1UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_MSDOS (0xeUL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_SOLARIS (0x1dUL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_LINUX (0x24UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_FREEBSD (0x2aUL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_ESXI (0x68UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN864 (0x73UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN2012R2 (0x74UL << 0)
u8 ver_maj;
u8 ver_min;
u8 ver_upd;
@@ -1498,9 +1516,7 @@ struct hwrm_port_phy_qcfg_output {
u8 force_pause;
#define PORT_PHY_QCFG_RESP_FORCE_PAUSE_TX 0x1UL
#define PORT_PHY_QCFG_RESP_FORCE_PAUSE_RX 0x2UL
- u8 duplex_setting;
- #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_HALF (0x0UL << 0)
- #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_FULL (0x1UL << 0)
+ u8 reserved1;
__le32 preemphasis;
u8 phy_maj;
u8 phy_min;
@@ -1601,33 +1617,6 @@ struct hwrm_port_mac_cfg_output {
u8 valid;
};
-/* hwrm_port_enable */
-/* Input (24 bytes) */
-struct hwrm_port_enable_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 flags;
- #define PORT_ENABLE_REQ_FLAGS_FORWARD_TRAFFIC 0x1UL
- __le16 port_id;
- __le16 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_port_enable_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
/* hwrm_port_qstats */
/* Input (40 bytes) */
struct hwrm_port_qstats_input {
@@ -1651,10 +1640,11 @@ struct hwrm_port_qstats_output {
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le32 unused_0;
+ __le16 tx_stat_size;
+ __le16 rx_stat_size;
+ u8 unused_0;
u8 unused_1;
u8 unused_2;
- u8 unused_3;
u8 valid;
};
@@ -1668,7 +1658,7 @@ struct hwrm_port_lpbk_qstats_input {
__le64 resp_addr;
};
-/* Output (64 bytes) */
+/* Output (96 bytes) */
struct hwrm_port_lpbk_qstats_output {
__le16 error_code;
__le16 req_type;
@@ -1680,6 +1670,10 @@ struct hwrm_port_lpbk_qstats_output {
__le64 lpbk_ucast_bytes;
__le64 lpbk_mcast_bytes;
__le64 lpbk_bcast_bytes;
+ __le64 tx_stat_discard;
+ __le64 tx_stat_error;
+ __le64 rx_stat_discard;
+ __le64 rx_stat_error;
__le32 unused_0;
u8 unused_1;
u8 unused_2;
@@ -1884,12 +1878,11 @@ struct hwrm_queue_buffers_cfg_input {
__le32 enables;
#define QUEUE_BUFFERS_CFG_REQ_ENABLES_RESERVED 0x1UL
#define QUEUE_BUFFERS_CFG_REQ_ENABLES_SHARED 0x2UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_GROUP 0x4UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XOFF 0x8UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XON 0x10UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_FULL 0x20UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_NOTFULL 0x40UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_MAX 0x80UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XOFF 0x4UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XON 0x8UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_FULL 0x10UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_NOTFULL 0x20UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_MAX 0x40UL
__le32 queue_id;
__le32 reserved;
__le32 shared;
@@ -1921,15 +1914,15 @@ struct hwrm_queue_pfcenable_cfg_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 enables;
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI0_PFC_ENABLED 0x1UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI1_PFC_ENABLED 0x2UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI2_PFC_ENABLED 0x4UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI3_PFC_ENABLED 0x8UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI4_PFC_ENABLED 0x10UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI5_PFC_ENABLED 0x20UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI6_PFC_ENABLED 0x40UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI7_PFC_ENABLED 0x80UL
+ __le32 flags;
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_ENABLED 0x1UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_ENABLED 0x2UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_ENABLED 0x4UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_ENABLED 0x8UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_ENABLED 0x10UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_ENABLED 0x20UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_ENABLED 0x40UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_ENABLED 0x80UL
__le16 port_id;
__le16 unused_0;
};
@@ -1962,14 +1955,14 @@ struct hwrm_queue_pri2cos_cfg_input {
#define QUEUE_PRI2COS_CFG_REQ_FLAGS_IVLAN 0x2UL
__le32 enables;
u8 port_id;
- u8 pri0_cos;
- u8 pri1_cos;
- u8 pri2_cos;
- u8 pri3_cos;
- u8 pri4_cos;
- u8 pri5_cos;
- u8 pri6_cos;
- u8 pri7_cos;
+ u8 pri0_cos_queue_id;
+ u8 pri1_cos_queue_id;
+ u8 pri2_cos_queue_id;
+ u8 pri3_cos_queue_id;
+ u8 pri4_cos_queue_id;
+ u8 pri5_cos_queue_id;
+ u8 pri6_cos_queue_id;
+ u8 pri7_cos_queue_id;
u8 unused_0[7];
};
@@ -2164,6 +2157,7 @@ struct hwrm_vnic_cfg_input {
__le32 flags;
#define VNIC_CFG_REQ_FLAGS_DEFAULT 0x1UL
#define VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE 0x2UL
+ #define VNIC_CFG_REQ_FLAGS_BD_STALL_MODE 0x4UL
__le32 enables;
#define VNIC_CFG_REQ_ENABLES_DFLT_RING_GRP 0x1UL
#define VNIC_CFG_REQ_ENABLES_RSS_RULE 0x2UL
@@ -2380,18 +2374,16 @@ struct hwrm_ring_alloc_input {
__le16 target_id;
__le64 resp_addr;
__le32 enables;
- #define RING_ALLOC_REQ_ENABLES_ARB_GRP_ID_VALID 0x1UL
- #define RING_ALLOC_REQ_ENABLES_INPUT_NUM_VALID 0x2UL
- #define RING_ALLOC_REQ_ENABLES_WEIGHT_VALID 0x4UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED1 0x1UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED2 0x2UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED3 0x4UL
#define RING_ALLOC_REQ_ENABLES_STAT_CTX_ID_VALID 0x8UL
- #define RING_ALLOC_REQ_ENABLES_MIN_BW_VALID 0x10UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED4 0x10UL
#define RING_ALLOC_REQ_ENABLES_MAX_BW_VALID 0x20UL
u8 ring_type;
#define RING_ALLOC_REQ_RING_TYPE_CMPL (0x0UL << 0)
#define RING_ALLOC_REQ_RING_TYPE_TX (0x1UL << 0)
#define RING_ALLOC_REQ_RING_TYPE_RX (0x2UL << 0)
- #define RING_ALLOC_REQ_RING_TYPE_STATUS (0x3UL << 0)
- #define RING_ALLOC_REQ_RING_TYPE_CMD (0x4UL << 0)
u8 unused_0;
__le16 unused_1;
__le64 page_tbl_addr;
@@ -2406,17 +2398,17 @@ struct hwrm_ring_alloc_input {
__le16 queue_id;
u8 unused_4;
u8 unused_5;
- __le32 arb_grp_id;
- __le16 input_number;
+ __le32 reserved1;
+ __le16 reserved2;
u8 unused_6;
u8 unused_7;
- __le32 weight;
+ __le32 reserved3;
__le32 stat_ctx_id;
- __le32 min_bw;
+ __le32 reserved4;
__le32 max_bw;
u8 int_mode;
#define RING_ALLOC_REQ_INT_MODE_LEGACY (0x0UL << 0)
- #define RING_ALLOC_REQ_INT_MODE_MSI (0x1UL << 0)
+ #define RING_ALLOC_REQ_INT_MODE_RSVD (0x1UL << 0)
#define RING_ALLOC_REQ_INT_MODE_MSIX (0x2UL << 0)
#define RING_ALLOC_REQ_INT_MODE_POLL (0x3UL << 0)
u8 unused_8[3];
@@ -2448,8 +2440,6 @@ struct hwrm_ring_free_input {
#define RING_FREE_REQ_RING_TYPE_CMPL (0x0UL << 0)
#define RING_FREE_REQ_RING_TYPE_TX (0x1UL << 0)
#define RING_FREE_REQ_RING_TYPE_RX (0x2UL << 0)
- #define RING_FREE_REQ_RING_TYPE_STATUS (0x3UL << 0)
- #define RING_FREE_REQ_RING_TYPE_CMD (0x4UL << 0)
u8 unused_0;
__le16 ring_id;
__le32 unused_1;
@@ -2550,8 +2540,6 @@ struct hwrm_ring_reset_input {
#define RING_RESET_REQ_RING_TYPE_CMPL (0x0UL << 0)
#define RING_RESET_REQ_RING_TYPE_TX (0x1UL << 0)
#define RING_RESET_REQ_RING_TYPE_RX (0x2UL << 0)
- #define RING_RESET_REQ_RING_TYPE_STATUS (0x3UL << 0)
- #define RING_RESET_REQ_RING_TYPE_CMD (0x4UL << 0)
u8 unused_0;
__le16 ring_id;
__le32 unused_1;
@@ -2622,61 +2610,6 @@ struct hwrm_ring_grp_free_output {
u8 valid;
};
-/* hwrm_arb_grp_alloc */
-/* Input (24 bytes) */
-struct hwrm_arb_grp_alloc_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le16 input_number;
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_arb_grp_alloc_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le16 arb_grp_id;
- u8 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
-/* hwrm_arb_grp_cfg */
-/* Input (32 bytes) */
-struct hwrm_arb_grp_cfg_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 arb_grp_id;
- __le16 input_number;
- __le16 tx_ring;
- __le32 weight;
- __le32 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_arb_grp_cfg_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
/* hwrm_cfa_l2_filter_alloc */
/* Input (96 bytes) */
struct hwrm_cfa_l2_filter_alloc_input {
@@ -2708,7 +2641,7 @@ struct hwrm_cfa_l2_filter_alloc_input {
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_TYPE 0x1000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_ID 0x2000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE 0x4000UL
- #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID 0x8000UL
+ #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x8000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x10000UL
u8 l2_addr[6];
u8 unused_0;
@@ -2751,7 +2684,7 @@ struct hwrm_cfa_l2_filter_alloc_input {
#define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
#define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_7;
- __le16 dst_vnic_id;
+ __le16 dst_id;
__le16 mirror_vnic_id;
u8 pri_hint;
#define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER (0x0UL << 0)
@@ -2816,10 +2749,11 @@ struct hwrm_cfa_l2_filter_cfg_input {
#define CFA_L2_FILTER_CFG_REQ_FLAGS_PATH_RX (0x1UL << 0)
#define CFA_L2_FILTER_CFG_REQ_FLAGS_DROP 0x2UL
__le32 enables;
- #define CFA_L2_FILTER_CFG_REQ_ENABLES_DST_VNIC_ID_VALID 0x1UL
+ #define CFA_L2_FILTER_CFG_REQ_ENABLES_DST_ID 0x1UL
+ #define CFA_L2_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID 0x2UL
__le64 l2_filter_id;
- __le32 dst_vnic_id;
- __le32 unused_0;
+ __le32 dst_id;
+ __le32 new_mirror_vnic_id;
};
/* Output (16 bytes) */
@@ -2843,9 +2777,9 @@ struct hwrm_cfa_l2_set_rx_mask_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 dflt_vnic_id;
+ __le32 vnic_id;
__le32 mask;
- #define CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST 0x1UL
+ #define CFA_L2_SET_RX_MASK_REQ_MASK_RESERVED 0x1UL
#define CFA_L2_SET_RX_MASK_REQ_MASK_MCAST 0x2UL
#define CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST 0x4UL
#define CFA_L2_SET_RX_MASK_REQ_MASK_BCAST 0x8UL
@@ -2869,46 +2803,6 @@ struct hwrm_cfa_l2_set_rx_mask_output {
u8 valid;
};
-/* hwrm_cfa_l2_set_bcastmcast_mirroring */
-/* Input (32 bytes) */
-struct hwrm_cfa_l2_set_bcastmcast_mirroring_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 dflt_vnic_id;
- __le32 mirroring_flags;
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_MIRRORING 0x1UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_MIRRORING 0x2UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_SRC_KNOCKOUT 0x4UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_SRC_KNOCKOUT 0x8UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_VLAN_ID_VALID 0x10UL
- __le16 vlan_id;
- u8 bcast_domain;
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_PFONLY (0x0UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFS (0x1UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFSVFS (0x2UL << 0)
- u8 mcast_domain;
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_PFONLY (0x0UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFS (0x1UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFSVFS (0x2UL << 0)
- __le32 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_cfa_l2_set_bcastmcast_mirroring_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
/* hwrm_cfa_tunnel_filter_alloc */
/* Input (88 bytes) */
struct hwrm_cfa_tunnel_filter_alloc_input {
@@ -3017,17 +2911,16 @@ struct hwrm_cfa_encap_record_alloc_input {
__le32 encap_data[16];
};
-/* Output (24 bytes) */
+/* Output (16 bytes) */
struct hwrm_cfa_encap_record_alloc_output {
__le16 error_code;
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le64 encap_record_id;
- __le32 unused_0;
+ __le32 encap_record_id;
+ u8 unused_0;
u8 unused_1;
u8 unused_2;
- u8 unused_3;
u8 valid;
};
@@ -3039,7 +2932,8 @@ struct hwrm_cfa_encap_record_free_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le64 encap_record_id;
+ __le32 encap_record_id;
+ __le32 unused_0;
};
/* Output (16 bytes) */
@@ -3083,14 +2977,21 @@ struct hwrm_cfa_ntuple_filter_alloc_input {
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK 0x2000UL
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_PRI_HINT 0x4000UL
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_NTUPLE_FILTER_ID 0x8000UL
- #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID 0x10000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x10000UL
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x20000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR 0x40000UL
__le64 l2_filter_id;
u8 src_macaddr[6];
__be16 ethertype;
- u8 ipaddr_type;
+ u8 ip_addr_type;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_UNKNOWN (0x0UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4 (0x4UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6 (0x6UL << 0)
u8 ip_protocol;
- __le16 dst_vnic_id;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN (0x0UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP (0x6UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP (0x11UL << 0)
+ __le16 dst_id;
__le16 mirror_vnic_id;
u8 tunnel_type;
#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
@@ -3104,6 +3005,11 @@ struct hwrm_cfa_ntuple_filter_alloc_input {
#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 pri_hint;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER (0x0UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_ABOVE (0x1UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_BELOW (0x2UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_HIGHEST (0x3UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LOWEST (0x4UL << 0)
__be32 src_ipaddr[4];
__be32 src_ipaddr_mask[4];
__be32 dst_ipaddr[4];
@@ -3162,11 +3068,11 @@ struct hwrm_cfa_ntuple_filter_cfg_input {
__le16 target_id;
__le64 resp_addr;
__le32 enables;
- #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_DST_VNIC_ID_VALID 0x1UL
- #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID_VALID 0x2UL
+ #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_DST_ID 0x1UL
+ #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID 0x2UL
__le32 unused_0;
__le64 ntuple_filter_id;
- __le32 new_dst_vnic_id;
+ __le32 new_dst_id;
__le32 new_mirror_vnic_id;
};
@@ -3192,16 +3098,8 @@ struct hwrm_tunnel_dst_port_query_input {
__le16 target_id;
__le64 resp_addr;
u8 tunnel_type;
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
#define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0)
#define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_STT (0x7UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_0[7];
};
@@ -3228,16 +3126,8 @@ struct hwrm_tunnel_dst_port_alloc_input {
__le16 target_id;
__le64 resp_addr;
u8 tunnel_type;
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
#define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0)
#define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_STT (0x7UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_0;
__be16 tunnel_dst_port_val;
__le32 unused_1;
@@ -3267,16 +3157,8 @@ struct hwrm_tunnel_dst_port_free_input {
__le16 target_id;
__le64 resp_addr;
u8 tunnel_type;
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
#define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0)
#define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_STT (0x7UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_0;
__le16 tunnel_dst_port_id;
__le32 unused_1;
@@ -3416,68 +3298,145 @@ struct hwrm_stat_ctx_clr_stats_output {
u8 valid;
};
-/* hwrm_mgmt_l2_filter_alloc */
-/* Input (56 bytes) */
-struct hwrm_mgmt_l2_filter_alloc_input {
+/* hwrm_fw_reset */
+/* Input (24 bytes) */
+struct hwrm_fw_reset_input {
__le16 req_type;
__le16 cmpl_ring;
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 flags;
- #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH 0x1UL
- #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_TX (0x0UL << 0)
- #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX (0x1UL << 0)
- __le32 enables;
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDRESS 0x1UL
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_OVLAN 0x2UL
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_IVLAN 0x4UL
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_ACTION_ID 0x8UL
- u8 l2_address[6];
+ u8 embedded_proc_type;
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT (0x0UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT (0x1UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL (0x2UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE (0x3UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_RSVD (0x4UL << 0)
+ u8 selfrst_status;
+ #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
+ #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
+ #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
+ __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fw_reset_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ u8 selfrst_status;
+ #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
+ #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
+ #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
u8 unused_0;
+ __le16 unused_1;
+ u8 unused_2;
+ u8 unused_3;
+ u8 unused_4;
+ u8 valid;
+};
+
+/* hwrm_exec_fwd_resp */
+/* Input (128 bytes) */
+struct hwrm_exec_fwd_resp_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le32 encap_request[26];
+ __le16 encap_resp_target_id;
+ __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_exec_fwd_resp_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ __le32 unused_0;
u8 unused_1;
- u8 l2_address_mask[6];
- __le16 ovlan;
- __le16 ovlan_mask;
- __le16 ivlan;
- __le16 ivlan_mask;
u8 unused_2;
u8 unused_3;
- __le32 action_id;
- u8 action_bypass;
- #define MGMT_L2_FILTER_ALLOC_REQ_ACTION_BYPASS 0x1UL
- u8 unused_5[3];
+ u8 valid;
+};
+
+/* hwrm_reject_fwd_resp */
+/* Input (128 bytes) */
+struct hwrm_reject_fwd_resp_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le32 encap_request[26];
+ __le16 encap_resp_target_id;
+ __le16 unused_0[3];
};
/* Output (16 bytes) */
-struct hwrm_mgmt_l2_filter_alloc_output {
+struct hwrm_reject_fwd_resp_output {
__le16 error_code;
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le16 mgmt_l2_filter_id;
+ __le32 unused_0;
+ u8 unused_1;
+ u8 unused_2;
+ u8 unused_3;
+ u8 valid;
+};
+
+/* hwrm_fwd_resp */
+/* Input (40 bytes) */
+struct hwrm_fwd_resp_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le16 encap_resp_target_id;
+ __le16 encap_resp_cmpl_ring;
+ __le16 encap_resp_len;
u8 unused_0;
u8 unused_1;
+ __le64 encap_resp_addr;
+ __le32 encap_resp[24];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fwd_resp_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ __le32 unused_0;
+ u8 unused_1;
u8 unused_2;
u8 unused_3;
- u8 unused_4;
u8 valid;
};
-/* hwrm_mgmt_l2_filter_free */
-/* Input (24 bytes) */
-struct hwrm_mgmt_l2_filter_free_input {
+/* hwrm_fwd_async_event_cmpl */
+/* Input (32 bytes) */
+struct hwrm_fwd_async_event_cmpl_input {
__le16 req_type;
__le16 cmpl_ring;
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le16 mgmt_l2_filter_id;
- __le16 unused_0[3];
+ __le16 encap_async_event_target_id;
+ u8 unused_0;
+ u8 unused_1;
+ u8 unused_2[3];
+ u8 unused_3;
+ __le32 encap_async_event_cmpl[4];
};
/* Output (16 bytes) */
-struct hwrm_mgmt_l2_filter_free_output {
+struct hwrm_fwd_async_event_cmpl_output {
__le16 error_code;
__le16 req_type;
__le16 seq_id;
@@ -3489,6 +3448,31 @@ struct hwrm_mgmt_l2_filter_free_output {
u8 valid;
};
+/* hwrm_temp_monitor_query */
+/* Input (16 bytes) */
+struct hwrm_temp_monitor_query_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_temp_monitor_query_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ u8 temp;
+ u8 unused_0;
+ __le16 unused_1;
+ u8 unused_2;
+ u8 unused_3;
+ u8 unused_4;
+ u8 valid;
+};
+
/* hwrm_nvm_raw_write_blk */
/* Input (32 bytes) */
struct hwrm_nvm_raw_write_blk_input {
@@ -3621,7 +3605,7 @@ struct hwrm_nvm_get_dir_info_output {
};
/* hwrm_nvm_write */
-/* Input (40 bytes) */
+/* Input (48 bytes) */
struct hwrm_nvm_write_input {
__le16 req_type;
__le16 cmpl_ring;
@@ -3637,6 +3621,8 @@ struct hwrm_nvm_write_input {
__le16 option;
__le16 flags;
#define NVM_WRITE_REQ_FLAGS_KEEP_ORIG_ACTIVE_IMG 0x1UL
+ __le32 dir_item_length;
+ __le32 unused_0;
};
/* Output (16 bytes) */
@@ -3645,10 +3631,9 @@ struct hwrm_nvm_write_output {
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
+ __le32 dir_item_length;
+ __le16 dir_idx;
+ u8 unused_0;
u8 valid;
};
@@ -3833,214 +3818,4 @@ struct hwrm_nvm_verify_update_output {
u8 valid;
};
-/* hwrm_exec_fwd_resp */
-/* Input (120 bytes) */
-struct hwrm_exec_fwd_resp_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 encap_request[24];
- __le16 encap_resp_target_id;
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_exec_fwd_resp_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_reject_fwd_resp */
-/* Input (120 bytes) */
-struct hwrm_reject_fwd_resp_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 encap_request[24];
- __le16 encap_resp_target_id;
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_reject_fwd_resp_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_fwd_resp */
-/* Input (40 bytes) */
-struct hwrm_fwd_resp_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le16 encap_resp_target_id;
- __le16 encap_resp_cmpl_ring;
- __le16 encap_resp_len;
- u8 unused_0;
- u8 unused_1;
- __le64 encap_resp_addr;
- __le32 encap_resp[24];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fwd_resp_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_fwd_async_event_cmpl */
-/* Input (32 bytes) */
-struct hwrm_fwd_async_event_cmpl_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le16 encap_async_event_target_id;
- u8 unused_0;
- u8 unused_1;
- u8 unused_2[3];
- u8 unused_3;
- __le32 encap_async_event_cmpl[4];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fwd_async_event_cmpl_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_fw_reset */
-/* Input (24 bytes) */
-struct hwrm_fw_reset_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- u8 embedded_proc_type;
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIMP (0x0UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_APE (0x1UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_KONG (0x2UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BONO (0x3UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_TANG (0x4UL << 0)
- u8 selfrst_status;
- #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
- #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
- #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fw_reset_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- u8 selfrst_status;
- #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
- #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
- #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
- u8 unused_0;
- __le16 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
-/* hwrm_fw_qstatus */
-/* Input (24 bytes) */
-struct hwrm_fw_qstatus_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- u8 embedded_proc_type;
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_CHIMP (0x0UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_APE (0x1UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_KONG (0x2UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_BONO (0x3UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_TANG (0x4UL << 0)
- u8 unused_0[7];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fw_qstatus_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- u8 selfrst_status;
- #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
- #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
- #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
- u8 unused_0;
- __le16 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
-/* hwrm_temp_monitor_query */
-/* Input (16 bytes) */
-struct hwrm_temp_monitor_query_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
-};
-
-/* Output (16 bytes) */
-struct hwrm_temp_monitor_query_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- u8 temp;
- u8 unused_0;
- __le16 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
index ea044bbcd384..c1cc83d7e38c 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
@@ -64,7 +64,7 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting)
* the spoof check should also include vlan anti-spoofing
*/
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(func_flags);
rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
if (!rc) {
@@ -128,7 +128,7 @@ int bnxt_set_vf_mac(struct net_device *dev, int vf_id, u8 *mac)
memcpy(vf->mac_addr, mac, ETH_ALEN);
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(vf->func_flags);
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_MAC_ADDR);
memcpy(req.dflt_mac_addr, mac, ETH_ALEN);
@@ -159,7 +159,7 @@ int bnxt_set_vf_vlan(struct net_device *dev, int vf_id, u16 vlan_id, u8 qos)
return 0;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(vf->func_flags);
req.dflt_vlan = cpu_to_le16(vlan_tag);
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_VLAN);
@@ -198,7 +198,7 @@ int bnxt_set_vf_bw(struct net_device *dev, int vf_id, int min_tx_rate,
if (min_tx_rate == vf->min_tx_rate && max_tx_rate == vf->max_tx_rate)
return 0;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(vf->func_flags);
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MAX_BW);
req.max_bw = cpu_to_le32(max_tx_rate);
@@ -363,10 +363,11 @@ static int bnxt_hwrm_func_buf_rgtr(struct bnxt *bp)
}
/* only call by PF to reserve resources for VF */
-static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
+static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs)
{
u32 rc = 0, mtu, i;
u16 vf_tx_rings, vf_rx_rings, vf_cp_rings, vf_stat_ctx, vf_vnics;
+ u16 vf_ring_grps;
struct hwrm_func_cfg_input req = {0};
struct bnxt_pf_info *pf = &bp->pf;
@@ -378,18 +379,18 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
* be removed once new HWRM provides HW ring groups capability in
* hwrm_func_qcap.
*/
- vf_cp_rings = min_t(u16, bp->pf.max_cp_rings, bp->pf.max_stat_ctxs);
- vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / *num_vfs;
+ vf_cp_rings = min_t(u16, pf->max_cp_rings, pf->max_stat_ctxs);
+ vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / num_vfs;
/* TODO: restore this logic below once the WA above is removed */
- /* vf_cp_rings = (bp->pf.max_cp_rings - bp->cp_nr_rings) / *num_vfs; */
- vf_stat_ctx = (bp->pf.max_stat_ctxs - bp->num_stat_ctxs) / *num_vfs;
+ /* vf_cp_rings = (pf->max_cp_rings - bp->cp_nr_rings) / num_vfs; */
+ vf_stat_ctx = (pf->max_stat_ctxs - bp->num_stat_ctxs) / num_vfs;
if (bp->flags & BNXT_FLAG_AGG_RINGS)
- vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings * 2) /
- *num_vfs;
+ vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings * 2) /
+ num_vfs;
else
- vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings) /
- *num_vfs;
- vf_tx_rings = (bp->pf.max_tx_rings - bp->tx_nr_rings) / *num_vfs;
+ vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings) / num_vfs;
+ vf_ring_grps = (bp->pf.max_hw_ring_grps - bp->rx_nr_rings) / num_vfs;
+ vf_tx_rings = (pf->max_tx_rings - bp->tx_nr_rings) / num_vfs;
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MTU |
FUNC_CFG_REQ_ENABLES_MRU |
@@ -399,7 +400,8 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS |
FUNC_CFG_REQ_ENABLES_NUM_RX_RINGS |
FUNC_CFG_REQ_ENABLES_NUM_L2_CTXS |
- FUNC_CFG_REQ_ENABLES_NUM_VNICS);
+ FUNC_CFG_REQ_ENABLES_NUM_VNICS |
+ FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS);
mtu = bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
req.mru = cpu_to_le16(mtu);
@@ -409,6 +411,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
req.num_cmpl_rings = cpu_to_le16(vf_cp_rings);
req.num_tx_rings = cpu_to_le16(vf_tx_rings);
req.num_rx_rings = cpu_to_le16(vf_rx_rings);
+ req.num_hw_ring_grps = cpu_to_le16(vf_ring_grps);
req.num_l2_ctxs = cpu_to_le16(4);
vf_vnics = 1;
@@ -417,22 +420,24 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
req.num_stat_ctxs = cpu_to_le16(vf_stat_ctx);
mutex_lock(&bp->hwrm_cmd_lock);
- for (i = 0; i < *num_vfs; i++) {
- req.vf_id = cpu_to_le16(pf->first_vf_id + i);
+ for (i = 0; i < num_vfs; i++) {
+ req.fid = cpu_to_le16(pf->first_vf_id + i);
rc = _hwrm_send_message(bp, &req, sizeof(req),
HWRM_CMD_TIMEOUT);
if (rc)
break;
- bp->pf.active_vfs = i + 1;
- bp->pf.vf[i].fw_fid = le16_to_cpu(req.vf_id);
+ pf->active_vfs = i + 1;
+ pf->vf[i].fw_fid = le16_to_cpu(req.fid);
}
mutex_unlock(&bp->hwrm_cmd_lock);
if (!rc) {
- bp->pf.max_pf_tx_rings = bp->tx_nr_rings;
- if (bp->flags & BNXT_FLAG_AGG_RINGS)
- bp->pf.max_pf_rx_rings = bp->rx_nr_rings * 2;
- else
- bp->pf.max_pf_rx_rings = bp->rx_nr_rings;
+ pf->max_tx_rings -= vf_tx_rings * num_vfs;
+ pf->max_rx_rings -= vf_rx_rings * num_vfs;
+ pf->max_hw_ring_grps -= vf_ring_grps * num_vfs;
+ pf->max_cp_rings -= vf_cp_rings * num_vfs;
+ pf->max_rsscos_ctxs -= num_vfs;
+ pf->max_stat_ctxs -= vf_stat_ctx * num_vfs;
+ pf->max_vnics -= vf_vnics * num_vfs;
}
return rc;
}
@@ -492,7 +497,7 @@ static int bnxt_sriov_enable(struct bnxt *bp, int *num_vfs)
goto err_out1;
/* Reserve resources for VFs */
- rc = bnxt_hwrm_func_cfg(bp, num_vfs);
+ rc = bnxt_hwrm_func_cfg(bp, *num_vfs);
if (rc)
goto err_out2;
@@ -536,8 +541,8 @@ void bnxt_sriov_disable(struct bnxt *bp)
bnxt_free_vf_resources(bp);
bp->pf.active_vfs = 0;
- bp->pf.max_pf_rx_rings = bp->pf.max_rx_rings;
- bp->pf.max_pf_tx_rings = bp->pf.max_tx_rings;
+ /* Reclaim all resources for the PF. */
+ bnxt_hwrm_func_qcaps(bp);
}
int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs)
@@ -595,6 +600,7 @@ static int bnxt_hwrm_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
/* Set the new target id */
req.target_id = cpu_to_le16(vf->fw_fid);
+ req.encap_resp_target_id = cpu_to_le16(vf->fw_fid);
req.encap_resp_len = cpu_to_le16(msg_size);
req.encap_resp_addr = encap_resp_addr;
req.encap_resp_cmpl_ring = encap_resp_cpr;
@@ -629,6 +635,7 @@ static int bnxt_hwrm_fwd_err_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_REJECT_FWD_RESP, -1, -1);
/* Set the new target id */
req.target_id = cpu_to_le16(vf->fw_fid);
+ req.encap_resp_target_id = cpu_to_le16(vf->fw_fid);
memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size);
mutex_lock(&bp->hwrm_cmd_lock);
@@ -660,6 +667,7 @@ static int bnxt_hwrm_exec_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_EXEC_FWD_RESP, -1, -1);
/* Set the new target id */
req.target_id = cpu_to_le16(vf->fw_fid);
+ req.encap_resp_target_id = cpu_to_le16(vf->fw_fid);
memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size);
mutex_lock(&bp->hwrm_cmd_lock);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 17f017ab4dac..b15a60d787c7 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -2041,11 +2041,11 @@ static void bcmgenet_init_tx_napi(struct bcmgenet_priv *priv)
for (i = 0; i < priv->hw_params->tx_queues; ++i) {
ring = &priv->tx_rings[i];
- netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
+ netif_tx_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
}
ring = &priv->tx_rings[DESC_INDEX];
- netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
+ netif_tx_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
}
static void bcmgenet_enable_tx_napi(struct bcmgenet_priv *priv)
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 8bdfe53754ba..0d775964b060 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -401,9 +401,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
* Ethernet MAC ISRs
*/
if (priv->internal_phy)
- priv->mii_bus->irq[phydev->addr] = PHY_IGNORE_INTERRUPT;
- else
- priv->mii_bus->irq[phydev->addr] = PHY_POLL;
+ priv->mii_bus->irq[phydev->mdio.addr] = PHY_IGNORE_INTERRUPT;
return 0;
}
@@ -477,12 +475,6 @@ static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d",
priv->pdev->name, priv->pdev->id);
- bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!bus->irq) {
- mdiobus_free(priv->mii_bus);
- return -ENOMEM;
- }
-
return 0;
}
@@ -581,7 +573,7 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
}
if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
- phydev = mdio->phy_map[pd->phy_address];
+ phydev = mdiobus_get_phy(mdio, pd->phy_address);
else
phydev = phy_find_first(mdio);
@@ -648,7 +640,6 @@ int bcmgenet_mii_init(struct net_device *dev)
out:
of_node_put(priv->phy_dn);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
return ret;
}
@@ -659,6 +650,5 @@ void bcmgenet_mii_exit(struct net_device *dev)
of_node_put(priv->phy_dn);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
}
diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c
index f557a2aaec23..eacc559679bf 100644
--- a/drivers/net/ethernet/broadcom/sb1250-mac.c
+++ b/drivers/net/ethernet/broadcom/sb1250-mac.c
@@ -238,7 +238,6 @@ struct sbmac_softc {
struct napi_struct napi;
struct phy_device *phy_dev; /* the associated PHY device */
struct mii_bus *mii_bus; /* the MII bus */
- int phy_irq[PHY_MAX_ADDR];
spinlock_t sbm_lock; /* spin lock */
int sbm_devflags; /* current device flags */
@@ -2250,9 +2249,6 @@ static int sbmac_init(struct platform_device *pldev, long long base)
sc->mii_bus->priv = sc;
sc->mii_bus->read = sbmac_mii_read;
sc->mii_bus->write = sbmac_mii_write;
- sc->mii_bus->irq = sc->phy_irq;
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- sc->mii_bus->irq[i] = SBMAC_PHY_INT;
sc->mii_bus->parent = &pldev->dev;
/*
@@ -2358,20 +2354,15 @@ static int sbmac_mii_probe(struct net_device *dev)
{
struct sbmac_softc *sc = netdev_priv(dev);
struct phy_device *phy_dev;
- int i;
- for (i = 0; i < PHY_MAX_ADDR; i++) {
- phy_dev = sc->mii_bus->phy_map[i];
- if (phy_dev)
- break;
- }
+ phy_dev = phy_find_first(sc->mii_bus);
if (!phy_dev) {
printk(KERN_ERR "%s: no PHY found\n", dev->name);
return -ENXIO;
}
- phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &sbmac_mii_poll,
- PHY_INTERFACE_MODE_GMII);
+ phy_dev = phy_connect(dev, dev_name(&phy_dev->mdio.dev),
+ &sbmac_mii_poll, PHY_INTERFACE_MODE_GMII);
if (IS_ERR(phy_dev)) {
printk(KERN_ERR "%s: could not attach to PHY\n", dev->name);
return PTR_ERR(phy_dev);
@@ -2388,11 +2379,10 @@ static int sbmac_mii_probe(struct net_device *dev)
SUPPORTED_MII |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause;
- phy_dev->advertising = phy_dev->supported;
- pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- dev->name, phy_dev->drv->name,
- dev_name(&phy_dev->dev), phy_dev->irq);
+ phy_attached_info(phy_dev);
+
+ phy_dev->advertising = phy_dev->supported;
sc->phy_dev = phy_dev;
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 79789d8e52da..9293675df7ba 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -1406,7 +1406,7 @@ static void tg3_mdio_config_5785(struct tg3 *tp)
u32 val;
struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
case PHY_ID_BCM50610:
case PHY_ID_BCM50610M:
@@ -1538,10 +1538,6 @@ static int tg3_mdio_init(struct tg3 *tp)
tp->mdio_bus->read = &tg3_mdio_read;
tp->mdio_bus->write = &tg3_mdio_write;
tp->mdio_bus->phy_mask = ~(1 << tp->phy_addr);
- tp->mdio_bus->irq = &tp->mdio_irq[0];
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- tp->mdio_bus->irq[i] = PHY_POLL;
/* The bus registration will look for all the PHYs on the mdio bus.
* Unfortunately, it does not ensure the PHY is powered up before
@@ -1558,7 +1554,7 @@ static int tg3_mdio_init(struct tg3 *tp)
return i;
}
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
if (!phydev || !phydev->drv) {
dev_warn(&tp->pdev->dev, "No PHY devices\n");
@@ -1968,7 +1964,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
u32 old_tx_mode = tp->tx_mode;
if (tg3_flag(tp, USE_PHYLIB))
- autoneg = tp->mdio_bus->phy_map[tp->phy_addr]->autoneg;
+ autoneg = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr)->autoneg;
else
autoneg = tp->link_config.autoneg;
@@ -2004,7 +2000,7 @@ static void tg3_adjust_link(struct net_device *dev)
u8 oldflowctrl, linkmesg = 0;
u32 mac_mode, lcl_adv, rmt_adv;
struct tg3 *tp = netdev_priv(dev);
- struct phy_device *phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ struct phy_device *phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
spin_lock_bh(&tp->lock);
@@ -2093,10 +2089,10 @@ static int tg3_phy_init(struct tg3 *tp)
/* Bring the PHY back to a known state. */
tg3_bmcr_reset(tp);
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
/* Attach the MAC to the PHY. */
- phydev = phy_connect(tp->dev, dev_name(&phydev->dev),
+ phydev = phy_connect(tp->dev, phydev_name(phydev),
tg3_adjust_link, phydev->interface);
if (IS_ERR(phydev)) {
dev_err(&tp->pdev->dev, "Could not attach to PHY\n");
@@ -2120,7 +2116,7 @@ static int tg3_phy_init(struct tg3 *tp)
SUPPORTED_Asym_Pause);
break;
default:
- phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]);
+ phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
return -EINVAL;
}
@@ -2128,6 +2124,8 @@ static int tg3_phy_init(struct tg3 *tp)
phydev->advertising = phydev->supported;
+ phy_attached_info(phydev);
+
return 0;
}
@@ -2138,7 +2136,7 @@ static void tg3_phy_start(struct tg3 *tp)
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER;
@@ -2158,13 +2156,13 @@ static void tg3_phy_stop(struct tg3 *tp)
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return;
- phy_stop(tp->mdio_bus->phy_map[tp->phy_addr]);
+ phy_stop(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
}
static void tg3_phy_fini(struct tg3 *tp)
{
if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
- phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]);
+ phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
tp->phy_flags &= ~TG3_PHYFLG_IS_CONNECTED;
}
}
@@ -4048,7 +4046,7 @@ static int tg3_power_down_prepare(struct tg3 *tp)
struct phy_device *phydev;
u32 phyid, advertising;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER;
@@ -12076,7 +12074,7 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
struct phy_device *phydev;
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
return phy_ethtool_gset(phydev, cmd);
}
@@ -12143,7 +12141,7 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
struct phy_device *phydev;
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
return phy_ethtool_sset(phydev, cmd);
}
@@ -12298,7 +12296,7 @@ static int tg3_nway_reset(struct net_device *dev)
if (tg3_flag(tp, USE_PHYLIB)) {
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- r = phy_start_aneg(tp->mdio_bus->phy_map[tp->phy_addr]);
+ r = phy_start_aneg(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
} else {
u32 bmcr;
@@ -12416,7 +12414,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
u32 newadv;
struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
if (!(phydev->supported & SUPPORTED_Pause) ||
(!(phydev->supported & SUPPORTED_Asym_Pause) &&
@@ -13926,7 +13924,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct phy_device *phydev;
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
return phy_mii_ioctl(phydev, ifr, cmd);
}
@@ -17898,13 +17896,7 @@ static int tg3_init_one(struct pci_dev *pdev,
tg3_bus_string(tp, str),
dev->dev_addr);
- if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
- struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
- netdev_info(dev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
- } else {
+ if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) {
char *ethtype;
if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 31c9f8295953..3b5e98ecba00 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -3254,7 +3254,6 @@ struct tg3 {
int pcie_readrq;
struct mii_bus *mdio_bus;
- int mdio_irq[PHY_MAX_ADDR];
int old_link;
u8 phy_addr;
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 169059c92f80..c56347536f6b 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -28,6 +29,7 @@
#include <linux/phy.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
@@ -439,12 +441,6 @@ static int macb_mii_init(struct macb *bp)
bp->mii_bus->parent = &bp->dev->dev;
pdata = dev_get_platdata(&bp->pdev->dev);
- bp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (!bp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
dev_set_drvdata(&bp->dev->dev, bp->mii_bus);
np = bp->pdev->dev.of_node;
@@ -469,9 +465,6 @@ static int macb_mii_init(struct macb *bp)
goto err_out_unregister_bus;
}
} else {
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bp->mii_bus->irq[i] = PHY_POLL;
-
if (pdata)
bp->mii_bus->phy_mask = pdata->phy_mask;
@@ -479,7 +472,7 @@ static int macb_mii_init(struct macb *bp)
}
if (err)
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
err = macb_mii_probe(bp->dev);
if (err)
@@ -489,8 +482,6 @@ static int macb_mii_init(struct macb *bp)
err_out_unregister_bus:
mdiobus_unregister(bp->mii_bus);
-err_out_free_mdio_irq:
- kfree(bp->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(bp->mii_bus);
err_out:
@@ -2122,7 +2113,8 @@ static void macb_get_regs(struct net_device *dev, struct ethtool_regs *regs,
regs_buff[10] = macb_tx_dma(&bp->queues[0], tail);
regs_buff[11] = macb_tx_dma(&bp->queues[0], head);
- regs_buff[12] = macb_or_gem_readl(bp, USRIO);
+ if (!(bp->caps & MACB_CAPS_USRIO_DISABLED))
+ regs_buff[12] = macb_or_gem_readl(bp, USRIO);
if (macb_is_gem(bp)) {
regs_buff[13] = gem_readl(bp, DMACFG);
}
@@ -2401,19 +2393,21 @@ static int macb_init(struct platform_device *pdev)
dev->hw_features &= ~NETIF_F_SG;
dev->features = dev->hw_features;
- val = 0;
- if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
- val = GEM_BIT(RGMII);
- else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
- (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
- val = MACB_BIT(RMII);
- else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
- val = MACB_BIT(MII);
+ if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) {
+ val = 0;
+ if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
+ val = GEM_BIT(RGMII);
+ else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
+ (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ val = MACB_BIT(RMII);
+ else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ val = MACB_BIT(MII);
- if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
- val |= MACB_BIT(CLKEN);
+ if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
+ val |= MACB_BIT(CLKEN);
- macb_or_gem_writel(bp, USRIO, val);
+ macb_or_gem_writel(bp, USRIO, val);
+ }
/* Set MII management clock divider */
val = macb_mdc_clk_div(bp);
@@ -2776,6 +2770,11 @@ static const struct macb_config emac_config = {
.init = at91ether_init,
};
+static const struct macb_config np4_config = {
+ .caps = MACB_CAPS_USRIO_DISABLED,
+ .clk_init = macb_clk_init,
+ .init = macb_init,
+};
static const struct macb_config zynqmp_config = {
.caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO,
@@ -2796,6 +2795,7 @@ static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "cdns,at32ap7000-macb" },
{ .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config },
{ .compatible = "cdns,macb" },
+ { .compatible = "cdns,np4-macb", .data = &np4_config },
{ .compatible = "cdns,pc302-gem", .data = &pc302gem_config },
{ .compatible = "cdns,gem", .data = &pc302gem_config },
{ .compatible = "atmel,sama5d2-gem", .data = &sama5d2_config },
@@ -2817,6 +2817,7 @@ static int macb_probe(struct platform_device *pdev)
= macb_clk_init;
int (*init)(struct platform_device *) = macb_init;
struct device_node *np = pdev->dev.of_node;
+ struct device_node *phy_node;
const struct macb_config *macb_config = NULL;
struct clk *pclk, *hclk, *tx_clk;
unsigned int queue_mask, num_queues;
@@ -2904,6 +2905,16 @@ static int macb_probe(struct platform_device *pdev)
else
macb_get_hwaddr(bp);
+ /* Power up the PHY if there is a GPIO reset */
+ phy_node = of_get_next_available_child(np, NULL);
+ if (phy_node) {
+ int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0);
+ if (gpio_is_valid(gpio))
+ bp->reset_gpio = gpio_to_desc(gpio);
+ gpiod_set_value(bp->reset_gpio, GPIOD_OUT_HIGH);
+ }
+ of_node_put(phy_node);
+
err = of_get_phy_mode(np);
if (err < 0) {
pdata = dev_get_platdata(&pdev->dev);
@@ -2937,8 +2948,7 @@ static int macb_probe(struct platform_device *pdev)
dev->base_addr, dev->irq, dev->dev_addr);
phydev = bp->phy_dev;
- netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
@@ -2968,8 +2978,11 @@ static int macb_remove(struct platform_device *pdev)
if (bp->phy_dev)
phy_disconnect(bp->phy_dev);
mdiobus_unregister(bp->mii_bus);
- kfree(bp->mii_bus->irq);
mdiobus_free(bp->mii_bus);
+
+ /* Shutdown the PHY if there is a GPIO reset */
+ gpiod_set_value(bp->reset_gpio, GPIOD_OUT_LOW);
+
unregister_netdev(dev);
clk_disable_unprepare(bp->tx_clk);
clk_disable_unprepare(bp->hclk);
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index d83b0db77821..0d4ecfcd60b7 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -400,6 +400,7 @@
#define MACB_CAPS_USRIO_HAS_CLKEN 0x00000002
#define MACB_CAPS_USRIO_DEFAULT_IS_MII 0x00000004
#define MACB_CAPS_NO_GIGABIT_HALF 0x00000008
+#define MACB_CAPS_USRIO_DISABLED 0x00000010
#define MACB_CAPS_FIFO_MODE 0x10000000
#define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000
#define MACB_CAPS_SG_DISABLED 0x40000000
@@ -829,6 +830,7 @@ struct macb {
unsigned int dma_burst_length;
phy_interface_t phy_interface;
+ struct gpio_desc *reset_gpio;
/* AT91RM9200 transmit */
struct sk_buff *skb; /* holds skb until xmit interrupt completes */
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h
index 39ca6744a4e6..688828865c48 100644
--- a/drivers/net/ethernet/cavium/thunder/nic.h
+++ b/drivers/net/ethernet/cavium/thunder/nic.h
@@ -265,6 +265,7 @@ struct nicvf {
u8 tns_mode:1;
u8 sqs_mode:1;
u8 loopback_supported:1;
+ bool hw_tso;
u16 mtu;
struct queue_set *qs;
#define MAX_SQS_PER_VF_SINGLE_NODE 5
@@ -489,6 +490,11 @@ static inline int nic_get_node_id(struct pci_dev *pdev)
return ((addr >> NIC_NODE_ID_SHIFT) & NIC_NODE_ID_MASK);
}
+static inline bool pass1_silicon(struct pci_dev *pdev)
+{
+ return pdev->revision < 8;
+}
+
int nicvf_set_real_num_queues(struct net_device *netdev,
int tx_queues, int rx_queues);
int nicvf_open(struct net_device *netdev);
diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c
index 5f24d11cb16a..4dded90076c8 100644
--- a/drivers/net/ethernet/cavium/thunder/nic_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nic_main.c
@@ -54,11 +54,6 @@ struct nicpf {
bool irq_allocated[NIC_PF_MSIX_VECTORS];
};
-static inline bool pass1_silicon(struct nicpf *nic)
-{
- return nic->pdev->revision < 8;
-}
-
/* Supported devices */
static const struct pci_device_id nic_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_NIC_PF) },
@@ -122,7 +117,7 @@ static void nic_send_msg_to_vf(struct nicpf *nic, int vf, union nic_mbx *mbx)
* when PF writes to MBOX(1), in next revisions when
* PF writes to MBOX(0)
*/
- if (pass1_silicon(nic)) {
+ if (pass1_silicon(nic->pdev)) {
/* see the comment for nic_reg_write()/nic_reg_read()
* functions above
*/
@@ -397,7 +392,7 @@ static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg)
padd = cpi % 8; /* 3 bits CS out of 6bits DSCP */
/* Leave RSS_SIZE as '0' to disable RSS */
- if (pass1_silicon(nic)) {
+ if (pass1_silicon(nic->pdev)) {
nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3),
(vnic << 24) | (padd << 16) |
(rssi_base + rssi));
@@ -467,7 +462,7 @@ static void nic_config_rss(struct nicpf *nic, struct rss_cfg_msg *cfg)
}
cpi_base = nic->cpi_base[cfg->vf_id];
- if (pass1_silicon(nic))
+ if (pass1_silicon(nic->pdev))
idx_addr = NIC_PF_CPI_0_2047_CFG;
else
idx_addr = NIC_PF_MPI_0_2047_CFG;
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index dde8dc720cd3..c24cb2a86a42 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -525,14 +525,22 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev,
__func__, cqe_tx->sq_qs, cqe_tx->sq_idx,
cqe_tx->sqe_ptr, hdr->subdesc_cnt);
- nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
nicvf_check_cqe_tx_errs(nic, cq, cqe_tx);
skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr];
- /* For TSO offloaded packets only one head SKB needs to be freed */
+ /* For TSO offloaded packets only one SQE will have a valid SKB */
if (skb) {
+ nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
prefetch(skb);
dev_consume_skb_any(skb);
sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
+ } else {
+ /* In case of HW TSO, HW sends a CQE for each segment of a TSO
+ * packet instead of a single CQE for the whole TSO packet
+ * transmitted. Each of this CQE points to the same SQE, so
+ * avoid freeing same SQE multiple times.
+ */
+ if (!nic->hw_tso)
+ nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
}
}
@@ -1549,6 +1557,9 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+ if (!pass1_silicon(nic->pdev))
+ nic->hw_tso = true;
+
netdev->netdev_ops = &nicvf_netdev_ops;
netdev->watchdog_timeo = NICVF_TX_TIMEOUT;
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index 206b6a71a545..d0d1b5490061 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -18,14 +18,6 @@
#include "q_struct.h"
#include "nicvf_queues.h"
-struct rbuf_info {
- struct page *page;
- void *data;
- u64 offset;
-};
-
-#define GET_RBUF_INFO(x) ((struct rbuf_info *)(x - NICVF_RCV_BUF_ALIGN_BYTES))
-
/* Poll a register for a specific value */
static int nicvf_poll_reg(struct nicvf *nic, int qidx,
u64 reg, int bit_pos, int bits, int val)
@@ -86,8 +78,6 @@ static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem)
static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp,
u32 buf_len, u64 **rbuf)
{
- u64 data;
- struct rbuf_info *rinfo;
int order = get_order(buf_len);
/* Check if request can be accomodated in previous allocated page */
@@ -113,46 +103,28 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp,
nic->rb_page_offset = 0;
}
- data = (u64)page_address(nic->rb_page) + nic->rb_page_offset;
-
- /* Align buffer addr to cache line i.e 128 bytes */
- rinfo = (struct rbuf_info *)(data + NICVF_RCV_BUF_ALIGN_LEN(data));
- /* Save page address for reference updation */
- rinfo->page = nic->rb_page;
- /* Store start address for later retrieval */
- rinfo->data = (void *)data;
- /* Store alignment offset */
- rinfo->offset = NICVF_RCV_BUF_ALIGN_LEN(data);
+ *rbuf = (u64 *)((u64)page_address(nic->rb_page) + nic->rb_page_offset);
- data += rinfo->offset;
-
- /* Give next aligned address to hw for DMA */
- *rbuf = (u64 *)(data + NICVF_RCV_BUF_ALIGN_BYTES);
return 0;
}
-/* Retrieve actual buffer start address and build skb for received packet */
+/* Build skb around receive buffer */
static struct sk_buff *nicvf_rb_ptr_to_skb(struct nicvf *nic,
u64 rb_ptr, int len)
{
+ void *data;
struct sk_buff *skb;
- struct rbuf_info *rinfo;
- rb_ptr = (u64)phys_to_virt(rb_ptr);
- /* Get buffer start address and alignment offset */
- rinfo = GET_RBUF_INFO(rb_ptr);
+ data = phys_to_virt(rb_ptr);
/* Now build an skb to give to stack */
- skb = build_skb(rinfo->data, RCV_FRAG_LEN);
+ skb = build_skb(data, RCV_FRAG_LEN);
if (!skb) {
- put_page(rinfo->page);
+ put_page(virt_to_page(data));
return NULL;
}
- /* Set correct skb->data */
- skb_reserve(skb, rinfo->offset + NICVF_RCV_BUF_ALIGN_BYTES);
-
- prefetch((void *)rb_ptr);
+ prefetch(skb->data);
return skb;
}
@@ -196,7 +168,6 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr)
int head, tail;
u64 buf_addr;
struct rbdr_entry_t *desc;
- struct rbuf_info *rinfo;
if (!rbdr)
return;
@@ -212,16 +183,14 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr)
while (head != tail) {
desc = GET_RBDR_DESC(rbdr, head);
buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN;
- rinfo = GET_RBUF_INFO((u64)phys_to_virt(buf_addr));
- put_page(rinfo->page);
+ put_page(virt_to_page(phys_to_virt(buf_addr)));
head++;
head &= (rbdr->dmem.q_len - 1);
}
/* Free SKB of tail desc */
desc = GET_RBDR_DESC(rbdr, tail);
buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN;
- rinfo = GET_RBUF_INFO((u64)phys_to_virt(buf_addr));
- put_page(rinfo->page);
+ put_page(virt_to_page(phys_to_virt(buf_addr)));
/* Free RBDR ring */
nicvf_free_q_desc_mem(nic, &rbdr->dmem);
@@ -330,7 +299,7 @@ static int nicvf_init_cmp_queue(struct nicvf *nic,
return err;
cq->desc = cq->dmem.base;
- cq->thresh = CMP_QUEUE_CQE_THRESH;
+ cq->thresh = pass1_silicon(nic->pdev) ? 0 : CMP_QUEUE_CQE_THRESH;
nic->cq_coalesce_usecs = (CMP_QUEUE_TIMER_THRESH * 0.05) - 1;
return 0;
@@ -956,7 +925,7 @@ static int nicvf_sq_subdesc_required(struct nicvf *nic, struct sk_buff *skb)
{
int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT;
- if (skb_shinfo(skb)->gso_size) {
+ if (skb_shinfo(skb)->gso_size && !nic->hw_tso) {
subdesc_cnt = nicvf_tso_count_subdescs(skb);
return subdesc_cnt;
}
@@ -971,7 +940,7 @@ static int nicvf_sq_subdesc_required(struct nicvf *nic, struct sk_buff *skb)
* First subdescriptor for every send descriptor.
*/
static inline void
-nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
+nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry,
int subdesc_cnt, struct sk_buff *skb, int len)
{
int proto;
@@ -1007,6 +976,15 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
break;
}
}
+
+ if (nic->hw_tso && skb_shinfo(skb)->gso_size) {
+ hdr->tso = 1;
+ hdr->tso_start = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ hdr->tso_max_paysize = skb_shinfo(skb)->gso_size;
+ /* For non-tunneled pkts, point this to L2 ethertype */
+ hdr->inner_l3_offset = skb_network_offset(skb) - 2;
+ nic->drv_stats.tx_tso++;
+ }
}
/* SQ GATHER subdescriptor
@@ -1076,7 +1054,7 @@ static int nicvf_sq_append_tso(struct nicvf *nic, struct snd_queue *sq,
data_left -= size;
tso_build_data(skb, &tso, size);
}
- nicvf_sq_add_hdr_subdesc(sq, hdr_qentry,
+ nicvf_sq_add_hdr_subdesc(nic, sq, hdr_qentry,
seg_subdescs - 1, skb, seg_len);
sq->skbuff[hdr_qentry] = (u64)NULL;
qentry = nicvf_get_nxt_sqentry(sq, qentry);
@@ -1129,11 +1107,12 @@ int nicvf_sq_append_skb(struct nicvf *nic, struct sk_buff *skb)
qentry = nicvf_get_sq_desc(sq, subdesc_cnt);
/* Check if its a TSO packet */
- if (skb_shinfo(skb)->gso_size)
+ if (skb_shinfo(skb)->gso_size && !nic->hw_tso)
return nicvf_sq_append_tso(nic, sq, sq_num, qentry, skb);
/* Add SQ header subdesc */
- nicvf_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, skb, skb->len);
+ nicvf_sq_add_hdr_subdesc(nic, sq, qentry, subdesc_cnt - 1,
+ skb, skb->len);
/* Add SQ gather subdescs */
qentry = nicvf_get_nxt_sqentry(sq, qentry);
@@ -1234,153 +1213,93 @@ struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx)
return skb;
}
-/* Enable interrupt */
-void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx)
+static u64 nicvf_int_type_to_mask(int int_type, int q_idx)
{
u64 reg_val;
- reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S);
-
switch (int_type) {
case NICVF_INTR_CQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
+ reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
break;
case NICVF_INTR_SQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
+ reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
break;
case NICVF_INTR_RBDR:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
+ reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
break;
case NICVF_INTR_PKT_DROP:
- reg_val |= (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
break;
case NICVF_INTR_TCP_TIMER:
- reg_val |= (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
break;
case NICVF_INTR_MBOX:
- reg_val |= (1ULL << NICVF_INTR_MBOX_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT);
break;
case NICVF_INTR_QS_ERR:
- reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_QS_ERR_SHIFT);
break;
default:
- netdev_err(nic->netdev,
- "Failed to enable interrupt: unknown type\n");
- break;
+ reg_val = 0;
}
- nicvf_reg_write(nic, NIC_VF_ENA_W1S, reg_val);
+ return reg_val;
+}
+
+/* Enable interrupt */
+void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx)
+{
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
+
+ if (!mask) {
+ netdev_dbg(nic->netdev,
+ "Failed to enable interrupt: unknown type\n");
+ return;
+ }
+ nicvf_reg_write(nic, NIC_VF_ENA_W1S,
+ nicvf_reg_read(nic, NIC_VF_ENA_W1S) | mask);
}
/* Disable interrupt */
void nicvf_disable_intr(struct nicvf *nic, int int_type, int q_idx)
{
- u64 reg_val = 0;
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
- switch (int_type) {
- case NICVF_INTR_CQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
- break;
- case NICVF_INTR_SQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
- break;
- case NICVF_INTR_RBDR:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
- break;
- case NICVF_INTR_PKT_DROP:
- reg_val |= (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
- break;
- case NICVF_INTR_TCP_TIMER:
- reg_val |= (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
- break;
- case NICVF_INTR_MBOX:
- reg_val |= (1ULL << NICVF_INTR_MBOX_SHIFT);
- break;
- case NICVF_INTR_QS_ERR:
- reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT);
- break;
- default:
- netdev_err(nic->netdev,
+ if (!mask) {
+ netdev_dbg(nic->netdev,
"Failed to disable interrupt: unknown type\n");
- break;
+ return;
}
- nicvf_reg_write(nic, NIC_VF_ENA_W1C, reg_val);
+ nicvf_reg_write(nic, NIC_VF_ENA_W1C, mask);
}
/* Clear interrupt */
void nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx)
{
- u64 reg_val = 0;
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
- switch (int_type) {
- case NICVF_INTR_CQ:
- reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
- break;
- case NICVF_INTR_SQ:
- reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
- break;
- case NICVF_INTR_RBDR:
- reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
- break;
- case NICVF_INTR_PKT_DROP:
- reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
- break;
- case NICVF_INTR_TCP_TIMER:
- reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
- break;
- case NICVF_INTR_MBOX:
- reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT);
- break;
- case NICVF_INTR_QS_ERR:
- reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT);
- break;
- default:
- netdev_err(nic->netdev,
+ if (!mask) {
+ netdev_dbg(nic->netdev,
"Failed to clear interrupt: unknown type\n");
- break;
+ return;
}
- nicvf_reg_write(nic, NIC_VF_INT, reg_val);
+ nicvf_reg_write(nic, NIC_VF_INT, mask);
}
/* Check if interrupt is enabled */
int nicvf_is_intr_enabled(struct nicvf *nic, int int_type, int q_idx)
{
- u64 reg_val;
- u64 mask = 0xff;
-
- reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S);
-
- switch (int_type) {
- case NICVF_INTR_CQ:
- mask = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
- break;
- case NICVF_INTR_SQ:
- mask = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
- break;
- case NICVF_INTR_RBDR:
- mask = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
- break;
- case NICVF_INTR_PKT_DROP:
- mask = NICVF_INTR_PKT_DROP_MASK;
- break;
- case NICVF_INTR_TCP_TIMER:
- mask = NICVF_INTR_TCP_TIMER_MASK;
- break;
- case NICVF_INTR_MBOX:
- mask = NICVF_INTR_MBOX_MASK;
- break;
- case NICVF_INTR_QS_ERR:
- mask = NICVF_INTR_QS_ERR_MASK;
- break;
- default:
- netdev_err(nic->netdev,
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
+ /* If interrupt type is unknown, we treat it disabled. */
+ if (!mask) {
+ netdev_dbg(nic->netdev,
"Failed to check interrupt enable: unknown type\n");
- break;
+ return 0;
}
- return (reg_val & mask);
+ return mask & nicvf_reg_read(nic, NIC_VF_ENA_W1S);
}
void nicvf_update_rq_stats(struct nicvf *nic, int rq_idx)
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
index 033e8306e91c..c5030a7f213a 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
@@ -75,7 +75,7 @@
*/
#define CMP_QSIZE CMP_QUEUE_SIZE2
#define CMP_QUEUE_LEN (1ULL << (CMP_QSIZE + 10))
-#define CMP_QUEUE_CQE_THRESH 0
+#define CMP_QUEUE_CQE_THRESH (NAPI_POLL_WEIGHT / 2)
#define CMP_QUEUE_TIMER_THRESH 80 /* ~2usec */
#define RBDR_SIZE RBDR_SIZE0
@@ -83,10 +83,8 @@
#define MAX_RCV_BUF_COUNT (1ULL << (RBDR_SIZE6 + 13))
#define RBDR_THRESH (RCV_BUF_COUNT / 2)
#define DMA_BUFFER_LEN 2048 /* In multiples of 128bytes */
-#define RCV_FRAG_LEN (SKB_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD) + \
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \
- (NICVF_RCV_BUF_ALIGN_BYTES * 2))
-#define RCV_DATA_OFFSET NICVF_RCV_BUF_ALIGN_BYTES
+#define RCV_FRAG_LEN (SKB_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD) + \
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
#define MAX_CQES_FOR_TX ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) * \
MAX_CQE_PER_PKT_XMIT)
@@ -108,10 +106,6 @@
#define NICVF_SQ_BASE_ALIGN_BYTES 128 /* 7 bits */
#define NICVF_ALIGNED_ADDR(ADDR, ALIGN_BYTES) ALIGN(ADDR, ALIGN_BYTES)
-#define NICVF_ADDR_ALIGN_LEN(ADDR, BYTES)\
- (NICVF_ALIGNED_ADDR(ADDR, BYTES) - BYTES)
-#define NICVF_RCV_BUF_ALIGN_LEN(X)\
- (NICVF_ALIGNED_ADDR(X, NICVF_RCV_BUF_ALIGN_BYTES) - X)
/* Queue enable/disable */
#define NICVF_SQ_EN BIT_ULL(19)
diff --git a/drivers/net/ethernet/cavium/thunder/q_struct.h b/drivers/net/ethernet/cavium/thunder/q_struct.h
index 3c1de97b1add..9e6d9876bfd0 100644
--- a/drivers/net/ethernet/cavium/thunder/q_struct.h
+++ b/drivers/net/ethernet/cavium/thunder/q_struct.h
@@ -545,25 +545,28 @@ struct sq_hdr_subdesc {
u64 subdesc_cnt:8;
u64 csum_l4:2;
u64 csum_l3:1;
- u64 rsvd0:5;
+ u64 csum_inner_l4:2;
+ u64 csum_inner_l3:1;
+ u64 rsvd0:2;
u64 l4_offset:8;
u64 l3_offset:8;
u64 rsvd1:4;
u64 tot_len:20; /* W0 */
- u64 tso_sdc_cont:8;
- u64 tso_sdc_first:8;
- u64 tso_l4_offset:8;
- u64 tso_flags_last:12;
- u64 tso_flags_first:12;
- u64 rsvd2:2;
+ u64 rsvd2:24;
+ u64 inner_l4_offset:8;
+ u64 inner_l3_offset:8;
+ u64 tso_start:8;
+ u64 rsvd3:2;
u64 tso_max_paysize:14; /* W1 */
#elif defined(__LITTLE_ENDIAN_BITFIELD)
u64 tot_len:20;
u64 rsvd1:4;
u64 l3_offset:8;
u64 l4_offset:8;
- u64 rsvd0:5;
+ u64 rsvd0:2;
+ u64 csum_inner_l3:1;
+ u64 csum_inner_l4:2;
u64 csum_l3:1;
u64 csum_l4:2;
u64 subdesc_cnt:8;
@@ -574,12 +577,11 @@ struct sq_hdr_subdesc {
u64 subdesc_type:4; /* W0 */
u64 tso_max_paysize:14;
- u64 rsvd2:2;
- u64 tso_flags_first:12;
- u64 tso_flags_last:12;
- u64 tso_l4_offset:8;
- u64 tso_sdc_first:8;
- u64 tso_sdc_cont:8; /* W1 */
+ u64 rsvd3:2;
+ u64 tso_start:8;
+ u64 inner_l3_offset:8;
+ u64 inner_l4_offset:8;
+ u64 rsvd2:24; /* W1 */
#endif
};
diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig
index a79813a17b6e..4d187f22c48b 100644
--- a/drivers/net/ethernet/chelsio/Kconfig
+++ b/drivers/net/ethernet/chelsio/Kconfig
@@ -65,13 +65,14 @@ config CHELSIO_T3
will be called cxgb3.
config CHELSIO_T4
- tristate "Chelsio Communications T4/T5 Ethernet support"
+ tristate "Chelsio Communications T4/T5/T6 Ethernet support"
depends on PCI && (IPV6 || IPV6=n)
select FW_LOADER
select MDIO
---help---
- This driver supports Chelsio T4 and T5 based gigabit, 10Gb Ethernet
- adapter and T5 based 40Gb Ethernet adapter.
+ This driver supports Chelsio T4, T5 & T6 based gigabit, 10Gb Ethernet
+ adapter and T5/T6 based 40Gb and T6 based 25Gb, 50Gb and 100Gb
+ Ethernet adapters.
For general information about Chelsio and our products, visit
our website at <http://www.chelsio.com>.
@@ -85,7 +86,7 @@ config CHELSIO_T4
will be called cxgb4.
config CHELSIO_T4_DCB
- bool "Data Center Bridging (DCB) Support for Chelsio T4/T5 cards"
+ bool "Data Center Bridging (DCB) Support for Chelsio T4/T5/T6 cards"
default n
depends on CHELSIO_T4 && DCB
---help---
@@ -107,12 +108,12 @@ config CHELSIO_T4_FCOE
If unsure, say N.
config CHELSIO_T4VF
- tristate "Chelsio Communications T4/T5 Virtual Function Ethernet support"
+ tristate "Chelsio Communications T4/T5/T6 Virtual Function Ethernet support"
depends on PCI
---help---
- This driver supports Chelsio T4 and T5 based gigabit, 10Gb Ethernet
- adapters and T5 based 40Gb Ethernet adapters with PCI-E SR-IOV Virtual
- Functions.
+ This driver supports Chelsio T4, T5 & T6 based gigabit, 10Gb Ethernet
+ adapters and T5/T6 based 40Gb and T6 based 25Gb, 50Gb and 100Gb
+ Ethernet adapters with PCI-E SR-IOV Virtual Functions.
For general information about Chelsio and our products, visit
our website at <http://www.chelsio.com>.
diff --git a/drivers/net/ethernet/chelsio/cxgb/cphy.h b/drivers/net/ethernet/chelsio/cxgb/cphy.h
index a4d2a4c08d3f..bf43da6c6a63 100644
--- a/drivers/net/ethernet/chelsio/cxgb/cphy.h
+++ b/drivers/net/ethernet/chelsio/cxgb/cphy.h
@@ -137,7 +137,7 @@ static inline int simple_mdio_write(struct cphy *cphy, int reg,
/* Convenience initializer */
static inline void cphy_init(struct cphy *phy, struct net_device *dev,
- int phy_addr, struct cphy_ops *phy_ops,
+ int phy_addr, const struct cphy_ops *phy_ops,
const struct mdio_ops *mdio_ops)
{
struct adapter *adapter = netdev_priv(dev);
diff --git a/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
index 71018a4fdf15..76ce6e538326 100644
--- a/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
+++ b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
@@ -337,7 +337,7 @@ static void mv88e1xxx_destroy(struct cphy *cphy)
kfree(cphy);
}
-static struct cphy_ops mv88e1xxx_ops = {
+static const struct cphy_ops mv88e1xxx_ops = {
.destroy = mv88e1xxx_destroy,
.reset = mv88e1xxx_reset,
.interrupt_enable = mv88e1xxx_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
index d0cf611551a1..7ddb301bcba0 100644
--- a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
+++ b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
@@ -195,7 +195,7 @@ static void mv88x201x_destroy(struct cphy *cphy)
kfree(cphy);
}
-static struct cphy_ops mv88x201x_ops = {
+static const struct cphy_ops mv88x201x_ops = {
.destroy = mv88x201x_destroy,
.reset = mv88x201x_reset,
.interrupt_enable = mv88x201x_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/my3126.c b/drivers/net/ethernet/chelsio/cxgb/my3126.c
index a683fd3bb624..d546f46c8ef7 100644
--- a/drivers/net/ethernet/chelsio/cxgb/my3126.c
+++ b/drivers/net/ethernet/chelsio/cxgb/my3126.c
@@ -154,7 +154,7 @@ static void my3126_destroy(struct cphy *cphy)
kfree(cphy);
}
-static struct cphy_ops my3126_ops = {
+static const struct cphy_ops my3126_ops = {
.destroy = my3126_destroy,
.reset = my3126_reset,
.interrupt_enable = my3126_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/pm3393.c b/drivers/net/ethernet/chelsio/cxgb/pm3393.c
index ec5e05052d99..eb462d7db427 100644
--- a/drivers/net/ethernet/chelsio/cxgb/pm3393.c
+++ b/drivers/net/ethernet/chelsio/cxgb/pm3393.c
@@ -570,7 +570,7 @@ static void pm3393_destroy(struct cmac *cmac)
kfree(cmac);
}
-static struct cmac_ops pm3393_ops = {
+static const struct cmac_ops pm3393_ops = {
.destroy = pm3393_destroy,
.reset = pm3393_reset,
.interrupt_enable = pm3393_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
index b0cb388f5e12..6f30b6f78553 100644
--- a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
+++ b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
@@ -666,7 +666,7 @@ static void mac_destroy(struct cmac *mac)
kfree(mac);
}
-static struct cmac_ops vsc7326_ops = {
+static const struct cmac_ops vsc7326_ops = {
.destroy = mac_destroy,
.reset = mac_reset,
.interrupt_handler = mac_intr_handler,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/ael1002.c b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
index 2028da95afa1..dadf11e3dddb 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
@@ -198,7 +198,7 @@ static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
return 0;
}
-static struct cphy_ops ael1002_ops = {
+static const struct cphy_ops ael1002_ops = {
.reset = ael1002_reset,
.intr_enable = ael1002_intr_noop,
.intr_disable = ael1002_intr_noop,
@@ -224,7 +224,7 @@ static int ael1006_reset(struct cphy *phy, int wait)
return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait);
}
-static struct cphy_ops ael1006_ops = {
+static const struct cphy_ops ael1006_ops = {
.reset = ael1006_reset,
.intr_enable = t3_phy_lasi_intr_enable,
.intr_disable = t3_phy_lasi_intr_disable,
@@ -495,7 +495,7 @@ static int ael2005_intr_handler(struct cphy *phy)
return ret ? ret : cphy_cause_link_change;
}
-static struct cphy_ops ael2005_ops = {
+static const struct cphy_ops ael2005_ops = {
.reset = ael2005_reset,
.intr_enable = ael2005_intr_enable,
.intr_disable = ael2005_intr_disable,
@@ -801,7 +801,7 @@ static int ael2020_intr_handler(struct cphy *phy)
return ret ? ret : cphy_cause_link_change;
}
-static struct cphy_ops ael2020_ops = {
+static const struct cphy_ops ael2020_ops = {
.reset = ael2020_reset,
.intr_enable = ael2020_intr_enable,
.intr_disable = ael2020_intr_disable,
@@ -856,7 +856,7 @@ static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,
return 0;
}
-static struct cphy_ops qt2045_ops = {
+static const struct cphy_ops qt2045_ops = {
.reset = ael1006_reset,
.intr_enable = t3_phy_lasi_intr_enable,
.intr_disable = t3_phy_lasi_intr_disable,
@@ -921,7 +921,7 @@ static int xaui_direct_power_down(struct cphy *phy, int enable)
return 0;
}
-static struct cphy_ops xaui_direct_ops = {
+static const struct cphy_ops xaui_direct_ops = {
.reset = xaui_direct_reset,
.intr_enable = ael1002_intr_noop,
.intr_disable = ael1002_intr_noop,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/aq100x.c b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
index 341b7ef1508f..6af5d200e44f 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
@@ -247,7 +247,7 @@ static int aq100x_get_link_status(struct cphy *phy, int *link_ok,
return 0;
}
-static struct cphy_ops aq100x_ops = {
+static const struct cphy_ops aq100x_ops = {
.reset = aq100x_reset,
.intr_enable = aq100x_intr_enable,
.intr_disable = aq100x_intr_disable,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/common.h b/drivers/net/ethernet/chelsio/cxgb3/common.h
index 442480982d3f..1bd7d89666c4 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/common.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/common.h
@@ -575,7 +575,7 @@ static inline int t3_mdio_write(struct cphy *phy, int mmd, int reg,
/* Convenience initializer */
static inline void cphy_init(struct cphy *phy, struct adapter *adapter,
- int phy_addr, struct cphy_ops *phy_ops,
+ int phy_addr, const struct cphy_ops *phy_ops,
const struct mdio_ops *mdio_ops,
unsigned int caps, const char *desc)
{
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index 8f7aa53a4c4b..60908eab3b3a 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -701,15 +701,16 @@ static ssize_t attr_store(struct device *d,
ssize_t(*set) (struct net_device *, unsigned int),
unsigned int min_val, unsigned int max_val)
{
- char *endp;
ssize_t ret;
unsigned int val;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- val = simple_strtoul(buf, &endp, 0);
- if (endp == buf || val < min_val || val > max_val)
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+ if (val < min_val || val > max_val)
return -EINVAL;
rtnl_lock();
@@ -829,14 +830,15 @@ static ssize_t tm_attr_store(struct device *d,
struct port_info *pi = netdev_priv(to_net_dev(d));
struct adapter *adap = pi->adapter;
unsigned int val;
- char *endp;
ssize_t ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- val = simple_strtoul(buf, &endp, 0);
- if (endp == buf || val > 10000000)
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+ if (val > 10000000)
return -EINVAL;
rtnl_lock();
diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
index a22768c94200..ee04caa6c4d8 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
@@ -709,11 +709,21 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p)
return ret;
}
- p->cclk = simple_strtoul(vpd.cclk_data, NULL, 10);
- p->mclk = simple_strtoul(vpd.mclk_data, NULL, 10);
- p->uclk = simple_strtoul(vpd.uclk_data, NULL, 10);
- p->mdc = simple_strtoul(vpd.mdc_data, NULL, 10);
- p->mem_timing = simple_strtoul(vpd.mt_data, NULL, 10);
+ ret = kstrtouint(vpd.cclk_data, 10, &p->cclk);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.mclk_data, 10, &p->mclk);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.uclk_data, 10, &p->uclk);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.mdc_data, 10, &p->mdc);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.mt_data, 10, &p->mem_timing);
+ if (ret)
+ return ret;
memcpy(p->sn, vpd.sn_data, SERNUM_LEN);
/* Old eeproms didn't have port information */
@@ -723,8 +733,12 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p)
} else {
p->port_type[0] = hex_to_bin(vpd.port0_data[0]);
p->port_type[1] = hex_to_bin(vpd.port1_data[0]);
- p->xauicfg[0] = simple_strtoul(vpd.xaui0cfg_data, NULL, 16);
- p->xauicfg[1] = simple_strtoul(vpd.xaui1cfg_data, NULL, 16);
+ ret = kstrtou16(vpd.xaui0cfg_data, 16, &p->xauicfg[0]);
+ if (ret)
+ return ret;
+ ret = kstrtou16(vpd.xaui1cfg_data, 16, &p->xauicfg[1]);
+ if (ret)
+ return ret;
}
ret = hex2bin(p->eth_base, vpd.na_data, 6);
diff --git a/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
index 4f9a1c2724f4..8638ad42bf60 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
@@ -336,7 +336,7 @@ static int vsc8211_intr_handler(struct cphy *cphy)
return cphy_cause;
}
-static struct cphy_ops vsc8211_ops = {
+static const struct cphy_ops vsc8211_ops = {
.reset = vsc8211_reset,
.intr_enable = vsc8211_intr_enable,
.intr_disable = vsc8211_intr_disable,
@@ -350,7 +350,7 @@ static struct cphy_ops vsc8211_ops = {
.power_down = vsc8211_power_down,
};
-static struct cphy_ops vsc8211_fiber_ops = {
+static const struct cphy_ops vsc8211_fiber_ops = {
.reset = vsc8211_reset,
.intr_enable = vsc8211_intr_enable,
.intr_disable = vsc8211_intr_disable,
diff --git a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c
index 11dd91e4db56..7ad43af6bde1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c
@@ -118,6 +118,11 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
ret = clip6_get_mbox(dev, (const struct in6_addr *)lip);
if (ret) {
write_unlock_bh(&ctbl->lock);
+ dev_err(adap->pdev_dev,
+ "CLIP FW cmd failed with error %d, "
+ "Connections using %pI6c wont be "
+ "offloaded",
+ ret, ce->addr6.sin6_addr.s6_addr);
return ret;
}
} else {
@@ -127,6 +132,9 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
}
} else {
write_unlock_bh(&ctbl->lock);
+ dev_info(adap->pdev_dev, "CLIP table overflow, "
+ "Connections using %pI6c wont be offloaded",
+ (void *)lip);
return -ENOMEM;
}
write_unlock_bh(&ctbl->lock);
@@ -146,6 +154,9 @@ void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6)
int hash;
int ret = -1;
+ if (!ctbl)
+ return;
+
hash = clip_addr_hash(ctbl, addr, v6);
read_lock_bh(&ctbl->lock);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 55a47de544ea..ec6e849676c1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -301,6 +301,8 @@ struct devlog_params {
/* Stores chip specific parameters */
struct arch_specific_params {
u8 nchan;
+ u8 pm_stats_cnt;
+ u8 cng_ch_bits_log; /* congestion channel map bits width */
u16 mps_rplc_size;
u16 vfcount;
u32 sge_fl_db;
@@ -398,11 +400,10 @@ struct link_config {
enum {
MAX_ETH_QSETS = 32, /* # of Ethernet Tx/Rx queue sets */
- MAX_OFLD_QSETS = 16, /* # of offload Tx/Rx queue sets */
+ MAX_OFLD_QSETS = 16, /* # of offload Tx, iscsi Rx queue sets */
MAX_CTRL_QUEUES = NCHAN, /* # of control Tx queues */
MAX_RDMA_QUEUES = NCHAN, /* # of streaming RDMA Rx queues */
MAX_RDMA_CIQS = 32, /* # of RDMA concentrator IQs */
- MAX_ISCSI_QUEUES = NCHAN, /* # of streaming iSCSI Rx queues */
};
enum {
@@ -420,7 +421,7 @@ enum {
INGQ_EXTRAS = 2, /* firmware event queue and */
/* forwarded interrupts */
MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES
- + MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS,
+ + MAX_RDMA_CIQS + INGQ_EXTRAS,
};
struct adapter;
@@ -483,6 +484,8 @@ struct sge_fl { /* SGE free-buffer queue state */
unsigned int pidx; /* producer index */
unsigned long alloc_failed; /* # of times buffer allocation failed */
unsigned long large_alloc_failed;
+ unsigned long mapping_err; /* # of RX Buffer DMA Mapping failures */
+ unsigned long low; /* # of times momentarily starving */
unsigned long starving;
/* RO fields */
unsigned int cntxt_id; /* SGE context id for the free list */
@@ -618,6 +621,7 @@ struct sge_ofld_txq { /* state for an SGE offload Tx queue */
struct adapter *adap;
struct sk_buff_head sendq; /* list of backpressured packets */
struct tasklet_struct qresume_tsk; /* restarts the queue */
+ bool service_ofldq_running; /* service_ofldq() is processing sendq */
u8 full; /* the Tx ring is full */
unsigned long mapping_err; /* # of I/O MMU packet mapping errors */
} ____cacheline_aligned_in_smp;
@@ -636,7 +640,7 @@ struct sge {
struct sge_ctrl_txq ctrlq[MAX_CTRL_QUEUES];
struct sge_eth_rxq ethrxq[MAX_ETH_QSETS];
- struct sge_ofld_rxq ofldrxq[MAX_OFLD_QSETS];
+ struct sge_ofld_rxq iscsirxq[MAX_OFLD_QSETS];
struct sge_ofld_rxq rdmarxq[MAX_RDMA_QUEUES];
struct sge_ofld_rxq rdmaciq[MAX_RDMA_CIQS];
struct sge_rspq fw_evtq ____cacheline_aligned_in_smp;
@@ -647,10 +651,10 @@ struct sge {
u16 max_ethqsets; /* # of available Ethernet queue sets */
u16 ethqsets; /* # of active Ethernet queue sets */
u16 ethtxq_rover; /* Tx queue to clean up next */
- u16 ofldqsets; /* # of active offload queue sets */
+ u16 iscsiqsets; /* # of active iSCSI queue sets */
u16 rdmaqs; /* # of available RDMA Rx queues */
u16 rdmaciqs; /* # of available RDMA concentrator IQs */
- u16 ofld_rxq[MAX_OFLD_QSETS];
+ u16 iscsi_rxq[MAX_OFLD_QSETS];
u16 rdma_rxq[MAX_RDMA_QUEUES];
u16 rdma_ciq[MAX_RDMA_CIQS];
u16 timer_val[SGE_NTIMERS];
@@ -676,7 +680,7 @@ struct sge {
};
#define for_each_ethrxq(sge, i) for (i = 0; i < (sge)->ethqsets; i++)
-#define for_each_ofldrxq(sge, i) for (i = 0; i < (sge)->ofldqsets; i++)
+#define for_each_iscsirxq(sge, i) for (i = 0; i < (sge)->iscsiqsets; i++)
#define for_each_rdmarxq(sge, i) for (i = 0; i < (sge)->rdmaqs; i++)
#define for_each_rdmaciq(sge, i) for (i = 0; i < (sge)->rdmaciqs; i++)
@@ -1253,6 +1257,7 @@ int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver);
int t4_fwcache(struct adapter *adap, enum fw_params_param_dev_fwcache op);
int t4_fw_upgrade(struct adapter *adap, unsigned int mbox,
const u8 *fw_data, unsigned int size, int force);
+int t4_fl_pkt_align(struct adapter *adap);
unsigned int t4_flash_cfg_addr(struct adapter *adapter);
int t4_check_fw_version(struct adapter *adap);
int t4_get_fw_version(struct adapter *adapter, u32 *vers);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 4269944c5db5..e6a4072b494b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -757,8 +757,8 @@ static int pm_stats_show(struct seq_file *seq, void *v)
};
int i;
- u32 tx_cnt[PM_NSTATS], rx_cnt[PM_NSTATS];
- u64 tx_cyc[PM_NSTATS], rx_cyc[PM_NSTATS];
+ u32 tx_cnt[T6_PM_NSTATS], rx_cnt[T6_PM_NSTATS];
+ u64 tx_cyc[T6_PM_NSTATS], rx_cyc[T6_PM_NSTATS];
struct adapter *adap = seq->private;
t4_pmtx_get_stats(adap, tx_cnt, tx_cyc);
@@ -773,6 +773,32 @@ static int pm_stats_show(struct seq_file *seq, void *v)
for (i = 0; i < PM_NSTATS - 1; i++)
seq_printf(seq, "%-13s %10u %20llu\n",
rx_pm_stats[i], rx_cnt[i], rx_cyc[i]);
+
+ if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) {
+ /* In T5 the granularity of the total wait is too fine.
+ * It is not useful as it reaches the max value too fast.
+ * Hence display this Input FIFO wait for T6 onwards.
+ */
+ seq_printf(seq, "%13s %10s %20s\n",
+ " ", "Total wait", "Total Occupancy");
+ seq_printf(seq, "Tx FIFO wait %10u %20llu\n",
+ tx_cnt[i], tx_cyc[i]);
+ seq_printf(seq, "Rx FIFO wait %10u %20llu\n",
+ rx_cnt[i], rx_cyc[i]);
+
+ /* Skip index 6 as there is nothing useful ihere */
+ i += 2;
+
+ /* At index 7, a new stat for read latency (count, total wait)
+ * is added.
+ */
+ seq_printf(seq, "%13s %10s %20s\n",
+ " ", "Reads", "Total wait");
+ seq_printf(seq, "Tx latency %10u %20llu\n",
+ tx_cnt[i], tx_cyc[i]);
+ seq_printf(seq, "Rx latency %10u %20llu\n",
+ rx_cnt[i], rx_cyc[i]);
+ }
return 0;
}
@@ -1559,25 +1585,35 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
{
struct adapter *adap = seq->private;
unsigned int chip_ver = CHELSIO_CHIP_VERSION(adap->params.chip);
-
if (v == SEQ_START_TOKEN) {
- if (adap->params.arch.mps_rplc_size > 128)
+ if (chip_ver > CHELSIO_T5) {
seq_puts(seq, "Idx Ethernet address Mask "
+ " VNI Mask IVLAN Vld "
+ "DIP_Hit Lookup Port "
"Vld Ports PF VF "
"Replication "
" P0 P1 P2 P3 ML\n");
- else
- seq_puts(seq, "Idx Ethernet address Mask "
- "Vld Ports PF VF Replication"
- " P0 P1 P2 P3 ML\n");
+ } else {
+ if (adap->params.arch.mps_rplc_size > 128)
+ seq_puts(seq, "Idx Ethernet address Mask "
+ "Vld Ports PF VF "
+ "Replication "
+ " P0 P1 P2 P3 ML\n");
+ else
+ seq_puts(seq, "Idx Ethernet address Mask "
+ "Vld Ports PF VF Replication"
+ " P0 P1 P2 P3 ML\n");
+ }
} else {
u64 mask;
u8 addr[ETH_ALEN];
- bool replicate;
+ bool replicate, dip_hit = false, vlan_vld = false;
unsigned int idx = (uintptr_t)v - 2;
u64 tcamy, tcamx, val;
- u32 cls_lo, cls_hi, ctl;
+ u32 cls_lo, cls_hi, ctl, data2, vnix = 0, vniy = 0;
u32 rplc[8] = {0};
+ u8 lookup_type = 0, port_num = 0;
+ u16 ivlan = 0;
if (chip_ver > CHELSIO_T5) {
/* CtlCmdType - 0: Read, 1: Write
@@ -1596,6 +1632,22 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
val = t4_read_reg(adap, MPS_CLS_TCAM_DATA1_A);
tcamy = DMACH_G(val) << 32;
tcamy |= t4_read_reg(adap, MPS_CLS_TCAM_DATA0_A);
+ data2 = t4_read_reg(adap, MPS_CLS_TCAM_DATA2_CTL_A);
+ lookup_type = DATALKPTYPE_G(data2);
+ /* 0 - Outer header, 1 - Inner header
+ * [71:48] bit locations are overloaded for
+ * outer vs. inner lookup types.
+ */
+ if (lookup_type && (lookup_type != DATALKPTYPE_M)) {
+ /* Inner header VNI */
+ vniy = ((data2 & DATAVIDH2_F) << 23) |
+ (DATAVIDH1_G(data2) << 16) | VIDL_G(val);
+ dip_hit = data2 & DATADIPHIT_F;
+ } else {
+ vlan_vld = data2 & DATAVIDH2_F;
+ ivlan = VIDL_G(val);
+ }
+ port_num = DATAPORTNUM_G(data2);
/* Read tcamx. Change the control param */
ctl |= CTLXYBITSEL_V(1);
@@ -1603,6 +1655,12 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
val = t4_read_reg(adap, MPS_CLS_TCAM_DATA1_A);
tcamx = DMACH_G(val) << 32;
tcamx |= t4_read_reg(adap, MPS_CLS_TCAM_DATA0_A);
+ data2 = t4_read_reg(adap, MPS_CLS_TCAM_DATA2_CTL_A);
+ if (lookup_type && (lookup_type != DATALKPTYPE_M)) {
+ /* Inner header VNI mask */
+ vnix = ((data2 & DATAVIDH2_F) << 23) |
+ (DATAVIDH1_G(data2) << 16) | VIDL_G(val);
+ }
} else {
tcamy = t4_read_reg64(adap, MPS_CLS_TCAM_Y_L(idx));
tcamx = t4_read_reg64(adap, MPS_CLS_TCAM_X_L(idx));
@@ -1662,17 +1720,47 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
}
tcamxy2valmask(tcamx, tcamy, addr, &mask);
- if (chip_ver > CHELSIO_T5)
- seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x "
- "%012llx%3c %#x%4u%4d",
- idx, addr[0], addr[1], addr[2], addr[3],
- addr[4], addr[5], (unsigned long long)mask,
- (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N',
- PORTMAP_G(cls_hi),
- T6_PF_G(cls_lo),
- (cls_lo & T6_VF_VALID_F) ?
- T6_VF_G(cls_lo) : -1);
- else
+ if (chip_ver > CHELSIO_T5) {
+ /* Inner header lookup */
+ if (lookup_type && (lookup_type != DATALKPTYPE_M)) {
+ seq_printf(seq,
+ "%3u %02x:%02x:%02x:%02x:%02x:%02x "
+ "%012llx %06x %06x - - %3c"
+ " 'I' %4x "
+ "%3c %#x%4u%4d", idx, addr[0],
+ addr[1], addr[2], addr[3],
+ addr[4], addr[5],
+ (unsigned long long)mask,
+ vniy, vnix, dip_hit ? 'Y' : 'N',
+ port_num,
+ (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N',
+ PORTMAP_G(cls_hi),
+ T6_PF_G(cls_lo),
+ (cls_lo & T6_VF_VALID_F) ?
+ T6_VF_G(cls_lo) : -1);
+ } else {
+ seq_printf(seq,
+ "%3u %02x:%02x:%02x:%02x:%02x:%02x "
+ "%012llx - - ",
+ idx, addr[0], addr[1], addr[2],
+ addr[3], addr[4], addr[5],
+ (unsigned long long)mask);
+
+ if (vlan_vld)
+ seq_printf(seq, "%4u Y ", ivlan);
+ else
+ seq_puts(seq, " - N ");
+
+ seq_printf(seq,
+ "- %3c %4x %3c %#x%4u%4d",
+ lookup_type ? 'I' : 'O', port_num,
+ (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N',
+ PORTMAP_G(cls_hi),
+ T6_PF_G(cls_lo),
+ (cls_lo & T6_VF_VALID_F) ?
+ T6_VF_G(cls_lo) : -1);
+ }
+ } else
seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x "
"%012llx%3c %#x%4u%4d",
idx, addr[0], addr[1], addr[2], addr[3],
@@ -2245,7 +2333,7 @@ static int sge_qinfo_show(struct seq_file *seq, void *v)
{
struct adapter *adap = seq->private;
int eth_entries = DIV_ROUND_UP(adap->sge.ethqsets, 4);
- int iscsi_entries = DIV_ROUND_UP(adap->sge.ofldqsets, 4);
+ int iscsi_entries = DIV_ROUND_UP(adap->sge.iscsiqsets, 4);
int rdma_entries = DIV_ROUND_UP(adap->sge.rdmaqs, 4);
int ciq_entries = DIV_ROUND_UP(adap->sge.rdmaciqs, 4);
int ctrl_entries = DIV_ROUND_UP(MAX_CTRL_QUEUES, 4);
@@ -2325,14 +2413,16 @@ do { \
TL("TxMapErr:", mapping_err);
RL("FLAllocErr:", fl.alloc_failed);
RL("FLLrgAlcErr:", fl.large_alloc_failed);
+ RL("FLMapErr:", fl.mapping_err);
+ RL("FLLow:", fl.low);
RL("FLStarving:", fl.starving);
} else if (iscsi_idx < iscsi_entries) {
const struct sge_ofld_rxq *rx =
- &adap->sge.ofldrxq[iscsi_idx * 4];
+ &adap->sge.iscsirxq[iscsi_idx * 4];
const struct sge_ofld_txq *tx =
&adap->sge.ofldtxq[iscsi_idx * 4];
- int n = min(4, adap->sge.ofldqsets - 4 * iscsi_idx);
+ int n = min(4, adap->sge.iscsiqsets - 4 * iscsi_idx);
S("QType:", "iSCSI");
T("TxQ ID:", q.cntxt_id);
@@ -2359,6 +2449,8 @@ do { \
RL("RxNoMem:", stats.nomem);
RL("FLAllocErr:", fl.alloc_failed);
RL("FLLrgAlcErr:", fl.large_alloc_failed);
+ RL("FLMapErr:", fl.mapping_err);
+ RL("FLLow:", fl.low);
RL("FLStarving:", fl.starving);
} else if (rdma_idx < rdma_entries) {
@@ -2388,6 +2480,8 @@ do { \
RL("RxNoMem:", stats.nomem);
RL("FLAllocErr:", fl.alloc_failed);
RL("FLLrgAlcErr:", fl.large_alloc_failed);
+ RL("FLMapErr:", fl.mapping_err);
+ RL("FLLow:", fl.low);
RL("FLStarving:", fl.starving);
} else if (ciq_idx < ciq_entries) {
@@ -2448,7 +2542,7 @@ do { \
static int sge_queue_entries(const struct adapter *adap)
{
return DIV_ROUND_UP(adap->sge.ethqsets, 4) +
- DIV_ROUND_UP(adap->sge.ofldqsets, 4) +
+ DIV_ROUND_UP(adap->sge.iscsiqsets, 4) +
DIV_ROUND_UP(adap->sge.rdmaqs, 4) +
DIV_ROUND_UP(adap->sge.rdmaciqs, 4) +
DIV_ROUND_UP(MAX_CTRL_QUEUES, 4) + 1;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
index a077f9476daf..7a0b92b2f73c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
@@ -35,79 +35,79 @@ static void set_msglevel(struct net_device *dev, u32 val)
}
static const char stats_strings[][ETH_GSTRING_LEN] = {
- "tx_octets_ok ",
- "tx_frames_ok ",
- "tx_broadcast_frames ",
- "tx_multicast_frames ",
- "tx_unicast_frames ",
- "tx_error_frames ",
-
- "tx_frames_64 ",
- "tx_frames_65_to_127 ",
- "tx_frames_128_to_255 ",
- "tx_frames_256_to_511 ",
- "tx_frames_512_to_1023 ",
- "tx_frames_1024_to_1518 ",
- "tx_frames_1519_to_max ",
-
- "tx_frames_dropped ",
- "tx_pause_frames ",
- "tx_ppp0_frames ",
- "tx_ppp1_frames ",
- "tx_ppp2_frames ",
- "tx_ppp3_frames ",
- "tx_ppp4_frames ",
- "tx_ppp5_frames ",
- "tx_ppp6_frames ",
- "tx_ppp7_frames ",
-
- "rx_octets_ok ",
- "rx_frames_ok ",
- "rx_broadcast_frames ",
- "rx_multicast_frames ",
- "rx_unicast_frames ",
-
- "rx_frames_too_long ",
- "rx_jabber_errors ",
- "rx_fcs_errors ",
- "rx_length_errors ",
- "rx_symbol_errors ",
- "rx_runt_frames ",
-
- "rx_frames_64 ",
- "rx_frames_65_to_127 ",
- "rx_frames_128_to_255 ",
- "rx_frames_256_to_511 ",
- "rx_frames_512_to_1023 ",
- "rx_frames_1024_to_1518 ",
- "rx_frames_1519_to_max ",
-
- "rx_pause_frames ",
- "rx_ppp0_frames ",
- "rx_ppp1_frames ",
- "rx_ppp2_frames ",
- "rx_ppp3_frames ",
- "rx_ppp4_frames ",
- "rx_ppp5_frames ",
- "rx_ppp6_frames ",
- "rx_ppp7_frames ",
-
- "rx_bg0_frames_dropped ",
- "rx_bg1_frames_dropped ",
- "rx_bg2_frames_dropped ",
- "rx_bg3_frames_dropped ",
- "rx_bg0_frames_trunc ",
- "rx_bg1_frames_trunc ",
- "rx_bg2_frames_trunc ",
- "rx_bg3_frames_trunc ",
-
- "tso ",
- "tx_csum_offload ",
- "rx_csum_good ",
- "vlan_extractions ",
- "vlan_insertions ",
- "gro_packets ",
- "gro_merged ",
+ "tx_octets_ok ",
+ "tx_frames_ok ",
+ "tx_broadcast_frames ",
+ "tx_multicast_frames ",
+ "tx_unicast_frames ",
+ "tx_error_frames ",
+
+ "tx_frames_64 ",
+ "tx_frames_65_to_127 ",
+ "tx_frames_128_to_255 ",
+ "tx_frames_256_to_511 ",
+ "tx_frames_512_to_1023 ",
+ "tx_frames_1024_to_1518 ",
+ "tx_frames_1519_to_max ",
+
+ "tx_frames_dropped ",
+ "tx_pause_frames ",
+ "tx_ppp0_frames ",
+ "tx_ppp1_frames ",
+ "tx_ppp2_frames ",
+ "tx_ppp3_frames ",
+ "tx_ppp4_frames ",
+ "tx_ppp5_frames ",
+ "tx_ppp6_frames ",
+ "tx_ppp7_frames ",
+
+ "rx_octets_ok ",
+ "rx_frames_ok ",
+ "rx_broadcast_frames ",
+ "rx_multicast_frames ",
+ "rx_unicast_frames ",
+
+ "rx_frames_too_long ",
+ "rx_jabber_errors ",
+ "rx_fcs_errors ",
+ "rx_length_errors ",
+ "rx_symbol_errors ",
+ "rx_runt_frames ",
+
+ "rx_frames_64 ",
+ "rx_frames_65_to_127 ",
+ "rx_frames_128_to_255 ",
+ "rx_frames_256_to_511 ",
+ "rx_frames_512_to_1023 ",
+ "rx_frames_1024_to_1518 ",
+ "rx_frames_1519_to_max ",
+
+ "rx_pause_frames ",
+ "rx_ppp0_frames ",
+ "rx_ppp1_frames ",
+ "rx_ppp2_frames ",
+ "rx_ppp3_frames ",
+ "rx_ppp4_frames ",
+ "rx_ppp5_frames ",
+ "rx_ppp6_frames ",
+ "rx_ppp7_frames ",
+
+ "rx_bg0_frames_dropped ",
+ "rx_bg1_frames_dropped ",
+ "rx_bg2_frames_dropped ",
+ "rx_bg3_frames_dropped ",
+ "rx_bg0_frames_trunc ",
+ "rx_bg1_frames_trunc ",
+ "rx_bg2_frames_trunc ",
+ "rx_bg3_frames_trunc ",
+
+ "tso ",
+ "tx_csum_offload ",
+ "rx_csum_good ",
+ "vlan_extractions ",
+ "vlan_insertions ",
+ "gro_packets ",
+ "gro_merged ",
};
static char adapter_stats_strings[][ETH_GSTRING_LEN] = {
@@ -686,7 +686,7 @@ static int set_pauseparam(struct net_device *dev,
if (epause->tx_pause)
lc->requested_fc |= PAUSE_TX;
if (netif_running(dev))
- return t4_link_l1cfg(p->adapter, p->adapter->pf, p->tx_chan,
+ return t4_link_l1cfg(p->adapter, p->adapter->mbox, p->tx_chan,
lc);
return 0;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 0d147610a06f..b8a5fb0c32d4 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -162,19 +162,8 @@ MODULE_FIRMWARE(FW6_FNAME);
static uint force_init;
module_param(force_init, uint, 0644);
-MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter");
-
-/*
- * Normally if the firmware we connect to has Configuration File support, we
- * use that and only fall back to the old Driver-based initialization if the
- * Configuration File fails for some reason. If force_old_init is set, then
- * we'll always use the old Driver-based initialization sequence.
- */
-static uint force_old_init;
-
-module_param(force_old_init, uint, 0644);
-MODULE_PARM_DESC(force_old_init, "Force old initialization sequence, deprecated"
- " parameter");
+MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter,"
+ "deprecated parameter");
static int dflt_msg_enable = DFLT_MSG_ENABLE;
@@ -196,23 +185,6 @@ module_param(msi, int, 0644);
MODULE_PARM_DESC(msi, "whether to use INTx (0), MSI (1) or MSI-X (2)");
/*
- * Queue interrupt hold-off timer values. Queues default to the first of these
- * upon creation.
- */
-static unsigned int intr_holdoff[SGE_NTIMERS - 1] = { 5, 10, 20, 50, 100 };
-
-module_param_array(intr_holdoff, uint, NULL, 0644);
-MODULE_PARM_DESC(intr_holdoff, "values for queue interrupt hold-off timers "
- "0..4 in microseconds, deprecated parameter");
-
-static unsigned int intr_cnt[SGE_NCOUNTERS - 1] = { 4, 8, 16 };
-
-module_param_array(intr_cnt, uint, NULL, 0644);
-MODULE_PARM_DESC(intr_cnt,
- "thresholds 1..3 for queue interrupt packet counters, "
- "deprecated parameter");
-
-/*
* Normally we tell the chip to deliver Ingress Packets into our DMA buffers
* offset by 2 bytes in order to have the IP headers line up on 4-byte
* boundaries. This is a requirement for many architectures which will throw
@@ -226,13 +198,7 @@ MODULE_PARM_DESC(intr_cnt,
*/
static int rx_dma_offset = 2;
-static bool vf_acls;
-
#ifdef CONFIG_PCI_IOV
-module_param(vf_acls, bool, 0644);
-MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement, "
- "deprecated parameter");
-
/* Configure the number of PCI-E Virtual Function which are to be instantiated
* on SR-IOV Capable Physical Functions.
*/
@@ -253,12 +219,6 @@ module_param(select_queue, int, 0644);
MODULE_PARM_DESC(select_queue,
"Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method.");
-static unsigned int tp_vlan_pri_map = HW_TPL_FR_MT_PR_IV_P_FC;
-
-module_param(tp_vlan_pri_map, uint, 0644);
-MODULE_PARM_DESC(tp_vlan_pri_map, "global compressed filter configuration, "
- "deprecated parameter");
-
static struct dentry *cxgb4_debugfs_root;
static LIST_HEAD(adapter_list);
@@ -766,8 +726,8 @@ static void name_msix_vecs(struct adapter *adap)
}
/* offload queues */
- for_each_ofldrxq(&adap->sge, i)
- snprintf(adap->msix_info[msi_idx++].desc, n, "%s-ofld%d",
+ for_each_iscsirxq(&adap->sge, i)
+ snprintf(adap->msix_info[msi_idx++].desc, n, "%s-iscsi%d",
adap->port[0]->name, i);
for_each_rdmarxq(&adap->sge, i)
@@ -782,7 +742,7 @@ static void name_msix_vecs(struct adapter *adap)
static int request_msix_queue_irqs(struct adapter *adap)
{
struct sge *s = &adap->sge;
- int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0;
+ int err, ethqidx, iscsiqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0;
int msi_index = 2;
err = request_irq(adap->msix_info[1].vec, t4_sge_intr_msix, 0,
@@ -799,11 +759,11 @@ static int request_msix_queue_irqs(struct adapter *adap)
goto unwind;
msi_index++;
}
- for_each_ofldrxq(s, ofldqidx) {
+ for_each_iscsirxq(s, iscsiqidx) {
err = request_irq(adap->msix_info[msi_index].vec,
t4_sge_intr_msix, 0,
adap->msix_info[msi_index].desc,
- &s->ofldrxq[ofldqidx].rspq);
+ &s->iscsirxq[iscsiqidx].rspq);
if (err)
goto unwind;
msi_index++;
@@ -835,9 +795,9 @@ unwind:
while (--rdmaqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->rdmarxq[rdmaqidx].rspq);
- while (--ofldqidx >= 0)
+ while (--iscsiqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
- &s->ofldrxq[ofldqidx].rspq);
+ &s->iscsirxq[iscsiqidx].rspq);
while (--ethqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->ethrxq[ethqidx].rspq);
@@ -853,8 +813,9 @@ static void free_msix_queue_irqs(struct adapter *adap)
free_irq(adap->msix_info[1].vec, &s->fw_evtq);
for_each_ethrxq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->ethrxq[i].rspq);
- for_each_ofldrxq(s, i)
- free_irq(adap->msix_info[msi_index++].vec, &s->ofldrxq[i].rspq);
+ for_each_iscsirxq(s, i)
+ free_irq(adap->msix_info[msi_index++].vec,
+ &s->iscsirxq[i].rspq);
for_each_rdmarxq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->rdmarxq[i].rspq);
for_each_rdmaciq(s, i)
@@ -1093,8 +1054,8 @@ freeout: t4_free_sge_resources(adap);
}
}
- j = s->ofldqsets / adap->params.nports; /* ofld queues per channel */
- for_each_ofldrxq(s, i) {
+ j = s->iscsiqsets / adap->params.nports; /* iscsi queues per channel */
+ for_each_iscsirxq(s, i) {
err = t4_sge_alloc_ofld_txq(adap, &s->ofldtxq[i],
adap->port[i / j],
s->fw_evtq.cntxt_id);
@@ -1110,7 +1071,7 @@ freeout: t4_free_sge_resources(adap);
msi_idx += nq; \
} while (0)
- ALLOC_OFLD_RXQS(s->ofldrxq, s->ofldqsets, j, s->ofld_rxq);
+ ALLOC_OFLD_RXQS(s->iscsirxq, s->iscsiqsets, j, s->iscsi_rxq);
ALLOC_OFLD_RXQS(s->rdmarxq, s->rdmaqs, 1, s->rdma_rxq);
j = s->rdmaciqs / adap->params.nports; /* rdmaq queues per channel */
ALLOC_OFLD_RXQS(s->rdmaciq, s->rdmaciqs, j, s->rdma_ciq);
@@ -1181,16 +1142,10 @@ static int set_filter_wr(struct adapter *adapter, int fidx)
*/
if (f->fs.newdmac || f->fs.newvlan) {
/* allocate L2T entry for new filter */
- f->l2t = t4_l2t_alloc_switching(adapter->l2t);
+ f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan,
+ f->fs.eport, f->fs.dmac);
if (f->l2t == NULL) {
kfree_skb(skb);
- return -EAGAIN;
- }
- if (t4_l2t_set_switching(adapter, f->l2t, f->fs.vlan,
- f->fs.eport, f->fs.dmac)) {
- cxgb4_l2t_release(f->l2t);
- f->l2t = NULL;
- kfree_skb(skb);
return -ENOMEM;
}
}
@@ -1511,7 +1466,7 @@ int cxgb4_alloc_stid(struct tid_info *t, int family, void *data)
else
stid = -1;
} else {
- stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 2);
+ stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 1);
if (stid < 0)
stid = -1;
}
@@ -1525,7 +1480,7 @@ int cxgb4_alloc_stid(struct tid_info *t, int family, void *data)
if (family == PF_INET)
t->stids_in_use++;
else
- t->stids_in_use += 4;
+ t->stids_in_use += 2;
}
spin_unlock_bh(&t->stid_lock);
return stid;
@@ -1576,13 +1531,13 @@ void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family)
if (family == PF_INET)
__clear_bit(stid, t->stid_bmap);
else
- bitmap_release_region(t->stid_bmap, stid, 2);
+ bitmap_release_region(t->stid_bmap, stid, 1);
t->stid_tab[stid].data = NULL;
if (stid < t->nstids) {
if (family == PF_INET)
t->stids_in_use--;
else
- t->stids_in_use -= 4;
+ t->stids_in_use -= 2;
} else {
t->sftids_in_use--;
}
@@ -2283,7 +2238,7 @@ static void disable_dbs(struct adapter *adap)
for_each_ethrxq(&adap->sge, i)
disable_txq_db(&adap->sge.ethtxq[i].q);
- for_each_ofldrxq(&adap->sge, i)
+ for_each_iscsirxq(&adap->sge, i)
disable_txq_db(&adap->sge.ofldtxq[i].q);
for_each_port(adap, i)
disable_txq_db(&adap->sge.ctrlq[i].q);
@@ -2295,7 +2250,7 @@ static void enable_dbs(struct adapter *adap)
for_each_ethrxq(&adap->sge, i)
enable_txq_db(adap, &adap->sge.ethtxq[i].q);
- for_each_ofldrxq(&adap->sge, i)
+ for_each_iscsirxq(&adap->sge, i)
enable_txq_db(adap, &adap->sge.ofldtxq[i].q);
for_each_port(adap, i)
enable_txq_db(adap, &adap->sge.ctrlq[i].q);
@@ -2365,7 +2320,7 @@ static void recover_all_queues(struct adapter *adap)
for_each_ethrxq(&adap->sge, i)
sync_txq_pidx(adap, &adap->sge.ethtxq[i].q);
- for_each_ofldrxq(&adap->sge, i)
+ for_each_iscsirxq(&adap->sge, i)
sync_txq_pidx(adap, &adap->sge.ofldtxq[i].q);
for_each_port(adap, i)
sync_txq_pidx(adap, &adap->sge.ctrlq[i].q);
@@ -2449,10 +2404,10 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
lli.nrxq = adap->sge.rdmaqs;
lli.nciq = adap->sge.rdmaciqs;
} else if (uld == CXGB4_ULD_ISCSI) {
- lli.rxq_ids = adap->sge.ofld_rxq;
- lli.nrxq = adap->sge.ofldqsets;
+ lli.rxq_ids = adap->sge.iscsi_rxq;
+ lli.nrxq = adap->sge.iscsiqsets;
}
- lli.ntxq = adap->sge.ofldqsets;
+ lli.ntxq = adap->sge.iscsiqsets;
lli.nchan = adap->params.nports;
lli.nports = adap->params.nports;
lli.wr_cred = adap->params.ofldq_wr_cred;
@@ -3146,16 +3101,6 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c)
if (ret < 0)
return ret;
- /* select capabilities we'll be using */
- if (c->niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) {
- if (!vf_acls)
- c->niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM);
- else
- c->niccaps = htons(FW_CAPS_CONFIG_NIC_VM);
- } else if (vf_acls) {
- dev_err(adap->pdev_dev, "virtualization ACLs not supported");
- return ret;
- }
c->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
ret = t4_wr_mbox(adap, adap->mbox, c, sizeof(*c), NULL);
@@ -4348,11 +4293,11 @@ static void cfg_queues(struct adapter *adap)
* capped by the number of available cores.
*/
if (n10g) {
- i = min_t(int, ARRAY_SIZE(s->ofldrxq),
+ i = min_t(int, ARRAY_SIZE(s->iscsirxq),
num_online_cpus());
- s->ofldqsets = roundup(i, adap->params.nports);
+ s->iscsiqsets = roundup(i, adap->params.nports);
} else
- s->ofldqsets = adap->params.nports;
+ s->iscsiqsets = adap->params.nports;
/* For RDMA one Rx queue per channel suffices */
s->rdmaqs = adap->params.nports;
/* Try and allow at least 1 CIQ per cpu rounding down
@@ -4383,8 +4328,8 @@ static void cfg_queues(struct adapter *adap)
for (i = 0; i < ARRAY_SIZE(s->ofldtxq); i++)
s->ofldtxq[i].q.size = 1024;
- for (i = 0; i < ARRAY_SIZE(s->ofldrxq); i++) {
- struct sge_ofld_rxq *r = &s->ofldrxq[i];
+ for (i = 0; i < ARRAY_SIZE(s->iscsirxq); i++) {
+ struct sge_ofld_rxq *r = &s->iscsirxq[i];
init_rspq(adap, &r->rspq, 5, 1, 1024, 64);
r->rspq.uld = CXGB4_ULD_ISCSI;
@@ -4465,7 +4410,7 @@ static int enable_msix(struct adapter *adap)
want = s->max_ethqsets + EXTRA_VECS;
if (is_offload(adap)) {
- want += s->rdmaqs + s->rdmaciqs + s->ofldqsets;
+ want += s->rdmaqs + s->rdmaciqs + s->iscsiqsets;
/* need nchan for each possible ULD */
ofld_need = 3 * nchan;
}
@@ -4504,13 +4449,13 @@ static int enable_msix(struct adapter *adap)
/* leftovers go to OFLD */
i = allocated - EXTRA_VECS - s->max_ethqsets -
s->rdmaqs - s->rdmaciqs;
- s->ofldqsets = (i / nchan) * nchan; /* round down */
+ s->iscsiqsets = (i / nchan) * nchan; /* round down */
}
for (i = 0; i < allocated; ++i)
adap->msix_info[i].vec = entries[i].vector;
dev_info(adap->pdev_dev, "%d MSI-X vectors allocated, "
"nic %d iscsi %d rdma cpl %d rdma ciq %d\n",
- allocated, s->max_ethqsets, s->ofldqsets, s->rdmaqs,
+ allocated, s->max_ethqsets, s->iscsiqsets, s->rdmaqs,
s->rdmaciqs);
kfree(entries);
@@ -4538,6 +4483,79 @@ static int init_rss(struct adapter *adap)
return 0;
}
+static int cxgb4_get_pcie_dev_link_caps(struct adapter *adap,
+ enum pci_bus_speed *speed,
+ enum pcie_link_width *width)
+{
+ u32 lnkcap1, lnkcap2;
+ int err1, err2;
+
+#define PCIE_MLW_CAP_SHIFT 4 /* start of MLW mask in link capabilities */
+
+ *speed = PCI_SPEED_UNKNOWN;
+ *width = PCIE_LNK_WIDTH_UNKNOWN;
+
+ err1 = pcie_capability_read_dword(adap->pdev, PCI_EXP_LNKCAP,
+ &lnkcap1);
+ err2 = pcie_capability_read_dword(adap->pdev, PCI_EXP_LNKCAP2,
+ &lnkcap2);
+ if (!err2 && lnkcap2) { /* PCIe r3.0-compliant */
+ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
+ *speed = PCIE_SPEED_8_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
+ *speed = PCIE_SPEED_5_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
+ *speed = PCIE_SPEED_2_5GT;
+ }
+ if (!err1) {
+ *width = (lnkcap1 & PCI_EXP_LNKCAP_MLW) >> PCIE_MLW_CAP_SHIFT;
+ if (!lnkcap2) { /* pre-r3.0 */
+ if (lnkcap1 & PCI_EXP_LNKCAP_SLS_5_0GB)
+ *speed = PCIE_SPEED_5_0GT;
+ else if (lnkcap1 & PCI_EXP_LNKCAP_SLS_2_5GB)
+ *speed = PCIE_SPEED_2_5GT;
+ }
+ }
+
+ if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN)
+ return err1 ? err1 : err2 ? err2 : -EINVAL;
+ return 0;
+}
+
+static void cxgb4_check_pcie_caps(struct adapter *adap)
+{
+ enum pcie_link_width width, width_cap;
+ enum pci_bus_speed speed, speed_cap;
+
+#define PCIE_SPEED_STR(speed) \
+ (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : \
+ speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : \
+ speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : \
+ "Unknown")
+
+ if (cxgb4_get_pcie_dev_link_caps(adap, &speed_cap, &width_cap)) {
+ dev_warn(adap->pdev_dev,
+ "Unable to determine PCIe device BW capabilities\n");
+ return;
+ }
+
+ if (pcie_get_minimum_link(adap->pdev, &speed, &width) ||
+ speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) {
+ dev_warn(adap->pdev_dev,
+ "Unable to determine PCI Express bandwidth.\n");
+ return;
+ }
+
+ dev_info(adap->pdev_dev, "PCIe link speed is %s, device supports %s\n",
+ PCIE_SPEED_STR(speed), PCIE_SPEED_STR(speed_cap));
+ dev_info(adap->pdev_dev, "PCIe link width is x%d, device supports x%d\n",
+ width, width_cap);
+ if (speed < speed_cap || width < width_cap)
+ dev_info(adap->pdev_dev,
+ "A slot with more lanes and/or higher speed is "
+ "suggested for optimal performance.\n");
+}
+
static void print_port_info(const struct net_device *dev)
{
char buf[80];
@@ -4565,10 +4583,10 @@ static void print_port_info(const struct net_device *dev)
--bufp;
sprintf(bufp, "BASE-%s", t4_get_port_type_description(pi->port_type));
- netdev_info(dev, "Chelsio %s rev %d %s %sNIC PCIe x%d%s%s\n",
+ netdev_info(dev, "Chelsio %s rev %d %s %sNIC %s\n",
adap->params.vpd.id,
CHELSIO_CHIP_RELEASE(adap->params.chip), buf,
- is_offload(adap) ? "R" : "", adap->params.pci.width, spd,
+ is_offload(adap) ? "R" : "",
(adap->flags & USING_MSIX) ? " MSI-X" :
(adap->flags & USING_MSI) ? " MSI" : "");
netdev_info(dev, "S/N: %s, P/N: %s\n",
@@ -4787,8 +4805,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* configure SGE_STAT_CFG_A to read WC stats */
if (!is_t4(adapter->params.chip))
- t4_write_reg(adapter, SGE_STAT_CFG_A,
- STATSOURCE_T5_V(7) | STATMODE_V(0));
+ t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7) |
+ (is_t5(adapter->params.chip) ? STATMODE_V(0) :
+ T6_STATMODE_V(0)));
for_each_port(adapter, i) {
struct net_device *netdev;
@@ -4865,15 +4884,25 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
#if IS_ENABLED(CONFIG_IPV6)
- adapter->clipt = t4_init_clip_tbl(adapter->clipt_start,
- adapter->clipt_end);
- if (!adapter->clipt) {
- /* We tolerate a lack of clip_table, giving up
- * some functionality
+ if ((CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) &&
+ (!(t4_read_reg(adapter, LE_DB_CONFIG_A) & ASLIPCOMPEN_F))) {
+ /* CLIP functionality is not present in hardware,
+ * hence disable all offload features
*/
dev_warn(&pdev->dev,
- "could not allocate Clip table, continuing\n");
+ "CLIP not enabled in hardware, continuing\n");
adapter->params.offload = 0;
+ } else {
+ adapter->clipt = t4_init_clip_tbl(adapter->clipt_start,
+ adapter->clipt_end);
+ if (!adapter->clipt) {
+ /* We tolerate a lack of clip_table, giving up
+ * some functionality
+ */
+ dev_warn(&pdev->dev,
+ "could not allocate Clip table, continuing\n");
+ adapter->params.offload = 0;
+ }
}
#endif
if (is_offload(adapter) && tid_init(&adapter->tids) < 0) {
@@ -4904,6 +4933,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
else if (msi > 0 && pci_enable_msi(pdev) == 0)
adapter->flags |= USING_MSI;
+ /* check for PCI Express bandwidth capabiltites */
+ cxgb4_check_pcie_caps(adapter);
+
err = init_rss(adapter);
if (err)
goto out_free_dev;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
index ac27898c6ab0..5b0f3ef348e9 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
@@ -66,7 +66,7 @@ struct l2t_data {
static inline unsigned int vlan_prio(const struct l2t_entry *e)
{
- return e->vlan >> 13;
+ return e->vlan >> VLAN_PRIO_SHIFT;
}
static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e)
@@ -161,8 +161,7 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync)
memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac));
memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac));
- set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0);
- t4_ofld_send(adap, skb);
+ t4_mgmt_tx(adap, skb);
if (sync && e->state != L2T_STATE_SWITCHING)
e->state = L2T_STATE_SYNC_WRITE;
@@ -175,14 +174,10 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync)
*/
static void send_pending(struct adapter *adap, struct l2t_entry *e)
{
- while (e->arpq_head) {
- struct sk_buff *skb = e->arpq_head;
+ struct sk_buff *skb;
- e->arpq_head = skb->next;
- skb->next = NULL;
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL)
t4_ofld_send(adap, skb);
- }
- e->arpq_tail = NULL;
}
/*
@@ -222,12 +217,7 @@ void do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl)
*/
static inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb)
{
- skb->next = NULL;
- if (e->arpq_head)
- e->arpq_tail->next = skb;
- else
- e->arpq_head = skb;
- e->arpq_tail = skb;
+ __skb_queue_tail(&e->arpq, skb);
}
int cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb,
@@ -259,7 +249,8 @@ again:
if (e->state == L2T_STATE_RESOLVING &&
!neigh_event_send(e->neigh, NULL)) {
spin_lock_bh(&e->lock);
- if (e->state == L2T_STATE_RESOLVING && e->arpq_head)
+ if (e->state == L2T_STATE_RESOLVING &&
+ !skb_queue_empty(&e->arpq))
write_l2e(adap, e, 1);
spin_unlock_bh(&e->lock);
}
@@ -305,12 +296,82 @@ found:
return e;
}
-/*
- * Called when an L2T entry has no more users.
+static struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan,
+ u8 port, u8 *dmac)
+{
+ struct l2t_entry *end, *e, **p;
+ struct l2t_entry *first_free = NULL;
+
+ for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) {
+ if (atomic_read(&e->refcnt) == 0) {
+ if (!first_free)
+ first_free = e;
+ } else {
+ if (e->state == L2T_STATE_SWITCHING) {
+ if (ether_addr_equal(e->dmac, dmac) &&
+ (e->vlan == vlan) && (e->lport == port))
+ goto exists;
+ }
+ }
+ }
+
+ if (first_free) {
+ e = first_free;
+ goto found;
+ }
+
+ return NULL;
+
+found:
+ /* The entry we found may be an inactive entry that is
+ * presently in the hash table. We need to remove it.
+ */
+ if (e->state < L2T_STATE_SWITCHING)
+ for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next)
+ if (*p == e) {
+ *p = e->next;
+ e->next = NULL;
+ break;
+ }
+ e->state = L2T_STATE_UNUSED;
+
+exists:
+ return e;
+}
+
+/* Called when an L2T entry has no more users. The entry is left in the hash
+ * table since it is likely to be reused but we also bump nfree to indicate
+ * that the entry can be reallocated for a different neighbor. We also drop
+ * the existing neighbor reference in case the neighbor is going away and is
+ * waiting on our reference.
+ *
+ * Because entries can be reallocated to other neighbors once their ref count
+ * drops to 0 we need to take the entry's lock to avoid races with a new
+ * incarnation.
*/
+static void _t4_l2e_free(struct l2t_entry *e)
+{
+ struct l2t_data *d;
+ struct sk_buff *skb;
+
+ if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */
+ if (e->neigh) {
+ neigh_release(e->neigh);
+ e->neigh = NULL;
+ }
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL)
+ kfree_skb(skb);
+ }
+
+ d = container_of(e, struct l2t_data, l2tab[e->idx]);
+ atomic_inc(&d->nfree);
+}
+
+/* Locked version of _t4_l2e_free */
static void t4_l2e_free(struct l2t_entry *e)
{
struct l2t_data *d;
+ struct sk_buff *skb;
spin_lock_bh(&e->lock);
if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */
@@ -318,13 +379,8 @@ static void t4_l2e_free(struct l2t_entry *e)
neigh_release(e->neigh);
e->neigh = NULL;
}
- while (e->arpq_head) {
- struct sk_buff *skb = e->arpq_head;
-
- e->arpq_head = skb->next;
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL)
kfree_skb(skb);
- }
- e->arpq_tail = NULL;
}
spin_unlock_bh(&e->lock);
@@ -457,18 +513,19 @@ EXPORT_SYMBOL(cxgb4_select_ntuple);
* on the arpq head. If a packet specifies a failure handler it is invoked,
* otherwise the packet is sent to the device.
*/
-static void handle_failed_resolution(struct adapter *adap, struct sk_buff *arpq)
+static void handle_failed_resolution(struct adapter *adap, struct l2t_entry *e)
{
- while (arpq) {
- struct sk_buff *skb = arpq;
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL) {
const struct l2t_skb_cb *cb = L2T_SKB_CB(skb);
- arpq = skb->next;
- skb->next = NULL;
+ spin_unlock(&e->lock);
if (cb->arp_err_handler)
cb->arp_err_handler(cb->handle, skb);
else
t4_ofld_send(adap, skb);
+ spin_lock(&e->lock);
}
}
@@ -479,7 +536,7 @@ static void handle_failed_resolution(struct adapter *adap, struct sk_buff *arpq)
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
{
struct l2t_entry *e;
- struct sk_buff *arpq = NULL;
+ struct sk_buff_head *arpq = NULL;
struct l2t_data *d = adap->l2t;
int addr_len = neigh->tbl->key_len;
u32 *addr = (u32 *) neigh->primary_key;
@@ -506,10 +563,9 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
if (e->state == L2T_STATE_RESOLVING) {
if (neigh->nud_state & NUD_FAILED) {
- arpq = e->arpq_head;
- e->arpq_head = e->arpq_tail = NULL;
+ arpq = &e->arpq;
} else if ((neigh->nud_state & (NUD_CONNECTED | NUD_STALE)) &&
- e->arpq_head) {
+ !skb_queue_empty(&e->arpq)) {
write_l2e(adap, e, 1);
}
} else {
@@ -519,43 +575,66 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
write_l2e(adap, e, 0);
}
- spin_unlock_bh(&e->lock);
-
if (arpq)
- handle_failed_resolution(adap, arpq);
+ handle_failed_resolution(adap, e);
+ spin_unlock_bh(&e->lock);
}
/* Allocate an L2T entry for use by a switching rule. Such need to be
* explicitly freed and while busy they are not on any hash chain, so normal
* address resolution updates do not see them.
*/
-struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d)
+struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
+ u8 port, u8 *eth_addr)
{
+ struct l2t_data *d = adap->l2t;
struct l2t_entry *e;
+ int ret;
write_lock_bh(&d->lock);
- e = alloc_l2e(d);
+ e = find_or_alloc_l2e(d, vlan, port, eth_addr);
if (e) {
spin_lock(&e->lock); /* avoid race with t4_l2t_free */
- e->state = L2T_STATE_SWITCHING;
- atomic_set(&e->refcnt, 1);
+ if (!atomic_read(&e->refcnt)) {
+ e->state = L2T_STATE_SWITCHING;
+ e->vlan = vlan;
+ e->lport = port;
+ ether_addr_copy(e->dmac, eth_addr);
+ atomic_set(&e->refcnt, 1);
+ ret = write_l2e(adap, e, 0);
+ if (ret < 0) {
+ _t4_l2e_free(e);
+ spin_unlock(&e->lock);
+ write_unlock_bh(&d->lock);
+ return NULL;
+ }
+ } else {
+ atomic_inc(&e->refcnt);
+ }
+
spin_unlock(&e->lock);
}
write_unlock_bh(&d->lock);
return e;
}
-/* Sets/updates the contents of a switching L2T entry that has been allocated
- * with an earlier call to @t4_l2t_alloc_switching.
+/**
+ * @dev: net_device pointer
+ * @vlan: VLAN Id
+ * @port: Associated port
+ * @dmac: Destination MAC address to add to L2T
+ * Returns pointer to the allocated l2t entry
+ *
+ * Allocates an L2T entry for use by switching rule of a filter
*/
-int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan,
- u8 port, u8 *eth_addr)
+struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan,
+ u8 port, u8 *dmac)
{
- e->vlan = vlan;
- e->lport = port;
- memcpy(e->dmac, eth_addr, ETH_ALEN);
- return write_l2e(adap, e, 0);
+ struct adapter *adap = netdev2adap(dev);
+
+ return t4_l2t_alloc_switching(adap, vlan, port, dmac);
}
+EXPORT_SYMBOL(cxgb4_l2t_alloc_switching);
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end)
{
@@ -585,6 +664,7 @@ struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end)
d->l2tab[i].state = L2T_STATE_UNUSED;
spin_lock_init(&d->l2tab[i].lock);
atomic_set(&d->l2tab[i].refcnt, 0);
+ skb_queue_head_init(&d->l2tab[i].arpq);
}
return d;
}
@@ -619,7 +699,8 @@ static char l2e_state(const struct l2t_entry *e)
case L2T_STATE_VALID: return 'V';
case L2T_STATE_STALE: return 'S';
case L2T_STATE_SYNC_WRITE: return 'W';
- case L2T_STATE_RESOLVING: return e->arpq_head ? 'A' : 'R';
+ case L2T_STATE_RESOLVING:
+ return skb_queue_empty(&e->arpq) ? 'R' : 'A';
case L2T_STATE_SWITCHING: return 'X';
default:
return 'U';
diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
index b38dc526aad5..4e2d47ac102b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/l2t.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
@@ -76,8 +76,7 @@ struct l2t_entry {
struct neighbour *neigh; /* associated neighbour */
struct l2t_entry *first; /* start of hash chain */
struct l2t_entry *next; /* next l2t_entry on chain */
- struct sk_buff *arpq_head; /* queue of packets awaiting resolution */
- struct sk_buff *arpq_tail;
+ struct sk_buff_head arpq; /* packet queue awaiting resolution */
spinlock_t lock;
atomic_t refcnt; /* entry reference count */
u16 hash; /* hash bucket the entry is on */
@@ -114,10 +113,11 @@ struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh,
unsigned int priority);
u64 cxgb4_select_ntuple(struct net_device *dev,
const struct l2t_entry *l2t);
+struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan,
+ u8 port, u8 *dmac);
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh);
-struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d);
-int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan,
- u8 port, u8 *eth_addr);
+struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
+ u8 port, u8 *dmac);
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end);
void do_l2t_write_rpl(struct adapter *p, const struct cpl_l2t_write_rpl *rpl);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index b7b93e7a643d..b4eb4680a27c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -406,7 +406,7 @@ static void free_tx_desc(struct adapter *adap, struct sge_txq *q,
*/
static inline int reclaimable(const struct sge_txq *q)
{
- int hw_cidx = ntohs(q->stat->cidx);
+ int hw_cidx = ntohs(ACCESS_ONCE(q->stat->cidx));
hw_cidx -= q->cidx;
return hw_cidx < 0 ? hw_cidx + q->size : hw_cidx;
}
@@ -613,6 +613,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n,
PCI_DMA_FROMDEVICE);
if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) {
__free_pages(pg, s->fl_pg_order);
+ q->mapping_err++;
goto out; /* do not try small pages for this error */
}
mapping |= RX_LARGE_PG_BUF;
@@ -642,6 +643,7 @@ alloc_small_pages:
PCI_DMA_FROMDEVICE);
if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) {
put_page(pg);
+ q->mapping_err++;
goto out;
}
*d++ = cpu_to_be64(mapping);
@@ -663,6 +665,7 @@ out: cred = q->avail - cred;
if (unlikely(fl_starving(adap, q))) {
smp_wmb();
+ q->low++;
set_bit(q->cntxt_id - adap->sge.egr_start,
adap->sge.starving_fl);
}
@@ -1029,6 +1032,30 @@ static void inline_tx_skb(const struct sk_buff *skb, const struct sge_txq *q,
*p = 0;
}
+static void *inline_tx_skb_header(const struct sk_buff *skb,
+ const struct sge_txq *q, void *pos,
+ int length)
+{
+ u64 *p;
+ int left = (void *)q->stat - pos;
+
+ if (likely(length <= left)) {
+ memcpy(pos, skb->data, length);
+ pos += length;
+ } else {
+ memcpy(pos, skb->data, left);
+ memcpy(q->desc, skb->data + left, length - left);
+ pos = (void *)q->desc + (length - left);
+ }
+ /* 0-pad to multiple of 16 */
+ p = PTR_ALIGN(pos, 8);
+ if ((uintptr_t)p & 8) {
+ *p = 0;
+ return p + 1;
+ }
+ return p;
+}
+
/*
* Figure out what HW csum a packet wants and return the appropriate control
* bits.
@@ -1320,7 +1347,7 @@ out_free: dev_kfree_skb_any(skb);
*/
static inline void reclaim_completed_tx_imm(struct sge_txq *q)
{
- int hw_cidx = ntohs(q->stat->cidx);
+ int hw_cidx = ntohs(ACCESS_ONCE(q->stat->cidx));
int reclaim = hw_cidx - q->cidx;
if (reclaim < 0)
@@ -1542,24 +1569,50 @@ static void ofldtxq_stop(struct sge_ofld_txq *q, struct sk_buff *skb)
}
/**
- * service_ofldq - restart a suspended offload queue
+ * service_ofldq - service/restart a suspended offload queue
* @q: the offload queue
*
- * Services an offload Tx queue by moving packets from its packet queue
- * to the HW Tx ring. The function starts and ends with the queue locked.
+ * Services an offload Tx queue by moving packets from its Pending Send
+ * Queue to the Hardware TX ring. The function starts and ends with the
+ * Send Queue locked, but drops the lock while putting the skb at the
+ * head of the Send Queue onto the Hardware TX Ring. Dropping the lock
+ * allows more skbs to be added to the Send Queue by other threads.
+ * The packet being processed at the head of the Pending Send Queue is
+ * left on the queue in case we experience DMA Mapping errors, etc.
+ * and need to give up and restart later.
+ *
+ * service_ofldq() can be thought of as a task which opportunistically
+ * uses other threads execution contexts. We use the Offload Queue
+ * boolean "service_ofldq_running" to make sure that only one instance
+ * is ever running at a time ...
*/
static void service_ofldq(struct sge_ofld_txq *q)
{
- u64 *pos;
+ u64 *pos, *before, *end;
int credits;
struct sk_buff *skb;
+ struct sge_txq *txq;
+ unsigned int left;
unsigned int written = 0;
unsigned int flits, ndesc;
+ /* If another thread is currently in service_ofldq() processing the
+ * Pending Send Queue then there's nothing to do. Otherwise, flag
+ * that we're doing the work and continue. Examining/modifying
+ * the Offload Queue boolean "service_ofldq_running" must be done
+ * while holding the Pending Send Queue Lock.
+ */
+ if (q->service_ofldq_running)
+ return;
+ q->service_ofldq_running = true;
+
while ((skb = skb_peek(&q->sendq)) != NULL && !q->full) {
- /*
- * We drop the lock but leave skb on sendq, thus retaining
- * exclusive access to the state of the queue.
+ /* We drop the lock while we're working with the skb at the
+ * head of the Pending Send Queue. This allows more skbs to
+ * be added to the Pending Send Queue while we're working on
+ * this one. We don't need to lock to guard the TX Ring
+ * updates because only one thread of execution is ever
+ * allowed into service_ofldq() at a time.
*/
spin_unlock(&q->sendq.lock);
@@ -1583,9 +1636,32 @@ static void service_ofldq(struct sge_ofld_txq *q)
} else {
int last_desc, hdr_len = skb_transport_offset(skb);
- memcpy(pos, skb->data, hdr_len);
- write_sgl(skb, &q->q, (void *)pos + hdr_len,
- pos + flits, hdr_len,
+ /* The WR headers may not fit within one descriptor.
+ * So we need to deal with wrap-around here.
+ */
+ before = (u64 *)pos;
+ end = (u64 *)pos + flits;
+ txq = &q->q;
+ pos = (void *)inline_tx_skb_header(skb, &q->q,
+ (void *)pos,
+ hdr_len);
+ if (before > (u64 *)pos) {
+ left = (u8 *)end - (u8 *)txq->stat;
+ end = (void *)txq->desc + left;
+ }
+
+ /* If current position is already at the end of the
+ * ofld queue, reset the current to point to
+ * start of the queue and update the end ptr as well.
+ */
+ if (pos == (u64 *)txq->stat) {
+ left = (u8 *)end - (u8 *)txq->stat;
+ end = (void *)txq->desc + left;
+ pos = (void *)txq->desc;
+ }
+
+ write_sgl(skb, &q->q, (void *)pos,
+ end, hdr_len,
(dma_addr_t *)skb->head);
#ifdef CONFIG_NEED_DMA_MAP_STATE
skb->dev = q->adap->port[0];
@@ -1604,6 +1680,11 @@ static void service_ofldq(struct sge_ofld_txq *q)
written = 0;
}
+ /* Reacquire the Pending Send Queue Lock so we can unlink the
+ * skb we've just successfully transferred to the TX Ring and
+ * loop for the next skb which may be at the head of the
+ * Pending Send Queue.
+ */
spin_lock(&q->sendq.lock);
__skb_unlink(skb, &q->sendq);
if (is_ofld_imm(skb))
@@ -1611,6 +1692,11 @@ static void service_ofldq(struct sge_ofld_txq *q)
}
if (likely(written))
ring_tx_db(q->adap, &q->q, written);
+
+ /*Indicate that no thread is processing the Pending Send Queue
+ * currently.
+ */
+ q->service_ofldq_running = false;
}
/**
@@ -1624,9 +1710,19 @@ static int ofld_xmit(struct sge_ofld_txq *q, struct sk_buff *skb)
{
skb->priority = calc_tx_flits_ofld(skb); /* save for restart */
spin_lock(&q->sendq.lock);
+
+ /* Queue the new skb onto the Offload Queue's Pending Send Queue. If
+ * that results in this new skb being the only one on the queue, start
+ * servicing it. If there are other skbs already on the list, then
+ * either the queue is currently being processed or it's been stopped
+ * for some reason and it'll be restarted at a later time. Restart
+ * paths are triggered by events like experiencing a DMA Mapping Error
+ * or filling the Hardware TX Ring.
+ */
__skb_queue_tail(&q->sendq, skb);
if (q->sendq.qlen == 1)
service_ofldq(q);
+
spin_unlock(&q->sendq.lock);
return NET_XMIT_SUCCESS;
}
@@ -1864,7 +1960,6 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
skb->truesize += skb->data_len;
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(skb, rxq->rspq.idx);
- skb_mark_napi_id(skb, &rxq->rspq.napi);
pi = netdev_priv(skb->dev);
if (pi->rxtstamp)
cxgb4_sgetim_to_hwtstamp(adapter, skb_hwtstamps(skb),
@@ -2193,7 +2288,7 @@ static int napi_rx_handler(struct napi_struct *napi, int budget)
if (likely(work_done < budget)) {
int timer_index;
- napi_complete(napi);
+ napi_complete_done(napi, work_done);
timer_index = QINTR_TIMER_IDX_G(q->next_intr_params);
if (q->adaptive_rx) {
@@ -2460,7 +2555,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
iq->size = roundup(iq->size, 16);
iq->desc = alloc_ring(adap->pdev_dev, iq->size, iq->iqe_len, 0,
- &iq->phys_addr, NULL, 0, NUMA_NO_NODE);
+ &iq->phys_addr, NULL, 0,
+ dev_to_node(adap->pdev_dev));
if (!iq->desc)
return -ENOMEM;
@@ -2500,7 +2596,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
fl->size = roundup(fl->size, 8);
fl->desc = alloc_ring(adap->pdev_dev, fl->size, sizeof(__be64),
sizeof(struct rx_sw_desc), &fl->addr,
- &fl->sdesc, s->stat_len, NUMA_NO_NODE);
+ &fl->sdesc, s->stat_len,
+ dev_to_node(adap->pdev_dev));
if (!fl->desc)
goto fl_nomem;
@@ -2528,7 +2625,6 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
goto err;
netif_napi_add(dev, &iq->napi, napi_rx_handler, 64);
- napi_hash_add(&iq->napi);
iq->cur_desc = iq->desc;
iq->cidx = 0;
iq->gen = 1;
@@ -2574,8 +2670,9 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
* simple (and hopefully less wrong).
*/
if (!is_t4(adap->params.chip) && cong >= 0) {
- u32 param, val;
+ u32 param, val, ch_map = 0;
int i;
+ u16 cng_ch_bits_log = adap->params.arch.cng_ch_bits_log;
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_CONM_CTXT) |
@@ -2587,9 +2684,9 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
CONMCTXT_CNGTPMODE_V(CONMCTXT_CNGTPMODE_CHANNEL_X);
for (i = 0; i < 4; i++) {
if (cong & (1 << i))
- val |=
- CONMCTXT_CNGCHMAP_V(1 << (i << 2));
+ ch_map |= 1 << (i << cng_ch_bits_log);
}
+ val |= CONMCTXT_CNGCHMAP_V(ch_map);
}
ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1,
&param, &val);
@@ -2884,7 +2981,7 @@ void t4_free_sge_resources(struct adapter *adap)
}
/* clean up RDMA and iSCSI Rx queues */
- t4_free_ofld_rxqs(adap, adap->sge.ofldqsets, adap->sge.ofldrxq);
+ t4_free_ofld_rxqs(adap, adap->sge.iscsiqsets, adap->sge.iscsirxq);
t4_free_ofld_rxqs(adap, adap->sge.rdmaqs, adap->sge.rdmarxq);
t4_free_ofld_rxqs(adap, adap->sge.rdmaciqs, adap->sge.rdmaciq);
@@ -3077,8 +3174,7 @@ static int t4_sge_init_soft(struct adapter *adap)
int t4_sge_init(struct adapter *adap)
{
struct sge *s = &adap->sge;
- u32 sge_control, sge_control2, sge_conm_ctrl;
- unsigned int ingpadboundary, ingpackboundary;
+ u32 sge_control, sge_conm_ctrl;
int ret, egress_threshold;
/*
@@ -3089,35 +3185,7 @@ int t4_sge_init(struct adapter *adap)
s->pktshift = PKTSHIFT_G(sge_control);
s->stat_len = (sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64;
- /* T4 uses a single control field to specify both the PCIe Padding and
- * Packing Boundary. T5 introduced the ability to specify these
- * separately. The actual Ingress Packet Data alignment boundary
- * within Packed Buffer Mode is the maximum of these two
- * specifications. (Note that it makes no real practical sense to
- * have the Pading Boudary be larger than the Packing Boundary but you
- * could set the chip up that way and, in fact, legacy T4 code would
- * end doing this because it would initialize the Padding Boundary and
- * leave the Packing Boundary initialized to 0 (16 bytes).)
- */
- ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) +
- INGPADBOUNDARY_SHIFT_X);
- if (is_t4(adap->params.chip)) {
- s->fl_align = ingpadboundary;
- } else {
- /* T5 has a different interpretation of one of the PCIe Packing
- * Boundary values.
- */
- sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A);
- ingpackboundary = INGPACKBOUNDARY_G(sge_control2);
- if (ingpackboundary == INGPACKBOUNDARY_16B_X)
- ingpackboundary = 16;
- else
- ingpackboundary = 1 << (ingpackboundary +
- INGPACKBOUNDARY_SHIFT_X);
-
- s->fl_align = max(ingpadboundary, ingpackboundary);
- }
-
+ s->fl_align = t4_fl_pkt_align(adap);
ret = t4_sge_init_soft(adap);
if (ret < 0)
return ret;
@@ -3135,10 +3203,21 @@ int t4_sge_init(struct adapter *adap)
* buffers.
*/
sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL_A);
- if (is_t4(adap->params.chip))
+ switch (CHELSIO_CHIP_VERSION(adap->params.chip)) {
+ case CHELSIO_T4:
egress_threshold = EGRTHRESHOLD_G(sge_conm_ctrl);
- else
+ break;
+ case CHELSIO_T5:
egress_threshold = EGRTHRESHOLDPACKING_G(sge_conm_ctrl);
+ break;
+ case CHELSIO_T6:
+ egress_threshold = T6_EGRTHRESHOLDPACKING_G(sge_conm_ctrl);
+ break;
+ default:
+ dev_err(adap->pdev_dev, "Unsupported Chip version %d\n",
+ CHELSIO_CHIP_VERSION(adap->params.chip));
+ return -EINVAL;
+ }
s->fl_starve_thres = 2*egress_threshold + 1;
t4_idma_monitor_init(adap, &s->idma_monitor);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index cf61a5869c6e..636b4691f252 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -1942,8 +1942,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x1190, 0x1194,
0x11a0, 0x11a4,
0x11b0, 0x11b4,
- 0x11fc, 0x1254,
- 0x1280, 0x133c,
+ 0x11fc, 0x1258,
+ 0x1280, 0x12d4,
+ 0x12d9, 0x12d9,
+ 0x12de, 0x12de,
+ 0x12e3, 0x12e3,
+ 0x12e8, 0x133c,
0x1800, 0x18fc,
0x3000, 0x302c,
0x3060, 0x30b0,
@@ -1973,7 +1977,7 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x5e50, 0x5e94,
0x5ea0, 0x5eb0,
0x5ec0, 0x5ec0,
- 0x5ec8, 0x5ecc,
+ 0x5ec8, 0x5ed0,
0x6000, 0x6020,
0x6028, 0x6040,
0x6058, 0x609c,
@@ -2048,7 +2052,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x19150, 0x19194,
0x1919c, 0x191b0,
0x191d0, 0x191e8,
- 0x19238, 0x192b0,
+ 0x19238, 0x19290,
+ 0x192a4, 0x192b0,
0x192bc, 0x192bc,
0x19348, 0x1934c,
0x193f8, 0x19418,
@@ -2442,7 +2447,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x40280, 0x40280,
0x40304, 0x40304,
0x40330, 0x4033c,
- 0x41304, 0x413c8,
+ 0x41304, 0x413b8,
+ 0x413c0, 0x413c8,
0x413d0, 0x413dc,
0x413f0, 0x413f0,
0x41400, 0x4140c,
@@ -5254,7 +5260,7 @@ void t4_pmtx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[])
int i;
u32 data[2];
- for (i = 0; i < PM_NSTATS; i++) {
+ for (i = 0; i < adap->params.arch.pm_stats_cnt; i++) {
t4_write_reg(adap, PM_TX_STAT_CONFIG_A, i + 1);
cnt[i] = t4_read_reg(adap, PM_TX_STAT_COUNT_A);
if (is_t4(adap->params.chip)) {
@@ -5281,7 +5287,7 @@ void t4_pmrx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[])
int i;
u32 data[2];
- for (i = 0; i < PM_NSTATS; i++) {
+ for (i = 0; i < adap->params.arch.pm_stats_cnt; i++) {
t4_write_reg(adap, PM_RX_STAT_CONFIG_A, i + 1);
cnt[i] = t4_read_reg(adap, PM_RX_STAT_COUNT_A);
if (is_t4(adap->params.chip)) {
@@ -5310,7 +5316,14 @@ unsigned int t4_get_mps_bg_map(struct adapter *adap, int idx)
if (n == 0)
return idx == 0 ? 0xf : 0;
- if (n == 1)
+ /* In T6 (which is a 2 port card),
+ * port 0 is mapped to channel 0 and port 1 is mapped to channel 1.
+ * For 2 port T4/T5 adapter,
+ * port 0 is mapped to channel 0 and 1,
+ * port 1 is mapped to channel 2 and 3.
+ */
+ if ((n == 1) &&
+ (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5))
return idx < 2 ? (3 << (2 * idx)) : 0;
return 1 << idx;
}
@@ -5689,6 +5702,39 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state)
"IDMA_FL_SEND_PADDING",
"IDMA_FL_SEND_COMPLETION_TO_IMSG",
};
+ static const char * const t6_decode[] = {
+ "IDMA_IDLE",
+ "IDMA_PUSH_MORE_CPL_FIFO",
+ "IDMA_PUSH_CPL_MSG_HEADER_TO_FIFO",
+ "IDMA_SGEFLRFLUSH_SEND_PCIEHDR",
+ "IDMA_PHYSADDR_SEND_PCIEHDR",
+ "IDMA_PHYSADDR_SEND_PAYLOAD_FIRST",
+ "IDMA_PHYSADDR_SEND_PAYLOAD",
+ "IDMA_FL_REQ_DATA_FL",
+ "IDMA_FL_DROP",
+ "IDMA_FL_DROP_SEND_INC",
+ "IDMA_FL_H_REQ_HEADER_FL",
+ "IDMA_FL_H_SEND_PCIEHDR",
+ "IDMA_FL_H_PUSH_CPL_FIFO",
+ "IDMA_FL_H_SEND_CPL",
+ "IDMA_FL_H_SEND_IP_HDR_FIRST",
+ "IDMA_FL_H_SEND_IP_HDR",
+ "IDMA_FL_H_REQ_NEXT_HEADER_FL",
+ "IDMA_FL_H_SEND_NEXT_PCIEHDR",
+ "IDMA_FL_H_SEND_IP_HDR_PADDING",
+ "IDMA_FL_D_SEND_PCIEHDR",
+ "IDMA_FL_D_SEND_CPL_AND_IP_HDR",
+ "IDMA_FL_D_REQ_NEXT_DATA_FL",
+ "IDMA_FL_SEND_PCIEHDR",
+ "IDMA_FL_PUSH_CPL_FIFO",
+ "IDMA_FL_SEND_CPL",
+ "IDMA_FL_SEND_PAYLOAD_FIRST",
+ "IDMA_FL_SEND_PAYLOAD",
+ "IDMA_FL_REQ_NEXT_DATA_FL",
+ "IDMA_FL_SEND_NEXT_PCIEHDR",
+ "IDMA_FL_SEND_PADDING",
+ "IDMA_FL_SEND_COMPLETION_TO_IMSG",
+ };
static const u32 sge_regs[] = {
SGE_DEBUG_DATA_LOW_INDEX_2_A,
SGE_DEBUG_DATA_LOW_INDEX_3_A,
@@ -5697,6 +5743,32 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state)
const char **sge_idma_decode;
int sge_idma_decode_nstates;
int i;
+ unsigned int chip_version = CHELSIO_CHIP_VERSION(adapter->params.chip);
+
+ /* Select the right set of decode strings to dump depending on the
+ * adapter chip type.
+ */
+ switch (chip_version) {
+ case CHELSIO_T4:
+ sge_idma_decode = (const char **)t4_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t4_decode);
+ break;
+
+ case CHELSIO_T5:
+ sge_idma_decode = (const char **)t5_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t5_decode);
+ break;
+
+ case CHELSIO_T6:
+ sge_idma_decode = (const char **)t6_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t6_decode);
+ break;
+
+ default:
+ dev_err(adapter->pdev_dev,
+ "Unsupported chip version %d\n", chip_version);
+ return;
+ }
if (is_t4(adapter->params.chip)) {
sge_idma_decode = (const char **)t4_decode;
@@ -6097,6 +6169,59 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox,
}
/**
+ * t4_fl_pkt_align - return the fl packet alignment
+ * @adap: the adapter
+ *
+ * T4 has a single field to specify the packing and padding boundary.
+ * T5 onwards has separate fields for this and hence the alignment for
+ * next packet offset is maximum of these two.
+ *
+ */
+int t4_fl_pkt_align(struct adapter *adap)
+{
+ u32 sge_control, sge_control2;
+ unsigned int ingpadboundary, ingpackboundary, fl_align, ingpad_shift;
+
+ sge_control = t4_read_reg(adap, SGE_CONTROL_A);
+
+ /* T4 uses a single control field to specify both the PCIe Padding and
+ * Packing Boundary. T5 introduced the ability to specify these
+ * separately. The actual Ingress Packet Data alignment boundary
+ * within Packed Buffer Mode is the maximum of these two
+ * specifications. (Note that it makes no real practical sense to
+ * have the Pading Boudary be larger than the Packing Boundary but you
+ * could set the chip up that way and, in fact, legacy T4 code would
+ * end doing this because it would initialize the Padding Boundary and
+ * leave the Packing Boundary initialized to 0 (16 bytes).)
+ * Padding Boundary values in T6 starts from 8B,
+ * where as it is 32B for T4 and T5.
+ */
+ if (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5)
+ ingpad_shift = INGPADBOUNDARY_SHIFT_X;
+ else
+ ingpad_shift = T6_INGPADBOUNDARY_SHIFT_X;
+
+ ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) + ingpad_shift);
+
+ fl_align = ingpadboundary;
+ if (!is_t4(adap->params.chip)) {
+ /* T5 has a weird interpretation of one of the PCIe Packing
+ * Boundary values. No idea why ...
+ */
+ sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A);
+ ingpackboundary = INGPACKBOUNDARY_G(sge_control2);
+ if (ingpackboundary == INGPACKBOUNDARY_16B_X)
+ ingpackboundary = 16;
+ else
+ ingpackboundary = 1 << (ingpackboundary +
+ INGPACKBOUNDARY_SHIFT_X);
+
+ fl_align = max(ingpadboundary, ingpackboundary);
+ }
+ return fl_align;
+}
+
+/**
* t4_fixup_host_params - fix up host-dependent parameters
* @adap: the adapter
* @page_size: the host's Base Page Size
@@ -6114,6 +6239,7 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,
unsigned int stat_len = cache_line_size > 64 ? 128 : 64;
unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size;
unsigned int fl_align_log = fls(fl_align) - 1;
+ unsigned int ingpad;
t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A,
HOSTPAGESIZEPF0_V(sge_hps) |
@@ -6161,10 +6287,16 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,
fl_align = 64;
fl_align_log = 6;
}
+
+ if (is_t5(adap->params.chip))
+ ingpad = INGPCIEBOUNDARY_32B_X;
+ else
+ ingpad = T6_INGPADBOUNDARY_32B_X;
+
t4_set_reg_field(adap, SGE_CONTROL_A,
INGPADBOUNDARY_V(INGPADBOUNDARY_M) |
EGRSTATUSPAGESIZE_F,
- INGPADBOUNDARY_V(INGPCIEBOUNDARY_32B_X) |
+ INGPADBOUNDARY_V(ingpad) |
EGRSTATUSPAGESIZE_V(stat_len != 64));
t4_set_reg_field(adap, SGE_CONTROL2_A,
INGPACKBOUNDARY_V(INGPACKBOUNDARY_M),
@@ -7060,7 +7192,12 @@ int t4_prep_adapter(struct adapter *adapter)
NUM_MPS_CLS_SRAM_L_INSTANCES;
adapter->params.arch.mps_rplc_size = 128;
adapter->params.arch.nchan = NCHAN;
+ adapter->params.arch.pm_stats_cnt = PM_NSTATS;
adapter->params.arch.vfcount = 128;
+ /* Congestion map is for 4 channels so that
+ * MPS can have 4 priority per port.
+ */
+ adapter->params.arch.cng_ch_bits_log = 2;
break;
case CHELSIO_T5:
adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T5, pl_rev);
@@ -7069,7 +7206,9 @@ int t4_prep_adapter(struct adapter *adapter)
NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
adapter->params.arch.mps_rplc_size = 128;
adapter->params.arch.nchan = NCHAN;
+ adapter->params.arch.pm_stats_cnt = PM_NSTATS;
adapter->params.arch.vfcount = 128;
+ adapter->params.arch.cng_ch_bits_log = 2;
break;
case CHELSIO_T6:
adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T6, pl_rev);
@@ -7078,7 +7217,12 @@ int t4_prep_adapter(struct adapter *adapter)
NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
adapter->params.arch.mps_rplc_size = 256;
adapter->params.arch.nchan = 2;
+ adapter->params.arch.pm_stats_cnt = T6_PM_NSTATS;
adapter->params.arch.vfcount = 256;
+ /* Congestion map will be for 2 channels so that
+ * MPS can have 8 priority per port.
+ */
+ adapter->params.arch.cng_ch_bits_log = 3;
break;
default:
dev_err(adapter->pdev_dev, "Device %d is not supported\n",
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
index 13708fde1668..2fc60e83a7a1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
@@ -48,6 +48,7 @@ enum {
NMTUS = 16, /* size of MTU table */
NCCTRL_WIN = 32, /* # of congestion control windows */
PM_NSTATS = 5, /* # of PM stats */
+ T6_PM_NSTATS = 7, /* # of PM stats in T6 */
MBOX_LEN = 64, /* mailbox size in bytes */
TRACE_LEN = 112, /* length of trace data and mask */
FILTER_OPT_LEN = 36, /* filter tuple width for optional components */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
index 03ed00c49823..a8dda635456d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
@@ -162,6 +162,9 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
CH_PCI_ID_TABLE_FENTRY(0x5095), /* Custom T540-CR-SO */
CH_PCI_ID_TABLE_FENTRY(0x5096), /* Custom T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x5097), /* Custom T520-KR */
+ CH_PCI_ID_TABLE_FENTRY(0x5098), /* Custom 2x40G QSFP */
+ CH_PCI_ID_TABLE_FENTRY(0x5099), /* Custom 2x40G QSFP */
+ CH_PCI_ID_TABLE_FENTRY(0x509a), /* Custom T520-CR */
/* T6 adapters:
*/
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index fc3044c8ac1c..9fea255c7e87 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
@@ -492,6 +492,9 @@
#define STATSOURCE_T5_V(x) ((x) << STATSOURCE_T5_S)
#define STATSOURCE_T5_G(x) (((x) >> STATSOURCE_T5_S) & STATSOURCE_T5_M)
+#define T6_STATMODE_S 0
+#define T6_STATMODE_V(x) ((x) << T6_STATMODE_S)
+
#define SGE_DBFIFO_STATUS2_A 0x1118
#define HP_INT_THRESH_T5_S 10
@@ -2395,6 +2398,30 @@
#define MPS_CLS_TCAM_DATA0_A 0xf000
#define MPS_CLS_TCAM_DATA1_A 0xf004
+#define VIDL_S 16
+#define VIDL_M 0xffffU
+#define VIDL_G(x) (((x) >> VIDL_S) & VIDL_M)
+
+#define DATALKPTYPE_S 10
+#define DATALKPTYPE_M 0x3U
+#define DATALKPTYPE_G(x) (((x) >> DATALKPTYPE_S) & DATALKPTYPE_M)
+
+#define DATAPORTNUM_S 12
+#define DATAPORTNUM_M 0xfU
+#define DATAPORTNUM_G(x) (((x) >> DATAPORTNUM_S) & DATAPORTNUM_M)
+
+#define DATADIPHIT_S 8
+#define DATADIPHIT_V(x) ((x) << DATADIPHIT_S)
+#define DATADIPHIT_F DATADIPHIT_V(1U)
+
+#define DATAVIDH2_S 7
+#define DATAVIDH2_V(x) ((x) << DATAVIDH2_S)
+#define DATAVIDH2_F DATAVIDH2_V(1U)
+
+#define DATAVIDH1_S 0
+#define DATAVIDH1_M 0x7fU
+#define DATAVIDH1_G(x) (((x) >> DATAVIDH1_S) & DATAVIDH1_M)
+
#define USED_S 16
#define USED_M 0x7ffU
#define USED_G(x) (((x) >> USED_S) & USED_M)
@@ -2802,6 +2829,10 @@
#define HASHEN_V(x) ((x) << HASHEN_S)
#define HASHEN_F HASHEN_V(1U)
+#define ASLIPCOMPEN_S 17
+#define ASLIPCOMPEN_V(x) ((x) << ASLIPCOMPEN_S)
+#define ASLIPCOMPEN_F ASLIPCOMPEN_V(1U)
+
#define REQQPARERR_S 16
#define REQQPARERR_V(x) ((x) << REQQPARERR_S)
#define REQQPARERR_F REQQPARERR_V(1U)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
index 7bdee3bf75ec..a5231fa771db 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
@@ -53,6 +53,9 @@
#define INGPADBOUNDARY_SHIFT_X 5
+#define T6_INGPADBOUNDARY_SHIFT_X 3
+#define T6_INGPADBOUNDARY_32B_X 2
+
/* CONTROL2 register */
#define INGPACKBOUNDARY_SHIFT_X 5
#define INGPACKBOUNDARY_16B_X 0
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index fa3786a9d30e..6528231d8a59 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -2607,7 +2607,7 @@ int t4vf_sge_init(struct adapter *adapter)
u32 fl0 = sge_params->sge_fl_buffer_size[0];
u32 fl1 = sge_params->sge_fl_buffer_size[1];
struct sge *s = &adapter->sge;
- unsigned int ingpadboundary, ingpackboundary;
+ unsigned int ingpadboundary, ingpackboundary, ingpad_shift;
/*
* Start by vetting the basic SGE parameters which have been set up by
@@ -2642,9 +2642,16 @@ int t4vf_sge_init(struct adapter *adapter)
* could set the chip up that way and, in fact, legacy T4 code would
* end doing this because it would initialize the Padding Boundary and
* leave the Packing Boundary initialized to 0 (16 bytes).)
+ * Padding Boundary values in T6 starts from 8B,
+ * where as it is 32B for T4 and T5.
*/
+ if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5)
+ ingpad_shift = INGPADBOUNDARY_SHIFT_X;
+ else
+ ingpad_shift = T6_INGPADBOUNDARY_SHIFT_X;
+
ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_params->sge_control) +
- INGPADBOUNDARY_SHIFT_X);
+ ingpad_shift);
if (is_t4(adapter->params.chip)) {
s->fl_align = ingpadboundary;
} else {
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
index b516b12b1884..f859db3d254c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
@@ -54,6 +54,7 @@
#define T4VF_MPS_BASE_ADDR 0x0100
#define T4VF_PL_BASE_ADDR 0x0200
#define T4VF_MBDATA_BASE_ADDR 0x0240
+#define T6VF_MBDATA_BASE_ADDR 0x0280
#define T4VF_CIM_BASE_ADDR 0x0300
#define T4VF_REGMAP_START 0x0000
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
index 63dd5fdac5b9..b6fa74aafe47 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
@@ -120,12 +120,19 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size,
1, 1, 3, 5, 10, 10, 20, 50, 100
};
- u32 v;
+ u32 v, mbox_data;
int i, ms, delay_idx;
const __be64 *p;
- u32 mbox_data = T4VF_MBDATA_BASE_ADDR;
u32 mbox_ctl = T4VF_CIM_BASE_ADDR + CIM_VF_EXT_MAILBOX_CTRL;
+ /* In T6, mailbox size is changed to 128 bytes to avoid
+ * invalidating the entire prefetch buffer.
+ */
+ if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5)
+ mbox_data = T4VF_MBDATA_BASE_ADDR;
+ else
+ mbox_data = T6VF_MBDATA_BASE_ADDR;
+
/*
* Commands must be multiples of 16 bytes in length and may not be
* larger than the size of the Mailbox Data register array.
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index b36643ef0593..b2182d3ba3cc 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -2458,13 +2458,11 @@ static int enic_dev_init(struct enic *enic)
switch (vnic_dev_get_intr_mode(enic->vdev)) {
default:
netif_napi_add(netdev, &enic->napi[0], enic_poll, 64);
- napi_hash_add(&enic->napi[0]);
break;
case VNIC_DEV_INTR_MODE_MSIX:
for (i = 0; i < enic->rq_count; i++) {
netif_napi_add(netdev, &enic->napi[i],
enic_poll_msix_rq, NAPI_POLL_WEIGHT);
- napi_hash_add(&enic->napi[i]);
}
for (i = 0; i < enic->wq_count; i++)
netif_napi_add(netdev, &enic->napi[enic_cq_wq(enic, i)],
diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c
index 8966f3159bb2..3acde3b9b767 100644
--- a/drivers/net/ethernet/dec/tulip/de4x5.c
+++ b/drivers/net/ethernet/dec/tulip/de4x5.c
@@ -1990,7 +1990,7 @@ SetMulticastFilter(struct net_device *dev)
static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
-static int __init de4x5_eisa_probe (struct device *gendev)
+static int de4x5_eisa_probe(struct device *gendev)
{
struct eisa_device *edev;
u_long iobase;
diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c
index ccca4799c27b..f92b6d948398 100644
--- a/drivers/net/ethernet/dlink/dl2k.c
+++ b/drivers/net/ethernet/dlink/dl2k.c
@@ -70,7 +70,6 @@ static const int multicast_filter_limit = 0x40;
static int rio_open (struct net_device *dev);
static void rio_timer (unsigned long data);
static void rio_tx_timeout (struct net_device *dev);
-static void alloc_list (struct net_device *dev);
static netdev_tx_t start_xmit (struct sk_buff *skb, struct net_device *dev);
static irqreturn_t rio_interrupt (int irq, void *dev_instance);
static void rio_free_tx (struct net_device *dev, int irq);
@@ -253,19 +252,6 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_out_unmap_rx;
- if (np->chip_id == CHIP_IP1000A &&
- (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) {
- /* PHY magic taken from ipg driver, undocumented registers */
- mii_write(dev, np->phy_addr, 31, 0x0001);
- mii_write(dev, np->phy_addr, 27, 0x01e0);
- mii_write(dev, np->phy_addr, 31, 0x0002);
- mii_write(dev, np->phy_addr, 27, 0xeb8e);
- mii_write(dev, np->phy_addr, 31, 0x0000);
- mii_write(dev, np->phy_addr, 30, 0x005e);
- /* advertise 1000BASE-T half & full duplex, prefer MASTER */
- mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700);
- }
-
/* Fiber device? */
np->phy_media = (dr16(ASICCtrl) & PhyMedia) ? 1 : 0;
np->link_status = 0;
@@ -275,13 +261,11 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
if (np->an_enable == 2) {
np->an_enable = 1;
}
- mii_set_media_pcs (dev);
} else {
/* Auto-Negotiation is mandatory for 1000BASE-T,
IEEE 802.3ab Annex 28D page 14 */
if (np->speed == 1000)
np->an_enable = 1;
- mii_set_media (dev);
}
err = register_netdev (dev);
@@ -446,19 +430,106 @@ static void rio_set_led_mode(struct net_device *dev)
dw32(ASICCtrl, mode);
}
-static int
-rio_open (struct net_device *dev)
+static inline dma_addr_t desc_to_dma(struct netdev_desc *desc)
+{
+ return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48);
+}
+
+static void free_list(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ struct sk_buff *skb;
+ int i;
+
+ /* Free all the skbuffs in the queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ skb = np->rx_skbuff[i];
+ if (skb) {
+ pci_unmap_single(np->pdev, desc_to_dma(&np->rx_ring[i]),
+ skb->len, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(skb);
+ np->rx_skbuff[i] = NULL;
+ }
+ np->rx_ring[i].status = 0;
+ np->rx_ring[i].fraginfo = 0;
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ skb = np->tx_skbuff[i];
+ if (skb) {
+ pci_unmap_single(np->pdev, desc_to_dma(&np->tx_ring[i]),
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb(skb);
+ np->tx_skbuff[i] = NULL;
+ }
+ }
+}
+
+static void rio_reset_ring(struct netdev_private *np)
+{
+ int i;
+
+ np->cur_rx = 0;
+ np->cur_tx = 0;
+ np->old_rx = 0;
+ np->old_tx = 0;
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ np->tx_ring[i].status = cpu_to_le64(TFDDone);
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ np->rx_ring[i].status = 0;
+}
+
+ /* allocate and initialize Tx and Rx descriptors */
+static int alloc_list(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ int i;
+
+ rio_reset_ring(np);
+ np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32);
+
+ /* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ np->tx_skbuff[i] = NULL;
+ np->tx_ring[i].next_desc = cpu_to_le64(np->tx_ring_dma +
+ ((i + 1) % TX_RING_SIZE) *
+ sizeof(struct netdev_desc));
+ }
+
+ /* Initialize Rx descriptors & allocate buffers */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ /* Allocated fixed size of skbuff */
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz);
+ np->rx_skbuff[i] = skb;
+ if (!skb) {
+ free_list(dev);
+ return -ENOMEM;
+ }
+
+ np->rx_ring[i].next_desc = cpu_to_le64(np->rx_ring_dma +
+ ((i + 1) % RX_RING_SIZE) *
+ sizeof(struct netdev_desc));
+ /* Rubicon now supports 40 bits of addressing space. */
+ np->rx_ring[i].fraginfo =
+ cpu_to_le64(pci_map_single(
+ np->pdev, skb->data, np->rx_buf_sz,
+ PCI_DMA_FROMDEVICE));
+ np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48);
+ }
+
+ return 0;
+}
+
+static void rio_hw_init(struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
void __iomem *ioaddr = np->ioaddr;
- const int irq = np->pdev->irq;
int i;
u16 macctrl;
- i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev);
- if (i)
- return i;
-
/* Reset all logic functions */
dw16(ASICCtrl + 2,
GlobalReset | DMAReset | FIFOReset | NetworkReset | HostReset);
@@ -469,11 +540,31 @@ rio_open (struct net_device *dev)
/* DebugCtrl bit 4, 5, 9 must set */
dw32(DebugCtrl, dr32(DebugCtrl) | 0x0230);
+ if (np->chip_id == CHIP_IP1000A &&
+ (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) {
+ /* PHY magic taken from ipg driver, undocumented registers */
+ mii_write(dev, np->phy_addr, 31, 0x0001);
+ mii_write(dev, np->phy_addr, 27, 0x01e0);
+ mii_write(dev, np->phy_addr, 31, 0x0002);
+ mii_write(dev, np->phy_addr, 27, 0xeb8e);
+ mii_write(dev, np->phy_addr, 31, 0x0000);
+ mii_write(dev, np->phy_addr, 30, 0x005e);
+ /* advertise 1000BASE-T half & full duplex, prefer MASTER */
+ mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700);
+ }
+
+ if (np->phy_media)
+ mii_set_media_pcs(dev);
+ else
+ mii_set_media(dev);
+
/* Jumbo frame */
if (np->jumbo != 0)
dw16(MaxFrameSize, MAX_JUMBO+14);
- alloc_list (dev);
+ /* Set RFDListPtr */
+ dw32(RFDListPtr0, np->rx_ring_dma);
+ dw32(RFDListPtr1, 0);
/* Set station address */
/* 16 or 32-bit access is required by TC9020 datasheet but 8-bit works
@@ -509,10 +600,6 @@ rio_open (struct net_device *dev)
dw32(MACCtrl, dr32(MACCtrl) | AutoVLANuntagging);
}
- setup_timer(&np->timer, rio_timer, (unsigned long)dev);
- np->timer.expires = jiffies + 1*HZ;
- add_timer (&np->timer);
-
/* Start Tx/Rx */
dw32(MACCtrl, dr32(MACCtrl) | StatsEnable | RxEnable | TxEnable);
@@ -522,6 +609,42 @@ rio_open (struct net_device *dev)
macctrl |= (np->tx_flow) ? TxFlowControlEnable : 0;
macctrl |= (np->rx_flow) ? RxFlowControlEnable : 0;
dw16(MACCtrl, macctrl);
+}
+
+static void rio_hw_stop(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->ioaddr;
+
+ /* Disable interrupts */
+ dw16(IntEnable, 0);
+
+ /* Stop Tx and Rx logics */
+ dw32(MACCtrl, TxDisable | RxDisable | StatsDisable);
+}
+
+static int rio_open(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ const int irq = np->pdev->irq;
+ int i;
+
+ i = alloc_list(dev);
+ if (i)
+ return i;
+
+ rio_hw_init(dev);
+
+ i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev);
+ if (i) {
+ rio_hw_stop(dev);
+ free_list(dev);
+ return i;
+ }
+
+ setup_timer(&np->timer, rio_timer, (unsigned long)dev);
+ np->timer.expires = jiffies + 1 * HZ;
+ add_timer(&np->timer);
netif_start_queue (dev);
@@ -586,60 +709,6 @@ rio_tx_timeout (struct net_device *dev)
dev->trans_start = jiffies; /* prevent tx timeout */
}
- /* allocate and initialize Tx and Rx descriptors */
-static void
-alloc_list (struct net_device *dev)
-{
- struct netdev_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->ioaddr;
- int i;
-
- np->cur_rx = np->cur_tx = 0;
- np->old_rx = np->old_tx = 0;
- np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32);
-
- /* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */
- for (i = 0; i < TX_RING_SIZE; i++) {
- np->tx_skbuff[i] = NULL;
- np->tx_ring[i].status = cpu_to_le64 (TFDDone);
- np->tx_ring[i].next_desc = cpu_to_le64 (np->tx_ring_dma +
- ((i+1)%TX_RING_SIZE) *
- sizeof (struct netdev_desc));
- }
-
- /* Initialize Rx descriptors */
- for (i = 0; i < RX_RING_SIZE; i++) {
- np->rx_ring[i].next_desc = cpu_to_le64 (np->rx_ring_dma +
- ((i + 1) % RX_RING_SIZE) *
- sizeof (struct netdev_desc));
- np->rx_ring[i].status = 0;
- np->rx_ring[i].fraginfo = 0;
- np->rx_skbuff[i] = NULL;
- }
-
- /* Allocate the rx buffers */
- for (i = 0; i < RX_RING_SIZE; i++) {
- /* Allocated fixed size of skbuff */
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz);
- np->rx_skbuff[i] = skb;
- if (skb == NULL)
- break;
-
- /* Rubicon now supports 40 bits of addressing space. */
- np->rx_ring[i].fraginfo =
- cpu_to_le64 ( pci_map_single (
- np->pdev, skb->data, np->rx_buf_sz,
- PCI_DMA_FROMDEVICE));
- np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48);
- }
-
- /* Set RFDListPtr */
- dw32(RFDListPtr0, np->rx_ring_dma);
- dw32(RFDListPtr1, 0);
-}
-
static netdev_tx_t
start_xmit (struct sk_buff *skb, struct net_device *dev)
{
@@ -748,11 +817,6 @@ rio_interrupt (int irq, void *dev_instance)
return IRQ_RETVAL(handled);
}
-static inline dma_addr_t desc_to_dma(struct netdev_desc *desc)
-{
- return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48);
-}
-
static void
rio_free_tx (struct net_device *dev, int irq)
{
@@ -1730,44 +1794,16 @@ static int
rio_close (struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->ioaddr;
-
struct pci_dev *pdev = np->pdev;
- struct sk_buff *skb;
- int i;
netif_stop_queue (dev);
- /* Disable interrupts */
- dw16(IntEnable, 0);
-
- /* Stop Tx and Rx logics */
- dw32(MACCtrl, TxDisable | RxDisable | StatsDisable);
+ rio_hw_stop(dev);
free_irq(pdev->irq, dev);
del_timer_sync (&np->timer);
- /* Free all the skbuffs in the queue. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- skb = np->rx_skbuff[i];
- if (skb) {
- pci_unmap_single(pdev, desc_to_dma(&np->rx_ring[i]),
- skb->len, PCI_DMA_FROMDEVICE);
- dev_kfree_skb (skb);
- np->rx_skbuff[i] = NULL;
- }
- np->rx_ring[i].status = 0;
- np->rx_ring[i].fraginfo = 0;
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- skb = np->tx_skbuff[i];
- if (skb) {
- pci_unmap_single(pdev, desc_to_dma(&np->tx_ring[i]),
- skb->len, PCI_DMA_TODEVICE);
- dev_kfree_skb (skb);
- np->tx_skbuff[i] = NULL;
- }
- }
+ free_list(dev);
return 0;
}
@@ -1795,11 +1831,55 @@ rio_remove1 (struct pci_dev *pdev)
}
}
+#ifdef CONFIG_PM_SLEEP
+static int rio_suspend(struct device *device)
+{
+ struct net_device *dev = dev_get_drvdata(device);
+ struct netdev_private *np = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ netif_device_detach(dev);
+ del_timer_sync(&np->timer);
+ rio_hw_stop(dev);
+
+ return 0;
+}
+
+static int rio_resume(struct device *device)
+{
+ struct net_device *dev = dev_get_drvdata(device);
+ struct netdev_private *np = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ rio_reset_ring(np);
+ rio_hw_init(dev);
+ np->timer.expires = jiffies + 1 * HZ;
+ add_timer(&np->timer);
+ netif_device_attach(dev);
+ dl2k_enable_int(np);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rio_pm_ops, rio_suspend, rio_resume);
+#define RIO_PM_OPS (&rio_pm_ops)
+
+#else
+
+#define RIO_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
static struct pci_driver rio_driver = {
.name = "dl2k",
.id_table = rio_pci_tbl,
.probe = rio_probe1,
.remove = rio_remove1,
+ .driver.pm = RIO_PM_OPS,
};
module_pci_driver(rio_driver);
diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c
index 13d00a38a5bd..b69a9eacc531 100644
--- a/drivers/net/ethernet/dnet.c
+++ b/drivers/net/ethernet/dnet.c
@@ -255,15 +255,9 @@ static int dnet_mii_probe(struct net_device *dev)
{
struct dnet *bp = netdev_priv(dev);
struct phy_device *phydev = NULL;
- int phy_addr;
/* find the first phy */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- if (bp->mii_bus->phy_map[phy_addr]) {
- phydev = bp->mii_bus->phy_map[phy_addr];
- break;
- }
- }
+ phydev = phy_find_first(bp->mii_bus);
if (!phydev) {
printk(KERN_ERR "%s: no PHY found\n", dev->name);
@@ -274,11 +268,11 @@ static int dnet_mii_probe(struct net_device *dev)
/* attach the mac to the phy */
if (bp->capabilities & DNET_HAS_RMII) {
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&dnet_handle_link_change,
PHY_INTERFACE_MODE_RMII);
} else {
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&dnet_handle_link_change,
PHY_INTERFACE_MODE_MII);
}
@@ -308,7 +302,7 @@ static int dnet_mii_probe(struct net_device *dev)
static int dnet_mii_init(struct dnet *bp)
{
- int err, i;
+ int err;
bp->mii_bus = mdiobus_alloc();
if (bp->mii_bus == NULL)
@@ -323,16 +317,6 @@ static int dnet_mii_init(struct dnet *bp)
bp->mii_bus->priv = bp;
- bp->mii_bus->irq = devm_kmalloc(&bp->pdev->dev,
- sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!bp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bp->mii_bus->irq[i] = PHY_POLL;
-
if (mdiobus_register(bp->mii_bus)) {
err = -ENXIO;
goto err_out;
@@ -892,9 +876,7 @@ static int dnet_probe(struct platform_device *pdev)
(bp->capabilities & DNET_HAS_GIGABIT) ? "" : "no ",
(bp->capabilities & DNET_HAS_DMA) ? "" : "no ");
phydev = bp->phy_dev;
- dev_info(&pdev->dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h
index 6ee78c203eca..cf837831304b 100644
--- a/drivers/net/ethernet/emulex/benet/be.h
+++ b/drivers/net/ethernet/emulex/benet/be.h
@@ -37,7 +37,7 @@
#include "be_hw.h"
#include "be_roce.h"
-#define DRV_VER "10.6.0.3"
+#define DRV_VER "11.0.0.0"
#define DRV_NAME "be2net"
#define BE_NAME "Emulex BladeEngine2"
#define BE3_NAME "Emulex BladeEngine3"
@@ -521,7 +521,7 @@ struct be_adapter {
struct be_drv_stats drv_stats;
struct be_aic_obj aic_obj[MAX_EVT_QS];
u8 vlan_prio_bmap; /* Available Priority BitMap */
- u16 recommended_prio; /* Recommended Priority */
+ u16 recommended_prio_bits;/* Recommended Priority bits in vlan tag */
struct be_dma_mem rx_filter; /* Cmd DMA mem for rx-filter */
struct be_dma_mem stats_cmd;
@@ -547,10 +547,6 @@ struct be_adapter {
u32 beacon_state; /* for set_phys_id */
- bool eeh_error;
- bool fw_timeout;
- bool hw_error;
-
u32 port_num;
char port_name;
u8 mc_type;
@@ -574,6 +570,8 @@ struct be_adapter {
struct be_resources pool_res; /* resources available for the port */
struct be_resources res; /* resources available for the func */
u16 num_vfs; /* Number of VFs provisioned by PF */
+ u8 pf_num; /* Numbering used by FW, starts at 0 */
+ u8 vf_num; /* Numbering used by FW, starts at 1 */
u8 virtfn;
struct be_vf_cfg *vf_cfg;
bool be3_native;
@@ -591,11 +589,10 @@ struct be_adapter {
u32 msg_enable;
int be_get_temp_freq;
struct be_hwmon hwmon_info;
- u8 pf_number;
- u8 pci_func_num;
struct rss_info rss_info;
/* Filters for packets that need to be sent to BMC */
u32 bmc_filt_mask;
+ u32 fat_dump_len;
u16 serial_num[CNTL_SERIAL_NUM_WORDS];
};
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 1795c935ff02..b63d8ad2e115 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -308,8 +308,7 @@ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter,
if (evt->valid) {
adapter->vlan_prio_bmap = evt->available_priority_bmap;
- adapter->recommended_prio &= ~VLAN_PRIO_MASK;
- adapter->recommended_prio =
+ adapter->recommended_prio_bits =
evt->reco_default_priority << VLAN_PRIO_SHIFT;
}
}
@@ -1713,49 +1712,40 @@ err:
}
/* Uses synchronous mcc */
-int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size)
+int be_cmd_get_fat_dump_len(struct be_adapter *adapter, u32 *dump_size)
{
- struct be_mcc_wrb *wrb;
+ struct be_mcc_wrb wrb = {0};
struct be_cmd_req_get_fat *req;
int status;
- spin_lock_bh(&adapter->mcc_lock);
-
- wrb = wrb_from_mccq(adapter);
- if (!wrb) {
- status = -EBUSY;
- goto err;
- }
- req = embedded_payload(wrb);
+ req = embedded_payload(&wrb);
be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb,
- NULL);
+ OPCODE_COMMON_MANAGE_FAT, sizeof(*req),
+ &wrb, NULL);
req->fat_operation = cpu_to_le32(QUERY_FAT);
- status = be_mcc_notify_wait(adapter);
+ status = be_cmd_notify_wait(adapter, &wrb);
if (!status) {
- struct be_cmd_resp_get_fat *resp = embedded_payload(wrb);
+ struct be_cmd_resp_get_fat *resp = embedded_payload(&wrb);
- if (log_size && resp->log_size)
- *log_size = le32_to_cpu(resp->log_size) -
+ if (dump_size && resp->log_size)
+ *dump_size = le32_to_cpu(resp->log_size) -
sizeof(u32);
}
-err:
- spin_unlock_bh(&adapter->mcc_lock);
return status;
}
-int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
+int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf)
{
struct be_dma_mem get_fat_cmd;
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_fat *req;
u32 offset = 0, total_size, buf_size,
log_offset = sizeof(u32), payload_len;
- int status = 0;
+ int status;
if (buf_len == 0)
- return -EIO;
+ return 0;
total_size = buf_len;
@@ -1763,11 +1753,8 @@ int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
get_fat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
get_fat_cmd.size,
&get_fat_cmd.dma, GFP_ATOMIC);
- if (!get_fat_cmd.va) {
- dev_err(&adapter->pdev->dev,
- "Memory allocation failure while reading FAT data\n");
+ if (!get_fat_cmd.va)
return -ENOMEM;
- }
spin_lock_bh(&adapter->mcc_lock);
@@ -2291,10 +2278,11 @@ err:
return status;
}
-int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 data_size, u32 data_offset,
- const char *obj_name, u32 *data_written,
- u8 *change_status, u8 *addn_status)
+static int lancer_cmd_write_object(struct be_adapter *adapter,
+ struct be_dma_mem *cmd, u32 data_size,
+ u32 data_offset, const char *obj_name,
+ u32 *data_written, u8 *change_status,
+ u8 *addn_status)
{
struct be_mcc_wrb *wrb;
struct lancer_cmd_req_write_object *req;
@@ -2410,7 +2398,8 @@ int be_cmd_query_sfp_info(struct be_adapter *adapter)
return status;
}
-int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name)
+static int lancer_cmd_delete_object(struct be_adapter *adapter,
+ const char *obj_name)
{
struct lancer_cmd_req_delete_object *req;
struct be_mcc_wrb *wrb;
@@ -2485,9 +2474,9 @@ err_unlock:
return status;
}
-int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 flash_type, u32 flash_opcode, u32 img_offset,
- u32 buf_size)
+static int be_cmd_write_flashrom(struct be_adapter *adapter,
+ struct be_dma_mem *cmd, u32 flash_type,
+ u32 flash_opcode, u32 img_offset, u32 buf_size)
{
struct be_mcc_wrb *wrb;
struct be_cmd_write_flashrom *req;
@@ -2533,8 +2522,8 @@ err_unlock:
return status;
}
-int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
- u16 img_optype, u32 img_offset, u32 crc_offset)
+static int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
+ u16 img_optype, u32 img_offset, u32 crc_offset)
{
struct be_cmd_read_flash_crc *req;
struct be_mcc_wrb *wrb;
@@ -2571,6 +2560,579 @@ err:
return status;
}
+static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "};
+
+static bool phy_flashing_required(struct be_adapter *adapter)
+{
+ return (adapter->phy.phy_type == PHY_TYPE_TN_8022 &&
+ adapter->phy.interface_type == PHY_TYPE_BASET_10GB);
+}
+
+static bool is_comp_in_ufi(struct be_adapter *adapter,
+ struct flash_section_info *fsec, int type)
+{
+ int i = 0, img_type = 0;
+ struct flash_section_info_g2 *fsec_g2 = NULL;
+
+ if (BE2_chip(adapter))
+ fsec_g2 = (struct flash_section_info_g2 *)fsec;
+
+ for (i = 0; i < MAX_FLASH_COMP; i++) {
+ if (fsec_g2)
+ img_type = le32_to_cpu(fsec_g2->fsec_entry[i].type);
+ else
+ img_type = le32_to_cpu(fsec->fsec_entry[i].type);
+
+ if (img_type == type)
+ return true;
+ }
+ return false;
+}
+
+static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
+ int header_size,
+ const struct firmware *fw)
+{
+ struct flash_section_info *fsec = NULL;
+ const u8 *p = fw->data;
+
+ p += header_size;
+ while (p < (fw->data + fw->size)) {
+ fsec = (struct flash_section_info *)p;
+ if (!memcmp(flash_cookie, fsec->cookie, sizeof(flash_cookie)))
+ return fsec;
+ p += 32;
+ }
+ return NULL;
+}
+
+static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p,
+ u32 img_offset, u32 img_size, int hdr_size,
+ u16 img_optype, bool *crc_match)
+{
+ u32 crc_offset;
+ int status;
+ u8 crc[4];
+
+ status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_offset,
+ img_size - 4);
+ if (status)
+ return status;
+
+ crc_offset = hdr_size + img_offset + img_size - 4;
+
+ /* Skip flashing, if crc of flashed region matches */
+ if (!memcmp(crc, p + crc_offset, 4))
+ *crc_match = true;
+ else
+ *crc_match = false;
+
+ return status;
+}
+
+static int be_flash(struct be_adapter *adapter, const u8 *img,
+ struct be_dma_mem *flash_cmd, int optype, int img_size,
+ u32 img_offset)
+{
+ u32 flash_op, num_bytes, total_bytes = img_size, bytes_sent = 0;
+ struct be_cmd_write_flashrom *req = flash_cmd->va;
+ int status;
+
+ while (total_bytes) {
+ num_bytes = min_t(u32, 32 * 1024, total_bytes);
+
+ total_bytes -= num_bytes;
+
+ if (!total_bytes) {
+ if (optype == OPTYPE_PHY_FW)
+ flash_op = FLASHROM_OPER_PHY_FLASH;
+ else
+ flash_op = FLASHROM_OPER_FLASH;
+ } else {
+ if (optype == OPTYPE_PHY_FW)
+ flash_op = FLASHROM_OPER_PHY_SAVE;
+ else
+ flash_op = FLASHROM_OPER_SAVE;
+ }
+
+ memcpy(req->data_buf, img, num_bytes);
+ img += num_bytes;
+ status = be_cmd_write_flashrom(adapter, flash_cmd, optype,
+ flash_op, img_offset +
+ bytes_sent, num_bytes);
+ if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST &&
+ optype == OPTYPE_PHY_FW)
+ break;
+ else if (status)
+ return status;
+
+ bytes_sent += num_bytes;
+ }
+ return 0;
+}
+
+/* For BE2, BE3 and BE3-R */
+static int be_flash_BEx(struct be_adapter *adapter,
+ const struct firmware *fw,
+ struct be_dma_mem *flash_cmd, int num_of_images)
+{
+ int img_hdrs_size = (num_of_images * sizeof(struct image_hdr));
+ struct device *dev = &adapter->pdev->dev;
+ struct flash_section_info *fsec = NULL;
+ int status, i, filehdr_size, num_comp;
+ const struct flash_comp *pflashcomp;
+ bool crc_match;
+ const u8 *p;
+
+ struct flash_comp gen3_flash_types[] = {
+ { BE3_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI},
+ { BE3_REDBOOT_START, OPTYPE_REDBOOT,
+ BE3_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE},
+ { BE3_ISCSI_BIOS_START, OPTYPE_BIOS,
+ BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI},
+ { BE3_PXE_BIOS_START, OPTYPE_PXE_BIOS,
+ BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE},
+ { BE3_FCOE_BIOS_START, OPTYPE_FCOE_BIOS,
+ BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE},
+ { BE3_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI},
+ { BE3_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE},
+ { BE3_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE},
+ { BE3_NCSI_START, OPTYPE_NCSI_FW,
+ BE3_NCSI_COMP_MAX_SIZE, IMAGE_NCSI},
+ { BE3_PHY_FW_START, OPTYPE_PHY_FW,
+ BE3_PHY_FW_COMP_MAX_SIZE, IMAGE_FIRMWARE_PHY}
+ };
+
+ struct flash_comp gen2_flash_types[] = {
+ { BE2_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI},
+ { BE2_REDBOOT_START, OPTYPE_REDBOOT,
+ BE2_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE},
+ { BE2_ISCSI_BIOS_START, OPTYPE_BIOS,
+ BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI},
+ { BE2_PXE_BIOS_START, OPTYPE_PXE_BIOS,
+ BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE},
+ { BE2_FCOE_BIOS_START, OPTYPE_FCOE_BIOS,
+ BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE},
+ { BE2_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI},
+ { BE2_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE},
+ { BE2_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE}
+ };
+
+ if (BE3_chip(adapter)) {
+ pflashcomp = gen3_flash_types;
+ filehdr_size = sizeof(struct flash_file_hdr_g3);
+ num_comp = ARRAY_SIZE(gen3_flash_types);
+ } else {
+ pflashcomp = gen2_flash_types;
+ filehdr_size = sizeof(struct flash_file_hdr_g2);
+ num_comp = ARRAY_SIZE(gen2_flash_types);
+ img_hdrs_size = 0;
+ }
+
+ /* Get flash section info*/
+ fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
+ if (!fsec) {
+ dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
+ return -1;
+ }
+ for (i = 0; i < num_comp; i++) {
+ if (!is_comp_in_ufi(adapter, fsec, pflashcomp[i].img_type))
+ continue;
+
+ if ((pflashcomp[i].optype == OPTYPE_NCSI_FW) &&
+ memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0)
+ continue;
+
+ if (pflashcomp[i].optype == OPTYPE_PHY_FW &&
+ !phy_flashing_required(adapter))
+ continue;
+
+ if (pflashcomp[i].optype == OPTYPE_REDBOOT) {
+ status = be_check_flash_crc(adapter, fw->data,
+ pflashcomp[i].offset,
+ pflashcomp[i].size,
+ filehdr_size +
+ img_hdrs_size,
+ OPTYPE_REDBOOT, &crc_match);
+ if (status) {
+ dev_err(dev,
+ "Could not get CRC for 0x%x region\n",
+ pflashcomp[i].optype);
+ continue;
+ }
+
+ if (crc_match)
+ continue;
+ }
+
+ p = fw->data + filehdr_size + pflashcomp[i].offset +
+ img_hdrs_size;
+ if (p + pflashcomp[i].size > fw->data + fw->size)
+ return -1;
+
+ status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype,
+ pflashcomp[i].size, 0);
+ if (status) {
+ dev_err(dev, "Flashing section type 0x%x failed\n",
+ pflashcomp[i].img_type);
+ return status;
+ }
+ }
+ return 0;
+}
+
+static u16 be_get_img_optype(struct flash_section_entry fsec_entry)
+{
+ u32 img_type = le32_to_cpu(fsec_entry.type);
+ u16 img_optype = le16_to_cpu(fsec_entry.optype);
+
+ if (img_optype != 0xFFFF)
+ return img_optype;
+
+ switch (img_type) {
+ case IMAGE_FIRMWARE_ISCSI:
+ img_optype = OPTYPE_ISCSI_ACTIVE;
+ break;
+ case IMAGE_BOOT_CODE:
+ img_optype = OPTYPE_REDBOOT;
+ break;
+ case IMAGE_OPTION_ROM_ISCSI:
+ img_optype = OPTYPE_BIOS;
+ break;
+ case IMAGE_OPTION_ROM_PXE:
+ img_optype = OPTYPE_PXE_BIOS;
+ break;
+ case IMAGE_OPTION_ROM_FCOE:
+ img_optype = OPTYPE_FCOE_BIOS;
+ break;
+ case IMAGE_FIRMWARE_BACKUP_ISCSI:
+ img_optype = OPTYPE_ISCSI_BACKUP;
+ break;
+ case IMAGE_NCSI:
+ img_optype = OPTYPE_NCSI_FW;
+ break;
+ case IMAGE_FLASHISM_JUMPVECTOR:
+ img_optype = OPTYPE_FLASHISM_JUMPVECTOR;
+ break;
+ case IMAGE_FIRMWARE_PHY:
+ img_optype = OPTYPE_SH_PHY_FW;
+ break;
+ case IMAGE_REDBOOT_DIR:
+ img_optype = OPTYPE_REDBOOT_DIR;
+ break;
+ case IMAGE_REDBOOT_CONFIG:
+ img_optype = OPTYPE_REDBOOT_CONFIG;
+ break;
+ case IMAGE_UFI_DIR:
+ img_optype = OPTYPE_UFI_DIR;
+ break;
+ default:
+ break;
+ }
+
+ return img_optype;
+}
+
+static int be_flash_skyhawk(struct be_adapter *adapter,
+ const struct firmware *fw,
+ struct be_dma_mem *flash_cmd, int num_of_images)
+{
+ int img_hdrs_size = num_of_images * sizeof(struct image_hdr);
+ bool crc_match, old_fw_img, flash_offset_support = true;
+ struct device *dev = &adapter->pdev->dev;
+ struct flash_section_info *fsec = NULL;
+ u32 img_offset, img_size, img_type;
+ u16 img_optype, flash_optype;
+ int status, i, filehdr_size;
+ const u8 *p;
+
+ filehdr_size = sizeof(struct flash_file_hdr_g3);
+ fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
+ if (!fsec) {
+ dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
+ return -EINVAL;
+ }
+
+retry_flash:
+ for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) {
+ img_offset = le32_to_cpu(fsec->fsec_entry[i].offset);
+ img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size);
+ img_type = le32_to_cpu(fsec->fsec_entry[i].type);
+ img_optype = be_get_img_optype(fsec->fsec_entry[i]);
+ old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF;
+
+ if (img_optype == 0xFFFF)
+ continue;
+
+ if (flash_offset_support)
+ flash_optype = OPTYPE_OFFSET_SPECIFIED;
+ else
+ flash_optype = img_optype;
+
+ /* Don't bother verifying CRC if an old FW image is being
+ * flashed
+ */
+ if (old_fw_img)
+ goto flash;
+
+ status = be_check_flash_crc(adapter, fw->data, img_offset,
+ img_size, filehdr_size +
+ img_hdrs_size, flash_optype,
+ &crc_match);
+ if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST ||
+ base_status(status) == MCC_STATUS_ILLEGAL_FIELD) {
+ /* The current FW image on the card does not support
+ * OFFSET based flashing. Retry using older mechanism
+ * of OPTYPE based flashing
+ */
+ if (flash_optype == OPTYPE_OFFSET_SPECIFIED) {
+ flash_offset_support = false;
+ goto retry_flash;
+ }
+
+ /* The current FW image on the card does not recognize
+ * the new FLASH op_type. The FW download is partially
+ * complete. Reboot the server now to enable FW image
+ * to recognize the new FLASH op_type. To complete the
+ * remaining process, download the same FW again after
+ * the reboot.
+ */
+ dev_err(dev, "Flash incomplete. Reset the server\n");
+ dev_err(dev, "Download FW image again after reset\n");
+ return -EAGAIN;
+ } else if (status) {
+ dev_err(dev, "Could not get CRC for 0x%x region\n",
+ img_optype);
+ return -EFAULT;
+ }
+
+ if (crc_match)
+ continue;
+
+flash:
+ p = fw->data + filehdr_size + img_offset + img_hdrs_size;
+ if (p + img_size > fw->data + fw->size)
+ return -1;
+
+ status = be_flash(adapter, p, flash_cmd, flash_optype, img_size,
+ img_offset);
+
+ /* The current FW image on the card does not support OFFSET
+ * based flashing. Retry using older mechanism of OPTYPE based
+ * flashing
+ */
+ if (base_status(status) == MCC_STATUS_ILLEGAL_FIELD &&
+ flash_optype == OPTYPE_OFFSET_SPECIFIED) {
+ flash_offset_support = false;
+ goto retry_flash;
+ }
+
+ /* For old FW images ignore ILLEGAL_FIELD error or errors on
+ * UFI_DIR region
+ */
+ if (old_fw_img &&
+ (base_status(status) == MCC_STATUS_ILLEGAL_FIELD ||
+ (img_optype == OPTYPE_UFI_DIR &&
+ base_status(status) == MCC_STATUS_FAILED))) {
+ continue;
+ } else if (status) {
+ dev_err(dev, "Flashing section type 0x%x failed\n",
+ img_type);
+
+ switch (addl_status(status)) {
+ case MCC_ADDL_STATUS_MISSING_SIGNATURE:
+ dev_err(dev,
+ "Digital signature missing in FW\n");
+ return -EINVAL;
+ case MCC_ADDL_STATUS_INVALID_SIGNATURE:
+ dev_err(dev,
+ "Invalid digital signature in FW\n");
+ return -EINVAL;
+ default:
+ return -EFAULT;
+ }
+ }
+ }
+ return 0;
+}
+
+int lancer_fw_download(struct be_adapter *adapter,
+ const struct firmware *fw)
+{
+ struct device *dev = &adapter->pdev->dev;
+ struct be_dma_mem flash_cmd;
+ const u8 *data_ptr = NULL;
+ u8 *dest_image_ptr = NULL;
+ size_t image_size = 0;
+ u32 chunk_size = 0;
+ u32 data_written = 0;
+ u32 offset = 0;
+ int status = 0;
+ u8 add_status = 0;
+ u8 change_status;
+
+ if (!IS_ALIGNED(fw->size, sizeof(u32))) {
+ dev_err(dev, "FW image size should be multiple of 4\n");
+ return -EINVAL;
+ }
+
+ flash_cmd.size = sizeof(struct lancer_cmd_req_write_object)
+ + LANCER_FW_DOWNLOAD_CHUNK;
+ flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size,
+ &flash_cmd.dma, GFP_KERNEL);
+ if (!flash_cmd.va)
+ return -ENOMEM;
+
+ dest_image_ptr = flash_cmd.va +
+ sizeof(struct lancer_cmd_req_write_object);
+ image_size = fw->size;
+ data_ptr = fw->data;
+
+ while (image_size) {
+ chunk_size = min_t(u32, image_size, LANCER_FW_DOWNLOAD_CHUNK);
+
+ /* Copy the image chunk content. */
+ memcpy(dest_image_ptr, data_ptr, chunk_size);
+
+ status = lancer_cmd_write_object(adapter, &flash_cmd,
+ chunk_size, offset,
+ LANCER_FW_DOWNLOAD_LOCATION,
+ &data_written, &change_status,
+ &add_status);
+ if (status)
+ break;
+
+ offset += data_written;
+ data_ptr += data_written;
+ image_size -= data_written;
+ }
+
+ if (!status) {
+ /* Commit the FW written */
+ status = lancer_cmd_write_object(adapter, &flash_cmd,
+ 0, offset,
+ LANCER_FW_DOWNLOAD_LOCATION,
+ &data_written, &change_status,
+ &add_status);
+ }
+
+ dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
+ if (status) {
+ dev_err(dev, "Firmware load error\n");
+ return be_cmd_status(status);
+ }
+
+ dev_info(dev, "Firmware flashed successfully\n");
+
+ if (change_status == LANCER_FW_RESET_NEEDED) {
+ dev_info(dev, "Resetting adapter to activate new FW\n");
+ status = lancer_physdev_ctrl(adapter,
+ PHYSDEV_CONTROL_FW_RESET_MASK);
+ if (status) {
+ dev_err(dev, "Adapter busy, could not reset FW\n");
+ dev_err(dev, "Reboot server to activate new FW\n");
+ }
+ } else if (change_status != LANCER_NO_RESET_NEEDED) {
+ dev_info(dev, "Reboot server to activate new FW\n");
+ }
+
+ return 0;
+}
+
+/* Check if the flash image file is compatible with the adapter that
+ * is being flashed.
+ */
+static bool be_check_ufi_compatibility(struct be_adapter *adapter,
+ struct flash_file_hdr_g3 *fhdr)
+{
+ if (!fhdr) {
+ dev_err(&adapter->pdev->dev, "Invalid FW UFI file");
+ return false;
+ }
+
+ /* First letter of the build version is used to identify
+ * which chip this image file is meant for.
+ */
+ switch (fhdr->build[0]) {
+ case BLD_STR_UFI_TYPE_SH:
+ if (!skyhawk_chip(adapter))
+ return false;
+ break;
+ case BLD_STR_UFI_TYPE_BE3:
+ if (!BE3_chip(adapter))
+ return false;
+ break;
+ case BLD_STR_UFI_TYPE_BE2:
+ if (!BE2_chip(adapter))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ /* In BE3 FW images the "asic_type_rev" field doesn't track the
+ * asic_rev of the chips it is compatible with.
+ * When asic_type_rev is 0 the image is compatible only with
+ * pre-BE3-R chips (asic_rev < 0x10)
+ */
+ if (BEx_chip(adapter) && fhdr->asic_type_rev == 0)
+ return adapter->asic_rev < 0x10;
+ else
+ return (fhdr->asic_type_rev >= adapter->asic_rev);
+}
+
+int be_fw_download(struct be_adapter *adapter, const struct firmware *fw)
+{
+ struct device *dev = &adapter->pdev->dev;
+ struct flash_file_hdr_g3 *fhdr3;
+ struct image_hdr *img_hdr_ptr;
+ int status = 0, i, num_imgs;
+ struct be_dma_mem flash_cmd;
+
+ fhdr3 = (struct flash_file_hdr_g3 *)fw->data;
+ if (!be_check_ufi_compatibility(adapter, fhdr3)) {
+ dev_err(dev, "Flash image is not compatible with adapter\n");
+ return -EINVAL;
+ }
+
+ flash_cmd.size = sizeof(struct be_cmd_write_flashrom);
+ flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
+ GFP_KERNEL);
+ if (!flash_cmd.va)
+ return -ENOMEM;
+
+ num_imgs = le32_to_cpu(fhdr3->num_imgs);
+ for (i = 0; i < num_imgs; i++) {
+ img_hdr_ptr = (struct image_hdr *)(fw->data +
+ (sizeof(struct flash_file_hdr_g3) +
+ i * sizeof(struct image_hdr)));
+ if (!BE2_chip(adapter) &&
+ le32_to_cpu(img_hdr_ptr->imageid) != 1)
+ continue;
+
+ if (skyhawk_chip(adapter))
+ status = be_flash_skyhawk(adapter, fw, &flash_cmd,
+ num_imgs);
+ else
+ status = be_flash_BEx(adapter, fw, &flash_cmd,
+ num_imgs);
+ }
+
+ dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
+ if (!status)
+ dev_info(dev, "Firmware flashed successfully\n");
+
+ return status;
+}
+
int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
struct be_dma_mem *nonemb_cmd)
{
@@ -2892,7 +3454,6 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
if (!status) {
attribs = attribs_cmd.va + sizeof(struct be_cmd_resp_hdr);
adapter->hba_port_num = attribs->hba_attribs.phy_port;
- adapter->pci_func_num = attribs->pci_func_num;
serial_num = attribs->hba_attribs.controller_serial_number;
for (i = 0; i < CNTL_SERIAL_NUM_WORDS; i++)
adapter->serial_num[i] = le32_to_cpu(serial_num[i]) &
@@ -3575,14 +4136,16 @@ int be_cmd_query_port_name(struct be_adapter *adapter)
return status;
}
-/* Descriptor type */
-enum {
- FUNC_DESC = 1,
- VFT_DESC = 2
-};
-
+/* When more than 1 NIC descriptor is present in the descriptor list,
+ * the caller must specify the pf_num to obtain the NIC descriptor
+ * corresponding to its pci function.
+ * get_vft must be true when the caller wants the VF-template desc of the
+ * PF-pool.
+ * The pf_num should be set to PF_NUM_IGNORE when the caller knows
+ * that only it's NIC descriptor is present in the descriptor list.
+ */
static struct be_nic_res_desc *be_get_nic_desc(u8 *buf, u32 desc_count,
- int desc_type)
+ bool get_vft, u8 pf_num)
{
struct be_res_desc_hdr *hdr = (struct be_res_desc_hdr *)buf;
struct be_nic_res_desc *nic;
@@ -3592,40 +4155,42 @@ static struct be_nic_res_desc *be_get_nic_desc(u8 *buf, u32 desc_count,
if (hdr->desc_type == NIC_RESOURCE_DESC_TYPE_V0 ||
hdr->desc_type == NIC_RESOURCE_DESC_TYPE_V1) {
nic = (struct be_nic_res_desc *)hdr;
- if (desc_type == FUNC_DESC ||
- (desc_type == VFT_DESC &&
- nic->flags & (1 << VFT_SHIFT)))
+
+ if ((pf_num == PF_NUM_IGNORE ||
+ nic->pf_num == pf_num) &&
+ (!get_vft || nic->flags & BIT(VFT_SHIFT)))
return nic;
}
-
hdr->desc_len = hdr->desc_len ? : RESOURCE_DESC_SIZE_V0;
hdr = (void *)hdr + hdr->desc_len;
}
return NULL;
}
-static struct be_nic_res_desc *be_get_vft_desc(u8 *buf, u32 desc_count)
+static struct be_nic_res_desc *be_get_vft_desc(u8 *buf, u32 desc_count,
+ u8 pf_num)
{
- return be_get_nic_desc(buf, desc_count, VFT_DESC);
+ return be_get_nic_desc(buf, desc_count, true, pf_num);
}
-static struct be_nic_res_desc *be_get_func_nic_desc(u8 *buf, u32 desc_count)
+static struct be_nic_res_desc *be_get_func_nic_desc(u8 *buf, u32 desc_count,
+ u8 pf_num)
{
- return be_get_nic_desc(buf, desc_count, FUNC_DESC);
+ return be_get_nic_desc(buf, desc_count, false, pf_num);
}
-static struct be_pcie_res_desc *be_get_pcie_desc(u8 devfn, u8 *buf,
- u32 desc_count)
+static struct be_pcie_res_desc *be_get_pcie_desc(u8 *buf, u32 desc_count,
+ u8 pf_num)
{
struct be_res_desc_hdr *hdr = (struct be_res_desc_hdr *)buf;
struct be_pcie_res_desc *pcie;
int i;
for (i = 0; i < desc_count; i++) {
- if ((hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V0 ||
- hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V1)) {
- pcie = (struct be_pcie_res_desc *)hdr;
- if (pcie->pf_num == devfn)
+ if (hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V0 ||
+ hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V1) {
+ pcie = (struct be_pcie_res_desc *)hdr;
+ if (pcie->pf_num == pf_num)
return pcie;
}
@@ -3710,13 +4275,23 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
u32 desc_count = le32_to_cpu(resp->desc_count);
struct be_nic_res_desc *desc;
- desc = be_get_func_nic_desc(resp->func_param, desc_count);
+ /* GET_FUNC_CONFIG returns resource descriptors of the
+ * current function only. So, pf_num should be set to
+ * PF_NUM_IGNORE.
+ */
+ desc = be_get_func_nic_desc(resp->func_param, desc_count,
+ PF_NUM_IGNORE);
if (!desc) {
status = -EINVAL;
goto err;
}
- adapter->pf_number = desc->pf_num;
- be_copy_nic_desc(res, desc);
+
+ /* Store pf_num & vf_num for later use in GET_PROFILE_CONFIG */
+ adapter->pf_num = desc->pf_num;
+ adapter->vf_num = desc->vf_num;
+
+ if (res)
+ be_copy_nic_desc(res, desc);
}
err:
mutex_unlock(&adapter->mbox_lock);
@@ -3726,10 +4301,7 @@ err:
return status;
}
-/* Will use MBOX only if MCCQ has not been created
- * non-zero domain => a PF is querying this on behalf of a VF
- * zero domain => a PF or a VF is querying this for itself
- */
+/* Will use MBOX only if MCCQ has not been created */
int be_cmd_get_profile_config(struct be_adapter *adapter,
struct be_resources *res, u8 query, u8 domain)
{
@@ -3759,12 +4331,7 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
if (!lancer_chip(adapter))
req->hdr.version = 1;
req->type = ACTIVE_PROFILE_TYPE;
- /* When a function is querying profile information relating to
- * itself hdr.pf_number must be set to it's pci_func_num + 1
- */
req->hdr.domain = domain;
- if (domain == 0)
- req->hdr.pf_num = adapter->pci_func_num + 1;
/* When QUERY_MODIFIABLE_FIELDS_TYPE bit is set, cmd returns the
* descriptors with all bits set to "1" for the fields which can be
@@ -3780,8 +4347,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
resp = cmd.va;
desc_count = le16_to_cpu(resp->desc_count);
- pcie = be_get_pcie_desc(adapter->pdev->devfn, resp->func_param,
- desc_count);
+ pcie = be_get_pcie_desc(resp->func_param, desc_count,
+ adapter->pf_num);
if (pcie)
res->max_vfs = le16_to_cpu(pcie->num_vfs);
@@ -3789,11 +4356,13 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
if (port)
adapter->mc_type = port->mc_type;
- nic = be_get_func_nic_desc(resp->func_param, desc_count);
+ nic = be_get_func_nic_desc(resp->func_param, desc_count,
+ adapter->pf_num);
if (nic)
be_copy_nic_desc(res, nic);
- vf_res = be_get_vft_desc(resp->func_param, desc_count);
+ vf_res = be_get_vft_desc(resp->func_param, desc_count,
+ adapter->pf_num);
if (vf_res)
res->vf_if_cap_flags = vf_res->cap_flags;
err:
@@ -3883,7 +4452,7 @@ int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, u16 link_speed,
return be_cmd_set_qos(adapter, max_rate / 10, domain);
be_reset_nic_desc(&nic_desc);
- nic_desc.pf_num = adapter->pf_number;
+ nic_desc.pf_num = adapter->pf_num;
nic_desc.vf_num = domain;
nic_desc.bw_min = 0;
if (lancer_chip(adapter)) {
@@ -4260,16 +4829,13 @@ err:
return status;
}
-int be_cmd_set_logical_link_config(struct be_adapter *adapter,
- int link_state, u8 domain)
+int __be_cmd_set_logical_link_config(struct be_adapter *adapter,
+ int link_state, int version, u8 domain)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_set_ll_link *req;
int status;
- if (BEx_chip(adapter) || lancer_chip(adapter))
- return -EOPNOTSUPP;
-
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
@@ -4284,14 +4850,15 @@ int be_cmd_set_logical_link_config(struct be_adapter *adapter,
OPCODE_COMMON_SET_LOGICAL_LINK_CONFIG,
sizeof(*req), wrb, NULL);
- req->hdr.version = 1;
+ req->hdr.version = version;
req->hdr.domain = domain;
- if (link_state == IFLA_VF_LINK_STATE_ENABLE)
- req->link_config |= 1;
+ if (link_state == IFLA_VF_LINK_STATE_ENABLE ||
+ link_state == IFLA_VF_LINK_STATE_AUTO)
+ req->link_config |= PLINK_ENABLE;
if (link_state == IFLA_VF_LINK_STATE_AUTO)
- req->link_config |= 1 << PLINK_TRACK_SHIFT;
+ req->link_config |= PLINK_TRACK;
status = be_mcc_notify_wait(adapter);
err:
@@ -4299,6 +4866,25 @@ err:
return status;
}
+int be_cmd_set_logical_link_config(struct be_adapter *adapter,
+ int link_state, u8 domain)
+{
+ int status;
+
+ if (BEx_chip(adapter))
+ return -EOPNOTSUPP;
+
+ status = __be_cmd_set_logical_link_config(adapter, link_state,
+ 2, domain);
+
+ /* Version 2 of the command will not be recognized by older FW.
+ * On such a failure issue version 1 of the command.
+ */
+ if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST)
+ status = __be_cmd_set_logical_link_config(adapter, link_state,
+ 1, domain);
+ return status;
+}
int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload,
int wrb_payload_size, u16 *cmd_status, u16 *ext_status)
{
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h
index 91155ea74f34..241819b36ca7 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -66,7 +66,9 @@ enum mcc_addl_status {
MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES = 0x16,
MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH = 0x4d,
MCC_ADDL_STATUS_TOO_MANY_INTERFACES = 0x4a,
- MCC_ADDL_STATUS_INSUFFICIENT_VLANS = 0xab
+ MCC_ADDL_STATUS_INSUFFICIENT_VLANS = 0xab,
+ MCC_ADDL_STATUS_INVALID_SIGNATURE = 0x56,
+ MCC_ADDL_STATUS_MISSING_SIGNATURE = 0x57
};
#define CQE_BASE_STATUS_MASK 0xFFFF
@@ -289,9 +291,7 @@ struct be_cmd_req_hdr {
u32 timeout; /* dword 1 */
u32 request_length; /* dword 2 */
u8 version; /* dword 3 */
- u8 rsvd1; /* dword 3 */
- u8 pf_num; /* dword 3 */
- u8 rsvd2; /* dword 3 */
+ u8 rsvd[3]; /* dword 3 */
};
#define RESP_HDR_INFO_OPCODE_SHIFT 0 /* bits 0 - 7 */
@@ -1207,68 +1207,85 @@ struct be_cmd_resp_get_beacon_state {
/* Flashrom related descriptors */
#define MAX_FLASH_COMP 32
-#define OPTYPE_ISCSI_ACTIVE 0
-#define OPTYPE_REDBOOT 1
-#define OPTYPE_BIOS 2
-#define OPTYPE_PXE_BIOS 3
-#define OPTYPE_OFFSET_SPECIFIED 7
-#define OPTYPE_FCOE_BIOS 8
-#define OPTYPE_ISCSI_BACKUP 9
-#define OPTYPE_FCOE_FW_ACTIVE 10
-#define OPTYPE_FCOE_FW_BACKUP 11
-#define OPTYPE_NCSI_FW 13
-#define OPTYPE_REDBOOT_DIR 18
-#define OPTYPE_REDBOOT_CONFIG 19
-#define OPTYPE_SH_PHY_FW 21
-#define OPTYPE_FLASHISM_JUMPVECTOR 22
-#define OPTYPE_UFI_DIR 23
-#define OPTYPE_PHY_FW 99
-
-#define FLASH_BIOS_IMAGE_MAX_SIZE_g2 262144 /* Max OPTION ROM image sz */
-#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g2 262144 /* Max Redboot image sz */
-#define FLASH_IMAGE_MAX_SIZE_g2 1310720 /* Max firmware image size */
-
-#define FLASH_NCSI_IMAGE_MAX_SIZE_g3 262144
-#define FLASH_PHY_FW_IMAGE_MAX_SIZE_g3 262144
-#define FLASH_BIOS_IMAGE_MAX_SIZE_g3 524288 /* Max OPTION ROM image sz */
-#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g3 1048576 /* Max Redboot image sz */
-#define FLASH_IMAGE_MAX_SIZE_g3 2097152 /* Max firmware image size */
-
-/* Offsets for components on Flash. */
-#define FLASH_REDBOOT_START_g2 0
-#define FLASH_FCoE_BIOS_START_g2 524288
-#define FLASH_iSCSI_PRIMARY_IMAGE_START_g2 1048576
-#define FLASH_iSCSI_BACKUP_IMAGE_START_g2 2359296
-#define FLASH_FCoE_PRIMARY_IMAGE_START_g2 3670016
-#define FLASH_FCoE_BACKUP_IMAGE_START_g2 4980736
-#define FLASH_iSCSI_BIOS_START_g2 7340032
-#define FLASH_PXE_BIOS_START_g2 7864320
-
-#define FLASH_REDBOOT_START_g3 262144
-#define FLASH_PHY_FW_START_g3 1310720
-#define FLASH_iSCSI_PRIMARY_IMAGE_START_g3 2097152
-#define FLASH_iSCSI_BACKUP_IMAGE_START_g3 4194304
-#define FLASH_FCoE_PRIMARY_IMAGE_START_g3 6291456
-#define FLASH_FCoE_BACKUP_IMAGE_START_g3 8388608
-#define FLASH_iSCSI_BIOS_START_g3 12582912
-#define FLASH_PXE_BIOS_START_g3 13107200
-#define FLASH_FCoE_BIOS_START_g3 13631488
-#define FLASH_NCSI_START_g3 15990784
-
-#define IMAGE_NCSI 16
-#define IMAGE_OPTION_ROM_PXE 32
-#define IMAGE_OPTION_ROM_FCoE 33
-#define IMAGE_OPTION_ROM_ISCSI 34
-#define IMAGE_FLASHISM_JUMPVECTOR 48
-#define IMAGE_FIRMWARE_iSCSI 160
-#define IMAGE_FIRMWARE_FCoE 162
-#define IMAGE_FIRMWARE_BACKUP_iSCSI 176
-#define IMAGE_FIRMWARE_BACKUP_FCoE 178
-#define IMAGE_FIRMWARE_PHY 192
-#define IMAGE_REDBOOT_DIR 208
-#define IMAGE_REDBOOT_CONFIG 209
-#define IMAGE_UFI_DIR 210
-#define IMAGE_BOOT_CODE 224
+/* Optypes of each component in the UFI */
+enum {
+ OPTYPE_ISCSI_ACTIVE = 0,
+ OPTYPE_REDBOOT = 1,
+ OPTYPE_BIOS = 2,
+ OPTYPE_PXE_BIOS = 3,
+ OPTYPE_OFFSET_SPECIFIED = 7,
+ OPTYPE_FCOE_BIOS = 8,
+ OPTYPE_ISCSI_BACKUP = 9,
+ OPTYPE_FCOE_FW_ACTIVE = 10,
+ OPTYPE_FCOE_FW_BACKUP = 11,
+ OPTYPE_NCSI_FW = 13,
+ OPTYPE_REDBOOT_DIR = 18,
+ OPTYPE_REDBOOT_CONFIG = 19,
+ OPTYPE_SH_PHY_FW = 21,
+ OPTYPE_FLASHISM_JUMPVECTOR = 22,
+ OPTYPE_UFI_DIR = 23,
+ OPTYPE_PHY_FW = 99
+};
+
+/* Maximum sizes of components in BE2 FW UFI */
+enum {
+ BE2_BIOS_COMP_MAX_SIZE = 0x40000,
+ BE2_REDBOOT_COMP_MAX_SIZE = 0x40000,
+ BE2_COMP_MAX_SIZE = 0x140000
+};
+
+/* Maximum sizes of components in BE3 FW UFI */
+enum {
+ BE3_NCSI_COMP_MAX_SIZE = 0x40000,
+ BE3_PHY_FW_COMP_MAX_SIZE = 0x40000,
+ BE3_BIOS_COMP_MAX_SIZE = 0x80000,
+ BE3_REDBOOT_COMP_MAX_SIZE = 0x100000,
+ BE3_COMP_MAX_SIZE = 0x200000
+};
+
+/* Offsets for components in BE2 FW UFI */
+enum {
+ BE2_REDBOOT_START = 0x8000,
+ BE2_FCOE_BIOS_START = 0x80000,
+ BE2_ISCSI_PRIMARY_IMAGE_START = 0x100000,
+ BE2_ISCSI_BACKUP_IMAGE_START = 0x240000,
+ BE2_FCOE_PRIMARY_IMAGE_START = 0x380000,
+ BE2_FCOE_BACKUP_IMAGE_START = 0x4c0000,
+ BE2_ISCSI_BIOS_START = 0x700000,
+ BE2_PXE_BIOS_START = 0x780000
+};
+
+/* Offsets for components in BE3 FW UFI */
+enum {
+ BE3_REDBOOT_START = 0x40000,
+ BE3_PHY_FW_START = 0x140000,
+ BE3_ISCSI_PRIMARY_IMAGE_START = 0x200000,
+ BE3_ISCSI_BACKUP_IMAGE_START = 0x400000,
+ BE3_FCOE_PRIMARY_IMAGE_START = 0x600000,
+ BE3_FCOE_BACKUP_IMAGE_START = 0x800000,
+ BE3_ISCSI_BIOS_START = 0xc00000,
+ BE3_PXE_BIOS_START = 0xc80000,
+ BE3_FCOE_BIOS_START = 0xd00000,
+ BE3_NCSI_START = 0xf40000
+};
+
+/* Component entry types */
+enum {
+ IMAGE_NCSI = 0x10,
+ IMAGE_OPTION_ROM_PXE = 0x20,
+ IMAGE_OPTION_ROM_FCOE = 0x21,
+ IMAGE_OPTION_ROM_ISCSI = 0x22,
+ IMAGE_FLASHISM_JUMPVECTOR = 0x30,
+ IMAGE_FIRMWARE_ISCSI = 0xa0,
+ IMAGE_FIRMWARE_FCOE = 0xa2,
+ IMAGE_FIRMWARE_BACKUP_ISCSI = 0xb0,
+ IMAGE_FIRMWARE_BACKUP_FCOE = 0xb2,
+ IMAGE_FIRMWARE_PHY = 0xc0,
+ IMAGE_REDBOOT_DIR = 0xd0,
+ IMAGE_REDBOOT_CONFIG = 0xd1,
+ IMAGE_UFI_DIR = 0xd2,
+ IMAGE_BOOT_CODE = 0xe2
+};
struct controller_id {
u32 vendor;
@@ -1394,6 +1411,9 @@ struct be_cmd_read_flash_crc {
} __packed;
/**************** Lancer Firmware Flash ************/
+#define LANCER_FW_DOWNLOAD_CHUNK (32 * 1024)
+#define LANCER_FW_DOWNLOAD_LOCATION "/prg"
+
struct amap_lancer_write_obj_context {
u8 write_length[24];
u8 reserved1[7];
@@ -1654,11 +1674,7 @@ struct mgmt_hba_attribs {
struct mgmt_controller_attrib {
struct mgmt_hba_attribs hba_attribs;
- u32 rsvd0[2];
- u16 rsvd1;
- u8 pci_func_num;
- u8 rsvd2;
- u32 rsvd3[7];
+ u32 rsvd0[10];
} __packed;
struct be_cmd_req_cntl_attribs {
@@ -2083,6 +2099,7 @@ struct be_port_res_desc {
#define NV_TYPE_VXLAN 3
#define SOCVID_SHIFT 2 /* Strip outer vlan */
#define RCVID_SHIFT 4 /* Report vlan */
+#define PF_NUM_IGNORE 255
u8 nv_flags;
u8 rsvd2;
__le16 nv_port; /* vxlan/gre port */
@@ -2246,7 +2263,8 @@ struct be_cmd_resp_get_iface_list {
};
/*************** Set logical link ********************/
-#define PLINK_TRACK_SHIFT 8
+#define PLINK_ENABLE BIT(0)
+#define PLINK_TRACK BIT(8)
struct be_cmd_req_set_ll_link {
struct be_cmd_req_hdr hdr;
u32 link_config; /* Bit 0: UP_DOWN, Bit 9: PLINK */
@@ -2321,19 +2339,11 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
u8 page_num, u8 *data);
int be_cmd_query_cable_type(struct be_adapter *adapter);
int be_cmd_query_sfp_info(struct be_adapter *adapter);
-int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 flash_oper, u32 flash_opcode, u32 img_offset,
- u32 buf_size);
-int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 data_size, u32 data_offset,
- const char *obj_name, u32 *data_written,
- u8 *change_status, u8 *addn_status);
int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
u32 data_size, u32 data_offset, const char *obj_name,
u32 *data_read, u32 *eof, u8 *addn_status);
-int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name);
-int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
- u16 img_optype, u32 img_offset, u32 crc_offset);
+int lancer_fw_download(struct be_adapter *adapter, const struct firmware *fw);
+int be_fw_download(struct be_adapter *adapter, const struct firmware *fw);
int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
struct be_dma_mem *nonemb_cmd);
int be_cmd_fw_init(struct be_adapter *adapter);
@@ -2355,9 +2365,9 @@ int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate,
void be_detect_error(struct be_adapter *adapter);
int be_cmd_get_die_temperature(struct be_adapter *adapter);
int be_cmd_get_cntl_attributes(struct be_adapter *adapter);
+int be_cmd_get_fat_dump_len(struct be_adapter *adapter, u32 *dump_size);
+int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf);
int be_cmd_req_native_mode(struct be_adapter *adapter);
-int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size);
-int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf);
int be_cmd_get_fn_privileges(struct be_adapter *adapter, u32 *privilege,
u32 domain);
int be_cmd_set_fn_privileges(struct be_adapter *adapter, u32 privileges,
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index 734f655c99c1..a19ac441336f 100644
--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -241,17 +241,28 @@ static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
u32 data_read = 0, eof;
u8 addn_status;
struct be_dma_mem data_len_cmd;
- int status;
memset(&data_len_cmd, 0, sizeof(data_len_cmd));
/* data_offset and data_size should be 0 to get reg len */
- status = lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0,
- file_name, &data_read, &eof,
- &addn_status);
+ lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0, file_name,
+ &data_read, &eof, &addn_status);
return data_read;
}
+static int be_get_dump_len(struct be_adapter *adapter)
+{
+ u32 dump_size = 0;
+
+ if (lancer_chip(adapter))
+ dump_size = lancer_cmd_get_file_len(adapter,
+ LANCER_FW_DUMP_FILE);
+ else
+ dump_size = adapter->fat_dump_len;
+
+ return dump_size;
+}
+
static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
u32 buf_len, void *buf)
{
@@ -293,37 +304,18 @@ static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
return status;
}
-static int be_get_reg_len(struct net_device *netdev)
+static int be_read_dump_data(struct be_adapter *adapter, u32 dump_len,
+ void *buf)
{
- struct be_adapter *adapter = netdev_priv(netdev);
- u32 log_size = 0;
-
- if (!check_privilege(adapter, MAX_PRIVILEGES))
- return 0;
-
- if (be_physfn(adapter)) {
- if (lancer_chip(adapter))
- log_size = lancer_cmd_get_file_len(adapter,
- LANCER_FW_DUMP_FILE);
- else
- be_cmd_get_reg_len(adapter, &log_size);
- }
- return log_size;
-}
+ int status = 0;
-static void
-be_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
-{
- struct be_adapter *adapter = netdev_priv(netdev);
+ if (lancer_chip(adapter))
+ status = lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE,
+ dump_len, buf);
+ else
+ status = be_cmd_get_fat_dump(adapter, dump_len, buf);
- if (be_physfn(adapter)) {
- memset(buf, 0, regs->len);
- if (lancer_chip(adapter))
- lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE,
- regs->len, buf);
- else
- be_cmd_get_regs(adapter, regs->len, buf);
- }
+ return status;
}
static int be_get_coalesce(struct net_device *netdev,
@@ -916,6 +908,34 @@ static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
return be_load_fw(adapter, efl->data);
}
+static int
+be_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ if (!check_privilege(adapter, MAX_PRIVILEGES))
+ return -EOPNOTSUPP;
+
+ dump->len = be_get_dump_len(adapter);
+ dump->version = 1;
+ dump->flag = 0x1; /* FW dump is enabled */
+ return 0;
+}
+
+static int
+be_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
+ void *buf)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ int status;
+
+ if (!check_privilege(adapter, MAX_PRIVILEGES))
+ return -EOPNOTSUPP;
+
+ status = be_read_dump_data(adapter, dump->len, buf);
+ return be_cmd_status(status);
+}
+
static int be_get_eeprom_len(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
@@ -1313,8 +1333,6 @@ const struct ethtool_ops be_ethtool_ops = {
.set_msglevel = be_set_msg_level,
.get_sset_count = be_get_sset_count,
.get_ethtool_stats = be_get_ethtool_stats,
- .get_regs_len = be_get_reg_len,
- .get_regs = be_get_regs,
.flash_device = be_do_flash,
.self_test = be_self_test,
.get_rxnfc = be_get_rxnfc,
@@ -1323,6 +1341,8 @@ const struct ethtool_ops be_ethtool_ops = {
.get_rxfh_key_size = be_get_rxfh_key_size,
.get_rxfh = be_get_rxfh,
.set_rxfh = be_set_rxfh,
+ .get_dump_flag = be_get_dump_flag,
+ .get_dump_data = be_get_dump_data,
.get_channels = be_get_channels,
.set_channels = be_set_channels,
.get_module_info = be_get_module_info,
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 8a1d9fffd7d6..f99de3657ce3 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -729,7 +729,7 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter,
/* If vlan priority provided by OS is NOT in available bmap */
if (!(adapter->vlan_prio_bmap & (1 << vlan_prio)))
vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) |
- adapter->recommended_prio;
+ adapter->recommended_prio_bits;
return vlan_tag;
}
@@ -2184,7 +2184,6 @@ static void be_rx_compl_process_gro(struct be_rx_obj *rxo,
skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3);
skb->csum_level = rxcp->tunneled;
- skb_mark_napi_id(skb, napi);
if (rxcp->vlanf)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag);
@@ -2631,7 +2630,6 @@ static int be_evt_queues_create(struct be_adapter *adapter)
eqo->affinity_mask);
netif_napi_add(adapter->netdev, &eqo->napi, be_poll,
BE_NAPI_WEIGHT);
- napi_hash_add(&eqo->napi);
}
return 0;
}
@@ -4204,10 +4202,17 @@ static int be_get_config(struct be_adapter *adapter)
int status, level;
u16 profile_id;
+ status = be_cmd_get_cntl_attributes(adapter);
+ if (status)
+ return status;
+
status = be_cmd_query_fw_cfg(adapter);
if (status)
return status;
+ if (!lancer_chip(adapter) && be_physfn(adapter))
+ be_cmd_get_fat_dump_len(adapter, &adapter->fat_dump_len);
+
if (BEx_chip(adapter)) {
level = be_cmd_get_fw_log_level(adapter);
adapter->msg_enable =
@@ -4402,10 +4407,14 @@ static int be_setup(struct be_adapter *adapter)
if (!lancer_chip(adapter))
be_cmd_req_native_mode(adapter);
- /* Need to invoke this cmd first to get the PCI Function Number */
- status = be_cmd_get_cntl_attributes(adapter);
- if (status)
- return status;
+ /* invoke this cmd first to get pf_num and vf_num which are needed
+ * for issuing profile related cmds
+ */
+ if (!BEx_chip(adapter)) {
+ status = be_cmd_get_func_config(adapter, NULL);
+ if (status)
+ return status;
+ }
if (!BE2_chip(adapter) && be_physfn(adapter))
be_alloc_sriov_res(adapter);
@@ -4490,570 +4499,6 @@ static void be_netpoll(struct net_device *netdev)
}
#endif
-static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "};
-
-static bool phy_flashing_required(struct be_adapter *adapter)
-{
- return (adapter->phy.phy_type == PHY_TYPE_TN_8022 &&
- adapter->phy.interface_type == PHY_TYPE_BASET_10GB);
-}
-
-static bool is_comp_in_ufi(struct be_adapter *adapter,
- struct flash_section_info *fsec, int type)
-{
- int i = 0, img_type = 0;
- struct flash_section_info_g2 *fsec_g2 = NULL;
-
- if (BE2_chip(adapter))
- fsec_g2 = (struct flash_section_info_g2 *)fsec;
-
- for (i = 0; i < MAX_FLASH_COMP; i++) {
- if (fsec_g2)
- img_type = le32_to_cpu(fsec_g2->fsec_entry[i].type);
- else
- img_type = le32_to_cpu(fsec->fsec_entry[i].type);
-
- if (img_type == type)
- return true;
- }
- return false;
-
-}
-
-static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
- int header_size,
- const struct firmware *fw)
-{
- struct flash_section_info *fsec = NULL;
- const u8 *p = fw->data;
-
- p += header_size;
- while (p < (fw->data + fw->size)) {
- fsec = (struct flash_section_info *)p;
- if (!memcmp(flash_cookie, fsec->cookie, sizeof(flash_cookie)))
- return fsec;
- p += 32;
- }
- return NULL;
-}
-
-static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p,
- u32 img_offset, u32 img_size, int hdr_size,
- u16 img_optype, bool *crc_match)
-{
- u32 crc_offset;
- int status;
- u8 crc[4];
-
- status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_offset,
- img_size - 4);
- if (status)
- return status;
-
- crc_offset = hdr_size + img_offset + img_size - 4;
-
- /* Skip flashing, if crc of flashed region matches */
- if (!memcmp(crc, p + crc_offset, 4))
- *crc_match = true;
- else
- *crc_match = false;
-
- return status;
-}
-
-static int be_flash(struct be_adapter *adapter, const u8 *img,
- struct be_dma_mem *flash_cmd, int optype, int img_size,
- u32 img_offset)
-{
- u32 flash_op, num_bytes, total_bytes = img_size, bytes_sent = 0;
- struct be_cmd_write_flashrom *req = flash_cmd->va;
- int status;
-
- while (total_bytes) {
- num_bytes = min_t(u32, 32*1024, total_bytes);
-
- total_bytes -= num_bytes;
-
- if (!total_bytes) {
- if (optype == OPTYPE_PHY_FW)
- flash_op = FLASHROM_OPER_PHY_FLASH;
- else
- flash_op = FLASHROM_OPER_FLASH;
- } else {
- if (optype == OPTYPE_PHY_FW)
- flash_op = FLASHROM_OPER_PHY_SAVE;
- else
- flash_op = FLASHROM_OPER_SAVE;
- }
-
- memcpy(req->data_buf, img, num_bytes);
- img += num_bytes;
- status = be_cmd_write_flashrom(adapter, flash_cmd, optype,
- flash_op, img_offset +
- bytes_sent, num_bytes);
- if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST &&
- optype == OPTYPE_PHY_FW)
- break;
- else if (status)
- return status;
-
- bytes_sent += num_bytes;
- }
- return 0;
-}
-
-/* For BE2, BE3 and BE3-R */
-static int be_flash_BEx(struct be_adapter *adapter,
- const struct firmware *fw,
- struct be_dma_mem *flash_cmd, int num_of_images)
-{
- int img_hdrs_size = (num_of_images * sizeof(struct image_hdr));
- struct device *dev = &adapter->pdev->dev;
- struct flash_section_info *fsec = NULL;
- int status, i, filehdr_size, num_comp;
- const struct flash_comp *pflashcomp;
- bool crc_match;
- const u8 *p;
-
- struct flash_comp gen3_flash_types[] = {
- { FLASH_iSCSI_PRIMARY_IMAGE_START_g3, OPTYPE_ISCSI_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_iSCSI},
- { FLASH_REDBOOT_START_g3, OPTYPE_REDBOOT,
- FLASH_REDBOOT_IMAGE_MAX_SIZE_g3, IMAGE_BOOT_CODE},
- { FLASH_iSCSI_BIOS_START_g3, OPTYPE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_ISCSI},
- { FLASH_PXE_BIOS_START_g3, OPTYPE_PXE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_PXE},
- { FLASH_FCoE_BIOS_START_g3, OPTYPE_FCOE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_FCoE},
- { FLASH_iSCSI_BACKUP_IMAGE_START_g3, OPTYPE_ISCSI_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_BACKUP_iSCSI},
- { FLASH_FCoE_PRIMARY_IMAGE_START_g3, OPTYPE_FCOE_FW_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_FCoE},
- { FLASH_FCoE_BACKUP_IMAGE_START_g3, OPTYPE_FCOE_FW_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_BACKUP_FCoE},
- { FLASH_NCSI_START_g3, OPTYPE_NCSI_FW,
- FLASH_NCSI_IMAGE_MAX_SIZE_g3, IMAGE_NCSI},
- { FLASH_PHY_FW_START_g3, OPTYPE_PHY_FW,
- FLASH_PHY_FW_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_PHY}
- };
-
- struct flash_comp gen2_flash_types[] = {
- { FLASH_iSCSI_PRIMARY_IMAGE_START_g2, OPTYPE_ISCSI_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_iSCSI},
- { FLASH_REDBOOT_START_g2, OPTYPE_REDBOOT,
- FLASH_REDBOOT_IMAGE_MAX_SIZE_g2, IMAGE_BOOT_CODE},
- { FLASH_iSCSI_BIOS_START_g2, OPTYPE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_ISCSI},
- { FLASH_PXE_BIOS_START_g2, OPTYPE_PXE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_PXE},
- { FLASH_FCoE_BIOS_START_g2, OPTYPE_FCOE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_FCoE},
- { FLASH_iSCSI_BACKUP_IMAGE_START_g2, OPTYPE_ISCSI_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_BACKUP_iSCSI},
- { FLASH_FCoE_PRIMARY_IMAGE_START_g2, OPTYPE_FCOE_FW_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_FCoE},
- { FLASH_FCoE_BACKUP_IMAGE_START_g2, OPTYPE_FCOE_FW_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_BACKUP_FCoE}
- };
-
- if (BE3_chip(adapter)) {
- pflashcomp = gen3_flash_types;
- filehdr_size = sizeof(struct flash_file_hdr_g3);
- num_comp = ARRAY_SIZE(gen3_flash_types);
- } else {
- pflashcomp = gen2_flash_types;
- filehdr_size = sizeof(struct flash_file_hdr_g2);
- num_comp = ARRAY_SIZE(gen2_flash_types);
- img_hdrs_size = 0;
- }
-
- /* Get flash section info*/
- fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
- if (!fsec) {
- dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
- return -1;
- }
- for (i = 0; i < num_comp; i++) {
- if (!is_comp_in_ufi(adapter, fsec, pflashcomp[i].img_type))
- continue;
-
- if ((pflashcomp[i].optype == OPTYPE_NCSI_FW) &&
- memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0)
- continue;
-
- if (pflashcomp[i].optype == OPTYPE_PHY_FW &&
- !phy_flashing_required(adapter))
- continue;
-
- if (pflashcomp[i].optype == OPTYPE_REDBOOT) {
- status = be_check_flash_crc(adapter, fw->data,
- pflashcomp[i].offset,
- pflashcomp[i].size,
- filehdr_size +
- img_hdrs_size,
- OPTYPE_REDBOOT, &crc_match);
- if (status) {
- dev_err(dev,
- "Could not get CRC for 0x%x region\n",
- pflashcomp[i].optype);
- continue;
- }
-
- if (crc_match)
- continue;
- }
-
- p = fw->data + filehdr_size + pflashcomp[i].offset +
- img_hdrs_size;
- if (p + pflashcomp[i].size > fw->data + fw->size)
- return -1;
-
- status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype,
- pflashcomp[i].size, 0);
- if (status) {
- dev_err(dev, "Flashing section type 0x%x failed\n",
- pflashcomp[i].img_type);
- return status;
- }
- }
- return 0;
-}
-
-static u16 be_get_img_optype(struct flash_section_entry fsec_entry)
-{
- u32 img_type = le32_to_cpu(fsec_entry.type);
- u16 img_optype = le16_to_cpu(fsec_entry.optype);
-
- if (img_optype != 0xFFFF)
- return img_optype;
-
- switch (img_type) {
- case IMAGE_FIRMWARE_iSCSI:
- img_optype = OPTYPE_ISCSI_ACTIVE;
- break;
- case IMAGE_BOOT_CODE:
- img_optype = OPTYPE_REDBOOT;
- break;
- case IMAGE_OPTION_ROM_ISCSI:
- img_optype = OPTYPE_BIOS;
- break;
- case IMAGE_OPTION_ROM_PXE:
- img_optype = OPTYPE_PXE_BIOS;
- break;
- case IMAGE_OPTION_ROM_FCoE:
- img_optype = OPTYPE_FCOE_BIOS;
- break;
- case IMAGE_FIRMWARE_BACKUP_iSCSI:
- img_optype = OPTYPE_ISCSI_BACKUP;
- break;
- case IMAGE_NCSI:
- img_optype = OPTYPE_NCSI_FW;
- break;
- case IMAGE_FLASHISM_JUMPVECTOR:
- img_optype = OPTYPE_FLASHISM_JUMPVECTOR;
- break;
- case IMAGE_FIRMWARE_PHY:
- img_optype = OPTYPE_SH_PHY_FW;
- break;
- case IMAGE_REDBOOT_DIR:
- img_optype = OPTYPE_REDBOOT_DIR;
- break;
- case IMAGE_REDBOOT_CONFIG:
- img_optype = OPTYPE_REDBOOT_CONFIG;
- break;
- case IMAGE_UFI_DIR:
- img_optype = OPTYPE_UFI_DIR;
- break;
- default:
- break;
- }
-
- return img_optype;
-}
-
-static int be_flash_skyhawk(struct be_adapter *adapter,
- const struct firmware *fw,
- struct be_dma_mem *flash_cmd, int num_of_images)
-{
- int img_hdrs_size = num_of_images * sizeof(struct image_hdr);
- bool crc_match, old_fw_img, flash_offset_support = true;
- struct device *dev = &adapter->pdev->dev;
- struct flash_section_info *fsec = NULL;
- u32 img_offset, img_size, img_type;
- u16 img_optype, flash_optype;
- int status, i, filehdr_size;
- const u8 *p;
-
- filehdr_size = sizeof(struct flash_file_hdr_g3);
- fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
- if (!fsec) {
- dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
- return -EINVAL;
- }
-
-retry_flash:
- for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) {
- img_offset = le32_to_cpu(fsec->fsec_entry[i].offset);
- img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size);
- img_type = le32_to_cpu(fsec->fsec_entry[i].type);
- img_optype = be_get_img_optype(fsec->fsec_entry[i]);
- old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF;
-
- if (img_optype == 0xFFFF)
- continue;
-
- if (flash_offset_support)
- flash_optype = OPTYPE_OFFSET_SPECIFIED;
- else
- flash_optype = img_optype;
-
- /* Don't bother verifying CRC if an old FW image is being
- * flashed
- */
- if (old_fw_img)
- goto flash;
-
- status = be_check_flash_crc(adapter, fw->data, img_offset,
- img_size, filehdr_size +
- img_hdrs_size, flash_optype,
- &crc_match);
- if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST ||
- base_status(status) == MCC_STATUS_ILLEGAL_FIELD) {
- /* The current FW image on the card does not support
- * OFFSET based flashing. Retry using older mechanism
- * of OPTYPE based flashing
- */
- if (flash_optype == OPTYPE_OFFSET_SPECIFIED) {
- flash_offset_support = false;
- goto retry_flash;
- }
-
- /* The current FW image on the card does not recognize
- * the new FLASH op_type. The FW download is partially
- * complete. Reboot the server now to enable FW image
- * to recognize the new FLASH op_type. To complete the
- * remaining process, download the same FW again after
- * the reboot.
- */
- dev_err(dev, "Flash incomplete. Reset the server\n");
- dev_err(dev, "Download FW image again after reset\n");
- return -EAGAIN;
- } else if (status) {
- dev_err(dev, "Could not get CRC for 0x%x region\n",
- img_optype);
- return -EFAULT;
- }
-
- if (crc_match)
- continue;
-
-flash:
- p = fw->data + filehdr_size + img_offset + img_hdrs_size;
- if (p + img_size > fw->data + fw->size)
- return -1;
-
- status = be_flash(adapter, p, flash_cmd, flash_optype, img_size,
- img_offset);
-
- /* The current FW image on the card does not support OFFSET
- * based flashing. Retry using older mechanism of OPTYPE based
- * flashing
- */
- if (base_status(status) == MCC_STATUS_ILLEGAL_FIELD &&
- flash_optype == OPTYPE_OFFSET_SPECIFIED) {
- flash_offset_support = false;
- goto retry_flash;
- }
-
- /* For old FW images ignore ILLEGAL_FIELD error or errors on
- * UFI_DIR region
- */
- if (old_fw_img &&
- (base_status(status) == MCC_STATUS_ILLEGAL_FIELD ||
- (img_optype == OPTYPE_UFI_DIR &&
- base_status(status) == MCC_STATUS_FAILED))) {
- continue;
- } else if (status) {
- dev_err(dev, "Flashing section type 0x%x failed\n",
- img_type);
- return -EFAULT;
- }
- }
- return 0;
-}
-
-static int lancer_fw_download(struct be_adapter *adapter,
- const struct firmware *fw)
-{
-#define LANCER_FW_DOWNLOAD_CHUNK (32 * 1024)
-#define LANCER_FW_DOWNLOAD_LOCATION "/prg"
- struct device *dev = &adapter->pdev->dev;
- struct be_dma_mem flash_cmd;
- const u8 *data_ptr = NULL;
- u8 *dest_image_ptr = NULL;
- size_t image_size = 0;
- u32 chunk_size = 0;
- u32 data_written = 0;
- u32 offset = 0;
- int status = 0;
- u8 add_status = 0;
- u8 change_status;
-
- if (!IS_ALIGNED(fw->size, sizeof(u32))) {
- dev_err(dev, "FW image size should be multiple of 4\n");
- return -EINVAL;
- }
-
- flash_cmd.size = sizeof(struct lancer_cmd_req_write_object)
- + LANCER_FW_DOWNLOAD_CHUNK;
- flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size,
- &flash_cmd.dma, GFP_KERNEL);
- if (!flash_cmd.va)
- return -ENOMEM;
-
- dest_image_ptr = flash_cmd.va +
- sizeof(struct lancer_cmd_req_write_object);
- image_size = fw->size;
- data_ptr = fw->data;
-
- while (image_size) {
- chunk_size = min_t(u32, image_size, LANCER_FW_DOWNLOAD_CHUNK);
-
- /* Copy the image chunk content. */
- memcpy(dest_image_ptr, data_ptr, chunk_size);
-
- status = lancer_cmd_write_object(adapter, &flash_cmd,
- chunk_size, offset,
- LANCER_FW_DOWNLOAD_LOCATION,
- &data_written, &change_status,
- &add_status);
- if (status)
- break;
-
- offset += data_written;
- data_ptr += data_written;
- image_size -= data_written;
- }
-
- if (!status) {
- /* Commit the FW written */
- status = lancer_cmd_write_object(adapter, &flash_cmd,
- 0, offset,
- LANCER_FW_DOWNLOAD_LOCATION,
- &data_written, &change_status,
- &add_status);
- }
-
- dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
- if (status) {
- dev_err(dev, "Firmware load error\n");
- return be_cmd_status(status);
- }
-
- dev_info(dev, "Firmware flashed successfully\n");
-
- if (change_status == LANCER_FW_RESET_NEEDED) {
- dev_info(dev, "Resetting adapter to activate new FW\n");
- status = lancer_physdev_ctrl(adapter,
- PHYSDEV_CONTROL_FW_RESET_MASK);
- if (status) {
- dev_err(dev, "Adapter busy, could not reset FW\n");
- dev_err(dev, "Reboot server to activate new FW\n");
- }
- } else if (change_status != LANCER_NO_RESET_NEEDED) {
- dev_info(dev, "Reboot server to activate new FW\n");
- }
-
- return 0;
-}
-
-/* Check if the flash image file is compatible with the adapter that
- * is being flashed.
- */
-static bool be_check_ufi_compatibility(struct be_adapter *adapter,
- struct flash_file_hdr_g3 *fhdr)
-{
- if (!fhdr) {
- dev_err(&adapter->pdev->dev, "Invalid FW UFI file");
- return false;
- }
-
- /* First letter of the build version is used to identify
- * which chip this image file is meant for.
- */
- switch (fhdr->build[0]) {
- case BLD_STR_UFI_TYPE_SH:
- if (!skyhawk_chip(adapter))
- return false;
- break;
- case BLD_STR_UFI_TYPE_BE3:
- if (!BE3_chip(adapter))
- return false;
- break;
- case BLD_STR_UFI_TYPE_BE2:
- if (!BE2_chip(adapter))
- return false;
- break;
- default:
- return false;
- }
-
- /* In BE3 FW images the "asic_type_rev" field doesn't track the
- * asic_rev of the chips it is compatible with.
- * When asic_type_rev is 0 the image is compatible only with
- * pre-BE3-R chips (asic_rev < 0x10)
- */
- if (BEx_chip(adapter) && fhdr->asic_type_rev == 0)
- return adapter->asic_rev < 0x10;
- else
- return (fhdr->asic_type_rev >= adapter->asic_rev);
-}
-
-static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
-{
- struct device *dev = &adapter->pdev->dev;
- struct flash_file_hdr_g3 *fhdr3;
- struct image_hdr *img_hdr_ptr;
- int status = 0, i, num_imgs;
- struct be_dma_mem flash_cmd;
-
- fhdr3 = (struct flash_file_hdr_g3 *)fw->data;
- if (!be_check_ufi_compatibility(adapter, fhdr3)) {
- dev_err(dev, "Flash image is not compatible with adapter\n");
- return -EINVAL;
- }
-
- flash_cmd.size = sizeof(struct be_cmd_write_flashrom);
- flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
- GFP_KERNEL);
- if (!flash_cmd.va)
- return -ENOMEM;
-
- num_imgs = le32_to_cpu(fhdr3->num_imgs);
- for (i = 0; i < num_imgs; i++) {
- img_hdr_ptr = (struct image_hdr *)(fw->data +
- (sizeof(struct flash_file_hdr_g3) +
- i * sizeof(struct image_hdr)));
- if (!BE2_chip(adapter) &&
- le32_to_cpu(img_hdr_ptr->imageid) != 1)
- continue;
-
- if (skyhawk_chip(adapter))
- status = be_flash_skyhawk(adapter, fw, &flash_cmd,
- num_imgs);
- else
- status = be_flash_BEx(adapter, fw, &flash_cmd,
- num_imgs);
- }
-
- dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
- if (!status)
- dev_info(dev, "Firmware flashed successfully\n");
-
- return status;
-}
-
int be_load_fw(struct be_adapter *adapter, u8 *fw_file)
{
const struct firmware *fw;
@@ -5108,6 +4553,9 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
return -EINVAL;
mode = nla_get_u16(attr);
+ if (BE3_chip(adapter) && mode == BRIDGE_MODE_VEPA)
+ return -EOPNOTSUPP;
+
if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB)
return -EINVAL;
@@ -5289,7 +4737,7 @@ static netdev_features_t be_features_check(struct sk_buff *skb,
skb->inner_protocol != htons(ETH_P_TEB) ||
skb_inner_mac_header(skb) - skb_transport_header(skb) !=
sizeof(struct udphdr) + sizeof(struct vxlanhdr))
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
return features;
}
diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c
index ff665493ca97..62fa136554ac 100644
--- a/drivers/net/ethernet/ethoc.c
+++ b/drivers/net/ethernet/ethoc.c
@@ -678,7 +678,7 @@ static int ethoc_mdio_probe(struct net_device *dev)
int err;
if (priv->phy_id != -1)
- phy = priv->mdio->phy_map[priv->phy_id];
+ phy = mdiobus_get_phy(priv->mdio, priv->phy_id);
else
phy = phy_find_first(priv->mdio);
@@ -766,7 +766,7 @@ static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (mdio->phy_id >= PHY_MAX_ADDR)
return -ERANGE;
- phy = priv->mdio->phy_map[mdio->phy_id];
+ phy = mdiobus_get_phy(priv->mdio, mdio->phy_id);
if (!phy)
return -ENODEV;
} else {
@@ -1015,7 +1015,6 @@ static int ethoc_probe(struct platform_device *pdev)
struct resource *mmio = NULL;
struct resource *mem = NULL;
struct ethoc *priv = NULL;
- unsigned int phy;
int num_bd;
int ret = 0;
bool random_mac = false;
@@ -1206,19 +1205,10 @@ static int ethoc_probe(struct platform_device *pdev)
priv->mdio->write = ethoc_mdio_write;
priv->mdio->priv = priv;
- priv->mdio->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!priv->mdio->irq) {
- ret = -ENOMEM;
- goto free_mdio;
- }
-
- for (phy = 0; phy < PHY_MAX_ADDR; phy++)
- priv->mdio->irq[phy] = PHY_POLL;
-
ret = mdiobus_register(priv->mdio);
if (ret) {
dev_err(&netdev->dev, "failed to register MDIO bus\n");
- goto free_mdio;
+ goto free;
}
ret = ethoc_mdio_probe(netdev);
@@ -1250,8 +1240,6 @@ error2:
netif_napi_del(&priv->napi);
error:
mdiobus_unregister(priv->mdio);
-free_mdio:
- kfree(priv->mdio->irq);
mdiobus_free(priv->mdio);
free:
if (priv->clk)
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 6d0c5d5eea6d..84384e1585a5 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -71,7 +71,6 @@ struct ftgmac100 {
struct napi_struct napi;
struct mii_bus *mii_bus;
- int phy_irq[PHY_MAX_ADDR];
struct phy_device *phydev;
int old_speed;
};
@@ -835,26 +834,15 @@ static void ftgmac100_adjust_link(struct net_device *netdev)
static int ftgmac100_mii_probe(struct ftgmac100 *priv)
{
struct net_device *netdev = priv->netdev;
- struct phy_device *phydev = NULL;
- int i;
-
- /* search for connect PHY device */
- for (i = 0; i < PHY_MAX_ADDR; i++) {
- struct phy_device *tmp = priv->mii_bus->phy_map[i];
-
- if (tmp) {
- phydev = tmp;
- break;
- }
- }
+ struct phy_device *phydev;
- /* now we are supposed to have a proper phydev, to attach to... */
+ phydev = phy_find_first(priv->mii_bus);
if (!phydev) {
netdev_info(netdev, "%s: no PHY found\n", netdev->name);
return -ENODEV;
}
- phydev = phy_connect(netdev, dev_name(&phydev->dev),
+ phydev = phy_connect(netdev, phydev_name(phydev),
&ftgmac100_adjust_link, PHY_INTERFACE_MODE_GMII);
if (IS_ERR(phydev)) {
@@ -1188,7 +1176,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
struct net_device *netdev;
struct ftgmac100 *priv;
int err;
- int i;
if (!pdev)
return -ENODEV;
@@ -1257,10 +1244,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
priv->mii_bus->priv = netdev;
priv->mii_bus->read = ftgmac100_mdiobus_read;
priv->mii_bus->write = ftgmac100_mdiobus_write;
- priv->mii_bus->irq = priv->phy_irq;
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- priv->mii_bus->irq[i] = PHY_POLL;
err = mdiobus_register(priv->mii_bus);
if (err) {
diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
index bee32a9d9876..d1ca45fbb164 100644
--- a/drivers/net/ethernet/freescale/Kconfig
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -54,6 +54,7 @@ config FEC_MPC52xx_MDIO
If compiled as module, it will be called fec_mpc52xx_phy.
source "drivers/net/ethernet/freescale/fs_enet/Kconfig"
+source "drivers/net/ethernet/freescale/fman/Kconfig"
config FSL_PQ_MDIO
tristate "Freescale PQ MDIO"
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
index 71debd1c18c9..4097c58d17a7 100644
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -17,3 +17,5 @@ gianfar_driver-objs := gianfar.o \
gianfar_ethtool.o
obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
+
+obj-$(CONFIG_FSL_FMAN) += fman/
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b2a32209ffbf..502da6f48f95 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -48,6 +48,7 @@
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/mdio.h>
#include <linux/phy.h>
#include <linux/fec.h>
#include <linux/of.h>
@@ -1926,11 +1927,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
} else {
/* check for attached phy */
for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
- if ((fep->mii_bus->phy_mask & (1 << phy_id)))
- continue;
- if (fep->mii_bus->phy_map[phy_id] == NULL)
- continue;
- if (fep->mii_bus->phy_map[phy_id]->phy_id == 0)
+ if (!mdiobus_is_registered_device(fep->mii_bus, phy_id))
continue;
if (dev_id--)
continue;
@@ -1972,9 +1969,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
fep->link = 0;
fep->full_duplex = 0;
- netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
- fep->phy_dev->irq);
+ phy_attached_info(phy_dev);
return 0;
}
@@ -1985,7 +1980,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
struct device_node *node;
- int err = -ENXIO, i;
+ int err = -ENXIO;
u32 mii_speed, holdtime;
/*
@@ -2067,15 +2062,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
fep->mii_bus->priv = fep;
fep->mii_bus->parent = &pdev->dev;
- fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!fep->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- fep->mii_bus->irq[i] = PHY_POLL;
-
node = of_get_child_by_name(pdev->dev.of_node, "mdio");
if (node) {
err = of_mdiobus_register(fep->mii_bus, node);
@@ -2085,7 +2071,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
}
if (err)
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
mii_cnt++;
@@ -2095,8 +2081,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
return 0;
-err_out_free_mdio_irq:
- kfree(fep->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(fep->mii_bus);
err_out:
@@ -2107,7 +2091,6 @@ static void fec_enet_mii_remove(struct fec_enet_private *fep)
{
if (--mii_cnt == 0) {
mdiobus_unregister(fep->mii_bus);
- kfree(fep->mii_bus->irq);
mdiobus_free(fep->mii_bus);
}
}
@@ -3277,7 +3260,6 @@ static void
fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
{
struct device_node *np = pdev->dev.of_node;
- int err;
*num_tx = *num_rx = 1;
@@ -3285,13 +3267,9 @@ fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
return;
/* parse the num of tx and rx queues */
- err = of_property_read_u32(np, "fsl,num-tx-queues", num_tx);
- if (err)
- *num_tx = 1;
+ of_property_read_u32(np, "fsl,num-tx-queues", num_tx);
- err = of_property_read_u32(np, "fsl,num-rx-queues", num_rx);
- if (err)
- *num_rx = 1;
+ of_property_read_u32(np, "fsl,num-rx-queues", num_rx);
if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) {
dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n",
diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c
index afe7f39cdd7c..25553ee857b4 100644
--- a/drivers/net/ethernet/freescale/fec_mpc52xx.c
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c
@@ -1084,27 +1084,23 @@ static struct platform_driver mpc52xx_fec_driver = {
/* Module */
/* ======================================================================== */
+static struct platform_driver * const drivers[] = {
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+ &mpc52xx_fec_mdio_driver,
+#endif
+ &mpc52xx_fec_driver,
+};
+
static int __init
mpc52xx_fec_init(void)
{
-#ifdef CONFIG_FEC_MPC52xx_MDIO
- int ret;
- ret = platform_driver_register(&mpc52xx_fec_mdio_driver);
- if (ret) {
- pr_err("failed to register mdio driver\n");
- return ret;
- }
-#endif
- return platform_driver_register(&mpc52xx_fec_driver);
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
static void __exit
mpc52xx_fec_exit(void)
{
- platform_driver_unregister(&mpc52xx_fec_driver);
-#ifdef CONFIG_FEC_MPC52xx_MDIO
- platform_driver_unregister(&mpc52xx_fec_mdio_driver);
-#endif
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
index 1e647beaf989..b5497e308302 100644
--- a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
@@ -22,7 +22,6 @@
struct mpc52xx_fec_mdio_priv {
struct mpc52xx_fec __iomem *regs;
- int mdio_irqs[PHY_MAX_ADDR];
};
static int mpc52xx_fec_mdio_transfer(struct mii_bus *bus, int phy_id,
@@ -83,9 +82,6 @@ static int mpc52xx_fec_mdio_probe(struct platform_device *of)
bus->read = mpc52xx_fec_mdio_read;
bus->write = mpc52xx_fec_mdio_write;
- /* setup irqs */
- bus->irq = priv->mdio_irqs;
-
/* setup registers */
err = of_address_to_resource(np, 0, &res);
if (err)
diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig
new file mode 100644
index 000000000000..79b7c84b7869
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/Kconfig
@@ -0,0 +1,9 @@
+config FSL_FMAN
+ tristate "FMan support"
+ depends on FSL_SOC || COMPILE_TEST
+ select GENERIC_ALLOCATOR
+ select PHYLIB
+ default n
+ help
+ Freescale Data-Path Acceleration Architecture Frame Manager
+ (FMan) support
diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile
new file mode 100644
index 000000000000..51fd2e6c1b84
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/Makefile
@@ -0,0 +1,7 @@
+subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman
+
+obj-y += fsl_fman.o fsl_fman_mac.o fsl_mac.o
+
+fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_port.o
+fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o
+fsl_mac-objs += mac.o
diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c
new file mode 100644
index 000000000000..623aa1c8ebc6
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman.c
@@ -0,0 +1,2871 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman.h"
+#include "fman_muram.h"
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/libfdt_env.h>
+
+/* General defines */
+#define FMAN_LIODN_TBL 64 /* size of LIODN table */
+#define MAX_NUM_OF_MACS 10
+#define FM_NUM_OF_FMAN_CTRL_EVENT_REGS 4
+#define BASE_RX_PORTID 0x08
+#define BASE_TX_PORTID 0x28
+
+/* Modules registers offsets */
+#define BMI_OFFSET 0x00080000
+#define QMI_OFFSET 0x00080400
+#define DMA_OFFSET 0x000C2000
+#define FPM_OFFSET 0x000C3000
+#define IMEM_OFFSET 0x000C4000
+#define CGP_OFFSET 0x000DB000
+
+/* Exceptions bit map */
+#define EX_DMA_BUS_ERROR 0x80000000
+#define EX_DMA_READ_ECC 0x40000000
+#define EX_DMA_SYSTEM_WRITE_ECC 0x20000000
+#define EX_DMA_FM_WRITE_ECC 0x10000000
+#define EX_FPM_STALL_ON_TASKS 0x08000000
+#define EX_FPM_SINGLE_ECC 0x04000000
+#define EX_FPM_DOUBLE_ECC 0x02000000
+#define EX_QMI_SINGLE_ECC 0x01000000
+#define EX_QMI_DEQ_FROM_UNKNOWN_PORTID 0x00800000
+#define EX_QMI_DOUBLE_ECC 0x00400000
+#define EX_BMI_LIST_RAM_ECC 0x00200000
+#define EX_BMI_STORAGE_PROFILE_ECC 0x00100000
+#define EX_BMI_STATISTICS_RAM_ECC 0x00080000
+#define EX_IRAM_ECC 0x00040000
+#define EX_MURAM_ECC 0x00020000
+#define EX_BMI_DISPATCH_RAM_ECC 0x00010000
+#define EX_DMA_SINGLE_PORT_ECC 0x00008000
+
+/* DMA defines */
+/* masks */
+#define DMA_MODE_BER 0x00200000
+#define DMA_MODE_ECC 0x00000020
+#define DMA_MODE_SECURE_PROT 0x00000800
+#define DMA_MODE_AXI_DBG_MASK 0x0F000000
+
+#define DMA_TRANSFER_PORTID_MASK 0xFF000000
+#define DMA_TRANSFER_TNUM_MASK 0x00FF0000
+#define DMA_TRANSFER_LIODN_MASK 0x00000FFF
+
+#define DMA_STATUS_BUS_ERR 0x08000000
+#define DMA_STATUS_READ_ECC 0x04000000
+#define DMA_STATUS_SYSTEM_WRITE_ECC 0x02000000
+#define DMA_STATUS_FM_WRITE_ECC 0x01000000
+#define DMA_STATUS_FM_SPDAT_ECC 0x00080000
+
+#define DMA_MODE_CACHE_OR_SHIFT 30
+#define DMA_MODE_AXI_DBG_SHIFT 24
+#define DMA_MODE_CEN_SHIFT 13
+#define DMA_MODE_CEN_MASK 0x00000007
+#define DMA_MODE_DBG_SHIFT 7
+#define DMA_MODE_AID_MODE_SHIFT 4
+
+#define DMA_THRESH_COMMQ_SHIFT 24
+#define DMA_THRESH_READ_INT_BUF_SHIFT 16
+#define DMA_THRESH_READ_INT_BUF_MASK 0x0000003f
+#define DMA_THRESH_WRITE_INT_BUF_MASK 0x0000003f
+
+#define DMA_TRANSFER_PORTID_SHIFT 24
+#define DMA_TRANSFER_TNUM_SHIFT 16
+
+#define DMA_CAM_SIZEOF_ENTRY 0x40
+#define DMA_CAM_UNITS 8
+
+#define DMA_LIODN_SHIFT 16
+#define DMA_LIODN_BASE_MASK 0x00000FFF
+
+/* FPM defines */
+#define FPM_EV_MASK_DOUBLE_ECC 0x80000000
+#define FPM_EV_MASK_STALL 0x40000000
+#define FPM_EV_MASK_SINGLE_ECC 0x20000000
+#define FPM_EV_MASK_RELEASE_FM 0x00010000
+#define FPM_EV_MASK_DOUBLE_ECC_EN 0x00008000
+#define FPM_EV_MASK_STALL_EN 0x00004000
+#define FPM_EV_MASK_SINGLE_ECC_EN 0x00002000
+#define FPM_EV_MASK_EXTERNAL_HALT 0x00000008
+#define FPM_EV_MASK_ECC_ERR_HALT 0x00000004
+
+#define FPM_RAM_MURAM_ECC 0x00008000
+#define FPM_RAM_IRAM_ECC 0x00004000
+#define FPM_IRAM_ECC_ERR_EX_EN 0x00020000
+#define FPM_MURAM_ECC_ERR_EX_EN 0x00040000
+#define FPM_RAM_IRAM_ECC_EN 0x40000000
+#define FPM_RAM_RAMS_ECC_EN 0x80000000
+#define FPM_RAM_RAMS_ECC_EN_SRC_SEL 0x08000000
+
+#define FPM_REV1_MAJOR_MASK 0x0000FF00
+#define FPM_REV1_MINOR_MASK 0x000000FF
+
+#define FPM_DISP_LIMIT_SHIFT 24
+
+#define FPM_PRT_FM_CTL1 0x00000001
+#define FPM_PRT_FM_CTL2 0x00000002
+#define FPM_PORT_FM_CTL_PORTID_SHIFT 24
+#define FPM_PRC_ORA_FM_CTL_SEL_SHIFT 16
+
+#define FPM_THR1_PRS_SHIFT 24
+#define FPM_THR1_KG_SHIFT 16
+#define FPM_THR1_PLCR_SHIFT 8
+#define FPM_THR1_BMI_SHIFT 0
+
+#define FPM_THR2_QMI_ENQ_SHIFT 24
+#define FPM_THR2_QMI_DEQ_SHIFT 0
+#define FPM_THR2_FM_CTL1_SHIFT 16
+#define FPM_THR2_FM_CTL2_SHIFT 8
+
+#define FPM_EV_MASK_CAT_ERR_SHIFT 1
+#define FPM_EV_MASK_DMA_ERR_SHIFT 0
+
+#define FPM_REV1_MAJOR_SHIFT 8
+
+#define FPM_RSTC_FM_RESET 0x80000000
+#define FPM_RSTC_MAC0_RESET 0x40000000
+#define FPM_RSTC_MAC1_RESET 0x20000000
+#define FPM_RSTC_MAC2_RESET 0x10000000
+#define FPM_RSTC_MAC3_RESET 0x08000000
+#define FPM_RSTC_MAC8_RESET 0x04000000
+#define FPM_RSTC_MAC4_RESET 0x02000000
+#define FPM_RSTC_MAC5_RESET 0x01000000
+#define FPM_RSTC_MAC6_RESET 0x00800000
+#define FPM_RSTC_MAC7_RESET 0x00400000
+#define FPM_RSTC_MAC9_RESET 0x00200000
+
+#define FPM_TS_INT_SHIFT 16
+#define FPM_TS_CTL_EN 0x80000000
+
+/* BMI defines */
+#define BMI_INIT_START 0x80000000
+#define BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC 0x80000000
+#define BMI_ERR_INTR_EN_LIST_RAM_ECC 0x40000000
+#define BMI_ERR_INTR_EN_STATISTICS_RAM_ECC 0x20000000
+#define BMI_ERR_INTR_EN_DISPATCH_RAM_ECC 0x10000000
+#define BMI_NUM_OF_TASKS_MASK 0x3F000000
+#define BMI_NUM_OF_EXTRA_TASKS_MASK 0x000F0000
+#define BMI_NUM_OF_DMAS_MASK 0x00000F00
+#define BMI_NUM_OF_EXTRA_DMAS_MASK 0x0000000F
+#define BMI_FIFO_SIZE_MASK 0x000003FF
+#define BMI_EXTRA_FIFO_SIZE_MASK 0x03FF0000
+#define BMI_CFG2_DMAS_MASK 0x0000003F
+#define BMI_CFG2_TASKS_MASK 0x0000003F
+
+#define BMI_CFG2_TASKS_SHIFT 16
+#define BMI_CFG2_DMAS_SHIFT 0
+#define BMI_CFG1_FIFO_SIZE_SHIFT 16
+#define BMI_NUM_OF_TASKS_SHIFT 24
+#define BMI_EXTRA_NUM_OF_TASKS_SHIFT 16
+#define BMI_NUM_OF_DMAS_SHIFT 8
+#define BMI_EXTRA_NUM_OF_DMAS_SHIFT 0
+
+#define BMI_FIFO_ALIGN 0x100
+
+#define BMI_EXTRA_FIFO_SIZE_SHIFT 16
+
+/* QMI defines */
+#define QMI_CFG_ENQ_EN 0x80000000
+#define QMI_CFG_DEQ_EN 0x40000000
+#define QMI_CFG_EN_COUNTERS 0x10000000
+#define QMI_CFG_DEQ_MASK 0x0000003F
+#define QMI_CFG_ENQ_MASK 0x00003F00
+#define QMI_CFG_ENQ_SHIFT 8
+
+#define QMI_ERR_INTR_EN_DOUBLE_ECC 0x80000000
+#define QMI_ERR_INTR_EN_DEQ_FROM_DEF 0x40000000
+#define QMI_INTR_EN_SINGLE_ECC 0x80000000
+
+#define QMI_GS_HALT_NOT_BUSY 0x00000002
+
+/* IRAM defines */
+#define IRAM_IADD_AIE 0x80000000
+#define IRAM_READY 0x80000000
+
+/* Default values */
+#define DEFAULT_CATASTROPHIC_ERR 0
+#define DEFAULT_DMA_ERR 0
+#define DEFAULT_AID_MODE FMAN_DMA_AID_OUT_TNUM
+#define DEFAULT_DMA_COMM_Q_LOW 0x2A
+#define DEFAULT_DMA_COMM_Q_HIGH 0x3F
+#define DEFAULT_CACHE_OVERRIDE 0
+#define DEFAULT_DMA_CAM_NUM_OF_ENTRIES 64
+#define DEFAULT_DMA_DBG_CNT_MODE 0
+#define DEFAULT_DMA_SOS_EMERGENCY 0
+#define DEFAULT_DMA_WATCHDOG 0
+#define DEFAULT_DISP_LIMIT 0
+#define DEFAULT_PRS_DISP_TH 16
+#define DEFAULT_PLCR_DISP_TH 16
+#define DEFAULT_KG_DISP_TH 16
+#define DEFAULT_BMI_DISP_TH 16
+#define DEFAULT_QMI_ENQ_DISP_TH 16
+#define DEFAULT_QMI_DEQ_DISP_TH 16
+#define DEFAULT_FM_CTL1_DISP_TH 16
+#define DEFAULT_FM_CTL2_DISP_TH 16
+
+#define DFLT_AXI_DBG_NUM_OF_BEATS 1
+
+#define DFLT_DMA_READ_INT_BUF_LOW(dma_thresh_max_buf) \
+ ((dma_thresh_max_buf + 1) / 2)
+#define DFLT_DMA_READ_INT_BUF_HIGH(dma_thresh_max_buf) \
+ ((dma_thresh_max_buf + 1) * 3 / 4)
+#define DFLT_DMA_WRITE_INT_BUF_LOW(dma_thresh_max_buf) \
+ ((dma_thresh_max_buf + 1) / 2)
+#define DFLT_DMA_WRITE_INT_BUF_HIGH(dma_thresh_max_buf)\
+ ((dma_thresh_max_buf + 1) * 3 / 4)
+
+#define DMA_COMM_Q_LOW_FMAN_V3 0x2A
+#define DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq) \
+ ((dma_thresh_max_commq + 1) / 2)
+#define DFLT_DMA_COMM_Q_LOW(major, dma_thresh_max_commq) \
+ ((major == 6) ? DMA_COMM_Q_LOW_FMAN_V3 : \
+ DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq))
+
+#define DMA_COMM_Q_HIGH_FMAN_V3 0x3f
+#define DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq) \
+ ((dma_thresh_max_commq + 1) * 3 / 4)
+#define DFLT_DMA_COMM_Q_HIGH(major, dma_thresh_max_commq) \
+ ((major == 6) ? DMA_COMM_Q_HIGH_FMAN_V3 : \
+ DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq))
+
+#define TOTAL_NUM_OF_TASKS_FMAN_V3L 59
+#define TOTAL_NUM_OF_TASKS_FMAN_V3H 124
+#define DFLT_TOTAL_NUM_OF_TASKS(major, minor, bmi_max_num_of_tasks) \
+ ((major == 6) ? ((minor == 1 || minor == 4) ? \
+ TOTAL_NUM_OF_TASKS_FMAN_V3L : TOTAL_NUM_OF_TASKS_FMAN_V3H) : \
+ bmi_max_num_of_tasks)
+
+#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 64
+#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V2 32
+#define DFLT_DMA_CAM_NUM_OF_ENTRIES(major) \
+ (major == 6 ? DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 : \
+ DMA_CAM_NUM_OF_ENTRIES_FMAN_V2)
+
+#define FM_TIMESTAMP_1_USEC_BIT 8
+
+/* Defines used for enabling/disabling FMan interrupts */
+#define ERR_INTR_EN_DMA 0x00010000
+#define ERR_INTR_EN_FPM 0x80000000
+#define ERR_INTR_EN_BMI 0x00800000
+#define ERR_INTR_EN_QMI 0x00400000
+#define ERR_INTR_EN_MURAM 0x00040000
+#define ERR_INTR_EN_MAC0 0x00004000
+#define ERR_INTR_EN_MAC1 0x00002000
+#define ERR_INTR_EN_MAC2 0x00001000
+#define ERR_INTR_EN_MAC3 0x00000800
+#define ERR_INTR_EN_MAC4 0x00000400
+#define ERR_INTR_EN_MAC5 0x00000200
+#define ERR_INTR_EN_MAC6 0x00000100
+#define ERR_INTR_EN_MAC7 0x00000080
+#define ERR_INTR_EN_MAC8 0x00008000
+#define ERR_INTR_EN_MAC9 0x00000040
+
+#define INTR_EN_QMI 0x40000000
+#define INTR_EN_MAC0 0x00080000
+#define INTR_EN_MAC1 0x00040000
+#define INTR_EN_MAC2 0x00020000
+#define INTR_EN_MAC3 0x00010000
+#define INTR_EN_MAC4 0x00000040
+#define INTR_EN_MAC5 0x00000020
+#define INTR_EN_MAC6 0x00000008
+#define INTR_EN_MAC7 0x00000002
+#define INTR_EN_MAC8 0x00200000
+#define INTR_EN_MAC9 0x00100000
+#define INTR_EN_REV0 0x00008000
+#define INTR_EN_REV1 0x00004000
+#define INTR_EN_REV2 0x00002000
+#define INTR_EN_REV3 0x00001000
+#define INTR_EN_TMR 0x01000000
+
+enum fman_dma_aid_mode {
+ FMAN_DMA_AID_OUT_PORT_ID = 0, /* 4 LSB of PORT_ID */
+ FMAN_DMA_AID_OUT_TNUM /* 4 LSB of TNUM */
+};
+
+struct fman_iram_regs {
+ u32 iadd; /* FM IRAM instruction address register */
+ u32 idata; /* FM IRAM instruction data register */
+ u32 itcfg; /* FM IRAM timing config register */
+ u32 iready; /* FM IRAM ready register */
+};
+
+struct fman_fpm_regs {
+ u32 fmfp_tnc; /* FPM TNUM Control 0x00 */
+ u32 fmfp_prc; /* FPM Port_ID FmCtl Association 0x04 */
+ u32 fmfp_brkc; /* FPM Breakpoint Control 0x08 */
+ u32 fmfp_mxd; /* FPM Flush Control 0x0c */
+ u32 fmfp_dist1; /* FPM Dispatch Thresholds1 0x10 */
+ u32 fmfp_dist2; /* FPM Dispatch Thresholds2 0x14 */
+ u32 fm_epi; /* FM Error Pending Interrupts 0x18 */
+ u32 fm_rie; /* FM Error Interrupt Enable 0x1c */
+ u32 fmfp_fcev[4]; /* FPM FMan-Controller Event 1-4 0x20-0x2f */
+ u32 res0030[4]; /* res 0x30 - 0x3f */
+ u32 fmfp_cee[4]; /* PM FMan-Controller Event 1-4 0x40-0x4f */
+ u32 res0050[4]; /* res 0x50-0x5f */
+ u32 fmfp_tsc1; /* FPM TimeStamp Control1 0x60 */
+ u32 fmfp_tsc2; /* FPM TimeStamp Control2 0x64 */
+ u32 fmfp_tsp; /* FPM Time Stamp 0x68 */
+ u32 fmfp_tsf; /* FPM Time Stamp Fraction 0x6c */
+ u32 fm_rcr; /* FM Rams Control 0x70 */
+ u32 fmfp_extc; /* FPM External Requests Control 0x74 */
+ u32 fmfp_ext1; /* FPM External Requests Config1 0x78 */
+ u32 fmfp_ext2; /* FPM External Requests Config2 0x7c */
+ u32 fmfp_drd[16]; /* FPM Data_Ram Data 0-15 0x80 - 0xbf */
+ u32 fmfp_dra; /* FPM Data Ram Access 0xc0 */
+ u32 fm_ip_rev_1; /* FM IP Block Revision 1 0xc4 */
+ u32 fm_ip_rev_2; /* FM IP Block Revision 2 0xc8 */
+ u32 fm_rstc; /* FM Reset Command 0xcc */
+ u32 fm_cld; /* FM Classifier Debug 0xd0 */
+ u32 fm_npi; /* FM Normal Pending Interrupts 0xd4 */
+ u32 fmfp_exte; /* FPM External Requests Enable 0xd8 */
+ u32 fmfp_ee; /* FPM Event&Mask 0xdc */
+ u32 fmfp_cev[4]; /* FPM CPU Event 1-4 0xe0-0xef */
+ u32 res00f0[4]; /* res 0xf0-0xff */
+ u32 fmfp_ps[50]; /* FPM Port Status 0x100-0x1c7 */
+ u32 res01c8[14]; /* res 0x1c8-0x1ff */
+ u32 fmfp_clfabc; /* FPM CLFABC 0x200 */
+ u32 fmfp_clfcc; /* FPM CLFCC 0x204 */
+ u32 fmfp_clfaval; /* FPM CLFAVAL 0x208 */
+ u32 fmfp_clfbval; /* FPM CLFBVAL 0x20c */
+ u32 fmfp_clfcval; /* FPM CLFCVAL 0x210 */
+ u32 fmfp_clfamsk; /* FPM CLFAMSK 0x214 */
+ u32 fmfp_clfbmsk; /* FPM CLFBMSK 0x218 */
+ u32 fmfp_clfcmsk; /* FPM CLFCMSK 0x21c */
+ u32 fmfp_clfamc; /* FPM CLFAMC 0x220 */
+ u32 fmfp_clfbmc; /* FPM CLFBMC 0x224 */
+ u32 fmfp_clfcmc; /* FPM CLFCMC 0x228 */
+ u32 fmfp_decceh; /* FPM DECCEH 0x22c */
+ u32 res0230[116]; /* res 0x230 - 0x3ff */
+ u32 fmfp_ts[128]; /* 0x400: FPM Task Status 0x400 - 0x5ff */
+ u32 res0600[0x400 - 384];
+};
+
+struct fman_bmi_regs {
+ u32 fmbm_init; /* BMI Initialization 0x00 */
+ u32 fmbm_cfg1; /* BMI Configuration 1 0x04 */
+ u32 fmbm_cfg2; /* BMI Configuration 2 0x08 */
+ u32 res000c[5]; /* 0x0c - 0x1f */
+ u32 fmbm_ievr; /* Interrupt Event Register 0x20 */
+ u32 fmbm_ier; /* Interrupt Enable Register 0x24 */
+ u32 fmbm_ifr; /* Interrupt Force Register 0x28 */
+ u32 res002c[5]; /* 0x2c - 0x3f */
+ u32 fmbm_arb[8]; /* BMI Arbitration 0x40 - 0x5f */
+ u32 res0060[12]; /* 0x60 - 0x8f */
+ u32 fmbm_dtc[3]; /* Debug Trap Counter 0x90 - 0x9b */
+ u32 res009c; /* 0x9c */
+ u32 fmbm_dcv[3][4]; /* Debug Compare val 0xa0-0xcf */
+ u32 fmbm_dcm[3][4]; /* Debug Compare Mask 0xd0-0xff */
+ u32 fmbm_gde; /* BMI Global Debug Enable 0x100 */
+ u32 fmbm_pp[63]; /* BMI Port Parameters 0x104 - 0x1ff */
+ u32 res0200; /* 0x200 */
+ u32 fmbm_pfs[63]; /* BMI Port FIFO Size 0x204 - 0x2ff */
+ u32 res0300; /* 0x300 */
+ u32 fmbm_spliodn[63]; /* Port Partition ID 0x304 - 0x3ff */
+};
+
+struct fman_qmi_regs {
+ u32 fmqm_gc; /* General Configuration Register 0x00 */
+ u32 res0004; /* 0x04 */
+ u32 fmqm_eie; /* Error Interrupt Event Register 0x08 */
+ u32 fmqm_eien; /* Error Interrupt Enable Register 0x0c */
+ u32 fmqm_eif; /* Error Interrupt Force Register 0x10 */
+ u32 fmqm_ie; /* Interrupt Event Register 0x14 */
+ u32 fmqm_ien; /* Interrupt Enable Register 0x18 */
+ u32 fmqm_if; /* Interrupt Force Register 0x1c */
+ u32 fmqm_gs; /* Global Status Register 0x20 */
+ u32 fmqm_ts; /* Task Status Register 0x24 */
+ u32 fmqm_etfc; /* Enqueue Total Frame Counter 0x28 */
+ u32 fmqm_dtfc; /* Dequeue Total Frame Counter 0x2c */
+ u32 fmqm_dc0; /* Dequeue Counter 0 0x30 */
+ u32 fmqm_dc1; /* Dequeue Counter 1 0x34 */
+ u32 fmqm_dc2; /* Dequeue Counter 2 0x38 */
+ u32 fmqm_dc3; /* Dequeue Counter 3 0x3c */
+ u32 fmqm_dfdc; /* Dequeue FQID from Default Counter 0x40 */
+ u32 fmqm_dfcc; /* Dequeue FQID from Context Counter 0x44 */
+ u32 fmqm_dffc; /* Dequeue FQID from FD Counter 0x48 */
+ u32 fmqm_dcc; /* Dequeue Confirm Counter 0x4c */
+ u32 res0050[7]; /* 0x50 - 0x6b */
+ u32 fmqm_tapc; /* Tnum Aging Period Control 0x6c */
+ u32 fmqm_dmcvc; /* Dequeue MAC Command Valid Counter 0x70 */
+ u32 fmqm_difdcc; /* Dequeue Invalid FD Command Counter 0x74 */
+ u32 fmqm_da1v; /* Dequeue A1 Valid Counter 0x78 */
+ u32 res007c; /* 0x7c */
+ u32 fmqm_dtc; /* 0x80 Debug Trap Counter 0x80 */
+ u32 fmqm_efddd; /* 0x84 Enqueue Frame desc Dynamic dbg 0x84 */
+ u32 res0088[2]; /* 0x88 - 0x8f */
+ struct {
+ u32 fmqm_dtcfg1; /* 0x90 dbg trap cfg 1 Register 0x00 */
+ u32 fmqm_dtval1; /* Debug Trap Value 1 Register 0x04 */
+ u32 fmqm_dtm1; /* Debug Trap Mask 1 Register 0x08 */
+ u32 fmqm_dtc1; /* Debug Trap Counter 1 Register 0x0c */
+ u32 fmqm_dtcfg2; /* dbg Trap cfg 2 Register 0x10 */
+ u32 fmqm_dtval2; /* Debug Trap Value 2 Register 0x14 */
+ u32 fmqm_dtm2; /* Debug Trap Mask 2 Register 0x18 */
+ u32 res001c; /* 0x1c */
+ } dbg_traps[3]; /* 0x90 - 0xef */
+ u8 res00f0[0x400 - 0xf0]; /* 0xf0 - 0x3ff */
+};
+
+struct fman_dma_regs {
+ u32 fmdmsr; /* FM DMA status register 0x00 */
+ u32 fmdmmr; /* FM DMA mode register 0x04 */
+ u32 fmdmtr; /* FM DMA bus threshold register 0x08 */
+ u32 fmdmhy; /* FM DMA bus hysteresis register 0x0c */
+ u32 fmdmsetr; /* FM DMA SOS emergency Threshold Register 0x10 */
+ u32 fmdmtah; /* FM DMA transfer bus address high reg 0x14 */
+ u32 fmdmtal; /* FM DMA transfer bus address low reg 0x18 */
+ u32 fmdmtcid; /* FM DMA transfer bus communication ID reg 0x1c */
+ u32 fmdmra; /* FM DMA bus internal ram address register 0x20 */
+ u32 fmdmrd; /* FM DMA bus internal ram data register 0x24 */
+ u32 fmdmwcr; /* FM DMA CAM watchdog counter value 0x28 */
+ u32 fmdmebcr; /* FM DMA CAM base in MURAM register 0x2c */
+ u32 fmdmccqdr; /* FM DMA CAM and CMD Queue Debug reg 0x30 */
+ u32 fmdmccqvr1; /* FM DMA CAM and CMD Queue Value reg #1 0x34 */
+ u32 fmdmccqvr2; /* FM DMA CAM and CMD Queue Value reg #2 0x38 */
+ u32 fmdmcqvr3; /* FM DMA CMD Queue Value register #3 0x3c */
+ u32 fmdmcqvr4; /* FM DMA CMD Queue Value register #4 0x40 */
+ u32 fmdmcqvr5; /* FM DMA CMD Queue Value register #5 0x44 */
+ u32 fmdmsefrc; /* FM DMA Semaphore Entry Full Reject Cntr 0x48 */
+ u32 fmdmsqfrc; /* FM DMA Semaphore Queue Full Reject Cntr 0x4c */
+ u32 fmdmssrc; /* FM DMA Semaphore SYNC Reject Counter 0x50 */
+ u32 fmdmdcr; /* FM DMA Debug Counter 0x54 */
+ u32 fmdmemsr; /* FM DMA Emergency Smoother Register 0x58 */
+ u32 res005c; /* 0x5c */
+ u32 fmdmplr[FMAN_LIODN_TBL / 2]; /* DMA LIODN regs 0x60-0xdf */
+ u32 res00e0[0x400 - 56];
+};
+
+/* Structure that holds current FMan state.
+ * Used for saving run time information.
+ */
+struct fman_state_struct {
+ u8 fm_id;
+ u16 fm_clk_freq;
+ struct fman_rev_info rev_info;
+ bool enabled_time_stamp;
+ u8 count1_micro_bit;
+ u8 total_num_of_tasks;
+ u8 accumulated_num_of_tasks;
+ u32 accumulated_fifo_size;
+ u8 accumulated_num_of_open_dmas;
+ u8 accumulated_num_of_deq_tnums;
+ u32 exceptions;
+ u32 extra_fifo_pool_size;
+ u8 extra_tasks_pool_size;
+ u8 extra_open_dmas_pool_size;
+ u16 port_mfl[MAX_NUM_OF_MACS];
+ u16 mac_mfl[MAX_NUM_OF_MACS];
+
+ /* SOC specific */
+ u32 fm_iram_size;
+ /* DMA */
+ u32 dma_thresh_max_commq;
+ u32 dma_thresh_max_buf;
+ u32 max_num_of_open_dmas;
+ /* QMI */
+ u32 qmi_max_num_of_tnums;
+ u32 qmi_def_tnums_thresh;
+ /* BMI */
+ u32 bmi_max_num_of_tasks;
+ u32 bmi_max_fifo_size;
+ /* General */
+ u32 fm_port_num_of_cg;
+ u32 num_of_rx_ports;
+ u32 total_fifo_size;
+
+ u32 qman_channel_base;
+ u32 num_of_qman_channels;
+
+ struct resource *res;
+};
+
+/* Structure that holds FMan initial configuration */
+struct fman_cfg {
+ u8 disp_limit_tsh;
+ u8 prs_disp_tsh;
+ u8 plcr_disp_tsh;
+ u8 kg_disp_tsh;
+ u8 bmi_disp_tsh;
+ u8 qmi_enq_disp_tsh;
+ u8 qmi_deq_disp_tsh;
+ u8 fm_ctl1_disp_tsh;
+ u8 fm_ctl2_disp_tsh;
+ int dma_cache_override;
+ enum fman_dma_aid_mode dma_aid_mode;
+ u32 dma_axi_dbg_num_of_beats;
+ u32 dma_cam_num_of_entries;
+ u32 dma_watchdog;
+ u8 dma_comm_qtsh_asrt_emer;
+ u32 dma_write_buf_tsh_asrt_emer;
+ u32 dma_read_buf_tsh_asrt_emer;
+ u8 dma_comm_qtsh_clr_emer;
+ u32 dma_write_buf_tsh_clr_emer;
+ u32 dma_read_buf_tsh_clr_emer;
+ u32 dma_sos_emergency;
+ int dma_dbg_cnt_mode;
+ int catastrophic_err;
+ int dma_err;
+ u32 exceptions;
+ u16 clk_freq;
+ u32 cam_base_addr;
+ u32 fifo_base_addr;
+ u32 total_fifo_size;
+ u32 total_num_of_tasks;
+ u32 qmi_def_tnums_thresh;
+};
+
+/* Structure that holds information received from device tree */
+struct fman_dts_params {
+ void __iomem *base_addr; /* FMan virtual address */
+ struct resource *res; /* FMan memory resource */
+ u8 id; /* FMan ID */
+
+ int err_irq; /* FMan Error IRQ */
+
+ u16 clk_freq; /* FMan clock freq (In Mhz) */
+
+ u32 qman_channel_base; /* QMan channels base */
+ u32 num_of_qman_channels; /* Number of QMan channels */
+
+ struct resource muram_res; /* MURAM resource */
+};
+
+/** fman_exceptions_cb
+ * fman - Pointer to FMan
+ * exception - The exception.
+ *
+ * Exceptions user callback routine, will be called upon an exception
+ * passing the exception identification.
+ *
+ * Return: irq status
+ */
+typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman,
+ enum fman_exceptions exception);
+
+/** fman_bus_error_cb
+ * fman - Pointer to FMan
+ * port_id - Port id
+ * addr - Address that caused the error
+ * tnum - Owner of error
+ * liodn - Logical IO device number
+ *
+ * Bus error user callback routine, will be called upon bus error,
+ * passing parameters describing the errors and the owner.
+ *
+ * Return: IRQ status
+ */
+typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id,
+ u64 addr, u8 tnum, u16 liodn);
+
+struct fman {
+ struct device *dev;
+ void __iomem *base_addr;
+ struct fman_intr_src intr_mng[FMAN_EV_CNT];
+
+ struct fman_fpm_regs __iomem *fpm_regs;
+ struct fman_bmi_regs __iomem *bmi_regs;
+ struct fman_qmi_regs __iomem *qmi_regs;
+ struct fman_dma_regs __iomem *dma_regs;
+ fman_exceptions_cb *exception_cb;
+ fman_bus_error_cb *bus_error_cb;
+ /* Spinlock for FMan use */
+ spinlock_t spinlock;
+ struct fman_state_struct *state;
+
+ struct fman_cfg *cfg;
+ struct muram_info *muram;
+ /* cam section in muram */
+ int cam_offset;
+ size_t cam_size;
+ /* Fifo in MURAM */
+ int fifo_offset;
+ size_t fifo_size;
+
+ u32 liodn_base[64];
+ u32 liodn_offset[64];
+
+ struct fman_dts_params dts_params;
+};
+
+static irqreturn_t fman_exceptions(struct fman *fman,
+ enum fman_exceptions exception)
+{
+ dev_dbg(fman->dev, "%s: FMan[%d] exception %d\n",
+ __func__, fman->state->fm_id, exception);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fman_bus_error(struct fman *fman, u8 __maybe_unused port_id,
+ u64 __maybe_unused addr,
+ u8 __maybe_unused tnum,
+ u16 __maybe_unused liodn)
+{
+ dev_dbg(fman->dev, "%s: FMan[%d] bus error: port_id[%d]\n",
+ __func__, fman->state->fm_id, port_id);
+
+ return IRQ_HANDLED;
+}
+
+static inline irqreturn_t call_mac_isr(struct fman *fman, u8 id)
+{
+ if (fman->intr_mng[id].isr_cb) {
+ fman->intr_mng[id].isr_cb(fman->intr_mng[id].src_handle);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static inline u8 hw_port_id_to_sw_port_id(u8 major, u8 hw_port_id)
+{
+ u8 sw_port_id = 0;
+
+ if (hw_port_id >= BASE_TX_PORTID)
+ sw_port_id = hw_port_id - BASE_TX_PORTID;
+ else if (hw_port_id >= BASE_RX_PORTID)
+ sw_port_id = hw_port_id - BASE_RX_PORTID;
+ else
+ sw_port_id = 0;
+
+ return sw_port_id;
+}
+
+static void set_port_order_restoration(struct fman_fpm_regs __iomem *fpm_rg,
+ u8 port_id)
+{
+ u32 tmp = 0;
+
+ tmp = port_id << FPM_PORT_FM_CTL_PORTID_SHIFT;
+
+ tmp |= FPM_PRT_FM_CTL2 | FPM_PRT_FM_CTL1;
+
+ /* order restoration */
+ if (port_id % 2)
+ tmp |= FPM_PRT_FM_CTL1 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT;
+ else
+ tmp |= FPM_PRT_FM_CTL2 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT;
+
+ iowrite32be(tmp, &fpm_rg->fmfp_prc);
+}
+
+static void set_port_liodn(struct fman *fman, u8 port_id,
+ u32 liodn_base, u32 liodn_ofst)
+{
+ u32 tmp;
+
+ /* set LIODN base for this port */
+ tmp = ioread32be(&fman->dma_regs->fmdmplr[port_id / 2]);
+ if (port_id % 2) {
+ tmp &= ~DMA_LIODN_BASE_MASK;
+ tmp |= liodn_base;
+ } else {
+ tmp &= ~(DMA_LIODN_BASE_MASK << DMA_LIODN_SHIFT);
+ tmp |= liodn_base << DMA_LIODN_SHIFT;
+ }
+ iowrite32be(tmp, &fman->dma_regs->fmdmplr[port_id / 2]);
+ iowrite32be(liodn_ofst, &fman->bmi_regs->fmbm_spliodn[port_id - 1]);
+}
+
+static void enable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fpm_rg->fm_rcr);
+ if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL)
+ iowrite32be(tmp | FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr);
+ else
+ iowrite32be(tmp | FPM_RAM_RAMS_ECC_EN |
+ FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr);
+}
+
+static void disable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fpm_rg->fm_rcr);
+ if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL)
+ iowrite32be(tmp & ~FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr);
+ else
+ iowrite32be(tmp & ~(FPM_RAM_RAMS_ECC_EN | FPM_RAM_IRAM_ECC_EN),
+ &fpm_rg->fm_rcr);
+}
+
+static void fman_defconfig(struct fman_cfg *cfg)
+{
+ memset(cfg, 0, sizeof(struct fman_cfg));
+
+ cfg->catastrophic_err = DEFAULT_CATASTROPHIC_ERR;
+ cfg->dma_err = DEFAULT_DMA_ERR;
+ cfg->dma_aid_mode = DEFAULT_AID_MODE;
+ cfg->dma_comm_qtsh_clr_emer = DEFAULT_DMA_COMM_Q_LOW;
+ cfg->dma_comm_qtsh_asrt_emer = DEFAULT_DMA_COMM_Q_HIGH;
+ cfg->dma_cache_override = DEFAULT_CACHE_OVERRIDE;
+ cfg->dma_cam_num_of_entries = DEFAULT_DMA_CAM_NUM_OF_ENTRIES;
+ cfg->dma_dbg_cnt_mode = DEFAULT_DMA_DBG_CNT_MODE;
+ cfg->dma_sos_emergency = DEFAULT_DMA_SOS_EMERGENCY;
+ cfg->dma_watchdog = DEFAULT_DMA_WATCHDOG;
+ cfg->disp_limit_tsh = DEFAULT_DISP_LIMIT;
+ cfg->prs_disp_tsh = DEFAULT_PRS_DISP_TH;
+ cfg->plcr_disp_tsh = DEFAULT_PLCR_DISP_TH;
+ cfg->kg_disp_tsh = DEFAULT_KG_DISP_TH;
+ cfg->bmi_disp_tsh = DEFAULT_BMI_DISP_TH;
+ cfg->qmi_enq_disp_tsh = DEFAULT_QMI_ENQ_DISP_TH;
+ cfg->qmi_deq_disp_tsh = DEFAULT_QMI_DEQ_DISP_TH;
+ cfg->fm_ctl1_disp_tsh = DEFAULT_FM_CTL1_DISP_TH;
+ cfg->fm_ctl2_disp_tsh = DEFAULT_FM_CTL2_DISP_TH;
+}
+
+static int dma_init(struct fman *fman)
+{
+ struct fman_dma_regs __iomem *dma_rg = fman->dma_regs;
+ struct fman_cfg *cfg = fman->cfg;
+ u32 tmp_reg;
+
+ /* Init DMA Registers */
+
+ /* clear status reg events */
+ tmp_reg = (DMA_STATUS_BUS_ERR | DMA_STATUS_READ_ECC |
+ DMA_STATUS_SYSTEM_WRITE_ECC | DMA_STATUS_FM_WRITE_ECC);
+ iowrite32be(ioread32be(&dma_rg->fmdmsr) | tmp_reg, &dma_rg->fmdmsr);
+
+ /* configure mode register */
+ tmp_reg = 0;
+ tmp_reg |= cfg->dma_cache_override << DMA_MODE_CACHE_OR_SHIFT;
+ if (cfg->exceptions & EX_DMA_BUS_ERROR)
+ tmp_reg |= DMA_MODE_BER;
+ if ((cfg->exceptions & EX_DMA_SYSTEM_WRITE_ECC) |
+ (cfg->exceptions & EX_DMA_READ_ECC) |
+ (cfg->exceptions & EX_DMA_FM_WRITE_ECC))
+ tmp_reg |= DMA_MODE_ECC;
+ if (cfg->dma_axi_dbg_num_of_beats)
+ tmp_reg |= (DMA_MODE_AXI_DBG_MASK &
+ ((cfg->dma_axi_dbg_num_of_beats - 1)
+ << DMA_MODE_AXI_DBG_SHIFT));
+
+ tmp_reg |= (((cfg->dma_cam_num_of_entries / DMA_CAM_UNITS) - 1) &
+ DMA_MODE_CEN_MASK) << DMA_MODE_CEN_SHIFT;
+ tmp_reg |= DMA_MODE_SECURE_PROT;
+ tmp_reg |= cfg->dma_dbg_cnt_mode << DMA_MODE_DBG_SHIFT;
+ tmp_reg |= cfg->dma_aid_mode << DMA_MODE_AID_MODE_SHIFT;
+
+ iowrite32be(tmp_reg, &dma_rg->fmdmmr);
+
+ /* configure thresholds register */
+ tmp_reg = ((u32)cfg->dma_comm_qtsh_asrt_emer <<
+ DMA_THRESH_COMMQ_SHIFT);
+ tmp_reg |= (cfg->dma_read_buf_tsh_asrt_emer &
+ DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT;
+ tmp_reg |= cfg->dma_write_buf_tsh_asrt_emer &
+ DMA_THRESH_WRITE_INT_BUF_MASK;
+
+ iowrite32be(tmp_reg, &dma_rg->fmdmtr);
+
+ /* configure hysteresis register */
+ tmp_reg = ((u32)cfg->dma_comm_qtsh_clr_emer <<
+ DMA_THRESH_COMMQ_SHIFT);
+ tmp_reg |= (cfg->dma_read_buf_tsh_clr_emer &
+ DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT;
+ tmp_reg |= cfg->dma_write_buf_tsh_clr_emer &
+ DMA_THRESH_WRITE_INT_BUF_MASK;
+
+ iowrite32be(tmp_reg, &dma_rg->fmdmhy);
+
+ /* configure emergency threshold */
+ iowrite32be(cfg->dma_sos_emergency, &dma_rg->fmdmsetr);
+
+ /* configure Watchdog */
+ iowrite32be((cfg->dma_watchdog * cfg->clk_freq), &dma_rg->fmdmwcr);
+
+ iowrite32be(cfg->cam_base_addr, &dma_rg->fmdmebcr);
+
+ /* Allocate MURAM for CAM */
+ fman->cam_size =
+ (u32)(fman->cfg->dma_cam_num_of_entries * DMA_CAM_SIZEOF_ENTRY);
+ fman->cam_offset = fman_muram_alloc(fman->muram, fman->cam_size);
+ if (IS_ERR_VALUE(fman->cam_offset)) {
+ dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (fman->state->rev_info.major == 2) {
+ u32 __iomem *cam_base_addr;
+
+ fman_muram_free_mem(fman->muram, fman->cam_offset,
+ fman->cam_size);
+
+ fman->cam_size = fman->cfg->dma_cam_num_of_entries * 72 + 128;
+ fman->cam_offset = fman_muram_alloc(fman->muram,
+ fman->cam_size);
+ if (IS_ERR_VALUE(fman->cam_offset)) {
+ dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (fman->cfg->dma_cam_num_of_entries % 8 ||
+ fman->cfg->dma_cam_num_of_entries > 32) {
+ dev_err(fman->dev, "%s: wrong dma_cam_num_of_entries\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cam_base_addr = (u32 __iomem *)
+ fman_muram_offset_to_vbase(fman->muram,
+ fman->cam_offset);
+ iowrite32be(~((1 <<
+ (32 - fman->cfg->dma_cam_num_of_entries)) - 1),
+ cam_base_addr);
+ }
+
+ fman->cfg->cam_base_addr = fman->cam_offset;
+
+ return 0;
+}
+
+static void fpm_init(struct fman_fpm_regs __iomem *fpm_rg, struct fman_cfg *cfg)
+{
+ u32 tmp_reg;
+ int i;
+
+ /* Init FPM Registers */
+
+ tmp_reg = (u32)(cfg->disp_limit_tsh << FPM_DISP_LIMIT_SHIFT);
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_mxd);
+
+ tmp_reg = (((u32)cfg->prs_disp_tsh << FPM_THR1_PRS_SHIFT) |
+ ((u32)cfg->kg_disp_tsh << FPM_THR1_KG_SHIFT) |
+ ((u32)cfg->plcr_disp_tsh << FPM_THR1_PLCR_SHIFT) |
+ ((u32)cfg->bmi_disp_tsh << FPM_THR1_BMI_SHIFT));
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_dist1);
+
+ tmp_reg =
+ (((u32)cfg->qmi_enq_disp_tsh << FPM_THR2_QMI_ENQ_SHIFT) |
+ ((u32)cfg->qmi_deq_disp_tsh << FPM_THR2_QMI_DEQ_SHIFT) |
+ ((u32)cfg->fm_ctl1_disp_tsh << FPM_THR2_FM_CTL1_SHIFT) |
+ ((u32)cfg->fm_ctl2_disp_tsh << FPM_THR2_FM_CTL2_SHIFT));
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_dist2);
+
+ /* define exceptions and error behavior */
+ tmp_reg = 0;
+ /* Clear events */
+ tmp_reg |= (FPM_EV_MASK_STALL | FPM_EV_MASK_DOUBLE_ECC |
+ FPM_EV_MASK_SINGLE_ECC);
+ /* enable interrupts */
+ if (cfg->exceptions & EX_FPM_STALL_ON_TASKS)
+ tmp_reg |= FPM_EV_MASK_STALL_EN;
+ if (cfg->exceptions & EX_FPM_SINGLE_ECC)
+ tmp_reg |= FPM_EV_MASK_SINGLE_ECC_EN;
+ if (cfg->exceptions & EX_FPM_DOUBLE_ECC)
+ tmp_reg |= FPM_EV_MASK_DOUBLE_ECC_EN;
+ tmp_reg |= (cfg->catastrophic_err << FPM_EV_MASK_CAT_ERR_SHIFT);
+ tmp_reg |= (cfg->dma_err << FPM_EV_MASK_DMA_ERR_SHIFT);
+ /* FMan is not halted upon external halt activation */
+ tmp_reg |= FPM_EV_MASK_EXTERNAL_HALT;
+ /* Man is not halted upon Unrecoverable ECC error behavior */
+ tmp_reg |= FPM_EV_MASK_ECC_ERR_HALT;
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_ee);
+
+ /* clear all fmCtls event registers */
+ for (i = 0; i < FM_NUM_OF_FMAN_CTRL_EVENT_REGS; i++)
+ iowrite32be(0xFFFFFFFF, &fpm_rg->fmfp_cev[i]);
+
+ /* RAM ECC - enable and clear events */
+ /* first we need to clear all parser memory,
+ * as it is uninitialized and may cause ECC errors
+ */
+ /* event bits */
+ tmp_reg = (FPM_RAM_MURAM_ECC | FPM_RAM_IRAM_ECC);
+
+ iowrite32be(tmp_reg, &fpm_rg->fm_rcr);
+
+ tmp_reg = 0;
+ if (cfg->exceptions & EX_IRAM_ECC) {
+ tmp_reg |= FPM_IRAM_ECC_ERR_EX_EN;
+ enable_rams_ecc(fpm_rg);
+ }
+ if (cfg->exceptions & EX_MURAM_ECC) {
+ tmp_reg |= FPM_MURAM_ECC_ERR_EX_EN;
+ enable_rams_ecc(fpm_rg);
+ }
+ iowrite32be(tmp_reg, &fpm_rg->fm_rie);
+}
+
+static void bmi_init(struct fman_bmi_regs __iomem *bmi_rg,
+ struct fman_cfg *cfg)
+{
+ u32 tmp_reg;
+
+ /* Init BMI Registers */
+
+ /* define common resources */
+ tmp_reg = cfg->fifo_base_addr;
+ tmp_reg = tmp_reg / BMI_FIFO_ALIGN;
+
+ tmp_reg |= ((cfg->total_fifo_size / FMAN_BMI_FIFO_UNITS - 1) <<
+ BMI_CFG1_FIFO_SIZE_SHIFT);
+ iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg1);
+
+ tmp_reg = ((cfg->total_num_of_tasks - 1) & BMI_CFG2_TASKS_MASK) <<
+ BMI_CFG2_TASKS_SHIFT;
+ /* num of DMA's will be dynamically updated when each port is set */
+ iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg2);
+
+ /* define unmaskable exceptions, enable and clear events */
+ tmp_reg = 0;
+ iowrite32be(BMI_ERR_INTR_EN_LIST_RAM_ECC |
+ BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC |
+ BMI_ERR_INTR_EN_STATISTICS_RAM_ECC |
+ BMI_ERR_INTR_EN_DISPATCH_RAM_ECC, &bmi_rg->fmbm_ievr);
+
+ if (cfg->exceptions & EX_BMI_LIST_RAM_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_LIST_RAM_ECC;
+ if (cfg->exceptions & EX_BMI_STORAGE_PROFILE_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC;
+ if (cfg->exceptions & EX_BMI_STATISTICS_RAM_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC;
+ if (cfg->exceptions & EX_BMI_DISPATCH_RAM_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC;
+ iowrite32be(tmp_reg, &bmi_rg->fmbm_ier);
+}
+
+static void qmi_init(struct fman_qmi_regs __iomem *qmi_rg,
+ struct fman_cfg *cfg)
+{
+ u32 tmp_reg;
+
+ /* Init QMI Registers */
+
+ /* Clear error interrupt events */
+
+ iowrite32be(QMI_ERR_INTR_EN_DOUBLE_ECC | QMI_ERR_INTR_EN_DEQ_FROM_DEF,
+ &qmi_rg->fmqm_eie);
+ tmp_reg = 0;
+ if (cfg->exceptions & EX_QMI_DEQ_FROM_UNKNOWN_PORTID)
+ tmp_reg |= QMI_ERR_INTR_EN_DEQ_FROM_DEF;
+ if (cfg->exceptions & EX_QMI_DOUBLE_ECC)
+ tmp_reg |= QMI_ERR_INTR_EN_DOUBLE_ECC;
+ /* enable events */
+ iowrite32be(tmp_reg, &qmi_rg->fmqm_eien);
+
+ tmp_reg = 0;
+ /* Clear interrupt events */
+ iowrite32be(QMI_INTR_EN_SINGLE_ECC, &qmi_rg->fmqm_ie);
+ if (cfg->exceptions & EX_QMI_SINGLE_ECC)
+ tmp_reg |= QMI_INTR_EN_SINGLE_ECC;
+ /* enable events */
+ iowrite32be(tmp_reg, &qmi_rg->fmqm_ien);
+}
+
+static int enable(struct fman *fman, struct fman_cfg *cfg)
+{
+ u32 cfg_reg = 0;
+
+ /* Enable all modules */
+
+ /* clear&enable global counters - calculate reg and save for later,
+ * because it's the same reg for QMI enable
+ */
+ cfg_reg = QMI_CFG_EN_COUNTERS;
+
+ /* Set enqueue and dequeue thresholds */
+ cfg_reg |= (cfg->qmi_def_tnums_thresh << 8) | cfg->qmi_def_tnums_thresh;
+
+ iowrite32be(BMI_INIT_START, &fman->bmi_regs->fmbm_init);
+ iowrite32be(cfg_reg | QMI_CFG_ENQ_EN | QMI_CFG_DEQ_EN,
+ &fman->qmi_regs->fmqm_gc);
+
+ return 0;
+}
+
+static int set_exception(struct fman *fman,
+ enum fman_exceptions exception, bool enable)
+{
+ u32 tmp;
+
+ switch (exception) {
+ case FMAN_EX_DMA_BUS_ERROR:
+ tmp = ioread32be(&fman->dma_regs->fmdmmr);
+ if (enable)
+ tmp |= DMA_MODE_BER;
+ else
+ tmp &= ~DMA_MODE_BER;
+ /* disable bus error */
+ iowrite32be(tmp, &fman->dma_regs->fmdmmr);
+ break;
+ case FMAN_EX_DMA_READ_ECC:
+ case FMAN_EX_DMA_SYSTEM_WRITE_ECC:
+ case FMAN_EX_DMA_FM_WRITE_ECC:
+ tmp = ioread32be(&fman->dma_regs->fmdmmr);
+ if (enable)
+ tmp |= DMA_MODE_ECC;
+ else
+ tmp &= ~DMA_MODE_ECC;
+ iowrite32be(tmp, &fman->dma_regs->fmdmmr);
+ break;
+ case FMAN_EX_FPM_STALL_ON_TASKS:
+ tmp = ioread32be(&fman->fpm_regs->fmfp_ee);
+ if (enable)
+ tmp |= FPM_EV_MASK_STALL_EN;
+ else
+ tmp &= ~FPM_EV_MASK_STALL_EN;
+ iowrite32be(tmp, &fman->fpm_regs->fmfp_ee);
+ break;
+ case FMAN_EX_FPM_SINGLE_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fmfp_ee);
+ if (enable)
+ tmp |= FPM_EV_MASK_SINGLE_ECC_EN;
+ else
+ tmp &= ~FPM_EV_MASK_SINGLE_ECC_EN;
+ iowrite32be(tmp, &fman->fpm_regs->fmfp_ee);
+ break;
+ case FMAN_EX_FPM_DOUBLE_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fmfp_ee);
+ if (enable)
+ tmp |= FPM_EV_MASK_DOUBLE_ECC_EN;
+ else
+ tmp &= ~FPM_EV_MASK_DOUBLE_ECC_EN;
+ iowrite32be(tmp, &fman->fpm_regs->fmfp_ee);
+ break;
+ case FMAN_EX_QMI_SINGLE_ECC:
+ tmp = ioread32be(&fman->qmi_regs->fmqm_ien);
+ if (enable)
+ tmp |= QMI_INTR_EN_SINGLE_ECC;
+ else
+ tmp &= ~QMI_INTR_EN_SINGLE_ECC;
+ iowrite32be(tmp, &fman->qmi_regs->fmqm_ien);
+ break;
+ case FMAN_EX_QMI_DOUBLE_ECC:
+ tmp = ioread32be(&fman->qmi_regs->fmqm_eien);
+ if (enable)
+ tmp |= QMI_ERR_INTR_EN_DOUBLE_ECC;
+ else
+ tmp &= ~QMI_ERR_INTR_EN_DOUBLE_ECC;
+ iowrite32be(tmp, &fman->qmi_regs->fmqm_eien);
+ break;
+ case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID:
+ tmp = ioread32be(&fman->qmi_regs->fmqm_eien);
+ if (enable)
+ tmp |= QMI_ERR_INTR_EN_DEQ_FROM_DEF;
+ else
+ tmp &= ~QMI_ERR_INTR_EN_DEQ_FROM_DEF;
+ iowrite32be(tmp, &fman->qmi_regs->fmqm_eien);
+ break;
+ case FMAN_EX_BMI_LIST_RAM_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_LIST_RAM_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_LIST_RAM_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_BMI_STORAGE_PROFILE_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_BMI_STATISTICS_RAM_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_STATISTICS_RAM_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_BMI_DISPATCH_RAM_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_DISPATCH_RAM_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_IRAM_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fm_rie);
+ if (enable) {
+ /* enable ECC if not enabled */
+ enable_rams_ecc(fman->fpm_regs);
+ /* enable ECC interrupts */
+ tmp |= FPM_IRAM_ECC_ERR_EX_EN;
+ } else {
+ /* ECC mechanism may be disabled,
+ * depending on driver status
+ */
+ disable_rams_ecc(fman->fpm_regs);
+ tmp &= ~FPM_IRAM_ECC_ERR_EX_EN;
+ }
+ iowrite32be(tmp, &fman->fpm_regs->fm_rie);
+ break;
+ case FMAN_EX_MURAM_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fm_rie);
+ if (enable) {
+ /* enable ECC if not enabled */
+ enable_rams_ecc(fman->fpm_regs);
+ /* enable ECC interrupts */
+ tmp |= FPM_MURAM_ECC_ERR_EX_EN;
+ } else {
+ /* ECC mechanism may be disabled,
+ * depending on driver status
+ */
+ disable_rams_ecc(fman->fpm_regs);
+ tmp &= ~FPM_MURAM_ECC_ERR_EX_EN;
+ }
+ iowrite32be(tmp, &fman->fpm_regs->fm_rie);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void resume(struct fman_fpm_regs __iomem *fpm_rg)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fpm_rg->fmfp_ee);
+ /* clear tmp_reg event bits in order not to clear standing events */
+ tmp &= ~(FPM_EV_MASK_DOUBLE_ECC |
+ FPM_EV_MASK_STALL | FPM_EV_MASK_SINGLE_ECC);
+ tmp |= FPM_EV_MASK_RELEASE_FM;
+
+ iowrite32be(tmp, &fpm_rg->fmfp_ee);
+}
+
+static int fill_soc_specific_params(struct fman_state_struct *state)
+{
+ u8 minor = state->rev_info.minor;
+ /* P4080 - Major 2
+ * P2041/P3041/P5020/P5040 - Major 3
+ * Tx/Bx - Major 6
+ */
+ switch (state->rev_info.major) {
+ case 3:
+ state->bmi_max_fifo_size = 160 * 1024;
+ state->fm_iram_size = 64 * 1024;
+ state->dma_thresh_max_commq = 31;
+ state->dma_thresh_max_buf = 127;
+ state->qmi_max_num_of_tnums = 64;
+ state->qmi_def_tnums_thresh = 48;
+ state->bmi_max_num_of_tasks = 128;
+ state->max_num_of_open_dmas = 32;
+ state->fm_port_num_of_cg = 256;
+ state->num_of_rx_ports = 6;
+ state->total_fifo_size = 122 * 1024;
+ break;
+
+ case 2:
+ state->bmi_max_fifo_size = 160 * 1024;
+ state->fm_iram_size = 64 * 1024;
+ state->dma_thresh_max_commq = 31;
+ state->dma_thresh_max_buf = 127;
+ state->qmi_max_num_of_tnums = 64;
+ state->qmi_def_tnums_thresh = 48;
+ state->bmi_max_num_of_tasks = 128;
+ state->max_num_of_open_dmas = 32;
+ state->fm_port_num_of_cg = 256;
+ state->num_of_rx_ports = 5;
+ state->total_fifo_size = 100 * 1024;
+ break;
+
+ case 6:
+ state->dma_thresh_max_commq = 83;
+ state->dma_thresh_max_buf = 127;
+ state->qmi_max_num_of_tnums = 64;
+ state->qmi_def_tnums_thresh = 32;
+ state->fm_port_num_of_cg = 256;
+
+ /* FManV3L */
+ if (minor == 1 || minor == 4) {
+ state->bmi_max_fifo_size = 192 * 1024;
+ state->bmi_max_num_of_tasks = 64;
+ state->max_num_of_open_dmas = 32;
+ state->num_of_rx_ports = 5;
+ if (minor == 1)
+ state->fm_iram_size = 32 * 1024;
+ else
+ state->fm_iram_size = 64 * 1024;
+ state->total_fifo_size = 156 * 1024;
+ }
+ /* FManV3H */
+ else if (minor == 0 || minor == 2 || minor == 3) {
+ state->bmi_max_fifo_size = 384 * 1024;
+ state->fm_iram_size = 64 * 1024;
+ state->bmi_max_num_of_tasks = 128;
+ state->max_num_of_open_dmas = 84;
+ state->num_of_rx_ports = 8;
+ state->total_fifo_size = 295 * 1024;
+ } else {
+ pr_err("Unsupported FManv3 version\n");
+ return -EINVAL;
+ }
+
+ break;
+ default:
+ pr_err("Unsupported FMan version\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool is_init_done(struct fman_cfg *cfg)
+{
+ /* Checks if FMan driver parameters were initialized */
+ if (!cfg)
+ return true;
+
+ return false;
+}
+
+static void free_init_resources(struct fman *fman)
+{
+ if (fman->cam_offset)
+ fman_muram_free_mem(fman->muram, fman->cam_offset,
+ fman->cam_size);
+ if (fman->fifo_offset)
+ fman_muram_free_mem(fman->muram, fman->fifo_offset,
+ fman->fifo_size);
+}
+
+static irqreturn_t bmi_err_event(struct fman *fman)
+{
+ u32 event, mask, force;
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&bmi_rg->fmbm_ievr);
+ mask = ioread32be(&bmi_rg->fmbm_ier);
+ event &= mask;
+ /* clear the forced events */
+ force = ioread32be(&bmi_rg->fmbm_ifr);
+ if (force & event)
+ iowrite32be(force & ~event, &bmi_rg->fmbm_ifr);
+ /* clear the acknowledged events */
+ iowrite32be(event, &bmi_rg->fmbm_ievr);
+
+ if (event & BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC);
+ if (event & BMI_ERR_INTR_EN_LIST_RAM_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_LIST_RAM_ECC);
+ if (event & BMI_ERR_INTR_EN_STATISTICS_RAM_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC);
+ if (event & BMI_ERR_INTR_EN_DISPATCH_RAM_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC);
+
+ return ret;
+}
+
+static irqreturn_t qmi_err_event(struct fman *fman)
+{
+ u32 event, mask, force;
+ struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&qmi_rg->fmqm_eie);
+ mask = ioread32be(&qmi_rg->fmqm_eien);
+ event &= mask;
+
+ /* clear the forced events */
+ force = ioread32be(&qmi_rg->fmqm_eif);
+ if (force & event)
+ iowrite32be(force & ~event, &qmi_rg->fmqm_eif);
+ /* clear the acknowledged events */
+ iowrite32be(event, &qmi_rg->fmqm_eie);
+
+ if (event & QMI_ERR_INTR_EN_DOUBLE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_QMI_DOUBLE_ECC);
+ if (event & QMI_ERR_INTR_EN_DEQ_FROM_DEF)
+ ret = fman->exception_cb(fman,
+ FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID);
+
+ return ret;
+}
+
+static irqreturn_t dma_err_event(struct fman *fman)
+{
+ u32 status, mask, com_id;
+ u8 tnum, port_id, relative_port_id;
+ u16 liodn;
+ struct fman_dma_regs __iomem *dma_rg = fman->dma_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ status = ioread32be(&dma_rg->fmdmsr);
+ mask = ioread32be(&dma_rg->fmdmmr);
+
+ /* clear DMA_STATUS_BUS_ERR if mask has no DMA_MODE_BER */
+ if ((mask & DMA_MODE_BER) != DMA_MODE_BER)
+ status &= ~DMA_STATUS_BUS_ERR;
+
+ /* clear relevant bits if mask has no DMA_MODE_ECC */
+ if ((mask & DMA_MODE_ECC) != DMA_MODE_ECC)
+ status &= ~(DMA_STATUS_FM_SPDAT_ECC |
+ DMA_STATUS_READ_ECC |
+ DMA_STATUS_SYSTEM_WRITE_ECC |
+ DMA_STATUS_FM_WRITE_ECC);
+
+ /* clear set events */
+ iowrite32be(status, &dma_rg->fmdmsr);
+
+ if (status & DMA_STATUS_BUS_ERR) {
+ u64 addr;
+
+ addr = (u64)ioread32be(&dma_rg->fmdmtal);
+ addr |= ((u64)(ioread32be(&dma_rg->fmdmtah)) << 32);
+
+ com_id = ioread32be(&dma_rg->fmdmtcid);
+ port_id = (u8)(((com_id & DMA_TRANSFER_PORTID_MASK) >>
+ DMA_TRANSFER_PORTID_SHIFT));
+ relative_port_id =
+ hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id);
+ tnum = (u8)((com_id & DMA_TRANSFER_TNUM_MASK) >>
+ DMA_TRANSFER_TNUM_SHIFT);
+ liodn = (u16)(com_id & DMA_TRANSFER_LIODN_MASK);
+ ret = fman->bus_error_cb(fman, relative_port_id, addr, tnum,
+ liodn);
+ }
+ if (status & DMA_STATUS_FM_SPDAT_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_SINGLE_PORT_ECC);
+ if (status & DMA_STATUS_READ_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_READ_ECC);
+ if (status & DMA_STATUS_SYSTEM_WRITE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC);
+ if (status & DMA_STATUS_FM_WRITE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_FM_WRITE_ECC);
+
+ return ret;
+}
+
+static irqreturn_t fpm_err_event(struct fman *fman)
+{
+ u32 event;
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&fpm_rg->fmfp_ee);
+ /* clear the all occurred events */
+ iowrite32be(event, &fpm_rg->fmfp_ee);
+
+ if ((event & FPM_EV_MASK_DOUBLE_ECC) &&
+ (event & FPM_EV_MASK_DOUBLE_ECC_EN))
+ ret = fman->exception_cb(fman, FMAN_EX_FPM_DOUBLE_ECC);
+ if ((event & FPM_EV_MASK_STALL) && (event & FPM_EV_MASK_STALL_EN))
+ ret = fman->exception_cb(fman, FMAN_EX_FPM_STALL_ON_TASKS);
+ if ((event & FPM_EV_MASK_SINGLE_ECC) &&
+ (event & FPM_EV_MASK_SINGLE_ECC_EN))
+ ret = fman->exception_cb(fman, FMAN_EX_FPM_SINGLE_ECC);
+
+ return ret;
+}
+
+static irqreturn_t muram_err_intr(struct fman *fman)
+{
+ u32 event, mask;
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&fpm_rg->fm_rcr);
+ mask = ioread32be(&fpm_rg->fm_rie);
+
+ /* clear MURAM event bit (do not clear IRAM event) */
+ iowrite32be(event & ~FPM_RAM_IRAM_ECC, &fpm_rg->fm_rcr);
+
+ if ((mask & FPM_MURAM_ECC_ERR_EX_EN) && (event & FPM_RAM_MURAM_ECC))
+ ret = fman->exception_cb(fman, FMAN_EX_MURAM_ECC);
+
+ return ret;
+}
+
+static irqreturn_t qmi_event(struct fman *fman)
+{
+ u32 event, mask, force;
+ struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&qmi_rg->fmqm_ie);
+ mask = ioread32be(&qmi_rg->fmqm_ien);
+ event &= mask;
+ /* clear the forced events */
+ force = ioread32be(&qmi_rg->fmqm_if);
+ if (force & event)
+ iowrite32be(force & ~event, &qmi_rg->fmqm_if);
+ /* clear the acknowledged events */
+ iowrite32be(event, &qmi_rg->fmqm_ie);
+
+ if (event & QMI_INTR_EN_SINGLE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_QMI_SINGLE_ECC);
+
+ return ret;
+}
+
+static void enable_time_stamp(struct fman *fman)
+{
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ u16 fm_clk_freq = fman->state->fm_clk_freq;
+ u32 tmp, intgr, ts_freq;
+ u64 frac;
+
+ ts_freq = (u32)(1 << fman->state->count1_micro_bit);
+ /* configure timestamp so that bit 8 will count 1 microsecond
+ * Find effective count rate at TIMESTAMP least significant bits:
+ * Effective_Count_Rate = 1MHz x 2^8 = 256MHz
+ * Find frequency ratio between effective count rate and the clock:
+ * Effective_Count_Rate / CLK e.g. for 600 MHz clock:
+ * 256/600 = 0.4266666...
+ */
+
+ intgr = ts_freq / fm_clk_freq;
+ /* we multiply by 2^16 to keep the fraction of the division
+ * we do not div back, since we write this value as a fraction
+ * see spec
+ */
+
+ frac = ((ts_freq << 16) - (intgr << 16) * fm_clk_freq) / fm_clk_freq;
+ /* we check remainder of the division in order to round up if not int */
+ if (((ts_freq << 16) - (intgr << 16) * fm_clk_freq) % fm_clk_freq)
+ frac++;
+
+ tmp = (intgr << FPM_TS_INT_SHIFT) | (u16)frac;
+ iowrite32be(tmp, &fpm_rg->fmfp_tsc2);
+
+ /* enable timestamp with original clock */
+ iowrite32be(FPM_TS_CTL_EN, &fpm_rg->fmfp_tsc1);
+ fman->state->enabled_time_stamp = true;
+}
+
+static int clear_iram(struct fman *fman)
+{
+ struct fman_iram_regs __iomem *iram;
+ int i, count;
+
+ iram = fman->base_addr + IMEM_OFFSET;
+
+ /* Enable the auto-increment */
+ iowrite32be(IRAM_IADD_AIE, &iram->iadd);
+ count = 100;
+ do {
+ udelay(1);
+ } while ((ioread32be(&iram->iadd) != IRAM_IADD_AIE) && --count);
+ if (count == 0)
+ return -EBUSY;
+
+ for (i = 0; i < (fman->state->fm_iram_size / 4); i++)
+ iowrite32be(0xffffffff, &iram->idata);
+
+ iowrite32be(fman->state->fm_iram_size - 4, &iram->iadd);
+ count = 100;
+ do {
+ udelay(1);
+ } while ((ioread32be(&iram->idata) != 0xffffffff) && --count);
+ if (count == 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static u32 get_exception_flag(enum fman_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FMAN_EX_DMA_BUS_ERROR:
+ bit_mask = EX_DMA_BUS_ERROR;
+ break;
+ case FMAN_EX_DMA_SINGLE_PORT_ECC:
+ bit_mask = EX_DMA_SINGLE_PORT_ECC;
+ break;
+ case FMAN_EX_DMA_READ_ECC:
+ bit_mask = EX_DMA_READ_ECC;
+ break;
+ case FMAN_EX_DMA_SYSTEM_WRITE_ECC:
+ bit_mask = EX_DMA_SYSTEM_WRITE_ECC;
+ break;
+ case FMAN_EX_DMA_FM_WRITE_ECC:
+ bit_mask = EX_DMA_FM_WRITE_ECC;
+ break;
+ case FMAN_EX_FPM_STALL_ON_TASKS:
+ bit_mask = EX_FPM_STALL_ON_TASKS;
+ break;
+ case FMAN_EX_FPM_SINGLE_ECC:
+ bit_mask = EX_FPM_SINGLE_ECC;
+ break;
+ case FMAN_EX_FPM_DOUBLE_ECC:
+ bit_mask = EX_FPM_DOUBLE_ECC;
+ break;
+ case FMAN_EX_QMI_SINGLE_ECC:
+ bit_mask = EX_QMI_SINGLE_ECC;
+ break;
+ case FMAN_EX_QMI_DOUBLE_ECC:
+ bit_mask = EX_QMI_DOUBLE_ECC;
+ break;
+ case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID:
+ bit_mask = EX_QMI_DEQ_FROM_UNKNOWN_PORTID;
+ break;
+ case FMAN_EX_BMI_LIST_RAM_ECC:
+ bit_mask = EX_BMI_LIST_RAM_ECC;
+ break;
+ case FMAN_EX_BMI_STORAGE_PROFILE_ECC:
+ bit_mask = EX_BMI_STORAGE_PROFILE_ECC;
+ break;
+ case FMAN_EX_BMI_STATISTICS_RAM_ECC:
+ bit_mask = EX_BMI_STATISTICS_RAM_ECC;
+ break;
+ case FMAN_EX_BMI_DISPATCH_RAM_ECC:
+ bit_mask = EX_BMI_DISPATCH_RAM_ECC;
+ break;
+ case FMAN_EX_MURAM_ECC:
+ bit_mask = EX_MURAM_ECC;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static int get_module_event(enum fman_event_modules module, u8 mod_id,
+ enum fman_intr_type intr_type)
+{
+ int event;
+
+ switch (module) {
+ case FMAN_MOD_MAC:
+ if (intr_type == FMAN_INTR_TYPE_ERR)
+ event = FMAN_EV_ERR_MAC0 + mod_id;
+ else
+ event = FMAN_EV_MAC0 + mod_id;
+ break;
+ case FMAN_MOD_FMAN_CTRL:
+ if (intr_type == FMAN_INTR_TYPE_ERR)
+ event = FMAN_EV_CNT;
+ else
+ event = (FMAN_EV_FMAN_CTRL_0 + mod_id);
+ break;
+ case FMAN_MOD_DUMMY_LAST:
+ event = FMAN_EV_CNT;
+ break;
+ default:
+ event = FMAN_EV_CNT;
+ break;
+ }
+
+ return event;
+}
+
+static int set_size_of_fifo(struct fman *fman, u8 port_id, u32 *size_of_fifo,
+ u32 *extra_size_of_fifo)
+{
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ u32 fifo = *size_of_fifo;
+ u32 extra_fifo = *extra_size_of_fifo;
+ u32 tmp;
+
+ /* if this is the first time a port requires extra_fifo_pool_size,
+ * the total extra_fifo_pool_size must be initialized to 1 buffer per
+ * port
+ */
+ if (extra_fifo && !fman->state->extra_fifo_pool_size)
+ fman->state->extra_fifo_pool_size =
+ fman->state->num_of_rx_ports * FMAN_BMI_FIFO_UNITS;
+
+ fman->state->extra_fifo_pool_size =
+ max(fman->state->extra_fifo_pool_size, extra_fifo);
+
+ /* check that there are enough uncommitted fifo size */
+ if ((fman->state->accumulated_fifo_size + fifo) >
+ (fman->state->total_fifo_size -
+ fman->state->extra_fifo_pool_size)) {
+ dev_err(fman->dev, "%s: Requested fifo size and extra size exceed total FIFO size.\n",
+ __func__);
+ return -EAGAIN;
+ }
+
+ /* Read, modify and write to HW */
+ tmp = (fifo / FMAN_BMI_FIFO_UNITS - 1) |
+ ((extra_fifo / FMAN_BMI_FIFO_UNITS) <<
+ BMI_EXTRA_FIFO_SIZE_SHIFT);
+ iowrite32be(tmp, &bmi_rg->fmbm_pfs[port_id - 1]);
+
+ /* update accumulated */
+ fman->state->accumulated_fifo_size += fifo;
+
+ return 0;
+}
+
+static int set_num_of_tasks(struct fman *fman, u8 port_id, u8 *num_of_tasks,
+ u8 *num_of_extra_tasks)
+{
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ u8 tasks = *num_of_tasks;
+ u8 extra_tasks = *num_of_extra_tasks;
+ u32 tmp;
+
+ if (extra_tasks)
+ fman->state->extra_tasks_pool_size =
+ max(fman->state->extra_tasks_pool_size, extra_tasks);
+
+ /* check that there are enough uncommitted tasks */
+ if ((fman->state->accumulated_num_of_tasks + tasks) >
+ (fman->state->total_num_of_tasks -
+ fman->state->extra_tasks_pool_size)) {
+ dev_err(fman->dev, "%s: Requested num_of_tasks and extra tasks pool for fm%d exceed total num_of_tasks.\n",
+ __func__, fman->state->fm_id);
+ return -EAGAIN;
+ }
+ /* update accumulated */
+ fman->state->accumulated_num_of_tasks += tasks;
+
+ /* Write to HW */
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) &
+ ~(BMI_NUM_OF_TASKS_MASK | BMI_NUM_OF_EXTRA_TASKS_MASK);
+ tmp |= ((u32)((tasks - 1) << BMI_NUM_OF_TASKS_SHIFT) |
+ (u32)(extra_tasks << BMI_EXTRA_NUM_OF_TASKS_SHIFT));
+ iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]);
+
+ return 0;
+}
+
+static int set_num_of_open_dmas(struct fman *fman, u8 port_id,
+ u8 *num_of_open_dmas,
+ u8 *num_of_extra_open_dmas)
+{
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ u8 open_dmas = *num_of_open_dmas;
+ u8 extra_open_dmas = *num_of_extra_open_dmas;
+ u8 total_num_dmas = 0, current_val = 0, current_extra_val = 0;
+ u32 tmp;
+
+ if (!open_dmas) {
+ /* Configuration according to values in the HW.
+ * read the current number of open Dma's
+ */
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]);
+ current_extra_val = (u8)((tmp & BMI_NUM_OF_EXTRA_DMAS_MASK) >>
+ BMI_EXTRA_NUM_OF_DMAS_SHIFT);
+
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]);
+ current_val = (u8)(((tmp & BMI_NUM_OF_DMAS_MASK) >>
+ BMI_NUM_OF_DMAS_SHIFT) + 1);
+
+ /* This is the first configuration and user did not
+ * specify value (!open_dmas), reset values will be used
+ * and we just save these values for resource management
+ */
+ fman->state->extra_open_dmas_pool_size =
+ (u8)max(fman->state->extra_open_dmas_pool_size,
+ current_extra_val);
+ fman->state->accumulated_num_of_open_dmas += current_val;
+ *num_of_open_dmas = current_val;
+ *num_of_extra_open_dmas = current_extra_val;
+ return 0;
+ }
+
+ if (extra_open_dmas > current_extra_val)
+ fman->state->extra_open_dmas_pool_size =
+ (u8)max(fman->state->extra_open_dmas_pool_size,
+ extra_open_dmas);
+
+ if ((fman->state->rev_info.major < 6) &&
+ (fman->state->accumulated_num_of_open_dmas - current_val +
+ open_dmas > fman->state->max_num_of_open_dmas)) {
+ dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds total num_of_open_dmas.\n",
+ __func__, fman->state->fm_id);
+ return -EAGAIN;
+ } else if ((fman->state->rev_info.major >= 6) &&
+ !((fman->state->rev_info.major == 6) &&
+ (fman->state->rev_info.minor == 0)) &&
+ (fman->state->accumulated_num_of_open_dmas -
+ current_val + open_dmas >
+ fman->state->dma_thresh_max_commq + 1)) {
+ dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds DMA Command queue (%d)\n",
+ __func__, fman->state->fm_id,
+ fman->state->dma_thresh_max_commq + 1);
+ return -EAGAIN;
+ }
+
+ WARN_ON(fman->state->accumulated_num_of_open_dmas < current_val);
+ /* update acummulated */
+ fman->state->accumulated_num_of_open_dmas -= current_val;
+ fman->state->accumulated_num_of_open_dmas += open_dmas;
+
+ if (fman->state->rev_info.major < 6)
+ total_num_dmas =
+ (u8)(fman->state->accumulated_num_of_open_dmas +
+ fman->state->extra_open_dmas_pool_size);
+
+ /* calculate reg */
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) &
+ ~(BMI_NUM_OF_DMAS_MASK | BMI_NUM_OF_EXTRA_DMAS_MASK);
+ tmp |= (u32)(((open_dmas - 1) << BMI_NUM_OF_DMAS_SHIFT) |
+ (extra_open_dmas << BMI_EXTRA_NUM_OF_DMAS_SHIFT));
+ iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]);
+
+ /* update total num of DMA's with committed number of open DMAS,
+ * and max uncommitted pool.
+ */
+ if (total_num_dmas) {
+ tmp = ioread32be(&bmi_rg->fmbm_cfg2) & ~BMI_CFG2_DMAS_MASK;
+ tmp |= (u32)(total_num_dmas - 1) << BMI_CFG2_DMAS_SHIFT;
+ iowrite32be(tmp, &bmi_rg->fmbm_cfg2);
+ }
+
+ return 0;
+}
+
+static int fman_config(struct fman *fman)
+{
+ void __iomem *base_addr;
+ int err;
+
+ base_addr = fman->dts_params.base_addr;
+
+ fman->state = kzalloc(sizeof(*fman->state), GFP_KERNEL);
+ if (!fman->state)
+ goto err_fm_state;
+
+ /* Allocate the FM driver's parameters structure */
+ fman->cfg = kzalloc(sizeof(*fman->cfg), GFP_KERNEL);
+ if (!fman->cfg)
+ goto err_fm_drv;
+
+ /* Initialize MURAM block */
+ fman->muram =
+ fman_muram_init(fman->dts_params.muram_res.start,
+ resource_size(&fman->dts_params.muram_res));
+ if (!fman->muram)
+ goto err_fm_soc_specific;
+
+ /* Initialize FM parameters which will be kept by the driver */
+ fman->state->fm_id = fman->dts_params.id;
+ fman->state->fm_clk_freq = fman->dts_params.clk_freq;
+ fman->state->qman_channel_base = fman->dts_params.qman_channel_base;
+ fman->state->num_of_qman_channels =
+ fman->dts_params.num_of_qman_channels;
+ fman->state->res = fman->dts_params.res;
+ fman->exception_cb = fman_exceptions;
+ fman->bus_error_cb = fman_bus_error;
+ fman->fpm_regs = base_addr + FPM_OFFSET;
+ fman->bmi_regs = base_addr + BMI_OFFSET;
+ fman->qmi_regs = base_addr + QMI_OFFSET;
+ fman->dma_regs = base_addr + DMA_OFFSET;
+ fman->base_addr = base_addr;
+
+ spin_lock_init(&fman->spinlock);
+ fman_defconfig(fman->cfg);
+
+ fman->state->extra_fifo_pool_size = 0;
+ fman->state->exceptions = (EX_DMA_BUS_ERROR |
+ EX_DMA_READ_ECC |
+ EX_DMA_SYSTEM_WRITE_ECC |
+ EX_DMA_FM_WRITE_ECC |
+ EX_FPM_STALL_ON_TASKS |
+ EX_FPM_SINGLE_ECC |
+ EX_FPM_DOUBLE_ECC |
+ EX_QMI_DEQ_FROM_UNKNOWN_PORTID |
+ EX_BMI_LIST_RAM_ECC |
+ EX_BMI_STORAGE_PROFILE_ECC |
+ EX_BMI_STATISTICS_RAM_ECC |
+ EX_MURAM_ECC |
+ EX_BMI_DISPATCH_RAM_ECC |
+ EX_QMI_DOUBLE_ECC |
+ EX_QMI_SINGLE_ECC);
+
+ /* Read FMan revision for future use*/
+ fman_get_revision(fman, &fman->state->rev_info);
+
+ err = fill_soc_specific_params(fman->state);
+ if (err)
+ goto err_fm_soc_specific;
+
+ /* FM_AID_MODE_NO_TNUM_SW005 Errata workaround */
+ if (fman->state->rev_info.major >= 6)
+ fman->cfg->dma_aid_mode = FMAN_DMA_AID_OUT_PORT_ID;
+
+ fman->cfg->qmi_def_tnums_thresh = fman->state->qmi_def_tnums_thresh;
+
+ fman->state->total_num_of_tasks =
+ (u8)DFLT_TOTAL_NUM_OF_TASKS(fman->state->rev_info.major,
+ fman->state->rev_info.minor,
+ fman->state->bmi_max_num_of_tasks);
+
+ if (fman->state->rev_info.major < 6) {
+ fman->cfg->dma_comm_qtsh_clr_emer =
+ (u8)DFLT_DMA_COMM_Q_LOW(fman->state->rev_info.major,
+ fman->state->dma_thresh_max_commq);
+
+ fman->cfg->dma_comm_qtsh_asrt_emer =
+ (u8)DFLT_DMA_COMM_Q_HIGH(fman->state->rev_info.major,
+ fman->state->dma_thresh_max_commq);
+
+ fman->cfg->dma_cam_num_of_entries =
+ DFLT_DMA_CAM_NUM_OF_ENTRIES(fman->state->rev_info.major);
+
+ fman->cfg->dma_read_buf_tsh_clr_emer =
+ DFLT_DMA_READ_INT_BUF_LOW(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_read_buf_tsh_asrt_emer =
+ DFLT_DMA_READ_INT_BUF_HIGH(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_write_buf_tsh_clr_emer =
+ DFLT_DMA_WRITE_INT_BUF_LOW(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_write_buf_tsh_asrt_emer =
+ DFLT_DMA_WRITE_INT_BUF_HIGH(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_axi_dbg_num_of_beats =
+ DFLT_AXI_DBG_NUM_OF_BEATS;
+ }
+
+ return 0;
+
+err_fm_soc_specific:
+ kfree(fman->cfg);
+err_fm_drv:
+ kfree(fman->state);
+err_fm_state:
+ kfree(fman);
+ return -EINVAL;
+}
+
+static int fman_init(struct fman *fman)
+{
+ struct fman_cfg *cfg = NULL;
+ int err = 0, i, count;
+
+ if (is_init_done(fman->cfg))
+ return -EINVAL;
+
+ fman->state->count1_micro_bit = FM_TIMESTAMP_1_USEC_BIT;
+
+ cfg = fman->cfg;
+
+ /* clear revision-dependent non existing exception */
+ if (fman->state->rev_info.major < 6)
+ fman->state->exceptions &= ~FMAN_EX_BMI_DISPATCH_RAM_ECC;
+
+ if (fman->state->rev_info.major >= 6)
+ fman->state->exceptions &= ~FMAN_EX_QMI_SINGLE_ECC;
+
+ /* clear CPG */
+ memset_io((void __iomem *)(fman->base_addr + CGP_OFFSET), 0,
+ fman->state->fm_port_num_of_cg);
+
+ /* Save LIODN info before FMan reset
+ * Skipping non-existent port 0 (i = 1)
+ */
+ for (i = 1; i < FMAN_LIODN_TBL; i++) {
+ u32 liodn_base;
+
+ fman->liodn_offset[i] =
+ ioread32be(&fman->bmi_regs->fmbm_spliodn[i - 1]);
+ liodn_base = ioread32be(&fman->dma_regs->fmdmplr[i / 2]);
+ if (i % 2) {
+ /* FMDM_PLR LSB holds LIODN base for odd ports */
+ liodn_base &= DMA_LIODN_BASE_MASK;
+ } else {
+ /* FMDM_PLR MSB holds LIODN base for even ports */
+ liodn_base >>= DMA_LIODN_SHIFT;
+ liodn_base &= DMA_LIODN_BASE_MASK;
+ }
+ fman->liodn_base[i] = liodn_base;
+ }
+
+ /* FMan Reset (supported only for FMan V2) */
+ if (fman->state->rev_info.major >= 6) {
+ /* Errata A007273 */
+ dev_dbg(fman->dev, "%s: FManV3 reset is not supported!\n",
+ __func__);
+ } else {
+ iowrite32be(FPM_RSTC_FM_RESET, &fman->fpm_regs->fm_rstc);
+ /* Wait for reset completion */
+ count = 100;
+ do {
+ udelay(1);
+ } while (((ioread32be(&fman->fpm_regs->fm_rstc)) &
+ FPM_RSTC_FM_RESET) && --count);
+ if (count == 0)
+ return -EBUSY;
+ }
+
+ if (ioread32be(&fman->qmi_regs->fmqm_gs) & QMI_GS_HALT_NOT_BUSY) {
+ resume(fman->fpm_regs);
+ /* Wait until QMI is not in halt not busy state */
+ count = 100;
+ do {
+ udelay(1);
+ } while (((ioread32be(&fman->qmi_regs->fmqm_gs)) &
+ QMI_GS_HALT_NOT_BUSY) && --count);
+ if (count == 0)
+ dev_warn(fman->dev, "%s: QMI is in halt not busy state\n",
+ __func__);
+ }
+
+ if (clear_iram(fman) != 0)
+ return -EINVAL;
+
+ cfg->exceptions = fman->state->exceptions;
+
+ /* Init DMA Registers */
+
+ err = dma_init(fman);
+ if (err != 0) {
+ free_init_resources(fman);
+ return err;
+ }
+
+ /* Init FPM Registers */
+ fpm_init(fman->fpm_regs, fman->cfg);
+
+ /* define common resources */
+ /* allocate MURAM for FIFO according to total size */
+ fman->fifo_offset = fman_muram_alloc(fman->muram,
+ fman->state->total_fifo_size);
+ if (IS_ERR_VALUE(fman->cam_offset)) {
+ free_init_resources(fman);
+ dev_err(fman->dev, "%s: MURAM alloc for BMI FIFO failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ cfg->fifo_base_addr = fman->fifo_offset;
+ cfg->total_fifo_size = fman->state->total_fifo_size;
+ cfg->total_num_of_tasks = fman->state->total_num_of_tasks;
+ cfg->clk_freq = fman->state->fm_clk_freq;
+
+ /* Init BMI Registers */
+ bmi_init(fman->bmi_regs, fman->cfg);
+
+ /* Init QMI Registers */
+ qmi_init(fman->qmi_regs, fman->cfg);
+
+ err = enable(fman, cfg);
+ if (err != 0)
+ return err;
+
+ enable_time_stamp(fman);
+
+ kfree(fman->cfg);
+ fman->cfg = NULL;
+
+ return 0;
+}
+
+static int fman_set_exception(struct fman *fman,
+ enum fman_exceptions exception, bool enable)
+{
+ u32 bit_mask = 0;
+
+ if (!is_init_done(fman->cfg))
+ return -EINVAL;
+
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ fman->state->exceptions |= bit_mask;
+ else
+ fman->state->exceptions &= ~bit_mask;
+ } else {
+ dev_err(fman->dev, "%s: Undefined exception (%d)\n",
+ __func__, exception);
+ return -EINVAL;
+ }
+
+ return set_exception(fman, exception, enable);
+}
+
+/**
+ * fman_register_intr
+ * @fman: A Pointer to FMan device
+ * @mod: Calling module
+ * @mod_id: Module id (if more than 1 exists, '0' if not)
+ * @intr_type: Interrupt type (error/normal) selection.
+ * @f_isr: The interrupt service routine.
+ * @h_src_arg: Argument to be passed to f_isr.
+ *
+ * Used to register an event handler to be processed by FMan
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+void fman_register_intr(struct fman *fman, enum fman_event_modules module,
+ u8 mod_id, enum fman_intr_type intr_type,
+ void (*isr_cb)(void *src_arg), void *src_arg)
+{
+ int event = 0;
+
+ event = get_module_event(module, mod_id, intr_type);
+ WARN_ON(event >= FMAN_EV_CNT);
+
+ /* register in local FM structure */
+ fman->intr_mng[event].isr_cb = isr_cb;
+ fman->intr_mng[event].src_handle = src_arg;
+}
+
+/**
+ * fman_unregister_intr
+ * @fman: A Pointer to FMan device
+ * @mod: Calling module
+ * @mod_id: Module id (if more than 1 exists, '0' if not)
+ * @intr_type: Interrupt type (error/normal) selection.
+ *
+ * Used to unregister an event handler to be processed by FMan
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+void fman_unregister_intr(struct fman *fman, enum fman_event_modules module,
+ u8 mod_id, enum fman_intr_type intr_type)
+{
+ int event = 0;
+
+ event = get_module_event(module, mod_id, intr_type);
+ WARN_ON(event >= FMAN_EV_CNT);
+
+ fman->intr_mng[event].isr_cb = NULL;
+ fman->intr_mng[event].src_handle = NULL;
+}
+
+/**
+ * fman_set_port_params
+ * @fman: A Pointer to FMan device
+ * @port_params: Port parameters
+ *
+ * Used by FMan Port to pass parameters to the FMan
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_set_port_params(struct fman *fman,
+ struct fman_port_init_params *port_params)
+{
+ int err;
+ unsigned long flags;
+ u8 port_id = port_params->port_id, mac_id;
+
+ spin_lock_irqsave(&fman->spinlock, flags);
+
+ err = set_num_of_tasks(fman, port_params->port_id,
+ &port_params->num_of_tasks,
+ &port_params->num_of_extra_tasks);
+ if (err)
+ goto return_err;
+
+ /* TX Ports */
+ if (port_params->port_type != FMAN_PORT_TYPE_RX) {
+ u32 enq_th, deq_th, reg;
+
+ /* update qmi ENQ/DEQ threshold */
+ fman->state->accumulated_num_of_deq_tnums +=
+ port_params->deq_pipeline_depth;
+ enq_th = (ioread32be(&fman->qmi_regs->fmqm_gc) &
+ QMI_CFG_ENQ_MASK) >> QMI_CFG_ENQ_SHIFT;
+ /* if enq_th is too big, we reduce it to the max value
+ * that is still 0
+ */
+ if (enq_th >= (fman->state->qmi_max_num_of_tnums -
+ fman->state->accumulated_num_of_deq_tnums)) {
+ enq_th =
+ fman->state->qmi_max_num_of_tnums -
+ fman->state->accumulated_num_of_deq_tnums - 1;
+
+ reg = ioread32be(&fman->qmi_regs->fmqm_gc);
+ reg &= ~QMI_CFG_ENQ_MASK;
+ reg |= (enq_th << QMI_CFG_ENQ_SHIFT);
+ iowrite32be(reg, &fman->qmi_regs->fmqm_gc);
+ }
+
+ deq_th = ioread32be(&fman->qmi_regs->fmqm_gc) &
+ QMI_CFG_DEQ_MASK;
+ /* if deq_th is too small, we enlarge it to the min
+ * value that is still 0.
+ * depTh may not be larger than 63
+ * (fman->state->qmi_max_num_of_tnums-1).
+ */
+ if ((deq_th <= fman->state->accumulated_num_of_deq_tnums) &&
+ (deq_th < fman->state->qmi_max_num_of_tnums - 1)) {
+ deq_th = fman->state->accumulated_num_of_deq_tnums + 1;
+ reg = ioread32be(&fman->qmi_regs->fmqm_gc);
+ reg &= ~QMI_CFG_DEQ_MASK;
+ reg |= deq_th;
+ iowrite32be(reg, &fman->qmi_regs->fmqm_gc);
+ }
+ }
+
+ err = set_size_of_fifo(fman, port_params->port_id,
+ &port_params->size_of_fifo,
+ &port_params->extra_size_of_fifo);
+ if (err)
+ goto return_err;
+
+ err = set_num_of_open_dmas(fman, port_params->port_id,
+ &port_params->num_of_open_dmas,
+ &port_params->num_of_extra_open_dmas);
+ if (err)
+ goto return_err;
+
+ set_port_liodn(fman, port_id, fman->liodn_base[port_id],
+ fman->liodn_offset[port_id]);
+
+ if (fman->state->rev_info.major < 6)
+ set_port_order_restoration(fman->fpm_regs, port_id);
+
+ mac_id = hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id);
+
+ if (port_params->max_frame_length >= fman->state->mac_mfl[mac_id]) {
+ fman->state->port_mfl[mac_id] = port_params->max_frame_length;
+ } else {
+ dev_warn(fman->dev, "%s: Port (%d) max_frame_length is smaller than MAC (%d) current MTU\n",
+ __func__, port_id, mac_id);
+ err = -EINVAL;
+ goto return_err;
+ }
+
+ spin_unlock_irqrestore(&fman->spinlock, flags);
+
+ return 0;
+
+return_err:
+ spin_unlock_irqrestore(&fman->spinlock, flags);
+ return err;
+}
+
+/**
+ * fman_reset_mac
+ * @fman: A Pointer to FMan device
+ * @mac_id: MAC id to be reset
+ *
+ * Reset a specific MAC
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_reset_mac(struct fman *fman, u8 mac_id)
+{
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ u32 msk, timeout = 100;
+
+ if (fman->state->rev_info.major >= 6) {
+ dev_err(fman->dev, "%s: FMan MAC reset no available for FMan V3!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Get the relevant bit mask */
+ switch (mac_id) {
+ case 0:
+ msk = FPM_RSTC_MAC0_RESET;
+ break;
+ case 1:
+ msk = FPM_RSTC_MAC1_RESET;
+ break;
+ case 2:
+ msk = FPM_RSTC_MAC2_RESET;
+ break;
+ case 3:
+ msk = FPM_RSTC_MAC3_RESET;
+ break;
+ case 4:
+ msk = FPM_RSTC_MAC4_RESET;
+ break;
+ case 5:
+ msk = FPM_RSTC_MAC5_RESET;
+ break;
+ case 6:
+ msk = FPM_RSTC_MAC6_RESET;
+ break;
+ case 7:
+ msk = FPM_RSTC_MAC7_RESET;
+ break;
+ case 8:
+ msk = FPM_RSTC_MAC8_RESET;
+ break;
+ case 9:
+ msk = FPM_RSTC_MAC9_RESET;
+ break;
+ default:
+ dev_warn(fman->dev, "%s: Illegal MAC Id [%d]\n",
+ __func__, mac_id);
+ return -EINVAL;
+ }
+
+ /* reset */
+ iowrite32be(msk, &fpm_rg->fm_rstc);
+ while ((ioread32be(&fpm_rg->fm_rstc) & msk) && --timeout)
+ udelay(10);
+
+ if (!timeout)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * fman_set_mac_max_frame
+ * @fman: A Pointer to FMan device
+ * @mac_id: MAC id
+ * @mfl: Maximum frame length
+ *
+ * Set maximum frame length of specific MAC in FMan driver
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl)
+{
+ /* if port is already initialized, check that MaxFrameLength is smaller
+ * or equal to the port's max
+ */
+ if ((!fman->state->port_mfl[mac_id]) ||
+ (fman->state->port_mfl[mac_id] &&
+ (mfl <= fman->state->port_mfl[mac_id]))) {
+ fman->state->mac_mfl[mac_id] = mfl;
+ } else {
+ dev_warn(fman->dev, "%s: MAC max_frame_length is larger than Port max_frame_length\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * fman_get_clock_freq
+ * @fman: A Pointer to FMan device
+ *
+ * Get FMan clock frequency
+ *
+ * Return: FMan clock frequency
+ */
+u16 fman_get_clock_freq(struct fman *fman)
+{
+ return fman->state->fm_clk_freq;
+}
+
+/**
+ * fman_get_bmi_max_fifo_size
+ * @fman: A Pointer to FMan device
+ *
+ * Get FMan maximum FIFO size
+ *
+ * Return: FMan Maximum FIFO size
+ */
+u32 fman_get_bmi_max_fifo_size(struct fman *fman)
+{
+ return fman->state->bmi_max_fifo_size;
+}
+
+/**
+ * fman_get_revision
+ * @fman - Pointer to the FMan module
+ * @rev_info - A structure of revision information parameters.
+ *
+ * Returns the FM revision
+ *
+ * Allowed only following fman_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fman->fpm_regs->fm_ip_rev_1);
+ rev_info->major = (u8)((tmp & FPM_REV1_MAJOR_MASK) >>
+ FPM_REV1_MAJOR_SHIFT);
+ rev_info->minor = tmp & FPM_REV1_MINOR_MASK;
+}
+
+/**
+ * fman_get_qman_channel_id
+ * @fman: A Pointer to FMan device
+ * @port_id: Port id
+ *
+ * Get QMan channel ID associated to the Port id
+ *
+ * Return: QMan channel ID
+ */
+u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id)
+{
+ int i;
+
+ if (fman->state->rev_info.major >= 6) {
+ u32 port_ids[] = {0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7};
+ for (i = 0; i < fman->state->num_of_qman_channels; i++) {
+ if (port_ids[i] == port_id)
+ break;
+ }
+ } else {
+ u32 port_ids[] = {0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1,
+ 0x2, 0x3, 0x4, 0x5, 0x7, 0x7};
+ for (i = 0; i < fman->state->num_of_qman_channels; i++) {
+ if (port_ids[i] == port_id)
+ break;
+ }
+ }
+
+ if (i == fman->state->num_of_qman_channels)
+ return 0;
+
+ return fman->state->qman_channel_base + i;
+}
+
+/**
+ * fman_get_mem_region
+ * @fman: A Pointer to FMan device
+ *
+ * Get FMan memory region
+ *
+ * Return: A structure with FMan memory region information
+ */
+struct resource *fman_get_mem_region(struct fman *fman)
+{
+ return fman->state->res;
+}
+
+/* Bootargs defines */
+/* Extra headroom for RX buffers - Default, min and max */
+#define FSL_FM_RX_EXTRA_HEADROOM 64
+#define FSL_FM_RX_EXTRA_HEADROOM_MIN 16
+#define FSL_FM_RX_EXTRA_HEADROOM_MAX 384
+
+/* Maximum frame length */
+#define FSL_FM_MAX_FRAME_SIZE 1522
+#define FSL_FM_MAX_POSSIBLE_FRAME_SIZE 9600
+#define FSL_FM_MIN_POSSIBLE_FRAME_SIZE 64
+
+/* Extra headroom for Rx buffers.
+ * FMan is instructed to allocate, on the Rx path, this amount of
+ * space at the beginning of a data buffer, beside the DPA private
+ * data area and the IC fields.
+ * Does not impact Tx buffer layout.
+ * Configurable from bootargs. 64 by default, it's needed on
+ * particular forwarding scenarios that add extra headers to the
+ * forwarded frame.
+ */
+int fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM;
+module_param(fsl_fm_rx_extra_headroom, int, 0);
+MODULE_PARM_DESC(fsl_fm_rx_extra_headroom, "Extra headroom for Rx buffers");
+
+/* Max frame size, across all interfaces.
+ * Configurable from bootargs, to avoid allocating oversized (socket)
+ * buffers when not using jumbo frames.
+ * Must be large enough to accommodate the network MTU, but small enough
+ * to avoid wasting skb memory.
+ *
+ * Could be overridden once, at boot-time, via the
+ * fm_set_max_frm() callback.
+ */
+int fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE;
+module_param(fsl_fm_max_frm, int, 0);
+MODULE_PARM_DESC(fsl_fm_max_frm, "Maximum frame size, across all interfaces");
+
+/**
+ * fman_get_max_frm
+ *
+ * Return: Max frame length configured in the FM driver
+ */
+u16 fman_get_max_frm(void)
+{
+ static bool fm_check_mfl;
+
+ if (!fm_check_mfl) {
+ if (fsl_fm_max_frm > FSL_FM_MAX_POSSIBLE_FRAME_SIZE ||
+ fsl_fm_max_frm < FSL_FM_MIN_POSSIBLE_FRAME_SIZE) {
+ pr_warn("Invalid fsl_fm_max_frm value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n",
+ fsl_fm_max_frm,
+ FSL_FM_MIN_POSSIBLE_FRAME_SIZE,
+ FSL_FM_MAX_POSSIBLE_FRAME_SIZE,
+ FSL_FM_MAX_FRAME_SIZE);
+ fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE;
+ }
+ fm_check_mfl = true;
+ }
+
+ return fsl_fm_max_frm;
+}
+EXPORT_SYMBOL(fman_get_max_frm);
+
+/**
+ * fman_get_rx_extra_headroom
+ *
+ * Return: Extra headroom size configured in the FM driver
+ */
+int fman_get_rx_extra_headroom(void)
+{
+ static bool fm_check_rx_extra_headroom;
+
+ if (!fm_check_rx_extra_headroom) {
+ if (fsl_fm_rx_extra_headroom > FSL_FM_RX_EXTRA_HEADROOM_MAX ||
+ fsl_fm_rx_extra_headroom < FSL_FM_RX_EXTRA_HEADROOM_MIN) {
+ pr_warn("Invalid fsl_fm_rx_extra_headroom value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n",
+ fsl_fm_rx_extra_headroom,
+ FSL_FM_RX_EXTRA_HEADROOM_MIN,
+ FSL_FM_RX_EXTRA_HEADROOM_MAX,
+ FSL_FM_RX_EXTRA_HEADROOM);
+ fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM;
+ }
+
+ fm_check_rx_extra_headroom = true;
+ fsl_fm_rx_extra_headroom = ALIGN(fsl_fm_rx_extra_headroom, 16);
+ }
+
+ return fsl_fm_rx_extra_headroom;
+}
+EXPORT_SYMBOL(fman_get_rx_extra_headroom);
+
+/**
+ * fman_bind
+ * @dev: FMan OF device pointer
+ *
+ * Bind to a specific FMan device.
+ *
+ * Allowed only after the port was created.
+ *
+ * Return: A pointer to the FMan device
+ */
+struct fman *fman_bind(struct device *fm_dev)
+{
+ return (struct fman *)(dev_get_drvdata(get_device(fm_dev)));
+}
+
+static irqreturn_t fman_err_irq(int irq, void *handle)
+{
+ struct fman *fman = (struct fman *)handle;
+ u32 pending;
+ struct fman_fpm_regs __iomem *fpm_rg;
+ irqreturn_t single_ret, ret = IRQ_NONE;
+
+ if (!is_init_done(fman->cfg))
+ return IRQ_NONE;
+
+ fpm_rg = fman->fpm_regs;
+
+ /* error interrupts */
+ pending = ioread32be(&fpm_rg->fm_epi);
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & ERR_INTR_EN_BMI) {
+ single_ret = bmi_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_QMI) {
+ single_ret = qmi_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_FPM) {
+ single_ret = fpm_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_DMA) {
+ single_ret = dma_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MURAM) {
+ single_ret = muram_err_intr(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ /* MAC error interrupts */
+ if (pending & ERR_INTR_EN_MAC0) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 0);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC1) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 1);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC2) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 2);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC3) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 3);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC4) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 4);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC5) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 5);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC6) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 6);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC7) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 7);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC8) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 8);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC9) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 9);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static irqreturn_t fman_irq(int irq, void *handle)
+{
+ struct fman *fman = (struct fman *)handle;
+ u32 pending;
+ struct fman_fpm_regs __iomem *fpm_rg;
+ irqreturn_t single_ret, ret = IRQ_NONE;
+
+ if (!is_init_done(fman->cfg))
+ return IRQ_NONE;
+
+ fpm_rg = fman->fpm_regs;
+
+ /* normal interrupts */
+ pending = ioread32be(&fpm_rg->fm_npi);
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & INTR_EN_QMI) {
+ single_ret = qmi_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ /* MAC interrupts */
+ if (pending & INTR_EN_MAC0) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 0);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC1) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 1);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC2) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 2);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC3) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 3);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC4) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 4);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC5) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 5);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC6) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 6);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC7) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 7);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC8) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 8);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC9) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 9);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static const struct of_device_id fman_muram_match[] = {
+ {
+ .compatible = "fsl,fman-muram"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, fman_muram_match);
+
+static struct fman *read_dts_node(struct platform_device *of_dev)
+{
+ struct fman *fman;
+ struct device_node *fm_node, *muram_node;
+ struct resource *res;
+ const u32 *u32_prop;
+ int lenp, err, irq;
+ struct clk *clk;
+ u32 clk_rate;
+ phys_addr_t phys_base_addr;
+ resource_size_t mem_size;
+
+ fman = kzalloc(sizeof(*fman), GFP_KERNEL);
+ if (!fman)
+ return NULL;
+
+ fm_node = of_node_get(of_dev->dev.of_node);
+
+ u32_prop = (const u32 *)of_get_property(fm_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(&of_dev->dev, "%s: of_get_property(%s, cell-index) failed\n",
+ __func__, fm_node->full_name);
+ goto fman_node_put;
+ }
+ if (WARN_ON(lenp != sizeof(u32)))
+ goto fman_node_put;
+
+ fman->dts_params.id = (u8)fdt32_to_cpu(u32_prop[0]);
+
+ /* Get the FM interrupt */
+ res = platform_get_resource(of_dev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&of_dev->dev, "%s: Can't get FMan IRQ resource\n",
+ __func__);
+ goto fman_node_put;
+ }
+ irq = res->start;
+
+ /* Get the FM error interrupt */
+ res = platform_get_resource(of_dev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ dev_err(&of_dev->dev, "%s: Can't get FMan Error IRQ resource\n",
+ __func__);
+ goto fman_node_put;
+ }
+ fman->dts_params.err_irq = res->start;
+
+ /* Get the FM address */
+ res = platform_get_resource(of_dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&of_dev->dev, "%s: Can't get FMan memory resouce\n",
+ __func__);
+ goto fman_node_put;
+ }
+
+ phys_base_addr = res->start;
+ mem_size = resource_size(res);
+
+ clk = of_clk_get(fm_node, 0);
+ if (IS_ERR(clk)) {
+ dev_err(&of_dev->dev, "%s: Failed to get FM%d clock structure\n",
+ __func__, fman->dts_params.id);
+ goto fman_node_put;
+ }
+
+ clk_rate = clk_get_rate(clk);
+ if (!clk_rate) {
+ dev_err(&of_dev->dev, "%s: Failed to determine FM%d clock rate\n",
+ __func__, fman->dts_params.id);
+ goto fman_node_put;
+ }
+ /* Rounding to MHz */
+ fman->dts_params.clk_freq = DIV_ROUND_UP(clk_rate, 1000000);
+
+ u32_prop = (const u32 *)of_get_property(fm_node,
+ "fsl,qman-channel-range",
+ &lenp);
+ if (!u32_prop) {
+ dev_err(&of_dev->dev, "%s: of_get_property(%s, fsl,qman-channel-range) failed\n",
+ __func__, fm_node->full_name);
+ goto fman_node_put;
+ }
+ if (WARN_ON(lenp != sizeof(u32) * 2))
+ goto fman_node_put;
+ fman->dts_params.qman_channel_base = fdt32_to_cpu(u32_prop[0]);
+ fman->dts_params.num_of_qman_channels = fdt32_to_cpu(u32_prop[1]);
+
+ /* Get the MURAM base address and size */
+ muram_node = of_find_matching_node(fm_node, fman_muram_match);
+ if (!muram_node) {
+ dev_err(&of_dev->dev, "%s: could not find MURAM node\n",
+ __func__);
+ goto fman_node_put;
+ }
+
+ err = of_address_to_resource(muram_node, 0,
+ &fman->dts_params.muram_res);
+ if (err) {
+ of_node_put(muram_node);
+ dev_err(&of_dev->dev, "%s: of_address_to_resource() = %d\n",
+ __func__, err);
+ goto fman_node_put;
+ }
+
+ of_node_put(muram_node);
+ of_node_put(fm_node);
+
+ err = devm_request_irq(&of_dev->dev, irq, fman_irq, 0, "fman", fman);
+ if (err < 0) {
+ dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n",
+ __func__, irq, err);
+ goto fman_free;
+ }
+
+ if (fman->dts_params.err_irq != 0) {
+ err = devm_request_irq(&of_dev->dev, fman->dts_params.err_irq,
+ fman_err_irq, IRQF_SHARED,
+ "fman-err", fman);
+ if (err < 0) {
+ dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n",
+ __func__, fman->dts_params.err_irq, err);
+ goto fman_free;
+ }
+ }
+
+ fman->dts_params.res =
+ devm_request_mem_region(&of_dev->dev, phys_base_addr,
+ mem_size, "fman");
+ if (!fman->dts_params.res) {
+ dev_err(&of_dev->dev, "%s: request_mem_region() failed\n",
+ __func__);
+ goto fman_free;
+ }
+
+ fman->dts_params.base_addr =
+ devm_ioremap(&of_dev->dev, phys_base_addr, mem_size);
+ if (fman->dts_params.base_addr == 0) {
+ dev_err(&of_dev->dev, "%s: devm_ioremap() failed\n", __func__);
+ goto fman_free;
+ }
+
+ return fman;
+
+fman_node_put:
+ of_node_put(fm_node);
+fman_free:
+ kfree(fman);
+ return NULL;
+}
+
+static int fman_probe(struct platform_device *of_dev)
+{
+ struct fman *fman;
+ struct device *dev;
+ int err;
+
+ dev = &of_dev->dev;
+
+ fman = read_dts_node(of_dev);
+ if (!fman)
+ return -EIO;
+
+ err = fman_config(fman);
+ if (err) {
+ dev_err(dev, "%s: FMan config failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (fman_init(fman) != 0) {
+ dev_err(dev, "%s: FMan init failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (fman->dts_params.err_irq == 0) {
+ fman_set_exception(fman, FMAN_EX_DMA_BUS_ERROR, false);
+ fman_set_exception(fman, FMAN_EX_DMA_READ_ECC, false);
+ fman_set_exception(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_DMA_FM_WRITE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_DMA_SINGLE_PORT_ECC, false);
+ fman_set_exception(fman, FMAN_EX_FPM_STALL_ON_TASKS, false);
+ fman_set_exception(fman, FMAN_EX_FPM_SINGLE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_FPM_DOUBLE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_QMI_SINGLE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_QMI_DOUBLE_ECC, false);
+ fman_set_exception(fman,
+ FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID, false);
+ fman_set_exception(fman, FMAN_EX_BMI_LIST_RAM_ECC, false);
+ fman_set_exception(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC,
+ false);
+ fman_set_exception(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC, false);
+ fman_set_exception(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC, false);
+ }
+
+ dev_set_drvdata(dev, fman);
+
+ fman->dev = dev;
+
+ dev_dbg(dev, "FMan%d probed\n", fman->dts_params.id);
+
+ return 0;
+}
+
+static const struct of_device_id fman_match[] = {
+ {
+ .compatible = "fsl,fman"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, fm_match);
+
+static struct platform_driver fman_driver = {
+ .driver = {
+ .name = "fsl-fman",
+ .of_match_table = fman_match,
+ },
+ .probe = fman_probe,
+};
+
+builtin_platform_driver(fman_driver);
diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h
new file mode 100644
index 000000000000..57aae8d17d77
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman.h
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FM_H
+#define __FM_H
+
+#include <linux/io.h>
+
+/* FM Frame descriptor macros */
+/* Frame queue Context Override */
+#define FM_FD_CMD_FCO 0x80000000
+#define FM_FD_CMD_RPD 0x40000000 /* Read Prepended Data */
+#define FM_FD_CMD_DTC 0x10000000 /* Do L4 Checksum */
+
+/* TX-Port: Unsupported Format */
+#define FM_FD_ERR_UNSUPPORTED_FORMAT 0x04000000
+/* TX Port: Length Error */
+#define FM_FD_ERR_LENGTH 0x02000000
+#define FM_FD_ERR_DMA 0x01000000 /* DMA Data error */
+
+/* IPR frame (not error) */
+#define FM_FD_IPR 0x00000001
+/* IPR non-consistent-sp */
+#define FM_FD_ERR_IPR_NCSP (0x00100000 | FM_FD_IPR)
+/* IPR error */
+#define FM_FD_ERR_IPR (0x00200000 | FM_FD_IPR)
+/* IPR timeout */
+#define FM_FD_ERR_IPR_TO (0x00300000 | FM_FD_IPR)
+/* TX Port: Length Error */
+#define FM_FD_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR)
+
+/* Rx FIFO overflow, FCS error, code error, running disparity error
+ * (SGMII and TBI modes), FIFO parity error. PHY Sequence error,
+ * PHY error control character detected.
+ */
+#define FM_FD_ERR_PHYSICAL 0x00080000
+/* Frame too long OR Frame size exceeds max_length_frame */
+#define FM_FD_ERR_SIZE 0x00040000
+/* classification discard */
+#define FM_FD_ERR_CLS_DISCARD 0x00020000
+/* Extract Out of Frame */
+#define FM_FD_ERR_EXTRACTION 0x00008000
+/* No Scheme Selected */
+#define FM_FD_ERR_NO_SCHEME 0x00004000
+/* Keysize Overflow */
+#define FM_FD_ERR_KEYSIZE_OVERFLOW 0x00002000
+/* Frame color is red */
+#define FM_FD_ERR_COLOR_RED 0x00000800
+/* Frame color is yellow */
+#define FM_FD_ERR_COLOR_YELLOW 0x00000400
+/* Parser Time out Exceed */
+#define FM_FD_ERR_PRS_TIMEOUT 0x00000080
+/* Invalid Soft Parser instruction */
+#define FM_FD_ERR_PRS_ILL_INSTRUCT 0x00000040
+/* Header error was identified during parsing */
+#define FM_FD_ERR_PRS_HDR_ERR 0x00000020
+/* Frame parsed beyind 256 first bytes */
+#define FM_FD_ERR_BLOCK_LIMIT_EXCEEDED 0x00000008
+
+/* non Frame-Manager error */
+#define FM_FD_RX_STATUS_ERR_NON_FM 0x00400000
+
+/* FMan driver defines */
+#define FMAN_BMI_FIFO_UNITS 0x100
+#define OFFSET_UNITS 16
+
+/* BMan defines */
+#define BM_MAX_NUM_OF_POOLS 64 /* Buffers pools */
+#define FMAN_PORT_MAX_EXT_POOLS_NUM 8 /* External BM pools per Rx port */
+
+struct fman; /* FMan data */
+
+/* Enum for defining port types */
+enum fman_port_type {
+ FMAN_PORT_TYPE_TX = 0, /* TX Port */
+ FMAN_PORT_TYPE_RX, /* RX Port */
+};
+
+struct fman_rev_info {
+ u8 major; /* Major revision */
+ u8 minor; /* Minor revision */
+};
+
+enum fman_exceptions {
+ FMAN_EX_DMA_BUS_ERROR = 0, /* DMA bus error. */
+ FMAN_EX_DMA_READ_ECC, /* Read Buffer ECC error */
+ FMAN_EX_DMA_SYSTEM_WRITE_ECC, /* Write Buffer ECC err on sys side */
+ FMAN_EX_DMA_FM_WRITE_ECC, /* Write Buffer ECC error on FM side */
+ FMAN_EX_DMA_SINGLE_PORT_ECC, /* Single Port ECC error on FM side */
+ FMAN_EX_FPM_STALL_ON_TASKS, /* Stall of tasks on FPM */
+ FMAN_EX_FPM_SINGLE_ECC, /* Single ECC on FPM. */
+ FMAN_EX_FPM_DOUBLE_ECC, /* Double ECC error on FPM ram access */
+ FMAN_EX_QMI_SINGLE_ECC, /* Single ECC on QMI. */
+ FMAN_EX_QMI_DOUBLE_ECC, /* Double bit ECC occurred on QMI */
+ FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID,/* DeQ from unknown port id */
+ FMAN_EX_BMI_LIST_RAM_ECC, /* Linked List RAM ECC error */
+ FMAN_EX_BMI_STORAGE_PROFILE_ECC,/* storage profile */
+ FMAN_EX_BMI_STATISTICS_RAM_ECC,/* Statistics RAM ECC Err Enable */
+ FMAN_EX_BMI_DISPATCH_RAM_ECC, /* Dispatch RAM ECC Error Enable */
+ FMAN_EX_IRAM_ECC, /* Double bit ECC occurred on IRAM */
+ FMAN_EX_MURAM_ECC /* Double bit ECC occurred on MURAM */
+};
+
+/* Parse results memory layout */
+struct fman_prs_result {
+ u8 lpid; /* Logical port id */
+ u8 shimr; /* Shim header result */
+ u16 l2r; /* Layer 2 result */
+ u16 l3r; /* Layer 3 result */
+ u8 l4r; /* Layer 4 result */
+ u8 cplan; /* Classification plan id */
+ u16 nxthdr; /* Next Header */
+ u16 cksum; /* Running-sum */
+ /* Flags&fragment-offset field of the last IP-header */
+ u16 flags_frag_off;
+ /* Routing type field of a IPV6 routing extension header */
+ u8 route_type;
+ /* Routing Extension Header Present; last bit is IP valid */
+ u8 rhp_ip_valid;
+ u8 shim_off[2]; /* Shim offset */
+ u8 ip_pid_off; /* IP PID (last IP-proto) offset */
+ u8 eth_off; /* ETH offset */
+ u8 llc_snap_off; /* LLC_SNAP offset */
+ u8 vlan_off[2]; /* VLAN offset */
+ u8 etype_off; /* ETYPE offset */
+ u8 pppoe_off; /* PPP offset */
+ u8 mpls_off[2]; /* MPLS offset */
+ u8 ip_off[2]; /* IP offset */
+ u8 gre_off; /* GRE offset */
+ u8 l4_off; /* Layer 4 offset */
+ u8 nxthdr_off; /* Parser end point */
+};
+
+/* A structure for defining buffer prefix area content. */
+struct fman_buffer_prefix_content {
+ /* Number of bytes to be left at the beginning of the external
+ * buffer; Note that the private-area will start from the base
+ * of the buffer address.
+ */
+ u16 priv_data_size;
+ /* true to pass the parse result to/from the FM;
+ * User may use FM_PORT_GetBufferPrsResult() in
+ * order to get the parser-result from a buffer.
+ */
+ bool pass_prs_result;
+ /* true to pass the timeStamp to/from the FM User */
+ bool pass_time_stamp;
+ /* true to pass the KG hash result to/from the FM User may
+ * use FM_PORT_GetBufferHashResult() in order to get the
+ * parser-result from a buffer.
+ */
+ bool pass_hash_result;
+ /* Add all other Internal-Context information: AD,
+ * hash-result, key, etc.
+ */
+ u16 data_align;
+};
+
+/* A structure of information about each of the external
+ * buffer pools used by a port or storage-profile.
+ */
+struct fman_ext_pool_params {
+ u8 id; /* External buffer pool id */
+ u16 size; /* External buffer pool buffer size */
+};
+
+/* A structure for informing the driver about the external
+ * buffer pools allocated in the BM and used by a port or a
+ * storage-profile.
+ */
+struct fman_ext_pools {
+ u8 num_of_pools_used; /* Number of pools use by this port */
+ struct fman_ext_pool_params ext_buf_pool[FMAN_PORT_MAX_EXT_POOLS_NUM];
+ /* Parameters for each port */
+};
+
+/* A structure for defining BM pool depletion criteria */
+struct fman_buf_pool_depletion {
+ /* select mode in which pause frames will be sent after a
+ * number of pools (all together!) are depleted
+ */
+ bool pools_grp_mode_enable;
+ /* the number of depleted pools that will invoke pause
+ * frames transmission.
+ */
+ u8 num_of_pools;
+ /* For each pool, true if it should be considered for
+ * depletion (Note - this pool must be used by this port!).
+ */
+ bool pools_to_consider[BM_MAX_NUM_OF_POOLS];
+ /* select mode in which pause frames will be sent
+ * after a single-pool is depleted;
+ */
+ bool single_pool_mode_enable;
+ /* For each pool, true if it should be considered
+ * for depletion (Note - this pool must be used by this port!)
+ */
+ bool pools_to_consider_for_single_mode[BM_MAX_NUM_OF_POOLS];
+};
+
+/* Enum for inter-module interrupts registration */
+enum fman_event_modules {
+ FMAN_MOD_MAC = 0, /* MAC event */
+ FMAN_MOD_FMAN_CTRL, /* FMAN Controller */
+ FMAN_MOD_DUMMY_LAST
+};
+
+/* Enum for interrupts types */
+enum fman_intr_type {
+ FMAN_INTR_TYPE_ERR,
+ FMAN_INTR_TYPE_NORMAL
+};
+
+/* Enum for inter-module interrupts registration */
+enum fman_inter_module_event {
+ FMAN_EV_ERR_MAC0 = 0, /* MAC 0 error event */
+ FMAN_EV_ERR_MAC1, /* MAC 1 error event */
+ FMAN_EV_ERR_MAC2, /* MAC 2 error event */
+ FMAN_EV_ERR_MAC3, /* MAC 3 error event */
+ FMAN_EV_ERR_MAC4, /* MAC 4 error event */
+ FMAN_EV_ERR_MAC5, /* MAC 5 error event */
+ FMAN_EV_ERR_MAC6, /* MAC 6 error event */
+ FMAN_EV_ERR_MAC7, /* MAC 7 error event */
+ FMAN_EV_ERR_MAC8, /* MAC 8 error event */
+ FMAN_EV_ERR_MAC9, /* MAC 9 error event */
+ FMAN_EV_MAC0, /* MAC 0 event (Magic packet detection) */
+ FMAN_EV_MAC1, /* MAC 1 event (Magic packet detection) */
+ FMAN_EV_MAC2, /* MAC 2 (Magic packet detection) */
+ FMAN_EV_MAC3, /* MAC 3 (Magic packet detection) */
+ FMAN_EV_MAC4, /* MAC 4 (Magic packet detection) */
+ FMAN_EV_MAC5, /* MAC 5 (Magic packet detection) */
+ FMAN_EV_MAC6, /* MAC 6 (Magic packet detection) */
+ FMAN_EV_MAC7, /* MAC 7 (Magic packet detection) */
+ FMAN_EV_MAC8, /* MAC 8 event (Magic packet detection) */
+ FMAN_EV_MAC9, /* MAC 9 event (Magic packet detection) */
+ FMAN_EV_FMAN_CTRL_0, /* Fman controller event 0 */
+ FMAN_EV_FMAN_CTRL_1, /* Fman controller event 1 */
+ FMAN_EV_FMAN_CTRL_2, /* Fman controller event 2 */
+ FMAN_EV_FMAN_CTRL_3, /* Fman controller event 3 */
+ FMAN_EV_CNT
+};
+
+struct fman_intr_src {
+ void (*isr_cb)(void *src_arg);
+ void *src_handle;
+};
+
+/* Structure for port-FM communication during fman_port_init. */
+struct fman_port_init_params {
+ u8 port_id; /* port Id */
+ enum fman_port_type port_type; /* Port type */
+ u16 port_speed; /* Port speed */
+ u16 liodn_offset; /* Port's requested resource */
+ u8 num_of_tasks; /* Port's requested resource */
+ u8 num_of_extra_tasks; /* Port's requested resource */
+ u8 num_of_open_dmas; /* Port's requested resource */
+ u8 num_of_extra_open_dmas; /* Port's requested resource */
+ u32 size_of_fifo; /* Port's requested resource */
+ u32 extra_size_of_fifo; /* Port's requested resource */
+ u8 deq_pipeline_depth; /* Port's requested resource */
+ u16 max_frame_length; /* Port's max frame length. */
+ u16 liodn_base;
+ /* LIODN base for this port, to be used together with LIODN offset. */
+};
+
+void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info);
+
+void fman_register_intr(struct fman *fman, enum fman_event_modules mod,
+ u8 mod_id, enum fman_intr_type intr_type,
+ void (*f_isr)(void *h_src_arg), void *h_src_arg);
+
+void fman_unregister_intr(struct fman *fman, enum fman_event_modules mod,
+ u8 mod_id, enum fman_intr_type intr_type);
+
+int fman_set_port_params(struct fman *fman,
+ struct fman_port_init_params *port_params);
+
+int fman_reset_mac(struct fman *fman, u8 mac_id);
+
+u16 fman_get_clock_freq(struct fman *fman);
+
+u32 fman_get_bmi_max_fifo_size(struct fman *fman);
+
+int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl);
+
+u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id);
+
+struct resource *fman_get_mem_region(struct fman *fman);
+
+u16 fman_get_max_frm(void);
+
+int fman_get_rx_extra_headroom(void);
+
+struct fman *fman_bind(struct device *dev);
+
+#endif /* __FM_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
new file mode 100644
index 000000000000..6b1261c0b1c2
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
@@ -0,0 +1,1453 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_dtsec.h"
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/crc32.h>
+#include <linux/of_mdio.h>
+#include <linux/mii.h>
+
+/* TBI register addresses */
+#define MII_TBICON 0x11
+
+/* TBICON register bit fields */
+#define TBICON_SOFT_RESET 0x8000 /* Soft reset */
+#define TBICON_DISABLE_RX_DIS 0x2000 /* Disable receive disparity */
+#define TBICON_DISABLE_TX_DIS 0x1000 /* Disable transmit disparity */
+#define TBICON_AN_SENSE 0x0100 /* Auto-negotiation sense enable */
+#define TBICON_CLK_SELECT 0x0020 /* Clock select */
+#define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */
+
+#define TBIANA_SGMII 0x4001
+#define TBIANA_1000X 0x01a0
+
+/* Interrupt Mask Register (IMASK) */
+#define DTSEC_IMASK_BREN 0x80000000
+#define DTSEC_IMASK_RXCEN 0x40000000
+#define DTSEC_IMASK_MSROEN 0x04000000
+#define DTSEC_IMASK_GTSCEN 0x02000000
+#define DTSEC_IMASK_BTEN 0x01000000
+#define DTSEC_IMASK_TXCEN 0x00800000
+#define DTSEC_IMASK_TXEEN 0x00400000
+#define DTSEC_IMASK_LCEN 0x00040000
+#define DTSEC_IMASK_CRLEN 0x00020000
+#define DTSEC_IMASK_XFUNEN 0x00010000
+#define DTSEC_IMASK_ABRTEN 0x00008000
+#define DTSEC_IMASK_IFERREN 0x00004000
+#define DTSEC_IMASK_MAGEN 0x00000800
+#define DTSEC_IMASK_MMRDEN 0x00000400
+#define DTSEC_IMASK_MMWREN 0x00000200
+#define DTSEC_IMASK_GRSCEN 0x00000100
+#define DTSEC_IMASK_TDPEEN 0x00000002
+#define DTSEC_IMASK_RDPEEN 0x00000001
+
+#define DTSEC_EVENTS_MASK \
+ ((u32)(DTSEC_IMASK_BREN | \
+ DTSEC_IMASK_RXCEN | \
+ DTSEC_IMASK_BTEN | \
+ DTSEC_IMASK_TXCEN | \
+ DTSEC_IMASK_TXEEN | \
+ DTSEC_IMASK_ABRTEN | \
+ DTSEC_IMASK_LCEN | \
+ DTSEC_IMASK_CRLEN | \
+ DTSEC_IMASK_XFUNEN | \
+ DTSEC_IMASK_IFERREN | \
+ DTSEC_IMASK_MAGEN | \
+ DTSEC_IMASK_TDPEEN | \
+ DTSEC_IMASK_RDPEEN))
+
+/* dtsec timestamp event bits */
+#define TMR_PEMASK_TSREEN 0x00010000
+#define TMR_PEVENT_TSRE 0x00010000
+
+/* Group address bit indication */
+#define MAC_GROUP_ADDRESS 0x0000010000000000ULL
+
+/* Defaults */
+#define DEFAULT_HALFDUP_RETRANSMIT 0xf
+#define DEFAULT_HALFDUP_COLL_WINDOW 0x37
+#define DEFAULT_TX_PAUSE_TIME 0xf000
+#define DEFAULT_RX_PREPEND 0
+#define DEFAULT_PREAMBLE_LEN 7
+#define DEFAULT_TX_PAUSE_TIME_EXTD 0
+#define DEFAULT_NON_BACK_TO_BACK_IPG1 0x40
+#define DEFAULT_NON_BACK_TO_BACK_IPG2 0x60
+#define DEFAULT_MIN_IFG_ENFORCEMENT 0x50
+#define DEFAULT_BACK_TO_BACK_IPG 0x60
+#define DEFAULT_MAXIMUM_FRAME 0x600
+
+/* register related defines (bits, field offsets..) */
+#define DTSEC_ID2_INT_REDUCED_OFF 0x00010000
+
+#define DTSEC_ECNTRL_GMIIM 0x00000040
+#define DTSEC_ECNTRL_TBIM 0x00000020
+#define DTSEC_ECNTRL_SGMIIM 0x00000002
+#define DTSEC_ECNTRL_RPM 0x00000010
+#define DTSEC_ECNTRL_R100M 0x00000008
+#define DTSEC_ECNTRL_QSGMIIM 0x00000001
+
+#define DTSEC_TCTRL_GTS 0x00000020
+
+#define RCTRL_PAL_MASK 0x001f0000
+#define RCTRL_PAL_SHIFT 16
+#define RCTRL_GHTX 0x00000400
+#define RCTRL_GRS 0x00000020
+#define RCTRL_MPROM 0x00000008
+#define RCTRL_RSF 0x00000004
+#define RCTRL_UPROM 0x00000001
+
+#define MACCFG1_SOFT_RESET 0x80000000
+#define MACCFG1_RX_FLOW 0x00000020
+#define MACCFG1_TX_FLOW 0x00000010
+#define MACCFG1_TX_EN 0x00000001
+#define MACCFG1_RX_EN 0x00000004
+
+#define MACCFG2_NIBBLE_MODE 0x00000100
+#define MACCFG2_BYTE_MODE 0x00000200
+#define MACCFG2_PAD_CRC_EN 0x00000004
+#define MACCFG2_FULL_DUPLEX 0x00000001
+#define MACCFG2_PREAMBLE_LENGTH_MASK 0x0000f000
+#define MACCFG2_PREAMBLE_LENGTH_SHIFT 12
+
+#define IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT 24
+#define IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT 16
+#define IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT 8
+
+#define IPGIFG_NON_BACK_TO_BACK_IPG_1 0x7F000000
+#define IPGIFG_NON_BACK_TO_BACK_IPG_2 0x007F0000
+#define IPGIFG_MIN_IFG_ENFORCEMENT 0x0000FF00
+#define IPGIFG_BACK_TO_BACK_IPG 0x0000007F
+
+#define HAFDUP_EXCESS_DEFER 0x00010000
+#define HAFDUP_COLLISION_WINDOW 0x000003ff
+#define HAFDUP_RETRANSMISSION_MAX_SHIFT 12
+#define HAFDUP_RETRANSMISSION_MAX 0x0000f000
+
+#define NUM_OF_HASH_REGS 8 /* Number of hash table registers */
+
+#define PTV_PTE_MASK 0xffff0000
+#define PTV_PT_MASK 0x0000ffff
+#define PTV_PTE_SHIFT 16
+
+#define MAX_PACKET_ALIGNMENT 31
+#define MAX_INTER_PACKET_GAP 0x7f
+#define MAX_RETRANSMISSION 0x0f
+#define MAX_COLLISION_WINDOW 0x03ff
+
+/* Hash table size (32 bits*8 regs) */
+#define DTSEC_HASH_TABLE_SIZE 256
+/* Extended Hash table size (32 bits*16 regs) */
+#define EXTENDED_HASH_TABLE_SIZE 512
+
+/* dTSEC Memory Map registers */
+struct dtsec_regs {
+ /* dTSEC General Control and Status Registers */
+ u32 tsec_id; /* 0x000 ETSEC_ID register */
+ u32 tsec_id2; /* 0x004 ETSEC_ID2 register */
+ u32 ievent; /* 0x008 Interrupt event register */
+ u32 imask; /* 0x00C Interrupt mask register */
+ u32 reserved0010[1];
+ u32 ecntrl; /* 0x014 E control register */
+ u32 ptv; /* 0x018 Pause time value register */
+ u32 tbipa; /* 0x01C TBI PHY address register */
+ u32 tmr_ctrl; /* 0x020 Time-stamp Control register */
+ u32 tmr_pevent; /* 0x024 Time-stamp event register */
+ u32 tmr_pemask; /* 0x028 Timer event mask register */
+ u32 reserved002c[5];
+ u32 tctrl; /* 0x040 Transmit control register */
+ u32 reserved0044[3];
+ u32 rctrl; /* 0x050 Receive control register */
+ u32 reserved0054[11];
+ u32 igaddr[8]; /* 0x080-0x09C Individual/group address */
+ u32 gaddr[8]; /* 0x0A0-0x0BC Group address registers 0-7 */
+ u32 reserved00c0[16];
+ u32 maccfg1; /* 0x100 MAC configuration #1 */
+ u32 maccfg2; /* 0x104 MAC configuration #2 */
+ u32 ipgifg; /* 0x108 IPG/IFG */
+ u32 hafdup; /* 0x10C Half-duplex */
+ u32 maxfrm; /* 0x110 Maximum frame */
+ u32 reserved0114[10];
+ u32 ifstat; /* 0x13C Interface status */
+ u32 macstnaddr1; /* 0x140 Station Address,part 1 */
+ u32 macstnaddr2; /* 0x144 Station Address,part 2 */
+ struct {
+ u32 exact_match1; /* octets 1-4 */
+ u32 exact_match2; /* octets 5-6 */
+ } macaddr[15]; /* 0x148-0x1BC mac exact match addresses 1-15 */
+ u32 reserved01c0[16];
+ u32 tr64; /* 0x200 Tx and Rx 64 byte frame counter */
+ u32 tr127; /* 0x204 Tx and Rx 65 to 127 byte frame counter */
+ u32 tr255; /* 0x208 Tx and Rx 128 to 255 byte frame counter */
+ u32 tr511; /* 0x20C Tx and Rx 256 to 511 byte frame counter */
+ u32 tr1k; /* 0x210 Tx and Rx 512 to 1023 byte frame counter */
+ u32 trmax; /* 0x214 Tx and Rx 1024 to 1518 byte frame counter */
+ u32 trmgv;
+ /* 0x218 Tx and Rx 1519 to 1522 byte good VLAN frame count */
+ u32 rbyt; /* 0x21C receive byte counter */
+ u32 rpkt; /* 0x220 receive packet counter */
+ u32 rfcs; /* 0x224 receive FCS error counter */
+ u32 rmca; /* 0x228 RMCA Rx multicast packet counter */
+ u32 rbca; /* 0x22C Rx broadcast packet counter */
+ u32 rxcf; /* 0x230 Rx control frame packet counter */
+ u32 rxpf; /* 0x234 Rx pause frame packet counter */
+ u32 rxuo; /* 0x238 Rx unknown OP code counter */
+ u32 raln; /* 0x23C Rx alignment error counter */
+ u32 rflr; /* 0x240 Rx frame length error counter */
+ u32 rcde; /* 0x244 Rx code error counter */
+ u32 rcse; /* 0x248 Rx carrier sense error counter */
+ u32 rund; /* 0x24C Rx undersize packet counter */
+ u32 rovr; /* 0x250 Rx oversize packet counter */
+ u32 rfrg; /* 0x254 Rx fragments counter */
+ u32 rjbr; /* 0x258 Rx jabber counter */
+ u32 rdrp; /* 0x25C Rx drop */
+ u32 tbyt; /* 0x260 Tx byte counter */
+ u32 tpkt; /* 0x264 Tx packet counter */
+ u32 tmca; /* 0x268 Tx multicast packet counter */
+ u32 tbca; /* 0x26C Tx broadcast packet counter */
+ u32 txpf; /* 0x270 Tx pause control frame counter */
+ u32 tdfr; /* 0x274 Tx deferral packet counter */
+ u32 tedf; /* 0x278 Tx excessive deferral packet counter */
+ u32 tscl; /* 0x27C Tx single collision packet counter */
+ u32 tmcl; /* 0x280 Tx multiple collision packet counter */
+ u32 tlcl; /* 0x284 Tx late collision packet counter */
+ u32 txcl; /* 0x288 Tx excessive collision packet counter */
+ u32 tncl; /* 0x28C Tx total collision counter */
+ u32 reserved0290[1];
+ u32 tdrp; /* 0x294 Tx drop frame counter */
+ u32 tjbr; /* 0x298 Tx jabber frame counter */
+ u32 tfcs; /* 0x29C Tx FCS error counter */
+ u32 txcf; /* 0x2A0 Tx control frame counter */
+ u32 tovr; /* 0x2A4 Tx oversize frame counter */
+ u32 tund; /* 0x2A8 Tx undersize frame counter */
+ u32 tfrg; /* 0x2AC Tx fragments frame counter */
+ u32 car1; /* 0x2B0 carry register one register* */
+ u32 car2; /* 0x2B4 carry register two register* */
+ u32 cam1; /* 0x2B8 carry register one mask register */
+ u32 cam2; /* 0x2BC carry register two mask register */
+ u32 reserved02c0[848];
+};
+
+/* struct dtsec_cfg - dTSEC configuration
+ * Transmit half-duplex flow control, under software control for 10/100-Mbps
+ * half-duplex media. If set, back pressure is applied to media by raising
+ * carrier.
+ * halfdup_retransmit:
+ * Number of retransmission attempts following a collision.
+ * If this is exceeded dTSEC aborts transmission due to excessive collisions.
+ * The standard specifies the attempt limit to be 15.
+ * halfdup_coll_window:
+ * The number of bytes of the frame during which collisions may occur.
+ * The default value of 55 corresponds to the frame byte at the end of the
+ * standard 512-bit slot time window. If collisions are detected after this
+ * byte, the late collision event is asserted and transmission of current
+ * frame is aborted.
+ * tx_pad_crc:
+ * Pad and append CRC. If set, the MAC pads all ransmitted short frames and
+ * appends a CRC to every frame regardless of padding requirement.
+ * tx_pause_time:
+ * Transmit pause time value. This pause value is used as part of the pause
+ * frame to be sent when a transmit pause frame is initiated.
+ * If set to 0 this disables transmission of pause frames.
+ * preamble_len:
+ * Length, in bytes, of the preamble field preceding each Ethernet
+ * start-of-frame delimiter byte. The default value of 0x7 should be used in
+ * order to guarantee reliable operation with IEEE 802.3 compliant hardware.
+ * rx_prepend:
+ * Packet alignment padding length. The specified number of bytes (1-31)
+ * of zero padding are inserted before the start of each received frame.
+ * For Ethernet, where optional preamble extraction is enabled, the padding
+ * appears before the preamble, otherwise the padding precedes the
+ * layer 2 header.
+ *
+ * This structure contains basic dTSEC configuration and must be passed to
+ * init() function. A default set of configuration values can be
+ * obtained by calling set_dflts().
+ */
+struct dtsec_cfg {
+ u16 halfdup_retransmit;
+ u16 halfdup_coll_window;
+ bool tx_pad_crc;
+ u16 tx_pause_time;
+ bool ptp_tsu_en;
+ bool ptp_exception_en;
+ u32 preamble_len;
+ u32 rx_prepend;
+ u16 tx_pause_time_extd;
+ u16 maximum_frame;
+ u32 non_back_to_back_ipg1;
+ u32 non_back_to_back_ipg2;
+ u32 min_ifg_enforcement;
+ u32 back_to_back_ipg;
+};
+
+struct fman_mac {
+ /* pointer to dTSEC memory mapped registers */
+ struct dtsec_regs __iomem *regs;
+ /* MAC address of device */
+ u64 addr;
+ /* Ethernet physical interface */
+ phy_interface_t phy_if;
+ u16 max_speed;
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *exception_cb;
+ fman_mac_exception_cb *event_cb;
+ /* Number of individual addresses in registers for this station */
+ u8 num_of_ind_addr_in_regs;
+ /* pointer to driver's global address hash table */
+ struct eth_hash_t *multicast_addr_hash;
+ /* pointer to driver's individual address hash table */
+ struct eth_hash_t *unicast_addr_hash;
+ u8 mac_id;
+ u32 exceptions;
+ bool ptp_tsu_enabled;
+ bool en_tsu_err_exeption;
+ struct dtsec_cfg *dtsec_drv_param;
+ void *fm;
+ struct fman_rev_info fm_rev_info;
+ bool basex_if;
+ struct phy_device *tbiphy;
+};
+
+static void set_dflts(struct dtsec_cfg *cfg)
+{
+ cfg->halfdup_retransmit = DEFAULT_HALFDUP_RETRANSMIT;
+ cfg->halfdup_coll_window = DEFAULT_HALFDUP_COLL_WINDOW;
+ cfg->tx_pad_crc = true;
+ cfg->tx_pause_time = DEFAULT_TX_PAUSE_TIME;
+ /* PHY address 0 is reserved (DPAA RM) */
+ cfg->rx_prepend = DEFAULT_RX_PREPEND;
+ cfg->ptp_tsu_en = true;
+ cfg->ptp_exception_en = true;
+ cfg->preamble_len = DEFAULT_PREAMBLE_LEN;
+ cfg->tx_pause_time_extd = DEFAULT_TX_PAUSE_TIME_EXTD;
+ cfg->non_back_to_back_ipg1 = DEFAULT_NON_BACK_TO_BACK_IPG1;
+ cfg->non_back_to_back_ipg2 = DEFAULT_NON_BACK_TO_BACK_IPG2;
+ cfg->min_ifg_enforcement = DEFAULT_MIN_IFG_ENFORCEMENT;
+ cfg->back_to_back_ipg = DEFAULT_BACK_TO_BACK_IPG;
+ cfg->maximum_frame = DEFAULT_MAXIMUM_FRAME;
+}
+
+static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg,
+ phy_interface_t iface, u16 iface_speed, u8 *macaddr,
+ u32 exception_mask, u8 tbi_addr)
+{
+ bool is_rgmii, is_sgmii, is_qsgmii;
+ int i;
+ u32 tmp;
+
+ /* Soft reset */
+ iowrite32be(MACCFG1_SOFT_RESET, &regs->maccfg1);
+ iowrite32be(0, &regs->maccfg1);
+
+ /* dtsec_id2 */
+ tmp = ioread32be(&regs->tsec_id2);
+
+ /* check RGMII support */
+ if (iface == PHY_INTERFACE_MODE_RGMII ||
+ iface == PHY_INTERFACE_MODE_RMII)
+ if (tmp & DTSEC_ID2_INT_REDUCED_OFF)
+ return -EINVAL;
+
+ if (iface == PHY_INTERFACE_MODE_SGMII ||
+ iface == PHY_INTERFACE_MODE_MII)
+ if (tmp & DTSEC_ID2_INT_REDUCED_OFF)
+ return -EINVAL;
+
+ is_rgmii = iface == PHY_INTERFACE_MODE_RGMII;
+ is_sgmii = iface == PHY_INTERFACE_MODE_SGMII;
+ is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII;
+
+ tmp = 0;
+ if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII)
+ tmp |= DTSEC_ECNTRL_GMIIM;
+ if (is_sgmii)
+ tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM);
+ if (is_qsgmii)
+ tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM |
+ DTSEC_ECNTRL_QSGMIIM);
+ if (is_rgmii)
+ tmp |= DTSEC_ECNTRL_RPM;
+ if (iface_speed == SPEED_100)
+ tmp |= DTSEC_ECNTRL_R100M;
+
+ iowrite32be(tmp, &regs->ecntrl);
+
+ tmp = 0;
+
+ if (cfg->tx_pause_time)
+ tmp |= cfg->tx_pause_time;
+ if (cfg->tx_pause_time_extd)
+ tmp |= cfg->tx_pause_time_extd << PTV_PTE_SHIFT;
+ iowrite32be(tmp, &regs->ptv);
+
+ tmp = 0;
+ tmp |= (cfg->rx_prepend << RCTRL_PAL_SHIFT) & RCTRL_PAL_MASK;
+ /* Accept short frames */
+ tmp |= RCTRL_RSF;
+
+ iowrite32be(tmp, &regs->rctrl);
+
+ /* Assign a Phy Address to the TBI (TBIPA).
+ * Done also in cases where TBI is not selected to avoid conflict with
+ * the external PHY's Physical address
+ */
+ iowrite32be(tbi_addr, &regs->tbipa);
+
+ iowrite32be(0, &regs->tmr_ctrl);
+
+ if (cfg->ptp_tsu_en) {
+ tmp = 0;
+ tmp |= TMR_PEVENT_TSRE;
+ iowrite32be(tmp, &regs->tmr_pevent);
+
+ if (cfg->ptp_exception_en) {
+ tmp = 0;
+ tmp |= TMR_PEMASK_TSREEN;
+ iowrite32be(tmp, &regs->tmr_pemask);
+ }
+ }
+
+ tmp = 0;
+ tmp |= MACCFG1_RX_FLOW;
+ tmp |= MACCFG1_TX_FLOW;
+ iowrite32be(tmp, &regs->maccfg1);
+
+ tmp = 0;
+
+ if (iface_speed < SPEED_1000)
+ tmp |= MACCFG2_NIBBLE_MODE;
+ else if (iface_speed == SPEED_1000)
+ tmp |= MACCFG2_BYTE_MODE;
+
+ tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) &
+ MACCFG2_PREAMBLE_LENGTH_MASK;
+ if (cfg->tx_pad_crc)
+ tmp |= MACCFG2_PAD_CRC_EN;
+ /* Full Duplex */
+ tmp |= MACCFG2_FULL_DUPLEX;
+ iowrite32be(tmp, &regs->maccfg2);
+
+ tmp = (((cfg->non_back_to_back_ipg1 <<
+ IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT)
+ & IPGIFG_NON_BACK_TO_BACK_IPG_1)
+ | ((cfg->non_back_to_back_ipg2 <<
+ IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT)
+ & IPGIFG_NON_BACK_TO_BACK_IPG_2)
+ | ((cfg->min_ifg_enforcement << IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT)
+ & IPGIFG_MIN_IFG_ENFORCEMENT)
+ | (cfg->back_to_back_ipg & IPGIFG_BACK_TO_BACK_IPG));
+ iowrite32be(tmp, &regs->ipgifg);
+
+ tmp = 0;
+ tmp |= HAFDUP_EXCESS_DEFER;
+ tmp |= ((cfg->halfdup_retransmit << HAFDUP_RETRANSMISSION_MAX_SHIFT)
+ & HAFDUP_RETRANSMISSION_MAX);
+ tmp |= (cfg->halfdup_coll_window & HAFDUP_COLLISION_WINDOW);
+
+ iowrite32be(tmp, &regs->hafdup);
+
+ /* Initialize Maximum frame length */
+ iowrite32be(cfg->maximum_frame, &regs->maxfrm);
+
+ iowrite32be(0xffffffff, &regs->cam1);
+ iowrite32be(0xffffffff, &regs->cam2);
+
+ iowrite32be(exception_mask, &regs->imask);
+
+ iowrite32be(0xffffffff, &regs->ievent);
+
+ tmp = (u32)((macaddr[5] << 24) |
+ (macaddr[4] << 16) | (macaddr[3] << 8) | macaddr[2]);
+ iowrite32be(tmp, &regs->macstnaddr1);
+
+ tmp = (u32)((macaddr[1] << 24) | (macaddr[0] << 16));
+ iowrite32be(tmp, &regs->macstnaddr2);
+
+ /* HASH */
+ for (i = 0; i < NUM_OF_HASH_REGS; i++) {
+ /* Initialize IADDRx */
+ iowrite32be(0, &regs->igaddr[i]);
+ /* Initialize GADDRx */
+ iowrite32be(0, &regs->gaddr[i]);
+ }
+
+ return 0;
+}
+
+static void set_mac_address(struct dtsec_regs __iomem *regs, u8 *adr)
+{
+ u32 tmp;
+
+ tmp = (u32)((adr[5] << 24) |
+ (adr[4] << 16) | (adr[3] << 8) | adr[2]);
+ iowrite32be(tmp, &regs->macstnaddr1);
+
+ tmp = (u32)((adr[1] << 24) | (adr[0] << 16));
+ iowrite32be(tmp, &regs->macstnaddr2);
+}
+
+static void set_bucket(struct dtsec_regs __iomem *regs, int bucket,
+ bool enable)
+{
+ int reg_idx = (bucket >> 5) & 0xf;
+ int bit_idx = bucket & 0x1f;
+ u32 bit_mask = 0x80000000 >> bit_idx;
+ u32 __iomem *reg;
+
+ if (reg_idx > 7)
+ reg = &regs->gaddr[reg_idx - 8];
+ else
+ reg = &regs->igaddr[reg_idx];
+
+ if (enable)
+ iowrite32be(ioread32be(reg) | bit_mask, reg);
+ else
+ iowrite32be(ioread32be(reg) & (~bit_mask), reg);
+}
+
+static int check_init_parameters(struct fman_mac *dtsec)
+{
+ if (dtsec->max_speed >= SPEED_10000) {
+ pr_err("1G MAC driver supports 1G or lower speeds\n");
+ return -EINVAL;
+ }
+ if (dtsec->addr == 0) {
+ pr_err("Ethernet MAC Must have a valid MAC Address\n");
+ return -EINVAL;
+ }
+ if ((dtsec->dtsec_drv_param)->rx_prepend >
+ MAX_PACKET_ALIGNMENT) {
+ pr_err("packetAlignmentPadding can't be > than %d\n",
+ MAX_PACKET_ALIGNMENT);
+ return -EINVAL;
+ }
+ if (((dtsec->dtsec_drv_param)->non_back_to_back_ipg1 >
+ MAX_INTER_PACKET_GAP) ||
+ ((dtsec->dtsec_drv_param)->non_back_to_back_ipg2 >
+ MAX_INTER_PACKET_GAP) ||
+ ((dtsec->dtsec_drv_param)->back_to_back_ipg >
+ MAX_INTER_PACKET_GAP)) {
+ pr_err("Inter packet gap can't be greater than %d\n",
+ MAX_INTER_PACKET_GAP);
+ return -EINVAL;
+ }
+ if ((dtsec->dtsec_drv_param)->halfdup_retransmit >
+ MAX_RETRANSMISSION) {
+ pr_err("maxRetransmission can't be greater than %d\n",
+ MAX_RETRANSMISSION);
+ return -EINVAL;
+ }
+ if ((dtsec->dtsec_drv_param)->halfdup_coll_window >
+ MAX_COLLISION_WINDOW) {
+ pr_err("collisionWindow can't be greater than %d\n",
+ MAX_COLLISION_WINDOW);
+ return -EINVAL;
+ /* If Auto negotiation process is disabled, need to set up the PHY
+ * using the MII Management Interface
+ */
+ }
+ if (!dtsec->exception_cb) {
+ pr_err("uninitialized exception_cb\n");
+ return -EINVAL;
+ }
+ if (!dtsec->event_cb) {
+ pr_err("uninitialized event_cb\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_exception_flag(enum fman_mac_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FM_MAC_EX_1G_BAB_RX:
+ bit_mask = DTSEC_IMASK_BREN;
+ break;
+ case FM_MAC_EX_1G_RX_CTL:
+ bit_mask = DTSEC_IMASK_RXCEN;
+ break;
+ case FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET:
+ bit_mask = DTSEC_IMASK_GTSCEN;
+ break;
+ case FM_MAC_EX_1G_BAB_TX:
+ bit_mask = DTSEC_IMASK_BTEN;
+ break;
+ case FM_MAC_EX_1G_TX_CTL:
+ bit_mask = DTSEC_IMASK_TXCEN;
+ break;
+ case FM_MAC_EX_1G_TX_ERR:
+ bit_mask = DTSEC_IMASK_TXEEN;
+ break;
+ case FM_MAC_EX_1G_LATE_COL:
+ bit_mask = DTSEC_IMASK_LCEN;
+ break;
+ case FM_MAC_EX_1G_COL_RET_LMT:
+ bit_mask = DTSEC_IMASK_CRLEN;
+ break;
+ case FM_MAC_EX_1G_TX_FIFO_UNDRN:
+ bit_mask = DTSEC_IMASK_XFUNEN;
+ break;
+ case FM_MAC_EX_1G_MAG_PCKT:
+ bit_mask = DTSEC_IMASK_MAGEN;
+ break;
+ case FM_MAC_EX_1G_MII_MNG_RD_COMPLET:
+ bit_mask = DTSEC_IMASK_MMRDEN;
+ break;
+ case FM_MAC_EX_1G_MII_MNG_WR_COMPLET:
+ bit_mask = DTSEC_IMASK_MMWREN;
+ break;
+ case FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET:
+ bit_mask = DTSEC_IMASK_GRSCEN;
+ break;
+ case FM_MAC_EX_1G_DATA_ERR:
+ bit_mask = DTSEC_IMASK_TDPEEN;
+ break;
+ case FM_MAC_EX_1G_RX_MIB_CNT_OVFL:
+ bit_mask = DTSEC_IMASK_MSROEN;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static bool is_init_done(struct dtsec_cfg *dtsec_drv_params)
+{
+ /* Checks if dTSEC driver parameters were initialized */
+ if (!dtsec_drv_params)
+ return true;
+
+ return false;
+}
+
+static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return 0;
+
+ return (u16)ioread32be(&regs->maxfrm);
+}
+
+static void dtsec_isr(void *handle)
+{
+ struct fman_mac *dtsec = (struct fman_mac *)handle;
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 event;
+
+ /* do not handle MDIO events */
+ event = ioread32be(&regs->ievent) &
+ (u32)(~(DTSEC_IMASK_MMRDEN | DTSEC_IMASK_MMWREN));
+
+ event &= ioread32be(&regs->imask);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & DTSEC_IMASK_BREN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_RX);
+ if (event & DTSEC_IMASK_RXCEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_RX_CTL);
+ if (event & DTSEC_IMASK_GTSCEN)
+ dtsec->exception_cb(dtsec->dev_id,
+ FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET);
+ if (event & DTSEC_IMASK_BTEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_TX);
+ if (event & DTSEC_IMASK_TXCEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_CTL);
+ if (event & DTSEC_IMASK_TXEEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_ERR);
+ if (event & DTSEC_IMASK_LCEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_LATE_COL);
+ if (event & DTSEC_IMASK_CRLEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT);
+ if (event & DTSEC_IMASK_XFUNEN) {
+ /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */
+ if (dtsec->fm_rev_info.major == 2) {
+ u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i;
+ /* a. Write 0x00E0_0C00 to DTSEC_ID
+ * This is a read only register
+ * b. Read and save the value of TPKT
+ */
+ tpkt1 = ioread32be(&regs->tpkt);
+
+ /* c. Read the register at dTSEC address offset 0x32C */
+ tmp_reg1 = ioread32be(&regs->reserved02c0[27]);
+
+ /* d. Compare bits [9:15] to bits [25:31] of the
+ * register at address offset 0x32C.
+ */
+ if ((tmp_reg1 & 0x007F0000) !=
+ (tmp_reg1 & 0x0000007F)) {
+ /* If they are not equal, save the value of
+ * this register and wait for at least
+ * MAXFRM*16 ns
+ */
+ usleep_range((u32)(min
+ (dtsec_get_max_frame_length(dtsec) *
+ 16 / 1000, 1)), (u32)
+ (min(dtsec_get_max_frame_length
+ (dtsec) * 16 / 1000, 1) + 1));
+ }
+
+ /* e. Read and save TPKT again and read the register
+ * at dTSEC address offset 0x32C again
+ */
+ tpkt2 = ioread32be(&regs->tpkt);
+ tmp_reg2 = ioread32be(&regs->reserved02c0[27]);
+
+ /* f. Compare the value of TPKT saved in step b to
+ * value read in step e. Also compare bits [9:15] of
+ * the register at offset 0x32C saved in step d to the
+ * value of bits [9:15] saved in step e. If the two
+ * registers values are unchanged, then the transmit
+ * portion of the dTSEC controller is locked up and
+ * the user should proceed to the recover sequence.
+ */
+ if ((tpkt1 == tpkt2) && ((tmp_reg1 & 0x007F0000) ==
+ (tmp_reg2 & 0x007F0000))) {
+ /* recover sequence */
+
+ /* a.Write a 1 to RCTRL[GRS] */
+
+ iowrite32be(ioread32be(&regs->rctrl) |
+ RCTRL_GRS, &regs->rctrl);
+
+ /* b.Wait until IEVENT[GRSC]=1, or at least
+ * 100 us has elapsed.
+ */
+ for (i = 0; i < 100; i++) {
+ if (ioread32be(&regs->ievent) &
+ DTSEC_IMASK_GRSCEN)
+ break;
+ udelay(1);
+ }
+ if (ioread32be(&regs->ievent) &
+ DTSEC_IMASK_GRSCEN)
+ iowrite32be(DTSEC_IMASK_GRSCEN,
+ &regs->ievent);
+ else
+ pr_debug("Rx lockup due to Tx lockup\n");
+
+ /* c.Write a 1 to bit n of FM_RSTC
+ * (offset 0x0CC of FPM)
+ */
+ fman_reset_mac(dtsec->fm, dtsec->mac_id);
+
+ /* d.Wait 4 Tx clocks (32 ns) */
+ udelay(1);
+
+ /* e.Write a 0 to bit n of FM_RSTC. */
+ /* cleared by FMAN
+ */
+ }
+ }
+
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_FIFO_UNDRN);
+ }
+ if (event & DTSEC_IMASK_MAGEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_MAG_PCKT);
+ if (event & DTSEC_IMASK_GRSCEN)
+ dtsec->exception_cb(dtsec->dev_id,
+ FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET);
+ if (event & DTSEC_IMASK_TDPEEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_DATA_ERR);
+ if (event & DTSEC_IMASK_RDPEEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_1G_RX_DATA_ERR);
+
+ /* masked interrupts */
+ WARN_ON(event & DTSEC_IMASK_ABRTEN);
+ WARN_ON(event & DTSEC_IMASK_IFERREN);
+}
+
+static void dtsec_1588_isr(void *handle)
+{
+ struct fman_mac *dtsec = (struct fman_mac *)handle;
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 event;
+
+ if (dtsec->ptp_tsu_enabled) {
+ event = ioread32be(&regs->tmr_pevent);
+ event &= ioread32be(&regs->tmr_pemask);
+
+ if (event) {
+ iowrite32be(event, &regs->tmr_pevent);
+ WARN_ON(event & TMR_PEVENT_TSRE);
+ dtsec->exception_cb(dtsec->dev_id,
+ FM_MAC_EX_1G_1588_TS_RX_ERR);
+ }
+ }
+}
+
+static void free_init_resources(struct fman_mac *dtsec)
+{
+ fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_ERR);
+ fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_NORMAL);
+
+ /* release the driver's group hash table */
+ free_hash_table(dtsec->multicast_addr_hash);
+ dtsec->multicast_addr_hash = NULL;
+
+ /* release the driver's individual hash table */
+ free_hash_table(dtsec->unicast_addr_hash);
+ dtsec->unicast_addr_hash = NULL;
+}
+
+int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val)
+{
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ dtsec->dtsec_drv_param->maximum_frame = new_val;
+
+ return 0;
+}
+
+int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val)
+{
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ dtsec->dtsec_drv_param->tx_pad_crc = new_val;
+
+ return 0;
+}
+
+int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Enable */
+ tmp = ioread32be(&regs->maccfg1);
+ if (mode & COMM_MODE_RX)
+ tmp |= MACCFG1_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp |= MACCFG1_TX_EN;
+
+ iowrite32be(tmp, &regs->maccfg1);
+
+ /* Graceful start - clear the graceful receive stop bit */
+ if (mode & COMM_MODE_TX)
+ iowrite32be(ioread32be(&regs->tctrl) & ~DTSEC_TCTRL_GTS,
+ &regs->tctrl);
+ if (mode & COMM_MODE_RX)
+ iowrite32be(ioread32be(&regs->rctrl) & ~RCTRL_GRS,
+ &regs->rctrl);
+
+ return 0;
+}
+
+int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Gracefull stop - Assert the graceful transmit stop bit */
+ if (mode & COMM_MODE_RX) {
+ tmp = ioread32be(&regs->rctrl) | RCTRL_GRS;
+ iowrite32be(tmp, &regs->rctrl);
+
+ if (dtsec->fm_rev_info.major == 2)
+ usleep_range(100, 200);
+ else
+ udelay(10);
+ }
+
+ if (mode & COMM_MODE_TX) {
+ if (dtsec->fm_rev_info.major == 2)
+ pr_debug("GTS not supported due to DTSEC_A004 errata.\n");
+ else
+ pr_debug("GTS not supported due to DTSEC_A0014 errata.\n");
+ }
+
+ tmp = ioread32be(&regs->maccfg1);
+ if (mode & COMM_MODE_RX)
+ tmp &= ~MACCFG1_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp &= ~MACCFG1_TX_EN;
+
+ iowrite32be(tmp, &regs->maccfg1);
+
+ return 0;
+}
+
+int dtsec_set_tx_pause_frames(struct fman_mac *dtsec,
+ u8 __maybe_unused priority,
+ u16 pause_time, u16 __maybe_unused thresh_time)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 ptv = 0;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */
+ if (dtsec->fm_rev_info.major == 2)
+ if (pause_time <= 320) {
+ pr_warn("pause-time: %d illegal.Should be > 320\n",
+ pause_time);
+ return -EINVAL;
+ }
+
+ if (pause_time) {
+ ptv = ioread32be(&regs->ptv);
+ ptv &= PTV_PTE_MASK;
+ ptv |= pause_time & PTV_PT_MASK;
+ iowrite32be(ptv, &regs->ptv);
+
+ /* trigger the transmission of a flow-control pause frame */
+ iowrite32be(ioread32be(&regs->maccfg1) | MACCFG1_TX_FLOW,
+ &regs->maccfg1);
+ } else
+ iowrite32be(ioread32be(&regs->maccfg1) & ~MACCFG1_TX_FLOW,
+ &regs->maccfg1);
+
+ return 0;
+}
+
+int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->maccfg1);
+ if (en)
+ tmp |= MACCFG1_RX_FLOW;
+ else
+ tmp &= ~MACCFG1_RX_FLOW;
+ iowrite32be(tmp, &regs->maccfg1);
+
+ return 0;
+}
+
+int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr)
+{
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Initialize MAC Station Address registers (1 & 2)
+ * Station address have to be swapped (big endian to little endian
+ */
+ dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr);
+ set_mac_address(dtsec->regs, (u8 *)(*enet_addr));
+
+ return 0;
+}
+
+int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ struct eth_hash_entry *hash_entry;
+ u64 addr;
+ s32 bucket;
+ u32 crc = 0xFFFFFFFF;
+ bool mcast, ghtx;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ ghtx = (bool)((ioread32be(&regs->rctrl) & RCTRL_GHTX) ? true : false);
+ mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false);
+
+ /* Cannot handle unicast mac addr when GHTX is on */
+ if (ghtx && !mcast) {
+ pr_err("Could not compute hash bucket\n");
+ return -EINVAL;
+ }
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+
+ /* considering the 9 highest order bits in crc H[8:0]:
+ *if ghtx = 0 H[8:6] (highest order 3 bits) identify the hash register
+ *and H[5:1] (next 5 bits) identify the hash bit
+ *if ghts = 1 H[8:5] (highest order 4 bits) identify the hash register
+ *and H[4:0] (next 5 bits) identify the hash bit.
+ *
+ *In bucket index output the low 5 bits identify the hash register
+ *bit, while the higher 4 bits identify the hash register
+ */
+
+ if (ghtx) {
+ bucket = (s32)((crc >> 23) & 0x1ff);
+ } else {
+ bucket = (s32)((crc >> 24) & 0xff);
+ /* if !ghtx and mcast the bit must be set in gaddr instead of
+ *igaddr.
+ */
+ if (mcast)
+ bucket += 0x100;
+ }
+
+ set_bucket(dtsec->regs, bucket, true);
+
+ /* Create element to be added to the driver hash table */
+ hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL);
+ if (!hash_entry)
+ return -ENOMEM;
+ hash_entry->addr = addr;
+ INIT_LIST_HEAD(&hash_entry->node);
+
+ if (addr & MAC_GROUP_ADDRESS)
+ /* Group Address */
+ list_add_tail(&hash_entry->node,
+ &dtsec->multicast_addr_hash->lsts[bucket]);
+ else
+ list_add_tail(&hash_entry->node,
+ &dtsec->unicast_addr_hash->lsts[bucket]);
+
+ return 0;
+}
+
+int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ struct list_head *pos;
+ struct eth_hash_entry *hash_entry = NULL;
+ u64 addr;
+ s32 bucket;
+ u32 crc = 0xFFFFFFFF;
+ bool mcast, ghtx;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ ghtx = (bool)((ioread32be(&regs->rctrl) & RCTRL_GHTX) ? true : false);
+ mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false);
+
+ /* Cannot handle unicast mac addr when GHTX is on */
+ if (ghtx && !mcast) {
+ pr_err("Could not compute hash bucket\n");
+ return -EINVAL;
+ }
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+
+ if (ghtx) {
+ bucket = (s32)((crc >> 23) & 0x1ff);
+ } else {
+ bucket = (s32)((crc >> 24) & 0xff);
+ /* if !ghtx and mcast the bit must be set
+ * in gaddr instead of igaddr.
+ */
+ if (mcast)
+ bucket += 0x100;
+ }
+
+ if (addr & MAC_GROUP_ADDRESS) {
+ /* Group Address */
+ list_for_each(pos,
+ &dtsec->multicast_addr_hash->lsts[bucket]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&dtsec->multicast_addr_hash->lsts[bucket]))
+ set_bucket(dtsec->regs, bucket, false);
+ } else {
+ /* Individual Address */
+ list_for_each(pos,
+ &dtsec->unicast_addr_hash->lsts[bucket]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&dtsec->unicast_addr_hash->lsts[bucket]))
+ set_bucket(dtsec->regs, bucket, false);
+ }
+
+ /* address does not exist */
+ WARN_ON(!hash_entry);
+
+ return 0;
+}
+
+int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Set unicast promiscuous */
+ tmp = ioread32be(&regs->rctrl);
+ if (new_val)
+ tmp |= RCTRL_UPROM;
+ else
+ tmp &= ~RCTRL_UPROM;
+
+ iowrite32be(tmp, &regs->rctrl);
+
+ /* Set multicast promiscuous */
+ tmp = ioread32be(&regs->rctrl);
+ if (new_val)
+ tmp |= RCTRL_MPROM;
+ else
+ tmp &= ~RCTRL_MPROM;
+
+ iowrite32be(tmp, &regs->rctrl);
+
+ return 0;
+}
+
+int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->maccfg2);
+
+ /* Full Duplex */
+ tmp |= MACCFG2_FULL_DUPLEX;
+
+ tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE);
+ if (speed < SPEED_1000)
+ tmp |= MACCFG2_NIBBLE_MODE;
+ else if (speed == SPEED_1000)
+ tmp |= MACCFG2_BYTE_MODE;
+ iowrite32be(tmp, &regs->maccfg2);
+
+ tmp = ioread32be(&regs->ecntrl);
+ if (speed == SPEED_100)
+ tmp |= DTSEC_ECNTRL_R100M;
+ else
+ tmp &= ~DTSEC_ECNTRL_R100M;
+ iowrite32be(tmp, &regs->ecntrl);
+
+ return 0;
+}
+
+int dtsec_restart_autoneg(struct fman_mac *dtsec)
+{
+ u16 tmp_reg16;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR);
+
+ tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000);
+ tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART |
+ BMCR_FULLDPLX | BMCR_SPEED1000);
+
+ phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
+
+ return 0;
+}
+
+int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ *mac_version = ioread32be(&regs->tsec_id);
+
+ return 0;
+}
+
+int dtsec_set_exception(struct fman_mac *dtsec,
+ enum fman_mac_exceptions exception, bool enable)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 bit_mask = 0;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) {
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ dtsec->exceptions |= bit_mask;
+ else
+ dtsec->exceptions &= ~bit_mask;
+ } else {
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ if (enable)
+ iowrite32be(ioread32be(&regs->imask) | bit_mask,
+ &regs->imask);
+ else
+ iowrite32be(ioread32be(&regs->imask) & ~bit_mask,
+ &regs->imask);
+ } else {
+ if (!dtsec->ptp_tsu_enabled) {
+ pr_err("Exception valid for 1588 only\n");
+ return -EINVAL;
+ }
+ switch (exception) {
+ case FM_MAC_EX_1G_1588_TS_RX_ERR:
+ if (enable) {
+ dtsec->en_tsu_err_exeption = true;
+ iowrite32be(ioread32be(&regs->tmr_pemask) |
+ TMR_PEMASK_TSREEN,
+ &regs->tmr_pemask);
+ } else {
+ dtsec->en_tsu_err_exeption = false;
+ iowrite32be(ioread32be(&regs->tmr_pemask) &
+ ~TMR_PEMASK_TSREEN,
+ &regs->tmr_pemask);
+ }
+ break;
+ default:
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int dtsec_init(struct fman_mac *dtsec)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ struct dtsec_cfg *dtsec_drv_param;
+ int err;
+ u16 max_frm_ln;
+ enet_addr_t eth_addr;
+
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ if (DEFAULT_RESET_ON_INIT &&
+ (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) {
+ pr_err("Can't reset MAC!\n");
+ return -EINVAL;
+ }
+
+ err = check_init_parameters(dtsec);
+ if (err)
+ return err;
+
+ dtsec_drv_param = dtsec->dtsec_drv_param;
+
+ MAKE_ENET_ADDR_FROM_UINT64(dtsec->addr, eth_addr);
+
+ err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if,
+ dtsec->max_speed, (u8 *)eth_addr, dtsec->exceptions,
+ dtsec->tbiphy->mdio.addr);
+ if (err) {
+ free_init_resources(dtsec);
+ pr_err("DTSEC version doesn't support this i/f mode\n");
+ return err;
+ }
+
+ if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) {
+ u16 tmp_reg16;
+
+ /* Configure the TBI PHY Control Register */
+ tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET;
+ phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16);
+
+ tmp_reg16 = TBICON_CLK_SELECT;
+ phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16);
+
+ tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE |
+ BMCR_FULLDPLX | BMCR_SPEED1000);
+ phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
+
+ if (dtsec->basex_if)
+ tmp_reg16 = TBIANA_1000X;
+ else
+ tmp_reg16 = TBIANA_SGMII;
+ phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16);
+
+ tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART |
+ BMCR_FULLDPLX | BMCR_SPEED1000);
+
+ phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
+ }
+
+ /* Max Frame Length */
+ max_frm_ln = (u16)ioread32be(&regs->maxfrm);
+ err = fman_set_mac_max_frame(dtsec->fm, dtsec->mac_id, max_frm_ln);
+ if (err) {
+ pr_err("Setting max frame length failed\n");
+ free_init_resources(dtsec);
+ return -EINVAL;
+ }
+
+ dtsec->multicast_addr_hash =
+ alloc_hash_table(EXTENDED_HASH_TABLE_SIZE);
+ if (!dtsec->multicast_addr_hash) {
+ free_init_resources(dtsec);
+ pr_err("MC hash table is failed\n");
+ return -ENOMEM;
+ }
+
+ dtsec->unicast_addr_hash = alloc_hash_table(DTSEC_HASH_TABLE_SIZE);
+ if (!dtsec->unicast_addr_hash) {
+ free_init_resources(dtsec);
+ pr_err("UC hash table is failed\n");
+ return -ENOMEM;
+ }
+
+ /* register err intr handler for dtsec to FPM (err) */
+ fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_ERR, dtsec_isr, dtsec);
+ /* register 1588 intr handler for TMR to FPM (normal) */
+ fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_NORMAL, dtsec_1588_isr, dtsec);
+
+ kfree(dtsec_drv_param);
+ dtsec->dtsec_drv_param = NULL;
+
+ return 0;
+}
+
+int dtsec_free(struct fman_mac *dtsec)
+{
+ free_init_resources(dtsec);
+
+ kfree(dtsec->dtsec_drv_param);
+ dtsec->dtsec_drv_param = NULL;
+ kfree(dtsec);
+
+ return 0;
+}
+
+struct fman_mac *dtsec_config(struct fman_mac_params *params)
+{
+ struct fman_mac *dtsec;
+ struct dtsec_cfg *dtsec_drv_param;
+ void __iomem *base_addr;
+
+ base_addr = params->base_addr;
+
+ /* allocate memory for the UCC GETH data structure. */
+ dtsec = kzalloc(sizeof(*dtsec), GFP_KERNEL);
+ if (!dtsec)
+ return NULL;
+
+ /* allocate memory for the d_tsec driver parameters data structure. */
+ dtsec_drv_param = kzalloc(sizeof(*dtsec_drv_param), GFP_KERNEL);
+ if (!dtsec_drv_param)
+ goto err_dtsec;
+
+ /* Plant parameter structure pointer */
+ dtsec->dtsec_drv_param = dtsec_drv_param;
+
+ set_dflts(dtsec_drv_param);
+
+ dtsec->regs = base_addr;
+ dtsec->addr = ENET_ADDR_TO_UINT64(params->addr);
+ dtsec->max_speed = params->max_speed;
+ dtsec->phy_if = params->phy_if;
+ dtsec->mac_id = params->mac_id;
+ dtsec->exceptions = (DTSEC_IMASK_BREN |
+ DTSEC_IMASK_RXCEN |
+ DTSEC_IMASK_BTEN |
+ DTSEC_IMASK_TXCEN |
+ DTSEC_IMASK_TXEEN |
+ DTSEC_IMASK_ABRTEN |
+ DTSEC_IMASK_LCEN |
+ DTSEC_IMASK_CRLEN |
+ DTSEC_IMASK_XFUNEN |
+ DTSEC_IMASK_IFERREN |
+ DTSEC_IMASK_MAGEN |
+ DTSEC_IMASK_TDPEEN |
+ DTSEC_IMASK_RDPEEN);
+ dtsec->exception_cb = params->exception_cb;
+ dtsec->event_cb = params->event_cb;
+ dtsec->dev_id = params->dev_id;
+ dtsec->ptp_tsu_enabled = dtsec->dtsec_drv_param->ptp_tsu_en;
+ dtsec->en_tsu_err_exeption = dtsec->dtsec_drv_param->ptp_exception_en;
+
+ dtsec->fm = params->fm;
+ dtsec->basex_if = params->basex_if;
+
+ if (!params->internal_phy_node) {
+ pr_err("TBI PHY node is not available\n");
+ goto err_dtsec_drv_param;
+ }
+
+ dtsec->tbiphy = of_phy_find_device(params->internal_phy_node);
+ if (!dtsec->tbiphy) {
+ pr_err("of_phy_find_device (TBI PHY) failed\n");
+ put_device(&dtsec->tbiphy->mdio.dev);
+ goto err_dtsec_drv_param;
+ }
+
+ put_device(&dtsec->tbiphy->mdio.dev);
+
+ /* Save FMan revision */
+ fman_get_revision(dtsec->fm, &dtsec->fm_rev_info);
+
+ return dtsec;
+
+err_dtsec_drv_param:
+ kfree(dtsec_drv_param);
+err_dtsec:
+ kfree(dtsec);
+ return NULL;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.h b/drivers/net/ethernet/freescale/fman/fman_dtsec.h
new file mode 100644
index 000000000000..c4467c072058
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DTSEC_H
+#define __DTSEC_H
+
+#include "fman_mac.h"
+
+struct fman_mac *dtsec_config(struct fman_mac_params *params);
+int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val);
+int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr);
+int dtsec_adjust_link(struct fman_mac *dtsec,
+ u16 speed);
+int dtsec_restart_autoneg(struct fman_mac *dtsec);
+int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val);
+int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val);
+int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode);
+int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode);
+int dtsec_init(struct fman_mac *dtsec);
+int dtsec_free(struct fman_mac *dtsec);
+int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en);
+int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u8 priority,
+ u16 pause_time, u16 thresh_time);
+int dtsec_set_exception(struct fman_mac *dtsec,
+ enum fman_mac_exceptions exception, bool enable);
+int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr);
+int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr);
+int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version);
+
+#endif /* __DTSEC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_mac.h b/drivers/net/ethernet/freescale/fman/fman_mac.h
new file mode 100644
index 000000000000..8ddeedbcef9c
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_mac.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* FM MAC ... */
+#ifndef __FM_MAC_H
+#define __FM_MAC_H
+
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/phy.h>
+#include <linux/if_ether.h>
+
+struct fman_mac;
+
+/* Ethernet Address */
+typedef u8 enet_addr_t[ETH_ALEN];
+
+#define ENET_ADDR_TO_UINT64(_enet_addr) \
+ (u64)(((u64)(_enet_addr)[0] << 40) | \
+ ((u64)(_enet_addr)[1] << 32) | \
+ ((u64)(_enet_addr)[2] << 24) | \
+ ((u64)(_enet_addr)[3] << 16) | \
+ ((u64)(_enet_addr)[4] << 8) | \
+ ((u64)(_enet_addr)[5]))
+
+#define MAKE_ENET_ADDR_FROM_UINT64(_addr64, _enet_addr) \
+ do { \
+ int i; \
+ for (i = 0; i < ETH_ALEN; i++) \
+ (_enet_addr)[i] = \
+ (u8)((_addr64) >> ((5 - i) * 8)); \
+ } while (0)
+
+/* defaults */
+#define DEFAULT_RESET_ON_INIT false
+
+/* PFC defines */
+#define FSL_FM_PAUSE_TIME_ENABLE 0xf000
+#define FSL_FM_PAUSE_TIME_DISABLE 0
+#define FSL_FM_PAUSE_THRESH_DEFAULT 0
+
+#define FM_MAC_NO_PFC 0xff
+
+/* HASH defines */
+#define ETH_HASH_ENTRY_OBJ(ptr) \
+ hlist_entry_safe(ptr, struct eth_hash_entry, node)
+
+/* Enumeration (bit flags) of communication modes (Transmit,
+ * receive or both).
+ */
+enum comm_mode {
+ COMM_MODE_NONE = 0, /* No transmit/receive communication */
+ COMM_MODE_RX = 1, /* Only receive communication */
+ COMM_MODE_TX = 2, /* Only transmit communication */
+ COMM_MODE_RX_AND_TX = 3 /* Both transmit and receive communication */
+};
+
+/* FM MAC Exceptions */
+enum fman_mac_exceptions {
+ FM_MAC_EX_10G_MDIO_SCAN_EVENT = 0
+ /* 10GEC MDIO scan event interrupt */
+ , FM_MAC_EX_10G_MDIO_CMD_CMPL
+ /* 10GEC MDIO command completion interrupt */
+ , FM_MAC_EX_10G_REM_FAULT
+ /* 10GEC, mEMAC Remote fault interrupt */
+ , FM_MAC_EX_10G_LOC_FAULT
+ /* 10GEC, mEMAC Local fault interrupt */
+ , FM_MAC_EX_10G_TX_ECC_ER
+ /* 10GEC, mEMAC Transmit frame ECC error interrupt */
+ , FM_MAC_EX_10G_TX_FIFO_UNFL
+ /* 10GEC, mEMAC Transmit FIFO underflow interrupt */
+ , FM_MAC_EX_10G_TX_FIFO_OVFL
+ /* 10GEC, mEMAC Transmit FIFO overflow interrupt */
+ , FM_MAC_EX_10G_TX_ER
+ /* 10GEC Transmit frame error interrupt */
+ , FM_MAC_EX_10G_RX_FIFO_OVFL
+ /* 10GEC, mEMAC Receive FIFO overflow interrupt */
+ , FM_MAC_EX_10G_RX_ECC_ER
+ /* 10GEC, mEMAC Receive frame ECC error interrupt */
+ , FM_MAC_EX_10G_RX_JAB_FRM
+ /* 10GEC Receive jabber frame interrupt */
+ , FM_MAC_EX_10G_RX_OVRSZ_FRM
+ /* 10GEC Receive oversized frame interrupt */
+ , FM_MAC_EX_10G_RX_RUNT_FRM
+ /* 10GEC Receive runt frame interrupt */
+ , FM_MAC_EX_10G_RX_FRAG_FRM
+ /* 10GEC Receive fragment frame interrupt */
+ , FM_MAC_EX_10G_RX_LEN_ER
+ /* 10GEC Receive payload length error interrupt */
+ , FM_MAC_EX_10G_RX_CRC_ER
+ /* 10GEC Receive CRC error interrupt */
+ , FM_MAC_EX_10G_RX_ALIGN_ER
+ /* 10GEC Receive alignment error interrupt */
+ , FM_MAC_EX_1G_BAB_RX
+ /* dTSEC Babbling receive error */
+ , FM_MAC_EX_1G_RX_CTL
+ /* dTSEC Receive control (pause frame) interrupt */
+ , FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET
+ /* dTSEC Graceful transmit stop complete */
+ , FM_MAC_EX_1G_BAB_TX
+ /* dTSEC Babbling transmit error */
+ , FM_MAC_EX_1G_TX_CTL
+ /* dTSEC Transmit control (pause frame) interrupt */
+ , FM_MAC_EX_1G_TX_ERR
+ /* dTSEC Transmit error */
+ , FM_MAC_EX_1G_LATE_COL
+ /* dTSEC Late collision */
+ , FM_MAC_EX_1G_COL_RET_LMT
+ /* dTSEC Collision retry limit */
+ , FM_MAC_EX_1G_TX_FIFO_UNDRN
+ /* dTSEC Transmit FIFO underrun */
+ , FM_MAC_EX_1G_MAG_PCKT
+ /* dTSEC Magic Packet detection */
+ , FM_MAC_EX_1G_MII_MNG_RD_COMPLET
+ /* dTSEC MII management read completion */
+ , FM_MAC_EX_1G_MII_MNG_WR_COMPLET
+ /* dTSEC MII management write completion */
+ , FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET
+ /* dTSEC Graceful receive stop complete */
+ , FM_MAC_EX_1G_DATA_ERR
+ /* dTSEC Internal data error on transmit */
+ , FM_MAC_1G_RX_DATA_ERR
+ /* dTSEC Internal data error on receive */
+ , FM_MAC_EX_1G_1588_TS_RX_ERR
+ /* dTSEC Time-Stamp Receive Error */
+ , FM_MAC_EX_1G_RX_MIB_CNT_OVFL
+ /* dTSEC MIB counter overflow */
+ , FM_MAC_EX_TS_FIFO_ECC_ERR
+ /* mEMAC Time-stamp FIFO ECC error interrupt;
+ * not supported on T4240/B4860 rev1 chips
+ */
+ , FM_MAC_EX_MAGIC_PACKET_INDICATION = FM_MAC_EX_1G_MAG_PCKT
+ /* mEMAC Magic Packet Indication Interrupt */
+};
+
+struct eth_hash_entry {
+ u64 addr; /* Ethernet Address */
+ struct list_head node;
+};
+
+typedef void (fman_mac_exception_cb)(void *dev_id,
+ enum fman_mac_exceptions exceptions);
+
+/* FMan MAC config input */
+struct fman_mac_params {
+ /* Base of memory mapped FM MAC registers */
+ void __iomem *base_addr;
+ /* MAC address of device; First octet is sent first */
+ enet_addr_t addr;
+ /* MAC ID; numbering of dTSEC and 1G-mEMAC:
+ * 0 - FM_MAX_NUM_OF_1G_MACS;
+ * numbering of 10G-MAC (TGEC) and 10G-mEMAC:
+ * 0 - FM_MAX_NUM_OF_10G_MACS
+ */
+ u8 mac_id;
+ /* PHY interface */
+ phy_interface_t phy_if;
+ /* Note that the speed should indicate the maximum rate that
+ * this MAC should support rather than the actual speed;
+ */
+ u16 max_speed;
+ /* A handle to the FM object this port related to */
+ void *fm;
+ /* MDIO exceptions interrupt source - not valid for all
+ * MACs; MUST be set to 'NO_IRQ' for MACs that don't have
+ * mdio-irq, or for polling
+ */
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */
+ fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */
+ /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC
+ * and phy or backplane; Note: 1000BaseX auto-negotiation relates only
+ * to interface between MAC and phy/backplane, SGMII phy can still
+ * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps
+ */
+ bool basex_if;
+ /* Pointer to TBI/PCS PHY node, used for TBI/PCS PHY access */
+ struct device_node *internal_phy_node;
+};
+
+struct eth_hash_t {
+ u16 size;
+ struct list_head *lsts;
+};
+
+static inline struct eth_hash_entry
+*dequeue_addr_from_hash_entry(struct list_head *addr_lst)
+{
+ struct eth_hash_entry *hash_entry = NULL;
+
+ if (!list_empty(addr_lst)) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(addr_lst->next);
+ list_del_init(&hash_entry->node);
+ }
+ return hash_entry;
+}
+
+static inline void free_hash_table(struct eth_hash_t *hash)
+{
+ struct eth_hash_entry *hash_entry;
+ int i = 0;
+
+ if (hash) {
+ if (hash->lsts) {
+ for (i = 0; i < hash->size; i++) {
+ hash_entry =
+ dequeue_addr_from_hash_entry(&hash->lsts[i]);
+ while (hash_entry) {
+ kfree(hash_entry);
+ hash_entry =
+ dequeue_addr_from_hash_entry(&hash->
+ lsts[i]);
+ }
+ }
+
+ kfree(hash->lsts);
+ }
+
+ kfree(hash);
+ }
+}
+
+static inline struct eth_hash_t *alloc_hash_table(u16 size)
+{
+ u32 i;
+ struct eth_hash_t *hash;
+
+ /* Allocate address hash table */
+ hash = kmalloc_array(size, sizeof(struct eth_hash_t *), GFP_KERNEL);
+ if (!hash)
+ return NULL;
+
+ hash->size = size;
+
+ hash->lsts = kmalloc_array(hash->size, sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!hash->lsts) {
+ kfree(hash);
+ return NULL;
+ }
+
+ for (i = 0; i < hash->size; i++)
+ INIT_LIST_HEAD(&hash->lsts[i]);
+
+ return hash;
+}
+
+#endif /* __FM_MAC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c
new file mode 100644
index 000000000000..45e98fd8b79e
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
@@ -0,0 +1,1170 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_memac.h"
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/phy.h>
+#include <linux/of_mdio.h>
+
+/* PCS registers */
+#define MDIO_SGMII_CR 0x00
+#define MDIO_SGMII_DEV_ABIL_SGMII 0x04
+#define MDIO_SGMII_LINK_TMR_L 0x12
+#define MDIO_SGMII_LINK_TMR_H 0x13
+#define MDIO_SGMII_IF_MODE 0x14
+
+/* SGMII Control defines */
+#define SGMII_CR_AN_EN 0x1000
+#define SGMII_CR_RESTART_AN 0x0200
+#define SGMII_CR_FD 0x0100
+#define SGMII_CR_SPEED_SEL1_1G 0x0040
+#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \
+ SGMII_CR_SPEED_SEL1_1G)
+
+/* SGMII Device Ability for SGMII defines */
+#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001
+#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0
+
+/* Link timer define */
+#define LINK_TMR_L 0xa120
+#define LINK_TMR_H 0x0007
+#define LINK_TMR_L_BASEX 0xaf08
+#define LINK_TMR_H_BASEX 0x002f
+
+/* SGMII IF Mode defines */
+#define IF_MODE_USE_SGMII_AN 0x0002
+#define IF_MODE_SGMII_EN 0x0001
+#define IF_MODE_SGMII_SPEED_100M 0x0004
+#define IF_MODE_SGMII_SPEED_1G 0x0008
+#define IF_MODE_SGMII_DUPLEX_HALF 0x0010
+
+/* Num of additional exact match MAC adr regs */
+#define MEMAC_NUM_OF_PADDRS 7
+
+/* Control and Configuration Register (COMMAND_CONFIG) */
+#define CMD_CFG_REG_LOWP_RXETY 0x01000000 /* 07 Rx low power indication */
+#define CMD_CFG_TX_LOWP_ENA 0x00800000 /* 08 Tx Low Power Idle Enable */
+#define CMD_CFG_PFC_MODE 0x00080000 /* 12 Enable PFC */
+#define CMD_CFG_NO_LEN_CHK 0x00020000 /* 14 Payload length check disable */
+#define CMD_CFG_SW_RESET 0x00001000 /* 19 S/W Reset, self clearing bit */
+#define CMD_CFG_TX_PAD_EN 0x00000800 /* 20 Enable Tx padding of frames */
+#define CMD_CFG_PAUSE_IGNORE 0x00000100 /* 23 Ignore Pause frame quanta */
+#define CMD_CFG_CRC_FWD 0x00000040 /* 25 Terminate/frwd CRC of frames */
+#define CMD_CFG_PAD_EN 0x00000020 /* 26 Frame padding removal */
+#define CMD_CFG_PROMIS_EN 0x00000010 /* 27 Promiscuous operation enable */
+#define CMD_CFG_RX_EN 0x00000002 /* 30 MAC receive path enable */
+#define CMD_CFG_TX_EN 0x00000001 /* 31 MAC transmit path enable */
+
+/* Transmit FIFO Sections Register (TX_FIFO_SECTIONS) */
+#define TX_FIFO_SECTIONS_TX_EMPTY_MASK 0xFFFF0000
+#define TX_FIFO_SECTIONS_TX_AVAIL_MASK 0x0000FFFF
+#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G 0x00400000
+#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G 0x00100000
+#define TX_FIFO_SECTIONS_TX_AVAIL_10G 0x00000019
+#define TX_FIFO_SECTIONS_TX_AVAIL_1G 0x00000020
+#define TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G 0x00000060
+
+#define GET_TX_EMPTY_DEFAULT_VALUE(_val) \
+do { \
+ _val &= ~TX_FIFO_SECTIONS_TX_EMPTY_MASK; \
+ ((_val == TX_FIFO_SECTIONS_TX_AVAIL_10G) ? \
+ (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G) :\
+ (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G));\
+} while (0)
+
+/* Interface Mode Register (IF_MODE) */
+
+#define IF_MODE_MASK 0x00000003 /* 30-31 Mask on i/f mode bits */
+#define IF_MODE_XGMII 0x00000000 /* 30-31 XGMII (10G) interface */
+#define IF_MODE_GMII 0x00000002 /* 30-31 GMII (1G) interface */
+#define IF_MODE_RGMII 0x00000004
+#define IF_MODE_RGMII_AUTO 0x00008000
+#define IF_MODE_RGMII_1000 0x00004000 /* 10 - 1000Mbps RGMII */
+#define IF_MODE_RGMII_100 0x00000000 /* 00 - 100Mbps RGMII */
+#define IF_MODE_RGMII_10 0x00002000 /* 01 - 10Mbps RGMII */
+#define IF_MODE_RGMII_SP_MASK 0x00006000 /* Setsp mask bits */
+#define IF_MODE_RGMII_FD 0x00001000 /* Full duplex RGMII */
+#define IF_MODE_HD 0x00000040 /* Half duplex operation */
+
+/* Hash table Control Register (HASHTABLE_CTRL) */
+#define HASH_CTRL_MCAST_EN 0x00000100
+/* 26-31 Hash table address code */
+#define HASH_CTRL_ADDR_MASK 0x0000003F
+/* MAC mcast indication */
+#define GROUP_ADDRESS 0x0000010000000000LL
+#define HASH_TABLE_SIZE 64 /* Hash tbl size */
+
+/* Interrupt Mask Register (IMASK) */
+#define MEMAC_IMASK_MGI 0x40000000 /* 1 Magic pkt detect indication */
+#define MEMAC_IMASK_TSECC_ER 0x20000000 /* 2 Timestamp FIFO ECC error evnt */
+#define MEMAC_IMASK_TECC_ER 0x02000000 /* 6 Transmit frame ECC error evnt */
+#define MEMAC_IMASK_RECC_ER 0x01000000 /* 7 Receive frame ECC error evnt */
+
+#define MEMAC_ALL_ERRS_IMASK \
+ ((u32)(MEMAC_IMASK_TSECC_ER | \
+ MEMAC_IMASK_TECC_ER | \
+ MEMAC_IMASK_RECC_ER | \
+ MEMAC_IMASK_MGI))
+
+#define MEMAC_IEVNT_PCS 0x80000000 /* PCS (XG). Link sync (G) */
+#define MEMAC_IEVNT_AN 0x40000000 /* Auto-negotiation */
+#define MEMAC_IEVNT_LT 0x20000000 /* Link Training/New page */
+#define MEMAC_IEVNT_MGI 0x00004000 /* Magic pkt detection */
+#define MEMAC_IEVNT_TS_ECC_ER 0x00002000 /* Timestamp FIFO ECC error*/
+#define MEMAC_IEVNT_RX_FIFO_OVFL 0x00001000 /* Rx FIFO overflow */
+#define MEMAC_IEVNT_TX_FIFO_UNFL 0x00000800 /* Tx FIFO underflow */
+#define MEMAC_IEVNT_TX_FIFO_OVFL 0x00000400 /* Tx FIFO overflow */
+#define MEMAC_IEVNT_TX_ECC_ER 0x00000200 /* Tx frame ECC error */
+#define MEMAC_IEVNT_RX_ECC_ER 0x00000100 /* Rx frame ECC error */
+#define MEMAC_IEVNT_LI_FAULT 0x00000080 /* Link Interruption flt */
+#define MEMAC_IEVNT_RX_EMPTY 0x00000040 /* Rx FIFO empty */
+#define MEMAC_IEVNT_TX_EMPTY 0x00000020 /* Tx FIFO empty */
+#define MEMAC_IEVNT_RX_LOWP 0x00000010 /* Low Power Idle */
+#define MEMAC_IEVNT_PHY_LOS 0x00000004 /* Phy loss of signal */
+#define MEMAC_IEVNT_REM_FAULT 0x00000002 /* Remote fault (XGMII) */
+#define MEMAC_IEVNT_LOC_FAULT 0x00000001 /* Local fault (XGMII) */
+
+#define DEFAULT_PAUSE_QUANTA 0xf000
+#define DEFAULT_FRAME_LENGTH 0x600
+#define DEFAULT_TX_IPG_LENGTH 12
+
+#define CLXY_PAUSE_QUANTA_CLX_PQNT 0x0000FFFF
+#define CLXY_PAUSE_QUANTA_CLY_PQNT 0xFFFF0000
+#define CLXY_PAUSE_THRESH_CLX_QTH 0x0000FFFF
+#define CLXY_PAUSE_THRESH_CLY_QTH 0xFFFF0000
+
+struct mac_addr {
+ /* Lower 32 bits of 48-bit MAC address */
+ u32 mac_addr_l;
+ /* Upper 16 bits of 48-bit MAC address */
+ u32 mac_addr_u;
+};
+
+/* memory map */
+struct memac_regs {
+ u32 res0000[2]; /* General Control and Status */
+ u32 command_config; /* 0x008 Ctrl and cfg */
+ struct mac_addr mac_addr0; /* 0x00C-0x010 MAC_ADDR_0...1 */
+ u32 maxfrm; /* 0x014 Max frame length */
+ u32 res0018[1];
+ u32 rx_fifo_sections; /* Receive FIFO configuration reg */
+ u32 tx_fifo_sections; /* Transmit FIFO configuration reg */
+ u32 res0024[2];
+ u32 hashtable_ctrl; /* 0x02C Hash table control */
+ u32 res0030[4];
+ u32 ievent; /* 0x040 Interrupt event */
+ u32 tx_ipg_length; /* 0x044 Transmitter inter-packet-gap */
+ u32 res0048;
+ u32 imask; /* 0x04C Interrupt mask */
+ u32 res0050;
+ u32 pause_quanta[4]; /* 0x054 Pause quanta */
+ u32 pause_thresh[4]; /* 0x064 Pause quanta threshold */
+ u32 rx_pause_status; /* 0x074 Receive pause status */
+ u32 res0078[2];
+ struct mac_addr mac_addr[MEMAC_NUM_OF_PADDRS];/* 0x80-0x0B4 mac padr */
+ u32 lpwake_timer; /* 0x0B8 Low Power Wakeup Timer */
+ u32 sleep_timer; /* 0x0BC Transmit EEE Low Power Timer */
+ u32 res00c0[8];
+ u32 statn_config; /* 0x0E0 Statistics configuration */
+ u32 res00e4[7];
+ /* Rx Statistics Counter */
+ u32 reoct_l;
+ u32 reoct_u;
+ u32 roct_l;
+ u32 roct_u;
+ u32 raln_l;
+ u32 raln_u;
+ u32 rxpf_l;
+ u32 rxpf_u;
+ u32 rfrm_l;
+ u32 rfrm_u;
+ u32 rfcs_l;
+ u32 rfcs_u;
+ u32 rvlan_l;
+ u32 rvlan_u;
+ u32 rerr_l;
+ u32 rerr_u;
+ u32 ruca_l;
+ u32 ruca_u;
+ u32 rmca_l;
+ u32 rmca_u;
+ u32 rbca_l;
+ u32 rbca_u;
+ u32 rdrp_l;
+ u32 rdrp_u;
+ u32 rpkt_l;
+ u32 rpkt_u;
+ u32 rund_l;
+ u32 rund_u;
+ u32 r64_l;
+ u32 r64_u;
+ u32 r127_l;
+ u32 r127_u;
+ u32 r255_l;
+ u32 r255_u;
+ u32 r511_l;
+ u32 r511_u;
+ u32 r1023_l;
+ u32 r1023_u;
+ u32 r1518_l;
+ u32 r1518_u;
+ u32 r1519x_l;
+ u32 r1519x_u;
+ u32 rovr_l;
+ u32 rovr_u;
+ u32 rjbr_l;
+ u32 rjbr_u;
+ u32 rfrg_l;
+ u32 rfrg_u;
+ u32 rcnp_l;
+ u32 rcnp_u;
+ u32 rdrntp_l;
+ u32 rdrntp_u;
+ u32 res01d0[12];
+ /* Tx Statistics Counter */
+ u32 teoct_l;
+ u32 teoct_u;
+ u32 toct_l;
+ u32 toct_u;
+ u32 res0210[2];
+ u32 txpf_l;
+ u32 txpf_u;
+ u32 tfrm_l;
+ u32 tfrm_u;
+ u32 tfcs_l;
+ u32 tfcs_u;
+ u32 tvlan_l;
+ u32 tvlan_u;
+ u32 terr_l;
+ u32 terr_u;
+ u32 tuca_l;
+ u32 tuca_u;
+ u32 tmca_l;
+ u32 tmca_u;
+ u32 tbca_l;
+ u32 tbca_u;
+ u32 res0258[2];
+ u32 tpkt_l;
+ u32 tpkt_u;
+ u32 tund_l;
+ u32 tund_u;
+ u32 t64_l;
+ u32 t64_u;
+ u32 t127_l;
+ u32 t127_u;
+ u32 t255_l;
+ u32 t255_u;
+ u32 t511_l;
+ u32 t511_u;
+ u32 t1023_l;
+ u32 t1023_u;
+ u32 t1518_l;
+ u32 t1518_u;
+ u32 t1519x_l;
+ u32 t1519x_u;
+ u32 res02a8[6];
+ u32 tcnp_l;
+ u32 tcnp_u;
+ u32 res02c8[14];
+ /* Line Interface Control */
+ u32 if_mode; /* 0x300 Interface Mode Control */
+ u32 if_status; /* 0x304 Interface Status */
+ u32 res0308[14];
+ /* HiGig/2 */
+ u32 hg_config; /* 0x340 Control and cfg */
+ u32 res0344[3];
+ u32 hg_pause_quanta; /* 0x350 Pause quanta */
+ u32 res0354[3];
+ u32 hg_pause_thresh; /* 0x360 Pause quanta threshold */
+ u32 res0364[3];
+ u32 hgrx_pause_status; /* 0x370 Receive pause status */
+ u32 hg_fifos_status; /* 0x374 fifos status */
+ u32 rhm; /* 0x378 rx messages counter */
+ u32 thm; /* 0x37C tx messages counter */
+};
+
+struct memac_cfg {
+ bool reset_on_init;
+ bool pause_ignore;
+ bool promiscuous_mode_enable;
+ struct fixed_phy_status *fixed_link;
+ u16 max_frame_length;
+ u16 pause_quanta;
+ u32 tx_ipg_length;
+};
+
+struct fman_mac {
+ /* Pointer to MAC memory mapped registers */
+ struct memac_regs __iomem *regs;
+ /* MAC address of device */
+ u64 addr;
+ /* Ethernet physical interface */
+ phy_interface_t phy_if;
+ u16 max_speed;
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *exception_cb;
+ fman_mac_exception_cb *event_cb;
+ /* Pointer to driver's global address hash table */
+ struct eth_hash_t *multicast_addr_hash;
+ /* Pointer to driver's individual address hash table */
+ struct eth_hash_t *unicast_addr_hash;
+ u8 mac_id;
+ u32 exceptions;
+ struct memac_cfg *memac_drv_param;
+ void *fm;
+ struct fman_rev_info fm_rev_info;
+ bool basex_if;
+ struct phy_device *pcsphy;
+};
+
+static void add_addr_in_paddr(struct memac_regs __iomem *regs, u8 *adr,
+ u8 paddr_num)
+{
+ u32 tmp0, tmp1;
+
+ tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24);
+ tmp1 = (u32)(adr[4] | adr[5] << 8);
+
+ if (paddr_num == 0) {
+ iowrite32be(tmp0, &regs->mac_addr0.mac_addr_l);
+ iowrite32be(tmp1, &regs->mac_addr0.mac_addr_u);
+ } else {
+ iowrite32be(tmp0, &regs->mac_addr[paddr_num - 1].mac_addr_l);
+ iowrite32be(tmp1, &regs->mac_addr[paddr_num - 1].mac_addr_u);
+ }
+}
+
+static int reset(struct memac_regs __iomem *regs)
+{
+ u32 tmp;
+ int count;
+
+ tmp = ioread32be(&regs->command_config);
+
+ tmp |= CMD_CFG_SW_RESET;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ count = 100;
+ do {
+ udelay(1);
+ } while ((ioread32be(&regs->command_config) & CMD_CFG_SW_RESET) &&
+ --count);
+
+ if (count == 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static void set_exception(struct memac_regs __iomem *regs, u32 val,
+ bool enable)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&regs->imask);
+ if (enable)
+ tmp |= val;
+ else
+ tmp &= ~val;
+
+ iowrite32be(tmp, &regs->imask);
+}
+
+static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg,
+ phy_interface_t phy_if, u16 speed, bool slow_10g_if,
+ u32 exceptions)
+{
+ u32 tmp;
+
+ /* Config */
+ tmp = 0;
+ if (cfg->promiscuous_mode_enable)
+ tmp |= CMD_CFG_PROMIS_EN;
+ if (cfg->pause_ignore)
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+
+ /* Payload length check disable */
+ tmp |= CMD_CFG_NO_LEN_CHK;
+ /* Enable padding of frames in transmit direction */
+ tmp |= CMD_CFG_TX_PAD_EN;
+
+ tmp |= CMD_CFG_CRC_FWD;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ /* Max Frame Length */
+ iowrite32be((u32)cfg->max_frame_length, &regs->maxfrm);
+
+ /* Pause Time */
+ iowrite32be((u32)cfg->pause_quanta, &regs->pause_quanta[0]);
+ iowrite32be((u32)0, &regs->pause_thresh[0]);
+
+ /* IF_MODE */
+ tmp = 0;
+ switch (phy_if) {
+ case PHY_INTERFACE_MODE_XGMII:
+ tmp |= IF_MODE_XGMII;
+ break;
+ default:
+ tmp |= IF_MODE_GMII;
+ if (phy_if == PHY_INTERFACE_MODE_RGMII)
+ tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO;
+ }
+ iowrite32be(tmp, &regs->if_mode);
+
+ /* TX_FIFO_SECTIONS */
+ tmp = 0;
+ if (phy_if == PHY_INTERFACE_MODE_XGMII) {
+ if (slow_10g_if) {
+ tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G |
+ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G);
+ } else {
+ tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G |
+ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G);
+ }
+ } else {
+ tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G |
+ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G);
+ }
+ iowrite32be(tmp, &regs->tx_fifo_sections);
+
+ /* clear all pending events and set-up interrupts */
+ iowrite32be(0xffffffff, &regs->ievent);
+ set_exception(regs, exceptions, true);
+
+ return 0;
+}
+
+static void set_dflts(struct memac_cfg *cfg)
+{
+ cfg->reset_on_init = false;
+ cfg->promiscuous_mode_enable = false;
+ cfg->pause_ignore = false;
+ cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH;
+ cfg->max_frame_length = DEFAULT_FRAME_LENGTH;
+ cfg->pause_quanta = DEFAULT_PAUSE_QUANTA;
+}
+
+static u32 get_mac_addr_hash_code(u64 eth_addr)
+{
+ u64 mask1, mask2;
+ u32 xor_val = 0;
+ u8 i, j;
+
+ for (i = 0; i < 6; i++) {
+ mask1 = eth_addr & (u64)0x01;
+ eth_addr >>= 1;
+
+ for (j = 0; j < 7; j++) {
+ mask2 = eth_addr & (u64)0x01;
+ mask1 ^= mask2;
+ eth_addr >>= 1;
+ }
+
+ xor_val |= (mask1 << (5 - i));
+ }
+
+ return xor_val;
+}
+
+static void setup_sgmii_internal_phy(struct fman_mac *memac,
+ struct fixed_phy_status *fixed_link)
+{
+ u16 tmp_reg16;
+
+ /* SGMII mode */
+ tmp_reg16 = IF_MODE_SGMII_EN;
+ if (!fixed_link)
+ /* AN enable */
+ tmp_reg16 |= IF_MODE_USE_SGMII_AN;
+ else {
+ switch (fixed_link->speed) {
+ case 10:
+ /* For 10M: IF_MODE[SPEED_10M] = 0 */
+ break;
+ case 100:
+ tmp_reg16 |= IF_MODE_SGMII_SPEED_100M;
+ break;
+ case 1000: /* fallthrough */
+ default:
+ tmp_reg16 |= IF_MODE_SGMII_SPEED_1G;
+ break;
+ }
+ if (!fixed_link->duplex)
+ tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF;
+ }
+ phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16);
+
+ /* Device ability according to SGMII specification */
+ tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE;
+ phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
+
+ /* Adjust link timer for SGMII -
+ * According to Cisco SGMII specification the timer should be 1.6 ms.
+ * The link_timer register is configured in units of the clock.
+ * - When running as 1G SGMII, Serdes clock is 125 MHz, so
+ * unit = 1 / (125*10^6 Hz) = 8 ns.
+ * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40
+ * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
+ * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
+ * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120.
+ * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
+ * we always set up here a value of 2.5 SGMII.
+ */
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H);
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L);
+
+ if (!fixed_link)
+ /* Restart AN */
+ tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
+ else
+ /* AN disabled */
+ tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN;
+ phy_write(memac->pcsphy, 0x0, tmp_reg16);
+}
+
+static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac)
+{
+ u16 tmp_reg16;
+
+ /* AN Device capability */
+ tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE;
+ phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
+
+ /* Adjust link timer for SGMII -
+ * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms.
+ * The link_timer register is configured in units of the clock.
+ * - When running as 1G SGMII, Serdes clock is 125 MHz, so
+ * unit = 1 / (125*10^6 Hz) = 8 ns.
+ * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0
+ * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
+ * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
+ * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08.
+ * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
+ * we always set up here a value of 2.5 SGMII.
+ */
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX);
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX);
+
+ /* Restart AN */
+ tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
+ phy_write(memac->pcsphy, 0x0, tmp_reg16);
+}
+
+static int check_init_parameters(struct fman_mac *memac)
+{
+ if (memac->addr == 0) {
+ pr_err("Ethernet MAC must have a valid MAC address\n");
+ return -EINVAL;
+ }
+ if (!memac->exception_cb) {
+ pr_err("Uninitialized exception handler\n");
+ return -EINVAL;
+ }
+ if (!memac->event_cb) {
+ pr_warn("Uninitialize event handler\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_exception_flag(enum fman_mac_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FM_MAC_EX_10G_TX_ECC_ER:
+ bit_mask = MEMAC_IMASK_TECC_ER;
+ break;
+ case FM_MAC_EX_10G_RX_ECC_ER:
+ bit_mask = MEMAC_IMASK_RECC_ER;
+ break;
+ case FM_MAC_EX_TS_FIFO_ECC_ERR:
+ bit_mask = MEMAC_IMASK_TSECC_ER;
+ break;
+ case FM_MAC_EX_MAGIC_PACKET_INDICATION:
+ bit_mask = MEMAC_IMASK_MGI;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static void memac_err_exception(void *handle)
+{
+ struct fman_mac *memac = (struct fman_mac *)handle;
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 event, imask;
+
+ event = ioread32be(&regs->ievent);
+ imask = ioread32be(&regs->imask);
+
+ /* Imask include both error and notification/event bits.
+ * Leaving only error bits enabled by imask.
+ * The imask error bits are shifted by 16 bits offset from
+ * their corresponding location in the ievent - hence the >> 16
+ */
+ event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & MEMAC_IEVNT_TS_ECC_ER)
+ memac->exception_cb(memac->dev_id, FM_MAC_EX_TS_FIFO_ECC_ERR);
+ if (event & MEMAC_IEVNT_TX_ECC_ER)
+ memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_TX_ECC_ER);
+ if (event & MEMAC_IEVNT_RX_ECC_ER)
+ memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_RX_ECC_ER);
+}
+
+static void memac_exception(void *handle)
+{
+ struct fman_mac *memac = (struct fman_mac *)handle;
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 event, imask;
+
+ event = ioread32be(&regs->ievent);
+ imask = ioread32be(&regs->imask);
+
+ /* Imask include both error and notification/event bits.
+ * Leaving only error bits enabled by imask.
+ * The imask error bits are shifted by 16 bits offset from
+ * their corresponding location in the ievent - hence the >> 16
+ */
+ event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & MEMAC_IEVNT_MGI)
+ memac->exception_cb(memac->dev_id,
+ FM_MAC_EX_MAGIC_PACKET_INDICATION);
+}
+
+static void free_init_resources(struct fman_mac *memac)
+{
+ fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_ERR);
+
+ fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_NORMAL);
+
+ /* release the driver's group hash table */
+ free_hash_table(memac->multicast_addr_hash);
+ memac->multicast_addr_hash = NULL;
+
+ /* release the driver's individual hash table */
+ free_hash_table(memac->unicast_addr_hash);
+ memac->unicast_addr_hash = NULL;
+}
+
+static bool is_init_done(struct memac_cfg *memac_drv_params)
+{
+ /* Checks if mEMAC driver parameters were initialized */
+ if (!memac_drv_params)
+ return true;
+
+ return false;
+}
+
+int memac_enable(struct fman_mac *memac, enum comm_mode mode)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp |= CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp |= CMD_CFG_TX_EN;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_disable(struct fman_mac *memac, enum comm_mode mode)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp &= ~CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp &= ~CMD_CFG_TX_EN;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_set_promiscuous(struct fman_mac *memac, bool new_val)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (new_val)
+ tmp |= CMD_CFG_PROMIS_EN;
+ else
+ tmp &= ~CMD_CFG_PROMIS_EN;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_adjust_link(struct fman_mac *memac, u16 speed)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->if_mode);
+
+ /* Set full duplex */
+ tmp &= ~IF_MODE_HD;
+
+ if (memac->phy_if == PHY_INTERFACE_MODE_RGMII) {
+ /* Configure RGMII in manual mode */
+ tmp &= ~IF_MODE_RGMII_AUTO;
+ tmp &= ~IF_MODE_RGMII_SP_MASK;
+ /* Full duplex */
+ tmp |= IF_MODE_RGMII_FD;
+
+ switch (speed) {
+ case SPEED_1000:
+ tmp |= IF_MODE_RGMII_1000;
+ break;
+ case SPEED_100:
+ tmp |= IF_MODE_RGMII_100;
+ break;
+ case SPEED_10:
+ tmp |= IF_MODE_RGMII_10;
+ break;
+ default:
+ break;
+ }
+ }
+
+ iowrite32be(tmp, &regs->if_mode);
+
+ return 0;
+}
+
+int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val)
+{
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ memac->memac_drv_param->max_frame_length = new_val;
+
+ return 0;
+}
+
+int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable)
+{
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ memac->memac_drv_param->reset_on_init = enable;
+
+ return 0;
+}
+
+int memac_cfg_fixed_link(struct fman_mac *memac,
+ struct fixed_phy_status *fixed_link)
+{
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ memac->memac_drv_param->fixed_link = fixed_link;
+
+ return 0;
+}
+
+int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority,
+ u16 pause_time, u16 thresh_time)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->tx_fifo_sections);
+
+ GET_TX_EMPTY_DEFAULT_VALUE(tmp);
+ iowrite32be(tmp, &regs->tx_fifo_sections);
+
+ tmp = ioread32be(&regs->command_config);
+ tmp &= ~CMD_CFG_PFC_MODE;
+ priority = 0;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ tmp = ioread32be(&regs->pause_quanta[priority / 2]);
+ if (priority % 2)
+ tmp &= CLXY_PAUSE_QUANTA_CLX_PQNT;
+ else
+ tmp &= CLXY_PAUSE_QUANTA_CLY_PQNT;
+ tmp |= ((u32)pause_time << (16 * (priority % 2)));
+ iowrite32be(tmp, &regs->pause_quanta[priority / 2]);
+
+ tmp = ioread32be(&regs->pause_thresh[priority / 2]);
+ if (priority % 2)
+ tmp &= CLXY_PAUSE_THRESH_CLX_QTH;
+ else
+ tmp &= CLXY_PAUSE_THRESH_CLY_QTH;
+ tmp |= ((u32)thresh_time << (16 * (priority % 2)));
+ iowrite32be(tmp, &regs->pause_thresh[priority / 2]);
+
+ return 0;
+}
+
+int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (en)
+ tmp &= ~CMD_CFG_PAUSE_IGNORE;
+ else
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr)
+{
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ add_addr_in_paddr(memac->regs, (u8 *)(*enet_addr), 0);
+
+ return 0;
+}
+
+int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ struct eth_hash_entry *hash_entry;
+ u32 hash;
+ u64 addr;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ if (!(addr & GROUP_ADDRESS)) {
+ /* Unicast addresses not supported in hash */
+ pr_err("Unicast Address\n");
+ return -EINVAL;
+ }
+ hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK;
+
+ /* Create element to be added to the driver hash table */
+ hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL);
+ if (!hash_entry)
+ return -ENOMEM;
+ hash_entry->addr = addr;
+ INIT_LIST_HEAD(&hash_entry->node);
+
+ list_add_tail(&hash_entry->node,
+ &memac->multicast_addr_hash->lsts[hash]);
+ iowrite32be(hash | HASH_CTRL_MCAST_EN, &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ struct eth_hash_entry *hash_entry = NULL;
+ struct list_head *pos;
+ u32 hash;
+ u64 addr;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK;
+
+ list_for_each(pos, &memac->multicast_addr_hash->lsts[hash]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&memac->multicast_addr_hash->lsts[hash]))
+ iowrite32be(hash & ~HASH_CTRL_MCAST_EN, &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int memac_set_exception(struct fman_mac *memac,
+ enum fman_mac_exceptions exception, bool enable)
+{
+ u32 bit_mask = 0;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ memac->exceptions |= bit_mask;
+ else
+ memac->exceptions &= ~bit_mask;
+ } else {
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ set_exception(memac->regs, bit_mask, enable);
+
+ return 0;
+}
+
+int memac_init(struct fman_mac *memac)
+{
+ struct memac_cfg *memac_drv_param;
+ u8 i;
+ enet_addr_t eth_addr;
+ bool slow_10g_if = false;
+ struct fixed_phy_status *fixed_link;
+ int err;
+ u32 reg32 = 0;
+
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ err = check_init_parameters(memac);
+ if (err)
+ return err;
+
+ memac_drv_param = memac->memac_drv_param;
+
+ if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4)
+ slow_10g_if = true;
+
+ /* First, reset the MAC if desired. */
+ if (memac_drv_param->reset_on_init) {
+ err = reset(memac->regs);
+ if (err) {
+ pr_err("mEMAC reset failed\n");
+ return err;
+ }
+ }
+
+ /* MAC Address */
+ MAKE_ENET_ADDR_FROM_UINT64(memac->addr, eth_addr);
+ add_addr_in_paddr(memac->regs, (u8 *)eth_addr, 0);
+
+ fixed_link = memac_drv_param->fixed_link;
+
+ init(memac->regs, memac->memac_drv_param, memac->phy_if,
+ memac->max_speed, slow_10g_if, memac->exceptions);
+
+ /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround
+ * Exists only in FMan 6.0 and 6.3.
+ */
+ if ((memac->fm_rev_info.major == 6) &&
+ ((memac->fm_rev_info.minor == 0) ||
+ (memac->fm_rev_info.minor == 3))) {
+ /* MAC strips CRC from received frames - this workaround
+ * should decrease the likelihood of bug appearance
+ */
+ reg32 = ioread32be(&memac->regs->command_config);
+ reg32 &= ~CMD_CFG_CRC_FWD;
+ iowrite32be(reg32, &memac->regs->command_config);
+ }
+
+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) {
+ /* Configure internal SGMII PHY */
+ if (memac->basex_if)
+ setup_sgmii_internal_phy_base_x(memac);
+ else
+ setup_sgmii_internal_phy(memac, fixed_link);
+ } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
+ /* Configure 4 internal SGMII PHYs */
+ for (i = 0; i < 4; i++) {
+ u8 qsmgii_phy_addr, phy_addr;
+ /* QSGMII PHY address occupies 3 upper bits of 5-bit
+ * phy_address; the lower 2 bits are used to extend
+ * register address space and access each one of 4
+ * ports inside QSGMII.
+ */
+ phy_addr = memac->pcsphy->mdio.addr;
+ qsmgii_phy_addr = (u8)((phy_addr << 2) | i);
+ memac->pcsphy->mdio.addr = qsmgii_phy_addr;
+ if (memac->basex_if)
+ setup_sgmii_internal_phy_base_x(memac);
+ else
+ setup_sgmii_internal_phy(memac, fixed_link);
+
+ memac->pcsphy->mdio.addr = phy_addr;
+ }
+ }
+
+ /* Max Frame Length */
+ err = fman_set_mac_max_frame(memac->fm, memac->mac_id,
+ memac_drv_param->max_frame_length);
+ if (err) {
+ pr_err("settings Mac max frame length is FAILED\n");
+ return err;
+ }
+
+ memac->multicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE);
+ if (!memac->multicast_addr_hash) {
+ free_init_resources(memac);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ memac->unicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE);
+ if (!memac->unicast_addr_hash) {
+ free_init_resources(memac);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_ERR, memac_err_exception, memac);
+
+ fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_NORMAL, memac_exception, memac);
+
+ kfree(memac_drv_param);
+ memac->memac_drv_param = NULL;
+
+ return 0;
+}
+
+int memac_free(struct fman_mac *memac)
+{
+ free_init_resources(memac);
+
+ kfree(memac->memac_drv_param);
+ kfree(memac);
+
+ return 0;
+}
+
+struct fman_mac *memac_config(struct fman_mac_params *params)
+{
+ struct fman_mac *memac;
+ struct memac_cfg *memac_drv_param;
+ void __iomem *base_addr;
+
+ base_addr = params->base_addr;
+ /* allocate memory for the m_emac data structure */
+ memac = kzalloc(sizeof(*memac), GFP_KERNEL);
+ if (!memac)
+ return NULL;
+
+ /* allocate memory for the m_emac driver parameters data structure */
+ memac_drv_param = kzalloc(sizeof(*memac_drv_param), GFP_KERNEL);
+ if (!memac_drv_param) {
+ memac_free(memac);
+ return NULL;
+ }
+
+ /* Plant parameter structure pointer */
+ memac->memac_drv_param = memac_drv_param;
+
+ set_dflts(memac_drv_param);
+
+ memac->addr = ENET_ADDR_TO_UINT64(params->addr);
+
+ memac->regs = base_addr;
+ memac->max_speed = params->max_speed;
+ memac->phy_if = params->phy_if;
+ memac->mac_id = params->mac_id;
+ memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER |
+ MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI);
+ memac->exception_cb = params->exception_cb;
+ memac->event_cb = params->event_cb;
+ memac->dev_id = params->dev_id;
+ memac->fm = params->fm;
+ memac->basex_if = params->basex_if;
+
+ /* Save FMan revision */
+ fman_get_revision(memac->fm, &memac->fm_rev_info);
+
+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) {
+ if (!params->internal_phy_node) {
+ pr_err("PCS PHY node is not available\n");
+ memac_free(memac);
+ return NULL;
+ }
+
+ memac->pcsphy = of_phy_find_device(params->internal_phy_node);
+ if (!memac->pcsphy) {
+ pr_err("of_phy_find_device (PCS PHY) failed\n");
+ memac_free(memac);
+ return NULL;
+ }
+ }
+
+ return memac;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h
new file mode 100644
index 000000000000..173d8e0fd716
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEMAC_H
+#define __MEMAC_H
+
+#include "fman_mac.h"
+
+#include <linux/netdevice.h>
+
+struct fman_mac *memac_config(struct fman_mac_params *params);
+int memac_set_promiscuous(struct fman_mac *memac, bool new_val);
+int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr);
+int memac_adjust_link(struct fman_mac *memac, u16 speed);
+int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val);
+int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable);
+int memac_cfg_fixed_link(struct fman_mac *memac,
+ struct fixed_phy_status *fixed_link);
+int memac_enable(struct fman_mac *memac, enum comm_mode mode);
+int memac_disable(struct fman_mac *memac, enum comm_mode mode);
+int memac_init(struct fman_mac *memac);
+int memac_free(struct fman_mac *memac);
+int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en);
+int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority,
+ u16 pause_time, u16 thresh_time);
+int memac_set_exception(struct fman_mac *memac,
+ enum fman_mac_exceptions exception, bool enable);
+int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr);
+int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr);
+
+#endif /* __MEMAC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.c b/drivers/net/ethernet/freescale/fman/fman_muram.c
new file mode 100644
index 000000000000..4eb0e9ac7182
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_muram.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fman_muram.h"
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/genalloc.h>
+
+struct muram_info {
+ struct gen_pool *pool;
+ void __iomem *vbase;
+ size_t size;
+ phys_addr_t pbase;
+};
+
+static unsigned long fman_muram_vbase_to_offset(struct muram_info *muram,
+ unsigned long vaddr)
+{
+ return vaddr - (unsigned long)muram->vbase;
+}
+
+/**
+ * fman_muram_init
+ * @base: Pointer to base of memory mapped FM-MURAM.
+ * @size: Size of the FM-MURAM partition.
+ *
+ * Creates partition in the MURAM.
+ * The routine returns a pointer to the MURAM partition.
+ * This pointer must be passed as to all other FM-MURAM function calls.
+ * No actual initialization or configuration of FM_MURAM hardware is done by
+ * this routine.
+ *
+ * Return: pointer to FM-MURAM object, or NULL for Failure.
+ */
+struct muram_info *fman_muram_init(phys_addr_t base, size_t size)
+{
+ struct muram_info *muram;
+ void __iomem *vaddr;
+ int ret;
+
+ muram = kzalloc(sizeof(*muram), GFP_KERNEL);
+ if (!muram)
+ return NULL;
+
+ muram->pool = gen_pool_create(ilog2(64), -1);
+ if (!muram->pool) {
+ pr_err("%s(): MURAM pool create failed\n", __func__);
+ goto muram_free;
+ }
+
+ vaddr = ioremap(base, size);
+ if (!vaddr) {
+ pr_err("%s(): MURAM ioremap failed\n", __func__);
+ goto pool_destroy;
+ }
+
+ ret = gen_pool_add_virt(muram->pool, (unsigned long)vaddr,
+ base, size, -1);
+ if (ret < 0) {
+ pr_err("%s(): MURAM pool add failed\n", __func__);
+ iounmap(vaddr);
+ goto pool_destroy;
+ }
+
+ memset_io(vaddr, 0, (int)size);
+
+ muram->vbase = vaddr;
+ muram->pbase = base;
+ return muram;
+
+pool_destroy:
+ gen_pool_destroy(muram->pool);
+muram_free:
+ kfree(muram);
+ return NULL;
+}
+
+/**
+ * fman_muram_offset_to_vbase
+ * @muram: FM-MURAM module pointer.
+ * @offset: the offset of the memory block
+ *
+ * Gives the address of the memory region from specific offset
+ *
+ * Return: The address of the memory block
+ */
+unsigned long fman_muram_offset_to_vbase(struct muram_info *muram,
+ unsigned long offset)
+{
+ return offset + (unsigned long)muram->vbase;
+}
+
+/**
+ * fman_muram_alloc
+ * @muram: FM-MURAM module pointer.
+ * @size: Size of the memory to be allocated.
+ *
+ * Allocate some memory from FM-MURAM partition.
+ *
+ * Return: address of the allocated memory; NULL otherwise.
+ */
+int fman_muram_alloc(struct muram_info *muram, size_t size)
+{
+ unsigned long vaddr;
+
+ vaddr = gen_pool_alloc(muram->pool, size);
+ if (!vaddr)
+ return -ENOMEM;
+
+ memset_io((void __iomem *)vaddr, 0, size);
+
+ return fman_muram_vbase_to_offset(muram, vaddr);
+}
+
+/**
+ * fman_muram_free_mem
+ * muram: FM-MURAM module pointer.
+ * offset: offset of the memory region to be freed.
+ * size: size of the memory to be freed.
+ *
+ * Free an allocated memory from FM-MURAM partition.
+ */
+void fman_muram_free_mem(struct muram_info *muram, u32 offset, size_t size)
+{
+ unsigned long addr = fman_muram_offset_to_vbase(muram, offset);
+
+ gen_pool_free(muram->pool, addr, size);
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.h b/drivers/net/ethernet/freescale/fman/fman_muram.h
new file mode 100644
index 000000000000..dbf0af9e5bb5
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_muram.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __FM_MURAM_EXT
+#define __FM_MURAM_EXT
+
+#include <linux/types.h>
+
+#define FM_MURAM_INVALID_ALLOCATION -1
+
+/* Structure for FM MURAM information */
+struct muram_info;
+
+struct muram_info *fman_muram_init(phys_addr_t base, size_t size);
+
+unsigned long fman_muram_offset_to_vbase(struct muram_info *muram,
+ unsigned long offset);
+
+int fman_muram_alloc(struct muram_info *muram, size_t size);
+
+void fman_muram_free_mem(struct muram_info *muram, u32 offset, size_t size);
+
+#endif /* __FM_MURAM_EXT */
diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c
new file mode 100644
index 000000000000..70c198d072dc
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_port.c
@@ -0,0 +1,1778 @@
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_port.h"
+#include "fman.h"
+#include "fman_sp.h"
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/libfdt_env.h>
+
+/* Queue ID */
+#define DFLT_FQ_ID 0x00FFFFFF
+
+/* General defines */
+#define PORT_BMI_FIFO_UNITS 0x100
+
+#define MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) \
+ min((u32)bmi_max_fifo_size, (u32)1024 * FMAN_BMI_FIFO_UNITS)
+
+#define PORT_CG_MAP_NUM 8
+#define PORT_PRS_RESULT_WORDS_NUM 8
+#define PORT_IC_OFFSET_UNITS 0x10
+
+#define MIN_EXT_BUF_SIZE 64
+
+#define BMI_PORT_REGS_OFFSET 0
+#define QMI_PORT_REGS_OFFSET 0x400
+
+/* Default values */
+#define DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN \
+ DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN
+
+#define DFLT_PORT_CUT_BYTES_FROM_END 4
+
+#define DFLT_PORT_ERRORS_TO_DISCARD FM_PORT_FRM_ERR_CLS_DISCARD
+#define DFLT_PORT_MAX_FRAME_LENGTH 9600
+
+#define DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(bmi_max_fifo_size) \
+ MAX_PORT_FIFO_SIZE(bmi_max_fifo_size)
+
+#define DFLT_PORT_RX_FIFO_THRESHOLD(major, bmi_max_fifo_size) \
+ (major == 6 ? \
+ MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) : \
+ (MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) * 3 / 4)) \
+
+#define DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS 0
+
+/* QMI defines */
+#define QMI_DEQ_CFG_SUBPORTAL_MASK 0x1f
+
+#define QMI_PORT_CFG_EN 0x80000000
+#define QMI_PORT_STATUS_DEQ_FD_BSY 0x20000000
+
+#define QMI_DEQ_CFG_PRI 0x80000000
+#define QMI_DEQ_CFG_TYPE1 0x10000000
+#define QMI_DEQ_CFG_TYPE2 0x20000000
+#define QMI_DEQ_CFG_TYPE3 0x30000000
+#define QMI_DEQ_CFG_PREFETCH_PARTIAL 0x01000000
+#define QMI_DEQ_CFG_PREFETCH_FULL 0x03000000
+#define QMI_DEQ_CFG_SP_MASK 0xf
+#define QMI_DEQ_CFG_SP_SHIFT 20
+
+#define QMI_BYTE_COUNT_LEVEL_CONTROL(_type) \
+ (_type == FMAN_PORT_TYPE_TX ? 0x1400 : 0x400)
+
+/* BMI defins */
+#define BMI_EBD_EN 0x80000000
+
+#define BMI_PORT_CFG_EN 0x80000000
+
+#define BMI_PORT_STATUS_BSY 0x80000000
+
+#define BMI_DMA_ATTR_SWP_SHIFT FMAN_SP_DMA_ATTR_SWP_SHIFT
+#define BMI_DMA_ATTR_WRITE_OPTIMIZE FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE
+
+#define BMI_RX_FIFO_PRI_ELEVATION_SHIFT 16
+#define BMI_RX_FIFO_THRESHOLD_ETHE 0x80000000
+
+#define BMI_FRAME_END_CS_IGNORE_SHIFT 24
+#define BMI_FRAME_END_CS_IGNORE_MASK 0x0000001f
+
+#define BMI_RX_FRAME_END_CUT_SHIFT 16
+#define BMI_RX_FRAME_END_CUT_MASK 0x0000001f
+
+#define BMI_IC_TO_EXT_SHIFT FMAN_SP_IC_TO_EXT_SHIFT
+#define BMI_IC_TO_EXT_MASK 0x0000001f
+#define BMI_IC_FROM_INT_SHIFT FMAN_SP_IC_FROM_INT_SHIFT
+#define BMI_IC_FROM_INT_MASK 0x0000000f
+#define BMI_IC_SIZE_MASK 0x0000001f
+
+#define BMI_INT_BUF_MARG_SHIFT 28
+#define BMI_INT_BUF_MARG_MASK 0x0000000f
+#define BMI_EXT_BUF_MARG_START_SHIFT FMAN_SP_EXT_BUF_MARG_START_SHIFT
+#define BMI_EXT_BUF_MARG_START_MASK 0x000001ff
+#define BMI_EXT_BUF_MARG_END_MASK 0x000001ff
+
+#define BMI_CMD_MR_LEAC 0x00200000
+#define BMI_CMD_MR_SLEAC 0x00100000
+#define BMI_CMD_MR_MA 0x00080000
+#define BMI_CMD_MR_DEAS 0x00040000
+#define BMI_CMD_RX_MR_DEF (BMI_CMD_MR_LEAC | \
+ BMI_CMD_MR_SLEAC | \
+ BMI_CMD_MR_MA | \
+ BMI_CMD_MR_DEAS)
+#define BMI_CMD_TX_MR_DEF 0
+
+#define BMI_CMD_ATTR_ORDER 0x80000000
+#define BMI_CMD_ATTR_SYNC 0x02000000
+#define BMI_CMD_ATTR_COLOR_SHIFT 26
+
+#define BMI_FIFO_PIPELINE_DEPTH_SHIFT 12
+#define BMI_FIFO_PIPELINE_DEPTH_MASK 0x0000000f
+#define BMI_NEXT_ENG_FD_BITS_SHIFT 24
+
+#define BMI_EXT_BUF_POOL_VALID FMAN_SP_EXT_BUF_POOL_VALID
+#define BMI_EXT_BUF_POOL_EN_COUNTER FMAN_SP_EXT_BUF_POOL_EN_COUNTER
+#define BMI_EXT_BUF_POOL_BACKUP FMAN_SP_EXT_BUF_POOL_BACKUP
+#define BMI_EXT_BUF_POOL_ID_SHIFT 16
+#define BMI_EXT_BUF_POOL_ID_MASK 0x003F0000
+#define BMI_POOL_DEP_NUM_OF_POOLS_SHIFT 16
+
+#define BMI_TX_FIFO_MIN_FILL_SHIFT 16
+
+#define BMI_PRIORITY_ELEVATION_LEVEL ((0x3FF + 1) * PORT_BMI_FIFO_UNITS)
+#define BMI_FIFO_THRESHOLD ((0x3FF + 1) * PORT_BMI_FIFO_UNITS)
+
+#define BMI_DEQUEUE_PIPELINE_DEPTH(_type, _speed) \
+ ((_type == FMAN_PORT_TYPE_TX && _speed == 10000) ? 4 : 1)
+
+#define RX_ERRS_TO_ENQ \
+ (FM_PORT_FRM_ERR_DMA | \
+ FM_PORT_FRM_ERR_PHYSICAL | \
+ FM_PORT_FRM_ERR_SIZE | \
+ FM_PORT_FRM_ERR_EXTRACTION | \
+ FM_PORT_FRM_ERR_NO_SCHEME | \
+ FM_PORT_FRM_ERR_PRS_TIMEOUT | \
+ FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT | \
+ FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED | \
+ FM_PORT_FRM_ERR_PRS_HDR_ERR | \
+ FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW | \
+ FM_PORT_FRM_ERR_IPRE)
+
+/* NIA defines */
+#define NIA_ORDER_RESTOR 0x00800000
+#define NIA_ENG_BMI 0x00500000
+#define NIA_ENG_QMI_ENQ 0x00540000
+#define NIA_ENG_QMI_DEQ 0x00580000
+
+#define NIA_BMI_AC_ENQ_FRAME 0x00000002
+#define NIA_BMI_AC_TX_RELEASE 0x000002C0
+#define NIA_BMI_AC_RELEASE 0x000000C0
+#define NIA_BMI_AC_TX 0x00000274
+#define NIA_BMI_AC_FETCH_ALL_FRAME 0x0000020c
+
+/* Port IDs */
+#define TX_10G_PORT_BASE 0x30
+#define RX_10G_PORT_BASE 0x10
+
+/* BMI Rx port register map */
+struct fman_port_rx_bmi_regs {
+ u32 fmbm_rcfg; /* Rx Configuration */
+ u32 fmbm_rst; /* Rx Status */
+ u32 fmbm_rda; /* Rx DMA attributes */
+ u32 fmbm_rfp; /* Rx FIFO Parameters */
+ u32 fmbm_rfed; /* Rx Frame End Data */
+ u32 fmbm_ricp; /* Rx Internal Context Parameters */
+ u32 fmbm_rim; /* Rx Internal Buffer Margins */
+ u32 fmbm_rebm; /* Rx External Buffer Margins */
+ u32 fmbm_rfne; /* Rx Frame Next Engine */
+ u32 fmbm_rfca; /* Rx Frame Command Attributes. */
+ u32 fmbm_rfpne; /* Rx Frame Parser Next Engine */
+ u32 fmbm_rpso; /* Rx Parse Start Offset */
+ u32 fmbm_rpp; /* Rx Policer Profile */
+ u32 fmbm_rccb; /* Rx Coarse Classification Base */
+ u32 fmbm_reth; /* Rx Excessive Threshold */
+ u32 reserved003c[1]; /* (0x03C 0x03F) */
+ u32 fmbm_rprai[PORT_PRS_RESULT_WORDS_NUM];
+ /* Rx Parse Results Array Init */
+ u32 fmbm_rfqid; /* Rx Frame Queue ID */
+ u32 fmbm_refqid; /* Rx Error Frame Queue ID */
+ u32 fmbm_rfsdm; /* Rx Frame Status Discard Mask */
+ u32 fmbm_rfsem; /* Rx Frame Status Error Mask */
+ u32 fmbm_rfene; /* Rx Frame Enqueue Next Engine */
+ u32 reserved0074[0x2]; /* (0x074-0x07C) */
+ u32 fmbm_rcmne; /* Rx Frame Continuous Mode Next Engine */
+ u32 reserved0080[0x20]; /* (0x080 0x0FF) */
+ u32 fmbm_ebmpi[FMAN_PORT_MAX_EXT_POOLS_NUM];
+ /* Buffer Manager pool Information- */
+ u32 fmbm_acnt[FMAN_PORT_MAX_EXT_POOLS_NUM]; /* Allocate Counter- */
+ u32 reserved0130[8]; /* 0x130/0x140 - 0x15F reserved - */
+ u32 fmbm_rcgm[PORT_CG_MAP_NUM]; /* Congestion Group Map */
+ u32 fmbm_mpd; /* BM Pool Depletion */
+ u32 reserved0184[0x1F]; /* (0x184 0x1FF) */
+ u32 fmbm_rstc; /* Rx Statistics Counters */
+ u32 fmbm_rfrc; /* Rx Frame Counter */
+ u32 fmbm_rfbc; /* Rx Bad Frames Counter */
+ u32 fmbm_rlfc; /* Rx Large Frames Counter */
+ u32 fmbm_rffc; /* Rx Filter Frames Counter */
+ u32 fmbm_rfdc; /* Rx Frame Discard Counter */
+ u32 fmbm_rfldec; /* Rx Frames List DMA Error Counter */
+ u32 fmbm_rodc; /* Rx Out of Buffers Discard nntr */
+ u32 fmbm_rbdc; /* Rx Buffers Deallocate Counter */
+ u32 fmbm_rpec; /* RX Prepare to enqueue Counte */
+ u32 reserved0224[0x16]; /* (0x224 0x27F) */
+ u32 fmbm_rpc; /* Rx Performance Counters */
+ u32 fmbm_rpcp; /* Rx Performance Count Parameters */
+ u32 fmbm_rccn; /* Rx Cycle Counter */
+ u32 fmbm_rtuc; /* Rx Tasks Utilization Counter */
+ u32 fmbm_rrquc; /* Rx Receive Queue Utilization cntr */
+ u32 fmbm_rduc; /* Rx DMA Utilization Counter */
+ u32 fmbm_rfuc; /* Rx FIFO Utilization Counter */
+ u32 fmbm_rpac; /* Rx Pause Activation Counter */
+ u32 reserved02a0[0x18]; /* (0x2A0 0x2FF) */
+ u32 fmbm_rdcfg[0x3]; /* Rx Debug Configuration */
+ u32 fmbm_rgpr; /* Rx General Purpose Register */
+ u32 reserved0310[0x3a];
+};
+
+/* BMI Tx port register map */
+struct fman_port_tx_bmi_regs {
+ u32 fmbm_tcfg; /* Tx Configuration */
+ u32 fmbm_tst; /* Tx Status */
+ u32 fmbm_tda; /* Tx DMA attributes */
+ u32 fmbm_tfp; /* Tx FIFO Parameters */
+ u32 fmbm_tfed; /* Tx Frame End Data */
+ u32 fmbm_ticp; /* Tx Internal Context Parameters */
+ u32 fmbm_tfdne; /* Tx Frame Dequeue Next Engine. */
+ u32 fmbm_tfca; /* Tx Frame Command attribute. */
+ u32 fmbm_tcfqid; /* Tx Confirmation Frame Queue ID. */
+ u32 fmbm_tefqid; /* Tx Frame Error Queue ID */
+ u32 fmbm_tfene; /* Tx Frame Enqueue Next Engine */
+ u32 fmbm_trlmts; /* Tx Rate Limiter Scale */
+ u32 fmbm_trlmt; /* Tx Rate Limiter */
+ u32 reserved0034[0x0e]; /* (0x034-0x6c) */
+ u32 fmbm_tccb; /* Tx Coarse Classification base */
+ u32 fmbm_tfne; /* Tx Frame Next Engine */
+ u32 fmbm_tpfcm[0x02];
+ /* Tx Priority based Flow Control (PFC) Mapping */
+ u32 fmbm_tcmne; /* Tx Frame Continuous Mode Next Engine */
+ u32 reserved0080[0x60]; /* (0x080-0x200) */
+ u32 fmbm_tstc; /* Tx Statistics Counters */
+ u32 fmbm_tfrc; /* Tx Frame Counter */
+ u32 fmbm_tfdc; /* Tx Frames Discard Counter */
+ u32 fmbm_tfledc; /* Tx Frame len error discard cntr */
+ u32 fmbm_tfufdc; /* Tx Frame unsprt frmt discard cntr */
+ u32 fmbm_tbdc; /* Tx Buffers Deallocate Counter */
+ u32 reserved0218[0x1A]; /* (0x218-0x280) */
+ u32 fmbm_tpc; /* Tx Performance Counters */
+ u32 fmbm_tpcp; /* Tx Performance Count Parameters */
+ u32 fmbm_tccn; /* Tx Cycle Counter */
+ u32 fmbm_ttuc; /* Tx Tasks Utilization Counter */
+ u32 fmbm_ttcquc; /* Tx Transmit conf Q util Counter */
+ u32 fmbm_tduc; /* Tx DMA Utilization Counter */
+ u32 fmbm_tfuc; /* Tx FIFO Utilization Counter */
+ u32 reserved029c[16]; /* (0x29C-0x2FF) */
+ u32 fmbm_tdcfg[0x3]; /* Tx Debug Configuration */
+ u32 fmbm_tgpr; /* Tx General Purpose Register */
+ u32 reserved0310[0x3a]; /* (0x310-0x3FF) */
+};
+
+/* BMI port register map */
+union fman_port_bmi_regs {
+ struct fman_port_rx_bmi_regs rx;
+ struct fman_port_tx_bmi_regs tx;
+};
+
+/* QMI port register map */
+struct fman_port_qmi_regs {
+ u32 fmqm_pnc; /* PortID n Configuration Register */
+ u32 fmqm_pns; /* PortID n Status Register */
+ u32 fmqm_pnts; /* PortID n Task Status Register */
+ u32 reserved00c[4]; /* 0xn00C - 0xn01B */
+ u32 fmqm_pnen; /* PortID n Enqueue NIA Register */
+ u32 fmqm_pnetfc; /* PortID n Enq Total Frame Counter */
+ u32 reserved024[2]; /* 0xn024 - 0x02B */
+ u32 fmqm_pndn; /* PortID n Dequeue NIA Register */
+ u32 fmqm_pndc; /* PortID n Dequeue Config Register */
+ u32 fmqm_pndtfc; /* PortID n Dequeue tot Frame cntr */
+ u32 fmqm_pndfdc; /* PortID n Dequeue FQID Dflt Cntr */
+ u32 fmqm_pndcc; /* PortID n Dequeue Confirm Counter */
+};
+
+/* QMI dequeue prefetch modes */
+enum fman_port_deq_prefetch {
+ FMAN_PORT_DEQ_NO_PREFETCH, /* No prefetch mode */
+ FMAN_PORT_DEQ_PART_PREFETCH, /* Partial prefetch mode */
+ FMAN_PORT_DEQ_FULL_PREFETCH /* Full prefetch mode */
+};
+
+/* A structure for defining FM port resources */
+struct fman_port_rsrc {
+ u32 num; /* Committed required resource */
+ u32 extra; /* Extra (not committed) required resource */
+};
+
+enum fman_port_dma_swap {
+ FMAN_PORT_DMA_NO_SWAP, /* No swap, transfer data as is */
+ FMAN_PORT_DMA_SWAP_LE,
+ /* The transferred data should be swapped in PPC Little Endian mode */
+ FMAN_PORT_DMA_SWAP_BE
+ /* The transferred data should be swapped in Big Endian mode */
+};
+
+/* Default port color */
+enum fman_port_color {
+ FMAN_PORT_COLOR_GREEN, /* Default port color is green */
+ FMAN_PORT_COLOR_YELLOW, /* Default port color is yellow */
+ FMAN_PORT_COLOR_RED, /* Default port color is red */
+ FMAN_PORT_COLOR_OVERRIDE /* Ignore color */
+};
+
+/* QMI dequeue from the SP channel - types */
+enum fman_port_deq_type {
+ FMAN_PORT_DEQ_BY_PRI,
+ /* Priority precedence and Intra-Class scheduling */
+ FMAN_PORT_DEQ_ACTIVE_FQ,
+ /* Active FQ precedence and Intra-Class scheduling */
+ FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS
+ /* Active FQ precedence and override Intra-Class scheduling */
+};
+
+/* External buffer pools configuration */
+struct fman_port_bpools {
+ u8 count; /* Num of pools to set up */
+ bool counters_enable; /* Enable allocate counters */
+ u8 grp_bp_depleted_num;
+ /* Number of depleted pools - if reached the BMI indicates
+ * the MAC to send a pause frame
+ */
+ struct {
+ u8 bpid; /* BM pool ID */
+ u16 size;
+ /* Pool's size - must be in ascending order */
+ bool is_backup;
+ /* If this is a backup pool */
+ bool grp_bp_depleted;
+ /* Consider this buffer in multiple pools depletion criteria */
+ bool single_bp_depleted;
+ /* Consider this buffer in single pool depletion criteria */
+ } bpool[FMAN_PORT_MAX_EXT_POOLS_NUM];
+};
+
+struct fman_port_cfg {
+ u32 dflt_fqid;
+ u32 err_fqid;
+ u8 deq_sp;
+ bool deq_high_priority;
+ enum fman_port_deq_type deq_type;
+ enum fman_port_deq_prefetch deq_prefetch_option;
+ u16 deq_byte_cnt;
+ u8 cheksum_last_bytes_ignore;
+ u8 rx_cut_end_bytes;
+ struct fman_buf_pool_depletion buf_pool_depletion;
+ struct fman_ext_pools ext_buf_pools;
+ u32 tx_fifo_min_level;
+ u32 tx_fifo_low_comf_level;
+ u32 rx_pri_elevation;
+ u32 rx_fifo_thr;
+ struct fman_sp_buf_margins buf_margins;
+ u32 int_buf_start_margin;
+ struct fman_sp_int_context_data_copy int_context;
+ u32 discard_mask;
+ u32 err_mask;
+ struct fman_buffer_prefix_content buffer_prefix_content;
+ bool dont_release_buf;
+
+ u8 rx_fd_bits;
+ u32 tx_fifo_deq_pipeline_depth;
+ bool errata_A006320;
+ bool excessive_threshold_register;
+ bool fmbm_tfne_has_features;
+
+ enum fman_port_dma_swap dma_swap_data;
+ enum fman_port_color color;
+};
+
+struct fman_port_rx_pools_params {
+ u8 num_of_pools;
+ u16 second_largest_buf_size;
+ u16 largest_buf_size;
+};
+
+struct fman_port_dts_params {
+ void __iomem *base_addr; /* FMan port virtual memory */
+ enum fman_port_type type; /* Port type */
+ u16 speed; /* Port speed */
+ u8 id; /* HW Port Id */
+ u32 qman_channel_id; /* QMan channel id (non RX only) */
+ struct fman *fman; /* FMan Handle */
+};
+
+struct fman_port {
+ void *fm;
+ struct device *dev;
+ struct fman_rev_info rev_info;
+ u8 port_id;
+ enum fman_port_type port_type;
+ u16 port_speed;
+
+ union fman_port_bmi_regs __iomem *bmi_regs;
+ struct fman_port_qmi_regs __iomem *qmi_regs;
+
+ struct fman_sp_buffer_offsets buffer_offsets;
+
+ u8 internal_buf_offset;
+ struct fman_ext_pools ext_buf_pools;
+
+ u16 max_frame_length;
+ struct fman_port_rsrc open_dmas;
+ struct fman_port_rsrc tasks;
+ struct fman_port_rsrc fifo_bufs;
+ struct fman_port_rx_pools_params rx_pools_params;
+
+ struct fman_port_cfg *cfg;
+ struct fman_port_dts_params dts_params;
+
+ u8 ext_pools_num;
+ u32 max_port_fifo_size;
+ u32 max_num_of_ext_pools;
+ u32 max_num_of_sub_portals;
+ u32 bm_max_num_of_pools;
+};
+
+static int init_bmi_rx(struct fman_port *port)
+{
+ struct fman_port_rx_bmi_regs __iomem *regs = &port->bmi_regs->rx;
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp;
+
+ /* DMA attributes */
+ tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT;
+ /* Enable write optimization */
+ tmp |= BMI_DMA_ATTR_WRITE_OPTIMIZE;
+ iowrite32be(tmp, &regs->fmbm_rda);
+
+ /* Rx FIFO parameters */
+ tmp = (cfg->rx_pri_elevation / PORT_BMI_FIFO_UNITS - 1) <<
+ BMI_RX_FIFO_PRI_ELEVATION_SHIFT;
+ tmp |= cfg->rx_fifo_thr / PORT_BMI_FIFO_UNITS - 1;
+ iowrite32be(tmp, &regs->fmbm_rfp);
+
+ if (cfg->excessive_threshold_register)
+ /* always allow access to the extra resources */
+ iowrite32be(BMI_RX_FIFO_THRESHOLD_ETHE, &regs->fmbm_reth);
+
+ /* Frame end data */
+ tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) <<
+ BMI_FRAME_END_CS_IGNORE_SHIFT;
+ tmp |= (cfg->rx_cut_end_bytes & BMI_RX_FRAME_END_CUT_MASK) <<
+ BMI_RX_FRAME_END_CUT_SHIFT;
+ if (cfg->errata_A006320)
+ tmp &= 0xffe0ffff;
+ iowrite32be(tmp, &regs->fmbm_rfed);
+
+ /* Internal context parameters */
+ tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT;
+ tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT;
+ tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_SIZE_MASK;
+ iowrite32be(tmp, &regs->fmbm_ricp);
+
+ /* Internal buffer offset */
+ tmp = ((cfg->int_buf_start_margin / PORT_IC_OFFSET_UNITS) &
+ BMI_INT_BUF_MARG_MASK) << BMI_INT_BUF_MARG_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_rim);
+
+ /* External buffer margins */
+ tmp = (cfg->buf_margins.start_margins & BMI_EXT_BUF_MARG_START_MASK) <<
+ BMI_EXT_BUF_MARG_START_SHIFT;
+ tmp |= cfg->buf_margins.end_margins & BMI_EXT_BUF_MARG_END_MASK;
+ iowrite32be(tmp, &regs->fmbm_rebm);
+
+ /* Frame attributes */
+ tmp = BMI_CMD_RX_MR_DEF;
+ tmp |= BMI_CMD_ATTR_ORDER;
+ tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT;
+ /* Synchronization request */
+ tmp |= BMI_CMD_ATTR_SYNC;
+
+ iowrite32be(tmp, &regs->fmbm_rfca);
+
+ /* NIA */
+ tmp = (u32)cfg->rx_fd_bits << BMI_NEXT_ENG_FD_BITS_SHIFT;
+
+ tmp |= NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME;
+ iowrite32be(tmp, &regs->fmbm_rfne);
+
+ /* Enqueue NIA */
+ iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, &regs->fmbm_rfene);
+
+ /* Default/error queues */
+ iowrite32be((cfg->dflt_fqid & DFLT_FQ_ID), &regs->fmbm_rfqid);
+ iowrite32be((cfg->err_fqid & DFLT_FQ_ID), &regs->fmbm_refqid);
+
+ /* Discard/error masks */
+ iowrite32be(cfg->discard_mask, &regs->fmbm_rfsdm);
+ iowrite32be(cfg->err_mask, &regs->fmbm_rfsem);
+
+ return 0;
+}
+
+static int init_bmi_tx(struct fman_port *port)
+{
+ struct fman_port_tx_bmi_regs __iomem *regs = &port->bmi_regs->tx;
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp;
+
+ /* Tx Configuration register */
+ tmp = 0;
+ iowrite32be(tmp, &regs->fmbm_tcfg);
+
+ /* DMA attributes */
+ tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_tda);
+
+ /* Tx FIFO parameters */
+ tmp = (cfg->tx_fifo_min_level / PORT_BMI_FIFO_UNITS) <<
+ BMI_TX_FIFO_MIN_FILL_SHIFT;
+ tmp |= ((cfg->tx_fifo_deq_pipeline_depth - 1) &
+ BMI_FIFO_PIPELINE_DEPTH_MASK) << BMI_FIFO_PIPELINE_DEPTH_SHIFT;
+ tmp |= (cfg->tx_fifo_low_comf_level / PORT_BMI_FIFO_UNITS) - 1;
+ iowrite32be(tmp, &regs->fmbm_tfp);
+
+ /* Frame end data */
+ tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) <<
+ BMI_FRAME_END_CS_IGNORE_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_tfed);
+
+ /* Internal context parameters */
+ tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT;
+ tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT;
+ tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_SIZE_MASK;
+ iowrite32be(tmp, &regs->fmbm_ticp);
+
+ /* Frame attributes */
+ tmp = BMI_CMD_TX_MR_DEF;
+ tmp |= BMI_CMD_ATTR_ORDER;
+ tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_tfca);
+
+ /* Dequeue NIA + enqueue NIA */
+ iowrite32be(NIA_ENG_QMI_DEQ, &regs->fmbm_tfdne);
+ iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, &regs->fmbm_tfene);
+ if (cfg->fmbm_tfne_has_features)
+ iowrite32be(!cfg->dflt_fqid ?
+ BMI_EBD_EN | NIA_BMI_AC_FETCH_ALL_FRAME :
+ NIA_BMI_AC_FETCH_ALL_FRAME, &regs->fmbm_tfne);
+ if (!cfg->dflt_fqid && cfg->dont_release_buf) {
+ iowrite32be(DFLT_FQ_ID, &regs->fmbm_tcfqid);
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE,
+ &regs->fmbm_tfene);
+ if (cfg->fmbm_tfne_has_features)
+ iowrite32be(ioread32be(&regs->fmbm_tfne) & ~BMI_EBD_EN,
+ &regs->fmbm_tfne);
+ }
+
+ /* Confirmation/error queues */
+ if (cfg->dflt_fqid || !cfg->dont_release_buf)
+ iowrite32be(cfg->dflt_fqid & DFLT_FQ_ID, &regs->fmbm_tcfqid);
+ iowrite32be((cfg->err_fqid & DFLT_FQ_ID), &regs->fmbm_tefqid);
+
+ return 0;
+}
+
+static int init_qmi(struct fman_port *port)
+{
+ struct fman_port_qmi_regs __iomem *regs = port->qmi_regs;
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp;
+
+ /* Rx port configuration */
+ if (port->port_type == FMAN_PORT_TYPE_RX) {
+ /* Enqueue NIA */
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_RELEASE, &regs->fmqm_pnen);
+ return 0;
+ }
+
+ /* Continue with Tx port configuration */
+ if (port->port_type == FMAN_PORT_TYPE_TX) {
+ /* Enqueue NIA */
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE,
+ &regs->fmqm_pnen);
+ /* Dequeue NIA */
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX, &regs->fmqm_pndn);
+ }
+
+ /* Dequeue Configuration register */
+ tmp = 0;
+ if (cfg->deq_high_priority)
+ tmp |= QMI_DEQ_CFG_PRI;
+
+ switch (cfg->deq_type) {
+ case FMAN_PORT_DEQ_BY_PRI:
+ tmp |= QMI_DEQ_CFG_TYPE1;
+ break;
+ case FMAN_PORT_DEQ_ACTIVE_FQ:
+ tmp |= QMI_DEQ_CFG_TYPE2;
+ break;
+ case FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS:
+ tmp |= QMI_DEQ_CFG_TYPE3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (cfg->deq_prefetch_option) {
+ case FMAN_PORT_DEQ_NO_PREFETCH:
+ break;
+ case FMAN_PORT_DEQ_PART_PREFETCH:
+ tmp |= QMI_DEQ_CFG_PREFETCH_PARTIAL;
+ break;
+ case FMAN_PORT_DEQ_FULL_PREFETCH:
+ tmp |= QMI_DEQ_CFG_PREFETCH_FULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp |= (cfg->deq_sp & QMI_DEQ_CFG_SP_MASK) << QMI_DEQ_CFG_SP_SHIFT;
+ tmp |= cfg->deq_byte_cnt;
+ iowrite32be(tmp, &regs->fmqm_pndc);
+
+ return 0;
+}
+
+static int init(struct fman_port *port)
+{
+ int err;
+
+ /* Init BMI registers */
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ err = init_bmi_rx(port);
+ break;
+ case FMAN_PORT_TYPE_TX:
+ err = init_bmi_tx(port);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (err)
+ return err;
+
+ /* Init QMI registers */
+ err = init_qmi(port);
+ return err;
+
+ return 0;
+}
+
+static int set_bpools(const struct fman_port *port,
+ const struct fman_port_bpools *bp)
+{
+ u32 __iomem *bp_reg, *bp_depl_reg;
+ u32 tmp;
+ u8 i, max_bp_num;
+ bool grp_depl_used = false, rx_port;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ max_bp_num = port->ext_pools_num;
+ rx_port = true;
+ bp_reg = port->bmi_regs->rx.fmbm_ebmpi;
+ bp_depl_reg = &port->bmi_regs->rx.fmbm_mpd;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rx_port) {
+ /* Check buffers are provided in ascending order */
+ for (i = 0; (i < (bp->count - 1) &&
+ (i < FMAN_PORT_MAX_EXT_POOLS_NUM - 1)); i++) {
+ if (bp->bpool[i].size > bp->bpool[i + 1].size)
+ return -EINVAL;
+ }
+ }
+
+ /* Set up external buffers pools */
+ for (i = 0; i < bp->count; i++) {
+ tmp = BMI_EXT_BUF_POOL_VALID;
+ tmp |= ((u32)bp->bpool[i].bpid <<
+ BMI_EXT_BUF_POOL_ID_SHIFT) & BMI_EXT_BUF_POOL_ID_MASK;
+
+ if (rx_port) {
+ if (bp->counters_enable)
+ tmp |= BMI_EXT_BUF_POOL_EN_COUNTER;
+
+ if (bp->bpool[i].is_backup)
+ tmp |= BMI_EXT_BUF_POOL_BACKUP;
+
+ tmp |= (u32)bp->bpool[i].size;
+ }
+
+ iowrite32be(tmp, &bp_reg[i]);
+ }
+
+ /* Clear unused pools */
+ for (i = bp->count; i < max_bp_num; i++)
+ iowrite32be(0, &bp_reg[i]);
+
+ /* Pools depletion */
+ tmp = 0;
+ for (i = 0; i < FMAN_PORT_MAX_EXT_POOLS_NUM; i++) {
+ if (bp->bpool[i].grp_bp_depleted) {
+ grp_depl_used = true;
+ tmp |= 0x80000000 >> i;
+ }
+
+ if (bp->bpool[i].single_bp_depleted)
+ tmp |= 0x80 >> i;
+ }
+
+ if (grp_depl_used)
+ tmp |= ((u32)bp->grp_bp_depleted_num - 1) <<
+ BMI_POOL_DEP_NUM_OF_POOLS_SHIFT;
+
+ iowrite32be(tmp, bp_depl_reg);
+ return 0;
+}
+
+static bool is_init_done(struct fman_port_cfg *cfg)
+{
+ /* Checks if FMan port driver parameters were initialized */
+ if (!cfg)
+ return true;
+
+ return false;
+}
+
+static int verify_size_of_fifo(struct fman_port *port)
+{
+ u32 min_fifo_size_required = 0, opt_fifo_size_for_b2b = 0;
+
+ /* TX Ports */
+ if (port->port_type == FMAN_PORT_TYPE_TX) {
+ min_fifo_size_required = (u32)
+ (roundup(port->max_frame_length,
+ FMAN_BMI_FIFO_UNITS) + (3 * FMAN_BMI_FIFO_UNITS));
+
+ min_fifo_size_required +=
+ port->cfg->tx_fifo_deq_pipeline_depth *
+ FMAN_BMI_FIFO_UNITS;
+
+ opt_fifo_size_for_b2b = min_fifo_size_required;
+
+ /* Add some margin for back-to-back capability to improve
+ * performance, allows the hardware to pipeline new frame dma
+ * while the previous frame not yet transmitted.
+ */
+ if (port->port_speed == 10000)
+ opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS;
+ else
+ opt_fifo_size_for_b2b += 2 * FMAN_BMI_FIFO_UNITS;
+ }
+
+ /* RX Ports */
+ else if (port->port_type == FMAN_PORT_TYPE_RX) {
+ if (port->rev_info.major >= 6)
+ min_fifo_size_required = (u32)
+ (roundup(port->max_frame_length,
+ FMAN_BMI_FIFO_UNITS) +
+ (5 * FMAN_BMI_FIFO_UNITS));
+ /* 4 according to spec + 1 for FOF>0 */
+ else
+ min_fifo_size_required = (u32)
+ (roundup(min(port->max_frame_length,
+ port->rx_pools_params.largest_buf_size),
+ FMAN_BMI_FIFO_UNITS) +
+ (7 * FMAN_BMI_FIFO_UNITS));
+
+ opt_fifo_size_for_b2b = min_fifo_size_required;
+
+ /* Add some margin for back-to-back capability to improve
+ * performance,allows the hardware to pipeline new frame dma
+ * while the previous frame not yet transmitted.
+ */
+ if (port->port_speed == 10000)
+ opt_fifo_size_for_b2b += 8 * FMAN_BMI_FIFO_UNITS;
+ else
+ opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS;
+ }
+
+ WARN_ON(min_fifo_size_required <= 0);
+ WARN_ON(opt_fifo_size_for_b2b < min_fifo_size_required);
+
+ /* Verify the size */
+ if (port->fifo_bufs.num < min_fifo_size_required)
+ dev_dbg(port->dev, "%s: FIFO size should be enlarged to %d bytes\n",
+ __func__, min_fifo_size_required);
+ else if (port->fifo_bufs.num < opt_fifo_size_for_b2b)
+ dev_dbg(port->dev, "%s: For b2b processing,FIFO may be enlarged to %d bytes\n",
+ __func__, opt_fifo_size_for_b2b);
+
+ return 0;
+}
+
+static int set_ext_buffer_pools(struct fman_port *port)
+{
+ struct fman_ext_pools *ext_buf_pools = &port->cfg->ext_buf_pools;
+ struct fman_buf_pool_depletion *buf_pool_depletion =
+ &port->cfg->buf_pool_depletion;
+ u8 ordered_array[FMAN_PORT_MAX_EXT_POOLS_NUM];
+ u16 sizes_array[BM_MAX_NUM_OF_POOLS];
+ int i = 0, j = 0, err;
+ struct fman_port_bpools bpools;
+
+ memset(&ordered_array, 0, sizeof(u8) * FMAN_PORT_MAX_EXT_POOLS_NUM);
+ memset(&sizes_array, 0, sizeof(u16) * BM_MAX_NUM_OF_POOLS);
+ memcpy(&port->ext_buf_pools, ext_buf_pools,
+ sizeof(struct fman_ext_pools));
+
+ fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(ext_buf_pools,
+ ordered_array,
+ sizes_array);
+
+ memset(&bpools, 0, sizeof(struct fman_port_bpools));
+ bpools.count = ext_buf_pools->num_of_pools_used;
+ bpools.counters_enable = true;
+ for (i = 0; i < ext_buf_pools->num_of_pools_used; i++) {
+ bpools.bpool[i].bpid = ordered_array[i];
+ bpools.bpool[i].size = sizes_array[ordered_array[i]];
+ }
+
+ /* save pools parameters for later use */
+ port->rx_pools_params.num_of_pools = ext_buf_pools->num_of_pools_used;
+ port->rx_pools_params.largest_buf_size =
+ sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 1]];
+ port->rx_pools_params.second_largest_buf_size =
+ sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 2]];
+
+ /* FMBM_RMPD reg. - pool depletion */
+ if (buf_pool_depletion->pools_grp_mode_enable) {
+ bpools.grp_bp_depleted_num = buf_pool_depletion->num_of_pools;
+ for (i = 0; i < port->bm_max_num_of_pools; i++) {
+ if (buf_pool_depletion->pools_to_consider[i]) {
+ for (j = 0; j < ext_buf_pools->
+ num_of_pools_used; j++) {
+ if (i == ordered_array[j]) {
+ bpools.bpool[j].
+ grp_bp_depleted = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (buf_pool_depletion->single_pool_mode_enable) {
+ for (i = 0; i < port->bm_max_num_of_pools; i++) {
+ if (buf_pool_depletion->
+ pools_to_consider_for_single_mode[i]) {
+ for (j = 0; j < ext_buf_pools->
+ num_of_pools_used; j++) {
+ if (i == ordered_array[j]) {
+ bpools.bpool[j].
+ single_bp_depleted = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ err = set_bpools(port, &bpools);
+ if (err != 0) {
+ dev_err(port->dev, "%s: set_bpools() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int init_low_level_driver(struct fman_port *port)
+{
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp_val;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ cfg->err_mask = (RX_ERRS_TO_ENQ & ~cfg->discard_mask);
+ break;
+ default:
+ break;
+ }
+
+ tmp_val = (u32)((port->internal_buf_offset % OFFSET_UNITS) ?
+ (port->internal_buf_offset / OFFSET_UNITS + 1) :
+ (port->internal_buf_offset / OFFSET_UNITS));
+ port->internal_buf_offset = (u8)(tmp_val * OFFSET_UNITS);
+ port->cfg->int_buf_start_margin = port->internal_buf_offset;
+
+ if (init(port) != 0) {
+ dev_err(port->dev, "%s: fman port initialization failed\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ /* The code bellow is a trick so the FM will not release the buffer
+ * to BM nor will try to enqueue the frame to QM
+ */
+ if (port->port_type == FMAN_PORT_TYPE_TX) {
+ if (!cfg->dflt_fqid && cfg->dont_release_buf) {
+ /* override fmbm_tcfqid 0 with a false non-0 value.
+ * This will force FM to act according to tfene.
+ * Otherwise, if fmbm_tcfqid is 0 the FM will release
+ * buffers to BM regardless of fmbm_tfene
+ */
+ iowrite32be(0xFFFFFF, &port->bmi_regs->tx.fmbm_tcfqid);
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE,
+ &port->bmi_regs->tx.fmbm_tfene);
+ }
+ }
+
+ return 0;
+}
+
+static int fill_soc_specific_params(struct fman_port *port)
+{
+ u32 bmi_max_fifo_size;
+
+ bmi_max_fifo_size = fman_get_bmi_max_fifo_size(port->fm);
+ port->max_port_fifo_size = MAX_PORT_FIFO_SIZE(bmi_max_fifo_size);
+ port->bm_max_num_of_pools = 64;
+
+ /* P4080 - Major 2
+ * P2041/P3041/P5020/P5040 - Major 3
+ * Tx/Bx - Major 6
+ */
+ switch (port->rev_info.major) {
+ case 2:
+ case 3:
+ port->max_num_of_ext_pools = 4;
+ port->max_num_of_sub_portals = 12;
+ break;
+
+ case 6:
+ port->max_num_of_ext_pools = 8;
+ port->max_num_of_sub_portals = 16;
+ break;
+
+ default:
+ dev_err(port->dev, "%s: Unsupported FMan version\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_dflt_fifo_deq_pipeline_depth(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ case FMAN_PORT_TYPE_TX:
+ switch (speed) {
+ case 10000:
+ return 4;
+ case 1000:
+ if (major >= 6)
+ return 2;
+ else
+ return 1;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_num_of_tasks(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ case FMAN_PORT_TYPE_TX:
+ switch (speed) {
+ case 10000:
+ return 16;
+ case 1000:
+ if (major >= 6)
+ return 4;
+ else
+ return 3;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_extra_num_of_tasks(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ /* FMan V3 */
+ if (major >= 6)
+ return 0;
+
+ /* FMan V2 */
+ if (speed == 10000)
+ return 8;
+ else
+ return 2;
+ case FMAN_PORT_TYPE_TX:
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_num_of_open_dmas(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ int val;
+
+ if (major >= 6) {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ val = 12;
+ else
+ val = 3;
+ break;
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 8;
+ else
+ val = 2;
+ break;
+ default:
+ return 0;
+ }
+ } else {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 8;
+ else
+ val = 1;
+ break;
+ default:
+ val = 0;
+ }
+ }
+
+ return val;
+}
+
+static int get_dflt_extra_num_of_open_dmas(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ /* FMan V3 */
+ if (major >= 6)
+ return 0;
+
+ /* FMan V2 */
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ return 8;
+ else
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_num_of_fifo_bufs(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ int val;
+
+ if (major >= 6) {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ val = 64;
+ else
+ val = 50;
+ break;
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 96;
+ else
+ val = 50;
+ break;
+ default:
+ val = 0;
+ }
+ } else {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ val = 48;
+ else
+ val = 44;
+ break;
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 48;
+ else
+ val = 45;
+ break;
+ default:
+ val = 0;
+ }
+ }
+
+ return val;
+}
+
+static void set_dflt_cfg(struct fman_port *port,
+ struct fman_port_params *port_params)
+{
+ struct fman_port_cfg *cfg = port->cfg;
+
+ cfg->dma_swap_data = FMAN_PORT_DMA_NO_SWAP;
+ cfg->color = FMAN_PORT_COLOR_GREEN;
+ cfg->rx_cut_end_bytes = DFLT_PORT_CUT_BYTES_FROM_END;
+ cfg->rx_pri_elevation = BMI_PRIORITY_ELEVATION_LEVEL;
+ cfg->rx_fifo_thr = BMI_FIFO_THRESHOLD;
+ cfg->tx_fifo_low_comf_level = (5 * 1024);
+ cfg->deq_type = FMAN_PORT_DEQ_BY_PRI;
+ cfg->deq_prefetch_option = FMAN_PORT_DEQ_FULL_PREFETCH;
+ cfg->tx_fifo_deq_pipeline_depth =
+ BMI_DEQUEUE_PIPELINE_DEPTH(port->port_type, port->port_speed);
+ cfg->deq_byte_cnt = QMI_BYTE_COUNT_LEVEL_CONTROL(port->port_type);
+
+ cfg->rx_pri_elevation =
+ DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(port->max_port_fifo_size);
+ port->cfg->rx_fifo_thr =
+ DFLT_PORT_RX_FIFO_THRESHOLD(port->rev_info.major,
+ port->max_port_fifo_size);
+
+ if ((port->rev_info.major == 6) &&
+ ((port->rev_info.minor == 0) || (port->rev_info.minor == 3)))
+ cfg->errata_A006320 = true;
+
+ /* Excessive Threshold register - exists for pre-FMv3 chips only */
+ if (port->rev_info.major < 6)
+ cfg->excessive_threshold_register = true;
+ else
+ cfg->fmbm_tfne_has_features = true;
+
+ cfg->buffer_prefix_content.data_align =
+ DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN;
+}
+
+static void set_rx_dflt_cfg(struct fman_port *port,
+ struct fman_port_params *port_params)
+{
+ port->cfg->discard_mask = DFLT_PORT_ERRORS_TO_DISCARD;
+
+ memcpy(&port->cfg->ext_buf_pools,
+ &port_params->specific_params.rx_params.ext_buf_pools,
+ sizeof(struct fman_ext_pools));
+ port->cfg->err_fqid =
+ port_params->specific_params.rx_params.err_fqid;
+ port->cfg->dflt_fqid =
+ port_params->specific_params.rx_params.dflt_fqid;
+}
+
+static void set_tx_dflt_cfg(struct fman_port *port,
+ struct fman_port_params *port_params,
+ struct fman_port_dts_params *dts_params)
+{
+ port->cfg->tx_fifo_deq_pipeline_depth =
+ get_dflt_fifo_deq_pipeline_depth(port->rev_info.major,
+ port->port_type,
+ port->port_speed);
+ port->cfg->err_fqid =
+ port_params->specific_params.non_rx_params.err_fqid;
+ port->cfg->deq_sp =
+ (u8)(dts_params->qman_channel_id & QMI_DEQ_CFG_SUBPORTAL_MASK);
+ port->cfg->dflt_fqid =
+ port_params->specific_params.non_rx_params.dflt_fqid;
+ port->cfg->deq_high_priority = true;
+}
+
+/**
+ * fman_port_config
+ * @port: Pointer to the port structure
+ * @params: Pointer to data structure of parameters
+ *
+ * Creates a descriptor for the FM PORT module.
+ * The routine returns a pointer to the FM PORT object.
+ * This descriptor must be passed as first parameter to all other FM PORT
+ * function calls.
+ * No actual initialization or configuration of FM hardware is done by this
+ * routine.
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_config(struct fman_port *port, struct fman_port_params *params)
+{
+ void __iomem *base_addr = port->dts_params.base_addr;
+ int err;
+
+ /* Allocate the FM driver's parameters structure */
+ port->cfg = kzalloc(sizeof(*port->cfg), GFP_KERNEL);
+ if (!port->cfg)
+ goto err_params;
+
+ /* Initialize FM port parameters which will be kept by the driver */
+ port->port_type = port->dts_params.type;
+ port->port_speed = port->dts_params.speed;
+ port->port_id = port->dts_params.id;
+ port->fm = port->dts_params.fman;
+ port->ext_pools_num = (u8)8;
+
+ /* get FM revision */
+ fman_get_revision(port->fm, &port->rev_info);
+
+ err = fill_soc_specific_params(port);
+ if (err)
+ goto err_port_cfg;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ set_rx_dflt_cfg(port, params);
+ case FMAN_PORT_TYPE_TX:
+ set_tx_dflt_cfg(port, params, &port->dts_params);
+ default:
+ set_dflt_cfg(port, params);
+ }
+
+ /* Continue with other parameters */
+ /* set memory map pointers */
+ port->bmi_regs = base_addr + BMI_PORT_REGS_OFFSET;
+ port->qmi_regs = base_addr + QMI_PORT_REGS_OFFSET;
+
+ port->max_frame_length = DFLT_PORT_MAX_FRAME_LENGTH;
+ /* resource distribution. */
+
+ port->fifo_bufs.num =
+ get_dflt_num_of_fifo_bufs(port->rev_info.major, port->port_type,
+ port->port_speed) * FMAN_BMI_FIFO_UNITS;
+ port->fifo_bufs.extra =
+ DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS * FMAN_BMI_FIFO_UNITS;
+
+ port->open_dmas.num =
+ get_dflt_num_of_open_dmas(port->rev_info.major,
+ port->port_type, port->port_speed);
+ port->open_dmas.extra =
+ get_dflt_extra_num_of_open_dmas(port->rev_info.major,
+ port->port_type, port->port_speed);
+ port->tasks.num =
+ get_dflt_num_of_tasks(port->rev_info.major,
+ port->port_type, port->port_speed);
+ port->tasks.extra =
+ get_dflt_extra_num_of_tasks(port->rev_info.major,
+ port->port_type, port->port_speed);
+
+ /* FM_HEAVY_TRAFFIC_SEQUENCER_HANG_ERRATA_FMAN_A006981 errata
+ * workaround
+ */
+ if ((port->rev_info.major == 6) && (port->rev_info.minor == 0) &&
+ (((port->port_type == FMAN_PORT_TYPE_TX) &&
+ (port->port_speed == 1000)))) {
+ port->open_dmas.num = 16;
+ port->open_dmas.extra = 0;
+ }
+
+ if (port->rev_info.major >= 6 &&
+ port->port_type == FMAN_PORT_TYPE_TX &&
+ port->port_speed == 1000) {
+ /* FM_WRONG_RESET_VALUES_ERRATA_FMAN_A005127 Errata
+ * workaround
+ */
+ if (port->rev_info.major >= 6) {
+ u32 reg;
+
+ reg = 0x00001013;
+ iowrite32be(reg, &port->bmi_regs->tx.fmbm_tfp);
+ }
+ }
+
+ return 0;
+
+err_port_cfg:
+ kfree(port->cfg);
+err_params:
+ kfree(port);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(fman_port_config);
+
+/**
+ * fman_port_init
+ * port: A pointer to a FM Port module.
+ * Initializes the FM PORT module by defining the software structure and
+ * configuring the hardware registers.
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_init(struct fman_port *port)
+{
+ struct fman_port_cfg *cfg;
+ int err;
+ struct fman_port_init_params params;
+
+ if (is_init_done(port->cfg))
+ return -EINVAL;
+
+ err = fman_sp_build_buffer_struct(&port->cfg->int_context,
+ &port->cfg->buffer_prefix_content,
+ &port->cfg->buf_margins,
+ &port->buffer_offsets,
+ &port->internal_buf_offset);
+ if (err)
+ return err;
+
+ cfg = port->cfg;
+
+ if (port->port_type == FMAN_PORT_TYPE_RX) {
+ /* Call the external Buffer routine which also checks fifo
+ * size and updates it if necessary
+ */
+ /* define external buffer pools and pool depletion */
+ err = set_ext_buffer_pools(port);
+ if (err)
+ return err;
+ /* check if the largest external buffer pool is large enough */
+ if (cfg->buf_margins.start_margins + MIN_EXT_BUF_SIZE +
+ cfg->buf_margins.end_margins >
+ port->rx_pools_params.largest_buf_size) {
+ dev_err(port->dev, "%s: buf_margins.start_margins (%d) + minimum buf size (64) + buf_margins.end_margins (%d) is larger than maximum external buffer size (%d)\n",
+ __func__, cfg->buf_margins.start_margins,
+ cfg->buf_margins.end_margins,
+ port->rx_pools_params.largest_buf_size);
+ return -EINVAL;
+ }
+ }
+
+ /* Call FM module routine for communicating parameters */
+ memset(&params, 0, sizeof(params));
+ params.port_id = port->port_id;
+ params.port_type = port->port_type;
+ params.port_speed = port->port_speed;
+ params.num_of_tasks = (u8)port->tasks.num;
+ params.num_of_extra_tasks = (u8)port->tasks.extra;
+ params.num_of_open_dmas = (u8)port->open_dmas.num;
+ params.num_of_extra_open_dmas = (u8)port->open_dmas.extra;
+
+ if (port->fifo_bufs.num) {
+ err = verify_size_of_fifo(port);
+ if (err)
+ return err;
+ }
+ params.size_of_fifo = port->fifo_bufs.num;
+ params.extra_size_of_fifo = port->fifo_bufs.extra;
+ params.deq_pipeline_depth = port->cfg->tx_fifo_deq_pipeline_depth;
+ params.max_frame_length = port->max_frame_length;
+
+ err = fman_set_port_params(port->fm, &params);
+ if (err)
+ return err;
+
+ err = init_low_level_driver(port);
+ if (err)
+ return err;
+
+ kfree(port->cfg);
+ port->cfg = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_init);
+
+/**
+ * fman_port_cfg_buf_prefix_content
+ * @port A pointer to a FM Port module.
+ * @buffer_prefix_content A structure of parameters describing
+ * the structure of the buffer.
+ * Out parameter:
+ * Start margin - offset of data from
+ * start of external buffer.
+ * Defines the structure, size and content of the application buffer.
+ * The prefix, in Tx ports, if 'pass_prs_result', the application should set
+ * a value to their offsets in the prefix of the FM will save the first
+ * 'priv_data_size', than, depending on 'pass_prs_result' and
+ * 'pass_time_stamp', copy parse result and timeStamp, and the packet itself
+ * (in this order), to the application buffer, and to offset.
+ * Calling this routine changes the buffer margins definitions in the internal
+ * driver data base from its default configuration:
+ * Data size: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE]
+ * Pass Parser result: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_PRS_RESULT].
+ * Pass timestamp: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_TIME_STAMP].
+ * May be used for all ports
+ *
+ * Allowed only following fman_port_config() and before fman_port_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_cfg_buf_prefix_content(struct fman_port *port,
+ struct fman_buffer_prefix_content *
+ buffer_prefix_content)
+{
+ if (is_init_done(port->cfg))
+ return -EINVAL;
+
+ memcpy(&port->cfg->buffer_prefix_content,
+ buffer_prefix_content,
+ sizeof(struct fman_buffer_prefix_content));
+ /* if data_align was not initialized by user,
+ * we return to driver's default
+ */
+ if (!port->cfg->buffer_prefix_content.data_align)
+ port->cfg->buffer_prefix_content.data_align =
+ DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN;
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_cfg_buf_prefix_content);
+
+/**
+ * fman_port_disable
+ * port: A pointer to a FM Port module.
+ *
+ * Gracefully disable an FM port. The port will not start new tasks after all
+ * tasks associated with the port are terminated.
+ *
+ * This is a blocking routine, it returns after port is gracefully stopped,
+ * i.e. the port will not except new frames, but it will finish all frames
+ * or tasks which were already began.
+ * Allowed only following fman_port_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_disable(struct fman_port *port)
+{
+ u32 __iomem *bmi_cfg_reg, *bmi_status_reg, tmp;
+ bool rx_port, failure = false;
+ int count;
+
+ if (!is_init_done(port->cfg))
+ return -EINVAL;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg;
+ bmi_status_reg = &port->bmi_regs->rx.fmbm_rst;
+ rx_port = true;
+ break;
+ case FMAN_PORT_TYPE_TX:
+ bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg;
+ bmi_status_reg = &port->bmi_regs->tx.fmbm_tst;
+ rx_port = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Disable QMI */
+ if (!rx_port) {
+ tmp = ioread32be(&port->qmi_regs->fmqm_pnc) & ~QMI_PORT_CFG_EN;
+ iowrite32be(tmp, &port->qmi_regs->fmqm_pnc);
+
+ /* Wait for QMI to finish FD handling */
+ count = 100;
+ do {
+ udelay(10);
+ tmp = ioread32be(&port->qmi_regs->fmqm_pns);
+ } while ((tmp & QMI_PORT_STATUS_DEQ_FD_BSY) && --count);
+
+ if (count == 0) {
+ /* Timeout */
+ failure = true;
+ }
+ }
+
+ /* Disable BMI */
+ tmp = ioread32be(bmi_cfg_reg) & ~BMI_PORT_CFG_EN;
+ iowrite32be(tmp, bmi_cfg_reg);
+
+ /* Wait for graceful stop end */
+ count = 500;
+ do {
+ udelay(10);
+ tmp = ioread32be(bmi_status_reg);
+ } while ((tmp & BMI_PORT_STATUS_BSY) && --count);
+
+ if (count == 0) {
+ /* Timeout */
+ failure = true;
+ }
+
+ if (failure)
+ dev_dbg(port->dev, "%s: FMan Port[%d]: BMI or QMI is Busy. Port forced down\n",
+ __func__, port->port_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_disable);
+
+/**
+ * fman_port_enable
+ * port: A pointer to a FM Port module.
+ *
+ * A runtime routine provided to allow disable/enable of port.
+ *
+ * Allowed only following fman_port_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_enable(struct fman_port *port)
+{
+ u32 __iomem *bmi_cfg_reg, tmp;
+ bool rx_port;
+
+ if (!is_init_done(port->cfg))
+ return -EINVAL;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg;
+ rx_port = true;
+ break;
+ case FMAN_PORT_TYPE_TX:
+ bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg;
+ rx_port = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable QMI */
+ if (!rx_port) {
+ tmp = ioread32be(&port->qmi_regs->fmqm_pnc) | QMI_PORT_CFG_EN;
+ iowrite32be(tmp, &port->qmi_regs->fmqm_pnc);
+ }
+
+ /* Enable BMI */
+ tmp = ioread32be(bmi_cfg_reg) | BMI_PORT_CFG_EN;
+ iowrite32be(tmp, bmi_cfg_reg);
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_enable);
+
+/**
+ * fman_port_bind
+ * dev: FMan Port OF device pointer
+ *
+ * Bind to a specific FMan Port.
+ *
+ * Allowed only after the port was created.
+ *
+ * Return: A pointer to the FMan port device.
+ */
+struct fman_port *fman_port_bind(struct device *dev)
+{
+ return (struct fman_port *)(dev_get_drvdata(get_device(dev)));
+}
+EXPORT_SYMBOL(fman_port_bind);
+
+/**
+ * fman_port_get_qman_channel_id
+ * port: Pointer to the FMan port devuce
+ *
+ * Get the QMan channel ID for the specific port
+ *
+ * Return: QMan channel ID
+ */
+u32 fman_port_get_qman_channel_id(struct fman_port *port)
+{
+ return port->dts_params.qman_channel_id;
+}
+EXPORT_SYMBOL(fman_port_get_qman_channel_id);
+
+static int fman_port_probe(struct platform_device *of_dev)
+{
+ struct fman_port *port;
+ struct fman *fman;
+ struct device_node *fm_node, *port_node;
+ struct resource res;
+ struct resource *dev_res;
+ const u32 *u32_prop;
+ int err = 0, lenp;
+ enum fman_port_type port_type;
+ u16 port_speed;
+ u8 port_id;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->dev = &of_dev->dev;
+
+ port_node = of_node_get(of_dev->dev.of_node);
+
+ /* Get the FM node */
+ fm_node = of_get_parent(port_node);
+ if (!fm_node) {
+ dev_err(port->dev, "%s: of_get_parent() failed\n", __func__);
+ err = -ENODEV;
+ goto return_err;
+ }
+
+ fman = dev_get_drvdata(&of_find_device_by_node(fm_node)->dev);
+ of_node_put(fm_node);
+ if (!fman) {
+ err = -EINVAL;
+ goto return_err;
+ }
+
+ u32_prop = (const u32 *)of_get_property(port_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(port->dev, "%s: of_get_property(%s, cell-index) failed\n",
+ __func__, port_node->full_name);
+ err = -EINVAL;
+ goto return_err;
+ }
+ if (WARN_ON(lenp != sizeof(u32))) {
+ err = -EINVAL;
+ goto return_err;
+ }
+ port_id = (u8)fdt32_to_cpu(u32_prop[0]);
+
+ port->dts_params.id = port_id;
+
+ if (of_device_is_compatible(port_node, "fsl,fman-v3-port-tx")) {
+ port_type = FMAN_PORT_TYPE_TX;
+ port_speed = 1000;
+ u32_prop = (const u32 *)of_get_property(port_node,
+ "fsl,fman-10g-port",
+ &lenp);
+ if (u32_prop)
+ port_speed = 10000;
+
+ } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-tx")) {
+ if (port_id >= TX_10G_PORT_BASE)
+ port_speed = 10000;
+ else
+ port_speed = 1000;
+ port_type = FMAN_PORT_TYPE_TX;
+
+ } else if (of_device_is_compatible(port_node, "fsl,fman-v3-port-rx")) {
+ port_type = FMAN_PORT_TYPE_RX;
+ port_speed = 1000;
+ u32_prop = (const u32 *)of_get_property(port_node,
+ "fsl,fman-10g-port", &lenp);
+ if (u32_prop)
+ port_speed = 10000;
+
+ } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-rx")) {
+ if (port_id >= RX_10G_PORT_BASE)
+ port_speed = 10000;
+ else
+ port_speed = 1000;
+ port_type = FMAN_PORT_TYPE_RX;
+
+ } else {
+ dev_err(port->dev, "%s: Illegal port type\n", __func__);
+ err = -EINVAL;
+ goto return_err;
+ }
+
+ port->dts_params.type = port_type;
+ port->dts_params.speed = port_speed;
+
+ if (port_type == FMAN_PORT_TYPE_TX) {
+ u32 qman_channel_id;
+
+ qman_channel_id = fman_get_qman_channel_id(fman, port_id);
+ if (qman_channel_id == 0) {
+ dev_err(port->dev, "%s: incorrect qman-channel-id\n",
+ __func__);
+ err = -EINVAL;
+ goto return_err;
+ }
+ port->dts_params.qman_channel_id = qman_channel_id;
+ }
+
+ err = of_address_to_resource(port_node, 0, &res);
+ if (err < 0) {
+ dev_err(port->dev, "%s: of_address_to_resource() failed\n",
+ __func__);
+ err = -ENOMEM;
+ goto return_err;
+ }
+
+ port->dts_params.fman = fman;
+
+ of_node_put(port_node);
+
+ dev_res = __devm_request_region(port->dev, &res, res.start,
+ resource_size(&res), "fman-port");
+ if (!dev_res) {
+ dev_err(port->dev, "%s: __devm_request_region() failed\n",
+ __func__);
+ err = -EINVAL;
+ goto free_port;
+ }
+
+ port->dts_params.base_addr = devm_ioremap(port->dev, res.start,
+ resource_size(&res));
+ if (port->dts_params.base_addr == 0)
+ dev_err(port->dev, "%s: devm_ioremap() failed\n", __func__);
+
+ dev_set_drvdata(&of_dev->dev, port);
+
+ return 0;
+
+return_err:
+ of_node_put(port_node);
+free_port:
+ kfree(port);
+ return err;
+}
+
+static const struct of_device_id fman_port_match[] = {
+ {.compatible = "fsl,fman-v3-port-rx"},
+ {.compatible = "fsl,fman-v2-port-rx"},
+ {.compatible = "fsl,fman-v3-port-tx"},
+ {.compatible = "fsl,fman-v2-port-tx"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, fman_port_match);
+
+static struct platform_driver fman_port_driver = {
+ .driver = {
+ .name = "fsl-fman-port",
+ .of_match_table = fman_port_match,
+ },
+ .probe = fman_port_probe,
+};
+
+builtin_platform_driver(fman_port_driver);
diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h
new file mode 100644
index 000000000000..8ba901737048
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_port.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FMAN_PORT_H
+#define __FMAN_PORT_H
+
+#include "fman.h"
+
+/* FM Port API
+ * The FM uses a general module called "port" to represent a Tx port (MAC),
+ * an Rx port (MAC).
+ * The number of ports in an FM varies between SOCs.
+ * The SW driver manages these ports as sub-modules of the FM,i.e. after an
+ * FM is initialized, its ports may be initialized and operated upon.
+ * The port is initialized aware of its type, but other functions on a port
+ * may be indifferent to its type. When necessary, the driver verifies
+ * coherence and returns error if applicable.
+ * On initialization, user specifies the port type and it's index (relative
+ * to the port's type) - always starting at 0.
+ */
+
+/* FM Frame error */
+/* Frame Descriptor errors */
+/* Not for Rx-Port! Unsupported Format */
+#define FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT FM_FD_ERR_UNSUPPORTED_FORMAT
+/* Not for Rx-Port! Length Error */
+#define FM_PORT_FRM_ERR_LENGTH FM_FD_ERR_LENGTH
+/* DMA Data error */
+#define FM_PORT_FRM_ERR_DMA FM_FD_ERR_DMA
+/* non Frame-Manager error; probably come from SEC that was chained to FM */
+#define FM_PORT_FRM_ERR_NON_FM FM_FD_RX_STATUS_ERR_NON_FM
+ /* IPR error */
+#define FM_PORT_FRM_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR)
+/* IPR non-consistent-sp */
+#define FM_PORT_FRM_ERR_IPR_NCSP (FM_FD_ERR_IPR_NCSP & \
+ ~FM_FD_IPR)
+
+/* Rx FIFO overflow, FCS error, code error, running disparity
+ * error (SGMII and TBI modes), FIFO parity error.
+ * PHY Sequence error, PHY error control character detected.
+ */
+#define FM_PORT_FRM_ERR_PHYSICAL FM_FD_ERR_PHYSICAL
+/* Frame too long OR Frame size exceeds max_length_frame */
+#define FM_PORT_FRM_ERR_SIZE FM_FD_ERR_SIZE
+/* indicates a classifier "drop" operation */
+#define FM_PORT_FRM_ERR_CLS_DISCARD FM_FD_ERR_CLS_DISCARD
+/* Extract Out of Frame */
+#define FM_PORT_FRM_ERR_EXTRACTION FM_FD_ERR_EXTRACTION
+/* No Scheme Selected */
+#define FM_PORT_FRM_ERR_NO_SCHEME FM_FD_ERR_NO_SCHEME
+/* Keysize Overflow */
+#define FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW FM_FD_ERR_KEYSIZE_OVERFLOW
+/* Frame color is red */
+#define FM_PORT_FRM_ERR_COLOR_RED FM_FD_ERR_COLOR_RED
+/* Frame color is yellow */
+#define FM_PORT_FRM_ERR_COLOR_YELLOW FM_FD_ERR_COLOR_YELLOW
+/* Parser Time out Exceed */
+#define FM_PORT_FRM_ERR_PRS_TIMEOUT FM_FD_ERR_PRS_TIMEOUT
+/* Invalid Soft Parser instruction */
+#define FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT FM_FD_ERR_PRS_ILL_INSTRUCT
+/* Header error was identified during parsing */
+#define FM_PORT_FRM_ERR_PRS_HDR_ERR FM_FD_ERR_PRS_HDR_ERR
+/* Frame parsed beyind 256 first bytes */
+#define FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED FM_FD_ERR_BLOCK_LIMIT_EXCEEDED
+/* FPM Frame Processing Timeout Exceeded */
+#define FM_PORT_FRM_ERR_PROCESS_TIMEOUT 0x00000001
+
+struct fman_port;
+
+/* A structure for additional Rx port parameters */
+struct fman_port_rx_params {
+ u32 err_fqid; /* Error Queue Id. */
+ u32 dflt_fqid; /* Default Queue Id. */
+ /* Which external buffer pools are used
+ * (up to FMAN_PORT_MAX_EXT_POOLS_NUM), and their sizes.
+ */
+ struct fman_ext_pools ext_buf_pools;
+};
+
+/* A structure for additional non-Rx port parameters */
+struct fman_port_non_rx_params {
+ /* Error Queue Id. */
+ u32 err_fqid;
+ /* For Tx - Default Confirmation queue, 0 means no Tx confirmation
+ * for processed frames. For OP port - default Rx queue.
+ */
+ u32 dflt_fqid;
+};
+
+/* A union for additional parameters depending on port type */
+union fman_port_specific_params {
+ /* Rx port parameters structure */
+ struct fman_port_rx_params rx_params;
+ /* Non-Rx port parameters structure */
+ struct fman_port_non_rx_params non_rx_params;
+};
+
+/* A structure representing FM initialization parameters */
+struct fman_port_params {
+ /* Virtual Address of memory mapped FM Port registers. */
+ void *fm;
+ union fman_port_specific_params specific_params;
+ /* Additional parameters depending on port type. */
+};
+
+int fman_port_config(struct fman_port *port, struct fman_port_params *params);
+
+int fman_port_init(struct fman_port *port);
+
+int fman_port_cfg_buf_prefix_content(struct fman_port *port,
+ struct fman_buffer_prefix_content
+ *buffer_prefix_content);
+
+int fman_port_disable(struct fman_port *port);
+
+int fman_port_enable(struct fman_port *port);
+
+u32 fman_port_get_qman_channel_id(struct fman_port *port);
+
+struct fman_port *fman_port_bind(struct device *dev);
+
+#endif /* __FMAN_PORT_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.c b/drivers/net/ethernet/freescale/fman/fman_sp.c
new file mode 100644
index 000000000000..f9e7aa385cba
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_sp.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fman_sp.h"
+#include "fman.h"
+
+void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools
+ *fm_ext_pools,
+ u8 *ordered_array,
+ u16 *sizes_array)
+{
+ u16 buf_size = 0;
+ int i = 0, j = 0, k = 0;
+
+ /* First we copy the external buffers pools information
+ * to an ordered local array
+ */
+ for (i = 0; i < fm_ext_pools->num_of_pools_used; i++) {
+ /* get pool size */
+ buf_size = fm_ext_pools->ext_buf_pool[i].size;
+
+ /* keep sizes in an array according to poolId
+ * for direct access
+ */
+ sizes_array[fm_ext_pools->ext_buf_pool[i].id] = buf_size;
+
+ /* save poolId in an ordered array according to size */
+ for (j = 0; j <= i; j++) {
+ /* this is the next free place in the array */
+ if (j == i)
+ ordered_array[i] =
+ fm_ext_pools->ext_buf_pool[i].id;
+ else {
+ /* find the right place for this poolId */
+ if (buf_size < sizes_array[ordered_array[j]]) {
+ /* move the pool_ids one place ahead
+ * to make room for this poolId
+ */
+ for (k = i; k > j; k--)
+ ordered_array[k] =
+ ordered_array[k - 1];
+
+ /* now k==j, this is the place for
+ * the new size
+ */
+ ordered_array[k] =
+ fm_ext_pools->ext_buf_pool[i].id;
+ break;
+ }
+ }
+ }
+ }
+}
+
+int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy *
+ int_context_data_copy,
+ struct fman_buffer_prefix_content *
+ buffer_prefix_content,
+ struct fman_sp_buf_margins *buf_margins,
+ struct fman_sp_buffer_offsets *buffer_offsets,
+ u8 *internal_buf_offset)
+{
+ u32 tmp;
+
+ /* Align start of internal context data to 16 byte */
+ int_context_data_copy->ext_buf_offset = (u16)
+ ((buffer_prefix_content->priv_data_size & (OFFSET_UNITS - 1)) ?
+ ((buffer_prefix_content->priv_data_size + OFFSET_UNITS) &
+ ~(u16)(OFFSET_UNITS - 1)) :
+ buffer_prefix_content->priv_data_size);
+
+ /* Translate margin and int_context params to FM parameters */
+ /* Initialize with illegal value. Later we'll set legal values. */
+ buffer_offsets->prs_result_offset = (u32)ILLEGAL_BASE;
+ buffer_offsets->time_stamp_offset = (u32)ILLEGAL_BASE;
+ buffer_offsets->hash_result_offset = (u32)ILLEGAL_BASE;
+
+ /* Internally the driver supports 4 options
+ * 1. prsResult/timestamp/hashResult selection (in fact 8 options,
+ * but for simplicity we'll
+ * relate to it as 1).
+ * 2. All IC context (from AD) not including debug.
+ */
+
+ /* This case covers the options under 1 */
+ /* Copy size must be in 16-byte granularity. */
+ int_context_data_copy->size =
+ (u16)((buffer_prefix_content->pass_prs_result ? 32 : 0) +
+ ((buffer_prefix_content->pass_time_stamp ||
+ buffer_prefix_content->pass_hash_result) ? 16 : 0));
+
+ /* Align start of internal context data to 16 byte */
+ int_context_data_copy->int_context_offset =
+ (u8)(buffer_prefix_content->pass_prs_result ? 32 :
+ ((buffer_prefix_content->pass_time_stamp ||
+ buffer_prefix_content->pass_hash_result) ? 64 : 0));
+
+ if (buffer_prefix_content->pass_prs_result)
+ buffer_offsets->prs_result_offset =
+ int_context_data_copy->ext_buf_offset;
+ if (buffer_prefix_content->pass_time_stamp)
+ buffer_offsets->time_stamp_offset =
+ buffer_prefix_content->pass_prs_result ?
+ (int_context_data_copy->ext_buf_offset +
+ sizeof(struct fman_prs_result)) :
+ int_context_data_copy->ext_buf_offset;
+ if (buffer_prefix_content->pass_hash_result)
+ /* If PR is not requested, whether TS is
+ * requested or not, IC will be copied from TS
+ */
+ buffer_offsets->hash_result_offset =
+ buffer_prefix_content->pass_prs_result ?
+ (int_context_data_copy->ext_buf_offset +
+ sizeof(struct fman_prs_result) + 8) :
+ int_context_data_copy->ext_buf_offset + 8;
+
+ if (int_context_data_copy->size)
+ buf_margins->start_margins =
+ (u16)(int_context_data_copy->ext_buf_offset +
+ int_context_data_copy->size);
+ else
+ /* No Internal Context passing, STartMargin is
+ * immediately after private_info
+ */
+ buf_margins->start_margins =
+ buffer_prefix_content->priv_data_size;
+
+ /* align data start */
+ tmp = (u32)(buf_margins->start_margins %
+ buffer_prefix_content->data_align);
+ if (tmp)
+ buf_margins->start_margins +=
+ (buffer_prefix_content->data_align - tmp);
+ buffer_offsets->data_offset = buf_margins->start_margins;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.h b/drivers/net/ethernet/freescale/fman/fman_sp.h
new file mode 100644
index 000000000000..820b7f63088f
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_sp.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FM_SP_H
+#define __FM_SP_H
+
+#include "fman.h"
+#include <linux/types.h>
+
+#define ILLEGAL_BASE (~0)
+
+/* defaults */
+#define DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN 64
+
+/* Registers bit fields */
+#define FMAN_SP_EXT_BUF_POOL_EN_COUNTER 0x40000000
+#define FMAN_SP_EXT_BUF_POOL_VALID 0x80000000
+#define FMAN_SP_EXT_BUF_POOL_BACKUP 0x20000000
+#define FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE 0x00100000
+#define FMAN_SP_SG_DISABLE 0x80000000
+
+/* shifts */
+#define FMAN_SP_EXT_BUF_MARG_START_SHIFT 16
+#define FMAN_SP_DMA_ATTR_SWP_SHIFT 30
+#define FMAN_SP_IC_TO_EXT_SHIFT 16
+#define FMAN_SP_IC_FROM_INT_SHIFT 8
+
+/* structure for defining internal context copying */
+struct fman_sp_int_context_data_copy {
+ /* < Offset in External buffer to which internal
+ * context is copied to (Rx) or taken from (Tx, Op).
+ */
+ u16 ext_buf_offset;
+ /* Offset within internal context to copy from
+ * (Rx) or to copy to (Tx, Op).
+ */
+ u8 int_context_offset;
+ /* Internal offset size to be copied */
+ u16 size;
+};
+
+/* struct for defining external buffer margins */
+struct fman_sp_buf_margins {
+ /* Number of bytes to be left at the beginning
+ * of the external buffer (must be divisible by 16)
+ */
+ u16 start_margins;
+ /* number of bytes to be left at the end
+ * of the external buffer(must be divisible by 16)
+ */
+ u16 end_margins;
+};
+
+struct fman_sp_buffer_offsets {
+ u32 data_offset;
+ u32 prs_result_offset;
+ u32 time_stamp_offset;
+ u32 hash_result_offset;
+};
+
+int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy
+ *int_context_data_copy,
+ struct fman_buffer_prefix_content
+ *buffer_prefix_content,
+ struct fman_sp_buf_margins *buf_margins,
+ struct fman_sp_buffer_offsets
+ *buffer_offsets,
+ u8 *internal_buf_offset);
+
+void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools
+ *fm_ext_pools,
+ u8 *ordered_array,
+ u16 *sizes_array);
+
+#endif /* __FM_SP_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c
new file mode 100644
index 000000000000..efabb04a1ae8
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_tgec.h"
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+#include <linux/crc32.h>
+
+/* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */
+#define TGEC_TX_IPG_LENGTH_MASK 0x000003ff
+
+/* Command and Configuration Register (COMMAND_CONFIG) */
+#define CMD_CFG_NO_LEN_CHK 0x00020000
+#define CMD_CFG_PAUSE_IGNORE 0x00000100
+#define CMF_CFG_CRC_FWD 0x00000040
+#define CMD_CFG_PROMIS_EN 0x00000010
+#define CMD_CFG_RX_EN 0x00000002
+#define CMD_CFG_TX_EN 0x00000001
+
+/* Interrupt Mask Register (IMASK) */
+#define TGEC_IMASK_MDIO_SCAN_EVENT 0x00010000
+#define TGEC_IMASK_MDIO_CMD_CMPL 0x00008000
+#define TGEC_IMASK_REM_FAULT 0x00004000
+#define TGEC_IMASK_LOC_FAULT 0x00002000
+#define TGEC_IMASK_TX_ECC_ER 0x00001000
+#define TGEC_IMASK_TX_FIFO_UNFL 0x00000800
+#define TGEC_IMASK_TX_FIFO_OVFL 0x00000400
+#define TGEC_IMASK_TX_ER 0x00000200
+#define TGEC_IMASK_RX_FIFO_OVFL 0x00000100
+#define TGEC_IMASK_RX_ECC_ER 0x00000080
+#define TGEC_IMASK_RX_JAB_FRM 0x00000040
+#define TGEC_IMASK_RX_OVRSZ_FRM 0x00000020
+#define TGEC_IMASK_RX_RUNT_FRM 0x00000010
+#define TGEC_IMASK_RX_FRAG_FRM 0x00000008
+#define TGEC_IMASK_RX_LEN_ER 0x00000004
+#define TGEC_IMASK_RX_CRC_ER 0x00000002
+#define TGEC_IMASK_RX_ALIGN_ER 0x00000001
+
+/* Hashtable Control Register (HASHTABLE_CTRL) */
+#define TGEC_HASH_MCAST_SHIFT 23
+#define TGEC_HASH_MCAST_EN 0x00000200
+#define TGEC_HASH_ADR_MSK 0x000001ff
+
+#define DEFAULT_TX_IPG_LENGTH 12
+#define DEFAULT_MAX_FRAME_LENGTH 0x600
+#define DEFAULT_PAUSE_QUANT 0xf000
+
+/* number of pattern match registers (entries) */
+#define TGEC_NUM_OF_PADDRS 1
+
+/* Group address bit indication */
+#define GROUP_ADDRESS 0x0000010000000000LL
+
+/* Hash table size (= 32 bits*8 regs) */
+#define TGEC_HASH_TABLE_SIZE 512
+
+/* tGEC memory map */
+struct tgec_regs {
+ u32 tgec_id; /* 0x000 Controller ID */
+ u32 reserved001[1]; /* 0x004 */
+ u32 command_config; /* 0x008 Control and configuration */
+ u32 mac_addr_0; /* 0x00c Lower 32 bits of the MAC adr */
+ u32 mac_addr_1; /* 0x010 Upper 16 bits of the MAC adr */
+ u32 maxfrm; /* 0x014 Maximum frame length */
+ u32 pause_quant; /* 0x018 Pause quanta */
+ u32 rx_fifo_sections; /* 0x01c */
+ u32 tx_fifo_sections; /* 0x020 */
+ u32 rx_fifo_almost_f_e; /* 0x024 */
+ u32 tx_fifo_almost_f_e; /* 0x028 */
+ u32 hashtable_ctrl; /* 0x02c Hash table control */
+ u32 mdio_cfg_status; /* 0x030 */
+ u32 mdio_command; /* 0x034 */
+ u32 mdio_data; /* 0x038 */
+ u32 mdio_regaddr; /* 0x03c */
+ u32 status; /* 0x040 */
+ u32 tx_ipg_len; /* 0x044 Transmitter inter-packet-gap */
+ u32 mac_addr_2; /* 0x048 Lower 32 bits of 2nd MAC adr */
+ u32 mac_addr_3; /* 0x04c Upper 16 bits of 2nd MAC adr */
+ u32 rx_fifo_ptr_rd; /* 0x050 */
+ u32 rx_fifo_ptr_wr; /* 0x054 */
+ u32 tx_fifo_ptr_rd; /* 0x058 */
+ u32 tx_fifo_ptr_wr; /* 0x05c */
+ u32 imask; /* 0x060 Interrupt mask */
+ u32 ievent; /* 0x064 Interrupt event */
+ u32 udp_port; /* 0x068 Defines a UDP Port number */
+ u32 type_1588v2; /* 0x06c Type field for 1588v2 */
+ u32 reserved070[4]; /* 0x070 */
+ /* 10Ge Statistics Counter */
+ u32 tfrm_u; /* 80 aFramesTransmittedOK */
+ u32 tfrm_l; /* 84 aFramesTransmittedOK */
+ u32 rfrm_u; /* 88 aFramesReceivedOK */
+ u32 rfrm_l; /* 8c aFramesReceivedOK */
+ u32 rfcs_u; /* 90 aFrameCheckSequenceErrors */
+ u32 rfcs_l; /* 94 aFrameCheckSequenceErrors */
+ u32 raln_u; /* 98 aAlignmentErrors */
+ u32 raln_l; /* 9c aAlignmentErrors */
+ u32 txpf_u; /* A0 aPAUSEMACCtrlFramesTransmitted */
+ u32 txpf_l; /* A4 aPAUSEMACCtrlFramesTransmitted */
+ u32 rxpf_u; /* A8 aPAUSEMACCtrlFramesReceived */
+ u32 rxpf_l; /* Ac aPAUSEMACCtrlFramesReceived */
+ u32 rlong_u; /* B0 aFrameTooLongErrors */
+ u32 rlong_l; /* B4 aFrameTooLongErrors */
+ u32 rflr_u; /* B8 aInRangeLengthErrors */
+ u32 rflr_l; /* Bc aInRangeLengthErrors */
+ u32 tvlan_u; /* C0 VLANTransmittedOK */
+ u32 tvlan_l; /* C4 VLANTransmittedOK */
+ u32 rvlan_u; /* C8 VLANReceivedOK */
+ u32 rvlan_l; /* Cc VLANReceivedOK */
+ u32 toct_u; /* D0 if_out_octets */
+ u32 toct_l; /* D4 if_out_octets */
+ u32 roct_u; /* D8 if_in_octets */
+ u32 roct_l; /* Dc if_in_octets */
+ u32 ruca_u; /* E0 if_in_ucast_pkts */
+ u32 ruca_l; /* E4 if_in_ucast_pkts */
+ u32 rmca_u; /* E8 ifInMulticastPkts */
+ u32 rmca_l; /* Ec ifInMulticastPkts */
+ u32 rbca_u; /* F0 ifInBroadcastPkts */
+ u32 rbca_l; /* F4 ifInBroadcastPkts */
+ u32 terr_u; /* F8 if_out_errors */
+ u32 terr_l; /* Fc if_out_errors */
+ u32 reserved100[2]; /* 100-108 */
+ u32 tuca_u; /* 108 if_out_ucast_pkts */
+ u32 tuca_l; /* 10c if_out_ucast_pkts */
+ u32 tmca_u; /* 110 ifOutMulticastPkts */
+ u32 tmca_l; /* 114 ifOutMulticastPkts */
+ u32 tbca_u; /* 118 ifOutBroadcastPkts */
+ u32 tbca_l; /* 11c ifOutBroadcastPkts */
+ u32 rdrp_u; /* 120 etherStatsDropEvents */
+ u32 rdrp_l; /* 124 etherStatsDropEvents */
+ u32 reoct_u; /* 128 etherStatsOctets */
+ u32 reoct_l; /* 12c etherStatsOctets */
+ u32 rpkt_u; /* 130 etherStatsPkts */
+ u32 rpkt_l; /* 134 etherStatsPkts */
+ u32 trund_u; /* 138 etherStatsUndersizePkts */
+ u32 trund_l; /* 13c etherStatsUndersizePkts */
+ u32 r64_u; /* 140 etherStatsPkts64Octets */
+ u32 r64_l; /* 144 etherStatsPkts64Octets */
+ u32 r127_u; /* 148 etherStatsPkts65to127Octets */
+ u32 r127_l; /* 14c etherStatsPkts65to127Octets */
+ u32 r255_u; /* 150 etherStatsPkts128to255Octets */
+ u32 r255_l; /* 154 etherStatsPkts128to255Octets */
+ u32 r511_u; /* 158 etherStatsPkts256to511Octets */
+ u32 r511_l; /* 15c etherStatsPkts256to511Octets */
+ u32 r1023_u; /* 160 etherStatsPkts512to1023Octets */
+ u32 r1023_l; /* 164 etherStatsPkts512to1023Octets */
+ u32 r1518_u; /* 168 etherStatsPkts1024to1518Octets */
+ u32 r1518_l; /* 16c etherStatsPkts1024to1518Octets */
+ u32 r1519x_u; /* 170 etherStatsPkts1519toX */
+ u32 r1519x_l; /* 174 etherStatsPkts1519toX */
+ u32 trovr_u; /* 178 etherStatsOversizePkts */
+ u32 trovr_l; /* 17c etherStatsOversizePkts */
+ u32 trjbr_u; /* 180 etherStatsJabbers */
+ u32 trjbr_l; /* 184 etherStatsJabbers */
+ u32 trfrg_u; /* 188 etherStatsFragments */
+ u32 trfrg_l; /* 18C etherStatsFragments */
+ u32 rerr_u; /* 190 if_in_errors */
+ u32 rerr_l; /* 194 if_in_errors */
+};
+
+struct tgec_cfg {
+ bool pause_ignore;
+ bool promiscuous_mode_enable;
+ u16 max_frame_length;
+ u16 pause_quant;
+ u32 tx_ipg_length;
+};
+
+struct fman_mac {
+ /* Pointer to the memory mapped registers. */
+ struct tgec_regs __iomem *regs;
+ /* MAC address of device; */
+ u64 addr;
+ u16 max_speed;
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *exception_cb;
+ fman_mac_exception_cb *event_cb;
+ /* pointer to driver's global address hash table */
+ struct eth_hash_t *multicast_addr_hash;
+ /* pointer to driver's individual address hash table */
+ struct eth_hash_t *unicast_addr_hash;
+ u8 mac_id;
+ u32 exceptions;
+ struct tgec_cfg *cfg;
+ void *fm;
+ struct fman_rev_info fm_rev_info;
+};
+
+static void set_mac_address(struct tgec_regs __iomem *regs, u8 *adr)
+{
+ u32 tmp0, tmp1;
+
+ tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24);
+ tmp1 = (u32)(adr[4] | adr[5] << 8);
+ iowrite32be(tmp0, &regs->mac_addr_0);
+ iowrite32be(tmp1, &regs->mac_addr_1);
+}
+
+static void set_dflts(struct tgec_cfg *cfg)
+{
+ cfg->promiscuous_mode_enable = false;
+ cfg->pause_ignore = false;
+ cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH;
+ cfg->max_frame_length = DEFAULT_MAX_FRAME_LENGTH;
+ cfg->pause_quant = DEFAULT_PAUSE_QUANT;
+}
+
+static int init(struct tgec_regs __iomem *regs, struct tgec_cfg *cfg,
+ u32 exception_mask)
+{
+ u32 tmp;
+
+ /* Config */
+ tmp = CMF_CFG_CRC_FWD;
+ if (cfg->promiscuous_mode_enable)
+ tmp |= CMD_CFG_PROMIS_EN;
+ if (cfg->pause_ignore)
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+ /* Payload length check disable */
+ tmp |= CMD_CFG_NO_LEN_CHK;
+ iowrite32be(tmp, &regs->command_config);
+
+ /* Max Frame Length */
+ iowrite32be((u32)cfg->max_frame_length, &regs->maxfrm);
+ /* Pause Time */
+ iowrite32be(cfg->pause_quant, &regs->pause_quant);
+
+ /* clear all pending events and set-up interrupts */
+ iowrite32be(0xffffffff, &regs->ievent);
+ iowrite32be(ioread32be(&regs->imask) | exception_mask, &regs->imask);
+
+ return 0;
+}
+
+static int check_init_parameters(struct fman_mac *tgec)
+{
+ if (tgec->max_speed < SPEED_10000) {
+ pr_err("10G MAC driver only support 10G speed\n");
+ return -EINVAL;
+ }
+ if (tgec->addr == 0) {
+ pr_err("Ethernet 10G MAC Must have valid MAC Address\n");
+ return -EINVAL;
+ }
+ if (!tgec->exception_cb) {
+ pr_err("uninitialized exception_cb\n");
+ return -EINVAL;
+ }
+ if (!tgec->event_cb) {
+ pr_err("uninitialized event_cb\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_exception_flag(enum fman_mac_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FM_MAC_EX_10G_MDIO_SCAN_EVENT:
+ bit_mask = TGEC_IMASK_MDIO_SCAN_EVENT;
+ break;
+ case FM_MAC_EX_10G_MDIO_CMD_CMPL:
+ bit_mask = TGEC_IMASK_MDIO_CMD_CMPL;
+ break;
+ case FM_MAC_EX_10G_REM_FAULT:
+ bit_mask = TGEC_IMASK_REM_FAULT;
+ break;
+ case FM_MAC_EX_10G_LOC_FAULT:
+ bit_mask = TGEC_IMASK_LOC_FAULT;
+ break;
+ case FM_MAC_EX_10G_TX_ECC_ER:
+ bit_mask = TGEC_IMASK_TX_ECC_ER;
+ break;
+ case FM_MAC_EX_10G_TX_FIFO_UNFL:
+ bit_mask = TGEC_IMASK_TX_FIFO_UNFL;
+ break;
+ case FM_MAC_EX_10G_TX_FIFO_OVFL:
+ bit_mask = TGEC_IMASK_TX_FIFO_OVFL;
+ break;
+ case FM_MAC_EX_10G_TX_ER:
+ bit_mask = TGEC_IMASK_TX_ER;
+ break;
+ case FM_MAC_EX_10G_RX_FIFO_OVFL:
+ bit_mask = TGEC_IMASK_RX_FIFO_OVFL;
+ break;
+ case FM_MAC_EX_10G_RX_ECC_ER:
+ bit_mask = TGEC_IMASK_RX_ECC_ER;
+ break;
+ case FM_MAC_EX_10G_RX_JAB_FRM:
+ bit_mask = TGEC_IMASK_RX_JAB_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_OVRSZ_FRM:
+ bit_mask = TGEC_IMASK_RX_OVRSZ_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_RUNT_FRM:
+ bit_mask = TGEC_IMASK_RX_RUNT_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_FRAG_FRM:
+ bit_mask = TGEC_IMASK_RX_FRAG_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_LEN_ER:
+ bit_mask = TGEC_IMASK_RX_LEN_ER;
+ break;
+ case FM_MAC_EX_10G_RX_CRC_ER:
+ bit_mask = TGEC_IMASK_RX_CRC_ER;
+ break;
+ case FM_MAC_EX_10G_RX_ALIGN_ER:
+ bit_mask = TGEC_IMASK_RX_ALIGN_ER;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static void tgec_err_exception(void *handle)
+{
+ struct fman_mac *tgec = (struct fman_mac *)handle;
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 event;
+
+ /* do not handle MDIO events */
+ event = ioread32be(&regs->ievent) &
+ ~(TGEC_IMASK_MDIO_SCAN_EVENT |
+ TGEC_IMASK_MDIO_CMD_CMPL);
+
+ event &= ioread32be(&regs->imask);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & TGEC_IMASK_REM_FAULT)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_REM_FAULT);
+ if (event & TGEC_IMASK_LOC_FAULT)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_LOC_FAULT);
+ if (event & TGEC_IMASK_TX_ECC_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ECC_ER);
+ if (event & TGEC_IMASK_TX_FIFO_UNFL)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_UNFL);
+ if (event & TGEC_IMASK_TX_FIFO_OVFL)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_OVFL);
+ if (event & TGEC_IMASK_TX_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ER);
+ if (event & TGEC_IMASK_RX_FIFO_OVFL)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FIFO_OVFL);
+ if (event & TGEC_IMASK_RX_ECC_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ECC_ER);
+ if (event & TGEC_IMASK_RX_JAB_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_JAB_FRM);
+ if (event & TGEC_IMASK_RX_OVRSZ_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_OVRSZ_FRM);
+ if (event & TGEC_IMASK_RX_RUNT_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_RUNT_FRM);
+ if (event & TGEC_IMASK_RX_FRAG_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FRAG_FRM);
+ if (event & TGEC_IMASK_RX_LEN_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_LEN_ER);
+ if (event & TGEC_IMASK_RX_CRC_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_CRC_ER);
+ if (event & TGEC_IMASK_RX_ALIGN_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ALIGN_ER);
+}
+
+static void free_init_resources(struct fman_mac *tgec)
+{
+ fman_unregister_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id,
+ FMAN_INTR_TYPE_ERR);
+
+ /* release the driver's group hash table */
+ free_hash_table(tgec->multicast_addr_hash);
+ tgec->multicast_addr_hash = NULL;
+
+ /* release the driver's individual hash table */
+ free_hash_table(tgec->unicast_addr_hash);
+ tgec->unicast_addr_hash = NULL;
+}
+
+static bool is_init_done(struct tgec_cfg *cfg)
+{
+ /* Checks if tGEC driver parameters were initialized */
+ if (!cfg)
+ return true;
+
+ return false;
+}
+
+int tgec_enable(struct fman_mac *tgec, enum comm_mode mode)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp |= CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp |= CMD_CFG_TX_EN;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_disable(struct fman_mac *tgec, enum comm_mode mode)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp &= ~CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp &= ~CMD_CFG_TX_EN;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (new_val)
+ tmp |= CMD_CFG_PROMIS_EN;
+ else
+ tmp &= ~CMD_CFG_PROMIS_EN;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val)
+{
+ if (is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tgec->cfg->max_frame_length = new_val;
+
+ return 0;
+}
+
+int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 __maybe_unused priority,
+ u16 pause_time, u16 __maybe_unused thresh_time)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ iowrite32be((u32)pause_time, &regs->pause_quant);
+
+ return 0;
+}
+
+int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (!en)
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+ else
+ tmp &= ~CMD_CFG_PAUSE_IGNORE;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *p_enet_addr)
+{
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr);
+ set_mac_address(tgec->regs, (u8 *)(*p_enet_addr));
+
+ return 0;
+}
+
+int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ struct eth_hash_entry *hash_entry;
+ u32 crc = 0xFFFFFFFF, hash;
+ u64 addr;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ if (!(addr & GROUP_ADDRESS)) {
+ /* Unicast addresses not supported in hash */
+ pr_err("Unicast Address\n");
+ return -EINVAL;
+ }
+ /* CRC calculation */
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+ /* Take 9 MSB bits */
+ hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK;
+
+ /* Create element to be added to the driver hash table */
+ hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL);
+ if (!hash_entry)
+ return -ENOMEM;
+ hash_entry->addr = addr;
+ INIT_LIST_HEAD(&hash_entry->node);
+
+ list_add_tail(&hash_entry->node,
+ &tgec->multicast_addr_hash->lsts[hash]);
+ iowrite32be((hash | TGEC_HASH_MCAST_EN), &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ struct eth_hash_entry *hash_entry = NULL;
+ struct list_head *pos;
+ u32 crc = 0xFFFFFFFF, hash;
+ u64 addr;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ addr = ((*(u64 *)eth_addr) >> 16);
+
+ /* CRC calculation */
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+ /* Take 9 MSB bits */
+ hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK;
+
+ list_for_each(pos, &tgec->multicast_addr_hash->lsts[hash]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&tgec->multicast_addr_hash->lsts[hash]))
+ iowrite32be((hash & ~TGEC_HASH_MCAST_EN),
+ &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int tgec_get_version(struct fman_mac *tgec, u32 *mac_version)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ *mac_version = ioread32be(&regs->tgec_id);
+
+ return 0;
+}
+
+int tgec_set_exception(struct fman_mac *tgec,
+ enum fman_mac_exceptions exception, bool enable)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 bit_mask = 0;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ tgec->exceptions |= bit_mask;
+ else
+ tgec->exceptions &= ~bit_mask;
+ } else {
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ if (enable)
+ iowrite32be(ioread32be(&regs->imask) | bit_mask, &regs->imask);
+ else
+ iowrite32be(ioread32be(&regs->imask) & ~bit_mask, &regs->imask);
+
+ return 0;
+}
+
+int tgec_init(struct fman_mac *tgec)
+{
+ struct tgec_cfg *cfg;
+ enet_addr_t eth_addr;
+ int err;
+
+ if (is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ if (DEFAULT_RESET_ON_INIT &&
+ (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) {
+ pr_err("Can't reset MAC!\n");
+ return -EINVAL;
+ }
+
+ err = check_init_parameters(tgec);
+ if (err)
+ return err;
+
+ cfg = tgec->cfg;
+
+ MAKE_ENET_ADDR_FROM_UINT64(tgec->addr, eth_addr);
+ set_mac_address(tgec->regs, (u8 *)eth_addr);
+
+ /* interrupts */
+ /* FM_10G_REM_N_LCL_FLT_EX_10GMAC_ERRATA_SW005 Errata workaround */
+ if (tgec->fm_rev_info.major <= 2)
+ tgec->exceptions &= ~(TGEC_IMASK_REM_FAULT |
+ TGEC_IMASK_LOC_FAULT);
+
+ err = init(tgec->regs, cfg, tgec->exceptions);
+ if (err) {
+ free_init_resources(tgec);
+ pr_err("TGEC version doesn't support this i/f mode\n");
+ return err;
+ }
+
+ /* Max Frame Length */
+ err = fman_set_mac_max_frame(tgec->fm, tgec->mac_id,
+ cfg->max_frame_length);
+ if (err) {
+ pr_err("Setting max frame length FAILED\n");
+ free_init_resources(tgec);
+ return -EINVAL;
+ }
+
+ /* FM_TX_FIFO_CORRUPTION_ERRATA_10GMAC_A007 Errata workaround */
+ if (tgec->fm_rev_info.major == 2) {
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ /* restore the default tx ipg Length */
+ tmp = (ioread32be(&regs->tx_ipg_len) &
+ ~TGEC_TX_IPG_LENGTH_MASK) | 12;
+
+ iowrite32be(tmp, &regs->tx_ipg_len);
+ }
+
+ tgec->multicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE);
+ if (!tgec->multicast_addr_hash) {
+ free_init_resources(tgec);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ tgec->unicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE);
+ if (!tgec->unicast_addr_hash) {
+ free_init_resources(tgec);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ fman_register_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id,
+ FMAN_INTR_TYPE_ERR, tgec_err_exception, tgec);
+
+ kfree(cfg);
+ tgec->cfg = NULL;
+
+ return 0;
+}
+
+int tgec_free(struct fman_mac *tgec)
+{
+ free_init_resources(tgec);
+
+ if (tgec->cfg)
+ tgec->cfg = NULL;
+
+ kfree(tgec->cfg);
+ kfree(tgec);
+
+ return 0;
+}
+
+struct fman_mac *tgec_config(struct fman_mac_params *params)
+{
+ struct fman_mac *tgec;
+ struct tgec_cfg *cfg;
+ void __iomem *base_addr;
+
+ base_addr = params->base_addr;
+ /* allocate memory for the UCC GETH data structure. */
+ tgec = kzalloc(sizeof(*tgec), GFP_KERNEL);
+ if (!tgec)
+ return NULL;
+
+ /* allocate memory for the 10G MAC driver parameters data structure. */
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg) {
+ tgec_free(tgec);
+ return NULL;
+ }
+
+ /* Plant parameter structure pointer */
+ tgec->cfg = cfg;
+
+ set_dflts(cfg);
+
+ tgec->regs = base_addr;
+ tgec->addr = ENET_ADDR_TO_UINT64(params->addr);
+ tgec->max_speed = params->max_speed;
+ tgec->mac_id = params->mac_id;
+ tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT |
+ TGEC_IMASK_REM_FAULT |
+ TGEC_IMASK_LOC_FAULT |
+ TGEC_IMASK_TX_ECC_ER |
+ TGEC_IMASK_TX_FIFO_UNFL |
+ TGEC_IMASK_TX_FIFO_OVFL |
+ TGEC_IMASK_TX_ER |
+ TGEC_IMASK_RX_FIFO_OVFL |
+ TGEC_IMASK_RX_ECC_ER |
+ TGEC_IMASK_RX_JAB_FRM |
+ TGEC_IMASK_RX_OVRSZ_FRM |
+ TGEC_IMASK_RX_RUNT_FRM |
+ TGEC_IMASK_RX_FRAG_FRM |
+ TGEC_IMASK_RX_CRC_ER |
+ TGEC_IMASK_RX_ALIGN_ER);
+ tgec->exception_cb = params->exception_cb;
+ tgec->event_cb = params->event_cb;
+ tgec->dev_id = params->dev_id;
+ tgec->fm = params->fm;
+
+ /* Save FMan revision */
+ fman_get_revision(tgec->fm, &tgec->fm_rev_info);
+
+ return tgec;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.h b/drivers/net/ethernet/freescale/fman/fman_tgec.h
new file mode 100644
index 000000000000..514bba9f47ce
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __TGEC_H
+#define __TGEC_H
+
+#include "fman_mac.h"
+
+struct fman_mac *tgec_config(struct fman_mac_params *params);
+int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val);
+int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *enet_addr);
+int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val);
+int tgec_enable(struct fman_mac *tgec, enum comm_mode mode);
+int tgec_disable(struct fman_mac *tgec, enum comm_mode mode);
+int tgec_init(struct fman_mac *tgec);
+int tgec_free(struct fman_mac *tgec);
+int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en);
+int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 priority,
+ u16 pause_time, u16 thresh_time);
+int tgec_set_exception(struct fman_mac *tgec,
+ enum fman_mac_exceptions exception, bool enable);
+int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr);
+int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr);
+int tgec_get_version(struct fman_mac *tgec, u32 *mac_version);
+
+#endif /* __TGEC_H */
diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c
new file mode 100644
index 000000000000..e33d9d24c1db
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/mac.c
@@ -0,0 +1,977 @@
+/* Copyright 2008-2015 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/device.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/phy_fixed.h>
+#include <linux/etherdevice.h>
+#include <linux/libfdt_env.h>
+
+#include "mac.h"
+#include "fman_mac.h"
+#include "fman_dtsec.h"
+#include "fman_tgec.h"
+#include "fman_memac.h"
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL FMan MAC API based driver");
+
+struct mac_priv_s {
+ struct device *dev;
+ void __iomem *vaddr;
+ u8 cell_index;
+ phy_interface_t phy_if;
+ struct fman *fman;
+ struct device_node *phy_node;
+ struct device_node *internal_phy_node;
+ /* List of multicast addresses */
+ struct list_head mc_addr_list;
+ struct platform_device *eth_dev;
+ struct fixed_phy_status *fixed_link;
+ u16 speed;
+ u16 max_speed;
+
+ int (*enable)(struct fman_mac *mac_dev, enum comm_mode mode);
+ int (*disable)(struct fman_mac *mac_dev, enum comm_mode mode);
+};
+
+struct mac_address {
+ u8 addr[ETH_ALEN];
+ struct list_head list;
+};
+
+static void mac_exception(void *handle, enum fman_mac_exceptions ex)
+{
+ struct mac_device *mac_dev;
+ struct mac_priv_s *priv;
+
+ mac_dev = handle;
+ priv = mac_dev->priv;
+
+ if (ex == FM_MAC_EX_10G_RX_FIFO_OVFL) {
+ /* don't flag RX FIFO after the first */
+ mac_dev->set_exception(mac_dev->fman_mac,
+ FM_MAC_EX_10G_RX_FIFO_OVFL, false);
+ dev_err(priv->dev, "10G MAC got RX FIFO Error = %x\n", ex);
+ }
+
+ dev_dbg(priv->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c",
+ __func__, ex);
+}
+
+static void set_fman_mac_params(struct mac_device *mac_dev,
+ struct fman_mac_params *params)
+{
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ params->base_addr = (typeof(params->base_addr))
+ devm_ioremap(priv->dev, mac_dev->res->start,
+ resource_size(mac_dev->res));
+ memcpy(&params->addr, mac_dev->addr, sizeof(mac_dev->addr));
+ params->max_speed = priv->max_speed;
+ params->phy_if = priv->phy_if;
+ params->basex_if = false;
+ params->mac_id = priv->cell_index;
+ params->fm = (void *)priv->fman;
+ params->exception_cb = mac_exception;
+ params->event_cb = mac_exception;
+ params->dev_id = mac_dev;
+ params->internal_phy_node = priv->internal_phy_node;
+}
+
+static int tgec_initialization(struct mac_device *mac_dev)
+{
+ int err;
+ struct mac_priv_s *priv;
+ struct fman_mac_params params;
+ u32 version;
+
+ priv = mac_dev->priv;
+
+ set_fman_mac_params(mac_dev, &params);
+
+ mac_dev->fman_mac = tgec_config(&params);
+ if (!mac_dev->fman_mac) {
+ err = -EINVAL;
+ goto _return;
+ }
+
+ err = tgec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm());
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = tgec_init(mac_dev->fman_mac);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ /* For 10G MAC, disable Tx ECC exception */
+ err = mac_dev->set_exception(mac_dev->fman_mac,
+ FM_MAC_EX_10G_TX_ECC_ER, false);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = tgec_get_version(mac_dev->fman_mac, &version);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ dev_info(priv->dev, "FMan XGEC version: 0x%08x\n", version);
+
+ goto _return;
+
+_return_fm_mac_free:
+ tgec_free(mac_dev->fman_mac);
+
+_return:
+ return err;
+}
+
+static int dtsec_initialization(struct mac_device *mac_dev)
+{
+ int err;
+ struct mac_priv_s *priv;
+ struct fman_mac_params params;
+ u32 version;
+
+ priv = mac_dev->priv;
+
+ set_fman_mac_params(mac_dev, &params);
+
+ mac_dev->fman_mac = dtsec_config(&params);
+ if (!mac_dev->fman_mac) {
+ err = -EINVAL;
+ goto _return;
+ }
+
+ err = dtsec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm());
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = dtsec_cfg_pad_and_crc(mac_dev->fman_mac, true);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = dtsec_init(mac_dev->fman_mac);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ /* For 1G MAC, disable by default the MIB counters overflow interrupt */
+ err = mac_dev->set_exception(mac_dev->fman_mac,
+ FM_MAC_EX_1G_RX_MIB_CNT_OVFL, false);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = dtsec_get_version(mac_dev->fman_mac, &version);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ dev_info(priv->dev, "FMan dTSEC version: 0x%08x\n", version);
+
+ goto _return;
+
+_return_fm_mac_free:
+ dtsec_free(mac_dev->fman_mac);
+
+_return:
+ return err;
+}
+
+static int memac_initialization(struct mac_device *mac_dev)
+{
+ int err;
+ struct mac_priv_s *priv;
+ struct fman_mac_params params;
+
+ priv = mac_dev->priv;
+
+ set_fman_mac_params(mac_dev, &params);
+
+ if (priv->max_speed == SPEED_10000)
+ params.phy_if = PHY_INTERFACE_MODE_XGMII;
+
+ mac_dev->fman_mac = memac_config(&params);
+ if (!mac_dev->fman_mac) {
+ err = -EINVAL;
+ goto _return;
+ }
+
+ err = memac_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm());
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = memac_cfg_reset_on_init(mac_dev->fman_mac, true);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = memac_cfg_fixed_link(mac_dev->fman_mac, priv->fixed_link);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = memac_init(mac_dev->fman_mac);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ dev_info(priv->dev, "FMan MEMAC\n");
+
+ goto _return;
+
+_return_fm_mac_free:
+ memac_free(mac_dev->fman_mac);
+
+_return:
+ return err;
+}
+
+static int start(struct mac_device *mac_dev)
+{
+ int err;
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ err = priv->enable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX);
+ if (!err && phy_dev)
+ phy_start(phy_dev);
+
+ return err;
+}
+
+static int stop(struct mac_device *mac_dev)
+{
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ if (mac_dev->phy_dev)
+ phy_stop(mac_dev->phy_dev);
+
+ return priv->disable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX);
+}
+
+static int set_multi(struct net_device *net_dev, struct mac_device *mac_dev)
+{
+ struct mac_priv_s *priv;
+ struct mac_address *old_addr, *tmp;
+ struct netdev_hw_addr *ha;
+ int err;
+ enet_addr_t *addr;
+
+ priv = mac_dev->priv;
+
+ /* Clear previous address list */
+ list_for_each_entry_safe(old_addr, tmp, &priv->mc_addr_list, list) {
+ addr = (enet_addr_t *)old_addr->addr;
+ err = mac_dev->remove_hash_mac_addr(mac_dev->fman_mac, addr);
+ if (err < 0)
+ return err;
+
+ list_del(&old_addr->list);
+ kfree(old_addr);
+ }
+
+ /* Add all the addresses from the new list */
+ netdev_for_each_mc_addr(ha, net_dev) {
+ addr = (enet_addr_t *)ha->addr;
+ err = mac_dev->add_hash_mac_addr(mac_dev->fman_mac, addr);
+ if (err < 0)
+ return err;
+
+ tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC);
+ if (!tmp)
+ return -ENOMEM;
+
+ ether_addr_copy(tmp->addr, ha->addr);
+ list_add(&tmp->list, &priv->mc_addr_list);
+ }
+ return 0;
+}
+
+/**
+ * fman_set_mac_active_pause
+ * @mac_dev: A pointer to the MAC device
+ * @rx: Pause frame setting for RX
+ * @tx: Pause frame setting for TX
+ *
+ * Set the MAC RX/TX PAUSE frames settings
+ *
+ * Avoid redundant calls to FMD, if the MAC driver already contains the desired
+ * active PAUSE settings. Otherwise, the new active settings should be reflected
+ * in FMan.
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx)
+{
+ struct fman_mac *fman_mac = mac_dev->fman_mac;
+ int err = 0;
+
+ if (rx != mac_dev->rx_pause_active) {
+ err = mac_dev->set_rx_pause(fman_mac, rx);
+ if (likely(err == 0))
+ mac_dev->rx_pause_active = rx;
+ }
+
+ if (tx != mac_dev->tx_pause_active) {
+ u16 pause_time = (tx ? FSL_FM_PAUSE_TIME_ENABLE :
+ FSL_FM_PAUSE_TIME_DISABLE);
+
+ err = mac_dev->set_tx_pause(fman_mac, 0, pause_time, 0);
+
+ if (likely(err == 0))
+ mac_dev->tx_pause_active = tx;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(fman_set_mac_active_pause);
+
+/**
+ * fman_get_pause_cfg
+ * @mac_dev: A pointer to the MAC device
+ * @rx: Return value for RX setting
+ * @tx: Return value for TX setting
+ *
+ * Determine the MAC RX/TX PAUSE frames settings based on PHY
+ * autonegotiation or values set by eththool.
+ *
+ * Return: Pointer to FMan device.
+ */
+void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause,
+ bool *tx_pause)
+{
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ u16 lcl_adv, rmt_adv;
+ u8 flowctrl;
+
+ *rx_pause = *tx_pause = false;
+
+ if (!phy_dev->duplex)
+ return;
+
+ /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings
+ * are those set by ethtool.
+ */
+ if (!mac_dev->autoneg_pause) {
+ *rx_pause = mac_dev->rx_pause_req;
+ *tx_pause = mac_dev->tx_pause_req;
+ return;
+ }
+
+ /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE
+ * settings depend on the result of the link negotiation.
+ */
+
+ /* get local capabilities */
+ lcl_adv = 0;
+ if (phy_dev->advertising & ADVERTISED_Pause)
+ lcl_adv |= ADVERTISE_PAUSE_CAP;
+ if (phy_dev->advertising & ADVERTISED_Asym_Pause)
+ lcl_adv |= ADVERTISE_PAUSE_ASYM;
+
+ /* get link partner capabilities */
+ rmt_adv = 0;
+ if (phy_dev->pause)
+ rmt_adv |= LPA_PAUSE_CAP;
+ if (phy_dev->asym_pause)
+ rmt_adv |= LPA_PAUSE_ASYM;
+
+ /* Calculate TX/RX settings based on local and peer advertised
+ * symmetric/asymmetric PAUSE capabilities.
+ */
+ flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+ if (flowctrl & FLOW_CTRL_RX)
+ *rx_pause = true;
+ if (flowctrl & FLOW_CTRL_TX)
+ *tx_pause = true;
+}
+EXPORT_SYMBOL(fman_get_pause_cfg);
+
+static void adjust_link_void(struct net_device *net_dev)
+{
+}
+
+static void adjust_link_dtsec(struct net_device *net_dev)
+{
+ struct device *dev = net_dev->dev.parent;
+ struct dpaa_eth_data *eth_data = dev->platform_data;
+ struct mac_device *mac_dev = eth_data->mac_dev;
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ struct fman_mac *fman_mac;
+ bool rx_pause, tx_pause;
+ int err;
+
+ fman_mac = mac_dev->fman_mac;
+ if (!phy_dev->link) {
+ dtsec_restart_autoneg(fman_mac);
+
+ return;
+ }
+
+ dtsec_adjust_link(fman_mac, phy_dev->speed);
+ fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
+ err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
+ if (err < 0)
+ netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err);
+}
+
+static void adjust_link_memac(struct net_device *net_dev)
+{
+ struct device *dev = net_dev->dev.parent;
+ struct dpaa_eth_data *eth_data = dev->platform_data;
+ struct mac_device *mac_dev = eth_data->mac_dev;
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ struct fman_mac *fman_mac;
+ bool rx_pause, tx_pause;
+ int err;
+
+ fman_mac = mac_dev->fman_mac;
+ memac_adjust_link(fman_mac, phy_dev->speed);
+
+ fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
+ err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
+ if (err < 0)
+ netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err);
+}
+
+/* Initializes driver's PHY state, and attaches to the PHY.
+ * Returns 0 on success.
+ */
+static int init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev,
+ void (*adj_lnk)(struct net_device *))
+{
+ struct phy_device *phy_dev;
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ phy_dev = of_phy_connect(net_dev, priv->phy_node, adj_lnk, 0,
+ priv->phy_if);
+ if (!phy_dev) {
+ netdev_err(net_dev, "Could not connect to PHY\n");
+ return -ENODEV;
+ }
+
+ /* Remove any features not supported by the controller */
+ phy_dev->supported &= mac_dev->if_support;
+ /* Enable the symmetric and asymmetric PAUSE frame advertisements,
+ * as most of the PHY drivers do not enable them by default.
+ */
+ phy_dev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+ phy_dev->advertising = phy_dev->supported;
+
+ mac_dev->phy_dev = phy_dev;
+
+ return 0;
+}
+
+static int dtsec_init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev)
+{
+ return init_phy(net_dev, mac_dev, &adjust_link_dtsec);
+}
+
+static int tgec_init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev)
+{
+ return init_phy(net_dev, mac_dev, adjust_link_void);
+}
+
+static int memac_init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev)
+{
+ return init_phy(net_dev, mac_dev, &adjust_link_memac);
+}
+
+static void setup_dtsec(struct mac_device *mac_dev)
+{
+ mac_dev->init_phy = dtsec_init_phy;
+ mac_dev->init = dtsec_initialization;
+ mac_dev->set_promisc = dtsec_set_promiscuous;
+ mac_dev->change_addr = dtsec_modify_mac_address;
+ mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address;
+ mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address;
+ mac_dev->set_tx_pause = dtsec_set_tx_pause_frames;
+ mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames;
+ mac_dev->set_exception = dtsec_set_exception;
+ mac_dev->set_multi = set_multi;
+ mac_dev->start = start;
+ mac_dev->stop = stop;
+
+ mac_dev->priv->enable = dtsec_enable;
+ mac_dev->priv->disable = dtsec_disable;
+}
+
+static void setup_tgec(struct mac_device *mac_dev)
+{
+ mac_dev->init_phy = tgec_init_phy;
+ mac_dev->init = tgec_initialization;
+ mac_dev->set_promisc = tgec_set_promiscuous;
+ mac_dev->change_addr = tgec_modify_mac_address;
+ mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address;
+ mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address;
+ mac_dev->set_tx_pause = tgec_set_tx_pause_frames;
+ mac_dev->set_rx_pause = tgec_accept_rx_pause_frames;
+ mac_dev->set_exception = tgec_set_exception;
+ mac_dev->set_multi = set_multi;
+ mac_dev->start = start;
+ mac_dev->stop = stop;
+
+ mac_dev->priv->enable = tgec_enable;
+ mac_dev->priv->disable = tgec_disable;
+}
+
+static void setup_memac(struct mac_device *mac_dev)
+{
+ mac_dev->init_phy = memac_init_phy;
+ mac_dev->init = memac_initialization;
+ mac_dev->set_promisc = memac_set_promiscuous;
+ mac_dev->change_addr = memac_modify_mac_address;
+ mac_dev->add_hash_mac_addr = memac_add_hash_mac_address;
+ mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address;
+ mac_dev->set_tx_pause = memac_set_tx_pause_frames;
+ mac_dev->set_rx_pause = memac_accept_rx_pause_frames;
+ mac_dev->set_exception = memac_set_exception;
+ mac_dev->set_multi = set_multi;
+ mac_dev->start = start;
+ mac_dev->stop = stop;
+
+ mac_dev->priv->enable = memac_enable;
+ mac_dev->priv->disable = memac_disable;
+}
+
+#define DTSEC_SUPPORTED \
+ (SUPPORTED_10baseT_Half \
+ | SUPPORTED_10baseT_Full \
+ | SUPPORTED_100baseT_Half \
+ | SUPPORTED_100baseT_Full \
+ | SUPPORTED_Autoneg \
+ | SUPPORTED_Pause \
+ | SUPPORTED_Asym_Pause \
+ | SUPPORTED_MII)
+
+static DEFINE_MUTEX(eth_lock);
+
+static const char phy_str[][11] = {
+ [PHY_INTERFACE_MODE_MII] = "mii",
+ [PHY_INTERFACE_MODE_GMII] = "gmii",
+ [PHY_INTERFACE_MODE_SGMII] = "sgmii",
+ [PHY_INTERFACE_MODE_TBI] = "tbi",
+ [PHY_INTERFACE_MODE_RMII] = "rmii",
+ [PHY_INTERFACE_MODE_RGMII] = "rgmii",
+ [PHY_INTERFACE_MODE_RGMII_ID] = "rgmii-id",
+ [PHY_INTERFACE_MODE_RGMII_RXID] = "rgmii-rxid",
+ [PHY_INTERFACE_MODE_RGMII_TXID] = "rgmii-txid",
+ [PHY_INTERFACE_MODE_RTBI] = "rtbi",
+ [PHY_INTERFACE_MODE_XGMII] = "xgmii"
+};
+
+static phy_interface_t __pure __attribute__((nonnull)) str2phy(const char *str)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(phy_str); i++)
+ if (strcmp(str, phy_str[i]) == 0)
+ return (phy_interface_t)i;
+
+ return PHY_INTERFACE_MODE_MII;
+}
+
+static const u16 phy2speed[] = {
+ [PHY_INTERFACE_MODE_MII] = SPEED_100,
+ [PHY_INTERFACE_MODE_GMII] = SPEED_1000,
+ [PHY_INTERFACE_MODE_SGMII] = SPEED_1000,
+ [PHY_INTERFACE_MODE_TBI] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RMII] = SPEED_100,
+ [PHY_INTERFACE_MODE_RGMII] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RTBI] = SPEED_1000,
+ [PHY_INTERFACE_MODE_XGMII] = SPEED_10000
+};
+
+static struct platform_device *dpaa_eth_add_device(int fman_id,
+ struct mac_device *mac_dev,
+ struct device_node *node)
+{
+ struct platform_device *pdev;
+ struct dpaa_eth_data data;
+ struct mac_priv_s *priv;
+ static int dpaa_eth_dev_cnt;
+ int ret;
+
+ priv = mac_dev->priv;
+
+ data.mac_dev = mac_dev;
+ data.mac_hw_id = priv->cell_index;
+ data.fman_hw_id = fman_id;
+ data.mac_node = node;
+
+ mutex_lock(&eth_lock);
+
+ pdev = platform_device_alloc("dpaa-ethernet", dpaa_eth_dev_cnt);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto no_mem;
+ }
+
+ ret = platform_device_add_data(pdev, &data, sizeof(data));
+ if (ret)
+ goto err;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto err;
+
+ dpaa_eth_dev_cnt++;
+ mutex_unlock(&eth_lock);
+
+ return pdev;
+
+err:
+ platform_device_put(pdev);
+no_mem:
+ mutex_unlock(&eth_lock);
+
+ return ERR_PTR(ret);
+}
+
+static const struct of_device_id mac_match[] = {
+ { .compatible = "fsl,fman-dtsec" },
+ { .compatible = "fsl,fman-xgec" },
+ { .compatible = "fsl,fman-memac" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mac_match);
+
+static int mac_probe(struct platform_device *_of_dev)
+{
+ int err, i, lenp, nph;
+ struct device *dev;
+ struct device_node *mac_node, *dev_node;
+ struct mac_device *mac_dev;
+ struct platform_device *of_dev;
+ struct resource res;
+ struct mac_priv_s *priv;
+ const u8 *mac_addr;
+ const char *char_prop;
+ const u32 *u32_prop;
+ u8 fman_id;
+
+ dev = &_of_dev->dev;
+ mac_node = dev->of_node;
+
+ mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL);
+ if (!mac_dev) {
+ err = -ENOMEM;
+ dev_err(dev, "devm_kzalloc() = %d\n", err);
+ goto _return;
+ }
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ err = -ENOMEM;
+ goto _return;
+ }
+
+ /* Save private information */
+ mac_dev->priv = priv;
+ priv->dev = dev;
+
+ if (of_device_is_compatible(mac_node, "fsl,fman-dtsec")) {
+ setup_dtsec(mac_dev);
+ priv->internal_phy_node = of_parse_phandle(mac_node,
+ "tbi-handle", 0);
+ } else if (of_device_is_compatible(mac_node, "fsl,fman-xgec")) {
+ setup_tgec(mac_dev);
+ } else if (of_device_is_compatible(mac_node, "fsl,fman-memac")) {
+ setup_memac(mac_dev);
+ priv->internal_phy_node = of_parse_phandle(mac_node,
+ "pcsphy-handle", 0);
+ } else {
+ dev_err(dev, "MAC node (%s) contains unsupported MAC\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return;
+ }
+
+ /* Register mac_dev */
+ dev_set_drvdata(dev, mac_dev);
+
+ INIT_LIST_HEAD(&priv->mc_addr_list);
+
+ /* Get the FM node */
+ dev_node = of_get_parent(mac_node);
+ if (!dev_node) {
+ dev_err(dev, "of_get_parent(%s) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+
+ of_dev = of_find_device_by_node(dev_node);
+ if (!of_dev) {
+ dev_err(dev, "of_find_device_by_node(%s) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+
+ /* Get the FMan cell-index */
+ u32_prop = of_get_property(dev_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(dev, "of_get_property(%s, cell-index) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+ WARN_ON(lenp != sizeof(u32));
+ /* cell-index 0 => FMan id 1 */
+ fman_id = (u8)(fdt32_to_cpu(u32_prop[0]) + 1);
+
+ priv->fman = fman_bind(&of_dev->dev);
+ if (!priv->fman) {
+ dev_err(dev, "fman_bind(%s) failed\n", dev_node->full_name);
+ err = -ENODEV;
+ goto _return_of_node_put;
+ }
+
+ of_node_put(dev_node);
+
+ /* Get the address of the memory mapped registers */
+ err = of_address_to_resource(mac_node, 0, &res);
+ if (err < 0) {
+ dev_err(dev, "of_address_to_resource(%s) = %d\n",
+ mac_node->full_name, err);
+ goto _return_dev_set_drvdata;
+ }
+
+ mac_dev->res = __devm_request_region(dev,
+ fman_get_mem_region(priv->fman),
+ res.start, res.end + 1 - res.start,
+ "mac");
+ if (!mac_dev->res) {
+ dev_err(dev, "__devm_request_mem_region(mac) failed\n");
+ err = -EBUSY;
+ goto _return_dev_set_drvdata;
+ }
+
+ priv->vaddr = devm_ioremap(dev, mac_dev->res->start,
+ mac_dev->res->end + 1 - mac_dev->res->start);
+ if (!priv->vaddr) {
+ dev_err(dev, "devm_ioremap() failed\n");
+ err = -EIO;
+ goto _return_dev_set_drvdata;
+ }
+
+ if (!of_device_is_available(mac_node)) {
+ devm_iounmap(dev, priv->vaddr);
+ __devm_release_region(dev, fman_get_mem_region(priv->fman),
+ res.start, res.end + 1 - res.start);
+ devm_kfree(dev, mac_dev);
+ dev_set_drvdata(dev, NULL);
+ return -ENODEV;
+ }
+
+ /* Get the cell-index */
+ u32_prop = of_get_property(mac_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(dev, "of_get_property(%s, cell-index) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+ WARN_ON(lenp != sizeof(u32));
+ priv->cell_index = (u8)fdt32_to_cpu(u32_prop[0]);
+
+ /* Get the MAC address */
+ mac_addr = of_get_mac_address(mac_node);
+ if (!mac_addr) {
+ dev_err(dev, "of_get_mac_address(%s) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+ memcpy(mac_dev->addr, mac_addr, sizeof(mac_dev->addr));
+
+ /* Get the port handles */
+ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL);
+ if (unlikely(nph < 0)) {
+ dev_err(dev, "of_count_phandle_with_args(%s, fsl,fman-ports) failed\n",
+ mac_node->full_name);
+ err = nph;
+ goto _return_dev_set_drvdata;
+ }
+
+ if (nph != ARRAY_SIZE(mac_dev->port)) {
+ dev_err(dev, "Not supported number of fman-ports handles of mac node %s from device tree\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) {
+ /* Find the port node */
+ dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i);
+ if (!dev_node) {
+ dev_err(dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+
+ of_dev = of_find_device_by_node(dev_node);
+ if (!of_dev) {
+ dev_err(dev, "of_find_device_by_node(%s) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+
+ mac_dev->port[i] = fman_port_bind(&of_dev->dev);
+ if (!mac_dev->port[i]) {
+ dev_err(dev, "dev_get_drvdata(%s) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+ of_node_put(dev_node);
+ }
+
+ /* Get the PHY connection type */
+ char_prop = (const char *)of_get_property(mac_node,
+ "phy-connection-type", NULL);
+ if (!char_prop) {
+ dev_warn(dev,
+ "of_get_property(%s, phy-connection-type) failed. Defaulting to MII\n",
+ mac_node->full_name);
+ priv->phy_if = PHY_INTERFACE_MODE_MII;
+ } else {
+ priv->phy_if = str2phy(char_prop);
+ }
+
+ priv->speed = phy2speed[priv->phy_if];
+ priv->max_speed = priv->speed;
+ mac_dev->if_support = DTSEC_SUPPORTED;
+ /* We don't support half-duplex in SGMII mode */
+ if (priv->phy_if == PHY_INTERFACE_MODE_SGMII)
+ mac_dev->if_support &= ~(SUPPORTED_10baseT_Half |
+ SUPPORTED_100baseT_Half);
+
+ /* Gigabit support (no half-duplex) */
+ if (priv->max_speed == 1000)
+ mac_dev->if_support |= SUPPORTED_1000baseT_Full;
+
+ /* The 10G interface only supports one mode */
+ if (priv->phy_if == PHY_INTERFACE_MODE_XGMII)
+ mac_dev->if_support = SUPPORTED_10000baseT_Full;
+
+ /* Get the rest of the PHY information */
+ priv->phy_node = of_parse_phandle(mac_node, "phy-handle", 0);
+ if (!priv->phy_node && of_phy_is_fixed_link(mac_node)) {
+ struct phy_device *phy;
+
+ err = of_phy_register_fixed_link(mac_node);
+ if (err)
+ goto _return_dev_set_drvdata;
+
+ priv->fixed_link = kzalloc(sizeof(*priv->fixed_link),
+ GFP_KERNEL);
+ if (!priv->fixed_link)
+ goto _return_dev_set_drvdata;
+
+ priv->phy_node = of_node_get(mac_node);
+ phy = of_phy_find_device(priv->phy_node);
+ if (!phy)
+ goto _return_dev_set_drvdata;
+
+ priv->fixed_link->link = phy->link;
+ priv->fixed_link->speed = phy->speed;
+ priv->fixed_link->duplex = phy->duplex;
+ priv->fixed_link->pause = phy->pause;
+ priv->fixed_link->asym_pause = phy->asym_pause;
+ }
+
+ err = mac_dev->init(mac_dev);
+ if (err < 0) {
+ dev_err(dev, "mac_dev->init() = %d\n", err);
+ of_node_put(priv->phy_node);
+ goto _return_dev_set_drvdata;
+ }
+
+ /* pause frame autonegotiation enabled */
+ mac_dev->autoneg_pause = true;
+
+ /* By intializing the values to false, force FMD to enable PAUSE frames
+ * on RX and TX
+ */
+ mac_dev->rx_pause_req = true;
+ mac_dev->tx_pause_req = true;
+ mac_dev->rx_pause_active = false;
+ mac_dev->tx_pause_active = false;
+ err = fman_set_mac_active_pause(mac_dev, true, true);
+ if (err < 0)
+ dev_err(dev, "fman_set_mac_active_pause() = %d\n", err);
+
+ dev_info(dev, "FMan MAC address: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
+ mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2],
+ mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5]);
+
+ priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev, mac_node);
+ if (IS_ERR(priv->eth_dev)) {
+ dev_err(dev, "failed to add Ethernet platform device for MAC %d\n",
+ priv->cell_index);
+ priv->eth_dev = NULL;
+ }
+
+ goto _return;
+
+_return_of_node_put:
+ of_node_put(dev_node);
+_return_dev_set_drvdata:
+ kfree(priv->fixed_link);
+ dev_set_drvdata(dev, NULL);
+_return:
+ return err;
+}
+
+static struct platform_driver mac_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = mac_match,
+ },
+ .probe = mac_probe,
+};
+
+builtin_platform_driver(mac_driver);
diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h
new file mode 100644
index 000000000000..0211cc9a46d6
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/mac.h
@@ -0,0 +1,97 @@
+/* Copyright 2008-2015 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MAC_H
+#define __MAC_H
+
+#include <linux/device.h>
+#include <linux/if_ether.h>
+#include <linux/phy.h>
+#include <linux/list.h>
+
+#include "fman_port.h"
+#include "fman.h"
+#include "fman_mac.h"
+
+struct fman_mac;
+struct mac_priv_s;
+
+struct mac_device {
+ struct resource *res;
+ u8 addr[ETH_ALEN];
+ struct fman_port *port[2];
+ u32 if_support;
+ struct phy_device *phy_dev;
+
+ bool autoneg_pause;
+ bool rx_pause_req;
+ bool tx_pause_req;
+ bool rx_pause_active;
+ bool tx_pause_active;
+ bool promisc;
+
+ int (*init_phy)(struct net_device *net_dev, struct mac_device *mac_dev);
+ int (*init)(struct mac_device *mac_dev);
+ int (*start)(struct mac_device *mac_dev);
+ int (*stop)(struct mac_device *mac_dev);
+ int (*set_promisc)(struct fman_mac *mac_dev, bool enable);
+ int (*change_addr)(struct fman_mac *mac_dev, enet_addr_t *enet_addr);
+ int (*set_multi)(struct net_device *net_dev,
+ struct mac_device *mac_dev);
+ int (*set_rx_pause)(struct fman_mac *mac_dev, bool en);
+ int (*set_tx_pause)(struct fman_mac *mac_dev, u8 priority,
+ u16 pause_time, u16 thresh_time);
+ int (*set_exception)(struct fman_mac *mac_dev,
+ enum fman_mac_exceptions exception, bool enable);
+ int (*add_hash_mac_addr)(struct fman_mac *mac_dev,
+ enet_addr_t *eth_addr);
+ int (*remove_hash_mac_addr)(struct fman_mac *mac_dev,
+ enet_addr_t *eth_addr);
+
+ struct fman_mac *fman_mac;
+ struct mac_priv_s *priv;
+};
+
+struct dpaa_eth_data {
+ struct device_node *mac_node;
+ struct mac_device *mac_dev;
+ int mac_hw_id;
+ int fman_hw_id;
+};
+
+extern const char *mac_driver_description;
+
+int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx);
+
+void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause,
+ bool *tx_pause);
+
+#endif /* __MAC_H */
diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
index cf8e54652df9..48a9c176e0d1 100644
--- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
@@ -1050,7 +1050,7 @@ static int fs_enet_probe(struct platform_device *ofdev)
ndev->netdev_ops = &fs_enet_netdev_ops;
ndev->watchdog_timeo = 2 * HZ;
netif_napi_add(ndev, &fep->napi, fs_enet_rx_napi, fpi->napi_weight);
- netif_napi_add(ndev, &fep->napi_tx, fs_enet_tx_napi, 2);
+ netif_tx_napi_add(ndev, &fep->napi_tx, fs_enet_tx_napi, 2);
ndev->ethtool_ops = &fs_ethtool_ops;
diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
index 016743e355de..bade2f8f9b5c 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
@@ -254,7 +254,7 @@ static void restart(struct net_device *dev)
int r;
u32 addrhi, addrlo;
- struct mii_bus* mii = fep->phydev->bus;
+ struct mii_bus *mii = fep->phydev->mdio.bus;
struct fec_info* fec_inf = mii->priv;
r = whack_reset(fep->fec.fecp);
@@ -363,7 +363,7 @@ static void stop(struct net_device *dev)
const struct fs_platform_info *fpi = fep->fpi;
struct fec __iomem *fecp = fep->fec.fecp;
- struct fec_info* feci= fep->phydev->bus->priv;
+ struct fec_info *feci = fep->phydev->mdio.bus->priv;
int i;
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
index 68a428de0bc0..1f015edcca22 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
@@ -172,23 +172,16 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
goto out_free_bus;
new_bus->phy_mask = ~0;
- new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq) {
- ret = -ENOMEM;
- goto out_unmap_regs;
- }
new_bus->parent = &ofdev->dev;
platform_set_drvdata(ofdev, new_bus);
ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
if (ret)
- goto out_free_irqs;
+ goto out_unmap_regs;
return 0;
-out_free_irqs:
- kfree(new_bus->irq);
out_unmap_regs:
iounmap(bitbang->dir);
out_free_bus:
@@ -205,7 +198,6 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev)
struct bb_info *bitbang = bus->priv;
mdiobus_unregister(bus);
- kfree(bus->irq);
free_mdio_bitbang(bus);
iounmap(bitbang->dir);
kfree(bitbang);
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
index 2be383e6d258..a89267b94352 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
@@ -166,23 +166,16 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
clrsetbits_be32(&fec->fecp->fec_mii_speed, 0x7E, fec->mii_speed);
new_bus->phy_mask = ~0;
- new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq) {
- ret = -ENOMEM;
- goto out_unmap_regs;
- }
new_bus->parent = &ofdev->dev;
platform_set_drvdata(ofdev, new_bus);
ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
if (ret)
- goto out_free_irqs;
+ goto out_unmap_regs;
return 0;
-out_free_irqs:
- kfree(new_bus->irq);
out_unmap_regs:
iounmap(fec->fecp);
out_res:
@@ -200,7 +193,6 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev)
struct fec_info *fec = bus->priv;
mdiobus_unregister(bus);
- kfree(bus->irq);
iounmap(fec->fecp);
kfree(fec);
mdiobus_free(bus);
diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
index 40071dad1c57..622005abf859 100644
--- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c
+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
@@ -69,7 +69,6 @@ struct fsl_pq_mdio {
struct fsl_pq_mdio_priv {
void __iomem *map;
struct fsl_pq_mii __iomem *regs;
- int irqs[PHY_MAX_ADDR];
};
/*
@@ -401,7 +400,6 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
new_bus->read = &fsl_pq_mdio_read;
new_bus->write = &fsl_pq_mdio_write;
new_bus->reset = &fsl_pq_mdio_reset;
- new_bus->irq = priv->irqs;
err = of_address_to_resource(np, 0, &res);
if (err < 0) {
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 3e233d924cce..2aa7b401cc3b 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -738,7 +738,6 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
struct gfar_private *priv = NULL;
struct device_node *np = ofdev->dev.of_node;
struct device_node *child = NULL;
- struct property *stash;
u32 stash_len = 0;
u32 stash_idx = 0;
unsigned int num_tx_qs, num_rx_qs;
@@ -854,9 +853,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
goto err_grp_init;
}
- stash = of_find_property(np, "bd-stash", NULL);
-
- if (stash) {
+ if (of_property_read_bool(np, "bd-stash")) {
priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING;
priv->bd_stash_en = 1;
}
@@ -1348,12 +1345,12 @@ static int gfar_probe(struct platform_device *ofdev)
if (priv->poll_mode == GFAR_SQ_POLLING) {
netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
gfar_poll_rx_sq, GFAR_DEV_WEIGHT);
- netif_napi_add(dev, &priv->gfargrp[i].napi_tx,
+ netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
gfar_poll_tx_sq, 2);
} else {
netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
gfar_poll_rx, GFAR_DEV_WEIGHT);
- netif_napi_add(dev, &priv->gfargrp[i].napi_tx,
+ netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
gfar_poll_tx, 2);
}
}
@@ -1837,7 +1834,7 @@ static void gfar_configure_serdes(struct net_device *dev)
* several seconds for it to come back.
*/
if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) {
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
return;
}
@@ -1852,7 +1849,7 @@ static void gfar_configure_serdes(struct net_device *dev)
BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
BMCR_SPEED1000);
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
}
static int __gfar_is_rx_idle(struct gfar_private *priv)
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index 650f7888e32b..cbddbe2d0429 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -1385,7 +1385,7 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
value &= ~0x1000; /* Turn off autonegotiation */
phy_write(tbiphy, ENET_TBI_MII_CR, value);
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
}
init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2);
@@ -1705,7 +1705,7 @@ static void uec_configure_serdes(struct net_device *dev)
* several seconds for it to come back.
*/
if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) {
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
return;
}
@@ -1716,7 +1716,7 @@ static void uec_configure_serdes(struct net_device *dev)
phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS);
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
}
/* Configure the PHY for dev.
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h
index cec95ac8687d..6ca94dc3dda3 100644
--- a/drivers/net/ethernet/hisilicon/hns/hnae.h
+++ b/drivers/net/ethernet/hisilicon/hns/hnae.h
@@ -35,7 +35,7 @@
#include <linux/phy.h>
#include <linux/types.h>
-#define HNAE_DRIVER_VERSION "1.3.0"
+#define HNAE_DRIVER_VERSION "2.0"
#define HNAE_DRIVER_NAME "hns"
#define HNAE_COPYRIGHT "Copyright(c) 2015 Huawei Corporation."
#define HNAE_DRIVER_STRING "Hisilicon Network Subsystem Driver"
@@ -63,6 +63,7 @@ do { \
#define AE_VERSION_1 ('6' << 16 | '6' << 8 | '0')
#define AE_VERSION_2 ('1' << 24 | '6' << 16 | '1' << 8 | '0')
+#define AE_IS_VER1(ver) ((ver) == AE_VERSION_1)
#define AE_NAME_SIZE 16
/* some said the RX and TX RCB format should not be the same in the future. But
@@ -144,23 +145,61 @@ enum hnae_led_state {
#define HNS_RXD_ASID_S 24
#define HNS_RXD_ASID_M (0xff << HNS_RXD_ASID_S)
+#define HNSV2_TXD_BUFNUM_S 0
+#define HNSV2_TXD_BUFNUM_M (0x7 << HNSV2_TXD_BUFNUM_S)
+#define HNSV2_TXD_RI_B 1
+#define HNSV2_TXD_L4CS_B 2
+#define HNSV2_TXD_L3CS_B 3
+#define HNSV2_TXD_FE_B 4
+#define HNSV2_TXD_VLD_B 5
+
+#define HNSV2_TXD_TSE_B 0
+#define HNSV2_TXD_VLAN_EN_B 1
+#define HNSV2_TXD_SNAP_B 2
+#define HNSV2_TXD_IPV6_B 3
+#define HNSV2_TXD_SCTP_B 4
+
/* hardware spec ring buffer format */
struct __packed hnae_desc {
__le64 addr;
union {
struct {
- __le16 asid_bufnum_pid;
+ union {
+ __le16 asid_bufnum_pid;
+ __le16 asid;
+ };
__le16 send_size;
- __le32 flag_ipoffset;
- __le32 reserved_3[4];
+ union {
+ __le32 flag_ipoffset;
+ struct {
+ __u8 bn_pid;
+ __u8 ra_ri_cs_fe_vld;
+ __u8 ip_offset;
+ __u8 tse_vlan_snap_v6_sctp_nth;
+ };
+ };
+ __le16 mss;
+ __u8 l4_len;
+ __u8 reserved1;
+ __le16 paylen;
+ __u8 vmid;
+ __u8 qid;
+ __le32 reserved2[2];
} tx;
struct {
__le32 ipoff_bnum_pid_flag;
__le16 pkt_len;
__le16 size;
- __le32 vlan_pri_asid;
- __le32 reserved_2[3];
+ union {
+ __le32 vlan_pri_asid;
+ struct {
+ __le16 asid;
+ __le16 vlan_cfi_pri;
+ };
+ };
+ __le32 rss_hash;
+ __le32 reserved_1[2];
} rx;
};
};
@@ -302,7 +341,8 @@ struct hnae_queue {
void __iomem *io_base;
phys_addr_t phy_base;
struct hnae_ae_dev *dev; /* the device who use this queue */
- struct hnae_ring rx_ring, tx_ring;
+ struct hnae_ring rx_ring ____cacheline_internodealigned_in_smp;
+ struct hnae_ring tx_ring ____cacheline_internodealigned_in_smp;
struct hnae_handle *handle;
};
@@ -435,6 +475,7 @@ struct hnae_ae_ops {
int (*set_mac_addr)(struct hnae_handle *handle, void *p);
int (*set_mc_addr)(struct hnae_handle *handle, void *addr);
int (*set_mtu)(struct hnae_handle *handle, int new_mtu);
+ void (*set_tso_stats)(struct hnae_handle *handle, int enable);
void (*update_stats)(struct hnae_handle *handle,
struct net_device_stats *net_stats);
void (*get_stats)(struct hnae_handle *handle, u64 *data);
@@ -446,6 +487,12 @@ struct hnae_ae_ops {
enum hnae_led_state status);
void (*get_regs)(struct hnae_handle *handle, void *data);
int (*get_regs_len)(struct hnae_handle *handle);
+ u32 (*get_rss_key_size)(struct hnae_handle *handle);
+ u32 (*get_rss_indir_size)(struct hnae_handle *handle);
+ int (*get_rss)(struct hnae_handle *handle, u32 *indir, u8 *key,
+ u8 *hfunc);
+ int (*set_rss)(struct hnae_handle *handle, const u32 *indir,
+ const u8 *key, const u8 hfunc);
};
struct hnae_ae_dev {
@@ -551,11 +598,9 @@ static inline void hnae_replace_buffer(struct hnae_ring *ring, int i,
struct hnae_desc_cb *res_cb)
{
struct hnae_buf_ops *bops = ring->q->handle->bops;
- struct hnae_desc_cb tmp_cb = ring->desc_cb[i];
bops->unmap_buffer(ring, &ring->desc_cb[i]);
ring->desc_cb[i] = *res_cb;
- *res_cb = tmp_cb;
ring->desc[i].addr = (__le64)ring->desc_cb[i].dma;
ring->desc[i].rx.ipoff_bnum_pid_flag = 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
index 1a16c0307b47..522b264866b4 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
@@ -252,7 +252,7 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
if (mac_cb->mac_type != HNAE_PORT_SERVICE)
return 0;
- ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, ENABLE);
+ ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, true);
if (ret) {
dev_err(handle->owner_dev,
"mac add mul_mac:%pM port%d fail, ret = %#x!\n",
@@ -261,7 +261,7 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
}
ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM,
- mac_addr, ENABLE);
+ mac_addr, true);
if (ret)
dev_err(handle->owner_dev,
"mac add mul_mac:%pM port%d fail, ret = %#x!\n",
@@ -277,12 +277,19 @@ static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu)
return hns_mac_set_mtu(mac_cb, new_mtu);
}
+static void hns_ae_set_tso_stats(struct hnae_handle *handle, int enable)
+{
+ struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+ hns_ppe_set_tso_enable(ppe_cb, enable);
+}
+
static int hns_ae_start(struct hnae_handle *handle)
{
int ret;
struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
- ret = hns_mac_vm_config_bc_en(mac_cb, 0, ENABLE);
+ ret = hns_mac_vm_config_bc_en(mac_cb, 0, true);
if (ret)
return ret;
@@ -309,7 +316,7 @@ void hns_ae_stop(struct hnae_handle *handle)
hns_ae_ring_enable_all(handle, 0);
- (void)hns_mac_vm_config_bc_en(mac_cb, 0, DISABLE);
+ (void)hns_mac_vm_config_bc_en(mac_cb, 0, false);
}
static void hns_ae_reset(struct hnae_handle *handle)
@@ -334,12 +341,30 @@ void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask)
else
flag = RCB_INT_FLAG_RX;
- hns_rcb_int_clr_hw(ring->q, flag);
hns_rcb_int_ctrl_hw(ring->q, flag, mask);
}
+static void hns_aev2_toggle_ring_irq(struct hnae_ring *ring, u32 mask)
+{
+ u32 flag;
+
+ if (is_tx_ring(ring))
+ flag = RCB_INT_FLAG_TX;
+ else
+ flag = RCB_INT_FLAG_RX;
+
+ hns_rcbv2_int_ctrl_hw(ring->q, flag, mask);
+}
+
static void hns_ae_toggle_queue_status(struct hnae_queue *queue, u32 val)
{
+ struct dsaf_device *dsaf_dev = hns_ae_get_dsaf_dev(queue->dev);
+
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver))
+ hns_rcb_int_clr_hw(queue, RCB_INT_FLAG_TX | RCB_INT_FLAG_RX);
+ else
+ hns_rcbv2_int_clr_hw(queue, RCB_INT_FLAG_TX | RCB_INT_FLAG_RX);
+
hns_rcb_start(queue, val);
}
@@ -730,6 +755,53 @@ int hns_ae_get_regs_len(struct hnae_handle *handle)
return total_num;
}
+static u32 hns_ae_get_rss_key_size(struct hnae_handle *handle)
+{
+ return HNS_PPEV2_RSS_KEY_SIZE;
+}
+
+static u32 hns_ae_get_rss_indir_size(struct hnae_handle *handle)
+{
+ return HNS_PPEV2_RSS_IND_TBL_SIZE;
+}
+
+static int hns_ae_get_rss(struct hnae_handle *handle, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+ /* currently we support only one type of hash function i.e. Toep hash */
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ /* get the RSS Key required by the user */
+ if (key)
+ memcpy(key, ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
+
+ /* update the current hash->queue mappings from the shadow RSS table */
+ memcpy(indir, ppe_cb->rss_indir_table, HNS_PPEV2_RSS_IND_TBL_SIZE);
+
+ return 0;
+}
+
+static int hns_ae_set_rss(struct hnae_handle *handle, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+{
+ struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+ /* set the RSS Hash Key if specififed by the user */
+ if (key)
+ hns_ppe_set_rss_key(ppe_cb, (int *)key);
+
+ /* update the shadow RSS table with user specified qids */
+ memcpy(ppe_cb->rss_indir_table, indir, HNS_PPEV2_RSS_IND_TBL_SIZE);
+
+ /* now update the hardware */
+ hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+
+ return 0;
+}
+
static struct hnae_ae_ops hns_dsaf_ops = {
.get_handle = hns_ae_get_handle,
.put_handle = hns_ae_put_handle,
@@ -758,19 +830,34 @@ static struct hnae_ae_ops hns_dsaf_ops = {
.set_mc_addr = hns_ae_set_multicast_one,
.set_mtu = hns_ae_set_mtu,
.update_stats = hns_ae_update_stats,
+ .set_tso_stats = hns_ae_set_tso_stats,
.get_stats = hns_ae_get_stats,
.get_strings = hns_ae_get_strings,
.get_sset_count = hns_ae_get_sset_count,
.update_led_status = hns_ae_update_led_status,
.set_led_id = hns_ae_cpld_set_led_id,
.get_regs = hns_ae_get_regs,
- .get_regs_len = hns_ae_get_regs_len
+ .get_regs_len = hns_ae_get_regs_len,
+ .get_rss_key_size = hns_ae_get_rss_key_size,
+ .get_rss_indir_size = hns_ae_get_rss_indir_size,
+ .get_rss = hns_ae_get_rss,
+ .set_rss = hns_ae_set_rss
};
int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev)
{
struct hnae_ae_dev *ae_dev = &dsaf_dev->ae_dev;
+ switch (dsaf_dev->dsaf_ver) {
+ case AE_VERSION_1:
+ hns_dsaf_ops.toggle_ring_irq = hns_ae_toggle_ring_irq;
+ break;
+ case AE_VERSION_2:
+ hns_dsaf_ops.toggle_ring_irq = hns_aev2_toggle_ring_irq;
+ break;
+ default:
+ break;
+ }
ae_dev->ops = &hns_dsaf_ops;
ae_dev->dev = dsaf_dev->dev;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
index 026b38676cba..5ef0e96e918a 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
@@ -283,7 +283,7 @@ int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb,
}
int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
- u32 port_num, char *addr, u8 en)
+ u32 port_num, char *addr, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
@@ -295,7 +295,7 @@ int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
mac_entry.in_port_num = mac_cb->mac_id;
mac_entry.port_num = port_num;
- if (en == DISABLE)
+ if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
@@ -368,7 +368,7 @@ static void hns_mac_param_get(struct mac_params *param,
*retuen 0 - success , negative --fail
*/
static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
- u32 port_num, u16 vlan_id, u8 en)
+ u32 port_num, u16 vlan_id, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
@@ -386,7 +386,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
mac_entry.in_port_num = mac_cb->mac_id;
mac_entry.port_num = port_num;
- if (en == DISABLE)
+ if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
@@ -403,7 +403,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
*@en:enable
*retuen 0 - success , negative --fail
*/
-int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en)
+int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
@@ -427,7 +427,7 @@ int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en)
return ret;
mac_entry.port_num = port_num;
- if (en == DISABLE)
+ if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
@@ -648,7 +648,7 @@ static int hns_mac_init_ex(struct hns_mac_cb *mac_cb)
hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex);
- ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, ENABLE);
+ ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, true);
if (ret)
goto free_mac_drv;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
index 7da95a7581f9..0b052191d751 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
@@ -425,8 +425,8 @@ void mac_adjust_link(struct net_device *net_dev);
void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status);
int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, u32 vmid, char *addr);
int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
- u32 port_num, char *addr, u8 en);
-int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, u8 en);
+ u32 port_num, char *addr, bool enable);
+int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, bool enable);
void hns_mac_start(struct hns_mac_cb *mac_cb);
void hns_mac_stop(struct hns_mac_cb *mac_cb);
int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
index b674414a4d72..1c33bd06bd5c 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
@@ -38,10 +38,10 @@ int hns_dsaf_get_cfg(struct dsaf_device *dsaf_dev)
const char *name, *mode_str;
struct device_node *np = dsaf_dev->dev->of_node;
- if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v2"))
- dsaf_dev->dsaf_ver = AE_VERSION_2;
- else
+ if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v1"))
dsaf_dev->dsaf_ver = AE_VERSION_1;
+ else
+ dsaf_dev->dsaf_ver = AE_VERSION_2;
ret = of_property_read_string(np, "dsa_name", &name);
if (ret) {
@@ -274,6 +274,8 @@ static void hns_dsaf_stp_port_type_cfg(struct dsaf_device *dsaf_dev,
}
}
+#define HNS_DSAF_SBM_NUM(dev) \
+ (AE_IS_VER1((dev)->dsaf_ver) ? DSAF_SBM_NUM : DSAFV2_SBM_NUM)
/**
* hns_dsaf_sbm_cfg - config sbm
* @dsaf_id: dsa fabric id
@@ -283,7 +285,7 @@ static void hns_dsaf_sbm_cfg(struct dsaf_device *dsaf_dev)
u32 o_sbm_cfg;
u32 i;
- for (i = 0; i < DSAF_SBM_NUM; i++) {
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
o_sbm_cfg = dsaf_read_dev(dsaf_dev,
DSAF_SBM_CFG_REG_0_REG + 0x80 * i);
dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_EN_S, 1);
@@ -304,13 +306,19 @@ static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev)
u32 reg;
u32 read_cnt;
- for (i = 0; i < DSAF_SBM_NUM; i++) {
+ /* validate configure by setting SBM_CFG_MIB_EN bit from 0 to 1. */
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
+ reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
+ dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 0);
+ }
+
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 1);
}
/* waitint for all sbm enable finished */
- for (i = 0; i < DSAF_SBM_NUM; i++) {
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
read_cnt = 0;
reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
do {
@@ -338,83 +346,156 @@ static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev)
*/
static void hns_dsaf_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev)
{
- u32 o_sbm_bp_cfg0;
- u32 o_sbm_bp_cfg1;
- u32 o_sbm_bp_cfg2;
- u32 o_sbm_bp_cfg3;
+ u32 o_sbm_bp_cfg;
u32 reg;
u32 i;
/* XGE */
for (i = 0; i < DSAF_XGE_NUM; i++) {
reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg0 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M,
DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S, 512);
- dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M,
DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0);
- dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M,
DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg0);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg1 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M,
DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0);
- dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M,
DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg1);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M,
DSAF_SBM_CFG2_SET_BUF_NUM_S, 104);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
DSAF_SBM_CFG2_RESET_BUF_NUM_S, 128);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg3,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110);
- dsaf_set_field(o_sbm_bp_cfg3,
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
/* for no enable pfc mode */
reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg3,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 128);
- dsaf_set_field(o_sbm_bp_cfg3,
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 192);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
}
/* PPE */
for (i = 0; i < DSAF_COMM_CHN; i++) {
reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M,
DSAF_SBM_CFG2_SET_BUF_NUM_S, 10);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
DSAF_SBM_CFG2_RESET_BUF_NUM_S, 12);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
}
/* RoCEE */
for (i = 0; i < DSAF_COMM_CHN; i++) {
reg = DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M,
DSAF_SBM_CFG2_SET_BUF_NUM_S, 2);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
DSAF_SBM_CFG2_RESET_BUF_NUM_S, 4);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+ }
+}
+
+static void hns_dsafv2_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev)
+{
+ u32 o_sbm_bp_cfg;
+ u32 reg;
+ u32 i;
+
+ /* XGE */
+ for (i = 0; i < DSAFV2_SBM_XGE_CHN; i++) {
+ reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_S, 256);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 104);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 128);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ /* for no enable pfc mode */
+ reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S, 128);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S, 192);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+ }
+
+ /* PPE */
+ reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 10);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 12);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+ /* RoCEE */
+ for (i = 0; i < DASFV2_ROCEE_CRD_NUM; i++) {
+ reg = DSAFV2_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 2);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 4);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
}
}
@@ -985,11 +1066,38 @@ static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev)
else
tc_cfg = HNS_DSAF_I8TC_CFG;
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver)) {
+ for (i = 0; i < DSAF_INODE_NUM; i++) {
+ reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i;
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAF_INODE_IN_PORT_NUM_M,
+ DSAF_INODE_IN_PORT_NUM_S,
+ i % DSAF_XGE_NUM);
+ }
+ } else {
+ for (i = 0; i < DSAF_PORT_TYPE_NUM; i++) {
+ reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i;
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAF_INODE_IN_PORT_NUM_M,
+ DSAF_INODE_IN_PORT_NUM_S, 0);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT1_NUM_M,
+ DSAFV2_INODE_IN_PORT1_NUM_S, 1);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT2_NUM_M,
+ DSAFV2_INODE_IN_PORT2_NUM_S, 2);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT3_NUM_M,
+ DSAFV2_INODE_IN_PORT3_NUM_S, 3);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT4_NUM_M,
+ DSAFV2_INODE_IN_PORT4_NUM_S, 4);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT5_NUM_M,
+ DSAFV2_INODE_IN_PORT5_NUM_S, 5);
+ }
+ }
for (i = 0; i < DSAF_INODE_NUM; i++) {
- reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i;
- dsaf_set_dev_field(dsaf_dev, reg, DSAF_INODE_IN_PORT_NUM_M,
- DSAF_INODE_IN_PORT_NUM_S, i % DSAF_XGE_NUM);
-
reg = DSAF_INODE_PRI_TC_CFG_0_REG + 0x80 * i;
dsaf_write_dev(dsaf_dev, reg, tc_cfg);
}
@@ -1002,10 +1110,17 @@ static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev)
static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev)
{
u32 flag;
+ u32 finish_msk;
u32 cnt = 0;
int ret;
- hns_dsaf_sbm_bp_wl_cfg(dsaf_dev);
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver)) {
+ hns_dsaf_sbm_bp_wl_cfg(dsaf_dev);
+ finish_msk = DSAF_SRAM_INIT_OVER_M;
+ } else {
+ hns_dsafv2_sbm_bp_wl_cfg(dsaf_dev);
+ finish_msk = DSAFV2_SRAM_INIT_OVER_M;
+ }
/* enable sbm chanel, disable sbm chanel shcut function*/
hns_dsaf_sbm_cfg(dsaf_dev);
@@ -1024,11 +1139,13 @@ static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev)
do {
usleep_range(200, 210);/*udelay(200);*/
- flag = dsaf_read_dev(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG);
+ flag = dsaf_get_dev_field(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG,
+ finish_msk, DSAF_SRAM_INIT_OVER_S);
cnt++;
- } while (flag != DSAF_SRAM_INIT_FINISH_FLAG && cnt < DSAF_CFG_READ_CNT);
+ } while (flag != (finish_msk >> DSAF_SRAM_INIT_OVER_S) &&
+ cnt < DSAF_CFG_READ_CNT);
- if (flag != DSAF_SRAM_INIT_FINISH_FLAG) {
+ if (flag != (finish_msk >> DSAF_SRAM_INIT_OVER_S)) {
dev_err(dsaf_dev->dev,
"hns_dsaf_sbm_init fail %s, flag=%d, cnt=%d\n",
dsaf_dev->ae_dev.name, flag, cnt);
@@ -2011,7 +2128,7 @@ void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data)
DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4);
/* dsaf inode registers */
- for (i = 0; i < DSAF_SBM_NUM / DSAF_COMM_CHN; i++) {
+ for (i = 0; i < HNS_DSAF_SBM_NUM(ddev) / DSAF_COMM_CHN; i++) {
j = i * DSAF_COMM_CHN + port;
p[232 + i] = dsaf_read_dev(ddev,
DSAF_SBM_CFG_REG_0_REG + j * 0x80);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
index b2b93484995c..31c312f9826e 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
@@ -19,24 +19,20 @@ struct hns_mac_cb;
#define DSAF_DRV_NAME "hns_dsaf"
#define DSAF_MOD_VERSION "v1.0"
-#define ENABLE (0x1)
-#define DISABLE (0x0)
+#define HNS_DSAF_DEBUG_NW_REG_OFFSET 0x100000
-#define HNS_DSAF_DEBUG_NW_REG_OFFSET (0x100000)
+#define DSAF_BASE_INNER_PORT_NUM 127/* mac tbl qid*/
-#define DSAF_BASE_INNER_PORT_NUM (127) /* mac tbl qid*/
+#define DSAF_MAX_CHIP_NUM 2 /*max 2 chips */
-#define DSAF_MAX_CHIP_NUM (2) /*max 2 chips */
+#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE 22
-#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE (22)
+#define HNS_DSAF_MAX_DESC_CNT 1024
+#define HNS_DSAF_MIN_DESC_CNT 16
-#define HNS_DSAF_MAX_DESC_CNT (1024)
-#define HNS_DSAF_MIN_DESC_CNT (16)
+#define DSAF_INVALID_ENTRY_IDX 0xffff
-#define DSAF_INVALID_ENTRY_IDX (0xffff)
-
-#define DSAF_CFG_READ_CNT (30)
-#define DSAF_SRAM_INIT_FINISH_FLAG (0xff)
+#define DSAF_CFG_READ_CNT 30
#define MAC_NUM_OCTETS_PER_ADDR 6
@@ -274,10 +270,6 @@ struct dsaf_device {
struct device *dev;
struct hnae_ae_dev ae_dev;
- void *priv;
-
- int virq[DSAF_IRQ_NUM];
-
u8 __iomem *sc_base;
u8 __iomem *sds_base;
u8 __iomem *ppe_base;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c
index 523e9b83d304..607c3be42241 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c
@@ -149,7 +149,11 @@ void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val)
if (port < DSAF_SERVICE_NW_NUM) {
reg_val_1 = 0x1 << port;
- reg_val_2 = 0x1041041 << port;
+ /* there is difference between V1 and V2 in register.*/
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver))
+ reg_val_2 = 0x1041041 << port;
+ else
+ reg_val_2 = 0x2082082 << port;
if (val == 0) {
dsaf_write_reg(dsaf_dev->sc_base,
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
index 67f33f185a44..f302ef9073c6 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
@@ -19,6 +19,48 @@
#include "hns_dsaf_ppe.h"
+void hns_ppe_set_tso_enable(struct hns_ppe_cb *ppe_cb, u32 value)
+{
+ dsaf_set_dev_bit(ppe_cb, PPEV2_CFG_TSO_EN_REG, 0, !!value);
+}
+
+void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM])
+{
+ int key_item = 0;
+
+ for (key_item = 0; key_item < HNS_PPEV2_RSS_KEY_NUM; key_item++)
+ dsaf_write_dev(ppe_cb, PPEV2_RSS_KEY_REG + key_item * 0x4,
+ rss_key[key_item]);
+}
+
+void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE])
+{
+ int i;
+ int reg_value;
+
+ for (i = 0; i < (HNS_PPEV2_RSS_IND_TBL_SIZE / 4); i++) {
+ reg_value = dsaf_read_dev(ppe_cb,
+ PPEV2_INDRECTION_TBL_REG + i * 0x4);
+
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N0_M,
+ PPEV2_CFG_RSS_TBL_4N0_S,
+ rss_tab[i * 4 + 0] & 0x1F);
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N1_M,
+ PPEV2_CFG_RSS_TBL_4N1_S,
+ rss_tab[i * 4 + 1] & 0x1F);
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N2_M,
+ PPEV2_CFG_RSS_TBL_4N2_S,
+ rss_tab[i * 4 + 2] & 0x1F);
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N3_M,
+ PPEV2_CFG_RSS_TBL_4N3_S,
+ rss_tab[i * 4 + 3] & 0x1F);
+ dsaf_write_dev(
+ ppe_cb, PPEV2_INDRECTION_TBL_REG + i * 0x4, reg_value);
+ }
+}
+
static void __iomem *hns_ppe_common_get_ioaddr(
struct ppe_common_cb *ppe_common)
{
@@ -134,6 +176,11 @@ static void hns_ppe_cnt_clr_ce(struct hns_ppe_cb *ppe_cb)
PPE_CNT_CLR_CE_B, 1);
}
+static void hns_ppe_set_vlan_strip(struct hns_ppe_cb *ppe_cb, int en)
+{
+ dsaf_write_dev(ppe_cb, PPEV2_VLAN_STRIP_EN_REG, en);
+}
+
/**
* hns_ppe_checksum_hw - set ppe checksum caculate
* @ppe_device: ppe device
@@ -266,13 +313,17 @@ static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en)
/**
* ppe_init_hw - init ppe
- * @ppe_device: ppe device
+ * @ppe_cb: ppe device
*/
static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb)
{
struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb;
u32 port = ppe_cb->port;
struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev;
+ int i;
+
+ /* get default RSS key */
+ netdev_rss_key_fill(ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
hns_ppe_srst_by_port(dsaf_dev, port, 0);
mdelay(10);
@@ -285,8 +336,21 @@ static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb)
hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE);
else
hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE);
+
hns_ppe_checksum_hw(ppe_cb, 0xffffffff);
hns_ppe_cnt_clr_ce(ppe_cb);
+
+ if (!AE_IS_VER1(dsaf_dev->dsaf_ver)) {
+ hns_ppe_set_vlan_strip(ppe_cb, 0);
+
+ /* set default RSS key in h/w */
+ hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key);
+
+ /* Set default indrection table in h/w */
+ for (i = 0; i < HNS_PPEV2_RSS_IND_TBL_SIZE; i++)
+ ppe_cb->rss_indir_table[i] = i;
+ hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+ }
}
/**
@@ -341,13 +405,13 @@ void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index)
if (ret)
return;
+ for (i = 0; i < ppe_common->ppe_num; i++)
+ hns_ppe_init_hw(&ppe_common->ppe_cb[i]);
+
ret = hns_rcb_common_init_hw(dsaf_dev->rcb_common[ppe_common_index]);
if (ret)
return;
- for (i = 0; i < ppe_common->ppe_num; i++)
- hns_ppe_init_hw(&ppe_common->ppe_cb[i]);
-
hns_rcb_common_init_commit_hw(dsaf_dev->rcb_common[ppe_common_index]);
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
index 4894f9a0d39f..0f5cb6962acf 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
@@ -25,15 +25,24 @@
#define ETH_PPE_DUMP_NUM 576
#define ETH_PPE_STATIC_NUM 12
+
+#define HNS_PPEV2_RSS_IND_TBL_SIZE 256
+#define HNS_PPEV2_RSS_KEY_SIZE 40 /* in bytes or 320 bits */
+#define HNS_PPEV2_RSS_KEY_NUM (HNS_PPEV2_RSS_KEY_SIZE / sizeof(u32))
+
enum ppe_qid_mode {
- PPE_QID_MODE0 = 0, /* fixed queue id mode */
- PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */
- PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */
- PPE_QID_MODE3, /* switch:4TC/8TAG non switch:2Port/64VM */
- PPE_QID_MODE4, /* switch:8VM/16TAG non switch:2Port/16VM/4TC */
- PPE_QID_MODE5, /* non switch:6Port/16TAG */
- PPE_QID_MODE6, /* non switch:6Port/2VM/8TC */
- PPE_QID_MODE7, /* non switch:2Port/8VM/8TC */
+ PPE_QID_MODE0 = 0, /* fixed queue id mode */
+ PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */
+ PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */
+ PPE_QID_MODE3, /* switch:4TC/8RSS non switch:2Port/64VM */
+ PPE_QID_MODE4, /* switch:8VM/16RSS non switch:2Port/16VM/4TC */
+ PPE_QID_MODE5, /* switch:16VM/8TC non switch:6Port/16RSS */
+ PPE_QID_MODE6, /* switch:32VM/4RSS non switch:6Port/2VM/8TC */
+ PPE_QID_MODE7, /* switch:32RSS non switch:2Port/8VM/8TC */
+ PPE_QID_MODE8, /* switch:6VM/4TC/4RSS non switch:2Port/16VM/4RSS */
+ PPE_QID_MODE9, /* non switch:2Port/32VM/2RSS */
+ PPE_QID_MODE10, /* non switch:2Port/32RSS */
+ PPE_QID_MODE11, /* non switch:2Port/4TC/16RSS */
};
enum ppe_port_mode {
@@ -72,6 +81,8 @@ struct hns_ppe_cb {
u8 port; /* port id in dsaf */
void __iomem *io_base;
int virq;
+ u32 rss_indir_table[HNS_PPEV2_RSS_IND_TBL_SIZE]; /*shadow indir tab */
+ u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]; /* rss hash key */
};
struct ppe_common_cb {
@@ -102,4 +113,9 @@ void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data);
void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data);
void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data);
+void hns_ppe_set_tso_enable(struct hns_ppe_cb *ppe_cb, u32 value);
+void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]);
+void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE]);
#endif /* _HNS_DSAF_PPE_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
index 4db32c62f062..d2263c72bd8a 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
@@ -136,19 +136,37 @@ void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask)
void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag)
{
- u32 clr = 1;
-
if (flag & RCB_INT_FLAG_TX) {
- dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, clr);
- dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, clr);
+ dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, 1);
+ dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, 1);
}
if (flag & RCB_INT_FLAG_RX) {
- dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, clr);
- dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, clr);
+ dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, 1);
+ dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, 1);
}
}
+void hns_rcbv2_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask)
+{
+ u32 int_mask_en = !!mask;
+
+ if (flag & RCB_INT_FLAG_TX)
+ dsaf_write_dev(q, RCB_RING_INTMSK_TXWL_REG, int_mask_en);
+
+ if (flag & RCB_INT_FLAG_RX)
+ dsaf_write_dev(q, RCB_RING_INTMSK_RXWL_REG, int_mask_en);
+}
+
+void hns_rcbv2_int_clr_hw(struct hnae_queue *q, u32 flag)
+{
+ if (flag & RCB_INT_FLAG_TX)
+ dsaf_write_dev(q, RCBV2_TX_RING_INT_STS_REG, 1);
+
+ if (flag & RCB_INT_FLAG_RX)
+ dsaf_write_dev(q, RCBV2_RX_RING_INT_STS_REG, 1);
+}
+
/**
*hns_rcb_ring_enable_hw - enable ring
*@ring: rcb ring
@@ -193,6 +211,7 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type)
(u32)dma);
dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG,
(u32)((dma >> 31) >> 1));
+
dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG,
bd_size_type);
dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG,
@@ -204,6 +223,7 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type)
(u32)dma);
dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG,
(u32)((dma >> 31) >> 1));
+
dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG,
bd_size_type);
dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG,
@@ -232,9 +252,6 @@ void hns_rcb_init_hw(struct ring_pair_cb *ring)
static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common,
u32 port_idx, u32 desc_cnt)
{
- if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM)
- port_idx = 0;
-
dsaf_write_dev(rcb_common, RCB_CFG_BD_NUM_REG + port_idx * 4,
desc_cnt);
}
@@ -249,8 +266,6 @@ static int hns_rcb_set_port_coalesced_frames(struct rcb_common_cb *rcb_common,
u32 port_idx,
u32 coalesced_frames)
{
- if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM)
- port_idx = 0;
if (coalesced_frames >= rcb_common->desc_num ||
coalesced_frames > HNS_RCB_MAX_COALESCED_FRAMES)
return -EINVAL;
@@ -354,6 +369,9 @@ int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common)
dsaf_write_dev(rcb_common, RCB_COM_CFG_ENDIAN_REG,
HNS_RCB_COMMON_ENDIAN);
+ dsaf_write_dev(rcb_common, RCB_COM_CFG_FNA_REG, 0x0);
+ dsaf_write_dev(rcb_common, RCB_COM_CFG_FA_REG, 0x1);
+
return 0;
}
@@ -387,19 +405,23 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type)
struct rcb_common_cb *rcb_common;
struct ring_pair_cb *ring_pair_cb;
u32 buf_size;
- u16 desc_num;
- int irq_idx;
+ u16 desc_num, mdnum_ppkt;
+ bool irq_idx, is_ver1;
ring_pair_cb = container_of(q, struct ring_pair_cb, q);
+ is_ver1 = AE_IS_VER1(ring_pair_cb->rcb_common->dsaf_dev->dsaf_ver);
if (ring_type == RX_RING) {
ring = &q->rx_ring;
ring->io_base = ring_pair_cb->q.io_base;
irq_idx = HNS_RCB_IRQ_IDX_RX;
+ mdnum_ppkt = HNS_RCB_RING_MAX_BD_PER_PKT;
} else {
ring = &q->tx_ring;
ring->io_base = (u8 __iomem *)ring_pair_cb->q.io_base +
HNS_RCB_TX_REG_OFFSET;
irq_idx = HNS_RCB_IRQ_IDX_TX;
+ mdnum_ppkt = is_ver1 ? HNS_RCB_RING_MAX_TXBD_PER_PKT :
+ HNS_RCBV2_RING_MAX_TXBD_PER_PKT;
}
rcb_common = ring_pair_cb->rcb_common;
@@ -414,7 +436,7 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type)
ring->buf_size = buf_size;
ring->desc_num = desc_num;
- ring->max_desc_num_per_pkt = HNS_RCB_RING_MAX_BD_PER_PKT;
+ ring->max_desc_num_per_pkt = mdnum_ppkt;
ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE;
ring->max_pkt_size = HNS_RCB_MAX_PKT_SIZE;
ring->next_to_use = 0;
@@ -445,14 +467,22 @@ static int hns_rcb_get_port(struct rcb_common_cb *rcb_common, int ring_idx)
return port;
}
+#define SERVICE_RING_IRQ_IDX(v1) \
+ ((v1) ? HNS_SERVICE_RING_IRQ_IDX : HNSV2_SERVICE_RING_IRQ_IDX)
+#define DEBUG_RING_IRQ_IDX(v1) \
+ ((v1) ? HNS_DEBUG_RING_IRQ_IDX : HNSV2_DEBUG_RING_IRQ_IDX)
+#define DEBUG_RING_IRQ_OFFSET(v1) \
+ ((v1) ? HNS_DEBUG_RING_IRQ_OFFSET : HNSV2_DEBUG_RING_IRQ_OFFSET)
static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common)
{
int comm_index = rcb_common->comm_index;
+ bool is_ver1 = AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver);
if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX)
- return HNS_SERVICE_RING_IRQ_IDX;
+ return SERVICE_RING_IRQ_IDX(is_ver1);
else
- return HNS_DEBUG_RING_IRQ_IDX + (comm_index - 1) * 2;
+ return DEBUG_RING_IRQ_IDX(is_ver1) +
+ (comm_index - 1) * DEBUG_RING_IRQ_OFFSET(is_ver1);
}
#define RCB_COMM_BASE_TO_RING_BASE(base, ringid)\
@@ -468,6 +498,9 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common)
u32 ring_num = rcb_common->ring_num;
int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common);
struct device_node *np = rcb_common->dsaf_dev->dev->of_node;
+ struct platform_device *pdev =
+ to_platform_device(rcb_common->dsaf_dev->dev);
+ bool is_ver1 = AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver);
for (i = 0; i < ring_num; i++) {
ring_pair_cb = &rcb_common->ring_pair_cb[i];
@@ -477,10 +510,12 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common)
ring_pair_cb->q.io_base =
RCB_COMM_BASE_TO_RING_BASE(rcb_common->io_base, i);
ring_pair_cb->port_id_in_dsa = hns_rcb_get_port(rcb_common, i);
- ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX]
- = irq_of_parse_and_map(np, base_irq_idx + i * 2);
- ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX]
- = irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1);
+ ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] =
+ is_ver1 ? irq_of_parse_and_map(np, base_irq_idx + i * 2) :
+ platform_get_irq(pdev, base_irq_idx + i * 3 + 1);
+ ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] =
+ is_ver1 ? irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1) :
+ platform_get_irq(pdev, base_irq_idx + i * 3);
ring_pair_cb->q.phy_base =
RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i);
hns_rcb_ring_pair_get_cfg(ring_pair_cb);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h
index 3a2afe2dd8bb..29041b18741a 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h
@@ -26,6 +26,8 @@ struct rcb_common_cb;
#define HNS_RCB_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN
#define HNS_RCB_DEBUG_NW_ENGINE_NUM 1
#define HNS_RCB_RING_MAX_BD_PER_PKT 3
+#define HNS_RCB_RING_MAX_TXBD_PER_PKT 3
+#define HNS_RCBV2_RING_MAX_TXBD_PER_PKT 8
#define HNS_RCB_MAX_PKT_SIZE MAC_MAX_MTU
#define HNS_RCB_RING_MAX_PENDING_BD 1024
@@ -106,13 +108,17 @@ void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index);
int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common);
void hns_rcb_start(struct hnae_queue *q, u32 val);
void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common);
-void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common);
void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index,
u16 *max_vfn, u16 *max_q_per_vf);
+void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common);
+
void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val);
void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag);
void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 enable);
+void hns_rcbv2_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask);
+void hns_rcbv2_int_clr_hw(struct hnae_queue *q, u32 flag);
+
void hns_rcb_init_hw(struct ring_pair_cb *ring);
void hns_rcb_reset_ring_hw(struct hnae_queue *q);
void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
index bdbd80423b17..5d1b746e141d 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
@@ -10,21 +10,12 @@
#ifndef _DSAF_REG_H_
#define _DSAF_REG_H_
-#define HNS_GE_FIFO_ERR_INTNUM 8
-#define HNS_XGE_ERR_INTNUM 6
-#define HNS_RCB_COMM_ERR_INTNUM 12
-#define HNS_PPE_TNL_ERR_INTNUM 8
-#define HNS_DSAF_EVENT_INTNUM 21
-#define HNS_DEBUG_RING_INTNUM 4
-#define HNS_SERVICE_RING_INTNUM 256
-
-#define HNS_DEBUG_RING_IRQ_IDX (HNS_GE_FIFO_ERR_INTNUM + HNS_XGE_ERR_INTNUM +\
- HNS_RCB_COMM_ERR_INTNUM + HNS_PPE_TNL_ERR_INTNUM +\
- HNS_DSAF_EVENT_INTNUM)
-#define HNS_SERVICE_RING_IRQ_IDX (HNS_DEBUG_RING_IRQ_IDX +\
- HNS_DEBUG_RING_INTNUM)
-
-#define DSAF_IRQ_NUM 18
+#define HNS_DEBUG_RING_IRQ_IDX 55
+#define HNS_SERVICE_RING_IRQ_IDX 59
+#define HNS_DEBUG_RING_IRQ_OFFSET 2
+#define HNSV2_DEBUG_RING_IRQ_IDX 409
+#define HNSV2_SERVICE_RING_IRQ_IDX 25
+#define HNSV2_DEBUG_RING_IRQ_OFFSET 9
#define DSAF_MAX_PORT_NUM_PER_CHIP 8
#define DSAF_SERVICE_PORT_NUM_PER_DSAF 6
@@ -39,9 +30,15 @@
#define DSAF_GE_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM))
#define DSAF_PORT_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM))
#define DSAF_XGE_NUM DSAF_SERVICE_NW_NUM
+#define DSAF_PORT_TYPE_NUM 3
#define DSAF_NODE_NUM 18
#define DSAF_XOD_BIG_NUM DSAF_NODE_NUM
#define DSAF_SBM_NUM DSAF_NODE_NUM
+#define DSAFV2_SBM_NUM 8
+#define DSAFV2_SBM_XGE_CHN 6
+#define DSAFV2_SBM_PPE_CHN 1
+#define DASFV2_ROCEE_CRD_NUM 8
+
#define DSAF_VOQ_NUM DSAF_NODE_NUM
#define DSAF_INODE_NUM DSAF_NODE_NUM
#define DSAF_XOD_NUM 8
@@ -52,56 +49,56 @@
#define DSAF_TCAM_SUM 512
#define DSAF_LINE_SUM (2048 * 14)
-#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C
-#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190
-#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194
-#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300
-#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304
-#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308
-#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C
-#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310
-#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314
-#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318
-#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C
-#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320
-#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324
-#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350
-#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354
-#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00
-#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04
-#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08
-#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C
-#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10
-#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14
-#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18
-#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C
-#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20
-#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24
-#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48
-#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C
-#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88
-#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C
-#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060
-#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300
-#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300
-#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304
-#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308
-#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C
-#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310
-#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314
-#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318
-#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328
-#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00
-#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04
-#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08
-#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C
-#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10
-#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24
-#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44
+#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C
+#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190
+#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194
+#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300
+#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304
+#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308
+#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C
+#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310
+#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314
+#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318
+#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C
+#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320
+#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354
+#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00
+#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04
+#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08
+#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C
+#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10
+#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14
+#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18
+#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C
+#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20
+#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24
+#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48
+#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C
+#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060
+#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300
+#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300
+#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304
+#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308
+#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C
+#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310
+#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314
+#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328
+#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00
+#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04
+#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08
+#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C
+#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10
+#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44
/*serdes offset**/
#define HNS_MAC_HILINK3_REG DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG
@@ -178,6 +175,7 @@
#define DSAF_SBM_BP_CFG_2_XGE_REG_0_REG 0x200C
#define DSAF_SBM_BP_CFG_2_PPE_REG_0_REG 0x230C
#define DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x260C
+#define DSAFV2_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x238C
#define DSAF_SBM_FREE_CNT_0_0_REG 0x2010
#define DSAF_SBM_FREE_CNT_1_0_REG 0x2014
#define DSAF_SBM_BP_CNT_0_0_REG 0x2018
@@ -319,6 +317,8 @@
#define PPE_CFG_TAG_GEN_REG 0x90
#define PPE_CFG_PARSE_TAG_REG 0x94
#define PPE_CFG_PRO_CHECK_EN_REG 0x98
+#define PPEV2_CFG_TSO_EN_REG 0xA0
+#define PPEV2_VLAN_STRIP_EN_REG 0xAC
#define PPE_INTEN_REG 0x100
#define PPE_RINT_REG 0x104
#define PPE_INTSTS_REG 0x108
@@ -351,6 +351,8 @@
#define PPE_ECO0_REG 0x32C
#define PPE_ECO1_REG 0x330
#define PPE_ECO2_REG 0x334
+#define PPEV2_INDRECTION_TBL_REG 0x800
+#define PPEV2_RSS_KEY_REG 0x900
#define RCB_COM_CFG_ENDIAN_REG 0x0
#define RCB_COM_CFG_SYS_FSH_REG 0xC
@@ -431,8 +433,10 @@
#define RCB_RING_INTMSK_RXWL_REG 0x000A0
#define RCB_RING_INTSTS_RX_RING_REG 0x000A4
+#define RCBV2_RX_RING_INT_STS_REG 0x000A8
#define RCB_RING_INTMSK_TXWL_REG 0x000AC
#define RCB_RING_INTSTS_TX_RING_REG 0x000B0
+#define RCBV2_TX_RING_INT_STS_REG 0x000B4
#define RCB_RING_INTMSK_RX_OVERTIME_REG 0x000B8
#define RCB_RING_INTSTS_RX_OVERTIME_REG 0x000BC
#define RCB_RING_INTMSK_TX_OVERTIME_REG 0x000C4
@@ -678,6 +682,10 @@
#define XGMAC_TRX_CORE_SRST_M 0x2080
+#define DSAF_SRAM_INIT_OVER_M 0xff
+#define DSAFV2_SRAM_INIT_OVER_M 0x3ff
+#define DSAF_SRAM_INIT_OVER_S 0
+
#define DSAF_CFG_EN_S 0
#define DSAF_CFG_TC_MODE_S 1
#define DSAF_CFG_CRC_EN_S 2
@@ -685,6 +693,7 @@
#define DSAF_CFG_MIX_MODE_S 4
#define DSAF_CFG_STP_MODE_S 5
#define DSAF_CFG_LOCA_ADDR_EN_S 6
+#define DSAFV2_CFG_VLAN_TAG_MODE_S 17
#define DSAF_CNT_CLR_CE_S 0
#define DSAF_SNAP_EN_S 1
@@ -707,6 +716,16 @@
#define DSAF_INODE_IN_PORT_NUM_M 7
#define DSAF_INODE_IN_PORT_NUM_S 0
+#define DSAFV2_INODE_IN_PORT1_NUM_M (7ULL << 3)
+#define DSAFV2_INODE_IN_PORT1_NUM_S 3
+#define DSAFV2_INODE_IN_PORT2_NUM_M (7ULL << 6)
+#define DSAFV2_INODE_IN_PORT2_NUM_S 6
+#define DSAFV2_INODE_IN_PORT3_NUM_M (7ULL << 9)
+#define DSAFV2_INODE_IN_PORT3_NUM_S 9
+#define DSAFV2_INODE_IN_PORT4_NUM_M (7ULL << 12)
+#define DSAFV2_INODE_IN_PORT4_NUM_S 12
+#define DSAFV2_INODE_IN_PORT5_NUM_M (7ULL << 15)
+#define DSAFV2_INODE_IN_PORT5_NUM_S 15
#define HNS_DSAF_I4TC_CFG 0x18688688
#define HNS_DSAF_I8TC_CFG 0x18FAC688
@@ -738,6 +757,33 @@
#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 10
#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 10)
+#define DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_S 0
+#define DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_S 9
+#define DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 9)
+#define DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_S 18
+#define DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 18)
+
+#define DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_S 0
+#define DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_S 9
+#define DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 9)
+
+#define DSAFV2_SBM_CFG2_SET_BUF_NUM_S 0
+#define DSAFV2_SBM_CFG2_SET_BUF_NUM_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG2_RESET_BUF_NUM_S 9
+#define DSAFV2_SBM_CFG2_RESET_BUF_NUM_M (((1ULL << 9) - 1) << 9)
+
+#define DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S 0
+#define DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 9
+#define DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 9)
+
+#define DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S 0
+#define DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S 9
+#define DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 9)
+
#define DSAF_TBL_TCAM_ADDR_S 0
#define DSAF_TBL_TCAM_ADDR_M ((1ULL << 9) - 1)
@@ -797,6 +843,18 @@
#define PPE_CFG_QID_MODE_CF_QID_MODE_S 8
#define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S)
+#define PPEV2_CFG_RSS_TBL_4N0_S 0
+#define PPEV2_CFG_RSS_TBL_4N0_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N0_S)
+
+#define PPEV2_CFG_RSS_TBL_4N1_S 8
+#define PPEV2_CFG_RSS_TBL_4N1_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N1_S)
+
+#define PPEV2_CFG_RSS_TBL_4N2_S 16
+#define PPEV2_CFG_RSS_TBL_4N2_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N2_S)
+
+#define PPEV2_CFG_RSS_TBL_4N3_S 24
+#define PPEV2_CFG_RSS_TBL_4N3_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N3_S)
+
#define PPE_CNT_CLR_CE_B 0
#define PPE_CNT_CLR_SNAP_EN_B 1
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index 08cef0dfb5db..0e30846a24f8 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -33,10 +33,105 @@
#define RCB_IRQ_NOT_INITED 0
#define RCB_IRQ_INITED 1
+#define HNS_BUFFER_SIZE_2048 2048
+
+#define BD_MAX_SEND_SIZE 8191
+#define SKB_TMP_LEN(SKB) \
+ (((SKB)->transport_header - (SKB)->mac_header) + tcp_hdrlen(SKB))
+
+static void fill_v2_desc(struct hnae_ring *ring, void *priv,
+ int size, dma_addr_t dma, int frag_end,
+ int buf_num, enum hns_desc_type type, int mtu)
+{
+ struct hnae_desc *desc = &ring->desc[ring->next_to_use];
+ struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
+ struct iphdr *iphdr;
+ struct ipv6hdr *ipv6hdr;
+ struct sk_buff *skb;
+ int skb_tmp_len;
+ __be16 protocol;
+ u8 bn_pid = 0;
+ u8 rrcfv = 0;
+ u8 ip_offset = 0;
+ u8 tvsvsn = 0;
+ u16 mss = 0;
+ u8 l4_len = 0;
+ u16 paylen = 0;
+
+ desc_cb->priv = priv;
+ desc_cb->length = size;
+ desc_cb->dma = dma;
+ desc_cb->type = type;
+
+ desc->addr = cpu_to_le64(dma);
+ desc->tx.send_size = cpu_to_le16((u16)size);
+
+ /*config bd buffer end */
+ hnae_set_bit(rrcfv, HNSV2_TXD_VLD_B, 1);
+ hnae_set_field(bn_pid, HNSV2_TXD_BUFNUM_M, 0, buf_num - 1);
+
+ if (type == DESC_TYPE_SKB) {
+ skb = (struct sk_buff *)priv;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ skb_reset_mac_len(skb);
+ protocol = skb->protocol;
+ ip_offset = ETH_HLEN;
+
+ if (protocol == htons(ETH_P_8021Q)) {
+ ip_offset += VLAN_HLEN;
+ protocol = vlan_get_protocol(skb);
+ skb->protocol = protocol;
+ }
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ iphdr = ip_hdr(skb);
+ hnae_set_bit(rrcfv, HNSV2_TXD_L3CS_B, 1);
+ hnae_set_bit(rrcfv, HNSV2_TXD_L4CS_B, 1);
+
+ /* check for tcp/udp header */
+ if (iphdr->protocol == IPPROTO_TCP) {
+ hnae_set_bit(tvsvsn,
+ HNSV2_TXD_TSE_B, 1);
+ skb_tmp_len = SKB_TMP_LEN(skb);
+ l4_len = tcp_hdrlen(skb);
+ mss = mtu - skb_tmp_len - ETH_FCS_LEN;
+ paylen = skb->len - skb_tmp_len;
+ }
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ hnae_set_bit(tvsvsn, HNSV2_TXD_IPV6_B, 1);
+ ipv6hdr = ipv6_hdr(skb);
+ hnae_set_bit(rrcfv, HNSV2_TXD_L4CS_B, 1);
+
+ /* check for tcp/udp header */
+ if (ipv6hdr->nexthdr == IPPROTO_TCP) {
+ hnae_set_bit(tvsvsn,
+ HNSV2_TXD_TSE_B, 1);
+ skb_tmp_len = SKB_TMP_LEN(skb);
+ l4_len = tcp_hdrlen(skb);
+ mss = mtu - skb_tmp_len - ETH_FCS_LEN;
+ paylen = skb->len - skb_tmp_len;
+ }
+ }
+ desc->tx.ip_offset = ip_offset;
+ desc->tx.tse_vlan_snap_v6_sctp_nth = tvsvsn;
+ desc->tx.mss = cpu_to_le16(mss);
+ desc->tx.l4_len = l4_len;
+ desc->tx.paylen = cpu_to_le16(paylen);
+ }
+ }
+
+ hnae_set_bit(rrcfv, HNSV2_TXD_FE_B, frag_end);
+
+ desc->tx.bn_pid = bn_pid;
+ desc->tx.ra_ri_cs_fe_vld = rrcfv;
+
+ ring_ptr_move_fw(ring, next_to_use);
+}
static void fill_desc(struct hnae_ring *ring, void *priv,
int size, dma_addr_t dma, int frag_end,
- int buf_num, enum hns_desc_type type)
+ int buf_num, enum hns_desc_type type, int mtu)
{
struct hnae_desc *desc = &ring->desc[ring->next_to_use];
struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
@@ -100,47 +195,129 @@ static void unfill_desc(struct hnae_ring *ring)
ring_ptr_move_bw(ring, next_to_use);
}
-int hns_nic_net_xmit_hw(struct net_device *ndev,
- struct sk_buff *skb,
- struct hns_nic_ring_data *ring_data)
+static int hns_nic_maybe_stop_tx(
+ struct sk_buff **out_skb, int *bnum, struct hnae_ring *ring)
{
- struct hns_nic_priv *priv = netdev_priv(ndev);
- struct device *dev = priv->dev;
- struct hnae_ring *ring = ring_data->ring;
- struct netdev_queue *dev_queue;
- struct skb_frag_struct *frag;
+ struct sk_buff *skb = *out_skb;
+ struct sk_buff *new_skb = NULL;
int buf_num;
- dma_addr_t dma;
- int size, next_to_use;
- int i, j;
- struct sk_buff *new_skb;
-
- assert(ring->max_desc_num_per_pkt <= ring->desc_num);
/* no. of segments (plus a header) */
buf_num = skb_shinfo(skb)->nr_frags + 1;
if (unlikely(buf_num > ring->max_desc_num_per_pkt)) {
- if (ring_space(ring) < 1) {
- ring->stats.tx_busy++;
- goto out_net_tx_busy;
- }
+ if (ring_space(ring) < 1)
+ return -EBUSY;
new_skb = skb_copy(skb, GFP_ATOMIC);
- if (!new_skb) {
- ring->stats.sw_err_cnt++;
- netdev_err(ndev, "no memory to xmit!\n");
- goto out_err_tx_ok;
- }
+ if (!new_skb)
+ return -ENOMEM;
dev_kfree_skb_any(skb);
- skb = new_skb;
+ *out_skb = new_skb;
buf_num = 1;
- assert(skb_shinfo(skb)->nr_frags == 1);
} else if (buf_num > ring_space(ring)) {
+ return -EBUSY;
+ }
+
+ *bnum = buf_num;
+ return 0;
+}
+
+static int hns_nic_maybe_stop_tso(
+ struct sk_buff **out_skb, int *bnum, struct hnae_ring *ring)
+{
+ int i;
+ int size;
+ int buf_num;
+ int frag_num;
+ struct sk_buff *skb = *out_skb;
+ struct sk_buff *new_skb = NULL;
+ struct skb_frag_struct *frag;
+
+ size = skb_headlen(skb);
+ buf_num = (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+
+ frag_num = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < frag_num; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ size = skb_frag_size(frag);
+ buf_num += (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+ }
+
+ if (unlikely(buf_num > ring->max_desc_num_per_pkt)) {
+ buf_num = (skb->len + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+ if (ring_space(ring) < buf_num)
+ return -EBUSY;
+ /* manual split the send packet */
+ new_skb = skb_copy(skb, GFP_ATOMIC);
+ if (!new_skb)
+ return -ENOMEM;
+ dev_kfree_skb_any(skb);
+ *out_skb = new_skb;
+
+ } else if (ring_space(ring) < buf_num) {
+ return -EBUSY;
+ }
+
+ *bnum = buf_num;
+ return 0;
+}
+
+static void fill_tso_desc(struct hnae_ring *ring, void *priv,
+ int size, dma_addr_t dma, int frag_end,
+ int buf_num, enum hns_desc_type type, int mtu)
+{
+ int frag_buf_num;
+ int sizeoflast;
+ int k;
+
+ frag_buf_num = (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+ sizeoflast = size % BD_MAX_SEND_SIZE;
+ sizeoflast = sizeoflast ? sizeoflast : BD_MAX_SEND_SIZE;
+
+ /* when the frag size is bigger than hardware, split this frag */
+ for (k = 0; k < frag_buf_num; k++)
+ fill_v2_desc(ring, priv,
+ (k == frag_buf_num - 1) ?
+ sizeoflast : BD_MAX_SEND_SIZE,
+ dma + BD_MAX_SEND_SIZE * k,
+ frag_end && (k == frag_buf_num - 1) ? 1 : 0,
+ buf_num,
+ (type == DESC_TYPE_SKB && !k) ?
+ DESC_TYPE_SKB : DESC_TYPE_PAGE,
+ mtu);
+}
+
+int hns_nic_net_xmit_hw(struct net_device *ndev,
+ struct sk_buff *skb,
+ struct hns_nic_ring_data *ring_data)
+{
+ struct hns_nic_priv *priv = netdev_priv(ndev);
+ struct device *dev = priv->dev;
+ struct hnae_ring *ring = ring_data->ring;
+ struct netdev_queue *dev_queue;
+ struct skb_frag_struct *frag;
+ int buf_num;
+ int seg_num;
+ dma_addr_t dma;
+ int size, next_to_use;
+ int i;
+
+ switch (priv->ops.maybe_stop_tx(&skb, &buf_num, ring)) {
+ case -EBUSY:
ring->stats.tx_busy++;
goto out_net_tx_busy;
+ case -ENOMEM:
+ ring->stats.sw_err_cnt++;
+ netdev_err(ndev, "no memory to xmit!\n");
+ goto out_err_tx_ok;
+ default:
+ break;
}
+
+ /* no. of segments (plus a header) */
+ seg_num = skb_shinfo(skb)->nr_frags + 1;
next_to_use = ring->next_to_use;
/* fill the first part */
@@ -151,11 +328,11 @@ int hns_nic_net_xmit_hw(struct net_device *ndev,
ring->stats.sw_err_cnt++;
goto out_err_tx_ok;
}
- fill_desc(ring, skb, size, dma, buf_num == 1 ? 1 : 0, buf_num,
- DESC_TYPE_SKB);
+ priv->ops.fill_desc(ring, skb, size, dma, seg_num == 1 ? 1 : 0,
+ buf_num, DESC_TYPE_SKB, ndev->mtu);
/* fill the fragments */
- for (i = 1; i < buf_num; i++) {
+ for (i = 1; i < seg_num; i++) {
frag = &skb_shinfo(skb)->frags[i - 1];
size = skb_frag_size(frag);
dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE);
@@ -164,8 +341,9 @@ int hns_nic_net_xmit_hw(struct net_device *ndev,
ring->stats.sw_err_cnt++;
goto out_map_frag_fail;
}
- fill_desc(ring, skb_frag_page(frag), size, dma,
- buf_num - 1 == i ? 1 : 0, buf_num, DESC_TYPE_PAGE);
+ priv->ops.fill_desc(ring, skb_frag_page(frag), size, dma,
+ seg_num - 1 == i ? 1 : 0, buf_num,
+ DESC_TYPE_PAGE, ndev->mtu);
}
/*complete translate all packets*/
@@ -182,19 +360,20 @@ int hns_nic_net_xmit_hw(struct net_device *ndev,
out_map_frag_fail:
- for (j = i - 1; j > 0; j--) {
+ while (ring->next_to_use != next_to_use) {
unfill_desc(ring);
- next_to_use = ring->next_to_use;
- dma_unmap_page(dev, ring->desc_cb[next_to_use].dma,
- ring->desc_cb[next_to_use].length,
- DMA_TO_DEVICE);
+ if (ring->next_to_use != next_to_use)
+ dma_unmap_page(dev,
+ ring->desc_cb[ring->next_to_use].dma,
+ ring->desc_cb[ring->next_to_use].length,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_single(dev,
+ ring->desc_cb[next_to_use].dma,
+ ring->desc_cb[next_to_use].length,
+ DMA_TO_DEVICE);
}
- unfill_desc(ring);
- next_to_use = ring->next_to_use;
- dma_unmap_single(dev, ring->desc_cb[next_to_use].dma,
- ring->desc_cb[next_to_use].length, DMA_TO_DEVICE);
-
out_err_tx_ok:
dev_kfree_skb_any(skb);
@@ -313,51 +492,110 @@ static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag,
return max_size;
}
-static void
-hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset)
+static void hns_nic_reuse_page(struct sk_buff *skb, int i,
+ struct hnae_ring *ring, int pull_len,
+ struct hnae_desc_cb *desc_cb)
{
+ struct hnae_desc *desc;
+ int truesize, size;
+ int last_offset;
+ bool twobufs;
+
+ twobufs = ((PAGE_SIZE < 8192) && hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048);
+
+ desc = &ring->desc[ring->next_to_clean];
+ size = le16_to_cpu(desc->rx.size);
+
+ if (twobufs) {
+ truesize = hnae_buf_size(ring);
+ } else {
+ truesize = ALIGN(size, L1_CACHE_BYTES);
+ last_offset = hnae_page_size(ring) - hnae_buf_size(ring);
+ }
+
+ skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len,
+ size - pull_len, truesize - pull_len);
+
/* avoid re-using remote pages,flag default unreuse */
- if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) {
- /* move offset up to the next cache line */
- desc_cb->page_offset += tsize;
+ if (unlikely(page_to_nid(desc_cb->priv) != numa_node_id()))
+ return;
+
+ if (twobufs) {
+ /* if we are only owner of page we can reuse it */
+ if (likely(page_count(desc_cb->priv) == 1)) {
+ /* flip page offset to other buffer */
+ desc_cb->page_offset ^= truesize;
- if (desc_cb->page_offset <= last_offset) {
desc_cb->reuse_flag = 1;
/* bump ref count on page before it is given*/
get_page(desc_cb->priv);
}
+ return;
+ }
+
+ /* move offset up to the next cache line */
+ desc_cb->page_offset += truesize;
+
+ if (desc_cb->page_offset <= last_offset) {
+ desc_cb->reuse_flag = 1;
+ /* bump ref count on page before it is given*/
+ get_page(desc_cb->priv);
}
}
+static void get_v2rx_desc_bnum(u32 bnum_flag, int *out_bnum)
+{
+ *out_bnum = hnae_get_field(bnum_flag,
+ HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S) + 1;
+}
+
+static void get_rx_desc_bnum(u32 bnum_flag, int *out_bnum)
+{
+ *out_bnum = hnae_get_field(bnum_flag,
+ HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S);
+}
+
static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
struct sk_buff **out_skb, int *out_bnum)
{
struct hnae_ring *ring = ring_data->ring;
struct net_device *ndev = ring_data->napi.dev;
+ struct hns_nic_priv *priv = netdev_priv(ndev);
struct sk_buff *skb;
struct hnae_desc *desc;
struct hnae_desc_cb *desc_cb;
unsigned char *va;
- int bnum, length, size, i, truesize, last_offset;
+ int bnum, length, i;
int pull_len;
u32 bnum_flag;
- last_offset = hnae_page_size(ring) - hnae_buf_size(ring);
desc = &ring->desc[ring->next_to_clean];
desc_cb = &ring->desc_cb[ring->next_to_clean];
- length = le16_to_cpu(desc->rx.pkt_len);
- bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag);
- bnum = hnae_get_field(bnum_flag, HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S);
- *out_bnum = bnum;
+
+ prefetch(desc);
+
va = (unsigned char *)desc_cb->buf + desc_cb->page_offset;
- skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE);
+ /* prefetch first cache line of first page */
+ prefetch(va);
+#if L1_CACHE_BYTES < 128
+ prefetch(va + L1_CACHE_BYTES);
+#endif
+
+ skb = *out_skb = napi_alloc_skb(&ring_data->napi,
+ HNS_RX_HEAD_SIZE);
if (unlikely(!skb)) {
netdev_err(ndev, "alloc rx skb fail\n");
ring->stats.sw_err_cnt++;
return -ENOMEM;
}
+ prefetchw(skb->data);
+ length = le16_to_cpu(desc->rx.pkt_len);
+ bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag);
+ priv->ops.get_rxd_bnum(bnum_flag, &bnum);
+ *out_bnum = bnum;
+
if (length <= HNS_RX_HEAD_SIZE) {
memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
@@ -380,13 +618,7 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
memcpy(__skb_put(skb, pull_len), va,
ALIGN(pull_len, sizeof(long)));
- size = le16_to_cpu(desc->rx.size);
- truesize = ALIGN(size, L1_CACHE_BYTES);
- skb_add_rx_frag(skb, 0, desc_cb->priv,
- desc_cb->page_offset + pull_len,
- size - pull_len, truesize - pull_len);
-
- hns_nic_reuse_page(desc_cb, truesize, last_offset);
+ hns_nic_reuse_page(skb, 0, ring, pull_len, desc_cb);
ring_ptr_move_fw(ring, next_to_clean);
if (unlikely(bnum >= (int)MAX_SKB_FRAGS)) { /* check err*/
@@ -396,13 +628,8 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
for (i = 1; i < bnum; i++) {
desc = &ring->desc[ring->next_to_clean];
desc_cb = &ring->desc_cb[ring->next_to_clean];
- size = le16_to_cpu(desc->rx.size);
- truesize = ALIGN(size, L1_CACHE_BYTES);
- skb_add_rx_frag(skb, i, desc_cb->priv,
- desc_cb->page_offset,
- size, truesize);
- hns_nic_reuse_page(desc_cb, truesize, last_offset);
+ hns_nic_reuse_page(skb, i, ring, 0, desc_cb);
ring_ptr_move_fw(ring, next_to_clean);
}
}
@@ -540,20 +767,20 @@ recv:
}
/* make all data has been write before submit */
- if (clean_count > 0) {
- hns_nic_alloc_rx_buffers(ring_data, clean_count);
- clean_count = 0;
- }
-
if (recv_pkts < budget) {
ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
- rmb(); /*complete read rx ring bd number*/
- if (ex_num > 0) {
- num += ex_num;
+
+ if (ex_num > clean_count) {
+ num += ex_num - clean_count;
+ rmb(); /*complete read rx ring bd number*/
goto recv;
}
}
+ /* make all data has been write before submit */
+ if (clean_count > 0)
+ hns_nic_alloc_rx_buffers(ring_data, clean_count);
+
return recv_pkts;
}
@@ -642,14 +869,20 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data,
bytes = 0;
pkts = 0;
- while (head != ring->next_to_clean)
+ while (head != ring->next_to_clean) {
hns_nic_reclaim_one_desc(ring, &bytes, &pkts);
+ /* issue prefetch for next Tx descriptor */
+ prefetch(&ring->desc_cb[ring->next_to_clean]);
+ }
NETIF_TX_UNLOCK(ndev);
dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index);
netdev_tx_completed_queue(dev_queue, pkts, bytes);
+ if (unlikely(priv->link && !netif_carrier_ok(ndev)))
+ netif_carrier_on(ndev);
+
if (unlikely(pkts && netif_carrier_ok(ndev) &&
(ring_space(ring) >= ring->max_desc_num_per_pkt * 2))) {
/* Make sure that anybody stopping the queue after this
@@ -716,6 +949,7 @@ static int hns_nic_common_poll(struct napi_struct *napi, int budget)
ring_data->ring, 0);
ring_data->fini_process(ring_data);
+ return 0;
}
return clean_complete;
@@ -848,15 +1082,58 @@ static void hns_nic_ring_close(struct net_device *netdev, int idx)
napi_disable(&priv->ring_data[idx].napi);
}
-static int hns_nic_init_irq(struct hns_nic_priv *priv)
+static void hns_set_irq_affinity(struct hns_nic_priv *priv)
{
struct hnae_handle *h = priv->ae_handle;
struct hns_nic_ring_data *rd;
int i;
- int ret;
int cpu;
cpumask_t mask;
+ /*diffrent irq banlance for 16core and 32core*/
+ if (h->q_num == num_possible_cpus()) {
+ for (i = 0; i < h->q_num * 2; i++) {
+ rd = &priv->ring_data[i];
+ if (cpu_online(rd->queue_index)) {
+ cpumask_clear(&mask);
+ cpu = rd->queue_index;
+ cpumask_set_cpu(cpu, &mask);
+ (void)irq_set_affinity_hint(rd->ring->irq,
+ &mask);
+ }
+ }
+ } else {
+ for (i = 0; i < h->q_num; i++) {
+ rd = &priv->ring_data[i];
+ if (cpu_online(rd->queue_index * 2)) {
+ cpumask_clear(&mask);
+ cpu = rd->queue_index * 2;
+ cpumask_set_cpu(cpu, &mask);
+ (void)irq_set_affinity_hint(rd->ring->irq,
+ &mask);
+ }
+ }
+
+ for (i = h->q_num; i < h->q_num * 2; i++) {
+ rd = &priv->ring_data[i];
+ if (cpu_online(rd->queue_index * 2 + 1)) {
+ cpumask_clear(&mask);
+ cpu = rd->queue_index * 2 + 1;
+ cpumask_set_cpu(cpu, &mask);
+ (void)irq_set_affinity_hint(rd->ring->irq,
+ &mask);
+ }
+ }
+ }
+}
+
+static int hns_nic_init_irq(struct hns_nic_priv *priv)
+{
+ struct hnae_handle *h = priv->ae_handle;
+ struct hns_nic_ring_data *rd;
+ int i;
+ int ret;
+
for (i = 0; i < h->q_num * 2; i++) {
rd = &priv->ring_data[i];
@@ -878,16 +1155,11 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv)
}
disable_irq(rd->ring->irq);
rd->ring->irq_init_flag = RCB_IRQ_INITED;
-
- /*set cpu affinity*/
- if (cpu_online(rd->queue_index)) {
- cpumask_clear(&mask);
- cpu = rd->queue_index;
- cpumask_set_cpu(cpu, &mask);
- irq_set_affinity_hint(rd->ring->irq, &mask);
- }
}
+ /*set cpu affinity*/
+ hns_set_irq_affinity(priv);
+
return 0;
}
@@ -1136,6 +1408,51 @@ static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu)
return ret;
}
+static int hns_nic_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_handle *h = priv->ae_handle;
+
+ switch (priv->enet_ver) {
+ case AE_VERSION_1:
+ if (features & (NETIF_F_TSO | NETIF_F_TSO6))
+ netdev_info(netdev, "enet v1 do not support tso!\n");
+ break;
+ default:
+ if (features & (NETIF_F_TSO | NETIF_F_TSO6)) {
+ priv->ops.fill_desc = fill_tso_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso;
+ /* The chip only support 7*4096 */
+ netif_set_gso_max_size(netdev, 7 * 4096);
+ h->dev->ops->set_tso_stats(h, 1);
+ } else {
+ priv->ops.fill_desc = fill_v2_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx;
+ h->dev->ops->set_tso_stats(h, 0);
+ }
+ break;
+ }
+ netdev->features = features;
+ return 0;
+}
+
+static netdev_features_t hns_nic_fix_features(
+ struct net_device *netdev, netdev_features_t features)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+
+ switch (priv->enet_ver) {
+ case AE_VERSION_1:
+ features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_HW_VLAN_CTAG_FILTER);
+ break;
+ default:
+ break;
+ }
+ return features;
+}
+
/**
* nic_set_multicast_list - set mutl mac address
* @netdev: net device
@@ -1231,6 +1548,8 @@ static const struct net_device_ops hns_nic_netdev_ops = {
.ndo_set_mac_address = hns_nic_net_set_mac_address,
.ndo_change_mtu = hns_nic_change_mtu,
.ndo_do_ioctl = hns_nic_do_ioctl,
+ .ndo_set_features = hns_nic_set_features,
+ .ndo_fix_features = hns_nic_fix_features,
.ndo_get_stats64 = hns_nic_get_stats64,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = hns_nic_poll_controller,
@@ -1315,22 +1634,26 @@ static void hns_nic_reset_subtask(struct hns_nic_priv *priv)
return;
hns_nic_dump(priv);
- netdev_info(priv->netdev, "Reset %s port\n",
- (type == HNAE_PORT_DEBUG ? "debug" : "business"));
+ netdev_info(priv->netdev, "try to reset %s port!\n",
+ (type == HNAE_PORT_DEBUG ? "debug" : "service"));
rtnl_lock();
/* put off any impending NetWatchDogTimeout */
priv->netdev->trans_start = jiffies;
- if (type == HNAE_PORT_DEBUG)
+ if (type == HNAE_PORT_DEBUG) {
hns_nic_net_reinit(priv->netdev);
+ } else {
+ netif_carrier_off(priv->netdev);
+ netif_tx_disable(priv->netdev);
+ }
rtnl_unlock();
}
/* for doing service complete*/
static void hns_nic_service_event_complete(struct hns_nic_priv *priv)
{
- assert(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state));
+ WARN_ON(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state));
smp_mb__before_atomic();
clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state);
@@ -1435,8 +1758,9 @@ static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv)
for (i = 0; i < h->q_num * 2; i++) {
netif_napi_del(&priv->ring_data[i].napi);
if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) {
- irq_set_affinity_hint(priv->ring_data[i].ring->irq,
- NULL);
+ (void)irq_set_affinity_hint(
+ priv->ring_data[i].ring->irq,
+ NULL);
free_irq(priv->ring_data[i].ring->irq,
&priv->ring_data[i]);
}
@@ -1446,6 +1770,31 @@ static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv)
kfree(priv->ring_data);
}
+static void hns_nic_set_priv_ops(struct net_device *netdev)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_handle *h = priv->ae_handle;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ priv->ops.fill_desc = fill_desc;
+ priv->ops.get_rxd_bnum = get_rx_desc_bnum;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx;
+ } else {
+ priv->ops.get_rxd_bnum = get_v2rx_desc_bnum;
+ if ((netdev->features & NETIF_F_TSO) ||
+ (netdev->features & NETIF_F_TSO6)) {
+ priv->ops.fill_desc = fill_tso_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso;
+ /* This chip only support 7*4096 */
+ netif_set_gso_max_size(netdev, 7 * 4096);
+ h->dev->ops->set_tso_stats(h, 1);
+ } else {
+ priv->ops.fill_desc = fill_v2_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx;
+ }
+ }
+}
+
static int hns_nic_try_get_ae(struct net_device *ndev)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
@@ -1473,6 +1822,8 @@ static int hns_nic_try_get_ae(struct net_device *ndev)
goto out_init_ring_data;
}
+ hns_nic_set_priv_ops(ndev);
+
ret = register_netdev(ndev);
if (ret) {
dev_err(priv->dev, "probe register netdev fail!\n");
@@ -1524,10 +1875,10 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
priv->dev = dev;
priv->netdev = ndev;
- if (of_device_is_compatible(node, "hisilicon,hns-nic-v2"))
- priv->enet_ver = AE_VERSION_2;
- else
+ if (of_device_is_compatible(node, "hisilicon,hns-nic-v1"))
priv->enet_ver = AE_VERSION_1;
+ else
+ priv->enet_ver = AE_VERSION_2;
ret = of_property_read_string(node, "ae-name", &priv->ae_name);
if (ret)
@@ -1543,6 +1894,7 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
ndev->priv_flags |= IFF_UNICAST_FLT;
ndev->netdev_ops = &hns_nic_netdev_ops;
hns_ethtool_set_ops(ndev);
+
ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
NETIF_F_GRO;
@@ -1550,6 +1902,17 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO;
+ switch (priv->enet_ver) {
+ case AE_VERSION_2:
+ ndev->features |= NETIF_F_TSO | NETIF_F_TSO6;
+ ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
+ NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6;
+ break;
+ default:
+ break;
+ }
+
SET_NETDEV_DEV(ndev, dev);
if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
index dae0ed19ac6d..4b75270f014e 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
@@ -40,6 +40,16 @@ struct hns_nic_ring_data {
void (*fini_process)(struct hns_nic_ring_data *);
};
+/* compatible the difference between two versions */
+struct hns_nic_ops {
+ void (*fill_desc)(struct hnae_ring *ring, void *priv,
+ int size, dma_addr_t dma, int frag_end,
+ int buf_num, enum hns_desc_type type, int mtu);
+ int (*maybe_stop_tx)(struct sk_buff **out_skb,
+ int *bnum, struct hnae_ring *ring);
+ void (*get_rxd_bnum)(u32 bnum_flag, int *out_bnum);
+};
+
struct hns_nic_priv {
const char *ae_name;
u32 enet_ver;
@@ -51,6 +61,8 @@ struct hns_nic_priv {
struct device *dev;
struct hnae_handle *ae_handle;
+ struct hns_nic_ops ops;
+
/* the cb for nic to manage the ring buffer, the first half of the
* array is for tx_ring and vice versa for the second half
*/
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
index a0332129970b..3df22840fcd1 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
@@ -11,7 +11,6 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-
#include "hns_enet.h"
#define HNS_PHY_PAGE_MDIX 0
@@ -72,24 +71,22 @@ static void hns_get_mdix_mode(struct net_device *net_dev,
struct hns_nic_priv *priv = netdev_priv(net_dev);
struct phy_device *phy_dev = priv->phy;
- if (!phy_dev || !phy_dev->bus) {
+ if (!phy_dev || !phy_dev->mdio.bus) {
cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
cmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
return;
}
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_MDIX);
+ phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_MDIX);
- retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSC_REG);
+ retval = phy_read(phy_dev, HNS_PHY_CSC_REG);
mdix_ctrl = hnae_get_field(retval, PHY_MDIX_CTRL_M, PHY_MDIX_CTRL_S);
- retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSS_REG);
+ retval = phy_read(phy_dev, HNS_PHY_CSS_REG);
mdix = hnae_get_bit(retval, PHY_MDIX_STATUS_B);
is_resolved = hnae_get_bit(retval, PHY_SPEED_DUP_RESOLVE_B);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_COPPER);
+ phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
switch (mdix_ctrl) {
case 0x0:
@@ -254,53 +251,36 @@ static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en)
if (en) {
/* speed : 1000M */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG, 2);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 21, 0x1046);
+ phy_write(phy_dev, HNS_PHY_PAGE_REG, 2);
+ phy_write(phy_dev, 21, 0x1046);
/* Force Master */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 9, 0x1F00);
+ phy_write(phy_dev, 9, 0x1F00);
/* Soft-reset */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 0, 0x9140);
+ phy_write(phy_dev, 0, 0x9140);
/* If autoneg disabled,two soft-reset operations */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 0, 0x9140);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0xFA);
+ phy_write(phy_dev, 0, 0x9140);
+ phy_write(phy_dev, 22, 0xFA);
/* Default is 0x0400 */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 1, 0x418);
+ phy_write(phy_dev, 1, 0x418);
/* Force 1000M Link, Default is 0x0200 */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 7, 0x20C);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0);
+ phy_write(phy_dev, 7, 0x20C);
+ phy_write(phy_dev, 22, 0);
/* Enable MAC loop-back */
- val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG);
+ val = phy_read(phy_dev, COPPER_CONTROL_REG);
val |= PHY_LOOP_BACK;
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG, val);
+ phy_write(phy_dev, COPPER_CONTROL_REG, val);
} else {
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0xFA);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 1, 0x400);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 7, 0x200);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0);
-
- val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG);
+ phy_write(phy_dev, 22, 0xFA);
+ phy_write(phy_dev, 1, 0x400);
+ phy_write(phy_dev, 7, 0x200);
+ phy_write(phy_dev, 22, 0);
+
+ val = phy_read(phy_dev, COPPER_CONTROL_REG);
val &= ~PHY_LOOP_BACK;
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG, val);
+ phy_write(phy_dev, COPPER_CONTROL_REG, val);
}
return 0;
}
@@ -667,6 +647,7 @@ static void hns_nic_get_drvinfo(struct net_device *net_dev,
drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0';
strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN);
+ drvinfo->eedump_len = 0;
}
/**
@@ -1018,16 +999,9 @@ int hns_phy_led_set(struct net_device *netdev, int value)
struct hns_nic_priv *priv = netdev_priv(netdev);
struct phy_device *phy_dev = priv->phy;
- if (!phy_dev->bus) {
- netdev_err(netdev, "phy_dev->bus is null!\n");
- return -EINVAL;
- }
- retval = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED);
- retval = mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_LED_FC_REG,
- value);
- retval = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
+ retval = phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED);
+ retval = phy_write(phy_dev, HNS_LED_FC_REG, value);
+ retval = phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
if (retval) {
netdev_err(netdev, "mdiobus_write fail !\n");
return retval;
@@ -1052,19 +1026,15 @@ int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
if (phy_dev)
switch (state) {
case ETHTOOL_ID_ACTIVE:
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_LED);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_LED);
if (ret)
return ret;
- priv->phy_led_val = (u16)mdiobus_read(phy_dev->bus,
- phy_dev->addr,
- HNS_LED_FC_REG);
+ priv->phy_led_val = phy_read(phy_dev, HNS_LED_FC_REG);
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_COPPER);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_COPPER);
if (ret)
return ret;
return 2;
@@ -1079,20 +1049,18 @@ int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
return ret;
break;
case ETHTOOL_ID_INACTIVE:
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_LED);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_LED);
if (ret)
return ret;
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_LED_FC_REG, priv->phy_led_val);
+ ret = phy_write(phy_dev, HNS_LED_FC_REG,
+ priv->phy_led_val);
if (ret)
return ret;
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_COPPER);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_COPPER);
if (ret)
return ret;
break;
@@ -1187,6 +1155,95 @@ static int hns_nic_nway_reset(struct net_device *netdev)
return ret;
}
+static u32
+hns_get_rss_key_size(struct net_device *netdev)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ u32 ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+ ret = ops->get_rss_key_size(priv->ae_handle);
+
+ return ret;
+}
+
+static u32
+hns_get_rss_indir_size(struct net_device *netdev)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ u32 ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+ ret = ops->get_rss_indir_size(priv->ae_handle);
+
+ return ret;
+}
+
+static int
+hns_get_rss(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ int ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+
+ if (!indir)
+ return 0;
+
+ ret = ops->get_rss(priv->ae_handle, indir, key, hfunc);
+
+ return 0;
+}
+
+static int
+hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key,
+ const u8 hfunc)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ int ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+
+ /* currently hfunc can only be Toeplitz hash */
+ if (key ||
+ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+ if (!indir)
+ return 0;
+
+ ret = ops->set_rss(priv->ae_handle, indir, key, hfunc);
+
+ return 0;
+}
+
static struct ethtool_ops hns_ethtool_ops = {
.get_drvinfo = hns_nic_get_drvinfo,
.get_link = hns_nic_get_link,
@@ -1206,6 +1263,10 @@ static struct ethtool_ops hns_ethtool_ops = {
.get_regs_len = hns_get_regs_len,
.get_regs = hns_get_regs,
.nway_reset = hns_nic_nway_reset,
+ .get_rxfh_key_size = hns_get_rss_key_size,
+ .get_rxfh_indir_size = hns_get_rss_indir_size,
+ .get_rxfh = hns_get_rss,
+ .set_rxfh = hns_set_rss,
};
void hns_ethtool_set_ops(struct net_device *ndev)
diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c
index 37491c85bc42..58c96c412fe8 100644
--- a/drivers/net/ethernet/hisilicon/hns_mdio.c
+++ b/drivers/net/ethernet/hisilicon/hns_mdio.c
@@ -463,11 +463,6 @@ static int hns_mdio_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "no syscon hisilicon,peri-c-subctrl\n");
mdio_dev->subctrl_vbase = NULL;
}
- new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR,
- sizeof(int), GFP_KERNEL);
- if (!new_bus->irq)
- return -ENOMEM;
-
new_bus->parent = &pdev->dev;
platform_set_drvdata(pdev, new_bus);
diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c
index ae6e30d39f0f..1d5c3e16d8f4 100644
--- a/drivers/net/ethernet/hp/hp100.c
+++ b/drivers/net/ethernet/hp/hp100.c
@@ -2843,7 +2843,7 @@ static void cleanup_dev(struct net_device *d)
}
#ifdef CONFIG_EISA
-static int __init hp100_eisa_probe (struct device *gendev)
+static int hp100_eisa_probe(struct device *gendev)
{
struct net_device *dev = alloc_etherdev(sizeof(struct hp100_private));
struct eisa_device *edev = to_eisa_device(gendev);
diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig
index 99c1cebd002d..37dceabf8861 100644
--- a/drivers/net/ethernet/ibm/Kconfig
+++ b/drivers/net/ethernet/ibm/Kconfig
@@ -37,4 +37,14 @@ config EHEA
To compile the driver as a module, choose M here. The module
will be called ehea.
+config IBMVNIC
+ tristate "IBM Virtual NIC support"
+ depends on PPC_PSERIES
+ ---help---
+ This driver supports Virtual NIC adapters on IBM i and IBM System p
+ systems.
+
+ To compile this driver as a module, choose M here. The module will
+ be called ibmvnic.
+
endif # NET_VENDOR_IBM
diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile
index 2f04e71a5926..447865c8b632 100644
--- a/drivers/net/ethernet/ibm/Makefile
+++ b/drivers/net/ethernet/ibm/Makefile
@@ -3,5 +3,6 @@
#
obj-$(CONFIG_IBMVETH) += ibmveth.o
+obj-$(CONFIG_IBMVNIC) += ibmvnic.o
obj-$(CONFIG_IBM_EMAC) += emac/
obj-$(CONFIG_EHEA) += ehea/
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 7af870a3c549..335417b4756b 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -169,7 +169,7 @@ static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool)
if (!pool->free_map)
return -1;
- pool->dma_addr = kmalloc(sizeof(dma_addr_t) * pool->size, GFP_KERNEL);
+ pool->dma_addr = kcalloc(pool->size, sizeof(dma_addr_t), GFP_KERNEL);
if (!pool->dma_addr) {
kfree(pool->free_map);
pool->free_map = NULL;
@@ -187,8 +187,6 @@ static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool)
return -1;
}
- memset(pool->dma_addr, 0, sizeof(dma_addr_t) * pool->size);
-
for (i = 0; i < pool->size; ++i)
pool->free_map[i] = i;
@@ -763,7 +761,7 @@ static netdev_features_t ibmveth_fix_features(struct net_device *dev,
*/
if (!(features & NETIF_F_RXCSUM))
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
return features;
}
@@ -928,7 +926,8 @@ static int ibmveth_set_features(struct net_device *dev,
rc1 = ibmveth_set_csum_offload(dev, rx_csum);
if (rc1 && !adapter->rx_csum)
dev->features =
- features & ~(NETIF_F_ALL_CSUM | NETIF_F_RXCSUM);
+ features & ~(NETIF_F_CSUM_MASK |
+ NETIF_F_RXCSUM);
}
if (large_send != adapter->large_send) {
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
new file mode 100644
index 000000000000..7d6570843723
--- /dev/null
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -0,0 +1,3585 @@
+/**************************************************************************/
+/* */
+/* IBM System i and System p Virtual NIC Device Driver */
+/* Copyright (C) 2014 IBM Corp. */
+/* Santiago Leon (santi_leon@yahoo.com) */
+/* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */
+/* John Allen (jallen@linux.vnet.ibm.com) */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. */
+/* */
+/* This module contains the implementation of a virtual ethernet device */
+/* for use with IBM i/p Series LPAR Linux. It utilizes the logical LAN */
+/* option of the RS/6000 Platform Architecture to interface with virtual */
+/* ethernet NICs that are presented to the partition by the hypervisor. */
+/* */
+/* Messages are passed between the VNIC driver and the VNIC server using */
+/* Command/Response Queues (CRQs) and sub CRQs (sCRQs). CRQs are used to */
+/* issue and receive commands that initiate communication with the server */
+/* on driver initialization. Sub CRQs (sCRQs) are similar to CRQs, but */
+/* are used by the driver to notify the server that a packet is */
+/* ready for transmission or that a buffer has been added to receive a */
+/* packet. Subsequently, sCRQs are used by the server to notify the */
+/* driver that a packet transmission has been completed or that a packet */
+/* has been received and placed in a waiting buffer. */
+/* */
+/* In lieu of a more conventional "on-the-fly" DMA mapping strategy in */
+/* which skbs are DMA mapped and immediately unmapped when the transmit */
+/* or receive has been completed, the VNIC driver is required to use */
+/* "long term mapping". This entails that large, continuous DMA mapped */
+/* buffers are allocated on driver initialization and these buffers are */
+/* then continuously reused to pass skbs to and from the VNIC server. */
+/* */
+/**************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/completion.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/ethtool.h>
+#include <linux/proc_fs.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <net/net_namespace.h>
+#include <asm/hvcall.h>
+#include <linux/atomic.h>
+#include <asm/vio.h>
+#include <asm/iommu.h>
+#include <linux/uaccess.h>
+#include <asm/firmware.h>
+#include <linux/seq_file.h>
+
+#include "ibmvnic.h"
+
+static const char ibmvnic_driver_name[] = "ibmvnic";
+static const char ibmvnic_driver_string[] = "IBM System i/p Virtual NIC Driver";
+
+MODULE_AUTHOR("Santiago Leon <santi_leon@yahoo.com>");
+MODULE_DESCRIPTION("IBM System i/p Virtual NIC Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(IBMVNIC_DRIVER_VERSION);
+
+static int ibmvnic_version = IBMVNIC_INITIAL_VERSION;
+static int ibmvnic_remove(struct vio_dev *);
+static void release_sub_crqs(struct ibmvnic_adapter *);
+static int ibmvnic_reset_crq(struct ibmvnic_adapter *);
+static int ibmvnic_send_crq_init(struct ibmvnic_adapter *);
+static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *);
+static int ibmvnic_send_crq(struct ibmvnic_adapter *, union ibmvnic_crq *);
+static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle,
+ union sub_crq *sub_crq);
+static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance);
+static int enable_scrq_irq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static int disable_scrq_irq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static int pending_scrq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static int ibmvnic_poll(struct napi_struct *napi, int data);
+static void send_map_query(struct ibmvnic_adapter *adapter);
+static void send_request_map(struct ibmvnic_adapter *, dma_addr_t, __be32, u8);
+static void send_request_unmap(struct ibmvnic_adapter *, u8);
+
+struct ibmvnic_stat {
+ char name[ETH_GSTRING_LEN];
+ int offset;
+};
+
+#define IBMVNIC_STAT_OFF(stat) (offsetof(struct ibmvnic_adapter, stats) + \
+ offsetof(struct ibmvnic_statistics, stat))
+#define IBMVNIC_GET_STAT(a, off) (*((u64 *)(((unsigned long)(a)) + off)))
+
+static const struct ibmvnic_stat ibmvnic_stats[] = {
+ {"rx_packets", IBMVNIC_STAT_OFF(rx_packets)},
+ {"rx_bytes", IBMVNIC_STAT_OFF(rx_bytes)},
+ {"tx_packets", IBMVNIC_STAT_OFF(tx_packets)},
+ {"tx_bytes", IBMVNIC_STAT_OFF(tx_bytes)},
+ {"ucast_tx_packets", IBMVNIC_STAT_OFF(ucast_tx_packets)},
+ {"ucast_rx_packets", IBMVNIC_STAT_OFF(ucast_rx_packets)},
+ {"mcast_tx_packets", IBMVNIC_STAT_OFF(mcast_tx_packets)},
+ {"mcast_rx_packets", IBMVNIC_STAT_OFF(mcast_rx_packets)},
+ {"bcast_tx_packets", IBMVNIC_STAT_OFF(bcast_tx_packets)},
+ {"bcast_rx_packets", IBMVNIC_STAT_OFF(bcast_rx_packets)},
+ {"align_errors", IBMVNIC_STAT_OFF(align_errors)},
+ {"fcs_errors", IBMVNIC_STAT_OFF(fcs_errors)},
+ {"single_collision_frames", IBMVNIC_STAT_OFF(single_collision_frames)},
+ {"multi_collision_frames", IBMVNIC_STAT_OFF(multi_collision_frames)},
+ {"sqe_test_errors", IBMVNIC_STAT_OFF(sqe_test_errors)},
+ {"deferred_tx", IBMVNIC_STAT_OFF(deferred_tx)},
+ {"late_collisions", IBMVNIC_STAT_OFF(late_collisions)},
+ {"excess_collisions", IBMVNIC_STAT_OFF(excess_collisions)},
+ {"internal_mac_tx_errors", IBMVNIC_STAT_OFF(internal_mac_tx_errors)},
+ {"carrier_sense", IBMVNIC_STAT_OFF(carrier_sense)},
+ {"too_long_frames", IBMVNIC_STAT_OFF(too_long_frames)},
+ {"internal_mac_rx_errors", IBMVNIC_STAT_OFF(internal_mac_rx_errors)},
+};
+
+static long h_reg_sub_crq(unsigned long unit_address, unsigned long token,
+ unsigned long length, unsigned long *number,
+ unsigned long *irq)
+{
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+ long rc;
+
+ rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, token, length);
+ *number = retbuf[0];
+ *irq = retbuf[1];
+
+ return rc;
+}
+
+/* net_device_ops functions */
+
+static void init_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *rx_pool, int num, int index,
+ int buff_size, int active)
+{
+ netdev_dbg(adapter->netdev,
+ "Initializing rx_pool %d, %d buffs, %d bytes each\n",
+ index, num, buff_size);
+ rx_pool->size = num;
+ rx_pool->index = index;
+ rx_pool->buff_size = buff_size;
+ rx_pool->active = active;
+}
+
+static int alloc_long_term_buff(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_long_term_buff *ltb, int size)
+{
+ struct device *dev = &adapter->vdev->dev;
+
+ ltb->size = size;
+ ltb->buff = dma_alloc_coherent(dev, ltb->size, &ltb->addr,
+ GFP_KERNEL);
+
+ if (!ltb->buff) {
+ dev_err(dev, "Couldn't alloc long term buffer\n");
+ return -ENOMEM;
+ }
+ ltb->map_id = adapter->map_id;
+ adapter->map_id++;
+ send_request_map(adapter, ltb->addr,
+ ltb->size, ltb->map_id);
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+ return 0;
+}
+
+static void free_long_term_buff(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_long_term_buff *ltb)
+{
+ struct device *dev = &adapter->vdev->dev;
+
+ dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr);
+ send_request_unmap(adapter, ltb->map_id);
+}
+
+static int alloc_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *pool)
+{
+ struct device *dev = &adapter->vdev->dev;
+ int i;
+
+ pool->free_map = kcalloc(pool->size, sizeof(int), GFP_KERNEL);
+ if (!pool->free_map)
+ return -ENOMEM;
+
+ pool->rx_buff = kcalloc(pool->size, sizeof(struct ibmvnic_rx_buff),
+ GFP_KERNEL);
+
+ if (!pool->rx_buff) {
+ dev_err(dev, "Couldn't alloc rx buffers\n");
+ kfree(pool->free_map);
+ return -ENOMEM;
+ }
+
+ if (alloc_long_term_buff(adapter, &pool->long_term_buff,
+ pool->size * pool->buff_size)) {
+ kfree(pool->free_map);
+ kfree(pool->rx_buff);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pool->size; ++i)
+ pool->free_map[i] = i;
+
+ atomic_set(&pool->available, 0);
+ pool->next_alloc = 0;
+ pool->next_free = 0;
+
+ return 0;
+}
+
+static void replenish_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *pool)
+{
+ int count = pool->size - atomic_read(&pool->available);
+ struct device *dev = &adapter->vdev->dev;
+ int buffers_added = 0;
+ unsigned long lpar_rc;
+ union sub_crq sub_crq;
+ struct sk_buff *skb;
+ unsigned int offset;
+ dma_addr_t dma_addr;
+ unsigned char *dst;
+ u64 *handle_array;
+ int shift = 0;
+ int index;
+ int i;
+
+ handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
+ be32_to_cpu(adapter->login_rsp_buf->
+ off_rxadd_subcrqs));
+
+ for (i = 0; i < count; ++i) {
+ skb = alloc_skb(pool->buff_size, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(dev, "Couldn't replenish rx buff\n");
+ adapter->replenish_no_mem++;
+ break;
+ }
+
+ index = pool->free_map[pool->next_free];
+
+ if (pool->rx_buff[index].skb)
+ dev_err(dev, "Inconsistent free_map!\n");
+
+ /* Copy the skb to the long term mapped DMA buffer */
+ offset = index * pool->buff_size;
+ dst = pool->long_term_buff.buff + offset;
+ memset(dst, 0, pool->buff_size);
+ dma_addr = pool->long_term_buff.addr + offset;
+ pool->rx_buff[index].data = dst;
+
+ pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP;
+ pool->rx_buff[index].dma = dma_addr;
+ pool->rx_buff[index].skb = skb;
+ pool->rx_buff[index].pool_index = pool->index;
+ pool->rx_buff[index].size = pool->buff_size;
+
+ memset(&sub_crq, 0, sizeof(sub_crq));
+ sub_crq.rx_add.first = IBMVNIC_CRQ_CMD;
+ sub_crq.rx_add.correlator =
+ cpu_to_be64((u64)&pool->rx_buff[index]);
+ sub_crq.rx_add.ioba = cpu_to_be32(dma_addr);
+ sub_crq.rx_add.map_id = pool->long_term_buff.map_id;
+
+ /* The length field of the sCRQ is defined to be 24 bits so the
+ * buffer size needs to be left shifted by a byte before it is
+ * converted to big endian to prevent the last byte from being
+ * truncated.
+ */
+#ifdef __LITTLE_ENDIAN__
+ shift = 8;
+#endif
+ sub_crq.rx_add.len = cpu_to_be32(pool->buff_size << shift);
+
+ lpar_rc = send_subcrq(adapter, handle_array[pool->index],
+ &sub_crq);
+ if (lpar_rc != H_SUCCESS)
+ goto failure;
+
+ buffers_added++;
+ adapter->replenish_add_buff_success++;
+ pool->next_free = (pool->next_free + 1) % pool->size;
+ }
+ atomic_add(buffers_added, &pool->available);
+ return;
+
+failure:
+ dev_info(dev, "replenish pools failure\n");
+ pool->free_map[pool->next_free] = index;
+ pool->rx_buff[index].skb = NULL;
+ if (!dma_mapping_error(dev, dma_addr))
+ dma_unmap_single(dev, dma_addr, pool->buff_size,
+ DMA_FROM_DEVICE);
+
+ dev_kfree_skb_any(skb);
+ adapter->replenish_add_buff_failure++;
+ atomic_add(buffers_added, &pool->available);
+}
+
+static void replenish_pools(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ if (adapter->migrated)
+ return;
+
+ adapter->replenish_task_cycles++;
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ i++) {
+ if (adapter->rx_pool[i].active)
+ replenish_rx_pool(adapter, &adapter->rx_pool[i]);
+ }
+}
+
+static void free_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *pool)
+{
+ int i;
+
+ kfree(pool->free_map);
+ pool->free_map = NULL;
+
+ if (!pool->rx_buff)
+ return;
+
+ for (i = 0; i < pool->size; i++) {
+ if (pool->rx_buff[i].skb) {
+ dev_kfree_skb_any(pool->rx_buff[i].skb);
+ pool->rx_buff[i].skb = NULL;
+ }
+ }
+ kfree(pool->rx_buff);
+ pool->rx_buff = NULL;
+}
+
+static int ibmvnic_open(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_tx_pool *tx_pool;
+ union ibmvnic_crq crq;
+ int rxadd_subcrqs;
+ u64 *size_array;
+ int tx_subcrqs;
+ int i, j;
+
+ rxadd_subcrqs =
+ be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ tx_subcrqs =
+ be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
+ size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
+ be32_to_cpu(adapter->login_rsp_buf->
+ off_rxadd_buff_size));
+ adapter->map_id = 1;
+ adapter->napi = kcalloc(adapter->req_rx_queues,
+ sizeof(struct napi_struct), GFP_KERNEL);
+ if (!adapter->napi)
+ goto alloc_napi_failed;
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ netif_napi_add(netdev, &adapter->napi[i], ibmvnic_poll,
+ NAPI_POLL_WEIGHT);
+ napi_enable(&adapter->napi[i]);
+ }
+ adapter->rx_pool =
+ kcalloc(rxadd_subcrqs, sizeof(struct ibmvnic_rx_pool), GFP_KERNEL);
+
+ if (!adapter->rx_pool)
+ goto rx_pool_arr_alloc_failed;
+ send_map_query(adapter);
+ for (i = 0; i < rxadd_subcrqs; i++) {
+ init_rx_pool(adapter, &adapter->rx_pool[i],
+ IBMVNIC_BUFFS_PER_POOL, i,
+ be64_to_cpu(size_array[i]), 1);
+ if (alloc_rx_pool(adapter, &adapter->rx_pool[i])) {
+ dev_err(dev, "Couldn't alloc rx pool\n");
+ goto rx_pool_alloc_failed;
+ }
+ }
+ adapter->tx_pool =
+ kcalloc(tx_subcrqs, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL);
+
+ if (!adapter->tx_pool)
+ goto tx_pool_arr_alloc_failed;
+ for (i = 0; i < tx_subcrqs; i++) {
+ tx_pool = &adapter->tx_pool[i];
+ tx_pool->tx_buff =
+ kcalloc(adapter->max_tx_entries_per_subcrq,
+ sizeof(struct ibmvnic_tx_buff), GFP_KERNEL);
+ if (!tx_pool->tx_buff)
+ goto tx_pool_alloc_failed;
+
+ if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff,
+ adapter->max_tx_entries_per_subcrq *
+ adapter->req_mtu))
+ goto tx_ltb_alloc_failed;
+
+ tx_pool->free_map =
+ kcalloc(adapter->max_tx_entries_per_subcrq,
+ sizeof(int), GFP_KERNEL);
+ if (!tx_pool->free_map)
+ goto tx_fm_alloc_failed;
+
+ for (j = 0; j < adapter->max_tx_entries_per_subcrq; j++)
+ tx_pool->free_map[j] = j;
+
+ tx_pool->consumer_index = 0;
+ tx_pool->producer_index = 0;
+ }
+ adapter->bounce_buffer_size =
+ (netdev->mtu + ETH_HLEN - 1) / PAGE_SIZE + 1;
+ adapter->bounce_buffer = kmalloc(adapter->bounce_buffer_size,
+ GFP_KERNEL);
+ if (!adapter->bounce_buffer)
+ goto bounce_alloc_failed;
+
+ adapter->bounce_buffer_dma = dma_map_single(dev, adapter->bounce_buffer,
+ adapter->bounce_buffer_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
+ dev_err(dev, "Couldn't map tx bounce buffer\n");
+ goto bounce_map_failed;
+ }
+ replenish_pools(adapter);
+
+ /* We're ready to receive frames, enable the sub-crq interrupts and
+ * set the logical link state to up
+ */
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ enable_scrq_irq(adapter, adapter->rx_scrq[i]);
+
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ enable_scrq_irq(adapter, adapter->tx_scrq[i]);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.logical_link_state.first = IBMVNIC_CRQ_CMD;
+ crq.logical_link_state.cmd = LOGICAL_LINK_STATE;
+ crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_UP;
+ ibmvnic_send_crq(adapter, &crq);
+
+ netif_start_queue(netdev);
+ return 0;
+
+bounce_map_failed:
+ kfree(adapter->bounce_buffer);
+bounce_alloc_failed:
+ i = tx_subcrqs - 1;
+ kfree(adapter->tx_pool[i].free_map);
+tx_fm_alloc_failed:
+ free_long_term_buff(adapter, &adapter->tx_pool[i].long_term_buff);
+tx_ltb_alloc_failed:
+ kfree(adapter->tx_pool[i].tx_buff);
+tx_pool_alloc_failed:
+ for (j = 0; j < i; j++) {
+ kfree(adapter->tx_pool[j].tx_buff);
+ free_long_term_buff(adapter,
+ &adapter->tx_pool[j].long_term_buff);
+ kfree(adapter->tx_pool[j].free_map);
+ }
+ kfree(adapter->tx_pool);
+ adapter->tx_pool = NULL;
+tx_pool_arr_alloc_failed:
+ i = rxadd_subcrqs;
+rx_pool_alloc_failed:
+ for (j = 0; j < i; j++) {
+ free_rx_pool(adapter, &adapter->rx_pool[j]);
+ free_long_term_buff(adapter,
+ &adapter->rx_pool[j].long_term_buff);
+ }
+ kfree(adapter->rx_pool);
+ adapter->rx_pool = NULL;
+rx_pool_arr_alloc_failed:
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ napi_enable(&adapter->napi[i]);
+alloc_napi_failed:
+ return -ENOMEM;
+}
+
+static int ibmvnic_close(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq crq;
+ int i;
+
+ adapter->closing = true;
+
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ napi_disable(&adapter->napi[i]);
+
+ netif_stop_queue(netdev);
+
+ if (adapter->bounce_buffer) {
+ if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
+ dma_unmap_single(&adapter->vdev->dev,
+ adapter->bounce_buffer_dma,
+ adapter->bounce_buffer_size,
+ DMA_BIDIRECTIONAL);
+ adapter->bounce_buffer_dma = DMA_ERROR_CODE;
+ }
+ kfree(adapter->bounce_buffer);
+ adapter->bounce_buffer = NULL;
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.logical_link_state.first = IBMVNIC_CRQ_CMD;
+ crq.logical_link_state.cmd = LOGICAL_LINK_STATE;
+ crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_DN;
+ ibmvnic_send_crq(adapter, &crq);
+
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
+ i++) {
+ kfree(adapter->tx_pool[i].tx_buff);
+ free_long_term_buff(adapter,
+ &adapter->tx_pool[i].long_term_buff);
+ kfree(adapter->tx_pool[i].free_map);
+ }
+ kfree(adapter->tx_pool);
+ adapter->tx_pool = NULL;
+
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ i++) {
+ free_rx_pool(adapter, &adapter->rx_pool[i]);
+ free_long_term_buff(adapter,
+ &adapter->rx_pool[i].long_term_buff);
+ }
+ kfree(adapter->rx_pool);
+ adapter->rx_pool = NULL;
+
+ adapter->closing = false;
+
+ return 0;
+}
+
+static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int queue_num = skb_get_queue_mapping(skb);
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_tx_buff *tx_buff = NULL;
+ struct ibmvnic_tx_pool *tx_pool;
+ unsigned int tx_send_failed = 0;
+ unsigned int tx_map_failed = 0;
+ unsigned int tx_dropped = 0;
+ unsigned int tx_packets = 0;
+ unsigned int tx_bytes = 0;
+ dma_addr_t data_dma_addr;
+ struct netdev_queue *txq;
+ bool used_bounce = false;
+ unsigned long lpar_rc;
+ union sub_crq tx_crq;
+ unsigned int offset;
+ unsigned char *dst;
+ u64 *handle_array;
+ int index = 0;
+ int ret = 0;
+
+ tx_pool = &adapter->tx_pool[queue_num];
+ txq = netdev_get_tx_queue(netdev, skb_get_queue_mapping(skb));
+ handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
+ be32_to_cpu(adapter->login_rsp_buf->
+ off_txsubm_subcrqs));
+ if (adapter->migrated) {
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ index = tx_pool->free_map[tx_pool->consumer_index];
+ offset = index * adapter->req_mtu;
+ dst = tx_pool->long_term_buff.buff + offset;
+ memset(dst, 0, adapter->req_mtu);
+ skb_copy_from_linear_data(skb, dst, skb->len);
+ data_dma_addr = tx_pool->long_term_buff.addr + offset;
+
+ tx_pool->consumer_index =
+ (tx_pool->consumer_index + 1) %
+ adapter->max_tx_entries_per_subcrq;
+
+ tx_buff = &tx_pool->tx_buff[index];
+ tx_buff->skb = skb;
+ tx_buff->data_dma[0] = data_dma_addr;
+ tx_buff->data_len[0] = skb->len;
+ tx_buff->index = index;
+ tx_buff->pool_index = queue_num;
+ tx_buff->last_frag = true;
+ tx_buff->used_bounce = used_bounce;
+
+ memset(&tx_crq, 0, sizeof(tx_crq));
+ tx_crq.v1.first = IBMVNIC_CRQ_CMD;
+ tx_crq.v1.type = IBMVNIC_TX_DESC;
+ tx_crq.v1.n_crq_elem = 1;
+ tx_crq.v1.n_sge = 1;
+ tx_crq.v1.flags1 = IBMVNIC_TX_COMP_NEEDED;
+ tx_crq.v1.correlator = cpu_to_be32(index);
+ tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id);
+ tx_crq.v1.sge_len = cpu_to_be32(skb->len);
+ tx_crq.v1.ioba = cpu_to_be64(data_dma_addr);
+
+ if (adapter->vlan_header_insertion) {
+ tx_crq.v1.flags2 |= IBMVNIC_TX_VLAN_INSERT;
+ tx_crq.v1.vlan_id = cpu_to_be16(skb->vlan_tci);
+ }
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ if (ip_hdr(skb)->version == 4)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV4;
+ else if (ip_hdr(skb)->version == 6)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV6;
+
+ if (ip_hdr(skb)->protocol == IPPROTO_TCP)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_TCP;
+ else if (ip_hdr(skb)->protocol != IPPROTO_TCP)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_UDP;
+ }
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_CHKSUM_OFFLOAD;
+
+ lpar_rc = send_subcrq(adapter, handle_array[0], &tx_crq);
+
+ if (lpar_rc != H_SUCCESS) {
+ dev_err(dev, "tx failed with code %ld\n", lpar_rc);
+
+ if (tx_pool->consumer_index == 0)
+ tx_pool->consumer_index =
+ adapter->max_tx_entries_per_subcrq - 1;
+ else
+ tx_pool->consumer_index--;
+
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+ tx_packets++;
+ tx_bytes += skb->len;
+ txq->trans_start = jiffies;
+ ret = NETDEV_TX_OK;
+
+out:
+ netdev->stats.tx_dropped += tx_dropped;
+ netdev->stats.tx_bytes += tx_bytes;
+ netdev->stats.tx_packets += tx_packets;
+ adapter->tx_send_failed += tx_send_failed;
+ adapter->tx_map_failed += tx_map_failed;
+
+ return ret;
+}
+
+static void ibmvnic_set_multi(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct netdev_hw_addr *ha;
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_capability.first = IBMVNIC_CRQ_CMD;
+ crq.request_capability.cmd = REQUEST_CAPABILITY;
+
+ if (netdev->flags & IFF_PROMISC) {
+ if (!adapter->promisc_supported)
+ return;
+ } else {
+ if (netdev->flags & IFF_ALLMULTI) {
+ /* Accept all multicast */
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = IBMVNIC_ENABLE_ALL;
+ ibmvnic_send_crq(adapter, &crq);
+ } else if (netdev_mc_empty(netdev)) {
+ /* Reject all multicast */
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = IBMVNIC_DISABLE_ALL;
+ ibmvnic_send_crq(adapter, &crq);
+ } else {
+ /* Accept one or more multicast(s) */
+ netdev_for_each_mc_addr(ha, netdev) {
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = IBMVNIC_ENABLE_MC;
+ ether_addr_copy(&crq.multicast_ctrl.mac_addr[0],
+ ha->addr);
+ ibmvnic_send_crq(adapter, &crq);
+ }
+ }
+ }
+}
+
+static int ibmvnic_set_mac(struct net_device *netdev, void *p)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+ union ibmvnic_crq crq;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.change_mac_addr.first = IBMVNIC_CRQ_CMD;
+ crq.change_mac_addr.cmd = CHANGE_MAC_ADDR;
+ ether_addr_copy(&crq.change_mac_addr.mac_addr[0], addr->sa_data);
+ ibmvnic_send_crq(adapter, &crq);
+ /* netdev->dev_addr is changed in handle_change_mac_rsp function */
+ return 0;
+}
+
+static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ if (new_mtu > adapter->req_mtu || new_mtu < adapter->min_mtu)
+ return -EINVAL;
+
+ netdev->mtu = new_mtu;
+ return 0;
+}
+
+static void ibmvnic_tx_timeout(struct net_device *dev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(dev);
+ int rc;
+
+ /* Adapter timed out, resetting it */
+ release_sub_crqs(adapter);
+ rc = ibmvnic_reset_crq(adapter);
+ if (rc)
+ dev_err(&adapter->vdev->dev, "Adapter timeout, reset failed\n");
+ else
+ ibmvnic_send_crq_init(adapter);
+}
+
+static void remove_buff_from_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_buff *rx_buff)
+{
+ struct ibmvnic_rx_pool *pool = &adapter->rx_pool[rx_buff->pool_index];
+
+ rx_buff->skb = NULL;
+
+ pool->free_map[pool->next_alloc] = (int)(rx_buff - pool->rx_buff);
+ pool->next_alloc = (pool->next_alloc + 1) % pool->size;
+
+ atomic_dec(&pool->available);
+}
+
+static int ibmvnic_poll(struct napi_struct *napi, int budget)
+{
+ struct net_device *netdev = napi->dev;
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int scrq_num = (int)(napi - adapter->napi);
+ int frames_processed = 0;
+restart_poll:
+ while (frames_processed < budget) {
+ struct sk_buff *skb;
+ struct ibmvnic_rx_buff *rx_buff;
+ union sub_crq *next;
+ u32 length;
+ u16 offset;
+ u8 flags = 0;
+
+ if (!pending_scrq(adapter, adapter->rx_scrq[scrq_num]))
+ break;
+ next = ibmvnic_next_scrq(adapter, adapter->rx_scrq[scrq_num]);
+ rx_buff =
+ (struct ibmvnic_rx_buff *)be64_to_cpu(next->
+ rx_comp.correlator);
+ /* do error checking */
+ if (next->rx_comp.rc) {
+ netdev_err(netdev, "rx error %x\n", next->rx_comp.rc);
+ /* free the entry */
+ next->rx_comp.first = 0;
+ remove_buff_from_pool(adapter, rx_buff);
+ break;
+ }
+
+ length = be32_to_cpu(next->rx_comp.len);
+ offset = be16_to_cpu(next->rx_comp.off_frame_data);
+ flags = next->rx_comp.flags;
+ skb = rx_buff->skb;
+ skb_copy_to_linear_data(skb, rx_buff->data + offset,
+ length);
+ skb->vlan_tci = be16_to_cpu(next->rx_comp.vlan_tci);
+ /* free the entry */
+ next->rx_comp.first = 0;
+ remove_buff_from_pool(adapter, rx_buff);
+
+ skb_put(skb, length);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ if (flags & IBMVNIC_IP_CHKSUM_GOOD &&
+ flags & IBMVNIC_TCP_UDP_CHKSUM_GOOD) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+ length = skb->len;
+ napi_gro_receive(napi, skb); /* send it up */
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += length;
+ frames_processed++;
+ }
+ replenish_pools(adapter);
+
+ if (frames_processed < budget) {
+ enable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]);
+ napi_complete(napi);
+ if (pending_scrq(adapter, adapter->rx_scrq[scrq_num]) &&
+ napi_reschedule(napi)) {
+ disable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]);
+ goto restart_poll;
+ }
+ }
+ return frames_processed;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ibmvnic_netpoll_controller(struct net_device *dev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(dev);
+ int i;
+
+ replenish_pools(netdev_priv(dev));
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+}
+#endif
+
+static const struct net_device_ops ibmvnic_netdev_ops = {
+ .ndo_open = ibmvnic_open,
+ .ndo_stop = ibmvnic_close,
+ .ndo_start_xmit = ibmvnic_xmit,
+ .ndo_set_rx_mode = ibmvnic_set_multi,
+ .ndo_set_mac_address = ibmvnic_set_mac,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = ibmvnic_change_mtu,
+ .ndo_tx_timeout = ibmvnic_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ibmvnic_netpoll_controller,
+#endif
+};
+
+/* ethtool functions */
+
+static int ibmvnic_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ cmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
+ SUPPORTED_FIBRE);
+ cmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg |
+ ADVERTISED_FIBRE);
+ ethtool_cmd_speed_set(cmd, SPEED_1000);
+ cmd->duplex = DUPLEX_FULL;
+ cmd->port = PORT_FIBRE;
+ cmd->phy_address = 0;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->autoneg = AUTONEG_ENABLE;
+ cmd->maxtxpkt = 0;
+ cmd->maxrxpkt = 1;
+ return 0;
+}
+
+static void ibmvnic_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, ibmvnic_driver_name, sizeof(info->driver));
+ strlcpy(info->version, IBMVNIC_DRIVER_VERSION, sizeof(info->version));
+}
+
+static u32 ibmvnic_get_msglevel(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ return adapter->msg_enable;
+}
+
+static void ibmvnic_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ adapter->msg_enable = data;
+}
+
+static u32 ibmvnic_get_link(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ /* Don't need to send a query because we request a logical link up at
+ * init and then we wait for link state indications
+ */
+ return adapter->logical_link_state;
+}
+
+static void ibmvnic_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ ring->rx_max_pending = 0;
+ ring->tx_max_pending = 0;
+ ring->rx_mini_max_pending = 0;
+ ring->rx_jumbo_max_pending = 0;
+ ring->rx_pending = 0;
+ ring->tx_pending = 0;
+ ring->rx_mini_pending = 0;
+ ring->rx_jumbo_pending = 0;
+}
+
+static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++, data += ETH_GSTRING_LEN)
+ memcpy(data, ibmvnic_stats[i].name, ETH_GSTRING_LEN);
+}
+
+static int ibmvnic_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(ibmvnic_stats);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void ibmvnic_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(dev);
+ union ibmvnic_crq crq;
+ int i;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_statistics.first = IBMVNIC_CRQ_CMD;
+ crq.request_statistics.cmd = REQUEST_STATISTICS;
+ crq.request_statistics.ioba = cpu_to_be32(adapter->stats_token);
+ crq.request_statistics.len =
+ cpu_to_be32(sizeof(struct ibmvnic_statistics));
+ ibmvnic_send_crq(adapter, &crq);
+
+ /* Wait for data to be written */
+ init_completion(&adapter->stats_done);
+ wait_for_completion(&adapter->stats_done);
+
+ for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++)
+ data[i] = IBMVNIC_GET_STAT(adapter, ibmvnic_stats[i].offset);
+}
+
+static const struct ethtool_ops ibmvnic_ethtool_ops = {
+ .get_settings = ibmvnic_get_settings,
+ .get_drvinfo = ibmvnic_get_drvinfo,
+ .get_msglevel = ibmvnic_get_msglevel,
+ .set_msglevel = ibmvnic_set_msglevel,
+ .get_link = ibmvnic_get_link,
+ .get_ringparam = ibmvnic_get_ringparam,
+ .get_strings = ibmvnic_get_strings,
+ .get_sset_count = ibmvnic_get_sset_count,
+ .get_ethtool_stats = ibmvnic_get_ethtool_stats,
+};
+
+/* Routines for managing CRQs/sCRQs */
+
+static void release_sub_crq_queue(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ netdev_dbg(adapter->netdev, "Releasing sub-CRQ\n");
+
+ /* Close the sub-crqs */
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
+ adapter->vdev->unit_address,
+ scrq->crq_num);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ free_pages((unsigned long)scrq->msgs, 2);
+ kfree(scrq);
+}
+
+static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter
+ *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_sub_crq_queue *scrq;
+ int rc;
+
+ scrq = kmalloc(sizeof(*scrq), GFP_ATOMIC);
+ if (!scrq)
+ return NULL;
+
+ scrq->msgs = (union sub_crq *)__get_free_pages(GFP_KERNEL, 2);
+ memset(scrq->msgs, 0, 4 * PAGE_SIZE);
+ if (!scrq->msgs) {
+ dev_warn(dev, "Couldn't allocate crq queue messages page\n");
+ goto zero_page_failed;
+ }
+
+ scrq->msg_token = dma_map_single(dev, scrq->msgs, 4 * PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, scrq->msg_token)) {
+ dev_warn(dev, "Couldn't map crq queue messages page\n");
+ goto map_failed;
+ }
+
+ rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token,
+ 4 * PAGE_SIZE, &scrq->crq_num, &scrq->hw_irq);
+
+ if (rc == H_RESOURCE)
+ rc = ibmvnic_reset_crq(adapter);
+
+ if (rc == H_CLOSED) {
+ dev_warn(dev, "Partner adapter not ready, waiting.\n");
+ } else if (rc) {
+ dev_warn(dev, "Error %d registering sub-crq\n", rc);
+ goto reg_failed;
+ }
+
+ scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+ if (scrq->irq == NO_IRQ) {
+ dev_err(dev, "Error mapping irq\n");
+ goto map_irq_failed;
+ }
+
+ scrq->adapter = adapter;
+ scrq->size = 4 * PAGE_SIZE / sizeof(*scrq->msgs);
+ scrq->cur = 0;
+ scrq->rx_skb_top = NULL;
+ spin_lock_init(&scrq->lock);
+
+ netdev_dbg(adapter->netdev,
+ "sub-crq initialized, num %lx, hw_irq=%lx, irq=%x\n",
+ scrq->crq_num, scrq->hw_irq, scrq->irq);
+
+ return scrq;
+
+map_irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
+ adapter->vdev->unit_address,
+ scrq->crq_num);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_failed:
+ dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+map_failed:
+ free_pages((unsigned long)scrq->msgs, 2);
+zero_page_failed:
+ kfree(scrq);
+
+ return NULL;
+}
+
+static void release_sub_crqs(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ if (adapter->tx_scrq) {
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ if (adapter->tx_scrq[i]) {
+ free_irq(adapter->tx_scrq[i]->irq,
+ adapter->tx_scrq[i]);
+ release_sub_crq_queue(adapter,
+ adapter->tx_scrq[i]);
+ }
+ adapter->tx_scrq = NULL;
+ }
+
+ if (adapter->rx_scrq) {
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ if (adapter->rx_scrq[i]) {
+ free_irq(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+ release_sub_crq_queue(adapter,
+ adapter->rx_scrq[i]);
+ }
+ adapter->rx_scrq = NULL;
+ }
+
+ adapter->requested_caps = 0;
+}
+
+static int disable_scrq_irq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ unsigned long rc;
+
+ rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address,
+ H_DISABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0);
+ if (rc)
+ dev_err(dev, "Couldn't disable scrq irq 0x%lx. rc=%ld\n",
+ scrq->hw_irq, rc);
+ return rc;
+}
+
+static int enable_scrq_irq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ unsigned long rc;
+
+ if (scrq->hw_irq > 0x100000000ULL) {
+ dev_err(dev, "bad hw_irq = %lx\n", scrq->hw_irq);
+ return 1;
+ }
+
+ rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address,
+ H_ENABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0);
+ if (rc)
+ dev_err(dev, "Couldn't enable scrq irq 0x%lx. rc=%ld\n",
+ scrq->hw_irq, rc);
+ return rc;
+}
+
+static int ibmvnic_complete_tx(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_tx_buff *txbuff;
+ union sub_crq *next;
+ int index;
+ int i, j;
+
+restart_loop:
+ while (pending_scrq(adapter, scrq)) {
+ unsigned int pool = scrq->pool_index;
+
+ next = ibmvnic_next_scrq(adapter, scrq);
+ for (i = 0; i < next->tx_comp.num_comps; i++) {
+ if (next->tx_comp.rcs[i]) {
+ dev_err(dev, "tx error %x\n",
+ next->tx_comp.rcs[i]);
+ continue;
+ }
+ index = be32_to_cpu(next->tx_comp.correlators[i]);
+ txbuff = &adapter->tx_pool[pool].tx_buff[index];
+
+ for (j = 0; j < IBMVNIC_MAX_FRAGS_PER_CRQ; j++) {
+ if (!txbuff->data_dma[j])
+ continue;
+
+ txbuff->data_dma[j] = 0;
+ txbuff->used_bounce = false;
+ }
+
+ if (txbuff->last_frag)
+ dev_kfree_skb_any(txbuff->skb);
+
+ adapter->tx_pool[pool].free_map[adapter->tx_pool[pool].
+ producer_index] = index;
+ adapter->tx_pool[pool].producer_index =
+ (adapter->tx_pool[pool].producer_index + 1) %
+ adapter->max_tx_entries_per_subcrq;
+ }
+ /* remove tx_comp scrq*/
+ next->tx_comp.first = 0;
+ }
+
+ enable_scrq_irq(adapter, scrq);
+
+ if (pending_scrq(adapter, scrq)) {
+ disable_scrq_irq(adapter, scrq);
+ goto restart_loop;
+ }
+
+ return 0;
+}
+
+static irqreturn_t ibmvnic_interrupt_tx(int irq, void *instance)
+{
+ struct ibmvnic_sub_crq_queue *scrq = instance;
+ struct ibmvnic_adapter *adapter = scrq->adapter;
+
+ disable_scrq_irq(adapter, scrq);
+ ibmvnic_complete_tx(adapter, scrq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance)
+{
+ struct ibmvnic_sub_crq_queue *scrq = instance;
+ struct ibmvnic_adapter *adapter = scrq->adapter;
+
+ if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) {
+ disable_scrq_irq(adapter, scrq);
+ __napi_schedule(&adapter->napi[scrq->scrq_num]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_sub_crq_queue **allqueues;
+ int registered_queues = 0;
+ union ibmvnic_crq crq;
+ int total_queues;
+ int more = 0;
+ int i, j;
+ int rc;
+
+ if (!retry) {
+ /* Sub-CRQ entries are 32 byte long */
+ int entries_page = 4 * PAGE_SIZE / (sizeof(u64) * 4);
+
+ if (adapter->min_tx_entries_per_subcrq > entries_page ||
+ adapter->min_rx_add_entries_per_subcrq > entries_page) {
+ dev_err(dev, "Fatal, invalid entries per sub-crq\n");
+ goto allqueues_failed;
+ }
+
+ /* Get the minimum between the queried max and the entries
+ * that fit in our PAGE_SIZE
+ */
+ adapter->req_tx_entries_per_subcrq =
+ adapter->max_tx_entries_per_subcrq > entries_page ?
+ entries_page : adapter->max_tx_entries_per_subcrq;
+ adapter->req_rx_add_entries_per_subcrq =
+ adapter->max_rx_add_entries_per_subcrq > entries_page ?
+ entries_page : adapter->max_rx_add_entries_per_subcrq;
+
+ /* Choosing the maximum number of queues supported by firmware*/
+ adapter->req_tx_queues = adapter->min_tx_queues;
+ adapter->req_rx_queues = adapter->min_rx_queues;
+ adapter->req_rx_add_queues = adapter->min_rx_add_queues;
+
+ adapter->req_mtu = adapter->max_mtu;
+ }
+
+ total_queues = adapter->req_tx_queues + adapter->req_rx_queues;
+
+ allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_ATOMIC);
+ if (!allqueues)
+ goto allqueues_failed;
+
+ for (i = 0; i < total_queues; i++) {
+ allqueues[i] = init_sub_crq_queue(adapter);
+ if (!allqueues[i]) {
+ dev_warn(dev, "Couldn't allocate all sub-crqs\n");
+ break;
+ }
+ registered_queues++;
+ }
+
+ /* Make sure we were able to register the minimum number of queues */
+ if (registered_queues <
+ adapter->min_tx_queues + adapter->min_rx_queues) {
+ dev_err(dev, "Fatal: Couldn't init min number of sub-crqs\n");
+ goto tx_failed;
+ }
+
+ /* Distribute the failed allocated queues*/
+ for (i = 0; i < total_queues - registered_queues + more ; i++) {
+ netdev_dbg(adapter->netdev, "Reducing number of queues\n");
+ switch (i % 3) {
+ case 0:
+ if (adapter->req_rx_queues > adapter->min_rx_queues)
+ adapter->req_rx_queues--;
+ else
+ more++;
+ break;
+ case 1:
+ if (adapter->req_tx_queues > adapter->min_tx_queues)
+ adapter->req_tx_queues--;
+ else
+ more++;
+ break;
+ }
+ }
+
+ adapter->tx_scrq = kcalloc(adapter->req_tx_queues,
+ sizeof(*adapter->tx_scrq), GFP_ATOMIC);
+ if (!adapter->tx_scrq)
+ goto tx_failed;
+
+ for (i = 0; i < adapter->req_tx_queues; i++) {
+ adapter->tx_scrq[i] = allqueues[i];
+ adapter->tx_scrq[i]->pool_index = i;
+ rc = request_irq(adapter->tx_scrq[i]->irq, ibmvnic_interrupt_tx,
+ 0, "ibmvnic_tx", adapter->tx_scrq[i]);
+ if (rc) {
+ dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
+ adapter->tx_scrq[i]->irq, rc);
+ goto req_tx_irq_failed;
+ }
+ }
+
+ adapter->rx_scrq = kcalloc(adapter->req_rx_queues,
+ sizeof(*adapter->rx_scrq), GFP_ATOMIC);
+ if (!adapter->rx_scrq)
+ goto rx_failed;
+
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues];
+ adapter->rx_scrq[i]->scrq_num = i;
+ rc = request_irq(adapter->rx_scrq[i]->irq, ibmvnic_interrupt_rx,
+ 0, "ibmvnic_rx", adapter->rx_scrq[i]);
+ if (rc) {
+ dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n",
+ adapter->rx_scrq[i]->irq, rc);
+ goto req_rx_irq_failed;
+ }
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_capability.first = IBMVNIC_CRQ_CMD;
+ crq.request_capability.cmd = REQUEST_CAPABILITY;
+
+ crq.request_capability.capability = cpu_to_be16(REQ_TX_QUEUES);
+ crq.request_capability.number = cpu_to_be32(adapter->req_tx_queues);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = cpu_to_be16(REQ_RX_QUEUES);
+ crq.request_capability.number = cpu_to_be32(adapter->req_rx_queues);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = cpu_to_be16(REQ_RX_ADD_QUEUES);
+ crq.request_capability.number = cpu_to_be32(adapter->req_rx_add_queues);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability =
+ cpu_to_be16(REQ_TX_ENTRIES_PER_SUBCRQ);
+ crq.request_capability.number =
+ cpu_to_be32(adapter->req_tx_entries_per_subcrq);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability =
+ cpu_to_be16(REQ_RX_ADD_ENTRIES_PER_SUBCRQ);
+ crq.request_capability.number =
+ cpu_to_be32(adapter->req_rx_add_entries_per_subcrq);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = cpu_to_be16(REQ_MTU);
+ crq.request_capability.number = cpu_to_be32(adapter->req_mtu);
+ ibmvnic_send_crq(adapter, &crq);
+
+ if (adapter->netdev->flags & IFF_PROMISC) {
+ if (adapter->promisc_supported) {
+ crq.request_capability.capability =
+ cpu_to_be16(PROMISC_REQUESTED);
+ crq.request_capability.number = cpu_to_be32(1);
+ ibmvnic_send_crq(adapter, &crq);
+ }
+ } else {
+ crq.request_capability.capability =
+ cpu_to_be16(PROMISC_REQUESTED);
+ crq.request_capability.number = cpu_to_be32(0);
+ ibmvnic_send_crq(adapter, &crq);
+ }
+
+ kfree(allqueues);
+
+ return;
+
+req_rx_irq_failed:
+ for (j = 0; j < i; j++)
+ free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
+ i = adapter->req_tx_queues;
+req_tx_irq_failed:
+ for (j = 0; j < i; j++)
+ free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
+ kfree(adapter->rx_scrq);
+ adapter->rx_scrq = NULL;
+rx_failed:
+ kfree(adapter->tx_scrq);
+ adapter->tx_scrq = NULL;
+tx_failed:
+ for (i = 0; i < registered_queues; i++)
+ release_sub_crq_queue(adapter, allqueues[i]);
+ kfree(allqueues);
+allqueues_failed:
+ ibmvnic_remove(adapter->vdev);
+}
+
+static int pending_scrq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ union sub_crq *entry = &scrq->msgs[scrq->cur];
+
+ if (entry->generic.first & IBMVNIC_CRQ_CMD_RSP || adapter->closing)
+ return 1;
+ else
+ return 0;
+}
+
+static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ union sub_crq *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scrq->lock, flags);
+ entry = &scrq->msgs[scrq->cur];
+ if (entry->generic.first & IBMVNIC_CRQ_CMD_RSP) {
+ if (++scrq->cur == scrq->size)
+ scrq->cur = 0;
+ } else {
+ entry = NULL;
+ }
+ spin_unlock_irqrestore(&scrq->lock, flags);
+
+ return entry;
+}
+
+static union ibmvnic_crq *ibmvnic_next_crq(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *queue = &adapter->crq;
+ union ibmvnic_crq *crq;
+
+ crq = &queue->msgs[queue->cur];
+ if (crq->generic.first & IBMVNIC_CRQ_CMD_RSP) {
+ if (++queue->cur == queue->size)
+ queue->cur = 0;
+ } else {
+ crq = NULL;
+ }
+
+ return crq;
+}
+
+static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle,
+ union sub_crq *sub_crq)
+{
+ unsigned int ua = adapter->vdev->unit_address;
+ struct device *dev = &adapter->vdev->dev;
+ u64 *u64_crq = (u64 *)sub_crq;
+ int rc;
+
+ netdev_dbg(adapter->netdev,
+ "Sending sCRQ %016lx: %016lx %016lx %016lx %016lx\n",
+ (unsigned long int)cpu_to_be64(remote_handle),
+ (unsigned long int)cpu_to_be64(u64_crq[0]),
+ (unsigned long int)cpu_to_be64(u64_crq[1]),
+ (unsigned long int)cpu_to_be64(u64_crq[2]),
+ (unsigned long int)cpu_to_be64(u64_crq[3]));
+
+ /* Make sure the hypervisor sees the complete request */
+ mb();
+
+ rc = plpar_hcall_norets(H_SEND_SUB_CRQ, ua,
+ cpu_to_be64(remote_handle),
+ cpu_to_be64(u64_crq[0]),
+ cpu_to_be64(u64_crq[1]),
+ cpu_to_be64(u64_crq[2]),
+ cpu_to_be64(u64_crq[3]));
+
+ if (rc) {
+ if (rc == H_CLOSED)
+ dev_warn(dev, "CRQ Queue closed\n");
+ dev_err(dev, "Send error (rc=%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static int ibmvnic_send_crq(struct ibmvnic_adapter *adapter,
+ union ibmvnic_crq *crq)
+{
+ unsigned int ua = adapter->vdev->unit_address;
+ struct device *dev = &adapter->vdev->dev;
+ u64 *u64_crq = (u64 *)crq;
+ int rc;
+
+ netdev_dbg(adapter->netdev, "Sending CRQ: %016lx %016lx\n",
+ (unsigned long int)cpu_to_be64(u64_crq[0]),
+ (unsigned long int)cpu_to_be64(u64_crq[1]));
+
+ /* Make sure the hypervisor sees the complete request */
+ mb();
+
+ rc = plpar_hcall_norets(H_SEND_CRQ, ua,
+ cpu_to_be64(u64_crq[0]),
+ cpu_to_be64(u64_crq[1]));
+
+ if (rc) {
+ if (rc == H_CLOSED)
+ dev_warn(dev, "CRQ Queue closed\n");
+ dev_warn(dev, "Send error (rc=%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static int ibmvnic_send_crq_init(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.generic.first = IBMVNIC_CRQ_INIT_CMD;
+ crq.generic.cmd = IBMVNIC_CRQ_INIT;
+ netdev_dbg(adapter->netdev, "Sending CRQ init\n");
+
+ return ibmvnic_send_crq(adapter, &crq);
+}
+
+static int ibmvnic_send_crq_init_complete(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.generic.first = IBMVNIC_CRQ_INIT_CMD;
+ crq.generic.cmd = IBMVNIC_CRQ_INIT_COMPLETE;
+ netdev_dbg(adapter->netdev, "Sending CRQ init complete\n");
+
+ return ibmvnic_send_crq(adapter, &crq);
+}
+
+static int send_version_xchg(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.version_exchange.first = IBMVNIC_CRQ_CMD;
+ crq.version_exchange.cmd = VERSION_EXCHANGE;
+ crq.version_exchange.version = cpu_to_be16(ibmvnic_version);
+
+ return ibmvnic_send_crq(adapter, &crq);
+}
+
+static void send_login(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_login_rsp_buffer *login_rsp_buffer;
+ struct ibmvnic_login_buffer *login_buffer;
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ dma_addr_t rsp_buffer_token;
+ dma_addr_t buffer_token;
+ size_t rsp_buffer_size;
+ union ibmvnic_crq crq;
+ unsigned long flags;
+ size_t buffer_size;
+ __be64 *tx_list_p;
+ __be64 *rx_list_p;
+ int i;
+
+ buffer_size =
+ sizeof(struct ibmvnic_login_buffer) +
+ sizeof(u64) * (adapter->req_tx_queues + adapter->req_rx_queues);
+
+ login_buffer = kmalloc(buffer_size, GFP_ATOMIC);
+ if (!login_buffer)
+ goto buf_alloc_failed;
+
+ buffer_token = dma_map_single(dev, login_buffer, buffer_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, buffer_token)) {
+ dev_err(dev, "Couldn't map login buffer\n");
+ goto buf_map_failed;
+ }
+
+ rsp_buffer_size =
+ sizeof(struct ibmvnic_login_rsp_buffer) +
+ sizeof(u64) * (adapter->req_tx_queues +
+ adapter->req_rx_queues *
+ adapter->req_rx_add_queues + adapter->
+ req_rx_add_queues) +
+ sizeof(u8) * (IBMVNIC_TX_DESC_VERSIONS);
+
+ login_rsp_buffer = kmalloc(rsp_buffer_size, GFP_ATOMIC);
+ if (!login_rsp_buffer)
+ goto buf_rsp_alloc_failed;
+
+ rsp_buffer_token = dma_map_single(dev, login_rsp_buffer,
+ rsp_buffer_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, rsp_buffer_token)) {
+ dev_err(dev, "Couldn't map login rsp buffer\n");
+ goto buf_rsp_map_failed;
+ }
+ inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dev_err(dev, "Couldn't allocate inflight_cmd\n");
+ goto inflight_alloc_failed;
+ }
+ adapter->login_buf = login_buffer;
+ adapter->login_buf_token = buffer_token;
+ adapter->login_buf_sz = buffer_size;
+ adapter->login_rsp_buf = login_rsp_buffer;
+ adapter->login_rsp_buf_token = rsp_buffer_token;
+ adapter->login_rsp_buf_sz = rsp_buffer_size;
+
+ login_buffer->len = cpu_to_be32(buffer_size);
+ login_buffer->version = cpu_to_be32(INITIAL_VERSION_LB);
+ login_buffer->num_txcomp_subcrqs = cpu_to_be32(adapter->req_tx_queues);
+ login_buffer->off_txcomp_subcrqs =
+ cpu_to_be32(sizeof(struct ibmvnic_login_buffer));
+ login_buffer->num_rxcomp_subcrqs = cpu_to_be32(adapter->req_rx_queues);
+ login_buffer->off_rxcomp_subcrqs =
+ cpu_to_be32(sizeof(struct ibmvnic_login_buffer) +
+ sizeof(u64) * adapter->req_tx_queues);
+ login_buffer->login_rsp_ioba = cpu_to_be32(rsp_buffer_token);
+ login_buffer->login_rsp_len = cpu_to_be32(rsp_buffer_size);
+
+ tx_list_p = (__be64 *)((char *)login_buffer +
+ sizeof(struct ibmvnic_login_buffer));
+ rx_list_p = (__be64 *)((char *)login_buffer +
+ sizeof(struct ibmvnic_login_buffer) +
+ sizeof(u64) * adapter->req_tx_queues);
+
+ for (i = 0; i < adapter->req_tx_queues; i++) {
+ if (adapter->tx_scrq[i]) {
+ tx_list_p[i] = cpu_to_be64(adapter->tx_scrq[i]->
+ crq_num);
+ }
+ }
+
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ if (adapter->rx_scrq[i]) {
+ rx_list_p[i] = cpu_to_be64(adapter->rx_scrq[i]->
+ crq_num);
+ }
+ }
+
+ netdev_dbg(adapter->netdev, "Login Buffer:\n");
+ for (i = 0; i < (adapter->login_buf_sz - 1) / 8 + 1; i++) {
+ netdev_dbg(adapter->netdev, "%016lx\n",
+ ((unsigned long int *)(adapter->login_buf))[i]);
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.login.first = IBMVNIC_CRQ_CMD;
+ crq.login.cmd = LOGIN;
+ crq.login.ioba = cpu_to_be32(buffer_token);
+ crq.login.len = cpu_to_be32(buffer_size);
+
+ memcpy(&inflight_cmd->crq, &crq, sizeof(crq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ ibmvnic_send_crq(adapter, &crq);
+
+ return;
+
+inflight_alloc_failed:
+ dma_unmap_single(dev, rsp_buffer_token, rsp_buffer_size,
+ DMA_FROM_DEVICE);
+buf_rsp_map_failed:
+ kfree(login_rsp_buffer);
+buf_rsp_alloc_failed:
+ dma_unmap_single(dev, buffer_token, buffer_size, DMA_TO_DEVICE);
+buf_map_failed:
+ kfree(login_buffer);
+buf_alloc_failed:
+ return;
+}
+
+static void send_request_map(struct ibmvnic_adapter *adapter, dma_addr_t addr,
+ u32 len, u8 map_id)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_map.first = IBMVNIC_CRQ_CMD;
+ crq.request_map.cmd = REQUEST_MAP;
+ crq.request_map.map_id = map_id;
+ crq.request_map.ioba = cpu_to_be32(addr);
+ crq.request_map.len = cpu_to_be32(len);
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void send_request_unmap(struct ibmvnic_adapter *adapter, u8 map_id)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_unmap.first = IBMVNIC_CRQ_CMD;
+ crq.request_unmap.cmd = REQUEST_UNMAP;
+ crq.request_unmap.map_id = map_id;
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void send_map_query(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.query_map.first = IBMVNIC_CRQ_CMD;
+ crq.query_map.cmd = QUERY_MAP;
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+/* Send a series of CRQs requesting various capabilities of the VNIC server */
+static void send_cap_queries(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ atomic_set(&adapter->running_cap_queries, 0);
+ memset(&crq, 0, sizeof(crq));
+ crq.query_capability.first = IBMVNIC_CRQ_CMD;
+ crq.query_capability.cmd = QUERY_CAPABILITY;
+
+ crq.query_capability.capability = cpu_to_be16(MIN_TX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MIN_RX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MIN_RX_ADD_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_TX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_RX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_RX_ADD_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MIN_TX_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MIN_RX_ADD_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MAX_TX_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MAX_RX_ADD_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(TCP_IP_OFFLOAD);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(PROMISC_SUPPORTED);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MIN_MTU);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_MTU);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_MULTICAST_FILTERS);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(VLAN_HEADER_INSERTION);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_TX_SG_ENTRIES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(RX_SG_SUPPORTED);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(OPT_TX_COMP_SUB_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(OPT_RX_COMP_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(OPT_RX_BUFADD_Q_PER_RX_COMP_Q);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(OPT_TX_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(OPT_RXBA_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(TX_RX_DESC_REQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void handle_query_ip_offload_rsp(struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_query_ip_offload_buffer *buf = &adapter->ip_offload_buf;
+ union ibmvnic_crq crq;
+ int i;
+
+ dma_unmap_single(dev, adapter->ip_offload_tok,
+ sizeof(adapter->ip_offload_buf), DMA_FROM_DEVICE);
+
+ netdev_dbg(adapter->netdev, "Query IP Offload Buffer:\n");
+ for (i = 0; i < (sizeof(adapter->ip_offload_buf) - 1) / 8 + 1; i++)
+ netdev_dbg(adapter->netdev, "%016lx\n",
+ ((unsigned long int *)(buf))[i]);
+
+ netdev_dbg(adapter->netdev, "ipv4_chksum = %d\n", buf->ipv4_chksum);
+ netdev_dbg(adapter->netdev, "ipv6_chksum = %d\n", buf->ipv6_chksum);
+ netdev_dbg(adapter->netdev, "tcp_ipv4_chksum = %d\n",
+ buf->tcp_ipv4_chksum);
+ netdev_dbg(adapter->netdev, "tcp_ipv6_chksum = %d\n",
+ buf->tcp_ipv6_chksum);
+ netdev_dbg(adapter->netdev, "udp_ipv4_chksum = %d\n",
+ buf->udp_ipv4_chksum);
+ netdev_dbg(adapter->netdev, "udp_ipv6_chksum = %d\n",
+ buf->udp_ipv6_chksum);
+ netdev_dbg(adapter->netdev, "large_tx_ipv4 = %d\n",
+ buf->large_tx_ipv4);
+ netdev_dbg(adapter->netdev, "large_tx_ipv6 = %d\n",
+ buf->large_tx_ipv6);
+ netdev_dbg(adapter->netdev, "large_rx_ipv4 = %d\n",
+ buf->large_rx_ipv4);
+ netdev_dbg(adapter->netdev, "large_rx_ipv6 = %d\n",
+ buf->large_rx_ipv6);
+ netdev_dbg(adapter->netdev, "max_ipv4_hdr_sz = %d\n",
+ buf->max_ipv4_header_size);
+ netdev_dbg(adapter->netdev, "max_ipv6_hdr_sz = %d\n",
+ buf->max_ipv6_header_size);
+ netdev_dbg(adapter->netdev, "max_tcp_hdr_size = %d\n",
+ buf->max_tcp_header_size);
+ netdev_dbg(adapter->netdev, "max_udp_hdr_size = %d\n",
+ buf->max_udp_header_size);
+ netdev_dbg(adapter->netdev, "max_large_tx_size = %d\n",
+ buf->max_large_tx_size);
+ netdev_dbg(adapter->netdev, "max_large_rx_size = %d\n",
+ buf->max_large_rx_size);
+ netdev_dbg(adapter->netdev, "ipv6_ext_hdr = %d\n",
+ buf->ipv6_extension_header);
+ netdev_dbg(adapter->netdev, "tcp_pseudosum_req = %d\n",
+ buf->tcp_pseudosum_req);
+ netdev_dbg(adapter->netdev, "num_ipv6_ext_hd = %d\n",
+ buf->num_ipv6_ext_headers);
+ netdev_dbg(adapter->netdev, "off_ipv6_ext_hd = %d\n",
+ buf->off_ipv6_ext_headers);
+
+ adapter->ip_offload_ctrl_tok =
+ dma_map_single(dev, &adapter->ip_offload_ctrl,
+ sizeof(adapter->ip_offload_ctrl), DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->ip_offload_ctrl_tok)) {
+ dev_err(dev, "Couldn't map ip offload control buffer\n");
+ return;
+ }
+
+ adapter->ip_offload_ctrl.version = cpu_to_be32(INITIAL_VERSION_IOB);
+ adapter->ip_offload_ctrl.tcp_ipv4_chksum = buf->tcp_ipv4_chksum;
+ adapter->ip_offload_ctrl.udp_ipv4_chksum = buf->udp_ipv4_chksum;
+ adapter->ip_offload_ctrl.tcp_ipv6_chksum = buf->tcp_ipv6_chksum;
+ adapter->ip_offload_ctrl.udp_ipv6_chksum = buf->udp_ipv6_chksum;
+
+ /* large_tx/rx disabled for now, additional features needed */
+ adapter->ip_offload_ctrl.large_tx_ipv4 = 0;
+ adapter->ip_offload_ctrl.large_tx_ipv6 = 0;
+ adapter->ip_offload_ctrl.large_rx_ipv4 = 0;
+ adapter->ip_offload_ctrl.large_rx_ipv6 = 0;
+
+ adapter->netdev->features = NETIF_F_GSO;
+
+ if (buf->tcp_ipv4_chksum || buf->udp_ipv4_chksum)
+ adapter->netdev->features |= NETIF_F_IP_CSUM;
+
+ if (buf->tcp_ipv6_chksum || buf->udp_ipv6_chksum)
+ adapter->netdev->features |= NETIF_F_IPV6_CSUM;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ip_offload.first = IBMVNIC_CRQ_CMD;
+ crq.control_ip_offload.cmd = CONTROL_IP_OFFLOAD;
+ crq.control_ip_offload.len =
+ cpu_to_be32(sizeof(adapter->ip_offload_ctrl));
+ crq.control_ip_offload.ioba = cpu_to_be32(adapter->ip_offload_ctrl_tok);
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void handle_error_info_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_error_buff *error_buff;
+ unsigned long flags;
+ bool found = false;
+ int i;
+
+ if (!crq->request_error_rsp.rc.code) {
+ dev_info(dev, "Request Error Rsp returned with rc=%x\n",
+ crq->request_error_rsp.rc.code);
+ return;
+ }
+
+ spin_lock_irqsave(&adapter->error_list_lock, flags);
+ list_for_each_entry(error_buff, &adapter->errors, list)
+ if (error_buff->error_id == crq->request_error_rsp.error_id) {
+ found = true;
+ list_del(&error_buff->list);
+ break;
+ }
+ spin_unlock_irqrestore(&adapter->error_list_lock, flags);
+
+ if (!found) {
+ dev_err(dev, "Couldn't find error id %x\n",
+ crq->request_error_rsp.error_id);
+ return;
+ }
+
+ dev_err(dev, "Detailed info for error id %x:",
+ crq->request_error_rsp.error_id);
+
+ for (i = 0; i < error_buff->len; i++) {
+ pr_cont("%02x", (int)error_buff->buff[i]);
+ if (i % 8 == 7)
+ pr_cont(" ");
+ }
+ pr_cont("\n");
+
+ dma_unmap_single(dev, error_buff->dma, error_buff->len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ kfree(error_buff);
+}
+
+static void handle_dump_size_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ int len = be32_to_cpu(crq->request_dump_size_rsp.len);
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq newcrq;
+ unsigned long flags;
+
+ /* allocate and map buffer */
+ adapter->dump_data = kmalloc(len, GFP_KERNEL);
+ if (!adapter->dump_data) {
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ adapter->dump_data_token = dma_map_single(dev, adapter->dump_data, len,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->dump_data_token)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map dump data\n");
+ kfree(adapter->dump_data);
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dma_unmap_single(dev, adapter->dump_data_token, len,
+ DMA_FROM_DEVICE);
+ kfree(adapter->dump_data);
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.request_dump.first = IBMVNIC_CRQ_CMD;
+ newcrq.request_dump.cmd = REQUEST_DUMP;
+ newcrq.request_dump.ioba = cpu_to_be32(adapter->dump_data_token);
+ newcrq.request_dump.len = cpu_to_be32(adapter->dump_data_size);
+
+ memcpy(&inflight_cmd->crq, &newcrq, sizeof(newcrq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ ibmvnic_send_crq(adapter, &newcrq);
+}
+
+static void handle_error_indication(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ int detail_len = be32_to_cpu(crq->error_indication.detail_error_sz);
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_error_buff *error_buff;
+ union ibmvnic_crq new_crq;
+ unsigned long flags;
+
+ dev_err(dev, "Firmware reports %serror id %x, cause %d\n",
+ crq->error_indication.
+ flags & IBMVNIC_FATAL_ERROR ? "FATAL " : "",
+ crq->error_indication.error_id,
+ crq->error_indication.error_cause);
+
+ error_buff = kmalloc(sizeof(*error_buff), GFP_ATOMIC);
+ if (!error_buff)
+ return;
+
+ error_buff->buff = kmalloc(detail_len, GFP_ATOMIC);
+ if (!error_buff->buff) {
+ kfree(error_buff);
+ return;
+ }
+
+ error_buff->dma = dma_map_single(dev, error_buff->buff, detail_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, error_buff->dma)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map error buffer\n");
+ kfree(error_buff->buff);
+ kfree(error_buff);
+ return;
+ }
+
+ inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dma_unmap_single(dev, error_buff->dma, detail_len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ kfree(error_buff);
+ return;
+ }
+
+ error_buff->len = detail_len;
+ error_buff->error_id = crq->error_indication.error_id;
+
+ spin_lock_irqsave(&adapter->error_list_lock, flags);
+ list_add_tail(&error_buff->list, &adapter->errors);
+ spin_unlock_irqrestore(&adapter->error_list_lock, flags);
+
+ memset(&new_crq, 0, sizeof(new_crq));
+ new_crq.request_error_info.first = IBMVNIC_CRQ_CMD;
+ new_crq.request_error_info.cmd = REQUEST_ERROR_INFO;
+ new_crq.request_error_info.ioba = cpu_to_be32(error_buff->dma);
+ new_crq.request_error_info.len = cpu_to_be32(detail_len);
+ new_crq.request_error_info.error_id = crq->error_indication.error_id;
+
+ memcpy(&inflight_cmd->crq, &crq, sizeof(crq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ ibmvnic_send_crq(adapter, &new_crq);
+}
+
+static void handle_change_mac_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ rc = crq->change_mac_addr_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in CHANGE_MAC_ADDR_RSP\n", rc);
+ return;
+ }
+ memcpy(netdev->dev_addr, &crq->change_mac_addr_rsp.mac_addr[0],
+ ETH_ALEN);
+}
+
+static void handle_request_cap_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ u64 *req_value;
+ char *name;
+
+ switch (be16_to_cpu(crq->request_capability_rsp.capability)) {
+ case REQ_TX_QUEUES:
+ req_value = &adapter->req_tx_queues;
+ name = "tx";
+ break;
+ case REQ_RX_QUEUES:
+ req_value = &adapter->req_rx_queues;
+ name = "rx";
+ break;
+ case REQ_RX_ADD_QUEUES:
+ req_value = &adapter->req_rx_add_queues;
+ name = "rx_add";
+ break;
+ case REQ_TX_ENTRIES_PER_SUBCRQ:
+ req_value = &adapter->req_tx_entries_per_subcrq;
+ name = "tx_entries_per_subcrq";
+ break;
+ case REQ_RX_ADD_ENTRIES_PER_SUBCRQ:
+ req_value = &adapter->req_rx_add_entries_per_subcrq;
+ name = "rx_add_entries_per_subcrq";
+ break;
+ case REQ_MTU:
+ req_value = &adapter->req_mtu;
+ name = "mtu";
+ break;
+ case PROMISC_REQUESTED:
+ req_value = &adapter->promisc;
+ name = "promisc";
+ break;
+ default:
+ dev_err(dev, "Got invalid cap request rsp %d\n",
+ crq->request_capability.capability);
+ return;
+ }
+
+ switch (crq->request_capability_rsp.rc.code) {
+ case SUCCESS:
+ break;
+ case PARTIALSUCCESS:
+ dev_info(dev, "req=%lld, rsp=%ld in %s queue, retrying.\n",
+ *req_value,
+ (long int)be32_to_cpu(crq->request_capability_rsp.
+ number), name);
+ release_sub_crqs(adapter);
+ *req_value = be32_to_cpu(crq->request_capability_rsp.number);
+ complete(&adapter->init_done);
+ return;
+ default:
+ dev_err(dev, "Error %d in request cap rsp\n",
+ crq->request_capability_rsp.rc.code);
+ return;
+ }
+
+ /* Done receiving requested capabilities, query IP offload support */
+ if (++adapter->requested_caps == 7) {
+ union ibmvnic_crq newcrq;
+ int buf_sz = sizeof(struct ibmvnic_query_ip_offload_buffer);
+ struct ibmvnic_query_ip_offload_buffer *ip_offload_buf =
+ &adapter->ip_offload_buf;
+
+ adapter->ip_offload_tok = dma_map_single(dev, ip_offload_buf,
+ buf_sz,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->ip_offload_tok)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map offload buffer\n");
+ return;
+ }
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.query_ip_offload.first = IBMVNIC_CRQ_CMD;
+ newcrq.query_ip_offload.cmd = QUERY_IP_OFFLOAD;
+ newcrq.query_ip_offload.len = cpu_to_be32(buf_sz);
+ newcrq.query_ip_offload.ioba =
+ cpu_to_be32(adapter->ip_offload_tok);
+
+ ibmvnic_send_crq(adapter, &newcrq);
+ }
+}
+
+static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf;
+ struct ibmvnic_login_buffer *login = adapter->login_buf;
+ union ibmvnic_crq crq;
+ int i;
+
+ dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(dev, adapter->login_rsp_buf_token,
+ adapter->login_rsp_buf_sz, DMA_BIDIRECTIONAL);
+
+ netdev_dbg(adapter->netdev, "Login Response Buffer:\n");
+ for (i = 0; i < (adapter->login_rsp_buf_sz - 1) / 8 + 1; i++) {
+ netdev_dbg(adapter->netdev, "%016lx\n",
+ ((unsigned long int *)(adapter->login_rsp_buf))[i]);
+ }
+
+ /* Sanity checks */
+ if (login->num_txcomp_subcrqs != login_rsp->num_txsubm_subcrqs ||
+ (be32_to_cpu(login->num_rxcomp_subcrqs) *
+ adapter->req_rx_add_queues !=
+ be32_to_cpu(login_rsp->num_rxadd_subcrqs))) {
+ dev_err(dev, "FATAL: Inconsistent login and login rsp\n");
+ ibmvnic_remove(adapter->vdev);
+ return -EIO;
+ }
+ complete(&adapter->init_done);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_ras_comp_num.first = IBMVNIC_CRQ_CMD;
+ crq.request_ras_comp_num.cmd = REQUEST_RAS_COMP_NUM;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return 0;
+}
+
+static void handle_request_map_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ u8 map_id = crq->request_map_rsp.map_id;
+ int tx_subcrqs;
+ int rx_subcrqs;
+ long rc;
+ int i;
+
+ tx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
+ rx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+
+ rc = crq->request_map_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in REQUEST_MAP_RSP\n", rc);
+ adapter->map_id--;
+ /* need to find and zero tx/rx_pool map_id */
+ for (i = 0; i < tx_subcrqs; i++) {
+ if (adapter->tx_pool[i].long_term_buff.map_id == map_id)
+ adapter->tx_pool[i].long_term_buff.map_id = 0;
+ }
+ for (i = 0; i < rx_subcrqs; i++) {
+ if (adapter->rx_pool[i].long_term_buff.map_id == map_id)
+ adapter->rx_pool[i].long_term_buff.map_id = 0;
+ }
+ }
+ complete(&adapter->fw_done);
+}
+
+static void handle_request_unmap_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ rc = crq->request_unmap_rsp.rc.code;
+ if (rc)
+ dev_err(dev, "Error %ld in REQUEST_UNMAP_RSP\n", rc);
+}
+
+static void handle_query_map_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ rc = crq->query_map_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in QUERY_MAP_RSP\n", rc);
+ return;
+ }
+ netdev_dbg(netdev, "page_size = %d\ntot_pages = %d\nfree_pages = %d\n",
+ crq->query_map_rsp.page_size, crq->query_map_rsp.tot_pages,
+ crq->query_map_rsp.free_pages);
+}
+
+static void handle_query_cap_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ atomic_dec(&adapter->running_cap_queries);
+ netdev_dbg(netdev, "Outstanding queries: %d\n",
+ atomic_read(&adapter->running_cap_queries));
+ rc = crq->query_capability.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in QUERY_CAP_RSP\n", rc);
+ goto out;
+ }
+
+ switch (be16_to_cpu(crq->query_capability.capability)) {
+ case MIN_TX_QUEUES:
+ adapter->min_tx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_tx_queues = %lld\n",
+ adapter->min_tx_queues);
+ break;
+ case MIN_RX_QUEUES:
+ adapter->min_rx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_rx_queues = %lld\n",
+ adapter->min_rx_queues);
+ break;
+ case MIN_RX_ADD_QUEUES:
+ adapter->min_rx_add_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_rx_add_queues = %lld\n",
+ adapter->min_rx_add_queues);
+ break;
+ case MAX_TX_QUEUES:
+ adapter->max_tx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_tx_queues = %lld\n",
+ adapter->max_tx_queues);
+ break;
+ case MAX_RX_QUEUES:
+ adapter->max_rx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_rx_queues = %lld\n",
+ adapter->max_rx_queues);
+ break;
+ case MAX_RX_ADD_QUEUES:
+ adapter->max_rx_add_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_rx_add_queues = %lld\n",
+ adapter->max_rx_add_queues);
+ break;
+ case MIN_TX_ENTRIES_PER_SUBCRQ:
+ adapter->min_tx_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_tx_entries_per_subcrq = %lld\n",
+ adapter->min_tx_entries_per_subcrq);
+ break;
+ case MIN_RX_ADD_ENTRIES_PER_SUBCRQ:
+ adapter->min_rx_add_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_rx_add_entrs_per_subcrq = %lld\n",
+ adapter->min_rx_add_entries_per_subcrq);
+ break;
+ case MAX_TX_ENTRIES_PER_SUBCRQ:
+ adapter->max_tx_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_tx_entries_per_subcrq = %lld\n",
+ adapter->max_tx_entries_per_subcrq);
+ break;
+ case MAX_RX_ADD_ENTRIES_PER_SUBCRQ:
+ adapter->max_rx_add_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_rx_add_entrs_per_subcrq = %lld\n",
+ adapter->max_rx_add_entries_per_subcrq);
+ break;
+ case TCP_IP_OFFLOAD:
+ adapter->tcp_ip_offload =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "tcp_ip_offload = %lld\n",
+ adapter->tcp_ip_offload);
+ break;
+ case PROMISC_SUPPORTED:
+ adapter->promisc_supported =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "promisc_supported = %lld\n",
+ adapter->promisc_supported);
+ break;
+ case MIN_MTU:
+ adapter->min_mtu = be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_mtu = %lld\n", adapter->min_mtu);
+ break;
+ case MAX_MTU:
+ adapter->max_mtu = be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_mtu = %lld\n", adapter->max_mtu);
+ break;
+ case MAX_MULTICAST_FILTERS:
+ adapter->max_multicast_filters =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_multicast_filters = %lld\n",
+ adapter->max_multicast_filters);
+ break;
+ case VLAN_HEADER_INSERTION:
+ adapter->vlan_header_insertion =
+ be32_to_cpu(crq->query_capability.number);
+ if (adapter->vlan_header_insertion)
+ netdev->features |= NETIF_F_HW_VLAN_STAG_TX;
+ netdev_dbg(netdev, "vlan_header_insertion = %lld\n",
+ adapter->vlan_header_insertion);
+ break;
+ case MAX_TX_SG_ENTRIES:
+ adapter->max_tx_sg_entries =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_tx_sg_entries = %lld\n",
+ adapter->max_tx_sg_entries);
+ break;
+ case RX_SG_SUPPORTED:
+ adapter->rx_sg_supported =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "rx_sg_supported = %lld\n",
+ adapter->rx_sg_supported);
+ break;
+ case OPT_TX_COMP_SUB_QUEUES:
+ adapter->opt_tx_comp_sub_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_tx_comp_sub_queues = %lld\n",
+ adapter->opt_tx_comp_sub_queues);
+ break;
+ case OPT_RX_COMP_QUEUES:
+ adapter->opt_rx_comp_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_rx_comp_queues = %lld\n",
+ adapter->opt_rx_comp_queues);
+ break;
+ case OPT_RX_BUFADD_Q_PER_RX_COMP_Q:
+ adapter->opt_rx_bufadd_q_per_rx_comp_q =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_rx_bufadd_q_per_rx_comp_q = %lld\n",
+ adapter->opt_rx_bufadd_q_per_rx_comp_q);
+ break;
+ case OPT_TX_ENTRIES_PER_SUBCRQ:
+ adapter->opt_tx_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_tx_entries_per_subcrq = %lld\n",
+ adapter->opt_tx_entries_per_subcrq);
+ break;
+ case OPT_RXBA_ENTRIES_PER_SUBCRQ:
+ adapter->opt_rxba_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_rxba_entries_per_subcrq = %lld\n",
+ adapter->opt_rxba_entries_per_subcrq);
+ break;
+ case TX_RX_DESC_REQ:
+ adapter->tx_rx_desc_req = crq->query_capability.number;
+ netdev_dbg(netdev, "tx_rx_desc_req = %llx\n",
+ adapter->tx_rx_desc_req);
+ break;
+
+ default:
+ netdev_err(netdev, "Got invalid cap rsp %d\n",
+ crq->query_capability.capability);
+ }
+
+out:
+ if (atomic_read(&adapter->running_cap_queries) == 0)
+ complete(&adapter->init_done);
+ /* We're done querying the capabilities, initialize sub-crqs */
+}
+
+static void handle_control_ras_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ u8 correlator = crq->control_ras_rsp.correlator;
+ struct device *dev = &adapter->vdev->dev;
+ bool found = false;
+ int i;
+
+ if (crq->control_ras_rsp.rc.code) {
+ dev_warn(dev, "Control ras failed rc=%d\n",
+ crq->control_ras_rsp.rc.code);
+ return;
+ }
+
+ for (i = 0; i < adapter->ras_comp_num; i++) {
+ if (adapter->ras_comps[i].correlator == correlator) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_warn(dev, "Correlator not found on control_ras_rsp\n");
+ return;
+ }
+
+ switch (crq->control_ras_rsp.op) {
+ case IBMVNIC_TRACE_LEVEL:
+ adapter->ras_comps[i].trace_level = crq->control_ras.level;
+ break;
+ case IBMVNIC_ERROR_LEVEL:
+ adapter->ras_comps[i].error_check_level =
+ crq->control_ras.level;
+ break;
+ case IBMVNIC_TRACE_PAUSE:
+ adapter->ras_comp_int[i].paused = 1;
+ break;
+ case IBMVNIC_TRACE_RESUME:
+ adapter->ras_comp_int[i].paused = 0;
+ break;
+ case IBMVNIC_TRACE_ON:
+ adapter->ras_comps[i].trace_on = 1;
+ break;
+ case IBMVNIC_TRACE_OFF:
+ adapter->ras_comps[i].trace_on = 0;
+ break;
+ case IBMVNIC_CHG_TRACE_BUFF_SZ:
+ /* trace_buff_sz is 3 bytes, stuff it into an int */
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[0] = 0;
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[1] =
+ crq->control_ras_rsp.trace_buff_sz[0];
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[2] =
+ crq->control_ras_rsp.trace_buff_sz[1];
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[3] =
+ crq->control_ras_rsp.trace_buff_sz[2];
+ break;
+ default:
+ dev_err(dev, "invalid op %d on control_ras_rsp",
+ crq->control_ras_rsp.op);
+ }
+}
+
+static int ibmvnic_fw_comp_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t trace_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_fw_trace_entry *trace;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ dma_addr_t trace_tok;
+
+ if (*ppos >= be32_to_cpu(adapter->ras_comps[num].trace_buff_size))
+ return 0;
+
+ trace =
+ dma_alloc_coherent(dev,
+ be32_to_cpu(adapter->ras_comps[num].
+ trace_buff_size), &trace_tok,
+ GFP_KERNEL);
+ if (!trace) {
+ dev_err(dev, "Couldn't alloc trace buffer\n");
+ return 0;
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.collect_fw_trace.first = IBMVNIC_CRQ_CMD;
+ crq.collect_fw_trace.cmd = COLLECT_FW_TRACE;
+ crq.collect_fw_trace.correlator = adapter->ras_comps[num].correlator;
+ crq.collect_fw_trace.ioba = cpu_to_be32(trace_tok);
+ crq.collect_fw_trace.len = adapter->ras_comps[num].trace_buff_size;
+ ibmvnic_send_crq(adapter, &crq);
+
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+
+ if (*ppos + len > be32_to_cpu(adapter->ras_comps[num].trace_buff_size))
+ len =
+ be32_to_cpu(adapter->ras_comps[num].trace_buff_size) -
+ *ppos;
+
+ copy_to_user(user_buf, &((u8 *)trace)[*ppos], len);
+
+ dma_free_coherent(dev,
+ be32_to_cpu(adapter->ras_comps[num].trace_buff_size),
+ trace, trace_tok);
+ *ppos += len;
+ return len;
+}
+
+static const struct file_operations trace_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = trace_read,
+};
+
+static ssize_t paused_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* 1 or 0 plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comp_int[num].paused);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t paused_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ adapter->ras_comp_int[num].paused = val ? 1 : 0;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = val ? IBMVNIC_TRACE_PAUSE : IBMVNIC_TRACE_RESUME;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations paused_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = paused_read,
+ .write = paused_write,
+};
+
+static ssize_t tracing_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* 1 or 0 plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_on);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t tracing_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = val ? IBMVNIC_TRACE_ON : IBMVNIC_TRACE_OFF;
+
+ return len;
+}
+
+static const struct file_operations tracing_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = tracing_read,
+ .write = tracing_write,
+};
+
+static ssize_t error_level_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* decimal max char plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].error_check_level);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t error_level_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ if (val > 9)
+ val = 9;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = IBMVNIC_ERROR_LEVEL;
+ crq.control_ras.level = val;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations error_level_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = error_level_read,
+ .write = error_level_write,
+};
+
+static ssize_t trace_level_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* decimal max char plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_level);
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t trace_level_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+ if (val > 9)
+ val = 9;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator =
+ adapter->ras_comps[ras_comp_int->num].correlator;
+ crq.control_ras.op = IBMVNIC_TRACE_LEVEL;
+ crq.control_ras.level = val;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations trace_level_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = trace_level_read,
+ .write = trace_level_write,
+};
+
+static ssize_t trace_buff_size_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_buff_size);
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t trace_buff_size_write(struct file *file,
+ const char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator =
+ adapter->ras_comps[ras_comp_int->num].correlator;
+ crq.control_ras.op = IBMVNIC_CHG_TRACE_BUFF_SZ;
+ /* trace_buff_sz is 3 bytes, stuff an int into it */
+ crq.control_ras.trace_buff_sz[0] = ((u8 *)(&val))[5];
+ crq.control_ras.trace_buff_sz[1] = ((u8 *)(&val))[6];
+ crq.control_ras.trace_buff_sz[2] = ((u8 *)(&val))[7];
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations trace_size_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = trace_buff_size_read,
+ .write = trace_buff_size_write,
+};
+
+static void handle_request_ras_comps_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct dentry *dir_ent;
+ struct dentry *ent;
+ int i;
+
+ debugfs_remove_recursive(adapter->ras_comps_ent);
+
+ adapter->ras_comps_ent = debugfs_create_dir("ras_comps",
+ adapter->debugfs_dir);
+ if (!adapter->ras_comps_ent || IS_ERR(adapter->ras_comps_ent)) {
+ dev_info(dev, "debugfs create ras_comps dir failed\n");
+ return;
+ }
+
+ for (i = 0; i < adapter->ras_comp_num; i++) {
+ dir_ent = debugfs_create_dir(adapter->ras_comps[i].name,
+ adapter->ras_comps_ent);
+ if (!dir_ent || IS_ERR(dir_ent)) {
+ dev_info(dev, "debugfs create %s dir failed\n",
+ adapter->ras_comps[i].name);
+ continue;
+ }
+
+ adapter->ras_comp_int[i].adapter = adapter;
+ adapter->ras_comp_int[i].num = i;
+ adapter->ras_comp_int[i].desc_blob.data =
+ &adapter->ras_comps[i].description;
+ adapter->ras_comp_int[i].desc_blob.size =
+ sizeof(adapter->ras_comps[i].description);
+
+ /* Don't need to remember the dentry's because the debugfs dir
+ * gets removed recursively
+ */
+ ent = debugfs_create_blob("description", S_IRUGO, dir_ent,
+ &adapter->ras_comp_int[i].desc_blob);
+ ent = debugfs_create_file("trace_buf_size", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_size_ops);
+ ent = debugfs_create_file("trace_level",
+ S_IRUGO |
+ (adapter->ras_comps[i].trace_level !=
+ 0xFF ? S_IWUSR : 0),
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_level_ops);
+ ent = debugfs_create_file("error_level",
+ S_IRUGO |
+ (adapter->
+ ras_comps[i].error_check_level !=
+ 0xFF ? S_IWUSR : 0),
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_level_ops);
+ ent = debugfs_create_file("tracing", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &tracing_ops);
+ ent = debugfs_create_file("paused", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &paused_ops);
+ ent = debugfs_create_file("trace", S_IRUGO, dir_ent,
+ &adapter->ras_comp_int[i],
+ &trace_ops);
+ }
+}
+
+static void handle_request_ras_comp_num_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ int len = adapter->ras_comp_num * sizeof(struct ibmvnic_fw_component);
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq newcrq;
+
+ adapter->ras_comps = dma_alloc_coherent(dev, len,
+ &adapter->ras_comps_tok,
+ GFP_KERNEL);
+ if (!adapter->ras_comps) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't alloc fw comps buffer\n");
+ return;
+ }
+
+ adapter->ras_comp_int = kmalloc(adapter->ras_comp_num *
+ sizeof(struct ibmvnic_fw_comp_internal),
+ GFP_KERNEL);
+ if (!adapter->ras_comp_int)
+ dma_free_coherent(dev, len, adapter->ras_comps,
+ adapter->ras_comps_tok);
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.request_ras_comps.first = IBMVNIC_CRQ_CMD;
+ newcrq.request_ras_comps.cmd = REQUEST_RAS_COMPS;
+ newcrq.request_ras_comps.ioba = cpu_to_be32(adapter->ras_comps_tok);
+ newcrq.request_ras_comps.len = cpu_to_be32(len);
+ ibmvnic_send_crq(adapter, &newcrq);
+}
+
+static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_error_buff *error_buff;
+ unsigned long flags;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_for_each_entry(inflight_cmd, &adapter->inflight, list) {
+ switch (inflight_cmd->crq.generic.cmd) {
+ case LOGIN:
+ dma_unmap_single(dev, adapter->login_buf_token,
+ adapter->login_buf_sz,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(dev, adapter->login_rsp_buf_token,
+ adapter->login_rsp_buf_sz,
+ DMA_BIDIRECTIONAL);
+ kfree(adapter->login_rsp_buf);
+ kfree(adapter->login_buf);
+ break;
+ case REQUEST_DUMP:
+ complete(&adapter->fw_done);
+ break;
+ case REQUEST_ERROR_INFO:
+ spin_lock_irqsave(&adapter->error_list_lock, flags2);
+ list_for_each_entry(error_buff, &adapter->errors,
+ list) {
+ dma_unmap_single(dev, error_buff->dma,
+ error_buff->len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ list_del(&error_buff->list);
+ kfree(error_buff);
+ }
+ spin_unlock_irqrestore(&adapter->error_list_lock,
+ flags2);
+ break;
+ }
+ list_del(&inflight_cmd->list);
+ kfree(inflight_cmd);
+ }
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+}
+
+static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_generic_crq *gen_crq = &crq->generic;
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ netdev_dbg(netdev, "Handling CRQ: %016lx %016lx\n",
+ ((unsigned long int *)crq)[0],
+ ((unsigned long int *)crq)[1]);
+ switch (gen_crq->first) {
+ case IBMVNIC_CRQ_INIT_RSP:
+ switch (gen_crq->cmd) {
+ case IBMVNIC_CRQ_INIT:
+ dev_info(dev, "Partner initialized\n");
+ /* Send back a response */
+ rc = ibmvnic_send_crq_init_complete(adapter);
+ if (rc == 0)
+ send_version_xchg(adapter);
+ else
+ dev_err(dev, "Can't send initrsp rc=%ld\n", rc);
+ break;
+ case IBMVNIC_CRQ_INIT_COMPLETE:
+ dev_info(dev, "Partner initialization complete\n");
+ send_version_xchg(adapter);
+ break;
+ default:
+ dev_err(dev, "Unknown crq cmd: %d\n", gen_crq->cmd);
+ }
+ return;
+ case IBMVNIC_CRQ_XPORT_EVENT:
+ if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) {
+ dev_info(dev, "Re-enabling adapter\n");
+ adapter->migrated = true;
+ ibmvnic_free_inflight(adapter);
+ release_sub_crqs(adapter);
+ rc = ibmvnic_reenable_crq_queue(adapter);
+ if (rc)
+ dev_err(dev, "Error after enable rc=%ld\n", rc);
+ adapter->migrated = false;
+ rc = ibmvnic_send_crq_init(adapter);
+ if (rc)
+ dev_err(dev, "Error sending init rc=%ld\n", rc);
+ } else {
+ /* The adapter lost the connection */
+ dev_err(dev, "Virtual Adapter failed (rc=%d)\n",
+ gen_crq->cmd);
+ ibmvnic_free_inflight(adapter);
+ release_sub_crqs(adapter);
+ }
+ return;
+ case IBMVNIC_CRQ_CMD_RSP:
+ break;
+ default:
+ dev_err(dev, "Got an invalid msg type 0x%02x\n",
+ gen_crq->first);
+ return;
+ }
+
+ switch (gen_crq->cmd) {
+ case VERSION_EXCHANGE_RSP:
+ rc = crq->version_exchange_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in VERSION_EXCHG_RSP\n", rc);
+ break;
+ }
+ dev_info(dev, "Partner protocol version is %d\n",
+ crq->version_exchange_rsp.version);
+ if (be16_to_cpu(crq->version_exchange_rsp.version) <
+ ibmvnic_version)
+ ibmvnic_version =
+ be16_to_cpu(crq->version_exchange_rsp.version);
+ send_cap_queries(adapter);
+ break;
+ case QUERY_CAPABILITY_RSP:
+ handle_query_cap_rsp(crq, adapter);
+ break;
+ case QUERY_MAP_RSP:
+ handle_query_map_rsp(crq, adapter);
+ break;
+ case REQUEST_MAP_RSP:
+ handle_request_map_rsp(crq, adapter);
+ break;
+ case REQUEST_UNMAP_RSP:
+ handle_request_unmap_rsp(crq, adapter);
+ break;
+ case REQUEST_CAPABILITY_RSP:
+ handle_request_cap_rsp(crq, adapter);
+ break;
+ case LOGIN_RSP:
+ netdev_dbg(netdev, "Got Login Response\n");
+ handle_login_rsp(crq, adapter);
+ break;
+ case LOGICAL_LINK_STATE_RSP:
+ netdev_dbg(netdev, "Got Logical Link State Response\n");
+ adapter->logical_link_state =
+ crq->logical_link_state_rsp.link_state;
+ break;
+ case LINK_STATE_INDICATION:
+ netdev_dbg(netdev, "Got Logical Link State Indication\n");
+ adapter->phys_link_state =
+ crq->link_state_indication.phys_link_state;
+ adapter->logical_link_state =
+ crq->link_state_indication.logical_link_state;
+ break;
+ case CHANGE_MAC_ADDR_RSP:
+ netdev_dbg(netdev, "Got MAC address change Response\n");
+ handle_change_mac_rsp(crq, adapter);
+ break;
+ case ERROR_INDICATION:
+ netdev_dbg(netdev, "Got Error Indication\n");
+ handle_error_indication(crq, adapter);
+ break;
+ case REQUEST_ERROR_RSP:
+ netdev_dbg(netdev, "Got Error Detail Response\n");
+ handle_error_info_rsp(crq, adapter);
+ break;
+ case REQUEST_STATISTICS_RSP:
+ netdev_dbg(netdev, "Got Statistics Response\n");
+ complete(&adapter->stats_done);
+ break;
+ case REQUEST_DUMP_SIZE_RSP:
+ netdev_dbg(netdev, "Got Request Dump Size Response\n");
+ handle_dump_size_rsp(crq, adapter);
+ break;
+ case REQUEST_DUMP_RSP:
+ netdev_dbg(netdev, "Got Request Dump Response\n");
+ complete(&adapter->fw_done);
+ break;
+ case QUERY_IP_OFFLOAD_RSP:
+ netdev_dbg(netdev, "Got Query IP offload Response\n");
+ handle_query_ip_offload_rsp(adapter);
+ break;
+ case MULTICAST_CTRL_RSP:
+ netdev_dbg(netdev, "Got multicast control Response\n");
+ break;
+ case CONTROL_IP_OFFLOAD_RSP:
+ netdev_dbg(netdev, "Got Control IP offload Response\n");
+ dma_unmap_single(dev, adapter->ip_offload_ctrl_tok,
+ sizeof(adapter->ip_offload_ctrl),
+ DMA_TO_DEVICE);
+ /* We're done with the queries, perform the login */
+ send_login(adapter);
+ break;
+ case REQUEST_RAS_COMP_NUM_RSP:
+ netdev_dbg(netdev, "Got Request RAS Comp Num Response\n");
+ if (crq->request_ras_comp_num_rsp.rc.code == 10) {
+ netdev_dbg(netdev, "Request RAS Comp Num not supported\n");
+ break;
+ }
+ adapter->ras_comp_num =
+ be32_to_cpu(crq->request_ras_comp_num_rsp.num_components);
+ handle_request_ras_comp_num_rsp(crq, adapter);
+ break;
+ case REQUEST_RAS_COMPS_RSP:
+ netdev_dbg(netdev, "Got Request RAS Comps Response\n");
+ handle_request_ras_comps_rsp(crq, adapter);
+ break;
+ case CONTROL_RAS_RSP:
+ netdev_dbg(netdev, "Got Control RAS Response\n");
+ handle_control_ras_rsp(crq, adapter);
+ break;
+ case COLLECT_FW_TRACE_RSP:
+ netdev_dbg(netdev, "Got Collect firmware trace Response\n");
+ complete(&adapter->fw_done);
+ break;
+ default:
+ netdev_err(netdev, "Got an invalid cmd type 0x%02x\n",
+ gen_crq->cmd);
+ }
+}
+
+static irqreturn_t ibmvnic_interrupt(int irq, void *instance)
+{
+ struct ibmvnic_adapter *adapter = instance;
+ struct ibmvnic_crq_queue *queue = &adapter->crq;
+ struct vio_dev *vdev = adapter->vdev;
+ union ibmvnic_crq *crq;
+ unsigned long flags;
+ bool done = false;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ vio_disable_interrupts(vdev);
+ while (!done) {
+ /* Pull all the valid messages off the CRQ */
+ while ((crq = ibmvnic_next_crq(adapter)) != NULL) {
+ ibmvnic_handle_crq(crq, adapter);
+ crq->generic.first = 0;
+ }
+ vio_enable_interrupts(vdev);
+ crq = ibmvnic_next_crq(adapter);
+ if (crq) {
+ vio_disable_interrupts(vdev);
+ ibmvnic_handle_crq(crq, adapter);
+ crq->generic.first = 0;
+ } else {
+ done = true;
+ }
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *adapter)
+{
+ struct vio_dev *vdev = adapter->vdev;
+ int rc;
+
+ do {
+ rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
+ } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ if (rc)
+ dev_err(&vdev->dev, "Error enabling adapter (rc=%d)\n", rc);
+
+ return rc;
+}
+
+static int ibmvnic_reset_crq(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *crq = &adapter->crq;
+ struct device *dev = &adapter->vdev->dev;
+ struct vio_dev *vdev = adapter->vdev;
+ int rc;
+
+ /* Close the CRQ */
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ /* Clean out the queue */
+ memset(crq->msgs, 0, PAGE_SIZE);
+ crq->cur = 0;
+
+ /* And re-open it again */
+ rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+ crq->msg_token, PAGE_SIZE);
+
+ if (rc == H_CLOSED)
+ /* Adapter is good, but other end is not ready */
+ dev_warn(dev, "Partner adapter not ready\n");
+ else if (rc != 0)
+ dev_warn(dev, "Couldn't register crq (rc=%d)\n", rc);
+
+ return rc;
+}
+
+static void ibmvnic_release_crq_queue(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *crq = &adapter->crq;
+ struct vio_dev *vdev = adapter->vdev;
+ long rc;
+
+ netdev_dbg(adapter->netdev, "Releasing CRQ\n");
+ free_irq(vdev->irq, adapter);
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ dma_unmap_single(&vdev->dev, crq->msg_token, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ free_page((unsigned long)crq->msgs);
+}
+
+static int ibmvnic_init_crq_queue(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *crq = &adapter->crq;
+ struct device *dev = &adapter->vdev->dev;
+ struct vio_dev *vdev = adapter->vdev;
+ int rc, retrc = -ENOMEM;
+
+ crq->msgs = (union ibmvnic_crq *)get_zeroed_page(GFP_KERNEL);
+ /* Should we allocate more than one page? */
+
+ if (!crq->msgs)
+ return -ENOMEM;
+
+ crq->size = PAGE_SIZE / sizeof(*crq->msgs);
+ crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, crq->msg_token))
+ goto map_failed;
+
+ rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+ crq->msg_token, PAGE_SIZE);
+
+ if (rc == H_RESOURCE)
+ /* maybe kexecing and resource is busy. try a reset */
+ rc = ibmvnic_reset_crq(adapter);
+ retrc = rc;
+
+ if (rc == H_CLOSED) {
+ dev_warn(dev, "Partner adapter not ready\n");
+ } else if (rc) {
+ dev_warn(dev, "Error %d opening adapter\n", rc);
+ goto reg_crq_failed;
+ }
+
+ retrc = 0;
+
+ netdev_dbg(adapter->netdev, "registering irq 0x%x\n", vdev->irq);
+ rc = request_irq(vdev->irq, ibmvnic_interrupt, 0, IBMVNIC_NAME,
+ adapter);
+ if (rc) {
+ dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n",
+ vdev->irq, rc);
+ goto req_irq_failed;
+ }
+
+ rc = vio_enable_interrupts(vdev);
+ if (rc) {
+ dev_err(dev, "Error %d enabling interrupts\n", rc);
+ goto req_irq_failed;
+ }
+
+ crq->cur = 0;
+ spin_lock_init(&crq->lock);
+
+ return retrc;
+
+req_irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_crq_failed:
+ dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+map_failed:
+ free_page((unsigned long)crq->msgs);
+ return retrc;
+}
+
+/* debugfs for dump */
+static int ibmvnic_dump_show(struct seq_file *seq, void *v)
+{
+ struct net_device *netdev = seq->private;
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_dump_size.first = IBMVNIC_CRQ_CMD;
+ crq.request_dump_size.cmd = REQUEST_DUMP_SIZE;
+ ibmvnic_send_crq(adapter, &crq);
+
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+
+ seq_write(seq, adapter->dump_data, adapter->dump_data_size);
+
+ dma_unmap_single(dev, adapter->dump_data_token, adapter->dump_data_size,
+ DMA_BIDIRECTIONAL);
+
+ kfree(adapter->dump_data);
+
+ return 0;
+}
+
+static int ibmvnic_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ibmvnic_dump_show, inode->i_private);
+}
+
+static const struct file_operations ibmvnic_dump_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
+{
+ struct ibmvnic_adapter *adapter;
+ struct net_device *netdev;
+ unsigned char *mac_addr_p;
+ struct dentry *ent;
+ char buf[16]; /* debugfs name buf */
+ int rc;
+
+ dev_dbg(&dev->dev, "entering ibmvnic_probe for UA 0x%x\n",
+ dev->unit_address);
+
+ mac_addr_p = (unsigned char *)vio_get_attribute(dev,
+ VETH_MAC_ADDR, NULL);
+ if (!mac_addr_p) {
+ dev_err(&dev->dev,
+ "(%s:%3.3d) ERROR: Can't find MAC_ADDR attribute\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ netdev = alloc_etherdev_mq(sizeof(struct ibmvnic_adapter),
+ IBMVNIC_MAX_TX_QUEUES);
+ if (!netdev)
+ return -ENOMEM;
+
+ adapter = netdev_priv(netdev);
+ dev_set_drvdata(&dev->dev, netdev);
+ adapter->vdev = dev;
+ adapter->netdev = netdev;
+
+ ether_addr_copy(adapter->mac_addr, mac_addr_p);
+ ether_addr_copy(netdev->dev_addr, adapter->mac_addr);
+ netdev->irq = dev->irq;
+ netdev->netdev_ops = &ibmvnic_netdev_ops;
+ netdev->ethtool_ops = &ibmvnic_ethtool_ops;
+ SET_NETDEV_DEV(netdev, &dev->dev);
+
+ spin_lock_init(&adapter->stats_lock);
+
+ rc = ibmvnic_init_crq_queue(adapter);
+ if (rc) {
+ dev_err(&dev->dev, "Couldn't initialize crq. rc=%d\n", rc);
+ goto free_netdev;
+ }
+
+ INIT_LIST_HEAD(&adapter->errors);
+ INIT_LIST_HEAD(&adapter->inflight);
+ spin_lock_init(&adapter->error_list_lock);
+ spin_lock_init(&adapter->inflight_lock);
+
+ adapter->stats_token = dma_map_single(&dev->dev, &adapter->stats,
+ sizeof(struct ibmvnic_statistics),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->dev, adapter->stats_token)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(&dev->dev, "Couldn't map stats buffer\n");
+ goto free_crq;
+ }
+
+ snprintf(buf, sizeof(buf), "ibmvnic_%x", dev->unit_address);
+ ent = debugfs_create_dir(buf, NULL);
+ if (!ent || IS_ERR(ent)) {
+ dev_info(&dev->dev, "debugfs create directory failed\n");
+ adapter->debugfs_dir = NULL;
+ } else {
+ adapter->debugfs_dir = ent;
+ ent = debugfs_create_file("dump", S_IRUGO, adapter->debugfs_dir,
+ netdev, &ibmvnic_dump_ops);
+ if (!ent || IS_ERR(ent)) {
+ dev_info(&dev->dev,
+ "debugfs create dump file failed\n");
+ adapter->debugfs_dump = NULL;
+ } else {
+ adapter->debugfs_dump = ent;
+ }
+ }
+ ibmvnic_send_crq_init(adapter);
+
+ init_completion(&adapter->init_done);
+ wait_for_completion(&adapter->init_done);
+
+ /* needed to pull init_sub_crqs outside of an interrupt context
+ * because it creates IRQ mappings for the subCRQ queues, causing
+ * a kernel warning
+ */
+ init_sub_crqs(adapter, 0);
+
+ reinit_completion(&adapter->init_done);
+ wait_for_completion(&adapter->init_done);
+
+ /* if init_sub_crqs is partially successful, retry */
+ while (!adapter->tx_scrq || !adapter->rx_scrq) {
+ init_sub_crqs(adapter, 1);
+
+ reinit_completion(&adapter->init_done);
+ wait_for_completion(&adapter->init_done);
+ }
+
+ netdev->real_num_tx_queues = adapter->req_tx_queues;
+
+ rc = register_netdev(netdev);
+ if (rc) {
+ dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc);
+ goto free_debugfs;
+ }
+ dev_info(&dev->dev, "ibmvnic registered\n");
+
+ return 0;
+
+free_debugfs:
+ if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
+ debugfs_remove_recursive(adapter->debugfs_dir);
+free_crq:
+ ibmvnic_release_crq_queue(adapter);
+free_netdev:
+ free_netdev(netdev);
+ return rc;
+}
+
+static int ibmvnic_remove(struct vio_dev *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(&dev->dev);
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ unregister_netdev(netdev);
+
+ release_sub_crqs(adapter);
+
+ ibmvnic_release_crq_queue(adapter);
+
+ if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
+ debugfs_remove_recursive(adapter->debugfs_dir);
+
+ if (adapter->ras_comps)
+ dma_free_coherent(&dev->dev,
+ adapter->ras_comp_num *
+ sizeof(struct ibmvnic_fw_component),
+ adapter->ras_comps, adapter->ras_comps_tok);
+
+ kfree(adapter->ras_comp_int);
+
+ free_netdev(netdev);
+ dev_set_drvdata(&dev->dev, NULL);
+
+ return 0;
+}
+
+static unsigned long ibmvnic_get_desired_dma(struct vio_dev *vdev)
+{
+ struct net_device *netdev = dev_get_drvdata(&vdev->dev);
+ struct ibmvnic_adapter *adapter;
+ struct iommu_table *tbl;
+ unsigned long ret = 0;
+ int i;
+
+ tbl = get_iommu_table_base(&vdev->dev);
+
+ /* netdev inits at probe time along with the structures we need below*/
+ if (!netdev)
+ return IOMMU_PAGE_ALIGN(IBMVNIC_IO_ENTITLEMENT_DEFAULT, tbl);
+
+ adapter = netdev_priv(netdev);
+
+ ret += PAGE_SIZE; /* the crq message queue */
+ ret += adapter->bounce_buffer_size;
+ ret += IOMMU_PAGE_ALIGN(sizeof(struct ibmvnic_statistics), tbl);
+
+ for (i = 0; i < adapter->req_tx_queues + adapter->req_rx_queues; i++)
+ ret += 4 * PAGE_SIZE; /* the scrq message queue */
+
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ i++)
+ ret += adapter->rx_pool[i].size *
+ IOMMU_PAGE_ALIGN(adapter->rx_pool[i].buff_size, tbl);
+
+ return ret;
+}
+
+static int ibmvnic_resume(struct device *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(dev);
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int i;
+
+ /* kick the interrupt handlers just in case we lost an interrupt */
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+
+ return 0;
+}
+
+static struct vio_device_id ibmvnic_device_table[] = {
+ {"network", "IBM,vnic"},
+ {"", "" }
+};
+MODULE_DEVICE_TABLE(vio, ibmvnic_device_table);
+
+static const struct dev_pm_ops ibmvnic_pm_ops = {
+ .resume = ibmvnic_resume
+};
+
+static struct vio_driver ibmvnic_driver = {
+ .id_table = ibmvnic_device_table,
+ .probe = ibmvnic_probe,
+ .remove = ibmvnic_remove,
+ .get_desired_dma = ibmvnic_get_desired_dma,
+ .name = ibmvnic_driver_name,
+ .pm = &ibmvnic_pm_ops,
+};
+
+/* module functions */
+static int __init ibmvnic_module_init(void)
+{
+ pr_info("%s: %s %s\n", ibmvnic_driver_name, ibmvnic_driver_string,
+ IBMVNIC_DRIVER_VERSION);
+
+ return vio_register_driver(&ibmvnic_driver);
+}
+
+static void __exit ibmvnic_module_exit(void)
+{
+ vio_unregister_driver(&ibmvnic_driver);
+}
+
+module_init(ibmvnic_module_init);
+module_exit(ibmvnic_module_exit);
diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h
new file mode 100644
index 000000000000..1242925ad34c
--- /dev/null
+++ b/drivers/net/ethernet/ibm/ibmvnic.h
@@ -0,0 +1,1046 @@
+/**************************************************************************/
+/* */
+/* IBM System i and System p Virtual NIC Device Driver */
+/* Copyright (C) 2014 IBM Corp. */
+/* Santiago Leon (santi_leon@yahoo.com) */
+/* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */
+/* John Allen (jallen@linux.vnet.ibm.com) */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. */
+/* */
+/* This module contains the implementation of a virtual ethernet device */
+/* for use with IBM i/pSeries LPAR Linux. It utilizes the logical LAN */
+/* option of the RS/6000 Platform Architecture to interface with virtual */
+/* ethernet NICs that are presented to the partition by the hypervisor. */
+/* */
+/**************************************************************************/
+
+#define IBMVNIC_NAME "ibmvnic"
+#define IBMVNIC_DRIVER_VERSION "1.0"
+#define IBMVNIC_INVALID_MAP -1
+#define IBMVNIC_STATS_TIMEOUT 1
+/* basic structures plus 100 2k buffers */
+#define IBMVNIC_IO_ENTITLEMENT_DEFAULT 610305
+
+/* Initial module_parameters */
+#define IBMVNIC_RX_WEIGHT 16
+/* when changing this, update IBMVNIC_IO_ENTITLEMENT_DEFAULT */
+#define IBMVNIC_BUFFS_PER_POOL 100
+#define IBMVNIC_MAX_TX_QUEUES 5
+
+struct ibmvnic_login_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_LB 1
+ __be32 num_txcomp_subcrqs;
+ __be32 off_txcomp_subcrqs;
+ __be32 num_rxcomp_subcrqs;
+ __be32 off_rxcomp_subcrqs;
+ __be32 login_rsp_ioba;
+ __be32 login_rsp_len;
+} __packed __aligned(8);
+
+struct ibmvnic_login_rsp_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_LRB 1
+ __be32 num_txsubm_subcrqs;
+ __be32 off_txsubm_subcrqs;
+ __be32 num_rxadd_subcrqs;
+ __be32 off_rxadd_subcrqs;
+ __be32 off_rxadd_buff_size;
+ __be32 num_supp_tx_desc;
+ __be32 off_supp_tx_desc;
+} __packed __aligned(8);
+
+struct ibmvnic_query_ip_offload_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 ipv4_chksum;
+ u8 ipv6_chksum;
+ u8 tcp_ipv4_chksum;
+ u8 tcp_ipv6_chksum;
+ u8 udp_ipv4_chksum;
+ u8 udp_ipv6_chksum;
+ u8 large_tx_ipv4;
+ u8 large_tx_ipv6;
+ u8 large_rx_ipv4;
+ u8 large_rx_ipv6;
+ u8 reserved1[14];
+ __be16 max_ipv4_header_size;
+ __be16 max_ipv6_header_size;
+ __be16 max_tcp_header_size;
+ __be16 max_udp_header_size;
+ __be32 max_large_tx_size;
+ __be32 max_large_rx_size;
+ u8 reserved2[16];
+ u8 ipv6_extension_header;
+#define IPV6_EH_NOT_SUPPORTED 0x00
+#define IPV6_EH_SUPPORTED_LIM 0x01
+#define IPV6_EH_SUPPORTED 0xFF
+ u8 tcp_pseudosum_req;
+#define TCP_PS_NOT_REQUIRED 0x00
+#define TCP_PS_REQUIRED 0x01
+ u8 reserved3[30];
+ __be16 num_ipv6_ext_headers;
+ __be32 off_ipv6_ext_headers;
+ u8 reserved4[154];
+} __packed __aligned(8);
+
+struct ibmvnic_control_ip_offload_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 ipv4_chksum;
+ u8 ipv6_chksum;
+ u8 tcp_ipv4_chksum;
+ u8 tcp_ipv6_chksum;
+ u8 udp_ipv4_chksum;
+ u8 udp_ipv6_chksum;
+ u8 large_tx_ipv4;
+ u8 large_tx_ipv6;
+ u8 bad_packet_rx;
+ u8 large_rx_ipv4;
+ u8 large_rx_ipv6;
+ u8 reserved4[111];
+} __packed __aligned(8);
+
+struct ibmvnic_fw_component {
+ u8 name[48];
+ __be32 trace_buff_size;
+ u8 correlator;
+ u8 trace_level;
+ u8 parent_correlator;
+ u8 error_check_level;
+ u8 trace_on;
+ u8 reserved[7];
+ u8 description[192];
+} __packed __aligned(8);
+
+struct ibmvnic_fw_trace_entry {
+ __be32 trace_id;
+ u8 num_valid_data;
+ u8 reserved[3];
+ __be64 pmc_registers;
+ __be64 timebase;
+ __be64 trace_data[5];
+} __packed __aligned(8);
+
+struct ibmvnic_statistics {
+ __be32 version;
+ __be32 promiscuous;
+ __be64 rx_packets;
+ __be64 rx_bytes;
+ __be64 tx_packets;
+ __be64 tx_bytes;
+ __be64 ucast_tx_packets;
+ __be64 ucast_rx_packets;
+ __be64 mcast_tx_packets;
+ __be64 mcast_rx_packets;
+ __be64 bcast_tx_packets;
+ __be64 bcast_rx_packets;
+ __be64 align_errors;
+ __be64 fcs_errors;
+ __be64 single_collision_frames;
+ __be64 multi_collision_frames;
+ __be64 sqe_test_errors;
+ __be64 deferred_tx;
+ __be64 late_collisions;
+ __be64 excess_collisions;
+ __be64 internal_mac_tx_errors;
+ __be64 carrier_sense;
+ __be64 too_long_frames;
+ __be64 internal_mac_rx_errors;
+ u8 reserved[72];
+} __packed __aligned(8);
+
+struct ibmvnic_acl_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 mac_acls_restrict;
+ u8 vlan_acls_restrict;
+ u8 reserved1[22];
+ __be32 num_mac_addrs;
+ __be32 offset_mac_addrs;
+ __be32 num_vlan_ids;
+ __be32 offset_vlan_ids;
+ u8 reserved2[80];
+} __packed __aligned(8);
+
+/* descriptors have been changed, how should this be defined? 1? 4? */
+
+#define IBMVNIC_TX_DESC_VERSIONS 3
+
+/* is this still needed? */
+struct ibmvnic_tx_comp_desc {
+ u8 first;
+ u8 num_comps;
+ __be16 rcs[5];
+ __be32 correlators[5];
+} __packed __aligned(8);
+
+/* some flags that included in v0 descriptor, which is gone
+ * only used for IBMVNIC_TCP_CHKSUM and IBMVNIC_UDP_CHKSUM
+ * and only in some offload_flags variable that doesn't seem
+ * to be used anywhere, can probably be removed?
+ */
+
+#define IBMVNIC_TCP_CHKSUM 0x20
+#define IBMVNIC_UDP_CHKSUM 0x08
+
+#define IBMVNIC_MAX_FRAGS_PER_CRQ 3
+
+struct ibmvnic_tx_desc {
+ u8 first;
+ u8 type;
+
+#define IBMVNIC_TX_DESC 0x10
+ u8 n_crq_elem;
+ u8 n_sge;
+ u8 flags1;
+#define IBMVNIC_TX_COMP_NEEDED 0x80
+#define IBMVNIC_TX_CHKSUM_OFFLOAD 0x40
+#define IBMVNIC_TX_LSO 0x20
+#define IBMVNIC_TX_PROT_TCP 0x10
+#define IBMVNIC_TX_PROT_UDP 0x08
+#define IBMVNIC_TX_PROT_IPV4 0x04
+#define IBMVNIC_TX_PROT_IPV6 0x02
+#define IBMVNIC_TX_VLAN_PRESENT 0x01
+ u8 flags2;
+#define IBMVNIC_TX_VLAN_INSERT 0x80
+ __be16 mss;
+ u8 reserved[4];
+ __be32 correlator;
+ __be16 vlan_id;
+ __be16 dma_reg;
+ __be32 sge_len;
+ __be64 ioba;
+} __packed __aligned(8);
+
+struct ibmvnic_hdr_desc {
+ u8 first;
+ u8 type;
+#define IBMVNIC_HDR_DESC 0x11
+ u8 len;
+ u8 l2_len;
+ __be16 l3_len;
+ u8 l4_len;
+ u8 flag;
+ u8 data[24];
+} __packed __aligned(8);
+
+struct ibmvnic_hdr_ext_desc {
+ u8 first;
+ u8 type;
+#define IBMVNIC_HDR_EXT_DESC 0x12
+ u8 len;
+ u8 data[29];
+} __packed __aligned(8);
+
+struct ibmvnic_sge_desc {
+ u8 first;
+ u8 type;
+#define IBMVNIC_SGE_DESC 0x30
+ __be16 sge1_dma_reg;
+ __be32 sge1_len;
+ __be64 sge1_ioba;
+ __be16 reserved;
+ __be16 sge2_dma_reg;
+ __be32 sge2_len;
+ __be64 sge2_ioba;
+} __packed __aligned(8);
+
+struct ibmvnic_rx_comp_desc {
+ u8 first;
+ u8 flags;
+#define IBMVNIC_IP_CHKSUM_GOOD 0x80
+#define IBMVNIC_TCP_UDP_CHKSUM_GOOD 0x40
+#define IBMVNIC_END_FRAME 0x20
+#define IBMVNIC_EXACT_MC 0x10
+#define IBMVNIC_VLAN_STRIPPED 0x08
+ __be16 off_frame_data;
+ __be32 len;
+ __be64 correlator;
+ __be16 vlan_tci;
+ __be16 rc;
+ u8 reserved[12];
+} __packed __aligned(8);
+
+struct ibmvnic_generic_scrq {
+ u8 first;
+ u8 reserved[31];
+} __packed __aligned(8);
+
+struct ibmvnic_rx_buff_add_desc {
+ u8 first;
+ u8 reserved[7];
+ __be64 correlator;
+ __be32 ioba;
+ u8 map_id;
+ __be32 len:24;
+ u8 reserved2[8];
+} __packed __aligned(8);
+
+struct ibmvnic_rc {
+ u8 code; /* one of enum ibmvnic_rc_codes */
+ u8 detailed_data[3];
+} __packed __aligned(4);
+
+struct ibmvnic_generic_crq {
+ u8 first;
+ u8 cmd;
+ u8 params[10];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_version_exchange {
+ u8 first;
+ u8 cmd;
+ __be16 version;
+#define IBMVNIC_INITIAL_VERSION 1
+ u8 reserved[8];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_capability {
+ u8 first;
+ u8 cmd;
+ __be16 capability; /* one of ibmvnic_capabilities */
+ struct ibmvnic_rc rc;
+ __be32 number; /*FIX: should be __be64, but I'm getting the least
+ * significant word first
+ */
+} __packed __aligned(8);
+
+struct ibmvnic_login {
+ u8 first;
+ u8 cmd;
+ u8 reserved[6];
+ __be32 ioba;
+ __be32 len;
+} __packed __aligned(8);
+
+struct ibmvnic_phys_parms {
+ u8 first;
+ u8 cmd;
+ u8 flags1;
+#define IBMVNIC_EXTERNAL_LOOPBACK 0x80
+#define IBMVNIC_INTERNAL_LOOPBACK 0x40
+#define IBMVNIC_PROMISC 0x20
+#define IBMVNIC_PHYS_LINK_ACTIVE 0x10
+#define IBMVNIC_AUTONEG_DUPLEX 0x08
+#define IBMVNIC_FULL_DUPLEX 0x04
+#define IBMVNIC_HALF_DUPLEX 0x02
+#define IBMVNIC_CAN_CHG_PHYS_PARMS 0x01
+ u8 flags2;
+#define IBMVNIC_LOGICAL_LNK_ACTIVE 0x80
+ __be32 speed;
+#define IBMVNIC_AUTONEG 0x80
+#define IBMVNIC_10MBPS 0x40
+#define IBMVNIC_100MBPS 0x20
+#define IBMVNIC_1GBPS 0x10
+#define IBMVNIC_10GBPS 0x08
+ __be32 mtu;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_logical_link_state {
+ u8 first;
+ u8 cmd;
+ u8 link_state;
+#define IBMVNIC_LOGICAL_LNK_DN 0x00
+#define IBMVNIC_LOGICAL_LNK_UP 0x01
+#define IBMVNIC_LOGICAL_LNK_QUERY 0xff
+ u8 reserved[9];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_query_ip_offload {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 len;
+ __be32 ioba;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_control_ip_offload {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_dump_size {
+ u8 first;
+ u8 cmd;
+ u8 reserved[6];
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_dump {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_dump_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved[6];
+ __be32 dumped_len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_ras_comp_num {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 num_components;
+ u8 reserved2[4];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_ras_comps {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_control_ras {
+ u8 first;
+ u8 cmd;
+ u8 correlator;
+ u8 level;
+ u8 op;
+#define IBMVNIC_TRACE_LEVEL 1
+#define IBMVNIC_ERROR_LEVEL 2
+#define IBMVNIC_TRACE_PAUSE 3
+#define IBMVNIC_TRACE_RESUME 4
+#define IBMVNIC_TRACE_ON 5
+#define IBMVNIC_TRACE_OFF 6
+#define IBMVNIC_CHG_TRACE_BUFF_SZ 7
+ u8 trace_buff_sz[3];
+ u8 reserved[4];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_collect_fw_trace {
+ u8 first;
+ u8 cmd;
+ u8 correlator;
+ u8 reserved;
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_statistics {
+ u8 first;
+ u8 cmd;
+ u8 flags;
+#define IBMVNIC_PHYSICAL_PORT 0x80
+ u8 reserved1;
+ __be32 ioba;
+ __be32 len;
+ u8 reserved[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_debug_stats {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_error_indication {
+ u8 first;
+ u8 cmd;
+ u8 flags;
+#define IBMVNIC_FATAL_ERROR 0x80
+ u8 reserved1;
+ __be32 error_id;
+ __be32 detail_error_sz;
+ __be16 error_cause;
+ u8 reserved2[2];
+} __packed __aligned(8);
+
+struct ibmvnic_request_error_info {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ __be32 error_id;
+} __packed __aligned(8);
+
+struct ibmvnic_request_error_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 error_id;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_link_state_indication {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ u8 phys_link_state;
+ u8 logical_link_state;
+ u8 reserved2[10];
+} __packed __aligned(8);
+
+struct ibmvnic_change_mac_addr {
+ u8 first;
+ u8 cmd;
+ u8 mac_addr[6];
+ struct ibmvnic_rc rc;
+ u8 reserved[4];
+} __packed __aligned(8);
+
+struct ibmvnic_multicast_ctrl {
+ u8 first;
+ u8 cmd;
+ u8 mac_addr[6];
+ u8 flags;
+#define IBMVNIC_ENABLE_MC 0x80
+#define IBMVNIC_DISABLE_MC 0x40
+#define IBMVNIC_ENABLE_ALL 0x20
+#define IBMVNIC_DISABLE_ALL 0x10
+ u8 reserved1;
+ __be16 reserved2; /* was num_enabled_mc_addr; */
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_get_vpd_size_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be64 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_get_vpd {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved[4];
+} __packed __aligned(8);
+
+struct ibmvnic_acl_change_indication {
+ u8 first;
+ u8 cmd;
+ __be16 change_type;
+#define IBMVNIC_MAC_ACL 0
+#define IBMVNIC_VLAN_ACL 1
+ u8 reserved[12];
+} __packed __aligned(8);
+
+struct ibmvnic_acl_query {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_tune {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_map {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_map_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ u8 reserved2[4];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_unmap {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ u8 reserved2[12];
+} __packed __aligned(8);
+
+struct ibmvnic_request_unmap_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ u8 reserved2[8];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_query_map {
+ u8 first;
+ u8 cmd;
+ u8 reserved[14];
+} __packed __aligned(8);
+
+struct ibmvnic_query_map_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved;
+ u8 page_size;
+ __be32 tot_pages;
+ __be32 free_pages;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+union ibmvnic_crq {
+ struct ibmvnic_generic_crq generic;
+ struct ibmvnic_version_exchange version_exchange;
+ struct ibmvnic_version_exchange version_exchange_rsp;
+ struct ibmvnic_capability query_capability;
+ struct ibmvnic_capability query_capability_rsp;
+ struct ibmvnic_capability request_capability;
+ struct ibmvnic_capability request_capability_rsp;
+ struct ibmvnic_login login;
+ struct ibmvnic_generic_crq login_rsp;
+ struct ibmvnic_phys_parms query_phys_parms;
+ struct ibmvnic_phys_parms query_phys_parms_rsp;
+ struct ibmvnic_phys_parms query_phys_capabilities;
+ struct ibmvnic_phys_parms query_phys_capabilities_rsp;
+ struct ibmvnic_phys_parms set_phys_parms;
+ struct ibmvnic_phys_parms set_phys_parms_rsp;
+ struct ibmvnic_logical_link_state logical_link_state;
+ struct ibmvnic_logical_link_state logical_link_state_rsp;
+ struct ibmvnic_query_ip_offload query_ip_offload;
+ struct ibmvnic_query_ip_offload query_ip_offload_rsp;
+ struct ibmvnic_control_ip_offload control_ip_offload;
+ struct ibmvnic_control_ip_offload control_ip_offload_rsp;
+ struct ibmvnic_request_dump_size request_dump_size;
+ struct ibmvnic_request_dump_size request_dump_size_rsp;
+ struct ibmvnic_request_dump request_dump;
+ struct ibmvnic_request_dump_rsp request_dump_rsp;
+ struct ibmvnic_request_ras_comp_num request_ras_comp_num;
+ struct ibmvnic_request_ras_comp_num request_ras_comp_num_rsp;
+ struct ibmvnic_request_ras_comps request_ras_comps;
+ struct ibmvnic_request_ras_comps request_ras_comps_rsp;
+ struct ibmvnic_control_ras control_ras;
+ struct ibmvnic_control_ras control_ras_rsp;
+ struct ibmvnic_collect_fw_trace collect_fw_trace;
+ struct ibmvnic_collect_fw_trace collect_fw_trace_rsp;
+ struct ibmvnic_request_statistics request_statistics;
+ struct ibmvnic_generic_crq request_statistics_rsp;
+ struct ibmvnic_request_debug_stats request_debug_stats;
+ struct ibmvnic_request_debug_stats request_debug_stats_rsp;
+ struct ibmvnic_error_indication error_indication;
+ struct ibmvnic_request_error_info request_error_info;
+ struct ibmvnic_request_error_rsp request_error_rsp;
+ struct ibmvnic_link_state_indication link_state_indication;
+ struct ibmvnic_change_mac_addr change_mac_addr;
+ struct ibmvnic_change_mac_addr change_mac_addr_rsp;
+ struct ibmvnic_multicast_ctrl multicast_ctrl;
+ struct ibmvnic_multicast_ctrl multicast_ctrl_rsp;
+ struct ibmvnic_generic_crq get_vpd_size;
+ struct ibmvnic_get_vpd_size_rsp get_vpd_size_rsp;
+ struct ibmvnic_get_vpd get_vpd;
+ struct ibmvnic_generic_crq get_vpd_rsp;
+ struct ibmvnic_acl_change_indication acl_change_indication;
+ struct ibmvnic_acl_query acl_query;
+ struct ibmvnic_generic_crq acl_query_rsp;
+ struct ibmvnic_tune tune;
+ struct ibmvnic_generic_crq tune_rsp;
+ struct ibmvnic_request_map request_map;
+ struct ibmvnic_request_map_rsp request_map_rsp;
+ struct ibmvnic_request_unmap request_unmap;
+ struct ibmvnic_request_unmap_rsp request_unmap_rsp;
+ struct ibmvnic_query_map query_map;
+ struct ibmvnic_query_map_rsp query_map_rsp;
+};
+
+enum ibmvnic_rc_codes {
+ SUCCESS = 0,
+ PARTIALSUCCESS = 1,
+ PERMISSION = 2,
+ NOMEMORY = 3,
+ PARAMETER = 4,
+ UNKNOWNCOMMAND = 5,
+ ABORTED = 6,
+ INVALIDSTATE = 7,
+ INVALIDIOBA = 8,
+ INVALIDLENGTH = 9,
+ UNSUPPORTEDOPTION = 10,
+};
+
+enum ibmvnic_capabilities {
+ MIN_TX_QUEUES = 1,
+ MIN_RX_QUEUES = 2,
+ MIN_RX_ADD_QUEUES = 3,
+ MAX_TX_QUEUES = 4,
+ MAX_RX_QUEUES = 5,
+ MAX_RX_ADD_QUEUES = 6,
+ REQ_TX_QUEUES = 7,
+ REQ_RX_QUEUES = 8,
+ REQ_RX_ADD_QUEUES = 9,
+ MIN_TX_ENTRIES_PER_SUBCRQ = 10,
+ MIN_RX_ADD_ENTRIES_PER_SUBCRQ = 11,
+ MAX_TX_ENTRIES_PER_SUBCRQ = 12,
+ MAX_RX_ADD_ENTRIES_PER_SUBCRQ = 13,
+ REQ_TX_ENTRIES_PER_SUBCRQ = 14,
+ REQ_RX_ADD_ENTRIES_PER_SUBCRQ = 15,
+ TCP_IP_OFFLOAD = 16,
+ PROMISC_REQUESTED = 17,
+ PROMISC_SUPPORTED = 18,
+ MIN_MTU = 19,
+ MAX_MTU = 20,
+ REQ_MTU = 21,
+ MAX_MULTICAST_FILTERS = 22,
+ VLAN_HEADER_INSERTION = 23,
+ MAX_TX_SG_ENTRIES = 25,
+ RX_SG_SUPPORTED = 26,
+ RX_SG_REQUESTED = 27,
+ OPT_TX_COMP_SUB_QUEUES = 28,
+ OPT_RX_COMP_QUEUES = 29,
+ OPT_RX_BUFADD_Q_PER_RX_COMP_Q = 30,
+ OPT_TX_ENTRIES_PER_SUBCRQ = 31,
+ OPT_RXBA_ENTRIES_PER_SUBCRQ = 32,
+ TX_RX_DESC_REQ = 33,
+};
+
+enum ibmvnic_error_cause {
+ ADAPTER_PROBLEM = 0,
+ BUS_PROBLEM = 1,
+ FW_PROBLEM = 2,
+ DD_PROBLEM = 3,
+ EEH_RECOVERY = 4,
+ FW_UPDATED = 5,
+ LOW_MEMORY = 6,
+};
+
+enum ibmvnic_commands {
+ VERSION_EXCHANGE = 0x01,
+ VERSION_EXCHANGE_RSP = 0x81,
+ QUERY_CAPABILITY = 0x02,
+ QUERY_CAPABILITY_RSP = 0x82,
+ REQUEST_CAPABILITY = 0x03,
+ REQUEST_CAPABILITY_RSP = 0x83,
+ LOGIN = 0x04,
+ LOGIN_RSP = 0x84,
+ QUERY_PHYS_PARMS = 0x05,
+ QUERY_PHYS_PARMS_RSP = 0x85,
+ QUERY_PHYS_CAPABILITIES = 0x06,
+ QUERY_PHYS_CAPABILITIES_RSP = 0x86,
+ SET_PHYS_PARMS = 0x07,
+ SET_PHYS_PARMS_RSP = 0x87,
+ ERROR_INDICATION = 0x08,
+ REQUEST_ERROR_INFO = 0x09,
+ REQUEST_ERROR_RSP = 0x89,
+ REQUEST_DUMP_SIZE = 0x0A,
+ REQUEST_DUMP_SIZE_RSP = 0x8A,
+ REQUEST_DUMP = 0x0B,
+ REQUEST_DUMP_RSP = 0x8B,
+ LOGICAL_LINK_STATE = 0x0C,
+ LOGICAL_LINK_STATE_RSP = 0x8C,
+ REQUEST_STATISTICS = 0x0D,
+ REQUEST_STATISTICS_RSP = 0x8D,
+ REQUEST_RAS_COMP_NUM = 0x0E,
+ REQUEST_RAS_COMP_NUM_RSP = 0x8E,
+ REQUEST_RAS_COMPS = 0x0F,
+ REQUEST_RAS_COMPS_RSP = 0x8F,
+ CONTROL_RAS = 0x10,
+ CONTROL_RAS_RSP = 0x90,
+ COLLECT_FW_TRACE = 0x11,
+ COLLECT_FW_TRACE_RSP = 0x91,
+ LINK_STATE_INDICATION = 0x12,
+ CHANGE_MAC_ADDR = 0x13,
+ CHANGE_MAC_ADDR_RSP = 0x93,
+ MULTICAST_CTRL = 0x14,
+ MULTICAST_CTRL_RSP = 0x94,
+ GET_VPD_SIZE = 0x15,
+ GET_VPD_SIZE_RSP = 0x95,
+ GET_VPD = 0x16,
+ GET_VPD_RSP = 0x96,
+ TUNE = 0x17,
+ TUNE_RSP = 0x97,
+ QUERY_IP_OFFLOAD = 0x18,
+ QUERY_IP_OFFLOAD_RSP = 0x98,
+ CONTROL_IP_OFFLOAD = 0x19,
+ CONTROL_IP_OFFLOAD_RSP = 0x99,
+ ACL_CHANGE_INDICATION = 0x1A,
+ ACL_QUERY = 0x1B,
+ ACL_QUERY_RSP = 0x9B,
+ REQUEST_DEBUG_STATS = 0x1C,
+ REQUEST_DEBUG_STATS_RSP = 0x9C,
+ QUERY_MAP = 0x1D,
+ QUERY_MAP_RSP = 0x9D,
+ REQUEST_MAP = 0x1E,
+ REQUEST_MAP_RSP = 0x9E,
+ REQUEST_UNMAP = 0x1F,
+ REQUEST_UNMAP_RSP = 0x9F,
+ VLAN_CTRL = 0x20,
+ VLAN_CTRL_RSP = 0xA0,
+};
+
+enum ibmvnic_crq_type {
+ IBMVNIC_CRQ_CMD = 0x80,
+ IBMVNIC_CRQ_CMD_RSP = 0x80,
+ IBMVNIC_CRQ_INIT_CMD = 0xC0,
+ IBMVNIC_CRQ_INIT_RSP = 0xC0,
+ IBMVNIC_CRQ_XPORT_EVENT = 0xFF,
+};
+
+enum ibmvfc_crq_format {
+ IBMVNIC_CRQ_INIT = 0x01,
+ IBMVNIC_CRQ_INIT_COMPLETE = 0x02,
+ IBMVNIC_PARTITION_MIGRATED = 0x06,
+};
+
+struct ibmvnic_crq_queue {
+ union ibmvnic_crq *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ spinlock_t lock;
+};
+
+union sub_crq {
+ struct ibmvnic_generic_scrq generic;
+ struct ibmvnic_tx_comp_desc tx_comp;
+ struct ibmvnic_tx_desc v1;
+ struct ibmvnic_hdr_desc hdr;
+ struct ibmvnic_hdr_ext_desc hdr_ext;
+ struct ibmvnic_sge_desc sge;
+ struct ibmvnic_rx_comp_desc rx_comp;
+ struct ibmvnic_rx_buff_add_desc rx_add;
+};
+
+struct ibmvnic_sub_crq_queue {
+ union sub_crq *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ unsigned long crq_num;
+ unsigned long hw_irq;
+ unsigned int irq;
+ unsigned int pool_index;
+ int scrq_num;
+ spinlock_t lock;
+ struct sk_buff *rx_skb_top;
+ struct ibmvnic_adapter *adapter;
+};
+
+struct ibmvnic_long_term_buff {
+ unsigned char *buff;
+ dma_addr_t addr;
+ u64 size;
+ u8 map_id;
+};
+
+struct ibmvnic_tx_buff {
+ struct sk_buff *skb;
+ dma_addr_t data_dma[IBMVNIC_MAX_FRAGS_PER_CRQ];
+ unsigned int data_len[IBMVNIC_MAX_FRAGS_PER_CRQ];
+ int index;
+ int pool_index;
+ bool last_frag;
+ bool used_bounce;
+};
+
+struct ibmvnic_tx_pool {
+ struct ibmvnic_tx_buff *tx_buff;
+ int *free_map;
+ int consumer_index;
+ int producer_index;
+ wait_queue_head_t ibmvnic_tx_comp_q;
+ struct task_struct *work_thread;
+ struct ibmvnic_long_term_buff long_term_buff;
+};
+
+struct ibmvnic_rx_buff {
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ unsigned char *data;
+ int size;
+ int pool_index;
+};
+
+struct ibmvnic_rx_pool {
+ struct ibmvnic_rx_buff *rx_buff;
+ int size;
+ int index;
+ int buff_size;
+ atomic_t available;
+ int *free_map;
+ int next_free;
+ int next_alloc;
+ int active;
+ struct ibmvnic_long_term_buff long_term_buff;
+};
+
+struct ibmvnic_error_buff {
+ char *buff;
+ dma_addr_t dma;
+ int len;
+ struct list_head list;
+ __be32 error_id;
+};
+
+struct ibmvnic_fw_comp_internal {
+ struct ibmvnic_adapter *adapter;
+ int num;
+ struct debugfs_blob_wrapper desc_blob;
+ int paused;
+};
+
+struct ibmvnic_inflight_cmd {
+ union ibmvnic_crq crq;
+ struct list_head list;
+};
+
+struct ibmvnic_adapter {
+ struct vio_dev *vdev;
+ struct net_device *netdev;
+ struct ibmvnic_crq_queue crq;
+ u8 mac_addr[ETH_ALEN];
+ struct ibmvnic_query_ip_offload_buffer ip_offload_buf;
+ dma_addr_t ip_offload_tok;
+ struct ibmvnic_control_ip_offload_buffer ip_offload_ctrl;
+ dma_addr_t ip_offload_ctrl_tok;
+ bool migrated;
+ u32 msg_enable;
+ void *bounce_buffer;
+ int bounce_buffer_size;
+ dma_addr_t bounce_buffer_dma;
+
+ /* Statistics */
+ struct net_device_stats net_stats;
+ struct ibmvnic_statistics stats;
+ dma_addr_t stats_token;
+ struct completion stats_done;
+ spinlock_t stats_lock;
+ int replenish_no_mem;
+ int replenish_add_buff_success;
+ int replenish_add_buff_failure;
+ int replenish_task_cycles;
+ int tx_send_failed;
+ int tx_map_failed;
+
+ int phys_link_state;
+ int logical_link_state;
+
+ /* login data */
+ struct ibmvnic_login_buffer *login_buf;
+ dma_addr_t login_buf_token;
+ int login_buf_sz;
+
+ struct ibmvnic_login_rsp_buffer *login_rsp_buf;
+ dma_addr_t login_rsp_buf_token;
+ int login_rsp_buf_sz;
+
+ atomic_t running_cap_queries;
+
+ struct ibmvnic_sub_crq_queue **tx_scrq;
+ struct ibmvnic_sub_crq_queue **rx_scrq;
+ int requested_caps;
+
+ /* rx structs */
+ struct napi_struct *napi;
+ struct ibmvnic_rx_pool *rx_pool;
+ u64 promisc;
+
+ struct ibmvnic_tx_pool *tx_pool;
+ bool closing;
+ struct completion init_done;
+
+ struct list_head errors;
+ spinlock_t error_list_lock;
+
+ /* debugfs */
+ struct dentry *debugfs_dir;
+ struct dentry *debugfs_dump;
+ struct completion fw_done;
+ char *dump_data;
+ dma_addr_t dump_data_token;
+ int dump_data_size;
+ int ras_comp_num;
+ struct ibmvnic_fw_component *ras_comps;
+ struct ibmvnic_fw_comp_internal *ras_comp_int;
+ dma_addr_t ras_comps_tok;
+ struct dentry *ras_comps_ent;
+
+ /* in-flight commands that allocate and/or map memory*/
+ struct list_head inflight;
+ spinlock_t inflight_lock;
+
+ /* partner capabilities */
+ u64 min_tx_queues;
+ u64 min_rx_queues;
+ u64 min_rx_add_queues;
+ u64 max_tx_queues;
+ u64 max_rx_queues;
+ u64 max_rx_add_queues;
+ u64 req_tx_queues;
+ u64 req_rx_queues;
+ u64 req_rx_add_queues;
+ u64 min_tx_entries_per_subcrq;
+ u64 min_rx_add_entries_per_subcrq;
+ u64 max_tx_entries_per_subcrq;
+ u64 max_rx_add_entries_per_subcrq;
+ u64 req_tx_entries_per_subcrq;
+ u64 req_rx_add_entries_per_subcrq;
+ u64 tcp_ip_offload;
+ u64 promisc_requested;
+ u64 promisc_supported;
+ u64 min_mtu;
+ u64 max_mtu;
+ u64 req_mtu;
+ u64 max_multicast_filters;
+ u64 vlan_header_insertion;
+ u64 max_tx_sg_entries;
+ u64 rx_sg_supported;
+ u64 rx_sg_requested;
+ u64 opt_tx_comp_sub_queues;
+ u64 opt_rx_comp_queues;
+ u64 opt_rx_bufadd_q_per_rx_comp_q;
+ u64 opt_tx_entries_per_subcrq;
+ u64 opt_rxba_entries_per_subcrq;
+ __be64 tx_rx_desc_req;
+ u8 map_id;
+};
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 4163b16489b3..fa593dd3efe1 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -280,6 +280,16 @@ config I40E_VXLAN
Say Y here if you want to use Virtual eXtensible Local Area Network
(VXLAN) in the driver.
+config I40E_GENEVE
+ bool "Generic Network Virtualization Encapsulation (GENEVE) Support"
+ depends on I40E && GENEVE && !(I40E=y && GENEVE=m)
+ default n
+ ---help---
+ This allows one to create GENEVE virtual interfaces that provide
+ Layer 2 Networks over Layer 3 Networks. GENEVE is often used
+ to tunnel virtual network infrastructure in virtualized environments.
+ Say Y here if you want to use GENEVE in the driver.
+
config I40E_DCB
bool "Data Center Bridging (DCB) Support"
default n
diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h
index 69707108d23c..98fe5a2cd6e3 100644
--- a/drivers/net/ethernet/intel/e1000/e1000.h
+++ b/drivers/net/ethernet/intel/e1000/e1000.h
@@ -213,8 +213,11 @@ struct e1000_rx_ring {
};
#define E1000_DESC_UNUSED(R) \
- ((((R)->next_to_clean > (R)->next_to_use) \
- ? 0 : (R)->count) + (R)->next_to_clean - (R)->next_to_use - 1)
+({ \
+ unsigned int clean = smp_load_acquire(&(R)->next_to_clean); \
+ unsigned int use = READ_ONCE((R)->next_to_use); \
+ (clean > use ? 0 : (R)->count) + clean - use - 1; \
+})
#define E1000_RX_DESC_EXT(R, i) \
(&(((union e1000_rx_desc_extended *)((R).desc))[i]))
diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c
index b1af0d613caa..8172cf08cc33 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_hw.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c
@@ -1,5 +1,5 @@
/*******************************************************************************
-
+*
Intel PRO/1000 Linux driver
Copyright(c) 1999 - 2006 Intel Corporation.
@@ -106,7 +106,7 @@ u16 e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = {
120, 120
};
-static DEFINE_SPINLOCK(e1000_eeprom_lock);
+static DEFINE_MUTEX(e1000_eeprom_lock);
static DEFINE_SPINLOCK(e1000_phy_lock);
/**
@@ -624,8 +624,8 @@ s32 e1000_init_hw(struct e1000_hw *hw)
/* Workaround for PCI-X problem when BIOS sets MMRBC
* incorrectly.
*/
- if (hw->bus_type == e1000_bus_type_pcix
- && e1000_pcix_get_mmrbc(hw) > 2048)
+ if (hw->bus_type == e1000_bus_type_pcix &&
+ e1000_pcix_get_mmrbc(hw) > 2048)
e1000_pcix_set_mmrbc(hw, 2048);
break;
}
@@ -683,10 +683,9 @@ static s32 e1000_adjust_serdes_amplitude(struct e1000_hw *hw)
}
ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1,
- &eeprom_data);
- if (ret_val) {
+ &eeprom_data);
+ if (ret_val)
return ret_val;
- }
if (eeprom_data != EEPROM_RESERVED_WORD) {
/* Adjust SERDES output amplitude only. */
@@ -1074,8 +1073,8 @@ static s32 e1000_copper_link_preconfig(struct e1000_hw *hw)
if (hw->mac_type <= e1000_82543 ||
hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 ||
- hw->mac_type == e1000_82541_rev_2
- || hw->mac_type == e1000_82547_rev_2)
+ hw->mac_type == e1000_82541_rev_2 ||
+ hw->mac_type == e1000_82547_rev_2)
hw->phy_reset_disable = false;
return E1000_SUCCESS;
@@ -1652,7 +1651,7 @@ s32 e1000_phy_setup_autoneg(struct e1000_hw *hw)
mii_1000t_ctrl_reg = 0;
} else {
ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
- mii_1000t_ctrl_reg);
+ mii_1000t_ctrl_reg);
if (ret_val)
return ret_val;
}
@@ -1881,10 +1880,11 @@ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543)
- && (!hw->autoneg)
- && (hw->forced_speed_duplex == e1000_10_full
- || hw->forced_speed_duplex == e1000_10_half)) {
+ if ((hw->mac_type == e1000_82544 ||
+ hw->mac_type == e1000_82543) &&
+ (!hw->autoneg) &&
+ (hw->forced_speed_duplex == e1000_10_full ||
+ hw->forced_speed_duplex == e1000_10_half)) {
ret_val = e1000_polarity_reversal_workaround(hw);
if (ret_val)
return ret_val;
@@ -2084,11 +2084,12 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw)
* so we had to force link. In this case, we need to force the
* configuration of the MAC to match the "fc" parameter.
*/
- if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed))
- || ((hw->media_type == e1000_media_type_internal_serdes)
- && (hw->autoneg_failed))
- || ((hw->media_type == e1000_media_type_copper)
- && (!hw->autoneg))) {
+ if (((hw->media_type == e1000_media_type_fiber) &&
+ (hw->autoneg_failed)) ||
+ ((hw->media_type == e1000_media_type_internal_serdes) &&
+ (hw->autoneg_failed)) ||
+ ((hw->media_type == e1000_media_type_copper) &&
+ (!hw->autoneg))) {
ret_val = e1000_force_mac_fc(hw);
if (ret_val) {
e_dbg("Error forcing flow control settings\n");
@@ -2193,8 +2194,7 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw)
else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
- (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR))
- {
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc = E1000_FC_TX_PAUSE;
e_dbg
("Flow Control = TX PAUSE frames only.\n");
@@ -2210,8 +2210,7 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw)
else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
!(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
- (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR))
- {
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc = E1000_FC_RX_PAUSE;
e_dbg
("Flow Control = RX PAUSE frames only.\n");
@@ -2460,10 +2459,11 @@ s32 e1000_check_for_link(struct e1000_hw *hw)
* happen due to the execution of this workaround.
*/
- if ((hw->mac_type == e1000_82544
- || hw->mac_type == e1000_82543) && (!hw->autoneg)
- && (hw->forced_speed_duplex == e1000_10_full
- || hw->forced_speed_duplex == e1000_10_half)) {
+ if ((hw->mac_type == e1000_82544 ||
+ hw->mac_type == e1000_82543) &&
+ (!hw->autoneg) &&
+ (hw->forced_speed_duplex == e1000_10_full ||
+ hw->forced_speed_duplex == e1000_10_half)) {
ew32(IMC, 0xffffffff);
ret_val =
e1000_polarity_reversal_workaround(hw);
@@ -2528,8 +2528,10 @@ s32 e1000_check_for_link(struct e1000_hw *hw)
*/
if (hw->tbi_compatibility_en) {
u16 speed, duplex;
+
ret_val =
e1000_get_speed_and_duplex(hw, &speed, &duplex);
+
if (ret_val) {
e_dbg
("Error getting link speed and duplex\n");
@@ -2628,10 +2630,10 @@ s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex)
e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data);
if (ret_val)
return ret_val;
- if ((*speed == SPEED_100
- && !(phy_data & NWAY_LPAR_100TX_FD_CAPS))
- || (*speed == SPEED_10
- && !(phy_data & NWAY_LPAR_10T_FD_CAPS)))
+ if ((*speed == SPEED_100 &&
+ !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) ||
+ (*speed == SPEED_10 &&
+ !(phy_data & NWAY_LPAR_10T_FD_CAPS)))
*duplex = HALF_DUPLEX;
}
}
@@ -2664,9 +2666,9 @@ static s32 e1000_wait_autoneg(struct e1000_hw *hw)
ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
if (ret_val)
return ret_val;
- if (phy_data & MII_SR_AUTONEG_COMPLETE) {
+ if (phy_data & MII_SR_AUTONEG_COMPLETE)
return E1000_SUCCESS;
- }
+
msleep(100);
}
return E1000_SUCCESS;
@@ -2803,11 +2805,11 @@ static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw)
return data;
}
-
/**
* e1000_read_phy_reg - read a phy register
* @hw: Struct containing variables accessed by shared code
* @reg_addr: address of the PHY register to read
+ * @phy_data: pointer to the value on the PHY register
*
* Reads the value from a PHY register, if the value is on a specific non zero
* page, sets the page first.
@@ -2823,14 +2825,13 @@ s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data)
(reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
(u16) reg_addr);
- if (ret_val) {
- spin_unlock_irqrestore(&e1000_phy_lock, flags);
- return ret_val;
- }
+ if (ret_val)
+ goto out;
}
ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr,
phy_data);
+out:
spin_unlock_irqrestore(&e1000_phy_lock, flags);
return ret_val;
@@ -2881,7 +2882,7 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
e_dbg("MDI Read Error\n");
return -E1000_ERR_PHY;
}
- *phy_data = (u16) mdic;
+ *phy_data = (u16)mdic;
} else {
mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) |
(phy_addr << E1000_MDIC_PHY_SHIFT) |
@@ -2906,7 +2907,7 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
e_dbg("MDI Error\n");
return -E1000_ERR_PHY;
}
- *phy_data = (u16) mdic;
+ *phy_data = (u16)mdic;
}
} else {
/* We must first send a preamble through the MDIO pin to signal
@@ -2960,7 +2961,7 @@ s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 phy_data)
if ((hw->phy_type == e1000_phy_igp) &&
(reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
- (u16) reg_addr);
+ (u16)reg_addr);
if (ret_val) {
spin_unlock_irqrestore(&e1000_phy_lock, flags);
return ret_val;
@@ -2993,7 +2994,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
* the desired data.
*/
if (hw->mac_type == e1000_ce4100) {
- mdic = (((u32) phy_data) |
+ mdic = (((u32)phy_data) |
(reg_addr << E1000_MDIC_REG_SHIFT) |
(phy_addr << E1000_MDIC_PHY_SHIFT) |
(INTEL_CE_GBE_MDIC_OP_WRITE) |
@@ -3015,7 +3016,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
return -E1000_ERR_PHY;
}
} else {
- mdic = (((u32) phy_data) |
+ mdic = (((u32)phy_data) |
(reg_addr << E1000_MDIC_REG_SHIFT) |
(phy_addr << E1000_MDIC_PHY_SHIFT) |
(E1000_MDIC_OP_WRITE));
@@ -3053,7 +3054,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) |
(PHY_OP_WRITE << 12) | (PHY_SOF << 14));
mdic <<= 16;
- mdic |= (u32) phy_data;
+ mdic |= (u32)phy_data;
e1000_shift_out_mdi_bits(hw, mdic, 32);
}
@@ -3176,14 +3177,14 @@ static s32 e1000_detect_gig_phy(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- hw->phy_id = (u32) (phy_id_high << 16);
+ hw->phy_id = (u32)(phy_id_high << 16);
udelay(20);
ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low);
if (ret_val)
return ret_val;
- hw->phy_id |= (u32) (phy_id_low & PHY_REVISION_MASK);
- hw->phy_revision = (u32) phy_id_low & ~PHY_REVISION_MASK;
+ hw->phy_id |= (u32)(phy_id_low & PHY_REVISION_MASK);
+ hw->phy_revision = (u32)phy_id_low & ~PHY_REVISION_MASK;
switch (hw->mac_type) {
case e1000_82543:
@@ -3401,7 +3402,6 @@ static s32 e1000_phy_m88_get_info(struct e1000_hw *hw,
phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >>
SR_1000T_REMOTE_RX_STATUS_SHIFT) ?
e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
-
}
return E1000_SUCCESS;
@@ -3449,7 +3449,7 @@ s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info)
if (hw->phy_type == e1000_phy_igp)
return e1000_phy_igp_get_info(hw, phy_info);
else if ((hw->phy_type == e1000_phy_8211) ||
- (hw->phy_type == e1000_phy_8201))
+ (hw->phy_type == e1000_phy_8201))
return E1000_SUCCESS;
else
return e1000_phy_m88_get_info(hw, phy_info);
@@ -3611,11 +3611,11 @@ static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, u16 count)
*/
mask = 0x01 << (count - 1);
eecd = er32(EECD);
- if (eeprom->type == e1000_eeprom_microwire) {
+ if (eeprom->type == e1000_eeprom_microwire)
eecd &= ~E1000_EECD_DO;
- } else if (eeprom->type == e1000_eeprom_spi) {
+ else if (eeprom->type == e1000_eeprom_spi)
eecd |= E1000_EECD_DO;
- }
+
do {
/* A "1" is shifted out to the EEPROM by setting bit "DI" to a
* "1", and then raising and then lowering the clock (the SK bit
@@ -3851,7 +3851,7 @@ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw)
do {
e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI,
hw->eeprom.opcode_bits);
- spi_stat_reg = (u8) e1000_shift_in_ee_bits(hw, 8);
+ spi_stat_reg = (u8)e1000_shift_in_ee_bits(hw, 8);
if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI))
break;
@@ -3882,9 +3882,10 @@ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw)
s32 e1000_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
{
s32 ret;
- spin_lock(&e1000_eeprom_lock);
+
+ mutex_lock(&e1000_eeprom_lock);
ret = e1000_do_read_eeprom(hw, offset, words, data);
- spin_unlock(&e1000_eeprom_lock);
+ mutex_unlock(&e1000_eeprom_lock);
return ret;
}
@@ -3896,15 +3897,16 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
if (hw->mac_type == e1000_ce4100) {
GBE_CONFIG_FLASH_READ(GBE_CONFIG_BASE_VIRT, offset, words,
- data);
+ data);
return E1000_SUCCESS;
}
/* A check for invalid values: offset too large, too many words, and
* not enough words.
*/
- if ((offset >= eeprom->word_size)
- || (words > eeprom->word_size - offset) || (words == 0)) {
+ if ((offset >= eeprom->word_size) ||
+ (words > eeprom->word_size - offset) ||
+ (words == 0)) {
e_dbg("\"words\" parameter out of bounds. Words = %d,"
"size = %d\n", offset, eeprom->word_size);
return -E1000_ERR_EEPROM;
@@ -3940,7 +3942,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
/* Send the READ command (opcode + addr) */
e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) (offset * 2),
+ e1000_shift_out_ee_bits(hw, (u16)(offset * 2),
eeprom->address_bits);
/* Read the data. The address of the eeprom internally
@@ -3960,7 +3962,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
e1000_shift_out_ee_bits(hw,
EEPROM_READ_OPCODE_MICROWIRE,
eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) (offset + i),
+ e1000_shift_out_ee_bits(hw, (u16)(offset + i),
eeprom->address_bits);
/* Read the data. For microwire, each word requires the
@@ -3968,6 +3970,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
*/
data[i] = e1000_shift_in_ee_bits(hw, 16);
e1000_standby_eeprom(hw);
+ cond_resched();
}
}
@@ -4004,7 +4007,7 @@ s32 e1000_validate_eeprom_checksum(struct e1000_hw *hw)
return E1000_SUCCESS;
#endif
- if (checksum == (u16) EEPROM_SUM)
+ if (checksum == (u16)EEPROM_SUM)
return E1000_SUCCESS;
else {
e_dbg("EEPROM Checksum Invalid\n");
@@ -4031,7 +4034,7 @@ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw)
}
checksum += eeprom_data;
}
- checksum = (u16) EEPROM_SUM - checksum;
+ checksum = (u16)EEPROM_SUM - checksum;
if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) {
e_dbg("EEPROM Write Error\n");
return -E1000_ERR_EEPROM;
@@ -4052,9 +4055,10 @@ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw)
s32 e1000_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
{
s32 ret;
- spin_lock(&e1000_eeprom_lock);
+
+ mutex_lock(&e1000_eeprom_lock);
ret = e1000_do_write_eeprom(hw, offset, words, data);
- spin_unlock(&e1000_eeprom_lock);
+ mutex_unlock(&e1000_eeprom_lock);
return ret;
}
@@ -4066,15 +4070,16 @@ static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
if (hw->mac_type == e1000_ce4100) {
GBE_CONFIG_FLASH_WRITE(GBE_CONFIG_BASE_VIRT, offset, words,
- data);
+ data);
return E1000_SUCCESS;
}
/* A check for invalid values: offset too large, too many words, and
* not enough words.
*/
- if ((offset >= eeprom->word_size)
- || (words > eeprom->word_size - offset) || (words == 0)) {
+ if ((offset >= eeprom->word_size) ||
+ (words > eeprom->word_size - offset) ||
+ (words == 0)) {
e_dbg("\"words\" parameter out of bounds\n");
return -E1000_ERR_EEPROM;
}
@@ -4116,6 +4121,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words,
return -E1000_ERR_EEPROM;
e1000_standby_eeprom(hw);
+ cond_resched();
/* Send the WRITE ENABLE command (8 bit opcode ) */
e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI,
@@ -4132,7 +4138,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words,
/* Send the Write command (8-bit opcode + addr) */
e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) ((offset + widx) * 2),
+ e1000_shift_out_ee_bits(hw, (u16)((offset + widx) * 2),
eeprom->address_bits);
/* Send the data */
@@ -4142,6 +4148,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words,
*/
while (widx < words) {
u16 word_out = data[widx];
+
word_out = (word_out >> 8) | (word_out << 8);
e1000_shift_out_ee_bits(hw, word_out, 16);
widx++;
@@ -4183,9 +4190,9 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
* EEPROM into write/erase mode.
*/
e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE,
- (u16) (eeprom->opcode_bits + 2));
+ (u16)(eeprom->opcode_bits + 2));
- e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2));
+ e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2));
/* Prepare the EEPROM */
e1000_standby_eeprom(hw);
@@ -4195,7 +4202,7 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE,
eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) (offset + words_written),
+ e1000_shift_out_ee_bits(hw, (u16)(offset + words_written),
eeprom->address_bits);
/* Send the data */
@@ -4224,6 +4231,7 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
/* Recover from write */
e1000_standby_eeprom(hw);
+ cond_resched();
words_written++;
}
@@ -4235,9 +4243,9 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
* EEPROM out of write/erase mode.
*/
e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE,
- (u16) (eeprom->opcode_bits + 2));
+ (u16)(eeprom->opcode_bits + 2));
- e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2));
+ e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2));
return E1000_SUCCESS;
}
@@ -4260,8 +4268,8 @@ s32 e1000_read_mac_addr(struct e1000_hw *hw)
e_dbg("EEPROM Read Error\n");
return -E1000_ERR_EEPROM;
}
- hw->perm_mac_addr[i] = (u8) (eeprom_data & 0x00FF);
- hw->perm_mac_addr[i + 1] = (u8) (eeprom_data >> 8);
+ hw->perm_mac_addr[i] = (u8)(eeprom_data & 0x00FF);
+ hw->perm_mac_addr[i + 1] = (u8)(eeprom_data >> 8);
}
switch (hw->mac_type) {
@@ -4328,19 +4336,19 @@ u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr)
*/
case 0:
/* [47:36] i.e. 0x563 for above example address */
- hash_value = ((mc_addr[4] >> 4) | (((u16) mc_addr[5]) << 4));
+ hash_value = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4));
break;
case 1:
/* [46:35] i.e. 0xAC6 for above example address */
- hash_value = ((mc_addr[4] >> 3) | (((u16) mc_addr[5]) << 5));
+ hash_value = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5));
break;
case 2:
/* [45:34] i.e. 0x5D8 for above example address */
- hash_value = ((mc_addr[4] >> 2) | (((u16) mc_addr[5]) << 6));
+ hash_value = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6));
break;
case 3:
/* [43:32] i.e. 0x634 for above example address */
- hash_value = ((mc_addr[4]) | (((u16) mc_addr[5]) << 8));
+ hash_value = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8));
break;
}
@@ -4361,9 +4369,9 @@ void e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
/* HW expects these in little endian so we reverse the byte order
* from network order (big endian) to little endian
*/
- rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) |
- ((u32) addr[2] << 16) | ((u32) addr[3] << 24));
- rar_high = ((u32) addr[4] | ((u32) addr[5] << 8));
+ rar_low = ((u32)addr[0] | ((u32)addr[1] << 8) |
+ ((u32)addr[2] << 16) | ((u32)addr[3] << 24));
+ rar_high = ((u32)addr[4] | ((u32)addr[5] << 8));
/* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx
* unit hang.
@@ -4537,7 +4545,7 @@ s32 e1000_setup_led(struct e1000_hw *hw)
if (ret_val)
return ret_val;
ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
- (u16) (hw->phy_spd_default &
+ (u16)(hw->phy_spd_default &
~IGP01E1000_GMII_SPD));
if (ret_val)
return ret_val;
@@ -4802,7 +4810,7 @@ void e1000_reset_adaptive(struct e1000_hw *hw)
void e1000_update_adaptive(struct e1000_hw *hw)
{
if (hw->adaptive_ifs) {
- if ((hw->collision_delta *hw->ifs_ratio) > hw->tx_packet_delta) {
+ if ((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) {
if (hw->tx_packet_delta > MIN_NUM_XMITS) {
hw->in_ifs_mode = true;
if (hw->current_ifs_val < hw->ifs_max_val) {
@@ -4816,8 +4824,8 @@ void e1000_update_adaptive(struct e1000_hw *hw)
}
}
} else {
- if (hw->in_ifs_mode
- && (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
+ if (hw->in_ifs_mode &&
+ (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
hw->current_ifs_val = 0;
hw->in_ifs_mode = false;
ew32(AIT, 0);
@@ -4922,7 +4930,6 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length,
/* Use old method for Phy older than IGP */
if (hw->phy_type == e1000_phy_m88) {
-
ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
&phy_data);
if (ret_val)
@@ -4966,7 +4973,6 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length,
};
/* Read the AGC registers for all channels */
for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
-
ret_val =
e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data);
if (ret_val)
@@ -4976,8 +4982,8 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length,
/* Value bound check. */
if ((cur_agc_value >=
- IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1)
- || (cur_agc_value == 0))
+ IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) ||
+ (cur_agc_value == 0))
return -E1000_ERR_PHY;
agc_value += cur_agc_value;
@@ -5054,7 +5060,6 @@ static s32 e1000_check_polarity(struct e1000_hw *hw,
*/
if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
IGP01E1000_PSSR_SPEED_1000MBPS) {
-
/* Read the GIG initialization PCS register (0x00B4) */
ret_val =
e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG,
@@ -5175,8 +5180,8 @@ static s32 e1000_1000Mb_check_cable_length(struct e1000_hw *hw)
hw->ffe_config_state = e1000_ffe_config_active;
ret_val = e1000_write_phy_reg(hw,
- IGP01E1000_PHY_DSP_FFE,
- IGP01E1000_PHY_DSP_FFE_CM_CP);
+ IGP01E1000_PHY_DSP_FFE,
+ IGP01E1000_PHY_DSP_FFE_CM_CP);
if (ret_val)
return ret_val;
break;
@@ -5243,7 +5248,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
msleep(20);
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_FORCE_GIGA);
+ IGP01E1000_IEEE_FORCE_GIGA);
if (ret_val)
return ret_val;
for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
@@ -5264,7 +5269,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
}
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_RESTART_AUTONEG);
+ IGP01E1000_IEEE_RESTART_AUTONEG);
if (ret_val)
return ret_val;
@@ -5299,7 +5304,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
msleep(20);
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_FORCE_GIGA);
+ IGP01E1000_IEEE_FORCE_GIGA);
if (ret_val)
return ret_val;
ret_val =
@@ -5309,7 +5314,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
return ret_val;
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_RESTART_AUTONEG);
+ IGP01E1000_IEEE_RESTART_AUTONEG);
if (ret_val)
return ret_val;
@@ -5346,9 +5351,8 @@ static s32 e1000_set_phy_mode(struct e1000_hw *hw)
ret_val =
e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1,
&eeprom_data);
- if (ret_val) {
+ if (ret_val)
return ret_val;
- }
if ((eeprom_data != EEPROM_RESERVED_WORD) &&
(eeprom_data & EEPROM_PHY_CLASS_A)) {
@@ -5395,8 +5399,8 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active)
* from the lowest speeds starting from 10Mbps. The capability is used
* for Dx transitions and states
*/
- if (hw->mac_type == e1000_82541_rev_2
- || hw->mac_type == e1000_82547_rev_2) {
+ if (hw->mac_type == e1000_82541_rev_2 ||
+ hw->mac_type == e1000_82547_rev_2) {
ret_val =
e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data);
if (ret_val)
@@ -5446,11 +5450,9 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active)
if (ret_val)
return ret_val;
}
- } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT)
- || (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL)
- || (hw->autoneg_advertised ==
- AUTONEG_ADVERTISE_10_100_ALL)) {
-
+ } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) ||
+ (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL) ||
+ (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) {
if (hw->mac_type == e1000_82541_rev_2 ||
hw->mac_type == e1000_82547_rev_2) {
phy_data |= IGP01E1000_GMII_FLEX_SPD;
@@ -5474,7 +5476,6 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active)
phy_data);
if (ret_val)
return ret_val;
-
}
return E1000_SUCCESS;
}
@@ -5542,7 +5543,6 @@ static s32 e1000_set_vco_speed(struct e1000_hw *hw)
return E1000_SUCCESS;
}
-
/**
* e1000_enable_mng_pass_thru - check for bmc pass through
* @hw: Struct containing variables accessed by shared code
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index fd7be860c201..3fc7bde699ba 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -99,13 +99,13 @@ int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
void e1000_free_all_tx_resources(struct e1000_adapter *adapter);
void e1000_free_all_rx_resources(struct e1000_adapter *adapter);
static int e1000_setup_tx_resources(struct e1000_adapter *adapter,
- struct e1000_tx_ring *txdr);
+ struct e1000_tx_ring *txdr);
static int e1000_setup_rx_resources(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rxdr);
+ struct e1000_rx_ring *rxdr);
static void e1000_free_tx_resources(struct e1000_adapter *adapter,
- struct e1000_tx_ring *tx_ring);
+ struct e1000_tx_ring *tx_ring);
static void e1000_free_rx_resources(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rx_ring);
+ struct e1000_rx_ring *rx_ring);
void e1000_update_stats(struct e1000_adapter *adapter);
static int e1000_init_module(void);
@@ -122,16 +122,16 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter);
static void e1000_clean_all_tx_rings(struct e1000_adapter *adapter);
static void e1000_clean_all_rx_rings(struct e1000_adapter *adapter);
static void e1000_clean_tx_ring(struct e1000_adapter *adapter,
- struct e1000_tx_ring *tx_ring);
+ struct e1000_tx_ring *tx_ring);
static void e1000_clean_rx_ring(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rx_ring);
+ struct e1000_rx_ring *rx_ring);
static void e1000_set_rx_mode(struct net_device *netdev);
static void e1000_update_phy_info_task(struct work_struct *work);
static void e1000_watchdog(struct work_struct *work);
static void e1000_82547_tx_fifo_stall_task(struct work_struct *work);
static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
struct net_device *netdev);
-static struct net_device_stats * e1000_get_stats(struct net_device *netdev);
+static struct net_device_stats *e1000_get_stats(struct net_device *netdev);
static int e1000_change_mtu(struct net_device *netdev, int new_mtu);
static int e1000_set_mac(struct net_device *netdev, void *p);
static irqreturn_t e1000_intr(int irq, void *data);
@@ -164,7 +164,7 @@ static void e1000_tx_timeout(struct net_device *dev);
static void e1000_reset_task(struct work_struct *work);
static void e1000_smartspeed(struct e1000_adapter *adapter);
static int e1000_82547_fifo_workaround(struct e1000_adapter *adapter,
- struct sk_buff *skb);
+ struct sk_buff *skb);
static bool e1000_vlan_used(struct e1000_adapter *adapter);
static void e1000_vlan_mode(struct net_device *netdev,
@@ -195,7 +195,7 @@ MODULE_PARM_DESC(copybreak,
"Maximum size of packet that is copied to a new buffer on receive");
static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
- pci_channel_state_t state);
+ pci_channel_state_t state);
static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev);
static void e1000_io_resume(struct pci_dev *pdev);
@@ -287,7 +287,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter)
int err;
err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name,
- netdev);
+ netdev);
if (err) {
e_err(probe, "Unable to allocate interrupt Error: %d\n", err);
}
@@ -636,8 +636,8 @@ void e1000_reset(struct e1000_adapter *adapter)
* but don't include ethernet FCS because hardware appends it
*/
min_tx_space = (hw->max_frame_size +
- sizeof(struct e1000_tx_desc) -
- ETH_FCS_LEN) * 2;
+ sizeof(struct e1000_tx_desc) -
+ ETH_FCS_LEN) * 2;
min_tx_space = ALIGN(min_tx_space, 1024);
min_tx_space >>= 10;
/* software strips receive CRC, so leave room for it */
@@ -943,8 +943,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct e1000_adapter *adapter;
struct e1000_hw *hw;
- static int cards_found = 0;
- static int global_quad_port_a = 0; /* global ksp3 port a indication */
+ static int cards_found;
+ static int global_quad_port_a; /* global ksp3 port a indication */
int i, err, pci_using_dac;
u16 eeprom_data = 0;
u16 tmp = 0;
@@ -1046,7 +1046,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (hw->mac_type == e1000_ce4100) {
hw->ce4100_gbe_mdio_base_virt =
ioremap(pci_resource_start(pdev, BAR_1),
- pci_resource_len(pdev, BAR_1));
+ pci_resource_len(pdev, BAR_1));
if (!hw->ce4100_gbe_mdio_base_virt)
goto err_mdio_ioremap;
@@ -1148,7 +1148,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
case e1000_82546:
case e1000_82546_rev_3:
- if (er32(STATUS) & E1000_STATUS_FUNC_1){
+ if (er32(STATUS) & E1000_STATUS_FUNC_1) {
e1000_read_eeprom(hw,
EEPROM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
break;
@@ -1199,13 +1199,13 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
for (i = 0; i < 32; i++) {
hw->phy_addr = i;
e1000_read_phy_reg(hw, PHY_ID2, &tmp);
- if (tmp == 0 || tmp == 0xFF) {
- if (i == 31)
- goto err_eeprom;
- continue;
- } else
+
+ if (tmp != 0 && tmp != 0xFF)
break;
}
+
+ if (i >= 32)
+ goto err_eeprom;
}
/* reset the hardware with the new settings */
@@ -1263,7 +1263,7 @@ err_pci_reg:
* @pdev: PCI device information struct
*
* e1000_remove is called by the PCI subsystem to alert the driver
- * that it should release a PCI device. The could be caused by a
+ * that it should release a PCI device. That could be caused by a
* Hot-Plug event, or because the driver is going to be removed from
* memory.
**/
@@ -1334,12 +1334,12 @@ static int e1000_sw_init(struct e1000_adapter *adapter)
static int e1000_alloc_queues(struct e1000_adapter *adapter)
{
adapter->tx_ring = kcalloc(adapter->num_tx_queues,
- sizeof(struct e1000_tx_ring), GFP_KERNEL);
+ sizeof(struct e1000_tx_ring), GFP_KERNEL);
if (!adapter->tx_ring)
return -ENOMEM;
adapter->rx_ring = kcalloc(adapter->num_rx_queues,
- sizeof(struct e1000_rx_ring), GFP_KERNEL);
+ sizeof(struct e1000_rx_ring), GFP_KERNEL);
if (!adapter->rx_ring) {
kfree(adapter->tx_ring);
return -ENOMEM;
@@ -1811,20 +1811,20 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
rctl &= ~E1000_RCTL_SZ_4096;
rctl |= E1000_RCTL_BSEX;
switch (adapter->rx_buffer_len) {
- case E1000_RXBUFFER_2048:
- default:
- rctl |= E1000_RCTL_SZ_2048;
- rctl &= ~E1000_RCTL_BSEX;
- break;
- case E1000_RXBUFFER_4096:
- rctl |= E1000_RCTL_SZ_4096;
- break;
- case E1000_RXBUFFER_8192:
- rctl |= E1000_RCTL_SZ_8192;
- break;
- case E1000_RXBUFFER_16384:
- rctl |= E1000_RCTL_SZ_16384;
- break;
+ case E1000_RXBUFFER_2048:
+ default:
+ rctl |= E1000_RCTL_SZ_2048;
+ rctl &= ~E1000_RCTL_BSEX;
+ break;
+ case E1000_RXBUFFER_4096:
+ rctl |= E1000_RCTL_SZ_4096;
+ break;
+ case E1000_RXBUFFER_8192:
+ rctl |= E1000_RCTL_SZ_8192;
+ break;
+ case E1000_RXBUFFER_16384:
+ rctl |= E1000_RCTL_SZ_16384;
+ break;
}
/* This is useful for sniffing bad packets. */
@@ -1861,12 +1861,12 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
if (adapter->netdev->mtu > ETH_DATA_LEN) {
rdlen = adapter->rx_ring[0].count *
- sizeof(struct e1000_rx_desc);
+ sizeof(struct e1000_rx_desc);
adapter->clean_rx = e1000_clean_jumbo_rx_irq;
adapter->alloc_rx_buf = e1000_alloc_jumbo_rx_buffers;
} else {
rdlen = adapter->rx_ring[0].count *
- sizeof(struct e1000_rx_desc);
+ sizeof(struct e1000_rx_desc);
adapter->clean_rx = e1000_clean_rx_irq;
adapter->alloc_rx_buf = e1000_alloc_rx_buffers;
}
@@ -2761,7 +2761,9 @@ static int e1000_tso(struct e1000_adapter *adapter,
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- if (++i == tx_ring->count) i = 0;
+ if (++i == tx_ring->count)
+ i = 0;
+
tx_ring->next_to_use = i;
return true;
@@ -2816,7 +2818,9 @@ static bool e1000_tx_csum(struct e1000_adapter *adapter,
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- if (unlikely(++i == tx_ring->count)) i = 0;
+ if (unlikely(++i == tx_ring->count))
+ i = 0;
+
tx_ring->next_to_use = i;
return true;
@@ -2865,8 +2869,8 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
* packet is smaller than 2048 - 16 - 16 (or 2016) bytes
*/
if (unlikely((hw->bus_type == e1000_bus_type_pcix) &&
- (size > 2015) && count == 0))
- size = 2015;
+ (size > 2015) && count == 0))
+ size = 2015;
/* Workaround for potential 82544 hang in PCI-X. Avoid
* terminating buffers within evenly-aligned dwords.
@@ -2963,7 +2967,7 @@ dma_error:
count--;
while (count--) {
- if (i==0)
+ if (i == 0)
i += tx_ring->count;
i--;
buffer_info = &tx_ring->buffer_info[i];
@@ -3013,7 +3017,8 @@ static void e1000_tx_queue(struct e1000_adapter *adapter,
tx_desc->lower.data =
cpu_to_le32(txd_lower | buffer_info->length);
tx_desc->upper.data = cpu_to_le32(txd_upper);
- if (unlikely(++i == tx_ring->count)) i = 0;
+ if (unlikely(++i == tx_ring->count))
+ i = 0;
}
tx_desc->lower.data |= cpu_to_le32(adapter->txd_cmd);
@@ -3101,7 +3106,7 @@ static int e1000_maybe_stop_tx(struct net_device *netdev,
return __e1000_maybe_stop_tx(netdev, size);
}
-#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1 )
+#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1)
static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
struct net_device *netdev)
{
@@ -3841,7 +3846,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
struct e1000_tx_buffer *buffer_info;
unsigned int i, eop;
unsigned int count = 0;
- unsigned int total_tx_bytes=0, total_tx_packets=0;
+ unsigned int total_tx_bytes = 0, total_tx_packets = 0;
unsigned int bytes_compl = 0, pkts_compl = 0;
i = tx_ring->next_to_clean;
@@ -3869,14 +3874,18 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
e1000_unmap_and_free_tx_resource(adapter, buffer_info);
tx_desc->upper.data = 0;
- if (unlikely(++i == tx_ring->count)) i = 0;
+ if (unlikely(++i == tx_ring->count))
+ i = 0;
}
eop = tx_ring->buffer_info[i].next_to_watch;
eop_desc = E1000_TX_DESC(*tx_ring, eop);
}
- tx_ring->next_to_clean = i;
+ /* Synchronize with E1000_DESC_UNUSED called from e1000_xmit_frame,
+ * which will reuse the cleaned buffers.
+ */
+ smp_store_release(&tx_ring->next_to_clean, i);
netdev_completed_queue(netdev, pkts_compl, bytes_compl);
@@ -3954,9 +3963,11 @@ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err,
skb_checksum_none_assert(skb);
/* 82543 or newer only */
- if (unlikely(hw->mac_type < e1000_82543)) return;
+ if (unlikely(hw->mac_type < e1000_82543))
+ return;
/* Ignore Checksum bit is set */
- if (unlikely(status & E1000_RXD_STAT_IXSM)) return;
+ if (unlikely(status & E1000_RXD_STAT_IXSM))
+ return;
/* TCP/UDP checksum error bit is set */
if (unlikely(errors & E1000_RXD_ERR_TCPE)) {
/* let the stack verify checksum errors */
@@ -4136,7 +4147,7 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
unsigned int i;
int cleaned_count = 0;
bool cleaned = false;
- unsigned int total_rx_bytes=0, total_rx_packets=0;
+ unsigned int total_rx_bytes = 0, total_rx_packets = 0;
i = rx_ring->next_to_clean;
rx_desc = E1000_RX_DESC(*rx_ring, i);
@@ -4153,7 +4164,9 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
status = rx_desc->status;
- if (++i == rx_ring->count) i = 0;
+ if (++i == rx_ring->count)
+ i = 0;
+
next_rxd = E1000_RX_DESC(*rx_ring, i);
prefetch(next_rxd);
@@ -4356,7 +4369,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
unsigned int i;
int cleaned_count = 0;
bool cleaned = false;
- unsigned int total_rx_bytes=0, total_rx_packets=0;
+ unsigned int total_rx_bytes = 0, total_rx_packets = 0;
i = rx_ring->next_to_clean;
rx_desc = E1000_RX_DESC(*rx_ring, i);
@@ -4395,7 +4408,9 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
buffer_info->rxbuf.data = NULL;
}
- if (++i == rx_ring->count) i = 0;
+ if (++i == rx_ring->count)
+ i = 0;
+
next_rxd = E1000_RX_DESC(*rx_ring, i);
prefetch(next_rxd);
@@ -4683,9 +4698,11 @@ static void e1000_smartspeed(struct e1000_adapter *adapter)
* we assume back-to-back
*/
e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status);
- if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return;
+ if (!(phy_status & SR_1000T_MS_CONFIG_FAULT))
+ return;
e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status);
- if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return;
+ if (!(phy_status & SR_1000T_MS_CONFIG_FAULT))
+ return;
e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_ctrl);
if (phy_ctrl & CR_1000T_MS_ENABLE) {
phy_ctrl &= ~CR_1000T_MS_ENABLE;
diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index 133d4074dbe4..f7c7804d79e5 100644
--- a/drivers/net/ethernet/intel/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
@@ -441,12 +441,13 @@
#define E1000_IMS_RXQ1 E1000_ICR_RXQ1 /* Rx Queue 1 Interrupt */
#define E1000_IMS_TXQ0 E1000_ICR_TXQ0 /* Tx Queue 0 Interrupt */
#define E1000_IMS_TXQ1 E1000_ICR_TXQ1 /* Tx Queue 1 Interrupt */
-#define E1000_IMS_OTHER E1000_ICR_OTHER /* Other Interrupts */
+#define E1000_IMS_OTHER E1000_ICR_OTHER /* Other Interrupt */
/* Interrupt Cause Set */
#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */
#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */
#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */
+#define E1000_ICS_OTHER E1000_ICR_OTHER /* Other Interrupt */
/* Transmit Descriptor Control */
#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 0b748d1959d9..1dc293bad87b 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -480,7 +480,7 @@ extern const char e1000e_driver_version[];
void e1000e_check_options(struct e1000_adapter *adapter);
void e1000e_set_ethtool_ops(struct net_device *netdev);
-int e1000e_up(struct e1000_adapter *adapter);
+void e1000e_up(struct e1000_adapter *adapter);
void e1000e_down(struct e1000_adapter *adapter, bool reset);
void e1000e_reinit_locked(struct e1000_adapter *adapter);
void e1000e_reset(struct e1000_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h
index c9da4654e9ca..b3949d5bef5c 100644
--- a/drivers/net/ethernet/intel/e1000e/hw.h
+++ b/drivers/net/ethernet/intel/e1000e/hw.h
@@ -91,6 +91,7 @@ struct e1000_hw;
#define E1000_DEV_ID_PCH_SPT_I219_V 0x1570 /* SPT PCH */
#define E1000_DEV_ID_PCH_SPT_I219_LM2 0x15B7 /* SPT-H PCH */
#define E1000_DEV_ID_PCH_SPT_I219_V2 0x15B8 /* SPT-H PCH */
+#define E1000_DEV_ID_PCH_LBG_I219_LM3 0x15B9 /* LBG PCH */
#define E1000_REVISION_4 4
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 91a5a0ae9cd7..a049e30639a1 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -1984,7 +1984,7 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
int i = 0;
while ((blocked = !(er32(FWSM) & E1000_ICH_FWSM_RSPCIPHY)) &&
- (i++ < 10))
+ (i++ < 30))
usleep_range(10000, 20000);
return blocked ? E1000_BLK_PHY_RESET : 0;
}
@@ -3093,24 +3093,45 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
struct e1000_nvm_info *nvm = &hw->nvm;
u32 bank1_offset = nvm->flash_bank_size * sizeof(u16);
u32 act_offset = E1000_ICH_NVM_SIG_WORD * 2 + 1;
+ u32 nvm_dword = 0;
u8 sig_byte = 0;
s32 ret_val;
switch (hw->mac.type) {
- /* In SPT, read from the CTRL_EXT reg instead of
- * accessing the sector valid bits from the nvm
- */
case e1000_pch_spt:
- *bank = er32(CTRL_EXT)
- & E1000_CTRL_EXT_NVMVS;
- if ((*bank == 0) || (*bank == 1)) {
- e_dbg("ERROR: No valid NVM bank present\n");
- return -E1000_ERR_NVM;
- } else {
- *bank = *bank - 2;
+ bank1_offset = nvm->flash_bank_size;
+ act_offset = E1000_ICH_NVM_SIG_WORD;
+
+ /* set bank to 0 in case flash read fails */
+ *bank = 0;
+
+ /* Check bank 0 */
+ ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset,
+ &nvm_dword);
+ if (ret_val)
+ return ret_val;
+ sig_byte = (u8)((nvm_dword & 0xFF00) >> 8);
+ if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+ E1000_ICH_NVM_SIG_VALUE) {
+ *bank = 0;
return 0;
}
- break;
+
+ /* Check bank 1 */
+ ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset +
+ bank1_offset,
+ &nvm_dword);
+ if (ret_val)
+ return ret_val;
+ sig_byte = (u8)((nvm_dword & 0xFF00) >> 8);
+ if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+ E1000_ICH_NVM_SIG_VALUE) {
+ *bank = 1;
+ return 0;
+ }
+
+ e_dbg("ERROR: No valid NVM bank present\n");
+ return -E1000_ERR_NVM;
case e1000_ich8lan:
case e1000_ich9lan:
eecd = er32(EECD);
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 0a854a47d31a..c71ba1bfc1ec 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -1905,30 +1905,15 @@ static irqreturn_t e1000_msix_other(int __always_unused irq, void *data)
struct net_device *netdev = data;
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- u32 icr = er32(ICR);
-
- if (!(icr & E1000_ICR_INT_ASSERTED)) {
- if (!test_bit(__E1000_DOWN, &adapter->state))
- ew32(IMS, E1000_IMS_OTHER);
- return IRQ_NONE;
- }
- if (icr & adapter->eiac_mask)
- ew32(ICS, (icr & adapter->eiac_mask));
+ hw->mac.get_link_status = true;
- if (icr & E1000_ICR_OTHER) {
- if (!(icr & E1000_ICR_LSC))
- goto no_link_interrupt;
- hw->mac.get_link_status = true;
- /* guard against interrupt when we're going down */
- if (!test_bit(__E1000_DOWN, &adapter->state))
- mod_timer(&adapter->watchdog_timer, jiffies + 1);
+ /* guard against interrupt when we're going down */
+ if (!test_bit(__E1000_DOWN, &adapter->state)) {
+ mod_timer(&adapter->watchdog_timer, jiffies + 1);
+ ew32(IMS, E1000_IMS_OTHER);
}
-no_link_interrupt:
- if (!test_bit(__E1000_DOWN, &adapter->state))
- ew32(IMS, E1000_IMS_LSC | E1000_IMS_OTHER);
-
return IRQ_HANDLED;
}
@@ -1946,6 +1931,9 @@ static irqreturn_t e1000_intr_msix_tx(int __always_unused irq, void *data)
/* Ring was not completely cleaned, so fire another interrupt */
ew32(ICS, tx_ring->ims_val);
+ if (!test_bit(__E1000_DOWN, &adapter->state))
+ ew32(IMS, adapter->tx_ring->ims_val);
+
return IRQ_HANDLED;
}
@@ -1959,8 +1947,10 @@ static irqreturn_t e1000_intr_msix_rx(int __always_unused irq, void *data)
* previous interrupt.
*/
if (rx_ring->set_itr) {
- writel(1000000000 / (rx_ring->itr_val * 256),
- rx_ring->itr_register);
+ u32 itr = rx_ring->itr_val ?
+ 1000000000 / (rx_ring->itr_val * 256) : 0;
+
+ writel(itr, rx_ring->itr_register);
rx_ring->set_itr = 0;
}
@@ -2025,6 +2015,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)
hw->hw_addr + E1000_EITR_82574(vector));
else
writel(1, hw->hw_addr + E1000_EITR_82574(vector));
+ adapter->eiac_mask |= E1000_IMS_OTHER;
/* Cause Tx interrupts on every write back */
ivar |= (1 << 31);
@@ -2032,12 +2023,8 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)
ew32(IVAR, ivar);
/* enable MSI-X PBA support */
- ctrl_ext = er32(CTRL_EXT);
- ctrl_ext |= E1000_CTRL_EXT_PBA_CLR;
-
- /* Auto-Mask Other interrupts upon ICR read */
- ew32(IAM, ~E1000_EIAC_MASK_82574 | E1000_IMS_OTHER);
- ctrl_ext |= E1000_CTRL_EXT_EIAME;
+ ctrl_ext = er32(CTRL_EXT) & ~E1000_CTRL_EXT_IAME;
+ ctrl_ext |= E1000_CTRL_EXT_PBA_CLR | E1000_CTRL_EXT_EIAME;
ew32(CTRL_EXT, ctrl_ext);
e1e_flush();
}
@@ -2253,7 +2240,7 @@ static void e1000_irq_enable(struct e1000_adapter *adapter)
if (adapter->msix_entries) {
ew32(EIAC_82574, adapter->eiac_mask & E1000_EIAC_MASK_82574);
- ew32(IMS, adapter->eiac_mask | E1000_IMS_OTHER | E1000_IMS_LSC);
+ ew32(IMS, adapter->eiac_mask | E1000_IMS_LSC);
} else if ((hw->mac.type == e1000_pch_lpt) ||
(hw->mac.type == e1000_pch_spt)) {
ew32(IMS, IMS_ENABLE_MASK | E1000_IMS_ECCER);
@@ -4144,10 +4131,24 @@ void e1000e_reset(struct e1000_adapter *adapter)
}
-int e1000e_up(struct e1000_adapter *adapter)
+/**
+ * e1000e_trigger_lsc - trigger an LSC interrupt
+ * @adapter:
+ *
+ * Fire a link status change interrupt to start the watchdog.
+ **/
+static void e1000e_trigger_lsc(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
+ if (adapter->msix_entries)
+ ew32(ICS, E1000_ICS_OTHER);
+ else
+ ew32(ICS, E1000_ICS_LSC);
+}
+
+void e1000e_up(struct e1000_adapter *adapter)
+{
/* hardware has been reset, we need to reload some things */
e1000_configure(adapter);
@@ -4159,13 +4160,7 @@ int e1000e_up(struct e1000_adapter *adapter)
netif_start_queue(adapter->netdev);
- /* fire a link change interrupt to start the watchdog */
- if (adapter->msix_entries)
- ew32(ICS, E1000_ICS_LSC | E1000_ICR_OTHER);
- else
- ew32(ICS, E1000_ICS_LSC);
-
- return 0;
+ e1000e_trigger_lsc(adapter);
}
static void e1000e_flush_descriptors(struct e1000_adapter *adapter)
@@ -4590,11 +4585,7 @@ static int e1000_open(struct net_device *netdev)
hw->mac.get_link_status = true;
pm_runtime_put(&pdev->dev);
- /* fire a link status change interrupt to start the watchdog */
- if (adapter->msix_entries)
- ew32(ICS, E1000_ICS_LSC | E1000_ICR_OTHER);
- else
- ew32(ICS, E1000_ICS_LSC);
+ e1000e_trigger_lsc(adapter);
return 0;
@@ -6631,7 +6622,7 @@ static int e1000e_pm_runtime_resume(struct device *dev)
return rc;
if (netdev->flags & IFF_UP)
- rc = e1000e_up(adapter);
+ e1000e_up(adapter);
return rc;
}
@@ -6822,13 +6813,8 @@ static void e1000_io_resume(struct pci_dev *pdev)
e1000_init_manageability_pt(adapter);
- if (netif_running(netdev)) {
- if (e1000e_up(adapter)) {
- dev_err(&pdev->dev,
- "can't bring device back up after reset\n");
- return;
- }
- }
+ if (netif_running(netdev))
+ e1000e_up(adapter);
netif_device_attach(netdev);
@@ -7465,6 +7451,7 @@ static const struct pci_device_id e1000_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V), board_pch_spt },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM2), board_pch_spt },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V2), board_pch_spt },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LBG_I219_LM3), board_pch_spt },
{ 0, 0, 0, 0, 0, 0, 0 } /* terminate list */
};
@@ -7504,14 +7491,11 @@ static struct pci_driver e1000_driver = {
**/
static int __init e1000_init_module(void)
{
- int ret;
-
pr_info("Intel(R) PRO/1000 Network Driver - %s\n",
e1000e_driver_version);
pr_info("Copyright(c) 1999 - 2015 Intel Corporation.\n");
- ret = pci_register_driver(&e1000_driver);
- return ret;
+ return pci_register_driver(&e1000_driver);
}
module_init(e1000_init_module);
diff --git a/drivers/net/ethernet/intel/fm10k/Makefile b/drivers/net/ethernet/intel/fm10k/Makefile
index 08859dd220a8..b006ff66d028 100644
--- a/drivers/net/ethernet/intel/fm10k/Makefile
+++ b/drivers/net/ethernet/intel/fm10k/Makefile
@@ -1,7 +1,7 @@
################################################################################
#
# Intel Ethernet Switch Host Interface Driver
-# Copyright(c) 2013 - 2014 Intel Corporation.
+# Copyright(c) 2013 - 2015 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,
@@ -27,7 +27,17 @@
obj-$(CONFIG_FM10K) += fm10k.o
-fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \
- fm10k_netdev.o fm10k_ethtool.o fm10k_pf.o fm10k_vf.o \
- fm10k_mbx.o fm10k_iov.o fm10k_tlv.o \
- fm10k_debugfs.o fm10k_ptp.o fm10k_dcbnl.o
+fm10k-y := fm10k_main.o \
+ fm10k_common.o \
+ fm10k_pci.o \
+ fm10k_ptp.o \
+ fm10k_netdev.o \
+ fm10k_ethtool.o \
+ fm10k_pf.o \
+ fm10k_vf.o \
+ fm10k_mbx.o \
+ fm10k_iov.o \
+ fm10k_tlv.o
+
+fm10k-$(CONFIG_DEBUG_FS) += fm10k_debugfs.o
+fm10k-$(CONFIG_DCB) += fm10k_dcbnl.o
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h
index 14440200499b..b34bb008b104 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k.h
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/etherdevice.h>
+#include <linux/cpumask.h>
#include <linux/rtnetlink.h>
#include <linux/if_vlan.h>
#include <linux/pci.h>
@@ -33,7 +34,7 @@
#include "fm10k_pf.h"
#include "fm10k_vf.h"
-#define FM10K_MAX_JUMBO_FRAME_SIZE 15358 /* Maximum supported size 15K */
+#define FM10K_MAX_JUMBO_FRAME_SIZE 15342 /* Maximum supported size 15K */
#define MAX_QUEUES FM10K_MAX_QUEUES_PF
@@ -66,6 +67,7 @@ struct fm10k_l2_accel {
enum fm10k_ring_state_t {
__FM10K_TX_DETECT_HANG,
__FM10K_HANG_CHECK_ARMED,
+ __FM10K_TX_XPS_INIT_DONE,
};
#define check_for_tx_hang(ring) \
@@ -138,7 +140,7 @@ struct fm10k_ring {
* different for DCB and RSS modes
*/
u8 qos_pc; /* priority class of queue */
- u16 vid; /* default vlan ID of queue */
+ u16 vid; /* default VLAN ID of queue */
u16 count; /* amount of descriptors */
u16 next_to_alloc;
@@ -164,14 +166,20 @@ struct fm10k_ring_container {
unsigned int total_packets; /* total packets processed this int */
u16 work_limit; /* total work allowed per interrupt */
u16 itr; /* interrupt throttle rate value */
+ u8 itr_scale; /* ITR adjustment based on PCI speed */
u8 count; /* total number of rings in vector */
};
#define FM10K_ITR_MAX 0x0FFF /* maximum value for ITR */
#define FM10K_ITR_10K 100 /* 100us */
#define FM10K_ITR_20K 50 /* 50us */
+#define FM10K_ITR_40K 25 /* 25us */
#define FM10K_ITR_ADAPTIVE 0x8000 /* adaptive interrupt moderation flag */
+#define ITR_IS_ADAPTIVE(itr) (!!(itr & FM10K_ITR_ADAPTIVE))
+
+#define FM10K_TX_ITR_DEFAULT FM10K_ITR_40K
+#define FM10K_RX_ITR_DEFAULT FM10K_ITR_20K
#define FM10K_ITR_ENABLE (FM10K_ITR_AUTOMASK | FM10K_ITR_MASK_CLEAR)
static inline struct netdev_queue *txring_txq(const struct fm10k_ring *ring)
@@ -203,6 +211,7 @@ struct fm10k_q_vector {
struct fm10k_ring_container rx, tx;
struct napi_struct napi;
+ cpumask_t affinity_mask;
char name[IFNAMSIZ + 9];
#ifdef CONFIG_DEBUG_FS
@@ -413,7 +422,7 @@ static inline u16 fm10k_desc_unused(struct fm10k_ring *ring)
(&(((union fm10k_rx_desc *)((R)->desc))[i]))
#define FM10K_MAX_TXD_PWR 14
-#define FM10K_MAX_DATA_PER_TXD (1 << FM10K_MAX_TXD_PWR)
+#define FM10K_MAX_DATA_PER_TXD BIT(FM10K_MAX_TXD_PWR)
/* Tx Descriptors needed, worst case */
#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), FM10K_MAX_DATA_PER_TXD)
@@ -434,7 +443,7 @@ union fm10k_ftag_info {
struct {
/* dglort and sglort combined into a single 32bit desc read */
__le32 glort;
- /* upper 16 bits of vlan are reserved 0 for swpri_type_user */
+ /* upper 16 bits of VLAN are reserved 0 for swpri_type_user */
__le32 vlan;
} d;
struct {
@@ -484,7 +493,7 @@ void fm10k_netpoll(struct net_device *netdev);
#endif
/* Netdev */
-struct net_device *fm10k_alloc_netdev(void);
+struct net_device *fm10k_alloc_netdev(const struct fm10k_info *info);
int fm10k_setup_rx_resources(struct fm10k_ring *);
int fm10k_setup_tx_resources(struct fm10k_ring *);
void fm10k_free_rx_resources(struct fm10k_ring *);
@@ -551,5 +560,9 @@ int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
/* DCB */
+#ifdef CONFIG_DCB
void fm10k_dcbnl_set_ops(struct net_device *dev);
+#else
+static inline void fm10k_dcbnl_set_ops(struct net_device *dev) {}
+#endif
#endif /* _FM10K_H_ */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
index 5c7a4d7662d8..2be4361839db 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
@@ -20,7 +20,6 @@
#include "fm10k.h"
-#ifdef CONFIG_DCB
/**
* fm10k_dcbnl_ieee_getets - get the ETS configuration for the device
* @dev: netdev interface for the device
@@ -155,7 +154,6 @@ static const struct dcbnl_rtnl_ops fm10k_dcbnl_ops = {
.setdcbx = fm10k_dcbnl_setdcbx,
};
-#endif /* CONFIG_DCB */
/**
* fm10k_dcbnl_set_ops - Configures dcbnl ops pointer for netdev
* @dev: netdev interface for the device
@@ -164,11 +162,9 @@ static const struct dcbnl_rtnl_ops fm10k_dcbnl_ops = {
**/
void fm10k_dcbnl_set_ops(struct net_device *dev)
{
-#ifdef CONFIG_DCB
struct fm10k_intfc *interface = netdev_priv(dev);
struct fm10k_hw *hw = &interface->hw;
if (hw->mac.type == fm10k_mac_pf)
dev->dcbnl_ops = &fm10k_dcbnl_ops;
-#endif /* CONFIG_DCB */
}
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
index 5304bc1fbecd..5d6137faf7d1 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
@@ -18,8 +18,6 @@
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*/
-#ifdef CONFIG_DEBUG_FS
-
#include "fm10k.h"
#include <linux/debugfs.h>
@@ -258,5 +256,3 @@ void fm10k_dbg_exit(void)
debugfs_remove_recursive(dbg_root);
dbg_root = NULL;
}
-
-#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index 2ce0eba5e040..2f6a05b57228 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -111,12 +111,14 @@ static const struct fm10k_stats fm10k_gstrings_pf_stats[] = {
static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = {
FM10K_MBX_STAT("mbx_tx_busy", tx_busy),
- FM10K_MBX_STAT("mbx_tx_oversized", tx_dropped),
+ FM10K_MBX_STAT("mbx_tx_dropped", tx_dropped),
FM10K_MBX_STAT("mbx_tx_messages", tx_messages),
FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords),
+ FM10K_MBX_STAT("mbx_tx_mbmem_pulled", tx_mbmem_pulled),
FM10K_MBX_STAT("mbx_rx_messages", rx_messages),
FM10K_MBX_STAT("mbx_rx_dwords", rx_dwords),
FM10K_MBX_STAT("mbx_rx_parse_err", rx_parse_err),
+ FM10K_MBX_STAT("mbx_rx_mbmem_pushed", rx_mbmem_pushed),
};
#define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_global_stats)
@@ -125,7 +127,7 @@ static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = {
#define FM10K_MBX_STATS_LEN ARRAY_SIZE(fm10k_gstrings_mbx_stats)
#define FM10K_QUEUE_STATS_LEN(_n) \
- ( (_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64)))
+ ((_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64)))
#define FM10K_STATIC_STATS_LEN (FM10K_GLOBAL_STATS_LEN + \
FM10K_NETDEV_STATS_LEN + \
@@ -257,7 +259,8 @@ static int fm10k_get_sset_count(struct net_device *dev, int sset)
stats_len += FM10K_DEBUG_STATS_LEN;
if (iov_data)
- stats_len += FM10K_MBX_STATS_LEN * iov_data->num_vfs;
+ stats_len += FM10K_MBX_STATS_LEN *
+ iov_data->num_vfs;
}
return stats_len;
@@ -296,14 +299,16 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
if (interface->flags & FM10K_FLAG_DEBUG_STATS) {
for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) {
- p = (char *)interface + fm10k_gstrings_debug_stats[i].stat_offset;
+ p = (char *)interface +
+ fm10k_gstrings_debug_stats[i].stat_offset;
*(data++) = (fm10k_gstrings_debug_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
}
for (i = 0; i < FM10K_MBX_STATS_LEN; i++) {
- p = (char *)&interface->hw.mbx + fm10k_gstrings_mbx_stats[i].stat_offset;
+ p = (char *)&interface->hw.mbx +
+ fm10k_gstrings_mbx_stats[i].stat_offset;
*(data++) = (fm10k_gstrings_mbx_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
@@ -320,6 +325,7 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) {
for (i = 0; i < iov_data->num_vfs; i++) {
struct fm10k_vf_info *vf_info;
+
vf_info = &iov_data->vf_info[i];
/* skip stats if we don't have a vf info */
@@ -329,7 +335,8 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
}
for (j = 0; j < FM10K_MBX_STATS_LEN; j++) {
- p = (char *)&vf_info->mbx + fm10k_gstrings_mbx_stats[j].stat_offset;
+ p = (char *)&vf_info->mbx +
+ fm10k_gstrings_mbx_stats[j].stat_offset;
*(data++) = (fm10k_gstrings_mbx_stats[j].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
@@ -699,12 +706,10 @@ static int fm10k_get_coalesce(struct net_device *dev,
{
struct fm10k_intfc *interface = netdev_priv(dev);
- ec->use_adaptive_tx_coalesce =
- !!(interface->tx_itr & FM10K_ITR_ADAPTIVE);
+ ec->use_adaptive_tx_coalesce = ITR_IS_ADAPTIVE(interface->tx_itr);
ec->tx_coalesce_usecs = interface->tx_itr & ~FM10K_ITR_ADAPTIVE;
- ec->use_adaptive_rx_coalesce =
- !!(interface->rx_itr & FM10K_ITR_ADAPTIVE);
+ ec->use_adaptive_rx_coalesce = ITR_IS_ADAPTIVE(interface->rx_itr);
ec->rx_coalesce_usecs = interface->rx_itr & ~FM10K_ITR_ADAPTIVE;
return 0;
@@ -729,10 +734,10 @@ static int fm10k_set_coalesce(struct net_device *dev,
/* set initial values for adaptive ITR */
if (ec->use_adaptive_tx_coalesce)
- tx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_10K;
+ tx_itr = FM10K_ITR_ADAPTIVE | FM10K_TX_ITR_DEFAULT;
if (ec->use_adaptive_rx_coalesce)
- rx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_20K;
+ rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT;
/* update interface */
interface->tx_itr = tx_itr;
@@ -1020,7 +1025,6 @@ static int fm10k_set_priv_flags(struct net_device *netdev, u32 priv_flags)
return 0;
}
-
static u32 fm10k_get_reta_size(struct net_device __always_unused *netdev)
{
return FM10K_RETA_SIZE * FM10K_RETA_ENTRIES_PER_REG;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
index e76a44cf330c..b243c3cbe68f 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
@@ -28,7 +28,7 @@
#include "fm10k.h"
-#define DRV_VERSION "0.15.2-k"
+#define DRV_VERSION "0.19.3-k"
const char fm10k_driver_version[] = DRV_VERSION;
char fm10k_driver_name[] = "fm10k";
static const char fm10k_driver_string[] =
@@ -42,7 +42,7 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
/* single workqueue for entire fm10k driver */
-struct workqueue_struct *fm10k_workqueue = NULL;
+struct workqueue_struct *fm10k_workqueue;
/**
* fm10k_init_module - Driver Registration Routine
@@ -56,8 +56,7 @@ static int __init fm10k_init_module(void)
pr_info("%s\n", fm10k_copyright);
/* create driver workqueue */
- if (!fm10k_workqueue)
- fm10k_workqueue = create_workqueue("fm10k");
+ fm10k_workqueue = create_workqueue("fm10k");
fm10k_dbg_init();
@@ -80,7 +79,6 @@ static void __exit fm10k_exit_module(void)
/* destroy driver workqueue */
flush_workqueue(fm10k_workqueue);
destroy_workqueue(fm10k_workqueue);
- fm10k_workqueue = NULL;
}
module_exit(fm10k_exit_module);
@@ -917,7 +915,7 @@ static u8 fm10k_tx_desc_flags(struct sk_buff *skb, u32 tx_flags)
/* set timestamping bits */
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
likely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
- desc_flags |= FM10K_TXD_FLAG_TIME;
+ desc_flags |= FM10K_TXD_FLAG_TIME;
/* set checksum offload bits */
desc_flags |= FM10K_SET_FLAG(tx_flags, FM10K_TX_FLAGS_CSUM,
@@ -1094,11 +1092,11 @@ dma_error:
netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb,
struct fm10k_ring *tx_ring)
{
+ u16 count = TXD_USE_COUNT(skb_headlen(skb));
struct fm10k_tx_buffer *first;
- int tso;
- u32 tx_flags = 0;
unsigned short f;
- u16 count = TXD_USE_COUNT(skb_headlen(skb));
+ u32 tx_flags = 0;
+ int tso;
/* need: 1 descriptor per page * PAGE_SIZE/FM10K_MAX_DATA_PER_TXD,
* + 1 desc for skb_headlen/FM10K_MAX_DATA_PER_TXD,
@@ -1363,10 +1361,10 @@ static bool fm10k_clean_tx_irq(struct fm10k_q_vector *q_vector,
**/
static void fm10k_update_itr(struct fm10k_ring_container *ring_container)
{
- unsigned int avg_wire_size, packets;
+ unsigned int avg_wire_size, packets, itr_round;
/* Only update ITR if we are using adaptive setting */
- if (!(ring_container->itr & FM10K_ITR_ADAPTIVE))
+ if (!ITR_IS_ADAPTIVE(ring_container->itr))
goto clear_counts;
packets = ring_container->total_packets;
@@ -1375,18 +1373,44 @@ static void fm10k_update_itr(struct fm10k_ring_container *ring_container)
avg_wire_size = ring_container->total_bytes / packets;
- /* Add 24 bytes to size to account for CRC, preamble, and gap */
- avg_wire_size += 24;
-
- /* Don't starve jumbo frames */
- if (avg_wire_size > 3000)
- avg_wire_size = 3000;
+ /* The following is a crude approximation of:
+ * wmem_default / (size + overhead) = desired_pkts_per_int
+ * rate / bits_per_byte / (size + ethernet overhead) = pkt_rate
+ * (desired_pkt_rate / pkt_rate) * usecs_per_sec = ITR value
+ *
+ * Assuming wmem_default is 212992 and overhead is 640 bytes per
+ * packet, (256 skb, 64 headroom, 320 shared info), we can reduce the
+ * formula down to
+ *
+ * (34 * (size + 24)) / (size + 640) = ITR
+ *
+ * We first do some math on the packet size and then finally bitshift
+ * by 8 after rounding up. We also have to account for PCIe link speed
+ * difference as ITR scales based on this.
+ */
+ if (avg_wire_size <= 360) {
+ /* Start at 250K ints/sec and gradually drop to 77K ints/sec */
+ avg_wire_size *= 8;
+ avg_wire_size += 376;
+ } else if (avg_wire_size <= 1152) {
+ /* 77K ints/sec to 45K ints/sec */
+ avg_wire_size *= 3;
+ avg_wire_size += 2176;
+ } else if (avg_wire_size <= 1920) {
+ /* 45K ints/sec to 38K ints/sec */
+ avg_wire_size += 4480;
+ } else {
+ /* plateau at a limit of 38K ints/sec */
+ avg_wire_size = 6656;
+ }
- /* Give a little boost to mid-size frames */
- if ((avg_wire_size > 300) && (avg_wire_size < 1200))
- avg_wire_size /= 3;
- else
- avg_wire_size /= 2;
+ /* Perform final bitshift for division after rounding up to ensure
+ * that the calculation will never get below a 1. The bit shift
+ * accounts for changes in the ITR due to PCIe link speed.
+ */
+ itr_round = ACCESS_ONCE(ring_container->itr_scale) + 8;
+ avg_wire_size += (1 << itr_round) - 1;
+ avg_wire_size >>= itr_round;
/* write back value and retain adaptive flag */
ring_container->itr = avg_wire_size | FM10K_ITR_ADAPTIVE;
@@ -1428,11 +1452,15 @@ static int fm10k_poll(struct napi_struct *napi, int budget)
fm10k_for_each_ring(ring, q_vector->tx)
clean_complete &= fm10k_clean_tx_irq(q_vector, ring);
+ /* Handle case where we are called by netpoll with a budget of 0 */
+ if (budget <= 0)
+ return budget;
+
/* attempt to distribute budget to each queue fairly, but don't
* allow the budget to go below 1 because we'll exit polling
*/
if (q_vector->rx.count > 1)
- per_ring_budget = max(budget/q_vector->rx.count, 1);
+ per_ring_budget = max(budget / q_vector->rx.count, 1);
else
per_ring_budget = budget;
@@ -1600,6 +1628,7 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface,
q_vector->tx.ring = ring;
q_vector->tx.work_limit = FM10K_DEFAULT_TX_WORK;
q_vector->tx.itr = interface->tx_itr;
+ q_vector->tx.itr_scale = interface->hw.mac.itr_scale;
q_vector->tx.count = txr_count;
while (txr_count) {
@@ -1628,6 +1657,7 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface,
/* save Rx ring container info */
q_vector->rx.ring = ring;
q_vector->rx.itr = interface->rx_itr;
+ q_vector->rx.itr_scale = interface->hw.mac.itr_scale;
q_vector->rx.count = rxr_count;
while (rxr_count) {
@@ -1966,8 +1996,10 @@ int fm10k_init_queueing_scheme(struct fm10k_intfc *interface)
/* Allocate memory for queues */
err = fm10k_alloc_q_vectors(interface);
- if (err)
+ if (err) {
+ fm10k_reset_msix_capability(interface);
return err;
+ }
/* Map rings to devices, and map devices to physical queues */
fm10k_assign_rings(interface);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
index af09a1b272e6..98202c3d591c 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
@@ -57,7 +57,7 @@ static u16 fm10k_fifo_unused(struct fm10k_mbx_fifo *fifo)
}
/**
- * fm10k_fifo_empty - Test to verify if fifo is empty
+ * fm10k_fifo_empty - Test to verify if FIFO is empty
* @fifo: pointer to FIFO
*
* This function returns true if the FIFO is empty, else false
@@ -72,7 +72,7 @@ static bool fm10k_fifo_empty(struct fm10k_mbx_fifo *fifo)
* @fifo: pointer to FIFO
* @offset: offset to add to head
*
- * This function returns the indices into the fifo based on head + offset
+ * This function returns the indices into the FIFO based on head + offset
**/
static u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
{
@@ -84,7 +84,7 @@ static u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
* @fifo: pointer to FIFO
* @offset: offset to add to tail
*
- * This function returns the indices into the fifo based on tail + offset
+ * This function returns the indices into the FIFO based on tail + offset
**/
static u16 fm10k_fifo_tail_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
{
@@ -160,7 +160,7 @@ static u16 fm10k_mbx_index_len(struct fm10k_mbx_info *mbx, u16 head, u16 tail)
/**
* fm10k_mbx_tail_add - Determine new tail value with added offset
* @mbx: pointer to mailbox
- * @offset: length to add to head offset
+ * @offset: length to add to tail offset
*
* This function takes the local tail index and recomputes it for
* a given length added as an offset.
@@ -176,7 +176,7 @@ static u16 fm10k_mbx_tail_add(struct fm10k_mbx_info *mbx, u16 offset)
/**
* fm10k_mbx_tail_sub - Determine new tail value with subtracted offset
* @mbx: pointer to mailbox
- * @offset: length to add to head offset
+ * @offset: length to add to tail offset
*
* This function takes the local tail index and recomputes it for
* a given length added as an offset.
@@ -240,7 +240,7 @@ static u16 fm10k_mbx_pushed_tail_len(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_fifo_write_copy - pulls data off of msg and places it in fifo
+ * fm10k_fifo_write_copy - pulls data off of msg and places it in FIFO
* @fifo: pointer to FIFO
* @msg: message array to populate
* @tail_offset: additional offset to add to tail pointer
@@ -336,6 +336,7 @@ static u16 fm10k_mbx_validate_msg_size(struct fm10k_mbx_info *mbx, u16 len)
/**
* fm10k_mbx_write_copy - pulls data off of Tx FIFO and places it in mbmem
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will take a section of the Tx FIFO and copy it into the
@@ -375,6 +376,8 @@ static void fm10k_mbx_write_copy(struct fm10k_hw *hw,
if (!tail)
tail++;
+ mbx->tx_mbmem_pulled++;
+
/* write message to hardware FIFO */
fm10k_write_reg(hw, mbmem + tail++, *(head++));
} while (--len && --end);
@@ -459,6 +462,8 @@ static void fm10k_mbx_read_copy(struct fm10k_hw *hw,
if (!head)
head++;
+ mbx->rx_mbmem_pushed++;
+
/* read message from hardware FIFO */
*(tail++) = fm10k_read_reg(hw, mbmem + head++);
} while (--len && --end);
@@ -707,7 +712,7 @@ static bool fm10k_mbx_tx_complete(struct fm10k_mbx_info *mbx)
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
- * This function dequeues messages and hands them off to the tlv parser.
+ * This function dequeues messages and hands them off to the TLV parser.
* It will return the number of messages processed when called.
**/
static u16 fm10k_mbx_dequeue_rx(struct fm10k_hw *hw,
@@ -899,7 +904,7 @@ static void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mailbox header
+ * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mbox hdr
* @mbx: pointer to mailbox
*
* This function creates a fake disconnect header for loading into remote
@@ -920,7 +925,7 @@ static void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_mbx_create_error_msg - Generate a error message
+ * fm10k_mbx_create_error_msg - Generate an error message
* @mbx: pointer to mailbox
* @err: local error encountered
*
@@ -953,7 +958,6 @@ static void fm10k_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err)
/**
* fm10k_mbx_validate_msg_hdr - Validate common fields in the message header
* @mbx: pointer to mailbox
- * @msg: message array to read
*
* This function will parse up the fields in the mailbox header and return
* an error if the header contains any of a number of invalid configurations
@@ -1017,11 +1021,12 @@ static s32 fm10k_mbx_validate_msg_hdr(struct fm10k_mbx_info *mbx)
/**
* fm10k_mbx_create_reply - Generate reply based on state and remote head
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
* @head: acknowledgement number
*
* This function will generate an outgoing message based on the current
- * mailbox state and the remote fifo head. It will return the length
+ * mailbox state and the remote FIFO head. It will return the length
* of the outgoing message excluding header on success, and a negative value
* on error.
**/
@@ -1147,8 +1152,8 @@ static void fm10k_mbx_connect_reset(struct fm10k_mbx_info *mbx)
/**
* fm10k_mbx_process_connect - Process connect header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
- * @msg: message array to process
*
* This function will read an incoming connect header and reply with the
* appropriate message. It will return a value indicating the number of
@@ -1194,6 +1199,7 @@ static s32 fm10k_mbx_process_connect(struct fm10k_hw *hw,
/**
* fm10k_mbx_process_data - Process data header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will read an incoming data header and reply with the
@@ -1235,6 +1241,7 @@ static s32 fm10k_mbx_process_data(struct fm10k_hw *hw,
/**
* fm10k_mbx_process_disconnect - Process disconnect header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will read an incoming disconnect header and reply with the
@@ -1287,6 +1294,7 @@ static s32 fm10k_mbx_process_disconnect(struct fm10k_hw *hw,
/**
* fm10k_mbx_process_error - Process error header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will read an incoming error header and reply with the
@@ -1556,7 +1564,7 @@ static s32 fm10k_mbx_register_handlers(struct fm10k_mbx_info *mbx,
* @id: ID reference for PF as it supports up to 64 PF/VF mailboxes
*
* This function initializes the mailbox for use. It will split the
- * buffer provided an use that th populate both the Tx and Rx FIFO by
+ * buffer provided and use that to populate both the Tx and Rx FIFO by
* evenly splitting it. In order to allow for easy masking of head/tail
* the value reported in size must be a power of 2 and is reported in
* DWORDs, not bytes. Any invalid values will cause the mailbox to return
@@ -1633,7 +1641,7 @@ s32 fm10k_pfvf_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx,
* fm10k_sm_mbx_create_data_hdr - Generate a mailbox header for local FIFO
* @mbx: pointer to mailbox
*
- * This function returns a connection mailbox header
+ * This function returns a data mailbox header
**/
static void fm10k_sm_mbx_create_data_hdr(struct fm10k_mbx_info *mbx)
{
@@ -1726,8 +1734,6 @@ static s32 fm10k_sm_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx)
fm10k_sm_mbx_create_connect_hdr(mbx, 0);
fm10k_mbx_write(hw, mbx);
- /* enable interrupt and notify other party of new message */
-
return 0;
}
@@ -1771,7 +1777,7 @@ static void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw,
}
/**
- * fm10k_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header
+ * fm10k_sm_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header
* @mbx: pointer to mailbox
*
* This function will parse up the fields in the mailbox header and return
@@ -1849,7 +1855,7 @@ static void fm10k_sm_mbx_process_error(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_sm_mbx_create_error_message - Process an error in FIFO hdr
+ * fm10k_sm_mbx_create_error_msg - Process an error in FIFO header
* @mbx: pointer to mailbox
* @err: local error encountered
*
@@ -1879,6 +1885,7 @@ static void fm10k_sm_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err)
* fm10k_sm_mbx_receive - Take message from Rx mailbox FIFO and put it in Rx
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
+ * @tail: tail index of message
*
* This function will dequeue one message from the Rx switch manager mailbox
* FIFO and place it in the Rx mailbox FIFO for processing by software.
@@ -1918,6 +1925,7 @@ static s32 fm10k_sm_mbx_receive(struct fm10k_hw *hw,
* fm10k_sm_mbx_transmit - Take message from Tx and put it in Tx mailbox FIFO
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
+ * @head: head index of message
*
* This function will dequeue one message from the Tx mailbox FIFO and place
* it in the Tx switch manager mailbox FIFO for processing by hardware.
@@ -1957,11 +1965,12 @@ static void fm10k_sm_mbx_transmit(struct fm10k_hw *hw,
/**
* fm10k_sm_mbx_create_reply - Generate reply based on state and remote head
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
* @head: acknowledgement number
*
* This function will generate an outgoing message based on the current
- * mailbox state and the remote fifo head. It will return the length
+ * mailbox state and the remote FIFO head. It will return the length
* of the outgoing message excluding header on success, and a negative value
* on error.
**/
@@ -2073,7 +2082,7 @@ send_reply:
}
/**
- * fm10k_sm_mbx_process - Process mailbox switch mailbox interrupt
+ * fm10k_sm_mbx_process - Process switch manager mailbox interrupt
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
@@ -2129,13 +2138,19 @@ fifo_err:
* @mbx: pointer to mailbox
* @msg_data: handlers for mailbox events
*
- * This function for now is used to stub out the PF/SM mailbox
+ * This function initializes the PF/SM mailbox for use. It will split the
+ * buffer provided and use that to populate both the Tx and Rx FIFO by
+ * evenly splitting it. In order to allow for easy masking of head/tail
+ * the value reported in size must be a power of 2 and is reported in
+ * DWORDs, not bytes. Any invalid values will cause the mailbox to return
+ * error.
**/
s32 fm10k_sm_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx,
const struct fm10k_msg_data *msg_data)
{
mbx->mbx_reg = FM10K_GMBX;
mbx->mbmem_reg = FM10K_MBMEM_PF(0);
+
/* start out in closed state */
mbx->state = FM10K_STATE_CLOSED;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
index 0419a7f0035e..245a0a3dc32e 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * Copyright(c) 2013 - 2015 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,
@@ -128,11 +128,11 @@ enum fm10k_mbx_state {
* The maximum message size is provided during connect to avoid
* jamming the mailbox with messages that do not fit.
* Err_no: Error number - Applies only to error headers
- * The error number provides a indication of the type of error
+ * The error number provides an indication of the type of error
* experienced.
*/
-/* macros for retriving and setting header values */
+/* macros for retrieving and setting header values */
#define FM10K_MSG_HDR_MASK(name) \
((0x1u << FM10K_MSG_##name##_SIZE) - 1)
#define FM10K_MSG_HDR_FIELD_SET(value, name) \
@@ -291,8 +291,10 @@ struct fm10k_mbx_info {
u64 tx_dropped;
u64 tx_messages;
u64 tx_dwords;
+ u64 tx_mbmem_pulled;
u64 rx_messages;
u64 rx_dwords;
+ u64 rx_mbmem_pushed;
u64 rx_parse_err;
/* Buffer to store messages */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
index 7781e80896a6..662569d5b7c0 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
@@ -20,7 +20,7 @@
#include "fm10k.h"
#include <linux/vmalloc.h>
-#if IS_ENABLED(CONFIG_FM10K_VXLAN)
+#ifdef CONFIG_FM10K_VXLAN
#include <net/vxlan.h>
#endif /* CONFIG_FM10K_VXLAN */
@@ -556,11 +556,11 @@ int fm10k_open(struct net_device *netdev)
if (err)
goto err_set_queues;
-#if IS_ENABLED(CONFIG_FM10K_VXLAN)
+#ifdef CONFIG_FM10K_VXLAN
/* update VXLAN port configuration */
vxlan_get_rx_port(netdev);
-
#endif
+
fm10k_up(interface);
return 0;
@@ -608,7 +608,7 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev)
unsigned int r_idx = skb->queue_mapping;
int err;
- if ((skb->protocol == htons(ETH_P_8021Q)) &&
+ if ((skb->protocol == htons(ETH_P_8021Q)) &&
!skb_vlan_tag_present(skb)) {
/* FM10K only supports hardware tagging, any tags in frame
* are considered 2nd level or "outer" tags
@@ -632,7 +632,7 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
- /* locate vlan header */
+ /* locate VLAN header */
vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
/* pull the 2 key pieces of data out of it */
@@ -705,7 +705,7 @@ static void fm10k_tx_timeout(struct net_device *netdev)
} else {
netif_info(interface, drv, netdev,
"Fake Tx hang detected with timeout of %d seconds\n",
- netdev->watchdog_timeo/HZ);
+ netdev->watchdog_timeo / HZ);
/* fake Tx hang - increase the kernel timeout */
if (netdev->watchdog_timeo < TX_TIMEO_LIMIT)
@@ -778,7 +778,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
if (!set)
clear_bit(vid, interface->active_vlans);
- /* disable the default VID on ring if we have an active VLAN */
+ /* disable the default VLAN ID on ring if we have an active VLAN */
for (i = 0; i < interface->num_rx_queues; i++) {
struct fm10k_ring *rx_ring = interface->rx_ring[i];
u16 rx_vid = rx_ring->vid & (VLAN_N_VID - 1);
@@ -789,7 +789,9 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
rx_ring->vid &= ~FM10K_VLAN_CLEAR;
}
- /* Do not remove default VID related entries from VLAN and MAC tables */
+ /* Do not remove default VLAN ID related entries from VLAN and MAC
+ * tables
+ */
if (!set && vid == hw->mac.default_vid)
return 0;
@@ -814,7 +816,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
if (err)
goto err_out;
- /* set vid prior to syncing/unsyncing the VLAN */
+ /* set VLAN ID prior to syncing/unsyncing the VLAN */
interface->vid = vid + (set ? VLAN_N_VID : 0);
/* Update the unicast and multicast address list to add/drop VLAN */
@@ -1151,6 +1153,7 @@ static struct rtnl_link_stats64 *fm10k_get_stats64(struct net_device *netdev,
int fm10k_setup_tc(struct net_device *dev, u8 tc)
{
struct fm10k_intfc *interface = netdev_priv(dev);
+ int err;
/* Currently only the PF supports priority classes */
if (tc && (interface->hw.mac.type != fm10k_mac_pf))
@@ -1175,17 +1178,30 @@ int fm10k_setup_tc(struct net_device *dev, u8 tc)
netdev_reset_tc(dev);
netdev_set_num_tc(dev, tc);
- fm10k_init_queueing_scheme(interface);
+ err = fm10k_init_queueing_scheme(interface);
+ if (err)
+ goto err_queueing_scheme;
- fm10k_mbx_request_irq(interface);
+ err = fm10k_mbx_request_irq(interface);
+ if (err)
+ goto err_mbx_irq;
- if (netif_running(dev))
- fm10k_open(dev);
+ err = netif_running(dev) ? fm10k_open(dev) : 0;
+ if (err)
+ goto err_open;
/* flag to indicate SWPRI has yet to be updated */
interface->flags |= FM10K_FLAG_SWPRI_CONFIG;
return 0;
+err_open:
+ fm10k_mbx_free_irq(interface);
+err_mbx_irq:
+ fm10k_clear_queueing_scheme(interface);
+err_queueing_scheme:
+ netif_device_detach(dev);
+
+ return err;
}
static int fm10k_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
@@ -1355,7 +1371,7 @@ static netdev_features_t fm10k_features_check(struct sk_buff *skb,
if (!skb->encapsulation || fm10k_tx_encap_offload(skb))
return features;
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
}
static const struct net_device_ops fm10k_netdev_ops = {
@@ -1388,8 +1404,9 @@ static const struct net_device_ops fm10k_netdev_ops = {
#define DEFAULT_DEBUG_LEVEL_SHIFT 3
-struct net_device *fm10k_alloc_netdev(void)
+struct net_device *fm10k_alloc_netdev(const struct fm10k_info *info)
{
+ netdev_features_t hw_features;
struct fm10k_intfc *interface;
struct net_device *dev;
@@ -1412,27 +1429,31 @@ struct net_device *fm10k_alloc_netdev(void)
NETIF_F_TSO |
NETIF_F_TSO6 |
NETIF_F_TSO_ECN |
- NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_RXHASH |
NETIF_F_RXCSUM;
+ /* Only the PF can support VXLAN and NVGRE tunnel offloads */
+ if (info->mac == fm10k_mac_pf) {
+ dev->hw_enc_features = NETIF_F_IP_CSUM |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_TSO_ECN |
+ NETIF_F_GSO_UDP_TUNNEL |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_SG;
+
+ dev->features |= NETIF_F_GSO_UDP_TUNNEL;
+ }
+
/* all features defined to this point should be changeable */
- dev->hw_features |= dev->features;
+ hw_features = dev->features;
/* allow user to enable L2 forwarding acceleration */
- dev->hw_features |= NETIF_F_HW_L2FW_DOFFLOAD;
+ hw_features |= NETIF_F_HW_L2FW_DOFFLOAD;
/* configure VLAN features */
dev->vlan_features |= dev->features;
- /* configure tunnel offloads */
- dev->hw_enc_features |= NETIF_F_IP_CSUM |
- NETIF_F_TSO |
- NETIF_F_TSO6 |
- NETIF_F_TSO_ECN |
- NETIF_F_GSO_UDP_TUNNEL |
- NETIF_F_IPV6_CSUM;
-
/* we want to leave these both on as we cannot disable VLAN tag
* insertion or stripping on the hardware since it is contained
* in the FTAG and not in the frame itself.
@@ -1443,5 +1464,7 @@ struct net_device *fm10k_alloc_netdev(void)
dev->priv_flags |= IFF_UNICAST_FLT;
+ dev->hw_features |= hw_features;
+
return dev;
}
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
index 74be792f3f1b..4eb7a6fa6b0d 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
@@ -159,16 +159,40 @@ static void fm10k_reinit(struct fm10k_intfc *interface)
fm10k_mbx_free_irq(interface);
+ /* free interrupts */
+ fm10k_clear_queueing_scheme(interface);
+
/* delay any future reset requests */
interface->last_reset = jiffies + (10 * HZ);
/* reset and initialize the hardware so it is in a known state */
- err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw);
- if (err)
+ err = hw->mac.ops.reset_hw(hw);
+ if (err) {
+ dev_err(&interface->pdev->dev, "reset_hw failed: %d\n", err);
+ goto reinit_err;
+ }
+
+ err = hw->mac.ops.init_hw(hw);
+ if (err) {
dev_err(&interface->pdev->dev, "init_hw failed: %d\n", err);
+ goto reinit_err;
+ }
+
+ err = fm10k_init_queueing_scheme(interface);
+ if (err) {
+ dev_err(&interface->pdev->dev,
+ "init_queueing_scheme failed: %d\n", err);
+ goto reinit_err;
+ }
/* reassociate interrupts */
- fm10k_mbx_request_irq(interface);
+ err = fm10k_mbx_request_irq(interface);
+ if (err)
+ goto err_mbx_irq;
+
+ err = fm10k_hw_ready(interface);
+ if (err)
+ goto err_open;
/* update hardware address for VFs if perm_addr has changed */
if (hw->mac.type == fm10k_mac_vf) {
@@ -188,14 +212,27 @@ static void fm10k_reinit(struct fm10k_intfc *interface)
/* reset clock */
fm10k_ts_reset(interface);
- if (netif_running(netdev))
- fm10k_open(netdev);
+ err = netif_running(netdev) ? fm10k_open(netdev) : 0;
+ if (err)
+ goto err_open;
fm10k_iov_resume(interface->pdev);
rtnl_unlock();
clear_bit(__FM10K_RESETTING, &interface->state);
+
+ return;
+err_open:
+ fm10k_mbx_free_irq(interface);
+err_mbx_irq:
+ fm10k_clear_queueing_scheme(interface);
+reinit_err:
+ netif_device_detach(netdev);
+
+ rtnl_unlock();
+
+ clear_bit(__FM10K_RESETTING, &interface->state);
}
static void fm10k_reset_subtask(struct fm10k_intfc *interface)
@@ -563,7 +600,7 @@ static void fm10k_configure_tx_ring(struct fm10k_intfc *interface,
/* store tail pointer */
ring->tail = &interface->uc_addr[FM10K_TDT(reg_idx)];
- /* reset ntu and ntc to place SW in sync with hardwdare */
+ /* reset ntu and ntc to place SW in sync with hardware */
ring->next_to_clean = 0;
ring->next_to_use = 0;
@@ -579,6 +616,13 @@ static void fm10k_configure_tx_ring(struct fm10k_intfc *interface,
fm10k_write_reg(hw, FM10K_PFVTCTL(reg_idx),
FM10K_PFVTCTL_FTAG_DESC_ENABLE);
+ /* Initialize XPS */
+ if (!test_and_set_bit(__FM10K_TX_XPS_INIT_DONE, &ring->state) &&
+ ring->q_vector)
+ netif_set_xps_queue(ring->netdev,
+ &ring->q_vector->affinity_mask,
+ ring->queue_index);
+
/* enable queue */
fm10k_write_reg(hw, FM10K_TXDCTL(reg_idx), txdctl);
}
@@ -669,7 +713,7 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface,
/* store tail pointer */
ring->tail = &interface->uc_addr[FM10K_RDT(reg_idx)];
- /* reset ntu and ntc to place SW in sync with hardwdare */
+ /* reset ntu and ntc to place SW in sync with hardware */
ring->next_to_clean = 0;
ring->next_to_use = 0;
ring->next_to_alloc = 0;
@@ -694,7 +738,7 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface,
/* assign default VLAN to queue */
ring->vid = hw->mac.default_vid;
- /* if we have an active VLAN, disable default VID */
+ /* if we have an active VLAN, disable default VLAN ID */
if (test_bit(hw->mac.default_vid, interface->active_vlans))
ring->vid |= FM10K_VLAN_CLEAR;
@@ -846,7 +890,7 @@ static irqreturn_t fm10k_msix_clean_rings(int __always_unused irq, void *data)
struct fm10k_q_vector *q_vector = data;
if (q_vector->rx.count || q_vector->tx.count)
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -859,7 +903,8 @@ static irqreturn_t fm10k_msix_mbx_vf(int __always_unused irq, void *data)
/* re-enable mailbox interrupt and indicate 20us delay */
fm10k_write_reg(hw, FM10K_VFITR(FM10K_MBX_VECTOR),
- FM10K_ITR_ENABLE | FM10K_MBX_INT_DELAY);
+ FM10K_ITR_ENABLE | (FM10K_MBX_INT_DELAY >>
+ hw->mac.itr_scale));
/* service upstream mailbox */
if (fm10k_mbx_trylock(interface)) {
@@ -867,7 +912,7 @@ static irqreturn_t fm10k_msix_mbx_vf(int __always_unused irq, void *data)
fm10k_mbx_unlock(interface);
}
- hw->mac.get_host_state = 1;
+ hw->mac.get_host_state = true;
fm10k_service_event_schedule(interface);
return IRQ_HANDLED;
@@ -897,7 +942,7 @@ void fm10k_netpoll(struct net_device *netdev)
#endif
#define FM10K_ERR_MSG(type) case (type): error = #type; break
static void fm10k_handle_fault(struct fm10k_intfc *interface, int type,
- struct fm10k_fault *fault)
+ struct fm10k_fault *fault)
{
struct pci_dev *pdev = interface->pdev;
struct fm10k_hw *hw = &interface->hw;
@@ -1083,14 +1128,15 @@ static irqreturn_t fm10k_msix_mbx_pf(int __always_unused irq, void *data)
}
/* we should validate host state after interrupt event */
- hw->mac.get_host_state = 1;
+ hw->mac.get_host_state = true;
/* validate host state, and handle VF mailboxes in the service task */
fm10k_service_event_schedule(interface);
/* re-enable mailbox interrupt and indicate 20us delay */
fm10k_write_reg(hw, FM10K_ITR(FM10K_MBX_VECTOR),
- FM10K_ITR_ENABLE | FM10K_MBX_INT_DELAY);
+ FM10K_ITR_ENABLE | (FM10K_MBX_INT_DELAY >>
+ hw->mac.itr_scale));
return IRQ_HANDLED;
}
@@ -1101,6 +1147,10 @@ void fm10k_mbx_free_irq(struct fm10k_intfc *interface)
struct fm10k_hw *hw = &interface->hw;
int itr_reg;
+ /* no mailbox IRQ to free if MSI-X is not enabled */
+ if (!interface->msix_entries)
+ return;
+
/* disconnect the mailbox */
hw->mbx.ops.disconnect(hw, &hw->mbx);
@@ -1141,7 +1191,7 @@ static s32 fm10k_mbx_mac_addr(struct fm10k_hw *hw, u32 **results,
/* MAC was changed so we need reset */
if (is_valid_ether_addr(hw->mac.perm_addr) &&
- memcmp(hw->mac.perm_addr, hw->mac.addr, ETH_ALEN))
+ !ether_addr_equal(hw->mac.perm_addr, hw->mac.addr))
interface->flags |= FM10K_FLAG_RESET_REQUESTED;
/* VLAN override was changed, or default VLAN changed */
@@ -1269,7 +1319,7 @@ static s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results,
if (!fm10k_glort_valid_pf(hw, glort))
return FM10K_ERR_PARAM;
- /* verify VID is valid */
+ /* verify VLAN ID is valid */
if (pvid >= FM10K_VLAN_TABLE_VID_MAX)
return FM10K_ERR_PARAM;
@@ -1388,14 +1438,14 @@ static int fm10k_mbx_request_irq_pf(struct fm10k_intfc *interface)
}
/* Enable interrupts w/ no moderation for "other" interrupts */
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_PCIeFault), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SwitchUpDown), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SRAM), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_MaxHoldTime), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_VFLR), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_pcie_fault), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_switch_up_down), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_sram), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_max_hold_time), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_vflr), other_itr);
/* Enable interrupts w/ moderation for mailbox */
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_Mailbox), mbx_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_mailbox), mbx_itr);
/* Enable individual interrupt causes */
fm10k_write_reg(hw, FM10K_EIMR, FM10K_EIMR_ENABLE(PCA_FAULT) |
@@ -1423,10 +1473,15 @@ int fm10k_mbx_request_irq(struct fm10k_intfc *interface)
err = fm10k_mbx_request_irq_pf(interface);
else
err = fm10k_mbx_request_irq_vf(interface);
+ if (err)
+ return err;
/* connect mailbox */
- if (!err)
- err = hw->mbx.ops.connect(hw, &hw->mbx);
+ err = hw->mbx.ops.connect(hw, &hw->mbx);
+
+ /* if the mailbox failed to connect, then free IRQ */
+ if (err)
+ fm10k_mbx_free_irq(interface);
return err;
}
@@ -1455,8 +1510,10 @@ void fm10k_qv_free_irq(struct fm10k_intfc *interface)
if (!q_vector->tx.count && !q_vector->rx.count)
continue;
- /* disable interrupts */
+ /* clear the affinity_mask in the IRQ descriptor */
+ irq_set_affinity_hint(entry->vector, NULL);
+ /* disable interrupts */
writel(FM10K_ITR_MASK_SET, q_vector->itr);
free_irq(entry->vector, q_vector);
@@ -1514,6 +1571,9 @@ int fm10k_qv_request_irq(struct fm10k_intfc *interface)
goto err_out;
}
+ /* assign the mask for this irq */
+ irq_set_affinity_hint(entry->vector, &q_vector->affinity_mask);
+
/* Enable q_vector */
writel(FM10K_ITR_ENABLE, q_vector->itr);
@@ -1534,8 +1594,10 @@ err_out:
if (!q_vector->tx.count && !q_vector->rx.count)
continue;
- /* disable interrupts */
+ /* clear the affinity_mask in the IRQ descriptor */
+ irq_set_affinity_hint(entry->vector, NULL);
+ /* disable interrupts */
writel(FM10K_ITR_MASK_SET, q_vector->itr);
free_irq(entry->vector, q_vector);
@@ -1573,7 +1635,7 @@ void fm10k_up(struct fm10k_intfc *interface)
netif_tx_start_all_queues(interface->netdev);
/* kick off the service timer now */
- hw->mac.get_host_state = 1;
+ hw->mac.get_host_state = true;
mod_timer(&interface->service_timer, jiffies);
}
@@ -1684,7 +1746,13 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
interface->last_reset = jiffies + (10 * HZ);
/* reset and initialize the hardware so it is in a known state */
- err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw);
+ err = hw->mac.ops.reset_hw(hw);
+ if (err) {
+ dev_err(&pdev->dev, "reset_hw failed: %d\n", err);
+ return err;
+ }
+
+ err = hw->mac.ops.init_hw(hw);
if (err) {
dev_err(&pdev->dev, "init_hw failed: %d\n", err);
return err;
@@ -1722,13 +1790,6 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
pci_resource_len(pdev, 4));
hw->sw_addr = interface->sw_addr;
- /* Only the PF can support VXLAN and NVGRE offloads */
- if (hw->mac.type != fm10k_mac_pf) {
- netdev->hw_enc_features = 0;
- netdev->features &= ~NETIF_F_GSO_UDP_TUNNEL;
- netdev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL;
- }
-
/* initialize DCBNL interface */
fm10k_dcbnl_set_ops(netdev);
@@ -1749,8 +1810,8 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
interface->rx_ring_count = FM10K_DEFAULT_RXD;
/* set default interrupt moderation */
- interface->tx_itr = FM10K_ITR_10K;
- interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_20K;
+ interface->tx_itr = FM10K_TX_ITR_DEFAULT;
+ interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT;
/* initialize vxlan_port list */
INIT_LIST_HEAD(&interface->vxlan_port);
@@ -1835,17 +1896,18 @@ static void fm10k_slot_warn(struct fm10k_intfc *interface)
return;
}
- if (max_gts < expected_gts) {
- dev_warn(&interface->pdev->dev,
- "This device requires %dGT/s of bandwidth for optimal performance.\n",
- expected_gts);
- dev_warn(&interface->pdev->dev,
- "A %sslot with x%d lanes is suggested.\n",
- (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " :
- hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " :
- hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""),
- hw->bus_caps.width);
- }
+ if (max_gts >= expected_gts)
+ return;
+
+ dev_warn(&interface->pdev->dev,
+ "This device requires %dGT/s of bandwidth for optimal performance.\n",
+ expected_gts);
+ dev_warn(&interface->pdev->dev,
+ "A %sslot with x%d lanes is suggested.\n",
+ (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " :
+ hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " :
+ hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""),
+ hw->bus_caps.width);
}
/**
@@ -1859,8 +1921,7 @@ static void fm10k_slot_warn(struct fm10k_intfc *interface)
* The OS initialization, configuring of the interface private structure,
* and a hardware reset occur.
**/
-static int fm10k_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *netdev;
struct fm10k_intfc *interface;
@@ -1894,7 +1955,7 @@ static int fm10k_probe(struct pci_dev *pdev,
pci_set_master(pdev);
pci_save_state(pdev);
- netdev = fm10k_alloc_netdev();
+ netdev = fm10k_alloc_netdev(fm10k_info_tbl[ent->driver_data]);
if (!netdev) {
err = -ENOMEM;
goto err_alloc_netdev;
@@ -2071,8 +2132,10 @@ static int fm10k_resume(struct pci_dev *pdev)
/* reset hardware to known state */
err = hw->mac.ops.init_hw(&interface->hw);
- if (err)
+ if (err) {
+ dev_err(&pdev->dev, "init_hw failed: %d\n", err);
return err;
+ }
/* reset statistics starting values */
hw->mac.ops.rebind_hw_stats(hw, &interface->stats);
@@ -2083,16 +2146,22 @@ static int fm10k_resume(struct pci_dev *pdev)
rtnl_lock();
err = fm10k_init_queueing_scheme(interface);
- if (!err) {
- fm10k_mbx_request_irq(interface);
- if (netif_running(netdev))
- err = fm10k_open(netdev);
- }
+ if (err)
+ goto err_queueing_scheme;
- rtnl_unlock();
+ err = fm10k_mbx_request_irq(interface);
+ if (err)
+ goto err_mbx_irq;
+ err = fm10k_hw_ready(interface);
if (err)
- return err;
+ goto err_open;
+
+ err = netif_running(netdev) ? fm10k_open(netdev) : 0;
+ if (err)
+ goto err_open;
+
+ rtnl_unlock();
/* assume host is not ready, to prevent race with watchdog in case we
* actually don't have connection to the switch
@@ -2110,6 +2179,14 @@ static int fm10k_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
return 0;
+err_open:
+ fm10k_mbx_free_irq(interface);
+err_mbx_irq:
+ fm10k_clear_queueing_scheme(interface);
+err_queueing_scheme:
+ rtnl_unlock();
+
+ return err;
}
/**
@@ -2185,6 +2262,9 @@ static pci_ers_result_t fm10k_io_error_detected(struct pci_dev *pdev,
if (netif_running(netdev))
fm10k_close(netdev);
+ /* free interrupts */
+ fm10k_clear_queueing_scheme(interface);
+
fm10k_mbx_free_irq(interface);
pci_disable_device(pdev);
@@ -2248,11 +2328,22 @@ static void fm10k_io_resume(struct pci_dev *pdev)
int err = 0;
/* reset hardware to known state */
- hw->mac.ops.init_hw(&interface->hw);
+ err = hw->mac.ops.init_hw(&interface->hw);
+ if (err) {
+ dev_err(&pdev->dev, "init_hw failed: %d\n", err);
+ return;
+ }
/* reset statistics starting values */
hw->mac.ops.rebind_hw_stats(hw, &interface->stats);
+ err = fm10k_init_queueing_scheme(interface);
+ if (err) {
+ dev_err(&interface->pdev->dev,
+ "init_queueing_scheme failed: %d\n", err);
+ return;
+ }
+
/* reassociate interrupts */
fm10k_mbx_request_irq(interface);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
index 8c0bdc4e4edd..62ccebc5f728 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * Copyright(c) 2013 - 2015 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,
@@ -150,19 +150,26 @@ static s32 fm10k_init_hw_pf(struct fm10k_hw *hw)
FM10K_TPH_RXCTRL_HDR_WROEN);
}
- /* set max hold interval to align with 1.024 usec in all modes */
+ /* set max hold interval to align with 1.024 usec in all modes and
+ * store ITR scale
+ */
switch (hw->bus.speed) {
case fm10k_bus_speed_2500:
dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN1;
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN1;
break;
case fm10k_bus_speed_5000:
dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN2;
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN2;
break;
case fm10k_bus_speed_8000:
dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN3;
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3;
break;
default:
dma_ctrl = 0;
+ /* just in case, assume Gen3 ITR scale */
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3;
break;
}
@@ -259,7 +266,6 @@ static s32 fm10k_read_mac_addr_pf(struct fm10k_hw *hw)
{
u8 perm_addr[ETH_ALEN];
u32 serial_num;
- int i;
serial_num = fm10k_read_reg(hw, FM10K_SM_AREA(1));
@@ -281,10 +287,8 @@ static s32 fm10k_read_mac_addr_pf(struct fm10k_hw *hw)
perm_addr[4] = (u8)(serial_num >> 8);
perm_addr[5] = (u8)(serial_num);
- for (i = 0; i < ETH_ALEN; i++) {
- hw->mac.perm_addr[i] = perm_addr[i];
- hw->mac.addr[i] = perm_addr[i];
- }
+ ether_addr_copy(hw->mac.perm_addr, perm_addr);
+ ether_addr_copy(hw->mac.addr, perm_addr);
return 0;
}
@@ -325,7 +329,7 @@ static s32 fm10k_update_xc_addr_pf(struct fm10k_hw *hw, u16 glort,
/* clear set bit from VLAN ID */
vid &= ~FM10K_VLAN_CLEAR;
- /* if glort or vlan are not valid return error */
+ /* if glort or VLAN are not valid return error */
if (!fm10k_glort_valid_pf(hw, glort) || vid >= FM10K_VLAN_TABLE_VID_MAX)
return FM10K_ERR_PARAM;
@@ -334,8 +338,8 @@ static s32 fm10k_update_xc_addr_pf(struct fm10k_hw *hw, u16 glort,
((u32)mac[3] << 16) |
((u32)mac[4] << 8) |
((u32)mac[5]));
- mac_update.mac_upper = cpu_to_le16(((u32)mac[0] << 8) |
- ((u32)mac[1]));
+ mac_update.mac_upper = cpu_to_le16(((u16)mac[0] << 8) |
+ ((u16)mac[1]));
mac_update.vlan = cpu_to_le16(vid);
mac_update.glort = cpu_to_le16(glort);
mac_update.action = add ? 0 : 1;
@@ -410,6 +414,7 @@ static s32 fm10k_update_xcast_mode_pf(struct fm10k_hw *hw, u16 glort, u8 mode)
if (mode > FM10K_XCAST_MODE_NONE)
return FM10K_ERR_PARAM;
+
/* if glort is not valid return error */
if (!fm10k_glort_valid_pf(hw, glort))
return FM10K_ERR_PARAM;
@@ -903,6 +908,13 @@ static s32 fm10k_iov_assign_default_mac_vlan_pf(struct fm10k_hw *hw,
fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx), tdbal);
fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx), tdbah);
+ /* Provide the VF the ITR scale, using software-defined fields in TDLEN
+ * to pass the information during VF initialization. See definition of
+ * FM10K_TDLEN_ITR_SCALE_SHIFT for more details.
+ */
+ fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx), hw->mac.itr_scale <<
+ FM10K_TDLEN_ITR_SCALE_SHIFT);
+
err_out:
/* configure Queue control register */
txqctl = ((u32)vf_vid << FM10K_TXQCTL_VID_SHIFT) &
@@ -910,7 +922,7 @@ err_out:
txqctl |= (vf_idx << FM10K_TXQCTL_TC_SHIFT) |
FM10K_TXQCTL_VF | vf_idx;
- /* assign VID */
+ /* assign VLAN ID */
for (i = 0; i < queues_per_pool; i++)
fm10k_write_reg(hw, FM10K_TXQCTL(vf_q_idx + i), txqctl);
@@ -1035,6 +1047,12 @@ static s32 fm10k_iov_reset_resources_pf(struct fm10k_hw *hw,
for (i = queues_per_pool; i--;) {
fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx + i), tdbal);
fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx + i), tdbah);
+ /* See definition of FM10K_TDLEN_ITR_SCALE_SHIFT for an
+ * explanation of how TDLEN is used.
+ */
+ fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx + i),
+ hw->mac.itr_scale <<
+ FM10K_TDLEN_ITR_SCALE_SHIFT);
fm10k_write_reg(hw, FM10K_TQMAP(qmap_idx + i), vf_q_idx + i);
fm10k_write_reg(hw, FM10K_RQMAP(qmap_idx + i), vf_q_idx + i);
}
@@ -1155,14 +1173,14 @@ s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *hw, u32 **results,
}
/**
- * fm10k_iov_select_vid - Select correct default VID
+ * fm10k_iov_select_vid - Select correct default VLAN ID
* @hw: Pointer to hardware structure
- * @vid: VID to correct
+ * @vid: VLAN ID to correct
*
- * Will report an error if VID is out of range. For VID = 0, it will return
- * either the pf_vid or sw_vid depending on which one is set.
+ * Will report an error if the VLAN ID is out of range. For VID = 0, it will
+ * return either the pf_vid or sw_vid depending on which one is set.
*/
-static inline s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid)
+static s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid)
{
if (!vid)
return vf_info->pf_vid ? vf_info->pf_vid : vf_info->sw_vid;
@@ -1212,11 +1230,11 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
set = !(vid & FM10K_VLAN_CLEAR);
vid &= ~FM10K_VLAN_CLEAR;
- err = fm10k_iov_select_vid(vf_info, vid);
+ err = fm10k_iov_select_vid(vf_info, (u16)vid);
if (err < 0)
return err;
- else
- vid = err;
+
+ vid = err;
/* update VSI info for VF in regards to VLAN table */
err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi, set);
@@ -1232,7 +1250,7 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
/* block attempts to set MAC for a locked device */
if (is_valid_ether_addr(vf_info->mac) &&
- memcmp(mac, vf_info->mac, ETH_ALEN))
+ !ether_addr_equal(mac, vf_info->mac))
return FM10K_ERR_PARAM;
set = !(vlan & FM10K_VLAN_CLEAR);
@@ -1241,8 +1259,8 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
err = fm10k_iov_select_vid(vf_info, vlan);
if (err < 0)
return err;
- else
- vlan = err;
+
+ vlan = (u16)err;
/* notify switch of request for new unicast address */
err = hw->mac.ops.update_uc_addr(hw, vf_info->glort,
@@ -1267,8 +1285,8 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
err = fm10k_iov_select_vid(vf_info, vlan);
if (err < 0)
return err;
- else
- vlan = err;
+
+ vlan = (u16)err;
/* notify switch of request for new multicast address */
err = hw->mac.ops.update_mc_addr(hw, vf_info->glort,
@@ -1396,14 +1414,6 @@ s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *hw, u32 **results,
return err;
}
-const struct fm10k_msg_data fm10k_iov_msg_data_pf[] = {
- FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
- FM10K_VF_MSG_MSIX_HANDLER(fm10k_iov_msg_msix_pf),
- FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_iov_msg_mac_vlan_pf),
- FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_iov_msg_lport_state_pf),
- FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error),
-};
-
/**
* fm10k_update_stats_hw_pf - Updates hardware related statistics of PF
* @hw: pointer to hardware structure
@@ -1431,9 +1441,10 @@ static void fm10k_update_hw_stats_pf(struct fm10k_hw *hw,
xec = fm10k_read_hw_stats_32b(hw, FM10K_STATS_XEC, &stats->xec);
vlan_drop = fm10k_read_hw_stats_32b(hw, FM10K_STATS_VLAN_DROP,
&stats->vlan_drop);
- loopback_drop = fm10k_read_hw_stats_32b(hw,
- FM10K_STATS_LOOPBACK_DROP,
- &stats->loopback_drop);
+ loopback_drop =
+ fm10k_read_hw_stats_32b(hw,
+ FM10K_STATS_LOOPBACK_DROP,
+ &stats->loopback_drop);
nodesc_drop = fm10k_read_hw_stats_32b(hw,
FM10K_STATS_NODESC_DROP,
&stats->nodesc_drop);
@@ -1678,8 +1689,8 @@ const struct fm10k_tlv_attr fm10k_update_pvid_msg_attr[] = {
*
* This handler configures the default VLAN for the PF
**/
-s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results,
- struct fm10k_mbx_info *mbx)
+static s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results,
+ struct fm10k_mbx_info *mbx)
{
u16 glort, pvid;
u32 pvid_update;
@@ -1698,7 +1709,7 @@ s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results,
if (!fm10k_glort_valid_pf(hw, glort))
return FM10K_ERR_PARAM;
- /* verify VID is valid */
+ /* verify VLAN ID is valid */
if (pvid >= FM10K_VLAN_TABLE_VID_MAX)
return FM10K_ERR_PARAM;
@@ -1855,39 +1866,39 @@ static const struct fm10k_msg_data fm10k_msg_data_pf[] = {
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error),
};
-static struct fm10k_mac_ops mac_ops_pf = {
- .get_bus_info = &fm10k_get_bus_info_generic,
- .reset_hw = &fm10k_reset_hw_pf,
- .init_hw = &fm10k_init_hw_pf,
- .start_hw = &fm10k_start_hw_generic,
- .stop_hw = &fm10k_stop_hw_generic,
- .update_vlan = &fm10k_update_vlan_pf,
- .read_mac_addr = &fm10k_read_mac_addr_pf,
- .update_uc_addr = &fm10k_update_uc_addr_pf,
- .update_mc_addr = &fm10k_update_mc_addr_pf,
- .update_xcast_mode = &fm10k_update_xcast_mode_pf,
- .update_int_moderator = &fm10k_update_int_moderator_pf,
- .update_lport_state = &fm10k_update_lport_state_pf,
- .update_hw_stats = &fm10k_update_hw_stats_pf,
- .rebind_hw_stats = &fm10k_rebind_hw_stats_pf,
- .configure_dglort_map = &fm10k_configure_dglort_map_pf,
- .set_dma_mask = &fm10k_set_dma_mask_pf,
- .get_fault = &fm10k_get_fault_pf,
- .get_host_state = &fm10k_get_host_state_pf,
- .adjust_systime = &fm10k_adjust_systime_pf,
- .read_systime = &fm10k_read_systime_pf,
+static const struct fm10k_mac_ops mac_ops_pf = {
+ .get_bus_info = fm10k_get_bus_info_generic,
+ .reset_hw = fm10k_reset_hw_pf,
+ .init_hw = fm10k_init_hw_pf,
+ .start_hw = fm10k_start_hw_generic,
+ .stop_hw = fm10k_stop_hw_generic,
+ .update_vlan = fm10k_update_vlan_pf,
+ .read_mac_addr = fm10k_read_mac_addr_pf,
+ .update_uc_addr = fm10k_update_uc_addr_pf,
+ .update_mc_addr = fm10k_update_mc_addr_pf,
+ .update_xcast_mode = fm10k_update_xcast_mode_pf,
+ .update_int_moderator = fm10k_update_int_moderator_pf,
+ .update_lport_state = fm10k_update_lport_state_pf,
+ .update_hw_stats = fm10k_update_hw_stats_pf,
+ .rebind_hw_stats = fm10k_rebind_hw_stats_pf,
+ .configure_dglort_map = fm10k_configure_dglort_map_pf,
+ .set_dma_mask = fm10k_set_dma_mask_pf,
+ .get_fault = fm10k_get_fault_pf,
+ .get_host_state = fm10k_get_host_state_pf,
+ .adjust_systime = fm10k_adjust_systime_pf,
+ .read_systime = fm10k_read_systime_pf,
};
-static struct fm10k_iov_ops iov_ops_pf = {
- .assign_resources = &fm10k_iov_assign_resources_pf,
- .configure_tc = &fm10k_iov_configure_tc_pf,
- .assign_int_moderator = &fm10k_iov_assign_int_moderator_pf,
+static const struct fm10k_iov_ops iov_ops_pf = {
+ .assign_resources = fm10k_iov_assign_resources_pf,
+ .configure_tc = fm10k_iov_configure_tc_pf,
+ .assign_int_moderator = fm10k_iov_assign_int_moderator_pf,
.assign_default_mac_vlan = fm10k_iov_assign_default_mac_vlan_pf,
- .reset_resources = &fm10k_iov_reset_resources_pf,
- .set_lport = &fm10k_iov_set_lport_pf,
- .reset_lport = &fm10k_iov_reset_lport_pf,
- .update_stats = &fm10k_iov_update_stats_pf,
- .report_timestamp = &fm10k_iov_report_timestamp_pf,
+ .reset_resources = fm10k_iov_reset_resources_pf,
+ .set_lport = fm10k_iov_set_lport_pf,
+ .reset_lport = fm10k_iov_reset_lport_pf,
+ .update_stats = fm10k_iov_update_stats_pf,
+ .report_timestamp = fm10k_iov_report_timestamp_pf,
};
static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw)
@@ -1897,9 +1908,9 @@ static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw)
return fm10k_sm_mbx_init(hw, &hw->mbx, fm10k_msg_data_pf);
}
-struct fm10k_info fm10k_pf_info = {
+const struct fm10k_info fm10k_pf_info = {
.mac = fm10k_mac_pf,
- .get_invariants = &fm10k_get_invariants_pf,
+ .get_invariants = fm10k_get_invariants_pf,
.mac_ops = &mac_ops_pf,
.iov_ops = &iov_ops_pf,
};
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
index 40a0dbc62a04..b2d96b45ca3c 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * Copyright(c) 2013 - 2015 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,
@@ -74,6 +74,11 @@ enum fm10k_pf_tlv_attr_id_v1 {
#define FM10K_MSG_UPDATE_PVID_PVID_SHIFT 16
#define FM10K_MSG_UPDATE_PVID_PVID_SIZE 16
+/* The following data structures are overlayed directly onto TLV mailbox
+ * messages, and must not break 4 byte alignment. Ensure the structures line
+ * up correctly as per their TLV definition.
+ */
+
struct fm10k_mac_update {
__le32 mac_lower;
__le16 mac_upper;
@@ -81,34 +86,32 @@ struct fm10k_mac_update {
__le16 glort;
u8 flags;
u8 action;
-} __packed;
+} __aligned(4) __packed;
struct fm10k_global_table_data {
__le32 used;
__le32 avail;
-} __packed;
+} __aligned(4) __packed;
struct fm10k_swapi_error {
__le32 status;
struct fm10k_global_table_data mac;
struct fm10k_global_table_data nexthop;
struct fm10k_global_table_data ffu;
-} __packed;
+} __aligned(4) __packed;
struct fm10k_swapi_1588_timestamp {
__le64 egress;
__le64 ingress;
__le16 dglort;
__le16 sglort;
-} __packed;
+} __aligned(4) __packed;
s32 fm10k_msg_lport_map_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *);
extern const struct fm10k_tlv_attr fm10k_lport_map_msg_attr[];
#define FM10K_PF_MSG_LPORT_MAP_HANDLER(func) \
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_LPORT_MAP, \
fm10k_lport_map_msg_attr, func)
-s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *, u32 **,
- struct fm10k_mbx_info *);
extern const struct fm10k_tlv_attr fm10k_update_pvid_msg_attr[];
#define FM10K_PF_MSG_UPDATE_PVID_HANDLER(func) \
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_UPDATE_PVID, \
@@ -129,7 +132,6 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *, u32 **,
struct fm10k_mbx_info *);
s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *, u32 **,
struct fm10k_mbx_info *);
-extern const struct fm10k_msg_data fm10k_iov_msg_data_pf[];
-extern struct fm10k_info fm10k_pf_info;
+extern const struct fm10k_info fm10k_pf_info;
#endif /* _FM10K_PF_H */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
index 9b29d7b0377a..ab01bb30752f 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * Copyright(c) 2013 - 2015 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,
@@ -48,8 +48,8 @@ s32 fm10k_tlv_msg_init(u32 *msg, u16 msg_id)
* the attribute buffer. It will return success if provided with a valid
* pointers.
**/
-s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
- const unsigned char *string)
+static s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
+ const unsigned char *string)
{
u32 attr_data = 0, len = 0;
u32 *attr;
@@ -98,7 +98,7 @@ s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
* it in the array pointed by by string. It will return success if provided
* with a valid pointers.
**/
-s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
+static s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
{
u32 len;
@@ -353,7 +353,7 @@ s32 fm10k_tlv_attr_get_le_struct(u32 *attr, void *le_struct, u32 len)
* function will return NULL on failure, and a pointer to the start
* of the nested attributes on success.
**/
-u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
+static u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
{
u32 *attr;
@@ -370,7 +370,7 @@ u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
}
/**
- * fm10k_tlv_attr_nest_start - Start a set of nested attributes
+ * fm10k_tlv_attr_nest_stop - Stop a set of nested attributes
* @msg: Pointer to message block
*
* This function closes off an existing set of nested attributes. The
@@ -378,7 +378,7 @@ u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
* the case of a nest within the nest this would be the outer nest pointer.
* This function will return success provided all pointers are valid.
**/
-s32 fm10k_tlv_attr_nest_stop(u32 *msg)
+static s32 fm10k_tlv_attr_nest_stop(u32 *msg)
{
u32 *attr;
u32 len;
@@ -483,8 +483,8 @@ static s32 fm10k_tlv_attr_validate(u32 *attr,
* FM10K_NOT_IMPLEMENTED for any attribute that is outside of the array
* and 0 on success.
**/
-s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
- const struct fm10k_tlv_attr *tlv_attr)
+static s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
+ const struct fm10k_tlv_attr *tlv_attr)
{
u32 i, attr_id, offset = 0;
s32 err = 0;
@@ -755,7 +755,7 @@ parse_nested:
err = fm10k_tlv_attr_get_mac_vlan(
results[FM10K_TEST_MSG_MAC_ADDR],
result_mac, &result_vlan);
- if (!err && memcmp(test_mac, result_mac, ETH_ALEN))
+ if (!err && !ether_addr_equal(test_mac, result_mac))
err = FM10K_ERR_INVALID_VALUE;
if (!err && test_vlan != result_vlan)
err = FM10K_ERR_INVALID_VALUE;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
index 7e045e8bf1eb..e1845e0a17d8 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * Copyright(c) 2013 - 2015 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,
@@ -38,9 +38,9 @@ struct fm10k_msg_data;
* mailbox size we will provide a message with the above header and it
* will be segmented and transported to the mailbox to the other side where
* it is reassembled. It contains the following fields:
- * Len: Length of the message in bytes excluding the message header
+ * Length: Length of the message in bytes excluding the message header
* Flags: TBD
- * Rule: These will be the message/argument types we pass
+ * Type/ID: These will be the message/argument types we pass
*/
/* message data header */
#define FM10K_TLV_ID_SHIFT 0
@@ -106,8 +106,6 @@ struct fm10k_msg_data {
#define FM10K_MSG_HANDLER(id, attr, func) { id, attr, func }
s32 fm10k_tlv_msg_init(u32 *, u16);
-s32 fm10k_tlv_attr_put_null_string(u32 *, u16, const unsigned char *);
-s32 fm10k_tlv_attr_get_null_string(u32 *, unsigned char *);
s32 fm10k_tlv_attr_put_mac_vlan(u32 *, u16, const u8 *, u16);
s32 fm10k_tlv_attr_get_mac_vlan(u32 *, u8 *, u16 *);
s32 fm10k_tlv_attr_put_bool(u32 *, u16);
@@ -147,9 +145,6 @@ s32 fm10k_tlv_attr_get_value(u32 *, void *, u32);
fm10k_tlv_attr_get_value(attr, ptr, sizeof(s64))
s32 fm10k_tlv_attr_put_le_struct(u32 *, u16, const void *, u32);
s32 fm10k_tlv_attr_get_le_struct(u32 *, void *, u32);
-u32 *fm10k_tlv_attr_nest_start(u32 *, u16);
-s32 fm10k_tlv_attr_nest_stop(u32 *);
-s32 fm10k_tlv_attr_parse(u32 *, u32 **, const struct fm10k_tlv_attr *);
s32 fm10k_tlv_msg_parse(struct fm10k_hw *, u32 *, struct fm10k_mbx_info *,
const struct fm10k_msg_data *);
s32 fm10k_tlv_msg_error(struct fm10k_hw *hw, u32 **results,
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
index 318a212f0a78..854ebb1906bf 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * Copyright(c) 2013 - 2015 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,
@@ -77,6 +77,7 @@ struct fm10k_hw;
#define FM10K_PCIE_SRIOV_CTRL_VFARI 0x10
#define FM10K_ERR_PARAM -2
+#define FM10K_ERR_NO_RESOURCES -3
#define FM10K_ERR_REQUESTS_PENDING -4
#define FM10K_ERR_RESET_REQUESTED -5
#define FM10K_ERR_DMA_PENDING -6
@@ -271,6 +272,20 @@ struct fm10k_hw;
#define FM10K_TDBAL(_n) ((0x40 * (_n)) + 0x8000)
#define FM10K_TDBAH(_n) ((0x40 * (_n)) + 0x8001)
#define FM10K_TDLEN(_n) ((0x40 * (_n)) + 0x8002)
+/* When fist initialized, VFs need to know the Interrupt Throttle Rate (ITR)
+ * scale which is based on the PCIe speed but the speed information in the PCI
+ * configuration space may not be accurate. The PF already knows the ITR scale
+ * but there is no defined method to pass that information from the PF to the
+ * VF. This is accomplished during VF initialization by temporarily co-opting
+ * the yet-to-be-used TDLEN register to have the PF store the ITR shift for
+ * the VF to retrieve before the VF needs to use the TDLEN register for its
+ * intended purpose, i.e. before the Tx resources are allocated.
+ */
+#define FM10K_TDLEN_ITR_SCALE_SHIFT 9
+#define FM10K_TDLEN_ITR_SCALE_MASK 0x00000E00
+#define FM10K_TDLEN_ITR_SCALE_GEN1 2
+#define FM10K_TDLEN_ITR_SCALE_GEN2 1
+#define FM10K_TDLEN_ITR_SCALE_GEN3 0
#define FM10K_TPH_TXCTRL(_n) ((0x40 * (_n)) + 0x8003)
#define FM10K_TPH_TXCTRL_DESC_TPHEN 0x00000020
#define FM10K_TPH_TXCTRL_DESC_RROEN 0x00000200
@@ -339,7 +354,7 @@ struct fm10k_hw;
#define FM10K_VLAN_TABLE_VID_MAX 4096
#define FM10K_VLAN_TABLE_VSI_MAX 64
#define FM10K_VLAN_LENGTH_SHIFT 16
-#define FM10K_VLAN_CLEAR (1 << 15)
+#define FM10K_VLAN_CLEAR BIT(15)
#define FM10K_VLAN_ALL \
((FM10K_VLAN_TABLE_VID_MAX - 1) << FM10K_VLAN_LENGTH_SHIFT)
@@ -373,13 +388,13 @@ struct fm10k_hw;
#define FM10K_SW_SYSTIME_PULSE(_n) ((_n) + 0x02252)
enum fm10k_int_source {
- fm10k_int_Mailbox = 0,
- fm10k_int_PCIeFault = 1,
- fm10k_int_SwitchUpDown = 2,
- fm10k_int_SwitchEvent = 3,
- fm10k_int_SRAM = 4,
- fm10k_int_VFLR = 5,
- fm10k_int_MaxHoldTime = 6,
+ fm10k_int_mailbox = 0,
+ fm10k_int_pcie_fault = 1,
+ fm10k_int_switch_up_down = 2,
+ fm10k_int_switch_event = 3,
+ fm10k_int_sram = 4,
+ fm10k_int_vflr = 5,
+ fm10k_int_max_hold_time = 6,
fm10k_int_sources_max_pf
};
@@ -535,7 +550,6 @@ struct fm10k_mac_ops {
struct fm10k_dglort_cfg *);
void (*set_dma_mask)(struct fm10k_hw *, u64);
s32 (*get_fault)(struct fm10k_hw *, int, struct fm10k_fault *);
- void (*request_lport_map)(struct fm10k_hw *);
s32 (*adjust_systime)(struct fm10k_hw *, s32 ppb);
u64 (*read_systime)(struct fm10k_hw *);
};
@@ -559,6 +573,7 @@ struct fm10k_mac_info {
bool get_host_state;
bool tx_ready;
u32 dglort_map;
+ u8 itr_scale;
};
struct fm10k_swapi_table_info {
@@ -644,10 +659,10 @@ enum fm10k_devices {
};
struct fm10k_info {
- enum fm10k_mac_type mac;
- s32 (*get_invariants)(struct fm10k_hw *);
- struct fm10k_mac_ops *mac_ops;
- struct fm10k_iov_ops *iov_ops;
+ enum fm10k_mac_type mac;
+ s32 (*get_invariants)(struct fm10k_hw *);
+ const struct fm10k_mac_ops *mac_ops;
+ const struct fm10k_iov_ops *iov_ops;
};
struct fm10k_hw {
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
index 36c8b0aa08fd..91f8d7311f3b 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
@@ -28,7 +28,7 @@
static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
{
u8 *perm_addr = hw->mac.perm_addr;
- u32 bal = 0, bah = 0;
+ u32 bal = 0, bah = 0, tdlen;
s32 err;
u16 i;
@@ -48,6 +48,9 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
((u32)perm_addr[2]);
}
+ /* restore default itr_scale for next VF initialization */
+ tdlen = hw->mac.itr_scale << FM10K_TDLEN_ITR_SCALE_SHIFT;
+
/* The queues have already been disabled so we just need to
* update their base address registers
*/
@@ -56,6 +59,12 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
fm10k_write_reg(hw, FM10K_TDBAH(i), bah);
fm10k_write_reg(hw, FM10K_RDBAL(i), bal);
fm10k_write_reg(hw, FM10K_RDBAH(i), bah);
+ /* Restore ITR scale in software-defined mechanism in TDLEN
+ * for next VF initialization. See definition of
+ * FM10K_TDLEN_ITR_SCALE_SHIFT for more details on the use of
+ * TDLEN here.
+ */
+ fm10k_write_reg(hw, FM10K_TDLEN(i), tdlen);
}
return 0;
@@ -103,7 +112,14 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw)
s32 err;
u16 i;
- /* assume we always have at least 1 queue */
+ /* verify we have at least 1 queue */
+ if (!~fm10k_read_reg(hw, FM10K_TXQCTL(0)) ||
+ !~fm10k_read_reg(hw, FM10K_RXQCTL(0))) {
+ err = FM10K_ERR_NO_RESOURCES;
+ goto reset_max_queues;
+ }
+
+ /* determine how many queues we have */
for (i = 1; tqdloc0 && (i < FM10K_MAX_QUEUES_POOL); i++) {
/* verify the Descriptor cache offsets are increasing */
tqdloc = ~fm10k_read_reg(hw, FM10K_TQDLOC(i));
@@ -119,16 +135,28 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw)
/* shut down queues we own and reset DMA configuration */
err = fm10k_disable_queues_generic(hw, i);
if (err)
- return err;
+ goto reset_max_queues;
/* record maximum queue count */
hw->mac.max_queues = i;
- /* fetch default VLAN */
+ /* fetch default VLAN and ITR scale */
hw->mac.default_vid = (fm10k_read_reg(hw, FM10K_TXQCTL(0)) &
FM10K_TXQCTL_VID_MASK) >> FM10K_TXQCTL_VID_SHIFT;
+ /* Read the ITR scale from TDLEN. See the definition of
+ * FM10K_TDLEN_ITR_SCALE_SHIFT for more information about how TDLEN is
+ * used here.
+ */
+ hw->mac.itr_scale = (fm10k_read_reg(hw, FM10K_TDLEN(0)) &
+ FM10K_TDLEN_ITR_SCALE_MASK) >>
+ FM10K_TDLEN_ITR_SCALE_SHIFT;
return 0;
+
+reset_max_queues:
+ hw->mac.max_queues = 0;
+
+ return err;
}
/* This structure defines the attibutes to be parsed below */
@@ -270,7 +298,7 @@ static s32 fm10k_update_uc_addr_vf(struct fm10k_hw *hw, u16 glort,
/* verify we are not locked down on the MAC address */
if (is_valid_ether_addr(hw->mac.perm_addr) &&
- memcmp(hw->mac.perm_addr, mac, ETH_ALEN))
+ !ether_addr_equal(hw->mac.perm_addr, mac))
return FM10K_ERR_PARAM;
/* add bit to notify us if this is a set or clear operation */
@@ -414,6 +442,7 @@ static s32 fm10k_update_xcast_mode_vf(struct fm10k_hw *hw, u16 glort, u8 mode)
if (mode > FM10K_XCAST_MODE_NONE)
return FM10K_ERR_PARAM;
+
/* generate message requesting to change xcast mode */
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_LPORT_STATE);
fm10k_tlv_attr_put_u8(msg, FM10K_LPORT_STATE_MSG_XCAST_MODE, mode);
@@ -533,25 +562,25 @@ static const struct fm10k_msg_data fm10k_msg_data_vf[] = {
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error),
};
-static struct fm10k_mac_ops mac_ops_vf = {
- .get_bus_info = &fm10k_get_bus_info_generic,
- .reset_hw = &fm10k_reset_hw_vf,
- .init_hw = &fm10k_init_hw_vf,
- .start_hw = &fm10k_start_hw_generic,
- .stop_hw = &fm10k_stop_hw_vf,
- .update_vlan = &fm10k_update_vlan_vf,
- .read_mac_addr = &fm10k_read_mac_addr_vf,
- .update_uc_addr = &fm10k_update_uc_addr_vf,
- .update_mc_addr = &fm10k_update_mc_addr_vf,
- .update_xcast_mode = &fm10k_update_xcast_mode_vf,
- .update_int_moderator = &fm10k_update_int_moderator_vf,
- .update_lport_state = &fm10k_update_lport_state_vf,
- .update_hw_stats = &fm10k_update_hw_stats_vf,
- .rebind_hw_stats = &fm10k_rebind_hw_stats_vf,
- .configure_dglort_map = &fm10k_configure_dglort_map_vf,
- .get_host_state = &fm10k_get_host_state_generic,
- .adjust_systime = &fm10k_adjust_systime_vf,
- .read_systime = &fm10k_read_systime_vf,
+static const struct fm10k_mac_ops mac_ops_vf = {
+ .get_bus_info = fm10k_get_bus_info_generic,
+ .reset_hw = fm10k_reset_hw_vf,
+ .init_hw = fm10k_init_hw_vf,
+ .start_hw = fm10k_start_hw_generic,
+ .stop_hw = fm10k_stop_hw_vf,
+ .update_vlan = fm10k_update_vlan_vf,
+ .read_mac_addr = fm10k_read_mac_addr_vf,
+ .update_uc_addr = fm10k_update_uc_addr_vf,
+ .update_mc_addr = fm10k_update_mc_addr_vf,
+ .update_xcast_mode = fm10k_update_xcast_mode_vf,
+ .update_int_moderator = fm10k_update_int_moderator_vf,
+ .update_lport_state = fm10k_update_lport_state_vf,
+ .update_hw_stats = fm10k_update_hw_stats_vf,
+ .rebind_hw_stats = fm10k_rebind_hw_stats_vf,
+ .configure_dglort_map = fm10k_configure_dglort_map_vf,
+ .get_host_state = fm10k_get_host_state_generic,
+ .adjust_systime = fm10k_adjust_systime_vf,
+ .read_systime = fm10k_read_systime_vf,
};
static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw)
@@ -561,8 +590,8 @@ static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw)
return fm10k_pfvf_mbx_init(hw, &hw->mbx, fm10k_msg_data_vf, 0);
}
-struct fm10k_info fm10k_vf_info = {
+const struct fm10k_info fm10k_vf_info = {
.mac = fm10k_mac_vf,
- .get_invariants = &fm10k_get_invariants_vf,
+ .get_invariants = fm10k_get_invariants_vf,
.mac_ops = &mac_ops_vf,
};
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
index 06a99d794c99..c4439f1313a0 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
@@ -74,5 +74,5 @@ extern const struct fm10k_tlv_attr fm10k_1588_msg_attr[];
#define FM10K_VF_MSG_1588_HANDLER(func) \
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_1588, fm10k_1588_msg_attr, func)
-extern struct fm10k_info fm10k_vf_info;
+extern const struct fm10k_info fm10k_vf_info;
#endif /* _FM10K_VF_H */
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 4dd3e26129b4..68f2204ec6f3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -42,7 +42,6 @@
#include <linux/string.h>
#include <linux/in.h>
#include <linux/ip.h>
-#include <linux/tcp.h>
#include <linux/sctp.h>
#include <linux/pkt_sched.h>
#include <linux/ipv6.h>
@@ -104,6 +103,7 @@
#define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1)
#define I40E_PRIV_FLAGS_FD_ATR BIT(2)
#define I40E_PRIV_FLAGS_VEB_STATS BIT(3)
+#define I40E_PRIV_FLAGS_PS BIT(4)
#define I40E_NVM_VERSION_LO_SHIFT 0
#define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT)
@@ -187,6 +187,7 @@ struct i40e_lump_tracking {
#define I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR (I40E_FDIR_BUFFER_HEAD_ROOM * 4)
#define I40E_HKEY_ARRAY_SIZE ((I40E_PFQF_HKEY_MAX_INDEX + 1) * 4)
+#define I40E_HLUT_ARRAY_SIZE ((I40E_PFQF_HLUT_MAX_INDEX + 1) * 4)
enum i40e_fd_stat_idx {
I40E_FD_STAT_ATR,
@@ -244,6 +245,11 @@ struct i40e_tc_configuration {
struct i40e_tc_info tc_info[I40E_MAX_TRAFFIC_CLASS];
};
+struct i40e_udp_port_config {
+ __be16 index;
+ u8 type;
+};
+
/* struct that defines the Ethernet device */
struct i40e_pf {
struct pci_dev *pdev;
@@ -265,7 +271,7 @@ struct i40e_pf {
u16 num_lan_qps; /* num lan queues this PF has set up */
u16 num_lan_msix; /* num queue vectors for the base PF vsi */
int queues_left; /* queues left unclaimed */
- u16 rss_size; /* num queues in the RSS array */
+ u16 alloc_rss_size; /* allocated RSS queues */
u16 rss_size_max; /* HW defined max RSS queues */
u16 fdir_pf_filter_count; /* num of guaranteed filters for this PF */
u16 num_alloc_vsi; /* num VSIs this driver supports */
@@ -280,11 +286,9 @@ struct i40e_pf {
u32 fd_atr_cnt;
u32 fd_tcp_rule;
-#ifdef CONFIG_I40E_VXLAN
- __be16 vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
- u16 pending_vxlan_bitmap;
+ struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
+ u16 pending_udp_bitmap;
-#endif
enum i40e_interrupt_policy int_policy;
u16 rx_itr_default;
u16 tx_itr_default;
@@ -321,9 +325,7 @@ struct i40e_pf {
#define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(22)
#define I40E_FLAG_PTP BIT_ULL(25)
#define I40E_FLAG_MFP_ENABLED BIT_ULL(26)
-#ifdef CONFIG_I40E_VXLAN
-#define I40E_FLAG_VXLAN_FILTER_SYNC BIT_ULL(27)
-#endif
+#define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(27)
#define I40E_FLAG_PORT_ID_VALID BIT_ULL(28)
#define I40E_FLAG_DCB_CAPABLE BIT_ULL(29)
#define I40E_FLAG_RSS_AQ_CAPABLE BIT_ULL(31)
@@ -335,7 +337,9 @@ struct i40e_pf {
#define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38)
#define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39)
#define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40)
+#define I40E_FLAG_GENEVE_OFFLOAD_CAPABLE BIT_ULL(41)
#define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42)
+#define I40E_FLAG_PF_MAC BIT_ULL(50)
/* tracks features that get auto disabled by errors */
u64 auto_disable_flags;
@@ -412,7 +416,7 @@ struct i40e_pf {
u32 rx_hwtstamp_cleared;
bool ptp_tx;
bool ptp_rx;
- u16 rss_table_size;
+ u16 rss_table_size; /* HW RSS table size */
/* These are only valid in NPAR modes */
u32 npar_max_bw;
u32 npar_min_bw;
@@ -487,6 +491,7 @@ struct i40e_vsi {
u32 tx_restart;
u32 tx_busy;
u64 tx_linearize;
+ u64 tx_force_wb;
u32 rx_buf_failed;
u32 rx_page_failed;
@@ -504,8 +509,10 @@ struct i40e_vsi {
u16 tx_itr_setting;
u16 int_rate_limit; /* value in usecs */
- u16 rss_table_size;
- u16 rss_size;
+ u16 rss_table_size; /* HW RSS table size */
+ u16 rss_size; /* Allocated RSS queues */
+ u8 *rss_hkey_user; /* User configured hash keys */
+ u8 *rss_lut_user; /* User configured lookup table entries */
u16 max_frame;
u16 rx_hdr_len;
@@ -575,6 +582,9 @@ struct i40e_q_vector {
u8 num_ringpairs; /* total number of ring pairs in vector */
+#define I40E_Q_VECTOR_HUNG_DETECT 0 /* Bit Index for hung detection logic */
+ unsigned long hung_detected; /* Set/Reset for hung_detection logic */
+
cpumask_t affinity_mask;
struct rcu_head rcu; /* to avoid race with update stats on free */
char name[I40E_INT_NAME_STR_LEN];
@@ -602,8 +612,8 @@ static inline char *i40e_nvm_version_str(struct i40e_hw *hw)
full_ver = hw->nvm.oem_ver;
ver = (u8)(full_ver >> I40E_OEM_VER_SHIFT);
- build = (u16)((full_ver >> I40E_OEM_VER_BUILD_SHIFT)
- & I40E_OEM_VER_BUILD_MASK);
+ build = (u16)((full_ver >> I40E_OEM_VER_BUILD_SHIFT) &
+ I40E_OEM_VER_BUILD_MASK);
patch = (u8)(full_ver & I40E_OEM_VER_PATCH_MASK);
snprintf(buf, sizeof(buf),
@@ -668,6 +678,8 @@ extern const char i40e_driver_name[];
extern const char i40e_driver_version_str[];
void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags);
void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags);
+int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
+int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id);
void i40e_update_stats(struct i40e_vsi *vsi);
void i40e_update_eth_stats(struct i40e_vsi *vsi);
@@ -691,7 +703,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
bool is_vf, bool is_netdev);
void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan,
bool is_vf, bool is_netdev);
-int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl);
+int i40e_sync_vsi_filters(struct i40e_vsi *vsi);
struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
u16 uplink, u32 param1);
int i40e_vsi_release(struct i40e_vsi *vsi);
@@ -709,7 +721,7 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, u16 uplink_seid,
void i40e_veb_release(struct i40e_veb *veb);
int i40e_veb_config_tc(struct i40e_veb *veb, u8 enabled_tc);
-i40e_status i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid);
+int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid);
void i40e_vsi_remove_pvid(struct i40e_vsi *vsi);
void i40e_vsi_reset_stats(struct i40e_vsi *vsi);
void i40e_pf_reset_stats(struct i40e_pf *pf);
@@ -767,6 +779,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid);
int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid);
struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr,
bool is_vf, bool is_netdev);
+int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr,
+ bool is_vf, bool is_netdev);
bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi);
struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr,
bool is_vf, bool is_netdev);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index 6584b6cd73fd..b22012a446a6 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -227,6 +227,7 @@ enum i40e_admin_queue_opc {
i40e_aqc_opc_nvm_update = 0x0703,
i40e_aqc_opc_nvm_config_read = 0x0704,
i40e_aqc_opc_nvm_config_write = 0x0705,
+ i40e_aqc_opc_oem_post_update = 0x0720,
/* virtualization commands */
i40e_aqc_opc_send_msg_to_pf = 0x0801,
@@ -1891,6 +1892,26 @@ struct i40e_aqc_nvm_config_data_immediate_field {
I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field);
+/* OEM Post Update (indirect 0x0720)
+ * no command data struct used
+ */
+struct i40e_aqc_nvm_oem_post_update {
+#define I40E_AQ_NVM_OEM_POST_UPDATE_EXTERNAL_DATA 0x01
+ u8 sel_data;
+ u8 reserved[7];
+};
+
+I40E_CHECK_STRUCT_LEN(0x8, i40e_aqc_nvm_oem_post_update);
+
+struct i40e_aqc_nvm_oem_post_update_buffer {
+ u8 str_len;
+ u8 dev_addr;
+ __le16 eeprom_addr;
+ u8 data[36];
+};
+
+I40E_CHECK_STRUCT_LEN(0x28, i40e_aqc_nvm_oem_post_update_buffer);
+
/* Send to PF command (indirect 0x0801) id is only used by PF
* Send to VF command (indirect 0x0802) id is only used by PF
* Send to Peer PF command (indirect 0x0803)
@@ -2403,4 +2424,4 @@ struct i40e_aqc_debug_modify_internals {
I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals);
-#endif
+#endif /* _I40E_ADMINQ_CMD_H_ */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 2d74c6e4d7b6..6a034ddac36a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -44,7 +44,6 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw)
switch (hw->device_id) {
case I40E_DEV_ID_SFP_XL710:
case I40E_DEV_ID_QEMU:
- case I40E_DEV_ID_KX_A:
case I40E_DEV_ID_KX_B:
case I40E_DEV_ID_KX_C:
case I40E_DEV_ID_QSFP_A:
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index d4b7af9a2fc8..10744a698d6f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -103,8 +103,8 @@ static ssize_t i40e_dbg_dump_read(struct file *filp, char __user *buffer,
len = min_t(int, count, (i40e_dbg_dump_data_len - *ppos));
bytes_not_copied = copy_to_user(buffer, &i40e_dbg_dump_buf[*ppos], len);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
*ppos += len;
return len;
@@ -353,8 +353,8 @@ static ssize_t i40e_dbg_command_read(struct file *filp, char __user *buffer,
bytes_not_copied = copy_to_user(buffer, buf, len);
kfree(buf);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
*ppos = len;
return len;
@@ -981,12 +981,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
if (!cmd_buf)
return count;
bytes_not_copied = copy_from_user(cmd_buf, buffer, count);
- if (bytes_not_copied < 0) {
+ if (bytes_not_copied) {
kfree(cmd_buf);
- return bytes_not_copied;
+ return -EFAULT;
}
- if (bytes_not_copied > 0)
- count -= bytes_not_copied;
cmd_buf[count] = '\0';
cmd_buf_tmp = strchr(cmd_buf, '\n');
@@ -1140,7 +1138,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
spin_lock_bh(&vsi->mac_filter_list_lock);
f = i40e_add_filter(vsi, ma, vlan, false, false);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- ret = i40e_sync_vsi_filters(vsi, true);
+ ret = i40e_sync_vsi_filters(vsi);
if (f && !ret)
dev_info(&pf->pdev->dev,
"add macaddr: %pM vlan=%d added to VSI %d\n",
@@ -1179,7 +1177,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
spin_lock_bh(&vsi->mac_filter_list_lock);
i40e_del_filter(vsi, ma, vlan, false, false);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- ret = i40e_sync_vsi_filters(vsi, true);
+ ret = i40e_sync_vsi_filters(vsi);
if (!ret)
dev_info(&pf->pdev->dev,
"del macaddr: %pM vlan=%d removed from VSI %d\n",
@@ -2034,8 +2032,8 @@ static ssize_t i40e_dbg_netdev_ops_read(struct file *filp, char __user *buffer,
bytes_not_copied = copy_to_user(buffer, buf, len);
kfree(buf);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
*ppos = len;
return len;
@@ -2068,10 +2066,8 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
memset(i40e_dbg_netdev_ops_buf, 0, sizeof(i40e_dbg_netdev_ops_buf));
bytes_not_copied = copy_from_user(i40e_dbg_netdev_ops_buf,
buffer, count);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
- else if (bytes_not_copied > 0)
- count -= bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
i40e_dbg_netdev_ops_buf[count] = '\0';
buf_tmp = strchr(i40e_dbg_netdev_ops_buf, '\n');
diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h
index c601ca4a610c..448ef4c17efb 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_devids.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h
@@ -30,7 +30,6 @@
/* Device IDs */
#define I40E_DEV_ID_SFP_XL710 0x1572
#define I40E_DEV_ID_QEMU 0x1574
-#define I40E_DEV_ID_KX_A 0x157F
#define I40E_DEV_ID_KX_B 0x1580
#define I40E_DEV_ID_KX_C 0x1581
#define I40E_DEV_ID_QSFP_A 0x1583
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 3f385ffe420f..29d5833e24a3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -88,6 +88,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = {
I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
I40E_VSI_STAT("tx_linearize", tx_linearize),
+ I40E_VSI_STAT("tx_force_wb", tx_force_wb),
};
/* These PF_STATs might look like duplicates of some NETDEV_STATs,
@@ -230,6 +231,7 @@ static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = {
"LinkPolling",
"flow-director-atr",
"veb-stats",
+ "packet-split",
};
#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings)
@@ -2110,7 +2112,7 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
- cmd->data = vsi->alloc_queue_pairs;
+ cmd->data = vsi->num_queue_pairs;
ret = 0;
break;
case ETHTOOL_GRXFH:
@@ -2583,7 +2585,6 @@ static int i40e_set_channels(struct net_device *dev,
return -EINVAL;
}
-#define I40E_HLUT_ARRAY_SIZE ((I40E_PFQF_HLUT_MAX_INDEX + 1) * 4)
/**
* i40e_get_rxfh_key_size - get the RSS hash key size
* @netdev: network interface device structure
@@ -2611,10 +2612,9 @@ static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
- struct i40e_pf *pf = vsi->back;
- struct i40e_hw *hw = &pf->hw;
- u32 reg_val;
- int i, j;
+ u8 *lut, *seed = NULL;
+ int ret;
+ u16 i;
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
@@ -2622,24 +2622,20 @@ static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
if (!indir)
return 0;
- for (i = 0, j = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) {
- reg_val = rd32(hw, I40E_PFQF_HLUT(i));
- indir[j++] = reg_val & 0xff;
- indir[j++] = (reg_val >> 8) & 0xff;
- indir[j++] = (reg_val >> 16) & 0xff;
- indir[j++] = (reg_val >> 24) & 0xff;
- }
+ seed = key;
+ lut = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+ ret = i40e_get_rss(vsi, seed, lut, I40E_HLUT_ARRAY_SIZE);
+ if (ret)
+ goto out;
+ for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++)
+ indir[i] = (u32)(lut[i]);
- if (key) {
- for (i = 0, j = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) {
- reg_val = rd32(hw, I40E_PFQF_HKEY(i));
- key[j++] = (u8)(reg_val & 0xff);
- key[j++] = (u8)((reg_val >> 8) & 0xff);
- key[j++] = (u8)((reg_val >> 16) & 0xff);
- key[j++] = (u8)((reg_val >> 24) & 0xff);
- }
- }
- return 0;
+out:
+ kfree(lut);
+
+ return ret;
}
/**
@@ -2656,10 +2652,8 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir,
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
- struct i40e_pf *pf = vsi->back;
- struct i40e_hw *hw = &pf->hw;
- u32 reg_val;
- int i, j;
+ u8 *seed = NULL;
+ u16 i;
if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
return -EOPNOTSUPP;
@@ -2667,24 +2661,28 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir,
if (!indir)
return 0;
- for (i = 0, j = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) {
- reg_val = indir[j++];
- reg_val |= indir[j++] << 8;
- reg_val |= indir[j++] << 16;
- reg_val |= indir[j++] << 24;
- wr32(hw, I40E_PFQF_HLUT(i), reg_val);
- }
-
if (key) {
- for (i = 0, j = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) {
- reg_val = key[j++];
- reg_val |= key[j++] << 8;
- reg_val |= key[j++] << 16;
- reg_val |= key[j++] << 24;
- wr32(hw, I40E_PFQF_HKEY(i), reg_val);
+ if (!vsi->rss_hkey_user) {
+ vsi->rss_hkey_user = kzalloc(I40E_HKEY_ARRAY_SIZE,
+ GFP_KERNEL);
+ if (!vsi->rss_hkey_user)
+ return -ENOMEM;
}
+ memcpy(vsi->rss_hkey_user, key, I40E_HKEY_ARRAY_SIZE);
+ seed = vsi->rss_hkey_user;
}
- return 0;
+ if (!vsi->rss_lut_user) {
+ vsi->rss_lut_user = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!vsi->rss_lut_user)
+ return -ENOMEM;
+ }
+
+ /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+ for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++)
+ vsi->rss_lut_user[i] = (u8)(indir[i]);
+
+ return i40e_config_rss(vsi, seed, vsi->rss_lut_user,
+ I40E_HLUT_ARRAY_SIZE);
}
/**
@@ -2712,6 +2710,8 @@ static u32 i40e_get_priv_flags(struct net_device *dev)
I40E_PRIV_FLAGS_FD_ATR : 0;
ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ?
I40E_PRIV_FLAGS_VEB_STATS : 0;
+ ret_flags |= pf->flags & I40E_FLAG_RX_PS_ENABLED ?
+ I40E_PRIV_FLAGS_PS : 0;
return ret_flags;
}
@@ -2726,6 +2726,26 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
struct i40e_netdev_priv *np = netdev_priv(dev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
+ bool reset_required = false;
+
+ /* NOTE: MFP is not settable */
+
+ /* allow the user to control the method of receive
+ * buffer DMA, whether the packet is split at header
+ * boundaries into two separate buffers. In some cases
+ * one routine or the other will perform better.
+ */
+ if ((flags & I40E_PRIV_FLAGS_PS) &&
+ !(pf->flags & I40E_FLAG_RX_PS_ENABLED)) {
+ pf->flags |= I40E_FLAG_RX_PS_ENABLED;
+ pf->flags &= ~I40E_FLAG_RX_1BUF_ENABLED;
+ reset_required = true;
+ } else if (!(flags & I40E_PRIV_FLAGS_PS) &&
+ (pf->flags & I40E_FLAG_RX_PS_ENABLED)) {
+ pf->flags &= ~I40E_FLAG_RX_PS_ENABLED;
+ pf->flags |= I40E_FLAG_RX_1BUF_ENABLED;
+ reset_required = true;
+ }
if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG)
pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED;
@@ -2748,6 +2768,10 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
else
pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED;
+ /* if needed, issue reset to cause things to take effect */
+ if (reset_required)
+ i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED));
+
return 0;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
index fe5d9bf3ed6d..579a46ca82df 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
@@ -1544,8 +1544,6 @@ void i40e_fcoe_vsi_setup(struct i40e_pf *pf)
if (!(pf->flags & I40E_FLAG_FCOE_ENABLED))
return;
- BUG_ON(!pf->vsi[pf->lan_vsi]);
-
for (i = 0; i < pf->num_alloc_vsi; i++) {
vsi = pf->vsi[i];
if (vsi && vsi->type == I40E_VSI_FCOE) {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
index 79ae7beeafe5..daa9204426d4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
@@ -762,7 +762,7 @@ static void i40e_write_byte(u8 *hmc_bits,
/* prepare the bits and mask */
shift_width = ce_info->lsb % 8;
- mask = BIT(ce_info->width) - 1;
+ mask = (u8)(BIT(ce_info->width) - 1);
src_byte = *from;
src_byte &= mask;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 4a9873ec28c7..bb4612c159fd 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -24,12 +24,24 @@
*
******************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/of_net.h>
+#include <linux/pci.h>
+
+#ifdef CONFIG_SPARC
+#include <asm/idprom.h>
+#include <asm/prom.h>
+#endif
+
/* Local includes */
#include "i40e.h"
#include "i40e_diag.h"
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN)
#include <net/vxlan.h>
#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+#include <net/geneve.h>
+#endif
const char i40e_driver_name[] = "i40e";
static const char i40e_driver_string[] =
@@ -38,8 +50,8 @@ static const char i40e_driver_string[] =
#define DRV_KERN "-k"
#define DRV_VERSION_MAJOR 1
-#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 46
+#define DRV_VERSION_MINOR 4
+#define DRV_VERSION_BUILD 8
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN
@@ -55,6 +67,8 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit);
static int i40e_setup_misc_vector(struct i40e_pf *pf);
static void i40e_determine_queue_usage(struct i40e_pf *pf);
static int i40e_setup_pf_filter_control(struct i40e_pf *pf);
+static void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut,
+ u16 rss_table_size, u16 rss_size);
static void i40e_fdir_sb_setup(struct i40e_pf *pf);
static int i40e_veb_get_bw_info(struct i40e_veb *veb);
@@ -68,7 +82,6 @@ static int i40e_veb_get_bw_info(struct i40e_veb *veb);
static const struct pci_device_id i40e_pci_tbl[] = {
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0},
- {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_A), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0},
@@ -790,75 +803,6 @@ static void i40e_update_fcoe_stats(struct i40e_vsi *vsi)
#endif
/**
- * i40e_update_link_xoff_rx - Update XOFF received in link flow control mode
- * @pf: the corresponding PF
- *
- * Update the Rx XOFF counter (PAUSE frames) in link flow control mode
- **/
-static void i40e_update_link_xoff_rx(struct i40e_pf *pf)
-{
- struct i40e_hw_port_stats *osd = &pf->stats_offsets;
- struct i40e_hw_port_stats *nsd = &pf->stats;
- struct i40e_hw *hw = &pf->hw;
- u64 xoff = 0;
-
- if ((hw->fc.current_mode != I40E_FC_FULL) &&
- (hw->fc.current_mode != I40E_FC_RX_PAUSE))
- return;
-
- xoff = nsd->link_xoff_rx;
- i40e_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port),
- pf->stat_offsets_loaded,
- &osd->link_xoff_rx, &nsd->link_xoff_rx);
-
- /* No new LFC xoff rx */
- if (!(nsd->link_xoff_rx - xoff))
- return;
-
-}
-
-/**
- * i40e_update_prio_xoff_rx - Update XOFF received in PFC mode
- * @pf: the corresponding PF
- *
- * Update the Rx XOFF counter (PAUSE frames) in PFC mode
- **/
-static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
-{
- struct i40e_hw_port_stats *osd = &pf->stats_offsets;
- struct i40e_hw_port_stats *nsd = &pf->stats;
- bool xoff[I40E_MAX_TRAFFIC_CLASS] = {false};
- struct i40e_dcbx_config *dcb_cfg;
- struct i40e_hw *hw = &pf->hw;
- u16 i;
- u8 tc;
-
- dcb_cfg = &hw->local_dcbx_config;
-
- /* Collect Link XOFF stats when PFC is disabled */
- if (!dcb_cfg->pfc.pfcenable) {
- i40e_update_link_xoff_rx(pf);
- return;
- }
-
- for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
- u64 prio_xoff = nsd->priority_xoff_rx[i];
-
- i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i),
- pf->stat_offsets_loaded,
- &osd->priority_xoff_rx[i],
- &nsd->priority_xoff_rx[i]);
-
- /* No new PFC xoff rx */
- if (!(nsd->priority_xoff_rx[i] - prio_xoff))
- continue;
- /* Get the TC for given priority */
- tc = dcb_cfg->etscfg.prioritytable[i];
- xoff[tc] = true;
- }
-}
-
-/**
* i40e_update_vsi_stats - Update the vsi statistics counters.
* @vsi: the VSI to be updated
*
@@ -881,6 +825,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
u64 bytes, packets;
unsigned int start;
u64 tx_linearize;
+ u64 tx_force_wb;
u64 rx_p, rx_b;
u64 tx_p, tx_b;
u16 q;
@@ -899,7 +844,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
*/
rx_b = rx_p = 0;
tx_b = tx_p = 0;
- tx_restart = tx_busy = tx_linearize = 0;
+ tx_restart = tx_busy = tx_linearize = tx_force_wb = 0;
rx_page = 0;
rx_buf = 0;
rcu_read_lock();
@@ -917,6 +862,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
tx_restart += p->tx_stats.restart_queue;
tx_busy += p->tx_stats.tx_busy;
tx_linearize += p->tx_stats.tx_linearize;
+ tx_force_wb += p->tx_stats.tx_force_wb;
/* Rx queue is part of the same block as Tx queue */
p = &p[1];
@@ -934,6 +880,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
vsi->tx_restart = tx_restart;
vsi->tx_busy = tx_busy;
vsi->tx_linearize = tx_linearize;
+ vsi->tx_force_wb = tx_force_wb;
vsi->rx_page_failed = rx_page;
vsi->rx_buf_failed = rx_buf;
@@ -1049,12 +996,18 @@ static void i40e_update_pf_stats(struct i40e_pf *pf)
i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xon_tx, &nsd->link_xon_tx);
- i40e_update_prio_xoff_rx(pf); /* handles I40E_GLPRT_LXOFFRXC */
+ i40e_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port),
+ pf->stat_offsets_loaded,
+ &osd->link_xoff_rx, &nsd->link_xoff_rx);
i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xoff_tx, &nsd->link_xoff_tx);
for (i = 0; i < 8; i++) {
+ i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i),
+ pf->stat_offsets_loaded,
+ &osd->priority_xoff_rx[i],
+ &nsd->priority_xoff_rx[i]);
i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i),
pf->stat_offsets_loaded,
&osd->priority_xon_rx[i],
@@ -1317,6 +1270,42 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr,
}
/**
+ * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS
+ * @vsi: the VSI to be searched
+ * @macaddr: the mac address to be removed
+ * @is_vf: true if it is a VF
+ * @is_netdev: true if it is a netdev
+ *
+ * Removes a given MAC address from a VSI, regardless of VLAN
+ *
+ * Returns 0 for success, or error
+ **/
+int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr,
+ bool is_vf, bool is_netdev)
+{
+ struct i40e_mac_filter *f = NULL;
+ int changed = 0;
+
+ WARN(!spin_is_locked(&vsi->mac_filter_list_lock),
+ "Missing mac_filter_list_lock\n");
+ list_for_each_entry(f, &vsi->mac_filter_list, list) {
+ if ((ether_addr_equal(macaddr, f->macaddr)) &&
+ (is_vf == f->is_vf) &&
+ (is_netdev == f->is_netdev)) {
+ f->counter--;
+ f->changed = true;
+ changed = 1;
+ }
+ }
+ if (changed) {
+ vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
+ vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
+ return 0;
+ }
+ return -ENOENT;
+}
+
+/**
* i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM
* @vsi: the PF Main VSI - inappropriate for any other VSI
* @macaddr: the MAC address
@@ -1547,10 +1536,9 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
spin_unlock_bh(&vsi->mac_filter_list_lock);
}
- i40e_sync_vsi_filters(vsi, false);
ether_addr_copy(netdev->dev_addr, addr->sa_data);
- return 0;
+ return i40e_sync_vsi_filters(vsi);
}
/**
@@ -1590,7 +1578,7 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
if (enabled_tc && (vsi->back->flags & I40E_FLAG_DCB_ENABLED)) {
/* Find numtc from enabled TC bitmap */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i)) /* TC is enabled */
+ if (enabled_tc & BIT(i)) /* TC is enabled */
numtc++;
}
if (!numtc) {
@@ -1619,13 +1607,14 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
/* Setup queue offset/count for all TCs for given VSI */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
/* See if the given TC is enabled for the given VSI */
- if (vsi->tc_config.enabled_tc & BIT_ULL(i)) {
+ if (vsi->tc_config.enabled_tc & BIT(i)) {
/* TC is enabled */
int pow, num_qps;
switch (vsi->type) {
case I40E_VSI_MAIN:
- qcount = min_t(int, pf->rss_size, num_tc_qps);
+ qcount = min_t(int, pf->alloc_rss_size,
+ num_tc_qps);
break;
#ifdef I40E_FCOE
case I40E_VSI_FCOE:
@@ -1851,13 +1840,12 @@ static void i40e_cleanup_add_list(struct list_head *add_list)
/**
* i40e_sync_vsi_filters - Update the VSI filter list to the HW
* @vsi: ptr to the VSI
- * @grab_rtnl: whether RTNL needs to be grabbed
*
* Push any outstanding VSI filter changes through the AdminQ.
*
* Returns 0 or error value
**/
-int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
+int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
{
struct list_head tmp_del_list, tmp_add_list;
struct i40e_mac_filter *f, *ftmp, *fclone;
@@ -1865,8 +1853,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
bool add_happened = false;
int filter_list_len = 0;
u32 changed_flags = 0;
+ i40e_status aq_ret = 0;
bool err_cond = false;
- i40e_status ret = 0;
+ int retval = 0;
struct i40e_pf *pf;
int num_add = 0;
int num_del = 0;
@@ -1929,17 +1918,22 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
spin_unlock_bh(&vsi->mac_filter_list_lock);
- if (err_cond)
+ if (err_cond) {
i40e_cleanup_add_list(&tmp_add_list);
+ retval = -ENOMEM;
+ goto out;
+ }
}
/* Now process 'del_list' outside the lock */
if (!list_empty(&tmp_del_list)) {
+ int del_list_size;
+
filter_list_len = pf->hw.aq.asq_buf_size /
sizeof(struct i40e_aqc_remove_macvlan_element_data);
- del_list = kcalloc(filter_list_len,
- sizeof(struct i40e_aqc_remove_macvlan_element_data),
- GFP_KERNEL);
+ del_list_size = filter_list_len *
+ sizeof(struct i40e_aqc_remove_macvlan_element_data);
+ del_list = kzalloc(del_list_size, GFP_KERNEL);
if (!del_list) {
i40e_cleanup_add_list(&tmp_add_list);
@@ -1948,7 +1942,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
i40e_undo_del_filter_entries(vsi, &tmp_del_list);
i40e_undo_add_filter_entries(vsi);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto out;
}
list_for_each_entry_safe(f, ftmp, &tmp_del_list, list) {
@@ -1966,18 +1961,22 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
/* flush a full buffer */
if (num_del == filter_list_len) {
- ret = i40e_aq_remove_macvlan(&pf->hw,
- vsi->seid, del_list, num_del,
- NULL);
+ aq_ret = i40e_aq_remove_macvlan(&pf->hw,
+ vsi->seid,
+ del_list,
+ num_del,
+ NULL);
aq_err = pf->hw.aq.asq_last_status;
num_del = 0;
- memset(del_list, 0, sizeof(*del_list));
+ memset(del_list, 0, del_list_size);
- if (ret && aq_err != I40E_AQ_RC_ENOENT)
+ if (aq_ret && aq_err != I40E_AQ_RC_ENOENT) {
+ retval = -EIO;
dev_err(&pf->pdev->dev,
"ignoring delete macvlan error, err %s, aq_err %s while flushing a full buffer\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw, aq_err));
+ }
}
/* Release memory for MAC filter entries which were
* synced up with HW.
@@ -1987,15 +1986,16 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
if (num_del) {
- ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid,
- del_list, num_del, NULL);
+ aq_ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid,
+ del_list, num_del,
+ NULL);
aq_err = pf->hw.aq.asq_last_status;
num_del = 0;
- if (ret && aq_err != I40E_AQ_RC_ENOENT)
+ if (aq_ret && aq_err != I40E_AQ_RC_ENOENT)
dev_info(&pf->pdev->dev,
"ignoring delete macvlan error, err %s aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw, aq_err));
}
@@ -2004,13 +2004,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
if (!list_empty(&tmp_add_list)) {
+ int add_list_size;
/* do all the adds now */
filter_list_len = pf->hw.aq.asq_buf_size /
sizeof(struct i40e_aqc_add_macvlan_element_data),
- add_list = kcalloc(filter_list_len,
- sizeof(struct i40e_aqc_add_macvlan_element_data),
- GFP_KERNEL);
+ add_list_size = filter_list_len *
+ sizeof(struct i40e_aqc_add_macvlan_element_data);
+ add_list = kzalloc(add_list_size, GFP_KERNEL);
if (!add_list) {
/* Purge element from temporary lists */
i40e_cleanup_add_list(&tmp_add_list);
@@ -2019,7 +2020,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
spin_lock_bh(&vsi->mac_filter_list_lock);
i40e_undo_add_filter_entries(vsi);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto out;
}
list_for_each_entry_safe(f, ftmp, &tmp_add_list, list) {
@@ -2040,15 +2042,15 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
/* flush a full buffer */
if (num_add == filter_list_len) {
- ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
- add_list, num_add,
- NULL);
+ aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
+ add_list, num_add,
+ NULL);
aq_err = pf->hw.aq.asq_last_status;
num_add = 0;
- if (ret)
+ if (aq_ret)
break;
- memset(add_list, 0, sizeof(*add_list));
+ memset(add_list, 0, add_list_size);
}
/* Entries from tmp_add_list were cloned from MAC
* filter list, hence clean those cloned entries
@@ -2058,18 +2060,19 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
if (num_add) {
- ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
- add_list, num_add, NULL);
+ aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
+ add_list, num_add, NULL);
aq_err = pf->hw.aq.asq_last_status;
num_add = 0;
}
kfree(add_list);
add_list = NULL;
- if (add_happened && ret && aq_err != I40E_AQ_RC_EINVAL) {
+ if (add_happened && aq_ret && aq_err != I40E_AQ_RC_EINVAL) {
+ retval = i40e_aq_rc_to_posix(aq_ret, aq_err);
dev_info(&pf->pdev->dev,
"add filter failed, err %s aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw, aq_err));
if ((pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOSPC) &&
!test_bit(__I40E_FILTER_OVERFLOW_PROMISC,
@@ -2087,16 +2090,19 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
bool cur_multipromisc;
cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI);
- ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw,
- vsi->seid,
- cur_multipromisc,
- NULL);
- if (ret)
+ aq_ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw,
+ vsi->seid,
+ cur_multipromisc,
+ NULL);
+ if (aq_ret) {
+ retval = i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set multi promisc failed, err %s aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw,
pf->hw.aq.asq_last_status));
+ }
}
if ((changed_flags & IFF_PROMISC) || promisc_forced_on) {
bool cur_promisc;
@@ -2112,44 +2118,50 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
*/
if (pf->cur_promisc != cur_promisc) {
pf->cur_promisc = cur_promisc;
- if (grab_rtnl)
- i40e_do_reset_safe(pf,
- BIT(__I40E_PF_RESET_REQUESTED));
- else
- i40e_do_reset(pf,
- BIT(__I40E_PF_RESET_REQUESTED));
+ set_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
}
} else {
- ret = i40e_aq_set_vsi_unicast_promiscuous(
+ aq_ret = i40e_aq_set_vsi_unicast_promiscuous(
&vsi->back->hw,
vsi->seid,
cur_promisc, NULL);
- if (ret)
+ if (aq_ret) {
+ retval =
+ i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set unicast promisc failed, err %d, aq_err %d\n",
- ret, pf->hw.aq.asq_last_status);
- ret = i40e_aq_set_vsi_multicast_promiscuous(
+ aq_ret, pf->hw.aq.asq_last_status);
+ }
+ aq_ret = i40e_aq_set_vsi_multicast_promiscuous(
&vsi->back->hw,
vsi->seid,
cur_promisc, NULL);
- if (ret)
+ if (aq_ret) {
+ retval =
+ i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set multicast promisc failed, err %d, aq_err %d\n",
- ret, pf->hw.aq.asq_last_status);
+ aq_ret, pf->hw.aq.asq_last_status);
+ }
}
- ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw,
- vsi->seid,
- cur_promisc, NULL);
- if (ret)
+ aq_ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw,
+ vsi->seid,
+ cur_promisc, NULL);
+ if (aq_ret) {
+ retval = i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set brdcast promisc failed, err %s, aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw,
pf->hw.aq.asq_last_status));
+ }
}
-
+out:
clear_bit(__I40E_CONFIG_BUSY, &vsi->state);
- return 0;
+ return retval;
}
/**
@@ -2166,8 +2178,15 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
for (v = 0; v < pf->num_alloc_vsi; v++) {
if (pf->vsi[v] &&
- (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED))
- i40e_sync_vsi_filters(pf->vsi[v], true);
+ (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED)) {
+ int ret = i40e_sync_vsi_filters(pf->vsi[v]);
+
+ if (ret) {
+ /* come back and try again later */
+ pf->flags |= I40E_FLAG_FILTER_SYNC;
+ break;
+ }
+ }
}
}
@@ -2377,16 +2396,13 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
}
}
- /* Make sure to release before sync_vsi_filter because that
- * function will lock/unlock as necessary
- */
spin_unlock_bh(&vsi->mac_filter_list_lock);
- if (test_bit(__I40E_DOWN, &vsi->back->state) ||
- test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state))
- return 0;
-
- return i40e_sync_vsi_filters(vsi, false);
+ /* schedule our worker thread which will take care of
+ * applying the new filter changes
+ */
+ i40e_service_event_schedule(vsi->back);
+ return 0;
}
/**
@@ -2459,16 +2475,13 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
}
}
- /* Make sure to release before sync_vsi_filter because that
- * function with lock/unlock as necessary
- */
spin_unlock_bh(&vsi->mac_filter_list_lock);
- if (test_bit(__I40E_DOWN, &vsi->back->state) ||
- test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state))
- return 0;
-
- return i40e_sync_vsi_filters(vsi, false);
+ /* schedule our worker thread which will take care of
+ * applying the new filter changes
+ */
+ i40e_service_event_schedule(vsi->back);
+ return 0;
}
/**
@@ -2711,6 +2724,11 @@ static void i40e_config_xps_tx_ring(struct i40e_ring *ring)
netif_set_xps_queue(ring->netdev, mask, ring->queue_index);
free_cpumask_var(mask);
}
+
+ /* schedule our worker thread which will take care of
+ * applying the new filter changes
+ */
+ i40e_service_event_schedule(vsi->back);
}
/**
@@ -4360,17 +4378,41 @@ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi)
else
val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0);
+ /* Bail out if interrupts are disabled because napi_poll
+ * execution in-progress or will get scheduled soon.
+ * napi_poll cleans TX and RX queues and updates 'next_to_clean'.
+ */
+ if (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))
+ return;
+
head = i40e_get_head(tx_ring);
tx_pending = i40e_get_tx_pending(tx_ring);
- /* Interrupts are disabled and TX pending is non-zero,
- * trigger the SW interrupt (don't wait). Worst case
- * there will be one extra interrupt which may result
- * into not cleaning any queues because queues are cleaned.
+ /* HW is done executing descriptors, updated HEAD write back,
+ * but SW hasn't processed those descriptors. If interrupt is
+ * not generated from this point ON, it could result into
+ * dev_watchdog detecting timeout on those netdev_queue,
+ * hence proactively trigger SW interrupt.
*/
- if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK)))
- i40e_force_wb(vsi, tx_ring->q_vector);
+ if (tx_pending) {
+ /* NAPI Poll didn't run and clear since it was set */
+ if (test_and_clear_bit(I40E_Q_VECTOR_HUNG_DETECT,
+ &tx_ring->q_vector->hung_detected)) {
+ netdev_info(vsi->netdev, "VSI_seid %d, Hung TX queue %d, tx_pending: %d, NTC:0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x\n",
+ vsi->seid, q_idx, tx_pending,
+ tx_ring->next_to_clean, head,
+ tx_ring->next_to_use,
+ readl(tx_ring->tail));
+ netdev_info(vsi->netdev, "VSI_seid %d, Issuing force_wb for TX queue %d, Interrupt Reg: 0x%x\n",
+ vsi->seid, q_idx, val);
+ i40e_force_wb(vsi, tx_ring->q_vector);
+ } else {
+ /* First Chance - detected possible hung */
+ set_bit(I40E_Q_VECTOR_HUNG_DETECT,
+ &tx_ring->q_vector->hung_detected);
+ }
+ }
}
/**
@@ -4441,7 +4483,7 @@ static u8 i40e_get_iscsi_tc_map(struct i40e_pf *pf)
if (app.selector == I40E_APP_SEL_TCPIP &&
app.protocolid == I40E_APP_PROTOID_ISCSI) {
tc = dcbcfg->etscfg.prioritytable[app.priority];
- enabled_tc |= BIT_ULL(tc);
+ enabled_tc |= BIT(tc);
break;
}
}
@@ -4525,7 +4567,7 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf)
/* At least have TC0 */
enabled_tc = (enabled_tc ? enabled_tc : 0x1);
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
num_tc++;
}
return num_tc;
@@ -4547,7 +4589,7 @@ static u8 i40e_pf_get_default_tc(struct i40e_pf *pf)
/* Find the first enabled TC */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
break;
}
@@ -4707,7 +4749,7 @@ static void i40e_vsi_config_netdev_tc(struct i40e_vsi *vsi, u8 enabled_tc)
* will set the numtc for netdev as 2 that will be
* referenced by the netdev layer as TC 0 and 1.
*/
- if (vsi->tc_config.enabled_tc & BIT_ULL(i))
+ if (vsi->tc_config.enabled_tc & BIT(i))
netdev_set_tc_queue(netdev,
vsi->tc_config.tc_info[i].netdev_tc,
vsi->tc_config.tc_info[i].qcount,
@@ -4769,7 +4811,7 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc)
/* Enable ETS TCs with equal BW Share for now across all VSIs */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
bw_share[i] = 1;
}
@@ -4843,7 +4885,7 @@ int i40e_veb_config_tc(struct i40e_veb *veb, u8 enabled_tc)
/* Enable ETS TCs with equal BW Share for now */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
bw_data.tc_bw_share_credits[i] = 1;
}
@@ -5240,7 +5282,7 @@ static int i40e_setup_tc(struct net_device *netdev, u8 tc)
/* Generate TC map for number of tc requested */
for (i = 0; i < tc; i++)
- enabled_tc |= BIT_ULL(i);
+ enabled_tc |= BIT(i);
/* Requesting same TC configuration as already enabled */
if (enabled_tc == vsi->tc_config.enabled_tc)
@@ -5305,6 +5347,9 @@ int i40e_open(struct net_device *netdev)
#ifdef CONFIG_I40E_VXLAN
vxlan_get_rx_port(netdev);
#endif
+#ifdef CONFIG_I40E_GENEVE
+ geneve_get_rx_port(netdev);
+#endif
return 0;
}
@@ -5738,7 +5783,7 @@ static void i40e_handle_lan_overflow_event(struct i40e_pf *pf,
**/
static void i40e_service_event_complete(struct i40e_pf *pf)
{
- BUG_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state));
+ WARN_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state));
/* flush memory to make sure state is correct before next watchog */
smp_mb__before_atomic();
@@ -6013,6 +6058,9 @@ static void i40e_link_event(struct i40e_pf *pf)
i40e_status status;
bool new_link, old_link;
+ /* save off old link status information */
+ pf->hw.phy.link_info_old = pf->hw.phy.link_info;
+
/* set this to force the get_link_status call to refresh state */
pf->hw.phy.get_link_info = true;
@@ -6101,23 +6149,23 @@ static void i40e_reset_subtask(struct i40e_pf *pf)
rtnl_lock();
if (test_bit(__I40E_REINIT_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_REINIT_REQUESTED);
+ reset_flags |= BIT(__I40E_REINIT_REQUESTED);
clear_bit(__I40E_REINIT_REQUESTED, &pf->state);
}
if (test_bit(__I40E_PF_RESET_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_PF_RESET_REQUESTED);
+ reset_flags |= BIT(__I40E_PF_RESET_REQUESTED);
clear_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
}
if (test_bit(__I40E_CORE_RESET_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_CORE_RESET_REQUESTED);
+ reset_flags |= BIT(__I40E_CORE_RESET_REQUESTED);
clear_bit(__I40E_CORE_RESET_REQUESTED, &pf->state);
}
if (test_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED);
+ reset_flags |= BIT(__I40E_GLOBAL_RESET_REQUESTED);
clear_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state);
}
if (test_bit(__I40E_DOWN_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_DOWN_REQUESTED);
+ reset_flags |= BIT(__I40E_DOWN_REQUESTED);
clear_bit(__I40E_DOWN_REQUESTED, &pf->state);
}
@@ -6147,13 +6195,9 @@ unlock:
static void i40e_handle_link_event(struct i40e_pf *pf,
struct i40e_arq_event_info *e)
{
- struct i40e_hw *hw = &pf->hw;
struct i40e_aqc_get_link_status *status =
(struct i40e_aqc_get_link_status *)&e->desc.params.raw;
- /* save off old link status information */
- hw->phy.link_info_old = hw->phy.link_info;
-
/* Do a new status request to re-enable LSE reporting
* and load new status information into the hw struct
* This completely ignores any state information
@@ -6192,15 +6236,18 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
val = rd32(&pf->hw, pf->hw.aq.arq.len);
oldval = val;
if (val & I40E_PF_ARQLEN_ARQVFE_MASK) {
- dev_info(&pf->pdev->dev, "ARQ VF Error detected\n");
+ if (hw->debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ARQ VF Error detected\n");
val &= ~I40E_PF_ARQLEN_ARQVFE_MASK;
}
if (val & I40E_PF_ARQLEN_ARQOVFL_MASK) {
- dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n");
+ if (hw->debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n");
val &= ~I40E_PF_ARQLEN_ARQOVFL_MASK;
}
if (val & I40E_PF_ARQLEN_ARQCRIT_MASK) {
- dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n");
+ if (hw->debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n");
val &= ~I40E_PF_ARQLEN_ARQCRIT_MASK;
}
if (oldval != val)
@@ -6209,15 +6256,18 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
val = rd32(&pf->hw, pf->hw.aq.asq.len);
oldval = val;
if (val & I40E_PF_ATQLEN_ATQVFE_MASK) {
- dev_info(&pf->pdev->dev, "ASQ VF Error detected\n");
+ if (pf->hw.debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ASQ VF Error detected\n");
val &= ~I40E_PF_ATQLEN_ATQVFE_MASK;
}
if (val & I40E_PF_ATQLEN_ATQOVFL_MASK) {
- dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n");
+ if (pf->hw.debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n");
val &= ~I40E_PF_ATQLEN_ATQOVFL_MASK;
}
if (val & I40E_PF_ATQLEN_ATQCRIT_MASK) {
- dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n");
+ if (pf->hw.debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n");
val &= ~I40E_PF_ATQLEN_ATQCRIT_MASK;
}
if (oldval != val)
@@ -6268,6 +6318,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
break;
case i40e_aqc_opc_nvm_erase:
case i40e_aqc_opc_nvm_update:
+ case i40e_aqc_opc_oem_post_update:
i40e_debug(&pf->hw, I40E_DEBUG_NVM, "ARQ NVM operation completed\n");
break;
default:
@@ -6685,6 +6736,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
struct i40e_hw *hw = &pf->hw;
u8 set_fc_aq_fail = 0;
i40e_status ret;
+ u32 val;
u32 v;
/* Now we wait for GRST to settle out.
@@ -6823,6 +6875,20 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
}
}
+ /* Reconfigure hardware for allowing smaller MSS in the case
+ * of TSO, so that we avoid the MDD being fired and causing
+ * a reset in the case of small MSS+TSO.
+ */
+#define I40E_REG_MSS 0x000E64DC
+#define I40E_REG_MSS_MIN_MASK 0x3FF0000
+#define I40E_64BYTE_MSS 0x400000
+ val = rd32(hw, I40E_REG_MSS);
+ if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) {
+ val &= ~I40E_REG_MSS_MIN_MASK;
+ val |= I40E_64BYTE_MSS;
+ wr32(hw, I40E_REG_MSS, val);
+ }
+
if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) ||
(pf->hw.aq.fw_maj_ver < 4)) {
msleep(75);
@@ -6984,30 +7050,30 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf)
i40e_flush(hw);
}
-#ifdef CONFIG_I40E_VXLAN
/**
- * i40e_sync_vxlan_filters_subtask - Sync the VSI filter list with HW
+ * i40e_sync_udp_filters_subtask - Sync the VSI filter list with HW
* @pf: board private structure
**/
-static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
+static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf)
{
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
struct i40e_hw *hw = &pf->hw;
i40e_status ret;
__be16 port;
int i;
- if (!(pf->flags & I40E_FLAG_VXLAN_FILTER_SYNC))
+ if (!(pf->flags & I40E_FLAG_UDP_FILTER_SYNC))
return;
- pf->flags &= ~I40E_FLAG_VXLAN_FILTER_SYNC;
+ pf->flags &= ~I40E_FLAG_UDP_FILTER_SYNC;
for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
- if (pf->pending_vxlan_bitmap & BIT_ULL(i)) {
- pf->pending_vxlan_bitmap &= ~BIT_ULL(i);
- port = pf->vxlan_ports[i];
+ if (pf->pending_udp_bitmap & BIT_ULL(i)) {
+ pf->pending_udp_bitmap &= ~BIT_ULL(i);
+ port = pf->udp_ports[i].index;
if (port)
ret = i40e_aq_add_udp_tunnel(hw, ntohs(port),
- I40E_AQC_TUNNEL_TYPE_VXLAN,
+ pf->udp_ports[i].type,
NULL, NULL);
else
ret = i40e_aq_del_udp_tunnel(hw, i, NULL);
@@ -7020,13 +7086,13 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
i40e_stat_str(&pf->hw, ret),
i40e_aq_str(&pf->hw,
pf->hw.aq.asq_last_status));
- pf->vxlan_ports[i] = 0;
+ pf->udp_ports[i].index = 0;
}
}
}
+#endif
}
-#endif
/**
* i40e_service_task - Run the driver's async subtasks
* @work: pointer to work_struct containing our data
@@ -7051,8 +7117,8 @@ static void i40e_service_task(struct work_struct *work)
i40e_watchdog_subtask(pf);
i40e_fdir_reinit_subtask(pf);
i40e_sync_filters_subtask(pf);
-#ifdef CONFIG_I40E_VXLAN
- i40e_sync_vxlan_filters_subtask(pf);
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
+ i40e_sync_udp_filters_subtask(pf);
#endif
i40e_clean_adminq_subtask(pf);
@@ -7282,6 +7348,23 @@ static void i40e_vsi_free_arrays(struct i40e_vsi *vsi, bool free_qvectors)
}
/**
+ * i40e_clear_rss_config_user - clear the user configured RSS hash keys
+ * and lookup table
+ * @vsi: Pointer to VSI structure
+ */
+static void i40e_clear_rss_config_user(struct i40e_vsi *vsi)
+{
+ if (!vsi)
+ return;
+
+ kfree(vsi->rss_hkey_user);
+ vsi->rss_hkey_user = NULL;
+
+ kfree(vsi->rss_lut_user);
+ vsi->rss_lut_user = NULL;
+}
+
+/**
* i40e_vsi_clear - Deallocate the VSI provided
* @vsi: the VSI being un-configured
**/
@@ -7318,6 +7401,7 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi)
i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx);
i40e_vsi_free_arrays(vsi, true);
+ i40e_clear_rss_config_user(vsi);
pf->vsi[vsi->idx] = NULL;
if (vsi->idx < pf->next_vsi)
@@ -7780,7 +7864,8 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf)
* @vsi: vsi structure
* @seed: RSS hash seed
**/
-static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed)
+static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
{
struct i40e_aqc_get_set_rss_key_data rss_key;
struct i40e_pf *pf = vsi->back;
@@ -7833,43 +7918,57 @@ static int i40e_vsi_config_rss(struct i40e_vsi *vsi)
{
u8 seed[I40E_HKEY_ARRAY_SIZE];
struct i40e_pf *pf = vsi->back;
+ u8 *lut;
+ int ret;
- netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
- vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs);
+ if (!(pf->flags & I40E_FLAG_RSS_AQ_CAPABLE))
+ return 0;
- if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)
- return i40e_config_rss_aq(vsi, seed);
+ lut = kzalloc(vsi->rss_table_size, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
- return 0;
+ i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size);
+ netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
+ vsi->rss_size = min_t(int, pf->alloc_rss_size, vsi->num_queue_pairs);
+ ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size);
+ kfree(lut);
+
+ return ret;
}
/**
- * i40e_config_rss_reg - Prepare for RSS if used
- * @pf: board private structure
+ * i40e_config_rss_reg - Configure RSS keys and lut by writing registers
+ * @vsi: Pointer to vsi structure
* @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
**/
-static int i40e_config_rss_reg(struct i40e_pf *pf, const u8 *seed)
+static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+ const u8 *lut, u16 lut_size)
{
- struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
+ struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw;
- u32 *seed_dw = (u32 *)seed;
- u32 current_queue = 0;
- u32 lut = 0;
- int i, j;
+ u8 i;
/* Fill out hash function seed */
- for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
- wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]);
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
- for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) {
- lut = 0;
- for (j = 0; j < 4; j++) {
- if (current_queue == vsi->rss_size)
- current_queue = 0;
- lut |= ((current_queue) << (8 * j));
- current_queue++;
- }
- wr32(&pf->hw, I40E_PFQF_HLUT(i), lut);
+ for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
+ wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]);
+ }
+
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40E_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+
+ for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++)
+ wr32(hw, I40E_PFQF_HLUT(i), lut_dw[i]);
}
i40e_flush(hw);
@@ -7877,18 +7976,101 @@ static int i40e_config_rss_reg(struct i40e_pf *pf, const u8 *seed)
}
/**
- * i40e_config_rss - Prepare for RSS if used
+ * i40e_get_rss_reg - Get the RSS keys and lut by reading registers
+ * @vsi: Pointer to VSI structure
+ * @seed: Buffer to store the keys
+ * @lut: Buffer to store the lookup table entries
+ * @lut_size: Size of buffer to store the lookup table entries
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int i40e_get_rss_reg(struct i40e_vsi *vsi, u8 *seed,
+ u8 *lut, u16 lut_size)
+{
+ struct i40e_pf *pf = vsi->back;
+ struct i40e_hw *hw = &pf->hw;
+ u16 i;
+
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
+
+ for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
+ seed_dw[i] = rd32(hw, I40E_PFQF_HKEY(i));
+ }
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40E_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+ for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++)
+ lut_dw[i] = rd32(hw, I40E_PFQF_HLUT(i));
+ }
+
+ return 0;
+}
+
+/**
+ * i40e_config_rss - Configure RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ */
+int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+ struct i40e_pf *pf = vsi->back;
+
+ if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)
+ return i40e_config_rss_aq(vsi, seed, lut, lut_size);
+ else
+ return i40e_config_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40e_get_rss - Get RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: Buffer to store the keys
+ * @lut: Buffer to store the lookup table entries
+ * lut_size: Size of buffer to store the lookup table entries
+ *
+ * Returns 0 on success, negative on failure
+ */
+int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+ return i40e_get_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40e_fill_rss_lut - Fill the RSS lookup table with default values
+ * @pf: Pointer to board private structure
+ * @lut: Lookup table
+ * @rss_table_size: Lookup table size
+ * @rss_size: Range of queue number for hashing
+ */
+static void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut,
+ u16 rss_table_size, u16 rss_size)
+{
+ u16 i;
+
+ for (i = 0; i < rss_table_size; i++)
+ lut[i] = i % rss_size;
+}
+
+/**
+ * i40e_pf_config_rss - Prepare for RSS if used
* @pf: board private structure
**/
-static int i40e_config_rss(struct i40e_pf *pf)
+static int i40e_pf_config_rss(struct i40e_pf *pf)
{
struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
u8 seed[I40E_HKEY_ARRAY_SIZE];
+ u8 *lut;
struct i40e_hw *hw = &pf->hw;
u32 reg_val;
u64 hena;
-
- netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
+ int ret;
/* By default we enable TCP/UDP with IPv4/IPv6 ptypes */
hena = (u64)rd32(hw, I40E_PFQF_HENA(0)) |
@@ -7898,8 +8080,6 @@ static int i40e_config_rss(struct i40e_pf *pf)
wr32(hw, I40E_PFQF_HENA(0), (u32)hena);
wr32(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32));
- vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs);
-
/* Determine the RSS table size based on the hardware capabilities */
reg_val = rd32(hw, I40E_PFQF_CTL_0);
reg_val = (pf->rss_table_size == 512) ?
@@ -7907,10 +8087,32 @@ static int i40e_config_rss(struct i40e_pf *pf)
(reg_val & ~I40E_PFQF_CTL_0_HASHLUTSIZE_512);
wr32(hw, I40E_PFQF_CTL_0, reg_val);
- if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)
- return i40e_config_rss_aq(pf->vsi[pf->lan_vsi], seed);
+ /* Determine the RSS size of the VSI */
+ if (!vsi->rss_size)
+ vsi->rss_size = min_t(int, pf->alloc_rss_size,
+ vsi->num_queue_pairs);
+
+ lut = kzalloc(vsi->rss_table_size, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ /* Use user configured lut if there is one, otherwise use default */
+ if (vsi->rss_lut_user)
+ memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size);
+ else
+ i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size);
+
+ /* Use user configured hash key if there is one, otherwise
+ * use default.
+ */
+ if (vsi->rss_hkey_user)
+ memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE);
else
- return i40e_config_rss_reg(pf, seed);
+ netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
+ ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size);
+ kfree(lut);
+
+ return ret;
}
/**
@@ -7935,13 +8137,28 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count)
vsi->req_queue_pairs = queue_count;
i40e_prep_for_reset(pf);
- pf->rss_size = new_rss_size;
+ pf->alloc_rss_size = new_rss_size;
i40e_reset_and_rebuild(pf, true);
- i40e_config_rss(pf);
+
+ /* Discard the user configured hash keys and lut, if less
+ * queues are enabled.
+ */
+ if (queue_count < vsi->rss_size) {
+ i40e_clear_rss_config_user(vsi);
+ dev_dbg(&pf->pdev->dev,
+ "discard user configured hash keys and lut\n");
+ }
+
+ /* Reset vsi->rss_size, as number of enabled queues changed */
+ vsi->rss_size = min_t(int, pf->alloc_rss_size,
+ vsi->num_queue_pairs);
+
+ i40e_pf_config_rss(pf);
}
- dev_info(&pf->pdev->dev, "RSS count: %d\n", pf->rss_size);
- return pf->rss_size;
+ dev_info(&pf->pdev->dev, "RSS count/HW max RSS count: %d/%d\n",
+ pf->alloc_rss_size, pf->rss_size_max);
+ return pf->alloc_rss_size;
}
/**
@@ -8112,13 +8329,14 @@ static int i40e_sw_init(struct i40e_pf *pf)
* maximum might end up larger than the available queues
*/
pf->rss_size_max = BIT(pf->hw.func_caps.rss_table_entry_width);
- pf->rss_size = 1;
+ pf->alloc_rss_size = 1;
pf->rss_table_size = pf->hw.func_caps.rss_table_size;
pf->rss_size_max = min_t(int, pf->rss_size_max,
pf->hw.func_caps.num_tx_qp);
if (pf->hw.func_caps.rss) {
pf->flags |= I40E_FLAG_RSS_ENABLED;
- pf->rss_size = min_t(int, pf->rss_size_max, num_online_cpus());
+ pf->alloc_rss_size = min_t(int, pf->rss_size_max,
+ num_online_cpus());
}
/* MFP mode enabled */
@@ -8176,7 +8394,8 @@ static int i40e_sw_init(struct i40e_pf *pf)
I40E_FLAG_HW_ATR_EVICT_CAPABLE |
I40E_FLAG_OUTER_UDP_CSUM_CAPABLE |
I40E_FLAG_WB_ON_ITR_CAPABLE |
- I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE;
+ I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE |
+ I40E_FLAG_GENEVE_OFFLOAD_CAPABLE;
}
pf->eeprom_version = 0xDEAD;
pf->lan_veb = I40E_NO_VEB;
@@ -8275,26 +8494,27 @@ static int i40e_set_features(struct net_device *netdev,
return 0;
}
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
/**
- * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port
+ * i40e_get_udp_port_idx - Lookup a possibly offloaded for Rx UDP port
* @pf: board private structure
* @port: The UDP port to look up
*
* Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found
**/
-static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port)
+static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, __be16 port)
{
u8 i;
for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
- if (pf->vxlan_ports[i] == port)
+ if (pf->udp_ports[i].index == port)
return i;
}
return i;
}
+#endif
/**
* i40e_add_vxlan_port - Get notifications about VXLAN ports that come up
* @netdev: This physical port's netdev
@@ -8304,6 +8524,7 @@ static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port)
static void i40e_add_vxlan_port(struct net_device *netdev,
sa_family_t sa_family, __be16 port)
{
+#if IS_ENABLED(CONFIG_VXLAN)
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
@@ -8313,7 +8534,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
if (sa_family == AF_INET6)
return;
- idx = i40e_get_vxlan_port_idx(pf, port);
+ idx = i40e_get_udp_port_idx(pf, port);
/* Check if port already exists */
if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
@@ -8323,7 +8544,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
}
/* Now check if there is space to add the new port */
- next_idx = i40e_get_vxlan_port_idx(pf, 0);
+ next_idx = i40e_get_udp_port_idx(pf, 0);
if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
netdev_info(netdev, "maximum number of vxlan UDP ports reached, not adding port %d\n",
@@ -8332,9 +8553,11 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
}
/* New port: add it and mark its index in the bitmap */
- pf->vxlan_ports[next_idx] = port;
- pf->pending_vxlan_bitmap |= BIT_ULL(next_idx);
- pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+ pf->udp_ports[next_idx].index = port;
+ pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN;
+ pf->pending_udp_bitmap |= BIT_ULL(next_idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+#endif
}
/**
@@ -8346,6 +8569,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
static void i40e_del_vxlan_port(struct net_device *netdev,
sa_family_t sa_family, __be16 port)
{
+#if IS_ENABLED(CONFIG_VXLAN)
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
@@ -8354,23 +8578,108 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
if (sa_family == AF_INET6)
return;
- idx = i40e_get_vxlan_port_idx(pf, port);
+ idx = i40e_get_udp_port_idx(pf, port);
/* Check if port already exists */
if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
/* if port exists, set it to 0 (mark for deletion)
* and make it pending
*/
- pf->vxlan_ports[idx] = 0;
- pf->pending_vxlan_bitmap |= BIT_ULL(idx);
- pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+ pf->udp_ports[idx].index = 0;
+ pf->pending_udp_bitmap |= BIT_ULL(idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
} else {
netdev_warn(netdev, "vxlan port %d was not found, not deleting\n",
ntohs(port));
}
+#endif
+}
+
+/**
+ * i40e_add_geneve_port - Get notifications about GENEVE ports that come up
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that GENEVE is notifying us about
+ * @port: New UDP port number that GENEVE started listening to
+ **/
+static void i40e_add_geneve_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+#if IS_ENABLED(CONFIG_GENEVE)
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
+ struct i40e_vsi *vsi = np->vsi;
+ struct i40e_pf *pf = vsi->back;
+ u8 next_idx;
+ u8 idx;
+
+ if (sa_family == AF_INET6)
+ return;
+
+ idx = i40e_get_udp_port_idx(pf, port);
+
+ /* Check if port already exists */
+ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+ netdev_info(netdev, "udp port %d already offloaded\n",
+ ntohs(port));
+ return;
+ }
+
+ /* Now check if there is space to add the new port */
+ next_idx = i40e_get_udp_port_idx(pf, 0);
+
+ if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+ netdev_info(netdev, "maximum number of UDP ports reached, not adding port %d\n",
+ ntohs(port));
+ return;
+ }
+
+ /* New port: add it and mark its index in the bitmap */
+ pf->udp_ports[next_idx].index = port;
+ pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE;
+ pf->pending_udp_bitmap |= BIT_ULL(next_idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+
+ dev_info(&pf->pdev->dev, "adding geneve port %d\n", ntohs(port));
+#endif
}
+/**
+ * i40e_del_geneve_port - Get notifications about GENEVE ports that go away
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that GENEVE is notifying us about
+ * @port: UDP port number that GENEVE stopped listening to
+ **/
+static void i40e_del_geneve_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+#if IS_ENABLED(CONFIG_GENEVE)
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
+ struct i40e_vsi *vsi = np->vsi;
+ struct i40e_pf *pf = vsi->back;
+ u8 idx;
+
+ if (sa_family == AF_INET6)
+ return;
+
+ idx = i40e_get_udp_port_idx(pf, port);
+
+ /* Check if port already exists */
+ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+ /* if port exists, set it to 0 (mark for deletion)
+ * and make it pending
+ */
+ pf->udp_ports[idx].index = 0;
+ pf->pending_udp_bitmap |= BIT_ULL(idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+
+ dev_info(&pf->pdev->dev, "deleting geneve port %d\n",
+ ntohs(port));
+ } else {
+ netdev_warn(netdev, "geneve port %d was not found, not deleting\n",
+ ntohs(port));
+ }
#endif
+}
+
static int i40e_get_phys_port_id(struct net_device *netdev,
struct netdev_phys_item_id *ppid)
{
@@ -8548,7 +8857,10 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
nlflags, 0, 0, filter_mask, NULL);
}
-#define I40E_MAX_TUNNEL_HDR_LEN 80
+/* Hardware supports L4 tunnel length of 128B (=2^7) which includes
+ * inner mac plus all inner ethertypes.
+ */
+#define I40E_MAX_TUNNEL_HDR_LEN 128
/**
* i40e_features_check - Validate encapsulated packet conforms to limits
* @skb: skb buff
@@ -8560,9 +8872,9 @@ static netdev_features_t i40e_features_check(struct sk_buff *skb,
netdev_features_t features)
{
if (skb->encapsulation &&
- (skb_inner_mac_header(skb) - skb_transport_header(skb) >
+ ((skb_inner_network_header(skb) - skb_transport_header(skb)) >
I40E_MAX_TUNNEL_HDR_LEN))
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
return features;
}
@@ -8595,10 +8907,14 @@ static const struct net_device_ops i40e_netdev_ops = {
.ndo_get_vf_config = i40e_ndo_get_vf_config,
.ndo_set_vf_link_state = i40e_ndo_set_vf_link_state,
.ndo_set_vf_spoofchk = i40e_ndo_set_vf_spoofchk,
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN)
.ndo_add_vxlan_port = i40e_add_vxlan_port,
.ndo_del_vxlan_port = i40e_del_vxlan_port,
#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+ .ndo_add_geneve_port = i40e_add_geneve_port,
+ .ndo_del_geneve_port = i40e_del_geneve_port,
+#endif
.ndo_get_phys_port_id = i40e_get_phys_port_id,
.ndo_fdb_add = i40e_ndo_fdb_add,
.ndo_features_check = i40e_features_check,
@@ -8632,13 +8948,14 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
np->vsi = vsi;
netdev->hw_enc_features |= NETIF_F_IP_CSUM |
+ NETIF_F_RXCSUM |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_GRE |
NETIF_F_TSO;
netdev->features = NETIF_F_SG |
NETIF_F_IP_CSUM |
- NETIF_F_SCTP_CSUM |
+ NETIF_F_SCTP_CRC |
NETIF_F_HIGHDMA |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_GRE |
@@ -9051,7 +9368,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
f->is_vf, f->is_netdev);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- i40e_sync_vsi_filters(vsi, false);
+ i40e_sync_vsi_filters(vsi);
i40e_vsi_delete(vsi);
i40e_vsi_free_q_vectors(vsi);
@@ -9213,6 +9530,44 @@ err_vsi:
}
/**
+ * i40e_macaddr_init - explicitly write the mac address filters.
+ *
+ * @vsi: pointer to the vsi.
+ * @macaddr: the MAC address
+ *
+ * This is needed when the macaddr has been obtained by other
+ * means than the default, e.g., from Open Firmware or IDPROM.
+ * Returns 0 on success, negative on failure
+ **/
+static int i40e_macaddr_init(struct i40e_vsi *vsi, u8 *macaddr)
+{
+ int ret;
+ struct i40e_aqc_add_macvlan_element_data element;
+
+ ret = i40e_aq_mac_address_write(&vsi->back->hw,
+ I40E_AQC_WRITE_TYPE_LAA_WOL,
+ macaddr, NULL);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "Addr change for VSI failed: %d\n", ret);
+ return -EADDRNOTAVAIL;
+ }
+
+ memset(&element, 0, sizeof(element));
+ ether_addr_copy(element.mac_addr, macaddr);
+ element.flags = cpu_to_le16(I40E_AQC_MACVLAN_ADD_PERFECT_MATCH);
+ ret = i40e_aq_add_macvlan(&vsi->back->hw, vsi->seid, &element, 1, NULL);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "add filter failed err %s aq_err %s\n",
+ i40e_stat_str(&vsi->back->hw, ret),
+ i40e_aq_str(&vsi->back->hw,
+ vsi->back->hw.aq.asq_last_status));
+ }
+ return ret;
+}
+
+/**
* i40e_vsi_setup - Set up a VSI by a given type
* @pf: board private structure
* @type: VSI type
@@ -9336,6 +9691,17 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
switch (vsi->type) {
/* setup the netdev if needed */
case I40E_VSI_MAIN:
+ /* Apply relevant filters if a platform-specific mac
+ * address was selected.
+ */
+ if (!!(pf->flags & I40E_FLAG_PF_MAC)) {
+ ret = i40e_macaddr_init(vsi, pf->hw.mac.addr);
+ if (ret) {
+ dev_warn(&pf->pdev->dev,
+ "could not set up macaddr; err %d\n",
+ ret);
+ }
+ }
case I40E_VSI_VMDQ2:
case I40E_VSI_FCOE:
ret = i40e_config_netdev(vsi);
@@ -9947,7 +10313,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
* the hash
*/
if ((pf->flags & I40E_FLAG_RSS_ENABLED))
- i40e_config_rss(pf);
+ i40e_pf_config_rss(pf);
/* fill in link information and enable LSE reporting */
i40e_update_link_info(&pf->hw);
@@ -9985,7 +10351,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
!(pf->flags & I40E_FLAG_MSIX_ENABLED)) {
/* one qp for PF, no queues for anything else */
queues_left = 0;
- pf->rss_size = pf->num_lan_qps = 1;
+ pf->alloc_rss_size = pf->num_lan_qps = 1;
/* make sure all the fancies are disabled */
pf->flags &= ~(I40E_FLAG_RSS_ENABLED |
@@ -10002,7 +10368,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
I40E_FLAG_FD_ATR_ENABLED |
I40E_FLAG_DCB_CAPABLE))) {
/* one qp for PF */
- pf->rss_size = pf->num_lan_qps = 1;
+ pf->alloc_rss_size = pf->num_lan_qps = 1;
queues_left -= pf->num_lan_qps;
pf->flags &= ~(I40E_FLAG_RSS_ENABLED |
@@ -10072,8 +10438,9 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
"qs_avail=%d FD SB=%d lan_qs=%d lan_tc0=%d vf=%d*%d vmdq=%d*%d, remaining=%d\n",
pf->hw.func_caps.num_tx_qp,
!!(pf->flags & I40E_FLAG_FD_SB_ENABLED),
- pf->num_lan_qps, pf->rss_size, pf->num_req_vfs, pf->num_vf_qps,
- pf->num_vmdq_vsis, pf->num_vmdq_qps, queues_left);
+ pf->num_lan_qps, pf->alloc_rss_size, pf->num_req_vfs,
+ pf->num_vf_qps, pf->num_vmdq_vsis, pf->num_vmdq_qps,
+ queues_left);
#ifdef I40E_FCOE
dev_dbg(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps);
#endif
@@ -10111,55 +10478,86 @@ static int i40e_setup_pf_filter_control(struct i40e_pf *pf)
}
#define INFO_STRING_LEN 255
+#define REMAIN(__x) (INFO_STRING_LEN - (__x))
static void i40e_print_features(struct i40e_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
- char *buf, *string;
+ char *buf;
+ int i;
- string = kzalloc(INFO_STRING_LEN, GFP_KERNEL);
- if (!string) {
- dev_err(&pf->pdev->dev, "Features string allocation failed\n");
+ buf = kmalloc(INFO_STRING_LEN, GFP_KERNEL);
+ if (!buf)
return;
- }
- buf = string;
-
- buf += sprintf(string, "Features: PF-id[%d] ", hw->pf_id);
+ i = snprintf(buf, INFO_STRING_LEN, "Features: PF-id[%d]", hw->pf_id);
#ifdef CONFIG_PCI_IOV
- buf += sprintf(buf, "VFs: %d ", pf->num_req_vfs);
+ i += snprintf(&buf[i], REMAIN(i), " VFs: %d", pf->num_req_vfs);
#endif
- buf += sprintf(buf, "VSIs: %d QP: %d RX: %s ",
- pf->hw.func_caps.num_vsis,
- pf->vsi[pf->lan_vsi]->num_queue_pairs,
- pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF");
+ i += snprintf(&buf[i], REMAIN(i), " VSIs: %d QP: %d RX: %s",
+ pf->hw.func_caps.num_vsis,
+ pf->vsi[pf->lan_vsi]->num_queue_pairs,
+ pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF");
if (pf->flags & I40E_FLAG_RSS_ENABLED)
- buf += sprintf(buf, "RSS ");
+ i += snprintf(&buf[i], REMAIN(i), " RSS");
if (pf->flags & I40E_FLAG_FD_ATR_ENABLED)
- buf += sprintf(buf, "FD_ATR ");
+ i += snprintf(&buf[i], REMAIN(i), " FD_ATR");
if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
- buf += sprintf(buf, "FD_SB ");
- buf += sprintf(buf, "NTUPLE ");
+ i += snprintf(&buf[i], REMAIN(i), " FD_SB");
+ i += snprintf(&buf[i], REMAIN(i), " NTUPLE");
}
if (pf->flags & I40E_FLAG_DCB_CAPABLE)
- buf += sprintf(buf, "DCB ");
+ i += snprintf(&buf[i], REMAIN(i), " DCB");
#if IS_ENABLED(CONFIG_VXLAN)
- buf += sprintf(buf, "VxLAN ");
+ i += snprintf(&buf[i], REMAIN(i), " VxLAN");
+#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+ i += snprintf(&buf[i], REMAIN(i), " Geneve");
#endif
if (pf->flags & I40E_FLAG_PTP)
- buf += sprintf(buf, "PTP ");
+ i += snprintf(&buf[i], REMAIN(i), " PTP");
#ifdef I40E_FCOE
if (pf->flags & I40E_FLAG_FCOE_ENABLED)
- buf += sprintf(buf, "FCOE ");
+ i += snprintf(&buf[i], REMAIN(i), " FCOE");
#endif
if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED)
- buf += sprintf(buf, "VEB ");
+ i += snprintf(&buf[i], REMAIN(i), " VEB");
else
- buf += sprintf(buf, "VEPA ");
+ i += snprintf(&buf[i], REMAIN(i), " VEPA");
- BUG_ON(buf > (string + INFO_STRING_LEN));
- dev_info(&pf->pdev->dev, "%s\n", string);
- kfree(string);
+ dev_info(&pf->pdev->dev, "%s\n", buf);
+ kfree(buf);
+ WARN_ON(i > INFO_STRING_LEN);
+}
+
+/**
+ * i40e_get_platform_mac_addr - get platform-specific MAC address
+ *
+ * @pdev: PCI device information struct
+ * @pf: board private structure
+ *
+ * Look up the MAC address in Open Firmware on systems that support it,
+ * and use IDPROM on SPARC if no OF address is found. On return, the
+ * I40E_FLAG_PF_MAC will be wset in pf->flags if a platform-specific value
+ * has been selected.
+ **/
+static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf)
+{
+ struct device_node *dp = pci_device_to_OF_node(pdev);
+ const unsigned char *addr;
+ u8 *mac_addr = pf->hw.mac.addr;
+
+ pf->flags &= ~I40E_FLAG_PF_MAC;
+ addr = of_get_mac_address(dp);
+ if (addr) {
+ ether_addr_copy(mac_addr, addr);
+ pf->flags |= I40E_FLAG_PF_MAC;
+#ifdef CONFIG_SPARC
+ } else {
+ ether_addr_copy(mac_addr, idprom->id_ethaddr);
+ pf->flags |= I40E_FLAG_PF_MAC;
+#endif /* CONFIG_SPARC */
+ }
}
/**
@@ -10183,6 +10581,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
u16 link_status;
int err;
u32 len;
+ u32 val;
u32 i;
u8 set_fc_aq_fail;
@@ -10302,6 +10701,16 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
mutex_init(&hw->aq.arq_mutex);
err = i40e_init_adminq(hw);
+ if (err) {
+ if (err == I40E_ERR_FIRMWARE_API_VERSION)
+ dev_info(&pdev->dev,
+ "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n");
+ else
+ dev_info(&pdev->dev,
+ "The driver for the device stopped because the device firmware failed to init. Try updating your NVM image.\n");
+
+ goto err_pf_reset;
+ }
/* provide nvm, fw, api versions */
dev_info(&pdev->dev, "fw %d.%d.%05d api %d.%d nvm %s\n",
@@ -10309,12 +10718,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->aq.api_maj_ver, hw->aq.api_min_ver,
i40e_nvm_version_str(hw));
- if (err) {
- dev_info(&pdev->dev,
- "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n");
- goto err_pf_reset;
- }
-
if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR)
dev_info(&pdev->dev,
@@ -10367,6 +10770,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
i40e_get_mac_addr(hw, hw->mac.addr);
+ /* allow a platform config to override the HW addr */
+ i40e_get_platform_mac_addr(pdev, pf);
if (!is_valid_ether_addr(hw->mac.addr)) {
dev_info(&pdev->dev, "invalid MAC address %pM\n", hw->mac.addr);
err = -EIO;
@@ -10411,7 +10816,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
- if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1)
+ if (BIT (hw->port) & wol_nvm_bits || hw->partition_id != 1)
pf->wol_en = false;
else
pf->wol_en = true;
@@ -10493,6 +10898,17 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
i40e_stat_str(&pf->hw, err),
i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+ /* Reconfigure hardware for allowing smaller MSS in the case
+ * of TSO, so that we avoid the MDD being fired and causing
+ * a reset in the case of small MSS+TSO.
+ */
+ val = rd32(hw, I40E_REG_MSS);
+ if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) {
+ val &= ~I40E_REG_MSS_MIN_MASK;
+ val |= I40E_64BYTE_MSS;
+ wr32(hw, I40E_REG_MSS, val);
+ }
+
if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) ||
(pf->hw.aq.fw_maj_ver < 4)) {
msleep(75);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 635b3ac17877..720516b0e8ee 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -235,6 +235,9 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
"Filter deleted for PCTYPE %d loc = %d\n",
fd_data->pctype, fd_data->fd_id);
}
+ if (err)
+ kfree(raw_packet);
+
return err ? -EOPNOTSUPP : 0;
}
@@ -312,6 +315,9 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
fd_data->pctype, fd_data->fd_id);
}
+ if (err)
+ kfree(raw_packet);
+
return err ? -EOPNOTSUPP : 0;
}
@@ -322,7 +328,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
* @fd_data: the flow director data required for the FDir descriptor
* @add: true adds a filter, false removes it
*
- * Always returns -EOPNOTSUPP
+ * Returns 0 if the filters were successfully added or removed
**/
static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi,
struct i40e_fdir_filter *fd_data,
@@ -387,6 +393,9 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi,
}
}
+ if (err)
+ kfree(raw_packet);
+
return err ? -EOPNOTSUPP : 0;
}
@@ -506,9 +515,6 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
pf->auto_disable_flags |=
I40E_FLAG_FD_SB_ENABLED;
}
- } else {
- dev_info(&pdev->dev,
- "FD filter programming failed due to incorrect filter parameters\n");
}
} else if (error == BIT(I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) {
if (I40E_DEBUG_FD & pf->hw.debug_mask)
@@ -526,11 +532,7 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
struct i40e_tx_buffer *tx_buffer)
{
if (tx_buffer->skb) {
- if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
- kfree(tx_buffer->raw_buf);
- else
- dev_kfree_skb_any(tx_buffer->skb);
-
+ dev_kfree_skb_any(tx_buffer->skb);
if (dma_unmap_len(tx_buffer, len))
dma_unmap_single(ring->dev,
dma_unmap_addr(tx_buffer, dma),
@@ -542,6 +544,10 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
dma_unmap_len(tx_buffer, len),
DMA_TO_DEVICE);
}
+
+ if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
+ kfree(tx_buffer->raw_buf);
+
tx_buffer->next_to_watch = NULL;
tx_buffer->skb = NULL;
dma_unmap_len_set(tx_buffer, len, 0);
@@ -1374,7 +1380,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
return;
- /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+ /* If VXLAN/GENEVE traffic has an outer UDPv4 checksum we need to check
* it in the driver, hardware does not do it for us.
* Since L3L4P bit was set we assume a valid IHL value (>=5)
* so the total length of IPv4 header is IHL*4 bytes
@@ -1416,31 +1422,12 @@ checksum_fail:
}
/**
- * i40e_rx_hash - returns the hash value from the Rx descriptor
- * @ring: descriptor ring
- * @rx_desc: specific descriptor
- **/
-static inline u32 i40e_rx_hash(struct i40e_ring *ring,
- union i40e_rx_desc *rx_desc)
-{
- const __le64 rss_mask =
- cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
- I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
-
- if ((ring->netdev->features & NETIF_F_RXHASH) &&
- (rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask)
- return le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
- else
- return 0;
-}
-
-/**
- * i40e_ptype_to_hash - get a hash type
+ * i40e_ptype_to_htype - get a hash type
* @ptype: the ptype value from the descriptor
*
* Returns a hash type to be used by skb_set_hash
**/
-static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
+static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype)
{
struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype);
@@ -1458,6 +1445,30 @@ static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
}
/**
+ * i40e_rx_hash - set the hash value in the skb
+ * @ring: descriptor ring
+ * @rx_desc: specific descriptor
+ **/
+static inline void i40e_rx_hash(struct i40e_ring *ring,
+ union i40e_rx_desc *rx_desc,
+ struct sk_buff *skb,
+ u8 rx_ptype)
+{
+ u32 hash;
+ const __le64 rss_mask =
+ cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
+ I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
+
+ if (ring->netdev->features & NETIF_F_RXHASH)
+ return;
+
+ if ((rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) {
+ hash = le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
+ skb_set_hash(skb, hash, i40e_ptype_to_htype(rx_ptype));
+ }
+}
+
+/**
* i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split
* @rx_ring: rx ring to clean
* @budget: how many cleans we're allowed
@@ -1606,8 +1617,8 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
+
if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) {
i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status &
I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >>
@@ -1632,7 +1643,6 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
#endif
- skb_mark_napi_id(skb, &rx_ring->q_vector->napi);
i40e_receive_skb(rx_ring, skb, vlan_tag);
rx_desc->wb.qword1.status_error_len = 0;
@@ -1736,8 +1746,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) {
i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status &
I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >>
@@ -1864,7 +1873,6 @@ enable_int:
q_vector->itr_countdown--;
else
q_vector->itr_countdown = ITR_COUNTDOWN_START;
-
}
/**
@@ -1892,12 +1900,14 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
return 0;
}
+ /* Clear hung_detected bit */
+ clear_bit(I40E_Q_VECTOR_HUNG_DETECT, &q_vector->hung_detected);
/* Since the actual Tx work is minimal, we can give the Tx a larger
* budget and be more aggressive about cleaning up the Tx descriptors.
*/
i40e_for_each_ring(ring, q_vector->tx) {
clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit);
- arm_wb |= ring->arm_wb;
+ arm_wb = arm_wb || ring->arm_wb;
ring->arm_wb = false;
}
@@ -1926,8 +1936,10 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
/* If work not completed, return budget and polling will return */
if (!clean_complete) {
tx_only:
- if (arm_wb)
+ if (arm_wb) {
+ q_vector->tx.ring[0].tx_stats.tx_force_wb++;
i40e_force_wb(vsi, q_vector);
+ }
return budget;
}
@@ -1993,7 +2005,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (!(tx_flags & (I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6)))
return;
- if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL)) {
+ if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL)) {
/* snag network header to get L4 type and address */
hdr.network = skb_network_header(skb);
@@ -2078,7 +2090,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT;
dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;
- if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL))
+ if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL))
dtype_cmd |=
((u32)I40E_FD_ATR_STAT_IDX(pf->hw.pf_id) <<
I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) &
@@ -2187,14 +2199,12 @@ out:
* @tx_ring: ptr to the ring to send
* @skb: ptr to the skb we're sending
* @hdr_len: ptr to the size of the packet header
- * @cd_type_cmd_tso_mss: ptr to u64 object
- * @cd_tunneling: ptr to context descriptor bits
+ * @cd_type_cmd_tso_mss: Quad Word 1
*
* Returns 0 if no TSO can happen, 1 if tso is going, or error
**/
static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
- u8 *hdr_len, u64 *cd_type_cmd_tso_mss,
- u32 *cd_tunneling)
+ u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
{
u32 cd_cmd, cd_tso_len, cd_mss;
struct ipv6hdr *ipv6h;
@@ -2247,7 +2257,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
* @tx_ring: ptr to the ring to send
* @skb: ptr to the skb we're sending
* @tx_flags: the collected send information
- * @cd_type_cmd_tso_mss: ptr to u64 object
+ * @cd_type_cmd_tso_mss: Quad Word 1
*
* Returns 0 if no Tx timestamp can happen and 1 if the timestamp will happen
**/
@@ -2313,7 +2323,7 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
oudph = udp_hdr(skb);
oiph = ip_hdr(skb);
l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING;
- *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL;
+ *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL;
break;
case IPPROTO_GRE:
l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING;
@@ -2807,6 +2817,9 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
int tsyn;
int tso;
+ /* prefetch the data, we'll need it later */
+ prefetch(skb->data);
+
if (0 == i40e_xmit_descriptor_count(skb, tx_ring))
return NETDEV_TX_BUSY;
@@ -2826,8 +2839,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
else if (protocol == htons(ETH_P_IPV6))
tx_flags |= I40E_TX_FLAGS_IPV6;
- tso = i40e_tso(tx_ring, skb, &hdr_len,
- &cd_type_cmd_tso_mss, &cd_tunneling);
+ tso = i40e_tso(tx_ring, skb, &hdr_len, &cd_type_cmd_tso_mss);
if (tso < 0)
goto out_drop;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index 6779fb771d6a..3f081e25e097 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -163,7 +163,7 @@ enum i40e_dyn_idx_t {
#define I40E_TX_FLAGS_FSO BIT(7)
#define I40E_TX_FLAGS_TSYN BIT(8)
#define I40E_TX_FLAGS_FD_SB BIT(9)
-#define I40E_TX_FLAGS_VXLAN_TUNNEL BIT(10)
+#define I40E_TX_FLAGS_UDP_TUNNEL BIT(10)
#define I40E_TX_FLAGS_VLAN_MASK 0xffff0000
#define I40E_TX_FLAGS_VLAN_PRIO_MASK 0xe0000000
#define I40E_TX_FLAGS_VLAN_PRIO_SHIFT 29
@@ -202,6 +202,7 @@ struct i40e_tx_queue_stats {
u64 tx_busy;
u64 tx_done_old;
u64 tx_linearize;
+ u64 tx_force_wb;
};
struct i40e_rx_queue_stats {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
index ae879826084b..3226946bf3d4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
@@ -153,6 +153,7 @@ struct i40e_virtchnl_vsi_resource {
#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020
#define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000
#define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000
+#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000
struct i40e_virtchnl_vf_resource {
u16 num_vsis;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index 44462b40f2d7..63e62f9aec6e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -290,8 +290,8 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id,
next_q = find_first_bit(&linklistmap,
(I40E_MAX_VSI_QP *
I40E_VIRTCHNL_SUPPORTED_QTYPES));
- vsi_queue_id = next_q/I40E_VIRTCHNL_SUPPORTED_QTYPES;
- qtype = next_q%I40E_VIRTCHNL_SUPPORTED_QTYPES;
+ vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES;
+ qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES;
pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id);
reg = ((qtype << I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT) | pf_queue_id);
@@ -549,12 +549,15 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
i40e_vsi_add_pvid(vsi, vf->port_vlan_id);
spin_lock_bh(&vsi->mac_filter_list_lock);
- f = i40e_add_filter(vsi, vf->default_lan_addr.addr,
- vf->port_vlan_id ? vf->port_vlan_id : -1,
- true, false);
- if (!f)
- dev_info(&pf->pdev->dev,
- "Could not allocate VF MAC addr\n");
+ if (is_valid_ether_addr(vf->default_lan_addr.addr)) {
+ f = i40e_add_filter(vsi, vf->default_lan_addr.addr,
+ vf->port_vlan_id ? vf->port_vlan_id : -1,
+ true, false);
+ if (!f)
+ dev_info(&pf->pdev->dev,
+ "Could not add MAC filter %pM for VF %d\n",
+ vf->default_lan_addr.addr, vf->vf_id);
+ }
f = i40e_add_filter(vsi, brdcast,
vf->port_vlan_id ? vf->port_vlan_id : -1,
true, false);
@@ -565,7 +568,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
}
/* program mac filter */
- ret = i40e_sync_vsi_filters(vsi, false);
+ ret = i40e_sync_vsi_filters(vsi);
if (ret)
dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
@@ -1094,8 +1097,8 @@ static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode,
/* single place to detect unsuccessful return values */
if (v_retval) {
vf->num_invalid_msgs++;
- dev_err(&pf->pdev->dev, "Failed opcode %d Error: %d\n",
- v_opcode, v_retval);
+ dev_err(&pf->pdev->dev, "VF %d failed opcode %d, error: %d\n",
+ vf->vf_id, v_opcode, v_retval);
if (vf->num_invalid_msgs >
I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED) {
dev_err(&pf->pdev->dev,
@@ -1623,7 +1626,8 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
if (!f) {
dev_err(&pf->pdev->dev,
- "Unable to add VF MAC filter\n");
+ "Unable to add MAC filter %pM for VF %d\n",
+ al->list[i].addr, vf->vf_id);
ret = I40E_ERR_PARAM;
spin_unlock_bh(&vsi->mac_filter_list_lock);
goto error_param;
@@ -1632,8 +1636,10 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
spin_unlock_bh(&vsi->mac_filter_list_lock);
/* program the updated filter list */
- if (i40e_sync_vsi_filters(vsi, false))
- dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n");
+ ret = i40e_sync_vsi_filters(vsi);
+ if (ret)
+ dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n",
+ vf->vf_id, ret);
error_param:
/* send the response to the VF */
@@ -1669,8 +1675,8 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
for (i = 0; i < al->num_elements; i++) {
if (is_broadcast_ether_addr(al->list[i].addr) ||
is_zero_ether_addr(al->list[i].addr)) {
- dev_err(&pf->pdev->dev, "invalid VF MAC addr %pM\n",
- al->list[i].addr);
+ dev_err(&pf->pdev->dev, "Invalid MAC addr %pM for VF %d\n",
+ al->list[i].addr, vf->vf_id);
ret = I40E_ERR_INVALID_MAC_ADDR;
goto error_param;
}
@@ -1680,13 +1686,19 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
spin_lock_bh(&vsi->mac_filter_list_lock);
/* delete addresses from the list */
for (i = 0; i < al->num_elements; i++)
- i40e_del_filter(vsi, al->list[i].addr,
- I40E_VLAN_ANY, true, false);
+ if (i40e_del_mac_all_vlan(vsi, al->list[i].addr, true, false)) {
+ ret = I40E_ERR_INVALID_MAC_ADDR;
+ spin_unlock_bh(&vsi->mac_filter_list_lock);
+ goto error_param;
+ }
+
spin_unlock_bh(&vsi->mac_filter_list_lock);
/* program the updated filter list */
- if (i40e_sync_vsi_filters(vsi, false))
- dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n");
+ ret = i40e_sync_vsi_filters(vsi);
+ if (ret)
+ dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n",
+ vf->vf_id, ret);
error_param:
/* send the response to the VF */
@@ -1740,8 +1752,8 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
if (ret)
dev_err(&pf->pdev->dev,
- "Unable to add VF vlan filter %d, error %d\n",
- vfl->vlan_id[i], ret);
+ "Unable to add VLAN filter %d for VF %d, error %d\n",
+ vfl->vlan_id[i], vf->vf_id, ret);
}
error_param:
@@ -1792,8 +1804,8 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
if (ret)
dev_err(&pf->pdev->dev,
- "Unable to delete VF vlan filter %d, error %d\n",
- vfl->vlan_id[i], ret);
+ "Unable to delete VLAN filter %d for VF %d, error %d\n",
+ vfl->vlan_id[i], vf->vf_id, ret);
}
error_param:
@@ -2066,15 +2078,15 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
vf = &(pf->vf[vf_id]);
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev,
- "Uninitialized VF %d\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error_param;
}
- if (!is_valid_ether_addr(mac)) {
+ if (is_multicast_ether_addr(mac)) {
dev_err(&pf->pdev->dev,
- "Invalid VF ethernet address\n");
+ "Invalid Ethernet address %pM for VF %d\n", mac, vf_id);
ret = -EINVAL;
goto error_param;
}
@@ -2085,9 +2097,10 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
spin_lock_bh(&vsi->mac_filter_list_lock);
/* delete the temporary mac address */
- i40e_del_filter(vsi, vf->default_lan_addr.addr,
- vf->port_vlan_id ? vf->port_vlan_id : -1,
- true, false);
+ if (!is_zero_ether_addr(vf->default_lan_addr.addr))
+ i40e_del_filter(vsi, vf->default_lan_addr.addr,
+ vf->port_vlan_id ? vf->port_vlan_id : -1,
+ true, false);
/* Delete all the filters for this VSI - we're going to kill it
* anyway.
@@ -2099,7 +2112,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id);
/* program mac filter */
- if (i40e_sync_vsi_filters(vsi, false)) {
+ if (i40e_sync_vsi_filters(vsi)) {
dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
ret = -EIO;
goto error_param;
@@ -2150,8 +2163,9 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
vf = &(pf->vf[vf_id]);
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error_pvid;
}
@@ -2270,8 +2284,9 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
vf = &(pf->vf[vf_id]);
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev, "Uninitialized VF %d.\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error;
}
@@ -2344,8 +2359,9 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
/* first vsi is always the LAN vsi */
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error_param;
}
@@ -2460,6 +2476,12 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
}
vf = &(pf->vf[vf_id]);
+ if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
+ goto out;
+ }
if (enable == vf->spoofchk)
goto out;
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
index fcb9ef34cc7a..f5b2b369dc7c 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
@@ -227,6 +227,7 @@ enum i40e_admin_queue_opc {
i40e_aqc_opc_nvm_update = 0x0703,
i40e_aqc_opc_nvm_config_read = 0x0704,
i40e_aqc_opc_nvm_config_write = 0x0705,
+ i40e_aqc_opc_oem_post_update = 0x0720,
/* virtualization commands */
i40e_aqc_opc_send_msg_to_pf = 0x0801,
@@ -1888,6 +1889,26 @@ struct i40e_aqc_nvm_config_data_immediate_field {
I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field);
+/* OEM Post Update (indirect 0x0720)
+ * no command data struct used
+ */
+ struct i40e_aqc_nvm_oem_post_update {
+#define I40E_AQ_NVM_OEM_POST_UPDATE_EXTERNAL_DATA 0x01
+ u8 sel_data;
+ u8 reserved[7];
+};
+
+I40E_CHECK_STRUCT_LEN(0x8, i40e_aqc_nvm_oem_post_update);
+
+struct i40e_aqc_nvm_oem_post_update_buffer {
+ u8 str_len;
+ u8 dev_addr;
+ __le16 eeprom_addr;
+ u8 data[36];
+};
+
+I40E_CHECK_STRUCT_LEN(0x28, i40e_aqc_nvm_oem_post_update_buffer);
+
/* Send to PF command (indirect 0x0801) id is only used by PF
* Send to VF command (indirect 0x0802) id is only used by PF
* Send to Peer PF command (indirect 0x0803)
@@ -2311,4 +2332,4 @@ struct i40e_aqc_debug_modify_internals {
I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals);
-#endif
+#endif /* _I40E_ADMINQ_CMD_H_ */
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c
index 72b1942a94aa..938783e0baac 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c
@@ -44,7 +44,6 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw)
switch (hw->device_id) {
case I40E_DEV_ID_SFP_XL710:
case I40E_DEV_ID_QEMU:
- case I40E_DEV_ID_KX_A:
case I40E_DEV_ID_KX_B:
case I40E_DEV_ID_KX_C:
case I40E_DEV_ID_QSFP_A:
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h
index e6a39c9862e8..ca8b58c3d1f5 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_devids.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_devids.h
@@ -30,7 +30,6 @@
/* Device IDs */
#define I40E_DEV_ID_SFP_XL710 0x1572
#define I40E_DEV_ID_QEMU 0x1574
-#define I40E_DEV_ID_KX_A 0x157F
#define I40E_DEV_ID_KX_B 0x1580
#define I40E_DEV_ID_KX_C 0x1581
#define I40E_DEV_ID_QSFP_A 0x1583
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
index 47e9a90d6b10..7a00657dacda 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
@@ -51,11 +51,7 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
struct i40e_tx_buffer *tx_buffer)
{
if (tx_buffer->skb) {
- if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
- kfree(tx_buffer->raw_buf);
- else
- dev_kfree_skb_any(tx_buffer->skb);
-
+ dev_kfree_skb_any(tx_buffer->skb);
if (dma_unmap_len(tx_buffer, len))
dma_unmap_single(ring->dev,
dma_unmap_addr(tx_buffer, dma),
@@ -67,6 +63,10 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
dma_unmap_len(tx_buffer, len),
DMA_TO_DEVICE);
}
+
+ if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
+ kfree(tx_buffer->raw_buf);
+
tx_buffer->next_to_watch = NULL;
tx_buffer->skb = NULL;
dma_unmap_len_set(tx_buffer, len, 0);
@@ -127,17 +127,24 @@ void i40evf_free_tx_resources(struct i40e_ring *tx_ring)
}
/**
- * i40e_get_head - Retrieve head from head writeback
- * @tx_ring: tx ring to fetch head of
+ * i40evf_get_tx_pending - how many Tx descriptors not processed
+ * @tx_ring: the ring of descriptors
*
- * Returns value of Tx ring head based on value stored
- * in head write-back location
+ * Since there is no access to the ring head register
+ * in XL710, we need to use our local copies
**/
-static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+u32 i40evf_get_tx_pending(struct i40e_ring *ring)
{
- void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+ u32 head, tail;
+
+ head = i40e_get_head(ring);
+ tail = readl(ring->tail);
- return le32_to_cpu(*(volatile __le32 *)head);
+ if (head != tail)
+ return (head < tail) ?
+ tail - head : (tail + ring->count - head);
+
+ return 0;
}
#define WB_STRIDE 0x3
@@ -245,16 +252,6 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
tx_ring->q_vector->tx.total_bytes += total_bytes;
tx_ring->q_vector->tx.total_packets += total_packets;
- /* check to see if there are any non-cache aligned descriptors
- * waiting to be written back, and kick the hardware to force
- * them to be written back in case of napi polling
- */
- if (budget &&
- !((i & WB_STRIDE) == WB_STRIDE) &&
- !test_bit(__I40E_DOWN, &tx_ring->vsi->state) &&
- (I40E_DESC_UNUSED(tx_ring) != tx_ring->count))
- tx_ring->arm_wb = true;
-
netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev,
tx_ring->queue_index),
total_packets, total_bytes);
@@ -414,7 +411,7 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
return false;
}
-/*
+/**
* i40evf_setup_tx_descriptors - Allocate the Tx descriptors
* @tx_ring: the tx ring to set up
*
@@ -889,31 +886,12 @@ checksum_fail:
}
/**
- * i40e_rx_hash - returns the hash value from the Rx descriptor
- * @ring: descriptor ring
- * @rx_desc: specific descriptor
- **/
-static inline u32 i40e_rx_hash(struct i40e_ring *ring,
- union i40e_rx_desc *rx_desc)
-{
- const __le64 rss_mask =
- cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
- I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
-
- if ((ring->netdev->features & NETIF_F_RXHASH) &&
- (rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask)
- return le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
- else
- return 0;
-}
-
-/**
- * i40e_ptype_to_hash - get a hash type
+ * i40e_ptype_to_htype - get a hash type
* @ptype: the ptype value from the descriptor
*
* Returns a hash type to be used by skb_set_hash
**/
-static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
+static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype)
{
struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype);
@@ -931,6 +909,30 @@ static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
}
/**
+ * i40e_rx_hash - set the hash value in the skb
+ * @ring: descriptor ring
+ * @rx_desc: specific descriptor
+ **/
+static inline void i40e_rx_hash(struct i40e_ring *ring,
+ union i40e_rx_desc *rx_desc,
+ struct sk_buff *skb,
+ u8 rx_ptype)
+{
+ u32 hash;
+ const __le64 rss_mask =
+ cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
+ I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
+
+ if (ring->netdev->features & NETIF_F_RXHASH)
+ return;
+
+ if ((rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) {
+ hash = le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
+ skb_set_hash(skb, hash, i40e_ptype_to_htype(rx_ptype));
+ }
+}
+
+/**
* i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split
* @rx_ring: rx ring to clean
* @budget: how many cleans we're allowed
@@ -1071,8 +1073,8 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
+
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
total_rx_packets++;
@@ -1090,7 +1092,6 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
#endif
- skb_mark_napi_id(skb, &rx_ring->q_vector->napi);
i40e_receive_skb(rx_ring, skb, vlan_tag);
rx_desc->wb.qword1.status_error_len = 0;
@@ -1189,8 +1190,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
total_rx_packets++;
@@ -1263,10 +1263,12 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi,
rx = i40e_set_new_dynamic_itr(&q_vector->rx);
rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr);
}
+
if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) {
tx = i40e_set_new_dynamic_itr(&q_vector->tx);
txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr);
}
+
if (rx || tx) {
/* get the higher of the two ITR adjustments and
* use the same value for both ITR registers
@@ -1302,7 +1304,6 @@ enable_int:
q_vector->itr_countdown--;
else
q_vector->itr_countdown = ITR_COUNTDOWN_START;
-
}
/**
@@ -1335,7 +1336,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
*/
i40e_for_each_ring(ring, q_vector->tx) {
clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit);
- arm_wb |= ring->arm_wb;
+ arm_wb = arm_wb || ring->arm_wb;
ring->arm_wb = false;
}
@@ -1364,8 +1365,10 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
/* If work not completed, return budget and polling will return */
if (!clean_complete) {
tx_only:
- if (arm_wb)
+ if (arm_wb) {
+ q_vector->tx.ring[0].tx_stats.tx_force_wb++;
i40evf_force_wb(vsi, q_vector);
+ }
return budget;
}
@@ -1437,13 +1440,12 @@ out:
* @tx_ring: ptr to the ring to send
* @skb: ptr to the skb we're sending
* @hdr_len: ptr to the size of the packet header
- * @cd_tunneling: ptr to context descriptor bits
+ * @cd_type_cmd_tso_mss: Quad Word 1
*
* Returns 0 if no TSO can happen, 1 if tso is going, or error
**/
static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
- u8 *hdr_len, u64 *cd_type_cmd_tso_mss,
- u32 *cd_tunneling)
+ u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
{
u32 cd_cmd, cd_tso_len, cd_mss;
struct ipv6hdr *ipv6h;
@@ -1555,7 +1557,6 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
*tx_flags |= I40E_TX_FLAGS_IPV6;
}
-
if ((tx_ring->flags & I40E_TXR_FLAGS_OUTER_UDP_CSUM) &&
(l4_tunnel == I40E_TXD_CTX_UDP_TUNNELING) &&
(*cd_tunneling & I40E_TXD_CTX_QW0_EXT_IP_MASK)) {
@@ -1654,7 +1655,7 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
context_desc->type_cmd_tso_mss = cpu_to_le64(cd_type_cmd_tso_mss);
}
- /**
+/**
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
* @skb: send buffer
* @tx_flags: collected send information
@@ -1770,6 +1771,9 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
u32 td_tag = 0;
dma_addr_t dma;
u16 gso_segs;
+ u16 desc_count = 0;
+ bool tail_bump = true;
+ bool do_rs = false;
if (tx_flags & I40E_TX_FLAGS_HW_VLAN) {
td_cmd |= I40E_TX_DESC_CMD_IL2TAG1;
@@ -1810,6 +1814,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_desc++;
i++;
+ desc_count++;
+
if (i == tx_ring->count) {
tx_desc = I40E_TX_DESC(tx_ring, 0);
i = 0;
@@ -1829,6 +1835,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_desc++;
i++;
+ desc_count++;
+
if (i == tx_ring->count) {
tx_desc = I40E_TX_DESC(tx_ring, 0);
i = 0;
@@ -1843,35 +1851,6 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_bi = &tx_ring->tx_bi[i];
}
- /* Place RS bit on last descriptor of any packet that spans across the
- * 4th descriptor (WB_STRIDE aka 0x3) in a 64B cacheline.
- */
-#define WB_STRIDE 0x3
- if (((i & WB_STRIDE) != WB_STRIDE) &&
- (first <= &tx_ring->tx_bi[i]) &&
- (first >= &tx_ring->tx_bi[i & ~WB_STRIDE])) {
- tx_desc->cmd_type_offset_bsz =
- build_ctob(td_cmd, td_offset, size, td_tag) |
- cpu_to_le64((u64)I40E_TX_DESC_CMD_EOP <<
- I40E_TXD_QW1_CMD_SHIFT);
- } else {
- tx_desc->cmd_type_offset_bsz =
- build_ctob(td_cmd, td_offset, size, td_tag) |
- cpu_to_le64((u64)I40E_TXD_CMD <<
- I40E_TXD_QW1_CMD_SHIFT);
- }
-
- netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev,
- tx_ring->queue_index),
- first->bytecount);
-
- /* Force memory writes to complete before letting h/w
- * know there are new descriptors to fetch. (Only
- * applicable for weak-ordered memory model archs,
- * such as IA-64).
- */
- wmb();
-
/* set next_to_watch value indicating a packet is present */
first->next_to_watch = tx_desc;
@@ -1881,15 +1860,72 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_ring->next_to_use = i;
+ netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev,
+ tx_ring->queue_index),
+ first->bytecount);
i40evf_maybe_stop_tx(tx_ring, DESC_NEEDED);
+
+ /* Algorithm to optimize tail and RS bit setting:
+ * if xmit_more is supported
+ * if xmit_more is true
+ * do not update tail and do not mark RS bit.
+ * if xmit_more is false and last xmit_more was false
+ * if every packet spanned less than 4 desc
+ * then set RS bit on 4th packet and update tail
+ * on every packet
+ * else
+ * update tail and set RS bit on every packet.
+ * if xmit_more is false and last_xmit_more was true
+ * update tail and set RS bit.
+ *
+ * Optimization: wmb to be issued only in case of tail update.
+ * Also optimize the Descriptor WB path for RS bit with the same
+ * algorithm.
+ *
+ * Note: If there are less than 4 packets
+ * pending and interrupts were disabled the service task will
+ * trigger a force WB.
+ */
+ if (skb->xmit_more &&
+ !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
+ tx_ring->queue_index))) {
+ tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET;
+ tail_bump = false;
+ } else if (!skb->xmit_more &&
+ !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
+ tx_ring->queue_index)) &&
+ (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) &&
+ (tx_ring->packet_stride < WB_STRIDE) &&
+ (desc_count < WB_STRIDE)) {
+ tx_ring->packet_stride++;
+ } else {
+ tx_ring->packet_stride = 0;
+ tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET;
+ do_rs = true;
+ }
+ if (do_rs)
+ tx_ring->packet_stride = 0;
+
+ tx_desc->cmd_type_offset_bsz =
+ build_ctob(td_cmd, td_offset, size, td_tag) |
+ cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD :
+ I40E_TX_DESC_CMD_EOP) <<
+ I40E_TXD_QW1_CMD_SHIFT);
+
/* notify HW of packet */
- if (!skb->xmit_more ||
- netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
- tx_ring->queue_index)))
- writel(i, tx_ring->tail);
- else
+ if (!tail_bump)
prefetchw(tx_desc + 1);
+ if (tail_bump) {
+ /* Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64).
+ */
+ wmb();
+ writel(i, tx_ring->tail);
+ }
+
return;
dma_error:
@@ -1961,6 +1997,9 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
u8 hdr_len = 0;
int tso;
+ /* prefetch the data, we'll need it later */
+ prefetch(skb->data);
+
if (0 == i40evf_xmit_descriptor_count(skb, tx_ring))
return NETDEV_TX_BUSY;
@@ -1980,8 +2019,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
else if (protocol == htons(ETH_P_IPV6))
tx_flags |= I40E_TX_FLAGS_IPV6;
- tso = i40e_tso(tx_ring, skb, &hdr_len,
- &cd_type_cmd_tso_mss, &cd_tunneling);
+ tso = i40e_tso(tx_ring, skb, &hdr_len, &cd_type_cmd_tso_mss);
if (tso < 0)
goto out_drop;
@@ -2029,7 +2067,7 @@ out_drop:
netdev_tx_t i40evf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- struct i40e_ring *tx_ring = adapter->tx_rings[skb->queue_mapping];
+ struct i40e_ring *tx_ring = &adapter->tx_rings[skb->queue_mapping];
/* hardware can't handle really short frames, hardware padding works
* beyond this point
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
index ebc1bf77f036..e29bb3e86cfd 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
@@ -201,6 +201,7 @@ struct i40e_tx_queue_stats {
u64 tx_busy;
u64 tx_done_old;
u64 tx_linearize;
+ u64 tx_force_wb;
};
struct i40e_rx_queue_stats {
@@ -267,6 +268,8 @@ struct i40e_ring {
bool ring_active; /* is ring online or not */
bool arm_wb; /* do something to arm write back */
+ u8 packet_stride;
+#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2)
u16 flags;
#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0)
@@ -321,4 +324,19 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring);
void i40evf_free_tx_resources(struct i40e_ring *tx_ring);
void i40evf_free_rx_resources(struct i40e_ring *rx_ring);
int i40evf_napi_poll(struct napi_struct *napi, int budget);
+u32 i40evf_get_tx_pending(struct i40e_ring *ring);
+
+/**
+ * i40e_get_head - Retrieve head from head writeback
+ * @tx_ring: Tx ring to fetch head of
+ *
+ * Returns value of Tx ring head based on value stored
+ * in head write-back location
+ **/
+static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+{
+ void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+
+ return le32_to_cpu(*(volatile __le32 *)head);
+}
#endif /* _I40E_TXRX_H_ */
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
index 9f7b279b9d9c..3b9d2037456c 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
@@ -153,6 +153,7 @@ struct i40e_virtchnl_vsi_resource {
#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020
#define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000
#define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000
+#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000
struct i40e_virtchnl_vf_resource {
u16 num_vsis;
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index 22fc3d49c4b9..be1b72b93888 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -67,6 +67,8 @@ struct i40e_vsi {
u16 rx_itr_setting;
u16 tx_itr_setting;
u16 qs_handle;
+ u8 *rss_hkey_user; /* User configured hash keys */
+ u8 *rss_lut_user; /* User configured lookup table entries */
};
/* How many Rx Buffers do we bundle into one write to the hardware ? */
@@ -95,10 +97,10 @@ struct i40e_vsi {
#define I40E_TX_DESC(R, i) (&(((struct i40e_tx_desc *)((R)->desc))[i]))
#define I40E_TX_CTXTDESC(R, i) \
(&(((struct i40e_tx_context_desc *)((R)->desc))[i]))
-#define MAX_RX_QUEUES 8
-#define MAX_TX_QUEUES MAX_RX_QUEUES
+#define MAX_QUEUES 16
#define I40EVF_HKEY_ARRAY_SIZE ((I40E_VFQF_HKEY_MAX_INDEX + 1) * 4)
+#define I40EVF_HLUT_ARRAY_SIZE ((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4)
/* MAX_MSIX_Q_VECTORS of these are allocated,
* but we only use one per queue-specific vector.
@@ -142,9 +144,6 @@ struct i40e_q_vector {
#define OTHER_VECTOR 1
#define NONQ_VECS (OTHER_VECTOR)
-#define MAX_MSIX_Q_VECTORS 4
-#define MAX_MSIX_COUNT 5
-
#define MIN_MSIX_Q_VECTORS 1
#define MIN_MSIX_COUNT (MIN_MSIX_Q_VECTORS + NONQ_VECS)
@@ -190,19 +189,19 @@ struct i40evf_adapter {
struct work_struct reset_task;
struct work_struct adminq_task;
struct delayed_work init_task;
- struct i40e_q_vector *q_vector[MAX_MSIX_Q_VECTORS];
+ struct i40e_q_vector *q_vectors;
struct list_head vlan_filter_list;
char misc_vector_name[IFNAMSIZ + 9];
int num_active_queues;
/* TX */
- struct i40e_ring *tx_rings[I40E_MAX_VSI_QP];
+ struct i40e_ring *tx_rings;
u32 tx_timeout_count;
struct list_head mac_filter_list;
u32 tx_desc_count;
/* RX */
- struct i40e_ring *rx_rings[I40E_MAX_VSI_QP];
+ struct i40e_ring *rx_rings;
u64 hw_csum_rx_error;
u32 rx_desc_count;
int num_msix_vectors;
@@ -313,4 +312,8 @@ void i40evf_request_reset(struct i40evf_adapter *adapter);
void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
enum i40e_virtchnl_ops v_opcode,
i40e_status v_retval, u8 *msg, u16 msglen);
+int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut,
+ u16 lut_size);
+int i40evf_get_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut,
+ u16 lut_size);
#endif /* _I40EVF_H_ */
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
index 4790437a50ac..a4c9feb589e7 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
@@ -121,12 +121,12 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev,
data[i] = *(u64 *)p;
}
for (j = 0; j < adapter->num_active_queues; j++) {
- data[i++] = adapter->tx_rings[j]->stats.packets;
- data[i++] = adapter->tx_rings[j]->stats.bytes;
+ data[i++] = adapter->tx_rings[j].stats.packets;
+ data[i++] = adapter->tx_rings[j].stats.bytes;
}
for (j = 0; j < adapter->num_active_queues; j++) {
- data[i++] = adapter->rx_rings[j]->stats.packets;
- data[i++] = adapter->rx_rings[j]->stats.bytes;
+ data[i++] = adapter->rx_rings[j].stats.packets;
+ data[i++] = adapter->rx_rings[j].stats.bytes;
}
}
@@ -351,7 +351,7 @@ static int i40evf_set_coalesce(struct net_device *netdev,
vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) {
- q_vector = adapter->q_vector[i];
+ q_vector = &adapter->q_vectors[i];
q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr);
q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
@@ -634,25 +634,34 @@ static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
u8 *hfunc)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- struct i40e_hw *hw = &adapter->hw;
- u32 hlut_val;
- int i, j;
+ struct i40e_vsi *vsi = &adapter->vsi;
+ u8 *seed = NULL, *lut;
+ int ret;
+ u16 i;
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
if (!indir)
return 0;
- if (indir) {
- for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
- hlut_val = rd32(hw, I40E_VFQF_HLUT(i));
- indir[j++] = hlut_val & 0xff;
- indir[j++] = (hlut_val >> 8) & 0xff;
- indir[j++] = (hlut_val >> 16) & 0xff;
- indir[j++] = (hlut_val >> 24) & 0xff;
- }
- }
- return 0;
+ seed = key;
+
+ lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ ret = i40evf_get_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE);
+ if (ret)
+ goto out;
+
+ /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+ for (i = 0; i < I40EVF_HLUT_ARRAY_SIZE; i++)
+ indir[i] = (u32)lut[i];
+
+out:
+ kfree(lut);
+
+ return ret;
}
/**
@@ -668,9 +677,9 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir,
const u8 *key, const u8 hfunc)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- struct i40e_hw *hw = &adapter->hw;
- u32 hlut_val;
- int i, j;
+ struct i40e_vsi *vsi = &adapter->vsi;
+ u8 *seed = NULL;
+ u16 i;
/* We do not allow change in unsupported parameters */
if (key ||
@@ -679,15 +688,29 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir,
if (!indir)
return 0;
- for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
- hlut_val = indir[j++];
- hlut_val |= indir[j++] << 8;
- hlut_val |= indir[j++] << 16;
- hlut_val |= indir[j++] << 24;
- wr32(hw, I40E_VFQF_HLUT(i), hlut_val);
+ if (key) {
+ if (!vsi->rss_hkey_user) {
+ vsi->rss_hkey_user = kzalloc(I40EVF_HKEY_ARRAY_SIZE,
+ GFP_KERNEL);
+ if (!vsi->rss_hkey_user)
+ return -ENOMEM;
+ }
+ memcpy(vsi->rss_hkey_user, key, I40EVF_HKEY_ARRAY_SIZE);
+ seed = vsi->rss_hkey_user;
+ }
+ if (!vsi->rss_lut_user) {
+ vsi->rss_lut_user = kzalloc(I40EVF_HLUT_ARRAY_SIZE,
+ GFP_KERNEL);
+ if (!vsi->rss_lut_user)
+ return -ENOMEM;
}
- return 0;
+ /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+ for (i = 0; i < I40EVF_HLUT_ARRAY_SIZE; i++)
+ vsi->rss_lut_user[i] = (u8)(indir[i]);
+
+ return i40evf_config_rss(vsi, seed, vsi->rss_lut_user,
+ I40EVF_HLUT_ARRAY_SIZE);
}
static const struct ethtool_ops i40evf_ethtool_ops = {
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index 99d2cffae0cd..a0bbb6b9f5aa 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -34,7 +34,15 @@ char i40evf_driver_name[] = "i40evf";
static const char i40evf_driver_string[] =
"Intel(R) XL710/X710 Virtual Function Network Driver";
-#define DRV_VERSION "1.3.33"
+#define DRV_KERN "-k"
+
+#define DRV_VERSION_MAJOR 1
+#define DRV_VERSION_MINOR 4
+#define DRV_VERSION_BUILD 4
+#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
+ __stringify(DRV_VERSION_MINOR) "." \
+ __stringify(DRV_VERSION_BUILD) \
+ DRV_KERN
const char i40evf_driver_version[] = DRV_VERSION;
static const char i40evf_copyright[] =
"Copyright (c) 2013 - 2015 Intel Corporation.";
@@ -259,7 +267,7 @@ static void i40evf_fire_sw_int(struct i40evf_adapter *adapter, u32 mask)
{
struct i40e_hw *hw = &adapter->hw;
int i;
- uint32_t dyn_ctl;
+ u32 dyn_ctl;
if (mask & 1) {
dyn_ctl = rd32(hw, I40E_VFINT_DYN_CTL01);
@@ -307,10 +315,9 @@ static irqreturn_t i40evf_msix_aq(int irq, void *data)
struct i40e_hw *hw = &adapter->hw;
u32 val;
- /* handle non-queue interrupts */
- rd32(hw, I40E_VFINT_ICR01);
- rd32(hw, I40E_VFINT_ICR0_ENA1);
-
+ /* handle non-queue interrupts, these reads clear the registers */
+ val = rd32(hw, I40E_VFINT_ICR01);
+ val = rd32(hw, I40E_VFINT_ICR0_ENA1);
val = rd32(hw, I40E_VFINT_DYN_CTL01) |
I40E_VFINT_DYN_CTL01_CLEARPBA_MASK;
@@ -348,8 +355,8 @@ static irqreturn_t i40evf_msix_clean_rings(int irq, void *data)
static void
i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
{
- struct i40e_q_vector *q_vector = adapter->q_vector[v_idx];
- struct i40e_ring *rx_ring = adapter->rx_rings[r_idx];
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx];
+ struct i40e_ring *rx_ring = &adapter->rx_rings[r_idx];
rx_ring->q_vector = q_vector;
rx_ring->next = q_vector->rx.ring;
@@ -369,8 +376,8 @@ i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
static void
i40evf_map_vector_to_txq(struct i40evf_adapter *adapter, int v_idx, int t_idx)
{
- struct i40e_q_vector *q_vector = adapter->q_vector[v_idx];
- struct i40e_ring *tx_ring = adapter->tx_rings[t_idx];
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx];
+ struct i40e_ring *tx_ring = &adapter->tx_rings[t_idx];
tx_ring->q_vector = q_vector;
tx_ring->next = q_vector->tx.ring;
@@ -465,7 +472,7 @@ static void i40evf_netpoll(struct net_device *netdev)
return;
for (i = 0; i < q_vectors; i++)
- i40evf_msix_clean_rings(0, adapter->q_vector[i]);
+ i40evf_msix_clean_rings(0, &adapter->q_vectors[i]);
}
#endif
@@ -487,7 +494,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename)
q_vectors = adapter->num_msix_vectors - NONQ_VECS;
for (vector = 0; vector < q_vectors; vector++) {
- struct i40e_q_vector *q_vector = adapter->q_vector[vector];
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[vector];
if (q_vector->tx.ring && q_vector->rx.ring) {
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
@@ -532,7 +539,7 @@ free_queue_irqs:
adapter->msix_entries[vector + NONQ_VECS].vector,
NULL);
free_irq(adapter->msix_entries[vector + NONQ_VECS].vector,
- adapter->q_vector[vector]);
+ &adapter->q_vectors[vector]);
}
return err;
}
@@ -582,7 +589,7 @@ static void i40evf_free_traffic_irqs(struct i40evf_adapter *adapter)
irq_set_affinity_hint(adapter->msix_entries[i+1].vector,
NULL);
free_irq(adapter->msix_entries[i+1].vector,
- adapter->q_vector[i]);
+ &adapter->q_vectors[i]);
}
}
@@ -611,7 +618,7 @@ static void i40evf_configure_tx(struct i40evf_adapter *adapter)
int i;
for (i = 0; i < adapter->num_active_queues; i++)
- adapter->tx_rings[i]->tail = hw->hw_addr + I40E_QTX_TAIL1(i);
+ adapter->tx_rings[i].tail = hw->hw_addr + I40E_QTX_TAIL1(i);
}
/**
@@ -656,8 +663,8 @@ static void i40evf_configure_rx(struct i40evf_adapter *adapter)
}
for (i = 0; i < adapter->num_active_queues; i++) {
- adapter->rx_rings[i]->tail = hw->hw_addr + I40E_QRX_TAIL1(i);
- adapter->rx_rings[i]->rx_buf_len = rx_buf_len;
+ adapter->rx_rings[i].tail = hw->hw_addr + I40E_QRX_TAIL1(i);
+ adapter->rx_rings[i].rx_buf_len = rx_buf_len;
}
}
@@ -954,7 +961,7 @@ static void i40evf_napi_enable_all(struct i40evf_adapter *adapter)
for (q_idx = 0; q_idx < q_vectors; q_idx++) {
struct napi_struct *napi;
- q_vector = adapter->q_vector[q_idx];
+ q_vector = &adapter->q_vectors[q_idx];
napi = &q_vector->napi;
napi_enable(napi);
}
@@ -971,7 +978,7 @@ static void i40evf_napi_disable_all(struct i40evf_adapter *adapter)
int q_vectors = adapter->num_msix_vectors - NONQ_VECS;
for (q_idx = 0; q_idx < q_vectors; q_idx++) {
- q_vector = adapter->q_vector[q_idx];
+ q_vector = &adapter->q_vectors[q_idx];
napi_disable(&q_vector->napi);
}
}
@@ -992,7 +999,7 @@ static void i40evf_configure(struct i40evf_adapter *adapter)
adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_QUEUES;
for (i = 0; i < adapter->num_active_queues; i++) {
- struct i40e_ring *ring = adapter->rx_rings[i];
+ struct i40e_ring *ring = &adapter->rx_rings[i];
i40evf_alloc_rx_buffers_1buf(ring, ring->count);
ring->next_to_use = ring->count - 1;
@@ -1112,16 +1119,10 @@ i40evf_acquire_msix_vectors(struct i40evf_adapter *adapter, int vectors)
**/
static void i40evf_free_queues(struct i40evf_adapter *adapter)
{
- int i;
-
if (!adapter->vsi_res)
return;
- for (i = 0; i < adapter->num_active_queues; i++) {
- if (adapter->tx_rings[i])
- kfree_rcu(adapter->tx_rings[i], rcu);
- adapter->tx_rings[i] = NULL;
- adapter->rx_rings[i] = NULL;
- }
+ kfree(adapter->tx_rings);
+ kfree(adapter->rx_rings);
}
/**
@@ -1136,13 +1137,20 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
{
int i;
+ adapter->tx_rings = kcalloc(adapter->num_active_queues,
+ sizeof(struct i40e_ring), GFP_KERNEL);
+ if (!adapter->tx_rings)
+ goto err_out;
+ adapter->rx_rings = kcalloc(adapter->num_active_queues,
+ sizeof(struct i40e_ring), GFP_KERNEL);
+ if (!adapter->rx_rings)
+ goto err_out;
+
for (i = 0; i < adapter->num_active_queues; i++) {
struct i40e_ring *tx_ring;
struct i40e_ring *rx_ring;
- tx_ring = kzalloc(sizeof(*tx_ring) * 2, GFP_KERNEL);
- if (!tx_ring)
- goto err_out;
+ tx_ring = &adapter->tx_rings[i];
tx_ring->queue_index = i;
tx_ring->netdev = adapter->netdev;
@@ -1150,14 +1158,12 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
tx_ring->count = adapter->tx_desc_count;
if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE)
tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR;
- adapter->tx_rings[i] = tx_ring;
- rx_ring = &tx_ring[1];
+ rx_ring = &adapter->rx_rings[i];
rx_ring->queue_index = i;
rx_ring->netdev = adapter->netdev;
rx_ring->dev = &adapter->pdev->dev;
rx_ring->count = adapter->rx_desc_count;
- adapter->rx_rings[i] = rx_ring;
}
return 0;
@@ -1207,115 +1213,273 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter)
err = i40evf_acquire_msix_vectors(adapter, v_budget);
out:
- adapter->netdev->real_num_tx_queues = pairs;
+ netif_set_real_num_rx_queues(adapter->netdev, pairs);
+ netif_set_real_num_tx_queues(adapter->netdev, pairs);
return err;
}
/**
- * i40e_configure_rss_aq - Prepare for RSS using AQ commands
+ * i40e_config_rss_aq - Prepare for RSS using AQ commands
* @vsi: vsi structure
* @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Return 0 on success, negative on failure
**/
-static void i40evf_configure_rss_aq(struct i40e_vsi *vsi, const u8 *seed)
+static int i40evf_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
{
- struct i40e_aqc_get_set_rss_key_data rss_key;
struct i40evf_adapter *adapter = vsi->back;
struct i40e_hw *hw = &adapter->hw;
- int ret = 0, i;
- u8 *rss_lut;
+ int ret = 0;
if (!vsi->id)
- return;
+ return -EINVAL;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
dev_err(&adapter->pdev->dev, "Cannot confiure RSS, command %d pending\n",
adapter->current_op);
- return;
+ return -EBUSY;
}
- memset(&rss_key, 0, sizeof(rss_key));
- memcpy(&rss_key, seed, sizeof(rss_key));
+ if (seed) {
+ struct i40e_aqc_get_set_rss_key_data *rss_key =
+ (struct i40e_aqc_get_set_rss_key_data *)seed;
+ ret = i40evf_aq_set_rss_key(hw, vsi->id, rss_key);
+ if (ret) {
+ dev_err(&adapter->pdev->dev, "Cannot set RSS key, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
+ }
+ }
- rss_lut = kzalloc(((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4), GFP_KERNEL);
- if (!rss_lut)
- return;
+ if (lut) {
+ ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, lut, lut_size);
+ if (ret) {
+ dev_err(&adapter->pdev->dev,
+ "Cannot set RSS lut, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
+ }
+ }
- /* Populate the LUT with max no. PF queues in round robin fashion */
- for (i = 0; i <= (I40E_VFQF_HLUT_MAX_INDEX * 4); i++)
- rss_lut[i] = i % adapter->num_active_queues;
+ return ret;
+}
- ret = i40evf_aq_set_rss_key(hw, vsi->id, &rss_key);
- if (ret) {
- dev_err(&adapter->pdev->dev,
- "Cannot set RSS key, err %s aq_err %s\n",
- i40evf_stat_str(hw, ret),
- i40evf_aq_str(hw, hw->aq.asq_last_status));
- return;
+/**
+ * i40evf_config_rss_reg - Configure RSS keys and lut by writing registers
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int i40evf_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+ const u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+ struct i40e_hw *hw = &adapter->hw;
+ u16 i;
+
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
+
+ for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
+ wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]);
}
- ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, rss_lut,
- (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4);
- if (ret)
- dev_err(&adapter->pdev->dev,
- "Cannot set RSS lut, err %s aq_err %s\n",
- i40evf_stat_str(hw, ret),
- i40evf_aq_str(hw, hw->aq.asq_last_status));
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40EVF_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+
+ for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++)
+ wr32(hw, I40E_VFQF_HLUT(i), lut_dw[i]);
+ }
+ i40e_flush(hw);
+
+ return 0;
}
/**
- * i40e_configure_rss_reg - Prepare for RSS if used
- * @adapter: board private structure
- * @seed: RSS hash seed
+ * * i40evf_get_rss_aq - Get RSS keys and lut by using AQ commands
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Return 0 on success, negative on failure
**/
-static void i40evf_configure_rss_reg(struct i40evf_adapter *adapter,
- const u8 *seed)
+static int i40evf_get_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
{
+ struct i40evf_adapter *adapter = vsi->back;
struct i40e_hw *hw = &adapter->hw;
- u32 *seed_dw = (u32 *)seed;
- u32 cqueue = 0;
- u32 lut = 0;
- int i, j;
+ int ret = 0;
- /* Fill out hash function seed */
- for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
- wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]);
-
- /* Populate the LUT with max no. PF queues in round robin fashion */
- for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
- lut = 0;
- for (j = 0; j < 4; j++) {
- if (cqueue == adapter->num_active_queues)
- cqueue = 0;
- lut |= ((cqueue) << (8 * j));
- cqueue++;
+ if (seed) {
+ ret = i40evf_aq_get_rss_key(hw, vsi->id,
+ (struct i40e_aqc_get_set_rss_key_data *)seed);
+ if (ret) {
+ dev_err(&adapter->pdev->dev,
+ "Cannot get RSS key, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
}
- wr32(hw, I40E_VFQF_HLUT(i), lut);
}
- i40e_flush(hw);
+
+ if (lut) {
+ ret = i40evf_aq_get_rss_lut(hw, vsi->id, seed, lut, lut_size);
+ if (ret) {
+ dev_err(&adapter->pdev->dev,
+ "Cannot get RSS lut, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
+ }
+ }
+
+ return ret;
}
/**
- * i40evf_configure_rss - Prepare for RSS
+ * * i40evf_get_rss_reg - Get RSS keys and lut by reading registers
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int i40evf_get_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+ const u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+ struct i40e_hw *hw = &adapter->hw;
+ u16 i;
+
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
+
+ for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
+ seed_dw[i] = rd32(hw, I40E_VFQF_HKEY(i));
+ }
+
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40EVF_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+
+ for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++)
+ lut_dw[i] = rd32(hw, I40E_VFQF_HLUT(i));
+ }
+
+ return 0;
+}
+
+/**
+ * i40evf_config_rss - Configure RSS keys and lut
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+
+ if (RSS_AQ(adapter))
+ return i40evf_config_rss_aq(vsi, seed, lut, lut_size);
+ else
+ return i40evf_config_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40evf_get_rss - Get RSS keys and lut
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int i40evf_get_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+
+ if (RSS_AQ(adapter))
+ return i40evf_get_rss_aq(vsi, seed, lut, lut_size);
+ else
+ return i40evf_get_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40evf_fill_rss_lut - Fill the lut with default values
+ * @lut: Lookup table to be filled with
+ * @rss_table_size: Lookup table size
+ * @rss_size: Range of queue number for hashing
+ **/
+static void i40evf_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size)
+{
+ u16 i;
+
+ for (i = 0; i < rss_table_size; i++)
+ lut[i] = i % rss_size;
+}
+
+/**
+ * i40evf_init_rss - Prepare for RSS
* @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
**/
-static void i40evf_configure_rss(struct i40evf_adapter *adapter)
+static int i40evf_init_rss(struct i40evf_adapter *adapter)
{
+ struct i40e_vsi *vsi = &adapter->vsi;
struct i40e_hw *hw = &adapter->hw;
u8 seed[I40EVF_HKEY_ARRAY_SIZE];
u64 hena;
-
- netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE);
+ u8 *lut;
+ int ret;
/* Enable PCTYPES for RSS, TCP/UDP with IPv4/IPv6 */
hena = I40E_DEFAULT_RSS_HENA;
wr32(hw, I40E_VFQF_HENA(0), (u32)hena);
wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32));
- if (RSS_AQ(adapter))
- i40evf_configure_rss_aq(&adapter->vsi, seed);
+ lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ /* Use user configured lut if there is one, otherwise use default */
+ if (vsi->rss_lut_user)
+ memcpy(lut, vsi->rss_lut_user, I40EVF_HLUT_ARRAY_SIZE);
+ else
+ i40evf_fill_rss_lut(lut, I40EVF_HLUT_ARRAY_SIZE,
+ adapter->num_active_queues);
+
+ /* Use user configured hash key if there is one, otherwise
+ * user default.
+ */
+ if (vsi->rss_hkey_user)
+ memcpy(seed, vsi->rss_hkey_user, I40EVF_HKEY_ARRAY_SIZE);
else
- i40evf_configure_rss_reg(adapter, seed);
+ netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE);
+ ret = i40evf_config_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE);
+ kfree(lut);
+
+ return ret;
}
/**
@@ -1327,21 +1491,22 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter)
**/
static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
{
- int q_idx, num_q_vectors;
+ int q_idx = 0, num_q_vectors;
struct i40e_q_vector *q_vector;
num_q_vectors = adapter->num_msix_vectors - NONQ_VECS;
+ adapter->q_vectors = kcalloc(num_q_vectors, sizeof(*q_vector),
+ GFP_KERNEL);
+ if (!adapter->q_vectors)
+ goto err_out;
for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
- q_vector = kzalloc(sizeof(*q_vector), GFP_KERNEL);
- if (!q_vector)
- goto err_out;
+ q_vector = &adapter->q_vectors[q_idx];
q_vector->adapter = adapter;
q_vector->vsi = &adapter->vsi;
q_vector->v_idx = q_idx;
netif_napi_add(adapter->netdev, &q_vector->napi,
i40evf_napi_poll, NAPI_POLL_WEIGHT);
- adapter->q_vector[q_idx] = q_vector;
}
return 0;
@@ -1349,11 +1514,10 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
err_out:
while (q_idx) {
q_idx--;
- q_vector = adapter->q_vector[q_idx];
+ q_vector = &adapter->q_vectors[q_idx];
netif_napi_del(&q_vector->napi);
- kfree(q_vector);
- adapter->q_vector[q_idx] = NULL;
}
+ kfree(adapter->q_vectors);
return -ENOMEM;
}
@@ -1374,13 +1538,11 @@ static void i40evf_free_q_vectors(struct i40evf_adapter *adapter)
napi_vectors = adapter->num_active_queues;
for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
- struct i40e_q_vector *q_vector = adapter->q_vector[q_idx];
-
- adapter->q_vector[q_idx] = NULL;
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[q_idx];
if (q_idx < napi_vectors)
netif_napi_del(&q_vector->napi);
- kfree(q_vector);
}
+ kfree(adapter->q_vectors);
}
/**
@@ -1439,6 +1601,22 @@ err_set_interrupt:
}
/**
+ * i40evf_clear_rss_config_user - Clear user configurations of RSS
+ * @vsi: Pointer to VSI structure
+ **/
+static void i40evf_clear_rss_config_user(struct i40e_vsi *vsi)
+{
+ if (!vsi)
+ return;
+
+ kfree(vsi->rss_hkey_user);
+ vsi->rss_hkey_user = NULL;
+
+ kfree(vsi->rss_lut_user);
+ vsi->rss_lut_user = NULL;
+}
+
+/**
* i40evf_watchdog_timer - Periodic call-back timer
* @data: pointer to adapter disguised as unsigned long
**/
@@ -1565,7 +1743,7 @@ static void i40evf_watchdog_task(struct work_struct *work)
* PF, so we don't have to set current_op as we will
* not get a response through the ARQ.
*/
- i40evf_configure_rss(adapter);
+ i40evf_init_rss(adapter);
adapter->aq_required &= ~I40EVF_FLAG_AQ_CONFIGURE_RSS;
goto watchdog_done;
}
@@ -1864,9 +2042,12 @@ void i40evf_free_all_tx_resources(struct i40evf_adapter *adapter)
{
int i;
+ if (!adapter->tx_rings)
+ return;
+
for (i = 0; i < adapter->num_active_queues; i++)
- if (adapter->tx_rings[i]->desc)
- i40evf_free_tx_resources(adapter->tx_rings[i]);
+ if (adapter->tx_rings[i].desc)
+ i40evf_free_tx_resources(&adapter->tx_rings[i]);
}
/**
@@ -1884,8 +2065,8 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter)
int i, err = 0;
for (i = 0; i < adapter->num_active_queues; i++) {
- adapter->tx_rings[i]->count = adapter->tx_desc_count;
- err = i40evf_setup_tx_descriptors(adapter->tx_rings[i]);
+ adapter->tx_rings[i].count = adapter->tx_desc_count;
+ err = i40evf_setup_tx_descriptors(&adapter->tx_rings[i]);
if (!err)
continue;
dev_err(&adapter->pdev->dev,
@@ -1911,8 +2092,8 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter)
int i, err = 0;
for (i = 0; i < adapter->num_active_queues; i++) {
- adapter->rx_rings[i]->count = adapter->rx_desc_count;
- err = i40evf_setup_rx_descriptors(adapter->rx_rings[i]);
+ adapter->rx_rings[i].count = adapter->rx_desc_count;
+ err = i40evf_setup_rx_descriptors(&adapter->rx_rings[i]);
if (!err)
continue;
dev_err(&adapter->pdev->dev,
@@ -1932,9 +2113,12 @@ void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter)
{
int i;
+ if (!adapter->rx_rings)
+ return;
+
for (i = 0; i < adapter->num_active_queues; i++)
- if (adapter->rx_rings[i]->desc)
- i40evf_free_rx_resources(adapter->rx_rings[i]);
+ if (adapter->rx_rings[i].desc)
+ i40evf_free_rx_resources(&adapter->rx_rings[i]);
}
/**
@@ -2137,7 +2321,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
netdev->features |= NETIF_F_HIGHDMA |
NETIF_F_SG |
NETIF_F_IP_CSUM |
- NETIF_F_SCTP_CSUM |
+ NETIF_F_SCTP_CRC |
NETIF_F_IPV6_CSUM |
NETIF_F_TSO |
NETIF_F_TSO6 |
@@ -2263,6 +2447,14 @@ static void i40evf_init_task(struct work_struct *work)
if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) {
err = i40evf_send_vf_config_msg(adapter);
goto err;
+ } else if (err == I40E_ERR_PARAM) {
+ /* We only get ERR_PARAM if the device is in a very bad
+ * state or if we've been disabled for previous bad
+ * behavior. Either way, we're done now.
+ */
+ i40evf_shutdown_adminq(hw);
+ dev_err(&pdev->dev, "Unable to get VF config due to PF error condition, not retrying\n");
+ return;
}
if (err) {
dev_err(&pdev->dev, "Unable to get VF config (%d)\n",
@@ -2313,7 +2505,7 @@ static void i40evf_init_task(struct work_struct *work)
I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE;
if (!RSS_AQ(adapter))
- i40evf_configure_rss(adapter);
+ i40evf_init_rss(adapter);
err = i40evf_request_misc_irq(adapter);
if (err)
goto err_sw_init;
@@ -2334,7 +2526,6 @@ static void i40evf_init_task(struct work_struct *work)
if (netdev->features & NETIF_F_GRO)
dev_info(&pdev->dev, "GRO is enabled\n");
- dev_info(&pdev->dev, "%s\n", i40evf_driver_string);
adapter->state = __I40EVF_DOWN;
set_bit(__I40E_DOWN, &adapter->vsi.state);
i40evf_misc_irq_enable(adapter);
@@ -2343,7 +2534,7 @@ static void i40evf_init_task(struct work_struct *work)
adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_RSS;
mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
} else {
- i40evf_configure_rss(adapter);
+ i40evf_init_rss(adapter);
}
return;
restart:
@@ -2438,8 +2629,7 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_master(pdev);
- netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter),
- MAX_TX_QUEUES);
+ netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter), MAX_QUEUES);
if (!netdev) {
err = -ENOMEM;
goto err_alloc_etherdev;
@@ -2632,6 +2822,9 @@ static void i40evf_remove(struct pci_dev *pdev)
flush_scheduled_work();
+ /* Clear user configurations for RSS */
+ i40evf_clear_rss_config_user(&adapter->vsi);
+
if (hw->aq.asq.count)
i40evf_shutdown_adminq(hw);
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index 32e620e1eb5c..c1c526283757 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -157,7 +157,9 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ |
I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG |
I40E_VIRTCHNL_VF_OFFLOAD_VLAN |
- I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR;
+ I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR |
+ I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2;
+
adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG;
if (PF_IS_V11(adapter))
@@ -242,7 +244,7 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES;
len = sizeof(struct i40e_virtchnl_vsi_queue_config_info) +
(sizeof(struct i40e_virtchnl_queue_pair_info) * pairs);
- vqci = kzalloc(len, GFP_ATOMIC);
+ vqci = kzalloc(len, GFP_KERNEL);
if (!vqci)
return;
@@ -255,19 +257,19 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
for (i = 0; i < pairs; i++) {
vqpi->txq.vsi_id = vqci->vsi_id;
vqpi->txq.queue_id = i;
- vqpi->txq.ring_len = adapter->tx_rings[i]->count;
- vqpi->txq.dma_ring_addr = adapter->tx_rings[i]->dma;
+ vqpi->txq.ring_len = adapter->tx_rings[i].count;
+ vqpi->txq.dma_ring_addr = adapter->tx_rings[i].dma;
vqpi->txq.headwb_enabled = 1;
vqpi->txq.dma_headwb_addr = vqpi->txq.dma_ring_addr +
(vqpi->txq.ring_len * sizeof(struct i40e_tx_desc));
vqpi->rxq.vsi_id = vqci->vsi_id;
vqpi->rxq.queue_id = i;
- vqpi->rxq.ring_len = adapter->rx_rings[i]->count;
- vqpi->rxq.dma_ring_addr = adapter->rx_rings[i]->dma;
+ vqpi->rxq.ring_len = adapter->rx_rings[i].count;
+ vqpi->rxq.dma_ring_addr = adapter->rx_rings[i].dma;
vqpi->rxq.max_pkt_size = adapter->netdev->mtu
+ ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN;
- vqpi->rxq.databuffer_size = adapter->rx_rings[i]->rx_buf_len;
+ vqpi->rxq.databuffer_size = adapter->rx_rings[i].rx_buf_len;
vqpi++;
}
@@ -353,14 +355,14 @@ void i40evf_map_queues(struct i40evf_adapter *adapter)
len = sizeof(struct i40e_virtchnl_irq_map_info) +
(adapter->num_msix_vectors *
sizeof(struct i40e_virtchnl_vector_map));
- vimi = kzalloc(len, GFP_ATOMIC);
+ vimi = kzalloc(len, GFP_KERNEL);
if (!vimi)
return;
vimi->num_vectors = adapter->num_msix_vectors;
/* Queue vectors first */
for (v_idx = 0; v_idx < q_vectors; v_idx++) {
- q_vector = adapter->q_vector[v_idx];
+ q_vector = adapter->q_vectors + v_idx;
vimi->vecmap[v_idx].vsi_id = adapter->vsi_res->vsi_id;
vimi->vecmap[v_idx].vector_id = v_idx + NONQ_VECS;
vimi->vecmap[v_idx].txq_map = q_vector->ring_mask;
@@ -391,6 +393,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
struct i40e_virtchnl_ether_addr_list *veal;
int len, i = 0, count = 0;
struct i40evf_mac_filter *f;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -415,10 +418,12 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_ether_addr_list)) /
sizeof(struct i40e_virtchnl_ether_addr);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_ether_addr_list) +
+ (count * sizeof(struct i40e_virtchnl_ether_addr));
+ more = true;
}
- veal = kzalloc(len, GFP_ATOMIC);
+ veal = kzalloc(len, GFP_KERNEL);
if (!veal)
return;
@@ -431,7 +436,8 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
f->add = false;
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_MAC_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_MAC_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS,
(u8 *)veal, len);
kfree(veal);
@@ -450,6 +456,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
struct i40e_virtchnl_ether_addr_list *veal;
struct i40evf_mac_filter *f, *ftmp;
int len, i = 0, count = 0;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -474,9 +481,11 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_ether_addr_list)) /
sizeof(struct i40e_virtchnl_ether_addr);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_ether_addr_list) +
+ (count * sizeof(struct i40e_virtchnl_ether_addr));
+ more = true;
}
- veal = kzalloc(len, GFP_ATOMIC);
+ veal = kzalloc(len, GFP_KERNEL);
if (!veal)
return;
@@ -490,7 +499,8 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
kfree(f);
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_MAC_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_MAC_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS,
(u8 *)veal, len);
kfree(veal);
@@ -509,6 +519,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
struct i40e_virtchnl_vlan_filter_list *vvfl;
int len, i = 0, count = 0;
struct i40evf_vlan_filter *f;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -534,9 +545,11 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_vlan_filter_list)) /
sizeof(u16);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
+ (count * sizeof(u16));
+ more = true;
}
- vvfl = kzalloc(len, GFP_ATOMIC);
+ vvfl = kzalloc(len, GFP_KERNEL);
if (!vvfl)
return;
@@ -549,7 +562,8 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
f->add = false;
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_VLAN, (u8 *)vvfl, len);
kfree(vvfl);
}
@@ -567,6 +581,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
struct i40e_virtchnl_vlan_filter_list *vvfl;
struct i40evf_vlan_filter *f, *ftmp;
int len, i = 0, count = 0;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -592,9 +607,11 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_vlan_filter_list)) /
sizeof(u16);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
+ (count * sizeof(u16));
+ more = true;
}
- vvfl = kzalloc(len, GFP_ATOMIC);
+ vvfl = kzalloc(len, GFP_KERNEL);
if (!vvfl)
return;
@@ -608,7 +625,8 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
kfree(f);
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_VLAN, (u8 *)vvfl, len);
kfree(vvfl);
}
@@ -724,9 +742,29 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
return;
}
if (v_retval) {
- dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n",
- v_retval, i40evf_stat_str(&adapter->hw, v_retval),
- v_opcode);
+ switch (v_opcode) {
+ case I40E_VIRTCHNL_OP_ADD_VLAN:
+ dev_err(&adapter->pdev->dev, "Failed to add VLAN filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
+ dev_err(&adapter->pdev->dev, "Failed to add MAC filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ case I40E_VIRTCHNL_OP_DEL_VLAN:
+ dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
+ dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ default:
+ dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n",
+ v_retval,
+ i40evf_stat_str(&adapter->hw, v_retval),
+ v_opcode);
+ }
}
switch (v_opcode) {
case I40E_VIRTCHNL_OP_GET_STATS: {
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 7a73510e547c..adb33e2a0137 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -45,8 +45,6 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *);
static s32 igb_init_hw_82575(struct e1000_hw *);
static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *);
static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *, u32, u16 *);
-static s32 igb_read_phy_reg_82580(struct e1000_hw *, u32, u16 *);
-static s32 igb_write_phy_reg_82580(struct e1000_hw *, u32, u16);
static s32 igb_reset_hw_82575(struct e1000_hw *);
static s32 igb_reset_hw_82580(struct e1000_hw *);
static s32 igb_set_d0_lplu_state_82575(struct e1000_hw *, bool);
@@ -205,13 +203,10 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
case e1000_82580:
case e1000_i350:
case e1000_i354:
- phy->ops.read_reg = igb_read_phy_reg_82580;
- phy->ops.write_reg = igb_write_phy_reg_82580;
- break;
case e1000_i210:
case e1000_i211:
- phy->ops.read_reg = igb_read_phy_reg_gs40g;
- phy->ops.write_reg = igb_write_phy_reg_gs40g;
+ phy->ops.read_reg = igb_read_phy_reg_82580;
+ phy->ops.write_reg = igb_write_phy_reg_82580;
break;
default:
phy->ops.read_reg = igb_read_phy_reg_igp;
@@ -272,6 +267,11 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
if (ret_val)
goto out;
}
+ if (phy->id == M88E1543_E_PHY_ID) {
+ ret_val = igb_initialize_M88E1543_phy(hw);
+ if (ret_val)
+ goto out;
+ }
break;
case IGP03E1000_E_PHY_ID:
phy->type = e1000_phy_igp_3;
@@ -294,6 +294,7 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
case I210_I_PHY_ID:
phy->type = e1000_phy_i210;
phy->ops.check_polarity = igb_check_polarity_m88;
+ phy->ops.get_cfg_done = igb_get_cfg_done_i210;
phy->ops.get_phy_info = igb_get_phy_info_m88;
phy->ops.get_cable_length = igb_get_cable_length_m88_gen2;
phy->ops.set_d0_lplu_state = igb_set_d0_lplu_state_82580;
@@ -925,6 +926,8 @@ static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *hw)
if (phy->id == M88E1512_E_PHY_ID)
ret_val = igb_initialize_M88E1512_phy(hw);
+ if (phy->id == M88E1543_E_PHY_ID)
+ ret_val = igb_initialize_M88E1543_phy(hw);
out:
return ret_val;
}
@@ -2145,7 +2148,7 @@ void igb_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable)
* Reads the MDI control register in the PHY at offset and stores the
* information read to data.
**/
-static s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data)
+s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data)
{
s32 ret_val;
@@ -2169,7 +2172,7 @@ out:
*
* Writes data to MDI control register in the PHY at offset.
**/
-static s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data)
+s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data)
{
s32 ret_val;
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index b1915043bc0c..c3c598c347a9 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -927,7 +927,10 @@
/* Intel i347-AT4 Registers */
-#define I347AT4_PCDL 0x10 /* PHY Cable Diagnostics Length */
+#define I347AT4_PCDL0 0x10 /* Pair 0 PHY Cable Diagnostics Length */
+#define I347AT4_PCDL1 0x11 /* Pair 1 PHY Cable Diagnostics Length */
+#define I347AT4_PCDL2 0x12 /* Pair 2 PHY Cable Diagnostics Length */
+#define I347AT4_PCDL3 0x13 /* Pair 3 PHY Cable Diagnostics Length */
#define I347AT4_PCDC 0x15 /* PHY Cable Diagnostics Control */
#define I347AT4_PAGE_SELECT 0x16
@@ -990,6 +993,7 @@
#define E1000_M88E1543_PAGE_ADDR 0x16 /* Page Offset Register */
#define E1000_M88E1543_EEE_CTRL_1 0x0
#define E1000_M88E1543_EEE_CTRL_1_MS 0x0001 /* EEE Master/Slave */
+#define E1000_M88E1543_FIBER_CTRL 0x0
#define E1000_EEE_ADV_DEV_I354 7
#define E1000_EEE_ADV_ADDR_I354 60
#define E1000_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index 2003b3756ba2..4034207eb5cc 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -441,6 +441,7 @@ struct e1000_phy_info {
u16 cable_length;
u16 max_cable_length;
u16 min_cable_length;
+ u16 pair_length[4];
u8 mdix;
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c
index 65d931669f81..8aa798737d4d 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.c
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.c
@@ -861,10 +861,10 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw)
if (ret_val)
nvm_word = E1000_INVM_DEFAULT_AL;
tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL;
+ igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, E1000_PHY_PLL_FREQ_PAGE);
for (i = 0; i < E1000_MAX_PLL_TRIES; i++) {
/* check current state directly from internal PHY */
- igb_read_phy_reg_gs40g(hw, (E1000_PHY_PLL_FREQ_PAGE |
- E1000_PHY_PLL_FREQ_REG), &phy_word);
+ igb_read_phy_reg_82580(hw, E1000_PHY_PLL_FREQ_REG, &phy_word);
if ((phy_word & E1000_PHY_PLL_UNCONF)
!= E1000_PHY_PLL_UNCONF) {
ret_val = 0;
@@ -896,7 +896,35 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw)
/* restore WUC register */
wr32(E1000_WUC, wuc);
}
+ igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, 0);
/* restore MDICNFG setting */
wr32(E1000_MDICNFG, mdicnfg);
return ret_val;
}
+
+/**
+ * igb_get_cfg_done_i210 - Read config done bit
+ * @hw: pointer to the HW structure
+ *
+ * Read the management control register for the config done bit for
+ * completion status. NOTE: silicon which is EEPROM-less will fail trying
+ * to read the config done bit, so an error is *ONLY* logged and returns
+ * 0. If we were to return with error, EEPROM-less silicon
+ * would not be able to be reset or change link.
+ **/
+s32 igb_get_cfg_done_i210(struct e1000_hw *hw)
+{
+ s32 timeout = PHY_CFG_TIMEOUT;
+ u32 mask = E1000_NVM_CFG_DONE_PORT_0;
+
+ while (timeout) {
+ if (rd32(E1000_EEMNGCTL_I210) & mask)
+ break;
+ usleep_range(1000, 2000);
+ timeout--;
+ }
+ if (!timeout)
+ hw_dbg("MNG configuration cycle has not completed.\n");
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h
index 3442b6357d01..b2964a2a60b1 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.h
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.h
@@ -34,6 +34,7 @@ s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data);
s32 igb_init_nvm_params_i210(struct e1000_hw *hw);
bool igb_get_flash_presence_i210(struct e1000_hw *hw);
s32 igb_pll_workaround_i210(struct e1000_hw *hw);
+s32 igb_get_cfg_done_i210(struct e1000_hw *hw);
#define E1000_STM_OPCODE 0xDB00
#define E1000_EEPROM_FLASH_SIZE_WORD 0x11
@@ -84,7 +85,7 @@ enum E1000_INVM_STRUCTURE_TYPE {
#define E1000_PCI_PMCSR_D3 0x03
#define E1000_MAX_PLL_TRIES 5
#define E1000_PHY_PLL_UNCONF 0xFF
-#define E1000_PHY_PLL_FREQ_PAGE 0xFC0000
+#define E1000_PHY_PLL_FREQ_PAGE 0xFC
#define E1000_PHY_PLL_FREQ_REG 0x000E
#define E1000_INVM_DEFAULT_AL 0x202F
#define E1000_INVM_AUTOLOAD 0x0A
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index 23ec28f43f6d..5b54254aed4f 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -1717,59 +1717,76 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw)
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 phy_data, phy_data2, index, default_page, is_cm;
+ int len_tot = 0;
+ u16 len_min;
+ u16 len_max;
switch (hw->phy.id) {
+ case M88E1543_E_PHY_ID:
+ case M88E1512_E_PHY_ID:
+ case I347AT4_E_PHY_ID:
case I210_I_PHY_ID:
- /* Get cable length from PHY Cable Diagnostics Control Reg */
- ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) +
- (I347AT4_PCDL + phy->addr),
- &phy_data);
+ /* Remember the original page select and set it to 7 */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT,
+ &default_page);
if (ret_val)
- return ret_val;
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07);
+ if (ret_val)
+ goto out;
/* Check if the unit of cable length is meters or cm */
- ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) +
- I347AT4_PCDC, &phy_data2);
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2);
if (ret_val)
- return ret_val;
+ goto out;
is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT);
- /* Populate the phy structure with cable length in meters */
- phy->min_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->max_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->cable_length = phy_data / (is_cm ? 100 : 1);
- break;
- case M88E1543_E_PHY_ID:
- case M88E1512_E_PHY_ID:
- case I347AT4_E_PHY_ID:
- /* Remember the original page select and set it to 7 */
- ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT,
- &default_page);
+ /* Get cable length from Pair 0 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL0, &phy_data);
if (ret_val)
goto out;
- ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07);
+ phy->pair_length[0] = phy_data / (is_cm ? 100 : 1);
+ len_tot = phy->pair_length[0];
+ len_min = phy->pair_length[0];
+ len_max = phy->pair_length[0];
+
+ /* Get cable length from Pair 1 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL1, &phy_data);
if (ret_val)
goto out;
- /* Get cable length from PHY Cable Diagnostics Control Reg */
- ret_val = phy->ops.read_reg(hw, (I347AT4_PCDL + phy->addr),
- &phy_data);
+ phy->pair_length[1] = phy_data / (is_cm ? 100 : 1);
+ len_tot += phy->pair_length[1];
+ len_min = min(len_min, phy->pair_length[1]);
+ len_max = max(len_max, phy->pair_length[1]);
+
+ /* Get cable length from Pair 2 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL2, &phy_data);
if (ret_val)
goto out;
- /* Check if the unit of cable length is meters or cm */
- ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2);
+ phy->pair_length[2] = phy_data / (is_cm ? 100 : 1);
+ len_tot += phy->pair_length[2];
+ len_min = min(len_min, phy->pair_length[2]);
+ len_max = max(len_max, phy->pair_length[2]);
+
+ /* Get cable length from Pair 3 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL3, &phy_data);
if (ret_val)
goto out;
- is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT);
+ phy->pair_length[3] = phy_data / (is_cm ? 100 : 1);
+ len_tot += phy->pair_length[3];
+ len_min = min(len_min, phy->pair_length[3]);
+ len_max = max(len_max, phy->pair_length[3]);
/* Populate the phy structure with cable length in meters */
- phy->min_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->max_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->cable_length = phy_data / (is_cm ? 100 : 1);
+ phy->min_cable_length = len_min;
+ phy->max_cable_length = len_max;
+ phy->cable_length = len_tot / 4;
/* Reset the page selec to its original value */
ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT,
@@ -2278,6 +2295,100 @@ out:
}
/**
+ * igb_initialize_M88E1543_phy - Initialize M88E1512 PHY
+ * @hw: pointer to the HW structure
+ *
+ * Initialize Marvell 1543 to work correctly with Avoton.
+ **/
+s32 igb_initialize_M88E1543_phy(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val = 0;
+
+ /* Switch to PHY page 0xFF. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FF);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x214B);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2144);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x0C28);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2146);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xB233);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x214D);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xDC0C);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2159);
+ if (ret_val)
+ goto out;
+
+ /* Switch to PHY page 0xFB. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FB);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_3, 0x0C0D);
+ if (ret_val)
+ goto out;
+
+ /* Switch to PHY page 0x12. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x12);
+ if (ret_val)
+ goto out;
+
+ /* Change mode to SGMII-to-Copper */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_MODE, 0x8001);
+ if (ret_val)
+ goto out;
+
+ /* Switch to PHY page 1. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x1);
+ if (ret_val)
+ goto out;
+
+ /* Change mode to 1000BASE-X/SGMII and autoneg enable */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_FIBER_CTRL, 0x9140);
+ if (ret_val)
+ goto out;
+
+ /* Return the PHY to page 0. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0);
+ if (ret_val)
+ goto out;
+
+ ret_val = igb_phy_sw_reset(hw);
+ if (ret_val) {
+ hw_dbg("Error committing the PHY changes\n");
+ return ret_val;
+ }
+
+ /* msec_delay(1000); */
+ usleep_range(1000, 2000);
+out:
+ return ret_val;
+}
+
+/**
* igb_power_up_phy_copper - Restore copper link in case of PHY power down
* @hw: pointer to the HW structure
*
@@ -2494,66 +2605,6 @@ out:
}
/**
- * igb_write_phy_reg_gs40g - Write GS40G PHY register
- * @hw: pointer to the HW structure
- * @offset: lower half is register offset to write to
- * upper half is page to use.
- * @data: data to write at register offset
- *
- * Acquires semaphore, if necessary, then writes the data to PHY register
- * at the offset. Release any acquired semaphores before exiting.
- **/
-s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data)
-{
- s32 ret_val;
- u16 page = offset >> GS40G_PAGE_SHIFT;
-
- offset = offset & GS40G_OFFSET_MASK;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- return ret_val;
-
- ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page);
- if (ret_val)
- goto release;
- ret_val = igb_write_phy_reg_mdic(hw, offset, data);
-
-release:
- hw->phy.ops.release(hw);
- return ret_val;
-}
-
-/**
- * igb_read_phy_reg_gs40g - Read GS40G PHY register
- * @hw: pointer to the HW structure
- * @offset: lower half is register offset to read to
- * upper half is page to use.
- * @data: data to read at register offset
- *
- * Acquires semaphore, if necessary, then reads the data in the PHY register
- * at the offset. Release any acquired semaphores before exiting.
- **/
-s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data)
-{
- s32 ret_val;
- u16 page = offset >> GS40G_PAGE_SHIFT;
-
- offset = offset & GS40G_OFFSET_MASK;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- return ret_val;
-
- ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page);
- if (ret_val)
- goto release;
- ret_val = igb_read_phy_reg_mdic(hw, offset, data);
-
-release:
- hw->phy.ops.release(hw);
- return ret_val;
-}
-
-/**
* igb_set_master_slave_mode - Setup PHY for Master/slave mode
* @hw: pointer to the HW structure
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h
index 24d55edbb0e3..969a6ddafa3b 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.h
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.h
@@ -62,6 +62,7 @@ void igb_power_up_phy_copper(struct e1000_hw *hw);
void igb_power_down_phy_copper(struct e1000_hw *hw);
s32 igb_phy_init_script_igp3(struct e1000_hw *hw);
s32 igb_initialize_M88E1512_phy(struct e1000_hw *hw);
+s32 igb_initialize_M88E1543_phy(struct e1000_hw *hw);
s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
@@ -71,8 +72,8 @@ s32 igb_copper_link_setup_82580(struct e1000_hw *hw);
s32 igb_get_phy_info_82580(struct e1000_hw *hw);
s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw);
s32 igb_get_cable_length_82580(struct e1000_hw *hw);
-s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data);
-s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data);
+s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_check_polarity_m88(struct e1000_hw *hw);
/* IGP01E1000 Specific Registers */
@@ -143,17 +144,6 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw);
#define E1000_CABLE_LENGTH_UNDEFINED 0xFF
-/* GS40G - I210 PHY defines */
-#define GS40G_PAGE_SELECT 0x16
-#define GS40G_PAGE_SHIFT 16
-#define GS40G_OFFSET_MASK 0xFFFF
-#define GS40G_PAGE_2 0x20000
-#define GS40G_MAC_REG2 0x15
-#define GS40G_MAC_LB 0x4140
-#define GS40G_MAC_SPEED_1G 0X0006
-#define GS40G_COPPER_SPEC 0x0010
-#define GS40G_LINE_LB 0x4000
-
/* SFP modules ID memory locations */
#define E1000_SFF_IDENTIFIER_OFFSET 0x00
#define E1000_SFF_IDENTIFIER_SFF 0x02
diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 4af2870e49f8..21d9d02885cb 100644
--- a/drivers/net/ethernet/intel/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -66,6 +66,7 @@
#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */
#define E1000_PBS 0x01008 /* Packet Buffer Size */
#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */
+#define E1000_EEMNGCTL_I210 0x12030 /* MNG EEprom Control */
#define E1000_EEARBC_I210 0x12024 /* EEPROM Auto Read Bus Control */
#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */
#define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */
@@ -385,8 +386,7 @@ do { \
#define array_wr32(reg, offset, value) \
wr32((reg) + ((offset) << 2), (value))
-#define array_rd32(reg, offset) \
- (readl(hw->hw_addr + reg + ((offset) << 2)))
+#define array_rd32(reg, offset) (igb_rd32(hw, reg + ((offset) << 2)))
/* DMA Coalescing registers */
#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index 1a2f1cc44b28..e3cb93bdb21a 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -389,6 +389,8 @@ struct igb_adapter {
u16 link_speed;
u16 link_duplex;
+ u8 __iomem *io_addr; /* Mainly for iounmap use */
+
struct work_struct reset_task;
struct work_struct watchdog_task;
bool fc_autoneg;
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 2529bc625de4..1d329f1d047b 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -127,10 +127,20 @@ static const struct igb_stats igb_gstrings_net_stats[] = {
#define IGB_STATS_LEN \
(IGB_GLOBAL_STATS_LEN + IGB_NETDEV_STATS_LEN + IGB_QUEUE_STATS_LEN)
+enum igb_diagnostics_results {
+ TEST_REG = 0,
+ TEST_EEP,
+ TEST_IRQ,
+ TEST_LOOP,
+ TEST_LINK
+};
+
static const char igb_gstrings_test[][ETH_GSTRING_LEN] = {
- "Register test (offline)", "Eeprom test (offline)",
- "Interrupt test (offline)", "Loopback test (offline)",
- "Link test (on/offline)"
+ [TEST_REG] = "Register test (offline)",
+ [TEST_EEP] = "Eeprom test (offline)",
+ [TEST_IRQ] = "Interrupt test (offline)",
+ [TEST_LOOP] = "Loopback test (offline)",
+ [TEST_LINK] = "Link test (on/offline)"
};
#define IGB_TEST_LEN (sizeof(igb_gstrings_test) / ETH_GSTRING_LEN)
@@ -2002,7 +2012,7 @@ static void igb_diag_test(struct net_device *netdev,
/* Link test performed before hardware reset so autoneg doesn't
* interfere with test result
*/
- if (igb_link_test(adapter, &data[4]))
+ if (igb_link_test(adapter, &data[TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
if (if_running)
@@ -2011,21 +2021,21 @@ static void igb_diag_test(struct net_device *netdev,
else
igb_reset(adapter);
- if (igb_reg_test(adapter, &data[0]))
+ if (igb_reg_test(adapter, &data[TEST_REG]))
eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter);
- if (igb_eeprom_test(adapter, &data[1]))
+ if (igb_eeprom_test(adapter, &data[TEST_EEP]))
eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter);
- if (igb_intr_test(adapter, &data[2]))
+ if (igb_intr_test(adapter, &data[TEST_IRQ]))
eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter);
/* power up link for loopback test */
igb_power_up_link(adapter);
- if (igb_loopback_test(adapter, &data[3]))
+ if (igb_loopback_test(adapter, &data[TEST_LOOP]))
eth_test->flags |= ETH_TEST_FL_FAILED;
/* restore speed, duplex, autoneg settings */
@@ -2045,16 +2055,16 @@ static void igb_diag_test(struct net_device *netdev,
dev_info(&adapter->pdev->dev, "online testing starting\n");
/* PHY is powered down when interface is down */
- if (if_running && igb_link_test(adapter, &data[4]))
+ if (if_running && igb_link_test(adapter, &data[TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
else
- data[4] = 0;
+ data[TEST_LINK] = 0;
/* Online tests aren't run; pass by default */
- data[0] = 0;
- data[1] = 0;
- data[2] = 0;
- data[3] = 0;
+ data[TEST_REG] = 0;
+ data[TEST_EEP] = 0;
+ data[TEST_IRQ] = 0;
+ data[TEST_LOOP] = 0;
clear_bit(__IGB_TESTING, &adapter->state);
}
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ea7b09887245..31e5f3942839 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -946,7 +946,6 @@ static void igb_configure_msix(struct igb_adapter *adapter)
static int igb_request_msix(struct igb_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
- struct e1000_hw *hw = &adapter->hw;
int i, err = 0, vector = 0, free_vector = 0;
err = request_irq(adapter->msix_entries[vector].vector,
@@ -959,7 +958,7 @@ static int igb_request_msix(struct igb_adapter *adapter)
vector++;
- q_vector->itr_register = hw->hw_addr + E1000_EITR(vector);
+ q_vector->itr_register = adapter->io_addr + E1000_EITR(vector);
if (q_vector->rx.ring && q_vector->tx.ring)
sprintf(q_vector->name, "%s-TxRx-%u", netdev->name,
@@ -1230,7 +1229,7 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
q_vector->tx.work_limit = adapter->tx_work_limit;
/* initialize ITR configuration */
- q_vector->itr_register = adapter->hw.hw_addr + E1000_EITR(0);
+ q_vector->itr_register = adapter->io_addr + E1000_EITR(0);
q_vector->itr_val = IGB_START_ITR;
/* initialize pointer to rings */
@@ -2294,9 +2293,11 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
err = -EIO;
- hw->hw_addr = pci_iomap(pdev, 0, 0);
- if (!hw->hw_addr)
+ adapter->io_addr = pci_iomap(pdev, 0, 0);
+ if (!adapter->io_addr)
goto err_ioremap;
+ /* hw->hw_addr can be altered, we'll use adapter->io_addr for unmap */
+ hw->hw_addr = adapter->io_addr;
netdev->netdev_ops = &igb_netdev_ops;
igb_set_ethtool_ops(netdev);
@@ -2378,8 +2379,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
if (hw->mac.type >= e1000_82576) {
- netdev->hw_features |= NETIF_F_SCTP_CSUM;
- netdev->features |= NETIF_F_SCTP_CSUM;
+ netdev->hw_features |= NETIF_F_SCTP_CRC;
+ netdev->features |= NETIF_F_SCTP_CRC;
}
netdev->priv_flags |= IFF_UNICAST_FLT;
@@ -2656,7 +2657,7 @@ err_sw_init:
#ifdef CONFIG_PCI_IOV
igb_disable_sriov(pdev);
#endif
- pci_iounmap(pdev, hw->hw_addr);
+ pci_iounmap(pdev, adapter->io_addr);
err_ioremap:
free_netdev(netdev);
err_alloc_etherdev:
@@ -2823,7 +2824,7 @@ static void igb_remove(struct pci_dev *pdev)
igb_clear_interrupt_scheme(adapter);
- pci_iounmap(pdev, hw->hw_addr);
+ pci_iounmap(pdev, adapter->io_addr);
if (hw->flash_address)
iounmap(hw->flash_address);
pci_release_selected_regions(pdev,
@@ -2856,6 +2857,13 @@ static void igb_probe_vfs(struct igb_adapter *adapter)
if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211))
return;
+ /* Of the below we really only want the effect of getting
+ * IGB_FLAG_HAS_MSIX set (if available), without which
+ * igb_enable_sriov() has no effect.
+ */
+ igb_set_interrupt_capability(adapter, true);
+ igb_reset_interrupt_capability(adapter);
+
pci_sriov_set_totalvfs(pdev, 7);
igb_enable_sriov(pdev, max_vfs);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index 1d2174526a4c..4b9156cd8b93 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -139,6 +139,7 @@ enum ixgbe_tx_flags {
#define IXGBE_X540_VF_DEVICE_ID 0x1515
struct vf_data_storage {
+ struct pci_dev *vfdev;
unsigned char vf_mac_addresses[ETH_ALEN];
u16 vf_mc_hashes[IXGBE_MAX_VF_MC_ENTRIES];
u16 num_vf_mc_hashes;
@@ -224,6 +225,8 @@ struct ixgbe_rx_queue_stats {
u64 csum_err;
};
+#define IXGBE_TS_HDR_LEN 8
+
enum ixgbe_ring_state_t {
__IXGBE_TX_FDIR_INIT_DONE,
__IXGBE_TX_XPS_INIT_DONE,
@@ -282,6 +285,8 @@ struct ixgbe_ring {
u16 next_to_use;
u16 next_to_clean;
+ unsigned long last_rx_timestamp;
+
union {
u16 next_to_alloc;
struct {
@@ -312,7 +317,7 @@ enum ixgbe_ring_f_enum {
};
#define IXGBE_MAX_RSS_INDICES 16
-#define IXGBE_MAX_RSS_INDICES_X550 64
+#define IXGBE_MAX_RSS_INDICES_X550 63
#define IXGBE_MAX_VMDQ_INDICES 64
#define IXGBE_MAX_FDIR_INDICES 63 /* based on q_vector limit */
#define IXGBE_MAX_FCOE_INDICES 8
@@ -587,9 +592,10 @@ static inline u16 ixgbe_desc_unused(struct ixgbe_ring *ring)
struct ixgbe_mac_addr {
u8 addr[ETH_ALEN];
- u16 queue;
+ u16 pool;
u16 state; /* bitmask */
};
+
#define IXGBE_MAC_STATE_DEFAULT 0x1
#define IXGBE_MAC_STATE_MODIFIED 0x2
#define IXGBE_MAC_STATE_IN_USE 0x4
@@ -639,6 +645,8 @@ struct ixgbe_adapter {
#define IXGBE_FLAG_SRIOV_CAPABLE (u32)(1 << 22)
#define IXGBE_FLAG_SRIOV_ENABLED (u32)(1 << 23)
#define IXGBE_FLAG_VXLAN_OFFLOAD_CAPABLE BIT(24)
+#define IXGBE_FLAG_RX_HWTSTAMP_ENABLED BIT(25)
+#define IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER BIT(26)
u32 flags2;
#define IXGBE_FLAG2_RSC_CAPABLE (u32)(1 << 0)
@@ -656,6 +664,7 @@ struct ixgbe_adapter {
#ifdef CONFIG_IXGBE_VXLAN
#define IXGBE_FLAG2_VXLAN_REREG_NEEDED BIT(12)
#endif
+#define IXGBE_FLAG2_VLAN_PROMISC BIT(13)
/* Tx fast path data */
int num_tx_queues;
@@ -755,9 +764,12 @@ struct ixgbe_adapter {
unsigned long last_rx_ptp_check;
unsigned long last_rx_timestamp;
spinlock_t tmreg_lock;
- struct cyclecounter cc;
- struct timecounter tc;
+ struct cyclecounter hw_cc;
+ struct timecounter hw_tc;
u32 base_incval;
+ u32 tx_hwtstamp_timeouts;
+ u32 rx_hwtstamp_cleared;
+ void (*ptp_setup_sdp)(struct ixgbe_adapter *);
/* SR-IOV */
DECLARE_BITMAP(active_vfs, IXGBE_MAX_VF_FUNCTIONS);
@@ -883,9 +895,10 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter);
#endif
int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
- u8 *addr, u16 queue);
+ const u8 *addr, u16 queue);
int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
- u8 *addr, u16 queue);
+ const u8 *addr, u16 queue);
+void ixgbe_update_pf_promisc_vlvf(struct ixgbe_adapter *adapter, u32 vid);
void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter);
netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *,
struct ixgbe_ring *);
@@ -968,12 +981,33 @@ void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter);
void ixgbe_ptp_stop(struct ixgbe_adapter *adapter);
void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter);
void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter);
-void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb);
+void ixgbe_ptp_rx_pktstamp(struct ixgbe_q_vector *, struct sk_buff *);
+void ixgbe_ptp_rx_rgtstamp(struct ixgbe_q_vector *, struct sk_buff *skb);
+static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring,
+ union ixgbe_adv_rx_desc *rx_desc,
+ struct sk_buff *skb)
+{
+ if (unlikely(ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_TSIP))) {
+ ixgbe_ptp_rx_pktstamp(rx_ring->q_vector, skb);
+ return;
+ }
+
+ if (unlikely(!ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS)))
+ return;
+
+ ixgbe_ptp_rx_rgtstamp(rx_ring->q_vector, skb);
+
+ /* Update the last_rx_timestamp timer in order to enable watchdog check
+ * for error case of latched timestamp on a dropped packet.
+ */
+ rx_ring->last_rx_timestamp = jiffies;
+}
+
int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr);
int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr);
void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter);
void ixgbe_ptp_reset(struct ixgbe_adapter *adapter);
-void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr);
+void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter);
#ifdef CONFIG_PCI_IOV
void ixgbe_sriov_reinit(struct ixgbe_adapter *adapter);
#endif
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
index 65db69b862fb..d8a9fb8a59e2 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2014 Intel Corporation.
+ Copyright(c) 1999 - 2015 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,
@@ -765,13 +765,14 @@ mac_reset_top:
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL) | IXGBE_CTRL_RST;
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST) {
status = IXGBE_ERR_RESET_FAILED;
@@ -879,11 +880,12 @@ static s32 ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq)
* @vlan: VLAN id to write to VLAN filter
* @vind: VMDq output index that maps queue to VLAN id in VFTA
* @vlan_on: boolean flag to turn on/off VLAN in VFTA
+ * @vlvf_bypass: boolean flag - unused
*
* Turn on/off specified VLAN in the VLAN filter table.
**/
static s32 ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind,
- bool vlan_on)
+ bool vlan_on, bool vlvf_bypass)
{
u32 regindex;
u32 bitindex;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index a39afcf03e2c..fa8d4f40ac2a 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -990,13 +990,14 @@ mac_reset_top:
ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST_MASK) {
@@ -1082,12 +1083,16 @@ mac_reset_top:
/* Add the SAN MAC address to the RAR only if it's a valid address */
if (is_valid_ether_addr(hw->mac.san_addr)) {
- hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
- hw->mac.san_addr, 0, IXGBE_RAH_AV);
-
/* Save the SAN MAC RAR index */
hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
+ hw->mac.ops.set_rar(hw, hw->mac.san_mac_rar_index,
+ hw->mac.san_addr, 0, IXGBE_RAH_AV);
+
+ /* clear VMDq pool/queue selection for this RAR */
+ hw->mac.ops.clear_vmdq(hw, hw->mac.san_mac_rar_index,
+ IXGBE_CLEAR_VMDQ_ALL);
+
/* Reserve the last RAR for the SAN MAC address */
hw->mac.num_rar_entries--;
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
index ce61b36b94f1..64045053e874 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2014 Intel Corporation.
+ Copyright(c) 1999 - 2015 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,
@@ -1884,10 +1884,11 @@ s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw)
hw_dbg(hw, " New MAC Addr =%pM\n", hw->mac.addr);
hw->mac.ops.set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV);
-
- /* clear VMDq pool/queue selection for RAR 0 */
- hw->mac.ops.clear_vmdq(hw, 0, IXGBE_CLEAR_VMDQ_ALL);
}
+
+ /* clear VMDq pool/queue selection for RAR 0 */
+ hw->mac.ops.clear_vmdq(hw, 0, IXGBE_CLEAR_VMDQ_ALL);
+
hw->addr_ctrl.overflow_promisc = 0;
hw->addr_ctrl.rar_used_count = 1;
@@ -2454,6 +2455,17 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
/* Always set this bit to ensure any future transactions are blocked */
IXGBE_WRITE_REG(hw, IXGBE_CTRL, IXGBE_CTRL_GIO_DIS);
+ /* Poll for bit to read as set */
+ for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) {
+ if (IXGBE_READ_REG(hw, IXGBE_CTRL) & IXGBE_CTRL_GIO_DIS)
+ break;
+ usleep_range(100, 120);
+ }
+ if (i >= IXGBE_PCI_MASTER_DISABLE_TIMEOUT) {
+ hw_dbg(hw, "GIO disable did not set - requesting resets\n");
+ goto gio_disable_fail;
+ }
+
/* Exit if master requests are blocked */
if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO) ||
ixgbe_removed(hw->hw_addr))
@@ -2475,6 +2487,7 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
* again to clear out any effects they may have had on our device.
*/
hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n");
+gio_disable_fail:
hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
if (hw->mac.type >= ixgbe_mac_X550)
@@ -2987,43 +3000,44 @@ s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw)
* return the VLVF index where this VLAN id should be placed
*
**/
-static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan)
+static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass)
{
- u32 bits = 0;
- u32 first_empty_slot = 0;
- s32 regindex;
+ s32 regindex, first_empty_slot;
+ u32 bits;
/* short cut the special case */
if (vlan == 0)
return 0;
- /*
- * Search for the vlan id in the VLVF entries. Save off the first empty
- * slot found along the way
- */
- for (regindex = 1; regindex < IXGBE_VLVF_ENTRIES; regindex++) {
+ /* if vlvf_bypass is set we don't want to use an empty slot, we
+ * will simply bypass the VLVF if there are no entries present in the
+ * VLVF that contain our VLAN
+ */
+ first_empty_slot = vlvf_bypass ? IXGBE_ERR_NO_SPACE : 0;
+
+ /* add VLAN enable bit for comparison */
+ vlan |= IXGBE_VLVF_VIEN;
+
+ /* Search for the vlan id in the VLVF entries. Save off the first empty
+ * slot found along the way.
+ *
+ * pre-decrement loop covering (IXGBE_VLVF_ENTRIES - 1) .. 1
+ */
+ for (regindex = IXGBE_VLVF_ENTRIES; --regindex;) {
bits = IXGBE_READ_REG(hw, IXGBE_VLVF(regindex));
- if (!bits && !(first_empty_slot))
+ if (bits == vlan)
+ return regindex;
+ if (!first_empty_slot && !bits)
first_empty_slot = regindex;
- else if ((bits & 0x0FFF) == vlan)
- break;
}
- /*
- * If regindex is less than IXGBE_VLVF_ENTRIES, then we found the vlan
- * in the VLVF. Else use the first empty VLVF register for this
- * vlan id.
- */
- if (regindex >= IXGBE_VLVF_ENTRIES) {
- if (first_empty_slot)
- regindex = first_empty_slot;
- else {
- hw_dbg(hw, "No space in VLVF.\n");
- regindex = IXGBE_ERR_NO_SPACE;
- }
- }
+ /* If we are here then we didn't find the VLAN. Return first empty
+ * slot we found during our search, else error.
+ */
+ if (!first_empty_slot)
+ hw_dbg(hw, "No space in VLVF.\n");
- return regindex;
+ return first_empty_slot ? : IXGBE_ERR_NO_SPACE;
}
/**
@@ -3032,21 +3046,17 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan)
* @vlan: VLAN id to write to VLAN filter
* @vind: VMDq output index that maps queue to VLAN id in VFVFB
* @vlan_on: boolean flag to turn on/off VLAN in VFVF
+ * @vlvf_bypass: boolean flag indicating updating default pool is okay
*
* Turn on/off specified VLAN in the VLAN filter table.
**/
s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
- bool vlan_on)
+ bool vlan_on, bool vlvf_bypass)
{
- s32 regindex;
- u32 bitindex;
- u32 vfta;
- u32 bits;
- u32 vt;
- u32 targetbit;
- bool vfta_changed = false;
+ u32 regidx, vfta_delta, vfta, bits;
+ s32 vlvf_index;
- if (vlan > 4095)
+ if ((vlan > 4095) || (vind > 63))
return IXGBE_ERR_PARAM;
/*
@@ -3061,22 +3071,16 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
* bits[11-5]: which register
* bits[4-0]: which bit in the register
*/
- regindex = (vlan >> 5) & 0x7F;
- bitindex = vlan & 0x1F;
- targetbit = (1 << bitindex);
- vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(regindex));
-
- if (vlan_on) {
- if (!(vfta & targetbit)) {
- vfta |= targetbit;
- vfta_changed = true;
- }
- } else {
- if ((vfta & targetbit)) {
- vfta &= ~targetbit;
- vfta_changed = true;
- }
- }
+ regidx = vlan / 32;
+ vfta_delta = 1 << (vlan % 32);
+ vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(regidx));
+
+ /* vfta_delta represents the difference between the current value
+ * of vfta and the value we want in the register. Since the diff
+ * is an XOR mask we can just update vfta using an XOR.
+ */
+ vfta_delta &= vlan_on ? ~vfta : vfta;
+ vfta ^= vfta_delta;
/* Part 2
* If VT Mode is set
@@ -3086,85 +3090,67 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
* Or !vlan_on
* clear the pool bit and possibly the vind
*/
- vt = IXGBE_READ_REG(hw, IXGBE_VT_CTL);
- if (vt & IXGBE_VT_CTL_VT_ENABLE) {
- s32 vlvf_index;
-
- vlvf_index = ixgbe_find_vlvf_slot(hw, vlan);
- if (vlvf_index < 0)
- return vlvf_index;
-
- if (vlan_on) {
- /* set the pool bit */
- if (vind < 32) {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB(vlvf_index*2));
- bits |= (1 << vind);
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB(vlvf_index*2),
- bits);
- } else {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1));
- bits |= (1 << (vind-32));
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1),
- bits);
- }
- } else {
- /* clear the pool bit */
- if (vind < 32) {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB(vlvf_index*2));
- bits &= ~(1 << vind);
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB(vlvf_index*2),
- bits);
- bits |= IXGBE_READ_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1));
- } else {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1));
- bits &= ~(1 << (vind-32));
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1),
- bits);
- bits |= IXGBE_READ_REG(hw,
- IXGBE_VLVFB(vlvf_index*2));
- }
- }
+ if (!(IXGBE_READ_REG(hw, IXGBE_VT_CTL) & IXGBE_VT_CTL_VT_ENABLE))
+ goto vfta_update;
+
+ vlvf_index = ixgbe_find_vlvf_slot(hw, vlan, vlvf_bypass);
+ if (vlvf_index < 0) {
+ if (vlvf_bypass)
+ goto vfta_update;
+ return vlvf_index;
+ }
- /*
- * If there are still bits set in the VLVFB registers
- * for the VLAN ID indicated we need to see if the
- * caller is requesting that we clear the VFTA entry bit.
- * If the caller has requested that we clear the VFTA
- * entry bit but there are still pools/VFs using this VLAN
- * ID entry then ignore the request. We're not worried
- * about the case where we're turning the VFTA VLAN ID
- * entry bit on, only when requested to turn it off as
- * there may be multiple pools and/or VFs using the
- * VLAN ID entry. In that case we cannot clear the
- * VFTA bit until all pools/VFs using that VLAN ID have also
- * been cleared. This will be indicated by "bits" being
- * zero.
+ bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32));
+
+ /* set the pool bit */
+ bits |= 1 << (vind % 32);
+ if (vlan_on)
+ goto vlvf_update;
+
+ /* clear the pool bit */
+ bits ^= 1 << (vind % 32);
+
+ if (!bits &&
+ !IXGBE_READ_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + 1 - vind / 32))) {
+ /* Clear VFTA first, then disable VLVF. Otherwise
+ * we run the risk of stray packets leaking into
+ * the PF via the default pool
*/
- if (bits) {
- IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index),
- (IXGBE_VLVF_VIEN | vlan));
- if (!vlan_on) {
- /* someone wants to clear the vfta entry
- * but some pools/VFs are still using it.
- * Ignore it. */
- vfta_changed = false;
- }
- } else {
- IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0);
- }
+ if (vfta_delta)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta);
+
+ /* disable VLVF and clear remaining bit from pool */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32), 0);
+
+ return 0;
}
- if (vfta_changed)
- IXGBE_WRITE_REG(hw, IXGBE_VFTA(regindex), vfta);
+ /* If there are still bits set in the VLVFB registers
+ * for the VLAN ID indicated we need to see if the
+ * caller is requesting that we clear the VFTA entry bit.
+ * If the caller has requested that we clear the VFTA
+ * entry bit but there are still pools/VFs using this VLAN
+ * ID entry then ignore the request. We're not worried
+ * about the case where we're turning the VFTA VLAN ID
+ * entry bit on, only when requested to turn it off as
+ * there may be multiple pools and/or VFs using the
+ * VLAN ID entry. In that case we cannot clear the
+ * VFTA bit until all pools/VFs using that VLAN ID have also
+ * been cleared. This will be indicated by "bits" being
+ * zero.
+ */
+ vfta_delta = 0;
+
+vlvf_update:
+ /* record pool change and enable VLAN ID if not already enabled */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32), bits);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), IXGBE_VLVF_VIEN | vlan);
+
+vfta_update:
+ /* Update VFTA now that we are ready for traffic */
+ if (vfta_delta)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta);
return 0;
}
@@ -3184,8 +3170,8 @@ s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw)
for (offset = 0; offset < IXGBE_VLVF_ENTRIES; offset++) {
IXGBE_WRITE_REG(hw, IXGBE_VLVF(offset), 0);
- IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset*2), 0);
- IXGBE_WRITE_REG(hw, IXGBE_VLVFB((offset*2)+1), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset * 2), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset * 2 + 1), 0);
}
return 0;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
index a0044e4a8b90..2b9563137fd8 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
@@ -92,7 +92,7 @@ s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq);
s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq);
s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw);
s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan,
- u32 vind, bool vlan_on);
+ u32 vind, bool vlan_on, bool vlvf_bypass);
s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw);
s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw,
ixgbe_link_speed *speed,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
index a507a6fe3624..02c7333a9c83 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
@@ -139,6 +139,11 @@ s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw,
/* Calculate credit refill ratio using multiplier */
credit_refill = min(link_percentage * min_multiplier,
MAX_CREDIT_REFILL);
+
+ /* Refill at least minimum credit */
+ if (credit_refill < min_credit)
+ credit_refill = min_credit;
+
p->data_credits_refill = (u16)credit_refill;
/* Calculate maximum credit for the TC */
@@ -149,7 +154,7 @@ s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw,
* of a TC is too small, the maximum credit may not be
* enough to send out a jumbo frame in data plane arbitration.
*/
- if (credit_max && (credit_max < min_credit))
+ if (credit_max < min_credit)
credit_max = min_credit;
if (direction == DCB_TX_CONFIG) {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
index 23277ab153b6..b5cc989a3d23 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
@@ -223,13 +223,13 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
reg |= IXGBE_MFLCN_DPF;
/*
- * X540 supports per TC Rx priority flow control. So
- * clear all TCs and only enable those that should be
+ * X540 & X550 supports per TC Rx priority flow control.
+ * So clear all TCs and only enable those that should be
* enabled.
*/
reg &= ~(IXGBE_MFLCN_RPFCE_MASK | IXGBE_MFLCN_RFCE);
- if (hw->mac.type == ixgbe_mac_X540)
+ if (hw->mac.type >= ixgbe_mac_X540)
reg |= pfc_en << IXGBE_MFLCN_RPFCE_SHIFT;
if (pfc_en)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index d681273bd39d..bea96b3bc90c 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -151,47 +151,70 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = {
};
#define IXGBE_TEST_LEN sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN
+/* currently supported speeds for 10G */
+#define ADVRTSD_MSK_10G (SUPPORTED_10000baseT_Full | \
+ SUPPORTED_10000baseKX4_Full | \
+ SUPPORTED_10000baseKR_Full)
+
+#define ixgbe_isbackplane(type) ((type) == ixgbe_media_type_backplane)
+
+static u32 ixgbe_get_supported_10gtypes(struct ixgbe_hw *hw)
+{
+ if (!ixgbe_isbackplane(hw->phy.media_type))
+ return SUPPORTED_10000baseT_Full;
+
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_82598:
+ case IXGBE_DEV_ID_82599_KX4:
+ case IXGBE_DEV_ID_82599_KX4_MEZZ:
+ case IXGBE_DEV_ID_X550EM_X_KX4:
+ return SUPPORTED_10000baseKX4_Full;
+ case IXGBE_DEV_ID_82598_BX:
+ case IXGBE_DEV_ID_82599_KR:
+ case IXGBE_DEV_ID_X550EM_X_KR:
+ return SUPPORTED_10000baseKR_Full;
+ default:
+ return SUPPORTED_10000baseKX4_Full |
+ SUPPORTED_10000baseKR_Full;
+ }
+}
+
static int ixgbe_get_settings(struct net_device *netdev,
struct ethtool_cmd *ecmd)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
ixgbe_link_speed supported_link;
- u32 link_speed = 0;
bool autoneg = false;
- bool link_up;
hw->mac.ops.get_link_capabilities(hw, &supported_link, &autoneg);
/* set the supported link speeds */
if (supported_link & IXGBE_LINK_SPEED_10GB_FULL)
- ecmd->supported |= SUPPORTED_10000baseT_Full;
- if (supported_link & IXGBE_LINK_SPEED_2_5GB_FULL)
- ecmd->supported |= SUPPORTED_2500baseX_Full;
+ ecmd->supported |= ixgbe_get_supported_10gtypes(hw);
if (supported_link & IXGBE_LINK_SPEED_1GB_FULL)
ecmd->supported |= SUPPORTED_1000baseT_Full;
if (supported_link & IXGBE_LINK_SPEED_100_FULL)
- ecmd->supported |= SUPPORTED_100baseT_Full;
+ ecmd->supported |= ixgbe_isbackplane(hw->phy.media_type) ?
+ SUPPORTED_1000baseKX_Full :
+ SUPPORTED_1000baseT_Full;
+ /* default advertised speed if phy.autoneg_advertised isn't set */
+ ecmd->advertising = ecmd->supported;
/* set the advertised speeds */
if (hw->phy.autoneg_advertised) {
+ ecmd->advertising = 0;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
ecmd->advertising |= ADVERTISED_100baseT_Full;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
- ecmd->advertising |= ADVERTISED_10000baseT_Full;
- if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_2_5GB_FULL)
- ecmd->advertising |= ADVERTISED_2500baseX_Full;
- if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL)
- ecmd->advertising |= ADVERTISED_1000baseT_Full;
+ ecmd->advertising |= ecmd->supported & ADVRTSD_MSK_10G;
+ if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) {
+ if (ecmd->supported & SUPPORTED_1000baseKX_Full)
+ ecmd->advertising |= ADVERTISED_1000baseKX_Full;
+ else
+ ecmd->advertising |= ADVERTISED_1000baseT_Full;
+ }
} else {
- /* default modes in case phy.autoneg_advertised isn't set */
- if (supported_link & IXGBE_LINK_SPEED_10GB_FULL)
- ecmd->advertising |= ADVERTISED_10000baseT_Full;
- if (supported_link & IXGBE_LINK_SPEED_1GB_FULL)
- ecmd->advertising |= ADVERTISED_1000baseT_Full;
- if (supported_link & IXGBE_LINK_SPEED_100_FULL)
- ecmd->advertising |= ADVERTISED_100baseT_Full;
-
if (hw->phy.multispeed_fiber && !autoneg) {
if (supported_link & IXGBE_LINK_SPEED_10GB_FULL)
ecmd->advertising = ADVERTISED_10000baseT_Full;
@@ -229,6 +252,10 @@ static int ixgbe_get_settings(struct net_device *netdev,
case ixgbe_phy_sfp_avago:
case ixgbe_phy_sfp_intel:
case ixgbe_phy_sfp_unknown:
+ case ixgbe_phy_qsfp_passive_unknown:
+ case ixgbe_phy_qsfp_active_unknown:
+ case ixgbe_phy_qsfp_intel:
+ case ixgbe_phy_qsfp_unknown:
/* SFP+ devices, further checking needed */
switch (adapter->hw.phy.sfp_type) {
case ixgbe_sfp_type_da_cu:
@@ -284,9 +311,8 @@ static int ixgbe_get_settings(struct net_device *netdev,
break;
}
- hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
- if (link_up) {
- switch (link_speed) {
+ if (netif_carrier_ok(netdev)) {
+ switch (adapter->link_speed) {
case IXGBE_LINK_SPEED_10GB_FULL:
ethtool_cmd_speed_set(ecmd, SPEED_10000);
break;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
index 631c603fc966..2a653ec954f5 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
@@ -77,7 +77,7 @@ int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid)
if (!netdev)
return 0;
- if (xid >= IXGBE_FCOE_DDP_MAX)
+ if (xid >= netdev->fcoe_ddp_xid)
return 0;
adapter = netdev_priv(netdev);
@@ -177,7 +177,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
return 0;
adapter = netdev_priv(netdev);
- if (xid >= IXGBE_FCOE_DDP_MAX) {
+ if (xid >= netdev->fcoe_ddp_xid) {
e_warn(drv, "xid=0x%x out-of-range\n", xid);
return 0;
}
@@ -517,6 +517,7 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring,
u32 vlan_macip_lens;
u32 fcoe_sof_eof = 0;
u32 mss_l4len_idx;
+ u32 type_tucmd = IXGBE_ADVTXT_TUCMD_FCOE;
u8 sof, eof;
if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type != SKB_GSO_FCOE)) {
@@ -593,6 +594,8 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring,
skb_shinfo(skb)->gso_size);
first->bytecount += (first->gso_segs - 1) * *hdr_len;
first->tx_flags |= IXGBE_TX_FLAGS_TSO;
+ /* Hardware expects L4T to be RSV for FCoE TSO */
+ type_tucmd |= IXGBE_ADVTXD_TUCMD_L4T_RSV;
}
/* set flag indicating FCOE to ixgbe_tx_map call */
@@ -610,7 +613,7 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring,
/* write context desc */
ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, fcoe_sof_eof,
- IXGBE_ADVTXT_TUCMD_FCOE, mss_l4len_idx);
+ type_tucmd, mss_l4len_idx);
return 0;
}
@@ -620,8 +623,7 @@ static void ixgbe_fcoe_dma_pool_free(struct ixgbe_fcoe *fcoe, unsigned int cpu)
struct ixgbe_fcoe_ddp_pool *ddp_pool;
ddp_pool = per_cpu_ptr(fcoe->ddp_pool, cpu);
- if (ddp_pool->pool)
- dma_pool_destroy(ddp_pool->pool);
+ dma_pool_destroy(ddp_pool->pool);
ddp_pool->pool = NULL;
}
@@ -997,8 +999,7 @@ int ixgbe_fcoe_get_hbainfo(struct net_device *netdev,
return -EINVAL;
/* Don't return information on unsupported devices */
- if (hw->mac.type != ixgbe_mac_82599EB &&
- hw->mac.type != ixgbe_mac_X540)
+ if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))
return -EINVAL;
/* Manufacturer */
@@ -1044,6 +1045,10 @@ int ixgbe_fcoe_get_hbainfo(struct net_device *netdev,
snprintf(info->model,
sizeof(info->model),
"Intel 82599");
+ } else if (hw->mac.type == ixgbe_mac_X550) {
+ snprintf(info->model,
+ sizeof(info->model),
+ "Intel X550");
} else {
snprintf(info->model,
sizeof(info->model),
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
index f3168bcc7d87..e771e764daa3 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
@@ -844,7 +844,6 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
/* initialize NAPI */
netif_napi_add(adapter->netdev, &q_vector->napi,
ixgbe_poll, 64);
- napi_hash_add(&q_vector->napi);
#ifdef CONFIG_NET_RX_BUSY_POLL
/* initialize busy poll */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index aed8d029b23d..c4003a88bbf6 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -65,9 +65,6 @@
#include "ixgbe_common.h"
#include "ixgbe_dcb_82599.h"
#include "ixgbe_sriov.h"
-#ifdef CONFIG_IXGBE_VXLAN
-#include <net/vxlan.h>
-#endif
char ixgbe_driver_name[] = "ixgbe";
static const char ixgbe_driver_string[] =
@@ -175,6 +172,8 @@ MODULE_DESCRIPTION("Intel(R) 10 Gigabit PCI Express Network Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
+static struct workqueue_struct *ixgbe_wq;
+
static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev);
static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
@@ -316,7 +315,7 @@ static void ixgbe_service_event_schedule(struct ixgbe_adapter *adapter)
if (!test_bit(__IXGBE_DOWN, &adapter->state) &&
!test_bit(__IXGBE_REMOVING, &adapter->state) &&
!test_and_set_bit(__IXGBE_SERVICE_SCHED, &adapter->state))
- schedule_work(&adapter->service_task);
+ queue_work(ixgbe_wq, &adapter->service_task);
}
static void ixgbe_remove_adapter(struct ixgbe_hw *hw)
@@ -1484,7 +1483,7 @@ static inline void ixgbe_rx_checksum(struct ixgbe_ring *ring,
return;
if (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_OUTERIPER)) {
- ring->rx_stats.csum_err++;
+ skb->ip_summed = CHECKSUM_NONE;
return;
}
/* If we checked the outer header let the stack know */
@@ -1635,6 +1634,7 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
struct sk_buff *skb)
{
struct net_device *dev = rx_ring->netdev;
+ u32 flags = rx_ring->q_vector->adapter->flags;
ixgbe_update_rsc_stats(rx_ring, skb);
@@ -1642,8 +1642,8 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
ixgbe_rx_checksum(rx_ring, rx_desc, skb);
- if (unlikely(ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS)))
- ixgbe_ptp_rx_hwtstamp(rx_ring->q_vector->adapter, skb);
+ if (unlikely(flags & IXGBE_FLAG_RX_HWTSTAMP_ENABLED))
+ ixgbe_ptp_rx_hwtstamp(rx_ring, rx_desc, skb);
if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_VP)) {
@@ -1659,6 +1659,7 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector,
struct sk_buff *skb)
{
+ skb_mark_napi_id(skb, &q_vector->napi);
if (ixgbe_qv_busy_polling(q_vector))
netif_receive_skb(skb);
else
@@ -2123,7 +2124,6 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
}
#endif /* IXGBE_FCOE */
- skb_mark_napi_id(skb, &q_vector->napi);
ixgbe_rx_skb(q_vector, skb);
/* update budget accounting */
@@ -2741,7 +2741,7 @@ static irqreturn_t ixgbe_msix_other(int irq, void *data)
ixgbe_check_fan_failure(adapter, eicr);
if (unlikely(eicr & IXGBE_EICR_TIMESYNC))
- ixgbe_ptp_check_pps_event(adapter, eicr);
+ ixgbe_ptp_check_pps_event(adapter);
/* re-enable the original interrupt state, no lsc, no queues */
if (!test_bit(__IXGBE_DOWN, &adapter->state))
@@ -2757,7 +2757,7 @@ static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
/* EIAM disabled interrupts (on this vector) for us */
if (q_vector->rx.ring || q_vector->tx.ring)
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -2786,7 +2786,8 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
ixgbe_for_each_ring(ring, q_vector->tx)
clean_complete &= !!ixgbe_clean_tx_irq(q_vector, ring);
- if (!ixgbe_qv_lock_napi(q_vector))
+ /* Exit if we are called by netpoll or busy polling is active */
+ if ((budget <= 0) || !ixgbe_qv_lock_napi(q_vector))
return budget;
/* attempt to distribute budget to each queue fairly, but don't allow
@@ -2947,10 +2948,10 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
ixgbe_check_fan_failure(adapter, eicr);
if (unlikely(eicr & IXGBE_EICR_TIMESYNC))
- ixgbe_ptp_check_pps_event(adapter, eicr);
+ ixgbe_ptp_check_pps_event(adapter);
/* would disable interrupts here but EIAM disabled it */
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
/*
* re-enable link(maybe) and non-queue interrupts, no flush.
@@ -3315,8 +3316,7 @@ static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter,
}
/**
- * Return a number of entries in the RSS indirection table
- *
+ * ixgbe_rss_indir_tbl_entries - Return RSS indirection table entries
* @adapter: device handle
*
* - 82598/82599/X540: 128
@@ -3334,8 +3334,7 @@ u32 ixgbe_rss_indir_tbl_entries(struct ixgbe_adapter *adapter)
}
/**
- * Write the RETA table to HW
- *
+ * ixgbe_store_reta - Write the RETA table to HW
* @adapter: device handle
*
* Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW.
@@ -3374,8 +3373,7 @@ void ixgbe_store_reta(struct ixgbe_adapter *adapter)
}
/**
- * Write the RETA table to HW (for x550 devices in SRIOV mode)
- *
+ * ixgbe_store_vfreta - Write the RETA table to HW (x550 devices in SRIOV mode)
* @adapter: device handle
*
* Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW.
@@ -3621,6 +3619,9 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter,
IXGBE_WRITE_REG(hw, IXGBE_RDBAH(reg_idx), (rdba >> 32));
IXGBE_WRITE_REG(hw, IXGBE_RDLEN(reg_idx),
ring->count * sizeof(union ixgbe_adv_rx_desc));
+ /* Force flushing of IXGBE_RDLEN to prevent MDD */
+ IXGBE_WRITE_FLUSH(hw);
+
IXGBE_WRITE_REG(hw, IXGBE_RDH(reg_idx), 0);
IXGBE_WRITE_REG(hw, IXGBE_RDT(reg_idx), 0);
ring->tail = adapter->io_addr + IXGBE_RDT(reg_idx);
@@ -3704,6 +3705,9 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
/* Map PF MAC address in RAR Entry 0 to first pool following VFs */
hw->mac.ops.set_vmdq(hw, 0, VMDQ_P(0));
+ /* clear VLAN promisc flag so VFTA will be updated if necessary */
+ adapter->flags2 &= ~IXGBE_FLAG2_VLAN_PROMISC;
+
/*
* Set up VF register offsets for selected VT Mode,
* i.e. 32 or 64 VFs for SR-IOV
@@ -3901,12 +3905,56 @@ static int ixgbe_vlan_rx_add_vid(struct net_device *netdev,
struct ixgbe_hw *hw = &adapter->hw;
/* add VID to filter table */
- hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), true);
+ hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), true, true);
set_bit(vid, adapter->active_vlans);
return 0;
}
+static int ixgbe_find_vlvf_entry(struct ixgbe_hw *hw, u32 vlan)
+{
+ u32 vlvf;
+ int idx;
+
+ /* short cut the special case */
+ if (vlan == 0)
+ return 0;
+
+ /* Search for the vlan id in the VLVF entries */
+ for (idx = IXGBE_VLVF_ENTRIES; --idx;) {
+ vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(idx));
+ if ((vlvf & VLAN_VID_MASK) == vlan)
+ break;
+ }
+
+ return idx;
+}
+
+void ixgbe_update_pf_promisc_vlvf(struct ixgbe_adapter *adapter, u32 vid)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 bits, word;
+ int idx;
+
+ idx = ixgbe_find_vlvf_entry(hw, vid);
+ if (!idx)
+ return;
+
+ /* See if any other pools are set for this VLAN filter
+ * entry other than the PF.
+ */
+ word = idx * 2 + (VMDQ_P(0) / 32);
+ bits = ~(1 << (VMDQ_P(0)) % 32);
+ bits &= IXGBE_READ_REG(hw, IXGBE_VLVFB(word));
+
+ /* Disable the filter so this falls into the default pool. */
+ if (!bits && !IXGBE_READ_REG(hw, IXGBE_VLVFB(word ^ 1))) {
+ if (!(adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC))
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(idx), 0);
+ }
+}
+
static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev,
__be16 proto, u16 vid)
{
@@ -3914,7 +3962,11 @@ static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev,
struct ixgbe_hw *hw = &adapter->hw;
/* remove VID from filter table */
- hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), false);
+ if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC)
+ ixgbe_update_pf_promisc_vlvf(adapter, vid);
+ else
+ hw->mac.ops.set_vfta(hw, vid, VMDQ_P(0), false, true);
+
clear_bit(vid, adapter->active_vlans);
return 0;
@@ -3992,6 +4044,129 @@ static void ixgbe_vlan_strip_enable(struct ixgbe_adapter *adapter)
}
}
+static void ixgbe_vlan_promisc_enable(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 vlnctrl, i;
+
+ switch (hw->mac.type) {
+ case ixgbe_mac_82599EB:
+ case ixgbe_mac_X540:
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ default:
+ if (adapter->flags & IXGBE_FLAG_VMDQ_ENABLED)
+ break;
+ /* fall through */
+ case ixgbe_mac_82598EB:
+ /* legacy case, we can just disable VLAN filtering */
+ vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+ vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
+ IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
+ return;
+ }
+
+ /* We are already in VLAN promisc, nothing to do */
+ if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC)
+ return;
+
+ /* Set flag so we don't redo unnecessary work */
+ adapter->flags2 |= IXGBE_FLAG2_VLAN_PROMISC;
+
+ /* Add PF to all active pools */
+ for (i = IXGBE_VLVF_ENTRIES; --i;) {
+ u32 reg_offset = IXGBE_VLVFB(i * 2 + VMDQ_P(0) / 32);
+ u32 vlvfb = IXGBE_READ_REG(hw, reg_offset);
+
+ vlvfb |= 1 << (VMDQ_P(0) % 32);
+ IXGBE_WRITE_REG(hw, reg_offset, vlvfb);
+ }
+
+ /* Set all bits in the VLAN filter table array */
+ for (i = hw->mac.vft_size; i--;)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), ~0U);
+}
+
+#define VFTA_BLOCK_SIZE 8
+static void ixgbe_scrub_vfta(struct ixgbe_adapter *adapter, u32 vfta_offset)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 vfta[VFTA_BLOCK_SIZE] = { 0 };
+ u32 vid_start = vfta_offset * 32;
+ u32 vid_end = vid_start + (VFTA_BLOCK_SIZE * 32);
+ u32 i, vid, word, bits;
+
+ for (i = IXGBE_VLVF_ENTRIES; --i;) {
+ u32 vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(i));
+
+ /* pull VLAN ID from VLVF */
+ vid = vlvf & VLAN_VID_MASK;
+
+ /* only concern outselves with a certain range */
+ if (vid < vid_start || vid >= vid_end)
+ continue;
+
+ if (vlvf) {
+ /* record VLAN ID in VFTA */
+ vfta[(vid - vid_start) / 32] |= 1 << (vid % 32);
+
+ /* if PF is part of this then continue */
+ if (test_bit(vid, adapter->active_vlans))
+ continue;
+ }
+
+ /* remove PF from the pool */
+ word = i * 2 + VMDQ_P(0) / 32;
+ bits = ~(1 << (VMDQ_P(0) % 32));
+ bits &= IXGBE_READ_REG(hw, IXGBE_VLVFB(word));
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), bits);
+ }
+
+ /* extract values from active_vlans and write back to VFTA */
+ for (i = VFTA_BLOCK_SIZE; i--;) {
+ vid = (vfta_offset + i) * 32;
+ word = vid / BITS_PER_LONG;
+ bits = vid % BITS_PER_LONG;
+
+ vfta[i] |= adapter->active_vlans[word] >> bits;
+
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(vfta_offset + i), vfta[i]);
+ }
+}
+
+static void ixgbe_vlan_promisc_disable(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 vlnctrl, i;
+
+ switch (hw->mac.type) {
+ case ixgbe_mac_82599EB:
+ case ixgbe_mac_X540:
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ default:
+ if (adapter->flags & IXGBE_FLAG_VMDQ_ENABLED)
+ break;
+ /* fall through */
+ case ixgbe_mac_82598EB:
+ vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+ vlnctrl &= ~IXGBE_VLNCTRL_CFIEN;
+ vlnctrl |= IXGBE_VLNCTRL_VFE;
+ IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
+ return;
+ }
+
+ /* We are not in VLAN promisc, nothing to do */
+ if (!(adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC))
+ return;
+
+ /* Set flag so we don't redo unnecessary work */
+ adapter->flags2 &= ~IXGBE_FLAG2_VLAN_PROMISC;
+
+ for (i = 0; i < hw->mac.vft_size; i += VFTA_BLOCK_SIZE)
+ ixgbe_scrub_vfta(adapter, i);
+}
+
static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
{
u16 vid;
@@ -4034,124 +4209,156 @@ static int ixgbe_write_mc_addr_list(struct net_device *netdev)
#ifdef CONFIG_PCI_IOV
void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
- hw->mac.ops.set_rar(hw, i, adapter->mac_table[i].addr,
- adapter->mac_table[i].queue,
+
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED;
+
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
+ hw->mac.ops.set_rar(hw, i,
+ mac_table->addr,
+ mac_table->pool,
IXGBE_RAH_AV);
else
hw->mac.ops.clear_rar(hw, i);
-
- adapter->mac_table[i].state &= ~(IXGBE_MAC_STATE_MODIFIED);
}
}
-#endif
+#endif
static void ixgbe_sync_mac_table(struct ixgbe_adapter *adapter)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state & IXGBE_MAC_STATE_MODIFIED) {
- if (adapter->mac_table[i].state &
- IXGBE_MAC_STATE_IN_USE)
- hw->mac.ops.set_rar(hw, i,
- adapter->mac_table[i].addr,
- adapter->mac_table[i].queue,
- IXGBE_RAH_AV);
- else
- hw->mac.ops.clear_rar(hw, i);
- adapter->mac_table[i].state &=
- ~(IXGBE_MAC_STATE_MODIFIED);
- }
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ if (!(mac_table->state & IXGBE_MAC_STATE_MODIFIED))
+ continue;
+
+ mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED;
+
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
+ hw->mac.ops.set_rar(hw, i,
+ mac_table->addr,
+ mac_table->pool,
+ IXGBE_RAH_AV);
+ else
+ hw->mac.ops.clear_rar(hw, i);
}
}
static void ixgbe_flush_sw_mac_table(struct ixgbe_adapter *adapter)
{
- int i;
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
+ int i;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
- adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
- eth_zero_addr(adapter->mac_table[i].addr);
- adapter->mac_table[i].queue = 0;
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ mac_table->state |= IXGBE_MAC_STATE_MODIFIED;
+ mac_table->state &= ~IXGBE_MAC_STATE_IN_USE;
}
+
ixgbe_sync_mac_table(adapter);
}
-static int ixgbe_available_rars(struct ixgbe_adapter *adapter)
+static int ixgbe_available_rars(struct ixgbe_adapter *adapter, u16 pool)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i, count = 0;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state == 0)
- count++;
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ /* do not count default RAR as available */
+ if (mac_table->state & IXGBE_MAC_STATE_DEFAULT)
+ continue;
+
+ /* only count unused and addresses that belong to us */
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE) {
+ if (mac_table->pool != pool)
+ continue;
+ }
+
+ count++;
}
+
return count;
}
/* this function destroys the first RAR entry */
-static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter,
- u8 *addr)
+static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
- memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN);
- adapter->mac_table[0].queue = VMDQ_P(0);
- adapter->mac_table[0].state = (IXGBE_MAC_STATE_DEFAULT |
- IXGBE_MAC_STATE_IN_USE);
- hw->mac.ops.set_rar(hw, 0, adapter->mac_table[0].addr,
- adapter->mac_table[0].queue,
+ memcpy(&mac_table->addr, hw->mac.addr, ETH_ALEN);
+ mac_table->pool = VMDQ_P(0);
+
+ mac_table->state = IXGBE_MAC_STATE_DEFAULT | IXGBE_MAC_STATE_IN_USE;
+
+ hw->mac.ops.set_rar(hw, 0, mac_table->addr, mac_table->pool,
IXGBE_RAH_AV);
}
-int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
+ const u8 *addr, u16 pool)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i;
if (is_zero_ether_addr(addr))
return -EINVAL;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
continue;
- adapter->mac_table[i].state |= (IXGBE_MAC_STATE_MODIFIED |
- IXGBE_MAC_STATE_IN_USE);
- ether_addr_copy(adapter->mac_table[i].addr, addr);
- adapter->mac_table[i].queue = queue;
+
+ ether_addr_copy(mac_table->addr, addr);
+ mac_table->pool = pool;
+
+ mac_table->state |= IXGBE_MAC_STATE_MODIFIED |
+ IXGBE_MAC_STATE_IN_USE;
+
ixgbe_sync_mac_table(adapter);
+
return i;
}
+
return -ENOMEM;
}
-int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
+ const u8 *addr, u16 pool)
{
- /* search table for addr, if found, set to 0 and sync */
- int i;
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
+ int i;
if (is_zero_ether_addr(addr))
return -EINVAL;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (ether_addr_equal(addr, adapter->mac_table[i].addr) &&
- adapter->mac_table[i].queue == queue) {
- adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
- adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
- eth_zero_addr(adapter->mac_table[i].addr);
- adapter->mac_table[i].queue = 0;
- ixgbe_sync_mac_table(adapter);
- return 0;
- }
+ /* search table for addr, if found clear IN_USE flag and sync */
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ /* we can only delete an entry if it is in use */
+ if (!(mac_table->state & IXGBE_MAC_STATE_IN_USE))
+ continue;
+ /* we only care about entries that belong to the given pool */
+ if (mac_table->pool != pool)
+ continue;
+ /* we only care about a specific MAC address */
+ if (!ether_addr_equal(addr, mac_table->addr))
+ continue;
+
+ mac_table->state |= IXGBE_MAC_STATE_MODIFIED;
+ mac_table->state &= ~IXGBE_MAC_STATE_IN_USE;
+
+ ixgbe_sync_mac_table(adapter);
+
+ return 0;
}
+
return -ENOMEM;
}
/**
@@ -4169,7 +4376,7 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn)
int count = 0;
/* return ENOMEM indicating insufficient memory for addresses */
- if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter))
+ if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter, vfn))
return -ENOMEM;
if (!netdev_uc_empty(netdev)) {
@@ -4183,6 +4390,25 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn)
return count;
}
+static int ixgbe_uc_sync(struct net_device *netdev, const unsigned char *addr)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int ret;
+
+ ret = ixgbe_add_mac_filter(adapter, addr, VMDQ_P(0));
+
+ return min_t(int, ret, 0);
+}
+
+static int ixgbe_uc_unsync(struct net_device *netdev, const unsigned char *addr)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ ixgbe_del_mac_filter(adapter, addr, VMDQ_P(0));
+
+ return 0;
+}
+
/**
* ixgbe_set_rx_mode - Unicast, Multicast and Promiscuous mode set
* @netdev: network interface device structure
@@ -4197,12 +4423,10 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
u32 fctrl, vmolr = IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE;
- u32 vlnctrl;
int count;
/* Check for Promiscuous and All Multicast modes */
fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
- vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
/* set all bits that we expect to always be set */
fctrl &= ~IXGBE_FCTRL_SBP; /* disable store-bad-packets */
@@ -4212,25 +4436,18 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
/* clear the bits we are changing the status of */
fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
- vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
if (netdev->flags & IFF_PROMISC) {
hw->addr_ctrl.user_set_promisc = true;
fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
vmolr |= IXGBE_VMOLR_MPE;
- /* Only disable hardware filter vlans in promiscuous mode
- * if SR-IOV and VMDQ are disabled - otherwise ensure
- * that hardware VLAN filters remain enabled.
- */
- if (adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
- IXGBE_FLAG_SRIOV_ENABLED))
- vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
+ ixgbe_vlan_promisc_enable(adapter);
} else {
if (netdev->flags & IFF_ALLMULTI) {
fctrl |= IXGBE_FCTRL_MPE;
vmolr |= IXGBE_VMOLR_MPE;
}
- vlnctrl |= IXGBE_VLNCTRL_VFE;
hw->addr_ctrl.user_set_promisc = false;
+ ixgbe_vlan_promisc_disable(adapter);
}
/*
@@ -4238,8 +4455,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
* sufficient space to store all the addresses then enable
* unicast promiscuous mode
*/
- count = ixgbe_write_uc_addr_list(netdev, VMDQ_P(0));
- if (count < 0) {
+ if (__dev_uc_sync(netdev, ixgbe_uc_sync, ixgbe_uc_unsync)) {
fctrl |= IXGBE_FCTRL_UPE;
vmolr |= IXGBE_VMOLR_ROPE;
}
@@ -4275,7 +4491,6 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
/* NOTE: VLAN filtering is disabled by setting PROMISC */
}
- IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl);
if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -5042,7 +5257,6 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
int err;
- u8 old_addr[ETH_ALEN];
if (ixgbe_removed(hw->hw_addr))
return;
@@ -5078,10 +5292,13 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
}
clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state);
- /* do not flush user set addresses */
- memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
+
+ /* flush entries out of MAC table */
ixgbe_flush_sw_mac_table(adapter);
- ixgbe_mac_set_default_filter(adapter, old_addr);
+ __dev_uc_unsync(netdev, NULL);
+
+ /* do not flush user set addresses */
+ ixgbe_mac_set_default_filter(adapter);
/* update SAN MAC vmdq pool selection */
if (hw->mac.san_mac_rar_index)
@@ -5331,6 +5548,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
adapter->mac_table = kzalloc(sizeof(struct ixgbe_mac_addr) *
hw->mac.num_rar_entries,
GFP_ATOMIC);
+ if (!adapter->mac_table)
+ return -ENOMEM;
/* Set MAC specific capability flags and exceptions */
switch (hw->mac.type) {
@@ -6616,10 +6835,8 @@ static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
- struct pci_dev *vfdev;
+ unsigned int vf;
u32 gpc;
- int pos;
- unsigned short vf_id;
if (!(netif_carrier_ok(adapter->netdev)))
return;
@@ -6636,26 +6853,17 @@ static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter)
if (!pdev)
return;
- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
- if (!pos)
- return;
-
- /* get the device ID for the VF */
- pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id);
-
/* check status reg for all VFs owned by this PF */
- vfdev = pci_get_device(pdev->vendor, vf_id, NULL);
- while (vfdev) {
- if (vfdev->is_virtfn && (vfdev->physfn == pdev)) {
- u16 status_reg;
-
- pci_read_config_word(vfdev, PCI_STATUS, &status_reg);
- if (status_reg & PCI_STATUS_REC_MASTER_ABORT)
- /* issue VFLR */
- ixgbe_issue_vf_flr(adapter, vfdev);
- }
+ for (vf = 0; vf < adapter->num_vfs; ++vf) {
+ struct pci_dev *vfdev = adapter->vfinfo[vf].vfdev;
+ u16 status_reg;
- vfdev = pci_get_device(pdev->vendor, vf_id, vfdev);
+ if (!vfdev)
+ continue;
+ pci_read_config_word(vfdev, PCI_STATUS, &status_reg);
+ if (status_reg != IXGBE_FAILED_READ_CFG_WORD &&
+ status_reg & PCI_STATUS_REC_MASTER_ABORT)
+ ixgbe_issue_vf_flr(adapter, vfdev);
}
}
@@ -7024,6 +7232,7 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
struct tcphdr *tcphdr;
u8 *raw;
} transport_hdr;
+ __be16 frag_off;
if (skb->encapsulation) {
network_hdr.raw = skb_inner_network_header(skb);
@@ -7047,13 +7256,17 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
case 6:
vlan_macip_lens |= transport_hdr.raw - network_hdr.raw;
l4_hdr = network_hdr.ipv6->nexthdr;
+ if (likely((transport_hdr.raw - network_hdr.raw) ==
+ sizeof(struct ipv6hdr)))
+ break;
+ ipv6_skip_exthdr(skb, network_hdr.raw - skb->data +
+ sizeof(struct ipv6hdr),
+ &l4_hdr, &frag_off);
+ if (unlikely(frag_off))
+ l4_hdr = NEXTHDR_FRAGMENT;
break;
default:
- if (unlikely(net_ratelimit())) {
- dev_warn(tx_ring->dev,
- "partial checksum but version=%d\n",
- network_hdr.ipv4->version);
- }
+ break;
}
switch (l4_hdr) {
@@ -7074,16 +7287,18 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
default:
if (unlikely(net_ratelimit())) {
dev_warn(tx_ring->dev,
- "partial checksum but l4 proto=%x!\n",
- l4_hdr);
+ "partial checksum, version=%d, l4 proto=%x\n",
+ network_hdr.ipv4->version, l4_hdr);
}
- break;
+ skb_checksum_help(skb);
+ goto no_csum;
}
/* update TX checksum flag */
first->tx_flags |= IXGBE_TX_FLAGS_CSUM;
}
+no_csum:
/* vlan_macip_lens: MACLEN, VLAN tag */
vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
@@ -7358,7 +7573,9 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
/* snag network header to get L4 type and address */
skb = first->skb;
hdr.network = skb_network_header(skb);
- if (skb->encapsulation) {
+ if (!skb->encapsulation) {
+ th = tcp_hdr(skb);
+ } else {
#ifdef CONFIG_IXGBE_VXLAN
struct ixgbe_adapter *adapter = q_vector->adapter;
@@ -7377,14 +7594,34 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
#else
return;
#endif /* CONFIG_IXGBE_VXLAN */
- } else {
- /* Currently only IPv4/IPv6 with TCP is supported */
- if ((first->protocol != htons(ETH_P_IPV6) ||
- hdr.ipv6->nexthdr != IPPROTO_TCP) &&
- (first->protocol != htons(ETH_P_IP) ||
- hdr.ipv4->protocol != IPPROTO_TCP))
+ }
+
+ /* Currently only IPv4/IPv6 with TCP is supported */
+ switch (hdr.ipv4->version) {
+ case IPVERSION:
+ if (hdr.ipv4->protocol != IPPROTO_TCP)
return;
- th = tcp_hdr(skb);
+ break;
+ case 6:
+ if (likely((unsigned char *)th - hdr.network ==
+ sizeof(struct ipv6hdr))) {
+ if (hdr.ipv6->nexthdr != IPPROTO_TCP)
+ return;
+ } else {
+ __be16 frag_off;
+ u8 l4_hdr;
+
+ ipv6_skip_exthdr(skb, hdr.network - skb->data +
+ sizeof(struct ipv6hdr),
+ &l4_hdr, &frag_off);
+ if (unlikely(frag_off))
+ return;
+ if (l4_hdr != IPPROTO_TCP)
+ return;
+ }
+ break;
+ default:
+ return;
}
/* skip this packet since it is invalid or the socket is closing */
@@ -7419,10 +7656,12 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
common.port.src ^= th->dest ^ first->protocol;
common.port.dst ^= th->source;
- if (first->protocol == htons(ETH_P_IP)) {
+ switch (hdr.ipv4->version) {
+ case IPVERSION:
input.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr;
- } else {
+ break;
+ case 6:
input.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV6;
common.ip ^= hdr.ipv6->saddr.s6_addr32[0] ^
hdr.ipv6->saddr.s6_addr32[1] ^
@@ -7432,6 +7671,9 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
hdr.ipv6->daddr.s6_addr32[1] ^
hdr.ipv6->daddr.s6_addr32[2] ^
hdr.ipv6->daddr.s6_addr32[3];
+ break;
+ default:
+ break;
}
#ifdef CONFIG_IXGBE_VXLAN
@@ -7659,17 +7901,16 @@ static int ixgbe_set_mac(struct net_device *netdev, void *p)
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
struct sockaddr *addr = p;
- int ret;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- ixgbe_del_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
- ret = ixgbe_add_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
- return ret > 0 ? 0 : ret;
+ ixgbe_mac_set_default_filter(adapter);
+
+ return 0;
}
static int
@@ -8155,7 +8396,10 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
{
/* guarantee we can provide a unique filter for the unicast address */
if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) {
- if (IXGBE_MAX_PF_MACVLANS <= netdev_uc_count(dev))
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+ u16 pool = VMDQ_P(0);
+
+ if (netdev_uc_count(dev) >= ixgbe_available_rars(adapter, pool))
return -ENOMEM;
}
@@ -8387,7 +8631,7 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev,
if (unlikely(skb_inner_mac_header(skb) - skb_transport_header(skb) >
IXGBE_MAX_TUNNEL_HDR_LEN))
- return features & ~NETIF_F_ALL_CSUM;
+ return features & ~NETIF_F_CSUM_MASK;
return features;
}
@@ -8784,8 +9028,8 @@ skip_sriov:
case ixgbe_mac_X540:
case ixgbe_mac_X550:
case ixgbe_mac_X550EM_x:
- netdev->features |= NETIF_F_SCTP_CSUM;
- netdev->hw_features |= NETIF_F_SCTP_CSUM |
+ netdev->features |= NETIF_F_SCTP_CRC;
+ netdev->hw_features |= NETIF_F_SCTP_CRC |
NETIF_F_NTUPLE;
break;
default:
@@ -8801,8 +9045,7 @@ skip_sriov:
netdev->vlan_features |= NETIF_F_IPV6_CSUM;
netdev->vlan_features |= NETIF_F_SG;
- netdev->hw_enc_features |= NETIF_F_SG | NETIF_F_IP_CSUM |
- NETIF_F_IPV6_CSUM;
+ netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->priv_flags |= IFF_SUPP_NOFCS;
@@ -8811,9 +9054,7 @@ skip_sriov:
switch (adapter->hw.mac.type) {
case ixgbe_mac_X550:
case ixgbe_mac_X550EM_x:
- netdev->hw_enc_features |= NETIF_F_RXCSUM |
- NETIF_F_IP_CSUM |
- NETIF_F_IPV6_CSUM;
+ netdev->hw_enc_features |= NETIF_F_RXCSUM;
break;
default:
break;
@@ -8873,7 +9114,7 @@ skip_sriov:
goto err_sw_init;
}
- ixgbe_mac_set_default_filter(adapter, hw->mac.perm_addr);
+ ixgbe_mac_set_default_filter(adapter);
setup_timer(&adapter->service_timer, &ixgbe_service_timer,
(unsigned long) adapter);
@@ -9328,6 +9569,12 @@ static int __init ixgbe_init_module(void)
pr_info("%s - version %s\n", ixgbe_driver_string, ixgbe_driver_version);
pr_info("%s\n", ixgbe_copyright);
+ ixgbe_wq = create_singlethread_workqueue(ixgbe_driver_name);
+ if (!ixgbe_wq) {
+ pr_err("%s: Failed to create workqueue\n", ixgbe_driver_name);
+ return -ENOMEM;
+ }
+
ixgbe_dbg_init();
ret = pci_register_driver(&ixgbe_driver);
@@ -9359,6 +9606,10 @@ static void __exit ixgbe_exit_module(void)
pci_unregister_driver(&ixgbe_driver);
ixgbe_dbg_exit();
+ if (ixgbe_wq) {
+ destroy_workqueue(ixgbe_wq);
+ ixgbe_wq = NULL;
+ }
}
#ifdef CONFIG_IXGBE_DCA
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index fb8673d63806..db0731e05401 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -2393,6 +2393,9 @@ s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on)
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
return 0;
+ if (!on && ixgbe_mng_present(hw))
+ return 0;
+
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
index e5ba04025e2b..ef1504d41890 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2015 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,
@@ -27,6 +27,7 @@
*******************************************************************************/
#include "ixgbe.h"
#include <linux/ptp_classify.h>
+#include <linux/clocksource.h>
/*
* The 82599 and the X540 do not have true 64bit nanosecond scale
@@ -93,7 +94,6 @@
#define IXGBE_INCVAL_SHIFT_82599 7
#define IXGBE_INCPER_SHIFT_82599 24
-#define IXGBE_MAX_TIMEADJ_VALUE 0x7FFFFFFFFFFFFFFFULL
#define IXGBE_OVERFLOW_PERIOD (HZ * 30)
#define IXGBE_PTP_TX_TIMEOUT (HZ * 15)
@@ -104,8 +104,68 @@
*/
#define IXGBE_PTP_PPS_HALF_SECOND 500000000ULL
+/* In contrast, the X550 controller has two registers, SYSTIMEH and SYSTIMEL
+ * which contain measurements of seconds and nanoseconds respectively. This
+ * matches the standard linux representation of time in the kernel. In addition,
+ * the X550 also has a SYSTIMER register which represents residue, or
+ * subnanosecond overflow adjustments. To control clock adjustment, the TIMINCA
+ * register is used, but it is unlike the X540 and 82599 devices. TIMINCA
+ * represents units of 2^-32 nanoseconds, and uses 31 bits for this, with the
+ * high bit representing whether the adjustent is positive or negative. Every
+ * clock cycle, the X550 will add 12.5 ns + TIMINCA which can result in a range
+ * of 12 to 13 nanoseconds adjustment. Unlike the 82599 and X540 devices, the
+ * X550's clock for purposes of SYSTIME generation is constant and not dependent
+ * on the link speed.
+ *
+ * SYSTIMEH SYSTIMEL SYSTIMER
+ * +--------------+ +--------------+ +-------------+
+ * X550 | 32 | | 32 | | 32 |
+ * *--------------+ +--------------+ +-------------+
+ * \____seconds___/ \_nanoseconds_/ \__2^-32 ns__/
+ *
+ * This results in a full 96 bits to represent the clock, with 32 bits for
+ * seconds, 32 bits for nanoseconds (largest value is 0d999999999 or just under
+ * 1 second) and an additional 32 bits to measure sub nanosecond adjustments for
+ * underflow of adjustments.
+ *
+ * The 32 bits of seconds for the X550 overflows every
+ * 2^32 / ( 365.25 * 24 * 60 * 60 ) = ~136 years.
+ *
+ * In order to adjust the clock frequency for the X550, the TIMINCA register is
+ * provided. This register represents a + or minus nearly 0.5 ns adjustment to
+ * the base frequency. It is measured in 2^-32 ns units, with the high bit being
+ * the sign bit. This register enables software to calculate frequency
+ * adjustments and apply them directly to the clock rate.
+ *
+ * The math for converting ppb into TIMINCA values is fairly straightforward.
+ * TIMINCA value = ( Base_Frequency * ppb ) / 1000000000ULL
+ *
+ * This assumes that ppb is never high enough to create a value bigger than
+ * TIMINCA's 31 bits can store. This is ensured by the stack. Calculating this
+ * value is also simple.
+ * Max ppb = ( Max Adjustment / Base Frequency ) / 1000000000ULL
+ *
+ * For the X550, the Max adjustment is +/- 0.5 ns, and the base frequency is
+ * 12.5 nanoseconds. This means that the Max ppb is 39999999
+ * Note: We subtract one in order to ensure no overflow, because the TIMINCA
+ * register can only hold slightly under 0.5 nanoseconds.
+ *
+ * Because TIMINCA is measured in 2^-32 ns units, we have to convert 12.5 ns
+ * into 2^-32 units, which is
+ *
+ * 12.5 * 2^32 = C80000000
+ *
+ * Some revisions of hardware have a faster base frequency than the registers
+ * were defined for. To fix this, we use a timecounter structure with the
+ * proper mult and shift to convert the cycles into nanoseconds of time.
+ */
+#define IXGBE_X550_BASE_PERIOD 0xC80000000ULL
+#define INCVALUE_MASK 0x7FFFFFFF
+#define ISGN 0x80000000
+#define MAX_TIMADJ 0x7FFFFFFF
+
/**
- * ixgbe_ptp_setup_sdp
+ * ixgbe_ptp_setup_sdp_x540
* @hw: the hardware private structure
*
* this function enables or disables the clock out feature on SDP0 for
@@ -116,83 +176,116 @@
* aligns the start of the PPS signal to that value. The shift is
* necessary because it can change based on the link speed.
*/
-static void ixgbe_ptp_setup_sdp(struct ixgbe_adapter *adapter)
+static void ixgbe_ptp_setup_sdp_x540(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
- int shift = adapter->cc.shift;
+ int shift = adapter->hw_cc.shift;
u32 esdp, tsauxc, clktiml, clktimh, trgttiml, trgttimh, rem;
u64 ns = 0, clock_edge = 0;
- if ((adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED) &&
- (hw->mac.type == ixgbe_mac_X540)) {
+ /* disable the pin first */
+ IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
+ IXGBE_WRITE_FLUSH(hw);
- /* disable the pin first */
- IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
- IXGBE_WRITE_FLUSH(hw);
+ if (!(adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED))
+ return;
- esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+ esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
- /*
- * enable the SDP0 pin as output, and connected to the
- * native function for Timesync (ClockOut)
- */
- esdp |= (IXGBE_ESDP_SDP0_DIR |
- IXGBE_ESDP_SDP0_NATIVE);
+ /* enable the SDP0 pin as output, and connected to the
+ * native function for Timesync (ClockOut)
+ */
+ esdp |= IXGBE_ESDP_SDP0_DIR |
+ IXGBE_ESDP_SDP0_NATIVE;
- /*
- * enable the Clock Out feature on SDP0, and allow
- * interrupts to occur when the pin changes
- */
- tsauxc = (IXGBE_TSAUXC_EN_CLK |
- IXGBE_TSAUXC_SYNCLK |
- IXGBE_TSAUXC_SDP0_INT);
+ /* enable the Clock Out feature on SDP0, and allow
+ * interrupts to occur when the pin changes
+ */
+ tsauxc = IXGBE_TSAUXC_EN_CLK |
+ IXGBE_TSAUXC_SYNCLK |
+ IXGBE_TSAUXC_SDP0_INT;
- /* clock period (or pulse length) */
- clktiml = (u32)(IXGBE_PTP_PPS_HALF_SECOND << shift);
- clktimh = (u32)((IXGBE_PTP_PPS_HALF_SECOND << shift) >> 32);
+ /* clock period (or pulse length) */
+ clktiml = (u32)(IXGBE_PTP_PPS_HALF_SECOND << shift);
+ clktimh = (u32)((IXGBE_PTP_PPS_HALF_SECOND << shift) >> 32);
- /*
- * Account for the cyclecounter wrap-around value by
- * using the converted ns value of the current time to
- * check for when the next aligned second would occur.
- */
- clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
- clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32;
- ns = timecounter_cyc2time(&adapter->tc, clock_edge);
+ /* Account for the cyclecounter wrap-around value by
+ * using the converted ns value of the current time to
+ * check for when the next aligned second would occur.
+ */
+ clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+ clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32;
+ ns = timecounter_cyc2time(&adapter->hw_tc, clock_edge);
- div_u64_rem(ns, IXGBE_PTP_PPS_HALF_SECOND, &rem);
- clock_edge += ((IXGBE_PTP_PPS_HALF_SECOND - (u64)rem) << shift);
+ div_u64_rem(ns, IXGBE_PTP_PPS_HALF_SECOND, &rem);
+ clock_edge += ((IXGBE_PTP_PPS_HALF_SECOND - (u64)rem) << shift);
- /* specify the initial clock start time */
- trgttiml = (u32)clock_edge;
- trgttimh = (u32)(clock_edge >> 32);
+ /* specify the initial clock start time */
+ trgttiml = (u32)clock_edge;
+ trgttimh = (u32)(clock_edge >> 32);
- IXGBE_WRITE_REG(hw, IXGBE_CLKTIML, clktiml);
- IXGBE_WRITE_REG(hw, IXGBE_CLKTIMH, clktimh);
- IXGBE_WRITE_REG(hw, IXGBE_TRGTTIML0, trgttiml);
- IXGBE_WRITE_REG(hw, IXGBE_TRGTTIMH0, trgttimh);
+ IXGBE_WRITE_REG(hw, IXGBE_CLKTIML, clktiml);
+ IXGBE_WRITE_REG(hw, IXGBE_CLKTIMH, clktimh);
+ IXGBE_WRITE_REG(hw, IXGBE_TRGTTIML0, trgttiml);
+ IXGBE_WRITE_REG(hw, IXGBE_TRGTTIMH0, trgttimh);
- IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
- IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
- } else {
- IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
- }
+ IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
+ IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
IXGBE_WRITE_FLUSH(hw);
}
/**
- * ixgbe_ptp_read - read raw cycle counter (to be used by time counter)
+ * ixgbe_ptp_read_X550 - read cycle counter value
+ * @hw_cc: cyclecounter structure
+ *
+ * This function reads SYSTIME registers. It is called by the cyclecounter
+ * structure to convert from internal representation into nanoseconds. We need
+ * this for X550 since some skews do not have expected clock frequency and
+ * result of SYSTIME is 32bits of "billions of cycles" and 32 bits of
+ * "cycles", rather than seconds and nanoseconds.
+ */
+static cycle_t ixgbe_ptp_read_X550(const struct cyclecounter *hw_cc)
+{
+ struct ixgbe_adapter *adapter =
+ container_of(hw_cc, struct ixgbe_adapter, hw_cc);
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct timespec64 ts;
+
+ /* storage is 32 bits of 'billions of cycles' and 32 bits of 'cycles'.
+ * Some revisions of hardware run at a higher frequency and so the
+ * cycles are not guaranteed to be nanoseconds. The timespec64 created
+ * here is used for its math/conversions but does not necessarily
+ * represent nominal time.
+ *
+ * It should be noted that this cyclecounter will overflow at a
+ * non-bitmask field since we have to convert our billions of cycles
+ * into an actual cycles count. This results in some possible weird
+ * situations at high cycle counter stamps. However given that 32 bits
+ * of "seconds" is ~138 years this isn't a problem. Even at the
+ * increased frequency of some revisions, this is still ~103 years.
+ * Since the SYSTIME values start at 0 and we never write them, it is
+ * highly unlikely for the cyclecounter to overflow in practice.
+ */
+ IXGBE_READ_REG(hw, IXGBE_SYSTIMR);
+ ts.tv_nsec = IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+ ts.tv_sec = IXGBE_READ_REG(hw, IXGBE_SYSTIMH);
+
+ return (u64)timespec64_to_ns(&ts);
+}
+
+/**
+ * ixgbe_ptp_read_82599 - read raw cycle counter (to be used by time counter)
* @cc: the cyclecounter structure
*
* this function reads the cyclecounter registers and is called by the
* cyclecounter structure used to construct a ns counter from the
* arbitrary fixed point registers
*/
-static cycle_t ixgbe_ptp_read(const struct cyclecounter *cc)
+static cycle_t ixgbe_ptp_read_82599(const struct cyclecounter *cc)
{
struct ixgbe_adapter *adapter =
- container_of(cc, struct ixgbe_adapter, cc);
+ container_of(cc, struct ixgbe_adapter, hw_cc);
struct ixgbe_hw *hw = &adapter->hw;
u64 stamp = 0;
@@ -203,20 +296,79 @@ static cycle_t ixgbe_ptp_read(const struct cyclecounter *cc)
}
/**
- * ixgbe_ptp_adjfreq
+ * ixgbe_ptp_convert_to_hwtstamp - convert register value to hw timestamp
+ * @adapter: private adapter structure
+ * @hwtstamp: stack timestamp structure
+ * @systim: unsigned 64bit system time value
+ *
+ * We need to convert the adapter's RX/TXSTMP registers into a hwtstamp value
+ * which can be used by the stack's ptp functions.
+ *
+ * The lock is used to protect consistency of the cyclecounter and the SYSTIME
+ * registers. However, it does not need to protect against the Rx or Tx
+ * timestamp registers, as there can't be a new timestamp until the old one is
+ * unlatched by reading.
+ *
+ * In addition to the timestamp in hardware, some controllers need a software
+ * overflow cyclecounter, and this function takes this into account as well.
+ **/
+static void ixgbe_ptp_convert_to_hwtstamp(struct ixgbe_adapter *adapter,
+ struct skb_shared_hwtstamps *hwtstamp,
+ u64 timestamp)
+{
+ unsigned long flags;
+ struct timespec64 systime;
+ u64 ns;
+
+ memset(hwtstamp, 0, sizeof(*hwtstamp));
+
+ switch (adapter->hw.mac.type) {
+ /* X550 and later hardware supposedly represent time using a seconds
+ * and nanoseconds counter, instead of raw 64bits nanoseconds. We need
+ * to convert the timestamp into cycles before it can be fed to the
+ * cyclecounter. We need an actual cyclecounter because some revisions
+ * of hardware run at a higher frequency and thus the counter does
+ * not represent seconds/nanoseconds. Instead it can be thought of as
+ * cycles and billions of cycles.
+ */
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ /* Upper 32 bits represent billions of cycles, lower 32 bits
+ * represent cycles. However, we use timespec64_to_ns for the
+ * correct math even though the units haven't been corrected
+ * yet.
+ */
+ systime.tv_sec = timestamp >> 32;
+ systime.tv_nsec = timestamp & 0xFFFFFFFF;
+
+ timestamp = timespec64_to_ns(&systime);
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&adapter->tmreg_lock, flags);
+ ns = timecounter_cyc2time(&adapter->hw_tc, timestamp);
+ spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
+
+ hwtstamp->hwtstamp = ns_to_ktime(ns);
+}
+
+/**
+ * ixgbe_ptp_adjfreq_82599
* @ptp: the ptp clock structure
* @ppb: parts per billion adjustment from base
*
* adjust the frequency of the ptp cycle counter by the
* indicated ppb from the base frequency.
*/
-static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+static int ixgbe_ptp_adjfreq_82599(struct ptp_clock_info *ptp, s32 ppb)
{
struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps);
struct ixgbe_hw *hw = &adapter->hw;
- u64 freq;
- u32 diff, incval;
+ u64 freq, incval;
+ u32 diff;
int neg_adj = 0;
if (ppb < 0) {
@@ -235,12 +387,16 @@ static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
switch (hw->mac.type) {
case ixgbe_mac_X540:
- IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+ if (incval > 0xFFFFFFFFULL)
+ e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n");
+ IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, (u32)incval);
break;
case ixgbe_mac_82599EB:
+ if (incval > 0x00FFFFFFULL)
+ e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n");
IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
(1 << IXGBE_INCPER_SHIFT_82599) |
- incval);
+ ((u32)incval & 0x00FFFFFFUL));
break;
default:
break;
@@ -250,6 +406,43 @@ static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
}
/**
+ * ixgbe_ptp_adjfreq_X550
+ * @ptp: the ptp clock structure
+ * @ppb: parts per billion adjustment from base
+ *
+ * adjust the frequency of the SYSTIME registers by the indicated ppb from base
+ * frequency
+ */
+static int ixgbe_ptp_adjfreq_X550(struct ptp_clock_info *ptp, s32 ppb)
+{
+ struct ixgbe_adapter *adapter =
+ container_of(ptp, struct ixgbe_adapter, ptp_caps);
+ struct ixgbe_hw *hw = &adapter->hw;
+ int neg_adj = 0;
+ u64 rate = IXGBE_X550_BASE_PERIOD;
+ u32 inca;
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ rate *= ppb;
+ rate = div_u64(rate, 1000000000ULL);
+
+ /* warn if rate is too large */
+ if (rate >= INCVALUE_MASK)
+ e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n");
+
+ inca = rate & INCVALUE_MASK;
+ if (neg_adj)
+ inca |= ISGN;
+
+ IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, inca);
+
+ return 0;
+}
+
+/**
* ixgbe_ptp_adjtime
* @ptp: the ptp clock structure
* @delta: offset to adjust the cycle counter by
@@ -263,10 +456,11 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
unsigned long flags;
spin_lock_irqsave(&adapter->tmreg_lock, flags);
- timecounter_adjtime(&adapter->tc, delta);
+ timecounter_adjtime(&adapter->hw_tc, delta);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
- ixgbe_ptp_setup_sdp(adapter);
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
return 0;
}
@@ -283,11 +477,11 @@ static int ixgbe_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps);
- u64 ns;
unsigned long flags;
+ u64 ns;
spin_lock_irqsave(&adapter->tmreg_lock, flags);
- ns = timecounter_read(&adapter->tc);
+ ns = timecounter_read(&adapter->hw_tc);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
*ts = ns_to_timespec64(ns);
@@ -308,17 +502,16 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp,
{
struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps);
- u64 ns;
unsigned long flags;
-
- ns = timespec64_to_ns(ts);
+ u64 ns = timespec64_to_ns(ts);
/* reset the timecounter */
spin_lock_irqsave(&adapter->tmreg_lock, flags);
- timecounter_init(&adapter->tc, &adapter->cc, ns);
+ timecounter_init(&adapter->hw_tc, &adapter->hw_cc, ns);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
- ixgbe_ptp_setup_sdp(adapter);
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
return 0;
}
@@ -343,33 +536,26 @@ static int ixgbe_ptp_feature_enable(struct ptp_clock_info *ptp,
* event when the clock SDP triggers. Clear mask when PPS is
* disabled
*/
- if (rq->type == PTP_CLK_REQ_PPS) {
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_X540:
- if (on)
- adapter->flags2 |= IXGBE_FLAG2_PTP_PPS_ENABLED;
- else
- adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
-
- ixgbe_ptp_setup_sdp(adapter);
- return 0;
- default:
- break;
- }
- }
+ if (rq->type != PTP_CLK_REQ_PPS || !adapter->ptp_setup_sdp)
+ return -ENOTSUPP;
+
+ if (on)
+ adapter->flags2 |= IXGBE_FLAG2_PTP_PPS_ENABLED;
+ else
+ adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
- return -ENOTSUPP;
+ adapter->ptp_setup_sdp(adapter);
+ return 0;
}
/**
* ixgbe_ptp_check_pps_event
* @adapter: the private adapter structure
- * @eicr: the interrupt cause register value
*
* This function is called by the interrupt routine when checking for
* interrupts. It will check and handle a pps event.
*/
-void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr)
+void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct ptp_clock_event event;
@@ -425,7 +611,9 @@ void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
u32 tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
+ struct ixgbe_ring *rx_ring;
unsigned long rx_event;
+ int n;
/* if we don't have a valid timestamp in the registers, just update the
* timeout counter and exit
@@ -437,19 +625,43 @@ void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter)
/* determine the most recent watchdog or rx_timestamp event */
rx_event = adapter->last_rx_ptp_check;
- if (time_after(adapter->last_rx_timestamp, rx_event))
- rx_event = adapter->last_rx_timestamp;
+ for (n = 0; n < adapter->num_rx_queues; n++) {
+ rx_ring = adapter->rx_ring[n];
+ if (time_after(rx_ring->last_rx_timestamp, rx_event))
+ rx_event = rx_ring->last_rx_timestamp;
+ }
/* only need to read the high RXSTMP register to clear the lock */
- if (time_is_before_jiffies(rx_event + 5*HZ)) {
+ if (time_is_before_jiffies(rx_event + 5 * HZ)) {
IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
adapter->last_rx_ptp_check = jiffies;
+ adapter->rx_hwtstamp_cleared++;
e_warn(drv, "clearing RX Timestamp hang\n");
}
}
/**
+ * ixgbe_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state
+ * @adapter: the private adapter structure
+ *
+ * This function should be called whenever the state related to a Tx timestamp
+ * needs to be cleared. This helps ensure that all related bits are reset for
+ * the next Tx timestamp event.
+ */
+static void ixgbe_ptp_clear_tx_timestamp(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+ if (adapter->ptp_tx_skb) {
+ dev_kfree_skb_any(adapter->ptp_tx_skb);
+ adapter->ptp_tx_skb = NULL;
+ }
+ clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
+}
+
+/**
* ixgbe_ptp_tx_hwtstamp - utility function which checks for TX time stamp
* @adapter: the private adapter struct
*
@@ -461,23 +673,15 @@ static void ixgbe_ptp_tx_hwtstamp(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct skb_shared_hwtstamps shhwtstamps;
- u64 regval = 0, ns;
- unsigned long flags;
+ u64 regval = 0;
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPH) << 32;
- spin_lock_irqsave(&adapter->tmreg_lock, flags);
- ns = timecounter_cyc2time(&adapter->tc, regval);
- spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
-
- memset(&shhwtstamps, 0, sizeof(shhwtstamps));
- shhwtstamps.hwtstamp = ns_to_ktime(ns);
+ ixgbe_ptp_convert_to_hwtstamp(adapter, &shhwtstamps, regval);
skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps);
- dev_kfree_skb_any(adapter->ptp_tx_skb);
- adapter->ptp_tx_skb = NULL;
- clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
+ ixgbe_ptp_clear_tx_timestamp(adapter);
}
/**
@@ -497,38 +701,85 @@ static void ixgbe_ptp_tx_hwtstamp_work(struct work_struct *work)
IXGBE_PTP_TX_TIMEOUT);
u32 tsynctxctl;
- if (timeout) {
- dev_kfree_skb_any(adapter->ptp_tx_skb);
- adapter->ptp_tx_skb = NULL;
- clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
- e_warn(drv, "clearing Tx Timestamp hang\n");
+ /* we have to have a valid skb to poll for a timestamp */
+ if (!adapter->ptp_tx_skb) {
+ ixgbe_ptp_clear_tx_timestamp(adapter);
return;
}
+ /* stop polling once we have a valid timestamp */
tsynctxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
- if (tsynctxctl & IXGBE_TSYNCTXCTL_VALID)
+ if (tsynctxctl & IXGBE_TSYNCTXCTL_VALID) {
ixgbe_ptp_tx_hwtstamp(adapter);
- else
+ return;
+ }
+
+ if (timeout) {
+ ixgbe_ptp_clear_tx_timestamp(adapter);
+ adapter->tx_hwtstamp_timeouts++;
+ e_warn(drv, "clearing Tx Timestamp hang\n");
+ } else {
/* reschedule to keep checking if it's not available yet */
schedule_work(&adapter->ptp_tx_work);
+ }
}
/**
- * ixgbe_ptp_rx_hwtstamp - utility function which checks for RX time stamp
- * @adapter: pointer to adapter struct
+ * ixgbe_ptp_rx_pktstamp - utility function to get RX time stamp from buffer
+ * @q_vector: structure containing interrupt and ring information
+ * @skb: the packet
+ *
+ * This function will be called by the Rx routine of the timestamp for this
+ * packet is stored in the buffer. The value is stored in little endian format
+ * starting at the end of the packet data.
+ */
+void ixgbe_ptp_rx_pktstamp(struct ixgbe_q_vector *q_vector,
+ struct sk_buff *skb)
+{
+ __le64 regval;
+
+ /* copy the bits out of the skb, and then trim the skb length */
+ skb_copy_bits(skb, skb->len - IXGBE_TS_HDR_LEN, &regval,
+ IXGBE_TS_HDR_LEN);
+ __pskb_trim(skb, skb->len - IXGBE_TS_HDR_LEN);
+
+ /* The timestamp is recorded in little endian format, and is stored at
+ * the end of the packet.
+ *
+ * DWORD: N N + 1 N + 2
+ * Field: End of Packet SYSTIMH SYSTIML
+ */
+ ixgbe_ptp_convert_to_hwtstamp(q_vector->adapter, skb_hwtstamps(skb),
+ le64_to_cpu(regval));
+}
+
+/**
+ * ixgbe_ptp_rx_rgtstamp - utility function which checks for RX time stamp
+ * @q_vector: structure containing interrupt and ring information
* @skb: particular skb to send timestamp with
*
* if the timestamp is valid, we convert it into the timecounter ns
* value, then store that result into the shhwtstamps structure which
* is passed up the network stack
*/
-void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb)
+void ixgbe_ptp_rx_rgtstamp(struct ixgbe_q_vector *q_vector,
+ struct sk_buff *skb)
{
- struct ixgbe_hw *hw = &adapter->hw;
- struct skb_shared_hwtstamps *shhwtstamps;
- u64 regval = 0, ns;
+ struct ixgbe_adapter *adapter;
+ struct ixgbe_hw *hw;
+ u64 regval = 0;
u32 tsyncrxctl;
- unsigned long flags;
+
+ /* we cannot process timestamps on a ring without a q_vector */
+ if (!q_vector || !q_vector->adapter)
+ return;
+
+ adapter = q_vector->adapter;
+ hw = &adapter->hw;
+
+ /* Read the tsyncrxctl register afterwards in order to prevent taking an
+ * I/O hit on every packet.
+ */
tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
if (!(tsyncrxctl & IXGBE_TSYNCRXCTL_VALID))
@@ -537,17 +788,7 @@ void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb)
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPH) << 32;
- spin_lock_irqsave(&adapter->tmreg_lock, flags);
- ns = timecounter_cyc2time(&adapter->tc, regval);
- spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
-
- shhwtstamps = skb_hwtstamps(skb);
- shhwtstamps->hwtstamp = ns_to_ktime(ns);
-
- /* Update the last_rx_timestamp timer in order to enable watchdog check
- * for error case of latched timestamp on a dropped packet.
- */
- adapter->last_rx_timestamp = jiffies;
+ ixgbe_ptp_convert_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
}
int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
@@ -610,14 +851,20 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
case HWTSTAMP_FILTER_NONE:
tsync_rx_ctl = 0;
tsync_rx_mtrl = 0;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1;
tsync_rx_mtrl |= IXGBE_RXMTRL_V1_SYNC_MSG;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1;
tsync_rx_mtrl |= IXGBE_RXMTRL_V1_DELAY_REQ_MSG;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
@@ -631,9 +878,21 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2;
is_l2 = true;
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_ALL:
+ /* The X550 controller is capable of timestamping all packets,
+ * which allows it to accept any filter.
+ */
+ if (hw->mac.type >= ixgbe_mac_X550) {
+ tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED;
+ break;
+ }
+ /* fall through */
default:
/*
* register RXMTRL must be set in order to do V1 packets,
@@ -641,16 +900,46 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
* Delay_Req messages and hardware does not support
* timestamping all packets => return error
*/
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
config->rx_filter = HWTSTAMP_FILTER_NONE;
return -ERANGE;
}
if (hw->mac.type == ixgbe_mac_82598EB) {
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
if (tsync_rx_ctl | tsync_tx_ctl)
return -ERANGE;
return 0;
}
+ /* Per-packet timestamping only works if the filter is set to all
+ * packets. Since this is desired, always timestamp all packets as long
+ * as any Rx filter was configured.
+ */
+ switch (hw->mac.type) {
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ /* enable timestamping all packets only if at least some
+ * packets were requested. Otherwise, play nice and disable
+ * timestamping
+ */
+ if (config->rx_filter == HWTSTAMP_FILTER_NONE)
+ break;
+
+ tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED |
+ IXGBE_TSYNCRXCTL_TYPE_ALL |
+ IXGBE_TSYNCRXCTL_TSIP_UT_EN;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED;
+ adapter->flags &= ~IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER;
+ is_l2 = true;
+ break;
+ default:
+ break;
+ }
+
/* define ethertype filter for timestamping L2 packets */
if (is_l2)
IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -678,8 +967,8 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
IXGBE_WRITE_FLUSH(hw);
/* clear TX/RX time stamp registers, just to be sure */
- regval = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
- regval = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+ ixgbe_ptp_clear_tx_timestamp(adapter);
+ IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
return 0;
}
@@ -712,23 +1001,9 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
-EFAULT : 0;
}
-/**
- * ixgbe_ptp_start_cyclecounter - create the cycle counter from hw
- * @adapter: pointer to the adapter structure
- *
- * This function should be called to set the proper values for the TIMINCA
- * register and tell the cyclecounter structure what the tick rate of SYSTIME
- * is. It does not directly modify SYSTIME registers or the timecounter
- * structure. It should be called whenever a new TIMINCA value is necessary,
- * such as during initialization or when the link speed changes.
- */
-void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
+static void ixgbe_ptp_link_speed_adjust(struct ixgbe_adapter *adapter,
+ u32 *shift, u32 *incval)
{
- struct ixgbe_hw *hw = &adapter->hw;
- u32 incval = 0;
- u32 shift = 0;
- unsigned long flags;
-
/**
* Scale the NIC cycle counter by a large factor so that
* relatively small corrections to the frequency can be added
@@ -745,36 +1020,98 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
*/
switch (adapter->link_speed) {
case IXGBE_LINK_SPEED_100_FULL:
- incval = IXGBE_INCVAL_100;
- shift = IXGBE_INCVAL_SHIFT_100;
+ *shift = IXGBE_INCVAL_SHIFT_100;
+ *incval = IXGBE_INCVAL_100;
break;
case IXGBE_LINK_SPEED_1GB_FULL:
- incval = IXGBE_INCVAL_1GB;
- shift = IXGBE_INCVAL_SHIFT_1GB;
+ *shift = IXGBE_INCVAL_SHIFT_1GB;
+ *incval = IXGBE_INCVAL_1GB;
break;
case IXGBE_LINK_SPEED_10GB_FULL:
default:
- incval = IXGBE_INCVAL_10GB;
- shift = IXGBE_INCVAL_SHIFT_10GB;
+ *shift = IXGBE_INCVAL_SHIFT_10GB;
+ *incval = IXGBE_INCVAL_10GB;
break;
}
+}
- /**
- * Modify the calculated values to fit within the correct
- * number of bits specified by the hardware. The 82599 doesn't
- * have the same space as the X540, so bitshift the calculated
- * values to fit.
+/**
+ * ixgbe_ptp_start_cyclecounter - create the cycle counter from hw
+ * @adapter: pointer to the adapter structure
+ *
+ * This function should be called to set the proper values for the TIMINCA
+ * register and tell the cyclecounter structure what the tick rate of SYSTIME
+ * is. It does not directly modify SYSTIME registers or the timecounter
+ * structure. It should be called whenever a new TIMINCA value is necessary,
+ * such as during initialization or when the link speed changes.
+ */
+void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct cyclecounter cc;
+ unsigned long flags;
+ u32 incval = 0;
+ u32 tsauxc = 0;
+ u32 fuse0 = 0;
+
+ /* For some of the boards below this mask is technically incorrect.
+ * The timestamp mask overflows at approximately 61bits. However the
+ * particular hardware does not overflow on an even bitmask value.
+ * Instead, it overflows due to conversion of upper 32bits billions of
+ * cycles. Timecounters are not really intended for this purpose so
+ * they do not properly function if the overflow point isn't 2^N-1.
+ * However, the actual SYSTIME values in question take ~138 years to
+ * overflow. In practice this means they won't actually overflow. A
+ * proper fix to this problem would require modification of the
+ * timecounter delta calculations.
*/
+ cc.mask = CLOCKSOURCE_MASK(64);
+ cc.mult = 1;
+ cc.shift = 0;
+
switch (hw->mac.type) {
+ case ixgbe_mac_X550EM_x:
+ /* SYSTIME assumes X550EM_x board frequency is 300Mhz, and is
+ * designed to represent seconds and nanoseconds when this is
+ * the case. However, some revisions of hardware have a 400Mhz
+ * clock and we have to compensate for this frequency
+ * variation using corrected mult and shift values.
+ */
+ fuse0 = IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0));
+ if (!(fuse0 & IXGBE_FUSES0_300MHZ)) {
+ cc.mult = 3;
+ cc.shift = 2;
+ }
+ /* fallthrough */
+ case ixgbe_mac_X550:
+ cc.read = ixgbe_ptp_read_X550;
+
+ /* enable SYSTIME counter */
+ IXGBE_WRITE_REG(hw, IXGBE_SYSTIMR, 0);
+ IXGBE_WRITE_REG(hw, IXGBE_SYSTIML, 0);
+ IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0);
+ tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
+ IXGBE_WRITE_REG(hw, IXGBE_TSAUXC,
+ tsauxc & ~IXGBE_TSAUXC_DISABLE_SYSTIME);
+ IXGBE_WRITE_REG(hw, IXGBE_TSIM, IXGBE_TSIM_TXTS);
+ IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_TIMESYNC);
+
+ IXGBE_WRITE_FLUSH(hw);
+ break;
case ixgbe_mac_X540:
+ cc.read = ixgbe_ptp_read_82599;
+
+ ixgbe_ptp_link_speed_adjust(adapter, &cc.shift, &incval);
IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
break;
case ixgbe_mac_82599EB:
+ cc.read = ixgbe_ptp_read_82599;
+
+ ixgbe_ptp_link_speed_adjust(adapter, &cc.shift, &incval);
incval >>= IXGBE_INCVAL_SHIFT_82599;
- shift -= IXGBE_INCVAL_SHIFT_82599;
+ cc.shift -= IXGBE_INCVAL_SHIFT_82599;
IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
- (1 << IXGBE_INCPER_SHIFT_82599) |
- incval);
+ (1 << IXGBE_INCPER_SHIFT_82599) | incval);
break;
default:
/* other devices aren't supported */
@@ -787,13 +1124,7 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
/* need lock to prevent incorrect read while modifying cyclecounter */
spin_lock_irqsave(&adapter->tmreg_lock, flags);
-
- memset(&adapter->cc, 0, sizeof(adapter->cc));
- adapter->cc.read = ixgbe_ptp_read;
- adapter->cc.mask = CYCLECOUNTER_MASK(64);
- adapter->cc.shift = shift;
- adapter->cc.mult = 1;
-
+ memcpy(&adapter->hw_cc, &cc, sizeof(adapter->hw_cc));
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
}
@@ -814,29 +1145,27 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
unsigned long flags;
- /* set SYSTIME registers to 0 just in case */
- IXGBE_WRITE_REG(hw, IXGBE_SYSTIML, 0x00000000);
- IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x00000000);
- IXGBE_WRITE_FLUSH(hw);
-
/* reset the hardware timestamping mode */
ixgbe_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
+ /* 82598 does not support PTP */
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ return;
+
ixgbe_ptp_start_cyclecounter(adapter);
spin_lock_irqsave(&adapter->tmreg_lock, flags);
-
- /* reset the ns time counter */
- timecounter_init(&adapter->tc, &adapter->cc,
+ timecounter_init(&adapter->hw_tc, &adapter->hw_cc,
ktime_to_ns(ktime_get_real()));
-
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
- /*
- * Now that the shift has been calculated and the systime
+ adapter->last_overflow_check = jiffies;
+
+ /* Now that the shift has been calculated and the systime
* registers reset, (re-)enable the Clock out feature
*/
- ixgbe_ptp_setup_sdp(adapter);
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
}
/**
@@ -845,11 +1174,11 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
*
* This function performs setup of the user entry point function table and
* initializes the PTP clock device, which is used to access the clock-like
- * features of the PTP core. It will be called by ixgbe_ptp_init, only if
- * there isn't already a clock device (such as after a suspend/resume cycle,
- * where the clock device wasn't destroyed).
+ * features of the PTP core. It will be called by ixgbe_ptp_init, and may
+ * reuse a previously initialized clock (such as during a suspend/resume
+ * cycle).
*/
-static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
+static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
long err;
@@ -869,11 +1198,12 @@ static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
adapter->ptp_caps.n_ext_ts = 0;
adapter->ptp_caps.n_per_out = 0;
adapter->ptp_caps.pps = 1;
- adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq;
+ adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
+ adapter->ptp_setup_sdp = ixgbe_ptp_setup_sdp_x540;
break;
case ixgbe_mac_82599EB:
snprintf(adapter->ptp_caps.name,
@@ -885,14 +1215,31 @@ static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
adapter->ptp_caps.n_ext_ts = 0;
adapter->ptp_caps.n_per_out = 0;
adapter->ptp_caps.pps = 0;
- adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq;
+ adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
+ adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
+ adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
+ adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
+ adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
+ break;
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ snprintf(adapter->ptp_caps.name, 16, "%s", netdev->name);
+ adapter->ptp_caps.owner = THIS_MODULE;
+ adapter->ptp_caps.max_adj = 30000000;
+ adapter->ptp_caps.n_alarm = 0;
+ adapter->ptp_caps.n_ext_ts = 0;
+ adapter->ptp_caps.n_per_out = 0;
+ adapter->ptp_caps.pps = 0;
+ adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_X550;
adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
+ adapter->ptp_setup_sdp = NULL;
break;
default:
adapter->ptp_clock = NULL;
+ adapter->ptp_setup_sdp = NULL;
return -EOPNOTSUPP;
}
@@ -961,18 +1308,13 @@ void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter)
if (!test_and_clear_bit(__IXGBE_PTP_RUNNING, &adapter->state))
return;
- /* since this might be called in suspend, we don't clear the state,
- * but simply reset the auxiliary PPS signal control register
- */
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_TSAUXC, 0x0);
+ adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
/* ensure that we cancel any pending PTP Tx work item in progress */
cancel_work_sync(&adapter->ptp_tx_work);
- if (adapter->ptp_tx_skb) {
- dev_kfree_skb_any(adapter->ptp_tx_skb);
- adapter->ptp_tx_skb = NULL;
- clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
- }
+ ixgbe_ptp_clear_tx_timestamp(adapter);
}
/**
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
index fcd8b27a0ccb..8025a3f93598 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2014 Intel Corporation.
+ Copyright(c) 1999 - 2015 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,
@@ -130,6 +130,38 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
return -ENOMEM;
}
+/**
+ * ixgbe_get_vfs - Find and take references to all vf devices
+ * @adapter: Pointer to adapter struct
+ */
+static void ixgbe_get_vfs(struct ixgbe_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ u16 vendor = pdev->vendor;
+ struct pci_dev *vfdev;
+ int vf = 0;
+ u16 vf_id;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos)
+ return;
+ pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id);
+
+ vfdev = pci_get_device(vendor, vf_id, NULL);
+ for (; vfdev; vfdev = pci_get_device(vendor, vf_id, vfdev)) {
+ if (!vfdev->is_virtfn)
+ continue;
+ if (vfdev->physfn != pdev)
+ continue;
+ if (vf >= adapter->num_vfs)
+ continue;
+ pci_dev_get(vfdev);
+ adapter->vfinfo[vf].vfdev = vfdev;
+ ++vf;
+ }
+}
+
/* Note this function is called when the user wants to enable SR-IOV
* VFs using the now deprecated module parameter
*/
@@ -170,8 +202,10 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
}
}
- if (!__ixgbe_enable_sriov(adapter))
+ if (!__ixgbe_enable_sriov(adapter)) {
+ ixgbe_get_vfs(adapter);
return;
+ }
/* If we have gotten to this point then there is no memory available
* to manage the VF devices - print message and bail.
@@ -184,6 +218,7 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
#endif /* #ifdef CONFIG_PCI_IOV */
int ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
{
+ unsigned int num_vfs = adapter->num_vfs, vf;
struct ixgbe_hw *hw = &adapter->hw;
u32 gpie;
u32 vmdctl;
@@ -192,6 +227,16 @@ int ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
/* set num VFs to 0 to prevent access to vfinfo */
adapter->num_vfs = 0;
+ /* put the reference to all of the vf devices */
+ for (vf = 0; vf < num_vfs; ++vf) {
+ struct pci_dev *vfdev = adapter->vfinfo[vf].vfdev;
+
+ if (!vfdev)
+ continue;
+ adapter->vfinfo[vf].vfdev = NULL;
+ pci_dev_put(vfdev);
+ }
+
/* free VF control structures */
kfree(adapter->vfinfo);
adapter->vfinfo = NULL;
@@ -289,6 +334,7 @@ static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs)
e_dev_warn("Failed to enable PCI sriov: %d\n", err);
return err;
}
+ ixgbe_get_vfs(adapter);
ixgbe_sriov_reinit(adapter);
return num_vfs;
@@ -406,11 +452,34 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
static int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid,
u32 vf)
{
- /* VLAN 0 is a special case, don't allow it to be removed */
- if (!vid && !add)
- return 0;
+ struct ixgbe_hw *hw = &adapter->hw;
+ int err;
+
+ /* If VLAN overlaps with one the PF is currently monitoring make
+ * sure that we are able to allocate a VLVF entry. This may be
+ * redundant but it guarantees PF will maintain visibility to
+ * the VLAN.
+ */
+ if (add && test_bit(vid, adapter->active_vlans)) {
+ err = hw->mac.ops.set_vfta(hw, vid, VMDQ_P(0), true, false);
+ if (err)
+ return err;
+ }
+
+ err = hw->mac.ops.set_vfta(hw, vid, vf, !!add, false);
- return adapter->hw.mac.ops.set_vfta(&adapter->hw, vid, vf, (bool)add);
+ if (add && !err)
+ return err;
+
+ /* If we failed to add the VF VLAN or we are removing the VF VLAN
+ * we may need to drop the PF pool bit in order to allow us to free
+ * up the VLVF resources.
+ */
+ if (test_bit(vid, adapter->active_vlans) ||
+ (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC))
+ ixgbe_update_pf_promisc_vlvf(adapter, vid);
+
+ return err;
}
static s32 ixgbe_set_vf_lpe(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf)
@@ -516,13 +585,75 @@ static void ixgbe_clear_vmvir(struct ixgbe_adapter *adapter, u32 vf)
IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf), 0);
}
+
+static void ixgbe_clear_vf_vlans(struct ixgbe_adapter *adapter, u32 vf)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 i;
+
+ /* post increment loop, covers VLVF_ENTRIES - 1 to 0 */
+ for (i = IXGBE_VLVF_ENTRIES; i--;) {
+ u32 bits[2], vlvfb, vid, vfta, vlvf;
+ u32 word = i * 2 + vf / 32;
+ u32 mask = 1 << (vf % 32);
+
+ vlvfb = IXGBE_READ_REG(hw, IXGBE_VLVFB(word));
+
+ /* if our bit isn't set we can skip it */
+ if (!(vlvfb & mask))
+ continue;
+
+ /* clear our bit from vlvfb */
+ vlvfb ^= mask;
+
+ /* create 64b mask to chedk to see if we should clear VLVF */
+ bits[word % 2] = vlvfb;
+ bits[~word % 2] = IXGBE_READ_REG(hw, IXGBE_VLVFB(word ^ 1));
+
+ /* if promisc is enabled, PF will be present, leave VFTA */
+ if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC) {
+ bits[VMDQ_P(0) / 32] &= ~(1 << (VMDQ_P(0) % 32));
+
+ if (bits[0] || bits[1])
+ goto update_vlvfb;
+ goto update_vlvf;
+ }
+
+ /* if other pools are present, just remove ourselves */
+ if (bits[0] || bits[1])
+ goto update_vlvfb;
+
+ /* if we cannot determine VLAN just remove ourselves */
+ vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(i));
+ if (!vlvf)
+ goto update_vlvfb;
+
+ vid = vlvf & VLAN_VID_MASK;
+ mask = 1 << (vid % 32);
+
+ /* clear bit from VFTA */
+ vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(vid / 32));
+ if (vfta & mask)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(vid / 32), vfta ^ mask);
+update_vlvf:
+ /* clear POOL selection enable */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(i), 0);
+update_vlvfb:
+ /* clear pool bits */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), vlvfb);
+ }
+}
+
static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
{
struct ixgbe_hw *hw = &adapter->hw;
struct vf_data_storage *vfinfo = &adapter->vfinfo[vf];
u8 num_tcs = netdev_get_num_tc(adapter->netdev);
- /* add PF assigned VLAN or VLAN 0 */
+ /* remove VLAN filters beloning to this VF */
+ ixgbe_clear_vf_vlans(adapter, vf);
+
+ /* add back PF assigned VLAN or VLAN 0 */
ixgbe_set_vf_vlan(adapter, true, vfinfo->pf_vlan, vf);
/* reset offloads to defaults */
@@ -768,40 +899,14 @@ static int ixgbe_set_vf_mac_addr(struct ixgbe_adapter *adapter,
return ixgbe_set_vf_mac(adapter, vf, new_mac) < 0;
}
-static int ixgbe_find_vlvf_entry(struct ixgbe_hw *hw, u32 vlan)
-{
- u32 vlvf;
- s32 regindex;
-
- /* short cut the special case */
- if (vlan == 0)
- return 0;
-
- /* Search for the vlan id in the VLVF entries */
- for (regindex = 1; regindex < IXGBE_VLVF_ENTRIES; regindex++) {
- vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(regindex));
- if ((vlvf & VLAN_VID_MASK) == vlan)
- break;
- }
-
- /* Return a negative value if not found */
- if (regindex >= IXGBE_VLVF_ENTRIES)
- regindex = -1;
-
- return regindex;
-}
-
static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter,
u32 *msgbuf, u32 vf)
{
+ u32 add = (msgbuf[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT;
+ u32 vid = (msgbuf[1] & IXGBE_VLVF_VLANID_MASK);
+ u8 tcs = netdev_get_num_tc(adapter->netdev);
struct ixgbe_hw *hw = &adapter->hw;
- int add = (msgbuf[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT;
- int vid = (msgbuf[1] & IXGBE_VLVF_VLANID_MASK);
int err;
- s32 reg_ndx;
- u32 vlvf;
- u32 bits;
- u8 tcs = netdev_get_num_tc(adapter->netdev);
if (adapter->vfinfo[vf].pf_vlan || tcs) {
e_warn(drv,
@@ -811,54 +916,23 @@ static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter,
return -1;
}
- if (add)
- adapter->vfinfo[vf].vlan_count++;
- else if (adapter->vfinfo[vf].vlan_count)
- adapter->vfinfo[vf].vlan_count--;
-
- /* in case of promiscuous mode any VLAN filter set for a VF must
- * also have the PF pool added to it.
- */
- if (add && adapter->netdev->flags & IFF_PROMISC)
- err = ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0));
+ /* VLAN 0 is a special case, don't allow it to be removed */
+ if (!vid && !add)
+ return 0;
err = ixgbe_set_vf_vlan(adapter, add, vid, vf);
- if (!err && adapter->vfinfo[vf].spoofchk_enabled)
- hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf);
+ if (err)
+ return err;
- /* Go through all the checks to see if the VLAN filter should
- * be wiped completely.
- */
- if (!add && adapter->netdev->flags & IFF_PROMISC) {
- reg_ndx = ixgbe_find_vlvf_entry(hw, vid);
- if (reg_ndx < 0)
- return err;
- vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(reg_ndx));
- /* See if any other pools are set for this VLAN filter
- * entry other than the PF.
- */
- if (VMDQ_P(0) < 32) {
- bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2));
- bits &= ~(1 << VMDQ_P(0));
- bits |= IXGBE_READ_REG(hw,
- IXGBE_VLVFB(reg_ndx * 2) + 1);
- } else {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB(reg_ndx * 2) + 1);
- bits &= ~(1 << (VMDQ_P(0) - 32));
- bits |= IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2));
- }
+ if (adapter->vfinfo[vf].spoofchk_enabled)
+ hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf);
- /* If the filter was removed then ensure PF pool bit
- * is cleared if the PF only added itself to the pool
- * because the PF is in promiscuous mode.
- */
- if ((vlvf & VLAN_VID_MASK) == vid &&
- !test_bit(vid, adapter->active_vlans) && !bits)
- ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0));
- }
+ if (add)
+ adapter->vfinfo[vf].vlan_count++;
+ else if (adapter->vfinfo[vf].vlan_count)
+ adapter->vfinfo[vf].vlan_count--;
- return err;
+ return 0;
}
static int ixgbe_set_vf_macvlan_msg(struct ixgbe_adapter *adapter,
@@ -1239,6 +1313,9 @@ static int ixgbe_enable_port_vlan(struct ixgbe_adapter *adapter, int vf,
if (err)
goto out;
+ /* Revoke tagless access via VLAN 0 */
+ ixgbe_set_vf_vlan(adapter, false, 0, vf);
+
ixgbe_set_vmvir(adapter, vlan, qos, vf);
ixgbe_set_vmolr(hw, vf, false);
if (adapter->vfinfo[vf].spoofchk_enabled)
@@ -1272,6 +1349,8 @@ static int ixgbe_disable_port_vlan(struct ixgbe_adapter *adapter, int vf)
err = ixgbe_set_vf_vlan(adapter, false,
adapter->vfinfo[vf].pf_vlan, vf);
+ /* Restore tagless access via VLAN 0 */
+ ixgbe_set_vf_vlan(adapter, true, 0, vf);
ixgbe_clear_vmvir(adapter, vf);
ixgbe_set_vmolr(hw, vf, true);
hw->mac.ops.set_vlan_anti_spoofing(hw, false, vf);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
index 995f03107eac..bf7367a08716 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
@@ -1020,6 +1020,7 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_TXSTMPH 0x08C08 /* Tx timestamp value High - RO */
#define IXGBE_SYSTIML 0x08C0C /* System time register Low - RO */
#define IXGBE_SYSTIMH 0x08C10 /* System time register High - RO */
+#define IXGBE_SYSTIMR 0x08C58 /* System time register Residue - RO */
#define IXGBE_TIMINCA 0x08C14 /* Increment attributes register - RW */
#define IXGBE_TIMADJL 0x08C18 /* Time Adjustment Offset register Low - RW */
#define IXGBE_TIMADJH 0x08C1C /* Time Adjustment Offset register High - RW */
@@ -1036,6 +1037,7 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_AUXSTMPH0 0x08C40 /* Auxiliary Time Stamp 0 register High - RO */
#define IXGBE_AUXSTMPL1 0x08C44 /* Auxiliary Time Stamp 1 register Low - RO */
#define IXGBE_AUXSTMPH1 0x08C48 /* Auxiliary Time Stamp 1 register High - RO */
+#define IXGBE_TSIM 0x08C68 /* TimeSync Interrupt Mask Register - RW */
/* Diagnostic Registers */
#define IXGBE_RDSTATCTL 0x02C20
@@ -1345,7 +1347,10 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK 0xFF01 /* int chip-wide mask */
#define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_FLAG 0xFC01 /* int chip-wide mask */
#define IXGBE_MDIO_GLOBAL_ALARM_1 0xCC00 /* Global alarm 1 */
+#define IXGBE_MDIO_GLOBAL_ALM_1_DEV_FAULT 0x0010 /* device fault */
#define IXGBE_MDIO_GLOBAL_ALM_1_HI_TMP_FAIL 0x4000 /* high temp failure */
+#define IXGBE_MDIO_GLOBAL_FAULT_MSG 0xC850 /* global fault msg */
+#define IXGBE_MDIO_GLOBAL_FAULT_MSG_HI_TMP 0x8007 /* high temp failure */
#define IXGBE_MDIO_GLOBAL_INT_MASK 0xD400 /* Global int mask */
/* autoneg vendor alarm int enable */
#define IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN 0x1000
@@ -1353,6 +1358,7 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN 0x1 /* vendor alarm int enable */
#define IXGBE_MDIO_GLOBAL_STD_ALM2_INT 0x200 /* vendor alarm2 int mask */
#define IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN 0x4000 /* int high temp enable */
+#define IXGBE_MDIO_GLOBAL_INT_DEV_FAULT_EN 0x0010 /*int dev fault enable */
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR 0xC30A /* PHY_XS SDA/SCL Addr Reg */
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA 0xC30B /* PHY_XS SDA/SCL Data Reg */
@@ -2209,6 +2215,7 @@ enum {
#define IXGBE_TSAUXC_EN_CLK 0x00000004
#define IXGBE_TSAUXC_SYNCLK 0x00000008
#define IXGBE_TSAUXC_SDP0_INT 0x00000040
+#define IXGBE_TSAUXC_DISABLE_SYSTIME 0x80000000
#define IXGBE_TSYNCTXCTL_VALID 0x00000001 /* Tx timestamp valid */
#define IXGBE_TSYNCTXCTL_ENABLED 0x00000010 /* Tx timestamping enabled */
@@ -2218,8 +2225,12 @@ enum {
#define IXGBE_TSYNCRXCTL_TYPE_L2_V2 0x00
#define IXGBE_TSYNCRXCTL_TYPE_L4_V1 0x02
#define IXGBE_TSYNCRXCTL_TYPE_L2_L4_V2 0x04
+#define IXGBE_TSYNCRXCTL_TYPE_ALL 0x08
#define IXGBE_TSYNCRXCTL_TYPE_EVENT_V2 0x0A
#define IXGBE_TSYNCRXCTL_ENABLED 0x00000010 /* Rx Timestamping enabled */
+#define IXGBE_TSYNCRXCTL_TSIP_UT_EN 0x00800000 /* Rx Timestamp in Packet */
+
+#define IXGBE_TSIM_TXTS 0x00000002
#define IXGBE_RXMTRL_V1_CTRLT_MASK 0x000000FF
#define IXGBE_RXMTRL_V1_SYNC_MSG 0x00
@@ -2332,6 +2343,7 @@ enum {
#define IXGBE_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */
#define IXGBE_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */
#define IXGBE_RXD_STAT_LLINT 0x800 /* Pkt caused Low Latency Interrupt */
+#define IXGBE_RXD_STAT_TSIP 0x08000 /* Time Stamp in packet buffer */
#define IXGBE_RXD_STAT_TS 0x10000 /* Time Stamp */
#define IXGBE_RXD_STAT_SECP 0x20000 /* Security Processing */
#define IXGBE_RXD_STAT_LB 0x40000 /* Loopback Status */
@@ -2768,6 +2780,7 @@ struct ixgbe_adv_tx_context_desc {
#define IXGBE_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */
#define IXGBE_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */
#define IXGBE_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 Packet TYPE of SCTP */
+#define IXGBE_ADVTXD_TUCMD_L4T_RSV 0x00001800 /* RSV L4 Packet TYPE */
#define IXGBE_ADVTXD_TUCMD_MKRREQ 0x00002000 /*Req requires Markers and CRC*/
#define IXGBE_ADVTXD_POPTS_IPSEC 0x00000400 /* IPSec offload request */
#define IXGBE_ADVTXD_TUCMD_IPSEC_TYPE_ESP 0x00002000 /* IPSec Type ESP */
@@ -3288,7 +3301,7 @@ struct ixgbe_mac_operations {
s32 (*enable_mc)(struct ixgbe_hw *);
s32 (*disable_mc)(struct ixgbe_hw *);
s32 (*clear_vfta)(struct ixgbe_hw *);
- s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool);
+ s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool, bool);
s32 (*init_uta_tables)(struct ixgbe_hw *);
void (*set_mac_anti_spoofing)(struct ixgbe_hw *, bool, int);
void (*set_vlan_anti_spoofing)(struct ixgbe_hw *, bool, int);
@@ -3508,7 +3521,7 @@ struct ixgbe_info {
#define IXGBE_FUSES0_GROUP(_i) (0x11158 + ((_i) * 4))
#define IXGBE_FUSES0_300MHZ BIT(5)
-#define IXGBE_FUSES0_REV1 BIT(6)
+#define IXGBE_FUSES0_REV_MASK (3 << 6)
#define IXGBE_KRM_PORT_CAR_GEN_CTRL(P) ((P) ? 0x8010 : 0x4010)
#define IXGBE_KRM_LINK_CTRL_1(P) ((P) ? 0x820C : 0x420C)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
index c1d4584f6469..2358c1b7d586 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
@@ -57,8 +57,7 @@ s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw)
struct ixgbe_phy_info *phy = &hw->phy;
/* set_phy_power was set by default to NULL */
- if (!ixgbe_mng_present(hw))
- phy->ops.set_phy_power = ixgbe_set_copper_phy_power;
+ phy->ops.set_phy_power = ixgbe_set_copper_phy_power;
mac->mcft_size = IXGBE_X540_MC_TBL_SIZE;
mac->vft_size = IXGBE_X540_VFT_TBL_SIZE;
@@ -110,13 +109,14 @@ mac_reset_top:
ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST_MASK) {
@@ -154,12 +154,16 @@ mac_reset_top:
/* Add the SAN MAC address to the RAR only if it's a valid address */
if (is_valid_ether_addr(hw->mac.san_addr)) {
- hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
- hw->mac.san_addr, 0, IXGBE_RAH_AV);
-
/* Save the SAN MAC RAR index */
hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
+ hw->mac.ops.set_rar(hw, hw->mac.san_mac_rar_index,
+ hw->mac.san_addr, 0, IXGBE_RAH_AV);
+
+ /* clear VMDq pool/queue selection for this RAR */
+ hw->mac.ops.clear_vmdq(hw, hw->mac.san_mac_rar_index,
+ IXGBE_CLEAR_VMDQ_ALL);
+
/* Reserve the last RAR for the SAN MAC address */
hw->mac.num_rar_entries--;
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
index ebe0ac950b14..87aca3f7c3de 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
@@ -26,6 +26,8 @@
#include "ixgbe_common.h"
#include "ixgbe_phy.h"
+static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *, ixgbe_link_speed);
+
static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw)
{
struct ixgbe_mac_info *mac = &hw->mac;
@@ -85,79 +87,6 @@ static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value)
}
/**
- * ixgbe_check_cs4227_reg - Perform diag on a CS4227 register
- * @hw: pointer to hardware structure
- * @reg: the register to check
- *
- * Performs a diagnostic on a register in the CS4227 chip. Returns an error
- * if it is not operating correctly.
- * This function assumes that the caller has acquired the proper semaphore.
- */
-static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg)
-{
- s32 status;
- u32 retry;
- u16 reg_val;
-
- reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1;
- status = ixgbe_write_cs4227(hw, reg, reg_val);
- if (status)
- return status;
- for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
- msleep(IXGBE_CS4227_CHECK_DELAY);
- reg_val = 0xFFFF;
- ixgbe_read_cs4227(hw, reg, &reg_val);
- if (!reg_val)
- break;
- }
- if (reg_val) {
- hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg);
- return status;
- }
-
- return 0;
-}
-
-/**
- * ixgbe_get_cs4227_status - Return CS4227 status
- * @hw: pointer to hardware structure
- *
- * Performs a diagnostic on the CS4227 chip. Returns an error if it is
- * not operating correctly.
- * This function assumes that the caller has acquired the proper semaphore.
- */
-static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw)
-{
- s32 status;
- u16 value = 0;
-
- /* Exit if the diagnostic has already been performed. */
- status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
- if (status)
- return status;
- if (value == IXGBE_CS4227_RESET_COMPLETE)
- return 0;
-
- /* Check port 0. */
- status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB);
- if (status)
- return status;
-
- status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB);
- if (status)
- return status;
-
- /* Check port 1. */
- status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB +
- (1 << 12));
- if (status)
- return status;
-
- return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB +
- (1 << 12));
-}
-
-/**
* ixgbe_read_pe - Read register from port expander
* @hw: pointer to hardware structure
* @reg: register number to read
@@ -326,13 +255,6 @@ static void ixgbe_check_cs4227(struct ixgbe_hw *hw)
return;
}
- /* Is the CS4227 working correctly? */
- status = ixgbe_get_cs4227_status(hw);
- if (status) {
- hw_err(hw, "CS4227 status failed: %d", status);
- goto out;
- }
-
/* Record completion for next time. */
status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
IXGBE_CS4227_RESET_COMPLETE);
@@ -1257,31 +1179,71 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw,
if (status)
return status;
- /* Configure CS4227 LINE side to 10G SR. */
- slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12);
- value = IXGBE_CS4227_SPEED_10G;
- status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
- value);
-
- /* Configure CS4227 for HOST connection rate then type. */
- slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12);
- value = speed & IXGBE_LINK_SPEED_10GB_FULL ?
- IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G;
- status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
- value);
+ if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
+ /* Configure CS4227 LINE side to 10G SR. */
+ slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12);
+ value = IXGBE_CS4227_SPEED_10G;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
- slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12);
- if (setup_linear)
- value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
- else
+ slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12);
value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
- status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
- value);
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
+
+ /* Configure CS4227 for HOST connection rate then type. */
+ slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12);
+ value = speed & IXGBE_LINK_SPEED_10GB_FULL ?
+ IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
- /* If internal link mode is XFI, then setup XFI internal link. */
- if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE))
+ slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12);
+ if (setup_linear)
+ value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+ else
+ value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
+
+ /* Setup XFI internal link. */
status = ixgbe_setup_ixfi_x550em(hw, &speed);
+ if (status) {
+ hw_dbg(hw, "setup_ixfi failed with %d\n", status);
+ return status;
+ }
+ } else {
+ /* Configure internal PHY for KR/KX. */
+ status = ixgbe_setup_kr_speed_x550em(hw, speed);
+ if (status) {
+ hw_dbg(hw, "setup_kr_speed failed with %d\n", status);
+ return status;
+ }
+ /* Configure CS4227 LINE side to proper mode. */
+ slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12);
+ if (setup_linear)
+ value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+ else
+ value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
+ }
+
+ return 0;
+
+i2c_err:
+ hw_dbg(hw, "combined i2c access failed with %d\n", status);
return status;
}
@@ -1482,7 +1444,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc)
IXGBE_MDIO_GLOBAL_ALARM_1_INT)))
return status;
- /* High temperature failure alarm triggered */
+ /* Global alarm triggered */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_ALARM_1,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
@@ -1496,6 +1458,21 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc)
ixgbe_set_copper_phy_power(hw, false);
return IXGBE_ERR_OVERTEMP;
}
+ if (reg & IXGBE_MDIO_GLOBAL_ALM_1_DEV_FAULT) {
+ /* device fault alarm triggered */
+ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_FAULT_MSG,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+ &reg);
+ if (status)
+ return status;
+
+ /* if device fault was due to high temp alarm handle and exit */
+ if (reg == IXGBE_MDIO_GLOBAL_FAULT_MSG_HI_TMP) {
+ /* power down the PHY in case the PHY FW didn't */
+ ixgbe_set_copper_phy_power(hw, false);
+ return IXGBE_ERR_OVERTEMP;
+ }
+ }
/* Vendor alarm 2 triggered */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG,
@@ -1549,14 +1526,15 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw)
if (status)
return status;
- /* Enables high temperature failure alarm */
+ /* Enable high temperature failure and global fault alarms */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
if (status)
return status;
- reg |= IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN;
+ reg |= (IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN |
+ IXGBE_MDIO_GLOBAL_INT_DEV_FAULT_EN);
status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
@@ -1765,6 +1743,12 @@ static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw)
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
return IXGBE_ERR_CONFIG;
+ if (hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) {
+ speed = IXGBE_LINK_SPEED_10GB_FULL |
+ IXGBE_LINK_SPEED_1GB_FULL;
+ return ixgbe_setup_kr_speed_x550em(hw, speed);
+ }
+
/* If link is not up, then there is no setup necessary so return */
status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
if (status)
@@ -1873,10 +1857,6 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
u32 save_autoneg;
bool link_up;
- /* SW LPLU not required on later HW revisions. */
- if (IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0)))
- return 0;
-
/* If blocked by MNG FW, then don't restart AN */
if (ixgbe_check_reset_blocked(hw))
return 0;
@@ -1969,7 +1949,6 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
{
struct ixgbe_phy_info *phy = &hw->phy;
- ixgbe_link_speed speed;
s32 ret_val;
hw->mac.ops.set_lan_id(hw);
@@ -1982,13 +1961,6 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
* to determine internal PHY mode.
*/
phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
-
- /* If internal PHY mode is KR, then initialize KR link */
- if (phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) {
- speed = IXGBE_LINK_SPEED_10GB_FULL |
- IXGBE_LINK_SPEED_1GB_FULL;
- ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
- }
}
/* Identify the PHY or SFP module */
@@ -2020,18 +1992,13 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
/* If internal link mode is XFI, then setup iXFI internal link,
* else setup KR now.
*/
- if (!(phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
- phy->ops.setup_internal_link =
- ixgbe_setup_internal_phy_t_x550em;
- } else {
- speed = IXGBE_LINK_SPEED_10GB_FULL |
- IXGBE_LINK_SPEED_1GB_FULL;
- ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
- }
+ phy->ops.setup_internal_link =
+ ixgbe_setup_internal_phy_t_x550em;
/* setup SW LPLU only for first revision */
- if (!(IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw,
- IXGBE_FUSES0_GROUP(0))))
+ if (hw->mac.type == ixgbe_mac_X550EM_x &&
+ !(IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0)) &
+ IXGBE_FUSES0_REV_MASK))
phy->ops.enter_lplu = ixgbe_enter_lplu_t_x550em;
phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em;
@@ -2176,13 +2143,14 @@ mac_reset_top:
ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear meaning reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST_MASK) {
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index d3e5f5b37999..c48aef613b0a 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -774,7 +774,7 @@ static int ixgbevf_set_coalesce(struct net_device *netdev,
adapter->tx_itr_setting = ec->tx_coalesce_usecs;
if (adapter->tx_itr_setting == 1)
- tx_itr_param = IXGBE_10K_ITR;
+ tx_itr_param = IXGBE_12K_ITR;
else
tx_itr_param = adapter->tx_itr_setting;
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
index ec3147279621..68ec7daa04fd 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
@@ -326,8 +326,7 @@ static inline bool ixgbevf_qv_disable(struct ixgbevf_q_vector *q_vector)
#define IXGBE_MIN_RSC_ITR 24
#define IXGBE_100K_ITR 40
#define IXGBE_20K_ITR 200
-#define IXGBE_10K_ITR 400
-#define IXGBE_8K_ITR 500
+#define IXGBE_12K_ITR 336
/* Helper macros to switch between ints/sec and what the register uses.
* And yes, it's the same math going both ways. The lowest value
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 592ff237d692..3558f019b631 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -59,7 +59,7 @@ static const char ixgbevf_driver_string[] =
#define DRV_VERSION "2.12.1-k"
const char ixgbevf_driver_version[] = DRV_VERSION;
static char ixgbevf_copyright[] =
- "Copyright (c) 2009 - 2012 Intel Corporation.";
+ "Copyright (c) 2009 - 2015 Intel Corporation.";
static const struct ixgbevf_info *ixgbevf_info_tbl[] = {
[board_82599_vf] = &ixgbevf_82599_vf_info,
@@ -96,12 +96,14 @@ static int debug = -1;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+static struct workqueue_struct *ixgbevf_wq;
+
static void ixgbevf_service_event_schedule(struct ixgbevf_adapter *adapter)
{
if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
!test_bit(__IXGBEVF_REMOVING, &adapter->state) &&
!test_and_set_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state))
- schedule_work(&adapter->service_task);
+ queue_work(ixgbevf_wq, &adapter->service_task);
}
static void ixgbevf_service_event_complete(struct ixgbevf_adapter *adapter)
@@ -1014,6 +1016,8 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
ixgbevf_for_each_ring(ring, q_vector->tx)
clean_complete &= ixgbevf_clean_tx_irq(q_vector, ring);
+ if (budget <= 0)
+ return budget;
#ifdef CONFIG_NET_RX_BUSY_POLL
if (!ixgbevf_qv_lock_napi(q_vector))
return budget;
@@ -1043,7 +1047,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
return budget;
/* all work done, exit the polling mode */
napi_complete_done(napi, work_done);
- if (adapter->rx_itr_setting & 1)
+ if (adapter->rx_itr_setting == 1)
ixgbevf_set_itr(q_vector);
if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
!test_bit(__IXGBEVF_REMOVING, &adapter->state))
@@ -1138,7 +1142,7 @@ static void ixgbevf_configure_msix(struct ixgbevf_adapter *adapter)
if (q_vector->tx.ring && !q_vector->rx.ring) {
/* Tx only vector */
if (adapter->tx_itr_setting == 1)
- q_vector->itr = IXGBE_10K_ITR;
+ q_vector->itr = IXGBE_12K_ITR;
else
q_vector->itr = adapter->tx_itr_setting;
} else {
@@ -1196,7 +1200,7 @@ static void ixgbevf_update_itr(struct ixgbevf_q_vector *q_vector,
/* simple throttle rate management
* 0-20MB/s lowest (100000 ints/s)
* 20-100MB/s low (20000 ints/s)
- * 100-1249MB/s bulk (8000 ints/s)
+ * 100-1249MB/s bulk (12000 ints/s)
*/
/* what was last interrupt timeslice? */
timepassed_us = q_vector->itr >> 2;
@@ -1246,8 +1250,9 @@ static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector)
new_itr = IXGBE_20K_ITR;
break;
case bulk_latency:
+ new_itr = IXGBE_12K_ITR;
+ break;
default:
- new_itr = IXGBE_8K_ITR;
break;
}
@@ -1288,7 +1293,7 @@ static irqreturn_t ixgbevf_msix_clean_rings(int irq, void *data)
/* EIAM disabled interrupts (on this vector) for us */
if (q_vector->rx.ring || q_vector->tx.ring)
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -1332,7 +1337,6 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter)
int txr_remaining = adapter->num_tx_queues;
int i, j;
int rqpv, tqpv;
- int err = 0;
q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
@@ -1345,7 +1349,7 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter)
for (; txr_idx < txr_remaining; v_start++, txr_idx++)
map_vector_to_txq(adapter, v_start, txr_idx);
- goto out;
+ return 0;
}
/* If we don't have enough vectors for a 1-to-1
@@ -1370,8 +1374,7 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter)
}
}
-out:
- return err;
+ return 0;
}
/**
@@ -1469,9 +1472,7 @@ static inline void ixgbevf_reset_q_vectors(struct ixgbevf_adapter *adapter)
**/
static int ixgbevf_request_irq(struct ixgbevf_adapter *adapter)
{
- int err = 0;
-
- err = ixgbevf_request_msix_irqs(adapter);
+ int err = ixgbevf_request_msix_irqs(adapter);
if (err)
hw_dbg(&adapter->hw, "request_irq failed, Error %d\n", err);
@@ -1830,7 +1831,7 @@ static int ixgbevf_vlan_rx_kill_vid(struct net_device *netdev,
{
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
- int err = -EOPNOTSUPP;
+ int err;
spin_lock_bh(&adapter->mbx_lock);
@@ -2046,7 +2047,7 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter)
ixgbe_mbox_api_11,
ixgbe_mbox_api_10,
ixgbe_mbox_api_unknown };
- int err = 0, idx = 0;
+ int err, idx = 0;
spin_lock_bh(&adapter->mbx_lock);
@@ -2260,10 +2261,8 @@ void ixgbevf_reset(struct ixgbevf_adapter *adapter)
}
if (is_valid_ether_addr(adapter->hw.mac.addr)) {
- memcpy(netdev->dev_addr, adapter->hw.mac.addr,
- netdev->addr_len);
- memcpy(netdev->perm_addr, adapter->hw.mac.addr,
- netdev->addr_len);
+ ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
+ ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr);
}
adapter->last_reset = jiffies;
@@ -2421,7 +2420,7 @@ err_allocation:
static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
- int err = 0;
+ int err;
int vector, v_budget;
/* It's easy to be greedy for MSI-X vectors, but it really
@@ -2439,26 +2438,21 @@ static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter)
*/
adapter->msix_entries = kcalloc(v_budget,
sizeof(struct msix_entry), GFP_KERNEL);
- if (!adapter->msix_entries) {
- err = -ENOMEM;
- goto out;
- }
+ if (!adapter->msix_entries)
+ return -ENOMEM;
for (vector = 0; vector < v_budget; vector++)
adapter->msix_entries[vector].entry = vector;
err = ixgbevf_acquire_msix_vectors(adapter, v_budget);
if (err)
- goto out;
+ return err;
err = netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues);
if (err)
- goto out;
-
- err = netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues);
+ return err;
-out:
- return err;
+ return netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues);
}
/**
@@ -2483,9 +2477,6 @@ static int ixgbevf_alloc_q_vectors(struct ixgbevf_adapter *adapter)
q_vector->v_idx = q_idx;
netif_napi_add(adapter->netdev, &q_vector->napi,
ixgbevf_poll, 64);
-#ifdef CONFIG_NET_RX_BUSY_POLL
- napi_hash_add(&q_vector->napi);
-#endif
adapter->q_vector[q_idx] = q_vector;
}
@@ -2662,13 +2653,14 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter)
else if (is_zero_ether_addr(adapter->hw.mac.addr))
dev_info(&pdev->dev,
"MAC address not assigned by administrator.\n");
- memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len);
+ ether_addr_copy(netdev->dev_addr, hw->mac.addr);
}
if (!is_valid_ether_addr(netdev->dev_addr)) {
dev_info(&pdev->dev, "Assigning random MAC address\n");
eth_hw_addr_random(netdev);
- memcpy(hw->mac.addr, netdev->dev_addr, netdev->addr_len);
+ ether_addr_copy(hw->mac.addr, netdev->dev_addr);
+ ether_addr_copy(hw->mac.perm_addr, netdev->dev_addr);
}
/* Enable dynamic interrupt throttling rates */
@@ -3355,6 +3347,7 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
u8 l4_hdr = 0;
+ __be16 frag_off;
switch (first->protocol) {
case htons(ETH_P_IP):
@@ -3365,13 +3358,16 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
case htons(ETH_P_IPV6):
vlan_macip_lens |= skb_network_header_len(skb);
l4_hdr = ipv6_hdr(skb)->nexthdr;
+ if (likely(skb_network_header_len(skb) ==
+ sizeof(struct ipv6hdr)))
+ break;
+ ipv6_skip_exthdr(skb, skb_network_offset(skb) +
+ sizeof(struct ipv6hdr),
+ &l4_hdr, &frag_off);
+ if (unlikely(frag_off))
+ l4_hdr = NEXTHDR_FRAGMENT;
break;
default:
- if (unlikely(net_ratelimit())) {
- dev_warn(tx_ring->dev,
- "partial checksum but proto=%x!\n",
- first->protocol);
- }
break;
}
@@ -3393,16 +3389,18 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
default:
if (unlikely(net_ratelimit())) {
dev_warn(tx_ring->dev,
- "partial checksum but l4 proto=%x!\n",
- l4_hdr);
+ "partial checksum, l3 proto=%x, l4 proto=%x\n",
+ first->protocol, l4_hdr);
}
- break;
+ skb_checksum_help(skb);
+ goto no_csum;
}
/* update TX checksum flag */
first->tx_flags |= IXGBE_TX_FLAGS_CSUM;
}
+no_csum:
/* vlan_macip_lens: MACLEN, VLAN tag */
vlan_macip_lens |= skb_network_offset(skb) << IXGBE_ADVTXD_MACLEN_SHIFT;
vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
@@ -3698,8 +3696,8 @@ static int ixgbevf_set_mac(struct net_device *netdev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
- memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
+ ether_addr_copy(netdev->dev_addr, addr->sa_data);
+ ether_addr_copy(hw->mac.addr, addr->sa_data);
spin_lock_bh(&adapter->mbx_lock);
@@ -4248,15 +4246,17 @@ static struct pci_driver ixgbevf_driver = {
**/
static int __init ixgbevf_init_module(void)
{
- int ret;
-
pr_info("%s - version %s\n", ixgbevf_driver_string,
ixgbevf_driver_version);
pr_info("%s\n", ixgbevf_copyright);
+ ixgbevf_wq = create_singlethread_workqueue(ixgbevf_driver_name);
+ if (!ixgbevf_wq) {
+ pr_err("%s: Failed to create workqueue\n", ixgbevf_driver_name);
+ return -ENOMEM;
+ }
- ret = pci_register_driver(&ixgbevf_driver);
- return ret;
+ return pci_register_driver(&ixgbevf_driver);
}
module_init(ixgbevf_init_module);
@@ -4270,6 +4270,10 @@ module_init(ixgbevf_init_module);
static void __exit ixgbevf_exit_module(void)
{
pci_unregister_driver(&ixgbevf_driver);
+ if (ixgbevf_wq) {
+ destroy_workqueue(ixgbevf_wq);
+ ixgbevf_wq = NULL;
+ }
}
#ifdef DEBUG
diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c
index 427f3605cbfc..61a98f4c5746 100644
--- a/drivers/net/ethernet/intel/ixgbevf/vf.c
+++ b/drivers/net/ethernet/intel/ixgbevf/vf.c
@@ -117,7 +117,9 @@ static s32 ixgbevf_reset_hw_vf(struct ixgbe_hw *hw)
msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_NACK))
return IXGBE_ERR_INVALID_MAC_ADDR;
- ether_addr_copy(hw->mac.perm_addr, addr);
+ if (msgbuf[0] == (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_ACK))
+ ether_addr_copy(hw->mac.perm_addr, addr);
+
hw->mac.mc_filter_type = msgbuf[IXGBE_VF_MC_TYPE_WORD];
return 0;
diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c
index 060dd3922974..b1de7afd4116 100644
--- a/drivers/net/ethernet/jme.c
+++ b/drivers/net/ethernet/jme.c
@@ -2753,7 +2753,7 @@ static netdev_features_t
jme_fix_features(struct net_device *netdev, netdev_features_t features)
{
if (netdev->mtu > 1900)
- features &= ~(NETIF_F_ALL_TSO | NETIF_F_ALL_CSUM);
+ features &= ~(NETIF_F_ALL_TSO | NETIF_F_CSUM_MASK);
return features;
}
diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c
index 581928c068f2..b630ef1e9646 100644
--- a/drivers/net/ethernet/lantiq_etop.c
+++ b/drivers/net/ethernet/lantiq_etop.c
@@ -375,22 +375,16 @@ static int
ltq_etop_mdio_probe(struct net_device *dev)
{
struct ltq_etop_priv *priv = netdev_priv(dev);
- struct phy_device *phydev = NULL;
- int phy_addr;
+ struct phy_device *phydev;
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- if (priv->mii_bus->phy_map[phy_addr]) {
- phydev = priv->mii_bus->phy_map[phy_addr];
- break;
- }
- }
+ phydev = phy_find_first(priv->mii_bus);
if (!phydev) {
netdev_err(dev, "no PHY found\n");
return -ENODEV;
}
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&ltq_etop_mdio_link, priv->pldata->mii_mode);
if (IS_ERR(phydev)) {
@@ -408,9 +402,7 @@ ltq_etop_mdio_probe(struct net_device *dev)
phydev->advertising = phydev->supported;
priv->phydev = phydev;
- pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n",
- dev->name, phydev->drv->name,
- dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
}
@@ -435,18 +427,9 @@ ltq_etop_mdio_init(struct net_device *dev)
priv->mii_bus->name = "ltq_mii";
snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
priv->pdev->name, priv->pdev->id);
- priv->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!priv->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- priv->mii_bus->irq[i] = PHY_POLL;
-
if (mdiobus_register(priv->mii_bus)) {
err = -ENXIO;
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
}
if (ltq_etop_mdio_probe(dev)) {
@@ -457,8 +440,6 @@ ltq_etop_mdio_init(struct net_device *dev)
err_out_unregister_bus:
mdiobus_unregister(priv->mii_bus);
-err_out_free_mdio_irq:
- kfree(priv->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(priv->mii_bus);
err_out:
@@ -472,7 +453,6 @@ ltq_etop_mdio_cleanup(struct net_device *dev)
phy_disconnect(priv->phydev);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
}
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index 4182290fdbcf..a0c03834a2f7 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -3133,7 +3133,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
if (!mp->phy)
err = -ENODEV;
else
- phy_addr_set(mp, mp->phy->addr);
+ phy_addr_set(mp, mp->phy->mdio.addr);
} else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) {
mp->phy = phy_scan(mp, pd->phy_addr);
@@ -3257,25 +3257,20 @@ static struct platform_driver mv643xx_eth_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &mv643xx_eth_shared_driver,
+ &mv643xx_eth_driver,
+};
+
static int __init mv643xx_eth_init_module(void)
{
- int rc;
-
- rc = platform_driver_register(&mv643xx_eth_shared_driver);
- if (!rc) {
- rc = platform_driver_register(&mv643xx_eth_driver);
- if (rc)
- platform_driver_unregister(&mv643xx_eth_shared_driver);
- }
-
- return rc;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(mv643xx_eth_init_module);
static void __exit mv643xx_eth_cleanup_module(void)
{
- platform_driver_unregister(&mv643xx_eth_driver);
- platform_driver_unregister(&mv643xx_eth_shared_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(mv643xx_eth_cleanup_module);
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
index fc2fb25343f4..8982c882af1b 100644
--- a/drivers/net/ethernet/marvell/mvmdio.c
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -187,7 +187,7 @@ static int orion_mdio_probe(struct platform_device *pdev)
struct resource *r;
struct mii_bus *bus;
struct orion_mdio_dev *dev;
- int i, ret;
+ int ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
@@ -207,14 +207,6 @@ static int orion_mdio_probe(struct platform_device *pdev)
dev_name(&pdev->dev));
bus->parent = &pdev->dev;
- bus->irq = devm_kmalloc_array(&pdev->dev, PHY_MAX_ADDR, sizeof(int),
- GFP_KERNEL);
- if (!bus->irq)
- return -ENOMEM;
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bus->irq[i] = PHY_POLL;
-
dev = bus->priv;
dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (!dev->regs) {
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index ed622fa29dfa..fabc8df40392 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -110,9 +110,17 @@
#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2))
#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff
#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00
+#define MVNETA_CPU_RXQ_ACCESS(rxq) BIT(rxq)
+#define MVNETA_CPU_TXQ_ACCESS(txq) BIT(txq + 8)
#define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2))
-/* Exception Interrupt Port/Queue Cause register */
+/* Exception Interrupt Port/Queue Cause register
+ *
+ * Their behavior depend of the mapping done using the PCPX2Q
+ * registers. For a given CPU if the bit associated to a queue is not
+ * set, then for the register a read from this CPU will always return
+ * 0 and a write won't do anything
+ */
#define MVNETA_INTR_NEW_CAUSE 0x25a0
#define MVNETA_INTR_NEW_MASK 0x25a4
@@ -254,6 +262,11 @@
#define MVNETA_TX_MTU_MAX 0x3ffff
+/* The RSS lookup table actually has 256 entries but we do not use
+ * them yet
+ */
+#define MVNETA_RSS_LU_TABLE_SIZE 1
+
/* TSO header size */
#define TSO_HEADER_SIZE 128
@@ -356,6 +369,7 @@ struct mvneta_port {
struct mvneta_tx_queue *txqs;
struct net_device *dev;
struct notifier_block cpu_notifier;
+ int rxq_def;
/* Core clock */
struct clk *clk;
@@ -371,9 +385,11 @@ struct mvneta_port {
unsigned int duplex;
unsigned int speed;
unsigned int tx_csum_limit;
- int use_inband_status:1;
+ unsigned int use_inband_status:1;
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
+
+ u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
};
/* The mvneta_tx_desc and mvneta_rx_desc structures describe the
@@ -499,6 +515,9 @@ struct mvneta_tx_queue {
/* DMA address of TSO headers */
dma_addr_t tso_hdrs_phys;
+
+ /* Affinity mask for CPUs*/
+ cpumask_t affinity_mask;
};
struct mvneta_rx_queue {
@@ -819,7 +838,13 @@ static void mvneta_port_up(struct mvneta_port *pp)
mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
/* Enable all initialized RXQs. */
- mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def));
+ for (queue = 0; queue < rxq_number; queue++) {
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
+
+ if (rxq->descs != NULL)
+ q_map |= (1 << queue);
+ }
+ mvreg_write(pp, MVNETA_RXQ_CMD, q_map);
}
/* Stop the Ethernet port activity */
@@ -973,6 +998,44 @@ static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue)
mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
}
+static void mvneta_set_autoneg(struct mvneta_port *pp, int enable)
+{
+ u32 val;
+
+ if (enable) {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
+ MVNETA_GMAC_FORCE_LINK_DOWN |
+ MVNETA_GMAC_AN_FLOW_CTRL_EN);
+ val |= MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_AN_SPEED_EN |
+ MVNETA_GMAC_AN_DUPLEX_EN;
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+ val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+ val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+ } else {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_AN_SPEED_EN |
+ MVNETA_GMAC_AN_DUPLEX_EN);
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+ val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+ val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+ }
+}
+
/* This method sets defaults to the NETA port:
* Clears interrupt Cause and Mask registers.
* Clears all MAC tables.
@@ -987,6 +1050,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
int cpu;
int queue;
u32 val;
+ int max_cpu = num_present_cpus();
/* Clear all Cause registers */
mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0);
@@ -1002,13 +1066,33 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
/* Enable MBUS Retry bit16 */
mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20);
- /* Set CPU queue access map - all CPUs have access to all RX
- * queues and to all TX queues
+ /* Set CPU queue access map. CPUs are assigned to the RX and
+ * TX queues modulo their number. If there is only one TX
+ * queue then it is assigned to the CPU associated to the
+ * default RX queue.
*/
- for_each_present_cpu(cpu)
- mvreg_write(pp, MVNETA_CPU_MAP(cpu),
- (MVNETA_CPU_RXQ_ACCESS_ALL_MASK |
- MVNETA_CPU_TXQ_ACCESS_ALL_MASK));
+ for_each_present_cpu(cpu) {
+ int rxq_map = 0, txq_map = 0;
+ int rxq, txq;
+
+ for (rxq = 0; rxq < rxq_number; rxq++)
+ if ((rxq % max_cpu) == cpu)
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
+
+ for (txq = 0; txq < txq_number; txq++)
+ if ((txq % max_cpu) == cpu)
+ txq_map |= MVNETA_CPU_TXQ_ACCESS(txq);
+
+ /* With only one TX queue we configure a special case
+ * which will allow to get all the irq on a single
+ * CPU
+ */
+ if (txq_number == 1)
+ txq_map = (cpu == pp->rxq_def) ?
+ MVNETA_CPU_TXQ_ACCESS(1) : 0;
+
+ mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
+ }
/* Reset RX and TX DMAs */
mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET);
@@ -1029,7 +1113,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
mvreg_write(pp, MVNETA_ACC_MODE, val);
/* Update val of portCfg register accordingly with all RxQueue types */
- val = MVNETA_PORT_CONFIG_DEFL_VALUE(rxq_def);
+ val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def);
mvreg_write(pp, MVNETA_PORT_CONFIG, val);
val = 0;
@@ -1058,26 +1142,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
val &= ~MVNETA_PHY_POLLING_ENABLE;
mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
- if (pp->use_inband_status) {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
- MVNETA_GMAC_FORCE_LINK_DOWN |
- MVNETA_GMAC_AN_FLOW_CTRL_EN);
- val |= MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
- val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
- } else {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- }
-
+ mvneta_set_autoneg(pp, pp->use_inband_status);
mvneta_set_ucast_table(pp, -1);
mvneta_set_special_mcast_table(pp, -1);
mvneta_set_other_mcast_table(pp, -1);
@@ -2082,19 +2147,19 @@ static void mvneta_set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) {
/* Accept all: Multicast + Unicast */
mvneta_rx_unicast_promisc_set(pp, 1);
- mvneta_set_ucast_table(pp, rxq_def);
- mvneta_set_special_mcast_table(pp, rxq_def);
- mvneta_set_other_mcast_table(pp, rxq_def);
+ mvneta_set_ucast_table(pp, pp->rxq_def);
+ mvneta_set_special_mcast_table(pp, pp->rxq_def);
+ mvneta_set_other_mcast_table(pp, pp->rxq_def);
} else {
/* Accept single Unicast */
mvneta_rx_unicast_promisc_set(pp, 0);
mvneta_set_ucast_table(pp, -1);
- mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
+ mvneta_mac_addr_set(pp, dev->dev_addr, pp->rxq_def);
if (dev->flags & IFF_ALLMULTI) {
/* Accept all multicast */
- mvneta_set_special_mcast_table(pp, rxq_def);
- mvneta_set_other_mcast_table(pp, rxq_def);
+ mvneta_set_special_mcast_table(pp, pp->rxq_def);
+ mvneta_set_other_mcast_table(pp, pp->rxq_def);
} else {
/* Accept only initialized multicast */
mvneta_set_special_mcast_table(pp, -1);
@@ -2103,7 +2168,7 @@ static void mvneta_set_rx_mode(struct net_device *dev)
if (!netdev_mc_empty(dev)) {
netdev_for_each_mc_addr(ha, dev) {
mvneta_mcast_addr_set(pp, ha->addr,
- rxq_def);
+ pp->rxq_def);
}
}
}
@@ -2154,6 +2219,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
{
int rx_done = 0;
u32 cause_rx_tx;
+ int rx_queue;
struct mvneta_port *pp = netdev_priv(napi->dev);
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
@@ -2185,8 +2251,15 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
/* For the case where the last mvneta_poll did not process all
* RX packets
*/
+ rx_queue = fls(((cause_rx_tx >> 8) & 0xff));
+
cause_rx_tx |= port->cause_rx_tx;
- rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]);
+
+ if (rx_queue) {
+ rx_queue = rx_queue - 1;
+ rx_done = mvneta_rx(pp, budget, &pp->rxqs[rx_queue]);
+ }
+
budget -= rx_done;
if (budget > 0) {
@@ -2303,6 +2376,8 @@ static void mvneta_rxq_deinit(struct mvneta_port *pp,
static int mvneta_txq_init(struct mvneta_port *pp,
struct mvneta_tx_queue *txq)
{
+ int cpu;
+
txq->size = pp->tx_ring_size;
/* A queue must always have room for at least one skb.
@@ -2355,6 +2430,14 @@ static int mvneta_txq_init(struct mvneta_port *pp,
}
mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
+ /* Setup XPS mapping */
+ if (txq_number > 1)
+ cpu = txq->id % num_present_cpus();
+ else
+ cpu = pp->rxq_def % num_present_cpus();
+ cpumask_set_cpu(cpu, &txq->affinity_mask);
+ netif_set_xps_queue(pp->dev, &txq->affinity_mask, txq->id);
+
return 0;
}
@@ -2399,19 +2482,27 @@ static void mvneta_cleanup_txqs(struct mvneta_port *pp)
/* Cleanup all Rx queues */
static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
{
- mvneta_rxq_deinit(pp, &pp->rxqs[rxq_def]);
+ int queue;
+
+ for (queue = 0; queue < txq_number; queue++)
+ mvneta_rxq_deinit(pp, &pp->rxqs[queue]);
}
/* Init all Rx queues */
static int mvneta_setup_rxqs(struct mvneta_port *pp)
{
- int err = mvneta_rxq_init(pp, &pp->rxqs[rxq_def]);
- if (err) {
- netdev_err(pp->dev, "%s: can't create rxq=%d\n",
- __func__, rxq_def);
- mvneta_cleanup_rxqs(pp);
- return err;
+ int queue;
+
+ for (queue = 0; queue < rxq_number; queue++) {
+ int err = mvneta_rxq_init(pp, &pp->rxqs[queue]);
+
+ if (err) {
+ netdev_err(pp->dev, "%s: can't create rxq=%d\n",
+ __func__, queue);
+ mvneta_cleanup_rxqs(pp);
+ return err;
+ }
}
return 0;
@@ -2435,6 +2526,31 @@ static int mvneta_setup_txqs(struct mvneta_port *pp)
return 0;
}
+static void mvneta_percpu_unmask_interrupt(void *arg)
+{
+ struct mvneta_port *pp = arg;
+
+ /* All the queue are unmasked, but actually only the ones
+ * maped to this CPU will be unmasked
+ */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK,
+ MVNETA_RX_INTR_MASK_ALL |
+ MVNETA_TX_INTR_MASK_ALL |
+ MVNETA_MISCINTR_INTR_MASK);
+}
+
+static void mvneta_percpu_mask_interrupt(void *arg)
+{
+ struct mvneta_port *pp = arg;
+
+ /* All the queue are masked, but actually only the ones
+ * maped to this CPU will be masked
+ */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
+}
+
static void mvneta_start_dev(struct mvneta_port *pp)
{
unsigned int cpu;
@@ -2452,11 +2568,10 @@ static void mvneta_start_dev(struct mvneta_port *pp)
napi_enable(&port->napi);
}
- /* Unmask interrupts */
- mvreg_write(pp, MVNETA_INTR_NEW_MASK,
- MVNETA_RX_INTR_MASK(rxq_number) |
- MVNETA_TX_INTR_MASK(txq_number) |
- MVNETA_MISCINTR_INTR_MASK);
+ /* Unmask interrupts. It has to be done from each CPU */
+ for_each_online_cpu(cpu)
+ smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt,
+ pp, true);
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE |
@@ -2615,7 +2730,7 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
mvneta_mac_addr_set(pp, dev->dev_addr, -1);
/* Set new addr in hw */
- mvneta_mac_addr_set(pp, sockaddr->sa_data, rxq_def);
+ mvneta_mac_addr_set(pp, sockaddr->sa_data, pp->rxq_def);
eth_commit_mac_addr_change(dev, addr);
return 0;
@@ -2732,22 +2847,45 @@ static void mvneta_percpu_disable(void *arg)
static void mvneta_percpu_elect(struct mvneta_port *pp)
{
- int online_cpu_idx, cpu, i = 0;
+ int online_cpu_idx, max_cpu, cpu, i = 0;
- online_cpu_idx = rxq_def % num_online_cpus();
+ online_cpu_idx = pp->rxq_def % num_online_cpus();
+ max_cpu = num_present_cpus();
for_each_online_cpu(cpu) {
+ int rxq_map = 0, txq_map = 0;
+ int rxq;
+
+ for (rxq = 0; rxq < rxq_number; rxq++)
+ if ((rxq % max_cpu) == cpu)
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
+
if (i == online_cpu_idx)
- /* Enable per-CPU interrupt on the one CPU we
- * just elected
+ /* Map the default receive queue queue to the
+ * elected CPU
*/
- smp_call_function_single(cpu, mvneta_percpu_enable,
- pp, true);
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(pp->rxq_def);
+
+ /* We update the TX queue map only if we have one
+ * queue. In this case we associate the TX queue to
+ * the CPU bound to the default RX queue
+ */
+ if (txq_number == 1)
+ txq_map = (i == online_cpu_idx) ?
+ MVNETA_CPU_TXQ_ACCESS(1) : 0;
else
- /* Disable per-CPU interrupt on all the other CPU */
- smp_call_function_single(cpu, mvneta_percpu_disable,
- pp, true);
+ txq_map = mvreg_read(pp, MVNETA_CPU_MAP(cpu)) &
+ MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
+
+ mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
+
+ /* Update the interrupt mask on each CPU according the
+ * new mapping
+ */
+ smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt,
+ pp, true);
i++;
+
}
};
@@ -2782,12 +2920,22 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb,
mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
napi_enable(&port->napi);
+
+ /* Enable per-CPU interrupts on the CPU that is
+ * brought up.
+ */
+ smp_call_function_single(cpu, mvneta_percpu_enable,
+ pp, true);
+
/* Enable per-CPU interrupt on the one CPU we care
* about.
*/
mvneta_percpu_elect(pp);
- /* Unmask all ethernet port interrupts */
+ /* Unmask all ethernet port interrupts, as this
+ * notifier is called for each CPU then the CPU to
+ * Queue mapping is applied
+ */
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
MVNETA_RX_INTR_MASK(rxq_number) |
MVNETA_TX_INTR_MASK(txq_number) |
@@ -2838,7 +2986,7 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb,
static int mvneta_open(struct net_device *dev)
{
struct mvneta_port *pp = netdev_priv(dev);
- int ret;
+ int ret, cpu;
pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
@@ -2868,8 +3016,13 @@ static int mvneta_open(struct net_device *dev)
*/
mvneta_percpu_disable(pp);
- /* Elect a CPU to handle our RX queue interrupt */
- mvneta_percpu_elect(pp);
+ /* Enable per-CPU interrupt on all the CPU to handle our RX
+ * queue interrupts
+ */
+ for_each_online_cpu(cpu)
+ smp_call_function_single(cpu, mvneta_percpu_enable,
+ pp, true);
+
/* Register a CPU notifier to handle the case where our CPU
* might be taken offline.
@@ -2943,10 +3096,43 @@ int mvneta_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct mvneta_port *pp = netdev_priv(dev);
+ struct phy_device *phydev = pp->phy_dev;
- if (!pp->phy_dev)
+ if (!phydev)
return -ENODEV;
+ if ((cmd->autoneg == AUTONEG_ENABLE) != pp->use_inband_status) {
+ u32 val;
+
+ mvneta_set_autoneg(pp, cmd->autoneg == AUTONEG_ENABLE);
+
+ if (cmd->autoneg == AUTONEG_DISABLE) {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX);
+
+ if (phydev->duplex)
+ val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+
+ if (phydev->speed == SPEED_1000)
+ val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
+ else if (phydev->speed == SPEED_100)
+ val |= MVNETA_GMAC_CONFIG_MII_SPEED;
+
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ }
+
+ pp->use_inband_status = (cmd->autoneg == AUTONEG_ENABLE);
+ netdev_info(pp->dev, "autoneg status set to %i\n",
+ pp->use_inband_status);
+
+ if (netif_running(dev)) {
+ mvneta_port_down(pp);
+ mvneta_port_up(pp);
+ }
+ }
+
return phy_ethtool_sset(pp->phy_dev, cmd);
}
@@ -3098,6 +3284,106 @@ static int mvneta_ethtool_get_sset_count(struct net_device *dev, int sset)
return -EOPNOTSUPP;
}
+static u32 mvneta_ethtool_get_rxfh_indir_size(struct net_device *dev)
+{
+ return MVNETA_RSS_LU_TABLE_SIZE;
+}
+
+static int mvneta_ethtool_get_rxnfc(struct net_device *dev,
+ struct ethtool_rxnfc *info,
+ u32 *rules __always_unused)
+{
+ switch (info->cmd) {
+ case ETHTOOL_GRXRINGS:
+ info->data = rxq_number;
+ return 0;
+ case ETHTOOL_GRXFH:
+ return -EOPNOTSUPP;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mvneta_config_rss(struct mvneta_port *pp)
+{
+ int cpu;
+ u32 val;
+
+ netif_tx_stop_all_queues(pp->dev);
+
+ for_each_online_cpu(cpu)
+ smp_call_function_single(cpu, mvneta_percpu_mask_interrupt,
+ pp, true);
+
+ /* We have to synchronise on the napi of each CPU */
+ for_each_online_cpu(cpu) {
+ struct mvneta_pcpu_port *pcpu_port =
+ per_cpu_ptr(pp->ports, cpu);
+
+ napi_synchronize(&pcpu_port->napi);
+ napi_disable(&pcpu_port->napi);
+ }
+
+ pp->rxq_def = pp->indir[0];
+
+ /* Update unicast mapping */
+ mvneta_set_rx_mode(pp->dev);
+
+ /* Update val of portCfg register accordingly with all RxQueue types */
+ val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def);
+ mvreg_write(pp, MVNETA_PORT_CONFIG, val);
+
+ /* Update the elected CPU matching the new rxq_def */
+ mvneta_percpu_elect(pp);
+
+ /* We have to synchronise on the napi of each CPU */
+ for_each_online_cpu(cpu) {
+ struct mvneta_pcpu_port *pcpu_port =
+ per_cpu_ptr(pp->ports, cpu);
+
+ napi_enable(&pcpu_port->napi);
+ }
+
+ netif_tx_start_all_queues(pp->dev);
+
+ return 0;
+}
+
+static int mvneta_ethtool_set_rxfh(struct net_device *dev, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ /* We require at least one supported parameter to be changed
+ * and no change in any of the unsupported parameters
+ */
+ if (key ||
+ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+
+ if (!indir)
+ return 0;
+
+ memcpy(pp->indir, indir, MVNETA_RSS_LU_TABLE_SIZE);
+
+ return mvneta_config_rss(pp);
+}
+
+static int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ if (!indir)
+ return 0;
+
+ memcpy(indir, pp->indir, MVNETA_RSS_LU_TABLE_SIZE);
+
+ return 0;
+}
+
static const struct net_device_ops mvneta_netdev_ops = {
.ndo_open = mvneta_open,
.ndo_stop = mvneta_stop,
@@ -3122,6 +3408,10 @@ const struct ethtool_ops mvneta_eth_tool_ops = {
.get_strings = mvneta_ethtool_get_strings,
.get_ethtool_stats = mvneta_ethtool_get_stats,
.get_sset_count = mvneta_ethtool_get_sset_count,
+ .get_rxfh_indir_size = mvneta_ethtool_get_rxfh_indir_size,
+ .get_rxnfc = mvneta_ethtool_get_rxnfc,
+ .get_rxfh = mvneta_ethtool_get_rxfh,
+ .set_rxfh = mvneta_ethtool_set_rxfh,
};
/* Initialize hw */
@@ -3230,9 +3520,6 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)
return -EINVAL;
}
- if (pp->use_inband_status)
- ctrl |= MVNETA_GMAC2_INBAND_AN_ENABLE;
-
/* Cancel Port Reset */
ctrl &= ~MVNETA_GMAC2_PORT_RESET;
mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl);
@@ -3314,6 +3601,10 @@ static int mvneta_probe(struct platform_device *pdev)
strcmp(managed, "in-band-status") == 0);
pp->cpu_notifier.notifier_call = mvneta_percpu_notifier;
+ pp->rxq_def = rxq_def;
+
+ pp->indir[0] = rxq_def;
+
pp->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pp->clk)) {
err = PTR_ERR(pp->clk);
@@ -3423,7 +3714,7 @@ static int mvneta_probe(struct platform_device *pdev)
mvneta_fixed_link_update(pp, phy);
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
}
return 0;
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 5606a043063e..ec0a22119e09 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -4380,7 +4380,7 @@ static netdev_features_t sky2_fix_features(struct net_device *dev,
*/
if (dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U) {
netdev_info(dev, "checksum offload not possible with jumbo frames\n");
- features &= ~(NETIF_F_TSO|NETIF_F_SG|NETIF_F_ALL_CSUM);
+ features &= ~(NETIF_F_TSO | NETIF_F_SG | NETIF_F_CSUM_MASK);
}
/* Some hardware requires receive checksum for RSS to work. */
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
index eb8a4988de63..af975a2b74c6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
@@ -155,13 +155,11 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
cq->mcq.comp = cq->is_tx ? mlx4_en_tx_irq : mlx4_en_rx_irq;
cq->mcq.event = mlx4_en_cq_event;
- if (cq->is_tx) {
- netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq,
- NAPI_POLL_WEIGHT);
- } else {
+ if (cq->is_tx)
+ netif_tx_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq,
+ NAPI_POLL_WEIGHT);
+ else
netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64);
- napi_hash_add(&cq->napi);
- }
napi_enable(&cq->napi);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index ddb5541882f5..dd84cabb2a51 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -337,11 +337,7 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset)
case ETH_SS_STATS:
return bitmap_iterator_count(&it) +
(priv->tx_ring_num * 2) +
-#ifdef CONFIG_NET_RX_BUSY_POLL
- (priv->rx_ring_num * 5);
-#else
(priv->rx_ring_num * 2);
-#endif
case ETH_SS_TEST:
return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags
& MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2;
@@ -408,11 +404,6 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev,
for (i = 0; i < priv->rx_ring_num; i++) {
data[index++] = priv->rx_ring[i]->packets;
data[index++] = priv->rx_ring[i]->bytes;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- data[index++] = priv->rx_ring[i]->yields;
- data[index++] = priv->rx_ring[i]->misses;
- data[index++] = priv->rx_ring[i]->cleaned;
-#endif
}
spin_unlock_bh(&priv->stats_lock);
@@ -486,14 +477,6 @@ static void mlx4_en_get_strings(struct net_device *dev,
"rx%d_packets", i);
sprintf(data + (index++) * ETH_GSTRING_LEN,
"rx%d_bytes", i);
-#ifdef CONFIG_NET_RX_BUSY_POLL
- sprintf(data + (index++) * ETH_GSTRING_LEN,
- "rx%d_napi_yield", i);
- sprintf(data + (index++) * ETH_GSTRING_LEN,
- "rx%d_misses", i);
- sprintf(data + (index++) * ETH_GSTRING_LEN,
- "rx%d_cleaned", i);
-#endif
}
break;
case ETH_SS_PRIV_FLAGS:
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 7869f97de5da..0c7e3f69a73b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -69,34 +69,6 @@ int mlx4_en_setup_tc(struct net_device *dev, u8 up)
return 0;
}
-#ifdef CONFIG_NET_RX_BUSY_POLL
-/* must be called with local_bh_disable()d */
-static int mlx4_en_low_latency_recv(struct napi_struct *napi)
-{
- struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi);
- struct net_device *dev = cq->dev;
- struct mlx4_en_priv *priv = netdev_priv(dev);
- struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring];
- int done;
-
- if (!priv->port_up)
- return LL_FLUSH_FAILED;
-
- if (!mlx4_en_cq_lock_poll(cq))
- return LL_FLUSH_BUSY;
-
- done = mlx4_en_process_rx_cq(dev, cq, 4);
- if (likely(done))
- rx_ring->cleaned += done;
- else
- rx_ring->misses++;
-
- mlx4_en_cq_unlock_poll(cq);
-
- return done;
-}
-#endif /* CONFIG_NET_RX_BUSY_POLL */
-
#ifdef CONFIG_RFS_ACCEL
struct mlx4_en_filter {
@@ -1561,8 +1533,6 @@ int mlx4_en_start_port(struct net_device *dev)
for (i = 0; i < priv->rx_ring_num; i++) {
cq = priv->rx_cq[i];
- mlx4_en_cq_init_lock(cq);
-
err = mlx4_en_init_affinity_hint(priv, i);
if (err) {
en_err(priv, "Failed preparing IRQ affinity hint\n");
@@ -1859,13 +1829,6 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
for (i = 0; i < priv->rx_ring_num; i++) {
struct mlx4_en_cq *cq = priv->rx_cq[i];
- local_bh_disable();
- while (!mlx4_en_cq_lock_napi(cq)) {
- pr_info("CQ %d locked\n", i);
- mdelay(1);
- }
- local_bh_enable();
-
napi_synchronize(&cq->napi);
mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
mlx4_en_deactivate_cq(priv, cq);
@@ -2507,9 +2470,6 @@ static const struct net_device_ops mlx4_netdev_ops = {
#ifdef CONFIG_RFS_ACCEL
.ndo_rx_flow_steer = mlx4_en_filter_rfs,
#endif
-#ifdef CONFIG_NET_RX_BUSY_POLL
- .ndo_busy_poll = mlx4_en_low_latency_recv,
-#endif
.ndo_get_phys_port_id = mlx4_en_get_phys_port_id,
#ifdef CONFIG_MLX4_EN_VXLAN
.ndo_add_vxlan_port = mlx4_en_add_vxlan_port,
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index e7a5000aa12c..41440b2b20a3 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -873,10 +873,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
* - TCP/IP (v4)
* - without IP options
* - not an IP fragment
- * - no LLS polling in progress
*/
- if (!mlx4_en_cq_busy_polling(cq) &&
- (dev->features & NETIF_F_GRO)) {
+ if (dev->features & NETIF_F_GRO) {
struct sk_buff *gro_skb = napi_get_frags(&cq->napi);
if (!gro_skb)
goto next;
@@ -927,7 +925,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
PKT_HASH_TYPE_L3);
skb_record_rx_queue(gro_skb, cq->ring);
- skb_mark_napi_id(gro_skb, &cq->napi);
if (ring->hwtstamp_rx_filter == HWTSTAMP_FILTER_ALL) {
timestamp = mlx4_en_get_cqe_ts(cqe);
@@ -990,13 +987,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
timestamp);
}
- skb_mark_napi_id(skb, &cq->napi);
-
- if (!mlx4_en_cq_busy_polling(cq))
- napi_gro_receive(&cq->napi, skb);
- else
- netif_receive_skb(skb);
-
+ napi_gro_receive(&cq->napi, skb);
next:
for (nr = 0; nr < priv->num_frags; nr++)
mlx4_en_free_frag(priv, frags, nr);
@@ -1038,13 +1029,8 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
struct mlx4_en_priv *priv = netdev_priv(dev);
int done;
- if (!mlx4_en_cq_lock_napi(cq))
- return budget;
-
done = mlx4_en_process_rx_cq(dev, cq, budget);
- mlx4_en_cq_unlock_napi(cq);
-
/* If we used up all the quota - we're probably not done yet... */
if (done == budget) {
const struct cpumask *aff;
diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index 603d1c3d3b2e..4696053165f8 100644
--- a/drivers/net/ethernet/mellanox/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -151,6 +151,17 @@ void mlx4_gen_slave_eqe(struct work_struct *work)
eqe = next_slave_event_eqe(slave_eq)) {
slave = eqe->slave_id;
+ if (eqe->type == MLX4_EVENT_TYPE_PORT_CHANGE &&
+ eqe->subtype == MLX4_PORT_CHANGE_SUBTYPE_DOWN &&
+ mlx4_is_bonded(dev)) {
+ struct mlx4_port_cap port_cap;
+
+ if (!mlx4_QUERY_PORT(dev, 1, &port_cap) && port_cap.link_state)
+ goto consume;
+
+ if (!mlx4_QUERY_PORT(dev, 2, &port_cap) && port_cap.link_state)
+ goto consume;
+ }
/* All active slaves need to receive the event */
if (slave == ALL_SLAVES) {
for (i = 0; i <= dev->persist->num_vfs; i++) {
@@ -174,6 +185,7 @@ void mlx4_gen_slave_eqe(struct work_struct *work)
mlx4_warn(dev, "Failed to generate event for slave %d\n",
slave);
}
+consume:
++slave_eq->cons;
}
}
@@ -594,7 +606,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
break;
for (i = 0; i < dev->persist->num_vfs + 1;
i++) {
- if (!test_bit(i, slaves_port.slaves))
+ int reported_port = mlx4_is_bonded(dev) ? 1 : mlx4_phys_to_slave_port(dev, i, port);
+
+ if (!test_bit(i, slaves_port.slaves) && !mlx4_is_bonded(dev))
continue;
if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) {
if (i == mlx4_master_func_num(dev))
@@ -606,7 +620,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
eqe->event.port_change.port =
cpu_to_be32(
(be32_to_cpu(eqe->event.port_change.port) & 0xFFFFFFF)
- | (mlx4_phys_to_slave_port(dev, i, port) << 28));
+ | (reported_port << 28));
mlx4_slave_event(dev, i, eqe);
}
} else { /* IB port */
@@ -636,7 +650,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
for (i = 0;
i < dev->persist->num_vfs + 1;
i++) {
- if (!test_bit(i, slaves_port.slaves))
+ int reported_port = mlx4_is_bonded(dev) ? 1 : mlx4_phys_to_slave_port(dev, i, port);
+
+ if (!test_bit(i, slaves_port.slaves) && !mlx4_is_bonded(dev))
continue;
if (i == mlx4_master_func_num(dev))
continue;
@@ -645,7 +661,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
eqe->event.port_change.port =
cpu_to_be32(
(be32_to_cpu(eqe->event.port_change.port) & 0xFFFFFFF)
- | (mlx4_phys_to_slave_port(dev, i, port) << 28));
+ | (reported_port << 28));
mlx4_slave_event(dev, i, eqe);
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index 90db94e83fde..2c2baab9d880 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -1104,6 +1104,7 @@ int mlx4_QUERY_PORT(struct mlx4_dev *dev, int port, struct mlx4_port_cap *port_c
goto out;
MLX4_GET(field, outbox, QUERY_PORT_SUPPORTED_TYPE_OFFSET);
+ port_cap->link_state = (field & 0x80) >> 7;
port_cap->supported_port_types = field & 3;
port_cap->suggested_type = (field >> 3) & 1;
port_cap->default_sense = (field >> 4) & 1;
@@ -1310,6 +1311,15 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
port_type |= MLX4_PORT_LINK_UP_MASK;
else if (IFLA_VF_LINK_STATE_DISABLE == admin_link_state)
port_type &= ~MLX4_PORT_LINK_UP_MASK;
+ else if (IFLA_VF_LINK_STATE_AUTO == admin_link_state && mlx4_is_bonded(dev)) {
+ int other_port = (port == 1) ? 2 : 1;
+ struct mlx4_port_cap port_cap;
+
+ err = mlx4_QUERY_PORT(dev, other_port, &port_cap);
+ if (err)
+ goto out;
+ port_type |= (port_cap.link_state << 7);
+ }
MLX4_PUT(outbox->buf, port_type,
QUERY_PORT_SUPPORTED_TYPE_OFFSET);
@@ -1325,7 +1335,7 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
MLX4_PUT(outbox->buf, short_field,
QUERY_PORT_CUR_MAX_PKEY_OFFSET);
}
-
+out:
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h
index 08de5555c2f4..7ea258af636a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.h
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.h
@@ -44,6 +44,7 @@ struct mlx4_mod_stat_cfg {
};
struct mlx4_port_cap {
+ u8 link_state;
u8 supported_port_types;
u8 suggested_type;
u8 default_sense;
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 31c491e02e69..f1b6d219e445 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -1221,6 +1221,76 @@ err_set_port:
return err ? err : count;
}
+/* bond for multi-function device */
+#define MAX_MF_BOND_ALLOWED_SLAVES 63
+static int mlx4_mf_bond(struct mlx4_dev *dev)
+{
+ int err = 0;
+ struct mlx4_slaves_pport slaves_port1;
+ struct mlx4_slaves_pport slaves_port2;
+ DECLARE_BITMAP(slaves_port_1_2, MLX4_MFUNC_MAX);
+
+ slaves_port1 = mlx4_phys_to_slaves_pport(dev, 1);
+ slaves_port2 = mlx4_phys_to_slaves_pport(dev, 2);
+ bitmap_and(slaves_port_1_2,
+ slaves_port1.slaves, slaves_port2.slaves,
+ dev->persist->num_vfs + 1);
+
+ /* only single port vfs are allowed */
+ if (bitmap_weight(slaves_port_1_2, dev->persist->num_vfs + 1) > 1) {
+ mlx4_warn(dev, "HA mode unsupported for dual ported VFs\n");
+ return -EINVAL;
+ }
+
+ /* limit on maximum allowed VFs */
+ if ((bitmap_weight(slaves_port1.slaves, dev->persist->num_vfs + 1) +
+ bitmap_weight(slaves_port2.slaves, dev->persist->num_vfs + 1)) >
+ MAX_MF_BOND_ALLOWED_SLAVES)
+ return -EINVAL;
+
+ if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) {
+ mlx4_warn(dev, "HA mode unsupported for NON DMFS steering\n");
+ return -EINVAL;
+ }
+
+ err = mlx4_bond_mac_table(dev);
+ if (err)
+ return err;
+ err = mlx4_bond_vlan_table(dev);
+ if (err)
+ goto err1;
+ err = mlx4_bond_fs_rules(dev);
+ if (err)
+ goto err2;
+
+ return 0;
+err2:
+ (void)mlx4_unbond_vlan_table(dev);
+err1:
+ (void)mlx4_unbond_mac_table(dev);
+ return err;
+}
+
+static int mlx4_mf_unbond(struct mlx4_dev *dev)
+{
+ int ret, ret1;
+
+ ret = mlx4_unbond_fs_rules(dev);
+ if (ret)
+ mlx4_warn(dev, "multifunction unbond for flow rules failedi (%d)\n", ret);
+ ret1 = mlx4_unbond_mac_table(dev);
+ if (ret1) {
+ mlx4_warn(dev, "multifunction unbond for MAC table failed (%d)\n", ret1);
+ ret = ret1;
+ }
+ ret1 = mlx4_unbond_vlan_table(dev);
+ if (ret1) {
+ mlx4_warn(dev, "multifunction unbond for VLAN table failed (%d)\n", ret1);
+ ret = ret1;
+ }
+ return ret;
+}
+
int mlx4_bond(struct mlx4_dev *dev)
{
int ret = 0;
@@ -1228,16 +1298,23 @@ int mlx4_bond(struct mlx4_dev *dev)
mutex_lock(&priv->bond_mutex);
- if (!mlx4_is_bonded(dev))
+ if (!mlx4_is_bonded(dev)) {
ret = mlx4_do_bond(dev, true);
- else
- ret = 0;
+ if (ret)
+ mlx4_err(dev, "Failed to bond device: %d\n", ret);
+ if (!ret && mlx4_is_master(dev)) {
+ ret = mlx4_mf_bond(dev);
+ if (ret) {
+ mlx4_err(dev, "bond for multifunction failed\n");
+ mlx4_do_bond(dev, false);
+ }
+ }
+ }
mutex_unlock(&priv->bond_mutex);
- if (ret)
- mlx4_err(dev, "Failed to bond device: %d\n", ret);
- else
+ if (!ret)
mlx4_dbg(dev, "Device is bonded\n");
+
return ret;
}
EXPORT_SYMBOL_GPL(mlx4_bond);
@@ -1249,14 +1326,24 @@ int mlx4_unbond(struct mlx4_dev *dev)
mutex_lock(&priv->bond_mutex);
- if (mlx4_is_bonded(dev))
+ if (mlx4_is_bonded(dev)) {
+ int ret2 = 0;
+
ret = mlx4_do_bond(dev, false);
+ if (ret)
+ mlx4_err(dev, "Failed to unbond device: %d\n", ret);
+ if (mlx4_is_master(dev))
+ ret2 = mlx4_mf_unbond(dev);
+ if (ret2) {
+ mlx4_warn(dev, "Failed to unbond device for multifunction (%d)\n", ret2);
+ ret = ret2;
+ }
+ }
mutex_unlock(&priv->bond_mutex);
- if (ret)
- mlx4_err(dev, "Failed to unbond device: %d\n", ret);
- else
+ if (!ret)
mlx4_dbg(dev, "Device is unbonded\n");
+
return ret;
}
EXPORT_SYMBOL_GPL(mlx4_unbond);
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index e1cf9036af22..2404c22ad2b2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -736,6 +736,7 @@ struct mlx4_catas_err {
struct mlx4_mac_table {
__be64 entries[MLX4_MAX_MAC_NUM];
int refs[MLX4_MAX_MAC_NUM];
+ bool is_dup[MLX4_MAX_MAC_NUM];
struct mutex mutex;
int total;
int max;
@@ -758,6 +759,7 @@ struct mlx4_roce_gid_table {
struct mlx4_vlan_table {
__be32 entries[MLX4_MAX_VLAN_NUM];
int refs[MLX4_MAX_VLAN_NUM];
+ int is_dup[MLX4_MAX_VLAN_NUM];
struct mutex mutex;
int total;
int max;
@@ -1225,6 +1227,10 @@ void mlx4_init_roce_gid_table(struct mlx4_dev *dev,
struct mlx4_roce_gid_table *table);
void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan);
int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index);
+int mlx4_bond_vlan_table(struct mlx4_dev *dev);
+int mlx4_unbond_vlan_table(struct mlx4_dev *dev);
+int mlx4_bond_mac_table(struct mlx4_dev *dev);
+int mlx4_unbond_mac_table(struct mlx4_dev *dev);
int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz);
/* resource tracker functions*/
@@ -1385,6 +1391,8 @@ int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port);
int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave);
int mlx4_config_mad_demux(struct mlx4_dev *dev);
int mlx4_do_bond(struct mlx4_dev *dev, bool enable);
+int mlx4_bond_fs_rules(struct mlx4_dev *dev);
+int mlx4_unbond_fs_rules(struct mlx4_dev *dev);
enum mlx4_zone_flags {
MLX4_ZONE_ALLOW_ALLOC_FROM_LOWER_PRIO = 1UL << 0,
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index c41f15102ae0..35de7d2e6b34 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -320,11 +320,6 @@ struct mlx4_en_rx_ring {
void *rx_info;
unsigned long bytes;
unsigned long packets;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned long yields;
- unsigned long misses;
- unsigned long cleaned;
-#endif
unsigned long csum_ok;
unsigned long csum_none;
unsigned long csum_complete;
@@ -347,18 +342,6 @@ struct mlx4_en_cq {
struct mlx4_cqe *buf;
#define MLX4_EN_OPCODE_ERROR 0x1e
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned int state;
-#define MLX4_EN_CQ_STATE_IDLE 0
-#define MLX4_EN_CQ_STATE_NAPI 1 /* NAPI owns this CQ */
-#define MLX4_EN_CQ_STATE_POLL 2 /* poll owns this CQ */
-#define MLX4_CQ_LOCKED (MLX4_EN_CQ_STATE_NAPI | MLX4_EN_CQ_STATE_POLL)
-#define MLX4_EN_CQ_STATE_NAPI_YIELD 4 /* NAPI yielded this CQ */
-#define MLX4_EN_CQ_STATE_POLL_YIELD 8 /* poll yielded this CQ */
-#define CQ_YIELD (MLX4_EN_CQ_STATE_NAPI_YIELD | MLX4_EN_CQ_STATE_POLL_YIELD)
-#define CQ_USER_PEND (MLX4_EN_CQ_STATE_POLL | MLX4_EN_CQ_STATE_POLL_YIELD)
- spinlock_t poll_lock; /* protects from LLS/napi conflicts */
-#endif /* CONFIG_NET_RX_BUSY_POLL */
struct irq_desc *irq_desc;
};
@@ -622,115 +605,6 @@ static inline struct mlx4_cqe *mlx4_en_get_cqe(void *buf, int idx, int cqe_sz)
return buf + idx * cqe_sz;
}
-#ifdef CONFIG_NET_RX_BUSY_POLL
-static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq)
-{
- spin_lock_init(&cq->poll_lock);
- cq->state = MLX4_EN_CQ_STATE_IDLE;
-}
-
-/* called from the device poll rutine to get ownership of a cq */
-static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq)
-{
- int rc = true;
- spin_lock(&cq->poll_lock);
- if (cq->state & MLX4_CQ_LOCKED) {
- WARN_ON(cq->state & MLX4_EN_CQ_STATE_NAPI);
- cq->state |= MLX4_EN_CQ_STATE_NAPI_YIELD;
- rc = false;
- } else
- /* we don't care if someone yielded */
- cq->state = MLX4_EN_CQ_STATE_NAPI;
- spin_unlock(&cq->poll_lock);
- return rc;
-}
-
-/* returns true is someone tried to get the cq while napi had it */
-static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq)
-{
- int rc = false;
- spin_lock(&cq->poll_lock);
- WARN_ON(cq->state & (MLX4_EN_CQ_STATE_POLL |
- MLX4_EN_CQ_STATE_NAPI_YIELD));
-
- if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD)
- rc = true;
- cq->state = MLX4_EN_CQ_STATE_IDLE;
- spin_unlock(&cq->poll_lock);
- return rc;
-}
-
-/* called from mlx4_en_low_latency_poll() */
-static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq)
-{
- int rc = true;
- spin_lock_bh(&cq->poll_lock);
- if ((cq->state & MLX4_CQ_LOCKED)) {
- struct net_device *dev = cq->dev;
- struct mlx4_en_priv *priv = netdev_priv(dev);
- struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring];
-
- cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD;
- rc = false;
- rx_ring->yields++;
- } else
- /* preserve yield marks */
- cq->state |= MLX4_EN_CQ_STATE_POLL;
- spin_unlock_bh(&cq->poll_lock);
- return rc;
-}
-
-/* returns true if someone tried to get the cq while it was locked */
-static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq)
-{
- int rc = false;
- spin_lock_bh(&cq->poll_lock);
- WARN_ON(cq->state & (MLX4_EN_CQ_STATE_NAPI));
-
- if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD)
- rc = true;
- cq->state = MLX4_EN_CQ_STATE_IDLE;
- spin_unlock_bh(&cq->poll_lock);
- return rc;
-}
-
-/* true if a socket is polling, even if it did not get the lock */
-static inline bool mlx4_en_cq_busy_polling(struct mlx4_en_cq *cq)
-{
- WARN_ON(!(cq->state & MLX4_CQ_LOCKED));
- return cq->state & CQ_USER_PEND;
-}
-#else
-static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq)
-{
-}
-
-static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq)
-{
- return true;
-}
-
-static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq)
-{
- return false;
-}
-
-static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq)
-{
- return false;
-}
-
-static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq)
-{
- return false;
-}
-
-static inline bool mlx4_en_cq_busy_polling(struct mlx4_en_cq *cq)
-{
- return false;
-}
-#endif /* CONFIG_NET_RX_BUSY_POLL */
-
#define MLX4_EN_WOL_DO_MODIFY (1ULL << 63)
void mlx4_en_update_loopback_state(struct net_device *dev,
diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c
index c2b21313dba7..f2550425c251 100644
--- a/drivers/net/ethernet/mellanox/mlx4/port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/port.c
@@ -61,6 +61,7 @@ void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table)
for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
table->entries[i] = 0;
table->refs[i] = 0;
+ table->is_dup[i] = false;
}
table->max = 1 << dev->caps.log_num_macs;
table->total = 0;
@@ -74,6 +75,7 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table)
for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
table->entries[i] = 0;
table->refs[i] = 0;
+ table->is_dup[i] = false;
}
table->max = (1 << dev->caps.log_num_vlans) - MLX4_VLAN_REGULAR;
table->total = 0;
@@ -159,21 +161,94 @@ int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx)
}
EXPORT_SYMBOL_GPL(mlx4_find_cached_mac);
+static bool mlx4_need_mf_bond(struct mlx4_dev *dev)
+{
+ int i, num_eth_ports = 0;
+
+ if (!mlx4_is_mfunc(dev))
+ return false;
+ mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH)
+ ++num_eth_ports;
+
+ return (num_eth_ports == 2) ? true : false;
+}
+
int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
{
struct mlx4_port_info *info = &mlx4_priv(dev)->port[port];
struct mlx4_mac_table *table = &info->mac_table;
int i, err = 0;
int free = -1;
+ int free_for_dup = -1;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
+ bool need_mf_bond = mlx4_need_mf_bond(dev);
+ bool can_mf_bond = true;
+
+ mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d %s duplicate\n",
+ (unsigned long long)mac, port,
+ dup ? "with" : "without");
+
+ if (need_mf_bond) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
+
+ if (need_mf_bond) {
+ int index_at_port = -1;
+ int index_at_dup_port = -1;
- mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d\n",
- (unsigned long long) mac, port);
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))))
+ index_at_port = i;
+ if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i]))))
+ index_at_dup_port = i;
+ }
+
+ /* check that same mac is not in the tables at different indices */
+ if ((index_at_port != index_at_dup_port) &&
+ (index_at_port >= 0) &&
+ (index_at_dup_port >= 0))
+ can_mf_bond = false;
+
+ /* If the mac is already in the primary table, the slot must be
+ * available in the duplicate table as well.
+ */
+ if (index_at_port >= 0 && index_at_dup_port < 0 &&
+ dup_table->refs[index_at_port]) {
+ can_mf_bond = false;
+ }
+ /* If the mac is already in the duplicate table, check that the
+ * corresponding index is not occupied in the primary table, or
+ * the primary table already contains the mac at the same index.
+ * Otherwise, you cannot bond (primary contains a different mac
+ * at that index).
+ */
+ if (index_at_dup_port >= 0) {
+ if (!table->refs[index_at_dup_port] ||
+ ((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[index_at_dup_port]))))
+ free_for_dup = index_at_dup_port;
+ else
+ can_mf_bond = false;
+ }
+ }
- mutex_lock(&table->mutex);
for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
if (!table->refs[i]) {
if (free < 0)
free = i;
+ if (free_for_dup < 0 && need_mf_bond && can_mf_bond) {
+ if (!dup_table->refs[i])
+ free_for_dup = i;
+ }
continue;
}
@@ -182,10 +257,30 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
/* MAC already registered, increment ref count */
err = i;
++table->refs[i];
+ if (dup) {
+ u64 dup_mac = MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i]);
+
+ if (dup_mac != mac || !dup_table->is_dup[i]) {
+ mlx4_warn(dev, "register mac: expect duplicate mac 0x%llx on port %d index %d\n",
+ mac, dup_port, i);
+ }
+ }
goto out;
}
}
+ if (need_mf_bond && (free_for_dup < 0)) {
+ if (dup) {
+ mlx4_warn(dev, "Fail to allocate duplicate MAC table entry\n");
+ mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n");
+ dup = false;
+ }
+ can_mf_bond = false;
+ }
+
+ if (need_mf_bond && can_mf_bond)
+ free = free_for_dup;
+
mlx4_dbg(dev, "Free MAC index is %d\n", free);
if (table->total == table->max) {
@@ -205,10 +300,35 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
goto out;
}
table->refs[free] = 1;
- err = free;
+ table->is_dup[free] = false;
++table->total;
+ if (dup) {
+ dup_table->refs[free] = 0;
+ dup_table->is_dup[free] = true;
+ dup_table->entries[free] = cpu_to_be64(mac | MLX4_MAC_VALID);
+
+ err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries);
+ if (unlikely(err)) {
+ mlx4_warn(dev, "Failed adding duplicate mac: 0x%llx\n", mac);
+ dup_table->is_dup[free] = false;
+ dup_table->entries[free] = 0;
+ goto out;
+ }
+ ++dup_table->total;
+ }
+ err = free;
out:
- mutex_unlock(&table->mutex);
+ if (need_mf_bond) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
return err;
}
EXPORT_SYMBOL_GPL(__mlx4_register_mac);
@@ -255,6 +375,9 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
struct mlx4_port_info *info;
struct mlx4_mac_table *table;
int index;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
if (port < 1 || port > dev->caps.num_ports) {
mlx4_warn(dev, "invalid port number (%d), aborting...\n", port);
@@ -262,22 +385,59 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
}
info = &mlx4_priv(dev)->port[port];
table = &info->mac_table;
- mutex_lock(&table->mutex);
+
+ if (dup) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
+
index = find_index(dev, table, mac);
if (validate_index(dev, table, index))
goto out;
- if (--table->refs[index]) {
+
+ if (--table->refs[index] || table->is_dup[index]) {
mlx4_dbg(dev, "Have more references for index %d, no need to modify mac table\n",
index);
+ if (!table->refs[index])
+ dup_table->is_dup[index] = false;
goto out;
}
table->entries[index] = 0;
- mlx4_set_port_mac_table(dev, port, table->entries);
+ if (mlx4_set_port_mac_table(dev, port, table->entries))
+ mlx4_warn(dev, "Fail to set mac in port %d during unregister\n", port);
--table->total;
+
+ if (dup) {
+ dup_table->is_dup[index] = false;
+ if (dup_table->refs[index])
+ goto out;
+ dup_table->entries[index] = 0;
+ if (mlx4_set_port_mac_table(dev, dup_port, dup_table->entries))
+ mlx4_warn(dev, "Fail to set mac in duplicate port %d during unregister\n", dup_port);
+
+ --table->total;
+ }
out:
- mutex_unlock(&table->mutex);
+ if (dup) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
}
EXPORT_SYMBOL_GPL(__mlx4_unregister_mac);
@@ -311,9 +471,22 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac)
struct mlx4_mac_table *table = &info->mac_table;
int index = qpn - info->base_qpn;
int err = 0;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
/* CX1 doesn't support multi-functions */
- mutex_lock(&table->mutex);
+ if (dup) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
err = validate_index(dev, table, index);
if (err)
@@ -326,9 +499,30 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac)
mlx4_err(dev, "Failed adding MAC: 0x%llx\n",
(unsigned long long) new_mac);
table->entries[index] = 0;
+ } else {
+ if (dup) {
+ dup_table->entries[index] = cpu_to_be64(new_mac | MLX4_MAC_VALID);
+
+ err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries);
+ if (unlikely(err)) {
+ mlx4_err(dev, "Failed adding duplicate MAC: 0x%llx\n",
+ (unsigned long long)new_mac);
+ dup_table->entries[index] = 0;
+ }
+ }
}
out:
- mutex_unlock(&table->mutex);
+ if (dup) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
return err;
}
EXPORT_SYMBOL_GPL(__mlx4_replace_mac);
@@ -380,8 +574,28 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
int i, err = 0;
int free = -1;
-
- mutex_lock(&table->mutex);
+ int free_for_dup = -1;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table;
+ bool need_mf_bond = mlx4_need_mf_bond(dev);
+ bool can_mf_bond = true;
+
+ mlx4_dbg(dev, "Registering VLAN: %d for port %d %s duplicate\n",
+ vlan, port,
+ dup ? "with" : "without");
+
+ if (need_mf_bond) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
if (table->total == table->max) {
/* No free vlan entries */
@@ -389,22 +603,85 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
goto out;
}
+ if (need_mf_bond) {
+ int index_at_port = -1;
+ int index_at_dup_port = -1;
+
+ for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) {
+ if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(table->entries[i]))))
+ index_at_port = i;
+ if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i]))))
+ index_at_dup_port = i;
+ }
+ /* check that same vlan is not in the tables at different indices */
+ if ((index_at_port != index_at_dup_port) &&
+ (index_at_port >= 0) &&
+ (index_at_dup_port >= 0))
+ can_mf_bond = false;
+
+ /* If the vlan is already in the primary table, the slot must be
+ * available in the duplicate table as well.
+ */
+ if (index_at_port >= 0 && index_at_dup_port < 0 &&
+ dup_table->refs[index_at_port]) {
+ can_mf_bond = false;
+ }
+ /* If the vlan is already in the duplicate table, check that the
+ * corresponding index is not occupied in the primary table, or
+ * the primary table already contains the vlan at the same index.
+ * Otherwise, you cannot bond (primary contains a different vlan
+ * at that index).
+ */
+ if (index_at_dup_port >= 0) {
+ if (!table->refs[index_at_dup_port] ||
+ (vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[index_at_dup_port]))))
+ free_for_dup = index_at_dup_port;
+ else
+ can_mf_bond = false;
+ }
+ }
+
for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) {
- if (free < 0 && (table->refs[i] == 0)) {
- free = i;
- continue;
+ if (!table->refs[i]) {
+ if (free < 0)
+ free = i;
+ if (free_for_dup < 0 && need_mf_bond && can_mf_bond) {
+ if (!dup_table->refs[i])
+ free_for_dup = i;
+ }
}
- if (table->refs[i] &&
+ if ((table->refs[i] || table->is_dup[i]) &&
(vlan == (MLX4_VLAN_MASK &
be32_to_cpu(table->entries[i])))) {
/* Vlan already registered, increase references count */
+ mlx4_dbg(dev, "vlan %u is already registered.\n", vlan);
*index = i;
++table->refs[i];
+ if (dup) {
+ u16 dup_vlan = MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i]);
+
+ if (dup_vlan != vlan || !dup_table->is_dup[i]) {
+ mlx4_warn(dev, "register vlan: expected duplicate vlan %u on port %d index %d\n",
+ vlan, dup_port, i);
+ }
+ }
goto out;
}
}
+ if (need_mf_bond && (free_for_dup < 0)) {
+ if (dup) {
+ mlx4_warn(dev, "Fail to allocate duplicate VLAN table entry\n");
+ mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n");
+ dup = false;
+ }
+ can_mf_bond = false;
+ }
+
+ if (need_mf_bond && can_mf_bond)
+ free = free_for_dup;
+
if (free < 0) {
err = -ENOMEM;
goto out;
@@ -412,6 +689,7 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
/* Register new VLAN */
table->refs[free] = 1;
+ table->is_dup[free] = false;
table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID);
err = mlx4_set_port_vlan_table(dev, port, table->entries);
@@ -421,11 +699,35 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
table->entries[free] = 0;
goto out;
}
+ ++table->total;
+ if (dup) {
+ dup_table->refs[free] = 0;
+ dup_table->is_dup[free] = true;
+ dup_table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID);
+
+ err = mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries);
+ if (unlikely(err)) {
+ mlx4_warn(dev, "Failed adding duplicate vlan: %u\n", vlan);
+ dup_table->is_dup[free] = false;
+ dup_table->entries[free] = 0;
+ goto out;
+ }
+ ++dup_table->total;
+ }
*index = free;
- ++table->total;
out:
- mutex_unlock(&table->mutex);
+ if (need_mf_bond) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
return err;
}
@@ -455,8 +757,22 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
{
struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
int index;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table;
+
+ if (dup) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
- mutex_lock(&table->mutex);
if (mlx4_find_cached_vlan(dev, port, vlan, &index)) {
mlx4_warn(dev, "vlan 0x%x is not in the vlan table\n", vlan);
goto out;
@@ -467,16 +783,38 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
goto out;
}
- if (--table->refs[index]) {
+ if (--table->refs[index] || table->is_dup[index]) {
mlx4_dbg(dev, "Have %d more references for index %d, no need to modify vlan table\n",
table->refs[index], index);
+ if (!table->refs[index])
+ dup_table->is_dup[index] = false;
goto out;
}
table->entries[index] = 0;
- mlx4_set_port_vlan_table(dev, port, table->entries);
+ if (mlx4_set_port_vlan_table(dev, port, table->entries))
+ mlx4_warn(dev, "Fail to set vlan in port %d during unregister\n", port);
--table->total;
+ if (dup) {
+ dup_table->is_dup[index] = false;
+ if (dup_table->refs[index])
+ goto out;
+ dup_table->entries[index] = 0;
+ if (mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries))
+ mlx4_warn(dev, "Fail to set vlan in duplicate port %d during unregister\n", dup_port);
+ --dup_table->total;
+ }
out:
- mutex_unlock(&table->mutex);
+ if (dup) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
}
void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
@@ -495,6 +833,220 @@ void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
}
EXPORT_SYMBOL_GPL(mlx4_unregister_vlan);
+int mlx4_bond_mac_table(struct mlx4_dev *dev)
+{
+ struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table;
+ struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table;
+ int ret = 0;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if ((t1->entries[i] != t2->entries[i]) &&
+ t1->entries[i] && t2->entries[i]) {
+ mlx4_warn(dev, "can't duplicate entry %d in mac table\n", i);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (t1->entries[i] && !t2->entries[i]) {
+ t2->entries[i] = t1->entries[i];
+ t2->is_dup[i] = true;
+ update2 = true;
+ } else if (!t1->entries[i] && t2->entries[i]) {
+ t1->entries[i] = t2->entries[i];
+ t1->is_dup[i] = true;
+ update1 = true;
+ } else if (t1->entries[i] && t2->entries[i]) {
+ t1->is_dup[i] = true;
+ t2->is_dup[i] = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_mac_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set MAC table for port 1 (%d)\n", ret);
+ }
+ if (!ret && update2) {
+ ret = mlx4_set_port_mac_table(dev, 2, t2->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set MAC table for port 2 (%d)\n", ret);
+ }
+
+ if (ret)
+ mlx4_warn(dev, "failed to create mirror MAC tables\n");
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
+int mlx4_unbond_mac_table(struct mlx4_dev *dev)
+{
+ struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table;
+ struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table;
+ int ret = 0;
+ int ret1;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (t1->entries[i] != t2->entries[i]) {
+ mlx4_warn(dev, "mac table is in an unexpected state when trying to unbond\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (!t1->entries[i])
+ continue;
+ t1->is_dup[i] = false;
+ if (!t1->refs[i]) {
+ t1->entries[i] = 0;
+ update1 = true;
+ }
+ t2->is_dup[i] = false;
+ if (!t2->refs[i]) {
+ t2->entries[i] = 0;
+ update2 = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_mac_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to unmirror MAC tables for port 1(%d)\n", ret);
+ }
+ if (update2) {
+ ret1 = mlx4_set_port_mac_table(dev, 2, t2->entries);
+ if (ret1) {
+ mlx4_warn(dev, "failed to unmirror MAC tables for port 2(%d)\n", ret1);
+ ret = ret1;
+ }
+ }
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
+int mlx4_bond_vlan_table(struct mlx4_dev *dev)
+{
+ struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table;
+ struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table;
+ int ret = 0;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if ((t1->entries[i] != t2->entries[i]) &&
+ t1->entries[i] && t2->entries[i]) {
+ mlx4_warn(dev, "can't duplicate entry %d in vlan table\n", i);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if (t1->entries[i] && !t2->entries[i]) {
+ t2->entries[i] = t1->entries[i];
+ t2->is_dup[i] = true;
+ update2 = true;
+ } else if (!t1->entries[i] && t2->entries[i]) {
+ t1->entries[i] = t2->entries[i];
+ t1->is_dup[i] = true;
+ update1 = true;
+ } else if (t1->entries[i] && t2->entries[i]) {
+ t1->is_dup[i] = true;
+ t2->is_dup[i] = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_vlan_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set VLAN table for port 1 (%d)\n", ret);
+ }
+ if (!ret && update2) {
+ ret = mlx4_set_port_vlan_table(dev, 2, t2->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set VLAN table for port 2 (%d)\n", ret);
+ }
+
+ if (ret)
+ mlx4_warn(dev, "failed to create mirror VLAN tables\n");
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
+int mlx4_unbond_vlan_table(struct mlx4_dev *dev)
+{
+ struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table;
+ struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table;
+ int ret = 0;
+ int ret1;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if (t1->entries[i] != t2->entries[i]) {
+ mlx4_warn(dev, "vlan table is in an unexpected state when trying to unbond\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if (!t1->entries[i])
+ continue;
+ t1->is_dup[i] = false;
+ if (!t1->refs[i]) {
+ t1->entries[i] = 0;
+ update1 = true;
+ }
+ t2->is_dup[i] = false;
+ if (!t2->refs[i]) {
+ t2->entries[i] = 0;
+ update2 = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_vlan_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to unmirror VLAN tables for port 1(%d)\n", ret);
+ }
+ if (update2) {
+ ret1 = mlx4_set_port_vlan_table(dev, 2, t2->entries);
+ if (ret1) {
+ mlx4_warn(dev, "failed to unmirror VLAN tables for port 2(%d)\n", ret1);
+ ret = ret1;
+ }
+ }
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
{
struct mlx4_cmd_mailbox *inmailbox, *outmailbox;
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index cad6c44df91c..b46dbe29ef6c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -222,6 +222,13 @@ enum res_fs_rule_states {
struct res_fs_rule {
struct res_common com;
int qpn;
+ /* VF DMFS mbox with port flipped */
+ void *mirr_mbox;
+ /* > 0 --> apply mirror when getting into HA mode */
+ /* = 0 --> un-apply mirror when getting out of HA mode */
+ u32 mirr_mbox_size;
+ struct list_head mirr_list;
+ u64 mirr_rule_id;
};
static void *res_tracker_lookup(struct rb_root *root, u64 res_id)
@@ -4284,6 +4291,22 @@ err_mac:
return err;
}
+static u32 qp_attach_mbox_size(void *mbox)
+{
+ u32 size = sizeof(struct mlx4_net_trans_rule_hw_ctrl);
+ struct _rule_hw *rule_header;
+
+ rule_header = (struct _rule_hw *)(mbox + size);
+
+ while (rule_header->size) {
+ size += rule_header->size * sizeof(u32);
+ rule_header += 1;
+ }
+ return size;
+}
+
+static int mlx4_do_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule);
+
int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
@@ -4300,6 +4323,8 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_net_trans_rule_hw_ctrl *ctrl;
struct _rule_hw *rule_header;
int header_id;
+ struct res_fs_rule *rrule;
+ u32 mbox_size;
if (dev->caps.steering_mode !=
MLX4_STEERING_MODE_DEVICE_MANAGED)
@@ -4329,7 +4354,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
case MLX4_NET_TRANS_RULE_ID_ETH:
if (validate_eth_header_mac(slave, rule_header, rlist)) {
err = -EINVAL;
- goto err_put;
+ goto err_put_qp;
}
break;
case MLX4_NET_TRANS_RULE_ID_IB:
@@ -4340,7 +4365,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
pr_warn("Can't attach FS rule without L2 headers, adding L2 header\n");
if (add_eth_header(dev, slave, inbox, rlist, header_id)) {
err = -EINVAL;
- goto err_put;
+ goto err_put_qp;
}
vhcr->in_modifier +=
sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2;
@@ -4348,7 +4373,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
default:
pr_err("Corrupted mailbox\n");
err = -EINVAL;
- goto err_put;
+ goto err_put_qp;
}
execute:
@@ -4357,23 +4382,69 @@ execute:
MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
MLX4_CMD_NATIVE);
if (err)
- goto err_put;
+ goto err_put_qp;
+
err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, qpn);
if (err) {
mlx4_err(dev, "Fail to add flow steering resources\n");
- /* detach rule*/
+ goto err_detach;
+ }
+
+ err = get_res(dev, slave, vhcr->out_param, RES_FS_RULE, &rrule);
+ if (err)
+ goto err_detach;
+
+ mbox_size = qp_attach_mbox_size(inbox->buf);
+ rrule->mirr_mbox = kmalloc(mbox_size, GFP_KERNEL);
+ if (!rrule->mirr_mbox) {
+ err = -ENOMEM;
+ goto err_put_rule;
+ }
+ rrule->mirr_mbox_size = mbox_size;
+ rrule->mirr_rule_id = 0;
+ memcpy(rrule->mirr_mbox, inbox->buf, mbox_size);
+
+ /* set different port */
+ ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)rrule->mirr_mbox;
+ if (ctrl->port == 1)
+ ctrl->port = 2;
+ else
+ ctrl->port = 1;
+
+ if (mlx4_is_bonded(dev))
+ mlx4_do_mirror_rule(dev, rrule);
+
+ atomic_inc(&rqp->ref_count);
+
+err_put_rule:
+ put_res(dev, slave, vhcr->out_param, RES_FS_RULE);
+err_detach:
+ /* detach rule on error */
+ if (err)
mlx4_cmd(dev, vhcr->out_param, 0, 0,
MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,
MLX4_CMD_NATIVE);
- goto err_put;
- }
- atomic_inc(&rqp->ref_count);
-err_put:
+err_put_qp:
put_res(dev, slave, qpn, RES_QP);
return err;
}
+static int mlx4_undo_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule)
+{
+ int err;
+
+ err = rem_res_range(dev, fs_rule->com.owner, fs_rule->com.res_id, 1, RES_FS_RULE, 0);
+ if (err) {
+ mlx4_err(dev, "Fail to remove flow steering resources\n");
+ return err;
+ }
+
+ mlx4_cmd(dev, fs_rule->com.res_id, 0, 0, MLX4_QP_FLOW_STEERING_DETACH,
+ MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
+ return 0;
+}
+
int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
@@ -4383,6 +4454,7 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
int err;
struct res_qp *rqp;
struct res_fs_rule *rrule;
+ u64 mirr_reg_id;
if (dev->caps.steering_mode !=
MLX4_STEERING_MODE_DEVICE_MANAGED)
@@ -4391,12 +4463,30 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
err = get_res(dev, slave, vhcr->in_param, RES_FS_RULE, &rrule);
if (err)
return err;
+
+ if (!rrule->mirr_mbox) {
+ mlx4_err(dev, "Mirror rules cannot be removed explicitly\n");
+ put_res(dev, slave, vhcr->in_param, RES_FS_RULE);
+ return -EINVAL;
+ }
+ mirr_reg_id = rrule->mirr_rule_id;
+ kfree(rrule->mirr_mbox);
+
/* Release the rule form busy state before removal */
put_res(dev, slave, vhcr->in_param, RES_FS_RULE);
err = get_res(dev, slave, rrule->qpn, RES_QP, &rqp);
if (err)
return err;
+ if (mirr_reg_id && mlx4_is_bonded(dev)) {
+ err = get_res(dev, slave, mirr_reg_id, RES_FS_RULE, &rrule);
+ if (err) {
+ mlx4_err(dev, "Fail to get resource of mirror rule\n");
+ } else {
+ put_res(dev, slave, mirr_reg_id, RES_FS_RULE);
+ mlx4_undo_mirror_rule(dev, rrule);
+ }
+ }
err = rem_res_range(dev, slave, vhcr->in_param, 1, RES_FS_RULE, 0);
if (err) {
mlx4_err(dev, "Fail to remove flow steering resources\n");
@@ -4834,6 +4924,91 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave)
spin_unlock_irq(mlx4_tlock(dev));
}
+static int mlx4_do_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ int err;
+ struct res_fs_rule *mirr_rule;
+ u64 reg_id;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ if (!fs_rule->mirr_mbox) {
+ mlx4_err(dev, "rule mirroring mailbox is null\n");
+ return -EINVAL;
+ }
+ memcpy(mailbox->buf, fs_rule->mirr_mbox, fs_rule->mirr_mbox_size);
+ err = mlx4_cmd_imm(dev, mailbox->dma, &reg_id, fs_rule->mirr_mbox_size >> 2, 0,
+ MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
+ MLX4_CMD_NATIVE);
+ mlx4_free_cmd_mailbox(dev, mailbox);
+
+ if (err)
+ goto err;
+
+ err = add_res_range(dev, fs_rule->com.owner, reg_id, 1, RES_FS_RULE, fs_rule->qpn);
+ if (err)
+ goto err_detach;
+
+ err = get_res(dev, fs_rule->com.owner, reg_id, RES_FS_RULE, &mirr_rule);
+ if (err)
+ goto err_rem;
+
+ fs_rule->mirr_rule_id = reg_id;
+ mirr_rule->mirr_rule_id = 0;
+ mirr_rule->mirr_mbox_size = 0;
+ mirr_rule->mirr_mbox = NULL;
+ put_res(dev, fs_rule->com.owner, reg_id, RES_FS_RULE);
+
+ return 0;
+err_rem:
+ rem_res_range(dev, fs_rule->com.owner, reg_id, 1, RES_FS_RULE, 0);
+err_detach:
+ mlx4_cmd(dev, reg_id, 0, 0, MLX4_QP_FLOW_STEERING_DETACH,
+ MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
+err:
+ return err;
+}
+
+static int mlx4_mirror_fs_rules(struct mlx4_dev *dev, bool bond)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ struct mlx4_resource_tracker *tracker =
+ &priv->mfunc.master.res_tracker;
+ struct rb_root *root = &tracker->res_tree[RES_FS_RULE];
+ struct rb_node *p;
+ struct res_fs_rule *fs_rule;
+ int err = 0;
+ LIST_HEAD(mirr_list);
+
+ for (p = rb_first(root); p; p = rb_next(p)) {
+ fs_rule = rb_entry(p, struct res_fs_rule, com.node);
+ if ((bond && fs_rule->mirr_mbox_size) ||
+ (!bond && !fs_rule->mirr_mbox_size))
+ list_add_tail(&fs_rule->mirr_list, &mirr_list);
+ }
+
+ list_for_each_entry(fs_rule, &mirr_list, mirr_list) {
+ if (bond)
+ err += mlx4_do_mirror_rule(dev, fs_rule);
+ else
+ err += mlx4_undo_mirror_rule(dev, fs_rule);
+ }
+ return err;
+}
+
+int mlx4_bond_fs_rules(struct mlx4_dev *dev)
+{
+ return mlx4_mirror_fs_rules(dev, true);
+}
+
+int mlx4_unbond_fs_rules(struct mlx4_dev *dev)
+{
+ return mlx4_mirror_fs_rules(dev, false);
+}
+
static void rem_slave_fs_rule(struct mlx4_dev *dev, int slave)
{
struct mlx4_priv *priv = mlx4_priv(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index 158c88c69ef9..c503ea05e742 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -13,6 +13,7 @@ config MLX5_CORE
config MLX5_CORE_EN
bool "Mellanox Technologies ConnectX-4 Ethernet support"
depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
+ select PTP_1588_CLOCK
default n
---help---
Ethernet support in Mellanox Technologies ConnectX-4 NIC.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index 26a68b8af2c5..01c0256effb8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o
mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \
- mad.o transobj.o vport.o
-mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o \
- en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \
- en_txrx.o
+ mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o
+mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \
+ en_main.o en_fs.o en_ethtool.o en_tx.o en_rx.o \
+ en_txrx.o en_clock.o
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 22e72bf1ae48..9ea49a893323 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -32,6 +32,9 @@
#include <linux/if_vlan.h>
#include <linux/etherdevice.h>
+#include <linux/timecounter.h>
+#include <linux/net_tstamp.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/qp.h>
#include <linux/mlx5/cq.h>
@@ -64,6 +67,8 @@
#define MLX5E_UPDATE_STATS_INTERVAL 200 /* msecs */
#define MLX5E_SQ_BF_BUDGET 16
+#define MLX5E_NUM_MAIN_GROUPS 9
+
static const char vport_strings[][ETH_GSTRING_LEN] = {
/* vport statistics */
"rx_packets",
@@ -282,6 +287,19 @@ struct mlx5e_params {
u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE];
};
+struct mlx5e_tstamp {
+ rwlock_t lock;
+ struct cyclecounter cycles;
+ struct timecounter clock;
+ struct hwtstamp_config hwtstamp_config;
+ u32 nominal_c_mult;
+ unsigned long overflow_period;
+ struct delayed_work overflow_work;
+ struct mlx5_core_dev *mdev;
+ struct ptp_clock *ptp;
+ struct ptp_clock_info ptp_info;
+};
+
enum {
MLX5E_RQ_STATE_POST_WQES_ENABLE,
};
@@ -313,6 +331,7 @@ struct mlx5e_rq {
struct device *pdev;
struct net_device *netdev;
+ struct mlx5e_tstamp *tstamp;
struct mlx5e_rq_stats stats;
struct mlx5e_cq cq;
@@ -326,14 +345,12 @@ struct mlx5e_rq {
struct mlx5e_priv *priv;
} ____cacheline_aligned_in_smp;
-struct mlx5e_tx_skb_cb {
+struct mlx5e_tx_wqe_info {
u32 num_bytes;
u8 num_wqebbs;
u8 num_dma;
};
-#define MLX5E_TX_SKB_CB(__skb) ((struct mlx5e_tx_skb_cb *)__skb->cb)
-
enum mlx5e_dma_map_type {
MLX5E_DMA_MAP_SINGLE,
MLX5E_DMA_MAP_PAGE
@@ -369,6 +386,7 @@ struct mlx5e_sq {
/* pointers to per packet info: write@xmit, read@completion */
struct sk_buff **skb;
struct mlx5e_sq_dma *dma_fifo;
+ struct mlx5e_tx_wqe_info *wqe_info;
/* read only */
struct mlx5_wq_cyc wq;
@@ -381,6 +399,7 @@ struct mlx5e_sq {
u16 max_inline;
u16 edge;
struct device *pdev;
+ struct mlx5e_tstamp *tstamp;
__be32 mkey_be;
unsigned long state;
@@ -442,7 +461,7 @@ enum mlx5e_rqt_ix {
struct mlx5e_eth_addr_info {
u8 addr[ETH_ALEN + 2];
u32 tt_vec;
- u32 ft_ix[MLX5E_NUM_TT]; /* flow table index per traffic type */
+ struct mlx5_flow_rule *ft_rule[MLX5E_NUM_TT];
};
#define MLX5E_ETH_ADDR_HASH_SIZE (1 << BITS_PER_BYTE)
@@ -465,15 +484,23 @@ enum {
};
struct mlx5e_vlan_db {
- u32 active_vlans_ft_ix[VLAN_N_VID];
- u32 untagged_rule_ft_ix;
- u32 any_vlan_rule_ft_ix;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ struct mlx5_flow_rule *active_vlans_rule[VLAN_N_VID];
+ struct mlx5_flow_rule *untagged_rule;
+ struct mlx5_flow_rule *any_vlan_rule;
bool filter_disabled;
};
struct mlx5e_flow_table {
- void *vlan;
- void *main;
+ int num_groups;
+ struct mlx5_flow_table *t;
+ struct mlx5_flow_group **g;
+};
+
+struct mlx5e_flow_tables {
+ struct mlx5_flow_namespace *ns;
+ struct mlx5e_flow_table vlan;
+ struct mlx5e_flow_table main;
};
struct mlx5e_priv {
@@ -496,7 +523,7 @@ struct mlx5e_priv {
u32 rqtn[MLX5E_NUM_RQT];
u32 tirn[MLX5E_NUM_TT];
- struct mlx5e_flow_table ft;
+ struct mlx5e_flow_tables fts;
struct mlx5e_eth_addr_db eth_addr;
struct mlx5e_vlan_db vlan;
@@ -509,6 +536,7 @@ struct mlx5e_priv {
struct mlx5_core_dev *mdev;
struct net_device *netdev;
struct mlx5e_stats stats;
+ struct mlx5e_tstamp tstamp;
};
#define MLX5E_NET_IP_ALIGN 2
@@ -564,7 +592,7 @@ void mlx5e_completion_event(struct mlx5_core_cq *mcq);
void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event);
int mlx5e_napi_poll(struct napi_struct *napi, int budget);
bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq);
-bool mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
+int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq);
struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq);
@@ -575,6 +603,13 @@ void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv);
void mlx5e_init_eth_addr(struct mlx5e_priv *priv);
void mlx5e_set_rx_mode_work(struct work_struct *work);
+void mlx5e_fill_hwstamp(struct mlx5e_tstamp *clock, u64 timestamp,
+ struct skb_shared_hwtstamps *hwts);
+void mlx5e_timestamp_init(struct mlx5e_priv *priv);
+void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv);
+int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr);
+int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr);
+
int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto,
u16 vid);
int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c
new file mode 100644
index 000000000000..be6543570b2b
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/clocksource.h>
+#include "en.h"
+
+enum {
+ MLX5E_CYCLES_SHIFT = 23
+};
+
+void mlx5e_fill_hwstamp(struct mlx5e_tstamp *tstamp, u64 timestamp,
+ struct skb_shared_hwtstamps *hwts)
+{
+ u64 nsec;
+
+ read_lock(&tstamp->lock);
+ nsec = timecounter_cyc2time(&tstamp->clock, timestamp);
+ read_unlock(&tstamp->lock);
+
+ hwts->hwtstamp = ns_to_ktime(nsec);
+}
+
+static cycle_t mlx5e_read_internal_timer(const struct cyclecounter *cc)
+{
+ struct mlx5e_tstamp *tstamp = container_of(cc, struct mlx5e_tstamp,
+ cycles);
+
+ return mlx5_read_internal_timer(tstamp->mdev) & cc->mask;
+}
+
+static void mlx5e_timestamp_overflow(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct mlx5e_tstamp *tstamp = container_of(dwork, struct mlx5e_tstamp,
+ overflow_work);
+
+ write_lock(&tstamp->lock);
+ timecounter_read(&tstamp->clock);
+ write_unlock(&tstamp->lock);
+ schedule_delayed_work(&tstamp->overflow_work, tstamp->overflow_period);
+}
+
+int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct hwtstamp_config config;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ /* TX HW timestamp */
+ switch (config.tx_type) {
+ case HWTSTAMP_TX_OFF:
+ case HWTSTAMP_TX_ON:
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /* RX HW timestamp */
+ switch (config.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ break;
+ case HWTSTAMP_FILTER_ALL:
+ case HWTSTAMP_FILTER_SOME:
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ memcpy(&priv->tstamp.hwtstamp_config, &config, sizeof(config));
+
+ return copy_to_user(ifr->ifr_data, &config,
+ sizeof(config)) ? -EFAULT : 0;
+}
+
+int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct hwtstamp_config *cfg = &priv->tstamp.hwtstamp_config;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return -EOPNOTSUPP;
+
+ return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0;
+}
+
+static int mlx5e_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+ u64 ns = timespec64_to_ns(ts);
+
+ write_lock(&tstamp->lock);
+ timecounter_init(&tstamp->clock, &tstamp->cycles, ns);
+ write_unlock(&tstamp->lock);
+
+ return 0;
+}
+
+static int mlx5e_ptp_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+ u64 ns;
+
+ write_lock(&tstamp->lock);
+ ns = timecounter_read(&tstamp->clock);
+ write_unlock(&tstamp->lock);
+
+ *ts = ns_to_timespec64(ns);
+
+ return 0;
+}
+
+static int mlx5e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+
+ write_lock(&tstamp->lock);
+ timecounter_adjtime(&tstamp->clock, delta);
+ write_unlock(&tstamp->lock);
+
+ return 0;
+}
+
+static int mlx5e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta)
+{
+ u64 adj;
+ u32 diff;
+ int neg_adj = 0;
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+
+ if (delta < 0) {
+ neg_adj = 1;
+ delta = -delta;
+ }
+
+ adj = tstamp->nominal_c_mult;
+ adj *= delta;
+ diff = div_u64(adj, 1000000000ULL);
+
+ write_lock(&tstamp->lock);
+ timecounter_read(&tstamp->clock);
+ tstamp->cycles.mult = neg_adj ? tstamp->nominal_c_mult - diff :
+ tstamp->nominal_c_mult + diff;
+ write_unlock(&tstamp->lock);
+
+ return 0;
+}
+
+static const struct ptp_clock_info mlx5e_ptp_clock_info = {
+ .owner = THIS_MODULE,
+ .max_adj = 100000000,
+ .n_alarm = 0,
+ .n_ext_ts = 0,
+ .n_per_out = 0,
+ .n_pins = 0,
+ .pps = 0,
+ .adjfreq = mlx5e_ptp_adjfreq,
+ .adjtime = mlx5e_ptp_adjtime,
+ .gettime64 = mlx5e_ptp_gettime,
+ .settime64 = mlx5e_ptp_settime,
+ .enable = NULL,
+};
+
+static void mlx5e_timestamp_init_config(struct mlx5e_tstamp *tstamp)
+{
+ tstamp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF;
+ tstamp->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+}
+
+void mlx5e_timestamp_init(struct mlx5e_priv *priv)
+{
+ struct mlx5e_tstamp *tstamp = &priv->tstamp;
+ u64 ns;
+ u64 frac = 0;
+ u32 dev_freq;
+
+ mlx5e_timestamp_init_config(tstamp);
+ dev_freq = MLX5_CAP_GEN(priv->mdev, device_frequency_khz);
+ if (!dev_freq) {
+ mlx5_core_warn(priv->mdev, "invalid device_frequency_khz, aborting HW clock init\n");
+ return;
+ }
+ rwlock_init(&tstamp->lock);
+ tstamp->cycles.read = mlx5e_read_internal_timer;
+ tstamp->cycles.shift = MLX5E_CYCLES_SHIFT;
+ tstamp->cycles.mult = clocksource_khz2mult(dev_freq,
+ tstamp->cycles.shift);
+ tstamp->nominal_c_mult = tstamp->cycles.mult;
+ tstamp->cycles.mask = CLOCKSOURCE_MASK(41);
+ tstamp->mdev = priv->mdev;
+
+ timecounter_init(&tstamp->clock, &tstamp->cycles,
+ ktime_to_ns(ktime_get_real()));
+
+ /* Calculate period in seconds to call the overflow watchdog - to make
+ * sure counter is checked at least once every wrap around.
+ */
+ ns = cyclecounter_cyc2ns(&tstamp->cycles, tstamp->cycles.mask,
+ frac, &frac);
+ do_div(ns, NSEC_PER_SEC / 2 / HZ);
+ tstamp->overflow_period = ns;
+
+ INIT_DELAYED_WORK(&tstamp->overflow_work, mlx5e_timestamp_overflow);
+ if (tstamp->overflow_period)
+ schedule_delayed_work(&tstamp->overflow_work, 0);
+ else
+ mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n");
+
+ /* Configure the PHC */
+ tstamp->ptp_info = mlx5e_ptp_clock_info;
+ snprintf(tstamp->ptp_info.name, 16, "mlx5 ptp");
+
+ tstamp->ptp = ptp_clock_register(&tstamp->ptp_info,
+ &priv->mdev->pdev->dev);
+ if (IS_ERR_OR_NULL(tstamp->ptp)) {
+ mlx5_core_warn(priv->mdev, "ptp_clock_register failed %ld\n",
+ PTR_ERR(tstamp->ptp));
+ tstamp->ptp = NULL;
+ }
+}
+
+void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv)
+{
+ struct mlx5e_tstamp *tstamp = &priv->tstamp;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return;
+
+ if (priv->tstamp.ptp) {
+ ptp_clock_unregister(priv->tstamp.ptp);
+ priv->tstamp.ptp = NULL;
+ }
+
+ cancel_delayed_work_sync(&tstamp->overflow_work);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
index 2e022e900939..65624ac65b4c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
@@ -855,6 +855,35 @@ static int mlx5e_set_pauseparam(struct net_device *netdev,
return err;
}
+static int mlx5e_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = ethtool_op_get_ts_info(dev, info);
+ if (ret)
+ return ret;
+
+ info->phc_index = priv->tstamp.ptp ?
+ ptp_clock_index(priv->tstamp.ptp) : -1;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return 0;
+
+ info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ info->tx_types = (BIT(1) << HWTSTAMP_TX_OFF) |
+ (BIT(1) << HWTSTAMP_TX_ON);
+
+ info->rx_filters = (BIT(1) << HWTSTAMP_FILTER_NONE) |
+ (BIT(1) << HWTSTAMP_FILTER_ALL);
+
+ return 0;
+}
+
const struct ethtool_ops mlx5e_ethtool_ops = {
.get_drvinfo = mlx5e_get_drvinfo,
.get_link = ethtool_op_get_link,
@@ -878,4 +907,5 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
.set_tunable = mlx5e_set_tunable,
.get_pauseparam = mlx5e_get_pauseparam,
.set_pauseparam = mlx5e_set_pauseparam,
+ .get_ts_info = mlx5e_get_ts_info,
};
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c
deleted file mode 100644
index 22d603f78273..000000000000
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c
+++ /dev/null
@@ -1,907 +0,0 @@
-/*
- * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses. You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <linux/list.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <linux/tcp.h>
-#include <linux/mlx5/flow_table.h>
-#include "en.h"
-
-enum {
- MLX5E_FULLMATCH = 0,
- MLX5E_ALLMULTI = 1,
- MLX5E_PROMISC = 2,
-};
-
-enum {
- MLX5E_UC = 0,
- MLX5E_MC_IPV4 = 1,
- MLX5E_MC_IPV6 = 2,
- MLX5E_MC_OTHER = 3,
-};
-
-enum {
- MLX5E_ACTION_NONE = 0,
- MLX5E_ACTION_ADD = 1,
- MLX5E_ACTION_DEL = 2,
-};
-
-struct mlx5e_eth_addr_hash_node {
- struct hlist_node hlist;
- u8 action;
- struct mlx5e_eth_addr_info ai;
-};
-
-static inline int mlx5e_hash_eth_addr(u8 *addr)
-{
- return addr[5];
-}
-
-static void mlx5e_add_eth_addr_to_hash(struct hlist_head *hash, u8 *addr)
-{
- struct mlx5e_eth_addr_hash_node *hn;
- int ix = mlx5e_hash_eth_addr(addr);
- int found = 0;
-
- hlist_for_each_entry(hn, &hash[ix], hlist)
- if (ether_addr_equal_64bits(hn->ai.addr, addr)) {
- found = 1;
- break;
- }
-
- if (found) {
- hn->action = MLX5E_ACTION_NONE;
- return;
- }
-
- hn = kzalloc(sizeof(*hn), GFP_ATOMIC);
- if (!hn)
- return;
-
- ether_addr_copy(hn->ai.addr, addr);
- hn->action = MLX5E_ACTION_ADD;
-
- hlist_add_head(&hn->hlist, &hash[ix]);
-}
-
-static void mlx5e_del_eth_addr_from_hash(struct mlx5e_eth_addr_hash_node *hn)
-{
- hlist_del(&hn->hlist);
- kfree(hn);
-}
-
-static void mlx5e_del_eth_addr_from_flow_table(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_info *ai)
-{
- void *ft = priv->ft.main;
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV6_IPSEC_ESP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV4_IPSEC_ESP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV6_IPSEC_AH]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV4_IPSEC_AH]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_TCP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6_TCP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_TCP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4_TCP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_UDP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6_UDP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_UDP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4_UDP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_ANY))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_ANY]);
-}
-
-static int mlx5e_get_eth_addr_type(u8 *addr)
-{
- if (is_unicast_ether_addr(addr))
- return MLX5E_UC;
-
- if ((addr[0] == 0x01) &&
- (addr[1] == 0x00) &&
- (addr[2] == 0x5e) &&
- !(addr[3] & 0x80))
- return MLX5E_MC_IPV4;
-
- if ((addr[0] == 0x33) &&
- (addr[1] == 0x33))
- return MLX5E_MC_IPV6;
-
- return MLX5E_MC_OTHER;
-}
-
-static u32 mlx5e_get_tt_vec(struct mlx5e_eth_addr_info *ai, int type)
-{
- int eth_addr_type;
- u32 ret;
-
- switch (type) {
- case MLX5E_FULLMATCH:
- eth_addr_type = mlx5e_get_eth_addr_type(ai->addr);
- switch (eth_addr_type) {
- case MLX5E_UC:
- ret =
- BIT(MLX5E_TT_IPV4_TCP) |
- BIT(MLX5E_TT_IPV6_TCP) |
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV4_IPSEC_AH) |
- BIT(MLX5E_TT_IPV6_IPSEC_AH) |
- BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV4) |
- BIT(MLX5E_TT_IPV6) |
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
-
- case MLX5E_MC_IPV4:
- ret =
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV4) |
- 0;
- break;
-
- case MLX5E_MC_IPV6:
- ret =
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV6) |
- 0;
- break;
-
- case MLX5E_MC_OTHER:
- ret =
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
- }
-
- break;
-
- case MLX5E_ALLMULTI:
- ret =
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV4) |
- BIT(MLX5E_TT_IPV6) |
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
-
- default: /* MLX5E_PROMISC */
- ret =
- BIT(MLX5E_TT_IPV4_TCP) |
- BIT(MLX5E_TT_IPV6_TCP) |
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV4_IPSEC_AH) |
- BIT(MLX5E_TT_IPV6_IPSEC_AH) |
- BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV4) |
- BIT(MLX5E_TT_IPV6) |
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
- }
-
- return ret;
-}
-
-static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_info *ai, int type,
- void *flow_context, void *match_criteria)
-{
- u8 match_criteria_enable = 0;
- void *match_value;
- void *dest;
- u8 *dmac;
- u8 *match_criteria_dmac;
- void *ft = priv->ft.main;
- u32 *tirn = priv->tirn;
- u32 *ft_ix;
- u32 tt_vec;
- int err;
-
- match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
- dmac = MLX5_ADDR_OF(fte_match_param, match_value,
- outer_headers.dmac_47_16);
- match_criteria_dmac = MLX5_ADDR_OF(fte_match_param, match_criteria,
- outer_headers.dmac_47_16);
- dest = MLX5_ADDR_OF(flow_context, flow_context, destination);
-
- MLX5_SET(flow_context, flow_context, action,
- MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
- MLX5_SET(flow_context, flow_context, destination_list_size, 1);
- MLX5_SET(dest_format_struct, dest, destination_type,
- MLX5_FLOW_CONTEXT_DEST_TYPE_TIR);
-
- switch (type) {
- case MLX5E_FULLMATCH:
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- memset(match_criteria_dmac, 0xff, ETH_ALEN);
- ether_addr_copy(dmac, ai->addr);
- break;
-
- case MLX5E_ALLMULTI:
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- match_criteria_dmac[0] = 0x01;
- dmac[0] = 0x01;
- break;
-
- case MLX5E_PROMISC:
- break;
- }
-
- tt_vec = mlx5e_get_tt_vec(ai, type);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_ANY];
- if (tt_vec & BIT(MLX5E_TT_ANY)) {
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_ANY]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_ANY);
- }
-
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.ethertype);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4];
- if (tt_vec & BIT(MLX5E_TT_IPV4)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6];
- if (tt_vec & BIT(MLX5E_TT_IPV6)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6);
- }
-
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.ip_protocol);
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_UDP);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_UDP];
- if (tt_vec & BIT(MLX5E_TT_IPV4_UDP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_UDP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_UDP);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_UDP];
- if (tt_vec & BIT(MLX5E_TT_IPV6_UDP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_UDP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_UDP);
- }
-
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_TCP);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_TCP];
- if (tt_vec & BIT(MLX5E_TT_IPV4_TCP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_TCP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_TCP);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_TCP];
- if (tt_vec & BIT(MLX5E_TT_IPV6_TCP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_TCP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_TCP);
- }
-
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_AH);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_IPSEC_AH];
- if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_IPSEC_AH]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_AH);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_IPSEC_AH];
- if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_IPSEC_AH]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_AH);
- }
-
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_ESP);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_IPSEC_ESP];
- if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_IPSEC_ESP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_ESP);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_IPSEC_ESP];
- if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_IPSEC_ESP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_ESP);
- }
-
- return 0;
-
-err_del_ai:
- mlx5e_del_eth_addr_from_flow_table(priv, ai);
-
- return err;
-}
-
-static int mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_info *ai, int type)
-{
- u32 *flow_context;
- u32 *match_criteria;
- int err;
-
- flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) +
- MLX5_ST_SZ_BYTES(dest_format_struct));
- match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
- if (!flow_context || !match_criteria) {
- netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
- err = -ENOMEM;
- goto add_eth_addr_rule_out;
- }
-
- err = __mlx5e_add_eth_addr_rule(priv, ai, type, flow_context,
- match_criteria);
- if (err)
- netdev_err(priv->netdev, "%s: failed\n", __func__);
-
-add_eth_addr_rule_out:
- kvfree(match_criteria);
- kvfree(flow_context);
- return err;
-}
-
-enum mlx5e_vlan_rule_type {
- MLX5E_VLAN_RULE_TYPE_UNTAGGED,
- MLX5E_VLAN_RULE_TYPE_ANY_VID,
- MLX5E_VLAN_RULE_TYPE_MATCH_VID,
-};
-
-static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv,
- enum mlx5e_vlan_rule_type rule_type, u16 vid)
-{
- u8 match_criteria_enable = 0;
- u32 *flow_context;
- void *match_value;
- void *dest;
- u32 *match_criteria;
- u32 *ft_ix;
- int err;
-
- flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) +
- MLX5_ST_SZ_BYTES(dest_format_struct));
- match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
- if (!flow_context || !match_criteria) {
- netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
- err = -ENOMEM;
- goto add_vlan_rule_out;
- }
- match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
- dest = MLX5_ADDR_OF(flow_context, flow_context, destination);
-
- MLX5_SET(flow_context, flow_context, action,
- MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
- MLX5_SET(flow_context, flow_context, destination_list_size, 1);
- MLX5_SET(dest_format_struct, dest, destination_type,
- MLX5_FLOW_CONTEXT_DEST_TYPE_FLOW_TABLE);
- MLX5_SET(dest_format_struct, dest, destination_id,
- mlx5_get_flow_table_id(priv->ft.main));
-
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.vlan_tag);
-
- switch (rule_type) {
- case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
- ft_ix = &priv->vlan.untagged_rule_ft_ix;
- break;
- case MLX5E_VLAN_RULE_TYPE_ANY_VID:
- ft_ix = &priv->vlan.any_vlan_rule_ft_ix;
- MLX5_SET(fte_match_param, match_value, outer_headers.vlan_tag,
- 1);
- break;
- default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */
- ft_ix = &priv->vlan.active_vlans_ft_ix[vid];
- MLX5_SET(fte_match_param, match_value, outer_headers.vlan_tag,
- 1);
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.first_vid);
- MLX5_SET(fte_match_param, match_value, outer_headers.first_vid,
- vid);
- break;
- }
-
- err = mlx5_add_flow_table_entry(priv->ft.vlan, match_criteria_enable,
- match_criteria, flow_context, ft_ix);
- if (err)
- netdev_err(priv->netdev, "%s: failed\n", __func__);
-
-add_vlan_rule_out:
- kvfree(match_criteria);
- kvfree(flow_context);
- return err;
-}
-
-static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv,
- enum mlx5e_vlan_rule_type rule_type, u16 vid)
-{
- switch (rule_type) {
- case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
- mlx5_del_flow_table_entry(priv->ft.vlan,
- priv->vlan.untagged_rule_ft_ix);
- break;
- case MLX5E_VLAN_RULE_TYPE_ANY_VID:
- mlx5_del_flow_table_entry(priv->ft.vlan,
- priv->vlan.any_vlan_rule_ft_ix);
- break;
- case MLX5E_VLAN_RULE_TYPE_MATCH_VID:
- mlx5_del_flow_table_entry(priv->ft.vlan,
- priv->vlan.active_vlans_ft_ix[vid]);
- break;
- }
-}
-
-void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv)
-{
- if (!priv->vlan.filter_disabled)
- return;
-
- priv->vlan.filter_disabled = false;
- if (priv->netdev->flags & IFF_PROMISC)
- return;
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
-}
-
-void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv)
-{
- if (priv->vlan.filter_disabled)
- return;
-
- priv->vlan.filter_disabled = true;
- if (priv->netdev->flags & IFF_PROMISC)
- return;
- mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
-}
-
-int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto,
- u16 vid)
-{
- struct mlx5e_priv *priv = netdev_priv(dev);
-
- return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
-}
-
-int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto,
- u16 vid)
-{
- struct mlx5e_priv *priv = netdev_priv(dev);
-
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
-
- return 0;
-}
-
-#define mlx5e_for_each_hash_node(hn, tmp, hash, i) \
- for (i = 0; i < MLX5E_ETH_ADDR_HASH_SIZE; i++) \
- hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist)
-
-static void mlx5e_execute_action(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_hash_node *hn)
-{
- switch (hn->action) {
- case MLX5E_ACTION_ADD:
- mlx5e_add_eth_addr_rule(priv, &hn->ai, MLX5E_FULLMATCH);
- hn->action = MLX5E_ACTION_NONE;
- break;
-
- case MLX5E_ACTION_DEL:
- mlx5e_del_eth_addr_from_flow_table(priv, &hn->ai);
- mlx5e_del_eth_addr_from_hash(hn);
- break;
- }
-}
-
-static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv)
-{
- struct net_device *netdev = priv->netdev;
- struct netdev_hw_addr *ha;
-
- netif_addr_lock_bh(netdev);
-
- mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc,
- priv->netdev->dev_addr);
-
- netdev_for_each_uc_addr(ha, netdev)
- mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, ha->addr);
-
- netdev_for_each_mc_addr(ha, netdev)
- mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_mc, ha->addr);
-
- netif_addr_unlock_bh(netdev);
-}
-
-static void mlx5e_apply_netdev_addr(struct mlx5e_priv *priv)
-{
- struct mlx5e_eth_addr_hash_node *hn;
- struct hlist_node *tmp;
- int i;
-
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
- mlx5e_execute_action(priv, hn);
-
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
- mlx5e_execute_action(priv, hn);
-}
-
-static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv)
-{
- struct mlx5e_eth_addr_hash_node *hn;
- struct hlist_node *tmp;
- int i;
-
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
- hn->action = MLX5E_ACTION_DEL;
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
- hn->action = MLX5E_ACTION_DEL;
-
- if (!test_bit(MLX5E_STATE_DESTROYING, &priv->state))
- mlx5e_sync_netdev_addr(priv);
-
- mlx5e_apply_netdev_addr(priv);
-}
-
-void mlx5e_set_rx_mode_work(struct work_struct *work)
-{
- struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
- set_rx_mode_work);
-
- struct mlx5e_eth_addr_db *ea = &priv->eth_addr;
- struct net_device *ndev = priv->netdev;
-
- bool rx_mode_enable = !test_bit(MLX5E_STATE_DESTROYING, &priv->state);
- bool promisc_enabled = rx_mode_enable && (ndev->flags & IFF_PROMISC);
- bool allmulti_enabled = rx_mode_enable && (ndev->flags & IFF_ALLMULTI);
- bool broadcast_enabled = rx_mode_enable;
-
- bool enable_promisc = !ea->promisc_enabled && promisc_enabled;
- bool disable_promisc = ea->promisc_enabled && !promisc_enabled;
- bool enable_allmulti = !ea->allmulti_enabled && allmulti_enabled;
- bool disable_allmulti = ea->allmulti_enabled && !allmulti_enabled;
- bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled;
- bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled;
-
- if (enable_promisc) {
- mlx5e_add_eth_addr_rule(priv, &ea->promisc, MLX5E_PROMISC);
- if (!priv->vlan.filter_disabled)
- mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
- 0);
- }
- if (enable_allmulti)
- mlx5e_add_eth_addr_rule(priv, &ea->allmulti, MLX5E_ALLMULTI);
- if (enable_broadcast)
- mlx5e_add_eth_addr_rule(priv, &ea->broadcast, MLX5E_FULLMATCH);
-
- mlx5e_handle_netdev_addr(priv);
-
- if (disable_broadcast)
- mlx5e_del_eth_addr_from_flow_table(priv, &ea->broadcast);
- if (disable_allmulti)
- mlx5e_del_eth_addr_from_flow_table(priv, &ea->allmulti);
- if (disable_promisc) {
- if (!priv->vlan.filter_disabled)
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
- 0);
- mlx5e_del_eth_addr_from_flow_table(priv, &ea->promisc);
- }
-
- ea->promisc_enabled = promisc_enabled;
- ea->allmulti_enabled = allmulti_enabled;
- ea->broadcast_enabled = broadcast_enabled;
-}
-
-void mlx5e_init_eth_addr(struct mlx5e_priv *priv)
-{
- ether_addr_copy(priv->eth_addr.broadcast.addr, priv->netdev->broadcast);
-}
-
-static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv)
-{
- struct mlx5_flow_table_group *g;
- u8 *dmac;
-
- g = kcalloc(9, sizeof(*g), GFP_KERNEL);
- if (!g)
- return -ENOMEM;
-
- g[0].log_sz = 3;
- g[0].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.ethertype);
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.ip_protocol);
-
- g[1].log_sz = 1;
- g[1].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[1].match_criteria,
- outer_headers.ethertype);
-
- g[2].log_sz = 0;
-
- g[3].log_sz = 14;
- g[3].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[3].match_criteria,
- outer_headers.dmac_47_16);
- memset(dmac, 0xff, ETH_ALEN);
- MLX5_SET_TO_ONES(fte_match_param, g[3].match_criteria,
- outer_headers.ethertype);
- MLX5_SET_TO_ONES(fte_match_param, g[3].match_criteria,
- outer_headers.ip_protocol);
-
- g[4].log_sz = 13;
- g[4].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[4].match_criteria,
- outer_headers.dmac_47_16);
- memset(dmac, 0xff, ETH_ALEN);
- MLX5_SET_TO_ONES(fte_match_param, g[4].match_criteria,
- outer_headers.ethertype);
-
- g[5].log_sz = 11;
- g[5].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[5].match_criteria,
- outer_headers.dmac_47_16);
- memset(dmac, 0xff, ETH_ALEN);
-
- g[6].log_sz = 2;
- g[6].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[6].match_criteria,
- outer_headers.dmac_47_16);
- dmac[0] = 0x01;
- MLX5_SET_TO_ONES(fte_match_param, g[6].match_criteria,
- outer_headers.ethertype);
- MLX5_SET_TO_ONES(fte_match_param, g[6].match_criteria,
- outer_headers.ip_protocol);
-
- g[7].log_sz = 1;
- g[7].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[7].match_criteria,
- outer_headers.dmac_47_16);
- dmac[0] = 0x01;
- MLX5_SET_TO_ONES(fte_match_param, g[7].match_criteria,
- outer_headers.ethertype);
-
- g[8].log_sz = 0;
- g[8].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[8].match_criteria,
- outer_headers.dmac_47_16);
- dmac[0] = 0x01;
- priv->ft.main = mlx5_create_flow_table(priv->mdev, 1,
- MLX5_FLOW_TABLE_TYPE_NIC_RCV,
- 9, g);
- kfree(g);
-
- return priv->ft.main ? 0 : -ENOMEM;
-}
-
-static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv)
-{
- mlx5_destroy_flow_table(priv->ft.main);
-}
-
-static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv)
-{
- struct mlx5_flow_table_group *g;
-
- g = kcalloc(2, sizeof(*g), GFP_KERNEL);
- if (!g)
- return -ENOMEM;
-
- g[0].log_sz = 12;
- g[0].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.vlan_tag);
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.first_vid);
-
- /* untagged + any vlan id */
- g[1].log_sz = 1;
- g[1].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[1].match_criteria,
- outer_headers.vlan_tag);
-
- priv->ft.vlan = mlx5_create_flow_table(priv->mdev, 0,
- MLX5_FLOW_TABLE_TYPE_NIC_RCV,
- 2, g);
-
- kfree(g);
- return priv->ft.vlan ? 0 : -ENOMEM;
-}
-
-static void mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv)
-{
- mlx5_destroy_flow_table(priv->ft.vlan);
-}
-
-int mlx5e_create_flow_tables(struct mlx5e_priv *priv)
-{
- int err;
-
- err = mlx5e_create_main_flow_table(priv);
- if (err)
- return err;
-
- err = mlx5e_create_vlan_flow_table(priv);
- if (err)
- goto err_destroy_main_flow_table;
-
- err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
- if (err)
- goto err_destroy_vlan_flow_table;
-
- return 0;
-
-err_destroy_vlan_flow_table:
- mlx5e_destroy_vlan_flow_table(priv);
-
-err_destroy_main_flow_table:
- mlx5e_destroy_main_flow_table(priv);
-
- return err;
-}
-
-void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv)
-{
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
- mlx5e_destroy_vlan_flow_table(priv);
- mlx5e_destroy_main_flow_table(priv);
-}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
new file mode 100644
index 000000000000..80d81abc4820
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
@@ -0,0 +1,1224 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/list.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/mlx5/fs.h>
+#include "en.h"
+
+#define MLX5_SET_CFG(p, f, v) MLX5_SET(create_flow_group_in, p, f, v)
+
+enum {
+ MLX5E_FULLMATCH = 0,
+ MLX5E_ALLMULTI = 1,
+ MLX5E_PROMISC = 2,
+};
+
+enum {
+ MLX5E_UC = 0,
+ MLX5E_MC_IPV4 = 1,
+ MLX5E_MC_IPV6 = 2,
+ MLX5E_MC_OTHER = 3,
+};
+
+enum {
+ MLX5E_ACTION_NONE = 0,
+ MLX5E_ACTION_ADD = 1,
+ MLX5E_ACTION_DEL = 2,
+};
+
+struct mlx5e_eth_addr_hash_node {
+ struct hlist_node hlist;
+ u8 action;
+ struct mlx5e_eth_addr_info ai;
+};
+
+static inline int mlx5e_hash_eth_addr(u8 *addr)
+{
+ return addr[5];
+}
+
+static void mlx5e_add_eth_addr_to_hash(struct hlist_head *hash, u8 *addr)
+{
+ struct mlx5e_eth_addr_hash_node *hn;
+ int ix = mlx5e_hash_eth_addr(addr);
+ int found = 0;
+
+ hlist_for_each_entry(hn, &hash[ix], hlist)
+ if (ether_addr_equal_64bits(hn->ai.addr, addr)) {
+ found = 1;
+ break;
+ }
+
+ if (found) {
+ hn->action = MLX5E_ACTION_NONE;
+ return;
+ }
+
+ hn = kzalloc(sizeof(*hn), GFP_ATOMIC);
+ if (!hn)
+ return;
+
+ ether_addr_copy(hn->ai.addr, addr);
+ hn->action = MLX5E_ACTION_ADD;
+
+ hlist_add_head(&hn->hlist, &hash[ix]);
+}
+
+static void mlx5e_del_eth_addr_from_hash(struct mlx5e_eth_addr_hash_node *hn)
+{
+ hlist_del(&hn->hlist);
+ kfree(hn);
+}
+
+static void mlx5e_del_eth_addr_from_flow_table(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_info *ai)
+{
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_TCP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_TCP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_TCP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_TCP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_UDP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_UDP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_UDP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_UDP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_ANY))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_ANY]);
+}
+
+static int mlx5e_get_eth_addr_type(u8 *addr)
+{
+ if (is_unicast_ether_addr(addr))
+ return MLX5E_UC;
+
+ if ((addr[0] == 0x01) &&
+ (addr[1] == 0x00) &&
+ (addr[2] == 0x5e) &&
+ !(addr[3] & 0x80))
+ return MLX5E_MC_IPV4;
+
+ if ((addr[0] == 0x33) &&
+ (addr[1] == 0x33))
+ return MLX5E_MC_IPV6;
+
+ return MLX5E_MC_OTHER;
+}
+
+static u32 mlx5e_get_tt_vec(struct mlx5e_eth_addr_info *ai, int type)
+{
+ int eth_addr_type;
+ u32 ret;
+
+ switch (type) {
+ case MLX5E_FULLMATCH:
+ eth_addr_type = mlx5e_get_eth_addr_type(ai->addr);
+ switch (eth_addr_type) {
+ case MLX5E_UC:
+ ret =
+ BIT(MLX5E_TT_IPV4_TCP) |
+ BIT(MLX5E_TT_IPV6_TCP) |
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV4_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV6_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV4) |
+ BIT(MLX5E_TT_IPV6) |
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+
+ case MLX5E_MC_IPV4:
+ ret =
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV4) |
+ 0;
+ break;
+
+ case MLX5E_MC_IPV6:
+ ret =
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV6) |
+ 0;
+ break;
+
+ case MLX5E_MC_OTHER:
+ ret =
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+ }
+
+ break;
+
+ case MLX5E_ALLMULTI:
+ ret =
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV4) |
+ BIT(MLX5E_TT_IPV6) |
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+
+ default: /* MLX5E_PROMISC */
+ ret =
+ BIT(MLX5E_TT_IPV4_TCP) |
+ BIT(MLX5E_TT_IPV6_TCP) |
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV4_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV6_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV4) |
+ BIT(MLX5E_TT_IPV6) |
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_info *ai,
+ int type, u32 *mc, u32 *mv)
+{
+ struct mlx5_flow_destination dest;
+ u8 match_criteria_enable = 0;
+ struct mlx5_flow_rule **rule_p;
+ struct mlx5_flow_table *ft = priv->fts.main.t;
+ u8 *mc_dmac = MLX5_ADDR_OF(fte_match_param, mc,
+ outer_headers.dmac_47_16);
+ u8 *mv_dmac = MLX5_ADDR_OF(fte_match_param, mv,
+ outer_headers.dmac_47_16);
+ u32 *tirn = priv->tirn;
+ u32 tt_vec;
+ int err = 0;
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
+
+ switch (type) {
+ case MLX5E_FULLMATCH:
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ eth_broadcast_addr(mc_dmac);
+ ether_addr_copy(mv_dmac, ai->addr);
+ break;
+
+ case MLX5E_ALLMULTI:
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ mc_dmac[0] = 0x01;
+ mv_dmac[0] = 0x01;
+ break;
+
+ case MLX5E_PROMISC:
+ break;
+ }
+
+ tt_vec = mlx5e_get_tt_vec(ai, type);
+
+ if (tt_vec & BIT(MLX5E_TT_ANY)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_ANY];
+ dest.tir_num = tirn[MLX5E_TT_ANY];
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_ANY);
+ }
+
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4];
+ dest.tir_num = tirn[MLX5E_TT_IPV4];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6];
+ dest.tir_num = tirn[MLX5E_TT_IPV6];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6);
+ }
+
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_UDP);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_UDP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_UDP];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_UDP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_UDP);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_UDP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_UDP];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_UDP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_UDP);
+ }
+
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_TCP);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_TCP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_TCP];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_TCP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_TCP);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_TCP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_TCP];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_TCP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_TCP);
+ }
+
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_AH);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_AH];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_AH);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_AH];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_AH);
+ }
+
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_ESP);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_ESP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_ESP);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_ESP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_ESP);
+ }
+
+ return 0;
+
+err_del_ai:
+ err = PTR_ERR(*rule_p);
+ *rule_p = NULL;
+ mlx5e_del_eth_addr_from_flow_table(priv, ai);
+
+ return err;
+}
+
+static int mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_info *ai, int type)
+{
+ u32 *match_criteria;
+ u32 *match_value;
+ int err = 0;
+
+ match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!match_value || !match_criteria) {
+ netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
+ err = -ENOMEM;
+ goto add_eth_addr_rule_out;
+ }
+
+ err = __mlx5e_add_eth_addr_rule(priv, ai, type, match_criteria,
+ match_value);
+
+add_eth_addr_rule_out:
+ kvfree(match_criteria);
+ kvfree(match_value);
+
+ return err;
+}
+
+static int mlx5e_vport_context_update_vlans(struct mlx5e_priv *priv)
+{
+ struct net_device *ndev = priv->netdev;
+ int max_list_size;
+ int list_size;
+ u16 *vlans;
+ int vlan;
+ int err;
+ int i;
+
+ list_size = 0;
+ for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID)
+ list_size++;
+
+ max_list_size = 1 << MLX5_CAP_GEN(priv->mdev, log_max_vlan_list);
+
+ if (list_size > max_list_size) {
+ netdev_warn(ndev,
+ "netdev vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n",
+ list_size, max_list_size);
+ list_size = max_list_size;
+ }
+
+ vlans = kcalloc(list_size, sizeof(*vlans), GFP_KERNEL);
+ if (!vlans)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) {
+ if (i >= list_size)
+ break;
+ vlans[i++] = vlan;
+ }
+
+ err = mlx5_modify_nic_vport_vlans(priv->mdev, vlans, list_size);
+ if (err)
+ netdev_err(ndev, "Failed to modify vport vlans list err(%d)\n",
+ err);
+
+ kfree(vlans);
+ return err;
+}
+
+enum mlx5e_vlan_rule_type {
+ MLX5E_VLAN_RULE_TYPE_UNTAGGED,
+ MLX5E_VLAN_RULE_TYPE_ANY_VID,
+ MLX5E_VLAN_RULE_TYPE_MATCH_VID,
+};
+
+static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv,
+ enum mlx5e_vlan_rule_type rule_type,
+ u16 vid, u32 *mc, u32 *mv)
+{
+ struct mlx5_flow_table *ft = priv->fts.vlan.t;
+ struct mlx5_flow_destination dest;
+ u8 match_criteria_enable = 0;
+ struct mlx5_flow_rule **rule_p;
+ int err = 0;
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest.ft = priv->fts.main.t;
+
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag);
+
+ switch (rule_type) {
+ case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
+ rule_p = &priv->vlan.untagged_rule;
+ break;
+ case MLX5E_VLAN_RULE_TYPE_ANY_VID:
+ rule_p = &priv->vlan.any_vlan_rule;
+ MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1);
+ break;
+ default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */
+ rule_p = &priv->vlan.active_vlans_rule[vid];
+ MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid);
+ MLX5_SET(fte_match_param, mv, outer_headers.first_vid, vid);
+ break;
+ }
+
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG,
+ &dest);
+
+ if (IS_ERR(*rule_p)) {
+ err = PTR_ERR(*rule_p);
+ *rule_p = NULL;
+ netdev_err(priv->netdev, "%s: add rule failed\n", __func__);
+ }
+
+ return err;
+}
+
+static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv,
+ enum mlx5e_vlan_rule_type rule_type, u16 vid)
+{
+ u32 *match_criteria;
+ u32 *match_value;
+ int err = 0;
+
+ match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!match_value || !match_criteria) {
+ netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
+ err = -ENOMEM;
+ goto add_vlan_rule_out;
+ }
+
+ if (rule_type == MLX5E_VLAN_RULE_TYPE_MATCH_VID)
+ mlx5e_vport_context_update_vlans(priv);
+
+ err = __mlx5e_add_vlan_rule(priv, rule_type, vid, match_criteria,
+ match_value);
+
+add_vlan_rule_out:
+ kvfree(match_criteria);
+ kvfree(match_value);
+
+ return err;
+}
+
+static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv,
+ enum mlx5e_vlan_rule_type rule_type, u16 vid)
+{
+ switch (rule_type) {
+ case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
+ if (priv->vlan.untagged_rule) {
+ mlx5_del_flow_rule(priv->vlan.untagged_rule);
+ priv->vlan.untagged_rule = NULL;
+ }
+ break;
+ case MLX5E_VLAN_RULE_TYPE_ANY_VID:
+ if (priv->vlan.any_vlan_rule) {
+ mlx5_del_flow_rule(priv->vlan.any_vlan_rule);
+ priv->vlan.any_vlan_rule = NULL;
+ }
+ break;
+ case MLX5E_VLAN_RULE_TYPE_MATCH_VID:
+ mlx5e_vport_context_update_vlans(priv);
+ if (priv->vlan.active_vlans_rule[vid]) {
+ mlx5_del_flow_rule(priv->vlan.active_vlans_rule[vid]);
+ priv->vlan.active_vlans_rule[vid] = NULL;
+ }
+ mlx5e_vport_context_update_vlans(priv);
+ break;
+ }
+}
+
+void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv)
+{
+ if (!priv->vlan.filter_disabled)
+ return;
+
+ priv->vlan.filter_disabled = false;
+ if (priv->netdev->flags & IFF_PROMISC)
+ return;
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
+}
+
+void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv)
+{
+ if (priv->vlan.filter_disabled)
+ return;
+
+ priv->vlan.filter_disabled = true;
+ if (priv->netdev->flags & IFF_PROMISC)
+ return;
+ mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
+}
+
+int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto,
+ u16 vid)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+
+ set_bit(vid, priv->vlan.active_vlans);
+
+ return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
+}
+
+int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto,
+ u16 vid)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+
+ clear_bit(vid, priv->vlan.active_vlans);
+
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
+
+ return 0;
+}
+
+#define mlx5e_for_each_hash_node(hn, tmp, hash, i) \
+ for (i = 0; i < MLX5E_ETH_ADDR_HASH_SIZE; i++) \
+ hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist)
+
+static void mlx5e_execute_action(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_hash_node *hn)
+{
+ switch (hn->action) {
+ case MLX5E_ACTION_ADD:
+ mlx5e_add_eth_addr_rule(priv, &hn->ai, MLX5E_FULLMATCH);
+ hn->action = MLX5E_ACTION_NONE;
+ break;
+
+ case MLX5E_ACTION_DEL:
+ mlx5e_del_eth_addr_from_flow_table(priv, &hn->ai);
+ mlx5e_del_eth_addr_from_hash(hn);
+ break;
+ }
+}
+
+static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct netdev_hw_addr *ha;
+
+ netif_addr_lock_bh(netdev);
+
+ mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc,
+ priv->netdev->dev_addr);
+
+ netdev_for_each_uc_addr(ha, netdev)
+ mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, ha->addr);
+
+ netdev_for_each_mc_addr(ha, netdev)
+ mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_mc, ha->addr);
+
+ netif_addr_unlock_bh(netdev);
+}
+
+static void mlx5e_fill_addr_array(struct mlx5e_priv *priv, int list_type,
+ u8 addr_array[][ETH_ALEN], int size)
+{
+ bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC);
+ struct net_device *ndev = priv->netdev;
+ struct mlx5e_eth_addr_hash_node *hn;
+ struct hlist_head *addr_list;
+ struct hlist_node *tmp;
+ int i = 0;
+ int hi;
+
+ addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc;
+
+ if (is_uc) /* Make sure our own address is pushed first */
+ ether_addr_copy(addr_array[i++], ndev->dev_addr);
+ else if (priv->eth_addr.broadcast_enabled)
+ ether_addr_copy(addr_array[i++], ndev->broadcast);
+
+ mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) {
+ if (ether_addr_equal(ndev->dev_addr, hn->ai.addr))
+ continue;
+ if (i >= size)
+ break;
+ ether_addr_copy(addr_array[i++], hn->ai.addr);
+ }
+}
+
+static void mlx5e_vport_context_update_addr_list(struct mlx5e_priv *priv,
+ int list_type)
+{
+ bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC);
+ struct mlx5e_eth_addr_hash_node *hn;
+ u8 (*addr_array)[ETH_ALEN] = NULL;
+ struct hlist_head *addr_list;
+ struct hlist_node *tmp;
+ int max_size;
+ int size;
+ int err;
+ int hi;
+
+ size = is_uc ? 0 : (priv->eth_addr.broadcast_enabled ? 1 : 0);
+ max_size = is_uc ?
+ 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_uc_list) :
+ 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_mc_list);
+
+ addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc;
+ mlx5e_for_each_hash_node(hn, tmp, addr_list, hi)
+ size++;
+
+ if (size > max_size) {
+ netdev_warn(priv->netdev,
+ "netdev %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n",
+ is_uc ? "UC" : "MC", size, max_size);
+ size = max_size;
+ }
+
+ if (size) {
+ addr_array = kcalloc(size, ETH_ALEN, GFP_KERNEL);
+ if (!addr_array) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mlx5e_fill_addr_array(priv, list_type, addr_array, size);
+ }
+
+ err = mlx5_modify_nic_vport_mac_list(priv->mdev, list_type, addr_array, size);
+out:
+ if (err)
+ netdev_err(priv->netdev,
+ "Failed to modify vport %s list err(%d)\n",
+ is_uc ? "UC" : "MC", err);
+ kfree(addr_array);
+}
+
+static void mlx5e_vport_context_update(struct mlx5e_priv *priv)
+{
+ struct mlx5e_eth_addr_db *ea = &priv->eth_addr;
+
+ mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_UC);
+ mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_MC);
+ mlx5_modify_nic_vport_promisc(priv->mdev, 0,
+ ea->allmulti_enabled,
+ ea->promisc_enabled);
+}
+
+static void mlx5e_apply_netdev_addr(struct mlx5e_priv *priv)
+{
+ struct mlx5e_eth_addr_hash_node *hn;
+ struct hlist_node *tmp;
+ int i;
+
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
+ mlx5e_execute_action(priv, hn);
+
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
+ mlx5e_execute_action(priv, hn);
+}
+
+static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv)
+{
+ struct mlx5e_eth_addr_hash_node *hn;
+ struct hlist_node *tmp;
+ int i;
+
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
+ hn->action = MLX5E_ACTION_DEL;
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
+ hn->action = MLX5E_ACTION_DEL;
+
+ if (!test_bit(MLX5E_STATE_DESTROYING, &priv->state))
+ mlx5e_sync_netdev_addr(priv);
+
+ mlx5e_apply_netdev_addr(priv);
+}
+
+void mlx5e_set_rx_mode_work(struct work_struct *work)
+{
+ struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
+ set_rx_mode_work);
+
+ struct mlx5e_eth_addr_db *ea = &priv->eth_addr;
+ struct net_device *ndev = priv->netdev;
+
+ bool rx_mode_enable = !test_bit(MLX5E_STATE_DESTROYING, &priv->state);
+ bool promisc_enabled = rx_mode_enable && (ndev->flags & IFF_PROMISC);
+ bool allmulti_enabled = rx_mode_enable && (ndev->flags & IFF_ALLMULTI);
+ bool broadcast_enabled = rx_mode_enable;
+
+ bool enable_promisc = !ea->promisc_enabled && promisc_enabled;
+ bool disable_promisc = ea->promisc_enabled && !promisc_enabled;
+ bool enable_allmulti = !ea->allmulti_enabled && allmulti_enabled;
+ bool disable_allmulti = ea->allmulti_enabled && !allmulti_enabled;
+ bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled;
+ bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled;
+
+ if (enable_promisc) {
+ mlx5e_add_eth_addr_rule(priv, &ea->promisc, MLX5E_PROMISC);
+ if (!priv->vlan.filter_disabled)
+ mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
+ 0);
+ }
+ if (enable_allmulti)
+ mlx5e_add_eth_addr_rule(priv, &ea->allmulti, MLX5E_ALLMULTI);
+ if (enable_broadcast)
+ mlx5e_add_eth_addr_rule(priv, &ea->broadcast, MLX5E_FULLMATCH);
+
+ mlx5e_handle_netdev_addr(priv);
+
+ if (disable_broadcast)
+ mlx5e_del_eth_addr_from_flow_table(priv, &ea->broadcast);
+ if (disable_allmulti)
+ mlx5e_del_eth_addr_from_flow_table(priv, &ea->allmulti);
+ if (disable_promisc) {
+ if (!priv->vlan.filter_disabled)
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
+ 0);
+ mlx5e_del_eth_addr_from_flow_table(priv, &ea->promisc);
+ }
+
+ ea->promisc_enabled = promisc_enabled;
+ ea->allmulti_enabled = allmulti_enabled;
+ ea->broadcast_enabled = broadcast_enabled;
+
+ mlx5e_vport_context_update(priv);
+}
+
+static void mlx5e_destroy_groups(struct mlx5e_flow_table *ft)
+{
+ int i;
+
+ for (i = ft->num_groups - 1; i >= 0; i--) {
+ if (!IS_ERR_OR_NULL(ft->g[i]))
+ mlx5_destroy_flow_group(ft->g[i]);
+ ft->g[i] = NULL;
+ }
+ ft->num_groups = 0;
+}
+
+void mlx5e_init_eth_addr(struct mlx5e_priv *priv)
+{
+ ether_addr_copy(priv->eth_addr.broadcast.addr, priv->netdev->broadcast);
+}
+
+#define MLX5E_MAIN_GROUP0_SIZE BIT(3)
+#define MLX5E_MAIN_GROUP1_SIZE BIT(1)
+#define MLX5E_MAIN_GROUP2_SIZE BIT(0)
+#define MLX5E_MAIN_GROUP3_SIZE BIT(14)
+#define MLX5E_MAIN_GROUP4_SIZE BIT(13)
+#define MLX5E_MAIN_GROUP5_SIZE BIT(11)
+#define MLX5E_MAIN_GROUP6_SIZE BIT(2)
+#define MLX5E_MAIN_GROUP7_SIZE BIT(1)
+#define MLX5E_MAIN_GROUP8_SIZE BIT(0)
+#define MLX5E_MAIN_TABLE_SIZE (MLX5E_MAIN_GROUP0_SIZE +\
+ MLX5E_MAIN_GROUP1_SIZE +\
+ MLX5E_MAIN_GROUP2_SIZE +\
+ MLX5E_MAIN_GROUP3_SIZE +\
+ MLX5E_MAIN_GROUP4_SIZE +\
+ MLX5E_MAIN_GROUP5_SIZE +\
+ MLX5E_MAIN_GROUP6_SIZE +\
+ MLX5E_MAIN_GROUP7_SIZE +\
+ MLX5E_MAIN_GROUP8_SIZE)
+
+static int __mlx5e_create_main_groups(struct mlx5e_flow_table *ft, u32 *in,
+ int inlen)
+{
+ u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
+ u8 *dmac = MLX5_ADDR_OF(create_flow_group_in, in,
+ match_criteria.outer_headers.dmac_47_16);
+ int err;
+ int ix = 0;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP0_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP1_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP2_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ eth_broadcast_addr(dmac);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP3_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ eth_broadcast_addr(dmac);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP4_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ eth_broadcast_addr(dmac);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP5_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ dmac[0] = 0x01;
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP6_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ dmac[0] = 0x01;
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP7_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ dmac[0] = 0x01;
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP8_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ return 0;
+
+err_destroy_groups:
+ err = PTR_ERR(ft->g[ft->num_groups]);
+ ft->g[ft->num_groups] = NULL;
+ mlx5e_destroy_groups(ft);
+
+ return err;
+}
+
+static int mlx5e_create_main_groups(struct mlx5e_flow_table *ft)
+{
+ u32 *in;
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in)
+ return -ENOMEM;
+
+ err = __mlx5e_create_main_groups(ft, in, inlen);
+
+ kvfree(in);
+ return err;
+}
+
+static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv)
+{
+ struct mlx5e_flow_table *ft = &priv->fts.main;
+ int err;
+
+ ft->num_groups = 0;
+ ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_MAIN_TABLE_SIZE);
+
+ if (IS_ERR(ft->t)) {
+ err = PTR_ERR(ft->t);
+ ft->t = NULL;
+ return err;
+ }
+ ft->g = kcalloc(MLX5E_NUM_MAIN_GROUPS, sizeof(*ft->g), GFP_KERNEL);
+ if (!ft->g) {
+ err = -ENOMEM;
+ goto err_destroy_main_flow_table;
+ }
+
+ err = mlx5e_create_main_groups(ft);
+ if (err)
+ goto err_free_g;
+ return 0;
+
+err_free_g:
+ kfree(ft->g);
+
+err_destroy_main_flow_table:
+ mlx5_destroy_flow_table(ft->t);
+ ft->t = NULL;
+
+ return err;
+}
+
+static void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft)
+{
+ mlx5e_destroy_groups(ft);
+ kfree(ft->g);
+ mlx5_destroy_flow_table(ft->t);
+ ft->t = NULL;
+}
+
+static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv)
+{
+ mlx5e_destroy_flow_table(&priv->fts.main);
+}
+
+#define MLX5E_NUM_VLAN_GROUPS 2
+#define MLX5E_VLAN_GROUP0_SIZE BIT(12)
+#define MLX5E_VLAN_GROUP1_SIZE BIT(1)
+#define MLX5E_VLAN_TABLE_SIZE (MLX5E_VLAN_GROUP0_SIZE +\
+ MLX5E_VLAN_GROUP1_SIZE)
+
+static int __mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft, u32 *in,
+ int inlen)
+{
+ int err;
+ int ix = 0;
+ u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_VLAN_GROUP0_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_VLAN_GROUP1_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ return 0;
+
+err_destroy_groups:
+ err = PTR_ERR(ft->g[ft->num_groups]);
+ ft->g[ft->num_groups] = NULL;
+ mlx5e_destroy_groups(ft);
+
+ return err;
+}
+
+static int mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft)
+{
+ u32 *in;
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in)
+ return -ENOMEM;
+
+ err = __mlx5e_create_vlan_groups(ft, in, inlen);
+
+ kvfree(in);
+ return err;
+}
+
+static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv)
+{
+ struct mlx5e_flow_table *ft = &priv->fts.vlan;
+ int err;
+
+ ft->num_groups = 0;
+ ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_VLAN_TABLE_SIZE);
+
+ if (IS_ERR(ft->t)) {
+ err = PTR_ERR(ft->t);
+ ft->t = NULL;
+ return err;
+ }
+ ft->g = kcalloc(MLX5E_NUM_VLAN_GROUPS, sizeof(*ft->g), GFP_KERNEL);
+ if (!ft->g) {
+ err = -ENOMEM;
+ goto err_destroy_vlan_flow_table;
+ }
+
+ err = mlx5e_create_vlan_groups(ft);
+ if (err)
+ goto err_free_g;
+
+ return 0;
+
+err_free_g:
+ kfree(ft->g);
+
+err_destroy_vlan_flow_table:
+ mlx5_destroy_flow_table(ft->t);
+ ft->t = NULL;
+
+ return err;
+}
+
+static void mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv)
+{
+ mlx5e_destroy_flow_table(&priv->fts.vlan);
+}
+
+int mlx5e_create_flow_tables(struct mlx5e_priv *priv)
+{
+ int err;
+
+ priv->fts.ns = mlx5_get_flow_namespace(priv->mdev,
+ MLX5_FLOW_NAMESPACE_KERNEL);
+
+ if (!priv->fts.ns)
+ return -EINVAL;
+
+ err = mlx5e_create_vlan_flow_table(priv);
+ if (err)
+ return err;
+
+ err = mlx5e_create_main_flow_table(priv);
+ if (err)
+ goto err_destroy_vlan_flow_table;
+
+ err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
+ if (err)
+ goto err_destroy_main_flow_table;
+
+ return 0;
+
+err_destroy_main_flow_table:
+ mlx5e_destroy_main_flow_table(priv);
+err_destroy_vlan_flow_table:
+ mlx5e_destroy_vlan_flow_table(priv);
+
+ return err;
+}
+
+void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv)
+{
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
+ mlx5e_destroy_main_flow_table(priv);
+ mlx5e_destroy_vlan_flow_table(priv);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 1e52db32c73d..5c74a734f158 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -30,8 +30,9 @@
* SOFTWARE.
*/
-#include <linux/mlx5/flow_table.h>
+#include <linux/mlx5/fs.h>
#include "en.h"
+#include "eswitch.h"
struct mlx5e_rq_param {
u32 rqc[MLX5_ST_SZ_DW(rqc)];
@@ -63,7 +64,7 @@ static void mlx5e_update_carrier(struct mlx5e_priv *priv)
u8 port_state;
port_state = mlx5_query_vport_state(mdev,
- MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT);
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT, 0);
if (port_state == VPORT_STATE_UP)
netif_carrier_on(priv->netdev);
@@ -350,6 +351,7 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
rq->pdev = c->pdev;
rq->netdev = c->netdev;
+ rq->tstamp = &priv->tstamp;
rq->channel = c;
rq->ix = c->ix;
rq->priv = c->priv;
@@ -506,6 +508,7 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq)
static void mlx5e_free_sq_db(struct mlx5e_sq *sq)
{
+ kfree(sq->wqe_info);
kfree(sq->dma_fifo);
kfree(sq->skb);
}
@@ -518,8 +521,10 @@ static int mlx5e_alloc_sq_db(struct mlx5e_sq *sq, int numa)
sq->skb = kzalloc_node(wq_sz * sizeof(*sq->skb), GFP_KERNEL, numa);
sq->dma_fifo = kzalloc_node(df_sz * sizeof(*sq->dma_fifo), GFP_KERNEL,
numa);
+ sq->wqe_info = kzalloc_node(wq_sz * sizeof(*sq->wqe_info), GFP_KERNEL,
+ numa);
- if (!sq->skb || !sq->dma_fifo) {
+ if (!sq->skb || !sq->dma_fifo || !sq->wqe_info) {
mlx5e_free_sq_db(sq);
return -ENOMEM;
}
@@ -567,6 +572,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c,
sq->txq = netdev_get_tx_queue(priv->netdev, txq_ix);
sq->pdev = c->pdev;
+ sq->tstamp = &priv->tstamp;
sq->mkey_be = c->mkey_be;
sq->channel = c;
sq->tc = tc;
@@ -1020,6 +1026,7 @@ err_close_tx_cqs:
err_napi_del:
netif_napi_del(&c->napi);
+ napi_hash_del(&c->napi);
kfree(c);
return err;
@@ -1033,6 +1040,10 @@ static void mlx5e_close_channel(struct mlx5e_channel *c)
mlx5e_close_cq(&c->rq.cq);
mlx5e_close_tx_cqs(c);
netif_napi_del(&c->napi);
+
+ napi_hash_del(&c->napi);
+ synchronize_rcu();
+
kfree(c);
}
@@ -1421,6 +1432,7 @@ int mlx5e_open_locked(struct net_device *netdev)
mlx5e_update_carrier(priv);
mlx5e_redirect_rqts(priv);
+ mlx5e_timestamp_init(priv);
schedule_delayed_work(&priv->update_stats_work, 0);
@@ -1457,6 +1469,7 @@ int mlx5e_close_locked(struct net_device *netdev)
clear_bit(MLX5E_STATE_OPENED, &priv->state);
+ mlx5e_timestamp_cleanup(priv);
mlx5e_redirect_rqts(priv);
netif_carrier_off(priv->netdev);
mlx5e_close_channels(priv);
@@ -1926,6 +1939,91 @@ static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu)
return err;
}
+static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCSHWTSTAMP:
+ return mlx5e_hwstamp_set(dev, ifr);
+ case SIOCGHWTSTAMP:
+ return mlx5e_hwstamp_get(dev, ifr);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_set_vport_mac(mdev->priv.eswitch, vf + 1, mac);
+}
+
+static int mlx5e_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_set_vport_vlan(mdev->priv.eswitch, vf + 1,
+ vlan, qos);
+}
+
+static int mlx5_vport_link2ifla(u8 esw_link)
+{
+ switch (esw_link) {
+ case MLX5_ESW_VPORT_ADMIN_STATE_DOWN:
+ return IFLA_VF_LINK_STATE_DISABLE;
+ case MLX5_ESW_VPORT_ADMIN_STATE_UP:
+ return IFLA_VF_LINK_STATE_ENABLE;
+ }
+ return IFLA_VF_LINK_STATE_AUTO;
+}
+
+static int mlx5_ifla_link2vport(u8 ifla_link)
+{
+ switch (ifla_link) {
+ case IFLA_VF_LINK_STATE_DISABLE:
+ return MLX5_ESW_VPORT_ADMIN_STATE_DOWN;
+ case IFLA_VF_LINK_STATE_ENABLE:
+ return MLX5_ESW_VPORT_ADMIN_STATE_UP;
+ }
+ return MLX5_ESW_VPORT_ADMIN_STATE_AUTO;
+}
+
+static int mlx5e_set_vf_link_state(struct net_device *dev, int vf,
+ int link_state)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_set_vport_state(mdev->priv.eswitch, vf + 1,
+ mlx5_ifla_link2vport(link_state));
+}
+
+static int mlx5e_get_vf_config(struct net_device *dev,
+ int vf, struct ifla_vf_info *ivi)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+ int err;
+
+ err = mlx5_eswitch_get_vport_config(mdev->priv.eswitch, vf + 1, ivi);
+ if (err)
+ return err;
+ ivi->linkstate = mlx5_vport_link2ifla(ivi->linkstate);
+ return 0;
+}
+
+static int mlx5e_get_vf_stats(struct net_device *dev,
+ int vf, struct ifla_vf_stats *vf_stats)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_get_vport_stats(mdev->priv.eswitch, vf + 1,
+ vf_stats);
+}
+
static struct net_device_ops mlx5e_netdev_ops = {
.ndo_open = mlx5e_open,
.ndo_stop = mlx5e_close,
@@ -1937,6 +2035,7 @@ static struct net_device_ops mlx5e_netdev_ops = {
.ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid,
.ndo_set_features = mlx5e_set_features,
.ndo_change_mtu = mlx5e_change_mtu,
+ .ndo_do_ioctl = mlx5e_ioctl,
};
static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev)
@@ -2023,7 +2122,12 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
- mlx5_query_nic_vport_mac_address(priv->mdev, netdev->dev_addr);
+ mlx5_query_nic_vport_mac_address(priv->mdev, 0, netdev->dev_addr);
+ if (is_zero_ether_addr(netdev->dev_addr) &&
+ !MLX5_CAP_GEN(priv->mdev, vport_group_manager)) {
+ eth_hw_addr_random(netdev);
+ mlx5_core_info(priv->mdev, "Assigned random MAC address %pM\n", netdev->dev_addr);
+ }
}
static void mlx5e_build_netdev(struct net_device *netdev)
@@ -2036,6 +2140,14 @@ static void mlx5e_build_netdev(struct net_device *netdev)
if (priv->params.num_tc > 1)
mlx5e_netdev_ops.ndo_select_queue = mlx5e_select_queue;
+ if (MLX5_CAP_GEN(mdev, vport_group_manager)) {
+ mlx5e_netdev_ops.ndo_set_vf_mac = mlx5e_set_vf_mac;
+ mlx5e_netdev_ops.ndo_set_vf_vlan = mlx5e_set_vf_vlan;
+ mlx5e_netdev_ops.ndo_get_vf_config = mlx5e_get_vf_config;
+ mlx5e_netdev_ops.ndo_set_vf_link_state = mlx5e_set_vf_link_state;
+ mlx5e_netdev_ops.ndo_get_vf_stats = mlx5e_get_vf_stats;
+ }
+
netdev->netdev_ops = &mlx5e_netdev_ops;
netdev->watchdog_timeo = 15 * HZ;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index cf0098596e85..dd959d929aad 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -33,8 +33,14 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
+#include <net/busy_poll.h>
#include "en.h"
+static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp)
+{
+ return tstamp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL;
+}
+
static inline int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq,
struct mlx5e_rx_wqe *wqe, u16 ix)
{
@@ -189,6 +195,7 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
{
struct net_device *netdev = rq->netdev;
u32 cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
+ struct mlx5e_tstamp *tstamp = rq->tstamp;
int lro_num_seg;
skb_put(skb, cqe_bcnt);
@@ -201,6 +208,9 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
rq->stats.lro_bytes += cqe_bcnt;
}
+ if (unlikely(mlx5e_rx_hw_stamp(tstamp)))
+ mlx5e_fill_hwstamp(tstamp, get_cqe_ts(cqe), skb_hwtstamps(skb));
+
mlx5e_handle_csum(netdev, cqe, rq, skb);
skb->protocol = eth_type_trans(skb, netdev);
@@ -215,16 +225,16 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
be16_to_cpu(cqe->vlan_info));
}
-bool mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
+int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
{
struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq);
- int i;
+ int work_done;
/* avoid accessing cq (dma coherent memory) if not needed */
if (!test_and_clear_bit(MLX5E_CQ_HAS_CQES, &cq->flags))
- return false;
+ return 0;
- for (i = 0; i < budget; i++) {
+ for (work_done = 0; work_done < budget; work_done++) {
struct mlx5e_rx_wqe *wqe;
struct mlx5_cqe64 *cqe;
struct sk_buff *skb;
@@ -269,10 +279,8 @@ wq_ll_pop:
/* ensure cq space is freed before enabling more cqes */
wmb();
- if (i == budget) {
+ if (work_done == budget)
set_bit(MLX5E_CQ_HAS_CQES, &cq->flags);
- return true;
- }
- return false;
+ return work_done;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
index 1341b1d3c421..2c3fba0fff54 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
@@ -92,11 +92,11 @@ static inline struct mlx5e_sq_dma *mlx5e_dma_get(struct mlx5e_sq *sq, u32 i)
return &sq->dma_fifo[i & sq->dma_fifo_mask];
}
-static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, struct sk_buff *skb)
+static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, u8 num_dma)
{
int i;
- for (i = 0; i < MLX5E_TX_SKB_CB(skb)->num_dma; i++) {
+ for (i = 0; i < num_dma; i++) {
struct mlx5e_sq_dma *last_pushed_dma =
mlx5e_dma_get(sq, --sq->dma_fifo_pc);
@@ -139,19 +139,28 @@ static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq,
return MLX5E_MIN_INLINE;
}
-static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs)
+static inline void mlx5e_tx_skb_pull_inline(unsigned char **skb_data,
+ unsigned int *skb_len,
+ unsigned int len)
+{
+ *skb_len -= len;
+ *skb_data += len;
+}
+
+static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs,
+ unsigned char **skb_data,
+ unsigned int *skb_len)
{
struct vlan_ethhdr *vhdr = (struct vlan_ethhdr *)start;
int cpy1_sz = 2 * ETH_ALEN;
int cpy2_sz = ihs - cpy1_sz;
- skb_copy_from_linear_data(skb, vhdr, cpy1_sz);
- skb_pull_inline(skb, cpy1_sz);
+ memcpy(vhdr, *skb_data, cpy1_sz);
+ mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy1_sz);
vhdr->h_vlan_proto = skb->vlan_proto;
vhdr->h_vlan_TCI = cpu_to_be16(skb_vlan_tag_get(skb));
- skb_copy_from_linear_data(skb, &vhdr->h_vlan_encapsulated_proto,
- cpy2_sz);
- skb_pull_inline(skb, cpy2_sz);
+ memcpy(&vhdr->h_vlan_encapsulated_proto, *skb_data, cpy2_sz);
+ mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy2_sz);
}
static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
@@ -160,11 +169,14 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
u16 pi = sq->pc & wq->sz_m1;
struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi);
+ struct mlx5e_tx_wqe_info *wi = &sq->wqe_info[pi];
struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl;
struct mlx5_wqe_eth_seg *eseg = &wqe->eth;
struct mlx5_wqe_data_seg *dseg;
+ unsigned char *skb_data = skb->data;
+ unsigned int skb_len = skb->len;
u8 opcode = MLX5_OPCODE_SEND;
dma_addr_t dma_addr = 0;
bool bf = false;
@@ -192,8 +204,8 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
opcode = MLX5_OPCODE_LSO;
ihs = skb_transport_offset(skb) + tcp_hdrlen(skb);
payload_len = skb->len - ihs;
- MLX5E_TX_SKB_CB(skb)->num_bytes = skb->len +
- (skb_shinfo(skb)->gso_segs - 1) * ihs;
+ wi->num_bytes = skb->len +
+ (skb_shinfo(skb)->gso_segs - 1) * ihs;
sq->stats.tso_packets++;
sq->stats.tso_bytes += payload_len;
} else {
@@ -201,16 +213,16 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
!skb->xmit_more &&
!skb_shinfo(skb)->nr_frags;
ihs = mlx5e_get_inline_hdr_size(sq, skb, bf);
- MLX5E_TX_SKB_CB(skb)->num_bytes = max_t(unsigned int, skb->len,
- ETH_ZLEN);
+ wi->num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN);
}
if (skb_vlan_tag_present(skb)) {
- mlx5e_insert_vlan(eseg->inline_hdr_start, skb, ihs);
+ mlx5e_insert_vlan(eseg->inline_hdr_start, skb, ihs, &skb_data,
+ &skb_len);
ihs += VLAN_HLEN;
} else {
- skb_copy_from_linear_data(skb, eseg->inline_hdr_start, ihs);
- skb_pull_inline(skb, ihs);
+ memcpy(eseg->inline_hdr_start, skb_data, ihs);
+ mlx5e_tx_skb_pull_inline(&skb_data, &skb_len, ihs);
}
eseg->inline_hdr_sz = cpu_to_be16(ihs);
@@ -220,11 +232,11 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
MLX5_SEND_WQE_DS);
dseg = (struct mlx5_wqe_data_seg *)cseg + ds_cnt;
- MLX5E_TX_SKB_CB(skb)->num_dma = 0;
+ wi->num_dma = 0;
- headlen = skb_headlen(skb);
+ headlen = skb_len - skb->data_len;
if (headlen) {
- dma_addr = dma_map_single(sq->pdev, skb->data, headlen,
+ dma_addr = dma_map_single(sq->pdev, skb_data, headlen,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(sq->pdev, dma_addr)))
goto dma_unmap_wqe_err;
@@ -234,7 +246,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
dseg->byte_count = cpu_to_be32(headlen);
mlx5e_dma_push(sq, dma_addr, headlen, MLX5E_DMA_MAP_SINGLE);
- MLX5E_TX_SKB_CB(skb)->num_dma++;
+ wi->num_dma++;
dseg++;
}
@@ -253,23 +265,25 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
dseg->byte_count = cpu_to_be32(fsz);
mlx5e_dma_push(sq, dma_addr, fsz, MLX5E_DMA_MAP_PAGE);
- MLX5E_TX_SKB_CB(skb)->num_dma++;
+ wi->num_dma++;
dseg++;
}
- ds_cnt += MLX5E_TX_SKB_CB(skb)->num_dma;
+ ds_cnt += wi->num_dma;
cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode);
cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt);
sq->skb[pi] = skb;
- MLX5E_TX_SKB_CB(skb)->num_wqebbs = DIV_ROUND_UP(ds_cnt,
- MLX5_SEND_WQEBB_NUM_DS);
- sq->pc += MLX5E_TX_SKB_CB(skb)->num_wqebbs;
+ wi->num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS);
+ sq->pc += wi->num_wqebbs;
+
+ netdev_tx_sent_queue(sq->txq, wi->num_bytes);
- netdev_tx_sent_queue(sq->txq, MLX5E_TX_SKB_CB(skb)->num_bytes);
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
if (unlikely(!mlx5e_sq_has_room_for(sq, MLX5E_SQ_STOP_ROOM))) {
netif_tx_stop_queue(sq->txq);
@@ -280,7 +294,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
int bf_sz = 0;
if (bf && sq->uar_bf_map)
- bf_sz = MLX5E_TX_SKB_CB(skb)->num_wqebbs << 3;
+ bf_sz = wi->num_wqebbs << 3;
cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
mlx5e_tx_notify_hw(sq, wqe, bf_sz);
@@ -297,7 +311,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
dma_unmap_wqe_err:
sq->stats.dropped++;
- mlx5e_dma_unmap_wqe_err(sq, skb);
+ mlx5e_dma_unmap_wqe_err(sq, wi->num_dma);
dev_kfree_skb_any(skb);
@@ -352,6 +366,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
wqe_counter = be16_to_cpu(cqe->wqe_counter);
do {
+ struct mlx5e_tx_wqe_info *wi;
struct sk_buff *skb;
u16 ci;
int j;
@@ -360,6 +375,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
ci = sqcc & sq->wq.sz_m1;
skb = sq->skb[ci];
+ wi = &sq->wqe_info[ci];
if (unlikely(!skb)) { /* nop */
sq->stats.nop++;
@@ -367,7 +383,16 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
continue;
}
- for (j = 0; j < MLX5E_TX_SKB_CB(skb)->num_dma; j++) {
+ if (unlikely(skb_shinfo(skb)->tx_flags &
+ SKBTX_HW_TSTAMP)) {
+ struct skb_shared_hwtstamps hwts = {};
+
+ mlx5e_fill_hwstamp(sq->tstamp,
+ get_cqe_ts(cqe), &hwts);
+ skb_tstamp_tx(skb, &hwts);
+ }
+
+ for (j = 0; j < wi->num_dma; j++) {
struct mlx5e_sq_dma *dma =
mlx5e_dma_get(sq, dma_fifo_cc++);
@@ -375,8 +400,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
}
npkts++;
- nbytes += MLX5E_TX_SKB_CB(skb)->num_bytes;
- sqcc += MLX5E_TX_SKB_CB(skb)->num_wqebbs;
+ nbytes += wi->num_bytes;
+ sqcc += wi->num_wqebbs;
dev_kfree_skb(skb);
} while (!last_wqe);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
index 2c7cb6755d1d..4ac8d716dbdd 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
@@ -54,6 +54,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
struct mlx5e_channel *c = container_of(napi, struct mlx5e_channel,
napi);
bool busy = false;
+ int work_done;
int i;
clear_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags);
@@ -61,26 +62,26 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
for (i = 0; i < c->num_tc; i++)
busy |= mlx5e_poll_tx_cq(&c->sq[i].cq);
- busy |= mlx5e_poll_rx_cq(&c->rq.cq, budget);
-
+ work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget);
+ busy |= work_done == budget;
busy |= mlx5e_post_rx_wqes(&c->rq);
if (busy)
return budget;
- napi_complete(napi);
+ napi_complete_done(napi, work_done);
/* avoid losing completion event during/after polling cqs */
if (test_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags)) {
napi_schedule(napi);
- return 0;
+ return work_done;
}
for (i = 0; i < c->num_tc; i++)
mlx5e_cq_arm(&c->sq[i].cq);
mlx5e_cq_arm(&c->rq.cq);
- return 0;
+ return work_done;
}
void mlx5e_completion_event(struct mlx5_core_cq *mcq)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
index 713ead583347..23c244a7e5d7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
@@ -35,6 +35,9 @@
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cmd.h>
#include "mlx5_core.h"
+#ifdef CONFIG_MLX5_CORE_EN
+#include "eswitch.h"
+#endif
enum {
MLX5_EQE_SIZE = sizeof(struct mlx5_eqe),
@@ -287,6 +290,11 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
break;
#endif
+#ifdef CONFIG_MLX5_CORE_EN
+ case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE:
+ mlx5_eswitch_vport_event(dev->priv.eswitch, eqe);
+ break;
+#endif
default:
mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n",
eqe->type, eq->eqn);
@@ -459,6 +467,11 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev)
if (MLX5_CAP_GEN(dev, pg))
async_event_mask |= (1ull << MLX5_EVENT_TYPE_PAGE_FAULT);
+ if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH &&
+ MLX5_CAP_GEN(dev, vport_group_manager) &&
+ mlx5_core_is_pf(dev))
+ async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE);
+
err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD,
MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD,
"mlx5_cmd_eq", &dev->priv.uuari.uars[0]);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
new file mode 100644
index 000000000000..bc3d9f8a75c1
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -0,0 +1,1097 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/mlx5_ifc.h>
+#include <linux/mlx5/vport.h>
+#include <linux/mlx5/fs.h>
+#include "mlx5_core.h"
+#include "eswitch.h"
+
+#define UPLINK_VPORT 0xFFFF
+
+#define MLX5_DEBUG_ESWITCH_MASK BIT(3)
+
+#define esw_info(dev, format, ...) \
+ pr_info("(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
+
+#define esw_warn(dev, format, ...) \
+ pr_warn("(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
+
+#define esw_debug(dev, format, ...) \
+ mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__)
+
+enum {
+ MLX5_ACTION_NONE = 0,
+ MLX5_ACTION_ADD = 1,
+ MLX5_ACTION_DEL = 2,
+};
+
+/* E-Switch UC L2 table hash node */
+struct esw_uc_addr {
+ struct l2addr_node node;
+ u32 table_index;
+ u32 vport;
+};
+
+/* E-Switch MC FDB table hash node */
+struct esw_mc_addr { /* SRIOV only */
+ struct l2addr_node node;
+ struct mlx5_flow_rule *uplink_rule; /* Forward to uplink rule */
+ u32 refcnt;
+};
+
+/* Vport UC/MC hash node */
+struct vport_addr {
+ struct l2addr_node node;
+ u8 action;
+ u32 vport;
+ struct mlx5_flow_rule *flow_rule; /* SRIOV only */
+};
+
+enum {
+ UC_ADDR_CHANGE = BIT(0),
+ MC_ADDR_CHANGE = BIT(1),
+};
+
+/* Vport context events */
+#define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \
+ MC_ADDR_CHANGE)
+
+static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
+ u32 events_mask)
+{
+ int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)];
+ int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+ void *nic_vport_ctx;
+ int err;
+
+ memset(out, 0, sizeof(out));
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(modify_nic_vport_context_in, in,
+ opcode, MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+ MLX5_SET(modify_nic_vport_context_in, in, field_select.change_event, 1);
+ MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
+ in, nic_vport_context);
+
+ MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1);
+
+ if (events_mask & UC_ADDR_CHANGE)
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ event_on_uc_address_change, 1);
+ if (events_mask & MC_ADDR_CHANGE)
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ event_on_mc_address_change, 1);
+
+ err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+ if (err)
+ goto ex;
+ err = mlx5_cmd_status_to_err_v2(out);
+ if (err)
+ goto ex;
+ return 0;
+ex:
+ return err;
+}
+
+/* E-Switch vport context HW commands */
+static int query_esw_vport_context_cmd(struct mlx5_core_dev *mdev, u32 vport,
+ u32 *out, int outlen)
+{
+ u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)];
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(query_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
+
+ MLX5_SET(query_esw_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_esw_vport_context_in, in, other_vport, 1);
+
+ return mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
+}
+
+static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
+ u16 *vlan, u8 *qos)
+{
+ u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)];
+ int err;
+ bool cvlan_strip;
+ bool cvlan_insert;
+
+ memset(out, 0, sizeof(out));
+
+ *vlan = 0;
+ *qos = 0;
+
+ if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
+ !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
+ return -ENOTSUPP;
+
+ err = query_esw_vport_context_cmd(dev, vport, out, sizeof(out));
+ if (err)
+ goto out;
+
+ cvlan_strip = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.vport_cvlan_strip);
+
+ cvlan_insert = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.vport_cvlan_insert);
+
+ if (cvlan_strip || cvlan_insert) {
+ *vlan = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.cvlan_id);
+ *qos = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.cvlan_pcp);
+ }
+
+ esw_debug(dev, "Query Vport[%d] cvlan: VLAN %d qos=%d\n",
+ vport, *vlan, *qos);
+out:
+ return err;
+}
+
+static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
+ void *in, int inlen)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)];
+
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
+
+ MLX5_SET(modify_esw_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
+
+ return mlx5_cmd_exec_check_status(dev, in, inlen,
+ out, sizeof(out));
+}
+
+static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
+ u16 vlan, u8 qos, bool set)
+{
+ u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)];
+
+ memset(in, 0, sizeof(in));
+
+ if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
+ !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
+ return -ENOTSUPP;
+
+ esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%d\n",
+ vport, vlan, qos, set);
+
+ if (set) {
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.vport_cvlan_strip, 1);
+ /* insert only if no vlan in packet */
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.vport_cvlan_insert, 1);
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.cvlan_pcp, qos);
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.cvlan_id, vlan);
+ }
+
+ MLX5_SET(modify_esw_vport_context_in, in,
+ field_select.vport_cvlan_strip, 1);
+ MLX5_SET(modify_esw_vport_context_in, in,
+ field_select.vport_cvlan_insert, 1);
+
+ return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in));
+}
+
+/* HW L2 Table (MPFS) management */
+static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index,
+ u8 *mac, u8 vlan_valid, u16 vlan)
+{
+ u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)];
+ u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)];
+ u8 *in_mac_addr;
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(set_l2_table_entry_in, in, opcode,
+ MLX5_CMD_OP_SET_L2_TABLE_ENTRY);
+ MLX5_SET(set_l2_table_entry_in, in, table_index, index);
+ MLX5_SET(set_l2_table_entry_in, in, vlan_valid, vlan_valid);
+ MLX5_SET(set_l2_table_entry_in, in, vlan, vlan);
+
+ in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address);
+ ether_addr_copy(&in_mac_addr[2], mac);
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in),
+ out, sizeof(out));
+}
+
+static int del_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index)
+{
+ u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)];
+ u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(delete_l2_table_entry_in, in, opcode,
+ MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY);
+ MLX5_SET(delete_l2_table_entry_in, in, table_index, index);
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in),
+ out, sizeof(out));
+}
+
+static int alloc_l2_table_index(struct mlx5_l2_table *l2_table, u32 *ix)
+{
+ int err = 0;
+
+ *ix = find_first_zero_bit(l2_table->bitmap, l2_table->size);
+ if (*ix >= l2_table->size)
+ err = -ENOSPC;
+ else
+ __set_bit(*ix, l2_table->bitmap);
+
+ return err;
+}
+
+static void free_l2_table_index(struct mlx5_l2_table *l2_table, u32 ix)
+{
+ __clear_bit(ix, l2_table->bitmap);
+}
+
+static int set_l2_table_entry(struct mlx5_core_dev *dev, u8 *mac,
+ u8 vlan_valid, u16 vlan,
+ u32 *index)
+{
+ struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
+ int err;
+
+ err = alloc_l2_table_index(l2_table, index);
+ if (err)
+ return err;
+
+ err = set_l2_table_entry_cmd(dev, *index, mac, vlan_valid, vlan);
+ if (err)
+ free_l2_table_index(l2_table, *index);
+
+ return err;
+}
+
+static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index)
+{
+ struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
+
+ del_l2_table_entry_cmd(dev, index);
+ free_l2_table_index(l2_table, index);
+}
+
+/* E-Switch FDB */
+static struct mlx5_flow_rule *
+esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport)
+{
+ int match_header = MLX5_MATCH_OUTER_HEADERS;
+ struct mlx5_flow_destination dest;
+ struct mlx5_flow_rule *flow_rule = NULL;
+ u32 *match_v;
+ u32 *match_c;
+ u8 *dmac_v;
+ u8 *dmac_c;
+
+ match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ if (!match_v || !match_c) {
+ pr_warn("FDB: Failed to alloc match parameters\n");
+ goto out;
+ }
+ dmac_v = MLX5_ADDR_OF(fte_match_param, match_v,
+ outer_headers.dmac_47_16);
+ dmac_c = MLX5_ADDR_OF(fte_match_param, match_c,
+ outer_headers.dmac_47_16);
+
+ ether_addr_copy(dmac_v, mac);
+ /* Match criteria mask */
+ memset(dmac_c, 0xff, 6);
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
+ dest.vport_num = vport;
+
+ esw_debug(esw->dev,
+ "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
+ dmac_v, dmac_c, vport);
+ flow_rule =
+ mlx5_add_flow_rule(esw->fdb_table.fdb,
+ match_header,
+ match_c,
+ match_v,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ 0, &dest);
+ if (IS_ERR_OR_NULL(flow_rule)) {
+ pr_warn(
+ "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n",
+ dmac_v, dmac_c, vport, PTR_ERR(flow_rule));
+ flow_rule = NULL;
+ }
+out:
+ kfree(match_v);
+ kfree(match_c);
+ return flow_rule;
+}
+
+static int esw_create_fdb_table(struct mlx5_eswitch *esw, int nvports)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ struct mlx5_core_dev *dev = esw->dev;
+ struct mlx5_flow_namespace *root_ns;
+ struct mlx5_flow_table *fdb;
+ struct mlx5_flow_group *g;
+ void *match_criteria;
+ int table_size;
+ u32 *flow_group_in;
+ u8 *dmac;
+ int err = 0;
+
+ esw_debug(dev, "Create FDB log_max_size(%d)\n",
+ MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
+
+ root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
+ if (!root_ns) {
+ esw_warn(dev, "Failed to get FDB flow namespace\n");
+ return -ENOMEM;
+ }
+
+ flow_group_in = mlx5_vzalloc(inlen);
+ if (!flow_group_in)
+ return -ENOMEM;
+ memset(flow_group_in, 0, inlen);
+
+ table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
+ fdb = mlx5_create_flow_table(root_ns, 0, table_size);
+ if (IS_ERR_OR_NULL(fdb)) {
+ err = PTR_ERR(fdb);
+ esw_warn(dev, "Failed to create FDB Table err %d\n", err);
+ goto out;
+ }
+
+ MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
+ MLX5_MATCH_OUTER_HEADERS);
+ match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
+ dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16);
+ MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
+ MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1);
+ eth_broadcast_addr(dmac);
+
+ g = mlx5_create_flow_group(fdb, flow_group_in);
+ if (IS_ERR_OR_NULL(g)) {
+ err = PTR_ERR(g);
+ esw_warn(dev, "Failed to create flow group err(%d)\n", err);
+ goto out;
+ }
+
+ esw->fdb_table.addr_grp = g;
+ esw->fdb_table.fdb = fdb;
+out:
+ kfree(flow_group_in);
+ if (err && !IS_ERR_OR_NULL(fdb))
+ mlx5_destroy_flow_table(fdb);
+ return err;
+}
+
+static void esw_destroy_fdb_table(struct mlx5_eswitch *esw)
+{
+ if (!esw->fdb_table.fdb)
+ return;
+
+ esw_debug(esw->dev, "Destroy FDB Table\n");
+ mlx5_destroy_flow_group(esw->fdb_table.addr_grp);
+ mlx5_destroy_flow_table(esw->fdb_table.fdb);
+ esw->fdb_table.fdb = NULL;
+ esw->fdb_table.addr_grp = NULL;
+}
+
+/* E-Switch vport UC/MC lists management */
+typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
+ struct vport_addr *vaddr);
+
+static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->l2_table.l2_hash;
+ struct esw_uc_addr *esw_uc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+ int err;
+
+ esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
+ if (esw_uc) {
+ esw_warn(esw->dev,
+ "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n",
+ mac, vport, esw_uc->vport);
+ return -EEXIST;
+ }
+
+ esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL);
+ if (!esw_uc)
+ return -ENOMEM;
+ esw_uc->vport = vport;
+
+ err = set_l2_table_entry(esw->dev, mac, 0, 0, &esw_uc->table_index);
+ if (err)
+ goto abort;
+
+ if (esw->fdb_table.fdb) /* SRIOV is enabled: Forward UC MAC to vport */
+ vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
+
+ esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n",
+ vport, mac, esw_uc->table_index, vaddr->flow_rule);
+ return err;
+abort:
+ l2addr_hash_del(esw_uc);
+ return err;
+}
+
+static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->l2_table.l2_hash;
+ struct esw_uc_addr *esw_uc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+
+ esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
+ if (!esw_uc || esw_uc->vport != vport) {
+ esw_debug(esw->dev,
+ "MAC(%pM) doesn't belong to vport (%d)\n",
+ mac, vport);
+ return -EINVAL;
+ }
+ esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n",
+ vport, mac, esw_uc->table_index, vaddr->flow_rule);
+
+ del_l2_table_entry(esw->dev, esw_uc->table_index);
+
+ if (vaddr->flow_rule)
+ mlx5_del_flow_rule(vaddr->flow_rule);
+ vaddr->flow_rule = NULL;
+
+ l2addr_hash_del(esw_uc);
+ return 0;
+}
+
+static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->mc_table;
+ struct esw_mc_addr *esw_mc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+
+ if (!esw->fdb_table.fdb)
+ return 0;
+
+ esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
+ if (esw_mc)
+ goto add;
+
+ esw_mc = l2addr_hash_add(hash, mac, struct esw_mc_addr, GFP_KERNEL);
+ if (!esw_mc)
+ return -ENOMEM;
+
+ esw_mc->uplink_rule = /* Forward MC MAC to Uplink */
+ esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT);
+add:
+ esw_mc->refcnt++;
+ /* Forward MC MAC to vport */
+ vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
+ esw_debug(esw->dev,
+ "\tADDED MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
+ vport, mac, vaddr->flow_rule,
+ esw_mc->refcnt, esw_mc->uplink_rule);
+ return 0;
+}
+
+static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->mc_table;
+ struct esw_mc_addr *esw_mc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+
+ if (!esw->fdb_table.fdb)
+ return 0;
+
+ esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
+ if (!esw_mc) {
+ esw_warn(esw->dev,
+ "Failed to find eswitch MC addr for MAC(%pM) vport(%d)",
+ mac, vport);
+ return -EINVAL;
+ }
+ esw_debug(esw->dev,
+ "\tDELETE MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
+ vport, mac, vaddr->flow_rule, esw_mc->refcnt,
+ esw_mc->uplink_rule);
+
+ if (vaddr->flow_rule)
+ mlx5_del_flow_rule(vaddr->flow_rule);
+ vaddr->flow_rule = NULL;
+
+ if (--esw_mc->refcnt)
+ return 0;
+
+ if (esw_mc->uplink_rule)
+ mlx5_del_flow_rule(esw_mc->uplink_rule);
+
+ l2addr_hash_del(esw_mc);
+ return 0;
+}
+
+/* Apply vport UC/MC list to HW l2 table and FDB table */
+static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
+ u32 vport_num, int list_type)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
+ vport_addr_action vport_addr_add;
+ vport_addr_action vport_addr_del;
+ struct vport_addr *addr;
+ struct l2addr_node *node;
+ struct hlist_head *hash;
+ struct hlist_node *tmp;
+ int hi;
+
+ vport_addr_add = is_uc ? esw_add_uc_addr :
+ esw_add_mc_addr;
+ vport_addr_del = is_uc ? esw_del_uc_addr :
+ esw_del_mc_addr;
+
+ hash = is_uc ? vport->uc_list : vport->mc_list;
+ for_each_l2hash_node(node, tmp, hash, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ switch (addr->action) {
+ case MLX5_ACTION_ADD:
+ vport_addr_add(esw, addr);
+ addr->action = MLX5_ACTION_NONE;
+ break;
+ case MLX5_ACTION_DEL:
+ vport_addr_del(esw, addr);
+ l2addr_hash_del(addr);
+ break;
+ }
+ }
+}
+
+/* Sync vport UC/MC list from vport context */
+static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
+ u32 vport_num, int list_type)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
+ u8 (*mac_list)[ETH_ALEN];
+ struct l2addr_node *node;
+ struct vport_addr *addr;
+ struct hlist_head *hash;
+ struct hlist_node *tmp;
+ int size;
+ int err;
+ int hi;
+ int i;
+
+ size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
+ MLX5_MAX_MC_PER_VPORT(esw->dev);
+
+ mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL);
+ if (!mac_list)
+ return;
+
+ hash = is_uc ? vport->uc_list : vport->mc_list;
+
+ for_each_l2hash_node(node, tmp, hash, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ addr->action = MLX5_ACTION_DEL;
+ }
+
+ err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
+ mac_list, &size);
+ if (err)
+ return;
+ esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
+ vport_num, is_uc ? "UC" : "MC", size);
+
+ for (i = 0; i < size; i++) {
+ if (is_uc && !is_valid_ether_addr(mac_list[i]))
+ continue;
+
+ if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
+ continue;
+
+ addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
+ if (addr) {
+ addr->action = MLX5_ACTION_NONE;
+ continue;
+ }
+
+ addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
+ GFP_KERNEL);
+ if (!addr) {
+ esw_warn(esw->dev,
+ "Failed to add MAC(%pM) to vport[%d] DB\n",
+ mac_list[i], vport_num);
+ continue;
+ }
+ addr->vport = vport_num;
+ addr->action = MLX5_ACTION_ADD;
+ }
+ kfree(mac_list);
+}
+
+static void esw_vport_change_handler(struct work_struct *work)
+{
+ struct mlx5_vport *vport =
+ container_of(work, struct mlx5_vport, vport_change_handler);
+ struct mlx5_core_dev *dev = vport->dev;
+ struct mlx5_eswitch *esw = dev->priv.eswitch;
+ u8 mac[ETH_ALEN];
+
+ mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
+ esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
+ vport->vport, mac);
+
+ if (vport->enabled_events & UC_ADDR_CHANGE) {
+ esw_update_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_UC);
+ esw_apply_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_UC);
+ }
+
+ if (vport->enabled_events & MC_ADDR_CHANGE) {
+ esw_update_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_MC);
+ esw_apply_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_MC);
+ }
+
+ esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
+ if (vport->enabled)
+ arm_vport_context_events_cmd(dev, vport->vport,
+ vport->enabled_events);
+}
+
+static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
+ int enable_events)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ unsigned long flags;
+
+ WARN_ON(vport->enabled);
+
+ esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num);
+ mlx5_modify_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport_num,
+ MLX5_ESW_VPORT_ADMIN_STATE_AUTO);
+
+ /* Sync with current vport context */
+ vport->enabled_events = enable_events;
+ esw_vport_change_handler(&vport->vport_change_handler);
+
+ spin_lock_irqsave(&vport->lock, flags);
+ vport->enabled = true;
+ spin_unlock_irqrestore(&vport->lock, flags);
+
+ arm_vport_context_events_cmd(esw->dev, vport_num, enable_events);
+
+ esw->enabled_vports++;
+ esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num);
+}
+
+static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ struct l2addr_node *node;
+ struct vport_addr *addr;
+ struct hlist_node *tmp;
+ int hi;
+
+ for_each_l2hash_node(node, tmp, vport->uc_list, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ addr->action = MLX5_ACTION_DEL;
+ }
+ esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_UC);
+
+ for_each_l2hash_node(node, tmp, vport->mc_list, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ addr->action = MLX5_ACTION_DEL;
+ }
+ esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_MC);
+}
+
+static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ unsigned long flags;
+
+ if (!vport->enabled)
+ return;
+
+ esw_debug(esw->dev, "Disabling vport(%d)\n", vport_num);
+ /* Mark this vport as disabled to discard new events */
+ spin_lock_irqsave(&vport->lock, flags);
+ vport->enabled = false;
+ vport->enabled_events = 0;
+ spin_unlock_irqrestore(&vport->lock, flags);
+
+ mlx5_modify_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport_num,
+ MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
+ /* Wait for current already scheduled events to complete */
+ flush_workqueue(esw->work_queue);
+ /* Disable events from this vport */
+ arm_vport_context_events_cmd(esw->dev, vport->vport, 0);
+ /* We don't assume VFs will cleanup after themselves */
+ esw_cleanup_vport(esw, vport_num);
+ esw->enabled_vports--;
+}
+
+/* Public E-Switch API */
+int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs)
+{
+ int err;
+ int i;
+
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
+ MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return 0;
+
+ if (!MLX5_CAP_GEN(esw->dev, eswitch_flow_table) ||
+ !MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ft_support)) {
+ esw_warn(esw->dev, "E-Switch FDB is not supported, aborting ...\n");
+ return -ENOTSUPP;
+ }
+
+ esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d)\n", nvfs);
+
+ esw_disable_vport(esw, 0);
+
+ err = esw_create_fdb_table(esw, nvfs + 1);
+ if (err)
+ goto abort;
+
+ for (i = 0; i <= nvfs; i++)
+ esw_enable_vport(esw, i, SRIOV_VPORT_EVENTS);
+
+ esw_info(esw->dev, "SRIOV enabled: active vports(%d)\n",
+ esw->enabled_vports);
+ return 0;
+
+abort:
+ esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
+ return err;
+}
+
+void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
+{
+ int i;
+
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
+ MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return;
+
+ esw_info(esw->dev, "disable SRIOV: active vports(%d)\n",
+ esw->enabled_vports);
+
+ for (i = 0; i < esw->total_vports; i++)
+ esw_disable_vport(esw, i);
+
+ esw_destroy_fdb_table(esw);
+
+ /* VPORT 0 (PF) must be enabled back with non-sriov configuration */
+ esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
+}
+
+int mlx5_eswitch_init(struct mlx5_core_dev *dev)
+{
+ int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table);
+ int total_vports = 1 + pci_sriov_get_totalvfs(dev->pdev);
+ struct mlx5_eswitch *esw;
+ int vport_num;
+ int err;
+
+ if (!MLX5_CAP_GEN(dev, vport_group_manager) ||
+ MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return 0;
+
+ esw_info(dev,
+ "Total vports %d, l2 table size(%d), per vport: max uc(%d) max mc(%d)\n",
+ total_vports, l2_table_size,
+ MLX5_MAX_UC_PER_VPORT(dev),
+ MLX5_MAX_MC_PER_VPORT(dev));
+
+ esw = kzalloc(sizeof(*esw), GFP_KERNEL);
+ if (!esw)
+ return -ENOMEM;
+
+ esw->dev = dev;
+
+ esw->l2_table.bitmap = kcalloc(BITS_TO_LONGS(l2_table_size),
+ sizeof(uintptr_t), GFP_KERNEL);
+ if (!esw->l2_table.bitmap) {
+ err = -ENOMEM;
+ goto abort;
+ }
+ esw->l2_table.size = l2_table_size;
+
+ esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq");
+ if (!esw->work_queue) {
+ err = -ENOMEM;
+ goto abort;
+ }
+
+ esw->vports = kcalloc(total_vports, sizeof(struct mlx5_vport),
+ GFP_KERNEL);
+ if (!esw->vports) {
+ err = -ENOMEM;
+ goto abort;
+ }
+
+ for (vport_num = 0; vport_num < total_vports; vport_num++) {
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+
+ vport->vport = vport_num;
+ vport->dev = dev;
+ INIT_WORK(&vport->vport_change_handler,
+ esw_vport_change_handler);
+ spin_lock_init(&vport->lock);
+ }
+
+ esw->total_vports = total_vports;
+ esw->enabled_vports = 0;
+
+ dev->priv.eswitch = esw;
+ esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
+ /* VF Vports will be enabled when SRIOV is enabled */
+ return 0;
+abort:
+ if (esw->work_queue)
+ destroy_workqueue(esw->work_queue);
+ kfree(esw->l2_table.bitmap);
+ kfree(esw->vports);
+ kfree(esw);
+ return err;
+}
+
+void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
+{
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
+ MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return;
+
+ esw_info(esw->dev, "cleanup\n");
+ esw_disable_vport(esw, 0);
+
+ esw->dev->priv.eswitch = NULL;
+ destroy_workqueue(esw->work_queue);
+ kfree(esw->l2_table.bitmap);
+ kfree(esw->vports);
+ kfree(esw);
+}
+
+void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe)
+{
+ struct mlx5_eqe_vport_change *vc_eqe = &eqe->data.vport_change;
+ u16 vport_num = be16_to_cpu(vc_eqe->vport_num);
+ struct mlx5_vport *vport;
+
+ if (!esw) {
+ pr_warn("MLX5 E-Switch: vport %d got an event while eswitch is not initialized\n",
+ vport_num);
+ return;
+ }
+
+ vport = &esw->vports[vport_num];
+ spin_lock(&vport->lock);
+ if (vport->enabled)
+ queue_work(esw->work_queue, &vport->vport_change_handler);
+ spin_unlock(&vport->lock);
+}
+
+/* Vport Administration */
+#define ESW_ALLOWED(esw) \
+ (esw && MLX5_CAP_GEN(esw->dev, vport_group_manager) && mlx5_core_is_pf(esw->dev))
+#define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports)
+
+int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
+ int vport, u8 mac[ETH_ALEN])
+{
+ int err = 0;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac);
+ if (err) {
+ mlx5_core_warn(esw->dev,
+ "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n",
+ vport, err);
+ return err;
+ }
+
+ return err;
+}
+
+int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
+ int vport, int link_state)
+{
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ return mlx5_modify_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport, link_state);
+}
+
+int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
+ int vport, struct ifla_vf_info *ivi)
+{
+ u16 vlan;
+ u8 qos;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ memset(ivi, 0, sizeof(*ivi));
+ ivi->vf = vport - 1;
+
+ mlx5_query_nic_vport_mac_address(esw->dev, vport, ivi->mac);
+ ivi->linkstate = mlx5_query_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport);
+ query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos);
+ ivi->vlan = vlan;
+ ivi->qos = qos;
+ ivi->spoofchk = 0;
+
+ return 0;
+}
+
+int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
+ int vport, u16 vlan, u8 qos)
+{
+ int set = 0;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport) || (vlan > 4095) || (qos > 7))
+ return -EINVAL;
+
+ if (vlan || qos)
+ set = 1;
+
+ return modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set);
+}
+
+int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
+ int vport,
+ struct ifla_vf_stats *vf_stats)
+{
+ int outlen = MLX5_ST_SZ_BYTES(query_vport_counter_out);
+ u32 in[MLX5_ST_SZ_DW(query_vport_counter_in)];
+ int err = 0;
+ u32 *out;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ out = mlx5_vzalloc(outlen);
+ if (!out)
+ return -ENOMEM;
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(query_vport_counter_in, in, opcode,
+ MLX5_CMD_OP_QUERY_VPORT_COUNTER);
+ MLX5_SET(query_vport_counter_in, in, op_mod, 0);
+ MLX5_SET(query_vport_counter_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_vport_counter_in, in, other_vport, 1);
+
+ memset(out, 0, outlen);
+ err = mlx5_cmd_exec(esw->dev, in, sizeof(in), out, outlen);
+ if (err)
+ goto free_out;
+
+ #define MLX5_GET_CTR(p, x) \
+ MLX5_GET64(query_vport_counter_out, p, x)
+
+ memset(vf_stats, 0, sizeof(*vf_stats));
+ vf_stats->rx_packets =
+ MLX5_GET_CTR(out, received_eth_unicast.packets) +
+ MLX5_GET_CTR(out, received_eth_multicast.packets) +
+ MLX5_GET_CTR(out, received_eth_broadcast.packets);
+
+ vf_stats->rx_bytes =
+ MLX5_GET_CTR(out, received_eth_unicast.octets) +
+ MLX5_GET_CTR(out, received_eth_multicast.octets) +
+ MLX5_GET_CTR(out, received_eth_broadcast.octets);
+
+ vf_stats->tx_packets =
+ MLX5_GET_CTR(out, transmitted_eth_unicast.packets) +
+ MLX5_GET_CTR(out, transmitted_eth_multicast.packets) +
+ MLX5_GET_CTR(out, transmitted_eth_broadcast.packets);
+
+ vf_stats->tx_bytes =
+ MLX5_GET_CTR(out, transmitted_eth_unicast.octets) +
+ MLX5_GET_CTR(out, transmitted_eth_multicast.octets) +
+ MLX5_GET_CTR(out, transmitted_eth_broadcast.octets);
+
+ vf_stats->multicast =
+ MLX5_GET_CTR(out, received_eth_multicast.packets);
+
+ vf_stats->broadcast =
+ MLX5_GET_CTR(out, received_eth_broadcast.packets);
+
+free_out:
+ kvfree(out);
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
new file mode 100644
index 000000000000..3416a428f70f
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies, Ltd. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __MLX5_ESWITCH_H__
+#define __MLX5_ESWITCH_H__
+
+#include <linux/if_ether.h>
+#include <linux/if_link.h>
+#include <linux/mlx5/device.h>
+
+#define MLX5_MAX_UC_PER_VPORT(dev) \
+ (1 << MLX5_CAP_GEN(dev, log_max_current_uc_list))
+
+#define MLX5_MAX_MC_PER_VPORT(dev) \
+ (1 << MLX5_CAP_GEN(dev, log_max_current_mc_list))
+
+#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE))
+#define MLX5_L2_ADDR_HASH(addr) (addr[5])
+
+/* L2 -mac address based- hash helpers */
+struct l2addr_node {
+ struct hlist_node hlist;
+ u8 addr[ETH_ALEN];
+};
+
+#define for_each_l2hash_node(hn, tmp, hash, i) \
+ for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \
+ hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist)
+
+#define l2addr_hash_find(hash, mac, type) ({ \
+ int ix = MLX5_L2_ADDR_HASH(mac); \
+ bool found = false; \
+ type *ptr = NULL; \
+ \
+ hlist_for_each_entry(ptr, &hash[ix], node.hlist) \
+ if (ether_addr_equal(ptr->node.addr, mac)) {\
+ found = true; \
+ break; \
+ } \
+ if (!found) \
+ ptr = NULL; \
+ ptr; \
+})
+
+#define l2addr_hash_add(hash, mac, type, gfp) ({ \
+ int ix = MLX5_L2_ADDR_HASH(mac); \
+ type *ptr = NULL; \
+ \
+ ptr = kzalloc(sizeof(type), gfp); \
+ if (ptr) { \
+ ether_addr_copy(ptr->node.addr, mac); \
+ hlist_add_head(&ptr->node.hlist, &hash[ix]);\
+ } \
+ ptr; \
+})
+
+#define l2addr_hash_del(ptr) ({ \
+ hlist_del(&ptr->node.hlist); \
+ kfree(ptr); \
+})
+
+struct mlx5_vport {
+ struct mlx5_core_dev *dev;
+ int vport;
+ struct hlist_head uc_list[MLX5_L2_ADDR_HASH_SIZE];
+ struct hlist_head mc_list[MLX5_L2_ADDR_HASH_SIZE];
+ struct work_struct vport_change_handler;
+
+ /* This spinlock protects access to vport data, between
+ * "esw_vport_disable" and ongoing interrupt "mlx5_eswitch_vport_event"
+ * once vport marked as disabled new interrupts are discarded.
+ */
+ spinlock_t lock; /* vport events sync */
+ bool enabled;
+ u16 enabled_events;
+};
+
+struct mlx5_l2_table {
+ struct hlist_head l2_hash[MLX5_L2_ADDR_HASH_SIZE];
+ u32 size;
+ unsigned long *bitmap;
+};
+
+struct mlx5_eswitch_fdb {
+ void *fdb;
+ struct mlx5_flow_group *addr_grp;
+};
+
+struct mlx5_eswitch {
+ struct mlx5_core_dev *dev;
+ struct mlx5_l2_table l2_table;
+ struct mlx5_eswitch_fdb fdb_table;
+ struct hlist_head mc_table[MLX5_L2_ADDR_HASH_SIZE];
+ struct workqueue_struct *work_queue;
+ struct mlx5_vport *vports;
+ int total_vports;
+ int enabled_vports;
+};
+
+/* E-Switch API */
+int mlx5_eswitch_init(struct mlx5_core_dev *dev);
+void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw);
+void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe);
+int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs);
+void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw);
+int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
+ int vport, u8 mac[ETH_ALEN]);
+int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
+ int vport, int link_state);
+int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
+ int vport, u16 vlan, u8 qos);
+int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
+ int vport, struct ifla_vf_info *ivi);
+int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
+ int vport,
+ struct ifla_vf_stats *vf_stats);
+
+#endif /* __MLX5_ESWITCH_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c
deleted file mode 100644
index ca90b9bc3b95..000000000000
--- a/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses. You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <linux/export.h>
-#include <linux/mlx5/driver.h>
-#include <linux/mlx5/flow_table.h>
-#include "mlx5_core.h"
-
-struct mlx5_ftg {
- struct mlx5_flow_table_group g;
- u32 id;
- u32 start_ix;
-};
-
-struct mlx5_flow_table {
- struct mlx5_core_dev *dev;
- u8 level;
- u8 type;
- u32 id;
- struct mutex mutex; /* sync bitmap alloc */
- u16 num_groups;
- struct mlx5_ftg *group;
- unsigned long *bitmap;
- u32 size;
-};
-
-static int mlx5_set_flow_entry_cmd(struct mlx5_flow_table *ft, u32 group_ix,
- u32 flow_index, void *flow_context)
-{
- u32 out[MLX5_ST_SZ_DW(set_fte_out)];
- u32 *in;
- void *in_flow_context;
- int fcdls =
- MLX5_GET(flow_context, flow_context, destination_list_size) *
- MLX5_ST_SZ_BYTES(dest_format_struct);
- int inlen = MLX5_ST_SZ_BYTES(set_fte_in) + fcdls;
- int err;
-
- in = mlx5_vzalloc(inlen);
- if (!in) {
- mlx5_core_warn(ft->dev, "failed to allocate inbox\n");
- return -ENOMEM;
- }
-
- MLX5_SET(set_fte_in, in, table_type, ft->type);
- MLX5_SET(set_fte_in, in, table_id, ft->id);
- MLX5_SET(set_fte_in, in, flow_index, flow_index);
- MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY);
-
- in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
- memcpy(in_flow_context, flow_context,
- MLX5_ST_SZ_BYTES(flow_context) + fcdls);
-
- MLX5_SET(flow_context, in_flow_context, group_id,
- ft->group[group_ix].id);
-
- memset(out, 0, sizeof(out));
- err = mlx5_cmd_exec_check_status(ft->dev, in, inlen, out,
- sizeof(out));
- kvfree(in);
-
- return err;
-}
-
-static void mlx5_del_flow_entry_cmd(struct mlx5_flow_table *ft, u32 flow_index)
-{
- u32 in[MLX5_ST_SZ_DW(delete_fte_in)];
- u32 out[MLX5_ST_SZ_DW(delete_fte_out)];
-
- memset(in, 0, sizeof(in));
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_DFTEI(p, x, v) MLX5_SET(delete_fte_in, p, x, v)
- MLX5_SET_DFTEI(in, table_type, ft->type);
- MLX5_SET_DFTEI(in, table_id, ft->id);
- MLX5_SET_DFTEI(in, flow_index, flow_index);
- MLX5_SET_DFTEI(in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
-
- mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out));
-}
-
-static void mlx5_destroy_flow_group_cmd(struct mlx5_flow_table *ft, int i)
-{
- u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)];
- u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)];
-
- memset(in, 0, sizeof(in));
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_DFGI(p, x, v) MLX5_SET(destroy_flow_group_in, p, x, v)
- MLX5_SET_DFGI(in, table_type, ft->type);
- MLX5_SET_DFGI(in, table_id, ft->id);
- MLX5_SET_DFGI(in, opcode, MLX5_CMD_OP_DESTROY_FLOW_GROUP);
- MLX5_SET_DFGI(in, group_id, ft->group[i].id);
- mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int mlx5_create_flow_group_cmd(struct mlx5_flow_table *ft, int i)
-{
- u32 out[MLX5_ST_SZ_DW(create_flow_group_out)];
- u32 *in;
- void *in_match_criteria;
- int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
- struct mlx5_flow_table_group *g = &ft->group[i].g;
- u32 start_ix = ft->group[i].start_ix;
- u32 end_ix = start_ix + (1 << g->log_sz) - 1;
- int err;
-
- in = mlx5_vzalloc(inlen);
- if (!in) {
- mlx5_core_warn(ft->dev, "failed to allocate inbox\n");
- return -ENOMEM;
- }
- in_match_criteria = MLX5_ADDR_OF(create_flow_group_in, in,
- match_criteria);
-
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_CFGI(p, x, v) MLX5_SET(create_flow_group_in, p, x, v)
- MLX5_SET_CFGI(in, table_type, ft->type);
- MLX5_SET_CFGI(in, table_id, ft->id);
- MLX5_SET_CFGI(in, opcode, MLX5_CMD_OP_CREATE_FLOW_GROUP);
- MLX5_SET_CFGI(in, start_flow_index, start_ix);
- MLX5_SET_CFGI(in, end_flow_index, end_ix);
- MLX5_SET_CFGI(in, match_criteria_enable, g->match_criteria_enable);
-
- memcpy(in_match_criteria, g->match_criteria,
- MLX5_ST_SZ_BYTES(fte_match_param));
-
- err = mlx5_cmd_exec_check_status(ft->dev, in, inlen, out,
- sizeof(out));
- if (!err)
- ft->group[i].id = MLX5_GET(create_flow_group_out, out,
- group_id);
-
- kvfree(in);
-
- return err;
-}
-
-static void mlx5_destroy_flow_table_groups(struct mlx5_flow_table *ft)
-{
- int i;
-
- for (i = 0; i < ft->num_groups; i++)
- mlx5_destroy_flow_group_cmd(ft, i);
-}
-
-static int mlx5_create_flow_table_groups(struct mlx5_flow_table *ft)
-{
- int err;
- int i;
-
- for (i = 0; i < ft->num_groups; i++) {
- err = mlx5_create_flow_group_cmd(ft, i);
- if (err)
- goto err_destroy_flow_table_groups;
- }
-
- return 0;
-
-err_destroy_flow_table_groups:
- for (i--; i >= 0; i--)
- mlx5_destroy_flow_group_cmd(ft, i);
-
- return err;
-}
-
-static int mlx5_create_flow_table_cmd(struct mlx5_flow_table *ft)
-{
- u32 in[MLX5_ST_SZ_DW(create_flow_table_in)];
- u32 out[MLX5_ST_SZ_DW(create_flow_table_out)];
- int err;
-
- memset(in, 0, sizeof(in));
-
- MLX5_SET(create_flow_table_in, in, table_type, ft->type);
- MLX5_SET(create_flow_table_in, in, level, ft->level);
- MLX5_SET(create_flow_table_in, in, log_size, order_base_2(ft->size));
-
- MLX5_SET(create_flow_table_in, in, opcode,
- MLX5_CMD_OP_CREATE_FLOW_TABLE);
-
- memset(out, 0, sizeof(out));
- err = mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out,
- sizeof(out));
- if (err)
- return err;
-
- ft->id = MLX5_GET(create_flow_table_out, out, table_id);
-
- return 0;
-}
-
-static void mlx5_destroy_flow_table_cmd(struct mlx5_flow_table *ft)
-{
- u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)];
- u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)];
-
- memset(in, 0, sizeof(in));
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_DFTI(p, x, v) MLX5_SET(destroy_flow_table_in, p, x, v)
- MLX5_SET_DFTI(in, table_type, ft->type);
- MLX5_SET_DFTI(in, table_id, ft->id);
- MLX5_SET_DFTI(in, opcode, MLX5_CMD_OP_DESTROY_FLOW_TABLE);
-
- mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int mlx5_find_group(struct mlx5_flow_table *ft, u8 match_criteria_enable,
- u32 *match_criteria, int *group_ix)
-{
- void *mc_outer = MLX5_ADDR_OF(fte_match_param, match_criteria,
- outer_headers);
- void *mc_misc = MLX5_ADDR_OF(fte_match_param, match_criteria,
- misc_parameters);
- void *mc_inner = MLX5_ADDR_OF(fte_match_param, match_criteria,
- inner_headers);
- int mc_outer_sz = MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4);
- int mc_misc_sz = MLX5_ST_SZ_BYTES(fte_match_set_misc);
- int mc_inner_sz = MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4);
- int i;
-
- for (i = 0; i < ft->num_groups; i++) {
- struct mlx5_flow_table_group *g = &ft->group[i].g;
- void *gmc_outer = MLX5_ADDR_OF(fte_match_param,
- g->match_criteria,
- outer_headers);
- void *gmc_misc = MLX5_ADDR_OF(fte_match_param,
- g->match_criteria,
- misc_parameters);
- void *gmc_inner = MLX5_ADDR_OF(fte_match_param,
- g->match_criteria,
- inner_headers);
-
- if (g->match_criteria_enable != match_criteria_enable)
- continue;
-
- if (match_criteria_enable & MLX5_MATCH_OUTER_HEADERS)
- if (memcmp(mc_outer, gmc_outer, mc_outer_sz))
- continue;
-
- if (match_criteria_enable & MLX5_MATCH_MISC_PARAMETERS)
- if (memcmp(mc_misc, gmc_misc, mc_misc_sz))
- continue;
-
- if (match_criteria_enable & MLX5_MATCH_INNER_HEADERS)
- if (memcmp(mc_inner, gmc_inner, mc_inner_sz))
- continue;
-
- *group_ix = i;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int alloc_flow_index(struct mlx5_flow_table *ft, int group_ix, u32 *ix)
-{
- struct mlx5_ftg *g = &ft->group[group_ix];
- int err = 0;
-
- mutex_lock(&ft->mutex);
-
- *ix = find_next_zero_bit(ft->bitmap, ft->size, g->start_ix);
- if (*ix >= (g->start_ix + (1 << g->g.log_sz)))
- err = -ENOSPC;
- else
- __set_bit(*ix, ft->bitmap);
-
- mutex_unlock(&ft->mutex);
-
- return err;
-}
-
-static void mlx5_free_flow_index(struct mlx5_flow_table *ft, u32 ix)
-{
- __clear_bit(ix, ft->bitmap);
-}
-
-int mlx5_add_flow_table_entry(void *flow_table, u8 match_criteria_enable,
- void *match_criteria, void *flow_context,
- u32 *flow_index)
-{
- struct mlx5_flow_table *ft = flow_table;
- int group_ix;
- int err;
-
- err = mlx5_find_group(ft, match_criteria_enable, match_criteria,
- &group_ix);
- if (err) {
- mlx5_core_warn(ft->dev, "mlx5_find_group failed\n");
- return err;
- }
-
- err = alloc_flow_index(ft, group_ix, flow_index);
- if (err) {
- mlx5_core_warn(ft->dev, "alloc_flow_index failed\n");
- return err;
- }
-
- return mlx5_set_flow_entry_cmd(ft, group_ix, *flow_index, flow_context);
-}
-EXPORT_SYMBOL(mlx5_add_flow_table_entry);
-
-void mlx5_del_flow_table_entry(void *flow_table, u32 flow_index)
-{
- struct mlx5_flow_table *ft = flow_table;
-
- mlx5_del_flow_entry_cmd(ft, flow_index);
- mlx5_free_flow_index(ft, flow_index);
-}
-EXPORT_SYMBOL(mlx5_del_flow_table_entry);
-
-void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 table_type,
- u16 num_groups,
- struct mlx5_flow_table_group *group)
-{
- struct mlx5_flow_table *ft;
- u32 start_ix = 0;
- u32 ft_size = 0;
- void *gr;
- void *bm;
- int err;
- int i;
-
- for (i = 0; i < num_groups; i++)
- ft_size += (1 << group[i].log_sz);
-
- ft = kzalloc(sizeof(*ft), GFP_KERNEL);
- gr = kcalloc(num_groups, sizeof(struct mlx5_ftg), GFP_KERNEL);
- bm = kcalloc(BITS_TO_LONGS(ft_size), sizeof(uintptr_t), GFP_KERNEL);
- if (!ft || !gr || !bm)
- goto err_free_ft;
-
- ft->group = gr;
- ft->bitmap = bm;
- ft->num_groups = num_groups;
- ft->level = level;
- ft->type = table_type;
- ft->size = ft_size;
- ft->dev = dev;
- mutex_init(&ft->mutex);
-
- for (i = 0; i < ft->num_groups; i++) {
- memcpy(&ft->group[i].g, &group[i], sizeof(*group));
- ft->group[i].start_ix = start_ix;
- start_ix += 1 << group[i].log_sz;
- }
-
- err = mlx5_create_flow_table_cmd(ft);
- if (err)
- goto err_free_ft;
-
- err = mlx5_create_flow_table_groups(ft);
- if (err)
- goto err_destroy_flow_table_cmd;
-
- return ft;
-
-err_destroy_flow_table_cmd:
- mlx5_destroy_flow_table_cmd(ft);
-
-err_free_ft:
- mlx5_core_warn(dev, "failed to alloc flow table\n");
- kfree(bm);
- kfree(gr);
- kfree(ft);
-
- return NULL;
-}
-EXPORT_SYMBOL(mlx5_create_flow_table);
-
-void mlx5_destroy_flow_table(void *flow_table)
-{
- struct mlx5_flow_table *ft = flow_table;
-
- mlx5_destroy_flow_table_groups(ft);
- mlx5_destroy_flow_table_cmd(ft);
- kfree(ft->bitmap);
- kfree(ft->group);
- kfree(ft);
-}
-EXPORT_SYMBOL(mlx5_destroy_flow_table);
-
-u32 mlx5_get_flow_table_id(void *flow_table)
-{
- struct mlx5_flow_table *ft = flow_table;
-
- return ft->id;
-}
-EXPORT_SYMBOL(mlx5_get_flow_table_id);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
new file mode 100644
index 000000000000..a9894d2e8e26
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/device.h>
+#include <linux/mlx5/mlx5_ifc.h>
+
+#include "fs_core.h"
+#include "fs_cmd.h"
+#include "mlx5_core.h"
+
+int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft)
+{
+ u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)];
+ u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)];
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(set_flow_table_root_in, in, opcode,
+ MLX5_CMD_OP_SET_FLOW_TABLE_ROOT);
+ MLX5_SET(set_flow_table_root_in, in, table_type, ft->type);
+ MLX5_SET(set_flow_table_root_in, in, table_id, ft->id);
+
+ memset(out, 0, sizeof(out));
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
+ enum fs_flow_table_type type, unsigned int level,
+ unsigned int log_size, struct mlx5_flow_table
+ *next_ft, unsigned int *table_id)
+{
+ u32 out[MLX5_ST_SZ_DW(create_flow_table_out)];
+ u32 in[MLX5_ST_SZ_DW(create_flow_table_in)];
+ int err;
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(create_flow_table_in, in, opcode,
+ MLX5_CMD_OP_CREATE_FLOW_TABLE);
+
+ if (next_ft) {
+ MLX5_SET(create_flow_table_in, in, table_miss_mode, 1);
+ MLX5_SET(create_flow_table_in, in, table_miss_id, next_ft->id);
+ }
+ MLX5_SET(create_flow_table_in, in, table_type, type);
+ MLX5_SET(create_flow_table_in, in, level, level);
+ MLX5_SET(create_flow_table_in, in, log_size, log_size);
+
+ memset(out, 0, sizeof(out));
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+
+ if (!err)
+ *table_id = MLX5_GET(create_flow_table_out, out,
+ table_id);
+ return err;
+}
+
+int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft)
+{
+ u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)];
+ u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(destroy_flow_table_in, in, opcode,
+ MLX5_CMD_OP_DESTROY_FLOW_TABLE);
+ MLX5_SET(destroy_flow_table_in, in, table_type, ft->type);
+ MLX5_SET(destroy_flow_table_in, in, table_id, ft->id);
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_table *next_ft)
+{
+ u32 in[MLX5_ST_SZ_DW(modify_flow_table_in)];
+ u32 out[MLX5_ST_SZ_DW(modify_flow_table_out)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(modify_flow_table_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_FLOW_TABLE);
+ MLX5_SET(modify_flow_table_in, in, table_type, ft->type);
+ MLX5_SET(modify_flow_table_in, in, table_id, ft->id);
+ MLX5_SET(modify_flow_table_in, in, modify_field_select,
+ MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID);
+ if (next_ft) {
+ MLX5_SET(modify_flow_table_in, in, table_miss_mode, 1);
+ MLX5_SET(modify_flow_table_in, in, table_miss_id, next_ft->id);
+ } else {
+ MLX5_SET(modify_flow_table_in, in, table_miss_mode, 0);
+ }
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ u32 out[MLX5_ST_SZ_DW(create_flow_group_out)];
+ int err;
+
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(create_flow_group_in, in, opcode,
+ MLX5_CMD_OP_CREATE_FLOW_GROUP);
+ MLX5_SET(create_flow_group_in, in, table_type, ft->type);
+ MLX5_SET(create_flow_group_in, in, table_id, ft->id);
+
+ err = mlx5_cmd_exec_check_status(dev, in,
+ inlen, out,
+ sizeof(out));
+ if (!err)
+ *group_id = MLX5_GET(create_flow_group_out, out,
+ group_id);
+
+ return err;
+}
+
+int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id)
+{
+ u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)];
+ u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(destroy_flow_group_in, in, opcode,
+ MLX5_CMD_OP_DESTROY_FLOW_GROUP);
+ MLX5_SET(destroy_flow_group_in, in, table_type, ft->type);
+ MLX5_SET(destroy_flow_group_in, in, table_id, ft->id);
+ MLX5_SET(destroy_flow_group_in, in, group_id, group_id);
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
+ int opmod, int modify_mask,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte)
+{
+ unsigned int inlen = MLX5_ST_SZ_BYTES(set_fte_in) +
+ fte->dests_size * MLX5_ST_SZ_BYTES(dest_format_struct);
+ u32 out[MLX5_ST_SZ_DW(set_fte_out)];
+ struct mlx5_flow_rule *dst;
+ void *in_flow_context;
+ void *in_match_value;
+ void *in_dests;
+ u32 *in;
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ mlx5_core_warn(dev, "failed to allocate inbox\n");
+ return -ENOMEM;
+ }
+
+ MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY);
+ MLX5_SET(set_fte_in, in, op_mod, opmod);
+ MLX5_SET(set_fte_in, in, modify_enable_mask, modify_mask);
+ MLX5_SET(set_fte_in, in, table_type, ft->type);
+ MLX5_SET(set_fte_in, in, table_id, ft->id);
+ MLX5_SET(set_fte_in, in, flow_index, fte->index);
+
+ in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
+ MLX5_SET(flow_context, in_flow_context, group_id, group_id);
+ MLX5_SET(flow_context, in_flow_context, flow_tag, fte->flow_tag);
+ MLX5_SET(flow_context, in_flow_context, action, fte->action);
+ MLX5_SET(flow_context, in_flow_context, destination_list_size,
+ fte->dests_size);
+ in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context,
+ match_value);
+ memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param));
+
+ in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination);
+ list_for_each_entry(dst, &fte->node.children, node.list) {
+ unsigned int id;
+
+ MLX5_SET(dest_format_struct, in_dests, destination_type,
+ dst->dest_attr.type);
+ if (dst->dest_attr.type ==
+ MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE)
+ id = dst->dest_attr.ft->id;
+ else
+ id = dst->dest_attr.tir_num;
+ MLX5_SET(dest_format_struct, in_dests, destination_id, id);
+ in_dests += MLX5_ST_SZ_BYTES(dest_format_struct);
+ }
+ memset(out, 0, sizeof(out));
+ err = mlx5_cmd_exec_check_status(dev, in, inlen, out,
+ sizeof(out));
+ kvfree(in);
+
+ return err;
+}
+
+int mlx5_cmd_create_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte)
+{
+ return mlx5_cmd_set_fte(dev, 0, 0, ft, group_id, fte);
+}
+
+int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte)
+{
+ int opmod;
+ int modify_mask;
+ int atomic_mod_cap = MLX5_CAP_FLOWTABLE(dev,
+ flow_table_properties_nic_receive.
+ flow_modify_en);
+ if (!atomic_mod_cap)
+ return -ENOTSUPP;
+ opmod = 1;
+ modify_mask = 1 <<
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST;
+
+ return mlx5_cmd_set_fte(dev, opmod, modify_mask, ft, group_id, fte);
+}
+
+int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int index)
+{
+ u32 out[MLX5_ST_SZ_DW(delete_fte_out)];
+ u32 in[MLX5_ST_SZ_DW(delete_fte_in)];
+ int err;
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
+ MLX5_SET(delete_fte_in, in, table_type, ft->type);
+ MLX5_SET(delete_fte_in, in, table_id, ft->id);
+ MLX5_SET(delete_fte_in, in, flow_index, index);
+
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, sizeof(out));
+
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h
new file mode 100644
index 000000000000..9814d4784803
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MLX5_FS_CMD_
+#define _MLX5_FS_CMD_
+
+int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
+ enum fs_flow_table_type type, unsigned int level,
+ unsigned int log_size, struct mlx5_flow_table
+ *next_ft, unsigned int *table_id);
+
+int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft);
+
+int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_table *next_ft);
+
+int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in, unsigned int *group_id);
+
+int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id);
+
+int mlx5_cmd_create_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte);
+
+int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte);
+
+int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int index);
+
+int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft);
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
new file mode 100644
index 000000000000..6f68dba8d7ed
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -0,0 +1,1514 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/mutex.h>
+#include <linux/mlx5/driver.h>
+
+#include "mlx5_core.h"
+#include "fs_core.h"
+#include "fs_cmd.h"
+
+#define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\
+ sizeof(struct init_tree_node))
+
+#define ADD_PRIO(num_prios_val, min_level_val, max_ft_val, caps_val,\
+ ...) {.type = FS_TYPE_PRIO,\
+ .min_ft_level = min_level_val,\
+ .max_ft = max_ft_val,\
+ .num_leaf_prios = num_prios_val,\
+ .caps = caps_val,\
+ .children = (struct init_tree_node[]) {__VA_ARGS__},\
+ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \
+}
+
+#define ADD_MULTIPLE_PRIO(num_prios_val, max_ft_val, ...)\
+ ADD_PRIO(num_prios_val, 0, max_ft_val, {},\
+ __VA_ARGS__)\
+
+#define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\
+ .children = (struct init_tree_node[]) {__VA_ARGS__},\
+ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \
+}
+
+#define INIT_CAPS_ARRAY_SIZE(...) (sizeof((long[]){__VA_ARGS__}) /\
+ sizeof(long))
+
+#define FS_CAP(cap) (__mlx5_bit_off(flow_table_nic_cap, cap))
+
+#define FS_REQUIRED_CAPS(...) {.arr_sz = INIT_CAPS_ARRAY_SIZE(__VA_ARGS__), \
+ .caps = (long[]) {__VA_ARGS__} }
+
+#define LEFTOVERS_MAX_FT 1
+#define LEFTOVERS_NUM_PRIOS 1
+#define BY_PASS_PRIO_MAX_FT 1
+#define BY_PASS_MIN_LEVEL (KENREL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\
+ LEFTOVERS_MAX_FT)
+
+#define KERNEL_MAX_FT 2
+#define KERNEL_NUM_PRIOS 1
+#define KENREL_MIN_LEVEL 2
+
+struct node_caps {
+ size_t arr_sz;
+ long *caps;
+};
+static struct init_tree_node {
+ enum fs_node_type type;
+ struct init_tree_node *children;
+ int ar_size;
+ struct node_caps caps;
+ int min_ft_level;
+ int num_leaf_prios;
+ int prio;
+ int max_ft;
+} root_fs = {
+ .type = FS_TYPE_NAMESPACE,
+ .ar_size = 3,
+ .children = (struct init_tree_node[]) {
+ ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0,
+ FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en),
+ FS_CAP(flow_table_properties_nic_receive.modify_root),
+ FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode),
+ FS_CAP(flow_table_properties_nic_receive.flow_table_modify)),
+ ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, BY_PASS_PRIO_MAX_FT))),
+ ADD_PRIO(0, KENREL_MIN_LEVEL, 0, {},
+ ADD_NS(ADD_MULTIPLE_PRIO(KERNEL_NUM_PRIOS, KERNEL_MAX_FT))),
+ ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0,
+ FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en),
+ FS_CAP(flow_table_properties_nic_receive.modify_root),
+ FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode),
+ FS_CAP(flow_table_properties_nic_receive.flow_table_modify)),
+ ADD_NS(ADD_MULTIPLE_PRIO(LEFTOVERS_NUM_PRIOS, LEFTOVERS_MAX_FT))),
+ }
+};
+
+enum fs_i_mutex_lock_class {
+ FS_MUTEX_GRANDPARENT,
+ FS_MUTEX_PARENT,
+ FS_MUTEX_CHILD
+};
+
+static void del_rule(struct fs_node *node);
+static void del_flow_table(struct fs_node *node);
+static void del_flow_group(struct fs_node *node);
+static void del_fte(struct fs_node *node);
+
+static void tree_init_node(struct fs_node *node,
+ unsigned int refcount,
+ void (*remove_func)(struct fs_node *))
+{
+ atomic_set(&node->refcount, refcount);
+ INIT_LIST_HEAD(&node->list);
+ INIT_LIST_HEAD(&node->children);
+ mutex_init(&node->lock);
+ node->remove_func = remove_func;
+}
+
+static void tree_add_node(struct fs_node *node, struct fs_node *parent)
+{
+ if (parent)
+ atomic_inc(&parent->refcount);
+ node->parent = parent;
+
+ /* Parent is the root */
+ if (!parent)
+ node->root = node;
+ else
+ node->root = parent->root;
+}
+
+static void tree_get_node(struct fs_node *node)
+{
+ atomic_inc(&node->refcount);
+}
+
+static void nested_lock_ref_node(struct fs_node *node,
+ enum fs_i_mutex_lock_class class)
+{
+ if (node) {
+ mutex_lock_nested(&node->lock, class);
+ atomic_inc(&node->refcount);
+ }
+}
+
+static void lock_ref_node(struct fs_node *node)
+{
+ if (node) {
+ mutex_lock(&node->lock);
+ atomic_inc(&node->refcount);
+ }
+}
+
+static void unlock_ref_node(struct fs_node *node)
+{
+ if (node) {
+ atomic_dec(&node->refcount);
+ mutex_unlock(&node->lock);
+ }
+}
+
+static void tree_put_node(struct fs_node *node)
+{
+ struct fs_node *parent_node = node->parent;
+
+ lock_ref_node(parent_node);
+ if (atomic_dec_and_test(&node->refcount)) {
+ if (parent_node)
+ list_del_init(&node->list);
+ if (node->remove_func)
+ node->remove_func(node);
+ kfree(node);
+ node = NULL;
+ }
+ unlock_ref_node(parent_node);
+ if (!node && parent_node)
+ tree_put_node(parent_node);
+}
+
+static int tree_remove_node(struct fs_node *node)
+{
+ if (atomic_read(&node->refcount) > 1)
+ return -EPERM;
+ tree_put_node(node);
+ return 0;
+}
+
+static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns,
+ unsigned int prio)
+{
+ struct fs_prio *iter_prio;
+
+ fs_for_each_prio(iter_prio, ns) {
+ if (iter_prio->prio == prio)
+ return iter_prio;
+ }
+
+ return NULL;
+}
+
+static unsigned int find_next_free_level(struct fs_prio *prio)
+{
+ if (!list_empty(&prio->node.children)) {
+ struct mlx5_flow_table *ft;
+
+ ft = list_last_entry(&prio->node.children,
+ struct mlx5_flow_table,
+ node.list);
+ return ft->level + 1;
+ }
+ return prio->start_level;
+}
+
+static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i++, mask++, val1++, val2++)
+ if ((*((u8 *)val1) & (*(u8 *)mask)) !=
+ ((*(u8 *)val2) & (*(u8 *)mask)))
+ return false;
+
+ return true;
+}
+
+static bool compare_match_value(struct mlx5_flow_group_mask *mask,
+ void *fte_param1, void *fte_param2)
+{
+ if (mask->match_criteria_enable &
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) {
+ void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
+ fte_param1, outer_headers);
+ void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
+ fte_param2, outer_headers);
+ void *fte_mask = MLX5_ADDR_OF(fte_match_param,
+ mask->match_criteria, outer_headers);
+
+ if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
+ MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
+ return false;
+ }
+
+ if (mask->match_criteria_enable &
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) {
+ void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
+ fte_param1, misc_parameters);
+ void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
+ fte_param2, misc_parameters);
+ void *fte_mask = MLX5_ADDR_OF(fte_match_param,
+ mask->match_criteria, misc_parameters);
+
+ if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
+ MLX5_ST_SZ_BYTES(fte_match_set_misc)))
+ return false;
+ }
+
+ if (mask->match_criteria_enable &
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) {
+ void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
+ fte_param1, inner_headers);
+ void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
+ fte_param2, inner_headers);
+ void *fte_mask = MLX5_ADDR_OF(fte_match_param,
+ mask->match_criteria, inner_headers);
+
+ if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
+ MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
+ return false;
+ }
+ return true;
+}
+
+static bool compare_match_criteria(u8 match_criteria_enable1,
+ u8 match_criteria_enable2,
+ void *mask1, void *mask2)
+{
+ return match_criteria_enable1 == match_criteria_enable2 &&
+ !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param));
+}
+
+static struct mlx5_flow_root_namespace *find_root(struct fs_node *node)
+{
+ struct fs_node *root;
+ struct mlx5_flow_namespace *ns;
+
+ root = node->root;
+
+ if (WARN_ON(root->type != FS_TYPE_NAMESPACE)) {
+ pr_warn("mlx5: flow steering node is not in tree or garbaged\n");
+ return NULL;
+ }
+
+ ns = container_of(root, struct mlx5_flow_namespace, node);
+ return container_of(ns, struct mlx5_flow_root_namespace, ns);
+}
+
+static inline struct mlx5_core_dev *get_dev(struct fs_node *node)
+{
+ struct mlx5_flow_root_namespace *root = find_root(node);
+
+ if (root)
+ return root->dev;
+ return NULL;
+}
+
+static void del_flow_table(struct fs_node *node)
+{
+ struct mlx5_flow_table *ft;
+ struct mlx5_core_dev *dev;
+ struct fs_prio *prio;
+ int err;
+
+ fs_get_obj(ft, node);
+ dev = get_dev(&ft->node);
+
+ err = mlx5_cmd_destroy_flow_table(dev, ft);
+ if (err)
+ pr_warn("flow steering can't destroy ft\n");
+ fs_get_obj(prio, ft->node.parent);
+ prio->num_ft--;
+}
+
+static void del_rule(struct fs_node *node)
+{
+ struct mlx5_flow_rule *rule;
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_group *fg;
+ struct fs_fte *fte;
+ u32 *match_value;
+ struct mlx5_core_dev *dev = get_dev(node);
+ int match_len = MLX5_ST_SZ_BYTES(fte_match_param);
+ int err;
+
+ match_value = mlx5_vzalloc(match_len);
+ if (!match_value) {
+ pr_warn("failed to allocate inbox\n");
+ return;
+ }
+
+ fs_get_obj(rule, node);
+ fs_get_obj(fte, rule->node.parent);
+ fs_get_obj(fg, fte->node.parent);
+ memcpy(match_value, fte->val, sizeof(fte->val));
+ fs_get_obj(ft, fg->node.parent);
+ list_del(&rule->node.list);
+ fte->dests_size--;
+ if (fte->dests_size) {
+ err = mlx5_cmd_update_fte(dev, ft,
+ fg->id, fte);
+ if (err)
+ pr_warn("%s can't del rule fg id=%d fte_index=%d\n",
+ __func__, fg->id, fte->index);
+ }
+ kvfree(match_value);
+}
+
+static void del_fte(struct fs_node *node)
+{
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_group *fg;
+ struct mlx5_core_dev *dev;
+ struct fs_fte *fte;
+ int err;
+
+ fs_get_obj(fte, node);
+ fs_get_obj(fg, fte->node.parent);
+ fs_get_obj(ft, fg->node.parent);
+
+ dev = get_dev(&ft->node);
+ err = mlx5_cmd_delete_fte(dev, ft,
+ fte->index);
+ if (err)
+ pr_warn("flow steering can't delete fte in index %d of flow group id %d\n",
+ fte->index, fg->id);
+
+ fte->status = 0;
+ fg->num_ftes--;
+}
+
+static void del_flow_group(struct fs_node *node)
+{
+ struct mlx5_flow_group *fg;
+ struct mlx5_flow_table *ft;
+ struct mlx5_core_dev *dev;
+
+ fs_get_obj(fg, node);
+ fs_get_obj(ft, fg->node.parent);
+ dev = get_dev(&ft->node);
+
+ if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id))
+ pr_warn("flow steering can't destroy fg %d of ft %d\n",
+ fg->id, ft->id);
+}
+
+static struct fs_fte *alloc_fte(u8 action,
+ u32 flow_tag,
+ u32 *match_value,
+ unsigned int index)
+{
+ struct fs_fte *fte;
+
+ fte = kzalloc(sizeof(*fte), GFP_KERNEL);
+ if (!fte)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(fte->val, match_value, sizeof(fte->val));
+ fte->node.type = FS_TYPE_FLOW_ENTRY;
+ fte->flow_tag = flow_tag;
+ fte->index = index;
+ fte->action = action;
+
+ return fte;
+}
+
+static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in)
+{
+ struct mlx5_flow_group *fg;
+ void *match_criteria = MLX5_ADDR_OF(create_flow_group_in,
+ create_fg_in, match_criteria);
+ u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
+ create_fg_in,
+ match_criteria_enable);
+ fg = kzalloc(sizeof(*fg), GFP_KERNEL);
+ if (!fg)
+ return ERR_PTR(-ENOMEM);
+
+ fg->mask.match_criteria_enable = match_criteria_enable;
+ memcpy(&fg->mask.match_criteria, match_criteria,
+ sizeof(fg->mask.match_criteria));
+ fg->node.type = FS_TYPE_FLOW_GROUP;
+ fg->start_index = MLX5_GET(create_flow_group_in, create_fg_in,
+ start_flow_index);
+ fg->max_ftes = MLX5_GET(create_flow_group_in, create_fg_in,
+ end_flow_index) - fg->start_index + 1;
+ return fg;
+}
+
+static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte,
+ enum fs_flow_table_type table_type)
+{
+ struct mlx5_flow_table *ft;
+
+ ft = kzalloc(sizeof(*ft), GFP_KERNEL);
+ if (!ft)
+ return NULL;
+
+ ft->level = level;
+ ft->node.type = FS_TYPE_FLOW_TABLE;
+ ft->type = table_type;
+ ft->max_fte = max_fte;
+
+ return ft;
+}
+
+/* If reverse is false, then we search for the first flow table in the
+ * root sub-tree from start(closest from right), else we search for the
+ * last flow table in the root sub-tree till start(closest from left).
+ */
+static struct mlx5_flow_table *find_closest_ft_recursive(struct fs_node *root,
+ struct list_head *start,
+ bool reverse)
+{
+#define list_advance_entry(pos, reverse) \
+ ((reverse) ? list_prev_entry(pos, list) : list_next_entry(pos, list))
+
+#define list_for_each_advance_continue(pos, head, reverse) \
+ for (pos = list_advance_entry(pos, reverse); \
+ &pos->list != (head); \
+ pos = list_advance_entry(pos, reverse))
+
+ struct fs_node *iter = list_entry(start, struct fs_node, list);
+ struct mlx5_flow_table *ft = NULL;
+
+ if (!root)
+ return NULL;
+
+ list_for_each_advance_continue(iter, &root->children, reverse) {
+ if (iter->type == FS_TYPE_FLOW_TABLE) {
+ fs_get_obj(ft, iter);
+ return ft;
+ }
+ ft = find_closest_ft_recursive(iter, &iter->children, reverse);
+ if (ft)
+ return ft;
+ }
+
+ return ft;
+}
+
+/* If reverse if false then return the first flow table in next priority of
+ * prio in the tree, else return the last flow table in the previous priority
+ * of prio in the tree.
+ */
+static struct mlx5_flow_table *find_closest_ft(struct fs_prio *prio, bool reverse)
+{
+ struct mlx5_flow_table *ft = NULL;
+ struct fs_node *curr_node;
+ struct fs_node *parent;
+
+ parent = prio->node.parent;
+ curr_node = &prio->node;
+ while (!ft && parent) {
+ ft = find_closest_ft_recursive(parent, &curr_node->list, reverse);
+ curr_node = parent;
+ parent = curr_node->parent;
+ }
+ return ft;
+}
+
+/* Assuming all the tree is locked by mutex chain lock */
+static struct mlx5_flow_table *find_next_chained_ft(struct fs_prio *prio)
+{
+ return find_closest_ft(prio, false);
+}
+
+/* Assuming all the tree is locked by mutex chain lock */
+static struct mlx5_flow_table *find_prev_chained_ft(struct fs_prio *prio)
+{
+ return find_closest_ft(prio, true);
+}
+
+static int connect_fts_in_prio(struct mlx5_core_dev *dev,
+ struct fs_prio *prio,
+ struct mlx5_flow_table *ft)
+{
+ struct mlx5_flow_table *iter;
+ int i = 0;
+ int err;
+
+ fs_for_each_ft(iter, prio) {
+ i++;
+ err = mlx5_cmd_modify_flow_table(dev,
+ iter,
+ ft);
+ if (err) {
+ mlx5_core_warn(dev, "Failed to modify flow table %d\n",
+ iter->id);
+ /* The driver is out of sync with the FW */
+ if (i > 1)
+ WARN_ON(true);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* Connect flow tables from previous priority of prio to ft */
+static int connect_prev_fts(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_prio *prio)
+{
+ struct mlx5_flow_table *prev_ft;
+
+ prev_ft = find_prev_chained_ft(prio);
+ if (prev_ft) {
+ struct fs_prio *prev_prio;
+
+ fs_get_obj(prev_prio, prev_ft->node.parent);
+ return connect_fts_in_prio(dev, prev_prio, ft);
+ }
+ return 0;
+}
+
+static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio
+ *prio)
+{
+ struct mlx5_flow_root_namespace *root = find_root(&prio->node);
+ int min_level = INT_MAX;
+ int err;
+
+ if (root->root_ft)
+ min_level = root->root_ft->level;
+
+ if (ft->level >= min_level)
+ return 0;
+
+ err = mlx5_cmd_update_root_ft(root->dev, ft);
+ if (err)
+ mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n",
+ ft->id);
+ else
+ root->root_ft = ft;
+
+ return err;
+}
+
+static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft,
+ struct fs_prio *prio)
+{
+ int err = 0;
+
+ /* Connect_prev_fts and update_root_ft_create are mutually exclusive */
+
+ if (list_empty(&prio->node.children)) {
+ err = connect_prev_fts(dev, ft, prio);
+ if (err)
+ return err;
+ }
+
+ if (MLX5_CAP_FLOWTABLE(dev,
+ flow_table_properties_nic_receive.modify_root))
+ err = update_root_ft_create(ft, prio);
+ return err;
+}
+
+struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int max_fte)
+{
+ struct mlx5_flow_table *next_ft = NULL;
+ struct mlx5_flow_table *ft;
+ int err;
+ int log_table_sz;
+ struct mlx5_flow_root_namespace *root =
+ find_root(&ns->node);
+ struct fs_prio *fs_prio = NULL;
+
+ if (!root) {
+ pr_err("mlx5: flow steering failed to find root of namespace\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&root->chain_lock);
+ fs_prio = find_prio(ns, prio);
+ if (!fs_prio) {
+ err = -EINVAL;
+ goto unlock_root;
+ }
+ if (fs_prio->num_ft == fs_prio->max_ft) {
+ err = -ENOSPC;
+ goto unlock_root;
+ }
+
+ ft = alloc_flow_table(find_next_free_level(fs_prio),
+ roundup_pow_of_two(max_fte),
+ root->table_type);
+ if (!ft) {
+ err = -ENOMEM;
+ goto unlock_root;
+ }
+
+ tree_init_node(&ft->node, 1, del_flow_table);
+ log_table_sz = ilog2(ft->max_fte);
+ next_ft = find_next_chained_ft(fs_prio);
+ err = mlx5_cmd_create_flow_table(root->dev, ft->type, ft->level,
+ log_table_sz, next_ft, &ft->id);
+ if (err)
+ goto free_ft;
+
+ err = connect_flow_table(root->dev, ft, fs_prio);
+ if (err)
+ goto destroy_ft;
+ lock_ref_node(&fs_prio->node);
+ tree_add_node(&ft->node, &fs_prio->node);
+ list_add_tail(&ft->node.list, &fs_prio->node.children);
+ fs_prio->num_ft++;
+ unlock_ref_node(&fs_prio->node);
+ mutex_unlock(&root->chain_lock);
+ return ft;
+destroy_ft:
+ mlx5_cmd_destroy_flow_table(root->dev, ft);
+free_ft:
+ kfree(ft);
+unlock_root:
+ mutex_unlock(&root->chain_lock);
+ return ERR_PTR(err);
+}
+
+struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int num_flow_table_entries,
+ int max_num_groups)
+{
+ struct mlx5_flow_table *ft;
+
+ if (max_num_groups > num_flow_table_entries)
+ return ERR_PTR(-EINVAL);
+
+ ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries);
+ if (IS_ERR(ft))
+ return ft;
+
+ ft->autogroup.active = true;
+ ft->autogroup.required_groups = max_num_groups;
+
+ return ft;
+}
+EXPORT_SYMBOL(mlx5_create_auto_grouped_flow_table);
+
+/* Flow table should be locked */
+static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table *ft,
+ u32 *fg_in,
+ struct list_head
+ *prev_fg,
+ bool is_auto_fg)
+{
+ struct mlx5_flow_group *fg;
+ struct mlx5_core_dev *dev = get_dev(&ft->node);
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ fg = alloc_flow_group(fg_in);
+ if (IS_ERR(fg))
+ return fg;
+
+ err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id);
+ if (err) {
+ kfree(fg);
+ return ERR_PTR(err);
+ }
+
+ if (ft->autogroup.active)
+ ft->autogroup.num_groups++;
+ /* Add node to tree */
+ tree_init_node(&fg->node, !is_auto_fg, del_flow_group);
+ tree_add_node(&fg->node, &ft->node);
+ /* Add node to group list */
+ list_add(&fg->node.list, ft->node.children.prev);
+
+ return fg;
+}
+
+struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
+ u32 *fg_in)
+{
+ struct mlx5_flow_group *fg;
+
+ if (ft->autogroup.active)
+ return ERR_PTR(-EPERM);
+
+ lock_ref_node(&ft->node);
+ fg = create_flow_group_common(ft, fg_in, &ft->node.children, false);
+ unlock_ref_node(&ft->node);
+
+ return fg;
+}
+
+static struct mlx5_flow_rule *alloc_rule(struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_rule *rule;
+
+ rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+ if (!rule)
+ return NULL;
+
+ rule->node.type = FS_TYPE_FLOW_DEST;
+ memcpy(&rule->dest_attr, dest, sizeof(*dest));
+
+ return rule;
+}
+
+/* fte should not be deleted while calling this function */
+static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte,
+ struct mlx5_flow_group *fg,
+ struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_rule *rule;
+ int err;
+
+ rule = alloc_rule(dest);
+ if (!rule)
+ return ERR_PTR(-ENOMEM);
+
+ fs_get_obj(ft, fg->node.parent);
+ /* Add dest to dests list- added as first element after the head */
+ tree_init_node(&rule->node, 1, del_rule);
+ list_add_tail(&rule->node.list, &fte->node.children);
+ fte->dests_size++;
+ if (fte->dests_size == 1)
+ err = mlx5_cmd_create_fte(get_dev(&ft->node),
+ ft, fg->id, fte);
+ else
+ err = mlx5_cmd_update_fte(get_dev(&ft->node),
+ ft, fg->id, fte);
+ if (err)
+ goto free_rule;
+
+ fte->status |= FS_FTE_STATUS_EXISTING;
+
+ return rule;
+
+free_rule:
+ list_del(&rule->node.list);
+ kfree(rule);
+ fte->dests_size--;
+ return ERR_PTR(err);
+}
+
+/* Assumed fg is locked */
+static unsigned int get_free_fte_index(struct mlx5_flow_group *fg,
+ struct list_head **prev)
+{
+ struct fs_fte *fte;
+ unsigned int start = fg->start_index;
+
+ if (prev)
+ *prev = &fg->node.children;
+
+ /* assumed list is sorted by index */
+ fs_for_each_fte(fte, fg) {
+ if (fte->index != start)
+ return start;
+ start++;
+ if (prev)
+ *prev = &fte->node.list;
+ }
+
+ return start;
+}
+
+/* prev is output, prev->next = new_fte */
+static struct fs_fte *create_fte(struct mlx5_flow_group *fg,
+ u32 *match_value,
+ u8 action,
+ u32 flow_tag,
+ struct list_head **prev)
+{
+ struct fs_fte *fte;
+ int index;
+
+ index = get_free_fte_index(fg, prev);
+ fte = alloc_fte(action, flow_tag, match_value, index);
+ if (IS_ERR(fte))
+ return fte;
+
+ return fte;
+}
+
+static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ struct list_head *prev = &ft->node.children;
+ unsigned int candidate_index = 0;
+ struct mlx5_flow_group *fg;
+ void *match_criteria_addr;
+ unsigned int group_size = 0;
+ u32 *in;
+
+ if (!ft->autogroup.active)
+ return ERR_PTR(-ENOENT);
+
+ in = mlx5_vzalloc(inlen);
+ if (!in)
+ return ERR_PTR(-ENOMEM);
+
+ if (ft->autogroup.num_groups < ft->autogroup.required_groups)
+ /* We save place for flow groups in addition to max types */
+ group_size = ft->max_fte / (ft->autogroup.required_groups + 1);
+
+ /* ft->max_fte == ft->autogroup.max_types */
+ if (group_size == 0)
+ group_size = 1;
+
+ /* sorted by start_index */
+ fs_for_each_fg(fg, ft) {
+ if (candidate_index + group_size > fg->start_index)
+ candidate_index = fg->start_index + fg->max_ftes;
+ else
+ break;
+ prev = &fg->node.list;
+ }
+
+ if (candidate_index + group_size > ft->max_fte) {
+ fg = ERR_PTR(-ENOSPC);
+ goto out;
+ }
+
+ MLX5_SET(create_flow_group_in, in, match_criteria_enable,
+ match_criteria_enable);
+ MLX5_SET(create_flow_group_in, in, start_flow_index, candidate_index);
+ MLX5_SET(create_flow_group_in, in, end_flow_index, candidate_index +
+ group_size - 1);
+ match_criteria_addr = MLX5_ADDR_OF(create_flow_group_in,
+ in, match_criteria);
+ memcpy(match_criteria_addr, match_criteria,
+ MLX5_ST_SZ_BYTES(fte_match_param));
+
+ fg = create_flow_group_common(ft, in, prev, true);
+out:
+ kvfree(in);
+ return fg;
+}
+
+static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg,
+ u32 *match_value,
+ u8 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest)
+{
+ struct fs_fte *fte;
+ struct mlx5_flow_rule *rule;
+ struct mlx5_flow_table *ft;
+ struct list_head *prev;
+
+ nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT);
+ fs_for_each_fte(fte, fg) {
+ nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD);
+ if (compare_match_value(&fg->mask, match_value, &fte->val) &&
+ action == fte->action && flow_tag == fte->flow_tag) {
+ rule = add_rule_fte(fte, fg, dest);
+ unlock_ref_node(&fte->node);
+ if (IS_ERR(rule))
+ goto unlock_fg;
+ else
+ goto add_rule;
+ }
+ unlock_ref_node(&fte->node);
+ }
+ fs_get_obj(ft, fg->node.parent);
+ if (fg->num_ftes >= fg->max_ftes) {
+ rule = ERR_PTR(-ENOSPC);
+ goto unlock_fg;
+ }
+
+ fte = create_fte(fg, match_value, action, flow_tag, &prev);
+ if (IS_ERR(fte)) {
+ rule = (void *)fte;
+ goto unlock_fg;
+ }
+ tree_init_node(&fte->node, 0, del_fte);
+ rule = add_rule_fte(fte, fg, dest);
+ if (IS_ERR(rule)) {
+ kfree(fte);
+ goto unlock_fg;
+ }
+
+ fg->num_ftes++;
+
+ tree_add_node(&fte->node, &fg->node);
+ list_add(&fte->node.list, prev);
+add_rule:
+ tree_add_node(&rule->node, &fte->node);
+unlock_fg:
+ unlock_ref_node(&fg->node);
+ return rule;
+}
+
+static struct mlx5_flow_rule *add_rule_to_auto_fg(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria,
+ u32 *match_value,
+ u8 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_rule *rule;
+ struct mlx5_flow_group *g;
+
+ g = create_autogroup(ft, match_criteria_enable, match_criteria);
+ if (IS_ERR(g))
+ return (void *)g;
+
+ rule = add_rule_fg(g, match_value,
+ action, flow_tag, dest);
+ if (IS_ERR(rule)) {
+ /* Remove assumes refcount > 0 and autogroup creates a group
+ * with a refcount = 0.
+ */
+ tree_get_node(&g->node);
+ tree_remove_node(&g->node);
+ }
+ return rule;
+}
+
+struct mlx5_flow_rule *
+mlx5_add_flow_rule(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria,
+ u32 *match_value,
+ u32 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_group *g;
+ struct mlx5_flow_rule *rule;
+
+ nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT);
+ fs_for_each_fg(g, ft)
+ if (compare_match_criteria(g->mask.match_criteria_enable,
+ match_criteria_enable,
+ g->mask.match_criteria,
+ match_criteria)) {
+ rule = add_rule_fg(g, match_value,
+ action, flow_tag, dest);
+ if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC)
+ goto unlock;
+ }
+
+ rule = add_rule_to_auto_fg(ft, match_criteria_enable, match_criteria,
+ match_value, action, flow_tag, dest);
+unlock:
+ unlock_ref_node(&ft->node);
+ return rule;
+}
+EXPORT_SYMBOL(mlx5_add_flow_rule);
+
+void mlx5_del_flow_rule(struct mlx5_flow_rule *rule)
+{
+ tree_remove_node(&rule->node);
+}
+EXPORT_SYMBOL(mlx5_del_flow_rule);
+
+/* Assuming prio->node.children(flow tables) is sorted by level */
+static struct mlx5_flow_table *find_next_ft(struct mlx5_flow_table *ft)
+{
+ struct fs_prio *prio;
+
+ fs_get_obj(prio, ft->node.parent);
+
+ if (!list_is_last(&ft->node.list, &prio->node.children))
+ return list_next_entry(ft, node.list);
+ return find_next_chained_ft(prio);
+}
+
+static int update_root_ft_destroy(struct mlx5_flow_table *ft)
+{
+ struct mlx5_flow_root_namespace *root = find_root(&ft->node);
+ struct mlx5_flow_table *new_root_ft = NULL;
+
+ if (root->root_ft != ft)
+ return 0;
+
+ new_root_ft = find_next_ft(ft);
+ if (new_root_ft) {
+ int err = mlx5_cmd_update_root_ft(root->dev, new_root_ft);
+
+ if (err) {
+ mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n",
+ ft->id);
+ return err;
+ }
+ root->root_ft = new_root_ft;
+ }
+ return 0;
+}
+
+/* Connect flow table from previous priority to
+ * the next flow table.
+ */
+static int disconnect_flow_table(struct mlx5_flow_table *ft)
+{
+ struct mlx5_core_dev *dev = get_dev(&ft->node);
+ struct mlx5_flow_table *next_ft;
+ struct fs_prio *prio;
+ int err = 0;
+
+ err = update_root_ft_destroy(ft);
+ if (err)
+ return err;
+
+ fs_get_obj(prio, ft->node.parent);
+ if (!(list_first_entry(&prio->node.children,
+ struct mlx5_flow_table,
+ node.list) == ft))
+ return 0;
+
+ next_ft = find_next_chained_ft(prio);
+ err = connect_prev_fts(dev, next_ft, prio);
+ if (err)
+ mlx5_core_warn(dev, "Failed to disconnect flow table %d\n",
+ ft->id);
+ return err;
+}
+
+int mlx5_destroy_flow_table(struct mlx5_flow_table *ft)
+{
+ struct mlx5_flow_root_namespace *root = find_root(&ft->node);
+ int err = 0;
+
+ mutex_lock(&root->chain_lock);
+ err = disconnect_flow_table(ft);
+ if (err) {
+ mutex_unlock(&root->chain_lock);
+ return err;
+ }
+ if (tree_remove_node(&ft->node))
+ mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n",
+ ft->id);
+ mutex_unlock(&root->chain_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_destroy_flow_table);
+
+void mlx5_destroy_flow_group(struct mlx5_flow_group *fg)
+{
+ if (tree_remove_node(&fg->node))
+ mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n",
+ fg->id);
+}
+
+struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
+ enum mlx5_flow_namespace_type type)
+{
+ struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns;
+ int prio;
+ static struct fs_prio *fs_prio;
+ struct mlx5_flow_namespace *ns;
+
+ if (!root_ns)
+ return NULL;
+
+ switch (type) {
+ case MLX5_FLOW_NAMESPACE_BYPASS:
+ case MLX5_FLOW_NAMESPACE_KERNEL:
+ case MLX5_FLOW_NAMESPACE_LEFTOVERS:
+ prio = type;
+ break;
+ case MLX5_FLOW_NAMESPACE_FDB:
+ if (dev->priv.fdb_root_ns)
+ return &dev->priv.fdb_root_ns->ns;
+ else
+ return NULL;
+ default:
+ return NULL;
+ }
+
+ fs_prio = find_prio(&root_ns->ns, prio);
+ if (!fs_prio)
+ return NULL;
+
+ ns = list_first_entry(&fs_prio->node.children,
+ typeof(*ns),
+ node.list);
+
+ return ns;
+}
+EXPORT_SYMBOL(mlx5_get_flow_namespace);
+
+static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns,
+ unsigned prio, int max_ft)
+{
+ struct fs_prio *fs_prio;
+
+ fs_prio = kzalloc(sizeof(*fs_prio), GFP_KERNEL);
+ if (!fs_prio)
+ return ERR_PTR(-ENOMEM);
+
+ fs_prio->node.type = FS_TYPE_PRIO;
+ tree_init_node(&fs_prio->node, 1, NULL);
+ tree_add_node(&fs_prio->node, &ns->node);
+ fs_prio->max_ft = max_ft;
+ fs_prio->prio = prio;
+ list_add_tail(&fs_prio->node.list, &ns->node.children);
+
+ return fs_prio;
+}
+
+static struct mlx5_flow_namespace *fs_init_namespace(struct mlx5_flow_namespace
+ *ns)
+{
+ ns->node.type = FS_TYPE_NAMESPACE;
+
+ return ns;
+}
+
+static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio)
+{
+ struct mlx5_flow_namespace *ns;
+
+ ns = kzalloc(sizeof(*ns), GFP_KERNEL);
+ if (!ns)
+ return ERR_PTR(-ENOMEM);
+
+ fs_init_namespace(ns);
+ tree_init_node(&ns->node, 1, NULL);
+ tree_add_node(&ns->node, &prio->node);
+ list_add_tail(&ns->node.list, &prio->node.children);
+
+ return ns;
+}
+
+static int create_leaf_prios(struct mlx5_flow_namespace *ns, struct init_tree_node
+ *prio_metadata)
+{
+ struct fs_prio *fs_prio;
+ int i;
+
+ for (i = 0; i < prio_metadata->num_leaf_prios; i++) {
+ fs_prio = fs_create_prio(ns, i, prio_metadata->max_ft);
+ if (IS_ERR(fs_prio))
+ return PTR_ERR(fs_prio);
+ }
+ return 0;
+}
+
+#define FLOW_TABLE_BIT_SZ 1
+#define GET_FLOW_TABLE_CAP(dev, offset) \
+ ((be32_to_cpu(*((__be32 *)(dev->hca_caps_cur[MLX5_CAP_FLOW_TABLE]) + \
+ offset / 32)) >> \
+ (32 - FLOW_TABLE_BIT_SZ - (offset & 0x1f))) & FLOW_TABLE_BIT_SZ)
+static bool has_required_caps(struct mlx5_core_dev *dev, struct node_caps *caps)
+{
+ int i;
+
+ for (i = 0; i < caps->arr_sz; i++) {
+ if (!GET_FLOW_TABLE_CAP(dev, caps->caps[i]))
+ return false;
+ }
+ return true;
+}
+
+static int init_root_tree_recursive(struct mlx5_core_dev *dev,
+ struct init_tree_node *init_node,
+ struct fs_node *fs_parent_node,
+ struct init_tree_node *init_parent_node,
+ int index)
+{
+ int max_ft_level = MLX5_CAP_FLOWTABLE(dev,
+ flow_table_properties_nic_receive.
+ max_ft_level);
+ struct mlx5_flow_namespace *fs_ns;
+ struct fs_prio *fs_prio;
+ struct fs_node *base;
+ int i;
+ int err;
+
+ if (init_node->type == FS_TYPE_PRIO) {
+ if ((init_node->min_ft_level > max_ft_level) ||
+ !has_required_caps(dev, &init_node->caps))
+ return 0;
+
+ fs_get_obj(fs_ns, fs_parent_node);
+ if (init_node->num_leaf_prios)
+ return create_leaf_prios(fs_ns, init_node);
+ fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft);
+ if (IS_ERR(fs_prio))
+ return PTR_ERR(fs_prio);
+ base = &fs_prio->node;
+ } else if (init_node->type == FS_TYPE_NAMESPACE) {
+ fs_get_obj(fs_prio, fs_parent_node);
+ fs_ns = fs_create_namespace(fs_prio);
+ if (IS_ERR(fs_ns))
+ return PTR_ERR(fs_ns);
+ base = &fs_ns->node;
+ } else {
+ return -EINVAL;
+ }
+ for (i = 0; i < init_node->ar_size; i++) {
+ err = init_root_tree_recursive(dev, &init_node->children[i],
+ base, init_node, i);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int init_root_tree(struct mlx5_core_dev *dev,
+ struct init_tree_node *init_node,
+ struct fs_node *fs_parent_node)
+{
+ int i;
+ struct mlx5_flow_namespace *fs_ns;
+ int err;
+
+ fs_get_obj(fs_ns, fs_parent_node);
+ for (i = 0; i < init_node->ar_size; i++) {
+ err = init_root_tree_recursive(dev, &init_node->children[i],
+ &fs_ns->node,
+ init_node, i);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev,
+ enum fs_flow_table_type
+ table_type)
+{
+ struct mlx5_flow_root_namespace *root_ns;
+ struct mlx5_flow_namespace *ns;
+
+ /* Create the root namespace */
+ root_ns = mlx5_vzalloc(sizeof(*root_ns));
+ if (!root_ns)
+ return NULL;
+
+ root_ns->dev = dev;
+ root_ns->table_type = table_type;
+
+ ns = &root_ns->ns;
+ fs_init_namespace(ns);
+ mutex_init(&root_ns->chain_lock);
+ tree_init_node(&ns->node, 1, NULL);
+ tree_add_node(&ns->node, NULL);
+
+ return root_ns;
+}
+
+static void set_prio_attrs_in_prio(struct fs_prio *prio, int acc_level);
+
+static int set_prio_attrs_in_ns(struct mlx5_flow_namespace *ns, int acc_level)
+{
+ struct fs_prio *prio;
+
+ fs_for_each_prio(prio, ns) {
+ /* This updates prio start_level and max_ft */
+ set_prio_attrs_in_prio(prio, acc_level);
+ acc_level += prio->max_ft;
+ }
+ return acc_level;
+}
+
+static void set_prio_attrs_in_prio(struct fs_prio *prio, int acc_level)
+{
+ struct mlx5_flow_namespace *ns;
+ int acc_level_ns = acc_level;
+
+ prio->start_level = acc_level;
+ fs_for_each_ns(ns, prio)
+ /* This updates start_level and max_ft of ns's priority descendants */
+ acc_level_ns = set_prio_attrs_in_ns(ns, acc_level);
+ if (!prio->max_ft)
+ prio->max_ft = acc_level_ns - prio->start_level;
+ WARN_ON(prio->max_ft < acc_level_ns - prio->start_level);
+}
+
+static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns)
+{
+ struct mlx5_flow_namespace *ns = &root_ns->ns;
+ struct fs_prio *prio;
+ int start_level = 0;
+
+ fs_for_each_prio(prio, ns) {
+ set_prio_attrs_in_prio(prio, start_level);
+ start_level += prio->max_ft;
+ }
+}
+
+static int init_root_ns(struct mlx5_core_dev *dev)
+{
+
+ dev->priv.root_ns = create_root_ns(dev, FS_FT_NIC_RX);
+ if (IS_ERR_OR_NULL(dev->priv.root_ns))
+ goto cleanup;
+
+ if (init_root_tree(dev, &root_fs, &dev->priv.root_ns->ns.node))
+ goto cleanup;
+
+ set_prio_attrs(dev->priv.root_ns);
+
+ return 0;
+
+cleanup:
+ mlx5_cleanup_fs(dev);
+ return -ENOMEM;
+}
+
+static void cleanup_single_prio_root_ns(struct mlx5_core_dev *dev,
+ struct mlx5_flow_root_namespace *root_ns)
+{
+ struct fs_node *prio;
+
+ if (!root_ns)
+ return;
+
+ if (!list_empty(&root_ns->ns.node.children)) {
+ prio = list_first_entry(&root_ns->ns.node.children,
+ struct fs_node,
+ list);
+ if (tree_remove_node(prio))
+ mlx5_core_warn(dev,
+ "Flow steering priority wasn't destroyed, refcount > 1\n");
+ }
+ if (tree_remove_node(&root_ns->ns.node))
+ mlx5_core_warn(dev,
+ "Flow steering namespace wasn't destroyed, refcount > 1\n");
+ root_ns = NULL;
+}
+
+static void cleanup_root_ns(struct mlx5_core_dev *dev)
+{
+ struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns;
+ struct fs_prio *iter_prio;
+
+ if (!MLX5_CAP_GEN(dev, nic_flow_table))
+ return;
+
+ if (!root_ns)
+ return;
+
+ /* stage 1 */
+ fs_for_each_prio(iter_prio, &root_ns->ns) {
+ struct fs_node *node;
+ struct mlx5_flow_namespace *iter_ns;
+
+ fs_for_each_ns_or_ft(node, iter_prio) {
+ if (node->type == FS_TYPE_FLOW_TABLE)
+ continue;
+ fs_get_obj(iter_ns, node);
+ while (!list_empty(&iter_ns->node.children)) {
+ struct fs_prio *obj_iter_prio2;
+ struct fs_node *iter_prio2 =
+ list_first_entry(&iter_ns->node.children,
+ struct fs_node,
+ list);
+
+ fs_get_obj(obj_iter_prio2, iter_prio2);
+ if (tree_remove_node(iter_prio2)) {
+ mlx5_core_warn(dev,
+ "Priority %d wasn't destroyed, refcount > 1\n",
+ obj_iter_prio2->prio);
+ return;
+ }
+ }
+ }
+ }
+
+ /* stage 2 */
+ fs_for_each_prio(iter_prio, &root_ns->ns) {
+ while (!list_empty(&iter_prio->node.children)) {
+ struct fs_node *iter_ns =
+ list_first_entry(&iter_prio->node.children,
+ struct fs_node,
+ list);
+ if (tree_remove_node(iter_ns)) {
+ mlx5_core_warn(dev,
+ "Namespace wasn't destroyed, refcount > 1\n");
+ return;
+ }
+ }
+ }
+
+ /* stage 3 */
+ while (!list_empty(&root_ns->ns.node.children)) {
+ struct fs_prio *obj_prio_node;
+ struct fs_node *prio_node =
+ list_first_entry(&root_ns->ns.node.children,
+ struct fs_node,
+ list);
+
+ fs_get_obj(obj_prio_node, prio_node);
+ if (tree_remove_node(prio_node)) {
+ mlx5_core_warn(dev,
+ "Priority %d wasn't destroyed, refcount > 1\n",
+ obj_prio_node->prio);
+ return;
+ }
+ }
+
+ if (tree_remove_node(&root_ns->ns.node)) {
+ mlx5_core_warn(dev,
+ "root namespace wasn't destroyed, refcount > 1\n");
+ return;
+ }
+
+ dev->priv.root_ns = NULL;
+}
+
+void mlx5_cleanup_fs(struct mlx5_core_dev *dev)
+{
+ cleanup_root_ns(dev);
+ cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns);
+}
+
+static int init_fdb_root_ns(struct mlx5_core_dev *dev)
+{
+ struct fs_prio *prio;
+
+ dev->priv.fdb_root_ns = create_root_ns(dev, FS_FT_FDB);
+ if (!dev->priv.fdb_root_ns)
+ return -ENOMEM;
+
+ /* Create single prio */
+ prio = fs_create_prio(&dev->priv.fdb_root_ns->ns, 0, 1);
+ if (IS_ERR(prio)) {
+ cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns);
+ return PTR_ERR(prio);
+ } else {
+ return 0;
+ }
+}
+
+int mlx5_init_fs(struct mlx5_core_dev *dev)
+{
+ int err = 0;
+
+ if (MLX5_CAP_GEN(dev, nic_flow_table)) {
+ err = init_root_ns(dev);
+ if (err)
+ return err;
+ }
+ if (MLX5_CAP_GEN(dev, eswitch_flow_table)) {
+ err = init_fdb_root_ns(dev);
+ if (err)
+ cleanup_root_ns(dev);
+ }
+
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
new file mode 100644
index 000000000000..00245fd7e4bc
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MLX5_FS_CORE_
+#define _MLX5_FS_CORE_
+
+#include <linux/mlx5/fs.h>
+
+enum fs_node_type {
+ FS_TYPE_NAMESPACE,
+ FS_TYPE_PRIO,
+ FS_TYPE_FLOW_TABLE,
+ FS_TYPE_FLOW_GROUP,
+ FS_TYPE_FLOW_ENTRY,
+ FS_TYPE_FLOW_DEST
+};
+
+enum fs_flow_table_type {
+ FS_FT_NIC_RX = 0x0,
+ FS_FT_FDB = 0X4,
+};
+
+enum fs_fte_status {
+ FS_FTE_STATUS_EXISTING = 1UL << 0,
+};
+
+struct fs_node {
+ struct list_head list;
+ struct list_head children;
+ enum fs_node_type type;
+ struct fs_node *parent;
+ struct fs_node *root;
+ /* lock the node for writing and traversing */
+ struct mutex lock;
+ atomic_t refcount;
+ void (*remove_func)(struct fs_node *);
+};
+
+struct mlx5_flow_rule {
+ struct fs_node node;
+ struct mlx5_flow_destination dest_attr;
+};
+
+/* Type of children is mlx5_flow_group */
+struct mlx5_flow_table {
+ struct fs_node node;
+ u32 id;
+ unsigned int max_fte;
+ unsigned int level;
+ enum fs_flow_table_type type;
+ struct {
+ bool active;
+ unsigned int required_groups;
+ unsigned int num_groups;
+ } autogroup;
+};
+
+/* Type of children is mlx5_flow_rule */
+struct fs_fte {
+ struct fs_node node;
+ u32 val[MLX5_ST_SZ_DW(fte_match_param)];
+ u32 dests_size;
+ u32 flow_tag;
+ u32 index;
+ u32 action;
+ enum fs_fte_status status;
+};
+
+/* Type of children is mlx5_flow_table/namespace */
+struct fs_prio {
+ struct fs_node node;
+ unsigned int max_ft;
+ unsigned int start_level;
+ unsigned int prio;
+ unsigned int num_ft;
+};
+
+/* Type of children is fs_prio */
+struct mlx5_flow_namespace {
+ /* parent == NULL => root ns */
+ struct fs_node node;
+};
+
+struct mlx5_flow_group_mask {
+ u8 match_criteria_enable;
+ u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)];
+};
+
+/* Type of children is fs_fte */
+struct mlx5_flow_group {
+ struct fs_node node;
+ struct mlx5_flow_group_mask mask;
+ u32 start_index;
+ u32 max_ftes;
+ u32 num_ftes;
+ u32 id;
+};
+
+struct mlx5_flow_root_namespace {
+ struct mlx5_flow_namespace ns;
+ enum fs_flow_table_type table_type;
+ struct mlx5_core_dev *dev;
+ struct mlx5_flow_table *root_ft;
+ /* Should be held when chaining flow tables */
+ struct mutex chain_lock;
+};
+
+int mlx5_init_fs(struct mlx5_core_dev *dev);
+void mlx5_cleanup_fs(struct mlx5_core_dev *dev);
+
+#define fs_get_obj(v, _node) {v = container_of((_node), typeof(*v), node); }
+
+#define fs_list_for_each_entry(pos, root) \
+ list_for_each_entry(pos, root, node.list)
+
+#define fs_for_each_ns_or_ft_reverse(pos, prio) \
+ list_for_each_entry_reverse(pos, &(prio)->node.children, list)
+
+#define fs_for_each_ns_or_ft(pos, prio) \
+ list_for_each_entry(pos, (&(prio)->node.children), list)
+
+#define fs_for_each_prio(pos, ns) \
+ fs_list_for_each_entry(pos, &(ns)->node.children)
+
+#define fs_for_each_ns(pos, prio) \
+ fs_list_for_each_entry(pos, &(prio)->node.children)
+
+#define fs_for_each_ft(pos, prio) \
+ fs_list_for_each_entry(pos, &(prio)->node.children)
+
+#define fs_for_each_fg(pos, ft) \
+ fs_list_for_each_entry(pos, &(ft)->node.children)
+
+#define fs_for_each_fte(pos, fg) \
+ fs_list_for_each_entry(pos, &(fg)->node.children)
+
+#define fs_for_each_dst(pos, fte) \
+ fs_list_for_each_entry(pos, &(fte)->node.children)
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
index 9335e5ae18cc..aa1ab4702385 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
@@ -160,6 +160,30 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev)
if (err)
return err;
}
+
+ if (MLX5_CAP_GEN(dev, vport_group_manager) &&
+ MLX5_CAP_GEN(dev, eswitch_flow_table)) {
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH_FLOW_TABLE,
+ HCA_CAP_OPMOD_GET_CUR);
+ if (err)
+ return err;
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH_FLOW_TABLE,
+ HCA_CAP_OPMOD_GET_MAX);
+ if (err)
+ return err;
+ }
+
+ if (MLX5_CAP_GEN(dev, eswitch_flow_table)) {
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH,
+ HCA_CAP_OPMOD_GET_CUR);
+ if (err)
+ return err;
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH,
+ HCA_CAP_OPMOD_GET_MAX);
+ if (err)
+ return err;
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 4ac8d4cc4973..67676cf0d507 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -49,6 +49,10 @@
#include <linux/delay.h>
#include <linux/mlx5/mlx5_ifc.h>
#include "mlx5_core.h"
+#include "fs_core.h"
+#ifdef CONFIG_MLX5_CORE_EN
+#include "eswitch.h"
+#endif
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
MODULE_DESCRIPTION("Mellanox Connect-IB, ConnectX-4 core driver");
@@ -454,6 +458,9 @@ static int set_hca_ctrl(struct mlx5_core_dev *dev)
struct mlx5_reg_host_endianess he_out;
int err;
+ if (!mlx5_core_is_pf(dev))
+ return 0;
+
memset(&he_in, 0, sizeof(he_in));
he_in.he = MLX5_SET_HOST_ENDIANNESS;
err = mlx5_core_access_reg(dev, &he_in, sizeof(he_in),
@@ -462,42 +469,52 @@ static int set_hca_ctrl(struct mlx5_core_dev *dev)
return err;
}
-static int mlx5_core_enable_hca(struct mlx5_core_dev *dev)
+int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id)
{
+ u32 out[MLX5_ST_SZ_DW(enable_hca_out)];
+ u32 in[MLX5_ST_SZ_DW(enable_hca_in)];
int err;
- struct mlx5_enable_hca_mbox_in in;
- struct mlx5_enable_hca_mbox_out out;
- memset(&in, 0, sizeof(in));
- memset(&out, 0, sizeof(out));
- in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ENABLE_HCA);
+ memset(in, 0, sizeof(in));
+ MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA);
+ MLX5_SET(enable_hca_in, in, function_id, func_id);
+ memset(out, 0, sizeof(out));
+
err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
if (err)
return err;
- if (out.hdr.status)
- return mlx5_cmd_status_to_err(&out.hdr);
-
- return 0;
+ return mlx5_cmd_status_to_err_v2(out);
}
-static int mlx5_core_disable_hca(struct mlx5_core_dev *dev)
+int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id)
{
+ u32 out[MLX5_ST_SZ_DW(disable_hca_out)];
+ u32 in[MLX5_ST_SZ_DW(disable_hca_in)];
int err;
- struct mlx5_disable_hca_mbox_in in;
- struct mlx5_disable_hca_mbox_out out;
- memset(&in, 0, sizeof(in));
- memset(&out, 0, sizeof(out));
- in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DISABLE_HCA);
- err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ memset(in, 0, sizeof(in));
+ MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA);
+ MLX5_SET(disable_hca_in, in, function_id, func_id);
+ memset(out, 0, sizeof(out));
+ err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
if (err)
return err;
- if (out.hdr.status)
- return mlx5_cmd_status_to_err(&out.hdr);
+ return mlx5_cmd_status_to_err_v2(out);
+}
- return 0;
+cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev)
+{
+ u32 timer_h, timer_h1, timer_l;
+
+ timer_h = ioread32be(&dev->iseg->internal_timer_h);
+ timer_l = ioread32be(&dev->iseg->internal_timer_l);
+ timer_h1 = ioread32be(&dev->iseg->internal_timer_h);
+ if (timer_h != timer_h1) /* wrap around */
+ timer_l = ioread32be(&dev->iseg->internal_timer_l);
+
+ return (cycle_t)timer_l | (cycle_t)timer_h1 << 32;
}
static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i)
@@ -942,7 +959,7 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
mlx5_pagealloc_init(dev);
- err = mlx5_core_enable_hca(dev);
+ err = mlx5_core_enable_hca(dev, 0);
if (err) {
dev_err(&pdev->dev, "enable hca failed\n");
goto err_pagealloc_cleanup;
@@ -1052,6 +1069,25 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
mlx5_init_srq_table(dev);
mlx5_init_mr_table(dev);
+ err = mlx5_init_fs(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init flow steering\n");
+ goto err_fs;
+ }
+#ifdef CONFIG_MLX5_CORE_EN
+ err = mlx5_eswitch_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "eswitch init failed %d\n", err);
+ goto err_reg_dev;
+ }
+#endif
+
+ err = mlx5_sriov_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "sriov init failed %d\n", err);
+ goto err_sriov;
+ }
+
err = mlx5_register_device(dev);
if (err) {
dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err);
@@ -1068,7 +1104,16 @@ out:
return 0;
+err_sriov:
+ if (mlx5_sriov_cleanup(dev))
+ dev_err(&dev->pdev->dev, "sriov cleanup failed\n");
+
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_cleanup(dev->priv.eswitch);
+#endif
err_reg_dev:
+ mlx5_cleanup_fs(dev);
+err_fs:
mlx5_cleanup_mr_table(dev);
mlx5_cleanup_srq_table(dev);
mlx5_cleanup_qp_table(dev);
@@ -1106,7 +1151,7 @@ reclaim_boot_pages:
mlx5_reclaim_startup_pages(dev);
err_disable_hca:
- mlx5_core_disable_hca(dev);
+ mlx5_core_disable_hca(dev, 0);
err_pagealloc_cleanup:
mlx5_pagealloc_cleanup(dev);
@@ -1123,6 +1168,13 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
{
int err = 0;
+ err = mlx5_sriov_cleanup(dev);
+ if (err) {
+ dev_warn(&dev->pdev->dev, "%s: sriov cleanup failed - abort\n",
+ __func__);
+ return err;
+ }
+
mutex_lock(&dev->intf_state_mutex);
if (dev->interface_state == MLX5_INTERFACE_STATE_DOWN) {
dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n",
@@ -1130,6 +1182,11 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
goto out;
}
mlx5_unregister_device(dev);
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_cleanup(dev->priv.eswitch);
+#endif
+
+ mlx5_cleanup_fs(dev);
mlx5_cleanup_mr_table(dev);
mlx5_cleanup_srq_table(dev);
mlx5_cleanup_qp_table(dev);
@@ -1149,7 +1206,7 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
}
mlx5_pagealloc_stop(dev);
mlx5_reclaim_startup_pages(dev);
- mlx5_core_disable_hca(dev);
+ mlx5_core_disable_hca(dev, 0);
mlx5_pagealloc_cleanup(dev);
mlx5_cmd_cleanup(dev);
@@ -1195,6 +1252,7 @@ static int init_one(struct pci_dev *pdev,
return -ENOMEM;
}
priv = &dev->priv;
+ priv->pci_dev_data = id->driver_data;
pci_set_drvdata(pdev, dev);
@@ -1365,12 +1423,12 @@ static const struct pci_error_handlers mlx5_err_handler = {
};
static const struct pci_device_id mlx5_core_pci_table[] = {
- { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */
- { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */
- { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */
- { PCI_VDEVICE(MELLANOX, 0x1014) }, /* ConnectX-4 VF */
- { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */
- { PCI_VDEVICE(MELLANOX, 0x1016) }, /* ConnectX-4LX VF */
+ { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */
+ { PCI_VDEVICE(MELLANOX, 0x1012), MLX5_PCI_DEV_IS_VF}, /* Connect-IB VF */
+ { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */
+ { PCI_VDEVICE(MELLANOX, 0x1014), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4 VF */
+ { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */
+ { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4LX VF */
{ 0, }
};
@@ -1381,7 +1439,8 @@ static struct pci_driver mlx5_core_driver = {
.id_table = mlx5_core_pci_table,
.probe = init_one,
.remove = remove_one,
- .err_handler = &mlx5_err_handler
+ .err_handler = &mlx5_err_handler,
+ .sriov_configure = mlx5_core_sriov_configure,
};
static int __init init(void)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index cee5b7a839bc..0336847ec9a1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -36,6 +36,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/if_link.h>
#define DRIVER_NAME "mlx5_core"
#define DRIVER_VERSION "3.0-1"
@@ -64,6 +65,9 @@ do { \
(__dev)->priv.name, __func__, __LINE__, current->pid, \
##__VA_ARGS__)
+#define mlx5_core_info(__dev, format, ...) \
+ dev_info(&(__dev)->pdev->dev, format, ##__VA_ARGS__)
+
enum {
MLX5_CMD_DATA, /* print command payload only */
MLX5_CMD_TIME, /* print command execution time */
@@ -90,6 +94,11 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
unsigned long param);
void mlx5_enter_error_state(struct mlx5_core_dev *dev);
void mlx5_disable_device(struct mlx5_core_dev *dev);
+int mlx5_core_sriov_configure(struct pci_dev *dev, int num_vfs);
+int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id);
+int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id);
+int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev);
+cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev);
void mlx5e_init(void);
void mlx5e_cleanup(void);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
index 4d3377b12657..9eeee0545f1c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
@@ -33,6 +33,7 @@
#include <linux/highmem.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cmd.h>
#include "mlx5_core.h"
@@ -95,6 +96,7 @@ struct mlx5_manage_pages_outbox {
enum {
MAX_RECLAIM_TIME_MSECS = 5000,
+ MAX_RECLAIM_VFS_PAGES_TIME_MSECS = 2 * 1000 * 60,
};
enum {
@@ -352,6 +354,10 @@ retry:
goto out_4k;
}
+ dev->priv.fw_pages += npages;
+ if (func_id)
+ dev->priv.vfs_pages += npages;
+
mlx5_core_dbg(dev, "err %d\n", err);
kvfree(in);
@@ -405,6 +411,12 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
}
num_claimed = be32_to_cpu(out->num_entries);
+ if (num_claimed > npages) {
+ mlx5_core_warn(dev, "fw returned %d, driver asked %d => corruption\n",
+ num_claimed, npages);
+ err = -EINVAL;
+ goto out_free;
+ }
if (nclaimed)
*nclaimed = num_claimed;
@@ -412,6 +424,9 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
addr = be64_to_cpu(out->pas[i]);
free_4k(dev, addr);
}
+ dev->priv.fw_pages -= num_claimed;
+ if (func_id)
+ dev->priv.vfs_pages -= num_claimed;
out_free:
kvfree(out);
@@ -548,3 +563,26 @@ void mlx5_pagealloc_stop(struct mlx5_core_dev *dev)
{
destroy_workqueue(dev->priv.pg_wq);
}
+
+int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS);
+ int prev_vfs_pages = dev->priv.vfs_pages;
+
+ mlx5_core_dbg(dev, "Waiting for %d pages from %s\n", prev_vfs_pages,
+ dev->priv.name);
+ while (dev->priv.vfs_pages) {
+ if (time_after(jiffies, end)) {
+ mlx5_core_warn(dev, "aborting while there are %d pending pages\n", dev->priv.vfs_pages);
+ return -ETIMEDOUT;
+ }
+ if (dev->priv.vfs_pages < prev_vfs_pages) {
+ end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS);
+ prev_vfs_pages = dev->priv.vfs_pages;
+ }
+ msleep(50);
+ }
+
+ mlx5_core_dbg(dev, "All pages received from %s\n", dev->priv.name);
+ return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c
new file mode 100644
index 000000000000..7b24386794f9
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2014, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/mlx5/driver.h>
+#include "mlx5_core.h"
+#ifdef CONFIG_MLX5_CORE_EN
+#include "eswitch.h"
+#endif
+
+static void enable_vfs(struct mlx5_core_dev *dev, int num_vfs)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int err;
+ int vf;
+
+ for (vf = 1; vf <= num_vfs; vf++) {
+ err = mlx5_core_enable_hca(dev, vf);
+ if (err) {
+ mlx5_core_warn(dev, "failed to enable VF %d\n", vf - 1);
+ } else {
+ sriov->vfs_ctx[vf - 1].enabled = 1;
+ mlx5_core_dbg(dev, "successfully enabled VF %d\n", vf - 1);
+ }
+ }
+}
+
+static void disable_vfs(struct mlx5_core_dev *dev, int num_vfs)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int vf;
+
+ for (vf = 1; vf <= num_vfs; vf++) {
+ if (sriov->vfs_ctx[vf - 1].enabled) {
+ if (mlx5_core_disable_hca(dev, vf))
+ mlx5_core_warn(dev, "failed to disable VF %d\n", vf - 1);
+ else
+ sriov->vfs_ctx[vf - 1].enabled = 0;
+ }
+ }
+}
+
+static int mlx5_core_create_vfs(struct pci_dev *pdev, int num_vfs)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ int err;
+
+ if (pci_num_vf(pdev))
+ pci_disable_sriov(pdev);
+
+ enable_vfs(dev, num_vfs);
+
+ err = pci_enable_sriov(pdev, num_vfs);
+ if (err) {
+ dev_warn(&pdev->dev, "enable sriov failed %d\n", err);
+ goto ex;
+ }
+
+ return 0;
+
+ex:
+ disable_vfs(dev, num_vfs);
+ return err;
+}
+
+static int mlx5_core_sriov_enable(struct pci_dev *pdev, int num_vfs)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int err;
+
+ kfree(sriov->vfs_ctx);
+ sriov->vfs_ctx = kcalloc(num_vfs, sizeof(*sriov->vfs_ctx), GFP_ATOMIC);
+ if (!sriov->vfs_ctx)
+ return -ENOMEM;
+
+ sriov->enabled_vfs = num_vfs;
+ err = mlx5_core_create_vfs(pdev, num_vfs);
+ if (err) {
+ kfree(sriov->vfs_ctx);
+ sriov->vfs_ctx = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+static void mlx5_core_init_vfs(struct mlx5_core_dev *dev, int num_vfs)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+
+ sriov->num_vfs = num_vfs;
+}
+
+static void mlx5_core_cleanup_vfs(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_sriov *sriov;
+
+ sriov = &dev->priv.sriov;
+ disable_vfs(dev, sriov->num_vfs);
+
+ if (mlx5_wait_for_vf_pages(dev))
+ mlx5_core_warn(dev, "timeout claiming VFs pages\n");
+
+ sriov->num_vfs = 0;
+}
+
+int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int err;
+
+ mlx5_core_dbg(dev, "requsted num_vfs %d\n", num_vfs);
+ if (!mlx5_core_is_pf(dev))
+ return -EPERM;
+
+ mlx5_core_cleanup_vfs(dev);
+
+ if (!num_vfs) {
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_disable_sriov(dev->priv.eswitch);
+#endif
+ kfree(sriov->vfs_ctx);
+ sriov->vfs_ctx = NULL;
+ if (!pci_vfs_assigned(pdev))
+ pci_disable_sriov(pdev);
+ else
+ pr_info("unloading PF driver while leaving orphan VFs\n");
+ return 0;
+ }
+
+ err = mlx5_core_sriov_enable(pdev, num_vfs);
+ if (err) {
+ dev_warn(&pdev->dev, "mlx5_core_sriov_enable failed %d\n", err);
+ return err;
+ }
+
+ mlx5_core_init_vfs(dev, num_vfs);
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_enable_sriov(dev->priv.eswitch, num_vfs);
+#endif
+
+ return num_vfs;
+}
+
+static int sync_required(struct pci_dev *pdev)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int cur_vfs = pci_num_vf(pdev);
+
+ if (cur_vfs != sriov->num_vfs) {
+ pr_info("current VFs %d, registered %d - sync needed\n", cur_vfs, sriov->num_vfs);
+ return 1;
+ }
+
+ return 0;
+}
+
+int mlx5_sriov_init(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ struct pci_dev *pdev = dev->pdev;
+ int cur_vfs;
+
+ if (!mlx5_core_is_pf(dev))
+ return 0;
+
+ if (!sync_required(dev->pdev))
+ return 0;
+
+ cur_vfs = pci_num_vf(pdev);
+ sriov->vfs_ctx = kcalloc(cur_vfs, sizeof(*sriov->vfs_ctx), GFP_KERNEL);
+ if (!sriov->vfs_ctx)
+ return -ENOMEM;
+
+ sriov->enabled_vfs = cur_vfs;
+
+ mlx5_core_init_vfs(dev, cur_vfs);
+#ifdef CONFIG_MLX5_CORE_EN
+ if (cur_vfs)
+ mlx5_eswitch_enable_sriov(dev->priv.eswitch, cur_vfs);
+#endif
+
+ enable_vfs(dev, cur_vfs);
+
+ return 0;
+}
+
+int mlx5_sriov_cleanup(struct mlx5_core_dev *dev)
+{
+ struct pci_dev *pdev = dev->pdev;
+ int err;
+
+ if (!mlx5_core_is_pf(dev))
+ return 0;
+
+ err = mlx5_core_sriov_configure(pdev, 0);
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
index b94177ebcf3a..076197efea9b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
@@ -36,54 +36,399 @@
#include <linux/mlx5/vport.h>
#include "mlx5_core.h"
-u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod)
+static int _mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport, u32 *out, int outlen)
{
- u32 in[MLX5_ST_SZ_DW(query_vport_state_in)];
- u32 out[MLX5_ST_SZ_DW(query_vport_state_out)];
int err;
+ u32 in[MLX5_ST_SZ_DW(query_vport_state_in)];
memset(in, 0, sizeof(in));
MLX5_SET(query_vport_state_in, in, opcode,
MLX5_CMD_OP_QUERY_VPORT_STATE);
MLX5_SET(query_vport_state_in, in, op_mod, opmod);
+ MLX5_SET(query_vport_state_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_vport_state_in, in, other_vport, 1);
- err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out,
- sizeof(out));
+ err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
if (err)
mlx5_core_warn(mdev, "MLX5_CMD_OP_QUERY_VPORT_STATE failed\n");
+ return err;
+}
+
+u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
+{
+ u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0};
+
+ _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out));
+
return MLX5_GET(query_vport_state_out, out, state);
}
-EXPORT_SYMBOL(mlx5_query_vport_state);
+EXPORT_SYMBOL_GPL(mlx5_query_vport_state);
+
+u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
+{
+ u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0};
+
+ _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out));
+
+ return MLX5_GET(query_vport_state_out, out, admin_state);
+}
+EXPORT_SYMBOL(mlx5_query_vport_admin_state);
-void mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u8 *addr)
+int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport, u8 state)
+{
+ u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)];
+ u32 out[MLX5_ST_SZ_DW(modify_vport_state_out)];
+ int err;
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(modify_vport_state_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_VPORT_STATE);
+ MLX5_SET(modify_vport_state_in, in, op_mod, opmod);
+ MLX5_SET(modify_vport_state_in, in, vport_number, vport);
+
+ if (vport)
+ MLX5_SET(modify_vport_state_in, in, other_vport, 1);
+
+ MLX5_SET(modify_vport_state_in, in, admin_state, state);
+
+ err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out,
+ sizeof(out));
+ if (err)
+ mlx5_core_warn(mdev, "MLX5_CMD_OP_MODIFY_VPORT_STATE failed\n");
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_modify_vport_admin_state);
+
+static int mlx5_query_nic_vport_context(struct mlx5_core_dev *mdev, u16 vport,
+ u32 *out, int outlen)
+{
+ u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(query_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
+
+ MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
+
+ return mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
+}
+
+static int mlx5_modify_nic_vport_context(struct mlx5_core_dev *mdev, void *in,
+ int inlen)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+
+ MLX5_SET(modify_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+
+ memset(out, 0, sizeof(out));
+ return mlx5_cmd_exec_check_status(mdev, in, inlen, out, sizeof(out));
+}
+
+int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
+ u16 vport, u8 *addr)
{
- u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
u32 *out;
int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out);
u8 *out_addr;
+ int err;
out = mlx5_vzalloc(outlen);
if (!out)
- return;
+ return -ENOMEM;
out_addr = MLX5_ADDR_OF(query_nic_vport_context_out, out,
nic_vport_context.permanent_address);
+ err = mlx5_query_nic_vport_context(mdev, vport, out, outlen);
+ if (err)
+ goto out;
+
+ ether_addr_copy(addr, &out_addr[2]);
+
+out:
+ kvfree(out);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_address);
+
+int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev,
+ u16 vport, u8 *addr)
+{
+ void *in;
+ int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
+ int err;
+ void *nic_vport_ctx;
+ u8 *perm_mac;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ mlx5_core_warn(mdev, "failed to allocate inbox\n");
+ return -ENOMEM;
+ }
+
+ MLX5_SET(modify_nic_vport_context_in, in,
+ field_select.permanent_address, 1);
+ MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
+
+ if (vport)
+ MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
+
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
+ in, nic_vport_context);
+ perm_mac = MLX5_ADDR_OF(nic_vport_context, nic_vport_ctx,
+ permanent_address);
+
+ ether_addr_copy(&perm_mac[2], addr);
+
+ err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+
+ kvfree(in);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_modify_nic_vport_mac_address);
+
+int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ u32 vport,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int *list_size)
+{
+ u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
+ void *nic_vport_ctx;
+ int max_list_size;
+ int req_list_size;
+ int out_sz;
+ void *out;
+ int err;
+ int i;
+
+ req_list_size = *list_size;
+
+ max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ?
+ 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) :
+ 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list);
+
+ if (req_list_size > max_list_size) {
+ mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n",
+ req_list_size, max_list_size);
+ req_list_size = max_list_size;
+ }
+
+ out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
+
memset(in, 0, sizeof(in));
+ out = kzalloc(out_sz, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
MLX5_SET(query_nic_vport_context_in, in, opcode,
MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
+ MLX5_SET(query_nic_vport_context_in, in, allowed_list_type, list_type);
+ MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
- memset(out, 0, outlen);
- mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
+ if (vport)
+ MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
- ether_addr_copy(addr, &out_addr[2]);
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz);
+ if (err)
+ goto out;
- kvfree(out);
+ nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out,
+ nic_vport_context);
+ req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size);
+
+ *list_size = req_list_size;
+ for (i = 0; i < req_list_size; i++) {
+ u8 *mac_addr = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]) + 2;
+ ether_addr_copy(addr_list[i], mac_addr);
+ }
+out:
+ kfree(out);
+ return err;
}
-EXPORT_SYMBOL(mlx5_query_nic_vport_mac_address);
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_list);
+
+int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int list_size)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+ void *nic_vport_ctx;
+ int max_list_size;
+ int in_sz;
+ void *in;
+ int err;
+ int i;
+
+ max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ?
+ 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) :
+ 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list);
+
+ if (list_size > max_list_size)
+ return -ENOSPC;
+
+ in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
+
+ memset(out, 0, sizeof(out));
+ in = kzalloc(in_sz, GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ MLX5_SET(modify_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ field_select.addresses_list, 1);
+
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in,
+ nic_vport_context);
+
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_type, list_type);
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size, list_size);
+
+ for (i = 0; i < list_size; i++) {
+ u8 *curr_mac = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]) + 2;
+ ether_addr_copy(curr_mac, addr_list[i]);
+ }
+
+ err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out));
+ kfree(in);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mac_list);
+
+int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u32 vport,
+ u16 vlans[],
+ int *size)
+{
+ u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
+ void *nic_vport_ctx;
+ int req_list_size;
+ int max_list_size;
+ int out_sz;
+ void *out;
+ int err;
+ int i;
+
+ req_list_size = *size;
+ max_list_size = 1 << MLX5_CAP_GEN(dev, log_max_vlan_list);
+ if (req_list_size > max_list_size) {
+ mlx5_core_warn(dev, "Requested list size (%d) > (%d) max list size\n",
+ req_list_size, max_list_size);
+ req_list_size = max_list_size;
+ }
+
+ out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ req_list_size * MLX5_ST_SZ_BYTES(vlan_layout);
+
+ memset(in, 0, sizeof(in));
+ out = kzalloc(out_sz, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ MLX5_SET(query_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
+ MLX5_SET(query_nic_vport_context_in, in, allowed_list_type,
+ MLX5_NVPRT_LIST_TYPE_VLAN);
+ MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
+
+ if (vport)
+ MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
+
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz);
+ if (err)
+ goto out;
+
+ nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out,
+ nic_vport_context);
+ req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size);
+
+ *size = req_list_size;
+ for (i = 0; i < req_list_size; i++) {
+ void *vlan_addr = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]);
+ vlans[i] = MLX5_GET(vlan_layout, vlan_addr, vlan);
+ }
+out:
+ kfree(out);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_vlans);
+
+int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u16 vlans[],
+ int list_size)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+ void *nic_vport_ctx;
+ int max_list_size;
+ int in_sz;
+ void *in;
+ int err;
+ int i;
+
+ max_list_size = 1 << MLX5_CAP_GEN(dev, log_max_vlan_list);
+
+ if (list_size > max_list_size)
+ return -ENOSPC;
+
+ in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ list_size * MLX5_ST_SZ_BYTES(vlan_layout);
+
+ memset(out, 0, sizeof(out));
+ in = kzalloc(in_sz, GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ MLX5_SET(modify_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ field_select.addresses_list, 1);
+
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in,
+ nic_vport_context);
+
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_type, MLX5_NVPRT_LIST_TYPE_VLAN);
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size, list_size);
+
+ for (i = 0; i < list_size; i++) {
+ void *vlan_addr = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]);
+ MLX5_SET(vlan_layout, vlan_addr, vlan, vlans[i]);
+ }
+
+ err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out));
+ kfree(in);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_vlans);
int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport,
u8 port_num, u16 vf_num, u16 gid_index,
@@ -343,3 +688,65 @@ int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev,
return err;
}
EXPORT_SYMBOL_GPL(mlx5_query_hca_vport_node_guid);
+
+int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ u32 vport,
+ int *promisc_uc,
+ int *promisc_mc,
+ int *promisc_all)
+{
+ u32 *out;
+ int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out);
+ int err;
+
+ out = kzalloc(outlen, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ err = mlx5_query_nic_vport_context(mdev, vport, out, outlen);
+ if (err)
+ goto out;
+
+ *promisc_uc = MLX5_GET(query_nic_vport_context_out, out,
+ nic_vport_context.promisc_uc);
+ *promisc_mc = MLX5_GET(query_nic_vport_context_out, out,
+ nic_vport_context.promisc_mc);
+ *promisc_all = MLX5_GET(query_nic_vport_context_out, out,
+ nic_vport_context.promisc_all);
+
+out:
+ kfree(out);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_promisc);
+
+int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ int promisc_uc,
+ int promisc_mc,
+ int promisc_all)
+{
+ void *in;
+ int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ mlx5_core_err(mdev, "failed to allocate inbox\n");
+ return -ENOMEM;
+ }
+
+ MLX5_SET(modify_nic_vport_context_in, in, field_select.promisc, 1);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ nic_vport_context.promisc_uc, promisc_uc);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ nic_vport_context.promisc_mc, promisc_mc);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ nic_vport_context.promisc_all, promisc_all);
+
+ err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+
+ kvfree(in);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_promisc);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
index e36e12219c9b..ce26adcb4988 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
@@ -10,6 +10,14 @@ config MLXSW_CORE
To compile this driver as a module, choose M here: the
module will be called mlxsw_core.
+config MLXSW_CORE_HWMON
+ bool "HWMON support for Mellanox Technologies Switch ASICs"
+ depends on MLXSW_CORE && HWMON
+ depends on !(MLXSW_CORE=y && HWMON=m)
+ default y
+ ---help---
+ Say Y here if you want to expose HWMON interface on mlxsw devices.
+
config MLXSW_PCI
tristate "PCI bus implementation for Mellanox Technologies Switch ASICs"
depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE
@@ -33,7 +41,7 @@ config MLXSW_SWITCHX2
config MLXSW_SPECTRUM
tristate "Mellanox Technologies Spectrum support"
- depends on MLXSW_CORE && NET_SWITCHDEV
+ depends on MLXSW_CORE && NET_SWITCHDEV && VLAN_8021Q
default m
---help---
This driver supports Mellanox Technologies Spectrum Ethernet
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile
index af015818fd19..584cac444852 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Makefile
+++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o
mlxsw_core-objs := core.o
+mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o
obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o
mlxsw_pci-objs := pci.o
obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index 97f0d93caf99..22379eb8e924 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -105,6 +105,10 @@ struct mlxsw_core {
struct debugfs_blob_wrapper vsd_blob;
struct debugfs_blob_wrapper psid_blob;
} dbg;
+ struct {
+ u8 *mapping; /* lag_id+port_index to local_port mapping */
+ } lag;
+ struct mlxsw_hwmon *hwmon;
unsigned long driver_priv[0];
/* driver_priv has to be always the last item */
};
@@ -814,6 +818,17 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
goto err_alloc_stats;
}
+ if (mlxsw_driver->profile->used_max_lag &&
+ mlxsw_driver->profile->used_max_port_per_lag) {
+ alloc_size = sizeof(u8) * mlxsw_driver->profile->max_lag *
+ mlxsw_driver->profile->max_port_per_lag;
+ mlxsw_core->lag.mapping = kzalloc(alloc_size, GFP_KERNEL);
+ if (!mlxsw_core->lag.mapping) {
+ err = -ENOMEM;
+ goto err_alloc_lag_mapping;
+ }
+ }
+
err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile);
if (err)
goto err_bus_init;
@@ -822,6 +837,10 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
if (err)
goto err_emad_init;
+ err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
+ if (err)
+ goto err_hwmon_init;
+
err = mlxsw_driver->init(mlxsw_core->driver_priv, mlxsw_core,
mlxsw_bus_info);
if (err)
@@ -836,10 +855,13 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
err_debugfs_init:
mlxsw_core->driver->fini(mlxsw_core->driver_priv);
err_driver_init:
+err_hwmon_init:
mlxsw_emad_fini(mlxsw_core);
err_emad_init:
mlxsw_bus->fini(bus_priv);
err_bus_init:
+ kfree(mlxsw_core->lag.mapping);
+err_alloc_lag_mapping:
free_percpu(mlxsw_core->pcpu_stats);
err_alloc_stats:
kfree(mlxsw_core);
@@ -857,6 +879,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core)
mlxsw_core->driver->fini(mlxsw_core->driver_priv);
mlxsw_emad_fini(mlxsw_core);
mlxsw_core->bus->fini(mlxsw_core->bus_priv);
+ kfree(mlxsw_core->lag.mapping);
free_percpu(mlxsw_core->pcpu_stats);
kfree(mlxsw_core);
mlxsw_core_driver_put(device_kind);
@@ -1188,11 +1211,25 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
struct mlxsw_rx_listener_item *rxl_item;
const struct mlxsw_rx_listener *rxl;
struct mlxsw_core_pcpu_stats *pcpu_stats;
- u8 local_port = rx_info->sys_port;
+ u8 local_port;
bool found = false;
- dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: sys_port = %d, trap_id = 0x%x\n",
- __func__, rx_info->sys_port, rx_info->trap_id);
+ if (rx_info->is_lag) {
+ dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: lag_id = %d, lag_port_index = 0x%x\n",
+ __func__, rx_info->u.lag_id,
+ rx_info->trap_id);
+ /* Upper layer does not care if the skb came from LAG or not,
+ * so just get the local_port for the lag port and push it up.
+ */
+ local_port = mlxsw_core_lag_mapping_get(mlxsw_core,
+ rx_info->u.lag_id,
+ rx_info->lag_port_index);
+ } else {
+ local_port = rx_info->u.sys_port;
+ }
+
+ dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: local_port = %d, trap_id = 0x%x\n",
+ __func__, local_port, rx_info->trap_id);
if ((rx_info->trap_id >= MLXSW_TRAP_ID_MAX) ||
(local_port >= MLXSW_PORT_MAX_PORTS))
@@ -1236,6 +1273,48 @@ drop:
}
EXPORT_SYMBOL(mlxsw_core_skb_receive);
+static int mlxsw_core_lag_mapping_index(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index)
+{
+ return mlxsw_core->driver->profile->max_port_per_lag * lag_id +
+ port_index;
+}
+
+void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index, u8 local_port)
+{
+ int index = mlxsw_core_lag_mapping_index(mlxsw_core,
+ lag_id, port_index);
+
+ mlxsw_core->lag.mapping[index] = local_port;
+}
+EXPORT_SYMBOL(mlxsw_core_lag_mapping_set);
+
+u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index)
+{
+ int index = mlxsw_core_lag_mapping_index(mlxsw_core,
+ lag_id, port_index);
+
+ return mlxsw_core->lag.mapping[index];
+}
+EXPORT_SYMBOL(mlxsw_core_lag_mapping_get);
+
+void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 local_port)
+{
+ int i;
+
+ for (i = 0; i < mlxsw_core->driver->profile->max_port_per_lag; i++) {
+ int index = mlxsw_core_lag_mapping_index(mlxsw_core,
+ lag_id, i);
+
+ if (mlxsw_core->lag.mapping[index] == local_port)
+ mlxsw_core->lag.mapping[index] = 0;
+ }
+}
+EXPORT_SYMBOL(mlxsw_core_lag_mapping_clear);
+
int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod,
u32 in_mod, bool out_mbox_direct,
char *in_mbox, size_t in_mbox_size,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h
index 807827350a89..a01723600f0a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.h
@@ -112,13 +112,25 @@ int mlxsw_reg_write(struct mlxsw_core *mlxsw_core,
const struct mlxsw_reg_info *reg, char *payload);
struct mlxsw_rx_info {
- u16 sys_port;
+ bool is_lag;
+ union {
+ u16 sys_port;
+ u16 lag_id;
+ } u;
+ u8 lag_port_index;
int trap_id;
};
void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
struct mlxsw_rx_info *rx_info);
+void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index, u8 local_port);
+u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index);
+void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 local_port);
+
#define MLXSW_CONFIG_PROFILE_SWID_COUNT 8
struct mlxsw_swid_config {
@@ -209,4 +221,24 @@ struct mlxsw_bus_info {
u8 psid[MLXSW_CMD_BOARDINFO_PSID_LEN];
};
+struct mlxsw_hwmon;
+
+#ifdef CONFIG_MLXSW_CORE_HWMON
+
+int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
+ const struct mlxsw_bus_info *mlxsw_bus_info,
+ struct mlxsw_hwmon **p_hwmon);
+void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon);
+
+#else
+
+static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
+ const struct mlxsw_bus_info *mlxsw_bus_info,
+ struct mlxsw_hwmon **p_hwmon)
+{
+ return 0;
+}
+
+#endif
+
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
new file mode 100644
index 000000000000..1ac8bf187168
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
@@ -0,0 +1,372 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
+ * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+
+#include "core.h"
+
+#define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127
+#define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \
+ MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX)
+
+struct mlxsw_hwmon_attr {
+ struct device_attribute dev_attr;
+ struct mlxsw_hwmon *hwmon;
+ unsigned int type_index;
+ char name[32];
+};
+
+struct mlxsw_hwmon {
+ struct mlxsw_core *core;
+ const struct mlxsw_bus_info *bus_info;
+ struct device *hwmon_dev;
+ struct attribute_group group;
+ const struct attribute_group *groups[2];
+ struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
+ struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
+ unsigned int attrs_count;
+};
+
+static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ unsigned int temp;
+ int err;
+
+ mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
+ false, false);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
+ return err;
+ }
+ mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);
+ return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ unsigned int temp_max;
+ int err;
+
+ mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
+ false, false);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
+ return err;
+ }
+ mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL);
+ return sprintf(buf, "%u\n", temp_max);
+}
+
+static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val != 1)
+ return -EINVAL;
+
+ mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, true, true);
+ err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n");
+ return err;
+ }
+ return len;
+}
+
+static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mfsm_pl[MLXSW_REG_MFSM_LEN];
+ int err;
+
+ mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
+ return err;
+ }
+ return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));
+}
+
+static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mfsc_pl[MLXSW_REG_MFSC_LEN];
+ int err;
+
+ mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n");
+ return err;
+ }
+ return sprintf(buf, "%u\n",
+ mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl));
+}
+
+static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mfsc_pl[MLXSW_REG_MFSC_LEN];
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val > 255)
+ return -EINVAL;
+
+ mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val);
+ err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n");
+ return err;
+ }
+ return len;
+}
+
+enum mlxsw_hwmon_attr_type {
+ MLXSW_HWMON_ATTR_TYPE_TEMP,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_RST,
+ MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
+ MLXSW_HWMON_ATTR_TYPE_PWM,
+};
+
+static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
+ enum mlxsw_hwmon_attr_type attr_type,
+ unsigned int type_index, unsigned int num) {
+ struct mlxsw_hwmon_attr *mlxsw_hwmon_attr;
+ unsigned int attr_index;
+
+ attr_index = mlxsw_hwmon->attrs_count;
+ mlxsw_hwmon_attr = &mlxsw_hwmon->hwmon_attrs[attr_index];
+
+ switch (attr_type) {
+ case MLXSW_HWMON_ATTR_TYPE_TEMP:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "temp%u_input", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "temp%u_highest", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_TEMP_RST:
+ mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "temp%u_reset_history", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_FAN_RPM:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "fan%u_input", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_PWM:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
+ mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR | S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "pwm%u", num + 1);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ mlxsw_hwmon_attr->type_index = type_index;
+ mlxsw_hwmon_attr->hwmon = mlxsw_hwmon;
+ mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name;
+ sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr);
+
+ mlxsw_hwmon->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr;
+ mlxsw_hwmon->attrs_count++;
+}
+
+static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+ char mtcap_pl[MLXSW_REG_MTCAP_LEN];
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ u8 sensor_count;
+ int i;
+ int err;
+
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
+ return err;
+ }
+ sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
+ for (i = 0; i < sensor_count; i++) {
+ mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true);
+ err = mlxsw_reg_write(mlxsw_hwmon->core,
+ MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",
+ i);
+ return err;
+ }
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_TEMP, i, i);
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i);
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i);
+ }
+ return 0;
+}
+
+static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+ char mfcr_pl[MLXSW_REG_MFCR_LEN];
+ enum mlxsw_reg_mfcr_pwm_frequency freq;
+ unsigned int type_index;
+ unsigned int num;
+ u16 tacho_active;
+ u8 pwm_active;
+ int err;
+
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n");
+ return err;
+ }
+ mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
+ num = 0;
+ for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {
+ if (tacho_active & BIT(type_index))
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
+ type_index, num++);
+ }
+ num = 0;
+ for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {
+ if (pwm_active & BIT(type_index))
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_PWM,
+ type_index, num++);
+ }
+ return 0;
+}
+
+int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
+ const struct mlxsw_bus_info *mlxsw_bus_info,
+ struct mlxsw_hwmon **p_hwmon)
+{
+ struct mlxsw_hwmon *mlxsw_hwmon;
+ struct device *hwmon_dev;
+ int err;
+
+ mlxsw_hwmon = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_hwmon),
+ GFP_KERNEL);
+ if (!mlxsw_hwmon)
+ return -ENOMEM;
+ mlxsw_hwmon->core = mlxsw_core;
+ mlxsw_hwmon->bus_info = mlxsw_bus_info;
+
+ err = mlxsw_hwmon_temp_init(mlxsw_hwmon);
+ if (err)
+ goto err_temp_init;
+
+ err = mlxsw_hwmon_fans_init(mlxsw_hwmon);
+ if (err)
+ goto err_fans_init;
+
+ mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
+ mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(mlxsw_bus_info->dev,
+ "mlxsw",
+ mlxsw_hwmon,
+ mlxsw_hwmon->groups);
+ if (IS_ERR(hwmon_dev)) {
+ err = PTR_ERR(hwmon_dev);
+ goto err_hwmon_register;
+ }
+
+ mlxsw_hwmon->hwmon_dev = hwmon_dev;
+ *p_hwmon = mlxsw_hwmon;
+ return 0;
+
+err_hwmon_register:
+err_fans_init:
+err_temp_init:
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c
index de69e719dc9d..c071077aafbd 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c
@@ -384,7 +384,7 @@ static int mlxsw_pci_sdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
/* Set CQ of same number of this SDQ. */
mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, q->num);
- mlxsw_cmd_mbox_sw2hw_dq_sdq_tclass_set(mbox, 7);
+ mlxsw_cmd_mbox_sw2hw_dq_sdq_tclass_set(mbox, 3);
mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */
for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) {
dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i);
@@ -686,11 +686,15 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
if (q->consumer_counter++ != consumer_counter_limit)
dev_dbg_ratelimited(&pdev->dev, "Consumer counter does not match limit in RDQ\n");
- /* We do not support lag now */
- if (mlxsw_pci_cqe_lag_get(cqe))
- goto drop;
+ if (mlxsw_pci_cqe_lag_get(cqe)) {
+ rx_info.is_lag = true;
+ rx_info.u.lag_id = mlxsw_pci_cqe_lag_id_get(cqe);
+ rx_info.lag_port_index = mlxsw_pci_cqe_lag_port_index_get(cqe);
+ } else {
+ rx_info.is_lag = false;
+ rx_info.u.sys_port = mlxsw_pci_cqe_system_port_get(cqe);
+ }
- rx_info.sys_port = mlxsw_pci_cqe_system_port_get(cqe);
rx_info.trap_id = mlxsw_pci_cqe_trap_id_get(cqe);
byte_count = mlxsw_pci_cqe_byte_count_get(cqe);
@@ -699,7 +703,6 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
skb_put(skb, byte_count);
mlxsw_core_skb_receive(mlxsw_pci->core, skb, &rx_info);
-put_new_skb:
memset(wqe, 0, q->elem_size);
err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info);
if (err)
@@ -708,10 +711,6 @@ put_new_skb:
q->producer_counter++;
mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
return;
-
-drop:
- dev_kfree_skb_any(skb);
- goto put_new_skb;
}
static char *mlxsw_pci_cq_sw_cqe_get(struct mlxsw_pci_queue *q)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h
index 142f33d978c5..912106054ff2 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h
@@ -129,13 +129,15 @@ MLXSW_ITEM64_INDEXED(pci, wqe, address, 0x08, 0, 64, 0x8, 0x0, false);
*/
MLXSW_ITEM32(pci, cqe, lag, 0x00, 23, 1);
-/* pci_cqe_system_port
+/* pci_cqe_system_port/lag_id
* When lag=0: System port on which the packet was received
* When lag=1:
* bits [15:4] LAG ID on which the packet was received
* bits [3:0] sub_port on which the packet was received
*/
MLXSW_ITEM32(pci, cqe, system_port, 0x00, 0, 16);
+MLXSW_ITEM32(pci, cqe, lag_id, 0x00, 4, 12);
+MLXSW_ITEM32(pci, cqe, lag_port_index, 0x00, 0, 4);
/* pci_cqe_wqe_counter
* WQE count of the WQEs completed on the associated dqn
diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index 236fb5d2ad69..0c5237264e3e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -99,6 +99,55 @@ static const struct mlxsw_reg_info mlxsw_reg_spad = {
*/
MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6);
+/* SMID - Switch Multicast ID
+ * --------------------------
+ * The MID record maps from a MID (Multicast ID), which is a unique identifier
+ * of the multicast group within the stacking domain, into a list of local
+ * ports into which the packet is replicated.
+ */
+#define MLXSW_REG_SMID_ID 0x2007
+#define MLXSW_REG_SMID_LEN 0x240
+
+static const struct mlxsw_reg_info mlxsw_reg_smid = {
+ .id = MLXSW_REG_SMID_ID,
+ .len = MLXSW_REG_SMID_LEN,
+};
+
+/* reg_smid_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, smid, swid, 0x00, 24, 8);
+
+/* reg_smid_mid
+ * Multicast identifier - global identifier that represents the multicast group
+ * across all devices.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, smid, mid, 0x00, 0, 16);
+
+/* reg_smid_port
+ * Local port memebership (1 bit per port).
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, smid, port, 0x20, 0x20, 1);
+
+/* reg_smid_port_mask
+ * Local port mask (1 bit per port).
+ * Access: W
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, smid, port_mask, 0x220, 0x20, 1);
+
+static inline void mlxsw_reg_smid_pack(char *payload, u16 mid,
+ u8 port, bool set)
+{
+ MLXSW_REG_ZERO(smid, payload);
+ mlxsw_reg_smid_swid_set(payload, 0);
+ mlxsw_reg_smid_mid_set(payload, mid);
+ mlxsw_reg_smid_port_set(payload, port, set);
+ mlxsw_reg_smid_port_mask_set(payload, port, 1);
+}
+
/* SSPR - Switch System Port Record Register
* -----------------------------------------
* Configures the system port to local port mapping.
@@ -286,6 +335,8 @@ MLXSW_ITEM32_INDEXED(reg, sfd, rec_swid, MLXSW_REG_SFD_BASE_LEN, 24, 8,
enum mlxsw_reg_sfd_rec_type {
MLXSW_REG_SFD_REC_TYPE_UNICAST = 0x0,
+ MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG = 0x1,
+ MLXSW_REG_SFD_REC_TYPE_MULTICAST = 0x2,
};
/* reg_sfd_rec_type
@@ -376,36 +427,144 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
MLXSW_ITEM32_INDEXED(reg, sfd, uc_system_port, MLXSW_REG_SFD_BASE_LEN, 0, 16,
MLXSW_REG_SFD_REC_LEN, 0x0C, false);
-static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index,
- enum mlxsw_reg_sfd_rec_policy policy,
- const char *mac, u16 vid,
- enum mlxsw_reg_sfd_rec_action action,
- u8 local_port)
+static inline void mlxsw_reg_sfd_rec_pack(char *payload, int rec_index,
+ enum mlxsw_reg_sfd_rec_type rec_type,
+ const char *mac,
+ enum mlxsw_reg_sfd_rec_action action)
{
u8 num_rec = mlxsw_reg_sfd_num_rec_get(payload);
if (rec_index >= num_rec)
mlxsw_reg_sfd_num_rec_set(payload, rec_index + 1);
mlxsw_reg_sfd_rec_swid_set(payload, rec_index, 0);
- mlxsw_reg_sfd_rec_type_set(payload, rec_index,
- MLXSW_REG_SFD_REC_TYPE_UNICAST);
- mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
+ mlxsw_reg_sfd_rec_type_set(payload, rec_index, rec_type);
mlxsw_reg_sfd_rec_mac_memcpy_to(payload, rec_index, mac);
- mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0);
- mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid);
mlxsw_reg_sfd_rec_action_set(payload, rec_index, action);
+}
+
+static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index,
+ enum mlxsw_reg_sfd_rec_policy policy,
+ const char *mac, u16 fid_vid,
+ enum mlxsw_reg_sfd_rec_action action,
+ u8 local_port)
+{
+ mlxsw_reg_sfd_rec_pack(payload, rec_index,
+ MLXSW_REG_SFD_REC_TYPE_UNICAST, mac, action);
+ mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
+ mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0);
+ mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, fid_vid);
mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port);
}
static inline void mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index,
- char *mac, u16 *p_vid,
+ char *mac, u16 *p_fid_vid,
u8 *p_local_port)
{
mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac);
- *p_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index);
+ *p_fid_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index);
*p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index);
}
+/* reg_sfd_uc_lag_sub_port
+ * LAG sub port.
+ * Must be 0 if multichannel VEPA is not enabled.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_uc_lag_fid_vid
+ * Filtering ID or VLAN ID
+ * For SwitchX and SwitchX-2:
+ * - Dynamic entries (policy 2,3) use FID
+ * - Static entries (policy 0) use VID
+ * - When independent learning is configured, VID=FID
+ * For Spectrum: use FID for both Dynamic and Static entries.
+ * VID should not be used.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_uc_lag_lag_vid
+ * Indicates VID in case of vFIDs. Reserved for FIDs.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_vid, MLXSW_REG_SFD_BASE_LEN, 16, 12,
+ MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+/* reg_sfd_uc_lag_lag_id
+ * LAG Identifier - pointer into the LAG descriptor table.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_id, MLXSW_REG_SFD_BASE_LEN, 0, 10,
+ MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+static inline void
+mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index,
+ enum mlxsw_reg_sfd_rec_policy policy,
+ const char *mac, u16 fid_vid,
+ enum mlxsw_reg_sfd_rec_action action, u16 lag_vid,
+ u16 lag_id)
+{
+ mlxsw_reg_sfd_rec_pack(payload, rec_index,
+ MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG,
+ mac, action);
+ mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
+ mlxsw_reg_sfd_uc_lag_sub_port_set(payload, rec_index, 0);
+ mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, fid_vid);
+ mlxsw_reg_sfd_uc_lag_lag_vid_set(payload, rec_index, lag_vid);
+ mlxsw_reg_sfd_uc_lag_lag_id_set(payload, rec_index, lag_id);
+}
+
+static inline void mlxsw_reg_sfd_uc_lag_unpack(char *payload, int rec_index,
+ char *mac, u16 *p_vid,
+ u16 *p_lag_id)
+{
+ mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac);
+ *p_vid = mlxsw_reg_sfd_uc_lag_fid_vid_get(payload, rec_index);
+ *p_lag_id = mlxsw_reg_sfd_uc_lag_lag_id_get(payload, rec_index);
+}
+
+/* reg_sfd_mc_pgi
+ *
+ * Multicast port group index - index into the port group table.
+ * Value 0x1FFF indicates the pgi should point to the MID entry.
+ * For Spectrum this value must be set to 0x1FFF
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, mc_pgi, MLXSW_REG_SFD_BASE_LEN, 16, 13,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_mc_fid_vid
+ *
+ * Filtering ID or VLAN ID
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, mc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_mc_mid
+ *
+ * Multicast identifier - global identifier that represents the multicast
+ * group across all devices.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, mc_mid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+ MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+static inline void
+mlxsw_reg_sfd_mc_pack(char *payload, int rec_index,
+ const char *mac, u16 fid_vid,
+ enum mlxsw_reg_sfd_rec_action action, u16 mid)
+{
+ mlxsw_reg_sfd_rec_pack(payload, rec_index,
+ MLXSW_REG_SFD_REC_TYPE_MULTICAST, mac, action);
+ mlxsw_reg_sfd_mc_pgi_set(payload, rec_index, 0x1FFF);
+ mlxsw_reg_sfd_mc_fid_vid_set(payload, rec_index, fid_vid);
+ mlxsw_reg_sfd_mc_mid_set(payload, rec_index, mid);
+}
+
/* SFN - Switch FDB Notification Register
* -------------------------------------------
* The switch provides notifications on newly learned FDB entries and
@@ -456,8 +615,12 @@ MLXSW_ITEM32_INDEXED(reg, sfn, rec_swid, MLXSW_REG_SFN_BASE_LEN, 24, 8,
enum mlxsw_reg_sfn_rec_type {
/* MAC addresses learned on a regular port. */
MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC = 0x5,
- /* Aged-out MAC address on a regular port */
+ /* MAC addresses learned on a LAG port. */
+ MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG = 0x6,
+ /* Aged-out MAC address on a regular port. */
MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7,
+ /* Aged-out MAC address on a LAG port. */
+ MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG = 0x8,
};
/* reg_sfn_rec_type
@@ -505,6 +668,22 @@ static inline void mlxsw_reg_sfn_mac_unpack(char *payload, int rec_index,
*p_local_port = mlxsw_reg_sfn_mac_system_port_get(payload, rec_index);
}
+/* reg_sfn_mac_lag_lag_id
+ * LAG ID (pointer into the LAG descriptor table).
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, mac_lag_lag_id, MLXSW_REG_SFN_BASE_LEN, 0, 10,
+ MLXSW_REG_SFN_REC_LEN, 0x0C, false);
+
+static inline void mlxsw_reg_sfn_mac_lag_unpack(char *payload, int rec_index,
+ char *mac, u16 *p_vid,
+ u16 *p_lag_id)
+{
+ mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac);
+ *p_vid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index);
+ *p_lag_id = mlxsw_reg_sfn_mac_lag_lag_id_get(payload, rec_index);
+}
+
/* SPMS - Switch Port MSTP/RSTP State Register
* -------------------------------------------
* Configures the spanning tree state of a physical port.
@@ -865,6 +1044,293 @@ static inline void mlxsw_reg_sftr_pack(char *payload,
mlxsw_reg_sftr_port_mask_set(payload, port, 1);
}
+/* SLDR - Switch LAG Descriptor Register
+ * -----------------------------------------
+ * The switch LAG descriptor register is populated by LAG descriptors.
+ * Each LAG descriptor is indexed by lag_id. The LAG ID runs from 0 to
+ * max_lag-1.
+ */
+#define MLXSW_REG_SLDR_ID 0x2014
+#define MLXSW_REG_SLDR_LEN 0x0C /* counting in only one port in list */
+
+static const struct mlxsw_reg_info mlxsw_reg_sldr = {
+ .id = MLXSW_REG_SLDR_ID,
+ .len = MLXSW_REG_SLDR_LEN,
+};
+
+enum mlxsw_reg_sldr_op {
+ /* Indicates a creation of a new LAG-ID, lag_id must be valid */
+ MLXSW_REG_SLDR_OP_LAG_CREATE,
+ MLXSW_REG_SLDR_OP_LAG_DESTROY,
+ /* Ports that appear in the list have the Distributor enabled */
+ MLXSW_REG_SLDR_OP_LAG_ADD_PORT_LIST,
+ /* Removes ports from the disributor list */
+ MLXSW_REG_SLDR_OP_LAG_REMOVE_PORT_LIST,
+};
+
+/* reg_sldr_op
+ * Operation.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sldr, op, 0x00, 29, 3);
+
+/* reg_sldr_lag_id
+ * LAG identifier. The lag_id is the index into the LAG descriptor table.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sldr, lag_id, 0x00, 0, 10);
+
+static inline void mlxsw_reg_sldr_lag_create_pack(char *payload, u8 lag_id)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_CREATE);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+}
+
+static inline void mlxsw_reg_sldr_lag_destroy_pack(char *payload, u8 lag_id)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_DESTROY);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+}
+
+/* reg_sldr_num_ports
+ * The number of member ports of the LAG.
+ * Reserved for Create / Destroy operations
+ * For Add / Remove operations - indicates the number of ports in the list.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sldr, num_ports, 0x04, 24, 8);
+
+/* reg_sldr_system_port
+ * System port.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sldr, system_port, 0x08, 0, 16, 4, 0, false);
+
+static inline void mlxsw_reg_sldr_lag_add_port_pack(char *payload, u8 lag_id,
+ u8 local_port)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_ADD_PORT_LIST);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+ mlxsw_reg_sldr_num_ports_set(payload, 1);
+ mlxsw_reg_sldr_system_port_set(payload, 0, local_port);
+}
+
+static inline void mlxsw_reg_sldr_lag_remove_port_pack(char *payload, u8 lag_id,
+ u8 local_port)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_REMOVE_PORT_LIST);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+ mlxsw_reg_sldr_num_ports_set(payload, 1);
+ mlxsw_reg_sldr_system_port_set(payload, 0, local_port);
+}
+
+/* SLCR - Switch LAG Configuration 2 Register
+ * -------------------------------------------
+ * The Switch LAG Configuration register is used for configuring the
+ * LAG properties of the switch.
+ */
+#define MLXSW_REG_SLCR_ID 0x2015
+#define MLXSW_REG_SLCR_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_slcr = {
+ .id = MLXSW_REG_SLCR_ID,
+ .len = MLXSW_REG_SLCR_LEN,
+};
+
+enum mlxsw_reg_slcr_pp {
+ /* Global Configuration (for all ports) */
+ MLXSW_REG_SLCR_PP_GLOBAL,
+ /* Per port configuration, based on local_port field */
+ MLXSW_REG_SLCR_PP_PER_PORT,
+};
+
+/* reg_slcr_pp
+ * Per Port Configuration
+ * Note: Reading at Global mode results in reading port 1 configuration.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcr, pp, 0x00, 24, 1);
+
+/* reg_slcr_local_port
+ * Local port number
+ * Supported from CPU port
+ * Not supported from router port
+ * Reserved when pp = Global Configuration
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcr, local_port, 0x00, 16, 8);
+
+enum mlxsw_reg_slcr_type {
+ MLXSW_REG_SLCR_TYPE_CRC, /* default */
+ MLXSW_REG_SLCR_TYPE_XOR,
+ MLXSW_REG_SLCR_TYPE_RANDOM,
+};
+
+/* reg_slcr_type
+ * Hash type
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcr, type, 0x00, 0, 4);
+
+/* Ingress port */
+#define MLXSW_REG_SLCR_LAG_HASH_IN_PORT BIT(0)
+/* SMAC - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_SMAC_IP BIT(1)
+/* SMAC - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_SMAC_NONIP BIT(2)
+#define MLXSW_REG_SLCR_LAG_HASH_SMAC \
+ (MLXSW_REG_SLCR_LAG_HASH_SMAC_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_SMAC_NONIP)
+/* DMAC - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_DMAC_IP BIT(3)
+/* DMAC - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_DMAC_NONIP BIT(4)
+#define MLXSW_REG_SLCR_LAG_HASH_DMAC \
+ (MLXSW_REG_SLCR_LAG_HASH_DMAC_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_DMAC_NONIP)
+/* Ethertype - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_IP BIT(5)
+/* Ethertype - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_NONIP BIT(6)
+#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE \
+ (MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_NONIP)
+/* VLAN ID - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_VLANID_IP BIT(7)
+/* VLAN ID - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_VLANID_NONIP BIT(8)
+#define MLXSW_REG_SLCR_LAG_HASH_VLANID \
+ (MLXSW_REG_SLCR_LAG_HASH_VLANID_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_VLANID_NONIP)
+/* Source IP address (can be IPv4 or IPv6) */
+#define MLXSW_REG_SLCR_LAG_HASH_SIP BIT(9)
+/* Destination IP address (can be IPv4 or IPv6) */
+#define MLXSW_REG_SLCR_LAG_HASH_DIP BIT(10)
+/* TCP/UDP source port */
+#define MLXSW_REG_SLCR_LAG_HASH_SPORT BIT(11)
+/* TCP/UDP destination port*/
+#define MLXSW_REG_SLCR_LAG_HASH_DPORT BIT(12)
+/* IPv4 Protocol/IPv6 Next Header */
+#define MLXSW_REG_SLCR_LAG_HASH_IPPROTO BIT(13)
+/* IPv6 Flow label */
+#define MLXSW_REG_SLCR_LAG_HASH_FLOWLABEL BIT(14)
+/* SID - FCoE source ID */
+#define MLXSW_REG_SLCR_LAG_HASH_FCOE_SID BIT(15)
+/* DID - FCoE destination ID */
+#define MLXSW_REG_SLCR_LAG_HASH_FCOE_DID BIT(16)
+/* OXID - FCoE originator exchange ID */
+#define MLXSW_REG_SLCR_LAG_HASH_FCOE_OXID BIT(17)
+/* Destination QP number - for RoCE packets */
+#define MLXSW_REG_SLCR_LAG_HASH_ROCE_DQP BIT(19)
+
+/* reg_slcr_lag_hash
+ * LAG hashing configuration. This is a bitmask, in which each set
+ * bit includes the corresponding item in the LAG hash calculation.
+ * The default lag_hash contains SMAC, DMAC, VLANID and
+ * Ethertype (for all packet types).
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcr, lag_hash, 0x04, 0, 20);
+
+static inline void mlxsw_reg_slcr_pack(char *payload, u16 lag_hash)
+{
+ MLXSW_REG_ZERO(slcr, payload);
+ mlxsw_reg_slcr_pp_set(payload, MLXSW_REG_SLCR_PP_GLOBAL);
+ mlxsw_reg_slcr_type_set(payload, MLXSW_REG_SLCR_TYPE_XOR);
+ mlxsw_reg_slcr_lag_hash_set(payload, lag_hash);
+}
+
+/* SLCOR - Switch LAG Collector Register
+ * -------------------------------------
+ * The Switch LAG Collector register controls the Local Port membership
+ * in a LAG and enablement of the collector.
+ */
+#define MLXSW_REG_SLCOR_ID 0x2016
+#define MLXSW_REG_SLCOR_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_slcor = {
+ .id = MLXSW_REG_SLCOR_ID,
+ .len = MLXSW_REG_SLCOR_LEN,
+};
+
+enum mlxsw_reg_slcor_col {
+ /* Port is added with collector disabled */
+ MLXSW_REG_SLCOR_COL_LAG_ADD_PORT,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_DISABLED,
+ MLXSW_REG_SLCOR_COL_LAG_REMOVE_PORT,
+};
+
+/* reg_slcor_col
+ * Collector configuration
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcor, col, 0x00, 30, 2);
+
+/* reg_slcor_local_port
+ * Local port number
+ * Not supported for CPU port
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcor, local_port, 0x00, 16, 8);
+
+/* reg_slcor_lag_id
+ * LAG Identifier. Index into the LAG descriptor table.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcor, lag_id, 0x00, 0, 10);
+
+/* reg_slcor_port_index
+ * Port index in the LAG list. Only valid on Add Port to LAG col.
+ * Valid range is from 0 to cap_max_lag_members-1
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcor, port_index, 0x04, 0, 10);
+
+static inline void mlxsw_reg_slcor_pack(char *payload,
+ u8 local_port, u16 lag_id,
+ enum mlxsw_reg_slcor_col col)
+{
+ MLXSW_REG_ZERO(slcor, payload);
+ mlxsw_reg_slcor_col_set(payload, col);
+ mlxsw_reg_slcor_local_port_set(payload, local_port);
+ mlxsw_reg_slcor_lag_id_set(payload, lag_id);
+}
+
+static inline void mlxsw_reg_slcor_port_add_pack(char *payload,
+ u8 local_port, u16 lag_id,
+ u8 port_index)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_ADD_PORT);
+ mlxsw_reg_slcor_port_index_set(payload, port_index);
+}
+
+static inline void mlxsw_reg_slcor_port_remove_pack(char *payload,
+ u8 local_port, u16 lag_id)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_REMOVE_PORT);
+}
+
+static inline void mlxsw_reg_slcor_col_enable_pack(char *payload,
+ u8 local_port, u16 lag_id)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED);
+}
+
+static inline void mlxsw_reg_slcor_col_disable_pack(char *payload,
+ u8 local_port, u16 lag_id)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED);
+}
+
/* SPMLR - Switch Port MAC Learning Register
* -----------------------------------------
* Controls the Switch MAC learning policy per port.
@@ -2087,6 +2553,284 @@ static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id)
mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT);
}
+/* MFCR - Management Fan Control Register
+ * --------------------------------------
+ * This register controls the settings of the Fan Speed PWM mechanism.
+ */
+#define MLXSW_REG_MFCR_ID 0x9001
+#define MLXSW_REG_MFCR_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mfcr = {
+ .id = MLXSW_REG_MFCR_ID,
+ .len = MLXSW_REG_MFCR_LEN,
+};
+
+enum mlxsw_reg_mfcr_pwm_frequency {
+ MLXSW_REG_MFCR_PWM_FEQ_11HZ = 0x00,
+ MLXSW_REG_MFCR_PWM_FEQ_14_7HZ = 0x01,
+ MLXSW_REG_MFCR_PWM_FEQ_22_1HZ = 0x02,
+ MLXSW_REG_MFCR_PWM_FEQ_1_4KHZ = 0x40,
+ MLXSW_REG_MFCR_PWM_FEQ_5KHZ = 0x41,
+ MLXSW_REG_MFCR_PWM_FEQ_20KHZ = 0x42,
+ MLXSW_REG_MFCR_PWM_FEQ_22_5KHZ = 0x43,
+ MLXSW_REG_MFCR_PWM_FEQ_25KHZ = 0x44,
+};
+
+/* reg_mfcr_pwm_frequency
+ * Controls the frequency of the PWM signal.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mfcr, pwm_frequency, 0x00, 0, 6);
+
+#define MLXSW_MFCR_TACHOS_MAX 10
+
+/* reg_mfcr_tacho_active
+ * Indicates which of the tachometer is active (bit per tachometer).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mfcr, tacho_active, 0x04, 16, MLXSW_MFCR_TACHOS_MAX);
+
+#define MLXSW_MFCR_PWMS_MAX 5
+
+/* reg_mfcr_pwm_active
+ * Indicates which of the PWM control is active (bit per PWM).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mfcr, pwm_active, 0x04, 0, MLXSW_MFCR_PWMS_MAX);
+
+static inline void
+mlxsw_reg_mfcr_pack(char *payload,
+ enum mlxsw_reg_mfcr_pwm_frequency pwm_frequency)
+{
+ MLXSW_REG_ZERO(mfcr, payload);
+ mlxsw_reg_mfcr_pwm_frequency_set(payload, pwm_frequency);
+}
+
+static inline void
+mlxsw_reg_mfcr_unpack(char *payload,
+ enum mlxsw_reg_mfcr_pwm_frequency *p_pwm_frequency,
+ u16 *p_tacho_active, u8 *p_pwm_active)
+{
+ *p_pwm_frequency = mlxsw_reg_mfcr_pwm_frequency_get(payload);
+ *p_tacho_active = mlxsw_reg_mfcr_tacho_active_get(payload);
+ *p_pwm_active = mlxsw_reg_mfcr_pwm_active_get(payload);
+}
+
+/* MFSC - Management Fan Speed Control Register
+ * --------------------------------------------
+ * This register controls the settings of the Fan Speed PWM mechanism.
+ */
+#define MLXSW_REG_MFSC_ID 0x9002
+#define MLXSW_REG_MFSC_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mfsc = {
+ .id = MLXSW_REG_MFSC_ID,
+ .len = MLXSW_REG_MFSC_LEN,
+};
+
+/* reg_mfsc_pwm
+ * Fan pwm to control / monitor.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mfsc, pwm, 0x00, 24, 3);
+
+/* reg_mfsc_pwm_duty_cycle
+ * Controls the duty cycle of the PWM. Value range from 0..255 to
+ * represent duty cycle of 0%...100%.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mfsc, pwm_duty_cycle, 0x04, 0, 8);
+
+static inline void mlxsw_reg_mfsc_pack(char *payload, u8 pwm,
+ u8 pwm_duty_cycle)
+{
+ MLXSW_REG_ZERO(mfsc, payload);
+ mlxsw_reg_mfsc_pwm_set(payload, pwm);
+ mlxsw_reg_mfsc_pwm_duty_cycle_set(payload, pwm_duty_cycle);
+}
+
+/* MFSM - Management Fan Speed Measurement
+ * ---------------------------------------
+ * This register controls the settings of the Tacho measurements and
+ * enables reading the Tachometer measurements.
+ */
+#define MLXSW_REG_MFSM_ID 0x9003
+#define MLXSW_REG_MFSM_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mfsm = {
+ .id = MLXSW_REG_MFSM_ID,
+ .len = MLXSW_REG_MFSM_LEN,
+};
+
+/* reg_mfsm_tacho
+ * Fan tachometer index.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mfsm, tacho, 0x00, 24, 4);
+
+/* reg_mfsm_rpm
+ * Fan speed (round per minute).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mfsm, rpm, 0x04, 0, 16);
+
+static inline void mlxsw_reg_mfsm_pack(char *payload, u8 tacho)
+{
+ MLXSW_REG_ZERO(mfsm, payload);
+ mlxsw_reg_mfsm_tacho_set(payload, tacho);
+}
+
+/* MTCAP - Management Temperature Capabilities
+ * -------------------------------------------
+ * This register exposes the capabilities of the device and
+ * system temperature sensing.
+ */
+#define MLXSW_REG_MTCAP_ID 0x9009
+#define MLXSW_REG_MTCAP_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mtcap = {
+ .id = MLXSW_REG_MTCAP_ID,
+ .len = MLXSW_REG_MTCAP_LEN,
+};
+
+/* reg_mtcap_sensor_count
+ * Number of sensors supported by the device.
+ * This includes the QSFP module sensors (if exists in the QSFP module).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mtcap, sensor_count, 0x00, 0, 7);
+
+/* MTMP - Management Temperature
+ * -----------------------------
+ * This register controls the settings of the temperature measurements
+ * and enables reading the temperature measurements. Note that temperature
+ * is in 0.125 degrees Celsius.
+ */
+#define MLXSW_REG_MTMP_ID 0x900A
+#define MLXSW_REG_MTMP_LEN 0x20
+
+static const struct mlxsw_reg_info mlxsw_reg_mtmp = {
+ .id = MLXSW_REG_MTMP_ID,
+ .len = MLXSW_REG_MTMP_LEN,
+};
+
+/* reg_mtmp_sensor_index
+ * Sensors index to access.
+ * 64-127 of sensor_index are mapped to the SFP+/QSFP modules sequentially
+ * (module 0 is mapped to sensor_index 64).
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mtmp, sensor_index, 0x00, 0, 7);
+
+/* Convert to milli degrees Celsius */
+#define MLXSW_REG_MTMP_TEMP_TO_MC(val) (val * 125)
+
+/* reg_mtmp_temperature
+ * Temperature reading from the sensor. Reading is in 0.125 Celsius
+ * degrees units.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mtmp, temperature, 0x04, 0, 16);
+
+/* reg_mtmp_mte
+ * Max Temperature Enable - enables measuring the max temperature on a sensor.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mtmp, mte, 0x08, 31, 1);
+
+/* reg_mtmp_mtr
+ * Max Temperature Reset - clears the value of the max temperature register.
+ * Access: WO
+ */
+MLXSW_ITEM32(reg, mtmp, mtr, 0x08, 30, 1);
+
+/* reg_mtmp_max_temperature
+ * The highest measured temperature from the sensor.
+ * When the bit mte is cleared, the field max_temperature is reserved.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mtmp, max_temperature, 0x08, 0, 16);
+
+#define MLXSW_REG_MTMP_SENSOR_NAME_SIZE 8
+
+/* reg_mtmp_sensor_name
+ * Sensor Name
+ * Access: RO
+ */
+MLXSW_ITEM_BUF(reg, mtmp, sensor_name, 0x18, MLXSW_REG_MTMP_SENSOR_NAME_SIZE);
+
+static inline void mlxsw_reg_mtmp_pack(char *payload, u8 sensor_index,
+ bool max_temp_enable,
+ bool max_temp_reset)
+{
+ MLXSW_REG_ZERO(mtmp, payload);
+ mlxsw_reg_mtmp_sensor_index_set(payload, sensor_index);
+ mlxsw_reg_mtmp_mte_set(payload, max_temp_enable);
+ mlxsw_reg_mtmp_mtr_set(payload, max_temp_reset);
+}
+
+static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp,
+ unsigned int *p_max_temp,
+ char *sensor_name)
+{
+ u16 temp;
+
+ if (p_temp) {
+ temp = mlxsw_reg_mtmp_temperature_get(payload);
+ *p_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
+ }
+ if (p_max_temp) {
+ temp = mlxsw_reg_mtmp_max_temperature_get(payload);
+ *p_max_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
+ }
+ if (sensor_name)
+ mlxsw_reg_mtmp_sensor_name_memcpy_from(payload, sensor_name);
+}
+
+/* MLCR - Management LED Control Register
+ * --------------------------------------
+ * Controls the system LEDs.
+ */
+#define MLXSW_REG_MLCR_ID 0x902B
+#define MLXSW_REG_MLCR_LEN 0x0C
+
+static const struct mlxsw_reg_info mlxsw_reg_mlcr = {
+ .id = MLXSW_REG_MLCR_ID,
+ .len = MLXSW_REG_MLCR_LEN,
+};
+
+/* reg_mlcr_local_port
+ * Local port number.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mlcr, local_port, 0x00, 16, 8);
+
+#define MLXSW_REG_MLCR_DURATION_MAX 0xFFFF
+
+/* reg_mlcr_beacon_duration
+ * Duration of the beacon to be active, in seconds.
+ * 0x0 - Will turn off the beacon.
+ * 0xFFFF - Will turn on the beacon until explicitly turned off.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mlcr, beacon_duration, 0x04, 0, 16);
+
+/* reg_mlcr_beacon_remain
+ * Remaining duration of the beacon, in seconds.
+ * 0xFFFF indicates an infinite amount of time.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mlcr, beacon_remain, 0x08, 0, 16);
+
+static inline void mlxsw_reg_mlcr_pack(char *payload, u8 local_port,
+ bool active)
+{
+ MLXSW_REG_ZERO(mlcr, payload);
+ mlxsw_reg_mlcr_local_port_set(payload, local_port);
+ mlxsw_reg_mlcr_beacon_duration_set(payload, active ?
+ MLXSW_REG_MLCR_DURATION_MAX : 0);
+}
+
/* SBPR - Shared Buffer Pools Register
* -----------------------------------
* The SBPR configures and retrieves the shared buffer pools and configuration.
@@ -2357,6 +3101,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
return "SGCR";
case MLXSW_REG_SPAD_ID:
return "SPAD";
+ case MLXSW_REG_SMID_ID:
+ return "SMID";
case MLXSW_REG_SSPR_ID:
return "SSPR";
case MLXSW_REG_SFDAT_ID:
@@ -2375,6 +3121,12 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
return "SFGC";
case MLXSW_REG_SFTR_ID:
return "SFTR";
+ case MLXSW_REG_SLDR_ID:
+ return "SLDR";
+ case MLXSW_REG_SLCR_ID:
+ return "SLCR";
+ case MLXSW_REG_SLCOR_ID:
+ return "SLCOR";
case MLXSW_REG_SPMLR_ID:
return "SPMLR";
case MLXSW_REG_SVFA_ID:
@@ -2405,6 +3157,18 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
return "HTGT";
case MLXSW_REG_HPKT_ID:
return "HPKT";
+ case MLXSW_REG_MFCR_ID:
+ return "MFCR";
+ case MLXSW_REG_MFSC_ID:
+ return "MFSC";
+ case MLXSW_REG_MFSM_ID:
+ return "MFSM";
+ case MLXSW_REG_MTCAP_ID:
+ return "MTCAP";
+ case MLXSW_REG_MTMP_ID:
+ return "MTMP";
+ case MLXSW_REG_MLCR_ID:
+ return "MLCR";
case MLXSW_REG_SBPR_ID:
return "SBPR";
case MLXSW_REG_SBCM_ID:
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 3be4a2355ead..ce6845d534a8 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -48,6 +48,7 @@
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
+#include <linux/list.h>
#include <net/switchdev.h>
#include <generated/utsrelease.h>
@@ -186,33 +187,6 @@ static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port,
return 0;
}
-static int mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid)
-{
- char sfmr_pl[MLXSW_REG_SFMR_LEN];
- int err;
-
- mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID,
- MLXSW_SP_VFID_BASE + vfid, 0);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
-
- if (err)
- return err;
-
- set_bit(vfid, mlxsw_sp->active_vfids);
- return 0;
-}
-
-static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid)
-{
- char sfmr_pl[MLXSW_REG_SFMR_LEN];
-
- clear_bit(vfid, mlxsw_sp->active_vfids);
-
- mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID,
- MLXSW_SP_VFID_BASE + vfid, 0);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
-}
-
static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
unsigned char *addr)
{
@@ -417,6 +391,10 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
+static void mlxsw_sp_set_rx_mode(struct net_device *dev)
+{
+}
+
static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
@@ -545,12 +523,132 @@ static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
return 0;
}
+static struct mlxsw_sp_vfid *
+mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, u16 vid)
+{
+ struct mlxsw_sp_vfid *vfid;
+
+ list_for_each_entry(vfid, &mlxsw_sp->port_vfids.list, list) {
+ if (vfid->vid == vid)
+ return vfid;
+ }
+
+ return NULL;
+}
+
+static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp)
+{
+ return find_first_zero_bit(mlxsw_sp->port_vfids.mapped,
+ MLXSW_SP_VFID_PORT_MAX);
+}
+
+static int __mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid)
+{
+ u16 fid = mlxsw_sp_vfid_to_fid(vfid);
+ char sfmr_pl[MLXSW_REG_SFMR_LEN];
+
+ mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid, 0);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+}
+
+static void __mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid)
+{
+ u16 fid = mlxsw_sp_vfid_to_fid(vfid);
+ char sfmr_pl[MLXSW_REG_SFMR_LEN];
+
+ mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, fid, 0);
+ mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+}
+
+static struct mlxsw_sp_vfid *mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp,
+ u16 vid)
+{
+ struct device *dev = mlxsw_sp->bus_info->dev;
+ struct mlxsw_sp_vfid *vfid;
+ u16 n_vfid;
+ int err;
+
+ n_vfid = mlxsw_sp_avail_vfid_get(mlxsw_sp);
+ if (n_vfid == MLXSW_SP_VFID_PORT_MAX) {
+ dev_err(dev, "No available vFIDs\n");
+ return ERR_PTR(-ERANGE);
+ }
+
+ err = __mlxsw_sp_vfid_create(mlxsw_sp, n_vfid);
+ if (err) {
+ dev_err(dev, "Failed to create vFID=%d\n", n_vfid);
+ return ERR_PTR(err);
+ }
+
+ vfid = kzalloc(sizeof(*vfid), GFP_KERNEL);
+ if (!vfid)
+ goto err_allocate_vfid;
+
+ vfid->vfid = n_vfid;
+ vfid->vid = vid;
+
+ list_add(&vfid->list, &mlxsw_sp->port_vfids.list);
+ set_bit(n_vfid, mlxsw_sp->port_vfids.mapped);
+
+ return vfid;
+
+err_allocate_vfid:
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, n_vfid);
+ return ERR_PTR(-ENOMEM);
+}
+
+static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_vfid *vfid)
+{
+ clear_bit(vfid->vfid, mlxsw_sp->port_vfids.mapped);
+ list_del(&vfid->list);
+
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, vfid->vfid);
+
+ kfree(vfid);
+}
+
+static struct mlxsw_sp_port *
+mlxsw_sp_port_vport_create(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct mlxsw_sp_vfid *vfid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ mlxsw_sp_vport = kzalloc(sizeof(*mlxsw_sp_vport), GFP_KERNEL);
+ if (!mlxsw_sp_vport)
+ return NULL;
+
+ /* dev will be set correctly after the VLAN device is linked
+ * with the real device. In case of bridge SELF invocation, dev
+ * will remain as is.
+ */
+ mlxsw_sp_vport->dev = mlxsw_sp_port->dev;
+ mlxsw_sp_vport->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ mlxsw_sp_vport->local_port = mlxsw_sp_port->local_port;
+ mlxsw_sp_vport->stp_state = BR_STATE_FORWARDING;
+ mlxsw_sp_vport->lagged = mlxsw_sp_port->lagged;
+ mlxsw_sp_vport->lag_id = mlxsw_sp_port->lag_id;
+ mlxsw_sp_vport->vport.vfid = vfid;
+ mlxsw_sp_vport->vport.vid = vfid->vid;
+
+ list_add(&mlxsw_sp_vport->vport.list, &mlxsw_sp_port->vports_list);
+
+ return mlxsw_sp_vport;
+}
+
+static void mlxsw_sp_port_vport_destroy(struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ list_del(&mlxsw_sp_vport->vport.list);
+ kfree(mlxsw_sp_vport);
+}
+
int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
u16 vid)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- char *sftr_pl;
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ struct mlxsw_sp_vfid *vfid;
int err;
/* VLAN 0 is added to HW filter when device goes up, but it is
@@ -559,100 +657,105 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
if (!vid)
return 0;
- if (test_bit(vid, mlxsw_sp_port->active_vfids)) {
+ if (mlxsw_sp_port_vport_find(mlxsw_sp_port, vid)) {
netdev_warn(dev, "VID=%d already configured\n", vid);
return 0;
}
- if (!test_bit(vid, mlxsw_sp->active_vfids)) {
- err = mlxsw_sp_vfid_create(mlxsw_sp, vid);
- if (err) {
- netdev_err(dev, "Failed to create vFID=%d\n",
- MLXSW_SP_VFID_BASE + vid);
- return err;
+ vfid = mlxsw_sp_vfid_find(mlxsw_sp, vid);
+ if (!vfid) {
+ vfid = mlxsw_sp_vfid_create(mlxsw_sp, vid);
+ if (IS_ERR(vfid)) {
+ netdev_err(dev, "Failed to create vFID for VID=%d\n",
+ vid);
+ return PTR_ERR(vfid);
}
+ }
- sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
- if (!sftr_pl) {
- err = -ENOMEM;
- goto err_flood_table_alloc;
- }
- mlxsw_reg_sftr_pack(sftr_pl, 0, vid,
- MLXSW_REG_SFGC_TABLE_TYPE_FID, 0,
- MLXSW_PORT_CPU_PORT, true);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
- kfree(sftr_pl);
+ mlxsw_sp_vport = mlxsw_sp_port_vport_create(mlxsw_sp_port, vfid);
+ if (!mlxsw_sp_vport) {
+ netdev_err(dev, "Failed to create vPort for VID=%d\n", vid);
+ err = -ENOMEM;
+ goto err_port_vport_create;
+ }
+
+ if (!vfid->nr_vports) {
+ err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid,
+ true, false);
if (err) {
- netdev_err(dev, "Failed to configure flood table\n");
- goto err_flood_table_config;
+ netdev_err(dev, "Failed to setup flooding for vFID=%d\n",
+ vfid->vfid);
+ goto err_vport_flood_set;
}
}
- /* In case we fail in the following steps, we intentionally do not
- * destroy the associated vFID.
- */
-
/* When adding the first VLAN interface on a bridged port we need to
* transition all the active 802.1Q bridge VLANs to use explicit
* {Port, VID} to FID mappings and set the port's mode to Virtual mode.
*/
- if (!mlxsw_sp_port->nr_vfids) {
+ if (list_is_singular(&mlxsw_sp_port->vports_list)) {
err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
if (err) {
netdev_err(dev, "Failed to set to Virtual mode\n");
- return err;
+ goto err_port_vp_mode_trans;
}
}
- err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
- true, MLXSW_SP_VFID_BASE + vid, vid);
+ true,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
+ vid);
if (err) {
netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n",
- vid, MLXSW_SP_VFID_BASE + vid);
+ vid, vfid->vfid);
goto err_port_vid_to_fid_set;
}
- err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false);
if (err) {
netdev_err(dev, "Failed to disable learning for VID=%d\n", vid);
goto err_port_vid_learning_set;
}
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, false);
+ err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, true, false);
if (err) {
netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
vid);
goto err_port_add_vid;
}
- err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid,
+ err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid,
MLXSW_REG_SPMS_STATE_FORWARDING);
if (err) {
netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
goto err_port_stp_state_set;
}
- mlxsw_sp_port->nr_vfids++;
- set_bit(vid, mlxsw_sp_port->active_vfids);
+ vfid->nr_vports++;
return 0;
-err_flood_table_config:
-err_flood_table_alloc:
- mlxsw_sp_vfid_destroy(mlxsw_sp, vid);
- return err;
-
err_port_stp_state_set:
- mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
+ mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false);
err_port_add_vid:
- mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
+ mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
err_port_vid_learning_set:
- mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+ mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false,
- MLXSW_SP_VFID_BASE + vid, vid);
+ mlxsw_sp_vfid_to_fid(vfid->vfid), vid);
err_port_vid_to_fid_set:
- mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+ if (list_is_singular(&mlxsw_sp_port->vports_list))
+ mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+err_port_vp_mode_trans:
+ if (!vfid->nr_vports)
+ mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false,
+ false);
+err_vport_flood_set:
+ mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
+err_port_vport_create:
+ if (!vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp, vfid);
return err;
}
@@ -660,6 +763,8 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ struct mlxsw_sp_vfid *vfid;
int err;
/* VLAN 0 is removed from HW filter when device goes down, but
@@ -668,38 +773,42 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
if (!vid)
return 0;
- if (!test_bit(vid, mlxsw_sp_port->active_vfids)) {
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ if (!mlxsw_sp_vport) {
netdev_warn(dev, "VID=%d does not exist\n", vid);
return 0;
}
- err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid,
+ vfid = mlxsw_sp_vport->vport.vfid;
+
+ err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid,
MLXSW_REG_SPMS_STATE_DISCARDING);
if (err) {
netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
return err;
}
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
+ err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false);
if (err) {
netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
vid);
return err;
}
- err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
if (err) {
netdev_err(dev, "Failed to enable learning for VID=%d\n", vid);
return err;
}
- err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
- false, MLXSW_SP_VFID_BASE + vid,
+ false,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
vid);
if (err) {
netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n",
- vid, MLXSW_SP_VFID_BASE + vid);
+ vid, vfid->vfid);
return err;
}
@@ -707,7 +816,7 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
* transition all active 802.1Q bridge VLANs to use VID to FID
* mappings and set port's mode to VLAN mode.
*/
- if (mlxsw_sp_port->nr_vfids == 1) {
+ if (list_is_singular(&mlxsw_sp_port->vports_list)) {
err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
if (err) {
netdev_err(dev, "Failed to set to VLAN mode\n");
@@ -715,8 +824,12 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
}
}
- mlxsw_sp_port->nr_vfids--;
- clear_bit(vid, mlxsw_sp_port->active_vfids);
+ vfid->nr_vports--;
+ mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
+
+ /* Destroy the vFID if no vPorts are assigned to it anymore. */
+ if (!vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp_port->mlxsw_sp, vfid);
return 0;
}
@@ -725,6 +838,7 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
.ndo_open = mlxsw_sp_port_open,
.ndo_stop = mlxsw_sp_port_stop,
.ndo_start_xmit = mlxsw_sp_port_xmit,
+ .ndo_set_rx_mode = mlxsw_sp_set_rx_mode,
.ndo_set_mac_address = mlxsw_sp_port_set_mac_address,
.ndo_change_mtu = mlxsw_sp_port_change_mtu,
.ndo_get_stats64 = mlxsw_sp_port_get_stats64,
@@ -859,6 +973,29 @@ static void mlxsw_sp_port_get_strings(struct net_device *dev,
}
}
+static int mlxsw_sp_port_set_phys_id(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char mlcr_pl[MLXSW_REG_MLCR_LEN];
+ bool active;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ active = true;
+ break;
+ case ETHTOOL_ID_INACTIVE:
+ active = false;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mlxsw_reg_mlcr_pack(mlcr_pl, mlxsw_sp_port->local_port, active);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
+}
+
static void mlxsw_sp_port_get_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
@@ -1205,6 +1342,7 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
.get_drvinfo = mlxsw_sp_port_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_strings = mlxsw_sp_port_get_strings,
+ .set_phys_id = mlxsw_sp_port_set_phys_id,
.get_ethtool_stats = mlxsw_sp_port_get_stats,
.get_sset_count = mlxsw_sp_port_get_sset_count,
.get_settings = mlxsw_sp_port_get_settings,
@@ -1216,6 +1354,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
struct mlxsw_sp_port *mlxsw_sp_port;
struct net_device *dev;
bool usable;
+ size_t bytes;
int err;
dev = alloc_etherdev(sizeof(struct mlxsw_sp_port));
@@ -1225,10 +1364,18 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
mlxsw_sp_port->dev = dev;
mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
mlxsw_sp_port->local_port = local_port;
- mlxsw_sp_port->learning = 1;
- mlxsw_sp_port->learning_sync = 1;
- mlxsw_sp_port->uc_flood = 1;
- mlxsw_sp_port->pvid = 1;
+ bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE);
+ mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL);
+ if (!mlxsw_sp_port->active_vlans) {
+ err = -ENOMEM;
+ goto err_port_active_vlans_alloc;
+ }
+ mlxsw_sp_port->untagged_vlans = kzalloc(bytes, GFP_KERNEL);
+ if (!mlxsw_sp_port->untagged_vlans) {
+ err = -ENOMEM;
+ goto err_port_untagged_vlans_alloc;
+ }
+ INIT_LIST_HEAD(&mlxsw_sp_port->vports_list);
mlxsw_sp_port->pcpu_stats =
netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats);
@@ -1330,16 +1477,29 @@ err_port_module_check:
err_dev_addr_init:
free_percpu(mlxsw_sp_port->pcpu_stats);
err_alloc_stats:
+ kfree(mlxsw_sp_port->untagged_vlans);
+err_port_untagged_vlans_alloc:
+ kfree(mlxsw_sp_port->active_vlans);
+err_port_active_vlans_alloc:
free_netdev(dev);
return err;
}
-static void mlxsw_sp_vfids_fini(struct mlxsw_sp *mlxsw_sp)
+static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port)
{
- u16 vfid;
+ struct net_device *dev = mlxsw_sp_port->dev;
+ struct mlxsw_sp_port *mlxsw_sp_vport, *tmp;
- for_each_set_bit(vfid, mlxsw_sp->active_vfids, VLAN_N_VID)
- mlxsw_sp_vfid_destroy(mlxsw_sp, vfid);
+ list_for_each_entry_safe(mlxsw_sp_vport, tmp,
+ &mlxsw_sp_port->vports_list, vport.list) {
+ u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+
+ /* vPorts created for VLAN devices should already be gone
+ * by now, since we unregistered the port netdev.
+ */
+ WARN_ON(is_vlan_dev(mlxsw_sp_vport->dev));
+ mlxsw_sp_port_kill_vid(dev, 0, vid);
+ }
}
static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
@@ -1348,10 +1508,12 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
if (!mlxsw_sp_port)
return;
- mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1);
unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
+ mlxsw_sp_port_vports_fini(mlxsw_sp_port);
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
free_percpu(mlxsw_sp_port->pcpu_stats);
+ kfree(mlxsw_sp_port->untagged_vlans);
+ kfree(mlxsw_sp_port->active_vlans);
free_netdev(mlxsw_sp_port->dev);
}
@@ -1633,16 +1795,15 @@ static int __mlxsw_sp_flood_init(struct mlxsw_core *mlxsw_core,
enum mlxsw_sp_flood_table flood_table;
char sfgc_pl[MLXSW_REG_SFGC_LEN];
- if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID) {
+ if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID)
table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
- flood_table = 0;
- } else {
+ else
table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
- if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST)
- flood_table = MLXSW_SP_FLOOD_TABLE_UC;
- else
- flood_table = MLXSW_SP_FLOOD_TABLE_BM;
- }
+
+ if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST)
+ flood_table = MLXSW_SP_FLOOD_TABLE_UC;
+ else
+ flood_table = MLXSW_SP_FLOOD_TABLE_BM;
mlxsw_reg_sfgc_pack(sfgc_pl, type, bridge_type, table_type,
flood_table);
@@ -1653,9 +1814,6 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
{
int type, err;
- /* For non-offloaded netdevs, flood all traffic types to CPU
- * port.
- */
for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) {
if (type == MLXSW_REG_SFGC_TYPE_RESERVED)
continue;
@@ -1664,15 +1822,6 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
MLXSW_REG_SFGC_BRIDGE_TYPE_VFID);
if (err)
return err;
- }
-
- /* For bridged ports, use one flooding table for unknown unicast
- * traffic and a second table for unregistered multicast and
- * broadcast.
- */
- for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) {
- if (type == MLXSW_REG_SFGC_TYPE_RESERVED)
- continue;
err = __mlxsw_sp_flood_init(mlxsw_sp->core, type,
MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID);
@@ -1683,6 +1832,22 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
return 0;
}
+static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp)
+{
+ char slcr_pl[MLXSW_REG_SLCR_LEN];
+
+ mlxsw_reg_slcr_pack(slcr_pl, MLXSW_REG_SLCR_LAG_HASH_SMAC |
+ MLXSW_REG_SLCR_LAG_HASH_DMAC |
+ MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE |
+ MLXSW_REG_SLCR_LAG_HASH_VLANID |
+ MLXSW_REG_SLCR_LAG_HASH_SIP |
+ MLXSW_REG_SLCR_LAG_HASH_DIP |
+ MLXSW_REG_SLCR_LAG_HASH_SPORT |
+ MLXSW_REG_SLCR_LAG_HASH_DPORT |
+ MLXSW_REG_SLCR_LAG_HASH_IPPROTO);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcr), slcr_pl);
+}
+
static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *mlxsw_bus_info)
{
@@ -1691,6 +1856,9 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
mlxsw_sp->core = mlxsw_core;
mlxsw_sp->bus_info = mlxsw_bus_info;
+ INIT_LIST_HEAD(&mlxsw_sp->port_vfids.list);
+ INIT_LIST_HEAD(&mlxsw_sp->br_vfids.list);
+ INIT_LIST_HEAD(&mlxsw_sp->br_mids.list);
err = mlxsw_sp_base_mac_get(mlxsw_sp);
if (err) {
@@ -1701,7 +1869,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
err = mlxsw_sp_ports_create(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
- goto err_ports_create;
+ return err;
}
err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
@@ -1728,6 +1896,12 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
goto err_buffers_init;
}
+ err = mlxsw_sp_lag_init(mlxsw_sp);
+ if (err) {
+ dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize LAG\n");
+ goto err_lag_init;
+ }
+
err = mlxsw_sp_switchdev_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n");
@@ -1737,6 +1911,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
return 0;
err_switchdev_init:
+err_lag_init:
err_buffers_init:
err_flood_init:
mlxsw_sp_traps_fini(mlxsw_sp);
@@ -1744,8 +1919,6 @@ err_rx_listener_register:
mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
err_event_register:
mlxsw_sp_ports_remove(mlxsw_sp);
-err_ports_create:
- mlxsw_sp_vfids_fini(mlxsw_sp);
return err;
}
@@ -1757,18 +1930,17 @@ static void mlxsw_sp_fini(void *priv)
mlxsw_sp_traps_fini(mlxsw_sp);
mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
mlxsw_sp_ports_remove(mlxsw_sp);
- mlxsw_sp_vfids_fini(mlxsw_sp);
}
static struct mlxsw_config_profile mlxsw_sp_config_profile = {
.used_max_vepa_channels = 1,
.max_vepa_channels = 0,
.used_max_lag = 1,
- .max_lag = 64,
+ .max_lag = MLXSW_SP_LAG_MAX,
.used_max_port_per_lag = 1,
- .max_port_per_lag = 16,
+ .max_port_per_lag = MLXSW_SP_PORT_PER_LAG_MAX,
.used_max_mid = 1,
- .max_mid = 7000,
+ .max_mid = MLXSW_SP_MID_MAX,
.used_max_pgt = 1,
.max_pgt = 0,
.used_max_system_port = 1,
@@ -1782,8 +1954,8 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = {
.flood_mode = 3,
.max_fid_offset_flood_tables = 2,
.fid_offset_flood_table_size = VLAN_N_VID - 1,
- .max_fid_flood_tables = 1,
- .fid_flood_table_size = VLAN_N_VID,
+ .max_fid_flood_tables = 2,
+ .fid_flood_table_size = MLXSW_SP_VFID_MAX,
.used_max_ib_mc = 1,
.max_ib_mc = 0,
.used_max_pkey = 1,
@@ -1824,24 +1996,29 @@ static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port)
*/
err = mlxsw_sp_port_kill_vid(dev, 0, 1);
if (err)
- netdev_err(dev, "Failed to remove VID 1\n");
+ return err;
- return err;
+ mlxsw_sp_port->learning = 1;
+ mlxsw_sp_port->learning_sync = 1;
+ mlxsw_sp_port->uc_flood = 1;
+ mlxsw_sp_port->bridged = 1;
+
+ return 0;
}
static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port)
{
struct net_device *dev = mlxsw_sp_port->dev;
- int err;
+
+ mlxsw_sp_port->learning = 0;
+ mlxsw_sp_port->learning_sync = 0;
+ mlxsw_sp_port->uc_flood = 0;
+ mlxsw_sp_port->bridged = 0;
/* Add implicit VLAN interface in the device, so that untagged
* packets will be classified to the default vFID.
*/
- err = mlxsw_sp_port_add_vid(dev, 0, 1);
- if (err)
- netdev_err(dev, "Failed to add VID 1\n");
-
- return err;
+ return mlxsw_sp_port_add_vid(dev, 0, 1);
}
static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp,
@@ -1865,19 +2042,293 @@ static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp->master_bridge.dev = NULL;
}
-static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
- unsigned long event, void *ptr)
+static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
+{
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_create_pack(sldr_pl, lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
+{
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_lag_col_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id, u8 port_index)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_port_add_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id, port_index);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_col_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_port_remove_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_col_port_enable(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_col_enable_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_col_disable_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *lag_dev,
+ u16 *p_lag_id)
+{
+ struct mlxsw_sp_upper *lag;
+ int free_lag_id = -1;
+ int i;
+
+ for (i = 0; i < MLXSW_SP_LAG_MAX; i++) {
+ lag = mlxsw_sp_lag_get(mlxsw_sp, i);
+ if (lag->ref_count) {
+ if (lag->dev == lag_dev) {
+ *p_lag_id = i;
+ return 0;
+ }
+ } else if (free_lag_id < 0) {
+ free_lag_id = i;
+ }
+ }
+ if (free_lag_id < 0)
+ return -EBUSY;
+ *p_lag_id = free_lag_id;
+ return 0;
+}
+
+static bool
+mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *lag_dev,
+ struct netdev_lag_upper_info *lag_upper_info)
+{
+ u16 lag_id;
+
+ if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0)
+ return false;
+ if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ return false;
+ return true;
+}
+
+static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp,
+ u16 lag_id, u8 *p_port_index)
+{
+ int i;
+
+ for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) {
+ if (!mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i)) {
+ *p_port_index = i;
+ return 0;
+ }
+ }
+ return -EBUSY;
+}
+
+static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *lag_dev)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_upper *lag;
+ u16 lag_id;
+ u8 port_index;
+ int err;
+
+ err = mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id);
+ if (err)
+ return err;
+ lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
+ if (!lag->ref_count) {
+ err = mlxsw_sp_lag_create(mlxsw_sp, lag_id);
+ if (err)
+ return err;
+ lag->dev = lag_dev;
+ }
+
+ err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index);
+ if (err)
+ return err;
+ err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
+ if (err)
+ goto err_col_port_add;
+ err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port, lag_id);
+ if (err)
+ goto err_col_port_enable;
+
+ mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index,
+ mlxsw_sp_port->local_port);
+ mlxsw_sp_port->lag_id = lag_id;
+ mlxsw_sp_port->lagged = 1;
+ lag->ref_count++;
+ return 0;
+
+err_col_port_add:
+ if (!lag->ref_count)
+ mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
+err_col_port_enable:
+ mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
+ return err;
+}
+
+static int mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *lag_dev)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_upper *lag;
+ u16 lag_id = mlxsw_sp_port->lag_id;
+ int err;
+
+ if (!mlxsw_sp_port->lagged)
+ return 0;
+ lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
+ WARN_ON(lag->ref_count == 0);
+
+ err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, lag_id);
+ if (err)
+ return err;
+ err = mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
+ if (err)
+ return err;
+
+ if (lag->ref_count == 1) {
+ err = mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
+ if (err)
+ return err;
+ }
+
+ mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id,
+ mlxsw_sp_port->local_port);
+ mlxsw_sp_port->lagged = 0;
+ lag->ref_count--;
+ return 0;
+}
+
+static int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_add_port_pack(sldr_pl, lag_id,
+ mlxsw_sp_port->local_port);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_remove_port_pack(sldr_pl, lag_id,
+ mlxsw_sp_port->local_port);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_port_lag_tx_en_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ bool lag_tx_enabled)
+{
+ if (lag_tx_enabled)
+ return mlxsw_sp_lag_dist_port_add(mlxsw_sp_port,
+ mlxsw_sp_port->lag_id);
+ else
+ return mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
+ mlxsw_sp_port->lag_id);
+}
+
+static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct netdev_lag_lower_state_info *info)
+{
+ return mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port, info->tx_enabled);
+}
+
+static int mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport,
+ struct net_device *br_dev);
+
+static int mlxsw_sp_port_vlan_link(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *vlan_dev)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ if (!mlxsw_sp_vport) {
+ WARN_ON(!mlxsw_sp_vport);
+ return -EINVAL;
+ }
+
+ mlxsw_sp_vport->dev = vlan_dev;
+
+ return 0;
+}
+
+static int mlxsw_sp_port_vlan_unlink(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *vlan_dev)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ if (!mlxsw_sp_vport) {
+ WARN_ON(!mlxsw_sp_vport);
+ return -EINVAL;
+ }
+
+ /* When removing a VLAN device while still bridged we should first
+ * remove it from the bridge, as we receive the bridge's notification
+ * when the vPort is already gone.
+ */
+ if (mlxsw_sp_vport->bridged) {
+ struct net_device *br_dev;
+
+ br_dev = mlxsw_sp_vport_br_get(mlxsw_sp_vport);
+ mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport, br_dev);
+ }
+
+ mlxsw_sp_vport->dev = mlxsw_sp_port->dev;
+
+ return 0;
+}
+
+static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
+ unsigned long event, void *ptr)
{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct netdev_notifier_changeupper_info *info;
struct mlxsw_sp_port *mlxsw_sp_port;
struct net_device *upper_dev;
struct mlxsw_sp *mlxsw_sp;
int err;
- if (!mlxsw_sp_port_dev_check(dev))
- return NOTIFY_DONE;
-
mlxsw_sp_port = netdev_priv(dev);
mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
info = ptr;
@@ -1885,28 +2336,66 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
switch (event) {
case NETDEV_PRECHANGEUPPER:
upper_dev = info->upper_dev;
+ if (!info->master || !info->linking)
+ break;
/* HW limitation forbids to put ports to multiple bridges. */
- if (info->master && info->linking &&
- netif_is_bridge_master(upper_dev) &&
+ if (netif_is_bridge_master(upper_dev) &&
!mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
return NOTIFY_BAD;
+ if (netif_is_lag_master(upper_dev) &&
+ !mlxsw_sp_master_lag_check(mlxsw_sp, upper_dev,
+ info->upper_info))
+ return NOTIFY_BAD;
break;
case NETDEV_CHANGEUPPER:
upper_dev = info->upper_dev;
- if (info->master &&
- netif_is_bridge_master(upper_dev)) {
+ if (is_vlan_dev(upper_dev)) {
+ if (info->linking) {
+ err = mlxsw_sp_port_vlan_link(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to link VLAN device\n");
+ return NOTIFY_BAD;
+ }
+ } else {
+ err = mlxsw_sp_port_vlan_unlink(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to unlink VLAN device\n");
+ return NOTIFY_BAD;
+ }
+ }
+ } else if (netif_is_bridge_master(upper_dev)) {
if (info->linking) {
err = mlxsw_sp_port_bridge_join(mlxsw_sp_port);
- if (err)
+ if (err) {
netdev_err(dev, "Failed to join bridge\n");
+ return NOTIFY_BAD;
+ }
mlxsw_sp_master_bridge_inc(mlxsw_sp, upper_dev);
- mlxsw_sp_port->bridged = 1;
} else {
err = mlxsw_sp_port_bridge_leave(mlxsw_sp_port);
- if (err)
- netdev_err(dev, "Failed to leave bridge\n");
- mlxsw_sp_port->bridged = 0;
mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to leave bridge\n");
+ return NOTIFY_BAD;
+ }
+ }
+ } else if (netif_is_lag_master(upper_dev)) {
+ if (info->linking) {
+ err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to join link aggregation\n");
+ return NOTIFY_BAD;
+ }
+ } else {
+ err = mlxsw_sp_port_lag_leave(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to leave link aggregation\n");
+ return NOTIFY_BAD;
+ }
}
}
break;
@@ -1915,6 +2404,443 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
return NOTIFY_DONE;
}
+static int mlxsw_sp_netdevice_port_lower_event(struct net_device *dev,
+ unsigned long event, void *ptr)
+{
+ struct netdev_notifier_changelowerstate_info *info;
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ int err;
+
+ mlxsw_sp_port = netdev_priv(dev);
+ info = ptr;
+
+ switch (event) {
+ case NETDEV_CHANGELOWERSTATE:
+ if (netif_is_lag_port(dev) && mlxsw_sp_port->lagged) {
+ err = mlxsw_sp_port_lag_changed(mlxsw_sp_port,
+ info->lower_state_info);
+ if (err)
+ netdev_err(dev, "Failed to reflect link aggregation lower state change\n");
+ }
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_port_event(struct net_device *dev,
+ unsigned long event, void *ptr)
+{
+ switch (event) {
+ case NETDEV_PRECHANGEUPPER:
+ case NETDEV_CHANGEUPPER:
+ return mlxsw_sp_netdevice_port_upper_event(dev, event, ptr);
+ case NETDEV_CHANGELOWERSTATE:
+ return mlxsw_sp_netdevice_port_lower_event(dev, event, ptr);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev;
+ struct list_head *iter;
+ int ret;
+
+ netdev_for_each_lower_dev(lag_dev, dev, iter) {
+ if (mlxsw_sp_port_dev_check(dev)) {
+ ret = mlxsw_sp_netdevice_port_event(dev, event, ptr);
+ if (ret == NOTIFY_BAD)
+ return ret;
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct mlxsw_sp_vfid *
+mlxsw_sp_br_vfid_find(const struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *br_dev)
+{
+ struct mlxsw_sp_vfid *vfid;
+
+ list_for_each_entry(vfid, &mlxsw_sp->br_vfids.list, list) {
+ if (vfid->br_dev == br_dev)
+ return vfid;
+ }
+
+ return NULL;
+}
+
+static u16 mlxsw_sp_vfid_to_br_vfid(u16 vfid)
+{
+ return vfid - MLXSW_SP_VFID_PORT_MAX;
+}
+
+static u16 mlxsw_sp_br_vfid_to_vfid(u16 br_vfid)
+{
+ return MLXSW_SP_VFID_PORT_MAX + br_vfid;
+}
+
+static u16 mlxsw_sp_avail_br_vfid_get(const struct mlxsw_sp *mlxsw_sp)
+{
+ return find_first_zero_bit(mlxsw_sp->br_vfids.mapped,
+ MLXSW_SP_VFID_BR_MAX);
+}
+
+static struct mlxsw_sp_vfid *mlxsw_sp_br_vfid_create(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *br_dev)
+{
+ struct device *dev = mlxsw_sp->bus_info->dev;
+ struct mlxsw_sp_vfid *vfid;
+ u16 n_vfid;
+ int err;
+
+ n_vfid = mlxsw_sp_br_vfid_to_vfid(mlxsw_sp_avail_br_vfid_get(mlxsw_sp));
+ if (n_vfid == MLXSW_SP_VFID_MAX) {
+ dev_err(dev, "No available vFIDs\n");
+ return ERR_PTR(-ERANGE);
+ }
+
+ err = __mlxsw_sp_vfid_create(mlxsw_sp, n_vfid);
+ if (err) {
+ dev_err(dev, "Failed to create vFID=%d\n", n_vfid);
+ return ERR_PTR(err);
+ }
+
+ vfid = kzalloc(sizeof(*vfid), GFP_KERNEL);
+ if (!vfid)
+ goto err_allocate_vfid;
+
+ vfid->vfid = n_vfid;
+ vfid->br_dev = br_dev;
+
+ list_add(&vfid->list, &mlxsw_sp->br_vfids.list);
+ set_bit(mlxsw_sp_vfid_to_br_vfid(n_vfid), mlxsw_sp->br_vfids.mapped);
+
+ return vfid;
+
+err_allocate_vfid:
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, n_vfid);
+ return ERR_PTR(-ENOMEM);
+}
+
+static void mlxsw_sp_br_vfid_destroy(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_vfid *vfid)
+{
+ u16 br_vfid = mlxsw_sp_vfid_to_br_vfid(vfid->vfid);
+
+ clear_bit(br_vfid, mlxsw_sp->br_vfids.mapped);
+ list_del(&vfid->list);
+
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, vfid->vfid);
+
+ kfree(vfid);
+}
+
+static int mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport,
+ struct net_device *br_dev)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+ u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ struct net_device *dev = mlxsw_sp_vport->dev;
+ struct mlxsw_sp_vfid *vfid, *new_vfid;
+ int err;
+
+ vfid = mlxsw_sp_br_vfid_find(mlxsw_sp, br_dev);
+ if (!vfid) {
+ WARN_ON(!vfid);
+ return -EINVAL;
+ }
+
+ /* We need a vFID to go back to after leaving the bridge's vFID. */
+ new_vfid = mlxsw_sp_vfid_find(mlxsw_sp, vid);
+ if (!new_vfid) {
+ new_vfid = mlxsw_sp_vfid_create(mlxsw_sp, vid);
+ if (IS_ERR(new_vfid)) {
+ netdev_err(dev, "Failed to create vFID for VID=%d\n",
+ vid);
+ return PTR_ERR(new_vfid);
+ }
+ }
+
+ /* Invalidate existing {Port, VID} to vFID mapping and create a new
+ * one for the new vFID.
+ */
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ false,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to invalidate {Port, VID} to vFID=%d mapping\n",
+ vfid->vfid);
+ goto err_port_vid_to_fid_invalidate;
+ }
+
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ true,
+ mlxsw_sp_vfid_to_fid(new_vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to map {Port, VID} to vFID=%d\n",
+ new_vfid->vfid);
+ goto err_port_vid_to_fid_validate;
+ }
+
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false);
+ if (err) {
+ netdev_err(dev, "Failed to disable learning\n");
+ goto err_port_vid_learning_set;
+ }
+
+ err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false,
+ false);
+ if (err) {
+ netdev_err(dev, "Failed clear to clear flooding\n");
+ goto err_vport_flood_set;
+ }
+
+ /* Switch between the vFIDs and destroy the old one if needed. */
+ new_vfid->nr_vports++;
+ mlxsw_sp_vport->vport.vfid = new_vfid;
+ vfid->nr_vports--;
+ if (!vfid->nr_vports)
+ mlxsw_sp_br_vfid_destroy(mlxsw_sp, vfid);
+
+ mlxsw_sp_vport->learning = 0;
+ mlxsw_sp_vport->learning_sync = 0;
+ mlxsw_sp_vport->uc_flood = 0;
+ mlxsw_sp_vport->bridged = 0;
+
+ return 0;
+
+err_vport_flood_set:
+err_port_vid_learning_set:
+err_port_vid_to_fid_validate:
+err_port_vid_to_fid_invalidate:
+ /* Rollback vFID only if new. */
+ if (!new_vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp, new_vfid);
+ return err;
+}
+
+static int mlxsw_sp_vport_bridge_join(struct mlxsw_sp_port *mlxsw_sp_vport,
+ struct net_device *br_dev)
+{
+ struct mlxsw_sp_vfid *old_vfid = mlxsw_sp_vport->vport.vfid;
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+ u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ struct net_device *dev = mlxsw_sp_vport->dev;
+ struct mlxsw_sp_vfid *vfid;
+ int err;
+
+ vfid = mlxsw_sp_br_vfid_find(mlxsw_sp, br_dev);
+ if (!vfid) {
+ vfid = mlxsw_sp_br_vfid_create(mlxsw_sp, br_dev);
+ if (IS_ERR(vfid)) {
+ netdev_err(dev, "Failed to create bridge vFID\n");
+ return PTR_ERR(vfid);
+ }
+ }
+
+ err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, true, false);
+ if (err) {
+ netdev_err(dev, "Failed to setup flooding for vFID=%d\n",
+ vfid->vfid);
+ goto err_port_flood_set;
+ }
+
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
+ if (err) {
+ netdev_err(dev, "Failed to enable learning\n");
+ goto err_port_vid_learning_set;
+ }
+
+ /* We need to invalidate existing {Port, VID} to vFID mapping and
+ * create a new one for the bridge's vFID.
+ */
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ false,
+ mlxsw_sp_vfid_to_fid(old_vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to invalidate {Port, VID} to vFID=%d mapping\n",
+ old_vfid->vfid);
+ goto err_port_vid_to_fid_invalidate;
+ }
+
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ true,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to map {Port, VID} to vFID=%d\n",
+ vfid->vfid);
+ goto err_port_vid_to_fid_validate;
+ }
+
+ /* Switch between the vFIDs and destroy the old one if needed. */
+ vfid->nr_vports++;
+ mlxsw_sp_vport->vport.vfid = vfid;
+ old_vfid->nr_vports--;
+ if (!old_vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp, old_vfid);
+
+ mlxsw_sp_vport->learning = 1;
+ mlxsw_sp_vport->learning_sync = 1;
+ mlxsw_sp_vport->uc_flood = 1;
+ mlxsw_sp_vport->bridged = 1;
+
+ return 0;
+
+err_port_vid_to_fid_validate:
+ mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false,
+ mlxsw_sp_vfid_to_fid(old_vfid->vfid), vid);
+err_port_vid_to_fid_invalidate:
+ mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false);
+err_port_vid_learning_set:
+ mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false, false);
+err_port_flood_set:
+ if (!vfid->nr_vports)
+ mlxsw_sp_br_vfid_destroy(mlxsw_sp, vfid);
+ return err;
+}
+
+static bool
+mlxsw_sp_port_master_bridge_check(const struct mlxsw_sp_port *mlxsw_sp_port,
+ const struct net_device *br_dev)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
+ vport.list) {
+ if (mlxsw_sp_vport_br_get(mlxsw_sp_vport) == br_dev)
+ return false;
+ }
+
+ return true;
+}
+
+static int mlxsw_sp_netdevice_vport_event(struct net_device *dev,
+ unsigned long event, void *ptr,
+ u16 vid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct netdev_notifier_changeupper_info *info = ptr;
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ struct net_device *upper_dev;
+ int err;
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+
+ switch (event) {
+ case NETDEV_PRECHANGEUPPER:
+ upper_dev = info->upper_dev;
+ if (!info->master || !info->linking)
+ break;
+ if (!netif_is_bridge_master(upper_dev))
+ return NOTIFY_BAD;
+ /* We can't have multiple VLAN interfaces configured on
+ * the same port and being members in the same bridge.
+ */
+ if (!mlxsw_sp_port_master_bridge_check(mlxsw_sp_port,
+ upper_dev))
+ return NOTIFY_BAD;
+ break;
+ case NETDEV_CHANGEUPPER:
+ upper_dev = info->upper_dev;
+ if (!info->master)
+ break;
+ if (info->linking) {
+ if (!mlxsw_sp_vport) {
+ WARN_ON(!mlxsw_sp_vport);
+ return NOTIFY_BAD;
+ }
+ err = mlxsw_sp_vport_bridge_join(mlxsw_sp_vport,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to join bridge\n");
+ return NOTIFY_BAD;
+ }
+ } else {
+ /* We ignore bridge's unlinking notifications if vPort
+ * is gone, since we already left the bridge when the
+ * VLAN device was unlinked from the real device.
+ */
+ if (!mlxsw_sp_vport)
+ return NOTIFY_DONE;
+ err = mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to leave bridge\n");
+ return NOTIFY_BAD;
+ }
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_lag_vport_event(struct net_device *lag_dev,
+ unsigned long event, void *ptr,
+ u16 vid)
+{
+ struct net_device *dev;
+ struct list_head *iter;
+ int ret;
+
+ netdev_for_each_lower_dev(lag_dev, dev, iter) {
+ if (mlxsw_sp_port_dev_check(dev)) {
+ ret = mlxsw_sp_netdevice_vport_event(dev, event, ptr,
+ vid);
+ if (ret == NOTIFY_BAD)
+ return ret;
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
+ unsigned long event, void *ptr)
+{
+ struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
+ u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+ if (mlxsw_sp_port_dev_check(real_dev))
+ return mlxsw_sp_netdevice_vport_event(real_dev, event, ptr,
+ vid);
+ else if (netif_is_lag_master(real_dev))
+ return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr,
+ vid);
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+ if (mlxsw_sp_port_dev_check(dev))
+ return mlxsw_sp_netdevice_port_event(dev, event, ptr);
+
+ if (netif_is_lag_master(dev))
+ return mlxsw_sp_netdevice_lag_event(dev, event, ptr);
+
+ if (is_vlan_dev(dev))
+ return mlxsw_sp_netdevice_vlan_event(dev, event, ptr);
+
+ return NOTIFY_DONE;
+}
+
static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = {
.notifier_call = mlxsw_sp_netdevice_event,
};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 4365c8bccc6d..a23dc610d259 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -41,16 +41,73 @@
#include <linux/netdevice.h>
#include <linux/bitops.h>
#include <linux/if_vlan.h>
+#include <linux/list.h>
#include <net/switchdev.h>
+#include "port.h"
#include "core.h"
#define MLXSW_SP_VFID_BASE VLAN_N_VID
+#define MLXSW_SP_VFID_PORT_MAX 512 /* Non-bridged VLAN interfaces */
+#define MLXSW_SP_VFID_BR_MAX 8192 /* Bridged VLAN interfaces */
+#define MLXSW_SP_VFID_MAX (MLXSW_SP_VFID_PORT_MAX + MLXSW_SP_VFID_BR_MAX)
+
+#define MLXSW_SP_LAG_MAX 64
+#define MLXSW_SP_PORT_PER_LAG_MAX 16
+
+#define MLXSW_SP_MID_MAX 7000
struct mlxsw_sp_port;
+struct mlxsw_sp_upper {
+ struct net_device *dev;
+ unsigned int ref_count;
+};
+
+struct mlxsw_sp_vfid {
+ struct list_head list;
+ u16 nr_vports;
+ u16 vfid; /* Starting at 0 */
+ struct net_device *br_dev;
+ u16 vid;
+};
+
+struct mlxsw_sp_mid {
+ struct list_head list;
+ unsigned char addr[ETH_ALEN];
+ u16 vid;
+ u16 mid;
+ unsigned int ref_count;
+};
+
+static inline u16 mlxsw_sp_vfid_to_fid(u16 vfid)
+{
+ return MLXSW_SP_VFID_BASE + vfid;
+}
+
+static inline u16 mlxsw_sp_fid_to_vfid(u16 fid)
+{
+ return fid - MLXSW_SP_VFID_BASE;
+}
+
+static inline bool mlxsw_sp_fid_is_vfid(u16 fid)
+{
+ return fid >= MLXSW_SP_VFID_BASE;
+}
+
struct mlxsw_sp {
- unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
+ struct {
+ struct list_head list;
+ unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_PORT_MAX)];
+ } port_vfids;
+ struct {
+ struct list_head list;
+ unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_BR_MAX)];
+ } br_vfids;
+ struct {
+ struct list_head list;
+ unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_MID_MAX)];
+ } br_mids;
unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)];
struct mlxsw_sp_port **ports;
struct mlxsw_core *core;
@@ -63,12 +120,17 @@ struct mlxsw_sp {
} fdb_notify;
#define MLXSW_SP_DEFAULT_AGEING_TIME 300
u32 ageing_time;
- struct {
- struct net_device *dev;
- unsigned int ref_count;
- } master_bridge;
+ struct mutex fdb_lock; /* Make sure FDB sessions are atomic. */
+ struct mlxsw_sp_upper master_bridge;
+ struct mlxsw_sp_upper lags[MLXSW_SP_LAG_MAX];
};
+static inline struct mlxsw_sp_upper *
+mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
+{
+ return &mlxsw_sp->lags[lag_id];
+}
+
struct mlxsw_sp_port_pcpu_stats {
u64 rx_packets;
u64 rx_bytes;
@@ -87,15 +149,87 @@ struct mlxsw_sp_port {
u8 learning:1,
learning_sync:1,
uc_flood:1,
- bridged:1;
+ bridged:1,
+ lagged:1;
u16 pvid;
+ u16 lag_id;
+ struct {
+ struct list_head list;
+ struct mlxsw_sp_vfid *vfid;
+ u16 vid;
+ } vport;
/* 802.1Q bridge VLANs */
- unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ unsigned long *active_vlans;
+ unsigned long *untagged_vlans;
/* VLAN interfaces */
- unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
- u16 nr_vfids;
+ struct list_head vports_list;
};
+static inline struct mlxsw_sp_port *
+mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ u8 local_port;
+
+ local_port = mlxsw_core_lag_mapping_get(mlxsw_sp->core,
+ lag_id, port_index);
+ mlxsw_sp_port = mlxsw_sp->ports[local_port];
+ return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL;
+}
+
+static inline bool
+mlxsw_sp_port_is_vport(const struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ return mlxsw_sp_port->vport.vfid;
+}
+
+static inline struct net_device *
+mlxsw_sp_vport_br_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ return mlxsw_sp_vport->vport.vfid->br_dev;
+}
+
+static inline u16
+mlxsw_sp_vport_vid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ return mlxsw_sp_vport->vport.vid;
+}
+
+static inline u16
+mlxsw_sp_vport_vfid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ return mlxsw_sp_vport->vport.vfid->vfid;
+}
+
+static inline struct mlxsw_sp_port *
+mlxsw_sp_port_vport_find(const struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
+ vport.list) {
+ if (mlxsw_sp_vport_vid_get(mlxsw_sp_vport) == vid)
+ return mlxsw_sp_vport;
+ }
+
+ return NULL;
+}
+
+static inline struct mlxsw_sp_port *
+mlxsw_sp_port_vport_find_by_vfid(const struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 vfid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
+ vport.list) {
+ if (mlxsw_sp_vport_vfid_get(mlxsw_sp_vport) == vfid)
+ return mlxsw_sp_vport;
+ }
+
+ return NULL;
+}
+
enum mlxsw_sp_flood_table {
MLXSW_SP_FLOOD_TABLE_UC,
MLXSW_SP_FLOOD_TABLE_BM,
@@ -118,5 +252,7 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
u16 vid);
int mlxsw_sp_port_kill_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid);
+int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
+ bool set, bool only_uc);
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 617fb22b5d81..ffe894e6d287 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -51,12 +51,50 @@
#include "core.h"
#include "reg.h"
+static u16 mlxsw_sp_port_vid_to_fid_get(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 vid)
+{
+ u16 fid = vid;
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);
+
+ fid = mlxsw_sp_vfid_to_fid(vfid);
+ }
+
+ if (!fid)
+ fid = mlxsw_sp_port->pvid;
+
+ return fid;
+}
+
+static struct mlxsw_sp_port *
+mlxsw_sp_port_orig_get(struct net_device *dev,
+ struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ u16 vid;
+
+ if (!is_vlan_dev(dev))
+ return mlxsw_sp_port;
+
+ vid = vlan_dev_vlan_id(dev);
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ WARN_ON(!mlxsw_sp_vport);
+
+ return mlxsw_sp_vport;
+}
+
static int mlxsw_sp_port_attr_get(struct net_device *dev,
struct switchdev_attr *attr)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac);
@@ -105,8 +143,14 @@ static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
if (!spms_pl)
return -ENOMEM;
mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
- for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
+ } else {
+ for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
+ mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
+ }
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
kfree(spms_pl);
@@ -124,22 +168,38 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state);
}
+static bool mlxsw_sp_vfid_is_vport_br(u16 vfid)
+{
+ return vfid >= MLXSW_SP_VFID_PORT_MAX;
+}
+
static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
- u16 fid_begin, u16 fid_end, bool set,
+ u16 idx_begin, u16 idx_end, bool set,
bool only_uc)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- u16 range = fid_end - fid_begin + 1;
+ u16 local_port = mlxsw_sp_port->local_port;
+ enum mlxsw_flood_table_type table_type;
+ u16 range = idx_end - idx_begin + 1;
char *sftr_pl;
int err;
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
+ if (mlxsw_sp_vfid_is_vport_br(idx_begin))
+ local_port = mlxsw_sp_port->local_port;
+ else
+ local_port = MLXSW_PORT_CPU_PORT;
+ } else {
+ table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
+ }
+
sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
if (!sftr_pl)
return -ENOMEM;
- mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin,
- MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
- mlxsw_sp_port->local_port, set);
+ mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, idx_begin,
+ table_type, range, local_port, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
if (err)
goto buffer_out;
@@ -150,9 +210,8 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
if (only_uc)
goto buffer_out;
- mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin,
- MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
- mlxsw_sp_port->local_port, set);
+ mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, idx_begin,
+ table_type, range, local_port, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
buffer_out:
@@ -167,6 +226,13 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid, last_visited_vid;
int err;
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);
+
+ return __mlxsw_sp_port_flood_set(mlxsw_sp_port, vfid, vfid,
+ set, true);
+ }
+
for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, set,
true);
@@ -185,6 +251,16 @@ err_port_flood_set:
return err;
}
+int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
+ bool set, bool only_uc)
+{
+ /* In case of vFIDs, index into the flooding table is relative to
+ * the start of the vFIDs range.
+ */
+ return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set,
+ only_uc);
+}
+
static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_trans *trans,
unsigned long brport_flags)
@@ -193,6 +269,9 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool set;
int err;
+ if (!mlxsw_sp_port->bridged)
+ return -EINVAL;
+
if (switchdev_trans_ph_prepare(trans))
return 0;
@@ -237,6 +316,22 @@ static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
}
+static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct switchdev_trans *trans,
+ struct net_device *orig_dev,
+ bool vlan_enabled)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+ /* SWITCHDEV_TRANS_PREPARE phase */
+ if ((!vlan_enabled) && (mlxsw_sp->master_bridge.dev == orig_dev)) {
+ netdev_err(mlxsw_sp_port->dev, "Bridge must be vlan-aware\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int mlxsw_sp_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr,
struct switchdev_trans *trans)
@@ -244,6 +339,10 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans,
@@ -257,6 +356,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
attr->u.ageing_time);
break;
+ case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+ err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans,
+ attr->orig_dev,
+ attr->u.vlan_filtering);
+ break;
default:
err = -EOPNOTSUPP;
break;
@@ -304,7 +408,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{
enum mlxsw_reg_svfa_mt mt;
- if (mlxsw_sp_port->nr_vfids)
+ if (!list_empty(&mlxsw_sp_port->vports_list))
mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
else
mt = MLXSW_REG_SVFA_MT_VID_TO_FID;
@@ -316,7 +420,7 @@ static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{
enum mlxsw_reg_svfa_mt mt;
- if (!mlxsw_sp_port->nr_vfids)
+ if (list_empty(&mlxsw_sp_port->vports_list))
return 0;
mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
@@ -342,14 +446,35 @@ err_port_add_vid:
return err;
}
+static int __mlxsw_sp_port_vlans_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 vid_begin, u16 vid_end, bool is_member,
+ bool untagged)
+{
+ u16 vid, vid_e;
+ int err;
+
+ for (vid = vid_begin; vid <= vid_end;
+ vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
+ vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
+ vid_end);
+
+ err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e,
+ is_member, untagged);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid_begin, u16 vid_end,
bool flag_untagged, bool flag_pvid)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct net_device *dev = mlxsw_sp_port->dev;
+ u16 vid, last_visited_vid, old_pvid;
enum mlxsw_reg_svfa_mt mt;
- u16 vid, vid_e;
int err;
/* In case this is invoked with BRIDGE_FLAGS_SELF and port is
@@ -377,15 +502,18 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
if (err) {
netdev_err(dev, "Failed to create FID=VID=%d mapping\n",
vid);
- return err;
+ goto err_port_vid_to_fid_set;
}
}
+ }
- /* Set FID mapping according to port's mode */
+ /* Set FID mapping according to port's mode */
+ for (vid = vid_begin; vid <= vid_end; vid++) {
err = mlxsw_sp_port_fid_map(mlxsw_sp_port, vid);
if (err) {
netdev_err(dev, "Failed to map FID=%d", vid);
- return err;
+ last_visited_vid = --vid;
+ goto err_port_fid_map;
}
}
@@ -393,83 +521,133 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
true, false);
if (err) {
netdev_err(dev, "Failed to configure flooding\n");
- return err;
+ goto err_port_flood_set;
}
- for (vid = vid_begin; vid <= vid_end;
- vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
- vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
- vid_end);
-
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, true,
- flag_untagged);
- if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to add VIDs %d-%d\n",
- vid, vid_e);
- return err;
- }
+ err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end,
+ true, flag_untagged);
+ if (err) {
+ netdev_err(dev, "Unable to add VIDs %d-%d\n", vid_begin,
+ vid_end);
+ goto err_port_vlans_set;
}
- vid = vid_begin;
- if (flag_pvid && mlxsw_sp_port->pvid != vid) {
- err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid);
+ old_pvid = mlxsw_sp_port->pvid;
+ if (flag_pvid && old_pvid != vid_begin) {
+ err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid_begin);
if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to add PVID %d\n",
- vid);
- return err;
+ netdev_err(dev, "Unable to add PVID %d\n", vid_begin);
+ goto err_port_pvid_set;
}
- mlxsw_sp_port->pvid = vid;
+ mlxsw_sp_port->pvid = vid_begin;
}
/* Changing activity bits only if HW operation succeded */
- for (vid = vid_begin; vid <= vid_end; vid++)
+ for (vid = vid_begin; vid <= vid_end; vid++) {
set_bit(vid, mlxsw_sp_port->active_vlans);
+ if (flag_untagged)
+ set_bit(vid, mlxsw_sp_port->untagged_vlans);
+ else
+ clear_bit(vid, mlxsw_sp_port->untagged_vlans);
+ }
- return mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
- mlxsw_sp_port->stp_state);
+ /* STP state change must be done after we set active VLANs */
+ err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
+ mlxsw_sp_port->stp_state);
+ if (err) {
+ netdev_err(dev, "Failed to set STP state\n");
+ goto err_port_stp_state_set;
+ }
+
+ return 0;
+
+err_port_vid_to_fid_set:
+ mlxsw_sp_fid_destroy(mlxsw_sp, vid);
+ return err;
+
+err_port_stp_state_set:
+ for (vid = vid_begin; vid <= vid_end; vid++)
+ clear_bit(vid, mlxsw_sp_port->active_vlans);
+ if (old_pvid != mlxsw_sp_port->pvid)
+ mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
+err_port_pvid_set:
+ __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false,
+ false);
+err_port_vlans_set:
+ __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end, false,
+ false);
+err_port_flood_set:
+ last_visited_vid = vid_end;
+err_port_fid_map:
+ for (vid = last_visited_vid; vid >= vid_begin; vid--)
+ mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid);
+ return err;
}
static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans)
{
- bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
- bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+ bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
if (switchdev_trans_ph_prepare(trans))
return 0;
return __mlxsw_sp_port_vlans_add(mlxsw_sp_port,
vlan->vid_begin, vlan->vid_end,
- untagged_flag, pvid_flag);
+ flag_untagged, flag_pvid);
+}
+
+static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
+{
+ return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
+ MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
+}
+
+static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
+{
+ return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
+ MLXSW_REG_SFD_OP_WRITE_REMOVE;
}
-static int mlxsw_sp_port_fdb_op(struct mlxsw_sp_port *mlxsw_sp_port,
- const char *mac, u16 vid, bool adding,
- bool dynamic)
+static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port,
+ const char *mac, u16 fid, bool adding,
+ bool dynamic)
{
- enum mlxsw_reg_sfd_rec_policy policy;
- enum mlxsw_reg_sfd_op op;
char *sfd_pl;
int err;
- if (!vid)
- vid = mlxsw_sp_port->pvid;
+ sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
+ if (!sfd_pl)
+ return -ENOMEM;
+
+ mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
+ mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
+ mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
+ local_port);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
+ kfree(sfd_pl);
+
+ return err;
+}
+
+static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,
+ const char *mac, u16 fid, u16 lag_vid,
+ bool adding, bool dynamic)
+{
+ char *sfd_pl;
+ int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
if (!sfd_pl)
return -ENOMEM;
- policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
- MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
- op = adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
- MLXSW_REG_SFD_OP_WRITE_REMOVE;
- mlxsw_reg_sfd_pack(sfd_pl, op, 0);
- mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy,
- mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP,
- mlxsw_sp_port->local_port);
- err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd),
- sfd_pl);
+ mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
+ mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
+ mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
+ lag_vid, lag_id);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
kfree(sfd_pl);
return err;
@@ -480,11 +658,162 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid);
+ u16 lag_vid = 0;
+
+ if (switchdev_trans_ph_prepare(trans))
+ return 0;
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ }
+
+ if (!mlxsw_sp_port->lagged)
+ return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->local_port,
+ fdb->addr, fid, true, false);
+ else
+ return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->lag_id,
+ fdb->addr, fid, lag_vid,
+ true, false);
+}
+
+static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
+ u16 fid, u16 mid, bool adding)
+{
+ char *sfd_pl;
+ int err;
+
+ sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
+ if (!sfd_pl)
+ return -ENOMEM;
+
+ mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
+ mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid,
+ MLXSW_REG_SFD_REC_ACTION_NOP, mid);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
+ kfree(sfd_pl);
+ return err;
+}
+
+static int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mid,
+ bool add, bool clear_all_ports)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char *smid_pl;
+ int err, i;
+
+ smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
+ if (!smid_pl)
+ return -ENOMEM;
+
+ mlxsw_reg_smid_pack(smid_pl, mid, mlxsw_sp_port->local_port, add);
+ if (clear_all_ports) {
+ for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++)
+ if (mlxsw_sp->ports[i])
+ mlxsw_reg_smid_port_mask_set(smid_pl, i, 1);
+ }
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
+ kfree(smid_pl);
+ return err;
+}
+
+static struct mlxsw_sp_mid *__mlxsw_sp_mc_get(struct mlxsw_sp *mlxsw_sp,
+ const unsigned char *addr,
+ u16 vid)
+{
+ struct mlxsw_sp_mid *mid;
+
+ list_for_each_entry(mid, &mlxsw_sp->br_mids.list, list) {
+ if (ether_addr_equal(mid->addr, addr) && mid->vid == vid)
+ return mid;
+ }
+ return NULL;
+}
+
+static struct mlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp,
+ const unsigned char *addr,
+ u16 vid)
+{
+ struct mlxsw_sp_mid *mid;
+ u16 mid_idx;
+
+ mid_idx = find_first_zero_bit(mlxsw_sp->br_mids.mapped,
+ MLXSW_SP_MID_MAX);
+ if (mid_idx == MLXSW_SP_MID_MAX)
+ return NULL;
+
+ mid = kzalloc(sizeof(*mid), GFP_KERNEL);
+ if (!mid)
+ return NULL;
+
+ set_bit(mid_idx, mlxsw_sp->br_mids.mapped);
+ ether_addr_copy(mid->addr, addr);
+ mid->vid = vid;
+ mid->mid = mid_idx;
+ mid->ref_count = 0;
+ list_add_tail(&mid->list, &mlxsw_sp->br_mids.list);
+
+ return mid;
+}
+
+static int __mlxsw_sp_mc_dec_ref(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mid *mid)
+{
+ if (--mid->ref_count == 0) {
+ list_del(&mid->list);
+ clear_bit(mid->mid, mlxsw_sp->br_mids.mapped);
+ kfree(mid);
+ return 1;
+ }
+ return 0;
+}
+
+static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct switchdev_trans *trans)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct net_device *dev = mlxsw_sp_port->dev;
+ struct mlxsw_sp_mid *mid;
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid);
+ int err = 0;
+
if (switchdev_trans_ph_prepare(trans))
return 0;
- return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
- true, false);
+ mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, mdb->vid);
+ if (!mid) {
+ mid = __mlxsw_sp_mc_alloc(mlxsw_sp, mdb->addr, mdb->vid);
+ if (!mid) {
+ netdev_err(dev, "Unable to allocate MC group\n");
+ return -ENOMEM;
+ }
+ }
+ mid->ref_count++;
+
+ err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, true,
+ mid->ref_count == 1);
+ if (err) {
+ netdev_err(dev, "Unable to set SMID\n");
+ goto err_out;
+ }
+
+ if (mid->ref_count == 1) {
+ err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid->mid,
+ true);
+ if (err) {
+ netdev_err(dev, "Unable to set MC SFD\n");
+ goto err_out;
+ }
+ }
+
+ return 0;
+
+err_out:
+ __mlxsw_sp_mc_dec_ref(mlxsw_sp, mid);
+ return err;
}
static int mlxsw_sp_port_obj_add(struct net_device *dev,
@@ -494,8 +823,15 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
+ return 0;
+
err = mlxsw_sp_port_vlans_add(mlxsw_sp_port,
SWITCHDEV_OBJ_PORT_VLAN(obj),
trans);
@@ -505,6 +841,11 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
SWITCHDEV_OBJ_PORT_FDB(obj),
trans);
break;
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
+ SWITCHDEV_OBJ_PORT_MDB(obj),
+ trans);
+ break;
default:
err = -EOPNOTSUPP;
break;
@@ -532,7 +873,7 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid_begin, u16 vid_end, bool init)
{
struct net_device *dev = mlxsw_sp_port->dev;
- u16 vid, vid_e;
+ u16 vid, pvid;
int err;
/* In case this is invoked with BRIDGE_FLAGS_SELF and port is
@@ -542,30 +883,23 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
if (!init && !mlxsw_sp_port->bridged)
return mlxsw_sp_port_kill_vids(dev, vid_begin, vid_end);
- for (vid = vid_begin; vid <= vid_end;
- vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
- vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
- vid_end);
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false,
- false);
- if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to del VIDs %d-%d\n",
- vid, vid_e);
- return err;
- }
+ err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end,
+ false, false);
+ if (err) {
+ netdev_err(dev, "Unable to del VIDs %d-%d\n", vid_begin,
+ vid_end);
+ return err;
}
- if ((mlxsw_sp_port->pvid >= vid_begin) &&
- (mlxsw_sp_port->pvid <= vid_end)) {
+ pvid = mlxsw_sp_port->pvid;
+ if (pvid >= vid_begin && pvid <= vid_end && pvid != 1) {
/* Default VLAN is always 1 */
- mlxsw_sp_port->pvid = 1;
- err = mlxsw_sp_port_pvid_set(mlxsw_sp_port,
- mlxsw_sp_port->pvid);
+ err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to del PVID %d\n",
- vid);
+ netdev_err(dev, "Unable to del PVID %d\n", pvid);
return err;
}
+ mlxsw_sp_port->pvid = 1;
}
if (init)
@@ -606,8 +940,54 @@ static int
mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
const struct switchdev_obj_port_fdb *fdb)
{
- return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
- false, false);
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid);
+ u16 lag_vid = 0;
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ }
+
+ if (!mlxsw_sp_port->lagged)
+ return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->local_port,
+ fdb->addr, fid,
+ false, false);
+ else
+ return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->lag_id,
+ fdb->addr, fid, lag_vid,
+ false, false);
+}
+
+static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port,
+ const struct switchdev_obj_port_mdb *mdb)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct net_device *dev = mlxsw_sp_port->dev;
+ struct mlxsw_sp_mid *mid;
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid);
+ u16 mid_idx;
+ int err = 0;
+
+ mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, mdb->vid);
+ if (!mid) {
+ netdev_err(dev, "Unable to remove port from MC DB\n");
+ return -EINVAL;
+ }
+
+ err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false, false);
+ if (err)
+ netdev_err(dev, "Unable to remove port from SMID\n");
+
+ mid_idx = mid->mid;
+ if (__mlxsw_sp_mc_dec_ref(mlxsw_sp, mid)) {
+ err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid_idx,
+ false);
+ if (err)
+ netdev_err(dev, "Unable to remove MC SFD\n");
+ }
+
+ return err;
}
static int mlxsw_sp_port_obj_del(struct net_device *dev,
@@ -616,8 +996,15 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
+ return 0;
+
err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
SWITCHDEV_OBJ_PORT_VLAN(obj));
break;
@@ -625,6 +1012,9 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port,
SWITCHDEV_OBJ_PORT_FDB(obj));
break;
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ err = mlxsw_sp_port_mdb_del(mlxsw_sp_port,
+ SWITCHDEV_OBJ_PORT_MDB(obj));
default:
err = -EOPNOTSUPP;
break;
@@ -633,14 +1023,31 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
return err;
}
+static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
+ u16 lag_id)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ int i;
+
+ for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) {
+ mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i);
+ if (mlxsw_sp_port)
+ return mlxsw_sp_port;
+ }
+ return NULL;
+}
+
static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_obj_port_fdb *fdb,
switchdev_obj_dump_cb_t *cb)
{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ u16 vport_vid = 0, vport_fid = 0;
char *sfd_pl;
char mac[ETH_ALEN];
- u16 vid;
+ u16 fid;
u8 local_port;
+ u16 lag_id;
u8 num_rec;
int stored_err = 0;
int i;
@@ -650,11 +1057,19 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
if (!sfd_pl)
return -ENOMEM;
+ mutex_lock(&mlxsw_sp_port->mlxsw_sp->fdb_lock);
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ u16 tmp;
+
+ tmp = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);
+ vport_fid = mlxsw_sp_vfid_to_fid(tmp);
+ vport_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ }
+
mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
do {
mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
- err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core,
- MLXSW_REG(sfd), sfd_pl);
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
if (err)
goto out;
@@ -669,21 +1084,46 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
for (i = 0; i < num_rec; i++) {
switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) {
case MLXSW_REG_SFD_REC_TYPE_UNICAST:
- mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &vid,
+ mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &fid,
&local_port);
if (local_port == mlxsw_sp_port->local_port) {
+ if (vport_fid && vport_fid != fid)
+ continue;
+ else if (vport_fid)
+ fdb->vid = vport_vid;
+ else
+ fdb->vid = fid;
+ ether_addr_copy(fdb->addr, mac);
+ fdb->ndm_state = NUD_REACHABLE;
+ err = cb(&fdb->obj);
+ if (err)
+ stored_err = err;
+ }
+ break;
+ case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG:
+ mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i,
+ mac, &fid, &lag_id);
+ if (mlxsw_sp_port ==
+ mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id)) {
+ if (vport_fid && vport_fid != fid)
+ continue;
+ else if (vport_fid)
+ fdb->vid = vport_vid;
+ else
+ fdb->vid = fid;
ether_addr_copy(fdb->addr, mac);
fdb->ndm_state = NUD_REACHABLE;
- fdb->vid = vid;
err = cb(&fdb->obj);
if (err)
stored_err = err;
}
+ break;
}
}
} while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
out:
+ mutex_unlock(&mlxsw_sp_port->mlxsw_sp->fdb_lock);
kfree(sfd_pl);
return stored_err ? stored_err : err;
}
@@ -695,10 +1135,19 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid;
int err = 0;
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ vlan->flags = 0;
+ vlan->vid_begin = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ vlan->vid_end = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ return cb(&vlan->obj);
+ }
+
for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
vlan->flags = 0;
if (vid == mlxsw_sp_port->pvid)
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
+ if (test_bit(vid, mlxsw_sp_port->untagged_vlans))
+ vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
vlan->vid_begin = vid;
vlan->vid_end = vid;
err = cb(&vlan->obj);
@@ -715,6 +1164,10 @@ static int mlxsw_sp_port_obj_dump(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port,
@@ -740,6 +1193,21 @@ static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
.switchdev_port_obj_dump = mlxsw_sp_port_obj_dump,
};
+static void mlxsw_sp_fdb_call_notifiers(bool learning, bool learning_sync,
+ bool adding, char *mac, u16 vid,
+ struct net_device *dev)
+{
+ struct switchdev_notifier_fdb_info info;
+ unsigned long notifier_type;
+
+ if (learning && learning_sync) {
+ info.addr = mac;
+ info.vid = vid;
+ notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
+ call_switchdev_notifiers(notifier_type, dev, &info.info);
+ }
+}
+
static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
char *sfn_pl, int rec_index,
bool adding)
@@ -747,34 +1215,119 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port;
char mac[ETH_ALEN];
u8 local_port;
- u16 vid;
+ u16 vid, fid;
+ bool do_notification = true;
int err;
- mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &vid, &local_port);
+ mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port);
mlxsw_sp_port = mlxsw_sp->ports[local_port];
if (!mlxsw_sp_port) {
dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n");
- return;
+ goto just_remove;
+ }
+
+ if (mlxsw_sp_fid_is_vfid(fid)) {
+ u16 vfid = mlxsw_sp_fid_to_vfid(fid);
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port,
+ vfid);
+ if (!mlxsw_sp_vport) {
+ netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
+ goto just_remove;
+ }
+ vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ /* Override the physical port with the vPort. */
+ mlxsw_sp_port = mlxsw_sp_vport;
+ } else {
+ vid = fid;
}
- err = mlxsw_sp_port_fdb_op(mlxsw_sp_port, mac, vid,
- adding && mlxsw_sp_port->learning, true);
+ adding = adding && mlxsw_sp_port->learning;
+
+do_fdb_op:
+ err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
+ adding, true);
if (err) {
if (net_ratelimit())
netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
return;
}
- if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) {
- struct switchdev_notifier_fdb_info info;
- unsigned long notifier_type;
+ if (!do_notification)
+ return;
+ mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
+ mlxsw_sp_port->learning_sync,
+ adding, mac, vid, mlxsw_sp_port->dev);
+ return;
+
+just_remove:
+ adding = false;
+ do_notification = false;
+ goto do_fdb_op;
+}
- info.addr = mac;
- info.vid = vid;
- notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
- call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev,
- &info.info);
+static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
+ char *sfn_pl, int rec_index,
+ bool adding)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ char mac[ETH_ALEN];
+ u16 lag_vid = 0;
+ u16 lag_id;
+ u16 vid, fid;
+ bool do_notification = true;
+ int err;
+
+ mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id);
+ mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
+ if (!mlxsw_sp_port) {
+ dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n");
+ goto just_remove;
}
+
+ if (mlxsw_sp_fid_is_vfid(fid)) {
+ u16 vfid = mlxsw_sp_fid_to_vfid(fid);
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port,
+ vfid);
+ if (!mlxsw_sp_vport) {
+ netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
+ goto just_remove;
+ }
+
+ vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ lag_vid = vid;
+ /* Override the physical port with the vPort. */
+ mlxsw_sp_port = mlxsw_sp_vport;
+ } else {
+ vid = fid;
+ }
+
+ adding = adding && mlxsw_sp_port->learning;
+
+do_fdb_op:
+ err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
+ adding, true);
+ if (err) {
+ if (net_ratelimit())
+ netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
+ return;
+ }
+
+ if (!do_notification)
+ return;
+ mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
+ mlxsw_sp_port->learning_sync,
+ adding, mac, vid,
+ mlxsw_sp_lag_get(mlxsw_sp, lag_id)->dev);
+ return;
+
+just_remove:
+ adding = false;
+ do_notification = false;
+ goto do_fdb_op;
}
static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
@@ -789,6 +1342,14 @@ static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
rec_index, false);
break;
+ case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG:
+ mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
+ rec_index, true);
+ break;
+ case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG:
+ mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
+ rec_index, false);
+ break;
}
}
@@ -812,6 +1373,7 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work);
+ mutex_lock(&mlxsw_sp->fdb_lock);
do {
mlxsw_reg_sfn_pack(sfn_pl);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
@@ -824,6 +1386,7 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
} while (num_rec);
+ mutex_unlock(&mlxsw_sp->fdb_lock);
kfree(sfn_pl);
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
@@ -838,6 +1401,7 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n");
return err;
}
+ mutex_init(&mlxsw_sp->fdb_lock);
INIT_DELAYED_WORK(&mlxsw_sp->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
mlxsw_sp->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
@@ -877,7 +1441,8 @@ int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port)
* with VID 1.
*/
mlxsw_sp_port->pvid = 1;
- err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true);
+ err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID - 1,
+ true);
if (err) {
netdev_err(dev, "Unable to init VLANs\n");
return err;
diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c
index 2056b719c262..7df318346b05 100644
--- a/drivers/net/ethernet/microchip/encx24j600.c
+++ b/drivers/net/ethernet/microchip/encx24j600.c
@@ -600,22 +600,11 @@ static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv)
static int encx24j600_hw_init(struct encx24j600_priv *priv)
{
- struct net_device *dev = priv->ndev;
int ret = 0;
- u16 eidled;
u16 macon2;
priv->hw_enabled = false;
- eidled = encx24j600_read_reg(priv, EIDLED);
- if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) {
- ret = -EINVAL;
- goto err_out;
- }
-
- netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n",
- (eidled & REVID_MASK) >> REVID_SHIFT);
-
/* PHY Leds: link status,
* LEDA: Link State + collision events
* LEDB: Link State + transmit/receive events
@@ -655,7 +644,6 @@ static int encx24j600_hw_init(struct encx24j600_priv *priv)
if (netif_msg_hw(priv))
encx24j600_dump_config(priv, "Hw is initialized");
-err_out:
return ret;
}
@@ -1004,6 +992,7 @@ static int encx24j600_spi_probe(struct spi_device *spi)
struct net_device *ndev;
struct encx24j600_priv *priv;
+ u16 eidled;
ndev = alloc_etherdev(sizeof(struct encx24j600_priv));
@@ -1072,10 +1061,21 @@ static int encx24j600_spi_probe(struct spi_device *spi)
goto out_free;
}
+ eidled = encx24j600_read_reg(priv, EIDLED);
+ if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) {
+ ret = -EINVAL;
+ goto out_unregister;
+ }
+
+ netif_info(priv, probe, ndev, "Silicon rev ID: 0x%02x\n",
+ (eidled & REVID_MASK) >> REVID_SHIFT);
+
netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr);
return ret;
+out_unregister:
+ unregister_netdev(priv->ndev);
out_free:
free_netdev(ndev);
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index 83651ac8ddb9..270c9eeb7ab6 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -1488,7 +1488,6 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum)
}
myri10ge_vlan_rx(mgp->dev, va, skb);
skb_record_rx_queue(skb, ss - &mgp->ss[0]);
- skb_mark_napi_id(skb, &ss->napi);
if (polling) {
int hlen;
@@ -1506,6 +1505,7 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum)
skb->data_len -= hlen;
skb->tail += hlen;
skb->protocol = eth_type_trans(skb, dev);
+ skb_mark_napi_id(skb, &ss->napi);
netif_receive_skb(skb);
}
else
@@ -3814,7 +3814,6 @@ static int myri10ge_alloc_slices(struct myri10ge_priv *mgp)
ss->dev = mgp->dev;
netif_napi_add(ss->dev, &ss->napi, myri10ge_poll,
myri10ge_napi_weight);
- napi_hash_add(&ss->napi);
}
return 0;
abort:
diff --git a/drivers/net/ethernet/netronome/Kconfig b/drivers/net/ethernet/netronome/Kconfig
new file mode 100644
index 000000000000..9508ad782c30
--- /dev/null
+++ b/drivers/net/ethernet/netronome/Kconfig
@@ -0,0 +1,36 @@
+#
+# Netronome device configuration
+#
+
+config NET_VENDOR_NETRONOME
+ bool "Netronome(R) devices"
+ default y
+ ---help---
+ If you have a Netronome(R) network (Ethernet) card or device, say Y.
+
+ 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 Netronome(R) cards. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_NETRONOME
+
+config NFP_NETVF
+ tristate "Netronome(R) NFP4000/NFP6000 VF NIC driver"
+ depends on PCI && PCI_MSI
+ depends on VXLAN || VXLAN=n
+ ---help---
+ This driver supports SR-IOV virtual functions of
+ the Netronome(R) NFP4000/NFP6000 cards working as
+ a advanced Ethernet NIC.
+
+config NFP_NET_DEBUG
+ bool "Debug support for Netronome(R) NFP3200/NFP6000 NIC drivers"
+ depends on NFP_NET || NFP_NETVF
+ ---help---
+ Enable extra sanity checks and debugfs support in
+ Netronome(R) NFP3200/NFP6000 NIC PF and VF drivers.
+ Note: selecting this option may adversely impact
+ performance.
+
+endif
diff --git a/drivers/net/ethernet/netronome/Makefile b/drivers/net/ethernet/netronome/Makefile
new file mode 100644
index 000000000000..dcb7b383f634
--- /dev/null
+++ b/drivers/net/ethernet/netronome/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Netronome network device drivers
+#
+
+obj-$(CONFIG_NFP_NETVF) += nfp/
diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile
new file mode 100644
index 000000000000..68178819ff12
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_NFP_NETVF) += nfp_netvf.o
+
+nfp_netvf-objs := \
+ nfp_net_common.o \
+ nfp_net_ethtool.o \
+ nfp_netvf_main.o
+
+nfp_netvf-$(CONFIG_NFP_NET_DEBUG) += nfp_net_debugfs.o
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
new file mode 100644
index 000000000000..ab264e1bccd0
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * nfp_net.h
+ * Declarations for Netronome network device driver.
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ */
+
+#ifndef _NFP_NET_H_
+#define _NFP_NET_H_
+
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <asm-generic/io-64-nonatomic-hi-lo.h>
+
+#include "nfp_net_ctrl.h"
+
+#define nn_err(nn, fmt, args...) netdev_err((nn)->netdev, fmt, ## args)
+#define nn_warn(nn, fmt, args...) netdev_warn((nn)->netdev, fmt, ## args)
+#define nn_info(nn, fmt, args...) netdev_info((nn)->netdev, fmt, ## args)
+#define nn_dbg(nn, fmt, args...) netdev_dbg((nn)->netdev, fmt, ## args)
+#define nn_warn_ratelimit(nn, fmt, args...) \
+ do { \
+ if (unlikely(net_ratelimit())) \
+ netdev_warn((nn)->netdev, fmt, ## args); \
+ } while (0)
+
+/* Max time to wait for NFP to respond on updates (in ms) */
+#define NFP_NET_POLL_TIMEOUT 5000
+
+/* Bar allocation */
+#define NFP_NET_CRTL_BAR 0
+#define NFP_NET_Q0_BAR 2
+#define NFP_NET_Q1_BAR 4 /* OBSOLETE */
+
+/* Max bits in DMA address */
+#define NFP_NET_MAX_DMA_BITS 40
+
+/* Default size for MTU and freelist buffer sizes */
+#define NFP_NET_DEFAULT_MTU 1500
+#define NFP_NET_DEFAULT_RX_BUFSZ 2048
+
+/* Maximum number of bytes prepended to a packet */
+#define NFP_NET_MAX_PREPEND 64
+
+/* Interrupt definitions */
+#define NFP_NET_NON_Q_VECTORS 2
+#define NFP_NET_IRQ_LSC_IDX 0
+#define NFP_NET_IRQ_EXN_IDX 1
+
+/* Queue/Ring definitions */
+#define NFP_NET_MAX_TX_RINGS 64 /* Max. # of Tx rings per device */
+#define NFP_NET_MAX_RX_RINGS 64 /* Max. # of Rx rings per device */
+
+#define NFP_NET_MIN_TX_DESCS 256 /* Min. # of Tx descs per ring */
+#define NFP_NET_MIN_RX_DESCS 256 /* Min. # of Rx descs per ring */
+#define NFP_NET_MAX_TX_DESCS (256 * 1024) /* Max. # of Tx descs per ring */
+#define NFP_NET_MAX_RX_DESCS (256 * 1024) /* Max. # of Rx descs per ring */
+
+#define NFP_NET_TX_DESCS_DEFAULT 4096 /* Default # of Tx descs per ring */
+#define NFP_NET_RX_DESCS_DEFAULT 4096 /* Default # of Rx descs per ring */
+
+#define NFP_NET_FL_BATCH 16 /* Add freelist in this Batch size */
+
+/* Offload definitions */
+#define NFP_NET_N_VXLAN_PORTS (NFP_NET_CFG_VXLAN_SZ / sizeof(__be16))
+
+/* Forward declarations */
+struct nfp_net;
+struct nfp_net_r_vector;
+
+/* Convenience macro for writing dma address into RX/TX descriptors */
+#define nfp_desc_set_dma_addr(desc, dma_addr) \
+ do { \
+ __typeof(desc) __d = (desc); \
+ dma_addr_t __addr = (dma_addr); \
+ \
+ __d->dma_addr_lo = cpu_to_le32(lower_32_bits(__addr)); \
+ __d->dma_addr_hi = upper_32_bits(__addr) & 0xff; \
+ } while (0)
+
+/* TX descriptor format */
+
+#define PCIE_DESC_TX_EOP BIT(7)
+#define PCIE_DESC_TX_OFFSET_MASK GENMASK(6, 0)
+#define PCIE_DESC_TX_MSS_MASK GENMASK(13, 0)
+
+/* Flags in the host TX descriptor */
+#define PCIE_DESC_TX_CSUM BIT(7)
+#define PCIE_DESC_TX_IP4_CSUM BIT(6)
+#define PCIE_DESC_TX_TCP_CSUM BIT(5)
+#define PCIE_DESC_TX_UDP_CSUM BIT(4)
+#define PCIE_DESC_TX_VLAN BIT(3)
+#define PCIE_DESC_TX_LSO BIT(2)
+#define PCIE_DESC_TX_ENCAP BIT(1)
+#define PCIE_DESC_TX_O_IP4_CSUM BIT(0)
+
+struct nfp_net_tx_desc {
+ union {
+ struct {
+ u8 dma_addr_hi; /* High bits of host buf address */
+ __le16 dma_len; /* Length to DMA for this desc */
+ u8 offset_eop; /* Offset in buf where pkt starts +
+ * highest bit is eop flag.
+ */
+ __le32 dma_addr_lo; /* Low 32bit of host buf addr */
+
+ __le16 mss; /* MSS to be used for LSO */
+ u8 l4_offset; /* LSO, where the L4 data starts */
+ u8 flags; /* TX Flags, see @PCIE_DESC_TX_* */
+
+ __le16 vlan; /* VLAN tag to add if indicated */
+ __le16 data_len; /* Length of frame + meta data */
+ } __packed;
+ __le32 vals[4];
+ };
+};
+
+/**
+ * struct nfp_net_tx_buf - software TX buffer descriptor
+ * @skb: sk_buff associated with this buffer
+ * @dma_addr: DMA mapping address of the buffer
+ * @fidx: Fragment index (-1 for the head and [0..nr_frags-1] for frags)
+ * @pkt_cnt: Number of packets to be produced out of the skb associated
+ * with this buffer (valid only on the head's buffer).
+ * Will be 1 for all non-TSO packets.
+ * @real_len: Number of bytes which to be produced out of the skb (valid only
+ * on the head's buffer). Equal to skb->len for non-TSO packets.
+ */
+struct nfp_net_tx_buf {
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ short int fidx;
+ u16 pkt_cnt;
+ u32 real_len;
+};
+
+/**
+ * struct nfp_net_tx_ring - TX ring structure
+ * @r_vec: Back pointer to ring vector structure
+ * @idx: Ring index from Linux's perspective
+ * @qcidx: Queue Controller Peripheral (QCP) queue index for the TX queue
+ * @qcp_q: Pointer to base of the QCP TX queue
+ * @cnt: Size of the queue in number of descriptors
+ * @wr_p: TX ring write pointer (free running)
+ * @rd_p: TX ring read pointer (free running)
+ * @qcp_rd_p: Local copy of QCP TX queue read pointer
+ * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer
+ * (used for .xmit_more delayed kick)
+ * @txbufs: Array of transmitted TX buffers, to free on transmit
+ * @txds: Virtual address of TX ring in host memory
+ * @dma: DMA address of the TX ring
+ * @size: Size, in bytes, of the TX ring (needed to free)
+ */
+struct nfp_net_tx_ring {
+ struct nfp_net_r_vector *r_vec;
+
+ u32 idx;
+ int qcidx;
+ u8 __iomem *qcp_q;
+
+ u32 cnt;
+ u32 wr_p;
+ u32 rd_p;
+ u32 qcp_rd_p;
+
+ u32 wr_ptr_add;
+
+ struct nfp_net_tx_buf *txbufs;
+ struct nfp_net_tx_desc *txds;
+
+ dma_addr_t dma;
+ unsigned int size;
+} ____cacheline_aligned;
+
+/* RX and freelist descriptor format */
+
+#define PCIE_DESC_RX_DD BIT(7)
+#define PCIE_DESC_RX_META_LEN_MASK GENMASK(6, 0)
+
+/* Flags in the RX descriptor */
+#define PCIE_DESC_RX_RSS cpu_to_le16(BIT(15))
+#define PCIE_DESC_RX_I_IP4_CSUM cpu_to_le16(BIT(14))
+#define PCIE_DESC_RX_I_IP4_CSUM_OK cpu_to_le16(BIT(13))
+#define PCIE_DESC_RX_I_TCP_CSUM cpu_to_le16(BIT(12))
+#define PCIE_DESC_RX_I_TCP_CSUM_OK cpu_to_le16(BIT(11))
+#define PCIE_DESC_RX_I_UDP_CSUM cpu_to_le16(BIT(10))
+#define PCIE_DESC_RX_I_UDP_CSUM_OK cpu_to_le16(BIT(9))
+#define PCIE_DESC_RX_SPARE cpu_to_le16(BIT(8))
+#define PCIE_DESC_RX_EOP cpu_to_le16(BIT(7))
+#define PCIE_DESC_RX_IP4_CSUM cpu_to_le16(BIT(6))
+#define PCIE_DESC_RX_IP4_CSUM_OK cpu_to_le16(BIT(5))
+#define PCIE_DESC_RX_TCP_CSUM cpu_to_le16(BIT(4))
+#define PCIE_DESC_RX_TCP_CSUM_OK cpu_to_le16(BIT(3))
+#define PCIE_DESC_RX_UDP_CSUM cpu_to_le16(BIT(2))
+#define PCIE_DESC_RX_UDP_CSUM_OK cpu_to_le16(BIT(1))
+#define PCIE_DESC_RX_VLAN cpu_to_le16(BIT(0))
+
+#define PCIE_DESC_RX_CSUM_ALL (PCIE_DESC_RX_IP4_CSUM | \
+ PCIE_DESC_RX_TCP_CSUM | \
+ PCIE_DESC_RX_UDP_CSUM | \
+ PCIE_DESC_RX_I_IP4_CSUM | \
+ PCIE_DESC_RX_I_TCP_CSUM | \
+ PCIE_DESC_RX_I_UDP_CSUM)
+#define PCIE_DESC_RX_CSUM_OK_SHIFT 1
+#define __PCIE_DESC_RX_CSUM_ALL le16_to_cpu(PCIE_DESC_RX_CSUM_ALL)
+#define __PCIE_DESC_RX_CSUM_ALL_OK (__PCIE_DESC_RX_CSUM_ALL >> \
+ PCIE_DESC_RX_CSUM_OK_SHIFT)
+
+struct nfp_net_rx_desc {
+ union {
+ struct {
+ u8 dma_addr_hi; /* High bits of the buf address */
+ __le16 reserved; /* Must be zero */
+ u8 meta_len_dd; /* Must be zero */
+
+ __le32 dma_addr_lo; /* Low bits of the buffer address */
+ } __packed fld;
+
+ struct {
+ __le16 data_len; /* Length of the frame + meta data */
+ u8 reserved;
+ u8 meta_len_dd; /* Length of meta data prepended +
+ * descriptor done flag.
+ */
+
+ __le16 flags; /* RX flags. See @PCIE_DESC_RX_* */
+ __le16 vlan; /* VLAN if stripped */
+ } __packed rxd;
+
+ __le32 vals[2];
+ };
+};
+
+struct nfp_net_rx_hash {
+ __be32 hash_type;
+ __be32 hash;
+};
+
+/**
+ * struct nfp_net_rx_buf - software RX buffer descriptor
+ * @skb: sk_buff associated with this buffer
+ * @dma_addr: DMA mapping address of the buffer
+ */
+struct nfp_net_rx_buf {
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+};
+
+/**
+ * struct nfp_net_rx_ring - RX ring structure
+ * @r_vec: Back pointer to ring vector structure
+ * @cnt: Size of the queue in number of descriptors
+ * @wr_p: FL/RX ring write pointer (free running)
+ * @rd_p: FL/RX ring read pointer (free running)
+ * @idx: Ring index from Linux's perspective
+ * @fl_qcidx: Queue Controller Peripheral (QCP) queue index for the freelist
+ * @rx_qcidx: Queue Controller Peripheral (QCP) queue index for the RX queue
+ * @qcp_fl: Pointer to base of the QCP freelist queue
+ * @qcp_rx: Pointer to base of the QCP RX queue
+ * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer
+ * (used for free list batching)
+ * @rxbufs: Array of transmitted FL/RX buffers
+ * @rxds: Virtual address of FL/RX ring in host memory
+ * @dma: DMA address of the FL/RX ring
+ * @size: Size, in bytes, of the FL/RX ring (needed to free)
+ */
+struct nfp_net_rx_ring {
+ struct nfp_net_r_vector *r_vec;
+
+ u32 cnt;
+ u32 wr_p;
+ u32 rd_p;
+
+ u16 idx;
+ u16 wr_ptr_add;
+
+ int fl_qcidx;
+ int rx_qcidx;
+ u8 __iomem *qcp_fl;
+ u8 __iomem *qcp_rx;
+
+ struct nfp_net_rx_buf *rxbufs;
+ struct nfp_net_rx_desc *rxds;
+
+ dma_addr_t dma;
+ unsigned int size;
+} ____cacheline_aligned;
+
+/**
+ * struct nfp_net_r_vector - Per ring interrupt vector configuration
+ * @nfp_net: Backpointer to nfp_net structure
+ * @napi: NAPI structure for this ring vec
+ * @tx_ring: Pointer to TX ring
+ * @rx_ring: Pointer to RX ring
+ * @irq_idx: Index into MSI-X table
+ * @rx_sync: Seqlock for atomic updates of RX stats
+ * @rx_pkts: Number of received packets
+ * @rx_bytes: Number of received bytes
+ * @rx_drops: Number of packets dropped on RX due to lack of resources
+ * @hw_csum_rx_ok: Counter of packets where the HW checksum was OK
+ * @hw_csum_rx_inner_ok: Counter of packets where the inner HW checksum was OK
+ * @hw_csum_rx_error: Counter of packets with bad checksums
+ * @tx_sync: Seqlock for atomic updates of TX stats
+ * @tx_pkts: Number of Transmitted packets
+ * @tx_bytes: Number of Transmitted bytes
+ * @hw_csum_tx: Counter of packets with TX checksum offload requested
+ * @hw_csum_tx_inner: Counter of inner TX checksum offload requests
+ * @tx_gather: Counter of packets with Gather DMA
+ * @tx_lso: Counter of LSO packets sent
+ * @tx_errors: How many TX errors were encountered
+ * @tx_busy: How often was TX busy (no space)?
+ * @handler: Interrupt handler for this ring vector
+ * @name: Name of the interrupt vector
+ * @affinity_mask: SMP affinity mask for this vector
+ *
+ * This structure ties RX and TX rings to interrupt vectors and a NAPI
+ * context. This currently only supports one RX and TX ring per
+ * interrupt vector but might be extended in the future to allow
+ * association of multiple rings per vector.
+ */
+struct nfp_net_r_vector {
+ struct nfp_net *nfp_net;
+ struct napi_struct napi;
+
+ struct nfp_net_tx_ring *tx_ring;
+ struct nfp_net_rx_ring *rx_ring;
+
+ int irq_idx;
+
+ struct u64_stats_sync rx_sync;
+ u64 rx_pkts;
+ u64 rx_bytes;
+ u64 rx_drops;
+ u64 hw_csum_rx_ok;
+ u64 hw_csum_rx_inner_ok;
+ u64 hw_csum_rx_error;
+
+ struct u64_stats_sync tx_sync;
+ u64 tx_pkts;
+ u64 tx_bytes;
+ u64 hw_csum_tx;
+ u64 hw_csum_tx_inner;
+ u64 tx_gather;
+ u64 tx_lso;
+ u64 tx_errors;
+ u64 tx_busy;
+
+ irq_handler_t handler;
+ char name[IFNAMSIZ + 8];
+ cpumask_t affinity_mask;
+} ____cacheline_aligned;
+
+/* Firmware version as it is written in the 32bit value in the BAR */
+struct nfp_net_fw_version {
+ u8 minor;
+ u8 major;
+ u8 class;
+ u8 resv;
+} __packed;
+
+static inline bool nfp_net_fw_ver_eq(struct nfp_net_fw_version *fw_ver,
+ u8 resv, u8 class, u8 major, u8 minor)
+{
+ return fw_ver->resv == resv &&
+ fw_ver->class == class &&
+ fw_ver->major == major &&
+ fw_ver->minor == minor;
+}
+
+/**
+ * struct nfp_net - NFP network device structure
+ * @pdev: Backpointer to PCI device
+ * @netdev: Backpointer to net_device structure
+ * @nfp_fallback: Is the driver used in fallback mode?
+ * @is_vf: Is the driver attached to a VF?
+ * @is_nfp3200: Is the driver for a NFP-3200 card?
+ * @fw_loaded: Is the firmware loaded?
+ * @ctrl: Local copy of the control register/word.
+ * @fl_bufsz: Currently configured size of the freelist buffers
+ * @rx_offset: Offset in the RX buffers where packet data starts
+ * @cpp: Pointer to the CPP handle
+ * @nfp_dev_cpp: Pointer to the NFP Device handle
+ * @ctrl_area: Pointer to the CPP area for the control BAR
+ * @tx_area: Pointer to the CPP area for the TX queues
+ * @rx_area: Pointer to the CPP area for the FL/RX queues
+ * @fw_ver: Firmware version
+ * @cap: Capabilities advertised by the Firmware
+ * @max_mtu: Maximum support MTU advertised by the Firmware
+ * @rss_cfg: RSS configuration
+ * @rss_key: RSS secret key
+ * @rss_itbl: RSS indirection table
+ * @max_tx_rings: Maximum number of TX rings supported by the Firmware
+ * @max_rx_rings: Maximum number of RX rings supported by the Firmware
+ * @num_tx_rings: Currently configured number of TX rings
+ * @num_rx_rings: Currently configured number of RX rings
+ * @txd_cnt: Size of the TX ring in number of descriptors
+ * @rxd_cnt: Size of the RX ring in number of descriptors
+ * @tx_rings: Array of pre-allocated TX ring structures
+ * @rx_rings: Array of pre-allocated RX ring structures
+ * @num_irqs: Number of allocated interrupt vectors
+ * @num_r_vecs: Number of used ring vectors
+ * @r_vecs: Pre-allocated array of ring vectors
+ * @irq_entries: Pre-allocated array of MSI-X entries
+ * @lsc_handler: Handler for Link State Change interrupt
+ * @lsc_name: Name for Link State Change interrupt
+ * @exn_handler: Handler for Exception interrupt
+ * @exn_name: Name for Exception interrupt
+ * @shared_handler: Handler for shared interrupts
+ * @shared_name: Name for shared interrupt
+ * @me_freq_mhz: ME clock_freq (MHz)
+ * @reconfig_lock: Protects HW reconfiguration request regs/machinery
+ * @link_up: Is the link up?
+ * @link_status_lock: Protects @link_up and ensures atomicity with BAR reading
+ * @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter
+ * @rx_coalesce_max_frames: RX interrupt moderation frame count parameter
+ * @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter
+ * @tx_coalesce_max_frames: TX interrupt moderation frame count parameter
+ * @vxlan_ports: VXLAN ports for RX inner csum offload communicated to HW
+ * @vxlan_usecnt: IPv4/IPv6 VXLAN port use counts
+ * @qcp_cfg: Pointer to QCP queue used for configuration notification
+ * @ctrl_bar: Pointer to mapped control BAR
+ * @tx_bar: Pointer to mapped TX queues
+ * @rx_bar: Pointer to mapped FL/RX queues
+ * @debugfs_dir: Device directory in debugfs
+ */
+struct nfp_net {
+ struct pci_dev *pdev;
+ struct net_device *netdev;
+
+ unsigned nfp_fallback:1;
+ unsigned is_vf:1;
+ unsigned is_nfp3200:1;
+ unsigned fw_loaded:1;
+
+ u32 ctrl;
+ u32 fl_bufsz;
+
+ u32 rx_offset;
+
+#ifdef CONFIG_PCI_IOV
+ unsigned int num_vfs;
+ struct vf_data_storage *vfinfo;
+ int vf_rate_link_speed;
+#endif
+
+ struct nfp_cpp *cpp;
+ struct platform_device *nfp_dev_cpp;
+ struct nfp_cpp_area *ctrl_area;
+ struct nfp_cpp_area *tx_area;
+ struct nfp_cpp_area *rx_area;
+
+ struct nfp_net_fw_version fw_ver;
+ u32 cap;
+ u32 max_mtu;
+
+ u32 rss_cfg;
+ u8 rss_key[NFP_NET_CFG_RSS_KEY_SZ];
+ u8 rss_itbl[NFP_NET_CFG_RSS_ITBL_SZ];
+
+ int max_tx_rings;
+ int max_rx_rings;
+
+ int num_tx_rings;
+ int num_rx_rings;
+
+ int stride_tx;
+ int stride_rx;
+
+ int txd_cnt;
+ int rxd_cnt;
+
+ struct nfp_net_tx_ring tx_rings[NFP_NET_MAX_TX_RINGS];
+ struct nfp_net_rx_ring rx_rings[NFP_NET_MAX_RX_RINGS];
+
+ u8 num_irqs;
+ u8 num_r_vecs;
+ struct nfp_net_r_vector r_vecs[NFP_NET_MAX_TX_RINGS];
+ struct msix_entry irq_entries[NFP_NET_NON_Q_VECTORS +
+ NFP_NET_MAX_TX_RINGS];
+
+ irq_handler_t lsc_handler;
+ char lsc_name[IFNAMSIZ + 8];
+
+ irq_handler_t exn_handler;
+ char exn_name[IFNAMSIZ + 8];
+
+ irq_handler_t shared_handler;
+ char shared_name[IFNAMSIZ + 8];
+
+ u32 me_freq_mhz;
+
+ bool link_up;
+ spinlock_t link_status_lock;
+
+ spinlock_t reconfig_lock;
+
+ u32 rx_coalesce_usecs;
+ u32 rx_coalesce_max_frames;
+ u32 tx_coalesce_usecs;
+ u32 tx_coalesce_max_frames;
+
+ __be16 vxlan_ports[NFP_NET_N_VXLAN_PORTS];
+ u8 vxlan_usecnt[NFP_NET_N_VXLAN_PORTS];
+
+ u8 __iomem *qcp_cfg;
+
+ u8 __iomem *ctrl_bar;
+ u8 __iomem *q_bar;
+ u8 __iomem *tx_bar;
+ u8 __iomem *rx_bar;
+
+ struct dentry *debugfs_dir;
+};
+
+/* Functions to read/write from/to a BAR
+ * Performs any endian conversion necessary.
+ */
+static inline void nn_writeb(struct nfp_net *nn, int off, u8 val)
+{
+ writeb(val, nn->ctrl_bar + off);
+}
+
+/* NFP-3200 can't handle 16-bit accesses too well - hence no readw/writew */
+
+static inline u32 nn_readl(struct nfp_net *nn, int off)
+{
+ return readl(nn->ctrl_bar + off);
+}
+
+static inline void nn_writel(struct nfp_net *nn, int off, u32 val)
+{
+ writel(val, nn->ctrl_bar + off);
+}
+
+static inline u64 nn_readq(struct nfp_net *nn, int off)
+{
+ return readq(nn->ctrl_bar + off);
+}
+
+static inline void nn_writeq(struct nfp_net *nn, int off, u64 val)
+{
+ writeq(val, nn->ctrl_bar + off);
+}
+
+/* Flush posted PCI writes by reading something without side effects */
+static inline void nn_pci_flush(struct nfp_net *nn)
+{
+ nn_readl(nn, NFP_NET_CFG_VERSION);
+}
+
+/* Queue Controller Peripheral access functions and definitions.
+ *
+ * Some of the BARs of the NFP are mapped to portions of the Queue
+ * Controller Peripheral (QCP) address space on the NFP. A QCP queue
+ * has a read and a write pointer (as well as a size and flags,
+ * indicating overflow etc). The QCP offers a number of different
+ * operation on queue pointers, but here we only offer function to
+ * either add to a pointer or to read the pointer value.
+ */
+#define NFP_QCP_QUEUE_ADDR_SZ 0x800
+#define NFP_QCP_QUEUE_OFF(_x) ((_x) * NFP_QCP_QUEUE_ADDR_SZ)
+#define NFP_QCP_QUEUE_ADD_RPTR 0x0000
+#define NFP_QCP_QUEUE_ADD_WPTR 0x0004
+#define NFP_QCP_QUEUE_STS_LO 0x0008
+#define NFP_QCP_QUEUE_STS_LO_READPTR_mask 0x3ffff
+#define NFP_QCP_QUEUE_STS_HI 0x000c
+#define NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask 0x3ffff
+
+/* The offset of a QCP queues in the PCIe Target (same on NFP3200 and NFP6000 */
+#define NFP_PCIE_QUEUE(_q) (0x80000 + (NFP_QCP_QUEUE_ADDR_SZ * ((_q) & 0xff)))
+
+/* nfp_qcp_ptr - Read or Write Pointer of a queue */
+enum nfp_qcp_ptr {
+ NFP_QCP_READ_PTR = 0,
+ NFP_QCP_WRITE_PTR
+};
+
+/* There appear to be an *undocumented* upper limit on the value which
+ * one can add to a queue and that value is either 0x3f or 0x7f. We
+ * go with 0x3f as a conservative measure.
+ */
+#define NFP_QCP_MAX_ADD 0x3f
+
+static inline void _nfp_qcp_ptr_add(u8 __iomem *q,
+ enum nfp_qcp_ptr ptr, u32 val)
+{
+ u32 off;
+
+ if (ptr == NFP_QCP_READ_PTR)
+ off = NFP_QCP_QUEUE_ADD_RPTR;
+ else
+ off = NFP_QCP_QUEUE_ADD_WPTR;
+
+ while (val > NFP_QCP_MAX_ADD) {
+ writel(NFP_QCP_MAX_ADD, q + off);
+ val -= NFP_QCP_MAX_ADD;
+ }
+
+ writel(val, q + off);
+}
+
+/**
+ * nfp_qcp_rd_ptr_add() - Add the value to the read pointer of a queue
+ *
+ * @q: Base address for queue structure
+ * @val: Value to add to the queue pointer
+ *
+ * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed.
+ */
+static inline void nfp_qcp_rd_ptr_add(u8 __iomem *q, u32 val)
+{
+ _nfp_qcp_ptr_add(q, NFP_QCP_READ_PTR, val);
+}
+
+/**
+ * nfp_qcp_wr_ptr_add() - Add the value to the write pointer of a queue
+ *
+ * @q: Base address for queue structure
+ * @val: Value to add to the queue pointer
+ *
+ * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed.
+ */
+static inline void nfp_qcp_wr_ptr_add(u8 __iomem *q, u32 val)
+{
+ _nfp_qcp_ptr_add(q, NFP_QCP_WRITE_PTR, val);
+}
+
+static inline u32 _nfp_qcp_read(u8 __iomem *q, enum nfp_qcp_ptr ptr)
+{
+ u32 off;
+ u32 val;
+
+ if (ptr == NFP_QCP_READ_PTR)
+ off = NFP_QCP_QUEUE_STS_LO;
+ else
+ off = NFP_QCP_QUEUE_STS_HI;
+
+ val = readl(q + off);
+
+ if (ptr == NFP_QCP_READ_PTR)
+ return val & NFP_QCP_QUEUE_STS_LO_READPTR_mask;
+ else
+ return val & NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask;
+}
+
+/**
+ * nfp_qcp_rd_ptr_read() - Read the current read pointer value for a queue
+ * @q: Base address for queue structure
+ *
+ * Return: Value read.
+ */
+static inline u32 nfp_qcp_rd_ptr_read(u8 __iomem *q)
+{
+ return _nfp_qcp_read(q, NFP_QCP_READ_PTR);
+}
+
+/**
+ * nfp_qcp_wr_ptr_read() - Read the current write pointer value for a queue
+ * @q: Base address for queue structure
+ *
+ * Return: Value read.
+ */
+static inline u32 nfp_qcp_wr_ptr_read(u8 __iomem *q)
+{
+ return _nfp_qcp_read(q, NFP_QCP_WRITE_PTR);
+}
+
+/* Globals */
+extern const char nfp_net_driver_name[];
+extern const char nfp_net_driver_version[];
+
+/* Prototypes */
+void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
+ void __iomem *ctrl_bar);
+
+struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev,
+ int max_tx_rings, int max_rx_rings);
+void nfp_net_netdev_free(struct nfp_net *nn);
+int nfp_net_netdev_init(struct net_device *netdev);
+void nfp_net_netdev_clean(struct net_device *netdev);
+void nfp_net_set_ethtool_ops(struct net_device *netdev);
+void nfp_net_info(struct nfp_net *nn);
+int nfp_net_reconfig(struct nfp_net *nn, u32 update);
+void nfp_net_rss_write_itbl(struct nfp_net *nn);
+void nfp_net_rss_write_key(struct nfp_net *nn);
+void nfp_net_coalesce_write_cfg(struct nfp_net *nn);
+int nfp_net_irqs_alloc(struct nfp_net *nn);
+void nfp_net_irqs_disable(struct nfp_net *nn);
+
+#ifdef CONFIG_NFP_NET_DEBUG
+void nfp_net_debugfs_create(void);
+void nfp_net_debugfs_destroy(void);
+void nfp_net_debugfs_adapter_add(struct nfp_net *nn);
+void nfp_net_debugfs_adapter_del(struct nfp_net *nn);
+#else
+static inline void nfp_net_debugfs_create(void)
+{
+}
+
+static inline void nfp_net_debugfs_destroy(void)
+{
+}
+
+static inline void nfp_net_debugfs_adapter_add(struct nfp_net *nn)
+{
+}
+
+static inline void nfp_net_debugfs_adapter_del(struct nfp_net *nn)
+{
+}
+#endif /* CONFIG_NFP_NET_DEBUG */
+
+#endif /* _NFP_NET_H_ */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
new file mode 100644
index 000000000000..43c618bafdb6
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -0,0 +1,2435 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * nfp_net_common.c
+ * Netronome network device driver: Common functions between PF and VF
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ * Brad Petrus <brad.petrus@netronome.com>
+ * Chris Telfer <chris.telfer@netronome.com>
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/msi.h>
+#include <linux/ethtool.h>
+#include <linux/log2.h>
+#include <linux/if_vlan.h>
+#include <linux/random.h>
+
+#include <linux/ktime.h>
+
+#include <net/vxlan.h>
+
+#include "nfp_net_ctrl.h"
+#include "nfp_net.h"
+
+/**
+ * nfp_net_get_fw_version() - Read and parse the FW version
+ * @fw_ver: Output fw_version structure to read to
+ * @ctrl_bar: Mapped address of the control BAR
+ */
+void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
+ void __iomem *ctrl_bar)
+{
+ u32 reg;
+
+ reg = readl(ctrl_bar + NFP_NET_CFG_VERSION);
+ put_unaligned_le32(reg, fw_ver);
+}
+
+/**
+ * nfp_net_reconfig() - Reconfigure the firmware
+ * @nn: NFP Net device to reconfigure
+ * @update: The value for the update field in the BAR config
+ *
+ * Write the update word to the BAR and ping the reconfig queue. The
+ * poll until the firmware has acknowledged the update by zeroing the
+ * update word.
+ *
+ * Return: Negative errno on error, 0 on success
+ */
+int nfp_net_reconfig(struct nfp_net *nn, u32 update)
+{
+ int cnt, ret = 0;
+ u32 new;
+
+ spin_lock_bh(&nn->reconfig_lock);
+
+ nn_writel(nn, NFP_NET_CFG_UPDATE, update);
+ /* ensure update is written before pinging HW */
+ nn_pci_flush(nn);
+ nfp_qcp_wr_ptr_add(nn->qcp_cfg, 1);
+
+ /* Poll update field, waiting for NFP to ack the config */
+ for (cnt = 0; ; cnt++) {
+ new = nn_readl(nn, NFP_NET_CFG_UPDATE);
+ if (new == 0)
+ break;
+ if (new & NFP_NET_CFG_UPDATE_ERR) {
+ nn_err(nn, "Reconfig error: 0x%08x\n", new);
+ ret = -EIO;
+ break;
+ } else if (cnt >= NFP_NET_POLL_TIMEOUT) {
+ nn_err(nn, "Reconfig timeout for 0x%08x after %dms\n",
+ update, cnt);
+ ret = -EIO;
+ break;
+ }
+ mdelay(1);
+ }
+
+ spin_unlock_bh(&nn->reconfig_lock);
+ return ret;
+}
+
+/* Interrupt configuration and handling
+ */
+
+/**
+ * nfp_net_irq_unmask_msix() - Unmask MSI-X after automasking
+ * @nn: NFP Network structure
+ * @entry_nr: MSI-X table entry
+ *
+ * Clear the MSI-X table mask bit for the given entry bypassing Linux irq
+ * handling subsystem. Use *only* to reenable automasked vectors.
+ */
+static void nfp_net_irq_unmask_msix(struct nfp_net *nn, unsigned int entry_nr)
+{
+ struct list_head *msi_head = &nn->pdev->dev.msi_list;
+ struct msi_desc *entry;
+ u32 off;
+
+ /* All MSI-Xs have the same mask_base */
+ entry = list_first_entry(msi_head, struct msi_desc, list);
+
+ off = (PCI_MSIX_ENTRY_SIZE * entry_nr) +
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
+ writel(0, entry->mask_base + off);
+ readl(entry->mask_base);
+}
+
+/**
+ * nfp_net_irq_unmask() - Unmask automasked interrupt
+ * @nn: NFP Network structure
+ * @entry_nr: MSI-X table entry
+ *
+ * If MSI-X auto-masking is enabled clear the mask bit, otherwise
+ * clear the ICR for the entry.
+ */
+static void nfp_net_irq_unmask(struct nfp_net *nn, unsigned int entry_nr)
+{
+ if (nn->ctrl & NFP_NET_CFG_CTRL_MSIXAUTO) {
+ nfp_net_irq_unmask_msix(nn, entry_nr);
+ return;
+ }
+
+ nn_writeb(nn, NFP_NET_CFG_ICR(entry_nr), NFP_NET_CFG_ICR_UNMASKED);
+ nn_pci_flush(nn);
+}
+
+/**
+ * nfp_net_msix_alloc() - Try to allocate MSI-X irqs
+ * @nn: NFP Network structure
+ * @nr_vecs: Number of MSI-X vectors to allocate
+ *
+ * For MSI-X we want at least NFP_NET_NON_Q_VECTORS + 1 vectors.
+ *
+ * Return: Number of MSI-X vectors obtained or 0 on error.
+ */
+static int nfp_net_msix_alloc(struct nfp_net *nn, int nr_vecs)
+{
+ struct pci_dev *pdev = nn->pdev;
+ int nvecs;
+ int i;
+
+ for (i = 0; i < nr_vecs; i++)
+ nn->irq_entries[i].entry = i;
+
+ nvecs = pci_enable_msix_range(pdev, nn->irq_entries,
+ NFP_NET_NON_Q_VECTORS + 1, nr_vecs);
+ if (nvecs < 0) {
+ nn_warn(nn, "Failed to enable MSI-X. Wanted %d-%d (err=%d)\n",
+ NFP_NET_NON_Q_VECTORS + 1, nr_vecs, nvecs);
+ return 0;
+ }
+
+ return nvecs;
+}
+
+/**
+ * nfp_net_irqs_wanted() - Work out how many interrupt vectors we want
+ * @nn: NFP Network structure
+ *
+ * We want a vector per CPU (or ring), whatever is smaller plus
+ * NFP_NET_NON_Q_VECTORS for LSC etc.
+ *
+ * Return: Number of interrupts wanted
+ */
+static int nfp_net_irqs_wanted(struct nfp_net *nn)
+{
+ int ncpus;
+ int vecs;
+
+ ncpus = num_online_cpus();
+
+ vecs = max_t(int, nn->num_tx_rings, nn->num_rx_rings);
+ vecs = min_t(int, vecs, ncpus);
+
+ return vecs + NFP_NET_NON_Q_VECTORS;
+}
+
+/**
+ * nfp_net_irqs_alloc() - allocates MSI-X irqs
+ * @nn: NFP Network structure
+ *
+ * Return: Number of irqs obtained or 0 on error.
+ */
+int nfp_net_irqs_alloc(struct nfp_net *nn)
+{
+ int wanted_irqs;
+
+ wanted_irqs = nfp_net_irqs_wanted(nn);
+
+ nn->num_irqs = nfp_net_msix_alloc(nn, wanted_irqs);
+ if (nn->num_irqs == 0) {
+ nn_err(nn, "Failed to allocate MSI-X IRQs\n");
+ return 0;
+ }
+
+ nn->num_r_vecs = nn->num_irqs - NFP_NET_NON_Q_VECTORS;
+
+ if (nn->num_irqs < wanted_irqs)
+ nn_warn(nn, "Unable to allocate %d vectors. Got %d instead\n",
+ wanted_irqs, nn->num_irqs);
+
+ return nn->num_irqs;
+}
+
+/**
+ * nfp_net_irqs_disable() - Disable interrupts
+ * @nn: NFP Network structure
+ *
+ * Undoes what @nfp_net_irqs_alloc() does.
+ */
+void nfp_net_irqs_disable(struct nfp_net *nn)
+{
+ pci_disable_msix(nn->pdev);
+}
+
+/**
+ * nfp_net_irq_rxtx() - Interrupt service routine for RX/TX rings.
+ * @irq: Interrupt
+ * @data: Opaque data structure
+ *
+ * Return: Indicate if the interrupt has been handled.
+ */
+static irqreturn_t nfp_net_irq_rxtx(int irq, void *data)
+{
+ struct nfp_net_r_vector *r_vec = data;
+
+ napi_schedule_irqoff(&r_vec->napi);
+
+ /* The FW auto-masks any interrupt, either via the MASK bit in
+ * the MSI-X table or via the per entry ICR field. So there
+ * is no need to disable interrupts here.
+ */
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfp_net_read_link_status() - Reread link status from control BAR
+ * @nn: NFP Network structure
+ */
+static void nfp_net_read_link_status(struct nfp_net *nn)
+{
+ unsigned long flags;
+ bool link_up;
+ u32 sts;
+
+ spin_lock_irqsave(&nn->link_status_lock, flags);
+
+ sts = nn_readl(nn, NFP_NET_CFG_STS);
+ link_up = !!(sts & NFP_NET_CFG_STS_LINK);
+
+ if (nn->link_up == link_up)
+ goto out;
+
+ nn->link_up = link_up;
+
+ if (nn->link_up) {
+ netif_carrier_on(nn->netdev);
+ netdev_info(nn->netdev, "NIC Link is Up\n");
+ } else {
+ netif_carrier_off(nn->netdev);
+ netdev_info(nn->netdev, "NIC Link is Down\n");
+ }
+out:
+ spin_unlock_irqrestore(&nn->link_status_lock, flags);
+}
+
+/**
+ * nfp_net_irq_lsc() - Interrupt service routine for link state changes
+ * @irq: Interrupt
+ * @data: Opaque data structure
+ *
+ * Return: Indicate if the interrupt has been handled.
+ */
+static irqreturn_t nfp_net_irq_lsc(int irq, void *data)
+{
+ struct nfp_net *nn = data;
+
+ nfp_net_read_link_status(nn);
+
+ nfp_net_irq_unmask(nn, NFP_NET_IRQ_LSC_IDX);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfp_net_irq_exn() - Interrupt service routine for exceptions
+ * @irq: Interrupt
+ * @data: Opaque data structure
+ *
+ * Return: Indicate if the interrupt has been handled.
+ */
+static irqreturn_t nfp_net_irq_exn(int irq, void *data)
+{
+ struct nfp_net *nn = data;
+
+ nn_err(nn, "%s: UNIMPLEMENTED.\n", __func__);
+ /* XXX TO BE IMPLEMENTED */
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfp_net_tx_ring_init() - Fill in the boilerplate for a TX ring
+ * @tx_ring: TX ring structure
+ */
+static void nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+
+ tx_ring->qcidx = tx_ring->idx * nn->stride_tx;
+ tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx);
+}
+
+/**
+ * nfp_net_rx_ring_init() - Fill in the boilerplate for a RX ring
+ * @rx_ring: RX ring structure
+ */
+static void nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+
+ rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx;
+ rx_ring->rx_qcidx = rx_ring->fl_qcidx + (nn->stride_rx - 1);
+
+ rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx);
+ rx_ring->qcp_rx = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->rx_qcidx);
+}
+
+/**
+ * nfp_net_irqs_assign() - Assign IRQs and setup rvecs.
+ * @netdev: netdev structure
+ */
+static void nfp_net_irqs_assign(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ struct nfp_net_r_vector *r_vec;
+ int r;
+
+ /* Assumes nn->num_tx_rings == nn->num_rx_rings */
+ if (nn->num_tx_rings > nn->num_r_vecs) {
+ nn_warn(nn, "More rings (%d) than vectors (%d).\n",
+ nn->num_tx_rings, nn->num_r_vecs);
+ nn->num_tx_rings = nn->num_r_vecs;
+ nn->num_rx_rings = nn->num_r_vecs;
+ }
+
+ nn->lsc_handler = nfp_net_irq_lsc;
+ nn->exn_handler = nfp_net_irq_exn;
+
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ r_vec = &nn->r_vecs[r];
+ r_vec->nfp_net = nn;
+ r_vec->handler = nfp_net_irq_rxtx;
+ r_vec->irq_idx = NFP_NET_NON_Q_VECTORS + r;
+
+ cpumask_set_cpu(r, &r_vec->affinity_mask);
+
+ r_vec->tx_ring = &nn->tx_rings[r];
+ nn->tx_rings[r].idx = r;
+ nn->tx_rings[r].r_vec = r_vec;
+ nfp_net_tx_ring_init(r_vec->tx_ring);
+
+ r_vec->rx_ring = &nn->rx_rings[r];
+ nn->rx_rings[r].idx = r;
+ nn->rx_rings[r].r_vec = r_vec;
+ nfp_net_rx_ring_init(r_vec->rx_ring);
+ }
+}
+
+/**
+ * nfp_net_aux_irq_request() - Request an auxiliary interrupt (LSC or EXN)
+ * @nn: NFP Network structure
+ * @ctrl_offset: Control BAR offset where IRQ configuration should be written
+ * @format: printf-style format to construct the interrupt name
+ * @name: Pointer to allocated space for interrupt name
+ * @name_sz: Size of space for interrupt name
+ * @vector_idx: Index of MSI-X vector used for this interrupt
+ * @handler: IRQ handler to register for this interrupt
+ */
+static int
+nfp_net_aux_irq_request(struct nfp_net *nn, u32 ctrl_offset,
+ const char *format, char *name, size_t name_sz,
+ unsigned int vector_idx, irq_handler_t handler)
+{
+ struct msix_entry *entry;
+ int err;
+
+ entry = &nn->irq_entries[vector_idx];
+
+ snprintf(name, name_sz, format, netdev_name(nn->netdev));
+ err = request_irq(entry->vector, handler, 0, name, nn);
+ if (err) {
+ nn_err(nn, "Failed to request IRQ %d (err=%d).\n",
+ entry->vector, err);
+ return err;
+ }
+ nn_writeb(nn, ctrl_offset, vector_idx);
+
+ return 0;
+}
+
+/**
+ * nfp_net_aux_irq_free() - Free an auxiliary interrupt (LSC or EXN)
+ * @nn: NFP Network structure
+ * @ctrl_offset: Control BAR offset where IRQ configuration should be written
+ * @vector_idx: Index of MSI-X vector used for this interrupt
+ */
+static void nfp_net_aux_irq_free(struct nfp_net *nn, u32 ctrl_offset,
+ unsigned int vector_idx)
+{
+ nn_writeb(nn, ctrl_offset, 0xff);
+ free_irq(nn->irq_entries[vector_idx].vector, nn);
+}
+
+/* Transmit
+ *
+ * One queue controller peripheral queue is used for transmit. The
+ * driver en-queues packets for transmit by advancing the write
+ * pointer. The device indicates that packets have transmitted by
+ * advancing the read pointer. The driver maintains a local copy of
+ * the read and write pointer in @struct nfp_net_tx_ring. The driver
+ * keeps @wr_p in sync with the queue controller write pointer and can
+ * determine how many packets have been transmitted by comparing its
+ * copy of the read pointer @rd_p with the read pointer maintained by
+ * the queue controller peripheral.
+ */
+
+/**
+ * nfp_net_tx_full() - Check if the TX ring is full
+ * @tx_ring: TX ring to check
+ * @dcnt: Number of descriptors that need to be enqueued (must be >= 1)
+ *
+ * This function checks, based on the *host copy* of read/write
+ * pointer if a given TX ring is full. The real TX queue may have
+ * some newly made available slots.
+ *
+ * Return: True if the ring is full.
+ */
+static inline int nfp_net_tx_full(struct nfp_net_tx_ring *tx_ring, int dcnt)
+{
+ return (tx_ring->wr_p - tx_ring->rd_p) >= (tx_ring->cnt - dcnt);
+}
+
+/* Wrappers for deciding when to stop and restart TX queues */
+static int nfp_net_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring)
+{
+ return !nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS * 4);
+}
+
+static int nfp_net_tx_ring_should_stop(struct nfp_net_tx_ring *tx_ring)
+{
+ return nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS + 1);
+}
+
+/**
+ * nfp_net_tx_ring_stop() - stop tx ring
+ * @nd_q: netdev queue
+ * @tx_ring: driver tx queue structure
+ *
+ * Safely stop TX ring. Remember that while we are running .start_xmit()
+ * someone else may be cleaning the TX ring completions so we need to be
+ * extra careful here.
+ */
+static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q,
+ struct nfp_net_tx_ring *tx_ring)
+{
+ netif_tx_stop_queue(nd_q);
+
+ /* We can race with the TX completion out of NAPI so recheck */
+ smp_mb();
+ if (unlikely(nfp_net_tx_ring_should_wake(tx_ring)))
+ netif_tx_start_queue(nd_q);
+}
+
+/**
+ * nfp_net_tx_tso() - Set up Tx descriptor for LSO
+ * @nn: NFP Net device
+ * @r_vec: per-ring structure
+ * @txbuf: Pointer to driver soft TX descriptor
+ * @txd: Pointer to HW TX descriptor
+ * @skb: Pointer to SKB
+ *
+ * Set up Tx descriptor for LSO, do nothing for non-LSO skbs.
+ * Return error on packet header greater than maximum supported LSO header size.
+ */
+static void nfp_net_tx_tso(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
+ struct nfp_net_tx_buf *txbuf,
+ struct nfp_net_tx_desc *txd, struct sk_buff *skb)
+{
+ u32 hdrlen;
+ u16 mss;
+
+ if (!skb_is_gso(skb))
+ return;
+
+ if (!skb->encapsulation)
+ hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ else
+ hdrlen = skb_inner_transport_header(skb) - skb->data +
+ inner_tcp_hdrlen(skb);
+
+ txbuf->pkt_cnt = skb_shinfo(skb)->gso_segs;
+ txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1);
+
+ mss = skb_shinfo(skb)->gso_size & PCIE_DESC_TX_MSS_MASK;
+ txd->l4_offset = hdrlen;
+ txd->mss = cpu_to_le16(mss);
+ txd->flags |= PCIE_DESC_TX_LSO;
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_lso++;
+ u64_stats_update_end(&r_vec->tx_sync);
+}
+
+/**
+ * nfp_net_tx_csum() - Set TX CSUM offload flags in TX descriptor
+ * @nn: NFP Net device
+ * @r_vec: per-ring structure
+ * @txbuf: Pointer to driver soft TX descriptor
+ * @txd: Pointer to TX descriptor
+ * @skb: Pointer to SKB
+ *
+ * This function sets the TX checksum flags in the TX descriptor based
+ * on the configuration and the protocol of the packet to be transmitted.
+ */
+static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
+ struct nfp_net_tx_buf *txbuf,
+ struct nfp_net_tx_desc *txd, struct sk_buff *skb)
+{
+ struct ipv6hdr *ipv6h;
+ struct iphdr *iph;
+ u8 l4_hdr;
+
+ if (!(nn->ctrl & NFP_NET_CFG_CTRL_TXCSUM))
+ return;
+
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return;
+
+ txd->flags |= PCIE_DESC_TX_CSUM;
+ if (skb->encapsulation)
+ txd->flags |= PCIE_DESC_TX_ENCAP;
+
+ iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
+ ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
+
+ if (iph->version == 4) {
+ txd->flags |= PCIE_DESC_TX_IP4_CSUM;
+ l4_hdr = iph->protocol;
+ } else if (ipv6h->version == 6) {
+ l4_hdr = ipv6h->nexthdr;
+ } else {
+ nn_warn_ratelimit(nn, "partial checksum but ipv=%x!\n",
+ iph->version);
+ return;
+ }
+
+ switch (l4_hdr) {
+ case IPPROTO_TCP:
+ txd->flags |= PCIE_DESC_TX_TCP_CSUM;
+ break;
+ case IPPROTO_UDP:
+ txd->flags |= PCIE_DESC_TX_UDP_CSUM;
+ break;
+ default:
+ nn_warn_ratelimit(nn, "partial checksum but l4 proto=%x!\n",
+ l4_hdr);
+ return;
+ }
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ if (skb->encapsulation)
+ r_vec->hw_csum_tx_inner += txbuf->pkt_cnt;
+ else
+ r_vec->hw_csum_tx += txbuf->pkt_cnt;
+ u64_stats_update_end(&r_vec->tx_sync);
+}
+
+/**
+ * nfp_net_tx() - Main transmit entry point
+ * @skb: SKB to transmit
+ * @netdev: netdev structure
+ *
+ * Return: NETDEV_TX_OK on success.
+ */
+static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ const struct skb_frag_struct *frag;
+ struct nfp_net_r_vector *r_vec;
+ struct nfp_net_tx_desc *txd, txdg;
+ struct nfp_net_tx_buf *txbuf;
+ struct nfp_net_tx_ring *tx_ring;
+ struct netdev_queue *nd_q;
+ dma_addr_t dma_addr;
+ unsigned int fsize;
+ int f, nr_frags;
+ int wr_idx;
+ u16 qidx;
+
+ qidx = skb_get_queue_mapping(skb);
+ tx_ring = &nn->tx_rings[qidx];
+ r_vec = tx_ring->r_vec;
+ nd_q = netdev_get_tx_queue(nn->netdev, qidx);
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) {
+ nn_warn_ratelimit(nn, "TX ring %d busy. wrp=%u rdp=%u\n",
+ qidx, tx_ring->wr_p, tx_ring->rd_p);
+ netif_tx_stop_queue(nd_q);
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_busy++;
+ u64_stats_update_end(&r_vec->tx_sync);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* Start with the head skbuf */
+ dma_addr = dma_map_single(&nn->pdev->dev, skb->data, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&nn->pdev->dev, dma_addr))
+ goto err_free;
+
+ wr_idx = tx_ring->wr_p % tx_ring->cnt;
+
+ /* Stash the soft descriptor of the head then initialize it */
+ txbuf = &tx_ring->txbufs[wr_idx];
+ txbuf->skb = skb;
+ txbuf->dma_addr = dma_addr;
+ txbuf->fidx = -1;
+ txbuf->pkt_cnt = 1;
+ txbuf->real_len = skb->len;
+
+ /* Build TX descriptor */
+ txd = &tx_ring->txds[wr_idx];
+ txd->offset_eop = (nr_frags == 0) ? PCIE_DESC_TX_EOP : 0;
+ txd->dma_len = cpu_to_le16(skb_headlen(skb));
+ nfp_desc_set_dma_addr(txd, dma_addr);
+ txd->data_len = cpu_to_le16(skb->len);
+
+ txd->flags = 0;
+ txd->mss = 0;
+ txd->l4_offset = 0;
+
+ nfp_net_tx_tso(nn, r_vec, txbuf, txd, skb);
+
+ nfp_net_tx_csum(nn, r_vec, txbuf, txd, skb);
+
+ if (skb_vlan_tag_present(skb) && nn->ctrl & NFP_NET_CFG_CTRL_TXVLAN) {
+ txd->flags |= PCIE_DESC_TX_VLAN;
+ txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb));
+ }
+
+ /* Gather DMA */
+ if (nr_frags > 0) {
+ /* all descs must match except for in addr, length and eop */
+ txdg = *txd;
+
+ for (f = 0; f < nr_frags; f++) {
+ frag = &skb_shinfo(skb)->frags[f];
+ fsize = skb_frag_size(frag);
+
+ dma_addr = skb_frag_dma_map(&nn->pdev->dev, frag, 0,
+ fsize, DMA_TO_DEVICE);
+ if (dma_mapping_error(&nn->pdev->dev, dma_addr))
+ goto err_unmap;
+
+ wr_idx = (wr_idx + 1) % tx_ring->cnt;
+ tx_ring->txbufs[wr_idx].skb = skb;
+ tx_ring->txbufs[wr_idx].dma_addr = dma_addr;
+ tx_ring->txbufs[wr_idx].fidx = f;
+
+ txd = &tx_ring->txds[wr_idx];
+ *txd = txdg;
+ txd->dma_len = cpu_to_le16(fsize);
+ nfp_desc_set_dma_addr(txd, dma_addr);
+ txd->offset_eop =
+ (f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0;
+ }
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_gather++;
+ u64_stats_update_end(&r_vec->tx_sync);
+ }
+
+ netdev_tx_sent_queue(nd_q, txbuf->real_len);
+
+ tx_ring->wr_p += nr_frags + 1;
+ if (nfp_net_tx_ring_should_stop(tx_ring))
+ nfp_net_tx_ring_stop(nd_q, tx_ring);
+
+ tx_ring->wr_ptr_add += nr_frags + 1;
+ if (!skb->xmit_more || netif_xmit_stopped(nd_q)) {
+ /* force memory write before we let HW know */
+ wmb();
+ nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add);
+ tx_ring->wr_ptr_add = 0;
+ }
+
+ skb_tx_timestamp(skb);
+
+ return NETDEV_TX_OK;
+
+err_unmap:
+ --f;
+ while (f >= 0) {
+ frag = &skb_shinfo(skb)->frags[f];
+ dma_unmap_page(&nn->pdev->dev,
+ tx_ring->txbufs[wr_idx].dma_addr,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ tx_ring->txbufs[wr_idx].skb = NULL;
+ tx_ring->txbufs[wr_idx].dma_addr = 0;
+ tx_ring->txbufs[wr_idx].fidx = -2;
+ wr_idx = wr_idx - 1;
+ if (wr_idx < 0)
+ wr_idx += tx_ring->cnt;
+ }
+ dma_unmap_single(&nn->pdev->dev, tx_ring->txbufs[wr_idx].dma_addr,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ tx_ring->txbufs[wr_idx].skb = NULL;
+ tx_ring->txbufs[wr_idx].dma_addr = 0;
+ tx_ring->txbufs[wr_idx].fidx = -2;
+err_free:
+ nn_warn_ratelimit(nn, "Failed to map DMA TX buffer\n");
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_errors++;
+ u64_stats_update_end(&r_vec->tx_sync);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * nfp_net_tx_complete() - Handled completed TX packets
+ * @tx_ring: TX ring structure
+ *
+ * Return: Number of completed TX descriptors
+ */
+static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ const struct skb_frag_struct *frag;
+ struct netdev_queue *nd_q;
+ u32 done_pkts = 0, done_bytes = 0;
+ struct sk_buff *skb;
+ int todo, nr_frags;
+ u32 qcp_rd_p;
+ int fidx;
+ int idx;
+
+ /* Work out how many descriptors have been transmitted */
+ qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q);
+
+ if (qcp_rd_p == tx_ring->qcp_rd_p)
+ return;
+
+ if (qcp_rd_p > tx_ring->qcp_rd_p)
+ todo = qcp_rd_p - tx_ring->qcp_rd_p;
+ else
+ todo = qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p;
+
+ while (todo--) {
+ idx = tx_ring->rd_p % tx_ring->cnt;
+ tx_ring->rd_p++;
+
+ skb = tx_ring->txbufs[idx].skb;
+ if (!skb)
+ continue;
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ fidx = tx_ring->txbufs[idx].fidx;
+
+ if (fidx == -1) {
+ /* unmap head */
+ dma_unmap_single(&nn->pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_headlen(skb), DMA_TO_DEVICE);
+
+ done_pkts += tx_ring->txbufs[idx].pkt_cnt;
+ done_bytes += tx_ring->txbufs[idx].real_len;
+ } else {
+ /* unmap fragment */
+ frag = &skb_shinfo(skb)->frags[fidx];
+ dma_unmap_page(&nn->pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ }
+
+ /* check for last gather fragment */
+ if (fidx == nr_frags - 1)
+ dev_kfree_skb_any(skb);
+
+ tx_ring->txbufs[idx].dma_addr = 0;
+ tx_ring->txbufs[idx].skb = NULL;
+ tx_ring->txbufs[idx].fidx = -2;
+ }
+
+ tx_ring->qcp_rd_p = qcp_rd_p;
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_bytes += done_bytes;
+ r_vec->tx_pkts += done_pkts;
+ u64_stats_update_end(&r_vec->tx_sync);
+
+ nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx);
+ netdev_tx_completed_queue(nd_q, done_pkts, done_bytes);
+ if (nfp_net_tx_ring_should_wake(tx_ring)) {
+ /* Make sure TX thread will see updated tx_ring->rd_p */
+ smp_mb();
+
+ if (unlikely(netif_tx_queue_stopped(nd_q)))
+ netif_tx_wake_queue(nd_q);
+ }
+
+ WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt,
+ "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n",
+ tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt);
+}
+
+/**
+ * nfp_net_tx_flush() - Free any untransmitted buffers currently on the TX ring
+ * @tx_ring: TX ring structure
+ *
+ * Assumes that the device is stopped
+ */
+static void nfp_net_tx_flush(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ const struct skb_frag_struct *frag;
+ struct netdev_queue *nd_q;
+ struct sk_buff *skb;
+ int nr_frags;
+ int fidx;
+ int idx;
+
+ while (tx_ring->rd_p != tx_ring->wr_p) {
+ idx = tx_ring->rd_p % tx_ring->cnt;
+
+ skb = tx_ring->txbufs[idx].skb;
+ if (skb) {
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ fidx = tx_ring->txbufs[idx].fidx;
+
+ if (fidx == -1) {
+ /* unmap head */
+ dma_unmap_single(&pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_headlen(skb),
+ DMA_TO_DEVICE);
+ } else {
+ /* unmap fragment */
+ frag = &skb_shinfo(skb)->frags[fidx];
+ dma_unmap_page(&pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ }
+
+ /* check for last gather fragment */
+ if (fidx == nr_frags - 1)
+ dev_kfree_skb_any(skb);
+
+ tx_ring->txbufs[idx].dma_addr = 0;
+ tx_ring->txbufs[idx].skb = NULL;
+ tx_ring->txbufs[idx].fidx = -2;
+ }
+
+ memset(&tx_ring->txds[idx], 0, sizeof(tx_ring->txds[idx]));
+
+ tx_ring->qcp_rd_p++;
+ tx_ring->rd_p++;
+ }
+
+ nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx);
+ netdev_tx_reset_queue(nd_q);
+}
+
+static void nfp_net_tx_timeout(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int i;
+
+ for (i = 0; i < nn->num_tx_rings; i++) {
+ if (!netif_tx_queue_stopped(netdev_get_tx_queue(netdev, i)))
+ continue;
+ nn_warn(nn, "TX timeout on ring: %d\n", i);
+ }
+ nn_warn(nn, "TX watchdog timeout\n");
+}
+
+/* Receive processing
+ */
+
+/**
+ * nfp_net_rx_space() - return the number of free slots on the RX ring
+ * @rx_ring: RX ring structure
+ *
+ * Make sure we leave at least one slot free.
+ *
+ * Return: True if there is space on the RX ring
+ */
+static inline int nfp_net_rx_space(struct nfp_net_rx_ring *rx_ring)
+{
+ return (rx_ring->cnt - 1) - (rx_ring->wr_p - rx_ring->rd_p);
+}
+
+/**
+ * nfp_net_rx_alloc_one() - Allocate and map skb for RX
+ * @rx_ring: RX ring structure of the skb
+ * @dma_addr: Pointer to storage for DMA address (output param)
+ *
+ * This function will allcate a new skb, map it for DMA.
+ *
+ * Return: allocated skb or NULL on failure.
+ */
+static struct sk_buff *
+nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr)
+{
+ struct nfp_net *nn = rx_ring->r_vec->nfp_net;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(nn->netdev, nn->fl_bufsz);
+ if (!skb) {
+ nn_warn_ratelimit(nn, "Failed to alloc receive SKB\n");
+ return NULL;
+ }
+
+ *dma_addr = dma_map_single(&nn->pdev->dev, skb->data,
+ nn->fl_bufsz, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) {
+ dev_kfree_skb_any(skb);
+ nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n");
+ return NULL;
+ }
+
+ return skb;
+}
+
+/**
+ * nfp_net_rx_give_one() - Put mapped skb on the software and hardware rings
+ * @rx_ring: RX ring structure
+ * @skb: Skb to put on rings
+ * @dma_addr: DMA address of skb mapping
+ */
+static void nfp_net_rx_give_one(struct nfp_net_rx_ring *rx_ring,
+ struct sk_buff *skb, dma_addr_t dma_addr)
+{
+ unsigned int wr_idx;
+
+ wr_idx = rx_ring->wr_p % rx_ring->cnt;
+
+ /* Stash SKB and DMA address away */
+ rx_ring->rxbufs[wr_idx].skb = skb;
+ rx_ring->rxbufs[wr_idx].dma_addr = dma_addr;
+
+ /* Fill freelist descriptor */
+ rx_ring->rxds[wr_idx].fld.reserved = 0;
+ rx_ring->rxds[wr_idx].fld.meta_len_dd = 0;
+ nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, dma_addr);
+
+ rx_ring->wr_p++;
+ rx_ring->wr_ptr_add++;
+ if (rx_ring->wr_ptr_add >= NFP_NET_FL_BATCH) {
+ /* Update write pointer of the freelist queue. Make
+ * sure all writes are flushed before telling the hardware.
+ */
+ wmb();
+ nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, rx_ring->wr_ptr_add);
+ rx_ring->wr_ptr_add = 0;
+ }
+}
+
+/**
+ * nfp_net_rx_flush() - Free any buffers currently on the RX ring
+ * @rx_ring: RX ring to remove buffers from
+ *
+ * Assumes that the device is stopped
+ */
+static void nfp_net_rx_flush(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net *nn = rx_ring->r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ int idx;
+
+ while (rx_ring->rd_p != rx_ring->wr_p) {
+ idx = rx_ring->rd_p % rx_ring->cnt;
+
+ if (rx_ring->rxbufs[idx].skb) {
+ dma_unmap_single(&pdev->dev,
+ rx_ring->rxbufs[idx].dma_addr,
+ nn->fl_bufsz, DMA_FROM_DEVICE);
+ dev_kfree_skb_any(rx_ring->rxbufs[idx].skb);
+ rx_ring->rxbufs[idx].dma_addr = 0;
+ rx_ring->rxbufs[idx].skb = NULL;
+ }
+
+ memset(&rx_ring->rxds[idx], 0, sizeof(rx_ring->rxds[idx]));
+
+ rx_ring->rd_p++;
+ }
+}
+
+/**
+ * nfp_net_rx_fill_freelist() - Attempt filling freelist with RX buffers
+ * @rx_ring: RX ring to fill
+ *
+ * Try to fill as many buffers as possible into freelist. Return
+ * number of buffers added.
+ *
+ * Return: Number of freelist buffers added.
+ */
+static int nfp_net_rx_fill_freelist(struct nfp_net_rx_ring *rx_ring)
+{
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+
+ while (nfp_net_rx_space(rx_ring)) {
+ skb = nfp_net_rx_alloc_one(rx_ring, &dma_addr);
+ if (!skb) {
+ nfp_net_rx_flush(rx_ring);
+ return -ENOMEM;
+ }
+ nfp_net_rx_give_one(rx_ring, skb, dma_addr);
+ }
+
+ return 0;
+}
+
+/**
+ * nfp_net_rx_csum_has_errors() - group check if rxd has any csum errors
+ * @flags: RX descriptor flags field in CPU byte order
+ */
+static int nfp_net_rx_csum_has_errors(u16 flags)
+{
+ u16 csum_all_checked, csum_all_ok;
+
+ csum_all_checked = flags & __PCIE_DESC_RX_CSUM_ALL;
+ csum_all_ok = flags & __PCIE_DESC_RX_CSUM_ALL_OK;
+
+ return csum_all_checked != (csum_all_ok << PCIE_DESC_RX_CSUM_OK_SHIFT);
+}
+
+/**
+ * nfp_net_rx_csum() - set SKB checksum field based on RX descriptor flags
+ * @nn: NFP Net device
+ * @r_vec: per-ring structure
+ * @rxd: Pointer to RX descriptor
+ * @skb: Pointer to SKB
+ */
+static void nfp_net_rx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
+ struct nfp_net_rx_desc *rxd, struct sk_buff *skb)
+{
+ skb_checksum_none_assert(skb);
+
+ if (!(nn->netdev->features & NETIF_F_RXCSUM))
+ return;
+
+ if (nfp_net_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) {
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->hw_csum_rx_error++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ return;
+ }
+
+ /* Assume that the firmware will never report inner CSUM_OK unless outer
+ * L4 headers were successfully parsed. FW will always report zero UDP
+ * checksum as CSUM_OK.
+ */
+ if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK ||
+ rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) {
+ __skb_incr_checksum_unnecessary(skb);
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->hw_csum_rx_ok++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ }
+
+ if (rxd->rxd.flags & PCIE_DESC_RX_I_TCP_CSUM_OK ||
+ rxd->rxd.flags & PCIE_DESC_RX_I_UDP_CSUM_OK) {
+ __skb_incr_checksum_unnecessary(skb);
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->hw_csum_rx_inner_ok++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ }
+}
+
+/**
+ * nfp_net_set_hash() - Set SKB hash data
+ * @netdev: adapter's net_device structure
+ * @skb: SKB to set the hash data on
+ * @rxd: RX descriptor
+ *
+ * The RSS hash and hash-type are pre-pended to the packet data.
+ * Extract and decode it and set the skb fields.
+ */
+static void nfp_net_set_hash(struct net_device *netdev, struct sk_buff *skb,
+ struct nfp_net_rx_desc *rxd)
+{
+ struct nfp_net_rx_hash *rx_hash;
+
+ if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS) ||
+ !(netdev->features & NETIF_F_RXHASH))
+ return;
+
+ rx_hash = (struct nfp_net_rx_hash *)(skb->data - sizeof(*rx_hash));
+
+ switch (be32_to_cpu(rx_hash->hash_type)) {
+ case NFP_NET_RSS_IPV4:
+ case NFP_NET_RSS_IPV6:
+ case NFP_NET_RSS_IPV6_EX:
+ skb_set_hash(skb, be32_to_cpu(rx_hash->hash), PKT_HASH_TYPE_L3);
+ break;
+ default:
+ skb_set_hash(skb, be32_to_cpu(rx_hash->hash), PKT_HASH_TYPE_L4);
+ break;
+ }
+}
+
+/**
+ * nfp_net_rx() - receive up to @budget packets on @rx_ring
+ * @rx_ring: RX ring to receive from
+ * @budget: NAPI budget
+ *
+ * Note, this function is separated out from the napi poll function to
+ * more cleanly separate packet receive code from other bookkeeping
+ * functions performed in the napi poll function.
+ *
+ * There are differences between the NFP-3200 firmware and the
+ * NFP-6000 firmware. The NFP-3200 firmware uses a dedicated RX queue
+ * to indicate that new packets have arrived. The NFP-6000 does not
+ * have this queue and uses the DD bit in the RX descriptor. This
+ * method cannot be used on the NFP-3200 as it causes a race
+ * condition: The RX ring write pointer on the NFP-3200 is updated
+ * after packets (and descriptors) have been DMAed. If the DD bit is
+ * used and subsequently the read pointer is updated this may lead to
+ * the RX queue to underflow (if the firmware has not yet update the
+ * write pointer). Therefore we use slightly ugly conditional code
+ * below to handle the differences. We may, in the future update the
+ * NFP-3200 firmware to behave the same as the firmware on the
+ * NFP-6000.
+ *
+ * Return: Number of packets received.
+ */
+static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ unsigned int data_len, meta_len;
+ int avail = 0, pkts_polled = 0;
+ struct sk_buff *skb, *new_skb;
+ struct nfp_net_rx_desc *rxd;
+ dma_addr_t new_dma_addr;
+ u32 qcp_wr_p;
+ int idx;
+
+ if (nn->is_nfp3200) {
+ /* Work out how many packets arrived */
+ qcp_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx);
+ idx = rx_ring->rd_p % rx_ring->cnt;
+
+ if (qcp_wr_p == idx)
+ /* No new packets */
+ return 0;
+
+ if (qcp_wr_p > idx)
+ avail = qcp_wr_p - idx;
+ else
+ avail = qcp_wr_p + rx_ring->cnt - idx;
+ } else {
+ avail = budget + 1;
+ }
+
+ while (avail > 0 && pkts_polled < budget) {
+ idx = rx_ring->rd_p % rx_ring->cnt;
+
+ rxd = &rx_ring->rxds[idx];
+ if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) {
+ if (nn->is_nfp3200)
+ nn_dbg(nn, "RX descriptor not valid (DD)%d:%u rxd[0]=%#x rxd[1]=%#x\n",
+ rx_ring->idx, idx,
+ rxd->vals[0], rxd->vals[1]);
+ break;
+ }
+ /* Memory barrier to ensure that we won't do other reads
+ * before the DD bit.
+ */
+ dma_rmb();
+
+ rx_ring->rd_p++;
+ pkts_polled++;
+ avail--;
+
+ skb = rx_ring->rxbufs[idx].skb;
+
+ new_skb = nfp_net_rx_alloc_one(rx_ring, &new_dma_addr);
+ if (!new_skb) {
+ nfp_net_rx_give_one(rx_ring, rx_ring->rxbufs[idx].skb,
+ rx_ring->rxbufs[idx].dma_addr);
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->rx_drops++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ continue;
+ }
+
+ dma_unmap_single(&nn->pdev->dev,
+ rx_ring->rxbufs[idx].dma_addr,
+ nn->fl_bufsz, DMA_FROM_DEVICE);
+
+ nfp_net_rx_give_one(rx_ring, new_skb, new_dma_addr);
+
+ meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK;
+ data_len = le16_to_cpu(rxd->rxd.data_len);
+
+ if (WARN_ON_ONCE(data_len > nn->fl_bufsz)) {
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) {
+ /* The packet data starts after the metadata */
+ skb_reserve(skb, meta_len);
+ } else {
+ /* The packet data starts at a fixed offset */
+ skb_reserve(skb, nn->rx_offset);
+ }
+
+ /* Adjust the SKB for the dynamic meta data pre-pended */
+ skb_put(skb, data_len - meta_len);
+
+ nfp_net_set_hash(nn->netdev, skb, rxd);
+
+ /* Pad small frames to minimum */
+ if (skb_put_padto(skb, 60))
+ break;
+
+ /* Stats update */
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->rx_pkts++;
+ r_vec->rx_bytes += skb->len;
+ u64_stats_update_end(&r_vec->rx_sync);
+
+ skb_record_rx_queue(skb, rx_ring->idx);
+ skb->protocol = eth_type_trans(skb, nn->netdev);
+
+ nfp_net_rx_csum(nn, r_vec, rxd, skb);
+
+ if (rxd->rxd.flags & PCIE_DESC_RX_VLAN)
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ le16_to_cpu(rxd->rxd.vlan));
+
+ napi_gro_receive(&rx_ring->r_vec->napi, skb);
+ }
+
+ if (nn->is_nfp3200)
+ nfp_qcp_rd_ptr_add(rx_ring->qcp_rx, pkts_polled);
+
+ return pkts_polled;
+}
+
+/**
+ * nfp_net_poll() - napi poll function
+ * @napi: NAPI structure
+ * @budget: NAPI budget
+ *
+ * Return: number of packets polled.
+ */
+static int nfp_net_poll(struct napi_struct *napi, int budget)
+{
+ struct nfp_net_r_vector *r_vec =
+ container_of(napi, struct nfp_net_r_vector, napi);
+ struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring;
+ struct nfp_net_tx_ring *tx_ring = r_vec->tx_ring;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct netdev_queue *txq;
+ unsigned int pkts_polled;
+
+ tx_ring = &nn->tx_rings[rx_ring->idx];
+ txq = netdev_get_tx_queue(nn->netdev, tx_ring->idx);
+ nfp_net_tx_complete(tx_ring);
+
+ pkts_polled = nfp_net_rx(rx_ring, budget);
+
+ if (pkts_polled < budget) {
+ napi_complete_done(napi, pkts_polled);
+ nfp_net_irq_unmask(nn, r_vec->irq_idx);
+ }
+
+ return pkts_polled;
+}
+
+/* Setup and Configuration
+ */
+
+/**
+ * nfp_net_tx_ring_free() - Free resources allocated to a TX ring
+ * @tx_ring: TX ring to free
+ */
+static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+
+ nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(tx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_TXR_SZ(tx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_TXR_VEC(tx_ring->idx), 0);
+
+ kfree(tx_ring->txbufs);
+
+ if (tx_ring->txds)
+ dma_free_coherent(&pdev->dev, tx_ring->size,
+ tx_ring->txds, tx_ring->dma);
+
+ tx_ring->cnt = 0;
+ tx_ring->wr_p = 0;
+ tx_ring->rd_p = 0;
+ tx_ring->qcp_rd_p = 0;
+ tx_ring->wr_ptr_add = 0;
+
+ tx_ring->txbufs = NULL;
+ tx_ring->txds = NULL;
+ tx_ring->dma = 0;
+ tx_ring->size = 0;
+}
+
+/**
+ * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring
+ * @tx_ring: TX Ring structure to allocate
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ int sz;
+
+ tx_ring->cnt = nn->txd_cnt;
+
+ tx_ring->size = sizeof(*tx_ring->txds) * tx_ring->cnt;
+ tx_ring->txds = dma_zalloc_coherent(&pdev->dev, tx_ring->size,
+ &tx_ring->dma, GFP_KERNEL);
+ if (!tx_ring->txds)
+ goto err_alloc;
+
+ sz = sizeof(*tx_ring->txbufs) * tx_ring->cnt;
+ tx_ring->txbufs = kzalloc(sz, GFP_KERNEL);
+ if (!tx_ring->txbufs)
+ goto err_alloc;
+
+ /* Write the DMA address, size and MSI-X info to the device */
+ nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(tx_ring->idx), tx_ring->dma);
+ nn_writeb(nn, NFP_NET_CFG_TXR_SZ(tx_ring->idx), ilog2(tx_ring->cnt));
+ nn_writeb(nn, NFP_NET_CFG_TXR_VEC(tx_ring->idx), r_vec->irq_idx);
+
+ netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, tx_ring->idx);
+
+ nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p\n",
+ tx_ring->idx, tx_ring->qcidx,
+ tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds);
+
+ return 0;
+
+err_alloc:
+ nfp_net_tx_ring_free(tx_ring);
+ return -ENOMEM;
+}
+
+/**
+ * nfp_net_rx_ring_free() - Free resources allocated to a RX ring
+ * @rx_ring: RX ring to free
+ */
+static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+
+ nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(rx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_RXR_SZ(rx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_RXR_VEC(rx_ring->idx), 0);
+
+ kfree(rx_ring->rxbufs);
+
+ if (rx_ring->rxds)
+ dma_free_coherent(&pdev->dev, rx_ring->size,
+ rx_ring->rxds, rx_ring->dma);
+
+ rx_ring->cnt = 0;
+ rx_ring->wr_p = 0;
+ rx_ring->rd_p = 0;
+ rx_ring->wr_ptr_add = 0;
+
+ rx_ring->rxbufs = NULL;
+ rx_ring->rxds = NULL;
+ rx_ring->dma = 0;
+ rx_ring->size = 0;
+}
+
+/**
+ * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring
+ * @rx_ring: RX ring to allocate
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ int sz;
+
+ rx_ring->cnt = nn->rxd_cnt;
+
+ rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt;
+ rx_ring->rxds = dma_zalloc_coherent(&pdev->dev, rx_ring->size,
+ &rx_ring->dma, GFP_KERNEL);
+ if (!rx_ring->rxds)
+ goto err_alloc;
+
+ sz = sizeof(*rx_ring->rxbufs) * rx_ring->cnt;
+ rx_ring->rxbufs = kzalloc(sz, GFP_KERNEL);
+ if (!rx_ring->rxbufs)
+ goto err_alloc;
+
+ /* Write the DMA address, size and MSI-X info to the device */
+ nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(rx_ring->idx), rx_ring->dma);
+ nn_writeb(nn, NFP_NET_CFG_RXR_SZ(rx_ring->idx), ilog2(rx_ring->cnt));
+ nn_writeb(nn, NFP_NET_CFG_RXR_VEC(rx_ring->idx), r_vec->irq_idx);
+
+ nn_dbg(nn, "RxQ%02d: FlQCidx=%02d RxQCidx=%02d cnt=%d dma=%#llx host=%p\n",
+ rx_ring->idx, rx_ring->fl_qcidx, rx_ring->rx_qcidx,
+ rx_ring->cnt, (unsigned long long)rx_ring->dma, rx_ring->rxds);
+
+ return 0;
+
+err_alloc:
+ nfp_net_rx_ring_free(rx_ring);
+ return -ENOMEM;
+}
+
+static void __nfp_net_free_rings(struct nfp_net *nn, unsigned int n_free)
+{
+ struct nfp_net_r_vector *r_vec;
+ struct msix_entry *entry;
+
+ while (n_free--) {
+ r_vec = &nn->r_vecs[n_free];
+ entry = &nn->irq_entries[r_vec->irq_idx];
+
+ nfp_net_rx_ring_free(r_vec->rx_ring);
+ nfp_net_tx_ring_free(r_vec->tx_ring);
+
+ irq_set_affinity_hint(entry->vector, NULL);
+ free_irq(entry->vector, r_vec);
+
+ netif_napi_del(&r_vec->napi);
+ }
+}
+
+/**
+ * nfp_net_free_rings() - Free all ring resources
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_free_rings(struct nfp_net *nn)
+{
+ __nfp_net_free_rings(nn, nn->num_r_vecs);
+}
+
+/**
+ * nfp_net_alloc_rings() - Allocate resources for RX and TX rings
+ * @nn: NFP Net device to reconfigure
+ *
+ * Return: 0 on success or negative errno on error.
+ */
+static int nfp_net_alloc_rings(struct nfp_net *nn)
+{
+ struct nfp_net_r_vector *r_vec;
+ struct msix_entry *entry;
+ int err;
+ int r;
+
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ r_vec = &nn->r_vecs[r];
+ entry = &nn->irq_entries[r_vec->irq_idx];
+
+ /* Setup NAPI */
+ netif_napi_add(nn->netdev, &r_vec->napi,
+ nfp_net_poll, NAPI_POLL_WEIGHT);
+
+ snprintf(r_vec->name, sizeof(r_vec->name),
+ "%s-rxtx-%d", nn->netdev->name, r);
+ err = request_irq(entry->vector, r_vec->handler, 0,
+ r_vec->name, r_vec);
+ if (err) {
+ nn_dbg(nn, "Error requesting IRQ %d\n", entry->vector);
+ goto err_napi_del;
+ }
+
+ irq_set_affinity_hint(entry->vector, &r_vec->affinity_mask);
+
+ nn_dbg(nn, "RV%02d: irq=%03d/%03d\n",
+ r, entry->vector, entry->entry);
+
+ /* Allocate TX ring resources */
+ err = nfp_net_tx_ring_alloc(r_vec->tx_ring);
+ if (err)
+ goto err_free_irq;
+
+ /* Allocate RX ring resources */
+ err = nfp_net_rx_ring_alloc(r_vec->rx_ring);
+ if (err)
+ goto err_free_tx;
+ }
+
+ return 0;
+
+err_free_tx:
+ nfp_net_tx_ring_free(r_vec->tx_ring);
+err_free_irq:
+ irq_set_affinity_hint(entry->vector, NULL);
+ free_irq(entry->vector, r_vec);
+err_napi_del:
+ netif_napi_del(&r_vec->napi);
+ __nfp_net_free_rings(nn, r);
+ return err;
+}
+
+/**
+ * nfp_net_rss_write_itbl() - Write RSS indirection table to device
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_rss_write_itbl(struct nfp_net *nn)
+{
+ int i;
+
+ for (i = 0; i < NFP_NET_CFG_RSS_ITBL_SZ; i += 4)
+ nn_writel(nn, NFP_NET_CFG_RSS_ITBL + i,
+ get_unaligned_le32(nn->rss_itbl + i));
+}
+
+/**
+ * nfp_net_rss_write_key() - Write RSS hash key to device
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_rss_write_key(struct nfp_net *nn)
+{
+ int i;
+
+ for (i = 0; i < NFP_NET_CFG_RSS_KEY_SZ; i += 4)
+ nn_writel(nn, NFP_NET_CFG_RSS_KEY + i,
+ get_unaligned_le32(nn->rss_key + i));
+}
+
+/**
+ * nfp_net_coalesce_write_cfg() - Write irq coalescence configuration to HW
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_coalesce_write_cfg(struct nfp_net *nn)
+{
+ u8 i;
+ u32 factor;
+ u32 value;
+
+ /* Compute factor used to convert coalesce '_usecs' parameters to
+ * ME timestamp ticks. There are 16 ME clock cycles for each timestamp
+ * count.
+ */
+ factor = nn->me_freq_mhz / 16;
+
+ /* copy RX interrupt coalesce parameters */
+ value = (nn->rx_coalesce_max_frames << 16) |
+ (factor * nn->rx_coalesce_usecs);
+ for (i = 0; i < nn->num_r_vecs; i++)
+ nn_writel(nn, NFP_NET_CFG_RXR_IRQ_MOD(i), value);
+
+ /* copy TX interrupt coalesce parameters */
+ value = (nn->tx_coalesce_max_frames << 16) |
+ (factor * nn->tx_coalesce_usecs);
+ for (i = 0; i < nn->num_r_vecs; i++)
+ nn_writel(nn, NFP_NET_CFG_TXR_IRQ_MOD(i), value);
+}
+
+/**
+ * nfp_net_write_mac_addr() - Write mac address to device registers
+ * @nn: NFP Net device to reconfigure
+ * @mac: Six-byte MAC address to be written
+ *
+ * We do a bit of byte swapping dance because firmware is LE.
+ */
+static void nfp_net_write_mac_addr(struct nfp_net *nn, const u8 *mac)
+{
+ nn_writel(nn, NFP_NET_CFG_MACADDR + 0,
+ get_unaligned_be32(nn->netdev->dev_addr));
+ /* We can't do writew for NFP-3200 compatibility */
+ nn_writel(nn, NFP_NET_CFG_MACADDR + 4,
+ get_unaligned_be16(nn->netdev->dev_addr + 4) << 16);
+}
+
+/**
+ * nfp_net_clear_config_and_disable() - Clear control BAR and disable NFP
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_clear_config_and_disable(struct nfp_net *nn)
+{
+ u32 new_ctrl, update;
+ int err;
+
+ new_ctrl = nn->ctrl;
+ new_ctrl &= ~NFP_NET_CFG_CTRL_ENABLE;
+ update = NFP_NET_CFG_UPDATE_GEN;
+ update |= NFP_NET_CFG_UPDATE_MSIX;
+ update |= NFP_NET_CFG_UPDATE_RING;
+
+ if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG)
+ new_ctrl &= ~NFP_NET_CFG_CTRL_RINGCFG;
+
+ nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0);
+ nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0);
+
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ err = nfp_net_reconfig(nn, update);
+ if (err) {
+ nn_err(nn, "Could not disable device: %d\n", err);
+ return;
+ }
+
+ nn->ctrl = new_ctrl;
+}
+
+/**
+ * nfp_net_start_vec() - Start ring vector
+ * @nn: NFP Net device structure
+ * @r_vec: Ring vector to be started
+ */
+static int nfp_net_start_vec(struct nfp_net *nn, struct nfp_net_r_vector *r_vec)
+{
+ unsigned int irq_vec;
+ int err = 0;
+
+ irq_vec = nn->irq_entries[r_vec->irq_idx].vector;
+
+ disable_irq(irq_vec);
+
+ err = nfp_net_rx_fill_freelist(r_vec->rx_ring);
+ if (err) {
+ nn_err(nn, "RV%02d: couldn't allocate enough buffers\n",
+ r_vec->irq_idx);
+ goto out;
+ }
+
+ napi_enable(&r_vec->napi);
+out:
+ enable_irq(irq_vec);
+
+ return err;
+}
+
+static int nfp_net_netdev_open(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int err, r;
+ u32 update = 0;
+ u32 new_ctrl;
+
+ if (nn->ctrl & NFP_NET_CFG_CTRL_ENABLE) {
+ nn_err(nn, "Dev is already enabled: 0x%08x\n", nn->ctrl);
+ return -EBUSY;
+ }
+
+ new_ctrl = nn->ctrl;
+
+ /* Step 1: Allocate resources for rings and the like
+ * - Request interrupts
+ * - Allocate RX and TX ring resources
+ * - Setup initial RSS table
+ */
+ err = nfp_net_aux_irq_request(nn, NFP_NET_CFG_EXN, "%s-exn",
+ nn->exn_name, sizeof(nn->exn_name),
+ NFP_NET_IRQ_EXN_IDX, nn->exn_handler);
+ if (err)
+ return err;
+
+ err = nfp_net_alloc_rings(nn);
+ if (err)
+ goto err_free_exn;
+
+ err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings);
+ if (err)
+ goto err_free_rings;
+
+ err = netif_set_real_num_rx_queues(netdev, nn->num_rx_rings);
+ if (err)
+ goto err_free_rings;
+
+ if (nn->cap & NFP_NET_CFG_CTRL_RSS) {
+ nfp_net_rss_write_key(nn);
+ nfp_net_rss_write_itbl(nn);
+ nn_writel(nn, NFP_NET_CFG_RSS_CTRL, nn->rss_cfg);
+ update |= NFP_NET_CFG_UPDATE_RSS;
+ }
+
+ if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) {
+ nfp_net_coalesce_write_cfg(nn);
+
+ new_ctrl |= NFP_NET_CFG_CTRL_IRQMOD;
+ update |= NFP_NET_CFG_UPDATE_IRQMOD;
+ }
+
+ /* Step 2: Configure the NFP
+ * - Enable rings from 0 to tx_rings/rx_rings - 1.
+ * - Write MAC address (in case it changed)
+ * - Set the MTU
+ * - Set the Freelist buffer size
+ * - Enable the FW
+ */
+ nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, nn->num_tx_rings == 64 ?
+ 0xffffffffffffffffULL : ((u64)1 << nn->num_tx_rings) - 1);
+
+ nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, nn->num_rx_rings == 64 ?
+ 0xffffffffffffffffULL : ((u64)1 << nn->num_rx_rings) - 1);
+
+ nfp_net_write_mac_addr(nn, netdev->dev_addr);
+
+ nn_writel(nn, NFP_NET_CFG_MTU, netdev->mtu);
+ nn_writel(nn, NFP_NET_CFG_FLBUFSZ, nn->fl_bufsz);
+
+ /* Enable device */
+ new_ctrl |= NFP_NET_CFG_CTRL_ENABLE;
+ update |= NFP_NET_CFG_UPDATE_GEN;
+ update |= NFP_NET_CFG_UPDATE_MSIX;
+ update |= NFP_NET_CFG_UPDATE_RING;
+ if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG)
+ new_ctrl |= NFP_NET_CFG_CTRL_RINGCFG;
+
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ err = nfp_net_reconfig(nn, update);
+ if (err)
+ goto err_clear_config;
+
+ nn->ctrl = new_ctrl;
+
+ /* Since reconfiguration requests while NFP is down are ignored we
+ * have to wipe the entire VXLAN configuration and reinitialize it.
+ */
+ if (nn->ctrl & NFP_NET_CFG_CTRL_VXLAN) {
+ memset(&nn->vxlan_ports, 0, sizeof(nn->vxlan_ports));
+ memset(&nn->vxlan_usecnt, 0, sizeof(nn->vxlan_usecnt));
+ vxlan_get_rx_port(netdev);
+ }
+
+ /* Step 3: Enable for kernel
+ * - put some freelist descriptors on each RX ring
+ * - enable NAPI on each ring
+ * - enable all TX queues
+ * - set link state
+ */
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ err = nfp_net_start_vec(nn, &nn->r_vecs[r]);
+ if (err)
+ goto err_disable_napi;
+ }
+
+ netif_tx_wake_all_queues(netdev);
+
+ err = nfp_net_aux_irq_request(nn, NFP_NET_CFG_LSC, "%s-lsc",
+ nn->lsc_name, sizeof(nn->lsc_name),
+ NFP_NET_IRQ_LSC_IDX, nn->lsc_handler);
+ if (err)
+ goto err_stop_tx;
+ nfp_net_read_link_status(nn);
+
+ return 0;
+
+err_stop_tx:
+ netif_tx_disable(netdev);
+ for (r = 0; r < nn->num_r_vecs; r++)
+ nfp_net_tx_flush(nn->r_vecs[r].tx_ring);
+err_disable_napi:
+ while (r--) {
+ napi_disable(&nn->r_vecs[r].napi);
+ nfp_net_rx_flush(nn->r_vecs[r].rx_ring);
+ }
+err_clear_config:
+ nfp_net_clear_config_and_disable(nn);
+err_free_rings:
+ nfp_net_free_rings(nn);
+err_free_exn:
+ nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX);
+ return err;
+}
+
+/**
+ * nfp_net_netdev_close() - Called when the device is downed
+ * @netdev: netdev structure
+ */
+static int nfp_net_netdev_close(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int r;
+
+ if (!(nn->ctrl & NFP_NET_CFG_CTRL_ENABLE)) {
+ nn_err(nn, "Dev is not up: 0x%08x\n", nn->ctrl);
+ return 0;
+ }
+
+ /* Step 1: Disable RX and TX rings from the Linux kernel perspective
+ */
+ nfp_net_aux_irq_free(nn, NFP_NET_CFG_LSC, NFP_NET_IRQ_LSC_IDX);
+ netif_carrier_off(netdev);
+ nn->link_up = false;
+
+ for (r = 0; r < nn->num_r_vecs; r++)
+ napi_disable(&nn->r_vecs[r].napi);
+
+ netif_tx_disable(netdev);
+
+ /* Step 2: Tell NFP
+ */
+ nfp_net_clear_config_and_disable(nn);
+
+ /* Step 3: Free resources
+ */
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ nfp_net_rx_flush(nn->r_vecs[r].rx_ring);
+ nfp_net_tx_flush(nn->r_vecs[r].tx_ring);
+ }
+
+ nfp_net_free_rings(nn);
+ nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX);
+
+ nn_dbg(nn, "%s down", netdev->name);
+ return 0;
+}
+
+static void nfp_net_set_rx_mode(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 new_ctrl;
+
+ new_ctrl = nn->ctrl;
+
+ if (netdev->flags & IFF_PROMISC) {
+ if (nn->cap & NFP_NET_CFG_CTRL_PROMISC)
+ new_ctrl |= NFP_NET_CFG_CTRL_PROMISC;
+ else
+ nn_warn(nn, "FW does not support promiscuous mode\n");
+ } else {
+ new_ctrl &= ~NFP_NET_CFG_CTRL_PROMISC;
+ }
+
+ if (new_ctrl == nn->ctrl)
+ return;
+
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ if (nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN))
+ return;
+
+ nn->ctrl = new_ctrl;
+}
+
+static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 tmp;
+
+ nn_dbg(nn, "New MTU = %d\n", new_mtu);
+
+ if (new_mtu < 68 || new_mtu > nn->max_mtu) {
+ nn_err(nn, "New MTU (%d) is not valid\n", new_mtu);
+ return -EINVAL;
+ }
+
+ netdev->mtu = new_mtu;
+
+ /* Freelist buffer size rounded up to the nearest 1K */
+ tmp = new_mtu + ETH_HLEN + VLAN_HLEN + NFP_NET_MAX_PREPEND;
+ nn->fl_bufsz = roundup(tmp, 1024);
+
+ /* restart if running */
+ if (netif_running(netdev)) {
+ nfp_net_netdev_close(netdev);
+ nfp_net_netdev_open(netdev);
+ }
+
+ return 0;
+}
+
+static struct rtnl_link_stats64 *nfp_net_stat64(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int r;
+
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ struct nfp_net_r_vector *r_vec = &nn->r_vecs[r];
+ u64 data[3];
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin(&r_vec->rx_sync);
+ data[0] = r_vec->rx_pkts;
+ data[1] = r_vec->rx_bytes;
+ data[2] = r_vec->rx_drops;
+ } while (u64_stats_fetch_retry(&r_vec->rx_sync, start));
+ stats->rx_packets += data[0];
+ stats->rx_bytes += data[1];
+ stats->rx_dropped += data[2];
+
+ do {
+ start = u64_stats_fetch_begin(&r_vec->tx_sync);
+ data[0] = r_vec->tx_pkts;
+ data[1] = r_vec->tx_bytes;
+ data[2] = r_vec->tx_errors;
+ } while (u64_stats_fetch_retry(&r_vec->tx_sync, start));
+ stats->tx_packets += data[0];
+ stats->tx_bytes += data[1];
+ stats->tx_errors += data[2];
+ }
+
+ return stats;
+}
+
+static int nfp_net_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ netdev_features_t changed = netdev->features ^ features;
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 new_ctrl;
+ int err;
+
+ /* Assume this is not called with features we have not advertised */
+
+ new_ctrl = nn->ctrl;
+
+ if (changed & NETIF_F_RXCSUM) {
+ if (features & NETIF_F_RXCSUM)
+ new_ctrl |= NFP_NET_CFG_CTRL_RXCSUM;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_RXCSUM;
+ }
+
+ if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
+ if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+ new_ctrl |= NFP_NET_CFG_CTRL_TXCSUM;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_TXCSUM;
+ }
+
+ if (changed & (NETIF_F_TSO | NETIF_F_TSO6)) {
+ if (features & (NETIF_F_TSO | NETIF_F_TSO6))
+ new_ctrl |= NFP_NET_CFG_CTRL_LSO;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_LSO;
+ }
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
+ if (features & NETIF_F_HW_VLAN_CTAG_RX)
+ new_ctrl |= NFP_NET_CFG_CTRL_RXVLAN;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_RXVLAN;
+ }
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_TX) {
+ if (features & NETIF_F_HW_VLAN_CTAG_TX)
+ new_ctrl |= NFP_NET_CFG_CTRL_TXVLAN;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_TXVLAN;
+ }
+
+ if (changed & NETIF_F_SG) {
+ if (features & NETIF_F_SG)
+ new_ctrl |= NFP_NET_CFG_CTRL_GATHER;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_GATHER;
+ }
+
+ nn_dbg(nn, "Feature change 0x%llx -> 0x%llx (changed=0x%llx)\n",
+ netdev->features, features, changed);
+
+ if (new_ctrl == nn->ctrl)
+ return 0;
+
+ nn_dbg(nn, "NIC ctrl: 0x%x -> 0x%x\n", nn->ctrl, new_ctrl);
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN);
+ if (err)
+ return err;
+
+ nn->ctrl = new_ctrl;
+
+ return 0;
+}
+
+static netdev_features_t
+nfp_net_features_check(struct sk_buff *skb, struct net_device *dev,
+ netdev_features_t features)
+{
+ u8 l4_hdr;
+
+ /* We can't do TSO over double tagged packets (802.1AD) */
+ features &= vlan_features_check(skb, features);
+
+ if (!skb->encapsulation)
+ return features;
+
+ /* Ensure that inner L4 header offset fits into TX descriptor field */
+ if (skb_is_gso(skb)) {
+ u32 hdrlen;
+
+ hdrlen = skb_inner_transport_header(skb) - skb->data +
+ inner_tcp_hdrlen(skb);
+
+ if (unlikely(hdrlen > NFP_NET_LSO_MAX_HDR_SZ))
+ features &= ~NETIF_F_GSO_MASK;
+ }
+
+ /* VXLAN/GRE check */
+ switch (vlan_get_protocol(skb)) {
+ case htons(ETH_P_IP):
+ l4_hdr = ip_hdr(skb)->protocol;
+ break;
+ case htons(ETH_P_IPV6):
+ l4_hdr = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
+ }
+
+ if (skb->inner_protocol_type != ENCAP_TYPE_ETHER ||
+ skb->inner_protocol != htons(ETH_P_TEB) ||
+ (l4_hdr != IPPROTO_UDP && l4_hdr != IPPROTO_GRE) ||
+ (l4_hdr == IPPROTO_UDP &&
+ (skb_inner_mac_header(skb) - skb_transport_header(skb) !=
+ sizeof(struct udphdr) + sizeof(struct vxlanhdr))))
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
+
+ return features;
+}
+
+/**
+ * nfp_net_set_vxlan_port() - set vxlan port in SW and reconfigure HW
+ * @nn: NFP Net device to reconfigure
+ * @idx: Index into the port table where new port should be written
+ * @port: UDP port to configure (pass zero to remove VXLAN port)
+ */
+static void nfp_net_set_vxlan_port(struct nfp_net *nn, int idx, __be16 port)
+{
+ int i;
+
+ nn->vxlan_ports[idx] = port;
+
+ if (!(nn->ctrl & NFP_NET_CFG_CTRL_VXLAN))
+ return;
+
+ BUILD_BUG_ON(NFP_NET_N_VXLAN_PORTS & 1);
+ for (i = 0; i < NFP_NET_N_VXLAN_PORTS; i += 2)
+ nn_writel(nn, NFP_NET_CFG_VXLAN_PORT + i * sizeof(port),
+ be16_to_cpu(nn->vxlan_ports[i + 1]) << 16 |
+ be16_to_cpu(nn->vxlan_ports[i]));
+
+ nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VXLAN);
+}
+
+/**
+ * nfp_net_find_vxlan_idx() - find table entry of the port or a free one
+ * @nn: NFP Network structure
+ * @port: UDP port to look for
+ *
+ * Return: if the port is already in the table -- it's position;
+ * if the port is not in the table -- free position to use;
+ * if the table is full -- -ENOSPC.
+ */
+static int nfp_net_find_vxlan_idx(struct nfp_net *nn, __be16 port)
+{
+ int i, free_idx = -ENOSPC;
+
+ for (i = 0; i < NFP_NET_N_VXLAN_PORTS; i++) {
+ if (nn->vxlan_ports[i] == port)
+ return i;
+ if (!nn->vxlan_usecnt[i])
+ free_idx = i;
+ }
+
+ return free_idx;
+}
+
+static void nfp_net_add_vxlan_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int idx;
+
+ idx = nfp_net_find_vxlan_idx(nn, port);
+ if (idx == -ENOSPC)
+ return;
+
+ if (!nn->vxlan_usecnt[idx]++)
+ nfp_net_set_vxlan_port(nn, idx, port);
+}
+
+static void nfp_net_del_vxlan_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int idx;
+
+ idx = nfp_net_find_vxlan_idx(nn, port);
+ if (!nn->vxlan_usecnt[idx] || idx == -ENOSPC)
+ return;
+
+ if (!--nn->vxlan_usecnt[idx])
+ nfp_net_set_vxlan_port(nn, idx, 0);
+}
+
+static const struct net_device_ops nfp_net_netdev_ops = {
+ .ndo_open = nfp_net_netdev_open,
+ .ndo_stop = nfp_net_netdev_close,
+ .ndo_start_xmit = nfp_net_tx,
+ .ndo_get_stats64 = nfp_net_stat64,
+ .ndo_tx_timeout = nfp_net_tx_timeout,
+ .ndo_set_rx_mode = nfp_net_set_rx_mode,
+ .ndo_change_mtu = nfp_net_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_features = nfp_net_set_features,
+ .ndo_features_check = nfp_net_features_check,
+ .ndo_add_vxlan_port = nfp_net_add_vxlan_port,
+ .ndo_del_vxlan_port = nfp_net_del_vxlan_port,
+};
+
+/**
+ * nfp_net_info() - Print general info about the NIC
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_info(struct nfp_net *nn)
+{
+ nn_info(nn, "Netronome %s %sNetdev: TxQs=%d/%d RxQs=%d/%d\n",
+ nn->is_nfp3200 ? "NFP-32xx" : "NFP-6xxx",
+ nn->is_vf ? "VF " : "",
+ nn->num_tx_rings, nn->max_tx_rings,
+ nn->num_rx_rings, nn->max_rx_rings);
+ nn_info(nn, "VER: %d.%d.%d.%d, Maximum supported MTU: %d\n",
+ nn->fw_ver.resv, nn->fw_ver.class,
+ nn->fw_ver.major, nn->fw_ver.minor,
+ nn->max_mtu);
+ nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ nn->cap,
+ nn->cap & NFP_NET_CFG_CTRL_PROMISC ? "PROMISC " : "",
+ nn->cap & NFP_NET_CFG_CTRL_L2BC ? "L2BCFILT " : "",
+ nn->cap & NFP_NET_CFG_CTRL_L2MC ? "L2MCFILT " : "",
+ nn->cap & NFP_NET_CFG_CTRL_RXCSUM ? "RXCSUM " : "",
+ nn->cap & NFP_NET_CFG_CTRL_TXCSUM ? "TXCSUM " : "",
+ nn->cap & NFP_NET_CFG_CTRL_RXVLAN ? "RXVLAN " : "",
+ nn->cap & NFP_NET_CFG_CTRL_TXVLAN ? "TXVLAN " : "",
+ nn->cap & NFP_NET_CFG_CTRL_SCATTER ? "SCATTER " : "",
+ nn->cap & NFP_NET_CFG_CTRL_GATHER ? "GATHER " : "",
+ nn->cap & NFP_NET_CFG_CTRL_LSO ? "TSO " : "",
+ nn->cap & NFP_NET_CFG_CTRL_RSS ? "RSS " : "",
+ nn->cap & NFP_NET_CFG_CTRL_L2SWITCH ? "L2SWITCH " : "",
+ nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO ? "AUTOMASK " : "",
+ nn->cap & NFP_NET_CFG_CTRL_IRQMOD ? "IRQMOD " : "",
+ nn->cap & NFP_NET_CFG_CTRL_VXLAN ? "VXLAN " : "",
+ nn->cap & NFP_NET_CFG_CTRL_NVGRE ? "NVGRE " : "");
+}
+
+/**
+ * nfp_net_netdev_alloc() - Allocate netdev and related structure
+ * @pdev: PCI device
+ * @max_tx_rings: Maximum number of TX rings supported by device
+ * @max_rx_rings: Maximum number of RX rings supported by device
+ *
+ * This function allocates a netdev device and fills in the initial
+ * part of the @struct nfp_net structure.
+ *
+ * Return: NFP Net device structure, or ERR_PTR on error.
+ */
+struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev,
+ int max_tx_rings, int max_rx_rings)
+{
+ struct net_device *netdev;
+ struct nfp_net *nn;
+ int nqs;
+
+ netdev = alloc_etherdev_mqs(sizeof(struct nfp_net),
+ max_tx_rings, max_rx_rings);
+ if (!netdev)
+ return ERR_PTR(-ENOMEM);
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ nn = netdev_priv(netdev);
+
+ nn->netdev = netdev;
+ nn->pdev = pdev;
+
+ nn->max_tx_rings = max_tx_rings;
+ nn->max_rx_rings = max_rx_rings;
+
+ nqs = netif_get_num_default_rss_queues();
+ nn->num_tx_rings = min_t(int, nqs, max_tx_rings);
+ nn->num_rx_rings = min_t(int, nqs, max_rx_rings);
+
+ nn->txd_cnt = NFP_NET_TX_DESCS_DEFAULT;
+ nn->rxd_cnt = NFP_NET_RX_DESCS_DEFAULT;
+
+ spin_lock_init(&nn->reconfig_lock);
+ spin_lock_init(&nn->link_status_lock);
+
+ return nn;
+}
+
+/**
+ * nfp_net_netdev_free() - Undo what @nfp_net_netdev_alloc() did
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_netdev_free(struct nfp_net *nn)
+{
+ free_netdev(nn->netdev);
+}
+
+/**
+ * nfp_net_rss_init() - Set the initial RSS parameters
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_rss_init(struct nfp_net *nn)
+{
+ int i;
+
+ netdev_rss_key_fill(nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ);
+
+ for (i = 0; i < sizeof(nn->rss_itbl); i++)
+ nn->rss_itbl[i] =
+ ethtool_rxfh_indir_default(i, nn->num_rx_rings);
+
+ /* Enable IPv4/IPv6 TCP by default */
+ nn->rss_cfg = NFP_NET_CFG_RSS_IPV4_TCP |
+ NFP_NET_CFG_RSS_IPV6_TCP |
+ NFP_NET_CFG_RSS_TOEPLITZ |
+ NFP_NET_CFG_RSS_MASK;
+}
+
+/**
+ * nfp_net_irqmod_init() - Set the initial IRQ moderation parameters
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_irqmod_init(struct nfp_net *nn)
+{
+ nn->rx_coalesce_usecs = 50;
+ nn->rx_coalesce_max_frames = 64;
+ nn->tx_coalesce_usecs = 50;
+ nn->tx_coalesce_max_frames = 64;
+}
+
+/**
+ * nfp_net_netdev_init() - Initialise/finalise the netdev structure
+ * @netdev: netdev structure
+ *
+ * Return: 0 on success or negative errno on error.
+ */
+int nfp_net_netdev_init(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int err;
+
+ /* Get some of the read-only fields from the BAR */
+ nn->cap = nn_readl(nn, NFP_NET_CFG_CAP);
+ nn->max_mtu = nn_readl(nn, NFP_NET_CFG_MAX_MTU);
+
+ nfp_net_write_mac_addr(nn, nn->netdev->dev_addr);
+
+ /* Set default MTU and Freelist buffer size */
+ if (nn->max_mtu < NFP_NET_DEFAULT_MTU)
+ netdev->mtu = nn->max_mtu;
+ else
+ netdev->mtu = NFP_NET_DEFAULT_MTU;
+ nn->fl_bufsz = NFP_NET_DEFAULT_RX_BUFSZ;
+
+ /* Advertise/enable offloads based on capabilities
+ *
+ * Note: netdev->features show the currently enabled features
+ * and netdev->hw_features advertises which features are
+ * supported. By default we enable most features.
+ */
+ netdev->hw_features = NETIF_F_HIGHDMA;
+ if (nn->cap & NFP_NET_CFG_CTRL_RXCSUM) {
+ netdev->hw_features |= NETIF_F_RXCSUM;
+ nn->ctrl |= NFP_NET_CFG_CTRL_RXCSUM;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_TXCSUM) {
+ netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+ nn->ctrl |= NFP_NET_CFG_CTRL_TXCSUM;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_GATHER) {
+ netdev->hw_features |= NETIF_F_SG;
+ nn->ctrl |= NFP_NET_CFG_CTRL_GATHER;
+ }
+ if ((nn->cap & NFP_NET_CFG_CTRL_LSO) && nn->fw_ver.major > 2) {
+ netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
+ nn->ctrl |= NFP_NET_CFG_CTRL_LSO;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_RSS) {
+ netdev->hw_features |= NETIF_F_RXHASH;
+ nfp_net_rss_init(nn);
+ nn->ctrl |= NFP_NET_CFG_CTRL_RSS;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_VXLAN &&
+ nn->cap & NFP_NET_CFG_CTRL_NVGRE) {
+ if (nn->cap & NFP_NET_CFG_CTRL_LSO)
+ netdev->hw_features |= NETIF_F_GSO_GRE |
+ NETIF_F_GSO_UDP_TUNNEL;
+ nn->ctrl |= NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE;
+
+ netdev->hw_enc_features = netdev->hw_features;
+ }
+
+ netdev->vlan_features = netdev->hw_features;
+
+ if (nn->cap & NFP_NET_CFG_CTRL_RXVLAN) {
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+ nn->ctrl |= NFP_NET_CFG_CTRL_RXVLAN;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_TXVLAN) {
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
+ nn->ctrl |= NFP_NET_CFG_CTRL_TXVLAN;
+ }
+
+ netdev->features = netdev->hw_features;
+
+ /* Advertise but disable TSO by default. */
+ netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
+
+ /* Allow L2 Broadcast and Multicast through by default, if supported */
+ if (nn->cap & NFP_NET_CFG_CTRL_L2BC)
+ nn->ctrl |= NFP_NET_CFG_CTRL_L2BC;
+ if (nn->cap & NFP_NET_CFG_CTRL_L2MC)
+ nn->ctrl |= NFP_NET_CFG_CTRL_L2MC;
+
+ /* Allow IRQ moderation, if supported */
+ if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) {
+ nfp_net_irqmod_init(nn);
+ nn->ctrl |= NFP_NET_CFG_CTRL_IRQMOD;
+ }
+
+ /* On NFP-3200 enable MSI-X auto-masking, if supported and the
+ * interrupts are not shared.
+ */
+ if (nn->is_nfp3200 && nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO)
+ nn->ctrl |= NFP_NET_CFG_CTRL_MSIXAUTO;
+
+ /* On NFP4000/NFP6000, determine RX packet/metadata boundary offset */
+ if (nn->fw_ver.major >= 2)
+ nn->rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET);
+ else
+ nn->rx_offset = NFP_NET_RX_OFFSET;
+
+ /* Stash the re-configuration queue away. First odd queue in TX Bar */
+ nn->qcp_cfg = nn->tx_bar + NFP_QCP_QUEUE_ADDR_SZ;
+
+ /* Make sure the FW knows the netdev is supposed to be disabled here */
+ nn_writel(nn, NFP_NET_CFG_CTRL, 0);
+ nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0);
+ nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0);
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RING |
+ NFP_NET_CFG_UPDATE_GEN);
+ if (err)
+ return err;
+
+ /* Finalise the netdev setup */
+ ether_setup(netdev);
+ netdev->netdev_ops = &nfp_net_netdev_ops;
+ netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000);
+ netif_carrier_off(netdev);
+
+ nfp_net_set_ethtool_ops(netdev);
+ nfp_net_irqs_assign(netdev);
+
+ return register_netdev(netdev);
+}
+
+/**
+ * nfp_net_netdev_clean() - Undo what nfp_net_netdev_init() did.
+ * @netdev: netdev structure
+ */
+void nfp_net_netdev_clean(struct net_device *netdev)
+{
+ unregister_netdev(netdev);
+}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
new file mode 100644
index 000000000000..8692003aeed8
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * nfp_net_ctrl.h
+ * Netronome network device driver: Control BAR layout
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ * Brad Petrus <brad.petrus@netronome.com>
+ */
+
+#ifndef _NFP_NET_CTRL_H_
+#define _NFP_NET_CTRL_H_
+
+/* IMPORTANT: This header file is shared with the FW,
+ * no OS specific constructs, please!
+ */
+
+/**
+ * Configuration BAR size.
+ *
+ * The configuration BAR is 8K in size, but on the NFP6000, due to
+ * THB-350, 32k needs to be reserved.
+ */
+#define NFP_NET_CFG_BAR_SZ (32 * 1024)
+
+/**
+ * Offset in Freelist buffer where packet starts on RX
+ */
+#define NFP_NET_RX_OFFSET 32
+
+/**
+ * Maximum header size supported for LSO frames
+ */
+#define NFP_NET_LSO_MAX_HDR_SZ 255
+
+/**
+ * Hash type pre-pended when a RSS hash was computed
+ */
+#define NFP_NET_RSS_NONE 0
+#define NFP_NET_RSS_IPV4 1
+#define NFP_NET_RSS_IPV6 2
+#define NFP_NET_RSS_IPV6_EX 3
+#define NFP_NET_RSS_IPV4_TCP 4
+#define NFP_NET_RSS_IPV6_TCP 5
+#define NFP_NET_RSS_IPV6_EX_TCP 6
+#define NFP_NET_RSS_IPV4_UDP 7
+#define NFP_NET_RSS_IPV6_UDP 8
+#define NFP_NET_RSS_IPV6_EX_UDP 9
+
+/**
+ * @NFP_NET_TXR_MAX: Maximum number of TX rings
+ * @NFP_NET_TXR_MASK: Mask for TX rings
+ * @NFP_NET_RXR_MAX: Maximum number of RX rings
+ * @NFP_NET_RXR_MASK: Mask for RX rings
+ */
+#define NFP_NET_TXR_MAX 64
+#define NFP_NET_TXR_MASK (NFP_NET_TXR_MAX - 1)
+#define NFP_NET_RXR_MAX 64
+#define NFP_NET_RXR_MASK (NFP_NET_RXR_MAX - 1)
+
+/**
+ * Read/Write config words (0x0000 - 0x002c)
+ * @NFP_NET_CFG_CTRL: Global control
+ * @NFP_NET_CFG_UPDATE: Indicate which fields are updated
+ * @NFP_NET_CFG_TXRS_ENABLE: Bitmask of enabled TX rings
+ * @NFP_NET_CFG_RXRS_ENABLE: Bitmask of enabled RX rings
+ * @NFP_NET_CFG_MTU: Set MTU size
+ * @NFP_NET_CFG_FLBUFSZ: Set freelist buffer size (must be larger than MTU)
+ * @NFP_NET_CFG_EXN: MSI-X table entry for exceptions
+ * @NFP_NET_CFG_LSC: MSI-X table entry for link state changes
+ * @NFP_NET_CFG_MACADDR: MAC address
+ *
+ * TODO:
+ * - define Error details in UPDATE
+ */
+#define NFP_NET_CFG_CTRL 0x0000
+#define NFP_NET_CFG_CTRL_ENABLE (0x1 << 0) /* Global enable */
+#define NFP_NET_CFG_CTRL_PROMISC (0x1 << 1) /* Enable Promisc mode */
+#define NFP_NET_CFG_CTRL_L2BC (0x1 << 2) /* Allow L2 Broadcast */
+#define NFP_NET_CFG_CTRL_L2MC (0x1 << 3) /* Allow L2 Multicast */
+#define NFP_NET_CFG_CTRL_RXCSUM (0x1 << 4) /* Enable RX Checksum */
+#define NFP_NET_CFG_CTRL_TXCSUM (0x1 << 5) /* Enable TX Checksum */
+#define NFP_NET_CFG_CTRL_RXVLAN (0x1 << 6) /* Enable VLAN strip */
+#define NFP_NET_CFG_CTRL_TXVLAN (0x1 << 7) /* Enable VLAN insert */
+#define NFP_NET_CFG_CTRL_SCATTER (0x1 << 8) /* Scatter DMA */
+#define NFP_NET_CFG_CTRL_GATHER (0x1 << 9) /* Gather DMA */
+#define NFP_NET_CFG_CTRL_LSO (0x1 << 10) /* LSO/TSO */
+#define NFP_NET_CFG_CTRL_RINGCFG (0x1 << 16) /* Ring runtime changes */
+#define NFP_NET_CFG_CTRL_RSS (0x1 << 17) /* RSS */
+#define NFP_NET_CFG_CTRL_IRQMOD (0x1 << 18) /* Interrupt moderation */
+#define NFP_NET_CFG_CTRL_RINGPRIO (0x1 << 19) /* Ring priorities */
+#define NFP_NET_CFG_CTRL_MSIXAUTO (0x1 << 20) /* MSI-X auto-masking */
+#define NFP_NET_CFG_CTRL_TXRWB (0x1 << 21) /* Write-back of TX ring*/
+#define NFP_NET_CFG_CTRL_L2SWITCH (0x1 << 22) /* L2 Switch */
+#define NFP_NET_CFG_CTRL_L2SWITCH_LOCAL (0x1 << 23) /* Switch to local */
+#define NFP_NET_CFG_CTRL_VXLAN (0x1 << 24) /* VXLAN tunnel support */
+#define NFP_NET_CFG_CTRL_NVGRE (0x1 << 25) /* NVGRE tunnel support */
+#define NFP_NET_CFG_UPDATE 0x0004
+#define NFP_NET_CFG_UPDATE_GEN (0x1 << 0) /* General update */
+#define NFP_NET_CFG_UPDATE_RING (0x1 << 1) /* Ring config change */
+#define NFP_NET_CFG_UPDATE_RSS (0x1 << 2) /* RSS config change */
+#define NFP_NET_CFG_UPDATE_TXRPRIO (0x1 << 3) /* TX Ring prio change */
+#define NFP_NET_CFG_UPDATE_RXRPRIO (0x1 << 4) /* RX Ring prio change */
+#define NFP_NET_CFG_UPDATE_MSIX (0x1 << 5) /* MSI-X change */
+#define NFP_NET_CFG_UPDATE_L2SWITCH (0x1 << 6) /* Switch changes */
+#define NFP_NET_CFG_UPDATE_RESET (0x1 << 7) /* Update due to FLR */
+#define NFP_NET_CFG_UPDATE_IRQMOD (0x1 << 8) /* IRQ mod change */
+#define NFP_NET_CFG_UPDATE_VXLAN (0x1 << 9) /* VXLAN port change */
+#define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */
+#define NFP_NET_CFG_TXRS_ENABLE 0x0008
+#define NFP_NET_CFG_RXRS_ENABLE 0x0010
+#define NFP_NET_CFG_MTU 0x0018
+#define NFP_NET_CFG_FLBUFSZ 0x001c
+#define NFP_NET_CFG_EXN 0x001f
+#define NFP_NET_CFG_LSC 0x0020
+#define NFP_NET_CFG_MACADDR 0x0024
+
+/**
+ * Read-only words (0x0030 - 0x0050):
+ * @NFP_NET_CFG_VERSION: Firmware version number
+ * @NFP_NET_CFG_STS: Status
+ * @NFP_NET_CFG_CAP: Capabilities (same bits as @NFP_NET_CFG_CTRL)
+ * @NFP_NET_MAX_TXRINGS: Maximum number of TX rings
+ * @NFP_NET_MAX_RXRINGS: Maximum number of RX rings
+ * @NFP_NET_MAX_MTU: Maximum support MTU
+ * @NFP_NET_CFG_START_TXQ: Start Queue Control Queue to use for TX (PF only)
+ * @NFP_NET_CFG_START_RXQ: Start Queue Control Queue to use for RX (PF only)
+ *
+ * TODO:
+ * - define more STS bits
+ */
+#define NFP_NET_CFG_VERSION 0x0030
+#define NFP_NET_CFG_VERSION_RESERVED_MASK (0xff << 24)
+#define NFP_NET_CFG_VERSION_CLASS_MASK (0xff << 16)
+#define NFP_NET_CFG_VERSION_CLASS(x) (((x) & 0xff) << 16)
+#define NFP_NET_CFG_VERSION_CLASS_GENERIC 0
+#define NFP_NET_CFG_VERSION_MAJOR_MASK (0xff << 8)
+#define NFP_NET_CFG_VERSION_MAJOR(x) (((x) & 0xff) << 8)
+#define NFP_NET_CFG_VERSION_MINOR_MASK (0xff << 0)
+#define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0)
+#define NFP_NET_CFG_STS 0x0034
+#define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */
+#define NFP_NET_CFG_CAP 0x0038
+#define NFP_NET_CFG_MAX_TXRINGS 0x003c
+#define NFP_NET_CFG_MAX_RXRINGS 0x0040
+#define NFP_NET_CFG_MAX_MTU 0x0044
+/* Next two words are being used by VFs for solving THB350 issue */
+#define NFP_NET_CFG_START_TXQ 0x0048
+#define NFP_NET_CFG_START_RXQ 0x004c
+
+/**
+ * NFP-3200 workaround (0x0050 - 0x0058)
+ * @NFP_NET_CFG_SPARE_ADDR: DMA address for ME code to use (e.g. YDS-155 fix)
+ */
+#define NFP_NET_CFG_SPARE_ADDR 0x0050
+/**
+ * NFP6000/NFP4000 - Prepend configuration
+ */
+#define NFP_NET_CFG_RX_OFFSET 0x0050
+#define NFP_NET_CFG_RX_OFFSET_DYNAMIC 0 /* Prepend mode */
+
+/**
+ * NFP6000/NFP4000 - VXLAN/UDP encap configuration
+ * @NFP_NET_CFG_VXLAN_PORT: Base address of table of tunnels' UDP dst ports
+ * @NFP_NET_CFG_VXLAN_SZ: Size of the UDP port table in bytes
+ */
+#define NFP_NET_CFG_VXLAN_PORT 0x0060
+#define NFP_NET_CFG_VXLAN_SZ 0x0008
+
+/**
+ * 64B reserved for future use (0x0080 - 0x00c0)
+ */
+#define NFP_NET_CFG_RESERVED 0x0080
+#define NFP_NET_CFG_RESERVED_SZ 0x0040
+
+/**
+ * RSS configuration (0x0100 - 0x01ac):
+ * Used only when NFP_NET_CFG_CTRL_RSS is enabled
+ * @NFP_NET_CFG_RSS_CFG: RSS configuration word
+ * @NFP_NET_CFG_RSS_KEY: RSS "secret" key
+ * @NFP_NET_CFG_RSS_ITBL: RSS indirection table
+ */
+#define NFP_NET_CFG_RSS_BASE 0x0100
+#define NFP_NET_CFG_RSS_CTRL NFP_NET_CFG_RSS_BASE
+#define NFP_NET_CFG_RSS_MASK (0x7f)
+#define NFP_NET_CFG_RSS_MASK_of(_x) ((_x) & 0x7f)
+#define NFP_NET_CFG_RSS_IPV4 (1 << 8) /* RSS for IPv4 */
+#define NFP_NET_CFG_RSS_IPV6 (1 << 9) /* RSS for IPv6 */
+#define NFP_NET_CFG_RSS_IPV4_TCP (1 << 10) /* RSS for IPv4/TCP */
+#define NFP_NET_CFG_RSS_IPV4_UDP (1 << 11) /* RSS for IPv4/UDP */
+#define NFP_NET_CFG_RSS_IPV6_TCP (1 << 12) /* RSS for IPv6/TCP */
+#define NFP_NET_CFG_RSS_IPV6_UDP (1 << 13) /* RSS for IPv6/UDP */
+#define NFP_NET_CFG_RSS_TOEPLITZ (1 << 24) /* Use Toeplitz hash */
+#define NFP_NET_CFG_RSS_KEY (NFP_NET_CFG_RSS_BASE + 0x4)
+#define NFP_NET_CFG_RSS_KEY_SZ 0x28
+#define NFP_NET_CFG_RSS_ITBL (NFP_NET_CFG_RSS_BASE + 0x4 + \
+ NFP_NET_CFG_RSS_KEY_SZ)
+#define NFP_NET_CFG_RSS_ITBL_SZ 0x80
+
+/**
+ * TX ring configuration (0x200 - 0x800)
+ * @NFP_NET_CFG_TXR_BASE: Base offset for TX ring configuration
+ * @NFP_NET_CFG_TXR_ADDR: Per TX ring DMA address (8B entries)
+ * @NFP_NET_CFG_TXR_WB_ADDR: Per TX ring write back DMA address (8B entries)
+ * @NFP_NET_CFG_TXR_SZ: Per TX ring ring size (1B entries)
+ * @NFP_NET_CFG_TXR_VEC: Per TX ring MSI-X table entry (1B entries)
+ * @NFP_NET_CFG_TXR_PRIO: Per TX ring priority (1B entries)
+ * @NFP_NET_CFG_TXR_IRQ_MOD: Per TX ring interrupt moderation packet
+ */
+#define NFP_NET_CFG_TXR_BASE 0x0200
+#define NFP_NET_CFG_TXR_ADDR(_x) (NFP_NET_CFG_TXR_BASE + ((_x) * 0x8))
+#define NFP_NET_CFG_TXR_WB_ADDR(_x) (NFP_NET_CFG_TXR_BASE + 0x200 + \
+ ((_x) * 0x8))
+#define NFP_NET_CFG_TXR_SZ(_x) (NFP_NET_CFG_TXR_BASE + 0x400 + (_x))
+#define NFP_NET_CFG_TXR_VEC(_x) (NFP_NET_CFG_TXR_BASE + 0x440 + (_x))
+#define NFP_NET_CFG_TXR_PRIO(_x) (NFP_NET_CFG_TXR_BASE + 0x480 + (_x))
+#define NFP_NET_CFG_TXR_IRQ_MOD(_x) (NFP_NET_CFG_TXR_BASE + 0x500 + \
+ ((_x) * 0x4))
+
+/**
+ * RX ring configuration (0x0800 - 0x0c00)
+ * @NFP_NET_CFG_RXR_BASE: Base offset for RX ring configuration
+ * @NFP_NET_CFG_RXR_ADDR: Per RX ring DMA address (8B entries)
+ * @NFP_NET_CFG_RXR_SZ: Per RX ring ring size (1B entries)
+ * @NFP_NET_CFG_RXR_VEC: Per RX ring MSI-X table entry (1B entries)
+ * @NFP_NET_CFG_RXR_PRIO: Per RX ring priority (1B entries)
+ * @NFP_NET_CFG_RXR_IRQ_MOD: Per RX ring interrupt moderation (4B entries)
+ */
+#define NFP_NET_CFG_RXR_BASE 0x0800
+#define NFP_NET_CFG_RXR_ADDR(_x) (NFP_NET_CFG_RXR_BASE + ((_x) * 0x8))
+#define NFP_NET_CFG_RXR_SZ(_x) (NFP_NET_CFG_RXR_BASE + 0x200 + (_x))
+#define NFP_NET_CFG_RXR_VEC(_x) (NFP_NET_CFG_RXR_BASE + 0x240 + (_x))
+#define NFP_NET_CFG_RXR_PRIO(_x) (NFP_NET_CFG_RXR_BASE + 0x280 + (_x))
+#define NFP_NET_CFG_RXR_IRQ_MOD(_x) (NFP_NET_CFG_RXR_BASE + 0x300 + \
+ ((_x) * 0x4))
+
+/**
+ * Interrupt Control/Cause registers (0x0c00 - 0x0d00)
+ * These registers are only used when MSI-X auto-masking is not
+ * enabled (@NFP_NET_CFG_CTRL_MSIXAUTO not set). The array is index
+ * by MSI-X entry and are 1B in size. If an entry is zero, the
+ * corresponding entry is enabled. If the FW generates an interrupt,
+ * it writes a cause into the corresponding field. This also masks
+ * the MSI-X entry and the host driver must clear the register to
+ * re-enable the interrupt.
+ */
+#define NFP_NET_CFG_ICR_BASE 0x0c00
+#define NFP_NET_CFG_ICR(_x) (NFP_NET_CFG_ICR_BASE + (_x))
+#define NFP_NET_CFG_ICR_UNMASKED 0x0
+#define NFP_NET_CFG_ICR_RXTX 0x1
+#define NFP_NET_CFG_ICR_LSC 0x2
+
+/**
+ * General device stats (0x0d00 - 0x0d90)
+ * all counters are 64bit.
+ */
+#define NFP_NET_CFG_STATS_BASE 0x0d00
+#define NFP_NET_CFG_STATS_RX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x00)
+#define NFP_NET_CFG_STATS_RX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x08)
+#define NFP_NET_CFG_STATS_RX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x10)
+#define NFP_NET_CFG_STATS_RX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x18)
+#define NFP_NET_CFG_STATS_RX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x20)
+#define NFP_NET_CFG_STATS_RX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x28)
+#define NFP_NET_CFG_STATS_RX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x30)
+#define NFP_NET_CFG_STATS_RX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x38)
+#define NFP_NET_CFG_STATS_RX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x40)
+
+#define NFP_NET_CFG_STATS_TX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x48)
+#define NFP_NET_CFG_STATS_TX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x50)
+#define NFP_NET_CFG_STATS_TX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x58)
+#define NFP_NET_CFG_STATS_TX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x60)
+#define NFP_NET_CFG_STATS_TX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x68)
+#define NFP_NET_CFG_STATS_TX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x70)
+#define NFP_NET_CFG_STATS_TX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x78)
+#define NFP_NET_CFG_STATS_TX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x80)
+#define NFP_NET_CFG_STATS_TX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x88)
+
+/**
+ * Per ring stats (0x1000 - 0x1800)
+ * options, 64bit per entry
+ * @NFP_NET_CFG_TXR_STATS: TX ring statistics (Packet and Byte count)
+ * @NFP_NET_CFG_RXR_STATS: RX ring statistics (Packet and Byte count)
+ */
+#define NFP_NET_CFG_TXR_STATS_BASE 0x1000
+#define NFP_NET_CFG_TXR_STATS(_x) (NFP_NET_CFG_TXR_STATS_BASE + \
+ ((_x) * 0x10))
+#define NFP_NET_CFG_RXR_STATS_BASE 0x1400
+#define NFP_NET_CFG_RXR_STATS(_x) (NFP_NET_CFG_RXR_STATS_BASE + \
+ ((_x) * 0x10))
+
+#endif /* _NFP_NET_CTRL_H_ */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
new file mode 100644
index 000000000000..4c97c713121c
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+
+#include "nfp_net.h"
+
+static struct dentry *nfp_dir;
+
+static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data)
+{
+ struct nfp_net_rx_ring *rx_ring = file->private;
+ int fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p, rxd_cnt;
+ struct nfp_net_rx_desc *rxd;
+ struct sk_buff *skb;
+ struct nfp_net *nn;
+ int i;
+
+ rtnl_lock();
+
+ if (!rx_ring->r_vec || !rx_ring->r_vec->nfp_net)
+ goto out;
+ nn = rx_ring->r_vec->nfp_net;
+ if (!netif_running(nn->netdev))
+ goto out;
+
+ rxd_cnt = rx_ring->cnt;
+
+ fl_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_fl);
+ fl_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_fl);
+ rx_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_rx);
+ rx_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx);
+
+ seq_printf(file, "RX[%02d]: H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d RX_RD=%d RX_WR=%d\n",
+ rx_ring->idx, rx_ring->rd_p, rx_ring->wr_p,
+ fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p);
+
+ for (i = 0; i < rxd_cnt; i++) {
+ rxd = &rx_ring->rxds[i];
+ seq_printf(file, "%04d: 0x%08x 0x%08x", i,
+ rxd->vals[0], rxd->vals[1]);
+
+ skb = READ_ONCE(rx_ring->rxbufs[i].skb);
+ if (skb)
+ seq_printf(file, " skb->head=%p skb->data=%p",
+ skb->head, skb->data);
+
+ if (rx_ring->rxbufs[i].dma_addr)
+ seq_printf(file, " dma_addr=%pad",
+ &rx_ring->rxbufs[i].dma_addr);
+
+ if (i == rx_ring->rd_p % rxd_cnt)
+ seq_puts(file, " H_RD ");
+ if (i == rx_ring->wr_p % rxd_cnt)
+ seq_puts(file, " H_WR ");
+ if (i == fl_rd_p % rxd_cnt)
+ seq_puts(file, " FL_RD");
+ if (i == fl_wr_p % rxd_cnt)
+ seq_puts(file, " FL_WR");
+ if (i == rx_rd_p % rxd_cnt)
+ seq_puts(file, " RX_RD");
+ if (i == rx_wr_p % rxd_cnt)
+ seq_puts(file, " RX_WR");
+
+ seq_putc(file, '\n');
+ }
+out:
+ rtnl_unlock();
+ return 0;
+}
+
+static int nfp_net_debugfs_rx_q_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, nfp_net_debugfs_rx_q_read, inode->i_private);
+}
+
+static const struct file_operations nfp_rx_q_fops = {
+ .owner = THIS_MODULE,
+ .open = nfp_net_debugfs_rx_q_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
+{
+ struct nfp_net_tx_ring *tx_ring = file->private;
+ struct nfp_net_tx_desc *txd;
+ int d_rd_p, d_wr_p, txd_cnt;
+ struct sk_buff *skb;
+ struct nfp_net *nn;
+ int i;
+
+ rtnl_lock();
+
+ if (!tx_ring->r_vec || !tx_ring->r_vec->nfp_net)
+ goto out;
+ nn = tx_ring->r_vec->nfp_net;
+ if (!netif_running(nn->netdev))
+ goto out;
+
+ txd_cnt = tx_ring->cnt;
+
+ d_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q);
+ d_wr_p = nfp_qcp_wr_ptr_read(tx_ring->qcp_q);
+
+ seq_printf(file, "TX[%02d]: H_RD=%d H_WR=%d D_RD=%d D_WR=%d\n",
+ tx_ring->idx, tx_ring->rd_p, tx_ring->wr_p, d_rd_p, d_wr_p);
+
+ for (i = 0; i < txd_cnt; i++) {
+ txd = &tx_ring->txds[i];
+ seq_printf(file, "%04d: 0x%08x 0x%08x 0x%08x 0x%08x", i,
+ txd->vals[0], txd->vals[1],
+ txd->vals[2], txd->vals[3]);
+
+ skb = READ_ONCE(tx_ring->txbufs[i].skb);
+ if (skb)
+ seq_printf(file, " skb->head=%p skb->data=%p",
+ skb->head, skb->data);
+ if (tx_ring->txbufs[i].dma_addr)
+ seq_printf(file, " dma_addr=%pad",
+ &tx_ring->txbufs[i].dma_addr);
+
+ if (i == tx_ring->rd_p % txd_cnt)
+ seq_puts(file, " H_RD");
+ if (i == tx_ring->wr_p % txd_cnt)
+ seq_puts(file, " H_WR");
+ if (i == d_rd_p % txd_cnt)
+ seq_puts(file, " D_RD");
+ if (i == d_wr_p % txd_cnt)
+ seq_puts(file, " D_WR");
+
+ seq_putc(file, '\n');
+ }
+out:
+ rtnl_unlock();
+ return 0;
+}
+
+static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, nfp_net_debugfs_tx_q_read, inode->i_private);
+}
+
+static const struct file_operations nfp_tx_q_fops = {
+ .owner = THIS_MODULE,
+ .open = nfp_net_debugfs_tx_q_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+void nfp_net_debugfs_adapter_add(struct nfp_net *nn)
+{
+ static struct dentry *queues, *tx, *rx;
+ char int_name[16];
+ int i;
+
+ if (IS_ERR_OR_NULL(nfp_dir))
+ return;
+
+ nn->debugfs_dir = debugfs_create_dir(pci_name(nn->pdev), nfp_dir);
+ if (IS_ERR_OR_NULL(nn->debugfs_dir))
+ return;
+
+ /* Create queue debugging sub-tree */
+ queues = debugfs_create_dir("queue", nn->debugfs_dir);
+ if (IS_ERR_OR_NULL(nn->debugfs_dir))
+ return;
+
+ rx = debugfs_create_dir("rx", queues);
+ tx = debugfs_create_dir("tx", queues);
+ if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx))
+ return;
+
+ for (i = 0; i < nn->num_rx_rings; i++) {
+ sprintf(int_name, "%d", i);
+ debugfs_create_file(int_name, S_IRUSR, rx,
+ &nn->rx_rings[i], &nfp_rx_q_fops);
+ }
+
+ for (i = 0; i < nn->num_tx_rings; i++) {
+ sprintf(int_name, "%d", i);
+ debugfs_create_file(int_name, S_IRUSR, tx,
+ &nn->tx_rings[i], &nfp_tx_q_fops);
+ }
+}
+
+void nfp_net_debugfs_adapter_del(struct nfp_net *nn)
+{
+ debugfs_remove_recursive(nn->debugfs_dir);
+ nn->debugfs_dir = NULL;
+}
+
+void nfp_net_debugfs_create(void)
+{
+ nfp_dir = debugfs_create_dir("nfp_net", NULL);
+}
+
+void nfp_net_debugfs_destroy(void)
+{
+ debugfs_remove_recursive(nfp_dir);
+ nfp_dir = NULL;
+}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
new file mode 100644
index 000000000000..9a4084a68db5
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * nfp_net_ethtool.c
+ * Netronome network device driver: ethtool support
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ * Brad Petrus <brad.petrus@netronome.com>
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/ethtool.h>
+
+#include "nfp_net_ctrl.h"
+#include "nfp_net.h"
+
+/* Support for stats. Returns netdev, driver, and device stats */
+enum { NETDEV_ET_STATS, NFP_NET_DRV_ET_STATS, NFP_NET_DEV_ET_STATS };
+struct _nfp_net_et_stats {
+ char name[ETH_GSTRING_LEN];
+ int type;
+ int sz;
+ int off;
+};
+
+#define NN_ET_NETDEV_STAT(m) NETDEV_ET_STATS, \
+ FIELD_SIZEOF(struct net_device_stats, m), \
+ offsetof(struct net_device_stats, m)
+/* For stats in the control BAR (other than Q stats) */
+#define NN_ET_DEV_STAT(m) NFP_NET_DEV_ET_STATS, \
+ sizeof(u64), \
+ (m)
+static const struct _nfp_net_et_stats nfp_net_et_stats[] = {
+ /* netdev stats */
+ {"rx_packets", NN_ET_NETDEV_STAT(rx_packets)},
+ {"tx_packets", NN_ET_NETDEV_STAT(tx_packets)},
+ {"rx_bytes", NN_ET_NETDEV_STAT(rx_bytes)},
+ {"tx_bytes", NN_ET_NETDEV_STAT(tx_bytes)},
+ {"rx_errors", NN_ET_NETDEV_STAT(rx_errors)},
+ {"tx_errors", NN_ET_NETDEV_STAT(tx_errors)},
+ {"rx_dropped", NN_ET_NETDEV_STAT(rx_dropped)},
+ {"tx_dropped", NN_ET_NETDEV_STAT(tx_dropped)},
+ {"multicast", NN_ET_NETDEV_STAT(multicast)},
+ {"collisions", NN_ET_NETDEV_STAT(collisions)},
+ {"rx_over_errors", NN_ET_NETDEV_STAT(rx_over_errors)},
+ {"rx_crc_errors", NN_ET_NETDEV_STAT(rx_crc_errors)},
+ {"rx_frame_errors", NN_ET_NETDEV_STAT(rx_frame_errors)},
+ {"rx_fifo_errors", NN_ET_NETDEV_STAT(rx_fifo_errors)},
+ {"rx_missed_errors", NN_ET_NETDEV_STAT(rx_missed_errors)},
+ {"tx_aborted_errors", NN_ET_NETDEV_STAT(tx_aborted_errors)},
+ {"tx_carrier_errors", NN_ET_NETDEV_STAT(tx_carrier_errors)},
+ {"tx_fifo_errors", NN_ET_NETDEV_STAT(tx_fifo_errors)},
+ /* Stats from the device */
+ {"dev_rx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_DISCARDS)},
+ {"dev_rx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_ERRORS)},
+ {"dev_rx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_OCTETS)},
+ {"dev_rx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_UC_OCTETS)},
+ {"dev_rx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_OCTETS)},
+ {"dev_rx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_OCTETS)},
+ {"dev_rx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_FRAMES)},
+ {"dev_rx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_FRAMES)},
+ {"dev_rx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_FRAMES)},
+
+ {"dev_tx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_DISCARDS)},
+ {"dev_tx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_ERRORS)},
+ {"dev_tx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_OCTETS)},
+ {"dev_tx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_UC_OCTETS)},
+ {"dev_tx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_OCTETS)},
+ {"dev_tx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_OCTETS)},
+ {"dev_tx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_FRAMES)},
+ {"dev_tx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_FRAMES)},
+ {"dev_tx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_FRAMES)},
+};
+
+#define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats)
+#define NN_ET_RVEC_STATS_LEN (nn->num_r_vecs * 3)
+#define NN_ET_RVEC_GATHER_STATS 7
+#define NN_ET_QUEUE_STATS_LEN ((nn->num_tx_rings + nn->num_rx_rings) * 2)
+#define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \
+ NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN)
+
+static void nfp_net_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ strlcpy(drvinfo->driver, nfp_net_driver_name, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, nfp_net_driver_version,
+ sizeof(drvinfo->version));
+
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+ "%d.%d.%d.%d",
+ nn->fw_ver.resv, nn->fw_ver.class,
+ nn->fw_ver.major, nn->fw_ver.minor);
+ strlcpy(drvinfo->bus_info, pci_name(nn->pdev),
+ sizeof(drvinfo->bus_info));
+
+ drvinfo->n_stats = NN_ET_STATS_LEN;
+ drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ;
+}
+
+static void nfp_net_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ ring->rx_max_pending = NFP_NET_MAX_RX_DESCS;
+ ring->tx_max_pending = NFP_NET_MAX_TX_DESCS;
+ ring->rx_pending = nn->rxd_cnt;
+ ring->tx_pending = nn->txd_cnt;
+}
+
+static int nfp_net_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 rxd_cnt, txd_cnt;
+
+ if (netif_running(netdev)) {
+ /* Some NIC drivers allow reconfiguration on the fly,
+ * some down the interface, change and then up it
+ * again. For now we don't allow changes when the
+ * device is up.
+ */
+ nn_warn(nn, "Can't change rings while device is up\n");
+ return -EBUSY;
+ }
+
+ /* We don't have separate queues/rings for small/large frames. */
+ if (ring->rx_mini_pending || ring->rx_jumbo_pending)
+ return -EINVAL;
+
+ /* Round up to supported values */
+ rxd_cnt = roundup_pow_of_two(ring->rx_pending);
+ rxd_cnt = max_t(u32, rxd_cnt, NFP_NET_MIN_RX_DESCS);
+ rxd_cnt = min_t(u32, rxd_cnt, NFP_NET_MAX_RX_DESCS);
+
+ txd_cnt = roundup_pow_of_two(ring->tx_pending);
+ txd_cnt = max_t(u32, txd_cnt, NFP_NET_MIN_TX_DESCS);
+ txd_cnt = min_t(u32, txd_cnt, NFP_NET_MAX_TX_DESCS);
+
+ if (nn->rxd_cnt != rxd_cnt || nn->txd_cnt != txd_cnt)
+ nn_dbg(nn, "Change ring size: RxQ %u->%u, TxQ %u->%u\n",
+ nn->rxd_cnt, rxd_cnt, nn->txd_cnt, txd_cnt);
+
+ nn->rxd_cnt = rxd_cnt;
+ nn->txd_cnt = txd_cnt;
+
+ return 0;
+}
+
+static void nfp_net_get_strings(struct net_device *netdev,
+ u32 stringset, u8 *data)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u8 *p = data;
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) {
+ memcpy(p, nfp_net_et_stats[i].name, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < nn->num_r_vecs; i++) {
+ sprintf(p, "rvec_%u_rx_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rvec_%u_tx_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rvec_%u_tx_busy", i);
+ p += ETH_GSTRING_LEN;
+ }
+ strncpy(p, "hw_rx_csum_ok", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_rx_csum_inner_ok", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_rx_csum_err", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_tx_csum", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_tx_inner_csum", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "tx_gather", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "tx_lso", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ for (i = 0; i < nn->num_tx_rings; i++) {
+ sprintf(p, "txq_%u_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "txq_%u_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < nn->num_rx_rings; i++) {
+ sprintf(p, "rxq_%u_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rxq_%u_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+ break;
+ }
+}
+
+static void nfp_net_get_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ u64 gathered_stats[NN_ET_RVEC_GATHER_STATS] = {};
+ struct nfp_net *nn = netdev_priv(netdev);
+ struct rtnl_link_stats64 *netdev_stats;
+ struct rtnl_link_stats64 temp = {};
+ u64 tmp[NN_ET_RVEC_GATHER_STATS];
+ u8 __iomem *io_p;
+ int i, j, k;
+ u8 *p;
+
+ netdev_stats = dev_get_stats(netdev, &temp);
+
+ for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) {
+ switch (nfp_net_et_stats[i].type) {
+ case NETDEV_ET_STATS:
+ p = (char *)netdev_stats + nfp_net_et_stats[i].off;
+ data[i] = nfp_net_et_stats[i].sz == sizeof(u64) ?
+ *(u64 *)p : *(u32 *)p;
+ break;
+
+ case NFP_NET_DEV_ET_STATS:
+ io_p = nn->ctrl_bar + nfp_net_et_stats[i].off;
+ data[i] = readq(io_p);
+ break;
+ }
+ }
+ for (j = 0; j < nn->num_r_vecs; j++) {
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin(&nn->r_vecs[j].rx_sync);
+ data[i++] = nn->r_vecs[j].rx_pkts;
+ tmp[0] = nn->r_vecs[j].hw_csum_rx_ok;
+ tmp[1] = nn->r_vecs[j].hw_csum_rx_inner_ok;
+ tmp[2] = nn->r_vecs[j].hw_csum_rx_error;
+ } while (u64_stats_fetch_retry(&nn->r_vecs[j].rx_sync, start));
+
+ do {
+ start = u64_stats_fetch_begin(&nn->r_vecs[j].tx_sync);
+ data[i++] = nn->r_vecs[j].tx_pkts;
+ data[i++] = nn->r_vecs[j].tx_busy;
+ tmp[3] = nn->r_vecs[j].hw_csum_tx;
+ tmp[4] = nn->r_vecs[j].hw_csum_tx_inner;
+ tmp[5] = nn->r_vecs[j].tx_gather;
+ tmp[6] = nn->r_vecs[j].tx_lso;
+ } while (u64_stats_fetch_retry(&nn->r_vecs[j].tx_sync, start));
+
+ for (k = 0; k < NN_ET_RVEC_GATHER_STATS; k++)
+ gathered_stats[k] += tmp[k];
+ }
+ for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++)
+ data[i++] = gathered_stats[j];
+ for (j = 0; j < nn->num_tx_rings; j++) {
+ io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j);
+ data[i++] = readq(io_p);
+ io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8;
+ data[i++] = readq(io_p);
+ }
+ for (j = 0; j < nn->num_rx_rings; j++) {
+ io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j);
+ data[i++] = readq(io_p);
+ io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8;
+ data[i++] = readq(io_p);
+ }
+}
+
+static int nfp_net_get_sset_count(struct net_device *netdev, int sset)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ return NN_ET_STATS_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/* RX network flow classification (RSS, filters, etc)
+ */
+static u32 ethtool_flow_to_nfp_flag(u32 flow_type)
+{
+ static const u32 xlate_ethtool_to_nfp[IPV6_FLOW + 1] = {
+ [TCP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_TCP,
+ [TCP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_TCP,
+ [UDP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_UDP,
+ [UDP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_UDP,
+ [IPV4_FLOW] = NFP_NET_CFG_RSS_IPV4,
+ [IPV6_FLOW] = NFP_NET_CFG_RSS_IPV6,
+ };
+
+ if (flow_type >= ARRAY_SIZE(xlate_ethtool_to_nfp))
+ return 0;
+
+ return xlate_ethtool_to_nfp[flow_type];
+}
+
+static int nfp_net_get_rss_hash_opts(struct nfp_net *nn,
+ struct ethtool_rxnfc *cmd)
+{
+ u32 nfp_rss_flag;
+
+ cmd->data = 0;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return -EOPNOTSUPP;
+
+ nfp_rss_flag = ethtool_flow_to_nfp_flag(cmd->flow_type);
+ if (!nfp_rss_flag)
+ return -EINVAL;
+
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST;
+ if (nn->rss_cfg & nfp_rss_flag)
+ cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+
+ return 0;
+}
+
+static int nfp_net_get_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXRINGS:
+ cmd->data = nn->num_rx_rings;
+ return 0;
+ case ETHTOOL_GRXFH:
+ return nfp_net_get_rss_hash_opts(nn, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int nfp_net_set_rss_hash_opt(struct nfp_net *nn,
+ struct ethtool_rxnfc *nfc)
+{
+ u32 new_rss_cfg = nn->rss_cfg;
+ u32 nfp_rss_flag;
+ int err;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return -EOPNOTSUPP;
+
+ /* RSS only supports IP SA/DA and L4 src/dst ports */
+ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3))
+ return -EINVAL;
+
+ /* We need at least the IP SA/DA fields for hashing */
+ if (!(nfc->data & RXH_IP_SRC) ||
+ !(nfc->data & RXH_IP_DST))
+ return -EINVAL;
+
+ nfp_rss_flag = ethtool_flow_to_nfp_flag(nfc->flow_type);
+ if (!nfp_rss_flag)
+ return -EINVAL;
+
+ switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+ case 0:
+ new_rss_cfg &= ~nfp_rss_flag;
+ break;
+ case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+ new_rss_cfg |= nfp_rss_flag;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ new_rss_cfg |= NFP_NET_CFG_RSS_TOEPLITZ;
+ new_rss_cfg |= NFP_NET_CFG_RSS_MASK;
+
+ if (new_rss_cfg == nn->rss_cfg)
+ return 0;
+
+ writel(new_rss_cfg, nn->ctrl_bar + NFP_NET_CFG_RSS_CTRL);
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS);
+ if (err)
+ return err;
+
+ nn->rss_cfg = new_rss_cfg;
+
+ nn_dbg(nn, "Changed RSS config to 0x%x\n", nn->rss_cfg);
+ return 0;
+}
+
+static int nfp_net_set_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ return nfp_net_set_rss_hash_opt(nn, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static u32 nfp_net_get_rxfh_indir_size(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return 0;
+
+ return ARRAY_SIZE(nn->rss_itbl);
+}
+
+static u32 nfp_net_get_rxfh_key_size(struct net_device *netdev)
+{
+ return NFP_NET_CFG_RSS_KEY_SZ;
+}
+
+static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int i;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return -EOPNOTSUPP;
+
+ if (indir)
+ for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++)
+ indir[i] = nn->rss_itbl[i];
+ if (key)
+ memcpy(key, nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ);
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ return 0;
+}
+
+static int nfp_net_set_rxfh(struct net_device *netdev,
+ const u32 *indir, const u8 *key,
+ const u8 hfunc)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int i;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS) ||
+ !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+
+ if (!key && !indir)
+ return 0;
+
+ if (key) {
+ memcpy(nn->rss_key, key, NFP_NET_CFG_RSS_KEY_SZ);
+ nfp_net_rss_write_key(nn);
+ }
+ if (indir) {
+ for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++)
+ nn->rss_itbl[i] = indir[i];
+
+ nfp_net_rss_write_itbl(nn);
+ }
+
+ return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS);
+}
+
+/* Dump BAR registers
+ */
+static int nfp_net_get_regs_len(struct net_device *netdev)
+{
+ return NFP_NET_CFG_BAR_SZ;
+}
+
+static void nfp_net_get_regs(struct net_device *netdev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 *regs_buf = p;
+ int i;
+
+ regs->version = nn_readl(nn, NFP_NET_CFG_VERSION);
+
+ for (i = 0; i < NFP_NET_CFG_BAR_SZ / sizeof(u32); i++)
+ regs_buf[i] = readl(nn->ctrl_bar + (i * sizeof(u32)));
+}
+
+static int nfp_net_get_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ec)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD))
+ return -EINVAL;
+
+ ec->rx_coalesce_usecs = nn->rx_coalesce_usecs;
+ ec->rx_max_coalesced_frames = nn->rx_coalesce_max_frames;
+ ec->tx_coalesce_usecs = nn->tx_coalesce_usecs;
+ ec->tx_max_coalesced_frames = nn->tx_coalesce_max_frames;
+
+ return 0;
+}
+
+static int nfp_net_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ec)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ unsigned int factor;
+
+ if (ec->rx_coalesce_usecs_irq ||
+ ec->rx_max_coalesced_frames_irq ||
+ ec->tx_coalesce_usecs_irq ||
+ ec->tx_max_coalesced_frames_irq ||
+ ec->stats_block_coalesce_usecs ||
+ ec->use_adaptive_rx_coalesce ||
+ ec->use_adaptive_tx_coalesce ||
+ ec->pkt_rate_low ||
+ ec->rx_coalesce_usecs_low ||
+ ec->rx_max_coalesced_frames_low ||
+ ec->tx_coalesce_usecs_low ||
+ ec->tx_max_coalesced_frames_low ||
+ ec->pkt_rate_high ||
+ ec->rx_coalesce_usecs_high ||
+ ec->rx_max_coalesced_frames_high ||
+ ec->tx_coalesce_usecs_high ||
+ ec->tx_max_coalesced_frames_high ||
+ ec->rate_sample_interval)
+ return -ENOTSUPP;
+
+ /* Compute factor used to convert coalesce '_usecs' parameters to
+ * ME timestamp ticks. There are 16 ME clock cycles for each timestamp
+ * count.
+ */
+ factor = nn->me_freq_mhz / 16;
+
+ /* Each pair of (usecs, max_frames) fields specifies that interrupts
+ * should be coalesced until
+ * (usecs > 0 && time_since_first_completion >= usecs) ||
+ * (max_frames > 0 && completed_frames >= max_frames)
+ *
+ * It is illegal to set both usecs and max_frames to zero as this would
+ * cause interrupts to never be generated. To disable coalescing, set
+ * usecs = 0 and max_frames = 1.
+ *
+ * Some implementations ignore the value of max_frames and use the
+ * condition time_since_first_completion >= usecs
+ */
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD))
+ return -EINVAL;
+
+ /* ensure valid configuration */
+ if (!ec->rx_coalesce_usecs && !ec->rx_max_coalesced_frames)
+ return -EINVAL;
+
+ if (!ec->tx_coalesce_usecs && !ec->tx_max_coalesced_frames)
+ return -EINVAL;
+
+ if (ec->rx_coalesce_usecs * factor >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ if (ec->tx_coalesce_usecs * factor >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ if (ec->rx_max_coalesced_frames >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ if (ec->tx_max_coalesced_frames >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ /* configuration is valid */
+ nn->rx_coalesce_usecs = ec->rx_coalesce_usecs;
+ nn->rx_coalesce_max_frames = ec->rx_max_coalesced_frames;
+ nn->tx_coalesce_usecs = ec->tx_coalesce_usecs;
+ nn->tx_coalesce_max_frames = ec->tx_max_coalesced_frames;
+
+ /* write configuration to device */
+ nfp_net_coalesce_write_cfg(nn);
+ return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_IRQMOD);
+}
+
+static const struct ethtool_ops nfp_net_ethtool_ops = {
+ .get_drvinfo = nfp_net_get_drvinfo,
+ .get_ringparam = nfp_net_get_ringparam,
+ .set_ringparam = nfp_net_set_ringparam,
+ .get_strings = nfp_net_get_strings,
+ .get_ethtool_stats = nfp_net_get_stats,
+ .get_sset_count = nfp_net_get_sset_count,
+ .get_rxnfc = nfp_net_get_rxnfc,
+ .set_rxnfc = nfp_net_set_rxnfc,
+ .get_rxfh_indir_size = nfp_net_get_rxfh_indir_size,
+ .get_rxfh_key_size = nfp_net_get_rxfh_key_size,
+ .get_rxfh = nfp_net_get_rxfh,
+ .set_rxfh = nfp_net_set_rxfh,
+ .get_regs_len = nfp_net_get_regs_len,
+ .get_regs = nfp_net_get_regs,
+ .get_coalesce = nfp_net_get_coalesce,
+ .set_coalesce = nfp_net_set_coalesce,
+};
+
+void nfp_net_set_ethtool_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &nfp_net_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
new file mode 100644
index 000000000000..e2b22b8a20f1
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * nfp_netvf_main.c
+ * Netronome virtual function network device driver: Main entry point
+ * Author: Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/etherdevice.h>
+
+#include "nfp_net_ctrl.h"
+#include "nfp_net.h"
+
+const char nfp_net_driver_name[] = "nfp_netvf";
+const char nfp_net_driver_version[] = "0.1";
+#define PCI_DEVICE_NFP6000VF 0x6003
+static const struct pci_device_id nfp_netvf_pci_device_ids[] = {
+ { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_NFP6000VF,
+ PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
+ PCI_ANY_ID, 0,
+ },
+ { 0, } /* Required last entry. */
+};
+MODULE_DEVICE_TABLE(pci, nfp_netvf_pci_device_ids);
+
+static void nfp_netvf_get_mac_addr(struct nfp_net *nn)
+{
+ u8 mac_addr[ETH_ALEN];
+
+ put_unaligned_be32(nn_readl(nn, NFP_NET_CFG_MACADDR + 0), &mac_addr[0]);
+ /* We can't do readw for NFP-3200 compatibility */
+ put_unaligned_be16(nn_readl(nn, NFP_NET_CFG_MACADDR + 4) >> 16,
+ &mac_addr[4]);
+
+ if (!is_valid_ether_addr(mac_addr)) {
+ eth_hw_addr_random(nn->netdev);
+ return;
+ }
+
+ ether_addr_copy(nn->netdev->dev_addr, mac_addr);
+ ether_addr_copy(nn->netdev->perm_addr, mac_addr);
+}
+
+static int nfp_netvf_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ struct nfp_net_fw_version fw_ver;
+ int max_tx_rings, max_rx_rings;
+ u32 tx_bar_off, rx_bar_off;
+ u32 tx_bar_sz, rx_bar_sz;
+ int tx_bar_no, rx_bar_no;
+ u8 __iomem *ctrl_bar;
+ struct nfp_net *nn;
+ int is_nfp3200;
+ u32 startq;
+ int stride;
+ int err;
+
+ err = pci_enable_device_mem(pdev);
+ if (err)
+ return err;
+
+ err = pci_request_regions(pdev, nfp_net_driver_name);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate device memory.\n");
+ goto err_pci_disable;
+ }
+
+ switch (pdev->device) {
+ case PCI_DEVICE_NFP6000VF:
+ is_nfp3200 = 0;
+ break;
+ default:
+ err = -ENODEV;
+ goto err_pci_regions;
+ }
+
+ pci_set_master(pdev);
+
+ err = dma_set_mask_and_coherent(&pdev->dev,
+ DMA_BIT_MASK(NFP_NET_MAX_DMA_BITS));
+ if (err)
+ goto err_pci_regions;
+
+ /* Map the Control BAR.
+ *
+ * Irrespective of the advertised BAR size we only map the
+ * first NFP_NET_CFG_BAR_SZ of the BAR. This keeps the code
+ * the identical for PF and VF drivers.
+ */
+ ctrl_bar = ioremap_nocache(pci_resource_start(pdev, NFP_NET_CRTL_BAR),
+ NFP_NET_CFG_BAR_SZ);
+ if (!ctrl_bar) {
+ dev_err(&pdev->dev,
+ "Failed to map resource %d\n", NFP_NET_CRTL_BAR);
+ err = -EIO;
+ goto err_pci_regions;
+ }
+
+ nfp_net_get_fw_version(&fw_ver, ctrl_bar);
+ if (fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) {
+ dev_err(&pdev->dev, "Unknown Firmware ABI %d.%d.%d.%d\n",
+ fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor);
+ err = -EINVAL;
+ goto err_ctrl_unmap;
+ }
+
+ /* Determine stride */
+ if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 0) ||
+ nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1) ||
+ nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0x12, 0x48)) {
+ stride = 2;
+ tx_bar_no = NFP_NET_Q0_BAR;
+ rx_bar_no = NFP_NET_Q1_BAR;
+ dev_warn(&pdev->dev, "OBSOLETE Firmware detected - VF isolation not available\n");
+ } else {
+ switch (fw_ver.major) {
+ case 1 ... 3:
+ if (is_nfp3200) {
+ stride = 2;
+ tx_bar_no = NFP_NET_Q0_BAR;
+ rx_bar_no = NFP_NET_Q1_BAR;
+ } else {
+ stride = 4;
+ tx_bar_no = NFP_NET_Q0_BAR;
+ rx_bar_no = tx_bar_no;
+ }
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported Firmware ABI %d.%d.%d.%d\n",
+ fw_ver.resv, fw_ver.class,
+ fw_ver.major, fw_ver.minor);
+ err = -EINVAL;
+ goto err_ctrl_unmap;
+ }
+ }
+
+ /* Find out how many rings are supported */
+ max_tx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_TXRINGS);
+ max_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS);
+
+ tx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_tx_rings * stride;
+ rx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_rx_rings * stride;
+
+ /* Sanity checks */
+ if (tx_bar_sz > pci_resource_len(pdev, tx_bar_no)) {
+ dev_err(&pdev->dev,
+ "TX BAR too small for number of TX rings. Adjusting\n");
+ tx_bar_sz = pci_resource_len(pdev, tx_bar_no);
+ max_tx_rings = (tx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2;
+ }
+ if (rx_bar_sz > pci_resource_len(pdev, rx_bar_no)) {
+ dev_err(&pdev->dev,
+ "RX BAR too small for number of RX rings. Adjusting\n");
+ rx_bar_sz = pci_resource_len(pdev, rx_bar_no);
+ max_rx_rings = (rx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2;
+ }
+
+ /* XXX Implement a workaround for THB-350 here. Ideally, we
+ * have a different PCI ID for A rev VFs.
+ */
+ switch (pdev->device) {
+ case PCI_DEVICE_NFP6000VF:
+ startq = readl(ctrl_bar + NFP_NET_CFG_START_TXQ);
+ tx_bar_off = NFP_PCIE_QUEUE(startq);
+ startq = readl(ctrl_bar + NFP_NET_CFG_START_RXQ);
+ rx_bar_off = NFP_PCIE_QUEUE(startq);
+ break;
+ default:
+ err = -ENODEV;
+ goto err_ctrl_unmap;
+ }
+
+ /* Allocate and initialise the netdev */
+ nn = nfp_net_netdev_alloc(pdev, max_tx_rings, max_rx_rings);
+ if (IS_ERR(nn)) {
+ err = PTR_ERR(nn);
+ goto err_ctrl_unmap;
+ }
+
+ nn->fw_ver = fw_ver;
+ nn->ctrl_bar = ctrl_bar;
+ nn->is_vf = 1;
+ nn->is_nfp3200 = is_nfp3200;
+ nn->stride_tx = stride;
+ nn->stride_rx = stride;
+
+ if (rx_bar_no == tx_bar_no) {
+ u32 bar_off, bar_sz;
+ resource_size_t map_addr;
+
+ /* Make a single overlapping BAR mapping */
+ if (tx_bar_off < rx_bar_off)
+ bar_off = tx_bar_off;
+ else
+ bar_off = rx_bar_off;
+
+ if ((tx_bar_off + tx_bar_sz) > (rx_bar_off + rx_bar_sz))
+ bar_sz = (tx_bar_off + tx_bar_sz) - bar_off;
+ else
+ bar_sz = (rx_bar_off + rx_bar_sz) - bar_off;
+
+ map_addr = pci_resource_start(pdev, tx_bar_no) + bar_off;
+ nn->q_bar = ioremap_nocache(map_addr, bar_sz);
+ if (!nn->q_bar) {
+ nn_err(nn, "Failed to map resource %d\n", tx_bar_no);
+ err = -EIO;
+ goto err_netdev_free;
+ }
+
+ /* TX queues */
+ nn->tx_bar = nn->q_bar + (tx_bar_off - bar_off);
+ /* RX queues */
+ nn->rx_bar = nn->q_bar + (rx_bar_off - bar_off);
+ } else {
+ resource_size_t map_addr;
+
+ /* TX queues */
+ map_addr = pci_resource_start(pdev, tx_bar_no) + tx_bar_off;
+ nn->tx_bar = ioremap_nocache(map_addr, tx_bar_sz);
+ if (!nn->tx_bar) {
+ nn_err(nn, "Failed to map resource %d\n", tx_bar_no);
+ err = -EIO;
+ goto err_netdev_free;
+ }
+
+ /* RX queues */
+ map_addr = pci_resource_start(pdev, rx_bar_no) + rx_bar_off;
+ nn->rx_bar = ioremap_nocache(map_addr, rx_bar_sz);
+ if (!nn->rx_bar) {
+ nn_err(nn, "Failed to map resource %d\n", rx_bar_no);
+ err = -EIO;
+ goto err_unmap_tx;
+ }
+ }
+
+ nfp_netvf_get_mac_addr(nn);
+
+ err = nfp_net_irqs_alloc(nn);
+ if (!err) {
+ nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n");
+ err = -EIO;
+ goto err_unmap_rx;
+ }
+
+ /* Get ME clock frequency from ctrl BAR
+ * XXX for now frequency is hardcoded until we figure out how
+ * to get the value from nfp-hwinfo into ctrl bar
+ */
+ nn->me_freq_mhz = 1200;
+
+ err = nfp_net_netdev_init(nn->netdev);
+ if (err)
+ goto err_irqs_disable;
+
+ pci_set_drvdata(pdev, nn);
+
+ nfp_net_info(nn);
+ nfp_net_debugfs_adapter_add(nn);
+
+ return 0;
+
+err_irqs_disable:
+ nfp_net_irqs_disable(nn);
+err_unmap_rx:
+ if (!nn->q_bar)
+ iounmap(nn->rx_bar);
+err_unmap_tx:
+ if (!nn->q_bar)
+ iounmap(nn->tx_bar);
+ else
+ iounmap(nn->q_bar);
+err_netdev_free:
+ pci_set_drvdata(pdev, NULL);
+ nfp_net_netdev_free(nn);
+err_ctrl_unmap:
+ iounmap(ctrl_bar);
+err_pci_regions:
+ pci_release_regions(pdev);
+err_pci_disable:
+ pci_disable_device(pdev);
+ return err;
+}
+
+static void nfp_netvf_pci_remove(struct pci_dev *pdev)
+{
+ struct nfp_net *nn = pci_get_drvdata(pdev);
+
+ /* Note, the order is slightly different from above as we need
+ * to keep the nn pointer around till we have freed everything.
+ */
+ nfp_net_debugfs_adapter_del(nn);
+
+ nfp_net_netdev_clean(nn->netdev);
+
+ nfp_net_irqs_disable(nn);
+
+ if (!nn->q_bar) {
+ iounmap(nn->rx_bar);
+ iounmap(nn->tx_bar);
+ } else {
+ iounmap(nn->q_bar);
+ }
+ iounmap(nn->ctrl_bar);
+
+ pci_set_drvdata(pdev, NULL);
+
+ nfp_net_netdev_free(nn);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver nfp_netvf_pci_driver = {
+ .name = nfp_net_driver_name,
+ .id_table = nfp_netvf_pci_device_ids,
+ .probe = nfp_netvf_pci_probe,
+ .remove = nfp_netvf_pci_remove,
+};
+
+static int __init nfp_netvf_init(void)
+{
+ int err;
+
+ pr_info("%s: NFP VF Network driver, Copyright (C) 2014-2015 Netronome Systems\n",
+ nfp_net_driver_name);
+
+ nfp_net_debugfs_create();
+ err = pci_register_driver(&nfp_netvf_pci_driver);
+ if (err) {
+ nfp_net_debugfs_destroy();
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit nfp_netvf_exit(void)
+{
+ pci_unregister_driver(&nfp_netvf_pci_driver);
+ nfp_net_debugfs_destroy();
+}
+
+module_init(nfp_netvf_init);
+module_exit(nfp_netvf_exit);
+
+MODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFP VF network device driver");
diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c
index 057665180f13..b1ce7aaa8f8b 100644
--- a/drivers/net/ethernet/nxp/lpc_eth.c
+++ b/drivers/net/ethernet/nxp/lpc_eth.c
@@ -797,7 +797,7 @@ static int lpc_mii_probe(struct net_device *ndev)
netdev_info(ndev, "using MII interface\n");
else
netdev_info(ndev, "using RMII interface\n");
- phydev = phy_connect(ndev, dev_name(&phydev->dev),
+ phydev = phy_connect(ndev, phydev_name(phydev),
&lpc_handle_link_change,
lpc_phy_interface_mode(&pldat->pdev->dev));
@@ -816,15 +816,14 @@ static int lpc_mii_probe(struct net_device *ndev)
pldat->duplex = -1;
pldat->phy_dev = phydev;
- netdev_info(ndev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
+
return 0;
}
static int lpc_mii_init(struct netdata_local *pldat)
{
- int err = -ENXIO, i;
+ int err = -ENXIO;
pldat->mii_bus = mdiobus_alloc();
if (!pldat->mii_bus) {
@@ -851,19 +850,10 @@ static int lpc_mii_init(struct netdata_local *pldat)
pldat->mii_bus->priv = pldat;
pldat->mii_bus->parent = &pldat->pdev->dev;
- pldat->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!pldat->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_1;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- pldat->mii_bus->irq[i] = PHY_POLL;
-
platform_set_drvdata(pldat->pdev, pldat->mii_bus);
if (mdiobus_register(pldat->mii_bus))
- goto err_out_free_mdio_irq;
+ goto err_out_unregister_bus;
if (lpc_mii_probe(pldat->ndev) != 0)
goto err_out_unregister_bus;
@@ -872,9 +862,6 @@ static int lpc_mii_init(struct netdata_local *pldat)
err_out_unregister_bus:
mdiobus_unregister(pldat->mii_bus);
-err_out_free_mdio_irq:
- kfree(pldat->mii_bus->irq);
-err_out_1:
mdiobus_free(pldat->mii_bus);
err_out:
return err;
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
index 08d4be616064..e097e6baaac4 100644
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
@@ -500,7 +500,7 @@ void pch_gbe_check_options(struct pch_gbe_adapter *adapter)
val = XsumTX;
pch_gbe_validate_option(&val, &opt, adapter);
if (!val)
- dev->features &= ~NETIF_F_ALL_CSUM;
+ dev->features &= ~NETIF_F_CSUM_MASK;
}
{ /* Flow Control */
static const struct pch_gbe_option opt = {
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index b2f8e854dfd1..264e954675d1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -3993,6 +3993,8 @@ struct public_drv_mb {
#define DRV_MSG_CODE_PHY_CORE_WRITE 0x000e0000
#define DRV_MSG_CODE_SET_VERSION 0x000f0000
+#define DRV_MSG_CODE_SET_LED_MODE 0x00200000
+
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
u32 drv_mb_param;
@@ -4044,6 +4046,10 @@ struct public_drv_mb {
#define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_SHIFT 8
#define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_MASK 0x0000FF00
+#define DRV_MB_PARAM_SET_LED_MODE_OPER 0x0
+#define DRV_MB_PARAM_SET_LED_MODE_ON 0x1
+#define DRV_MB_PARAM_SET_LED_MODE_OFF 0x2
+
u32 fw_mb_header;
#define FW_MSG_CODE_MASK 0xffff0000
#define FW_MSG_CODE_DRV_LOAD_ENGINE 0x10100000
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 174f7341c5c3..9d76ce249277 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -1115,6 +1115,23 @@ static int qed_drain(struct qed_dev *cdev)
return 0;
}
+static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode)
+{
+ struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *ptt;
+ int status = 0;
+
+ ptt = qed_ptt_acquire(hwfn);
+ if (!ptt)
+ return -EAGAIN;
+
+ status = qed_mcp_set_led(hwfn, ptt, mode);
+
+ qed_ptt_release(hwfn, ptt);
+
+ return status;
+}
+
const struct qed_common_ops qed_common_ops_pass = {
.probe = &qed_probe,
.remove = &qed_remove,
@@ -1135,6 +1152,7 @@ const struct qed_common_ops qed_common_ops_pass = {
.update_msglvl = &qed_init_dp,
.chain_alloc = &qed_chain_alloc,
.chain_free = &qed_chain_free,
+ .set_led = &qed_set_led,
};
u32 qed_get_protocol_version(enum qed_protocol protocol)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 20d048cdcb88..ba1b1f1ef789 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -858,3 +858,30 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn,
return 0;
}
+
+int qed_mcp_set_led(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
+ enum qed_led_mode mode)
+{
+ u32 resp = 0, param = 0, drv_mb_param;
+ int rc;
+
+ switch (mode) {
+ case QED_LED_MODE_ON:
+ drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_ON;
+ break;
+ case QED_LED_MODE_OFF:
+ drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_OFF;
+ break;
+ case QED_LED_MODE_RESTORE:
+ drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_OPER;
+ break;
+ default:
+ DP_NOTICE(p_hwfn, "Invalid LED mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_SET_LED_MODE,
+ drv_mb_param, &resp, &param);
+
+ return rc;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index dbaae586b4a7..506197d5c3dd 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -224,6 +224,19 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_mcp_drv_version *p_ver);
+/**
+ * @brief Set LED status
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param mode - LED mode
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_set_led(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_led_mode mode);
+
/* Using hwfn number (and not pf_num) is required since in CMT mode,
* same pf_num may be used by two different hwfn
* TODO - this shouldn't really be in .h file, but until all fields
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
index ea00d5f3bab4..7c6caf7f6612 100644
--- a/drivers/net/ethernet/qlogic/qede/qede.h
+++ b/drivers/net/ethernet/qlogic/qede/qede.h
@@ -116,6 +116,7 @@ struct qede_dev {
(edev)->dev_info.num_tc)
struct qede_fastpath *fp_array;
+ u16 req_rss;
u16 num_rss;
u8 num_tc;
#define QEDE_RSS_CNT(edev) ((edev)->num_rss)
@@ -269,13 +270,13 @@ int qede_change_mtu(struct net_device *dev, int new_mtu);
void qede_fill_by_demand_stats(struct qede_dev *edev);
#define RX_RING_SIZE_POW 13
-#define RX_RING_SIZE BIT(RX_RING_SIZE_POW)
+#define RX_RING_SIZE ((u16)BIT(RX_RING_SIZE_POW))
#define NUM_RX_BDS_MAX (RX_RING_SIZE - 1)
#define NUM_RX_BDS_MIN 128
#define NUM_RX_BDS_DEF NUM_RX_BDS_MAX
#define TX_RING_SIZE_POW 13
-#define TX_RING_SIZE BIT(TX_RING_SIZE_POW)
+#define TX_RING_SIZE ((u16)BIT(TX_RING_SIZE_POW))
#define NUM_TX_BDS_MAX (TX_RING_SIZE - 1)
#define NUM_TX_BDS_MIN 128
#define NUM_TX_BDS_DEF NUM_TX_BDS_MAX
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index 3a362476a22c..e442b85c9a5e 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -322,6 +322,30 @@ static void qede_set_msglevel(struct net_device *ndev, u32 level)
dp_module, dp_level);
}
+static int qede_nway_reset(struct net_device *dev)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ struct qed_link_output current_link;
+ struct qed_link_params link_params;
+
+ if (!netif_running(dev))
+ return 0;
+
+ memset(&current_link, 0, sizeof(current_link));
+ edev->ops->common->get_link(edev->cdev, &current_link);
+ if (!current_link.link_up)
+ return 0;
+
+ /* Toggle the link */
+ memset(&link_params, 0, sizeof(link_params));
+ link_params.link_up = false;
+ edev->ops->common->set_link(edev->cdev, &link_params);
+ link_params.link_up = true;
+ edev->ops->common->set_link(edev->cdev, &link_params);
+
+ return 0;
+}
+
static u32 qede_get_link(struct net_device *dev)
{
struct qede_dev *edev = netdev_priv(dev);
@@ -333,6 +357,106 @@ static u32 qede_get_link(struct net_device *dev)
return current_link.link_up;
}
+static void qede_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ ering->rx_max_pending = NUM_RX_BDS_MAX;
+ ering->rx_pending = edev->q_num_rx_buffers;
+ ering->tx_max_pending = NUM_TX_BDS_MAX;
+ ering->tx_pending = edev->q_num_tx_buffers;
+}
+
+static int qede_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "Set ring params command parameters: rx_pending = %d, tx_pending = %d\n",
+ ering->rx_pending, ering->tx_pending);
+
+ /* Validate legality of configuration */
+ if (ering->rx_pending > NUM_RX_BDS_MAX ||
+ ering->rx_pending < NUM_RX_BDS_MIN ||
+ ering->tx_pending > NUM_TX_BDS_MAX ||
+ ering->tx_pending < NUM_TX_BDS_MIN) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "Can only support Rx Buffer size [0%08x,...,0x%08x] and Tx Buffer size [0x%08x,...,0x%08x]\n",
+ NUM_RX_BDS_MIN, NUM_RX_BDS_MAX,
+ NUM_TX_BDS_MIN, NUM_TX_BDS_MAX);
+ return -EINVAL;
+ }
+
+ /* Change ring size and re-load */
+ edev->q_num_rx_buffers = ering->rx_pending;
+ edev->q_num_tx_buffers = ering->tx_pending;
+
+ if (netif_running(edev->ndev))
+ qede_reload(edev, NULL, NULL);
+
+ return 0;
+}
+
+static void qede_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ struct qed_link_output current_link;
+
+ memset(&current_link, 0, sizeof(current_link));
+ edev->ops->common->get_link(edev->cdev, &current_link);
+
+ if (current_link.pause_config & QED_LINK_PAUSE_AUTONEG_ENABLE)
+ epause->autoneg = true;
+ if (current_link.pause_config & QED_LINK_PAUSE_RX_ENABLE)
+ epause->rx_pause = true;
+ if (current_link.pause_config & QED_LINK_PAUSE_TX_ENABLE)
+ epause->tx_pause = true;
+
+ DP_VERBOSE(edev, QED_MSG_DEBUG,
+ "ethtool_pauseparam: cmd %d autoneg %d rx_pause %d tx_pause %d\n",
+ epause->cmd, epause->autoneg, epause->rx_pause,
+ epause->tx_pause);
+}
+
+static int qede_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ struct qed_link_params params;
+ struct qed_link_output current_link;
+
+ if (!edev->dev_info.common.is_mf) {
+ DP_INFO(edev,
+ "Pause parameters can not be updated in non-default mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ memset(&current_link, 0, sizeof(current_link));
+ edev->ops->common->get_link(edev->cdev, &current_link);
+
+ memset(&params, 0, sizeof(params));
+ params.override_flags |= QED_LINK_OVERRIDE_PAUSE_CONFIG;
+ if (epause->autoneg) {
+ if (!(current_link.supported_caps & SUPPORTED_Autoneg)) {
+ DP_INFO(edev, "autoneg not supported\n");
+ return -EINVAL;
+ }
+ params.pause_config |= QED_LINK_PAUSE_AUTONEG_ENABLE;
+ }
+ if (epause->rx_pause)
+ params.pause_config |= QED_LINK_PAUSE_RX_ENABLE;
+ if (epause->tx_pause)
+ params.pause_config |= QED_LINK_PAUSE_TX_ENABLE;
+
+ params.link_up = true;
+ edev->ops->common->set_link(edev->cdev, &params);
+
+ return 0;
+}
+
static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args)
{
edev->ndev->mtu = args->mtu;
@@ -366,17 +490,104 @@ int qede_change_mtu(struct net_device *ndev, int new_mtu)
return 0;
}
+static void qede_get_channels(struct net_device *dev,
+ struct ethtool_channels *channels)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ channels->max_combined = QEDE_MAX_RSS_CNT(edev);
+ channels->combined_count = QEDE_RSS_CNT(edev);
+}
+
+static int qede_set_channels(struct net_device *dev,
+ struct ethtool_channels *channels)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "set-channels command parameters: rx = %d, tx = %d, other = %d, combined = %d\n",
+ channels->rx_count, channels->tx_count,
+ channels->other_count, channels->combined_count);
+
+ /* We don't support separate rx / tx, nor `other' channels. */
+ if (channels->rx_count || channels->tx_count ||
+ channels->other_count || (channels->combined_count == 0) ||
+ (channels->combined_count > QEDE_MAX_RSS_CNT(edev))) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "command parameters not supported\n");
+ return -EINVAL;
+ }
+
+ /* Check if there was a change in the active parameters */
+ if (channels->combined_count == QEDE_RSS_CNT(edev)) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "No change in active parameters\n");
+ return 0;
+ }
+
+ /* We need the number of queues to be divisible between the hwfns */
+ if (channels->combined_count % edev->dev_info.common.num_hwfns) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "Number of channels must be divisable by %04x\n",
+ edev->dev_info.common.num_hwfns);
+ return -EINVAL;
+ }
+
+ /* Set number of queues and reload if necessary */
+ edev->req_rss = channels->combined_count;
+ if (netif_running(dev))
+ qede_reload(edev, NULL, NULL);
+
+ return 0;
+}
+
+static int qede_set_phys_id(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ u8 led_state = 0;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ return 1; /* cycle on/off once per second */
+
+ case ETHTOOL_ID_ON:
+ led_state = QED_LED_MODE_ON;
+ break;
+
+ case ETHTOOL_ID_OFF:
+ led_state = QED_LED_MODE_OFF;
+ break;
+
+ case ETHTOOL_ID_INACTIVE:
+ led_state = QED_LED_MODE_RESTORE;
+ break;
+ }
+
+ edev->ops->common->set_led(edev->cdev, led_state);
+
+ return 0;
+}
+
static const struct ethtool_ops qede_ethtool_ops = {
.get_settings = qede_get_settings,
.set_settings = qede_set_settings,
.get_drvinfo = qede_get_drvinfo,
.get_msglevel = qede_get_msglevel,
.set_msglevel = qede_set_msglevel,
+ .nway_reset = qede_nway_reset,
.get_link = qede_get_link,
+ .get_ringparam = qede_get_ringparam,
+ .set_ringparam = qede_set_ringparam,
+ .get_pauseparam = qede_get_pauseparam,
+ .set_pauseparam = qede_set_pauseparam,
.get_strings = qede_get_strings,
+ .set_phys_id = qede_set_phys_id,
.get_ethtool_stats = qede_get_ethtool_stats,
.get_sset_count = qede_get_sset_count,
+ .get_channels = qede_get_channels,
+ .set_channels = qede_set_channels,
};
void qede_set_ethtool_ops(struct net_device *dev)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index f4657a2e730a..6237f10b5119 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -1502,8 +1502,11 @@ static int qede_set_num_queues(struct qede_dev *edev)
u16 rss_num;
/* Setup queues according to possible resources*/
- rss_num = netif_get_num_default_rss_queues() *
- edev->dev_info.common.num_hwfns;
+ if (edev->req_rss)
+ rss_num = edev->req_rss;
+ else
+ rss_num = netif_get_num_default_rss_queues() *
+ edev->dev_info.common.num_hwfns;
rss_num = min_t(u16, QEDE_MAX_RSS_CNT(edev), rss_num);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
index a72bcddf160a..4b76c69fe86d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -167,7 +167,7 @@ struct qlcnic_dcb_cfg {
u32 version;
};
-static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
+static const struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
.init_dcbnl_ops = __qlcnic_init_dcbnl_ops,
.free = __qlcnic_dcb_free,
.attach = __qlcnic_dcb_attach,
@@ -180,7 +180,7 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
.aen_handler = qlcnic_83xx_dcb_aen_handler,
};
-static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
+static const struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
.init_dcbnl_ops = __qlcnic_init_dcbnl_ops,
.free = __qlcnic_dcb_free,
.attach = __qlcnic_dcb_attach,
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
index 3cf4a10fbe1e..9777e5713525 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
@@ -37,7 +37,7 @@ struct qlcnic_dcb {
struct qlcnic_adapter *adapter;
struct delayed_work aen_work;
struct workqueue_struct *wq;
- struct qlcnic_dcb_ops *ops;
+ const struct qlcnic_dcb_ops *ops;
struct qlcnic_dcb_cfg *cfg;
unsigned long state;
};
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index d4b5085a21fa..7bd6f25b4625 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -1604,7 +1604,7 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
- netif_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll,
+ netif_tx_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll,
NAPI_POLL_WEIGHT);
}
}
@@ -2135,7 +2135,7 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
- netif_napi_add(netdev, &tx_ring->napi,
+ netif_tx_napi_add(netdev, &tx_ring->napi,
qlcnic_83xx_msix_tx_poll,
NAPI_POLL_WEIGHT);
}
diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c
index 9a37247cf4b8..6b541e57c96a 100644
--- a/drivers/net/ethernet/rdc/r6040.c
+++ b/drivers/net/ethernet/rdc/r6040.c
@@ -1039,7 +1039,7 @@ static int r6040_mii_probe(struct net_device *dev)
return -ENODEV;
}
- phydev = phy_connect(dev, dev_name(&phydev->dev), &r6040_adjust_link,
+ phydev = phy_connect(dev, phydev_name(phydev), &r6040_adjust_link,
PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -1061,9 +1061,7 @@ static int r6040_mii_probe(struct net_device *dev)
lp->old_link = 0;
lp->old_duplex = -1;
- dev_info(&lp->pdev->dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
}
@@ -1077,7 +1075,6 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
static int card_idx = -1;
int bar = 0;
u16 *adrp;
- int i;
pr_info("%s\n", version);
@@ -1189,19 +1186,11 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
lp->mii_bus->name = "r6040_eth_mii";
snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
dev_name(&pdev->dev), card_idx);
- lp->mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!lp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_mdio;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- lp->mii_bus->irq[i] = PHY_POLL;
err = mdiobus_register(lp->mii_bus);
if (err) {
dev_err(&pdev->dev, "failed to register MII bus\n");
- goto err_out_mdio_irq;
+ goto err_out_mdio;
}
err = r6040_mii_probe(dev);
@@ -1220,8 +1209,6 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
err_out_mdio_unregister:
mdiobus_unregister(lp->mii_bus);
-err_out_mdio_irq:
- kfree(lp->mii_bus->irq);
err_out_mdio:
mdiobus_free(lp->mii_bus);
err_out_unmap:
@@ -1244,7 +1231,6 @@ static void r6040_remove_one(struct pci_dev *pdev)
unregister_netdev(dev);
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
netif_napi_del(&lp->napi);
pci_iounmap(pdev, lp->base);
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 79ef799f88ab..17d5571d0432 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -3894,7 +3894,7 @@ static void rtl8168h_1_hw_phy_config(struct rtl8169_private *tp)
/* disable phy pfm mode */
rtl_writephy(tp, 0x1f, 0x0a44);
- rtl_w0w1_phy(tp, 0x14, 0x0000, 0x0080);
+ rtl_w0w1_phy(tp, 0x11, 0x0000, 0x0080);
rtl_writephy(tp, 0x1f, 0x0000);
/* Check ALDPS bit, disable it if enabled */
@@ -3947,7 +3947,7 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp)
data = (ioffset_p3<<12)|(ioffset_p2<<8)|(ioffset_p1<<4)|(ioffset_p0);
if ((ioffset_p3 != 0x0f) || (ioffset_p2 != 0x0f) ||
- (ioffset_p1 != 0x0f) || (ioffset_p0 == 0x0f)) {
+ (ioffset_p1 != 0x0f) || (ioffset_p0 != 0x0f)) {
rtl_writephy(tp, 0x1f, 0x0bcf);
rtl_writephy(tp, 0x16, data);
rtl_writephy(tp, 0x1f, 0x0000);
@@ -3967,7 +3967,7 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp)
/* disable phy pfm mode */
rtl_writephy(tp, 0x1f, 0x0a44);
- rtl_w0w1_phy(tp, 0x14, 0x0000, 0x0080);
+ rtl_w0w1_phy(tp, 0x11, 0x0000, 0x0080);
rtl_writephy(tp, 0x1f, 0x0000);
/* Check ALDPS bit, disable it if enabled */
@@ -5818,11 +5818,10 @@ static void rtl_hw_start_8168d_4(struct rtl8169_private *tp)
void __iomem *ioaddr = tp->mmio_addr;
struct pci_dev *pdev = tp->pci_dev;
static const struct ephy_info e_info_8168d_4[] = {
- { 0x0b, ~0, 0x48 },
- { 0x19, 0x20, 0x50 },
- { 0x0c, ~0, 0x20 }
+ { 0x0b, 0x0000, 0x0048 },
+ { 0x19, 0x0020, 0x0050 },
+ { 0x0c, 0x0100, 0x0020 }
};
- int i;
rtl_csi_access_enable_1(tp);
@@ -5830,13 +5829,7 @@ static void rtl_hw_start_8168d_4(struct rtl8169_private *tp)
RTL_W8(MaxTxPacketSize, TxPacketMax);
- for (i = 0; i < ARRAY_SIZE(e_info_8168d_4); i++) {
- const struct ephy_info *e = e_info_8168d_4 + i;
- u16 w;
-
- w = rtl_ephy_read(tp, e->offset);
- rtl_ephy_write(tp, 0x03, (w & e->mask) | e->bits);
- }
+ rtl_ephy_init(tp, e_info_8168d_4, ARRAY_SIZE(e_info_8168d_4));
rtl_enable_clock_request(pdev);
}
@@ -6127,7 +6120,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~TX_10M_PS_EN);
@@ -6136,7 +6129,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
rtl_pcie_state_l2l3_enable(tp, false);
rtl_writephy(tp, 0x1f, 0x0c42);
- rg_saw_cnt = rtl_readphy(tp, 0x13);
+ rg_saw_cnt = (rtl_readphy(tp, 0x13) & 0x3fff);
rtl_writephy(tp, 0x1f, 0x0000);
if (rg_saw_cnt > 0) {
u16 sw_cnt_1ms_ini;
@@ -6252,7 +6245,7 @@ static void rtl_hw_start_8168ep_2(struct rtl8169_private *tp)
rtl_hw_start_8168ep(tp);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
}
static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
@@ -6274,7 +6267,7 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
rtl_hw_start_8168ep(tp);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
data = r8168_mac_ocp_read(tp, 0xd3e2);
data &= 0xf000;
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index 0623fff932e4..9fbe92ac225b 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -206,6 +206,7 @@ enum CCC_BIT {
CCC_OPC_RESET = 0x00000000,
CCC_OPC_CONFIG = 0x00000001,
CCC_OPC_OPERATION = 0x00000002,
+ CCC_GAC = 0x00000080,
CCC_DTSR = 0x00000100,
CCC_CSEL = 0x00030000,
CCC_CSEL_HPB = 0x00010000,
@@ -576,6 +577,9 @@ enum GTI_BIT {
GTI_TIV = 0x0FFFFFFF,
};
+#define GTI_TIV_MAX GTI_TIV
+#define GTI_TIV_MIN 0x20
+
/* GIC */
enum GIC_BIT {
GIC_PTCE = 0x00000001, /* Undocumented? */
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 467d41698fd5..ac43ed914fcf 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -32,6 +32,8 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <asm/div64.h>
+
#include "ravb.h"
#define RAVB_DEF_MSG_ENABLE \
@@ -113,12 +115,15 @@ static void ravb_read_mac_address(struct net_device *ndev, const u8 *mac)
if (mac) {
ether_addr_copy(ndev->dev_addr, mac);
} else {
- ndev->dev_addr[0] = (ravb_read(ndev, MAHR) >> 24);
- ndev->dev_addr[1] = (ravb_read(ndev, MAHR) >> 16) & 0xFF;
- ndev->dev_addr[2] = (ravb_read(ndev, MAHR) >> 8) & 0xFF;
- ndev->dev_addr[3] = (ravb_read(ndev, MAHR) >> 0) & 0xFF;
- ndev->dev_addr[4] = (ravb_read(ndev, MALR) >> 8) & 0xFF;
- ndev->dev_addr[5] = (ravb_read(ndev, MALR) >> 0) & 0xFF;
+ u32 mahr = ravb_read(ndev, MAHR);
+ u32 malr = ravb_read(ndev, MALR);
+
+ ndev->dev_addr[0] = (mahr >> 24) & 0xFF;
+ ndev->dev_addr[1] = (mahr >> 16) & 0xFF;
+ ndev->dev_addr[2] = (mahr >> 8) & 0xFF;
+ ndev->dev_addr[3] = (mahr >> 0) & 0xFF;
+ ndev->dev_addr[4] = (malr >> 8) & 0xFF;
+ ndev->dev_addr[5] = (malr >> 0) & 0xFF;
}
}
@@ -338,16 +343,13 @@ error:
static void ravb_emac_init(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
- u32 ecmr;
/* Receive frame limit set register */
ravb_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, RFLR);
/* PAUSE prohibition */
- ecmr = ravb_read(ndev, ECMR);
- ecmr &= ECMR_DM;
- ecmr |= ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) | ECMR_TE | ECMR_RE;
- ravb_write(ndev, ecmr, ECMR);
+ ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) |
+ ECMR_TE | ECMR_RE, ECMR);
ravb_set_rate(ndev);
@@ -405,9 +407,11 @@ static int ravb_dmac_init(struct net_device *ndev)
/* Timestamp enable */
ravb_write(ndev, TCCR_TFEN, TCCR);
- /* Interrupt enable: */
+ /* Interrupt init: */
/* Frame receive */
ravb_write(ndev, RIC0_FRE0 | RIC0_FRE1, RIC0);
+ /* Disable FIFO full warning */
+ ravb_write(ndev, 0, RIC1);
/* Receive FIFO full error, descriptor empty */
ravb_write(ndev, RIC2_QFE0 | RIC2_QFE1 | RIC2_RFFE, RIC2);
/* Frame transmitted, timestamp FIFO updated */
@@ -875,6 +879,7 @@ static int ravb_phy_init(struct net_device *ndev)
struct ravb_private *priv = netdev_priv(ndev);
struct phy_device *phydev;
struct device_node *pn;
+ int err;
priv->link = 0;
priv->speed = 0;
@@ -882,6 +887,17 @@ static int ravb_phy_init(struct net_device *ndev)
/* Try connecting to PHY */
pn = of_parse_phandle(np, "phy-handle", 0);
+ if (!pn) {
+ /* In the case of a fixed PHY, the DT node associated
+ * to the PHY is the Ethernet MAC DT node.
+ */
+ if (of_phy_is_fixed_link(np)) {
+ err = of_phy_register_fixed_link(np);
+ if (err)
+ return err;
+ }
+ pn = of_node_get(np);
+ }
phydev = of_phy_connect(ndev, pn, ravb_adjust_link, 0,
priv->phy_interface);
if (!phydev) {
@@ -908,8 +924,7 @@ static int ravb_phy_init(struct net_device *ndev)
/* 10BASE is not supported */
phydev->supported &= ~PHY_10BT_FEATURES;
- netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n",
- phydev->addr, phydev->irq, phydev->drv->name);
+ phy_attached_info(phydev);
priv->phydev = phydev;
@@ -1232,7 +1247,8 @@ static int ravb_open(struct net_device *ndev)
ravb_emac_init(ndev);
/* Initialise PTP Clock driver */
- ravb_ptp_init(ndev, priv->pdev);
+ if (priv->chip_id == RCAR_GEN2)
+ ravb_ptp_init(ndev, priv->pdev);
netif_tx_start_all_queues(ndev);
@@ -1245,7 +1261,8 @@ static int ravb_open(struct net_device *ndev)
out_ptp_stop:
/* Stop PTP Clock driver */
- ravb_ptp_stop(ndev);
+ if (priv->chip_id == RCAR_GEN2)
+ ravb_ptp_stop(ndev);
out_free_irq2:
if (priv->chip_id == RCAR_GEN3)
free_irq(priv->emac_irq, ndev);
@@ -1474,12 +1491,12 @@ static int ravb_close(struct net_device *ndev)
/* Disable interrupts by clearing the interrupt masks. */
ravb_write(ndev, 0, RIC0);
- ravb_write(ndev, 0, RIC1);
ravb_write(ndev, 0, RIC2);
ravb_write(ndev, 0, TIC);
/* Stop PTP Clock driver */
- ravb_ptp_stop(ndev);
+ if (priv->chip_id == RCAR_GEN2)
+ ravb_ptp_stop(ndev);
/* Set the config mode to stop the AVB-DMAC's processes */
if (ravb_stop_dma(ndev) < 0)
@@ -1659,11 +1676,45 @@ static int ravb_mdio_release(struct ravb_private *priv)
static const struct of_device_id ravb_match_table[] = {
{ .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 },
+ { .compatible = "renesas,etheravb-rcar-gen2", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 },
+ { .compatible = "renesas,etheravb-rcar-gen3", .data = (void *)RCAR_GEN3 },
{ }
};
MODULE_DEVICE_TABLE(of, ravb_match_table);
+static int ravb_set_gti(struct net_device *ndev)
+{
+
+ struct device *dev = ndev->dev.parent;
+ struct device_node *np = dev->of_node;
+ unsigned long rate;
+ struct clk *clk;
+ uint64_t inc;
+
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "could not get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ rate = clk_get_rate(clk);
+ clk_put(clk);
+
+ inc = 1000000000ULL << 20;
+ do_div(inc, rate);
+
+ if (inc < GTI_TIV_MIN || inc > GTI_TIV_MAX) {
+ dev_err(dev, "gti.tiv increment 0x%llx is outside the range 0x%x - 0x%x\n",
+ inc, GTI_TIV_MIN, GTI_TIV_MAX);
+ return -EINVAL;
+ }
+
+ ravb_write(ndev, inc, GTI);
+
+ return 0;
+}
+
static int ravb_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -1752,15 +1803,25 @@ static int ravb_probe(struct platform_device *pdev)
ndev->ethtool_ops = &ravb_ethtool_ops;
/* Set AVB config mode */
- ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) | CCC_OPC_CONFIG,
- CCC);
+ if (chip_id == RCAR_GEN2) {
+ ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) |
+ CCC_OPC_CONFIG, CCC);
+ /* Set CSEL value */
+ ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_CSEL) |
+ CCC_CSEL_HPB, CCC);
+ } else {
+ ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) |
+ CCC_OPC_CONFIG | CCC_GAC | CCC_CSEL_HPB, CCC);
+ }
/* Set CSEL value */
ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_CSEL) | CCC_CSEL_HPB,
CCC);
/* Set GTI value */
- ravb_write(ndev, ((1000 << 20) / 130) & GTI_TIV, GTI);
+ error = ravb_set_gti(ndev);
+ if (error)
+ goto out_release;
/* Request GTI loading */
ravb_write(ndev, ravb_read(ndev, GCCR) | GCCR_LTI, GCCR);
@@ -1783,6 +1844,10 @@ static int ravb_probe(struct platform_device *pdev)
/* Initialise HW timestamp list */
INIT_LIST_HEAD(&priv->ts_skb_list);
+ /* Initialise PTP Clock driver */
+ if (chip_id != RCAR_GEN2)
+ ravb_ptp_init(ndev, pdev);
+
/* Debug message level */
priv->msg_enable = RAVB_DEF_MSG_ENABLE;
@@ -1824,6 +1889,10 @@ out_napi_del:
out_dma_free:
dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
priv->desc_bat_dma);
+
+ /* Stop PTP Clock driver */
+ if (chip_id != RCAR_GEN2)
+ ravb_ptp_stop(ndev);
out_release:
if (ndev)
free_netdev(ndev);
@@ -1838,6 +1907,10 @@ static int ravb_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct ravb_private *priv = netdev_priv(ndev);
+ /* Stop PTP Clock driver */
+ if (priv->chip_id != RCAR_GEN2)
+ ravb_ptp_stop(ndev);
+
dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
priv->desc_bat_dma);
/* Set reset mode */
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 6a8fc0f341ff..dfa9e59c9442 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -473,6 +473,109 @@ static void sh_eth_set_duplex(struct net_device *ndev)
sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
}
+static void sh_eth_chip_reset(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ /* reset device */
+ sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
+ mdelay(1);
+}
+
+static void sh_eth_set_rate_gether(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ switch (mdp->speed) {
+ case 10: /* 10BASE */
+ sh_eth_write(ndev, GECMR_10, GECMR);
+ break;
+ case 100:/* 100BASE */
+ sh_eth_write(ndev, GECMR_100, GECMR);
+ break;
+ case 1000: /* 1000BASE */
+ sh_eth_write(ndev, GECMR_1000, GECMR);
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef CONFIG_OF
+/* R7S72100 */
+static struct sh_eth_cpu_data r7s72100_data = {
+ .chip_reset = sh_eth_chip_reset,
+ .set_duplex = sh_eth_set_duplex,
+
+ .register_type = SH_ETH_REG_FAST_RZ,
+
+ .ecsr_value = ECSR_ICD,
+ .ecsipr_value = ECSIPR_ICDIP,
+ .eesipr_value = 0xff7f009f,
+
+ .tx_check = EESR_TC1 | EESR_FTC,
+ .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
+ EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
+ EESR_TDE | EESR_ECI,
+ .fdr_value = 0x0000070f,
+
+ .no_psr = 1,
+ .apr = 1,
+ .mpr = 1,
+ .tpauser = 1,
+ .hw_swap = 1,
+ .rpadir = 1,
+ .rpadir_value = 2 << 16,
+ .no_trimd = 1,
+ .no_ade = 1,
+ .hw_crc = 1,
+ .tsu = 1,
+ .shift_rd0 = 1,
+};
+
+static void sh_eth_chip_reset_r8a7740(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ /* reset device */
+ sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
+ mdelay(1);
+
+ sh_eth_select_mii(ndev);
+}
+
+/* R8A7740 */
+static struct sh_eth_cpu_data r8a7740_data = {
+ .chip_reset = sh_eth_chip_reset_r8a7740,
+ .set_duplex = sh_eth_set_duplex,
+ .set_rate = sh_eth_set_rate_gether,
+
+ .register_type = SH_ETH_REG_GIGABIT,
+
+ .ecsr_value = ECSR_ICD | ECSR_MPD,
+ .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
+ .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
+
+ .tx_check = EESR_TC1 | EESR_FTC,
+ .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
+ EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
+ EESR_TDE | EESR_ECI,
+ .fdr_value = 0x0000070f,
+
+ .apr = 1,
+ .mpr = 1,
+ .tpauser = 1,
+ .bculr = 1,
+ .hw_swap = 1,
+ .rpadir = 1,
+ .rpadir_value = 2 << 16,
+ .no_trimd = 1,
+ .no_ade = 1,
+ .tsu = 1,
+ .select_mii = 1,
+ .shift_rd0 = 1,
+};
+
/* There is CPU dependent code */
static void sh_eth_set_rate_r8a777x(struct net_device *ndev)
{
@@ -538,6 +641,7 @@ static struct sh_eth_cpu_data r8a779x_data = {
.hw_swap = 1,
.rmiimode = 1,
};
+#endif /* CONFIG_OF */
static void sh_eth_set_rate_sh7724(struct net_device *ndev)
{
@@ -695,34 +799,6 @@ static struct sh_eth_cpu_data sh7757_data_giga = {
.tsu = 1,
};
-static void sh_eth_chip_reset(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
-
- /* reset device */
- sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
- mdelay(1);
-}
-
-static void sh_eth_set_rate_gether(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
-
- switch (mdp->speed) {
- case 10: /* 10BASE */
- sh_eth_write(ndev, GECMR_10, GECMR);
- break;
- case 100:/* 100BASE */
- sh_eth_write(ndev, GECMR_100, GECMR);
- break;
- case 1000: /* 1000BASE */
- sh_eth_write(ndev, GECMR_1000, GECMR);
- break;
- default:
- break;
- }
-}
-
/* SH7734 */
static struct sh_eth_cpu_data sh7734_data = {
.chip_reset = sh_eth_chip_reset,
@@ -780,80 +856,6 @@ static struct sh_eth_cpu_data sh7763_data = {
.irq_flags = IRQF_SHARED,
};
-static void sh_eth_chip_reset_r8a7740(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
-
- /* reset device */
- sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
- mdelay(1);
-
- sh_eth_select_mii(ndev);
-}
-
-/* R8A7740 */
-static struct sh_eth_cpu_data r8a7740_data = {
- .chip_reset = sh_eth_chip_reset_r8a7740,
- .set_duplex = sh_eth_set_duplex,
- .set_rate = sh_eth_set_rate_gether,
-
- .register_type = SH_ETH_REG_GIGABIT,
-
- .ecsr_value = ECSR_ICD | ECSR_MPD,
- .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
- .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
-
- .tx_check = EESR_TC1 | EESR_FTC,
- .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
- EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
- EESR_TDE | EESR_ECI,
- .fdr_value = 0x0000070f,
-
- .apr = 1,
- .mpr = 1,
- .tpauser = 1,
- .bculr = 1,
- .hw_swap = 1,
- .rpadir = 1,
- .rpadir_value = 2 << 16,
- .no_trimd = 1,
- .no_ade = 1,
- .tsu = 1,
- .select_mii = 1,
- .shift_rd0 = 1,
-};
-
-/* R7S72100 */
-static struct sh_eth_cpu_data r7s72100_data = {
- .chip_reset = sh_eth_chip_reset,
- .set_duplex = sh_eth_set_duplex,
-
- .register_type = SH_ETH_REG_FAST_RZ,
-
- .ecsr_value = ECSR_ICD,
- .ecsipr_value = ECSIPR_ICDIP,
- .eesipr_value = 0xff7f009f,
-
- .tx_check = EESR_TC1 | EESR_FTC,
- .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
- EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
- EESR_TDE | EESR_ECI,
- .fdr_value = 0x0000070f,
-
- .no_psr = 1,
- .apr = 1,
- .mpr = 1,
- .tpauser = 1,
- .hw_swap = 1,
- .rpadir = 1,
- .rpadir_value = 2 << 16,
- .no_trimd = 1,
- .no_ade = 1,
- .hw_crc = 1,
- .tsu = 1,
- .shift_rd0 = 1,
-};
-
static struct sh_eth_cpu_data sh7619_data = {
.register_type = SH_ETH_REG_FAST_SH3_SH2,
@@ -965,30 +967,6 @@ static void sh_eth_set_receive_align(struct sk_buff *skb)
skb_reserve(skb, SH_ETH_RX_ALIGN - reserve);
}
-
-/* CPU <-> EDMAC endian convert */
-static inline __u32 cpu_to_edmac(struct sh_eth_private *mdp, u32 x)
-{
- switch (mdp->edmac_endian) {
- case EDMAC_LITTLE_ENDIAN:
- return cpu_to_le32(x);
- case EDMAC_BIG_ENDIAN:
- return cpu_to_be32(x);
- }
- return x;
-}
-
-static inline __u32 edmac_to_cpu(struct sh_eth_private *mdp, u32 x)
-{
- switch (mdp->edmac_endian) {
- case EDMAC_LITTLE_ENDIAN:
- return le32_to_cpu(x);
- case EDMAC_BIG_ENDIAN:
- return be32_to_cpu(x);
- }
- return x;
-}
-
/* Program the hardware MAC address from dev->dev_addr. */
static void update_mac_address(struct net_device *ndev)
{
@@ -1011,12 +989,15 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac)
if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5]) {
memcpy(ndev->dev_addr, mac, ETH_ALEN);
} else {
- ndev->dev_addr[0] = (sh_eth_read(ndev, MAHR) >> 24);
- ndev->dev_addr[1] = (sh_eth_read(ndev, MAHR) >> 16) & 0xFF;
- ndev->dev_addr[2] = (sh_eth_read(ndev, MAHR) >> 8) & 0xFF;
- ndev->dev_addr[3] = (sh_eth_read(ndev, MAHR) & 0xFF);
- ndev->dev_addr[4] = (sh_eth_read(ndev, MALR) >> 8) & 0xFF;
- ndev->dev_addr[5] = (sh_eth_read(ndev, MALR) & 0xFF);
+ u32 mahr = sh_eth_read(ndev, MAHR);
+ u32 malr = sh_eth_read(ndev, MALR);
+
+ ndev->dev_addr[0] = (mahr >> 24) & 0xFF;
+ ndev->dev_addr[1] = (mahr >> 16) & 0xFF;
+ ndev->dev_addr[2] = (mahr >> 8) & 0xFF;
+ ndev->dev_addr[3] = (mahr >> 0) & 0xFF;
+ ndev->dev_addr[4] = (malr >> 8) & 0xFF;
+ ndev->dev_addr[5] = (malr >> 0) & 0xFF;
}
}
@@ -1032,56 +1013,34 @@ struct bb_info {
void (*set_gate)(void *addr);
struct mdiobb_ctrl ctrl;
void *addr;
- u32 mmd_msk;/* MMD */
- u32 mdo_msk;
- u32 mdi_msk;
- u32 mdc_msk;
};
-/* PHY bit set */
-static void bb_set(void *addr, u32 msk)
+static void sh_mdio_ctrl(struct mdiobb_ctrl *ctrl, u32 mask, int set)
{
- iowrite32(ioread32(addr) | msk, addr);
-}
+ struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
+ u32 pir;
-/* PHY bit clear */
-static void bb_clr(void *addr, u32 msk)
-{
- iowrite32((ioread32(addr) & ~msk), addr);
-}
+ if (bitbang->set_gate)
+ bitbang->set_gate(bitbang->addr);
-/* PHY bit read */
-static int bb_read(void *addr, u32 msk)
-{
- return (ioread32(addr) & msk) != 0;
+ pir = ioread32(bitbang->addr);
+ if (set)
+ pir |= mask;
+ else
+ pir &= ~mask;
+ iowrite32(pir, bitbang->addr);
}
/* Data I/O pin control */
static void sh_mmd_ctrl(struct mdiobb_ctrl *ctrl, int bit)
{
- struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
-
- if (bitbang->set_gate)
- bitbang->set_gate(bitbang->addr);
-
- if (bit)
- bb_set(bitbang->addr, bitbang->mmd_msk);
- else
- bb_clr(bitbang->addr, bitbang->mmd_msk);
+ sh_mdio_ctrl(ctrl, PIR_MMD, bit);
}
/* Set bit data*/
static void sh_set_mdio(struct mdiobb_ctrl *ctrl, int bit)
{
- struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
-
- if (bitbang->set_gate)
- bitbang->set_gate(bitbang->addr);
-
- if (bit)
- bb_set(bitbang->addr, bitbang->mdo_msk);
- else
- bb_clr(bitbang->addr, bitbang->mdo_msk);
+ sh_mdio_ctrl(ctrl, PIR_MDO, bit);
}
/* Get bit data*/
@@ -1092,21 +1051,13 @@ static int sh_get_mdio(struct mdiobb_ctrl *ctrl)
if (bitbang->set_gate)
bitbang->set_gate(bitbang->addr);
- return bb_read(bitbang->addr, bitbang->mdi_msk);
+ return (ioread32(bitbang->addr) & PIR_MDI) != 0;
}
/* MDC pin control */
static void sh_mdc_ctrl(struct mdiobb_ctrl *ctrl, int bit)
{
- struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
-
- if (bitbang->set_gate)
- bitbang->set_gate(bitbang->addr);
-
- if (bit)
- bb_set(bitbang->addr, bitbang->mdc_msk);
- else
- bb_clr(bitbang->addr, bitbang->mdc_msk);
+ sh_mdio_ctrl(ctrl, PIR_MDC, bit);
}
/* mdio bus control struct */
@@ -1189,7 +1140,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
rxdesc = &mdp->rx_ring[i];
/* The size of the buffer is a multiple of 32 bytes. */
buf_len = ALIGN(mdp->rx_buf_sz, 32);
- rxdesc->len = cpu_to_edmac(mdp, buf_len << 16);
+ rxdesc->len = cpu_to_le32(buf_len << 16);
dma_addr = dma_map_single(&ndev->dev, skb->data, buf_len,
DMA_FROM_DEVICE);
if (dma_mapping_error(&ndev->dev, dma_addr)) {
@@ -1197,8 +1148,8 @@ static void sh_eth_ring_format(struct net_device *ndev)
break;
}
mdp->rx_skbuff[i] = skb;
- rxdesc->addr = cpu_to_edmac(mdp, dma_addr);
- rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
+ rxdesc->addr = cpu_to_le32(dma_addr);
+ rxdesc->status = cpu_to_le32(RD_RACT | RD_RFP);
/* Rx descriptor address set */
if (i == 0) {
@@ -1212,7 +1163,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
mdp->dirty_rx = (u32) (i - mdp->num_rx_ring);
/* Mark the last entry as wrapping the ring. */
- rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE);
+ rxdesc->status |= cpu_to_le32(RD_RDLE);
memset(mdp->tx_ring, 0, tx_ringsize);
@@ -1220,8 +1171,8 @@ static void sh_eth_ring_format(struct net_device *ndev)
for (i = 0; i < mdp->num_tx_ring; i++) {
mdp->tx_skbuff[i] = NULL;
txdesc = &mdp->tx_ring[i];
- txdesc->status = cpu_to_edmac(mdp, TD_TFP);
- txdesc->len = cpu_to_edmac(mdp, 0);
+ txdesc->status = cpu_to_le32(TD_TFP);
+ txdesc->len = cpu_to_le32(0);
if (i == 0) {
/* Tx descriptor address set */
sh_eth_write(ndev, mdp->tx_desc_dma, TDLAR);
@@ -1231,7 +1182,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
}
}
- txdesc->status |= cpu_to_edmac(mdp, TD_TDLE);
+ txdesc->status |= cpu_to_le32(TD_TDLE);
}
/* Get skb and descriptor buffer */
@@ -1289,7 +1240,6 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
{
int ret = 0;
struct sh_eth_private *mdp = netdev_priv(ndev);
- u32 val;
/* Soft Reset */
ret = sh_eth_reset(ndev);
@@ -1342,10 +1292,8 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
}
/* PAUSE Prohibition */
- val = (sh_eth_read(ndev, ECMR) & ECMR_DM) |
- ECMR_ZPF | (mdp->duplex ? ECMR_DM : 0) | ECMR_TE | ECMR_RE;
-
- sh_eth_write(ndev, val, ECMR);
+ sh_eth_write(ndev, ECMR_ZPF | (mdp->duplex ? ECMR_DM : 0) |
+ ECMR_TE | ECMR_RE, ECMR);
if (mdp->cd->set_rate)
mdp->cd->set_rate(ndev);
@@ -1387,7 +1335,7 @@ static void sh_eth_dev_exit(struct net_device *ndev)
* packet boundary if it's currently running
*/
for (i = 0; i < mdp->num_tx_ring; i++)
- mdp->tx_ring[i].status &= ~cpu_to_edmac(mdp, TD_TACT);
+ mdp->tx_ring[i].status &= ~cpu_to_le32(TD_TACT);
/* Disable TX FIFO egress to MAC */
sh_eth_rcv_snd_disable(ndev);
@@ -1419,29 +1367,28 @@ static int sh_eth_txfree(struct net_device *ndev)
for (; mdp->cur_tx - mdp->dirty_tx > 0; mdp->dirty_tx++) {
entry = mdp->dirty_tx % mdp->num_tx_ring;
txdesc = &mdp->tx_ring[entry];
- if (txdesc->status & cpu_to_edmac(mdp, TD_TACT))
+ if (txdesc->status & cpu_to_le32(TD_TACT))
break;
/* TACT bit must be checked before all the following reads */
dma_rmb();
netif_info(mdp, tx_done, ndev,
"tx entry %d status 0x%08x\n",
- entry, edmac_to_cpu(mdp, txdesc->status));
+ entry, le32_to_cpu(txdesc->status));
/* Free the original skb. */
if (mdp->tx_skbuff[entry]) {
- dma_unmap_single(&ndev->dev,
- edmac_to_cpu(mdp, txdesc->addr),
- edmac_to_cpu(mdp, txdesc->len) >> 16,
+ dma_unmap_single(&ndev->dev, le32_to_cpu(txdesc->addr),
+ le32_to_cpu(txdesc->len) >> 16,
DMA_TO_DEVICE);
dev_kfree_skb_irq(mdp->tx_skbuff[entry]);
mdp->tx_skbuff[entry] = NULL;
free_num++;
}
- txdesc->status = cpu_to_edmac(mdp, TD_TFP);
+ txdesc->status = cpu_to_le32(TD_TFP);
if (entry >= mdp->num_tx_ring - 1)
- txdesc->status |= cpu_to_edmac(mdp, TD_TDLE);
+ txdesc->status |= cpu_to_le32(TD_TDLE);
ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += edmac_to_cpu(mdp, txdesc->len) >> 16;
+ ndev->stats.tx_bytes += le32_to_cpu(txdesc->len) >> 16;
}
return free_num;
}
@@ -1465,11 +1412,11 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
boguscnt = min(boguscnt, *quota);
limit = boguscnt;
rxdesc = &mdp->rx_ring[entry];
- while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
+ while (!(rxdesc->status & cpu_to_le32(RD_RACT))) {
/* RACT bit must be checked before all the following reads */
dma_rmb();
- desc_status = edmac_to_cpu(mdp, rxdesc->status);
- pkt_len = edmac_to_cpu(mdp, rxdesc->len) & RD_RFL;
+ desc_status = le32_to_cpu(rxdesc->status);
+ pkt_len = le32_to_cpu(rxdesc->len) & RD_RFL;
if (--boguscnt < 0)
break;
@@ -1507,7 +1454,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
if (desc_status & RD_RFS10)
ndev->stats.rx_over_errors++;
} else if (skb) {
- dma_addr = edmac_to_cpu(mdp, rxdesc->addr);
+ dma_addr = le32_to_cpu(rxdesc->addr);
if (!mdp->cd->hw_swap)
sh_eth_soft_swap(
phys_to_virt(ALIGN(dma_addr, 4)),
@@ -1536,7 +1483,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
rxdesc = &mdp->rx_ring[entry];
/* The size of the buffer is 32 byte boundary. */
buf_len = ALIGN(mdp->rx_buf_sz, 32);
- rxdesc->len = cpu_to_edmac(mdp, buf_len << 16);
+ rxdesc->len = cpu_to_le32(buf_len << 16);
if (mdp->rx_skbuff[entry] == NULL) {
skb = netdev_alloc_skb(ndev, skbuff_size);
@@ -1552,15 +1499,14 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
mdp->rx_skbuff[entry] = skb;
skb_checksum_none_assert(skb);
- rxdesc->addr = cpu_to_edmac(mdp, dma_addr);
+ rxdesc->addr = cpu_to_le32(dma_addr);
}
dma_wmb(); /* RACT bit must be set after all the above writes */
if (entry >= mdp->num_rx_ring - 1)
rxdesc->status |=
- cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDLE);
+ cpu_to_le32(RD_RACT | RD_RFP | RD_RDLE);
else
- rxdesc->status |=
- cpu_to_edmac(mdp, RD_RACT | RD_RFP);
+ rxdesc->status |= cpu_to_le32(RD_RACT | RD_RFP);
}
/* Restart Rx engine if stopped. */
@@ -1877,8 +1823,7 @@ static int sh_eth_phy_init(struct net_device *ndev)
return PTR_ERR(phydev);
}
- netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n",
- phydev->addr, phydev->irq, phydev->drv->name);
+ phy_attached_info(phydev);
mdp->phydev = phydev;
@@ -2360,8 +2305,8 @@ static void sh_eth_tx_timeout(struct net_device *ndev)
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < mdp->num_rx_ring; i++) {
rxdesc = &mdp->rx_ring[i];
- rxdesc->status = cpu_to_edmac(mdp, 0);
- rxdesc->addr = cpu_to_edmac(mdp, 0xBADF00D0);
+ rxdesc->status = cpu_to_le32(0);
+ rxdesc->addr = cpu_to_le32(0xBADF00D0);
dev_kfree_skb(mdp->rx_skbuff[i]);
mdp->rx_skbuff[i] = NULL;
}
@@ -2409,14 +2354,14 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
kfree_skb(skb);
return NETDEV_TX_OK;
}
- txdesc->addr = cpu_to_edmac(mdp, dma_addr);
- txdesc->len = cpu_to_edmac(mdp, skb->len << 16);
+ txdesc->addr = cpu_to_le32(dma_addr);
+ txdesc->len = cpu_to_le32(skb->len << 16);
dma_wmb(); /* TACT bit must be set after all the above writes */
if (entry >= mdp->num_tx_ring - 1)
- txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE);
+ txdesc->status |= cpu_to_le32(TD_TACT | TD_TDLE);
else
- txdesc->status |= cpu_to_edmac(mdp, TD_TACT);
+ txdesc->status |= cpu_to_le32(TD_TACT);
mdp->cur_tx++;
@@ -2911,7 +2856,7 @@ static int sh_mdio_release(struct sh_eth_private *mdp)
static int sh_mdio_init(struct sh_eth_private *mdp,
struct sh_eth_plat_data *pd)
{
- int ret, i;
+ int ret;
struct bb_info *bitbang;
struct platform_device *pdev = mdp->pdev;
struct device *dev = &mdp->pdev->dev;
@@ -2924,10 +2869,6 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
/* bitbang init */
bitbang->addr = mdp->addr + mdp->reg_offset[PIR];
bitbang->set_gate = pd->set_mdio_gate;
- bitbang->mdi_msk = PIR_MDI;
- bitbang->mdo_msk = PIR_MDO;
- bitbang->mmd_msk = PIR_MMD;
- bitbang->mdc_msk = PIR_MDC;
bitbang->ctrl.ops = &bb_ops;
/* MII controller setting */
@@ -2941,20 +2882,10 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
snprintf(mdp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
- /* PHY IRQ */
- mdp->mii_bus->irq = devm_kmalloc_array(dev, PHY_MAX_ADDR, sizeof(int),
- GFP_KERNEL);
- if (!mdp->mii_bus->irq) {
- ret = -ENOMEM;
- goto out_free_bus;
- }
-
/* register MDIO bus */
if (dev->of_node) {
ret = of_mdiobus_register(mdp->mii_bus, dev->of_node);
} else {
- for (i = 0; i < PHY_MAX_ADDR; i++)
- mdp->mii_bus->irq[i] = PHY_POLL;
if (pd->phy_irq > 0)
mdp->mii_bus->irq[pd->phy] = pd->phy_irq;
@@ -3126,8 +3057,6 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
/* get PHY ID */
mdp->phy_id = pd->phy;
mdp->phy_interface = pd->phy_interface;
- /* EDMAC endian */
- mdp->edmac_endian = pd->edmac_endian;
mdp->no_ether_link = pd->no_ether_link;
mdp->ether_link_active_low = pd->ether_link_active_low;
@@ -3307,13 +3236,6 @@ static struct platform_device_id sh_eth_id_table[] = {
{ "sh7757-ether", (kernel_ulong_t)&sh7757_data },
{ "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga },
{ "sh7763-gether", (kernel_ulong_t)&sh7763_data },
- { "r7s72100-ether", (kernel_ulong_t)&r7s72100_data },
- { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data },
- { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data },
- { "r8a7790-ether", (kernel_ulong_t)&r8a779x_data },
- { "r8a7791-ether", (kernel_ulong_t)&r8a779x_data },
- { "r8a7793-ether", (kernel_ulong_t)&r8a779x_data },
- { "r8a7794-ether", (kernel_ulong_t)&r8a779x_data },
{ }
};
MODULE_DEVICE_TABLE(platform, sh_eth_id_table);
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index 72fcfc924589..8fa4ef3a7fdd 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -513,7 +513,6 @@ struct sh_eth_private {
u32 cur_rx, dirty_rx; /* Producer/consumer ring indices */
u32 cur_tx, dirty_tx;
u32 rx_buf_sz; /* Based on MTU+slack. */
- int edmac_endian;
struct napi_struct napi;
bool irq_enabled;
/* MII transceiver section. */
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index e9f2349e98bc..a4ab71d43e4e 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -4998,7 +4998,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
dev->netdev_ops = &rocker_port_netdev_ops;
dev->ethtool_ops = &rocker_port_ethtool_ops;
dev->switchdev_ops = &rocker_port_switchdev_ops;
- netif_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx,
+ netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx,
NAPI_POLL_WEIGHT);
netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx,
NAPI_POLL_WEIGHT);
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
index 43ccb4a6de15..467ff7033606 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
@@ -180,7 +180,7 @@ int sxgbe_mdio_register(struct net_device *ndev)
}
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- struct phy_device *phy = mdio_bus->phy_map[phy_addr];
+ struct phy_device *phy = mdiobus_get_phy(mdio_bus, phy_addr);
if (phy) {
char irq_num[4];
@@ -216,7 +216,7 @@ int sxgbe_mdio_register(struct net_device *ndev)
}
netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n",
phy->phy_id, phy_addr, irq_str,
- dev_name(&phy->dev), act ? " active" : "");
+ phydev_name(phy), act ? " active" : "");
phy_found = true;
}
}
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index e6a084a6be12..98d33d462c6c 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -181,13 +181,6 @@ static int efx_ef10_init_datapath_caps(struct efx_nic *efx)
MCDI_WORD(outbuf, GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID);
if (!(nic_data->datapath_caps &
- (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN))) {
- netif_err(efx, drv, efx->net_dev,
- "current firmware does not support TSO\n");
- return -ENODEV;
- }
-
- if (!(nic_data->datapath_caps &
(1 << MC_CMD_GET_CAPABILITIES_OUT_RX_PREFIX_LEN_14_LBN))) {
netif_err(efx, probe, efx->net_dev,
"current firmware does not support an RX prefix\n");
@@ -493,10 +486,17 @@ static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
BUILD_BUG_ON(MC_CMD_ALLOC_PIOBUF_IN_LEN != 0);
for (i = 0; i < n; i++) {
- rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
- outbuf, sizeof(outbuf), &outlen);
- if (rc)
+ rc = efx_mcdi_rpc_quiet(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc) {
+ /* Don't display the MC error if we didn't have space
+ * for a VF.
+ */
+ if (!(efx_ef10_is_vf(efx) && rc == -ENOSPC))
+ efx_mcdi_display_error(efx, MC_CMD_ALLOC_PIOBUF,
+ 0, outbuf, outlen, rc);
break;
+ }
if (outlen < MC_CMD_ALLOC_PIOBUF_OUT_LEN) {
rc = -EIO;
break;
@@ -1797,6 +1797,12 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue)
ESF_DZ_TX_OPTION_UDP_TCP_CSUM, csum_offload,
ESF_DZ_TX_OPTION_IP_CSUM, csum_offload);
tx_queue->write_count = 1;
+
+ if (nic_data->datapath_caps &
+ (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN)) {
+ tx_queue->tso_version = 1;
+ }
+
wmb();
efx_ef10_push_tx_desc(tx_queue, txd);
@@ -2375,8 +2381,19 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
1 << MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_LBN) {
netif_info(efx, drv, efx->net_dev,
"other functions on NIC have been reset\n");
- /* MC's boot count has incremented */
- ++nic_data->warm_boot_count;
+
+ /* With MCFW v4.6.x and earlier, the
+ * boot count will have incremented,
+ * so re-read the warm_boot_count
+ * value now to ensure this function
+ * doesn't think it has changed next
+ * time it checks.
+ */
+ rc = efx_ef10_get_warm_boot_count(efx);
+ if (rc >= 0) {
+ nic_data->warm_boot_count = rc;
+ rc = 0;
+ }
}
nic_data->workaround_26807 = true;
} else if (rc == -EPERM) {
@@ -3823,13 +3840,12 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE);
MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE,
table->entry[filter_idx].handle);
- rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf),
- NULL, 0, NULL);
+ rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP, inbuf,
+ sizeof(inbuf), NULL, 0, NULL);
if (rc)
- netdev_WARN(efx->net_dev,
- "filter_idx=%#x handle=%#llx\n",
- filter_idx,
- table->entry[filter_idx].handle);
+ netif_info(efx, drv, efx->net_dev,
+ "%s: filter %04x remove failed\n",
+ __func__, filter_idx);
kfree(spec);
}
@@ -3838,11 +3854,14 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
}
#define EFX_EF10_FILTER_DO_MARK_OLD(id) \
- if (id != EFX_EF10_FILTER_ID_INVALID) { \
- filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \
- WARN_ON(!table->entry[filter_idx].spec); \
- table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; \
- }
+ if (id != EFX_EF10_FILTER_ID_INVALID) { \
+ filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \
+ if (!table->entry[filter_idx].spec) \
+ netif_dbg(efx, drv, efx->net_dev, \
+ "%s: marked null spec old %04x:%04x\n", \
+ __func__, id, filter_idx); \
+ table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;\
+ }
static void efx_ef10_filter_mark_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
@@ -4016,9 +4035,10 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
- netif_warn(efx, drv, efx->net_dev,
- "%scast mismatch filter insert failed rc=%d\n",
- multicast ? "Multi" : "Uni", rc);
+ netif_printk(efx, drv, rc == -EPERM ? KERN_DEBUG : KERN_WARNING,
+ efx->net_dev,
+ "%scast mismatch filter insert failed rc=%d\n",
+ multicast ? "Multi" : "Uni", rc);
} else if (multicast) {
table->mcdef_id = efx_ef10_filter_get_unsafe_id(efx, rc);
if (!nic_data->workaround_26807) {
@@ -4060,19 +4080,31 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
static void efx_ef10_filter_remove_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
- bool remove_failed = false;
+ int remove_failed = 0;
+ int remove_noent = 0;
+ int rc;
int i;
for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) {
if (ACCESS_ONCE(table->entry[i].spec) &
EFX_EF10_FILTER_FLAG_AUTO_OLD) {
- if (efx_ef10_filter_remove_internal(
- efx, 1U << EFX_FILTER_PRI_AUTO,
- i, true) < 0)
- remove_failed = true;
+ rc = efx_ef10_filter_remove_internal(efx,
+ 1U << EFX_FILTER_PRI_AUTO, i, true);
+ if (rc == -ENOENT)
+ remove_noent++;
+ else if (rc)
+ remove_failed++;
}
}
- WARN_ON(remove_failed);
+
+ if (remove_failed)
+ netif_info(efx, drv, efx->net_dev,
+ "%s: failed to remove %d filters\n",
+ __func__, remove_failed);
+ if (remove_noent)
+ netif_info(efx, drv, efx->net_dev,
+ "%s: failed to remove %d non-existent filters\n",
+ __func__, remove_noent);
}
static int efx_ef10_vport_set_mac_address(struct efx_nic *efx)
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index a3c42a376741..0705ec869487 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -2059,7 +2059,6 @@ static void efx_init_napi_channel(struct efx_channel *channel)
channel->napi_dev = efx->net_dev;
netif_napi_add(channel->napi_dev, &channel->napi_str,
efx_poll, napi_weight);
- napi_hash_add(&channel->napi_str);
efx_channel_busy_poll_init(channel);
}
@@ -2785,6 +2784,12 @@ static const struct pci_device_id efx_pci_table[] = {
.driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type},
{PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0923), /* SFC9140 PF */
.driver_data = (unsigned long) &efx_hunt_a0_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x1923), /* SFC9140 VF */
+ .driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0a03), /* SFC9220 PF */
+ .driver_data = (unsigned long) &efx_hunt_a0_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x1a03), /* SFC9220 VF */
+ .driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type},
{0} /* end of list */
};
@@ -3123,10 +3128,10 @@ static int efx_pci_probe(struct pci_dev *pci_dev,
net_dev->features |= (efx->type->offload_features | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO |
NETIF_F_RXCSUM);
- if (efx->type->offload_features & NETIF_F_V6_CSUM)
+ if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
net_dev->features |= NETIF_F_TSO6;
/* Mask for features that also apply to VLAN devices */
- net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG |
+ net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_ALL_TSO |
NETIF_F_RXCSUM);
/* All offloads can be toggled */
@@ -3169,14 +3174,15 @@ static int efx_pci_probe(struct pci_dev *pci_dev,
rtnl_lock();
rc = efx_mtd_probe(efx);
rtnl_unlock();
- if (rc)
+ if (rc && rc != -EPERM)
netif_warn(efx, probe, efx->net_dev,
"failed to create MTDs (%d)\n", rc);
rc = pci_enable_pcie_error_reporting(pci_dev);
if (rc && rc != -EINVAL)
- netif_warn(efx, probe, efx->net_dev,
- "pci_enable_pcie_error_reporting failed (%d)\n", rc);
+ netif_notice(efx, probe, efx->net_dev,
+ "PCIE error reporting unavailable (%d).\n",
+ rc);
return 0;
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index 41fb6b60a3f0..d28e7dd8fa3c 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -82,6 +82,7 @@ int efx_mcdi_init(struct efx_nic *efx)
mcdi->logging_enabled = mcdi_logging_default;
#endif
init_waitqueue_head(&mcdi->wq);
+ init_waitqueue_head(&mcdi->proxy_rx_wq);
spin_lock_init(&mcdi->iface_lock);
mcdi->state = MCDI_STATE_QUIESCENT;
mcdi->mode = MCDI_MODE_POLL;
@@ -315,6 +316,7 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx)
}
#endif
+ mcdi->resprc_raw = 0;
if (error && mcdi->resp_data_len == 0) {
netif_err(efx, hw, efx->net_dev, "MC rebooted\n");
mcdi->resprc = -EIO;
@@ -325,8 +327,8 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx)
mcdi->resprc = -EIO;
} else if (error) {
efx->type->mcdi_read_response(efx, &hdr, mcdi->resp_hdr_len, 4);
- mcdi->resprc =
- efx_mcdi_errno(EFX_DWORD_FIELD(hdr, EFX_DWORD_0));
+ mcdi->resprc_raw = EFX_DWORD_FIELD(hdr, EFX_DWORD_0);
+ mcdi->resprc = efx_mcdi_errno(mcdi->resprc_raw);
} else {
mcdi->resprc = 0;
}
@@ -621,9 +623,30 @@ efx_mcdi_check_supported(struct efx_nic *efx, unsigned int cmd, size_t inlen)
return 0;
}
-static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
+static bool efx_mcdi_get_proxy_handle(struct efx_nic *efx,
+ size_t hdr_len, size_t data_len,
+ u32 *proxy_handle)
+{
+ MCDI_DECLARE_BUF_ERR(testbuf);
+ const size_t buflen = sizeof(testbuf);
+
+ if (!proxy_handle || data_len < buflen)
+ return false;
+
+ efx->type->mcdi_read_response(efx, testbuf, hdr_len, buflen);
+ if (MCDI_DWORD(testbuf, ERR_CODE) == MC_CMD_ERR_PROXY_PENDING) {
+ *proxy_handle = MCDI_DWORD(testbuf, ERR_PROXY_PENDING_HANDLE);
+ return true;
+ }
+
+ return false;
+}
+
+static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned int cmd,
+ size_t inlen,
efx_dword_t *outbuf, size_t outlen,
- size_t *outlen_actual, bool quiet)
+ size_t *outlen_actual, bool quiet,
+ u32 *proxy_handle, int *raw_rc)
{
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
MCDI_DECLARE_BUF_ERR(errbuf);
@@ -657,6 +680,9 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
spin_unlock_bh(&mcdi->iface_lock);
}
+ if (proxy_handle)
+ *proxy_handle = 0;
+
if (rc != 0) {
if (outlen_actual)
*outlen_actual = 0;
@@ -669,6 +695,8 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
* acquiring the iface_lock. */
spin_lock_bh(&mcdi->iface_lock);
rc = mcdi->resprc;
+ if (raw_rc)
+ *raw_rc = mcdi->resprc_raw;
hdr_len = mcdi->resp_hdr_len;
data_len = mcdi->resp_data_len;
err_len = min(sizeof(errbuf), data_len);
@@ -689,6 +717,12 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
netif_err(efx, hw, efx->net_dev, "MC fatal error %d\n",
-rc);
efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
+ } else if (proxy_handle && (rc == -EPROTO) &&
+ efx_mcdi_get_proxy_handle(efx, hdr_len, data_len,
+ proxy_handle)) {
+ mcdi->proxy_rx_status = 0;
+ mcdi->proxy_rx_handle = 0;
+ mcdi->state = MCDI_STATE_PROXY_WAIT;
} else if (rc && !quiet) {
efx_mcdi_display_error(efx, cmd, inlen, errbuf, err_len,
rc);
@@ -701,34 +735,195 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
}
}
- efx_mcdi_release(mcdi);
+ if (!proxy_handle || !*proxy_handle)
+ efx_mcdi_release(mcdi);
return rc;
}
-static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
+static void efx_mcdi_proxy_abort(struct efx_mcdi_iface *mcdi)
+{
+ if (mcdi->state == MCDI_STATE_PROXY_WAIT) {
+ /* Interrupt the proxy wait. */
+ mcdi->proxy_rx_status = -EINTR;
+ wake_up(&mcdi->proxy_rx_wq);
+ }
+}
+
+static void efx_mcdi_ev_proxy_response(struct efx_nic *efx,
+ u32 handle, int status)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+
+ WARN_ON(mcdi->state != MCDI_STATE_PROXY_WAIT);
+
+ mcdi->proxy_rx_status = efx_mcdi_errno(status);
+ /* Ensure the status is written before we update the handle, since the
+ * latter is used to check if we've finished.
+ */
+ wmb();
+ mcdi->proxy_rx_handle = handle;
+ wake_up(&mcdi->proxy_rx_wq);
+}
+
+static int efx_mcdi_proxy_wait(struct efx_nic *efx, u32 handle, bool quiet)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+ int rc;
+
+ /* Wait for a proxy event, or timeout. */
+ rc = wait_event_timeout(mcdi->proxy_rx_wq,
+ mcdi->proxy_rx_handle != 0 ||
+ mcdi->proxy_rx_status == -EINTR,
+ MCDI_RPC_TIMEOUT);
+
+ if (rc <= 0) {
+ netif_dbg(efx, hw, efx->net_dev,
+ "MCDI proxy timeout %d\n", handle);
+ return -ETIMEDOUT;
+ } else if (mcdi->proxy_rx_handle != handle) {
+ netif_warn(efx, hw, efx->net_dev,
+ "MCDI proxy unexpected handle %d (expected %d)\n",
+ mcdi->proxy_rx_handle, handle);
+ return -EINVAL;
+ }
+
+ return mcdi->proxy_rx_status;
+}
+
+static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned int cmd,
const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
- size_t *outlen_actual, bool quiet)
+ size_t *outlen_actual, bool quiet, int *raw_rc)
{
+ u32 proxy_handle = 0; /* Zero is an invalid proxy handle. */
int rc;
+ if (inbuf && inlen && (inbuf == outbuf)) {
+ /* The input buffer can't be aliased with the output. */
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
rc = efx_mcdi_rpc_start(efx, cmd, inbuf, inlen);
- if (rc) {
- if (outlen_actual)
- *outlen_actual = 0;
+ if (rc)
return rc;
+
+ rc = _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
+ outlen_actual, quiet, &proxy_handle, raw_rc);
+
+ if (proxy_handle) {
+ /* Handle proxy authorisation. This allows approval of MCDI
+ * operations to be delegated to the admin function, allowing
+ * fine control over (eg) multicast subscriptions.
+ */
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+
+ netif_dbg(efx, hw, efx->net_dev,
+ "MCDI waiting for proxy auth %d\n",
+ proxy_handle);
+ rc = efx_mcdi_proxy_wait(efx, proxy_handle, quiet);
+
+ if (rc == 0) {
+ netif_dbg(efx, hw, efx->net_dev,
+ "MCDI proxy retry %d\n", proxy_handle);
+
+ /* We now retry the original request. */
+ mcdi->state = MCDI_STATE_RUNNING_SYNC;
+ efx_mcdi_send_request(efx, cmd, inbuf, inlen);
+
+ rc = _efx_mcdi_rpc_finish(efx, cmd, inlen,
+ outbuf, outlen, outlen_actual,
+ quiet, NULL, raw_rc);
+ } else {
+ netif_printk(efx, hw,
+ rc == -EPERM ? KERN_DEBUG : KERN_ERR,
+ efx->net_dev,
+ "MC command 0x%x failed after proxy auth rc=%d\n",
+ cmd, rc);
+
+ if (rc == -EINTR || rc == -EIO)
+ efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
+ efx_mcdi_release(mcdi);
+ }
}
- return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
- outlen_actual, quiet);
+
+ return rc;
}
+static int _efx_mcdi_rpc_evb_retry(struct efx_nic *efx, unsigned cmd,
+ const efx_dword_t *inbuf, size_t inlen,
+ efx_dword_t *outbuf, size_t outlen,
+ size_t *outlen_actual, bool quiet)
+{
+ int raw_rc = 0;
+ int rc;
+
+ rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
+ outbuf, outlen, outlen_actual, true, &raw_rc);
+
+ if ((rc == -EPROTO) && (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
+ efx->type->is_vf) {
+ /* If the EVB port isn't available within a VF this may
+ * mean the PF is still bringing the switch up. We should
+ * retry our request shortly.
+ */
+ unsigned long abort_time = jiffies + MCDI_RPC_TIMEOUT;
+ unsigned int delay_us = 10000;
+
+ netif_dbg(efx, hw, efx->net_dev,
+ "%s: NO_EVB_PORT; will retry request\n",
+ __func__);
+
+ do {
+ usleep_range(delay_us, delay_us + 10000);
+ rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
+ outbuf, outlen, outlen_actual,
+ true, &raw_rc);
+ if (delay_us < 100000)
+ delay_us <<= 1;
+ } while ((rc == -EPROTO) &&
+ (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
+ time_before(jiffies, abort_time));
+ }
+
+ if (rc && !quiet && !(cmd == MC_CMD_REBOOT && rc == -EIO))
+ efx_mcdi_display_error(efx, cmd, inlen,
+ outbuf, outlen, rc);
+
+ return rc;
+}
+
+/**
+ * efx_mcdi_rpc - Issue an MCDI command and wait for completion
+ * @efx: NIC through which to issue the command
+ * @cmd: Command type number
+ * @inbuf: Command parameters
+ * @inlen: Length of command parameters, in bytes. Must be a multiple
+ * of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
+ * @outbuf: Response buffer. May be %NULL if @outlen is 0.
+ * @outlen: Length of response buffer, in bytes. If the actual
+ * response is longer than @outlen & ~3, it will be truncated
+ * to that length.
+ * @outlen_actual: Pointer through which to return the actual response
+ * length. May be %NULL if this is not needed.
+ *
+ * This function may sleep and therefore must be called in an appropriate
+ * context.
+ *
+ * Return: A negative error code, or zero if successful. The error
+ * code may come from the MCDI response or may indicate a failure
+ * to communicate with the MC. In the former case, the response
+ * will still be copied to @outbuf and *@outlen_actual will be
+ * set accordingly. In the latter case, *@outlen_actual will be
+ * set to zero.
+ */
int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual)
{
- return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen,
- outlen_actual, false);
+ return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
+ outlen_actual, false);
}
/* Normally, on receiving an error code in the MCDI response,
@@ -744,8 +939,8 @@ int efx_mcdi_rpc_quiet(struct efx_nic *efx, unsigned cmd,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual)
{
- return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen,
- outlen_actual, true);
+ return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
+ outlen_actual, true);
}
int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
@@ -866,7 +1061,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
- outlen_actual, false);
+ outlen_actual, false, NULL, NULL);
}
int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen,
@@ -874,7 +1069,7 @@ int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
- outlen_actual, true);
+ outlen_actual, true, NULL, NULL);
}
void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd,
@@ -887,9 +1082,10 @@ void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd,
code = MCDI_DWORD(outbuf, ERR_CODE);
if (outlen >= MC_CMD_ERR_ARG_OFST + 4)
err_arg = MCDI_DWORD(outbuf, ERR_ARG);
- netif_err(efx, hw, efx->net_dev,
- "MC command 0x%x inlen %d failed rc=%d (raw=%d) arg=%d\n",
- cmd, (int)inlen, rc, code, err_arg);
+ netif_printk(efx, hw, rc == -EPERM ? KERN_DEBUG : KERN_ERR,
+ efx->net_dev,
+ "MC command 0x%x inlen %zu failed rc=%d (raw=%d) arg=%d\n",
+ cmd, inlen, rc, code, err_arg);
}
/* Switch to polled MCDI completions. This can be called in various
@@ -1014,8 +1210,13 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
* receiving a REBOOT event after posting the MCDI
* request. Did the mc reboot before or after the copyout? The
* best we can do always is just return failure.
+ *
+ * If there is an outstanding proxy response expected it is not going
+ * to arrive. We should thus abort it.
*/
spin_lock(&mcdi->iface_lock);
+ efx_mcdi_proxy_abort(mcdi);
+
if (efx_mcdi_complete_sync(mcdi)) {
if (mcdi->mode == MCDI_MODE_EVENTS) {
mcdi->resprc = rc;
@@ -1063,6 +1264,8 @@ static void efx_mcdi_ev_bist(struct efx_nic *efx)
spin_lock(&mcdi->iface_lock);
efx->mc_bist_for_other_fn = true;
+ efx_mcdi_proxy_abort(mcdi);
+
if (efx_mcdi_complete_sync(mcdi)) {
if (mcdi->mode == MCDI_MODE_EVENTS) {
mcdi->resprc = -EIO;
@@ -1171,6 +1374,11 @@ void efx_mcdi_process_event(struct efx_channel *channel,
EFX_QWORD_VAL(*event));
efx_schedule_reset(efx, RESET_TYPE_DMA_ERROR);
break;
+ case MCDI_EVENT_CODE_PROXY_RESPONSE:
+ efx_mcdi_ev_proxy_response(efx,
+ MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_HANDLE),
+ MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_RC));
+ break;
default:
netif_err(efx, hw, efx->net_dev, "Unknown MCDI event 0x%x\n",
code);
diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h
index 025d504c472b..c9aeb0701c9a 100644
--- a/drivers/net/ethernet/sfc/mcdi.h
+++ b/drivers/net/ethernet/sfc/mcdi.h
@@ -17,6 +17,8 @@
* @MCDI_STATE_RUNNING_SYNC: There is a synchronous MCDI request pending.
* Only the thread that moved into this state is allowed to move out of it.
* @MCDI_STATE_RUNNING_ASYNC: There is an asynchronous MCDI request pending.
+ * @MCDI_STATE_PROXY_WAIT: An MCDI request has completed with a response that
+ * indicates we must wait for a proxy try again message.
* @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread
* has not yet consumed the result. For all other threads, equivalent to
* %MCDI_STATE_RUNNING.
@@ -25,6 +27,7 @@ enum efx_mcdi_state {
MCDI_STATE_QUIESCENT,
MCDI_STATE_RUNNING_SYNC,
MCDI_STATE_RUNNING_ASYNC,
+ MCDI_STATE_PROXY_WAIT,
MCDI_STATE_COMPLETED,
};
@@ -60,6 +63,9 @@ enum efx_mcdi_mode {
* @async_timer: Timer for asynchronous request timeout
* @logging_buffer: buffer that may be used to build MCDI tracing messages
* @logging_enabled: whether to trace MCDI
+ * @proxy_rx_handle: Most recently received proxy authorisation handle
+ * @proxy_rx_status: Status of most recent proxy authorisation
+ * @proxy_rx_wq: Wait queue for updates to proxy_rx_handle
*/
struct efx_mcdi_iface {
struct efx_nic *efx;
@@ -71,6 +77,7 @@ struct efx_mcdi_iface {
unsigned int credits;
unsigned int seqno;
int resprc;
+ int resprc_raw;
size_t resp_hdr_len;
size_t resp_data_len;
spinlock_t async_lock;
@@ -80,6 +87,9 @@ struct efx_mcdi_iface {
char *logging_buffer;
bool logging_enabled;
#endif
+ unsigned int proxy_rx_handle;
+ int proxy_rx_status;
+ wait_queue_head_t proxy_rx_wq;
};
struct efx_mcdi_mon {
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index a8ddd122f685..38c422321cda 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -182,6 +182,7 @@ struct efx_tx_buffer {
*
* @efx: The associated Efx NIC
* @queue: DMA queue number
+ * @tso_version: Version of TSO in use for this queue.
* @channel: The associated channel
* @core_txq: The networking core TX queue structure
* @buffer: The software buffer ring
@@ -228,6 +229,7 @@ struct efx_tx_queue {
/* Members which don't change on the fast path */
struct efx_nic *efx ____cacheline_aligned_in_smp;
unsigned queue;
+ unsigned int tso_version;
struct efx_channel *channel;
struct netdev_queue *core_txq;
struct efx_tx_buffer *buffer;
@@ -1502,8 +1504,9 @@ static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue,
* same cycle, the XMAC can miss the IPG altogether. We work around
* this by adding a further 16 bytes.
*/
+#define EFX_FRAME_PAD 16
#define EFX_MAX_FRAME_LEN(mtu) \
- ((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */ + 7) & ~7) + 16)
+ (ALIGN(((mtu) + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN + EFX_FRAME_PAD), 8))
static inline bool efx_xmit_with_hwtstamp(struct sk_buff *skb)
{
diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c
index 809ea4610a77..8956995b2fe7 100644
--- a/drivers/net/ethernet/sfc/rx.c
+++ b/drivers/net/ethernet/sfc/rx.c
@@ -463,7 +463,6 @@ efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf,
skb_record_rx_queue(skb, channel->rx_queue.core_index);
- skb_mark_napi_id(skb, &channel->napi_str);
gro_result = napi_gro_frags(napi);
if (gro_result != GRO_DROP)
channel->irq_mod_score += 2;
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 67f6afaa022f..f7a0ec1bca97 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -1010,13 +1010,17 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue,
/* Parse the SKB header and initialise state. */
static int tso_start(struct tso_state *st, struct efx_nic *efx,
+ struct efx_tx_queue *tx_queue,
const struct sk_buff *skb)
{
- bool use_opt_desc = efx_nic_rev(efx) >= EFX_REV_HUNT_A0;
struct device *dma_dev = &efx->pci_dev->dev;
unsigned int header_len, in_len;
+ bool use_opt_desc = false;
dma_addr_t dma_addr;
+ if (tx_queue->tso_version == 1)
+ use_opt_desc = true;
+
st->ip_off = skb_network_header(skb) - skb->data;
st->tcp_off = skb_transport_header(skb) - skb->data;
header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u);
@@ -1271,7 +1275,7 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
/* Find the packet protocol and sanity-check it */
state.protocol = efx_tso_check_protocol(skb);
- rc = tso_start(&state, efx, skb);
+ rc = tso_start(&state, efx, tx_queue, skb);
if (rc)
goto mem_err;
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index 219a99b7a631..8af25563f627 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -864,8 +864,8 @@ static int smsc911x_phy_loopbacktest(struct net_device *dev)
for (i = 0; i < 10; i++) {
/* Set PHY to 10/FD, no ANEG, and loopback mode */
- smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR,
- BMCR_LOOPBACK | BMCR_FULLDPLX);
+ smsc911x_mii_write(phy_dev->mdio.bus, phy_dev->mdio.addr,
+ MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX);
/* Enable MAC tx/rx, FD */
spin_lock_irqsave(&pdata->mac_lock, flags);
@@ -893,7 +893,7 @@ static int smsc911x_phy_loopbacktest(struct net_device *dev)
spin_unlock_irqrestore(&pdata->mac_lock, flags);
/* Cancel PHY loopback mode */
- smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, 0);
+ smsc911x_mii_write(phy_dev->mdio.bus, phy_dev->mdio.addr, MII_BMCR, 0);
smsc911x_reg_write(pdata, TX_CFG, 0);
smsc911x_reg_write(pdata, RX_CFG, 0);
@@ -1021,7 +1021,7 @@ static int smsc911x_mii_probe(struct net_device *dev)
}
SMSC_TRACE(pdata, probe, "PHY: addr %d, phy_id 0x%08X",
- phydev->addr, phydev->phy_id);
+ phydev->mdio.addr, phydev->phy_id);
ret = phy_connect_direct(dev, phydev, &smsc911x_phy_adjust_link,
pdata->config.phy_interface);
@@ -1031,9 +1031,7 @@ static int smsc911x_mii_probe(struct net_device *dev)
return ret;
}
- netdev_info(dev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
/* mask with MAC supported features */
phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
@@ -1061,7 +1059,7 @@ static int smsc911x_mii_init(struct platform_device *pdev,
struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
- int err = -ENXIO, i;
+ int err = -ENXIO;
pdata->mii_bus = mdiobus_alloc();
if (!pdata->mii_bus) {
@@ -1075,9 +1073,7 @@ static int smsc911x_mii_init(struct platform_device *pdev,
pdata->mii_bus->priv = pdata;
pdata->mii_bus->read = smsc911x_mii_read;
pdata->mii_bus->write = smsc911x_mii_write;
- pdata->mii_bus->irq = pdata->phy_irq;
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- pdata->mii_bus->irq[i] = PHY_POLL;
+ memcpy(pdata->mii_bus->irq, pdata->phy_irq, sizeof(pdata->mii_bus));
pdata->mii_bus->parent = &pdev->dev;
@@ -1992,7 +1988,8 @@ smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
}
for (i = 0; i <= 31; i++)
- data[j++] = smsc911x_mii_read(phy_dev->bus, phy_dev->addr, i);
+ data[j++] = smsc911x_mii_read(phy_dev->mdio.bus,
+ phy_dev->mdio.addr, i);
}
static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c
index 4a90cdae5444..8594b9e8b28b 100644
--- a/drivers/net/ethernet/smsc/smsc9420.c
+++ b/drivers/net/ethernet/smsc/smsc9420.c
@@ -78,7 +78,6 @@ struct smsc9420_pdata {
struct phy_device *phy_dev;
struct mii_bus *mii_bus;
- int phy_irq[PHY_MAX_ADDR];
int last_duplex;
int last_carrier;
};
@@ -316,7 +315,8 @@ smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
return;
for (i = 0; i <= 31; i++)
- data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i);
+ data[j++] = smsc9420_mii_read(phy_dev->mdio.bus,
+ phy_dev->mdio.addr, i);
}
static void smsc9420_eeprom_enable_access(struct smsc9420_pdata *pd)
@@ -1158,16 +1158,13 @@ static int smsc9420_mii_probe(struct net_device *dev)
BUG_ON(pd->phy_dev);
/* Device only supports internal PHY at address 1 */
- if (!pd->mii_bus->phy_map[1]) {
+ phydev = mdiobus_get_phy(pd->mii_bus, 1);
+ if (!phydev) {
netdev_err(dev, "no PHY found at address 1\n");
return -ENODEV;
}
- phydev = pd->mii_bus->phy_map[1];
- netif_info(pd, probe, pd->dev, "PHY addr %d, phy_id 0x%08X\n",
- phydev->addr, phydev->phy_id);
-
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -1175,14 +1172,13 @@ static int smsc9420_mii_probe(struct net_device *dev)
return PTR_ERR(phydev);
}
- netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
-
/* mask with MAC supported features */
phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
SUPPORTED_Asym_Pause);
phydev->advertising = phydev->supported;
+ phy_attached_info(phydev);
+
pd->phy_dev = phydev;
pd->last_duplex = -1;
pd->last_carrier = -1;
@@ -1193,7 +1189,7 @@ static int smsc9420_mii_probe(struct net_device *dev)
static int smsc9420_mii_init(struct net_device *dev)
{
struct smsc9420_pdata *pd = netdev_priv(dev);
- int err = -ENXIO, i;
+ int err = -ENXIO;
pd->mii_bus = mdiobus_alloc();
if (!pd->mii_bus) {
@@ -1206,9 +1202,6 @@ static int smsc9420_mii_init(struct net_device *dev)
pd->mii_bus->priv = pd;
pd->mii_bus->read = smsc9420_mii_read;
pd->mii_bus->write = smsc9420_mii_write;
- pd->mii_bus->irq = pd->phy_irq;
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- pd->mii_bus->irq[i] = PHY_POLL;
/* Mask all PHYs except ID 1 (internal) */
pd->mii_bus->phy_mask = ~(1 << 1);
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 623c6ed8764a..1e19c8fd8b82 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -137,6 +137,31 @@ struct stmmac_extra_stats {
unsigned long pcs_link;
unsigned long pcs_duplex;
unsigned long pcs_speed;
+ /* debug register */
+ unsigned long mtl_tx_status_fifo_full;
+ unsigned long mtl_tx_fifo_not_empty;
+ unsigned long mmtl_fifo_ctrl;
+ unsigned long mtl_tx_fifo_read_ctrl_write;
+ unsigned long mtl_tx_fifo_read_ctrl_wait;
+ unsigned long mtl_tx_fifo_read_ctrl_read;
+ unsigned long mtl_tx_fifo_read_ctrl_idle;
+ unsigned long mac_tx_in_pause;
+ unsigned long mac_tx_frame_ctrl_xfer;
+ unsigned long mac_tx_frame_ctrl_idle;
+ unsigned long mac_tx_frame_ctrl_wait;
+ unsigned long mac_tx_frame_ctrl_pause;
+ unsigned long mac_gmii_tx_proto_engine;
+ unsigned long mtl_rx_fifo_fill_level_full;
+ unsigned long mtl_rx_fifo_fill_above_thresh;
+ unsigned long mtl_rx_fifo_fill_below_thresh;
+ unsigned long mtl_rx_fifo_fill_level_empty;
+ unsigned long mtl_rx_fifo_read_ctrl_flush;
+ unsigned long mtl_rx_fifo_read_ctrl_read_data;
+ unsigned long mtl_rx_fifo_read_ctrl_status;
+ unsigned long mtl_rx_fifo_read_ctrl_idle;
+ unsigned long mtl_rx_fifo_ctrl_active;
+ unsigned long mac_rx_frame_ctrl_fifo;
+ unsigned long mac_gmii_rx_proto_engine;
};
/* CSR Frequency Access Defines*/
@@ -408,12 +433,13 @@ struct stmmac_ops {
void (*set_eee_pls)(struct mac_device_info *hw, int link);
void (*ctrl_ane)(struct mac_device_info *hw, bool restart);
void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv);
+ void (*debug)(void __iomem *ioaddr, struct stmmac_extra_stats *x);
};
/* PTP and HW Timer helpers */
struct stmmac_hwtimestamp {
void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
- void (*config_sub_second_increment) (void __iomem *ioaddr);
+ u32 (*config_sub_second_increment) (void __iomem *ioaddr, u32 clk_rate);
int (*init_systime) (void __iomem *ioaddr, u32 sec, u32 nsec);
int (*config_addend) (void __iomem *ioaddr, u32 addend);
int (*adjust_systime) (void __iomem *ioaddr, u32 sec, u32 nsec,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
index 82de68b1a452..36d3355f2fb0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
@@ -198,19 +198,19 @@ static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed)
return 0;
}
-static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
+static int ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
{
struct device *dev = &gmac->pdev->dev;
gmac->phy_mode = of_get_phy_mode(dev->of_node);
if (gmac->phy_mode < 0) {
dev_err(dev, "missing phy mode property\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) {
dev_err(dev, "missing qcom id property\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
/* The GMACs are called 1 to 4 in the documentation, but to simplify the
@@ -219,13 +219,13 @@ static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
*/
if (gmac->id < 0 || gmac->id > 3) {
dev_err(dev, "invalid gmac id\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
gmac->core_clk = devm_clk_get(dev, "stmmaceth");
if (IS_ERR(gmac->core_clk)) {
dev_err(dev, "missing stmmaceth clk property\n");
- return gmac->core_clk;
+ return PTR_ERR(gmac->core_clk);
}
clk_set_rate(gmac->core_clk, 266000000);
@@ -234,18 +234,16 @@ static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
"qcom,nss-common");
if (IS_ERR(gmac->nss_common)) {
dev_err(dev, "missing nss-common node\n");
- return gmac->nss_common;
+ return PTR_ERR(gmac->nss_common);
}
/* Setup the register map for the qsgmii csr registers */
gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node,
"qcom,qsgmii-csr");
- if (IS_ERR(gmac->qsgmii_csr)) {
+ if (IS_ERR(gmac->qsgmii_csr))
dev_err(dev, "missing qsgmii-csr node\n");
- return gmac->qsgmii_csr;
- }
- return NULL;
+ return PTR_ERR_OR_ZERO(gmac->qsgmii_csr);
}
static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed)
@@ -262,7 +260,7 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct ipq806x_gmac *gmac;
int val;
- void *err;
+ int err;
val = stmmac_get_platform_resources(pdev, &stmmac_res);
if (val)
@@ -279,9 +277,9 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
gmac->pdev = pdev;
err = ipq806x_gmac_of_parse(gmac);
- if (IS_ERR(err)) {
+ if (err) {
dev_err(dev, "device tree parsing error\n");
- return PTR_ERR(err);
+ return err;
}
regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 401383b252a8..f0d797ab74d8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -32,6 +32,7 @@
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
+#define SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000010
#define EMAC_SPLITTER_CTRL_REG 0x0
#define EMAC_SPLITTER_CTRL_SPEED_MASK 0x3
@@ -47,6 +48,7 @@ struct socfpga_dwmac {
struct regmap *sys_mgr_base_addr;
struct reset_control *stmmac_rst;
void __iomem *splitter_base;
+ bool f2h_ptp_ref_clk;
};
static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed)
@@ -116,6 +118,8 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *
return -EINVAL;
}
+ dwmac->f2h_ptp_ref_clk = of_property_read_bool(np, "altr,f2h_ptp_ref_clk");
+
np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0);
if (np_splitter) {
if (of_address_to_resource(np_splitter, 0, &res_splitter)) {
@@ -171,6 +175,11 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
ctrl |= val << reg_shift;
+ if (dwmac->f2h_ptp_ref_clk)
+ ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2);
+ else
+ ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2));
+
regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
return 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index b3fe0575ff6b..8831a053ac13 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -34,6 +34,7 @@
#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */
#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */
#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */
+#define GMAC_DEBUG 0x00000024 /* GMAC debug register */
#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */
#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */
@@ -177,6 +178,47 @@ enum inter_frame_gap {
#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */
+/* DEBUG Register defines */
+/* MTL TxStatus FIFO */
+#define GMAC_DEBUG_TXSTSFSTS BIT(25) /* MTL TxStatus FIFO Full Status */
+#define GMAC_DEBUG_TXFSTS BIT(24) /* MTL Tx FIFO Not Empty Status */
+#define GMAC_DEBUG_TWCSTS BIT(22) /* MTL Tx FIFO Write Controller */
+/* MTL Tx FIFO Read Controller Status */
+#define GMAC_DEBUG_TRCSTS_MASK GENMASK(21, 20)
+#define GMAC_DEBUG_TRCSTS_SHIFT 20
+#define GMAC_DEBUG_TRCSTS_IDLE 0
+#define GMAC_DEBUG_TRCSTS_READ 1
+#define GMAC_DEBUG_TRCSTS_TXW 2
+#define GMAC_DEBUG_TRCSTS_WRITE 3
+#define GMAC_DEBUG_TXPAUSED BIT(19) /* MAC Transmitter in PAUSE */
+/* MAC Transmit Frame Controller Status */
+#define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17)
+#define GMAC_DEBUG_TFCSTS_SHIFT 17
+#define GMAC_DEBUG_TFCSTS_IDLE 0
+#define GMAC_DEBUG_TFCSTS_WAIT 1
+#define GMAC_DEBUG_TFCSTS_GEN_PAUSE 2
+#define GMAC_DEBUG_TFCSTS_XFER 3
+/* MAC GMII or MII Transmit Protocol Engine Status */
+#define GMAC_DEBUG_TPESTS BIT(16)
+#define GMAC_DEBUG_RXFSTS_MASK GENMASK(9, 8) /* MTL Rx FIFO Fill-level */
+#define GMAC_DEBUG_RXFSTS_SHIFT 8
+#define GMAC_DEBUG_RXFSTS_EMPTY 0
+#define GMAC_DEBUG_RXFSTS_BT 1
+#define GMAC_DEBUG_RXFSTS_AT 2
+#define GMAC_DEBUG_RXFSTS_FULL 3
+#define GMAC_DEBUG_RRCSTS_MASK GENMASK(6, 5) /* MTL Rx FIFO Read Controller */
+#define GMAC_DEBUG_RRCSTS_SHIFT 5
+#define GMAC_DEBUG_RRCSTS_IDLE 0
+#define GMAC_DEBUG_RRCSTS_RDATA 1
+#define GMAC_DEBUG_RRCSTS_RSTAT 2
+#define GMAC_DEBUG_RRCSTS_FLUSH 3
+#define GMAC_DEBUG_RWCSTS BIT(4) /* MTL Rx FIFO Write Controller Active */
+/* MAC Receive Frame Controller FIFO Status */
+#define GMAC_DEBUG_RFCFCSTS_MASK GENMASK(2, 1)
+#define GMAC_DEBUG_RFCFCSTS_SHIFT 1
+/* MAC GMII or MII Receive Protocol Engine Status */
+#define GMAC_DEBUG_RPESTS BIT(0)
+
/*--- DMA BLOCK defines ---*/
/* DMA Bus Mode register defines */
#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 371a669d69fd..c2941172f6d1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -397,6 +397,80 @@ static void dwmac1000_get_adv(struct mac_device_info *hw, struct rgmii_adv *adv)
adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;
}
+static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x)
+{
+ u32 value = readl(ioaddr + GMAC_DEBUG);
+
+ if (value & GMAC_DEBUG_TXSTSFSTS)
+ x->mtl_tx_status_fifo_full++;
+ if (value & GMAC_DEBUG_TXFSTS)
+ x->mtl_tx_fifo_not_empty++;
+ if (value & GMAC_DEBUG_TWCSTS)
+ x->mmtl_fifo_ctrl++;
+ if (value & GMAC_DEBUG_TRCSTS_MASK) {
+ u32 trcsts = (value & GMAC_DEBUG_TRCSTS_MASK)
+ >> GMAC_DEBUG_TRCSTS_SHIFT;
+ if (trcsts == GMAC_DEBUG_TRCSTS_WRITE)
+ x->mtl_tx_fifo_read_ctrl_write++;
+ else if (trcsts == GMAC_DEBUG_TRCSTS_TXW)
+ x->mtl_tx_fifo_read_ctrl_wait++;
+ else if (trcsts == GMAC_DEBUG_TRCSTS_READ)
+ x->mtl_tx_fifo_read_ctrl_read++;
+ else
+ x->mtl_tx_fifo_read_ctrl_idle++;
+ }
+ if (value & GMAC_DEBUG_TXPAUSED)
+ x->mac_tx_in_pause++;
+ if (value & GMAC_DEBUG_TFCSTS_MASK) {
+ u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK)
+ >> GMAC_DEBUG_TFCSTS_SHIFT;
+
+ if (tfcsts == GMAC_DEBUG_TFCSTS_XFER)
+ x->mac_tx_frame_ctrl_xfer++;
+ else if (tfcsts == GMAC_DEBUG_TFCSTS_GEN_PAUSE)
+ x->mac_tx_frame_ctrl_pause++;
+ else if (tfcsts == GMAC_DEBUG_TFCSTS_WAIT)
+ x->mac_tx_frame_ctrl_wait++;
+ else
+ x->mac_tx_frame_ctrl_idle++;
+ }
+ if (value & GMAC_DEBUG_TPESTS)
+ x->mac_gmii_tx_proto_engine++;
+ if (value & GMAC_DEBUG_RXFSTS_MASK) {
+ u32 rxfsts = (value & GMAC_DEBUG_RXFSTS_MASK)
+ >> GMAC_DEBUG_RRCSTS_SHIFT;
+
+ if (rxfsts == GMAC_DEBUG_RXFSTS_FULL)
+ x->mtl_rx_fifo_fill_level_full++;
+ else if (rxfsts == GMAC_DEBUG_RXFSTS_AT)
+ x->mtl_rx_fifo_fill_above_thresh++;
+ else if (rxfsts == GMAC_DEBUG_RXFSTS_BT)
+ x->mtl_rx_fifo_fill_below_thresh++;
+ else
+ x->mtl_rx_fifo_fill_level_empty++;
+ }
+ if (value & GMAC_DEBUG_RRCSTS_MASK) {
+ u32 rrcsts = (value & GMAC_DEBUG_RRCSTS_MASK) >>
+ GMAC_DEBUG_RRCSTS_SHIFT;
+
+ if (rrcsts == GMAC_DEBUG_RRCSTS_FLUSH)
+ x->mtl_rx_fifo_read_ctrl_flush++;
+ else if (rrcsts == GMAC_DEBUG_RRCSTS_RSTAT)
+ x->mtl_rx_fifo_read_ctrl_read_data++;
+ else if (rrcsts == GMAC_DEBUG_RRCSTS_RDATA)
+ x->mtl_rx_fifo_read_ctrl_status++;
+ else
+ x->mtl_rx_fifo_read_ctrl_idle++;
+ }
+ if (value & GMAC_DEBUG_RWCSTS)
+ x->mtl_rx_fifo_ctrl_active++;
+ if (value & GMAC_DEBUG_RFCFCSTS_MASK)
+ x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK)
+ >> GMAC_DEBUG_RFCFCSTS_SHIFT;
+ if (value & GMAC_DEBUG_RPESTS)
+ x->mac_gmii_rx_proto_engine++;
+}
+
static const struct stmmac_ops dwmac1000_ops = {
.core_init = dwmac1000_core_init,
.rx_ipc = dwmac1000_rx_ipc_enable,
@@ -413,6 +487,7 @@ static const struct stmmac_ops dwmac1000_ops = {
.set_eee_pls = dwmac1000_set_eee_pls,
.ctrl_ane = dwmac1000_ctrl_ane,
.get_adv = dwmac1000_get_adv,
+ .debug = dwmac1000_debug,
};
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 2e51b816a7e8..4c6486cc80fb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -136,6 +136,31 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(irq_pcs_ane_n),
STMMAC_STAT(irq_pcs_link_n),
STMMAC_STAT(irq_rgmii_n),
+ /* DEBUG */
+ STMMAC_STAT(mtl_tx_status_fifo_full),
+ STMMAC_STAT(mtl_tx_fifo_not_empty),
+ STMMAC_STAT(mmtl_fifo_ctrl),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_write),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_wait),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_read),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_idle),
+ STMMAC_STAT(mac_tx_in_pause),
+ STMMAC_STAT(mac_tx_frame_ctrl_xfer),
+ STMMAC_STAT(mac_tx_frame_ctrl_idle),
+ STMMAC_STAT(mac_tx_frame_ctrl_wait),
+ STMMAC_STAT(mac_tx_frame_ctrl_pause),
+ STMMAC_STAT(mac_gmii_tx_proto_engine),
+ STMMAC_STAT(mtl_rx_fifo_fill_level_full),
+ STMMAC_STAT(mtl_rx_fifo_fill_above_thresh),
+ STMMAC_STAT(mtl_rx_fifo_fill_below_thresh),
+ STMMAC_STAT(mtl_rx_fifo_fill_level_empty),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_flush),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_read_data),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_status),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_idle),
+ STMMAC_STAT(mtl_rx_fifo_ctrl_active),
+ STMMAC_STAT(mac_rx_frame_ctrl_fifo),
+ STMMAC_STAT(mac_gmii_rx_proto_engine),
};
#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
@@ -497,6 +522,11 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
if (val)
priv->xstats.phy_eee_wakeup_error_n = val;
}
+
+ if ((priv->hw->mac->debug) &&
+ (priv->synopsys_id >= DWMAC_CORE_3_50))
+ priv->hw->mac->debug(priv->ioaddr,
+ (void *)&priv->xstats);
}
for (i = 0; i < STMMAC_STATS_LEN; i++) {
char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
index 76ad214b4036..a77f68918010 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
@@ -33,22 +33,25 @@ static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data)
writel(data, ioaddr + PTP_TCR);
}
-static void stmmac_config_sub_second_increment(void __iomem *ioaddr)
+static u32 stmmac_config_sub_second_increment(void __iomem *ioaddr,
+ u32 ptp_clock)
{
u32 value = readl(ioaddr + PTP_TCR);
unsigned long data;
/* Convert the ptp_clock to nano second
- * formula = (1/ptp_clock) * 1000000000
+ * formula = (2/ptp_clock) * 1000000000
* where, ptp_clock = 50MHz.
*/
- data = (1000000000ULL / 50000000);
+ data = (2000000000ULL / ptp_clock);
/* 0.465ns accuracy */
if (!(value & PTP_TCR_TSCTRLSSR))
data = (data * 1000) / 465;
writel(data, ioaddr + PTP_SSIR);
+
+ return data;
}
static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index a5b869eb4678..c21015b68097 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -53,6 +53,7 @@
#include "stmmac.h"
#include <linux/reset.h>
#include <linux/of_mdio.h>
+#include "dwmac1000.h"
#define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x)
@@ -185,7 +186,7 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
priv->clk_csr = STMMAC_CSR_100_150M;
else if ((clk_rate >= CSR_F_150M) && (clk_rate < CSR_F_250M))
priv->clk_csr = STMMAC_CSR_150_250M;
- else if ((clk_rate >= CSR_F_250M) && (clk_rate <= CSR_F_300M))
+ else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M))
priv->clk_csr = STMMAC_CSR_250_300M;
}
}
@@ -435,6 +436,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
u32 ts_master_en = 0;
u32 ts_event_en = 0;
u32 value = 0;
+ u32 sec_inc;
if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
netdev_alert(priv->dev, "No support for HW time stamping\n");
@@ -598,24 +600,19 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
tstamp_all | ptp_v2 | ptp_over_ethernet |
ptp_over_ipv6_udp | ptp_over_ipv4_udp | ts_event_en |
ts_master_en | snap_type_sel);
-
priv->hw->ptp->config_hw_tstamping(priv->ioaddr, value);
/* program Sub Second Increment reg */
- priv->hw->ptp->config_sub_second_increment(priv->ioaddr);
+ sec_inc = priv->hw->ptp->config_sub_second_increment(
+ priv->ioaddr, priv->clk_ptp_rate);
+ temp = div_u64(1000000000ULL, sec_inc);
/* calculate default added value:
* formula is :
* addend = (2^32)/freq_div_ratio;
- * where, freq_div_ratio = clk_ptp_ref_i/50MHz
- * hence, addend = ((2^32) * 50MHz)/clk_ptp_ref_i;
- * NOTE: clk_ptp_ref_i should be >= 50MHz to
- * achieve 20ns accuracy.
- *
- * 2^x * y == (y << x), hence
- * 2^32 * 50000000 ==> (50000000 << 32)
+ * where, freq_div_ratio = 1e9ns/sec_inc
*/
- temp = (u64) (50000000ULL << 32);
+ temp = (u64)(temp << 32);
priv->default_addend = div_u64(temp, priv->clk_ptp_rate);
priv->hw->ptp->config_addend(priv->ioaddr,
priv->default_addend);
@@ -2402,7 +2399,7 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
features &= ~NETIF_F_RXCSUM;
if (!priv->plat->tx_coe)
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
/* Some GMAC devices have a bugged Jumbo frame support that
* needs to have the Tx COE disabled for oversized frames
@@ -2410,7 +2407,7 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
* the TX csum insertionin the TDES and not use SF.
*/
if (priv->plat->bugged_jumbo && (dev->mtu > ETH_DATA_LEN))
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
return features;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index bba670c42e37..0faf16336035 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -29,7 +29,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
-
+#include <linux/of_mdio.h>
#include <asm/io.h>
#include "stmmac.h"
@@ -196,25 +196,37 @@ int stmmac_mdio_register(struct net_device *ndev)
{
int err = 0;
struct mii_bus *new_bus;
- int *irqlist;
struct stmmac_priv *priv = netdev_priv(ndev);
struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
int addr, found;
+ struct device_node *mdio_node = NULL;
+ struct device_node *child_node = NULL;
if (!mdio_bus_data)
return 0;
+ if (IS_ENABLED(CONFIG_OF)) {
+ for_each_child_of_node(priv->device->of_node, child_node) {
+ if (of_device_is_compatible(child_node,
+ "snps,dwmac-mdio")) {
+ mdio_node = child_node;
+ break;
+ }
+ }
+
+ if (mdio_node) {
+ netdev_dbg(ndev, "FOUND MDIO subnode\n");
+ } else {
+ netdev_warn(ndev, "No MDIO subnode found\n");
+ }
+ }
+
new_bus = mdiobus_alloc();
if (new_bus == NULL)
return -ENOMEM;
- if (mdio_bus_data->irqs) {
- irqlist = mdio_bus_data->irqs;
- } else {
- for (addr = 0; addr < PHY_MAX_ADDR; addr++)
- priv->mii_irq[addr] = PHY_POLL;
- irqlist = priv->mii_irq;
- }
+ if (mdio_bus_data->irqs)
+ memcpy(new_bus->irq, mdio_bus_data, sizeof(new_bus->irq));
#ifdef CONFIG_OF
if (priv->device->of_node)
@@ -228,10 +240,13 @@ int stmmac_mdio_register(struct net_device *ndev)
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x",
new_bus->name, priv->plat->bus_id);
new_bus->priv = ndev;
- new_bus->irq = irqlist;
new_bus->phy_mask = mdio_bus_data->phy_mask;
new_bus->parent = priv->device;
- err = mdiobus_register(new_bus);
+
+ if (mdio_node)
+ err = of_mdiobus_register(new_bus, mdio_node);
+ else
+ err = mdiobus_register(new_bus);
if (err != 0) {
pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
goto bus_register_fail;
@@ -239,7 +254,7 @@ int stmmac_mdio_register(struct net_device *ndev)
found = 0;
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- struct phy_device *phydev = new_bus->phy_map[addr];
+ struct phy_device *phydev = mdiobus_get_phy(new_bus, addr);
if (phydev) {
int act = 0;
char irq_num[4];
@@ -251,7 +266,8 @@ int stmmac_mdio_register(struct net_device *ndev)
*/
if ((mdio_bus_data->irqs == NULL) &&
(mdio_bus_data->probed_phy_irq > 0)) {
- irqlist[addr] = mdio_bus_data->probed_phy_irq;
+ new_bus->irq[addr] =
+ mdio_bus_data->probed_phy_irq;
phydev->irq = mdio_bus_data->probed_phy_irq;
}
@@ -278,13 +294,13 @@ int stmmac_mdio_register(struct net_device *ndev)
}
pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n",
ndev->name, phydev->phy_id, addr,
- irq_str, dev_name(&phydev->dev),
+ irq_str, phydev_name(phydev),
act ? " active" : "");
found = 1;
}
}
- if (!found) {
+ if (!found && !mdio_node) {
pr_warn("%s: No PHY found\n", ndev->name);
mdiobus_unregister(new_bus);
mdiobus_free(new_bus);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index d02691ba3d7f..6a52fa18cbf2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -146,7 +146,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
- if (plat->phy_node || plat->phy_bus_name)
+ if ((plat->phy_node && !of_phy_is_fixed_link(np)) || plat->phy_bus_name)
plat->mdio_bus_data = NULL;
else
plat->mdio_bus_data =
diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c
index 9066d7a8483c..70814b7386b3 100644
--- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c
+++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c
@@ -972,9 +972,7 @@ static int dwceqos_mii_probe(struct net_device *ndev)
}
if (netif_msg_probe(lp))
- netdev_dbg(lp->ndev,
- "phydev %p, phydev->phy_id 0xa%x, phydev->addr 0x%x\n",
- phydev, phydev->phy_id, phydev->addr);
+ phy_attached_info(phydev);
phydev->supported &= PHY_GBIT_FEATURES;
@@ -983,14 +981,6 @@ static int dwceqos_mii_probe(struct net_device *ndev)
lp->duplex = DUPLEX_UNKNOWN;
lp->phy_dev = phydev;
- if (netif_msg_probe(lp)) {
- netdev_dbg(lp->ndev, "phy_addr 0x%x, phy_id 0x%08x\n",
- lp->phy_dev->addr, lp->phy_dev->phy_id);
-
- netdev_dbg(lp->ndev, "attach [%s] phy driver\n",
- lp->phy_dev->drv->name);
- }
-
return 0;
}
@@ -1230,7 +1220,7 @@ static void dwceqos_enable_mmc_interrupt(struct net_local *lp)
static int dwceqos_mii_init(struct net_local *lp)
{
- int ret = -ENXIO, i;
+ int ret = -ENXIO;
struct resource res;
struct device_node *mdionode;
@@ -1251,24 +1241,14 @@ static int dwceqos_mii_init(struct net_local *lp)
lp->mii_bus->priv = lp;
lp->mii_bus->parent = &lp->ndev->dev;
- lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!lp->mii_bus->irq) {
- ret = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- lp->mii_bus->irq[i] = PHY_POLL;
of_address_to_resource(lp->pdev->dev.of_node, 0, &res);
snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%.8llx",
(unsigned long long)res.start);
if (of_mdiobus_register(lp->mii_bus, mdionode))
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
return 0;
-err_out_free_mdio_irq:
- kfree(lp->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(lp->mii_bus);
err_out:
@@ -2107,7 +2087,7 @@ static int dwceqos_tx_frags(struct sk_buff *skb, struct net_local *lp,
dd = &lp->tx_descs[lp->tx_next];
/* Set DMA Descriptor fields */
- dd->des0 = dma_handle;
+ dd->des0 = dma_handle + consumed_size;
dd->des1 = 0;
dd->des2 = dma_size;
@@ -2987,7 +2967,6 @@ static int dwceqos_remove(struct platform_device *pdev)
if (lp->phy_dev)
phy_disconnect(lp->phy_dev);
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
unregister_netdev(ndev);
diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c
index 77d26fe286c0..7eef45e6d70a 100644
--- a/drivers/net/ethernet/ti/cpmac.c
+++ b/drivers/net/ethernet/ti/cpmac.c
@@ -316,8 +316,6 @@ static int cpmac_mdio_reset(struct mii_bus *bus)
return 0;
}
-static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
-
static struct mii_bus *cpmac_mii;
static void cpmac_set_multicast_list(struct net_device *dev)
@@ -1118,7 +1116,7 @@ static int cpmac_probe(struct platform_device *pdev)
for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
if (!(pdata->phy_mask & (1 << phy_id)))
continue;
- if (!cpmac_mii->phy_map[phy_id])
+ if (!mdiobus_get_phy(cpmac_mii, phy_id))
continue;
strncpy(mdio_bus_id, cpmac_mii->id, MII_BUS_ID_SIZE);
break;
@@ -1226,7 +1224,6 @@ int cpmac_init(void)
cpmac_mii->read = cpmac_mdio_read;
cpmac_mii->write = cpmac_mdio_write;
cpmac_mii->reset = cpmac_mdio_reset;
- cpmac_mii->irq = mii_irqs;
cpmac_mii->priv = ioremap(AR7_REGS_MDIO, 256);
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index fc958067d10a..42fdfd4d9d4f 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -1159,8 +1159,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
slave->data->phy_id, slave->slave_num);
slave->phy = NULL;
} else {
- dev_info(priv->dev, "phy found : id is : 0x%x\n",
- slave->phy->phy_id);
+ phy_attached_info(slave->phy);
+
phy_start(slave->phy);
/* Configure GMII_SEL register */
@@ -2050,7 +2050,8 @@ static int cpsw_probe_dt(struct cpsw_priv *priv,
if (!phy_dev)
return -ENODEV;
snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
- PHY_ID_FMT, phy_dev->bus->id, phy_dev->addr);
+ PHY_ID_FMT, phy_dev->mdio.bus->id,
+ phy_dev->mdio.addr);
} else if (parp) {
u32 phyid;
struct device_node *mdio_node;
@@ -2482,7 +2483,7 @@ static int cpsw_probe(struct platform_device *pdev)
ndev->netdev_ops = &cpsw_netdev_ops;
ndev->ethtool_ops = &cpsw_ethtool_ops;
netif_napi_add(ndev, &priv->napi_rx, cpsw_rx_poll, CPSW_POLL_WEIGHT);
- netif_napi_add(ndev, &priv->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT);
+ netif_tx_napi_add(ndev, &priv->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT);
/* register the network device */
SET_NETDEV_DEV(ndev, &pdev->dev);
diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
index 33bd3b902304..5d9abedd6b75 100644
--- a/drivers/net/ethernet/ti/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -1644,10 +1644,7 @@ static int emac_dev_open(struct net_device *ndev)
priv->speed = 0;
priv->duplex = ~0;
- dev_info(emac_dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, id=%x)\n",
- priv->phydev->drv->name, dev_name(&priv->phydev->dev),
- priv->phydev->phy_id);
+ phy_attached_info(priv->phydev);
}
if (!priv->phydev) {
diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c
index c00084d689f3..4e7c9b9b042a 100644
--- a/drivers/net/ethernet/ti/davinci_mdio.c
+++ b/drivers/net/ethernet/ti/davinci_mdio.c
@@ -393,10 +393,10 @@ static int davinci_mdio_probe(struct platform_device *pdev)
/* scan and dump the bus */
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- phy = data->bus->phy_map[addr];
+ phy = mdiobus_get_phy(data->bus, addr);
if (phy) {
dev_info(dev, "phy[%d]: device %s, driver %s\n",
- phy->addr, dev_name(&phy->dev),
+ phy->mdio.addr, phydev_name(phy),
phy->drv ? phy->drv->name : "unknown");
}
}
diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h
index bb1bb72121c0..17a26a429b71 100644
--- a/drivers/net/ethernet/ti/netcp.h
+++ b/drivers/net/ethernet/ti/netcp.h
@@ -113,7 +113,7 @@ struct netcp_intf {
#define NETCP_PSDATA_LEN KNAV_DMA_NUM_PS_WORDS
struct netcp_packet {
struct sk_buff *skb;
- u32 *epib;
+ __le32 *epib;
u32 *psdata;
unsigned int psdata_len;
struct netcp_intf *netcp;
diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c
index 37b9b39192ec..c61d66d38634 100644
--- a/drivers/net/ethernet/ti/netcp_core.c
+++ b/drivers/net/ethernet/ti/netcp_core.c
@@ -109,69 +109,80 @@ module_param(netcp_debug_level, int, 0);
MODULE_PARM_DESC(netcp_debug_level, "Netcp debug level (NETIF_MSG bits) (0=none,...,16=all)");
/* Helper functions - Get/Set */
-static void get_pkt_info(u32 *buff, u32 *buff_len, u32 *ndesc,
+static void get_pkt_info(dma_addr_t *buff, u32 *buff_len, dma_addr_t *ndesc,
struct knav_dma_desc *desc)
{
- *buff_len = desc->buff_len;
- *buff = desc->buff;
- *ndesc = desc->next_desc;
+ *buff_len = le32_to_cpu(desc->buff_len);
+ *buff = le32_to_cpu(desc->buff);
+ *ndesc = le32_to_cpu(desc->next_desc);
}
-static void get_pad_info(u32 *pad0, u32 *pad1, struct knav_dma_desc *desc)
+static void get_pad_info(u32 *pad0, u32 *pad1, u32 *pad2, struct knav_dma_desc *desc)
{
- *pad0 = desc->pad[0];
- *pad1 = desc->pad[1];
+ *pad0 = le32_to_cpu(desc->pad[0]);
+ *pad1 = le32_to_cpu(desc->pad[1]);
+ *pad2 = le32_to_cpu(desc->pad[2]);
}
-static void get_org_pkt_info(u32 *buff, u32 *buff_len,
+static void get_pad_ptr(void **padptr, struct knav_dma_desc *desc)
+{
+ u64 pad64;
+
+ pad64 = le32_to_cpu(desc->pad[0]) +
+ ((u64)le32_to_cpu(desc->pad[1]) << 32);
+ *padptr = (void *)(uintptr_t)pad64;
+}
+
+static void get_org_pkt_info(dma_addr_t *buff, u32 *buff_len,
struct knav_dma_desc *desc)
{
- *buff = desc->orig_buff;
- *buff_len = desc->orig_len;
+ *buff = le32_to_cpu(desc->orig_buff);
+ *buff_len = le32_to_cpu(desc->orig_len);
}
-static void get_words(u32 *words, int num_words, u32 *desc)
+static void get_words(dma_addr_t *words, int num_words, __le32 *desc)
{
int i;
for (i = 0; i < num_words; i++)
- words[i] = desc[i];
+ words[i] = le32_to_cpu(desc[i]);
}
-static void set_pkt_info(u32 buff, u32 buff_len, u32 ndesc,
+static void set_pkt_info(dma_addr_t buff, u32 buff_len, u32 ndesc,
struct knav_dma_desc *desc)
{
- desc->buff_len = buff_len;
- desc->buff = buff;
- desc->next_desc = ndesc;
+ desc->buff_len = cpu_to_le32(buff_len);
+ desc->buff = cpu_to_le32(buff);
+ desc->next_desc = cpu_to_le32(ndesc);
}
static void set_desc_info(u32 desc_info, u32 pkt_info,
struct knav_dma_desc *desc)
{
- desc->desc_info = desc_info;
- desc->packet_info = pkt_info;
+ desc->desc_info = cpu_to_le32(desc_info);
+ desc->packet_info = cpu_to_le32(pkt_info);
}
-static void set_pad_info(u32 pad0, u32 pad1, struct knav_dma_desc *desc)
+static void set_pad_info(u32 pad0, u32 pad1, u32 pad2, struct knav_dma_desc *desc)
{
- desc->pad[0] = pad0;
- desc->pad[1] = pad1;
+ desc->pad[0] = cpu_to_le32(pad0);
+ desc->pad[1] = cpu_to_le32(pad1);
+ desc->pad[2] = cpu_to_le32(pad1);
}
-static void set_org_pkt_info(u32 buff, u32 buff_len,
+static void set_org_pkt_info(dma_addr_t buff, u32 buff_len,
struct knav_dma_desc *desc)
{
- desc->orig_buff = buff;
- desc->orig_len = buff_len;
+ desc->orig_buff = cpu_to_le32(buff);
+ desc->orig_len = cpu_to_le32(buff_len);
}
-static void set_words(u32 *words, int num_words, u32 *desc)
+static void set_words(u32 *words, int num_words, __le32 *desc)
{
int i;
for (i = 0; i < num_words; i++)
- desc[i] = words[i];
+ desc[i] = cpu_to_le32(words[i]);
}
/* Read the e-fuse value as 32 bit values to be endian independent */
@@ -570,6 +581,7 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp,
dma_addr_t dma_desc, dma_buf;
unsigned int buf_len, dma_sz = sizeof(*ndesc);
void *buf_ptr;
+ u32 pad[2];
u32 tmp;
get_words(&dma_desc, 1, &desc->next_desc);
@@ -581,13 +593,15 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp,
break;
}
get_pkt_info(&dma_buf, &tmp, &dma_desc, ndesc);
- get_pad_info((u32 *)&buf_ptr, &tmp, ndesc);
+ get_pad_ptr(&buf_ptr, ndesc);
dma_unmap_page(netcp->dev, dma_buf, PAGE_SIZE, DMA_FROM_DEVICE);
__free_page(buf_ptr);
knav_pool_desc_put(netcp->rx_pool, desc);
}
- get_pad_info((u32 *)&buf_ptr, &buf_len, desc);
+ get_pad_info(&pad[0], &pad[1], &buf_len, desc);
+ buf_ptr = (void *)(uintptr_t)(pad[0] + ((u64)pad[1] << 32));
+
if (buf_ptr)
netcp_frag_free(buf_len <= PAGE_SIZE, buf_ptr);
knav_pool_desc_put(netcp->rx_pool, desc);
@@ -625,8 +639,8 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
dma_addr_t dma_desc, dma_buff;
struct netcp_packet p_info;
struct sk_buff *skb;
+ u32 pad[2];
void *org_buf_ptr;
- u32 tmp;
dma_desc = knav_queue_pop(netcp->rx_queue, &dma_sz);
if (!dma_desc)
@@ -639,7 +653,8 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
}
get_pkt_info(&dma_buff, &buf_len, &dma_desc, desc);
- get_pad_info((u32 *)&org_buf_ptr, &org_buf_len, desc);
+ get_pad_info(&pad[0], &pad[1], &org_buf_len, desc);
+ org_buf_ptr = (void *)(uintptr_t)(pad[0] + ((u64)pad[1] << 32));
if (unlikely(!org_buf_ptr)) {
dev_err(netcp->ndev_dev, "NULL bufptr in desc\n");
@@ -664,6 +679,7 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
/* Fill in the page fragment list */
while (dma_desc) {
struct page *page;
+ void *ptr;
ndesc = knav_pool_desc_unmap(netcp->rx_pool, dma_desc, dma_sz);
if (unlikely(!ndesc)) {
@@ -672,14 +688,15 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
}
get_pkt_info(&dma_buff, &buf_len, &dma_desc, ndesc);
- get_pad_info((u32 *)&page, &tmp, ndesc);
+ get_pad_ptr(&ptr, ndesc);
+ page = ptr;
if (likely(dma_buff && buf_len && page)) {
dma_unmap_page(netcp->dev, dma_buff, PAGE_SIZE,
DMA_FROM_DEVICE);
} else {
- dev_err(netcp->ndev_dev, "Bad Rx desc dma_buff(%p), len(%d), page(%p)\n",
- (void *)dma_buff, buf_len, page);
+ dev_err(netcp->ndev_dev, "Bad Rx desc dma_buff(%pad), len(%d), page(%p)\n",
+ &dma_buff, buf_len, page);
goto free_desc;
}
@@ -750,7 +767,6 @@ static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq)
unsigned int buf_len, dma_sz;
dma_addr_t dma;
void *buf_ptr;
- u32 tmp;
/* Allocate descriptor */
while ((dma = knav_queue_pop(netcp->rx_fdq[fdq], &dma_sz))) {
@@ -761,7 +777,7 @@ static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq)
}
get_org_pkt_info(&dma, &buf_len, desc);
- get_pad_info((u32 *)&buf_ptr, &tmp, desc);
+ get_pad_ptr(&buf_ptr, desc);
if (unlikely(!dma)) {
dev_err(netcp->ndev_dev, "NULL orig_buff in desc\n");
@@ -813,7 +829,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
struct page *page;
dma_addr_t dma;
void *bufptr;
- u32 pad[2];
+ u32 pad[3];
/* Allocate descriptor */
hwdesc = knav_pool_desc_get(netcp->rx_pool);
@@ -830,7 +846,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
bufptr = netdev_alloc_frag(primary_buf_len);
- pad[1] = primary_buf_len;
+ pad[2] = primary_buf_len;
if (unlikely(!bufptr)) {
dev_warn_ratelimited(netcp->ndev_dev,
@@ -842,7 +858,8 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
if (unlikely(dma_mapping_error(netcp->dev, dma)))
goto fail;
- pad[0] = (u32)bufptr;
+ pad[0] = lower_32_bits((uintptr_t)bufptr);
+ pad[1] = upper_32_bits((uintptr_t)bufptr);
} else {
/* Allocate a secondary receive queue entry */
@@ -853,8 +870,9 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
}
buf_len = PAGE_SIZE;
dma = dma_map_page(netcp->dev, page, 0, buf_len, DMA_TO_DEVICE);
- pad[0] = (u32)page;
- pad[1] = 0;
+ pad[0] = lower_32_bits(dma);
+ pad[1] = upper_32_bits(dma);
+ pad[2] = 0;
}
desc_info = KNAV_DMA_DESC_PS_INFO_IN_DESC;
@@ -864,7 +882,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
pkt_info |= (netcp->rx_queue_id & KNAV_DMA_DESC_RETQ_MASK) <<
KNAV_DMA_DESC_RETQ_SHIFT;
set_org_pkt_info(dma, buf_len, hwdesc);
- set_pad_info(pad[0], pad[1], hwdesc);
+ set_pad_info(pad[0], pad[1], pad[2], hwdesc);
set_desc_info(desc_info, pkt_info, hwdesc);
/* Push to FDQs */
@@ -935,8 +953,8 @@ static void netcp_free_tx_desc_chain(struct netcp_intf *netcp,
dma_unmap_single(netcp->dev, dma_buf, buf_len,
DMA_TO_DEVICE);
else
- dev_warn(netcp->ndev_dev, "bad Tx desc buf(%p), len(%d)\n",
- (void *)dma_buf, buf_len);
+ dev_warn(netcp->ndev_dev, "bad Tx desc buf(%pad), len(%d)\n",
+ &dma_buf, buf_len);
knav_pool_desc_put(netcp->tx_pool, ndesc);
ndesc = NULL;
@@ -953,11 +971,11 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp,
unsigned int budget)
{
struct knav_dma_desc *desc;
+ void *ptr;
struct sk_buff *skb;
unsigned int dma_sz;
dma_addr_t dma;
int pkts = 0;
- u32 tmp;
while (budget--) {
dma = knav_queue_pop(netcp->tx_compl_q, &dma_sz);
@@ -970,7 +988,8 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp,
continue;
}
- get_pad_info((u32 *)&skb, &tmp, desc);
+ get_pad_ptr(&ptr, desc);
+ skb = ptr;
netcp_free_tx_desc_chain(netcp, desc, dma_sz);
if (!skb) {
dev_err(netcp->ndev_dev, "No skb in Tx desc\n");
@@ -1059,6 +1078,7 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp)
u32 page_offset = frag->page_offset;
u32 buf_len = skb_frag_size(frag);
dma_addr_t desc_dma;
+ u32 desc_dma_32;
u32 pkt_info;
dma_addr = dma_map_page(dev, page, page_offset, buf_len,
@@ -1075,13 +1095,13 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp)
goto free_descs;
}
- desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool,
- (void *)ndesc);
+ desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool, ndesc);
pkt_info =
(netcp->tx_compl_qid & KNAV_DMA_DESC_RETQ_MASK) <<
KNAV_DMA_DESC_RETQ_SHIFT;
set_pkt_info(dma_addr, buf_len, 0, ndesc);
- set_words(&desc_dma, 1, &pdesc->next_desc);
+ desc_dma_32 = (u32)desc_dma;
+ set_words(&desc_dma_32, 1, &pdesc->next_desc);
pkt_len += buf_len;
if (pdesc != desc)
knav_pool_desc_map(netcp->tx_pool, pdesc,
@@ -1129,8 +1149,8 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp,
p_info.ts_context = NULL;
p_info.txtstamp_complete = NULL;
p_info.epib = desc->epib;
- p_info.psdata = desc->psdata;
- memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(u32));
+ p_info.psdata = (u32 __force *)desc->psdata;
+ memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(__le32));
/* Find out where to inject the packet for transmission */
list_for_each_entry(tx_hook, &netcp->txhook_list_head, list) {
@@ -1154,11 +1174,12 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp,
/* update descriptor */
if (p_info.psdata_len) {
- u32 *psdata = p_info.psdata;
+ /* psdata points to both native-endian and device-endian data */
+ __le32 *psdata = (void __force *)p_info.psdata;
memmove(p_info.psdata, p_info.psdata + p_info.psdata_len,
p_info.psdata_len);
- set_words(psdata, p_info.psdata_len, psdata);
+ set_words(p_info.psdata, p_info.psdata_len, psdata);
tmp |= (p_info.psdata_len & KNAV_DMA_DESC_PSLEN_MASK) <<
KNAV_DMA_DESC_PSLEN_SHIFT;
}
@@ -1173,11 +1194,14 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp,
}
set_words(&tmp, 1, &desc->packet_info);
- set_words((u32 *)&skb, 1, &desc->pad[0]);
+ tmp = lower_32_bits((uintptr_t)&skb);
+ set_words(&tmp, 1, &desc->pad[0]);
+ tmp = upper_32_bits((uintptr_t)&skb);
+ set_words(&tmp, 1, &desc->pad[1]);
if (tx_pipe->flags & SWITCH_TO_PORT_IN_TAGINFO) {
tmp = tx_pipe->switch_to_port;
- set_words((u32 *)&tmp, 1, &desc->tag_info);
+ set_words(&tmp, 1, &desc->tag_info);
}
/* submit packet descriptor */
@@ -1990,7 +2014,7 @@ static int netcp_create_interface(struct netcp_device *netcp_device,
/* NAPI register */
netif_napi_add(ndev, &netcp->rx_napi, netcp_rx_poll, NETCP_NAPI_WEIGHT);
- netif_napi_add(ndev, &netcp->tx_napi, netcp_tx_poll, NETCP_NAPI_WEIGHT);
+ netif_tx_napi_add(ndev, &netcp->tx_napi, netcp_tx_poll, NETCP_NAPI_WEIGHT);
/* Register the network device */
ndev->dev_id = 0;
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c
index 4e70e7586a09..d543298d6750 100644
--- a/drivers/net/ethernet/ti/netcp_ethss.c
+++ b/drivers/net/ethernet/ti/netcp_ethss.c
@@ -2178,7 +2178,7 @@ static int gbe_slave_open(struct gbe_intf *gbe_intf)
return -ENODEV;
}
dev_dbg(priv->dev, "phy found: id is: 0x%s\n",
- dev_name(&slave->phy->dev));
+ phydev_name(slave->phy));
phy_start(slave->phy);
phy_read_status(slave->phy);
}
@@ -2681,7 +2681,7 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev,
slave->phy = NULL;
} else {
dev_dbg(dev, "phy found: id is: 0x%s\n",
- dev_name(&slave->phy->dev));
+ phydev_name(slave->phy));
phy_start(slave->phy);
phy_read_status(slave->phy);
}
diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c
index 6f0a4495c7f3..298e059d0498 100644
--- a/drivers/net/ethernet/tile/tilepro.c
+++ b/drivers/net/ethernet/tile/tilepro.c
@@ -1349,8 +1349,7 @@ static int tile_net_open_inner(struct net_device *dev)
*/
static void tile_net_open_retry(struct work_struct *w)
{
- struct delayed_work *dw =
- container_of(w, struct delayed_work, work);
+ struct delayed_work *dw = to_delayed_work(w);
struct tile_net_priv *priv =
container_of(dw, struct tile_net_priv, retry_work);
diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c
index 45ac38d29ed8..54874783476a 100644
--- a/drivers/net/ethernet/toshiba/tc35815.c
+++ b/drivers/net/ethernet/toshiba/tc35815.c
@@ -608,40 +608,25 @@ static void tc_handle_link_change(struct net_device *dev)
static int tc_mii_probe(struct net_device *dev)
{
struct tc35815_local *lp = netdev_priv(dev);
- struct phy_device *phydev = NULL;
- int phy_addr;
+ struct phy_device *phydev;
u32 dropmask;
- /* find the first phy */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- if (lp->mii_bus->phy_map[phy_addr]) {
- if (phydev) {
- printk(KERN_ERR "%s: multiple PHYs found\n",
- dev->name);
- return -EINVAL;
- }
- phydev = lp->mii_bus->phy_map[phy_addr];
- break;
- }
- }
-
+ phydev = phy_find_first(lp->mii_bus);
if (!phydev) {
printk(KERN_ERR "%s: no PHY found\n", dev->name);
return -ENODEV;
}
/* attach the mac to the phy */
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&tc_handle_link_change,
lp->chiptype == TC35815_TX4939 ? PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
return PTR_ERR(phydev);
}
- printk(KERN_INFO "%s: attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, id=%x)\n",
- dev->name, phydev->drv->name, dev_name(&phydev->dev),
- phydev->phy_id);
+
+ phy_attached_info(phydev);
/* mask with MAC supported features */
phydev->supported &= PHY_BASIC_FEATURES;
@@ -669,7 +654,6 @@ static int tc_mii_init(struct net_device *dev)
{
struct tc35815_local *lp = netdev_priv(dev);
int err;
- int i;
lp->mii_bus = mdiobus_alloc();
if (lp->mii_bus == NULL) {
@@ -684,18 +668,9 @@ static int tc_mii_init(struct net_device *dev)
(lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn);
lp->mii_bus->priv = dev;
lp->mii_bus->parent = &lp->pci_dev->dev;
- lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!lp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mii_bus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- lp->mii_bus->irq[i] = PHY_POLL;
-
err = mdiobus_register(lp->mii_bus);
if (err)
- goto err_out_free_mdio_irq;
+ goto err_out_free_mii_bus;
err = tc_mii_probe(dev);
if (err)
goto err_out_unregister_bus;
@@ -703,8 +678,6 @@ static int tc_mii_init(struct net_device *dev)
err_out_unregister_bus:
mdiobus_unregister(lp->mii_bus);
-err_out_free_mdio_irq:
- kfree(lp->mii_bus->irq);
err_out_free_mii_bus:
mdiobus_free(lp->mii_bus);
err_out:
@@ -882,7 +855,6 @@ static void tc35815_remove_one(struct pci_dev *pdev)
phy_disconnect(lp->phy_dev);
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
unregister_netdev(dev);
free_netdev(dev);
diff --git a/drivers/net/ethernet/xilinx/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h
index 522abe2ff25a..902457e43628 100644
--- a/drivers/net/ethernet/xilinx/ll_temac.h
+++ b/drivers/net/ethernet/xilinx/ll_temac.h
@@ -337,7 +337,6 @@ struct temac_local {
/* MDIO bus data */
struct mii_bus *mii_bus; /* MII bus reference */
- int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */
/* IO registers, dma functions and IRQs */
void __iomem *regs;
diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c
index 415de1eaf641..7714aff78b7d 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c
@@ -92,7 +92,6 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
bus->read = temac_mdio_read;
bus->write = temac_mdio_write;
bus->parent = lp->dev;
- bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
lp->mii_bus = bus;
@@ -114,7 +113,6 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
void temac_mdio_teardown(struct temac_local *lp)
{
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
}
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 7cb9abac95c8..9ead4e269409 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -385,7 +385,6 @@ struct axidma_bd {
* @phy_dev: Pointer to PHY device structure attached to the axienet_local
* @phy_node: Pointer to device node structure
* @mii_bus: Pointer to MII bus structure
- * @mdio_irqs: IRQs table for MDIO bus required in mii_bus structure
* @regs: Base address for the axienet_local device address space
* @dma_regs: Base address for the axidma device address space
* @dma_err_tasklet: Tasklet structure to process Axi DMA errors
@@ -426,7 +425,6 @@ struct axienet_local {
/* MDIO bus data */
struct mii_bus *mii_bus; /* MII bus reference */
- int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */
/* IO registers, dma functions and IRQs */
void __iomem *regs;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index 507bbb0355c2..63307ea97846 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -212,7 +212,6 @@ issue:
bus->read = axienet_mdio_read;
bus->write = axienet_mdio_write;
bus->parent = lp->dev;
- bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
lp->mii_bus = bus;
ret = of_mdiobus_register(bus, np1);
@@ -232,7 +231,6 @@ issue:
void axienet_mdio_teardown(struct axienet_local *lp)
{
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
}
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index cf468c87ce57..e324b3092380 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -114,7 +114,6 @@
* @phy_dev: pointer to the PHY device
* @phy_node: pointer to the PHY device node
* @mii_bus: pointer to the MII bus
- * @mdio_irqs: IRQs table for MDIO bus
* @last_link: last link status
* @has_mdio: indicates whether MDIO is included in the HW
*/
@@ -135,7 +134,6 @@ struct net_local {
struct device_node *phy_node;
struct mii_bus *mii_bus;
- int mdio_irqs[PHY_MAX_ADDR];
int last_link;
bool has_mdio;
@@ -829,7 +827,7 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
dev_info(dev,
"MDIO of the phy is not registered yet\n");
else
- put_device(&phydev->dev);
+ put_device(&phydev->mdio.dev);
return 0;
}
@@ -852,7 +850,6 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
bus->read = xemaclite_mdio_read;
bus->write = xemaclite_mdio_write;
bus->parent = dev;
- bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
lp->mii_bus = bus;
@@ -1196,7 +1193,6 @@ static int xemaclite_of_remove(struct platform_device *of_dev)
/* Un-register the mii_bus, if configured */
if (lp->has_mdio) {
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
}
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 58efdec12f30..7456569f53c1 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -71,8 +71,14 @@ struct geneve_dev {
__be16 dst_port;
bool collect_md;
struct gro_cells gro_cells;
+ u32 flags;
};
+/* Geneve device flags */
+#define GENEVE_F_UDP_CSUM BIT(0)
+#define GENEVE_F_UDP_ZERO_CSUM6_TX BIT(1)
+#define GENEVE_F_UDP_ZERO_CSUM6_RX BIT(2)
+
struct geneve_sock {
bool collect_md;
struct list_head list;
@@ -81,6 +87,7 @@ struct geneve_sock {
int refcnt;
struct udp_offload udp_offloads;
struct hlist_head vni_list[VNI_HASH_SIZE];
+ u32 flags;
};
static inline __u32 geneve_net_vni_hash(u8 vni[3])
@@ -343,7 +350,7 @@ error:
}
static struct socket *geneve_create_sock(struct net *net, bool ipv6,
- __be16 port)
+ __be16 port, u32 flags)
{
struct socket *sock;
struct udp_port_cfg udp_conf;
@@ -354,6 +361,8 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
if (ipv6) {
udp_conf.family = AF_INET6;
udp_conf.ipv6_v6only = 1;
+ udp_conf.use_udp6_rx_checksums =
+ !(flags & GENEVE_F_UDP_ZERO_CSUM6_RX);
} else {
udp_conf.family = AF_INET;
udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -371,16 +380,27 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
static void geneve_notify_add_rx_port(struct geneve_sock *gs)
{
+ struct net_device *dev;
struct sock *sk = gs->sock->sk;
+ struct net *net = sock_net(sk);
sa_family_t sa_family = sk->sk_family;
+ __be16 port = inet_sk(sk)->inet_sport;
int err;
if (sa_family == AF_INET) {
- err = udp_add_offload(&gs->udp_offloads);
+ err = udp_add_offload(sock_net(sk), &gs->udp_offloads);
if (err)
pr_warn("geneve: udp_add_offload failed with status %d\n",
err);
}
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ if (dev->netdev_ops->ndo_add_geneve_port)
+ dev->netdev_ops->ndo_add_geneve_port(dev, sa_family,
+ port);
+ }
+ rcu_read_unlock();
}
static int geneve_hlen(struct genevehdr *gh)
@@ -480,7 +500,7 @@ static int geneve_gro_complete(struct sk_buff *skb, int nhoff,
/* Create new listen socket if needed */
static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
- bool ipv6)
+ bool ipv6, u32 flags)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_sock *gs;
@@ -492,7 +512,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
if (!gs)
return ERR_PTR(-ENOMEM);
- sock = geneve_create_sock(net, ipv6, port);
+ sock = geneve_create_sock(net, ipv6, port, flags);
if (IS_ERR(sock)) {
kfree(gs);
return ERR_CAST(sock);
@@ -521,8 +541,20 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
static void geneve_notify_del_rx_port(struct geneve_sock *gs)
{
+ struct net_device *dev;
struct sock *sk = gs->sock->sk;
+ struct net *net = sock_net(sk);
sa_family_t sa_family = sk->sk_family;
+ __be16 port = inet_sk(sk)->inet_sport;
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ if (dev->netdev_ops->ndo_del_geneve_port)
+ dev->netdev_ops->ndo_del_geneve_port(dev, sa_family,
+ port);
+ }
+
+ rcu_read_unlock();
if (sa_family == AF_INET)
udp_del_offload(&gs->udp_offloads);
@@ -575,12 +607,13 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
goto out;
}
- gs = geneve_socket_create(net, geneve->dst_port, ipv6);
+ gs = geneve_socket_create(net, geneve->dst_port, ipv6, geneve->flags);
if (IS_ERR(gs))
return PTR_ERR(gs);
out:
gs->collect_md = geneve->collect_md;
+ gs->flags = geneve->flags;
#if IS_ENABLED(CONFIG_IPV6)
if (ipv6)
geneve->sock6 = gs;
@@ -642,11 +675,12 @@ static void geneve_build_header(struct genevehdr *geneveh,
static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
__be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
- bool csum, bool xnet)
+ u32 flags, bool xnet)
{
struct genevehdr *gnvh;
int min_headroom;
int err;
+ bool udp_sum = !!(flags & GENEVE_F_UDP_CSUM);
skb_scrub_packet(skb, xnet);
@@ -658,7 +692,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
goto free_rt;
}
- skb = udp_tunnel_handle_offloads(skb, csum);
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto free_rt;
@@ -678,11 +712,12 @@ free_rt:
#if IS_ENABLED(CONFIG_IPV6)
static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
__be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
- bool csum, bool xnet)
+ u32 flags, bool xnet)
{
struct genevehdr *gnvh;
int min_headroom;
int err;
+ bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM6_TX);
skb_scrub_packet(skb, xnet);
@@ -694,7 +729,7 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
goto free_dst;
}
- skb = udp_tunnel_handle_offloads(skb, csum);
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto free_dst;
@@ -824,9 +859,9 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct flowi4 fl4;
__u8 tos, ttl;
__be16 sport;
- bool udp_csum;
__be16 df;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+ u32 flags = geneve->flags;
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
@@ -857,9 +892,13 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (key->tun_flags & TUNNEL_GENEVE_OPT)
opts = ip_tunnel_info_opts(info);
- udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+ if (key->tun_flags & TUNNEL_CSUM)
+ flags |= GENEVE_F_UDP_CSUM;
+ else
+ flags &= ~GENEVE_F_UDP_CSUM;
+
err = geneve_build_skb(rt, skb, key->tun_flags, vni,
- info->options_len, opts, udp_csum, xnet);
+ info->options_len, opts, flags, xnet);
if (unlikely(err))
goto err;
@@ -867,9 +906,8 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
ttl = key->ttl;
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
} else {
- udp_csum = false;
err = geneve_build_skb(rt, skb, 0, geneve->vni,
- 0, NULL, udp_csum, xnet);
+ 0, NULL, flags, xnet);
if (unlikely(err))
goto err;
@@ -880,12 +918,11 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
df = 0;
}
- err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
- tos, ttl, df, sport, geneve->dst_port,
- !net_eq(geneve->net, dev_net(geneve->dev)),
- !udp_csum);
+ udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
+ tos, ttl, df, sport, geneve->dst_port,
+ !net_eq(geneve->net, dev_net(geneve->dev)),
+ !(flags & GENEVE_F_UDP_CSUM));
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
return NETDEV_TX_OK;
tx_error:
@@ -912,8 +949,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct flowi6 fl6;
__u8 prio, ttl;
__be16 sport;
- bool udp_csum;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+ u32 flags = geneve->flags;
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
@@ -942,19 +979,22 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (key->tun_flags & TUNNEL_GENEVE_OPT)
opts = ip_tunnel_info_opts(info);
- udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+ if (key->tun_flags & TUNNEL_CSUM)
+ flags |= GENEVE_F_UDP_CSUM;
+ else
+ flags &= ~GENEVE_F_UDP_CSUM;
+
err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
info->options_len, opts,
- udp_csum, xnet);
+ flags, xnet);
if (unlikely(err))
goto err;
prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
ttl = key->ttl;
} else {
- udp_csum = false;
err = geneve6_build_skb(dst, skb, 0, geneve->vni,
- 0, NULL, udp_csum, xnet);
+ 0, NULL, flags, xnet);
if (unlikely(err))
goto err;
@@ -964,9 +1004,10 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
ttl = 1;
ttl = ttl ? : ip6_dst_hoplimit(dst);
}
- err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
- &fl6.saddr, &fl6.daddr, prio, ttl,
- sport, geneve->dst_port, !udp_csum);
+ udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+ &fl6.saddr, &fl6.daddr, prio, ttl,
+ sport, geneve->dst_port,
+ !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX));
return NETDEV_TX_OK;
tx_error:
@@ -1065,6 +1106,30 @@ static struct device_type geneve_type = {
.name = "geneve",
};
+/* Calls the ndo_add_geneve_port of the caller in order to
+ * supply the listening GENEVE udp ports. Callers are expected
+ * to implement the ndo_add_geneve_port.
+ */
+void geneve_get_rx_port(struct net_device *dev)
+{
+ struct net *net = dev_net(dev);
+ struct geneve_net *gn = net_generic(net, geneve_net_id);
+ struct geneve_sock *gs;
+ sa_family_t sa_family;
+ struct sock *sk;
+ __be16 port;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(gs, &gn->sock_list, list) {
+ sk = gs->sock->sk;
+ sa_family = sk->sk_family;
+ port = inet_sk(sk)->inet_sport;
+ dev->netdev_ops->ndo_add_geneve_port(dev, sa_family, port);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(geneve_get_rx_port);
+
/* Initialize the device structure. */
static void geneve_setup(struct net_device *dev)
{
@@ -1097,6 +1162,9 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
[IFLA_GENEVE_TOS] = { .type = NLA_U8 },
[IFLA_GENEVE_PORT] = { .type = NLA_U16 },
[IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG },
+ [IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
};
static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -1150,7 +1218,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
static int geneve_configure(struct net *net, struct net_device *dev,
union geneve_addr *remote,
__u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
- bool metadata)
+ bool metadata, u32 flags)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -1181,6 +1249,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
geneve->tos = tos;
geneve->dst_port = dst_port;
geneve->collect_md = metadata;
+ geneve->flags = flags;
t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
&tun_on_same_port, &tun_collect_md);
@@ -1219,6 +1288,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
bool metadata = false;
union geneve_addr remote = geneve_remote_unspec;
__u32 vni = 0;
+ u32 flags = 0;
if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
return -EINVAL;
@@ -1259,8 +1329,20 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
if (data[IFLA_GENEVE_COLLECT_METADATA])
metadata = true;
+ if (data[IFLA_GENEVE_UDP_CSUM] &&
+ nla_get_u8(data[IFLA_GENEVE_UDP_CSUM]))
+ flags |= GENEVE_F_UDP_CSUM;
+
+ if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] &&
+ nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
+ flags |= GENEVE_F_UDP_ZERO_CSUM6_TX;
+
+ if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] &&
+ nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
+ flags |= GENEVE_F_UDP_ZERO_CSUM6_RX;
+
return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
- metadata);
+ metadata, flags);
}
static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -1279,6 +1361,9 @@ static size_t geneve_get_size(const struct net_device *dev)
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */
nla_total_size(sizeof(__be16)) + /* IFLA_GENEVE_PORT */
nla_total_size(0) + /* IFLA_GENEVE_COLLECT_METADATA */
+ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_CSUM */
+ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_TX */
+ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_RX */
0;
}
@@ -1315,6 +1400,14 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
goto nla_put_failure;
}
+ if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM,
+ !!(geneve->flags & GENEVE_F_UDP_CSUM)) ||
+ nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+ !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_TX)) ||
+ nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+ !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_RX)))
+ goto nla_put_failure;
+
return 0;
nla_put_failure:
@@ -1348,7 +1441,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
return dev;
err = geneve_configure(net, dev, &geneve_remote_unspec,
- 0, 0, 0, htons(dst_port), true);
+ 0, 0, 0, htons(dst_port), true, 0);
if (err) {
free_netdev(dev);
return ERR_PTR(err);
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 5fa98f599b3d..f4130af09244 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -124,37 +124,22 @@ struct ndis_tcp_ip_checksum_info;
/*
* Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
* within the RNDIS
+ *
+ * The size of this structure is less than 48 bytes and we can now
+ * place this structure in the skb->cb field.
*/
struct hv_netvsc_packet {
/* Bookkeeping stuff */
- u32 status;
-
- bool is_data_pkt;
- bool xmit_more; /* from skb */
- bool cp_partial; /* partial copy into send buffer */
+ u8 cp_partial; /* partial copy into send buffer */
- u16 vlan_tci;
+ u8 rmsg_size; /* RNDIS header and PPI size */
+ u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */
+ u8 page_buf_cnt;
u16 q_idx;
- struct vmbus_channel *channel;
-
- u64 send_completion_tid;
- void *send_completion_ctx;
- void (*send_completion)(void *context);
-
u32 send_buf_index;
- /* This points to the memory after page_buf */
- struct rndis_message *rndis_msg;
-
- u32 rmsg_size; /* RNDIS header and PPI size */
- u32 rmsg_pgcnt; /* page count of RNDIS header and PPI */
-
u32 total_data_buflen;
- /* Points to the send/receive buffer where the ethernet frame is */
- void *data;
- u32 page_buf_cnt;
- struct hv_page_buffer *page_buf;
};
struct netvsc_device_info {
@@ -177,7 +162,6 @@ struct rndis_device {
enum rndis_device_state state;
bool link_state;
- bool link_change;
atomic_t new_req_id;
spinlock_t request_lock;
@@ -188,16 +172,22 @@ struct rndis_device {
/* Interface */
+struct rndis_message;
int netvsc_device_add(struct hv_device *device, void *additional_info);
int netvsc_device_remove(struct hv_device *device);
int netvsc_send(struct hv_device *device,
- struct hv_netvsc_packet *packet);
+ struct hv_netvsc_packet *packet,
+ struct rndis_message *rndis_msg,
+ struct hv_page_buffer **page_buffer,
+ struct sk_buff *skb);
void netvsc_linkstatus_callback(struct hv_device *device_obj,
struct rndis_message *resp);
-void netvsc_xmit_completion(void *context);
int netvsc_recv_callback(struct hv_device *device_obj,
struct hv_netvsc_packet *packet,
- struct ndis_tcp_ip_checksum_info *csum_info);
+ void **data,
+ struct ndis_tcp_ip_checksum_info *csum_info,
+ struct vmbus_channel *channel,
+ u16 vlan_tci);
void netvsc_channel_cb(void *context);
int rndis_filter_open(struct hv_device *dev);
int rndis_filter_close(struct hv_device *dev);
@@ -205,12 +195,13 @@ int rndis_filter_device_add(struct hv_device *dev,
void *additional_info);
void rndis_filter_device_remove(struct hv_device *dev);
int rndis_filter_receive(struct hv_device *dev,
- struct hv_netvsc_packet *pkt);
+ struct hv_netvsc_packet *pkt,
+ void **data,
+ struct vmbus_channel *channel);
int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter);
int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac);
-
#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF)
#define NVSP_PROTOCOL_VERSION_1 2
@@ -633,7 +624,6 @@ struct nvsp_message {
#define RNDIS_PKT_ALIGN_DEFAULT 8
struct multi_send_data {
- spinlock_t lock; /* protect struct multi_send_data */
struct hv_netvsc_packet *pkt; /* netvsc pkt pending */
u32 count; /* counter of batched packets */
};
@@ -644,11 +634,24 @@ struct netvsc_stats {
struct u64_stats_sync syncp;
};
+struct netvsc_reconfig {
+ struct list_head list;
+ u32 event;
+};
+
/* The context of the netvsc device */
struct net_device_context {
/* point back to our device context */
struct hv_device *device_ctx;
+ /* reconfigure work */
struct delayed_work dwork;
+ /* last reconfig time */
+ unsigned long last_reconfig;
+ /* reconfig events */
+ struct list_head reconfig_events;
+ /* list protection */
+ spinlock_t lock;
+
struct work_struct work;
u32 msg_enable; /* debug level */
@@ -1260,5 +1263,4 @@ struct rndis_message {
#define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP)
#define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP)
-
#endif /* _HYPERV_NET_H */
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 51e4c0fd0a74..059fc5231601 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -38,7 +38,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;
struct net_device *ndev = hv_get_drvdata(device);
- int i;
net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
if (!net_device)
@@ -58,9 +57,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
- for (i = 0; i < num_online_cpus(); i++)
- spin_lock_init(&net_device->msd[i].lock);
-
hv_set_drvdata(device, net_device);
return net_device;
}
@@ -610,6 +606,7 @@ static inline void netvsc_free_send_slot(struct netvsc_device *net_device,
}
static void netvsc_send_completion(struct netvsc_device *net_device,
+ struct vmbus_channel *incoming_channel,
struct hv_device *device,
struct vmpacket_descriptor *packet)
{
@@ -617,6 +614,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
struct hv_netvsc_packet *nvsc_packet;
struct net_device *ndev;
u32 send_index;
+ struct sk_buff *skb;
ndev = net_device->ndev;
@@ -642,18 +640,17 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
int queue_sends;
/* Get the send context */
- nvsc_packet = (struct hv_netvsc_packet *)(unsigned long)
- packet->trans_id;
+ skb = (struct sk_buff *)(unsigned long)packet->trans_id;
/* Notify the layer above us */
- if (nvsc_packet) {
+ if (skb) {
+ nvsc_packet = (struct hv_netvsc_packet *) skb->cb;
send_index = nvsc_packet->send_buf_index;
if (send_index != NETVSC_INVALID_INDEX)
netvsc_free_send_slot(net_device, send_index);
q_idx = nvsc_packet->q_idx;
- channel = nvsc_packet->channel;
- nvsc_packet->send_completion(nvsc_packet->
- send_completion_ctx);
+ channel = incoming_channel;
+ dev_kfree_skb_any(skb);
}
num_outstanding_sends =
@@ -705,12 +702,17 @@ static u32 netvsc_get_next_send_section(struct netvsc_device *net_device)
static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
unsigned int section_index,
u32 pend_size,
- struct hv_netvsc_packet *packet)
+ struct hv_netvsc_packet *packet,
+ struct rndis_message *rndis_msg,
+ struct hv_page_buffer **pb,
+ struct sk_buff *skb)
{
char *start = net_device->send_buf;
char *dest = start + (section_index * net_device->send_section_size)
+ pend_size;
int i;
+ bool is_data_pkt = (skb != NULL) ? true : false;
+ bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
u32 msg_size = 0;
u32 padding = 0;
u32 remain = packet->total_data_buflen % net_device->pkt_align;
@@ -718,17 +720,17 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
packet->page_buf_cnt;
/* Add padding */
- if (packet->is_data_pkt && packet->xmit_more && remain &&
+ if (is_data_pkt && xmit_more && remain &&
!packet->cp_partial) {
padding = net_device->pkt_align - remain;
- packet->rndis_msg->msg_len += padding;
+ rndis_msg->msg_len += padding;
packet->total_data_buflen += padding;
}
for (i = 0; i < page_count; i++) {
- char *src = phys_to_virt(packet->page_buf[i].pfn << PAGE_SHIFT);
- u32 offset = packet->page_buf[i].offset;
- u32 len = packet->page_buf[i].len;
+ char *src = phys_to_virt((*pb)[i].pfn << PAGE_SHIFT);
+ u32 offset = (*pb)[i].offset;
+ u32 len = (*pb)[i].len;
memcpy(dest, (src + offset), len);
msg_size += len;
@@ -745,19 +747,22 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
static inline int netvsc_send_pkt(
struct hv_netvsc_packet *packet,
- struct netvsc_device *net_device)
+ struct netvsc_device *net_device,
+ struct hv_page_buffer **pb,
+ struct sk_buff *skb)
{
struct nvsp_message nvmsg;
- struct vmbus_channel *out_channel = packet->channel;
u16 q_idx = packet->q_idx;
+ struct vmbus_channel *out_channel = net_device->chn_table[q_idx];
struct net_device *ndev = net_device->ndev;
u64 req_id;
int ret;
struct hv_page_buffer *pgbuf;
u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound);
+ bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
- if (packet->is_data_pkt) {
+ if (skb != NULL) {
/* 0 is RMC_DATA; */
nvmsg.msg.v1_msg.send_rndis_pkt.channel_type = 0;
} else {
@@ -773,10 +778,7 @@ static inline int netvsc_send_pkt(
nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size =
packet->total_data_buflen;
- if (packet->send_completion)
- req_id = (ulong)packet;
- else
- req_id = 0;
+ req_id = (ulong)skb;
if (out_channel->rescind)
return -ENODEV;
@@ -789,11 +791,11 @@ static inline int netvsc_send_pkt(
* unnecessarily.
*/
if (ring_avail < (RING_AVAIL_PERCENT_LOWATER + 1))
- packet->xmit_more = false;
+ xmit_more = false;
if (packet->page_buf_cnt) {
- pgbuf = packet->cp_partial ? packet->page_buf +
- packet->rmsg_pgcnt : packet->page_buf;
+ pgbuf = packet->cp_partial ? (*pb) +
+ packet->rmsg_pgcnt : (*pb);
ret = vmbus_sendpacket_pagebuffer_ctl(out_channel,
pgbuf,
packet->page_buf_cnt,
@@ -801,14 +803,14 @@ static inline int netvsc_send_pkt(
sizeof(struct nvsp_message),
req_id,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED,
- !packet->xmit_more);
+ !xmit_more);
} else {
ret = vmbus_sendpacket_ctl(out_channel, &nvmsg,
sizeof(struct nvsp_message),
req_id,
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED,
- !packet->xmit_more);
+ !xmit_more);
}
if (ret == 0) {
@@ -840,7 +842,10 @@ static inline int netvsc_send_pkt(
}
int netvsc_send(struct hv_device *device,
- struct hv_netvsc_packet *packet)
+ struct hv_netvsc_packet *packet,
+ struct rndis_message *rndis_msg,
+ struct hv_page_buffer **pb,
+ struct sk_buff *skb)
{
struct netvsc_device *net_device;
int ret = 0, m_ret = 0;
@@ -848,33 +853,35 @@ int netvsc_send(struct hv_device *device,
u16 q_idx = packet->q_idx;
u32 pktlen = packet->total_data_buflen, msd_len = 0;
unsigned int section_index = NETVSC_INVALID_INDEX;
- unsigned long flag;
struct multi_send_data *msdp;
struct hv_netvsc_packet *msd_send = NULL, *cur_send = NULL;
bool try_batch;
+ bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
net_device = get_outbound_net_device(device);
if (!net_device)
return -ENODEV;
out_channel = net_device->chn_table[q_idx];
- if (!out_channel) {
- out_channel = device->channel;
- q_idx = 0;
- packet->q_idx = 0;
- }
- packet->channel = out_channel;
+
packet->send_buf_index = NETVSC_INVALID_INDEX;
packet->cp_partial = false;
+ /* Send control message directly without accessing msd (Multi-Send
+ * Data) field which may be changed during data packet processing.
+ */
+ if (!skb) {
+ cur_send = packet;
+ goto send_now;
+ }
+
msdp = &net_device->msd[q_idx];
/* batch packets in send buffer if possible */
- spin_lock_irqsave(&msdp->lock, flag);
if (msdp->pkt)
msd_len = msdp->pkt->total_data_buflen;
- try_batch = packet->is_data_pkt && msd_len > 0 && msdp->count <
+ try_batch = (skb != NULL) && msd_len > 0 && msdp->count <
net_device->max_pkt;
if (try_batch && msd_len + pktlen + net_device->pkt_align <
@@ -886,7 +893,7 @@ int netvsc_send(struct hv_device *device,
section_index = msdp->pkt->send_buf_index;
packet->cp_partial = true;
- } else if (packet->is_data_pkt && pktlen + net_device->pkt_align <
+ } else if ((skb != NULL) && pktlen + net_device->pkt_align <
net_device->send_section_size) {
section_index = netvsc_get_next_send_section(net_device);
if (section_index != NETVSC_INVALID_INDEX) {
@@ -900,7 +907,7 @@ int netvsc_send(struct hv_device *device,
if (section_index != NETVSC_INVALID_INDEX) {
netvsc_copy_to_send_buf(net_device,
section_index, msd_len,
- packet);
+ packet, rndis_msg, pb, skb);
packet->send_buf_index = section_index;
@@ -913,9 +920,9 @@ int netvsc_send(struct hv_device *device,
}
if (msdp->pkt)
- netvsc_xmit_completion(msdp->pkt);
+ dev_kfree_skb_any(skb);
- if (packet->xmit_more && !packet->cp_partial) {
+ if (xmit_more && !packet->cp_partial) {
msdp->pkt = packet;
msdp->count++;
} else {
@@ -930,20 +937,19 @@ int netvsc_send(struct hv_device *device,
cur_send = packet;
}
- spin_unlock_irqrestore(&msdp->lock, flag);
-
if (msd_send) {
- m_ret = netvsc_send_pkt(msd_send, net_device);
+ m_ret = netvsc_send_pkt(msd_send, net_device, pb, skb);
if (m_ret != 0) {
netvsc_free_send_slot(net_device,
msd_send->send_buf_index);
- netvsc_xmit_completion(msd_send);
+ dev_kfree_skb_any(skb);
}
}
+send_now:
if (cur_send)
- ret = netvsc_send_pkt(cur_send, net_device);
+ ret = netvsc_send_pkt(cur_send, net_device, pb, skb);
if (ret != 0 && section_index != NETVSC_INVALID_INDEX)
netvsc_free_send_slot(net_device, section_index);
@@ -1009,6 +1015,7 @@ static void netvsc_receive(struct netvsc_device *net_device,
int i;
int count = 0;
struct net_device *ndev;
+ void *data;
ndev = net_device->ndev;
@@ -1043,22 +1050,19 @@ static void netvsc_receive(struct netvsc_device *net_device,
}
count = vmxferpage_packet->range_cnt;
- netvsc_packet->channel = channel;
/* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
for (i = 0; i < count; i++) {
/* Initialize the netvsc packet */
- netvsc_packet->status = NVSP_STAT_SUCCESS;
- netvsc_packet->data = (void *)((unsigned long)net_device->
+ data = (void *)((unsigned long)net_device->
recv_buf + vmxferpage_packet->ranges[i].byte_offset);
netvsc_packet->total_data_buflen =
vmxferpage_packet->ranges[i].byte_count;
/* Pass it to the upper layer */
- rndis_filter_receive(device, netvsc_packet);
+ status = rndis_filter_receive(device, netvsc_packet, &data,
+ channel);
- if (netvsc_packet->status != NVSP_STAT_SUCCESS)
- status = NVSP_STAT_FAIL;
}
netvsc_send_recv_completion(device, channel, net_device,
@@ -1150,6 +1154,7 @@ void netvsc_channel_cb(void *context)
switch (desc->type) {
case VM_PKT_COMP:
netvsc_send_completion(net_device,
+ channel,
device, desc);
break;
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 409b48e1e589..1c8db9afdcda 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -42,6 +42,7 @@
#define RING_SIZE_MIN 64
+#define LINKCHANGE_INT (2 * HZ)
static int ring_size = 128;
module_param(ring_size, int, S_IRUGO);
MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
@@ -272,17 +273,10 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
skb_set_hash(skb, hash, PKT_HASH_TYPE_L3);
}
- return q_idx;
-}
-
-void netvsc_xmit_completion(void *context)
-{
- struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
- struct sk_buff *skb = (struct sk_buff *)
- (unsigned long)packet->send_completion_tid;
+ if (!nvsc_dev->chn_table[q_idx])
+ q_idx = 0;
- if (skb)
- dev_kfree_skb_any(skb);
+ return q_idx;
}
static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
@@ -320,9 +314,10 @@ static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
}
static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb,
- struct hv_netvsc_packet *packet)
+ struct hv_netvsc_packet *packet,
+ struct hv_page_buffer **page_buf)
{
- struct hv_page_buffer *pb = packet->page_buf;
+ struct hv_page_buffer *pb = *page_buf;
u32 slots_used = 0;
char *data = skb->data;
int frags = skb_shinfo(skb)->nr_frags;
@@ -432,8 +427,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
u32 net_trans_info;
u32 hash;
u32 skb_length;
- u32 pkt_sz;
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
+ struct hv_page_buffer *pb = page_buf;
struct netvsc_stats *tx_stats = this_cpu_ptr(net_device_ctx->tx_stats);
/* We will atmost need two pages to describe the rndis
@@ -460,42 +455,34 @@ check_size:
goto check_size;
}
- pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE;
-
- ret = skb_cow_head(skb, pkt_sz);
+ /*
+ * Place the rndis header in the skb head room and
+ * the skb->cb will be used for hv_netvsc_packet
+ * structure.
+ */
+ ret = skb_cow_head(skb, RNDIS_AND_PPI_SIZE);
if (ret) {
netdev_err(net, "unable to alloc hv_netvsc_packet\n");
ret = -ENOMEM;
goto drop;
}
- /* Use the headroom for building up the packet */
- packet = (struct hv_netvsc_packet *)skb->head;
+ /* Use the skb control buffer for building up the packet */
+ BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) >
+ FIELD_SIZEOF(struct sk_buff, cb));
+ packet = (struct hv_netvsc_packet *)skb->cb;
- packet->status = 0;
- packet->xmit_more = skb->xmit_more;
-
- packet->vlan_tci = skb->vlan_tci;
- packet->page_buf = page_buf;
packet->q_idx = skb_get_queue_mapping(skb);
- packet->is_data_pkt = true;
packet->total_data_buflen = skb->len;
- packet->rndis_msg = (struct rndis_message *)((unsigned long)packet +
- sizeof(struct hv_netvsc_packet));
-
- memset(packet->rndis_msg, 0, RNDIS_AND_PPI_SIZE);
+ rndis_msg = (struct rndis_message *)skb->head;
- /* Set the completion routine */
- packet->send_completion = netvsc_xmit_completion;
- packet->send_completion_ctx = packet;
- packet->send_completion_tid = (unsigned long)skb;
+ memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE);
- isvlan = packet->vlan_tci & VLAN_TAG_PRESENT;
+ isvlan = skb->vlan_tci & VLAN_TAG_PRESENT;
/* Add the rndis header */
- rndis_msg = packet->rndis_msg;
rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET;
rndis_msg->msg_len = packet->total_data_buflen;
rndis_pkt = &rndis_msg->msg.pkt;
@@ -521,8 +508,8 @@ check_size:
IEEE_8021Q_INFO);
vlan = (struct ndis_pkt_8021q_info *)((void *)ppi +
ppi->ppi_offset);
- vlan->vlanid = packet->vlan_tci & VLAN_VID_MASK;
- vlan->pri = (packet->vlan_tci & VLAN_PRIO_MASK) >>
+ vlan->vlanid = skb->vlan_tci & VLAN_VID_MASK;
+ vlan->pri = (skb->vlan_tci & VLAN_PRIO_MASK) >>
VLAN_PRIO_SHIFT;
}
@@ -617,9 +604,10 @@ do_send:
rndis_msg->msg_len += rndis_msg_size;
packet->total_data_buflen = rndis_msg->msg_len;
packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size,
- skb, packet);
+ skb, packet, &pb);
- ret = netvsc_send(net_device_ctx->device_ctx, packet);
+ ret = netvsc_send(net_device_ctx->device_ctx, packet,
+ rndis_msg, &pb, skb);
drop:
if (ret == 0) {
@@ -647,37 +635,33 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
struct net_device *net;
struct net_device_context *ndev_ctx;
struct netvsc_device *net_device;
- struct rndis_device *rdev;
-
- net_device = hv_get_drvdata(device_obj);
- rdev = net_device->extension;
+ struct netvsc_reconfig *event;
+ unsigned long flags;
- switch (indicate->status) {
- case RNDIS_STATUS_MEDIA_CONNECT:
- rdev->link_state = false;
- break;
- case RNDIS_STATUS_MEDIA_DISCONNECT:
- rdev->link_state = true;
- break;
- case RNDIS_STATUS_NETWORK_CHANGE:
- rdev->link_change = true;
- break;
- default:
+ /* Handle link change statuses only */
+ if (indicate->status != RNDIS_STATUS_NETWORK_CHANGE &&
+ indicate->status != RNDIS_STATUS_MEDIA_CONNECT &&
+ indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT)
return;
- }
+ net_device = hv_get_drvdata(device_obj);
net = net_device->ndev;
if (!net || net->reg_state != NETREG_REGISTERED)
return;
ndev_ctx = netdev_priv(net);
- if (!rdev->link_state) {
- schedule_delayed_work(&ndev_ctx->dwork, 0);
- schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));
- } else {
- schedule_delayed_work(&ndev_ctx->dwork, 0);
- }
+
+ event = kzalloc(sizeof(*event), GFP_ATOMIC);
+ if (!event)
+ return;
+ event->event = indicate->status;
+
+ spin_lock_irqsave(&ndev_ctx->lock, flags);
+ list_add_tail(&event->list, &ndev_ctx->reconfig_events);
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+
+ schedule_delayed_work(&ndev_ctx->dwork, 0);
}
/*
@@ -686,7 +670,10 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
*/
int netvsc_recv_callback(struct hv_device *device_obj,
struct hv_netvsc_packet *packet,
- struct ndis_tcp_ip_checksum_info *csum_info)
+ void **data,
+ struct ndis_tcp_ip_checksum_info *csum_info,
+ struct vmbus_channel *channel,
+ u16 vlan_tci)
{
struct net_device *net;
struct net_device_context *net_device_ctx;
@@ -695,8 +682,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
net = ((struct netvsc_device *)hv_get_drvdata(device_obj))->ndev;
if (!net || net->reg_state != NETREG_REGISTERED) {
- packet->status = NVSP_STAT_FAIL;
- return 0;
+ return NVSP_STAT_FAIL;
}
net_device_ctx = netdev_priv(net);
rx_stats = this_cpu_ptr(net_device_ctx->rx_stats);
@@ -705,15 +691,14 @@ int netvsc_recv_callback(struct hv_device *device_obj,
skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen);
if (unlikely(!skb)) {
++net->stats.rx_dropped;
- packet->status = NVSP_STAT_FAIL;
- return 0;
+ return NVSP_STAT_FAIL;
}
/*
* Copy to skb. This copy is needed here since the memory pointed by
* hv_netvsc_packet cannot be deallocated
*/
- memcpy(skb_put(skb, packet->total_data_buflen), packet->data,
+ memcpy(skb_put(skb, packet->total_data_buflen), *data,
packet->total_data_buflen);
skb->protocol = eth_type_trans(skb, net);
@@ -728,11 +713,11 @@ int netvsc_recv_callback(struct hv_device *device_obj,
skb->ip_summed = CHECKSUM_NONE;
}
- if (packet->vlan_tci & VLAN_TAG_PRESENT)
+ if (vlan_tci & VLAN_TAG_PRESENT)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
- packet->vlan_tci);
+ vlan_tci);
- skb_record_rx_queue(skb, packet->channel->
+ skb_record_rx_queue(skb, channel->
offermsg.offer.sub_channel_index);
u64_stats_update_begin(&rx_stats->syncp);
@@ -1009,12 +994,9 @@ static const struct net_device_ops device_ops = {
};
/*
- * Send GARP packet to network peers after migrations.
- * After Quick Migration, the network is not immediately operational in the
- * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add
- * another netif_notify_peers() into a delayed work, otherwise GARP packet
- * will not be sent after quick migration, and cause network disconnection.
- * Also, we update the carrier status here.
+ * Handle link status changes. For RNDIS_STATUS_NETWORK_CHANGE emulate link
+ * down/up sequence. In case of RNDIS_STATUS_MEDIA_CONNECT when carrier is
+ * present send GARP packet to network peers with netif_notify_peers().
*/
static void netvsc_link_change(struct work_struct *w)
{
@@ -1022,36 +1004,89 @@ static void netvsc_link_change(struct work_struct *w)
struct net_device *net;
struct netvsc_device *net_device;
struct rndis_device *rdev;
- bool notify, refresh = false;
- char *argv[] = { "/etc/init.d/network", "restart", NULL };
- char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
-
- rtnl_lock();
+ struct netvsc_reconfig *event = NULL;
+ bool notify = false, reschedule = false;
+ unsigned long flags, next_reconfig, delay;
ndev_ctx = container_of(w, struct net_device_context, dwork.work);
net_device = hv_get_drvdata(ndev_ctx->device_ctx);
rdev = net_device->extension;
net = net_device->ndev;
- if (rdev->link_state) {
- netif_carrier_off(net);
- notify = false;
- } else {
- netif_carrier_on(net);
- notify = true;
- if (rdev->link_change) {
- rdev->link_change = false;
- refresh = true;
+ next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT;
+ if (time_is_after_jiffies(next_reconfig)) {
+ /* link_watch only sends one notification with current state
+ * per second, avoid doing reconfig more frequently. Handle
+ * wrap around.
+ */
+ delay = next_reconfig - jiffies;
+ delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT;
+ schedule_delayed_work(&ndev_ctx->dwork, delay);
+ return;
+ }
+ ndev_ctx->last_reconfig = jiffies;
+
+ spin_lock_irqsave(&ndev_ctx->lock, flags);
+ if (!list_empty(&ndev_ctx->reconfig_events)) {
+ event = list_first_entry(&ndev_ctx->reconfig_events,
+ struct netvsc_reconfig, list);
+ list_del(&event->list);
+ reschedule = !list_empty(&ndev_ctx->reconfig_events);
+ }
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+
+ if (!event)
+ return;
+
+ rtnl_lock();
+
+ switch (event->event) {
+ /* Only the following events are possible due to the check in
+ * netvsc_linkstatus_callback()
+ */
+ case RNDIS_STATUS_MEDIA_CONNECT:
+ if (rdev->link_state) {
+ rdev->link_state = false;
+ netif_carrier_on(net);
+ netif_tx_wake_all_queues(net);
+ } else {
+ notify = true;
+ }
+ kfree(event);
+ break;
+ case RNDIS_STATUS_MEDIA_DISCONNECT:
+ if (!rdev->link_state) {
+ rdev->link_state = true;
+ netif_carrier_off(net);
+ netif_tx_stop_all_queues(net);
+ }
+ kfree(event);
+ break;
+ case RNDIS_STATUS_NETWORK_CHANGE:
+ /* Only makes sense if carrier is present */
+ if (!rdev->link_state) {
+ rdev->link_state = true;
+ netif_carrier_off(net);
+ netif_tx_stop_all_queues(net);
+ event->event = RNDIS_STATUS_MEDIA_CONNECT;
+ spin_lock_irqsave(&ndev_ctx->lock, flags);
+ list_add_tail(&event->list, &ndev_ctx->reconfig_events);
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+ reschedule = true;
}
+ break;
}
rtnl_unlock();
- if (refresh)
- call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
-
if (notify)
netdev_notify_peers(net);
+
+ /* link_watch only sends one notification with current state per
+ * second, handle next reconfig event in 2 seconds.
+ */
+ if (reschedule)
+ schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
}
static void netvsc_free_netdev(struct net_device *netdev)
@@ -1071,16 +1106,12 @@ static int netvsc_probe(struct hv_device *dev,
struct netvsc_device_info device_info;
struct netvsc_device *nvdev;
int ret;
- u32 max_needed_headroom;
net = alloc_etherdev_mq(sizeof(struct net_device_context),
num_online_cpus());
if (!net)
return -ENOMEM;
- max_needed_headroom = sizeof(struct hv_netvsc_packet) +
- RNDIS_AND_PPI_SIZE;
-
netif_carrier_off(net);
net_device_ctx = netdev_priv(net);
@@ -1106,6 +1137,9 @@ static int netvsc_probe(struct hv_device *dev,
INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change);
INIT_WORK(&net_device_ctx->work, do_set_multicast);
+ spin_lock_init(&net_device_ctx->lock);
+ INIT_LIST_HEAD(&net_device_ctx->reconfig_events);
+
net->netdev_ops = &device_ops;
net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM |
@@ -1116,13 +1150,6 @@ static int netvsc_probe(struct hv_device *dev,
net->ethtool_ops = &ethtool_ops;
SET_NETDEV_DEV(net, &dev->device);
- /*
- * Request additional head room in the skb.
- * We will use this space to build the rndis
- * heaser and other state we need to maintain.
- */
- net->needed_headroom = max_needed_headroom;
-
/* Notify the netvsc driver of the new device */
memset(&device_info, 0, sizeof(device_info));
device_info.ring_size = ring_size;
@@ -1145,8 +1172,6 @@ static int netvsc_probe(struct hv_device *dev,
pr_err("Unable to register netdev.\n");
rndis_filter_device_remove(dev);
netvsc_free_netdev(net);
- } else {
- schedule_delayed_work(&net_device_ctx->dwork, 0);
}
return ret;
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index 5931a799aa17..a37bbda37ffa 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -210,37 +210,33 @@ static int rndis_filter_send_request(struct rndis_device *dev,
int ret;
struct hv_netvsc_packet *packet;
struct hv_page_buffer page_buf[2];
+ struct hv_page_buffer *pb = page_buf;
/* Setup the packet to send it */
packet = &req->pkt;
- packet->is_data_pkt = false;
packet->total_data_buflen = req->request_msg.msg_len;
packet->page_buf_cnt = 1;
- packet->page_buf = page_buf;
- packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >>
+ pb[0].pfn = virt_to_phys(&req->request_msg) >>
PAGE_SHIFT;
- packet->page_buf[0].len = req->request_msg.msg_len;
- packet->page_buf[0].offset =
+ pb[0].len = req->request_msg.msg_len;
+ pb[0].offset =
(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
/* Add one page_buf when request_msg crossing page boundary */
- if (packet->page_buf[0].offset + packet->page_buf[0].len > PAGE_SIZE) {
+ if (pb[0].offset + pb[0].len > PAGE_SIZE) {
packet->page_buf_cnt++;
- packet->page_buf[0].len = PAGE_SIZE -
- packet->page_buf[0].offset;
- packet->page_buf[1].pfn = virt_to_phys((void *)&req->request_msg
- + packet->page_buf[0].len) >> PAGE_SHIFT;
- packet->page_buf[1].offset = 0;
- packet->page_buf[1].len = req->request_msg.msg_len -
- packet->page_buf[0].len;
+ pb[0].len = PAGE_SIZE -
+ pb[0].offset;
+ pb[1].pfn = virt_to_phys((void *)&req->request_msg
+ + pb[0].len) >> PAGE_SHIFT;
+ pb[1].offset = 0;
+ pb[1].len = req->request_msg.msg_len -
+ pb[0].len;
}
- packet->send_completion = NULL;
- packet->xmit_more = false;
-
- ret = netvsc_send(dev->net_dev->dev, packet);
+ ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL);
return ret;
}
@@ -348,14 +344,17 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
return NULL;
}
-static void rndis_filter_receive_data(struct rndis_device *dev,
+static int rndis_filter_receive_data(struct rndis_device *dev,
struct rndis_message *msg,
- struct hv_netvsc_packet *pkt)
+ struct hv_netvsc_packet *pkt,
+ void **data,
+ struct vmbus_channel *channel)
{
struct rndis_packet *rndis_pkt;
u32 data_offset;
struct ndis_pkt_8021q_info *vlan;
struct ndis_tcp_ip_checksum_info *csum_info;
+ u16 vlan_tci = 0;
rndis_pkt = &msg->msg.pkt;
@@ -373,7 +372,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
"overflow detected (got %u, min %u)"
"...dropping this message!\n",
pkt->total_data_buflen, rndis_pkt->data_len);
- return;
+ return NVSP_STAT_FAIL;
}
/*
@@ -382,22 +381,23 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
* the data packet to the stack, without the rndis trailer padding
*/
pkt->total_data_buflen = rndis_pkt->data_len;
- pkt->data = (void *)((unsigned long)pkt->data + data_offset);
+ *data = (void *)((unsigned long)(*data) + data_offset);
vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
if (vlan) {
- pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
+ vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
(vlan->pri << VLAN_PRIO_SHIFT);
- } else {
- pkt->vlan_tci = 0;
}
csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
- netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info);
+ return netvsc_recv_callback(dev->net_dev->dev, pkt, data,
+ csum_info, channel, vlan_tci);
}
int rndis_filter_receive(struct hv_device *dev,
- struct hv_netvsc_packet *pkt)
+ struct hv_netvsc_packet *pkt,
+ void **data,
+ struct vmbus_channel *channel)
{
struct netvsc_device *net_dev = hv_get_drvdata(dev);
struct rndis_device *rndis_dev;
@@ -406,7 +406,7 @@ int rndis_filter_receive(struct hv_device *dev,
int ret = 0;
if (!net_dev) {
- ret = -EINVAL;
+ ret = NVSP_STAT_FAIL;
goto exit;
}
@@ -416,7 +416,7 @@ int rndis_filter_receive(struct hv_device *dev,
if (!net_dev->extension) {
netdev_err(ndev, "got rndis message but no rndis device - "
"dropping this message!\n");
- ret = -ENODEV;
+ ret = NVSP_STAT_FAIL;
goto exit;
}
@@ -424,11 +424,11 @@ int rndis_filter_receive(struct hv_device *dev,
if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
netdev_err(ndev, "got rndis message but rndis device "
"uninitialized...dropping this message!\n");
- ret = -ENODEV;
+ ret = NVSP_STAT_FAIL;
goto exit;
}
- rndis_msg = pkt->data;
+ rndis_msg = *data;
if (netif_msg_rx_err(net_dev->nd_ctx))
dump_rndis_message(dev, rndis_msg);
@@ -436,7 +436,8 @@ int rndis_filter_receive(struct hv_device *dev,
switch (rndis_msg->ndis_msg_type) {
case RNDIS_MSG_PACKET:
/* data msg */
- rndis_filter_receive_data(rndis_dev, rndis_msg, pkt);
+ ret = rndis_filter_receive_data(rndis_dev, rndis_msg, pkt,
+ data, channel);
break;
case RNDIS_MSG_INIT_C:
@@ -459,9 +460,6 @@ int rndis_filter_receive(struct hv_device *dev,
}
exit:
- if (ret != 0)
- pkt->status = NVSP_STAT_FAIL;
-
return ret;
}
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig
index ce5f1a21e6d7..3057a8df4ce9 100644
--- a/drivers/net/ieee802154/Kconfig
+++ b/drivers/net/ieee802154/Kconfig
@@ -71,3 +71,14 @@ config IEEE802154_ATUSB
This driver can also be built as a module. To do so say M here.
The module will be called 'atusb'.
+
+config IEEE802154_ADF7242
+ tristate "ADF7242 transceiver driver"
+ depends on IEEE802154_DRIVERS && MAC802154
+ depends on SPI
+ ---help---
+ Say Y here to enable the ADF7242 SPI 802.15.4 wireless
+ controller.
+
+ This driver can also be built as a module. To do so, say M here.
+ the module will be called 'adf7242'.
diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile
index cf1d2a6db023..3a923d339497 100644
--- a/drivers/net/ieee802154/Makefile
+++ b/drivers/net/ieee802154/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o
obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o
obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o
obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o
+obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o
diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c
new file mode 100644
index 000000000000..89154c079788
--- /dev/null
+++ b/drivers/net/ieee802154/adf7242.c
@@ -0,0 +1,1285 @@
+/*
+ * Analog Devices ADF7242 Low-Power IEEE 802.15.4 Transceiver
+ *
+ * Copyright 2009-2015 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * http://www.analog.com/ADF7242
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/firmware.h>
+#include <linux/spi/spi.h>
+#include <linux/skbuff.h>
+#include <linux/of.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/ieee802154.h>
+#include <net/mac802154.h>
+#include <net/cfg802154.h>
+
+#define FIRMWARE "adf7242_firmware.bin"
+#define MAX_POLL_LOOPS 200
+
+/* All Registers */
+
+#define REG_EXT_CTRL 0x100 /* RW External LNA/PA and internal PA control */
+#define REG_TX_FSK_TEST 0x101 /* RW TX FSK test mode configuration */
+#define REG_CCA1 0x105 /* RW RSSI threshold for CCA */
+#define REG_CCA2 0x106 /* RW CCA mode configuration */
+#define REG_BUFFERCFG 0x107 /* RW RX_BUFFER overwrite control */
+#define REG_PKT_CFG 0x108 /* RW FCS evaluation configuration */
+#define REG_DELAYCFG0 0x109 /* RW RC_RX command to SFD or sync word delay */
+#define REG_DELAYCFG1 0x10A /* RW RC_TX command to TX state */
+#define REG_DELAYCFG2 0x10B /* RW Mac delay extension */
+#define REG_SYNC_WORD0 0x10C /* RW sync word bits [7:0] of [23:0] */
+#define REG_SYNC_WORD1 0x10D /* RW sync word bits [15:8] of [23:0] */
+#define REG_SYNC_WORD2 0x10E /* RW sync word bits [23:16] of [23:0] */
+#define REG_SYNC_CONFIG 0x10F /* RW sync word configuration */
+#define REG_RC_CFG 0x13E /* RW RX / TX packet configuration */
+#define REG_RC_VAR44 0x13F /* RW RESERVED */
+#define REG_CH_FREQ0 0x300 /* RW Channel Frequency Settings - Low */
+#define REG_CH_FREQ1 0x301 /* RW Channel Frequency Settings - Middle */
+#define REG_CH_FREQ2 0x302 /* RW Channel Frequency Settings - High */
+#define REG_TX_FD 0x304 /* RW TX Frequency Deviation Register */
+#define REG_DM_CFG0 0x305 /* RW RX Discriminator BW Register */
+#define REG_TX_M 0x306 /* RW TX Mode Register */
+#define REG_RX_M 0x307 /* RW RX Mode Register */
+#define REG_RRB 0x30C /* R RSSI Readback Register */
+#define REG_LRB 0x30D /* R Link Quality Readback Register */
+#define REG_DR0 0x30E /* RW bits [15:8] of [15:0] data rate setting */
+#define REG_DR1 0x30F /* RW bits [7:0] of [15:0] data rate setting */
+#define REG_PRAMPG 0x313 /* RW RESERVED */
+#define REG_TXPB 0x314 /* RW TX Packet Storage Base Address */
+#define REG_RXPB 0x315 /* RW RX Packet Storage Base Address */
+#define REG_TMR_CFG0 0x316 /* RW Wake up Timer Conf Register - High */
+#define REG_TMR_CFG1 0x317 /* RW Wake up Timer Conf Register - Low */
+#define REG_TMR_RLD0 0x318 /* RW Wake up Timer Value Register - High */
+#define REG_TMR_RLD1 0x319 /* RW Wake up Timer Value Register - Low */
+#define REG_TMR_CTRL 0x31A /* RW Wake up Timer Timeout flag */
+#define REG_PD_AUX 0x31E /* RW Battmon enable */
+#define REG_GP_CFG 0x32C /* RW GPIO Configuration */
+#define REG_GP_OUT 0x32D /* RW GPIO Configuration */
+#define REG_GP_IN 0x32E /* R GPIO Configuration */
+#define REG_SYNT 0x335 /* RW bandwidth calibration timers */
+#define REG_CAL_CFG 0x33D /* RW Calibration Settings */
+#define REG_PA_BIAS 0x36E /* RW PA BIAS */
+#define REG_SYNT_CAL 0x371 /* RW Oscillator and Doubler Configuration */
+#define REG_IIRF_CFG 0x389 /* RW BB Filter Decimation Rate */
+#define REG_CDR_CFG 0x38A /* RW CDR kVCO */
+#define REG_DM_CFG1 0x38B /* RW Postdemodulator Filter */
+#define REG_AGCSTAT 0x38E /* R RXBB Ref Osc Calibration Engine Readback */
+#define REG_RXCAL0 0x395 /* RW RX BB filter tuning, LSB */
+#define REG_RXCAL1 0x396 /* RW RX BB filter tuning, MSB */
+#define REG_RXFE_CFG 0x39B /* RW RXBB Ref Osc & RXFE Calibration */
+#define REG_PA_RR 0x3A7 /* RW Set PA ramp rate */
+#define REG_PA_CFG 0x3A8 /* RW PA enable */
+#define REG_EXTPA_CFG 0x3A9 /* RW External PA BIAS DAC */
+#define REG_EXTPA_MSC 0x3AA /* RW PA Bias Mode */
+#define REG_ADC_RBK 0x3AE /* R Readback temp */
+#define REG_AGC_CFG1 0x3B2 /* RW GC Parameters */
+#define REG_AGC_MAX 0x3B4 /* RW Slew rate */
+#define REG_AGC_CFG2 0x3B6 /* RW RSSI Parameters */
+#define REG_AGC_CFG3 0x3B7 /* RW RSSI Parameters */
+#define REG_AGC_CFG4 0x3B8 /* RW RSSI Parameters */
+#define REG_AGC_CFG5 0x3B9 /* RW RSSI & NDEC Parameters */
+#define REG_AGC_CFG6 0x3BA /* RW NDEC Parameters */
+#define REG_OCL_CFG1 0x3C4 /* RW OCL System Parameters */
+#define REG_IRQ1_EN0 0x3C7 /* RW Interrupt Mask set bits for IRQ1 */
+#define REG_IRQ1_EN1 0x3C8 /* RW Interrupt Mask set bits for IRQ1 */
+#define REG_IRQ2_EN0 0x3C9 /* RW Interrupt Mask set bits for IRQ2 */
+#define REG_IRQ2_EN1 0x3CA /* RW Interrupt Mask set bits for IRQ2 */
+#define REG_IRQ1_SRC0 0x3CB /* RW Interrupt Source bits for IRQ */
+#define REG_IRQ1_SRC1 0x3CC /* RW Interrupt Source bits for IRQ */
+#define REG_OCL_BW0 0x3D2 /* RW OCL System Parameters */
+#define REG_OCL_BW1 0x3D3 /* RW OCL System Parameters */
+#define REG_OCL_BW2 0x3D4 /* RW OCL System Parameters */
+#define REG_OCL_BW3 0x3D5 /* RW OCL System Parameters */
+#define REG_OCL_BW4 0x3D6 /* RW OCL System Parameters */
+#define REG_OCL_BWS 0x3D7 /* RW OCL System Parameters */
+#define REG_OCL_CFG13 0x3E0 /* RW OCL System Parameters */
+#define REG_GP_DRV 0x3E3 /* RW I/O pads Configuration and bg trim */
+#define REG_BM_CFG 0x3E6 /* RW Batt. Monitor Threshold Voltage setting */
+#define REG_SFD_15_4 0x3F4 /* RW Option to set non standard SFD */
+#define REG_AFC_CFG 0x3F7 /* RW AFC mode and polarity */
+#define REG_AFC_KI_KP 0x3F8 /* RW AFC ki and kp */
+#define REG_AFC_RANGE 0x3F9 /* RW AFC range */
+#define REG_AFC_READ 0x3FA /* RW Readback frequency error */
+
+/* REG_EXTPA_MSC */
+#define PA_PWR(x) (((x) & 0xF) << 4)
+#define EXTPA_BIAS_SRC BIT(3)
+#define EXTPA_BIAS_MODE(x) (((x) & 0x7) << 0)
+
+/* REG_PA_CFG */
+#define PA_BRIDGE_DBIAS(x) (((x) & 0x1F) << 0)
+#define PA_DBIAS_HIGH_POWER 21
+#define PA_DBIAS_LOW_POWER 13
+
+/* REG_PA_BIAS */
+#define PA_BIAS_CTRL(x) (((x) & 0x1F) << 1)
+#define REG_PA_BIAS_DFL BIT(0)
+#define PA_BIAS_HIGH_POWER 63
+#define PA_BIAS_LOW_POWER 55
+
+#define REG_PAN_ID0 0x112
+#define REG_PAN_ID1 0x113
+#define REG_SHORT_ADDR_0 0x114
+#define REG_SHORT_ADDR_1 0x115
+#define REG_IEEE_ADDR_0 0x116
+#define REG_IEEE_ADDR_1 0x117
+#define REG_IEEE_ADDR_2 0x118
+#define REG_IEEE_ADDR_3 0x119
+#define REG_IEEE_ADDR_4 0x11A
+#define REG_IEEE_ADDR_5 0x11B
+#define REG_IEEE_ADDR_6 0x11C
+#define REG_IEEE_ADDR_7 0x11D
+#define REG_FFILT_CFG 0x11E
+#define REG_AUTO_CFG 0x11F
+#define REG_AUTO_TX1 0x120
+#define REG_AUTO_TX2 0x121
+#define REG_AUTO_STATUS 0x122
+
+/* REG_FFILT_CFG */
+#define ACCEPT_BEACON_FRAMES BIT(0)
+#define ACCEPT_DATA_FRAMES BIT(1)
+#define ACCEPT_ACK_FRAMES BIT(2)
+#define ACCEPT_MACCMD_FRAMES BIT(3)
+#define ACCEPT_RESERVED_FRAMES BIT(4)
+#define ACCEPT_ALL_ADDRESS BIT(5)
+
+/* REG_AUTO_CFG */
+#define AUTO_ACK_FRAMEPEND BIT(0)
+#define IS_PANCOORD BIT(1)
+#define RX_AUTO_ACK_EN BIT(3)
+#define CSMA_CA_RX_TURNAROUND BIT(4)
+
+/* REG_AUTO_TX1 */
+#define MAX_FRAME_RETRIES(x) ((x) & 0xF)
+#define MAX_CCA_RETRIES(x) (((x) & 0x7) << 4)
+
+/* REG_AUTO_TX2 */
+#define CSMA_MAX_BE(x) ((x) & 0xF)
+#define CSMA_MIN_BE(x) (((x) & 0xF) << 4)
+
+#define CMD_SPI_NOP 0xFF /* No operation. Use for dummy writes */
+#define CMD_SPI_PKT_WR 0x10 /* Write telegram to the Packet RAM
+ * starting from the TX packet base address
+ * pointer tx_packet_base
+ */
+#define CMD_SPI_PKT_RD 0x30 /* Read telegram from the Packet RAM
+ * starting from RX packet base address
+ * pointer rxpb.rx_packet_base
+ */
+#define CMD_SPI_MEM_WR(x) (0x18 + (x >> 8)) /* Write data to MCR or
+ * Packet RAM sequentially
+ */
+#define CMD_SPI_MEM_RD(x) (0x38 + (x >> 8)) /* Read data from MCR or
+ * Packet RAM sequentially
+ */
+#define CMD_SPI_MEMR_WR(x) (0x08 + (x >> 8)) /* Write data to MCR or Packet
+ * RAM as random block
+ */
+#define CMD_SPI_MEMR_RD(x) (0x28 + (x >> 8)) /* Read data from MCR or
+ * Packet RAM random block
+ */
+#define CMD_SPI_PRAM_WR 0x1E /* Write data sequentially to current
+ * PRAM page selected
+ */
+#define CMD_SPI_PRAM_RD 0x3E /* Read data sequentially from current
+ * PRAM page selected
+ */
+#define CMD_RC_SLEEP 0xB1 /* Invoke transition of radio controller
+ * into SLEEP state
+ */
+#define CMD_RC_IDLE 0xB2 /* Invoke transition of radio controller
+ * into IDLE state
+ */
+#define CMD_RC_PHY_RDY 0xB3 /* Invoke transition of radio controller
+ * into PHY_RDY state
+ */
+#define CMD_RC_RX 0xB4 /* Invoke transition of radio controller
+ * into RX state
+ */
+#define CMD_RC_TX 0xB5 /* Invoke transition of radio controller
+ * into TX state
+ */
+#define CMD_RC_MEAS 0xB6 /* Invoke transition of radio controller
+ * into MEAS state
+ */
+#define CMD_RC_CCA 0xB7 /* Invoke Clear channel assessment */
+#define CMD_RC_CSMACA 0xC1 /* initiates CSMA-CA channel access
+ * sequence and frame transmission
+ */
+#define CMD_RC_PC_RESET 0xC7 /* Program counter reset */
+#define CMD_RC_RESET 0xC8 /* Resets the ADF7242 and puts it in
+ * the sleep state
+ */
+#define CMD_RC_PC_RESET_NO_WAIT (CMD_RC_PC_RESET | BIT(31))
+
+/* STATUS */
+
+#define STAT_SPI_READY BIT(7)
+#define STAT_IRQ_STATUS BIT(6)
+#define STAT_RC_READY BIT(5)
+#define STAT_CCA_RESULT BIT(4)
+#define RC_STATUS_IDLE 1
+#define RC_STATUS_MEAS 2
+#define RC_STATUS_PHY_RDY 3
+#define RC_STATUS_RX 4
+#define RC_STATUS_TX 5
+#define RC_STATUS_MASK 0xF
+
+/* AUTO_STATUS */
+
+#define SUCCESS 0
+#define SUCCESS_DATPEND 1
+#define FAILURE_CSMACA 2
+#define FAILURE_NOACK 3
+#define AUTO_STATUS_MASK 0x3
+
+#define PRAM_PAGESIZE 256
+
+/* IRQ1 */
+
+#define IRQ_CCA_COMPLETE BIT(0)
+#define IRQ_SFD_RX BIT(1)
+#define IRQ_SFD_TX BIT(2)
+#define IRQ_RX_PKT_RCVD BIT(3)
+#define IRQ_TX_PKT_SENT BIT(4)
+#define IRQ_FRAME_VALID BIT(5)
+#define IRQ_ADDRESS_VALID BIT(6)
+#define IRQ_CSMA_CA BIT(7)
+
+#define AUTO_TX_TURNAROUND BIT(3)
+#define ADDON_EN BIT(4)
+
+#define FLAG_XMIT 0
+#define FLAG_START 1
+
+#define ADF7242_REPORT_CSMA_CA_STAT 0 /* framework doesn't handle yet */
+
+struct adf7242_local {
+ struct spi_device *spi;
+ struct completion tx_complete;
+ struct ieee802154_hw *hw;
+ struct mutex bmux; /* protect SPI messages */
+ struct spi_message stat_msg;
+ struct spi_transfer stat_xfer;
+ struct dentry *debugfs_root;
+ unsigned long flags;
+ int tx_stat;
+ bool promiscuous;
+ s8 rssi;
+ u8 max_frame_retries;
+ u8 max_cca_retries;
+ u8 max_be;
+ u8 min_be;
+
+ /* DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ u8 buf[3] ____cacheline_aligned;
+ u8 buf_reg_tx[3];
+ u8 buf_read_tx[4];
+ u8 buf_read_rx[4];
+ u8 buf_stat_rx;
+ u8 buf_stat_tx;
+ u8 buf_cmd;
+};
+
+static int adf7242_soft_reset(struct adf7242_local *lp, int line);
+
+static int adf7242_status(struct adf7242_local *lp, u8 *stat)
+{
+ int status;
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &lp->stat_msg);
+ *stat = lp->buf_stat_rx;
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_wait_status(struct adf7242_local *lp, unsigned status,
+ unsigned mask, int line)
+{
+ int cnt = 0, ret = 0;
+ u8 stat;
+
+ do {
+ adf7242_status(lp, &stat);
+ cnt++;
+ } while (((stat & mask) != status) && (cnt < MAX_POLL_LOOPS));
+
+ if (cnt >= MAX_POLL_LOOPS) {
+ ret = -ETIMEDOUT;
+
+ if (!(stat & STAT_RC_READY)) {
+ adf7242_soft_reset(lp, line);
+ adf7242_status(lp, &stat);
+
+ if ((stat & mask) == status)
+ ret = 0;
+ }
+
+ if (ret < 0)
+ dev_warn(&lp->spi->dev,
+ "%s:line %d Timeout status 0x%x (%d)\n",
+ __func__, line, stat, cnt);
+ }
+
+ dev_vdbg(&lp->spi->dev, "%s : loops=%d line %d\n", __func__, cnt, line);
+
+ return ret;
+}
+
+static int adf7242_wait_ready(struct adf7242_local *lp, int line)
+{
+ return adf7242_wait_status(lp, STAT_RC_READY | STAT_SPI_READY,
+ STAT_RC_READY | STAT_SPI_READY, line);
+}
+
+static int adf7242_write_fbuf(struct adf7242_local *lp, u8 *data, u8 len)
+{
+ u8 *buf = lp->buf;
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer_head = {
+ .len = 2,
+ .tx_buf = buf,
+
+ };
+ struct spi_transfer xfer_buf = {
+ .len = len,
+ .tx_buf = data,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ buf[0] = CMD_SPI_PKT_WR;
+ buf[1] = len + 2;
+
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_read_fbuf(struct adf7242_local *lp,
+ u8 *data, size_t len, bool packet_read)
+{
+ u8 *buf = lp->buf;
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer_head = {
+ .len = 3,
+ .tx_buf = buf,
+ .rx_buf = buf,
+ };
+ struct spi_transfer xfer_buf = {
+ .len = len,
+ .rx_buf = data,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ if (packet_read) {
+ buf[0] = CMD_SPI_PKT_RD;
+ buf[1] = CMD_SPI_NOP;
+ buf[2] = 0; /* PHR */
+ } else {
+ buf[0] = CMD_SPI_PRAM_RD;
+ buf[1] = 0;
+ buf[2] = CMD_SPI_NOP;
+ }
+
+ status = spi_sync(lp->spi, &msg);
+
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_read_reg(struct adf7242_local *lp, u16 addr, u8 *data)
+{
+ int status;
+ struct spi_message msg;
+
+ struct spi_transfer xfer = {
+ .len = 4,
+ .tx_buf = lp->buf_read_tx,
+ .rx_buf = lp->buf_read_rx,
+ };
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ lp->buf_read_tx[0] = CMD_SPI_MEM_RD(addr);
+ lp->buf_read_tx[1] = addr;
+ lp->buf_read_tx[2] = CMD_SPI_NOP;
+ lp->buf_read_tx[3] = CMD_SPI_NOP;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ status = spi_sync(lp->spi, &msg);
+ if (msg.status)
+ status = msg.status;
+
+ if (!status)
+ *data = lp->buf_read_rx[3];
+
+ mutex_unlock(&lp->bmux);
+
+ dev_vdbg(&lp->spi->dev, "%s : REG 0x%X, VAL 0x%X\n", __func__,
+ addr, *data);
+
+ return status;
+}
+
+static int adf7242_write_reg(struct adf7242_local *lp, u16 addr, u8 data)
+{
+ int status;
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ lp->buf_reg_tx[0] = CMD_SPI_MEM_WR(addr);
+ lp->buf_reg_tx[1] = addr;
+ lp->buf_reg_tx[2] = data;
+ status = spi_write(lp->spi, lp->buf_reg_tx, 3);
+ mutex_unlock(&lp->bmux);
+
+ dev_vdbg(&lp->spi->dev, "%s : REG 0x%X, VAL 0x%X\n",
+ __func__, addr, data);
+
+ return status;
+}
+
+static int adf7242_cmd(struct adf7242_local *lp, unsigned cmd)
+{
+ int status;
+
+ dev_vdbg(&lp->spi->dev, "%s : CMD=0x%X\n", __func__, cmd);
+
+ if (cmd != CMD_RC_PC_RESET_NO_WAIT)
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ lp->buf_cmd = cmd;
+ status = spi_write(lp->spi, &lp->buf_cmd, 1);
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_upload_firmware(struct adf7242_local *lp, u8 *data, u16 len)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer_buf = { };
+ int status, i, page = 0;
+ u8 *buf = lp->buf;
+
+ struct spi_transfer xfer_head = {
+ .len = 2,
+ .tx_buf = buf,
+ };
+
+ buf[0] = CMD_SPI_PRAM_WR;
+ buf[1] = 0;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ for (i = len; i >= 0; i -= PRAM_PAGESIZE) {
+ adf7242_write_reg(lp, REG_PRAMPG, page);
+
+ xfer_buf.len = (i >= PRAM_PAGESIZE) ? PRAM_PAGESIZE : i;
+ xfer_buf.tx_buf = &data[page * PRAM_PAGESIZE];
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+ page++;
+ }
+
+ return status;
+}
+
+static int adf7242_verify_firmware(struct adf7242_local *lp,
+ const u8 *data, size_t len)
+{
+#ifdef DEBUG
+ int i, j;
+ unsigned int page;
+ u8 *buf = kmalloc(PRAM_PAGESIZE, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ for (page = 0, i = len; i >= 0; i -= PRAM_PAGESIZE, page++) {
+ size_t nb = (i >= PRAM_PAGESIZE) ? PRAM_PAGESIZE : i;
+
+ adf7242_write_reg(lp, REG_PRAMPG, page);
+ adf7242_read_fbuf(lp, buf, nb, false);
+
+ for (j = 0; j < nb; j++) {
+ if (buf[j] != data[page * PRAM_PAGESIZE + j]) {
+ kfree(buf);
+ return -EIO;
+ }
+ }
+ }
+ kfree(buf);
+#endif
+ return 0;
+}
+
+static int adf7242_set_txpower(struct ieee802154_hw *hw, int mbm)
+{
+ struct adf7242_local *lp = hw->priv;
+ u8 pwr, bias_ctrl, dbias, tmp;
+ int db = mbm / 100;
+
+ dev_vdbg(&lp->spi->dev, "%s : Power %d dB\n", __func__, db);
+
+ if (db > 5 || db < -26)
+ return -EINVAL;
+
+ db = DIV_ROUND_CLOSEST(db + 29, 2);
+
+ if (db > 15) {
+ dbias = PA_DBIAS_HIGH_POWER;
+ bias_ctrl = PA_BIAS_HIGH_POWER;
+ } else {
+ dbias = PA_DBIAS_LOW_POWER;
+ bias_ctrl = PA_BIAS_LOW_POWER;
+ }
+
+ pwr = clamp_t(u8, db, 3, 15);
+
+ adf7242_read_reg(lp, REG_PA_CFG, &tmp);
+ tmp &= ~PA_BRIDGE_DBIAS(~0);
+ tmp |= PA_BRIDGE_DBIAS(dbias);
+ adf7242_write_reg(lp, REG_PA_CFG, tmp);
+
+ adf7242_read_reg(lp, REG_PA_BIAS, &tmp);
+ tmp &= ~PA_BIAS_CTRL(~0);
+ tmp |= PA_BIAS_CTRL(bias_ctrl);
+ adf7242_write_reg(lp, REG_PA_BIAS, tmp);
+
+ adf7242_read_reg(lp, REG_EXTPA_MSC, &tmp);
+ tmp &= ~PA_PWR(~0);
+ tmp |= PA_PWR(pwr);
+
+ return adf7242_write_reg(lp, REG_EXTPA_MSC, tmp);
+}
+
+static int adf7242_set_csma_params(struct ieee802154_hw *hw, u8 min_be,
+ u8 max_be, u8 retries)
+{
+ struct adf7242_local *lp = hw->priv;
+ int ret;
+
+ dev_vdbg(&lp->spi->dev, "%s : min_be=%d max_be=%d retries=%d\n",
+ __func__, min_be, max_be, retries);
+
+ if (min_be > max_be || max_be > 8 || retries > 5)
+ return -EINVAL;
+
+ ret = adf7242_write_reg(lp, REG_AUTO_TX1,
+ MAX_FRAME_RETRIES(lp->max_frame_retries) |
+ MAX_CCA_RETRIES(retries));
+ if (ret)
+ return ret;
+
+ lp->max_cca_retries = retries;
+ lp->max_be = max_be;
+ lp->min_be = min_be;
+
+ return adf7242_write_reg(lp, REG_AUTO_TX2, CSMA_MAX_BE(max_be) |
+ CSMA_MIN_BE(min_be));
+}
+
+static int adf7242_set_frame_retries(struct ieee802154_hw *hw, s8 retries)
+{
+ struct adf7242_local *lp = hw->priv;
+ int ret = 0;
+
+ dev_vdbg(&lp->spi->dev, "%s : Retries = %d\n", __func__, retries);
+
+ if (retries < -1 || retries > 15)
+ return -EINVAL;
+
+ if (retries >= 0)
+ ret = adf7242_write_reg(lp, REG_AUTO_TX1,
+ MAX_FRAME_RETRIES(retries) |
+ MAX_CCA_RETRIES(lp->max_cca_retries));
+
+ lp->max_frame_retries = retries;
+
+ return ret;
+}
+
+static int adf7242_ed(struct ieee802154_hw *hw, u8 *level)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ *level = lp->rssi;
+
+ dev_vdbg(&lp->spi->dev, "%s :Exit level=%d\n",
+ __func__, *level);
+
+ return 0;
+}
+
+static int adf7242_start(struct ieee802154_hw *hw)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+ enable_irq(lp->spi->irq);
+ set_bit(FLAG_START, &lp->flags);
+
+ return adf7242_cmd(lp, CMD_RC_RX);
+}
+
+static void adf7242_stop(struct ieee802154_hw *hw)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ adf7242_cmd(lp, CMD_RC_IDLE);
+ clear_bit(FLAG_START, &lp->flags);
+ disable_irq(lp->spi->irq);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+}
+
+static int adf7242_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
+{
+ struct adf7242_local *lp = hw->priv;
+ unsigned long freq;
+
+ dev_dbg(&lp->spi->dev, "%s :Channel=%d\n", __func__, channel);
+
+ might_sleep();
+
+ WARN_ON(page != 0);
+ WARN_ON(channel < 11);
+ WARN_ON(channel > 26);
+
+ freq = (2405 + 5 * (channel - 11)) * 100;
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+
+ adf7242_write_reg(lp, REG_CH_FREQ0, freq);
+ adf7242_write_reg(lp, REG_CH_FREQ1, freq >> 8);
+ adf7242_write_reg(lp, REG_CH_FREQ2, freq >> 16);
+
+ return adf7242_cmd(lp, CMD_RC_RX);
+}
+
+static int adf7242_set_hw_addr_filt(struct ieee802154_hw *hw,
+ struct ieee802154_hw_addr_filt *filt,
+ unsigned long changed)
+{
+ struct adf7242_local *lp = hw->priv;
+ u8 reg;
+
+ dev_dbg(&lp->spi->dev, "%s :Changed=0x%lX\n", __func__, changed);
+
+ might_sleep();
+
+ if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
+ u8 addr[8], i;
+
+ memcpy(addr, &filt->ieee_addr, 8);
+
+ for (i = 0; i < 8; i++)
+ adf7242_write_reg(lp, REG_IEEE_ADDR_0 + i, addr[i]);
+ }
+
+ if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
+ u16 saddr = le16_to_cpu(filt->short_addr);
+
+ adf7242_write_reg(lp, REG_SHORT_ADDR_0, saddr);
+ adf7242_write_reg(lp, REG_SHORT_ADDR_1, saddr >> 8);
+ }
+
+ if (changed & IEEE802154_AFILT_PANID_CHANGED) {
+ u16 pan_id = le16_to_cpu(filt->pan_id);
+
+ adf7242_write_reg(lp, REG_PAN_ID0, pan_id);
+ adf7242_write_reg(lp, REG_PAN_ID1, pan_id >> 8);
+ }
+
+ if (changed & IEEE802154_AFILT_PANC_CHANGED) {
+ adf7242_read_reg(lp, REG_AUTO_CFG, &reg);
+ if (filt->pan_coord)
+ reg |= IS_PANCOORD;
+ else
+ reg &= ~IS_PANCOORD;
+ adf7242_write_reg(lp, REG_AUTO_CFG, reg);
+ }
+
+ return 0;
+}
+
+static int adf7242_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ dev_dbg(&lp->spi->dev, "%s : mode %d\n", __func__, on);
+
+ lp->promiscuous = on;
+
+ if (on) {
+ adf7242_write_reg(lp, REG_AUTO_CFG, 0);
+ return adf7242_write_reg(lp, REG_FFILT_CFG,
+ ACCEPT_BEACON_FRAMES |
+ ACCEPT_DATA_FRAMES |
+ ACCEPT_MACCMD_FRAMES |
+ ACCEPT_ALL_ADDRESS |
+ ACCEPT_ACK_FRAMES |
+ ACCEPT_RESERVED_FRAMES);
+ } else {
+ adf7242_write_reg(lp, REG_FFILT_CFG,
+ ACCEPT_BEACON_FRAMES |
+ ACCEPT_DATA_FRAMES |
+ ACCEPT_MACCMD_FRAMES |
+ ACCEPT_RESERVED_FRAMES);
+
+ return adf7242_write_reg(lp, REG_AUTO_CFG, RX_AUTO_ACK_EN);
+ }
+}
+
+static int adf7242_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct adf7242_local *lp = hw->priv;
+ s8 level = clamp_t(s8, mbm / 100, S8_MIN, S8_MAX);
+
+ dev_dbg(&lp->spi->dev, "%s : level %d\n", __func__, level);
+
+ return adf7242_write_reg(lp, REG_CCA1, level);
+}
+
+static int adf7242_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+ struct adf7242_local *lp = hw->priv;
+ int ret;
+
+ set_bit(FLAG_XMIT, &lp->flags);
+ reinit_completion(&lp->tx_complete);
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+
+ ret = adf7242_write_fbuf(lp, skb->data, skb->len);
+ if (ret)
+ goto err;
+
+ ret = adf7242_cmd(lp, CMD_RC_CSMACA);
+ if (ret)
+ goto err;
+
+ ret = wait_for_completion_interruptible_timeout(&lp->tx_complete,
+ HZ / 10);
+ if (ret < 0)
+ goto err;
+ if (ret == 0) {
+ dev_dbg(&lp->spi->dev, "Timeout waiting for TX interrupt\n");
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (lp->tx_stat != SUCCESS) {
+ dev_dbg(&lp->spi->dev,
+ "Error xmit: Retry count exceeded Status=0x%x\n",
+ lp->tx_stat);
+ ret = -ECOMM;
+ } else {
+ ret = 0;
+ }
+
+err:
+ clear_bit(FLAG_XMIT, &lp->flags);
+ adf7242_cmd(lp, CMD_RC_RX);
+
+ return ret;
+}
+
+static int adf7242_rx(struct adf7242_local *lp)
+{
+ struct sk_buff *skb;
+ size_t len;
+ int ret;
+ u8 lqi, len_u8, *data;
+
+ adf7242_read_reg(lp, 0, &len_u8);
+
+ len = len_u8;
+
+ if (!ieee802154_is_valid_psdu_len(len)) {
+ dev_dbg(&lp->spi->dev,
+ "corrupted frame received len %d\n", (int)len);
+ len = IEEE802154_MTU;
+ }
+
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ adf7242_cmd(lp, CMD_RC_RX);
+ return -ENOMEM;
+ }
+
+ data = skb_put(skb, len);
+ ret = adf7242_read_fbuf(lp, data, len, true);
+ if (ret < 0) {
+ kfree_skb(skb);
+ adf7242_cmd(lp, CMD_RC_RX);
+ return ret;
+ }
+
+ lqi = data[len - 2];
+ lp->rssi = data[len - 1];
+
+ adf7242_cmd(lp, CMD_RC_RX);
+
+ skb_trim(skb, len - 2); /* Don't put RSSI/LQI or CRC into the frame */
+
+ ieee802154_rx_irqsafe(lp->hw, skb, lqi);
+
+ dev_dbg(&lp->spi->dev, "%s: ret=%d len=%d lqi=%d rssi=%d\n",
+ __func__, ret, (int)len, (int)lqi, lp->rssi);
+
+ return 0;
+}
+
+static struct ieee802154_ops adf7242_ops = {
+ .owner = THIS_MODULE,
+ .xmit_sync = adf7242_xmit,
+ .ed = adf7242_ed,
+ .set_channel = adf7242_channel,
+ .set_hw_addr_filt = adf7242_set_hw_addr_filt,
+ .start = adf7242_start,
+ .stop = adf7242_stop,
+ .set_csma_params = adf7242_set_csma_params,
+ .set_frame_retries = adf7242_set_frame_retries,
+ .set_txpower = adf7242_set_txpower,
+ .set_promiscuous_mode = adf7242_set_promiscuous_mode,
+ .set_cca_ed_level = adf7242_set_cca_ed_level,
+};
+
+static void adf7242_debug(u8 irq1)
+{
+#ifdef DEBUG
+ u8 stat;
+
+ adf7242_status(lp, &stat);
+
+ dev_dbg(&lp->spi->dev, "%s IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n",
+ __func__, irq1,
+ irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "",
+ irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "",
+ irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "",
+ irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "",
+ irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "",
+ irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "",
+ irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "",
+ irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : "");
+
+ dev_dbg(&lp->spi->dev, "%s STATUS = %X:\n%s\n%s%s%s%s%s\n",
+ __func__, stat,
+ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY",
+ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "",
+ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "",
+ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "",
+ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "",
+ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : "");
+ }
+#endif
+}
+
+static irqreturn_t adf7242_isr(int irq, void *data)
+{
+ struct adf7242_local *lp = data;
+ unsigned xmit;
+ u8 irq1;
+
+ adf7242_wait_status(lp, RC_STATUS_PHY_RDY, RC_STATUS_MASK, __LINE__);
+
+ adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, irq1);
+
+ if (!(irq1 & (IRQ_RX_PKT_RCVD | IRQ_CSMA_CA)))
+ dev_err(&lp->spi->dev, "%s :ERROR IRQ1 = 0x%X\n",
+ __func__, irq1);
+
+ adf7242_debug(irq1);
+
+ xmit = test_bit(FLAG_XMIT, &lp->flags);
+
+ if (xmit && (irq1 & IRQ_CSMA_CA)) {
+ if (ADF7242_REPORT_CSMA_CA_STAT) {
+ u8 astat;
+
+ adf7242_read_reg(lp, REG_AUTO_STATUS, &astat);
+ astat &= AUTO_STATUS_MASK;
+
+ dev_dbg(&lp->spi->dev, "AUTO_STATUS = %X:\n%s%s%s%s\n",
+ astat,
+ astat == SUCCESS ? "SUCCESS" : "",
+ astat ==
+ SUCCESS_DATPEND ? "SUCCESS_DATPEND" : "",
+ astat == FAILURE_CSMACA ? "FAILURE_CSMACA" : "",
+ astat == FAILURE_NOACK ? "FAILURE_NOACK" : "");
+
+ /* save CSMA-CA completion status */
+ lp->tx_stat = astat;
+ } else {
+ lp->tx_stat = SUCCESS;
+ }
+ complete(&lp->tx_complete);
+ } else if (!xmit && (irq1 & IRQ_RX_PKT_RCVD) &&
+ (irq1 & IRQ_FRAME_VALID)) {
+ adf7242_rx(lp);
+ } else if (!xmit && test_bit(FLAG_START, &lp->flags)) {
+ /* Invalid packet received - drop it and restart */
+ dev_dbg(&lp->spi->dev, "%s:%d : ERROR IRQ1 = 0x%X\n",
+ __func__, __LINE__, irq1);
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+ adf7242_cmd(lp, CMD_RC_RX);
+ } else {
+ /* This can only be xmit without IRQ, likely a RX packet.
+ * we get an TX IRQ shortly - do nothing or let the xmit
+ * timeout handle this
+ */
+ dev_dbg(&lp->spi->dev, "%s:%d : ERROR IRQ1 = 0x%X, xmit %d\n",
+ __func__, __LINE__, irq1, xmit);
+ complete(&lp->tx_complete);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int adf7242_soft_reset(struct adf7242_local *lp, int line)
+{
+ dev_warn(&lp->spi->dev, "%s (line %d)\n", __func__, line);
+
+ if (test_bit(FLAG_START, &lp->flags))
+ disable_irq_nosync(lp->spi->irq);
+
+ adf7242_cmd(lp, CMD_RC_PC_RESET_NO_WAIT);
+ usleep_range(200, 250);
+ adf7242_write_reg(lp, REG_PKT_CFG, ADDON_EN | BIT(2));
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ adf7242_set_promiscuous_mode(lp->hw, lp->promiscuous);
+ adf7242_set_csma_params(lp->hw, lp->min_be, lp->max_be,
+ lp->max_cca_retries);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+
+ if (test_bit(FLAG_START, &lp->flags)) {
+ enable_irq(lp->spi->irq);
+ return adf7242_cmd(lp, CMD_RC_RX);
+ }
+
+ return 0;
+}
+
+static int adf7242_hw_init(struct adf7242_local *lp)
+{
+ int ret;
+ const struct firmware *fw;
+
+ adf7242_cmd(lp, CMD_RC_RESET);
+ adf7242_cmd(lp, CMD_RC_IDLE);
+
+ /* get ADF7242 addon firmware
+ * build this driver as module
+ * and place under /lib/firmware/adf7242_firmware.bin
+ * or compile firmware into the kernel.
+ */
+ ret = request_firmware(&fw, FIRMWARE, &lp->spi->dev);
+ if (ret) {
+ dev_err(&lp->spi->dev,
+ "request_firmware() failed with %d\n", ret);
+ return ret;
+ }
+
+ ret = adf7242_upload_firmware(lp, (u8 *)fw->data, fw->size);
+ if (ret) {
+ dev_err(&lp->spi->dev,
+ "upload firmware failed with %d\n", ret);
+ return ret;
+ }
+
+ ret = adf7242_verify_firmware(lp, (u8 *)fw->data, fw->size);
+ if (ret) {
+ dev_err(&lp->spi->dev,
+ "verify firmware failed with %d\n", ret);
+ return ret;
+ }
+
+ adf7242_cmd(lp, CMD_RC_PC_RESET);
+
+ release_firmware(fw);
+
+ adf7242_write_reg(lp, REG_FFILT_CFG,
+ ACCEPT_BEACON_FRAMES |
+ ACCEPT_DATA_FRAMES |
+ ACCEPT_MACCMD_FRAMES |
+ ACCEPT_RESERVED_FRAMES);
+
+ adf7242_write_reg(lp, REG_AUTO_CFG, RX_AUTO_ACK_EN);
+
+ adf7242_write_reg(lp, REG_PKT_CFG, ADDON_EN | BIT(2));
+
+ adf7242_write_reg(lp, REG_EXTPA_MSC, 0xF1);
+ adf7242_write_reg(lp, REG_RXFE_CFG, 0x1D);
+
+ adf7242_write_reg(lp, REG_IRQ1_EN0, 0);
+ adf7242_write_reg(lp, REG_IRQ1_EN1, IRQ_RX_PKT_RCVD | IRQ_CSMA_CA);
+
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+ adf7242_write_reg(lp, REG_IRQ1_SRC0, 0xFF);
+
+ adf7242_cmd(lp, CMD_RC_IDLE);
+
+ return 0;
+}
+
+static int adf7242_stats_show(struct seq_file *file, void *offset)
+{
+ struct adf7242_local *lp = spi_get_drvdata(file->private);
+ u8 stat, irq1;
+
+ adf7242_status(lp, &stat);
+ adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1);
+
+ seq_printf(file, "IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n", irq1,
+ irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "",
+ irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "",
+ irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "",
+ irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "",
+ irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "",
+ irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "",
+ irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "",
+ irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : "");
+
+ seq_printf(file, "STATUS = %X:\n%s\n%s%s%s%s%s\n", stat,
+ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY",
+ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "",
+ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "",
+ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "",
+ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "",
+ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : "");
+
+ seq_printf(file, "RSSI = %d\n", lp->rssi);
+
+ return 0;
+}
+
+static int adf7242_debugfs_init(struct adf7242_local *lp)
+{
+ char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "adf7242-";
+ struct dentry *stats;
+
+ strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN);
+
+ lp->debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL);
+ if (IS_ERR_OR_NULL(lp->debugfs_root))
+ return PTR_ERR_OR_ZERO(lp->debugfs_root);
+
+ stats = debugfs_create_devm_seqfile(&lp->spi->dev, "status",
+ lp->debugfs_root,
+ adf7242_stats_show);
+ return PTR_ERR_OR_ZERO(stats);
+
+ return 0;
+}
+
+static const s32 adf7242_powers[] = {
+ 500, 400, 300, 200, 100, 0, -100, -200, -300, -400, -500, -600, -700,
+ -800, -900, -1000, -1100, -1200, -1300, -1400, -1500, -1600, -1700,
+ -1800, -1900, -2000, -2100, -2200, -2300, -2400, -2500, -2600,
+};
+
+static const s32 adf7242_ed_levels[] = {
+ -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100,
+ -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100,
+ -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100,
+ -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100,
+ -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100,
+ -4000, -3900, -3800, -3700, -3600, -3500, -3400, -3200, -3100, -3000
+};
+
+static int adf7242_probe(struct spi_device *spi)
+{
+ struct ieee802154_hw *hw;
+ struct adf7242_local *lp;
+ int ret, irq_type;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ specified\n");
+ return -EINVAL;
+ }
+
+ hw = ieee802154_alloc_hw(sizeof(*lp), &adf7242_ops);
+ if (!hw)
+ return -ENOMEM;
+
+ lp = hw->priv;
+ lp->hw = hw;
+ lp->spi = spi;
+
+ hw->priv = lp;
+ hw->parent = &spi->dev;
+ hw->extra_tx_headroom = 0;
+
+ /* We support only 2.4 Ghz */
+ hw->phy->supported.channels[0] = 0x7FFF800;
+
+ hw->flags = IEEE802154_HW_OMIT_CKSUM |
+ IEEE802154_HW_CSMA_PARAMS |
+ IEEE802154_HW_FRAME_RETRIES | IEEE802154_HW_AFILT |
+ IEEE802154_HW_PROMISCUOUS;
+
+ hw->phy->flags = WPAN_PHY_FLAG_TXPOWER |
+ WPAN_PHY_FLAG_CCA_ED_LEVEL |
+ WPAN_PHY_FLAG_CCA_MODE;
+
+ hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY);
+
+ hw->phy->supported.cca_ed_levels = adf7242_ed_levels;
+ hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(adf7242_ed_levels);
+
+ hw->phy->cca.mode = NL802154_CCA_ENERGY;
+
+ hw->phy->supported.tx_powers = adf7242_powers;
+ hw->phy->supported.tx_powers_size = ARRAY_SIZE(adf7242_powers);
+
+ hw->phy->supported.min_minbe = 0;
+ hw->phy->supported.max_minbe = 8;
+
+ hw->phy->supported.min_maxbe = 3;
+ hw->phy->supported.max_maxbe = 8;
+
+ hw->phy->supported.min_frame_retries = 0;
+ hw->phy->supported.max_frame_retries = 15;
+
+ hw->phy->supported.min_csma_backoffs = 0;
+ hw->phy->supported.max_csma_backoffs = 5;
+
+ ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
+
+ mutex_init(&lp->bmux);
+ init_completion(&lp->tx_complete);
+
+ /* Setup Status Message */
+ lp->stat_xfer.len = 1;
+ lp->stat_xfer.tx_buf = &lp->buf_stat_tx;
+ lp->stat_xfer.rx_buf = &lp->buf_stat_rx;
+ lp->buf_stat_tx = CMD_SPI_NOP;
+
+ spi_message_init(&lp->stat_msg);
+ spi_message_add_tail(&lp->stat_xfer, &lp->stat_msg);
+
+ spi_set_drvdata(spi, lp);
+
+ ret = adf7242_hw_init(lp);
+ if (ret)
+ goto err_hw_init;
+
+ irq_type = irq_get_trigger_type(spi->irq);
+ if (!irq_type)
+ irq_type = IRQF_TRIGGER_HIGH;
+
+ ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, adf7242_isr,
+ irq_type | IRQF_ONESHOT,
+ dev_name(&spi->dev), lp);
+ if (ret)
+ goto err_hw_init;
+
+ disable_irq(spi->irq);
+
+ ret = ieee802154_register_hw(lp->hw);
+ if (ret)
+ goto err_hw_init;
+
+ dev_set_drvdata(&spi->dev, lp);
+
+ adf7242_debugfs_init(lp);
+
+ dev_info(&spi->dev, "mac802154 IRQ-%d registered\n", spi->irq);
+
+ return ret;
+
+err_hw_init:
+ mutex_destroy(&lp->bmux);
+ ieee802154_free_hw(lp->hw);
+
+ return ret;
+}
+
+static int adf7242_remove(struct spi_device *spi)
+{
+ struct adf7242_local *lp = spi_get_drvdata(spi);
+
+ if (!IS_ERR_OR_NULL(lp->debugfs_root))
+ debugfs_remove_recursive(lp->debugfs_root);
+
+ ieee802154_unregister_hw(lp->hw);
+ mutex_destroy(&lp->bmux);
+ ieee802154_free_hw(lp->hw);
+
+ return 0;
+}
+
+static const struct of_device_id adf7242_of_match[] = {
+ { .compatible = "adi,adf7242", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adf7242_of_match);
+
+static const struct spi_device_id adf7242_device_id[] = {
+ { .name = "adf7242", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, adf7242_device_id);
+
+static struct spi_driver adf7242_driver = {
+ .id_table = adf7242_device_id,
+ .driver = {
+ .of_match_table = of_match_ptr(adf7242_of_match),
+ .name = "adf7242",
+ .owner = THIS_MODULE,
+ },
+ .probe = adf7242_probe,
+ .remove = adf7242_remove,
+};
+
+module_spi_driver(adf7242_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("ADF7242 IEEE802.15.4 Transceiver Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c
index 199a94a9c8bc..b1cd865ade2e 100644
--- a/drivers/net/ieee802154/atusb.c
+++ b/drivers/net/ieee802154/atusb.c
@@ -310,8 +310,7 @@ static void atusb_free_urbs(struct atusb *atusb)
urb = usb_get_from_anchor(&atusb->idle_urbs);
if (!urb)
break;
- if (urb->context)
- kfree_skb(urb->context);
+ kfree_skb(urb->context);
usb_free_urb(urb);
}
}
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c
index e65b60591317..d50add705a79 100644
--- a/drivers/net/ieee802154/cc2520.c
+++ b/drivers/net/ieee802154/cc2520.c
@@ -21,6 +21,8 @@
#include <linux/skbuff.h>
#include <linux/of_gpio.h>
#include <linux/ieee802154.h>
+#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
#include <net/mac802154.h>
#include <net/cfg802154.h>
@@ -189,6 +191,18 @@
#define CC2520_RXFIFOCNT 0x3E
#define CC2520_TXFIFOCNT 0x3F
+/* CC2520_FRMFILT0 */
+#define FRMFILT0_FRAME_FILTER_EN BIT(0)
+#define FRMFILT0_PAN_COORDINATOR BIT(1)
+
+/* CC2520_FRMCTRL0 */
+#define FRMCTRL0_AUTOACK BIT(5)
+#define FRMCTRL0_AUTOCRC BIT(6)
+
+/* CC2520_FRMCTRL1 */
+#define FRMCTRL1_SET_RXENMASK_ON_TX BIT(0)
+#define FRMCTRL1_IGNORE_TX_UNDERF BIT(1)
+
/* Driver private information */
struct cc2520_private {
struct spi_device *spi; /* SPI device structure */
@@ -201,6 +215,7 @@ struct cc2520_private {
struct work_struct fifop_irqwork;/* Workqueue for FIFOP */
spinlock_t lock; /* Lock for is_tx*/
struct completion tx_complete; /* Work completion for Tx */
+ bool promiscuous; /* Flag for promiscuous mode */
};
/* Generic Functions */
@@ -367,14 +382,14 @@ cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data)
}
static int
-cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len)
+cc2520_write_txfifo(struct cc2520_private *priv, u8 pkt_len, u8 *data, u8 len)
{
int status;
/* length byte must include FCS even
* if it is calculated in the hardware
*/
- int len_byte = len + 2;
+ int len_byte = pkt_len;
struct spi_message msg;
@@ -414,7 +429,7 @@ cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len)
}
static int
-cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi)
+cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len)
{
int status;
struct spi_message msg;
@@ -470,12 +485,25 @@ cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
unsigned long flags;
int rc;
u8 status = 0;
+ u8 pkt_len;
+
+ /* In promiscuous mode we disable AUTOCRC so we can get the raw CRC
+ * values on RX. This means we need to manually add the CRC on TX.
+ */
+ if (priv->promiscuous) {
+ u16 crc = crc_ccitt(0, skb->data, skb->len);
+
+ put_unaligned_le16(crc, skb_put(skb, 2));
+ pkt_len = skb->len;
+ } else {
+ pkt_len = skb->len + 2;
+ }
rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX);
if (rc)
goto err_tx;
- rc = cc2520_write_txfifo(priv, skb->data, skb->len);
+ rc = cc2520_write_txfifo(priv, pkt_len, skb->data, skb->len);
if (rc)
goto err_tx;
@@ -518,22 +546,62 @@ static int cc2520_rx(struct cc2520_private *priv)
u8 len = 0, lqi = 0, bytes = 1;
struct sk_buff *skb;
- cc2520_read_rxfifo(priv, &len, bytes, &lqi);
+ /* Read single length byte from the radio. */
+ cc2520_read_rxfifo(priv, &len, bytes);
- if (len < 2 || len > IEEE802154_MTU)
- return -EINVAL;
+ if (!ieee802154_is_valid_psdu_len(len)) {
+ /* Corrupted frame received, clear frame buffer by
+ * reading entire buffer.
+ */
+ dev_dbg(&priv->spi->dev, "corrupted frame received\n");
+ len = IEEE802154_MTU;
+ }
skb = dev_alloc_skb(len);
if (!skb)
return -ENOMEM;
- if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) {
+ if (cc2520_read_rxfifo(priv, skb_put(skb, len), len)) {
dev_dbg(&priv->spi->dev, "frame reception failed\n");
kfree_skb(skb);
return -EINVAL;
}
- skb_trim(skb, skb->len - 2);
+ /* In promiscuous mode, we configure the radio to include the
+ * CRC (AUTOCRC==0) and we pass on the packet unconditionally. If not
+ * in promiscuous mode, we check the CRC here, but leave the
+ * RSSI/LQI/CRC_OK bytes as they will get removed in the mac layer.
+ */
+ if (!priv->promiscuous) {
+ bool crc_ok;
+
+ /* Check if the CRC is valid. With AUTOCRC set, the most
+ * significant bit of the last byte returned from the CC2520
+ * is CRC_OK flag. See section 20.3.4 of the datasheet.
+ */
+ crc_ok = skb->data[len - 1] & BIT(7);
+
+ /* If we failed CRC drop the packet in the driver layer. */
+ if (!crc_ok) {
+ dev_dbg(&priv->spi->dev, "CRC check failed\n");
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* To calculate LQI, the lower 7 bits of the last byte (the
+ * correlation value provided by the radio) must be scaled to
+ * the range 0-255. According to section 20.6, the correlation
+ * value ranges from 50-110. Ideally this would be calibrated
+ * per hardware design, but we use roughly the datasheet values
+ * to get close enough while avoiding floating point.
+ */
+ lqi = skb->data[len - 1] & 0x7f;
+ if (lqi < 50)
+ lqi = 50;
+ else if (lqi > 113)
+ lqi = 113;
+ lqi = (lqi - 50) * 4;
+ }
ieee802154_rx_irqsafe(priv->hw, skb, lqi);
@@ -619,14 +687,19 @@ cc2520_filter(struct ieee802154_hw *hw,
}
if (changed & IEEE802154_AFILT_PANC_CHANGED) {
+ u8 frmfilt0;
+
dev_vdbg(&priv->spi->dev,
"cc2520_filter called for panc change\n");
+
+ cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
+
if (filt->pan_coord)
- ret = cc2520_write_register(priv, CC2520_FRMFILT0,
- 0x02);
+ frmfilt0 |= FRMFILT0_PAN_COORDINATOR;
else
- ret = cc2520_write_register(priv, CC2520_FRMFILT0,
- 0x00);
+ frmfilt0 &= ~FRMFILT0_PAN_COORDINATOR;
+
+ ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
}
return ret;
@@ -723,6 +796,30 @@ cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm)
return cc2520_cc2591_set_tx_power(priv, mbm);
}
+static int
+cc2520_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+ struct cc2520_private *priv = hw->priv;
+ u8 frmfilt0;
+
+ dev_dbg(&priv->spi->dev, "%s : mode %d\n", __func__, on);
+
+ priv->promiscuous = on;
+
+ cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
+
+ if (on) {
+ /* Disable automatic ACK, automatic CRC, and frame filtering. */
+ cc2520_write_register(priv, CC2520_FRMCTRL0, 0);
+ frmfilt0 &= ~FRMFILT0_FRAME_FILTER_EN;
+ } else {
+ cc2520_write_register(priv, CC2520_FRMCTRL0, FRMCTRL0_AUTOACK |
+ FRMCTRL0_AUTOCRC);
+ frmfilt0 |= FRMFILT0_FRAME_FILTER_EN;
+ }
+ return cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
+}
+
static const struct ieee802154_ops cc2520_ops = {
.owner = THIS_MODULE,
.start = cc2520_start,
@@ -732,6 +829,7 @@ static const struct ieee802154_ops cc2520_ops = {
.set_channel = cc2520_set_channel,
.set_hw_addr_filt = cc2520_filter,
.set_txpower = cc2520_set_txpower,
+ .set_promiscuous_mode = cc2520_set_promiscuous_mode,
};
static int cc2520_register(struct cc2520_private *priv)
@@ -749,7 +847,8 @@ static int cc2520_register(struct cc2520_private *priv)
/* We do support only 2.4 Ghz */
priv->hw->phy->supported.channels[0] = 0x7FFF800;
- priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
+ priv->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
+ IEEE802154_HW_PROMISCUOUS;
priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER;
@@ -919,6 +1018,11 @@ static int cc2520_hw_init(struct cc2520_private *priv)
}
/* Registers default value: section 28.1 in Datasheet */
+
+ /* Set the CCA threshold to -50 dBm. This seems to have been copied
+ * from the TinyOS CC2520 driver and is much higher than the -84 dBm
+ * threshold suggested in the datasheet.
+ */
ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A);
if (ret)
goto err_ret;
@@ -955,15 +1059,10 @@ static int cc2520_hw_init(struct cc2520_private *priv)
if (ret)
goto err_ret;
- ret = cc2520_write_register(priv, CC2520_FRMCTRL0, 0x60);
- if (ret)
- goto err_ret;
-
- ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 0x03);
- if (ret)
- goto err_ret;
-
- ret = cc2520_write_register(priv, CC2520_FRMFILT0, 0x00);
+ /* Configure registers correctly for this driver. */
+ ret = cc2520_write_register(priv, CC2520_FRMCTRL1,
+ FRMCTRL1_SET_RXENMASK_ON_TX |
+ FRMCTRL1_IGNORE_TX_UNDERF);
if (ret)
goto err_ret;
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index a9268db4e349..f94392d07126 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -88,7 +88,7 @@ static struct lock_class_key ipvlan_netdev_xmit_lock_key;
static struct lock_class_key ipvlan_netdev_addr_lock_key;
#define IPVLAN_FEATURES \
- (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \
NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \
NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)
diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c
index 6d2f55959c49..b977d6d33e74 100644
--- a/drivers/net/irda/toim3232-sir.c
+++ b/drivers/net/irda/toim3232-sir.c
@@ -130,16 +130,6 @@ static int toim3232delay = 150; /* default is 150 ms */
module_param(toim3232delay, int, 0);
MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay");
-#if 0
-static int toim3232flipdtr = 0; /* default is DTR high to reset */
-module_param(toim3232flipdtr, int, 0);
-MODULE_PARM_DESC(toim3232flipdtr, "toim3232 dongle invert DTR (Reset)");
-
-static int toim3232fliprts = 0; /* default is RTS high for baud change */
-module_param(toim3232fliptrs, int, 0);
-MODULE_PARM_DESC(toim3232fliprts, "toim3232 dongle invert RTS (BR/D)");
-#endif
-
static int toim3232_open(struct sir_dev *);
static int toim3232_close(struct sir_dev *);
static int toim3232_change_speed(struct sir_dev *, unsigned);
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index dc7d970bd1c0..a400288cb37b 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -175,7 +175,7 @@ static void loopback_setup(struct net_device *dev)
| NETIF_F_UFO
| NETIF_F_HW_CSUM
| NETIF_F_RXCSUM
- | NETIF_F_SCTP_CSUM
+ | NETIF_F_SCTP_CRC
| NETIF_F_HIGHDMA
| NETIF_F_LLTX
| NETIF_F_NETNS_LOCAL
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 06c8bfeaccd6..6a57a005e0ca 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -758,11 +758,11 @@ static struct lock_class_key macvlan_netdev_xmit_lock_key;
static struct lock_class_key macvlan_netdev_addr_lock_key;
#define ALWAYS_ON_FEATURES \
- (NETIF_F_SG | NETIF_F_GEN_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX | \
+ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX | \
NETIF_F_GSO_ROBUST)
#define MACVLAN_FEATURES \
- (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_LRO | \
NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \
NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 0fc521941c71..d636d051fac8 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -388,7 +388,7 @@ static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb)
* check, we either support them all or none.
*/
if (skb->ip_summed == CHECKSUM_PARTIAL &&
- !(features & NETIF_F_ALL_CSUM) &&
+ !(features & NETIF_F_CSUM_MASK) &&
skb_checksum_help(skb))
goto drop;
skb_queue_tail(&q->sk.sk_receive_queue, skb);
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index f31a4e25cf15..680e88f9915a 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -1,6 +1,6 @@
# Makefile for Linux PHY drivers
-libphy-objs := phy.o phy_device.o mdio_bus.o
+libphy-objs := phy.o phy_device.o mdio_bus.o mdio_device.o
obj-$(CONFIG_PHYLIB) += libphy.o
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
diff --git a/drivers/net/phy/amd.c b/drivers/net/phy/amd.c
index 65a488f82eb8..18141c022b13 100644
--- a/drivers/net/phy/amd.c
+++ b/drivers/net/phy/amd.c
@@ -72,7 +72,6 @@ static struct phy_driver am79c_driver[] = { {
.read_status = genphy_read_status,
.ack_interrupt = am79c_ack_interrupt,
.config_intr = am79c_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(am79c_driver);
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index f1936b7a7af6..09b0b0aa8d68 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -128,7 +128,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
{
.phy_id = PHY_ID_AQ2104,
@@ -141,7 +140,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
{
.phy_id = PHY_ID_AQR105,
@@ -154,7 +152,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
{
.phy_id = PHY_ID_AQR405,
@@ -167,7 +164,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
};
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 2d020a3ec0b5..8a8f6fb2880d 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -190,7 +190,7 @@ static int at803x_resume(struct phy_device *phydev)
static int at803x_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct at803x_priv *priv;
struct gpio_desc *gpiod_reset;
@@ -281,8 +281,8 @@ static void at803x_link_change_notify(struct phy_device *phydev)
at803x_context_restore(phydev, &context);
- dev_dbg(&phydev->dev, "%s(): phy was reset\n",
- __func__);
+ phydev_dbg(phydev, "%s(): phy was reset\n",
+ __func__);
priv->phy_reset = true;
}
} else {
@@ -310,9 +310,6 @@ static struct phy_driver at803x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = at803x_ack_interrupt,
.config_intr = at803x_config_intr,
- .driver = {
- .owner = THIS_MODULE,
- },
}, {
/* ATHEROS 8030 */
.phy_id = ATH8030_PHY_ID,
@@ -331,9 +328,6 @@ static struct phy_driver at803x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = at803x_ack_interrupt,
.config_intr = at803x_config_intr,
- .driver = {
- .owner = THIS_MODULE,
- },
}, {
/* ATHEROS 8031 */
.phy_id = ATH8031_PHY_ID,
@@ -352,9 +346,6 @@ static struct phy_driver at803x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = &at803x_ack_interrupt,
.config_intr = &at803x_config_intr,
- .driver = {
- .owner = THIS_MODULE,
- },
} };
module_phy_driver(at803x_driver);
diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c
index ddb377e53633..df0416db0b88 100644
--- a/drivers/net/phy/bcm-phy-lib.c
+++ b/drivers/net/phy/bcm-phy-lib.c
@@ -184,25 +184,25 @@ int bcm_phy_enable_eee(struct phy_device *phydev)
/* Enable EEE at PHY level */
val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (val < 0)
return val;
val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
- MDIO_MMD_AN, phydev->addr, (u32)val);
+ MDIO_MMD_AN, (u32)val);
/* Advertise EEE */
val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (val < 0)
return val;
val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr, (u32)val);
+ MDIO_MMD_AN, (u32)val);
return 0;
}
diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c
index 86b28052bf06..e741bf614c4e 100644
--- a/drivers/net/phy/bcm63xx.c
+++ b/drivers/net/phy/bcm63xx.c
@@ -56,7 +56,6 @@ static struct phy_driver bcm63xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
/* same phy as above, with just a different OUI */
.phy_id = 0x002bdc00,
@@ -69,7 +68,6 @@ static struct phy_driver bcm63xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
} };
module_phy_driver(bcm63xx_driver);
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 03d4809a9126..bf241a3ec5e5 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -170,7 +170,7 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
int ret = 0;
pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
- dev_name(&phydev->dev), phydev->drv->name, rev, patch);
+ phydev_name(phydev), phydev->drv->name, rev, patch);
/* Dummy read to a register to workaround an issue upon reset where the
* internal inverter may not allow the first MDIO transaction to pass
@@ -324,7 +324,6 @@ static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
.config_aneg = genphy_config_aneg, \
.read_status = genphy_read_status, \
.resume = bcm7xxx_28nm_resume, \
- .driver = { .owner = THIS_MODULE }, \
}
static struct phy_driver bcm7xxx_driver[] = {
@@ -346,7 +345,6 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM7429,
.phy_id_mask = 0xfffffff0,
@@ -359,7 +357,18 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
+}, {
+ .phy_id = PHY_ID_BCM7435,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM7435",
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = bcm7xxx_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .suspend = bcm7xxx_suspend,
+ .resume = bcm7xxx_config_init,
}, {
.phy_id = PHY_BCM_OUI_4,
.phy_id_mask = 0xffff0000,
@@ -372,7 +381,6 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_BCM_OUI_5,
.phy_id_mask = 0xffffff00,
@@ -385,7 +393,6 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
} };
static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
@@ -395,6 +402,7 @@ static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
{ PHY_ID_BCM7425, 0xfffffff0, },
{ PHY_ID_BCM7429, 0xfffffff0, },
{ PHY_ID_BCM7439, 0xfffffff0, },
+ { PHY_ID_BCM7435, 0xfffffff0, },
{ PHY_ID_BCM7445, 0xfffffff0, },
{ PHY_BCM_OUI_4, 0xffff0000 },
{ PHY_BCM_OUI_5, 0xffffff00 },
diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c
index 1eca20452f03..f7ebdcff53e4 100644
--- a/drivers/net/phy/bcm87xx.c
+++ b/drivers/net/phy/bcm87xx.c
@@ -40,10 +40,10 @@ static int bcm87xx_of_reg_init(struct phy_device *phydev)
const __be32 *paddr_end;
int len, ret;
- if (!phydev->dev.of_node)
+ if (!phydev->mdio.dev.of_node)
return 0;
- paddr = of_get_property(phydev->dev.of_node,
+ paddr = of_get_property(phydev->mdio.dev.of_node,
"broadcom,c45-reg-init", &len);
if (!paddr)
return 0;
@@ -163,8 +163,9 @@ static int bcm87xx_did_interrupt(struct phy_device *phydev)
reg = phy_read(phydev, BCM87XX_LASI_STATUS);
if (reg < 0) {
- dev_err(&phydev->dev,
- "Error: Read of BCM87XX_LASI_STATUS failed: %d\n", reg);
+ phydev_err(phydev,
+ "Error: Read of BCM87XX_LASI_STATUS failed: %d\n",
+ reg);
return 0;
}
return (reg & 1) != 0;
@@ -200,7 +201,6 @@ static struct phy_driver bcm87xx_driver[] = {
.config_intr = bcm87xx_config_intr,
.did_interrupt = bcm87xx_did_interrupt,
.match_phy_device = bcm8706_match_phy_device,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM8727,
.phy_id_mask = 0xffffffff,
@@ -213,7 +213,6 @@ static struct phy_driver bcm87xx_driver[] = {
.config_intr = bcm87xx_config_intr,
.did_interrupt = bcm87xx_did_interrupt,
.match_phy_device = bcm8727_match_phy_device,
- .driver = { .owner = THIS_MODULE },
} };
module_phy_driver(bcm87xx_driver);
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 3ce5d9514623..870327efccf7 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -460,7 +460,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5421,
.phy_id_mask = 0xfffffff0,
@@ -473,7 +472,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5461,
.phy_id_mask = 0xfffffff0,
@@ -486,7 +484,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM54616S,
.phy_id_mask = 0xfffffff0,
@@ -499,7 +496,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5464,
.phy_id_mask = 0xfffffff0,
@@ -512,7 +508,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5481,
.phy_id_mask = 0xfffffff0,
@@ -525,7 +520,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5482,
.phy_id_mask = 0xfffffff0,
@@ -538,7 +532,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = bcm5482_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM50610,
.phy_id_mask = 0xfffffff0,
@@ -551,7 +544,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM50610M,
.phy_id_mask = 0xfffffff0,
@@ -564,7 +556,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM57780,
.phy_id_mask = 0xfffffff0,
@@ -577,7 +568,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCMAC131,
.phy_id_mask = 0xfffffff0,
@@ -590,7 +580,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = brcm_fet_ack_interrupt,
.config_intr = brcm_fet_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5241,
.phy_id_mask = 0xfffffff0,
@@ -603,7 +592,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = brcm_fet_ack_interrupt,
.config_intr = brcm_fet_config_intr,
- .driver = { .owner = THIS_MODULE },
} };
module_phy_driver(broadcom_drivers);
diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c
index 27f5464899d4..d339c1afea77 100644
--- a/drivers/net/phy/cicada.c
+++ b/drivers/net/phy/cicada.c
@@ -114,7 +114,6 @@ static struct phy_driver cis820x_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &cis820x_ack_interrupt,
.config_intr = &cis820x_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x000fc440,
.name = "Cicada Cis8204",
@@ -126,7 +125,6 @@ static struct phy_driver cis820x_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &cis820x_ack_interrupt,
.config_intr = &cis820x_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(cis820x_driver);
diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c
index 2a328703b4ae..36e3e2033eca 100644
--- a/drivers/net/phy/davicom.c
+++ b/drivers/net/phy/davicom.c
@@ -156,7 +156,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x0181b8b0,
.name = "Davicom DM9161B/C",
@@ -168,7 +167,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x0181b8a0,
.name = "Davicom DM9161A",
@@ -180,7 +178,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x00181b80,
.name = "Davicom DM9131",
@@ -191,7 +188,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(dm91xx_driver);
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 47b711739ba9..180f69952779 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -220,9 +220,10 @@ static void rx_timestamp_work(struct work_struct *work);
#define BROADCAST_ADDR 31
-static inline int broadcast_write(struct mii_bus *bus, u32 regnum, u16 val)
+static inline int broadcast_write(struct phy_device *phydev, u32 regnum,
+ u16 val)
{
- return mdiobus_write(bus, BROADCAST_ADDR, regnum, val);
+ return mdiobus_write(phydev->mdio.bus, BROADCAST_ADDR, regnum, val);
}
/* Caller must hold extreg_lock. */
@@ -232,7 +233,7 @@ static int ext_read(struct phy_device *phydev, int page, u32 regnum)
int val;
if (dp83640->clock->page != page) {
- broadcast_write(phydev->bus, PAGESEL, page);
+ broadcast_write(phydev, PAGESEL, page);
dp83640->clock->page = page;
}
val = phy_read(phydev, regnum);
@@ -247,11 +248,11 @@ static void ext_write(int broadcast, struct phy_device *phydev,
struct dp83640_private *dp83640 = phydev->priv;
if (dp83640->clock->page != page) {
- broadcast_write(phydev->bus, PAGESEL, page);
+ broadcast_write(phydev, PAGESEL, page);
dp83640->clock->page = page;
}
if (broadcast)
- broadcast_write(phydev->bus, regnum, val);
+ broadcast_write(phydev, regnum, val);
else
phy_write(phydev, regnum, val);
}
@@ -1039,7 +1040,7 @@ static int choose_this_phy(struct dp83640_clock *clock,
if (chosen_phy == -1 && !clock->chosen)
return 1;
- if (chosen_phy == phydev->addr)
+ if (chosen_phy == phydev->mdio.addr)
return 1;
return 0;
@@ -1103,10 +1104,10 @@ static int dp83640_probe(struct phy_device *phydev)
struct dp83640_private *dp83640;
int err = -ENOMEM, i;
- if (phydev->addr == BROADCAST_ADDR)
+ if (phydev->mdio.addr == BROADCAST_ADDR)
return 0;
- clock = dp83640_clock_get_bus(phydev->bus);
+ clock = dp83640_clock_get_bus(phydev->mdio.bus);
if (!clock)
goto no_clock;
@@ -1132,7 +1133,8 @@ static int dp83640_probe(struct phy_device *phydev)
if (choose_this_phy(clock, phydev)) {
clock->chosen = dp83640;
- clock->ptp_clock = ptp_clock_register(&clock->caps, &phydev->dev);
+ clock->ptp_clock = ptp_clock_register(&clock->caps,
+ &phydev->mdio.dev);
if (IS_ERR(clock->ptp_clock)) {
err = PTR_ERR(clock->ptp_clock);
goto no_register;
@@ -1158,7 +1160,7 @@ static void dp83640_remove(struct phy_device *phydev)
struct list_head *this, *next;
struct dp83640_private *tmp, *dp83640 = phydev->priv;
- if (phydev->addr == BROADCAST_ADDR)
+ if (phydev->mdio.addr == BROADCAST_ADDR)
return;
enable_status_frames(phydev, false);
@@ -1490,12 +1492,11 @@ static struct phy_driver dp83640_driver = {
.hwtstamp = dp83640_hwtstamp,
.rxtstamp = dp83640_rxtstamp,
.txtstamp = dp83640_txtstamp,
- .driver = {.owner = THIS_MODULE,}
};
static int __init dp83640_init(void)
{
- return phy_driver_register(&dp83640_driver);
+ return phy_driver_register(&dp83640_driver, THIS_MODULE);
}
static void __exit dp83640_exit(void)
diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c
index 5ce9bef54468..5e14e629c597 100644
--- a/drivers/net/phy/dp83848.c
+++ b/drivers/net/phy/dp83848.c
@@ -88,8 +88,6 @@ static struct phy_driver dp83848_driver[] = {
/* IRQ related */
.ack_interrupt = dp83848_ack_interrupt,
.config_intr = dp83848_config_intr,
-
- .driver = { .owner = THIS_MODULE, },
},
};
module_phy_driver(dp83848_driver);
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 32f10662f4ac..2afa61b51d41 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -103,14 +103,11 @@ static int dp83867_config_intr(struct phy_device *phydev)
static int dp83867_of_init(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct device_node *of_node = dev->of_node;
int ret;
- if (!of_node && dev->parent->of_node)
- of_node = dev->parent->of_node;
-
- if (!phydev->dev.of_node)
+ if (!of_node)
return -ENODEV;
ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
@@ -140,7 +137,7 @@ static int dp83867_config_init(struct phy_device *phydev)
u16 val, delay;
if (!phydev->priv) {
- dp83867 = devm_kzalloc(&phydev->dev, sizeof(*dp83867),
+ dp83867 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83867),
GFP_KERNEL);
if (!dp83867)
return -ENOMEM;
@@ -163,7 +160,7 @@ static int dp83867_config_init(struct phy_device *phydev)
if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) &&
(phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) {
val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL,
- DP83867_DEVADDR, phydev->addr);
+ DP83867_DEVADDR);
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);
@@ -175,13 +172,13 @@ static int dp83867_config_init(struct phy_device *phydev)
val |= DP83867_RGMII_RX_CLK_DELAY_EN;
phy_write_mmd_indirect(phydev, DP83867_RGMIICTL,
- DP83867_DEVADDR, phydev->addr, val);
+ DP83867_DEVADDR, val);
delay = (dp83867->rx_id_delay |
(dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));
phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL,
- DP83867_DEVADDR, phydev->addr, delay);
+ DP83867_DEVADDR, delay);
}
return 0;
@@ -217,8 +214,6 @@ static struct phy_driver dp83867_driver[] = {
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = {.owner = THIS_MODULE,}
},
};
module_phy_driver(dp83867_driver);
diff --git a/drivers/net/phy/et1011c.c b/drivers/net/phy/et1011c.c
index a907743816a8..a9a4edfa23c8 100644
--- a/drivers/net/phy/et1011c.c
+++ b/drivers/net/phy/et1011c.c
@@ -95,7 +95,6 @@ static struct phy_driver et1011c_driver[] = { {
.flags = PHY_POLL,
.config_aneg = et1011c_config_aneg,
.read_status = et1011c_read_status,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(et1011c_driver);
diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index e23bf5b90e17..ab9c473d75ea 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -27,7 +27,6 @@
#define MII_REGS_NUM 29
struct fixed_mdio_bus {
- int irqs[PHY_MAX_ADDR];
struct mii_bus *mii_bus;
struct list_head phys;
};
@@ -198,11 +197,11 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
- if (!phydev || !phydev->bus)
+ if (!phydev || !phydev->mdio.bus)
return -EINVAL;
list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phydev->addr) {
+ if (fp->addr == phydev->mdio.addr) {
fp->link_update = link_update;
fp->phydev = phydev;
return 0;
@@ -220,11 +219,11 @@ int fixed_phy_update_state(struct phy_device *phydev,
struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
- if (!phydev || phydev->bus != fmb->mii_bus)
+ if (!phydev || phydev->mdio.bus != fmb->mii_bus)
return -EINVAL;
list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phydev->addr) {
+ if (fp->addr == phydev->mdio.addr) {
#define _UPD(x) if (changed->x) \
fp->status.x = status->x
_UPD(link);
@@ -256,7 +255,7 @@ int fixed_phy_add(unsigned int irq, int phy_addr,
memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM);
- fmb->irqs[phy_addr] = irq;
+ fmb->mii_bus->irq[phy_addr] = irq;
fp->addr = phy_addr;
fp->status = *status;
@@ -345,7 +344,7 @@ struct phy_device *fixed_phy_register(unsigned int irq,
}
of_node_get(np);
- phy->dev.of_node = np;
+ phy->mdio.dev.of_node = np;
phy->is_pseudo_fixed_link = true;
switch (status->speed) {
@@ -395,7 +394,6 @@ static int __init fixed_mdio_bus_init(void)
fmb->mii_bus->parent = &pdev->dev;
fmb->mii_bus->read = &fixed_mdio_read;
fmb->mii_bus->write = &fixed_mdio_write;
- fmb->mii_bus->irq = fmb->irqs;
ret = mdiobus_register(fmb->mii_bus);
if (ret)
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
index 0dbc445a5fa0..e5f251b91578 100644
--- a/drivers/net/phy/icplus.c
+++ b/drivers/net/phy/icplus.c
@@ -53,43 +53,43 @@ static int ip175c_config_init(struct phy_device *phydev)
if (full_reset_performed == 0) {
/* master reset */
- err = mdiobus_write(phydev->bus, 30, 0, 0x175c);
+ err = mdiobus_write(phydev->mdio.bus, 30, 0, 0x175c);
if (err < 0)
return err;
/* ensure no bus delays overlap reset period */
- err = mdiobus_read(phydev->bus, 30, 0);
+ err = mdiobus_read(phydev->mdio.bus, 30, 0);
/* data sheet specifies reset period is 2 msec */
mdelay(2);
/* enable IP175C mode */
- err = mdiobus_write(phydev->bus, 29, 31, 0x175c);
+ err = mdiobus_write(phydev->mdio.bus, 29, 31, 0x175c);
if (err < 0)
return err;
/* Set MII0 speed and duplex (in PHY mode) */
- err = mdiobus_write(phydev->bus, 29, 22, 0x420);
+ err = mdiobus_write(phydev->mdio.bus, 29, 22, 0x420);
if (err < 0)
return err;
/* reset switch ports */
for (i = 0; i < 5; i++) {
- err = mdiobus_write(phydev->bus, i,
+ err = mdiobus_write(phydev->mdio.bus, i,
MII_BMCR, BMCR_RESET);
if (err < 0)
return err;
}
for (i = 0; i < 5; i++)
- err = mdiobus_read(phydev->bus, i, MII_BMCR);
+ err = mdiobus_read(phydev->mdio.bus, i, MII_BMCR);
mdelay(2);
full_reset_performed = 1;
}
- if (phydev->addr != 4) {
+ if (phydev->mdio.addr != 4) {
phydev->state = PHY_RUNNING;
phydev->speed = SPEED_100;
phydev->duplex = DUPLEX_FULL;
@@ -184,7 +184,7 @@ static int ip101a_g_config_init(struct phy_device *phydev)
static int ip175c_read_status(struct phy_device *phydev)
{
- if (phydev->addr == 4) /* WAN port */
+ if (phydev->mdio.addr == 4) /* WAN port */
genphy_read_status(phydev);
else
/* Don't need to read status for switch ports */
@@ -195,7 +195,7 @@ static int ip175c_read_status(struct phy_device *phydev)
static int ip175c_config_aneg(struct phy_device *phydev)
{
- if (phydev->addr == 4) /* WAN port */
+ if (phydev->mdio.addr == 4) /* WAN port */
genphy_config_aneg(phydev);
return 0;
@@ -221,7 +221,6 @@ static struct phy_driver icplus_driver[] = {
.read_status = &ip175c_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x02430d90,
.name = "ICPlus IP1001",
@@ -233,7 +232,6 @@ static struct phy_driver icplus_driver[] = {
.read_status = &genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x02430c54,
.name = "ICPlus IP101A/G",
@@ -247,7 +245,6 @@ static struct phy_driver icplus_driver[] = {
.read_status = &genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(icplus_driver);
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
index a3a5a703635b..f6078376ef50 100644
--- a/drivers/net/phy/lxt.c
+++ b/drivers/net/phy/lxt.c
@@ -278,7 +278,6 @@ static struct phy_driver lxt97x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = lxt970_ack_interrupt,
.config_intr = lxt970_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001378e0,
.name = "LXT971",
@@ -289,7 +288,6 @@ static struct phy_driver lxt97x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = lxt971_ack_interrupt,
.config_intr = lxt971_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x00137a10,
.name = "LXT973-A2",
@@ -299,7 +297,6 @@ static struct phy_driver lxt97x_driver[] = {
.probe = lxt973_probe,
.config_aneg = lxt973_config_aneg,
.read_status = lxt973a2_read_status,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x00137a10,
.name = "LXT973",
@@ -309,7 +306,6 @@ static struct phy_driver lxt97x_driver[] = {
.probe = lxt973_probe,
.config_aneg = lxt973_config_aneg,
.read_status = genphy_read_status,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(lxt97x_driver);
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 0240552b50f3..e3eb96443c97 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -137,6 +137,22 @@ MODULE_DESCRIPTION("Marvell PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
+struct marvell_hw_stat {
+ const char *string;
+ u8 page;
+ u8 reg;
+ u8 bits;
+};
+
+static struct marvell_hw_stat marvell_hw_stats[] = {
+ { "phy_receive_errors", 0, 21, 16},
+ { "phy_idle_errors", 0, 10, 8 },
+};
+
+struct marvell_priv {
+ u64 stats[ARRAY_SIZE(marvell_hw_stats)];
+};
+
static int marvell_ack_interrupt(struct phy_device *phydev)
{
int err;
@@ -284,10 +300,11 @@ static int marvell_of_reg_init(struct phy_device *phydev)
const __be32 *paddr;
int len, i, saved_page, current_page, page_changed, ret;
- if (!phydev->dev.of_node)
+ if (!phydev->mdio.dev.of_node)
return 0;
- paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len);
+ paddr = of_get_property(phydev->mdio.dev.of_node,
+ "marvell,reg-init", &len);
if (!paddr || len < (4 * sizeof(*paddr)))
return 0;
@@ -986,12 +1003,80 @@ static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *w
return 0;
}
+static int marvell_get_sset_count(struct phy_device *phydev)
+{
+ return ARRAY_SIZE(marvell_hw_stats);
+}
+
+static void marvell_get_strings(struct phy_device *phydev, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
+ memcpy(data + i * ETH_GSTRING_LEN,
+ marvell_hw_stats[i].string, ETH_GSTRING_LEN);
+ }
+}
+
+#ifndef UINT64_MAX
+#define UINT64_MAX (u64)(~((u64)0))
+#endif
+static u64 marvell_get_stat(struct phy_device *phydev, int i)
+{
+ struct marvell_hw_stat stat = marvell_hw_stats[i];
+ struct marvell_priv *priv = phydev->priv;
+ int err, oldpage;
+ u64 val;
+
+ oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+ err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
+ stat.page);
+ if (err < 0)
+ return UINT64_MAX;
+
+ val = phy_read(phydev, stat.reg);
+ if (val < 0) {
+ val = UINT64_MAX;
+ } else {
+ val = val & ((1 << stat.bits) - 1);
+ priv->stats[i] += val;
+ val = priv->stats[i];
+ }
+
+ phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+
+ return val;
+}
+
+static void marvell_get_stats(struct phy_device *phydev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
+ data[i] = marvell_get_stat(phydev, i);
+}
+
+static int marvell_probe(struct phy_device *phydev)
+{
+ struct marvell_priv *priv;
+
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
static struct phy_driver marvell_drivers[] = {
{
.phy_id = MARVELL_PHY_ID_88E1101,
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1101",
.features = PHY_GBIT_FEATURES,
+ .probe = marvell_probe,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -999,7 +1084,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1112,
@@ -1007,6 +1094,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1112",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1111_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -1014,7 +1102,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1111,
@@ -1022,6 +1112,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1111",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1111_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &marvell_read_status,
@@ -1029,7 +1120,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1118,
@@ -1037,6 +1130,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1118",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1118_config_init,
.config_aneg = &m88e1118_config_aneg,
.read_status = &genphy_read_status,
@@ -1044,7 +1138,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = {.owner = THIS_MODULE,},
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1121R,
@@ -1052,6 +1148,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1121R",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1121_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1059,7 +1156,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1318S,
@@ -1067,6 +1166,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1318S",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1318_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1076,7 +1176,9 @@ static struct phy_driver marvell_drivers[] = {
.set_wol = &m88e1318_set_wol,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1145,
@@ -1084,6 +1186,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1145",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1145_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -1091,7 +1194,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1149R,
@@ -1099,6 +1204,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1149R",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1149_config_init,
.config_aneg = &m88e1118_config_aneg,
.read_status = &genphy_read_status,
@@ -1106,7 +1212,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1240,
@@ -1114,6 +1222,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1240",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1111_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -1121,7 +1230,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1116R,
@@ -1129,6 +1240,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1116R",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1116r_config_init,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
@@ -1136,7 +1248,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1510,
@@ -1144,6 +1258,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1510",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1151,7 +1266,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1540,
@@ -1159,6 +1276,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1540",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1166,7 +1284,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E3016,
@@ -1174,6 +1294,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E3016",
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &genphy_config_aneg,
.config_init = &m88e3016_config_init,
.aneg_done = &marvell_aneg_done,
@@ -1183,7 +1304,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
};
diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c
index 4bde5e728fe0..8c73b2e771dd 100644
--- a/drivers/net/phy/mdio-bcm-unimac.c
+++ b/drivers/net/phy/mdio-bcm-unimac.c
@@ -200,16 +200,10 @@ static int unimac_mdio_probe(struct platform_device *pdev)
bus->reset = unimac_mdio_reset;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
- bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto out_mdio_free;
- }
-
ret = of_mdiobus_register(bus, np);
if (ret) {
dev_err(&pdev->dev, "MDIO bus registration failed\n");
- goto out_mdio_irq;
+ goto out_mdio_free;
}
platform_set_drvdata(pdev, priv);
@@ -218,8 +212,6 @@ static int unimac_mdio_probe(struct platform_device *pdev)
return 0;
-out_mdio_irq:
- kfree(bus->irq);
out_mdio_free:
mdiobus_free(bus);
return ret;
@@ -230,7 +222,6 @@ static int unimac_mdio_remove(struct platform_device *pdev)
struct unimac_mdio_priv *priv = platform_get_drvdata(pdev);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
return 0;
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index 95f51d7267b3..27ab63064f95 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -159,7 +159,7 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
new_bus->phy_mask = pdata->phy_mask;
new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;
- new_bus->irq = pdata->irqs;
+ memcpy(new_bus->irq, pdata->irqs, sizeof(new_bus->irq));
new_bus->parent = dev;
if (new_bus->phy_mask == ~0)
diff --git a/drivers/net/phy/mdio-moxart.c b/drivers/net/phy/mdio-moxart.c
index f1fc51f655d9..5bb56d126693 100644
--- a/drivers/net/phy/mdio-moxart.c
+++ b/drivers/net/phy/mdio-moxart.c
@@ -130,13 +130,6 @@ static int moxart_mdio_probe(struct platform_device *pdev)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d-mii", pdev->name, pdev->id);
bus->parent = &pdev->dev;
- bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
- GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
/* Setting PHY_IGNORE_INTERRUPT here even if it has no effect,
* of_mdiobus_register() sets these PHY_POLL.
* Ideally, the interrupt from MAC controller could be used to
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 7f8e7662e28c..308ade0eb1b6 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -34,7 +34,6 @@ struct mdio_mux_child_bus {
struct mdio_mux_parent_bus *parent;
struct mdio_mux_child_bus *next;
int bus_number;
- int phy_irq[PHY_MAX_ADDR];
};
/*
@@ -157,7 +156,7 @@ int mdio_mux_init(struct device *dev,
break;
}
cb->mii_bus->priv = cb;
- cb->mii_bus->irq = cb->phy_irq;
+
cb->mii_bus->name = "mdio_mux";
snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x",
pb->parent_id, v);
diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c
index fcf4e4df7cc8..47d4f2f263d1 100644
--- a/drivers/net/phy/mdio-octeon.c
+++ b/drivers/net/phy/mdio-octeon.c
@@ -113,7 +113,6 @@ struct octeon_mdiobus {
resource_size_t mdio_phys;
resource_size_t regsize;
enum octeon_mdiobus_mode mode;
- int phy_irq[PHY_MAX_ADDR];
};
#ifdef CONFIG_CAVIUM_OCTEON_SOC
@@ -268,12 +267,13 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
static int octeon_mdiobus_probe(struct platform_device *pdev)
{
struct octeon_mdiobus *bus;
+ struct mii_bus *mii_bus;
struct resource *res_mem;
union cvmx_smix_en smi_en;
int err = -ENOENT;
- bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
- if (!bus)
+ mii_bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*bus));
+ if (!mii_bus)
return -ENOMEM;
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -282,6 +282,8 @@ static int octeon_mdiobus_probe(struct platform_device *pdev)
return -ENXIO;
}
+ bus = mii_bus->priv;
+ bus->mii_bus = mii_bus;
bus->mdio_phys = res_mem->start;
bus->regsize = resource_size(res_mem);
@@ -298,16 +300,11 @@ static int octeon_mdiobus_probe(struct platform_device *pdev)
return -ENOMEM;
}
- bus->mii_bus = mdiobus_alloc();
- if (!bus->mii_bus)
- goto fail;
-
smi_en.u64 = 0;
smi_en.s.en = 1;
oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN);
bus->mii_bus->priv = bus;
- bus->mii_bus->irq = bus->phy_irq;
bus->mii_bus->name = "mdio-octeon";
snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
bus->mii_bus->parent = &pdev->dev;
@@ -326,7 +323,6 @@ static int octeon_mdiobus_probe(struct platform_device *pdev)
return 0;
fail_register:
mdiobus_free(bus->mii_bus);
-fail:
smi_en.u64 = 0;
oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN);
return err;
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
index 15bc7f9ea224..f70522c35163 100644
--- a/drivers/net/phy/mdio-sun4i.c
+++ b/drivers/net/phy/mdio-sun4i.c
@@ -96,7 +96,7 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
struct mii_bus *bus;
struct sun4i_mdio_data *data;
struct resource *res;
- int ret, i;
+ int ret;
bus = mdiobus_alloc_size(sizeof(*data));
if (!bus)
@@ -108,16 +108,6 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
bus->parent = &pdev->dev;
- bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
- GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bus->irq[i] = PHY_POLL;
-
data = bus->priv;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->membase = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 88cb4592b6fb..0cba64f1ecf4 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -38,6 +38,48 @@
#include <asm/irq.h>
+int mdiobus_register_device(struct mdio_device *mdiodev)
+{
+ if (mdiodev->bus->mdio_map[mdiodev->addr])
+ return -EBUSY;
+
+ mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
+
+ return 0;
+}
+EXPORT_SYMBOL(mdiobus_register_device);
+
+int mdiobus_unregister_device(struct mdio_device *mdiodev)
+{
+ if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)
+ return -EINVAL;
+
+ mdiodev->bus->mdio_map[mdiodev->addr] = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(mdiobus_unregister_device);
+
+struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr)
+{
+ struct mdio_device *mdiodev = bus->mdio_map[addr];
+
+ if (!mdiodev)
+ return NULL;
+
+ if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY))
+ return NULL;
+
+ return container_of(mdiodev, struct phy_device, mdio);
+}
+EXPORT_SYMBOL(mdiobus_get_phy);
+
+bool mdiobus_is_registered_device(struct mii_bus *bus, int addr)
+{
+ return bus->mdio_map[addr];
+}
+EXPORT_SYMBOL(mdiobus_is_registered_device);
+
/**
* mdiobus_alloc_size - allocate a mii_bus structure
* @size: extra amount of memory to allocate for private storage.
@@ -51,6 +93,7 @@ struct mii_bus *mdiobus_alloc_size(size_t size)
struct mii_bus *bus;
size_t aligned_size = ALIGN(sizeof(*bus), NETDEV_ALIGN);
size_t alloc_size;
+ int i;
/* If we alloc extra space, it should be aligned */
if (size)
@@ -59,11 +102,16 @@ struct mii_bus *mdiobus_alloc_size(size_t size)
alloc_size = sizeof(*bus);
bus = kzalloc(alloc_size, GFP_KERNEL);
- if (bus) {
- bus->state = MDIOBUS_ALLOCATED;
- if (size)
- bus->priv = (void *)bus + aligned_size;
- }
+ if (!bus)
+ return NULL;
+
+ bus->state = MDIOBUS_ALLOCATED;
+ if (size)
+ bus->priv = (void *)bus + aligned_size;
+
+ /* Initialise the interrupts to polling */
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ bus->irq[i] = PHY_POLL;
return bus;
}
@@ -190,47 +238,48 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np)
}
EXPORT_SYMBOL(of_mdio_find_bus);
-/* Walk the list of subnodes of a mdio bus and look for a node that matches the
- * phy's address with its 'reg' property. If found, set the of_node pointer for
- * the phy. This allows auto-probed pyh devices to be supplied with information
- * passed in via DT.
+/* Walk the list of subnodes of a mdio bus and look for a node that
+ * matches the mdio device's address with its 'reg' property. If
+ * found, set the of_node pointer for the mdio device. This allows
+ * auto-probed phy devices to be supplied with information passed in
+ * via DT.
*/
-static void of_mdiobus_link_phydev(struct mii_bus *mdio,
- struct phy_device *phydev)
+static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
+ struct mdio_device *mdiodev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &mdiodev->dev;
struct device_node *child;
- if (dev->of_node || !mdio->dev.of_node)
+ if (dev->of_node || !bus->dev.of_node)
return;
- for_each_available_child_of_node(mdio->dev.of_node, child) {
+ for_each_available_child_of_node(bus->dev.of_node, child) {
int addr;
int ret;
ret = of_property_read_u32(child, "reg", &addr);
if (ret < 0) {
- dev_err(dev, "%s has invalid PHY address\n",
+ dev_err(dev, "%s has invalid MDIO address\n",
child->full_name);
continue;
}
- /* A PHY must have a reg property in the range [0-31] */
+ /* A MDIO device must have a reg property in the range [0-31] */
if (addr >= PHY_MAX_ADDR) {
- dev_err(dev, "%s PHY address %i is too large\n",
+ dev_err(dev, "%s MDIO address %i is too large\n",
child->full_name, addr);
continue;
}
- if (addr == phydev->addr) {
+ if (addr == mdiodev->addr) {
dev->of_node = child;
return;
}
}
}
#else /* !IS_ENABLED(CONFIG_OF_MDIO) */
-static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
- struct phy_device *phydev)
+static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
+ struct mdio_device *mdiodev)
{
}
#endif
@@ -243,12 +292,15 @@ static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
* Description: Called by a bus driver to bring up all the PHYs
* on a given bus, and attach them to the bus. Drivers should use
* mdiobus_register() rather than __mdiobus_register() unless they
- * need to pass a specific owner module.
+ * need to pass a specific owner module. MDIO devices which are not
+ * PHYs will not be brought up by this function. They are expected to
+ * to be explicitly listed in DT and instantiated by of_mdiobus_register().
*
* Returns 0 on success or < 0 on error.
*/
int __mdiobus_register(struct mii_bus *bus, struct module *owner)
{
+ struct mdio_device *mdiodev;
int i, err;
if (NULL == bus || NULL == bus->name ||
@@ -294,11 +346,12 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
error:
while (--i >= 0) {
- struct phy_device *phydev = bus->phy_map[i];
- if (phydev) {
- phy_device_remove(phydev);
- phy_device_free(phydev);
- }
+ mdiodev = bus->mdio_map[i];
+ if (!mdiodev)
+ continue;
+
+ mdiodev->device_remove(mdiodev);
+ mdiodev->device_free(mdiodev);
}
device_del(&bus->dev);
return err;
@@ -307,17 +360,19 @@ EXPORT_SYMBOL(__mdiobus_register);
void mdiobus_unregister(struct mii_bus *bus)
{
+ struct mdio_device *mdiodev;
int i;
BUG_ON(bus->state != MDIOBUS_REGISTERED);
bus->state = MDIOBUS_UNREGISTERED;
for (i = 0; i < PHY_MAX_ADDR; i++) {
- struct phy_device *phydev = bus->phy_map[i];
- if (phydev) {
- phy_device_remove(phydev);
- phy_device_free(phydev);
- }
+ mdiodev = bus->mdio_map[i];
+ if (!mdiodev)
+ continue;
+
+ mdiodev->device_remove(mdiodev);
+ mdiodev->device_free(mdiodev);
}
device_del(&bus->dev);
}
@@ -346,6 +401,18 @@ void mdiobus_free(struct mii_bus *bus)
}
EXPORT_SYMBOL(mdiobus_free);
+/**
+ * mdiobus_scan - scan a bus for MDIO devices.
+ * @bus: mii_bus to scan
+ * @addr: address on bus to scan
+ *
+ * This function scans the MDIO bus, looking for devices which can be
+ * identified using a vendor/product ID in registers 2 and 3. Not all
+ * MDIO devices have such registers, but PHY devices typically
+ * do. Hence this function assumes anything found is a PHY, or can be
+ * treated as a PHY. Other MDIO devices, such as switches, will
+ * probably not be found during the scan.
+ */
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
struct phy_device *phydev;
@@ -359,7 +426,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
* For DT, see if the auto-probed phy has a correspoding child
* in the bus node, and set the of_node pointer in this case.
*/
- of_mdiobus_link_phydev(bus, phydev);
+ of_mdiobus_link_mdiodev(bus, &phydev->mdio);
err = phy_device_register(phydev);
if (err) {
@@ -476,133 +543,56 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
EXPORT_SYMBOL(mdiobus_write);
/**
- * mdio_bus_match - determine if given PHY driver supports the given PHY device
- * @dev: target PHY device
- * @drv: given PHY driver
+ * mdio_bus_match - determine if given MDIO driver supports the given
+ * MDIO device
+ * @dev: target MDIO device
+ * @drv: given MDIO driver
*
- * Description: Given a PHY device, and a PHY driver, return 1 if
- * the driver supports the device. Otherwise, return 0.
+ * Description: Given a MDIO device, and a MDIO driver, return 1 if
+ * the driver supports the device. Otherwise, return 0. This may
+ * require calling the devices own match function, since different classes
+ * of MDIO devices have different match criteria.
*/
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
- struct phy_device *phydev = to_phy_device(dev);
- struct phy_driver *phydrv = to_phy_driver(drv);
- const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
- int i;
+ struct mdio_device *mdio = to_mdio_device(dev);
if (of_driver_match_device(dev, drv))
return 1;
- if (phydrv->match_phy_device)
- return phydrv->match_phy_device(phydev);
-
- if (phydev->is_c45) {
- for (i = 1; i < num_ids; i++) {
- if (!(phydev->c45_ids.devices_in_package & (1 << i)))
- continue;
+ if (mdio->bus_match)
+ return mdio->bus_match(dev, drv);
- if ((phydrv->phy_id & phydrv->phy_id_mask) ==
- (phydev->c45_ids.device_ids[i] &
- phydrv->phy_id_mask))
- return 1;
- }
- return 0;
- } else {
- return (phydrv->phy_id & phydrv->phy_id_mask) ==
- (phydev->phy_id & phydrv->phy_id_mask);
- }
+ return 0;
}
#ifdef CONFIG_PM
-
-static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
-{
- struct device_driver *drv = phydev->dev.driver;
- struct phy_driver *phydrv = to_phy_driver(drv);
- struct net_device *netdev = phydev->attached_dev;
-
- if (!drv || !phydrv->suspend)
- return false;
-
- /* PHY not attached? May suspend if the PHY has not already been
- * suspended as part of a prior call to phy_disconnect() ->
- * phy_detach() -> phy_suspend() because the parent netdev might be the
- * MDIO bus driver and clock gated at this point.
- */
- if (!netdev)
- return !phydev->suspended;
-
- /* Don't suspend PHY if the attched netdev parent may wakeup.
- * The parent may point to a PCI device, as in tg3 driver.
- */
- if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
- return false;
-
- /* Also don't suspend PHY if the netdev itself may wakeup. This
- * is the case for devices w/o underlaying pwr. mgmt. aware bus,
- * e.g. SoC devices.
- */
- if (device_may_wakeup(&netdev->dev))
- return false;
-
- return true;
-}
-
static int mdio_bus_suspend(struct device *dev)
{
- struct phy_device *phydev = to_phy_device(dev);
+ struct mdio_device *mdio = to_mdio_device(dev);
- /* We must stop the state machine manually, otherwise it stops out of
- * control, possibly with the phydev->lock held. Upon resume, netdev
- * may call phy routines that try to grab the same lock, and that may
- * lead to a deadlock.
- */
- if (phydev->attached_dev && phydev->adjust_link)
- phy_stop_machine(phydev);
+ if (mdio->pm_ops && mdio->pm_ops->suspend)
+ return mdio->pm_ops->suspend(dev);
- if (!mdio_bus_phy_may_suspend(phydev))
- return 0;
-
- return phy_suspend(phydev);
+ return 0;
}
static int mdio_bus_resume(struct device *dev)
{
- struct phy_device *phydev = to_phy_device(dev);
- int ret;
-
- if (!mdio_bus_phy_may_suspend(phydev))
- goto no_resume;
-
- ret = phy_resume(phydev);
- if (ret < 0)
- return ret;
+ struct mdio_device *mdio = to_mdio_device(dev);
-no_resume:
- if (phydev->attached_dev && phydev->adjust_link)
- phy_start_machine(phydev);
+ if (mdio->pm_ops && mdio->pm_ops->resume)
+ return mdio->pm_ops->resume(dev);
return 0;
}
static int mdio_bus_restore(struct device *dev)
{
- struct phy_device *phydev = to_phy_device(dev);
- struct net_device *netdev = phydev->attached_dev;
- int ret;
-
- if (!netdev)
- return 0;
+ struct mdio_device *mdio = to_mdio_device(dev);
- ret = phy_init_hw(phydev);
- if (ret < 0)
- return ret;
-
- /* The PHY needs to renegotiate. */
- phydev->link = 0;
- phydev->state = PHY_UP;
-
- phy_start_machine(phydev);
+ if (mdio->pm_ops && mdio->pm_ops->restore)
+ return mdio->pm_ops->restore(dev);
return 0;
}
@@ -623,52 +613,10 @@ static const struct dev_pm_ops mdio_bus_pm_ops = {
#endif /* CONFIG_PM */
-static ssize_t
-phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct phy_device *phydev = to_phy_device(dev);
-
- return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
-}
-static DEVICE_ATTR_RO(phy_id);
-
-static ssize_t
-phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct phy_device *phydev = to_phy_device(dev);
- const char *mode = NULL;
-
- if (phy_is_internal(phydev))
- mode = "internal";
- else
- mode = phy_modes(phydev->interface);
-
- return sprintf(buf, "%s\n", mode);
-}
-static DEVICE_ATTR_RO(phy_interface);
-
-static ssize_t
-phy_has_fixups_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct phy_device *phydev = to_phy_device(dev);
-
- return sprintf(buf, "%d\n", phydev->has_fixups);
-}
-static DEVICE_ATTR_RO(phy_has_fixups);
-
-static struct attribute *mdio_dev_attrs[] = {
- &dev_attr_phy_id.attr,
- &dev_attr_phy_interface.attr,
- &dev_attr_phy_has_fixups.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(mdio_dev);
-
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.match = mdio_bus_match,
.pm = MDIO_BUS_PM_OPS,
- .dev_groups = mdio_dev_groups,
};
EXPORT_SYMBOL(mdio_bus_type);
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
new file mode 100644
index 000000000000..9c88e6749b9a
--- /dev/null
+++ b/drivers/net/phy/mdio_device.c
@@ -0,0 +1,171 @@
+/* Framework for MDIO devices, other than PHYs.
+ *
+ * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/unistd.h>
+
+void mdio_device_free(struct mdio_device *mdiodev)
+{
+ put_device(&mdiodev->dev);
+}
+EXPORT_SYMBOL(mdio_device_free);
+
+static void mdio_device_release(struct device *dev)
+{
+ kfree(to_mdio_device(dev));
+}
+
+struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
+{
+ struct mdio_device *mdiodev;
+
+ /* We allocate the device, and initialize the default values */
+ mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL);
+ if (!mdiodev)
+ return ERR_PTR(-ENOMEM);
+
+ mdiodev->dev.release = mdio_device_release;
+ mdiodev->dev.parent = &bus->dev;
+ mdiodev->dev.bus = &mdio_bus_type;
+ mdiodev->device_free = mdio_device_free;
+ mdiodev->device_remove = mdio_device_remove;
+ mdiodev->bus = bus;
+ mdiodev->addr = addr;
+
+ dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
+
+ device_initialize(&mdiodev->dev);
+
+ return mdiodev;
+}
+EXPORT_SYMBOL(mdio_device_create);
+
+/**
+ * mdio_device_register - Register the mdio device on the MDIO bus
+ * @mdiodev: mdio_device structure to be added to the MDIO bus
+ */
+int mdio_device_register(struct mdio_device *mdiodev)
+{
+ int err;
+
+ dev_info(&mdiodev->dev, "mdio_device_register\n");
+
+ err = mdiobus_register_device(mdiodev);
+ if (err)
+ return err;
+
+ err = device_add(&mdiodev->dev);
+ if (err) {
+ pr_err("MDIO %d failed to add\n", mdiodev->addr);
+ goto out;
+ }
+
+ return 0;
+
+ out:
+ mdiobus_unregister_device(mdiodev);
+ return err;
+}
+EXPORT_SYMBOL(mdio_device_register);
+
+/**
+ * mdio_device_remove - Remove a previously registered mdio device from the
+ * MDIO bus
+ * @mdiodev: mdio_device structure to remove
+ *
+ * This doesn't free the mdio_device itself, it merely reverses the effects
+ * of mdio_device_register(). Use mdio_device_free() to free the device
+ * after calling this function.
+ */
+void mdio_device_remove(struct mdio_device *mdiodev)
+{
+ device_del(&mdiodev->dev);
+ mdiobus_unregister_device(mdiodev);
+}
+EXPORT_SYMBOL(mdio_device_remove);
+
+/**
+ * mdio_probe - probe an MDIO device
+ * @dev: device to probe
+ *
+ * Description: Take care of setting up the mdio_device structure
+ * and calling the driver to probe the device.
+ */
+static int mdio_probe(struct device *dev)
+{
+ struct mdio_device *mdiodev = to_mdio_device(dev);
+ struct device_driver *drv = mdiodev->dev.driver;
+ struct mdio_driver *mdiodrv = to_mdio_driver(drv);
+ int err = 0;
+
+ if (mdiodrv->probe)
+ err = mdiodrv->probe(mdiodev);
+
+ return err;
+}
+
+static int mdio_remove(struct device *dev)
+{
+ struct mdio_device *mdiodev = to_mdio_device(dev);
+ struct device_driver *drv = mdiodev->dev.driver;
+ struct mdio_driver *mdiodrv = to_mdio_driver(drv);
+
+ if (mdiodrv->remove)
+ mdiodrv->remove(mdiodev);
+
+ return 0;
+}
+
+/**
+ * mdio_driver_register - register an mdio_driver with the MDIO layer
+ * @new_driver: new mdio_driver to register
+ */
+int mdio_driver_register(struct mdio_driver *drv)
+{
+ struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
+ int retval;
+
+ pr_info("mdio_driver_register: %s\n", mdiodrv->driver.name);
+
+ mdiodrv->driver.bus = &mdio_bus_type;
+ mdiodrv->driver.probe = mdio_probe;
+ mdiodrv->driver.remove = mdio_remove;
+
+ retval = driver_register(&mdiodrv->driver);
+ if (retval) {
+ pr_err("%s: Error %d in registering driver\n",
+ mdiodrv->driver.name, retval);
+
+ return retval;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mdio_driver_register);
+
+void mdio_driver_unregister(struct mdio_driver *drv)
+{
+ struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
+
+ driver_unregister(&mdiodrv->driver);
+}
+EXPORT_SYMBOL(mdio_driver_unregister);
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index e13ad6cdcc22..03833dbfca67 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -73,6 +73,17 @@
#define PS_TO_REG 200
+struct kszphy_hw_stat {
+ const char *string;
+ u8 reg;
+ u8 bits;
+};
+
+static struct kszphy_hw_stat kszphy_hw_stats[] = {
+ { "phy_receive_errors", 21, 16},
+ { "phy_idle_errors", 10, 8 },
+};
+
struct kszphy_type {
u32 led_mode_reg;
u16 interrupt_level_mask;
@@ -86,6 +97,7 @@ struct kszphy_priv {
int led_mode;
bool rmii_ref_clk_sel;
bool rmii_ref_clk_sel_val;
+ u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
};
static const struct kszphy_type ksz8021_type = {
@@ -212,7 +224,7 @@ static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val)
rc = phy_write(phydev, reg, temp);
out:
if (rc < 0)
- dev_err(&phydev->dev, "failed to set led mode\n");
+ phydev_err(phydev, "failed to set led mode\n");
return rc;
}
@@ -231,7 +243,7 @@ static int kszphy_broadcast_disable(struct phy_device *phydev)
ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF);
out:
if (ret)
- dev_err(&phydev->dev, "failed to disable broadcast address\n");
+ phydev_err(phydev, "failed to disable broadcast address\n");
return ret;
}
@@ -251,7 +263,7 @@ static int kszphy_nand_tree_disable(struct phy_device *phydev)
ret & ~KSZPHY_OMSO_NAND_TREE_ON);
out:
if (ret)
- dev_err(&phydev->dev, "failed to disable NAND tree mode\n");
+ phydev_err(phydev, "failed to disable NAND tree mode\n");
return ret;
}
@@ -276,7 +288,8 @@ static int kszphy_config_init(struct phy_device *phydev)
if (priv->rmii_ref_clk_sel) {
ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val);
if (ret) {
- dev_err(&phydev->dev, "failed to set rmii reference clock\n");
+ phydev_err(phydev,
+ "failed to set rmii reference clock\n");
return ret;
}
}
@@ -337,7 +350,7 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev,
static int ksz9021_config_init(struct phy_device *phydev)
{
- const struct device *dev = &phydev->dev;
+ const struct device *dev = &phydev->mdio.dev;
const struct device_node *of_node = dev->of_node;
const struct device *dev_walker;
@@ -345,7 +358,7 @@ static int ksz9021_config_init(struct phy_device *phydev)
* properties in the MAC node. Walk up the tree of devices to
* find a device with an OF node.
*/
- dev_walker = &phydev->dev;
+ dev_walker = &phydev->mdio.dev;
do {
of_node = dev_walker->of_node;
dev_walker = dev_walker->parent;
@@ -458,7 +471,7 @@ static int ksz9031_center_flp_timing(struct phy_device *phydev)
static int ksz9031_config_init(struct phy_device *phydev)
{
- const struct device *dev = &phydev->dev;
+ const struct device *dev = &phydev->mdio.dev;
const struct device_node *of_node = dev->of_node;
static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"};
static const char *rx_data_skews[4] = {
@@ -470,9 +483,17 @@ static int ksz9031_config_init(struct phy_device *phydev)
"txd2-skew-ps", "txd3-skew-ps"
};
static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"};
+ const struct device *dev_walker;
- if (!of_node && dev->parent->of_node)
- of_node = dev->parent->of_node;
+ /* The Micrel driver has a deprecated option to place phy OF
+ * properties in the MAC node. Walk up the tree of devices to
+ * find a device with an OF node.
+ */
+ dev_walker = &phydev->mdio.dev;
+ do {
+ of_node = dev_walker->of_node;
+ dev_walker = dev_walker->parent;
+ } while (!of_node && dev_walker);
if (of_node) {
ksz9031_of_load_skew_values(phydev, of_node,
@@ -569,15 +590,60 @@ ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum,
{
}
+static int kszphy_get_sset_count(struct phy_device *phydev)
+{
+ return ARRAY_SIZE(kszphy_hw_stats);
+}
+
+static void kszphy_get_strings(struct phy_device *phydev, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) {
+ memcpy(data + i * ETH_GSTRING_LEN,
+ kszphy_hw_stats[i].string, ETH_GSTRING_LEN);
+ }
+}
+
+#ifndef UINT64_MAX
+#define UINT64_MAX (u64)(~((u64)0))
+#endif
+static u64 kszphy_get_stat(struct phy_device *phydev, int i)
+{
+ struct kszphy_hw_stat stat = kszphy_hw_stats[i];
+ struct kszphy_priv *priv = phydev->priv;
+ u64 val;
+
+ val = phy_read(phydev, stat.reg);
+ if (val < 0) {
+ val = UINT64_MAX;
+ } else {
+ val = val & ((1 << stat.bits) - 1);
+ priv->stats[i] += val;
+ val = priv->stats[i];
+ }
+
+ return val;
+}
+
+static void kszphy_get_stats(struct phy_device *phydev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++)
+ data[i] = kszphy_get_stat(phydev, i);
+}
+
static int kszphy_probe(struct phy_device *phydev)
{
const struct kszphy_type *type = phydev->drv->driver_data;
- const struct device_node *np = phydev->dev.of_node;
+ const struct device_node *np = phydev->mdio.dev.of_node;
struct kszphy_priv *priv;
struct clk *clk;
int ret;
- priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -592,15 +658,15 @@ static int kszphy_probe(struct phy_device *phydev)
priv->led_mode = -1;
if (priv->led_mode > 3) {
- dev_err(&phydev->dev, "invalid led mode: 0x%02x\n",
- priv->led_mode);
+ phydev_err(phydev, "invalid led mode: 0x%02x\n",
+ priv->led_mode);
priv->led_mode = -1;
}
} else {
priv->led_mode = -1;
}
- clk = devm_clk_get(&phydev->dev, "rmii-ref");
+ clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref");
/* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
if (!IS_ERR_OR_NULL(clk)) {
unsigned long rate = clk_get_rate(clk);
@@ -615,7 +681,8 @@ static int kszphy_probe(struct phy_device *phydev)
} else if (rate > 49500000 && rate < 50500000) {
priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz;
} else {
- dev_err(&phydev->dev, "Clock rate out of range: %ld\n", rate);
+ phydev_err(phydev, "Clock rate out of range: %ld\n",
+ rate);
return -EINVAL;
}
}
@@ -642,9 +709,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8021,
.phy_id_mask = 0x00ffffff,
@@ -659,9 +728,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8031,
.phy_id_mask = 0x00ffffff,
@@ -676,9 +747,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8041,
.phy_id_mask = 0x00fffff0,
@@ -693,9 +766,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8041RNLI,
.phy_id_mask = 0x00fffff0,
@@ -710,9 +785,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8051,
.phy_id_mask = 0x00fffff0,
@@ -727,9 +804,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8001,
.name = "Micrel KSZ8001 or KS8721",
@@ -743,9 +822,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8081,
.name = "Micrel KSZ8081 or KSZ8091",
@@ -759,9 +840,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8061,
.name = "Micrel KSZ8061",
@@ -773,9 +856,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ9021,
.phy_id_mask = 0x000ffffe,
@@ -788,11 +873,13 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
.read_mmd_indirect = ksz9021_rd_mmd_phyreg,
.write_mmd_indirect = ksz9021_wr_mmd_phyreg,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = PHY_ID_KSZ9031,
.phy_id_mask = 0x00fffff0,
@@ -805,9 +892,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = ksz9031_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = PHY_ID_KSZ8873MLL,
.phy_id_mask = 0x00fffff0,
@@ -817,9 +906,11 @@ static struct phy_driver ksphy_driver[] = {
.config_init = kszphy_config_init,
.config_aneg = ksz8873mll_config_aneg,
.read_status = ksz8873mll_read_status,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = PHY_ID_KSZ886X,
.phy_id_mask = 0x00fffff0,
@@ -829,9 +920,11 @@ static struct phy_driver ksphy_driver[] = {
.config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
} };
module_phy_driver(ksphy_driver);
diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c
index c0a20ebd083b..15f820648f82 100644
--- a/drivers/net/phy/microchip.c
+++ b/drivers/net/phy/microchip.c
@@ -68,7 +68,7 @@ int lan88xx_suspend(struct phy_device *phydev)
static int lan88xx_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct lan88xx_priv *priv;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -78,10 +78,9 @@ static int lan88xx_probe(struct phy_device *phydev)
priv->wolopts = 0;
/* these values can be used to identify internal PHY */
- priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID,
- 3, phydev->addr);
+ priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3);
priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV,
- 3, phydev->addr);
+ 3);
phydev->priv = priv;
@@ -90,7 +89,7 @@ static int lan88xx_probe(struct phy_device *phydev)
static void lan88xx_remove(struct phy_device *phydev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct lan88xx_priv *priv = phydev->priv;
if (priv)
@@ -130,8 +129,6 @@ static struct phy_driver microchip_phy_driver[] = {
.suspend = lan88xx_suspend,
.resume = genphy_resume,
.set_wol = lan88xx_set_wol,
-
- .driver = { .owner = THIS_MODULE, }
} };
module_phy_driver(microchip_phy_driver);
diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c
index 0a7b9c7f09a2..2a1b490bc587 100644
--- a/drivers/net/phy/national.c
+++ b/drivers/net/phy/national.c
@@ -140,7 +140,6 @@ static struct phy_driver dp83865_driver[] = { {
.read_status = genphy_read_status,
.ack_interrupt = ns_ack_interrupt,
.config_intr = ns_config_intr,
- .driver = {.owner = THIS_MODULE,}
} };
module_phy_driver(dp83865_driver);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 47cd306dbb3c..8763bb20988a 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -319,7 +319,7 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
{
u32 speed = ethtool_cmd_speed(cmd);
- if (cmd->phy_address != phydev->addr)
+ if (cmd->phy_address != phydev->mdio.addr)
return -EINVAL;
/* We make sure that we don't pass unsupported values in to the PHY */
@@ -375,7 +375,7 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
cmd->port = PORT_BNC;
else
cmd->port = PORT_MII;
- cmd->phy_address = phydev->addr;
+ cmd->phy_address = phydev->mdio.addr;
cmd->transceiver = phy_is_internal(phydev) ?
XCVR_INTERNAL : XCVR_EXTERNAL;
cmd->autoneg = phydev->autoneg;
@@ -403,16 +403,17 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
switch (cmd) {
case SIOCGMIIPHY:
- mii_data->phy_id = phydev->addr;
+ mii_data->phy_id = phydev->mdio.addr;
/* fall through */
case SIOCGMIIREG:
- mii_data->val_out = mdiobus_read(phydev->bus, mii_data->phy_id,
+ mii_data->val_out = mdiobus_read(phydev->mdio.bus,
+ mii_data->phy_id,
mii_data->reg_num);
return 0;
case SIOCSMIIREG:
- if (mii_data->phy_id == phydev->addr) {
+ if (mii_data->phy_id == phydev->mdio.addr) {
switch (mii_data->reg_num) {
case MII_BMCR:
if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {
@@ -445,10 +446,10 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
}
}
- mdiobus_write(phydev->bus, mii_data->phy_id,
+ mdiobus_write(phydev->mdio.bus, mii_data->phy_id,
mii_data->reg_num, val);
- if (mii_data->phy_id == phydev->addr &&
+ if (mii_data->phy_id == phydev->mdio.addr &&
mii_data->reg_num == MII_BMCR &&
val & BMCR_RESET)
return phy_init_hw(phydev);
@@ -643,7 +644,7 @@ int phy_start_interrupts(struct phy_device *phydev)
if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt",
phydev) < 0) {
pr_warn("%s: Can't get IRQ %d (PHY)\n",
- phydev->bus->name, phydev->irq);
+ phydev->mdio.bus->name, phydev->irq);
phydev->irq = PHY_POLL;
return 0;
}
@@ -995,8 +996,9 @@ void phy_state_machine(struct work_struct *work)
if (err < 0)
phy_error(phydev);
- dev_dbg(&phydev->dev, "PHY state change %s -> %s\n",
- phy_state_to_str(old_state), phy_state_to_str(phydev->state));
+ phydev_dbg(phydev, "PHY state change %s -> %s\n",
+ phy_state_to_str(old_state),
+ phy_state_to_str(phydev->state));
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,
PHY_STATE_TIME * HZ);
@@ -1028,7 +1030,6 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
* @phydev: The PHY device bus
* @prtad: MMD Address
* @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
*
* Description: it reads data from the MMD registers (clause 22 to access to
* clause 45) of the specified phy address.
@@ -1038,14 +1039,14 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
* 3) Write reg 13 // MMD Data Command for MMD DEVAD
* 3) Read reg 14 // Read MMD data
*/
-int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr)
+int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad)
{
struct phy_driver *phydrv = phydev->drv;
+ int addr = phydev->mdio.addr;
int value = -1;
if (!phydrv->read_mmd_indirect) {
- struct mii_bus *bus = phydev->bus;
+ struct mii_bus *bus = phydev->mdio.bus;
mutex_lock(&bus->mdio_lock);
mmd_phy_indirect(bus, prtad, devad, addr);
@@ -1065,7 +1066,6 @@ EXPORT_SYMBOL(phy_read_mmd_indirect);
* @phydev: The PHY device
* @prtad: MMD Address
* @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
* @data: data to write in the MMD register
*
* Description: Write data from the MMD registers of the specified
@@ -1077,12 +1077,13 @@ EXPORT_SYMBOL(phy_read_mmd_indirect);
* 3) Write reg 14 // Write MMD data
*/
void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr, u32 data)
+ int devad, u32 data)
{
struct phy_driver *phydrv = phydev->drv;
+ int addr = phydev->mdio.addr;
if (!phydrv->write_mmd_indirect) {
- struct mii_bus *bus = phydev->bus;
+ struct mii_bus *bus = phydev->mdio.bus;
mutex_lock(&bus->mdio_lock);
mmd_phy_indirect(bus, prtad, devad, addr);
@@ -1129,7 +1130,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
/* First check if the EEE ability is supported */
eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE,
- MDIO_MMD_PCS, phydev->addr);
+ MDIO_MMD_PCS);
if (eee_cap <= 0)
goto eee_exit_err;
@@ -1141,12 +1142,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
* the EEE advertising registers.
*/
eee_lp = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (eee_lp <= 0)
goto eee_exit_err;
eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (eee_adv <= 0)
goto eee_exit_err;
@@ -1160,15 +1161,13 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
* clock while it is signaling LPI.
*/
int val = phy_read_mmd_indirect(phydev, MDIO_CTRL1,
- MDIO_MMD_PCS,
- phydev->addr);
+ MDIO_MMD_PCS);
if (val < 0)
return val;
val |= MDIO_PCS_CTRL1_CLKSTOP_EN;
phy_write_mmd_indirect(phydev, MDIO_CTRL1,
- MDIO_MMD_PCS, phydev->addr,
- val);
+ MDIO_MMD_PCS, val);
}
return 0; /* EEE supported */
@@ -1187,8 +1186,7 @@ EXPORT_SYMBOL(phy_init_eee);
*/
int phy_get_eee_err(struct phy_device *phydev)
{
- return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR,
- MDIO_MMD_PCS, phydev->addr);
+ return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS);
}
EXPORT_SYMBOL(phy_get_eee_err);
@@ -1205,22 +1203,19 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
int val;
/* Get Supported EEE */
- val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE,
- MDIO_MMD_PCS, phydev->addr);
+ val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS);
if (val < 0)
return val;
data->supported = mmd_eee_cap_to_ethtool_sup_t(val);
/* Get advertisement EEE */
- val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr);
+ val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN);
if (val < 0)
return val;
data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
/* Get LP advertisement EEE */
- val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE,
- MDIO_MMD_AN, phydev->addr);
+ val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN);
if (val < 0)
return val;
data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
@@ -1240,8 +1235,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
{
int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised);
- phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN,
- phydev->addr, val);
+ phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val);
return 0;
}
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 0bfbabad4431..903737adfc01 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -43,15 +43,31 @@ MODULE_LICENSE("GPL");
void phy_device_free(struct phy_device *phydev)
{
- put_device(&phydev->dev);
+ put_device(&phydev->mdio.dev);
}
EXPORT_SYMBOL(phy_device_free);
+static void phy_mdio_device_free(struct mdio_device *mdiodev)
+{
+ struct phy_device *phydev;
+
+ phydev = container_of(mdiodev, struct phy_device, mdio);
+ phy_device_free(phydev);
+}
+
static void phy_device_release(struct device *dev)
{
kfree(to_phy_device(dev));
}
+static void phy_mdio_device_remove(struct mdio_device *mdiodev)
+{
+ struct phy_device *phydev;
+
+ phydev = container_of(mdiodev, struct phy_device, mdio);
+ phy_device_remove(phydev);
+}
+
enum genphy_driver {
GENPHY_DRV_1G,
GENPHY_DRV_10G,
@@ -63,9 +79,118 @@ static struct phy_driver genphy_driver[GENPHY_DRV_MAX];
static LIST_HEAD(phy_fixup_list);
static DEFINE_MUTEX(phy_fixup_lock);
+#ifdef CONFIG_PM
+static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
+{
+ struct device_driver *drv = phydev->mdio.dev.driver;
+ struct phy_driver *phydrv = to_phy_driver(drv);
+ struct net_device *netdev = phydev->attached_dev;
+
+ if (!drv || !phydrv->suspend)
+ return false;
+
+ /* PHY not attached? May suspend if the PHY has not already been
+ * suspended as part of a prior call to phy_disconnect() ->
+ * phy_detach() -> phy_suspend() because the parent netdev might be the
+ * MDIO bus driver and clock gated at this point.
+ */
+ if (!netdev)
+ return !phydev->suspended;
+
+ /* Don't suspend PHY if the attached netdev parent may wakeup.
+ * The parent may point to a PCI device, as in tg3 driver.
+ */
+ if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
+ return false;
+
+ /* Also don't suspend PHY if the netdev itself may wakeup. This
+ * is the case for devices w/o underlaying pwr. mgmt. aware bus,
+ * e.g. SoC devices.
+ */
+ if (device_may_wakeup(&netdev->dev))
+ return false;
+
+ return true;
+}
+
+static int mdio_bus_phy_suspend(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ /* We must stop the state machine manually, otherwise it stops out of
+ * control, possibly with the phydev->lock held. Upon resume, netdev
+ * may call phy routines that try to grab the same lock, and that may
+ * lead to a deadlock.
+ */
+ if (phydev->attached_dev && phydev->adjust_link)
+ phy_stop_machine(phydev);
+
+ if (!mdio_bus_phy_may_suspend(phydev))
+ return 0;
+
+ return phy_suspend(phydev);
+}
+
+static int mdio_bus_phy_resume(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ int ret;
+
+ if (!mdio_bus_phy_may_suspend(phydev))
+ goto no_resume;
+
+ ret = phy_resume(phydev);
+ if (ret < 0)
+ return ret;
+
+no_resume:
+ if (phydev->attached_dev && phydev->adjust_link)
+ phy_start_machine(phydev);
+
+ return 0;
+}
+
+static int mdio_bus_phy_restore(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct net_device *netdev = phydev->attached_dev;
+ int ret;
+
+ if (!netdev)
+ return 0;
+
+ ret = phy_init_hw(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* The PHY needs to renegotiate. */
+ phydev->link = 0;
+ phydev->state = PHY_UP;
+
+ phy_start_machine(phydev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mdio_bus_phy_pm_ops = {
+ .suspend = mdio_bus_phy_suspend,
+ .resume = mdio_bus_phy_resume,
+ .freeze = mdio_bus_phy_suspend,
+ .thaw = mdio_bus_phy_resume,
+ .restore = mdio_bus_phy_restore,
+};
+
+#define MDIO_BUS_PHY_PM_OPS (&mdio_bus_phy_pm_ops)
+
+#else
+
+#define MDIO_BUS_PHY_PM_OPS NULL
+
+#endif /* CONFIG_PM */
+
/**
* phy_register_fixup - creates a new phy_fixup and adds it to the list
- * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID)
+ * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID)
* @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
* It can also be PHY_ANY_UID
* @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
@@ -114,7 +239,7 @@ EXPORT_SYMBOL(phy_register_fixup_for_id);
*/
static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
{
- if (strcmp(fixup->bus_id, dev_name(&phydev->dev)) != 0)
+ if (strcmp(fixup->bus_id, phydev_name(phydev)) != 0)
if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
return 0;
@@ -148,18 +273,59 @@ static int phy_scan_fixups(struct phy_device *phydev)
return 0;
}
+static int phy_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct phy_driver *phydrv = to_phy_driver(drv);
+ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+ int i;
+
+ if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+ return 0;
+
+ if (phydrv->match_phy_device)
+ return phydrv->match_phy_device(phydev);
+
+ if (phydev->is_c45) {
+ for (i = 1; i < num_ids; i++) {
+ if (!(phydev->c45_ids.devices_in_package & (1 << i)))
+ continue;
+
+ if ((phydrv->phy_id & phydrv->phy_id_mask) ==
+ (phydev->c45_ids.device_ids[i] &
+ phydrv->phy_id_mask))
+ return 1;
+ }
+ return 0;
+ } else {
+ return (phydrv->phy_id & phydrv->phy_id_mask) ==
+ (phydev->phy_id & phydrv->phy_id_mask);
+ }
+}
+
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids)
{
struct phy_device *dev;
+ struct mdio_device *mdiodev;
/* We allocate the device, and initialize the default values */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
- dev->dev.release = phy_device_release;
+ mdiodev = &dev->mdio;
+ mdiodev->dev.release = phy_device_release;
+ mdiodev->dev.parent = &bus->dev;
+ mdiodev->dev.bus = &mdio_bus_type;
+ mdiodev->bus = bus;
+ mdiodev->pm_ops = MDIO_BUS_PHY_PM_OPS;
+ mdiodev->bus_match = phy_bus_match;
+ mdiodev->addr = addr;
+ mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
+ mdiodev->device_free = phy_mdio_device_free;
+ mdiodev->device_remove = phy_mdio_device_remove;
dev->speed = 0;
dev->duplex = -1;
@@ -171,15 +337,11 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
dev->autoneg = AUTONEG_ENABLE;
dev->is_c45 = is_c45;
- dev->addr = addr;
dev->phy_id = phy_id;
if (c45_ids)
dev->c45_ids = *c45_ids;
- dev->bus = bus;
- dev->dev.parent = &bus->dev;
- dev->dev.bus = &mdio_bus_type;
- dev->irq = bus->irq ? bus->irq[addr] : PHY_POLL;
- dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr);
+ dev->irq = bus->irq[addr];
+ dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
dev->state = PHY_DOWN;
@@ -199,7 +361,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
*/
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
- device_initialize(&dev->dev);
+ device_initialize(&mdiodev->dev);
return dev;
}
@@ -373,6 +535,48 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
}
EXPORT_SYMBOL(get_phy_device);
+static ssize_t
+phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
+}
+static DEVICE_ATTR_RO(phy_id);
+
+static ssize_t
+phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ const char *mode = NULL;
+
+ if (phy_is_internal(phydev))
+ mode = "internal";
+ else
+ mode = phy_modes(phydev->interface);
+
+ return sprintf(buf, "%s\n", mode);
+}
+static DEVICE_ATTR_RO(phy_interface);
+
+static ssize_t
+phy_has_fixups_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return sprintf(buf, "%d\n", phydev->has_fixups);
+}
+static DEVICE_ATTR_RO(phy_has_fixups);
+
+static struct attribute *phy_dev_attrs[] = {
+ &dev_attr_phy_id.attr,
+ &dev_attr_phy_interface.attr,
+ &dev_attr_phy_has_fixups.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(phy_dev);
+
/**
* phy_device_register - Register the phy device on the MDIO bus
* @phydev: phy_device structure to be added to the MDIO bus
@@ -381,28 +585,29 @@ int phy_device_register(struct phy_device *phydev)
{
int err;
- /* Don't register a phy if one is already registered at this address */
- if (phydev->bus->phy_map[phydev->addr])
- return -EINVAL;
- phydev->bus->phy_map[phydev->addr] = phydev;
+ err = mdiobus_register_device(&phydev->mdio);
+ if (err)
+ return err;
/* Run all of the fixups for this PHY */
err = phy_scan_fixups(phydev);
if (err) {
- pr_err("PHY %d failed to initialize\n", phydev->addr);
+ pr_err("PHY %d failed to initialize\n", phydev->mdio.addr);
goto out;
}
- err = device_add(&phydev->dev);
+ phydev->mdio.dev.groups = phy_dev_groups;
+
+ err = device_add(&phydev->mdio.dev);
if (err) {
- pr_err("PHY %d failed to add\n", phydev->addr);
+ pr_err("PHY %d failed to add\n", phydev->mdio.addr);
goto out;
}
return 0;
out:
- phydev->bus->phy_map[phydev->addr] = NULL;
+ mdiobus_unregister_device(&phydev->mdio);
return err;
}
EXPORT_SYMBOL(phy_device_register);
@@ -417,11 +622,8 @@ EXPORT_SYMBOL(phy_device_register);
*/
void phy_device_remove(struct phy_device *phydev)
{
- struct mii_bus *bus = phydev->bus;
- int addr = phydev->addr;
-
- device_del(&phydev->dev);
- bus->phy_map[addr] = NULL;
+ device_del(&phydev->mdio.dev);
+ mdiobus_unregister_device(&phydev->mdio);
}
EXPORT_SYMBOL(phy_device_remove);
@@ -431,11 +633,13 @@ EXPORT_SYMBOL(phy_device_remove);
*/
struct phy_device *phy_find_first(struct mii_bus *bus)
{
+ struct phy_device *phydev;
int addr;
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- if (bus->phy_map[addr])
- return bus->phy_map[addr];
+ phydev = mdiobus_get_phy(bus, addr);
+ if (phydev)
+ return phydev;
}
return NULL;
}
@@ -607,6 +811,33 @@ int phy_init_hw(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_init_hw);
+void phy_attached_info(struct phy_device *phydev)
+{
+ phy_attached_print(phydev, NULL);
+}
+EXPORT_SYMBOL(phy_attached_info);
+
+#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)"
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+{
+ if (!fmt) {
+ dev_info(&phydev->mdio.dev, ATTACHED_FMT "\n",
+ phydev->drv->name, phydev_name(phydev),
+ phydev->irq);
+ } else {
+ va_list ap;
+
+ dev_info(&phydev->mdio.dev, ATTACHED_FMT,
+ phydev->drv->name, phydev_name(phydev),
+ phydev->irq);
+
+ va_start(ap, fmt);
+ vprintk(fmt, ap);
+ va_end(ap);
+ }
+}
+EXPORT_SYMBOL(phy_attached_print);
+
/**
* phy_attach_direct - attach a network device to a given PHY device pointer
* @dev: network device to attach
@@ -625,8 +856,8 @@ EXPORT_SYMBOL(phy_init_hw);
int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
u32 flags, phy_interface_t interface)
{
- struct mii_bus *bus = phydev->bus;
- struct device *d = &phydev->dev;
+ struct mii_bus *bus = phydev->mdio.bus;
+ struct device *d = &phydev->mdio.dev;
int err;
if (!try_module_get(bus->owner)) {
@@ -641,9 +872,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
*/
if (!d->driver) {
if (phydev->is_c45)
- d->driver = &genphy_driver[GENPHY_DRV_10G].driver;
+ d->driver =
+ &genphy_driver[GENPHY_DRV_10G].mdiodrv.driver;
else
- d->driver = &genphy_driver[GENPHY_DRV_1G].driver;
+ d->driver =
+ &genphy_driver[GENPHY_DRV_1G].mdiodrv.driver;
err = d->driver->probe(d);
if (err >= 0)
@@ -744,8 +977,9 @@ void phy_detach(struct phy_device *phydev)
* real driver could be loaded
*/
for (i = 0; i < ARRAY_SIZE(genphy_driver); i++) {
- if (phydev->dev.driver == &genphy_driver[i].driver) {
- device_release_driver(&phydev->dev);
+ if (phydev->mdio.dev.driver ==
+ &genphy_driver[i].mdiodrv.driver) {
+ device_release_driver(&phydev->mdio.dev);
break;
}
}
@@ -754,16 +988,16 @@ void phy_detach(struct phy_device *phydev)
* The phydev might go away on the put_device() below, so avoid
* a use-after-free bug by reading the underlying bus first.
*/
- bus = phydev->bus;
+ bus = phydev->mdio.bus;
- put_device(&phydev->dev);
+ put_device(&phydev->mdio.dev);
module_put(bus->owner);
}
EXPORT_SYMBOL(phy_detach);
int phy_suspend(struct phy_device *phydev)
{
- struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
int ret = 0;
@@ -786,7 +1020,7 @@ EXPORT_SYMBOL(phy_suspend);
int phy_resume(struct phy_device *phydev)
{
- struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
int ret = 0;
if (phydrv->resume)
@@ -1303,7 +1537,7 @@ EXPORT_SYMBOL(phy_set_max_speed);
static void of_set_phy_supported(struct phy_device *phydev)
{
- struct device_node *node = phydev->dev.of_node;
+ struct device_node *node = phydev->mdio.dev.of_node;
u32 max_speed;
if (!IS_ENABLED(CONFIG_OF_MDIO))
@@ -1327,7 +1561,7 @@ static void of_set_phy_supported(struct phy_device *phydev)
static int phy_probe(struct device *dev)
{
struct phy_device *phydev = to_phy_device(dev);
- struct device_driver *drv = phydev->dev.driver;
+ struct device_driver *drv = phydev->mdio.dev.driver;
struct phy_driver *phydrv = to_phy_driver(drv);
int err = 0;
@@ -1382,17 +1616,20 @@ static int phy_remove(struct device *dev)
/**
* phy_driver_register - register a phy_driver with the PHY layer
* @new_driver: new phy_driver to register
+ * @owner: module owning this PHY
*/
-int phy_driver_register(struct phy_driver *new_driver)
+int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
{
int retval;
- new_driver->driver.name = new_driver->name;
- new_driver->driver.bus = &mdio_bus_type;
- new_driver->driver.probe = phy_probe;
- new_driver->driver.remove = phy_remove;
+ new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
+ new_driver->mdiodrv.driver.name = new_driver->name;
+ new_driver->mdiodrv.driver.bus = &mdio_bus_type;
+ new_driver->mdiodrv.driver.probe = phy_probe;
+ new_driver->mdiodrv.driver.remove = phy_remove;
+ new_driver->mdiodrv.driver.owner = owner;
- retval = driver_register(&new_driver->driver);
+ retval = driver_register(&new_driver->mdiodrv.driver);
if (retval) {
pr_err("%s: Error %d in registering driver\n",
new_driver->name, retval);
@@ -1406,12 +1643,13 @@ int phy_driver_register(struct phy_driver *new_driver)
}
EXPORT_SYMBOL(phy_driver_register);
-int phy_drivers_register(struct phy_driver *new_driver, int n)
+int phy_drivers_register(struct phy_driver *new_driver, int n,
+ struct module *owner)
{
int i, ret = 0;
for (i = 0; i < n; i++) {
- ret = phy_driver_register(new_driver + i);
+ ret = phy_driver_register(new_driver + i, owner);
if (ret) {
while (i-- > 0)
phy_driver_unregister(new_driver + i);
@@ -1424,7 +1662,7 @@ EXPORT_SYMBOL(phy_drivers_register);
void phy_driver_unregister(struct phy_driver *drv)
{
- driver_unregister(&drv->driver);
+ driver_unregister(&drv->mdiodrv.driver);
}
EXPORT_SYMBOL(phy_driver_unregister);
@@ -1452,7 +1690,6 @@ static struct phy_driver genphy_driver[] = {
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
@@ -1464,7 +1701,6 @@ static struct phy_driver genphy_driver[] = {
.read_status = gen10g_read_status,
.suspend = gen10g_suspend,
.resume = gen10g_resume,
- .driver = {.owner = THIS_MODULE, },
} };
static int __init phy_init(void)
@@ -1476,7 +1712,7 @@ static int __init phy_init(void)
return rc;
rc = phy_drivers_register(genphy_driver,
- ARRAY_SIZE(genphy_driver));
+ ARRAY_SIZE(genphy_driver), THIS_MODULE);
if (rc)
mdio_bus_exit();
diff --git a/drivers/net/phy/qsemi.c b/drivers/net/phy/qsemi.c
index be4c6f7c3645..d470db89e8dd 100644
--- a/drivers/net/phy/qsemi.c
+++ b/drivers/net/phy/qsemi.c
@@ -122,7 +122,6 @@ static struct phy_driver qs6612_driver[] = { {
.read_status = genphy_read_status,
.ack_interrupt = qs6612_ack_interrupt,
.config_intr = qs6612_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(qs6612_driver);
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index 43ab691362d4..aadd6e9f54ad 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -124,7 +124,6 @@ static struct phy_driver realtek_drvs[] = {
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc912,
.name = "RTL8211B Gigabit Ethernet",
@@ -135,7 +134,6 @@ static struct phy_driver realtek_drvs[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &rtl821x_ack_interrupt,
.config_intr = &rtl8211b_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc914,
.name = "RTL8211DN Gigabit Ethernet",
@@ -148,7 +146,6 @@ static struct phy_driver realtek_drvs[] = {
.config_intr = rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc915,
.name = "RTL8211E Gigabit Ethernet",
@@ -161,7 +158,6 @@ static struct phy_driver realtek_drvs[] = {
.config_intr = &rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc916,
.name = "RTL8211F Gigabit Ethernet",
@@ -175,7 +171,6 @@ static struct phy_driver realtek_drvs[] = {
.config_intr = &rtl8211f_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE },
},
};
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index dc2da8770918..e485f2653c82 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -44,7 +44,7 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev)
static int smsc_phy_config_init(struct phy_device *phydev)
{
int __maybe_unused len;
- struct device *dev __maybe_unused = &phydev->dev;
+ struct device *dev __maybe_unused = &phydev->mdio.dev;
struct device_node *of_node __maybe_unused = dev->of_node;
int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
int enable_energy = 1;
@@ -171,8 +171,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
.phy_id_mask = 0xfffffff0,
@@ -194,8 +192,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
.phy_id_mask = 0xfffffff0,
@@ -217,8 +213,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
.phy_id_mask = 0xfffffff0,
@@ -239,8 +233,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
.phy_id_mask = 0xfffffff0,
@@ -262,8 +254,27 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
+}, {
+ .phy_id = 0x0007c110,
+ .phy_id_mask = 0xfffffff0,
+ .name = "SMSC LAN8740",
+
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
+ | SUPPORTED_Asym_Pause),
+ .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+
+ /* basic functions */
+ .config_aneg = genphy_config_aneg,
+ .read_status = lan87xx_read_status,
+ .config_init = smsc_phy_config_init,
+ .soft_reset = smsc_phy_reset,
- .driver = { .owner = THIS_MODULE, }
+ /* IRQ related */
+ .ack_interrupt = smsc_phy_ack_interrupt,
+ .config_intr = smsc_phy_config_intr,
+
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
} };
module_phy_driver(smsc_phy_driver);
@@ -278,6 +289,7 @@ static struct mdio_device_id __maybe_unused smsc_tbl[] = {
{ 0x0007c0c0, 0xfffffff0 },
{ 0x0007c0d0, 0xfffffff0 },
{ 0x0007c0f0, 0xfffffff0 },
+ { 0x0007c110, 0xfffffff0 },
{ }
};
diff --git a/drivers/net/phy/ste10Xp.c b/drivers/net/phy/ste10Xp.c
index 3fc199b773e6..d00cfb64529e 100644
--- a/drivers/net/phy/ste10Xp.c
+++ b/drivers/net/phy/ste10Xp.c
@@ -95,7 +95,6 @@ static struct phy_driver ste10xp_pdriver[] = {
.config_intr = ste10Xp_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = {.owner = THIS_MODULE,}
}, {
.phy_id = STE100P_PHY_ID,
.phy_id_mask = 0xffffffff,
@@ -109,7 +108,6 @@ static struct phy_driver ste10xp_pdriver[] = {
.config_intr = ste10Xp_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = {.owner = THIS_MODULE,}
} };
module_phy_driver(ste10xp_pdriver);
diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c
index 07463fcca212..fb2cef764e9a 100644
--- a/drivers/net/phy/teranetics.c
+++ b/drivers/net/phy/teranetics.c
@@ -108,7 +108,6 @@ static struct phy_driver teranetics_driver[] = {
.config_aneg = teranetics_config_aneg,
.read_status = teranetics_read_status,
.match_phy_device = teranetics_match_phy_device,
- .driver = { .owner = THIS_MODULE,},
},
};
diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c
index dd295dbaa074..2e37eb337d48 100644
--- a/drivers/net/phy/vitesse.c
+++ b/drivers/net/phy/vitesse.c
@@ -236,7 +236,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8244,
.name = "Vitesse VSC8244",
@@ -248,7 +247,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8514,
.name = "Vitesse VSC8514",
@@ -260,7 +258,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8574,
.name = "Vitesse VSC8574",
@@ -272,7 +269,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8601,
.name = "Vitesse VSC8601",
@@ -284,7 +280,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8662,
.name = "Vitesse VSC8662",
@@ -296,7 +291,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
/* Vitesse 8221 */
.phy_id = PHY_ID_VSC8221,
@@ -309,7 +303,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
/* Vitesse 8211 */
.phy_id = PHY_ID_VSC8211,
@@ -322,7 +315,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(vsc82xx_driver);
diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c
index 040b8978d6ca..9c4b41a4df7d 100644
--- a/drivers/net/plip/plip.c
+++ b/drivers/net/plip/plip.c
@@ -1249,6 +1249,7 @@ static void plip_attach (struct parport *port)
struct net_device *dev;
struct net_local *nl;
char name[IFNAMSIZ];
+ struct pardev_cb plip_cb;
if ((parport[0] == -1 && (!timid || !port->devices)) ||
plip_searchfor(parport, port->number)) {
@@ -1273,9 +1274,15 @@ static void plip_attach (struct parport *port)
nl = netdev_priv(dev);
nl->dev = dev;
- nl->pardev = parport_register_device(port, dev->name, plip_preempt,
- plip_wakeup, plip_interrupt,
- 0, dev);
+
+ memset(&plip_cb, 0, sizeof(plip_cb));
+ plip_cb.private = dev;
+ plip_cb.preempt = plip_preempt;
+ plip_cb.wakeup = plip_wakeup;
+ plip_cb.irq_func = plip_interrupt;
+
+ nl->pardev = parport_register_dev_model(port, dev->name,
+ &plip_cb, unit);
if (!nl->pardev) {
printk(KERN_ERR "%s: parport_register failed\n", name);
@@ -1315,10 +1322,23 @@ static void plip_detach (struct parport *port)
/* Nothing to do */
}
+static int plip_probe(struct pardevice *par_dev)
+{
+ struct device_driver *drv = par_dev->dev.driver;
+ int len = strlen(drv->name);
+
+ if (strncmp(par_dev->name, drv->name, len))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver plip_driver = {
- .name = "plip",
- .attach = plip_attach,
- .detach = plip_detach
+ .name = "plip",
+ .probe = plip_probe,
+ .match_port = plip_attach,
+ .detach = plip_detach,
+ .devmodel = true,
};
static void __exit plip_cleanup_module (void)
@@ -1326,8 +1346,6 @@ static void __exit plip_cleanup_module (void)
struct net_device *dev;
int i;
- parport_unregister_driver (&plip_driver);
-
for (i=0; i < PLIP_MAX; i++) {
if ((dev = dev_plip[i])) {
struct net_local *nl = netdev_priv(dev);
@@ -1339,6 +1357,8 @@ static void __exit plip_cleanup_module (void)
dev_plip[i] = NULL;
}
}
+
+ parport_unregister_driver(&plip_driver);
}
#ifndef MODULE
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 9a863c6a6a33..fc8ad001bc94 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1138,9 +1138,15 @@ static const struct net_device_ops ppp_netdev_ops = {
.ndo_get_stats64 = ppp_get_stats64,
};
+static struct device_type ppp_type = {
+ .name = "ppp",
+};
+
static void ppp_setup(struct net_device *dev)
{
dev->netdev_ops = &ppp_netdev_ops;
+ SET_NETDEV_DEVTYPE(dev, &ppp_type);
+
dev->hard_header_len = PPP_HDRLEN;
dev->mtu = PPP_MRU;
dev->addr_len = 0;
@@ -2720,8 +2726,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit,
int ret = -ENOMEM;
int i;
- dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_UNKNOWN,
- ppp_setup);
+ dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_ENUM, ppp_setup);
if (!dev)
goto out1;
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 0a37f840fcc5..f3c63022eb3c 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -311,7 +311,7 @@ static void pppoe_flush_dev(struct net_device *dev)
lock_sock(sk);
if (po->pppoe_dev == dev &&
- sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
+ sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
pppox_unbind_sock(sk);
sk->sk_state_change(sk);
po->pppoe_dev = NULL;
@@ -500,27 +500,9 @@ static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev,
pn = pppoe_pernet(dev_net(dev));
po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
- if (po) {
- struct sock *sk = sk_pppox(po);
-
- bh_lock_sock(sk);
-
- /* If the user has locked the socket, just ignore
- * the packet. With the way two rcv protocols hook into
- * one socket family type, we cannot (easily) distinguish
- * what kind of SKB it is during backlog rcv.
- */
- if (sock_owned_by_user(sk) == 0) {
- /* We're no longer connect at the PPPOE layer,
- * and must wait for ppp channel to disconnect us.
- */
- sk->sk_state = PPPOX_ZOMBIE;
- }
-
- bh_unlock_sock(sk);
+ if (po)
if (!schedule_work(&po->proto.pppoe.padt_work))
- sock_put(sk);
- }
+ sock_put(sk_pppox(po));
abort:
kfree_skb(skb);
@@ -799,7 +781,7 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd,
struct pppox_sock *relay_po;
err = -EBUSY;
- if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD))
+ if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD))
break;
err = -ENOTCONN;
@@ -1226,4 +1208,4 @@ module_exit(pppoe_exit);
MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
MODULE_DESCRIPTION("PPP over Ethernet driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(PF_PPPOX);
+MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OE);
diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c
index 0e1b30622477..b9c8be6283d3 100644
--- a/drivers/net/ppp/pppox.c
+++ b/drivers/net/ppp/pppox.c
@@ -58,7 +58,7 @@ void pppox_unbind_sock(struct sock *sk)
{
/* Clear connection to ppp device, if attached. */
- if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
+ if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) {
ppp_unregister_channel(&pppox_sk(sk)->chan);
sk->sk_state = PPPOX_DEAD;
}
@@ -113,7 +113,7 @@ static int pppox_create(struct net *net, struct socket *sock, int protocol,
rc = -EPROTONOSUPPORT;
if (!pppox_protos[protocol])
- request_module("pppox-proto-%d", protocol);
+ request_module("net-pf-%d-proto-%d", PF_PPPOX, protocol);
if (!pppox_protos[protocol] ||
!try_module_get(pppox_protos[protocol]->owner))
goto out;
@@ -147,3 +147,4 @@ module_exit(pppox_exit);
MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_PPPOX);
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 597c53e0a2ec..90868ca5e341 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -724,3 +724,4 @@ module_exit(pptp_exit_module);
MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol");
MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
MODULE_LICENSE("GPL");
+MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_PPTP);
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 651d35ea22c5..2528331de193 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -91,10 +91,24 @@ void team_modeop_port_change_dev_addr(struct team *team,
}
EXPORT_SYMBOL(team_modeop_port_change_dev_addr);
+static void team_lower_state_changed(struct team_port *port)
+{
+ struct netdev_lag_lower_state_info info;
+
+ info.link_up = port->linkup;
+ info.tx_enabled = team_port_enabled(port);
+ netdev_lower_state_changed(port->dev, &info);
+}
+
static void team_refresh_port_linkup(struct team_port *port)
{
- port->linkup = port->user.linkup_enabled ? port->user.linkup :
- port->state.linkup;
+ bool new_linkup = port->user.linkup_enabled ? port->user.linkup :
+ port->state.linkup;
+
+ if (port->linkup != new_linkup) {
+ port->linkup = new_linkup;
+ team_lower_state_changed(port);
+ }
}
@@ -932,6 +946,7 @@ static void team_port_enable(struct team *team,
team->ops.port_enabled(team, port);
team_notify_peers(team);
team_mcast_rejoin(team);
+ team_lower_state_changed(port);
}
static void __reconstruct_port_hlist(struct team *team, int rm_index)
@@ -963,16 +978,21 @@ static void team_port_disable(struct team *team,
team_adjust_ops(team);
team_notify_peers(team);
team_mcast_rejoin(team);
+ team_lower_state_changed(port);
}
-#define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
+#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
NETIF_F_HIGHDMA | NETIF_F_LRO)
+#define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
+
static void __team_compute_features(struct team *team)
{
struct team_port *port;
u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL;
+ netdev_features_t enc_features = TEAM_ENC_FEATURES;
unsigned short max_hard_header_len = ETH_HLEN;
unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
IFF_XMIT_DST_RELEASE_PERM;
@@ -981,6 +1001,11 @@ static void __team_compute_features(struct team *team)
vlan_features = netdev_increment_features(vlan_features,
port->dev->vlan_features,
TEAM_VLAN_FEATURES);
+ enc_features =
+ netdev_increment_features(enc_features,
+ port->dev->hw_enc_features,
+ TEAM_ENC_FEATURES);
+
dst_release_flag &= port->dev->priv_flags;
if (port->dev->hard_header_len > max_hard_header_len)
@@ -988,6 +1013,7 @@ static void __team_compute_features(struct team *team)
}
team->dev->vlan_features = vlan_features;
+ team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL;
team->dev->hard_header_len = max_hard_header_len;
team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
@@ -1078,23 +1104,24 @@ static void team_port_disable_netpoll(struct team_port *port)
}
#endif
-static int team_upper_dev_link(struct net_device *dev,
- struct net_device *port_dev)
+static int team_upper_dev_link(struct team *team, struct team_port *port)
{
+ struct netdev_lag_upper_info lag_upper_info;
int err;
- err = netdev_master_upper_dev_link(port_dev, dev);
+ lag_upper_info.tx_type = team->mode->lag_tx_type;
+ err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
+ &lag_upper_info);
if (err)
return err;
- port_dev->priv_flags |= IFF_TEAM_PORT;
+ port->dev->priv_flags |= IFF_TEAM_PORT;
return 0;
}
-static void team_upper_dev_unlink(struct net_device *dev,
- struct net_device *port_dev)
+static void team_upper_dev_unlink(struct team *team, struct team_port *port)
{
- netdev_upper_dev_unlink(port_dev, dev);
- port_dev->priv_flags &= ~IFF_TEAM_PORT;
+ netdev_upper_dev_unlink(port->dev, team->dev);
+ port->dev->priv_flags &= ~IFF_TEAM_PORT;
}
static void __team_port_change_port_added(struct team_port *port, bool linkup);
@@ -1194,7 +1221,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_handler_register;
}
- err = team_upper_dev_link(dev, port_dev);
+ err = team_upper_dev_link(team, port);
if (err) {
netdev_err(dev, "Device %s failed to set upper link\n",
portname);
@@ -1220,7 +1247,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
return 0;
err_option_port_add:
- team_upper_dev_unlink(dev, port_dev);
+ team_upper_dev_unlink(team, port);
err_set_upper_link:
netdev_rx_handler_unregister(port_dev);
@@ -1264,7 +1291,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
team_port_disable(team, port);
list_del_rcu(&port->list);
- team_upper_dev_unlink(dev, port_dev);
+ team_upper_dev_unlink(team, port);
netdev_rx_handler_unregister(port_dev);
team_port_disable_netpoll(port);
vlan_vids_del_by_dev(port_dev, dev);
@@ -2054,6 +2081,7 @@ static void team_setup(struct net_device *dev)
dev->flags |= IFF_MULTICAST;
dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
dev->priv_flags |= IFF_NO_QUEUE;
+ dev->priv_flags |= IFF_TEAM;
/*
* Indicate we support unicast address filtering. That way core won't
@@ -2073,7 +2101,7 @@ static void team_setup(struct net_device *dev)
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER;
- dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM);
+ dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
dev->features |= dev->hw_features;
}
@@ -2420,9 +2448,13 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
struct nlattr *nl_option;
LIST_HEAD(opt_inst_list);
+ rtnl_lock();
+
team = team_nl_team_get(info);
- if (!team)
- return -EINVAL;
+ if (!team) {
+ err = -EINVAL;
+ goto rtnl_unlock;
+ }
err = -EINVAL;
if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
@@ -2549,7 +2581,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
team_put:
team_nl_team_put(team);
-
+rtnl_unlock:
+ rtnl_unlock();
return err;
}
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
index 40fd3381b693..3f189823ba3b 100644
--- a/drivers/net/team/team_mode_activebackup.c
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -127,6 +127,7 @@ static const struct team_mode ab_mode = {
.owner = THIS_MODULE,
.priv_size = sizeof(struct ab_priv),
.ops = &ab_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP,
};
static int __init ab_init_module(void)
diff --git a/drivers/net/team/team_mode_broadcast.c b/drivers/net/team/team_mode_broadcast.c
index c366cd299c06..302ff35b0cbc 100644
--- a/drivers/net/team/team_mode_broadcast.c
+++ b/drivers/net/team/team_mode_broadcast.c
@@ -56,6 +56,7 @@ static const struct team_mode bc_mode = {
.kind = "broadcast",
.owner = THIS_MODULE,
.ops = &bc_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_BROADCAST,
};
static int __init bc_init_module(void)
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index a1536d0d83a9..cdb19b385d42 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -661,6 +661,7 @@ static const struct team_mode lb_mode = {
.priv_size = sizeof(struct lb_priv),
.port_priv_size = sizeof(struct lb_port_priv),
.ops = &lb_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_HASH,
};
static int __init lb_init_module(void)
diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mode_random.c
index cd2f692b8074..215f845782db 100644
--- a/drivers/net/team/team_mode_random.c
+++ b/drivers/net/team/team_mode_random.c
@@ -46,6 +46,7 @@ static const struct team_mode rnd_mode = {
.kind = "random",
.owner = THIS_MODULE,
.ops = &rnd_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_RANDOM,
};
static int __init rnd_init_module(void)
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
index 53665850b59e..0aa234118c03 100644
--- a/drivers/net/team/team_mode_roundrobin.c
+++ b/drivers/net/team/team_mode_roundrobin.c
@@ -58,6 +58,7 @@ static const struct team_mode rr_mode = {
.owner = THIS_MODULE,
.priv_size = sizeof(struct rr_priv),
.ops = &rr_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_ROUNDROBIN,
};
static int __init rr_init_module(void)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index f0db770e8b2f..88bb8cc3555b 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1095,6 +1095,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
u32 rxhash;
ssize_t n;
+ if (!(tun->dev->flags & IFF_UP))
+ return -EIO;
+
if (!(tun->flags & IFF_NO_PI)) {
if (len < sizeof(pi))
return -EINVAL;
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index bd9acff1eb7b..0c5c22b84da8 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -118,7 +118,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
return 0;
}
if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
- netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
+ netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
size);
return 0;
}
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
index 5f18fcb8dcc7..224e7d82de6d 100644
--- a/drivers/net/usb/ax88172a.c
+++ b/drivers/net/usb/ax88172a.c
@@ -98,7 +98,7 @@ static void ax88172a_status(struct usbnet *dev, struct urb *urb)
static int ax88172a_init_mdio(struct usbnet *dev)
{
struct ax88172a_private *priv = dev->driver_priv;
- int ret, i;
+ int ret;
priv->mdio = mdiobus_alloc();
if (!priv->mdio) {
@@ -114,25 +114,15 @@ static int ax88172a_init_mdio(struct usbnet *dev)
snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
dev->udev->bus->busnum, dev->udev->devnum);
- priv->mdio->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!priv->mdio->irq) {
- ret = -ENOMEM;
- goto mfree;
- }
- for (i = 0; i < PHY_MAX_ADDR; i++)
- priv->mdio->irq[i] = PHY_POLL;
-
ret = mdiobus_register(priv->mdio);
if (ret) {
netdev_err(dev->net, "Could not register MDIO bus\n");
- goto ifree;
+ goto mfree;
}
netdev_info(dev->net, "registered mdio bus %s\n", priv->mdio->id);
return 0;
-ifree:
- kfree(priv->mdio->irq);
mfree:
mdiobus_free(priv->mdio);
return ret;
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 3da70bf9936a..7cba2c3759df 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -160,6 +160,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
info->u = header.usb_cdc_union_desc;
info->header = header.usb_cdc_header_desc;
info->ether = header.usb_cdc_ether_desc;
+ if (!info->u) {
+ if (rndis)
+ goto skip;
+ else /* in that case a quirk is mandatory */
+ goto bad_desc;
+ }
/* we need a master/control interface (what we're
* probed with) and a slave/data interface; union
* descriptors sort this all out.
@@ -256,7 +262,7 @@ skip:
goto bad_desc;
}
- } else if (!info->header || !info->u || (!rndis && !info->ether)) {
+ } else if (!info->header || (!rndis && !info->ether)) {
dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n",
info->header ? "" : "header ",
info->u ? "" : "union ",
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index e8a1144c5a8b..dc0212c3cc28 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -284,6 +284,48 @@ static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store
static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
+static ssize_t ndp_to_end_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+ return sprintf(buf, "%c\n", ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END ? 'Y' : 'N');
+}
+
+static ssize_t ndp_to_end_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+ bool enable;
+
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ /* no change? */
+ if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
+ return len;
+
+ if (enable && !ctx->delayed_ndp16) {
+ ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
+ if (!ctx->delayed_ndp16)
+ return -ENOMEM;
+ }
+
+ /* flush pending data before changing flag */
+ netif_tx_lock_bh(dev->net);
+ usbnet_start_xmit(NULL, dev->net);
+ spin_lock_bh(&ctx->mtx);
+ if (enable)
+ ctx->drvflags |= CDC_NCM_FLAG_NDP_TO_END;
+ else
+ ctx->drvflags &= ~CDC_NCM_FLAG_NDP_TO_END;
+ spin_unlock_bh(&ctx->mtx);
+ netif_tx_unlock_bh(dev->net);
+
+ return len;
+}
+static DEVICE_ATTR_RW(ndp_to_end);
+
#define NCM_PARM_ATTR(name, format, tocpu) \
static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \
{ \
@@ -306,6 +348,7 @@ NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu);
static struct attribute *cdc_ncm_sysfs_attrs[] = {
&dev_attr_min_tx_pkt.attr,
+ &dev_attr_ndp_to_end.attr,
&dev_attr_rx_max.attr,
&dev_attr_tx_max.attr,
&dev_attr_tx_timer_usecs.attr,
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 226668ead0d8..2ed53331bfb2 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -603,6 +603,59 @@ static int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset,
return 0;
}
+static int lan78xx_write_raw_otp(struct lan78xx_net *dev, u32 offset,
+ u32 length, u8 *data)
+{
+ int i;
+ int ret;
+ u32 buf;
+ unsigned long timeout;
+
+ ret = lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
+
+ if (buf & OTP_PWR_DN_PWRDN_N_) {
+ /* clear it and wait to be cleared */
+ ret = lan78xx_write_reg(dev, OTP_PWR_DN, 0);
+
+ timeout = jiffies + HZ;
+ do {
+ udelay(1);
+ ret = lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
+ if (time_after(jiffies, timeout)) {
+ netdev_warn(dev->net,
+ "timeout on OTP_PWR_DN completion");
+ return -EIO;
+ }
+ } while (buf & OTP_PWR_DN_PWRDN_N_);
+ }
+
+ /* set to BYTE program mode */
+ ret = lan78xx_write_reg(dev, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_);
+
+ for (i = 0; i < length; i++) {
+ ret = lan78xx_write_reg(dev, OTP_ADDR1,
+ ((offset + i) >> 8) & OTP_ADDR1_15_11);
+ ret = lan78xx_write_reg(dev, OTP_ADDR2,
+ ((offset + i) & OTP_ADDR2_10_3));
+ ret = lan78xx_write_reg(dev, OTP_PRGM_DATA, data[i]);
+ ret = lan78xx_write_reg(dev, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_);
+ ret = lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_);
+
+ timeout = jiffies + HZ;
+ do {
+ udelay(1);
+ ret = lan78xx_read_reg(dev, OTP_STATUS, &buf);
+ if (time_after(jiffies, timeout)) {
+ netdev_warn(dev->net,
+ "Timeout on OTP_STATUS completion");
+ return -EIO;
+ }
+ } while (buf & OTP_STATUS_BUSY_);
+ }
+
+ return 0;
+}
+
static int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset,
u32 length, u8 *data)
{
@@ -969,7 +1022,7 @@ static int lan78xx_ethtool_set_eeprom(struct net_device *netdev,
(ee->offset == 0) &&
(ee->len == 512) &&
(data[0] == OTP_INDICATOR_1))
- return lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data);
+ return lan78xx_write_raw_otp(dev, ee->offset, ee->len, data);
return -EINVAL;
}
@@ -1458,12 +1511,6 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)
snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
dev->udev->bus->busnum, dev->udev->devnum);
- dev->mdiobus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!dev->mdiobus->irq) {
- ret = -ENOMEM;
- goto exit1;
- }
-
/* handle our own interrupt */
for (i = 0; i < PHY_MAX_ADDR; i++)
dev->mdiobus->irq[i] = PHY_IGNORE_INTERRUPT;
@@ -1479,13 +1526,11 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)
ret = mdiobus_register(dev->mdiobus);
if (ret) {
netdev_err(dev->net, "can't register MDIO bus\n");
- goto exit2;
+ goto exit1;
}
netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id);
return 0;
-exit2:
- kfree(dev->mdiobus->irq);
exit1:
mdiobus_free(dev->mdiobus);
return ret;
@@ -1494,7 +1539,6 @@ exit1:
static void lan78xx_remove_mdio(struct lan78xx_net *dev)
{
mdiobus_unregister(dev->mdiobus);
- kfree(dev->mdiobus->irq);
mdiobus_free(dev->mdiobus);
}
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 5fccc5a8153f..23e9880791fc 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -14,7 +14,9 @@
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
#include <linux/mii.h>
+#include <linux/rtnetlink.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/usb/usbnet.h>
@@ -48,11 +50,100 @@
struct qmi_wwan_state {
struct usb_driver *subdriver;
atomic_t pmcount;
- unsigned long unused;
+ unsigned long flags;
struct usb_interface *control;
struct usb_interface *data;
};
+enum qmi_wwan_flags {
+ QMI_WWAN_FLAG_RAWIP = 1 << 0,
+};
+
+static void qmi_wwan_netdev_setup(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ if (info->flags & QMI_WWAN_FLAG_RAWIP) {
+ net->header_ops = NULL; /* No header */
+ net->type = ARPHRD_NONE;
+ net->hard_header_len = 0;
+ net->addr_len = 0;
+ net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ netdev_dbg(net, "mode: raw IP\n");
+ } else if (!net->header_ops) { /* don't bother if already set */
+ ether_setup(net);
+ netdev_dbg(net, "mode: Ethernet\n");
+ }
+
+ /* recalculate buffers after changing hard_header_len */
+ usbnet_change_mtu(net, net->mtu);
+}
+
+static ssize_t raw_ip_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ return sprintf(buf, "%c\n", info->flags & QMI_WWAN_FLAG_RAWIP ? 'Y' : 'N');
+}
+
+static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct qmi_wwan_state *info = (void *)&dev->data;
+ bool enable;
+ int ret;
+
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ /* no change? */
+ if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP))
+ return len;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ /* we don't want to modify a running netdev */
+ if (netif_running(dev->net)) {
+ netdev_err(dev->net, "Cannot change a running device\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* let other drivers deny the change */
+ ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net);
+ ret = notifier_to_errno(ret);
+ if (ret) {
+ netdev_err(dev->net, "Type change was refused\n");
+ goto err;
+ }
+
+ if (enable)
+ info->flags |= QMI_WWAN_FLAG_RAWIP;
+ else
+ info->flags &= ~QMI_WWAN_FLAG_RAWIP;
+ qmi_wwan_netdev_setup(dev->net);
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net);
+ ret = len;
+err:
+ rtnl_unlock();
+ return ret;
+}
+
+static DEVICE_ATTR_RW(raw_ip);
+
+static struct attribute *qmi_wwan_sysfs_attrs[] = {
+ &dev_attr_raw_ip.attr,
+ NULL,
+};
+
+static struct attribute_group qmi_wwan_sysfs_attr_group = {
+ .name = "qmi",
+ .attrs = qmi_wwan_sysfs_attrs,
+};
+
/* default ethernet address used by the modem */
static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3};
@@ -80,6 +171,8 @@ static const u8 buggy_fw_addr[ETH_ALEN] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00};
*/
static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
+ struct qmi_wwan_state *info = (void *)&dev->data;
+ bool rawip = info->flags & QMI_WWAN_FLAG_RAWIP;
__be16 proto;
/* This check is no longer done by usbnet */
@@ -94,15 +187,25 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
proto = htons(ETH_P_IPV6);
break;
case 0x00:
+ if (rawip)
+ return 0;
if (is_multicast_ether_addr(skb->data))
return 1;
/* possibly bogus destination - rewrite just in case */
skb_reset_mac_header(skb);
goto fix_dest;
default:
+ if (rawip)
+ return 0;
/* pass along other packets without modifications */
return 1;
}
+ if (rawip) {
+ skb->dev = dev->net; /* normally set by eth_type_trans */
+ skb->protocol = proto;
+ return 1;
+ }
+
if (skb_headroom(skb) < ETH_HLEN)
return 0;
skb_push(skb, ETH_HLEN);
@@ -223,6 +326,20 @@ err:
return rv;
}
+/* Send CDC SetControlLineState request, setting or clearing the DTR.
+ * "Required for Autoconnect and 9x30 to wake up" according to the
+ * GobiNet driver. The requirement has been verified on an MDM9230
+ * based Sierra Wireless MC7455
+ */
+static int qmi_wwan_change_dtr(struct usbnet *dev, bool on)
+{
+ u8 intf = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ return usbnet_write_cmd(dev, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ on ? 0x01 : 0x00, intf, NULL, 0);
+}
+
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status = -1;
@@ -257,7 +374,10 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
"bogus CDC Union: master=%u, slave=%u\n",
cdc_union->bMasterInterface0,
cdc_union->bSlaveInterface0);
- goto err;
+
+ /* ignore and continue... */
+ cdc_union = NULL;
+ info->data = intf;
}
}
@@ -280,6 +400,24 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
usb_driver_release_interface(driver, info->data);
}
+ /* disabling remote wakeup on MDM9x30 devices has the same
+ * effect as clearing DTR. The device will not respond to QMI
+ * requests until we set DTR again. This is similar to a
+ * QMI_CTL SYNC request, clearing a lot of firmware state
+ * including the client ID allocations.
+ *
+ * Our usage model allows a session to span multiple
+ * open/close events, so we must prevent the firmware from
+ * clearing out state the clients might need.
+ *
+ * MDM9x30 is the first QMI chipset with USB3 support. Abuse
+ * this fact to enable the quirk.
+ */
+ if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+ qmi_wwan_manage_power(dev, 1);
+ qmi_wwan_change_dtr(dev, true);
+ }
+
/* Never use the same address on both ends of the link, even if the
* buggy firmware told us to. Or, if device is assigned the well-known
* buggy firmware MAC address, replace it with a random address,
@@ -294,6 +432,7 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */
}
dev->net->netdev_ops = &qmi_wwan_netdev_ops;
+ dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group;
err:
return status;
}
@@ -307,6 +446,12 @@ static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
if (info->subdriver && info->subdriver->disconnect)
info->subdriver->disconnect(info->control);
+ /* disable MDM9x30 quirk */
+ if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+ qmi_wwan_change_dtr(dev, false);
+ qmi_wwan_manage_power(dev, 0);
+ }
+
/* allow user to unbind using either control or data */
if (intf == info->control)
other = info->data;
@@ -715,8 +860,6 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x1199, 0x9056, 8)}, /* Sierra Wireless Modem */
{QMI_FIXED_INTF(0x1199, 0x9057, 8)},
{QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */
- {QMI_FIXED_INTF(0x1199, 0x9070, 8)}, /* Sierra Wireless MC74xx/EM74xx */
- {QMI_FIXED_INTF(0x1199, 0x9070, 10)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
@@ -743,6 +886,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
{QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
+ {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 2fb637ad594a..d1f78c2c97aa 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -25,12 +25,13 @@
#include <uapi/linux/mdio.h>
#include <linux/mdio.h>
#include <linux/usb/cdc.h>
+#include <linux/suspend.h>
/* Information for net-next */
#define NETNEXT_VERSION "08"
/* Information for net */
-#define NET_VERSION "2"
+#define NET_VERSION "3"
#define DRIVER_VERSION "v1." NETNEXT_VERSION "." NET_VERSION
#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
@@ -604,6 +605,9 @@ struct r8152 {
struct delayed_work schedule;
struct mii_if_info mii;
struct mutex control; /* use for hw setting */
+#ifdef CONFIG_PM_SLEEP
+ struct notifier_block pm_notifier;
+#endif
struct rtl_ops {
void (*init)(struct r8152 *);
@@ -1938,7 +1942,6 @@ static void _rtl8152_set_rx_mode(struct net_device *netdev)
__le32 tmp[2];
u32 ocp_data;
- clear_bit(RTL8152_SET_RX_MODE, &tp->flags);
netif_stop_queue(netdev);
ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
ocp_data &= ~RCR_ACPT_ALL;
@@ -1986,7 +1989,7 @@ rtl8152_features_check(struct sk_buff *skb, struct net_device *dev,
int offset = skb_transport_offset(skb);
if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) && offset > max_offset)
- features &= ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz)
features &= ~NETIF_F_GSO_MASK;
@@ -2424,8 +2427,6 @@ static void rtl_phy_reset(struct r8152 *tp)
u16 data;
int i;
- clear_bit(PHY_RESET, &tp->flags);
-
data = r8152_mdio_read(tp, MII_BMCR);
/* don't reset again before the previous one complete */
@@ -2455,23 +2456,23 @@ static void r8153_teredo_off(struct r8152 *tp)
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0);
}
-static void r8152b_disable_aldps(struct r8152 *tp)
+static void r8152_aldps_en(struct r8152 *tp, bool enable)
{
- ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE);
- msleep(20);
-}
-
-static inline void r8152b_enable_aldps(struct r8152 *tp)
-{
- ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS |
- LINKENA | DIS_SDSAVE);
+ if (enable) {
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS |
+ LINKENA | DIS_SDSAVE);
+ } else {
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA |
+ DIS_SDSAVE);
+ msleep(20);
+ }
}
static void rtl8152_disable(struct r8152 *tp)
{
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
rtl_disable(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
}
static void r8152b_hw_phy_cfg(struct r8152 *tp)
@@ -2783,30 +2784,26 @@ static void r8153_enter_oob(struct r8152 *tp)
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
}
-static void r8153_disable_aldps(struct r8152 *tp)
-{
- u16 data;
-
- data = ocp_reg_read(tp, OCP_POWER_CFG);
- data &= ~EN_ALDPS;
- ocp_reg_write(tp, OCP_POWER_CFG, data);
- msleep(20);
-}
-
-static void r8153_enable_aldps(struct r8152 *tp)
+static void r8153_aldps_en(struct r8152 *tp, bool enable)
{
u16 data;
data = ocp_reg_read(tp, OCP_POWER_CFG);
- data |= EN_ALDPS;
- ocp_reg_write(tp, OCP_POWER_CFG, data);
+ if (enable) {
+ data |= EN_ALDPS;
+ ocp_reg_write(tp, OCP_POWER_CFG, data);
+ } else {
+ data &= ~EN_ALDPS;
+ ocp_reg_write(tp, OCP_POWER_CFG, data);
+ msleep(20);
+ }
}
static void rtl8153_disable(struct r8152 *tp)
{
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
rtl_disable(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
usb_enable_lpm(tp->udev);
}
@@ -2884,10 +2881,9 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
r8152_mdio_write(tp, MII_ADVERTISE, anar);
r8152_mdio_write(tp, MII_BMCR, bmcr);
- if (test_bit(PHY_RESET, &tp->flags)) {
+ if (test_and_clear_bit(PHY_RESET, &tp->flags)) {
int i;
- clear_bit(PHY_RESET, &tp->flags);
for (i = 0; i < 50; i++) {
msleep(20);
if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0)
@@ -2896,7 +2892,6 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
}
out:
-
return ret;
}
@@ -2905,9 +2900,9 @@ static void rtl8152_up(struct r8152 *tp)
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
r8152b_exit_oob(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
}
static void rtl8152_down(struct r8152 *tp)
@@ -2918,9 +2913,9 @@ static void rtl8152_down(struct r8152 *tp)
}
r8152_power_cut_en(tp, false);
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
r8152b_enter_oob(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
}
static void rtl8153_up(struct r8152 *tp)
@@ -2929,9 +2924,9 @@ static void rtl8153_up(struct r8152 *tp)
return;
r8153_u1u2en(tp, false);
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
r8153_first_init(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
r8153_u2p3en(tp, true);
r8153_u1u2en(tp, true);
usb_enable_lpm(tp->udev);
@@ -2947,9 +2942,9 @@ static void rtl8153_down(struct r8152 *tp)
r8153_u1u2en(tp, false);
r8153_u2p3en(tp, false);
r8153_power_cut_en(tp, false);
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
r8153_enter_oob(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
}
static bool rtl8152_in_nway(struct r8152 *tp)
@@ -2983,7 +2978,6 @@ static void set_carrier(struct r8152 *tp)
struct net_device *netdev = tp->netdev;
u8 speed;
- clear_bit(RTL8152_LINK_CHG, &tp->flags);
speed = rtl8152_get_speed(tp);
if (speed & LINK_STATUS) {
@@ -3026,20 +3020,18 @@ static void rtl_work_func_t(struct work_struct *work)
goto out1;
}
- if (test_bit(RTL8152_LINK_CHG, &tp->flags))
+ if (test_and_clear_bit(RTL8152_LINK_CHG, &tp->flags))
set_carrier(tp);
- if (test_bit(RTL8152_SET_RX_MODE, &tp->flags))
+ if (test_and_clear_bit(RTL8152_SET_RX_MODE, &tp->flags))
_rtl8152_set_rx_mode(tp->netdev);
/* don't schedule napi before linking */
- if (test_bit(SCHEDULE_NAPI, &tp->flags) &&
- netif_carrier_ok(tp->netdev)) {
- clear_bit(SCHEDULE_NAPI, &tp->flags);
+ if (test_and_clear_bit(SCHEDULE_NAPI, &tp->flags) &&
+ netif_carrier_ok(tp->netdev))
napi_schedule(&tp->napi);
- }
- if (test_bit(PHY_RESET, &tp->flags))
+ if (test_and_clear_bit(PHY_RESET, &tp->flags))
rtl_phy_reset(tp);
mutex_unlock(&tp->control);
@@ -3048,6 +3040,33 @@ out1:
usb_autopm_put_interface(tp->intf);
}
+#ifdef CONFIG_PM_SLEEP
+static int rtl_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct r8152 *tp = container_of(nb, struct r8152, pm_notifier);
+
+ switch (action) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ usb_autopm_get_interface(tp->intf);
+ break;
+
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ usb_autopm_put_interface(tp->intf);
+ break;
+
+ case PM_POST_RESTORE:
+ case PM_RESTORE_PREPARE:
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+#endif
+
static int rtl8152_open(struct net_device *netdev)
{
struct r8152 *tp = netdev_priv(netdev);
@@ -3090,6 +3109,10 @@ static int rtl8152_open(struct net_device *netdev)
mutex_unlock(&tp->control);
usb_autopm_put_interface(tp->intf);
+#ifdef CONFIG_PM_SLEEP
+ tp->pm_notifier.notifier_call = rtl_notifier;
+ register_pm_notifier(&tp->pm_notifier);
+#endif
out:
return res;
@@ -3100,6 +3123,9 @@ static int rtl8152_close(struct net_device *netdev)
struct r8152 *tp = netdev_priv(netdev);
int res = 0;
+#ifdef CONFIG_PM_SLEEP
+ unregister_pm_notifier(&tp->pm_notifier);
+#endif
napi_disable(&tp->napi);
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
@@ -3238,7 +3264,7 @@ static void r8152b_init(struct r8152 *tp)
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
if (tp->version == RTL_VER_01) {
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE);
@@ -3260,7 +3286,7 @@ static void r8152b_init(struct r8152 *tp)
ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data);
r8152b_enable_eee(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
r8152b_enable_fc(tp);
rtl_tally_reset(tp);
@@ -3278,7 +3304,7 @@ static void r8153_init(struct r8152 *tp)
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
r8153_u1u2en(tp, false);
for (i = 0; i < 500; i++) {
@@ -3367,7 +3393,7 @@ static void r8153_init(struct r8152 *tp)
EEE_SPDWN_EN);
r8153_enable_eee(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
r8152b_enable_fc(tp);
rtl_tally_reset(tp);
r8153_u2p3en(tp, true);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 0744bf2ef2d6..0b0ba7ef14e4 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -324,7 +324,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
return;
}
- skb->protocol = eth_type_trans (skb, dev->net);
+ /* only update if unset to allow minidriver rx_fixup override */
+ if (skb->protocol == 0)
+ skb->protocol = eth_type_trans (skb, dev->net);
+
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb->len;
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index f94ab786088f..767ab11a6e9f 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -522,8 +522,6 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
skb_shinfo(skb)->gso_segs = 0;
}
- skb_mark_napi_id(skb, &rq->napi);
-
napi_gro_receive(&rq->napi, skb);
return;
@@ -1616,7 +1614,6 @@ static int virtnet_alloc_queues(struct virtnet_info *vi)
vi->rq[i].pages = NULL;
netif_napi_add(vi->dev, &vi->rq[i].napi, virtnet_poll,
napi_weight);
- napi_hash_add(&vi->rq[i].napi);
sg_init_table(vi->rq[i].sg, ARRAY_SIZE(vi->rq[i].sg));
ewma_pkt_len_init(&vi->rq[i].mrg_avg_pkt_len);
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 0a242b200df4..66addb7a7911 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -46,17 +46,7 @@
#define vrf_master_get_rcu(dev) \
((struct net_device *)rcu_dereference(dev->rx_handler_data))
-struct slave {
- struct list_head list;
- struct net_device *dev;
-};
-
-struct slave_queue {
- struct list_head all_slaves;
-};
-
struct net_vrf {
- struct slave_queue queue;
struct rtable *rth;
struct rt6_info *rt6;
u32 tb_id;
@@ -621,42 +611,9 @@ static void cycle_netdev(struct net_device *dev)
}
}
-static struct slave *__vrf_find_slave_dev(struct slave_queue *queue,
- struct net_device *dev)
-{
- struct list_head *head = &queue->all_slaves;
- struct slave *slave;
-
- list_for_each_entry(slave, head, list) {
- if (slave->dev == dev)
- return slave;
- }
-
- return NULL;
-}
-
-/* inverse of __vrf_insert_slave */
-static void __vrf_remove_slave(struct slave_queue *queue, struct slave *slave)
-{
- list_del(&slave->list);
-}
-
-static void __vrf_insert_slave(struct slave_queue *queue, struct slave *slave)
-{
- list_add(&slave->list, &queue->all_slaves);
-}
-
static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
{
- struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL);
- struct net_vrf *vrf = netdev_priv(dev);
- struct slave_queue *queue = &vrf->queue;
- int ret = -ENOMEM;
-
- if (!slave)
- goto out_fail;
-
- slave->dev = port_dev;
+ int ret;
/* register the packet handler for slave ports */
ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev);
@@ -667,12 +624,11 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
goto out_fail;
}
- ret = netdev_master_upper_dev_link(port_dev, dev);
+ ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL);
if (ret < 0)
goto out_unregister;
port_dev->priv_flags |= IFF_L3MDEV_SLAVE;
- __vrf_insert_slave(queue, slave);
cycle_netdev(port_dev);
return 0;
@@ -680,7 +636,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
out_unregister:
netdev_rx_handler_unregister(port_dev);
out_fail:
- kfree(slave);
return ret;
}
@@ -695,10 +650,6 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
/* inverse of do_vrf_add_slave */
static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
{
- struct net_vrf *vrf = netdev_priv(dev);
- struct slave_queue *queue = &vrf->queue;
- struct slave *slave;
-
netdev_upper_dev_unlink(port_dev, dev);
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
@@ -706,12 +657,6 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
cycle_netdev(port_dev);
- slave = __vrf_find_slave_dev(queue, port_dev);
- if (slave)
- __vrf_remove_slave(queue, slave);
-
- kfree(slave);
-
return 0;
}
@@ -723,15 +668,14 @@ static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
static void vrf_dev_uninit(struct net_device *dev)
{
struct net_vrf *vrf = netdev_priv(dev);
- struct slave_queue *queue = &vrf->queue;
- struct list_head *head = &queue->all_slaves;
- struct slave *slave, *next;
+ struct net_device *port_dev;
+ struct list_head *iter;
vrf_rtable_destroy(vrf);
vrf_rt6_destroy(vrf);
- list_for_each_entry_safe(slave, next, head, list)
- vrf_del_slave(dev, slave->dev);
+ netdev_for_each_lower_dev(dev, port_dev, iter)
+ vrf_del_slave(dev, port_dev);
free_percpu(dev->dstats);
dev->dstats = NULL;
@@ -741,8 +685,6 @@ static int vrf_dev_init(struct net_device *dev)
{
struct net_vrf *vrf = netdev_priv(dev);
- INIT_LIST_HEAD(&vrf->queue.all_slaves);
-
dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
if (!dev->dstats)
goto out_nomem;
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index ba363cedef80..2d88c799d2ac 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -621,7 +621,7 @@ static void vxlan_notify_add_rx_port(struct vxlan_sock *vs)
int err;
if (sa_family == AF_INET) {
- err = udp_add_offload(&vs->udp_offloads);
+ err = udp_add_offload(net, &vs->udp_offloads);
if (err)
pr_warn("vxlan: udp_add_offload failed with status %d\n", err);
}
@@ -1841,9 +1841,10 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *sk
skb_set_inner_protocol(skb, htons(ETH_P_TEB));
- return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos,
- ttl, df, src_port, dst_port, xnet,
- !(vxflags & VXLAN_F_UDP_CSUM));
+ udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos, ttl, df,
+ src_port, dst_port, xnet,
+ !(vxflags & VXLAN_F_UDP_CSUM));
+ return 0;
}
#if IS_ENABLED(CONFIG_IPV6)
@@ -2056,8 +2057,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
skb = NULL;
goto rt_tx_error;
}
-
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
#if IS_ENABLED(CONFIG_IPV6)
} else {
struct dst_entry *ndst;
@@ -2751,7 +2750,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
struct vxlan_config *conf)
{
struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
- struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct vxlan_dev *vxlan = netdev_priv(dev), *tmp;
struct vxlan_rdst *dst = &vxlan->default_dst;
unsigned short needed_headroom = ETH_HLEN;
int err;
@@ -2817,9 +2816,15 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
if (!vxlan->cfg.age_interval)
vxlan->cfg.age_interval = FDB_AGE_DEFAULT;
- if (vxlan_find_vni(src_net, conf->vni, use_ipv6 ? AF_INET6 : AF_INET,
- vxlan->cfg.dst_port, vxlan->flags))
+ list_for_each_entry(tmp, &vn->vxlan_list, next) {
+ if (tmp->cfg.vni == conf->vni &&
+ (tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 ||
+ tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 &&
+ tmp->cfg.dst_port == vxlan->cfg.dst_port &&
+ (tmp->flags & VXLAN_F_RCV_FLAGS) ==
+ (vxlan->flags & VXLAN_F_RCV_FLAGS))
return -EEXIST;
+ }
dev->ethtool_ops = &vxlan_ethtool_ops;
diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c
index 51f6cee8aab2..9bd4aa8083ce 100644
--- a/drivers/net/wan/hdlc.c
+++ b/drivers/net/wan/hdlc.c
@@ -266,8 +266,8 @@ struct net_device *alloc_hdlcdev(void *priv)
void unregister_hdlc_device(struct net_device *dev)
{
rtnl_lock();
- unregister_netdevice(dev);
detach_hdlc_protocol(dev);
+ unregister_netdevice(dev);
rtnl_unlock();
}
@@ -276,7 +276,11 @@ void unregister_hdlc_device(struct net_device *dev)
int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
size_t size)
{
- detach_hdlc_protocol(dev);
+ int err;
+
+ err = detach_hdlc_protocol(dev);
+ if (err)
+ return err;
if (!try_module_get(proto->module))
return -ENOSYS;
@@ -289,15 +293,24 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
}
}
dev_to_hdlc(dev)->proto = proto;
+
return 0;
}
-void detach_hdlc_protocol(struct net_device *dev)
+int detach_hdlc_protocol(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
+ int err;
if (hdlc->proto) {
+ err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
+ err = notifier_to_errno(err);
+ if (err) {
+ netdev_err(dev, "Refused to change device type\n");
+ return err;
+ }
+
if (hdlc->proto->detach)
hdlc->proto->detach(dev);
module_put(hdlc->proto->module);
@@ -306,6 +319,8 @@ void detach_hdlc_protocol(struct net_device *dev)
kfree(hdlc->state);
hdlc->state = NULL;
hdlc_setup_dev(dev);
+
+ return 0;
}
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index 3f20808b5ff8..a408abc25512 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -378,6 +378,7 @@ static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
spin_lock_init(&state(hdlc)->lock);
dev->header_ops = &cisco_header_ops;
dev->type = ARPHRD_CISCO;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_on(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index 89541cc90e87..b6e0cfb095d3 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -1240,6 +1240,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
}
memcpy(&state(hdlc)->settings, &new_settings, size);
dev->type = ARPHRD_FRAD;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
return 0;
case IF_PROTO_FR_ADD_PVC:
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 0d7645581f91..47fdb87d3567 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -687,6 +687,7 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
dev->hard_header_len = sizeof(struct hdlc_header);
dev->header_ops = &ppp_header_ops;
dev->type = ARPHRD_PPP;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_on(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c
index 5dc153e8a29d..4feb45001aac 100644
--- a/drivers/net/wan/hdlc_raw.c
+++ b/drivers/net/wan/hdlc_raw.c
@@ -84,6 +84,7 @@ static int raw_ioctl(struct net_device *dev, struct ifreq *ifr)
return result;
memcpy(hdlc->state, &new_settings, size);
dev->type = ARPHRD_RAWHDLC;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_off(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c
index 3ab72b3082de..2f11836078ab 100644
--- a/drivers/net/wan/hdlc_raw_eth.c
+++ b/drivers/net/wan/hdlc_raw_eth.c
@@ -102,6 +102,7 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
ether_setup(dev);
dev->tx_queue_len = old_qlen;
eth_hw_addr_random(dev);
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_off(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c
index a49aec5efd20..e867638067a6 100644
--- a/drivers/net/wan/hdlc_x25.c
+++ b/drivers/net/wan/hdlc_x25.c
@@ -213,6 +213,7 @@ static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
if ((result = attach_hdlc_protocol(dev, &proto, 0)))
return result;
dev->type = ARPHRD_X25;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_off(dev);
return 0;
}
diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c
index e73f13857846..a20d688d2595 100644
--- a/drivers/net/wan/wanxl.c
+++ b/drivers/net/wan/wanxl.c
@@ -586,6 +586,7 @@ static int wanxl_pci_init_one(struct pci_dev *pdev,
if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(28)) ||
pci_set_dma_mask(pdev, DMA_BIT_MASK(28))) {
pr_err("No usable DMA configuration\n");
+ pci_disable_device(pdev);
return -EIO;
}
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f9f94229bf1b..8c8edaf1bba6 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -17,6 +17,22 @@ menuconfig WLAN
if WLAN
+source "drivers/net/wireless/admtek/Kconfig"
+source "drivers/net/wireless/ath/Kconfig"
+source "drivers/net/wireless/atmel/Kconfig"
+source "drivers/net/wireless/broadcom/Kconfig"
+source "drivers/net/wireless/cisco/Kconfig"
+source "drivers/net/wireless/intel/Kconfig"
+source "drivers/net/wireless/intersil/Kconfig"
+source "drivers/net/wireless/marvell/Kconfig"
+source "drivers/net/wireless/mediatek/Kconfig"
+source "drivers/net/wireless/ralink/Kconfig"
+source "drivers/net/wireless/realtek/Kconfig"
+source "drivers/net/wireless/rsi/Kconfig"
+source "drivers/net/wireless/st/Kconfig"
+source "drivers/net/wireless/ti/Kconfig"
+source "drivers/net/wireless/zydas/Kconfig"
+
config PCMCIA_RAYCS
tristate "Aviator/Raytheon 2.4GHz wireless support"
depends on PCMCIA
@@ -32,110 +48,6 @@ config PCMCIA_RAYCS
To compile this driver as a module, choose M here: the module will be
called ray_cs. If unsure, say N.
-config LIBERTAS_THINFIRM
- tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware"
- depends on MAC80211
- select FW_LOADER
- ---help---
- A library for Marvell Libertas 8xxx devices using thinfirm.
-
-config LIBERTAS_THINFIRM_DEBUG
- bool "Enable full debugging output in the Libertas thin firmware module."
- depends on LIBERTAS_THINFIRM
- ---help---
- Debugging support.
-
-config LIBERTAS_THINFIRM_USB
- tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware"
- depends on LIBERTAS_THINFIRM && USB
- ---help---
- A driver for Marvell Libertas 8388 USB devices using thinfirm.
-
-config AIRO
- tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
- depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN)
- select WIRELESS_EXT
- select CRYPTO
- select WEXT_SPY
- select WEXT_PRIV
- ---help---
- This is the standard Linux driver to support Cisco/Aironet ISA and
- PCI 802.11 wireless cards.
- It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
- - with or without encryption) as well as card before the Cisco
- acquisition (Aironet 4500, Aironet 4800, Aironet 4800B).
-
- This driver support both the standard Linux Wireless Extensions
- and Cisco proprietary API, so both the Linux Wireless Tools and the
- Cisco Linux utilities can be used to configure the card.
-
- The driver can be compiled as a module and will be named "airo".
-
-config ATMEL
- tristate "Atmel at76c50x chipset 802.11b support"
- depends on CFG80211 && (PCI || PCMCIA)
- select WIRELESS_EXT
- select WEXT_PRIV
- select FW_LOADER
- select CRC32
- ---help---
- A driver 802.11b wireless cards based on the Atmel fast-vnet
- chips. This driver supports standard Linux wireless extensions.
-
- Many cards based on this chipset do not have flash memory
- and need their firmware loaded at start-up. If yours is
- one of these, you will need to provide a firmware image
- to be loaded into the card by the driver. The Atmel
- firmware package can be downloaded from
- <http://www.thekelleys.org.uk/atmel>
-
-config PCI_ATMEL
- tristate "Atmel at76c506 PCI cards"
- depends on ATMEL && PCI
- ---help---
- Enable support for PCI and mini-PCI cards containing the
- Atmel at76c506 chip.
-
-config PCMCIA_ATMEL
- tristate "Atmel at76c502/at76c504 PCMCIA cards"
- depends on ATMEL && PCMCIA
- select WIRELESS_EXT
- select FW_LOADER
- select CRC32
- ---help---
- Enable support for PCMCIA cards containing the
- Atmel at76c502 and at76c504 chips.
-
-config AT76C50X_USB
- tristate "Atmel at76c503/at76c505/at76c505a USB cards"
- depends on MAC80211 && USB
- select FW_LOADER
- ---help---
- Enable support for USB Wireless devices using Atmel at76c503,
- at76c505 or at76c505a chips.
-
-config AIRO_CS
- tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
- depends on CFG80211 && PCMCIA && (BROKEN || !M32R)
- select WIRELESS_EXT
- select WEXT_SPY
- select WEXT_PRIV
- select CRYPTO
- select CRYPTO_AES
- ---help---
- This is the standard Linux driver to support Cisco/Aironet PCMCIA
- 802.11 wireless cards. This driver is the same as the Aironet
- driver part of the Linux Pcmcia package.
- It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
- - with or without encryption) as well as card before the Cisco
- acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also
- supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom
- 802.11b cards.
-
- This driver support both the standard Linux Wireless Extensions
- and Cisco proprietary API, so both the Linux Wireless Tools and the
- Cisco Linux utilities can be used to configure the card.
-
config PCMCIA_WL3501
tristate "Planet WL3501 PCMCIA cards"
depends on CFG80211 && PCMCIA
@@ -146,44 +58,18 @@ config PCMCIA_WL3501
It has basic support for Linux wireless extensions and initial
micro support for ethtool.
-config PRISM54
- tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)'
- depends on PCI
- select WIRELESS_EXT
- select WEXT_SPY
- select WEXT_PRIV
- select FW_LOADER
- ---help---
- This enables support for FullMAC PCI/Cardbus prism54 devices. This
- driver is now deprecated in favor for the SoftMAC driver, p54pci.
- p54pci supports FullMAC PCI/Cardbus devices as well.
-
- For more information refer to the p54 wiki:
-
- http://wireless.kernel.org/en/users/Drivers/p54
-
- Note: You need a motherboard with DMA support to use any of these cards
-
- When built as module you get the module prism54
-
-config USB_ZD1201
- tristate "USB ZD1201 based Wireless device support"
- depends on CFG80211 && USB
- select WIRELESS_EXT
- select WEXT_PRIV
- select FW_LOADER
+config MAC80211_HWSIM
+ tristate "Simulated radio testing tool for mac80211"
+ depends on MAC80211
---help---
- Say Y if you want to use wireless LAN adapters based on the ZyDAS
- ZD1201 chip.
-
- This driver makes the adapter appear as a normal Ethernet interface,
- typically on wlan0.
-
- The zd1201 device requires external firmware to be loaded.
- This can be found at http://linux-lc100020.sourceforge.net/
+ This driver is a developer testing tool that can be used to test
+ IEEE 802.11 networking stack (mac80211) functionality. This is not
+ needed for normal wireless LAN usage and is only for testing. See
+ Documentation/networking/mac80211_hwsim for more information on how
+ to use this tool.
- To compile this driver as a module, choose M here: the
- module will be called zd1201.
+ To compile this driver as a module, choose M here: the module will be
+ called mac80211_hwsim. If unsure, say N.
config USB_NET_RNDIS_WLAN
tristate "Wireless RNDIS USB support"
@@ -214,76 +100,4 @@ config USB_NET_RNDIS_WLAN
If you choose to build a module, it'll be called rndis_wlan.
-config ADM8211
- tristate "ADMtek ADM8211 support"
- depends on MAC80211 && PCI
- select CRC32
- select EEPROM_93CX6
- ---help---
- This driver is for ADM8211A, ADM8211B, and ADM8211C based cards.
- These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as:
-
- Xterasys Cardbus XN-2411b
- Blitz NetWave Point PC
- TrendNet 221pc
- Belkin F5D6001
- SMC 2635W
- Linksys WPC11 v1
- Fiberline FL-WL-200X
- 3com Office Connect (3CRSHPW796)
- Corega WLPCIB-11
- SMC 2602W V2 EU
- D-Link DWL-520 Revision C
-
- However, some of these cards have been replaced with other chips
- like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or
- the Ralink RT2400 (SMC2635W) without a model number change.
-
- Thanks to Infineon-ADMtek for their support of this driver.
-
-source "drivers/net/wireless/realtek/rtl818x/Kconfig"
-
-config MAC80211_HWSIM
- tristate "Simulated radio testing tool for mac80211"
- depends on MAC80211
- ---help---
- This driver is a developer testing tool that can be used to test
- IEEE 802.11 networking stack (mac80211) functionality. This is not
- needed for normal wireless LAN usage and is only for testing. See
- Documentation/networking/mac80211_hwsim for more information on how
- to use this tool.
-
- To compile this driver as a module, choose M here: the module will be
- called mac80211_hwsim. If unsure, say N.
-
-config MWL8K
- tristate "Marvell 88W8xxx PCI/PCIe Wireless support"
- depends on MAC80211 && PCI
- ---help---
- This driver supports Marvell TOPDOG 802.11 wireless cards.
-
- To compile this driver as a module, choose M here: the module
- will be called mwl8k. If unsure, say N.
-
-source "drivers/net/wireless/ath/Kconfig"
-source "drivers/net/wireless/b43/Kconfig"
-source "drivers/net/wireless/b43legacy/Kconfig"
-source "drivers/net/wireless/brcm80211/Kconfig"
-source "drivers/net/wireless/hostap/Kconfig"
-source "drivers/net/wireless/ipw2x00/Kconfig"
-source "drivers/net/wireless/iwlwifi/Kconfig"
-source "drivers/net/wireless/iwlegacy/Kconfig"
-source "drivers/net/wireless/libertas/Kconfig"
-source "drivers/net/wireless/orinoco/Kconfig"
-source "drivers/net/wireless/p54/Kconfig"
-source "drivers/net/wireless/rt2x00/Kconfig"
-source "drivers/net/wireless/mediatek/Kconfig"
-source "drivers/net/wireless/realtek/rtlwifi/Kconfig"
-source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig"
-source "drivers/net/wireless/ti/Kconfig"
-source "drivers/net/wireless/zd1211rw/Kconfig"
-source "drivers/net/wireless/mwifiex/Kconfig"
-source "drivers/net/wireless/cw1200/Kconfig"
-source "drivers/net/wireless/rsi/Kconfig"
-
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 740fdd353c5d..f00d42953fb8 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -2,27 +2,21 @@
# Makefile for the Linux Wireless network device drivers.
#
-obj-$(CONFIG_IPW2100) += ipw2x00/
-obj-$(CONFIG_IPW2200) += ipw2x00/
-
-obj-$(CONFIG_HERMES) += orinoco/
-
-obj-$(CONFIG_AIRO) += airo.o
-obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
-
-obj-$(CONFIG_ATMEL) += atmel.o
-obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o
-obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
-
-obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o
-
-obj-$(CONFIG_PRISM54) += prism54/
-
-obj-$(CONFIG_HOSTAP) += hostap/
-obj-$(CONFIG_B43) += b43/
-obj-$(CONFIG_B43LEGACY) += b43legacy/
-obj-$(CONFIG_ZD1211RW) += zd1211rw/
-obj-$(CONFIG_WLAN) += realtek/
+obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/
+obj-$(CONFIG_WLAN_VENDOR_ATH) += ath/
+obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/
+obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/
+obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/
+obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/
+obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
+obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
+obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
+obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
+obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
+obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
+obj-$(CONFIG_WLAN_VENDOR_ST) += st/
+obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
+obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
@@ -30,33 +24,4 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o
-obj-$(CONFIG_USB_ZD1201) += zd1201.o
-obj-$(CONFIG_LIBERTAS) += libertas/
-
-obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
-
-obj-$(CONFIG_ADM8211) += adm8211.o
-
-obj-$(CONFIG_MWL8K) += mwl8k.o
-
-obj-$(CONFIG_IWLWIFI) += iwlwifi/
-obj-$(CONFIG_IWLEGACY) += iwlegacy/
-obj-$(CONFIG_RT2X00) += rt2x00/
-
-obj-$(CONFIG_WL_MEDIATEK) += mediatek/
-
-obj-$(CONFIG_P54_COMMON) += p54/
-
-obj-$(CONFIG_ATH_CARDS) += ath/
-
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
-
-obj-$(CONFIG_WL_TI) += ti/
-
-obj-$(CONFIG_MWIFIEX) += mwifiex/
-
-obj-$(CONFIG_BRCMFMAC) += brcm80211/
-obj-$(CONFIG_BRCMSMAC) += brcm80211/
-
-obj-$(CONFIG_CW1200) += cw1200/
-obj-$(CONFIG_RSI_91X) += rsi/
diff --git a/drivers/net/wireless/admtek/Kconfig b/drivers/net/wireless/admtek/Kconfig
new file mode 100644
index 000000000000..d5a2dc728078
--- /dev/null
+++ b/drivers/net/wireless/admtek/Kconfig
@@ -0,0 +1,41 @@
+config WLAN_VENDOR_ADMTEK
+ bool "ADMtek devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ADMTEK
+
+config ADM8211
+ tristate "ADMtek ADM8211 support"
+ depends on MAC80211 && PCI
+ select CRC32
+ select EEPROM_93CX6
+ ---help---
+ This driver is for ADM8211A, ADM8211B, and ADM8211C based cards.
+ These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as:
+
+ Xterasys Cardbus XN-2411b
+ Blitz NetWave Point PC
+ TrendNet 221pc
+ Belkin F5D6001
+ SMC 2635W
+ Linksys WPC11 v1
+ Fiberline FL-WL-200X
+ 3com Office Connect (3CRSHPW796)
+ Corega WLPCIB-11
+ SMC 2602W V2 EU
+ D-Link DWL-520 Revision C
+
+ However, some of these cards have been replaced with other chips
+ like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or
+ the Ralink RT2400 (SMC2635W) without a model number change.
+
+ Thanks to Infineon-ADMtek for their support of this driver.
+
+endif # WLAN_VENDOR_ADMTEK
diff --git a/drivers/net/wireless/admtek/Makefile b/drivers/net/wireless/admtek/Makefile
new file mode 100644
index 000000000000..9cca7e571cdd
--- /dev/null
+++ b/drivers/net/wireless/admtek/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ADM8211) += adm8211.o
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/admtek/adm8211.c
index 15f057ed41ad..15f057ed41ad 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/admtek/adm8211.c
diff --git a/drivers/net/wireless/adm8211.h b/drivers/net/wireless/admtek/adm8211.h
index bbc10b1cde87..bbc10b1cde87 100644
--- a/drivers/net/wireless/adm8211.h
+++ b/drivers/net/wireless/admtek/adm8211.h
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
deleted file mode 100644
index 17c40f06f13e..000000000000
--- a/drivers/net/wireless/airo.c
+++ /dev/null
@@ -1,8236 +0,0 @@
-/*======================================================================
-
- Aironet driver for 4500 and 4800 series cards
-
- This code is released under both the GPL version 2 and BSD licenses.
- Either license may be used. The respective licenses are found at
- the end of this file.
-
- This code was developed by Benjamin Reed <breed@users.sourceforge.net>
- including portions of which come from the Aironet PC4500
- Developer's Reference Manual and used with permission. Copyright
- (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use
- code in the Developer's manual was granted for this driver by
- Aironet. Major code contributions were received from Javier Achirica
- <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>.
- Code was also integrated from the Cisco Aironet driver for Linux.
- Support for MPI350 cards was added by Fabrice Bellet
- <fabrice@bellet.info>.
-
-======================================================================*/
-
-#include <linux/err.h>
-#include <linux/init.h>
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/bitops.h>
-#include <linux/scatterlist.h>
-#include <linux/crypto.h>
-#include <linux/io.h>
-#include <asm/unaligned.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/uaccess.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-
-#include <net/cfg80211.h>
-#include <net/iw_handler.h>
-
-#include "airo.h"
-
-#define DRV_NAME "airo"
-
-#ifdef CONFIG_PCI
-static const struct pci_device_id card_ids[] = {
- { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
- { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
- { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
- { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
- { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
- { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
- { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci, card_ids);
-
-static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
-static void airo_pci_remove(struct pci_dev *);
-static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state);
-static int airo_pci_resume(struct pci_dev *pdev);
-
-static struct pci_driver airo_driver = {
- .name = DRV_NAME,
- .id_table = card_ids,
- .probe = airo_pci_probe,
- .remove = airo_pci_remove,
- .suspend = airo_pci_suspend,
- .resume = airo_pci_resume,
-};
-#endif /* CONFIG_PCI */
-
-/* Include Wireless Extension definition and check version - Jean II */
-#include <linux/wireless.h>
-#define WIRELESS_SPY /* enable iwspy support */
-
-#define CISCO_EXT /* enable Cisco extensions */
-#ifdef CISCO_EXT
-#include <linux/delay.h>
-#endif
-
-/* Hack to do some power saving */
-#define POWER_ON_DOWN
-
-/* As you can see this list is HUGH!
- I really don't know what a lot of these counts are about, but they
- are all here for completeness. If the IGNLABEL macro is put in
- infront of the label, that statistic will not be included in the list
- of statistics in the /proc filesystem */
-
-#define IGNLABEL(comment) NULL
-static const char *statsLabels[] = {
- "RxOverrun",
- IGNLABEL("RxPlcpCrcErr"),
- IGNLABEL("RxPlcpFormatErr"),
- IGNLABEL("RxPlcpLengthErr"),
- "RxMacCrcErr",
- "RxMacCrcOk",
- "RxWepErr",
- "RxWepOk",
- "RetryLong",
- "RetryShort",
- "MaxRetries",
- "NoAck",
- "NoCts",
- "RxAck",
- "RxCts",
- "TxAck",
- "TxRts",
- "TxCts",
- "TxMc",
- "TxBc",
- "TxUcFrags",
- "TxUcPackets",
- "TxBeacon",
- "RxBeacon",
- "TxSinColl",
- "TxMulColl",
- "DefersNo",
- "DefersProt",
- "DefersEngy",
- "DupFram",
- "RxFragDisc",
- "TxAged",
- "RxAged",
- "LostSync-MaxRetry",
- "LostSync-MissedBeacons",
- "LostSync-ArlExceeded",
- "LostSync-Deauth",
- "LostSync-Disassoced",
- "LostSync-TsfTiming",
- "HostTxMc",
- "HostTxBc",
- "HostTxUc",
- "HostTxFail",
- "HostRxMc",
- "HostRxBc",
- "HostRxUc",
- "HostRxDiscard",
- IGNLABEL("HmacTxMc"),
- IGNLABEL("HmacTxBc"),
- IGNLABEL("HmacTxUc"),
- IGNLABEL("HmacTxFail"),
- IGNLABEL("HmacRxMc"),
- IGNLABEL("HmacRxBc"),
- IGNLABEL("HmacRxUc"),
- IGNLABEL("HmacRxDiscard"),
- IGNLABEL("HmacRxAccepted"),
- "SsidMismatch",
- "ApMismatch",
- "RatesMismatch",
- "AuthReject",
- "AuthTimeout",
- "AssocReject",
- "AssocTimeout",
- IGNLABEL("ReasonOutsideTable"),
- IGNLABEL("ReasonStatus1"),
- IGNLABEL("ReasonStatus2"),
- IGNLABEL("ReasonStatus3"),
- IGNLABEL("ReasonStatus4"),
- IGNLABEL("ReasonStatus5"),
- IGNLABEL("ReasonStatus6"),
- IGNLABEL("ReasonStatus7"),
- IGNLABEL("ReasonStatus8"),
- IGNLABEL("ReasonStatus9"),
- IGNLABEL("ReasonStatus10"),
- IGNLABEL("ReasonStatus11"),
- IGNLABEL("ReasonStatus12"),
- IGNLABEL("ReasonStatus13"),
- IGNLABEL("ReasonStatus14"),
- IGNLABEL("ReasonStatus15"),
- IGNLABEL("ReasonStatus16"),
- IGNLABEL("ReasonStatus17"),
- IGNLABEL("ReasonStatus18"),
- IGNLABEL("ReasonStatus19"),
- "RxMan",
- "TxMan",
- "RxRefresh",
- "TxRefresh",
- "RxPoll",
- "TxPoll",
- "HostRetries",
- "LostSync-HostReq",
- "HostTxBytes",
- "HostRxBytes",
- "ElapsedUsec",
- "ElapsedSec",
- "LostSyncBetterAP",
- "PrivacyMismatch",
- "Jammed",
- "DiscRxNotWepped",
- "PhyEleMismatch",
- (char*)-1 };
-#ifndef RUN_AT
-#define RUN_AT(x) (jiffies+(x))
-#endif
-
-
-/* These variables are for insmod, since it seems that the rates
- can only be set in setup_card. Rates should be a comma separated
- (no spaces) list of rates (up to 8). */
-
-static int rates[8];
-static char *ssids[3];
-
-static int io[4];
-static int irq[4];
-
-static
-int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
- 0 means no limit. For old cards this was 4 */
-
-static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
-static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
- the bap, needed on some older cards and buses. */
-static int adhoc;
-
-static int probe = 1;
-
-static kuid_t proc_kuid;
-static int proc_uid /* = 0 */;
-
-static kgid_t proc_kgid;
-static int proc_gid /* = 0 */;
-
-static int airo_perm = 0555;
-
-static int proc_perm = 0644;
-
-MODULE_AUTHOR("Benjamin Reed");
-MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. "
- "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs.");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
-module_param_array(io, int, NULL, 0);
-module_param_array(irq, int, NULL, 0);
-module_param_array(rates, int, NULL, 0);
-module_param_array(ssids, charp, NULL, 0);
-module_param(auto_wep, int, 0);
-MODULE_PARM_DESC(auto_wep,
- "If non-zero, the driver will keep looping through the authentication options until an association is made. "
- "The value of auto_wep is number of the wep keys to check. "
- "A value of 2 will try using the key at index 0 and index 1.");
-module_param(aux_bap, int, 0);
-MODULE_PARM_DESC(aux_bap,
- "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. "
- "Before switching it checks that the switch is needed.");
-module_param(maxencrypt, int, 0);
-MODULE_PARM_DESC(maxencrypt,
- "The maximum speed that the card can do encryption. "
- "Units are in 512kbs. "
- "Zero (default) means there is no limit. "
- "Older cards used to be limited to 2mbs (4).");
-module_param(adhoc, int, 0);
-MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
-module_param(probe, int, 0);
-MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
-
-module_param(proc_uid, int, 0);
-MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
-module_param(proc_gid, int, 0);
-MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
-module_param(airo_perm, int, 0);
-MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
-module_param(proc_perm, int, 0);
-MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
-
-/* This is a kind of sloppy hack to get this information to OUT4500 and
- IN4500. I would be extremely interested in the situation where this
- doesn't work though!!! */
-static int do8bitIO /* = 0 */;
-
-/* Return codes */
-#define SUCCESS 0
-#define ERROR -1
-#define NO_PACKET -2
-
-/* Commands */
-#define NOP2 0x0000
-#define MAC_ENABLE 0x0001
-#define MAC_DISABLE 0x0002
-#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */
-#define CMD_SOFTRESET 0x0004
-#define HOSTSLEEP 0x0005
-#define CMD_MAGIC_PKT 0x0006
-#define CMD_SETWAKEMASK 0x0007
-#define CMD_READCFG 0x0008
-#define CMD_SETMODE 0x0009
-#define CMD_ALLOCATETX 0x000a
-#define CMD_TRANSMIT 0x000b
-#define CMD_DEALLOCATETX 0x000c
-#define NOP 0x0010
-#define CMD_WORKAROUND 0x0011
-#define CMD_ALLOCATEAUX 0x0020
-#define CMD_ACCESS 0x0021
-#define CMD_PCIBAP 0x0022
-#define CMD_PCIAUX 0x0023
-#define CMD_ALLOCBUF 0x0028
-#define CMD_GETTLV 0x0029
-#define CMD_PUTTLV 0x002a
-#define CMD_DELTLV 0x002b
-#define CMD_FINDNEXTTLV 0x002c
-#define CMD_PSPNODES 0x0030
-#define CMD_SETCW 0x0031
-#define CMD_SETPCF 0x0032
-#define CMD_SETPHYREG 0x003e
-#define CMD_TXTEST 0x003f
-#define MAC_ENABLETX 0x0101
-#define CMD_LISTBSS 0x0103
-#define CMD_SAVECFG 0x0108
-#define CMD_ENABLEAUX 0x0111
-#define CMD_WRITERID 0x0121
-#define CMD_USEPSPNODES 0x0130
-#define MAC_ENABLERX 0x0201
-
-/* Command errors */
-#define ERROR_QUALIF 0x00
-#define ERROR_ILLCMD 0x01
-#define ERROR_ILLFMT 0x02
-#define ERROR_INVFID 0x03
-#define ERROR_INVRID 0x04
-#define ERROR_LARGE 0x05
-#define ERROR_NDISABL 0x06
-#define ERROR_ALLOCBSY 0x07
-#define ERROR_NORD 0x0B
-#define ERROR_NOWR 0x0C
-#define ERROR_INVFIDTX 0x0D
-#define ERROR_TESTACT 0x0E
-#define ERROR_TAGNFND 0x12
-#define ERROR_DECODE 0x20
-#define ERROR_DESCUNAV 0x21
-#define ERROR_BADLEN 0x22
-#define ERROR_MODE 0x80
-#define ERROR_HOP 0x81
-#define ERROR_BINTER 0x82
-#define ERROR_RXMODE 0x83
-#define ERROR_MACADDR 0x84
-#define ERROR_RATES 0x85
-#define ERROR_ORDER 0x86
-#define ERROR_SCAN 0x87
-#define ERROR_AUTH 0x88
-#define ERROR_PSMODE 0x89
-#define ERROR_RTYPE 0x8A
-#define ERROR_DIVER 0x8B
-#define ERROR_SSID 0x8C
-#define ERROR_APLIST 0x8D
-#define ERROR_AUTOWAKE 0x8E
-#define ERROR_LEAP 0x8F
-
-/* Registers */
-#define COMMAND 0x00
-#define PARAM0 0x02
-#define PARAM1 0x04
-#define PARAM2 0x06
-#define STATUS 0x08
-#define RESP0 0x0a
-#define RESP1 0x0c
-#define RESP2 0x0e
-#define LINKSTAT 0x10
-#define SELECT0 0x18
-#define OFFSET0 0x1c
-#define RXFID 0x20
-#define TXALLOCFID 0x22
-#define TXCOMPLFID 0x24
-#define DATA0 0x36
-#define EVSTAT 0x30
-#define EVINTEN 0x32
-#define EVACK 0x34
-#define SWS0 0x28
-#define SWS1 0x2a
-#define SWS2 0x2c
-#define SWS3 0x2e
-#define AUXPAGE 0x3A
-#define AUXOFF 0x3C
-#define AUXDATA 0x3E
-
-#define FID_TX 1
-#define FID_RX 2
-/* Offset into aux memory for descriptors */
-#define AUX_OFFSET 0x800
-/* Size of allocated packets */
-#define PKTSIZE 1840
-#define RIDSIZE 2048
-/* Size of the transmit queue */
-#define MAXTXQ 64
-
-/* BAP selectors */
-#define BAP0 0 /* Used for receiving packets */
-#define BAP1 2 /* Used for xmiting packets and working with RIDS */
-
-/* Flags */
-#define COMMAND_BUSY 0x8000
-
-#define BAP_BUSY 0x8000
-#define BAP_ERR 0x4000
-#define BAP_DONE 0x2000
-
-#define PROMISC 0xffff
-#define NOPROMISC 0x0000
-
-#define EV_CMD 0x10
-#define EV_CLEARCOMMANDBUSY 0x4000
-#define EV_RX 0x01
-#define EV_TX 0x02
-#define EV_TXEXC 0x04
-#define EV_ALLOC 0x08
-#define EV_LINK 0x80
-#define EV_AWAKE 0x100
-#define EV_TXCPY 0x400
-#define EV_UNKNOWN 0x800
-#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
-#define EV_AWAKEN 0x2000
-#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
-
-#ifdef CHECK_UNKNOWN_INTS
-#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN)
-#else
-#define IGNORE_INTS (~STATUS_INTS)
-#endif
-
-/* RID TYPES */
-#define RID_RW 0x20
-
-/* The RIDs */
-#define RID_CAPABILITIES 0xFF00
-#define RID_APINFO 0xFF01
-#define RID_RADIOINFO 0xFF02
-#define RID_UNKNOWN3 0xFF03
-#define RID_RSSI 0xFF04
-#define RID_CONFIG 0xFF10
-#define RID_SSID 0xFF11
-#define RID_APLIST 0xFF12
-#define RID_DRVNAME 0xFF13
-#define RID_ETHERENCAP 0xFF14
-#define RID_WEP_TEMP 0xFF15
-#define RID_WEP_PERM 0xFF16
-#define RID_MODULATION 0xFF17
-#define RID_OPTIONS 0xFF18
-#define RID_ACTUALCONFIG 0xFF20 /*readonly*/
-#define RID_FACTORYCONFIG 0xFF21
-#define RID_UNKNOWN22 0xFF22
-#define RID_LEAPUSERNAME 0xFF23
-#define RID_LEAPPASSWORD 0xFF24
-#define RID_STATUS 0xFF50
-#define RID_BEACON_HST 0xFF51
-#define RID_BUSY_HST 0xFF52
-#define RID_RETRIES_HST 0xFF53
-#define RID_UNKNOWN54 0xFF54
-#define RID_UNKNOWN55 0xFF55
-#define RID_UNKNOWN56 0xFF56
-#define RID_MIC 0xFF57
-#define RID_STATS16 0xFF60
-#define RID_STATS16DELTA 0xFF61
-#define RID_STATS16DELTACLEAR 0xFF62
-#define RID_STATS 0xFF68
-#define RID_STATSDELTA 0xFF69
-#define RID_STATSDELTACLEAR 0xFF6A
-#define RID_ECHOTEST_RID 0xFF70
-#define RID_ECHOTEST_RESULTS 0xFF71
-#define RID_BSSLISTFIRST 0xFF72
-#define RID_BSSLISTNEXT 0xFF73
-#define RID_WPA_BSSLISTFIRST 0xFF74
-#define RID_WPA_BSSLISTNEXT 0xFF75
-
-typedef struct {
- u16 cmd;
- u16 parm0;
- u16 parm1;
- u16 parm2;
-} Cmd;
-
-typedef struct {
- u16 status;
- u16 rsp0;
- u16 rsp1;
- u16 rsp2;
-} Resp;
-
-/*
- * Rids and endian-ness: The Rids will always be in cpu endian, since
- * this all the patches from the big-endian guys end up doing that.
- * so all rid access should use the read/writeXXXRid routines.
- */
-
-/* This structure came from an email sent to me from an engineer at
- aironet for inclusion into this driver */
-typedef struct WepKeyRid WepKeyRid;
-struct WepKeyRid {
- __le16 len;
- __le16 kindex;
- u8 mac[ETH_ALEN];
- __le16 klen;
- u8 key[16];
-} __packed;
-
-/* These structures are from the Aironet's PC4500 Developers Manual */
-typedef struct Ssid Ssid;
-struct Ssid {
- __le16 len;
- u8 ssid[32];
-} __packed;
-
-typedef struct SsidRid SsidRid;
-struct SsidRid {
- __le16 len;
- Ssid ssids[3];
-} __packed;
-
-typedef struct ModulationRid ModulationRid;
-struct ModulationRid {
- __le16 len;
- __le16 modulation;
-#define MOD_DEFAULT cpu_to_le16(0)
-#define MOD_CCK cpu_to_le16(1)
-#define MOD_MOK cpu_to_le16(2)
-} __packed;
-
-typedef struct ConfigRid ConfigRid;
-struct ConfigRid {
- __le16 len; /* sizeof(ConfigRid) */
- __le16 opmode; /* operating mode */
-#define MODE_STA_IBSS cpu_to_le16(0)
-#define MODE_STA_ESS cpu_to_le16(1)
-#define MODE_AP cpu_to_le16(2)
-#define MODE_AP_RPTR cpu_to_le16(3)
-#define MODE_CFG_MASK cpu_to_le16(0xff)
-#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */
-#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */
-#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */
-#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */
-#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */
-#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */
-#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */
-#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */
-#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */
- __le16 rmode; /* receive mode */
-#define RXMODE_BC_MC_ADDR cpu_to_le16(0)
-#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */
-#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */
-#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */
-#define RXMODE_RFMON_ANYBSS cpu_to_le16(4)
-#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */
-#define RXMODE_MASK cpu_to_le16(255)
-#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */
-#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER)
-#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */
- __le16 fragThresh;
- __le16 rtsThres;
- u8 macAddr[ETH_ALEN];
- u8 rates[8];
- __le16 shortRetryLimit;
- __le16 longRetryLimit;
- __le16 txLifetime; /* in kusec */
- __le16 rxLifetime; /* in kusec */
- __le16 stationary;
- __le16 ordering;
- __le16 u16deviceType; /* for overriding device type */
- __le16 cfpRate;
- __le16 cfpDuration;
- __le16 _reserved1[3];
- /*---------- Scanning/Associating ----------*/
- __le16 scanMode;
-#define SCANMODE_ACTIVE cpu_to_le16(0)
-#define SCANMODE_PASSIVE cpu_to_le16(1)
-#define SCANMODE_AIROSCAN cpu_to_le16(2)
- __le16 probeDelay; /* in kusec */
- __le16 probeEnergyTimeout; /* in kusec */
- __le16 probeResponseTimeout;
- __le16 beaconListenTimeout;
- __le16 joinNetTimeout;
- __le16 authTimeout;
- __le16 authType;
-#define AUTH_OPEN cpu_to_le16(0x1)
-#define AUTH_ENCRYPT cpu_to_le16(0x101)
-#define AUTH_SHAREDKEY cpu_to_le16(0x102)
-#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200)
- __le16 associationTimeout;
- __le16 specifiedApTimeout;
- __le16 offlineScanInterval;
- __le16 offlineScanDuration;
- __le16 linkLossDelay;
- __le16 maxBeaconLostTime;
- __le16 refreshInterval;
-#define DISABLE_REFRESH cpu_to_le16(0xFFFF)
- __le16 _reserved1a[1];
- /*---------- Power save operation ----------*/
- __le16 powerSaveMode;
-#define POWERSAVE_CAM cpu_to_le16(0)
-#define POWERSAVE_PSP cpu_to_le16(1)
-#define POWERSAVE_PSPCAM cpu_to_le16(2)
- __le16 sleepForDtims;
- __le16 listenInterval;
- __le16 fastListenInterval;
- __le16 listenDecay;
- __le16 fastListenDelay;
- __le16 _reserved2[2];
- /*---------- Ap/Ibss config items ----------*/
- __le16 beaconPeriod;
- __le16 atimDuration;
- __le16 hopPeriod;
- __le16 channelSet;
- __le16 channel;
- __le16 dtimPeriod;
- __le16 bridgeDistance;
- __le16 radioID;
- /*---------- Radio configuration ----------*/
- __le16 radioType;
-#define RADIOTYPE_DEFAULT cpu_to_le16(0)
-#define RADIOTYPE_802_11 cpu_to_le16(1)
-#define RADIOTYPE_LEGACY cpu_to_le16(2)
- u8 rxDiversity;
- u8 txDiversity;
- __le16 txPower;
-#define TXPOWER_DEFAULT 0
- __le16 rssiThreshold;
-#define RSSI_DEFAULT 0
- __le16 modulation;
-#define PREAMBLE_AUTO cpu_to_le16(0)
-#define PREAMBLE_LONG cpu_to_le16(1)
-#define PREAMBLE_SHORT cpu_to_le16(2)
- __le16 preamble;
- __le16 homeProduct;
- __le16 radioSpecific;
- /*---------- Aironet Extensions ----------*/
- u8 nodeName[16];
- __le16 arlThreshold;
- __le16 arlDecay;
- __le16 arlDelay;
- __le16 _reserved4[1];
- /*---------- Aironet Extensions ----------*/
- u8 magicAction;
-#define MAGIC_ACTION_STSCHG 1
-#define MAGIC_ACTION_RESUME 2
-#define MAGIC_IGNORE_MCAST (1<<8)
-#define MAGIC_IGNORE_BCAST (1<<9)
-#define MAGIC_SWITCH_TO_PSP (0<<10)
-#define MAGIC_STAY_IN_CAM (1<<10)
- u8 magicControl;
- __le16 autoWake;
-} __packed;
-
-typedef struct StatusRid StatusRid;
-struct StatusRid {
- __le16 len;
- u8 mac[ETH_ALEN];
- __le16 mode;
- __le16 errorCode;
- __le16 sigQuality;
- __le16 SSIDlen;
- char SSID[32];
- char apName[16];
- u8 bssid[4][ETH_ALEN];
- __le16 beaconPeriod;
- __le16 dimPeriod;
- __le16 atimDuration;
- __le16 hopPeriod;
- __le16 channelSet;
- __le16 channel;
- __le16 hopsToBackbone;
- __le16 apTotalLoad;
- __le16 generatedLoad;
- __le16 accumulatedArl;
- __le16 signalQuality;
- __le16 currentXmitRate;
- __le16 apDevExtensions;
- __le16 normalizedSignalStrength;
- __le16 shortPreamble;
- u8 apIP[4];
- u8 noisePercent; /* Noise percent in last second */
- u8 noisedBm; /* Noise dBm in last second */
- u8 noiseAvePercent; /* Noise percent in last minute */
- u8 noiseAvedBm; /* Noise dBm in last minute */
- u8 noiseMaxPercent; /* Highest noise percent in last minute */
- u8 noiseMaxdBm; /* Highest noise dbm in last minute */
- __le16 load;
- u8 carrier[4];
- __le16 assocStatus;
-#define STAT_NOPACKETS 0
-#define STAT_NOCARRIERSET 10
-#define STAT_GOTCARRIERSET 11
-#define STAT_WRONGSSID 20
-#define STAT_BADCHANNEL 25
-#define STAT_BADBITRATES 30
-#define STAT_BADPRIVACY 35
-#define STAT_APFOUND 40
-#define STAT_APREJECTED 50
-#define STAT_AUTHENTICATING 60
-#define STAT_DEAUTHENTICATED 61
-#define STAT_AUTHTIMEOUT 62
-#define STAT_ASSOCIATING 70
-#define STAT_DEASSOCIATED 71
-#define STAT_ASSOCTIMEOUT 72
-#define STAT_NOTAIROAP 73
-#define STAT_ASSOCIATED 80
-#define STAT_LEAPING 90
-#define STAT_LEAPFAILED 91
-#define STAT_LEAPTIMEDOUT 92
-#define STAT_LEAPCOMPLETE 93
-} __packed;
-
-typedef struct StatsRid StatsRid;
-struct StatsRid {
- __le16 len;
- __le16 spacer;
- __le32 vals[100];
-} __packed;
-
-typedef struct APListRid APListRid;
-struct APListRid {
- __le16 len;
- u8 ap[4][ETH_ALEN];
-} __packed;
-
-typedef struct CapabilityRid CapabilityRid;
-struct CapabilityRid {
- __le16 len;
- char oui[3];
- char zero;
- __le16 prodNum;
- char manName[32];
- char prodName[16];
- char prodVer[8];
- char factoryAddr[ETH_ALEN];
- char aironetAddr[ETH_ALEN];
- __le16 radioType;
- __le16 country;
- char callid[ETH_ALEN];
- char supportedRates[8];
- char rxDiversity;
- char txDiversity;
- __le16 txPowerLevels[8];
- __le16 hardVer;
- __le16 hardCap;
- __le16 tempRange;
- __le16 softVer;
- __le16 softSubVer;
- __le16 interfaceVer;
- __le16 softCap;
- __le16 bootBlockVer;
- __le16 requiredHard;
- __le16 extSoftCap;
-} __packed;
-
-/* Only present on firmware >= 5.30.17 */
-typedef struct BSSListRidExtra BSSListRidExtra;
-struct BSSListRidExtra {
- __le16 unknown[4];
- u8 fixed[12]; /* WLAN management frame */
- u8 iep[624];
-} __packed;
-
-typedef struct BSSListRid BSSListRid;
-struct BSSListRid {
- __le16 len;
- __le16 index; /* First is 0 and 0xffff means end of list */
-#define RADIO_FH 1 /* Frequency hopping radio type */
-#define RADIO_DS 2 /* Direct sequence radio type */
-#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
- __le16 radioType;
- u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
- u8 zero;
- u8 ssidLen;
- u8 ssid[32];
- __le16 dBm;
-#define CAP_ESS cpu_to_le16(1<<0)
-#define CAP_IBSS cpu_to_le16(1<<1)
-#define CAP_PRIVACY cpu_to_le16(1<<4)
-#define CAP_SHORTHDR cpu_to_le16(1<<5)
- __le16 cap;
- __le16 beaconInterval;
- u8 rates[8]; /* Same as rates for config rid */
- struct { /* For frequency hopping only */
- __le16 dwell;
- u8 hopSet;
- u8 hopPattern;
- u8 hopIndex;
- u8 fill;
- } fh;
- __le16 dsChannel;
- __le16 atimWindow;
-
- /* Only present on firmware >= 5.30.17 */
- BSSListRidExtra extra;
-} __packed;
-
-typedef struct {
- BSSListRid bss;
- struct list_head list;
-} BSSListElement;
-
-typedef struct tdsRssiEntry tdsRssiEntry;
-struct tdsRssiEntry {
- u8 rssipct;
- u8 rssidBm;
-} __packed;
-
-typedef struct tdsRssiRid tdsRssiRid;
-struct tdsRssiRid {
- u16 len;
- tdsRssiEntry x[256];
-} __packed;
-
-typedef struct MICRid MICRid;
-struct MICRid {
- __le16 len;
- __le16 state;
- __le16 multicastValid;
- u8 multicast[16];
- __le16 unicastValid;
- u8 unicast[16];
-} __packed;
-
-typedef struct MICBuffer MICBuffer;
-struct MICBuffer {
- __be16 typelen;
-
- union {
- u8 snap[8];
- struct {
- u8 dsap;
- u8 ssap;
- u8 control;
- u8 orgcode[3];
- u8 fieldtype[2];
- } llc;
- } u;
- __be32 mic;
- __be32 seq;
-} __packed;
-
-typedef struct {
- u8 da[ETH_ALEN];
- u8 sa[ETH_ALEN];
-} etherHead;
-
-#define TXCTL_TXOK (1<<1) /* report if tx is ok */
-#define TXCTL_TXEX (1<<2) /* report if tx fails */
-#define TXCTL_802_3 (0<<3) /* 802.3 packet */
-#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
-#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
-#define TXCTL_LLC (1<<4) /* payload is llc */
-#define TXCTL_RELEASE (0<<5) /* release after completion */
-#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
-
-#define BUSY_FID 0x10000
-
-#ifdef CISCO_EXT
-#define AIROMAGIC 0xa55a
-/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
-#ifdef SIOCIWFIRSTPRIV
-#ifdef SIOCDEVPRIVATE
-#define AIROOLDIOCTL SIOCDEVPRIVATE
-#define AIROOLDIDIFC AIROOLDIOCTL + 1
-#endif /* SIOCDEVPRIVATE */
-#else /* SIOCIWFIRSTPRIV */
-#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
-#endif /* SIOCIWFIRSTPRIV */
-/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
- * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
- * only and don't return the modified struct ifreq to the application which
- * is usually a problem. - Jean II */
-#define AIROIOCTL SIOCIWFIRSTPRIV
-#define AIROIDIFC AIROIOCTL + 1
-
-/* Ioctl constants to be used in airo_ioctl.command */
-
-#define AIROGCAP 0 // Capability rid
-#define AIROGCFG 1 // USED A LOT
-#define AIROGSLIST 2 // System ID list
-#define AIROGVLIST 3 // List of specified AP's
-#define AIROGDRVNAM 4 // NOTUSED
-#define AIROGEHTENC 5 // NOTUSED
-#define AIROGWEPKTMP 6
-#define AIROGWEPKNV 7
-#define AIROGSTAT 8
-#define AIROGSTATSC32 9
-#define AIROGSTATSD32 10
-#define AIROGMICRID 11
-#define AIROGMICSTATS 12
-#define AIROGFLAGS 13
-#define AIROGID 14
-#define AIRORRID 15
-#define AIRORSWVERSION 17
-
-/* Leave gap of 40 commands after AIROGSTATSD32 for future */
-
-#define AIROPCAP AIROGSTATSD32 + 40
-#define AIROPVLIST AIROPCAP + 1
-#define AIROPSLIST AIROPVLIST + 1
-#define AIROPCFG AIROPSLIST + 1
-#define AIROPSIDS AIROPCFG + 1
-#define AIROPAPLIST AIROPSIDS + 1
-#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */
-#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */
-#define AIROPSTCLR AIROPMACOFF + 1
-#define AIROPWEPKEY AIROPSTCLR + 1
-#define AIROPWEPKEYNV AIROPWEPKEY + 1
-#define AIROPLEAPPWD AIROPWEPKEYNV + 1
-#define AIROPLEAPUSR AIROPLEAPPWD + 1
-
-/* Flash codes */
-
-#define AIROFLSHRST AIROPWEPKEYNV + 40
-#define AIROFLSHGCHR AIROFLSHRST + 1
-#define AIROFLSHSTFL AIROFLSHGCHR + 1
-#define AIROFLSHPCHR AIROFLSHSTFL + 1
-#define AIROFLPUTBUF AIROFLSHPCHR + 1
-#define AIRORESTART AIROFLPUTBUF + 1
-
-#define FLASHSIZE 32768
-#define AUXMEMSIZE (256 * 1024)
-
-typedef struct aironet_ioctl {
- unsigned short command; // What to do
- unsigned short len; // Len of data
- unsigned short ridnum; // rid number
- unsigned char __user *data; // d-data
-} aironet_ioctl;
-
-static const char swversion[] = "2.1";
-#endif /* CISCO_EXT */
-
-#define NUM_MODULES 2
-#define MIC_MSGLEN_MAX 2400
-#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
-#define AIRO_DEF_MTU 2312
-
-typedef struct {
- u32 size; // size
- u8 enabled; // MIC enabled or not
- u32 rxSuccess; // successful packets received
- u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison
- u32 rxNotMICed; // pkts dropped due to not being MIC'd
- u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed
- u32 rxWrongSequence; // pkts dropped due to sequence number violation
- u32 reserve[32];
-} mic_statistics;
-
-typedef struct {
- u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
- u64 accum; // accumulated mic, reduced to u32 in final()
- int position; // current position (byte offset) in message
- union {
- u8 d8[4];
- __be32 d32;
- } part; // saves partial message word across update() calls
-} emmh32_context;
-
-typedef struct {
- emmh32_context seed; // Context - the seed
- u32 rx; // Received sequence number
- u32 tx; // Tx sequence number
- u32 window; // Start of window
- u8 valid; // Flag to say if context is valid or not
- u8 key[16];
-} miccntx;
-
-typedef struct {
- miccntx mCtx; // Multicast context
- miccntx uCtx; // Unicast context
-} mic_module;
-
-typedef struct {
- unsigned int rid: 16;
- unsigned int len: 15;
- unsigned int valid: 1;
- dma_addr_t host_addr;
-} Rid;
-
-typedef struct {
- unsigned int offset: 15;
- unsigned int eoc: 1;
- unsigned int len: 15;
- unsigned int valid: 1;
- dma_addr_t host_addr;
-} TxFid;
-
-struct rx_hdr {
- __le16 status, len;
- u8 rssi[2];
- u8 rate;
- u8 freq;
- __le16 tmp[4];
-} __packed;
-
-typedef struct {
- unsigned int ctl: 15;
- unsigned int rdy: 1;
- unsigned int len: 15;
- unsigned int valid: 1;
- dma_addr_t host_addr;
-} RxFid;
-
-/*
- * Host receive descriptor
- */
-typedef struct {
- unsigned char __iomem *card_ram_off; /* offset into card memory of the
- desc */
- RxFid rx_desc; /* card receive descriptor */
- char *virtual_host_addr; /* virtual address of host receive
- buffer */
- int pending;
-} HostRxDesc;
-
-/*
- * Host transmit descriptor
- */
-typedef struct {
- unsigned char __iomem *card_ram_off; /* offset into card memory of the
- desc */
- TxFid tx_desc; /* card transmit descriptor */
- char *virtual_host_addr; /* virtual address of host receive
- buffer */
- int pending;
-} HostTxDesc;
-
-/*
- * Host RID descriptor
- */
-typedef struct {
- unsigned char __iomem *card_ram_off; /* offset into card memory of the
- descriptor */
- Rid rid_desc; /* card RID descriptor */
- char *virtual_host_addr; /* virtual address of host receive
- buffer */
-} HostRidDesc;
-
-typedef struct {
- u16 sw0;
- u16 sw1;
- u16 status;
- u16 len;
-#define HOST_SET (1 << 0)
-#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
-#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
-#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
-#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
-#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
-#define HOST_CLR_AID (1 << 7) /* clear AID failure */
-#define HOST_RTS (1 << 9) /* Force RTS use */
-#define HOST_SHORT (1 << 10) /* Do short preamble */
- u16 ctl;
- u16 aid;
- u16 retries;
- u16 fill;
-} TxCtlHdr;
-
-typedef struct {
- u16 ctl;
- u16 duration;
- char addr1[6];
- char addr2[6];
- char addr3[6];
- u16 seq;
- char addr4[6];
-} WifiHdr;
-
-
-typedef struct {
- TxCtlHdr ctlhdr;
- u16 fill1;
- u16 fill2;
- WifiHdr wifihdr;
- u16 gaplen;
- u16 status;
-} WifiCtlHdr;
-
-static WifiCtlHdr wifictlhdr8023 = {
- .ctlhdr = {
- .ctl = HOST_DONT_RLSE,
- }
-};
-
-// A few details needed for WEP (Wireless Equivalent Privacy)
-#define MAX_KEY_SIZE 13 // 128 (?) bits
-#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP
-typedef struct wep_key_t {
- u16 len;
- u8 key[16]; /* 40-bit and 104-bit keys */
-} wep_key_t;
-
-/* List of Wireless Handlers (new API) */
-static const struct iw_handler_def airo_handler_def;
-
-static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
-
-struct airo_info;
-
-static int get_dec_u16( char *buffer, int *start, int limit );
-static void OUT4500( struct airo_info *, u16 register, u16 value );
-static unsigned short IN4500( struct airo_info *, u16 register );
-static u16 setup_card(struct airo_info*, u8 *mac, int lock);
-static int enable_MAC(struct airo_info *ai, int lock);
-static void disable_MAC(struct airo_info *ai, int lock);
-static void enable_interrupts(struct airo_info*);
-static void disable_interrupts(struct airo_info*);
-static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
-static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
-static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
- int whichbap);
-static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
- int whichbap);
-static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
- int whichbap);
-static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
-static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
-static int PC4500_writerid(struct airo_info*, u16 rid, const void
- *pBuf, int len, int lock);
-static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
- int len, int dummy );
-static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
-static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
-static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
-
-static int mpi_send_packet (struct net_device *dev);
-static void mpi_unmap_card(struct pci_dev *pci);
-static void mpi_receive_802_3(struct airo_info *ai);
-static void mpi_receive_802_11(struct airo_info *ai);
-static int waitbusy (struct airo_info *ai);
-
-static irqreturn_t airo_interrupt( int irq, void* dev_id);
-static int airo_thread(void *data);
-static void timer_func( struct net_device *dev );
-static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
-static void airo_read_wireless_stats (struct airo_info *local);
-#ifdef CISCO_EXT
-static int readrids(struct net_device *dev, aironet_ioctl *comp);
-static int writerids(struct net_device *dev, aironet_ioctl *comp);
-static int flashcard(struct net_device *dev, aironet_ioctl *comp);
-#endif /* CISCO_EXT */
-static void micinit(struct airo_info *ai);
-static int micsetup(struct airo_info *ai);
-static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
-static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
-
-static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
-static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
-
-static void airo_networks_free(struct airo_info *ai);
-
-struct airo_info {
- struct net_device *dev;
- struct list_head dev_list;
- /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we
- use the high bit to mark whether it is in use. */
-#define MAX_FIDS 6
-#define MPI_MAX_FIDS 1
- u32 fids[MAX_FIDS];
- ConfigRid config;
- char keyindex; // Used with auto wep
- char defindex; // Used with auto wep
- struct proc_dir_entry *proc_entry;
- spinlock_t aux_lock;
-#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
-#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
-#define FLAG_RADIO_MASK 0x03
-#define FLAG_ENABLED 2
-#define FLAG_ADHOC 3 /* Needed by MIC */
-#define FLAG_MIC_CAPABLE 4
-#define FLAG_UPDATE_MULTI 5
-#define FLAG_UPDATE_UNI 6
-#define FLAG_802_11 7
-#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
-#define FLAG_PENDING_XMIT 9
-#define FLAG_PENDING_XMIT11 10
-#define FLAG_MPI 11
-#define FLAG_REGISTERED 12
-#define FLAG_COMMIT 13
-#define FLAG_RESET 14
-#define FLAG_FLASHING 15
-#define FLAG_WPA_CAPABLE 16
- unsigned long flags;
-#define JOB_DIE 0
-#define JOB_XMIT 1
-#define JOB_XMIT11 2
-#define JOB_STATS 3
-#define JOB_PROMISC 4
-#define JOB_MIC 5
-#define JOB_EVENT 6
-#define JOB_AUTOWEP 7
-#define JOB_WSTATS 8
-#define JOB_SCAN_RESULTS 9
- unsigned long jobs;
- int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
- int whichbap);
- unsigned short *flash;
- tdsRssiEntry *rssi;
- struct task_struct *list_bss_task;
- struct task_struct *airo_thread_task;
- struct semaphore sem;
- wait_queue_head_t thr_wait;
- unsigned long expires;
- struct {
- struct sk_buff *skb;
- int fid;
- } xmit, xmit11;
- struct net_device *wifidev;
- struct iw_statistics wstats; // wireless stats
- unsigned long scan_timeout; /* Time scan should be read */
- struct iw_spy_data spy_data;
- struct iw_public_data wireless_data;
- /* MIC stuff */
- struct crypto_cipher *tfm;
- mic_module mod[2];
- mic_statistics micstats;
- HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
- HostTxDesc txfids[MPI_MAX_FIDS];
- HostRidDesc config_desc;
- unsigned long ridbus; // phys addr of config_desc
- struct sk_buff_head txq;// tx queue used by mpi350 code
- struct pci_dev *pci;
- unsigned char __iomem *pcimem;
- unsigned char __iomem *pciaux;
- unsigned char *shared;
- dma_addr_t shared_dma;
- pm_message_t power;
- SsidRid *SSID;
- APListRid APList;
-#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
- char proc_name[IFNAMSIZ];
-
- int wep_capable;
- int max_wep_idx;
- int last_auth;
-
- /* WPA-related stuff */
- unsigned int bssListFirst;
- unsigned int bssListNext;
- unsigned int bssListRidLen;
-
- struct list_head network_list;
- struct list_head network_free_list;
- BSSListElement *networks;
-};
-
-static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
- int whichbap)
-{
- return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
-}
-
-static int setup_proc_entry( struct net_device *dev,
- struct airo_info *apriv );
-static int takedown_proc_entry( struct net_device *dev,
- struct airo_info *apriv );
-
-static int cmdreset(struct airo_info *ai);
-static int setflashmode (struct airo_info *ai);
-static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
-static int flashputbuf(struct airo_info *ai);
-static int flashrestart(struct airo_info *ai,struct net_device *dev);
-
-#define airo_print(type, name, fmt, args...) \
- printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
-
-#define airo_print_info(name, fmt, args...) \
- airo_print(KERN_INFO, name, fmt, ##args)
-
-#define airo_print_dbg(name, fmt, args...) \
- airo_print(KERN_DEBUG, name, fmt, ##args)
-
-#define airo_print_warn(name, fmt, args...) \
- airo_print(KERN_WARNING, name, fmt, ##args)
-
-#define airo_print_err(name, fmt, args...) \
- airo_print(KERN_ERR, name, fmt, ##args)
-
-#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
-
-/***********************************************************************
- * MIC ROUTINES *
- ***********************************************************************
- */
-
-static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
-static void MoveWindow(miccntx *context, u32 micSeq);
-static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
- struct crypto_cipher *tfm);
-static void emmh32_init(emmh32_context *context);
-static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
-static void emmh32_final(emmh32_context *context, u8 digest[4]);
-static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
-
-static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len,
- struct crypto_cipher *tfm)
-{
- /* If the current MIC context is valid and its key is the same as
- * the MIC register, there's nothing to do.
- */
- if (cur->valid && (memcmp(cur->key, key, key_len) == 0))
- return;
-
- /* Age current mic Context */
- memcpy(old, cur, sizeof(*cur));
-
- /* Initialize new context */
- memcpy(cur->key, key, key_len);
- cur->window = 33; /* Window always points to the middle */
- cur->rx = 0; /* Rx Sequence numbers */
- cur->tx = 0; /* Tx sequence numbers */
- cur->valid = 1; /* Key is now valid */
-
- /* Give key to mic seed */
- emmh32_setseed(&cur->seed, key, key_len, tfm);
-}
-
-/* micinit - Initialize mic seed */
-
-static void micinit(struct airo_info *ai)
-{
- MICRid mic_rid;
-
- clear_bit(JOB_MIC, &ai->jobs);
- PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
- up(&ai->sem);
-
- ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0;
- if (!ai->micstats.enabled) {
- /* So next time we have a valid key and mic is enabled, we will
- * update the sequence number if the key is the same as before.
- */
- ai->mod[0].uCtx.valid = 0;
- ai->mod[0].mCtx.valid = 0;
- return;
- }
-
- if (mic_rid.multicastValid) {
- age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx,
- mic_rid.multicast, sizeof(mic_rid.multicast),
- ai->tfm);
- }
-
- if (mic_rid.unicastValid) {
- age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx,
- mic_rid.unicast, sizeof(mic_rid.unicast),
- ai->tfm);
- }
-}
-
-/* micsetup - Get ready for business */
-
-static int micsetup(struct airo_info *ai) {
- int i;
-
- if (ai->tfm == NULL)
- ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
-
- if (IS_ERR(ai->tfm)) {
- airo_print_err(ai->dev->name, "failed to load transform for AES");
- ai->tfm = NULL;
- return ERROR;
- }
-
- for (i=0; i < NUM_MODULES; i++) {
- memset(&ai->mod[i].mCtx,0,sizeof(miccntx));
- memset(&ai->mod[i].uCtx,0,sizeof(miccntx));
- }
- return SUCCESS;
-}
-
-static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
-
-/*===========================================================================
- * Description: Mic a packet
- *
- * Inputs: etherHead * pointer to an 802.3 frame
- *
- * Returns: BOOLEAN if successful, otherwise false.
- * PacketTxLen will be updated with the mic'd packets size.
- *
- * Caveats: It is assumed that the frame buffer will already
- * be big enough to hold the largets mic message possible.
- * (No memory allocation is done here).
- *
- * Author: sbraneky (10/15/01)
- * Merciless hacks by rwilcher (1/14/02)
- */
-
-static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen)
-{
- miccntx *context;
-
- // Determine correct context
- // If not adhoc, always use unicast key
-
- if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
- context = &ai->mod[0].mCtx;
- else
- context = &ai->mod[0].uCtx;
-
- if (!context->valid)
- return ERROR;
-
- mic->typelen = htons(payLen + 16); //Length of Mic'd packet
-
- memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
-
- // Add Tx sequence
- mic->seq = htonl(context->tx);
- context->tx += 2;
-
- emmh32_init(&context->seed); // Mic the packet
- emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA
- emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap
- emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ
- emmh32_update(&context->seed,(u8*)(frame + 1),payLen); //payload
- emmh32_final(&context->seed, (u8*)&mic->mic);
-
- /* New Type/length ?????????? */
- mic->typelen = 0; //Let NIC know it could be an oversized packet
- return SUCCESS;
-}
-
-typedef enum {
- NONE,
- NOMIC,
- NOMICPLUMMED,
- SEQUENCE,
- INCORRECTMIC,
-} mic_error;
-
-/*===========================================================================
- * Description: Decapsulates a MIC'd packet and returns the 802.3 packet
- * (removes the MIC stuff) if packet is a valid packet.
- *
- * Inputs: etherHead pointer to the 802.3 packet
- *
- * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
- *
- * Author: sbraneky (10/15/01)
- * Merciless hacks by rwilcher (1/14/02)
- *---------------------------------------------------------------------------
- */
-
-static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
-{
- int i;
- u32 micSEQ;
- miccntx *context;
- u8 digest[4];
- mic_error micError = NONE;
-
- // Check if the packet is a Mic'd packet
-
- if (!ai->micstats.enabled) {
- //No Mic set or Mic OFF but we received a MIC'd packet.
- if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
- ai->micstats.rxMICPlummed++;
- return ERROR;
- }
- return SUCCESS;
- }
-
- if (ntohs(mic->typelen) == 0x888E)
- return SUCCESS;
-
- if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
- // Mic enabled but packet isn't Mic'd
- ai->micstats.rxMICPlummed++;
- return ERROR;
- }
-
- micSEQ = ntohl(mic->seq); //store SEQ as CPU order
-
- //At this point we a have a mic'd packet and mic is enabled
- //Now do the mic error checking.
-
- //Receive seq must be odd
- if ( (micSEQ & 1) == 0 ) {
- ai->micstats.rxWrongSequence++;
- return ERROR;
- }
-
- for (i = 0; i < NUM_MODULES; i++) {
- int mcast = eth->da[0] & 1;
- //Determine proper context
- context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
-
- //Make sure context is valid
- if (!context->valid) {
- if (i == 0)
- micError = NOMICPLUMMED;
- continue;
- }
- //DeMic it
-
- if (!mic->typelen)
- mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
-
- emmh32_init(&context->seed);
- emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
- emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
- emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq));
- emmh32_update(&context->seed, (u8 *)(eth + 1),payLen);
- //Calculate MIC
- emmh32_final(&context->seed, digest);
-
- if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
- //Invalid Mic
- if (i == 0)
- micError = INCORRECTMIC;
- continue;
- }
-
- //Check Sequence number if mics pass
- if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
- ai->micstats.rxSuccess++;
- return SUCCESS;
- }
- if (i == 0)
- micError = SEQUENCE;
- }
-
- // Update statistics
- switch (micError) {
- case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break;
- case SEQUENCE: ai->micstats.rxWrongSequence++; break;
- case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
- case NONE: break;
- case NOMIC: break;
- }
- return ERROR;
-}
-
-/*===========================================================================
- * Description: Checks the Rx Seq number to make sure it is valid
- * and hasn't already been received
- *
- * Inputs: miccntx - mic context to check seq against
- * micSeq - the Mic seq number
- *
- * Returns: TRUE if valid otherwise FALSE.
- *
- * Author: sbraneky (10/15/01)
- * Merciless hacks by rwilcher (1/14/02)
- *---------------------------------------------------------------------------
- */
-
-static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq)
-{
- u32 seq,index;
-
- //Allow for the ap being rebooted - if it is then use the next
- //sequence number of the current sequence number - might go backwards
-
- if (mcast) {
- if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
- clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
- context->window = (micSeq > 33) ? micSeq : 33;
- context->rx = 0; // Reset rx
- }
- } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
- clear_bit (FLAG_UPDATE_UNI, &ai->flags);
- context->window = (micSeq > 33) ? micSeq : 33; // Move window
- context->rx = 0; // Reset rx
- }
-
- //Make sequence number relative to START of window
- seq = micSeq - (context->window - 33);
-
- //Too old of a SEQ number to check.
- if ((s32)seq < 0)
- return ERROR;
-
- if ( seq > 64 ) {
- //Window is infinite forward
- MoveWindow(context,micSeq);
- return SUCCESS;
- }
-
- // We are in the window. Now check the context rx bit to see if it was already sent
- seq >>= 1; //divide by 2 because we only have odd numbers
- index = 1 << seq; //Get an index number
-
- if (!(context->rx & index)) {
- //micSEQ falls inside the window.
- //Add seqence number to the list of received numbers.
- context->rx |= index;
-
- MoveWindow(context,micSeq);
-
- return SUCCESS;
- }
- return ERROR;
-}
-
-static void MoveWindow(miccntx *context, u32 micSeq)
-{
- u32 shift;
-
- //Move window if seq greater than the middle of the window
- if (micSeq > context->window) {
- shift = (micSeq - context->window) >> 1;
-
- //Shift out old
- if (shift < 32)
- context->rx >>= shift;
- else
- context->rx = 0;
-
- context->window = micSeq; //Move window
- }
-}
-
-/*==============================================*/
-/*========== EMMH ROUTINES ====================*/
-/*==============================================*/
-
-/* mic accumulate */
-#define MIC_ACCUM(val) \
- context->accum += (u64)(val) * context->coeff[coeff_position++];
-
-static unsigned char aes_counter[16];
-
-/* expand the key to fill the MMH coefficient array */
-static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
- struct crypto_cipher *tfm)
-{
- /* take the keying material, expand if necessary, truncate at 16-bytes */
- /* run through AES counter mode to generate context->coeff[] */
-
- int i,j;
- u32 counter;
- u8 *cipher, plain[16];
-
- crypto_cipher_setkey(tfm, pkey, 16);
- counter = 0;
- for (i = 0; i < ARRAY_SIZE(context->coeff); ) {
- aes_counter[15] = (u8)(counter >> 0);
- aes_counter[14] = (u8)(counter >> 8);
- aes_counter[13] = (u8)(counter >> 16);
- aes_counter[12] = (u8)(counter >> 24);
- counter++;
- memcpy (plain, aes_counter, 16);
- crypto_cipher_encrypt_one(tfm, plain, plain);
- cipher = plain;
- for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) {
- context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]);
- j += 4;
- }
- }
-}
-
-/* prepare for calculation of a new mic */
-static void emmh32_init(emmh32_context *context)
-{
- /* prepare for new mic calculation */
- context->accum = 0;
- context->position = 0;
-}
-
-/* add some bytes to the mic calculation */
-static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
-{
- int coeff_position, byte_position;
-
- if (len == 0) return;
-
- coeff_position = context->position >> 2;
-
- /* deal with partial 32-bit word left over from last update */
- byte_position = context->position & 3;
- if (byte_position) {
- /* have a partial word in part to deal with */
- do {
- if (len == 0) return;
- context->part.d8[byte_position++] = *pOctets++;
- context->position++;
- len--;
- } while (byte_position < 4);
- MIC_ACCUM(ntohl(context->part.d32));
- }
-
- /* deal with full 32-bit words */
- while (len >= 4) {
- MIC_ACCUM(ntohl(*(__be32 *)pOctets));
- context->position += 4;
- pOctets += 4;
- len -= 4;
- }
-
- /* deal with partial 32-bit word that will be left over from this update */
- byte_position = 0;
- while (len > 0) {
- context->part.d8[byte_position++] = *pOctets++;
- context->position++;
- len--;
- }
-}
-
-/* mask used to zero empty bytes for final partial word */
-static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
-
-/* calculate the mic */
-static void emmh32_final(emmh32_context *context, u8 digest[4])
-{
- int coeff_position, byte_position;
- u32 val;
-
- u64 sum, utmp;
- s64 stmp;
-
- coeff_position = context->position >> 2;
-
- /* deal with partial 32-bit word left over from last update */
- byte_position = context->position & 3;
- if (byte_position) {
- /* have a partial word in part to deal with */
- val = ntohl(context->part.d32);
- MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */
- }
-
- /* reduce the accumulated u64 to a 32-bit MIC */
- sum = context->accum;
- stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15);
- utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
- sum = utmp & 0xffffffffLL;
- if (utmp > 0x10000000fLL)
- sum -= 15;
-
- val = (u32)sum;
- digest[0] = (val>>24) & 0xFF;
- digest[1] = (val>>16) & 0xFF;
- digest[2] = (val>>8) & 0xFF;
- digest[3] = val & 0xFF;
-}
-
-static int readBSSListRid(struct airo_info *ai, int first,
- BSSListRid *list)
-{
- Cmd cmd;
- Resp rsp;
-
- if (first == 1) {
- if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd=CMD_LISTBSS;
- if (down_interruptible(&ai->sem))
- return -ERESTARTSYS;
- ai->list_bss_task = current;
- issuecommand(ai, &cmd, &rsp);
- up(&ai->sem);
- /* Let the command take effect */
- schedule_timeout_uninterruptible(3 * HZ);
- ai->list_bss_task = NULL;
- }
- return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
- list, ai->bssListRidLen, 1);
-}
-
-static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
-{
- return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
- wkr, sizeof(*wkr), lock);
-}
-
-static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
-{
- int rc;
- rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
- if (rc!=SUCCESS)
- airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
- if (perm) {
- rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
- if (rc!=SUCCESS)
- airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
- }
- return rc;
-}
-
-static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
-{
- return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
-}
-
-static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
-{
- return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
-}
-
-static int readConfigRid(struct airo_info *ai, int lock)
-{
- int rc;
- ConfigRid cfg;
-
- if (ai->config.len)
- return SUCCESS;
-
- rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
- if (rc != SUCCESS)
- return rc;
-
- ai->config = cfg;
- return SUCCESS;
-}
-
-static inline void checkThrottle(struct airo_info *ai)
-{
- int i;
-/* Old hardware had a limit on encryption speed */
- if (ai->config.authType != AUTH_OPEN && maxencrypt) {
- for(i=0; i<8; i++) {
- if (ai->config.rates[i] > maxencrypt) {
- ai->config.rates[i] = 0;
- }
- }
- }
-}
-
-static int writeConfigRid(struct airo_info *ai, int lock)
-{
- ConfigRid cfgr;
-
- if (!test_bit (FLAG_COMMIT, &ai->flags))
- return SUCCESS;
-
- clear_bit (FLAG_COMMIT, &ai->flags);
- clear_bit (FLAG_RESET, &ai->flags);
- checkThrottle(ai);
- cfgr = ai->config;
-
- if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
- set_bit(FLAG_ADHOC, &ai->flags);
- else
- clear_bit(FLAG_ADHOC, &ai->flags);
-
- return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
-}
-
-static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
-{
- return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
-}
-
-static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
-{
- return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
-}
-
-static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
-{
- return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
-}
-
-static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
-{
- return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
-}
-
-static void try_auto_wep(struct airo_info *ai)
-{
- if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) {
- ai->expires = RUN_AT(3*HZ);
- wake_up_interruptible(&ai->thr_wait);
- }
-}
-
-static int airo_open(struct net_device *dev) {
- struct airo_info *ai = dev->ml_priv;
- int rc = 0;
-
- if (test_bit(FLAG_FLASHING, &ai->flags))
- return -EIO;
-
- /* Make sure the card is configured.
- * Wireless Extensions may postpone config changes until the card
- * is open (to pipeline changes and speed-up card setup). If
- * those changes are not yet committed, do it now - Jean II */
- if (test_bit(FLAG_COMMIT, &ai->flags)) {
- disable_MAC(ai, 1);
- writeConfigRid(ai, 1);
- }
-
- if (ai->wifidev != dev) {
- clear_bit(JOB_DIE, &ai->jobs);
- ai->airo_thread_task = kthread_run(airo_thread, dev, "%s",
- dev->name);
- if (IS_ERR(ai->airo_thread_task))
- return (int)PTR_ERR(ai->airo_thread_task);
-
- rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
- dev->name, dev);
- if (rc) {
- airo_print_err(dev->name,
- "register interrupt %d failed, rc %d",
- dev->irq, rc);
- set_bit(JOB_DIE, &ai->jobs);
- kthread_stop(ai->airo_thread_task);
- return rc;
- }
-
- /* Power on the MAC controller (which may have been disabled) */
- clear_bit(FLAG_RADIO_DOWN, &ai->flags);
- enable_interrupts(ai);
-
- try_auto_wep(ai);
- }
- enable_MAC(ai, 1);
-
- netif_start_queue(dev);
- return 0;
-}
-
-static netdev_tx_t mpi_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- int npacks, pending;
- unsigned long flags;
- struct airo_info *ai = dev->ml_priv;
-
- if (!skb) {
- airo_print_err(dev->name, "%s: skb == NULL!",__func__);
- return NETDEV_TX_OK;
- }
- npacks = skb_queue_len (&ai->txq);
-
- if (npacks >= MAXTXQ - 1) {
- netif_stop_queue (dev);
- if (npacks > MAXTXQ) {
- dev->stats.tx_fifo_errors++;
- return NETDEV_TX_BUSY;
- }
- skb_queue_tail (&ai->txq, skb);
- return NETDEV_TX_OK;
- }
-
- spin_lock_irqsave(&ai->aux_lock, flags);
- skb_queue_tail (&ai->txq, skb);
- pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
- spin_unlock_irqrestore(&ai->aux_lock,flags);
- netif_wake_queue (dev);
-
- if (pending == 0) {
- set_bit(FLAG_PENDING_XMIT, &ai->flags);
- mpi_send_packet (dev);
- }
- return NETDEV_TX_OK;
-}
-
-/*
- * @mpi_send_packet
- *
- * Attempt to transmit a packet. Can be called from interrupt
- * or transmit . return number of packets we tried to send
- */
-
-static int mpi_send_packet (struct net_device *dev)
-{
- struct sk_buff *skb;
- unsigned char *buffer;
- s16 len;
- __le16 *payloadLen;
- struct airo_info *ai = dev->ml_priv;
- u8 *sendbuf;
-
- /* get a packet to send */
-
- if ((skb = skb_dequeue(&ai->txq)) == NULL) {
- airo_print_err(dev->name,
- "%s: Dequeue'd zero in send_packet()",
- __func__);
- return 0;
- }
-
- /* check min length*/
- len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- buffer = skb->data;
-
- ai->txfids[0].tx_desc.offset = 0;
- ai->txfids[0].tx_desc.valid = 1;
- ai->txfids[0].tx_desc.eoc = 1;
- ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
-
-/*
- * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
- * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
- * is immediately after it. ------------------------------------------------
- * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
- * ------------------------------------------------
- */
-
- memcpy(ai->txfids[0].virtual_host_addr,
- (char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
-
- payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
- sizeof(wifictlhdr8023));
- sendbuf = ai->txfids[0].virtual_host_addr +
- sizeof(wifictlhdr8023) + 2 ;
-
- /*
- * Firmware automatically puts 802 header on so
- * we don't need to account for it in the length
- */
- if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
- (ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
- MICBuffer pMic;
-
- if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
- return ERROR;
-
- *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
- ai->txfids[0].tx_desc.len += sizeof(pMic);
- /* copy data into airo dma buffer */
- memcpy (sendbuf, buffer, sizeof(etherHead));
- buffer += sizeof(etherHead);
- sendbuf += sizeof(etherHead);
- memcpy (sendbuf, &pMic, sizeof(pMic));
- sendbuf += sizeof(pMic);
- memcpy (sendbuf, buffer, len - sizeof(etherHead));
- } else {
- *payloadLen = cpu_to_le16(len - sizeof(etherHead));
-
- dev->trans_start = jiffies;
-
- /* copy data into airo dma buffer */
- memcpy(sendbuf, buffer, len);
- }
-
- memcpy_toio(ai->txfids[0].card_ram_off,
- &ai->txfids[0].tx_desc, sizeof(TxFid));
-
- OUT4500(ai, EVACK, 8);
-
- dev_kfree_skb_any(skb);
- return 1;
-}
-
-static void get_tx_error(struct airo_info *ai, s32 fid)
-{
- __le16 status;
-
- if (fid < 0)
- status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
- else {
- if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
- return;
- bap_read(ai, &status, 2, BAP0);
- }
- if (le16_to_cpu(status) & 2) /* Too many retries */
- ai->dev->stats.tx_aborted_errors++;
- if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
- ai->dev->stats.tx_heartbeat_errors++;
- if (le16_to_cpu(status) & 8) /* Aid fail */
- { }
- if (le16_to_cpu(status) & 0x10) /* MAC disabled */
- ai->dev->stats.tx_carrier_errors++;
- if (le16_to_cpu(status) & 0x20) /* Association lost */
- { }
- /* We produce a TXDROP event only for retry or lifetime
- * exceeded, because that's the only status that really mean
- * that this particular node went away.
- * Other errors means that *we* screwed up. - Jean II */
- if ((le16_to_cpu(status) & 2) ||
- (le16_to_cpu(status) & 4)) {
- union iwreq_data wrqu;
- char junk[0x18];
-
- /* Faster to skip over useless data than to do
- * another bap_setup(). We are at offset 0x6 and
- * need to go to 0x18 and read 6 bytes - Jean II */
- bap_read(ai, (__le16 *) junk, 0x18, BAP0);
-
- /* Copy 802.11 dest address.
- * We use the 802.11 header because the frame may
- * not be 802.3 or may be mangled...
- * In Ad-Hoc mode, it will be the node address.
- * In managed mode, it will be most likely the AP addr
- * User space will figure out how to convert it to
- * whatever it needs (IP address or else).
- * - Jean II */
- memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
- wrqu.addr.sa_family = ARPHRD_ETHER;
-
- /* Send event to user space */
- wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
- }
-}
-
-static void airo_end_xmit(struct net_device *dev) {
- u16 status;
- int i;
- struct airo_info *priv = dev->ml_priv;
- struct sk_buff *skb = priv->xmit.skb;
- int fid = priv->xmit.fid;
- u32 *fids = priv->fids;
-
- clear_bit(JOB_XMIT, &priv->jobs);
- clear_bit(FLAG_PENDING_XMIT, &priv->flags);
- status = transmit_802_3_packet (priv, fids[fid], skb->data);
- up(&priv->sem);
-
- i = 0;
- if ( status == SUCCESS ) {
- dev->trans_start = jiffies;
- for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
- } else {
- priv->fids[fid] &= 0xffff;
- dev->stats.tx_window_errors++;
- }
- if (i < MAX_FIDS / 2)
- netif_wake_queue(dev);
- dev_kfree_skb(skb);
-}
-
-static netdev_tx_t airo_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- s16 len;
- int i, j;
- struct airo_info *priv = dev->ml_priv;
- u32 *fids = priv->fids;
-
- if ( skb == NULL ) {
- airo_print_err(dev->name, "%s: skb == NULL!", __func__);
- return NETDEV_TX_OK;
- }
-
- /* Find a vacant FID */
- for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ );
- for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ );
-
- if ( j >= MAX_FIDS / 2 ) {
- netif_stop_queue(dev);
-
- if (i == MAX_FIDS / 2) {
- dev->stats.tx_fifo_errors++;
- return NETDEV_TX_BUSY;
- }
- }
- /* check min length*/
- len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- /* Mark fid as used & save length for later */
- fids[i] |= (len << 16);
- priv->xmit.skb = skb;
- priv->xmit.fid = i;
- if (down_trylock(&priv->sem) != 0) {
- set_bit(FLAG_PENDING_XMIT, &priv->flags);
- netif_stop_queue(dev);
- set_bit(JOB_XMIT, &priv->jobs);
- wake_up_interruptible(&priv->thr_wait);
- } else
- airo_end_xmit(dev);
- return NETDEV_TX_OK;
-}
-
-static void airo_end_xmit11(struct net_device *dev) {
- u16 status;
- int i;
- struct airo_info *priv = dev->ml_priv;
- struct sk_buff *skb = priv->xmit11.skb;
- int fid = priv->xmit11.fid;
- u32 *fids = priv->fids;
-
- clear_bit(JOB_XMIT11, &priv->jobs);
- clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
- status = transmit_802_11_packet (priv, fids[fid], skb->data);
- up(&priv->sem);
-
- i = MAX_FIDS / 2;
- if ( status == SUCCESS ) {
- dev->trans_start = jiffies;
- for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
- } else {
- priv->fids[fid] &= 0xffff;
- dev->stats.tx_window_errors++;
- }
- if (i < MAX_FIDS)
- netif_wake_queue(dev);
- dev_kfree_skb(skb);
-}
-
-static netdev_tx_t airo_start_xmit11(struct sk_buff *skb,
- struct net_device *dev)
-{
- s16 len;
- int i, j;
- struct airo_info *priv = dev->ml_priv;
- u32 *fids = priv->fids;
-
- if (test_bit(FLAG_MPI, &priv->flags)) {
- /* Not implemented yet for MPI350 */
- netif_stop_queue(dev);
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
-
- if ( skb == NULL ) {
- airo_print_err(dev->name, "%s: skb == NULL!", __func__);
- return NETDEV_TX_OK;
- }
-
- /* Find a vacant FID */
- for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ );
- for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ );
-
- if ( j >= MAX_FIDS ) {
- netif_stop_queue(dev);
-
- if (i == MAX_FIDS) {
- dev->stats.tx_fifo_errors++;
- return NETDEV_TX_BUSY;
- }
- }
- /* check min length*/
- len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- /* Mark fid as used & save length for later */
- fids[i] |= (len << 16);
- priv->xmit11.skb = skb;
- priv->xmit11.fid = i;
- if (down_trylock(&priv->sem) != 0) {
- set_bit(FLAG_PENDING_XMIT11, &priv->flags);
- netif_stop_queue(dev);
- set_bit(JOB_XMIT11, &priv->jobs);
- wake_up_interruptible(&priv->thr_wait);
- } else
- airo_end_xmit11(dev);
- return NETDEV_TX_OK;
-}
-
-static void airo_read_stats(struct net_device *dev)
-{
- struct airo_info *ai = dev->ml_priv;
- StatsRid stats_rid;
- __le32 *vals = stats_rid.vals;
-
- clear_bit(JOB_STATS, &ai->jobs);
- if (ai->power.event) {
- up(&ai->sem);
- return;
- }
- readStatsRid(ai, &stats_rid, RID_STATS, 0);
- up(&ai->sem);
-
- dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
- le32_to_cpu(vals[45]);
- dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
- le32_to_cpu(vals[41]);
- dev->stats.rx_bytes = le32_to_cpu(vals[92]);
- dev->stats.tx_bytes = le32_to_cpu(vals[91]);
- dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
- le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
- dev->stats.tx_errors = le32_to_cpu(vals[42]) +
- dev->stats.tx_fifo_errors;
- dev->stats.multicast = le32_to_cpu(vals[43]);
- dev->stats.collisions = le32_to_cpu(vals[89]);
-
- /* detailed rx_errors: */
- dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
- dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
- dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
- dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
-}
-
-static struct net_device_stats *airo_get_stats(struct net_device *dev)
-{
- struct airo_info *local = dev->ml_priv;
-
- if (!test_bit(JOB_STATS, &local->jobs)) {
- /* Get stats out of the card if available */
- if (down_trylock(&local->sem) != 0) {
- set_bit(JOB_STATS, &local->jobs);
- wake_up_interruptible(&local->thr_wait);
- } else
- airo_read_stats(dev);
- }
-
- return &dev->stats;
-}
-
-static void airo_set_promisc(struct airo_info *ai) {
- Cmd cmd;
- Resp rsp;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd=CMD_SETMODE;
- clear_bit(JOB_PROMISC, &ai->jobs);
- cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
- issuecommand(ai, &cmd, &rsp);
- up(&ai->sem);
-}
-
-static void airo_set_multicast_list(struct net_device *dev) {
- struct airo_info *ai = dev->ml_priv;
-
- if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
- change_bit(FLAG_PROMISC, &ai->flags);
- if (down_trylock(&ai->sem) != 0) {
- set_bit(JOB_PROMISC, &ai->jobs);
- wake_up_interruptible(&ai->thr_wait);
- } else
- airo_set_promisc(ai);
- }
-
- if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
- /* Turn on multicast. (Should be already setup...) */
- }
-}
-
-static int airo_set_mac_address(struct net_device *dev, void *p)
-{
- struct airo_info *ai = dev->ml_priv;
- struct sockaddr *addr = p;
-
- readConfigRid(ai, 1);
- memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
- set_bit (FLAG_COMMIT, &ai->flags);
- disable_MAC(ai, 1);
- writeConfigRid (ai, 1);
- enable_MAC(ai, 1);
- memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
- if (ai->wifidev)
- memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
- return 0;
-}
-
-static int airo_change_mtu(struct net_device *dev, int new_mtu)
-{
- if ((new_mtu < 68) || (new_mtu > 2400))
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
-static LIST_HEAD(airo_devices);
-
-static void add_airo_dev(struct airo_info *ai)
-{
- /* Upper layers already keep track of PCI devices,
- * so we only need to remember our non-PCI cards. */
- if (!ai->pci)
- list_add_tail(&ai->dev_list, &airo_devices);
-}
-
-static void del_airo_dev(struct airo_info *ai)
-{
- if (!ai->pci)
- list_del(&ai->dev_list);
-}
-
-static int airo_close(struct net_device *dev) {
- struct airo_info *ai = dev->ml_priv;
-
- netif_stop_queue(dev);
-
- if (ai->wifidev != dev) {
-#ifdef POWER_ON_DOWN
- /* Shut power to the card. The idea is that the user can save
- * power when he doesn't need the card with "ifconfig down".
- * That's the method that is most friendly towards the network
- * stack (i.e. the network stack won't try to broadcast
- * anything on the interface and routes are gone. Jean II */
- set_bit(FLAG_RADIO_DOWN, &ai->flags);
- disable_MAC(ai, 1);
-#endif
- disable_interrupts( ai );
-
- free_irq(dev->irq, dev);
-
- set_bit(JOB_DIE, &ai->jobs);
- kthread_stop(ai->airo_thread_task);
- }
- return 0;
-}
-
-void stop_airo_card( struct net_device *dev, int freeres )
-{
- struct airo_info *ai = dev->ml_priv;
-
- set_bit(FLAG_RADIO_DOWN, &ai->flags);
- disable_MAC(ai, 1);
- disable_interrupts(ai);
- takedown_proc_entry( dev, ai );
- if (test_bit(FLAG_REGISTERED, &ai->flags)) {
- unregister_netdev( dev );
- if (ai->wifidev) {
- unregister_netdev(ai->wifidev);
- free_netdev(ai->wifidev);
- ai->wifidev = NULL;
- }
- clear_bit(FLAG_REGISTERED, &ai->flags);
- }
- /*
- * Clean out tx queue
- */
- if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
- struct sk_buff *skb = NULL;
- for (;(skb = skb_dequeue(&ai->txq));)
- dev_kfree_skb(skb);
- }
-
- airo_networks_free (ai);
-
- kfree(ai->flash);
- kfree(ai->rssi);
- kfree(ai->SSID);
- if (freeres) {
- /* PCMCIA frees this stuff, so only for PCI and ISA */
- release_region( dev->base_addr, 64 );
- if (test_bit(FLAG_MPI, &ai->flags)) {
- if (ai->pci)
- mpi_unmap_card(ai->pci);
- if (ai->pcimem)
- iounmap(ai->pcimem);
- if (ai->pciaux)
- iounmap(ai->pciaux);
- pci_free_consistent(ai->pci, PCI_SHARED_LEN,
- ai->shared, ai->shared_dma);
- }
- }
- crypto_free_cipher(ai->tfm);
- del_airo_dev(ai);
- free_netdev( dev );
-}
-
-EXPORT_SYMBOL(stop_airo_card);
-
-static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
-{
- memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
- return ETH_ALEN;
-}
-
-static void mpi_unmap_card(struct pci_dev *pci)
-{
- unsigned long mem_start = pci_resource_start(pci, 1);
- unsigned long mem_len = pci_resource_len(pci, 1);
- unsigned long aux_start = pci_resource_start(pci, 2);
- unsigned long aux_len = AUXMEMSIZE;
-
- release_mem_region(aux_start, aux_len);
- release_mem_region(mem_start, mem_len);
-}
-
-/*************************************************************
- * This routine assumes that descriptors have been setup .
- * Run at insmod time or after reset when the decriptors
- * have been initialized . Returns 0 if all is well nz
- * otherwise . Does not allocate memory but sets up card
- * using previously allocated descriptors.
- */
-static int mpi_init_descriptors (struct airo_info *ai)
-{
- Cmd cmd;
- Resp rsp;
- int i;
- int rc = SUCCESS;
-
- /* Alloc card RX descriptors */
- netif_stop_queue(ai->dev);
-
- memset(&rsp,0,sizeof(rsp));
- memset(&cmd,0,sizeof(cmd));
-
- cmd.cmd = CMD_ALLOCATEAUX;
- cmd.parm0 = FID_RX;
- cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
- cmd.parm2 = MPI_MAX_FIDS;
- rc=issuecommand(ai, &cmd, &rsp);
- if (rc != SUCCESS) {
- airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
- return rc;
- }
-
- for (i=0; i<MPI_MAX_FIDS; i++) {
- memcpy_toio(ai->rxfids[i].card_ram_off,
- &ai->rxfids[i].rx_desc, sizeof(RxFid));
- }
-
- /* Alloc card TX descriptors */
-
- memset(&rsp,0,sizeof(rsp));
- memset(&cmd,0,sizeof(cmd));
-
- cmd.cmd = CMD_ALLOCATEAUX;
- cmd.parm0 = FID_TX;
- cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
- cmd.parm2 = MPI_MAX_FIDS;
-
- for (i=0; i<MPI_MAX_FIDS; i++) {
- ai->txfids[i].tx_desc.valid = 1;
- memcpy_toio(ai->txfids[i].card_ram_off,
- &ai->txfids[i].tx_desc, sizeof(TxFid));
- }
- ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
-
- rc=issuecommand(ai, &cmd, &rsp);
- if (rc != SUCCESS) {
- airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
- return rc;
- }
-
- /* Alloc card Rid descriptor */
- memset(&rsp,0,sizeof(rsp));
- memset(&cmd,0,sizeof(cmd));
-
- cmd.cmd = CMD_ALLOCATEAUX;
- cmd.parm0 = RID_RW;
- cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
- cmd.parm2 = 1; /* Magic number... */
- rc=issuecommand(ai, &cmd, &rsp);
- if (rc != SUCCESS) {
- airo_print_err(ai->dev->name, "Couldn't allocate RID");
- return rc;
- }
-
- memcpy_toio(ai->config_desc.card_ram_off,
- &ai->config_desc.rid_desc, sizeof(Rid));
-
- return rc;
-}
-
-/*
- * We are setting up three things here:
- * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
- * 2) Map PCI memory for issuing commands.
- * 3) Allocate memory (shared) to send and receive ethernet frames.
- */
-static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
-{
- unsigned long mem_start, mem_len, aux_start, aux_len;
- int rc = -1;
- int i;
- dma_addr_t busaddroff;
- unsigned char *vpackoff;
- unsigned char __iomem *pciaddroff;
-
- mem_start = pci_resource_start(pci, 1);
- mem_len = pci_resource_len(pci, 1);
- aux_start = pci_resource_start(pci, 2);
- aux_len = AUXMEMSIZE;
-
- if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
- airo_print_err("", "Couldn't get region %x[%x]",
- (int)mem_start, (int)mem_len);
- goto out;
- }
- if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
- airo_print_err("", "Couldn't get region %x[%x]",
- (int)aux_start, (int)aux_len);
- goto free_region1;
- }
-
- ai->pcimem = ioremap(mem_start, mem_len);
- if (!ai->pcimem) {
- airo_print_err("", "Couldn't map region %x[%x]",
- (int)mem_start, (int)mem_len);
- goto free_region2;
- }
- ai->pciaux = ioremap(aux_start, aux_len);
- if (!ai->pciaux) {
- airo_print_err("", "Couldn't map region %x[%x]",
- (int)aux_start, (int)aux_len);
- goto free_memmap;
- }
-
- /* Reserve PKTSIZE for each fid and 2K for the Rids */
- ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
- if (!ai->shared) {
- airo_print_err("", "Couldn't alloc_consistent %d",
- PCI_SHARED_LEN);
- goto free_auxmap;
- }
-
- /*
- * Setup descriptor RX, TX, CONFIG
- */
- busaddroff = ai->shared_dma;
- pciaddroff = ai->pciaux + AUX_OFFSET;
- vpackoff = ai->shared;
-
- /* RX descriptor setup */
- for(i = 0; i < MPI_MAX_FIDS; i++) {
- ai->rxfids[i].pending = 0;
- ai->rxfids[i].card_ram_off = pciaddroff;
- ai->rxfids[i].virtual_host_addr = vpackoff;
- ai->rxfids[i].rx_desc.host_addr = busaddroff;
- ai->rxfids[i].rx_desc.valid = 1;
- ai->rxfids[i].rx_desc.len = PKTSIZE;
- ai->rxfids[i].rx_desc.rdy = 0;
-
- pciaddroff += sizeof(RxFid);
- busaddroff += PKTSIZE;
- vpackoff += PKTSIZE;
- }
-
- /* TX descriptor setup */
- for(i = 0; i < MPI_MAX_FIDS; i++) {
- ai->txfids[i].card_ram_off = pciaddroff;
- ai->txfids[i].virtual_host_addr = vpackoff;
- ai->txfids[i].tx_desc.valid = 1;
- ai->txfids[i].tx_desc.host_addr = busaddroff;
- memcpy(ai->txfids[i].virtual_host_addr,
- &wifictlhdr8023, sizeof(wifictlhdr8023));
-
- pciaddroff += sizeof(TxFid);
- busaddroff += PKTSIZE;
- vpackoff += PKTSIZE;
- }
- ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
-
- /* Rid descriptor setup */
- ai->config_desc.card_ram_off = pciaddroff;
- ai->config_desc.virtual_host_addr = vpackoff;
- ai->config_desc.rid_desc.host_addr = busaddroff;
- ai->ridbus = busaddroff;
- ai->config_desc.rid_desc.rid = 0;
- ai->config_desc.rid_desc.len = RIDSIZE;
- ai->config_desc.rid_desc.valid = 1;
- pciaddroff += sizeof(Rid);
- busaddroff += RIDSIZE;
- vpackoff += RIDSIZE;
-
- /* Tell card about descriptors */
- if (mpi_init_descriptors (ai) != SUCCESS)
- goto free_shared;
-
- return 0;
- free_shared:
- pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
- free_auxmap:
- iounmap(ai->pciaux);
- free_memmap:
- iounmap(ai->pcimem);
- free_region2:
- release_mem_region(aux_start, aux_len);
- free_region1:
- release_mem_region(mem_start, mem_len);
- out:
- return rc;
-}
-
-static const struct header_ops airo_header_ops = {
- .parse = wll_header_parse,
-};
-
-static const struct net_device_ops airo11_netdev_ops = {
- .ndo_open = airo_open,
- .ndo_stop = airo_close,
- .ndo_start_xmit = airo_start_xmit11,
- .ndo_get_stats = airo_get_stats,
- .ndo_set_mac_address = airo_set_mac_address,
- .ndo_do_ioctl = airo_ioctl,
- .ndo_change_mtu = airo_change_mtu,
-};
-
-static void wifi_setup(struct net_device *dev)
-{
- dev->netdev_ops = &airo11_netdev_ops;
- dev->header_ops = &airo_header_ops;
- dev->wireless_handlers = &airo_handler_def;
-
- dev->type = ARPHRD_IEEE80211;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = AIRO_DEF_MTU;
- dev->addr_len = ETH_ALEN;
- dev->tx_queue_len = 100;
-
- eth_broadcast_addr(dev->broadcast);
-
- dev->flags = IFF_BROADCAST|IFF_MULTICAST;
-}
-
-static struct net_device *init_wifidev(struct airo_info *ai,
- struct net_device *ethdev)
-{
- int err;
- struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN,
- wifi_setup);
- if (!dev)
- return NULL;
- dev->ml_priv = ethdev->ml_priv;
- dev->irq = ethdev->irq;
- dev->base_addr = ethdev->base_addr;
- dev->wireless_data = ethdev->wireless_data;
- SET_NETDEV_DEV(dev, ethdev->dev.parent);
- eth_hw_addr_inherit(dev, ethdev);
- err = register_netdev(dev);
- if (err<0) {
- free_netdev(dev);
- return NULL;
- }
- return dev;
-}
-
-static int reset_card( struct net_device *dev , int lock) {
- struct airo_info *ai = dev->ml_priv;
-
- if (lock && down_interruptible(&ai->sem))
- return -1;
- waitbusy (ai);
- OUT4500(ai,COMMAND,CMD_SOFTRESET);
- msleep(200);
- waitbusy (ai);
- msleep(200);
- if (lock)
- up(&ai->sem);
- return 0;
-}
-
-#define AIRO_MAX_NETWORK_COUNT 64
-static int airo_networks_allocate(struct airo_info *ai)
-{
- if (ai->networks)
- return 0;
-
- ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement),
- GFP_KERNEL);
- if (!ai->networks) {
- airo_print_warn("", "Out of memory allocating beacons");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void airo_networks_free(struct airo_info *ai)
-{
- kfree(ai->networks);
- ai->networks = NULL;
-}
-
-static void airo_networks_initialize(struct airo_info *ai)
-{
- int i;
-
- INIT_LIST_HEAD(&ai->network_free_list);
- INIT_LIST_HEAD(&ai->network_list);
- for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
- list_add_tail(&ai->networks[i].list,
- &ai->network_free_list);
-}
-
-static const struct net_device_ops airo_netdev_ops = {
- .ndo_open = airo_open,
- .ndo_stop = airo_close,
- .ndo_start_xmit = airo_start_xmit,
- .ndo_get_stats = airo_get_stats,
- .ndo_set_rx_mode = airo_set_multicast_list,
- .ndo_set_mac_address = airo_set_mac_address,
- .ndo_do_ioctl = airo_ioctl,
- .ndo_change_mtu = airo_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static const struct net_device_ops mpi_netdev_ops = {
- .ndo_open = airo_open,
- .ndo_stop = airo_close,
- .ndo_start_xmit = mpi_start_xmit,
- .ndo_get_stats = airo_get_stats,
- .ndo_set_rx_mode = airo_set_multicast_list,
- .ndo_set_mac_address = airo_set_mac_address,
- .ndo_do_ioctl = airo_ioctl,
- .ndo_change_mtu = airo_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-
-static struct net_device *_init_airo_card( unsigned short irq, int port,
- int is_pcmcia, struct pci_dev *pci,
- struct device *dmdev )
-{
- struct net_device *dev;
- struct airo_info *ai;
- int i, rc;
- CapabilityRid cap_rid;
-
- /* Create the network device object. */
- dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup);
- if (!dev) {
- airo_print_err("", "Couldn't alloc_etherdev");
- return NULL;
- }
-
- ai = dev->ml_priv = netdev_priv(dev);
- ai->wifidev = NULL;
- ai->flags = 1 << FLAG_RADIO_DOWN;
- ai->jobs = 0;
- ai->dev = dev;
- if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
- airo_print_dbg("", "Found an MPI350 card");
- set_bit(FLAG_MPI, &ai->flags);
- }
- spin_lock_init(&ai->aux_lock);
- sema_init(&ai->sem, 1);
- ai->config.len = 0;
- ai->pci = pci;
- init_waitqueue_head (&ai->thr_wait);
- ai->tfm = NULL;
- add_airo_dev(ai);
- ai->APList.len = cpu_to_le16(sizeof(struct APListRid));
-
- if (airo_networks_allocate (ai))
- goto err_out_free;
- airo_networks_initialize (ai);
-
- skb_queue_head_init (&ai->txq);
-
- /* The Airo-specific entries in the device structure. */
- if (test_bit(FLAG_MPI,&ai->flags))
- dev->netdev_ops = &mpi_netdev_ops;
- else
- dev->netdev_ops = &airo_netdev_ops;
- dev->wireless_handlers = &airo_handler_def;
- ai->wireless_data.spy_data = &ai->spy_data;
- dev->wireless_data = &ai->wireless_data;
- dev->irq = irq;
- dev->base_addr = port;
- dev->priv_flags &= ~IFF_TX_SKB_SHARING;
-
- SET_NETDEV_DEV(dev, dmdev);
-
- reset_card (dev, 1);
- msleep(400);
-
- if (!is_pcmcia) {
- if (!request_region(dev->base_addr, 64, DRV_NAME)) {
- rc = -EBUSY;
- airo_print_err(dev->name, "Couldn't request region");
- goto err_out_nets;
- }
- }
-
- if (test_bit(FLAG_MPI,&ai->flags)) {
- if (mpi_map_card(ai, pci)) {
- airo_print_err("", "Could not map memory");
- goto err_out_res;
- }
- }
-
- if (probe) {
- if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
- airo_print_err(dev->name, "MAC could not be enabled" );
- rc = -EIO;
- goto err_out_map;
- }
- } else if (!test_bit(FLAG_MPI,&ai->flags)) {
- ai->bap_read = fast_bap_read;
- set_bit(FLAG_FLASHING, &ai->flags);
- }
-
- strcpy(dev->name, "eth%d");
- rc = register_netdev(dev);
- if (rc) {
- airo_print_err(dev->name, "Couldn't register_netdev");
- goto err_out_map;
- }
- ai->wifidev = init_wifidev(ai, dev);
- if (!ai->wifidev)
- goto err_out_reg;
-
- rc = readCapabilityRid(ai, &cap_rid, 1);
- if (rc != SUCCESS) {
- rc = -EIO;
- goto err_out_wifi;
- }
- /* WEP capability discovery */
- ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0;
- ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0;
-
- airo_print_info(dev->name, "Firmware version %x.%x.%02d",
- ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
- (le16_to_cpu(cap_rid.softVer) & 0xFF),
- le16_to_cpu(cap_rid.softSubVer));
-
- /* Test for WPA support */
- /* Only firmware versions 5.30.17 or better can do WPA */
- if (le16_to_cpu(cap_rid.softVer) > 0x530
- || (le16_to_cpu(cap_rid.softVer) == 0x530
- && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
- airo_print_info(ai->dev->name, "WPA supported.");
-
- set_bit(FLAG_WPA_CAPABLE, &ai->flags);
- ai->bssListFirst = RID_WPA_BSSLISTFIRST;
- ai->bssListNext = RID_WPA_BSSLISTNEXT;
- ai->bssListRidLen = sizeof(BSSListRid);
- } else {
- airo_print_info(ai->dev->name, "WPA unsupported with firmware "
- "versions older than 5.30.17.");
-
- ai->bssListFirst = RID_BSSLISTFIRST;
- ai->bssListNext = RID_BSSLISTNEXT;
- ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
- }
-
- set_bit(FLAG_REGISTERED,&ai->flags);
- airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
-
- /* Allocate the transmit buffers */
- if (probe && !test_bit(FLAG_MPI,&ai->flags))
- for( i = 0; i < MAX_FIDS; i++ )
- ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
-
- if (setup_proc_entry(dev, dev->ml_priv) < 0)
- goto err_out_wifi;
-
- return dev;
-
-err_out_wifi:
- unregister_netdev(ai->wifidev);
- free_netdev(ai->wifidev);
-err_out_reg:
- unregister_netdev(dev);
-err_out_map:
- if (test_bit(FLAG_MPI,&ai->flags) && pci) {
- pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
- iounmap(ai->pciaux);
- iounmap(ai->pcimem);
- mpi_unmap_card(ai->pci);
- }
-err_out_res:
- if (!is_pcmcia)
- release_region( dev->base_addr, 64 );
-err_out_nets:
- airo_networks_free(ai);
-err_out_free:
- del_airo_dev(ai);
- free_netdev(dev);
- return NULL;
-}
-
-struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia,
- struct device *dmdev)
-{
- return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev);
-}
-
-EXPORT_SYMBOL(init_airo_card);
-
-static int waitbusy (struct airo_info *ai) {
- int delay = 0;
- while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
- udelay (10);
- if ((++delay % 20) == 0)
- OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
- }
- return delay < 10000;
-}
-
-int reset_airo_card( struct net_device *dev )
-{
- int i;
- struct airo_info *ai = dev->ml_priv;
-
- if (reset_card (dev, 1))
- return -1;
-
- if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
- airo_print_err(dev->name, "MAC could not be enabled");
- return -1;
- }
- airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
- /* Allocate the transmit buffers if needed */
- if (!test_bit(FLAG_MPI,&ai->flags))
- for( i = 0; i < MAX_FIDS; i++ )
- ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
-
- enable_interrupts( ai );
- netif_wake_queue(dev);
- return 0;
-}
-
-EXPORT_SYMBOL(reset_airo_card);
-
-static void airo_send_event(struct net_device *dev) {
- struct airo_info *ai = dev->ml_priv;
- union iwreq_data wrqu;
- StatusRid status_rid;
-
- clear_bit(JOB_EVENT, &ai->jobs);
- PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
- up(&ai->sem);
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-
- /* Send event to user space */
- wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
-}
-
-static void airo_process_scan_results (struct airo_info *ai) {
- union iwreq_data wrqu;
- BSSListRid bss;
- int rc;
- BSSListElement * loop_net;
- BSSListElement * tmp_net;
-
- /* Blow away current list of scan results */
- list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
- list_move_tail (&loop_net->list, &ai->network_free_list);
- /* Don't blow away ->list, just BSS data */
- memset (loop_net, 0, sizeof (loop_net->bss));
- }
-
- /* Try to read the first entry of the scan result */
- rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
- if((rc) || (bss.index == cpu_to_le16(0xffff))) {
- /* No scan results */
- goto out;
- }
-
- /* Read and parse all entries */
- tmp_net = NULL;
- while((!rc) && (bss.index != cpu_to_le16(0xffff))) {
- /* Grab a network off the free list */
- if (!list_empty(&ai->network_free_list)) {
- tmp_net = list_entry(ai->network_free_list.next,
- BSSListElement, list);
- list_del(ai->network_free_list.next);
- }
-
- if (tmp_net != NULL) {
- memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
- list_add_tail(&tmp_net->list, &ai->network_list);
- tmp_net = NULL;
- }
-
- /* Read next entry */
- rc = PC4500_readrid(ai, ai->bssListNext,
- &bss, ai->bssListRidLen, 0);
- }
-
-out:
- /* write APList back (we cleared it in airo_set_scan) */
- disable_MAC(ai, 2);
- writeAPListRid(ai, &ai->APList, 0);
- enable_MAC(ai, 0);
-
- ai->scan_timeout = 0;
- clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
- up(&ai->sem);
-
- /* Send an empty event to user space.
- * We don't send the received data on
- * the event because it would require
- * us to do complex transcoding, and
- * we want to minimise the work done in
- * the irq handler. Use a request to
- * extract the data - Jean II */
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
-}
-
-static int airo_thread(void *data) {
- struct net_device *dev = data;
- struct airo_info *ai = dev->ml_priv;
- int locked;
-
- set_freezable();
- while(1) {
- /* make swsusp happy with our thread */
- try_to_freeze();
-
- if (test_bit(JOB_DIE, &ai->jobs))
- break;
-
- if (ai->jobs) {
- locked = down_interruptible(&ai->sem);
- } else {
- wait_queue_t wait;
-
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&ai->thr_wait, &wait);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (ai->jobs)
- break;
- if (ai->expires || ai->scan_timeout) {
- if (ai->scan_timeout &&
- time_after_eq(jiffies,ai->scan_timeout)){
- set_bit(JOB_SCAN_RESULTS, &ai->jobs);
- break;
- } else if (ai->expires &&
- time_after_eq(jiffies,ai->expires)){
- set_bit(JOB_AUTOWEP, &ai->jobs);
- break;
- }
- if (!kthread_should_stop() &&
- !freezing(current)) {
- unsigned long wake_at;
- if (!ai->expires || !ai->scan_timeout) {
- wake_at = max(ai->expires,
- ai->scan_timeout);
- } else {
- wake_at = min(ai->expires,
- ai->scan_timeout);
- }
- schedule_timeout(wake_at - jiffies);
- continue;
- }
- } else if (!kthread_should_stop() &&
- !freezing(current)) {
- schedule();
- continue;
- }
- break;
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&ai->thr_wait, &wait);
- locked = 1;
- }
-
- if (locked)
- continue;
-
- if (test_bit(JOB_DIE, &ai->jobs)) {
- up(&ai->sem);
- break;
- }
-
- if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
- up(&ai->sem);
- continue;
- }
-
- if (test_bit(JOB_XMIT, &ai->jobs))
- airo_end_xmit(dev);
- else if (test_bit(JOB_XMIT11, &ai->jobs))
- airo_end_xmit11(dev);
- else if (test_bit(JOB_STATS, &ai->jobs))
- airo_read_stats(dev);
- else if (test_bit(JOB_WSTATS, &ai->jobs))
- airo_read_wireless_stats(ai);
- else if (test_bit(JOB_PROMISC, &ai->jobs))
- airo_set_promisc(ai);
- else if (test_bit(JOB_MIC, &ai->jobs))
- micinit(ai);
- else if (test_bit(JOB_EVENT, &ai->jobs))
- airo_send_event(dev);
- else if (test_bit(JOB_AUTOWEP, &ai->jobs))
- timer_func(dev);
- else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
- airo_process_scan_results(ai);
- else /* Shouldn't get here, but we make sure to unlock */
- up(&ai->sem);
- }
-
- return 0;
-}
-
-static int header_len(__le16 ctl)
-{
- u16 fc = le16_to_cpu(ctl);
- switch (fc & 0xc) {
- case 4:
- if ((fc & 0xe0) == 0xc0)
- return 10; /* one-address control packet */
- return 16; /* two-address control packet */
- case 8:
- if ((fc & 0x300) == 0x300)
- return 30; /* WDS packet */
- }
- return 24;
-}
-
-static void airo_handle_cisco_mic(struct airo_info *ai)
-{
- if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
- set_bit(JOB_MIC, &ai->jobs);
- wake_up_interruptible(&ai->thr_wait);
- }
-}
-
-/* Airo Status codes */
-#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */
-#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */
-#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/
-#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */
-#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */
-#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */
-#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */
-#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */
-#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */
-#define STAT_ASSOC 0x0400 /* Associated */
-#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */
-
-static void airo_print_status(const char *devname, u16 status)
-{
- u8 reason = status & 0xFF;
-
- switch (status & 0xFF00) {
- case STAT_NOBEACON:
- switch (status) {
- case STAT_NOBEACON:
- airo_print_dbg(devname, "link lost (missed beacons)");
- break;
- case STAT_MAXRETRIES:
- case STAT_MAXARL:
- airo_print_dbg(devname, "link lost (max retries)");
- break;
- case STAT_FORCELOSS:
- airo_print_dbg(devname, "link lost (local choice)");
- break;
- case STAT_TSFSYNC:
- airo_print_dbg(devname, "link lost (TSF sync lost)");
- break;
- default:
- airo_print_dbg(devname, "unknown status %x\n", status);
- break;
- }
- break;
- case STAT_DEAUTH:
- airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
- break;
- case STAT_DISASSOC:
- airo_print_dbg(devname, "disassociated (reason: %d)", reason);
- break;
- case STAT_ASSOC_FAIL:
- airo_print_dbg(devname, "association failed (reason: %d)",
- reason);
- break;
- case STAT_AUTH_FAIL:
- airo_print_dbg(devname, "authentication failed (reason: %d)",
- reason);
- break;
- case STAT_ASSOC:
- case STAT_REASSOC:
- break;
- default:
- airo_print_dbg(devname, "unknown status %x\n", status);
- break;
- }
-}
-
-static void airo_handle_link(struct airo_info *ai)
-{
- union iwreq_data wrqu;
- int scan_forceloss = 0;
- u16 status;
-
- /* Get new status and acknowledge the link change */
- status = le16_to_cpu(IN4500(ai, LINKSTAT));
- OUT4500(ai, EVACK, EV_LINK);
-
- if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
- scan_forceloss = 1;
-
- airo_print_status(ai->dev->name, status);
-
- if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
- if (auto_wep)
- ai->expires = 0;
- if (ai->list_bss_task)
- wake_up_process(ai->list_bss_task);
- set_bit(FLAG_UPDATE_UNI, &ai->flags);
- set_bit(FLAG_UPDATE_MULTI, &ai->flags);
-
- if (down_trylock(&ai->sem) != 0) {
- set_bit(JOB_EVENT, &ai->jobs);
- wake_up_interruptible(&ai->thr_wait);
- } else
- airo_send_event(ai->dev);
- netif_carrier_on(ai->dev);
- } else if (!scan_forceloss) {
- if (auto_wep && !ai->expires) {
- ai->expires = RUN_AT(3*HZ);
- wake_up_interruptible(&ai->thr_wait);
- }
-
- /* Send event to user space */
- eth_zero_addr(wrqu.ap_addr.sa_data);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
- netif_carrier_off(ai->dev);
- } else {
- netif_carrier_off(ai->dev);
- }
-}
-
-static void airo_handle_rx(struct airo_info *ai)
-{
- struct sk_buff *skb = NULL;
- __le16 fc, v, *buffer, tmpbuf[4];
- u16 len, hdrlen = 0, gap, fid;
- struct rx_hdr hdr;
- int success = 0;
-
- if (test_bit(FLAG_MPI, &ai->flags)) {
- if (test_bit(FLAG_802_11, &ai->flags))
- mpi_receive_802_11(ai);
- else
- mpi_receive_802_3(ai);
- OUT4500(ai, EVACK, EV_RX);
- return;
- }
-
- fid = IN4500(ai, RXFID);
-
- /* Get the packet length */
- if (test_bit(FLAG_802_11, &ai->flags)) {
- bap_setup (ai, fid, 4, BAP0);
- bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
- /* Bad CRC. Ignore packet */
- if (le16_to_cpu(hdr.status) & 2)
- hdr.len = 0;
- if (ai->wifidev == NULL)
- hdr.len = 0;
- } else {
- bap_setup(ai, fid, 0x36, BAP0);
- bap_read(ai, &hdr.len, 2, BAP0);
- }
- len = le16_to_cpu(hdr.len);
-
- if (len > AIRO_DEF_MTU) {
- airo_print_err(ai->dev->name, "Bad size %d", len);
- goto done;
- }
- if (len == 0)
- goto done;
-
- if (test_bit(FLAG_802_11, &ai->flags)) {
- bap_read(ai, &fc, sizeof (fc), BAP0);
- hdrlen = header_len(fc);
- } else
- hdrlen = ETH_ALEN * 2;
-
- skb = dev_alloc_skb(len + hdrlen + 2 + 2);
- if (!skb) {
- ai->dev->stats.rx_dropped++;
- goto done;
- }
-
- skb_reserve(skb, 2); /* This way the IP header is aligned */
- buffer = (__le16 *) skb_put(skb, len + hdrlen);
- if (test_bit(FLAG_802_11, &ai->flags)) {
- buffer[0] = fc;
- bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
- if (hdrlen == 24)
- bap_read(ai, tmpbuf, 6, BAP0);
-
- bap_read(ai, &v, sizeof(v), BAP0);
- gap = le16_to_cpu(v);
- if (gap) {
- if (gap <= 8) {
- bap_read(ai, tmpbuf, gap, BAP0);
- } else {
- airo_print_err(ai->dev->name, "gaplen too "
- "big. Problems will follow...");
- }
- }
- bap_read(ai, buffer + hdrlen/2, len, BAP0);
- } else {
- MICBuffer micbuf;
-
- bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
- if (ai->micstats.enabled) {
- bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
- if (ntohs(micbuf.typelen) > 0x05DC)
- bap_setup(ai, fid, 0x44, BAP0);
- else {
- if (len <= sizeof (micbuf)) {
- dev_kfree_skb_irq(skb);
- goto done;
- }
-
- len -= sizeof(micbuf);
- skb_trim(skb, len + hdrlen);
- }
- }
-
- bap_read(ai, buffer + ETH_ALEN, len, BAP0);
- if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
- dev_kfree_skb_irq (skb);
- else
- success = 1;
- }
-
-#ifdef WIRELESS_SPY
- if (success && (ai->spy_data.spy_number > 0)) {
- char *sa;
- struct iw_quality wstats;
-
- /* Prepare spy data : addr + qual */
- if (!test_bit(FLAG_802_11, &ai->flags)) {
- sa = (char *) buffer + 6;
- bap_setup(ai, fid, 8, BAP0);
- bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
- } else
- sa = (char *) buffer + 10;
- wstats.qual = hdr.rssi[0];
- if (ai->rssi)
- wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
- else
- wstats.level = (hdr.rssi[1] + 321) / 2;
- wstats.noise = ai->wstats.qual.noise;
- wstats.updated = IW_QUAL_LEVEL_UPDATED
- | IW_QUAL_QUAL_UPDATED
- | IW_QUAL_DBM;
- /* Update spy records */
- wireless_spy_update(ai->dev, sa, &wstats);
- }
-#endif /* WIRELESS_SPY */
-
-done:
- OUT4500(ai, EVACK, EV_RX);
-
- if (success) {
- if (test_bit(FLAG_802_11, &ai->flags)) {
- skb_reset_mac_header(skb);
- skb->pkt_type = PACKET_OTHERHOST;
- skb->dev = ai->wifidev;
- skb->protocol = htons(ETH_P_802_2);
- } else
- skb->protocol = eth_type_trans(skb, ai->dev);
- skb->ip_summed = CHECKSUM_NONE;
-
- netif_rx(skb);
- }
-}
-
-static void airo_handle_tx(struct airo_info *ai, u16 status)
-{
- int i, len = 0, index = -1;
- u16 fid;
-
- if (test_bit(FLAG_MPI, &ai->flags)) {
- unsigned long flags;
-
- if (status & EV_TXEXC)
- get_tx_error(ai, -1);
-
- spin_lock_irqsave(&ai->aux_lock, flags);
- if (!skb_queue_empty(&ai->txq)) {
- spin_unlock_irqrestore(&ai->aux_lock,flags);
- mpi_send_packet(ai->dev);
- } else {
- clear_bit(FLAG_PENDING_XMIT, &ai->flags);
- spin_unlock_irqrestore(&ai->aux_lock,flags);
- netif_wake_queue(ai->dev);
- }
- OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
- return;
- }
-
- fid = IN4500(ai, TXCOMPLFID);
-
- for(i = 0; i < MAX_FIDS; i++) {
- if ((ai->fids[i] & 0xffff) == fid) {
- len = ai->fids[i] >> 16;
- index = i;
- }
- }
-
- if (index != -1) {
- if (status & EV_TXEXC)
- get_tx_error(ai, index);
-
- OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
-
- /* Set up to be used again */
- ai->fids[index] &= 0xffff;
- if (index < MAX_FIDS / 2) {
- if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
- netif_wake_queue(ai->dev);
- } else {
- if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
- netif_wake_queue(ai->wifidev);
- }
- } else {
- OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
- airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
- }
-}
-
-static irqreturn_t airo_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- u16 status, savedInterrupts = 0;
- struct airo_info *ai = dev->ml_priv;
- int handled = 0;
-
- if (!netif_device_present(dev))
- return IRQ_NONE;
-
- for (;;) {
- status = IN4500(ai, EVSTAT);
- if (!(status & STATUS_INTS) || (status == 0xffff))
- break;
-
- handled = 1;
-
- if (status & EV_AWAKE) {
- OUT4500(ai, EVACK, EV_AWAKE);
- OUT4500(ai, EVACK, EV_AWAKE);
- }
-
- if (!savedInterrupts) {
- savedInterrupts = IN4500(ai, EVINTEN);
- OUT4500(ai, EVINTEN, 0);
- }
-
- if (status & EV_MIC) {
- OUT4500(ai, EVACK, EV_MIC);
- airo_handle_cisco_mic(ai);
- }
-
- if (status & EV_LINK) {
- /* Link status changed */
- airo_handle_link(ai);
- }
-
- /* Check to see if there is something to receive */
- if (status & EV_RX)
- airo_handle_rx(ai);
-
- /* Check to see if a packet has been transmitted */
- if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
- airo_handle_tx(ai, status);
-
- if ( status & ~STATUS_INTS & ~IGNORE_INTS ) {
- airo_print_warn(ai->dev->name, "Got weird status %x",
- status & ~STATUS_INTS & ~IGNORE_INTS );
- }
- }
-
- if (savedInterrupts)
- OUT4500(ai, EVINTEN, savedInterrupts);
-
- return IRQ_RETVAL(handled);
-}
-
-/*
- * Routines to talk to the card
- */
-
-/*
- * This was originally written for the 4500, hence the name
- * NOTE: If use with 8bit mode and SMP bad things will happen!
- * Why would some one do 8 bit IO in an SMP machine?!?
- */
-static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) {
- if (test_bit(FLAG_MPI,&ai->flags))
- reg <<= 1;
- if ( !do8bitIO )
- outw( val, ai->dev->base_addr + reg );
- else {
- outb( val & 0xff, ai->dev->base_addr + reg );
- outb( val >> 8, ai->dev->base_addr + reg + 1 );
- }
-}
-
-static u16 IN4500( struct airo_info *ai, u16 reg ) {
- unsigned short rc;
-
- if (test_bit(FLAG_MPI,&ai->flags))
- reg <<= 1;
- if ( !do8bitIO )
- rc = inw( ai->dev->base_addr + reg );
- else {
- rc = inb( ai->dev->base_addr + reg );
- rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8;
- }
- return rc;
-}
-
-static int enable_MAC(struct airo_info *ai, int lock)
-{
- int rc;
- Cmd cmd;
- Resp rsp;
-
- /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
- * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
- * Note : we could try to use !netif_running(dev) in enable_MAC()
- * instead of this flag, but I don't trust it *within* the
- * open/close functions, and testing both flags together is
- * "cheaper" - Jean II */
- if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
-
- if (lock && down_interruptible(&ai->sem))
- return -ERESTARTSYS;
-
- if (!test_bit(FLAG_ENABLED, &ai->flags)) {
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd = MAC_ENABLE;
- rc = issuecommand(ai, &cmd, &rsp);
- if (rc == SUCCESS)
- set_bit(FLAG_ENABLED, &ai->flags);
- } else
- rc = SUCCESS;
-
- if (lock)
- up(&ai->sem);
-
- if (rc)
- airo_print_err(ai->dev->name, "Cannot enable MAC");
- else if ((rsp.status & 0xFF00) != 0) {
- airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
- "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
- rc = ERROR;
- }
- return rc;
-}
-
-static void disable_MAC( struct airo_info *ai, int lock ) {
- Cmd cmd;
- Resp rsp;
-
- if (lock == 1 && down_interruptible(&ai->sem))
- return;
-
- if (test_bit(FLAG_ENABLED, &ai->flags)) {
- if (lock != 2) /* lock == 2 means don't disable carrier */
- netif_carrier_off(ai->dev);
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd = MAC_DISABLE; // disable in case already enabled
- issuecommand(ai, &cmd, &rsp);
- clear_bit(FLAG_ENABLED, &ai->flags);
- }
- if (lock == 1)
- up(&ai->sem);
-}
-
-static void enable_interrupts( struct airo_info *ai ) {
- /* Enable the interrupts */
- OUT4500( ai, EVINTEN, STATUS_INTS );
-}
-
-static void disable_interrupts( struct airo_info *ai ) {
- OUT4500( ai, EVINTEN, 0 );
-}
-
-static void mpi_receive_802_3(struct airo_info *ai)
-{
- RxFid rxd;
- int len = 0;
- struct sk_buff *skb;
- char *buffer;
- int off = 0;
- MICBuffer micbuf;
-
- memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
- /* Make sure we got something */
- if (rxd.rdy && rxd.valid == 0) {
- len = rxd.len + 12;
- if (len < 12 || len > 2048)
- goto badrx;
-
- skb = dev_alloc_skb(len);
- if (!skb) {
- ai->dev->stats.rx_dropped++;
- goto badrx;
- }
- buffer = skb_put(skb,len);
- memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
- if (ai->micstats.enabled) {
- memcpy(&micbuf,
- ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
- sizeof(micbuf));
- if (ntohs(micbuf.typelen) <= 0x05DC) {
- if (len <= sizeof(micbuf) + ETH_ALEN * 2)
- goto badmic;
-
- off = sizeof(micbuf);
- skb_trim (skb, len - off);
- }
- }
- memcpy(buffer + ETH_ALEN * 2,
- ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
- len - ETH_ALEN * 2 - off);
- if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
-badmic:
- dev_kfree_skb_irq (skb);
- goto badrx;
- }
-#ifdef WIRELESS_SPY
- if (ai->spy_data.spy_number > 0) {
- char *sa;
- struct iw_quality wstats;
- /* Prepare spy data : addr + qual */
- sa = buffer + ETH_ALEN;
- wstats.qual = 0; /* XXX Where do I get that info from ??? */
- wstats.level = 0;
- wstats.updated = 0;
- /* Update spy records */
- wireless_spy_update(ai->dev, sa, &wstats);
- }
-#endif /* WIRELESS_SPY */
-
- skb->ip_summed = CHECKSUM_NONE;
- skb->protocol = eth_type_trans(skb, ai->dev);
- netif_rx(skb);
- }
-badrx:
- if (rxd.valid == 0) {
- rxd.valid = 1;
- rxd.rdy = 0;
- rxd.len = PKTSIZE;
- memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
- }
-}
-
-static void mpi_receive_802_11(struct airo_info *ai)
-{
- RxFid rxd;
- struct sk_buff *skb = NULL;
- u16 len, hdrlen = 0;
- __le16 fc;
- struct rx_hdr hdr;
- u16 gap;
- u16 *buffer;
- char *ptr = ai->rxfids[0].virtual_host_addr + 4;
-
- memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
- memcpy ((char *)&hdr, ptr, sizeof(hdr));
- ptr += sizeof(hdr);
- /* Bad CRC. Ignore packet */
- if (le16_to_cpu(hdr.status) & 2)
- hdr.len = 0;
- if (ai->wifidev == NULL)
- hdr.len = 0;
- len = le16_to_cpu(hdr.len);
- if (len > AIRO_DEF_MTU) {
- airo_print_err(ai->dev->name, "Bad size %d", len);
- goto badrx;
- }
- if (len == 0)
- goto badrx;
-
- fc = get_unaligned((__le16 *)ptr);
- hdrlen = header_len(fc);
-
- skb = dev_alloc_skb( len + hdrlen + 2 );
- if ( !skb ) {
- ai->dev->stats.rx_dropped++;
- goto badrx;
- }
- buffer = (u16*)skb_put (skb, len + hdrlen);
- memcpy ((char *)buffer, ptr, hdrlen);
- ptr += hdrlen;
- if (hdrlen == 24)
- ptr += 6;
- gap = get_unaligned_le16(ptr);
- ptr += sizeof(__le16);
- if (gap) {
- if (gap <= 8)
- ptr += gap;
- else
- airo_print_err(ai->dev->name,
- "gaplen too big. Problems will follow...");
- }
- memcpy ((char *)buffer + hdrlen, ptr, len);
- ptr += len;
-#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
- if (ai->spy_data.spy_number > 0) {
- char *sa;
- struct iw_quality wstats;
- /* Prepare spy data : addr + qual */
- sa = (char*)buffer + 10;
- wstats.qual = hdr.rssi[0];
- if (ai->rssi)
- wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
- else
- wstats.level = (hdr.rssi[1] + 321) / 2;
- wstats.noise = ai->wstats.qual.noise;
- wstats.updated = IW_QUAL_QUAL_UPDATED
- | IW_QUAL_LEVEL_UPDATED
- | IW_QUAL_DBM;
- /* Update spy records */
- wireless_spy_update(ai->dev, sa, &wstats);
- }
-#endif /* IW_WIRELESS_SPY */
- skb_reset_mac_header(skb);
- skb->pkt_type = PACKET_OTHERHOST;
- skb->dev = ai->wifidev;
- skb->protocol = htons(ETH_P_802_2);
- skb->ip_summed = CHECKSUM_NONE;
- netif_rx( skb );
-
-badrx:
- if (rxd.valid == 0) {
- rxd.valid = 1;
- rxd.rdy = 0;
- rxd.len = PKTSIZE;
- memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
- }
-}
-
-static inline void set_auth_type(struct airo_info *local, int auth_type)
-{
- local->config.authType = auth_type;
- /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT).
- * Used by airo_set_auth()
- */
- if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT)
- local->last_auth = auth_type;
-}
-
-static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
-{
- Cmd cmd;
- Resp rsp;
- int status;
- SsidRid mySsid;
- __le16 lastindex;
- WepKeyRid wkr;
- int rc;
-
- memset( &mySsid, 0, sizeof( mySsid ) );
- kfree (ai->flash);
- ai->flash = NULL;
-
- /* The NOP is the first step in getting the card going */
- cmd.cmd = NOP;
- cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
- if (lock && down_interruptible(&ai->sem))
- return ERROR;
- if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) {
- if (lock)
- up(&ai->sem);
- return ERROR;
- }
- disable_MAC( ai, 0);
-
- // Let's figure out if we need to use the AUX port
- if (!test_bit(FLAG_MPI,&ai->flags)) {
- cmd.cmd = CMD_ENABLEAUX;
- if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
- if (lock)
- up(&ai->sem);
- airo_print_err(ai->dev->name, "Error checking for AUX port");
- return ERROR;
- }
- if (!aux_bap || rsp.status & 0xff00) {
- ai->bap_read = fast_bap_read;
- airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
- } else {
- ai->bap_read = aux_bap_read;
- airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
- }
- }
- if (lock)
- up(&ai->sem);
- if (ai->config.len == 0) {
- int i;
- tdsRssiRid rssi_rid;
- CapabilityRid cap_rid;
-
- kfree(ai->SSID);
- ai->SSID = NULL;
- // general configuration (read/modify/write)
- status = readConfigRid(ai, lock);
- if ( status != SUCCESS ) return ERROR;
-
- status = readCapabilityRid(ai, &cap_rid, lock);
- if ( status != SUCCESS ) return ERROR;
-
- status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
- if ( status == SUCCESS ) {
- if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
- memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
- }
- else {
- kfree(ai->rssi);
- ai->rssi = NULL;
- if (cap_rid.softCap & cpu_to_le16(8))
- ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
- else
- airo_print_warn(ai->dev->name, "unknown received signal "
- "level scale");
- }
- ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
- set_auth_type(ai, AUTH_OPEN);
- ai->config.modulation = MOD_CCK;
-
- if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
- (cap_rid.extSoftCap & cpu_to_le16(1)) &&
- micsetup(ai) == SUCCESS) {
- ai->config.opmode |= MODE_MIC;
- set_bit(FLAG_MIC_CAPABLE, &ai->flags);
- }
-
- /* Save off the MAC */
- for( i = 0; i < ETH_ALEN; i++ ) {
- mac[i] = ai->config.macAddr[i];
- }
-
- /* Check to see if there are any insmod configured
- rates to add */
- if ( rates[0] ) {
- memset(ai->config.rates,0,sizeof(ai->config.rates));
- for( i = 0; i < 8 && rates[i]; i++ ) {
- ai->config.rates[i] = rates[i];
- }
- }
- set_bit (FLAG_COMMIT, &ai->flags);
- }
-
- /* Setup the SSIDs if present */
- if ( ssids[0] ) {
- int i;
- for( i = 0; i < 3 && ssids[i]; i++ ) {
- size_t len = strlen(ssids[i]);
- if (len > 32)
- len = 32;
- mySsid.ssids[i].len = cpu_to_le16(len);
- memcpy(mySsid.ssids[i].ssid, ssids[i], len);
- }
- mySsid.len = cpu_to_le16(sizeof(mySsid));
- }
-
- status = writeConfigRid(ai, lock);
- if ( status != SUCCESS ) return ERROR;
-
- /* Set up the SSID list */
- if ( ssids[0] ) {
- status = writeSsidRid(ai, &mySsid, lock);
- if ( status != SUCCESS ) return ERROR;
- }
-
- status = enable_MAC(ai, lock);
- if (status != SUCCESS)
- return ERROR;
-
- /* Grab the initial wep key, we gotta save it for auto_wep */
- rc = readWepKeyRid(ai, &wkr, 1, lock);
- if (rc == SUCCESS) do {
- lastindex = wkr.kindex;
- if (wkr.kindex == cpu_to_le16(0xffff)) {
- ai->defindex = wkr.mac[0];
- }
- rc = readWepKeyRid(ai, &wkr, 0, lock);
- } while(lastindex != wkr.kindex);
-
- try_auto_wep(ai);
-
- return SUCCESS;
-}
-
-static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
- // Im really paranoid about letting it run forever!
- int max_tries = 600000;
-
- if (IN4500(ai, EVSTAT) & EV_CMD)
- OUT4500(ai, EVACK, EV_CMD);
-
- OUT4500(ai, PARAM0, pCmd->parm0);
- OUT4500(ai, PARAM1, pCmd->parm1);
- OUT4500(ai, PARAM2, pCmd->parm2);
- OUT4500(ai, COMMAND, pCmd->cmd);
-
- while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
- if ((IN4500(ai, COMMAND)) == pCmd->cmd)
- // PC4500 didn't notice command, try again
- OUT4500(ai, COMMAND, pCmd->cmd);
- if (!in_atomic() && (max_tries & 255) == 0)
- schedule();
- }
-
- if ( max_tries == -1 ) {
- airo_print_err(ai->dev->name,
- "Max tries exceeded when issuing command");
- if (IN4500(ai, COMMAND) & COMMAND_BUSY)
- OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
- return ERROR;
- }
-
- // command completed
- pRsp->status = IN4500(ai, STATUS);
- pRsp->rsp0 = IN4500(ai, RESP0);
- pRsp->rsp1 = IN4500(ai, RESP1);
- pRsp->rsp2 = IN4500(ai, RESP2);
- if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
- airo_print_err(ai->dev->name,
- "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
- pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
- pRsp->rsp2);
-
- // clear stuck command busy if necessary
- if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
- OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
- }
- // acknowledge processing the status/response
- OUT4500(ai, EVACK, EV_CMD);
-
- return SUCCESS;
-}
-
-/* Sets up the bap to start exchange data. whichbap should
- * be one of the BAP0 or BAP1 defines. Locks should be held before
- * calling! */
-static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
-{
- int timeout = 50;
- int max_tries = 3;
-
- OUT4500(ai, SELECT0+whichbap, rid);
- OUT4500(ai, OFFSET0+whichbap, offset);
- while (1) {
- int status = IN4500(ai, OFFSET0+whichbap);
- if (status & BAP_BUSY) {
- /* This isn't really a timeout, but its kinda
- close */
- if (timeout--) {
- continue;
- }
- } else if ( status & BAP_ERR ) {
- /* invalid rid or offset */
- airo_print_err(ai->dev->name, "BAP error %x %d",
- status, whichbap );
- return ERROR;
- } else if (status & BAP_DONE) { // success
- return SUCCESS;
- }
- if ( !(max_tries--) ) {
- airo_print_err(ai->dev->name,
- "BAP setup error too many retries\n");
- return ERROR;
- }
- // -- PC4500 missed it, try again
- OUT4500(ai, SELECT0+whichbap, rid);
- OUT4500(ai, OFFSET0+whichbap, offset);
- timeout = 50;
- }
-}
-
-/* should only be called by aux_bap_read. This aux function and the
- following use concepts not documented in the developers guide. I
- got them from a patch given to my by Aironet */
-static u16 aux_setup(struct airo_info *ai, u16 page,
- u16 offset, u16 *len)
-{
- u16 next;
-
- OUT4500(ai, AUXPAGE, page);
- OUT4500(ai, AUXOFF, 0);
- next = IN4500(ai, AUXDATA);
- *len = IN4500(ai, AUXDATA)&0xff;
- if (offset != 4) OUT4500(ai, AUXOFF, offset);
- return next;
-}
-
-/* requires call to bap_setup() first */
-static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
- int bytelen, int whichbap)
-{
- u16 len;
- u16 page;
- u16 offset;
- u16 next;
- int words;
- int i;
- unsigned long flags;
-
- spin_lock_irqsave(&ai->aux_lock, flags);
- page = IN4500(ai, SWS0+whichbap);
- offset = IN4500(ai, SWS2+whichbap);
- next = aux_setup(ai, page, offset, &len);
- words = (bytelen+1)>>1;
-
- for (i=0; i<words;) {
- int count;
- count = (len>>1) < (words-i) ? (len>>1) : (words-i);
- if ( !do8bitIO )
- insw( ai->dev->base_addr+DATA0+whichbap,
- pu16Dst+i,count );
- else
- insb( ai->dev->base_addr+DATA0+whichbap,
- pu16Dst+i, count << 1 );
- i += count;
- if (i<words) {
- next = aux_setup(ai, next, 4, &len);
- }
- }
- spin_unlock_irqrestore(&ai->aux_lock, flags);
- return SUCCESS;
-}
-
-
-/* requires call to bap_setup() first */
-static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
- int bytelen, int whichbap)
-{
- bytelen = (bytelen + 1) & (~1); // round up to even value
- if ( !do8bitIO )
- insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 );
- else
- insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen );
- return SUCCESS;
-}
-
-/* requires call to bap_setup() first */
-static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
- int bytelen, int whichbap)
-{
- bytelen = (bytelen + 1) & (~1); // round up to even value
- if ( !do8bitIO )
- outsw( ai->dev->base_addr+DATA0+whichbap,
- pu16Src, bytelen>>1 );
- else
- outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen );
- return SUCCESS;
-}
-
-static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
-{
- Cmd cmd; /* for issuing commands */
- Resp rsp; /* response from commands */
- u16 status;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd = accmd;
- cmd.parm0 = rid;
- status = issuecommand(ai, &cmd, &rsp);
- if (status != 0) return status;
- if ( (rsp.status & 0x7F00) != 0) {
- return (accmd << 8) + (rsp.rsp0 & 0xFF);
- }
- return 0;
-}
-
-/* Note, that we are using BAP1 which is also used by transmit, so
- * we must get a lock. */
-static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
-{
- u16 status;
- int rc = SUCCESS;
-
- if (lock) {
- if (down_interruptible(&ai->sem))
- return ERROR;
- }
- if (test_bit(FLAG_MPI,&ai->flags)) {
- Cmd cmd;
- Resp rsp;
-
- memset(&cmd, 0, sizeof(cmd));
- memset(&rsp, 0, sizeof(rsp));
- ai->config_desc.rid_desc.valid = 1;
- ai->config_desc.rid_desc.len = RIDSIZE;
- ai->config_desc.rid_desc.rid = 0;
- ai->config_desc.rid_desc.host_addr = ai->ridbus;
-
- cmd.cmd = CMD_ACCESS;
- cmd.parm0 = rid;
-
- memcpy_toio(ai->config_desc.card_ram_off,
- &ai->config_desc.rid_desc, sizeof(Rid));
-
- rc = issuecommand(ai, &cmd, &rsp);
-
- if (rsp.status & 0x7f00)
- rc = rsp.rsp0;
- if (!rc)
- memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
- goto done;
- } else {
- if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
- rc = status;
- goto done;
- }
- if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
- rc = ERROR;
- goto done;
- }
- // read the rid length field
- bap_read(ai, pBuf, 2, BAP1);
- // length for remaining part of rid
- len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
-
- if ( len <= 2 ) {
- airo_print_err(ai->dev->name,
- "Rid %x has a length of %d which is too short",
- (int)rid, (int)len );
- rc = ERROR;
- goto done;
- }
- // read remainder of the rid
- rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
- }
-done:
- if (lock)
- up(&ai->sem);
- return rc;
-}
-
-/* Note, that we are using BAP1 which is also used by transmit, so
- * make sure this isn't called when a transmit is happening */
-static int PC4500_writerid(struct airo_info *ai, u16 rid,
- const void *pBuf, int len, int lock)
-{
- u16 status;
- int rc = SUCCESS;
-
- *(__le16*)pBuf = cpu_to_le16((u16)len);
-
- if (lock) {
- if (down_interruptible(&ai->sem))
- return ERROR;
- }
- if (test_bit(FLAG_MPI,&ai->flags)) {
- Cmd cmd;
- Resp rsp;
-
- if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
- airo_print_err(ai->dev->name,
- "%s: MAC should be disabled (rid=%04x)",
- __func__, rid);
- memset(&cmd, 0, sizeof(cmd));
- memset(&rsp, 0, sizeof(rsp));
-
- ai->config_desc.rid_desc.valid = 1;
- ai->config_desc.rid_desc.len = *((u16 *)pBuf);
- ai->config_desc.rid_desc.rid = 0;
-
- cmd.cmd = CMD_WRITERID;
- cmd.parm0 = rid;
-
- memcpy_toio(ai->config_desc.card_ram_off,
- &ai->config_desc.rid_desc, sizeof(Rid));
-
- if (len < 4 || len > 2047) {
- airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
- rc = -1;
- } else {
- memcpy(ai->config_desc.virtual_host_addr,
- pBuf, len);
-
- rc = issuecommand(ai, &cmd, &rsp);
- if ((rc & 0xff00) != 0) {
- airo_print_err(ai->dev->name, "%s: Write rid Error %d",
- __func__, rc);
- airo_print_err(ai->dev->name, "%s: Cmd=%04x",
- __func__, cmd.cmd);
- }
-
- if ((rsp.status & 0x7f00))
- rc = rsp.rsp0;
- }
- } else {
- // --- first access so that we can write the rid data
- if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
- rc = status;
- goto done;
- }
- // --- now write the rid data
- if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
- rc = ERROR;
- goto done;
- }
- bap_write(ai, pBuf, len, BAP1);
- // ---now commit the rid data
- rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
- }
-done:
- if (lock)
- up(&ai->sem);
- return rc;
-}
-
-/* Allocates a FID to be used for transmitting packets. We only use
- one for now. */
-static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
-{
- unsigned int loop = 3000;
- Cmd cmd;
- Resp rsp;
- u16 txFid;
- __le16 txControl;
-
- cmd.cmd = CMD_ALLOCATETX;
- cmd.parm0 = lenPayload;
- if (down_interruptible(&ai->sem))
- return ERROR;
- if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
- txFid = ERROR;
- goto done;
- }
- if ( (rsp.status & 0xFF00) != 0) {
- txFid = ERROR;
- goto done;
- }
- /* wait for the allocate event/indication
- * It makes me kind of nervous that this can just sit here and spin,
- * but in practice it only loops like four times. */
- while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
- if (!loop) {
- txFid = ERROR;
- goto done;
- }
-
- // get the allocated fid and acknowledge
- txFid = IN4500(ai, TXALLOCFID);
- OUT4500(ai, EVACK, EV_ALLOC);
-
- /* The CARD is pretty cool since it converts the ethernet packet
- * into 802.11. Also note that we don't release the FID since we
- * will be using the same one over and over again. */
- /* We only have to setup the control once since we are not
- * releasing the fid. */
- if (raw)
- txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
- | TXCTL_ETHERNET | TXCTL_NORELEASE);
- else
- txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
- | TXCTL_ETHERNET | TXCTL_NORELEASE);
- if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
- txFid = ERROR;
- else
- bap_write(ai, &txControl, sizeof(txControl), BAP1);
-
-done:
- up(&ai->sem);
-
- return txFid;
-}
-
-/* In general BAP1 is dedicated to transmiting packets. However,
- since we need a BAP when accessing RIDs, we also use BAP1 for that.
- Make sure the BAP1 spinlock is held when this is called. */
-static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
-{
- __le16 payloadLen;
- Cmd cmd;
- Resp rsp;
- int miclen = 0;
- u16 txFid = len;
- MICBuffer pMic;
-
- len >>= 16;
-
- if (len <= ETH_ALEN * 2) {
- airo_print_warn(ai->dev->name, "Short packet %d", len);
- return ERROR;
- }
- len -= ETH_ALEN * 2;
-
- if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
- (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
- if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
- return ERROR;
- miclen = sizeof(pMic);
- }
- // packet is destination[6], source[6], payload[len-12]
- // write the payload length and dst/src/payload
- if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
- /* The hardware addresses aren't counted as part of the payload, so
- * we have to subtract the 12 bytes for the addresses off */
- payloadLen = cpu_to_le16(len + miclen);
- bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
- bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
- if (miclen)
- bap_write(ai, (__le16*)&pMic, miclen, BAP1);
- bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
- // issue the transmit command
- memset( &cmd, 0, sizeof( cmd ) );
- cmd.cmd = CMD_TRANSMIT;
- cmd.parm0 = txFid;
- if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
- if ( (rsp.status & 0xFF00) != 0) return ERROR;
- return SUCCESS;
-}
-
-static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
-{
- __le16 fc, payloadLen;
- Cmd cmd;
- Resp rsp;
- int hdrlen;
- static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
- /* padding of header to full size + le16 gaplen (6) + gaplen bytes */
- u16 txFid = len;
- len >>= 16;
-
- fc = *(__le16*)pPacket;
- hdrlen = header_len(fc);
-
- if (len < hdrlen) {
- airo_print_warn(ai->dev->name, "Short packet %d", len);
- return ERROR;
- }
-
- /* packet is 802.11 header + payload
- * write the payload length and dst/src/payload */
- if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
- /* The 802.11 header aren't counted as part of the payload, so
- * we have to subtract the header bytes off */
- payloadLen = cpu_to_le16(len-hdrlen);
- bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
- if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
- bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
- bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
-
- bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
- // issue the transmit command
- memset( &cmd, 0, sizeof( cmd ) );
- cmd.cmd = CMD_TRANSMIT;
- cmd.parm0 = txFid;
- if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
- if ( (rsp.status & 0xFF00) != 0) return ERROR;
- return SUCCESS;
-}
-
-/*
- * This is the proc_fs routines. It is a bit messier than I would
- * like! Feel free to clean it up!
- */
-
-static ssize_t proc_read( struct file *file,
- char __user *buffer,
- size_t len,
- loff_t *offset);
-
-static ssize_t proc_write( struct file *file,
- const char __user *buffer,
- size_t len,
- loff_t *offset );
-static int proc_close( struct inode *inode, struct file *file );
-
-static int proc_stats_open( struct inode *inode, struct file *file );
-static int proc_statsdelta_open( struct inode *inode, struct file *file );
-static int proc_status_open( struct inode *inode, struct file *file );
-static int proc_SSID_open( struct inode *inode, struct file *file );
-static int proc_APList_open( struct inode *inode, struct file *file );
-static int proc_BSSList_open( struct inode *inode, struct file *file );
-static int proc_config_open( struct inode *inode, struct file *file );
-static int proc_wepkey_open( struct inode *inode, struct file *file );
-
-static const struct file_operations proc_statsdelta_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .open = proc_statsdelta_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static const struct file_operations proc_stats_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .open = proc_stats_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static const struct file_operations proc_status_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .open = proc_status_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static const struct file_operations proc_SSID_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .write = proc_write,
- .open = proc_SSID_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static const struct file_operations proc_BSSList_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .write = proc_write,
- .open = proc_BSSList_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static const struct file_operations proc_APList_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .write = proc_write,
- .open = proc_APList_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static const struct file_operations proc_config_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .write = proc_write,
- .open = proc_config_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static const struct file_operations proc_wepkey_ops = {
- .owner = THIS_MODULE,
- .read = proc_read,
- .write = proc_write,
- .open = proc_wepkey_open,
- .release = proc_close,
- .llseek = default_llseek,
-};
-
-static struct proc_dir_entry *airo_entry;
-
-struct proc_data {
- int release_buffer;
- int readlen;
- char *rbuffer;
- int writelen;
- int maxwritelen;
- char *wbuffer;
- void (*on_close) (struct inode *, struct file *);
-};
-
-static int setup_proc_entry( struct net_device *dev,
- struct airo_info *apriv ) {
- struct proc_dir_entry *entry;
-
- /* First setup the device directory */
- strcpy(apriv->proc_name,dev->name);
- apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm,
- airo_entry);
- if (!apriv->proc_entry)
- return -ENOMEM;
- proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid);
-
- /* Setup the StatsDelta */
- entry = proc_create_data("StatsDelta", S_IRUGO & proc_perm,
- apriv->proc_entry, &proc_statsdelta_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
-
- /* Setup the Stats */
- entry = proc_create_data("Stats", S_IRUGO & proc_perm,
- apriv->proc_entry, &proc_stats_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
-
- /* Setup the Status */
- entry = proc_create_data("Status", S_IRUGO & proc_perm,
- apriv->proc_entry, &proc_status_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
-
- /* Setup the Config */
- entry = proc_create_data("Config", proc_perm,
- apriv->proc_entry, &proc_config_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
-
- /* Setup the SSID */
- entry = proc_create_data("SSID", proc_perm,
- apriv->proc_entry, &proc_SSID_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
-
- /* Setup the APList */
- entry = proc_create_data("APList", proc_perm,
- apriv->proc_entry, &proc_APList_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
-
- /* Setup the BSSList */
- entry = proc_create_data("BSSList", proc_perm,
- apriv->proc_entry, &proc_BSSList_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
-
- /* Setup the WepKey */
- entry = proc_create_data("WepKey", proc_perm,
- apriv->proc_entry, &proc_wepkey_ops, dev);
- if (!entry)
- goto fail;
- proc_set_user(entry, proc_kuid, proc_kgid);
- return 0;
-
-fail:
- remove_proc_subtree(apriv->proc_name, airo_entry);
- return -ENOMEM;
-}
-
-static int takedown_proc_entry( struct net_device *dev,
- struct airo_info *apriv )
-{
- remove_proc_subtree(apriv->proc_name, airo_entry);
- return 0;
-}
-
-/*
- * What we want from the proc_fs is to be able to efficiently read
- * and write the configuration. To do this, we want to read the
- * configuration when the file is opened and write it when the file is
- * closed. So basically we allocate a read buffer at open and fill it
- * with data, and allocate a write buffer and read it at close.
- */
-
-/*
- * The read routine is generic, it relies on the preallocated rbuffer
- * to supply the data.
- */
-static ssize_t proc_read( struct file *file,
- char __user *buffer,
- size_t len,
- loff_t *offset )
-{
- struct proc_data *priv = file->private_data;
-
- if (!priv->rbuffer)
- return -EINVAL;
-
- return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
- priv->readlen);
-}
-
-/*
- * The write routine is generic, it fills in a preallocated rbuffer
- * to supply the data.
- */
-static ssize_t proc_write( struct file *file,
- const char __user *buffer,
- size_t len,
- loff_t *offset )
-{
- ssize_t ret;
- struct proc_data *priv = file->private_data;
-
- if (!priv->wbuffer)
- return -EINVAL;
-
- ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset,
- buffer, len);
- if (ret > 0)
- priv->writelen = max_t(int, priv->writelen, *offset);
-
- return ret;
-}
-
-static int proc_status_open(struct inode *inode, struct file *file)
-{
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *apriv = dev->ml_priv;
- CapabilityRid cap_rid;
- StatusRid status_rid;
- u16 mode;
- int i;
-
- if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- data = file->private_data;
- if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
- kfree (file->private_data);
- return -ENOMEM;
- }
-
- readStatusRid(apriv, &status_rid, 1);
- readCapabilityRid(apriv, &cap_rid, 1);
-
- mode = le16_to_cpu(status_rid.mode);
-
- i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
- mode & 1 ? "CFG ": "",
- mode & 2 ? "ACT ": "",
- mode & 0x10 ? "SYN ": "",
- mode & 0x20 ? "LNK ": "",
- mode & 0x40 ? "LEAP ": "",
- mode & 0x80 ? "PRIV ": "",
- mode & 0x100 ? "KEY ": "",
- mode & 0x200 ? "WEP ": "",
- mode & 0x8000 ? "ERR ": "");
- sprintf( data->rbuffer+i, "Mode: %x\n"
- "Signal Strength: %d\n"
- "Signal Quality: %d\n"
- "SSID: %-.*s\n"
- "AP: %-.16s\n"
- "Freq: %d\n"
- "BitRate: %dmbs\n"
- "Driver Version: %s\n"
- "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
- "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
- "Software Version: %x\nSoftware Subversion: %x\n"
- "Boot block version: %x\n",
- le16_to_cpu(status_rid.mode),
- le16_to_cpu(status_rid.normalizedSignalStrength),
- le16_to_cpu(status_rid.signalQuality),
- le16_to_cpu(status_rid.SSIDlen),
- status_rid.SSID,
- status_rid.apName,
- le16_to_cpu(status_rid.channel),
- le16_to_cpu(status_rid.currentXmitRate) / 2,
- version,
- cap_rid.prodName,
- cap_rid.manName,
- cap_rid.prodVer,
- le16_to_cpu(cap_rid.radioType),
- le16_to_cpu(cap_rid.country),
- le16_to_cpu(cap_rid.hardVer),
- le16_to_cpu(cap_rid.softVer),
- le16_to_cpu(cap_rid.softSubVer),
- le16_to_cpu(cap_rid.bootBlockVer));
- data->readlen = strlen( data->rbuffer );
- return 0;
-}
-
-static int proc_stats_rid_open(struct inode*, struct file*, u16);
-static int proc_statsdelta_open( struct inode *inode,
- struct file *file ) {
- if (file->f_mode&FMODE_WRITE) {
- return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
- }
- return proc_stats_rid_open(inode, file, RID_STATSDELTA);
-}
-
-static int proc_stats_open( struct inode *inode, struct file *file ) {
- return proc_stats_rid_open(inode, file, RID_STATS);
-}
-
-static int proc_stats_rid_open( struct inode *inode,
- struct file *file,
- u16 rid )
-{
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *apriv = dev->ml_priv;
- StatsRid stats;
- int i, j;
- __le32 *vals = stats.vals;
- int len;
-
- if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- data = file->private_data;
- if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
- kfree (file->private_data);
- return -ENOMEM;
- }
-
- readStatsRid(apriv, &stats, rid, 1);
- len = le16_to_cpu(stats.len);
-
- j = 0;
- for(i=0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
- if (!statsLabels[i]) continue;
- if (j+strlen(statsLabels[i])+16>4096) {
- airo_print_warn(apriv->dev->name,
- "Potentially disastrous buffer overflow averted!");
- break;
- }
- j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
- le32_to_cpu(vals[i]));
- }
- if (i*4 >= len) {
- airo_print_warn(apriv->dev->name, "Got a short rid");
- }
- data->readlen = j;
- return 0;
-}
-
-static int get_dec_u16( char *buffer, int *start, int limit ) {
- u16 value;
- int valid = 0;
- for (value = 0; *start < limit && buffer[*start] >= '0' &&
- buffer[*start] <= '9'; (*start)++) {
- valid = 1;
- value *= 10;
- value += buffer[*start] - '0';
- }
- if ( !valid ) return -1;
- return value;
-}
-
-static int airo_config_commit(struct net_device *dev,
- struct iw_request_info *info, void *zwrq,
- char *extra);
-
-static inline int sniffing_mode(struct airo_info *ai)
-{
- return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >=
- le16_to_cpu(RXMODE_RFMON);
-}
-
-static void proc_config_on_close(struct inode *inode, struct file *file)
-{
- struct proc_data *data = file->private_data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- char *line;
-
- if ( !data->writelen ) return;
-
- readConfigRid(ai, 1);
- set_bit (FLAG_COMMIT, &ai->flags);
-
- line = data->wbuffer;
- while( line[0] ) {
-/*** Mode processing */
- if ( !strncmp( line, "Mode: ", 6 ) ) {
- line += 6;
- if (sniffing_mode(ai))
- set_bit (FLAG_RESET, &ai->flags);
- ai->config.rmode &= ~RXMODE_FULL_MASK;
- clear_bit (FLAG_802_11, &ai->flags);
- ai->config.opmode &= ~MODE_CFG_MASK;
- ai->config.scanMode = SCANMODE_ACTIVE;
- if ( line[0] == 'a' ) {
- ai->config.opmode |= MODE_STA_IBSS;
- } else {
- ai->config.opmode |= MODE_STA_ESS;
- if ( line[0] == 'r' ) {
- ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
- ai->config.scanMode = SCANMODE_PASSIVE;
- set_bit (FLAG_802_11, &ai->flags);
- } else if ( line[0] == 'y' ) {
- ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
- ai->config.scanMode = SCANMODE_PASSIVE;
- set_bit (FLAG_802_11, &ai->flags);
- } else if ( line[0] == 'l' )
- ai->config.rmode |= RXMODE_LANMON;
- }
- set_bit (FLAG_COMMIT, &ai->flags);
- }
-
-/*** Radio status */
- else if (!strncmp(line,"Radio: ", 7)) {
- line += 7;
- if (!strncmp(line,"off",3)) {
- set_bit (FLAG_RADIO_OFF, &ai->flags);
- } else {
- clear_bit (FLAG_RADIO_OFF, &ai->flags);
- }
- }
-/*** NodeName processing */
- else if ( !strncmp( line, "NodeName: ", 10 ) ) {
- int j;
-
- line += 10;
- memset( ai->config.nodeName, 0, 16 );
-/* Do the name, assume a space between the mode and node name */
- for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
- ai->config.nodeName[j] = line[j];
- }
- set_bit (FLAG_COMMIT, &ai->flags);
- }
-
-/*** PowerMode processing */
- else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
- line += 11;
- if ( !strncmp( line, "PSPCAM", 6 ) ) {
- ai->config.powerSaveMode = POWERSAVE_PSPCAM;
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "PSP", 3 ) ) {
- ai->config.powerSaveMode = POWERSAVE_PSP;
- set_bit (FLAG_COMMIT, &ai->flags);
- } else {
- ai->config.powerSaveMode = POWERSAVE_CAM;
- set_bit (FLAG_COMMIT, &ai->flags);
- }
- } else if ( !strncmp( line, "DataRates: ", 11 ) ) {
- int v, i = 0, k = 0; /* i is index into line,
- k is index to rates */
-
- line += 11;
- while((v = get_dec_u16(line, &i, 3))!=-1) {
- ai->config.rates[k++] = (u8)v;
- line += i + 1;
- i = 0;
- }
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "Channel: ", 9 ) ) {
- int v, i = 0;
- line += 9;
- v = get_dec_u16(line, &i, i+3);
- if ( v != -1 ) {
- ai->config.channelSet = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- }
- } else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
- int v, i = 0;
- line += 11;
- v = get_dec_u16(line, &i, i+3);
- if ( v != -1 ) {
- ai->config.txPower = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- }
- } else if ( !strncmp( line, "WEP: ", 5 ) ) {
- line += 5;
- switch( line[0] ) {
- case 's':
- set_auth_type(ai, AUTH_SHAREDKEY);
- break;
- case 'e':
- set_auth_type(ai, AUTH_ENCRYPT);
- break;
- default:
- set_auth_type(ai, AUTH_OPEN);
- break;
- }
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
- int v, i = 0;
-
- line += 16;
- v = get_dec_u16(line, &i, 3);
- v = (v<0) ? 0 : ((v>255) ? 255 : v);
- ai->config.longRetryLimit = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
- int v, i = 0;
-
- line += 17;
- v = get_dec_u16(line, &i, 3);
- v = (v<0) ? 0 : ((v>255) ? 255 : v);
- ai->config.shortRetryLimit = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
- int v, i = 0;
-
- line += 14;
- v = get_dec_u16(line, &i, 4);
- v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
- ai->config.rtsThres = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
- int v, i = 0;
-
- line += 16;
- v = get_dec_u16(line, &i, 5);
- v = (v<0) ? 0 : v;
- ai->config.txLifetime = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
- int v, i = 0;
-
- line += 16;
- v = get_dec_u16(line, &i, 5);
- v = (v<0) ? 0 : v;
- ai->config.rxLifetime = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
- ai->config.txDiversity =
- (line[13]=='l') ? 1 :
- ((line[13]=='r')? 2: 3);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
- ai->config.rxDiversity =
- (line[13]=='l') ? 1 :
- ((line[13]=='r')? 2: 3);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
- int v, i = 0;
-
- line += 15;
- v = get_dec_u16(line, &i, 4);
- v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
- v = v & 0xfffe; /* Make sure its even */
- ai->config.fragThresh = cpu_to_le16(v);
- set_bit (FLAG_COMMIT, &ai->flags);
- } else if (!strncmp(line, "Modulation: ", 12)) {
- line += 12;
- switch(*line) {
- case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
- case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
- case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
- default: airo_print_warn(ai->dev->name, "Unknown modulation");
- }
- } else if (!strncmp(line, "Preamble: ", 10)) {
- line += 10;
- switch(*line) {
- case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
- case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
- case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
- default: airo_print_warn(ai->dev->name, "Unknown preamble");
- }
- } else {
- airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
- }
- while( line[0] && line[0] != '\n' ) line++;
- if ( line[0] ) line++;
- }
- airo_config_commit(dev, NULL, NULL, NULL);
-}
-
-static const char *get_rmode(__le16 mode)
-{
- switch(mode & RXMODE_MASK) {
- case RXMODE_RFMON: return "rfmon";
- case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
- case RXMODE_LANMON: return "lanmon";
- }
- return "ESS";
-}
-
-static int proc_config_open(struct inode *inode, struct file *file)
-{
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- int i;
- __le16 mode;
-
- if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- data = file->private_data;
- if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
- kfree (file->private_data);
- return -ENOMEM;
- }
- if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) {
- kfree (data->rbuffer);
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->maxwritelen = 2048;
- data->on_close = proc_config_on_close;
-
- readConfigRid(ai, 1);
-
- mode = ai->config.opmode & MODE_CFG_MASK;
- i = sprintf( data->rbuffer,
- "Mode: %s\n"
- "Radio: %s\n"
- "NodeName: %-16s\n"
- "PowerMode: %s\n"
- "DataRates: %d %d %d %d %d %d %d %d\n"
- "Channel: %d\n"
- "XmitPower: %d\n",
- mode == MODE_STA_IBSS ? "adhoc" :
- mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
- mode == MODE_AP ? "AP" :
- mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
- test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
- ai->config.nodeName,
- ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
- ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
- ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
- "Error",
- (int)ai->config.rates[0],
- (int)ai->config.rates[1],
- (int)ai->config.rates[2],
- (int)ai->config.rates[3],
- (int)ai->config.rates[4],
- (int)ai->config.rates[5],
- (int)ai->config.rates[6],
- (int)ai->config.rates[7],
- le16_to_cpu(ai->config.channelSet),
- le16_to_cpu(ai->config.txPower)
- );
- sprintf( data->rbuffer + i,
- "LongRetryLimit: %d\n"
- "ShortRetryLimit: %d\n"
- "RTSThreshold: %d\n"
- "TXMSDULifetime: %d\n"
- "RXMSDULifetime: %d\n"
- "TXDiversity: %s\n"
- "RXDiversity: %s\n"
- "FragThreshold: %d\n"
- "WEP: %s\n"
- "Modulation: %s\n"
- "Preamble: %s\n",
- le16_to_cpu(ai->config.longRetryLimit),
- le16_to_cpu(ai->config.shortRetryLimit),
- le16_to_cpu(ai->config.rtsThres),
- le16_to_cpu(ai->config.txLifetime),
- le16_to_cpu(ai->config.rxLifetime),
- ai->config.txDiversity == 1 ? "left" :
- ai->config.txDiversity == 2 ? "right" : "both",
- ai->config.rxDiversity == 1 ? "left" :
- ai->config.rxDiversity == 2 ? "right" : "both",
- le16_to_cpu(ai->config.fragThresh),
- ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
- ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
- ai->config.modulation == MOD_DEFAULT ? "default" :
- ai->config.modulation == MOD_CCK ? "cck" :
- ai->config.modulation == MOD_MOK ? "mok" : "error",
- ai->config.preamble == PREAMBLE_AUTO ? "auto" :
- ai->config.preamble == PREAMBLE_LONG ? "long" :
- ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
- );
- data->readlen = strlen( data->rbuffer );
- return 0;
-}
-
-static void proc_SSID_on_close(struct inode *inode, struct file *file)
-{
- struct proc_data *data = file->private_data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- SsidRid SSID_rid;
- int i;
- char *p = data->wbuffer;
- char *end = p + data->writelen;
-
- if (!data->writelen)
- return;
-
- *end = '\n'; /* sentinel; we have space for it */
-
- memset(&SSID_rid, 0, sizeof(SSID_rid));
-
- for (i = 0; i < 3 && p < end; i++) {
- int j = 0;
- /* copy up to 32 characters from this line */
- while (*p != '\n' && j < 32)
- SSID_rid.ssids[i].ssid[j++] = *p++;
- if (j == 0)
- break;
- SSID_rid.ssids[i].len = cpu_to_le16(j);
- /* skip to the beginning of the next line */
- while (*p++ != '\n')
- ;
- }
- if (i)
- SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
- disable_MAC(ai, 1);
- writeSsidRid(ai, &SSID_rid, 1);
- enable_MAC(ai, 1);
-}
-
-static void proc_APList_on_close( struct inode *inode, struct file *file ) {
- struct proc_data *data = file->private_data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- APListRid *APList_rid = &ai->APList;
- int i;
-
- if ( !data->writelen ) return;
-
- memset(APList_rid, 0, sizeof(*APList_rid));
- APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
-
- for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
- int j;
- for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
- switch(j%3) {
- case 0:
- APList_rid->ap[i][j/3]=
- hex_to_bin(data->wbuffer[j+i*6*3])<<4;
- break;
- case 1:
- APList_rid->ap[i][j/3]|=
- hex_to_bin(data->wbuffer[j+i*6*3]);
- break;
- }
- }
- }
- disable_MAC(ai, 1);
- writeAPListRid(ai, APList_rid, 1);
- enable_MAC(ai, 1);
-}
-
-/* This function wraps PC4500_writerid with a MAC disable */
-static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
- int len, int dummy ) {
- int rc;
-
- disable_MAC(ai, 1);
- rc = PC4500_writerid(ai, rid, rid_data, len, 1);
- enable_MAC(ai, 1);
- return rc;
-}
-
-/* Returns the WEP key at the specified index, or -1 if that key does
- * not exist. The buffer is assumed to be at least 16 bytes in length.
- */
-static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen)
-{
- WepKeyRid wkr;
- int rc;
- __le16 lastindex;
-
- rc = readWepKeyRid(ai, &wkr, 1, 1);
- if (rc != SUCCESS)
- return -1;
- do {
- lastindex = wkr.kindex;
- if (le16_to_cpu(wkr.kindex) == index) {
- int klen = min_t(int, buflen, le16_to_cpu(wkr.klen));
- memcpy(buf, wkr.key, klen);
- return klen;
- }
- rc = readWepKeyRid(ai, &wkr, 0, 1);
- if (rc != SUCCESS)
- return -1;
- } while (lastindex != wkr.kindex);
- return -1;
-}
-
-static int get_wep_tx_idx(struct airo_info *ai)
-{
- WepKeyRid wkr;
- int rc;
- __le16 lastindex;
-
- rc = readWepKeyRid(ai, &wkr, 1, 1);
- if (rc != SUCCESS)
- return -1;
- do {
- lastindex = wkr.kindex;
- if (wkr.kindex == cpu_to_le16(0xffff))
- return wkr.mac[0];
- rc = readWepKeyRid(ai, &wkr, 0, 1);
- if (rc != SUCCESS)
- return -1;
- } while (lastindex != wkr.kindex);
- return -1;
-}
-
-static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
- u16 keylen, int perm, int lock)
-{
- static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
- WepKeyRid wkr;
- int rc;
-
- if (WARN_ON(keylen == 0))
- return -1;
-
- memset(&wkr, 0, sizeof(wkr));
- wkr.len = cpu_to_le16(sizeof(wkr));
- wkr.kindex = cpu_to_le16(index);
- wkr.klen = cpu_to_le16(keylen);
- memcpy(wkr.key, key, keylen);
- memcpy(wkr.mac, macaddr, ETH_ALEN);
-
- if (perm) disable_MAC(ai, lock);
- rc = writeWepKeyRid(ai, &wkr, perm, lock);
- if (perm) enable_MAC(ai, lock);
- return rc;
-}
-
-static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock)
-{
- WepKeyRid wkr;
- int rc;
-
- memset(&wkr, 0, sizeof(wkr));
- wkr.len = cpu_to_le16(sizeof(wkr));
- wkr.kindex = cpu_to_le16(0xffff);
- wkr.mac[0] = (char)index;
-
- if (perm) {
- ai->defindex = (char)index;
- disable_MAC(ai, lock);
- }
-
- rc = writeWepKeyRid(ai, &wkr, perm, lock);
-
- if (perm)
- enable_MAC(ai, lock);
- return rc;
-}
-
-static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- int i, rc;
- char key[16];
- u16 index = 0;
- int j = 0;
-
- memset(key, 0, sizeof(key));
-
- data = file->private_data;
- if ( !data->writelen ) return;
-
- if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
- (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
- index = data->wbuffer[0] - '0';
- if (data->wbuffer[1] == '\n') {
- rc = set_wep_tx_idx(ai, index, 1, 1);
- if (rc < 0) {
- airo_print_err(ai->dev->name, "failed to set "
- "WEP transmit index to %d: %d.",
- index, rc);
- }
- return;
- }
- j = 2;
- } else {
- airo_print_err(ai->dev->name, "WepKey passed invalid key index");
- return;
- }
-
- for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) {
- switch(i%3) {
- case 0:
- key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4;
- break;
- case 1:
- key[i/3] |= hex_to_bin(data->wbuffer[i+j]);
- break;
- }
- }
-
- rc = set_wep_key(ai, index, key, i/3, 1, 1);
- if (rc < 0) {
- airo_print_err(ai->dev->name, "failed to set WEP key at index "
- "%d: %d.", index, rc);
- }
-}
-
-static int proc_wepkey_open( struct inode *inode, struct file *file )
-{
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- char *ptr;
- WepKeyRid wkr;
- __le16 lastindex;
- int j=0;
- int rc;
-
- if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- memset(&wkr, 0, sizeof(wkr));
- data = file->private_data;
- if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) {
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->writelen = 0;
- data->maxwritelen = 80;
- if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) {
- kfree (data->rbuffer);
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->on_close = proc_wepkey_on_close;
-
- ptr = data->rbuffer;
- strcpy(ptr, "No wep keys\n");
- rc = readWepKeyRid(ai, &wkr, 1, 1);
- if (rc == SUCCESS) do {
- lastindex = wkr.kindex;
- if (wkr.kindex == cpu_to_le16(0xffff)) {
- j += sprintf(ptr+j, "Tx key = %d\n",
- (int)wkr.mac[0]);
- } else {
- j += sprintf(ptr+j, "Key %d set with length = %d\n",
- le16_to_cpu(wkr.kindex),
- le16_to_cpu(wkr.klen));
- }
- readWepKeyRid(ai, &wkr, 0, 1);
- } while((lastindex != wkr.kindex) && (j < 180-30));
-
- data->readlen = strlen( data->rbuffer );
- return 0;
-}
-
-static int proc_SSID_open(struct inode *inode, struct file *file)
-{
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- int i;
- char *ptr;
- SsidRid SSID_rid;
-
- if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- data = file->private_data;
- if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->writelen = 0;
- data->maxwritelen = 33*3;
- /* allocate maxwritelen + 1; we'll want a sentinel */
- if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
- kfree (data->rbuffer);
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->on_close = proc_SSID_on_close;
-
- readSsidRid(ai, &SSID_rid);
- ptr = data->rbuffer;
- for (i = 0; i < 3; i++) {
- int j;
- size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
- if (!len)
- break;
- if (len > 32)
- len = 32;
- for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
- *ptr++ = SSID_rid.ssids[i].ssid[j];
- *ptr++ = '\n';
- }
- *ptr = '\0';
- data->readlen = strlen( data->rbuffer );
- return 0;
-}
-
-static int proc_APList_open( struct inode *inode, struct file *file ) {
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- int i;
- char *ptr;
- APListRid *APList_rid = &ai->APList;
-
- if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- data = file->private_data;
- if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->writelen = 0;
- data->maxwritelen = 4*6*3;
- if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) {
- kfree (data->rbuffer);
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->on_close = proc_APList_on_close;
-
- ptr = data->rbuffer;
- for( i = 0; i < 4; i++ ) {
-// We end when we find a zero MAC
- if ( !*(int*)APList_rid->ap[i] &&
- !*(int*)&APList_rid->ap[i][2]) break;
- ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]);
- }
- if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
-
- *ptr = '\0';
- data->readlen = strlen( data->rbuffer );
- return 0;
-}
-
-static int proc_BSSList_open( struct inode *inode, struct file *file ) {
- struct proc_data *data;
- struct net_device *dev = PDE_DATA(inode);
- struct airo_info *ai = dev->ml_priv;
- char *ptr;
- BSSListRid BSSList_rid;
- int rc;
- /* If doLoseSync is not 1, we won't do a Lose Sync */
- int doLoseSync = -1;
-
- if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- data = file->private_data;
- if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) {
- kfree (file->private_data);
- return -ENOMEM;
- }
- data->writelen = 0;
- data->maxwritelen = 0;
- data->wbuffer = NULL;
- data->on_close = NULL;
-
- if (file->f_mode & FMODE_WRITE) {
- if (!(file->f_mode & FMODE_READ)) {
- Cmd cmd;
- Resp rsp;
-
- if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd=CMD_LISTBSS;
- if (down_interruptible(&ai->sem))
- return -ERESTARTSYS;
- issuecommand(ai, &cmd, &rsp);
- up(&ai->sem);
- data->readlen = 0;
- return 0;
- }
- doLoseSync = 1;
- }
- ptr = data->rbuffer;
- /* There is a race condition here if there are concurrent opens.
- Since it is a rare condition, we'll just live with it, otherwise
- we have to add a spin lock... */
- rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
- while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
- ptr += sprintf(ptr, "%pM %*s rssi = %d",
- BSSList_rid.bssid,
- (int)BSSList_rid.ssidLen,
- BSSList_rid.ssid,
- le16_to_cpu(BSSList_rid.dBm));
- ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
- le16_to_cpu(BSSList_rid.dsChannel),
- BSSList_rid.cap & CAP_ESS ? "ESS" : "",
- BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
- BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
- BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
- rc = readBSSListRid(ai, 0, &BSSList_rid);
- }
- *ptr = '\0';
- data->readlen = strlen( data->rbuffer );
- return 0;
-}
-
-static int proc_close( struct inode *inode, struct file *file )
-{
- struct proc_data *data = file->private_data;
-
- if (data->on_close != NULL)
- data->on_close(inode, file);
- kfree(data->rbuffer);
- kfree(data->wbuffer);
- kfree(data);
- return 0;
-}
-
-/* Since the card doesn't automatically switch to the right WEP mode,
- we will make it do it. If the card isn't associated, every secs we
- will switch WEP modes to see if that will help. If the card is
- associated we will check every minute to see if anything has
- changed. */
-static void timer_func( struct net_device *dev ) {
- struct airo_info *apriv = dev->ml_priv;
-
-/* We don't have a link so try changing the authtype */
- readConfigRid(apriv, 0);
- disable_MAC(apriv, 0);
- switch(apriv->config.authType) {
- case AUTH_ENCRYPT:
-/* So drop to OPEN */
- apriv->config.authType = AUTH_OPEN;
- break;
- case AUTH_SHAREDKEY:
- if (apriv->keyindex < auto_wep) {
- set_wep_tx_idx(apriv, apriv->keyindex, 0, 0);
- apriv->config.authType = AUTH_SHAREDKEY;
- apriv->keyindex++;
- } else {
- /* Drop to ENCRYPT */
- apriv->keyindex = 0;
- set_wep_tx_idx(apriv, apriv->defindex, 0, 0);
- apriv->config.authType = AUTH_ENCRYPT;
- }
- break;
- default: /* We'll escalate to SHAREDKEY */
- apriv->config.authType = AUTH_SHAREDKEY;
- }
- set_bit (FLAG_COMMIT, &apriv->flags);
- writeConfigRid(apriv, 0);
- enable_MAC(apriv, 0);
- up(&apriv->sem);
-
-/* Schedule check to see if the change worked */
- clear_bit(JOB_AUTOWEP, &apriv->jobs);
- apriv->expires = RUN_AT(HZ*3);
-}
-
-#ifdef CONFIG_PCI
-static int airo_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *pent)
-{
- struct net_device *dev;
-
- if (pci_enable_device(pdev))
- return -ENODEV;
- pci_set_master(pdev);
-
- if (pdev->device == 0x5000 || pdev->device == 0xa504)
- dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
- else
- dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
- if (!dev) {
- pci_disable_device(pdev);
- return -ENODEV;
- }
-
- pci_set_drvdata(pdev, dev);
- return 0;
-}
-
-static void airo_pci_remove(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
-
- airo_print_info(dev->name, "Unregistering...");
- stop_airo_card(dev, 1);
- pci_disable_device(pdev);
-}
-
-static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct airo_info *ai = dev->ml_priv;
- Cmd cmd;
- Resp rsp;
-
- if (!ai->SSID)
- ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
- if (!ai->SSID)
- return -ENOMEM;
- readSsidRid(ai, ai->SSID);
- memset(&cmd, 0, sizeof(cmd));
- /* the lock will be released at the end of the resume callback */
- if (down_interruptible(&ai->sem))
- return -EAGAIN;
- disable_MAC(ai, 0);
- netif_device_detach(dev);
- ai->power = state;
- cmd.cmd = HOSTSLEEP;
- issuecommand(ai, &cmd, &rsp);
-
- pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
- pci_save_state(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
- return 0;
-}
-
-static int airo_pci_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct airo_info *ai = dev->ml_priv;
- pci_power_t prev_state = pdev->current_state;
-
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
- pci_enable_wake(pdev, PCI_D0, 0);
-
- if (prev_state != PCI_D1) {
- reset_card(dev, 0);
- mpi_init_descriptors(ai);
- setup_card(ai, dev->dev_addr, 0);
- clear_bit(FLAG_RADIO_OFF, &ai->flags);
- clear_bit(FLAG_PENDING_XMIT, &ai->flags);
- } else {
- OUT4500(ai, EVACK, EV_AWAKEN);
- OUT4500(ai, EVACK, EV_AWAKEN);
- msleep(100);
- }
-
- set_bit(FLAG_COMMIT, &ai->flags);
- disable_MAC(ai, 0);
- msleep(200);
- if (ai->SSID) {
- writeSsidRid(ai, ai->SSID, 0);
- kfree(ai->SSID);
- ai->SSID = NULL;
- }
- writeAPListRid(ai, &ai->APList, 0);
- writeConfigRid(ai, 0);
- enable_MAC(ai, 0);
- ai->power = PMSG_ON;
- netif_device_attach(dev);
- netif_wake_queue(dev);
- enable_interrupts(ai);
- up(&ai->sem);
- return 0;
-}
-#endif
-
-static int __init airo_init_module( void )
-{
- int i;
-
- proc_kuid = make_kuid(&init_user_ns, proc_uid);
- proc_kgid = make_kgid(&init_user_ns, proc_gid);
- if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid))
- return -EINVAL;
-
- airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL);
-
- if (airo_entry)
- proc_set_user(airo_entry, proc_kuid, proc_kgid);
-
- for (i = 0; i < 4 && io[i] && irq[i]; i++) {
- airo_print_info("", "Trying to configure ISA adapter at irq=%d "
- "io=0x%x", irq[i], io[i] );
- if (init_airo_card( irq[i], io[i], 0, NULL ))
- /* do nothing */ ;
- }
-
-#ifdef CONFIG_PCI
- airo_print_info("", "Probing for PCI adapters");
- i = pci_register_driver(&airo_driver);
- airo_print_info("", "Finished probing for PCI adapters");
-
- if (i) {
- remove_proc_entry("driver/aironet", NULL);
- return i;
- }
-#endif
-
- /* Always exit with success, as we are a library module
- * as well as a driver module
- */
- return 0;
-}
-
-static void __exit airo_cleanup_module( void )
-{
- struct airo_info *ai;
- while(!list_empty(&airo_devices)) {
- ai = list_entry(airo_devices.next, struct airo_info, dev_list);
- airo_print_info(ai->dev->name, "Unregistering...");
- stop_airo_card(ai->dev, 1);
- }
-#ifdef CONFIG_PCI
- pci_unregister_driver(&airo_driver);
-#endif
- remove_proc_entry("driver/aironet", NULL);
-}
-
-/*
- * Initial Wireless Extension code for Aironet driver by :
- * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
- * Conversion to new driver API by :
- * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
- * Javier also did a good amount of work here, adding some new extensions
- * and fixing my code. Let's just say that without him this code just
- * would not work at all... - Jean II
- */
-
-static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
-{
- if (!rssi_rid)
- return 0;
-
- return (0x100 - rssi_rid[rssi].rssidBm);
-}
-
-static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
-{
- int i;
-
- if (!rssi_rid)
- return 0;
-
- for (i = 0; i < 256; i++)
- if (rssi_rid[i].rssidBm == dbm)
- return rssi_rid[i].rssipct;
-
- return 0;
-}
-
-
-static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
-{
- int quality = 0;
- u16 sq;
-
- if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
- return 0;
-
- if (!(cap_rid->hardCap & cpu_to_le16(8)))
- return 0;
-
- sq = le16_to_cpu(status_rid->signalQuality);
- if (memcmp(cap_rid->prodName, "350", 3))
- if (sq > 0x20)
- quality = 0;
- else
- quality = 0x20 - sq;
- else
- if (sq > 0xb0)
- quality = 0;
- else if (sq < 0x10)
- quality = 0xa0;
- else
- quality = 0xb0 - sq;
- return quality;
-}
-
-#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
-#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get protocol name
- */
-static int airo_get_name(struct net_device *dev,
- struct iw_request_info *info,
- char *cwrq,
- char *extra)
-{
- strcpy(cwrq, "IEEE 802.11-DS");
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set frequency
- */
-static int airo_set_freq(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_freq *fwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- int rc = -EINPROGRESS; /* Call commit handler */
-
- /* If setting by frequency, convert to a channel */
- if(fwrq->e == 1) {
- int f = fwrq->m / 100000;
-
- /* Hack to fall through... */
- fwrq->e = 0;
- fwrq->m = ieee80211_frequency_to_channel(f);
- }
- /* Setting by channel number */
- if((fwrq->m > 1000) || (fwrq->e > 0))
- rc = -EOPNOTSUPP;
- else {
- int channel = fwrq->m;
- /* We should do a better check than that,
- * based on the card capability !!! */
- if((channel < 1) || (channel > 14)) {
- airo_print_dbg(dev->name, "New channel value of %d is invalid!",
- fwrq->m);
- rc = -EINVAL;
- } else {
- readConfigRid(local, 1);
- /* Yes ! We can set it !!! */
- local->config.channelSet = cpu_to_le16(channel);
- set_bit (FLAG_COMMIT, &local->flags);
- }
- }
- return rc;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get frequency
- */
-static int airo_get_freq(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_freq *fwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- StatusRid status_rid; /* Card status info */
- int ch;
-
- readConfigRid(local, 1);
- if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
- status_rid.channel = local->config.channelSet;
- else
- readStatusRid(local, &status_rid, 1);
-
- ch = le16_to_cpu(status_rid.channel);
- if((ch > 0) && (ch < 15)) {
- fwrq->m = 100000 *
- ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ);
- fwrq->e = 1;
- } else {
- fwrq->m = ch;
- fwrq->e = 0;
- }
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set ESSID
- */
-static int airo_set_essid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- SsidRid SSID_rid; /* SSIDs */
-
- /* Reload the list of current SSID */
- readSsidRid(local, &SSID_rid);
-
- /* Check if we asked for `any' */
- if (dwrq->flags == 0) {
- /* Just send an empty SSID list */
- memset(&SSID_rid, 0, sizeof(SSID_rid));
- } else {
- unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
-
- /* Check the size of the string */
- if (dwrq->length > IW_ESSID_MAX_SIZE)
- return -E2BIG ;
-
- /* Check if index is valid */
- if (index >= ARRAY_SIZE(SSID_rid.ssids))
- return -EINVAL;
-
- /* Set the SSID */
- memset(SSID_rid.ssids[index].ssid, 0,
- sizeof(SSID_rid.ssids[index].ssid));
- memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
- SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
- }
- SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
- /* Write it to the card */
- disable_MAC(local, 1);
- writeSsidRid(local, &SSID_rid, 1);
- enable_MAC(local, 1);
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get ESSID
- */
-static int airo_get_essid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- StatusRid status_rid; /* Card status info */
-
- readStatusRid(local, &status_rid, 1);
-
- /* Note : if dwrq->flags != 0, we should
- * get the relevant SSID from the SSID list... */
-
- /* Get the current SSID */
- memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
- /* If none, we may want to get the one that was set */
-
- /* Push it out ! */
- dwrq->length = le16_to_cpu(status_rid.SSIDlen);
- dwrq->flags = 1; /* active */
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set AP address
- */
-static int airo_set_wap(struct net_device *dev,
- struct iw_request_info *info,
- struct sockaddr *awrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- Cmd cmd;
- Resp rsp;
- APListRid *APList_rid = &local->APList;
-
- if (awrq->sa_family != ARPHRD_ETHER)
- return -EINVAL;
- else if (is_broadcast_ether_addr(awrq->sa_data) ||
- is_zero_ether_addr(awrq->sa_data)) {
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd=CMD_LOSE_SYNC;
- if (down_interruptible(&local->sem))
- return -ERESTARTSYS;
- issuecommand(local, &cmd, &rsp);
- up(&local->sem);
- } else {
- memset(APList_rid, 0, sizeof(*APList_rid));
- APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
- memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN);
- disable_MAC(local, 1);
- writeAPListRid(local, APList_rid, 1);
- enable_MAC(local, 1);
- }
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get AP address
- */
-static int airo_get_wap(struct net_device *dev,
- struct iw_request_info *info,
- struct sockaddr *awrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- StatusRid status_rid; /* Card status info */
-
- readStatusRid(local, &status_rid, 1);
-
- /* Tentative. This seems to work, wow, I'm lucky !!! */
- memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
- awrq->sa_family = ARPHRD_ETHER;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Nickname
- */
-static int airo_set_nick(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- /* Check the size of the string */
- if(dwrq->length > 16) {
- return -E2BIG;
- }
- readConfigRid(local, 1);
- memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
- memcpy(local->config.nodeName, extra, dwrq->length);
- set_bit (FLAG_COMMIT, &local->flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Nickname
- */
-static int airo_get_nick(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- strncpy(extra, local->config.nodeName, 16);
- extra[16] = '\0';
- dwrq->length = strlen(extra);
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Bit-Rate
- */
-static int airo_set_rate(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- CapabilityRid cap_rid; /* Card capability info */
- u8 brate = 0;
- int i;
-
- /* First : get a valid bit rate value */
- readCapabilityRid(local, &cap_rid, 1);
-
- /* Which type of value ? */
- if((vwrq->value < 8) && (vwrq->value >= 0)) {
- /* Setting by rate index */
- /* Find value in the magic rate table */
- brate = cap_rid.supportedRates[vwrq->value];
- } else {
- /* Setting by frequency value */
- u8 normvalue = (u8) (vwrq->value/500000);
-
- /* Check if rate is valid */
- for(i = 0 ; i < 8 ; i++) {
- if(normvalue == cap_rid.supportedRates[i]) {
- brate = normvalue;
- break;
- }
- }
- }
- /* -1 designed the max rate (mostly auto mode) */
- if(vwrq->value == -1) {
- /* Get the highest available rate */
- for(i = 0 ; i < 8 ; i++) {
- if(cap_rid.supportedRates[i] == 0)
- break;
- }
- if(i != 0)
- brate = cap_rid.supportedRates[i - 1];
- }
- /* Check that it is valid */
- if(brate == 0) {
- return -EINVAL;
- }
-
- readConfigRid(local, 1);
- /* Now, check if we want a fixed or auto value */
- if(vwrq->fixed == 0) {
- /* Fill all the rates up to this max rate */
- memset(local->config.rates, 0, 8);
- for(i = 0 ; i < 8 ; i++) {
- local->config.rates[i] = cap_rid.supportedRates[i];
- if(local->config.rates[i] == brate)
- break;
- }
- } else {
- /* Fixed mode */
- /* One rate, fixed */
- memset(local->config.rates, 0, 8);
- local->config.rates[0] = brate;
- }
- set_bit (FLAG_COMMIT, &local->flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Bit-Rate
- */
-static int airo_get_rate(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- StatusRid status_rid; /* Card status info */
-
- readStatusRid(local, &status_rid, 1);
-
- vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
- /* If more than one rate, set auto */
- readConfigRid(local, 1);
- vwrq->fixed = (local->config.rates[1] == 0);
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set RTS threshold
- */
-static int airo_set_rts(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- int rthr = vwrq->value;
-
- if(vwrq->disabled)
- rthr = AIRO_DEF_MTU;
- if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
- return -EINVAL;
- }
- readConfigRid(local, 1);
- local->config.rtsThres = cpu_to_le16(rthr);
- set_bit (FLAG_COMMIT, &local->flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get RTS threshold
- */
-static int airo_get_rts(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- vwrq->value = le16_to_cpu(local->config.rtsThres);
- vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
- vwrq->fixed = 1;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Fragmentation threshold
- */
-static int airo_set_frag(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- int fthr = vwrq->value;
-
- if(vwrq->disabled)
- fthr = AIRO_DEF_MTU;
- if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
- return -EINVAL;
- }
- fthr &= ~0x1; /* Get an even value - is it really needed ??? */
- readConfigRid(local, 1);
- local->config.fragThresh = cpu_to_le16(fthr);
- set_bit (FLAG_COMMIT, &local->flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Fragmentation threshold
- */
-static int airo_get_frag(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- vwrq->value = le16_to_cpu(local->config.fragThresh);
- vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
- vwrq->fixed = 1;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Mode of Operation
- */
-static int airo_set_mode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *uwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- int reset = 0;
-
- readConfigRid(local, 1);
- if (sniffing_mode(local))
- reset = 1;
-
- switch(*uwrq) {
- case IW_MODE_ADHOC:
- local->config.opmode &= ~MODE_CFG_MASK;
- local->config.opmode |= MODE_STA_IBSS;
- local->config.rmode &= ~RXMODE_FULL_MASK;
- local->config.scanMode = SCANMODE_ACTIVE;
- clear_bit (FLAG_802_11, &local->flags);
- break;
- case IW_MODE_INFRA:
- local->config.opmode &= ~MODE_CFG_MASK;
- local->config.opmode |= MODE_STA_ESS;
- local->config.rmode &= ~RXMODE_FULL_MASK;
- local->config.scanMode = SCANMODE_ACTIVE;
- clear_bit (FLAG_802_11, &local->flags);
- break;
- case IW_MODE_MASTER:
- local->config.opmode &= ~MODE_CFG_MASK;
- local->config.opmode |= MODE_AP;
- local->config.rmode &= ~RXMODE_FULL_MASK;
- local->config.scanMode = SCANMODE_ACTIVE;
- clear_bit (FLAG_802_11, &local->flags);
- break;
- case IW_MODE_REPEAT:
- local->config.opmode &= ~MODE_CFG_MASK;
- local->config.opmode |= MODE_AP_RPTR;
- local->config.rmode &= ~RXMODE_FULL_MASK;
- local->config.scanMode = SCANMODE_ACTIVE;
- clear_bit (FLAG_802_11, &local->flags);
- break;
- case IW_MODE_MONITOR:
- local->config.opmode &= ~MODE_CFG_MASK;
- local->config.opmode |= MODE_STA_ESS;
- local->config.rmode &= ~RXMODE_FULL_MASK;
- local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
- local->config.scanMode = SCANMODE_PASSIVE;
- set_bit (FLAG_802_11, &local->flags);
- break;
- default:
- return -EINVAL;
- }
- if (reset)
- set_bit (FLAG_RESET, &local->flags);
- set_bit (FLAG_COMMIT, &local->flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Mode of Operation
- */
-static int airo_get_mode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *uwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- /* If not managed, assume it's ad-hoc */
- switch (local->config.opmode & MODE_CFG_MASK) {
- case MODE_STA_ESS:
- *uwrq = IW_MODE_INFRA;
- break;
- case MODE_AP:
- *uwrq = IW_MODE_MASTER;
- break;
- case MODE_AP_RPTR:
- *uwrq = IW_MODE_REPEAT;
- break;
- default:
- *uwrq = IW_MODE_ADHOC;
- }
-
- return 0;
-}
-
-static inline int valid_index(struct airo_info *ai, int index)
-{
- return (index >= 0) && (index <= ai->max_wep_idx);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Encryption Key
- */
-static int airo_set_encode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1);
- __le16 currentAuthType = local->config.authType;
- int rc = 0;
-
- if (!local->wep_capable)
- return -EOPNOTSUPP;
-
- readConfigRid(local, 1);
-
- /* Basic checking: do we have a key to set ?
- * Note : with the new API, it's impossible to get a NULL pointer.
- * Therefore, we need to check a key size == 0 instead.
- * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
- * when no key is present (only change flags), but older versions
- * don't do it. - Jean II */
- if (dwrq->length > 0) {
- wep_key_t key;
- int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
- int current_index;
-
- /* Check the size of the key */
- if (dwrq->length > MAX_KEY_SIZE) {
- return -EINVAL;
- }
-
- current_index = get_wep_tx_idx(local);
- if (current_index < 0)
- current_index = 0;
-
- /* Check the index (none -> use current) */
- if (!valid_index(local, index))
- index = current_index;
-
- /* Set the length */
- if (dwrq->length > MIN_KEY_SIZE)
- key.len = MAX_KEY_SIZE;
- else
- key.len = MIN_KEY_SIZE;
- /* Check if the key is not marked as invalid */
- if(!(dwrq->flags & IW_ENCODE_NOKEY)) {
- /* Cleanup */
- memset(key.key, 0, MAX_KEY_SIZE);
- /* Copy the key in the driver */
- memcpy(key.key, extra, dwrq->length);
- /* Send the key to the card */
- rc = set_wep_key(local, index, key.key, key.len, perm, 1);
- if (rc < 0) {
- airo_print_err(local->dev->name, "failed to set"
- " WEP key at index %d: %d.",
- index, rc);
- return rc;
- }
- }
- /* WE specify that if a valid key is set, encryption
- * should be enabled (user may turn it off later)
- * This is also how "iwconfig ethX key on" works */
- if((index == current_index) && (key.len > 0) &&
- (local->config.authType == AUTH_OPEN))
- set_auth_type(local, AUTH_ENCRYPT);
- } else {
- /* Do we want to just set the transmit key index ? */
- int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
- if (valid_index(local, index)) {
- rc = set_wep_tx_idx(local, index, perm, 1);
- if (rc < 0) {
- airo_print_err(local->dev->name, "failed to set"
- " WEP transmit index to %d: %d.",
- index, rc);
- return rc;
- }
- } else {
- /* Don't complain if only change the mode */
- if (!(dwrq->flags & IW_ENCODE_MODE))
- return -EINVAL;
- }
- }
- /* Read the flags */
- if (dwrq->flags & IW_ENCODE_DISABLED)
- set_auth_type(local, AUTH_OPEN); /* disable encryption */
- if(dwrq->flags & IW_ENCODE_RESTRICTED)
- set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
- if (dwrq->flags & IW_ENCODE_OPEN)
- set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */
- /* Commit the changes to flags if needed */
- if (local->config.authType != currentAuthType)
- set_bit (FLAG_COMMIT, &local->flags);
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Encryption Key
- */
-static int airo_get_encode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
- int wep_key_len;
- u8 buf[16];
-
- if (!local->wep_capable)
- return -EOPNOTSUPP;
-
- readConfigRid(local, 1);
-
- /* Check encryption mode */
- switch(local->config.authType) {
- case AUTH_ENCRYPT:
- dwrq->flags = IW_ENCODE_OPEN;
- break;
- case AUTH_SHAREDKEY:
- dwrq->flags = IW_ENCODE_RESTRICTED;
- break;
- default:
- case AUTH_OPEN:
- dwrq->flags = IW_ENCODE_DISABLED;
- break;
- }
- /* We can't return the key, so set the proper flag and return zero */
- dwrq->flags |= IW_ENCODE_NOKEY;
- memset(extra, 0, 16);
-
- /* Which key do we want ? -1 -> tx index */
- if (!valid_index(local, index)) {
- index = get_wep_tx_idx(local);
- if (index < 0)
- index = 0;
- }
- dwrq->flags |= index + 1;
-
- /* Copy the key to the user buffer */
- wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf));
- if (wep_key_len < 0) {
- dwrq->length = 0;
- } else {
- dwrq->length = wep_key_len;
- memcpy(extra, buf, dwrq->length);
- }
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set extended Encryption parameters
- */
-static int airo_set_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 );
- __le16 currentAuthType = local->config.authType;
- int idx, key_len, alg = ext->alg, set_key = 1, rc;
- wep_key_t key;
-
- if (!local->wep_capable)
- return -EOPNOTSUPP;
-
- readConfigRid(local, 1);
-
- /* Determine and validate the key index */
- idx = encoding->flags & IW_ENCODE_INDEX;
- if (idx) {
- if (!valid_index(local, idx - 1))
- return -EINVAL;
- idx--;
- } else {
- idx = get_wep_tx_idx(local);
- if (idx < 0)
- idx = 0;
- }
-
- if (encoding->flags & IW_ENCODE_DISABLED)
- alg = IW_ENCODE_ALG_NONE;
-
- if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
- /* Only set transmit key index here, actual
- * key is set below if needed.
- */
- rc = set_wep_tx_idx(local, idx, perm, 1);
- if (rc < 0) {
- airo_print_err(local->dev->name, "failed to set "
- "WEP transmit index to %d: %d.",
- idx, rc);
- return rc;
- }
- set_key = ext->key_len > 0 ? 1 : 0;
- }
-
- if (set_key) {
- /* Set the requested key first */
- memset(key.key, 0, MAX_KEY_SIZE);
- switch (alg) {
- case IW_ENCODE_ALG_NONE:
- key.len = 0;
- break;
- case IW_ENCODE_ALG_WEP:
- if (ext->key_len > MIN_KEY_SIZE) {
- key.len = MAX_KEY_SIZE;
- } else if (ext->key_len > 0) {
- key.len = MIN_KEY_SIZE;
- } else {
- return -EINVAL;
- }
- key_len = min (ext->key_len, key.len);
- memcpy(key.key, ext->key, key_len);
- break;
- default:
- return -EINVAL;
- }
- if (key.len == 0) {
- rc = set_wep_tx_idx(local, idx, perm, 1);
- if (rc < 0) {
- airo_print_err(local->dev->name,
- "failed to set WEP transmit index to %d: %d.",
- idx, rc);
- return rc;
- }
- } else {
- rc = set_wep_key(local, idx, key.key, key.len, perm, 1);
- if (rc < 0) {
- airo_print_err(local->dev->name,
- "failed to set WEP key at index %d: %d.",
- idx, rc);
- return rc;
- }
- }
- }
-
- /* Read the flags */
- if (encoding->flags & IW_ENCODE_DISABLED)
- set_auth_type(local, AUTH_OPEN); /* disable encryption */
- if(encoding->flags & IW_ENCODE_RESTRICTED)
- set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
- if (encoding->flags & IW_ENCODE_OPEN)
- set_auth_type(local, AUTH_ENCRYPT);
- /* Commit the changes to flags if needed */
- if (local->config.authType != currentAuthType)
- set_bit (FLAG_COMMIT, &local->flags);
-
- return -EINPROGRESS;
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get extended Encryption parameters
- */
-static int airo_get_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int idx, max_key_len, wep_key_len;
- u8 buf[16];
-
- if (!local->wep_capable)
- return -EOPNOTSUPP;
-
- readConfigRid(local, 1);
-
- max_key_len = encoding->length - sizeof(*ext);
- if (max_key_len < 0)
- return -EINVAL;
-
- idx = encoding->flags & IW_ENCODE_INDEX;
- if (idx) {
- if (!valid_index(local, idx - 1))
- return -EINVAL;
- idx--;
- } else {
- idx = get_wep_tx_idx(local);
- if (idx < 0)
- idx = 0;
- }
-
- encoding->flags = idx + 1;
- memset(ext, 0, sizeof(*ext));
-
- /* Check encryption mode */
- switch(local->config.authType) {
- case AUTH_ENCRYPT:
- encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
- break;
- case AUTH_SHAREDKEY:
- encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
- break;
- default:
- case AUTH_OPEN:
- encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
- break;
- }
- /* We can't return the key, so set the proper flag and return zero */
- encoding->flags |= IW_ENCODE_NOKEY;
- memset(extra, 0, 16);
-
- /* Copy the key to the user buffer */
- wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
- if (wep_key_len < 0) {
- ext->key_len = 0;
- } else {
- ext->key_len = wep_key_len;
- memcpy(extra, buf, ext->key_len);
- }
-
- return 0;
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set extended authentication parameters
- */
-static int airo_set_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- struct iw_param *param = &wrqu->param;
- __le16 currentAuthType = local->config.authType;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- case IW_AUTH_PRIVACY_INVOKED:
- /*
- * airo does not use these parameters
- */
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:
- if (param->value) {
- /* Only change auth type if unencrypted */
- if (currentAuthType == AUTH_OPEN)
- set_auth_type(local, AUTH_ENCRYPT);
- } else {
- set_auth_type(local, AUTH_OPEN);
- }
-
- /* Commit the changes to flags if needed */
- if (local->config.authType != currentAuthType)
- set_bit (FLAG_COMMIT, &local->flags);
- break;
-
- case IW_AUTH_80211_AUTH_ALG: {
- if (param->value & IW_AUTH_ALG_SHARED_KEY) {
- set_auth_type(local, AUTH_SHAREDKEY);
- } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
- /* We don't know here if WEP open system or
- * unencrypted mode was requested - so use the
- * last mode (of these two) used last time
- */
- set_auth_type(local, local->last_auth);
- } else
- return -EINVAL;
-
- /* Commit the changes to flags if needed */
- if (local->config.authType != currentAuthType)
- set_bit (FLAG_COMMIT, &local->flags);
- break;
- }
-
- case IW_AUTH_WPA_ENABLED:
- /* Silently accept disable of WPA */
- if (param->value > 0)
- return -EOPNOTSUPP;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return -EINPROGRESS;
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get extended authentication parameters
- */
-static int airo_get_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- struct iw_param *param = &wrqu->param;
- __le16 currentAuthType = local->config.authType;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_DROP_UNENCRYPTED:
- switch (currentAuthType) {
- case AUTH_SHAREDKEY:
- case AUTH_ENCRYPT:
- param->value = 1;
- break;
- default:
- param->value = 0;
- break;
- }
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- switch (currentAuthType) {
- case AUTH_SHAREDKEY:
- param->value = IW_AUTH_ALG_SHARED_KEY;
- break;
- case AUTH_ENCRYPT:
- default:
- param->value = IW_AUTH_ALG_OPEN_SYSTEM;
- break;
- }
- break;
-
- case IW_AUTH_WPA_ENABLED:
- param->value = 0;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Tx-Power
- */
-static int airo_set_txpow(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- CapabilityRid cap_rid; /* Card capability info */
- int i;
- int rc = -EINVAL;
- __le16 v = cpu_to_le16(vwrq->value);
-
- readCapabilityRid(local, &cap_rid, 1);
-
- if (vwrq->disabled) {
- set_bit (FLAG_RADIO_OFF, &local->flags);
- set_bit (FLAG_COMMIT, &local->flags);
- return -EINPROGRESS; /* Call commit handler */
- }
- if (vwrq->flags != IW_TXPOW_MWATT) {
- return -EINVAL;
- }
- clear_bit (FLAG_RADIO_OFF, &local->flags);
- for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
- if (v == cap_rid.txPowerLevels[i]) {
- readConfigRid(local, 1);
- local->config.txPower = v;
- set_bit (FLAG_COMMIT, &local->flags);
- rc = -EINPROGRESS; /* Call commit handler */
- break;
- }
- return rc;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Tx-Power
- */
-static int airo_get_txpow(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- vwrq->value = le16_to_cpu(local->config.txPower);
- vwrq->fixed = 1; /* No power control */
- vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
- vwrq->flags = IW_TXPOW_MWATT;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Retry limits
- */
-static int airo_set_retry(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- int rc = -EINVAL;
-
- if(vwrq->disabled) {
- return -EINVAL;
- }
- readConfigRid(local, 1);
- if(vwrq->flags & IW_RETRY_LIMIT) {
- __le16 v = cpu_to_le16(vwrq->value);
- if(vwrq->flags & IW_RETRY_LONG)
- local->config.longRetryLimit = v;
- else if (vwrq->flags & IW_RETRY_SHORT)
- local->config.shortRetryLimit = v;
- else {
- /* No modifier : set both */
- local->config.longRetryLimit = v;
- local->config.shortRetryLimit = v;
- }
- set_bit (FLAG_COMMIT, &local->flags);
- rc = -EINPROGRESS; /* Call commit handler */
- }
- if(vwrq->flags & IW_RETRY_LIFETIME) {
- local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
- set_bit (FLAG_COMMIT, &local->flags);
- rc = -EINPROGRESS; /* Call commit handler */
- }
- return rc;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Retry limits
- */
-static int airo_get_retry(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- vwrq->disabled = 0; /* Can't be disabled */
-
- readConfigRid(local, 1);
- /* Note : by default, display the min retry number */
- if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
- vwrq->flags = IW_RETRY_LIFETIME;
- vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
- } else if((vwrq->flags & IW_RETRY_LONG)) {
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- vwrq->value = le16_to_cpu(local->config.longRetryLimit);
- } else {
- vwrq->flags = IW_RETRY_LIMIT;
- vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
- if(local->config.shortRetryLimit != local->config.longRetryLimit)
- vwrq->flags |= IW_RETRY_SHORT;
- }
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get range info
- */
-static int airo_get_range(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- struct iw_range *range = (struct iw_range *) extra;
- CapabilityRid cap_rid; /* Card capability info */
- int i;
- int k;
-
- readCapabilityRid(local, &cap_rid, 1);
-
- dwrq->length = sizeof(struct iw_range);
- memset(range, 0, sizeof(*range));
- range->min_nwid = 0x0000;
- range->max_nwid = 0x0000;
- range->num_channels = 14;
- /* Should be based on cap_rid.country to give only
- * what the current card support */
- k = 0;
- for(i = 0; i < 14; i++) {
- range->freq[k].i = i + 1; /* List index */
- range->freq[k].m = 100000 *
- ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ);
- range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */
- }
- range->num_frequency = k;
-
- range->sensitivity = 65535;
-
- /* Hum... Should put the right values there */
- if (local->rssi)
- range->max_qual.qual = 100; /* % */
- else
- range->max_qual.qual = airo_get_max_quality(&cap_rid);
- range->max_qual.level = 0x100 - 120; /* -120 dBm */
- range->max_qual.noise = 0x100 - 120; /* -120 dBm */
-
- /* Experimental measurements - boundary 11/5.5 Mb/s */
- /* Note : with or without the (local->rssi), results
- * are somewhat different. - Jean II */
- if (local->rssi) {
- range->avg_qual.qual = 50; /* % */
- range->avg_qual.level = 0x100 - 70; /* -70 dBm */
- } else {
- range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
- range->avg_qual.level = 0x100 - 80; /* -80 dBm */
- }
- range->avg_qual.noise = 0x100 - 85; /* -85 dBm */
-
- for(i = 0 ; i < 8 ; i++) {
- range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
- if(range->bitrate[i] == 0)
- break;
- }
- range->num_bitrates = i;
-
- /* Set an indication of the max TCP throughput
- * in bit/s that we can expect using this interface.
- * May be use for QoS stuff... Jean II */
- if(i > 2)
- range->throughput = 5000 * 1000;
- else
- range->throughput = 1500 * 1000;
-
- range->min_rts = 0;
- range->max_rts = AIRO_DEF_MTU;
- range->min_frag = 256;
- range->max_frag = AIRO_DEF_MTU;
-
- if(cap_rid.softCap & cpu_to_le16(2)) {
- // WEP: RC4 40 bits
- range->encoding_size[0] = 5;
- // RC4 ~128 bits
- if (cap_rid.softCap & cpu_to_le16(0x100)) {
- range->encoding_size[1] = 13;
- range->num_encoding_sizes = 2;
- } else
- range->num_encoding_sizes = 1;
- range->max_encoding_tokens =
- cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
- } else {
- range->num_encoding_sizes = 0;
- range->max_encoding_tokens = 0;
- }
- range->min_pmp = 0;
- range->max_pmp = 5000000; /* 5 secs */
- range->min_pmt = 0;
- range->max_pmt = 65535 * 1024; /* ??? */
- range->pmp_flags = IW_POWER_PERIOD;
- range->pmt_flags = IW_POWER_TIMEOUT;
- range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
-
- /* Transmit Power - values are in mW */
- for(i = 0 ; i < 8 ; i++) {
- range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
- if(range->txpower[i] == 0)
- break;
- }
- range->num_txpower = i;
- range->txpower_capa = IW_TXPOW_MWATT;
- range->we_version_source = 19;
- range->we_version_compiled = WIRELESS_EXT;
- range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
- range->retry_flags = IW_RETRY_LIMIT;
- range->r_time_flags = IW_RETRY_LIFETIME;
- range->min_retry = 1;
- range->max_retry = 65535;
- range->min_r_time = 1024;
- range->max_r_time = 65535 * 1024;
-
- /* Event capability (kernel + driver) */
- range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
- IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
- IW_EVENT_CAPA_MASK(SIOCGIWAP) |
- IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
- range->event_capa[1] = IW_EVENT_CAPA_K_1;
- range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Power Management
- */
-static int airo_set_power(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- if (vwrq->disabled) {
- if (sniffing_mode(local))
- return -EINVAL;
- local->config.powerSaveMode = POWERSAVE_CAM;
- local->config.rmode &= ~RXMODE_MASK;
- local->config.rmode |= RXMODE_BC_MC_ADDR;
- set_bit (FLAG_COMMIT, &local->flags);
- return -EINPROGRESS; /* Call commit handler */
- }
- if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
- local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
- local->config.powerSaveMode = POWERSAVE_PSPCAM;
- set_bit (FLAG_COMMIT, &local->flags);
- } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
- local->config.fastListenInterval =
- local->config.listenInterval =
- cpu_to_le16((vwrq->value + 500) / 1024);
- local->config.powerSaveMode = POWERSAVE_PSPCAM;
- set_bit (FLAG_COMMIT, &local->flags);
- }
- switch (vwrq->flags & IW_POWER_MODE) {
- case IW_POWER_UNICAST_R:
- if (sniffing_mode(local))
- return -EINVAL;
- local->config.rmode &= ~RXMODE_MASK;
- local->config.rmode |= RXMODE_ADDR;
- set_bit (FLAG_COMMIT, &local->flags);
- break;
- case IW_POWER_ALL_R:
- if (sniffing_mode(local))
- return -EINVAL;
- local->config.rmode &= ~RXMODE_MASK;
- local->config.rmode |= RXMODE_BC_MC_ADDR;
- set_bit (FLAG_COMMIT, &local->flags);
- case IW_POWER_ON:
- /* This is broken, fixme ;-) */
- break;
- default:
- return -EINVAL;
- }
- // Note : we may want to factor local->need_commit here
- // Note2 : may also want to factor RXMODE_RFMON test
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Power Management
- */
-static int airo_get_power(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- __le16 mode;
-
- readConfigRid(local, 1);
- mode = local->config.powerSaveMode;
- if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
- return 0;
- if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
- vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
- vwrq->flags = IW_POWER_TIMEOUT;
- } else {
- vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
- vwrq->flags = IW_POWER_PERIOD;
- }
- if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
- vwrq->flags |= IW_POWER_UNICAST_R;
- else
- vwrq->flags |= IW_POWER_ALL_R;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : set Sensitivity
- */
-static int airo_set_sens(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- local->config.rssiThreshold =
- cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
- set_bit (FLAG_COMMIT, &local->flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get Sensitivity
- */
-static int airo_get_sens(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
-
- readConfigRid(local, 1);
- vwrq->value = le16_to_cpu(local->config.rssiThreshold);
- vwrq->disabled = (vwrq->value == 0);
- vwrq->fixed = 1;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : get AP List
- * Note : this is deprecated in favor of IWSCAN
- */
-static int airo_get_aplist(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *local = dev->ml_priv;
- struct sockaddr *address = (struct sockaddr *) extra;
- struct iw_quality *qual;
- BSSListRid BSSList;
- int i;
- int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
-
- qual = kmalloc(IW_MAX_AP * sizeof(*qual), GFP_KERNEL);
- if (!qual)
- return -ENOMEM;
-
- for (i = 0; i < IW_MAX_AP; i++) {
- u16 dBm;
- if (readBSSListRid(local, loseSync, &BSSList))
- break;
- loseSync = 0;
- memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
- address[i].sa_family = ARPHRD_ETHER;
- dBm = le16_to_cpu(BSSList.dBm);
- if (local->rssi) {
- qual[i].level = 0x100 - dBm;
- qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
- qual[i].updated = IW_QUAL_QUAL_UPDATED
- | IW_QUAL_LEVEL_UPDATED
- | IW_QUAL_DBM;
- } else {
- qual[i].level = (dBm + 321) / 2;
- qual[i].qual = 0;
- qual[i].updated = IW_QUAL_QUAL_INVALID
- | IW_QUAL_LEVEL_UPDATED
- | IW_QUAL_DBM;
- }
- qual[i].noise = local->wstats.qual.noise;
- if (BSSList.index == cpu_to_le16(0xffff))
- break;
- }
- if (!i) {
- StatusRid status_rid; /* Card status info */
- readStatusRid(local, &status_rid, 1);
- for (i = 0;
- i < min(IW_MAX_AP, 4) &&
- (status_rid.bssid[i][0]
- & status_rid.bssid[i][1]
- & status_rid.bssid[i][2]
- & status_rid.bssid[i][3]
- & status_rid.bssid[i][4]
- & status_rid.bssid[i][5])!=0xff &&
- (status_rid.bssid[i][0]
- | status_rid.bssid[i][1]
- | status_rid.bssid[i][2]
- | status_rid.bssid[i][3]
- | status_rid.bssid[i][4]
- | status_rid.bssid[i][5]);
- i++) {
- memcpy(address[i].sa_data,
- status_rid.bssid[i], ETH_ALEN);
- address[i].sa_family = ARPHRD_ETHER;
- }
- } else {
- dwrq->flags = 1; /* Should be define'd */
- memcpy(extra + sizeof(struct sockaddr) * i, qual,
- sizeof(struct iw_quality) * i);
- }
- dwrq->length = i;
-
- kfree(qual);
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : Initiate Scan
- */
-static int airo_set_scan(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *ai = dev->ml_priv;
- Cmd cmd;
- Resp rsp;
- int wake = 0;
- APListRid APList_rid_empty;
-
- /* Note : you may have realised that, as this is a SET operation,
- * this is privileged and therefore a normal user can't
- * perform scanning.
- * This is not an error, while the device perform scanning,
- * traffic doesn't flow, so it's a perfect DoS...
- * Jean II */
- if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
-
- if (down_interruptible(&ai->sem))
- return -ERESTARTSYS;
-
- /* If there's already a scan in progress, don't
- * trigger another one. */
- if (ai->scan_timeout > 0)
- goto out;
-
- /* Clear APList as it affects scan results */
- memset(&APList_rid_empty, 0, sizeof(APList_rid_empty));
- APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty));
- disable_MAC(ai, 2);
- writeAPListRid(ai, &APList_rid_empty, 0);
- enable_MAC(ai, 0);
-
- /* Initiate a scan command */
- ai->scan_timeout = RUN_AT(3*HZ);
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd=CMD_LISTBSS;
- issuecommand(ai, &cmd, &rsp);
- wake = 1;
-
-out:
- up(&ai->sem);
- if (wake)
- wake_up_interruptible(&ai->thr_wait);
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Translate scan data returned from the card to a card independent
- * format that the Wireless Tools will understand - Jean II
- */
-static inline char *airo_translate_scan(struct net_device *dev,
- struct iw_request_info *info,
- char *current_ev,
- char *end_buf,
- BSSListRid *bss)
-{
- struct airo_info *ai = dev->ml_priv;
- struct iw_event iwe; /* Temporary buffer */
- __le16 capabilities;
- char * current_val; /* For rates */
- int i;
- char * buf;
- u16 dBm;
-
- /* First entry *MUST* be the AP MAC address */
- iwe.cmd = SIOCGIWAP;
- iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_ADDR_LEN);
-
- /* Other entries will be displayed in the order we give them */
-
- /* Add the ESSID */
- iwe.u.data.length = bss->ssidLen;
- if(iwe.u.data.length > 32)
- iwe.u.data.length = 32;
- iwe.cmd = SIOCGIWESSID;
- iwe.u.data.flags = 1;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, bss->ssid);
-
- /* Add mode */
- iwe.cmd = SIOCGIWMODE;
- capabilities = bss->cap;
- if(capabilities & (CAP_ESS | CAP_IBSS)) {
- if(capabilities & CAP_ESS)
- iwe.u.mode = IW_MODE_MASTER;
- else
- iwe.u.mode = IW_MODE_ADHOC;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_UINT_LEN);
- }
-
- /* Add frequency */
- iwe.cmd = SIOCGIWFREQ;
- iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
- iwe.u.freq.m = 100000 *
- ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ);
- iwe.u.freq.e = 1;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_FREQ_LEN);
-
- dBm = le16_to_cpu(bss->dBm);
-
- /* Add quality statistics */
- iwe.cmd = IWEVQUAL;
- if (ai->rssi) {
- iwe.u.qual.level = 0x100 - dBm;
- iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
- iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
- | IW_QUAL_LEVEL_UPDATED
- | IW_QUAL_DBM;
- } else {
- iwe.u.qual.level = (dBm + 321) / 2;
- iwe.u.qual.qual = 0;
- iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
- | IW_QUAL_LEVEL_UPDATED
- | IW_QUAL_DBM;
- }
- iwe.u.qual.noise = ai->wstats.qual.noise;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_QUAL_LEN);
-
- /* Add encryption capability */
- iwe.cmd = SIOCGIWENCODE;
- if(capabilities & CAP_PRIVACY)
- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- else
- iwe.u.data.flags = IW_ENCODE_DISABLED;
- iwe.u.data.length = 0;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, bss->ssid);
-
- /* Rate : stuffing multiple values in a single event require a bit
- * more of magic - Jean II */
- current_val = current_ev + iwe_stream_lcp_len(info);
-
- iwe.cmd = SIOCGIWRATE;
- /* Those two flags are ignored... */
- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
- /* Max 8 values */
- for(i = 0 ; i < 8 ; i++) {
- /* NULL terminated */
- if(bss->rates[i] == 0)
- break;
- /* Bit rate given in 500 kb/s units (+ 0x80) */
- iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
- /* Add new value to event */
- current_val = iwe_stream_add_value(info, current_ev,
- current_val, end_buf,
- &iwe, IW_EV_PARAM_LEN);
- }
- /* Check if we added any event */
- if ((current_val - current_ev) > iwe_stream_lcp_len(info))
- current_ev = current_val;
-
- /* Beacon interval */
- buf = kmalloc(30, GFP_KERNEL);
- if (buf) {
- iwe.cmd = IWEVCUSTOM;
- sprintf(buf, "bcn_int=%d", bss->beaconInterval);
- iwe.u.data.length = strlen(buf);
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, buf);
- kfree(buf);
- }
-
- /* Put WPA/RSN Information Elements into the event stream */
- if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
- unsigned int num_null_ies = 0;
- u16 length = sizeof (bss->extra.iep);
- u8 *ie = (void *)&bss->extra.iep;
-
- while ((length >= 2) && (num_null_ies < 2)) {
- if (2 + ie[1] > length) {
- /* Invalid element, don't continue parsing IE */
- break;
- }
-
- switch (ie[0]) {
- case WLAN_EID_SSID:
- /* Two zero-length SSID elements
- * mean we're done parsing elements */
- if (!ie[1])
- num_null_ies++;
- break;
-
- case WLAN_EID_VENDOR_SPECIFIC:
- if (ie[1] >= 4 &&
- ie[2] == 0x00 &&
- ie[3] == 0x50 &&
- ie[4] == 0xf2 &&
- ie[5] == 0x01) {
- iwe.cmd = IWEVGENIE;
- /* 64 is an arbitrary cut-off */
- iwe.u.data.length = min(ie[1] + 2,
- 64);
- current_ev = iwe_stream_add_point(
- info, current_ev,
- end_buf, &iwe, ie);
- }
- break;
-
- case WLAN_EID_RSN:
- iwe.cmd = IWEVGENIE;
- /* 64 is an arbitrary cut-off */
- iwe.u.data.length = min(ie[1] + 2, 64);
- current_ev = iwe_stream_add_point(
- info, current_ev, end_buf,
- &iwe, ie);
- break;
-
- default:
- break;
- }
-
- length -= 2 + ie[1];
- ie += 2 + ie[1];
- }
- }
- return current_ev;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wireless Handler : Read Scan Results
- */
-static int airo_get_scan(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct airo_info *ai = dev->ml_priv;
- BSSListElement *net;
- int err = 0;
- char *current_ev = extra;
-
- /* If a scan is in-progress, return -EAGAIN */
- if (ai->scan_timeout > 0)
- return -EAGAIN;
-
- if (down_interruptible(&ai->sem))
- return -EAGAIN;
-
- list_for_each_entry (net, &ai->network_list, list) {
- /* Translate to WE format this entry */
- current_ev = airo_translate_scan(dev, info, current_ev,
- extra + dwrq->length,
- &net->bss);
-
- /* Check if there is space for one more entry */
- if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
- /* Ask user space to try again with a bigger buffer */
- err = -E2BIG;
- goto out;
- }
- }
-
- /* Length of data */
- dwrq->length = (current_ev - extra);
- dwrq->flags = 0; /* todo */
-
-out:
- up(&ai->sem);
- return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Commit handler : called after a bunch of SET operations
- */
-static int airo_config_commit(struct net_device *dev,
- struct iw_request_info *info, /* NULL */
- void *zwrq, /* NULL */
- char *extra) /* NULL */
-{
- struct airo_info *local = dev->ml_priv;
-
- if (!test_bit (FLAG_COMMIT, &local->flags))
- return 0;
-
- /* Some of the "SET" function may have modified some of the
- * parameters. It's now time to commit them in the card */
- disable_MAC(local, 1);
- if (test_bit (FLAG_RESET, &local->flags)) {
- SsidRid SSID_rid;
-
- readSsidRid(local, &SSID_rid);
- if (test_bit(FLAG_MPI,&local->flags))
- setup_card(local, dev->dev_addr, 1 );
- else
- reset_airo_card(dev);
- disable_MAC(local, 1);
- writeSsidRid(local, &SSID_rid, 1);
- writeAPListRid(local, &local->APList, 1);
- }
- if (down_interruptible(&local->sem))
- return -ERESTARTSYS;
- writeConfigRid(local, 0);
- enable_MAC(local, 0);
- if (test_bit (FLAG_RESET, &local->flags))
- airo_set_promisc(local);
- else
- up(&local->sem);
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Structures to export the Wireless Handlers
- */
-
-static const struct iw_priv_args airo_private_args[] = {
-/*{ cmd, set_args, get_args, name } */
- { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
- IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
- { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
-};
-
-static const iw_handler airo_handler[] =
-{
- (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */
- (iw_handler) airo_get_name, /* SIOCGIWNAME */
- (iw_handler) NULL, /* SIOCSIWNWID */
- (iw_handler) NULL, /* SIOCGIWNWID */
- (iw_handler) airo_set_freq, /* SIOCSIWFREQ */
- (iw_handler) airo_get_freq, /* SIOCGIWFREQ */
- (iw_handler) airo_set_mode, /* SIOCSIWMODE */
- (iw_handler) airo_get_mode, /* SIOCGIWMODE */
- (iw_handler) airo_set_sens, /* SIOCSIWSENS */
- (iw_handler) airo_get_sens, /* SIOCGIWSENS */
- (iw_handler) NULL, /* SIOCSIWRANGE */
- (iw_handler) airo_get_range, /* SIOCGIWRANGE */
- (iw_handler) NULL, /* SIOCSIWPRIV */
- (iw_handler) NULL, /* SIOCGIWPRIV */
- (iw_handler) NULL, /* SIOCSIWSTATS */
- (iw_handler) NULL, /* SIOCGIWSTATS */
- iw_handler_set_spy, /* SIOCSIWSPY */
- iw_handler_get_spy, /* SIOCGIWSPY */
- iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
- iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
- (iw_handler) airo_set_wap, /* SIOCSIWAP */
- (iw_handler) airo_get_wap, /* SIOCGIWAP */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */
- (iw_handler) airo_set_scan, /* SIOCSIWSCAN */
- (iw_handler) airo_get_scan, /* SIOCGIWSCAN */
- (iw_handler) airo_set_essid, /* SIOCSIWESSID */
- (iw_handler) airo_get_essid, /* SIOCGIWESSID */
- (iw_handler) airo_set_nick, /* SIOCSIWNICKN */
- (iw_handler) airo_get_nick, /* SIOCGIWNICKN */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) airo_set_rate, /* SIOCSIWRATE */
- (iw_handler) airo_get_rate, /* SIOCGIWRATE */
- (iw_handler) airo_set_rts, /* SIOCSIWRTS */
- (iw_handler) airo_get_rts, /* SIOCGIWRTS */
- (iw_handler) airo_set_frag, /* SIOCSIWFRAG */
- (iw_handler) airo_get_frag, /* SIOCGIWFRAG */
- (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */
- (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */
- (iw_handler) airo_set_retry, /* SIOCSIWRETRY */
- (iw_handler) airo_get_retry, /* SIOCGIWRETRY */
- (iw_handler) airo_set_encode, /* SIOCSIWENCODE */
- (iw_handler) airo_get_encode, /* SIOCGIWENCODE */
- (iw_handler) airo_set_power, /* SIOCSIWPOWER */
- (iw_handler) airo_get_power, /* SIOCGIWPOWER */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* SIOCSIWGENIE */
- (iw_handler) NULL, /* SIOCGIWGENIE */
- (iw_handler) airo_set_auth, /* SIOCSIWAUTH */
- (iw_handler) airo_get_auth, /* SIOCGIWAUTH */
- (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */
- (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */
- (iw_handler) NULL, /* SIOCSIWPMKSA */
-};
-
-/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
- * We want to force the use of the ioctl code, because those can't be
- * won't work the iw_handler code (because they simultaneously read
- * and write data and iw_handler can't do that).
- * Note that it's perfectly legal to read/write on a single ioctl command,
- * you just can't use iwpriv and need to force it via the ioctl handler.
- * Jean II */
-static const iw_handler airo_private_handler[] =
-{
- NULL, /* SIOCIWFIRSTPRIV */
-};
-
-static const struct iw_handler_def airo_handler_def =
-{
- .num_standard = ARRAY_SIZE(airo_handler),
- .num_private = ARRAY_SIZE(airo_private_handler),
- .num_private_args = ARRAY_SIZE(airo_private_args),
- .standard = airo_handler,
- .private = airo_private_handler,
- .private_args = airo_private_args,
- .get_wireless_stats = airo_get_wireless_stats,
-};
-
-/*
- * This defines the configuration part of the Wireless Extensions
- * Note : irq and spinlock protection will occur in the subroutines
- *
- * TODO :
- * o Check input value more carefully and fill correct values in range
- * o Test and shakeout the bugs (if any)
- *
- * Jean II
- *
- * Javier Achirica did a great job of merging code from the unnamed CISCO
- * developer that added support for flashing the card.
- */
-static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- int rc = 0;
- struct airo_info *ai = dev->ml_priv;
-
- if (ai->power.event)
- return 0;
-
- switch (cmd) {
-#ifdef CISCO_EXT
- case AIROIDIFC:
-#ifdef AIROOLDIDIFC
- case AIROOLDIDIFC:
-#endif
- {
- int val = AIROMAGIC;
- aironet_ioctl com;
- if (copy_from_user(&com,rq->ifr_data,sizeof(com)))
- rc = -EFAULT;
- else if (copy_to_user(com.data,(char *)&val,sizeof(val)))
- rc = -EFAULT;
- }
- break;
-
- case AIROIOCTL:
-#ifdef AIROOLDIOCTL
- case AIROOLDIOCTL:
-#endif
- /* Get the command struct and hand it off for evaluation by
- * the proper subfunction
- */
- {
- aironet_ioctl com;
- if (copy_from_user(&com,rq->ifr_data,sizeof(com))) {
- rc = -EFAULT;
- break;
- }
-
- /* Separate R/W functions bracket legality here
- */
- if ( com.command == AIRORSWVERSION ) {
- if (copy_to_user(com.data, swversion, sizeof(swversion)))
- rc = -EFAULT;
- else
- rc = 0;
- }
- else if ( com.command <= AIRORRID)
- rc = readrids(dev,&com);
- else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) )
- rc = writerids(dev,&com);
- else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART )
- rc = flashcard(dev,&com);
- else
- rc = -EINVAL; /* Bad command in ioctl */
- }
- break;
-#endif /* CISCO_EXT */
-
- // All other calls are currently unsupported
- default:
- rc = -EOPNOTSUPP;
- }
- return rc;
-}
-
-/*
- * Get the Wireless stats out of the driver
- * Note : irq and spinlock protection will occur in the subroutines
- *
- * TODO :
- * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
- *
- * Jean
- */
-static void airo_read_wireless_stats(struct airo_info *local)
-{
- StatusRid status_rid;
- StatsRid stats_rid;
- CapabilityRid cap_rid;
- __le32 *vals = stats_rid.vals;
-
- /* Get stats out of the card */
- clear_bit(JOB_WSTATS, &local->jobs);
- if (local->power.event) {
- up(&local->sem);
- return;
- }
- readCapabilityRid(local, &cap_rid, 0);
- readStatusRid(local, &status_rid, 0);
- readStatsRid(local, &stats_rid, RID_STATS, 0);
- up(&local->sem);
-
- /* The status */
- local->wstats.status = le16_to_cpu(status_rid.mode);
-
- /* Signal quality and co */
- if (local->rssi) {
- local->wstats.qual.level =
- airo_rssi_to_dbm(local->rssi,
- le16_to_cpu(status_rid.sigQuality));
- /* normalizedSignalStrength appears to be a percentage */
- local->wstats.qual.qual =
- le16_to_cpu(status_rid.normalizedSignalStrength);
- } else {
- local->wstats.qual.level =
- (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
- local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
- }
- if (le16_to_cpu(status_rid.len) >= 124) {
- local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
- local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
- } else {
- local->wstats.qual.noise = 0;
- local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM;
- }
-
- /* Packets discarded in the wireless adapter due to wireless
- * specific problems */
- local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
- le32_to_cpu(vals[57]) +
- le32_to_cpu(vals[58]); /* SSID Mismatch */
- local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
- local->wstats.discard.fragment = le32_to_cpu(vals[30]);
- local->wstats.discard.retries = le32_to_cpu(vals[10]);
- local->wstats.discard.misc = le32_to_cpu(vals[1]) +
- le32_to_cpu(vals[32]);
- local->wstats.miss.beacon = le32_to_cpu(vals[34]);
-}
-
-static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
-{
- struct airo_info *local = dev->ml_priv;
-
- if (!test_bit(JOB_WSTATS, &local->jobs)) {
- /* Get stats out of the card if available */
- if (down_trylock(&local->sem) != 0) {
- set_bit(JOB_WSTATS, &local->jobs);
- wake_up_interruptible(&local->thr_wait);
- } else
- airo_read_wireless_stats(local);
- }
-
- return &local->wstats;
-}
-
-#ifdef CISCO_EXT
-/*
- * This just translates from driver IOCTL codes to the command codes to
- * feed to the radio's host interface. Things can be added/deleted
- * as needed. This represents the READ side of control I/O to
- * the card
- */
-static int readrids(struct net_device *dev, aironet_ioctl *comp) {
- unsigned short ridcode;
- unsigned char *iobuf;
- int len;
- struct airo_info *ai = dev->ml_priv;
-
- if (test_bit(FLAG_FLASHING, &ai->flags))
- return -EIO;
-
- switch(comp->command)
- {
- case AIROGCAP: ridcode = RID_CAPABILITIES; break;
- case AIROGCFG: ridcode = RID_CONFIG;
- if (test_bit(FLAG_COMMIT, &ai->flags)) {
- disable_MAC (ai, 1);
- writeConfigRid (ai, 1);
- enable_MAC(ai, 1);
- }
- break;
- case AIROGSLIST: ridcode = RID_SSID; break;
- case AIROGVLIST: ridcode = RID_APLIST; break;
- case AIROGDRVNAM: ridcode = RID_DRVNAME; break;
- case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
- case AIROGWEPKTMP: ridcode = RID_WEP_TEMP;
- /* Only super-user can read WEP keys */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- break;
- case AIROGWEPKNV: ridcode = RID_WEP_PERM;
- /* Only super-user can read WEP keys */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- break;
- case AIROGSTAT: ridcode = RID_STATUS; break;
- case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
- case AIROGSTATSC32: ridcode = RID_STATS; break;
- case AIROGMICSTATS:
- if (copy_to_user(comp->data, &ai->micstats,
- min((int)comp->len,(int)sizeof(ai->micstats))))
- return -EFAULT;
- return 0;
- case AIRORRID: ridcode = comp->ridnum; break;
- default:
- return -EINVAL;
- }
-
- if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
- return -ENOMEM;
-
- PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1);
- /* get the count of bytes in the rid docs say 1st 2 bytes is it.
- * then return it to the user
- * 9/22/2000 Honor user given length
- */
- len = comp->len;
-
- if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
- kfree (iobuf);
- return -EFAULT;
- }
- kfree (iobuf);
- return 0;
-}
-
-/*
- * Danger Will Robinson write the rids here
- */
-
-static int writerids(struct net_device *dev, aironet_ioctl *comp) {
- struct airo_info *ai = dev->ml_priv;
- int ridcode;
- int enabled;
- static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
- unsigned char *iobuf;
-
- /* Only super-user can write RIDs */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- if (test_bit(FLAG_FLASHING, &ai->flags))
- return -EIO;
-
- ridcode = 0;
- writer = do_writerid;
-
- switch(comp->command)
- {
- case AIROPSIDS: ridcode = RID_SSID; break;
- case AIROPCAP: ridcode = RID_CAPABILITIES; break;
- case AIROPAPLIST: ridcode = RID_APLIST; break;
- case AIROPCFG: ai->config.len = 0;
- clear_bit(FLAG_COMMIT, &ai->flags);
- ridcode = RID_CONFIG; break;
- case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
- case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
- case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break;
- case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
- break;
- case AIROPLEAPUSR+1: ridcode = 0xFF2A; break;
- case AIROPLEAPUSR+2: ridcode = 0xFF2B; break;
-
- /* this is not really a rid but a command given to the card
- * same with MAC off
- */
- case AIROPMACON:
- if (enable_MAC(ai, 1) != 0)
- return -EIO;
- return 0;
-
- /*
- * Evidently this code in the airo driver does not get a symbol
- * as disable_MAC. it's probably so short the compiler does not gen one.
- */
- case AIROPMACOFF:
- disable_MAC(ai, 1);
- return 0;
-
- /* This command merely clears the counts does not actually store any data
- * only reads rid. But as it changes the cards state, I put it in the
- * writerid routines.
- */
- case AIROPSTCLR:
- if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
- return -ENOMEM;
-
- PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1);
-
- enabled = ai->micstats.enabled;
- memset(&ai->micstats,0,sizeof(ai->micstats));
- ai->micstats.enabled = enabled;
-
- if (copy_to_user(comp->data, iobuf,
- min((int)comp->len, (int)RIDSIZE))) {
- kfree (iobuf);
- return -EFAULT;
- }
- kfree (iobuf);
- return 0;
-
- default:
- return -EOPNOTSUPP; /* Blarg! */
- }
- if(comp->len > RIDSIZE)
- return -EINVAL;
-
- if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
- return -ENOMEM;
-
- if (copy_from_user(iobuf,comp->data,comp->len)) {
- kfree (iobuf);
- return -EFAULT;
- }
-
- if (comp->command == AIROPCFG) {
- ConfigRid *cfg = (ConfigRid *)iobuf;
-
- if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
- cfg->opmode |= MODE_MIC;
-
- if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
- set_bit (FLAG_ADHOC, &ai->flags);
- else
- clear_bit (FLAG_ADHOC, &ai->flags);
- }
-
- if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
- kfree (iobuf);
- return -EIO;
- }
- kfree (iobuf);
- return 0;
-}
-
-/*****************************************************************************
- * Ancillary flash / mod functions much black magic lurkes here *
- *****************************************************************************
- */
-
-/*
- * Flash command switch table
- */
-
-static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
- int z;
-
- /* Only super-user can modify flash */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- switch(comp->command)
- {
- case AIROFLSHRST:
- return cmdreset((struct airo_info *)dev->ml_priv);
-
- case AIROFLSHSTFL:
- if (!AIRO_FLASH(dev) &&
- (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
- return -ENOMEM;
- return setflashmode((struct airo_info *)dev->ml_priv);
-
- case AIROFLSHGCHR: /* Get char from aux */
- if(comp->len != sizeof(int))
- return -EINVAL;
- if (copy_from_user(&z,comp->data,comp->len))
- return -EFAULT;
- return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
-
- case AIROFLSHPCHR: /* Send char to card. */
- if(comp->len != sizeof(int))
- return -EINVAL;
- if (copy_from_user(&z,comp->data,comp->len))
- return -EFAULT;
- return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
-
- case AIROFLPUTBUF: /* Send 32k to card */
- if (!AIRO_FLASH(dev))
- return -ENOMEM;
- if(comp->len > FLASHSIZE)
- return -EINVAL;
- if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
- return -EFAULT;
-
- flashputbuf((struct airo_info *)dev->ml_priv);
- return 0;
-
- case AIRORESTART:
- if (flashrestart((struct airo_info *)dev->ml_priv, dev))
- return -EIO;
- return 0;
- }
- return -EINVAL;
-}
-
-#define FLASH_COMMAND 0x7e7e
-
-/*
- * STEP 1)
- * Disable MAC and do soft reset on
- * card.
- */
-
-static int cmdreset(struct airo_info *ai) {
- disable_MAC(ai, 1);
-
- if(!waitbusy (ai)){
- airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
- return -EBUSY;
- }
-
- OUT4500(ai,COMMAND,CMD_SOFTRESET);
-
- ssleep(1); /* WAS 600 12/7/00 */
-
- if(!waitbusy (ai)){
- airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
- return -EBUSY;
- }
- return 0;
-}
-
-/* STEP 2)
- * Put the card in legendary flash
- * mode
- */
-
-static int setflashmode (struct airo_info *ai) {
- set_bit (FLAG_FLASHING, &ai->flags);
-
- OUT4500(ai, SWS0, FLASH_COMMAND);
- OUT4500(ai, SWS1, FLASH_COMMAND);
- if (probe) {
- OUT4500(ai, SWS0, FLASH_COMMAND);
- OUT4500(ai, COMMAND,0x10);
- } else {
- OUT4500(ai, SWS2, FLASH_COMMAND);
- OUT4500(ai, SWS3, FLASH_COMMAND);
- OUT4500(ai, COMMAND,0);
- }
- msleep(500); /* 500ms delay */
-
- if(!waitbusy(ai)) {
- clear_bit (FLAG_FLASHING, &ai->flags);
- airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
- return -EIO;
- }
- return 0;
-}
-
-/* Put character to SWS0 wait for dwelltime
- * x 50us for echo .
- */
-
-static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
- int echo;
- int waittime;
-
- byte |= 0x8000;
-
- if(dwelltime == 0 )
- dwelltime = 200;
-
- waittime=dwelltime;
-
- /* Wait for busy bit d15 to go false indicating buffer empty */
- while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
- udelay (50);
- waittime -= 50;
- }
-
- /* timeout for busy clear wait */
- if(waittime <= 0 ){
- airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
- return -EBUSY;
- }
-
- /* Port is clear now write byte and wait for it to echo back */
- do {
- OUT4500(ai,SWS0,byte);
- udelay(50);
- dwelltime -= 50;
- echo = IN4500(ai,SWS1);
- } while (dwelltime >= 0 && echo != byte);
-
- OUT4500(ai,SWS1,0);
-
- return (echo == byte) ? 0 : -EIO;
-}
-
-/*
- * Get a character from the card matching matchbyte
- * Step 3)
- */
-static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
- int rchar;
- unsigned char rbyte=0;
-
- do {
- rchar = IN4500(ai,SWS1);
-
- if(dwelltime && !(0x8000 & rchar)){
- dwelltime -= 10;
- mdelay(10);
- continue;
- }
- rbyte = 0xff & rchar;
-
- if( (rbyte == matchbyte) && (0x8000 & rchar) ){
- OUT4500(ai,SWS1,0);
- return 0;
- }
- if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
- break;
- OUT4500(ai,SWS1,0);
-
- }while(dwelltime > 0);
- return -EIO;
-}
-
-/*
- * Transfer 32k of firmware data from user buffer to our buffer and
- * send to the card
- */
-
-static int flashputbuf(struct airo_info *ai){
- int nwords;
-
- /* Write stuff */
- if (test_bit(FLAG_MPI,&ai->flags))
- memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
- else {
- OUT4500(ai,AUXPAGE,0x100);
- OUT4500(ai,AUXOFF,0);
-
- for(nwords=0;nwords != FLASHSIZE / 2;nwords++){
- OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff);
- }
- }
- OUT4500(ai,SWS0,0x8000);
-
- return 0;
-}
-
-/*
- *
- */
-static int flashrestart(struct airo_info *ai,struct net_device *dev){
- int i,status;
-
- ssleep(1); /* Added 12/7/00 */
- clear_bit (FLAG_FLASHING, &ai->flags);
- if (test_bit(FLAG_MPI, &ai->flags)) {
- status = mpi_init_descriptors(ai);
- if (status != SUCCESS)
- return status;
- }
- status = setup_card(ai, dev->dev_addr, 1);
-
- if (!test_bit(FLAG_MPI,&ai->flags))
- for( i = 0; i < MAX_FIDS; i++ ) {
- ai->fids[i] = transmit_allocate
- ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
- }
-
- ssleep(1); /* Added 12/7/00 */
- return status;
-}
-#endif /* CISCO_EXT */
-
-/*
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- In addition:
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote
- products derived from this software without specific prior written
- permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-*/
-
-module_init(airo_init_module);
-module_exit(airo_cleanup_module);
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index ce7826009eeb..44b2470af81d 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -1,13 +1,16 @@
config ATH_COMMON
tristate
-menuconfig ATH_CARDS
- tristate "Atheros Wireless Cards"
- depends on CFG80211 && (!UML || BROKEN)
+config WLAN_VENDOR_ATH
+ bool "Atheros/Qualcomm devices"
+ default y
---help---
- This will enable the support for the Atheros wireless drivers.
- ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
- enables the common ath.ko module which shares common helpers.
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
For more information and documentation on this module you can visit:
@@ -17,7 +20,7 @@ menuconfig ATH_CARDS
http://wireless.kernel.org/en/users/Drivers/Atheros
-if ATH_CARDS
+if WLAN_VENDOR_ATH
config ATH_DEBUG
bool "Atheros wireless debugging"
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index 72acb822bb11..03aa35f999a1 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -2,6 +2,7 @@ config ATH10K
tristate "Atheros 802.11ac wireless cards support"
depends on MAC80211 && HAS_DMA
select ATH_COMMON
+ select CRC32
---help---
This module adds support for wireless adapters based on
Atheros IEEE 802.11ac family of chipsets.
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 0947cc271e69..b41eb3f4ee56 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -59,6 +59,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
.dir = QCA988X_HW_2_0_FW_DIR,
.fw = QCA988X_HW_2_0_FW_FILE,
@@ -95,6 +96,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
.dir = QCA6174_HW_2_1_FW_DIR,
.fw = QCA6174_HW_2_1_FW_FILE,
@@ -113,6 +115,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
.dir = QCA6174_HW_3_0_FW_DIR,
.fw = QCA6174_HW_3_0_FW_FILE,
@@ -131,6 +134,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
/* uses same binaries as hw3.0 */
.dir = QCA6174_HW_3_0_FW_DIR,
@@ -151,6 +155,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.continuous_frag_desc = true,
.channel_counters_freq_hz = 150000,
.max_probe_resp_desc_thres = 24,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE,
.fw = {
.dir = QCA99X0_HW_2_0_FW_DIR,
.fw = QCA99X0_HW_2_0_FW_FILE,
@@ -211,6 +216,7 @@ static const char *const ath10k_core_fw_feature_str[] = {
[ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init",
[ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode",
[ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca",
+ [ATH10K_FW_FEATURE_MFP_SUPPORT] = "mfp",
};
static unsigned int ath10k_core_get_fw_feature_str(char *buf,
@@ -887,7 +893,7 @@ out:
if (!ar->board_data || !ar->board_len) {
ath10k_err(ar,
"failed to fetch board data for %s from %s/%s\n",
- ar->hw_params.fw.dir, boardname, filename);
+ boardname, ar->hw_params.fw.dir, filename);
ret = -ENODATA;
goto err;
}
@@ -1790,9 +1796,11 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
goto err_power_down;
}
+ ath10k_debug_print_hwfw_info(ar);
+
ret = ath10k_core_get_board_id_from_otp(ar);
if (ret && ret != -EOPNOTSUPP) {
- ath10k_err(ar, "failed to get board id from otp for qca99x0: %d\n",
+ ath10k_err(ar, "failed to get board id from otp: %d\n",
ret);
return ret;
}
@@ -1803,6 +1811,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
goto err_free_firmware_files;
}
+ ath10k_debug_print_board_info(ar);
+
ret = ath10k_core_init_firmware_features(ar);
if (ret) {
ath10k_err(ar, "fatal problem with firmware features: %d\n",
@@ -1825,7 +1835,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
goto err_unlock;
}
- ath10k_print_driver_info(ar);
+ ath10k_debug_print_boot_info(ar);
ath10k_core_stop(ar);
mutex_unlock(&ar->conf_mutex);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 858d75f49a9f..7840cf3ef7a6 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -81,26 +81,20 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus)
return "unknown";
}
+enum ath10k_skb_flags {
+ ATH10K_SKB_F_NO_HWCRYPT = BIT(0),
+ ATH10K_SKB_F_DTIM_ZERO = BIT(1),
+ ATH10K_SKB_F_DELIVER_CAB = BIT(2),
+ ATH10K_SKB_F_MGMT = BIT(3),
+ ATH10K_SKB_F_QOS = BIT(4),
+};
+
struct ath10k_skb_cb {
dma_addr_t paddr;
+ u8 flags;
u8 eid;
- u8 vdev_id;
- enum ath10k_hw_txrx_mode txmode;
- bool is_protected;
-
- struct {
- u8 tid;
- u16 freq;
- bool is_offchan;
- bool nohwcrypt;
- struct ath10k_htt_txbuf *txbuf;
- u32 txbuf_paddr;
- } __packed htt;
-
- struct {
- bool dtim_zero;
- bool deliver_cab;
- } bcn;
+ u16 msdu_id;
+ struct ieee80211_vif *vif;
} __packed;
struct ath10k_skb_rxcb {
@@ -151,6 +145,7 @@ struct ath10k_wmi {
struct wmi_vdev_param_map *vdev_param;
struct wmi_pdev_param_map *pdev_param;
const struct wmi_ops *ops;
+ const struct wmi_peer_flags_map *peer_flags;
u32 num_mem_chunks;
u32 rx_decap_mode;
@@ -512,6 +507,9 @@ enum ath10k_fw_features {
/* Firmware Supports Adaptive CCA*/
ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA = 11,
+ /* Firmware supports management frame protection */
+ ATH10K_FW_FEATURE_MFP_SUPPORT = 12,
+
/* keep last */
ATH10K_FW_FEATURE_COUNT,
};
@@ -534,6 +532,9 @@ enum ath10k_dev_flags {
/* Disable HW crypto engine */
ATH10K_FLAG_HW_CRYPTO_DISABLED,
+
+ /* Bluetooth coexistance enabled */
+ ATH10K_FLAG_BTCOEX,
};
enum ath10k_cal_mode {
@@ -662,6 +663,9 @@ struct ath10k {
*/
u32 max_probe_resp_desc_thres;
+ /* The padding bytes's location is different on various chips */
+ enum ath10k_hw_4addr_pad hw_4addr_pad;
+
struct ath10k_hw_params_fw {
const char *dir;
const char *fw;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 6cc1aa3449c8..2bdf5408b0d9 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -19,6 +19,8 @@
#include <linux/debugfs.h>
#include <linux/vmalloc.h>
#include <linux/utsname.h>
+#include <linux/crc32.h>
+#include <linux/firmware.h>
#include "core.h"
#include "debug.h"
@@ -122,28 +124,51 @@ void ath10k_info(struct ath10k *ar, const char *fmt, ...)
}
EXPORT_SYMBOL(ath10k_info);
-void ath10k_print_driver_info(struct ath10k *ar)
+void ath10k_debug_print_hwfw_info(struct ath10k *ar)
{
char fw_features[128] = {};
- char boardinfo[100];
ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));
- if (ar->id.bmi_ids_valid)
- scnprintf(boardinfo, sizeof(boardinfo), "bmi %d:%d",
- ar->id.bmi_chip_id, ar->id.bmi_board_id);
- else
- scnprintf(boardinfo, sizeof(boardinfo), "sub %04x:%04x",
- ar->id.subsystem_vendor, ar->id.subsystem_device);
-
- ath10k_info(ar, "%s (0x%08x, 0x%08x %s) fw %s fwapi %d bdapi %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n",
+ ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x",
ar->hw_params.name,
ar->target_version,
ar->chip_id,
- boardinfo,
+ ar->id.subsystem_vendor, ar->id.subsystem_device);
+
+ ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n",
+ config_enabled(CONFIG_ATH10K_DEBUG),
+ config_enabled(CONFIG_ATH10K_DEBUGFS),
+ config_enabled(CONFIG_ATH10K_TRACING),
+ config_enabled(CONFIG_ATH10K_DFS_CERTIFIED),
+ config_enabled(CONFIG_NL80211_TESTMODE));
+
+ ath10k_info(ar, "firmware ver %s api %d features %s crc32 %08x\n",
ar->hw->wiphy->fw_version,
ar->fw_api,
+ fw_features,
+ crc32_le(0, ar->firmware->data, ar->firmware->size));
+}
+
+void ath10k_debug_print_board_info(struct ath10k *ar)
+{
+ char boardinfo[100];
+
+ if (ar->id.bmi_ids_valid)
+ scnprintf(boardinfo, sizeof(boardinfo), "%d:%d",
+ ar->id.bmi_chip_id, ar->id.bmi_board_id);
+ else
+ scnprintf(boardinfo, sizeof(boardinfo), "N/A");
+
+ ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x",
ar->bd_api,
+ boardinfo,
+ crc32_le(0, ar->board->data, ar->board->size));
+}
+
+void ath10k_debug_print_boot_info(struct ath10k *ar)
+{
+ ath10k_info(ar, "htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d\n",
ar->htt.target_version_major,
ar->htt.target_version_minor,
ar->wmi.op_version,
@@ -151,14 +176,14 @@ void ath10k_print_driver_info(struct ath10k *ar)
ath10k_cal_mode_str(ar->cal_mode),
ar->max_num_stations,
test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags),
- !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags),
- fw_features);
- ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
- config_enabled(CONFIG_ATH10K_DEBUG),
- config_enabled(CONFIG_ATH10K_DEBUGFS),
- config_enabled(CONFIG_ATH10K_TRACING),
- config_enabled(CONFIG_ATH10K_DFS_CERTIFIED),
- config_enabled(CONFIG_NL80211_TESTMODE));
+ !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags));
+}
+
+void ath10k_print_driver_info(struct ath10k *ar)
+{
+ ath10k_debug_print_hwfw_info(ar);
+ ath10k_debug_print_board_info(ar);
+ ath10k_debug_print_boot_info(ar);
}
EXPORT_SYMBOL(ath10k_print_driver_info);
@@ -1114,7 +1139,7 @@ static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,
{
struct ath10k *ar = file->private_data;
char buf[64];
- u8 amsdu = 3, ampdu = 64;
+ u8 amsdu, ampdu;
unsigned int len;
mutex_lock(&ar->conf_mutex);
@@ -2074,6 +2099,121 @@ static const struct file_operations fops_quiet_period = {
.open = simple_open
};
+static ssize_t ath10k_write_btcoex(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ char buf[32];
+ size_t buf_size;
+ bool val;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, ubuf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+
+ if (strtobool(buf, &val) != 0)
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val))
+ goto exit;
+
+ if (val)
+ set_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
+ else
+ clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
+
+ if (ar->state != ATH10K_STATE_ON)
+ goto exit;
+
+ ath10k_info(ar, "restarting firmware due to btcoex change");
+
+ queue_work(ar->workqueue, &ar->restart_work);
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return count;
+}
+
+static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ struct ath10k *ar = file->private_data;
+ int len = 0;
+
+ mutex_lock(&ar->conf_mutex);
+ len = scnprintf(buf, sizeof(buf) - len, "%d\n",
+ test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags));
+ mutex_unlock(&ar->conf_mutex);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_btcoex = {
+ .read = ath10k_read_btcoex,
+ .write = ath10k_write_btcoex,
+ .open = simple_open
+};
+
+static ssize_t ath10k_debug_fw_checksums_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ unsigned int len = 0, buf_len = 4096;
+ ssize_t ret_cnt;
+ char *buf;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (len > buf_len)
+ len = buf_len;
+
+ len += scnprintf(buf + len, buf_len - len,
+ "firmware-N.bin\t\t%08x\n",
+ crc32_le(0, ar->firmware->data, ar->firmware->size));
+ len += scnprintf(buf + len, buf_len - len,
+ "athwlan\t\t\t%08x\n",
+ crc32_le(0, ar->firmware_data, ar->firmware_len));
+ len += scnprintf(buf + len, buf_len - len,
+ "otp\t\t\t%08x\n",
+ crc32_le(0, ar->otp_data, ar->otp_len));
+ len += scnprintf(buf + len, buf_len - len,
+ "codeswap\t\t%08x\n",
+ crc32_le(0, ar->swap.firmware_codeswap_data,
+ ar->swap.firmware_codeswap_len));
+ len += scnprintf(buf + len, buf_len - len,
+ "board-N.bin\t\t%08x\n",
+ crc32_le(0, ar->board->data, ar->board->size));
+ len += scnprintf(buf + len, buf_len - len,
+ "board\t\t\t%08x\n",
+ crc32_le(0, ar->board_data, ar->board_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_checksums = {
+ .read = ath10k_debug_fw_checksums_read,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data));
@@ -2123,8 +2263,8 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_wmi_services);
- debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_simulate_fw_crash);
+ debugfs_create_file("simulate_fw_crash", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_simulate_fw_crash);
debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_fw_crash_dump);
@@ -2141,15 +2281,15 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_chip_id);
- debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_htt_stats_mask);
+ debugfs_create_file("htt_stats_mask", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_htt_stats_mask);
debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar,
&fops_htt_max_amsdu_ampdu);
- debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_fw_dbglog);
+ debugfs_create_file("fw_dbglog", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_fw_dbglog);
debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_cal_data);
@@ -2183,6 +2323,13 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("tpc_stats", S_IRUSR,
ar->debug.debugfs_phy, ar, &fops_tpc_stats);
+ if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
+ debugfs_create_file("btcoex", S_IRUGO | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_btcoex);
+
+ debugfs_create_file("fw_checksums", S_IRUSR,
+ ar->debug.debugfs_phy, ar, &fops_fw_checksums);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 7de780c4ec8d..814719cf4f22 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -63,6 +63,10 @@ extern unsigned int ath10k_debug_mask;
__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
__printf(2, 3) void ath10k_err(struct ath10k *ar, const char *fmt, ...);
__printf(2, 3) void ath10k_warn(struct ath10k *ar, const char *fmt, ...);
+
+void ath10k_debug_print_hwfw_info(struct ath10k *ar);
+void ath10k_debug_print_board_info(struct ath10k *ar);
+void ath10k_debug_print_boot_info(struct ath10k *ar);
void ath10k_print_driver_info(struct ath10k *ar);
#ifdef CONFIG_ATH10K_DEBUGFS
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 2bad50e520b5..47ca048feaf0 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -166,8 +166,13 @@ struct htt_data_tx_desc {
__le16 len;
__le16 id;
__le32 frags_paddr;
- __le16 peerid;
- __le16 freq;
+ union {
+ __le32 peerid;
+ struct {
+ __le16 peerid;
+ __le16 freq;
+ } __packed offchan_tx;
+ } __packed;
u8 prefetch[0]; /* start of frame, for FW classification engine */
} __packed;
@@ -1597,6 +1602,10 @@ void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc);
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
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 *);
+int ath10k_htt_tx(struct ath10k_htt *htt,
+ enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *msdu);
+void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
+ struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 6060dda4e910..91afa3ae414c 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -536,7 +536,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
size = htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring);
- vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_DMA);
+ vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_KERNEL);
if (!vaddr)
goto err_dma_ring;
@@ -545,7 +545,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
vaddr = dma_alloc_coherent(htt->ar->dev,
sizeof(*htt->rx_ring.alloc_idx.vaddr),
- &paddr, GFP_DMA);
+ &paddr, GFP_KERNEL);
if (!vaddr)
goto err_dma_idx;
@@ -674,7 +674,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
rate &= ~RX_PPDU_START_RATE_FLAG;
sband = &ar->mac.sbands[status->band];
- status->rate_idx = ath10k_mac_hw_rate_to_idx(sband, rate);
+ status->rate_idx = ath10k_mac_hw_rate_to_idx(sband, rate, cck);
break;
case HTT_RX_HT:
case HTT_RX_HT_WITH_TXBF:
@@ -1114,7 +1114,20 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
*/
/* pull decapped header and copy SA & DA */
- hdr = (struct ieee80211_hdr *)msdu->data;
+ if ((ar->hw_params.hw_4addr_pad == ATH10K_HW_4ADDR_PAD_BEFORE) &&
+ ieee80211_has_a4(((struct ieee80211_hdr *)first_hdr)->frame_control)) {
+ /* The QCA99X0 4 address mode pad 2 bytes at the
+ * beginning of MSDU
+ */
+ hdr = (struct ieee80211_hdr *)(msdu->data + 2);
+ /* The skb length need be extended 2 as the 2 bytes at the tail
+ * be excluded due to the padding
+ */
+ skb_put(msdu, 2);
+ } else {
+ hdr = (struct ieee80211_hdr *)(msdu->data);
+ }
+
hdr_len = ath10k_htt_rx_nwifi_hdrlen(ar, hdr);
ether_addr_copy(da, ieee80211_get_DA(hdr));
ether_addr_copy(sa, ieee80211_get_SA(hdr));
@@ -2127,6 +2140,18 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
}
EXPORT_SYMBOL(ath10k_htt_t2h_msg_handler);
+void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ struct ath10k_pktlog_10_4_hdr *hdr =
+ (struct ath10k_pktlog_10_4_hdr *)skb->data;
+
+ trace_ath10k_htt_pktlog(ar, hdr->payload,
+ sizeof(*hdr) + __le16_to_cpu(hdr->size));
+ dev_kfree_skb_any(skb);
+}
+EXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler);
+
static void ath10k_htt_txrx_compl_task(unsigned long ptr)
{
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 16823970dbfd..b3adadb5f824 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -111,7 +111,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf);
htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size,
&htt->txbuf.paddr,
- GFP_DMA);
+ GFP_KERNEL);
if (!htt->txbuf.vaddr) {
ath10k_err(ar, "failed to alloc tx buffer\n");
ret = -ENOMEM;
@@ -124,7 +124,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc);
htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size,
&htt->frag_desc.paddr,
- GFP_DMA);
+ GFP_KERNEL);
if (!htt->frag_desc.vaddr) {
ath10k_warn(ar, "failed to alloc fragment desc memory\n");
ret = -ENOMEM;
@@ -439,6 +439,35 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
return 0;
}
+static u8 ath10k_htt_tx_get_vdev_id(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+ struct ath10k_vif *arvif = (void *)cb->vif->drv_priv;
+
+ if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+ return ar->scan.vdev_id;
+ else if (cb->vif)
+ return arvif->vdev_id;
+ else if (ar->monitor_started)
+ return ar->monitor_vdev_id;
+ else
+ return 0;
+}
+
+static u8 ath10k_htt_tx_get_tid(struct sk_buff *skb, bool is_eth)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+ if (!is_eth && ieee80211_is_mgmt(hdr->frame_control))
+ return HTT_DATA_TX_EXT_TID_MGMT;
+ else if (cb->flags & ATH10K_SKB_F_QOS)
+ return skb->priority % IEEE80211_QOS_CTL_TID_MASK;
+ else
+ return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+}
+
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
{
struct ath10k *ar = htt->ar;
@@ -446,7 +475,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
struct sk_buff *txdesc = NULL;
struct htt_cmd *cmd;
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
- u8 vdev_id = skb_cb->vdev_id;
+ u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu);
int len = 0;
int msdu_id = -1;
int res;
@@ -477,6 +506,13 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
msdu_id = res;
+ if ((ieee80211_is_action(hdr->frame_control) ||
+ ieee80211_is_deauth(hdr->frame_control) ||
+ ieee80211_is_disassoc(hdr->frame_control)) &&
+ ieee80211_has_protected(hdr->frame_control)) {
+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
+ }
+
txdesc = ath10k_htc_alloc_skb(ar, len);
if (!txdesc) {
res = -ENOMEM;
@@ -503,8 +539,6 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
memcpy(cmd->mgmt_tx.hdr, msdu->data,
min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
- skb_cb->htt.txbuf = NULL;
-
res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
if (res)
goto err_unmap_msdu;
@@ -525,21 +559,27 @@ err:
return res;
}
-int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *msdu)
{
struct ath10k *ar = htt->ar;
struct device *dev = ar->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
struct ath10k_hif_sg_item sg_items[2];
+ struct ath10k_htt_txbuf *txbuf;
struct htt_data_tx_desc_frag *frags;
- u8 vdev_id = skb_cb->vdev_id;
- u8 tid = skb_cb->htt.tid;
+ bool is_eth = (txmode == ATH10K_HW_TXRX_ETHERNET);
+ u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu);
+ u8 tid = ath10k_htt_tx_get_tid(msdu, is_eth);
int prefetch_len;
int res;
u8 flags0 = 0;
u16 msdu_id, flags1 = 0;
+ u16 freq = 0;
u32 frags_paddr = 0;
+ u32 txbuf_paddr;
struct htt_msdu_ext_desc *ext_desc = NULL;
bool limit_mgmt_desc = false;
bool is_probe_resp = false;
@@ -567,17 +607,17 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
prefetch_len = min(htt->prefetch_len, msdu->len);
prefetch_len = roundup(prefetch_len, 4);
- skb_cb->htt.txbuf = &htt->txbuf.vaddr[msdu_id];
- skb_cb->htt.txbuf_paddr = htt->txbuf.paddr +
- (sizeof(struct ath10k_htt_txbuf) * msdu_id);
+ txbuf = &htt->txbuf.vaddr[msdu_id];
+ txbuf_paddr = htt->txbuf.paddr +
+ (sizeof(struct ath10k_htt_txbuf) * msdu_id);
if ((ieee80211_is_action(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control) ||
ieee80211_is_disassoc(hdr->frame_control)) &&
ieee80211_has_protected(hdr->frame_control)) {
skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
- } else if (!skb_cb->htt.nohwcrypt &&
- skb_cb->txmode == ATH10K_HW_TXRX_RAW &&
+ } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
+ txmode == ATH10K_HW_TXRX_RAW &&
ieee80211_has_protected(hdr->frame_control)) {
skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
}
@@ -590,7 +630,10 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
goto err_free_msdu_id;
}
- switch (skb_cb->txmode) {
+ if (unlikely(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN))
+ freq = ar->scan.roc_freq;
+
+ switch (txmode) {
case ATH10K_HW_TXRX_RAW:
case ATH10K_HW_TXRX_NATIVE_WIFI:
flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
@@ -610,16 +653,16 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
frags_paddr = htt->frag_desc.paddr +
(sizeof(struct htt_msdu_ext_desc) * msdu_id);
} else {
- frags = skb_cb->htt.txbuf->frags;
+ frags = txbuf->frags;
frags[0].dword_addr.paddr =
__cpu_to_le32(skb_cb->paddr);
frags[0].dword_addr.len = __cpu_to_le32(msdu->len);
frags[1].dword_addr.paddr = 0;
frags[1].dword_addr.len = 0;
- frags_paddr = skb_cb->htt.txbuf_paddr;
+ frags_paddr = txbuf_paddr;
}
- flags0 |= SM(skb_cb->txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+ flags0 |= SM(txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
break;
case ATH10K_HW_TXRX_MGMT:
flags0 |= SM(ATH10K_HW_TXRX_MGMT,
@@ -646,17 +689,13 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
* avoid extra memory allocations, compress data structures and thus
* improve performance. */
- skb_cb->htt.txbuf->htc_hdr.eid = htt->eid;
- skb_cb->htt.txbuf->htc_hdr.len = __cpu_to_le16(
- sizeof(skb_cb->htt.txbuf->cmd_hdr) +
- sizeof(skb_cb->htt.txbuf->cmd_tx) +
- prefetch_len);
- skb_cb->htt.txbuf->htc_hdr.flags = 0;
-
- if (skb_cb->htt.nohwcrypt)
- flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+ txbuf->htc_hdr.eid = htt->eid;
+ txbuf->htc_hdr.len = __cpu_to_le16(sizeof(txbuf->cmd_hdr) +
+ sizeof(txbuf->cmd_tx) +
+ prefetch_len);
+ txbuf->htc_hdr.flags = 0;
- if (!skb_cb->is_protected)
+ if (skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT)
flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
@@ -675,20 +714,27 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
*/
flags1 |= HTT_DATA_TX_DESC_FLAGS1_POSTPONED;
- skb_cb->htt.txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
- skb_cb->htt.txbuf->cmd_tx.flags0 = flags0;
- skb_cb->htt.txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1);
- skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
- skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
- skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
- skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID);
- skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq);
+ txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
+ txbuf->cmd_tx.flags0 = flags0;
+ txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1);
+ txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
+ txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
+ txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+ if (ath10k_mac_tx_frm_has_freq(ar)) {
+ txbuf->cmd_tx.offchan_tx.peerid =
+ __cpu_to_le16(HTT_INVALID_PEERID);
+ txbuf->cmd_tx.offchan_tx.freq =
+ __cpu_to_le16(freq);
+ } else {
+ txbuf->cmd_tx.peerid =
+ __cpu_to_le32(HTT_INVALID_PEERID);
+ }
trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid);
ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n",
flags0, flags1, msdu->len, msdu_id, frags_paddr,
- (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq);
+ (u32)skb_cb->paddr, vdev_id, tid, freq);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
msdu->data, msdu->len);
trace_ath10k_tx_hdr(ar, msdu->data, msdu->len);
@@ -696,12 +742,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
sg_items[0].transfer_id = 0;
sg_items[0].transfer_context = NULL;
- sg_items[0].vaddr = &skb_cb->htt.txbuf->htc_hdr;
- sg_items[0].paddr = skb_cb->htt.txbuf_paddr +
- sizeof(skb_cb->htt.txbuf->frags);
- sg_items[0].len = sizeof(skb_cb->htt.txbuf->htc_hdr) +
- sizeof(skb_cb->htt.txbuf->cmd_hdr) +
- sizeof(skb_cb->htt.txbuf->cmd_tx);
+ sg_items[0].vaddr = &txbuf->htc_hdr;
+ sg_items[0].paddr = txbuf_paddr +
+ sizeof(txbuf->frags);
+ sg_items[0].len = sizeof(txbuf->htc_hdr) +
+ sizeof(txbuf->cmd_hdr) +
+ sizeof(txbuf->cmd_tx);
sg_items[1].transfer_id = 0;
sg_items[1].transfer_context = NULL;
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 713c2bcea178..0678831e8671 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -286,6 +286,16 @@ struct ath10k_pktlog_hdr {
u8 payload[0];
} __packed;
+struct ath10k_pktlog_10_4_hdr {
+ __le16 flags;
+ __le16 missed_cnt;
+ __le16 log_type;
+ __le16 size;
+ __le32 timestamp;
+ __le32 type_specific_data;
+ u8 payload[0];
+} __packed;
+
enum ath10k_hw_rate_ofdm {
ATH10K_HW_RATE_OFDM_48M = 0,
ATH10K_HW_RATE_OFDM_24M,
@@ -307,6 +317,11 @@ enum ath10k_hw_rate_cck {
ATH10K_HW_RATE_CCK_SP_2M,
};
+enum ath10k_hw_4addr_pad {
+ ATH10K_HW_4ADDR_PAD_AFTER,
+ ATH10K_HW_4ADDR_PAD_BEFORE,
+};
+
/* Target specific defines for MAIN firmware */
#define TARGET_NUM_VDEVS 8
#define TARGET_NUM_PEER_AST 2
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 95a55405ebf0..6146a293601a 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -90,7 +90,7 @@ static u8 ath10k_mac_bitrate_to_rate(int bitrate)
}
u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
- u8 hw_rate)
+ u8 hw_rate, bool cck)
{
const struct ieee80211_rate *rate;
int i;
@@ -98,6 +98,9 @@ u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
for (i = 0; i < sband->n_bitrates; i++) {
rate = &sband->bitrates[i];
+ if (ath10k_mac_bitrate_is_cck(rate->bitrate) != cck)
+ continue;
+
if (rate->hw_value == hw_rate)
return i;
else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE &&
@@ -247,7 +250,8 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
lockdep_assert_held(&ar->conf_mutex);
if (WARN_ON(arvif->vif->type != NL80211_IFTYPE_AP &&
- arvif->vif->type != NL80211_IFTYPE_ADHOC))
+ arvif->vif->type != NL80211_IFTYPE_ADHOC &&
+ arvif->vif->type != NL80211_IFTYPE_MESH_POINT))
return -EINVAL;
spin_lock_bh(&ar->data_lock);
@@ -1960,7 +1964,7 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
ether_addr_copy(arg->addr, sta->addr);
arg->vdev_id = arvif->vdev_id;
arg->peer_aid = aid;
- arg->peer_flags |= WMI_PEER_AUTH;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->auth;
arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif);
arg->peer_num_spatial_streams = 1;
arg->peer_caps = vif->bss_conf.assoc_capability;
@@ -1968,6 +1972,7 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
struct wmi_peer_assoc_complete_arg *arg)
{
struct ieee80211_bss_conf *info = &vif->bss_conf;
@@ -2002,12 +2007,17 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
/* FIXME: base on RSN IE/WPA IE is a correct idea? */
if (rsnie || wpaie) {
ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
- arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
+ arg->peer_flags |= ar->wmi.peer_flags->need_ptk_4_way;
}
if (wpaie) {
ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
- arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
+ arg->peer_flags |= ar->wmi.peer_flags->need_gtk_2_way;
+ }
+
+ if (sta->mfp &&
+ test_bit(ATH10K_FW_FEATURE_MFP_SUPPORT, ar->fw_features)) {
+ arg->peer_flags |= ar->wmi.peer_flags->pmf;
}
}
@@ -2104,7 +2114,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
return;
- arg->peer_flags |= WMI_PEER_HT;
+ arg->peer_flags |= ar->wmi.peer_flags->ht;
arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
ht_cap->ampdu_factor)) - 1;
@@ -2115,10 +2125,10 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
arg->peer_rate_caps |= WMI_RC_HT_FLAG;
if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
- arg->peer_flags |= WMI_PEER_LDPC;
+ arg->peer_flags |= ar->wmi.peer_flags->ldbc;
if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
- arg->peer_flags |= WMI_PEER_40MHZ;
+ arg->peer_flags |= ar->wmi.peer_flags->bw40;
arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
}
@@ -2132,7 +2142,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
- arg->peer_flags |= WMI_PEER_STBC;
+ arg->peer_flags |= ar->wmi.peer_flags->stbc;
}
if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
@@ -2140,7 +2150,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
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;
+ arg->peer_flags |= ar->wmi.peer_flags->stbc;
}
if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
@@ -2321,10 +2331,10 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
if (ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
return;
- arg->peer_flags |= WMI_PEER_VHT;
+ arg->peer_flags |= ar->wmi.peer_flags->vht;
if (def.chan->band == IEEE80211_BAND_2GHZ)
- arg->peer_flags |= WMI_PEER_VHT_2G;
+ arg->peer_flags |= ar->wmi.peer_flags->vht_2g;
arg->peer_vht_caps = vht_cap->cap;
@@ -2341,7 +2351,7 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
ampdu_factor)) - 1);
if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
- arg->peer_flags |= WMI_PEER_80MHZ;
+ arg->peer_flags |= ar->wmi.peer_flags->bw80;
arg->peer_vht_rates.rx_max_rate =
__le16_to_cpu(vht_cap->vht_mcs.rx_highest);
@@ -2366,27 +2376,28 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_AP:
if (sta->wme)
- arg->peer_flags |= WMI_PEER_QOS;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
if (sta->wme && sta->uapsd_queues) {
- arg->peer_flags |= WMI_PEER_APSD;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->apsd;
arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
}
break;
case WMI_VDEV_TYPE_STA:
if (vif->bss_conf.qos)
- arg->peer_flags |= WMI_PEER_QOS;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
case WMI_VDEV_TYPE_IBSS:
if (sta->wme)
- arg->peer_flags |= WMI_PEER_QOS;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
default:
break;
}
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n",
- sta->addr, !!(arg->peer_flags & WMI_PEER_QOS));
+ sta->addr, !!(arg->peer_flags &
+ arvif->ar->wmi.peer_flags->qos));
}
static bool ath10k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta)
@@ -2479,7 +2490,7 @@ static int ath10k_peer_assoc_prepare(struct ath10k *ar,
memset(arg, 0, sizeof(*arg));
ath10k_peer_assoc_h_basic(ar, vif, sta, arg);
- ath10k_peer_assoc_h_crypto(ar, vif, arg);
+ ath10k_peer_assoc_h_crypto(ar, vif, sta, arg);
ath10k_peer_assoc_h_rates(ar, vif, sta, arg);
ath10k_peer_assoc_h_ht(ar, vif, sta, arg);
ath10k_peer_assoc_h_vht(ar, vif, sta, arg);
@@ -3112,35 +3123,11 @@ void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id,
spin_unlock_bh(&ar->htt.tx_lock);
}
-static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr)
-{
- if (ieee80211_is_mgmt(hdr->frame_control))
- return HTT_DATA_TX_EXT_TID_MGMT;
-
- if (!ieee80211_is_data_qos(hdr->frame_control))
- return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-
- if (!is_unicast_ether_addr(ieee80211_get_DA(hdr)))
- return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-
- return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
-}
-
-static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, struct ieee80211_vif *vif)
-{
- if (vif)
- return ath10k_vif_to_arvif(vif)->vdev_id;
-
- if (ar->monitor_started)
- return ar->monitor_vdev_id;
-
- ath10k_warn(ar, "failed to resolve vdev id\n");
- return 0;
-}
-
static enum ath10k_hw_txrx_mode
-ath10k_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, struct sk_buff *skb)
+ath10k_mac_tx_h_get_txmode(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb)
{
const struct ieee80211_hdr *hdr = (void *)skb->data;
__le16 fc = hdr->frame_control;
@@ -3190,14 +3177,22 @@ ath10k_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif,
}
static bool ath10k_tx_h_use_hwcrypto(struct ieee80211_vif *vif,
- struct sk_buff *skb) {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct sk_buff *skb)
+{
+ const struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ const struct ieee80211_hdr *hdr = (void *)skb->data;
const u32 mask = IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_INJECTED;
+
+ if (!ieee80211_has_protected(hdr->frame_control))
+ return false;
+
if ((info->flags & mask) == mask)
return false;
+
if (vif)
return !ath10k_vif_to_arvif(vif)->nohwcrypt;
+
return true;
}
@@ -3224,7 +3219,7 @@ static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb)
*/
hdr = (void *)skb->data;
if (ieee80211_is_qos_nullfunc(hdr->frame_control))
- cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+ cb->flags &= ~ATH10K_SKB_F_QOS;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
}
@@ -3280,7 +3275,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
}
}
-static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
+bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
{
/* FIXME: Not really sure since when the behaviour changed. At some
* point new firmware stopped requiring creation of peer entries for
@@ -3288,8 +3283,9 @@ static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
* tx credit replenishment and reliability). Assuming it's at least 3.4
* because that's when the `freq` was introduced to TX_FRM HTT command.
*/
- return !(ar->htt.target_version_major >= 3 &&
- ar->htt.target_version_minor >= 4);
+ return (ar->htt.target_version_major >= 3 &&
+ ar->htt.target_version_minor >= 4 &&
+ ar->htt.op_version == ATH10K_FW_HTT_OP_VERSION_TLV);
}
static int ath10k_mac_tx_wmi_mgmt(struct ath10k *ar, struct sk_buff *skb)
@@ -3314,24 +3310,24 @@ unlock:
return ret;
}
-static void ath10k_mac_tx(struct ath10k *ar, struct sk_buff *skb)
+static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *skb)
{
- struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
struct ath10k_htt *htt = &ar->htt;
int ret = 0;
- switch (cb->txmode) {
+ switch (txmode) {
case ATH10K_HW_TXRX_RAW:
case ATH10K_HW_TXRX_NATIVE_WIFI:
case ATH10K_HW_TXRX_ETHERNET:
- ret = ath10k_htt_tx(htt, skb);
+ ret = ath10k_htt_tx(htt, txmode, skb);
break;
case ATH10K_HW_TXRX_MGMT:
if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
ar->fw_features))
ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
else if (ar->htt.target_version_major >= 3)
- ret = ath10k_htt_tx(htt, skb);
+ ret = ath10k_htt_tx(htt, txmode, skb);
else
ret = ath10k_htt_mgmt_tx(htt, skb);
break;
@@ -3361,9 +3357,13 @@ 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 ath10k_vif *arvif;
struct ieee80211_hdr *hdr;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
struct sk_buff *skb;
const u8 *peer_addr;
+ enum ath10k_hw_txrx_mode txmode;
int vdev_id;
int ret;
unsigned long time_left;
@@ -3388,9 +3388,9 @@ void ath10k_offchan_tx_work(struct work_struct *work)
hdr = (struct ieee80211_hdr *)skb->data;
peer_addr = ieee80211_get_DA(hdr);
- vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
spin_lock_bh(&ar->data_lock);
+ vdev_id = ar->scan.vdev_id;
peer = ath10k_peer_find(ar, vdev_id, peer_addr);
spin_unlock_bh(&ar->data_lock);
@@ -3413,7 +3413,22 @@ void ath10k_offchan_tx_work(struct work_struct *work)
ar->offchan_tx_skb = skb;
spin_unlock_bh(&ar->data_lock);
- ath10k_mac_tx(ar, skb);
+ /* It's safe to access vif and sta - conf_mutex guarantees that
+ * sta_state() and remove_interface() are locked exclusively
+ * out wrt to this offchannel worker.
+ */
+ arvif = ath10k_get_arvif(ar, vdev_id);
+ if (arvif) {
+ vif = arvif->vif;
+ sta = ieee80211_find_sta(vif, peer_addr);
+ } else {
+ vif = NULL;
+ sta = NULL;
+ }
+
+ txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+
+ ath10k_mac_tx(ar, txmode, skb);
time_left =
wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
@@ -3488,6 +3503,7 @@ void __ath10k_scan_finish(struct ath10k *ar)
case ATH10K_SCAN_STARTING:
ar->scan.state = ATH10K_SCAN_IDLE;
ar->scan_channel = NULL;
+ ar->scan.roc_freq = 0;
ath10k_offchan_tx_purge(ar);
cancel_delayed_work(&ar->scan.timeout);
complete_all(&ar->scan.completed);
@@ -3631,25 +3647,32 @@ static void ath10k_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct ath10k *ar = hw->priv;
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- __le16 fc = hdr->frame_control;
+ enum ath10k_hw_txrx_mode txmode;
/* We should disable CCK RATE due to P2P */
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
- ATH10K_SKB_CB(skb)->htt.is_offchan = false;
- ATH10K_SKB_CB(skb)->htt.freq = 0;
- ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr);
- ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb);
- ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif);
- ATH10K_SKB_CB(skb)->txmode = ath10k_tx_h_get_txmode(ar, vif, sta, skb);
- ATH10K_SKB_CB(skb)->is_protected = ieee80211_has_protected(fc);
+ txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+
+ skb_cb->flags = 0;
+ if (!ath10k_tx_h_use_hwcrypto(vif, skb))
+ skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
+
+ if (ieee80211_is_mgmt(hdr->frame_control))
+ skb_cb->flags |= ATH10K_SKB_F_MGMT;
- switch (ATH10K_SKB_CB(skb)->txmode) {
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ skb_cb->flags |= ATH10K_SKB_F_QOS;
+
+ skb_cb->vif = vif;
+
+ switch (txmode) {
case ATH10K_HW_TXRX_MGMT:
case ATH10K_HW_TXRX_NATIVE_WIFI:
ath10k_tx_h_nwifi(hw, skb);
@@ -3668,15 +3691,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
}
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
- spin_lock_bh(&ar->data_lock);
- ATH10K_SKB_CB(skb)->htt.freq = ar->scan.roc_freq;
- ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
- spin_unlock_bh(&ar->data_lock);
-
- if (ath10k_mac_need_offchan_tx_work(ar)) {
- ATH10K_SKB_CB(skb)->htt.freq = 0;
- ATH10K_SKB_CB(skb)->htt.is_offchan = true;
-
+ if (!ath10k_mac_tx_frm_has_freq(ar)) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
skb);
@@ -3686,7 +3701,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
}
}
- ath10k_mac_tx(ar, skb);
+ ath10k_mac_tx(ar, txmode, skb);
}
/* Must not be called with conf_mutex held as workers can use that also. */
@@ -3845,7 +3860,8 @@ static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
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;
+ ht_cap.cap |=
+ WLAN_HT_CAP_SM_PS_DISABLED << IEEE80211_HT_CAP_SM_PS_SHIFT;
if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
@@ -4350,7 +4366,9 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
break;
case NL80211_IFTYPE_MESH_POINT:
- if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+ if (test_bit(WMI_SERVICE_MESH, ar->wmi.svc_map)) {
+ arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH;
+ } else if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
ret = -EINVAL;
ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n");
goto err;
@@ -6907,35 +6925,39 @@ void ath10k_mac_destroy(struct ath10k *ar)
static const struct ieee80211_iface_limit ath10k_if_limits[] = {
{
- .max = 8,
- .types = BIT(NL80211_IFTYPE_STATION)
- | BIT(NL80211_IFTYPE_P2P_CLIENT)
+ .max = 8,
+ .types = BIT(NL80211_IFTYPE_STATION)
+ | BIT(NL80211_IFTYPE_P2P_CLIENT)
},
{
- .max = 3,
- .types = BIT(NL80211_IFTYPE_P2P_GO)
+ .max = 3,
+ .types = BIT(NL80211_IFTYPE_P2P_GO)
},
{
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
},
{
- .max = 7,
- .types = BIT(NL80211_IFTYPE_AP)
+ .max = 7,
+ .types = BIT(NL80211_IFTYPE_AP)
#ifdef CONFIG_MAC80211_MESH
- | BIT(NL80211_IFTYPE_MESH_POINT)
+ | BIT(NL80211_IFTYPE_MESH_POINT)
#endif
},
};
static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
{
- .max = 8,
- .types = BIT(NL80211_IFTYPE_AP)
+ .max = 8,
+ .types = BIT(NL80211_IFTYPE_AP)
#ifdef CONFIG_MAC80211_MESH
- | BIT(NL80211_IFTYPE_MESH_POINT)
+ | BIT(NL80211_IFTYPE_MESH_POINT)
#endif
},
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION)
+ },
};
static const struct ieee80211_iface_combination ath10k_if_comb[] = {
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index e3cefe4c7cfd..53091588090d 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -66,7 +66,7 @@ void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id,
enum wmi_tlv_tx_pause_action action);
u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
- u8 hw_rate);
+ u8 hw_rate, bool cck);
u8 ath10k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband,
u32 bitrate);
@@ -74,6 +74,7 @@ void ath10k_mac_tx_lock(struct ath10k *ar, int reason);
void ath10k_mac_tx_unlock(struct ath10k *ar, int reason);
void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
+bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 930785a724e1..ee925c618535 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -108,6 +108,7 @@ static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_pci_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_pci_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state);
static struct ce_attr host_ce_config_wlan[] = {
/* CE0: host->target HTC control and raw streams */
@@ -186,6 +187,7 @@ static struct ce_attr host_ce_config_wlan[] = {
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 128,
+ .recv_cb = ath10k_pci_pktlog_rx_cb,
},
/* CE9 target autonomous qcache memcpy */
@@ -485,6 +487,9 @@ static int ath10k_pci_force_wake(struct ath10k *ar)
unsigned long flags;
int ret = 0;
+ if (ar_pci->pci_ps)
+ return ret;
+
spin_lock_irqsave(&ar_pci->ps_lock, flags);
if (!ar_pci->ps_awake) {
@@ -1215,6 +1220,15 @@ static void ath10k_pci_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
ath10k_pci_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler);
}
+/* Called by lower (CE) layer when data is received from the Target.
+ * Only 10.4 firmware uses separate CE to transfer pktlog data.
+ */
+static void ath10k_pci_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+ ath10k_pci_process_rx_cb(ce_state,
+ ath10k_htt_rx_pktlog_completion_handler);
+}
+
/* Called by lower (CE) layer when a send to HTT Target completes. */
static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state)
{
@@ -2469,12 +2483,10 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
u32 val;
int ret = 0;
- if (ar_pci->pci_ps == 0) {
- ret = ath10k_pci_force_wake(ar);
- if (ret) {
- ath10k_err(ar, "failed to wake up target: %d\n", ret);
- return ret;
- }
+ ret = ath10k_pci_force_wake(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to wake up target: %d\n", ret);
+ return ret;
}
/* Suspend/Resume resets the PCI configuration space, so we have to
@@ -2581,13 +2593,10 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret;
- if (ar_pci->pci_ps == 0) {
- ret = ath10k_pci_force_wake(ar);
- if (ret) {
- ath10k_warn(ar, "failed to wake device up on irq: %d\n",
- ret);
- return IRQ_NONE;
- }
+ ret = ath10k_pci_force_wake(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wake device up on irq: %d\n", ret);
+ return IRQ_NONE;
}
if (ar_pci->num_msi_intrs == 0) {
@@ -3060,17 +3069,15 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_sleep;
}
+ ret = ath10k_pci_force_wake(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wake up device : %d\n", ret);
+ goto err_free_pipes;
+ }
+
ath10k_pci_ce_deinit(ar);
ath10k_pci_irq_disable(ar);
- if (ar_pci->pci_ps == 0) {
- ret = ath10k_pci_force_wake(ar);
- if (ret) {
- ath10k_warn(ar, "failed to wake up device : %d\n", ret);
- goto err_free_pipes;
- }
- }
-
ret = ath10k_pci_init_irq(ar);
if (ret) {
ath10k_err(ar, "failed to init irqs: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c
index 60fe562e3041..444b52c7e4f3 100644
--- a/drivers/net/wireless/ath/ath10k/thermal.c
+++ b/drivers/net/wireless/ath/ath10k/thermal.c
@@ -187,7 +187,7 @@ int ath10k_thermal_register(struct ath10k *ar)
/* Do not register hwmon device when temperature reading is not
* supported by firmware
*/
- if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4)
+ if (!(ar->wmi.ops->gen_pdev_get_temperature))
return 0;
/* Avoid linking error on devm_hwmon_device_register_with_groups, I
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 6d1105ab4592..fbfb608e48ab 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -23,7 +23,12 @@
static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
{
- if (!ATH10K_SKB_CB(skb)->htt.is_offchan)
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (likely(!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)))
+ return;
+
+ if (ath10k_mac_tx_frm_has_freq(ar))
return;
/* If the original wait_for_completion() timed out before
@@ -52,8 +57,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
struct ieee80211_tx_info *info;
struct ath10k_skb_cb *skb_cb;
struct sk_buff *msdu;
- struct ieee80211_hdr *hdr;
- __le16 fc;
bool limit_mgmt_desc = false;
ath10k_dbg(ar, ATH10K_DBG_HTT,
@@ -76,10 +79,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
return;
}
- hdr = (struct ieee80211_hdr *)msdu->data;
- fc = hdr->frame_control;
+ skb_cb = ATH10K_SKB_CB(msdu);
- if (unlikely(ieee80211_is_mgmt(fc)) &&
+ if (unlikely(skb_cb->flags & ATH10K_SKB_F_MGMT) &&
ar->hw_params.max_probe_resp_desc_thres)
limit_mgmt_desc = true;
@@ -89,7 +91,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
wake_up(&htt->empty_tx_wq);
spin_unlock_bh(&htt->tx_lock);
- skb_cb = ATH10K_SKB_CB(msdu);
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
ath10k_report_offchan_tx(htt->ar, msdu);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 6fbd17b69469..3b3a27b859f3 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -3485,6 +3485,24 @@ static const struct wmi_ops wmi_tlv_ops = {
.fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
};
+static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = {
+ .auth = WMI_TLV_PEER_AUTH,
+ .qos = WMI_TLV_PEER_QOS,
+ .need_ptk_4_way = WMI_TLV_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_TLV_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_TLV_PEER_APSD,
+ .ht = WMI_TLV_PEER_HT,
+ .bw40 = WMI_TLV_PEER_40MHZ,
+ .stbc = WMI_TLV_PEER_STBC,
+ .ldbc = WMI_TLV_PEER_LDPC,
+ .dyn_mimops = WMI_TLV_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_TLV_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_TLV_PEER_SPATIAL_MUX,
+ .vht = WMI_TLV_PEER_VHT,
+ .bw80 = WMI_TLV_PEER_80MHZ,
+ .pmf = WMI_TLV_PEER_PMF,
+};
+
/************/
/* TLV init */
/************/
@@ -3495,4 +3513,5 @@ void ath10k_wmi_tlv_attach(struct ath10k *ar)
ar->wmi.vdev_param = &wmi_tlv_vdev_param_map;
ar->wmi.pdev_param = &wmi_tlv_pdev_param_map;
ar->wmi.ops = &wmi_tlv_ops;
+ ar->wmi.peer_flags = &wmi_tlv_peer_flags_map;
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index ad655c44afdb..dd678590531a 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -527,6 +527,24 @@ enum wmi_tlv_vdev_param {
WMI_TLV_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE,
};
+enum wmi_tlv_peer_flags {
+ WMI_TLV_PEER_AUTH = 0x00000001,
+ WMI_TLV_PEER_QOS = 0x00000002,
+ WMI_TLV_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_TLV_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_TLV_PEER_APSD = 0x00000800,
+ WMI_TLV_PEER_HT = 0x00001000,
+ WMI_TLV_PEER_40MHZ = 0x00002000,
+ WMI_TLV_PEER_STBC = 0x00008000,
+ WMI_TLV_PEER_LDPC = 0x00010000,
+ WMI_TLV_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_TLV_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_TLV_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_TLV_PEER_VHT = 0x02000000,
+ WMI_TLV_PEER_80MHZ = 0x04000000,
+ WMI_TLV_PEER_PMF = 0x08000000,
+};
+
enum wmi_tlv_tag {
WMI_TLV_TAG_LAST_RESERVED = 15,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 7569db0f69b5..a7c3d299639b 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1546,6 +1546,61 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
.arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
};
+static const struct wmi_peer_flags_map wmi_peer_flags_map = {
+ .auth = WMI_PEER_AUTH,
+ .qos = WMI_PEER_QOS,
+ .need_ptk_4_way = WMI_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_PEER_APSD,
+ .ht = WMI_PEER_HT,
+ .bw40 = WMI_PEER_40MHZ,
+ .stbc = WMI_PEER_STBC,
+ .ldbc = WMI_PEER_LDPC,
+ .dyn_mimops = WMI_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_PEER_SPATIAL_MUX,
+ .vht = WMI_PEER_VHT,
+ .bw80 = WMI_PEER_80MHZ,
+ .vht_2g = WMI_PEER_VHT_2G,
+ .pmf = WMI_PEER_PMF,
+};
+
+static const struct wmi_peer_flags_map wmi_10x_peer_flags_map = {
+ .auth = WMI_10X_PEER_AUTH,
+ .qos = WMI_10X_PEER_QOS,
+ .need_ptk_4_way = WMI_10X_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_10X_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_10X_PEER_APSD,
+ .ht = WMI_10X_PEER_HT,
+ .bw40 = WMI_10X_PEER_40MHZ,
+ .stbc = WMI_10X_PEER_STBC,
+ .ldbc = WMI_10X_PEER_LDPC,
+ .dyn_mimops = WMI_10X_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_10X_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_10X_PEER_SPATIAL_MUX,
+ .vht = WMI_10X_PEER_VHT,
+ .bw80 = WMI_10X_PEER_80MHZ,
+};
+
+static const struct wmi_peer_flags_map wmi_10_2_peer_flags_map = {
+ .auth = WMI_10_2_PEER_AUTH,
+ .qos = WMI_10_2_PEER_QOS,
+ .need_ptk_4_way = WMI_10_2_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_10_2_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_10_2_PEER_APSD,
+ .ht = WMI_10_2_PEER_HT,
+ .bw40 = WMI_10_2_PEER_40MHZ,
+ .stbc = WMI_10_2_PEER_STBC,
+ .ldbc = WMI_10_2_PEER_LDPC,
+ .dyn_mimops = WMI_10_2_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_10_2_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_10_2_PEER_SPATIAL_MUX,
+ .vht = WMI_10_2_PEER_VHT,
+ .bw80 = WMI_10_2_PEER_80MHZ,
+ .vht_2g = WMI_10_2_PEER_VHT_2G,
+ .pmf = WMI_10_2_PEER_PMF,
+};
+
void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
const struct wmi_channel_arg *arg)
{
@@ -1660,6 +1715,8 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
struct ath10k *ar = arvif->ar;
struct ath10k_skb_cb *cb;
struct sk_buff *bcn;
+ bool dtim_zero;
+ bool deliver_cab;
int ret;
spin_lock_bh(&ar->data_lock);
@@ -1679,12 +1736,14 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
arvif->beacon_state = ATH10K_BEACON_SENDING;
spin_unlock_bh(&ar->data_lock);
+ dtim_zero = !!(cb->flags & ATH10K_SKB_F_DTIM_ZERO);
+ deliver_cab = !!(cb->flags & ATH10K_SKB_F_DELIVER_CAB);
ret = ath10k_wmi_beacon_send_ref_nowait(arvif->ar,
arvif->vdev_id,
bcn->data, bcn->len,
cb->paddr,
- cb->bcn.dtim_zero,
- cb->bcn.deliver_cab);
+ dtim_zero,
+ deliver_cab);
spin_lock_bh(&ar->data_lock);
@@ -1755,16 +1814,24 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
static struct sk_buff *
ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
{
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu);
+ struct ath10k_vif *arvif = (void *)cb->vif->drv_priv;
struct wmi_mgmt_tx_cmd *cmd;
struct ieee80211_hdr *hdr;
struct sk_buff *skb;
int len;
+ u32 vdev_id;
u32 buf_len = msdu->len;
u16 fc;
hdr = (struct ieee80211_hdr *)msdu->data;
fc = le16_to_cpu(hdr->frame_control);
+ if (cb->vif)
+ vdev_id = arvif->vdev_id;
+ else
+ vdev_id = 0;
+
if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
return ERR_PTR(-EINVAL);
@@ -1786,7 +1853,7 @@ ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
cmd = (struct wmi_mgmt_tx_cmd *)skb->data;
- cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id);
+ cmd->hdr.vdev_id = __cpu_to_le32(vdev_id);
cmd->hdr.tx_rate = 0;
cmd->hdr.tx_power = 0;
cmd->hdr.buf_len = __cpu_to_le32(buf_len);
@@ -2204,22 +2271,9 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg(ar, ATH10K_DBG_MGMT,
"event mgmt rx status %08x\n", rx_status);
- if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- if (rx_status & WMI_RX_STATUS_ERR_CRC) {
+ if ((test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) ||
+ (rx_status & (WMI_RX_STATUS_ERR_DECRYPT |
+ WMI_RX_STATUS_ERR_KEY_CACHE_MISS | WMI_RX_STATUS_ERR_CRC))) {
dev_kfree_skb(skb);
return 0;
}
@@ -3115,10 +3169,10 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
if (tim->dtim_count == 0) {
- ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
+ ATH10K_SKB_CB(bcn)->flags |= ATH10K_SKB_F_DTIM_ZERO;
if (__le32_to_cpu(tim_info->tim_mcast) == 1)
- ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
+ ATH10K_SKB_CB(bcn)->flags |= ATH10K_SKB_F_DELIVER_CAB;
}
ath10k_dbg(ar, ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
@@ -4258,34 +4312,58 @@ void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
}
-static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
- u32 num_units, u32 unit_len)
+static int ath10k_wmi_alloc_chunk(struct ath10k *ar, u32 req_id,
+ u32 num_units, u32 unit_len)
{
dma_addr_t paddr;
- u32 pool_size;
+ u32 pool_size = 0;
int idx = ar->wmi.num_mem_chunks;
+ void *vaddr = NULL;
- pool_size = num_units * round_up(unit_len, 4);
+ if (ar->wmi.num_mem_chunks == ARRAY_SIZE(ar->wmi.mem_chunks))
+ return -ENOMEM;
- if (!pool_size)
- return -EINVAL;
+ while (!vaddr && num_units) {
+ pool_size = num_units * round_up(unit_len, 4);
+ if (!pool_size)
+ return -EINVAL;
- ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev,
- pool_size,
- &paddr,
- GFP_KERNEL);
- if (!ar->wmi.mem_chunks[idx].vaddr) {
- ath10k_warn(ar, "failed to allocate memory chunk\n");
- return -ENOMEM;
+ vaddr = kzalloc(pool_size, GFP_KERNEL | __GFP_NOWARN);
+ if (!vaddr)
+ num_units /= 2;
}
- memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size);
+ if (!num_units)
+ return -ENOMEM;
+ paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_TO_DEVICE);
+ if (dma_mapping_error(ar->dev, paddr)) {
+ kfree(vaddr);
+ return -ENOMEM;
+ }
+
+ ar->wmi.mem_chunks[idx].vaddr = vaddr;
ar->wmi.mem_chunks[idx].paddr = paddr;
ar->wmi.mem_chunks[idx].len = pool_size;
ar->wmi.mem_chunks[idx].req_id = req_id;
ar->wmi.num_mem_chunks++;
+ return num_units;
+}
+
+static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+ u32 num_units, u32 unit_len)
+{
+ int ret;
+
+ while (num_units) {
+ ret = ath10k_wmi_alloc_chunk(ar, req_id, num_units, unit_len);
+ if (ret < 0)
+ return ret;
+
+ num_units -= ret;
+ }
+
return 0;
}
@@ -5061,6 +5139,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_10_4_UPDATE_STATS_EVENTID:
ath10k_wmi_event_update_stats(ar, skb);
break;
+ case WMI_10_4_PDEV_TEMPERATURE_EVENTID:
+ ath10k_wmi_event_temperature(ar, skb);
+ break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;
@@ -5431,8 +5512,11 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
cmd = (struct wmi_init_cmd_10_2 *)buf->data;
features = WMI_10_2_RX_BATCH_MODE;
- if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
+
+ if (test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) &&
+ test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
features |= WMI_10_2_COEX_GPIO;
+
cmd->resource_config.feature_mask = __cpu_to_le32(features);
memcpy(&cmd->resource_config.common, &config, sizeof(config));
@@ -6328,6 +6412,16 @@ ath10k_wmi_peer_assoc_fill_10_2(struct ath10k *ar, void *buf,
cmd->info0 = __cpu_to_le32(info0);
}
+static void
+ath10k_wmi_peer_assoc_fill_10_4(struct ath10k *ar, void *buf,
+ const struct wmi_peer_assoc_complete_arg *arg)
+{
+ struct wmi_10_4_peer_assoc_complete_cmd *cmd = buf;
+
+ ath10k_wmi_peer_assoc_fill_10_2(ar, buf, arg);
+ cmd->peer_bw_rxnss_override = 0;
+}
+
static int
ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg)
{
@@ -6417,6 +6511,31 @@ ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar,
}
static struct sk_buff *
+ath10k_wmi_10_4_op_gen_peer_assoc(struct ath10k *ar,
+ const struct wmi_peer_assoc_complete_arg *arg)
+{
+ size_t len = sizeof(struct wmi_10_4_peer_assoc_complete_cmd);
+ struct sk_buff *skb;
+ int ret;
+
+ ret = ath10k_wmi_peer_assoc_check_arg(arg);
+ if (ret)
+ return ERR_PTR(ret);
+
+ skb = ath10k_wmi_alloc_skb(ar, len);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ ath10k_wmi_peer_assoc_fill_10_4(ar, skb->data, arg);
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi peer assoc vdev %d addr %pM (%s)\n",
+ arg->vdev_id, arg->addr,
+ arg->peer_reassoc ? "reassociate" : "new");
+ return skb;
+}
+
+static struct sk_buff *
ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar)
{
struct sk_buff *skb;
@@ -7536,6 +7655,7 @@ static const struct wmi_ops wmi_10_4_ops = {
.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
.gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
+ .gen_peer_assoc = ath10k_wmi_10_4_op_gen_peer_assoc,
.gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
.gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
.gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
@@ -7555,8 +7675,8 @@ static const struct wmi_ops wmi_10_4_ops = {
.fw_stats_fill = ath10k_wmi_10_4_op_fw_stats_fill,
/* shared with 10.2 */
- .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
.gen_request_stats = ath10k_wmi_op_gen_request_stats,
+ .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature,
};
int ath10k_wmi_attach(struct ath10k *ar)
@@ -7567,30 +7687,35 @@ int ath10k_wmi_attach(struct ath10k *ar)
ar->wmi.cmd = &wmi_10_4_cmd_map;
ar->wmi.vdev_param = &wmi_10_4_vdev_param_map;
ar->wmi.pdev_param = &wmi_10_4_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10_2_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_10_2_4:
ar->wmi.cmd = &wmi_10_2_4_cmd_map;
ar->wmi.ops = &wmi_10_2_4_ops;
ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map;
ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10_2_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_10_2:
ar->wmi.cmd = &wmi_10_2_cmd_map;
ar->wmi.ops = &wmi_10_2_ops;
ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10_2_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_10_1:
ar->wmi.cmd = &wmi_10x_cmd_map;
ar->wmi.ops = &wmi_10_1_ops;
ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10x_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_MAIN:
ar->wmi.cmd = &wmi_cmd_map;
ar->wmi.ops = &wmi_ops;
ar->wmi.vdev_param = &wmi_vdev_param_map;
ar->wmi.pdev_param = &wmi_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_TLV:
ath10k_wmi_tlv_attach(ar);
@@ -7616,10 +7741,11 @@ void ath10k_wmi_free_host_mem(struct ath10k *ar)
/* free the host memory chunks requested by firmware */
for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- dma_free_coherent(ar->dev,
- ar->wmi.mem_chunks[i].len,
- ar->wmi.mem_chunks[i].vaddr,
- ar->wmi.mem_chunks[i].paddr);
+ dma_unmap_single(ar->dev,
+ ar->wmi.mem_chunks[i].paddr,
+ ar->wmi.mem_chunks[i].len,
+ DMA_TO_DEVICE);
+ kfree(ar->wmi.mem_chunks[i].vaddr);
}
ar->wmi.num_mem_chunks = 0;
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 72a4ef709577..d85ad7855d20 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -175,6 +175,8 @@ enum wmi_service {
WMI_SERVICE_AUX_SPECTRAL_INTF,
WMI_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_SERVICE_MESH,
/* keep last */
WMI_SERVICE_MAX,
@@ -206,6 +208,11 @@ enum wmi_10x_service {
WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
WMI_10X_SERVICE_ATF,
WMI_10X_SERVICE_COEX_GPIO,
+ WMI_10X_SERVICE_AUX_SPECTRAL_INTF,
+ WMI_10X_SERVICE_AUX_CHAN_LOAD_INTF,
+ WMI_10X_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_10X_SERVICE_MESH,
+ WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT,
};
enum wmi_main_service {
@@ -286,6 +293,8 @@ enum wmi_10_4_service {
WMI_10_4_SERVICE_AUX_SPECTRAL_INTF,
WMI_10_4_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_10_4_SERVICE_MESH,
};
static inline char *wmi_service_name(int service_id)
@@ -375,6 +384,8 @@ static inline char *wmi_service_name(int service_id)
SVCSTR(WMI_SERVICE_AUX_SPECTRAL_INTF);
SVCSTR(WMI_SERVICE_AUX_CHAN_LOAD_INTF);
SVCSTR(WMI_SERVICE_BSS_CHANNEL_INFO_64);
+ SVCSTR(WMI_SERVICE_EXT_RES_CFG_SUPPORT);
+ SVCSTR(WMI_SERVICE_MESH);
default:
return NULL;
}
@@ -442,6 +453,16 @@ static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
WMI_SERVICE_ATF, len);
SVCMAP(WMI_10X_SERVICE_COEX_GPIO,
WMI_SERVICE_COEX_GPIO, len);
+ SVCMAP(WMI_10X_SERVICE_AUX_SPECTRAL_INTF,
+ WMI_SERVICE_AUX_SPECTRAL_INTF, len);
+ SVCMAP(WMI_10X_SERVICE_AUX_CHAN_LOAD_INTF,
+ WMI_SERVICE_AUX_CHAN_LOAD_INTF, len);
+ SVCMAP(WMI_10X_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_SERVICE_BSS_CHANNEL_INFO_64, len);
+ SVCMAP(WMI_10X_SERVICE_MESH,
+ WMI_SERVICE_MESH, len);
+ SVCMAP(WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_SERVICE_EXT_RES_CFG_SUPPORT, len);
}
static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
@@ -600,6 +621,10 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out,
WMI_SERVICE_AUX_CHAN_LOAD_INTF, len);
SVCMAP(WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64,
WMI_SERVICE_BSS_CHANNEL_INFO_64, len);
+ SVCMAP(WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_SERVICE_EXT_RES_CFG_SUPPORT, len);
+ SVCMAP(WMI_10_4_SERVICE_MESH,
+ WMI_SERVICE_MESH, len);
}
#undef SVCMAP
@@ -1576,6 +1601,9 @@ enum wmi_10_4_cmd_id {
WMI_10_4_MU_CAL_START_CMDID,
WMI_10_4_SET_CCA_PARAMS_CMDID,
WMI_10_4_PDEV_BSS_CHAN_INFO_REQUEST_CMDID,
+ WMI_10_4_EXT_RESOURCE_CFG_CMDID,
+ WMI_10_4_VDEV_SET_IE_CMDID,
+ WMI_10_4_SET_LTEU_CONFIG_CMDID,
WMI_10_4_PDEV_UTF_CMDID = WMI_10_4_END_CMDID - 1,
};
@@ -1638,6 +1666,7 @@ enum wmi_10_4_event_id {
WMI_10_4_PDEV_TEMPERATURE_EVENTID,
WMI_10_4_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENTID,
WMI_10_4_PDEV_BSS_CHAN_INFO_EVENTID,
+ WMI_10_4_MU_REPORT_EVENTID,
WMI_10_4_PDEV_UTF_EVENTID = WMI_10_4_END_EVENTID - 1,
};
@@ -3650,6 +3679,12 @@ enum wmi_10_4_pdev_param {
WMI_10_4_PDEV_PARAM_WAPI_MBSSID_OFFSET,
WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+ WMI_10_4_PDEV_PARAM_TXPOWER_DECR_DB,
+ WMI_10_4_PDEV_PARAM_RX_BATCHMODE,
+ WMI_10_4_PDEV_PARAM_PACKET_AGGR_DELAY,
+ WMI_10_4_PDEV_PARAM_ATF_OBSS_NOISE_SCH,
+ WMI_10_4_PDEV_PARAM_ATF_OBSS_NOISE_SCALING_FACTOR,
+ WMI_10_4_PDEV_PARAM_CUST_TXPOWER_SCALE,
};
struct wmi_pdev_set_param_cmd {
@@ -4239,6 +4274,8 @@ enum wmi_vdev_subtype {
WMI_VDEV_SUBTYPE_P2P_DEVICE = 1,
WMI_VDEV_SUBTYPE_P2P_CLIENT = 2,
WMI_VDEV_SUBTYPE_P2P_GO = 3,
+ WMI_VDEV_SUBTYPE_PROXY_STA = 4,
+ WMI_VDEV_SUBTYPE_MESH = 5,
};
/* values for vdev_subtype */
@@ -5641,21 +5678,79 @@ struct wmi_peer_set_q_empty_callback_cmd {
__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_VHT_2G 0x08000000
+struct wmi_peer_flags_map {
+ u32 auth;
+ u32 qos;
+ u32 need_ptk_4_way;
+ u32 need_gtk_2_way;
+ u32 apsd;
+ u32 ht;
+ u32 bw40;
+ u32 stbc;
+ u32 ldbc;
+ u32 dyn_mimops;
+ u32 static_mimops;
+ u32 spatial_mux;
+ u32 vht;
+ u32 bw80;
+ u32 vht_2g;
+ u32 pmf;
+};
+
+enum wmi_peer_flags {
+ WMI_PEER_AUTH = 0x00000001,
+ WMI_PEER_QOS = 0x00000002,
+ WMI_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_PEER_APSD = 0x00000800,
+ WMI_PEER_HT = 0x00001000,
+ WMI_PEER_40MHZ = 0x00002000,
+ WMI_PEER_STBC = 0x00008000,
+ WMI_PEER_LDPC = 0x00010000,
+ WMI_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_PEER_VHT = 0x02000000,
+ WMI_PEER_80MHZ = 0x04000000,
+ WMI_PEER_VHT_2G = 0x08000000,
+ WMI_PEER_PMF = 0x10000000,
+};
+
+enum wmi_10x_peer_flags {
+ WMI_10X_PEER_AUTH = 0x00000001,
+ WMI_10X_PEER_QOS = 0x00000002,
+ WMI_10X_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_10X_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_10X_PEER_APSD = 0x00000800,
+ WMI_10X_PEER_HT = 0x00001000,
+ WMI_10X_PEER_40MHZ = 0x00002000,
+ WMI_10X_PEER_STBC = 0x00008000,
+ WMI_10X_PEER_LDPC = 0x00010000,
+ WMI_10X_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_10X_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_10X_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_10X_PEER_VHT = 0x02000000,
+ WMI_10X_PEER_80MHZ = 0x04000000,
+};
+
+enum wmi_10_2_peer_flags {
+ WMI_10_2_PEER_AUTH = 0x00000001,
+ WMI_10_2_PEER_QOS = 0x00000002,
+ WMI_10_2_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_10_2_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_10_2_PEER_APSD = 0x00000800,
+ WMI_10_2_PEER_HT = 0x00001000,
+ WMI_10_2_PEER_40MHZ = 0x00002000,
+ WMI_10_2_PEER_STBC = 0x00008000,
+ WMI_10_2_PEER_LDPC = 0x00010000,
+ WMI_10_2_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_10_2_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_10_2_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_10_2_PEER_VHT = 0x02000000,
+ WMI_10_2_PEER_80MHZ = 0x04000000,
+ WMI_10_2_PEER_VHT_2G = 0x08000000,
+ WMI_10_2_PEER_PMF = 0x10000000,
+};
/*
* Peer rate capabilities.
@@ -5721,6 +5816,11 @@ struct wmi_10_2_peer_assoc_complete_cmd {
__le32 info0; /* WMI_PEER_ASSOC_INFO0_ */
} __packed;
+struct wmi_10_4_peer_assoc_complete_cmd {
+ struct wmi_10_2_peer_assoc_complete_cmd cmd;
+ __le32 peer_bw_rxnss_override;
+} __packed;
+
struct wmi_peer_assoc_complete_arg {
u8 addr[ETH_ALEN];
u32 vdev_id;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 342563a3706f..3d946d8b2db2 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -767,7 +767,7 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
if (info->flags & IEEE80211_TX_CTL_NO_ACK)
flags |= AR5K_TXDESC_NOACK;
- rc_flags = info->control.rates[0].flags;
+ rc_flags = bf->rates[0].flags;
hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0);
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 81ac8c59f0ec..7f3f94fbf157 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -3930,8 +3930,8 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
- ar->hw.tx_ant = 2;
- ar->hw.rx_ant = 2;
+ ar->hw.tx_ant = 0x3; /* mask, 2 antenna */
+ ar->hw.rx_ant = 0x3;
} else {
ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c
index fffb65b3e652..65c31da43c47 100644
--- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c
+++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c
@@ -2222,8 +2222,9 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
}
if (status) {
- ath6kl_err("failed to get pending recv messages: %d\n",
- status);
+ if (status != -ECANCELED)
+ ath6kl_err("failed to get pending recv messages: %d\n",
+ status);
/* cleanup any packets in sync completion queue */
list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) {
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 6ae0734f86e0..da557dc742e6 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -954,8 +954,10 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name);
ret = request_firmware(&fw, filename, ar->dev);
- if (ret)
+ if (ret) {
+ ath6kl_err("Failed request firmware, rv: %d\n", ret);
return ret;
+ }
data = fw->data;
len = fw->size;
@@ -964,11 +966,15 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1;
if (len < magic_len) {
+ ath6kl_err("Magic length is invalid, len: %zd magic_len: %zd\n",
+ len, magic_len);
ret = -EINVAL;
goto out;
}
if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) {
+ ath6kl_err("Magic is invalid, magic_len: %zd\n",
+ magic_len);
ret = -EINVAL;
goto out;
}
@@ -987,7 +993,12 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
len -= sizeof(*hdr);
data += sizeof(*hdr);
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "ie-id: %d len: %zd (0x%zx)\n",
+ ie_id, ie_len, ie_len);
+
if (len < ie_len) {
+ ath6kl_err("IE len is invalid, len: %zd ie_len: %zd ie-id: %d\n",
+ len, ie_len, ie_id);
ret = -EINVAL;
goto out;
}
@@ -1008,6 +1019,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL);
if (ar->fw_otp == NULL) {
+ ath6kl_err("fw_otp cannot be allocated\n");
ret = -ENOMEM;
goto out;
}
@@ -1025,6 +1037,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
ar->fw = vmalloc(ie_len);
if (ar->fw == NULL) {
+ ath6kl_err("fw storage cannot be allocated, len: %zd\n", ie_len);
ret = -ENOMEM;
goto out;
}
@@ -1039,6 +1052,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL);
if (ar->fw_patch == NULL) {
+ ath6kl_err("fw_patch storage cannot be allocated, len: %zd\n", ie_len);
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index fee0cadb0f5e..40fa915d6f35 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -176,3 +176,14 @@ config ATH9K_HTC_DEBUGFS
depends on ATH9K_HTC && DEBUG_FS
---help---
Say Y, if you need access to ath9k_htc's statistics.
+
+config ATH9K_HWRNG
+ bool "Random number generator support"
+ depends on ATH9K && (HW_RANDOM = y || HW_RANDOM = ATH9K)
+ default y
+ ---help---
+ This option incorporates the ADC register output as a source of
+ randomness into Linux entropy pool (/dev/urandom and /dev/random)
+
+ Say Y, feeds the entropy directly from the WiFi driver to the input
+ pool.
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index ecda613c2d54..76f9dc37500b 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -15,6 +15,7 @@ ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o
ath9k-$(CONFIG_ATH9K_TX99) += tx99.o
ath9k-$(CONFIG_ATH9K_WOW) += wow.o
+ath9k-$(CONFIG_ATH9K_HWRNG) += rng.o
ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b42f4a963ef4..5294595da5a7 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -23,6 +23,7 @@
#include <linux/leds.h>
#include <linux/completion.h>
#include <linux/time.h>
+#include <linux/hw_random.h>
#include "common.h"
#include "debug.h"
@@ -981,6 +982,7 @@ struct ath_softc {
struct ath_offchannel offchannel;
struct ath_chanctx *next_chan;
struct completion go_beacon;
+ struct timespec last_event_time;
#endif
unsigned long driver_data;
@@ -1040,6 +1042,11 @@ struct ath_softc {
u32 wow_intr_before_sleep;
bool force_wow;
#endif
+
+#ifdef CONFIG_ATH9K_HWRNG
+ u32 rng_last;
+ struct task_struct *rng_task;
+#endif
};
/********/
@@ -1062,6 +1069,22 @@ static inline int ath9k_tx99_send(struct ath_softc *sc,
}
#endif /* CONFIG_ATH9K_TX99 */
+/***************************/
+/* Random Number Generator */
+/***************************/
+#ifdef CONFIG_ATH9K_HWRNG
+void ath9k_rng_start(struct ath_softc *sc);
+void ath9k_rng_stop(struct ath_softc *sc);
+#else
+static inline void ath9k_rng_start(struct ath_softc *sc)
+{
+}
+
+static inline void ath9k_rng_stop(struct ath_softc *sc)
+{
+}
+#endif
+
static inline void ath_read_cachesize(struct ath_common *common, int *csz)
{
common->bus_ops->read_cachesize(common, csz);
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index f50a6bc5d06e..5cf0cd7cb2d1 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -148,7 +148,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
ath_assign_seq(common, skb);
- if (vif->p2p)
+ /* Always assign NOA attr when MCC enabled */
+ if (ath9k_is_chanctx_enabled())
ath9k_beacon_add_noa(sc, avp, skb);
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 90f5773a1a61..50e614b915f1 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -226,6 +226,20 @@ static const char *chanctx_state_string(enum ath_chanctx_state state)
}
}
+static const u32 chanctx_event_delta(struct ath_softc *sc)
+{
+ u64 ms;
+ struct timespec ts, *old;
+
+ getrawmonotonic(&ts);
+ old = &sc->last_event_time;
+ ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ ms -= old->tv_sec * 1000 + old->tv_nsec / 1000000;
+ sc->last_event_time = ts;
+
+ return (u32)ms;
+}
+
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -356,14 +370,16 @@ static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
+ unsigned long timeout;
ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
tsf_time -= ath9k_hw_gettsf32(ah);
- tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1;
- mod_timer(&sc->sched.timer, jiffies + tsf_time);
+ timeout = msecs_to_jiffies(tsf_time / 1000) + 1;
+ mod_timer(&sc->sched.timer, jiffies + timeout);
ath_dbg(common, CHAN_CTX,
- "Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time));
+ "Setup chanctx timer with timeout: %d (%d) ms\n",
+ tsf_time / 1000, jiffies_to_msecs(timeout));
}
static void ath_chanctx_handle_bmiss(struct ath_softc *sc,
@@ -403,7 +419,7 @@ static void ath_chanctx_offchannel_noa(struct ath_softc *sc,
avp->offchannel_duration = sc->sched.offchannel_duration;
ath_dbg(common, CHAN_CTX,
- "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
+ "offchannel noa_duration: %d, noa_start: %u, noa_index: %d\n",
avp->offchannel_duration,
avp->offchannel_start,
avp->noa_index);
@@ -443,7 +459,7 @@ static void ath_chanctx_set_periodic_noa(struct ath_softc *sc,
avp->periodic_noa = true;
ath_dbg(common, CHAN_CTX,
- "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ "noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n",
avp->noa_duration,
avp->noa_start,
avp->noa_index,
@@ -464,7 +480,7 @@ static void ath_chanctx_set_oneshot_noa(struct ath_softc *sc,
avp->noa_duration = duration + sc->sched.channel_switch_time;
ath_dbg(common, CHAN_CTX,
- "oneshot noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ "oneshot noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n",
avp->noa_duration,
avp->noa_start,
avp->noa_index,
@@ -487,10 +503,11 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
spin_lock_bh(&sc->chan_lock);
- ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s\n",
+ ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s, delta: %u ms\n",
sc->cur_chan->chandef.center_freq1,
chanctx_event_string(ev),
- chanctx_state_string(sc->sched.state));
+ chanctx_state_string(sc->sched.state),
+ chanctx_event_delta(sc));
switch (ev) {
case ATH_CHANCTX_EVENT_BEACON_PREPARE:
@@ -1099,6 +1116,7 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
nullfunc->frame_control |=
cpu_to_le16(IEEE80211_FCTL_PM);
+ skb->priority = 7;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
dev_kfree_skb_any(skb);
@@ -1401,8 +1419,9 @@ void ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx)
static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
- s32 tsf, target_tsf;
+ u32 tsf, target_tsf;
if (!avp || !avp->noa.has_next_tsf)
return;
@@ -1414,11 +1433,17 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
target_tsf = avp->noa.next_tsf;
if (!avp->noa.absent)
target_tsf -= ATH_P2P_PS_STOP_TIME;
+ else
+ target_tsf += ATH_P2P_PS_STOP_TIME;
if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME)
target_tsf = tsf + ATH_P2P_PS_STOP_TIME;
- ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000);
+ ath_dbg(common, CHAN_CTX, "%s absent %d tsf 0x%08X next_tsf 0x%08X (%dms)\n",
+ __func__, avp->noa.absent, tsf, target_tsf,
+ (target_tsf - tsf) / 1000);
+
+ ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, target_tsf, 1000000);
}
static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
@@ -1433,6 +1458,10 @@ static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
return;
sc->p2p_ps_vif = avp;
+
+ if (sc->ps_flags & PS_BEACON_SYNC)
+ return;
+
tsf = ath9k_hw_gettsf32(sc->sc_ah);
ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
ath9k_update_p2p_ps_timer(sc, avp);
@@ -1495,6 +1524,8 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
noa->index = avp->noa_index;
noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp);
+ if (noa->oppps_ctwindow)
+ noa->oppps_ctwindow |= BIT(7);
if (avp->noa_duration) {
if (avp->periodic_noa) {
@@ -1536,6 +1567,8 @@ void ath9k_p2p_ps_timer(void *priv)
tsf = ath9k_hw_gettsf32(sc->sc_ah);
if (!avp->noa.absent)
tsf += ATH_P2P_PS_STOP_TIME;
+ else
+ tsf -= ATH_P2P_PS_STOP_TIME;
if (!avp->noa.has_next_tsf ||
avp->noa.next_tsf - tsf > BIT(31))
@@ -1571,8 +1604,7 @@ void ath9k_p2p_bss_info_changed(struct ath_softc *sc,
spin_lock_bh(&sc->sc_pcu_lock);
spin_lock_irqsave(&sc->sc_pm_lock, flags);
- if (!(sc->ps_flags & PS_BEACON_SYNC))
- ath9k_update_p2p_ps(sc, vif);
+ ath9k_update_p2p_ps(sc, vif);
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
spin_unlock_bh(&sc->sc_pcu_lock);
}
diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c
index 6ad44470d0f2..01d6d3205a65 100644
--- a/drivers/net/wireless/ath/ath9k/common-beacon.c
+++ b/drivers/net/wireless/ath/ath9k/common-beacon.c
@@ -18,30 +18,16 @@
#define FUDGE 2
-/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */
-static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu)
-{
- u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo;
-
- tsf_mod = tsf & (BIT(10) - 1);
- tsf_hi = tsf >> 32;
- tsf_lo = ((u32) tsf) >> 10;
-
- mod_hi = tsf_hi % div_tu;
- mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu;
-
- return (mod_lo << 10) | tsf_mod;
-}
-
static u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf,
unsigned int interval)
{
- unsigned int offset;
+ unsigned int offset, divisor;
tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time);
- offset = ath9k_mod_tsf64_tu(tsf, interval);
+ divisor = TU_TO_USEC(interval);
+ div_u64_rem(tsf, divisor, &offset);
- return (u32) tsf + TU_TO_USEC(interval) - offset;
+ return (u32) tsf + divisor - offset;
}
/*
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index cc81482c934d..a7afdeee698c 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -138,6 +138,80 @@ bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data)
return ret;
}
+int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size)
+{
+ u16 magic;
+ u16 *eepdata;
+ int i;
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
+ ath_err(common, "Reading Magic # failed\n");
+ return -EIO;
+ }
+
+ if (magic == AR5416_EEPROM_MAGIC) {
+ *swap_needed = false;
+ } else if (swab16(magic) == AR5416_EEPROM_MAGIC) {
+ if (ah->ah_flags & AH_NO_EEP_SWAP) {
+ ath_info(common,
+ "Ignoring endianness difference in EEPROM magic bytes.\n");
+
+ *swap_needed = false;
+ } else {
+ *swap_needed = true;
+ }
+ } else {
+ ath_err(common,
+ "Invalid EEPROM Magic (0x%04x).\n", magic);
+ return -EINVAL;
+ }
+
+ eepdata = (u16 *)(&ah->eeprom);
+
+ if (*swap_needed) {
+ ath_dbg(common, EEPROM,
+ "EEPROM Endianness is not native.. Changing.\n");
+
+ for (i = 0; i < size; i++)
+ eepdata[i] = swab16(eepdata[i]);
+ }
+
+ return 0;
+}
+
+bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size)
+{
+ u32 i, sum = 0;
+ u16 *eepdata = (u16 *)(&ah->eeprom);
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ for (i = 0; i < size; i++)
+ sum ^= eepdata[i];
+
+ if (sum != 0xffff) {
+ ath_err(common, "Bad EEPROM checksum 0x%x\n", sum);
+ return false;
+ }
+
+ return true;
+}
+
+bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (ah->eep_ops->get_eeprom_ver(ah) != version ||
+ ah->eep_ops->get_eeprom_rev(ah) < minrev) {
+ ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n",
+ ah->eep_ops->get_eeprom_ver(ah),
+ ah->eep_ops->get_eeprom_rev(ah));
+ return false;
+ }
+
+ return true;
+}
+
void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
u8 *pVpdList, u16 numIntercepts,
u8 *pRetVpdList)
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index 40d4f62d0f16..4465c6566f20 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -664,6 +664,9 @@ int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight,
bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize,
u16 *indexL, u16 *indexR);
bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data);
+int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size);
+bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size);
+bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev);
void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data,
int eep_start_loc, int size);
void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
index 4773da6dc6f2..5da0826bf1be 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -177,74 +177,30 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
}
#endif
-
-#undef SIZE_EEPROM_4K
-
static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
{
-#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
- struct ath_common *common = ath9k_hw_common(ah);
struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
- u16 *eepdata, temp, magic, magic2;
- u32 sum = 0, el;
- bool need_swap = false;
- int i, addr;
+ u32 el;
+ bool need_swap;
+ int i, err;
-
- if (!ath9k_hw_use_flash(ah)) {
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
- &magic)) {
- ath_err(common, "Reading Magic # failed\n");
- return false;
- }
-
- ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic);
-
- if (magic != AR5416_EEPROM_MAGIC) {
- magic2 = swab16(magic);
-
- if (magic2 == AR5416_EEPROM_MAGIC) {
- need_swap = true;
- eepdata = (u16 *) (&ah->eeprom);
-
- for (addr = 0; addr < EEPROM_4K_SIZE; addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- } else {
- ath_err(common,
- "Invalid EEPROM Magic. Endianness mismatch.\n");
- return -EINVAL;
- }
- }
- }
-
- ath_dbg(common, EEPROM, "need_swap = %s\n",
- need_swap ? "True" : "False");
+ err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_4K);
+ if (err)
+ return err;
if (need_swap)
- el = swab16(ah->eeprom.map4k.baseEepHeader.length);
- else
- el = ah->eeprom.map4k.baseEepHeader.length;
-
- if (el > sizeof(struct ar5416_eeprom_4k))
- el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16);
+ el = swab16(eep->baseEepHeader.length);
else
- el = el / sizeof(u16);
+ el = eep->baseEepHeader.length;
- eepdata = (u16 *)(&ah->eeprom);
-
- for (i = 0; i < el; i++)
- sum ^= *eepdata++;
+ el = min(el / sizeof(u16), SIZE_EEPROM_4K);
+ if (!ath9k_hw_nvram_validate_checksum(ah, el))
+ return -EINVAL;
if (need_swap) {
u32 integer;
u16 word;
- ath_dbg(common, EEPROM,
- "EEPROM Endianness is not native.. Changing\n");
-
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -283,17 +239,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
}
}
- if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
- ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER,
+ AR5416_EEP_NO_BACK_VER))
return -EINVAL;
- }
return 0;
-#undef EEPROM_4K_SIZE
}
+#undef SIZE_EEPROM_4K
+
static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
enum eeprom_param param)
{
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index 6ca33dfde1fd..1a019a39eda1 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -177,59 +177,24 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah)
{
- u32 sum = 0, el, integer;
- u16 temp, word, magic, magic2, *eepdata;
- int i, addr;
- bool need_swap = false;
+ u32 el, integer;
+ u16 word;
+ int i, err;
+ bool need_swap;
struct ar9287_eeprom *eep = &ah->eeprom.map9287;
- struct ath_common *common = ath9k_hw_common(ah);
-
- if (!ath9k_hw_use_flash(ah)) {
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
- &magic)) {
- ath_err(common, "Reading Magic # failed\n");
- return false;
- }
-
- ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic);
-
- if (magic != AR5416_EEPROM_MAGIC) {
- magic2 = swab16(magic);
-
- if (magic2 == AR5416_EEPROM_MAGIC) {
- need_swap = true;
- eepdata = (u16 *)(&ah->eeprom);
-
- for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- } else {
- ath_err(common,
- "Invalid EEPROM Magic. Endianness mismatch.\n");
- return -EINVAL;
- }
- }
- }
- ath_dbg(common, EEPROM, "need_swap = %s\n",
- need_swap ? "True" : "False");
+ err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_AR9287);
+ if (err)
+ return err;
if (need_swap)
- el = swab16(ah->eeprom.map9287.baseEepHeader.length);
- else
- el = ah->eeprom.map9287.baseEepHeader.length;
-
- if (el > sizeof(struct ar9287_eeprom))
- el = sizeof(struct ar9287_eeprom) / sizeof(u16);
+ el = swab16(eep->baseEepHeader.length);
else
- el = el / sizeof(u16);
-
- eepdata = (u16 *)(&ah->eeprom);
+ el = eep->baseEepHeader.length;
- for (i = 0; i < el; i++)
- sum ^= *eepdata++;
+ el = min(el / sizeof(u16), SIZE_EEPROM_AR9287);
+ if (!ath9k_hw_nvram_validate_checksum(ah, el))
+ return -EINVAL;
if (need_swap) {
word = swab16(eep->baseEepHeader.length);
@@ -270,16 +235,15 @@ static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah)
}
}
- if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER
- || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ if (!ath9k_hw_nvram_check_version(ah, AR9287_EEP_VER,
+ AR5416_EEP_NO_BACK_VER))
return -EINVAL;
- }
return 0;
}
+#undef SIZE_EEPROM_AR9287
+
static u32 ath9k_hw_ar9287_get_eeprom(struct ath_hw *ah,
enum eeprom_param param)
{
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 056f516bf017..959682f7909c 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -126,8 +126,6 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah)
return __ath9k_hw_def_fill_eeprom(ah);
}
-#undef SIZE_EEPROM_DEF
-
#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size,
struct modal_eep_header *modal_hdr)
@@ -257,59 +255,31 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
}
#endif
-
static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
{
struct ar5416_eeprom_def *eep = &ah->eeprom.def;
struct ath_common *common = ath9k_hw_common(ah);
- u16 *eepdata, temp, magic;
- u32 sum = 0, el;
- bool need_swap = false;
- int i, addr, size;
-
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
- ath_err(common, "Reading Magic # failed\n");
- return false;
- }
-
- if (swab16(magic) == AR5416_EEPROM_MAGIC &&
- !(ah->ah_flags & AH_NO_EEP_SWAP)) {
- size = sizeof(struct ar5416_eeprom_def);
- need_swap = true;
- eepdata = (u16 *) (&ah->eeprom);
-
- for (addr = 0; addr < size / sizeof(u16); addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- }
+ u32 el;
+ bool need_swap;
+ int i, err;
- ath_dbg(common, EEPROM, "need_swap = %s\n",
- need_swap ? "True" : "False");
+ err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_DEF);
+ if (err)
+ return err;
if (need_swap)
- el = swab16(ah->eeprom.def.baseEepHeader.length);
+ el = swab16(eep->baseEepHeader.length);
else
- el = ah->eeprom.def.baseEepHeader.length;
+ el = eep->baseEepHeader.length;
- if (el > sizeof(struct ar5416_eeprom_def))
- el = sizeof(struct ar5416_eeprom_def) / sizeof(u16);
- else
- el = el / sizeof(u16);
-
- eepdata = (u16 *)(&ah->eeprom);
-
- for (i = 0; i < el; i++)
- sum ^= *eepdata++;
+ el = min(el / sizeof(u16), SIZE_EEPROM_DEF);
+ if (!ath9k_hw_nvram_validate_checksum(ah, el))
+ return -EINVAL;
if (need_swap) {
u32 integer, j;
u16 word;
- ath_dbg(common, EEPROM,
- "EEPROM Endianness is not native.. Changing.\n");
-
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -356,12 +326,9 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
}
}
- if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
- ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER,
+ AR5416_EEP_NO_BACK_VER))
return -EINVAL;
- }
/* Enable fixup for AR_AN_TOP2 if necessary */
if ((ah->hw_version.devid == AR9280_DEVID_PCI) &&
@@ -376,6 +343,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
return 0;
}
+#undef SIZE_EEPROM_DEF
+
static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
enum eeprom_param param)
{
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index a680a970b7f7..fe1fd1a5ae15 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -834,7 +834,7 @@ void ath9k_htc_ani_work(struct work_struct *work)
if (longcal || shortcal)
common->ani.caldone =
ath9k_hw_calibrate(ah, ah->curchan,
- ah->rxchainmask, longcal);
+ ah->rxchainmask, longcal) > 0;
ath9k_htc_ps_restore(priv);
}
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index 2294709ee8b0..fd85f996c554 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -414,7 +414,7 @@ void ath9k_htc_rx_msg(struct htc_target *htc_handle,
return;
}
- if (epid >= ENDPOINT_MAX) {
+ if (epid < 0 || epid >= ENDPOINT_MAX) {
if (pipe_id != USB_REG_IN_PIPE)
dev_kfree_skb_any(skb);
else
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 41382f89abe1..257f46ed4a04 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2299,10 +2299,10 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
else
nextTbtt = bs->bs_nexttbtt;
- ath_dbg(common, BEACON, "next DTIM %d\n", bs->bs_nextdtim);
- ath_dbg(common, BEACON, "next beacon %d\n", nextTbtt);
- ath_dbg(common, BEACON, "beacon period %d\n", beaconintval);
- ath_dbg(common, BEACON, "DTIM period %d\n", dtimperiod);
+ ath_dbg(common, BEACON, "next DTIM %u\n", bs->bs_nextdtim);
+ ath_dbg(common, BEACON, "next beacon %u\n", nextTbtt);
+ ath_dbg(common, BEACON, "beacon period %u\n", beaconintval);
+ ath_dbg(common, BEACON, "DTIM period %u\n", dtimperiod);
ENABLE_REGWRITE_BUFFER(ah);
@@ -2761,9 +2761,6 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits)
ENABLE_REGWRITE_BUFFER(ah);
- if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
- bits |= ATH9K_RX_FILTER_CONTROL_WRAPPER;
-
REG_WRITE(ah, AR_RX_FILTER, bits);
phybits = 0;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 2e2b92ba96b8..ab7a1ac37849 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -828,6 +828,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
ieee80211_hw_set(hw, RX_INCLUDES_FCS);
ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
if (ath9k_ps_enable)
ieee80211_hw_set(hw, SUPPORTS_PS);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index d184e682e636..c1b33fdcca08 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -739,6 +739,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath9k_ps_restore(sc);
+ ath9k_rng_start(sc);
+
return 0;
}
@@ -828,6 +830,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath9k_deinit_channel_context(sc);
+ ath9k_rng_stop(sc);
+
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 994daf6c6297..32160fca876a 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -424,6 +424,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
AR_SREV_9561(sc->sc_ah))
rfilt |= ATH9K_RX_FILTER_4ADDRESS;
+ if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah))
+ rfilt |= ATH9K_RX_FILTER_CONTROL_WRAPPER;
+
if (ath9k_is_chanctx_enabled() &&
test_bit(ATH_OP_SCANNING, &common->op_flags))
rfilt |= ATH9K_RX_FILTER_BEACON;
diff --git a/drivers/net/wireless/ath/ath9k/rng.c b/drivers/net/wireless/ath/ath9k/rng.c
new file mode 100644
index 000000000000..c9cb2aad7b6f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/rng.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 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/hw_random.h>
+#include <linux/kthread.h>
+
+#include "ath9k.h"
+#include "hw.h"
+#include "ar9003_phy.h"
+
+#define ATH9K_RNG_BUF_SIZE 320
+#define ATH9K_RNG_ENTROPY(x) (((x) * 8 * 320) >> 10) /* quality: 320/1024 */
+
+static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size)
+{
+ int i, j;
+ u32 v1, v2, rng_last = sc->rng_last;
+ struct ath_hw *ah = sc->sc_ah;
+
+ ath9k_ps_wakeup(sc);
+
+ REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
+ REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
+ REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
+
+ for (i = 0, j = 0; i < buf_size; i++) {
+ v1 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
+ v2 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
+
+ /* wait for data ready */
+ if (v1 && v2 && rng_last != v1 && v1 != v2 && v1 != 0xffff &&
+ v2 != 0xffff)
+ buf[j++] = (v1 << 16) | v2;
+
+ rng_last = v2;
+ }
+
+ ath9k_ps_restore(sc);
+
+ sc->rng_last = rng_last;
+
+ return j << 2;
+}
+
+static int ath9k_rng_kthread(void *data)
+{
+ int bytes_read;
+ struct ath_softc *sc = data;
+ u32 *rng_buf;
+
+ rng_buf = kmalloc_array(ATH9K_RNG_BUF_SIZE, sizeof(u32), GFP_KERNEL);
+ if (!rng_buf)
+ goto out;
+
+ while (!kthread_should_stop()) {
+ bytes_read = ath9k_rng_data_read(sc, rng_buf,
+ ATH9K_RNG_BUF_SIZE);
+ if (unlikely(!bytes_read)) {
+ msleep_interruptible(10);
+ continue;
+ }
+
+ /* sleep until entropy bits under write_wakeup_threshold */
+ add_hwgenerator_randomness((void *)rng_buf, bytes_read,
+ ATH9K_RNG_ENTROPY(bytes_read));
+ }
+
+ kfree(rng_buf);
+out:
+ sc->rng_task = NULL;
+
+ return 0;
+}
+
+void ath9k_rng_start(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+
+ if (sc->rng_task)
+ return;
+
+ if (!AR_SREV_9300_20_OR_LATER(ah))
+ return;
+
+ sc->rng_task = kthread_run(ath9k_rng_kthread, sc, "ath9k-hwrng");
+ if (IS_ERR(sc->rng_task))
+ sc->rng_task = NULL;
+}
+
+void ath9k_rng_stop(struct ath_softc *sc)
+{
+ if (sc->rng_task)
+ kthread_stop(sc->rng_task);
+}
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 3e3dac3d7060..fe795fc5288c 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1473,11 +1473,14 @@ static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
u16 tid, u16 *ssn)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *txtid;
struct ath_txq *txq;
struct ath_node *an;
u8 density;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
an = (struct ath_node *)sta->drv_priv;
txtid = ATH_AN_2_TID(an, tid);
txq = txtid->txq;
@@ -1512,10 +1515,13 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_node *an = (struct ath_node *)sta->drv_priv;
struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
struct ath_txq *txq = txtid->txq;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
ath_txq_lock(sc, txq);
txtid->active = false;
ath_tx_flush_tid(sc, txtid);
@@ -1526,11 +1532,14 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
struct ath_node *an)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *tid;
struct ath_txq *txq;
bool buffered;
int tidno;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
for (tidno = 0, tid = &an->tid[tidno];
tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
@@ -1555,10 +1564,13 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *tid;
struct ath_txq *txq;
int tidno;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
for (tidno = 0, tid = &an->tid[tidno];
tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
@@ -1579,10 +1591,13 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
u16 tidno)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *tid;
struct ath_node *an;
struct ath_txq *txq;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
an = (struct ath_node *)sta->drv_priv;
tid = ATH_AN_2_TID(an, tidno);
txq = tid->txq;
@@ -2316,6 +2331,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
queue = ieee80211_is_data_present(hdr->frame_control);
+ /* If chanctx, queue all null frames while NOA could be there */
+ if (ath9k_is_chanctx_enabled() &&
+ ieee80211_is_nullfunc(hdr->frame_control) &&
+ !txctl->force_channel)
+ queue = true;
+
/* Force queueing of all frames that belong to a virtual interface on
* a different channel context, to ensure that they are sent on the
* correct channel.
@@ -2894,7 +2915,7 @@ int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb,
if (skb_headroom(skb) < padsize) {
ath_dbg(common, XMIT,
"tx99 padding failed\n");
- return -EINVAL;
+ return -EINVAL;
}
skb_push(skb, padsize);
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
index f8dfa05b290a..8643801f31b6 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.c
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
@@ -474,36 +474,37 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
struct wcn36xx_dxe_desc *dxe = ctl->desc;
dma_addr_t dma_addr;
struct sk_buff *skb;
+ int ret = 0, int_mask;
+ u32 value;
+
+ if (ch->ch_type == WCN36XX_DXE_CH_RX_L) {
+ value = WCN36XX_DXE_CTRL_RX_L;
+ int_mask = WCN36XX_DXE_INT_CH1_MASK;
+ } else {
+ value = WCN36XX_DXE_CTRL_RX_H;
+ int_mask = WCN36XX_DXE_INT_CH3_MASK;
+ }
while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
skb = ctl->skb;
dma_addr = dxe->dst_addr_l;
- wcn36xx_dxe_fill_skb(wcn->dev, ctl);
-
- switch (ch->ch_type) {
- case WCN36XX_DXE_CH_RX_L:
- dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
- wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
- WCN36XX_DXE_INT_CH1_MASK);
- break;
- case WCN36XX_DXE_CH_RX_H:
- dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
- wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
- WCN36XX_DXE_INT_CH3_MASK);
- break;
- default:
- wcn36xx_warn("Unknown channel\n");
- }
-
- dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE,
- DMA_FROM_DEVICE);
- wcn36xx_rx_skb(wcn, skb);
+ ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl);
+ if (0 == ret) {
+ /* new skb allocation ok. Use the new one and queue
+ * the old one to network system.
+ */
+ dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE,
+ DMA_FROM_DEVICE);
+ wcn36xx_rx_skb(wcn, skb);
+ } /* else keep old skb not submitted and use it for rx DMA */
+
+ dxe->ctrl = value;
ctl = ctl->next;
dxe = ctl->desc;
}
+ wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask);
ch->head_blk_ctl = ctl;
-
return 0;
}
diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h
index a1f1127d7808..b947de0fb2e5 100644
--- a/drivers/net/wireless/ath/wcn36xx/hal.h
+++ b/drivers/net/wireless/ath/wcn36xx/hal.h
@@ -345,6 +345,8 @@ enum wcn36xx_hal_host_msg_type {
WCN36XX_HAL_DHCP_START_IND = 189,
WCN36XX_HAL_DHCP_STOP_IND = 190,
+ WCN36XX_HAL_AVOID_FREQ_RANGE_IND = 233,
+
WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE
};
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index c9263e1c75d4..74f56a81ad9a 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -302,6 +302,22 @@ static int wcn36xx_smd_rsp_status_check(void *buf, size_t len)
return 0;
}
+static int wcn36xx_smd_rsp_status_check_v2(struct wcn36xx *wcn, void *buf,
+ size_t len)
+{
+ struct wcn36xx_fw_msg_status_rsp_v2 *rsp;
+
+ if (len < sizeof(struct wcn36xx_hal_msg_header) + sizeof(*rsp))
+ return wcn36xx_smd_rsp_status_check(buf, len);
+
+ rsp = buf + sizeof(struct wcn36xx_hal_msg_header);
+
+ if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status)
+ return rsp->status;
+
+ return 0;
+}
+
int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
{
struct nv_data *nv_d;
@@ -1582,7 +1598,8 @@ int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
wcn36xx_err("Sending hal_remove_bsskey failed\n");
goto out;
}
- ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+ ret = wcn36xx_smd_rsp_status_check_v2(wcn, wcn->hal_buf,
+ wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_remove_bsskey response failed err=%d\n", ret);
goto out;
@@ -1951,7 +1968,8 @@ int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index)
wcn36xx_err("Sending hal_trigger_ba failed\n");
goto out;
}
- ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+ ret = wcn36xx_smd_rsp_status_check_v2(wcn, wcn->hal_buf,
+ wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_trigger_ba response failed err=%d\n", ret);
goto out;
@@ -2128,6 +2146,8 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
complete(&wcn->hal_rsp_compl);
break;
+ case WCN36XX_HAL_COEX_IND:
+ case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
case WCN36XX_HAL_OTA_TX_COMPL_IND:
case WCN36XX_HAL_MISSED_BEACON_IND:
case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
@@ -2174,6 +2194,9 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
switch (msg_header->msg_type) {
+ case WCN36XX_HAL_COEX_IND:
+ case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
+ break;
case WCN36XX_HAL_OTA_TX_COMPL_IND:
wcn36xx_smd_tx_compl_ind(wcn,
hal_ind_msg->msg,
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h
index 008d03423dbf..8361f9e3995b 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.h
+++ b/drivers/net/wireless/ath/wcn36xx/smd.h
@@ -44,6 +44,15 @@ struct wcn36xx_fw_msg_status_rsp {
u32 status;
} __packed;
+/* wcn3620 returns this for tigger_ba */
+
+struct wcn36xx_fw_msg_status_rsp_v2 {
+ u8 bss_id[6];
+ u32 status __packed;
+ u16 count_following_candidates __packed;
+ /* candidate list follows */
+};
+
struct wcn36xx_hal_ind_msg {
struct list_head list;
u8 *msg;
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 50c136e843c4..4f2ffa5c6e17 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -394,9 +394,13 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
wil_fw_core_dump(wil);
wil_notify_fw_error(wil);
isr &= ~ISR_MISC_FW_ERROR;
- wil_fw_error_recovery(wil);
+ if (wil->platform_ops.notify_crash) {
+ wil_err(wil, "notify platform driver about FW crash");
+ wil->platform_ops.notify_crash(wil->platform_handle);
+ } else {
+ wil_fw_error_recovery(wil);
+ }
}
-
if (isr & ISR_MISC_MBOX_EVT) {
wil_dbg_irq(wil, "MBOX event\n");
wmi_recv_cmd(wil);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index bb69a5949aea..b39f0bfc591e 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -401,20 +401,26 @@ void wil_bcast_fini(struct wil6210_priv *wil)
static void wil_connect_worker(struct work_struct *work)
{
- int rc;
+ int rc, cid, ringid;
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
connect_worker);
struct net_device *ndev = wil_to_ndev(wil);
- int cid = wil->pending_connect_cid;
- int ringid = wil_find_free_vring(wil);
+ mutex_lock(&wil->mutex);
+ cid = wil->pending_connect_cid;
if (cid < 0) {
wil_err(wil, "No connection pending\n");
- return;
+ goto out;
+ }
+ ringid = wil_find_free_vring(wil);
+ if (ringid < 0) {
+ wil_err(wil, "No free vring found\n");
+ goto out;
}
- wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
+ wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n",
+ cid, ringid);
rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
wil->pending_connect_cid = -1;
@@ -424,6 +430,8 @@ static void wil_connect_worker(struct work_struct *work)
} else {
wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true);
}
+out:
+ mutex_unlock(&wil->mutex);
}
int wil_priv_init(struct wil6210_priv *wil)
@@ -773,8 +781,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
wil_bcast_fini(wil);
- /* prevent NAPI from being scheduled */
+ /* prevent NAPI from being scheduled and prevent wmi commands */
+ mutex_lock(&wil->wmi_mutex);
bitmap_zero(wil->status, wil_status_last);
+ mutex_unlock(&wil->wmi_mutex);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
@@ -977,7 +987,7 @@ int __wil_down(struct wil6210_priv *wil)
}
mutex_lock(&wil->mutex);
- if (!iter)
+ if (iter < 0)
wil_err(wil, "timeout waiting for idle FW/HW\n");
wil_reset(wil, false);
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index e3b3c8fb4605..56aaa2d4fb0e 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -183,7 +183,7 @@ void *wil_if_alloc(struct device *dev)
netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
WIL6210_NAPI_BUDGET);
- netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+ netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
WIL6210_NAPI_BUDGET);
netif_tx_stop_all_queues(ndev);
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 1a3142c332e1..e36f2a0c8cb6 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2015 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
@@ -125,11 +125,37 @@ static int wil_if_pcie_disable(struct wil6210_priv *wil)
return 0;
}
+static int wil_platform_rop_ramdump(void *wil_handle, void *buf, uint32_t size)
+{
+ struct wil6210_priv *wil = wil_handle;
+
+ if (!wil)
+ return -EINVAL;
+
+ return wil_fw_copy_crash_dump(wil, buf, size);
+}
+
+static int wil_platform_rop_fw_recovery(void *wil_handle)
+{
+ struct wil6210_priv *wil = wil_handle;
+
+ if (!wil)
+ return -EINVAL;
+
+ wil_fw_error_recovery(wil);
+
+ return 0;
+}
+
static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct wil6210_priv *wil;
struct device *dev = &pdev->dev;
int rc;
+ const struct wil_platform_rops rops = {
+ .ramdump = wil_platform_rop_ramdump,
+ .fw_recovery = wil_platform_rop_fw_recovery,
+ };
/* check HW */
dev_info(&pdev->dev, WIL_NAME
@@ -154,7 +180,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* rollback to if_free */
wil->platform_handle =
- wil_platform_init(&pdev->dev, &wil->platform_ops);
+ wil_platform_init(&pdev->dev, &wil->platform_ops, &rops, wil);
if (!wil->platform_handle) {
rc = -ENODEV;
wil_err(wil, "wil_platform_init failed\n");
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index e3d1be82f314..32031e7a11d5 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -261,9 +261,19 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
struct wil_tid_ampdu_rx *r)
{
+ int i;
+
if (!r)
return;
- wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size);
+
+ /* Do not pass remaining frames to the network stack - it may be
+ * not expecting to get any more Rx. Rx from here may lead to
+ * kernel OOPS since some per-socket accounting info was already
+ * released.
+ */
+ for (i = 0; i < r->buf_size; i++)
+ kfree_skb(r->reorder_buf[i]);
+
kfree(r->reorder_buf);
kfree(r->reorder_time);
kfree(r);
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 3bc9bc0efbac..7887e6cfd817 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -160,6 +160,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
struct device *dev = wil_to_dev(wil);
size_t sz = vring->size * sizeof(vring->va[0]);
+ lockdep_assert_held(&wil->mutex);
if (tx) {
int vring_index = vring - wil->vring_tx;
@@ -749,6 +750,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+ lockdep_assert_held(&wil->mutex);
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -821,6 +823,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+ lockdep_assert_held(&wil->mutex);
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -872,7 +875,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
struct vring *vring = &wil->vring_tx[id];
struct vring_tx_data *txdata = &wil->vring_tx_data[id];
- WARN_ON(!mutex_is_locked(&wil->mutex));
+ lockdep_assert_held(&wil->mutex);
if (!vring->va)
return;
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index ade5f3b8274b..235e205ce2bc 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -828,6 +828,7 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_resume(struct wil6210_priv *wil, bool is_runtime);
+int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size);
void wil_fw_core_dump(struct wil6210_priv *wil);
#endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
index 7e70934990ae..b57d280946e0 100644
--- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
+++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
@@ -51,8 +51,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil,
return 0;
}
-static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest,
- u32 size)
+int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
{
int i;
const struct fw_map *map;
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c
index 2e831bf20117..4eed05bddb60 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.c
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.c
@@ -33,7 +33,8 @@ void wil_platform_modexit(void)
* It returns a handle which is used with the rest of the API
*
*/
-void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops)
+void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops,
+ const struct wil_platform_rops *rops, void *wil_handle)
{
void *handle = ops; /* to return some non-NULL for 'void' impl. */
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h
index d7fa19b7886d..9a949d910343 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.h
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2015 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
@@ -20,16 +20,48 @@
struct device;
/**
- * struct wil_platform_ops - wil platform module callbacks
+ * struct wil_platform_ops - wil platform module calls from this
+ * driver to platform driver
*/
struct wil_platform_ops {
int (*bus_request)(void *handle, uint32_t kbps /* KBytes/Sec */);
int (*suspend)(void *handle);
int (*resume)(void *handle);
void (*uninit)(void *handle);
+ int (*notify_crash)(void *handle);
};
-void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops);
+/**
+ * struct wil_platform_rops - wil platform module callbacks from
+ * platform driver to this driver
+ * @ramdump: store a ramdump from the wil firmware. The platform
+ * driver may add additional data to the ramdump to
+ * generate the final crash dump.
+ * @fw_recovery: start a firmware recovery process. Called as
+ * part of a crash recovery process which may include other
+ * related platform subsystems.
+ */
+struct wil_platform_rops {
+ int (*ramdump)(void *wil_handle, void *buf, uint32_t size);
+ int (*fw_recovery)(void *wil_handle);
+};
+
+/**
+ * wil_platform_init - initialize the platform driver
+ *
+ * @dev - pointer to the wil6210 device
+ * @ops - structure with platform driver operations. Platform
+ * driver will fill this structure with function pointers.
+ * @rops - structure with callbacks from platform driver to
+ * this driver. The platform driver copies the structure to
+ * its own storage. Can be NULL if this driver does not
+ * support crash recovery.
+ * @wil_handle - context for this driver that will be passed
+ * when platform driver invokes one of the callbacks in
+ * rops. May be NULL if rops is NULL.
+ */
+void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops,
+ const struct wil_platform_rops *rops, void *wil_handle);
int __init wil_platform_modinit(void);
void wil_platform_modexit(void);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 6ed26baca0e5..e3ea74cdd4aa 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -228,6 +228,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
/* wait till FW finish with previous command */
for (retry = 5; retry > 0; retry--) {
+ if (!test_bit(wil_status_fwready, wil->status)) {
+ wil_err(wil, "WMI: cannot send command while FW not ready\n");
+ return -EAGAIN;
+ }
r->tail = wil_r(wil, RGF_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.tail));
if (next_head != r->tail)
diff --git a/drivers/net/wireless/atmel/Kconfig b/drivers/net/wireless/atmel/Kconfig
new file mode 100644
index 000000000000..a43cfd163254
--- /dev/null
+++ b/drivers/net/wireless/atmel/Kconfig
@@ -0,0 +1,57 @@
+config WLAN_VENDOR_ATMEL
+ bool "Atmel devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ATMEL
+
+config ATMEL
+ tristate "Atmel at76c50x chipset 802.11b support"
+ depends on CFG80211 && (PCI || PCMCIA)
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select FW_LOADER
+ select CRC32
+ ---help---
+ A driver 802.11b wireless cards based on the Atmel fast-vnet
+ chips. This driver supports standard Linux wireless extensions.
+
+ Many cards based on this chipset do not have flash memory
+ and need their firmware loaded at start-up. If yours is
+ one of these, you will need to provide a firmware image
+ to be loaded into the card by the driver. The Atmel
+ firmware package can be downloaded from
+ <http://www.thekelleys.org.uk/atmel>
+
+config PCI_ATMEL
+ tristate "Atmel at76c506 PCI cards"
+ depends on ATMEL && PCI
+ ---help---
+ Enable support for PCI and mini-PCI cards containing the
+ Atmel at76c506 chip.
+
+config PCMCIA_ATMEL
+ tristate "Atmel at76c502/at76c504 PCMCIA cards"
+ depends on ATMEL && PCMCIA
+ select WIRELESS_EXT
+ select FW_LOADER
+ select CRC32
+ ---help---
+ Enable support for PCMCIA cards containing the
+ Atmel at76c502 and at76c504 chips.
+
+config AT76C50X_USB
+ tristate "Atmel at76c503/at76c505/at76c505a USB cards"
+ depends on MAC80211 && USB
+ select FW_LOADER
+ ---help---
+ Enable support for USB Wireless devices using Atmel at76c503,
+ at76c505 or at76c505a chips.
+
+endif # WLAN_VENDOR_ATMEL
diff --git a/drivers/net/wireless/atmel/Makefile b/drivers/net/wireless/atmel/Makefile
new file mode 100644
index 000000000000..e62e345f7af6
--- /dev/null
+++ b/drivers/net/wireless/atmel/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_ATMEL) += atmel.o
+obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o
+obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
+
+obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c
index dab25136214a..dab25136214a 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/atmel/at76c50x-usb.c
diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/atmel/at76c50x-usb.h
index ae03271f878e..ae03271f878e 100644
--- a/drivers/net/wireless/at76c50x-usb.h
+++ b/drivers/net/wireless/atmel/at76c50x-usb.h
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel/atmel.c
index 6a1f03c271c1..6a1f03c271c1 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel/atmel.c
diff --git a/drivers/net/wireless/atmel.h b/drivers/net/wireless/atmel/atmel.h
index 96f7318cbb04..96f7318cbb04 100644
--- a/drivers/net/wireless/atmel.h
+++ b/drivers/net/wireless/atmel/atmel.h
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel/atmel_cs.c
index 7afc9c5329fb..7afc9c5329fb 100644
--- a/drivers/net/wireless/atmel_cs.c
+++ b/drivers/net/wireless/atmel/atmel_cs.c
diff --git a/drivers/net/wireless/atmel_pci.c b/drivers/net/wireless/atmel/atmel_pci.c
index bcf1f274a251..bcf1f274a251 100644
--- a/drivers/net/wireless/atmel_pci.c
+++ b/drivers/net/wireless/atmel/atmel_pci.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
deleted file mode 100644
index dc4c75083085..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Makefile fragment for Broadcom 802.11n Networking Device Driver
-#
-# Copyright (c) 2010 Broadcom Corporation
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-ccflags-y += \
- -Idrivers/net/wireless/brcm80211/brcmfmac \
- -Idrivers/net/wireless/brcm80211/include
-
-ccflags-y += -D__CHECK_ENDIAN__
-
-obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
-brcmfmac-objs += \
- cfg80211.o \
- chip.o \
- fwil.o \
- fweh.o \
- fwsignal.o \
- p2p.o \
- proto.o \
- common.o \
- core.o \
- firmware.o \
- feature.o \
- btcoex.o \
- vendor.o
-brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \
- bcdc.o
-brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \
- commonring.o \
- flowring.o \
- msgbuf.o
-brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
- sdio.o \
- bcmsdh.o
-brcmfmac-$(CONFIG_BRCMFMAC_USB) += \
- usb.o
-brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \
- pcie.o
-brcmfmac-$(CONFIG_BRCMDBG) += \
- debug.o
-brcmfmac-$(CONFIG_BRCM_TRACING) += \
- tracepoint.o
-brcmfmac-$(CONFIG_OF) += \
- of.o
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
deleted file mode 100644
index 288c84e7c56b..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*******************************************************************************
- * Communicates with the dongle by using dcmd codes.
- * For certain dcmd codes, the dongle interprets string data from the host.
- ******************************************************************************/
-
-#include <linux/types.h>
-#include <linux/netdevice.h>
-
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-
-#include "core.h"
-#include "bus.h"
-#include "fwsignal.h"
-#include "debug.h"
-#include "tracepoint.h"
-#include "proto.h"
-#include "bcdc.h"
-
-struct brcmf_proto_bcdc_dcmd {
- __le32 cmd; /* dongle command value */
- __le32 len; /* lower 16: output buflen;
- * upper 16: input buflen (excludes header) */
- __le32 flags; /* flag defns given below */
- __le32 status; /* status code returned from the device */
-};
-
-/* BCDC flag definitions */
-#define BCDC_DCMD_ERROR 0x01 /* 1=cmd failed */
-#define BCDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */
-#define BCDC_DCMD_IF_MASK 0xF000 /* I/F index */
-#define BCDC_DCMD_IF_SHIFT 12
-#define BCDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */
-#define BCDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */
-#define BCDC_DCMD_ID(flags) \
- (((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
-
-/*
- * BCDC header - Broadcom specific extension of CDC.
- * Used on data packets to convey priority across USB.
- */
-#define BCDC_HEADER_LEN 4
-#define BCDC_PROTO_VER 2 /* Protocol version */
-#define BCDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */
-#define BCDC_FLAG_VER_SHIFT 4 /* Protocol version shift */
-#define BCDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */
-#define BCDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */
-#define BCDC_PRIORITY_MASK 0x7
-#define BCDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */
-#define BCDC_FLAG2_IF_SHIFT 0
-
-#define BCDC_GET_IF_IDX(hdr) \
- ((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
-#define BCDC_SET_IF_IDX(hdr, idx) \
- ((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
- ((idx) << BCDC_FLAG2_IF_SHIFT)))
-
-/**
- * struct brcmf_proto_bcdc_header - BCDC header format
- *
- * @flags: flags contain protocol and checksum info.
- * @priority: 802.1d priority and USB flow control info (bit 4:7).
- * @flags2: additional flags containing dongle interface index.
- * @data_offset: start of packet data. header is following by firmware signals.
- */
-struct brcmf_proto_bcdc_header {
- u8 flags;
- u8 priority;
- u8 flags2;
- u8 data_offset;
-};
-
-/*
- * maximum length of firmware signal data between
- * the BCDC header and packet data in the tx path.
- */
-#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12
-
-#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
-#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE
- * (amount of header tha might be added)
- * plus any space that might be needed
- * for bus alignment padding.
- */
-struct brcmf_bcdc {
- u16 reqid;
- u8 bus_header[BUS_HEADER_LEN];
- struct brcmf_proto_bcdc_dcmd msg;
- unsigned char buf[BRCMF_DCMD_MAXLEN];
-};
-
-
-static int
-brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
- uint len, bool set)
-{
- struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
- struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
- u32 flags;
-
- brcmf_dbg(BCDC, "Enter\n");
-
- memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
-
- msg->cmd = cpu_to_le32(cmd);
- msg->len = cpu_to_le32(len);
- flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
- if (set)
- flags |= BCDC_DCMD_SET;
- flags = (flags & ~BCDC_DCMD_IF_MASK) |
- (ifidx << BCDC_DCMD_IF_SHIFT);
- msg->flags = cpu_to_le32(flags);
-
- if (buf)
- memcpy(bcdc->buf, buf, len);
-
- len += sizeof(*msg);
- if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
- len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
-
- /* Send request */
- return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
-}
-
-static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
-{
- int ret;
- struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
-
- brcmf_dbg(BCDC, "Enter\n");
- len += sizeof(struct brcmf_proto_bcdc_dcmd);
- do {
- ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
- len);
- if (ret < 0)
- break;
- } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
-
- return ret;
-}
-
-static int
-brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
- void *buf, uint len)
-{
- struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
- struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
- void *info;
- int ret = 0, retries = 0;
- u32 id, flags;
-
- brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
-
- ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
- if (ret < 0) {
- brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n",
- ret);
- goto done;
- }
-
-retry:
- /* wait for interrupt and get first fragment */
- ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
- if (ret < 0)
- goto done;
-
- flags = le32_to_cpu(msg->flags);
- id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
-
- if ((id < bcdc->reqid) && (++retries < RETRIES))
- goto retry;
- if (id != bcdc->reqid) {
- brcmf_err("%s: unexpected request id %d (expected %d)\n",
- brcmf_ifname(drvr, ifidx), id, bcdc->reqid);
- ret = -EINVAL;
- goto done;
- }
-
- /* Check info buffer */
- info = (void *)&msg[1];
-
- /* Copy info buffer */
- if (buf) {
- if (ret < (int)len)
- len = ret;
- memcpy(buf, info, len);
- }
-
- /* Check the ERROR flag */
- if (flags & BCDC_DCMD_ERROR)
- ret = le32_to_cpu(msg->status);
-
-done:
- return ret;
-}
-
-static int
-brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
- void *buf, uint len)
-{
- struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
- struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
- int ret = 0;
- u32 flags, id;
-
- brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
-
- ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
- if (ret < 0)
- goto done;
-
- ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
- if (ret < 0)
- goto done;
-
- flags = le32_to_cpu(msg->flags);
- id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
-
- if (id != bcdc->reqid) {
- brcmf_err("%s: unexpected request id %d (expected %d)\n",
- brcmf_ifname(drvr, ifidx), id, bcdc->reqid);
- ret = -EINVAL;
- goto done;
- }
-
- /* Check the ERROR flag */
- if (flags & BCDC_DCMD_ERROR)
- ret = le32_to_cpu(msg->status);
-
-done:
- return ret;
-}
-
-static void
-brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
- struct sk_buff *pktbuf)
-{
- struct brcmf_proto_bcdc_header *h;
-
- brcmf_dbg(BCDC, "Enter\n");
-
- /* Push BDC header used to convey priority for buses that don't */
- skb_push(pktbuf, BCDC_HEADER_LEN);
-
- h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
-
- h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
- if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
- h->flags |= BCDC_FLAG_SUM_NEEDED;
-
- h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
- h->flags2 = 0;
- h->data_offset = offset;
- BCDC_SET_IF_IDX(h, ifidx);
- trace_brcmf_bcdchdr(pktbuf->data);
-}
-
-static int
-brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
- struct sk_buff *pktbuf, struct brcmf_if **ifp)
-{
- struct brcmf_proto_bcdc_header *h;
- struct brcmf_if *tmp_if;
-
- brcmf_dbg(BCDC, "Enter\n");
-
- /* Pop BCDC header used to convey priority for buses that don't */
- if (pktbuf->len <= BCDC_HEADER_LEN) {
- brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
- pktbuf->len, BCDC_HEADER_LEN);
- return -EBADE;
- }
-
- trace_brcmf_bcdchdr(pktbuf->data);
- h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
-
- tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
- if (!tmp_if) {
- brcmf_dbg(INFO, "no matching ifp found\n");
- return -EBADE;
- }
- if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
- BCDC_PROTO_VER) {
- brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
- brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
- return -EBADE;
- }
-
- if (h->flags & BCDC_FLAG_SUM_GOOD) {
- brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
- brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
- pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
- }
-
- pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
-
- skb_pull(pktbuf, BCDC_HEADER_LEN);
- if (do_fws)
- brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
- else
- skb_pull(pktbuf, h->data_offset << 2);
-
- if (pktbuf->len == 0)
- return -ENODATA;
-
- *ifp = tmp_if;
- return 0;
-}
-
-static int
-brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
- struct sk_buff *pktbuf)
-{
- brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
- return brcmf_bus_txdata(drvr->bus_if, pktbuf);
-}
-
-static void
-brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
- enum proto_addr_mode addr_mode)
-{
-}
-
-static void
-brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
- u8 peer[ETH_ALEN])
-{
-}
-
-static void
-brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
- u8 peer[ETH_ALEN])
-{
-}
-
-int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
-{
- struct brcmf_bcdc *bcdc;
-
- bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
- if (!bcdc)
- goto fail;
-
- /* ensure that the msg buf directly follows the cdc msg struct */
- if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
- brcmf_err("struct brcmf_proto_bcdc is not correctly defined\n");
- goto fail;
- }
-
- drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
- drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
- drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
- drvr->proto->txdata = brcmf_proto_bcdc_txdata;
- drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
- drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
- drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
- drvr->proto->pd = bcdc;
-
- drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
- drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
- sizeof(struct brcmf_proto_bcdc_dcmd);
- return 0;
-
-fail:
- kfree(bcdc);
- return -ENOMEM;
-}
-
-void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
-{
- kfree(drvr->proto->pd);
- drvr->proto->pd = NULL;
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
deleted file mode 100644
index 410a6645d316..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ /dev/null
@@ -1,1380 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-/* ****************** SDIO CARD Interface Functions **************************/
-
-#include <linux/types.h>
-#include <linux/netdevice.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-#include <linux/sched.h>
-#include <linux/completion.h>
-#include <linux/scatterlist.h>
-#include <linux/mmc/sdio.h>
-#include <linux/mmc/core.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/host.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/brcmfmac-sdio.h>
-#include <linux/pm_runtime.h>
-#include <linux/suspend.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/acpi.h>
-#include <net/cfg80211.h>
-
-#include <defs.h>
-#include <brcm_hw_ids.h>
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-#include <chipcommon.h>
-#include <soc.h>
-#include "chip.h"
-#include "bus.h"
-#include "debug.h"
-#include "sdio.h"
-#include "of.h"
-
-#define SDIOH_API_ACCESS_RETRY_LIMIT 2
-
-#define DMA_ALIGN_MASK 0x03
-
-#define SDIO_FUNC1_BLOCKSIZE 64
-#define SDIO_FUNC2_BLOCKSIZE 512
-/* Maximum milliseconds to wait for F2 to come up */
-#define SDIO_WAIT_F2RDY 3000
-
-#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */
-#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */
-
-struct brcmf_sdiod_freezer {
- atomic_t freezing;
- atomic_t thread_count;
- u32 frozen_count;
- wait_queue_head_t thread_freeze;
- struct completion resumed;
-};
-
-static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;
-module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);
-MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]");
-
-static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev_id);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
-
- brcmf_dbg(INTR, "OOB intr triggered\n");
-
- /* out-of-band interrupt is level-triggered which won't
- * be cleared until dpc
- */
- if (sdiodev->irq_en) {
- disable_irq_nosync(irq);
- sdiodev->irq_en = false;
- }
-
- brcmf_sdio_isr(sdiodev->bus);
-
- return IRQ_HANDLED;
-}
-
-static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
-
- brcmf_dbg(INTR, "IB intr triggered\n");
-
- brcmf_sdio_isr(sdiodev->bus);
-}
-
-/* dummy handler for SDIO function 2 interrupt */
-static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func)
-{
-}
-
-int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
-{
- int ret = 0;
- u8 data;
- u32 addr, gpiocontrol;
- unsigned long flags;
-
- if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
- brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
- sdiodev->pdata->oob_irq_nr);
- ret = request_irq(sdiodev->pdata->oob_irq_nr,
- brcmf_sdiod_oob_irqhandler,
- sdiodev->pdata->oob_irq_flags,
- "brcmf_oob_intr",
- &sdiodev->func[1]->dev);
- if (ret != 0) {
- brcmf_err("request_irq failed %d\n", ret);
- return ret;
- }
- sdiodev->oob_irq_requested = true;
- spin_lock_init(&sdiodev->irq_en_lock);
- spin_lock_irqsave(&sdiodev->irq_en_lock, flags);
- sdiodev->irq_en = true;
- spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
-
- ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr);
- if (ret != 0) {
- brcmf_err("enable_irq_wake failed %d\n", ret);
- return ret;
- }
- sdiodev->irq_wake = true;
-
- sdio_claim_host(sdiodev->func[1]);
-
- if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) {
- /* assign GPIO to SDIO core */
- addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol);
- gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret);
- gpiocontrol |= 0x2;
- brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret);
-
- brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf,
- &ret);
- brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret);
- brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret);
- }
-
- /* must configure SDIO_CCCR_IENx to enable irq */
- data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
- data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
- brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
-
- /* redirect, configure and enable io for interrupt signal */
- data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
- if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
- data |= SDIO_SEPINT_ACT_HI;
- brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
-
- sdio_release_host(sdiodev->func[1]);
- } else {
- brcmf_dbg(SDIO, "Entering\n");
- sdio_claim_host(sdiodev->func[1]);
- sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler);
- sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler);
- sdio_release_host(sdiodev->func[1]);
- }
-
- return 0;
-}
-
-int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
-{
- brcmf_dbg(SDIO, "Entering\n");
-
- if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
- sdio_claim_host(sdiodev->func[1]);
- brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
- brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
- sdio_release_host(sdiodev->func[1]);
-
- if (sdiodev->oob_irq_requested) {
- sdiodev->oob_irq_requested = false;
- if (sdiodev->irq_wake) {
- disable_irq_wake(sdiodev->pdata->oob_irq_nr);
- sdiodev->irq_wake = false;
- }
- free_irq(sdiodev->pdata->oob_irq_nr,
- &sdiodev->func[1]->dev);
- sdiodev->irq_en = false;
- }
- } else {
- sdio_claim_host(sdiodev->func[1]);
- sdio_release_irq(sdiodev->func[2]);
- sdio_release_irq(sdiodev->func[1]);
- sdio_release_host(sdiodev->func[1]);
- }
-
- return 0;
-}
-
-void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
- enum brcmf_sdiod_state state)
-{
- if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM ||
- state == sdiodev->state)
- return;
-
- brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state);
- switch (sdiodev->state) {
- case BRCMF_SDIOD_DATA:
- /* any other state means bus interface is down */
- brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN);
- break;
- case BRCMF_SDIOD_DOWN:
- /* transition from DOWN to DATA means bus interface is up */
- if (state == BRCMF_SDIOD_DATA)
- brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP);
- break;
- default:
- break;
- }
- sdiodev->state = state;
-}
-
-static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func,
- uint regaddr, u8 byte)
-{
- int err_ret;
-
- /*
- * Can only directly write to some F0 registers.
- * Handle CCCR_IENx and CCCR_ABORT command
- * as a special case.
- */
- if ((regaddr == SDIO_CCCR_ABORT) ||
- (regaddr == SDIO_CCCR_IENx))
- sdio_writeb(func, byte, regaddr, &err_ret);
- else
- sdio_f0_writeb(func, byte, regaddr, &err_ret);
-
- return err_ret;
-}
-
-static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
- u32 addr, u8 regsz, void *data, bool write)
-{
- struct sdio_func *func;
- int ret;
-
- brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
- write, fn, addr, regsz);
-
- /* only allow byte access on F0 */
- if (WARN_ON(regsz > 1 && !fn))
- return -EINVAL;
- func = sdiodev->func[fn];
-
- switch (regsz) {
- case sizeof(u8):
- if (write) {
- if (fn)
- sdio_writeb(func, *(u8 *)data, addr, &ret);
- else
- ret = brcmf_sdiod_f0_writeb(func, addr,
- *(u8 *)data);
- } else {
- if (fn)
- *(u8 *)data = sdio_readb(func, addr, &ret);
- else
- *(u8 *)data = sdio_f0_readb(func, addr, &ret);
- }
- break;
- case sizeof(u16):
- if (write)
- sdio_writew(func, *(u16 *)data, addr, &ret);
- else
- *(u16 *)data = sdio_readw(func, addr, &ret);
- break;
- case sizeof(u32):
- if (write)
- sdio_writel(func, *(u32 *)data, addr, &ret);
- else
- *(u32 *)data = sdio_readl(func, addr, &ret);
- break;
- default:
- brcmf_err("invalid size: %d\n", regsz);
- break;
- }
-
- if (ret)
- brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
- write ? "write" : "read", fn, addr, ret);
-
- return ret;
-}
-
-static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
- u8 regsz, void *data, bool write)
-{
- u8 func;
- s32 retry = 0;
- int ret;
-
- if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
- return -ENOMEDIUM;
-
- /*
- * figure out how to read the register based on address range
- * 0x00 ~ 0x7FF: function 0 CCCR and FBR
- * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
- * The rest: function 1 silicon backplane core registers
- */
- if ((addr & ~REG_F0_REG_MASK) == 0)
- func = SDIO_FUNC_0;
- else
- func = SDIO_FUNC_1;
-
- do {
- if (!write)
- memset(data, 0, regsz);
- /* for retry wait for 1 ms till bus get settled down */
- if (retry)
- usleep_range(1000, 2000);
- ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz,
- data, write);
- } while (ret != 0 && ret != -ENOMEDIUM &&
- retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
-
- if (ret == -ENOMEDIUM)
- brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
- else if (ret != 0) {
- /*
- * SleepCSR register access can fail when
- * waking up the device so reduce this noise
- * in the logs.
- */
- if (addr != SBSDIO_FUNC1_SLEEPCSR)
- brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
- write ? "write" : "read", func, addr, ret);
- else
- brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
- write ? "write" : "read", func, addr, ret);
- }
- return ret;
-}
-
-static int
-brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
-{
- int err = 0, i;
- u8 addr[3];
-
- if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
- return -ENOMEDIUM;
-
- addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
- addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK;
- addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK;
-
- for (i = 0; i < 3; i++) {
- err = brcmf_sdiod_regrw_helper(sdiodev,
- SBSDIO_FUNC1_SBADDRLOW + i,
- sizeof(u8), &addr[i], true);
- if (err) {
- brcmf_err("failed at addr: 0x%0x\n",
- SBSDIO_FUNC1_SBADDRLOW + i);
- break;
- }
- }
-
- return err;
-}
-
-static int
-brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
-{
- uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
- int err = 0;
-
- if (bar0 != sdiodev->sbwad) {
- err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0);
- if (err)
- return err;
-
- sdiodev->sbwad = bar0;
- }
-
- *addr &= SBSDIO_SB_OFT_ADDR_MASK;
-
- if (width == 4)
- *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
-
- return 0;
-}
-
-u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
-{
- u8 data;
- int retval;
-
- brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
- retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
- false);
- brcmf_dbg(SDIO, "data:0x%02x\n", data);
-
- if (ret)
- *ret = retval;
-
- return data;
-}
-
-u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
-{
- u32 data;
- int retval;
-
- brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
- retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
- if (retval)
- goto done;
- retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
- false);
- brcmf_dbg(SDIO, "data:0x%08x\n", data);
-
-done:
- if (ret)
- *ret = retval;
-
- return data;
-}
-
-void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
- u8 data, int *ret)
-{
- int retval;
-
- brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
- retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
- true);
- if (ret)
- *ret = retval;
-}
-
-void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
- u32 data, int *ret)
-{
- int retval;
-
- brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
- retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
- if (retval)
- goto done;
- retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
- true);
-
-done:
- if (ret)
- *ret = retval;
-}
-
-static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
- bool write, u32 addr, struct sk_buff *pkt)
-{
- unsigned int req_sz;
- int err;
-
- /* Single skb use the standard mmc interface */
- req_sz = pkt->len + 3;
- req_sz &= (uint)~3;
-
- if (write)
- err = sdio_memcpy_toio(sdiodev->func[fn], addr,
- ((u8 *)(pkt->data)), req_sz);
- else if (fn == 1)
- err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)),
- addr, req_sz);
- else
- /* function 2 read is FIFO operation */
- err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr,
- req_sz);
- if (err == -ENOMEDIUM)
- brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
- return err;
-}
-
-/**
- * brcmf_sdiod_sglist_rw - 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_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
- bool write, u32 addr,
- struct sk_buff_head *pktlist)
-{
- unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
- unsigned int max_req_sz, orig_offset, dst_offset;
- unsigned short max_seg_cnt, seg_sz;
- unsigned char *pkt_data, *orig_data, *dst_data;
- struct sk_buff *pkt_next = NULL, *local_pkt_next;
- struct sk_buff_head local_list, *target_list;
- struct mmc_request mmc_req;
- struct mmc_command mmc_cmd;
- struct mmc_data mmc_dat;
- struct scatterlist *sgl;
- int ret = 0;
-
- if (!pktlist->qlen)
- return -EINVAL;
-
- target_list = pktlist;
- /* for host with broken sg support, prepare a page aligned list */
- __skb_queue_head_init(&local_list);
- if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
- req_sz = 0;
- skb_queue_walk(pktlist, pkt_next)
- req_sz += pkt_next->len;
- req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize);
- while (req_sz > PAGE_SIZE) {
- pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE);
- if (pkt_next == NULL) {
- ret = -ENOMEM;
- goto exit;
- }
- __skb_queue_tail(&local_list, pkt_next);
- req_sz -= PAGE_SIZE;
- }
- pkt_next = brcmu_pkt_buf_get_skb(req_sz);
- if (pkt_next == NULL) {
- ret = -ENOMEM;
- goto exit;
- }
- __skb_queue_tail(&local_list, pkt_next);
- target_list = &local_list;
- }
-
- func_blk_sz = sdiodev->func[fn]->cur_blksize;
- max_req_sz = sdiodev->max_request_size;
- max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count,
- target_list->qlen);
- seg_sz = target_list->qlen;
- pkt_offset = 0;
- pkt_next = target_list->next;
-
- memset(&mmc_req, 0, sizeof(struct mmc_request));
- memset(&mmc_cmd, 0, sizeof(struct mmc_command));
- memset(&mmc_dat, 0, sizeof(struct mmc_data));
-
- mmc_dat.sg = sdiodev->sgtable.sgl;
- mmc_dat.blksz = func_blk_sz;
- mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
- mmc_cmd.opcode = SD_IO_RW_EXTENDED;
- mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */
- mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */
- mmc_cmd.arg |= 1<<27; /* block mode */
- /* for function 1 the addr will be incremented */
- mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
- mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
- mmc_req.cmd = &mmc_cmd;
- mmc_req.data = &mmc_dat;
-
- while (seg_sz) {
- req_sz = 0;
- sg_cnt = 0;
- sgl = sdiodev->sgtable.sgl;
- /* prep sg table */
- while (pkt_next != (struct sk_buff *)target_list) {
- pkt_data = pkt_next->data + pkt_offset;
- sg_data_sz = pkt_next->len - pkt_offset;
- if (sg_data_sz > sdiodev->max_segment_size)
- sg_data_sz = sdiodev->max_segment_size;
- if (sg_data_sz > max_req_sz - req_sz)
- sg_data_sz = max_req_sz - req_sz;
-
- 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_cnt)
- 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);
- ret = -ENOTBLK;
- goto exit;
- }
-
- mmc_dat.sg_len = sg_cnt;
- mmc_dat.blocks = req_sz / func_blk_sz;
- mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */
- mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */
- /* incrementing addr for function 1 */
- if (fn == 1)
- addr += req_sz;
-
- mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
- mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req);
-
- ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
- if (ret == -ENOMEDIUM) {
- brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
- break;
- } else if (ret != 0) {
- brcmf_err("CMD53 sg block %s failed %d\n",
- write ? "write" : "read", ret);
- ret = -EIO;
- break;
- }
- }
-
- if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
- local_pkt_next = local_list.next;
- orig_offset = 0;
- skb_queue_walk(pktlist, pkt_next) {
- dst_offset = 0;
- do {
- req_sz = local_pkt_next->len - orig_offset;
- req_sz = min_t(uint, pkt_next->len - dst_offset,
- req_sz);
- orig_data = local_pkt_next->data + orig_offset;
- dst_data = pkt_next->data + dst_offset;
- memcpy(dst_data, orig_data, req_sz);
- orig_offset += req_sz;
- dst_offset += req_sz;
- if (orig_offset == local_pkt_next->len) {
- orig_offset = 0;
- local_pkt_next = local_pkt_next->next;
- }
- if (dst_offset == pkt_next->len)
- break;
- } while (!skb_queue_empty(&local_list));
- }
- }
-
-exit:
- sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents);
- while ((pkt_next = __skb_dequeue(&local_list)) != NULL)
- brcmu_pkt_buf_free_skb(pkt_next);
-
- return ret;
-}
-
-int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
-{
- struct sk_buff *mypkt;
- int err;
-
- mypkt = brcmu_pkt_buf_get_skb(nbytes);
- if (!mypkt) {
- brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
- nbytes);
- return -EIO;
- }
-
- err = brcmf_sdiod_recv_pkt(sdiodev, mypkt);
- if (!err)
- memcpy(buf, mypkt->data, nbytes);
-
- brcmu_pkt_buf_free_skb(mypkt);
- return err;
-}
-
-int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt)
-{
- u32 addr = sdiodev->sbwad;
- int err = 0;
-
- brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len);
-
- err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
- if (err)
- goto done;
-
- err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt);
-
-done:
- return err;
-}
-
-int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
- struct sk_buff_head *pktq, uint totlen)
-{
- struct sk_buff *glom_skb;
- struct sk_buff *skb;
- u32 addr = sdiodev->sbwad;
- int err = 0;
-
- brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n",
- addr, pktq->qlen);
-
- err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
- if (err)
- goto done;
-
- if (pktq->qlen == 1)
- err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
- pktq->next);
- else if (!sdiodev->sg_support) {
- glom_skb = brcmu_pkt_buf_get_skb(totlen);
- if (!glom_skb)
- return -ENOMEM;
- err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
- glom_skb);
- if (err)
- goto done;
-
- skb_queue_walk(pktq, skb) {
- memcpy(skb->data, glom_skb->data, skb->len);
- skb_pull(glom_skb, skb->len);
- }
- } else
- err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr,
- pktq);
-
-done:
- return err;
-}
-
-int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
-{
- struct sk_buff *mypkt;
- u32 addr = sdiodev->sbwad;
- int err;
-
- mypkt = brcmu_pkt_buf_get_skb(nbytes);
- if (!mypkt) {
- brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
- nbytes);
- return -EIO;
- }
-
- memcpy(mypkt->data, buf, nbytes);
-
- err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
-
- if (!err)
- err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr,
- mypkt);
-
- brcmu_pkt_buf_free_skb(mypkt);
- return err;
-
-}
-
-int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
- struct sk_buff_head *pktq)
-{
- struct sk_buff *skb;
- u32 addr = sdiodev->sbwad;
- int err;
-
- brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen);
-
- err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
- if (err)
- return err;
-
- if (pktq->qlen == 1 || !sdiodev->sg_support)
- skb_queue_walk(pktq, skb) {
- err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true,
- addr, skb);
- if (err)
- break;
- }
- else
- err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr,
- pktq);
-
- return err;
-}
-
-int
-brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
- u8 *data, uint size)
-{
- int bcmerror = 0;
- struct sk_buff *pkt;
- u32 sdaddr;
- uint dsize;
-
- dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
- pkt = dev_alloc_skb(dsize);
- if (!pkt) {
- brcmf_err("dev_alloc_skb failed: len %d\n", dsize);
- return -EIO;
- }
- pkt->priority = 0;
-
- /* Determine initial transfer parameters */
- sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
- if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
- dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
- else
- dsize = size;
-
- sdio_claim_host(sdiodev->func[1]);
-
- /* Do the transfer(s) */
- while (size) {
- /* Set the backplane window to include the start address */
- bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address);
- if (bcmerror)
- break;
-
- brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
- write ? "write" : "read", dsize,
- sdaddr, address & SBSDIO_SBWINDOW_MASK);
-
- sdaddr &= SBSDIO_SB_OFT_ADDR_MASK;
- sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
-
- skb_put(pkt, dsize);
- if (write)
- memcpy(pkt->data, data, dsize);
- bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write,
- sdaddr, pkt);
- if (bcmerror) {
- brcmf_err("membytes transfer failed\n");
- break;
- }
- if (!write)
- memcpy(data, pkt->data, dsize);
- skb_trim(pkt, 0);
-
- /* Adjust for next transfer (if any) */
- size -= dsize;
- if (size) {
- data += dsize;
- address += dsize;
- sdaddr = 0;
- dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
- }
- }
-
- dev_kfree_skb(pkt);
-
- /* Return the window to backplane enumeration space for core access */
- if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad))
- brcmf_err("FAILED to set window back to 0x%x\n",
- sdiodev->sbwad);
-
- sdio_release_host(sdiodev->func[1]);
-
- return bcmerror;
-}
-
-int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
-{
- char t_func = (char)fn;
- brcmf_dbg(SDIO, "Enter\n");
-
- /* issue abort cmd52 command through F0 */
- brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT,
- sizeof(t_func), &t_func, true);
-
- brcmf_dbg(SDIO, "Exit\n");
- return 0;
-}
-
-static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
-{
- uint nents;
- int err;
-
- if (!sdiodev->sg_support)
- return;
-
- nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz);
- nents += (nents >> 4) + 1;
-
- WARN_ON(nents > sdiodev->max_segment_count);
-
- brcmf_dbg(TRACE, "nents=%d\n", nents);
- err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL);
- if (err < 0) {
- brcmf_err("allocation failed: disable scatter-gather");
- sdiodev->sg_support = false;
- }
-
- sdiodev->txglomsz = brcmf_sdiod_txglomsz;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
-{
- sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL);
- if (!sdiodev->freezer)
- return -ENOMEM;
- atomic_set(&sdiodev->freezer->thread_count, 0);
- atomic_set(&sdiodev->freezer->freezing, 0);
- init_waitqueue_head(&sdiodev->freezer->thread_freeze);
- init_completion(&sdiodev->freezer->resumed);
- return 0;
-}
-
-static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
-{
- if (sdiodev->freezer) {
- WARN_ON(atomic_read(&sdiodev->freezer->freezing));
- kfree(sdiodev->freezer);
- }
-}
-
-static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev)
-{
- atomic_t *expect = &sdiodev->freezer->thread_count;
- int res = 0;
-
- sdiodev->freezer->frozen_count = 0;
- reinit_completion(&sdiodev->freezer->resumed);
- atomic_set(&sdiodev->freezer->freezing, 1);
- brcmf_sdio_trigger_dpc(sdiodev->bus);
- wait_event(sdiodev->freezer->thread_freeze,
- atomic_read(expect) == sdiodev->freezer->frozen_count);
- sdio_claim_host(sdiodev->func[1]);
- res = brcmf_sdio_sleep(sdiodev->bus, true);
- sdio_release_host(sdiodev->func[1]);
- return res;
-}
-
-static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev)
-{
- sdio_claim_host(sdiodev->func[1]);
- brcmf_sdio_sleep(sdiodev->bus, false);
- sdio_release_host(sdiodev->func[1]);
- atomic_set(&sdiodev->freezer->freezing, 0);
- complete_all(&sdiodev->freezer->resumed);
-}
-
-bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
-{
- return atomic_read(&sdiodev->freezer->freezing);
-}
-
-void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
-{
- if (!brcmf_sdiod_freezing(sdiodev))
- return;
- sdiodev->freezer->frozen_count++;
- wake_up(&sdiodev->freezer->thread_freeze);
- wait_for_completion(&sdiodev->freezer->resumed);
-}
-
-void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
-{
- atomic_inc(&sdiodev->freezer->thread_count);
-}
-
-void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
-{
- atomic_dec(&sdiodev->freezer->thread_count);
-}
-#else
-static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
-{
- return 0;
-}
-
-static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
-{
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
-{
- sdiodev->state = BRCMF_SDIOD_DOWN;
- if (sdiodev->bus) {
- brcmf_sdio_remove(sdiodev->bus);
- sdiodev->bus = NULL;
- }
-
- brcmf_sdiod_freezer_detach(sdiodev);
-
- /* Disable Function 2 */
- sdio_claim_host(sdiodev->func[2]);
- sdio_disable_func(sdiodev->func[2]);
- sdio_release_host(sdiodev->func[2]);
-
- /* Disable Function 1 */
- sdio_claim_host(sdiodev->func[1]);
- sdio_disable_func(sdiodev->func[1]);
- sdio_release_host(sdiodev->func[1]);
-
- sg_free_table(&sdiodev->sgtable);
- sdiodev->sbwad = 0;
-
- pm_runtime_allow(sdiodev->func[1]->card->host->parent);
- return 0;
-}
-
-static void brcmf_sdiod_host_fixup(struct mmc_host *host)
-{
- /* runtime-pm powers off the device */
- pm_runtime_forbid(host->parent);
- /* avoid removal detection upon resume */
- host->caps |= MMC_CAP_NONREMOVABLE;
-}
-
-static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
-{
- struct sdio_func *func;
- struct mmc_host *host;
- uint max_blocks;
- int ret = 0;
-
- sdiodev->num_funcs = 2;
-
- sdio_claim_host(sdiodev->func[1]);
-
- ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE);
- if (ret) {
- brcmf_err("Failed to set F1 blocksize\n");
- sdio_release_host(sdiodev->func[1]);
- goto out;
- }
- ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE);
- if (ret) {
- brcmf_err("Failed to set F2 blocksize\n");
- sdio_release_host(sdiodev->func[1]);
- goto out;
- }
-
- /* increase F2 timeout */
- sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY;
-
- /* Enable Function 1 */
- ret = sdio_enable_func(sdiodev->func[1]);
- sdio_release_host(sdiodev->func[1]);
- if (ret) {
- brcmf_err("Failed to enable F1: err=%d\n", ret);
- goto out;
- }
-
- /*
- * determine host related variables after brcmf_sdiod_probe()
- * as func->cur_blksize is properly set and F2 init has been
- * completed successfully.
- */
- func = sdiodev->func[2];
- host = func->card->host;
- sdiodev->sg_support = host->max_segs > 1;
- max_blocks = min_t(uint, host->max_blk_count, 511u);
- sdiodev->max_request_size = min_t(uint, host->max_req_size,
- max_blocks * func->cur_blksize);
- sdiodev->max_segment_count = min_t(uint, host->max_segs,
- SG_MAX_SINGLE_ALLOC);
- sdiodev->max_segment_size = host->max_seg_size;
-
- /* allocate scatter-gather table. sg support
- * will be disabled upon allocation failure.
- */
- brcmf_sdiod_sgtable_alloc(sdiodev);
-
- ret = brcmf_sdiod_freezer_attach(sdiodev);
- if (ret)
- goto out;
-
- /* try to attach to the target device */
- sdiodev->bus = brcmf_sdio_probe(sdiodev);
- if (!sdiodev->bus) {
- ret = -ENODEV;
- goto out;
- }
- brcmf_sdiod_host_fixup(host);
-out:
- if (ret)
- brcmf_sdiod_remove(sdiodev);
-
- return ret;
-}
-
-#define BRCMF_SDIO_DEVICE(dev_id) \
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)}
-
-/* devices we support, null terminated */
-static const struct sdio_device_id brcmf_sdmmc_ids[] = {
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
- { /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
-
-static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
-
-
-static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev,
- int val)
-{
-#if IS_ENABLED(CONFIG_ACPI)
- struct acpi_device *adev;
-
- adev = ACPI_COMPANION(dev);
- if (adev)
- adev->flags.power_manageable = 0;
-#endif
-}
-
-static int brcmf_ops_sdio_probe(struct sdio_func *func,
- const struct sdio_device_id *id)
-{
- int err;
- struct brcmf_sdio_dev *sdiodev;
- struct brcmf_bus *bus_if;
- struct device *dev;
-
- brcmf_dbg(SDIO, "Enter\n");
- brcmf_dbg(SDIO, "Class=%x\n", func->class);
- brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
- brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
- brcmf_dbg(SDIO, "Function#: %d\n", func->num);
-
- dev = &func->dev;
- /* prohibit ACPI power management for this device */
- brcmf_sdiod_acpi_set_power_manageable(dev, 0);
-
- /* Consume func num 1 but dont do anything with it. */
- if (func->num == 1)
- return 0;
-
- /* Ignore anything but func 2 */
- if (func->num != 2)
- return -ENODEV;
-
- bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
- if (!bus_if)
- return -ENOMEM;
- sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
- if (!sdiodev) {
- kfree(bus_if);
- return -ENOMEM;
- }
-
- /* store refs to functions used. mmc_card does
- * not hold the F0 function pointer.
- */
- sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL);
- sdiodev->func[0]->num = 0;
- sdiodev->func[1] = func->card->sdio_func[0];
- sdiodev->func[2] = func;
-
- sdiodev->bus_if = bus_if;
- bus_if->bus_priv.sdio = sdiodev;
- bus_if->proto_type = BRCMF_PROTO_BCDC;
- dev_set_drvdata(&func->dev, bus_if);
- dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
- sdiodev->dev = &sdiodev->func[1]->dev;
- sdiodev->pdata = brcmfmac_sdio_pdata;
-
- if (!sdiodev->pdata)
- brcmf_of_probe(sdiodev);
-
-#ifdef CONFIG_PM_SLEEP
- /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
- * is true or when platform data OOB irq is true).
- */
- if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
- ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
- (sdiodev->pdata && sdiodev->pdata->oob_irq_supported)))
- bus_if->wowl_supported = true;
-#endif
-
- brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
-
- brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
- err = brcmf_sdiod_probe(sdiodev);
- if (err) {
- brcmf_err("F2 error, probe failed %d...\n", err);
- goto fail;
- }
-
- brcmf_dbg(SDIO, "F2 init completed...\n");
- return 0;
-
-fail:
- dev_set_drvdata(&func->dev, NULL);
- dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
- kfree(sdiodev->func[0]);
- kfree(sdiodev);
- kfree(bus_if);
- return err;
-}
-
-static void brcmf_ops_sdio_remove(struct sdio_func *func)
-{
- struct brcmf_bus *bus_if;
- struct brcmf_sdio_dev *sdiodev;
-
- brcmf_dbg(SDIO, "Enter\n");
- brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
- brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
- brcmf_dbg(SDIO, "Function: %d\n", func->num);
-
- if (func->num != 1)
- return;
-
- bus_if = dev_get_drvdata(&func->dev);
- if (bus_if) {
- sdiodev = bus_if->bus_priv.sdio;
- brcmf_sdiod_remove(sdiodev);
-
- dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
- dev_set_drvdata(&sdiodev->func[2]->dev, NULL);
-
- kfree(bus_if);
- kfree(sdiodev->func[0]);
- kfree(sdiodev);
- }
-
- brcmf_dbg(SDIO, "Exit\n");
-}
-
-void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
-
- brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
- sdiodev->wowl_enabled = enabled;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int brcmf_ops_sdio_suspend(struct device *dev)
-{
- struct sdio_func *func;
- struct brcmf_bus *bus_if;
- struct brcmf_sdio_dev *sdiodev;
- mmc_pm_flag_t sdio_flags;
-
- func = container_of(dev, struct sdio_func, dev);
- brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
- if (func->num != SDIO_FUNC_1)
- return 0;
-
-
- bus_if = dev_get_drvdata(dev);
- sdiodev = bus_if->bus_priv.sdio;
-
- brcmf_sdiod_freezer_on(sdiodev);
- brcmf_sdio_wd_timer(sdiodev->bus, 0);
-
- sdio_flags = MMC_PM_KEEP_POWER;
- if (sdiodev->wowl_enabled) {
- if (sdiodev->pdata->oob_irq_supported)
- enable_irq_wake(sdiodev->pdata->oob_irq_nr);
- else
- sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
- }
- if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
- brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
- return 0;
-}
-
-static int brcmf_ops_sdio_resume(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct sdio_func *func = container_of(dev, struct sdio_func, dev);
-
- brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
- if (func->num != SDIO_FUNC_2)
- return 0;
-
- brcmf_sdiod_freezer_off(sdiodev);
- return 0;
-}
-
-static const struct dev_pm_ops brcmf_sdio_pm_ops = {
- .suspend = brcmf_ops_sdio_suspend,
- .resume = brcmf_ops_sdio_resume,
-};
-#endif /* CONFIG_PM_SLEEP */
-
-static struct sdio_driver brcmf_sdmmc_driver = {
- .probe = brcmf_ops_sdio_probe,
- .remove = brcmf_ops_sdio_remove,
- .name = BRCMFMAC_SDIO_PDATA_NAME,
- .id_table = brcmf_sdmmc_ids,
- .drv = {
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM_SLEEP
- .pm = &brcmf_sdio_pm_ops,
-#endif /* CONFIG_PM_SLEEP */
- },
-};
-
-static int __init brcmf_sdio_pd_probe(struct platform_device *pdev)
-{
- brcmf_dbg(SDIO, "Enter\n");
-
- brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
-
- if (brcmfmac_sdio_pdata->power_on)
- brcmfmac_sdio_pdata->power_on();
-
- return 0;
-}
-
-static int brcmf_sdio_pd_remove(struct platform_device *pdev)
-{
- brcmf_dbg(SDIO, "Enter\n");
-
- if (brcmfmac_sdio_pdata->power_off)
- brcmfmac_sdio_pdata->power_off();
-
- sdio_unregister_driver(&brcmf_sdmmc_driver);
-
- return 0;
-}
-
-static struct platform_driver brcmf_sdio_pd = {
- .remove = brcmf_sdio_pd_remove,
- .driver = {
- .name = BRCMFMAC_SDIO_PDATA_NAME,
- }
-};
-
-void brcmf_sdio_register(void)
-{
- int ret;
-
- ret = sdio_register_driver(&brcmf_sdmmc_driver);
- if (ret)
- brcmf_err("sdio_register_driver failed: %d\n", ret);
-}
-
-void brcmf_sdio_exit(void)
-{
- brcmf_dbg(SDIO, "Enter\n");
-
- if (brcmfmac_sdio_pdata)
- platform_driver_unregister(&brcmf_sdio_pd);
- else
- sdio_unregister_driver(&brcmf_sdmmc_driver);
-}
-
-void __init brcmf_sdio_init(void)
-{
- int ret;
-
- brcmf_dbg(SDIO, "Enter\n");
-
- ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
- if (ret == -ENODEV)
- brcmf_dbg(SDIO, "No platform data available.\n");
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
deleted file mode 100644
index 4e33f96b3dd1..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * Copyright (c) 2013 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <net/cfg80211.h>
-
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include <defs.h>
-#include "core.h"
-#include "debug.h"
-#include "fwil.h"
-#include "fwil_types.h"
-#include "btcoex.h"
-#include "p2p.h"
-#include "cfg80211.h"
-
-/* T1 start SCO/eSCO priority suppression */
-#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000
-
-/* BT registers values during DHCP */
-#define BRCMF_BT_DHCP_REG50 0x8022
-#define BRCMF_BT_DHCP_REG51 0
-#define BRCMF_BT_DHCP_REG64 0
-#define BRCMF_BT_DHCP_REG65 0
-#define BRCMF_BT_DHCP_REG71 0
-#define BRCMF_BT_DHCP_REG66 0x2710
-#define BRCMF_BT_DHCP_REG41 0x33
-#define BRCMF_BT_DHCP_REG68 0x190
-
-/* number of samples for SCO detection */
-#define BRCMF_BT_SCO_SAMPLES 12
-
-/**
-* enum brcmf_btcoex_state - BT coex DHCP state machine states
-* @BRCMF_BT_DHCP_IDLE: DCHP is idle
-* @BRCMF_BT_DHCP_START: DHCP started, wait before
-* boosting wifi priority
-* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended,
-* boost wifi priority
-* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end,
-* restore defaults
-*/
-enum brcmf_btcoex_state {
- BRCMF_BT_DHCP_IDLE,
- BRCMF_BT_DHCP_START,
- BRCMF_BT_DHCP_OPPR_WIN,
- BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT
-};
-
-/**
- * struct brcmf_btcoex_info - BT coex related information
- * @vif: interface for which request was done.
- * @timer: timer for DHCP state machine
- * @timeout: configured timeout.
- * @timer_on: DHCP timer active
- * @dhcp_done: DHCP finished before T1/T2 timer expiration
- * @bt_state: DHCP state machine state
- * @work: DHCP state machine work
- * @cfg: driver private data for cfg80211 interface
- * @reg66: saved value of btc_params 66
- * @reg41: saved value of btc_params 41
- * @reg68: saved value of btc_params 68
- * @saved_regs_part1: flag indicating regs 66,41,68
- * have been saved
- * @reg51: saved value of btc_params 51
- * @reg64: saved value of btc_params 64
- * @reg65: saved value of btc_params 65
- * @reg71: saved value of btc_params 71
- * @saved_regs_part1: flag indicating regs 50,51,64,65,71
- * have been saved
- */
-struct brcmf_btcoex_info {
- struct brcmf_cfg80211_vif *vif;
- struct timer_list timer;
- u16 timeout;
- bool timer_on;
- bool dhcp_done;
- enum brcmf_btcoex_state bt_state;
- struct work_struct work;
- struct brcmf_cfg80211_info *cfg;
- u32 reg66;
- u32 reg41;
- u32 reg68;
- bool saved_regs_part1;
- u32 reg50;
- u32 reg51;
- u32 reg64;
- u32 reg65;
- u32 reg71;
- bool saved_regs_part2;
-};
-
-/**
- * brcmf_btcoex_params_write() - write btc_params firmware variable
- * @ifp: interface
- * @addr: btc_params register number
- * @data: data to write
- */
-static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data)
-{
- struct {
- __le32 addr;
- __le32 data;
- } reg_write;
-
- reg_write.addr = cpu_to_le32(addr);
- reg_write.data = cpu_to_le32(data);
- return brcmf_fil_iovar_data_set(ifp, "btc_params",
- &reg_write, sizeof(reg_write));
-}
-
-/**
- * brcmf_btcoex_params_read() - read btc_params firmware variable
- * @ifp: interface
- * @addr: btc_params register number
- * @data: read data
- */
-static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)
-{
- *data = addr;
-
- return brcmf_fil_iovar_int_get(ifp, "btc_params", data);
-}
-
-/**
- * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters
- * @btci: BT coex info
- * @trump_sco:
- * true - set SCO/eSCO parameters for compatibility
- * during DHCP window
- * false - restore saved parameter values
- *
- * Enhanced BT COEX settings for eSCO compatibility during DHCP window
- */
-static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
- bool trump_sco)
-{
- struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
-
- if (trump_sco && !btci->saved_regs_part2) {
- /* this should reduce eSCO agressive
- * retransmit w/o breaking it
- */
-
- /* save current */
- brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
- brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
- brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
- brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
- brcmf_btcoex_params_read(ifp, 65, &btci->reg65);
- brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
-
- btci->saved_regs_part2 = true;
- brcmf_dbg(INFO,
- "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
- btci->reg50, btci->reg51, btci->reg64,
- btci->reg65, btci->reg71);
-
- /* pacify the eSco */
- brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50);
- brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51);
- brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64);
- brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65);
- brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71);
-
- } else if (btci->saved_regs_part2) {
- /* restore previously saved bt params */
- brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
- brcmf_btcoex_params_write(ifp, 50, btci->reg50);
- brcmf_btcoex_params_write(ifp, 51, btci->reg51);
- brcmf_btcoex_params_write(ifp, 64, btci->reg64);
- brcmf_btcoex_params_write(ifp, 65, btci->reg65);
- brcmf_btcoex_params_write(ifp, 71, btci->reg71);
-
- brcmf_dbg(INFO,
- "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
- btci->reg50, btci->reg51, btci->reg64,
- btci->reg65, btci->reg71);
-
- btci->saved_regs_part2 = false;
- } else {
- brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
- }
-}
-
-/**
- * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active
- * @ifp: interface
- *
- * return: true if SCO/eSCO session is active
- */
-static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
-{
- int ioc_res = 0;
- bool res = false;
- int sco_id_cnt = 0;
- u32 param27;
- int i;
-
- for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) {
- ioc_res = brcmf_btcoex_params_read(ifp, 27, &param27);
-
- if (ioc_res < 0) {
- brcmf_err("ioc read btc params error\n");
- break;
- }
-
- brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
-
- if ((param27 & 0x6) == 2) { /* count both sco & esco */
- sco_id_cnt++;
- }
-
- if (sco_id_cnt > 2) {
- brcmf_dbg(INFO,
- "sco/esco detected, pkt id_cnt:%d samples:%d\n",
- sco_id_cnt, i);
- res = true;
- break;
- }
- }
- brcmf_dbg(TRACE, "exit: result=%d\n", res);
- return res;
-}
-
-/**
- * btcmf_btcoex_save_part1() - save first step parameters.
- */
-static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
-{
- struct brcmf_if *ifp = btci->vif->ifp;
-
- if (!btci->saved_regs_part1) {
- /* Retrieve and save original reg value */
- brcmf_btcoex_params_read(ifp, 66, &btci->reg66);
- brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
- brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
- btci->saved_regs_part1 = true;
- brcmf_dbg(INFO,
- "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
- btci->reg66, btci->reg41,
- btci->reg68);
- }
-}
-
-/**
- * brcmf_btcoex_restore_part1() - restore first step parameters.
- */
-static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
-{
- struct brcmf_if *ifp;
-
- if (btci->saved_regs_part1) {
- btci->saved_regs_part1 = false;
- ifp = btci->vif->ifp;
- brcmf_btcoex_params_write(ifp, 66, btci->reg66);
- brcmf_btcoex_params_write(ifp, 41, btci->reg41);
- brcmf_btcoex_params_write(ifp, 68, btci->reg68);
- brcmf_dbg(INFO,
- "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
- btci->reg66, btci->reg41,
- btci->reg68);
- }
-}
-
-/**
- * brcmf_btcoex_timerfunc() - BT coex timer callback
- */
-static void brcmf_btcoex_timerfunc(ulong data)
-{
- struct brcmf_btcoex_info *bt_local = (struct brcmf_btcoex_info *)data;
- brcmf_dbg(TRACE, "enter\n");
-
- bt_local->timer_on = false;
- schedule_work(&bt_local->work);
-}
-
-/**
- * brcmf_btcoex_handler() - BT coex state machine work handler
- * @work: work
- */
-static void brcmf_btcoex_handler(struct work_struct *work)
-{
- struct brcmf_btcoex_info *btci;
- btci = container_of(work, struct brcmf_btcoex_info, work);
- if (btci->timer_on) {
- btci->timer_on = false;
- del_timer_sync(&btci->timer);
- }
-
- switch (btci->bt_state) {
- case BRCMF_BT_DHCP_START:
- /* DHCP started provide OPPORTUNITY window
- to get DHCP address
- */
- brcmf_dbg(INFO, "DHCP started\n");
- btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
- if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
- mod_timer(&btci->timer, btci->timer.expires);
- } else {
- btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;
- mod_timer(&btci->timer,
- jiffies +
- msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME));
- }
- btci->timer_on = true;
- break;
-
- case BRCMF_BT_DHCP_OPPR_WIN:
- if (btci->dhcp_done) {
- brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
- goto idle;
- }
-
- /* DHCP is not over yet, start lowering BT priority */
- brcmf_dbg(INFO, "DHCP T1:%d expired\n",
- BRCMF_BTCOEX_OPPR_WIN_TIME);
- brcmf_btcoex_boost_wifi(btci, true);
-
- btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;
- mod_timer(&btci->timer,
- jiffies + msecs_to_jiffies(btci->timeout));
- btci->timer_on = true;
- break;
-
- case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
- if (btci->dhcp_done)
- brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
- else
- brcmf_dbg(INFO, "DHCP T2:%d expired\n",
- BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
-
- goto idle;
-
- default:
- brcmf_err("invalid state=%d !!!\n", btci->bt_state);
- goto idle;
- }
-
- return;
-
-idle:
- btci->bt_state = BRCMF_BT_DHCP_IDLE;
- btci->timer_on = false;
- brcmf_btcoex_boost_wifi(btci, false);
- cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL);
- brcmf_btcoex_restore_part1(btci);
- btci->vif = NULL;
-}
-
-/**
- * brcmf_btcoex_attach() - initialize BT coex data
- * @cfg: driver private cfg80211 data
- *
- * return: 0 on success
- */
-int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg)
-{
- struct brcmf_btcoex_info *btci = NULL;
- brcmf_dbg(TRACE, "enter\n");
-
- btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL);
- if (!btci)
- return -ENOMEM;
-
- btci->bt_state = BRCMF_BT_DHCP_IDLE;
-
- /* Set up timer for BT */
- btci->timer_on = false;
- btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME;
- init_timer(&btci->timer);
- btci->timer.data = (ulong)btci;
- btci->timer.function = brcmf_btcoex_timerfunc;
- btci->cfg = cfg;
- btci->saved_regs_part1 = false;
- btci->saved_regs_part2 = false;
-
- INIT_WORK(&btci->work, brcmf_btcoex_handler);
-
- cfg->btcoex = btci;
- return 0;
-}
-
-/**
- * brcmf_btcoex_detach - clean BT coex data
- * @cfg: driver private cfg80211 data
- */
-void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)
-{
- brcmf_dbg(TRACE, "enter\n");
-
- if (!cfg->btcoex)
- return;
-
- if (cfg->btcoex->timer_on) {
- cfg->btcoex->timer_on = false;
- del_timer_sync(&cfg->btcoex->timer);
- }
-
- cancel_work_sync(&cfg->btcoex->work);
-
- brcmf_btcoex_boost_wifi(cfg->btcoex, false);
- brcmf_btcoex_restore_part1(cfg->btcoex);
-
- kfree(cfg->btcoex);
- cfg->btcoex = NULL;
-}
-
-static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci)
-{
- struct brcmf_if *ifp = btci->vif->ifp;
-
- btcmf_btcoex_save_part1(btci);
- /* set new regs values */
- brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66);
- brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41);
- brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68);
- btci->dhcp_done = false;
- btci->bt_state = BRCMF_BT_DHCP_START;
- schedule_work(&btci->work);
- brcmf_dbg(TRACE, "enable BT DHCP Timer\n");
-}
-
-static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
-{
- /* Stop any bt timer because DHCP session is done */
- btci->dhcp_done = true;
- if (btci->timer_on) {
- brcmf_dbg(INFO, "disable BT DHCP Timer\n");
- btci->timer_on = false;
- del_timer_sync(&btci->timer);
-
- /* schedule worker if transition to IDLE is needed */
- if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
- brcmf_dbg(INFO, "bt_state:%d\n",
- btci->bt_state);
- schedule_work(&btci->work);
- }
- } else {
- /* Restore original values */
- brcmf_btcoex_restore_part1(btci);
- }
-}
-
-/**
- * brcmf_btcoex_set_mode - set BT coex mode
- * @cfg: driver private cfg80211 data
- * @mode: Wifi-Bluetooth coexistence mode
- *
- * return: 0 on success
- */
-int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
- enum brcmf_btcoex_mode mode, u16 duration)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
- struct brcmf_btcoex_info *btci = cfg->btcoex;
- struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
-
- switch (mode) {
- case BRCMF_BTCOEX_DISABLED:
- brcmf_dbg(INFO, "DHCP session starts\n");
- if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
- return -EBUSY;
- /* Start BT timer only for SCO connection */
- if (brcmf_btcoex_is_sco_active(ifp)) {
- btci->timeout = duration;
- btci->vif = vif;
- brcmf_btcoex_dhcp_start(btci);
- }
- break;
-
- case BRCMF_BTCOEX_ENABLED:
- brcmf_dbg(INFO, "DHCP session ends\n");
- if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
- vif == btci->vif) {
- brcmf_btcoex_dhcp_end(btci);
- }
- break;
- default:
- brcmf_dbg(INFO, "Unknown mode, ignored\n");
- }
- return 0;
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
deleted file mode 100644
index 230cad788ace..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef BRCMFMAC_BUS_H
-#define BRCMFMAC_BUS_H
-
-#include "debug.h"
-
-/* IDs of the 6 default common rings of msgbuf protocol */
-#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0
-#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1
-#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2
-#define BRCMF_D2H_MSGRING_TX_COMPLETE 3
-#define BRCMF_D2H_MSGRING_RX_COMPLETE 4
-
-#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2
-#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3
-#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \
- BRCMF_NROF_D2H_COMMON_MSGRINGS)
-
-/* The level of bus communication with the dongle */
-enum brcmf_bus_state {
- BRCMF_BUS_DOWN, /* Not ready for frame transfers */
- BRCMF_BUS_UP /* Ready for frame transfers */
-};
-
-/* The level of bus communication with the dongle */
-enum brcmf_bus_protocol_type {
- BRCMF_PROTO_BCDC,
- BRCMF_PROTO_MSGBUF
-};
-
-struct brcmf_bus_dcmd {
- char *name;
- char *param;
- int param_len;
- struct list_head list;
-};
-
-/**
- * struct brcmf_bus_ops - bus callback operations.
- *
- * @preinit: execute bus/device specific dongle init commands (optional).
- * @init: prepare for communication with dongle.
- * @stop: clear pending frames, disable data flow.
- * @txdata: send a data frame to the dongle. When the data
- * has been transferred, the common driver must be
- * notified using brcmf_txcomplete(). The common
- * driver calls this function with interrupts
- * disabled.
- * @txctl: transmit a control request message to dongle.
- * @rxctl: receive a control response message from dongle.
- * @gettxq: obtain a reference of bus transmit queue (optional).
- * @wowl_config: specify if dongle is configured for wowl when going to suspend
- * @get_ramsize: obtain size of device memory.
- * @get_memdump: obtain device memory dump in provided buffer.
- *
- * This structure provides an abstract interface towards the
- * bus specific driver. For control messages to common driver
- * will assure there is only one active transaction. Unless
- * indicated otherwise these callbacks are mandatory.
- */
-struct brcmf_bus_ops {
- int (*preinit)(struct device *dev);
- void (*stop)(struct device *dev);
- int (*txdata)(struct device *dev, struct sk_buff *skb);
- int (*txctl)(struct device *dev, unsigned char *msg, uint len);
- int (*rxctl)(struct device *dev, unsigned char *msg, uint len);
- struct pktq * (*gettxq)(struct device *dev);
- void (*wowl_config)(struct device *dev, bool enabled);
- size_t (*get_ramsize)(struct device *dev);
- int (*get_memdump)(struct device *dev, void *data, size_t len);
-};
-
-
-/**
- * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf.
- *
- * @commonrings: commonrings which are always there.
- * @flowrings: commonrings which are dynamically created and destroyed for data.
- * @rx_dataoffset: if set then all rx data has this this offset.
- * @max_rxbufpost: maximum number of buffers to post for rx.
- * @nrof_flowrings: number of flowrings.
- */
-struct brcmf_bus_msgbuf {
- struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS];
- struct brcmf_commonring **flowrings;
- u32 rx_dataoffset;
- u32 max_rxbufpost;
- u32 nrof_flowrings;
-};
-
-
-/**
- * struct brcmf_bus - interface structure between common and bus layer
- *
- * @bus_priv: pointer to private bus device.
- * @proto_type: protocol type, bcdc or msgbuf
- * @dev: device pointer of bus device.
- * @drvr: public driver information.
- * @state: operational state of the bus interface.
- * @maxctl: maximum size for rxctl request message.
- * @tx_realloc: number of tx packets realloced for headroom.
- * @dstats: dongle-based statistical data.
- * @dcmd_list: bus/device specific dongle initialization commands.
- * @chip: device identifier of the dongle chip.
- * @wowl_supported: is wowl supported by bus driver.
- * @chiprev: revision of the dongle chip.
- */
-struct brcmf_bus {
- union {
- struct brcmf_sdio_dev *sdio;
- struct brcmf_usbdev *usb;
- struct brcmf_pciedev *pcie;
- } bus_priv;
- enum brcmf_bus_protocol_type proto_type;
- struct device *dev;
- struct brcmf_pub *drvr;
- enum brcmf_bus_state state;
- uint maxctl;
- unsigned long tx_realloc;
- u32 chip;
- u32 chiprev;
- bool always_use_fws_queue;
- bool wowl_supported;
-
- struct brcmf_bus_ops *ops;
- struct brcmf_bus_msgbuf *msgbuf;
-};
-
-/*
- * callback wrappers
- */
-static inline int brcmf_bus_preinit(struct brcmf_bus *bus)
-{
- if (!bus->ops->preinit)
- return 0;
- return bus->ops->preinit(bus->dev);
-}
-
-static inline void brcmf_bus_stop(struct brcmf_bus *bus)
-{
- bus->ops->stop(bus->dev);
-}
-
-static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb)
-{
- return bus->ops->txdata(bus->dev, skb);
-}
-
-static inline
-int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len)
-{
- return bus->ops->txctl(bus->dev, msg, len);
-}
-
-static inline
-int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len)
-{
- return bus->ops->rxctl(bus->dev, msg, len);
-}
-
-static inline
-struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus)
-{
- if (!bus->ops->gettxq)
- return ERR_PTR(-ENOENT);
-
- return bus->ops->gettxq(bus->dev);
-}
-
-static inline
-void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled)
-{
- if (bus->ops->wowl_config)
- bus->ops->wowl_config(bus->dev, enabled);
-}
-
-static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus)
-{
- if (!bus->ops->get_ramsize)
- return 0;
-
- return bus->ops->get_ramsize(bus->dev);
-}
-
-static inline
-int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
-{
- if (!bus->ops->get_memdump)
- return -EOPNOTSUPP;
-
- return bus->ops->get_memdump(bus->dev, data, len);
-}
-
-/*
- * interface functions from common layer
- */
-
-bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt,
- int prec);
-
-/* Receive frame for delivery to OS. Callee disposes of rxp. */
-void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
-
-/* Indication from bus module regarding presence/insertion of dongle. */
-int brcmf_attach(struct device *dev);
-/* Indication from bus module regarding removal/absence of dongle */
-void brcmf_detach(struct device *dev);
-/* Indication from bus module that dongle should be reset */
-void brcmf_dev_reset(struct device *dev);
-/* Indication from bus module to change flow-control state */
-void brcmf_txflowblock(struct device *dev, bool state);
-
-/* Notify the bus has transferred the tx packet to firmware */
-void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success);
-
-/* Configure the "global" bus state used by upper layers */
-void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
-
-int brcmf_bus_start(struct device *dev);
-s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
-void brcmf_bus_add_txhdrlen(struct device *dev, uint len);
-
-#ifdef CONFIG_BRCMFMAC_SDIO
-void brcmf_sdio_exit(void);
-void brcmf_sdio_init(void);
-void brcmf_sdio_register(void);
-#endif
-#ifdef CONFIG_BRCMFMAC_USB
-void brcmf_usb_exit(void);
-void brcmf_usb_register(void);
-#endif
-
-#endif /* BRCMFMAC_BUS_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
deleted file mode 100644
index deb5f78dcacc..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ /dev/null
@@ -1,6357 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
-
-#include <linux/kernel.h>
-#include <linux/etherdevice.h>
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-#include <net/cfg80211.h>
-#include <net/netlink.h>
-
-#include <brcmu_utils.h>
-#include <defs.h>
-#include <brcmu_wifi.h>
-#include "core.h"
-#include "debug.h"
-#include "tracepoint.h"
-#include "fwil_types.h"
-#include "p2p.h"
-#include "btcoex.h"
-#include "cfg80211.h"
-#include "feature.h"
-#include "fwil.h"
-#include "proto.h"
-#include "vendor.h"
-#include "bus.h"
-#include "common.h"
-
-#define BRCMF_SCAN_IE_LEN_MAX 2048
-#define BRCMF_PNO_VERSION 2
-#define BRCMF_PNO_TIME 30
-#define BRCMF_PNO_REPEAT 4
-#define BRCMF_PNO_FREQ_EXPO_MAX 3
-#define BRCMF_PNO_MAX_PFN_COUNT 16
-#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
-#define BRCMF_PNO_HIDDEN_BIT 2
-#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
-#define BRCMF_PNO_SCAN_COMPLETE 1
-#define BRCMF_PNO_SCAN_INCOMPLETE 0
-
-#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
-#define WPA_OUI_TYPE 1
-#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
-#define WME_OUI_TYPE 2
-#define WPS_OUI_TYPE 4
-
-#define VS_IE_FIXED_HDR_LEN 6
-#define WPA_IE_VERSION_LEN 2
-#define WPA_IE_MIN_OUI_LEN 4
-#define WPA_IE_SUITE_COUNT_LEN 2
-
-#define WPA_CIPHER_NONE 0 /* None */
-#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
-#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
-#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
-#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
-
-#define RSN_AKM_NONE 0 /* None (IBSS) */
-#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
-#define RSN_AKM_PSK 2 /* Pre-shared Key */
-#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
-#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
-
-#define VNDR_IE_CMD_LEN 4 /* length of the set command
- * string :"add", "del" (+ NUL)
- */
-#define VNDR_IE_COUNT_OFFSET 4
-#define VNDR_IE_PKTFLAG_OFFSET 8
-#define VNDR_IE_VSIE_OFFSET 12
-#define VNDR_IE_HDR_SIZE 12
-#define VNDR_IE_PARSE_LIMIT 5
-
-#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
-#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
-
-#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
-#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
-#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
-
-#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
- (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
-
-static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
-{
- if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
- brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
- vif->sme_state);
- return false;
- }
- return true;
-}
-
-#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
-#define RATETAB_ENT(_rateid, _flags) \
- { \
- .bitrate = RATE_TO_BASE100KBPS(_rateid), \
- .hw_value = (_rateid), \
- .flags = (_flags), \
- }
-
-static struct ieee80211_rate __wl_rates[] = {
- RATETAB_ENT(BRCM_RATE_1M, 0),
- RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
- RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
- RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
- RATETAB_ENT(BRCM_RATE_6M, 0),
- RATETAB_ENT(BRCM_RATE_9M, 0),
- RATETAB_ENT(BRCM_RATE_12M, 0),
- RATETAB_ENT(BRCM_RATE_18M, 0),
- RATETAB_ENT(BRCM_RATE_24M, 0),
- RATETAB_ENT(BRCM_RATE_36M, 0),
- RATETAB_ENT(BRCM_RATE_48M, 0),
- RATETAB_ENT(BRCM_RATE_54M, 0),
-};
-
-#define wl_g_rates (__wl_rates + 0)
-#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
-#define wl_a_rates (__wl_rates + 4)
-#define wl_a_rates_size (wl_g_rates_size - 4)
-
-#define CHAN2G(_channel, _freq) { \
- .band = IEEE80211_BAND_2GHZ, \
- .center_freq = (_freq), \
- .hw_value = (_channel), \
- .flags = IEEE80211_CHAN_DISABLED, \
- .max_antenna_gain = 0, \
- .max_power = 30, \
-}
-
-#define CHAN5G(_channel) { \
- .band = IEEE80211_BAND_5GHZ, \
- .center_freq = 5000 + (5 * (_channel)), \
- .hw_value = (_channel), \
- .flags = IEEE80211_CHAN_DISABLED, \
- .max_antenna_gain = 0, \
- .max_power = 30, \
-}
-
-static struct ieee80211_channel __wl_2ghz_channels[] = {
- CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
- CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
- CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
- CHAN2G(13, 2472), CHAN2G(14, 2484)
-};
-
-static struct ieee80211_channel __wl_5ghz_channels[] = {
- CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
- CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
- CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
- CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
- CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
- CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
-};
-
-/* Band templates duplicated per wiphy. The channel info
- * above is added to the band during setup.
- */
-static const struct ieee80211_supported_band __wl_band_2ghz = {
- .band = IEEE80211_BAND_2GHZ,
- .bitrates = wl_g_rates,
- .n_bitrates = wl_g_rates_size,
-};
-
-static const struct ieee80211_supported_band __wl_band_5ghz = {
- .band = IEEE80211_BAND_5GHZ,
- .bitrates = wl_a_rates,
- .n_bitrates = wl_a_rates_size,
-};
-
-/* This is to override regulatory domains defined in cfg80211 module (reg.c)
- * By default world regulatory domain defined in reg.c puts the flags
- * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
- * With respect to these flags, wpa_supplicant doesn't * start p2p
- * operations on 5GHz channels. All the changes in world regulatory
- * domain are to be done here.
- */
-static const struct ieee80211_regdomain brcmf_regdom = {
- .n_reg_rules = 4,
- .alpha2 = "99",
- .reg_rules = {
- /* IEEE 802.11b/g, channels 1..11 */
- REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
- /* If any */
- /* IEEE 802.11 channel 14 - Only JP enables
- * this and for 802.11b only
- */
- REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
- /* IEEE 802.11a, channel 36..64 */
- REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
- /* IEEE 802.11a, channel 100..165 */
- REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
-};
-
-static const u32 __wl_cipher_suites[] = {
- WLAN_CIPHER_SUITE_WEP40,
- WLAN_CIPHER_SUITE_WEP104,
- WLAN_CIPHER_SUITE_TKIP,
- WLAN_CIPHER_SUITE_CCMP,
- WLAN_CIPHER_SUITE_AES_CMAC,
-};
-
-/* Vendor specific ie. id = 221, oui and type defines exact ie */
-struct brcmf_vs_tlv {
- u8 id;
- u8 len;
- u8 oui[3];
- u8 oui_type;
-};
-
-struct parsed_vndr_ie_info {
- u8 *ie_ptr;
- u32 ie_len; /* total length including id & length field */
- struct brcmf_vs_tlv vndrie;
-};
-
-struct parsed_vndr_ies {
- u32 count;
- struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
-};
-
-static int brcmf_roamoff;
-module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
-MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
-
-
-static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
- struct cfg80211_chan_def *ch)
-{
- struct brcmu_chan ch_inf;
- s32 primary_offset;
-
- brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
- ch->chan->center_freq, ch->center_freq1, ch->width);
- ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
- primary_offset = ch->center_freq1 - ch->chan->center_freq;
- switch (ch->width) {
- case NL80211_CHAN_WIDTH_20:
- case NL80211_CHAN_WIDTH_20_NOHT:
- ch_inf.bw = BRCMU_CHAN_BW_20;
- WARN_ON(primary_offset != 0);
- break;
- case NL80211_CHAN_WIDTH_40:
- ch_inf.bw = BRCMU_CHAN_BW_40;
- if (primary_offset < 0)
- ch_inf.sb = BRCMU_CHAN_SB_U;
- else
- ch_inf.sb = BRCMU_CHAN_SB_L;
- break;
- case NL80211_CHAN_WIDTH_80:
- ch_inf.bw = BRCMU_CHAN_BW_80;
- if (primary_offset < 0) {
- if (primary_offset < -CH_10MHZ_APART)
- ch_inf.sb = BRCMU_CHAN_SB_UU;
- else
- ch_inf.sb = BRCMU_CHAN_SB_UL;
- } else {
- if (primary_offset > CH_10MHZ_APART)
- ch_inf.sb = BRCMU_CHAN_SB_LL;
- else
- ch_inf.sb = BRCMU_CHAN_SB_LU;
- }
- break;
- case NL80211_CHAN_WIDTH_80P80:
- case NL80211_CHAN_WIDTH_160:
- case NL80211_CHAN_WIDTH_5:
- case NL80211_CHAN_WIDTH_10:
- default:
- WARN_ON_ONCE(1);
- }
- switch (ch->chan->band) {
- case IEEE80211_BAND_2GHZ:
- ch_inf.band = BRCMU_CHAN_BAND_2G;
- break;
- case IEEE80211_BAND_5GHZ:
- ch_inf.band = BRCMU_CHAN_BAND_5G;
- break;
- case IEEE80211_BAND_60GHZ:
- default:
- WARN_ON_ONCE(1);
- }
- d11inf->encchspec(&ch_inf);
-
- return ch_inf.chspec;
-}
-
-u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
- struct ieee80211_channel *ch)
-{
- struct brcmu_chan ch_inf;
-
- ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
- ch_inf.bw = BRCMU_CHAN_BW_20;
- d11inf->encchspec(&ch_inf);
-
- return ch_inf.chspec;
-}
-
-/* Traverse a string of 1-byte tag/1-byte length/variable-length value
- * triples, returning a pointer to the substring whose first element
- * matches tag
- */
-const struct brcmf_tlv *
-brcmf_parse_tlvs(const void *buf, int buflen, uint key)
-{
- const struct brcmf_tlv *elt = buf;
- int totlen = buflen;
-
- /* find tagged parameter */
- while (totlen >= TLV_HDR_LEN) {
- int len = elt->len;
-
- /* validate remaining totlen */
- if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
- return elt;
-
- elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
- totlen -= (len + TLV_HDR_LEN);
- }
-
- return NULL;
-}
-
-/* Is any of the tlvs the expected entry? If
- * not update the tlvs buffer pointer/length.
- */
-static bool
-brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
- const u8 *oui, u32 oui_len, u8 type)
-{
- /* If the contents match the OUI and the type */
- if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
- !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
- type == ie[TLV_BODY_OFF + oui_len]) {
- return true;
- }
-
- if (tlvs == NULL)
- return false;
- /* point to the next ie */
- ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
- /* calculate the length of the rest of the buffer */
- *tlvs_len -= (int)(ie - *tlvs);
- /* update the pointer to the start of the buffer */
- *tlvs = ie;
-
- return false;
-}
-
-static struct brcmf_vs_tlv *
-brcmf_find_wpaie(const u8 *parse, u32 len)
-{
- const struct brcmf_tlv *ie;
-
- while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
- if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
- WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
- return (struct brcmf_vs_tlv *)ie;
- }
- return NULL;
-}
-
-static struct brcmf_vs_tlv *
-brcmf_find_wpsie(const u8 *parse, u32 len)
-{
- const struct brcmf_tlv *ie;
-
- while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
- if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
- WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
- return (struct brcmf_vs_tlv *)ie;
- }
- return NULL;
-}
-
-static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
- struct brcmf_cfg80211_vif *vif,
- enum nl80211_iftype new_type)
-{
- int iftype_num[NUM_NL80211_IFTYPES];
- struct brcmf_cfg80211_vif *pos;
-
- memset(&iftype_num[0], 0, sizeof(iftype_num));
- list_for_each_entry(pos, &cfg->vif_list, list)
- if (pos == vif)
- iftype_num[new_type]++;
- else
- iftype_num[pos->wdev.iftype]++;
-
- return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
-}
-
-static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
- enum nl80211_iftype new_type)
-{
- int iftype_num[NUM_NL80211_IFTYPES];
- struct brcmf_cfg80211_vif *pos;
-
- memset(&iftype_num[0], 0, sizeof(iftype_num));
- list_for_each_entry(pos, &cfg->vif_list, list)
- iftype_num[pos->wdev.iftype]++;
-
- iftype_num[new_type]++;
- return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
-}
-
-static void convert_key_from_CPU(struct brcmf_wsec_key *key,
- struct brcmf_wsec_key_le *key_le)
-{
- key_le->index = cpu_to_le32(key->index);
- key_le->len = cpu_to_le32(key->len);
- key_le->algo = cpu_to_le32(key->algo);
- key_le->flags = cpu_to_le32(key->flags);
- key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
- key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
- key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
- memcpy(key_le->data, key->data, sizeof(key->data));
- memcpy(key_le->ea, key->ea, sizeof(key->ea));
-}
-
-static int
-send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
-{
- int err;
- struct brcmf_wsec_key_le key_le;
-
- convert_key_from_CPU(key, &key_le);
-
- brcmf_netdev_wait_pend8021x(ifp);
-
- err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
- sizeof(key_le));
-
- if (err)
- brcmf_err("wsec_key error (%d)\n", err);
- return err;
-}
-
-static s32
-brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
-{
- s32 err;
- u32 mode;
-
- if (enable)
- mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
- else
- mode = 0;
-
- /* Try to set and enable ARP offload feature, this may fail, then it */
- /* is simply not supported and err 0 will be returned */
- err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
- if (err) {
- brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
- mode, err);
- err = 0;
- } else {
- err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
- if (err) {
- brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
- enable, err);
- err = 0;
- } else
- brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
- enable, mode);
- }
-
- return err;
-}
-
-static void
-brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
-{
- struct brcmf_cfg80211_vif *vif;
- struct brcmf_if *ifp;
-
- vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
- ifp = vif->ifp;
-
- if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
- (wdev->iftype == NL80211_IFTYPE_AP) ||
- (wdev->iftype == NL80211_IFTYPE_P2P_GO))
- brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
- ADDR_DIRECT);
- else
- brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
- ADDR_INDIRECT);
-}
-
-static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
-{
- struct brcmf_mbss_ssid_le mbss_ssid_le;
- int bsscfgidx;
- int err;
-
- memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
- bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
- if (bsscfgidx < 0)
- return bsscfgidx;
-
- mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
- mbss_ssid_le.SSID_len = cpu_to_le32(5);
- sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
-
- err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
- sizeof(mbss_ssid_le));
- if (err < 0)
- brcmf_err("setting ssid failed %d\n", err);
-
- return err;
-}
-
-/**
- * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
- *
- * @wiphy: wiphy device of new interface.
- * @name: name of the new interface.
- * @flags: not used.
- * @params: contains mac address for AP device.
- */
-static
-struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
- u32 *flags, struct vif_params *params)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- struct brcmf_cfg80211_vif *vif;
- int err;
-
- if (brcmf_cfg80211_vif_event_armed(cfg))
- return ERR_PTR(-EBUSY);
-
- brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
-
- vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
- if (IS_ERR(vif))
- return (struct wireless_dev *)vif;
-
- brcmf_cfg80211_arm_vif_event(cfg, vif);
-
- err = brcmf_cfg80211_request_ap_if(ifp);
- if (err) {
- brcmf_cfg80211_arm_vif_event(cfg, NULL);
- goto fail;
- }
-
- /* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
- msecs_to_jiffies(1500));
- brcmf_cfg80211_arm_vif_event(cfg, NULL);
- if (!err) {
- brcmf_err("timeout occurred\n");
- err = -EIO;
- goto fail;
- }
-
- /* interface created in firmware */
- ifp = vif->ifp;
- if (!ifp) {
- brcmf_err("no if pointer provided\n");
- err = -ENOENT;
- goto fail;
- }
-
- strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
- err = brcmf_net_attach(ifp, true);
- if (err) {
- brcmf_err("Registering netdevice failed\n");
- goto fail;
- }
-
- return &ifp->vif->wdev;
-
-fail:
- brcmf_free_vif(vif);
- return ERR_PTR(err);
-}
-
-static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
-{
- enum nl80211_iftype iftype;
-
- iftype = vif->wdev.iftype;
- return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
-}
-
-static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
-{
- return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
-}
-
-static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
- const char *name,
- unsigned char name_assign_type,
- enum nl80211_iftype type,
- u32 *flags,
- struct vif_params *params)
-{
- struct wireless_dev *wdev;
- int err;
-
- brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
- err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
- if (err) {
- brcmf_err("iface validation failed: err=%d\n", err);
- return ERR_PTR(err);
- }
- switch (type) {
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_WDS:
- case NL80211_IFTYPE_MONITOR:
- case NL80211_IFTYPE_MESH_POINT:
- return ERR_PTR(-EOPNOTSUPP);
- case NL80211_IFTYPE_AP:
- wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
- if (!IS_ERR(wdev))
- brcmf_cfg80211_update_proto_addr_mode(wdev);
- return wdev;
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- case NL80211_IFTYPE_P2P_DEVICE:
- wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
- if (!IS_ERR(wdev))
- brcmf_cfg80211_update_proto_addr_mode(wdev);
- return wdev;
- case NL80211_IFTYPE_UNSPECIFIED:
- default:
- return ERR_PTR(-EINVAL);
- }
-}
-
-static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
-{
- if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
- brcmf_set_mpc(ifp, mpc);
-}
-
-void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
-{
- s32 err = 0;
-
- if (check_vif_up(ifp->vif)) {
- err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
- if (err) {
- brcmf_err("fail to set mpc\n");
- return;
- }
- brcmf_dbg(INFO, "MPC : %d\n", mpc);
- }
-}
-
-s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
- struct brcmf_if *ifp, bool aborted,
- bool fw_abort)
-{
- struct brcmf_scan_params_le params_le;
- struct cfg80211_scan_request *scan_request;
- s32 err = 0;
-
- brcmf_dbg(SCAN, "Enter\n");
-
- /* clear scan request, because the FW abort can cause a second call */
- /* to this functon and might cause a double cfg80211_scan_done */
- scan_request = cfg->scan_request;
- cfg->scan_request = NULL;
-
- if (timer_pending(&cfg->escan_timeout))
- del_timer_sync(&cfg->escan_timeout);
-
- if (fw_abort) {
- /* Do a scan abort to stop the driver's scan engine */
- brcmf_dbg(SCAN, "ABORT scan in firmware\n");
- memset(&params_le, 0, sizeof(params_le));
- eth_broadcast_addr(params_le.bssid);
- params_le.bss_type = DOT11_BSSTYPE_ANY;
- params_le.scan_type = 0;
- params_le.channel_num = cpu_to_le32(1);
- params_le.nprobes = cpu_to_le32(1);
- params_le.active_time = cpu_to_le32(-1);
- params_le.passive_time = cpu_to_le32(-1);
- params_le.home_time = cpu_to_le32(-1);
- /* Scan is aborted by setting channel_list[0] to -1 */
- params_le.channel_list[0] = cpu_to_le16(-1);
- /* E-Scan (or anyother type) can be aborted by SCAN */
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
- &params_le, sizeof(params_le));
- if (err)
- brcmf_err("Scan abort failed\n");
- }
-
- brcmf_scan_config_mpc(ifp, 1);
-
- /*
- * e-scan can be initiated by scheduled scan
- * which takes precedence.
- */
- if (cfg->sched_escan) {
- brcmf_dbg(SCAN, "scheduled scan completed\n");
- cfg->sched_escan = false;
- if (!aborted)
- cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
- } else if (scan_request) {
- brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
- aborted ? "Aborted" : "Done");
- cfg80211_scan_done(scan_request, aborted);
- }
- if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
- brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
-
- return err;
-}
-
-static
-int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct net_device *ndev = wdev->netdev;
-
- /* vif event pending in firmware */
- if (brcmf_cfg80211_vif_event_armed(cfg))
- return -EBUSY;
-
- if (ndev) {
- if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
- cfg->escan_info.ifp == netdev_priv(ndev))
- brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
- true, true);
-
- brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
- }
-
- switch (wdev->iftype) {
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_WDS:
- case NL80211_IFTYPE_MONITOR:
- case NL80211_IFTYPE_MESH_POINT:
- return -EOPNOTSUPP;
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- case NL80211_IFTYPE_P2P_DEVICE:
- return brcmf_p2p_del_vif(wiphy, wdev);
- case NL80211_IFTYPE_UNSPECIFIED:
- default:
- return -EINVAL;
- }
- return -EOPNOTSUPP;
-}
-
-static s32
-brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_vif *vif = ifp->vif;
- s32 infra = 0;
- s32 ap = 0;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type);
-
- /* WAR: There are a number of p2p interface related problems which
- * need to be handled initially (before doing the validate).
- * wpa_supplicant tends to do iface changes on p2p device/client/go
- * which are not always possible/allowed. However we need to return
- * OK otherwise the wpa_supplicant wont start. The situation differs
- * on configuration and setup (p2pon=1 module param). The first check
- * is to see if the request is a change to station for p2p iface.
- */
- if ((type == NL80211_IFTYPE_STATION) &&
- ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
- (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
- (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
- brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
- /* Now depending on whether module param p2pon=1 was used the
- * response needs to be either 0 or EOPNOTSUPP. The reason is
- * that if p2pon=1 is used, but a newer supplicant is used then
- * we should return an error, as this combination wont work.
- * In other situations 0 is returned and supplicant will start
- * normally. It will give a trace in cfg80211, but it is the
- * only way to get it working. Unfortunately this will result
- * in situation where we wont support new supplicant in
- * combination with module param p2pon=1, but that is the way
- * it is. If the user tries this then unloading of driver might
- * fail/lock.
- */
- if (cfg->p2p.p2pdev_dynamically)
- return -EOPNOTSUPP;
- else
- return 0;
- }
- err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
- if (err) {
- brcmf_err("iface validation failed: err=%d\n", err);
- return err;
- }
- switch (type) {
- case NL80211_IFTYPE_MONITOR:
- case NL80211_IFTYPE_WDS:
- brcmf_err("type (%d) : currently we do not support this type\n",
- type);
- return -EOPNOTSUPP;
- case NL80211_IFTYPE_ADHOC:
- infra = 0;
- break;
- case NL80211_IFTYPE_STATION:
- infra = 1;
- break;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_P2P_GO:
- ap = 1;
- break;
- default:
- err = -EINVAL;
- goto done;
- }
-
- if (ap) {
- if (type == NL80211_IFTYPE_P2P_GO) {
- brcmf_dbg(INFO, "IF Type = P2P GO\n");
- err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
- }
- if (!err) {
- brcmf_dbg(INFO, "IF Type = AP\n");
- }
- } else {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
- if (err) {
- brcmf_err("WLC_SET_INFRA error (%d)\n", err);
- err = -EAGAIN;
- goto done;
- }
- brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
- "Adhoc" : "Infra");
- }
- ndev->ieee80211_ptr->iftype = type;
-
- brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
-
-done:
- brcmf_dbg(TRACE, "Exit\n");
-
- return err;
-}
-
-static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
- struct brcmf_scan_params_le *params_le,
- struct cfg80211_scan_request *request)
-{
- u32 n_ssids;
- u32 n_channels;
- s32 i;
- s32 offset;
- u16 chanspec;
- char *ptr;
- struct brcmf_ssid_le ssid_le;
-
- eth_broadcast_addr(params_le->bssid);
- params_le->bss_type = DOT11_BSSTYPE_ANY;
- params_le->scan_type = 0;
- params_le->channel_num = 0;
- params_le->nprobes = cpu_to_le32(-1);
- params_le->active_time = cpu_to_le32(-1);
- params_le->passive_time = cpu_to_le32(-1);
- params_le->home_time = cpu_to_le32(-1);
- memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
-
- /* if request is null exit so it will be all channel broadcast scan */
- if (!request)
- return;
-
- n_ssids = request->n_ssids;
- n_channels = request->n_channels;
- /* Copy channel array if applicable */
- brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
- n_channels);
- if (n_channels > 0) {
- for (i = 0; i < n_channels; i++) {
- chanspec = channel_to_chanspec(&cfg->d11inf,
- request->channels[i]);
- brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
- request->channels[i]->hw_value, chanspec);
- params_le->channel_list[i] = cpu_to_le16(chanspec);
- }
- } else {
- brcmf_dbg(SCAN, "Scanning all channels\n");
- }
- /* Copy ssid array if applicable */
- brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
- if (n_ssids > 0) {
- offset = offsetof(struct brcmf_scan_params_le, channel_list) +
- n_channels * sizeof(u16);
- offset = roundup(offset, sizeof(u32));
- ptr = (char *)params_le + offset;
- for (i = 0; i < n_ssids; i++) {
- memset(&ssid_le, 0, sizeof(ssid_le));
- ssid_le.SSID_len =
- cpu_to_le32(request->ssids[i].ssid_len);
- memcpy(ssid_le.SSID, request->ssids[i].ssid,
- request->ssids[i].ssid_len);
- if (!ssid_le.SSID_len)
- brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
- else
- brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
- i, ssid_le.SSID, ssid_le.SSID_len);
- memcpy(ptr, &ssid_le, sizeof(ssid_le));
- ptr += sizeof(ssid_le);
- }
- } else {
- brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
- if ((request->ssids) && request->ssids->ssid_len) {
- brcmf_dbg(SCAN, "SSID %s len=%d\n",
- params_le->ssid_le.SSID,
- request->ssids->ssid_len);
- params_le->ssid_le.SSID_len =
- cpu_to_le32(request->ssids->ssid_len);
- memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
- request->ssids->ssid_len);
- }
- }
- /* Adding mask to channel numbers */
- params_le->channel_num =
- cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
- (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
-}
-
-static s32
-brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
- struct cfg80211_scan_request *request, u16 action)
-{
- s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
- offsetof(struct brcmf_escan_params_le, params_le);
- struct brcmf_escan_params_le *params;
- s32 err = 0;
-
- brcmf_dbg(SCAN, "E-SCAN START\n");
-
- if (request != NULL) {
- /* Allocate space for populating ssids in struct */
- params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
-
- /* Allocate space for populating ssids in struct */
- params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
- }
-
- params = kzalloc(params_size, GFP_KERNEL);
- if (!params) {
- err = -ENOMEM;
- goto exit;
- }
- BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
- brcmf_escan_prep(cfg, &params->params_le, request);
- params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
- params->action = cpu_to_le16(action);
- params->sync_id = cpu_to_le16(0x1234);
-
- err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
- if (err) {
- if (err == -EBUSY)
- brcmf_dbg(INFO, "system busy : escan canceled\n");
- else
- brcmf_err("error (%d)\n", err);
- }
-
- kfree(params);
-exit:
- return err;
-}
-
-static s32
-brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
- struct brcmf_if *ifp, struct cfg80211_scan_request *request)
-{
- s32 err;
- u32 passive_scan;
- struct brcmf_scan_results *results;
- struct escan_info *escan = &cfg->escan_info;
-
- brcmf_dbg(SCAN, "Enter\n");
- escan->ifp = ifp;
- escan->wiphy = wiphy;
- escan->escan_state = WL_ESCAN_STATE_SCANNING;
- passive_scan = cfg->active_scan ? 0 : 1;
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
- passive_scan);
- if (err) {
- brcmf_err("error (%d)\n", err);
- return err;
- }
- brcmf_scan_config_mpc(ifp, 0);
- results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
- results->version = 0;
- results->count = 0;
- results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
-
- err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
- if (err)
- brcmf_scan_config_mpc(ifp, 1);
- return err;
-}
-
-static s32
-brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
- struct cfg80211_scan_request *request,
- struct cfg80211_ssid *this_ssid)
-{
- struct brcmf_if *ifp = vif->ifp;
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct cfg80211_ssid *ssids;
- struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
- u32 passive_scan;
- bool escan_req;
- bool spec_scan;
- s32 err;
- u32 SSID_len;
-
- brcmf_dbg(SCAN, "START ESCAN\n");
-
- if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
- brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
- return -EAGAIN;
- }
- if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
- brcmf_err("Scanning being aborted: status (%lu)\n",
- cfg->scan_status);
- return -EAGAIN;
- }
- if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
- brcmf_err("Scanning suppressed: status (%lu)\n",
- cfg->scan_status);
- return -EAGAIN;
- }
- if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
- brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
- return -EAGAIN;
- }
-
- /* If scan req comes for p2p0, send it over primary I/F */
- if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
- vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
-
- escan_req = false;
- if (request) {
- /* scan bss */
- ssids = request->ssids;
- escan_req = true;
- } else {
- /* scan in ibss */
- /* we don't do escan in ibss */
- ssids = this_ssid;
- }
-
- cfg->scan_request = request;
- set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
- if (escan_req) {
- cfg->escan_info.run = brcmf_run_escan;
- err = brcmf_p2p_scan_prep(wiphy, request, vif);
- if (err)
- goto scan_out;
-
- err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
- if (err)
- goto scan_out;
- } else {
- brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
- ssids->ssid, ssids->ssid_len);
- memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
- SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
- sr->ssid_le.SSID_len = cpu_to_le32(0);
- spec_scan = false;
- if (SSID_len) {
- memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
- sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
- spec_scan = true;
- } else
- brcmf_dbg(SCAN, "Broadcast scan\n");
-
- passive_scan = cfg->active_scan ? 0 : 1;
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
- passive_scan);
- if (err) {
- brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
- goto scan_out;
- }
- brcmf_scan_config_mpc(ifp, 0);
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
- &sr->ssid_le, sizeof(sr->ssid_le));
- if (err) {
- if (err == -EBUSY)
- brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
- sr->ssid_le.SSID);
- else
- brcmf_err("WLC_SCAN error (%d)\n", err);
-
- brcmf_scan_config_mpc(ifp, 1);
- goto scan_out;
- }
- }
-
- /* Arm scan timeout timer */
- mod_timer(&cfg->escan_timeout, jiffies +
- WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
-
- return 0;
-
-scan_out:
- clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
- cfg->scan_request = NULL;
- return err;
-}
-
-static s32
-brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
-{
- struct brcmf_cfg80211_vif *vif;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
- if (!check_vif_up(vif))
- return -EIO;
-
- err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
-
- if (err)
- brcmf_err("scan error (%d)\n", err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
-{
- s32 err = 0;
-
- err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
- rts_threshold);
- if (err)
- brcmf_err("Error (%d)\n", err);
-
- return err;
-}
-
-static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
-{
- s32 err = 0;
-
- err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
- frag_threshold);
- if (err)
- brcmf_err("Error (%d)\n", err);
-
- return err;
-}
-
-static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
-{
- s32 err = 0;
- u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
-
- err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
- if (err) {
- brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
- return err;
- }
- return err;
-}
-
-static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
- (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
- cfg->conf->rts_threshold = wiphy->rts_threshold;
- err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
- if (!err)
- goto done;
- }
- if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
- (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
- cfg->conf->frag_threshold = wiphy->frag_threshold;
- err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
- if (!err)
- goto done;
- }
- if (changed & WIPHY_PARAM_RETRY_LONG
- && (cfg->conf->retry_long != wiphy->retry_long)) {
- cfg->conf->retry_long = wiphy->retry_long;
- err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
- if (!err)
- goto done;
- }
- if (changed & WIPHY_PARAM_RETRY_SHORT
- && (cfg->conf->retry_short != wiphy->retry_short)) {
- cfg->conf->retry_short = wiphy->retry_short;
- err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
- if (!err)
- goto done;
- }
-
-done:
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
-{
- memset(prof, 0, sizeof(*prof));
-}
-
-static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
-{
- u16 reason;
-
- switch (e->event_code) {
- case BRCMF_E_DEAUTH:
- case BRCMF_E_DEAUTH_IND:
- case BRCMF_E_DISASSOC_IND:
- reason = e->reason;
- break;
- case BRCMF_E_LINK:
- default:
- reason = 0;
- break;
- }
- return reason;
-}
-
-static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
- brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
- err = brcmf_fil_cmd_data_set(vif->ifp,
- BRCMF_C_DISASSOC, NULL, 0);
- if (err) {
- brcmf_err("WLC_DISASSOC failed (%d)\n", err);
- }
- clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
- cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
- true, GFP_KERNEL);
-
- }
- clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
- clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
- brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
- brcmf_dbg(TRACE, "Exit\n");
-}
-
-static s32
-brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_ibss_params *params)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
- struct brcmf_join_params join_params;
- size_t join_params_size = 0;
- s32 err = 0;
- s32 wsec = 0;
- s32 bcnprd;
- u16 chanspec;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- if (params->ssid)
- brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
- else {
- brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
- return -EOPNOTSUPP;
- }
-
- set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
-
- if (params->bssid)
- brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
- else
- brcmf_dbg(CONN, "No BSSID specified\n");
-
- if (params->chandef.chan)
- brcmf_dbg(CONN, "channel: %d\n",
- params->chandef.chan->center_freq);
- else
- brcmf_dbg(CONN, "no channel specified\n");
-
- if (params->channel_fixed)
- brcmf_dbg(CONN, "fixed channel required\n");
- else
- brcmf_dbg(CONN, "no fixed channel required\n");
-
- if (params->ie && params->ie_len)
- brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
- else
- brcmf_dbg(CONN, "no ie specified\n");
-
- if (params->beacon_interval)
- brcmf_dbg(CONN, "beacon interval: %d\n",
- params->beacon_interval);
- else
- brcmf_dbg(CONN, "no beacon interval specified\n");
-
- if (params->basic_rates)
- brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
- else
- brcmf_dbg(CONN, "no basic rates specified\n");
-
- if (params->privacy)
- brcmf_dbg(CONN, "privacy required\n");
- else
- brcmf_dbg(CONN, "no privacy required\n");
-
- /* Configure Privacy for starter */
- if (params->privacy)
- wsec |= WEP_ENABLED;
-
- err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
- if (err) {
- brcmf_err("wsec failed (%d)\n", err);
- goto done;
- }
-
- /* Configure Beacon Interval for starter */
- if (params->beacon_interval)
- bcnprd = params->beacon_interval;
- else
- bcnprd = 100;
-
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
- if (err) {
- brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
- goto done;
- }
-
- /* Configure required join parameter */
- memset(&join_params, 0, sizeof(struct brcmf_join_params));
-
- /* SSID */
- profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
- memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
- memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
- join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
- join_params_size = sizeof(join_params.ssid_le);
-
- /* BSSID */
- if (params->bssid) {
- memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
- join_params_size = sizeof(join_params.ssid_le) +
- BRCMF_ASSOC_PARAMS_FIXED_SIZE;
- memcpy(profile->bssid, params->bssid, ETH_ALEN);
- } else {
- eth_broadcast_addr(join_params.params_le.bssid);
- eth_zero_addr(profile->bssid);
- }
-
- /* Channel */
- if (params->chandef.chan) {
- u32 target_channel;
-
- cfg->channel =
- ieee80211_frequency_to_channel(
- params->chandef.chan->center_freq);
- if (params->channel_fixed) {
- /* adding chanspec */
- chanspec = chandef_to_chanspec(&cfg->d11inf,
- &params->chandef);
- join_params.params_le.chanspec_list[0] =
- cpu_to_le16(chanspec);
- join_params.params_le.chanspec_num = cpu_to_le32(1);
- join_params_size += sizeof(join_params.params_le);
- }
-
- /* set channel for starter */
- target_channel = cfg->channel;
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
- target_channel);
- if (err) {
- brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
- goto done;
- }
- } else
- cfg->channel = 0;
-
- cfg->ibss_starter = false;
-
-
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
- &join_params, join_params_size);
- if (err) {
- brcmf_err("WLC_SET_SSID failed (%d)\n", err);
- goto done;
- }
-
-done:
- if (err)
- clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
-
- brcmf_dbg(TRACE, "Exit\n");
-
- return 0;
-}
-
-static s32 brcmf_set_wpa_version(struct net_device *ndev,
- struct cfg80211_connect_params *sme)
-{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
- struct brcmf_cfg80211_security *sec;
- s32 val = 0;
- s32 err = 0;
-
- if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
- val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
- else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
- val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
- else
- val = WPA_AUTH_DISABLED;
- brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
- err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
- if (err) {
- brcmf_err("set wpa_auth failed (%d)\n", err);
- return err;
- }
- sec = &profile->sec;
- sec->wpa_versions = sme->crypto.wpa_versions;
- return err;
-}
-
-static s32 brcmf_set_auth_type(struct net_device *ndev,
- struct cfg80211_connect_params *sme)
-{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
- struct brcmf_cfg80211_security *sec;
- s32 val = 0;
- s32 err = 0;
-
- switch (sme->auth_type) {
- case NL80211_AUTHTYPE_OPEN_SYSTEM:
- val = 0;
- brcmf_dbg(CONN, "open system\n");
- break;
- case NL80211_AUTHTYPE_SHARED_KEY:
- val = 1;
- brcmf_dbg(CONN, "shared key\n");
- break;
- case NL80211_AUTHTYPE_AUTOMATIC:
- val = 2;
- brcmf_dbg(CONN, "automatic\n");
- break;
- case NL80211_AUTHTYPE_NETWORK_EAP:
- brcmf_dbg(CONN, "network eap\n");
- default:
- val = 2;
- brcmf_err("invalid auth type (%d)\n", sme->auth_type);
- break;
- }
-
- err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
- if (err) {
- brcmf_err("set auth failed (%d)\n", err);
- return err;
- }
- sec = &profile->sec;
- sec->auth_type = sme->auth_type;
- return err;
-}
-
-static s32
-brcmf_set_wsec_mode(struct net_device *ndev,
- struct cfg80211_connect_params *sme, bool mfp)
-{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
- struct brcmf_cfg80211_security *sec;
- s32 pval = 0;
- s32 gval = 0;
- s32 wsec;
- s32 err = 0;
-
- if (sme->crypto.n_ciphers_pairwise) {
- switch (sme->crypto.ciphers_pairwise[0]) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- pval = WEP_ENABLED;
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- pval = TKIP_ENABLED;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- pval = AES_ENABLED;
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- pval = AES_ENABLED;
- break;
- default:
- brcmf_err("invalid cipher pairwise (%d)\n",
- sme->crypto.ciphers_pairwise[0]);
- return -EINVAL;
- }
- }
- if (sme->crypto.cipher_group) {
- switch (sme->crypto.cipher_group) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- gval = WEP_ENABLED;
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- gval = TKIP_ENABLED;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- gval = AES_ENABLED;
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- gval = AES_ENABLED;
- break;
- default:
- brcmf_err("invalid cipher group (%d)\n",
- sme->crypto.cipher_group);
- return -EINVAL;
- }
- }
-
- brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
- /* In case of privacy, but no security and WPS then simulate */
- /* setting AES. WPS-2.0 allows no security */
- if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
- sme->privacy)
- pval = AES_ENABLED;
-
- if (mfp)
- wsec = pval | gval | MFP_CAPABLE;
- else
- wsec = pval | gval;
- err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
- if (err) {
- brcmf_err("error (%d)\n", err);
- return err;
- }
-
- sec = &profile->sec;
- sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
- sec->cipher_group = sme->crypto.cipher_group;
-
- return err;
-}
-
-static s32
-brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
-{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
- struct brcmf_cfg80211_security *sec;
- s32 val = 0;
- s32 err = 0;
-
- if (sme->crypto.n_akm_suites) {
- err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
- "wpa_auth", &val);
- if (err) {
- brcmf_err("could not get wpa_auth (%d)\n", err);
- return err;
- }
- if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
- switch (sme->crypto.akm_suites[0]) {
- case WLAN_AKM_SUITE_8021X:
- val = WPA_AUTH_UNSPECIFIED;
- break;
- case WLAN_AKM_SUITE_PSK:
- val = WPA_AUTH_PSK;
- break;
- default:
- brcmf_err("invalid cipher group (%d)\n",
- sme->crypto.cipher_group);
- return -EINVAL;
- }
- } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
- switch (sme->crypto.akm_suites[0]) {
- case WLAN_AKM_SUITE_8021X:
- val = WPA2_AUTH_UNSPECIFIED;
- break;
- case WLAN_AKM_SUITE_PSK:
- val = WPA2_AUTH_PSK;
- break;
- default:
- brcmf_err("invalid cipher group (%d)\n",
- sme->crypto.cipher_group);
- return -EINVAL;
- }
- }
-
- brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
- err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
- "wpa_auth", val);
- if (err) {
- brcmf_err("could not set wpa_auth (%d)\n", err);
- return err;
- }
- }
- sec = &profile->sec;
- sec->wpa_auth = sme->crypto.akm_suites[0];
-
- return err;
-}
-
-static s32
-brcmf_set_sharedkey(struct net_device *ndev,
- struct cfg80211_connect_params *sme)
-{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
- struct brcmf_cfg80211_security *sec;
- struct brcmf_wsec_key key;
- s32 val;
- s32 err = 0;
-
- brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
-
- if (sme->key_len == 0)
- return 0;
-
- sec = &profile->sec;
- brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
- sec->wpa_versions, sec->cipher_pairwise);
-
- if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
- return 0;
-
- if (!(sec->cipher_pairwise &
- (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
- return 0;
-
- memset(&key, 0, sizeof(key));
- key.len = (u32) sme->key_len;
- key.index = (u32) sme->key_idx;
- if (key.len > sizeof(key.data)) {
- brcmf_err("Too long key length (%u)\n", key.len);
- return -EINVAL;
- }
- memcpy(key.data, sme->key, key.len);
- key.flags = BRCMF_PRIMARY_KEY;
- switch (sec->cipher_pairwise) {
- case WLAN_CIPHER_SUITE_WEP40:
- key.algo = CRYPTO_ALGO_WEP1;
- break;
- case WLAN_CIPHER_SUITE_WEP104:
- key.algo = CRYPTO_ALGO_WEP128;
- break;
- default:
- brcmf_err("Invalid algorithm (%d)\n",
- sme->crypto.ciphers_pairwise[0]);
- return -EINVAL;
- }
- /* Set the new key/index */
- brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
- key.len, key.index, key.algo);
- brcmf_dbg(CONN, "key \"%s\"\n", key.data);
- err = send_key_to_dongle(netdev_priv(ndev), &key);
- if (err)
- return err;
-
- if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
- brcmf_dbg(CONN, "set auth_type to shared key\n");
- val = WL_AUTH_SHARED_KEY; /* shared key */
- err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
- if (err)
- brcmf_err("set auth failed (%d)\n", err);
- }
- return err;
-}
-
-static
-enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
- enum nl80211_auth_type type)
-{
- if (type == NL80211_AUTHTYPE_AUTOMATIC &&
- brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
- brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
- type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- }
- return type;
-}
-
-static s32
-brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_connect_params *sme)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
- struct ieee80211_channel *chan = sme->channel;
- struct brcmf_join_params join_params;
- size_t join_params_size;
- const struct brcmf_tlv *rsn_ie;
- const struct brcmf_vs_tlv *wpa_ie;
- const void *ie;
- u32 ie_len;
- struct brcmf_ext_join_params_le *ext_join_params;
- u16 chanspec;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- if (!sme->ssid) {
- brcmf_err("Invalid ssid\n");
- return -EOPNOTSUPP;
- }
-
- if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
- /* A normal (non P2P) connection request setup. */
- ie = NULL;
- ie_len = 0;
- /* find the WPA_IE */
- wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
- if (wpa_ie) {
- ie = wpa_ie;
- ie_len = wpa_ie->len + TLV_HDR_LEN;
- } else {
- /* find the RSN_IE */
- rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
- sme->ie_len,
- WLAN_EID_RSN);
- if (rsn_ie) {
- ie = rsn_ie;
- ie_len = rsn_ie->len + TLV_HDR_LEN;
- }
- }
- brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
- }
-
- err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
- sme->ie, sme->ie_len);
- if (err)
- brcmf_err("Set Assoc REQ IE Failed\n");
- else
- brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
-
- set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
-
- if (chan) {
- cfg->channel =
- ieee80211_frequency_to_channel(chan->center_freq);
- chanspec = channel_to_chanspec(&cfg->d11inf, chan);
- brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
- cfg->channel, chan->center_freq, chanspec);
- } else {
- cfg->channel = 0;
- chanspec = 0;
- }
-
- brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
-
- err = brcmf_set_wpa_version(ndev, sme);
- if (err) {
- brcmf_err("wl_set_wpa_version failed (%d)\n", err);
- goto done;
- }
-
- sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
- err = brcmf_set_auth_type(ndev, sme);
- if (err) {
- brcmf_err("wl_set_auth_type failed (%d)\n", err);
- goto done;
- }
-
- err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
- if (err) {
- brcmf_err("wl_set_set_cipher failed (%d)\n", err);
- goto done;
- }
-
- err = brcmf_set_key_mgmt(ndev, sme);
- if (err) {
- brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
- goto done;
- }
-
- err = brcmf_set_sharedkey(ndev, sme);
- if (err) {
- brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
- goto done;
- }
-
- profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
- (u32)sme->ssid_len);
- memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
- if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
- profile->ssid.SSID[profile->ssid.SSID_len] = 0;
- brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
- profile->ssid.SSID_len);
- }
-
- /* Join with specific BSSID and cached SSID
- * If SSID is zero join based on BSSID only
- */
- join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
- offsetof(struct brcmf_assoc_params_le, chanspec_list);
- if (cfg->channel)
- join_params_size += sizeof(u16);
- ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
- if (ext_join_params == NULL) {
- err = -ENOMEM;
- goto done;
- }
- ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
- memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
- profile->ssid.SSID_len);
-
- /* Set up join scan parameters */
- ext_join_params->scan_le.scan_type = -1;
- ext_join_params->scan_le.home_time = cpu_to_le32(-1);
-
- if (sme->bssid)
- memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
- else
- eth_broadcast_addr(ext_join_params->assoc_le.bssid);
-
- if (cfg->channel) {
- ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
-
- ext_join_params->assoc_le.chanspec_list[0] =
- cpu_to_le16(chanspec);
- /* Increase dwell time to receive probe response or detect
- * beacon from target AP at a noisy air only during connect
- * command.
- */
- ext_join_params->scan_le.active_time =
- cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
- ext_join_params->scan_le.passive_time =
- cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
- /* To sync with presence period of VSDB GO send probe request
- * more frequently. Probe request will be stopped when it gets
- * probe response from target AP/GO.
- */
- ext_join_params->scan_le.nprobes =
- cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
- BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
- } else {
- ext_join_params->scan_le.active_time = cpu_to_le32(-1);
- ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
- ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
- }
-
- err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
- join_params_size);
- kfree(ext_join_params);
- if (!err)
- /* This is it. join command worked, we are done */
- goto done;
-
- /* join command failed, fallback to set ssid */
- memset(&join_params, 0, sizeof(join_params));
- join_params_size = sizeof(join_params.ssid_le);
-
- memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
- join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
-
- if (sme->bssid)
- memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
- else
- eth_broadcast_addr(join_params.params_le.bssid);
-
- if (cfg->channel) {
- join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
- join_params.params_le.chanspec_num = cpu_to_le32(1);
- join_params_size += sizeof(join_params.params_le);
- }
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
- &join_params, join_params_size);
- if (err)
- brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
-
-done:
- if (err)
- clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
- u16 reason_code)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
- struct brcmf_scb_val_le scbval;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
- clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
- cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
-
- memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
- scbval.val = cpu_to_le32(reason_code);
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
- &scbval, sizeof(scbval));
- if (err)
- brcmf_err("error (%d)\n", err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
- enum nl80211_tx_power_setting type, s32 mbm)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err;
- s32 disable;
- u32 qdbm = 127;
-
- brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- switch (type) {
- case NL80211_TX_POWER_AUTOMATIC:
- break;
- case NL80211_TX_POWER_LIMITED:
- case NL80211_TX_POWER_FIXED:
- if (mbm < 0) {
- brcmf_err("TX_POWER_FIXED - dbm is negative\n");
- err = -EINVAL;
- goto done;
- }
- qdbm = MBM_TO_DBM(4 * mbm);
- if (qdbm > 127)
- qdbm = 127;
- qdbm |= WL_TXPWR_OVERRIDE;
- break;
- default:
- brcmf_err("Unsupported type %d\n", type);
- err = -EINVAL;
- goto done;
- }
- /* Make sure radio is off or on as far as software is concerned */
- disable = WL_RADIO_SW_DISABLE << 16;
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
- if (err)
- brcmf_err("WLC_SET_RADIO error (%d)\n", err);
-
- err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
- if (err)
- brcmf_err("qtxpower error (%d)\n", err);
-
-done:
- brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
- return err;
-}
-
-static s32
-brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
- s32 *dbm)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 qdbm = 0;
- s32 err;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
- if (err) {
- brcmf_err("error (%d)\n", err);
- goto done;
- }
- *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
-
-done:
- brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
- return err;
-}
-
-static s32
-brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, bool unicast, bool multicast)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- u32 index;
- u32 wsec;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- brcmf_dbg(CONN, "key index (%d)\n", key_idx);
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
- if (err) {
- brcmf_err("WLC_GET_WSEC error (%d)\n", err);
- goto done;
- }
-
- if (wsec & WEP_ENABLED) {
- /* Just select a new current key */
- index = key_idx;
- err = brcmf_fil_cmd_int_set(ifp,
- BRCMF_C_SET_KEY_PRIMARY, index);
- if (err)
- brcmf_err("error (%d)\n", err);
- }
-done:
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, const u8 *mac_addr, struct key_params *params)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_wsec_key key;
- s32 err = 0;
- u8 keybuf[8];
-
- memset(&key, 0, sizeof(key));
- key.index = (u32) key_idx;
- /* Instead of bcast for ea address for default wep keys,
- driver needs it to be Null */
- if (!is_multicast_ether_addr(mac_addr))
- memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
- key.len = (u32) params->key_len;
- /* check for key index change */
- if (key.len == 0) {
- /* key delete */
- err = send_key_to_dongle(ifp, &key);
- if (err)
- brcmf_err("key delete error (%d)\n", err);
- } else {
- if (key.len > sizeof(key.data)) {
- brcmf_err("Invalid key length (%d)\n", key.len);
- return -EINVAL;
- }
-
- brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
- memcpy(key.data, params->key, key.len);
-
- if (!brcmf_is_apmode(ifp->vif) &&
- (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
- brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
- memcpy(keybuf, &key.data[24], sizeof(keybuf));
- memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
- memcpy(&key.data[16], keybuf, sizeof(keybuf));
- }
-
- /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
- if (params->seq && params->seq_len == 6) {
- /* rx iv */
- u8 *ivptr;
- ivptr = (u8 *) params->seq;
- key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
- (ivptr[3] << 8) | ivptr[2];
- key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
- key.iv_initialized = true;
- }
-
- switch (params->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- key.algo = CRYPTO_ALGO_WEP1;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
- break;
- case WLAN_CIPHER_SUITE_WEP104:
- key.algo = CRYPTO_ALGO_WEP128;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- key.algo = CRYPTO_ALGO_TKIP;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- key.algo = CRYPTO_ALGO_AES_CCM;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- key.algo = CRYPTO_ALGO_AES_CCM;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
- break;
- default:
- brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
- return -EINVAL;
- }
- err = send_key_to_dongle(ifp, &key);
- if (err)
- brcmf_err("wsec_key error (%d)\n", err);
- }
- return err;
-}
-
-static s32
-brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, bool pairwise, const u8 *mac_addr,
- struct key_params *params)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_wsec_key *key;
- s32 val;
- s32 wsec;
- s32 err = 0;
- u8 keybuf[8];
-
- brcmf_dbg(TRACE, "Enter\n");
- brcmf_dbg(CONN, "key index (%d)\n", key_idx);
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
- /* we ignore this key index in this case */
- brcmf_err("invalid key index (%d)\n", key_idx);
- return -EINVAL;
- }
-
- if (mac_addr &&
- (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
- (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
- brcmf_dbg(TRACE, "Exit");
- return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
- }
-
- key = &ifp->vif->profile.key[key_idx];
- memset(key, 0, sizeof(*key));
-
- if (params->key_len > sizeof(key->data)) {
- brcmf_err("Too long key length (%u)\n", params->key_len);
- err = -EINVAL;
- goto done;
- }
- key->len = params->key_len;
- key->index = key_idx;
-
- memcpy(key->data, params->key, key->len);
-
- key->flags = BRCMF_PRIMARY_KEY;
- switch (params->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- key->algo = CRYPTO_ALGO_WEP1;
- val = WEP_ENABLED;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
- break;
- case WLAN_CIPHER_SUITE_WEP104:
- key->algo = CRYPTO_ALGO_WEP128;
- val = WEP_ENABLED;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- if (!brcmf_is_apmode(ifp->vif)) {
- brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
- memcpy(keybuf, &key->data[24], sizeof(keybuf));
- memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
- memcpy(&key->data[16], keybuf, sizeof(keybuf));
- }
- key->algo = CRYPTO_ALGO_TKIP;
- val = TKIP_ENABLED;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- key->algo = CRYPTO_ALGO_AES_CCM;
- val = AES_ENABLED;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- key->algo = CRYPTO_ALGO_AES_CCM;
- val = AES_ENABLED;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
- break;
- default:
- brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
- err = -EINVAL;
- goto done;
- }
-
- err = send_key_to_dongle(ifp, key);
- if (err)
- goto done;
-
- err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
- if (err) {
- brcmf_err("get wsec error (%d)\n", err);
- goto done;
- }
- wsec |= val;
- err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
- if (err) {
- brcmf_err("set wsec error (%d)\n", err);
- goto done;
- }
-
-done:
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, bool pairwise, const u8 *mac_addr)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_wsec_key key;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
- /* we ignore this key index in this case */
- return -EINVAL;
- }
-
- memset(&key, 0, sizeof(key));
-
- key.index = (u32) key_idx;
- key.flags = BRCMF_PRIMARY_KEY;
- key.algo = CRYPTO_ALGO_OFF;
-
- brcmf_dbg(CONN, "key index (%d)\n", key_idx);
-
- /* Set the new key/index */
- err = send_key_to_dongle(ifp, &key);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
- void (*callback) (void *cookie, struct key_params * params))
-{
- struct key_params params;
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
- struct brcmf_cfg80211_security *sec;
- s32 wsec;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- brcmf_dbg(CONN, "key index (%d)\n", key_idx);
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- memset(&params, 0, sizeof(params));
-
- err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
- if (err) {
- brcmf_err("WLC_GET_WSEC error (%d)\n", err);
- /* Ignore this error, may happen during DISASSOC */
- err = -EAGAIN;
- goto done;
- }
- if (wsec & WEP_ENABLED) {
- sec = &profile->sec;
- if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
- params.cipher = WLAN_CIPHER_SUITE_WEP40;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
- } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
- params.cipher = WLAN_CIPHER_SUITE_WEP104;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
- }
- } else if (wsec & TKIP_ENABLED) {
- params.cipher = WLAN_CIPHER_SUITE_TKIP;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
- } else if (wsec & AES_ENABLED) {
- params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
- } else {
- brcmf_err("Invalid algo (0x%x)\n", wsec);
- err = -EINVAL;
- goto done;
- }
- callback(cookie, &params);
-
-done:
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
- struct net_device *ndev, u8 key_idx)
-{
- brcmf_dbg(INFO, "Not supported\n");
-
- return -EOPNOTSUPP;
-}
-
-static void
-brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
-{
- s32 err;
- u8 key_idx;
- struct brcmf_wsec_key *key;
- s32 wsec;
-
- for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
- key = &ifp->vif->profile.key[key_idx];
- if ((key->algo == CRYPTO_ALGO_WEP1) ||
- (key->algo == CRYPTO_ALGO_WEP128))
- break;
- }
- if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
- return;
-
- err = send_key_to_dongle(ifp, key);
- if (err) {
- brcmf_err("Setting WEP key failed (%d)\n", err);
- return;
- }
- err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
- if (err) {
- brcmf_err("get wsec error (%d)\n", err);
- return;
- }
- wsec |= WEP_ENABLED;
- err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
- if (err)
- brcmf_err("set wsec error (%d)\n", err);
-}
-
-static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
-{
- struct nl80211_sta_flag_update *sfu;
-
- brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
- si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
- sfu = &si->sta_flags;
- sfu->mask = BIT(NL80211_STA_FLAG_WME) |
- BIT(NL80211_STA_FLAG_AUTHENTICATED) |
- BIT(NL80211_STA_FLAG_ASSOCIATED) |
- BIT(NL80211_STA_FLAG_AUTHORIZED);
- if (fw_sta_flags & BRCMF_STA_WME)
- sfu->set |= BIT(NL80211_STA_FLAG_WME);
- if (fw_sta_flags & BRCMF_STA_AUTHE)
- sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
- if (fw_sta_flags & BRCMF_STA_ASSOC)
- sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
- if (fw_sta_flags & BRCMF_STA_AUTHO)
- sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-}
-
-static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
-{
- struct {
- __le32 len;
- struct brcmf_bss_info_le bss_le;
- } *buf;
- u16 capability;
- int err;
-
- buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
- if (!buf)
- return;
-
- buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
- WL_BSS_INFO_MAX);
- if (err) {
- brcmf_err("Failed to get bss info (%d)\n", err);
- return;
- }
- si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
- si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
- si->bss_param.dtim_period = buf->bss_le.dtim_period;
- capability = le16_to_cpu(buf->bss_le.capability);
- if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
- si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
- if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
- si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
- if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
- si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
-}
-
-static s32
-brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
- const u8 *mac, struct station_info *sinfo)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err = 0;
- struct brcmf_sta_info_le sta_info_le;
- u32 sta_flags;
- u32 is_tdls_peer;
- s32 total_rssi;
- s32 count_rssi;
- u32 i;
-
- brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- memset(&sta_info_le, 0, sizeof(sta_info_le));
- memcpy(&sta_info_le, mac, ETH_ALEN);
- err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
- &sta_info_le,
- sizeof(sta_info_le));
- is_tdls_peer = !err;
- if (err) {
- err = brcmf_fil_iovar_data_get(ifp, "sta_info",
- &sta_info_le,
- sizeof(sta_info_le));
- if (err < 0) {
- brcmf_err("GET STA INFO failed, %d\n", err);
- goto done;
- }
- }
- brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
- sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
- sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
- sta_flags = le32_to_cpu(sta_info_le.flags);
- brcmf_convert_sta_flags(sta_flags, sinfo);
- sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
- if (is_tdls_peer)
- sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
- else
- sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
- if (sta_flags & BRCMF_STA_ASSOC) {
- sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
- sinfo->connected_time = le32_to_cpu(sta_info_le.in);
- brcmf_fill_bss_param(ifp, sinfo);
- }
- if (sta_flags & BRCMF_STA_SCBSTATS) {
- sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
- sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
- sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
- sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
- sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
- sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
- sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
- sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
- if (sinfo->tx_packets) {
- sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
- sinfo->txrate.legacy =
- le32_to_cpu(sta_info_le.tx_rate) / 100;
- }
- if (sinfo->rx_packets) {
- sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
- sinfo->rxrate.legacy =
- le32_to_cpu(sta_info_le.rx_rate) / 100;
- }
- if (le16_to_cpu(sta_info_le.ver) >= 4) {
- sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
- sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
- sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
- sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
- }
- total_rssi = 0;
- count_rssi = 0;
- for (i = 0; i < BRCMF_ANT_MAX; i++) {
- if (sta_info_le.rssi[i]) {
- sinfo->chain_signal_avg[count_rssi] =
- sta_info_le.rssi[i];
- sinfo->chain_signal[count_rssi] =
- sta_info_le.rssi[i];
- total_rssi += sta_info_le.rssi[i];
- count_rssi++;
- }
- }
- if (count_rssi) {
- sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
- sinfo->chains = count_rssi;
-
- sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
- total_rssi /= count_rssi;
- sinfo->signal = total_rssi;
- }
- }
-done:
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static int
-brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
- int idx, u8 *mac, struct station_info *sinfo)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err;
-
- brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
-
- if (idx == 0) {
- cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
- &cfg->assoclist,
- sizeof(cfg->assoclist));
- if (err) {
- brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
- err);
- cfg->assoclist.count = 0;
- return -EOPNOTSUPP;
- }
- }
- if (idx < le32_to_cpu(cfg->assoclist.count)) {
- memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
- return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
- }
- return -ENOENT;
-}
-
-static s32
-brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
- bool enabled, s32 timeout)
-{
- s32 pm;
- s32 err = 0;
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /*
- * Powersave enable/disable request is coming from the
- * cfg80211 even before the interface is up. In that
- * scenario, driver will be storing the power save
- * preference in cfg struct to apply this to
- * FW later while initializing the dongle
- */
- cfg->pwr_save = enabled;
- if (!check_vif_up(ifp->vif)) {
-
- brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
- goto done;
- }
-
- pm = enabled ? PM_FAST : PM_OFF;
- /* Do not enable the power save after assoc if it is a p2p interface */
- if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
- brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
- pm = PM_OFF;
- }
- brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
-
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
- if (err) {
- if (err == -ENODEV)
- brcmf_err("net_device is not ready yet\n");
- else
- brcmf_err("error (%d)\n", err);
- }
-done:
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
- struct brcmf_bss_info_le *bi)
-{
- struct wiphy *wiphy = cfg_to_wiphy(cfg);
- struct ieee80211_channel *notify_channel;
- struct cfg80211_bss *bss;
- struct ieee80211_supported_band *band;
- struct brcmu_chan ch;
- u16 channel;
- u32 freq;
- u16 notify_capability;
- u16 notify_interval;
- u8 *notify_ie;
- size_t notify_ielen;
- s32 notify_signal;
-
- if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
- brcmf_err("Bss info is larger than buffer. Discarding\n");
- return 0;
- }
-
- if (!bi->ctl_ch) {
- ch.chspec = le16_to_cpu(bi->chanspec);
- cfg->d11inf.decchspec(&ch);
- bi->ctl_ch = ch.chnum;
- }
- channel = bi->ctl_ch;
-
- if (channel <= CH_MAX_2G_CHANNEL)
- band = wiphy->bands[IEEE80211_BAND_2GHZ];
- else
- band = wiphy->bands[IEEE80211_BAND_5GHZ];
-
- freq = ieee80211_channel_to_frequency(channel, band->band);
- notify_channel = ieee80211_get_channel(wiphy, freq);
-
- notify_capability = le16_to_cpu(bi->capability);
- notify_interval = le16_to_cpu(bi->beacon_period);
- notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
- notify_ielen = le32_to_cpu(bi->ie_length);
- notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
-
- brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
- brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
- brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
- brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
- brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
-
- bss = cfg80211_inform_bss(wiphy, notify_channel,
- CFG80211_BSS_FTYPE_UNKNOWN,
- (const u8 *)bi->BSSID,
- 0, notify_capability,
- notify_interval, notify_ie,
- notify_ielen, notify_signal,
- GFP_KERNEL);
-
- if (!bss)
- return -ENOMEM;
-
- cfg80211_put_bss(wiphy, bss);
-
- return 0;
-}
-
-static struct brcmf_bss_info_le *
-next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
-{
- if (bss == NULL)
- return list->bss_info_le;
- return (struct brcmf_bss_info_le *)((unsigned long)bss +
- le32_to_cpu(bss->length));
-}
-
-static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
-{
- struct brcmf_scan_results *bss_list;
- struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
- s32 err = 0;
- int i;
-
- bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
- if (bss_list->count != 0 &&
- bss_list->version != BRCMF_BSS_INFO_VERSION) {
- brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
- bss_list->version);
- return -EOPNOTSUPP;
- }
- brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
- for (i = 0; i < bss_list->count; i++) {
- bi = next_bss_le(bss_list, bi);
- err = brcmf_inform_single_bss(cfg, bi);
- if (err)
- break;
- }
- return err;
-}
-
-static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev, const u8 *bssid)
-{
- struct wiphy *wiphy = cfg_to_wiphy(cfg);
- struct ieee80211_channel *notify_channel;
- struct brcmf_bss_info_le *bi = NULL;
- struct ieee80211_supported_band *band;
- struct cfg80211_bss *bss;
- struct brcmu_chan ch;
- u8 *buf = NULL;
- s32 err = 0;
- u32 freq;
- u16 notify_capability;
- u16 notify_interval;
- u8 *notify_ie;
- size_t notify_ielen;
- s32 notify_signal;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
- if (buf == NULL) {
- err = -ENOMEM;
- goto CleanUp;
- }
-
- *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
-
- err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
- buf, WL_BSS_INFO_MAX);
- if (err) {
- brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
- goto CleanUp;
- }
-
- bi = (struct brcmf_bss_info_le *)(buf + 4);
-
- ch.chspec = le16_to_cpu(bi->chanspec);
- cfg->d11inf.decchspec(&ch);
-
- if (ch.band == BRCMU_CHAN_BAND_2G)
- band = wiphy->bands[IEEE80211_BAND_2GHZ];
- else
- band = wiphy->bands[IEEE80211_BAND_5GHZ];
-
- freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
- notify_channel = ieee80211_get_channel(wiphy, freq);
-
- notify_capability = le16_to_cpu(bi->capability);
- notify_interval = le16_to_cpu(bi->beacon_period);
- notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
- notify_ielen = le32_to_cpu(bi->ie_length);
- notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
-
- brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
- brcmf_dbg(CONN, "capability: %X\n", notify_capability);
- brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
- brcmf_dbg(CONN, "signal: %d\n", notify_signal);
-
- bss = cfg80211_inform_bss(wiphy, notify_channel,
- CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
- notify_capability, notify_interval,
- notify_ie, notify_ielen, notify_signal,
- GFP_KERNEL);
-
- if (!bss) {
- err = -ENOMEM;
- goto CleanUp;
- }
-
- cfg80211_put_bss(wiphy, bss);
-
-CleanUp:
-
- kfree(buf);
-
- brcmf_dbg(TRACE, "Exit\n");
-
- return err;
-}
-
-static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
- struct brcmf_if *ifp)
-{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
- struct brcmf_bss_info_le *bi;
- struct brcmf_ssid *ssid;
- const struct brcmf_tlv *tim;
- u16 beacon_interval;
- u8 dtim_period;
- size_t ie_len;
- u8 *ie;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (brcmf_is_ibssmode(ifp->vif))
- return err;
-
- ssid = &profile->ssid;
-
- *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
- cfg->extra_buf, WL_EXTRA_BUF_MAX);
- if (err) {
- brcmf_err("Could not get bss info %d\n", err);
- goto update_bss_info_out;
- }
-
- bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
- err = brcmf_inform_single_bss(cfg, bi);
- if (err)
- goto update_bss_info_out;
-
- ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
- ie_len = le32_to_cpu(bi->ie_length);
- beacon_interval = le16_to_cpu(bi->beacon_period);
-
- tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
- if (tim)
- dtim_period = tim->data[1];
- else {
- /*
- * active scan was done so we could not get dtim
- * information out of probe response.
- * so we speficially query dtim information to dongle.
- */
- u32 var;
- err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
- if (err) {
- brcmf_err("wl dtim_assoc failed (%d)\n", err);
- goto update_bss_info_out;
- }
- dtim_period = (u8)var;
- }
-
-update_bss_info_out:
- brcmf_dbg(TRACE, "Exit");
- return err;
-}
-
-void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
-{
- struct escan_info *escan = &cfg->escan_info;
-
- set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
- if (cfg->scan_request) {
- escan->escan_state = WL_ESCAN_STATE_IDLE;
- brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
- }
- clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
- clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
-}
-
-static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
-{
- struct brcmf_cfg80211_info *cfg =
- container_of(work, struct brcmf_cfg80211_info,
- escan_timeout_work);
-
- brcmf_inform_bss(cfg);
- brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
-}
-
-static void brcmf_escan_timeout(unsigned long data)
-{
- struct brcmf_cfg80211_info *cfg =
- (struct brcmf_cfg80211_info *)data;
-
- if (cfg->scan_request) {
- brcmf_err("timer expired\n");
- schedule_work(&cfg->escan_timeout_work);
- }
-}
-
-static s32
-brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
- struct brcmf_bss_info_le *bss,
- struct brcmf_bss_info_le *bss_info_le)
-{
- struct brcmu_chan ch_bss, ch_bss_info_le;
-
- ch_bss.chspec = le16_to_cpu(bss->chanspec);
- cfg->d11inf.decchspec(&ch_bss);
- ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
- cfg->d11inf.decchspec(&ch_bss_info_le);
-
- if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
- ch_bss.band == ch_bss_info_le.band &&
- bss_info_le->SSID_len == bss->SSID_len &&
- !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
- if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
- (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
- s16 bss_rssi = le16_to_cpu(bss->RSSI);
- s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
-
- /* preserve max RSSI if the measurements are
- * both on-channel or both off-channel
- */
- if (bss_info_rssi > bss_rssi)
- bss->RSSI = bss_info_le->RSSI;
- } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
- (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
- /* preserve the on-channel rssi measurement
- * if the new measurement is off channel
- */
- bss->RSSI = bss_info_le->RSSI;
- bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
- }
- return 1;
- }
- return 0;
-}
-
-static s32
-brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e, void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- s32 status;
- struct brcmf_escan_result_le *escan_result_le;
- struct brcmf_bss_info_le *bss_info_le;
- struct brcmf_bss_info_le *bss = NULL;
- u32 bi_length;
- struct brcmf_scan_results *list;
- u32 i;
- bool aborted;
-
- status = e->status;
-
- if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
- brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
- return -EPERM;
- }
-
- if (status == BRCMF_E_STATUS_PARTIAL) {
- brcmf_dbg(SCAN, "ESCAN Partial result\n");
- escan_result_le = (struct brcmf_escan_result_le *) data;
- if (!escan_result_le) {
- brcmf_err("Invalid escan result (NULL pointer)\n");
- goto exit;
- }
- if (le16_to_cpu(escan_result_le->bss_count) != 1) {
- brcmf_err("Invalid bss_count %d: ignoring\n",
- escan_result_le->bss_count);
- goto exit;
- }
- bss_info_le = &escan_result_le->bss_info_le;
-
- if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
- goto exit;
-
- if (!cfg->scan_request) {
- brcmf_dbg(SCAN, "result without cfg80211 request\n");
- goto exit;
- }
-
- bi_length = le32_to_cpu(bss_info_le->length);
- if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
- WL_ESCAN_RESULTS_FIXED_SIZE)) {
- brcmf_err("Invalid bss_info length %d: ignoring\n",
- bi_length);
- goto exit;
- }
-
- if (!(cfg_to_wiphy(cfg)->interface_modes &
- BIT(NL80211_IFTYPE_ADHOC))) {
- if (le16_to_cpu(bss_info_le->capability) &
- WLAN_CAPABILITY_IBSS) {
- brcmf_err("Ignoring IBSS result\n");
- goto exit;
- }
- }
-
- list = (struct brcmf_scan_results *)
- cfg->escan_info.escan_buf;
- if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
- brcmf_err("Buffer is too small: ignoring\n");
- goto exit;
- }
-
- for (i = 0; i < list->count; i++) {
- bss = bss ? (struct brcmf_bss_info_le *)
- ((unsigned char *)bss +
- le32_to_cpu(bss->length)) : list->bss_info_le;
- if (brcmf_compare_update_same_bss(cfg, bss,
- bss_info_le))
- goto exit;
- }
- memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
- bss_info_le, bi_length);
- list->version = le32_to_cpu(bss_info_le->version);
- list->buflen += bi_length;
- list->count++;
- } else {
- cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
- if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
- goto exit;
- if (cfg->scan_request) {
- brcmf_inform_bss(cfg);
- aborted = status != BRCMF_E_STATUS_SUCCESS;
- brcmf_notify_escan_complete(cfg, ifp, aborted, false);
- } else
- brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
- status);
- }
-exit:
- return 0;
-}
-
-static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
-{
- brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
- brcmf_cfg80211_escan_handler);
- cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
- /* Init scan_timeout timer */
- init_timer(&cfg->escan_timeout);
- cfg->escan_timeout.data = (unsigned long) cfg;
- cfg->escan_timeout.function = brcmf_escan_timeout;
- INIT_WORK(&cfg->escan_timeout_work,
- brcmf_cfg80211_escan_timeout_worker);
-}
-
-static __always_inline void brcmf_delay(u32 ms)
-{
- if (ms < 1000 / HZ) {
- cond_resched();
- mdelay(ms);
- } else {
- msleep(ms);
- }
-}
-
-static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
- u8 *pattern, u32 patternsize, u8 *mask,
- u32 packet_offset)
-{
- struct brcmf_fil_wowl_pattern_le *filter;
- u32 masksize;
- u32 patternoffset;
- u8 *buf;
- u32 bufsize;
- s32 ret;
-
- masksize = (patternsize + 7) / 8;
- patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
-
- bufsize = sizeof(*filter) + patternsize + masksize;
- buf = kzalloc(bufsize, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- filter = (struct brcmf_fil_wowl_pattern_le *)buf;
-
- memcpy(filter->cmd, cmd, 4);
- filter->masksize = cpu_to_le32(masksize);
- filter->offset = cpu_to_le32(packet_offset);
- filter->patternoffset = cpu_to_le32(patternoffset);
- filter->patternsize = cpu_to_le32(patternsize);
- filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
-
- if ((mask) && (masksize))
- memcpy(buf + sizeof(*filter), mask, masksize);
- if ((pattern) && (patternsize))
- memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
-
- ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
-
- kfree(buf);
- return ret;
-}
-
-static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (cfg->wowl_enabled) {
- brcmf_configure_arp_offload(ifp, true);
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
- cfg->pre_wowl_pmmode);
- brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
- brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
- cfg->wowl_enabled = false;
- }
- return 0;
-}
-
-static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
- struct brcmf_if *ifp,
- struct cfg80211_wowlan *wowl)
-{
- u32 wowl_config;
- u32 i;
-
- brcmf_dbg(TRACE, "Suspend, wowl config.\n");
-
- brcmf_configure_arp_offload(ifp, false);
- brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
-
- wowl_config = 0;
- if (wowl->disconnect)
- wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
- if (wowl->magic_pkt)
- wowl_config |= BRCMF_WOWL_MAGIC;
- if ((wowl->patterns) && (wowl->n_patterns)) {
- wowl_config |= BRCMF_WOWL_NET;
- for (i = 0; i < wowl->n_patterns; i++) {
- brcmf_config_wowl_pattern(ifp, "add",
- (u8 *)wowl->patterns[i].pattern,
- wowl->patterns[i].pattern_len,
- (u8 *)wowl->patterns[i].mask,
- wowl->patterns[i].pkt_offset);
- }
- }
- brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
- brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
- brcmf_bus_wowl_config(cfg->pub->bus_if, true);
- cfg->wowl_enabled = true;
-}
-
-static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
- struct cfg80211_wowlan *wowl)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_vif *vif;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* if the primary net_device is not READY there is nothing
- * we can do but pray resume goes smoothly.
- */
- if (!check_vif_up(ifp->vif))
- goto exit;
-
- /* end any scanning */
- if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
- brcmf_abort_scanning(cfg);
-
- if (wowl == NULL) {
- brcmf_bus_wowl_config(cfg->pub->bus_if, false);
- list_for_each_entry(vif, &cfg->vif_list, list) {
- if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
- continue;
- /* While going to suspend if associated with AP
- * disassociate from AP to save power while system is
- * in suspended state
- */
- brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
- /* Make sure WPA_Supplicant receives all the event
- * generated due to DISASSOC call to the fw to keep
- * the state fw and WPA_Supplicant state consistent
- */
- brcmf_delay(500);
- }
- /* Configure MPC */
- brcmf_set_mpc(ifp, 1);
-
- } else {
- /* Configure WOWL paramaters */
- brcmf_configure_wowl(cfg, ifp, wowl);
- }
-
-exit:
- brcmf_dbg(TRACE, "Exit\n");
- /* clear any scanning activity */
- cfg->scan_status = 0;
- return 0;
-}
-
-static __used s32
-brcmf_update_pmklist(struct net_device *ndev,
- struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
-{
- int i, j;
- u32 pmkid_len;
-
- pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
-
- brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
- for (i = 0; i < pmkid_len; i++) {
- brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
- &pmk_list->pmkids.pmkid[i].BSSID);
- for (j = 0; j < WLAN_PMKID_LEN; j++)
- brcmf_dbg(CONN, "%02x\n",
- pmk_list->pmkids.pmkid[i].PMKID[j]);
- }
-
- if (!err)
- brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
- (char *)pmk_list, sizeof(*pmk_list));
-
- return err;
-}
-
-static s32
-brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_pmksa *pmksa)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
- s32 err = 0;
- u32 pmkid_len, i;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- pmkid_len = le32_to_cpu(pmkids->npmkid);
- for (i = 0; i < pmkid_len; i++)
- if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
- break;
- if (i < WL_NUM_PMKIDS_MAX) {
- memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
- memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
- if (i == pmkid_len) {
- pmkid_len++;
- pmkids->npmkid = cpu_to_le32(pmkid_len);
- }
- } else
- err = -EINVAL;
-
- brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
- pmkids->pmkid[pmkid_len].BSSID);
- for (i = 0; i < WLAN_PMKID_LEN; i++)
- brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
-
- err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_pmksa *pmksa)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct pmkid_list pmkid;
- s32 err = 0;
- u32 pmkid_len, i;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
- memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
-
- brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
- &pmkid.pmkid[0].BSSID);
- for (i = 0; i < WLAN_PMKID_LEN; i++)
- brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
-
- pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
- for (i = 0; i < pmkid_len; i++)
- if (!memcmp
- (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
- ETH_ALEN))
- break;
-
- if ((pmkid_len > 0)
- && (i < pmkid_len)) {
- memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
- sizeof(struct pmkid));
- for (; i < (pmkid_len - 1); i++) {
- memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
- &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
- ETH_ALEN);
- memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
- &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
- WLAN_PMKID_LEN);
- }
- cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
- } else
- err = -EINVAL;
-
- err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-
-}
-
-static s32
-brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
- err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-
-}
-
-/*
- * PFN result doesn't have all the info which are
- * required by the supplicant
- * (For e.g IEs) Do a target Escan so that sched scan results are reported
- * via wl_inform_single_bss in the required format. Escan does require the
- * scan request in the form of cfg80211_scan_request. For timebeing, create
- * cfg80211_scan_request one out of the received PNO event.
- */
-static s32
-brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e, void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
- struct cfg80211_scan_request *request = NULL;
- struct cfg80211_ssid *ssid = NULL;
- struct ieee80211_channel *channel = NULL;
- struct wiphy *wiphy = cfg_to_wiphy(cfg);
- int err = 0;
- int channel_req = 0;
- int band = 0;
- struct brcmf_pno_scanresults_le *pfn_result;
- u32 result_count;
- u32 status;
-
- brcmf_dbg(SCAN, "Enter\n");
-
- if (e->event_code == BRCMF_E_PFN_NET_LOST) {
- brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
- return 0;
- }
-
- pfn_result = (struct brcmf_pno_scanresults_le *)data;
- result_count = le32_to_cpu(pfn_result->count);
- status = le32_to_cpu(pfn_result->status);
-
- /*
- * PFN event is limited to fit 512 bytes so we may get
- * multiple NET_FOUND events. For now place a warning here.
- */
- WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
- brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
- if (result_count > 0) {
- int i;
-
- request = kzalloc(sizeof(*request), GFP_KERNEL);
- ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
- channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
- if (!request || !ssid || !channel) {
- err = -ENOMEM;
- goto out_err;
- }
-
- request->wiphy = wiphy;
- data += sizeof(struct brcmf_pno_scanresults_le);
- netinfo_start = (struct brcmf_pno_net_info_le *)data;
-
- for (i = 0; i < result_count; i++) {
- netinfo = &netinfo_start[i];
- if (!netinfo) {
- brcmf_err("Invalid netinfo ptr. index: %d\n",
- i);
- err = -EINVAL;
- goto out_err;
- }
-
- brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
- netinfo->SSID, netinfo->channel);
- memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
- ssid[i].ssid_len = netinfo->SSID_len;
- request->n_ssids++;
-
- channel_req = netinfo->channel;
- if (channel_req <= CH_MAX_2G_CHANNEL)
- band = NL80211_BAND_2GHZ;
- else
- band = NL80211_BAND_5GHZ;
- channel[i].center_freq =
- ieee80211_channel_to_frequency(channel_req,
- band);
- channel[i].band = band;
- channel[i].flags |= IEEE80211_CHAN_NO_HT40;
- request->channels[i] = &channel[i];
- request->n_channels++;
- }
-
- /* assign parsed ssid array */
- if (request->n_ssids)
- request->ssids = &ssid[0];
-
- if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
- /* Abort any on-going scan */
- brcmf_abort_scanning(cfg);
- }
-
- set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
- cfg->escan_info.run = brcmf_run_escan;
- err = brcmf_do_escan(cfg, wiphy, ifp, request);
- if (err) {
- clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
- goto out_err;
- }
- cfg->sched_escan = true;
- cfg->scan_request = request;
- } else {
- brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
- goto out_err;
- }
-
- kfree(ssid);
- kfree(channel);
- kfree(request);
- return 0;
-
-out_err:
- kfree(ssid);
- kfree(channel);
- kfree(request);
- cfg80211_sched_scan_stopped(wiphy);
- return err;
-}
-
-static int brcmf_dev_pno_clean(struct net_device *ndev)
-{
- int ret;
-
- /* Disable pfn */
- ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
- if (ret == 0) {
- /* clear pfn */
- ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
- NULL, 0);
- }
- if (ret < 0)
- brcmf_err("failed code %d\n", ret);
-
- return ret;
-}
-
-static int brcmf_dev_pno_config(struct net_device *ndev)
-{
- struct brcmf_pno_param_le pfn_param;
-
- memset(&pfn_param, 0, sizeof(pfn_param));
- pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
-
- /* set extra pno params */
- pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
- pfn_param.repeat = BRCMF_PNO_REPEAT;
- pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
-
- /* set up pno scan fr */
- pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
-
- return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
- &pfn_param, sizeof(pfn_param));
-}
-
-static int
-brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
- struct net_device *ndev,
- struct cfg80211_sched_scan_request *request)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct brcmf_pno_net_param_le pfn;
- int i;
- int ret = 0;
-
- brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
- request->n_match_sets, request->n_ssids);
- if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
- brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
- return -EAGAIN;
- }
- if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
- brcmf_err("Scanning suppressed: status (%lu)\n",
- cfg->scan_status);
- return -EAGAIN;
- }
-
- if (!request->n_ssids || !request->n_match_sets) {
- brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
- request->n_ssids);
- return -EINVAL;
- }
-
- if (request->n_ssids > 0) {
- for (i = 0; i < request->n_ssids; i++) {
- /* Active scan req for ssids */
- brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
- request->ssids[i].ssid);
-
- /*
- * match_set ssids is a supert set of n_ssid list,
- * so we need not add these set seperately.
- */
- }
- }
-
- if (request->n_match_sets > 0) {
- /* clean up everything */
- ret = brcmf_dev_pno_clean(ndev);
- if (ret < 0) {
- brcmf_err("failed error=%d\n", ret);
- return ret;
- }
-
- /* configure pno */
- ret = brcmf_dev_pno_config(ndev);
- if (ret < 0) {
- brcmf_err("PNO setup failed!! ret=%d\n", ret);
- return -EINVAL;
- }
-
- /* configure each match set */
- for (i = 0; i < request->n_match_sets; i++) {
- struct cfg80211_ssid *ssid;
- u32 ssid_len;
-
- ssid = &request->match_sets[i].ssid;
- ssid_len = ssid->ssid_len;
-
- if (!ssid_len) {
- brcmf_err("skip broadcast ssid\n");
- continue;
- }
- pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
- pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
- pfn.wsec = cpu_to_le32(0);
- pfn.infra = cpu_to_le32(1);
- pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
- pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
- memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
- ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
- sizeof(pfn));
- brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
- ret == 0 ? "set" : "failed", ssid->ssid);
- }
- /* Enable the PNO */
- if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
- brcmf_err("PNO enable failed!! ret=%d\n", ret);
- return -EINVAL;
- }
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
- struct net_device *ndev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
-
- brcmf_dbg(SCAN, "enter\n");
- brcmf_dev_pno_clean(ndev);
- if (cfg->sched_escan)
- brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
- return 0;
-}
-
-static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
-{
- s32 err;
-
- /* set auth */
- err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
- if (err < 0) {
- brcmf_err("auth error %d\n", err);
- return err;
- }
- /* set wsec */
- err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
- if (err < 0) {
- brcmf_err("wsec error %d\n", err);
- return err;
- }
- /* set upper-layer auth */
- err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
- if (err < 0) {
- brcmf_err("wpa_auth error %d\n", err);
- return err;
- }
-
- return 0;
-}
-
-static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
-{
- if (is_rsn_ie)
- return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
-
- return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
-}
-
-static s32
-brcmf_configure_wpaie(struct brcmf_if *ifp,
- const struct brcmf_vs_tlv *wpa_ie,
- bool is_rsn_ie)
-{
- u32 auth = 0; /* d11 open authentication */
- u16 count;
- s32 err = 0;
- s32 len = 0;
- u32 i;
- u32 wsec;
- u32 pval = 0;
- u32 gval = 0;
- u32 wpa_auth = 0;
- u32 offset;
- u8 *data;
- u16 rsn_cap;
- u32 wme_bss_disable;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (wpa_ie == NULL)
- goto exit;
-
- len = wpa_ie->len + TLV_HDR_LEN;
- data = (u8 *)wpa_ie;
- offset = TLV_HDR_LEN;
- if (!is_rsn_ie)
- offset += VS_IE_FIXED_HDR_LEN;
- else
- offset += WPA_IE_VERSION_LEN;
-
- /* check for multicast cipher suite */
- if (offset + WPA_IE_MIN_OUI_LEN > len) {
- err = -EINVAL;
- brcmf_err("no multicast cipher suite\n");
- goto exit;
- }
-
- if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
- err = -EINVAL;
- brcmf_err("ivalid OUI\n");
- goto exit;
- }
- offset += TLV_OUI_LEN;
-
- /* pick up multicast cipher */
- switch (data[offset]) {
- case WPA_CIPHER_NONE:
- gval = 0;
- break;
- case WPA_CIPHER_WEP_40:
- case WPA_CIPHER_WEP_104:
- gval = WEP_ENABLED;
- break;
- case WPA_CIPHER_TKIP:
- gval = TKIP_ENABLED;
- break;
- case WPA_CIPHER_AES_CCM:
- gval = AES_ENABLED;
- break;
- default:
- err = -EINVAL;
- brcmf_err("Invalid multi cast cipher info\n");
- goto exit;
- }
-
- offset++;
- /* walk thru unicast cipher list and pick up what we recognize */
- count = data[offset] + (data[offset + 1] << 8);
- offset += WPA_IE_SUITE_COUNT_LEN;
- /* Check for unicast suite(s) */
- if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
- err = -EINVAL;
- brcmf_err("no unicast cipher suite\n");
- goto exit;
- }
- for (i = 0; i < count; i++) {
- if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
- err = -EINVAL;
- brcmf_err("ivalid OUI\n");
- goto exit;
- }
- offset += TLV_OUI_LEN;
- switch (data[offset]) {
- case WPA_CIPHER_NONE:
- break;
- case WPA_CIPHER_WEP_40:
- case WPA_CIPHER_WEP_104:
- pval |= WEP_ENABLED;
- break;
- case WPA_CIPHER_TKIP:
- pval |= TKIP_ENABLED;
- break;
- case WPA_CIPHER_AES_CCM:
- pval |= AES_ENABLED;
- break;
- default:
- brcmf_err("Ivalid unicast security info\n");
- }
- offset++;
- }
- /* walk thru auth management suite list and pick up what we recognize */
- count = data[offset] + (data[offset + 1] << 8);
- offset += WPA_IE_SUITE_COUNT_LEN;
- /* Check for auth key management suite(s) */
- if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
- err = -EINVAL;
- brcmf_err("no auth key mgmt suite\n");
- goto exit;
- }
- for (i = 0; i < count; i++) {
- if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
- err = -EINVAL;
- brcmf_err("ivalid OUI\n");
- goto exit;
- }
- offset += TLV_OUI_LEN;
- switch (data[offset]) {
- case RSN_AKM_NONE:
- brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
- wpa_auth |= WPA_AUTH_NONE;
- break;
- case RSN_AKM_UNSPECIFIED:
- brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
- is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
- (wpa_auth |= WPA_AUTH_UNSPECIFIED);
- break;
- case RSN_AKM_PSK:
- brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
- is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
- (wpa_auth |= WPA_AUTH_PSK);
- break;
- default:
- brcmf_err("Ivalid key mgmt info\n");
- }
- offset++;
- }
-
- if (is_rsn_ie) {
- wme_bss_disable = 1;
- if ((offset + RSN_CAP_LEN) <= len) {
- rsn_cap = data[offset] + (data[offset + 1] << 8);
- if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
- wme_bss_disable = 0;
- }
- /* set wme_bss_disable to sync RSN Capabilities */
- err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
- wme_bss_disable);
- if (err < 0) {
- brcmf_err("wme_bss_disable error %d\n", err);
- goto exit;
- }
- }
- /* FOR WPS , set SES_OW_ENABLED */
- wsec = (pval | gval | SES_OW_ENABLED);
-
- /* set auth */
- err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
- if (err < 0) {
- brcmf_err("auth error %d\n", err);
- goto exit;
- }
- /* set wsec */
- err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
- if (err < 0) {
- brcmf_err("wsec error %d\n", err);
- goto exit;
- }
- /* set upper-layer auth */
- err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
- if (err < 0) {
- brcmf_err("wpa_auth error %d\n", err);
- goto exit;
- }
-
-exit:
- return err;
-}
-
-static s32
-brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
- struct parsed_vndr_ies *vndr_ies)
-{
- struct brcmf_vs_tlv *vndrie;
- struct brcmf_tlv *ie;
- struct parsed_vndr_ie_info *parsed_info;
- s32 remaining_len;
-
- remaining_len = (s32)vndr_ie_len;
- memset(vndr_ies, 0, sizeof(*vndr_ies));
-
- ie = (struct brcmf_tlv *)vndr_ie_buf;
- while (ie) {
- if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
- goto next;
- vndrie = (struct brcmf_vs_tlv *)ie;
- /* len should be bigger than OUI length + one */
- if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
- brcmf_err("invalid vndr ie. length is too small %d\n",
- vndrie->len);
- goto next;
- }
- /* if wpa or wme ie, do not add ie */
- if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
- ((vndrie->oui_type == WPA_OUI_TYPE) ||
- (vndrie->oui_type == WME_OUI_TYPE))) {
- brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
- goto next;
- }
-
- parsed_info = &vndr_ies->ie_info[vndr_ies->count];
-
- /* save vndr ie information */
- parsed_info->ie_ptr = (char *)vndrie;
- parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
- memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
-
- vndr_ies->count++;
-
- brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
- parsed_info->vndrie.oui[0],
- parsed_info->vndrie.oui[1],
- parsed_info->vndrie.oui[2],
- parsed_info->vndrie.oui_type);
-
- if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
- break;
-next:
- remaining_len -= (ie->len + TLV_HDR_LEN);
- if (remaining_len <= TLV_HDR_LEN)
- ie = NULL;
- else
- ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
- TLV_HDR_LEN);
- }
- return 0;
-}
-
-static u32
-brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
-{
-
- strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
- iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
-
- put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
-
- put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
-
- memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
-
- return ie_len + VNDR_IE_HDR_SIZE;
-}
-
-s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
- const u8 *vndr_ie_buf, u32 vndr_ie_len)
-{
- struct brcmf_if *ifp;
- struct vif_saved_ie *saved_ie;
- s32 err = 0;
- u8 *iovar_ie_buf;
- u8 *curr_ie_buf;
- u8 *mgmt_ie_buf = NULL;
- int mgmt_ie_buf_len;
- u32 *mgmt_ie_len;
- u32 del_add_ie_buf_len = 0;
- u32 total_ie_buf_len = 0;
- u32 parsed_ie_buf_len = 0;
- struct parsed_vndr_ies old_vndr_ies;
- struct parsed_vndr_ies new_vndr_ies;
- struct parsed_vndr_ie_info *vndrie_info;
- s32 i;
- u8 *ptr;
- int remained_buf_len;
-
- if (!vif)
- return -ENODEV;
- ifp = vif->ifp;
- saved_ie = &vif->saved_ie;
-
- brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
- iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
- if (!iovar_ie_buf)
- return -ENOMEM;
- curr_ie_buf = iovar_ie_buf;
- switch (pktflag) {
- case BRCMF_VNDR_IE_PRBREQ_FLAG:
- mgmt_ie_buf = saved_ie->probe_req_ie;
- mgmt_ie_len = &saved_ie->probe_req_ie_len;
- mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
- break;
- case BRCMF_VNDR_IE_PRBRSP_FLAG:
- mgmt_ie_buf = saved_ie->probe_res_ie;
- mgmt_ie_len = &saved_ie->probe_res_ie_len;
- mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
- break;
- case BRCMF_VNDR_IE_BEACON_FLAG:
- mgmt_ie_buf = saved_ie->beacon_ie;
- mgmt_ie_len = &saved_ie->beacon_ie_len;
- mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
- break;
- case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
- mgmt_ie_buf = saved_ie->assoc_req_ie;
- mgmt_ie_len = &saved_ie->assoc_req_ie_len;
- mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
- break;
- default:
- err = -EPERM;
- brcmf_err("not suitable type\n");
- goto exit;
- }
-
- if (vndr_ie_len > mgmt_ie_buf_len) {
- err = -ENOMEM;
- brcmf_err("extra IE size too big\n");
- goto exit;
- }
-
- /* parse and save new vndr_ie in curr_ie_buff before comparing it */
- if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
- ptr = curr_ie_buf;
- brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
- for (i = 0; i < new_vndr_ies.count; i++) {
- vndrie_info = &new_vndr_ies.ie_info[i];
- memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
- vndrie_info->ie_len);
- parsed_ie_buf_len += vndrie_info->ie_len;
- }
- }
-
- if (mgmt_ie_buf && *mgmt_ie_len) {
- if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
- (memcmp(mgmt_ie_buf, curr_ie_buf,
- parsed_ie_buf_len) == 0)) {
- brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
- goto exit;
- }
-
- /* parse old vndr_ie */
- brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
-
- /* make a command to delete old ie */
- for (i = 0; i < old_vndr_ies.count; i++) {
- vndrie_info = &old_vndr_ies.ie_info[i];
-
- brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
- vndrie_info->vndrie.id,
- vndrie_info->vndrie.len,
- vndrie_info->vndrie.oui[0],
- vndrie_info->vndrie.oui[1],
- vndrie_info->vndrie.oui[2]);
-
- del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
- vndrie_info->ie_ptr,
- vndrie_info->ie_len,
- "del");
- curr_ie_buf += del_add_ie_buf_len;
- total_ie_buf_len += del_add_ie_buf_len;
- }
- }
-
- *mgmt_ie_len = 0;
- /* Add if there is any extra IE */
- if (mgmt_ie_buf && parsed_ie_buf_len) {
- ptr = mgmt_ie_buf;
-
- remained_buf_len = mgmt_ie_buf_len;
-
- /* make a command to add new ie */
- for (i = 0; i < new_vndr_ies.count; i++) {
- vndrie_info = &new_vndr_ies.ie_info[i];
-
- /* verify remained buf size before copy data */
- if (remained_buf_len < (vndrie_info->vndrie.len +
- VNDR_IE_VSIE_OFFSET)) {
- brcmf_err("no space in mgmt_ie_buf: len left %d",
- remained_buf_len);
- break;
- }
- remained_buf_len -= (vndrie_info->ie_len +
- VNDR_IE_VSIE_OFFSET);
-
- brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
- vndrie_info->vndrie.id,
- vndrie_info->vndrie.len,
- vndrie_info->vndrie.oui[0],
- vndrie_info->vndrie.oui[1],
- vndrie_info->vndrie.oui[2]);
-
- del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
- vndrie_info->ie_ptr,
- vndrie_info->ie_len,
- "add");
-
- /* save the parsed IE in wl struct */
- memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
- vndrie_info->ie_len);
- *mgmt_ie_len += vndrie_info->ie_len;
-
- curr_ie_buf += del_add_ie_buf_len;
- total_ie_buf_len += del_add_ie_buf_len;
- }
- }
- if (total_ie_buf_len) {
- err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
- total_ie_buf_len);
- if (err)
- brcmf_err("vndr ie set error : %d\n", err);
- }
-
-exit:
- kfree(iovar_ie_buf);
- return err;
-}
-
-s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
-{
- s32 pktflags[] = {
- BRCMF_VNDR_IE_PRBREQ_FLAG,
- BRCMF_VNDR_IE_PRBRSP_FLAG,
- BRCMF_VNDR_IE_BEACON_FLAG
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(pktflags); i++)
- brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
-
- memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
- return 0;
-}
-
-static s32
-brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
- struct cfg80211_beacon_data *beacon)
-{
- s32 err;
-
- /* Set Beacon IEs to FW */
- err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
- beacon->tail, beacon->tail_len);
- if (err) {
- brcmf_err("Set Beacon IE Failed\n");
- return err;
- }
- brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
-
- /* Set Probe Response IEs to FW */
- err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
- beacon->proberesp_ies,
- beacon->proberesp_ies_len);
- if (err)
- brcmf_err("Set Probe Resp IE Failed\n");
- else
- brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
-
- return err;
-}
-
-static s32
-brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_ap_settings *settings)
-{
- s32 ie_offset;
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- const struct brcmf_tlv *ssid_ie;
- const struct brcmf_tlv *country_ie;
- struct brcmf_ssid_le ssid_le;
- s32 err = -EPERM;
- const struct brcmf_tlv *rsn_ie;
- const struct brcmf_vs_tlv *wpa_ie;
- struct brcmf_join_params join_params;
- enum nl80211_iftype dev_role;
- struct brcmf_fil_bss_enable_le bss_enable;
- u16 chanspec;
- bool mbss;
- int is_11d;
-
- brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
- settings->chandef.chan->hw_value,
- settings->chandef.center_freq1, settings->chandef.width,
- settings->beacon_interval, settings->dtim_period);
- brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
- settings->ssid, settings->ssid_len, settings->auth_type,
- settings->inactivity_timeout);
- dev_role = ifp->vif->wdev.iftype;
- mbss = ifp->vif->mbss;
-
- /* store current 11d setting */
- brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
- country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
- settings->beacon.tail_len,
- WLAN_EID_COUNTRY);
- is_11d = country_ie ? 1 : 0;
-
- memset(&ssid_le, 0, sizeof(ssid_le));
- if (settings->ssid == NULL || settings->ssid_len == 0) {
- ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
- ssid_ie = brcmf_parse_tlvs(
- (u8 *)&settings->beacon.head[ie_offset],
- settings->beacon.head_len - ie_offset,
- WLAN_EID_SSID);
- if (!ssid_ie)
- return -EINVAL;
-
- memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
- ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
- brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
- } else {
- memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
- ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
- }
-
- if (!mbss) {
- brcmf_set_mpc(ifp, 0);
- brcmf_configure_arp_offload(ifp, false);
- }
-
- /* find the RSN_IE */
- rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
- settings->beacon.tail_len, WLAN_EID_RSN);
-
- /* find the WPA_IE */
- wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
- settings->beacon.tail_len);
-
- if ((wpa_ie != NULL || rsn_ie != NULL)) {
- brcmf_dbg(TRACE, "WPA(2) IE is found\n");
- if (wpa_ie != NULL) {
- /* WPA IE */
- err = brcmf_configure_wpaie(ifp, wpa_ie, false);
- if (err < 0)
- goto exit;
- } else {
- struct brcmf_vs_tlv *tmp_ie;
-
- tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
-
- /* RSN IE */
- err = brcmf_configure_wpaie(ifp, tmp_ie, true);
- if (err < 0)
- goto exit;
- }
- } else {
- brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
- brcmf_configure_opensecurity(ifp);
- }
-
- brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
-
- if (!mbss) {
- chanspec = chandef_to_chanspec(&cfg->d11inf,
- &settings->chandef);
- err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
- if (err < 0) {
- brcmf_err("Set Channel failed: chspec=%d, %d\n",
- chanspec, err);
- goto exit;
- }
-
- if (is_11d != ifp->vif->is_11d) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
- is_11d);
- if (err < 0) {
- brcmf_err("Regulatory Set Error, %d\n", err);
- goto exit;
- }
- }
- if (settings->beacon_interval) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
- settings->beacon_interval);
- if (err < 0) {
- brcmf_err("Beacon Interval Set Error, %d\n",
- err);
- goto exit;
- }
- }
- if (settings->dtim_period) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
- settings->dtim_period);
- if (err < 0) {
- brcmf_err("DTIM Interval Set Error, %d\n", err);
- goto exit;
- }
- }
-
- if (dev_role == NL80211_IFTYPE_AP) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
- if (err < 0) {
- brcmf_err("BRCMF_C_DOWN error %d\n", err);
- goto exit;
- }
- brcmf_fil_iovar_int_set(ifp, "apsta", 0);
- }
-
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
- if (err < 0) {
- brcmf_err("SET INFRA error %d\n", err);
- goto exit;
- }
- } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
- /* Multiple-BSS should use same 11d configuration */
- err = -EINVAL;
- goto exit;
- }
- if (dev_role == NL80211_IFTYPE_AP) {
- if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
- brcmf_fil_iovar_int_set(ifp, "mbss", 1);
-
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
- if (err < 0) {
- brcmf_err("setting AP mode failed %d\n", err);
- goto exit;
- }
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
- if (err < 0) {
- brcmf_err("BRCMF_C_UP error (%d)\n", err);
- goto exit;
- }
- /* On DOWN the firmware removes the WEP keys, reconfigure
- * them if they were set.
- */
- brcmf_cfg80211_reconfigure_wep(ifp);
-
- memset(&join_params, 0, sizeof(join_params));
- /* join parameters starts with ssid */
- memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
- /* create softap */
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
- &join_params, sizeof(join_params));
- if (err < 0) {
- brcmf_err("SET SSID error (%d)\n", err);
- goto exit;
- }
- brcmf_dbg(TRACE, "AP mode configuration complete\n");
- } else {
- err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
- sizeof(ssid_le));
- if (err < 0) {
- brcmf_err("setting ssid failed %d\n", err);
- goto exit;
- }
- bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
- bss_enable.enable = cpu_to_le32(1);
- err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
- sizeof(bss_enable));
- if (err < 0) {
- brcmf_err("bss_enable config failed %d\n", err);
- goto exit;
- }
-
- brcmf_dbg(TRACE, "GO mode configuration complete\n");
- }
- set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
- brcmf_net_setcarrier(ifp, true);
-
-exit:
- if ((err) && (!mbss)) {
- brcmf_set_mpc(ifp, 1);
- brcmf_configure_arp_offload(ifp, true);
- }
- return err;
-}
-
-static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err;
- struct brcmf_fil_bss_enable_le bss_enable;
- struct brcmf_join_params join_params;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
- /* Due to most likely deauths outstanding we sleep */
- /* first to make sure they get processed by fw. */
- msleep(400);
-
- if (ifp->vif->mbss) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
- return err;
- }
-
- memset(&join_params, 0, sizeof(join_params));
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
- &join_params, sizeof(join_params));
- if (err < 0)
- brcmf_err("SET SSID error (%d)\n", err);
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
- if (err < 0)
- brcmf_err("BRCMF_C_DOWN error %d\n", err);
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
- if (err < 0)
- brcmf_err("setting AP mode failed %d\n", err);
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
- if (err < 0)
- brcmf_err("setting INFRA mode failed %d\n", err);
- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
- brcmf_fil_iovar_int_set(ifp, "mbss", 0);
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
- ifp->vif->is_11d);
- if (err < 0)
- brcmf_err("restoring REGULATORY setting failed %d\n",
- err);
- /* Bring device back up so it can be used again */
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
- if (err < 0)
- brcmf_err("BRCMF_C_UP error %d\n", err);
- } else {
- bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
- bss_enable.enable = cpu_to_le32(0);
- err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
- sizeof(bss_enable));
- if (err < 0)
- brcmf_err("bss_enable config failed %d\n", err);
- }
- brcmf_set_mpc(ifp, 1);
- brcmf_configure_arp_offload(ifp, true);
- clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
- brcmf_net_setcarrier(ifp, false);
-
- return err;
-}
-
-static s32
-brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_beacon_data *info)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
-
- return err;
-}
-
-static int
-brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
- struct station_del_parameters *params)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_scb_val_le scbval;
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err;
-
- if (!params->mac)
- return -EFAULT;
-
- brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
-
- if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
- ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- memcpy(&scbval.ea, params->mac, ETH_ALEN);
- scbval.val = cpu_to_le32(params->reason_code);
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
- &scbval, sizeof(scbval));
- if (err)
- brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static int
-brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
- const u8 *mac, struct station_parameters *params)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err;
-
- brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
- params->sta_flags_mask, params->sta_flags_set);
-
- /* Ignore all 00 MAC */
- if (is_zero_ether_addr(mac))
- return 0;
-
- if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
- return 0;
-
- if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
- (void *)mac, ETH_ALEN);
- else
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
- (void *)mac, ETH_ALEN);
- if (err < 0)
- brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
-
- return err;
-}
-
-static void
-brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u16 frame_type, bool reg)
-{
- struct brcmf_cfg80211_vif *vif;
- u16 mgmt_type;
-
- brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
-
- mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
- vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
- if (reg)
- vif->mgmt_rx_reg |= BIT(mgmt_type);
- else
- vif->mgmt_rx_reg &= ~BIT(mgmt_type);
-}
-
-
-static int
-brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct cfg80211_mgmt_tx_params *params, u64 *cookie)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct ieee80211_channel *chan = params->chan;
- const u8 *buf = params->buf;
- size_t len = params->len;
- const struct ieee80211_mgmt *mgmt;
- struct brcmf_cfg80211_vif *vif;
- s32 err = 0;
- s32 ie_offset;
- s32 ie_len;
- struct brcmf_fil_action_frame_le *action_frame;
- struct brcmf_fil_af_params_le *af_params;
- bool ack;
- s32 chan_nr;
- u32 freq;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- *cookie = 0;
-
- mgmt = (const struct ieee80211_mgmt *)buf;
-
- if (!ieee80211_is_mgmt(mgmt->frame_control)) {
- brcmf_err("Driver only allows MGMT packet type\n");
- 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 */
- /* wpa_supplicant. Unfortunately the probe is send */
- /* on primary ndev, while dongle wants it on the p2p */
- /* vif. Since this is only reason for a probe */
- /* response to be sent, the vif is taken from cfg. */
- /* If ever desired to send proberesp for non p2p */
- /* response then data should be checked for */
- /* "DIRECT-". Note in future supplicant will take */
- /* dedicated p2p wdev to do this and then this 'hack'*/
- /* is not needed anymore. */
- ie_offset = DOT11_MGMT_HDR_LEN +
- DOT11_BCN_PRB_FIXED_LEN;
- ie_len = len - ie_offset;
- 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,
- BRCMF_VNDR_IE_PRBRSP_FLAG,
- &buf[ie_offset],
- ie_len);
- cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
- GFP_KERNEL);
- } else if (ieee80211_is_action(mgmt->frame_control)) {
- af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
- if (af_params == NULL) {
- brcmf_err("unable to allocate frame\n");
- err = -ENOMEM;
- goto exit;
- }
- action_frame = &af_params->action_frame;
- /* Add the packet Id */
- action_frame->packet_id = cpu_to_le32(*cookie);
- /* Add BSSID */
- memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
- 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. 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), freq);
-
- ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
- af_params);
-
- cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
- GFP_KERNEL);
- kfree(af_params);
- } else {
- brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
- brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
- }
-
-exit:
- return err;
-}
-
-
-static int
-brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u64 cookie)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_cfg80211_vif *vif;
- int err = 0;
-
- brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
-
- vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
- if (vif == NULL) {
- brcmf_err("No p2p device available for probe response\n");
- err = -ENODEV;
- goto exit;
- }
- brcmf_p2p_cancel_remain_on_channel(vif->ifp);
-exit:
- return err;
-}
-
-static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- enum nl80211_crit_proto_id proto,
- u16 duration)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_cfg80211_vif *vif;
-
- vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
-
- /* only DHCP support for now */
- if (proto != NL80211_CRIT_PROTO_DHCP)
- return -EINVAL;
-
- /* suppress and abort scanning */
- set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
- brcmf_abort_scanning(cfg);
-
- return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
-}
-
-static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
- struct wireless_dev *wdev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_cfg80211_vif *vif;
-
- vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
-
- brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
- clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
-}
-
-static s32
-brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e, void *data)
-{
- switch (e->reason) {
- case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
- brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
- break;
- case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
- brcmf_dbg(TRACE, "TDLS Peer Connected\n");
- brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
- break;
- case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
- brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
- brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
- break;
- }
-
- return 0;
-}
-
-static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
-{
- int ret;
-
- switch (oper) {
- case NL80211_TDLS_DISCOVERY_REQ:
- ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
- break;
- case NL80211_TDLS_SETUP:
- ret = BRCMF_TDLS_MANUAL_EP_CREATE;
- break;
- case NL80211_TDLS_TEARDOWN:
- ret = BRCMF_TDLS_MANUAL_EP_DELETE;
- break;
- default:
- brcmf_err("unsupported operation: %d\n", oper);
- ret = -EOPNOTSUPP;
- }
- return ret;
-}
-
-static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
- struct net_device *ndev, const u8 *peer,
- enum nl80211_tdls_operation oper)
-{
- struct brcmf_if *ifp;
- struct brcmf_tdls_iovar_le info;
- int ret = 0;
-
- ret = brcmf_convert_nl80211_tdls_oper(oper);
- if (ret < 0)
- return ret;
-
- ifp = netdev_priv(ndev);
- memset(&info, 0, sizeof(info));
- info.mode = (u8)ret;
- if (peer)
- memcpy(info.ea, peer, ETH_ALEN);
-
- ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
- &info, sizeof(info));
- if (ret < 0)
- brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
-
- return ret;
-}
-
-static struct cfg80211_ops wl_cfg80211_ops = {
- .add_virtual_intf = brcmf_cfg80211_add_iface,
- .del_virtual_intf = brcmf_cfg80211_del_iface,
- .change_virtual_intf = brcmf_cfg80211_change_iface,
- .scan = brcmf_cfg80211_scan,
- .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
- .join_ibss = brcmf_cfg80211_join_ibss,
- .leave_ibss = brcmf_cfg80211_leave_ibss,
- .get_station = brcmf_cfg80211_get_station,
- .dump_station = brcmf_cfg80211_dump_station,
- .set_tx_power = brcmf_cfg80211_set_tx_power,
- .get_tx_power = brcmf_cfg80211_get_tx_power,
- .add_key = brcmf_cfg80211_add_key,
- .del_key = brcmf_cfg80211_del_key,
- .get_key = brcmf_cfg80211_get_key,
- .set_default_key = brcmf_cfg80211_config_default_key,
- .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
- .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
- .connect = brcmf_cfg80211_connect,
- .disconnect = brcmf_cfg80211_disconnect,
- .suspend = brcmf_cfg80211_suspend,
- .resume = brcmf_cfg80211_resume,
- .set_pmksa = brcmf_cfg80211_set_pmksa,
- .del_pmksa = brcmf_cfg80211_del_pmksa,
- .flush_pmksa = brcmf_cfg80211_flush_pmksa,
- .start_ap = brcmf_cfg80211_start_ap,
- .stop_ap = brcmf_cfg80211_stop_ap,
- .change_beacon = brcmf_cfg80211_change_beacon,
- .del_station = brcmf_cfg80211_del_station,
- .change_station = brcmf_cfg80211_change_station,
- .sched_scan_start = brcmf_cfg80211_sched_scan_start,
- .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
- .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
- .mgmt_tx = brcmf_cfg80211_mgmt_tx,
- .remain_on_channel = brcmf_p2p_remain_on_channel,
- .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
- .start_p2p_device = brcmf_p2p_start_device,
- .stop_p2p_device = brcmf_p2p_stop_device,
- .crit_proto_start = brcmf_cfg80211_crit_proto_start,
- .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
- .tdls_oper = brcmf_cfg80211_tdls_oper,
-};
-
-struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
- enum nl80211_iftype type,
- bool pm_block)
-{
- struct brcmf_cfg80211_vif *vif_walk;
- struct brcmf_cfg80211_vif *vif;
- bool mbss;
-
- brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
- sizeof(*vif));
- vif = kzalloc(sizeof(*vif), GFP_KERNEL);
- if (!vif)
- return ERR_PTR(-ENOMEM);
-
- vif->wdev.wiphy = cfg->wiphy;
- vif->wdev.iftype = type;
-
- vif->pm_block = pm_block;
- vif->roam_off = -1;
-
- brcmf_init_prof(&vif->profile);
-
- if (type == NL80211_IFTYPE_AP) {
- mbss = false;
- list_for_each_entry(vif_walk, &cfg->vif_list, list) {
- if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
- mbss = true;
- break;
- }
- }
- vif->mbss = mbss;
- }
-
- list_add_tail(&vif->list, &cfg->vif_list);
- return vif;
-}
-
-void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
-{
- list_del(&vif->list);
- kfree(vif);
-}
-
-void brcmf_cfg80211_free_netdev(struct net_device *ndev)
-{
- struct brcmf_cfg80211_vif *vif;
- struct brcmf_if *ifp;
-
- ifp = netdev_priv(ndev);
- vif = ifp->vif;
-
- if (vif)
- brcmf_free_vif(vif);
- free_netdev(ndev);
-}
-
-static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
-{
- u32 event = e->event_code;
- u32 status = e->status;
-
- if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
- brcmf_dbg(CONN, "Processing set ssid\n");
- return true;
- }
-
- return false;
-}
-
-static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
-{
- u32 event = e->event_code;
- u16 flags = e->flags;
-
- if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
- (event == BRCMF_E_DISASSOC_IND) ||
- ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
- brcmf_dbg(CONN, "Processing link down\n");
- return true;
- }
- return false;
-}
-
-static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
- const struct brcmf_event_msg *e)
-{
- u32 event = e->event_code;
- u32 status = e->status;
-
- if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
- brcmf_dbg(CONN, "Processing Link %s & no network found\n",
- e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
- return true;
- }
-
- if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
- brcmf_dbg(CONN, "Processing connecting & no network found\n");
- return true;
- }
-
- return false;
-}
-
-static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
-{
- struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
-
- kfree(conn_info->req_ie);
- conn_info->req_ie = NULL;
- conn_info->req_ie_len = 0;
- kfree(conn_info->resp_ie);
- conn_info->resp_ie = NULL;
- conn_info->resp_ie_len = 0;
-}
-
-static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
- struct brcmf_if *ifp)
-{
- struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
- struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
- u32 req_len;
- u32 resp_len;
- s32 err = 0;
-
- brcmf_clear_assoc_ies(cfg);
-
- err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
- cfg->extra_buf, WL_ASSOC_INFO_MAX);
- if (err) {
- brcmf_err("could not get assoc info (%d)\n", err);
- return err;
- }
- assoc_info =
- (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
- req_len = le32_to_cpu(assoc_info->req_len);
- resp_len = le32_to_cpu(assoc_info->resp_len);
- if (req_len) {
- err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
- cfg->extra_buf,
- WL_ASSOC_INFO_MAX);
- if (err) {
- brcmf_err("could not get assoc req (%d)\n", err);
- return err;
- }
- conn_info->req_ie_len = req_len;
- conn_info->req_ie =
- kmemdup(cfg->extra_buf, conn_info->req_ie_len,
- GFP_KERNEL);
- } else {
- conn_info->req_ie_len = 0;
- conn_info->req_ie = NULL;
- }
- if (resp_len) {
- err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
- cfg->extra_buf,
- WL_ASSOC_INFO_MAX);
- if (err) {
- brcmf_err("could not get assoc resp (%d)\n", err);
- return err;
- }
- conn_info->resp_ie_len = resp_len;
- conn_info->resp_ie =
- kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
- GFP_KERNEL);
- } else {
- conn_info->resp_ie_len = 0;
- conn_info->resp_ie = NULL;
- }
- brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
- conn_info->req_ie_len, conn_info->resp_ie_len);
-
- return err;
-}
-
-static s32
-brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev,
- const struct brcmf_event_msg *e)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
- struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
- struct wiphy *wiphy = cfg_to_wiphy(cfg);
- struct ieee80211_channel *notify_channel = NULL;
- struct ieee80211_supported_band *band;
- struct brcmf_bss_info_le *bi;
- struct brcmu_chan ch;
- u32 freq;
- s32 err = 0;
- u8 *buf;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- brcmf_get_assoc_ies(cfg, ifp);
- memcpy(profile->bssid, e->addr, ETH_ALEN);
- brcmf_update_bss_info(cfg, ifp);
-
- buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
- if (buf == NULL) {
- err = -ENOMEM;
- goto done;
- }
-
- /* data sent to dongle has to be little endian */
- *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
- buf, WL_BSS_INFO_MAX);
-
- if (err)
- goto done;
-
- bi = (struct brcmf_bss_info_le *)(buf + 4);
- ch.chspec = le16_to_cpu(bi->chanspec);
- cfg->d11inf.decchspec(&ch);
-
- if (ch.band == BRCMU_CHAN_BAND_2G)
- band = wiphy->bands[IEEE80211_BAND_2GHZ];
- else
- band = wiphy->bands[IEEE80211_BAND_5GHZ];
-
- freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
- notify_channel = ieee80211_get_channel(wiphy, freq);
-
-done:
- kfree(buf);
- cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
- conn_info->req_ie, conn_info->req_ie_len,
- conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
- brcmf_dbg(CONN, "Report roaming result\n");
-
- set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev, const struct brcmf_event_msg *e,
- bool completed)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
- struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
- &ifp->vif->sme_state)) {
- if (completed) {
- brcmf_get_assoc_ies(cfg, ifp);
- memcpy(profile->bssid, e->addr, ETH_ALEN);
- brcmf_update_bss_info(cfg, ifp);
- set_bit(BRCMF_VIF_STATUS_CONNECTED,
- &ifp->vif->sme_state);
- }
- cfg80211_connect_result(ndev,
- (u8 *)profile->bssid,
- conn_info->req_ie,
- conn_info->req_ie_len,
- conn_info->resp_ie,
- conn_info->resp_ie_len,
- completed ? WLAN_STATUS_SUCCESS :
- WLAN_STATUS_AUTH_TIMEOUT,
- GFP_KERNEL);
- brcmf_dbg(CONN, "Report connect result - connection %s\n",
- completed ? "succeeded" : "failed");
- }
- brcmf_dbg(TRACE, "Exit\n");
- return 0;
-}
-
-static s32
-brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev,
- const struct brcmf_event_msg *e, void *data)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- static int generation;
- u32 event = e->event_code;
- u32 reason = e->reason;
- struct station_info sinfo;
-
- brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
- if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
- ndev != cfg_to_ndev(cfg)) {
- brcmf_dbg(CONN, "AP mode link down\n");
- complete(&cfg->vif_disabled);
- if (ifp->vif->mbss)
- brcmf_remove_interface(ifp);
- return 0;
- }
-
- if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
- (reason == BRCMF_E_STATUS_SUCCESS)) {
- memset(&sinfo, 0, sizeof(sinfo));
- if (!data) {
- brcmf_err("No IEs present in ASSOC/REASSOC_IND");
- return -EINVAL;
- }
- sinfo.assoc_req_ies = data;
- sinfo.assoc_req_ies_len = e->datalen;
- generation++;
- sinfo.generation = generation;
- cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
- } else if ((event == BRCMF_E_DISASSOC_IND) ||
- (event == BRCMF_E_DEAUTH_IND) ||
- (event == BRCMF_E_DEAUTH)) {
- cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
- }
- return 0;
-}
-
-static s32
-brcmf_notify_connect_status(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e, void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- struct net_device *ndev = ifp->ndev;
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
- struct ieee80211_channel *chan;
- s32 err = 0;
-
- if ((e->event_code == BRCMF_E_DEAUTH) ||
- (e->event_code == BRCMF_E_DEAUTH_IND) ||
- (e->event_code == BRCMF_E_DISASSOC_IND) ||
- ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
- brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
- }
-
- if (brcmf_is_apmode(ifp->vif)) {
- err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
- } else if (brcmf_is_linkup(e)) {
- brcmf_dbg(CONN, "Linkup\n");
- if (brcmf_is_ibssmode(ifp->vif)) {
- chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
- memcpy(profile->bssid, e->addr, ETH_ALEN);
- wl_inform_ibss(cfg, ndev, e->addr);
- cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
- clear_bit(BRCMF_VIF_STATUS_CONNECTING,
- &ifp->vif->sme_state);
- set_bit(BRCMF_VIF_STATUS_CONNECTED,
- &ifp->vif->sme_state);
- } else
- brcmf_bss_connect_done(cfg, ndev, e, true);
- brcmf_net_setcarrier(ifp, true);
- } else if (brcmf_is_linkdown(e)) {
- brcmf_dbg(CONN, "Linkdown\n");
- if (!brcmf_is_ibssmode(ifp->vif)) {
- brcmf_bss_connect_done(cfg, ndev, e, false);
- }
- brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
- brcmf_init_prof(ndev_to_prof(ndev));
- if (ndev != cfg_to_ndev(cfg))
- complete(&cfg->vif_disabled);
- brcmf_net_setcarrier(ifp, false);
- } else if (brcmf_is_nonetwork(cfg, e)) {
- if (brcmf_is_ibssmode(ifp->vif))
- clear_bit(BRCMF_VIF_STATUS_CONNECTING,
- &ifp->vif->sme_state);
- else
- brcmf_bss_connect_done(cfg, ndev, e, false);
- }
-
- return err;
-}
-
-static s32
-brcmf_notify_roaming_status(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e, void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- u32 event = e->event_code;
- u32 status = e->status;
-
- if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
- if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
- brcmf_bss_roaming_done(cfg, ifp->ndev, e);
- else
- brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
- }
-
- return 0;
-}
-
-static s32
-brcmf_notify_mic_status(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e, void *data)
-{
- u16 flags = e->flags;
- enum nl80211_key_type key_type;
-
- if (flags & BRCMF_EVENT_MSG_GROUP)
- key_type = NL80211_KEYTYPE_GROUP;
- else
- key_type = NL80211_KEYTYPE_PAIRWISE;
-
- cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
- NULL, GFP_KERNEL);
-
- return 0;
-}
-
-static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e, void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
- struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
- struct brcmf_cfg80211_vif *vif;
-
- brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
- ifevent->action, ifevent->flags, ifevent->ifidx,
- ifevent->bssidx);
-
- mutex_lock(&event->vif_event_lock);
- event->action = ifevent->action;
- vif = event->vif;
-
- switch (ifevent->action) {
- case BRCMF_E_IF_ADD:
- /* waiting process may have timed out */
- if (!cfg->vif_event.vif) {
- mutex_unlock(&event->vif_event_lock);
- return -EBADF;
- }
-
- ifp->vif = vif;
- vif->ifp = ifp;
- if (ifp->ndev) {
- vif->wdev.netdev = ifp->ndev;
- ifp->ndev->ieee80211_ptr = &vif->wdev;
- SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
- }
- mutex_unlock(&event->vif_event_lock);
- wake_up(&event->vif_wq);
- return 0;
-
- case BRCMF_E_IF_DEL:
- mutex_unlock(&event->vif_event_lock);
- /* event may not be upon user request */
- if (brcmf_cfg80211_vif_event_armed(cfg))
- wake_up(&event->vif_wq);
- return 0;
-
- case BRCMF_E_IF_CHANGE:
- mutex_unlock(&event->vif_event_lock);
- wake_up(&event->vif_wq);
- return 0;
-
- default:
- mutex_unlock(&event->vif_event_lock);
- break;
- }
- return -EINVAL;
-}
-
-static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
-{
- conf->frag_threshold = (u32)-1;
- conf->rts_threshold = (u32)-1;
- conf->retry_short = (u32)-1;
- conf->retry_long = (u32)-1;
- conf->tx_power = -1;
-}
-
-static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
-{
- brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
- brcmf_notify_connect_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
- brcmf_notify_connect_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
- brcmf_notify_connect_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
- brcmf_notify_connect_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
- brcmf_notify_connect_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
- brcmf_notify_connect_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
- brcmf_notify_roaming_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
- brcmf_notify_mic_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
- brcmf_notify_connect_status);
- brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
- brcmf_notify_sched_scan_results);
- brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
- brcmf_notify_vif_event);
- brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
- brcmf_p2p_notify_rx_mgmt_p2p_probereq);
- brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
- brcmf_p2p_notify_listen_complete);
- brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
- brcmf_p2p_notify_action_frame_rx);
- brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
- brcmf_p2p_notify_action_tx_complete);
- brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
- brcmf_p2p_notify_action_tx_complete);
-}
-
-static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
-{
- kfree(cfg->conf);
- cfg->conf = NULL;
- kfree(cfg->escan_ioctl_buf);
- cfg->escan_ioctl_buf = NULL;
- kfree(cfg->extra_buf);
- cfg->extra_buf = NULL;
- kfree(cfg->pmk_list);
- cfg->pmk_list = NULL;
-}
-
-static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
-{
- cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
- if (!cfg->conf)
- goto init_priv_mem_out;
- cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
- if (!cfg->escan_ioctl_buf)
- goto init_priv_mem_out;
- cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
- if (!cfg->extra_buf)
- goto init_priv_mem_out;
- cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
- if (!cfg->pmk_list)
- goto init_priv_mem_out;
-
- return 0;
-
-init_priv_mem_out:
- brcmf_deinit_priv_mem(cfg);
-
- return -ENOMEM;
-}
-
-static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
-{
- s32 err = 0;
-
- cfg->scan_request = NULL;
- cfg->pwr_save = true;
- cfg->active_scan = true; /* we do active scan per default */
- cfg->dongle_up = false; /* dongle is not up yet */
- err = brcmf_init_priv_mem(cfg);
- if (err)
- return err;
- brcmf_register_event_handlers(cfg);
- mutex_init(&cfg->usr_sync);
- brcmf_init_escan(cfg);
- brcmf_init_conf(cfg->conf);
- init_completion(&cfg->vif_disabled);
- return err;
-}
-
-static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
-{
- cfg->dongle_up = false; /* dongle down */
- brcmf_abort_scanning(cfg);
- brcmf_deinit_priv_mem(cfg);
-}
-
-static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
-{
- init_waitqueue_head(&event->vif_wq);
- mutex_init(&event->vif_event_lock);
-}
-
-static s32
-brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
-{
- s32 err = 0;
- __le32 roamtrigger[2];
- __le32 roam_delta[2];
-
- /*
- * Setup timeout if Beacons are lost and roam is
- * off to report link down
- */
- if (brcmf_roamoff) {
- err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
- if (err) {
- brcmf_err("bcn_timeout error (%d)\n", err);
- goto dongle_rom_out;
- }
- }
-
- /*
- * Enable/Disable built-in roaming to allow supplicant
- * to take care of roaming
- */
- brcmf_dbg(INFO, "Internal Roaming = %s\n",
- brcmf_roamoff ? "Off" : "On");
- err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
- if (err) {
- brcmf_err("roam_off error (%d)\n", err);
- goto dongle_rom_out;
- }
-
- roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
- roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
- (void *)roamtrigger, sizeof(roamtrigger));
- if (err) {
- brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
- goto dongle_rom_out;
- }
-
- roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
- roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
- (void *)roam_delta, sizeof(roam_delta));
- if (err) {
- brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
- goto dongle_rom_out;
- }
-
-dongle_rom_out:
- return err;
-}
-
-static s32
-brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
- s32 scan_unassoc_time, s32 scan_passive_time)
-{
- s32 err = 0;
-
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
- scan_assoc_time);
- if (err) {
- if (err == -EOPNOTSUPP)
- brcmf_dbg(INFO, "Scan assoc time is not supported\n");
- else
- brcmf_err("Scan assoc time error (%d)\n", err);
- goto dongle_scantime_out;
- }
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
- scan_unassoc_time);
- if (err) {
- if (err == -EOPNOTSUPP)
- brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
- else
- brcmf_err("Scan unassoc time error (%d)\n", err);
- goto dongle_scantime_out;
- }
-
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
- scan_passive_time);
- if (err) {
- if (err == -EOPNOTSUPP)
- brcmf_dbg(INFO, "Scan passive time is not supported\n");
- else
- brcmf_err("Scan passive time error (%d)\n", err);
- goto dongle_scantime_out;
- }
-
-dongle_scantime_out:
- return err;
-}
-
-static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
- struct brcmu_chan *ch)
-{
- u32 ht40_flag;
-
- ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
- if (ch->sb == BRCMU_CHAN_SB_U) {
- if (ht40_flag == IEEE80211_CHAN_NO_HT40)
- channel->flags &= ~IEEE80211_CHAN_NO_HT40;
- channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
- } else {
- /* It should be one of
- * IEEE80211_CHAN_NO_HT40 or
- * IEEE80211_CHAN_NO_HT40PLUS
- */
- channel->flags &= ~IEEE80211_CHAN_NO_HT40;
- if (ht40_flag == IEEE80211_CHAN_NO_HT40)
- channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
- }
-}
-
-static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
- u32 bw_cap[])
-{
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- struct ieee80211_supported_band *band;
- struct ieee80211_channel *channel;
- struct wiphy *wiphy;
- struct brcmf_chanspec_list *list;
- struct brcmu_chan ch;
- int err;
- u8 *pbuf;
- u32 i, j;
- u32 total;
- u32 chaninfo;
- u32 index;
-
- pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
-
- if (pbuf == NULL)
- return -ENOMEM;
-
- list = (struct brcmf_chanspec_list *)pbuf;
-
- err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
- BRCMF_DCMD_MEDLEN);
- if (err) {
- brcmf_err("get chanspecs error (%d)\n", err);
- goto fail_pbuf;
- }
-
- wiphy = cfg_to_wiphy(cfg);
- band = wiphy->bands[IEEE80211_BAND_2GHZ];
- if (band)
- for (i = 0; i < band->n_channels; i++)
- band->channels[i].flags = IEEE80211_CHAN_DISABLED;
- band = wiphy->bands[IEEE80211_BAND_5GHZ];
- if (band)
- for (i = 0; i < band->n_channels; i++)
- band->channels[i].flags = IEEE80211_CHAN_DISABLED;
-
- total = le32_to_cpu(list->count);
- for (i = 0; i < total; i++) {
- ch.chspec = (u16)le32_to_cpu(list->element[i]);
- cfg->d11inf.decchspec(&ch);
-
- if (ch.band == BRCMU_CHAN_BAND_2G) {
- band = wiphy->bands[IEEE80211_BAND_2GHZ];
- } else if (ch.band == BRCMU_CHAN_BAND_5G) {
- band = wiphy->bands[IEEE80211_BAND_5GHZ];
- } else {
- brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
- continue;
- }
- if (!band)
- continue;
- if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
- ch.bw == BRCMU_CHAN_BW_40)
- continue;
- if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
- ch.bw == BRCMU_CHAN_BW_80)
- continue;
-
- channel = band->channels;
- index = band->n_channels;
- for (j = 0; j < band->n_channels; j++) {
- if (channel[j].hw_value == ch.chnum) {
- index = j;
- break;
- }
- }
- channel[index].center_freq =
- ieee80211_channel_to_frequency(ch.chnum, band->band);
- channel[index].hw_value = ch.chnum;
-
- /* assuming the chanspecs order is HT20,
- * HT40 upper, HT40 lower, and VHT80.
- */
- if (ch.bw == BRCMU_CHAN_BW_80) {
- channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
- } else if (ch.bw == BRCMU_CHAN_BW_40) {
- brcmf_update_bw40_channel_flag(&channel[index], &ch);
- } else {
- /* enable the channel and disable other bandwidths
- * for now as mentioned order assure they are enabled
- * for subsequent chanspecs.
- */
- channel[index].flags = IEEE80211_CHAN_NO_HT40 |
- IEEE80211_CHAN_NO_80MHZ;
- ch.bw = BRCMU_CHAN_BW_20;
- cfg->d11inf.encchspec(&ch);
- chaninfo = ch.chspec;
- err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
- &chaninfo);
- if (!err) {
- if (chaninfo & WL_CHAN_RADAR)
- channel[index].flags |=
- (IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_NO_IR);
- if (chaninfo & WL_CHAN_PASSIVE)
- channel[index].flags |=
- IEEE80211_CHAN_NO_IR;
- }
- }
- }
-
-fail_pbuf:
- kfree(pbuf);
- return err;
-}
-
-static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
-{
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- struct ieee80211_supported_band *band;
- struct brcmf_fil_bwcap_le band_bwcap;
- struct brcmf_chanspec_list *list;
- u8 *pbuf;
- u32 val;
- int err;
- struct brcmu_chan ch;
- u32 num_chan;
- int i, j;
-
- /* verify support for bw_cap command */
- val = WLC_BAND_5G;
- err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
-
- if (!err) {
- /* only set 2G bandwidth using bw_cap command */
- band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
- band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
- err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
- sizeof(band_bwcap));
- } else {
- brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
- val = WLC_N_BW_40ALL;
- err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
- }
-
- if (!err) {
- /* update channel info in 2G band */
- pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
-
- if (pbuf == NULL)
- return -ENOMEM;
-
- ch.band = BRCMU_CHAN_BAND_2G;
- ch.bw = BRCMU_CHAN_BW_40;
- ch.sb = BRCMU_CHAN_SB_NONE;
- ch.chnum = 0;
- cfg->d11inf.encchspec(&ch);
-
- /* pass encoded chanspec in query */
- *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
-
- err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
- BRCMF_DCMD_MEDLEN);
- if (err) {
- brcmf_err("get chanspecs error (%d)\n", err);
- kfree(pbuf);
- return err;
- }
-
- band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
- list = (struct brcmf_chanspec_list *)pbuf;
- num_chan = le32_to_cpu(list->count);
- for (i = 0; i < num_chan; i++) {
- ch.chspec = (u16)le32_to_cpu(list->element[i]);
- cfg->d11inf.decchspec(&ch);
- if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
- continue;
- if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
- continue;
- for (j = 0; j < band->n_channels; j++) {
- if (band->channels[j].hw_value == ch.chnum)
- break;
- }
- if (WARN_ON(j == band->n_channels))
- continue;
-
- brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
- }
- kfree(pbuf);
- }
- return err;
-}
-
-static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
-{
- u32 band, mimo_bwcap;
- int err;
-
- band = WLC_BAND_2G;
- err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
- if (!err) {
- bw_cap[IEEE80211_BAND_2GHZ] = band;
- band = WLC_BAND_5G;
- err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
- if (!err) {
- bw_cap[IEEE80211_BAND_5GHZ] = band;
- return;
- }
- WARN_ON(1);
- return;
- }
- brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
- mimo_bwcap = 0;
- err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
- if (err)
- /* assume 20MHz if firmware does not give a clue */
- mimo_bwcap = WLC_N_BW_20ALL;
-
- switch (mimo_bwcap) {
- case WLC_N_BW_40ALL:
- bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
- /* fall-thru */
- case WLC_N_BW_20IN2G_40IN5G:
- bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
- /* fall-thru */
- case WLC_N_BW_20ALL:
- bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
- bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
- break;
- default:
- brcmf_err("invalid mimo_bw_cap value\n");
- }
-}
-
-static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
- u32 bw_cap[2], u32 nchain)
-{
- band->ht_cap.ht_supported = true;
- if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
- band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
- band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- }
- band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
- band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
- band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
- band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
- memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
- band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
-}
-
-static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
-{
- u16 mcs_map;
- int i;
-
- for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
- mcs_map = (mcs_map << 2) | supp;
-
- return cpu_to_le16(mcs_map);
-}
-
-static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
- u32 bw_cap[2], u32 nchain)
-{
- __le16 mcs_map;
-
- /* not allowed in 2.4G band */
- if (band->band == IEEE80211_BAND_2GHZ)
- return;
-
- band->vht_cap.vht_supported = true;
- /* 80MHz is mandatory */
- band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
- if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
- band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
- band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
- }
- /* all support 256-QAM */
- mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
- band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
- band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
-}
-
-static int brcmf_setup_wiphybands(struct wiphy *wiphy)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- u32 nmode = 0;
- u32 vhtmode = 0;
- u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
- u32 rxchain;
- u32 nchain;
- int err;
- s32 i;
- struct ieee80211_supported_band *band;
-
- (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
- err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
- if (err) {
- brcmf_err("nmode error (%d)\n", err);
- } else {
- brcmf_get_bwcap(ifp, bw_cap);
- }
- brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
- nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
- bw_cap[IEEE80211_BAND_5GHZ]);
-
- err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
- if (err) {
- brcmf_err("rxchain error (%d)\n", err);
- nchain = 1;
- } else {
- for (nchain = 0; rxchain; nchain++)
- rxchain = rxchain & (rxchain - 1);
- }
- brcmf_dbg(INFO, "nchain=%d\n", nchain);
-
- err = brcmf_construct_chaninfo(cfg, bw_cap);
- if (err) {
- brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
- return err;
- }
-
- wiphy = cfg_to_wiphy(cfg);
- for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
- band = wiphy->bands[i];
- if (band == NULL)
- continue;
-
- if (nmode)
- brcmf_update_ht_cap(band, bw_cap, nchain);
- if (vhtmode)
- brcmf_update_vht_cap(band, bw_cap, nchain);
- }
-
- return 0;
-}
-
-static const struct ieee80211_txrx_stypes
-brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
- [NL80211_IFTYPE_STATION] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
- },
- [NL80211_IFTYPE_P2P_CLIENT] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
- },
- [NL80211_IFTYPE_P2P_GO] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
- BIT(IEEE80211_STYPE_DISASSOC >> 4) |
- BIT(IEEE80211_STYPE_AUTH >> 4) |
- BIT(IEEE80211_STYPE_DEAUTH >> 4) |
- BIT(IEEE80211_STYPE_ACTION >> 4)
- },
- [NL80211_IFTYPE_P2P_DEVICE] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
- }
-};
-
-/**
- * brcmf_setup_ifmodes() - determine interface modes and combinations.
- *
- * @wiphy: wiphy object.
- * @ifp: interface object needed for feat module api.
- *
- * The interface modes and combinations are determined dynamically here
- * based on firmware functionality.
- *
- * no p2p and no mbss:
- *
- * #STA <= 1, #AP <= 1, channels = 1, 2 total
- *
- * no p2p and mbss:
- *
- * #STA <= 1, #AP <= 1, channels = 1, 2 total
- * #AP <= 4, matching BI, channels = 1, 4 total
- *
- * p2p, no mchan, and mbss:
- *
- * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
- * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
- * #AP <= 4, matching BI, channels = 1, 4 total
- *
- * p2p, mchan, and mbss:
- *
- * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
- * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
- * #AP <= 4, matching BI, channels = 1, 4 total
- */
-static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
-{
- struct ieee80211_iface_combination *combo = NULL;
- struct ieee80211_iface_limit *c0_limits = NULL;
- struct ieee80211_iface_limit *p2p_limits = NULL;
- struct ieee80211_iface_limit *mbss_limits = NULL;
- bool mbss, p2p;
- int i, c, n_combos;
-
- mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
- p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
-
- n_combos = 1 + !!p2p + !!mbss;
- combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
- if (!combo)
- goto err;
-
- c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
- if (!c0_limits)
- goto err;
-
- if (p2p) {
- p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
- if (!p2p_limits)
- goto err;
- }
-
- if (mbss) {
- mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
- if (!mbss_limits)
- goto err;
- }
-
- wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_AP);
-
- c = 0;
- i = 0;
- combo[c].num_different_channels = 1;
- c0_limits[i].max = 1;
- c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
- if (p2p) {
- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
- combo[c].num_different_channels = 2;
- wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_DEVICE);
- c0_limits[i].max = 1;
- c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
- c0_limits[i].max = 1;
- c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO);
- } else {
- c0_limits[i].max = 1;
- c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
- }
- combo[c].max_interfaces = i;
- combo[c].n_limits = i;
- combo[c].limits = c0_limits;
-
- if (p2p) {
- c++;
- i = 0;
- combo[c].num_different_channels = 1;
- p2p_limits[i].max = 1;
- p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
- p2p_limits[i].max = 1;
- p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
- p2p_limits[i].max = 1;
- p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
- p2p_limits[i].max = 1;
- p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
- combo[c].max_interfaces = i;
- combo[c].n_limits = i;
- combo[c].limits = p2p_limits;
- }
-
- if (mbss) {
- c++;
- combo[c].beacon_int_infra_match = true;
- combo[c].num_different_channels = 1;
- mbss_limits[0].max = 4;
- mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
- combo[c].max_interfaces = 4;
- combo[c].n_limits = 1;
- combo[c].limits = mbss_limits;
- }
- wiphy->n_iface_combinations = n_combos;
- wiphy->iface_combinations = combo;
- return 0;
-
-err:
- kfree(c0_limits);
- kfree(p2p_limits);
- kfree(mbss_limits);
- kfree(combo);
- return -ENOMEM;
-}
-
-static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
-{
- /* scheduled scan settings */
- wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
- wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
- wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
- wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-}
-
-#ifdef CONFIG_PM
-static const struct wiphy_wowlan_support brcmf_wowlan_support = {
- .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
- .n_patterns = BRCMF_WOWL_MAXPATTERNS,
- .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
- .pattern_min_len = 1,
- .max_pkt_offset = 1500,
-};
-#endif
-
-static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
-{
-#ifdef CONFIG_PM
- /* wowl settings */
- wiphy->wowlan = &brcmf_wowlan_support;
-#endif
-}
-
-static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- const struct ieee80211_iface_combination *combo;
- struct ieee80211_supported_band *band;
- u16 max_interfaces = 0;
- __le32 bandlist[3];
- u32 n_bands;
- int err, i;
-
- wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
- wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
- wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
-
- err = brcmf_setup_ifmodes(wiphy, ifp);
- if (err)
- return err;
-
- for (i = 0, combo = wiphy->iface_combinations;
- i < wiphy->n_iface_combinations; i++, combo++) {
- max_interfaces = max(max_interfaces, combo->max_interfaces);
- }
-
- for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
- i++) {
- u8 *addr = drvr->addresses[i].addr;
-
- memcpy(addr, drvr->mac, ETH_ALEN);
- if (i) {
- addr[0] |= BIT(1);
- addr[ETH_ALEN - 1] ^= i;
- }
- }
- wiphy->addresses = drvr->addresses;
- wiphy->n_addresses = i;
-
- wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- wiphy->cipher_suites = __wl_cipher_suites;
- wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
- wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
- WIPHY_FLAG_OFFCHAN_TX |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
- WIPHY_FLAG_SUPPORTS_TDLS;
- if (!brcmf_roamoff)
- wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
- wiphy->mgmt_stypes = brcmf_txrx_stypes;
- wiphy->max_remain_on_channel_duration = 5000;
- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
- brcmf_wiphy_pno_params(wiphy);
-
- /* vendor commands/events support */
- wiphy->vendor_commands = brcmf_vendor_cmds;
- wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
-
- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
- brcmf_wiphy_wowl_params(wiphy);
-
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
- sizeof(bandlist));
- if (err) {
- brcmf_err("could not obtain band info: err=%d\n", err);
- return err;
- }
- /* first entry in bandlist is number of bands */
- n_bands = le32_to_cpu(bandlist[0]);
- for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
- if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
- band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
- GFP_KERNEL);
- if (!band)
- return -ENOMEM;
-
- band->channels = kmemdup(&__wl_2ghz_channels,
- sizeof(__wl_2ghz_channels),
- GFP_KERNEL);
- if (!band->channels) {
- kfree(band);
- return -ENOMEM;
- }
-
- band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
- wiphy->bands[IEEE80211_BAND_2GHZ] = band;
- }
- if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
- band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
- GFP_KERNEL);
- if (!band)
- return -ENOMEM;
-
- band->channels = kmemdup(&__wl_5ghz_channels,
- sizeof(__wl_5ghz_channels),
- GFP_KERNEL);
- if (!band->channels) {
- kfree(band);
- return -ENOMEM;
- }
-
- band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
- wiphy->bands[IEEE80211_BAND_5GHZ] = band;
- }
- }
- err = brcmf_setup_wiphybands(wiphy);
- return err;
-}
-
-static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
-{
- struct net_device *ndev;
- struct wireless_dev *wdev;
- struct brcmf_if *ifp;
- s32 power_mode;
- s32 err = 0;
-
- if (cfg->dongle_up)
- return err;
-
- ndev = cfg_to_ndev(cfg);
- wdev = ndev->ieee80211_ptr;
- ifp = netdev_priv(ndev);
-
- /* make sure RF is ready for work */
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
-
- brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
- WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
-
- power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
- if (err)
- goto default_conf_out;
- brcmf_dbg(INFO, "power save set to %s\n",
- (power_mode ? "enabled" : "disabled"));
-
- err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
- if (err)
- goto default_conf_out;
- err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
- NULL, NULL);
- if (err)
- goto default_conf_out;
-
- brcmf_configure_arp_offload(ifp, true);
-
- cfg->dongle_up = true;
-default_conf_out:
-
- return err;
-
-}
-
-static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
-{
- set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
-
- return brcmf_config_dongle(ifp->drvr->config);
-}
-
-static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
-
- /*
- * While going down, if associated with AP disassociate
- * from AP to save power
- */
- if (check_vif_up(ifp->vif)) {
- brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
-
- /* Make sure WPA_Supplicant receives all the event
- generated due to DISASSOC call to the fw to keep
- the state fw and WPA_Supplicant state consistent
- */
- brcmf_delay(500);
- }
-
- brcmf_abort_scanning(cfg);
- clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
-
- return 0;
-}
-
-s32 brcmf_cfg80211_up(struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- s32 err = 0;
-
- mutex_lock(&cfg->usr_sync);
- err = __brcmf_cfg80211_up(ifp);
- mutex_unlock(&cfg->usr_sync);
-
- return err;
-}
-
-s32 brcmf_cfg80211_down(struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- s32 err = 0;
-
- mutex_lock(&cfg->usr_sync);
- err = __brcmf_cfg80211_down(ifp);
- mutex_unlock(&cfg->usr_sync);
-
- return err;
-}
-
-enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
-{
- struct wireless_dev *wdev = &ifp->vif->wdev;
-
- return wdev->iftype;
-}
-
-bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
- unsigned long state)
-{
- struct brcmf_cfg80211_vif *vif;
-
- list_for_each_entry(vif, &cfg->vif_list, list) {
- if (test_bit(state, &vif->sme_state))
- return true;
- }
- return false;
-}
-
-static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
- u8 action)
-{
- u8 evt_action;
-
- mutex_lock(&event->vif_event_lock);
- evt_action = event->action;
- mutex_unlock(&event->vif_event_lock);
- return evt_action == action;
-}
-
-void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
- struct brcmf_cfg80211_vif *vif)
-{
- struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
-
- mutex_lock(&event->vif_event_lock);
- event->vif = vif;
- event->action = 0;
- mutex_unlock(&event->vif_event_lock);
-}
-
-bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
-{
- struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
- bool armed;
-
- mutex_lock(&event->vif_event_lock);
- armed = event->vif != NULL;
- mutex_unlock(&event->vif_event_lock);
-
- return armed;
-}
-int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
- u8 action, ulong timeout)
-{
- struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
-
- return wait_event_timeout(event->vif_wq,
- vif_event_equals(event, action), timeout);
-}
-
-static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
- struct regulatory_request *req)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- struct brcmf_fil_country_le ccreq;
- int i;
-
- brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
- req->alpha2[0], req->alpha2[1]);
-
- /* ignore non-ISO3166 country codes */
- for (i = 0; i < sizeof(req->alpha2); i++)
- if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
- brcmf_err("not a ISO3166 code\n");
- return;
- }
- memset(&ccreq, 0, sizeof(ccreq));
- ccreq.rev = cpu_to_le32(-1);
- memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
- if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
- brcmf_err("firmware rejected country setting\n");
- return;
- }
- brcmf_setup_wiphybands(wiphy);
-}
-
-static void brcmf_free_wiphy(struct wiphy *wiphy)
-{
- int i;
-
- if (!wiphy)
- return;
-
- if (wiphy->iface_combinations) {
- for (i = 0; i < wiphy->n_iface_combinations; i++)
- kfree(wiphy->iface_combinations[i].limits);
- }
- kfree(wiphy->iface_combinations);
- if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
- kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
- kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
- }
- if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
- kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
- kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
- }
- wiphy_free(wiphy);
-}
-
-struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
- struct device *busdev,
- bool p2pdev_forced)
-{
- struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
- struct brcmf_cfg80211_info *cfg;
- struct wiphy *wiphy;
- struct brcmf_cfg80211_vif *vif;
- struct brcmf_if *ifp;
- s32 err = 0;
- s32 io_type;
- u16 *cap = NULL;
-
- if (!ndev) {
- brcmf_err("ndev is invalid\n");
- return NULL;
- }
-
- ifp = netdev_priv(ndev);
- wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
- if (!wiphy) {
- brcmf_err("Could not allocate wiphy device\n");
- return NULL;
- }
- memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
- set_wiphy_dev(wiphy, busdev);
-
- cfg = wiphy_priv(wiphy);
- cfg->wiphy = wiphy;
- cfg->pub = drvr;
- init_vif_event(&cfg->vif_event);
- INIT_LIST_HEAD(&cfg->vif_list);
-
- vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
- if (IS_ERR(vif))
- goto wiphy_out;
-
- vif->ifp = ifp;
- vif->wdev.netdev = ndev;
- ndev->ieee80211_ptr = &vif->wdev;
- SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
-
- err = wl_init_priv(cfg);
- if (err) {
- brcmf_err("Failed to init iwm_priv (%d)\n", err);
- brcmf_free_vif(vif);
- goto wiphy_out;
- }
- ifp->vif = vif;
-
- /* determine d11 io type before wiphy setup */
- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
- if (err) {
- brcmf_err("Failed to get D11 version (%d)\n", err);
- goto priv_out;
- }
- cfg->d11inf.io_type = (u8)io_type;
- brcmu_d11_attach(&cfg->d11inf);
-
- err = brcmf_setup_wiphy(wiphy, ifp);
- if (err < 0)
- goto priv_out;
-
- brcmf_dbg(INFO, "Registering custom regulatory\n");
- wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
- wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
- wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
-
- /* firmware defaults to 40MHz disabled in 2G band. We signal
- * cfg80211 here that we do and have it decide we can enable
- * it. But first check if device does support 2G operation.
- */
- if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
- cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
- *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- }
- err = wiphy_register(wiphy);
- if (err < 0) {
- brcmf_err("Could not register wiphy device (%d)\n", err);
- goto priv_out;
- }
-
- /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
- * setup 40MHz in 2GHz band and enable OBSS scanning.
- */
- if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
- err = brcmf_enable_bw40_2g(cfg);
- if (!err)
- err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
- BRCMF_OBSS_COEX_AUTO);
- else
- *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- }
- /* p2p might require that "if-events" get processed by fweh. So
- * activate the already registered event handlers now and activate
- * the rest when initialization has completed. drvr->config needs to
- * be assigned before activating events.
- */
- drvr->config = cfg;
- err = brcmf_fweh_activate_events(ifp);
- if (err) {
- brcmf_err("FWEH activation failed (%d)\n", err);
- goto wiphy_unreg_out;
- }
-
- err = brcmf_p2p_attach(cfg, p2pdev_forced);
- if (err) {
- brcmf_err("P2P initilisation failed (%d)\n", err);
- goto wiphy_unreg_out;
- }
- err = brcmf_btcoex_attach(cfg);
- if (err) {
- brcmf_err("BT-coex initialisation failed (%d)\n", err);
- brcmf_p2p_detach(&cfg->p2p);
- goto wiphy_unreg_out;
- }
-
- err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
- if (err) {
- brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
- wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
- } else {
- brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
- brcmf_notify_tdls_peer_event);
- }
-
- /* (re-) activate FWEH event handling */
- err = brcmf_fweh_activate_events(ifp);
- if (err) {
- brcmf_err("FWEH activation failed (%d)\n", err);
- goto wiphy_unreg_out;
- }
-
- return cfg;
-
-wiphy_unreg_out:
- wiphy_unregister(cfg->wiphy);
-priv_out:
- wl_deinit_priv(cfg);
- brcmf_free_vif(vif);
- ifp->vif = NULL;
-wiphy_out:
- brcmf_free_wiphy(wiphy);
- return NULL;
-}
-
-void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
-{
- if (!cfg)
- return;
-
- brcmf_btcoex_detach(cfg);
- wiphy_unregister(cfg->wiphy);
- wl_deinit_priv(cfg);
- brcmf_free_wiphy(cfg->wiphy);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
deleted file mode 100644
index 6a878c8f883f..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef BRCMFMAC_CFG80211_H
-#define BRCMFMAC_CFG80211_H
-
-/* for brcmu_d11inf */
-#include <brcmu_d11.h>
-
-#define WL_NUM_SCAN_MAX 10
-#define WL_NUM_PMKIDS_MAX MAXPMKID
-#define WL_TLV_INFO_MAX 1024
-#define WL_BSS_INFO_MAX 2048
-#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */
-#define WL_EXTRA_BUF_MAX 2048
-#define WL_ROAM_TRIGGER_LEVEL -75
-#define WL_ROAM_DELTA 20
-#define WL_BEACON_TIMEOUT 3
-
-#define WL_SCAN_CHANNEL_TIME 40
-#define WL_SCAN_UNASSOC_TIME 40
-#define WL_SCAN_PASSIVE_TIME 120
-
-#define WL_ESCAN_BUF_SIZE (1024 * 64)
-#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */
-
-#define WL_ESCAN_ACTION_START 1
-#define WL_ESCAN_ACTION_CONTINUE 2
-#define WL_ESCAN_ACTION_ABORT 3
-
-#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */
-#define IE_MAX_LEN 512
-
-/* IE TLV processing */
-#define TLV_LEN_OFF 1 /* length offset */
-#define TLV_HDR_LEN 2 /* header length */
-#define TLV_BODY_OFF 2 /* body offset */
-#define TLV_OUI_LEN 3 /* oui id length */
-
-/* 802.11 Mgmt Packet flags */
-#define BRCMF_VNDR_IE_BEACON_FLAG 0x1
-#define BRCMF_VNDR_IE_PRBRSP_FLAG 0x2
-#define BRCMF_VNDR_IE_ASSOCRSP_FLAG 0x4
-#define BRCMF_VNDR_IE_AUTHRSP_FLAG 0x8
-#define BRCMF_VNDR_IE_PRBREQ_FLAG 0x10
-#define BRCMF_VNDR_IE_ASSOCREQ_FLAG 0x20
-/* vendor IE in IW advertisement protocol ID field */
-#define BRCMF_VNDR_IE_IWAPID_FLAG 0x40
-/* allow custom IE id */
-#define BRCMF_VNDR_IE_CUSTOM_FLAG 0x100
-
-/* P2P Action Frames flags (spec ordered) */
-#define BRCMF_VNDR_IE_GONREQ_FLAG 0x001000
-#define BRCMF_VNDR_IE_GONRSP_FLAG 0x002000
-#define BRCMF_VNDR_IE_GONCFM_FLAG 0x004000
-#define BRCMF_VNDR_IE_INVREQ_FLAG 0x008000
-#define BRCMF_VNDR_IE_INVRSP_FLAG 0x010000
-#define BRCMF_VNDR_IE_DISREQ_FLAG 0x020000
-#define BRCMF_VNDR_IE_DISRSP_FLAG 0x040000
-#define BRCMF_VNDR_IE_PRDREQ_FLAG 0x080000
-#define BRCMF_VNDR_IE_PRDRSP_FLAG 0x100000
-
-#define BRCMF_VNDR_IE_P2PAF_SHIFT 12
-
-#define BRCMF_MAX_DEFAULT_KEYS 4
-
-
-/**
- * enum brcmf_scan_status - scan engine status
- *
- * @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle.
- * @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle.
- * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver.
- */
-enum brcmf_scan_status {
- BRCMF_SCAN_STATUS_BUSY,
- BRCMF_SCAN_STATUS_ABORT,
- BRCMF_SCAN_STATUS_SUPPRESS,
-};
-
-/* dongle configuration */
-struct brcmf_cfg80211_conf {
- u32 frag_threshold;
- u32 rts_threshold;
- u32 retry_short;
- u32 retry_long;
- s32 tx_power;
- struct ieee80211_channel channel;
-};
-
-/* basic structure of scan request */
-struct brcmf_cfg80211_scan_req {
- struct brcmf_ssid_le ssid_le;
-};
-
-/* basic structure of information element */
-struct brcmf_cfg80211_ie {
- u16 offset;
- u8 buf[WL_TLV_INFO_MAX];
-};
-
-/* security information with currently associated ap */
-struct brcmf_cfg80211_security {
- u32 wpa_versions;
- u32 auth_type;
- u32 cipher_pairwise;
- u32 cipher_group;
- u32 wpa_auth;
-};
-
-/**
- * struct brcmf_cfg80211_profile - profile information.
- *
- * @ssid: ssid of associated/associating ap.
- * @bssid: bssid of joined/joining ibss.
- * @sec: security information.
- * @key: key information
- */
-struct brcmf_cfg80211_profile {
- struct brcmf_ssid ssid;
- u8 bssid[ETH_ALEN];
- struct brcmf_cfg80211_security sec;
- struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS];
-};
-
-/**
- * enum brcmf_vif_status - bit indices for vif status.
- *
- * @BRCMF_VIF_STATUS_READY: ready for operation.
- * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress.
- * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully.
- * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress.
- * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started.
- */
-enum brcmf_vif_status {
- BRCMF_VIF_STATUS_READY,
- BRCMF_VIF_STATUS_CONNECTING,
- BRCMF_VIF_STATUS_CONNECTED,
- BRCMF_VIF_STATUS_DISCONNECTING,
- BRCMF_VIF_STATUS_AP_CREATED
-};
-
-/**
- * struct vif_saved_ie - holds saved IEs for a virtual interface.
- *
- * @probe_req_ie: IE info for probe request.
- * @probe_res_ie: IE info for probe response.
- * @beacon_ie: IE info for beacon frame.
- * @probe_req_ie_len: IE info length for probe request.
- * @probe_res_ie_len: IE info length for probe response.
- * @beacon_ie_len: IE info length for beacon frame.
- */
-struct vif_saved_ie {
- u8 probe_req_ie[IE_MAX_LEN];
- u8 probe_res_ie[IE_MAX_LEN];
- u8 beacon_ie[IE_MAX_LEN];
- u8 assoc_req_ie[IE_MAX_LEN];
- u32 probe_req_ie_len;
- u32 probe_res_ie_len;
- u32 beacon_ie_len;
- u32 assoc_req_ie_len;
-};
-
-/**
- * struct brcmf_cfg80211_vif - virtual interface specific information.
- *
- * @ifp: lower layer interface pointer
- * @wdev: wireless device.
- * @profile: profile information.
- * @roam_off: roaming state.
- * @sme_state: SME state using enum brcmf_vif_status bits.
- * @pm_block: power-management blocked.
- * @list: linked list.
- * @mgmt_rx_reg: registered rx mgmt frame types.
- * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
- */
-struct brcmf_cfg80211_vif {
- struct brcmf_if *ifp;
- struct wireless_dev wdev;
- struct brcmf_cfg80211_profile profile;
- s32 roam_off;
- unsigned long sme_state;
- bool pm_block;
- struct vif_saved_ie saved_ie;
- struct list_head list;
- u16 mgmt_rx_reg;
- bool mbss;
- int is_11d;
-};
-
-/* association inform */
-struct brcmf_cfg80211_connect_info {
- u8 *req_ie;
- s32 req_ie_len;
- u8 *resp_ie;
- s32 resp_ie_len;
-};
-
-/* assoc ie length */
-struct brcmf_cfg80211_assoc_ielen_le {
- __le32 req_len;
- __le32 resp_len;
-};
-
-/* wpa2 pmk list */
-struct brcmf_cfg80211_pmk_list {
- struct pmkid_list pmkids;
- struct pmkid foo[MAXPMKID - 1];
-};
-
-/* dongle escan state */
-enum wl_escan_state {
- WL_ESCAN_STATE_IDLE,
- WL_ESCAN_STATE_SCANNING
-};
-
-struct escan_info {
- u32 escan_state;
- u8 escan_buf[WL_ESCAN_BUF_SIZE];
- struct wiphy *wiphy;
- struct brcmf_if *ifp;
- s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
- struct cfg80211_scan_request *request, u16 action);
-};
-
-/**
- * struct brcmf_pno_param_le - PNO scan configuration parameters
- *
- * @version: PNO parameters version.
- * @scan_freq: scan frequency.
- * @lost_network_timeout: #sec. to declare discovered network as lost.
- * @flags: Bit field to control features of PFN such as sort criteria auto
- * enable switch and background scan.
- * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
- * criteria.
- * @bestn: number of best networks in each scan.
- * @mscan: number of scans recorded.
- * @repeat: minimum number of scan intervals before scan frequency changes
- * in adaptive scan.
- * @exp: exponent of 2 for maximum scan interval.
- * @slow_freq: slow scan period.
- */
-struct brcmf_pno_param_le {
- __le32 version;
- __le32 scan_freq;
- __le32 lost_network_timeout;
- __le16 flags;
- __le16 rssi_margin;
- u8 bestn;
- u8 mscan;
- u8 repeat;
- u8 exp;
- __le32 slow_freq;
-};
-
-/**
- * struct brcmf_pno_net_param_le - scan parameters per preferred network.
- *
- * @ssid: ssid name and its length.
- * @flags: bit2: hidden.
- * @infra: BSS vs IBSS.
- * @auth: Open vs Closed.
- * @wpa_auth: WPA type.
- * @wsec: wsec value.
- */
-struct brcmf_pno_net_param_le {
- struct brcmf_ssid_le ssid;
- __le32 flags;
- __le32 infra;
- __le32 auth;
- __le32 wpa_auth;
- __le32 wsec;
-};
-
-/**
- * struct brcmf_pno_net_info_le - information per found network.
- *
- * @bssid: BSS network identifier.
- * @channel: channel number only.
- * @SSID_len: length of ssid.
- * @SSID: ssid characters.
- * @RSSI: receive signal strength (in dBm).
- * @timestamp: age in seconds.
- */
-struct brcmf_pno_net_info_le {
- u8 bssid[ETH_ALEN];
- u8 channel;
- u8 SSID_len;
- u8 SSID[32];
- __le16 RSSI;
- __le16 timestamp;
-};
-
-/**
- * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
- *
- * @version: PNO version identifier.
- * @status: indicates completion status of PNO scan.
- * @count: amount of brcmf_pno_net_info_le entries appended.
- */
-struct brcmf_pno_scanresults_le {
- __le32 version;
- __le32 status;
- __le32 count;
-};
-
-/**
- * struct brcmf_cfg80211_vif_event - virtual interface event information.
- *
- * @vif_wq: waitqueue awaiting interface event from firmware.
- * @vif_event_lock: protects other members in this structure.
- * @vif_complete: completion for net attach.
- * @action: either add, change, or delete.
- * @vif: virtual interface object related to the event.
- */
-struct brcmf_cfg80211_vif_event {
- wait_queue_head_t vif_wq;
- struct mutex vif_event_lock;
- u8 action;
- struct brcmf_cfg80211_vif *vif;
-};
-
-/**
- * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
- *
- * @wiphy: wiphy object for cfg80211 interface.
- * @conf: dongle configuration.
- * @p2p: peer-to-peer specific information.
- * @btcoex: Bluetooth coexistence information.
- * @scan_request: cfg80211 scan request object.
- * @usr_sync: mainly for dongle up/down synchronization.
- * @bss_list: bss_list holding scanned ap information.
- * @scan_req_int: internal scan request object.
- * @bss_info: bss information for cfg80211 layer.
- * @ie: information element object for internal purpose.
- * @conn_info: association info.
- * @pmk_list: wpa2 pmk list.
- * @scan_status: scan activity on the dongle.
- * @pub: common driver information.
- * @channel: current channel.
- * @active_scan: current scan mode.
- * @sched_escan: e-scan for scheduled scan support running.
- * @ibss_starter: indicates this sta is ibss starter.
- * @pwr_save: indicate whether dongle to support power save mode.
- * @dongle_up: indicate whether dongle up or not.
- * @roam_on: on/off switch for dongle self-roaming.
- * @scan_tried: indicates if first scan attempted.
- * @dcmd_buf: dcmd buffer.
- * @extra_buf: mainly to grab assoc information.
- * @debugfsdir: debugfs folder for this device.
- * @escan_info: escan information.
- * @escan_timeout: Timer for catch scan timeout.
- * @escan_timeout_work: scan timeout worker.
- * @escan_ioctl_buf: dongle command buffer for escan commands.
- * @vif_list: linked list of vif instances.
- * @vif_cnt: number of vif instances.
- * @vif_event: vif event signalling.
- * @wowl_enabled; set during suspend, is wowl used.
- * @pre_wowl_pmmode: intermediate storage of pm mode during wowl.
- */
-struct brcmf_cfg80211_info {
- struct wiphy *wiphy;
- struct brcmf_cfg80211_conf *conf;
- struct brcmf_p2p_info p2p;
- struct brcmf_btcoex_info *btcoex;
- struct cfg80211_scan_request *scan_request;
- struct mutex usr_sync;
- struct brcmf_cfg80211_scan_req scan_req_int;
- struct wl_cfg80211_bss_info *bss_info;
- struct brcmf_cfg80211_ie ie;
- struct brcmf_cfg80211_connect_info conn_info;
- struct brcmf_cfg80211_pmk_list *pmk_list;
- unsigned long scan_status;
- struct brcmf_pub *pub;
- u32 channel;
- bool active_scan;
- bool sched_escan;
- bool ibss_starter;
- bool pwr_save;
- bool dongle_up;
- bool scan_tried;
- u8 *dcmd_buf;
- u8 *extra_buf;
- struct dentry *debugfsdir;
- struct escan_info escan_info;
- struct timer_list escan_timeout;
- struct work_struct escan_timeout_work;
- u8 *escan_ioctl_buf;
- struct list_head vif_list;
- struct brcmf_cfg80211_vif_event vif_event;
- struct completion vif_disabled;
- struct brcmu_d11inf d11inf;
- bool wowl_enabled;
- u32 pre_wowl_pmmode;
- struct brcmf_assoclist_le assoclist;
-};
-
-/**
- * struct brcmf_tlv - tag_ID/length/value_buffer tuple.
- *
- * @id: tag identifier.
- * @len: number of bytes in value buffer.
- * @data: value buffer.
- */
-struct brcmf_tlv {
- u8 id;
- u8 len;
- u8 data[1];
-};
-
-static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg)
-{
- return cfg->wiphy;
-}
-
-static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w)
-{
- return (struct brcmf_cfg80211_info *)(wiphy_priv(w));
-}
-
-static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd)
-{
- return (struct brcmf_cfg80211_info *)(wdev_priv(wd));
-}
-
-static inline
-struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg)
-{
- struct brcmf_cfg80211_vif *vif;
- vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list);
- return vif->wdev.netdev;
-}
-
-static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev)
-{
- return wdev_to_cfg(ndev->ieee80211_ptr);
-}
-
-static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd)
-{
- struct brcmf_if *ifp = netdev_priv(nd);
- return &ifp->vif->profile;
-}
-
-static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- return ifp->vif;
-}
-
-static inline struct
-brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg)
-{
- return &cfg->conn_info;
-}
-
-struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
- struct device *busdev,
- bool p2pdev_forced);
-void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg);
-s32 brcmf_cfg80211_up(struct net_device *ndev);
-s32 brcmf_cfg80211_down(struct net_device *ndev);
-enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp);
-
-struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
- enum nl80211_iftype type,
- bool pm_block);
-void brcmf_free_vif(struct brcmf_cfg80211_vif *vif);
-
-s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
- const u8 *vndr_ie_buf, u32 vndr_ie_len);
-s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif);
-const struct brcmf_tlv *
-brcmf_parse_tlvs(const void *buf, int buflen, uint key);
-u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
- struct ieee80211_channel *ch);
-bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
- unsigned long state);
-void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
- struct brcmf_cfg80211_vif *vif);
-bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
-int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
- u8 action, ulong timeout);
-s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
- struct brcmf_if *ifp, bool aborted,
- bool fw_abort);
-void brcmf_set_mpc(struct brcmf_if *ndev, int mpc);
-void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
-void brcmf_cfg80211_free_netdev(struct net_device *ndev);
-
-#endif /* BRCMFMAC_CFG80211_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
deleted file mode 100644
index f04833db2fd0..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+++ /dev/null
@@ -1,1331 +0,0 @@
-/*
- * Copyright (c) 2014 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/list.h>
-#include <linux/ssb/ssb_regs.h>
-#include <linux/bcma/bcma.h>
-#include <linux/bcma/bcma_regs.h>
-
-#include <defs.h>
-#include <soc.h>
-#include <brcm_hw_ids.h>
-#include <brcmu_utils.h>
-#include <chipcommon.h>
-#include "debug.h"
-#include "chip.h"
-
-/* SOC Interconnect types (aka chip types) */
-#define SOCI_SB 0
-#define SOCI_AI 1
-
-/* PL-368 DMP definitions */
-#define DMP_DESC_TYPE_MSK 0x0000000F
-#define DMP_DESC_EMPTY 0x00000000
-#define DMP_DESC_VALID 0x00000001
-#define DMP_DESC_COMPONENT 0x00000001
-#define DMP_DESC_MASTER_PORT 0x00000003
-#define DMP_DESC_ADDRESS 0x00000005
-#define DMP_DESC_ADDRSIZE_GT32 0x00000008
-#define DMP_DESC_EOT 0x0000000F
-
-#define DMP_COMP_DESIGNER 0xFFF00000
-#define DMP_COMP_DESIGNER_S 20
-#define DMP_COMP_PARTNUM 0x000FFF00
-#define DMP_COMP_PARTNUM_S 8
-#define DMP_COMP_CLASS 0x000000F0
-#define DMP_COMP_CLASS_S 4
-#define DMP_COMP_REVISION 0xFF000000
-#define DMP_COMP_REVISION_S 24
-#define DMP_COMP_NUM_SWRAP 0x00F80000
-#define DMP_COMP_NUM_SWRAP_S 19
-#define DMP_COMP_NUM_MWRAP 0x0007C000
-#define DMP_COMP_NUM_MWRAP_S 14
-#define DMP_COMP_NUM_SPORT 0x00003E00
-#define DMP_COMP_NUM_SPORT_S 9
-#define DMP_COMP_NUM_MPORT 0x000001F0
-#define DMP_COMP_NUM_MPORT_S 4
-
-#define DMP_MASTER_PORT_UID 0x0000FF00
-#define DMP_MASTER_PORT_UID_S 8
-#define DMP_MASTER_PORT_NUM 0x000000F0
-#define DMP_MASTER_PORT_NUM_S 4
-
-#define DMP_SLAVE_ADDR_BASE 0xFFFFF000
-#define DMP_SLAVE_ADDR_BASE_S 12
-#define DMP_SLAVE_PORT_NUM 0x00000F00
-#define DMP_SLAVE_PORT_NUM_S 8
-#define DMP_SLAVE_TYPE 0x000000C0
-#define DMP_SLAVE_TYPE_S 6
-#define DMP_SLAVE_TYPE_SLAVE 0
-#define DMP_SLAVE_TYPE_BRIDGE 1
-#define DMP_SLAVE_TYPE_SWRAP 2
-#define DMP_SLAVE_TYPE_MWRAP 3
-#define DMP_SLAVE_SIZE_TYPE 0x00000030
-#define DMP_SLAVE_SIZE_TYPE_S 4
-#define DMP_SLAVE_SIZE_4K 0
-#define DMP_SLAVE_SIZE_8K 1
-#define DMP_SLAVE_SIZE_16K 2
-#define DMP_SLAVE_SIZE_DESC 3
-
-/* EROM CompIdentB */
-#define CIB_REV_MASK 0xff000000
-#define CIB_REV_SHIFT 24
-
-/* ARM CR4 core specific control flag bits */
-#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
-
-/* D11 core specific control flag bits */
-#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004
-#define D11_BCMA_IOCTL_PHYRESET 0x0008
-
-/* chip core base & ramsize */
-/* bcm4329 */
-/* SDIO device core, ID 0x829 */
-#define BCM4329_CORE_BUS_BASE 0x18011000
-/* internal memory core, ID 0x80e */
-#define BCM4329_CORE_SOCRAM_BASE 0x18003000
-/* ARM Cortex M3 core, ID 0x82a */
-#define BCM4329_CORE_ARM_BASE 0x18002000
-
-/* Max possibly supported memory size (limited by IO mapped memory) */
-#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024)
-
-#define CORE_SB(base, field) \
- (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
-#define SBCOREREV(sbidh) \
- ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
- ((sbidh) & SSB_IDHIGH_RCLO))
-
-struct sbconfig {
- u32 PAD[2];
- u32 sbipsflag; /* initiator port ocp slave flag */
- u32 PAD[3];
- u32 sbtpsflag; /* target port ocp slave flag */
- u32 PAD[11];
- u32 sbtmerrloga; /* (sonics >= 2.3) */
- u32 PAD;
- u32 sbtmerrlog; /* (sonics >= 2.3) */
- u32 PAD[3];
- u32 sbadmatch3; /* address match3 */
- u32 PAD;
- u32 sbadmatch2; /* address match2 */
- u32 PAD;
- u32 sbadmatch1; /* address match1 */
- u32 PAD[7];
- u32 sbimstate; /* initiator agent state */
- u32 sbintvec; /* interrupt mask */
- u32 sbtmstatelow; /* target state */
- u32 sbtmstatehigh; /* target state */
- u32 sbbwa0; /* bandwidth allocation table0 */
- u32 PAD;
- u32 sbimconfiglow; /* initiator configuration */
- u32 sbimconfighigh; /* initiator configuration */
- u32 sbadmatch0; /* address match0 */
- u32 PAD;
- u32 sbtmconfiglow; /* target configuration */
- u32 sbtmconfighigh; /* target configuration */
- u32 sbbconfig; /* broadcast configuration */
- u32 PAD;
- u32 sbbstate; /* broadcast state */
- u32 PAD[3];
- u32 sbactcnfg; /* activate configuration */
- u32 PAD[3];
- u32 sbflagst; /* current sbflags */
- u32 PAD[3];
- u32 sbidlow; /* identification */
- u32 sbidhigh; /* identification */
-};
-
-/* bankidx and bankinfo reg defines corerev >= 8 */
-#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000
-#define SOCRAM_BANKINFO_SZMASK 0x0000007f
-#define SOCRAM_BANKIDX_ROM_MASK 0x00000100
-
-#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8
-/* socram bankinfo memtype */
-#define SOCRAM_MEMTYPE_RAM 0
-#define SOCRAM_MEMTYPE_R0M 1
-#define SOCRAM_MEMTYPE_DEVRAM 2
-
-#define SOCRAM_BANKINFO_SZBASE 8192
-#define SRCI_LSS_MASK 0x00f00000
-#define SRCI_LSS_SHIFT 20
-#define SRCI_SRNB_MASK 0xf0
-#define SRCI_SRNB_SHIFT 4
-#define SRCI_SRBSZ_MASK 0xf
-#define SRCI_SRBSZ_SHIFT 0
-#define SR_BSZ_BASE 14
-
-struct sbsocramregs {
- u32 coreinfo;
- u32 bwalloc;
- u32 extracoreinfo;
- u32 biststat;
- u32 bankidx;
- u32 standbyctrl;
-
- u32 errlogstatus; /* rev 6 */
- u32 errlogaddr; /* rev 6 */
- /* used for patching rev 3 & 5 */
- u32 cambankidx;
- u32 cambankstandbyctrl;
- u32 cambankpatchctrl;
- u32 cambankpatchtblbaseaddr;
- u32 cambankcmdreg;
- u32 cambankdatareg;
- u32 cambankmaskreg;
- u32 PAD[1];
- u32 bankinfo; /* corev 8 */
- u32 bankpda;
- u32 PAD[14];
- u32 extmemconfig;
- u32 extmemparitycsr;
- u32 extmemparityerrdata;
- u32 extmemparityerrcnt;
- u32 extmemwrctrlandsize;
- u32 PAD[84];
- u32 workaround;
- u32 pwrctl; /* corerev >= 2 */
- u32 PAD[133];
- u32 sr_control; /* corerev >= 15 */
- u32 sr_status; /* corerev >= 15 */
- u32 sr_address; /* corerev >= 15 */
- u32 sr_data; /* corerev >= 15 */
-};
-
-#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f)
-#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f)
-
-#define ARMCR4_CAP (0x04)
-#define ARMCR4_BANKIDX (0x40)
-#define ARMCR4_BANKINFO (0x44)
-#define ARMCR4_BANKPDA (0x4C)
-
-#define ARMCR4_TCBBNB_MASK 0xf0
-#define ARMCR4_TCBBNB_SHIFT 4
-#define ARMCR4_TCBANB_MASK 0xf
-#define ARMCR4_TCBANB_SHIFT 0
-
-#define ARMCR4_BSZ_MASK 0x3f
-#define ARMCR4_BSZ_MULT 8192
-
-struct brcmf_core_priv {
- struct brcmf_core pub;
- u32 wrapbase;
- struct list_head list;
- struct brcmf_chip_priv *chip;
-};
-
-struct brcmf_chip_priv {
- struct brcmf_chip pub;
- const struct brcmf_buscore_ops *ops;
- void *ctx;
- /* assured first core is chipcommon, second core is buscore */
- struct list_head cores;
- u16 num_cores;
-
- bool (*iscoreup)(struct brcmf_core_priv *core);
- void (*coredisable)(struct brcmf_core_priv *core, u32 prereset,
- u32 reset);
- void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset,
- u32 postreset);
-};
-
-static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci,
- struct brcmf_core *core)
-{
- u32 regdata;
-
- regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh));
- core->rev = SBCOREREV(regdata);
-}
-
-static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core)
-{
- struct brcmf_chip_priv *ci;
- u32 regdata;
- u32 address;
-
- ci = core->chip;
- address = CORE_SB(core->pub.base, sbtmstatelow);
- regdata = ci->ops->read32(ci->ctx, address);
- regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
- SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
- return SSB_TMSLOW_CLOCK == regdata;
-}
-
-static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core)
-{
- struct brcmf_chip_priv *ci;
- u32 regdata;
- bool ret;
-
- ci = core->chip;
- regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
- ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
-
- regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
- ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
-
- return ret;
-}
-
-static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core,
- u32 prereset, u32 reset)
-{
- struct brcmf_chip_priv *ci;
- u32 val, base;
-
- ci = core->chip;
- base = core->pub.base;
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- if (val & SSB_TMSLOW_RESET)
- return;
-
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- if ((val & SSB_TMSLOW_CLOCK) != 0) {
- /*
- * set target reject and spin until busy is clear
- * (preserve core-specific bits)
- */
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
- val | SSB_TMSLOW_REJECT);
-
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- udelay(1);
- SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh))
- & SSB_TMSHIGH_BUSY), 100000);
-
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
- if (val & SSB_TMSHIGH_BUSY)
- brcmf_err("core state still busy\n");
-
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
- if (val & SSB_IDLOW_INITIATOR) {
- val = ci->ops->read32(ci->ctx,
- CORE_SB(base, sbimstate));
- val |= SSB_IMSTATE_REJECT;
- ci->ops->write32(ci->ctx,
- CORE_SB(base, sbimstate), val);
- val = ci->ops->read32(ci->ctx,
- CORE_SB(base, sbimstate));
- udelay(1);
- SPINWAIT((ci->ops->read32(ci->ctx,
- CORE_SB(base, sbimstate)) &
- SSB_IMSTATE_BUSY), 100000);
- }
-
- /* set reset and reject while enabling the clocks */
- val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
- SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
- ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val);
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- udelay(10);
-
- /* clear the initiator reject bit */
- val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
- if (val & SSB_IDLOW_INITIATOR) {
- val = ci->ops->read32(ci->ctx,
- CORE_SB(base, sbimstate));
- val &= ~SSB_IMSTATE_REJECT;
- ci->ops->write32(ci->ctx,
- CORE_SB(base, sbimstate), val);
- }
- }
-
- /* leave reset and reject asserted */
- ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
- (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET));
- udelay(1);
-}
-
-static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core,
- u32 prereset, u32 reset)
-{
- struct brcmf_chip_priv *ci;
- u32 regdata;
-
- ci = core->chip;
-
- /* if core is already in reset, skip reset */
- regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
- if ((regdata & BCMA_RESET_CTL_RESET) != 0)
- goto in_reset_configure;
-
- /* configure reset */
- ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
- prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
- ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
-
- /* put in reset */
- ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL,
- BCMA_RESET_CTL_RESET);
- usleep_range(10, 20);
-
- /* wait till reset is 1 */
- SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) !=
- BCMA_RESET_CTL_RESET, 300);
-
-in_reset_configure:
- /* in-reset configure */
- ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
- reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
- ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
-}
-
-static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset,
- u32 reset, u32 postreset)
-{
- struct brcmf_chip_priv *ci;
- u32 regdata;
- u32 base;
-
- ci = core->chip;
- base = core->pub.base;
- /*
- * Must do the disable sequence first to work for
- * arbitrary current core state.
- */
- brcmf_chip_sb_coredisable(core, 0, 0);
-
- /*
- * Now do the initialization sequence.
- * set reset while enabling the clock and
- * forcing them on throughout the core
- */
- ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
- SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
- SSB_TMSLOW_RESET);
- regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- udelay(1);
-
- /* clear any serror */
- regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
- if (regdata & SSB_TMSHIGH_SERR)
- ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0);
-
- regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate));
- if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) {
- regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO);
- ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata);
- }
-
- /* clear reset and allow it to propagate throughout the core */
- ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
- SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK);
- regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- udelay(1);
-
- /* leave clock enabled */
- ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
- SSB_TMSLOW_CLOCK);
- regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
- udelay(1);
-}
-
-static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
- u32 reset, u32 postreset)
-{
- struct brcmf_chip_priv *ci;
- int count;
-
- ci = core->chip;
-
- /* must disable first to work for arbitrary current core state */
- brcmf_chip_ai_coredisable(core, prereset, reset);
-
- count = 0;
- while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
- BCMA_RESET_CTL_RESET) {
- ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0);
- count++;
- if (count > 50)
- break;
- usleep_range(40, 60);
- }
-
- ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
- postreset | BCMA_IOCTL_CLK);
- ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
-}
-
-static char *brcmf_chip_name(uint chipid, char *buf, uint len)
-{
- const char *fmt;
-
- fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
- snprintf(buf, len, fmt, chipid);
- return buf;
-}
-
-static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci,
- u16 coreid, u32 base,
- u32 wrapbase)
-{
- struct brcmf_core_priv *core;
-
- core = kzalloc(sizeof(*core), GFP_KERNEL);
- if (!core)
- return ERR_PTR(-ENOMEM);
-
- core->pub.id = coreid;
- core->pub.base = base;
- core->chip = ci;
- core->wrapbase = wrapbase;
-
- list_add_tail(&core->list, &ci->cores);
- return &core->pub;
-}
-
-/* safety check for chipinfo */
-static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
-{
- struct brcmf_core_priv *core;
- bool need_socram = false;
- bool has_socram = false;
- bool cpu_found = false;
- int idx = 1;
-
- list_for_each_entry(core, &ci->cores, list) {
- brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
- idx++, core->pub.id, core->pub.rev, core->pub.base,
- core->wrapbase);
-
- switch (core->pub.id) {
- case BCMA_CORE_ARM_CM3:
- cpu_found = true;
- need_socram = true;
- break;
- case BCMA_CORE_INTERNAL_MEM:
- has_socram = true;
- break;
- case BCMA_CORE_ARM_CR4:
- cpu_found = true;
- break;
- case BCMA_CORE_ARM_CA7:
- cpu_found = true;
- break;
- default:
- break;
- }
- }
-
- if (!cpu_found) {
- brcmf_err("CPU core not detected\n");
- return -ENXIO;
- }
- /* check RAM core presence for ARM CM3 core */
- if (need_socram && !has_socram) {
- brcmf_err("RAM core not provided with ARM CM3 core\n");
- return -ENODEV;
- }
- return 0;
-}
-
-static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg)
-{
- return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg);
-}
-
-static void brcmf_chip_core_write32(struct brcmf_core_priv *core,
- u16 reg, u32 val)
-{
- core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val);
-}
-
-static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx,
- u32 *banksize)
-{
- u32 bankinfo;
- u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT);
-
- bankidx |= idx;
- brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx);
- bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo));
- *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1;
- *banksize *= SOCRAM_BANKINFO_SZBASE;
- return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK);
-}
-
-static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
- u32 *srsize)
-{
- u32 coreinfo;
- uint nb, banksize, lss;
- bool retent;
- int i;
-
- *ramsize = 0;
- *srsize = 0;
-
- if (WARN_ON(sr->pub.rev < 4))
- return;
-
- if (!brcmf_chip_iscoreup(&sr->pub))
- brcmf_chip_resetcore(&sr->pub, 0, 0, 0);
-
- /* Get info for determining size */
- coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo));
- nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
-
- if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) {
- banksize = (coreinfo & SRCI_SRBSZ_MASK);
- lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
- if (lss != 0)
- nb--;
- *ramsize = nb * (1 << (banksize + SR_BSZ_BASE));
- if (lss != 0)
- *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE));
- } else {
- nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
- for (i = 0; i < nb; i++) {
- retent = brcmf_chip_socram_banksize(sr, i, &banksize);
- *ramsize += banksize;
- if (retent)
- *srsize += banksize;
- }
- }
-
- /* hardcoded save&restore memory sizes */
- switch (sr->chip->pub.chip) {
- case BRCM_CC_4334_CHIP_ID:
- if (sr->chip->pub.chiprev < 2)
- *srsize = (32 * 1024);
- break;
- case BRCM_CC_43430_CHIP_ID:
- /* assume sr for now as we can not check
- * firmware sr capability at this point.
- */
- *srsize = (64 * 1024);
- break;
- default:
- break;
- }
-}
-
-/** Return the SYS MEM size */
-static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem)
-{
- u32 memsize = 0;
- u32 coreinfo;
- u32 idx;
- u32 nb;
- u32 banksize;
-
- if (!brcmf_chip_iscoreup(&sysmem->pub))
- brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0);
-
- coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo));
- nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
-
- for (idx = 0; idx < nb; idx++) {
- brcmf_chip_socram_banksize(sysmem, idx, &banksize);
- memsize += banksize;
- }
-
- return memsize;
-}
-
-/** Return the TCM-RAM size of the ARMCR4 core. */
-static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4)
-{
- u32 corecap;
- u32 memsize = 0;
- u32 nab;
- u32 nbb;
- u32 totb;
- u32 bxinfo;
- u32 idx;
-
- corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP);
-
- nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT;
- nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT;
- totb = nab + nbb;
-
- for (idx = 0; idx < totb; idx++) {
- brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx);
- bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO);
- memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT;
- }
-
- return memsize;
-}
-
-static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
-{
- switch (ci->pub.chip) {
- case BRCM_CC_4345_CHIP_ID:
- return 0x198000;
- case BRCM_CC_4335_CHIP_ID:
- case BRCM_CC_4339_CHIP_ID:
- case BRCM_CC_4350_CHIP_ID:
- case BRCM_CC_4354_CHIP_ID:
- case BRCM_CC_4356_CHIP_ID:
- case BRCM_CC_43567_CHIP_ID:
- case BRCM_CC_43569_CHIP_ID:
- case BRCM_CC_43570_CHIP_ID:
- case BRCM_CC_4358_CHIP_ID:
- case BRCM_CC_43602_CHIP_ID:
- case BRCM_CC_4371_CHIP_ID:
- return 0x180000;
- case BRCM_CC_4365_CHIP_ID:
- case BRCM_CC_4366_CHIP_ID:
- return 0x200000;
- default:
- brcmf_err("unknown chip: %s\n", ci->pub.name);
- break;
- }
- return 0;
-}
-
-static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
-{
- struct brcmf_core_priv *mem_core;
- struct brcmf_core *mem;
-
- mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4);
- if (mem) {
- mem_core = container_of(mem, struct brcmf_core_priv, pub);
- ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core);
- ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
- if (!ci->pub.rambase) {
- brcmf_err("RAM base not provided with ARM CR4 core\n");
- return -EINVAL;
- }
- } else {
- mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM);
- if (mem) {
- mem_core = container_of(mem, struct brcmf_core_priv,
- pub);
- ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core);
- ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
- if (!ci->pub.rambase) {
- brcmf_err("RAM base not provided with ARM CA7 core\n");
- return -EINVAL;
- }
- } else {
- mem = brcmf_chip_get_core(&ci->pub,
- BCMA_CORE_INTERNAL_MEM);
- if (!mem) {
- brcmf_err("No memory cores found\n");
- return -ENOMEM;
- }
- mem_core = container_of(mem, struct brcmf_core_priv,
- pub);
- brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize,
- &ci->pub.srsize);
- }
- }
- brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n",
- ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize,
- ci->pub.srsize, ci->pub.srsize);
-
- if (!ci->pub.ramsize) {
- brcmf_err("RAM size is undetermined\n");
- return -ENOMEM;
- }
-
- if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) {
- brcmf_err("RAM size is incorrect\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr,
- u8 *type)
-{
- u32 val;
-
- /* read next descriptor */
- val = ci->ops->read32(ci->ctx, *eromaddr);
- *eromaddr += 4;
-
- if (!type)
- return val;
-
- /* determine descriptor type */
- *type = (val & DMP_DESC_TYPE_MSK);
- if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS)
- *type = DMP_DESC_ADDRESS;
-
- return val;
-}
-
-static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
- u32 *regbase, u32 *wrapbase)
-{
- u8 desc;
- u32 val;
- u8 mpnum = 0;
- u8 stype, sztype, wraptype;
-
- *regbase = 0;
- *wrapbase = 0;
-
- val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
- if (desc == DMP_DESC_MASTER_PORT) {
- mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S;
- wraptype = DMP_SLAVE_TYPE_MWRAP;
- } else if (desc == DMP_DESC_ADDRESS) {
- /* revert erom address */
- *eromaddr -= 4;
- wraptype = DMP_SLAVE_TYPE_SWRAP;
- } else {
- *eromaddr -= 4;
- return -EILSEQ;
- }
-
- do {
- /* locate address descriptor */
- do {
- val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
- /* unexpected table end */
- if (desc == DMP_DESC_EOT) {
- *eromaddr -= 4;
- return -EFAULT;
- }
- } while (desc != DMP_DESC_ADDRESS);
-
- /* skip upper 32-bit address descriptor */
- if (val & DMP_DESC_ADDRSIZE_GT32)
- brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
-
- sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S;
-
- /* next size descriptor can be skipped */
- if (sztype == DMP_SLAVE_SIZE_DESC) {
- val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
- /* skip upper size descriptor if present */
- if (val & DMP_DESC_ADDRSIZE_GT32)
- brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
- }
-
- /* only look for 4K register regions */
- if (sztype != DMP_SLAVE_SIZE_4K)
- continue;
-
- stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S;
-
- /* only regular slave and wrapper */
- if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE)
- *regbase = val & DMP_SLAVE_ADDR_BASE;
- if (*wrapbase == 0 && stype == wraptype)
- *wrapbase = val & DMP_SLAVE_ADDR_BASE;
- } while (*regbase == 0 || *wrapbase == 0);
-
- return 0;
-}
-
-static
-int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci)
-{
- struct brcmf_core *core;
- u32 eromaddr;
- u8 desc_type = 0;
- u32 val;
- u16 id;
- u8 nmp, nsp, nmw, nsw, rev;
- u32 base, wrap;
- int err;
-
- eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr));
-
- while (desc_type != DMP_DESC_EOT) {
- val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
- if (!(val & DMP_DESC_VALID))
- continue;
-
- if (desc_type == DMP_DESC_EMPTY)
- continue;
-
- /* need a component descriptor */
- if (desc_type != DMP_DESC_COMPONENT)
- continue;
-
- id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S;
-
- /* next descriptor must be component as well */
- val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
- if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT))
- return -EFAULT;
-
- /* only look at cores with master port(s) */
- nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S;
- nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S;
- nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S;
- nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S;
- rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
-
- /* need core with ports */
- if (nmw + nsw == 0)
- continue;
-
- /* try to obtain register address info */
- err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap);
- if (err)
- continue;
-
- /* finally a core to be added */
- core = brcmf_chip_add_core(ci, id, base, wrap);
- if (IS_ERR(core))
- return PTR_ERR(core);
-
- core->rev = rev;
- }
-
- return 0;
-}
-
-static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
-{
- struct brcmf_core *core;
- u32 regdata;
- u32 socitype;
- int ret;
-
- /* Get CC core rev
- * Chipid is assume to be at offset 0 from SI_ENUM_BASE
- * For different chiptypes or old sdio hosts w/o chipcommon,
- * other ways of recognition should be added here.
- */
- regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid));
- ci->pub.chip = regdata & CID_ID_MASK;
- ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
- socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
-
- brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name));
- brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n",
- socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name,
- ci->pub.chiprev);
-
- if (socitype == SOCI_SB) {
- if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) {
- brcmf_err("SB chip is not supported\n");
- return -ENODEV;
- }
- ci->iscoreup = brcmf_chip_sb_iscoreup;
- ci->coredisable = brcmf_chip_sb_coredisable;
- ci->resetcore = brcmf_chip_sb_resetcore;
-
- core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON,
- SI_ENUM_BASE, 0);
- brcmf_chip_sb_corerev(ci, core);
- core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV,
- BCM4329_CORE_BUS_BASE, 0);
- brcmf_chip_sb_corerev(ci, core);
- core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM,
- BCM4329_CORE_SOCRAM_BASE, 0);
- brcmf_chip_sb_corerev(ci, core);
- core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3,
- BCM4329_CORE_ARM_BASE, 0);
- brcmf_chip_sb_corerev(ci, core);
-
- core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0);
- brcmf_chip_sb_corerev(ci, core);
- } else if (socitype == SOCI_AI) {
- ci->iscoreup = brcmf_chip_ai_iscoreup;
- ci->coredisable = brcmf_chip_ai_coredisable;
- ci->resetcore = brcmf_chip_ai_resetcore;
-
- brcmf_chip_dmp_erom_scan(ci);
- } else {
- brcmf_err("chip backplane type %u is not supported\n",
- socitype);
- return -ENODEV;
- }
-
- ret = brcmf_chip_cores_check(ci);
- if (ret)
- return ret;
-
- /* assure chip is passive for core access */
- brcmf_chip_set_passive(&ci->pub);
-
- /* Call bus specific reset function now. Cores have been determined
- * but further access may require a chip specific reset at this point.
- */
- if (ci->ops->reset) {
- ci->ops->reset(ci->ctx, &ci->pub);
- brcmf_chip_set_passive(&ci->pub);
- }
-
- return brcmf_chip_get_raminfo(ci);
-}
-
-static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
-{
- struct brcmf_core *core;
- struct brcmf_core_priv *cpu;
- u32 val;
-
-
- core = brcmf_chip_get_core(&chip->pub, id);
- if (!core)
- return;
-
- switch (id) {
- case BCMA_CORE_ARM_CM3:
- brcmf_chip_coredisable(core, 0, 0);
- break;
- case BCMA_CORE_ARM_CR4:
- case BCMA_CORE_ARM_CA7:
- cpu = container_of(core, struct brcmf_core_priv, pub);
-
- /* clear all IOCTL bits except HALT bit */
- val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL);
- val &= ARMCR4_BCMA_IOCTL_CPUHALT;
- brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT,
- ARMCR4_BCMA_IOCTL_CPUHALT);
- break;
- default:
- brcmf_err("unknown id: %u\n", id);
- break;
- }
-}
-
-static int brcmf_chip_setup(struct brcmf_chip_priv *chip)
-{
- struct brcmf_chip *pub;
- struct brcmf_core_priv *cc;
- u32 base;
- u32 val;
- int ret = 0;
-
- pub = &chip->pub;
- cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
- base = cc->pub.base;
-
- /* get chipcommon capabilites */
- pub->cc_caps = chip->ops->read32(chip->ctx,
- CORE_CC_REG(base, capabilities));
-
- /* get pmu caps & rev */
- if (pub->cc_caps & CC_CAP_PMU) {
- val = chip->ops->read32(chip->ctx,
- CORE_CC_REG(base, pmucapabilities));
- pub->pmurev = val & PCAP_REV_MASK;
- pub->pmucaps = val;
- }
-
- brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n",
- cc->pub.rev, pub->pmurev, pub->pmucaps);
-
- /* execute bus core specific setup */
- if (chip->ops->setup)
- ret = chip->ops->setup(chip->ctx, pub);
-
- return ret;
-}
-
-struct brcmf_chip *brcmf_chip_attach(void *ctx,
- const struct brcmf_buscore_ops *ops)
-{
- struct brcmf_chip_priv *chip;
- int err = 0;
-
- if (WARN_ON(!ops->read32))
- err = -EINVAL;
- if (WARN_ON(!ops->write32))
- err = -EINVAL;
- if (WARN_ON(!ops->prepare))
- err = -EINVAL;
- if (WARN_ON(!ops->activate))
- err = -EINVAL;
- if (err < 0)
- return ERR_PTR(-EINVAL);
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return ERR_PTR(-ENOMEM);
-
- INIT_LIST_HEAD(&chip->cores);
- chip->num_cores = 0;
- chip->ops = ops;
- chip->ctx = ctx;
-
- err = ops->prepare(ctx);
- if (err < 0)
- goto fail;
-
- err = brcmf_chip_recognition(chip);
- if (err < 0)
- goto fail;
-
- err = brcmf_chip_setup(chip);
- if (err < 0)
- goto fail;
-
- return &chip->pub;
-
-fail:
- brcmf_chip_detach(&chip->pub);
- return ERR_PTR(err);
-}
-
-void brcmf_chip_detach(struct brcmf_chip *pub)
-{
- struct brcmf_chip_priv *chip;
- struct brcmf_core_priv *core;
- struct brcmf_core_priv *tmp;
-
- chip = container_of(pub, struct brcmf_chip_priv, pub);
- list_for_each_entry_safe(core, tmp, &chip->cores, list) {
- list_del(&core->list);
- kfree(core);
- }
- kfree(chip);
-}
-
-struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
-{
- struct brcmf_chip_priv *chip;
- struct brcmf_core_priv *core;
-
- chip = container_of(pub, struct brcmf_chip_priv, pub);
- list_for_each_entry(core, &chip->cores, list)
- if (core->pub.id == coreid)
- return &core->pub;
-
- return NULL;
-}
-
-struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub)
-{
- struct brcmf_chip_priv *chip;
- struct brcmf_core_priv *cc;
-
- chip = container_of(pub, struct brcmf_chip_priv, pub);
- cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
- if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON))
- return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON);
- return &cc->pub;
-}
-
-bool brcmf_chip_iscoreup(struct brcmf_core *pub)
-{
- struct brcmf_core_priv *core;
-
- core = container_of(pub, struct brcmf_core_priv, pub);
- return core->chip->iscoreup(core);
-}
-
-void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset)
-{
- struct brcmf_core_priv *core;
-
- core = container_of(pub, struct brcmf_core_priv, pub);
- core->chip->coredisable(core, prereset, reset);
-}
-
-void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset,
- u32 postreset)
-{
- struct brcmf_core_priv *core;
-
- core = container_of(pub, struct brcmf_core_priv, pub);
- core->chip->resetcore(core, prereset, reset, postreset);
-}
-
-static void
-brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip)
-{
- struct brcmf_core *core;
- struct brcmf_core_priv *sr;
-
- brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
- brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN);
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
- brcmf_chip_resetcore(core, 0, 0, 0);
-
- /* disable bank #3 remap for this device */
- if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) {
- sr = container_of(core, struct brcmf_core_priv, pub);
- brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3);
- brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0);
- }
-}
-
-static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip)
-{
- struct brcmf_core *core;
-
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
- if (!brcmf_chip_iscoreup(core)) {
- brcmf_err("SOCRAM core is down after reset?\n");
- return false;
- }
-
- chip->ops->activate(chip->ctx, &chip->pub, 0);
-
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3);
- brcmf_chip_resetcore(core, 0, 0, 0);
-
- return true;
-}
-
-static inline void
-brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip)
-{
- struct brcmf_core *core;
-
- brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
-
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
- brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN);
-}
-
-static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
-{
- struct brcmf_core *core;
-
- chip->ops->activate(chip->ctx, &chip->pub, rstvec);
-
- /* restore ARM */
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4);
- brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
-
- return true;
-}
-
-static inline void
-brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip)
-{
- struct brcmf_core *core;
-
- brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7);
-
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
- brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN);
-}
-
-static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
-{
- struct brcmf_core *core;
-
- chip->ops->activate(chip->ctx, &chip->pub, rstvec);
-
- /* restore ARM */
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7);
- brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
-
- return true;
-}
-
-void brcmf_chip_set_passive(struct brcmf_chip *pub)
-{
- struct brcmf_chip_priv *chip;
- struct brcmf_core *arm;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- chip = container_of(pub, struct brcmf_chip_priv, pub);
- arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
- if (arm) {
- brcmf_chip_cr4_set_passive(chip);
- return;
- }
- arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
- if (arm) {
- brcmf_chip_ca7_set_passive(chip);
- return;
- }
- arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
- if (arm) {
- brcmf_chip_cm3_set_passive(chip);
- return;
- }
-}
-
-bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
-{
- struct brcmf_chip_priv *chip;
- struct brcmf_core *arm;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- chip = container_of(pub, struct brcmf_chip_priv, pub);
- arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
- if (arm)
- return brcmf_chip_cr4_set_active(chip, rstvec);
- arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
- if (arm)
- return brcmf_chip_ca7_set_active(chip, rstvec);
- arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
- if (arm)
- return brcmf_chip_cm3_set_active(chip);
-
- return false;
-}
-
-bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
-{
- u32 base, addr, reg, pmu_cc3_mask = ~0;
- struct brcmf_chip_priv *chip;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* old chips with PMU version less than 17 don't support save restore */
- if (pub->pmurev < 17)
- return false;
-
- base = brcmf_chip_get_chipcommon(pub)->base;
- chip = container_of(pub, struct brcmf_chip_priv, pub);
-
- switch (pub->chip) {
- case BRCM_CC_4354_CHIP_ID:
- /* explicitly check SR engine enable bit */
- pmu_cc3_mask = BIT(2);
- /* fall-through */
- case BRCM_CC_43241_CHIP_ID:
- case BRCM_CC_4335_CHIP_ID:
- case BRCM_CC_4339_CHIP_ID:
- /* read PMU chipcontrol register 3 */
- addr = CORE_CC_REG(base, chipcontrol_addr);
- chip->ops->write32(chip->ctx, addr, 3);
- addr = CORE_CC_REG(base, chipcontrol_data);
- reg = chip->ops->read32(chip->ctx, addr);
- return (reg & pmu_cc3_mask) != 0;
- case BRCM_CC_43430_CHIP_ID:
- addr = CORE_CC_REG(base, sr_control1);
- reg = chip->ops->read32(chip->ctx, addr);
- return reg != 0;
- default:
- addr = CORE_CC_REG(base, pmucapabilities_ext);
- reg = chip->ops->read32(chip->ctx, addr);
- if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
- return false;
-
- addr = CORE_CC_REG(base, retention_ctl);
- reg = chip->ops->read32(chip->ctx, addr);
- return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
- PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
- }
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c
deleted file mode 100644
index fe54844c75e0..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/common.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/netdevice.h>
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include "core.h"
-#include "bus.h"
-#include "debug.h"
-#include "fwil.h"
-#include "fwil_types.h"
-#include "tracepoint.h"
-#include "common.h"
-
-const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
-#define BRCMF_DEFAULT_BCN_TIMEOUT 3
-#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40
-#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40
-
-/* boost value for RSSI_DELTA in preferred join selection */
-#define BRCMF_JOIN_PREF_RSSI_BOOST 8
-
-int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
-{
- s8 eventmask[BRCMF_EVENTING_MASK_LEN];
- u8 buf[BRCMF_DCMD_SMLEN];
- struct brcmf_join_pref_params join_pref_params[2];
- struct brcmf_rev_info_le revinfo;
- struct brcmf_rev_info *ri;
- char *ptr;
- s32 err;
-
- /* retreive mac address */
- err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
- sizeof(ifp->mac_addr));
- if (err < 0) {
- brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
- goto done;
- }
- memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
-
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,
- &revinfo, sizeof(revinfo));
- ri = &ifp->drvr->revinfo;
- if (err < 0) {
- brcmf_err("retrieving revision info failed, %d\n", err);
- } else {
- ri->vendorid = le32_to_cpu(revinfo.vendorid);
- ri->deviceid = le32_to_cpu(revinfo.deviceid);
- ri->radiorev = le32_to_cpu(revinfo.radiorev);
- ri->chiprev = le32_to_cpu(revinfo.chiprev);
- ri->corerev = le32_to_cpu(revinfo.corerev);
- ri->boardid = le32_to_cpu(revinfo.boardid);
- ri->boardvendor = le32_to_cpu(revinfo.boardvendor);
- ri->boardrev = le32_to_cpu(revinfo.boardrev);
- ri->driverrev = le32_to_cpu(revinfo.driverrev);
- ri->ucoderev = le32_to_cpu(revinfo.ucoderev);
- ri->bus = le32_to_cpu(revinfo.bus);
- ri->chipnum = le32_to_cpu(revinfo.chipnum);
- ri->phytype = le32_to_cpu(revinfo.phytype);
- ri->phyrev = le32_to_cpu(revinfo.phyrev);
- ri->anarev = le32_to_cpu(revinfo.anarev);
- ri->chippkg = le32_to_cpu(revinfo.chippkg);
- ri->nvramrev = le32_to_cpu(revinfo.nvramrev);
- }
- ri->result = err;
-
- /* query for 'ver' to get version info from firmware */
- memset(buf, 0, sizeof(buf));
- strcpy(buf, "ver");
- err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
- if (err < 0) {
- brcmf_err("Retreiving version information failed, %d\n",
- err);
- goto done;
- }
- ptr = (char *)buf;
- strsep(&ptr, "\n");
-
- /* Print fw version info */
- brcmf_err("Firmware version = %s\n", buf);
-
- /* locate firmware version number for ethtool */
- ptr = strrchr(buf, ' ') + 1;
- strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
-
- /* set mpc */
- err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
- if (err) {
- brcmf_err("failed setting mpc\n");
- goto done;
- }
-
- /*
- * Setup timeout if Beacons are lost and roam is off to report
- * link down
- */
- err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout",
- BRCMF_DEFAULT_BCN_TIMEOUT);
- if (err) {
- brcmf_err("bcn_timeout error (%d)\n", err);
- goto done;
- }
-
- /* Enable/Disable build-in roaming to allowed ext supplicant to take
- * of romaing
- */
- err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
- if (err) {
- brcmf_err("roam_off error (%d)\n", err);
- goto done;
- }
-
- /* Setup join_pref to select target by RSSI(with boost on 5GHz) */
- join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
- join_pref_params[0].len = 2;
- join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;
- join_pref_params[0].band = WLC_BAND_5G;
- join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;
- join_pref_params[1].len = 2;
- join_pref_params[1].rssi_gain = 0;
- join_pref_params[1].band = 0;
- err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
- sizeof(join_pref_params));
- if (err)
- brcmf_err("Set join_pref error (%d)\n", err);
-
- /* Setup event_msgs, enable E_IF */
- err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
- BRCMF_EVENTING_MASK_LEN);
- if (err) {
- brcmf_err("Get event_msgs error (%d)\n", err);
- goto done;
- }
- setbit(eventmask, BRCMF_E_IF);
- err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
- BRCMF_EVENTING_MASK_LEN);
- if (err) {
- brcmf_err("Set event_msgs error (%d)\n", err);
- goto done;
- }
-
- /* Setup default scan channel time */
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
- BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
- if (err) {
- brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n",
- err);
- goto done;
- }
-
- /* Setup default scan unassoc time */
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
- BRCMF_DEFAULT_SCAN_UNASSOC_TIME);
- if (err) {
- brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n",
- err);
- goto done;
- }
-
- /* do bus specific preinit here */
- err = brcmf_bus_preinit(ifp->drvr->bus_if);
-done:
- return err;
-}
-
-#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG)
-void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
-{
- struct va_format vaf = {
- .fmt = fmt,
- };
- va_list args;
-
- va_start(args, fmt);
- vaf.va = &args;
- if (brcmf_msg_level & level)
- pr_debug("%s %pV", func, &vaf);
- trace_brcmf_dbg(level, func, &vaf);
- va_end(args);
-}
-#endif
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.h b/drivers/net/wireless/brcm80211/brcmfmac/common.h
deleted file mode 100644
index 21c7488b4732..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/common.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Copyright (c) 2014 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef BRCMFMAC_COMMON_H
-#define BRCMFMAC_COMMON_H
-
-extern const u8 ALLFFMAC[ETH_ALEN];
-
-/* Sets dongle media info (drv_version, mac address). */
-int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
-
-#endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c
deleted file mode 100644
index b5ab98ee1445..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
+++ /dev/null
@@ -1,1260 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/etherdevice.h>
-#include <linux/module.h>
-#include <net/cfg80211.h>
-#include <net/rtnetlink.h>
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-
-#include "core.h"
-#include "bus.h"
-#include "debug.h"
-#include "fwil_types.h"
-#include "p2p.h"
-#include "cfg80211.h"
-#include "fwil.h"
-#include "fwsignal.h"
-#include "feature.h"
-#include "proto.h"
-#include "pcie.h"
-#include "common.h"
-
-MODULE_AUTHOR("Broadcom Corporation");
-MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
-MODULE_LICENSE("Dual BSD/GPL");
-
-#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */
-
-/* AMPDU rx reordering definitions */
-#define BRCMF_RXREORDER_FLOWID_OFFSET 0
-#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
-#define BRCMF_RXREORDER_FLAGS_OFFSET 4
-#define BRCMF_RXREORDER_CURIDX_OFFSET 6
-#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
-
-#define BRCMF_RXREORDER_DEL_FLOW 0x01
-#define BRCMF_RXREORDER_FLUSH_ALL 0x02
-#define BRCMF_RXREORDER_CURIDX_VALID 0x04
-#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
-#define BRCMF_RXREORDER_NEW_HOLE 0x10
-
-#define BRCMF_BSSIDX_INVALID -1
-
-/* Error bits */
-int brcmf_msg_level;
-module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
-MODULE_PARM_DESC(debug, "level of debug output");
-
-/* P2P0 enable */
-static int brcmf_p2p_enable;
-module_param_named(p2pon, brcmf_p2p_enable, int, 0);
-MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality");
-
-char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
-{
- if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
- brcmf_err("ifidx %d out of range\n", ifidx);
- return "<if_bad>";
- }
-
- if (drvr->iflist[ifidx] == NULL) {
- brcmf_err("null i/f %d\n", ifidx);
- return "<if_null>";
- }
-
- if (drvr->iflist[ifidx]->ndev)
- return drvr->iflist[ifidx]->ndev->name;
-
- return "<if_none>";
-}
-
-struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
-{
- struct brcmf_if *ifp;
- s32 bssidx;
-
- if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
- brcmf_err("ifidx %d out of range\n", ifidx);
- return NULL;
- }
-
- ifp = NULL;
- bssidx = drvr->if2bss[ifidx];
- if (bssidx >= 0)
- ifp = drvr->iflist[bssidx];
-
- return ifp;
-}
-
-static void _brcmf_set_multicast_list(struct work_struct *work)
-{
- struct brcmf_if *ifp;
- struct net_device *ndev;
- struct netdev_hw_addr *ha;
- u32 cmd_value, cnt;
- __le32 cnt_le;
- char *buf, *bufp;
- u32 buflen;
- s32 err;
-
- ifp = container_of(work, struct brcmf_if, multicast_work);
-
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
-
- ndev = ifp->ndev;
-
- /* Determine initial value of allmulti flag */
- cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
-
- /* Send down the multicast list first. */
- cnt = netdev_mc_count(ndev);
- buflen = sizeof(cnt) + (cnt * ETH_ALEN);
- buf = kmalloc(buflen, GFP_ATOMIC);
- if (!buf)
- return;
- bufp = buf;
-
- cnt_le = cpu_to_le32(cnt);
- memcpy(bufp, &cnt_le, sizeof(cnt_le));
- bufp += sizeof(cnt_le);
-
- netdev_for_each_mc_addr(ha, ndev) {
- if (!cnt)
- break;
- memcpy(bufp, ha->addr, ETH_ALEN);
- bufp += ETH_ALEN;
- cnt--;
- }
-
- err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen);
- if (err < 0) {
- brcmf_err("Setting mcast_list failed, %d\n", err);
- cmd_value = cnt ? true : cmd_value;
- }
-
- kfree(buf);
-
- /*
- * Now send the allmulti setting. This is based on the setting in the
- * net_device flags, but might be modified above to be turned on if we
- * were trying to set some addresses and dongle rejected it...
- */
- err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value);
- if (err < 0)
- brcmf_err("Setting allmulti failed, %d\n", err);
-
- /*Finally, pick up the PROMISC flag */
- cmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value);
- if (err < 0)
- brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n",
- err);
-}
-
-static void
-_brcmf_set_mac_address(struct work_struct *work)
-{
- struct brcmf_if *ifp;
- s32 err;
-
- ifp = container_of(work, struct brcmf_if, setmacaddr_work);
-
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
-
- err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr,
- ETH_ALEN);
- if (err < 0) {
- brcmf_err("Setting cur_etheraddr failed, %d\n", err);
- } else {
- brcmf_dbg(TRACE, "MAC address updated to %pM\n",
- ifp->mac_addr);
- memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
- }
-}
-
-static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct sockaddr *sa = (struct sockaddr *)addr;
-
- memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN);
- schedule_work(&ifp->setmacaddr_work);
- return 0;
-}
-
-static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
-
- schedule_work(&ifp->multicast_work);
-}
-
-static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
- struct net_device *ndev)
-{
- int ret;
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_pub *drvr = ifp->drvr;
- struct ethhdr *eh = (struct ethhdr *)(skb->data);
-
- brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
-
- /* Can the device send data? */
- if (drvr->bus_if->state != BRCMF_BUS_UP) {
- brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state);
- netif_stop_queue(ndev);
- dev_kfree_skb(skb);
- ret = -ENODEV;
- goto done;
- }
-
- if (!drvr->iflist[ifp->bssidx]) {
- brcmf_err("bad ifidx %d\n", ifp->bssidx);
- netif_stop_queue(ndev);
- dev_kfree_skb(skb);
- ret = -ENODEV;
- goto done;
- }
-
- /* Make sure there's enough room for any header */
- if (skb_headroom(skb) < drvr->hdrlen) {
- struct sk_buff *skb2;
-
- brcmf_dbg(INFO, "%s: insufficient headroom\n",
- brcmf_ifname(drvr, ifp->bssidx));
- drvr->bus_if->tx_realloc++;
- skb2 = skb_realloc_headroom(skb, drvr->hdrlen);
- dev_kfree_skb(skb);
- skb = skb2;
- if (skb == NULL) {
- brcmf_err("%s: skb_realloc_headroom failed\n",
- brcmf_ifname(drvr, ifp->bssidx));
- ret = -ENOMEM;
- goto done;
- }
- }
-
- /* validate length for ether packet */
- if (skb->len < sizeof(*eh)) {
- ret = -EINVAL;
- dev_kfree_skb(skb);
- goto done;
- }
-
- if (eh->h_proto == htons(ETH_P_PAE))
- atomic_inc(&ifp->pend_8021x_cnt);
-
- ret = brcmf_fws_process_skb(ifp, skb);
-
-done:
- if (ret) {
- ifp->stats.tx_dropped++;
- } else {
- ifp->stats.tx_packets++;
- ifp->stats.tx_bytes += skb->len;
- }
-
- /* Return ok: we always eat the packet */
- return NETDEV_TX_OK;
-}
-
-void brcmf_txflowblock_if(struct brcmf_if *ifp,
- enum brcmf_netif_stop_reason reason, bool state)
-{
- unsigned long flags;
-
- if (!ifp || !ifp->ndev)
- 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);
- ifp->netif_stop |= reason;
- } else {
- ifp->netif_stop &= ~reason;
- 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)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- brcmf_fws_bus_blocked(drvr, state);
-}
-
-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
-{
- skb->dev = ifp->ndev;
- skb->protocol = eth_type_trans(skb, skb->dev);
-
- if (skb->pkt_type == PACKET_MULTICAST)
- ifp->stats.multicast++;
-
- /* Process special event packets */
- brcmf_fweh_process_skb(ifp->drvr, skb);
-
- if (!(ifp->ndev->flags & IFF_UP)) {
- brcmu_pkt_buf_free_skb(skb);
- return;
- }
-
- ifp->stats.rx_bytes += skb->len;
- ifp->stats.rx_packets++;
-
- brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol));
- if (in_interrupt())
- netif_rx(skb);
- else
- /* If the receive is not processed inside an ISR,
- * the softirqd must be woken explicitly to service
- * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
- */
- netif_rx_ni(skb);
-}
-
-static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
- u8 start, u8 end,
- struct sk_buff_head *skb_list)
-{
- /* initialize return list */
- __skb_queue_head_init(skb_list);
-
- if (rfi->pend_pkts == 0) {
- brcmf_dbg(INFO, "no packets in reorder queue\n");
- return;
- }
-
- do {
- if (rfi->pktslots[start]) {
- __skb_queue_tail(skb_list, rfi->pktslots[start]);
- rfi->pktslots[start] = NULL;
- }
- start++;
- if (start > rfi->max_idx)
- start = 0;
- } while (start != end);
- rfi->pend_pkts -= skb_queue_len(skb_list);
-}
-
-static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
- struct sk_buff *pkt)
-{
- u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
- struct brcmf_ampdu_rx_reorder *rfi;
- struct sk_buff_head reorder_list;
- struct sk_buff *pnext;
- u8 flags;
- u32 buf_size;
-
- flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
- flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
-
- /* validate flags and flow id */
- if (flags == 0xFF) {
- brcmf_err("invalid flags...so ignore this packet\n");
- brcmf_netif_rx(ifp, pkt);
- return;
- }
-
- rfi = ifp->drvr->reorder_flows[flow_id];
- if (flags & BRCMF_RXREORDER_DEL_FLOW) {
- brcmf_dbg(INFO, "flow-%d: delete\n",
- flow_id);
-
- if (rfi == NULL) {
- brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
- flow_id);
- brcmf_netif_rx(ifp, pkt);
- return;
- }
-
- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
- &reorder_list);
- /* add the last packet */
- __skb_queue_tail(&reorder_list, pkt);
- kfree(rfi);
- ifp->drvr->reorder_flows[flow_id] = NULL;
- goto netif_rx;
- }
- /* from here on we need a flow reorder instance */
- if (rfi == NULL) {
- buf_size = sizeof(*rfi);
- max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
-
- buf_size += (max_idx + 1) * sizeof(pkt);
-
- /* allocate space for flow reorder info */
- brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
- flow_id, max_idx);
- rfi = kzalloc(buf_size, GFP_ATOMIC);
- if (rfi == NULL) {
- brcmf_err("failed to alloc buffer\n");
- brcmf_netif_rx(ifp, pkt);
- return;
- }
-
- ifp->drvr->reorder_flows[flow_id] = rfi;
- rfi->pktslots = (struct sk_buff **)(rfi+1);
- rfi->max_idx = max_idx;
- }
- if (flags & BRCMF_RXREORDER_NEW_HOLE) {
- if (rfi->pend_pkts) {
- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
- rfi->exp_idx,
- &reorder_list);
- WARN_ON(rfi->pend_pkts);
- } else {
- __skb_queue_head_init(&reorder_list);
- }
- rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
- rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
- rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
- rfi->pktslots[rfi->cur_idx] = pkt;
- rfi->pend_pkts++;
- brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
- flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
- } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
- cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
- exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
-
- if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
- /* still in the current hole */
- /* enqueue the current on the buffer chain */
- if (rfi->pktslots[cur_idx] != NULL) {
- brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
- brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
- rfi->pktslots[cur_idx] = NULL;
- }
- rfi->pktslots[cur_idx] = pkt;
- rfi->pend_pkts++;
- rfi->cur_idx = cur_idx;
- brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
- flow_id, cur_idx, exp_idx, rfi->pend_pkts);
-
- /* can return now as there is no reorder
- * list to process.
- */
- return;
- }
- if (rfi->exp_idx == cur_idx) {
- if (rfi->pktslots[cur_idx] != NULL) {
- brcmf_dbg(INFO, "error buffer pending..free it\n");
- brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
- rfi->pktslots[cur_idx] = NULL;
- }
- rfi->pktslots[cur_idx] = pkt;
- rfi->pend_pkts++;
-
- /* got the expected one. flush from current to expected
- * and update expected
- */
- brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
- flow_id, cur_idx, exp_idx, rfi->pend_pkts);
-
- rfi->cur_idx = cur_idx;
- rfi->exp_idx = exp_idx;
-
- brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
- &reorder_list);
- brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
- flow_id, skb_queue_len(&reorder_list),
- rfi->pend_pkts);
- } else {
- u8 end_idx;
-
- brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
- flow_id, flags, rfi->cur_idx, rfi->exp_idx,
- cur_idx, exp_idx);
- if (flags & BRCMF_RXREORDER_FLUSH_ALL)
- end_idx = rfi->exp_idx;
- else
- end_idx = exp_idx;
-
- /* flush pkts first */
- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
- &reorder_list);
-
- if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
- __skb_queue_tail(&reorder_list, pkt);
- } else {
- rfi->pktslots[cur_idx] = pkt;
- rfi->pend_pkts++;
- }
- rfi->exp_idx = exp_idx;
- rfi->cur_idx = cur_idx;
- }
- } else {
- /* explicity window move updating the expected index */
- exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
-
- brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
- flow_id, flags, rfi->exp_idx, exp_idx);
- if (flags & BRCMF_RXREORDER_FLUSH_ALL)
- end_idx = rfi->exp_idx;
- else
- end_idx = exp_idx;
-
- brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
- &reorder_list);
- __skb_queue_tail(&reorder_list, pkt);
- /* set the new expected idx */
- rfi->exp_idx = exp_idx;
- }
-netif_rx:
- skb_queue_walk_safe(&reorder_list, pkt, pnext) {
- __skb_unlink(pkt, &reorder_list);
- brcmf_netif_rx(ifp, pkt);
- }
-}
-
-void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
-{
- struct brcmf_if *ifp;
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
- struct brcmf_skb_reorder_data *rd;
- int ret;
-
- brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
-
- /* process and remove protocol-specific header */
- ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
-
- if (ret || !ifp || !ifp->ndev) {
- if (ret != -ENODATA && ifp)
- ifp->stats.rx_errors++;
- brcmu_pkt_buf_free_skb(skb);
- return;
- }
-
- rd = (struct brcmf_skb_reorder_data *)skb->cb;
- if (rd->reorder)
- brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
- else
- brcmf_netif_rx(ifp, skb);
-}
-
-void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
-{
- struct ethhdr *eh;
- u16 type;
-
- eh = (struct ethhdr *)(txp->data);
- type = ntohs(eh->h_proto);
-
- if (type == ETH_P_PAE) {
- atomic_dec(&ifp->pend_8021x_cnt);
- if (waitqueue_active(&ifp->pend_8021x_wait))
- wake_up(&ifp->pend_8021x_wait);
- }
-
- if (!success)
- ifp->stats.tx_errors++;
-
- brcmu_pkt_buf_free_skb(txp);
-}
-
-void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
- struct brcmf_if *ifp;
-
- /* await txstatus signal for firmware if active */
- if (brcmf_fws_fc_active(drvr->fws)) {
- if (!success)
- brcmf_fws_bustxfail(drvr->fws, txp);
- } else {
- if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
- brcmu_pkt_buf_free_skb(txp);
- else
- brcmf_txfinalize(ifp, txp, success);
- }
-}
-
-static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
-
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
-
- return &ifp->stats;
-}
-
-static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
- struct ethtool_drvinfo *info)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_pub *drvr = ifp->drvr;
- char drev[BRCMU_DOTREV_LEN] = "n/a";
-
- if (drvr->revinfo.result == 0)
- brcmu_dotrev_str(drvr->revinfo.driverrev, drev);
- strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
- strlcpy(info->version, drev, sizeof(info->version));
- strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version));
- strlcpy(info->bus_info, dev_name(drvr->bus_if->dev),
- sizeof(info->bus_info));
-}
-
-static const struct ethtool_ops brcmf_ethtool_ops = {
- .get_drvinfo = brcmf_ethtool_get_drvinfo,
-};
-
-static int brcmf_netdev_stop(struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
-
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
-
- brcmf_cfg80211_down(ndev);
-
- brcmf_net_setcarrier(ifp, false);
-
- return 0;
-}
-
-static int brcmf_netdev_open(struct net_device *ndev)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_pub *drvr = ifp->drvr;
- struct brcmf_bus *bus_if = drvr->bus_if;
- u32 toe_ol;
-
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
-
- /* If bus is not ready, can't continue */
- if (bus_if->state != BRCMF_BUS_UP) {
- brcmf_err("failed bus is not ready\n");
- return -EAGAIN;
- }
-
- atomic_set(&ifp->pend_8021x_cnt, 0);
-
- /* Get current TOE mode from dongle */
- if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0
- && (toe_ol & TOE_TX_CSUM_OL) != 0)
- ndev->features |= NETIF_F_IP_CSUM;
- else
- ndev->features &= ~NETIF_F_IP_CSUM;
-
- if (brcmf_cfg80211_up(ndev)) {
- brcmf_err("failed to bring up cfg80211\n");
- return -EIO;
- }
-
- /* Clear, carrier, set when connected or AP mode. */
- netif_carrier_off(ndev);
- return 0;
-}
-
-static const struct net_device_ops brcmf_netdev_ops_pri = {
- .ndo_open = brcmf_netdev_open,
- .ndo_stop = brcmf_netdev_stop,
- .ndo_get_stats = brcmf_netdev_get_stats,
- .ndo_start_xmit = brcmf_netdev_start_xmit,
- .ndo_set_mac_address = brcmf_netdev_set_mac_address,
- .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
-};
-
-int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- struct net_device *ndev;
- s32 err;
-
- brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx,
- ifp->mac_addr);
- ndev = ifp->ndev;
-
- /* set appropriate operations */
- ndev->netdev_ops = &brcmf_netdev_ops_pri;
-
- ndev->hard_header_len += drvr->hdrlen;
- ndev->ethtool_ops = &brcmf_ethtool_ops;
-
- drvr->rxsz = ndev->mtu + ndev->hard_header_len +
- drvr->hdrlen;
-
- /* set the mac address */
- memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
-
- INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address);
- INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
-
- if (rtnl_locked)
- err = register_netdevice(ndev);
- else
- err = register_netdev(ndev);
- if (err != 0) {
- brcmf_err("couldn't register the net device\n");
- goto fail;
- }
-
- brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
- return 0;
-
-fail:
- drvr->iflist[ifp->bssidx] = NULL;
- ndev->netdev_ops = NULL;
- free_netdev(ndev);
- return -EBADE;
-}
-
-static void brcmf_net_detach(struct net_device *ndev)
-{
- if (ndev->reg_state == NETREG_REGISTERED)
- unregister_netdev(ndev);
- else
- brcmf_cfg80211_free_netdev(ndev);
-}
-
-void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
-{
- struct net_device *ndev;
-
- brcmf_dbg(TRACE, "Enter, idx=%d carrier=%d\n", ifp->bssidx, on);
-
- ndev = ifp->ndev;
- brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on);
- if (on) {
- if (!netif_carrier_ok(ndev))
- netif_carrier_on(ndev);
-
- } else {
- if (netif_carrier_ok(ndev))
- netif_carrier_off(ndev);
- }
-}
-
-static int brcmf_net_p2p_open(struct net_device *ndev)
-{
- brcmf_dbg(TRACE, "Enter\n");
-
- return brcmf_cfg80211_up(ndev);
-}
-
-static int brcmf_net_p2p_stop(struct net_device *ndev)
-{
- brcmf_dbg(TRACE, "Enter\n");
-
- return brcmf_cfg80211_down(ndev);
-}
-
-static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb,
- struct net_device *ndev)
-{
- if (skb)
- dev_kfree_skb_any(skb);
-
- return NETDEV_TX_OK;
-}
-
-static const struct net_device_ops brcmf_netdev_ops_p2p = {
- .ndo_open = brcmf_net_p2p_open,
- .ndo_stop = brcmf_net_p2p_stop,
- .ndo_start_xmit = brcmf_net_p2p_start_xmit
-};
-
-static int brcmf_net_p2p_attach(struct brcmf_if *ifp)
-{
- struct net_device *ndev;
-
- brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx,
- ifp->mac_addr);
- ndev = ifp->ndev;
-
- ndev->netdev_ops = &brcmf_netdev_ops_p2p;
-
- /* set the mac address */
- memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
-
- if (register_netdev(ndev) != 0) {
- brcmf_err("couldn't register the p2p net device\n");
- goto fail;
- }
-
- brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
-
- return 0;
-
-fail:
- ifp->drvr->iflist[ifp->bssidx] = NULL;
- ndev->netdev_ops = NULL;
- free_netdev(ndev);
- return -EBADE;
-}
-
-struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
- bool is_p2pdev, char *name, u8 *mac_addr)
-{
- struct brcmf_if *ifp;
- struct net_device *ndev;
-
- brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx);
-
- ifp = drvr->iflist[bssidx];
- /*
- * Delete the existing interface before overwriting it
- * in case we missed the BRCMF_E_IF_DEL event.
- */
- if (ifp) {
- brcmf_err("ERROR: netdev:%s already exists\n",
- ifp->ndev->name);
- if (ifidx) {
- netif_stop_queue(ifp->ndev);
- brcmf_net_detach(ifp->ndev);
- drvr->iflist[bssidx] = NULL;
- } else {
- brcmf_err("ignore IF event\n");
- return ERR_PTR(-EINVAL);
- }
- }
-
- if (!brcmf_p2p_enable && is_p2pdev) {
- /* this is P2P_DEVICE interface */
- brcmf_dbg(INFO, "allocate non-netdev interface\n");
- ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
- if (!ifp)
- return ERR_PTR(-ENOMEM);
- } else {
- brcmf_dbg(INFO, "allocate netdev interface\n");
- /* Allocate netdev, including space for private structure */
- ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name,
- NET_NAME_UNKNOWN, ether_setup);
- if (!ndev)
- return ERR_PTR(-ENOMEM);
-
- ndev->destructor = brcmf_cfg80211_free_netdev;
- ifp = netdev_priv(ndev);
- ifp->ndev = ndev;
- /* store mapping ifidx to bssidx */
- if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID)
- drvr->if2bss[ifidx] = bssidx;
- }
-
- ifp->drvr = drvr;
- drvr->iflist[bssidx] = ifp;
- ifp->ifidx = 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);
-
- brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n",
- current->pid, name, ifp->mac_addr);
-
- return ifp;
-}
-
-static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
-{
- struct brcmf_if *ifp;
-
- ifp = drvr->iflist[bssidx];
- drvr->iflist[bssidx] = NULL;
- if (!ifp) {
- brcmf_err("Null interface, idx=%d\n", bssidx);
- return;
- }
- brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx);
- if (drvr->if2bss[ifp->ifidx] == bssidx)
- drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID;
- if (ifp->ndev) {
- if (bssidx == 0) {
- if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
- rtnl_lock();
- brcmf_netdev_stop(ifp->ndev);
- rtnl_unlock();
- }
- } else {
- netif_stop_queue(ifp->ndev);
- }
-
- if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
- cancel_work_sync(&ifp->setmacaddr_work);
- cancel_work_sync(&ifp->multicast_work);
- }
- brcmf_net_detach(ifp->ndev);
- } else {
- /* Only p2p device interfaces which get dynamically created
- * end up here. In this case the p2p module should be informed
- * about the removal of the interface within the firmware. If
- * not then p2p commands towards the firmware will cause some
- * serious troublesome side effects. The p2p module will clean
- * up the ifp if needed.
- */
- brcmf_p2p_ifp_removed(ifp);
- kfree(ifp);
- }
-}
-
-void brcmf_remove_interface(struct brcmf_if *ifp)
-{
- if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp))
- return;
- brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx,
- ifp->ifidx);
- brcmf_fws_del_interface(ifp);
- brcmf_del_if(ifp->drvr, ifp->bssidx);
-}
-
-int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
-{
- int ifidx;
- int bsscfgidx;
- bool available;
- int highest;
-
- available = false;
- bsscfgidx = 2;
- highest = 2;
- for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
- if (drvr->iflist[ifidx]) {
- if (drvr->iflist[ifidx]->bssidx == bsscfgidx)
- bsscfgidx = highest + 1;
- else if (drvr->iflist[ifidx]->bssidx > highest)
- highest = drvr->iflist[ifidx]->bssidx;
- } else {
- available = true;
- }
- }
-
- return available ? bsscfgidx : -ENOMEM;
-}
-
-int brcmf_attach(struct device *dev)
-{
- struct brcmf_pub *drvr = NULL;
- int ret = 0;
- int i;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* Allocate primary brcmf_info */
- drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC);
- if (!drvr)
- return -ENOMEM;
-
- for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++)
- drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
-
- mutex_init(&drvr->proto_block);
-
- /* Link to bus module */
- drvr->hdrlen = 0;
- drvr->bus_if = dev_get_drvdata(dev);
- drvr->bus_if->drvr = drvr;
-
- /* attach debug facilities */
- brcmf_debug_attach(drvr);
-
- /* Attach and link in the protocol */
- ret = brcmf_proto_attach(drvr);
- if (ret != 0) {
- brcmf_err("brcmf_prot_attach failed\n");
- goto fail;
- }
-
- /* attach firmware event handler */
- brcmf_fweh_attach(drvr);
-
- return ret;
-
-fail:
- brcmf_detach(dev);
-
- return ret;
-}
-
-static int brcmf_revinfo_read(struct seq_file *s, void *data)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(s->private);
- struct brcmf_rev_info *ri = &bus_if->drvr->revinfo;
- char drev[BRCMU_DOTREV_LEN];
- char brev[BRCMU_BOARDREV_LEN];
-
- seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid);
- seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid);
- seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev));
- seq_printf(s, "chipnum: %u (%x)\n", ri->chipnum, ri->chipnum);
- seq_printf(s, "chiprev: %u\n", ri->chiprev);
- seq_printf(s, "chippkg: %u\n", ri->chippkg);
- seq_printf(s, "corerev: %u\n", ri->corerev);
- seq_printf(s, "boardid: 0x%04x\n", ri->boardid);
- seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor);
- seq_printf(s, "boardrev: %s\n", brcmu_boardrev_str(ri->boardrev, brev));
- seq_printf(s, "driverrev: %s\n", brcmu_dotrev_str(ri->driverrev, drev));
- seq_printf(s, "ucoderev: %u\n", ri->ucoderev);
- seq_printf(s, "bus: %u\n", ri->bus);
- seq_printf(s, "phytype: %u\n", ri->phytype);
- seq_printf(s, "phyrev: %u\n", ri->phyrev);
- seq_printf(s, "anarev: %u\n", ri->anarev);
- seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
-
- return 0;
-}
-
-int brcmf_bus_start(struct device *dev)
-{
- int ret = -1;
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
- struct brcmf_if *ifp;
- struct brcmf_if *p2p_ifp;
-
- brcmf_dbg(TRACE, "\n");
-
- /* add primary networking interface */
- ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
- if (IS_ERR(ifp))
- return PTR_ERR(ifp);
-
- p2p_ifp = NULL;
-
- /* signal bus ready */
- brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
-
- /* Bus is ready, do any initialization */
- ret = brcmf_c_preinit_dcmds(ifp);
- if (ret < 0)
- goto fail;
-
- brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
-
- /* assure we have chipid before feature attach */
- if (!bus_if->chip) {
- bus_if->chip = drvr->revinfo.chipnum;
- bus_if->chiprev = drvr->revinfo.chiprev;
- brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n",
- bus_if->chip, bus_if->chip, bus_if->chiprev);
- }
- brcmf_feat_attach(drvr);
-
- ret = brcmf_fws_init(drvr);
- if (ret < 0)
- goto fail;
-
- brcmf_fws_add_interface(ifp);
-
- drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev,
- brcmf_p2p_enable);
- if (drvr->config == NULL) {
- ret = -ENOMEM;
- goto fail;
- }
-
- ret = brcmf_net_attach(ifp, false);
-
- if ((!ret) && (brcmf_p2p_enable)) {
- p2p_ifp = drvr->iflist[1];
- if (p2p_ifp)
- ret = brcmf_net_p2p_attach(p2p_ifp);
- }
-fail:
- if (ret < 0) {
- brcmf_err("failed: %d\n", ret);
- if (drvr->config) {
- brcmf_cfg80211_detach(drvr->config);
- drvr->config = NULL;
- }
- if (drvr->fws) {
- brcmf_fws_del_interface(ifp);
- brcmf_fws_deinit(drvr);
- }
- if (ifp)
- brcmf_net_detach(ifp->ndev);
- if (p2p_ifp)
- brcmf_net_detach(p2p_ifp->ndev);
- return ret;
- }
- return 0;
-}
-
-void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
-
- if (drvr) {
- drvr->hdrlen += len;
- }
-}
-
-static void brcmf_bus_detach(struct brcmf_pub *drvr)
-{
- brcmf_dbg(TRACE, "Enter\n");
-
- if (drvr) {
- /* Stop the bus module */
- brcmf_bus_stop(drvr->bus_if);
- }
-}
-
-void brcmf_dev_reset(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
-
- if (drvr == NULL)
- return;
-
- if (drvr->iflist[0])
- brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1);
-}
-
-void brcmf_detach(struct device *dev)
-{
- s32 i;
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (drvr == NULL)
- return;
-
- /* stop firmware event handling */
- brcmf_fweh_detach(drvr);
- if (drvr->config)
- brcmf_p2p_detach(&drvr->config->p2p);
-
- brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
-
- /* make sure primary interface removed last */
- for (i = BRCMF_MAX_IFS-1; i > -1; i--)
- brcmf_remove_interface(drvr->iflist[i]);
-
- brcmf_cfg80211_detach(drvr->config);
-
- brcmf_fws_deinit(drvr);
-
- brcmf_bus_detach(drvr);
-
- brcmf_proto_detach(drvr);
-
- brcmf_debug_detach(drvr);
- bus_if->drvr = NULL;
- kfree(drvr);
-}
-
-s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_if *ifp = bus_if->drvr->iflist[0];
-
- return brcmf_fil_iovar_data_set(ifp, name, data, len);
-}
-
-static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp)
-{
- return atomic_read(&ifp->pend_8021x_cnt);
-}
-
-int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
-{
- int err;
-
- err = wait_event_timeout(ifp->pend_8021x_wait,
- !brcmf_get_pend_8021x_cnt(ifp),
- msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX));
-
- WARN_ON(!err);
-
- return !err;
-}
-
-void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
-{
- struct brcmf_pub *drvr = bus->drvr;
- struct net_device *ndev;
- int ifidx;
-
- brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state);
- bus->state = state;
-
- if (state == BRCMF_BUS_UP) {
- for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
- if ((drvr->iflist[ifidx]) &&
- (drvr->iflist[ifidx]->ndev)) {
- ndev = drvr->iflist[ifidx]->ndev;
- if (netif_queue_stopped(ndev))
- netif_wake_queue(ndev);
- }
- }
- }
-}
-
-static void brcmf_driver_register(struct work_struct *work)
-{
-#ifdef CONFIG_BRCMFMAC_SDIO
- brcmf_sdio_register();
-#endif
-#ifdef CONFIG_BRCMFMAC_USB
- brcmf_usb_register();
-#endif
-#ifdef CONFIG_BRCMFMAC_PCIE
- brcmf_pcie_register();
-#endif
-}
-static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
-
-static int __init brcmfmac_module_init(void)
-{
- brcmf_debugfs_init();
-#ifdef CONFIG_BRCMFMAC_SDIO
- brcmf_sdio_init();
-#endif
- if (!schedule_work(&brcmf_driver_work))
- return -EBUSY;
-
- return 0;
-}
-
-static void __exit brcmfmac_module_exit(void)
-{
- cancel_work_sync(&brcmf_driver_work);
-
-#ifdef CONFIG_BRCMFMAC_SDIO
- brcmf_sdio_exit();
-#endif
-#ifdef CONFIG_BRCMFMAC_USB
- brcmf_usb_exit();
-#endif
-#ifdef CONFIG_BRCMFMAC_PCIE
- brcmf_pcie_exit();
-#endif
- brcmf_debugfs_exit();
-}
-
-module_init(brcmfmac_module_init);
-module_exit(brcmfmac_module_exit);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h
deleted file mode 100644
index 2f9101b2ad34..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/****************
- * Common types *
- */
-
-#ifndef BRCMFMAC_CORE_H
-#define BRCMFMAC_CORE_H
-
-#include <net/cfg80211.h>
-#include "fweh.h"
-
-#define TOE_TX_CSUM_OL 0x00000001
-#define TOE_RX_CSUM_OL 0x00000002
-
-/* For supporting multiple interfaces */
-#define BRCMF_MAX_IFS 16
-
-/* Small, medium and maximum buffer size for dcmd
- */
-#define BRCMF_DCMD_SMLEN 256
-#define BRCMF_DCMD_MEDLEN 1536
-#define BRCMF_DCMD_MAXLEN 8192
-
-/* IOCTL from host to device are limited in lenght. A device can only handle
- * ethernet frame size. This limitation is to be applied by protocol layer.
- */
-#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN)
-
-#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256
-
-/* Length of firmware version string stored for
- * ethtool driver info which uses 32 bytes as well.
- */
-#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32
-
-/**
- * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
- *
- * @pktslots: dynamic allocated array for ordering AMPDU packets.
- * @flow_id: AMPDU flow identifier.
- * @cur_idx: last AMPDU index from firmware.
- * @exp_idx: expected next AMPDU index.
- * @max_idx: maximum amount of packets per AMPDU.
- * @pend_pkts: number of packets currently in @pktslots.
- */
-struct brcmf_ampdu_rx_reorder {
- struct sk_buff **pktslots;
- u8 flow_id;
- u8 cur_idx;
- u8 exp_idx;
- u8 max_idx;
- u8 pend_pkts;
-};
-
-/* Forward decls for struct brcmf_pub (see below) */
-struct brcmf_proto; /* device communication protocol info */
-struct brcmf_cfg80211_dev; /* cfg80211 device info */
-struct brcmf_fws_info; /* firmware signalling info */
-
-/*
- * struct brcmf_rev_info
- *
- * The result field stores the error code of the
- * revision info request from firmware. For the
- * other fields see struct brcmf_rev_info_le in
- * fwil_types.h
- */
-struct brcmf_rev_info {
- int result;
- u32 vendorid;
- u32 deviceid;
- u32 radiorev;
- u32 chiprev;
- u32 corerev;
- u32 boardid;
- u32 boardvendor;
- u32 boardrev;
- u32 driverrev;
- u32 ucoderev;
- u32 bus;
- u32 chipnum;
- u32 phytype;
- u32 phyrev;
- u32 anarev;
- u32 chippkg;
- u32 nvramrev;
-};
-
-/* Common structure for module and instance linkage */
-struct brcmf_pub {
- /* Linkage ponters */
- struct brcmf_bus *bus_if;
- struct brcmf_proto *proto;
- struct brcmf_cfg80211_info *config;
-
- /* Internal brcmf items */
- uint hdrlen; /* Total BRCMF header length (proto + bus) */
- uint rxsz; /* Rx buffer size bus module should use */
-
- /* Dongle media info */
- char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN];
- u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */
-
- /* Multicast data packets sent to dongle */
- unsigned long tx_multicast;
-
- struct mac_address addresses[BRCMF_MAX_IFS];
-
- struct brcmf_if *iflist[BRCMF_MAX_IFS];
- s32 if2bss[BRCMF_MAX_IFS];
-
- struct mutex proto_block;
- unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
-
- struct brcmf_fweh_info fweh;
-
- struct brcmf_fws_info *fws;
-
- struct brcmf_ampdu_rx_reorder
- *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
-
- u32 feat_flags;
- u32 chip_quirks;
-
- struct brcmf_rev_info revinfo;
-#ifdef DEBUG
- struct dentry *dbgfs_dir;
-#endif
-};
-
-/* forward declarations */
-struct brcmf_cfg80211_vif;
-struct brcmf_fws_mac_descriptor;
-
-/**
- * enum brcmf_netif_stop_reason - reason for stopping netif queue.
- *
- * @BRCMF_NETIF_STOP_REASON_FWS_FC:
- * netif stopped due to firmware signalling flow control.
- * @BRCMF_NETIF_STOP_REASON_FLOW:
- * netif stopped due to flowring full.
- * @BRCMF_NETIF_STOP_REASON_DISCONNECTED:
- * netif stopped due to not being connected (STA mode).
- */
-enum brcmf_netif_stop_reason {
- BRCMF_NETIF_STOP_REASON_FWS_FC = BIT(0),
- BRCMF_NETIF_STOP_REASON_FLOW = BIT(1),
- BRCMF_NETIF_STOP_REASON_DISCONNECTED = BIT(2)
-};
-
-/**
- * struct brcmf_if - interface control information.
- *
- * @drvr: points to device related information.
- * @vif: points to cfg80211 specific interface information.
- * @ndev: associated network device.
- * @stats: interface specific network statistics.
- * @setmacaddr_work: worker object for setting mac address.
- * @multicast_work: worker object for multicast provisioning.
- * @fws_desc: interface specific firmware-signalling descriptor.
- * @ifidx: interface index in device firmware.
- * @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.
- */
-struct brcmf_if {
- struct brcmf_pub *drvr;
- struct brcmf_cfg80211_vif *vif;
- struct net_device *ndev;
- struct net_device_stats stats;
- struct work_struct setmacaddr_work;
- struct work_struct multicast_work;
- struct brcmf_fws_mac_descriptor *fws_desc;
- int ifidx;
- 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;
-};
-
-struct brcmf_skb_reorder_data {
- u8 *reorder;
-};
-
-int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
-
-/* Return pointer to interface name */
-char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
-struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
-int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
-struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
- bool is_p2pdev, char *name, u8 *mac_addr);
-void brcmf_remove_interface(struct brcmf_if *ifp);
-int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
-void brcmf_txflowblock_if(struct brcmf_if *ifp,
- enum brcmf_netif_stop_reason reason, bool state);
-void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
-void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
-
-#endif /* BRCMFMAC_CORE_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
deleted file mode 100644
index 1299dccc78b4..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <linux/debugfs.h>
-#include <linux/netdevice.h>
-#include <linux/module.h>
-#include <linux/devcoredump.h>
-
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include "core.h"
-#include "bus.h"
-#include "fweh.h"
-#include "debug.h"
-
-static struct dentry *root_folder;
-
-static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
- size_t len)
-{
- void *dump;
- size_t ramsize;
-
- ramsize = brcmf_bus_get_ramsize(bus);
- if (ramsize) {
- dump = vzalloc(len + ramsize);
- if (!dump)
- return -ENOMEM;
- memcpy(dump, data, len);
- brcmf_bus_get_memdump(bus, dump + len, ramsize);
- dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
- }
- return 0;
-}
-
-static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp,
- const struct brcmf_event_msg *evtmsg,
- void *data)
-{
- brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
-
- return brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
- evtmsg->datalen);
-}
-
-void brcmf_debugfs_init(void)
-{
- root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
- if (IS_ERR(root_folder))
- root_folder = NULL;
-}
-
-void brcmf_debugfs_exit(void)
-{
- if (!root_folder)
- return;
-
- debugfs_remove_recursive(root_folder);
- root_folder = NULL;
-}
-
-int brcmf_debug_attach(struct brcmf_pub *drvr)
-{
- struct device *dev = drvr->bus_if->dev;
-
- if (!root_folder)
- return -ENODEV;
-
- drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
- if (IS_ERR(drvr->dbgfs_dir))
- return PTR_ERR(drvr->dbgfs_dir);
-
-
- return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
- brcmf_debug_psm_watchdog_notify);
-}
-
-void brcmf_debug_detach(struct brcmf_pub *drvr)
-{
- brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);
-
- if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
- debugfs_remove_recursive(drvr->dbgfs_dir);
-}
-
-struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
-{
- return drvr->dbgfs_dir;
-}
-
-int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
- int (*read_fn)(struct seq_file *seq, void *data))
-{
- struct dentry *e;
-
- e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn,
- drvr->dbgfs_dir, read_fn);
- return PTR_ERR_OR_ZERO(e);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
deleted file mode 100644
index d0d9676f7f9d..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef BRCMFMAC_DEBUG_H
-#define BRCMFMAC_DEBUG_H
-
-/* message levels */
-#define BRCMF_TRACE_VAL 0x00000002
-#define BRCMF_INFO_VAL 0x00000004
-#define BRCMF_DATA_VAL 0x00000008
-#define BRCMF_CTL_VAL 0x00000010
-#define BRCMF_TIMER_VAL 0x00000020
-#define BRCMF_HDRS_VAL 0x00000040
-#define BRCMF_BYTES_VAL 0x00000080
-#define BRCMF_INTR_VAL 0x00000100
-#define BRCMF_GLOM_VAL 0x00000200
-#define BRCMF_EVENT_VAL 0x00000400
-#define BRCMF_BTA_VAL 0x00000800
-#define BRCMF_FIL_VAL 0x00001000
-#define BRCMF_USB_VAL 0x00002000
-#define BRCMF_SCAN_VAL 0x00004000
-#define BRCMF_CONN_VAL 0x00008000
-#define BRCMF_BCDC_VAL 0x00010000
-#define BRCMF_SDIO_VAL 0x00020000
-#define BRCMF_MSGBUF_VAL 0x00040000
-#define BRCMF_PCIE_VAL 0x00080000
-#define BRCMF_FWCON_VAL 0x00100000
-
-/* set default print format */
-#undef pr_fmt
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-/* Macro for error messages. net_ratelimit() is used when driver
- * debugging is not selected. When debugging the driver error
- * messages are as important as other tracing or even more so.
- */
-#ifndef CONFIG_BRCM_TRACING
-#ifdef CONFIG_BRCMDBG
-#define brcmf_err(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__)
-#else
-#define brcmf_err(fmt, ...) \
- do { \
- if (net_ratelimit()) \
- pr_err("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
-#endif
-#else
-__printf(2, 3)
-void __brcmf_err(const char *func, const char *fmt, ...);
-#define brcmf_err(fmt, ...) \
- __brcmf_err(__func__, fmt, ##__VA_ARGS__)
-#endif
-
-#if defined(DEBUG) || defined(CONFIG_BRCM_TRACING)
-__printf(3, 4)
-void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...);
-#define brcmf_dbg(level, fmt, ...) \
-do { \
- __brcmf_dbg(BRCMF_##level##_VAL, __func__, \
- fmt, ##__VA_ARGS__); \
-} while (0)
-#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL)
-#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL)
-#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL)
-#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL)
-#define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL)
-#define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL)
-#define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL)
-#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL)
-
-#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
-
-#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
-
-#define BRCMF_DATA_ON() 0
-#define BRCMF_CTL_ON() 0
-#define BRCMF_HDRS_ON() 0
-#define BRCMF_BYTES_ON() 0
-#define BRCMF_GLOM_ON() 0
-#define BRCMF_EVENT_ON() 0
-#define BRCMF_FIL_ON() 0
-#define BRCMF_FWCON_ON() 0
-
-#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
-
-#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \
-do { \
- trace_brcmf_hexdump((void *)data, len); \
- if (test) \
- brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \
-} while (0)
-
-extern int brcmf_msg_level;
-
-struct brcmf_pub;
-#ifdef DEBUG
-void brcmf_debugfs_init(void);
-void brcmf_debugfs_exit(void);
-int brcmf_debug_attach(struct brcmf_pub *drvr);
-void brcmf_debug_detach(struct brcmf_pub *drvr);
-struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
-int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
- int (*read_fn)(struct seq_file *seq, void *data));
-#else
-static inline void brcmf_debugfs_init(void)
-{
-}
-static inline void brcmf_debugfs_exit(void)
-{
-}
-static inline int brcmf_debug_attach(struct brcmf_pub *drvr)
-{
- return 0;
-}
-static inline void brcmf_debug_detach(struct brcmf_pub *drvr)
-{
-}
-static inline
-int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
- int (*read_fn)(struct seq_file *seq, void *data))
-{
- return 0;
-}
-#endif
-
-#endif /* BRCMFMAC_DEBUG_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
deleted file mode 100644
index 44bb30636690..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (c) 2014 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/netdevice.h>
-#include <linux/module.h>
-
-#include <brcm_hw_ids.h>
-#include "core.h"
-#include "bus.h"
-#include "debug.h"
-#include "fwil.h"
-#include "feature.h"
-
-
-/* Module param feature_disable (global for all devices) */
-static int brcmf_feature_disable;
-module_param_named(feature_disable, brcmf_feature_disable, int, 0);
-MODULE_PARM_DESC(feature_disable, "Disable features");
-
-/*
- * expand feature list to array of feature strings.
- */
-#define BRCMF_FEAT_DEF(_f) \
- #_f,
-static const char *brcmf_feat_names[] = {
- BRCMF_FEAT_LIST
-};
-#undef BRCMF_FEAT_DEF
-
-#ifdef DEBUG
-/*
- * expand quirk list to array of quirk strings.
- */
-#define BRCMF_QUIRK_DEF(_q) \
- #_q,
-static const char * const brcmf_quirk_names[] = {
- BRCMF_QUIRK_LIST
-};
-#undef BRCMF_QUIRK_DEF
-
-/**
- * brcmf_feat_debugfs_read() - expose feature info to debugfs.
- *
- * @seq: sequence for debugfs entry.
- * @data: raw data pointer.
- */
-static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
- u32 feats = bus_if->drvr->feat_flags;
- u32 quirks = bus_if->drvr->chip_quirks;
- int id;
-
- seq_printf(seq, "Features: %08x\n", feats);
- for (id = 0; id < BRCMF_FEAT_LAST; id++)
- if (feats & BIT(id))
- seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
- seq_printf(seq, "\nQuirks: %08x\n", quirks);
- for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
- if (quirks & BIT(id))
- seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
- return 0;
-}
-#else
-static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
-{
- return 0;
-}
-#endif /* DEBUG */
-
-/**
- * brcmf_feat_iovar_int_get() - determine feature through iovar query.
- *
- * @ifp: interface to query.
- * @id: feature id.
- * @name: iovar name.
- */
-static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
- enum brcmf_feat_id id, char *name)
-{
- u32 data;
- int err;
-
- err = brcmf_fil_iovar_int_get(ifp, name, &data);
- if (err == 0) {
- brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
- ifp->drvr->feat_flags |= BIT(id);
- } else {
- brcmf_dbg(TRACE, "%s feature check failed: %d\n",
- brcmf_feat_names[id], err);
- }
-}
-
-/**
- * brcmf_feat_iovar_int_set() - determine feature through iovar set.
- *
- * @ifp: interface to query.
- * @id: feature id.
- * @name: iovar name.
- */
-static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp,
- enum brcmf_feat_id id, char *name, u32 val)
-{
- int err;
-
- err = brcmf_fil_iovar_int_set(ifp, name, val);
- if (err == 0) {
- brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
- ifp->drvr->feat_flags |= BIT(id);
- } else {
- brcmf_dbg(TRACE, "%s feature check failed: %d\n",
- brcmf_feat_names[id], err);
- }
-}
-
-void brcmf_feat_attach(struct brcmf_pub *drvr)
-{
- struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
-
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
- if (drvr->bus_if->wowl_supported)
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
- if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
- brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p");
-
- if (brcmf_feature_disable) {
- brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
- ifp->drvr->feat_flags, brcmf_feature_disable);
- ifp->drvr->feat_flags &= ~brcmf_feature_disable;
- }
-
- /* set chip related quirks */
- switch (drvr->bus_if->chip) {
- case BRCM_CC_43236_CHIP_ID:
- drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
- break;
- case BRCM_CC_4329_CHIP_ID:
- drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
- break;
- default:
- /* no quirks */
- break;
- }
-
- brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
-}
-
-bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
-{
- return (ifp->drvr->feat_flags & BIT(id));
-}
-
-bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
- enum brcmf_feat_quirk quirk)
-{
- return (ifp->drvr->chip_quirks & BIT(quirk));
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
deleted file mode 100644
index 6b381f799f22..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2014 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef _BRCMF_FEATURE_H
-#define _BRCMF_FEATURE_H
-
-/*
- * Features:
- *
- * MBSS: multiple BSSID support (eg. guest network in AP mode).
- * MCHAN: multi-channel for concurrent P2P.
- * PNO: preferred network offload.
- * WOWL: Wake-On-WLAN.
- * P2P: peer-to-peer
- */
-#define BRCMF_FEAT_LIST \
- BRCMF_FEAT_DEF(MBSS) \
- BRCMF_FEAT_DEF(MCHAN) \
- BRCMF_FEAT_DEF(PNO) \
- BRCMF_FEAT_DEF(WOWL) \
- BRCMF_FEAT_DEF(P2P)
-/*
- * Quirks:
- *
- * AUTO_AUTH: workaround needed for automatic authentication type.
- * NEED_MPC: driver needs to disable MPC during scanning operation.
- */
-#define BRCMF_QUIRK_LIST \
- BRCMF_QUIRK_DEF(AUTO_AUTH) \
- BRCMF_QUIRK_DEF(NEED_MPC)
-
-#define BRCMF_FEAT_DEF(_f) \
- BRCMF_FEAT_ ## _f,
-/*
- * expand feature list to enumeration.
- */
-enum brcmf_feat_id {
- BRCMF_FEAT_LIST
- BRCMF_FEAT_LAST
-};
-#undef BRCMF_FEAT_DEF
-
-#define BRCMF_QUIRK_DEF(_q) \
- BRCMF_FEAT_QUIRK_ ## _q,
-/*
- * expand quirk list to enumeration.
- */
-enum brcmf_feat_quirk {
- BRCMF_QUIRK_LIST
- BRCMF_FEAT_QUIRK_LAST
-};
-#undef BRCMF_QUIRK_DEF
-
-/**
- * brcmf_feat_attach() - determine features and quirks.
- *
- * @drvr: driver instance.
- */
-void brcmf_feat_attach(struct brcmf_pub *drvr);
-
-/**
- * brcmf_feat_is_enabled() - query feature.
- *
- * @ifp: interface instance.
- * @id: feature id to check.
- *
- * Return: true is feature is enabled; otherwise false.
- */
-bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id);
-
-/**
- * brcmf_feat_is_quirk_enabled() - query chip quirk.
- *
- * @ifp: interface instance.
- * @quirk: quirk id to check.
- *
- * Return: true is quirk is enabled; otherwise false.
- */
-bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
- enum brcmf_feat_quirk quirk);
-
-#endif /* _BRCMF_FEATURE_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
deleted file mode 100644
index 4248f3c80e78..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+++ /dev/null
@@ -1,542 +0,0 @@
-/*
- * Copyright (c) 2013 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/bcm47xx_nvram.h>
-
-#include "debug.h"
-#include "firmware.h"
-
-#define BRCMF_FW_MAX_NVRAM_SIZE 64000
-#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
-#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
-
-char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
-module_param_string(alternative_fw_path, brcmf_firmware_path,
- BRCMF_FW_PATH_LEN, 0440);
-
-enum nvram_parser_state {
- IDLE,
- KEY,
- VALUE,
- COMMENT,
- END
-};
-
-/**
- * struct nvram_parser - internal info for parser.
- *
- * @state: current parser state.
- * @data: input buffer being parsed.
- * @nvram: output buffer with parse result.
- * @nvram_len: lenght of parse result.
- * @line: current line.
- * @column: current column in line.
- * @pos: byte offset in input buffer.
- * @entry: start position of key,value entry.
- * @multi_dev_v1: detect pcie multi device v1 (compressed).
- * @multi_dev_v2: detect pcie multi device v2.
- */
-struct nvram_parser {
- enum nvram_parser_state state;
- const u8 *data;
- u8 *nvram;
- u32 nvram_len;
- u32 line;
- u32 column;
- u32 pos;
- u32 entry;
- bool multi_dev_v1;
- bool multi_dev_v2;
-};
-
-/**
- * is_nvram_char() - check if char is a valid one for NVRAM entry
- *
- * It accepts all printable ASCII chars except for '#' which opens a comment.
- * Please note that ' ' (space) while accepted is not a valid key name char.
- */
-static bool is_nvram_char(char c)
-{
- /* comment marker excluded */
- if (c == '#')
- return false;
-
- /* key and value may have any other readable character */
- return (c >= 0x20 && c < 0x7f);
-}
-
-static bool is_whitespace(char c)
-{
- return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
-}
-
-static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
-{
- char c;
-
- c = nvp->data[nvp->pos];
- if (c == '\n')
- return COMMENT;
- if (is_whitespace(c))
- goto proceed;
- if (c == '#')
- return COMMENT;
- if (is_nvram_char(c)) {
- nvp->entry = nvp->pos;
- return KEY;
- }
- brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
- nvp->line, nvp->column);
-proceed:
- nvp->column++;
- nvp->pos++;
- return IDLE;
-}
-
-static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
-{
- enum nvram_parser_state st = nvp->state;
- char c;
-
- c = nvp->data[nvp->pos];
- if (c == '=') {
- /* ignore RAW1 by treating as comment */
- if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
- st = COMMENT;
- else
- st = VALUE;
- if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
- nvp->multi_dev_v1 = true;
- if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
- nvp->multi_dev_v2 = true;
- } else if (!is_nvram_char(c) || c == ' ') {
- brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
- nvp->line, nvp->column);
- return COMMENT;
- }
-
- nvp->column++;
- nvp->pos++;
- return st;
-}
-
-static enum nvram_parser_state
-brcmf_nvram_handle_value(struct nvram_parser *nvp)
-{
- char c;
- char *skv;
- char *ekv;
- u32 cplen;
-
- c = nvp->data[nvp->pos];
- if (!is_nvram_char(c)) {
- /* key,value pair complete */
- ekv = (u8 *)&nvp->data[nvp->pos];
- skv = (u8 *)&nvp->data[nvp->entry];
- cplen = ekv - skv;
- if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
- return END;
- /* copy to output buffer */
- memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
- nvp->nvram_len += cplen;
- nvp->nvram[nvp->nvram_len] = '\0';
- nvp->nvram_len++;
- return IDLE;
- }
- nvp->pos++;
- nvp->column++;
- return VALUE;
-}
-
-static enum nvram_parser_state
-brcmf_nvram_handle_comment(struct nvram_parser *nvp)
-{
- char *eoc, *sol;
-
- sol = (char *)&nvp->data[nvp->pos];
- eoc = strchr(sol, '\n');
- if (!eoc) {
- eoc = strchr(sol, '\0');
- if (!eoc)
- return END;
- }
-
- /* eat all moving to next line */
- nvp->line++;
- nvp->column = 1;
- nvp->pos += (eoc - sol) + 1;
- return IDLE;
-}
-
-static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
-{
- /* final state */
- return END;
-}
-
-static enum nvram_parser_state
-(*nv_parser_states[])(struct nvram_parser *nvp) = {
- brcmf_nvram_handle_idle,
- brcmf_nvram_handle_key,
- brcmf_nvram_handle_value,
- brcmf_nvram_handle_comment,
- brcmf_nvram_handle_end
-};
-
-static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
- const u8 *data, size_t data_len)
-{
- size_t size;
-
- memset(nvp, 0, sizeof(*nvp));
- nvp->data = data;
- /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
- if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
- size = BRCMF_FW_MAX_NVRAM_SIZE;
- else
- size = data_len;
- /* Alloc for extra 0 byte + roundup by 4 + length field */
- size += 1 + 3 + sizeof(u32);
- nvp->nvram = kzalloc(size, GFP_KERNEL);
- if (!nvp->nvram)
- return -ENOMEM;
-
- nvp->line = 1;
- nvp->column = 1;
- return 0;
-}
-
-/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
- * devices. Strip it down for one device, use domain_nr/bus_nr to determine
- * which data is to be returned. v1 is the version where nvram is stored
- * compressed and "devpath" maps to index for valid entries.
- */
-static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
- u16 bus_nr)
-{
- /* Device path with a leading '=' key-value separator */
- char pci_path[] = "=pci/?/?";
- size_t pci_len;
- char pcie_path[] = "=pcie/?/?";
- size_t pcie_len;
-
- u32 i, j;
- bool found;
- u8 *nvram;
- u8 id;
-
- nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
- if (!nvram)
- goto fail;
-
- /* min length: devpath0=pcie/1/4/ + 0:x=y */
- if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
- goto fail;
-
- /* First search for the devpathX and see if it is the configuration
- * for domain_nr/bus_nr. Search complete nvp
- */
- snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
- bus_nr);
- pci_len = strlen(pci_path);
- snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
- bus_nr);
- pcie_len = strlen(pcie_path);
- found = false;
- i = 0;
- while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
- /* Format: devpathX=pcie/Y/Z/
- * Y = domain_nr, Z = bus_nr, X = virtual ID
- */
- if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
- (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
- !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
- id = nvp->nvram[i + 7] - '0';
- found = true;
- break;
- }
- while (nvp->nvram[i] != 0)
- i++;
- i++;
- }
- if (!found)
- goto fail;
-
- /* Now copy all valid entries, release old nvram and assign new one */
- i = 0;
- j = 0;
- while (i < nvp->nvram_len) {
- if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
- i += 2;
- while (nvp->nvram[i] != 0) {
- nvram[j] = nvp->nvram[i];
- i++;
- j++;
- }
- nvram[j] = 0;
- j++;
- }
- while (nvp->nvram[i] != 0)
- i++;
- i++;
- }
- kfree(nvp->nvram);
- nvp->nvram = nvram;
- nvp->nvram_len = j;
- return;
-
-fail:
- kfree(nvram);
- nvp->nvram_len = 0;
-}
-
-/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
- * devices. Strip it down for one device, use domain_nr/bus_nr to determine
- * which data is to be returned. v2 is the version where nvram is stored
- * uncompressed, all relevant valid entries are identified by
- * pcie/domain_nr/bus_nr:
- */
-static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
- u16 bus_nr)
-{
- char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN];
- size_t len;
- u32 i, j;
- u8 *nvram;
-
- nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
- if (!nvram)
- goto fail;
-
- /* Copy all valid entries, release old nvram and assign new one.
- * Valid entries are of type pcie/X/Y/ where X = domain_nr and
- * Y = bus_nr.
- */
- snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr);
- len = strlen(prefix);
- i = 0;
- j = 0;
- while (i < nvp->nvram_len - len) {
- if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
- i += len;
- while (nvp->nvram[i] != 0) {
- nvram[j] = nvp->nvram[i];
- i++;
- j++;
- }
- nvram[j] = 0;
- j++;
- }
- while (nvp->nvram[i] != 0)
- i++;
- i++;
- }
- kfree(nvp->nvram);
- nvp->nvram = nvram;
- nvp->nvram_len = j;
- return;
-fail:
- kfree(nvram);
- nvp->nvram_len = 0;
-}
-
-/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
- * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
- * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
- * End of buffer is completed with token identifying length of buffer.
- */
-static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
- u32 *new_length, u16 domain_nr, u16 bus_nr)
-{
- struct nvram_parser nvp;
- u32 pad;
- u32 token;
- __le32 token_le;
-
- if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
- return NULL;
-
- while (nvp.pos < data_len) {
- nvp.state = nv_parser_states[nvp.state](&nvp);
- if (nvp.state == END)
- break;
- }
- if (nvp.multi_dev_v1)
- brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
- else if (nvp.multi_dev_v2)
- brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
-
- if (nvp.nvram_len == 0) {
- kfree(nvp.nvram);
- return NULL;
- }
-
- pad = nvp.nvram_len;
- *new_length = roundup(nvp.nvram_len + 1, 4);
- while (pad != *new_length) {
- nvp.nvram[pad] = 0;
- pad++;
- }
-
- token = *new_length / 4;
- token = (~token << 16) | (token & 0x0000FFFF);
- token_le = cpu_to_le32(token);
-
- memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
- *new_length += sizeof(token_le);
-
- return nvp.nvram;
-}
-
-void brcmf_fw_nvram_free(void *nvram)
-{
- kfree(nvram);
-}
-
-struct brcmf_fw {
- struct device *dev;
- u16 flags;
- const struct firmware *code;
- const char *nvram_name;
- u16 domain_nr;
- u16 bus_nr;
- void (*done)(struct device *dev, const struct firmware *fw,
- void *nvram_image, u32 nvram_len);
-};
-
-static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
-{
- struct brcmf_fw *fwctx = ctx;
- u32 nvram_length = 0;
- void *nvram = NULL;
- u8 *data = NULL;
- size_t data_len;
- bool raw_nvram;
-
- brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
- if (fw && fw->data) {
- data = (u8 *)fw->data;
- data_len = fw->size;
- raw_nvram = false;
- } else {
- data = bcm47xx_nvram_get_contents(&data_len);
- if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
- goto fail;
- raw_nvram = true;
- }
-
- if (data)
- nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length,
- fwctx->domain_nr, fwctx->bus_nr);
-
- if (raw_nvram)
- bcm47xx_nvram_release_contents(data);
- if (fw)
- release_firmware(fw);
- if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
- goto fail;
-
- fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
- kfree(fwctx);
- return;
-
-fail:
- brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
- release_firmware(fwctx->code);
- device_release_driver(fwctx->dev);
- kfree(fwctx);
-}
-
-static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
-{
- struct brcmf_fw *fwctx = ctx;
- int ret;
-
- brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
- if (!fw)
- goto fail;
-
- /* only requested code so done here */
- if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
- fwctx->done(fwctx->dev, fw, NULL, 0);
- kfree(fwctx);
- return;
- }
- fwctx->code = fw;
- ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
- fwctx->dev, GFP_KERNEL, fwctx,
- brcmf_fw_request_nvram_done);
-
- if (!ret)
- return;
-
- brcmf_fw_request_nvram_done(NULL, fwctx);
- return;
-
-fail:
- brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
- device_release_driver(fwctx->dev);
- kfree(fwctx);
-}
-
-int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
- const char *code, const char *nvram,
- void (*fw_cb)(struct device *dev,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len),
- u16 domain_nr, u16 bus_nr)
-{
- struct brcmf_fw *fwctx;
-
- brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
- if (!fw_cb || !code)
- return -EINVAL;
-
- if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
- return -EINVAL;
-
- fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
- if (!fwctx)
- return -ENOMEM;
-
- fwctx->dev = dev;
- fwctx->flags = flags;
- fwctx->done = fw_cb;
- if (flags & BRCMF_FW_REQUEST_NVRAM)
- fwctx->nvram_name = nvram;
- fwctx->domain_nr = domain_nr;
- fwctx->bus_nr = bus_nr;
-
- return request_firmware_nowait(THIS_MODULE, true, code, dev,
- GFP_KERNEL, fwctx,
- brcmf_fw_request_code_done);
-}
-
-int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
- const char *code, const char *nvram,
- void (*fw_cb)(struct device *dev,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len))
-{
- return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
- 0);
-}
-
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
deleted file mode 100644
index 604dd48ab4e0..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2013 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef BRCMFMAC_FIRMWARE_H
-#define BRCMFMAC_FIRMWARE_H
-
-#define BRCMF_FW_REQUEST 0x000F
-#define BRCMF_FW_REQUEST_NVRAM 0x0001
-#define BRCMF_FW_REQ_FLAGS 0x00F0
-#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
-
-#define BRCMF_FW_PATH_LEN 256
-#define BRCMF_FW_NAME_LEN 32
-
-extern char brcmf_firmware_path[];
-
-void brcmf_fw_nvram_free(void *nvram);
-/*
- * Request firmware(s) asynchronously. When the asynchronous request
- * fails it will not use the callback, but call device_release_driver()
- * instead which will call the driver .remove() callback.
- */
-int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
- const char *code, const char *nvram,
- void (*fw_cb)(struct device *dev,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len),
- u16 domain_nr, u16 bus_nr);
-int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
- const char *code, const char *nvram,
- void (*fw_cb)(struct device *dev,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len));
-
-#endif /* BRCMFMAC_FIRMWARE_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
deleted file mode 100644
index 3878b6f6cfce..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <linux/netdevice.h>
-
-#include "brcmu_wifi.h"
-#include "brcmu_utils.h"
-
-#include "core.h"
-#include "debug.h"
-#include "tracepoint.h"
-#include "fwsignal.h"
-#include "fweh.h"
-#include "fwil.h"
-
-/**
- * struct brcm_ethhdr - broadcom specific ether header.
- *
- * @subtype: subtype for this packet.
- * @length: TODO: length of appended data.
- * @version: version indication.
- * @oui: OUI of this packet.
- * @usr_subtype: subtype for this OUI.
- */
-struct brcm_ethhdr {
- __be16 subtype;
- __be16 length;
- u8 version;
- u8 oui[3];
- __be16 usr_subtype;
-} __packed;
-
-struct brcmf_event_msg_be {
- __be16 version;
- __be16 flags;
- __be32 event_type;
- __be32 status;
- __be32 reason;
- __be32 auth_type;
- __be32 datalen;
- u8 addr[ETH_ALEN];
- char ifname[IFNAMSIZ];
- u8 ifidx;
- u8 bsscfgidx;
-} __packed;
-
-/**
- * struct brcmf_event - contents of broadcom event packet.
- *
- * @eth: standard ether header.
- * @hdr: broadcom specific ether header.
- * @msg: common part of the actual event message.
- */
-struct brcmf_event {
- struct ethhdr eth;
- struct brcm_ethhdr hdr;
- struct brcmf_event_msg_be msg;
-} __packed;
-
-/**
- * struct brcmf_fweh_queue_item - event item on event queue.
- *
- * @q: list element for queuing.
- * @code: event code.
- * @ifidx: interface index related to this event.
- * @ifaddr: ethernet address for interface.
- * @emsg: common parameters of the firmware event message.
- * @data: event specific data part of the firmware event.
- */
-struct brcmf_fweh_queue_item {
- struct list_head q;
- enum brcmf_fweh_event_code code;
- u8 ifidx;
- u8 ifaddr[ETH_ALEN];
- struct brcmf_event_msg_be emsg;
- u8 data[0];
-};
-
-/**
- * struct brcmf_fweh_event_name - code, name mapping entry.
- */
-struct brcmf_fweh_event_name {
- enum brcmf_fweh_event_code code;
- const char *name;
-};
-
-#ifdef DEBUG
-#define BRCMF_ENUM_DEF(id, val) \
- { val, #id },
-
-/* array for mapping code to event name */
-static struct brcmf_fweh_event_name fweh_event_names[] = {
- BRCMF_FWEH_EVENT_ENUM_DEFLIST
-};
-#undef BRCMF_ENUM_DEF
-
-/**
- * brcmf_fweh_event_name() - returns name for given event code.
- *
- * @code: code to lookup.
- */
-static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) {
- if (fweh_event_names[i].code == code)
- return fweh_event_names[i].name;
- }
- return "unknown";
-}
-#else
-static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
-{
- return "nodebug";
-}
-#endif
-
-/**
- * brcmf_fweh_queue_event() - create and queue event.
- *
- * @fweh: firmware event handling info.
- * @event: event queue entry.
- */
-static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
- struct brcmf_fweh_queue_item *event)
-{
- ulong flags;
-
- spin_lock_irqsave(&fweh->evt_q_lock, flags);
- list_add_tail(&event->q, &fweh->event_q);
- spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
- schedule_work(&fweh->event_work);
-}
-
-static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp,
- enum brcmf_fweh_event_code code,
- struct brcmf_event_msg *emsg,
- void *data)
-{
- struct brcmf_fweh_info *fweh;
- int err = -EINVAL;
-
- if (ifp) {
- fweh = &ifp->drvr->fweh;
-
- /* handle the event if valid interface and handler */
- if (fweh->evt_handler[code])
- err = fweh->evt_handler[code](ifp, emsg, data);
- else
- brcmf_err("unhandled event %d ignored\n", code);
- } else {
- brcmf_err("no interface object\n");
- }
- return err;
-}
-
-/**
- * brcmf_fweh_handle_if_event() - handle IF event.
- *
- * @drvr: driver information object.
- * @item: queue entry.
- * @ifpp: interface object (may change upon ADD action).
- */
-static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
- struct brcmf_event_msg *emsg,
- void *data)
-{
- struct brcmf_if_event *ifevent = data;
- struct brcmf_if *ifp;
- bool is_p2pdev;
- int err = 0;
-
- brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n",
- ifevent->action, ifevent->ifidx, ifevent->bssidx,
- ifevent->flags, ifevent->role);
-
- /* The P2P Device interface event must not be ignored contrary to what
- * firmware tells us. Older firmware uses p2p noif, with sta role.
- * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
- * use the same ifevent and should be ignored.
- */
- is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
- (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
- ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
- (drvr->fweh.p2pdev_setup_ongoing))));
- if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
- brcmf_dbg(EVENT, "event can be ignored\n");
- return;
- }
- if (ifevent->ifidx >= BRCMF_MAX_IFS) {
- brcmf_err("invalid interface index: %u\n", ifevent->ifidx);
- return;
- }
-
- ifp = drvr->iflist[ifevent->bssidx];
-
- if (ifevent->action == BRCMF_E_IF_ADD) {
- brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
- emsg->addr);
- ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx,
- is_p2pdev, emsg->ifname, emsg->addr);
- if (IS_ERR(ifp))
- return;
- if (!is_p2pdev)
- brcmf_fws_add_interface(ifp);
- if (!drvr->fweh.evt_handler[BRCMF_E_IF])
- if (brcmf_net_attach(ifp, false) < 0)
- return;
- }
-
- if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
- brcmf_fws_reset_interface(ifp);
-
- err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
-
- if (ifp && ifevent->action == BRCMF_E_IF_DEL)
- brcmf_remove_interface(ifp);
-}
-
-/**
- * brcmf_fweh_dequeue_event() - get event from the queue.
- *
- * @fweh: firmware event handling info.
- */
-static struct brcmf_fweh_queue_item *
-brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh)
-{
- struct brcmf_fweh_queue_item *event = NULL;
- ulong flags;
-
- spin_lock_irqsave(&fweh->evt_q_lock, flags);
- if (!list_empty(&fweh->event_q)) {
- event = list_first_entry(&fweh->event_q,
- struct brcmf_fweh_queue_item, q);
- list_del(&event->q);
- }
- spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
-
- return event;
-}
-
-/**
- * brcmf_fweh_event_worker() - firmware event worker.
- *
- * @work: worker object.
- */
-static void brcmf_fweh_event_worker(struct work_struct *work)
-{
- struct brcmf_pub *drvr;
- struct brcmf_if *ifp;
- struct brcmf_fweh_info *fweh;
- struct brcmf_fweh_queue_item *event;
- int err = 0;
- struct brcmf_event_msg_be *emsg_be;
- struct brcmf_event_msg emsg;
-
- fweh = container_of(work, struct brcmf_fweh_info, event_work);
- drvr = container_of(fweh, struct brcmf_pub, fweh);
-
- while ((event = brcmf_fweh_dequeue_event(fweh))) {
- brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
- brcmf_fweh_event_name(event->code), event->code,
- event->emsg.ifidx, event->emsg.bsscfgidx,
- event->emsg.addr);
-
- /* convert event message */
- emsg_be = &event->emsg;
- emsg.version = be16_to_cpu(emsg_be->version);
- emsg.flags = be16_to_cpu(emsg_be->flags);
- emsg.event_code = event->code;
- emsg.status = be32_to_cpu(emsg_be->status);
- emsg.reason = be32_to_cpu(emsg_be->reason);
- emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
- emsg.datalen = be32_to_cpu(emsg_be->datalen);
- memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);
- memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));
- emsg.ifidx = emsg_be->ifidx;
- emsg.bsscfgidx = emsg_be->bsscfgidx;
-
- brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n",
- emsg.version, emsg.flags, emsg.status, emsg.reason);
- brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
- min_t(u32, emsg.datalen, 64),
- "event payload, len=%d\n", emsg.datalen);
-
- /* special handling of interface event */
- if (event->code == BRCMF_E_IF) {
- brcmf_fweh_handle_if_event(drvr, &emsg, event->data);
- goto event_free;
- }
-
- if (event->code == BRCMF_E_TDLS_PEER_EVENT)
- ifp = drvr->iflist[0];
- else
- ifp = drvr->iflist[emsg.bsscfgidx];
- err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg,
- event->data);
- if (err) {
- brcmf_err("event handler failed (%d)\n",
- event->code);
- err = 0;
- }
-event_free:
- kfree(event);
- }
-}
-
-/**
- * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
- *
- * @ifp: ifp on which setup is taking place or finished.
- * @ongoing: p2p device setup in progress (or not).
- */
-void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
-{
- ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
-}
-
-/**
- * brcmf_fweh_attach() - initialize firmware event handling.
- *
- * @drvr: driver information object.
- */
-void brcmf_fweh_attach(struct brcmf_pub *drvr)
-{
- struct brcmf_fweh_info *fweh = &drvr->fweh;
- INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
- spin_lock_init(&fweh->evt_q_lock);
- INIT_LIST_HEAD(&fweh->event_q);
-}
-
-/**
- * brcmf_fweh_detach() - cleanup firmware event handling.
- *
- * @drvr: driver information object.
- */
-void brcmf_fweh_detach(struct brcmf_pub *drvr)
-{
- struct brcmf_fweh_info *fweh = &drvr->fweh;
- struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
- s8 eventmask[BRCMF_EVENTING_MASK_LEN];
-
- if (ifp) {
- /* clear all events */
- memset(eventmask, 0, BRCMF_EVENTING_MASK_LEN);
- (void)brcmf_fil_iovar_data_set(ifp, "event_msgs",
- eventmask,
- BRCMF_EVENTING_MASK_LEN);
- }
- /* cancel the worker */
- cancel_work_sync(&fweh->event_work);
- WARN_ON(!list_empty(&fweh->event_q));
- memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
-}
-
-/**
- * brcmf_fweh_register() - register handler for given event code.
- *
- * @drvr: driver information object.
- * @code: event code.
- * @handler: handler for the given event code.
- */
-int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
- brcmf_fweh_handler_t handler)
-{
- if (drvr->fweh.evt_handler[code]) {
- brcmf_err("event code %d already registered\n", code);
- return -ENOSPC;
- }
- drvr->fweh.evt_handler[code] = handler;
- brcmf_dbg(TRACE, "event handler registered for %s\n",
- brcmf_fweh_event_name(code));
- return 0;
-}
-
-/**
- * brcmf_fweh_unregister() - remove handler for given code.
- *
- * @drvr: driver information object.
- * @code: event code.
- */
-void brcmf_fweh_unregister(struct brcmf_pub *drvr,
- enum brcmf_fweh_event_code code)
-{
- brcmf_dbg(TRACE, "event handler cleared for %s\n",
- brcmf_fweh_event_name(code));
- drvr->fweh.evt_handler[code] = NULL;
-}
-
-/**
- * brcmf_fweh_activate_events() - enables firmware events registered.
- *
- * @ifp: primary interface object.
- */
-int brcmf_fweh_activate_events(struct brcmf_if *ifp)
-{
- int i, err;
- s8 eventmask[BRCMF_EVENTING_MASK_LEN];
-
- for (i = 0; i < BRCMF_E_LAST; i++) {
- if (ifp->drvr->fweh.evt_handler[i]) {
- brcmf_dbg(EVENT, "enable event %s\n",
- brcmf_fweh_event_name(i));
- setbit(eventmask, i);
- }
- }
-
- /* want to handle IF event as well */
- brcmf_dbg(EVENT, "enable event IF\n");
- setbit(eventmask, BRCMF_E_IF);
-
- err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
- eventmask, BRCMF_EVENTING_MASK_LEN);
- if (err)
- brcmf_err("Set event_msgs error (%d)\n", err);
-
- return err;
-}
-
-/**
- * brcmf_fweh_process_event() - process skb as firmware event.
- *
- * @drvr: driver information object.
- * @event_packet: event packet to process.
- *
- * If the packet buffer contains a firmware event message it will
- * dispatch the event to a registered handler (using worker).
- */
-void brcmf_fweh_process_event(struct brcmf_pub *drvr,
- struct brcmf_event *event_packet)
-{
- enum brcmf_fweh_event_code code;
- struct brcmf_fweh_info *fweh = &drvr->fweh;
- struct brcmf_fweh_queue_item *event;
- gfp_t alloc_flag = GFP_KERNEL;
- void *data;
- u32 datalen;
-
- /* get event info */
- code = get_unaligned_be32(&event_packet->msg.event_type);
- datalen = get_unaligned_be32(&event_packet->msg.datalen);
- data = &event_packet[1];
-
- if (code >= BRCMF_E_LAST)
- return;
-
- if (code != BRCMF_E_IF && !fweh->evt_handler[code])
- return;
-
- if (in_interrupt())
- alloc_flag = GFP_ATOMIC;
-
- event = kzalloc(sizeof(*event) + datalen, alloc_flag);
- if (!event)
- return;
-
- event->code = code;
- event->ifidx = event_packet->msg.ifidx;
-
- /* use memcpy to get aligned event message */
- memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
- memcpy(event->data, data, datalen);
- memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
-
- brcmf_fweh_queue_event(fweh, event);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
deleted file mode 100644
index d9a942842382..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-
-#ifndef FWEH_H_
-#define FWEH_H_
-
-#include <asm/unaligned.h>
-#include <linux/skbuff.h>
-#include <linux/if_ether.h>
-#include <linux/if.h>
-
-/* formward declarations */
-struct brcmf_pub;
-struct brcmf_if;
-struct brcmf_cfg80211_info;
-struct brcmf_event;
-
-/* list of firmware events */
-#define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
- BRCMF_ENUM_DEF(SET_SSID, 0) \
- BRCMF_ENUM_DEF(JOIN, 1) \
- BRCMF_ENUM_DEF(START, 2) \
- BRCMF_ENUM_DEF(AUTH, 3) \
- BRCMF_ENUM_DEF(AUTH_IND, 4) \
- BRCMF_ENUM_DEF(DEAUTH, 5) \
- BRCMF_ENUM_DEF(DEAUTH_IND, 6) \
- BRCMF_ENUM_DEF(ASSOC, 7) \
- BRCMF_ENUM_DEF(ASSOC_IND, 8) \
- BRCMF_ENUM_DEF(REASSOC, 9) \
- BRCMF_ENUM_DEF(REASSOC_IND, 10) \
- BRCMF_ENUM_DEF(DISASSOC, 11) \
- BRCMF_ENUM_DEF(DISASSOC_IND, 12) \
- BRCMF_ENUM_DEF(QUIET_START, 13) \
- BRCMF_ENUM_DEF(QUIET_END, 14) \
- BRCMF_ENUM_DEF(BEACON_RX, 15) \
- BRCMF_ENUM_DEF(LINK, 16) \
- BRCMF_ENUM_DEF(MIC_ERROR, 17) \
- BRCMF_ENUM_DEF(NDIS_LINK, 18) \
- BRCMF_ENUM_DEF(ROAM, 19) \
- BRCMF_ENUM_DEF(TXFAIL, 20) \
- BRCMF_ENUM_DEF(PMKID_CACHE, 21) \
- BRCMF_ENUM_DEF(RETROGRADE_TSF, 22) \
- BRCMF_ENUM_DEF(PRUNE, 23) \
- BRCMF_ENUM_DEF(AUTOAUTH, 24) \
- BRCMF_ENUM_DEF(EAPOL_MSG, 25) \
- BRCMF_ENUM_DEF(SCAN_COMPLETE, 26) \
- BRCMF_ENUM_DEF(ADDTS_IND, 27) \
- BRCMF_ENUM_DEF(DELTS_IND, 28) \
- BRCMF_ENUM_DEF(BCNSENT_IND, 29) \
- BRCMF_ENUM_DEF(BCNRX_MSG, 30) \
- BRCMF_ENUM_DEF(BCNLOST_MSG, 31) \
- BRCMF_ENUM_DEF(ROAM_PREP, 32) \
- BRCMF_ENUM_DEF(PFN_NET_FOUND, 33) \
- BRCMF_ENUM_DEF(PFN_NET_LOST, 34) \
- BRCMF_ENUM_DEF(RESET_COMPLETE, 35) \
- BRCMF_ENUM_DEF(JOIN_START, 36) \
- BRCMF_ENUM_DEF(ROAM_START, 37) \
- BRCMF_ENUM_DEF(ASSOC_START, 38) \
- BRCMF_ENUM_DEF(IBSS_ASSOC, 39) \
- BRCMF_ENUM_DEF(RADIO, 40) \
- BRCMF_ENUM_DEF(PSM_WATCHDOG, 41) \
- BRCMF_ENUM_DEF(PROBREQ_MSG, 44) \
- BRCMF_ENUM_DEF(SCAN_CONFIRM_IND, 45) \
- BRCMF_ENUM_DEF(PSK_SUP, 46) \
- BRCMF_ENUM_DEF(COUNTRY_CODE_CHANGED, 47) \
- BRCMF_ENUM_DEF(EXCEEDED_MEDIUM_TIME, 48) \
- BRCMF_ENUM_DEF(ICV_ERROR, 49) \
- BRCMF_ENUM_DEF(UNICAST_DECODE_ERROR, 50) \
- BRCMF_ENUM_DEF(MULTICAST_DECODE_ERROR, 51) \
- BRCMF_ENUM_DEF(TRACE, 52) \
- BRCMF_ENUM_DEF(IF, 54) \
- BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \
- BRCMF_ENUM_DEF(RSSI, 56) \
- BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \
- BRCMF_ENUM_DEF(ACTION_FRAME, 59) \
- BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \
- BRCMF_ENUM_DEF(PRE_ASSOC_IND, 61) \
- BRCMF_ENUM_DEF(PRE_REASSOC_IND, 62) \
- BRCMF_ENUM_DEF(CHANNEL_ADOPTED, 63) \
- BRCMF_ENUM_DEF(AP_STARTED, 64) \
- BRCMF_ENUM_DEF(DFS_AP_STOP, 65) \
- BRCMF_ENUM_DEF(DFS_AP_RESUME, 66) \
- BRCMF_ENUM_DEF(ESCAN_RESULT, 69) \
- BRCMF_ENUM_DEF(ACTION_FRAME_OFF_CHAN_COMPLETE, 70) \
- BRCMF_ENUM_DEF(PROBERESP_MSG, 71) \
- 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(TDLS_PEER_EVENT, 92) \
- BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
-
-#define BRCMF_ENUM_DEF(id, val) \
- BRCMF_E_##id = (val),
-
-/* firmware event codes sent by the dongle */
-enum brcmf_fweh_event_code {
- BRCMF_FWEH_EVENT_ENUM_DEFLIST
- /* this determines event mask length which must match
- * minimum length check in device firmware so it is
- * hard-coded here.
- */
- BRCMF_E_LAST = 139
-};
-#undef BRCMF_ENUM_DEF
-
-#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8)
-
-/* flags field values in struct brcmf_event_msg */
-#define BRCMF_EVENT_MSG_LINK 0x01
-#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
-#define BRCMF_EVENT_MSG_GROUP 0x04
-
-/* status field values in struct brcmf_event_msg */
-#define BRCMF_E_STATUS_SUCCESS 0
-#define BRCMF_E_STATUS_FAIL 1
-#define BRCMF_E_STATUS_TIMEOUT 2
-#define BRCMF_E_STATUS_NO_NETWORKS 3
-#define BRCMF_E_STATUS_ABORT 4
-#define BRCMF_E_STATUS_NO_ACK 5
-#define BRCMF_E_STATUS_UNSOLICITED 6
-#define BRCMF_E_STATUS_ATTEMPT 7
-#define BRCMF_E_STATUS_PARTIAL 8
-#define BRCMF_E_STATUS_NEWSCAN 9
-#define BRCMF_E_STATUS_NEWASSOC 10
-#define BRCMF_E_STATUS_11HQUIET 11
-#define BRCMF_E_STATUS_SUPPRESS 12
-#define BRCMF_E_STATUS_NOCHANS 13
-#define BRCMF_E_STATUS_CS_ABORT 15
-#define BRCMF_E_STATUS_ERROR 16
-
-/* reason field values in struct brcmf_event_msg */
-#define BRCMF_E_REASON_INITIAL_ASSOC 0
-#define BRCMF_E_REASON_LOW_RSSI 1
-#define BRCMF_E_REASON_DEAUTH 2
-#define BRCMF_E_REASON_DISASSOC 3
-#define BRCMF_E_REASON_BCNS_LOST 4
-#define BRCMF_E_REASON_MINTXRATE 9
-#define BRCMF_E_REASON_TXFAIL 10
-
-#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4
-#define BRCMF_E_REASON_FAST_ROAM_FAILED 5
-#define BRCMF_E_REASON_DIRECTED_ROAM 6
-#define BRCMF_E_REASON_TSPEC_REJECTED 7
-#define BRCMF_E_REASON_BETTER_AP 8
-
-#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED 0
-#define BRCMF_E_REASON_TDLS_PEER_CONNECTED 1
-#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED 2
-
-/* action field values for brcmf_ifevent */
-#define BRCMF_E_IF_ADD 1
-#define BRCMF_E_IF_DEL 2
-#define BRCMF_E_IF_CHANGE 3
-
-/* flag field values for brcmf_ifevent */
-#define BRCMF_E_IF_FLAG_NOIF 1
-
-/* role field values for brcmf_ifevent */
-#define BRCMF_E_IF_ROLE_STA 0
-#define BRCMF_E_IF_ROLE_AP 1
-#define BRCMF_E_IF_ROLE_WDS 2
-#define BRCMF_E_IF_ROLE_P2P_GO 3
-#define BRCMF_E_IF_ROLE_P2P_CLIENT 4
-
-/**
- * definitions for event packet validation.
- */
-#define BRCMF_EVENT_OUI_OFFSET 19
-#define BRCM_OUI "\x00\x10\x18"
-#define DOT11_OUI_LEN 3
-#define BCMILCP_BCM_SUBTYPE_EVENT 1
-
-
-/**
- * struct brcmf_event_msg - firmware event message.
- *
- * @version: version information.
- * @flags: event flags.
- * @event_code: firmware event code.
- * @status: status information.
- * @reason: reason code.
- * @auth_type: authentication type.
- * @datalen: lenght of event data buffer.
- * @addr: ether address.
- * @ifname: interface name.
- * @ifidx: interface index.
- * @bsscfgidx: bsscfg index.
- */
-struct brcmf_event_msg {
- u16 version;
- u16 flags;
- u32 event_code;
- u32 status;
- u32 reason;
- s32 auth_type;
- u32 datalen;
- u8 addr[ETH_ALEN];
- char ifname[IFNAMSIZ];
- u8 ifidx;
- u8 bsscfgidx;
-};
-
-struct brcmf_if_event {
- u8 ifidx;
- u8 action;
- u8 flags;
- u8 bssidx;
- u8 role;
-};
-
-typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
- const struct brcmf_event_msg *evtmsg,
- void *data);
-
-/**
- * struct brcmf_fweh_info - firmware event handling information.
- *
- * @p2pdev_setup_ongoing: P2P device creation in progress.
- * @event_work: event worker.
- * @evt_q_lock: lock for event queue protection.
- * @event_q: event queue.
- * @evt_handler: registered event handlers.
- */
-struct brcmf_fweh_info {
- bool p2pdev_setup_ongoing;
- struct work_struct event_work;
- spinlock_t evt_q_lock;
- struct list_head event_q;
- int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp,
- const struct brcmf_event_msg *evtmsg,
- void *data);
-};
-
-void brcmf_fweh_attach(struct brcmf_pub *drvr);
-void brcmf_fweh_detach(struct brcmf_pub *drvr);
-int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
- int (*handler)(struct brcmf_if *ifp,
- const struct brcmf_event_msg *evtmsg,
- void *data));
-void brcmf_fweh_unregister(struct brcmf_pub *drvr,
- enum brcmf_fweh_event_code code);
-int brcmf_fweh_activate_events(struct brcmf_if *ifp);
-void brcmf_fweh_process_event(struct brcmf_pub *drvr,
- struct brcmf_event *event_packet);
-void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
-
-static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
- struct sk_buff *skb)
-{
- struct brcmf_event *event_packet;
- u8 *data;
- u16 usr_stype;
-
- /* only process events when protocol matches */
- if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
- return;
-
- /* check for BRCM oui match */
- event_packet = (struct brcmf_event *)skb_mac_header(skb);
- data = (u8 *)event_packet;
- data += BRCMF_EVENT_OUI_OFFSET;
- if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN))
- return;
-
- /* final match on usr_subtype */
- data += DOT11_OUI_LEN;
- usr_stype = get_unaligned_be16(data);
- if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT)
- return;
-
- brcmf_fweh_process_event(drvr, event_packet);
-}
-
-#endif /* FWEH_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
deleted file mode 100644
index dcfa0bb149ce..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/* FWIL is the Firmware Interface Layer. In this module the support functions
- * are located to set and get variables to and from the firmware.
- */
-
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-#include "core.h"
-#include "bus.h"
-#include "debug.h"
-#include "tracepoint.h"
-#include "fwil.h"
-#include "proto.h"
-
-
-#define MAX_HEX_DUMP_LEN 64
-
-#ifdef DEBUG
-static const char * const brcmf_fil_errstr[] = {
- "BCME_OK",
- "BCME_ERROR",
- "BCME_BADARG",
- "BCME_BADOPTION",
- "BCME_NOTUP",
- "BCME_NOTDOWN",
- "BCME_NOTAP",
- "BCME_NOTSTA",
- "BCME_BADKEYIDX",
- "BCME_RADIOOFF",
- "BCME_NOTBANDLOCKED",
- "BCME_NOCLK",
- "BCME_BADRATESET",
- "BCME_BADBAND",
- "BCME_BUFTOOSHORT",
- "BCME_BUFTOOLONG",
- "BCME_BUSY",
- "BCME_NOTASSOCIATED",
- "BCME_BADSSIDLEN",
- "BCME_OUTOFRANGECHAN",
- "BCME_BADCHAN",
- "BCME_BADADDR",
- "BCME_NORESOURCE",
- "BCME_UNSUPPORTED",
- "BCME_BADLEN",
- "BCME_NOTREADY",
- "BCME_EPERM",
- "BCME_NOMEM",
- "BCME_ASSOCIATED",
- "BCME_RANGE",
- "BCME_NOTFOUND",
- "BCME_WME_NOT_ENABLED",
- "BCME_TSPEC_NOTFOUND",
- "BCME_ACM_NOTSUPPORTED",
- "BCME_NOT_WME_ASSOCIATION",
- "BCME_SDIO_ERROR",
- "BCME_DONGLE_DOWN",
- "BCME_VERSION",
- "BCME_TXFAIL",
- "BCME_RXFAIL",
- "BCME_NODEVICE",
- "BCME_NMODE_DISABLED",
- "BCME_NONRESIDENT",
- "BCME_SCANREJECT",
- "BCME_USAGE_ERROR",
- "BCME_IOCTL_ERROR",
- "BCME_SERIAL_PORT_ERR",
- "BCME_DISABLED",
- "BCME_DECERR",
- "BCME_ENCERR",
- "BCME_MICERR",
- "BCME_REPLAY",
- "BCME_IE_NOTFOUND",
-};
-
-static const char *brcmf_fil_get_errstr(u32 err)
-{
- if (err >= ARRAY_SIZE(brcmf_fil_errstr))
- return "(unknown)";
-
- return brcmf_fil_errstr[err];
-}
-#else
-static const char *brcmf_fil_get_errstr(u32 err)
-{
- return "";
-}
-#endif /* DEBUG */
-
-static s32
-brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- s32 err;
-
- if (drvr->bus_if->state != BRCMF_BUS_UP) {
- brcmf_err("bus is down. we have nothing to do.\n");
- return -EIO;
- }
-
- if (data != NULL)
- len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
- if (set)
- err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len);
- else
- err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len);
-
- if (err >= 0)
- return 0;
-
- brcmf_dbg(FIL, "Failed: %s (%d)\n",
- brcmf_fil_get_errstr((u32)(-err)), err);
- return -EBADE;
-}
-
-s32
-brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
-{
- s32 err;
-
- mutex_lock(&ifp->drvr->proto_block);
-
- brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
- brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
- min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
-
- err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
- mutex_unlock(&ifp->drvr->proto_block);
-
- return err;
-}
-
-s32
-brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
-{
- s32 err;
-
- mutex_lock(&ifp->drvr->proto_block);
- err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
-
- brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
- brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
- min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
-
- mutex_unlock(&ifp->drvr->proto_block);
-
- return err;
-}
-
-
-s32
-brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data)
-{
- s32 err;
- __le32 data_le = cpu_to_le32(data);
-
- mutex_lock(&ifp->drvr->proto_block);
- brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
- err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
- mutex_unlock(&ifp->drvr->proto_block);
-
- return err;
-}
-
-s32
-brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
-{
- s32 err;
- __le32 data_le = cpu_to_le32(*data);
-
- mutex_lock(&ifp->drvr->proto_block);
- err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
- mutex_unlock(&ifp->drvr->proto_block);
- *data = le32_to_cpu(data_le);
- brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
-
- return err;
-}
-
-static u32
-brcmf_create_iovar(char *name, const char *data, u32 datalen,
- char *buf, u32 buflen)
-{
- u32 len;
-
- len = strlen(name) + 1;
-
- if ((len + datalen) > buflen)
- return 0;
-
- memcpy(buf, name, len);
-
- /* append data onto the end of the name string */
- if (data && datalen)
- memcpy(&buf[len], data, datalen);
-
- return len + datalen;
-}
-
-
-s32
-brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
- u32 len)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- s32 err;
- u32 buflen;
-
- mutex_lock(&drvr->proto_block);
-
- brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
- brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
- min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
-
- buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
- sizeof(drvr->proto_buf));
- if (buflen) {
- err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
- buflen, true);
- } else {
- err = -EPERM;
- brcmf_err("Creating iovar failed\n");
- }
-
- mutex_unlock(&drvr->proto_block);
- return err;
-}
-
-s32
-brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
- u32 len)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- s32 err;
- u32 buflen;
-
- mutex_lock(&drvr->proto_block);
-
- buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
- sizeof(drvr->proto_buf));
- if (buflen) {
- err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
- buflen, false);
- if (err == 0)
- memcpy(data, drvr->proto_buf, len);
- } else {
- err = -EPERM;
- brcmf_err("Creating iovar failed\n");
- }
-
- brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
- brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
- min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
-
- mutex_unlock(&drvr->proto_block);
- return err;
-}
-
-s32
-brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data)
-{
- __le32 data_le = cpu_to_le32(data);
-
- return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le));
-}
-
-s32
-brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data)
-{
- __le32 data_le = cpu_to_le32(*data);
- s32 err;
-
- err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le));
- if (err == 0)
- *data = le32_to_cpu(data_le);
- return err;
-}
-
-static u32
-brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf,
- u32 buflen)
-{
- const s8 *prefix = "bsscfg:";
- s8 *p;
- u32 prefixlen;
- u32 namelen;
- u32 iolen;
- __le32 bssidx_le;
-
- if (bssidx == 0)
- return brcmf_create_iovar(name, data, datalen, buf, buflen);
-
- prefixlen = strlen(prefix);
- namelen = strlen(name) + 1; /* lengh of iovar name + null */
- iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen;
-
- if (buflen < iolen) {
- brcmf_err("buffer is too short\n");
- return 0;
- }
-
- p = buf;
-
- /* copy prefix, no null */
- memcpy(p, prefix, prefixlen);
- p += prefixlen;
-
- /* copy iovar name including null */
- memcpy(p, name, namelen);
- p += namelen;
-
- /* bss config index as first data */
- bssidx_le = cpu_to_le32(bssidx);
- memcpy(p, &bssidx_le, sizeof(bssidx_le));
- p += sizeof(bssidx_le);
-
- /* parameter buffer follows */
- if (datalen)
- memcpy(p, data, datalen);
-
- return iolen;
-}
-
-s32
-brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
- void *data, u32 len)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- s32 err;
- u32 buflen;
-
- mutex_lock(&drvr->proto_block);
-
- brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
- ifp->bssidx, name, len);
- brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
- min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
-
- buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len,
- drvr->proto_buf, sizeof(drvr->proto_buf));
- if (buflen) {
- err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
- buflen, true);
- } else {
- err = -EPERM;
- brcmf_err("Creating bsscfg failed\n");
- }
-
- mutex_unlock(&drvr->proto_block);
- return err;
-}
-
-s32
-brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
- void *data, u32 len)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- s32 err;
- u32 buflen;
-
- mutex_lock(&drvr->proto_block);
-
- buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len,
- drvr->proto_buf, sizeof(drvr->proto_buf));
- if (buflen) {
- err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
- buflen, false);
- if (err == 0)
- memcpy(data, drvr->proto_buf, len);
- } else {
- err = -EPERM;
- brcmf_err("Creating bsscfg failed\n");
- }
- brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
- ifp->bssidx, name, len);
- brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
- min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
-
- mutex_unlock(&drvr->proto_block);
- return err;
-
-}
-
-s32
-brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data)
-{
- __le32 data_le = cpu_to_le32(data);
-
- return brcmf_fil_bsscfg_data_set(ifp, name, &data_le,
- sizeof(data_le));
-}
-
-s32
-brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data)
-{
- __le32 data_le = cpu_to_le32(*data);
- s32 err;
-
- err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le,
- sizeof(data_le));
- if (err == 0)
- *data = le32_to_cpu(data_le);
- return err;
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
deleted file mode 100644
index b20fc0f82a48..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _fwil_h_
-#define _fwil_h_
-
-/*******************************************************************************
- * Dongle command codes that are interpreted by firmware
- ******************************************************************************/
-#define BRCMF_C_GET_VERSION 1
-#define BRCMF_C_UP 2
-#define BRCMF_C_DOWN 3
-#define BRCMF_C_SET_PROMISC 10
-#define BRCMF_C_GET_RATE 12
-#define BRCMF_C_GET_INFRA 19
-#define BRCMF_C_SET_INFRA 20
-#define BRCMF_C_GET_AUTH 21
-#define BRCMF_C_SET_AUTH 22
-#define BRCMF_C_GET_BSSID 23
-#define BRCMF_C_GET_SSID 25
-#define BRCMF_C_SET_SSID 26
-#define BRCMF_C_TERMINATED 28
-#define BRCMF_C_GET_CHANNEL 29
-#define BRCMF_C_SET_CHANNEL 30
-#define BRCMF_C_GET_SRL 31
-#define BRCMF_C_SET_SRL 32
-#define BRCMF_C_GET_LRL 33
-#define BRCMF_C_SET_LRL 34
-#define BRCMF_C_GET_RADIO 37
-#define BRCMF_C_SET_RADIO 38
-#define BRCMF_C_GET_PHYTYPE 39
-#define BRCMF_C_SET_KEY 45
-#define BRCMF_C_GET_REGULATORY 46
-#define BRCMF_C_SET_REGULATORY 47
-#define BRCMF_C_SET_PASSIVE_SCAN 49
-#define BRCMF_C_SCAN 50
-#define BRCMF_C_SCAN_RESULTS 51
-#define BRCMF_C_DISASSOC 52
-#define BRCMF_C_REASSOC 53
-#define BRCMF_C_SET_ROAM_TRIGGER 55
-#define BRCMF_C_SET_ROAM_DELTA 57
-#define BRCMF_C_GET_BCNPRD 75
-#define BRCMF_C_SET_BCNPRD 76
-#define BRCMF_C_GET_DTIMPRD 77
-#define BRCMF_C_SET_DTIMPRD 78
-#define BRCMF_C_SET_COUNTRY 84
-#define BRCMF_C_GET_PM 85
-#define BRCMF_C_SET_PM 86
-#define BRCMF_C_GET_REVINFO 98
-#define BRCMF_C_GET_CURR_RATESET 114
-#define BRCMF_C_GET_AP 117
-#define BRCMF_C_SET_AP 118
-#define BRCMF_C_SET_SCB_AUTHORIZE 121
-#define BRCMF_C_SET_SCB_DEAUTHORIZE 122
-#define BRCMF_C_GET_RSSI 127
-#define BRCMF_C_GET_WSEC 133
-#define BRCMF_C_SET_WSEC 134
-#define BRCMF_C_GET_PHY_NOISE 135
-#define BRCMF_C_GET_BSS_INFO 136
-#define BRCMF_C_GET_BANDLIST 140
-#define BRCMF_C_SET_SCB_TIMEOUT 158
-#define BRCMF_C_GET_ASSOCLIST 159
-#define BRCMF_C_GET_PHYLIST 180
-#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185
-#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187
-#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201
-#define BRCMF_C_GET_VALID_CHANNELS 217
-#define BRCMF_C_GET_KEY_PRIMARY 235
-#define BRCMF_C_SET_KEY_PRIMARY 236
-#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258
-#define BRCMF_C_GET_VAR 262
-#define BRCMF_C_SET_VAR 263
-
-s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
-s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
-s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data);
-s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data);
-
-s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
- u32 len);
-s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
- u32 len);
-s32 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data);
-s32 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data);
-
-s32 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, void *data,
- u32 len);
-s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data,
- u32 len);
-s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data);
-s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data);
-
-#endif /* _fwil_h_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
deleted file mode 100644
index daa427b46712..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+++ /dev/null
@@ -1,637 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-
-#ifndef FWIL_TYPES_H_
-#define FWIL_TYPES_H_
-
-#include <linux/if_ether.h>
-
-
-#define BRCMF_FIL_ACTION_FRAME_SIZE 1800
-
-/* ARP Offload feature flags for arp_ol iovar */
-#define BRCMF_ARP_OL_AGENT 0x00000001
-#define BRCMF_ARP_OL_SNOOP 0x00000002
-#define BRCMF_ARP_OL_HOST_AUTO_REPLY 0x00000004
-#define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008
-
-#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
-#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002
-
-#define BRCMF_STA_WME 0x00000002 /* WMM association */
-#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */
-#define BRCMF_STA_ASSOC 0x00000010 /* Associated */
-#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */
-#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */
-
-/* size of brcmf_scan_params not including variable length array */
-#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
-
-/* masks for channel and ssid count */
-#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff
-#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16
-
-/* primary (ie tx) key */
-#define BRCMF_PRIMARY_KEY (1 << 1)
-#define DOT11_BSSTYPE_ANY 2
-#define BRCMF_ESCAN_REQ_VERSION 1
-
-#define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */
-
-/* OBSS Coex Auto/On/Off */
-#define BRCMF_OBSS_COEX_AUTO (-1)
-#define BRCMF_OBSS_COEX_OFF 0
-#define BRCMF_OBSS_COEX_ON 1
-
-/* WOWL bits */
-/* Wakeup on Magic packet: */
-#define BRCMF_WOWL_MAGIC (1 << 0)
-/* Wakeup on Netpattern */
-#define BRCMF_WOWL_NET (1 << 1)
-/* Wakeup on loss-of-link due to Disassoc/Deauth: */
-#define BRCMF_WOWL_DIS (1 << 2)
-/* Wakeup on retrograde TSF: */
-#define BRCMF_WOWL_RETR (1 << 3)
-/* Wakeup on loss of beacon: */
-#define BRCMF_WOWL_BCN (1 << 4)
-/* Wakeup after test: */
-#define BRCMF_WOWL_TST (1 << 5)
-/* Wakeup after PTK refresh: */
-#define BRCMF_WOWL_M1 (1 << 6)
-/* Wakeup after receipt of EAP-Identity Req: */
-#define BRCMF_WOWL_EAPID (1 << 7)
-/* Wakeind via PME(0) or GPIO(1): */
-#define BRCMF_WOWL_PME_GPIO (1 << 8)
-/* need tkip phase 1 key to be updated by the driver: */
-#define BRCMF_WOWL_NEEDTKIP1 (1 << 9)
-/* enable wakeup if GTK fails: */
-#define BRCMF_WOWL_GTK_FAILURE (1 << 10)
-/* support extended magic packets: */
-#define BRCMF_WOWL_EXTMAGPAT (1 << 11)
-/* support ARP/NS/keepalive offloading: */
-#define BRCMF_WOWL_ARPOFFLOAD (1 << 12)
-/* read protocol version for EAPOL frames: */
-#define BRCMF_WOWL_WPA2 (1 << 13)
-/* If the bit is set, use key rotaton: */
-#define BRCMF_WOWL_KEYROT (1 << 14)
-/* If the bit is set, frm received was bcast frame: */
-#define BRCMF_WOWL_BCAST (1 << 15)
-/* If the bit is set, scan offload is enabled: */
-#define BRCMF_WOWL_SCANOL (1 << 16)
-/* Wakeup on tcpkeep alive timeout: */
-#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17)
-/* Wakeup on mDNS Conflict Resolution: */
-#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18)
-/* Wakeup on mDNS Service Connect: */
-#define BRCMF_WOWL_MDNS_SERVICE (1 << 19)
-/* tcp keepalive got data: */
-#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20)
-/* Firmware died in wowl mode: */
-#define BRCMF_WOWL_FW_HALT (1 << 21)
-/* Enable detection of radio button changes: */
-#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22)
-/* Offloads detected MIC failure(s): */
-#define BRCMF_WOWL_MIC_FAIL (1 << 23)
-/* Wakeup in Unassociated state (Net/Magic Pattern): */
-#define BRCMF_WOWL_UNASSOC (1 << 24)
-/* Wakeup if received matched secured pattern: */
-#define BRCMF_WOWL_SECURE (1 << 25)
-/* Link Down indication in WoWL mode: */
-#define BRCMF_WOWL_LINKDOWN (1 << 31)
-
-#define BRCMF_WOWL_MAXPATTERNS 8
-#define BRCMF_WOWL_MAXPATTERNSIZE 128
-
-#define BRCMF_COUNTRY_BUF_SZ 4
-#define BRCMF_ANT_MAX 4
-
-#define BRCMF_MAX_ASSOCLIST 128
-
-/* join preference types for join_pref iovar */
-enum brcmf_join_pref_types {
- BRCMF_JOIN_PREF_RSSI = 1,
- BRCMF_JOIN_PREF_WPA,
- BRCMF_JOIN_PREF_BAND,
- BRCMF_JOIN_PREF_RSSI_DELTA,
-};
-
-enum brcmf_fil_p2p_if_types {
- BRCMF_FIL_P2P_IF_CLIENT,
- BRCMF_FIL_P2P_IF_GO,
- BRCMF_FIL_P2P_IF_DYNBCN_GO,
- BRCMF_FIL_P2P_IF_DEV,
-};
-
-enum brcmf_wowl_pattern_type {
- BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0,
- BRCMF_WOWL_PATTERN_TYPE_ARP,
- BRCMF_WOWL_PATTERN_TYPE_NA
-};
-
-struct brcmf_fil_p2p_if_le {
- u8 addr[ETH_ALEN];
- __le16 type;
- __le16 chspec;
-};
-
-struct brcmf_fil_chan_info_le {
- __le32 hw_channel;
- __le32 target_channel;
- __le32 scan_channel;
-};
-
-struct brcmf_fil_action_frame_le {
- u8 da[ETH_ALEN];
- __le16 len;
- __le32 packet_id;
- u8 data[BRCMF_FIL_ACTION_FRAME_SIZE];
-};
-
-struct brcmf_fil_af_params_le {
- __le32 channel;
- __le32 dwell_time;
- u8 bssid[ETH_ALEN];
- u8 pad[2];
- struct brcmf_fil_action_frame_le action_frame;
-};
-
-struct brcmf_fil_bss_enable_le {
- __le32 bsscfg_idx;
- __le32 enable;
-};
-
-struct brcmf_fil_bwcap_le {
- __le32 band;
- __le32 bw_cap;
-};
-
-/**
- * struct tdls_iovar - common structure for tdls iovars.
- *
- * @ea: ether address of peer station.
- * @mode: mode value depending on specific tdls iovar.
- * @chanspec: channel specification.
- * @pad: unused (for future use).
- */
-struct brcmf_tdls_iovar_le {
- u8 ea[ETH_ALEN]; /* Station address */
- u8 mode; /* mode: depends on iovar */
- __le16 chanspec;
- __le32 pad; /* future */
-};
-
-enum brcmf_tdls_manual_ep_ops {
- BRCMF_TDLS_MANUAL_EP_CREATE = 1,
- BRCMF_TDLS_MANUAL_EP_DELETE = 3,
- BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6
-};
-
-/* Pattern matching filter. Specifies an offset within received packets to
- * start matching, the pattern to match, the size of the pattern, and a bitmask
- * that indicates which bits within the pattern should be matched.
- */
-struct brcmf_pkt_filter_pattern_le {
- /*
- * Offset within received packet to start pattern matching.
- * Offset '0' is the first byte of the ethernet header.
- */
- __le32 offset;
- /* Size of the pattern. Bitmask must be the same size.*/
- __le32 size_bytes;
- /*
- * Variable length mask and pattern data. mask starts at offset 0.
- * Pattern immediately follows mask.
- */
- u8 mask_and_pattern[1];
-};
-
-/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */
-struct brcmf_pkt_filter_le {
- __le32 id; /* Unique filter id, specified by app. */
- __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */
- __le32 negate_match; /* Negate the result of filter matches */
- union { /* Filter definitions */
- struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */
- } u;
-};
-
-/* IOVAR "pkt_filter_enable" parameter. */
-struct brcmf_pkt_filter_enable_le {
- __le32 id; /* Unique filter id */
- __le32 enable; /* Enable/disable bool */
-};
-
-/* BSS info structure
- * Applications MUST CHECK ie_offset field and length field to access IEs and
- * next bss_info structure in a vector (in struct brcmf_scan_results)
- */
-struct brcmf_bss_info_le {
- __le32 version; /* version field */
- __le32 length; /* byte length of data in this record,
- * starting at version and including IEs
- */
- u8 BSSID[ETH_ALEN];
- __le16 beacon_period; /* units are Kusec */
- __le16 capability; /* Capability information */
- u8 SSID_len;
- u8 SSID[32];
- struct {
- __le32 count; /* # rates in this set */
- u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */
- } rateset; /* supported rates */
- __le16 chanspec; /* chanspec for bss */
- __le16 atim_window; /* units are Kusec */
- u8 dtim_period; /* DTIM period */
- __le16 RSSI; /* receive signal strength (in dBm) */
- s8 phy_noise; /* noise (in dBm) */
-
- u8 n_cap; /* BSS is 802.11N Capable */
- /* 802.11N BSS Capabilities (based on HT_CAP_*): */
- __le32 nbss_cap;
- u8 ctl_ch; /* 802.11N BSS control channel number */
- __le32 reserved32[1]; /* Reserved for expansion of BSS properties */
- u8 flags; /* flags */
- u8 reserved[3]; /* Reserved for expansion of BSS properties */
- u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */
-
- __le16 ie_offset; /* offset at which IEs start, from beginning */
- __le32 ie_length; /* byte length of Information Elements */
- __le16 SNR; /* average SNR of during frame reception */
- /* Add new fields here */
- /* variable length Information Elements */
-};
-
-struct brcm_rateset_le {
- /* # rates in this set */
- __le32 count;
- /* rates in 500kbps units w/hi bit set if basic */
- u8 rates[BRCMF_MAXRATES_IN_SET];
-};
-
-struct brcmf_ssid {
- u32 SSID_len;
- unsigned char SSID[32];
-};
-
-struct brcmf_ssid_le {
- __le32 SSID_len;
- unsigned char SSID[32];
-};
-
-struct brcmf_scan_params_le {
- struct brcmf_ssid_le ssid_le; /* default: {0, ""} */
- u8 bssid[ETH_ALEN]; /* default: bcast */
- s8 bss_type; /* default: any,
- * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT
- */
- u8 scan_type; /* flags, 0 use default */
- __le32 nprobes; /* -1 use default, number of probes per channel */
- __le32 active_time; /* -1 use default, dwell time per channel for
- * active scanning
- */
- __le32 passive_time; /* -1 use default, dwell time per channel
- * for passive scanning
- */
- __le32 home_time; /* -1 use default, dwell time for the
- * home channel between channel scans
- */
- __le32 channel_num; /* count of channels and ssids that follow
- *
- * low half is count of channels in
- * channel_list, 0 means default (use all
- * available channels)
- *
- * high half is entries in struct brcmf_ssid
- * array that follows channel_list, aligned for
- * s32 (4 bytes) meaning an odd channel count
- * implies a 2-byte pad between end of
- * channel_list and first ssid
- *
- * if ssid count is zero, single ssid in the
- * fixed parameter portion is assumed, otherwise
- * ssid in the fixed portion is ignored
- */
- __le16 channel_list[1]; /* list of chanspecs */
-};
-
-struct brcmf_scan_results {
- u32 buflen;
- u32 version;
- u32 count;
- struct brcmf_bss_info_le bss_info_le[];
-};
-
-struct brcmf_escan_params_le {
- __le32 version;
- __le16 action;
- __le16 sync_id;
- struct brcmf_scan_params_le params_le;
-};
-
-struct brcmf_escan_result_le {
- __le32 buflen;
- __le32 version;
- __le16 sync_id;
- __le16 bss_count;
- struct brcmf_bss_info_le bss_info_le;
-};
-
-#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \
- sizeof(struct brcmf_bss_info_le))
-
-/* used for association with a specific BSSID and chanspec list */
-struct brcmf_assoc_params_le {
- /* 00:00:00:00:00:00: broadcast scan */
- u8 bssid[ETH_ALEN];
- /* 0: all available channels, otherwise count of chanspecs in
- * chanspec_list */
- __le32 chanspec_num;
- /* list of chanspecs */
- __le16 chanspec_list[1];
-};
-
-/**
- * struct join_pref params - parameters for preferred join selection.
- *
- * @type: preference type (see enum brcmf_join_pref_types).
- * @len: length of bytes following (currently always 2).
- * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA).
- * @band: band to which selection preference applies.
- * This is used if @type is BAND or RSSI_DELTA.
- */
-struct brcmf_join_pref_params {
- u8 type;
- u8 len;
- u8 rssi_gain;
- u8 band;
-};
-
-/* used for join with or without a specific bssid and channel list */
-struct brcmf_join_params {
- struct brcmf_ssid_le ssid_le;
- struct brcmf_assoc_params_le params_le;
-};
-
-/* scan params for extended join */
-struct brcmf_join_scan_params_le {
- u8 scan_type; /* 0 use default, active or passive scan */
- __le32 nprobes; /* -1 use default, nr of probes per channel */
- __le32 active_time; /* -1 use default, dwell time per channel for
- * active scanning
- */
- __le32 passive_time; /* -1 use default, dwell time per channel
- * for passive scanning
- */
- __le32 home_time; /* -1 use default, dwell time for the home
- * channel between channel scans
- */
-};
-
-/* extended join params */
-struct brcmf_ext_join_params_le {
- struct brcmf_ssid_le ssid_le; /* {0, ""}: wildcard scan */
- struct brcmf_join_scan_params_le scan_le;
- struct brcmf_assoc_params_le assoc_le;
-};
-
-struct brcmf_wsec_key {
- u32 index; /* key index */
- u32 len; /* key length */
- u8 data[WLAN_MAX_KEY_LEN]; /* key data */
- u32 pad_1[18];
- u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
- u32 flags; /* misc flags */
- u32 pad_2[3];
- u32 iv_initialized; /* has IV been initialized already? */
- u32 pad_3;
- /* Rx IV */
- struct {
- u32 hi; /* upper 32 bits of IV */
- u16 lo; /* lower 16 bits of IV */
- } rxiv;
- u32 pad_4[2];
- u8 ea[ETH_ALEN]; /* per station */
-};
-
-/*
- * dongle requires same struct as above but with fields in little endian order
- */
-struct brcmf_wsec_key_le {
- __le32 index; /* key index */
- __le32 len; /* key length */
- u8 data[WLAN_MAX_KEY_LEN]; /* key data */
- __le32 pad_1[18];
- __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
- __le32 flags; /* misc flags */
- __le32 pad_2[3];
- __le32 iv_initialized; /* has IV been initialized already? */
- __le32 pad_3;
- /* Rx IV */
- struct {
- __le32 hi; /* upper 32 bits of IV */
- __le16 lo; /* lower 16 bits of IV */
- } rxiv;
- __le32 pad_4[2];
- u8 ea[ETH_ALEN]; /* per station */
-};
-
-/* Used to get specific STA parameters */
-struct brcmf_scb_val_le {
- __le32 val;
- u8 ea[ETH_ALEN];
-};
-
-/* channel encoding */
-struct brcmf_channel_info_le {
- __le32 hw_channel;
- __le32 target_channel;
- __le32 scan_channel;
-};
-
-struct brcmf_sta_info_le {
- __le16 ver; /* version of this struct */
- __le16 len; /* length in bytes of this structure */
- __le16 cap; /* sta's advertised capabilities */
- __le32 flags; /* flags defined below */
- __le32 idle; /* time since data pkt rx'd from sta */
- u8 ea[ETH_ALEN]; /* Station address */
- __le32 count; /* # rates in this set */
- u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
- /* w/hi bit set if basic */
- __le32 in; /* seconds elapsed since associated */
- __le32 listen_interval_inms; /* Min Listen interval in ms for STA */
- __le32 tx_pkts; /* # of packets transmitted */
- __le32 tx_failures; /* # of packets failed */
- __le32 rx_ucast_pkts; /* # of unicast packets received */
- __le32 rx_mcast_pkts; /* # of multicast packets received */
- __le32 tx_rate; /* Rate of last successful tx frame */
- __le32 rx_rate; /* Rate of last successful rx frame */
- __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
- __le32 rx_decrypt_failures; /* # of packet decrypted failed */
- __le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */
- __le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */
- __le32 tx_mcast_pkts; /* # of mcast pkts txed */
- __le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */
- __le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */
- __le64 tx_ucast_bytes; /* data bytes txed (ucast) */
- __le64 tx_mcast_bytes; /* # data bytes txed (mcast) */
- __le64 rx_ucast_bytes; /* data bytes recvd (ucast) */
- __le64 rx_mcast_bytes; /* data bytes recvd (mcast) */
- s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */
- s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */
- __le16 aid; /* association ID */
- __le16 ht_capabilities; /* advertised ht caps */
- __le16 vht_flags; /* converted vht flags */
- __le32 tx_pkts_retry_cnt; /* # of frames where a retry was
- * exhausted.
- */
- __le32 tx_pkts_retry_exhausted; /* # of user frames where a retry
- * was exhausted
- */
- s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last
- * received data frame.
- */
- /* TX WLAN retry/failure statistics:
- * Separated for host requested frames and locally generated frames.
- * Include unicast frame only where the retries/failures can be counted.
- */
- __le32 tx_pkts_total; /* # user frames sent successfully */
- __le32 tx_pkts_retries; /* # user frames retries */
- __le32 tx_pkts_fw_total; /* # FW generated sent successfully */
- __le32 tx_pkts_fw_retries; /* # retries for FW generated frames */
- __le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry
- * was exhausted
- */
- __le32 rx_pkts_retried; /* # rx with retry bit set */
- __le32 tx_rate_fallback; /* lowest fallback TX rate */
-};
-
-struct brcmf_chanspec_list {
- __le32 count; /* # of entries */
- __le32 element[1]; /* variable length uint32 list */
-};
-
-/*
- * WLC_E_PROBRESP_MSG
- * WLC_E_P2P_PROBREQ_MSG
- * WLC_E_ACTION_FRAME_RX
- */
-struct brcmf_rx_mgmt_data {
- __be16 version;
- __be16 chanspec;
- __be32 rssi;
- __be32 mactime;
- __be32 rate;
-};
-
-/**
- * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct.
- *
- * @cmd: "add", "del" or "clr".
- * @masksize: Size of the mask in #of bytes
- * @offset: Pattern byte offset in packet
- * @patternoffset: Offset of start of pattern. Starting from field masksize.
- * @patternsize: Size of the pattern itself in #of bytes
- * @id: id
- * @reasonsize: Size of the wakeup reason code
- * @type: Type of pattern (enum brcmf_wowl_pattern_type)
- */
-struct brcmf_fil_wowl_pattern_le {
- u8 cmd[4];
- __le32 masksize;
- __le32 offset;
- __le32 patternoffset;
- __le32 patternsize;
- __le32 id;
- __le32 reasonsize;
- __le32 type;
- /* u8 mask[] - Mask follows the structure above */
- /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */
-};
-
-struct brcmf_mbss_ssid_le {
- __le32 bsscfgidx;
- __le32 SSID_len;
- unsigned char SSID[32];
-};
-
-/**
- * struct brcmf_fil_country_le - country configuration structure.
- *
- * @country_abbrev: null-terminated country code used in the country IE.
- * @rev: revision specifier for ccode. on set, -1 indicates unspecified.
- * @ccode: null-terminated built-in country code.
- */
-struct brcmf_fil_country_le {
- char country_abbrev[BRCMF_COUNTRY_BUF_SZ];
- __le32 rev;
- char ccode[BRCMF_COUNTRY_BUF_SZ];
-};
-
-/**
- * struct brcmf_rev_info_le - device revision info.
- *
- * @vendorid: PCI vendor id.
- * @deviceid: device id of chip.
- * @radiorev: radio revision.
- * @chiprev: chip revision.
- * @corerev: core revision.
- * @boardid: board identifier (usu. PCI sub-device id).
- * @boardvendor: board vendor (usu. PCI sub-vendor id).
- * @boardrev: board revision.
- * @driverrev: driver version.
- * @ucoderev: microcode version.
- * @bus: bus type.
- * @chipnum: chip number.
- * @phytype: phy type.
- * @phyrev: phy revision.
- * @anarev: anacore rev.
- * @chippkg: chip package info.
- * @nvramrev: nvram revision number.
- */
-struct brcmf_rev_info_le {
- __le32 vendorid;
- __le32 deviceid;
- __le32 radiorev;
- __le32 chiprev;
- __le32 corerev;
- __le32 boardid;
- __le32 boardvendor;
- __le32 boardrev;
- __le32 driverrev;
- __le32 ucoderev;
- __le32 bus;
- __le32 chipnum;
- __le32 phytype;
- __le32 phyrev;
- __le32 anarev;
- __le32 chippkg;
- __le32 nvramrev;
-};
-
-/**
- * struct brcmf_assoclist_le - request assoc list.
- *
- * @count: indicates number of stations.
- * @mac: MAC addresses of stations.
- */
-struct brcmf_assoclist_le {
- __le32 count;
- u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN];
-};
-
-#endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
deleted file mode 100644
index 086cac3f86d6..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ /dev/null
@@ -1,2271 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/if_ether.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/err.h>
-#include <linux/jiffies.h>
-#include <net/cfg80211.h>
-
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-#include "core.h"
-#include "debug.h"
-#include "bus.h"
-#include "fwil.h"
-#include "fwil_types.h"
-#include "fweh.h"
-#include "fwsignal.h"
-#include "p2p.h"
-#include "cfg80211.h"
-#include "proto.h"
-
-/**
- * DOC: Firmware Signalling
- *
- * Firmware can send signals to host and vice versa, which are passed in the
- * data packets using TLV based header. This signalling layer is on top of the
- * BDC bus protocol layer.
- */
-
-/*
- * single definition for firmware-driver flow control tlv's.
- *
- * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).
- * A length value 0 indicates variable length tlv.
- */
-#define BRCMF_FWS_TLV_DEFLIST \
- BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \
- BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \
- BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \
- BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \
- BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \
- BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \
- BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \
- BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \
- BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \
- BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \
- BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \
- BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \
- BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \
- BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \
- BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \
- BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \
- BRCMF_FWS_TLV_DEF(FILLER, 255, 0)
-
-/*
- * enum brcmf_fws_tlv_type - definition of tlv identifiers.
- */
-#define BRCMF_FWS_TLV_DEF(name, id, len) \
- BRCMF_FWS_TYPE_ ## name = id,
-enum brcmf_fws_tlv_type {
- BRCMF_FWS_TLV_DEFLIST
- BRCMF_FWS_TYPE_INVALID
-};
-#undef BRCMF_FWS_TLV_DEF
-
-/*
- * enum brcmf_fws_tlv_len - definition of tlv lengths.
- */
-#define BRCMF_FWS_TLV_DEF(name, id, len) \
- BRCMF_FWS_TYPE_ ## name ## _LEN = (len),
-enum brcmf_fws_tlv_len {
- BRCMF_FWS_TLV_DEFLIST
-};
-#undef BRCMF_FWS_TLV_DEF
-
-#ifdef DEBUG
-/*
- * brcmf_fws_tlv_names - array of tlv names.
- */
-#define BRCMF_FWS_TLV_DEF(name, id, len) \
- { id, #name },
-static struct {
- enum brcmf_fws_tlv_type id;
- const char *name;
-} brcmf_fws_tlv_names[] = {
- BRCMF_FWS_TLV_DEFLIST
-};
-#undef BRCMF_FWS_TLV_DEF
-
-
-static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)
- if (brcmf_fws_tlv_names[i].id == id)
- return brcmf_fws_tlv_names[i].name;
-
- return "INVALID";
-}
-#else
-static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
-{
- return "NODEBUG";
-}
-#endif /* DEBUG */
-
-/*
- * The PKTTAG tlv has additional bytes when firmware-signalling
- * mode has REUSESEQ flag set.
- */
-#define BRCMF_FWS_TYPE_SEQ_LEN 2
-
-/*
- * flags used to enable tlv signalling from firmware.
- */
-#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001
-#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002
-#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004
-#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008
-#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010
-#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020
-#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040
-
-#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32
-#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff
-
-#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0
-#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1
-#define BRCMF_FWS_FLOWCONTROL_HIWATER 128
-#define BRCMF_FWS_FLOWCONTROL_LOWATER 64
-
-#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
-#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02
-
-#define BRCMF_FWS_RET_OK_NOSCHEDULE 0
-#define BRCMF_FWS_RET_OK_SCHEDULE 1
-
-#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */
-#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \
- ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \
- (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))
-#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \
- (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)
-
-/**
- * enum brcmf_fws_skb_state - indicates processing state of skb.
- *
- * @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_TIM
-};
-
-/**
- * struct brcmf_skbuff_cb - control buffer associated with skbuff.
- *
- * @bus_flags: 2 bytes reserved for bus specific parameters
- * @if_flags: holds interface index and packet related flags.
- * @htod: host to device packet identifier (used in PKTTAG tlv).
- * @htod_seq: this 16-bit is original seq number for every suppress packet.
- * @state: transmit state of the packet.
- * @mac: descriptor related to destination for this packet.
- *
- * This information is stored in control buffer struct sk_buff::cb, which
- * provides 48 bytes of storage so this structure should not exceed that.
- */
-struct brcmf_skbuff_cb {
- u16 bus_flags;
- u16 if_flags;
- u32 htod;
- u16 htod_seq;
- enum brcmf_fws_skb_state state;
- struct brcmf_fws_mac_descriptor *mac;
-};
-
-/*
- * macro casting skbuff control buffer to struct brcmf_skbuff_cb.
- */
-#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb))
-
-/*
- * sk_buff control if flags
- *
- * b[11] - packet sent upon firmware request.
- * b[10] - packet only contains signalling data.
- * b[9] - packet is a tx packet.
- * b[8] - packet used requested credit
- * b[7] - interface in AP mode.
- * b[3:0] - interface index.
- */
-#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800
-#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11
-#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400
-#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_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_INDEX_MASK 0x000f
-#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0
-
-#define brcmf_skb_if_flags_set_field(skb, field, value) \
- brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \
- BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
- BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))
-#define brcmf_skb_if_flags_get_field(skb, field) \
- brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \
- BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
- BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)
-
-/*
- * sk_buff control packet identifier
- *
- * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
- *
- * - Generated at the host (e.g. dhd)
- * - Seen as a generic sequence number by firmware except for the flags field.
- *
- * Generation : b[31] => generation number for this packet [host->fw]
- * OR, current generation number [fw->host]
- * Flags : b[30:27] => command, status flags
- * FIFO-AC : b[26:24] => AC-FIFO id
- * h-slot : b[23:8] => hanger-slot
- * freerun : b[7:0] => A free running counter
- */
-#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000
-#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31
-#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000
-#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27
-#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000
-#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24
-#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_set_field(skb, field, value) \
- brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
- BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
- BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))
-#define brcmf_skb_htod_tag_get_field(skb, field) \
- brcmu_maskget32(brcmf_skbcb(skb)->htod, \
- BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
- BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
-
-#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000
-#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13
-#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000
-#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12
-#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff
-#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0
-
-#define brcmf_skb_htod_seq_set_field(skb, field, value) \
- brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \
- BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
- BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))
-#define brcmf_skb_htod_seq_get_field(skb, field) \
- brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \
- BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
- BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)
-
-#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000
-#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31
-#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000
-#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27
-#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000
-#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24
-#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00
-#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8
-#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF
-#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0
-
-#define brcmf_txstatus_get_field(txs, field) \
- brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
- BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
-
-/* How long to defer borrowing in jiffies */
-#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
-
-/**
- * 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.
- * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic.
- * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only).
- * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only).
- * @BRCMF_FWS_FIFO_COUNT: number of fifos.
- */
-enum brcmf_fws_fifo {
- 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,
- BRCMF_FWS_FIFO_BCMC,
- BRCMF_FWS_FIFO_ATIM,
- BRCMF_FWS_FIFO_COUNT
-};
-
-/**
- * enum brcmf_fws_txstatus - txstatus flag values.
- *
- * @BRCMF_FWS_TXSTATUS_DISCARD:
- * host is free to discard the packet.
- * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS:
- * 802.11 core suppressed the packet.
- * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS:
- * 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_HOST_TOSSED
-};
-
-enum brcmf_fws_fcmode {
- BRCMF_FWS_FCMODE_NONE,
- BRCMF_FWS_FCMODE_IMPLIED_CREDIT,
- BRCMF_FWS_FCMODE_EXPLICIT_CREDIT
-};
-
-enum brcmf_fws_mac_desc_state {
- BRCMF_FWS_STATE_OPEN = 1,
- BRCMF_FWS_STATE_CLOSE
-};
-
-/**
- * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
- *
- * @occupied: slot is in use.
- * @mac_handle: handle for mac entry determined by firmware.
- * @interface_id: interface index.
- * @state: current state.
- * @suppressed: mac entry is suppressed.
- * @generation: generation bit.
- * @ac_bitmap: ac queue bitmap.
- * @requested_credit: credits requested by firmware.
- * @ea: ethernet address.
- * @seq: per-node free-running sequence.
- * @psq: power-save queue.
- * @transit_count: packet in transit to firmware.
- */
-struct brcmf_fws_mac_descriptor {
- char name[16];
- u8 occupied;
- u8 mac_handle;
- u8 interface_id;
- u8 state;
- bool suppressed;
- u8 generation;
- u8 ac_bitmap;
- u8 requested_credit;
- u8 requested_packet;
- u8 ea[ETH_ALEN];
- u8 seq[BRCMF_FWS_FIFO_COUNT];
- struct pktq psq;
- int transit_count;
- int suppr_transit_count;
- bool send_tim_signal;
- u8 traffic_pending_bmp;
- u8 traffic_lastreported_bmp;
-};
-
-#define BRCMF_FWS_HANGER_MAXITEMS 1024
-
-/**
- * enum brcmf_fws_hanger_item_state - state of hanger item.
- *
- * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use.
- * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use.
- * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed.
- */
-enum brcmf_fws_hanger_item_state {
- BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,
- BRCMF_FWS_HANGER_ITEM_STATE_INUSE,
- BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED
-};
-
-
-/**
- * struct brcmf_fws_hanger_item - single entry for tx pending packet.
- *
- * @state: entry is either free or occupied.
- * @pkt: packet itself.
- */
-struct brcmf_fws_hanger_item {
- enum brcmf_fws_hanger_item_state state;
- struct sk_buff *pkt;
-};
-
-/**
- * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus.
- *
- * @pushed: packets pushed to await txstatus.
- * @popped: packets popped upon handling txstatus.
- * @failed_to_push: packets that could not be pushed.
- * @failed_to_pop: packets that could not be popped.
- * @failed_slotfind: packets for which failed to find an entry.
- * @slot_pos: last returned item index for a free entry.
- * @items: array of hanger items.
- */
-struct brcmf_fws_hanger {
- u32 pushed;
- u32 popped;
- u32 failed_to_push;
- u32 failed_to_pop;
- u32 failed_slotfind;
- u32 slot_pos;
- struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];
-};
-
-struct brcmf_fws_macdesc_table {
- struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
- struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];
- struct brcmf_fws_mac_descriptor other;
-};
-
-struct brcmf_fws_stats {
- u32 tlv_parse_failed;
- u32 tlv_invalid_type;
- u32 header_only_pkt;
- u32 header_pulls;
- u32 pkt2bus;
- u32 send_pkts[5];
- u32 requested_sent[5];
- u32 generic_error;
- u32 mac_update_failed;
- u32 mac_ps_update_failed;
- u32 if_update_failed;
- u32 packet_request_failed;
- u32 credit_request_failed;
- u32 rollback_success;
- u32 rollback_failed;
- u32 delayq_full_error;
- u32 supprq_full_error;
- u32 txs_indicate;
- u32 txs_discard;
- 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_fws_info {
- struct brcmf_pub *drvr;
- spinlock_t spinlock;
- ulong flags;
- struct brcmf_fws_stats stats;
- struct brcmf_fws_hanger hanger;
- enum brcmf_fws_fcmode fcmode;
- bool fw_signals;
- bool bcmc_credit_check;
- struct brcmf_fws_macdesc_table desc;
- struct workqueue_struct *fws_wq;
- struct work_struct fws_dequeue_work;
- u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
- int fifo_credit[BRCMF_FWS_FIFO_COUNT];
- int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
- int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
- u32 fifo_credit_map;
- u32 fifo_delay_map;
- unsigned long borrow_defer_timestamp;
- bool bus_flow_blocked;
- bool creditmap_received;
- u8 mode;
- bool avoid_queueing;
-};
-
-/*
- * brcmf_fws_prio2fifo - mapping from 802.1d priority to firmware fifo index.
- */
-static const int brcmf_fws_prio2fifo[] = {
- BRCMF_FWS_FIFO_AC_BE,
- BRCMF_FWS_FIFO_AC_BK,
- BRCMF_FWS_FIFO_AC_BK,
- BRCMF_FWS_FIFO_AC_BE,
- BRCMF_FWS_FIFO_AC_VI,
- BRCMF_FWS_FIFO_AC_VI,
- BRCMF_FWS_FIFO_AC_VO,
- BRCMF_FWS_FIFO_AC_VO
-};
-
-static int fcmode;
-module_param(fcmode, int, S_IRUSR);
-MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control");
-
-#define BRCMF_FWS_TLV_DEF(name, id, len) \
- case BRCMF_FWS_TYPE_ ## name: \
- return len;
-
-/**
- * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
- *
- * @fws: firmware-signalling information.
- * @id: identifier of the TLV.
- *
- * Return: the specified length for the given TLV; Otherwise -EINVAL.
- */
-static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
- enum brcmf_fws_tlv_type id)
-{
- switch (id) {
- BRCMF_FWS_TLV_DEFLIST
- default:
- fws->stats.tlv_invalid_type++;
- break;
- }
- return -EINVAL;
-}
-#undef BRCMF_FWS_TLV_DEF
-
-static void brcmf_fws_lock(struct brcmf_fws_info *fws)
- __acquires(&fws->spinlock)
-{
- spin_lock_irqsave(&fws->spinlock, fws->flags);
-}
-
-static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
- __releases(&fws->spinlock)
-{
- spin_unlock_irqrestore(&fws->spinlock, fws->flags);
-}
-
-static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
-{
- u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
- return ifidx == *(int *)arg;
-}
-
-static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
- int ifidx)
-{
- bool (*matchfn)(struct sk_buff *, void *) = NULL;
- struct sk_buff *skb;
- int prec;
-
- if (ifidx != -1)
- matchfn = brcmf_fws_ifidx_match;
- for (prec = 0; prec < q->num_prec; prec++) {
- skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
- while (skb) {
- brcmu_pkt_buf_free_skb(skb);
- skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
- }
- }
-}
-
-static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
-{
- int i;
-
- memset(hanger, 0, sizeof(*hanger));
- for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
- hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
-}
-
-static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
-{
- u32 i;
-
- i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
-
- while (i != h->slot_pos) {
- if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
- h->slot_pos = i;
- goto done;
- }
- i++;
- if (i == BRCMF_FWS_HANGER_MAXITEMS)
- i = 0;
- }
- brcmf_err("all slots occupied\n");
- h->failed_slotfind++;
- i = BRCMF_FWS_HANGER_MAXITEMS;
-done:
- return i;
-}
-
-static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
- struct sk_buff *pkt, u32 slot_id)
-{
- if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
- return -ENOENT;
-
- if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
- brcmf_err("slot is not free\n");
- h->failed_to_push++;
- return -EINVAL;
- }
-
- h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;
- h->items[slot_id].pkt = pkt;
- h->pushed++;
- return 0;
-}
-
-static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
- u32 slot_id, struct sk_buff **pktout,
- bool remove_item)
-{
- if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
- return -ENOENT;
-
- if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
- brcmf_err("entry not in use\n");
- h->failed_to_pop++;
- return -EINVAL;
- }
-
- *pktout = h->items[slot_id].pkt;
- if (remove_item) {
- h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
- h->items[slot_id].pkt = NULL;
- h->popped++;
- }
- return 0;
-}
-
-static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
- u32 slot_id)
-{
- if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
- return -ENOENT;
-
- if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
- brcmf_err("entry not in use\n");
- return -EINVAL;
- }
-
- h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
- return 0;
-}
-
-static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
- bool (*fn)(struct sk_buff *, void *),
- int ifidx)
-{
- struct brcmf_fws_hanger *h = &fws->hanger;
- struct sk_buff *skb;
- int i;
- enum brcmf_fws_hanger_item_state s;
-
- for (i = 0; i < ARRAY_SIZE(h->items); i++) {
- s = h->items[i].state;
- if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
- s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
- skb = h->items[i].pkt;
- if (fn == NULL || fn(skb, &ifidx)) {
- /* suppress packets freed from psq */
- if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)
- brcmu_pkt_buf_free_skb(skb);
- h->items[i].state =
- BRCMF_FWS_HANGER_ITEM_STATE_FREE;
- }
- }
- }
-}
-
-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 */
- if (addr)
- memcpy(&desc->ea[0], addr, ETH_ALEN);
-}
-
-static
-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_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
-{
- struct brcmf_fws_mac_descriptor *entry;
- int i;
-
- if (ea == NULL)
- return ERR_PTR(-EINVAL);
-
- entry = &fws->desc.nodes[0];
- for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {
- if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
- return entry;
- entry++;
- }
-
- return ERR_PTR(-ENOENT);
-}
-
-static struct brcmf_fws_mac_descriptor*
-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;
-
- multicast = is_multicast_ether_addr(da);
-
- /* 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.
- */
- if (multicast && ifp->fws_desc) {
- entry = ifp->fws_desc;
- goto done;
- }
-
- entry = brcmf_fws_macdesc_lookup(fws, da);
- if (IS_ERR(entry))
- entry = ifp->fws_desc;
-
-done:
- return entry;
-}
-
-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;
-
- /* for unique destination entries the related interface
- * may be closed.
- */
- if (entry->mac_handle) {
- if_entry = &fws->desc.iface[entry->interface_id];
- if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
- return true;
- }
- /* an entry is closed when the state is closed and
- * the firmware did not request anything.
- */
- closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
- !entry->requested_credit && !entry->requested_packet;
-
- /* Or firmware does not allow traffic for given fifo */
- return closed || !(entry->ac_bitmap & BIT(fifo));
-}
-
-static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
- struct brcmf_fws_mac_descriptor *entry,
- int ifidx)
-{
- if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
- brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
- entry->occupied = !!(entry->psq.len);
- }
-}
-
-static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
- bool (*fn)(struct sk_buff *, void *),
- int ifidx)
-{
- struct brcmf_fws_hanger_item *hi;
- struct pktq *txq;
- struct sk_buff *skb;
- int prec;
- u32 hslot;
-
- txq = brcmf_bus_gettxq(fws->drvr->bus_if);
- if (IS_ERR(txq)) {
- brcmf_dbg(TRACE, "no txq to clean up\n");
- return;
- }
-
- for (prec = 0; prec < txq->num_prec; prec++) {
- skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
- while (skb) {
- hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
- hi = &fws->hanger.items[hslot];
- WARN_ON(skb != hi->pkt);
- hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
- brcmu_pkt_buf_free_skb(skb);
- skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
- }
- }
-}
-
-static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
-{
- int i;
- struct brcmf_fws_mac_descriptor *table;
- bool (*matchfn)(struct sk_buff *, void *) = NULL;
-
- if (fws == NULL)
- return;
-
- if (ifidx != -1)
- matchfn = brcmf_fws_ifidx_match;
-
- /* cleanup individual nodes */
- table = &fws->desc.nodes[0];
- for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
- brcmf_fws_macdesc_cleanup(fws, &table[i], 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 u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
-{
- struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
- u8 *wlh;
- u16 data_offset = 0;
- u8 fillers;
- __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
- __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);
-
- brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",
- entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
- (le32_to_cpu(pkttag) >> 8) & 0xffff,
- brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);
- if (entry->send_tim_signal)
- data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
- if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
- data_offset += BRCMF_FWS_TYPE_SEQ_LEN;
- /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
- data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
- fillers = round_up(data_offset, 4) - data_offset;
- 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));
- if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
- wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;
- memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,
- sizeof(pktseq));
- }
- wlh += wlh[1] + 2;
-
- if (entry->send_tim_signal) {
- entry->send_tim_signal = 0;
- 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: handle %d bmp 0x%X\n",
- entry->mac_handle, 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);
-
- return (u8)(data_offset >> 2);
-}
-
-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_skbuff_cb *skcb;
- s32 err;
- u32 len;
- u8 data_offset;
- int ifidx;
-
- /* check delayedQ and suppressQ in one call using bitmap */
- if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
- 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_SEQ_LEN +
- 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;
- skcb->htod = 0;
- skcb->htod_seq = 0;
- data_offset = brcmf_fws_hdrpush(fws, skb);
- ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
- brcmf_fws_unlock(fws);
- err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
- brcmf_fws_lock(fws);
- if (err)
- brcmu_pkt_buf_free_skb(skb);
- return true;
- }
- return false;
-}
-
-static void
-brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
- u8 if_id)
-{
- struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
-
- if (WARN_ON(!ifp))
- return;
-
- 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) {
- fws->stats.fws_flow_block++;
- brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
- }
- return;
-}
-
-static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
-{
- brcmf_dbg(CTL, "rssi %d\n", rssi);
- return 0;
-}
-
-static
-int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
-{
- struct brcmf_fws_mac_descriptor *entry, *existing;
- u8 mac_handle;
- u8 ifidx;
- u8 *addr;
-
- mac_handle = *data++;
- ifidx = *data++;
- addr = data;
-
- entry = &fws->desc.nodes[mac_handle & 0x1F];
- if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
- if (entry->occupied) {
- brcmf_dbg(TRACE, "deleting %s mac %pM\n",
- entry->name, addr);
- brcmf_fws_lock(fws);
- brcmf_fws_macdesc_cleanup(fws, entry, -1);
- brcmf_fws_macdesc_deinit(entry);
- brcmf_fws_unlock(fws);
- } else
- fws->stats.mac_update_failed++;
- return 0;
- }
-
- existing = brcmf_fws_macdesc_lookup(fws, addr);
- if (IS_ERR(existing)) {
- if (!entry->occupied) {
- brcmf_fws_lock(fws);
- entry->mac_handle = mac_handle;
- brcmf_fws_macdesc_init(entry, addr, ifidx);
- brcmf_fws_macdesc_set_name(fws, entry);
- brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
- BRCMF_FWS_PSQ_LEN);
- brcmf_fws_unlock(fws);
- brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
- } else {
- fws->stats.mac_update_failed++;
- }
- } else {
- if (entry != existing) {
- brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
- brcmf_fws_lock(fws);
- memcpy(entry, existing,
- offsetof(struct brcmf_fws_mac_descriptor, psq));
- entry->mac_handle = mac_handle;
- brcmf_fws_macdesc_deinit(existing);
- brcmf_fws_macdesc_set_name(fws, entry);
- brcmf_fws_unlock(fws);
- 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);
- /* TODO: what should we do here: continue, reinit, .. */
- }
- }
- return 0;
-}
-
-static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
- u8 type, u8 *data)
-{
- struct brcmf_fws_mac_descriptor *entry;
- u8 mac_handle;
- int ret;
-
- mac_handle = data[0];
- entry = &fws->desc.nodes[mac_handle & 0x1F];
- if (!entry->occupied) {
- fws->stats.mac_ps_update_failed++;
- return -ESRCH;
- }
- brcmf_fws_lock(fws);
- /* 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;
- ret = BRCMF_FWS_RET_OK_SCHEDULE;
- } else {
- entry->state = BRCMF_FWS_STATE_CLOSE;
- 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);
- ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
- }
- brcmf_fws_unlock(fws);
- return ret;
-}
-
-static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
- u8 type, u8 *data)
-{
- struct brcmf_fws_mac_descriptor *entry;
- u8 ifidx;
- int ret;
-
- ifidx = data[0];
-
- if (ifidx >= BRCMF_MAX_IFS) {
- ret = -ERANGE;
- goto fail;
- }
-
- entry = &fws->desc.iface[ifidx];
- if (!entry->occupied) {
- ret = -ESRCH;
- goto fail;
- }
-
- brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
- entry->name);
- brcmf_fws_lock(fws);
- switch (type) {
- case BRCMF_FWS_TYPE_INTERFACE_OPEN:
- entry->state = BRCMF_FWS_STATE_OPEN;
- ret = BRCMF_FWS_RET_OK_SCHEDULE;
- break;
- case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
- entry->state = BRCMF_FWS_STATE_CLOSE;
- ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
- break;
- default:
- ret = -EINVAL;
- brcmf_fws_unlock(fws);
- goto fail;
- }
- brcmf_fws_unlock(fws);
- return ret;
-
-fail:
- fws->stats.if_update_failed++;
- return ret;
-}
-
-static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
- u8 *data)
-{
- struct brcmf_fws_mac_descriptor *entry;
-
- entry = &fws->desc.nodes[data[1] & 0x1F];
- if (!entry->occupied) {
- if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
- fws->stats.credit_request_failed++;
- else
- fws->stats.packet_request_failed++;
- 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]);
- brcmf_fws_lock(fws);
- if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
- entry->requested_credit = data[0];
- else
- entry->requested_packet = data[0];
-
- entry->ac_bitmap = data[2];
- brcmf_fws_unlock(fws);
- 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)
-{
- int lender_ac;
- int *borrowed;
- int *fifo_credit;
-
- 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;
- lender_ac--) {
- borrowed = &fws->credits_borrowed[lender_ac];
- if (*borrowed) {
- fws->fifo_credit_map |= (1 << lender_ac);
- fifo_credit = &fws->fifo_credit[lender_ac];
- if (*borrowed >= credits) {
- *borrowed -= credits;
- *fifo_credit += credits;
- return;
- } else {
- credits -= *borrowed;
- *fifo_credit += *borrowed;
- *borrowed = 0;
- }
- }
- }
- }
-
- fws->fifo_credit[fifo] += credits;
-}
-
-static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
-{
- /* only schedule dequeue when there are credits for delayed traffic */
- if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
- (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
- queue_work(fws->fws_wq, &fws->fws_dequeue_work);
-}
-
-static int brcmf_fws_enq(struct brcmf_fws_info *fws,
- enum brcmf_fws_skb_state state, int fifo,
- struct sk_buff *p)
-{
- int prec = 2 * fifo;
- u32 *qfull_stat = &fws->stats.delayq_full_error;
- struct brcmf_fws_mac_descriptor *entry;
- struct pktq *pq;
- struct sk_buff_head *queue;
- struct sk_buff *p_head;
- struct sk_buff *p_tail;
- u32 fr_new;
- u32 fr_compare;
-
- entry = brcmf_skbcb(p)->mac;
- if (entry == NULL) {
- brcmf_err("no mac descriptor found for skb %p\n", p);
- return -ENOENT;
- }
-
- 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;
-
- /* Fix out of order delivery of frames. Dont assume frame */
- /* can be inserted at the end, but look for correct position */
- pq = &entry->psq;
- if (pktq_full(pq) || pktq_pfull(pq, prec)) {
- *qfull_stat += 1;
- return -ENFILE;
- }
- queue = &pq->q[prec].skblist;
-
- p_head = skb_peek(queue);
- p_tail = skb_peek_tail(queue);
- fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);
-
- while (p_head != p_tail) {
- fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
- FREERUN);
- /* be sure to handle wrap of 256 */
- if (((fr_new > fr_compare) &&
- ((fr_new - fr_compare) < 128)) ||
- ((fr_new < fr_compare) &&
- ((fr_compare - fr_new) > 128)))
- break;
- p_tail = skb_queue_prev(queue, p_tail);
- }
- /* Position found. Determine what to do */
- if (p_tail == NULL) {
- /* empty list */
- __skb_queue_tail(queue, p);
- } else {
- fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
- FREERUN);
- if (((fr_new > fr_compare) &&
- ((fr_new - fr_compare) < 128)) ||
- ((fr_new < fr_compare) &&
- ((fr_compare - fr_new) > 128))) {
- /* After tail */
- __skb_queue_after(queue, p_tail, p);
- } else {
- /* Before tail */
- __skb_insert(p, p_tail->prev, p_tail, queue);
- }
- }
-
- /* Complete the counters and statistics */
- pq->len++;
- if (pq->hi_prec < prec)
- pq->hi_prec = (u8) prec;
- } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {
- *qfull_stat += 1;
- return -ENFILE;
- }
-
- /* increment total enqueued packet count */
- fws->fifo_delay_map |= 1 << fifo;
- fws->fifo_enqpkt[fifo]++;
-
- /* update the sk_buff state */
- brcmf_skbcb(p)->state = state;
-
- /*
- * A packet has been pushed so update traffic
- * availability bitmap, if applicable
- */
- 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;
-}
-
-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 num_nodes;
- int node_pos;
- int prec_out;
- int pmsk;
- int i;
-
- table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
- num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);
- node_pos = fws->deq_node_pos[fifo];
-
- for (i = 0; i < num_nodes; i++) {
- entry = &table[(node_pos + i) % num_nodes];
- if (!entry->occupied ||
- brcmf_fws_macdesc_closed(fws, entry, fifo))
- continue;
-
- if (entry->suppressed)
- pmsk = 2;
- else
- pmsk = 3;
- p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
- if (p == NULL) {
- if (entry->suppressed) {
- if (entry->suppr_transit_count)
- continue;
- entry->suppressed = false;
- p = brcmu_pktq_mdeq(&entry->psq,
- 1 << (fifo * 2), &prec_out);
- }
- }
- if (p == NULL)
- continue;
-
- 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;
- brcmf_fws_flow_control_check(fws, &entry->psq,
- brcmf_skb_if_flags_get_field(p,
- INDEX)
- );
- /*
- * A packet has been picked up, update traffic
- * availability bitmap, if applicable
- */
- brcmf_fws_tim_update(fws, entry, fifo, false);
-
- /*
- * decrement total enqueued fifo packets and
- * clear delay bitmap if done.
- */
- fws->fifo_enqpkt[fifo]--;
- if (fws->fifo_enqpkt[fifo] == 0)
- fws->fifo_delay_map &= ~(1 << fifo);
- goto done;
- }
- p = NULL;
-done:
- brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
- return p;
-}
-
-static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
- struct sk_buff *skb,
- u32 genbit, u16 seq)
-{
- struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
- u32 hslot;
- int ret;
-
- hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
-
- /* this packet was suppressed */
- if (!entry->suppressed) {
- entry->suppressed = true;
- entry->suppr_transit_count = entry->transit_count;
- brcmf_dbg(DATA, "suppress %s: transit %d\n",
- entry->name, entry->transit_count);
- }
-
- entry->generation = genbit;
-
- brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
- brcmf_skbcb(skb)->htod_seq = seq;
- if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
- brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
- brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
- } else {
- brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
- }
- ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
-
- if (ret != 0) {
- /* suppress q is full drop this packet */
- brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
- } else {
- /* Mark suppressed to avoid a double free during wlfc cleanup */
- brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
- }
-
- return ret;
-}
-
-static int
-brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
- u32 genbit, u16 seq)
-{
- 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;
- struct brcmf_if *ifp;
-
- brcmf_dbg(DATA, "flags %d\n", flags);
-
- if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
- fws->stats.txs_discard++;
- else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
- fws->stats.txs_supp_core++;
- remove_from_hanger = false;
- } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
- fws->stats.txs_supp_ps++;
- 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");
-
- ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
- remove_from_hanger);
- if (ret != 0) {
- brcmf_err("no packet in hanger slot: hslot=%d\n", hslot);
- return ret;
- }
-
- 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 %d htod %X seq %X\n", entry->name, flags,
- skcb->htod, seq);
-
- /* pick up the implicit credit from this packet */
- fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
- 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);
-
- ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
- if (ret) {
- brcmu_pkt_buf_free_skb(skb);
- return -EINVAL;
- }
- if (!remove_from_hanger)
- ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
- genbit, seq);
- if (remove_from_hanger || ret)
- brcmf_txfinalize(ifp, skb, true);
-
- return 0;
-}
-
-static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
- u8 *data)
-{
- int i;
-
- if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
- brcmf_dbg(INFO, "ignored\n");
- return BRCMF_FWS_RET_OK_NOSCHEDULE;
- }
-
- brcmf_dbg(DATA, "enter: data %pM\n", data);
- brcmf_fws_lock(fws);
- for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
- brcmf_fws_return_credits(fws, i, data[i]);
-
- brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
- fws->fifo_delay_map);
- brcmf_fws_unlock(fws);
- return BRCMF_FWS_RET_OK_SCHEDULE;
-}
-
-static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
-{
- __le32 status_le;
- __le16 seq_le;
- u32 status;
- u32 hslot;
- u32 genbit;
- u8 flags;
- u16 seq;
-
- fws->stats.txs_indicate++;
- memcpy(&status_le, data, sizeof(status_le));
- status = le32_to_cpu(status_le);
- flags = brcmf_txstatus_get_field(status, FLAGS);
- hslot = brcmf_txstatus_get_field(status, HSLOT);
- genbit = brcmf_txstatus_get_field(status, GENERATION);
- if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
- memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN],
- sizeof(seq_le));
- seq = le16_to_cpu(seq_le);
- } else {
- seq = 0;
- }
-
- brcmf_fws_lock(fws);
- brcmf_fws_txs_process(fws, flags, hslot, genbit, seq);
- brcmf_fws_unlock(fws);
- return BRCMF_FWS_RET_OK_NOSCHEDULE;
-}
-
-static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
-{
- __le32 timestamp;
-
- memcpy(&timestamp, &data[2], sizeof(timestamp));
- brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
- le32_to_cpu(timestamp));
- return 0;
-}
-
-static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data)
-{
- struct brcmf_fws_info *fws = ifp->drvr->fws;
- int i;
- u8 *credits = data;
-
- if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
- 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(fws);
- for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
- if (*credits)
- fws->fifo_credit_map |= 1 << i;
- else
- fws->fifo_credit_map &= ~(1 << i);
- fws->fifo_credit[i] = *credits++;
- }
- brcmf_fws_schedule_deq(fws);
- brcmf_fws_unlock(fws);
- 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;
-
- brcmf_fws_lock(fws);
- if (fws)
- fws->bcmc_credit_check = true;
- brcmf_fws_unlock(fws);
- return 0;
-}
-
-void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
-{
- struct brcmf_skb_reorder_data *rd;
- struct brcmf_fws_info *fws = ifp->drvr->fws;
- u8 *signal_data;
- s16 data_len;
- u8 type;
- u8 len;
- u8 *data;
- s32 status;
- s32 err;
-
- brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
- ifp->ifidx, skb->len, siglen);
-
- WARN_ON(siglen > skb->len);
-
- if (!siglen)
- return;
- /* if flow control disabled, skip to packet data and leave */
- if ((!fws) || (!fws->fw_signals)) {
- skb_pull(skb, siglen);
- return;
- }
-
- fws->stats.header_pulls++;
- data_len = siglen;
- signal_data = skb->data;
-
- status = BRCMF_FWS_RET_OK_NOSCHEDULE;
- while (data_len > 0) {
- /* extract tlv info */
- type = signal_data[0];
-
- /* FILLER type is actually not a TLV, but
- * a single byte that can be skipped.
- */
- if (type == BRCMF_FWS_TYPE_FILLER) {
- signal_data += 1;
- data_len -= 1;
- continue;
- }
- len = signal_data[1];
- data = signal_data + 2;
-
- 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))
- break;
-
- err = BRCMF_FWS_RET_OK_NOSCHEDULE;
- switch (type) {
- case BRCMF_FWS_TYPE_COMP_TXSTATUS:
- break;
- case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
- rd = (struct brcmf_skb_reorder_data *)skb->cb;
- rd->reorder = data;
- break;
- case BRCMF_FWS_TYPE_MACDESC_ADD:
- case BRCMF_FWS_TYPE_MACDESC_DEL:
- brcmf_fws_macdesc_indicate(fws, type, data);
- break;
- case BRCMF_FWS_TYPE_MAC_OPEN:
- case BRCMF_FWS_TYPE_MAC_CLOSE:
- err = brcmf_fws_macdesc_state_indicate(fws, type, data);
- break;
- case BRCMF_FWS_TYPE_INTERFACE_OPEN:
- case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
- err = brcmf_fws_interface_state_indicate(fws, type,
- data);
- break;
- case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
- case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
- err = brcmf_fws_request_indicate(fws, type, data);
- break;
- case BRCMF_FWS_TYPE_TXSTATUS:
- brcmf_fws_txstatus_indicate(fws, data);
- break;
- case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
- err = brcmf_fws_fifocreditback_indicate(fws, data);
- break;
- case BRCMF_FWS_TYPE_RSSI:
- brcmf_fws_rssi_indicate(fws, *data);
- break;
- case BRCMF_FWS_TYPE_TRANS_ID:
- brcmf_fws_dbg_seqnum_check(fws, data);
- break;
- case BRCMF_FWS_TYPE_PKTTAG:
- case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
- default:
- fws->stats.tlv_invalid_type++;
- break;
- }
- if (err == BRCMF_FWS_RET_OK_SCHEDULE)
- status = BRCMF_FWS_RET_OK_SCHEDULE;
- signal_data += len + 2;
- data_len -= len + 2;
- }
-
- if (data_len != 0)
- fws->stats.tlv_parse_failed++;
-
- if (status == BRCMF_FWS_RET_OK_SCHEDULE)
- brcmf_fws_schedule_deq(fws);
-
- /* signalling processing result does
- * not affect the actual ethernet packet.
- */
- skb_pull(skb, siglen);
-
- /* this may be a signal-only packet
- */
- if (skb->len == 0)
- fws->stats.header_only_pkt++;
-}
-
-static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
- struct sk_buff *p)
-{
- struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
- struct brcmf_fws_mac_descriptor *entry = skcb->mac;
- u8 flags;
-
- if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)
- brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
- flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
- if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
- /*
- * 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);
- return brcmf_fws_hdrpush(fws, p);
-}
-
-static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
- struct sk_buff *skb, int fifo)
-{
- struct brcmf_fws_mac_descriptor *entry;
- struct sk_buff *pktout;
- int qidx, hslot;
- int rc = 0;
-
- entry = brcmf_skbcb(skb)->mac;
- if (entry->occupied) {
- qidx = 2 * fifo;
- if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
- qidx++;
-
- pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
- if (pktout == NULL) {
- brcmf_err("%s queue %d full\n", entry->name, qidx);
- rc = -ENOSPC;
- }
- } else {
- brcmf_err("%s entry removed\n", entry->name);
- rc = -ENOENT;
- }
-
- if (rc) {
- fws->stats.rollback_failed++;
- hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
- brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
- hslot, 0, 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)) {
- 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]) {
- fws->credits_borrowed[lender_ac]++;
- fws->fifo_credit[lender_ac]--;
- if (fws->fifo_credit[lender_ac] == 0)
- fws->fifo_credit_map &= ~(1 << 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;
-}
-
-static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
- struct sk_buff *skb)
-{
- struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
- struct brcmf_fws_mac_descriptor *entry;
- int rc;
- u8 ifidx;
- u8 data_offset;
-
- entry = skcb->mac;
- if (IS_ERR(entry))
- return PTR_ERR(entry);
-
- data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
- entry->transit_count++;
- if (entry->suppressed)
- entry->suppr_transit_count++;
- ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
- brcmf_fws_unlock(fws);
- rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
- brcmf_fws_lock(fws);
- brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
- skcb->if_flags, skcb->htod, rc);
- if (rc < 0) {
- entry->transit_count--;
- if (entry->suppressed)
- entry->suppr_transit_count--;
- (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
- goto rollback;
- }
-
- fws->stats.pkt2bus++;
- 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, fifo);
- return rc;
-}
-
-static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
- int fifo)
-{
- struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
- int rc, hslot;
-
- skcb->htod = 0;
- skcb->htod_seq = 0;
- hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
- brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
- brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
- brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
- rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
- if (!rc)
- skcb->mac->seq[fifo]++;
- else
- fws->stats.generic_error++;
- return rc;
-}
-
-int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
-{
- struct brcmf_pub *drvr = ifp->drvr;
- struct brcmf_fws_info *fws = drvr->fws;
- struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
- struct ethhdr *eh = (struct ethhdr *)(skb->data);
- int fifo = BRCMF_FWS_FIFO_BCMC;
- bool multicast = is_multicast_ether_addr(eh->h_dest);
- int rc = 0;
-
- brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
- /* determine the priority */
- if (!skb->priority)
- skb->priority = cfg80211_classify8021d(skb, NULL);
-
- drvr->tx_multicast += !!multicast;
-
- if (fws->avoid_queueing) {
- rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
- if (rc < 0)
- brcmf_txfinalize(ifp, skb, false);
- return rc;
- }
-
- /* set control buffer information */
- skcb->if_flags = 0;
- 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_fws_lock(fws);
- if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
- fws->borrow_defer_timestamp = jiffies +
- BRCMF_FWS_BORROW_DEFER_PERIOD;
-
- skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
- brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
- eh->h_dest, multicast, fifo);
- if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
- brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
- brcmf_fws_schedule_deq(fws);
- } else {
- brcmf_err("drop skb: no hanger slot\n");
- brcmf_txfinalize(ifp, skb, false);
- rc = -ENOMEM;
- }
- brcmf_fws_unlock(fws);
-
- return rc;
-}
-
-void brcmf_fws_reset_interface(struct brcmf_if *ifp)
-{
- struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
-
- brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
- if (!entry)
- return;
-
- brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
-}
-
-void brcmf_fws_add_interface(struct brcmf_if *ifp)
-{
- struct brcmf_fws_info *fws = ifp->drvr->fws;
- struct brcmf_fws_mac_descriptor *entry;
-
- if (!ifp->ndev)
- return;
-
- entry = &fws->desc.iface[ifp->ifidx];
- ifp->fws_desc = entry;
- 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)
-{
- struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
-
- if (!entry)
- return;
-
- brcmf_fws_lock(ifp->drvr->fws);
- ifp->fws_desc = NULL;
- brcmf_dbg(TRACE, "deleting %s\n", entry->name);
- brcmf_fws_macdesc_deinit(entry);
- brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
- brcmf_fws_unlock(ifp->drvr->fws);
-}
-
-static void brcmf_fws_dequeue_worker(struct work_struct *worker)
-{
- struct brcmf_fws_info *fws;
- struct brcmf_pub *drvr;
- struct sk_buff *skb;
- int fifo;
- u32 hslot;
- u32 ifidx;
- int ret;
-
- fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
- drvr = fws->drvr;
-
- brcmf_fws_lock(fws);
- for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
- fifo--) {
- if (!brcmf_fws_fc_active(fws)) {
- while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
- hslot = brcmf_skb_htod_tag_get_field(skb,
- HSLOT);
- brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
- &skb, true);
- ifidx = brcmf_skb_if_flags_get_field(skb,
- INDEX);
- /* Use proto layer to send data frame */
- brcmf_fws_unlock(fws);
- ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
- brcmf_fws_lock(fws);
- if (ret < 0)
- brcmf_txfinalize(brcmf_get_ifp(drvr,
- ifidx),
- skb, false);
- if (fws->bus_flow_blocked)
- break;
- }
- continue;
- }
- while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
- (fifo == BRCMF_FWS_FIFO_BCMC))) {
- skb = brcmf_fws_deq(fws, fifo);
- if (!skb)
- break;
- fws->fifo_credit[fifo]--;
- if (brcmf_fws_commit_skb(fws, fifo, skb))
- break;
- if (fws->bus_flow_blocked)
- break;
- }
- if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
- (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))
- break;
- if (fws->bus_flow_blocked)
- break;
- }
- }
- }
- brcmf_fws_unlock(fws);
-}
-
-#ifdef DEBUG
-static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
- struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats;
-
- seq_printf(seq,
- "header_pulls: %u\n"
- "header_only_pkt: %u\n"
- "tlv_parse_failed: %u\n"
- "tlv_invalid_type: %u\n"
- "mac_update_fails: %u\n"
- "ps_update_fails: %u\n"
- "if_update_fails: %u\n"
- "pkt2bus: %u\n"
- "generic_error: %u\n"
- "rollback_success: %u\n"
- "rollback_failed: %u\n"
- "delayq_full: %u\n"
- "supprq_full: %u\n"
- "txs_indicate: %u\n"
- "txs_discard: %u\n"
- "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"
- "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
- fwstats->header_pulls,
- fwstats->header_only_pkt,
- fwstats->tlv_parse_failed,
- fwstats->tlv_invalid_type,
- fwstats->mac_update_failed,
- fwstats->mac_ps_update_failed,
- fwstats->if_update_failed,
- fwstats->pkt2bus,
- fwstats->generic_error,
- fwstats->rollback_success,
- fwstats->rollback_failed,
- fwstats->delayq_full_error,
- fwstats->supprq_full_error,
- fwstats->txs_indicate,
- fwstats->txs_discard,
- 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->requested_sent[0],
- fwstats->requested_sent[1],
- fwstats->requested_sent[2],
- fwstats->requested_sent[3],
- fwstats->requested_sent[4]);
-
- return 0;
-}
-#else
-static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
-{
- return 0;
-}
-#endif
-
-int brcmf_fws_init(struct brcmf_pub *drvr)
-{
- struct brcmf_fws_info *fws;
- struct brcmf_if *ifp;
- u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
- int rc;
- u32 mode;
-
- drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
- if (!drvr->fws) {
- rc = -ENOMEM;
- goto fail;
- }
-
- fws = drvr->fws;
-
- spin_lock_init(&fws->spinlock);
-
- /* set linkage back */
- fws->drvr = drvr;
- fws->fcmode = fcmode;
-
- if ((drvr->bus_if->always_use_fws_queue == false) &&
- (fcmode == BRCMF_FWS_FCMODE_NONE)) {
- fws->avoid_queueing = true;
- brcmf_dbg(INFO, "FWS queueing will be avoided\n");
- return 0;
- }
-
- fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
- if (fws->fws_wq == NULL) {
- brcmf_err("workqueue creation failed\n");
- rc = -EBADF;
- goto fail;
- }
- INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
-
- /* enable firmware signalling if fcmode active */
- if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
- tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
- BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
- BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
- BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
-
- rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
- brcmf_fws_notify_credit_map);
- if (rc < 0) {
- 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
- * continue. Set mode back to none indicating not enabled.
- */
- fws->fw_signals = true;
- ifp = brcmf_get_ifp(drvr, 0);
- if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
- brcmf_err("failed to set bdcv2 tlv signaling\n");
- fws->fcmode = BRCMF_FWS_FCMODE_NONE;
- fws->fw_signals = false;
- }
-
- if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
- brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
-
- /* Enable seq number reuse, if supported */
- if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
- if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
- mode = 0;
- BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
- if (brcmf_fil_iovar_int_set(ifp,
- "wlfc_mode", mode) == 0) {
- BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
- }
- }
- }
-
- brcmf_fws_hanger_init(&fws->hanger);
- brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
- brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
- brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
- BRCMF_FWS_PSQ_LEN);
-
- /* create debugfs file for statistics */
- brcmf_debugfs_add_entry(drvr, "fws_stats",
- brcmf_debugfs_fws_stats_read);
-
- brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
- fws->fw_signals ? "enabled" : "disabled", tlv);
- return 0;
-
-fail:
- brcmf_fws_deinit(drvr);
- return rc;
-}
-
-void brcmf_fws_deinit(struct brcmf_pub *drvr)
-{
- struct brcmf_fws_info *fws = drvr->fws;
-
- if (!fws)
- return;
-
- if (drvr->fws->fws_wq)
- destroy_workqueue(drvr->fws->fws_wq);
-
- /* cleanup */
- brcmf_fws_lock(fws);
- brcmf_fws_cleanup(fws, -1);
- drvr->fws = NULL;
- brcmf_fws_unlock(fws);
-
- /* free top structure */
- kfree(fws);
-}
-
-bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
-{
- if (!fws->creditmap_received)
- return false;
-
- return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
-}
-
-void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
-{
- u32 hslot;
-
- if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
- brcmu_pkt_buf_free_skb(skb);
- return;
- }
- brcmf_fws_lock(fws);
- hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
- brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0);
- brcmf_fws_unlock(fws);
-}
-
-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++;
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
deleted file mode 100644
index 44e618f9d890..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+++ /dev/null
@@ -1,1561 +0,0 @@
-/* Copyright (c) 2014 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*******************************************************************************
- * Communicates with the dongle by using dcmd codes.
- * For certain dcmd codes, the dongle interprets string data from the host.
- ******************************************************************************/
-
-#include <linux/types.h>
-#include <linux/netdevice.h>
-
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-
-#include "core.h"
-#include "debug.h"
-#include "proto.h"
-#include "msgbuf.h"
-#include "commonring.h"
-#include "flowring.h"
-#include "bus.h"
-#include "tracepoint.h"
-
-
-#define MSGBUF_IOCTL_RESP_TIMEOUT 2000
-
-#define MSGBUF_TYPE_GEN_STATUS 0x1
-#define MSGBUF_TYPE_RING_STATUS 0x2
-#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3
-#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4
-#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5
-#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6
-#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7
-#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8
-#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9
-#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA
-#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB
-#define MSGBUF_TYPE_IOCTL_CMPLT 0xC
-#define MSGBUF_TYPE_EVENT_BUF_POST 0xD
-#define MSGBUF_TYPE_WL_EVENT 0xE
-#define MSGBUF_TYPE_TX_POST 0xF
-#define MSGBUF_TYPE_TX_STATUS 0x10
-#define MSGBUF_TYPE_RXBUF_POST 0x11
-#define MSGBUF_TYPE_RX_CMPLT 0x12
-#define MSGBUF_TYPE_LPBK_DMAXFER 0x13
-#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14
-
-#define NR_TX_PKTIDS 2048
-#define NR_RX_PKTIDS 1024
-
-#define BRCMF_IOCTL_REQ_PKTID 0xFFFE
-
-#define BRCMF_MSGBUF_MAX_PKT_SIZE 2048
-#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD 32
-#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST 8
-#define BRCMF_MSGBUF_MAX_EVENTBUF_POST 8
-
-#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3 0x01
-#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5
-
-#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32
-#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96
-
-#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96
-#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
-#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
-
-
-struct msgbuf_common_hdr {
- u8 msgtype;
- u8 ifidx;
- u8 flags;
- u8 rsvd0;
- __le32 request_id;
-};
-
-struct msgbuf_buf_addr {
- __le32 low_addr;
- __le32 high_addr;
-};
-
-struct msgbuf_ioctl_req_hdr {
- struct msgbuf_common_hdr msg;
- __le32 cmd;
- __le16 trans_id;
- __le16 input_buf_len;
- __le16 output_buf_len;
- __le16 rsvd0[3];
- struct msgbuf_buf_addr req_buf_addr;
- __le32 rsvd1[2];
-};
-
-struct msgbuf_tx_msghdr {
- struct msgbuf_common_hdr msg;
- u8 txhdr[ETH_HLEN];
- u8 flags;
- u8 seg_cnt;
- struct msgbuf_buf_addr metadata_buf_addr;
- struct msgbuf_buf_addr data_buf_addr;
- __le16 metadata_buf_len;
- __le16 data_len;
- __le32 rsvd0;
-};
-
-struct msgbuf_rx_bufpost {
- struct msgbuf_common_hdr msg;
- __le16 metadata_buf_len;
- __le16 data_buf_len;
- __le32 rsvd0;
- struct msgbuf_buf_addr metadata_buf_addr;
- struct msgbuf_buf_addr data_buf_addr;
-};
-
-struct msgbuf_rx_ioctl_resp_or_event {
- struct msgbuf_common_hdr msg;
- __le16 host_buf_len;
- __le16 rsvd0[3];
- struct msgbuf_buf_addr host_buf_addr;
- __le32 rsvd1[4];
-};
-
-struct msgbuf_completion_hdr {
- __le16 status;
- __le16 flow_ring_id;
-};
-
-struct msgbuf_rx_event {
- struct msgbuf_common_hdr msg;
- struct msgbuf_completion_hdr compl_hdr;
- __le16 event_data_len;
- __le16 seqnum;
- __le16 rsvd0[4];
-};
-
-struct msgbuf_ioctl_resp_hdr {
- struct msgbuf_common_hdr msg;
- struct msgbuf_completion_hdr compl_hdr;
- __le16 resp_len;
- __le16 trans_id;
- __le32 cmd;
- __le32 rsvd0;
-};
-
-struct msgbuf_tx_status {
- struct msgbuf_common_hdr msg;
- struct msgbuf_completion_hdr compl_hdr;
- __le16 metadata_len;
- __le16 tx_status;
-};
-
-struct msgbuf_rx_complete {
- struct msgbuf_common_hdr msg;
- struct msgbuf_completion_hdr compl_hdr;
- __le16 metadata_len;
- __le16 data_len;
- __le16 data_offset;
- __le16 flags;
- __le32 rx_status_0;
- __le32 rx_status_1;
- __le32 rsvd0;
-};
-
-struct msgbuf_tx_flowring_create_req {
- struct msgbuf_common_hdr msg;
- u8 da[ETH_ALEN];
- u8 sa[ETH_ALEN];
- u8 tid;
- u8 if_flags;
- __le16 flow_ring_id;
- u8 tc;
- u8 priority;
- __le16 int_vector;
- __le16 max_items;
- __le16 len_item;
- struct msgbuf_buf_addr flow_ring_addr;
-};
-
-struct msgbuf_tx_flowring_delete_req {
- struct msgbuf_common_hdr msg;
- __le16 flow_ring_id;
- __le16 reason;
- __le32 rsvd0[7];
-};
-
-struct msgbuf_flowring_create_resp {
- struct msgbuf_common_hdr msg;
- struct msgbuf_completion_hdr compl_hdr;
- __le32 rsvd0[3];
-};
-
-struct msgbuf_flowring_delete_resp {
- struct msgbuf_common_hdr msg;
- struct msgbuf_completion_hdr compl_hdr;
- __le32 rsvd0[3];
-};
-
-struct msgbuf_flowring_flush_resp {
- struct msgbuf_common_hdr msg;
- struct msgbuf_completion_hdr compl_hdr;
- __le32 rsvd0[3];
-};
-
-struct brcmf_msgbuf_work_item {
- struct list_head queue;
- u32 flowid;
- int ifidx;
- u8 sa[ETH_ALEN];
- u8 da[ETH_ALEN];
-};
-
-struct brcmf_msgbuf {
- struct brcmf_pub *drvr;
-
- struct brcmf_commonring **commonrings;
- struct brcmf_commonring **flowrings;
- dma_addr_t *flowring_dma_handle;
- u16 nrof_flowrings;
-
- u16 rx_dataoffset;
- u32 max_rxbufpost;
- u16 rx_metadata_offset;
- u32 rxbufpost;
-
- u32 max_ioctlrespbuf;
- u32 cur_ioctlrespbuf;
- u32 max_eventbuf;
- u32 cur_eventbuf;
-
- void *ioctbuf;
- dma_addr_t ioctbuf_handle;
- u32 ioctbuf_phys_hi;
- u32 ioctbuf_phys_lo;
- int ioctl_resp_status;
- u32 ioctl_resp_ret_len;
- u32 ioctl_resp_pktid;
-
- u16 data_seq_no;
- u16 ioctl_seq_no;
- u32 reqid;
- wait_queue_head_t ioctl_resp_wait;
- bool ctl_completed;
-
- struct brcmf_msgbuf_pktids *tx_pktids;
- struct brcmf_msgbuf_pktids *rx_pktids;
- struct brcmf_flowring *flow;
-
- struct workqueue_struct *txflow_wq;
- struct work_struct txflow_work;
- unsigned long *flow_map;
- unsigned long *txstatus_done_map;
-
- struct work_struct flowring_work;
- spinlock_t flowring_work_lock;
- struct list_head work_queue;
-};
-
-struct brcmf_msgbuf_pktid {
- atomic_t allocated;
- u16 data_offset;
- struct sk_buff *skb;
- dma_addr_t physaddr;
-};
-
-struct brcmf_msgbuf_pktids {
- u32 array_size;
- u32 last_allocated_idx;
- enum dma_data_direction direction;
- struct brcmf_msgbuf_pktid *array;
-};
-
-static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf);
-
-
-static struct brcmf_msgbuf_pktids *
-brcmf_msgbuf_init_pktids(u32 nr_array_entries,
- enum dma_data_direction direction)
-{
- struct brcmf_msgbuf_pktid *array;
- struct brcmf_msgbuf_pktids *pktids;
-
- array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL);
- if (!array)
- return NULL;
-
- pktids = kzalloc(sizeof(*pktids), GFP_KERNEL);
- if (!pktids) {
- kfree(array);
- return NULL;
- }
- pktids->array = array;
- pktids->array_size = nr_array_entries;
-
- return pktids;
-}
-
-
-static int
-brcmf_msgbuf_alloc_pktid(struct device *dev,
- struct brcmf_msgbuf_pktids *pktids,
- struct sk_buff *skb, u16 data_offset,
- dma_addr_t *physaddr, u32 *idx)
-{
- struct brcmf_msgbuf_pktid *array;
- u32 count;
-
- array = pktids->array;
-
- *physaddr = dma_map_single(dev, skb->data + data_offset,
- skb->len - data_offset, pktids->direction);
-
- if (dma_mapping_error(dev, *physaddr)) {
- brcmf_err("dma_map_single failed !!\n");
- return -ENOMEM;
- }
-
- *idx = pktids->last_allocated_idx;
-
- count = 0;
- do {
- (*idx)++;
- if (*idx == pktids->array_size)
- *idx = 0;
- if (array[*idx].allocated.counter == 0)
- if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0)
- break;
- count++;
- } while (count < pktids->array_size);
-
- if (count == pktids->array_size)
- return -ENOMEM;
-
- array[*idx].data_offset = data_offset;
- array[*idx].physaddr = *physaddr;
- array[*idx].skb = skb;
-
- pktids->last_allocated_idx = *idx;
-
- return 0;
-}
-
-
-static struct sk_buff *
-brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids,
- u32 idx)
-{
- struct brcmf_msgbuf_pktid *pktid;
- struct sk_buff *skb;
-
- if (idx >= pktids->array_size) {
- brcmf_err("Invalid packet id %d (max %d)\n", idx,
- pktids->array_size);
- return NULL;
- }
- if (pktids->array[idx].allocated.counter) {
- pktid = &pktids->array[idx];
- dma_unmap_single(dev, pktid->physaddr,
- pktid->skb->len - pktid->data_offset,
- pktids->direction);
- skb = pktid->skb;
- pktid->allocated.counter = 0;
- return skb;
- } else {
- brcmf_err("Invalid packet id %d (not in use)\n", idx);
- }
-
- return NULL;
-}
-
-
-static void
-brcmf_msgbuf_release_array(struct device *dev,
- struct brcmf_msgbuf_pktids *pktids)
-{
- struct brcmf_msgbuf_pktid *array;
- struct brcmf_msgbuf_pktid *pktid;
- u32 count;
-
- array = pktids->array;
- count = 0;
- do {
- if (array[count].allocated.counter) {
- pktid = &array[count];
- dma_unmap_single(dev, pktid->physaddr,
- pktid->skb->len - pktid->data_offset,
- pktids->direction);
- brcmu_pkt_buf_free_skb(pktid->skb);
- }
- count++;
- } while (count < pktids->array_size);
-
- kfree(array);
- kfree(pktids);
-}
-
-
-static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf)
-{
- if (msgbuf->rx_pktids)
- brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
- msgbuf->rx_pktids);
- if (msgbuf->tx_pktids)
- brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
- msgbuf->tx_pktids);
-}
-
-
-static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx,
- uint cmd, void *buf, uint len)
-{
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
- struct brcmf_commonring *commonring;
- struct msgbuf_ioctl_req_hdr *request;
- u16 buf_len;
- void *ret_ptr;
- int err;
-
- commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
- brcmf_commonring_lock(commonring);
- ret_ptr = brcmf_commonring_reserve_for_write(commonring);
- if (!ret_ptr) {
- brcmf_err("Failed to reserve space in commonring\n");
- brcmf_commonring_unlock(commonring);
- return -ENOMEM;
- }
-
- msgbuf->reqid++;
-
- request = (struct msgbuf_ioctl_req_hdr *)ret_ptr;
- request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ;
- request->msg.ifidx = (u8)ifidx;
- request->msg.flags = 0;
- request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID);
- request->cmd = cpu_to_le32(cmd);
- request->output_buf_len = cpu_to_le16(len);
- request->trans_id = cpu_to_le16(msgbuf->reqid);
-
- buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE);
- request->input_buf_len = cpu_to_le16(buf_len);
- request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi);
- request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo);
- if (buf)
- memcpy(msgbuf->ioctbuf, buf, buf_len);
- else
- memset(msgbuf->ioctbuf, 0, buf_len);
-
- err = brcmf_commonring_write_complete(commonring);
- brcmf_commonring_unlock(commonring);
-
- return err;
-}
-
-
-static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf)
-{
- return wait_event_timeout(msgbuf->ioctl_resp_wait,
- msgbuf->ctl_completed,
- msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT));
-}
-
-
-static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
-{
- msgbuf->ctl_completed = true;
- if (waitqueue_active(&msgbuf->ioctl_resp_wait))
- wake_up(&msgbuf->ioctl_resp_wait);
-}
-
-
-static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
- uint cmd, void *buf, uint len)
-{
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
- struct sk_buff *skb = NULL;
- int timeout;
- int err;
-
- brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len);
- msgbuf->ctl_completed = false;
- err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len);
- if (err)
- return err;
-
- timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf);
- if (!timeout) {
- brcmf_err("Timeout on response for query command\n");
- return -EIO;
- }
-
- skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->rx_pktids,
- msgbuf->ioctl_resp_pktid);
- if (msgbuf->ioctl_resp_ret_len != 0) {
- if (!skb)
- return -EBADF;
-
- memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
- len : msgbuf->ioctl_resp_ret_len);
- }
- brcmu_pkt_buf_free_skb(skb);
-
- return msgbuf->ioctl_resp_status;
-}
-
-
-static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx,
- uint cmd, void *buf, uint len)
-{
- return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len);
-}
-
-
-static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
- struct sk_buff *skb, struct brcmf_if **ifp)
-{
- return -ENODEV;
-}
-
-
-static void
-brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
-{
- u32 dma_sz;
- void *dma_buf;
-
- brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid);
-
- dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
- dma_buf = msgbuf->flowrings[flowid]->buf_addr;
- dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf,
- msgbuf->flowring_dma_handle[flowid]);
-
- brcmf_flowring_delete(msgbuf->flow, flowid);
-}
-
-
-static struct brcmf_msgbuf_work_item *
-brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf)
-{
- struct brcmf_msgbuf_work_item *work = NULL;
- ulong flags;
-
- spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
- if (!list_empty(&msgbuf->work_queue)) {
- work = list_first_entry(&msgbuf->work_queue,
- struct brcmf_msgbuf_work_item, queue);
- list_del(&work->queue);
- }
- spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
-
- return work;
-}
-
-
-static u32
-brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf,
- struct brcmf_msgbuf_work_item *work)
-{
- struct msgbuf_tx_flowring_create_req *create;
- struct brcmf_commonring *commonring;
- void *ret_ptr;
- u32 flowid;
- void *dma_buf;
- u32 dma_sz;
- u64 address;
- int err;
-
- flowid = work->flowid;
- dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
- dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz,
- &msgbuf->flowring_dma_handle[flowid],
- GFP_KERNEL);
- if (!dma_buf) {
- brcmf_err("dma_alloc_coherent failed\n");
- brcmf_flowring_delete(msgbuf->flow, flowid);
- return BRCMF_FLOWRING_INVALID_ID;
- }
-
- brcmf_commonring_config(msgbuf->flowrings[flowid],
- BRCMF_H2D_TXFLOWRING_MAX_ITEM,
- BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf);
-
- commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
- brcmf_commonring_lock(commonring);
- ret_ptr = brcmf_commonring_reserve_for_write(commonring);
- if (!ret_ptr) {
- brcmf_err("Failed to reserve space in commonring\n");
- brcmf_commonring_unlock(commonring);
- brcmf_msgbuf_remove_flowring(msgbuf, flowid);
- return BRCMF_FLOWRING_INVALID_ID;
- }
-
- create = (struct msgbuf_tx_flowring_create_req *)ret_ptr;
- create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE;
- create->msg.ifidx = work->ifidx;
- create->msg.request_id = 0;
- create->tid = brcmf_flowring_tid(msgbuf->flow, flowid);
- create->flow_ring_id = cpu_to_le16(flowid +
- BRCMF_NROF_H2D_COMMON_MSGRINGS);
- memcpy(create->sa, work->sa, ETH_ALEN);
- memcpy(create->da, work->da, ETH_ALEN);
- address = (u64)msgbuf->flowring_dma_handle[flowid];
- create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32);
- create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff);
- create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM);
- create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE);
-
- brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n",
- flowid, work->da, create->tid, work->ifidx);
-
- err = brcmf_commonring_write_complete(commonring);
- brcmf_commonring_unlock(commonring);
- if (err) {
- brcmf_err("Failed to write commonring\n");
- brcmf_msgbuf_remove_flowring(msgbuf, flowid);
- return BRCMF_FLOWRING_INVALID_ID;
- }
-
- return flowid;
-}
-
-
-static void brcmf_msgbuf_flowring_worker(struct work_struct *work)
-{
- struct brcmf_msgbuf *msgbuf;
- struct brcmf_msgbuf_work_item *create;
-
- msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work);
-
- while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) {
- brcmf_msgbuf_flowring_create_worker(msgbuf, create);
- kfree(create);
- }
-}
-
-
-static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx,
- struct sk_buff *skb)
-{
- struct brcmf_msgbuf_work_item *create;
- struct ethhdr *eh = (struct ethhdr *)(skb->data);
- u32 flowid;
- ulong flags;
-
- create = kzalloc(sizeof(*create), GFP_ATOMIC);
- if (create == NULL)
- return BRCMF_FLOWRING_INVALID_ID;
-
- flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest,
- skb->priority, ifidx);
- if (flowid == BRCMF_FLOWRING_INVALID_ID) {
- kfree(create);
- return flowid;
- }
-
- create->flowid = flowid;
- create->ifidx = ifidx;
- memcpy(create->sa, eh->h_source, ETH_ALEN);
- memcpy(create->da, eh->h_dest, ETH_ALEN);
-
- spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
- list_add_tail(&create->queue, &msgbuf->work_queue);
- spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
- schedule_work(&msgbuf->flowring_work);
-
- return flowid;
-}
-
-
-static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
-{
- struct brcmf_flowring *flow = msgbuf->flow;
- struct brcmf_commonring *commonring;
- void *ret_ptr;
- u32 count;
- struct sk_buff *skb;
- dma_addr_t physaddr;
- u32 pktid;
- struct msgbuf_tx_msghdr *tx_msghdr;
- u64 address;
-
- commonring = msgbuf->flowrings[flowid];
- if (!brcmf_commonring_write_available(commonring))
- return;
-
- brcmf_commonring_lock(commonring);
-
- count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1;
- while (brcmf_flowring_qlen(flow, flowid)) {
- skb = brcmf_flowring_dequeue(flow, flowid);
- if (skb == NULL) {
- brcmf_err("No SKB, but qlen %d\n",
- brcmf_flowring_qlen(flow, flowid));
- break;
- }
- skb_orphan(skb);
- if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->tx_pktids, skb, ETH_HLEN,
- &physaddr, &pktid)) {
- brcmf_flowring_reinsert(flow, flowid, skb);
- brcmf_err("No PKTID available !!\n");
- break;
- }
- ret_ptr = brcmf_commonring_reserve_for_write(commonring);
- if (!ret_ptr) {
- brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->tx_pktids, pktid);
- brcmf_flowring_reinsert(flow, flowid, skb);
- break;
- }
- count++;
-
- tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr;
-
- tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST;
- tx_msghdr->msg.request_id = cpu_to_le32(pktid);
- tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid);
- tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3;
- tx_msghdr->flags |= (skb->priority & 0x07) <<
- BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT;
- tx_msghdr->seg_cnt = 1;
- memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN);
- tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN);
- address = (u64)physaddr;
- tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32);
- tx_msghdr->data_buf_addr.low_addr =
- cpu_to_le32(address & 0xffffffff);
- tx_msghdr->metadata_buf_len = 0;
- tx_msghdr->metadata_buf_addr.high_addr = 0;
- tx_msghdr->metadata_buf_addr.low_addr = 0;
- atomic_inc(&commonring->outstanding_tx);
- if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) {
- brcmf_commonring_write_complete(commonring);
- count = 0;
- }
- }
- if (count)
- brcmf_commonring_write_complete(commonring);
- brcmf_commonring_unlock(commonring);
-}
-
-
-static void brcmf_msgbuf_txflow_worker(struct work_struct *worker)
-{
- struct brcmf_msgbuf *msgbuf;
- u32 flowid;
-
- msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work);
- for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) {
- clear_bit(flowid, msgbuf->flow_map);
- brcmf_msgbuf_txflow(msgbuf, flowid);
- }
-}
-
-
-static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid,
- bool force)
-{
- struct brcmf_commonring *commonring;
-
- set_bit(flowid, msgbuf->flow_map);
- commonring = msgbuf->flowrings[flowid];
- if ((force) || (atomic_read(&commonring->outstanding_tx) <
- BRCMF_MSGBUF_DELAY_TXWORKER_THRS))
- queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
-
- return 0;
-}
-
-
-static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx,
- u8 offset, struct sk_buff *skb)
-{
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
- struct brcmf_flowring *flow = msgbuf->flow;
- struct ethhdr *eh = (struct ethhdr *)(skb->data);
- u32 flowid;
- u32 queue_count;
- bool force;
-
- flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx);
- if (flowid == BRCMF_FLOWRING_INVALID_ID) {
- flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb);
- if (flowid == BRCMF_FLOWRING_INVALID_ID)
- return -ENOMEM;
- }
- queue_count = brcmf_flowring_enqueue(flow, flowid, skb);
- force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0);
- brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force);
-
- return 0;
-}
-
-
-static void
-brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
- enum proto_addr_mode addr_mode)
-{
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
-
- brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode);
-}
-
-
-static void
-brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
-{
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
-
- brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer);
-}
-
-
-static void
-brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
-{
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
-
- brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer);
-}
-
-
-static void
-brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf)
-{
- struct msgbuf_ioctl_resp_hdr *ioctl_resp;
-
- ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf;
-
- msgbuf->ioctl_resp_status =
- (s16)le16_to_cpu(ioctl_resp->compl_hdr.status);
- msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len);
- msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id);
-
- brcmf_msgbuf_ioctl_resp_wake(msgbuf);
-
- if (msgbuf->cur_ioctlrespbuf)
- msgbuf->cur_ioctlrespbuf--;
- brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
-}
-
-
-static void
-brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
-{
- struct brcmf_commonring *commonring;
- struct msgbuf_tx_status *tx_status;
- u32 idx;
- struct sk_buff *skb;
- u16 flowid;
-
- tx_status = (struct msgbuf_tx_status *)buf;
- idx = le32_to_cpu(tx_status->msg.request_id);
- flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id);
- flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
- skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->tx_pktids, idx);
- if (!skb)
- return;
-
- set_bit(flowid, msgbuf->txstatus_done_map);
- commonring = msgbuf->flowrings[flowid];
- atomic_dec(&commonring->outstanding_tx);
-
- brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
- skb, true);
-}
-
-
-static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count)
-{
- struct brcmf_commonring *commonring;
- void *ret_ptr;
- struct sk_buff *skb;
- u16 alloced;
- u32 pktlen;
- dma_addr_t physaddr;
- struct msgbuf_rx_bufpost *rx_bufpost;
- u64 address;
- u32 pktid;
- u32 i;
-
- commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
- ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
- count,
- &alloced);
- if (!ret_ptr) {
- brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n");
- return 0;
- }
-
- for (i = 0; i < alloced; i++) {
- rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr;
- memset(rx_bufpost, 0, sizeof(*rx_bufpost));
-
- skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);
-
- if (skb == NULL) {
- brcmf_err("Failed to alloc SKB\n");
- brcmf_commonring_write_cancel(commonring, alloced - i);
- break;
- }
-
- pktlen = skb->len;
- if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->rx_pktids, skb, 0,
- &physaddr, &pktid)) {
- dev_kfree_skb_any(skb);
- brcmf_err("No PKTID available !!\n");
- brcmf_commonring_write_cancel(commonring, alloced - i);
- break;
- }
-
- if (msgbuf->rx_metadata_offset) {
- address = (u64)physaddr;
- rx_bufpost->metadata_buf_len =
- cpu_to_le16(msgbuf->rx_metadata_offset);
- rx_bufpost->metadata_buf_addr.high_addr =
- cpu_to_le32(address >> 32);
- rx_bufpost->metadata_buf_addr.low_addr =
- cpu_to_le32(address & 0xffffffff);
-
- skb_pull(skb, msgbuf->rx_metadata_offset);
- pktlen = skb->len;
- physaddr += msgbuf->rx_metadata_offset;
- }
- rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST;
- rx_bufpost->msg.request_id = cpu_to_le32(pktid);
-
- address = (u64)physaddr;
- rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen);
- rx_bufpost->data_buf_addr.high_addr =
- cpu_to_le32(address >> 32);
- rx_bufpost->data_buf_addr.low_addr =
- cpu_to_le32(address & 0xffffffff);
-
- ret_ptr += brcmf_commonring_len_item(commonring);
- }
-
- if (i)
- brcmf_commonring_write_complete(commonring);
-
- return i;
-}
-
-
-static void
-brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf)
-{
- u32 fillbufs;
- u32 retcount;
-
- fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost;
-
- while (fillbufs) {
- retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs);
- if (!retcount)
- break;
- msgbuf->rxbufpost += retcount;
- fillbufs -= retcount;
- }
-}
-
-
-static void
-brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt)
-{
- msgbuf->rxbufpost -= rxcnt;
- if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost -
- BRCMF_MSGBUF_RXBUFPOST_THRESHOLD))
- brcmf_msgbuf_rxbuf_data_fill(msgbuf);
-}
-
-
-static u32
-brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf,
- u32 count)
-{
- struct brcmf_commonring *commonring;
- void *ret_ptr;
- struct sk_buff *skb;
- u16 alloced;
- u32 pktlen;
- dma_addr_t physaddr;
- struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost;
- u64 address;
- u32 pktid;
- u32 i;
-
- commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
- brcmf_commonring_lock(commonring);
- ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
- count,
- &alloced);
- if (!ret_ptr) {
- brcmf_err("Failed to reserve space in commonring\n");
- brcmf_commonring_unlock(commonring);
- return 0;
- }
-
- for (i = 0; i < alloced; i++) {
- rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr;
- memset(rx_bufpost, 0, sizeof(*rx_bufpost));
-
- skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);
-
- if (skb == NULL) {
- brcmf_err("Failed to alloc SKB\n");
- brcmf_commonring_write_cancel(commonring, alloced - i);
- break;
- }
-
- pktlen = skb->len;
- if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->rx_pktids, skb, 0,
- &physaddr, &pktid)) {
- dev_kfree_skb_any(skb);
- brcmf_err("No PKTID available !!\n");
- brcmf_commonring_write_cancel(commonring, alloced - i);
- break;
- }
- if (event_buf)
- rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST;
- else
- rx_bufpost->msg.msgtype =
- MSGBUF_TYPE_IOCTLRESP_BUF_POST;
- rx_bufpost->msg.request_id = cpu_to_le32(pktid);
-
- address = (u64)physaddr;
- rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen);
- rx_bufpost->host_buf_addr.high_addr =
- cpu_to_le32(address >> 32);
- rx_bufpost->host_buf_addr.low_addr =
- cpu_to_le32(address & 0xffffffff);
-
- ret_ptr += brcmf_commonring_len_item(commonring);
- }
-
- if (i)
- brcmf_commonring_write_complete(commonring);
-
- brcmf_commonring_unlock(commonring);
-
- return i;
-}
-
-
-static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf)
-{
- u32 count;
-
- count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf;
- count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count);
- msgbuf->cur_ioctlrespbuf += count;
-}
-
-
-static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf)
-{
- u32 count;
-
- count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf;
- count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count);
- msgbuf->cur_eventbuf += count;
-}
-
-
-static void
-brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
- u8 ifidx)
-{
- struct brcmf_if *ifp;
-
- ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
- if (!ifp || !ifp->ndev) {
- brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
- brcmu_pkt_buf_free_skb(skb);
- return;
- }
- brcmf_netif_rx(ifp, skb);
-}
-
-
-static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
-{
- struct msgbuf_rx_event *event;
- u32 idx;
- u16 buflen;
- struct sk_buff *skb;
-
- event = (struct msgbuf_rx_event *)buf;
- idx = le32_to_cpu(event->msg.request_id);
- buflen = le16_to_cpu(event->event_data_len);
-
- if (msgbuf->cur_eventbuf)
- msgbuf->cur_eventbuf--;
- brcmf_msgbuf_rxbuf_event_post(msgbuf);
-
- skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->rx_pktids, idx);
- if (!skb)
- return;
-
- if (msgbuf->rx_dataoffset)
- skb_pull(skb, msgbuf->rx_dataoffset);
-
- skb_trim(skb, buflen);
-
- brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx);
-}
-
-
-static void
-brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
-{
- struct msgbuf_rx_complete *rx_complete;
- struct sk_buff *skb;
- u16 data_offset;
- u16 buflen;
- u32 idx;
-
- brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
-
- rx_complete = (struct msgbuf_rx_complete *)buf;
- data_offset = le16_to_cpu(rx_complete->data_offset);
- buflen = le16_to_cpu(rx_complete->data_len);
- idx = le32_to_cpu(rx_complete->msg.request_id);
-
- skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
- msgbuf->rx_pktids, idx);
- if (!skb)
- return;
-
- if (data_offset)
- skb_pull(skb, data_offset);
- else if (msgbuf->rx_dataoffset)
- skb_pull(skb, msgbuf->rx_dataoffset);
-
- skb_trim(skb, buflen);
-
- brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx);
-}
-
-
-static void
-brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf,
- void *buf)
-{
- struct msgbuf_flowring_create_resp *flowring_create_resp;
- u16 status;
- u16 flowid;
-
- flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf;
-
- flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id);
- flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
- status = le16_to_cpu(flowring_create_resp->compl_hdr.status);
-
- if (status) {
- brcmf_err("Flowring creation failed, code %d\n", status);
- brcmf_msgbuf_remove_flowring(msgbuf, flowid);
- return;
- }
- brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid,
- status);
-
- brcmf_flowring_open(msgbuf->flow, flowid);
-
- brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
-}
-
-
-static void
-brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
- void *buf)
-{
- struct msgbuf_flowring_delete_resp *flowring_delete_resp;
- u16 status;
- u16 flowid;
-
- flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf;
-
- flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id);
- flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
- status = le16_to_cpu(flowring_delete_resp->compl_hdr.status);
-
- if (status) {
- brcmf_err("Flowring deletion failed, code %d\n", status);
- brcmf_flowring_delete(msgbuf->flow, flowid);
- return;
- }
- brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid,
- status);
-
- brcmf_msgbuf_remove_flowring(msgbuf, flowid);
-}
-
-
-static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
-{
- struct msgbuf_common_hdr *msg;
-
- msg = (struct msgbuf_common_hdr *)buf;
- switch (msg->msgtype) {
- case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT:
- brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n");
- brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf);
- break;
- case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT:
- brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n");
- brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf);
- break;
- case MSGBUF_TYPE_IOCTLPTR_REQ_ACK:
- brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n");
- break;
- case MSGBUF_TYPE_IOCTL_CMPLT:
- brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n");
- brcmf_msgbuf_process_ioctl_complete(msgbuf, buf);
- break;
- case MSGBUF_TYPE_WL_EVENT:
- brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n");
- brcmf_msgbuf_process_event(msgbuf, buf);
- break;
- case MSGBUF_TYPE_TX_STATUS:
- brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n");
- brcmf_msgbuf_process_txstatus(msgbuf, buf);
- break;
- case MSGBUF_TYPE_RX_CMPLT:
- brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n");
- brcmf_msgbuf_process_rx_complete(msgbuf, buf);
- break;
- default:
- brcmf_err("Unsupported msgtype %d\n", msg->msgtype);
- break;
- }
-}
-
-
-static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf,
- struct brcmf_commonring *commonring)
-{
- void *buf;
- u16 count;
- u16 processed;
-
-again:
- buf = brcmf_commonring_get_read_ptr(commonring, &count);
- if (buf == NULL)
- return;
-
- processed = 0;
- while (count) {
- brcmf_msgbuf_process_msgtype(msgbuf,
- buf + msgbuf->rx_dataoffset);
- buf += brcmf_commonring_len_item(commonring);
- processed++;
- if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
- brcmf_commonring_read_complete(commonring, processed);
- processed = 0;
- }
- count--;
- }
- if (processed)
- brcmf_commonring_read_complete(commonring, processed);
-
- if (commonring->r_ptr == 0)
- goto again;
-}
-
-
-int brcmf_proto_msgbuf_rx_trigger(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
- struct brcmf_commonring *commonring;
- void *buf;
- u32 flowid;
- int qlen;
-
- buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
- brcmf_msgbuf_process_rx(msgbuf, buf);
- buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
- brcmf_msgbuf_process_rx(msgbuf, buf);
- buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
- brcmf_msgbuf_process_rx(msgbuf, buf);
-
- for_each_set_bit(flowid, msgbuf->txstatus_done_map,
- msgbuf->nrof_flowrings) {
- clear_bit(flowid, msgbuf->txstatus_done_map);
- commonring = msgbuf->flowrings[flowid];
- qlen = brcmf_flowring_qlen(msgbuf->flow, flowid);
- if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) ||
- ((qlen) && (atomic_read(&commonring->outstanding_tx) <
- BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS)))
- brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
- }
-
- return 0;
-}
-
-
-void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid)
-{
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
- struct msgbuf_tx_flowring_delete_req *delete;
- struct brcmf_commonring *commonring;
- void *ret_ptr;
- u8 ifidx;
- int err;
-
- commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
- brcmf_commonring_lock(commonring);
- ret_ptr = brcmf_commonring_reserve_for_write(commonring);
- if (!ret_ptr) {
- brcmf_err("FW unaware, flowring will be removed !!\n");
- brcmf_commonring_unlock(commonring);
- brcmf_msgbuf_remove_flowring(msgbuf, flowid);
- return;
- }
-
- delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr;
-
- ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid);
-
- delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE;
- delete->msg.ifidx = ifidx;
- delete->msg.request_id = 0;
-
- delete->flow_ring_id = cpu_to_le16(flowid +
- BRCMF_NROF_H2D_COMMON_MSGRINGS);
- delete->reason = 0;
-
- brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n",
- flowid, ifidx);
-
- err = brcmf_commonring_write_complete(commonring);
- brcmf_commonring_unlock(commonring);
- if (err) {
- brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n");
- brcmf_msgbuf_remove_flowring(msgbuf, flowid);
- }
-}
-
-#ifdef DEBUG
-static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
- struct brcmf_pub *drvr = bus_if->drvr;
- struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
- struct brcmf_commonring *commonring;
- u16 i;
- struct brcmf_flowring_ring *ring;
- struct brcmf_flowring_hash *hash;
-
- commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
- seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n",
- commonring->r_ptr, commonring->w_ptr, commonring->depth);
- commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
- seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n",
- commonring->r_ptr, commonring->w_ptr, commonring->depth);
- commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
- seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n",
- commonring->r_ptr, commonring->w_ptr, commonring->depth);
- commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
- seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n",
- commonring->r_ptr, commonring->w_ptr, commonring->depth);
- commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
- seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n",
- commonring->r_ptr, commonring->w_ptr, commonring->depth);
-
- seq_printf(seq, "\nh2d_flowrings: depth %u\n",
- BRCMF_H2D_TXFLOWRING_MAX_ITEM);
- seq_puts(seq, "Active flowrings:\n");
- hash = msgbuf->flow->hash;
- for (i = 0; i < msgbuf->flow->nrofrings; i++) {
- if (!msgbuf->flow->rings[i])
- continue;
- ring = msgbuf->flow->rings[i];
- if (ring->status != RING_OPEN)
- continue;
- commonring = msgbuf->flowrings[i];
- hash = &msgbuf->flow->hash[ring->hash_id];
- seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n"
- " ifidx %u, fifo %u, da %pM\n",
- i, commonring->r_ptr, commonring->w_ptr,
- skb_queue_len(&ring->skblist), ring->blocked,
- hash->ifidx, hash->fifo, hash->mac);
- }
-
- return 0;
-}
-#else
-static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
-{
- return 0;
-}
-#endif
-
-int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
-{
- struct brcmf_bus_msgbuf *if_msgbuf;
- struct brcmf_msgbuf *msgbuf;
- u64 address;
- u32 count;
-
- if_msgbuf = drvr->bus_if->msgbuf;
- msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
- if (!msgbuf)
- goto fail;
-
- msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow");
- if (msgbuf->txflow_wq == NULL) {
- brcmf_err("workqueue creation failed\n");
- goto fail;
- }
- INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker);
- count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings);
- count = count * sizeof(unsigned long);
- msgbuf->flow_map = kzalloc(count, GFP_KERNEL);
- if (!msgbuf->flow_map)
- goto fail;
-
- msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL);
- if (!msgbuf->txstatus_done_map)
- goto fail;
-
- msgbuf->drvr = drvr;
- msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev,
- BRCMF_TX_IOCTL_MAX_MSG_SIZE,
- &msgbuf->ioctbuf_handle,
- GFP_KERNEL);
- if (!msgbuf->ioctbuf)
- goto fail;
- address = (u64)msgbuf->ioctbuf_handle;
- msgbuf->ioctbuf_phys_hi = address >> 32;
- msgbuf->ioctbuf_phys_lo = address & 0xffffffff;
-
- drvr->proto->hdrpull = brcmf_msgbuf_hdrpull;
- drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd;
- drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd;
- drvr->proto->txdata = brcmf_msgbuf_txdata;
- drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
- drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
- drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
- drvr->proto->pd = msgbuf;
-
- init_waitqueue_head(&msgbuf->ioctl_resp_wait);
-
- msgbuf->commonrings =
- (struct brcmf_commonring **)if_msgbuf->commonrings;
- msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings;
- msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings;
- msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings *
- sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL);
- if (!msgbuf->flowring_dma_handle)
- goto fail;
-
- msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset;
- msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost;
-
- msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST;
- msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST;
-
- msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS,
- DMA_TO_DEVICE);
- if (!msgbuf->tx_pktids)
- goto fail;
- msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS,
- DMA_FROM_DEVICE);
- if (!msgbuf->rx_pktids)
- goto fail;
-
- msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev,
- if_msgbuf->nrof_flowrings);
- if (!msgbuf->flow)
- goto fail;
-
-
- brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n",
- msgbuf->max_rxbufpost, msgbuf->max_eventbuf,
- msgbuf->max_ioctlrespbuf);
- count = 0;
- do {
- brcmf_msgbuf_rxbuf_data_fill(msgbuf);
- if (msgbuf->max_rxbufpost != msgbuf->rxbufpost)
- msleep(10);
- else
- break;
- count++;
- } while (count < 10);
- brcmf_msgbuf_rxbuf_event_post(msgbuf);
- brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
-
- INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker);
- spin_lock_init(&msgbuf->flowring_work_lock);
- INIT_LIST_HEAD(&msgbuf->work_queue);
-
- brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
-
- return 0;
-
-fail:
- if (msgbuf) {
- kfree(msgbuf->flow_map);
- kfree(msgbuf->txstatus_done_map);
- brcmf_msgbuf_release_pktids(msgbuf);
- kfree(msgbuf->flowring_dma_handle);
- if (msgbuf->ioctbuf)
- dma_free_coherent(drvr->bus_if->dev,
- BRCMF_TX_IOCTL_MAX_MSG_SIZE,
- msgbuf->ioctbuf,
- msgbuf->ioctbuf_handle);
- kfree(msgbuf);
- }
- return -ENOMEM;
-}
-
-
-void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr)
-{
- struct brcmf_msgbuf *msgbuf;
- struct brcmf_msgbuf_work_item *work;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (drvr->proto->pd) {
- msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
- cancel_work_sync(&msgbuf->flowring_work);
- while (!list_empty(&msgbuf->work_queue)) {
- work = list_first_entry(&msgbuf->work_queue,
- struct brcmf_msgbuf_work_item,
- queue);
- list_del(&work->queue);
- kfree(work);
- }
- kfree(msgbuf->flow_map);
- kfree(msgbuf->txstatus_done_map);
- if (msgbuf->txflow_wq)
- destroy_workqueue(msgbuf->txflow_wq);
-
- brcmf_flowring_detach(msgbuf->flow);
- dma_free_coherent(drvr->bus_if->dev,
- BRCMF_TX_IOCTL_MAX_MSG_SIZE,
- msgbuf->ioctbuf, msgbuf->ioctbuf_handle);
- brcmf_msgbuf_release_pktids(msgbuf);
- kfree(msgbuf->flowring_dma_handle);
- kfree(msgbuf);
- drvr->proto->pd = NULL;
- }
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
deleted file mode 100644
index d224b3dd72ed..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ /dev/null
@@ -1,2401 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <net/cfg80211.h>
-
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include <defs.h>
-#include "core.h"
-#include "debug.h"
-#include "fwil.h"
-#include "fwil_types.h"
-#include "p2p.h"
-#include "cfg80211.h"
-
-/* parameters used for p2p escan */
-#define P2PAPI_SCAN_NPROBES 1
-#define P2PAPI_SCAN_DWELL_TIME_MS 80
-#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40
-#define P2PAPI_SCAN_HOME_TIME_MS 60
-#define P2PAPI_SCAN_NPROBS_TIME_MS 30
-#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100
-#define WL_SCAN_CONNECT_DWELL_TIME_MS 200
-#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
-
-#define BRCMF_P2P_WILDCARD_SSID "DIRECT-"
-#define BRCMF_P2P_WILDCARD_SSID_LEN (sizeof(BRCMF_P2P_WILDCARD_SSID) - 1)
-
-#define SOCIAL_CHAN_1 1
-#define SOCIAL_CHAN_2 6
-#define SOCIAL_CHAN_3 11
-#define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \
- (channel == SOCIAL_CHAN_2) || \
- (channel == SOCIAL_CHAN_3))
-#define BRCMF_P2P_TEMP_CHAN SOCIAL_CHAN_3
-#define SOCIAL_CHAN_CNT 3
-#define AF_PEER_SEARCH_CNT 2
-
-#define BRCMF_SCB_TIMEOUT_VALUE 20
-
-#define P2P_VER 9 /* P2P version: 9=WiFi P2P v1.0 */
-#define P2P_PUB_AF_CATEGORY 0x04
-#define P2P_PUB_AF_ACTION 0x09
-#define P2P_AF_CATEGORY 0x7f
-#define P2P_OUI "\x50\x6F\x9A" /* P2P OUI */
-#define P2P_OUI_LEN 3 /* P2P OUI length */
-
-/* Action Frame Constants */
-#define DOT11_ACTION_HDR_LEN 2 /* action frame category + action */
-#define DOT11_ACTION_CAT_OFF 0 /* category offset */
-#define DOT11_ACTION_ACT_OFF 1 /* action offset */
-
-#define P2P_AF_DWELL_TIME 200
-#define P2P_AF_MIN_DWELL_TIME 100
-#define P2P_AF_MED_DWELL_TIME 400
-#define P2P_AF_LONG_DWELL_TIME 1000
-#define P2P_AF_TX_MAX_RETRY 1
-#define P2P_AF_MAX_WAIT_TIME 2000
-#define P2P_INVALID_CHANNEL -1
-#define P2P_CHANNEL_SYNC_RETRY 5
-#define P2P_AF_FRM_SCAN_MAX_WAIT 1500
-#define P2P_DEFAULT_SLEEP_TIME_VSDB 200
-
-/* WiFi P2P Public Action Frame OUI Subtypes */
-#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */
-#define P2P_PAF_GON_RSP 1 /* Group Owner Negotiation Rsp */
-#define P2P_PAF_GON_CONF 2 /* Group Owner Negotiation Confirm */
-#define P2P_PAF_INVITE_REQ 3 /* P2P Invitation Request */
-#define P2P_PAF_INVITE_RSP 4 /* P2P Invitation Response */
-#define P2P_PAF_DEVDIS_REQ 5 /* Device Discoverability Request */
-#define P2P_PAF_DEVDIS_RSP 6 /* Device Discoverability Response */
-#define P2P_PAF_PROVDIS_REQ 7 /* Provision Discovery Request */
-#define P2P_PAF_PROVDIS_RSP 8 /* Provision Discovery Response */
-#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */
-
-/* WiFi P2P Action Frame OUI Subtypes */
-#define P2P_AF_NOTICE_OF_ABSENCE 0 /* Notice of Absence */
-#define P2P_AF_PRESENCE_REQ 1 /* P2P Presence Request */
-#define P2P_AF_PRESENCE_RSP 2 /* P2P Presence Response */
-#define P2P_AF_GO_DISC_REQ 3 /* GO Discoverability Request */
-
-/* P2P Service Discovery related */
-#define P2PSD_ACTION_CATEGORY 0x04 /* Public action frame */
-#define P2PSD_ACTION_ID_GAS_IREQ 0x0a /* GAS Initial Request AF */
-#define P2PSD_ACTION_ID_GAS_IRESP 0x0b /* GAS Initial Response AF */
-#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */
-#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */
-
-/**
- * struct brcmf_p2p_disc_st_le - set discovery state in firmware.
- *
- * @state: requested discovery state (see enum brcmf_p2p_disc_state).
- * @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state.
- * @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state.
- */
-struct brcmf_p2p_disc_st_le {
- u8 state;
- __le16 chspec;
- __le16 dwell;
-};
-
-/**
- * enum brcmf_p2p_disc_state - P2P discovery state values
- *
- * @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE.
- * @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time.
- * @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE.
- */
-enum brcmf_p2p_disc_state {
- WL_P2P_DISC_ST_SCAN,
- WL_P2P_DISC_ST_LISTEN,
- WL_P2P_DISC_ST_SEARCH
-};
-
-/**
- * struct brcmf_p2p_scan_le - P2P specific scan request.
- *
- * @type: type of scan method requested (values: 'E' or 'S').
- * @reserved: reserved (ignored).
- * @eparams: parameters used for type 'E'.
- * @sparams: parameters used for type 'S'.
- */
-struct brcmf_p2p_scan_le {
- u8 type;
- u8 reserved[3];
- union {
- struct brcmf_escan_params_le eparams;
- struct brcmf_scan_params_le sparams;
- };
-};
-
-/**
- * struct brcmf_p2p_pub_act_frame - WiFi P2P Public Action Frame
- *
- * @category: P2P_PUB_AF_CATEGORY
- * @action: P2P_PUB_AF_ACTION
- * @oui[3]: P2P_OUI
- * @oui_type: OUI type - P2P_VER
- * @subtype: OUI subtype - P2P_TYPE_*
- * @dialog_token: nonzero, identifies req/rsp transaction
- * @elts[1]: Variable length information elements.
- */
-struct brcmf_p2p_pub_act_frame {
- u8 category;
- u8 action;
- u8 oui[3];
- u8 oui_type;
- u8 subtype;
- u8 dialog_token;
- u8 elts[1];
-};
-
-/**
- * struct brcmf_p2p_action_frame - WiFi P2P Action Frame
- *
- * @category: P2P_AF_CATEGORY
- * @OUI[3]: OUI - P2P_OUI
- * @type: OUI Type - P2P_VER
- * @subtype: OUI Subtype - P2P_AF_*
- * @dialog_token: nonzero, identifies req/resp tranaction
- * @elts[1]: Variable length information elements.
- */
-struct brcmf_p2p_action_frame {
- u8 category;
- u8 oui[3];
- u8 type;
- u8 subtype;
- u8 dialog_token;
- u8 elts[1];
-};
-
-/**
- * struct brcmf_p2psd_gas_pub_act_frame - Wi-Fi GAS Public Action Frame
- *
- * @category: 0x04 Public Action Frame
- * @action: 0x6c Advertisement Protocol
- * @dialog_token: nonzero, identifies req/rsp transaction
- * @query_data[1]: Query Data. SD gas ireq SD gas iresp
- */
-struct brcmf_p2psd_gas_pub_act_frame {
- u8 category;
- u8 action;
- u8 dialog_token;
- u8 query_data[1];
-};
-
-/**
- * struct brcmf_config_af_params - Action Frame Parameters for tx.
- *
- * @mpc_onoff: To make sure to send successfully action frame, we have to
- * turn off mpc 0: off, 1: on, (-1): do nothing
- * @search_channel: 1: search peer's channel to send af
- * extra_listen: keep the dwell time to get af response frame.
- */
-struct brcmf_config_af_params {
- s32 mpc_onoff;
- bool search_channel;
- bool extra_listen;
-};
-
-/**
- * brcmf_p2p_is_pub_action() - true if p2p public type frame.
- *
- * @frame: action frame data.
- * @frame_len: length of action frame data.
- *
- * Determine if action frame is p2p public action type
- */
-static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len)
-{
- struct brcmf_p2p_pub_act_frame *pact_frm;
-
- if (frame == NULL)
- return false;
-
- pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
- if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1)
- return false;
-
- if (pact_frm->category == P2P_PUB_AF_CATEGORY &&
- pact_frm->action == P2P_PUB_AF_ACTION &&
- pact_frm->oui_type == P2P_VER &&
- memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
- return true;
-
- return false;
-}
-
-/**
- * brcmf_p2p_is_p2p_action() - true if p2p action type frame.
- *
- * @frame: action frame data.
- * @frame_len: length of action frame data.
- *
- * Determine if action frame is p2p action type
- */
-static bool brcmf_p2p_is_p2p_action(void *frame, u32 frame_len)
-{
- struct brcmf_p2p_action_frame *act_frm;
-
- if (frame == NULL)
- return false;
-
- act_frm = (struct brcmf_p2p_action_frame *)frame;
- if (frame_len < sizeof(struct brcmf_p2p_action_frame) - 1)
- return false;
-
- if (act_frm->category == P2P_AF_CATEGORY &&
- act_frm->type == P2P_VER &&
- memcmp(act_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
- return true;
-
- return false;
-}
-
-/**
- * brcmf_p2p_is_gas_action() - true if p2p gas action type frame.
- *
- * @frame: action frame data.
- * @frame_len: length of action frame data.
- *
- * Determine if action frame is p2p gas action type
- */
-static bool brcmf_p2p_is_gas_action(void *frame, u32 frame_len)
-{
- struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
-
- if (frame == NULL)
- return false;
-
- sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
- if (frame_len < sizeof(struct brcmf_p2psd_gas_pub_act_frame) - 1)
- return false;
-
- if (sd_act_frm->category != P2PSD_ACTION_CATEGORY)
- return false;
-
- if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ ||
- sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP ||
- sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ ||
- sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP)
- return true;
-
- return false;
-}
-
-/**
- * brcmf_p2p_print_actframe() - debug print routine.
- *
- * @tx: Received or to be transmitted
- * @frame: action frame data.
- * @frame_len: length of action frame data.
- *
- * Print information about the p2p action frame
- */
-
-#ifdef DEBUG
-
-static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
-{
- struct brcmf_p2p_pub_act_frame *pact_frm;
- struct brcmf_p2p_action_frame *act_frm;
- struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
-
- if (!frame || frame_len <= 2)
- return;
-
- if (brcmf_p2p_is_pub_action(frame, frame_len)) {
- pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
- switch (pact_frm->subtype) {
- case P2P_PAF_GON_REQ:
- brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Req Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_GON_RSP:
- brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Rsp Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_GON_CONF:
- brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Confirm Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_INVITE_REQ:
- brcmf_dbg(TRACE, "%s P2P Invitation Request Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_INVITE_RSP:
- brcmf_dbg(TRACE, "%s P2P Invitation Response Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_DEVDIS_REQ:
- brcmf_dbg(TRACE, "%s P2P Device Discoverability Request Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_DEVDIS_RSP:
- brcmf_dbg(TRACE, "%s P2P Device Discoverability Response Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_PROVDIS_REQ:
- brcmf_dbg(TRACE, "%s P2P Provision Discovery Request Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_PAF_PROVDIS_RSP:
- brcmf_dbg(TRACE, "%s P2P Provision Discovery Response Frame\n",
- (tx) ? "TX" : "RX");
- break;
- default:
- brcmf_dbg(TRACE, "%s Unknown P2P Public Action Frame\n",
- (tx) ? "TX" : "RX");
- break;
- }
- } else if (brcmf_p2p_is_p2p_action(frame, frame_len)) {
- act_frm = (struct brcmf_p2p_action_frame *)frame;
- switch (act_frm->subtype) {
- case P2P_AF_NOTICE_OF_ABSENCE:
- brcmf_dbg(TRACE, "%s P2P Notice of Absence Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_AF_PRESENCE_REQ:
- brcmf_dbg(TRACE, "%s P2P Presence Request Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_AF_PRESENCE_RSP:
- brcmf_dbg(TRACE, "%s P2P Presence Response Frame\n",
- (tx) ? "TX" : "RX");
- break;
- case P2P_AF_GO_DISC_REQ:
- brcmf_dbg(TRACE, "%s P2P Discoverability Request Frame\n",
- (tx) ? "TX" : "RX");
- break;
- default:
- brcmf_dbg(TRACE, "%s Unknown P2P Action Frame\n",
- (tx) ? "TX" : "RX");
- }
-
- } else if (brcmf_p2p_is_gas_action(frame, frame_len)) {
- sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
- switch (sd_act_frm->action) {
- case P2PSD_ACTION_ID_GAS_IREQ:
- brcmf_dbg(TRACE, "%s P2P GAS Initial Request\n",
- (tx) ? "TX" : "RX");
- break;
- case P2PSD_ACTION_ID_GAS_IRESP:
- brcmf_dbg(TRACE, "%s P2P GAS Initial Response\n",
- (tx) ? "TX" : "RX");
- break;
- case P2PSD_ACTION_ID_GAS_CREQ:
- brcmf_dbg(TRACE, "%s P2P GAS Comback Request\n",
- (tx) ? "TX" : "RX");
- break;
- case P2PSD_ACTION_ID_GAS_CRESP:
- brcmf_dbg(TRACE, "%s P2P GAS Comback Response\n",
- (tx) ? "TX" : "RX");
- break;
- default:
- brcmf_dbg(TRACE, "%s Unknown P2P GAS Frame\n",
- (tx) ? "TX" : "RX");
- break;
- }
- }
-}
-
-#else
-
-static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
-{
-}
-
-#endif
-
-
-/**
- * brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation.
- *
- * @ifp: ifp to use for iovars (primary).
- * @p2p_mac: mac address to configure for p2p_da_override
- */
-static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac)
-{
- s32 ret = 0;
-
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
- brcmf_fil_iovar_int_set(ifp, "apsta", 1);
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
-
- /* In case of COB type, firmware has default mac address
- * After Initializing firmware, we have to set current mac address to
- * firmware for P2P device address. This must be done with discovery
- * disabled.
- */
- brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0);
-
- ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac,
- ETH_ALEN);
- if (ret)
- brcmf_err("failed to update device address ret %d\n", ret);
-
- return ret;
-}
-
-/**
- * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P.
- *
- * @p2p: P2P specific data.
- * @dev_addr: optional device address.
- *
- * P2P needs mac addresses for P2P device and interface. If no device
- * address it specified, these are derived from the primary net device, ie.
- * the permanent ethernet address of the device.
- */
-static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
-{
- struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
- bool local_admin = false;
-
- if (!dev_addr || is_zero_ether_addr(dev_addr)) {
- dev_addr = pri_ifp->mac_addr;
- local_admin = true;
- }
-
- /* Generate the P2P Device Address. This consists of the device's
- * primary MAC address with the locally administered bit set.
- */
- memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
- if (local_admin)
- p2p->dev_addr[0] |= 0x02;
-
- /* Generate the P2P Interface Address. If the discovery and connection
- * BSSCFGs need to simultaneously co-exist, then this address must be
- * different from the P2P Device Address, but also locally administered.
- */
- memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN);
- p2p->int_addr[0] |= 0x02;
- p2p->int_addr[4] ^= 0x80;
-}
-
-/**
- * brcmf_p2p_scan_is_p2p_request() - is cfg80211 scan request a P2P scan.
- *
- * @request: the scan request as received from cfg80211.
- *
- * returns true if one of the ssids in the request matches the
- * P2P wildcard ssid; otherwise returns false.
- */
-static bool brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request *request)
-{
- struct cfg80211_ssid *ssids = request->ssids;
- int i;
-
- for (i = 0; i < request->n_ssids; i++) {
- if (ssids[i].ssid_len != BRCMF_P2P_WILDCARD_SSID_LEN)
- continue;
-
- brcmf_dbg(INFO, "comparing ssid \"%s\"", ssids[i].ssid);
- if (!memcmp(BRCMF_P2P_WILDCARD_SSID, ssids[i].ssid,
- BRCMF_P2P_WILDCARD_SSID_LEN))
- return true;
- }
- return false;
-}
-
-/**
- * brcmf_p2p_set_discover_state - set discover state in firmware.
- *
- * @ifp: low-level interface object.
- * @state: discover state to set.
- * @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only).
- * @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only).
- */
-static s32 brcmf_p2p_set_discover_state(struct brcmf_if *ifp, u8 state,
- u16 chanspec, u16 listen_ms)
-{
- struct brcmf_p2p_disc_st_le discover_state;
- s32 ret = 0;
- brcmf_dbg(TRACE, "enter\n");
-
- discover_state.state = state;
- discover_state.chspec = cpu_to_le16(chanspec);
- discover_state.dwell = cpu_to_le16(listen_ms);
- ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state,
- sizeof(discover_state));
- return ret;
-}
-
-/**
- * brcmf_p2p_deinit_discovery() - disable P2P device discovery.
- *
- * @p2p: P2P specific data.
- *
- * Resets the discovery state and disables it in firmware.
- */
-static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p)
-{
- struct brcmf_cfg80211_vif *vif;
-
- brcmf_dbg(TRACE, "enter\n");
-
- /* Set the discovery state to SCAN */
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
- (void)brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
-
- /* Disable P2P discovery in the firmware */
- vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
- (void)brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 0);
-
- return 0;
-}
-
-/**
- * brcmf_p2p_enable_discovery() - initialize and configure discovery.
- *
- * @p2p: P2P specific data.
- *
- * Initializes the discovery device and configure the virtual interface.
- */
-static int brcmf_p2p_enable_discovery(struct brcmf_p2p_info *p2p)
-{
- struct brcmf_cfg80211_vif *vif;
- s32 ret = 0;
-
- brcmf_dbg(TRACE, "enter\n");
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
- if (!vif) {
- brcmf_err("P2P config device not available\n");
- ret = -EPERM;
- goto exit;
- }
-
- if (test_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status)) {
- brcmf_dbg(INFO, "P2P config device already configured\n");
- goto exit;
- }
-
- /* Re-initialize P2P Discovery in the firmware */
- vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
- ret = brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 1);
- if (ret < 0) {
- brcmf_err("set p2p_disc error\n");
- goto exit;
- }
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
- ret = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
- if (ret < 0) {
- brcmf_err("unable to set WL_P2P_DISC_ST_SCAN\n");
- goto exit;
- }
-
- /*
- * Set wsec to any non-zero value in the discovery bsscfg
- * to ensure our P2P probe responses have the privacy bit
- * set in the 802.11 WPA IE. Some peer devices may not
- * initiate WPS with us if this bit is not set.
- */
- ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED);
- if (ret < 0) {
- brcmf_err("wsec error %d\n", ret);
- goto exit;
- }
-
- set_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status);
-exit:
- return ret;
-}
-
-/**
- * brcmf_p2p_escan() - initiate a P2P scan.
- *
- * @p2p: P2P specific data.
- * @num_chans: number of channels to scan.
- * @chanspecs: channel parameters for @num_chans channels.
- * @search_state: P2P discover state to use.
- * @action: scan action to pass to firmware.
- * @bss_type: type of P2P bss.
- */
-static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
- u16 chanspecs[], s32 search_state, u16 action,
- enum p2p_bss_type bss_type)
-{
- s32 ret = 0;
- s32 memsize = offsetof(struct brcmf_p2p_scan_le,
- eparams.params_le.channel_list);
- s32 nprobes;
- s32 active;
- u32 i;
- u8 *memblk;
- struct brcmf_cfg80211_vif *vif;
- struct brcmf_p2p_scan_le *p2p_params;
- struct brcmf_scan_params_le *sparams;
- struct brcmf_ssid ssid;
-
- memsize += num_chans * sizeof(__le16);
- memblk = kzalloc(memsize, GFP_KERNEL);
- if (!memblk)
- return -ENOMEM;
-
- vif = p2p->bss_idx[bss_type].vif;
- if (vif == NULL) {
- brcmf_err("no vif for bss type %d\n", bss_type);
- ret = -EINVAL;
- goto exit;
- }
-
- switch (search_state) {
- case WL_P2P_DISC_ST_SEARCH:
- /*
- * If we in SEARCH STATE, we don't need to set SSID explictly
- * because dongle use P2P WILDCARD internally by default
- */
- /* use null ssid */
- ssid.SSID_len = 0;
- memset(ssid.SSID, 0, sizeof(ssid.SSID));
- break;
- case WL_P2P_DISC_ST_SCAN:
- /*
- * wpa_supplicant has p2p_find command with type social or
- * progressive. For progressive, we need to set the ssid to
- * P2P WILDCARD because we just do broadcast scan unless
- * setting SSID.
- */
- ssid.SSID_len = BRCMF_P2P_WILDCARD_SSID_LEN;
- memcpy(ssid.SSID, BRCMF_P2P_WILDCARD_SSID, ssid.SSID_len);
- break;
- default:
- brcmf_err(" invalid search state %d\n", search_state);
- ret = -EINVAL;
- goto exit;
- }
-
- brcmf_p2p_set_discover_state(vif->ifp, search_state, 0, 0);
-
- /*
- * set p2p scan parameters.
- */
- p2p_params = (struct brcmf_p2p_scan_le *)memblk;
- p2p_params->type = 'E';
-
- /* determine the scan engine parameters */
- sparams = &p2p_params->eparams.params_le;
- sparams->bss_type = DOT11_BSSTYPE_ANY;
- if (p2p->cfg->active_scan)
- sparams->scan_type = 0;
- else
- sparams->scan_type = 1;
-
- eth_broadcast_addr(sparams->bssid);
- if (ssid.SSID_len)
- memcpy(sparams->ssid_le.SSID, ssid.SSID, ssid.SSID_len);
- sparams->ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
- sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS);
-
- /*
- * SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan
- * supported by the supplicant.
- */
- if (num_chans == SOCIAL_CHAN_CNT || num_chans == (SOCIAL_CHAN_CNT + 1))
- active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
- else if (num_chans == AF_PEER_SEARCH_CNT)
- active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
- else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
- active = -1;
- else
- active = P2PAPI_SCAN_DWELL_TIME_MS;
-
- /* Override scan params to find a peer for a connection */
- if (num_chans == 1) {
- active = WL_SCAN_CONNECT_DWELL_TIME_MS;
- /* WAR to sync with presence period of VSDB GO.
- * send probe request more frequently
- */
- nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS;
- } else {
- nprobes = active / P2PAPI_SCAN_NPROBS_TIME_MS;
- }
-
- if (nprobes <= 0)
- nprobes = 1;
-
- brcmf_dbg(INFO, "nprobes # %d, active_time %d\n", nprobes, active);
- sparams->active_time = cpu_to_le32(active);
- sparams->nprobes = cpu_to_le32(nprobes);
- sparams->passive_time = cpu_to_le32(-1);
- sparams->channel_num = cpu_to_le32(num_chans &
- BRCMF_SCAN_PARAMS_COUNT_MASK);
- for (i = 0; i < num_chans; i++)
- sparams->channel_list[i] = cpu_to_le16(chanspecs[i]);
-
- /* set the escan specific parameters */
- p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
- p2p_params->eparams.action = cpu_to_le16(action);
- p2p_params->eparams.sync_id = cpu_to_le16(0x1234);
- /* perform p2p scan on primary device */
- ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize);
- if (!ret)
- set_bit(BRCMF_SCAN_STATUS_BUSY, &p2p->cfg->scan_status);
-exit:
- kfree(memblk);
- return ret;
-}
-
-/**
- * brcmf_p2p_run_escan() - escan callback for peer-to-peer.
- *
- * @cfg: driver private data for cfg80211 interface.
- * @ndev: net device for which scan is requested.
- * @request: scan request from cfg80211.
- * @action: scan action.
- *
- * Determines the P2P discovery state based to scan request parameters and
- * validates the channels in the request.
- */
-static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
- struct brcmf_if *ifp,
- struct cfg80211_scan_request *request,
- u16 action)
-{
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- s32 err = 0;
- s32 search_state = WL_P2P_DISC_ST_SCAN;
- struct brcmf_cfg80211_vif *vif;
- struct net_device *dev = NULL;
- int i, num_nodfs = 0;
- u16 *chanspecs;
-
- brcmf_dbg(TRACE, "enter\n");
-
- if (!request) {
- err = -EINVAL;
- goto exit;
- }
-
- if (request->n_channels) {
- chanspecs = kcalloc(request->n_channels, sizeof(*chanspecs),
- GFP_KERNEL);
- if (!chanspecs) {
- err = -ENOMEM;
- goto exit;
- }
- vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
- if (vif)
- dev = vif->wdev.netdev;
- if (request->n_channels == 3 &&
- request->channels[0]->hw_value == SOCIAL_CHAN_1 &&
- request->channels[1]->hw_value == SOCIAL_CHAN_2 &&
- request->channels[2]->hw_value == SOCIAL_CHAN_3) {
- /* SOCIAL CHANNELS 1, 6, 11 */
- search_state = WL_P2P_DISC_ST_SEARCH;
- brcmf_dbg(INFO, "P2P SEARCH PHASE START\n");
- } else if (dev != NULL &&
- vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
- /* If you are already a GO, then do SEARCH only */
- brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n");
- search_state = WL_P2P_DISC_ST_SEARCH;
- } else {
- brcmf_dbg(INFO, "P2P SCAN STATE START\n");
- }
-
- /*
- * no P2P scanning on passive or DFS channels.
- */
- for (i = 0; i < request->n_channels; i++) {
- struct ieee80211_channel *chan = request->channels[i];
-
- if (chan->flags & (IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_NO_IR))
- continue;
-
- chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf,
- chan);
- brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n",
- num_nodfs, chan->hw_value, chanspecs[i]);
- num_nodfs++;
- }
- err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state,
- action, P2PAPI_BSSCFG_DEVICE);
- kfree(chanspecs);
- }
-exit:
- if (err)
- brcmf_err("error (%d)\n", err);
- return err;
-}
-
-
-/**
- * brcmf_p2p_find_listen_channel() - find listen channel in ie string.
- *
- * @ie: string of information elements.
- * @ie_len: length of string.
- *
- * Scan ie for p2p ie and look for attribute 6 channel. If available determine
- * channel and return it.
- */
-static s32 brcmf_p2p_find_listen_channel(const u8 *ie, u32 ie_len)
-{
- u8 channel_ie[5];
- s32 listen_channel;
- s32 err;
-
- err = cfg80211_get_p2p_attr(ie, ie_len,
- IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
- channel_ie, sizeof(channel_ie));
- if (err < 0)
- return err;
-
- /* listen channel subel length format: */
- /* 3(country) + 1(op. class) + 1(chan num) */
- listen_channel = (s32)channel_ie[3 + 1];
-
- if (listen_channel == SOCIAL_CHAN_1 ||
- listen_channel == SOCIAL_CHAN_2 ||
- listen_channel == SOCIAL_CHAN_3) {
- brcmf_dbg(INFO, "Found my Listen Channel %d\n", listen_channel);
- return listen_channel;
- }
-
- return -EPERM;
-}
-
-
-/**
- * brcmf_p2p_scan_prep() - prepare scan based on request.
- *
- * @wiphy: wiphy device.
- * @request: scan request from cfg80211.
- * @vif: vif on which scan request is to be executed.
- *
- * Prepare the scan appropriately for type of scan requested. Overrides the
- * escan .run() callback for peer-to-peer scanning.
- */
-int brcmf_p2p_scan_prep(struct wiphy *wiphy,
- struct cfg80211_scan_request *request,
- struct brcmf_cfg80211_vif *vif)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- int err = 0;
-
- if (brcmf_p2p_scan_is_p2p_request(request)) {
- /* find my listen channel */
- err = brcmf_p2p_find_listen_channel(request->ie,
- request->ie_len);
- if (err < 0)
- return err;
-
- p2p->afx_hdl.my_listen_chan = err;
-
- clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
- brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
-
- err = brcmf_p2p_enable_discovery(p2p);
- if (err)
- return err;
-
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
-
- /* override .run_escan() callback. */
- cfg->escan_info.run = brcmf_p2p_run_escan;
- }
- err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
- request->ie, request->ie_len);
- return err;
-}
-
-
-/**
- * brcmf_p2p_discover_listen() - set firmware to discover listen state.
- *
- * @p2p: p2p device.
- * @channel: channel nr for discover listen.
- * @duration: time in ms to stay on channel.
- *
- */
-static s32
-brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
-{
- struct brcmf_cfg80211_vif *vif;
- struct brcmu_chan ch;
- s32 err = 0;
-
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
- if (!vif) {
- brcmf_err("Discovery is not set, so we have nothing to do\n");
- err = -EPERM;
- goto exit;
- }
-
- if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status)) {
- brcmf_err("Previous LISTEN is not completed yet\n");
- /* WAR: prevent cookie mismatch in wpa_supplicant return OK */
- goto exit;
- }
-
- ch.chnum = channel;
- ch.bw = BRCMU_CHAN_BW_20;
- p2p->cfg->d11inf.encchspec(&ch);
- err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN,
- ch.chspec, (u16)duration);
- if (!err) {
- set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status);
- p2p->remain_on_channel_cookie++;
- }
-exit:
- return err;
-}
-
-
-/**
- * brcmf_p2p_remain_on_channel() - put device on channel and stay there.
- *
- * @wiphy: wiphy device.
- * @channel: channel to stay on.
- * @duration: time in ms to remain on channel.
- *
- */
-int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct ieee80211_channel *channel,
- unsigned int duration, u64 *cookie)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- s32 err;
- u16 channel_nr;
-
- channel_nr = ieee80211_frequency_to_channel(channel->center_freq);
- brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr,
- duration);
-
- err = brcmf_p2p_enable_discovery(p2p);
- if (err)
- goto exit;
- err = brcmf_p2p_discover_listen(p2p, channel_nr, duration);
- if (err)
- goto exit;
-
- memcpy(&p2p->remain_on_channel, channel, sizeof(*channel));
- *cookie = p2p->remain_on_channel_cookie;
- cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL);
-
-exit:
- return err;
-}
-
-
-/**
- * brcmf_p2p_notify_listen_complete() - p2p listen has completed.
- *
- * @ifp: interfac control.
- * @e: event message. Not used, to make it usable for fweh event dispatcher.
- * @data: payload of message. Not used.
- *
- */
-int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- struct brcmf_p2p_info *p2p = &cfg->p2p;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN,
- &p2p->status)) {
- if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
- &p2p->status)) {
- clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
- &p2p->status);
- brcmf_dbg(INFO, "Listen DONE, wake up wait_next_af\n");
- complete(&p2p->wait_next_af);
- }
-
- cfg80211_remain_on_channel_expired(&ifp->vif->wdev,
- p2p->remain_on_channel_cookie,
- &p2p->remain_on_channel,
- GFP_KERNEL);
- }
- return 0;
-}
-
-
-/**
- * brcmf_p2p_cancel_remain_on_channel() - cancel p2p listen state.
- *
- * @ifp: interfac control.
- *
- */
-void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp)
-{
- if (!ifp)
- return;
- brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
- brcmf_p2p_notify_listen_complete(ifp, NULL, NULL);
-}
-
-
-/**
- * brcmf_p2p_act_frm_search() - search function for action frame.
- *
- * @p2p: p2p device.
- * channel: channel on which action frame is to be trasmitted.
- *
- * search function to reach at common channel to send action frame. When
- * channel is 0 then all social channels will be used to send af
- */
-static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
-{
- s32 err;
- u32 channel_cnt;
- u16 *default_chan_list;
- u32 i;
- struct brcmu_chan ch;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (channel)
- channel_cnt = AF_PEER_SEARCH_CNT;
- else
- channel_cnt = SOCIAL_CHAN_CNT;
- default_chan_list = kzalloc(channel_cnt * sizeof(*default_chan_list),
- GFP_KERNEL);
- if (default_chan_list == NULL) {
- brcmf_err("channel list allocation failed\n");
- err = -ENOMEM;
- goto exit;
- }
- ch.bw = BRCMU_CHAN_BW_20;
- if (channel) {
- ch.chnum = channel;
- p2p->cfg->d11inf.encchspec(&ch);
- /* insert same channel to the chan_list */
- for (i = 0; i < channel_cnt; i++)
- default_chan_list[i] = ch.chspec;
- } else {
- ch.chnum = SOCIAL_CHAN_1;
- p2p->cfg->d11inf.encchspec(&ch);
- default_chan_list[0] = ch.chspec;
- ch.chnum = SOCIAL_CHAN_2;
- p2p->cfg->d11inf.encchspec(&ch);
- default_chan_list[1] = ch.chspec;
- ch.chnum = SOCIAL_CHAN_3;
- p2p->cfg->d11inf.encchspec(&ch);
- default_chan_list[2] = ch.chspec;
- }
- err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list,
- WL_P2P_DISC_ST_SEARCH, WL_ESCAN_ACTION_START,
- P2PAPI_BSSCFG_DEVICE);
- kfree(default_chan_list);
-exit:
- return err;
-}
-
-
-/**
- * brcmf_p2p_afx_handler() - afx worker thread.
- *
- * @work:
- *
- */
-static void brcmf_p2p_afx_handler(struct work_struct *work)
-{
- struct afx_hdl *afx_hdl = container_of(work, struct afx_hdl, afx_work);
- struct brcmf_p2p_info *p2p = container_of(afx_hdl,
- struct brcmf_p2p_info,
- afx_hdl);
- s32 err;
-
- if (!afx_hdl->is_active)
- return;
-
- if (afx_hdl->is_listen && afx_hdl->my_listen_chan)
- /* 100ms ~ 300ms */
- err = brcmf_p2p_discover_listen(p2p, afx_hdl->my_listen_chan,
- 100 * (1 + prandom_u32() % 3));
- else
- err = brcmf_p2p_act_frm_search(p2p, afx_hdl->peer_listen_chan);
-
- if (err) {
- brcmf_err("ERROR occurred! value is (%d)\n", err);
- if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
- &p2p->status))
- complete(&afx_hdl->act_frm_scan);
- }
-}
-
-
-/**
- * brcmf_p2p_af_searching_channel() - search channel.
- *
- * @p2p: p2p device info struct.
- *
- */
-static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
-{
- struct afx_hdl *afx_hdl = &p2p->afx_hdl;
- struct brcmf_cfg80211_vif *pri_vif;
- unsigned long duration;
- s32 retry;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
-
- reinit_completion(&afx_hdl->act_frm_scan);
- set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
- afx_hdl->is_active = true;
- afx_hdl->peer_chan = P2P_INVALID_CHANNEL;
-
- /* Loop to wait until we find a peer's channel or the
- * pending action frame tx is cancelled.
- */
- retry = 0;
- duration = msecs_to_jiffies(P2P_AF_FRM_SCAN_MAX_WAIT);
- while ((retry < P2P_CHANNEL_SYNC_RETRY) &&
- (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) {
- afx_hdl->is_listen = false;
- brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n",
- retry);
- /* search peer on peer's listen channel */
- schedule_work(&afx_hdl->afx_work);
- wait_for_completion_timeout(&afx_hdl->act_frm_scan, duration);
- if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
- (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
- &p2p->status)))
- break;
-
- if (afx_hdl->my_listen_chan) {
- brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n",
- afx_hdl->my_listen_chan);
- /* listen on my listen channel */
- afx_hdl->is_listen = true;
- schedule_work(&afx_hdl->afx_work);
- wait_for_completion_timeout(&afx_hdl->act_frm_scan,
- duration);
- }
- if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
- (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
- &p2p->status)))
- break;
- retry++;
-
- /* if sta is connected or connecting, sleep for a while before
- * retry af tx or finding a peer
- */
- if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &pri_vif->sme_state) ||
- test_bit(BRCMF_VIF_STATUS_CONNECTING, &pri_vif->sme_state))
- msleep(P2P_DEFAULT_SLEEP_TIME_VSDB);
- }
-
- brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n",
- afx_hdl->peer_chan);
- afx_hdl->is_active = false;
-
- clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
-
- return afx_hdl->peer_chan;
-}
-
-
-/**
- * brcmf_p2p_scan_finding_common_channel() - was escan used for finding channel
- *
- * @cfg: common configuration struct.
- * @bi: bss info struct, result from scan.
- *
- */
-bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
- struct brcmf_bss_info_le *bi)
-
-{
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct afx_hdl *afx_hdl = &p2p->afx_hdl;
- struct brcmu_chan ch;
- u8 *ie;
- s32 err;
- u8 p2p_dev_addr[ETH_ALEN];
-
- if (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))
- return false;
-
- if (bi == NULL) {
- brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n");
- if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)
- complete(&afx_hdl->act_frm_scan);
- return true;
- }
-
- ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
- memset(p2p_dev_addr, 0, sizeof(p2p_dev_addr));
- err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
- IEEE80211_P2P_ATTR_DEVICE_INFO,
- p2p_dev_addr, sizeof(p2p_dev_addr));
- if (err < 0)
- err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
- IEEE80211_P2P_ATTR_DEVICE_ID,
- p2p_dev_addr, sizeof(p2p_dev_addr));
- if ((err >= 0) &&
- (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) {
- if (!bi->ctl_ch) {
- ch.chspec = le16_to_cpu(bi->chanspec);
- cfg->d11inf.decchspec(&ch);
- bi->ctl_ch = ch.chnum;
- }
- afx_hdl->peer_chan = bi->ctl_ch;
- brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n",
- afx_hdl->tx_dst_addr, afx_hdl->peer_chan);
- complete(&afx_hdl->act_frm_scan);
- }
- return true;
-}
-
-/**
- * brcmf_p2p_stop_wait_next_action_frame() - finish scan if af tx complete.
- *
- * @cfg: common configuration struct.
- *
- */
-static void
-brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg)
-{
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_if *ifp = cfg->escan_info.ifp;
-
- if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) &&
- (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) ||
- test_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status))) {
- brcmf_dbg(TRACE, "*** Wake UP ** abort actframe iovar\n");
- /* if channel is not zero, "actfame" uses off channel scan.
- * So abort scan for off channel completion.
- */
- if (p2p->af_sent_channel)
- brcmf_notify_escan_complete(cfg, ifp, true, true);
- } else if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
- &p2p->status)) {
- brcmf_dbg(TRACE, "*** Wake UP ** abort listen for next af frame\n");
- /* So abort scan to cancel listen */
- brcmf_notify_escan_complete(cfg, ifp, true, true);
- }
-}
-
-
-/**
- * brcmf_p2p_gon_req_collision() - Check if go negotiaton collission
- *
- * @p2p: p2p device info struct.
- *
- * return true if recevied action frame is to be dropped.
- */
-static bool
-brcmf_p2p_gon_req_collision(struct brcmf_p2p_info *p2p, u8 *mac)
-{
- struct brcmf_cfg80211_info *cfg = p2p->cfg;
- struct brcmf_if *ifp;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (!test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) ||
- !p2p->gon_req_action)
- return false;
-
- brcmf_dbg(TRACE, "GO Negotiation Request COLLISION !!!\n");
- /* if sa(peer) addr is less than da(my) addr, then this device
- * process peer's gon request and block to send gon req.
- * if not (sa addr > da addr),
- * this device will process gon request and drop gon req of peer.
- */
- ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp;
- if (memcmp(mac, ifp->mac_addr, ETH_ALEN) < 0) {
- brcmf_dbg(INFO, "Block transmit gon req !!!\n");
- p2p->block_gon_req_tx = true;
- /* if we are finding a common channel for sending af,
- * do not scan more to block to send current gon req
- */
- if (test_and_clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
- &p2p->status))
- complete(&p2p->afx_hdl.act_frm_scan);
- if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
- &p2p->status))
- brcmf_p2p_stop_wait_next_action_frame(cfg);
- return false;
- }
-
- /* drop gon request of peer to process gon request by this device. */
- brcmf_dbg(INFO, "Drop received gon req !!!\n");
-
- return true;
-}
-
-
-/**
- * brcmf_p2p_notify_action_frame_rx() - received action frame.
- *
- * @ifp: interfac control.
- * @e: event message. Not used, to make it usable for fweh event dispatcher.
- * @data: payload of message, containing action frame data.
- *
- */
-int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct afx_hdl *afx_hdl = &p2p->afx_hdl;
- struct wireless_dev *wdev;
- u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data);
- struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
- u8 *frame = (u8 *)(rxframe + 1);
- struct brcmf_p2p_pub_act_frame *act_frm;
- struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
- struct brcmu_chan ch;
- struct ieee80211_mgmt *mgmt_frame;
- s32 freq;
- u16 mgmt_type;
- u8 action;
-
- ch.chspec = be16_to_cpu(rxframe->chanspec);
- cfg->d11inf.decchspec(&ch);
- /* Check if wpa_supplicant has registered for this frame */
- brcmf_dbg(INFO, "ifp->vif->mgmt_rx_reg %04x\n", ifp->vif->mgmt_rx_reg);
- mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4;
- if ((ifp->vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
- return 0;
-
- brcmf_p2p_print_actframe(false, frame, mgmt_frame_len);
-
- action = P2P_PAF_SUBTYPE_INVALID;
- if (brcmf_p2p_is_pub_action(frame, mgmt_frame_len)) {
- act_frm = (struct brcmf_p2p_pub_act_frame *)frame;
- action = act_frm->subtype;
- if ((action == P2P_PAF_GON_REQ) &&
- (brcmf_p2p_gon_req_collision(p2p, (u8 *)e->addr))) {
- if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
- &p2p->status) &&
- (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
- afx_hdl->peer_chan = ch.chnum;
- brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n",
- afx_hdl->peer_chan);
- complete(&afx_hdl->act_frm_scan);
- }
- return 0;
- }
- /* After complete GO Negotiation, roll back to mpc mode */
- if ((action == P2P_PAF_GON_CONF) ||
- (action == P2P_PAF_PROVDIS_RSP))
- brcmf_set_mpc(ifp, 1);
- if (action == P2P_PAF_GON_CONF) {
- brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
- clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
- }
- } else if (brcmf_p2p_is_gas_action(frame, mgmt_frame_len)) {
- sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
- action = sd_act_frm->action;
- }
-
- if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
- (p2p->next_af_subtype == action)) {
- brcmf_dbg(TRACE, "We got a right next frame! (%d)\n", action);
- clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
- &p2p->status);
- /* Stop waiting for next AF. */
- brcmf_p2p_stop_wait_next_action_frame(cfg);
- }
-
- mgmt_frame = kzalloc(offsetof(struct ieee80211_mgmt, u) +
- mgmt_frame_len, GFP_KERNEL);
- if (!mgmt_frame) {
- brcmf_err("No memory available for action frame\n");
- return -ENOMEM;
- }
- memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN);
- brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid,
- ETH_ALEN);
- memcpy(mgmt_frame->sa, e->addr, ETH_ALEN);
- mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION);
- memcpy(&mgmt_frame->u, frame, mgmt_frame_len);
- mgmt_frame_len += offsetof(struct ieee80211_mgmt, u);
-
- freq = ieee80211_channel_to_frequency(ch.chnum,
- ch.band == BRCMU_CHAN_BAND_2G ?
- IEEE80211_BAND_2GHZ :
- IEEE80211_BAND_5GHZ);
-
- wdev = &ifp->vif->wdev;
- cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0);
-
- kfree(mgmt_frame);
- return 0;
-}
-
-
-/**
- * brcmf_p2p_notify_action_tx_complete() - transmit action frame complete
- *
- * @ifp: interfac control.
- * @e: event message. Not used, to make it usable for fweh event dispatcher.
- * @data: not used.
- *
- */
-int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- struct brcmf_p2p_info *p2p = &cfg->p2p;
-
- brcmf_dbg(INFO, "Enter: event %s, status=%d\n",
- e->event_code == BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE ?
- "ACTION_FRAME_OFF_CHAN_COMPLETE" : "ACTION_FRAME_COMPLETE",
- e->status);
-
- if (!test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status))
- return 0;
-
- if (e->event_code == BRCMF_E_ACTION_FRAME_COMPLETE) {
- if (e->status == BRCMF_E_STATUS_SUCCESS)
- set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
- &p2p->status);
- else {
- set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
- /* If there is no ack, we don't need to wait for
- * WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE event
- */
- brcmf_p2p_stop_wait_next_action_frame(cfg);
- }
-
- } else {
- complete(&p2p->send_af_done);
- }
- return 0;
-}
-
-
-/**
- * brcmf_p2p_tx_action_frame() - send action frame over fil.
- *
- * @p2p: p2p info struct for vif.
- * @af_params: action frame data/info.
- *
- * Send an action frame immediately without doing channel synchronization.
- *
- * This function waits for a completion event before returning.
- * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action
- * frame is transmitted.
- */
-static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
- struct brcmf_fil_af_params_le *af_params)
-{
- struct brcmf_cfg80211_vif *vif;
- s32 err = 0;
- s32 timeout = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- reinit_completion(&p2p->send_af_done);
- clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
- clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
-
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
- err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params,
- sizeof(*af_params));
- if (err) {
- brcmf_err(" sending action frame has failed\n");
- goto exit;
- }
-
- p2p->af_sent_channel = le32_to_cpu(af_params->channel);
- p2p->af_tx_sent_jiffies = jiffies;
-
- timeout = wait_for_completion_timeout(&p2p->send_af_done,
- msecs_to_jiffies(P2P_AF_MAX_WAIT_TIME));
-
- if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) {
- brcmf_dbg(TRACE, "TX action frame operation is success\n");
- } else {
- err = -EIO;
- brcmf_dbg(TRACE, "TX action frame operation has failed\n");
- }
- /* clear status bit for action tx */
- clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
- clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
-
-exit:
- return err;
-}
-
-
-/**
- * brcmf_p2p_pub_af_tx() - public action frame tx routine.
- *
- * @cfg: driver private data for cfg80211 interface.
- * @af_params: action frame data/info.
- * @config_af_params: configuration data for action frame.
- *
- * routine which transmits ation frame public type.
- */
-static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg,
- struct brcmf_fil_af_params_le *af_params,
- struct brcmf_config_af_params *config_af_params)
-{
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_fil_action_frame_le *action_frame;
- struct brcmf_p2p_pub_act_frame *act_frm;
- s32 err = 0;
- u16 ie_len;
-
- action_frame = &af_params->action_frame;
- act_frm = (struct brcmf_p2p_pub_act_frame *)(action_frame->data);
-
- config_af_params->extra_listen = true;
-
- switch (act_frm->subtype) {
- case P2P_PAF_GON_REQ:
- brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status set\n");
- set_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
- config_af_params->mpc_onoff = 0;
- config_af_params->search_channel = true;
- p2p->next_af_subtype = act_frm->subtype + 1;
- p2p->gon_req_action = true;
- /* increase dwell time to wait for RESP frame */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
- break;
- case P2P_PAF_GON_RSP:
- p2p->next_af_subtype = act_frm->subtype + 1;
- /* increase dwell time to wait for CONF frame */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
- break;
- case P2P_PAF_GON_CONF:
- /* If we reached till GO Neg confirmation reset the filter */
- brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
- clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
- /* turn on mpc again if go nego is done */
- config_af_params->mpc_onoff = 1;
- /* minimize dwell time */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
- config_af_params->extra_listen = false;
- break;
- case P2P_PAF_INVITE_REQ:
- config_af_params->search_channel = true;
- p2p->next_af_subtype = act_frm->subtype + 1;
- /* increase dwell time */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
- break;
- case P2P_PAF_INVITE_RSP:
- /* minimize dwell time */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
- config_af_params->extra_listen = false;
- break;
- case P2P_PAF_DEVDIS_REQ:
- config_af_params->search_channel = true;
- p2p->next_af_subtype = act_frm->subtype + 1;
- /* maximize dwell time to wait for RESP frame */
- af_params->dwell_time = cpu_to_le32(P2P_AF_LONG_DWELL_TIME);
- break;
- case P2P_PAF_DEVDIS_RSP:
- /* minimize dwell time */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
- config_af_params->extra_listen = false;
- break;
- case P2P_PAF_PROVDIS_REQ:
- ie_len = le16_to_cpu(action_frame->len) -
- offsetof(struct brcmf_p2p_pub_act_frame, elts);
- if (cfg80211_get_p2p_attr(&act_frm->elts[0], ie_len,
- IEEE80211_P2P_ATTR_GROUP_ID,
- NULL, 0) < 0)
- config_af_params->search_channel = true;
- config_af_params->mpc_onoff = 0;
- p2p->next_af_subtype = act_frm->subtype + 1;
- /* increase dwell time to wait for RESP frame */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
- break;
- case P2P_PAF_PROVDIS_RSP:
- /* wpa_supplicant send go nego req right after prov disc */
- p2p->next_af_subtype = P2P_PAF_GON_REQ;
- /* increase dwell time to MED level */
- af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
- config_af_params->extra_listen = false;
- break;
- default:
- brcmf_err("Unknown p2p pub act frame subtype: %d\n",
- act_frm->subtype);
- err = -EINVAL;
- }
- return err;
-}
-
-/**
- * brcmf_p2p_send_action_frame() - send action frame .
- *
- * @cfg: driver private data for cfg80211 interface.
- * @ndev: net device to transmit on.
- * @af_params: configuration data for action frame.
- */
-bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev,
- struct brcmf_fil_af_params_le *af_params)
-{
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_fil_action_frame_le *action_frame;
- struct brcmf_config_af_params config_af_params;
- struct afx_hdl *afx_hdl = &p2p->afx_hdl;
- u16 action_frame_len;
- bool ack = false;
- u8 category;
- u8 action;
- s32 tx_retry;
- s32 extra_listen_time;
- uint delta_ms;
-
- action_frame = &af_params->action_frame;
- action_frame_len = le16_to_cpu(action_frame->len);
-
- brcmf_p2p_print_actframe(true, action_frame->data, action_frame_len);
-
- /* Add the default dwell time. Dwell time to stay off-channel */
- /* to wait for a response action frame after transmitting an */
- /* GO Negotiation action frame */
- af_params->dwell_time = cpu_to_le32(P2P_AF_DWELL_TIME);
-
- category = action_frame->data[DOT11_ACTION_CAT_OFF];
- action = action_frame->data[DOT11_ACTION_ACT_OFF];
-
- /* initialize variables */
- p2p->next_af_subtype = P2P_PAF_SUBTYPE_INVALID;
- p2p->gon_req_action = false;
-
- /* config parameters */
- config_af_params.mpc_onoff = -1;
- config_af_params.search_channel = false;
- config_af_params.extra_listen = false;
-
- if (brcmf_p2p_is_pub_action(action_frame->data, action_frame_len)) {
- /* p2p public action frame process */
- if (brcmf_p2p_pub_af_tx(cfg, af_params, &config_af_params)) {
- /* Just send unknown subtype frame with */
- /* default parameters. */
- brcmf_err("P2P Public action frame, unknown subtype.\n");
- }
- } else if (brcmf_p2p_is_gas_action(action_frame->data,
- action_frame_len)) {
- /* service discovery process */
- if (action == P2PSD_ACTION_ID_GAS_IREQ ||
- action == P2PSD_ACTION_ID_GAS_CREQ) {
- /* configure service discovery query frame */
- config_af_params.search_channel = true;
-
- /* save next af suptype to cancel */
- /* remaining dwell time */
- p2p->next_af_subtype = action + 1;
-
- af_params->dwell_time =
- cpu_to_le32(P2P_AF_MED_DWELL_TIME);
- } else if (action == P2PSD_ACTION_ID_GAS_IRESP ||
- action == P2PSD_ACTION_ID_GAS_CRESP) {
- /* configure service discovery response frame */
- af_params->dwell_time =
- cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
- } else {
- brcmf_err("Unknown action type: %d\n", action);
- goto exit;
- }
- } else if (brcmf_p2p_is_p2p_action(action_frame->data,
- action_frame_len)) {
- /* do not configure anything. it will be */
- /* sent with a default configuration */
- } else {
- brcmf_err("Unknown Frame: category 0x%x, action 0x%x\n",
- category, action);
- return false;
- }
-
- /* if connecting on primary iface, sleep for a while before sending
- * af tx for VSDB
- */
- if (test_bit(BRCMF_VIF_STATUS_CONNECTING,
- &p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->sme_state))
- msleep(50);
-
- /* if scan is ongoing, abort current scan. */
- if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
- brcmf_abort_scanning(cfg);
-
- memcpy(afx_hdl->tx_dst_addr, action_frame->da, ETH_ALEN);
-
- /* To make sure to send successfully action frame, turn off mpc */
- if (config_af_params.mpc_onoff == 0)
- brcmf_set_mpc(ifp, 0);
-
- /* set status and destination address before sending af */
- if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) {
- /* set status to cancel the remained dwell time in rx process */
- set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
- }
-
- p2p->af_sent_channel = 0;
- set_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
- /* validate channel and p2p ies */
- if (config_af_params.search_channel &&
- IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) &&
- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) {
- afx_hdl = &p2p->afx_hdl;
- afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel);
-
- if (brcmf_p2p_af_searching_channel(p2p) ==
- P2P_INVALID_CHANNEL) {
- brcmf_err("Couldn't find peer's channel.\n");
- goto exit;
- }
-
- /* Abort scan even for VSDB scenarios. Scan gets aborted in
- * firmware but after the check of piggyback algorithm. To take
- * care of current piggback algo, lets abort the scan here
- * itself.
- */
- brcmf_notify_escan_complete(cfg, ifp, true, true);
-
- /* update channel */
- af_params->channel = cpu_to_le32(afx_hdl->peer_chan);
- }
-
- tx_retry = 0;
- while (!p2p->block_gon_req_tx &&
- (ack == false) && (tx_retry < P2P_AF_TX_MAX_RETRY)) {
- ack = !brcmf_p2p_tx_action_frame(p2p, af_params);
- tx_retry++;
- }
- if (ack == false) {
- brcmf_err("Failed to send Action Frame(retry %d)\n", tx_retry);
- clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
- }
-
-exit:
- clear_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
-
- /* WAR: sometimes dongle does not keep the dwell time of 'actframe'.
- * if we coundn't get the next action response frame and dongle does
- * not keep the dwell time, go to listen state again to get next action
- * response frame.
- */
- if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx &&
- test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
- p2p->af_sent_channel == afx_hdl->my_listen_chan) {
- delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies);
- if (le32_to_cpu(af_params->dwell_time) > delta_ms)
- extra_listen_time = le32_to_cpu(af_params->dwell_time) -
- delta_ms;
- else
- extra_listen_time = 0;
- if (extra_listen_time > 50) {
- set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
- &p2p->status);
- brcmf_dbg(INFO, "Wait more time! actual af time:%d, calculated extra listen:%d\n",
- le32_to_cpu(af_params->dwell_time),
- extra_listen_time);
- extra_listen_time += 100;
- if (!brcmf_p2p_discover_listen(p2p,
- p2p->af_sent_channel,
- extra_listen_time)) {
- unsigned long duration;
-
- extra_listen_time += 100;
- duration = msecs_to_jiffies(extra_listen_time);
- wait_for_completion_timeout(&p2p->wait_next_af,
- duration);
- }
- clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
- &p2p->status);
- }
- }
-
- if (p2p->block_gon_req_tx) {
- /* if ack is true, supplicant will wait more time(100ms).
- * so we will return it as a success to get more time .
- */
- p2p->block_gon_req_tx = false;
- ack = true;
- }
-
- clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
- /* if all done, turn mpc on again */
- if (config_af_params.mpc_onoff == 1)
- brcmf_set_mpc(ifp, 1);
-
- return ack;
-}
-
-/**
- * brcmf_p2p_notify_rx_mgmt_p2p_probereq() - Event handler for p2p probe req.
- *
- * @ifp: interface pointer for which event was received.
- * @e: even message.
- * @data: payload of event message (probe request).
- */
-s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data)
-{
- struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct afx_hdl *afx_hdl = &p2p->afx_hdl;
- struct brcmf_cfg80211_vif *vif = ifp->vif;
- struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
- u16 chanspec = be16_to_cpu(rxframe->chanspec);
- struct brcmu_chan ch;
- u8 *mgmt_frame;
- u32 mgmt_frame_len;
- s32 freq;
- u16 mgmt_type;
-
- brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
- e->reason);
-
- ch.chspec = be16_to_cpu(rxframe->chanspec);
- cfg->d11inf.decchspec(&ch);
-
- if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) &&
- (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
- afx_hdl->peer_chan = ch.chnum;
- brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n",
- afx_hdl->peer_chan);
- complete(&afx_hdl->act_frm_scan);
- }
-
- /* Firmware sends us two proberesponses for each idx one. At the */
- /* moment anything but bsscfgidx 0 is passed up to supplicant */
- if (e->bsscfgidx == 0)
- return 0;
-
- /* Filter any P2P probe reqs arriving during the GO-NEG Phase */
- if (test_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status)) {
- brcmf_dbg(INFO, "Filtering P2P probe_req in GO-NEG phase\n");
- return 0;
- }
-
- /* Check if wpa_supplicant has registered for this frame */
- brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg);
- mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4;
- if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
- return 0;
-
- mgmt_frame = (u8 *)(rxframe + 1);
- mgmt_frame_len = e->datalen - sizeof(*rxframe);
- freq = ieee80211_channel_to_frequency(ch.chnum,
- ch.band == BRCMU_CHAN_BAND_2G ?
- IEEE80211_BAND_2GHZ :
- IEEE80211_BAND_5GHZ);
-
- cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0);
-
- brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
- mgmt_frame_len, e->datalen, chanspec, freq);
-
- return 0;
-}
-
-
-/**
- * brcmf_p2p_get_current_chanspec() - Get current operation channel.
- *
- * @p2p: P2P specific data.
- * @chanspec: chanspec to be returned.
- */
-static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p,
- u16 *chanspec)
-{
- struct brcmf_if *ifp;
- u8 mac_addr[ETH_ALEN];
- struct brcmu_chan ch;
- struct brcmf_bss_info_le *bi;
- u8 *buf;
-
- ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
-
- if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mac_addr,
- ETH_ALEN) == 0) {
- buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
- if (buf != NULL) {
- *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
- if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
- buf, WL_BSS_INFO_MAX) == 0) {
- bi = (struct brcmf_bss_info_le *)(buf + 4);
- *chanspec = le16_to_cpu(bi->chanspec);
- kfree(buf);
- return;
- }
- kfree(buf);
- }
- }
- /* Use default channel for P2P */
- ch.chnum = BRCMF_P2P_TEMP_CHAN;
- ch.bw = BRCMU_CHAN_BW_20;
- p2p->cfg->d11inf.encchspec(&ch);
- *chanspec = ch.chspec;
-}
-
-/**
- * Change a P2P Role.
- * Parameters:
- * @mac: MAC address of the BSS to change a role
- * Returns 0 if success.
- */
-int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
- enum brcmf_fil_p2p_if_types if_type)
-{
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_cfg80211_vif *vif;
- struct brcmf_fil_p2p_if_le if_request;
- s32 err;
- u16 chanspec;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
- if (!vif) {
- brcmf_err("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n");
- return -EPERM;
- }
- brcmf_notify_escan_complete(cfg, vif->ifp, true, true);
- vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
- if (!vif) {
- brcmf_err("vif for P2PAPI_BSSCFG_CONNECTION does not exist\n");
- return -EPERM;
- }
- brcmf_set_mpc(vif->ifp, 0);
-
- /* In concurrency case, STA may be already associated in a particular */
- /* channel. so retrieve the current channel of primary interface and */
- /* then start the virtual interface on that. */
- brcmf_p2p_get_current_chanspec(p2p, &chanspec);
-
- if_request.type = cpu_to_le16((u16)if_type);
- if_request.chspec = cpu_to_le16(chanspec);
- memcpy(if_request.addr, p2p->int_addr, sizeof(if_request.addr));
-
- brcmf_cfg80211_arm_vif_event(cfg, vif);
- err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request,
- sizeof(if_request));
- if (err) {
- brcmf_err("p2p_ifupd FAILED, err=%d\n", err);
- brcmf_cfg80211_arm_vif_event(cfg, NULL);
- return err;
- }
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE,
- msecs_to_jiffies(1500));
- brcmf_cfg80211_arm_vif_event(cfg, NULL);
- if (!err) {
- brcmf_err("No BRCMF_E_IF_CHANGE event received\n");
- return -EIO;
- }
-
- err = brcmf_fil_cmd_int_set(vif->ifp, BRCMF_C_SET_SCB_TIMEOUT,
- BRCMF_SCB_TIMEOUT_VALUE);
-
- return err;
-}
-
-static int brcmf_p2p_request_p2p_if(struct brcmf_p2p_info *p2p,
- struct brcmf_if *ifp, u8 ea[ETH_ALEN],
- enum brcmf_fil_p2p_if_types iftype)
-{
- struct brcmf_fil_p2p_if_le if_request;
- int err;
- u16 chanspec;
-
- /* we need a default channel */
- brcmf_p2p_get_current_chanspec(p2p, &chanspec);
-
- /* fill the firmware request */
- memcpy(if_request.addr, ea, ETH_ALEN);
- if_request.type = cpu_to_le16((u16)iftype);
- if_request.chspec = cpu_to_le16(chanspec);
-
- err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request,
- sizeof(if_request));
- if (err)
- return err;
-
- return err;
-}
-
-static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif)
-{
- struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
- struct net_device *pri_ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(pri_ndev);
- u8 *addr = vif->wdev.netdev->dev_addr;
-
- return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN);
-}
-
-static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif)
-{
- struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
- struct net_device *pri_ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(pri_ndev);
- u8 *addr = vif->wdev.netdev->dev_addr;
-
- return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN);
-}
-
-/**
- * brcmf_p2p_create_p2pdev() - create a P2P_DEVICE virtual interface.
- *
- * @p2p: P2P specific data.
- * @wiphy: wiphy device of new interface.
- * @addr: mac address for this new interface.
- */
-static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
- struct wiphy *wiphy,
- u8 *addr)
-{
- struct brcmf_cfg80211_vif *p2p_vif;
- struct brcmf_if *p2p_ifp;
- struct brcmf_if *pri_ifp;
- int err;
- u32 bssidx;
-
- if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
- return ERR_PTR(-ENOSPC);
-
- p2p_vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE,
- false);
- if (IS_ERR(p2p_vif)) {
- brcmf_err("could not create discovery vif\n");
- return (struct wireless_dev *)p2p_vif;
- }
-
- pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
- brcmf_p2p_generate_bss_mac(p2p, addr);
- brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
-
- brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif);
- brcmf_fweh_p2pdev_setup(pri_ifp, true);
-
- /* Initialize P2P Discovery in the firmware */
- err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
- if (err < 0) {
- brcmf_err("set p2p_disc error\n");
- brcmf_fweh_p2pdev_setup(pri_ifp, false);
- brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
- goto fail;
- }
-
- /* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD,
- msecs_to_jiffies(1500));
- brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
- brcmf_fweh_p2pdev_setup(pri_ifp, false);
- if (!err) {
- brcmf_err("timeout occurred\n");
- err = -EIO;
- goto fail;
- }
-
- /* discovery interface created */
- p2p_ifp = p2p_vif->ifp;
- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
- memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
- memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr));
-
- /* verify bsscfg index for P2P discovery */
- err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
- if (err < 0) {
- brcmf_err("retrieving discover bsscfg index failed\n");
- goto fail;
- }
-
- WARN_ON(p2p_ifp->bssidx != bssidx);
-
- init_completion(&p2p->send_af_done);
- INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
- init_completion(&p2p->afx_hdl.act_frm_scan);
- init_completion(&p2p->wait_next_af);
-
- return &p2p_vif->wdev;
-
-fail:
- brcmf_free_vif(p2p_vif);
- return ERR_PTR(err);
-}
-
-/**
- * brcmf_p2p_add_vif() - create a new P2P virtual interface.
- *
- * @wiphy: wiphy device of new interface.
- * @name: name of the new interface.
- * @name_assign_type: origin of the interface name
- * @type: nl80211 interface type.
- * @flags: not used.
- * @params: contains mac address for P2P device.
- */
-struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
- unsigned char name_assign_type,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- struct brcmf_cfg80211_vif *vif;
- enum brcmf_fil_p2p_if_types iftype;
- int err;
-
- if (brcmf_cfg80211_vif_event_armed(cfg))
- return ERR_PTR(-EBUSY);
-
- brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type);
-
- switch (type) {
- case NL80211_IFTYPE_P2P_CLIENT:
- iftype = BRCMF_FIL_P2P_IF_CLIENT;
- break;
- case NL80211_IFTYPE_P2P_GO:
- iftype = BRCMF_FIL_P2P_IF_GO;
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy,
- params->macaddr);
- default:
- return ERR_PTR(-EOPNOTSUPP);
- }
-
- vif = brcmf_alloc_vif(cfg, type, false);
- if (IS_ERR(vif))
- return (struct wireless_dev *)vif;
- brcmf_cfg80211_arm_vif_event(cfg, vif);
-
- err = brcmf_p2p_request_p2p_if(&cfg->p2p, ifp, cfg->p2p.int_addr,
- iftype);
- if (err) {
- brcmf_cfg80211_arm_vif_event(cfg, NULL);
- goto fail;
- }
-
- /* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
- msecs_to_jiffies(1500));
- brcmf_cfg80211_arm_vif_event(cfg, NULL);
- if (!err) {
- brcmf_err("timeout occurred\n");
- err = -EIO;
- goto fail;
- }
-
- /* interface created in firmware */
- ifp = vif->ifp;
- if (!ifp) {
- brcmf_err("no if pointer provided\n");
- err = -ENOENT;
- goto fail;
- }
-
- strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
- ifp->ndev->name_assign_type = name_assign_type;
- err = brcmf_net_attach(ifp, true);
- if (err) {
- brcmf_err("Registering netdevice failed\n");
- goto fail;
- }
-
- cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif;
- /* Disable firmware roaming for P2P interface */
- brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
- if (iftype == BRCMF_FIL_P2P_IF_GO) {
- /* set station timeout for p2p */
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT,
- BRCMF_SCB_TIMEOUT_VALUE);
- }
- return &ifp->vif->wdev;
-
-fail:
- brcmf_free_vif(vif);
- return ERR_PTR(err);
-}
-
-/**
- * brcmf_p2p_del_vif() - delete a P2P virtual interface.
- *
- * @wiphy: wiphy device of interface.
- * @wdev: wireless device of interface.
- */
-int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_cfg80211_vif *vif;
- unsigned long jiffie_timeout = msecs_to_jiffies(1500);
- bool wait_for_disable = false;
- int err;
-
- brcmf_dbg(TRACE, "delete P2P vif\n");
- vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
-
- brcmf_cfg80211_arm_vif_event(cfg, vif);
- switch (vif->wdev.iftype) {
- case NL80211_IFTYPE_P2P_CLIENT:
- if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
- wait_for_disable = true;
- break;
-
- case NL80211_IFTYPE_P2P_GO:
- if (!brcmf_p2p_disable_p2p_if(vif))
- wait_for_disable = true;
- break;
-
- case NL80211_IFTYPE_P2P_DEVICE:
- if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
- return 0;
- brcmf_p2p_cancel_remain_on_channel(vif->ifp);
- brcmf_p2p_deinit_discovery(p2p);
- default:
- return -ENOTSUPP;
- }
-
- clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
- brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
-
- if (wait_for_disable)
- wait_for_completion_timeout(&cfg->vif_disabled,
- msecs_to_jiffies(500));
-
- err = 0;
- if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) {
- brcmf_vif_clear_mgmt_ies(vif);
- err = brcmf_p2p_release_p2p_if(vif);
- }
- if (!err) {
- /* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
- jiffie_timeout);
- if (!err)
- err = -EIO;
- else
- err = 0;
- }
- if (err)
- brcmf_remove_interface(vif->ifp);
-
- brcmf_cfg80211_arm_vif_event(cfg, NULL);
- if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE)
- p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
-
- return err;
-}
-
-void brcmf_p2p_ifp_removed(struct brcmf_if *ifp)
-{
- struct brcmf_cfg80211_info *cfg;
- struct brcmf_cfg80211_vif *vif;
-
- brcmf_dbg(INFO, "P2P: device interface removed\n");
- vif = ifp->vif;
- cfg = wdev_to_cfg(&vif->wdev);
- cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
- rtnl_lock();
- cfg80211_unregister_wdev(&vif->wdev);
- rtnl_unlock();
- brcmf_free_vif(vif);
-}
-
-int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_cfg80211_vif *vif;
- int err;
-
- vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
- mutex_lock(&cfg->usr_sync);
- err = brcmf_p2p_enable_discovery(p2p);
- if (!err)
- set_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
- mutex_unlock(&cfg->usr_sync);
- return err;
-}
-
-void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_cfg80211_vif *vif;
-
- vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
- /* This call can be result of the unregister_wdev call. In that case
- * we dont want to do anything anymore. Just return. The config vif
- * will have been cleared at this point.
- */
- if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) {
- mutex_lock(&cfg->usr_sync);
- /* Set the discovery state to SCAN */
- (void)brcmf_p2p_set_discover_state(vif->ifp,
- WL_P2P_DISC_ST_SCAN, 0, 0);
- brcmf_abort_scanning(cfg);
- clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
- mutex_unlock(&cfg->usr_sync);
- }
-}
-
-/**
- * brcmf_p2p_attach() - attach for P2P.
- *
- * @cfg: driver private data for cfg80211 interface.
- * @p2pdev_forced: create p2p device interface at attach.
- */
-s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced)
-{
- struct brcmf_p2p_info *p2p;
- struct brcmf_if *pri_ifp;
- s32 err = 0;
- void *err_ptr;
-
- p2p = &cfg->p2p;
- p2p->cfg = cfg;
-
- pri_ifp = brcmf_get_ifp(cfg->pub, 0);
- p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
-
- if (p2pdev_forced) {
- err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL);
- if (IS_ERR(err_ptr)) {
- brcmf_err("P2P device creation failed.\n");
- err = PTR_ERR(err_ptr);
- }
- } else {
- p2p->p2pdev_dynamically = true;
- }
- return err;
-}
-
-/**
- * brcmf_p2p_detach() - detach P2P.
- *
- * @p2p: P2P specific data.
- */
-void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
-{
- struct brcmf_cfg80211_vif *vif;
-
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
- if (vif != NULL) {
- brcmf_p2p_cancel_remain_on_channel(vif->ifp);
- brcmf_p2p_deinit_discovery(p2p);
- brcmf_remove_interface(vif->ifp);
- }
- /* just set it all to zero */
- memset(p2p, 0, sizeof(*p2p));
-}
-
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h
deleted file mode 100644
index 5d49059021a9..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (c) 2012 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef WL_CFGP2P_H_
-#define WL_CFGP2P_H_
-
-#include <net/cfg80211.h>
-
-struct brcmf_cfg80211_info;
-
-/**
- * enum p2p_bss_type - different type of BSS configurations.
- *
- * @P2PAPI_BSSCFG_PRIMARY: maps to driver's primary bsscfg.
- * @P2PAPI_BSSCFG_DEVICE: maps to driver's P2P device discovery bsscfg.
- * @P2PAPI_BSSCFG_CONNECTION: maps to driver's P2P connection bsscfg.
- * @P2PAPI_BSSCFG_MAX: used for range checking.
- */
-enum p2p_bss_type {
- P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */
- P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */
- P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */
- P2PAPI_BSSCFG_MAX
-};
-
-/**
- * struct p2p_bss - peer-to-peer bss related information.
- *
- * @vif: virtual interface of this P2P bss.
- * @private_data: TBD
- */
-struct p2p_bss {
- struct brcmf_cfg80211_vif *vif;
- void *private_data;
-};
-
-/**
- * enum brcmf_p2p_status - P2P specific dongle status.
- *
- * @BRCMF_P2P_STATUS_IF_ADD: peer-to-peer vif add sent to dongle.
- * @BRCMF_P2P_STATUS_IF_DEL: NOT-USED?
- * @BRCMF_P2P_STATUS_IF_DELETING: peer-to-peer vif delete sent to dongle.
- * @BRCMF_P2P_STATUS_IF_CHANGING: peer-to-peer vif change sent to dongle.
- * @BRCMF_P2P_STATUS_IF_CHANGED: peer-to-peer vif change completed on dongle.
- * @BRCMF_P2P_STATUS_ACTION_TX_COMPLETED: action frame tx completed.
- * @BRCMF_P2P_STATUS_ACTION_TX_NOACK: action frame tx not acked.
- * @BRCMF_P2P_STATUS_GO_NEG_PHASE: P2P GO negotiation ongoing.
- * @BRCMF_P2P_STATUS_DISCOVER_LISTEN: P2P listen, remaining on channel.
- * @BRCMF_P2P_STATUS_SENDING_ACT_FRAME: In the process of sending action frame.
- * @BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN: extra listen time for af tx.
- * @BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME: waiting for action frame response.
- * @BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL: search channel for AF active.
- */
-enum brcmf_p2p_status {
- BRCMF_P2P_STATUS_ENABLED,
- BRCMF_P2P_STATUS_IF_ADD,
- BRCMF_P2P_STATUS_IF_DEL,
- BRCMF_P2P_STATUS_IF_DELETING,
- BRCMF_P2P_STATUS_IF_CHANGING,
- BRCMF_P2P_STATUS_IF_CHANGED,
- BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
- BRCMF_P2P_STATUS_ACTION_TX_NOACK,
- BRCMF_P2P_STATUS_GO_NEG_PHASE,
- BRCMF_P2P_STATUS_DISCOVER_LISTEN,
- BRCMF_P2P_STATUS_SENDING_ACT_FRAME,
- BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
- BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
- BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL
-};
-
-/**
- * struct afx_hdl - action frame off channel storage.
- *
- * @afx_work: worker thread for searching channel
- * @act_frm_scan: thread synchronizing struct.
- * @is_active: channel searching active.
- * @peer_chan: current channel.
- * @is_listen: sets mode for afx worker.
- * @my_listen_chan: this peers listen channel.
- * @peer_listen_chan: remote peers listen channel.
- * @tx_dst_addr: mac address where tx af should be sent to.
- */
-struct afx_hdl {
- struct work_struct afx_work;
- struct completion act_frm_scan;
- bool is_active;
- s32 peer_chan;
- bool is_listen;
- u16 my_listen_chan;
- u16 peer_listen_chan;
- u8 tx_dst_addr[ETH_ALEN];
-};
-
-/**
- * struct brcmf_p2p_info - p2p specific driver information.
- *
- * @cfg: driver private data for cfg80211 interface.
- * @status: status of P2P (see enum brcmf_p2p_status).
- * @dev_addr: P2P device address.
- * @int_addr: P2P interface address.
- * @bss_idx: informate for P2P bss types.
- * @listen_timer: timer for @WL_P2P_DISC_ST_LISTEN discover state.
- * @ssid: ssid for P2P GO.
- * @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state.
- * @remain_on_channel: contains copy of struct used by cfg80211.
- * @remain_on_channel_cookie: cookie counter for remain on channel cmd
- * @next_af_subtype: expected action frame subtype.
- * @send_af_done: indication that action frame tx is complete.
- * @afx_hdl: action frame search handler info.
- * @af_sent_channel: channel action frame is sent.
- * @af_tx_sent_jiffies: jiffies time when af tx was transmitted.
- * @wait_next_af: thread synchronizing struct.
- * @gon_req_action: about to send go negotiation requets frame.
- * @block_gon_req_tx: drop tx go negotiation requets frame.
- * @p2pdev_dynamically: is p2p device if created by module param or supplicant.
- */
-struct brcmf_p2p_info {
- struct brcmf_cfg80211_info *cfg;
- unsigned long status;
- u8 dev_addr[ETH_ALEN];
- u8 int_addr[ETH_ALEN];
- struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX];
- struct timer_list listen_timer;
- struct brcmf_ssid ssid;
- u8 listen_channel;
- struct ieee80211_channel remain_on_channel;
- u32 remain_on_channel_cookie;
- u8 next_af_subtype;
- struct completion send_af_done;
- struct afx_hdl afx_hdl;
- u32 af_sent_channel;
- unsigned long af_tx_sent_jiffies;
- struct completion wait_next_af;
- bool gon_req_action;
- bool block_gon_req_tx;
- bool p2pdev_dynamically;
-};
-
-s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced);
-void brcmf_p2p_detach(struct brcmf_p2p_info *p2p);
-struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
- unsigned char name_assign_type,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params);
-int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev);
-int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
- enum brcmf_fil_p2p_if_types if_type);
-void brcmf_p2p_ifp_removed(struct brcmf_if *ifp);
-int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev);
-void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev);
-int brcmf_p2p_scan_prep(struct wiphy *wiphy,
- struct cfg80211_scan_request *request,
- struct brcmf_cfg80211_vif *vif);
-int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct ieee80211_channel *channel,
- unsigned int duration, u64 *cookie);
-int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data);
-void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp);
-int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data);
-int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data);
-bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev,
- struct brcmf_fil_af_params_le *af_params);
-bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
- struct brcmf_bss_info_le *bi);
-s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
- const struct brcmf_event_msg *e,
- void *data);
-#endif /* WL_CFGP2P_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
deleted file mode 100644
index 83d804221715..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+++ /dev/null
@@ -1,2107 +0,0 @@
-/* Copyright (c) 2014 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/firmware.h>
-#include <linux/pci.h>
-#include <linux/vmalloc.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/bcma/bcma.h>
-#include <linux/sched.h>
-#include <asm/unaligned.h>
-
-#include <soc.h>
-#include <chipcommon.h>
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-#include <brcm_hw_ids.h>
-
-#include "debug.h"
-#include "bus.h"
-#include "commonring.h"
-#include "msgbuf.h"
-#include "pcie.h"
-#include "firmware.h"
-#include "chip.h"
-
-
-enum brcmf_pcie_state {
- BRCMFMAC_PCIE_STATE_DOWN,
- BRCMFMAC_PCIE_STATE_UP
-};
-
-
-#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin"
-#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt"
-#define BRCMF_PCIE_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin"
-#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-pcie.txt"
-#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin"
-#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt"
-#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin"
-#define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt"
-#define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin"
-#define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt"
-#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin"
-#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt"
-#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin"
-#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt"
-#define BRCMF_PCIE_4371_FW_NAME "brcm/brcmfmac4371-pcie.bin"
-#define BRCMF_PCIE_4371_NVRAM_NAME "brcm/brcmfmac4371-pcie.txt"
-
-#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
-
-#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024)
-#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024)
-
-/* backplane addres space accessed by BAR0 */
-#define BRCMF_PCIE_BAR0_WINDOW 0x80
-#define BRCMF_PCIE_BAR0_REG_SIZE 0x1000
-#define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70
-
-#define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000
-#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000
-
-#define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40
-#define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C
-
-#define BRCMF_PCIE_REG_INTSTATUS 0x90
-#define BRCMF_PCIE_REG_INTMASK 0x94
-#define BRCMF_PCIE_REG_SBMBX 0x98
-
-#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC
-
-#define BRCMF_PCIE_PCIE2REG_INTMASK 0x24
-#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48
-#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C
-#define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120
-#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124
-#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140
-
-#define BRCMF_PCIE_GENREV1 1
-#define BRCMF_PCIE_GENREV2 2
-
-#define BRCMF_PCIE2_INTA 0x01
-#define BRCMF_PCIE2_INTB 0x02
-
-#define BRCMF_PCIE_INT_0 0x01
-#define BRCMF_PCIE_INT_1 0x02
-#define BRCMF_PCIE_INT_DEF (BRCMF_PCIE_INT_0 | \
- BRCMF_PCIE_INT_1)
-
-#define BRCMF_PCIE_MB_INT_FN0_0 0x0100
-#define BRCMF_PCIE_MB_INT_FN0_1 0x0200
-#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000
-#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000
-#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000
-#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000
-#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000
-#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000
-#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000
-#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000
-
-#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \
- BRCMF_PCIE_MB_INT_D2H0_DB1 | \
- BRCMF_PCIE_MB_INT_D2H1_DB0 | \
- BRCMF_PCIE_MB_INT_D2H1_DB1 | \
- BRCMF_PCIE_MB_INT_D2H2_DB0 | \
- BRCMF_PCIE_MB_INT_D2H2_DB1 | \
- BRCMF_PCIE_MB_INT_D2H3_DB0 | \
- BRCMF_PCIE_MB_INT_D2H3_DB1)
-
-#define BRCMF_PCIE_MIN_SHARED_VERSION 5
-#define BRCMF_PCIE_MAX_SHARED_VERSION 5
-#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF
-#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000
-#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000
-
-#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000
-#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000
-
-#define BRCMF_SHARED_MAX_RXBUFPOST_OFFSET 34
-#define BRCMF_SHARED_RING_BASE_OFFSET 52
-#define BRCMF_SHARED_RX_DATAOFFSET_OFFSET 36
-#define BRCMF_SHARED_CONSOLE_ADDR_OFFSET 20
-#define BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET 40
-#define BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET 44
-#define BRCMF_SHARED_RING_INFO_ADDR_OFFSET 48
-#define BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET 52
-#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56
-#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64
-#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68
-
-#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0
-#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1
-#define BRCMF_RING_H2D_RING_MEM_OFFSET 4
-#define BRCMF_RING_H2D_RING_STATE_OFFSET 8
-
-#define BRCMF_RING_MEM_BASE_ADDR_OFFSET 8
-#define BRCMF_RING_MAX_ITEM_OFFSET 4
-#define BRCMF_RING_LEN_ITEMS_OFFSET 6
-#define BRCMF_RING_MEM_SZ 16
-#define BRCMF_RING_STATE_SZ 8
-
-#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET 4
-#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8
-#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12
-#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16
-#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20
-#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28
-#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36
-#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44
-#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0
-#define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52
-
-#define BRCMF_DEF_MAX_RXBUFPOST 255
-
-#define BRCMF_CONSOLE_BUFADDR_OFFSET 8
-#define BRCMF_CONSOLE_BUFSIZE_OFFSET 12
-#define BRCMF_CONSOLE_WRITEIDX_OFFSET 16
-
-#define BRCMF_DMA_D2H_SCRATCH_BUF_LEN 8
-#define BRCMF_DMA_D2H_RINGUPD_BUF_LEN 1024
-
-#define BRCMF_D2H_DEV_D3_ACK 0x00000001
-#define BRCMF_D2H_DEV_DS_ENTER_REQ 0x00000002
-#define BRCMF_D2H_DEV_DS_EXIT_NOTE 0x00000004
-
-#define BRCMF_H2D_HOST_D3_INFORM 0x00000001
-#define BRCMF_H2D_HOST_DS_ACK 0x00000002
-#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008
-#define BRCMF_H2D_HOST_D0_INFORM 0x00000010
-
-#define BRCMF_PCIE_MBDATA_TIMEOUT 2000
-
-#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4
-#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C
-#define BRCMF_PCIE_CFGREG_MSI_CAP 0x58
-#define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C
-#define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60
-#define BRCMF_PCIE_CFGREG_MSI_DATA 0x64
-#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC
-#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC
-#define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228
-#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248
-#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0
-#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4
-#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3
-
-
-MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4371_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4371_NVRAM_NAME);
-
-
-struct brcmf_pcie_console {
- u32 base_addr;
- u32 buf_addr;
- u32 bufsize;
- u32 read_idx;
- u8 log_str[256];
- u8 log_idx;
-};
-
-struct brcmf_pcie_shared_info {
- u32 tcm_base_address;
- u32 flags;
- struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS];
- struct brcmf_pcie_ringbuf *flowrings;
- u16 max_rxbufpost;
- u32 nrof_flowrings;
- u32 rx_dataoffset;
- u32 htod_mb_data_addr;
- u32 dtoh_mb_data_addr;
- u32 ring_info_addr;
- struct brcmf_pcie_console console;
- void *scratch;
- dma_addr_t scratch_dmahandle;
- void *ringupd;
- dma_addr_t ringupd_dmahandle;
-};
-
-struct brcmf_pcie_core_info {
- u32 base;
- u32 wrapbase;
-};
-
-struct brcmf_pciedev_info {
- enum brcmf_pcie_state state;
- bool in_irq;
- bool irq_requested;
- struct pci_dev *pdev;
- char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
- char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
- void __iomem *regs;
- void __iomem *tcm;
- u32 tcm_size;
- u32 ram_base;
- u32 ram_size;
- struct brcmf_chip *ci;
- u32 coreid;
- u32 generic_corerev;
- struct brcmf_pcie_shared_info shared;
- void (*ringbell)(struct brcmf_pciedev_info *devinfo);
- wait_queue_head_t mbdata_resp_wait;
- bool mbdata_completed;
- bool irq_allocated;
- bool wowl_enabled;
- u8 dma_idx_sz;
- void *idxbuf;
- u32 idxbuf_sz;
- dma_addr_t idxbuf_dmahandle;
- u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset);
- void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
- u16 value);
-};
-
-struct brcmf_pcie_ringbuf {
- struct brcmf_commonring commonring;
- dma_addr_t dma_handle;
- u32 w_idx_addr;
- u32 r_idx_addr;
- struct brcmf_pciedev_info *devinfo;
- u8 id;
-};
-
-
-static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = {
- BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM,
- BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM,
- BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM,
- BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM,
- BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM
-};
-
-static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = {
- BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE,
- BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE,
- BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE,
- BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE,
- BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE
-};
-
-
-static u32
-brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset)
-{
- void __iomem *address = devinfo->regs + reg_offset;
-
- return (ioread32(address));
-}
-
-
-static void
-brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset,
- u32 value)
-{
- void __iomem *address = devinfo->regs + reg_offset;
-
- iowrite32(value, address);
-}
-
-
-static u8
-brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
-{
- void __iomem *address = devinfo->tcm + mem_offset;
-
- return (ioread8(address));
-}
-
-
-static u16
-brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
-{
- void __iomem *address = devinfo->tcm + mem_offset;
-
- return (ioread16(address));
-}
-
-
-static void
-brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
- u16 value)
-{
- void __iomem *address = devinfo->tcm + mem_offset;
-
- iowrite16(value, address);
-}
-
-
-static u16
-brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
-{
- u16 *address = devinfo->idxbuf + mem_offset;
-
- return (*(address));
-}
-
-
-static void
-brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
- u16 value)
-{
- u16 *address = devinfo->idxbuf + mem_offset;
-
- *(address) = value;
-}
-
-
-static u32
-brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
-{
- void __iomem *address = devinfo->tcm + mem_offset;
-
- return (ioread32(address));
-}
-
-
-static void
-brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
- u32 value)
-{
- void __iomem *address = devinfo->tcm + mem_offset;
-
- iowrite32(value, address);
-}
-
-
-static u32
-brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
-{
- void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset;
-
- return (ioread32(addr));
-}
-
-
-static void
-brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
- u32 value)
-{
- void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset;
-
- iowrite32(value, addr);
-}
-
-
-static void
-brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
- void *srcaddr, u32 len)
-{
- void __iomem *address = devinfo->tcm + mem_offset;
- __le32 *src32;
- __le16 *src16;
- u8 *src8;
-
- if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) {
- if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) {
- src8 = (u8 *)srcaddr;
- while (len) {
- iowrite8(*src8, address);
- address++;
- src8++;
- len--;
- }
- } else {
- len = len / 2;
- src16 = (__le16 *)srcaddr;
- while (len) {
- iowrite16(le16_to_cpu(*src16), address);
- address += 2;
- src16++;
- len--;
- }
- }
- } else {
- len = len / 4;
- src32 = (__le32 *)srcaddr;
- while (len) {
- iowrite32(le32_to_cpu(*src32), address);
- address += 4;
- src32++;
- len--;
- }
- }
-}
-
-
-static void
-brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
- void *dstaddr, u32 len)
-{
- void __iomem *address = devinfo->tcm + mem_offset;
- __le32 *dst32;
- __le16 *dst16;
- u8 *dst8;
-
- if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) {
- if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) {
- dst8 = (u8 *)dstaddr;
- while (len) {
- *dst8 = ioread8(address);
- address++;
- dst8++;
- len--;
- }
- } else {
- len = len / 2;
- dst16 = (__le16 *)dstaddr;
- while (len) {
- *dst16 = cpu_to_le16(ioread16(address));
- address += 2;
- dst16++;
- len--;
- }
- }
- } else {
- len = len / 4;
- dst32 = (__le32 *)dstaddr;
- while (len) {
- *dst32 = cpu_to_le32(ioread32(address));
- address += 4;
- dst32++;
- len--;
- }
- }
-}
-
-
-#define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \
- CHIPCREGOFFS(reg), value)
-
-
-static void
-brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid)
-{
- const struct pci_dev *pdev = devinfo->pdev;
- struct brcmf_core *core;
- u32 bar0_win;
-
- core = brcmf_chip_get_core(devinfo->ci, coreid);
- if (core) {
- bar0_win = core->base;
- pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, bar0_win);
- if (pci_read_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW,
- &bar0_win) == 0) {
- if (bar0_win != core->base) {
- bar0_win = core->base;
- pci_write_config_dword(pdev,
- BRCMF_PCIE_BAR0_WINDOW,
- bar0_win);
- }
- }
- } else {
- brcmf_err("Unsupported core selected %x\n", coreid);
- }
-}
-
-
-static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
-{
- struct brcmf_core *core;
- u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD,
- BRCMF_PCIE_CFGREG_PM_CSR,
- BRCMF_PCIE_CFGREG_MSI_CAP,
- BRCMF_PCIE_CFGREG_MSI_ADDR_L,
- BRCMF_PCIE_CFGREG_MSI_ADDR_H,
- BRCMF_PCIE_CFGREG_MSI_DATA,
- BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2,
- BRCMF_PCIE_CFGREG_RBAR_CTRL,
- BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1,
- BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG,
- BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG };
- u32 i;
- u32 val;
- u32 lsc;
-
- if (!devinfo->ci)
- return;
-
- /* Disable ASPM */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
- &lsc);
- val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB);
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
- val);
-
- /* Watchdog reset */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON);
- WRITECC32(devinfo, watchdog, 4);
- msleep(100);
-
- /* Restore ASPM */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
- lsc);
-
- core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
- if (core->rev <= 13) {
- for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
- brcmf_pcie_write_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_CONFIGADDR,
- cfg_offset[i]);
- val = brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_CONFIGDATA);
- brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
- cfg_offset[i], val);
- brcmf_pcie_write_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_CONFIGDATA,
- val);
- }
- }
-}
-
-
-static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo)
-{
- u32 config;
-
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- /* BAR1 window may not be sized properly */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0);
- config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config);
-
- device_wakeup_enable(&devinfo->pdev->dev);
-}
-
-
-static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo)
-{
- if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
- brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
- 5);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA,
- 0);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
- 7);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA,
- 0);
- }
- return 0;
-}
-
-
-static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo,
- u32 resetintr)
-{
- struct brcmf_core *core;
-
- if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
- core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_INTERNAL_MEM);
- brcmf_chip_resetcore(core, 0, 0, 0);
- }
-
- return !brcmf_chip_set_active(devinfo->ci, resetintr);
-}
-
-
-static int
-brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
-{
- struct brcmf_pcie_shared_info *shared;
- u32 addr;
- u32 cur_htod_mb_data;
- u32 i;
-
- shared = &devinfo->shared;
- addr = shared->htod_mb_data_addr;
- cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
-
- if (cur_htod_mb_data != 0)
- brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n",
- cur_htod_mb_data);
-
- i = 0;
- while (cur_htod_mb_data != 0) {
- msleep(10);
- i++;
- if (i > 100)
- return -EIO;
- cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
- }
-
- brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
-
- return 0;
-}
-
-
-static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
-{
- struct brcmf_pcie_shared_info *shared;
- u32 addr;
- u32 dtoh_mb_data;
-
- shared = &devinfo->shared;
- addr = shared->dtoh_mb_data_addr;
- dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
-
- if (!dtoh_mb_data)
- return;
-
- brcmf_pcie_write_tcm32(devinfo, addr, 0);
-
- brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data);
- if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) {
- brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n");
- brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK);
- brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n");
- }
- if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE)
- brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n");
- if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) {
- brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n");
- if (waitqueue_active(&devinfo->mbdata_resp_wait)) {
- devinfo->mbdata_completed = true;
- wake_up(&devinfo->mbdata_resp_wait);
- }
- }
-}
-
-
-static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo)
-{
- struct brcmf_pcie_shared_info *shared;
- struct brcmf_pcie_console *console;
- u32 addr;
-
- shared = &devinfo->shared;
- console = &shared->console;
- addr = shared->tcm_base_address + BRCMF_SHARED_CONSOLE_ADDR_OFFSET;
- console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr);
-
- addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET;
- console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr);
- addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET;
- console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr);
-
- brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n",
- console->base_addr, console->buf_addr, console->bufsize);
-}
-
-
-static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo)
-{
- struct brcmf_pcie_console *console;
- u32 addr;
- u8 ch;
- u32 newidx;
-
- if (!BRCMF_FWCON_ON())
- return;
-
- console = &devinfo->shared.console;
- addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET;
- newidx = brcmf_pcie_read_tcm32(devinfo, addr);
- while (newidx != console->read_idx) {
- addr = console->buf_addr + console->read_idx;
- ch = brcmf_pcie_read_tcm8(devinfo, addr);
- console->read_idx++;
- if (console->read_idx == console->bufsize)
- console->read_idx = 0;
- if (ch == '\r')
- continue;
- console->log_str[console->log_idx] = ch;
- console->log_idx++;
- if ((ch != '\n') &&
- (console->log_idx == (sizeof(console->log_str) - 2))) {
- ch = '\n';
- console->log_str[console->log_idx] = ch;
- console->log_idx++;
- }
- if (ch == '\n') {
- console->log_str[console->log_idx] = 0;
- pr_debug("CONSOLE: %s", console->log_str);
- console->log_idx = 0;
- }
- }
-}
-
-
-static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo)
-{
- u32 reg_value;
-
- brcmf_dbg(PCIE, "RING !\n");
- reg_value = brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_MAILBOXINT);
- reg_value |= BRCMF_PCIE2_INTB;
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
- reg_value);
-}
-
-
-static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo)
-{
- brcmf_dbg(PCIE, "RING !\n");
- /* Any arbitrary value will do, lets use 1 */
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
-}
-
-
-static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo)
-{
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
- 0);
- else
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
- 0);
-}
-
-
-static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo)
-{
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
- BRCMF_PCIE_INT_DEF);
- else
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
- BRCMF_PCIE_MB_INT_D2H_DB |
- BRCMF_PCIE_MB_INT_FN0_0 |
- BRCMF_PCIE_MB_INT_FN0_1);
-}
-
-
-static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
- u32 status;
-
- status = 0;
- pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
- if (status) {
- brcmf_pcie_intr_disable(devinfo);
- brcmf_dbg(PCIE, "Enter\n");
- return IRQ_WAKE_THREAD;
- }
- return IRQ_NONE;
-}
-
-
-static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
-
- if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) {
- brcmf_pcie_intr_disable(devinfo);
- brcmf_dbg(PCIE, "Enter\n");
- return IRQ_WAKE_THREAD;
- }
- return IRQ_NONE;
-}
-
-
-static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
- const struct pci_dev *pdev = devinfo->pdev;
- u32 status;
-
- devinfo->in_irq = true;
- status = 0;
- pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
- brcmf_dbg(PCIE, "Enter %x\n", status);
- if (status) {
- pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev);
- }
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_pcie_intr_enable(devinfo);
- devinfo->in_irq = false;
- return IRQ_HANDLED;
-}
-
-
-static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
- u32 status;
-
- devinfo->in_irq = true;
- status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
- brcmf_dbg(PCIE, "Enter %x\n", status);
- if (status) {
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
- status);
- if (status & (BRCMF_PCIE_MB_INT_FN0_0 |
- BRCMF_PCIE_MB_INT_FN0_1))
- brcmf_pcie_handle_mb_data(devinfo);
- if (status & BRCMF_PCIE_MB_INT_D2H_DB) {
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_proto_msgbuf_rx_trigger(
- &devinfo->pdev->dev);
- }
- }
- brcmf_pcie_bus_console_read(devinfo);
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_pcie_intr_enable(devinfo);
- devinfo->in_irq = false;
- return IRQ_HANDLED;
-}
-
-
-static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
-{
- struct pci_dev *pdev;
-
- pdev = devinfo->pdev;
-
- brcmf_pcie_intr_disable(devinfo);
-
- brcmf_dbg(PCIE, "Enter\n");
- /* is it a v1 or v2 implementation */
- devinfo->irq_requested = false;
- pci_enable_msi(pdev);
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
- if (request_threaded_irq(pdev->irq,
- brcmf_pcie_quick_check_isr_v1,
- brcmf_pcie_isr_thread_v1,
- IRQF_SHARED, "brcmf_pcie_intr",
- devinfo)) {
- pci_disable_msi(pdev);
- brcmf_err("Failed to request IRQ %d\n", pdev->irq);
- return -EIO;
- }
- } else {
- if (request_threaded_irq(pdev->irq,
- brcmf_pcie_quick_check_isr_v2,
- brcmf_pcie_isr_thread_v2,
- IRQF_SHARED, "brcmf_pcie_intr",
- devinfo)) {
- pci_disable_msi(pdev);
- brcmf_err("Failed to request IRQ %d\n", pdev->irq);
- return -EIO;
- }
- }
- devinfo->irq_requested = true;
- devinfo->irq_allocated = true;
- return 0;
-}
-
-
-static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo)
-{
- struct pci_dev *pdev;
- u32 status;
- u32 count;
-
- if (!devinfo->irq_allocated)
- return;
-
- pdev = devinfo->pdev;
-
- brcmf_pcie_intr_disable(devinfo);
- if (!devinfo->irq_requested)
- return;
- devinfo->irq_requested = false;
- free_irq(pdev->irq, devinfo);
- pci_disable_msi(pdev);
-
- msleep(50);
- count = 0;
- while ((devinfo->in_irq) && (count < 20)) {
- msleep(50);
- count++;
- }
- if (devinfo->in_irq)
- brcmf_err("Still in IRQ (processing) !!!\n");
-
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
- status = 0;
- pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
- pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
- } else {
- status = brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_MAILBOXINT);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
- status);
- }
- devinfo->irq_allocated = false;
-}
-
-
-static int brcmf_pcie_ring_mb_write_rptr(void *ctx)
-{
- struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
- struct brcmf_pciedev_info *devinfo = ring->devinfo;
- struct brcmf_commonring *commonring = &ring->commonring;
-
- if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
- return -EIO;
-
- brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr,
- commonring->w_ptr, ring->id);
-
- devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr);
-
- return 0;
-}
-
-
-static int brcmf_pcie_ring_mb_write_wptr(void *ctx)
-{
- struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
- struct brcmf_pciedev_info *devinfo = ring->devinfo;
- struct brcmf_commonring *commonring = &ring->commonring;
-
- if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
- return -EIO;
-
- brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr,
- commonring->r_ptr, ring->id);
-
- devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr);
-
- return 0;
-}
-
-
-static int brcmf_pcie_ring_mb_ring_bell(void *ctx)
-{
- struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
- struct brcmf_pciedev_info *devinfo = ring->devinfo;
-
- if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
- return -EIO;
-
- devinfo->ringbell(devinfo);
-
- return 0;
-}
-
-
-static int brcmf_pcie_ring_mb_update_rptr(void *ctx)
-{
- struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
- struct brcmf_pciedev_info *devinfo = ring->devinfo;
- struct brcmf_commonring *commonring = &ring->commonring;
-
- if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
- return -EIO;
-
- commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr);
-
- brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr,
- commonring->w_ptr, ring->id);
-
- return 0;
-}
-
-
-static int brcmf_pcie_ring_mb_update_wptr(void *ctx)
-{
- struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
- struct brcmf_pciedev_info *devinfo = ring->devinfo;
- struct brcmf_commonring *commonring = &ring->commonring;
-
- if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
- return -EIO;
-
- commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr);
-
- brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr,
- commonring->r_ptr, ring->id);
-
- return 0;
-}
-
-
-static void *
-brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo,
- u32 size, u32 tcm_dma_phys_addr,
- dma_addr_t *dma_handle)
-{
- void *ring;
- u64 address;
-
- ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle,
- GFP_KERNEL);
- if (!ring)
- return NULL;
-
- address = (u64)*dma_handle;
- brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr,
- address & 0xffffffff);
- brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32);
-
- memset(ring, 0, size);
-
- return (ring);
-}
-
-
-static struct brcmf_pcie_ringbuf *
-brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id,
- u32 tcm_ring_phys_addr)
-{
- void *dma_buf;
- dma_addr_t dma_handle;
- struct brcmf_pcie_ringbuf *ring;
- u32 size;
- u32 addr;
-
- size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id];
- dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size,
- tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET,
- &dma_handle);
- if (!dma_buf)
- return NULL;
-
- addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET;
- brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]);
- addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET;
- brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]);
-
- ring = kzalloc(sizeof(*ring), GFP_KERNEL);
- if (!ring) {
- dma_free_coherent(&devinfo->pdev->dev, size, dma_buf,
- dma_handle);
- return NULL;
- }
- brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id],
- brcmf_ring_itemsize[ring_id], dma_buf);
- ring->dma_handle = dma_handle;
- ring->devinfo = devinfo;
- brcmf_commonring_register_cb(&ring->commonring,
- brcmf_pcie_ring_mb_ring_bell,
- brcmf_pcie_ring_mb_update_rptr,
- brcmf_pcie_ring_mb_update_wptr,
- brcmf_pcie_ring_mb_write_rptr,
- brcmf_pcie_ring_mb_write_wptr, ring);
-
- return (ring);
-}
-
-
-static void brcmf_pcie_release_ringbuffer(struct device *dev,
- struct brcmf_pcie_ringbuf *ring)
-{
- void *dma_buf;
- u32 size;
-
- if (!ring)
- return;
-
- dma_buf = ring->commonring.buf_addr;
- if (dma_buf) {
- size = ring->commonring.depth * ring->commonring.item_len;
- dma_free_coherent(dev, size, dma_buf, ring->dma_handle);
- }
- kfree(ring);
-}
-
-
-static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo)
-{
- u32 i;
-
- for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) {
- brcmf_pcie_release_ringbuffer(&devinfo->pdev->dev,
- devinfo->shared.commonrings[i]);
- devinfo->shared.commonrings[i] = NULL;
- }
- kfree(devinfo->shared.flowrings);
- devinfo->shared.flowrings = NULL;
- if (devinfo->idxbuf) {
- dma_free_coherent(&devinfo->pdev->dev,
- devinfo->idxbuf_sz,
- devinfo->idxbuf,
- devinfo->idxbuf_dmahandle);
- devinfo->idxbuf = NULL;
- }
-}
-
-
-static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo)
-{
- struct brcmf_pcie_ringbuf *ring;
- struct brcmf_pcie_ringbuf *rings;
- u32 ring_addr;
- u32 d2h_w_idx_ptr;
- u32 d2h_r_idx_ptr;
- u32 h2d_w_idx_ptr;
- u32 h2d_r_idx_ptr;
- u32 addr;
- u32 ring_mem_ptr;
- u32 i;
- u64 address;
- u32 bufsz;
- u16 max_sub_queues;
- u8 idx_offset;
-
- ring_addr = devinfo->shared.ring_info_addr;
- brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr);
- addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES;
- max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr);
-
- if (devinfo->dma_idx_sz != 0) {
- bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) *
- devinfo->dma_idx_sz * 2;
- devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz,
- &devinfo->idxbuf_dmahandle,
- GFP_KERNEL);
- if (!devinfo->idxbuf)
- devinfo->dma_idx_sz = 0;
- }
-
- if (devinfo->dma_idx_sz == 0) {
- addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET;
- d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
- addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET;
- d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
- addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET;
- h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
- addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET;
- h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
- idx_offset = sizeof(u32);
- devinfo->write_ptr = brcmf_pcie_write_tcm16;
- devinfo->read_ptr = brcmf_pcie_read_tcm16;
- brcmf_dbg(PCIE, "Using TCM indices\n");
- } else {
- memset(devinfo->idxbuf, 0, bufsz);
- devinfo->idxbuf_sz = bufsz;
- idx_offset = devinfo->dma_idx_sz;
- devinfo->write_ptr = brcmf_pcie_write_idx;
- devinfo->read_ptr = brcmf_pcie_read_idx;
-
- h2d_w_idx_ptr = 0;
- addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET;
- address = (u64)devinfo->idxbuf_dmahandle;
- brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
- brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
-
- h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset;
- addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET;
- address += max_sub_queues * idx_offset;
- brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
- brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
-
- d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset;
- addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET;
- address += max_sub_queues * idx_offset;
- brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
- brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
-
- d2h_r_idx_ptr = d2h_w_idx_ptr +
- BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset;
- addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET;
- address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset;
- brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
- brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
- brcmf_dbg(PCIE, "Using host memory indices\n");
- }
-
- addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET;
- ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
-
- for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) {
- ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr);
- if (!ring)
- goto fail;
- ring->w_idx_addr = h2d_w_idx_ptr;
- ring->r_idx_addr = h2d_r_idx_ptr;
- ring->id = i;
- devinfo->shared.commonrings[i] = ring;
-
- h2d_w_idx_ptr += idx_offset;
- h2d_r_idx_ptr += idx_offset;
- ring_mem_ptr += BRCMF_RING_MEM_SZ;
- }
-
- for (i = BRCMF_NROF_H2D_COMMON_MSGRINGS;
- i < BRCMF_NROF_COMMON_MSGRINGS; i++) {
- ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr);
- if (!ring)
- goto fail;
- ring->w_idx_addr = d2h_w_idx_ptr;
- ring->r_idx_addr = d2h_r_idx_ptr;
- ring->id = i;
- devinfo->shared.commonrings[i] = ring;
-
- d2h_w_idx_ptr += idx_offset;
- d2h_r_idx_ptr += idx_offset;
- ring_mem_ptr += BRCMF_RING_MEM_SZ;
- }
-
- devinfo->shared.nrof_flowrings =
- max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS;
- rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring),
- GFP_KERNEL);
- if (!rings)
- goto fail;
-
- brcmf_dbg(PCIE, "Nr of flowrings is %d\n",
- devinfo->shared.nrof_flowrings);
-
- for (i = 0; i < devinfo->shared.nrof_flowrings; i++) {
- ring = &rings[i];
- ring->devinfo = devinfo;
- ring->id = i + BRCMF_NROF_COMMON_MSGRINGS;
- brcmf_commonring_register_cb(&ring->commonring,
- brcmf_pcie_ring_mb_ring_bell,
- brcmf_pcie_ring_mb_update_rptr,
- brcmf_pcie_ring_mb_update_wptr,
- brcmf_pcie_ring_mb_write_rptr,
- brcmf_pcie_ring_mb_write_wptr,
- ring);
- ring->w_idx_addr = h2d_w_idx_ptr;
- ring->r_idx_addr = h2d_r_idx_ptr;
- h2d_w_idx_ptr += idx_offset;
- h2d_r_idx_ptr += idx_offset;
- }
- devinfo->shared.flowrings = rings;
-
- return 0;
-
-fail:
- brcmf_err("Allocating ring buffers failed\n");
- brcmf_pcie_release_ringbuffers(devinfo);
- return -ENOMEM;
-}
-
-
-static void
-brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo)
-{
- if (devinfo->shared.scratch)
- dma_free_coherent(&devinfo->pdev->dev,
- BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
- devinfo->shared.scratch,
- devinfo->shared.scratch_dmahandle);
- if (devinfo->shared.ringupd)
- dma_free_coherent(&devinfo->pdev->dev,
- BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
- devinfo->shared.ringupd,
- devinfo->shared.ringupd_dmahandle);
-}
-
-static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo)
-{
- u64 address;
- u32 addr;
-
- devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev,
- BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
- &devinfo->shared.scratch_dmahandle, GFP_KERNEL);
- if (!devinfo->shared.scratch)
- goto fail;
-
- memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN);
-
- addr = devinfo->shared.tcm_base_address +
- BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET;
- address = (u64)devinfo->shared.scratch_dmahandle;
- brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
- brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
- addr = devinfo->shared.tcm_base_address +
- BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET;
- brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_SCRATCH_BUF_LEN);
-
- devinfo->shared.ringupd = dma_alloc_coherent(&devinfo->pdev->dev,
- BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
- &devinfo->shared.ringupd_dmahandle, GFP_KERNEL);
- if (!devinfo->shared.ringupd)
- goto fail;
-
- memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN);
-
- addr = devinfo->shared.tcm_base_address +
- BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET;
- address = (u64)devinfo->shared.ringupd_dmahandle;
- brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
- brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
- addr = devinfo->shared.tcm_base_address +
- BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET;
- brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_RINGUPD_BUF_LEN);
- return 0;
-
-fail:
- brcmf_err("Allocating scratch buffers failed\n");
- brcmf_pcie_release_scratchbuffers(devinfo);
- return -ENOMEM;
-}
-
-
-static void brcmf_pcie_down(struct device *dev)
-{
-}
-
-
-static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb)
-{
- return 0;
-}
-
-
-static int brcmf_pcie_tx_ctlpkt(struct device *dev, unsigned char *msg,
- uint len)
-{
- return 0;
-}
-
-
-static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg,
- uint len)
-{
- return 0;
-}
-
-
-static void brcmf_pcie_wowl_config(struct device *dev, bool enabled)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
- struct brcmf_pciedev_info *devinfo = buspub->devinfo;
-
- brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled);
- devinfo->wowl_enabled = enabled;
- if (enabled)
- device_set_wakeup_enable(&devinfo->pdev->dev, true);
- else
- device_set_wakeup_enable(&devinfo->pdev->dev, false);
-}
-
-
-static size_t brcmf_pcie_get_ramsize(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
- struct brcmf_pciedev_info *devinfo = buspub->devinfo;
-
- return devinfo->ci->ramsize - devinfo->ci->srsize;
-}
-
-
-static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
- struct brcmf_pciedev_info *devinfo = buspub->devinfo;
-
- brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len);
- brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len);
- return 0;
-}
-
-
-static struct brcmf_bus_ops brcmf_pcie_bus_ops = {
- .txdata = brcmf_pcie_tx,
- .stop = brcmf_pcie_down,
- .txctl = brcmf_pcie_tx_ctlpkt,
- .rxctl = brcmf_pcie_rx_ctlpkt,
- .wowl_config = brcmf_pcie_wowl_config,
- .get_ramsize = brcmf_pcie_get_ramsize,
- .get_memdump = brcmf_pcie_get_memdump,
-};
-
-
-static int
-brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
- u32 sharedram_addr)
-{
- struct brcmf_pcie_shared_info *shared;
- u32 addr;
- u32 version;
-
- shared = &devinfo->shared;
- shared->tcm_base_address = sharedram_addr;
-
- shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr);
- version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK;
- brcmf_dbg(PCIE, "PCIe protocol version %d\n", version);
- if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) ||
- (version < BRCMF_PCIE_MIN_SHARED_VERSION)) {
- brcmf_err("Unsupported PCIE version %d\n", version);
- return -EINVAL;
- }
-
- /* check firmware support dma indicies */
- if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) {
- if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX)
- devinfo->dma_idx_sz = sizeof(u16);
- else
- devinfo->dma_idx_sz = sizeof(u32);
- }
-
- addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET;
- shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr);
- if (shared->max_rxbufpost == 0)
- shared->max_rxbufpost = BRCMF_DEF_MAX_RXBUFPOST;
-
- addr = sharedram_addr + BRCMF_SHARED_RX_DATAOFFSET_OFFSET;
- shared->rx_dataoffset = brcmf_pcie_read_tcm32(devinfo, addr);
-
- addr = sharedram_addr + BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET;
- shared->htod_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr);
-
- addr = sharedram_addr + BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET;
- shared->dtoh_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr);
-
- addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET;
- shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr);
-
- brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n",
- shared->max_rxbufpost, shared->rx_dataoffset);
-
- brcmf_pcie_bus_console_init(devinfo);
-
- return 0;
-}
-
-
-static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo)
-{
- char *fw_name;
- char *nvram_name;
- uint fw_len, nv_len;
- char end;
-
- brcmf_dbg(PCIE, "Enter, chip 0x%04x chiprev %d\n", devinfo->ci->chip,
- devinfo->ci->chiprev);
-
- switch (devinfo->ci->chip) {
- case BRCM_CC_43602_CHIP_ID:
- fw_name = BRCMF_PCIE_43602_FW_NAME;
- nvram_name = BRCMF_PCIE_43602_NVRAM_NAME;
- break;
- case BRCM_CC_4350_CHIP_ID:
- fw_name = BRCMF_PCIE_4350_FW_NAME;
- nvram_name = BRCMF_PCIE_4350_NVRAM_NAME;
- break;
- case BRCM_CC_4356_CHIP_ID:
- fw_name = BRCMF_PCIE_4356_FW_NAME;
- nvram_name = BRCMF_PCIE_4356_NVRAM_NAME;
- break;
- case BRCM_CC_43567_CHIP_ID:
- case BRCM_CC_43569_CHIP_ID:
- case BRCM_CC_43570_CHIP_ID:
- fw_name = BRCMF_PCIE_43570_FW_NAME;
- nvram_name = BRCMF_PCIE_43570_NVRAM_NAME;
- break;
- case BRCM_CC_4358_CHIP_ID:
- fw_name = BRCMF_PCIE_4358_FW_NAME;
- nvram_name = BRCMF_PCIE_4358_NVRAM_NAME;
- break;
- case BRCM_CC_4365_CHIP_ID:
- fw_name = BRCMF_PCIE_4365_FW_NAME;
- nvram_name = BRCMF_PCIE_4365_NVRAM_NAME;
- break;
- case BRCM_CC_4366_CHIP_ID:
- fw_name = BRCMF_PCIE_4366_FW_NAME;
- nvram_name = BRCMF_PCIE_4366_NVRAM_NAME;
- break;
- case BRCM_CC_4371_CHIP_ID:
- fw_name = BRCMF_PCIE_4371_FW_NAME;
- nvram_name = BRCMF_PCIE_4371_NVRAM_NAME;
- break;
- default:
- brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip);
- return -ENODEV;
- }
-
- fw_len = sizeof(devinfo->fw_name) - 1;
- nv_len = sizeof(devinfo->nvram_name) - 1;
- /* check if firmware path is provided by module parameter */
- if (brcmf_firmware_path[0] != '\0') {
- strncpy(devinfo->fw_name, brcmf_firmware_path, fw_len);
- strncpy(devinfo->nvram_name, brcmf_firmware_path, nv_len);
- fw_len -= strlen(devinfo->fw_name);
- nv_len -= strlen(devinfo->nvram_name);
-
- end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
- if (end != '/') {
- strncat(devinfo->fw_name, "/", fw_len);
- strncat(devinfo->nvram_name, "/", nv_len);
- fw_len--;
- nv_len--;
- }
- }
- strncat(devinfo->fw_name, fw_name, fw_len);
- strncat(devinfo->nvram_name, nvram_name, nv_len);
-
- return 0;
-}
-
-
-static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
- const struct firmware *fw, void *nvram,
- u32 nvram_len)
-{
- u32 sharedram_addr;
- u32 sharedram_addr_written;
- u32 loop_counter;
- int err;
- u32 address;
- u32 resetintr;
-
- devinfo->ringbell = brcmf_pcie_ringbell_v2;
- devinfo->generic_corerev = BRCMF_PCIE_GENREV2;
-
- brcmf_dbg(PCIE, "Halt ARM.\n");
- err = brcmf_pcie_enter_download_state(devinfo);
- if (err)
- return err;
-
- brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name);
- brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase,
- (void *)fw->data, fw->size);
-
- resetintr = get_unaligned_le32(fw->data);
- release_firmware(fw);
-
- /* reset last 4 bytes of RAM address. to be used for shared
- * area. This identifies when FW is running
- */
- brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0);
-
- if (nvram) {
- brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name);
- address = devinfo->ci->rambase + devinfo->ci->ramsize -
- nvram_len;
- brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len);
- brcmf_fw_nvram_free(nvram);
- } else {
- brcmf_dbg(PCIE, "No matching NVRAM file found %s\n",
- devinfo->nvram_name);
- }
-
- sharedram_addr_written = brcmf_pcie_read_ram32(devinfo,
- devinfo->ci->ramsize -
- 4);
- brcmf_dbg(PCIE, "Bring ARM in running state\n");
- err = brcmf_pcie_exit_download_state(devinfo, resetintr);
- if (err)
- return err;
-
- brcmf_dbg(PCIE, "Wait for FW init\n");
- sharedram_addr = sharedram_addr_written;
- loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50;
- while ((sharedram_addr == sharedram_addr_written) && (loop_counter)) {
- msleep(50);
- sharedram_addr = brcmf_pcie_read_ram32(devinfo,
- devinfo->ci->ramsize -
- 4);
- loop_counter--;
- }
- if (sharedram_addr == sharedram_addr_written) {
- brcmf_err("FW failed to initialize\n");
- return -ENODEV;
- }
- brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr);
-
- return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr));
-}
-
-
-static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo)
-{
- struct pci_dev *pdev;
- int err;
- phys_addr_t bar0_addr, bar1_addr;
- ulong bar1_size;
-
- pdev = devinfo->pdev;
-
- err = pci_enable_device(pdev);
- if (err) {
- brcmf_err("pci_enable_device failed err=%d\n", err);
- return err;
- }
-
- pci_set_master(pdev);
-
- /* Bar-0 mapped address */
- bar0_addr = pci_resource_start(pdev, 0);
- /* Bar-1 mapped address */
- bar1_addr = pci_resource_start(pdev, 2);
- /* read Bar-1 mapped memory range */
- bar1_size = pci_resource_len(pdev, 2);
- if ((bar1_size == 0) || (bar1_addr == 0)) {
- brcmf_err("BAR1 Not enabled, device size=%ld, addr=%#016llx\n",
- bar1_size, (unsigned long long)bar1_addr);
- return -EINVAL;
- }
-
- devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE);
- devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE);
- devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE;
-
- if (!devinfo->regs || !devinfo->tcm) {
- brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs,
- devinfo->tcm);
- return -EINVAL;
- }
- brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n",
- devinfo->regs, (unsigned long long)bar0_addr);
- brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n",
- devinfo->tcm, (unsigned long long)bar1_addr);
-
- return 0;
-}
-
-
-static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo)
-{
- if (devinfo->tcm)
- iounmap(devinfo->tcm);
- if (devinfo->regs)
- iounmap(devinfo->regs);
-
- pci_disable_device(devinfo->pdev);
-}
-
-
-static int brcmf_pcie_attach_bus(struct device *dev)
-{
- int ret;
-
- /* Attach to the common driver interface */
- ret = brcmf_attach(dev);
- if (ret) {
- brcmf_err("brcmf_attach failed\n");
- } else {
- ret = brcmf_bus_start(dev);
- if (ret)
- brcmf_err("dongle is not responding\n");
- }
-
- return ret;
-}
-
-
-static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr)
-{
- u32 ret_addr;
-
- ret_addr = addr & (BRCMF_PCIE_BAR0_REG_SIZE - 1);
- addr &= ~(BRCMF_PCIE_BAR0_REG_SIZE - 1);
- pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, addr);
-
- return ret_addr;
-}
-
-
-static u32 brcmf_pcie_buscore_read32(void *ctx, u32 addr)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
-
- addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr);
- return brcmf_pcie_read_reg32(devinfo, addr);
-}
-
-
-static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
-
- addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr);
- brcmf_pcie_write_reg32(devinfo, addr, value);
-}
-
-
-static int brcmf_pcie_buscoreprep(void *ctx)
-{
- return brcmf_pcie_get_resource(ctx);
-}
-
-
-static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
- u32 val;
-
- devinfo->ci = chip;
- brcmf_pcie_reset_device(devinfo);
-
- val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
- if (val != 0xffffffff)
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
- val);
-
- return 0;
-}
-
-
-static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
- u32 rstvec)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
-
- brcmf_pcie_write_tcm32(devinfo, 0, rstvec);
-}
-
-
-static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
- .prepare = brcmf_pcie_buscoreprep,
- .reset = brcmf_pcie_buscore_reset,
- .activate = brcmf_pcie_buscore_activate,
- .read32 = brcmf_pcie_buscore_read32,
- .write32 = brcmf_pcie_buscore_write32,
-};
-
-static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw,
- void *nvram, u32 nvram_len)
-{
- struct brcmf_bus *bus = dev_get_drvdata(dev);
- struct brcmf_pciedev *pcie_bus_dev = bus->bus_priv.pcie;
- struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo;
- struct brcmf_commonring **flowrings;
- int ret;
- u32 i;
-
- brcmf_pcie_attach(devinfo);
-
- ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len);
- if (ret)
- goto fail;
-
- devinfo->state = BRCMFMAC_PCIE_STATE_UP;
-
- ret = brcmf_pcie_init_ringbuffers(devinfo);
- if (ret)
- goto fail;
-
- ret = brcmf_pcie_init_scratchbuffers(devinfo);
- if (ret)
- goto fail;
-
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- ret = brcmf_pcie_request_irq(devinfo);
- if (ret)
- goto fail;
-
- /* hook the commonrings in the bus structure. */
- for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++)
- bus->msgbuf->commonrings[i] =
- &devinfo->shared.commonrings[i]->commonring;
-
- flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings),
- GFP_KERNEL);
- if (!flowrings)
- goto fail;
-
- for (i = 0; i < devinfo->shared.nrof_flowrings; i++)
- flowrings[i] = &devinfo->shared.flowrings[i].commonring;
- bus->msgbuf->flowrings = flowrings;
-
- bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset;
- bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost;
- bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings;
-
- init_waitqueue_head(&devinfo->mbdata_resp_wait);
-
- brcmf_pcie_intr_enable(devinfo);
- if (brcmf_pcie_attach_bus(bus->dev) == 0)
- return;
-
- brcmf_pcie_bus_console_read(devinfo);
-
-fail:
- device_release_driver(dev);
-}
-
-static int
-brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- int ret;
- struct brcmf_pciedev_info *devinfo;
- struct brcmf_pciedev *pcie_bus_dev;
- struct brcmf_bus *bus;
- u16 domain_nr;
- u16 bus_nr;
-
- domain_nr = pci_domain_nr(pdev->bus) + 1;
- bus_nr = pdev->bus->number;
- brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device,
- domain_nr, bus_nr);
-
- ret = -ENOMEM;
- devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
- if (devinfo == NULL)
- return ret;
-
- devinfo->pdev = pdev;
- pcie_bus_dev = NULL;
- devinfo->ci = brcmf_chip_attach(devinfo, &brcmf_pcie_buscore_ops);
- if (IS_ERR(devinfo->ci)) {
- ret = PTR_ERR(devinfo->ci);
- devinfo->ci = NULL;
- goto fail;
- }
-
- pcie_bus_dev = kzalloc(sizeof(*pcie_bus_dev), GFP_KERNEL);
- if (pcie_bus_dev == NULL) {
- ret = -ENOMEM;
- goto fail;
- }
-
- bus = kzalloc(sizeof(*bus), GFP_KERNEL);
- if (!bus) {
- ret = -ENOMEM;
- goto fail;
- }
- bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL);
- if (!bus->msgbuf) {
- ret = -ENOMEM;
- kfree(bus);
- goto fail;
- }
-
- /* hook it all together. */
- pcie_bus_dev->devinfo = devinfo;
- pcie_bus_dev->bus = bus;
- bus->dev = &pdev->dev;
- bus->bus_priv.pcie = pcie_bus_dev;
- bus->ops = &brcmf_pcie_bus_ops;
- bus->proto_type = BRCMF_PROTO_MSGBUF;
- bus->chip = devinfo->coreid;
- bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
- dev_set_drvdata(&pdev->dev, bus);
-
- ret = brcmf_pcie_get_fwnames(devinfo);
- if (ret)
- goto fail_bus;
-
- ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM |
- BRCMF_FW_REQ_NV_OPTIONAL,
- devinfo->fw_name, devinfo->nvram_name,
- brcmf_pcie_setup, domain_nr, bus_nr);
- if (ret == 0)
- return 0;
-fail_bus:
- kfree(bus->msgbuf);
- kfree(bus);
-fail:
- brcmf_err("failed %x:%x\n", pdev->vendor, pdev->device);
- brcmf_pcie_release_resource(devinfo);
- if (devinfo->ci)
- brcmf_chip_detach(devinfo->ci);
- kfree(pcie_bus_dev);
- kfree(devinfo);
- return ret;
-}
-
-
-static void
-brcmf_pcie_remove(struct pci_dev *pdev)
-{
- struct brcmf_pciedev_info *devinfo;
- struct brcmf_bus *bus;
-
- brcmf_dbg(PCIE, "Enter\n");
-
- bus = dev_get_drvdata(&pdev->dev);
- if (bus == NULL)
- return;
-
- devinfo = bus->bus_priv.pcie->devinfo;
-
- devinfo->state = BRCMFMAC_PCIE_STATE_DOWN;
- if (devinfo->ci)
- brcmf_pcie_intr_disable(devinfo);
-
- brcmf_detach(&pdev->dev);
-
- kfree(bus->bus_priv.pcie);
- kfree(bus->msgbuf->flowrings);
- kfree(bus->msgbuf);
- kfree(bus);
-
- brcmf_pcie_release_irq(devinfo);
- brcmf_pcie_release_scratchbuffers(devinfo);
- brcmf_pcie_release_ringbuffers(devinfo);
- brcmf_pcie_reset_device(devinfo);
- brcmf_pcie_release_resource(devinfo);
-
- if (devinfo->ci)
- brcmf_chip_detach(devinfo->ci);
-
- kfree(devinfo);
- dev_set_drvdata(&pdev->dev, NULL);
-}
-
-
-#ifdef CONFIG_PM
-
-
-static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct brcmf_pciedev_info *devinfo;
- struct brcmf_bus *bus;
- int err;
-
- brcmf_dbg(PCIE, "Enter, state=%d, pdev=%p\n", state.event, pdev);
-
- bus = dev_get_drvdata(&pdev->dev);
- devinfo = bus->bus_priv.pcie->devinfo;
-
- brcmf_bus_change_state(bus, BRCMF_BUS_DOWN);
-
- devinfo->mbdata_completed = false;
- brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM);
-
- wait_event_timeout(devinfo->mbdata_resp_wait,
- devinfo->mbdata_completed,
- msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT));
- if (!devinfo->mbdata_completed) {
- brcmf_err("Timeout on response for entering D3 substate\n");
- return -EIO;
- }
- brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE);
-
- err = pci_save_state(pdev);
- if (err)
- brcmf_err("pci_save_state failed, err=%d\n", err);
- if ((err) || (!devinfo->wowl_enabled)) {
- brcmf_chip_detach(devinfo->ci);
- devinfo->ci = NULL;
- brcmf_pcie_remove(pdev);
- return 0;
- }
-
- return pci_prepare_to_sleep(pdev);
-}
-
-static int brcmf_pcie_resume(struct pci_dev *pdev)
-{
- struct brcmf_pciedev_info *devinfo;
- struct brcmf_bus *bus;
- int err;
-
- bus = dev_get_drvdata(&pdev->dev);
- brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus);
-
- err = pci_set_power_state(pdev, PCI_D0);
- if (err) {
- brcmf_err("pci_set_power_state failed, err=%d\n", err);
- goto cleanup;
- }
- pci_restore_state(pdev);
- pci_enable_wake(pdev, PCI_D3hot, false);
- pci_enable_wake(pdev, PCI_D3cold, false);
-
- /* Check if device is still up and running, if so we are ready */
- if (bus) {
- devinfo = bus->bus_priv.pcie->devinfo;
- if (brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_INTMASK) != 0) {
- if (brcmf_pcie_send_mb_data(devinfo,
- BRCMF_H2D_HOST_D0_INFORM))
- goto cleanup;
- brcmf_dbg(PCIE, "Hot resume, continue....\n");
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- brcmf_bus_change_state(bus, BRCMF_BUS_UP);
- brcmf_pcie_intr_enable(devinfo);
- return 0;
- }
- }
-
-cleanup:
- if (bus) {
- devinfo = bus->bus_priv.pcie->devinfo;
- brcmf_chip_detach(devinfo->ci);
- devinfo->ci = NULL;
- brcmf_pcie_remove(pdev);
- }
- err = brcmf_pcie_probe(pdev, NULL);
- if (err)
- brcmf_err("probe after resume failed, err=%d\n", err);
-
- return err;
-}
-
-
-#endif /* CONFIG_PM */
-
-
-#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
- PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
-
-static struct pci_device_id brcmf_pcie_devid_table[] = {
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID),
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID),
- { /* end: all zeroes */ }
-};
-
-
-MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table);
-
-
-static struct pci_driver brcmf_pciedrvr = {
- .node = {},
- .name = KBUILD_MODNAME,
- .id_table = brcmf_pcie_devid_table,
- .probe = brcmf_pcie_probe,
- .remove = brcmf_pcie_remove,
-#ifdef CONFIG_PM
- .suspend = brcmf_pcie_suspend,
- .resume = brcmf_pcie_resume
-#endif /* CONFIG_PM */
-};
-
-
-void brcmf_pcie_register(void)
-{
- int err;
-
- brcmf_dbg(PCIE, "Enter\n");
- err = pci_register_driver(&brcmf_pciedrvr);
- if (err)
- brcmf_err("PCIE driver registration failed, err=%d\n", err);
-}
-
-
-void brcmf_pcie_exit(void)
-{
- brcmf_dbg(PCIE, "Enter\n");
- pci_unregister_driver(&brcmf_pciedrvr);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
deleted file mode 100644
index 7e74ac3ad815..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ /dev/null
@@ -1,4376 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/types.h>
-#include <linux/atomic.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/printk.h>
-#include <linux/pci_ids.h>
-#include <linux/netdevice.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/mmc/sdio.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/card.h>
-#include <linux/semaphore.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/bcma/bcma.h>
-#include <linux/debugfs.h>
-#include <linux/vmalloc.h>
-#include <linux/platform_data/brcmfmac-sdio.h>
-#include <linux/moduleparam.h>
-#include <asm/unaligned.h>
-#include <defs.h>
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include <brcm_hw_ids.h>
-#include <soc.h>
-#include "sdio.h"
-#include "chip.h"
-#include "firmware.h"
-
-#define DCMD_RESP_TIMEOUT 2000 /* In milli second */
-#define CTL_DONE_TIMEOUT 2000 /* In milli second */
-
-#ifdef DEBUG
-
-#define BRCMF_TRAP_INFO_SIZE 80
-
-#define CBUF_LEN (128)
-
-/* Device console log buffer state */
-#define CONSOLE_BUFFER_MAX 2024
-
-struct rte_log_le {
- __le32 buf; /* Can't be pointer on (64-bit) hosts */
- __le32 buf_size;
- __le32 idx;
- char *_buf_compat; /* Redundant pointer for backward compat. */
-};
-
-struct rte_console {
- /* Virtual UART
- * When there is no UART (e.g. Quickturn),
- * the host should write a complete
- * input line directly into cbuf and then write
- * the length into vcons_in.
- * This may also be used when there is a real UART
- * (at risk of conflicting with
- * the real UART). vcons_out is currently unused.
- */
- uint vcons_in;
- uint vcons_out;
-
- /* Output (logging) buffer
- * Console output is written to a ring buffer log_buf at index log_idx.
- * The host may read the output when it sees log_idx advance.
- * Output will be lost if the output wraps around faster than the host
- * polls.
- */
- struct rte_log_le log_le;
-
- /* Console input line buffer
- * Characters are read one at a time into cbuf
- * until <CR> is received, then
- * the buffer is processed as a command line.
- * Also used for virtual UART.
- */
- uint cbuf_idx;
- char cbuf[CBUF_LEN];
-};
-
-#endif /* DEBUG */
-#include <chipcommon.h>
-
-#include "bus.h"
-#include "debug.h"
-#include "tracepoint.h"
-
-#define TXQLEN 2048 /* bulk tx queue length */
-#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
-#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */
-#define PRIOMASK 7
-
-#define TXRETRIES 2 /* # of retries for tx frames */
-
-#define BRCMF_RXBOUND 50 /* Default for max rx frames in
- one scheduling */
-
-#define BRCMF_TXBOUND 20 /* Default for max tx frames in
- one scheduling */
-
-#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */
-
-#define MEMBLOCK 2048 /* Block size used for downloading
- of dongle image */
-#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
- biggest possible glom */
-
-#define BRCMF_FIRSTREAD (1 << 6)
-
-#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */
-
-/* SBSDIO_DEVICE_CTL */
-
-/* 1: device will assert busy signal when receiving CMD53 */
-#define SBSDIO_DEVCTL_SETBUSY 0x01
-/* 1: assertion of sdio interrupt is synchronous to the sdio clock */
-#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02
-/* 1: mask all interrupts to host except the chipActive (rev 8) */
-#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04
-/* 1: isolate internal sdio signals, put external pads in tri-state; requires
- * sdio bus power cycle to clear (rev 9) */
-#define SBSDIO_DEVCTL_PADS_ISO 0x08
-/* Force SD->SB reset mapping (rev 11) */
-#define SBSDIO_DEVCTL_SB_RST_CTL 0x30
-/* Determined by CoreControl bit */
-#define SBSDIO_DEVCTL_RST_CORECTL 0x00
-/* Force backplane reset */
-#define SBSDIO_DEVCTL_RST_BPRESET 0x10
-/* Force no backplane reset */
-#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20
-
-/* direct(mapped) cis space */
-
-/* MAPPED common CIS address */
-#define SBSDIO_CIS_BASE_COMMON 0x1000
-/* maximum bytes in one CIS */
-#define SBSDIO_CIS_SIZE_LIMIT 0x200
-/* cis offset addr is < 17 bits */
-#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF
-
-/* manfid tuple length, include tuple, link bytes */
-#define SBSDIO_CIS_MANFID_TUPLE_LEN 6
-
-#define CORE_BUS_REG(base, field) \
- (base + offsetof(struct sdpcmd_regs, field))
-
-/* SDIO function 1 register CHIPCLKCSR */
-/* Force ALP request to backplane */
-#define SBSDIO_FORCE_ALP 0x01
-/* Force HT request to backplane */
-#define SBSDIO_FORCE_HT 0x02
-/* Force ILP request to backplane */
-#define SBSDIO_FORCE_ILP 0x04
-/* Make ALP ready (power up xtal) */
-#define SBSDIO_ALP_AVAIL_REQ 0x08
-/* Make HT ready (power up PLL) */
-#define SBSDIO_HT_AVAIL_REQ 0x10
-/* Squelch clock requests from HW */
-#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20
-/* Status: ALP is ready */
-#define SBSDIO_ALP_AVAIL 0x40
-/* Status: HT is ready */
-#define SBSDIO_HT_AVAIL 0x80
-#define SBSDIO_CSR_MASK 0x1F
-#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
-#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
-#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
-#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
-#define SBSDIO_CLKAV(regval, alponly) \
- (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
-
-/* intstatus */
-#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */
-#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */
-#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */
-#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */
-#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */
-#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */
-#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */
-#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */
-#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */
-#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */
-#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */
-#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */
-#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */
-#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */
-#define I_PC (1 << 10) /* descriptor error */
-#define I_PD (1 << 11) /* data error */
-#define I_DE (1 << 12) /* Descriptor protocol Error */
-#define I_RU (1 << 13) /* Receive descriptor Underflow */
-#define I_RO (1 << 14) /* Receive fifo Overflow */
-#define I_XU (1 << 15) /* Transmit fifo Underflow */
-#define I_RI (1 << 16) /* Receive Interrupt */
-#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */
-#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */
-#define I_XI (1 << 24) /* Transmit Interrupt */
-#define I_RF_TERM (1 << 25) /* Read Frame Terminate */
-#define I_WF_TERM (1 << 26) /* Write Frame Terminate */
-#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */
-#define I_SBINT (1 << 28) /* sbintstatus Interrupt */
-#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */
-#define I_SRESET (1 << 30) /* CCCR RES interrupt */
-#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */
-#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
-#define I_DMA (I_RI | I_XI | I_ERRORS)
-
-/* corecontrol */
-#define CC_CISRDY (1 << 0) /* CIS Ready */
-#define CC_BPRESEN (1 << 1) /* CCCR RES signal */
-#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */
-#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */
-#define CC_XMTDATAAVAIL_MODE (1 << 4)
-#define CC_XMTDATAAVAIL_CTRL (1 << 5)
-
-/* SDA_FRAMECTRL */
-#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */
-#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */
-#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */
-#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */
-
-/*
- * Software allocation of To SB Mailbox resources
- */
-
-/* tosbmailbox bits corresponding to intstatus bits */
-#define SMB_NAK (1 << 0) /* Frame NAK */
-#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
-#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
-#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */
-
-/* tosbmailboxdata */
-#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */
-
-/*
- * Software allocation of To Host Mailbox resources
- */
-
-/* intstatus bits */
-#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */
-#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */
-#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */
-#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */
-
-/* tohostmailboxdata */
-#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */
-#define HMB_DATA_DEVREADY 2 /* talk to host after enable */
-#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */
-#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */
-
-#define HMB_DATA_FCDATA_MASK 0xff000000
-#define HMB_DATA_FCDATA_SHIFT 24
-
-#define HMB_DATA_VERSION_MASK 0x00ff0000
-#define HMB_DATA_VERSION_SHIFT 16
-
-/*
- * Software-defined protocol header
- */
-
-/* Current protocol version */
-#define SDPCM_PROT_VERSION 4
-
-/*
- * Shared structure between dongle and the host.
- * The structure contains pointers to trap or assert information.
- */
-#define SDPCM_SHARED_VERSION 0x0003
-#define SDPCM_SHARED_VERSION_MASK 0x00FF
-#define SDPCM_SHARED_ASSERT_BUILT 0x0100
-#define SDPCM_SHARED_ASSERT 0x0200
-#define SDPCM_SHARED_TRAP 0x0400
-
-/* Space for header read, limit for data packets */
-#define MAX_HDR_READ (1 << 6)
-#define MAX_RX_DATASZ 2048
-
-/* Bump up limit on waiting for HT to account for first startup;
- * if the image is doing a CRC calculation before programming the PMU
- * for HT availability, it could take a couple hundred ms more, so
- * max out at a 1 second (1000000us).
- */
-#undef PMU_MAX_TRANSITION_DLY
-#define PMU_MAX_TRANSITION_DLY 1000000
-
-/* Value for ChipClockCSR during initial setup */
-#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
- SBSDIO_ALP_AVAIL_REQ)
-
-/* Flags for SDH calls */
-#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
-
-#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change
- * when idle
- */
-#define BRCMF_IDLE_INTERVAL 1
-
-#define KSO_WAIT_US 50
-#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
-
-/*
- * Conversion of 802.1D priority to precedence level
- */
-static uint prio2prec(u32 prio)
-{
- return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
- (prio^2) : prio;
-}
-
-#ifdef DEBUG
-/* Device console log buffer state */
-struct brcmf_console {
- uint count; /* Poll interval msec counter */
- uint log_addr; /* Log struct address (fixed) */
- struct rte_log_le log_le; /* Log struct (host copy) */
- uint bufsize; /* Size of log buffer */
- u8 *buf; /* Log buffer (host copy) */
- uint last; /* Last buffer read index */
-};
-
-struct brcmf_trap_info {
- __le32 type;
- __le32 epc;
- __le32 cpsr;
- __le32 spsr;
- __le32 r0; /* a1 */
- __le32 r1; /* a2 */
- __le32 r2; /* a3 */
- __le32 r3; /* a4 */
- __le32 r4; /* v1 */
- __le32 r5; /* v2 */
- __le32 r6; /* v3 */
- __le32 r7; /* v4 */
- __le32 r8; /* v5 */
- __le32 r9; /* sb/v6 */
- __le32 r10; /* sl/v7 */
- __le32 r11; /* fp/v8 */
- __le32 r12; /* ip */
- __le32 r13; /* sp */
- __le32 r14; /* lr */
- __le32 pc; /* r15 */
-};
-#endif /* DEBUG */
-
-struct sdpcm_shared {
- u32 flags;
- u32 trap_addr;
- u32 assert_exp_addr;
- u32 assert_file_addr;
- u32 assert_line;
- u32 console_addr; /* Address of struct rte_console */
- u32 msgtrace_addr;
- u8 tag[32];
- u32 brpt_addr;
-};
-
-struct sdpcm_shared_le {
- __le32 flags;
- __le32 trap_addr;
- __le32 assert_exp_addr;
- __le32 assert_file_addr;
- __le32 assert_line;
- __le32 console_addr; /* Address of struct rte_console */
- __le32 msgtrace_addr;
- u8 tag[32];
- __le32 brpt_addr;
-};
-
-/* dongle SDIO bus specific header info */
-struct brcmf_sdio_hdrinfo {
- u8 seq_num;
- u8 channel;
- u16 len;
- u16 len_left;
- u16 len_nxtfrm;
- u8 dat_offset;
- bool lastfrm;
- u16 tail_pad;
-};
-
-/*
- * hold counter variables
- */
-struct brcmf_sdio_count {
- uint intrcount; /* Count of device interrupt callbacks */
- uint lastintrs; /* Count as of last watchdog timer */
- uint pollcnt; /* Count of active polls */
- uint regfails; /* Count of R_REG failures */
- uint tx_sderrs; /* Count of tx attempts with sd errors */
- uint fcqueued; /* Tx packets that got queued */
- uint rxrtx; /* Count of rtx requests (NAK to dongle) */
- uint rx_toolong; /* Receive frames too long to receive */
- uint rxc_errors; /* SDIO errors when reading control frames */
- uint rx_hdrfail; /* SDIO errors on header reads */
- uint rx_badhdr; /* Bad received headers (roosync?) */
- uint rx_badseq; /* Mismatched rx sequence number */
- uint fc_rcvd; /* Number of flow-control events received */
- uint fc_xoff; /* Number which turned on flow-control */
- uint fc_xon; /* Number which turned off flow-control */
- uint rxglomfail; /* Failed deglom attempts */
- uint rxglomframes; /* Number of glom frames (superframes) */
- uint rxglompkts; /* Number of packets from glom frames */
- uint f2rxhdrs; /* Number of header reads */
- uint f2rxdata; /* Number of frame data reads */
- uint f2txdata; /* Number of f2 frame writes */
- uint f1regdata; /* Number of f1 register accesses */
- uint tickcnt; /* Number of watchdog been schedule */
- ulong tx_ctlerrs; /* Err of sending ctrl frames */
- ulong tx_ctlpkts; /* Ctrl frames sent to dongle */
- ulong rx_ctlerrs; /* Err of processing rx ctrl frames */
- ulong rx_ctlpkts; /* Ctrl frames processed from dongle */
- ulong rx_readahead_cnt; /* packets where header read-ahead was used */
-};
-
-/* misc chip info needed by some of the routines */
-/* Private data for SDIO bus interaction */
-struct brcmf_sdio {
- struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
- struct brcmf_chip *ci; /* Chip info struct */
-
- u32 hostintmask; /* Copy of Host Interrupt Mask */
- atomic_t intstatus; /* Intstatus bits (events) pending */
- atomic_t fcstate; /* State of dongle flow-control */
-
- uint blocksize; /* Block size of SDIO transfers */
- uint roundup; /* Max roundup limit */
-
- struct pktq txq; /* Queue length used for flow-control */
- u8 flowcontrol; /* per prio flow control bitmask */
- u8 tx_seq; /* Transmit sequence number (next) */
- u8 tx_max; /* Maximum transmit sequence allowed */
-
- u8 *hdrbuf; /* buffer for handling rx frame */
- u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
- u8 rx_seq; /* Receive sequence number (expected) */
- struct brcmf_sdio_hdrinfo cur_read;
- /* info of current read frame */
- bool rxskip; /* Skip receive (awaiting NAK ACK) */
- bool rxpending; /* Data frame pending in dongle */
-
- uint rxbound; /* Rx frames to read before resched */
- uint txbound; /* Tx frames to send before resched */
- uint txminmax;
-
- struct sk_buff *glomd; /* Packet containing glomming descriptor */
- struct sk_buff_head glom; /* Packet list for glommed superframe */
- uint glomerr; /* Glom packet read errors */
-
- u8 *rxbuf; /* Buffer for receiving control packets */
- uint rxblen; /* Allocated length of rxbuf */
- u8 *rxctl; /* Aligned pointer into rxbuf */
- u8 *rxctl_orig; /* pointer for freeing rxctl */
- uint rxlen; /* Length of valid data in buffer */
- spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */
-
- u8 sdpcm_ver; /* Bus protocol reported by dongle */
-
- bool intr; /* Use interrupts */
- bool poll; /* Use polling */
- atomic_t ipend; /* Device interrupt is pending */
- uint spurious; /* Count of spurious interrupts */
- uint pollrate; /* Ticks between device polls */
- uint polltick; /* Tick counter */
-
-#ifdef DEBUG
- uint console_interval;
- struct brcmf_console console; /* Console output polling support */
- uint console_addr; /* Console address from shared struct */
-#endif /* DEBUG */
-
- uint clkstate; /* State of sd and backplane clock(s) */
- s32 idletime; /* Control for activity timeout */
- s32 idlecount; /* Activity timeout counter */
- s32 idleclock; /* How to set bus driver when idle */
- bool rxflow_mode; /* Rx flow control mode */
- bool rxflow; /* Is rx flow control on */
- bool alp_only; /* Don't use HT clock (ALP only) */
-
- u8 *ctrl_frame_buf;
- u16 ctrl_frame_len;
- bool ctrl_frame_stat;
- int ctrl_frame_err;
-
- spinlock_t txq_lock; /* protect bus->txq */
- wait_queue_head_t ctrl_wait;
- wait_queue_head_t dcmd_resp_wait;
-
- struct timer_list timer;
- struct completion watchdog_wait;
- struct task_struct *watchdog_tsk;
- bool wd_timer_valid;
- uint save_ms;
-
- struct workqueue_struct *brcmf_wq;
- struct work_struct datawork;
- bool dpc_triggered;
- bool dpc_running;
-
- bool txoff; /* Transmit flow-controlled */
- struct brcmf_sdio_count sdcnt;
- bool sr_enabled; /* SaveRestore enabled */
- bool sleeping;
-
- u8 tx_hdrlen; /* sdio bus header length for tx packet */
- bool txglom; /* host tx glomming enable flag */
- u16 head_align; /* buffer pointer alignment */
- u16 sgentry_align; /* scatter-gather buffer alignment */
-};
-
-/* clkstate */
-#define CLK_NONE 0
-#define CLK_SDONLY 1
-#define CLK_PENDING 2
-#define CLK_AVAIL 3
-
-#ifdef DEBUG
-static int qcount[NUMPRIO];
-#endif /* DEBUG */
-
-#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */
-
-#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
-
-/* Retry count for register access failures */
-static const uint retry_limit = 2;
-
-/* Limit on rounding up frames */
-static const uint max_roundup = 512;
-
-#define ALIGNMENT 4
-
-enum brcmf_sdio_frmtype {
- BRCMF_SDIO_FT_NORMAL,
- BRCMF_SDIO_FT_SUPER,
- BRCMF_SDIO_FT_SUB,
-};
-
-#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
-
-/* SDIO Pad drive strength to select value mappings */
-struct sdiod_drive_str {
- u8 strength; /* Pad Drive Strength in mA */
- u8 sel; /* Chip-specific select value */
-};
-
-/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
-static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
- {32, 0x6},
- {26, 0x7},
- {22, 0x4},
- {16, 0x5},
- {12, 0x2},
- {8, 0x3},
- {4, 0x0},
- {0, 0x1}
-};
-
-/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
-static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
- {6, 0x7},
- {5, 0x6},
- {4, 0x5},
- {3, 0x4},
- {2, 0x2},
- {1, 0x1},
- {0, 0x0}
-};
-
-/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
-static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
- {3, 0x3},
- {2, 0x2},
- {1, 0x1},
- {0, 0x0} };
-
-/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
-static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
- {16, 0x7},
- {12, 0x5},
- {8, 0x3},
- {4, 0x1}
-};
-
-#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin"
-#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt"
-#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin"
-#define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt"
-#define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin"
-#define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt"
-#define BCM43241B5_FIRMWARE_NAME "brcm/brcmfmac43241b5-sdio.bin"
-#define BCM43241B5_NVRAM_NAME "brcm/brcmfmac43241b5-sdio.txt"
-#define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin"
-#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt"
-#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin"
-#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt"
-#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin"
-#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt"
-#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin"
-#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt"
-#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin"
-#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt"
-#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin"
-#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt"
-#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin"
-#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt"
-#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin"
-#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt"
-#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin"
-#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt"
-#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin"
-#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt"
-
-MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43143_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4329_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4330_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4334_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43340_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4335_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43430_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43455_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4354_NVRAM_NAME);
-
-struct brcmf_firmware_names {
- u32 chipid;
- u32 revmsk;
- const char *bin;
- const char *nv;
-};
-
-enum brcmf_firmware_type {
- BRCMF_FIRMWARE_BIN,
- BRCMF_FIRMWARE_NVRAM
-};
-
-#define BRCMF_FIRMWARE_NVRAM(name) \
- name ## _FIRMWARE_NAME, name ## _NVRAM_NAME
-
-static const struct brcmf_firmware_names brcmf_fwname_data[] = {
- { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
- { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
- { BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
- { BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) },
- { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
- { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
- { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
- { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) },
- { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
- { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
- { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
- { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) },
- { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) },
- { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
-};
-
-static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
- struct brcmf_sdio_dev *sdiodev)
-{
- int i;
- char end;
-
- for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
- if (brcmf_fwname_data[i].chipid == ci->chip &&
- brcmf_fwname_data[i].revmsk & BIT(ci->chiprev))
- break;
- }
-
- if (i == ARRAY_SIZE(brcmf_fwname_data)) {
- brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev);
- return -ENODEV;
- }
-
- /* check if firmware path is provided by module parameter */
- if (brcmf_firmware_path[0] != '\0') {
- strlcpy(sdiodev->fw_name, brcmf_firmware_path,
- sizeof(sdiodev->fw_name));
- strlcpy(sdiodev->nvram_name, brcmf_firmware_path,
- sizeof(sdiodev->nvram_name));
-
- end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
- if (end != '/') {
- strlcat(sdiodev->fw_name, "/",
- sizeof(sdiodev->fw_name));
- strlcat(sdiodev->nvram_name, "/",
- sizeof(sdiodev->nvram_name));
- }
- }
- strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin,
- sizeof(sdiodev->fw_name));
- strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv,
- sizeof(sdiodev->nvram_name));
-
- return 0;
-}
-
-static void pkt_align(struct sk_buff *p, int len, int align)
-{
- uint datalign;
- datalign = (unsigned long)(p->data);
- datalign = roundup(datalign, (align)) - datalign;
- if (datalign)
- skb_pull(p, datalign);
- __skb_trim(p, len);
-}
-
-/* To check if there's window offered */
-static bool data_ok(struct brcmf_sdio *bus)
-{
- return (u8)(bus->tx_max - bus->tx_seq) != 0 &&
- ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0;
-}
-
-/*
- * Reads a register in the SDIO hardware block. This block occupies a series of
- * adresses on the 32 bit backplane bus.
- */
-static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
-{
- struct brcmf_core *core;
- int ret;
-
- core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
- *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);
-
- return ret;
-}
-
-static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
-{
- struct brcmf_core *core;
- int ret;
-
- core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
- brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);
-
- return ret;
-}
-
-static int
-brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
-{
- u8 wr_val = 0, rd_val, cmp_val, bmask;
- int err = 0;
- int try_cnt = 0;
-
- brcmf_dbg(TRACE, "Enter: on=%d\n", on);
-
- wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
- /* 1st KSO write goes to AOS wake up core if device is asleep */
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
- wr_val, &err);
-
- if (on) {
- /* device WAKEUP through KSO:
- * write bit 0 & read back until
- * both bits 0 (kso bit) & 1 (dev on status) are set
- */
- cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
- SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK;
- bmask = cmp_val;
- usleep_range(2000, 3000);
- } else {
- /* Put device to sleep, turn off KSO */
- cmp_val = 0;
- /* only check for bit0, bit1(dev on status) may not
- * get cleared right away
- */
- bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
- }
-
- do {
- /* reliable KSO bit set/clr:
- * the sdiod sleep write access is synced to PMU 32khz clk
- * just one write attempt may fail,
- * read it back until it matches written value
- */
- rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
- &err);
- if (((rd_val & bmask) == cmp_val) && !err)
- break;
-
- udelay(KSO_WAIT_US);
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
- wr_val, &err);
- } while (try_cnt++ < MAX_KSO_ATTEMPTS);
-
- if (try_cnt > 2)
- brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt,
- rd_val, err);
-
- if (try_cnt > MAX_KSO_ATTEMPTS)
- brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
-
- return err;
-}
-
-#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
-
-/* Turn backplane clock on or off */
-static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
-{
- int err;
- u8 clkctl, clkreq, devctl;
- unsigned long timeout;
-
- brcmf_dbg(SDIO, "Enter\n");
-
- clkctl = 0;
-
- if (bus->sr_enabled) {
- bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY);
- return 0;
- }
-
- if (on) {
- /* Request HT Avail */
- clkreq =
- bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
-
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- clkreq, &err);
- if (err) {
- brcmf_err("HT Avail request error: %d\n", err);
- return -EBADE;
- }
-
- /* Check current status */
- clkctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR, &err);
- if (err) {
- brcmf_err("HT Avail read error: %d\n", err);
- return -EBADE;
- }
-
- /* Go to pending and await interrupt if appropriate */
- if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
- /* Allow only clock-available interrupt */
- devctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_DEVICE_CTL, &err);
- if (err) {
- brcmf_err("Devctl error setting CA: %d\n",
- err);
- return -EBADE;
- }
-
- devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
- devctl, &err);
- brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
- bus->clkstate = CLK_PENDING;
-
- return 0;
- } else if (bus->clkstate == CLK_PENDING) {
- /* Cancel CA-only interrupt filter */
- devctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_DEVICE_CTL, &err);
- devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
- devctl, &err);
- }
-
- /* Otherwise, wait here (polling) for HT Avail */
- timeout = jiffies +
- msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
- while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
- clkctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR,
- &err);
- if (time_after(jiffies, timeout))
- break;
- else
- usleep_range(5000, 10000);
- }
- if (err) {
- brcmf_err("HT Avail request error: %d\n", err);
- return -EBADE;
- }
- if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
- brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n",
- PMU_MAX_TRANSITION_DLY, clkctl);
- return -EBADE;
- }
-
- /* Mark clock available */
- bus->clkstate = CLK_AVAIL;
- brcmf_dbg(SDIO, "CLKCTL: turned ON\n");
-
-#if defined(DEBUG)
- if (!bus->alp_only) {
- if (SBSDIO_ALPONLY(clkctl))
- brcmf_err("HT Clock should be on\n");
- }
-#endif /* defined (DEBUG) */
-
- } else {
- clkreq = 0;
-
- if (bus->clkstate == CLK_PENDING) {
- /* Cancel CA-only interrupt filter */
- devctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_DEVICE_CTL, &err);
- devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
- devctl, &err);
- }
-
- bus->clkstate = CLK_SDONLY;
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- clkreq, &err);
- brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
- if (err) {
- brcmf_err("Failed access turning clock off: %d\n",
- err);
- return -EBADE;
- }
- }
- return 0;
-}
-
-/* Change idle/active SD state */
-static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on)
-{
- brcmf_dbg(SDIO, "Enter\n");
-
- if (on)
- bus->clkstate = CLK_SDONLY;
- else
- bus->clkstate = CLK_NONE;
-
- return 0;
-}
-
-/* Transition SD and backplane clock readiness */
-static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
-{
-#ifdef DEBUG
- uint oldstate = bus->clkstate;
-#endif /* DEBUG */
-
- brcmf_dbg(SDIO, "Enter\n");
-
- /* Early exit if we're already there */
- if (bus->clkstate == target)
- return 0;
-
- switch (target) {
- case CLK_AVAIL:
- /* Make sure SD clock is available */
- if (bus->clkstate == CLK_NONE)
- brcmf_sdio_sdclk(bus, true);
- /* Now request HT Avail on the backplane */
- brcmf_sdio_htclk(bus, true, pendok);
- break;
-
- case CLK_SDONLY:
- /* Remove HT request, or bring up SD clock */
- if (bus->clkstate == CLK_NONE)
- brcmf_sdio_sdclk(bus, true);
- else if (bus->clkstate == CLK_AVAIL)
- brcmf_sdio_htclk(bus, false, false);
- else
- brcmf_err("request for %d -> %d\n",
- bus->clkstate, target);
- break;
-
- case CLK_NONE:
- /* Make sure to remove HT request */
- if (bus->clkstate == CLK_AVAIL)
- brcmf_sdio_htclk(bus, false, false);
- /* Now remove the SD clock */
- brcmf_sdio_sdclk(bus, false);
- break;
- }
-#ifdef DEBUG
- brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate);
-#endif /* DEBUG */
-
- return 0;
-}
-
-static int
-brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
-{
- int err = 0;
- u8 clkcsr;
-
- brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
- (sleep ? "SLEEP" : "WAKE"),
- (bus->sleeping ? "SLEEP" : "WAKE"));
-
- /* If SR is enabled control bus state with KSO */
- if (bus->sr_enabled) {
- /* Done if we're already in the requested state */
- if (sleep == bus->sleeping)
- goto end;
-
- /* Going to sleep */
- if (sleep) {
- clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR,
- &err);
- if ((clkcsr & SBSDIO_CSR_MASK) == 0) {
- brcmf_dbg(SDIO, "no clock, set ALP\n");
- brcmf_sdiod_regwb(bus->sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR,
- SBSDIO_ALP_AVAIL_REQ, &err);
- }
- err = brcmf_sdio_kso_control(bus, false);
- } else {
- err = brcmf_sdio_kso_control(bus, true);
- }
- if (err) {
- brcmf_err("error while changing bus sleep state %d\n",
- err);
- goto done;
- }
- }
-
-end:
- /* control clocks */
- if (sleep) {
- if (!bus->sr_enabled)
- brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
- } else {
- brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
- }
- bus->sleeping = sleep;
- brcmf_dbg(SDIO, "new state %s\n",
- (sleep ? "SLEEP" : "WAKE"));
-done:
- brcmf_dbg(SDIO, "Exit: err=%d\n", err);
- return err;
-
-}
-
-#ifdef DEBUG
-static inline bool brcmf_sdio_valid_shared_address(u32 addr)
-{
- return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
-}
-
-static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
- struct sdpcm_shared *sh)
-{
- u32 addr = 0;
- int rv;
- u32 shaddr = 0;
- struct sdpcm_shared_le sh_le;
- __le32 addr_le;
-
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_bus_sleep(bus, false, false);
-
- /*
- * Read last word in socram to determine
- * address of sdpcm_shared structure
- */
- shaddr = bus->ci->rambase + bus->ci->ramsize - 4;
- if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci))
- shaddr -= bus->ci->srsize;
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr,
- (u8 *)&addr_le, 4);
- if (rv < 0)
- goto fail;
-
- /*
- * Check if addr is valid.
- * NVRAM length at the end of memory should have been overwritten.
- */
- addr = le32_to_cpu(addr_le);
- if (!brcmf_sdio_valid_shared_address(addr)) {
- brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr);
- rv = -EINVAL;
- goto fail;
- }
-
- brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
-
- /* Read hndrte_shared structure */
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
- sizeof(struct sdpcm_shared_le));
- if (rv < 0)
- goto fail;
-
- sdio_release_host(bus->sdiodev->func[1]);
-
- /* Endianness */
- sh->flags = le32_to_cpu(sh_le.flags);
- sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
- sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
- sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
- sh->assert_line = le32_to_cpu(sh_le.assert_line);
- sh->console_addr = le32_to_cpu(sh_le.console_addr);
- sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
-
- if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
- brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
- SDPCM_SHARED_VERSION,
- sh->flags & SDPCM_SHARED_VERSION_MASK);
- return -EPROTO;
- }
- return 0;
-
-fail:
- brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n",
- rv, addr);
- sdio_release_host(bus->sdiodev->func[1]);
- return rv;
-}
-
-static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
-{
- struct sdpcm_shared sh;
-
- if (brcmf_sdio_readshared(bus, &sh) == 0)
- bus->console_addr = sh.console_addr;
-}
-#else
-static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
-{
-}
-#endif /* DEBUG */
-
-static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
-{
- u32 intstatus = 0;
- u32 hmb_data;
- u8 fcbits;
- int ret;
-
- brcmf_dbg(SDIO, "Enter\n");
-
- /* Read mailbox data and ack that we did so */
- ret = r_sdreg32(bus, &hmb_data,
- offsetof(struct sdpcmd_regs, tohostmailboxdata));
-
- if (ret == 0)
- w_sdreg32(bus, SMB_INT_ACK,
- offsetof(struct sdpcmd_regs, tosbmailbox));
- bus->sdcnt.f1regdata += 2;
-
- /* Dongle recomposed rx frames, accept them again */
- if (hmb_data & HMB_DATA_NAKHANDLED) {
- brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n",
- bus->rx_seq);
- if (!bus->rxskip)
- brcmf_err("unexpected NAKHANDLED!\n");
-
- bus->rxskip = false;
- intstatus |= I_HMB_FRAME_IND;
- }
-
- /*
- * DEVREADY does not occur with gSPI.
- */
- if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
- bus->sdpcm_ver =
- (hmb_data & HMB_DATA_VERSION_MASK) >>
- HMB_DATA_VERSION_SHIFT;
- if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
- brcmf_err("Version mismatch, dongle reports %d, "
- "expecting %d\n",
- bus->sdpcm_ver, SDPCM_PROT_VERSION);
- else
- brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
- bus->sdpcm_ver);
-
- /*
- * Retrieve console state address now that firmware should have
- * updated it.
- */
- brcmf_sdio_get_console_addr(bus);
- }
-
- /*
- * Flow Control has been moved into the RX headers and this out of band
- * method isn't used any more.
- * remaining backward compatible with older dongles.
- */
- if (hmb_data & HMB_DATA_FC) {
- fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
- HMB_DATA_FCDATA_SHIFT;
-
- if (fcbits & ~bus->flowcontrol)
- bus->sdcnt.fc_xoff++;
-
- if (bus->flowcontrol & ~fcbits)
- bus->sdcnt.fc_xon++;
-
- bus->sdcnt.fc_rcvd++;
- bus->flowcontrol = fcbits;
- }
-
- /* Shouldn't be any others */
- if (hmb_data & ~(HMB_DATA_DEVREADY |
- HMB_DATA_NAKHANDLED |
- HMB_DATA_FC |
- HMB_DATA_FWREADY |
- HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
- brcmf_err("Unknown mailbox data content: 0x%02x\n",
- hmb_data);
-
- return intstatus;
-}
-
-static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
-{
- uint retries = 0;
- u16 lastrbc;
- u8 hi, lo;
- int err;
-
- brcmf_err("%sterminate frame%s\n",
- abort ? "abort command, " : "",
- rtx ? ", send NAK" : "");
-
- if (abort)
- brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
-
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
- SFC_RF_TERM, &err);
- bus->sdcnt.f1regdata++;
-
- /* Wait until the packet has been flushed (device/FIFO stable) */
- for (lastrbc = retries = 0xffff; retries > 0; retries--) {
- hi = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_RFRAMEBCHI, &err);
- lo = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_RFRAMEBCLO, &err);
- bus->sdcnt.f1regdata += 2;
-
- if ((hi == 0) && (lo == 0))
- break;
-
- if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
- brcmf_err("count growing: last 0x%04x now 0x%04x\n",
- lastrbc, (hi << 8) + lo);
- }
- lastrbc = (hi << 8) + lo;
- }
-
- if (!retries)
- brcmf_err("count never zeroed: last 0x%04x\n", lastrbc);
- else
- brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries);
-
- if (rtx) {
- bus->sdcnt.rxrtx++;
- err = w_sdreg32(bus, SMB_NAK,
- offsetof(struct sdpcmd_regs, tosbmailbox));
-
- bus->sdcnt.f1regdata++;
- if (err == 0)
- bus->rxskip = true;
- }
-
- /* Clear partial in any case */
- bus->cur_read.len = 0;
-}
-
-static void brcmf_sdio_txfail(struct brcmf_sdio *bus)
-{
- struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
- u8 i, hi, lo;
-
- /* On failure, abort the command and terminate the frame */
- brcmf_err("sdio error, abort command and terminate frame\n");
- bus->sdcnt.tx_sderrs++;
-
- brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2);
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
- bus->sdcnt.f1regdata++;
-
- for (i = 0; i < 3; i++) {
- hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
- lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
- bus->sdcnt.f1regdata += 2;
- if ((hi == 0) && (lo == 0))
- break;
- }
-}
-
-/* return total length of buffer chain */
-static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)
-{
- struct sk_buff *p;
- uint total;
-
- total = 0;
- skb_queue_walk(&bus->glom, p)
- total += p->len;
- return total;
-}
-
-static void brcmf_sdio_free_glom(struct brcmf_sdio *bus)
-{
- struct sk_buff *cur, *next;
-
- skb_queue_walk_safe(&bus->glom, cur, next) {
- skb_unlink(cur, &bus->glom);
- brcmu_pkt_buf_free_skb(cur);
- }
-}
-
-/**
- * brcmfmac sdio bus specific header
- * This is the lowest layer header wrapped on the packets transmitted between
- * host and WiFi dongle which contains information needed for SDIO core and
- * firmware
- *
- * It consists of 3 parts: hardware header, hardware extension header and
- * software header
- * hardware header (frame tag) - 4 bytes
- * Byte 0~1: Frame length
- * Byte 2~3: Checksum, bit-wise inverse of frame length
- * hardware extension header - 8 bytes
- * Tx glom mode only, N/A for Rx or normal Tx
- * Byte 0~1: Packet length excluding hw frame tag
- * Byte 2: Reserved
- * Byte 3: Frame flags, bit 0: last frame indication
- * Byte 4~5: Reserved
- * Byte 6~7: Tail padding length
- * software header - 8 bytes
- * Byte 0: Rx/Tx sequence number
- * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
- * Byte 2: Length of next data frame, reserved for Tx
- * Byte 3: Data offset
- * Byte 4: Flow control bits, reserved for Tx
- * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
- * Byte 6~7: Reserved
- */
-#define SDPCM_HWHDR_LEN 4
-#define SDPCM_HWEXT_LEN 8
-#define SDPCM_SWHDR_LEN 8
-#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
-/* software header */
-#define SDPCM_SEQ_MASK 0x000000ff
-#define SDPCM_SEQ_WRAP 256
-#define SDPCM_CHANNEL_MASK 0x00000f00
-#define SDPCM_CHANNEL_SHIFT 8
-#define SDPCM_CONTROL_CHANNEL 0 /* Control */
-#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */
-#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */
-#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */
-#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */
-#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
-#define SDPCM_NEXTLEN_MASK 0x00ff0000
-#define SDPCM_NEXTLEN_SHIFT 16
-#define SDPCM_DOFFSET_MASK 0xff000000
-#define SDPCM_DOFFSET_SHIFT 24
-#define SDPCM_FCMASK_MASK 0x000000ff
-#define SDPCM_WINDOW_MASK 0x0000ff00
-#define SDPCM_WINDOW_SHIFT 8
-
-static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
-{
- u32 hdrvalue;
- hdrvalue = *(u32 *)swheader;
- return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
-}
-
-static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
- struct brcmf_sdio_hdrinfo *rd,
- enum brcmf_sdio_frmtype type)
-{
- u16 len, checksum;
- u8 rx_seq, fc, tx_seq_max;
- u32 swheader;
-
- trace_brcmf_sdpcm_hdr(SDPCM_RX, header);
-
- /* hw header */
- len = get_unaligned_le16(header);
- checksum = get_unaligned_le16(header + sizeof(u16));
- /* All zero means no more to read */
- if (!(len | checksum)) {
- bus->rxpending = false;
- return -ENODATA;
- }
- if ((u16)(~(len ^ checksum))) {
- brcmf_err("HW header checksum error\n");
- bus->sdcnt.rx_badhdr++;
- brcmf_sdio_rxfail(bus, false, false);
- return -EIO;
- }
- if (len < SDPCM_HDRLEN) {
- brcmf_err("HW header length error\n");
- return -EPROTO;
- }
- if (type == BRCMF_SDIO_FT_SUPER &&
- (roundup(len, bus->blocksize) != rd->len)) {
- brcmf_err("HW superframe header length error\n");
- return -EPROTO;
- }
- if (type == BRCMF_SDIO_FT_SUB && len > rd->len) {
- brcmf_err("HW subframe header length error\n");
- return -EPROTO;
- }
- rd->len = len;
-
- /* software header */
- header += SDPCM_HWHDR_LEN;
- swheader = le32_to_cpu(*(__le32 *)header);
- if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
- brcmf_err("Glom descriptor found in superframe head\n");
- rd->len = 0;
- return -EINVAL;
- }
- rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
- rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
- if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
- type != BRCMF_SDIO_FT_SUPER) {
- brcmf_err("HW header length too long\n");
- bus->sdcnt.rx_toolong++;
- brcmf_sdio_rxfail(bus, false, false);
- rd->len = 0;
- return -EPROTO;
- }
- if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) {
- brcmf_err("Wrong channel for superframe\n");
- rd->len = 0;
- return -EINVAL;
- }
- if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL &&
- rd->channel != SDPCM_EVENT_CHANNEL) {
- brcmf_err("Wrong channel for subframe\n");
- rd->len = 0;
- return -EINVAL;
- }
- rd->dat_offset = brcmf_sdio_getdatoffset(header);
- if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
- brcmf_err("seq %d: bad data offset\n", rx_seq);
- bus->sdcnt.rx_badhdr++;
- brcmf_sdio_rxfail(bus, false, false);
- rd->len = 0;
- return -ENXIO;
- }
- if (rd->seq_num != rx_seq) {
- brcmf_err("seq %d: sequence number error, expect %d\n",
- rx_seq, rd->seq_num);
- bus->sdcnt.rx_badseq++;
- rd->seq_num = rx_seq;
- }
- /* no need to check the reset for subframe */
- if (type == BRCMF_SDIO_FT_SUB)
- return 0;
- rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
- if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
- /* only warm for NON glom packet */
- if (rd->channel != SDPCM_GLOM_CHANNEL)
- brcmf_err("seq %d: next length error\n", rx_seq);
- rd->len_nxtfrm = 0;
- }
- swheader = le32_to_cpu(*(__le32 *)(header + 4));
- fc = swheader & SDPCM_FCMASK_MASK;
- if (bus->flowcontrol != fc) {
- if (~bus->flowcontrol & fc)
- bus->sdcnt.fc_xoff++;
- if (bus->flowcontrol & ~fc)
- bus->sdcnt.fc_xon++;
- bus->sdcnt.fc_rcvd++;
- bus->flowcontrol = fc;
- }
- tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
- if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
- brcmf_err("seq %d: max tx seq number error\n", rx_seq);
- tx_seq_max = bus->tx_seq + 2;
- }
- bus->tx_max = tx_seq_max;
-
- return 0;
-}
-
-static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
-{
- *(__le16 *)header = cpu_to_le16(frm_length);
- *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
-}
-
-static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
- struct brcmf_sdio_hdrinfo *hd_info)
-{
- u32 hdrval;
- u8 hdr_offset;
-
- brcmf_sdio_update_hwhdr(header, hd_info->len);
- hdr_offset = SDPCM_HWHDR_LEN;
-
- if (bus->txglom) {
- hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24);
- *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
- hdrval = (u16)hd_info->tail_pad << 16;
- *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval);
- hdr_offset += SDPCM_HWEXT_LEN;
- }
-
- hdrval = hd_info->seq_num;
- hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) &
- SDPCM_CHANNEL_MASK;
- hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) &
- SDPCM_DOFFSET_MASK;
- *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
- *(((__le32 *)(header + hdr_offset)) + 1) = 0;
- trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header);
-}
-
-static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
-{
- u16 dlen, totlen;
- u8 *dptr, num = 0;
- u16 sublen;
- struct sk_buff *pfirst, *pnext;
-
- int errcode;
- u8 doff, sfdoff;
-
- struct brcmf_sdio_hdrinfo rd_new;
-
- /* If packets, issue read(s) and send up packet chain */
- /* Return sequence numbers consumed? */
-
- brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
- bus->glomd, skb_peek(&bus->glom));
-
- /* If there's a descriptor, generate the packet chain */
- if (bus->glomd) {
- pfirst = pnext = NULL;
- dlen = (u16) (bus->glomd->len);
- dptr = bus->glomd->data;
- if (!dlen || (dlen & 1)) {
- brcmf_err("bad glomd len(%d), ignore descriptor\n",
- dlen);
- dlen = 0;
- }
-
- for (totlen = num = 0; dlen; num++) {
- /* Get (and move past) next length */
- sublen = get_unaligned_le16(dptr);
- dlen -= sizeof(u16);
- dptr += sizeof(u16);
- if ((sublen < SDPCM_HDRLEN) ||
- ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
- brcmf_err("descriptor len %d bad: %d\n",
- num, sublen);
- pnext = NULL;
- break;
- }
- if (sublen % bus->sgentry_align) {
- brcmf_err("sublen %d not multiple of %d\n",
- sublen, bus->sgentry_align);
- }
- totlen += sublen;
-
- /* For last frame, adjust read len so total
- is a block multiple */
- if (!dlen) {
- sublen +=
- (roundup(totlen, bus->blocksize) - totlen);
- totlen = roundup(totlen, bus->blocksize);
- }
-
- /* Allocate/chain packet for next subframe */
- pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align);
- if (pnext == NULL) {
- brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
- num, sublen);
- break;
- }
- skb_queue_tail(&bus->glom, pnext);
-
- /* Adhere to start alignment requirements */
- pkt_align(pnext, sublen, bus->sgentry_align);
- }
-
- /* If all allocations succeeded, save packet chain
- in bus structure */
- if (pnext) {
- brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
- totlen, num);
- if (BRCMF_GLOM_ON() && bus->cur_read.len &&
- totlen != bus->cur_read.len) {
- brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
- bus->cur_read.len, totlen, rxseq);
- }
- pfirst = pnext = NULL;
- } else {
- brcmf_sdio_free_glom(bus);
- num = 0;
- }
-
- /* Done with descriptor packet */
- brcmu_pkt_buf_free_skb(bus->glomd);
- bus->glomd = NULL;
- bus->cur_read.len = 0;
- }
-
- /* Ok -- either we just generated a packet chain,
- or had one from before */
- if (!skb_queue_empty(&bus->glom)) {
- if (BRCMF_GLOM_ON()) {
- brcmf_dbg(GLOM, "try superframe read, packet chain:\n");
- skb_queue_walk(&bus->glom, pnext) {
- brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n",
- pnext, (u8 *) (pnext->data),
- pnext->len, pnext->len);
- }
- }
-
- pfirst = skb_peek(&bus->glom);
- dlen = (u16) brcmf_sdio_glom_len(bus);
-
- /* Do an SDIO read for the superframe. Configurable iovar to
- * read directly into the chained packet, or allocate a large
- * packet and and copy into the chain.
- */
- sdio_claim_host(bus->sdiodev->func[1]);
- errcode = brcmf_sdiod_recv_chain(bus->sdiodev,
- &bus->glom, dlen);
- sdio_release_host(bus->sdiodev->func[1]);
- bus->sdcnt.f2rxdata++;
-
- /* On failure, kill the superframe, allow a couple retries */
- if (errcode < 0) {
- brcmf_err("glom read of %d bytes failed: %d\n",
- dlen, errcode);
-
- sdio_claim_host(bus->sdiodev->func[1]);
- if (bus->glomerr++ < 3) {
- brcmf_sdio_rxfail(bus, true, true);
- } else {
- bus->glomerr = 0;
- brcmf_sdio_rxfail(bus, true, false);
- bus->sdcnt.rxglomfail++;
- brcmf_sdio_free_glom(bus);
- }
- sdio_release_host(bus->sdiodev->func[1]);
- return 0;
- }
-
- brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
- pfirst->data, min_t(int, pfirst->len, 48),
- "SUPERFRAME:\n");
-
- rd_new.seq_num = rxseq;
- rd_new.len = dlen;
- sdio_claim_host(bus->sdiodev->func[1]);
- errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
- BRCMF_SDIO_FT_SUPER);
- sdio_release_host(bus->sdiodev->func[1]);
- bus->cur_read.len = rd_new.len_nxtfrm << 4;
-
- /* Remove superframe header, remember offset */
- skb_pull(pfirst, rd_new.dat_offset);
- sfdoff = rd_new.dat_offset;
- num = 0;
-
- /* Validate all the subframe headers */
- skb_queue_walk(&bus->glom, pnext) {
- /* leave when invalid subframe is found */
- if (errcode)
- break;
-
- rd_new.len = pnext->len;
- rd_new.seq_num = rxseq++;
- sdio_claim_host(bus->sdiodev->func[1]);
- errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
- BRCMF_SDIO_FT_SUB);
- sdio_release_host(bus->sdiodev->func[1]);
- brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
- pnext->data, 32, "subframe:\n");
-
- num++;
- }
-
- if (errcode) {
- /* Terminate frame on error, request
- a couple retries */
- sdio_claim_host(bus->sdiodev->func[1]);
- if (bus->glomerr++ < 3) {
- /* Restore superframe header space */
- skb_push(pfirst, sfdoff);
- brcmf_sdio_rxfail(bus, true, true);
- } else {
- bus->glomerr = 0;
- brcmf_sdio_rxfail(bus, true, false);
- bus->sdcnt.rxglomfail++;
- brcmf_sdio_free_glom(bus);
- }
- sdio_release_host(bus->sdiodev->func[1]);
- bus->cur_read.len = 0;
- return 0;
- }
-
- /* Basic SD framing looks ok - process each packet (header) */
-
- skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
- dptr = (u8 *) (pfirst->data);
- sublen = get_unaligned_le16(dptr);
- doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);
-
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
- dptr, pfirst->len,
- "Rx Subframe Data:\n");
-
- __skb_trim(pfirst, sublen);
- skb_pull(pfirst, doff);
-
- if (pfirst->len == 0) {
- skb_unlink(pfirst, &bus->glom);
- brcmu_pkt_buf_free_skb(pfirst);
- continue;
- }
-
- brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
- pfirst->data,
- min_t(int, pfirst->len, 32),
- "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n",
- bus->glom.qlen, pfirst, pfirst->data,
- pfirst->len, pfirst->next,
- pfirst->prev);
- skb_unlink(pfirst, &bus->glom);
- brcmf_rx_frame(bus->sdiodev->dev, pfirst);
- bus->sdcnt.rxglompkts++;
- }
-
- bus->sdcnt.rxglomframes++;
- }
- return num;
-}
-
-static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
- bool *pending)
-{
- DECLARE_WAITQUEUE(wait, current);
- int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT);
-
- /* Wait until control frame is available */
- add_wait_queue(&bus->dcmd_resp_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- while (!(*condition) && (!signal_pending(current) && timeout))
- timeout = schedule_timeout(timeout);
-
- if (signal_pending(current))
- *pending = true;
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&bus->dcmd_resp_wait, &wait);
-
- return timeout;
-}
-
-static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)
-{
- if (waitqueue_active(&bus->dcmd_resp_wait))
- wake_up_interruptible(&bus->dcmd_resp_wait);
-
- return 0;
-}
-static void
-brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
-{
- uint rdlen, pad;
- u8 *buf = NULL, *rbuf;
- int sdret;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (bus->rxblen)
- buf = vzalloc(bus->rxblen);
- if (!buf)
- goto done;
-
- rbuf = bus->rxbuf;
- pad = ((unsigned long)rbuf % bus->head_align);
- if (pad)
- rbuf += (bus->head_align - pad);
-
- /* Copy the already-read portion over */
- memcpy(buf, hdr, BRCMF_FIRSTREAD);
- if (len <= BRCMF_FIRSTREAD)
- goto gotpkt;
-
- /* Raise rdlen to next SDIO block to avoid tail command */
- rdlen = len - BRCMF_FIRSTREAD;
- if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
- pad = bus->blocksize - (rdlen % bus->blocksize);
- if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
- ((len + pad) < bus->sdiodev->bus_if->maxctl))
- rdlen += pad;
- } else if (rdlen % bus->head_align) {
- rdlen += bus->head_align - (rdlen % bus->head_align);
- }
-
- /* Drop if the read is too big or it exceeds our maximum */
- if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {
- brcmf_err("%d-byte control read exceeds %d-byte buffer\n",
- rdlen, bus->sdiodev->bus_if->maxctl);
- brcmf_sdio_rxfail(bus, false, false);
- goto done;
- }
-
- if ((len - doff) > bus->sdiodev->bus_if->maxctl) {
- brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
- len, len - doff, bus->sdiodev->bus_if->maxctl);
- bus->sdcnt.rx_toolong++;
- brcmf_sdio_rxfail(bus, false, false);
- goto done;
- }
-
- /* Read remain of frame body */
- sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen);
- bus->sdcnt.f2rxdata++;
-
- /* Control frame failures need retransmission */
- if (sdret < 0) {
- brcmf_err("read %d control bytes failed: %d\n",
- rdlen, sdret);
- bus->sdcnt.rxc_errors++;
- brcmf_sdio_rxfail(bus, true, true);
- goto done;
- } else
- memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen);
-
-gotpkt:
-
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
- buf, len, "RxCtrl:\n");
-
- /* Point to valid data and indicate its length */
- spin_lock_bh(&bus->rxctl_lock);
- if (bus->rxctl) {
- brcmf_err("last control frame is being processed.\n");
- spin_unlock_bh(&bus->rxctl_lock);
- vfree(buf);
- goto done;
- }
- bus->rxctl = buf + doff;
- bus->rxctl_orig = buf;
- bus->rxlen = len - doff;
- spin_unlock_bh(&bus->rxctl_lock);
-
-done:
- /* Awake any waiters */
- brcmf_sdio_dcmd_resp_wake(bus);
-}
-
-/* Pad read to blocksize for efficiency */
-static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
-{
- if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
- *pad = bus->blocksize - (*rdlen % bus->blocksize);
- if (*pad <= bus->roundup && *pad < bus->blocksize &&
- *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)
- *rdlen += *pad;
- } else if (*rdlen % bus->head_align) {
- *rdlen += bus->head_align - (*rdlen % bus->head_align);
- }
-}
-
-static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
-{
- struct sk_buff *pkt; /* Packet for event or data frames */
- u16 pad; /* Number of pad bytes to read */
- uint rxleft = 0; /* Remaining number of frames allowed */
- int ret; /* Return code from calls */
- uint rxcount = 0; /* Total frames read */
- struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
- u8 head_read = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* Not finished unless we encounter no more frames indication */
- bus->rxpending = true;
-
- for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
- !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA;
- rd->seq_num++, rxleft--) {
-
- /* Handle glomming separately */
- if (bus->glomd || !skb_queue_empty(&bus->glom)) {
- u8 cnt;
- brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
- bus->glomd, skb_peek(&bus->glom));
- cnt = brcmf_sdio_rxglom(bus, rd->seq_num);
- brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
- rd->seq_num += cnt - 1;
- rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
- continue;
- }
-
- rd->len_left = rd->len;
- /* read header first for unknow frame length */
- sdio_claim_host(bus->sdiodev->func[1]);
- if (!rd->len) {
- ret = brcmf_sdiod_recv_buf(bus->sdiodev,
- bus->rxhdr, BRCMF_FIRSTREAD);
- bus->sdcnt.f2rxhdrs++;
- if (ret < 0) {
- brcmf_err("RXHEADER FAILED: %d\n",
- ret);
- bus->sdcnt.rx_hdrfail++;
- brcmf_sdio_rxfail(bus, true, true);
- sdio_release_host(bus->sdiodev->func[1]);
- continue;
- }
-
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
- bus->rxhdr, SDPCM_HDRLEN,
- "RxHdr:\n");
-
- if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
- BRCMF_SDIO_FT_NORMAL)) {
- sdio_release_host(bus->sdiodev->func[1]);
- if (!bus->rxpending)
- break;
- else
- continue;
- }
-
- if (rd->channel == SDPCM_CONTROL_CHANNEL) {
- brcmf_sdio_read_control(bus, bus->rxhdr,
- rd->len,
- rd->dat_offset);
- /* prepare the descriptor for the next read */
- rd->len = rd->len_nxtfrm << 4;
- rd->len_nxtfrm = 0;
- /* treat all packet as event if we don't know */
- rd->channel = SDPCM_EVENT_CHANNEL;
- sdio_release_host(bus->sdiodev->func[1]);
- continue;
- }
- rd->len_left = rd->len > BRCMF_FIRSTREAD ?
- rd->len - BRCMF_FIRSTREAD : 0;
- head_read = BRCMF_FIRSTREAD;
- }
-
- brcmf_sdio_pad(bus, &pad, &rd->len_left);
-
- pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read +
- bus->head_align);
- if (!pkt) {
- /* Give up on data, request rtx of events */
- brcmf_err("brcmu_pkt_buf_get_skb failed\n");
- brcmf_sdio_rxfail(bus, false,
- RETRYCHAN(rd->channel));
- sdio_release_host(bus->sdiodev->func[1]);
- continue;
- }
- skb_pull(pkt, head_read);
- pkt_align(pkt, rd->len_left, bus->head_align);
-
- ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt);
- bus->sdcnt.f2rxdata++;
- sdio_release_host(bus->sdiodev->func[1]);
-
- if (ret < 0) {
- brcmf_err("read %d bytes from channel %d failed: %d\n",
- rd->len, rd->channel, ret);
- brcmu_pkt_buf_free_skb(pkt);
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_rxfail(bus, true,
- RETRYCHAN(rd->channel));
- sdio_release_host(bus->sdiodev->func[1]);
- continue;
- }
-
- if (head_read) {
- skb_push(pkt, head_read);
- memcpy(pkt->data, bus->rxhdr, head_read);
- head_read = 0;
- } else {
- memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
- rd_new.seq_num = rd->seq_num;
- sdio_claim_host(bus->sdiodev->func[1]);
- if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
- BRCMF_SDIO_FT_NORMAL)) {
- rd->len = 0;
- brcmu_pkt_buf_free_skb(pkt);
- }
- bus->sdcnt.rx_readahead_cnt++;
- if (rd->len != roundup(rd_new.len, 16)) {
- brcmf_err("frame length mismatch:read %d, should be %d\n",
- rd->len,
- roundup(rd_new.len, 16) >> 4);
- rd->len = 0;
- brcmf_sdio_rxfail(bus, true, true);
- sdio_release_host(bus->sdiodev->func[1]);
- brcmu_pkt_buf_free_skb(pkt);
- continue;
- }
- sdio_release_host(bus->sdiodev->func[1]);
- rd->len_nxtfrm = rd_new.len_nxtfrm;
- rd->channel = rd_new.channel;
- rd->dat_offset = rd_new.dat_offset;
-
- brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
- BRCMF_DATA_ON()) &&
- BRCMF_HDRS_ON(),
- bus->rxhdr, SDPCM_HDRLEN,
- "RxHdr:\n");
-
- if (rd_new.channel == SDPCM_CONTROL_CHANNEL) {
- brcmf_err("readahead on control packet %d?\n",
- rd_new.seq_num);
- /* Force retry w/normal header read */
- rd->len = 0;
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_rxfail(bus, false, true);
- sdio_release_host(bus->sdiodev->func[1]);
- brcmu_pkt_buf_free_skb(pkt);
- continue;
- }
- }
-
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
- pkt->data, rd->len, "Rx Data:\n");
-
- /* Save superframe descriptor and allocate packet frame */
- if (rd->channel == SDPCM_GLOM_CHANNEL) {
- if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
- brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
- rd->len);
- brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
- pkt->data, rd->len,
- "Glom Data:\n");
- __skb_trim(pkt, rd->len);
- skb_pull(pkt, SDPCM_HDRLEN);
- bus->glomd = pkt;
- } else {
- brcmf_err("%s: glom superframe w/o "
- "descriptor!\n", __func__);
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_rxfail(bus, false, false);
- sdio_release_host(bus->sdiodev->func[1]);
- }
- /* prepare the descriptor for the next read */
- rd->len = rd->len_nxtfrm << 4;
- rd->len_nxtfrm = 0;
- /* treat all packet as event if we don't know */
- rd->channel = SDPCM_EVENT_CHANNEL;
- continue;
- }
-
- /* Fill in packet len and prio, deliver upward */
- __skb_trim(pkt, rd->len);
- skb_pull(pkt, rd->dat_offset);
-
- /* prepare the descriptor for the next read */
- rd->len = rd->len_nxtfrm << 4;
- rd->len_nxtfrm = 0;
- /* treat all packet as event if we don't know */
- rd->channel = SDPCM_EVENT_CHANNEL;
-
- if (pkt->len == 0) {
- brcmu_pkt_buf_free_skb(pkt);
- continue;
- }
-
- brcmf_rx_frame(bus->sdiodev->dev, pkt);
- }
-
- rxcount = maxframes - rxleft;
- /* Message if we hit the limit */
- if (!rxleft)
- brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes);
- else
- brcmf_dbg(DATA, "processed %d frames\n", rxcount);
- /* Back off rxseq if awaiting rtx, update rx_seq */
- if (bus->rxskip)
- rd->seq_num--;
- bus->rx_seq = rd->seq_num;
-
- return rxcount;
-}
-
-static void
-brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
-{
- if (waitqueue_active(&bus->ctrl_wait))
- wake_up_interruptible(&bus->ctrl_wait);
- return;
-}
-
-static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt)
-{
- u16 head_pad;
- u8 *dat_buf;
-
- dat_buf = (u8 *)(pkt->data);
-
- /* Check head padding */
- head_pad = ((unsigned long)dat_buf % bus->head_align);
- if (head_pad) {
- if (skb_headroom(pkt) < head_pad) {
- bus->sdiodev->bus_if->tx_realloc++;
- head_pad = 0;
- if (skb_cow(pkt, head_pad))
- return -ENOMEM;
- }
- skb_push(pkt, head_pad);
- dat_buf = (u8 *)(pkt->data);
- memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
- }
- return head_pad;
-}
-
-/**
- * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for
- * bus layer usage.
- */
-/* flag marking a dummy skb added for DMA alignment requirement */
-#define ALIGN_SKB_FLAG 0x8000
-/* bit mask of data length chopped from the previous packet */
-#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff
-
-static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus,
- struct sk_buff_head *pktq,
- struct sk_buff *pkt, u16 total_len)
-{
- struct brcmf_sdio_dev *sdiodev;
- struct sk_buff *pkt_pad;
- u16 tail_pad, tail_chop, chain_pad;
- unsigned int blksize;
- bool lastfrm;
- int ntail, ret;
-
- sdiodev = bus->sdiodev;
- blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize;
- /* sg entry alignment should be a divisor of block size */
- WARN_ON(blksize % bus->sgentry_align);
-
- /* Check tail padding */
- lastfrm = skb_queue_is_last(pktq, pkt);
- tail_pad = 0;
- tail_chop = pkt->len % bus->sgentry_align;
- if (tail_chop)
- tail_pad = bus->sgentry_align - tail_chop;
- chain_pad = (total_len + tail_pad) % blksize;
- if (lastfrm && chain_pad)
- tail_pad += blksize - chain_pad;
- if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) {
- pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop +
- bus->head_align);
- if (pkt_pad == NULL)
- return -ENOMEM;
- ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad);
- if (unlikely(ret < 0)) {
- kfree_skb(pkt_pad);
- return ret;
- }
- memcpy(pkt_pad->data,
- pkt->data + pkt->len - tail_chop,
- tail_chop);
- *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
- skb_trim(pkt, pkt->len - tail_chop);
- skb_trim(pkt_pad, tail_pad + tail_chop);
- __skb_queue_after(pktq, pkt, pkt_pad);
- } else {
- ntail = pkt->data_len + tail_pad -
- (pkt->end - pkt->tail);
- if (skb_cloned(pkt) || ntail > 0)
- if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC))
- return -ENOMEM;
- if (skb_linearize(pkt))
- return -ENOMEM;
- __skb_put(pkt, tail_pad);
- }
-
- return tail_pad;
-}
-
-/**
- * brcmf_sdio_txpkt_prep - packet preparation for transmit
- * @bus: brcmf_sdio structure pointer
- * @pktq: packet list pointer
- * @chan: virtual channel to transmit the packet
- *
- * Processes to be applied to the packet
- * - Align data buffer pointer
- * - Align data buffer length
- * - Prepare header
- * Return: negative value if there is error
- */
-static int
-brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
- uint chan)
-{
- u16 head_pad, total_len;
- struct sk_buff *pkt_next;
- u8 txseq;
- int ret;
- struct brcmf_sdio_hdrinfo hd_info = {0};
-
- txseq = bus->tx_seq;
- total_len = 0;
- skb_queue_walk(pktq, pkt_next) {
- /* alignment packet inserted in previous
- * loop cycle can be skipped as it is
- * already properly aligned and does not
- * need an sdpcm header.
- */
- if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG)
- continue;
-
- /* align packet data pointer */
- ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next);
- if (ret < 0)
- return ret;
- head_pad = (u16)ret;
- if (head_pad)
- memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad);
-
- total_len += pkt_next->len;
-
- hd_info.len = pkt_next->len;
- hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next);
- if (bus->txglom && pktq->qlen > 1) {
- ret = brcmf_sdio_txpkt_prep_sg(bus, pktq,
- pkt_next, total_len);
- if (ret < 0)
- return ret;
- hd_info.tail_pad = (u16)ret;
- total_len += (u16)ret;
- }
-
- hd_info.channel = chan;
- hd_info.dat_offset = head_pad + bus->tx_hdrlen;
- hd_info.seq_num = txseq++;
-
- /* Now fill the header */
- brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info);
-
- if (BRCMF_BYTES_ON() &&
- ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
- (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
- brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len,
- "Tx Frame:\n");
- else if (BRCMF_HDRS_ON())
- brcmf_dbg_hex_dump(true, pkt_next->data,
- head_pad + bus->tx_hdrlen,
- "Tx Header:\n");
- }
- /* Hardware length tag of the first packet should be total
- * length of the chain (including padding)
- */
- if (bus->txglom)
- brcmf_sdio_update_hwhdr(pktq->next->data, total_len);
- return 0;
-}
-
-/**
- * brcmf_sdio_txpkt_postp - packet post processing for transmit
- * @bus: brcmf_sdio structure pointer
- * @pktq: packet list pointer
- *
- * Processes to be applied to the packet
- * - Remove head padding
- * - Remove tail padding
- */
-static void
-brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
-{
- u8 *hdr;
- u32 dat_offset;
- u16 tail_pad;
- u16 dummy_flags, chop_len;
- struct sk_buff *pkt_next, *tmp, *pkt_prev;
-
- skb_queue_walk_safe(pktq, pkt_next, tmp) {
- dummy_flags = *(u16 *)(pkt_next->cb);
- if (dummy_flags & ALIGN_SKB_FLAG) {
- chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK;
- if (chop_len) {
- pkt_prev = pkt_next->prev;
- skb_put(pkt_prev, chop_len);
- }
- __skb_unlink(pkt_next, pktq);
- brcmu_pkt_buf_free_skb(pkt_next);
- } else {
- hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN;
- dat_offset = le32_to_cpu(*(__le32 *)hdr);
- dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
- SDPCM_DOFFSET_SHIFT;
- skb_pull(pkt_next, dat_offset);
- if (bus->txglom) {
- tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2));
- skb_trim(pkt_next, pkt_next->len - tail_pad);
- }
- }
- }
-}
-
-/* Writes a HW/SW header into the packet and sends it. */
-/* Assumes: (a) header space already there, (b) caller holds lock */
-static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
- uint chan)
-{
- int ret;
- struct sk_buff *pkt_next, *tmp;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- ret = brcmf_sdio_txpkt_prep(bus, pktq, chan);
- if (ret)
- goto done;
-
- sdio_claim_host(bus->sdiodev->func[1]);
- ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq);
- bus->sdcnt.f2txdata++;
-
- if (ret < 0)
- brcmf_sdio_txfail(bus);
-
- sdio_release_host(bus->sdiodev->func[1]);
-
-done:
- brcmf_sdio_txpkt_postp(bus, pktq);
- if (ret == 0)
- bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP;
- skb_queue_walk_safe(pktq, pkt_next, tmp) {
- __skb_unlink(pkt_next, pktq);
- brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0);
- }
- return ret;
-}
-
-static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
-{
- struct sk_buff *pkt;
- struct sk_buff_head pktq;
- u32 intstatus = 0;
- int ret = 0, prec_out, i;
- uint cnt = 0;
- u8 tx_prec_map, pkt_num;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- tx_prec_map = ~bus->flowcontrol;
-
- /* Send frames until the limit or some other event */
- for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
- pkt_num = 1;
- if (bus->txglom)
- pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
- bus->sdiodev->txglomsz);
- pkt_num = min_t(u32, pkt_num,
- brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol));
- __skb_queue_head_init(&pktq);
- spin_lock_bh(&bus->txq_lock);
- for (i = 0; i < pkt_num; i++) {
- pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map,
- &prec_out);
- if (pkt == NULL)
- break;
- __skb_queue_tail(&pktq, pkt);
- }
- spin_unlock_bh(&bus->txq_lock);
- if (i == 0)
- break;
-
- ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
-
- cnt += i;
-
- /* In poll mode, need to check for other events */
- if (!bus->intr) {
- /* Check device status, signal pending interrupt */
- sdio_claim_host(bus->sdiodev->func[1]);
- ret = r_sdreg32(bus, &intstatus,
- offsetof(struct sdpcmd_regs,
- intstatus));
- sdio_release_host(bus->sdiodev->func[1]);
- bus->sdcnt.f2txdata++;
- if (ret != 0)
- break;
- if (intstatus & bus->hostintmask)
- atomic_set(&bus->ipend, 1);
- }
- }
-
- /* Deflow-control stack if needed */
- if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) &&
- bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
- bus->txoff = false;
- brcmf_txflowblock(bus->sdiodev->dev, false);
- }
-
- return cnt;
-}
-
-static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len)
-{
- u8 doff;
- u16 pad;
- uint retries = 0;
- struct brcmf_sdio_hdrinfo hd_info = {0};
- int ret;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* Back the pointer to make room for bus header */
- frame -= bus->tx_hdrlen;
- len += bus->tx_hdrlen;
-
- /* Add alignment padding (optional for ctl frames) */
- doff = ((unsigned long)frame % bus->head_align);
- if (doff) {
- frame -= doff;
- len += doff;
- memset(frame + bus->tx_hdrlen, 0, doff);
- }
-
- /* Round send length to next SDIO block */
- pad = 0;
- if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
- pad = bus->blocksize - (len % bus->blocksize);
- if ((pad > bus->roundup) || (pad >= bus->blocksize))
- pad = 0;
- } else if (len % bus->head_align) {
- pad = bus->head_align - (len % bus->head_align);
- }
- len += pad;
-
- hd_info.len = len - pad;
- hd_info.channel = SDPCM_CONTROL_CHANNEL;
- hd_info.dat_offset = doff + bus->tx_hdrlen;
- hd_info.seq_num = bus->tx_seq;
- hd_info.lastfrm = true;
- hd_info.tail_pad = pad;
- brcmf_sdio_hdpack(bus, frame, &hd_info);
-
- if (bus->txglom)
- brcmf_sdio_update_hwhdr(frame, len);
-
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
- frame, len, "Tx Frame:\n");
- brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) &&
- BRCMF_HDRS_ON(),
- frame, min_t(u16, len, 16), "TxHdr:\n");
-
- do {
- ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len);
-
- if (ret < 0)
- brcmf_sdio_txfail(bus);
- else
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
- } while (ret < 0 && retries++ < TXRETRIES);
-
- return ret;
-}
-
-static void brcmf_sdio_bus_stop(struct device *dev)
-{
- u32 local_hostintmask;
- u8 saveclk;
- int err;
- 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;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (bus->watchdog_tsk) {
- send_sig(SIGTERM, bus->watchdog_tsk, 1);
- kthread_stop(bus->watchdog_tsk);
- bus->watchdog_tsk = NULL;
- }
-
- if (sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
- sdio_claim_host(sdiodev->func[1]);
-
- /* Enable clock for device interrupts */
- brcmf_sdio_bus_sleep(bus, false, false);
-
- /* Disable and clear interrupts at the chip level also */
- w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask));
- local_hostintmask = bus->hostintmask;
- bus->hostintmask = 0;
-
- /* Force backplane clocks to assure F2 interrupt propagates */
- saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- &err);
- if (!err)
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- (saveclk | SBSDIO_FORCE_HT), &err);
- if (err)
- brcmf_err("Failed to force clock for F2: err %d\n",
- err);
-
- /* Turn off the bus (F2), free any pending packets */
- brcmf_dbg(INTR, "disable SDIO interrupts\n");
- sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
-
- /* Clear any pending interrupts now that F2 is disabled */
- w_sdreg32(bus, local_hostintmask,
- offsetof(struct sdpcmd_regs, intstatus));
-
- sdio_release_host(sdiodev->func[1]);
- }
- /* Clear the data packet queues */
- brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
-
- /* Clear any held glomming stuff */
- brcmu_pkt_buf_free_skb(bus->glomd);
- brcmf_sdio_free_glom(bus);
-
- /* Clear rx control and wake any waiters */
- spin_lock_bh(&bus->rxctl_lock);
- bus->rxlen = 0;
- spin_unlock_bh(&bus->rxctl_lock);
- brcmf_sdio_dcmd_resp_wake(bus);
-
- /* Reset some F2 state stuff */
- bus->rxskip = false;
- bus->tx_seq = bus->rx_seq = 0;
-}
-
-static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
-{
- unsigned long flags;
-
- if (bus->sdiodev->oob_irq_requested) {
- spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
- if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
- enable_irq(bus->sdiodev->pdata->oob_irq_nr);
- bus->sdiodev->irq_en = true;
- }
- spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags);
- }
-}
-
-static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
-{
- struct brcmf_core *buscore;
- u32 addr;
- unsigned long val;
- int ret;
-
- buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
- addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus);
-
- val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
- bus->sdcnt.f1regdata++;
- if (ret != 0)
- return ret;
-
- val &= bus->hostintmask;
- atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));
-
- /* Clear interrupts */
- if (val) {
- brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
- bus->sdcnt.f1regdata++;
- atomic_or(val, &bus->intstatus);
- }
-
- return ret;
-}
-
-static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
-{
- u32 newstatus = 0;
- unsigned long intstatus;
- uint txlimit = bus->txbound; /* Tx frames to send before resched */
- uint framecnt; /* Temporary counter of tx/rx frames */
- int err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- sdio_claim_host(bus->sdiodev->func[1]);
-
- /* If waiting for HTAVAIL, check status */
- if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) {
- u8 clkctl, devctl = 0;
-
-#ifdef DEBUG
- /* Check for inconsistent device control */
- devctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_DEVICE_CTL, &err);
-#endif /* DEBUG */
-
- /* Read CSR, if clock on switch to AVAIL, else ignore */
- clkctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR, &err);
-
- brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
- devctl, clkctl);
-
- if (SBSDIO_HTAV(clkctl)) {
- devctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_DEVICE_CTL, &err);
- devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
- devctl, &err);
- bus->clkstate = CLK_AVAIL;
- }
- }
-
- /* Make sure backplane clock is on */
- brcmf_sdio_bus_sleep(bus, false, true);
-
- /* Pending interrupt indicates new device status */
- if (atomic_read(&bus->ipend) > 0) {
- atomic_set(&bus->ipend, 0);
- err = brcmf_sdio_intr_rstatus(bus);
- }
-
- /* Start with leftover status bits */
- intstatus = atomic_xchg(&bus->intstatus, 0);
-
- /* Handle flow-control change: read new state in case our ack
- * crossed another change interrupt. If change still set, assume
- * FC ON for safety, let next loop through do the debounce.
- */
- if (intstatus & I_HMB_FC_CHANGE) {
- intstatus &= ~I_HMB_FC_CHANGE;
- err = w_sdreg32(bus, I_HMB_FC_CHANGE,
- offsetof(struct sdpcmd_regs, intstatus));
-
- err = r_sdreg32(bus, &newstatus,
- offsetof(struct sdpcmd_regs, intstatus));
- bus->sdcnt.f1regdata += 2;
- atomic_set(&bus->fcstate,
- !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)));
- intstatus |= (newstatus & bus->hostintmask);
- }
-
- /* Handle host mailbox indication */
- if (intstatus & I_HMB_HOST_INT) {
- intstatus &= ~I_HMB_HOST_INT;
- intstatus |= brcmf_sdio_hostmail(bus);
- }
-
- sdio_release_host(bus->sdiodev->func[1]);
-
- /* Generally don't ask for these, can get CRC errors... */
- if (intstatus & I_WR_OOSYNC) {
- brcmf_err("Dongle reports WR_OOSYNC\n");
- intstatus &= ~I_WR_OOSYNC;
- }
-
- if (intstatus & I_RD_OOSYNC) {
- brcmf_err("Dongle reports RD_OOSYNC\n");
- intstatus &= ~I_RD_OOSYNC;
- }
-
- if (intstatus & I_SBINT) {
- brcmf_err("Dongle reports SBINT\n");
- intstatus &= ~I_SBINT;
- }
-
- /* Would be active due to wake-wlan in gSPI */
- if (intstatus & I_CHIPACTIVE) {
- brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n");
- intstatus &= ~I_CHIPACTIVE;
- }
-
- /* Ignore frame indications if rxskip is set */
- if (bus->rxskip)
- intstatus &= ~I_HMB_FRAME_IND;
-
- /* On frame indication, read available frames */
- if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) {
- brcmf_sdio_readframes(bus, bus->rxbound);
- if (!bus->rxpending)
- intstatus &= ~I_HMB_FRAME_IND;
- }
-
- /* Keep still-pending events for next scheduling */
- if (intstatus)
- atomic_or(intstatus, &bus->intstatus);
-
- brcmf_sdio_clrintr(bus);
-
- if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
- data_ok(bus)) {
- sdio_claim_host(bus->sdiodev->func[1]);
- if (bus->ctrl_frame_stat) {
- err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
- bus->ctrl_frame_len);
- bus->ctrl_frame_err = err;
- wmb();
- bus->ctrl_frame_stat = false;
- }
- sdio_release_host(bus->sdiodev->func[1]);
- brcmf_sdio_wait_event_wakeup(bus);
- }
- /* Send queued frames (limit 1 if rx may still be pending) */
- if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
- brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit &&
- data_ok(bus)) {
- framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
- txlimit;
- brcmf_sdio_sendfromq(bus, framecnt);
- }
-
- if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) {
- brcmf_err("failed backplane access over SDIO, halting operation\n");
- atomic_set(&bus->intstatus, 0);
- if (bus->ctrl_frame_stat) {
- sdio_claim_host(bus->sdiodev->func[1]);
- if (bus->ctrl_frame_stat) {
- bus->ctrl_frame_err = -ENODEV;
- wmb();
- bus->ctrl_frame_stat = false;
- brcmf_sdio_wait_event_wakeup(bus);
- }
- sdio_release_host(bus->sdiodev->func[1]);
- }
- } else if (atomic_read(&bus->intstatus) ||
- atomic_read(&bus->ipend) > 0 ||
- (!atomic_read(&bus->fcstate) &&
- brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
- data_ok(bus))) {
- bus->dpc_triggered = true;
- }
-}
-
-static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio *bus = sdiodev->bus;
-
- return &bus->txq;
-}
-
-static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec)
-{
- struct sk_buff *p;
- int eprec = -1; /* precedence to evict from */
-
- /* Fast case, precedence queue is not full and we are also not
- * exceeding total queue length
- */
- if (!pktq_pfull(q, prec) && !pktq_full(q)) {
- brcmu_pktq_penq(q, prec, pkt);
- return true;
- }
-
- /* Determine precedence from which to evict packet, if any */
- if (pktq_pfull(q, prec)) {
- eprec = prec;
- } else if (pktq_full(q)) {
- p = brcmu_pktq_peek_tail(q, &eprec);
- if (eprec > prec)
- return false;
- }
-
- /* Evict if needed */
- if (eprec >= 0) {
- /* Detect queueing to unconfigured precedence */
- if (eprec == prec)
- return false; /* refuse newer (incoming) packet */
- /* Evict packet according to discard policy */
- p = brcmu_pktq_pdeq_tail(q, eprec);
- if (p == NULL)
- brcmf_err("brcmu_pktq_pdeq_tail() failed\n");
- brcmu_pkt_buf_free_skb(p);
- }
-
- /* Enqueue */
- p = brcmu_pktq_penq(q, prec, pkt);
- if (p == NULL)
- brcmf_err("brcmu_pktq_penq() failed\n");
-
- return p != NULL;
-}
-
-static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
-{
- int ret = -EBADE;
- uint prec;
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio *bus = sdiodev->bus;
-
- brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
- if (sdiodev->state != BRCMF_SDIOD_DATA)
- return -EIO;
-
- /* Add space for the header */
- skb_push(pkt, bus->tx_hdrlen);
- /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
-
- prec = prio2prec((pkt->priority & PRIOMASK));
-
- /* Check for existing queue, current flow-control,
- pending event, or pending clock */
- brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
- bus->sdcnt.fcqueued++;
-
- /* Priority based enq */
- spin_lock_bh(&bus->txq_lock);
- /* reset bus_flags in packet cb */
- *(u16 *)(pkt->cb) = 0;
- if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) {
- skb_pull(pkt, bus->tx_hdrlen);
- brcmf_err("out of bus->txq !!!\n");
- ret = -ENOSR;
- } else {
- ret = 0;
- }
-
- if (pktq_len(&bus->txq) >= TXHI) {
- bus->txoff = true;
- brcmf_txflowblock(dev, true);
- }
- spin_unlock_bh(&bus->txq_lock);
-
-#ifdef DEBUG
- if (pktq_plen(&bus->txq, prec) > qcount[prec])
- qcount[prec] = pktq_plen(&bus->txq, prec);
-#endif
-
- brcmf_sdio_trigger_dpc(bus);
- return ret;
-}
-
-#ifdef DEBUG
-#define CONSOLE_LINE_MAX 192
-
-static int brcmf_sdio_readconsole(struct brcmf_sdio *bus)
-{
- struct brcmf_console *c = &bus->console;
- u8 line[CONSOLE_LINE_MAX], ch;
- u32 n, idx, addr;
- int rv;
-
- /* Don't do anything until FWREADY updates console address */
- if (bus->console_addr == 0)
- return 0;
-
- /* Read console log struct */
- addr = bus->console_addr + offsetof(struct rte_console, log_le);
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le,
- sizeof(c->log_le));
- if (rv < 0)
- return rv;
-
- /* Allocate console buffer (one time only) */
- if (c->buf == NULL) {
- c->bufsize = le32_to_cpu(c->log_le.buf_size);
- c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
- if (c->buf == NULL)
- return -ENOMEM;
- }
-
- idx = le32_to_cpu(c->log_le.idx);
-
- /* Protect against corrupt value */
- if (idx > c->bufsize)
- return -EBADE;
-
- /* Skip reading the console buffer if the index pointer
- has not moved */
- if (idx == c->last)
- return 0;
-
- /* Read the console buffer */
- addr = le32_to_cpu(c->log_le.buf);
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);
- if (rv < 0)
- return rv;
-
- while (c->last != idx) {
- for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
- if (c->last == idx) {
- /* This would output a partial line.
- * Instead, back up
- * the buffer pointer and output this
- * line next time around.
- */
- if (c->last >= n)
- c->last -= n;
- else
- c->last = c->bufsize - n;
- goto break2;
- }
- ch = c->buf[c->last];
- c->last = (c->last + 1) % c->bufsize;
- if (ch == '\n')
- break;
- line[n] = ch;
- }
-
- if (n > 0) {
- if (line[n - 1] == '\r')
- n--;
- line[n] = 0;
- pr_debug("CONSOLE: %s\n", line);
- }
- }
-break2:
-
- return 0;
-}
-#endif /* DEBUG */
-
-static int
-brcmf_sdio_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;
- int ret;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (sdiodev->state != BRCMF_SDIOD_DATA)
- return -EIO;
-
- /* Send from dpc */
- bus->ctrl_frame_buf = msg;
- bus->ctrl_frame_len = msglen;
- wmb();
- bus->ctrl_frame_stat = true;
-
- brcmf_sdio_trigger_dpc(bus);
- wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
- msecs_to_jiffies(CTL_DONE_TIMEOUT));
- ret = 0;
- if (bus->ctrl_frame_stat) {
- sdio_claim_host(bus->sdiodev->func[1]);
- if (bus->ctrl_frame_stat) {
- brcmf_dbg(SDIO, "ctrl_frame timeout\n");
- bus->ctrl_frame_stat = false;
- ret = -ETIMEDOUT;
- }
- sdio_release_host(bus->sdiodev->func[1]);
- }
- if (!ret) {
- brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n",
- bus->ctrl_frame_err);
- rmb();
- ret = bus->ctrl_frame_err;
- }
-
- if (ret)
- bus->sdcnt.tx_ctlerrs++;
- else
- bus->sdcnt.tx_ctlpkts++;
-
- return ret;
-}
-
-#ifdef DEBUG
-static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus,
- struct sdpcm_shared *sh)
-{
- u32 addr, console_ptr, console_size, console_index;
- char *conbuf = NULL;
- __le32 sh_val;
- int rv;
-
- /* obtain console information from device memory */
- addr = sh->console_addr + offsetof(struct rte_console, log_le);
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
- (u8 *)&sh_val, sizeof(u32));
- if (rv < 0)
- return rv;
- console_ptr = le32_to_cpu(sh_val);
-
- addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
- (u8 *)&sh_val, sizeof(u32));
- if (rv < 0)
- return rv;
- console_size = le32_to_cpu(sh_val);
-
- addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
- (u8 *)&sh_val, sizeof(u32));
- if (rv < 0)
- return rv;
- console_index = le32_to_cpu(sh_val);
-
- /* allocate buffer for console data */
- if (console_size <= CONSOLE_BUFFER_MAX)
- conbuf = vzalloc(console_size+1);
-
- if (!conbuf)
- return -ENOMEM;
-
- /* obtain the console data from device */
- conbuf[console_size] = '\0';
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf,
- console_size);
- if (rv < 0)
- goto done;
-
- rv = seq_write(seq, conbuf + console_index,
- console_size - console_index);
- if (rv < 0)
- goto done;
-
- if (console_index > 0)
- rv = seq_write(seq, conbuf, console_index - 1);
-
-done:
- vfree(conbuf);
- return rv;
-}
-
-static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus,
- struct sdpcm_shared *sh)
-{
- int error;
- struct brcmf_trap_info tr;
-
- if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
- brcmf_dbg(INFO, "no trap in firmware\n");
- return 0;
- }
-
- error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr,
- sizeof(struct brcmf_trap_info));
- if (error < 0)
- return error;
-
- seq_printf(seq,
- "dongle trap info: type 0x%x @ epc 0x%08x\n"
- " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
- " lr 0x%08x pc 0x%08x offset 0x%x\n"
- " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n"
- " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n",
- le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
- le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
- le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
- le32_to_cpu(tr.pc), sh->trap_addr,
- le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
- le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
- le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
- le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
-
- return 0;
-}
-
-static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus,
- struct sdpcm_shared *sh)
-{
- int error = 0;
- char file[80] = "?";
- char expr[80] = "<???>";
-
- if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
- brcmf_dbg(INFO, "firmware not built with -assert\n");
- return 0;
- } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) {
- brcmf_dbg(INFO, "no assert in dongle\n");
- return 0;
- }
-
- sdio_claim_host(bus->sdiodev->func[1]);
- if (sh->assert_file_addr != 0) {
- error = brcmf_sdiod_ramrw(bus->sdiodev, false,
- sh->assert_file_addr, (u8 *)file, 80);
- if (error < 0)
- return error;
- }
- if (sh->assert_exp_addr != 0) {
- error = brcmf_sdiod_ramrw(bus->sdiodev, false,
- sh->assert_exp_addr, (u8 *)expr, 80);
- if (error < 0)
- return error;
- }
- sdio_release_host(bus->sdiodev->func[1]);
-
- seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n",
- file, sh->assert_line, expr);
- return 0;
-}
-
-static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
-{
- int error;
- struct sdpcm_shared sh;
-
- error = brcmf_sdio_readshared(bus, &sh);
-
- if (error < 0)
- return error;
-
- if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0)
- brcmf_dbg(INFO, "firmware not built with -assert\n");
- else if (sh.flags & SDPCM_SHARED_ASSERT)
- brcmf_err("assertion in dongle\n");
-
- if (sh.flags & SDPCM_SHARED_TRAP)
- brcmf_err("firmware trap in dongle\n");
-
- return 0;
-}
-
-static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus)
-{
- int error = 0;
- struct sdpcm_shared sh;
-
- error = brcmf_sdio_readshared(bus, &sh);
- if (error < 0)
- goto done;
-
- error = brcmf_sdio_assert_info(seq, bus, &sh);
- if (error < 0)
- goto done;
-
- error = brcmf_sdio_trap_info(seq, bus, &sh);
- if (error < 0)
- goto done;
-
- error = brcmf_sdio_dump_console(seq, bus, &sh);
-
-done:
- return error;
-}
-
-static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
- struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus;
-
- return brcmf_sdio_died_dump(seq, bus);
-}
-
-static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt;
-
- seq_printf(seq,
- "intrcount: %u\nlastintrs: %u\n"
- "pollcnt: %u\nregfails: %u\n"
- "tx_sderrs: %u\nfcqueued: %u\n"
- "rxrtx: %u\nrx_toolong: %u\n"
- "rxc_errors: %u\nrx_hdrfail: %u\n"
- "rx_badhdr: %u\nrx_badseq: %u\n"
- "fc_rcvd: %u\nfc_xoff: %u\n"
- "fc_xon: %u\nrxglomfail: %u\n"
- "rxglomframes: %u\nrxglompkts: %u\n"
- "f2rxhdrs: %u\nf2rxdata: %u\n"
- "f2txdata: %u\nf1regdata: %u\n"
- "tickcnt: %u\ntx_ctlerrs: %lu\n"
- "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n"
- "rx_ctlpkts: %lu\nrx_readahead: %lu\n",
- sdcnt->intrcount, sdcnt->lastintrs,
- sdcnt->pollcnt, sdcnt->regfails,
- sdcnt->tx_sderrs, sdcnt->fcqueued,
- sdcnt->rxrtx, sdcnt->rx_toolong,
- sdcnt->rxc_errors, sdcnt->rx_hdrfail,
- sdcnt->rx_badhdr, sdcnt->rx_badseq,
- sdcnt->fc_rcvd, sdcnt->fc_xoff,
- sdcnt->fc_xon, sdcnt->rxglomfail,
- sdcnt->rxglomframes, sdcnt->rxglompkts,
- sdcnt->f2rxhdrs, sdcnt->f2rxdata,
- sdcnt->f2txdata, sdcnt->f1regdata,
- sdcnt->tickcnt, sdcnt->tx_ctlerrs,
- sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
- sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
-
- return 0;
-}
-
-static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
-{
- struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr;
- struct dentry *dentry = brcmf_debugfs_get_devdir(drvr);
-
- if (IS_ERR_OR_NULL(dentry))
- return;
-
- bus->console_interval = BRCMF_CONSOLE;
-
- brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
- brcmf_debugfs_add_entry(drvr, "counters",
- brcmf_debugfs_sdio_count_read);
- debugfs_create_u32("console_interval", 0644, dentry,
- &bus->console_interval);
-}
-#else
-static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
-{
- return 0;
-}
-
-static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
-{
-}
-#endif /* DEBUG */
-
-static int
-brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
-{
- int timeleft;
- uint rxlen = 0;
- bool pending;
- u8 *buf;
- 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;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (sdiodev->state != BRCMF_SDIOD_DATA)
- return -EIO;
-
- /* Wait until control frame is available */
- timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);
-
- spin_lock_bh(&bus->rxctl_lock);
- rxlen = bus->rxlen;
- memcpy(msg, bus->rxctl, min(msglen, rxlen));
- bus->rxctl = NULL;
- buf = bus->rxctl_orig;
- bus->rxctl_orig = NULL;
- bus->rxlen = 0;
- spin_unlock_bh(&bus->rxctl_lock);
- vfree(buf);
-
- if (rxlen) {
- brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n",
- rxlen, msglen);
- } else if (timeleft == 0) {
- brcmf_err("resumed on timeout\n");
- brcmf_sdio_checkdied(bus);
- } else if (pending) {
- brcmf_dbg(CTL, "cancelled\n");
- return -ERESTARTSYS;
- } else {
- brcmf_dbg(CTL, "resumed for unknown reason?\n");
- brcmf_sdio_checkdied(bus);
- }
-
- if (rxlen)
- bus->sdcnt.rx_ctlpkts++;
- else
- bus->sdcnt.rx_ctlerrs++;
-
- return rxlen ? (int)rxlen : -ETIMEDOUT;
-}
-
-#ifdef DEBUG
-static bool
-brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
- u8 *ram_data, uint ram_sz)
-{
- char *ram_cmp;
- int err;
- bool ret = true;
- int address;
- int offset;
- int len;
-
- /* read back and verify */
- brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr,
- ram_sz);
- ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL);
- /* do not proceed while no memory but */
- if (!ram_cmp)
- return true;
-
- address = ram_addr;
- offset = 0;
- while (offset < ram_sz) {
- len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK :
- ram_sz - offset;
- err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len);
- if (err) {
- brcmf_err("error %d on reading %d membytes at 0x%08x\n",
- err, len, address);
- ret = false;
- break;
- } else if (memcmp(ram_cmp, &ram_data[offset], len)) {
- brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n",
- offset, len);
- ret = false;
- break;
- }
- offset += len;
- address += len;
- }
-
- kfree(ram_cmp);
-
- return ret;
-}
-#else /* DEBUG */
-static bool
-brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
- u8 *ram_data, uint ram_sz)
-{
- return true;
-}
-#endif /* DEBUG */
-
-static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
- const struct firmware *fw)
-{
- int err;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase,
- (u8 *)fw->data, fw->size);
- if (err)
- brcmf_err("error %d on writing %d membytes at 0x%08x\n",
- err, (int)fw->size, bus->ci->rambase);
- else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
- (u8 *)fw->data, fw->size))
- err = -EIO;
-
- return err;
-}
-
-static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
- void *vars, u32 varsz)
-{
- int address;
- int err;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- address = bus->ci->ramsize - varsz + bus->ci->rambase;
- err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
- if (err)
- brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
- err, varsz, address);
- else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
- err = -EIO;
-
- return err;
-}
-
-static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
- const struct firmware *fw,
- void *nvram, u32 nvlen)
-{
- int bcmerror = -EFAULT;
- u32 rstvec;
-
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
-
- rstvec = get_unaligned_le32(fw->data);
- brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
-
- bcmerror = brcmf_sdio_download_code_file(bus, fw);
- release_firmware(fw);
- if (bcmerror) {
- brcmf_err("dongle image file download failed\n");
- brcmf_fw_nvram_free(nvram);
- goto err;
- }
-
- bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen);
- brcmf_fw_nvram_free(nvram);
- if (bcmerror) {
- brcmf_err("dongle nvram file download failed\n");
- goto err;
- }
-
- /* Take arm out of reset */
- if (!brcmf_chip_set_active(bus->ci, rstvec)) {
- brcmf_err("error getting out of ARM core reset\n");
- goto err;
- }
-
- /* Allow full data communication using DPC from now on. */
- brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
- bcmerror = 0;
-
-err:
- brcmf_sdio_clkctl(bus, CLK_SDONLY, false);
- sdio_release_host(bus->sdiodev->func[1]);
- return bcmerror;
-}
-
-static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
-{
- int err = 0;
- u8 val;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
- if (err) {
- brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
- return;
- }
-
- val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
- if (err) {
- brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
- return;
- }
-
- /* Add CMD14 Support */
- brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
- (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
- SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
- &err);
- if (err) {
- brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
- return;
- }
-
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- SBSDIO_FORCE_HT, &err);
- if (err) {
- brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
- return;
- }
-
- /* set flag */
- bus->sr_enabled = true;
- brcmf_dbg(INFO, "SR enabled\n");
-}
-
-/* enable KSO bit */
-static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
-{
- u8 val;
- int err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* KSO bit added in SDIO core rev 12 */
- if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)
- return 0;
-
- val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
- if (err) {
- brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");
- return err;
- }
-
- if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
- val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<
- SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
- val, &err);
- if (err) {
- brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");
- return err;
- }
- }
-
- return 0;
-}
-
-
-static int brcmf_sdio_bus_preinit(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio *bus = sdiodev->bus;
- uint pad_size;
- u32 value;
- int err;
-
- /* the commands below use the terms tx and rx from
- * a device perspective, ie. bus:txglom affects the
- * bus transfers from device to host.
- */
- if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) {
- /* for sdio core rev < 12, disable txgloming */
- value = 0;
- err = brcmf_iovar_data_set(dev, "bus:txglom", &value,
- sizeof(u32));
- } else {
- /* otherwise, set txglomalign */
- value = 4;
- if (sdiodev->pdata)
- value = sdiodev->pdata->sd_sgentry_align;
- /* SDIO ADMA requires at least 32 bit alignment */
- value = max_t(u32, value, 4);
- err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value,
- sizeof(u32));
- }
-
- if (err < 0)
- goto done;
-
- bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
- if (sdiodev->sg_support) {
- bus->txglom = false;
- value = 1;
- pad_size = bus->sdiodev->func[2]->cur_blksize << 1;
- err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom",
- &value, sizeof(u32));
- if (err < 0) {
- /* bus:rxglom is allowed to fail */
- err = 0;
- } else {
- bus->txglom = true;
- bus->tx_hdrlen += SDPCM_HWEXT_LEN;
- }
- }
- brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen);
-
-done:
- return err;
-}
-
-static size_t brcmf_sdio_bus_get_ramsize(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio *bus = sdiodev->bus;
-
- return bus->ci->ramsize - bus->ci->srsize;
-}
-
-static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data,
- size_t mem_size)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio *bus = sdiodev->bus;
- int err;
- int address;
- int offset;
- int len;
-
- brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase,
- mem_size);
-
- address = bus->ci->rambase;
- offset = err = 0;
- sdio_claim_host(sdiodev->func[1]);
- while (offset < mem_size) {
- len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK :
- mem_size - offset;
- err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len);
- if (err) {
- brcmf_err("error %d on reading %d membytes at 0x%08x\n",
- err, len, address);
- goto done;
- }
- data += len;
- offset += len;
- address += len;
- }
-
-done:
- sdio_release_host(sdiodev->func[1]);
- return err;
-}
-
-void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
-{
- if (!bus->dpc_triggered) {
- bus->dpc_triggered = true;
- queue_work(bus->brcmf_wq, &bus->datawork);
- }
-}
-
-void brcmf_sdio_isr(struct brcmf_sdio *bus)
-{
- brcmf_dbg(TRACE, "Enter\n");
-
- if (!bus) {
- brcmf_err("bus is null pointer, exiting\n");
- return;
- }
-
- /* Count the interrupt call */
- bus->sdcnt.intrcount++;
- if (in_interrupt())
- atomic_set(&bus->ipend, 1);
- else
- if (brcmf_sdio_intr_rstatus(bus)) {
- brcmf_err("failed backplane access\n");
- }
-
- /* Disable additional interrupts (is this needed now)? */
- if (!bus->intr)
- brcmf_err("isr w/o interrupt configured!\n");
-
- bus->dpc_triggered = true;
- queue_work(bus->brcmf_wq, &bus->datawork);
-}
-
-static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
-{
- brcmf_dbg(TIMER, "Enter\n");
-
- /* Poll period: check device if appropriate. */
- if (!bus->sr_enabled &&
- bus->poll && (++bus->polltick >= bus->pollrate)) {
- u32 intstatus = 0;
-
- /* Reset poll tick */
- bus->polltick = 0;
-
- /* Check device if no interrupts */
- if (!bus->intr ||
- (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
-
- if (!bus->dpc_triggered) {
- u8 devpend;
-
- sdio_claim_host(bus->sdiodev->func[1]);
- devpend = brcmf_sdiod_regrb(bus->sdiodev,
- SDIO_CCCR_INTx,
- NULL);
- sdio_release_host(bus->sdiodev->func[1]);
- intstatus = devpend & (INTR_STATUS_FUNC1 |
- INTR_STATUS_FUNC2);
- }
-
- /* If there is something, make like the ISR and
- schedule the DPC */
- if (intstatus) {
- bus->sdcnt.pollcnt++;
- atomic_set(&bus->ipend, 1);
-
- bus->dpc_triggered = true;
- queue_work(bus->brcmf_wq, &bus->datawork);
- }
- }
-
- /* Update interrupt tracking */
- bus->sdcnt.lastintrs = bus->sdcnt.intrcount;
- }
-#ifdef DEBUG
- /* Poll for console output periodically */
- if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
- bus->console_interval != 0) {
- bus->console.count += BRCMF_WD_POLL_MS;
- if (bus->console.count >= bus->console_interval) {
- bus->console.count -= bus->console_interval;
- sdio_claim_host(bus->sdiodev->func[1]);
- /* Make sure backplane clock is on */
- brcmf_sdio_bus_sleep(bus, false, false);
- if (brcmf_sdio_readconsole(bus) < 0)
- /* stop on error */
- bus->console_interval = 0;
- sdio_release_host(bus->sdiodev->func[1]);
- }
- }
-#endif /* DEBUG */
-
- /* On idle timeout clear activity flag and/or turn off clock */
- if (!bus->dpc_triggered) {
- rmb();
- if ((!bus->dpc_running) && (bus->idletime > 0) &&
- (bus->clkstate == CLK_AVAIL)) {
- bus->idlecount++;
- if (bus->idlecount > bus->idletime) {
- brcmf_dbg(SDIO, "idle\n");
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_wd_timer(bus, 0);
- bus->idlecount = 0;
- brcmf_sdio_bus_sleep(bus, true, false);
- sdio_release_host(bus->sdiodev->func[1]);
- }
- } else {
- bus->idlecount = 0;
- }
- } else {
- bus->idlecount = 0;
- }
-}
-
-static void brcmf_sdio_dataworker(struct work_struct *work)
-{
- struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
- datawork);
-
- bus->dpc_running = true;
- wmb();
- while (ACCESS_ONCE(bus->dpc_triggered)) {
- bus->dpc_triggered = false;
- brcmf_sdio_dpc(bus);
- bus->idlecount = 0;
- }
- bus->dpc_running = false;
- if (brcmf_sdiod_freezing(bus->sdiodev)) {
- brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN);
- brcmf_sdiod_try_freeze(bus->sdiodev);
- brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
- }
-}
-
-static void
-brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u32 drivestrength)
-{
- const struct sdiod_drive_str *str_tab = NULL;
- u32 str_mask;
- u32 str_shift;
- u32 base;
- u32 i;
- u32 drivestrength_sel = 0;
- u32 cc_data_temp;
- u32 addr;
-
- if (!(ci->cc_caps & CC_CAP_PMU))
- return;
-
- switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
- case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12):
- str_tab = sdiod_drvstr_tab1_1v8;
- str_mask = 0x00003800;
- str_shift = 11;
- break;
- case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17):
- str_tab = sdiod_drvstr_tab6_1v8;
- str_mask = 0x00001800;
- str_shift = 11;
- break;
- case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17):
- /* note: 43143 does not support tristate */
- i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
- if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
- str_tab = sdiod_drvstr_tab2_3v3;
- str_mask = 0x00000007;
- str_shift = 0;
- } else
- brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
- ci->name, drivestrength);
- break;
- case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13):
- str_tab = sdiod_drive_strength_tab5_1v8;
- str_mask = 0x00003800;
- str_shift = 11;
- break;
- default:
- brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
- ci->name, ci->chiprev, ci->pmurev);
- break;
- }
-
- if (str_tab != NULL) {
- for (i = 0; str_tab[i].strength != 0; i++) {
- if (drivestrength >= str_tab[i].strength) {
- drivestrength_sel = str_tab[i].sel;
- break;
- }
- }
- base = brcmf_chip_get_chipcommon(ci)->base;
- addr = CORE_CC_REG(base, chipcontrol_addr);
- brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
- cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
- cc_data_temp &= ~str_mask;
- drivestrength_sel <<= str_shift;
- cc_data_temp |= drivestrength_sel;
- brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
-
- brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
- str_tab[i].strength, drivestrength, cc_data_temp);
- }
-}
-
-static int brcmf_sdio_buscoreprep(void *ctx)
-{
- struct brcmf_sdio_dev *sdiodev = ctx;
- int err = 0;
- u8 clkval, clkset;
-
- /* Try forcing SDIO core to do ALPAvail request only */
- clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
- if (err) {
- brcmf_err("error writing for HT off\n");
- return err;
- }
-
- /* If register supported, wait for ALPAvail and then force ALP */
- /* This may take up to 15 milliseconds */
- clkval = brcmf_sdiod_regrb(sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR, NULL);
-
- if ((clkval & ~SBSDIO_AVBITS) != clkset) {
- brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
- clkset, clkval);
- return -EACCES;
- }
-
- SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
- !SBSDIO_ALPAV(clkval)),
- PMU_MAX_TRANSITION_DLY);
- if (!SBSDIO_ALPAV(clkval)) {
- brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
- clkval);
- return -EBUSY;
- }
-
- clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
- udelay(65);
-
- /* Also, disable the extra SDIO pull-ups */
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
-
- return 0;
-}
-
-static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip,
- u32 rstvec)
-{
- struct brcmf_sdio_dev *sdiodev = ctx;
- struct brcmf_core *core;
- u32 reg_addr;
-
- /* clear all interrupts */
- core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV);
- reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus);
- brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
-
- if (rstvec)
- /* Write reset vector to address 0 */
- brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
- sizeof(rstvec));
-}
-
-static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
-{
- struct brcmf_sdio_dev *sdiodev = ctx;
- u32 val, rev;
-
- val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
- if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
- addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
- rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
- if (rev >= 2) {
- val &= ~CID_ID_MASK;
- val |= BRCM_CC_4339_CHIP_ID;
- }
- }
- return val;
-}
-
-static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val)
-{
- struct brcmf_sdio_dev *sdiodev = ctx;
-
- brcmf_sdiod_regwl(sdiodev, addr, val, NULL);
-}
-
-static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
- .prepare = brcmf_sdio_buscoreprep,
- .activate = brcmf_sdio_buscore_activate,
- .read32 = brcmf_sdio_buscore_read32,
- .write32 = brcmf_sdio_buscore_write32,
-};
-
-static bool
-brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
-{
- u8 clkctl = 0;
- int err = 0;
- int reg_addr;
- u32 reg_val;
- u32 drivestrength;
-
- sdio_claim_host(bus->sdiodev->func[1]);
-
- pr_debug("F1 signature read @0x18000000=0x%4x\n",
- brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
-
- /*
- * Force PLL off until brcmf_chip_attach()
- * programs PLL control regs
- */
-
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- BRCMF_INIT_CLKCTL1, &err);
- if (!err)
- clkctl = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR, &err);
-
- if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
- brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
- err, BRCMF_INIT_CLKCTL1, clkctl);
- goto fail;
- }
-
- bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
- if (IS_ERR(bus->ci)) {
- brcmf_err("brcmf_chip_attach failed!\n");
- bus->ci = NULL;
- goto fail;
- }
-
- if (brcmf_sdio_kso_init(bus)) {
- brcmf_err("error enabling KSO\n");
- goto fail;
- }
-
- if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength))
- drivestrength = bus->sdiodev->pdata->drive_strength;
- else
- drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
- brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
-
- /* Set card control so an SDIO card reset does a WLAN backplane reset */
- reg_val = brcmf_sdiod_regrb(bus->sdiodev,
- SDIO_CCCR_BRCM_CARDCTRL, &err);
- if (err)
- goto fail;
-
- reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
-
- brcmf_sdiod_regwb(bus->sdiodev,
- SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
- if (err)
- goto fail;
-
- /* set PMUControl so a backplane reset does PMU state reload */
- reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
- pmucontrol);
- reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
- if (err)
- goto fail;
-
- reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
-
- brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);
- if (err)
- goto fail;
-
- sdio_release_host(bus->sdiodev->func[1]);
-
- brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
-
- /* allocate header buffer */
- bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL);
- if (!bus->hdrbuf)
- return false;
- /* Locate an appropriately-aligned portion of hdrbuf */
- bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0],
- bus->head_align);
-
- /* Set the poll and/or interrupt flags */
- bus->intr = true;
- bus->poll = false;
- if (bus->poll)
- bus->pollrate = 1;
-
- return true;
-
-fail:
- sdio_release_host(bus->sdiodev->func[1]);
- return false;
-}
-
-static int
-brcmf_sdio_watchdog_thread(void *data)
-{
- struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
- int wait;
-
- allow_signal(SIGTERM);
- /* Run until signal received */
- brcmf_sdiod_freezer_count(bus->sdiodev);
- while (1) {
- if (kthread_should_stop())
- break;
- brcmf_sdiod_freezer_uncount(bus->sdiodev);
- wait = wait_for_completion_interruptible(&bus->watchdog_wait);
- brcmf_sdiod_freezer_count(bus->sdiodev);
- brcmf_sdiod_try_freeze(bus->sdiodev);
- if (!wait) {
- brcmf_sdio_bus_watchdog(bus);
- /* Count the tick for reference */
- bus->sdcnt.tickcnt++;
- reinit_completion(&bus->watchdog_wait);
- } else
- break;
- }
- return 0;
-}
-
-static void
-brcmf_sdio_watchdog(unsigned long data)
-{
- struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
-
- if (bus->watchdog_tsk) {
- complete(&bus->watchdog_wait);
- /* Reschedule the watchdog */
- if (bus->wd_timer_valid)
- mod_timer(&bus->timer,
- jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS));
- }
-}
-
-static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
- .stop = brcmf_sdio_bus_stop,
- .preinit = brcmf_sdio_bus_preinit,
- .txdata = brcmf_sdio_bus_txdata,
- .txctl = brcmf_sdio_bus_txctl,
- .rxctl = brcmf_sdio_bus_rxctl,
- .gettxq = brcmf_sdio_bus_gettxq,
- .wowl_config = brcmf_sdio_wowl_config,
- .get_ramsize = brcmf_sdio_bus_get_ramsize,
- .get_memdump = brcmf_sdio_bus_get_memdump,
-};
-
-static void brcmf_sdio_firmware_callback(struct device *dev,
- const struct firmware *code,
- void *nvram, u32 nvram_len)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio *bus = sdiodev->bus;
- int err = 0;
- u8 saveclk;
-
- brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev));
-
- if (!bus_if->drvr)
- return;
-
- /* try to download image and nvram to the dongle */
- bus->alp_only = true;
- err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
- if (err)
- goto fail;
- bus->alp_only = false;
-
- /* Start the watchdog timer */
- bus->sdcnt.tickcnt = 0;
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
-
- sdio_claim_host(sdiodev->func[1]);
-
- /* Make sure backplane clock is on, needed to generate F2 interrupt */
- brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
- if (bus->clkstate != CLK_AVAIL)
- goto release;
-
- /* Force clocks on backplane to be sure F2 interrupt propagates */
- saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
- if (!err) {
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- (saveclk | SBSDIO_FORCE_HT), &err);
- }
- if (err) {
- brcmf_err("Failed to force clock for F2: err %d\n", err);
- goto release;
- }
-
- /* Enable function 2 (frame transfers) */
- w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
- offsetof(struct sdpcmd_regs, tosbmailboxdata));
- err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
-
-
- brcmf_dbg(INFO, "enable F2: err=%d\n", err);
-
- /* If F2 successfully enabled, set core and enable interrupts */
- if (!err) {
- /* Set up the interrupt mask and enable interrupts */
- bus->hostintmask = HOSTINTMASK;
- w_sdreg32(bus, bus->hostintmask,
- offsetof(struct sdpcmd_regs, hostintmask));
-
- brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
- } else {
- /* Disable F2 again */
- sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
- goto release;
- }
-
- if (brcmf_chip_sr_capable(bus->ci)) {
- brcmf_sdio_sr_init(bus);
- } else {
- /* Restore previous clock setting */
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- saveclk, &err);
- }
-
- if (err == 0) {
- err = brcmf_sdiod_intr_register(sdiodev);
- if (err != 0)
- brcmf_err("intr register failed:%d\n", err);
- }
-
- /* If we didn't come up, turn off backplane clock */
- if (err != 0)
- brcmf_sdio_clkctl(bus, CLK_NONE, false);
-
- sdio_release_host(sdiodev->func[1]);
-
- err = brcmf_bus_start(dev);
- if (err != 0) {
- brcmf_err("dongle is not responding\n");
- goto fail;
- }
- return;
-
-release:
- sdio_release_host(sdiodev->func[1]);
-fail:
- brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
- device_release_driver(dev);
-}
-
-struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
-{
- int ret;
- struct brcmf_sdio *bus;
- struct workqueue_struct *wq;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* Allocate private bus interface state */
- bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC);
- if (!bus)
- goto fail;
-
- bus->sdiodev = sdiodev;
- sdiodev->bus = bus;
- skb_queue_head_init(&bus->glom);
- bus->txbound = BRCMF_TXBOUND;
- bus->rxbound = BRCMF_RXBOUND;
- bus->txminmax = BRCMF_TXMINMAX;
- bus->tx_seq = SDPCM_SEQ_WRAP - 1;
-
- /* platform specific configuration:
- * alignments must be at least 4 bytes for ADMA
- */
- bus->head_align = ALIGNMENT;
- bus->sgentry_align = ALIGNMENT;
- if (sdiodev->pdata) {
- if (sdiodev->pdata->sd_head_align > ALIGNMENT)
- bus->head_align = sdiodev->pdata->sd_head_align;
- if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT)
- bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
- }
-
- /* single-threaded workqueue */
- wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
- dev_name(&sdiodev->func[1]->dev));
- if (!wq) {
- brcmf_err("insufficient memory to create txworkqueue\n");
- goto fail;
- }
- brcmf_sdiod_freezer_count(sdiodev);
- INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
- bus->brcmf_wq = wq;
-
- /* attempt to attach to the dongle */
- if (!(brcmf_sdio_probe_attach(bus))) {
- brcmf_err("brcmf_sdio_probe_attach failed\n");
- goto fail;
- }
-
- spin_lock_init(&bus->rxctl_lock);
- spin_lock_init(&bus->txq_lock);
- init_waitqueue_head(&bus->ctrl_wait);
- init_waitqueue_head(&bus->dcmd_resp_wait);
-
- /* Set up the watchdog timer */
- init_timer(&bus->timer);
- bus->timer.data = (unsigned long)bus;
- bus->timer.function = brcmf_sdio_watchdog;
-
- /* Initialize watchdog thread */
- init_completion(&bus->watchdog_wait);
- bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread,
- bus, "brcmf_wdog/%s",
- dev_name(&sdiodev->func[1]->dev));
- if (IS_ERR(bus->watchdog_tsk)) {
- pr_warn("brcmf_watchdog thread failed to start\n");
- bus->watchdog_tsk = NULL;
- }
- /* Initialize DPC thread */
- bus->dpc_triggered = false;
- bus->dpc_running = false;
-
- /* Assign bus interface call back */
- bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
- bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops;
- bus->sdiodev->bus_if->chip = bus->ci->chip;
- bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
-
- /* default sdio bus header length for tx packet */
- bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
-
- /* Attach to the common layer, reserve hdr space */
- ret = brcmf_attach(bus->sdiodev->dev);
- if (ret != 0) {
- brcmf_err("brcmf_attach failed\n");
- goto fail;
- }
-
- /* Query the F2 block size, set roundup accordingly */
- bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
- bus->roundup = min(max_roundup, bus->blocksize);
-
- /* Allocate buffers */
- if (bus->sdiodev->bus_if->maxctl) {
- bus->sdiodev->bus_if->maxctl += bus->roundup;
- bus->rxblen =
- roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
- ALIGNMENT) + bus->head_align;
- bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
- if (!(bus->rxbuf)) {
- brcmf_err("rxbuf allocation failed\n");
- goto fail;
- }
- }
-
- sdio_claim_host(bus->sdiodev->func[1]);
-
- /* Disable F2 to clear any intermediate frame state on the dongle */
- sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
-
- bus->rxflow = false;
-
- /* Done with backplane-dependent accesses, can drop clock... */
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
-
- sdio_release_host(bus->sdiodev->func[1]);
-
- /* ...and initialize clock/power states */
- bus->clkstate = CLK_SDONLY;
- bus->idletime = BRCMF_IDLE_INTERVAL;
- bus->idleclock = BRCMF_IDLE_ACTIVE;
-
- /* SR state */
- bus->sr_enabled = false;
-
- brcmf_sdio_debugfs_create(bus);
- brcmf_dbg(INFO, "completed!!\n");
-
- ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev);
- if (ret)
- goto fail;
-
- ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
- sdiodev->fw_name, sdiodev->nvram_name,
- brcmf_sdio_firmware_callback);
- if (ret != 0) {
- brcmf_err("async firmware request failed: %d\n", ret);
- goto fail;
- }
-
- return bus;
-
-fail:
- brcmf_sdio_remove(bus);
- return NULL;
-}
-
-/* Detach and free everything */
-void brcmf_sdio_remove(struct brcmf_sdio *bus)
-{
- brcmf_dbg(TRACE, "Enter\n");
-
- if (bus) {
- /* De-register interrupt handler */
- brcmf_sdiod_intr_unregister(bus->sdiodev);
-
- brcmf_detach(bus->sdiodev->dev);
-
- cancel_work_sync(&bus->datawork);
- if (bus->brcmf_wq)
- destroy_workqueue(bus->brcmf_wq);
-
- if (bus->ci) {
- if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_wd_timer(bus, 0);
- brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
- /* Leave the device in state where it is
- * 'passive'. This is done by resetting all
- * necessary cores.
- */
- msleep(20);
- brcmf_chip_set_passive(bus->ci);
- brcmf_sdio_clkctl(bus, CLK_NONE, false);
- sdio_release_host(bus->sdiodev->func[1]);
- }
- brcmf_chip_detach(bus->ci);
- }
-
- kfree(bus->rxbuf);
- kfree(bus->hdrbuf);
- kfree(bus);
- }
-
- brcmf_dbg(TRACE, "Disconnected\n");
-}
-
-void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick)
-{
- /* Totally stop the timer */
- if (!wdtick && bus->wd_timer_valid) {
- del_timer_sync(&bus->timer);
- bus->wd_timer_valid = false;
- bus->save_ms = wdtick;
- return;
- }
-
- /* don't start the wd until fw is loaded */
- if (bus->sdiodev->state != BRCMF_SDIOD_DATA)
- return;
-
- if (wdtick) {
- if (bus->save_ms != BRCMF_WD_POLL_MS) {
- if (bus->wd_timer_valid)
- /* Stop timer and restart at new value */
- del_timer_sync(&bus->timer);
-
- /* Create timer again when watchdog period is
- dynamically changed or in the first instance
- */
- bus->timer.expires =
- jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS);
- add_timer(&bus->timer);
-
- } else {
- /* Re arm the timer, at last watchdog period */
- mod_timer(&bus->timer,
- jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS));
- }
-
- bus->wd_timer_valid = true;
- bus->save_ms = wdtick;
- }
-}
-
-int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep)
-{
- int ret;
-
- sdio_claim_host(bus->sdiodev->func[1]);
- ret = brcmf_sdio_bus_sleep(bus, sleep, false);
- sdio_release_host(bus->sdiodev->func[1]);
-
- return ret;
-}
-
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
deleted file mode 100644
index 7328478b2d7b..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef BRCMFMAC_SDIO_H
-#define BRCMFMAC_SDIO_H
-
-#include <linux/skbuff.h>
-#include <linux/firmware.h>
-#include "firmware.h"
-
-#define SDIO_FUNC_0 0
-#define SDIO_FUNC_1 1
-#define SDIO_FUNC_2 2
-
-#define SDIOD_FBR_SIZE 0x100
-
-/* io_en */
-#define SDIO_FUNC_ENABLE_1 0x02
-#define SDIO_FUNC_ENABLE_2 0x04
-
-/* io_rdys */
-#define SDIO_FUNC_READY_1 0x02
-#define SDIO_FUNC_READY_2 0x04
-
-/* intr_status */
-#define INTR_STATUS_FUNC1 0x2
-#define INTR_STATUS_FUNC2 0x4
-
-/* Maximum number of I/O funcs */
-#define SDIOD_MAX_IOFUNCS 7
-
-/* mask of register map */
-#define REG_F0_REG_MASK 0x7FF
-#define REG_F1_MISC_MASK 0x1FFFF
-
-/* as of sdiod rev 0, supports 3 functions */
-#define SBSDIO_NUM_FUNCTION 3
-
-/* function 0 vendor specific CCCR registers */
-#define SDIO_CCCR_BRCM_CARDCAP 0xf0
-#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02
-#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04
-#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08
-#define SDIO_CCCR_BRCM_CARDCTRL 0xf1
-#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02
-#define SDIO_CCCR_BRCM_SEPINT 0xf2
-
-#define SDIO_SEPINT_MASK 0x01
-#define SDIO_SEPINT_OE 0x02
-#define SDIO_SEPINT_ACT_HI 0x04
-
-/* function 1 miscellaneous registers */
-
-/* sprom command and status */
-#define SBSDIO_SPROM_CS 0x10000
-/* sprom info register */
-#define SBSDIO_SPROM_INFO 0x10001
-/* sprom indirect access data byte 0 */
-#define SBSDIO_SPROM_DATA_LOW 0x10002
-/* sprom indirect access data byte 1 */
-#define SBSDIO_SPROM_DATA_HIGH 0x10003
-/* sprom indirect access addr byte 0 */
-#define SBSDIO_SPROM_ADDR_LOW 0x10004
-/* gpio select */
-#define SBSDIO_GPIO_SELECT 0x10005
-/* gpio output */
-#define SBSDIO_GPIO_OUT 0x10006
-/* gpio enable */
-#define SBSDIO_GPIO_EN 0x10007
-/* rev < 7, watermark for sdio device */
-#define SBSDIO_WATERMARK 0x10008
-/* control busy signal generation */
-#define SBSDIO_DEVICE_CTL 0x10009
-
-/* SB Address Window Low (b15) */
-#define SBSDIO_FUNC1_SBADDRLOW 0x1000A
-/* SB Address Window Mid (b23:b16) */
-#define SBSDIO_FUNC1_SBADDRMID 0x1000B
-/* SB Address Window High (b31:b24) */
-#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C
-/* Frame Control (frame term/abort) */
-#define SBSDIO_FUNC1_FRAMECTRL 0x1000D
-/* ChipClockCSR (ALP/HT ctl/status) */
-#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E
-/* SdioPullUp (on cmd, d0-d2) */
-#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F
-/* Write Frame Byte Count Low */
-#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019
-/* Write Frame Byte Count High */
-#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A
-/* Read Frame Byte Count Low */
-#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B
-/* Read Frame Byte Count High */
-#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C
-/* MesBusyCtl (rev 11) */
-#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D
-/* Sdio Core Rev 12 */
-#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E
-#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1
-#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0
-#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2
-#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1
-#define SBSDIO_FUNC1_SLEEPCSR 0x1001F
-#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1
-#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0
-#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1
-#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2
-#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1
-
-#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */
-#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */
-
-/* function 1 OCP space */
-
-/* sb offset addr is <= 15 bits, 32k */
-#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF
-#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000
-/* with b15, maps to 32-bit SB access */
-#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000
-
-/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
-
-#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */
-#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */
-#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */
-/* Address bits from SBADDR regs */
-#define SBSDIO_SBWINDOW_MASK 0xffff8000
-
-#define SDIOH_READ 0 /* Read request */
-#define SDIOH_WRITE 1 /* Write request */
-
-#define SDIOH_DATA_FIX 0 /* Fixed addressing */
-#define SDIOH_DATA_INC 1 /* Incremental addressing */
-
-/* internal return code */
-#define SUCCESS 0
-#define ERROR 1
-
-/* Packet alignment for most efficient SDIO (can change based on platform) */
-#define BRCMF_SDALIGN (1 << 6)
-
-/* watchdog polling interval in ms */
-#define BRCMF_WD_POLL_MS 10
-
-/**
- * enum brcmf_sdiod_state - the state of the bus.
- *
- * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC.
- * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled.
- * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible.
- */
-enum brcmf_sdiod_state {
- BRCMF_SDIOD_DOWN,
- BRCMF_SDIOD_DATA,
- BRCMF_SDIOD_NOMEDIUM
-};
-
-struct brcmf_sdreg {
- int func;
- int offset;
- int value;
-};
-
-struct brcmf_sdio;
-struct brcmf_sdiod_freezer;
-
-struct brcmf_sdio_dev {
- struct sdio_func *func[SDIO_MAX_FUNCS];
- u8 num_funcs; /* Supported funcs on client */
- u32 sbwad; /* Save backplane window address */
- struct brcmf_sdio *bus;
- struct device *dev;
- struct brcmf_bus *bus_if;
- struct brcmfmac_sdio_platform_data *pdata;
- bool oob_irq_requested;
- bool irq_en; /* irq enable flags */
- spinlock_t irq_en_lock;
- bool irq_wake; /* irq wake enable flags */
- bool sg_support;
- uint max_request_size;
- ushort max_segment_count;
- uint max_segment_size;
- uint txglomsz;
- struct sg_table sgtable;
- char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
- char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
- bool wowl_enabled;
- enum brcmf_sdiod_state state;
- struct brcmf_sdiod_freezer *freezer;
-};
-
-/* sdio core registers */
-struct sdpcmd_regs {
- u32 corecontrol; /* 0x00, rev8 */
- u32 corestatus; /* rev8 */
- u32 PAD[1];
- u32 biststatus; /* rev8 */
-
- /* PCMCIA access */
- u16 pcmciamesportaladdr; /* 0x010, rev8 */
- u16 PAD[1];
- u16 pcmciamesportalmask; /* rev8 */
- u16 PAD[1];
- u16 pcmciawrframebc; /* rev8 */
- u16 PAD[1];
- u16 pcmciaunderflowtimer; /* rev8 */
- u16 PAD[1];
-
- /* interrupt */
- u32 intstatus; /* 0x020, rev8 */
- u32 hostintmask; /* rev8 */
- u32 intmask; /* rev8 */
- u32 sbintstatus; /* rev8 */
- u32 sbintmask; /* rev8 */
- u32 funcintmask; /* rev4 */
- u32 PAD[2];
- u32 tosbmailbox; /* 0x040, rev8 */
- u32 tohostmailbox; /* rev8 */
- u32 tosbmailboxdata; /* rev8 */
- u32 tohostmailboxdata; /* rev8 */
-
- /* synchronized access to registers in SDIO clock domain */
- u32 sdioaccess; /* 0x050, rev8 */
- u32 PAD[3];
-
- /* PCMCIA frame control */
- u8 pcmciaframectrl; /* 0x060, rev8 */
- u8 PAD[3];
- u8 pcmciawatermark; /* rev8 */
- u8 PAD[155];
-
- /* interrupt batching control */
- u32 intrcvlazy; /* 0x100, rev8 */
- u32 PAD[3];
-
- /* counters */
- u32 cmd52rd; /* 0x110, rev8 */
- u32 cmd52wr; /* rev8 */
- u32 cmd53rd; /* rev8 */
- u32 cmd53wr; /* rev8 */
- u32 abort; /* rev8 */
- u32 datacrcerror; /* rev8 */
- u32 rdoutofsync; /* rev8 */
- u32 wroutofsync; /* rev8 */
- u32 writebusy; /* rev8 */
- u32 readwait; /* rev8 */
- u32 readterm; /* rev8 */
- u32 writeterm; /* rev8 */
- u32 PAD[40];
- u32 clockctlstatus; /* rev8 */
- u32 PAD[7];
-
- u32 PAD[128]; /* DMA engines */
-
- /* SDIO/PCMCIA CIS region */
- char cis[512]; /* 0x400-0x5ff, rev6 */
-
- /* PCMCIA function control registers */
- char pcmciafcr[256]; /* 0x600-6ff, rev6 */
- u16 PAD[55];
-
- /* PCMCIA backplane access */
- u16 backplanecsr; /* 0x76E, rev6 */
- u16 backplaneaddr0; /* rev6 */
- u16 backplaneaddr1; /* rev6 */
- u16 backplaneaddr2; /* rev6 */
- u16 backplaneaddr3; /* rev6 */
- u16 backplanedata0; /* rev6 */
- u16 backplanedata1; /* rev6 */
- u16 backplanedata2; /* rev6 */
- u16 backplanedata3; /* rev6 */
- u16 PAD[31];
-
- /* sprom "size" & "blank" info */
- u16 spromstatus; /* 0x7BE, rev2 */
- u32 PAD[464];
-
- u16 PAD[0x80];
-};
-
-/* Register/deregister interrupt handler. */
-int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev);
-int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev);
-
-/* sdio device register access interface */
-u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
-u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
-void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data,
- int *ret);
-void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
- int *ret);
-
-/* Buffer transfer to/from device (client) core via cmd53.
- * fn: function number
- * flags: backplane width, address increment, sync/async
- * buf: pointer to memory data buffer
- * nbytes: number of bytes to transfer to/from buf
- * pkt: pointer to packet associated with buf (if any)
- * complete: callback function for command completion (async only)
- * handle: handle for completion callback (first arg in callback)
- * Returns 0 or error code.
- * NOTE: Async operation is not currently supported.
- */
-int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
- struct sk_buff_head *pktq);
-int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
-
-int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt);
-int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
-int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
- struct sk_buff_head *pktq, uint totlen);
-
-/* Flags bits */
-
-/* Four-byte target (backplane) width (vs. two-byte) */
-#define SDIO_REQ_4BYTE 0x1
-/* Fixed address (FIFO) (vs. incrementing address) */
-#define SDIO_REQ_FIXED 0x2
-
-/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
- * rw: read or write (0/1)
- * addr: direct SDIO address
- * buf: pointer to memory data buffer
- * nbytes: number of bytes to transfer to/from buf
- * Returns 0 or error code.
- */
-int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
- u8 *data, uint size);
-
-/* Issue an abort to the specified function */
-int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
-void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
- enum brcmf_sdiod_state state);
-#ifdef CONFIG_PM_SLEEP
-bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev);
-void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev);
-void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev);
-void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev);
-#else
-static inline bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
-{
- return false;
-}
-static inline void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
-{
-}
-static inline void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
-{
-}
-static inline void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
-{
-}
-#endif /* CONFIG_PM_SLEEP */
-
-struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
-void brcmf_sdio_remove(struct brcmf_sdio *bus);
-void brcmf_sdio_isr(struct brcmf_sdio *bus);
-
-void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick);
-void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
-int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep);
-void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
-
-#endif /* BRCMFMAC_SDIO_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
deleted file mode 100644
index 689e64d004bc..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ /dev/null
@@ -1,1535 +0,0 @@
-/*
- * Copyright (c) 2011 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/firmware.h>
-#include <linux/usb.h>
-#include <linux/vmalloc.h>
-
-#include <brcmu_utils.h>
-#include <brcm_hw_ids.h>
-#include <brcmu_wifi.h>
-#include "bus.h"
-#include "debug.h"
-#include "firmware.h"
-#include "usb.h"
-
-
-#define IOCTL_RESP_TIMEOUT 2000
-
-#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
-#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10
-
-#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
- has boot up */
-#define BRCMF_USB_NRXQ 50
-#define BRCMF_USB_NTXQ 50
-
-#define BRCMF_USB_CBCTL_WRITE 0
-#define BRCMF_USB_CBCTL_READ 1
-#define BRCMF_USB_MAX_PKT_SIZE 1600
-
-#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
-#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
-#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
-#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin"
-
-#define TRX_MAGIC 0x30524448 /* "HDR0" */
-#define TRX_MAX_OFFSET 3 /* Max number of file offsets */
-#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */
-#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */
-#define TRX_OFFSETS_DLFWLEN_IDX 0
-
-/* Control messages: bRequest values */
-#define DL_GETSTATE 0 /* returns the rdl_state_t struct */
-#define DL_CHECK_CRC 1 /* currently unused */
-#define DL_GO 2 /* execute downloaded image */
-#define DL_START 3 /* initialize dl state */
-#define DL_REBOOT 4 /* reboot the device in 2 seconds */
-#define DL_GETVER 5 /* returns the bootrom_id_t struct */
-#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset
- * event to occur in 2 seconds. It is the
- * responsibility of the downloaded code to
- * clear this event
- */
-#define DL_EXEC 7 /* jump to a supplied address */
-#define DL_RESETCFG 8 /* To support single enum on dongle
- * - Not used by bootloader
- */
-#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
- * if resp unavailable
- */
-
-/* states */
-#define DL_WAITING 0 /* waiting to rx first pkt */
-#define DL_READY 1 /* hdr was good, waiting for more of the
- * compressed image
- */
-#define DL_BAD_HDR 2 /* hdr was corrupted */
-#define DL_BAD_CRC 3 /* compressed image was corrupted */
-#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */
-#define DL_START_FAIL 5 /* failed to initialize correctly */
-#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM
- * value
- */
-#define DL_IMAGE_TOOBIG 7 /* firmware image too big */
-
-
-struct trx_header_le {
- __le32 magic; /* "HDR0" */
- __le32 len; /* Length of file including header */
- __le32 crc32; /* CRC from flag_version to end of file */
- __le32 flag_version; /* 0:15 flags, 16:31 version */
- __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of
- * header
- */
-};
-
-struct rdl_state_le {
- __le32 state;
- __le32 bytes;
-};
-
-struct bootrom_id_le {
- __le32 chip; /* Chip id */
- __le32 chiprev; /* Chip rev */
- __le32 ramsize; /* Size of RAM */
- __le32 remapbase; /* Current remap base address */
- __le32 boardtype; /* Type of board */
- __le32 boardrev; /* Board revision */
-};
-
-struct brcmf_usb_image {
- struct list_head list;
- s8 *fwname;
- u8 *image;
- int image_len;
-};
-
-struct brcmf_usbdev_info {
- struct brcmf_usbdev bus_pub; /* MUST BE FIRST */
- spinlock_t qlock;
- struct list_head rx_freeq;
- struct list_head rx_postq;
- struct list_head tx_freeq;
- struct list_head tx_postq;
- uint rx_pipe, tx_pipe;
-
- int rx_low_watermark;
- int tx_low_watermark;
- 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;
-
- const u8 *image; /* buffer for combine fw and nvram */
- int image_len;
-
- struct usb_device *usbdev;
- struct device *dev;
- struct mutex dev_init_lock;
-
- int ctl_in_pipe, ctl_out_pipe;
- struct urb *ctl_urb; /* URB for control endpoint */
- struct usb_ctrlrequest ctl_write;
- struct usb_ctrlrequest ctl_read;
- u32 ctl_urb_actual_length;
- int ctl_urb_status;
- int ctl_completed;
- wait_queue_head_t ioctl_resp_wait;
- ulong ctl_op;
- u8 ifnum;
-
- struct urb *bulk_urb; /* used for FW download */
-
- bool wowl_enabled;
-};
-
-static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
- struct brcmf_usbreq *req);
-
-static struct brcmf_usbdev *brcmf_usb_get_buspub(struct device *dev)
-{
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- return bus_if->bus_priv.usb;
-}
-
-static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev)
-{
- return brcmf_usb_get_buspub(dev)->devinfo;
-}
-
-static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo)
-{
- return wait_event_timeout(devinfo->ioctl_resp_wait,
- devinfo->ctl_completed,
- msecs_to_jiffies(IOCTL_RESP_TIMEOUT));
-}
-
-static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo)
-{
- if (waitqueue_active(&devinfo->ioctl_resp_wait))
- wake_up(&devinfo->ioctl_resp_wait);
-}
-
-static void
-brcmf_usb_ctl_complete(struct brcmf_usbdev_info *devinfo, int type, int status)
-{
- brcmf_dbg(USB, "Enter, status=%d\n", status);
-
- if (unlikely(devinfo == NULL))
- return;
-
- if (type == BRCMF_USB_CBCTL_READ) {
- if (status == 0)
- devinfo->bus_pub.stats.rx_ctlpkts++;
- else
- devinfo->bus_pub.stats.rx_ctlerrs++;
- } else if (type == BRCMF_USB_CBCTL_WRITE) {
- if (status == 0)
- devinfo->bus_pub.stats.tx_ctlpkts++;
- else
- devinfo->bus_pub.stats.tx_ctlerrs++;
- }
-
- devinfo->ctl_urb_status = status;
- devinfo->ctl_completed = true;
- brcmf_usb_ioctl_resp_wake(devinfo);
-}
-
-static void
-brcmf_usb_ctlread_complete(struct urb *urb)
-{
- struct brcmf_usbdev_info *devinfo =
- (struct brcmf_usbdev_info *)urb->context;
-
- brcmf_dbg(USB, "Enter\n");
- devinfo->ctl_urb_actual_length = urb->actual_length;
- brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_READ,
- urb->status);
-}
-
-static void
-brcmf_usb_ctlwrite_complete(struct urb *urb)
-{
- struct brcmf_usbdev_info *devinfo =
- (struct brcmf_usbdev_info *)urb->context;
-
- brcmf_dbg(USB, "Enter\n");
- brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_WRITE,
- urb->status);
-}
-
-static int
-brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
-{
- int ret;
- u16 size;
-
- brcmf_dbg(USB, "Enter\n");
- if (devinfo == NULL || buf == NULL ||
- len == 0 || devinfo->ctl_urb == NULL)
- return -EINVAL;
-
- size = len;
- devinfo->ctl_write.wLength = cpu_to_le16p(&size);
- devinfo->ctl_urb->transfer_buffer_length = size;
- devinfo->ctl_urb_status = 0;
- devinfo->ctl_urb_actual_length = 0;
-
- usb_fill_control_urb(devinfo->ctl_urb,
- devinfo->usbdev,
- devinfo->ctl_out_pipe,
- (unsigned char *) &devinfo->ctl_write,
- buf, size,
- (usb_complete_t)brcmf_usb_ctlwrite_complete,
- devinfo);
-
- ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
- if (ret < 0)
- brcmf_err("usb_submit_urb failed %d\n", ret);
-
- return ret;
-}
-
-static int
-brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
-{
- int ret;
- u16 size;
-
- brcmf_dbg(USB, "Enter\n");
- if ((devinfo == NULL) || (buf == NULL) || (len == 0)
- || (devinfo->ctl_urb == NULL))
- return -EINVAL;
-
- size = len;
- devinfo->ctl_read.wLength = cpu_to_le16p(&size);
- devinfo->ctl_urb->transfer_buffer_length = size;
-
- devinfo->ctl_read.bRequestType = USB_DIR_IN
- | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- devinfo->ctl_read.bRequest = 1;
-
- usb_fill_control_urb(devinfo->ctl_urb,
- devinfo->usbdev,
- devinfo->ctl_in_pipe,
- (unsigned char *) &devinfo->ctl_read,
- buf, size,
- (usb_complete_t)brcmf_usb_ctlread_complete,
- devinfo);
-
- ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
- if (ret < 0)
- brcmf_err("usb_submit_urb failed %d\n", ret);
-
- return ret;
-}
-
-static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len)
-{
- int err = 0;
- int timeout = 0;
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
-
- brcmf_dbg(USB, "Enter\n");
- if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
- return -EIO;
-
- if (test_and_set_bit(0, &devinfo->ctl_op))
- return -EIO;
-
- devinfo->ctl_completed = false;
- err = brcmf_usb_send_ctl(devinfo, buf, len);
- if (err) {
- brcmf_err("fail %d bytes: %d\n", err, len);
- clear_bit(0, &devinfo->ctl_op);
- return err;
- }
- timeout = brcmf_usb_ioctl_resp_wait(devinfo);
- clear_bit(0, &devinfo->ctl_op);
- if (!timeout) {
- brcmf_err("Txctl wait timed out\n");
- err = -EIO;
- }
- return err;
-}
-
-static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
-{
- int err = 0;
- int timeout = 0;
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
-
- brcmf_dbg(USB, "Enter\n");
- if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
- return -EIO;
-
- if (test_and_set_bit(0, &devinfo->ctl_op))
- return -EIO;
-
- devinfo->ctl_completed = false;
- err = brcmf_usb_recv_ctl(devinfo, buf, len);
- if (err) {
- brcmf_err("fail %d bytes: %d\n", err, len);
- clear_bit(0, &devinfo->ctl_op);
- return err;
- }
- timeout = brcmf_usb_ioctl_resp_wait(devinfo);
- err = devinfo->ctl_urb_status;
- clear_bit(0, &devinfo->ctl_op);
- if (!timeout) {
- brcmf_err("rxctl wait timed out\n");
- err = -EIO;
- }
- if (!err)
- return devinfo->ctl_urb_actual_length;
- else
- return err;
-}
-
-static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo,
- struct list_head *q, int *counter)
-{
- unsigned long flags;
- struct brcmf_usbreq *req;
- spin_lock_irqsave(&devinfo->qlock, flags);
- if (list_empty(q)) {
- spin_unlock_irqrestore(&devinfo->qlock, flags);
- return NULL;
- }
- req = list_entry(q->next, struct brcmf_usbreq, list);
- list_del_init(q->next);
- if (counter)
- (*counter)--;
- spin_unlock_irqrestore(&devinfo->qlock, flags);
- return req;
-
-}
-
-static void brcmf_usb_enq(struct brcmf_usbdev_info *devinfo,
- struct list_head *q, struct brcmf_usbreq *req,
- int *counter)
-{
- unsigned long flags;
- spin_lock_irqsave(&devinfo->qlock, flags);
- list_add_tail(&req->list, q);
- if (counter)
- (*counter)++;
- spin_unlock_irqrestore(&devinfo->qlock, flags);
-}
-
-static struct brcmf_usbreq *
-brcmf_usbdev_qinit(struct list_head *q, int qsize)
-{
- int i;
- struct brcmf_usbreq *req, *reqs;
-
- reqs = kcalloc(qsize, sizeof(struct brcmf_usbreq), GFP_ATOMIC);
- if (reqs == NULL)
- return NULL;
-
- req = reqs;
-
- for (i = 0; i < qsize; i++) {
- req->urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!req->urb)
- goto fail;
-
- INIT_LIST_HEAD(&req->list);
- list_add_tail(&req->list, q);
- req++;
- }
- return reqs;
-fail:
- brcmf_err("fail!\n");
- while (!list_empty(q)) {
- req = list_entry(q->next, struct brcmf_usbreq, list);
- if (req)
- usb_free_urb(req->urb);
- list_del(q->next);
- }
- return NULL;
-
-}
-
-static void brcmf_usb_free_q(struct list_head *q, bool pending)
-{
- struct brcmf_usbreq *req, *next;
- int i = 0;
- list_for_each_entry_safe(req, next, q, list) {
- if (!req->urb) {
- brcmf_err("bad req\n");
- break;
- }
- i++;
- if (pending) {
- usb_kill_urb(req->urb);
- } else {
- usb_free_urb(req->urb);
- list_del_init(&req->list);
- }
- }
-}
-
-static void brcmf_usb_del_fromq(struct brcmf_usbdev_info *devinfo,
- struct brcmf_usbreq *req)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&devinfo->qlock, flags);
- list_del_init(&req->list);
- spin_unlock_irqrestore(&devinfo->qlock, flags);
-}
-
-
-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);
- brcmf_usb_del_fromq(devinfo, req);
-
- 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)
-{
- struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
- struct brcmf_usbdev_info *devinfo = req->devinfo;
- struct sk_buff *skb;
-
- brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
- brcmf_usb_del_fromq(devinfo, req);
- skb = req->skb;
- req->skb = NULL;
-
- /* zero lenght packets indicate usb "failure". Do not refill */
- if (urb->status != 0 || !urb->actual_length) {
- brcmu_pkt_buf_free_skb(skb);
- brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
- return;
- }
-
- if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
- skb_put(skb, urb->actual_length);
- brcmf_rx_frame(devinfo->dev, skb);
- brcmf_usb_rx_refill(devinfo, req);
- } else {
- brcmu_pkt_buf_free_skb(skb);
- brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
- }
- return;
-
-}
-
-static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
- struct brcmf_usbreq *req)
-{
- struct sk_buff *skb;
- int ret;
-
- if (!req || !devinfo)
- return;
-
- skb = dev_alloc_skb(devinfo->bus_pub.bus_mtu);
- if (!skb) {
- brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
- return;
- }
- req->skb = skb;
-
- usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe,
- skb->data, skb_tailroom(skb), brcmf_usb_rx_complete,
- req);
- req->devinfo = devinfo;
- brcmf_usb_enq(devinfo, &devinfo->rx_postq, req, NULL);
-
- ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (ret) {
- brcmf_usb_del_fromq(devinfo, req);
- brcmu_pkt_buf_free_skb(req->skb);
- req->skb = NULL;
- brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
- }
- return;
-}
-
-static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo)
-{
- struct brcmf_usbreq *req;
-
- if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
- brcmf_err("bus is not up=%d\n", devinfo->bus_pub.state);
- return;
- }
- while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL)
- brcmf_usb_rx_refill(devinfo, req);
-}
-
-static void
-brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
-{
- struct brcmf_bus *bcmf_bus = devinfo->bus_pub.bus;
- int old_state;
-
- brcmf_dbg(USB, "Enter, current state=%d, new state=%d\n",
- devinfo->bus_pub.state, state);
-
- if (devinfo->bus_pub.state == state)
- return;
-
- old_state = devinfo->bus_pub.state;
- devinfo->bus_pub.state = state;
-
- /* update state of upper layer */
- if (state == BRCMFMAC_USB_STATE_DOWN) {
- brcmf_dbg(USB, "DBUS is down\n");
- brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN);
- } else if (state == BRCMFMAC_USB_STATE_UP) {
- brcmf_dbg(USB, "DBUS is up\n");
- brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP);
- } else {
- brcmf_dbg(USB, "DBUS current state=%d\n", state);
- }
-}
-
-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) {
- ret = -EIO;
- goto fail;
- }
-
- req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq,
- &devinfo->tx_freecount);
- if (!req) {
- brcmf_err("no req to send\n");
- ret = -ENOMEM;
- goto fail;
- }
-
- req->skb = skb;
- req->devinfo = devinfo;
- usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
- skb->data, skb->len, brcmf_usb_tx_complete, req);
- req->urb->transfer_flags |= URB_ZERO_PACKET;
- brcmf_usb_enq(devinfo, &devinfo->tx_postq, req, NULL);
- ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (ret) {
- brcmf_err("brcmf_usb_tx usb_submit_urb FAILED\n");
- brcmf_usb_del_fromq(devinfo, req);
- req->skb = NULL;
- brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req,
- &devinfo->tx_freecount);
- 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:
- return ret;
-}
-
-
-static int brcmf_usb_up(struct device *dev)
-{
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
-
- brcmf_dbg(USB, "Enter\n");
- if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
- return 0;
-
- /* Success, indicate devinfo is fully up */
- brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP);
-
- if (devinfo->ctl_urb) {
- devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
- devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
-
- /* CTL Write */
- devinfo->ctl_write.bRequestType =
- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- devinfo->ctl_write.bRequest = 0;
- devinfo->ctl_write.wValue = cpu_to_le16(0);
- devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum);
-
- /* CTL Read */
- devinfo->ctl_read.bRequestType =
- USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- devinfo->ctl_read.bRequest = 1;
- devinfo->ctl_read.wValue = cpu_to_le16(0);
- devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
- }
- brcmf_usb_rx_fill_all(devinfo);
- return 0;
-}
-
-static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo)
-{
- if (devinfo->ctl_urb)
- usb_kill_urb(devinfo->ctl_urb);
- if (devinfo->bulk_urb)
- usb_kill_urb(devinfo->bulk_urb);
- brcmf_usb_free_q(&devinfo->tx_postq, true);
- brcmf_usb_free_q(&devinfo->rx_postq, true);
-}
-
-static void brcmf_usb_down(struct device *dev)
-{
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
-
- brcmf_dbg(USB, "Enter\n");
- if (devinfo == NULL)
- return;
-
- if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN)
- return;
-
- brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
-
- brcmf_cancel_all_urbs(devinfo);
-}
-
-static void
-brcmf_usb_sync_complete(struct urb *urb)
-{
- struct brcmf_usbdev_info *devinfo =
- (struct brcmf_usbdev_info *)urb->context;
-
- devinfo->ctl_completed = true;
- brcmf_usb_ioctl_resp_wake(devinfo);
-}
-
-static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
- void *buffer, int buflen)
-{
- int ret;
- char *tmpbuf;
- u16 size;
-
- if ((!devinfo) || (devinfo->ctl_urb == NULL))
- return -EINVAL;
-
- tmpbuf = kmalloc(buflen, GFP_ATOMIC);
- if (!tmpbuf)
- return -ENOMEM;
-
- size = buflen;
- devinfo->ctl_urb->transfer_buffer_length = size;
-
- devinfo->ctl_read.wLength = cpu_to_le16p(&size);
- devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR |
- USB_RECIP_INTERFACE;
- devinfo->ctl_read.bRequest = cmd;
-
- usb_fill_control_urb(devinfo->ctl_urb,
- devinfo->usbdev,
- usb_rcvctrlpipe(devinfo->usbdev, 0),
- (unsigned char *) &devinfo->ctl_read,
- (void *) tmpbuf, size,
- (usb_complete_t)brcmf_usb_sync_complete, devinfo);
-
- devinfo->ctl_completed = false;
- ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
- if (ret < 0) {
- brcmf_err("usb_submit_urb failed %d\n", ret);
- goto finalize;
- }
-
- if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
- usb_kill_urb(devinfo->ctl_urb);
- ret = -ETIMEDOUT;
- } else {
- memcpy(buffer, tmpbuf, buflen);
- }
-
-finalize:
- kfree(tmpbuf);
- return ret;
-}
-
-static bool
-brcmf_usb_dlneeded(struct brcmf_usbdev_info *devinfo)
-{
- struct bootrom_id_le id;
- u32 chipid, chiprev;
-
- brcmf_dbg(USB, "Enter\n");
-
- if (devinfo == NULL)
- return false;
-
- /* Check if firmware downloaded already by querying runtime ID */
- id.chip = cpu_to_le32(0xDEAD);
- brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
-
- chipid = le32_to_cpu(id.chip);
- chiprev = le32_to_cpu(id.chiprev);
-
- if ((chipid & 0x4300) == 0x4300)
- brcmf_dbg(USB, "chip %x rev 0x%x\n", chipid, chiprev);
- else
- brcmf_dbg(USB, "chip %d rev 0x%x\n", chipid, chiprev);
- if (chipid == BRCMF_POSTBOOT_ID) {
- brcmf_dbg(USB, "firmware already downloaded\n");
- brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id));
- return false;
- } else {
- devinfo->bus_pub.devid = chipid;
- devinfo->bus_pub.chiprev = chiprev;
- }
- return true;
-}
-
-static int
-brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
-{
- struct bootrom_id_le id;
- u32 loop_cnt;
- int err;
-
- brcmf_dbg(USB, "Enter\n");
-
- loop_cnt = 0;
- do {
- mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT);
- loop_cnt++;
- id.chip = cpu_to_le32(0xDEAD); /* Get the ID */
- err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
- if ((err) && (err != -ETIMEDOUT))
- return err;
- if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID))
- break;
- } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT);
-
- if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) {
- brcmf_dbg(USB, "postboot chip 0x%x/rev 0x%x\n",
- le32_to_cpu(id.chip), le32_to_cpu(id.chiprev));
-
- brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id));
- return 0;
- } else {
- brcmf_err("Cannot talk to Dongle. Firmware is not UP, %d ms\n",
- BRCMF_USB_RESET_GETVER_SPINWAIT * loop_cnt);
- return -EINVAL;
- }
-}
-
-
-static int
-brcmf_usb_dl_send_bulk(struct brcmf_usbdev_info *devinfo, void *buffer, int len)
-{
- int ret;
-
- if ((devinfo == NULL) || (devinfo->bulk_urb == NULL))
- return -EINVAL;
-
- /* Prepare the URB */
- usb_fill_bulk_urb(devinfo->bulk_urb, devinfo->usbdev,
- devinfo->tx_pipe, buffer, len,
- (usb_complete_t)brcmf_usb_sync_complete, devinfo);
-
- devinfo->bulk_urb->transfer_flags |= URB_ZERO_PACKET;
-
- devinfo->ctl_completed = false;
- ret = usb_submit_urb(devinfo->bulk_urb, GFP_ATOMIC);
- if (ret) {
- brcmf_err("usb_submit_urb failed %d\n", ret);
- return ret;
- }
- ret = brcmf_usb_ioctl_resp_wait(devinfo);
- return (ret == 0);
-}
-
-static int
-brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
-{
- unsigned int sendlen, sent, dllen;
- char *bulkchunk = NULL, *dlpos;
- struct rdl_state_le state;
- u32 rdlstate, rdlbytes;
- int err = 0;
-
- brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen);
-
- bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC);
- if (bulkchunk == NULL) {
- err = -ENOMEM;
- goto fail;
- }
-
- /* 1) Prepare USB boot loader for runtime image */
- brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
-
- rdlstate = le32_to_cpu(state.state);
- rdlbytes = le32_to_cpu(state.bytes);
-
- /* 2) Check we are in the Waiting state */
- if (rdlstate != DL_WAITING) {
- brcmf_err("Failed to DL_START\n");
- err = -EINVAL;
- goto fail;
- }
- sent = 0;
- dlpos = fw;
- dllen = fwlen;
-
- /* Get chip id and rev */
- while (rdlbytes != dllen) {
- /* Wait until the usb device reports it received all
- * the bytes we sent */
- if ((rdlbytes == sent) && (rdlbytes != dllen)) {
- if ((dllen-sent) < TRX_RDL_CHUNK)
- sendlen = dllen-sent;
- else
- sendlen = TRX_RDL_CHUNK;
-
- /* simply avoid having to send a ZLP by ensuring we
- * never have an even
- * multiple of 64
- */
- if (!(sendlen % 64))
- sendlen -= 4;
-
- /* send data */
- memcpy(bulkchunk, dlpos, sendlen);
- if (brcmf_usb_dl_send_bulk(devinfo, bulkchunk,
- sendlen)) {
- brcmf_err("send_bulk failed\n");
- err = -EINVAL;
- goto fail;
- }
-
- dlpos += sendlen;
- sent += sendlen;
- }
- err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
- sizeof(state));
- if (err) {
- brcmf_err("DL_GETSTATE Failed\n");
- goto fail;
- }
-
- rdlstate = le32_to_cpu(state.state);
- rdlbytes = le32_to_cpu(state.bytes);
-
- /* restart if an error is reported */
- if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) {
- brcmf_err("Bad Hdr or Bad CRC state %d\n",
- rdlstate);
- err = -EINVAL;
- goto fail;
- }
- }
-
-fail:
- kfree(bulkchunk);
- brcmf_dbg(USB, "Exit, err=%d\n", err);
- return err;
-}
-
-static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len)
-{
- int err;
-
- brcmf_dbg(USB, "Enter\n");
-
- if (devinfo == NULL)
- return -EINVAL;
-
- if (devinfo->bus_pub.devid == 0xDEAD)
- return -EINVAL;
-
- err = brcmf_usb_dl_writeimage(devinfo, fw, len);
- if (err == 0)
- devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE;
- else
- devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_FAIL;
- brcmf_dbg(USB, "Exit, err=%d\n", err);
-
- return err;
-}
-
-static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
-{
- struct rdl_state_le state;
-
- brcmf_dbg(USB, "Enter\n");
- if (!devinfo)
- return -EINVAL;
-
- if (devinfo->bus_pub.devid == 0xDEAD)
- return -EINVAL;
-
- /* Check we are runnable */
- state.state = 0;
- brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state));
-
- /* Start the image */
- if (state.state == cpu_to_le32(DL_RUNNABLE)) {
- if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state)))
- return -ENODEV;
- if (brcmf_usb_resetcfg(devinfo))
- return -ENODEV;
- /* The Dongle may go for re-enumeration. */
- } else {
- brcmf_err("Dongle not runnable\n");
- return -EINVAL;
- }
- brcmf_dbg(USB, "Exit\n");
- return 0;
-}
-
-static bool brcmf_usb_chip_support(int chipid, int chiprev)
-{
- switch(chipid) {
- case BRCM_CC_43143_CHIP_ID:
- return true;
- case BRCM_CC_43235_CHIP_ID:
- case BRCM_CC_43236_CHIP_ID:
- case BRCM_CC_43238_CHIP_ID:
- return (chiprev == 3);
- case BRCM_CC_43242_CHIP_ID:
- return true;
- case BRCM_CC_43566_CHIP_ID:
- case BRCM_CC_43569_CHIP_ID:
- return true;
- default:
- break;
- }
- return false;
-}
-
-static int
-brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
-{
- int devid, chiprev;
- int err;
-
- brcmf_dbg(USB, "Enter\n");
- if (devinfo == NULL)
- return -ENODEV;
-
- devid = devinfo->bus_pub.devid;
- chiprev = devinfo->bus_pub.chiprev;
-
- if (!brcmf_usb_chip_support(devid, chiprev)) {
- brcmf_err("unsupported chip %d rev %d\n",
- devid, chiprev);
- return -EINVAL;
- }
-
- if (!devinfo->image) {
- brcmf_err("No firmware!\n");
- return -ENOENT;
- }
-
- err = brcmf_usb_dlstart(devinfo,
- (u8 *)devinfo->image, devinfo->image_len);
- if (err == 0)
- err = brcmf_usb_dlrun(devinfo);
- return err;
-}
-
-
-static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo)
-{
- brcmf_dbg(USB, "Enter, devinfo %p\n", devinfo);
-
- /* free the URBS */
- brcmf_usb_free_q(&devinfo->rx_freeq, false);
- brcmf_usb_free_q(&devinfo->tx_freeq, false);
-
- usb_free_urb(devinfo->ctl_urb);
- usb_free_urb(devinfo->bulk_urb);
-
- kfree(devinfo->tx_reqs);
- kfree(devinfo->rx_reqs);
-}
-
-
-static int check_file(const u8 *headers)
-{
- struct trx_header_le *trx;
- int actual_len = -1;
-
- brcmf_dbg(USB, "Enter\n");
- /* Extract trx header */
- trx = (struct trx_header_le *) headers;
- if (trx->magic != cpu_to_le32(TRX_MAGIC))
- return -1;
-
- headers += sizeof(struct trx_header_le);
-
- if (le32_to_cpu(trx->flag_version) & TRX_UNCOMP_IMAGE) {
- actual_len = le32_to_cpu(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]);
- return actual_len + sizeof(struct trx_header_le);
- }
- return -1;
-}
-
-static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
-{
- switch (devinfo->bus_pub.devid) {
- case BRCM_CC_43143_CHIP_ID:
- return BRCMF_USB_43143_FW_NAME;
- case BRCM_CC_43235_CHIP_ID:
- case BRCM_CC_43236_CHIP_ID:
- case BRCM_CC_43238_CHIP_ID:
- return BRCMF_USB_43236_FW_NAME;
- case BRCM_CC_43242_CHIP_ID:
- return BRCMF_USB_43242_FW_NAME;
- case BRCM_CC_43566_CHIP_ID:
- case BRCM_CC_43569_CHIP_ID:
- return BRCMF_USB_43569_FW_NAME;
- default:
- return NULL;
- }
-}
-
-
-static
-struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
- int nrxq, int ntxq)
-{
- brcmf_dbg(USB, "Enter\n");
-
- devinfo->bus_pub.nrxq = nrxq;
- devinfo->rx_low_watermark = nrxq / 2;
- devinfo->bus_pub.devinfo = devinfo;
- devinfo->bus_pub.ntxq = ntxq;
- devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN;
-
- /* flow control when too many tx urbs posted */
- devinfo->tx_low_watermark = ntxq / 4;
- devinfo->tx_high_watermark = devinfo->tx_low_watermark * 3;
- devinfo->bus_pub.bus_mtu = BRCMF_USB_MAX_PKT_SIZE;
-
- /* Initialize other structure content */
- init_waitqueue_head(&devinfo->ioctl_resp_wait);
-
- /* 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);
-
- INIT_LIST_HEAD(&devinfo->tx_freeq);
- INIT_LIST_HEAD(&devinfo->tx_postq);
-
- devinfo->tx_flowblock = false;
-
- devinfo->rx_reqs = brcmf_usbdev_qinit(&devinfo->rx_freeq, nrxq);
- if (!devinfo->rx_reqs)
- goto error;
-
- devinfo->tx_reqs = brcmf_usbdev_qinit(&devinfo->tx_freeq, ntxq);
- if (!devinfo->tx_reqs)
- goto error;
- devinfo->tx_freecount = ntxq;
-
- devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!devinfo->ctl_urb) {
- brcmf_err("usb_alloc_urb (ctl) failed\n");
- goto error;
- }
- devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!devinfo->bulk_urb) {
- brcmf_err("usb_alloc_urb (bulk) failed\n");
- goto error;
- }
-
- return &devinfo->bus_pub;
-
-error:
- brcmf_err("failed!\n");
- brcmf_usb_detach(devinfo);
- return NULL;
-}
-
-static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
-{
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
-
- brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled);
- devinfo->wowl_enabled = enabled;
- if (enabled)
- device_set_wakeup_enable(devinfo->dev, true);
- else
- device_set_wakeup_enable(devinfo->dev, false);
-}
-
-static struct brcmf_bus_ops brcmf_usb_bus_ops = {
- .txdata = brcmf_usb_tx,
- .stop = brcmf_usb_down,
- .txctl = brcmf_usb_tx_ctlpkt,
- .rxctl = brcmf_usb_rx_ctlpkt,
- .wowl_config = brcmf_usb_wowl_config,
-};
-
-static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
-{
- int ret;
-
- /* Attach to the common driver interface */
- ret = brcmf_attach(devinfo->dev);
- if (ret) {
- brcmf_err("brcmf_attach failed\n");
- return ret;
- }
-
- ret = brcmf_usb_up(devinfo->dev);
- if (ret)
- goto fail;
-
- ret = brcmf_bus_start(devinfo->dev);
- if (ret)
- goto fail;
-
- return 0;
-fail:
- brcmf_detach(devinfo->dev);
- return ret;
-}
-
-static void brcmf_usb_probe_phase2(struct device *dev,
- const struct firmware *fw,
- void *nvram, u32 nvlen)
-{
- struct brcmf_bus *bus = dev_get_drvdata(dev);
- struct brcmf_usbdev_info *devinfo;
- int ret;
-
- brcmf_dbg(USB, "Start fw downloading\n");
-
- devinfo = bus->bus_priv.usb->devinfo;
- ret = check_file(fw->data);
- if (ret < 0) {
- brcmf_err("invalid firmware\n");
- release_firmware(fw);
- goto error;
- }
-
- devinfo->image = fw->data;
- devinfo->image_len = fw->size;
-
- ret = brcmf_usb_fw_download(devinfo);
- release_firmware(fw);
- if (ret)
- goto error;
-
- ret = brcmf_usb_bus_setup(devinfo);
- if (ret)
- goto error;
-
- mutex_unlock(&devinfo->dev_init_lock);
- return;
-error:
- brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
- mutex_unlock(&devinfo->dev_init_lock);
- device_release_driver(dev);
-}
-
-static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
-{
- struct brcmf_bus *bus = NULL;
- struct brcmf_usbdev *bus_pub = NULL;
- struct device *dev = devinfo->dev;
- int ret;
-
- brcmf_dbg(USB, "Enter\n");
- bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ);
- if (!bus_pub)
- return -ENODEV;
-
- bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC);
- if (!bus) {
- ret = -ENOMEM;
- goto fail;
- }
-
- bus->dev = dev;
- bus_pub->bus = bus;
- bus->bus_priv.usb = bus_pub;
- dev_set_drvdata(dev, bus);
- bus->ops = &brcmf_usb_bus_ops;
- bus->proto_type = BRCMF_PROTO_BCDC;
- bus->always_use_fws_queue = true;
-#ifdef CONFIG_PM
- bus->wowl_supported = true;
-#endif
-
- if (!brcmf_usb_dlneeded(devinfo)) {
- ret = brcmf_usb_bus_setup(devinfo);
- if (ret)
- goto fail;
- /* we are done */
- mutex_unlock(&devinfo->dev_init_lock);
- return 0;
- }
- bus->chip = bus_pub->devid;
- bus->chiprev = bus_pub->chiprev;
-
- /* request firmware here */
- ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo),
- NULL, brcmf_usb_probe_phase2);
- if (ret) {
- brcmf_err("firmware request failed: %d\n", ret);
- goto fail;
- }
-
- return 0;
-
-fail:
- /* Release resources in reverse order */
- kfree(bus);
- brcmf_usb_detach(devinfo);
- return ret;
-}
-
-static void
-brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
-{
- if (!devinfo)
- return;
- brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo);
-
- brcmf_detach(devinfo->dev);
- kfree(devinfo->bus_pub.bus);
- brcmf_usb_detach(devinfo);
-}
-
-static int
-brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *usb = interface_to_usbdev(intf);
- struct brcmf_usbdev_info *devinfo;
- struct usb_interface_descriptor *desc;
- struct usb_endpoint_descriptor *endpoint;
- int ret = 0;
- u32 num_of_eps;
- u8 endpoint_num, ep;
-
- brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct);
-
- devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC);
- if (devinfo == NULL)
- return -ENOMEM;
-
- devinfo->usbdev = usb;
- devinfo->dev = &usb->dev;
- /* Take an init lock, to protect for disconnect while still loading.
- * Necessary because of the asynchronous firmware load construction
- */
- mutex_init(&devinfo->dev_init_lock);
- mutex_lock(&devinfo->dev_init_lock);
-
- usb_set_intfdata(intf, devinfo);
-
- /* Check that the device supports only one configuration */
- if (usb->descriptor.bNumConfigurations != 1) {
- brcmf_err("Number of configurations: %d not supported\n",
- usb->descriptor.bNumConfigurations);
- ret = -ENODEV;
- goto fail;
- }
-
- if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) &&
- (usb->descriptor.bDeviceClass != USB_CLASS_MISC) &&
- (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) {
- brcmf_err("Device class: 0x%x not supported\n",
- usb->descriptor.bDeviceClass);
- ret = -ENODEV;
- goto fail;
- }
-
- desc = &intf->altsetting[0].desc;
- if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
- (desc->bInterfaceSubClass != 2) ||
- (desc->bInterfaceProtocol != 0xff)) {
- brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
- desc->bInterfaceNumber, desc->bInterfaceClass,
- desc->bInterfaceSubClass, desc->bInterfaceProtocol);
- ret = -ENODEV;
- goto fail;
- }
-
- num_of_eps = desc->bNumEndpoints;
- for (ep = 0; ep < num_of_eps; ep++) {
- endpoint = &intf->altsetting[0].endpoint[ep].desc;
- endpoint_num = usb_endpoint_num(endpoint);
- if (!usb_endpoint_xfer_bulk(endpoint))
- continue;
- if (usb_endpoint_dir_in(endpoint)) {
- if (!devinfo->rx_pipe)
- devinfo->rx_pipe =
- usb_rcvbulkpipe(usb, endpoint_num);
- } else {
- if (!devinfo->tx_pipe)
- devinfo->tx_pipe =
- usb_sndbulkpipe(usb, endpoint_num);
- }
- }
- if (devinfo->rx_pipe == 0) {
- brcmf_err("No RX (in) Bulk EP found\n");
- ret = -ENODEV;
- goto fail;
- }
- if (devinfo->tx_pipe == 0) {
- brcmf_err("No TX (out) Bulk EP found\n");
- ret = -ENODEV;
- goto fail;
- }
-
- devinfo->ifnum = desc->bInterfaceNumber;
-
- if (usb->speed == USB_SPEED_SUPER)
- brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n");
- else if (usb->speed == USB_SPEED_HIGH)
- brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n");
- else
- brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n");
-
- ret = brcmf_usb_probe_cb(devinfo);
- if (ret)
- goto fail;
-
- /* Success */
- return 0;
-
-fail:
- mutex_unlock(&devinfo->dev_init_lock);
- kfree(devinfo);
- usb_set_intfdata(intf, NULL);
- return ret;
-}
-
-static void
-brcmf_usb_disconnect(struct usb_interface *intf)
-{
- struct brcmf_usbdev_info *devinfo;
-
- brcmf_dbg(USB, "Enter\n");
- devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
-
- if (devinfo) {
- mutex_lock(&devinfo->dev_init_lock);
- /* Make sure that devinfo still exists. Firmware probe routines
- * may have released the device and cleared the intfdata.
- */
- if (!usb_get_intfdata(intf))
- goto done;
-
- brcmf_usb_disconnect_cb(devinfo);
- kfree(devinfo);
- }
-done:
- brcmf_dbg(USB, "Exit\n");
-}
-
-/*
- * only need to signal the bus being down and update the state.
- */
-static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
-{
- struct usb_device *usb = interface_to_usbdev(intf);
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
-
- brcmf_dbg(USB, "Enter\n");
- devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
- if (devinfo->wowl_enabled)
- brcmf_cancel_all_urbs(devinfo);
- else
- brcmf_detach(&usb->dev);
- return 0;
-}
-
-/*
- * (re-) start the bus.
- */
-static int brcmf_usb_resume(struct usb_interface *intf)
-{
- struct usb_device *usb = interface_to_usbdev(intf);
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
-
- brcmf_dbg(USB, "Enter\n");
- if (!devinfo->wowl_enabled)
- return brcmf_usb_bus_setup(devinfo);
-
- devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP;
- brcmf_usb_rx_fill_all(devinfo);
- return 0;
-}
-
-static int brcmf_usb_reset_resume(struct usb_interface *intf)
-{
- struct usb_device *usb = interface_to_usbdev(intf);
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
-
- brcmf_dbg(USB, "Enter\n");
-
- return brcmf_fw_get_firmwares(&usb->dev, 0,
- brcmf_usb_get_fwname(devinfo), NULL,
- brcmf_usb_probe_phase2);
-}
-
-#define BRCMF_USB_DEVICE(dev_id) \
- { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) }
-
-static struct usb_device_id brcmf_usb_devid_table[] = {
- BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID),
- BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID),
- BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID),
- BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID),
- /* special entry for device with firmware loaded and running */
- BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
- { /* end: all zeroes */ }
-};
-
-MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
-MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
-MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
-MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);
-MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME);
-
-static struct usb_driver brcmf_usbdrvr = {
- .name = KBUILD_MODNAME,
- .probe = brcmf_usb_probe,
- .disconnect = brcmf_usb_disconnect,
- .id_table = brcmf_usb_devid_table,
- .suspend = brcmf_usb_suspend,
- .resume = brcmf_usb_resume,
- .reset_resume = brcmf_usb_reset_resume,
- .supports_autosuspend = 1,
- .disable_hub_initiated_lpm = 1,
-};
-
-static int brcmf_usb_reset_device(struct device *dev, void *notused)
-{
- /* device past is the usb interface so we
- * need to use parent here.
- */
- brcmf_dev_reset(dev->parent);
- return 0;
-}
-
-void brcmf_usb_exit(void)
-{
- struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
- int ret;
-
- brcmf_dbg(USB, "Enter\n");
- ret = driver_for_each_device(drv, NULL, NULL,
- brcmf_usb_reset_device);
- usb_deregister(&brcmf_usbdrvr);
-}
-
-void brcmf_usb_register(void)
-{
- brcmf_dbg(USB, "Enter\n");
- usb_register(&brcmf_usbdrvr);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/brcm80211/brcmsmac/Makefile
deleted file mode 100644
index 32464acccd90..000000000000
--- a/drivers/net/wireless/brcm80211/brcmsmac/Makefile
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Makefile fragment for Broadcom 802.11n Networking Device Driver
-#
-# Copyright (c) 2010 Broadcom Corporation
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-ccflags-y := \
- -D__CHECK_ENDIAN__ \
- -Idrivers/net/wireless/brcm80211/brcmsmac \
- -Idrivers/net/wireless/brcm80211/brcmsmac/phy \
- -Idrivers/net/wireless/brcm80211/include
-
-brcmsmac-y := \
- mac80211_if.o \
- ucode_loader.o \
- ampdu.o \
- antsel.o \
- channel.o \
- main.o \
- phy_shim.o \
- pmu.o \
- rate.o \
- stf.o \
- aiutils.o \
- phy/phy_cmn.o \
- phy/phy_lcn.o \
- phy/phy_n.o \
- phy/phytbl_lcn.o \
- phy/phytbl_n.o \
- phy/phy_qmath.o \
- dma.o \
- brcms_trace_events.o \
- debug.o
-
-brcmsmac-$(CONFIG_BCMA_DRIVER_GPIO) += led.o
-
-obj-$(CONFIG_BRCMSMAC) += brcmsmac.o
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
deleted file mode 100644
index 635ae034c7e5..000000000000
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ /dev/null
@@ -1,774 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/types.h>
-#include <net/cfg80211.h>
-#include <net/mac80211.h>
-#include <net/regulatory.h>
-
-#include <defs.h>
-#include "pub.h"
-#include "phy/phy_hal.h"
-#include "main.h"
-#include "stf.h"
-#include "channel.h"
-#include "mac80211_if.h"
-#include "debug.h"
-
-/* QDB() macro takes a dB value and converts to a quarter dB value */
-#define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR)
-
-#define LOCALE_MIMO_IDX_bn 0
-#define LOCALE_MIMO_IDX_11n 0
-
-/* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */
-#define BRCMS_MAXPWR_MIMO_TBL_SIZE 14
-
-/* maxpwr mapping to 5GHz band channels:
- * maxpwr[0] - channels [34-48]
- * maxpwr[1] - channels [52-60]
- * maxpwr[2] - channels [62-64]
- * maxpwr[3] - channels [100-140]
- * maxpwr[4] - channels [149-165]
- */
-#define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */
-
-#define LC(id) LOCALE_MIMO_IDX_ ## id
-
-#define LOCALES(mimo2, mimo5) \
- {LC(mimo2), LC(mimo5)}
-
-/* macro to get 5 GHz channel group index for tx power */
-#define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \
- (((c) < 62) ? 1 : \
- (((c) < 100) ? 2 : \
- (((c) < 149) ? 3 : 4))))
-
-#define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0)
-#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \
- NL80211_RRF_NO_IR)
-
-#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \
- NL80211_RRF_NO_IR)
-#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \
- NL80211_RRF_DFS | \
- NL80211_RRF_NO_IR)
-#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \
- NL80211_RRF_DFS | \
- NL80211_RRF_NO_IR)
-#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \
- NL80211_RRF_NO_IR)
-
-static const struct ieee80211_regdomain brcms_regdom_x2 = {
- .n_reg_rules = 6,
- .alpha2 = "X2",
- .reg_rules = {
- BRCM_2GHZ_2412_2462,
- BRCM_2GHZ_2467_2472,
- BRCM_5GHZ_5180_5240,
- BRCM_5GHZ_5260_5320,
- BRCM_5GHZ_5500_5700,
- BRCM_5GHZ_5745_5825,
- }
-};
-
- /* locale per-channel tx power limits for MIMO frames
- * maxpwr arrays are index by channel for 2.4 GHz limits, and
- * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel)
- */
-struct locale_mimo_info {
- /* tx 20 MHz power limits, qdBm units */
- s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE];
- /* tx 40 MHz power limits, qdBm units */
- s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE];
-};
-
-/* Country names and abbreviations with locale defined from ISO 3166 */
-struct country_info {
- const u8 locale_mimo_2G; /* 2.4G mimo info */
- const u8 locale_mimo_5G; /* 5G mimo info */
-};
-
-struct brcms_regd {
- struct country_info country;
- const struct ieee80211_regdomain *regdomain;
-};
-
-struct brcms_cm_info {
- struct brcms_pub *pub;
- struct brcms_c_info *wlc;
- const struct brcms_regd *world_regd;
-};
-
-/*
- * MIMO Locale Definitions - 2.4 GHz
- */
-static const struct locale_mimo_info locale_bn = {
- {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
- QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
- QDB(13), QDB(13), QDB(13)},
- {0, 0, QDB(13), QDB(13), QDB(13),
- QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
- QDB(13), 0, 0},
-};
-
-static const struct locale_mimo_info *g_mimo_2g_table[] = {
- &locale_bn
-};
-
-/*
- * MIMO Locale Definitions - 5 GHz
- */
-static const struct locale_mimo_info locale_11n = {
- { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)},
- {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)},
-};
-
-static const struct locale_mimo_info *g_mimo_5g_table[] = {
- &locale_11n
-};
-
-static const struct brcms_regd cntry_locales[] = {
- /* Worldwide RoW 2, must always be at index 0 */
- {
- .country = LOCALES(bn, 11n),
- .regdomain = &brcms_regdom_x2,
- },
-};
-
-static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx)
-{
- if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table))
- return NULL;
-
- return g_mimo_2g_table[locale_idx];
-}
-
-static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx)
-{
- if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table))
- return NULL;
-
- return g_mimo_5g_table[locale_idx];
-}
-
-/*
- * Indicates whether the country provided is valid to pass
- * to cfg80211 or not.
- *
- * returns true if valid; false if not.
- */
-static bool brcms_c_country_valid(const char *ccode)
-{
- /*
- * only allow ascii alpha uppercase for the first 2
- * chars.
- */
- if (!((0x80 & ccode[0]) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A &&
- (0x80 & ccode[1]) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A))
- return false;
-
- /*
- * do not match ISO 3166-1 user assigned country codes
- * that may be in the driver table
- */
- if (!strcmp("AA", ccode) || /* AA */
- !strcmp("ZZ", ccode) || /* ZZ */
- ccode[0] == 'X' || /* XA - XZ */
- (ccode[0] == 'Q' && /* QM - QZ */
- (ccode[1] >= 'M' && ccode[1] <= 'Z')))
- return false;
-
- if (!strcmp("NA", ccode))
- return false;
-
- return true;
-}
-
-static const struct brcms_regd *brcms_world_regd(const char *regdom, int len)
-{
- const struct brcms_regd *regd = NULL;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) {
- if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) {
- regd = &cntry_locales[i];
- break;
- }
- }
-
- return regd;
-}
-
-static const struct brcms_regd *brcms_default_world_regd(void)
-{
- return &cntry_locales[0];
-}
-
-/* JP, J1 - J10 are Japan ccodes */
-static bool brcms_c_japan_ccode(const char *ccode)
-{
- return (ccode[0] == 'J' &&
- (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
-}
-
-static void
-brcms_c_channel_min_txpower_limits_with_local_constraint(
- struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr,
- u8 local_constraint_qdbm)
-{
- int j;
-
- /* CCK Rates */
- for (j = 0; j < WL_TX_POWER_CCK_NUM; j++)
- txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm);
-
- /* 20 MHz Legacy OFDM SISO */
- for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++)
- txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm);
-
- /* 20 MHz Legacy OFDM CDD */
- for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
- txpwr->ofdm_cdd[j] =
- min(txpwr->ofdm_cdd[j], local_constraint_qdbm);
-
- /* 40 MHz Legacy OFDM SISO */
- for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
- txpwr->ofdm_40_siso[j] =
- min(txpwr->ofdm_40_siso[j], local_constraint_qdbm);
-
- /* 40 MHz Legacy OFDM CDD */
- for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
- txpwr->ofdm_40_cdd[j] =
- min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm);
-
- /* 20MHz MCS 0-7 SISO */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
- txpwr->mcs_20_siso[j] =
- min(txpwr->mcs_20_siso[j], local_constraint_qdbm);
-
- /* 20MHz MCS 0-7 CDD */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
- txpwr->mcs_20_cdd[j] =
- min(txpwr->mcs_20_cdd[j], local_constraint_qdbm);
-
- /* 20MHz MCS 0-7 STBC */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
- txpwr->mcs_20_stbc[j] =
- min(txpwr->mcs_20_stbc[j], local_constraint_qdbm);
-
- /* 20MHz MCS 8-15 MIMO */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
- txpwr->mcs_20_mimo[j] =
- min(txpwr->mcs_20_mimo[j], local_constraint_qdbm);
-
- /* 40MHz MCS 0-7 SISO */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
- txpwr->mcs_40_siso[j] =
- min(txpwr->mcs_40_siso[j], local_constraint_qdbm);
-
- /* 40MHz MCS 0-7 CDD */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
- txpwr->mcs_40_cdd[j] =
- min(txpwr->mcs_40_cdd[j], local_constraint_qdbm);
-
- /* 40MHz MCS 0-7 STBC */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
- txpwr->mcs_40_stbc[j] =
- min(txpwr->mcs_40_stbc[j], local_constraint_qdbm);
-
- /* 40MHz MCS 8-15 MIMO */
- for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
- txpwr->mcs_40_mimo[j] =
- min(txpwr->mcs_40_mimo[j], local_constraint_qdbm);
-
- /* 40MHz MCS 32 */
- txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm);
-
-}
-
-/*
- * set the driver's current country and regulatory information
- * using a country code as the source. Look up built in country
- * information found with the country code.
- */
-static void
-brcms_c_set_country(struct brcms_cm_info *wlc_cm,
- const struct brcms_regd *regd)
-{
- struct brcms_c_info *wlc = wlc_cm->wlc;
-
- if ((wlc->pub->_n_enab & SUPPORT_11N) !=
- wlc->protection->nmode_user)
- brcms_c_set_nmode(wlc);
-
- brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
- brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
-
- brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
-
- return;
-}
-
-struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc)
-{
- struct brcms_cm_info *wlc_cm;
- struct brcms_pub *pub = wlc->pub;
- struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom;
- const char *ccode = sprom->alpha2;
- int ccode_len = sizeof(sprom->alpha2);
-
- wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC);
- if (wlc_cm == NULL)
- return NULL;
- wlc_cm->pub = pub;
- wlc_cm->wlc = wlc;
- wlc->cmi = wlc_cm;
-
- /* store the country code for passing up as a regulatory hint */
- wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len);
- if (brcms_c_country_valid(ccode))
- strncpy(wlc->pub->srom_ccode, ccode, ccode_len);
-
- /*
- * If no custom world domain is found in the SROM, use the
- * default "X2" domain.
- */
- if (!wlc_cm->world_regd) {
- wlc_cm->world_regd = brcms_default_world_regd();
- ccode = wlc_cm->world_regd->regdomain->alpha2;
- ccode_len = BRCM_CNTRY_BUF_SZ - 1;
- }
-
- /* save default country for exiting 11d regulatory mode */
- strncpy(wlc->country_default, ccode, ccode_len);
-
- /* initialize autocountry_default to driver default */
- strncpy(wlc->autocountry_default, ccode, ccode_len);
-
- brcms_c_set_country(wlc_cm, wlc_cm->world_regd);
-
- return wlc_cm;
-}
-
-void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm)
-{
- kfree(wlc_cm);
-}
-
-void
-brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
- u8 local_constraint_qdbm)
-{
- struct brcms_c_info *wlc = wlc_cm->wlc;
- struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan;
- struct txpwr_limits txpwr;
-
- brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
-
- brcms_c_channel_min_txpower_limits_with_local_constraint(
- wlc_cm, &txpwr, local_constraint_qdbm
- );
-
- /* set or restore gmode as required by regulatory */
- if (ch->flags & IEEE80211_CHAN_NO_OFDM)
- brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
- else
- brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
-
- brcms_b_set_chanspec(wlc->hw, chanspec,
- !!(ch->flags & IEEE80211_CHAN_NO_IR),
- &txpwr);
-}
-
-void
-brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec,
- struct txpwr_limits *txpwr)
-{
- struct brcms_c_info *wlc = wlc_cm->wlc;
- struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan;
- uint i;
- uint chan;
- int maxpwr;
- int delta;
- const struct country_info *country;
- struct brcms_band *band;
- int conducted_max = BRCMS_TXPWR_MAX;
- const struct locale_mimo_info *li_mimo;
- int maxpwr20, maxpwr40;
- int maxpwr_idx;
- uint j;
-
- memset(txpwr, 0, sizeof(struct txpwr_limits));
-
- if (WARN_ON(!ch))
- return;
-
- country = &wlc_cm->world_regd->country;
-
- chan = CHSPEC_CHANNEL(chanspec);
- band = wlc->bandstate[chspec_bandunit(chanspec)];
- li_mimo = (band->bandtype == BRCM_BAND_5G) ?
- brcms_c_get_mimo_5g(country->locale_mimo_5G) :
- brcms_c_get_mimo_2g(country->locale_mimo_2G);
-
- delta = band->antgain;
-
- if (band->bandtype == BRCM_BAND_2G)
- conducted_max = QDB(22);
-
- maxpwr = QDB(ch->max_power) - delta;
- maxpwr = max(maxpwr, 0);
- maxpwr = min(maxpwr, conducted_max);
-
- /* CCK txpwr limits for 2.4G band */
- if (band->bandtype == BRCM_BAND_2G) {
- for (i = 0; i < BRCMS_NUM_RATES_CCK; i++)
- txpwr->cck[i] = (u8) maxpwr;
- }
-
- for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
- txpwr->ofdm[i] = (u8) maxpwr;
-
- /*
- * OFDM 40 MHz SISO has the same power as the corresponding
- * MCS0-7 rate unless overriden by the locale specific code.
- * We set this value to 0 as a flag (presumably 0 dBm isn't
- * a possibility) and then copy the MCS0-7 value to the 40 MHz
- * value if it wasn't explicitly set.
- */
- txpwr->ofdm_40_siso[i] = 0;
-
- txpwr->ofdm_cdd[i] = (u8) maxpwr;
-
- txpwr->ofdm_40_cdd[i] = 0;
- }
-
- delta = 0;
- if (band->antgain > QDB(6))
- delta = band->antgain - QDB(6); /* Excess over 6 dB */
-
- if (band->bandtype == BRCM_BAND_2G)
- maxpwr_idx = (chan - 1);
- else
- maxpwr_idx = CHANNEL_POWER_IDX_5G(chan);
-
- maxpwr20 = li_mimo->maxpwr20[maxpwr_idx];
- maxpwr40 = li_mimo->maxpwr40[maxpwr_idx];
-
- maxpwr20 = maxpwr20 - delta;
- maxpwr20 = max(maxpwr20, 0);
- maxpwr40 = maxpwr40 - delta;
- maxpwr40 = max(maxpwr40, 0);
-
- /* Fill in the MCS 0-7 (SISO) rates */
- for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
-
- /*
- * 20 MHz has the same power as the corresponding OFDM rate
- * unless overriden by the locale specific code.
- */
- txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
- txpwr->mcs_40_siso[i] = 0;
- }
-
- /* Fill in the MCS 0-7 CDD rates */
- for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
- txpwr->mcs_20_cdd[i] = (u8) maxpwr20;
- txpwr->mcs_40_cdd[i] = (u8) maxpwr40;
- }
-
- /*
- * These locales have SISO expressed in the
- * table and override CDD later
- */
- if (li_mimo == &locale_bn) {
- if (li_mimo == &locale_bn) {
- maxpwr20 = QDB(16);
- maxpwr40 = 0;
-
- if (chan >= 3 && chan <= 11)
- maxpwr40 = QDB(16);
- }
-
- for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
- txpwr->mcs_20_siso[i] = (u8) maxpwr20;
- txpwr->mcs_40_siso[i] = (u8) maxpwr40;
- }
- }
-
- /* Fill in the MCS 0-7 STBC rates */
- for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
- txpwr->mcs_20_stbc[i] = 0;
- txpwr->mcs_40_stbc[i] = 0;
- }
-
- /* Fill in the MCS 8-15 SDM rates */
- for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) {
- txpwr->mcs_20_mimo[i] = (u8) maxpwr20;
- txpwr->mcs_40_mimo[i] = (u8) maxpwr40;
- }
-
- /* Fill in MCS32 */
- txpwr->mcs32 = (u8) maxpwr40;
-
- for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
- if (txpwr->ofdm_40_cdd[i] == 0)
- txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
- if (i == 0) {
- i = i + 1;
- if (txpwr->ofdm_40_cdd[i] == 0)
- txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
- }
- }
-
- /*
- * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO
- * value if it wasn't provided explicitly.
- */
- for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
- if (txpwr->mcs_40_siso[i] == 0)
- txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i];
- }
-
- for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
- if (txpwr->ofdm_40_siso[i] == 0)
- txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
- if (i == 0) {
- i = i + 1;
- if (txpwr->ofdm_40_siso[i] == 0)
- txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
- }
- }
-
- /*
- * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding
- * STBC values if they weren't provided explicitly.
- */
- for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
- if (txpwr->mcs_20_stbc[i] == 0)
- txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i];
-
- if (txpwr->mcs_40_stbc[i] == 0)
- txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i];
- }
-
- return;
-}
-
-/*
- * Verify the chanspec is using a legal set of parameters, i.e. that the
- * chanspec specified a band, bw, ctl_sb and channel and that the
- * combination could be legal given any set of circumstances.
- * RETURNS: true is the chanspec is malformed, false if it looks good.
- */
-static bool brcms_c_chspec_malformed(u16 chanspec)
-{
- /* must be 2G or 5G band */
- if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))
- return true;
- /* must be 20 or 40 bandwidth */
- if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))
- return true;
-
- /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */
- if (CHSPEC_IS20(chanspec)) {
- if (!CHSPEC_SB_NONE(chanspec))
- return true;
- } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) {
- return true;
- }
-
- return false;
-}
-
-/*
- * Validate the chanspec for this locale, for 40MHZ we need to also
- * check that the sidebands are valid 20MZH channels in this locale
- * and they are also a legal HT combination
- */
-static bool
-brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec)
-{
- struct brcms_c_info *wlc = wlc_cm->wlc;
- u8 channel = CHSPEC_CHANNEL(chspec);
-
- /* check the chanspec */
- if (brcms_c_chspec_malformed(chspec)) {
- brcms_err(wlc->hw->d11core, "wl%d: malformed chanspec 0x%x\n",
- wlc->pub->unit, chspec);
- return false;
- }
-
- if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) !=
- chspec_bandunit(chspec))
- return false;
-
- return true;
-}
-
-bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec)
-{
- return brcms_c_valid_chanspec_ext(wlc_cm, chspec);
-}
-
-static bool brcms_is_radar_freq(u16 center_freq)
-{
- return center_freq >= 5260 && center_freq <= 5700;
-}
-
-static void brcms_reg_apply_radar_flags(struct wiphy *wiphy)
-{
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch;
- int i;
-
- sband = wiphy->bands[IEEE80211_BAND_5GHZ];
- if (!sband)
- return;
-
- for (i = 0; i < sband->n_channels; i++) {
- ch = &sband->channels[i];
-
- if (!brcms_is_radar_freq(ch->center_freq))
- continue;
-
- /*
- * All channels in this range should be passive and have
- * DFS enabled.
- */
- if (!(ch->flags & IEEE80211_CHAN_DISABLED))
- ch->flags |= IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_NO_IR |
- IEEE80211_CHAN_NO_IR;
- }
-}
-
-static void
-brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
- enum nl80211_reg_initiator initiator)
-{
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch;
- const struct ieee80211_reg_rule *rule;
- int band, i;
-
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- sband = wiphy->bands[band];
- if (!sband)
- continue;
-
- for (i = 0; i < sband->n_channels; i++) {
- ch = &sband->channels[i];
-
- if (ch->flags &
- (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR))
- continue;
-
- if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- rule = freq_reg_info(wiphy,
- MHZ_TO_KHZ(ch->center_freq));
- if (IS_ERR(rule))
- continue;
-
- if (!(rule->flags & NL80211_RRF_NO_IR))
- ch->flags &= ~IEEE80211_CHAN_NO_IR;
- } else if (ch->beacon_found) {
- ch->flags &= ~IEEE80211_CHAN_NO_IR;
- }
- }
- }
-}
-
-static void brcms_reg_notifier(struct wiphy *wiphy,
- struct regulatory_request *request)
-{
- struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
- struct brcms_info *wl = hw->priv;
- struct brcms_c_info *wlc = wl->wlc;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch;
- int band, i;
- bool ch_found = false;
-
- brcms_reg_apply_radar_flags(wiphy);
-
- if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
- brcms_reg_apply_beaconing_flags(wiphy, request->initiator);
-
- /* Disable radio if all channels disallowed by regulatory */
- for (band = 0; !ch_found && band < IEEE80211_NUM_BANDS; band++) {
- sband = wiphy->bands[band];
- if (!sband)
- continue;
-
- for (i = 0; !ch_found && i < sband->n_channels; i++) {
- ch = &sband->channels[i];
-
- if (!(ch->flags & IEEE80211_CHAN_DISABLED))
- ch_found = true;
- }
- }
-
- if (ch_found) {
- mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
- } else {
- mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
- brcms_err(wlc->hw->d11core,
- "wl%d: %s: no valid channel for \"%s\"\n",
- wlc->pub->unit, __func__, request->alpha2);
- }
-
- if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G)
- wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
- brcms_c_japan_ccode(request->alpha2));
-}
-
-void brcms_c_regd_init(struct brcms_c_info *wlc)
-{
- struct wiphy *wiphy = wlc->wiphy;
- const struct brcms_regd *regd = wlc->cmi->world_regd;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch;
- struct brcms_chanvec sup_chan;
- struct brcms_band *band;
- int band_idx, i;
-
- /* Disable any channels not supported by the phy */
- for (band_idx = 0; band_idx < wlc->pub->_nbands; band_idx++) {
- band = wlc->bandstate[band_idx];
-
- wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
- &sup_chan);
-
- if (band_idx == BAND_2G_INDEX)
- sband = wiphy->bands[IEEE80211_BAND_2GHZ];
- else
- sband = wiphy->bands[IEEE80211_BAND_5GHZ];
-
- for (i = 0; i < sband->n_channels; i++) {
- ch = &sband->channels[i];
- if (!isset(sup_chan.vec, ch->hw_value))
- ch->flags |= IEEE80211_CHAN_DISABLED;
- }
- }
-
- wlc->wiphy->reg_notifier = brcms_reg_notifier;
- wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
- REGULATORY_STRICT_REG;
- wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain);
- brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmutil/Makefile b/drivers/net/wireless/brcm80211/brcmutil/Makefile
deleted file mode 100644
index 8a928184016a..000000000000
--- a/drivers/net/wireless/brcm80211/brcmutil/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Makefile fragment for Broadcom 802.11n Networking Device Driver Utilities
-#
-# Copyright (c) 2011 Broadcom Corporation
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-ccflags-y := \
- -Idrivers/net/wireless/brcm80211/brcmutil \
- -Idrivers/net/wireless/brcm80211/include
-
-obj-$(CONFIG_BRCMUTIL) += brcmutil.o
-brcmutil-objs = utils.o d11.o
diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
deleted file mode 100644
index aa06ea231db3..000000000000
--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _BRCM_HW_IDS_H_
-#define _BRCM_HW_IDS_H_
-
-#include <linux/pci_ids.h>
-#include <linux/mmc/sdio_ids.h>
-
-#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c
-#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM
-
-/* Chipcommon Core Chip IDs */
-#define BRCM_CC_43143_CHIP_ID 43143
-#define BRCM_CC_43235_CHIP_ID 43235
-#define BRCM_CC_43236_CHIP_ID 43236
-#define BRCM_CC_43238_CHIP_ID 43238
-#define BRCM_CC_43241_CHIP_ID 0x4324
-#define BRCM_CC_43242_CHIP_ID 43242
-#define BRCM_CC_4329_CHIP_ID 0x4329
-#define BRCM_CC_4330_CHIP_ID 0x4330
-#define BRCM_CC_4334_CHIP_ID 0x4334
-#define BRCM_CC_43340_CHIP_ID 43340
-#define BRCM_CC_43362_CHIP_ID 43362
-#define BRCM_CC_4335_CHIP_ID 0x4335
-#define BRCM_CC_4339_CHIP_ID 0x4339
-#define BRCM_CC_43430_CHIP_ID 43430
-#define BRCM_CC_4345_CHIP_ID 0x4345
-#define BRCM_CC_4350_CHIP_ID 0x4350
-#define BRCM_CC_4354_CHIP_ID 0x4354
-#define BRCM_CC_4356_CHIP_ID 0x4356
-#define BRCM_CC_43566_CHIP_ID 43566
-#define BRCM_CC_43567_CHIP_ID 43567
-#define BRCM_CC_43569_CHIP_ID 43569
-#define BRCM_CC_43570_CHIP_ID 43570
-#define BRCM_CC_4358_CHIP_ID 0x4358
-#define BRCM_CC_43602_CHIP_ID 43602
-#define BRCM_CC_4365_CHIP_ID 0x4365
-#define BRCM_CC_4366_CHIP_ID 0x4366
-#define BRCM_CC_4371_CHIP_ID 0x4371
-
-/* USB Device IDs */
-#define BRCM_USB_43143_DEVICE_ID 0xbd1e
-#define BRCM_USB_43236_DEVICE_ID 0xbd17
-#define BRCM_USB_43242_DEVICE_ID 0xbd1f
-#define BRCM_USB_43569_DEVICE_ID 0xbd27
-#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc
-
-/* PCIE Device IDs */
-#define BRCM_PCIE_4350_DEVICE_ID 0x43a3
-#define BRCM_PCIE_4354_DEVICE_ID 0x43df
-#define BRCM_PCIE_4356_DEVICE_ID 0x43ec
-#define BRCM_PCIE_43567_DEVICE_ID 0x43d3
-#define BRCM_PCIE_43570_DEVICE_ID 0x43d9
-#define BRCM_PCIE_4358_DEVICE_ID 0x43e9
-#define BRCM_PCIE_43602_DEVICE_ID 0x43ba
-#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb
-#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc
-#define BRCM_PCIE_43602_RAW_DEVICE_ID 43602
-#define BRCM_PCIE_4365_DEVICE_ID 0x43ca
-#define BRCM_PCIE_4365_2G_DEVICE_ID 0x43cb
-#define BRCM_PCIE_4365_5G_DEVICE_ID 0x43cc
-#define BRCM_PCIE_4366_DEVICE_ID 0x43c3
-#define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4
-#define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5
-#define BRCM_PCIE_4371_DEVICE_ID 0x440d
-
-
-/* brcmsmac IDs */
-#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */
-#define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */
-#define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */
-#define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */
-#define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */
-#define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */
-
-#define BCM4313_CHIP_ID 0x4313
-#define BCM43224_CHIP_ID 43224
-
-#endif /* _BRCM_HW_IDS_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
deleted file mode 100644
index 76b5d3a86294..000000000000
--- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _BRCMU_WIFI_H_
-#define _BRCMU_WIFI_H_
-
-#include <linux/if_ether.h> /* for ETH_ALEN */
-#include <linux/ieee80211.h> /* for WLAN_PMKID_LEN */
-
-/*
- * A chanspec (u16) holds the channel number, band, bandwidth and control
- * sideband
- */
-
-/* channel defines */
-#define CH_UPPER_SB 0x01
-#define CH_LOWER_SB 0x02
-#define CH_EWA_VALID 0x04
-#define CH_30MHZ_APART 6
-#define CH_20MHZ_APART 4
-#define CH_10MHZ_APART 2
-#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */
-#define CH_MIN_2G_CHANNEL 1
-#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */
-#define CH_MIN_5G_CHANNEL 34
-
-/* bandstate array indices */
-#define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */
-#define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */
-
-/*
- * max # supported channels. The max channel no is 216, this is that + 1
- * rounded up to a multiple of NBBY (8). DO NOT MAKE it > 255: channels are
- * u8's all over
-*/
-#define MAXCHANNEL 224
-
-#define WL_CHANSPEC_CHAN_MASK 0x00ff
-#define WL_CHANSPEC_CHAN_SHIFT 0
-
-#define WL_CHANSPEC_CTL_SB_MASK 0x0300
-#define WL_CHANSPEC_CTL_SB_SHIFT 8
-#define WL_CHANSPEC_CTL_SB_LOWER 0x0100
-#define WL_CHANSPEC_CTL_SB_UPPER 0x0200
-#define WL_CHANSPEC_CTL_SB_NONE 0x0300
-
-#define WL_CHANSPEC_BW_MASK 0x0C00
-#define WL_CHANSPEC_BW_SHIFT 10
-#define WL_CHANSPEC_BW_10 0x0400
-#define WL_CHANSPEC_BW_20 0x0800
-#define WL_CHANSPEC_BW_40 0x0C00
-#define WL_CHANSPEC_BW_80 0x2000
-
-#define WL_CHANSPEC_BAND_MASK 0xf000
-#define WL_CHANSPEC_BAND_SHIFT 12
-#define WL_CHANSPEC_BAND_5G 0x1000
-#define WL_CHANSPEC_BAND_2G 0x2000
-#define INVCHANSPEC 255
-
-#define WL_CHAN_VALID_HW (1 << 0) /* valid with current HW */
-#define WL_CHAN_VALID_SW (1 << 1) /* valid with country sett. */
-#define WL_CHAN_BAND_5G (1 << 2) /* 5GHz-band channel */
-#define WL_CHAN_RADAR (1 << 3) /* radar sensitive channel */
-#define WL_CHAN_INACTIVE (1 << 4) /* inactive due to radar */
-#define WL_CHAN_PASSIVE (1 << 5) /* channel in passive mode */
-#define WL_CHAN_RESTRICTED (1 << 6) /* restricted use channel */
-
-/* values for band specific 40MHz capabilities */
-#define WLC_N_BW_20ALL 0
-#define WLC_N_BW_40ALL 1
-#define WLC_N_BW_20IN2G_40IN5G 2
-
-#define WLC_BW_20MHZ_BIT BIT(0)
-#define WLC_BW_40MHZ_BIT BIT(1)
-#define WLC_BW_80MHZ_BIT BIT(2)
-#define WLC_BW_160MHZ_BIT BIT(3)
-
-/* Bandwidth capabilities */
-#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT)
-#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
-#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT| \
- WLC_BW_20MHZ_BIT)
-#define WLC_BW_CAP_160MHZ (WLC_BW_160MHZ_BIT|WLC_BW_80MHZ_BIT| \
- WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
-#define WLC_BW_CAP_UNRESTRICTED 0xFF
-
-/* band types */
-#define WLC_BAND_AUTO 0 /* auto-select */
-#define WLC_BAND_5G 1 /* 5 Ghz */
-#define WLC_BAND_2G 2 /* 2.4 Ghz */
-#define WLC_BAND_ALL 3 /* all bands */
-
-#define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK))
-#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK)
-
-#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK)
-#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK)
-
-#define CHSPEC_IS10(chspec) \
- (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
-
-#define CHSPEC_IS20(chspec) \
- (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
-
-#define CHSPEC_IS40(chspec) \
- (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
-
-#define CHSPEC_IS80(chspec) \
- (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80)
-
-#define CHSPEC_IS5G(chspec) \
- (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
-
-#define CHSPEC_IS2G(chspec) \
- (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
-
-#define CHSPEC_SB_NONE(chspec) \
- (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE)
-
-#define CHSPEC_SB_UPPER(chspec) \
- (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER)
-
-#define CHSPEC_SB_LOWER(chspec) \
- (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER)
-
-#define CHSPEC_CTL_CHAN(chspec) \
- ((CHSPEC_SB_LOWER(chspec)) ? \
- (lower_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \
- (upper_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))))
-
-#define CHSPEC2BAND(chspec) (CHSPEC_IS5G(chspec) ? BRCM_BAND_5G : BRCM_BAND_2G)
-
-#define CHANSPEC_STR_LEN 8
-
-static inline int lower_20_sb(int channel)
-{
- return channel > CH_10MHZ_APART ? (channel - CH_10MHZ_APART) : 0;
-}
-
-static inline int upper_20_sb(int channel)
-{
- return (channel < (MAXCHANNEL - CH_10MHZ_APART)) ?
- channel + CH_10MHZ_APART : 0;
-}
-
-static inline int chspec_bandunit(u16 chspec)
-{
- return CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX;
-}
-
-static inline u16 ch20mhz_chspec(int channel)
-{
- u16 rc = channel <= CH_MAX_2G_CHANNEL ?
- WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G;
-
- return (u16)((u16)channel | WL_CHANSPEC_BW_20 |
- WL_CHANSPEC_CTL_SB_NONE | rc);
-}
-
-static inline int next_20mhz_chan(int channel)
-{
- return channel < (MAXCHANNEL - CH_20MHZ_APART) ?
- channel + CH_20MHZ_APART : 0;
-}
-
-/* defined rate in 500kbps */
-#define BRCM_MAXRATE 108 /* in 500kbps units */
-#define BRCM_RATE_1M 2 /* in 500kbps units */
-#define BRCM_RATE_2M 4 /* in 500kbps units */
-#define BRCM_RATE_5M5 11 /* in 500kbps units */
-#define BRCM_RATE_11M 22 /* in 500kbps units */
-#define BRCM_RATE_6M 12 /* in 500kbps units */
-#define BRCM_RATE_9M 18 /* in 500kbps units */
-#define BRCM_RATE_12M 24 /* in 500kbps units */
-#define BRCM_RATE_18M 36 /* in 500kbps units */
-#define BRCM_RATE_24M 48 /* in 500kbps units */
-#define BRCM_RATE_36M 72 /* in 500kbps units */
-#define BRCM_RATE_48M 96 /* in 500kbps units */
-#define BRCM_RATE_54M 108 /* in 500kbps units */
-
-#define BRCM_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */
-
-#define MCSSET_LEN 16
-
-static inline bool ac_bitmap_tst(u8 bitmap, int prec)
-{
- return (bitmap & (1 << (prec))) != 0;
-}
-
-/* Enumerate crypto algorithms */
-#define CRYPTO_ALGO_OFF 0
-#define CRYPTO_ALGO_WEP1 1
-#define CRYPTO_ALGO_TKIP 2
-#define CRYPTO_ALGO_WEP128 3
-#define CRYPTO_ALGO_AES_CCM 4
-#define CRYPTO_ALGO_AES_RESERVED1 5
-#define CRYPTO_ALGO_AES_RESERVED2 6
-#define CRYPTO_ALGO_NALG 7
-
-/* wireless security bitvec */
-
-#define WEP_ENABLED 0x0001
-#define TKIP_ENABLED 0x0002
-#define AES_ENABLED 0x0004
-#define WSEC_SWFLAG 0x0008
-/* to go into transition mode without setting wep */
-#define SES_OW_ENABLED 0x0040
-/* MFP */
-#define MFP_CAPABLE 0x0200
-#define MFP_REQUIRED 0x0400
-
-/* WPA authentication mode bitvec */
-#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */
-#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */
-#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */
-#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */
-#define WPA_AUTH_RESERVED1 0x0008
-#define WPA_AUTH_RESERVED2 0x0010
-
-#define WPA2_AUTH_RESERVED1 0x0020
-#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */
-#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */
-#define WPA2_AUTH_RESERVED3 0x0200
-#define WPA2_AUTH_RESERVED4 0x0400
-#define WPA2_AUTH_RESERVED5 0x0800
-
-/* pmkid */
-#define MAXPMKID 16
-
-#define DOT11_DEFAULT_RTS_LEN 2347
-#define DOT11_DEFAULT_FRAG_LEN 2346
-
-#define DOT11_ICV_AES_LEN 8
-#define DOT11_QOS_LEN 2
-#define DOT11_IV_MAX_LEN 8
-#define DOT11_A4_HDR_LEN 30
-
-#define HT_CAP_RX_STBC_NO 0x0
-#define HT_CAP_RX_STBC_ONE_STREAM 0x1
-
-struct pmkid {
- u8 BSSID[ETH_ALEN];
- u8 PMKID[WLAN_PMKID_LEN];
-};
-
-struct pmkid_list {
- __le32 npmkid;
- struct pmkid pmkid[1];
-};
-
-struct pmkid_cand {
- u8 BSSID[ETH_ALEN];
- u8 preauth;
-};
-
-struct pmkid_cand_list {
- u32 npmkid_cand;
- struct pmkid_cand pmkid_cand[1];
-};
-
-#endif /* _BRCMU_WIFI_H_ */
diff --git a/drivers/net/wireless/broadcom/Kconfig b/drivers/net/wireless/broadcom/Kconfig
new file mode 100644
index 000000000000..d3651ceb5046
--- /dev/null
+++ b/drivers/net/wireless/broadcom/Kconfig
@@ -0,0 +1,18 @@
+config WLAN_VENDOR_BROADCOM
+ bool "Broadcom devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_BROADCOM
+
+source "drivers/net/wireless/broadcom/b43/Kconfig"
+source "drivers/net/wireless/broadcom/b43legacy/Kconfig"
+source "drivers/net/wireless/broadcom/brcm80211/Kconfig"
+
+endif # WLAN_VENDOR_BROADCOM
diff --git a/drivers/net/wireless/broadcom/Makefile b/drivers/net/wireless/broadcom/Makefile
new file mode 100644
index 000000000000..9d5ac95710c3
--- /dev/null
+++ b/drivers/net/wireless/broadcom/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_B43) += b43/
+obj-$(CONFIG_B43LEGACY) += b43legacy/
+
+obj-$(CONFIG_BRCMFMAC) += brcm80211/
+obj-$(CONFIG_BRCMSMAC) += brcm80211/
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig
index fba856032ca5..fba856032ca5 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/broadcom/b43/Kconfig
diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/broadcom/b43/Makefile
index ddc4df46656f..ddc4df46656f 100644
--- a/drivers/net/wireless/b43/Makefile
+++ b/drivers/net/wireless/broadcom/b43/Makefile
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/broadcom/b43/b43.h
index 036552439816..036552439816 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/broadcom/b43/b43.h
diff --git a/drivers/net/wireless/b43/bus.c b/drivers/net/wireless/broadcom/b43/bus.c
index 17d16a391fe6..17d16a391fe6 100644
--- a/drivers/net/wireless/b43/bus.c
+++ b/drivers/net/wireless/broadcom/b43/bus.c
diff --git a/drivers/net/wireless/b43/bus.h b/drivers/net/wireless/broadcom/b43/bus.h
index 256c2c17939a..256c2c17939a 100644
--- a/drivers/net/wireless/b43/bus.h
+++ b/drivers/net/wireless/broadcom/b43/bus.h
diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/broadcom/b43/debugfs.c
index b4bcd94aff6c..b4bcd94aff6c 100644
--- a/drivers/net/wireless/b43/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43/debugfs.c
diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/broadcom/b43/debugfs.h
index d05377745011..d05377745011 100644
--- a/drivers/net/wireless/b43/debugfs.h
+++ b/drivers/net/wireless/broadcom/b43/debugfs.h
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c
index 6837064908be..6837064908be 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/broadcom/b43/dma.c
diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h
index df8c8cdcbdb5..df8c8cdcbdb5 100644
--- a/drivers/net/wireless/b43/dma.h
+++ b/drivers/net/wireless/broadcom/b43/dma.h
diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/broadcom/b43/leds.c
index d79ab2a227e1..d79ab2a227e1 100644
--- a/drivers/net/wireless/b43/leds.c
+++ b/drivers/net/wireless/broadcom/b43/leds.c
diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/broadcom/b43/leds.h
index 32b66d53cdac..32b66d53cdac 100644
--- a/drivers/net/wireless/b43/leds.h
+++ b/drivers/net/wireless/broadcom/b43/leds.h
diff --git a/drivers/net/wireless/b43/lo.c b/drivers/net/wireless/broadcom/b43/lo.c
index a335f94c72ff..a335f94c72ff 100644
--- a/drivers/net/wireless/b43/lo.c
+++ b/drivers/net/wireless/broadcom/b43/lo.c
diff --git a/drivers/net/wireless/b43/lo.h b/drivers/net/wireless/broadcom/b43/lo.h
index 7b4df3883bc2..7b4df3883bc2 100644
--- a/drivers/net/wireless/b43/lo.h
+++ b/drivers/net/wireless/broadcom/b43/lo.h
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index ec013fbd6a81..ec013fbd6a81 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/broadcom/b43/main.h
index c46430cc725c..c46430cc725c 100644
--- a/drivers/net/wireless/b43/main.h
+++ b/drivers/net/wireless/broadcom/b43/main.h
diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/broadcom/b43/phy_a.c
index 99c036f5ecb7..99c036f5ecb7 100644
--- a/drivers/net/wireless/b43/phy_a.c
+++ b/drivers/net/wireless/broadcom/b43/phy_a.c
diff --git a/drivers/net/wireless/b43/phy_a.h b/drivers/net/wireless/broadcom/b43/phy_a.h
index f7d0d929a374..f7d0d929a374 100644
--- a/drivers/net/wireless/b43/phy_a.h
+++ b/drivers/net/wireless/broadcom/b43/phy_a.h
diff --git a/drivers/net/wireless/b43/phy_ac.c b/drivers/net/wireless/broadcom/b43/phy_ac.c
index e75633d67938..e75633d67938 100644
--- a/drivers/net/wireless/b43/phy_ac.c
+++ b/drivers/net/wireless/broadcom/b43/phy_ac.c
diff --git a/drivers/net/wireless/b43/phy_ac.h b/drivers/net/wireless/broadcom/b43/phy_ac.h
index d1ca79e0eb24..d1ca79e0eb24 100644
--- a/drivers/net/wireless/b43/phy_ac.h
+++ b/drivers/net/wireless/broadcom/b43/phy_ac.h
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/broadcom/b43/phy_common.c
index ec2b9c577b90..ec2b9c577b90 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/broadcom/b43/phy_common.c
diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/broadcom/b43/phy_common.h
index 78d86526799e..78d86526799e 100644
--- a/drivers/net/wireless/b43/phy_common.h
+++ b/drivers/net/wireless/broadcom/b43/phy_common.h
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/broadcom/b43/phy_g.c
index 462310e6e88f..462310e6e88f 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/broadcom/b43/phy_g.c
diff --git a/drivers/net/wireless/b43/phy_g.h b/drivers/net/wireless/broadcom/b43/phy_g.h
index 5413c906a3e7..5413c906a3e7 100644
--- a/drivers/net/wireless/b43/phy_g.h
+++ b/drivers/net/wireless/broadcom/b43/phy_g.h
diff --git a/drivers/net/wireless/b43/phy_ht.c b/drivers/net/wireless/broadcom/b43/phy_ht.c
index bd68945965d6..bd68945965d6 100644
--- a/drivers/net/wireless/b43/phy_ht.c
+++ b/drivers/net/wireless/broadcom/b43/phy_ht.c
diff --git a/drivers/net/wireless/b43/phy_ht.h b/drivers/net/wireless/broadcom/b43/phy_ht.h
index c086f56ce478..c086f56ce478 100644
--- a/drivers/net/wireless/b43/phy_ht.h
+++ b/drivers/net/wireless/broadcom/b43/phy_ht.h
diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/broadcom/b43/phy_lcn.c
index 97461ccf3e1e..97461ccf3e1e 100644
--- a/drivers/net/wireless/b43/phy_lcn.c
+++ b/drivers/net/wireless/broadcom/b43/phy_lcn.c
diff --git a/drivers/net/wireless/b43/phy_lcn.h b/drivers/net/wireless/broadcom/b43/phy_lcn.h
index 6a7092e13fff..6a7092e13fff 100644
--- a/drivers/net/wireless/b43/phy_lcn.h
+++ b/drivers/net/wireless/broadcom/b43/phy_lcn.h
diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/broadcom/b43/phy_lp.c
index 058a9f232050..058a9f232050 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/broadcom/b43/phy_lp.c
diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/broadcom/b43/phy_lp.h
index 62737f700cbc..62737f700cbc 100644
--- a/drivers/net/wireless/b43/phy_lp.h
+++ b/drivers/net/wireless/broadcom/b43/phy_lp.h
diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/broadcom/b43/phy_n.c
index 9f0bcf3b8414..9f0bcf3b8414 100644
--- a/drivers/net/wireless/b43/phy_n.c
+++ b/drivers/net/wireless/broadcom/b43/phy_n.c
diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/broadcom/b43/phy_n.h
index a6da2c31a99c..a6da2c31a99c 100644
--- a/drivers/net/wireless/b43/phy_n.h
+++ b/drivers/net/wireless/broadcom/b43/phy_n.h
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/broadcom/b43/pio.c
index a4ff5e2a42b9..a4ff5e2a42b9 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/broadcom/b43/pio.c
diff --git a/drivers/net/wireless/b43/pio.h b/drivers/net/wireless/broadcom/b43/pio.h
index 1e516147424f..1e516147424f 100644
--- a/drivers/net/wireless/b43/pio.h
+++ b/drivers/net/wireless/broadcom/b43/pio.h
diff --git a/drivers/net/wireless/b43/ppr.c b/drivers/net/wireless/broadcom/b43/ppr.c
index 9a770279c415..9a770279c415 100644
--- a/drivers/net/wireless/b43/ppr.c
+++ b/drivers/net/wireless/broadcom/b43/ppr.c
diff --git a/drivers/net/wireless/b43/ppr.h b/drivers/net/wireless/broadcom/b43/ppr.h
index 24d7447e9f01..24d7447e9f01 100644
--- a/drivers/net/wireless/b43/ppr.h
+++ b/drivers/net/wireless/broadcom/b43/ppr.h
diff --git a/drivers/net/wireless/b43/radio_2055.c b/drivers/net/wireless/broadcom/b43/radio_2055.c
index 5289a18ddd8c..5289a18ddd8c 100644
--- a/drivers/net/wireless/b43/radio_2055.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2055.c
diff --git a/drivers/net/wireless/b43/radio_2055.h b/drivers/net/wireless/broadcom/b43/radio_2055.h
index 67f96122f8d8..67f96122f8d8 100644
--- a/drivers/net/wireless/b43/radio_2055.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2055.h
diff --git a/drivers/net/wireless/b43/radio_2056.c b/drivers/net/wireless/broadcom/b43/radio_2056.c
index 2ce25607c60d..2ce25607c60d 100644
--- a/drivers/net/wireless/b43/radio_2056.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2056.c
diff --git a/drivers/net/wireless/b43/radio_2056.h b/drivers/net/wireless/broadcom/b43/radio_2056.h
index 5b86673459fa..5b86673459fa 100644
--- a/drivers/net/wireless/b43/radio_2056.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2056.h
diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/broadcom/b43/radio_2057.c
index ff1e026a61a1..ff1e026a61a1 100644
--- a/drivers/net/wireless/b43/radio_2057.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2057.c
diff --git a/drivers/net/wireless/b43/radio_2057.h b/drivers/net/wireless/broadcom/b43/radio_2057.h
index 220d080238ff..220d080238ff 100644
--- a/drivers/net/wireless/b43/radio_2057.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2057.h
diff --git a/drivers/net/wireless/b43/radio_2059.c b/drivers/net/wireless/broadcom/b43/radio_2059.c
index a3cf9efd7e21..a3cf9efd7e21 100644
--- a/drivers/net/wireless/b43/radio_2059.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2059.c
diff --git a/drivers/net/wireless/b43/radio_2059.h b/drivers/net/wireless/broadcom/b43/radio_2059.h
index 9e22fb60588b..9e22fb60588b 100644
--- a/drivers/net/wireless/b43/radio_2059.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2059.h
diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/broadcom/b43/rfkill.c
index 70c2fcedd1bb..70c2fcedd1bb 100644
--- a/drivers/net/wireless/b43/rfkill.c
+++ b/drivers/net/wireless/broadcom/b43/rfkill.c
diff --git a/drivers/net/wireless/b43/rfkill.h b/drivers/net/wireless/broadcom/b43/rfkill.h
index f046c3ca0519..f046c3ca0519 100644
--- a/drivers/net/wireless/b43/rfkill.h
+++ b/drivers/net/wireless/broadcom/b43/rfkill.h
diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/broadcom/b43/sdio.c
index 59a521800694..59a521800694 100644
--- a/drivers/net/wireless/b43/sdio.c
+++ b/drivers/net/wireless/broadcom/b43/sdio.c
diff --git a/drivers/net/wireless/b43/sdio.h b/drivers/net/wireless/broadcom/b43/sdio.h
index 1e93926f388f..1e93926f388f 100644
--- a/drivers/net/wireless/b43/sdio.h
+++ b/drivers/net/wireless/broadcom/b43/sdio.h
diff --git a/drivers/net/wireless/b43/sysfs.c b/drivers/net/wireless/broadcom/b43/sysfs.c
index 3190493bd07f..3190493bd07f 100644
--- a/drivers/net/wireless/b43/sysfs.c
+++ b/drivers/net/wireless/broadcom/b43/sysfs.c
diff --git a/drivers/net/wireless/b43/sysfs.h b/drivers/net/wireless/broadcom/b43/sysfs.h
index 12bda9ef1a85..12bda9ef1a85 100644
--- a/drivers/net/wireless/b43/sysfs.h
+++ b/drivers/net/wireless/broadcom/b43/sysfs.h
diff --git a/drivers/net/wireless/b43/tables.c b/drivers/net/wireless/broadcom/b43/tables.c
index ea288df8aee9..ea288df8aee9 100644
--- a/drivers/net/wireless/b43/tables.c
+++ b/drivers/net/wireless/broadcom/b43/tables.c
diff --git a/drivers/net/wireless/b43/tables.h b/drivers/net/wireless/broadcom/b43/tables.h
index 80e73c7cbac5..80e73c7cbac5 100644
--- a/drivers/net/wireless/b43/tables.h
+++ b/drivers/net/wireless/broadcom/b43/tables.h
diff --git a/drivers/net/wireless/b43/tables_lpphy.c b/drivers/net/wireless/broadcom/b43/tables_lpphy.c
index cff187c5616d..cff187c5616d 100644
--- a/drivers/net/wireless/b43/tables_lpphy.c
+++ b/drivers/net/wireless/broadcom/b43/tables_lpphy.c
diff --git a/drivers/net/wireless/b43/tables_lpphy.h b/drivers/net/wireless/broadcom/b43/tables_lpphy.h
index 84f1d265f657..84f1d265f657 100644
--- a/drivers/net/wireless/b43/tables_lpphy.h
+++ b/drivers/net/wireless/broadcom/b43/tables_lpphy.h
diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/broadcom/b43/tables_nphy.c
index b2f0d245bcf3..b2f0d245bcf3 100644
--- a/drivers/net/wireless/b43/tables_nphy.c
+++ b/drivers/net/wireless/broadcom/b43/tables_nphy.c
diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/broadcom/b43/tables_nphy.h
index b51f386db02f..b51f386db02f 100644
--- a/drivers/net/wireless/b43/tables_nphy.h
+++ b/drivers/net/wireless/broadcom/b43/tables_nphy.h
diff --git a/drivers/net/wireless/b43/tables_phy_ht.c b/drivers/net/wireless/broadcom/b43/tables_phy_ht.c
index 176c49d74ef4..176c49d74ef4 100644
--- a/drivers/net/wireless/b43/tables_phy_ht.c
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_ht.c
diff --git a/drivers/net/wireless/b43/tables_phy_ht.h b/drivers/net/wireless/broadcom/b43/tables_phy_ht.h
index 1b5ef2bc770c..1b5ef2bc770c 100644
--- a/drivers/net/wireless/b43/tables_phy_ht.h
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_ht.h
diff --git a/drivers/net/wireless/b43/tables_phy_lcn.c b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.c
index e347b8d80ea4..e347b8d80ea4 100644
--- a/drivers/net/wireless/b43/tables_phy_lcn.c
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.c
diff --git a/drivers/net/wireless/b43/tables_phy_lcn.h b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.h
index caff9db6831f..caff9db6831f 100644
--- a/drivers/net/wireless/b43/tables_phy_lcn.h
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.h
diff --git a/drivers/net/wireless/b43/wa.c b/drivers/net/wireless/broadcom/b43/wa.c
index c218c08fb2f5..c218c08fb2f5 100644
--- a/drivers/net/wireless/b43/wa.c
+++ b/drivers/net/wireless/broadcom/b43/wa.c
diff --git a/drivers/net/wireless/b43/wa.h b/drivers/net/wireless/broadcom/b43/wa.h
index e163c5e56e81..e163c5e56e81 100644
--- a/drivers/net/wireless/b43/wa.h
+++ b/drivers/net/wireless/broadcom/b43/wa.h
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c
index 426dc13c44cd..426dc13c44cd 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/broadcom/b43/xmit.c
diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/broadcom/b43/xmit.h
index ba6115308068..ba6115308068 100644
--- a/drivers/net/wireless/b43/xmit.h
+++ b/drivers/net/wireless/broadcom/b43/xmit.h
diff --git a/drivers/net/wireless/b43legacy/Kconfig b/drivers/net/wireless/broadcom/b43legacy/Kconfig
index 1ffa28835c58..1ffa28835c58 100644
--- a/drivers/net/wireless/b43legacy/Kconfig
+++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig
diff --git a/drivers/net/wireless/b43legacy/Makefile b/drivers/net/wireless/broadcom/b43legacy/Makefile
index 227a77e84362..227a77e84362 100644
--- a/drivers/net/wireless/b43legacy/Makefile
+++ b/drivers/net/wireless/broadcom/b43legacy/Makefile
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h
index 482476fdb1f3..482476fdb1f3 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h
diff --git a/drivers/net/wireless/b43legacy/debugfs.c b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
index 090910ea259e..090910ea259e 100644
--- a/drivers/net/wireless/b43legacy/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
diff --git a/drivers/net/wireless/b43legacy/debugfs.h b/drivers/net/wireless/broadcom/b43legacy/debugfs.h
index 9ee32158b947..9ee32158b947 100644
--- a/drivers/net/wireless/b43legacy/debugfs.h
+++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.h
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/broadcom/b43legacy/dma.c
index f9dd892b9f27..f9dd892b9f27 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/broadcom/b43legacy/dma.c
diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/broadcom/b43legacy/dma.h
index c3282f906bc7..c3282f906bc7 100644
--- a/drivers/net/wireless/b43legacy/dma.h
+++ b/drivers/net/wireless/broadcom/b43legacy/dma.h
diff --git a/drivers/net/wireless/b43legacy/ilt.c b/drivers/net/wireless/broadcom/b43legacy/ilt.c
index ee5682e54204..ee5682e54204 100644
--- a/drivers/net/wireless/b43legacy/ilt.c
+++ b/drivers/net/wireless/broadcom/b43legacy/ilt.c
diff --git a/drivers/net/wireless/b43legacy/ilt.h b/drivers/net/wireless/broadcom/b43legacy/ilt.h
index 48bcf37eccb8..48bcf37eccb8 100644
--- a/drivers/net/wireless/b43legacy/ilt.h
+++ b/drivers/net/wireless/broadcom/b43legacy/ilt.h
diff --git a/drivers/net/wireless/b43legacy/leds.c b/drivers/net/wireless/broadcom/b43legacy/leds.c
index fd4565389c77..fd4565389c77 100644
--- a/drivers/net/wireless/b43legacy/leds.c
+++ b/drivers/net/wireless/broadcom/b43legacy/leds.c
diff --git a/drivers/net/wireless/b43legacy/leds.h b/drivers/net/wireless/broadcom/b43legacy/leds.h
index 9ff6750dc57f..9ff6750dc57f 100644
--- a/drivers/net/wireless/b43legacy/leds.h
+++ b/drivers/net/wireless/broadcom/b43legacy/leds.h
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c
index afc1fb3e38df..afc1fb3e38df 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/broadcom/b43legacy/main.c
diff --git a/drivers/net/wireless/b43legacy/main.h b/drivers/net/wireless/broadcom/b43legacy/main.h
index b74a058d7bac..b74a058d7bac 100644
--- a/drivers/net/wireless/b43legacy/main.h
+++ b/drivers/net/wireless/broadcom/b43legacy/main.h
diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/broadcom/b43legacy/phy.c
index 995c7d0c212a..995c7d0c212a 100644
--- a/drivers/net/wireless/b43legacy/phy.c
+++ b/drivers/net/wireless/broadcom/b43legacy/phy.c
diff --git a/drivers/net/wireless/b43legacy/phy.h b/drivers/net/wireless/broadcom/b43legacy/phy.h
index 831a7a4760e5..831a7a4760e5 100644
--- a/drivers/net/wireless/b43legacy/phy.h
+++ b/drivers/net/wireless/broadcom/b43legacy/phy.h
diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/broadcom/b43legacy/pio.c
index 282eedec675e..282eedec675e 100644
--- a/drivers/net/wireless/b43legacy/pio.c
+++ b/drivers/net/wireless/broadcom/b43legacy/pio.c
diff --git a/drivers/net/wireless/b43legacy/pio.h b/drivers/net/wireless/broadcom/b43legacy/pio.h
index 8e6773ea6e75..8e6773ea6e75 100644
--- a/drivers/net/wireless/b43legacy/pio.h
+++ b/drivers/net/wireless/broadcom/b43legacy/pio.h
diff --git a/drivers/net/wireless/b43legacy/radio.c b/drivers/net/wireless/broadcom/b43legacy/radio.c
index 9501420340a9..9501420340a9 100644
--- a/drivers/net/wireless/b43legacy/radio.c
+++ b/drivers/net/wireless/broadcom/b43legacy/radio.c
diff --git a/drivers/net/wireless/b43legacy/radio.h b/drivers/net/wireless/broadcom/b43legacy/radio.h
index dd2976d1d561..dd2976d1d561 100644
--- a/drivers/net/wireless/b43legacy/radio.h
+++ b/drivers/net/wireless/broadcom/b43legacy/radio.h
diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/broadcom/b43legacy/rfkill.c
index 7c1bdbc02569..7c1bdbc02569 100644
--- a/drivers/net/wireless/b43legacy/rfkill.c
+++ b/drivers/net/wireless/broadcom/b43legacy/rfkill.c
diff --git a/drivers/net/wireless/b43legacy/rfkill.h b/drivers/net/wireless/broadcom/b43legacy/rfkill.h
index 75585571c544..75585571c544 100644
--- a/drivers/net/wireless/b43legacy/rfkill.h
+++ b/drivers/net/wireless/broadcom/b43legacy/rfkill.h
diff --git a/drivers/net/wireless/b43legacy/sysfs.c b/drivers/net/wireless/broadcom/b43legacy/sysfs.c
index 2a1da15c913b..2a1da15c913b 100644
--- a/drivers/net/wireless/b43legacy/sysfs.c
+++ b/drivers/net/wireless/broadcom/b43legacy/sysfs.c
diff --git a/drivers/net/wireless/b43legacy/sysfs.h b/drivers/net/wireless/broadcom/b43legacy/sysfs.h
index 417d509803c7..417d509803c7 100644
--- a/drivers/net/wireless/b43legacy/sysfs.h
+++ b/drivers/net/wireless/broadcom/b43legacy/sysfs.h
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/broadcom/b43legacy/xmit.c
index 34bf3f0b729f..34bf3f0b729f 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/broadcom/b43legacy/xmit.c
diff --git a/drivers/net/wireless/b43legacy/xmit.h b/drivers/net/wireless/broadcom/b43legacy/xmit.h
index 289db00a4a7b..289db00a4a7b 100644
--- a/drivers/net/wireless/b43legacy/xmit.h
+++ b/drivers/net/wireless/broadcom/b43legacy/xmit.h
diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig
index ab42b1fea03c..ab42b1fea03c 100644
--- a/drivers/net/wireless/brcm80211/Kconfig
+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
diff --git a/drivers/net/wireless/brcm80211/Makefile b/drivers/net/wireless/broadcom/brcm80211/Makefile
index b987920e982e..b987920e982e 100644
--- a/drivers/net/wireless/brcm80211/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/Makefile
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
new file mode 100644
index 000000000000..9e4b505ca593
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
@@ -0,0 +1,57 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver
+#
+# Copyright (c) 2010 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ccflags-y += \
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmfmac \
+ -Idrivers/net/wireless/broadcom/brcm80211/include
+
+ccflags-y += -D__CHECK_ENDIAN__
+
+obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
+brcmfmac-objs += \
+ cfg80211.o \
+ chip.o \
+ fwil.o \
+ fweh.o \
+ fwsignal.o \
+ p2p.o \
+ proto.o \
+ common.o \
+ core.o \
+ firmware.o \
+ feature.o \
+ btcoex.o \
+ vendor.o
+brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \
+ bcdc.o
+brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \
+ commonring.o \
+ flowring.o \
+ msgbuf.o
+brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
+ sdio.o \
+ bcmsdh.o
+brcmfmac-$(CONFIG_BRCMFMAC_USB) += \
+ usb.o
+brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \
+ pcie.o
+brcmfmac-$(CONFIG_BRCMDBG) += \
+ debug.o
+brcmfmac-$(CONFIG_BRCM_TRACING) += \
+ tracepoint.o
+brcmfmac-$(CONFIG_OF) += \
+ of.o
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
new file mode 100644
index 000000000000..6af658e443e4
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*******************************************************************************
+ * Communicates with the dongle by using dcmd codes.
+ * For certain dcmd codes, the dongle interprets string data from the host.
+ ******************************************************************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "core.h"
+#include "bus.h"
+#include "fwsignal.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "proto.h"
+#include "bcdc.h"
+
+struct brcmf_proto_bcdc_dcmd {
+ __le32 cmd; /* dongle command value */
+ __le32 len; /* lower 16: output buflen;
+ * upper 16: input buflen (excludes header) */
+ __le32 flags; /* flag defns given below */
+ __le32 status; /* status code returned from the device */
+};
+
+/* BCDC flag definitions */
+#define BCDC_DCMD_ERROR 0x01 /* 1=cmd failed */
+#define BCDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */
+#define BCDC_DCMD_IF_MASK 0xF000 /* I/F index */
+#define BCDC_DCMD_IF_SHIFT 12
+#define BCDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */
+#define BCDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */
+#define BCDC_DCMD_ID(flags) \
+ (((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
+
+/*
+ * BCDC header - Broadcom specific extension of CDC.
+ * Used on data packets to convey priority across USB.
+ */
+#define BCDC_HEADER_LEN 4
+#define BCDC_PROTO_VER 2 /* Protocol version */
+#define BCDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */
+#define BCDC_FLAG_VER_SHIFT 4 /* Protocol version shift */
+#define BCDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */
+#define BCDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */
+#define BCDC_PRIORITY_MASK 0x7
+#define BCDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */
+#define BCDC_FLAG2_IF_SHIFT 0
+
+#define BCDC_GET_IF_IDX(hdr) \
+ ((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
+#define BCDC_SET_IF_IDX(hdr, idx) \
+ ((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
+ ((idx) << BCDC_FLAG2_IF_SHIFT)))
+
+/**
+ * struct brcmf_proto_bcdc_header - BCDC header format
+ *
+ * @flags: flags contain protocol and checksum info.
+ * @priority: 802.1d priority and USB flow control info (bit 4:7).
+ * @flags2: additional flags containing dongle interface index.
+ * @data_offset: start of packet data. header is following by firmware signals.
+ */
+struct brcmf_proto_bcdc_header {
+ u8 flags;
+ u8 priority;
+ u8 flags2;
+ u8 data_offset;
+};
+
+/*
+ * maximum length of firmware signal data between
+ * the BCDC header and packet data in the tx path.
+ */
+#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12
+
+#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
+#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE
+ * (amount of header tha might be added)
+ * plus any space that might be needed
+ * for bus alignment padding.
+ */
+struct brcmf_bcdc {
+ u16 reqid;
+ u8 bus_header[BUS_HEADER_LEN];
+ struct brcmf_proto_bcdc_dcmd msg;
+ unsigned char buf[BRCMF_DCMD_MAXLEN];
+};
+
+
+static int
+brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
+ uint len, bool set)
+{
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+ struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
+ u32 flags;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+ memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
+
+ msg->cmd = cpu_to_le32(cmd);
+ msg->len = cpu_to_le32(len);
+ flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
+ if (set)
+ flags |= BCDC_DCMD_SET;
+ flags = (flags & ~BCDC_DCMD_IF_MASK) |
+ (ifidx << BCDC_DCMD_IF_SHIFT);
+ msg->flags = cpu_to_le32(flags);
+
+ if (buf)
+ memcpy(bcdc->buf, buf, len);
+
+ len += sizeof(*msg);
+ if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
+ len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
+
+ /* Send request */
+ return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
+}
+
+static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
+{
+ int ret;
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+
+ brcmf_dbg(BCDC, "Enter\n");
+ len += sizeof(struct brcmf_proto_bcdc_dcmd);
+ do {
+ ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
+ len);
+ if (ret < 0)
+ break;
+ } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
+
+ return ret;
+}
+
+static int
+brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len)
+{
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+ struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
+ void *info;
+ int ret = 0, retries = 0;
+ u32 id, flags;
+
+ brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
+
+ ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
+ if (ret < 0) {
+ brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n",
+ ret);
+ goto done;
+ }
+
+retry:
+ /* wait for interrupt and get first fragment */
+ ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
+ if (ret < 0)
+ goto done;
+
+ flags = le32_to_cpu(msg->flags);
+ id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
+
+ if ((id < bcdc->reqid) && (++retries < RETRIES))
+ goto retry;
+ if (id != bcdc->reqid) {
+ brcmf_err("%s: unexpected request id %d (expected %d)\n",
+ brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
+ bcdc->reqid);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check info buffer */
+ info = (void *)&msg[1];
+
+ /* Copy info buffer */
+ if (buf) {
+ if (ret < (int)len)
+ len = ret;
+ memcpy(buf, info, len);
+ }
+
+ /* Check the ERROR flag */
+ if (flags & BCDC_DCMD_ERROR)
+ ret = le32_to_cpu(msg->status);
+
+done:
+ return ret;
+}
+
+static int
+brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len)
+{
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+ struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
+ int ret = 0;
+ u32 flags, id;
+
+ brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
+
+ ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
+ if (ret < 0)
+ goto done;
+
+ ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
+ if (ret < 0)
+ goto done;
+
+ flags = le32_to_cpu(msg->flags);
+ id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
+
+ if (id != bcdc->reqid) {
+ brcmf_err("%s: unexpected request id %d (expected %d)\n",
+ brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
+ bcdc->reqid);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check the ERROR flag */
+ if (flags & BCDC_DCMD_ERROR)
+ ret = le32_to_cpu(msg->status);
+
+done:
+ return ret;
+}
+
+static void
+brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
+ struct sk_buff *pktbuf)
+{
+ struct brcmf_proto_bcdc_header *h;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+ /* Push BDC header used to convey priority for buses that don't */
+ skb_push(pktbuf, BCDC_HEADER_LEN);
+
+ h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+
+ h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
+ if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
+ h->flags |= BCDC_FLAG_SUM_NEEDED;
+
+ h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
+ h->flags2 = 0;
+ h->data_offset = offset;
+ BCDC_SET_IF_IDX(h, ifidx);
+ trace_brcmf_bcdchdr(pktbuf->data);
+}
+
+static int
+brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+ struct sk_buff *pktbuf, struct brcmf_if **ifp)
+{
+ struct brcmf_proto_bcdc_header *h;
+ struct brcmf_if *tmp_if;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+ /* Pop BCDC header used to convey priority for buses that don't */
+ if (pktbuf->len <= BCDC_HEADER_LEN) {
+ brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
+ pktbuf->len, BCDC_HEADER_LEN);
+ return -EBADE;
+ }
+
+ trace_brcmf_bcdchdr(pktbuf->data);
+ h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+
+ tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
+ if (!tmp_if) {
+ brcmf_dbg(INFO, "no matching ifp found\n");
+ return -EBADE;
+ }
+ if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
+ BCDC_PROTO_VER) {
+ brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
+ brcmf_ifname(tmp_if), h->flags);
+ return -EBADE;
+ }
+
+ if (h->flags & BCDC_FLAG_SUM_GOOD) {
+ brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
+ brcmf_ifname(tmp_if), h->flags);
+ pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+ pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
+
+ skb_pull(pktbuf, BCDC_HEADER_LEN);
+ if (do_fws)
+ brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
+ else
+ skb_pull(pktbuf, h->data_offset << 2);
+
+ if (pktbuf->len == 0)
+ return -ENODATA;
+
+ *ifp = tmp_if;
+ return 0;
+}
+
+static int
+brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
+ struct sk_buff *pktbuf)
+{
+ brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
+ return brcmf_bus_txdata(drvr->bus_if, pktbuf);
+}
+
+static void
+brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
+ enum proto_addr_mode addr_mode)
+{
+}
+
+static void
+brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
+ u8 peer[ETH_ALEN])
+{
+}
+
+static void
+brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
+ u8 peer[ETH_ALEN])
+{
+}
+
+int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_bcdc *bcdc;
+
+ bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
+ if (!bcdc)
+ goto fail;
+
+ /* ensure that the msg buf directly follows the cdc msg struct */
+ if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
+ brcmf_err("struct brcmf_proto_bcdc is not correctly defined\n");
+ goto fail;
+ }
+
+ drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
+ drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
+ drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
+ drvr->proto->txdata = brcmf_proto_bcdc_txdata;
+ drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
+ drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
+ drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
+ drvr->proto->pd = bcdc;
+
+ drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
+ drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
+ sizeof(struct brcmf_proto_bcdc_dcmd);
+ return 0;
+
+fail:
+ kfree(bcdc);
+ return -ENOMEM;
+}
+
+void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
+{
+ kfree(drvr->proto->pd);
+ drvr->proto->pd = NULL;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
index 6003179c0ceb..6003179c0ceb 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
new file mode 100644
index 000000000000..53637399bb99
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -0,0 +1,1378 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* ****************** SDIO CARD Interface Functions **************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/brcmfmac-sdio.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <net/cfg80211.h>
+
+#include <defs.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <chipcommon.h>
+#include <soc.h>
+#include "chip.h"
+#include "bus.h"
+#include "debug.h"
+#include "sdio.h"
+#include "of.h"
+#include "core.h"
+#include "common.h"
+
+#define SDIOH_API_ACCESS_RETRY_LIMIT 2
+
+#define DMA_ALIGN_MASK 0x03
+
+#define SDIO_FUNC1_BLOCKSIZE 64
+#define SDIO_FUNC2_BLOCKSIZE 512
+/* Maximum milliseconds to wait for F2 to come up */
+#define SDIO_WAIT_F2RDY 3000
+
+#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */
+
+struct brcmf_sdiod_freezer {
+ atomic_t freezing;
+ atomic_t thread_count;
+ u32 frozen_count;
+ wait_queue_head_t thread_freeze;
+ struct completion resumed;
+};
+
+static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev_id);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_dbg(INTR, "OOB intr triggered\n");
+
+ /* out-of-band interrupt is level-triggered which won't
+ * be cleared until dpc
+ */
+ if (sdiodev->irq_en) {
+ disable_irq_nosync(irq);
+ sdiodev->irq_en = false;
+ }
+
+ brcmf_sdio_isr(sdiodev->bus);
+
+ return IRQ_HANDLED;
+}
+
+static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_dbg(INTR, "IB intr triggered\n");
+
+ brcmf_sdio_isr(sdiodev->bus);
+}
+
+/* dummy handler for SDIO function 2 interrupt */
+static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func)
+{
+}
+
+int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
+{
+ int ret = 0;
+ u8 data;
+ u32 addr, gpiocontrol;
+ unsigned long flags;
+
+ if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
+ brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
+ sdiodev->pdata->oob_irq_nr);
+ ret = request_irq(sdiodev->pdata->oob_irq_nr,
+ brcmf_sdiod_oob_irqhandler,
+ sdiodev->pdata->oob_irq_flags,
+ "brcmf_oob_intr",
+ &sdiodev->func[1]->dev);
+ if (ret != 0) {
+ brcmf_err("request_irq failed %d\n", ret);
+ return ret;
+ }
+ sdiodev->oob_irq_requested = true;
+ spin_lock_init(&sdiodev->irq_en_lock);
+ spin_lock_irqsave(&sdiodev->irq_en_lock, flags);
+ sdiodev->irq_en = true;
+ spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
+
+ ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ if (ret != 0) {
+ brcmf_err("enable_irq_wake failed %d\n", ret);
+ return ret;
+ }
+ sdiodev->irq_wake = true;
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) {
+ /* assign GPIO to SDIO core */
+ addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol);
+ gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret);
+ gpiocontrol |= 0x2;
+ brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret);
+
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf,
+ &ret);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret);
+ }
+
+ /* must configure SDIO_CCCR_IENx to enable irq */
+ data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
+ data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
+
+ /* redirect, configure and enable io for interrupt signal */
+ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
+ if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
+ data |= SDIO_SEPINT_ACT_HI;
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
+
+ sdio_release_host(sdiodev->func[1]);
+ } else {
+ brcmf_dbg(SDIO, "Entering\n");
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler);
+ sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler);
+ sdio_release_host(sdiodev->func[1]);
+ }
+
+ return 0;
+}
+
+int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
+{
+ brcmf_dbg(SDIO, "Entering\n");
+
+ if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
+ sdio_claim_host(sdiodev->func[1]);
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
+ sdio_release_host(sdiodev->func[1]);
+
+ if (sdiodev->oob_irq_requested) {
+ sdiodev->oob_irq_requested = false;
+ if (sdiodev->irq_wake) {
+ disable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ sdiodev->irq_wake = false;
+ }
+ free_irq(sdiodev->pdata->oob_irq_nr,
+ &sdiodev->func[1]->dev);
+ sdiodev->irq_en = false;
+ }
+ } else {
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_release_irq(sdiodev->func[2]);
+ sdio_release_irq(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+ }
+
+ return 0;
+}
+
+void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
+ enum brcmf_sdiod_state state)
+{
+ if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM ||
+ state == sdiodev->state)
+ return;
+
+ brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state);
+ switch (sdiodev->state) {
+ case BRCMF_SDIOD_DATA:
+ /* any other state means bus interface is down */
+ brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN);
+ break;
+ case BRCMF_SDIOD_DOWN:
+ /* transition from DOWN to DATA means bus interface is up */
+ if (state == BRCMF_SDIOD_DATA)
+ brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP);
+ break;
+ default:
+ break;
+ }
+ sdiodev->state = state;
+}
+
+static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func,
+ uint regaddr, u8 byte)
+{
+ int err_ret;
+
+ /*
+ * Can only directly write to some F0 registers.
+ * Handle CCCR_IENx and CCCR_ABORT command
+ * as a special case.
+ */
+ if ((regaddr == SDIO_CCCR_ABORT) ||
+ (regaddr == SDIO_CCCR_IENx))
+ sdio_writeb(func, byte, regaddr, &err_ret);
+ else
+ sdio_f0_writeb(func, byte, regaddr, &err_ret);
+
+ return err_ret;
+}
+
+static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
+ u32 addr, u8 regsz, void *data, bool write)
+{
+ struct sdio_func *func;
+ int ret;
+
+ brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+ write, fn, addr, regsz);
+
+ /* only allow byte access on F0 */
+ if (WARN_ON(regsz > 1 && !fn))
+ return -EINVAL;
+ func = sdiodev->func[fn];
+
+ switch (regsz) {
+ case sizeof(u8):
+ if (write) {
+ if (fn)
+ sdio_writeb(func, *(u8 *)data, addr, &ret);
+ else
+ ret = brcmf_sdiod_f0_writeb(func, addr,
+ *(u8 *)data);
+ } else {
+ if (fn)
+ *(u8 *)data = sdio_readb(func, addr, &ret);
+ else
+ *(u8 *)data = sdio_f0_readb(func, addr, &ret);
+ }
+ break;
+ case sizeof(u16):
+ if (write)
+ sdio_writew(func, *(u16 *)data, addr, &ret);
+ else
+ *(u16 *)data = sdio_readw(func, addr, &ret);
+ break;
+ case sizeof(u32):
+ if (write)
+ sdio_writel(func, *(u32 *)data, addr, &ret);
+ else
+ *(u32 *)data = sdio_readl(func, addr, &ret);
+ break;
+ default:
+ brcmf_err("invalid size: %d\n", regsz);
+ break;
+ }
+
+ if (ret)
+ brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", fn, addr, ret);
+
+ return ret;
+}
+
+static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
+ u8 regsz, void *data, bool write)
+{
+ u8 func;
+ s32 retry = 0;
+ int ret;
+
+ if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
+ return -ENOMEDIUM;
+
+ /*
+ * figure out how to read the register based on address range
+ * 0x00 ~ 0x7FF: function 0 CCCR and FBR
+ * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
+ * The rest: function 1 silicon backplane core registers
+ */
+ if ((addr & ~REG_F0_REG_MASK) == 0)
+ func = SDIO_FUNC_0;
+ else
+ func = SDIO_FUNC_1;
+
+ do {
+ if (!write)
+ memset(data, 0, regsz);
+ /* for retry wait for 1 ms till bus get settled down */
+ if (retry)
+ usleep_range(1000, 2000);
+ ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz,
+ data, write);
+ } while (ret != 0 && ret != -ENOMEDIUM &&
+ retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
+
+ if (ret == -ENOMEDIUM)
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
+ else if (ret != 0) {
+ /*
+ * SleepCSR register access can fail when
+ * waking up the device so reduce this noise
+ * in the logs.
+ */
+ if (addr != SBSDIO_FUNC1_SLEEPCSR)
+ brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", func, addr, ret);
+ else
+ brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", func, addr, ret);
+ }
+ return ret;
+}
+
+static int
+brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
+{
+ int err = 0, i;
+ u8 addr[3];
+
+ if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
+ return -ENOMEDIUM;
+
+ addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
+ addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK;
+ addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK;
+
+ for (i = 0; i < 3; i++) {
+ err = brcmf_sdiod_regrw_helper(sdiodev,
+ SBSDIO_FUNC1_SBADDRLOW + i,
+ sizeof(u8), &addr[i], true);
+ if (err) {
+ brcmf_err("failed at addr: 0x%0x\n",
+ SBSDIO_FUNC1_SBADDRLOW + i);
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int
+brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
+{
+ uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ if (bar0 != sdiodev->sbwad) {
+ err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0);
+ if (err)
+ return err;
+
+ sdiodev->sbwad = bar0;
+ }
+
+ *addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+ if (width == 4)
+ *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ return 0;
+}
+
+u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
+{
+ u8 data;
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ false);
+ brcmf_dbg(SDIO, "data:0x%02x\n", data);
+
+ if (ret)
+ *ret = retval;
+
+ return data;
+}
+
+u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
+{
+ u32 data;
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
+ retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
+ if (retval)
+ goto done;
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ false);
+ brcmf_dbg(SDIO, "data:0x%08x\n", data);
+
+done:
+ if (ret)
+ *ret = retval;
+
+ return data;
+}
+
+void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
+ u8 data, int *ret)
+{
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ true);
+ if (ret)
+ *ret = retval;
+}
+
+void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
+ u32 data, int *ret)
+{
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
+ retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
+ if (retval)
+ goto done;
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ true);
+
+done:
+ if (ret)
+ *ret = retval;
+}
+
+static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+ bool write, u32 addr, struct sk_buff *pkt)
+{
+ unsigned int req_sz;
+ int err;
+
+ /* Single skb use the standard mmc interface */
+ req_sz = pkt->len + 3;
+ req_sz &= (uint)~3;
+
+ if (write)
+ err = sdio_memcpy_toio(sdiodev->func[fn], addr,
+ ((u8 *)(pkt->data)), req_sz);
+ else if (fn == 1)
+ err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)),
+ addr, req_sz);
+ else
+ /* function 2 read is FIFO operation */
+ err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr,
+ req_sz);
+ if (err == -ENOMEDIUM)
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
+ return err;
+}
+
+/**
+ * brcmf_sdiod_sglist_rw - 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_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
+ bool write, u32 addr,
+ struct sk_buff_head *pktlist)
+{
+ unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
+ unsigned int max_req_sz, orig_offset, dst_offset;
+ unsigned short max_seg_cnt, seg_sz;
+ unsigned char *pkt_data, *orig_data, *dst_data;
+ struct sk_buff *pkt_next = NULL, *local_pkt_next;
+ struct sk_buff_head local_list, *target_list;
+ struct mmc_request mmc_req;
+ struct mmc_command mmc_cmd;
+ struct mmc_data mmc_dat;
+ struct scatterlist *sgl;
+ int ret = 0;
+
+ if (!pktlist->qlen)
+ return -EINVAL;
+
+ target_list = pktlist;
+ /* for host with broken sg support, prepare a page aligned list */
+ __skb_queue_head_init(&local_list);
+ if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
+ req_sz = 0;
+ skb_queue_walk(pktlist, pkt_next)
+ req_sz += pkt_next->len;
+ req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize);
+ while (req_sz > PAGE_SIZE) {
+ pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE);
+ if (pkt_next == NULL) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ __skb_queue_tail(&local_list, pkt_next);
+ req_sz -= PAGE_SIZE;
+ }
+ pkt_next = brcmu_pkt_buf_get_skb(req_sz);
+ if (pkt_next == NULL) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ __skb_queue_tail(&local_list, pkt_next);
+ target_list = &local_list;
+ }
+
+ func_blk_sz = sdiodev->func[fn]->cur_blksize;
+ max_req_sz = sdiodev->max_request_size;
+ max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count,
+ target_list->qlen);
+ seg_sz = target_list->qlen;
+ pkt_offset = 0;
+ pkt_next = target_list->next;
+
+ memset(&mmc_req, 0, sizeof(struct mmc_request));
+ memset(&mmc_cmd, 0, sizeof(struct mmc_command));
+ memset(&mmc_dat, 0, sizeof(struct mmc_data));
+
+ mmc_dat.sg = sdiodev->sgtable.sgl;
+ mmc_dat.blksz = func_blk_sz;
+ mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+ mmc_cmd.opcode = SD_IO_RW_EXTENDED;
+ mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */
+ mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */
+ mmc_cmd.arg |= 1<<27; /* block mode */
+ /* for function 1 the addr will be incremented */
+ mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
+ mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+ mmc_req.cmd = &mmc_cmd;
+ mmc_req.data = &mmc_dat;
+
+ while (seg_sz) {
+ req_sz = 0;
+ sg_cnt = 0;
+ sgl = sdiodev->sgtable.sgl;
+ /* prep sg table */
+ while (pkt_next != (struct sk_buff *)target_list) {
+ pkt_data = pkt_next->data + pkt_offset;
+ sg_data_sz = pkt_next->len - pkt_offset;
+ if (sg_data_sz > sdiodev->max_segment_size)
+ sg_data_sz = sdiodev->max_segment_size;
+ if (sg_data_sz > max_req_sz - req_sz)
+ sg_data_sz = max_req_sz - req_sz;
+
+ 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_cnt)
+ 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);
+ ret = -ENOTBLK;
+ goto exit;
+ }
+
+ mmc_dat.sg_len = sg_cnt;
+ mmc_dat.blocks = req_sz / func_blk_sz;
+ mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */
+ mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */
+ /* incrementing addr for function 1 */
+ if (fn == 1)
+ addr += req_sz;
+
+ mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
+ mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req);
+
+ ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
+ if (ret == -ENOMEDIUM) {
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
+ break;
+ } else if (ret != 0) {
+ brcmf_err("CMD53 sg block %s failed %d\n",
+ write ? "write" : "read", ret);
+ ret = -EIO;
+ break;
+ }
+ }
+
+ if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
+ local_pkt_next = local_list.next;
+ orig_offset = 0;
+ skb_queue_walk(pktlist, pkt_next) {
+ dst_offset = 0;
+ do {
+ req_sz = local_pkt_next->len - orig_offset;
+ req_sz = min_t(uint, pkt_next->len - dst_offset,
+ req_sz);
+ orig_data = local_pkt_next->data + orig_offset;
+ dst_data = pkt_next->data + dst_offset;
+ memcpy(dst_data, orig_data, req_sz);
+ orig_offset += req_sz;
+ dst_offset += req_sz;
+ if (orig_offset == local_pkt_next->len) {
+ orig_offset = 0;
+ local_pkt_next = local_pkt_next->next;
+ }
+ if (dst_offset == pkt_next->len)
+ break;
+ } while (!skb_queue_empty(&local_list));
+ }
+ }
+
+exit:
+ sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents);
+ while ((pkt_next = __skb_dequeue(&local_list)) != NULL)
+ brcmu_pkt_buf_free_skb(pkt_next);
+
+ return ret;
+}
+
+int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
+{
+ struct sk_buff *mypkt;
+ int err;
+
+ mypkt = brcmu_pkt_buf_get_skb(nbytes);
+ if (!mypkt) {
+ brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
+ nbytes);
+ return -EIO;
+ }
+
+ err = brcmf_sdiod_recv_pkt(sdiodev, mypkt);
+ if (!err)
+ memcpy(buf, mypkt->data, nbytes);
+
+ brcmu_pkt_buf_free_skb(mypkt);
+ return err;
+}
+
+int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt)
+{
+ u32 addr = sdiodev->sbwad;
+ int err = 0;
+
+ brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+ if (err)
+ goto done;
+
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt);
+
+done:
+ return err;
+}
+
+int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq, uint totlen)
+{
+ struct sk_buff *glom_skb;
+ struct sk_buff *skb;
+ u32 addr = sdiodev->sbwad;
+ int err = 0;
+
+ brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n",
+ addr, pktq->qlen);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+ if (err)
+ goto done;
+
+ if (pktq->qlen == 1)
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
+ pktq->next);
+ else if (!sdiodev->sg_support) {
+ glom_skb = brcmu_pkt_buf_get_skb(totlen);
+ if (!glom_skb)
+ return -ENOMEM;
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
+ glom_skb);
+ if (err)
+ goto done;
+
+ skb_queue_walk(pktq, skb) {
+ memcpy(skb->data, glom_skb->data, skb->len);
+ skb_pull(glom_skb, skb->len);
+ }
+ } else
+ err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr,
+ pktq);
+
+done:
+ return err;
+}
+
+int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
+{
+ struct sk_buff *mypkt;
+ u32 addr = sdiodev->sbwad;
+ int err;
+
+ mypkt = brcmu_pkt_buf_get_skb(nbytes);
+ if (!mypkt) {
+ brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
+ nbytes);
+ return -EIO;
+ }
+
+ memcpy(mypkt->data, buf, nbytes);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+
+ if (!err)
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr,
+ mypkt);
+
+ brcmu_pkt_buf_free_skb(mypkt);
+ return err;
+
+}
+
+int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq)
+{
+ struct sk_buff *skb;
+ u32 addr = sdiodev->sbwad;
+ int err;
+
+ brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+ if (err)
+ return err;
+
+ if (pktq->qlen == 1 || !sdiodev->sg_support)
+ skb_queue_walk(pktq, skb) {
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true,
+ addr, skb);
+ if (err)
+ break;
+ }
+ else
+ err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr,
+ pktq);
+
+ return err;
+}
+
+int
+brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+ u8 *data, uint size)
+{
+ int bcmerror = 0;
+ struct sk_buff *pkt;
+ u32 sdaddr;
+ uint dsize;
+
+ dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ pkt = dev_alloc_skb(dsize);
+ if (!pkt) {
+ brcmf_err("dev_alloc_skb failed: len %d\n", dsize);
+ return -EIO;
+ }
+ pkt->priority = 0;
+
+ /* Determine initial transfer parameters */
+ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
+ if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
+ dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
+ else
+ dsize = size;
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ /* Do the transfer(s) */
+ while (size) {
+ /* Set the backplane window to include the start address */
+ bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address);
+ if (bcmerror)
+ break;
+
+ brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
+ write ? "write" : "read", dsize,
+ sdaddr, address & SBSDIO_SBWINDOW_MASK);
+
+ sdaddr &= SBSDIO_SB_OFT_ADDR_MASK;
+ sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ skb_put(pkt, dsize);
+ if (write)
+ memcpy(pkt->data, data, dsize);
+ bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write,
+ sdaddr, pkt);
+ if (bcmerror) {
+ brcmf_err("membytes transfer failed\n");
+ break;
+ }
+ if (!write)
+ memcpy(data, pkt->data, dsize);
+ skb_trim(pkt, 0);
+
+ /* Adjust for next transfer (if any) */
+ size -= dsize;
+ if (size) {
+ data += dsize;
+ address += dsize;
+ sdaddr = 0;
+ dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ }
+ }
+
+ dev_kfree_skb(pkt);
+
+ /* Return the window to backplane enumeration space for core access */
+ if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad))
+ brcmf_err("FAILED to set window back to 0x%x\n",
+ sdiodev->sbwad);
+
+ sdio_release_host(sdiodev->func[1]);
+
+ return bcmerror;
+}
+
+int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
+{
+ char t_func = (char)fn;
+ brcmf_dbg(SDIO, "Enter\n");
+
+ /* issue abort cmd52 command through F0 */
+ brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT,
+ sizeof(t_func), &t_func, true);
+
+ brcmf_dbg(SDIO, "Exit\n");
+ return 0;
+}
+
+static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
+{
+ uint nents;
+ int err;
+
+ if (!sdiodev->sg_support)
+ return;
+
+ nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE,
+ sdiodev->bus_if->drvr->settings->sdiod_txglomsz);
+ nents += (nents >> 4) + 1;
+
+ WARN_ON(nents > sdiodev->max_segment_count);
+
+ brcmf_dbg(TRACE, "nents=%d\n", nents);
+ err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL);
+ if (err < 0) {
+ brcmf_err("allocation failed: disable scatter-gather");
+ sdiodev->sg_support = false;
+ }
+
+ sdiodev->txglomsz = sdiodev->bus_if->drvr->settings->sdiod_txglomsz;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
+{
+ sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL);
+ if (!sdiodev->freezer)
+ return -ENOMEM;
+ atomic_set(&sdiodev->freezer->thread_count, 0);
+ atomic_set(&sdiodev->freezer->freezing, 0);
+ init_waitqueue_head(&sdiodev->freezer->thread_freeze);
+ init_completion(&sdiodev->freezer->resumed);
+ return 0;
+}
+
+static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
+{
+ if (sdiodev->freezer) {
+ WARN_ON(atomic_read(&sdiodev->freezer->freezing));
+ kfree(sdiodev->freezer);
+ }
+}
+
+static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev)
+{
+ atomic_t *expect = &sdiodev->freezer->thread_count;
+ int res = 0;
+
+ sdiodev->freezer->frozen_count = 0;
+ reinit_completion(&sdiodev->freezer->resumed);
+ atomic_set(&sdiodev->freezer->freezing, 1);
+ brcmf_sdio_trigger_dpc(sdiodev->bus);
+ wait_event(sdiodev->freezer->thread_freeze,
+ atomic_read(expect) == sdiodev->freezer->frozen_count);
+ sdio_claim_host(sdiodev->func[1]);
+ res = brcmf_sdio_sleep(sdiodev->bus, true);
+ sdio_release_host(sdiodev->func[1]);
+ return res;
+}
+
+static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev)
+{
+ sdio_claim_host(sdiodev->func[1]);
+ brcmf_sdio_sleep(sdiodev->bus, false);
+ sdio_release_host(sdiodev->func[1]);
+ atomic_set(&sdiodev->freezer->freezing, 0);
+ complete_all(&sdiodev->freezer->resumed);
+}
+
+bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
+{
+ return atomic_read(&sdiodev->freezer->freezing);
+}
+
+void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
+{
+ if (!brcmf_sdiod_freezing(sdiodev))
+ return;
+ sdiodev->freezer->frozen_count++;
+ wake_up(&sdiodev->freezer->thread_freeze);
+ wait_for_completion(&sdiodev->freezer->resumed);
+}
+
+void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
+{
+ atomic_inc(&sdiodev->freezer->thread_count);
+}
+
+void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
+{
+ atomic_dec(&sdiodev->freezer->thread_count);
+}
+#else
+static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
+{
+ return 0;
+}
+
+static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
+{
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
+{
+ sdiodev->state = BRCMF_SDIOD_DOWN;
+ if (sdiodev->bus) {
+ brcmf_sdio_remove(sdiodev->bus);
+ sdiodev->bus = NULL;
+ }
+
+ brcmf_sdiod_freezer_detach(sdiodev);
+
+ /* Disable Function 2 */
+ sdio_claim_host(sdiodev->func[2]);
+ sdio_disable_func(sdiodev->func[2]);
+ sdio_release_host(sdiodev->func[2]);
+
+ /* Disable Function 1 */
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_disable_func(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+
+ sg_free_table(&sdiodev->sgtable);
+ sdiodev->sbwad = 0;
+
+ pm_runtime_allow(sdiodev->func[1]->card->host->parent);
+ return 0;
+}
+
+static void brcmf_sdiod_host_fixup(struct mmc_host *host)
+{
+ /* runtime-pm powers off the device */
+ pm_runtime_forbid(host->parent);
+ /* avoid removal detection upon resume */
+ host->caps |= MMC_CAP_NONREMOVABLE;
+}
+
+static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
+{
+ struct sdio_func *func;
+ struct mmc_host *host;
+ uint max_blocks;
+ int ret = 0;
+
+ sdiodev->num_funcs = 2;
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE);
+ if (ret) {
+ brcmf_err("Failed to set F1 blocksize\n");
+ sdio_release_host(sdiodev->func[1]);
+ goto out;
+ }
+ ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE);
+ if (ret) {
+ brcmf_err("Failed to set F2 blocksize\n");
+ sdio_release_host(sdiodev->func[1]);
+ goto out;
+ }
+
+ /* increase F2 timeout */
+ sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY;
+
+ /* Enable Function 1 */
+ ret = sdio_enable_func(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+ if (ret) {
+ brcmf_err("Failed to enable F1: err=%d\n", ret);
+ goto out;
+ }
+
+ /*
+ * determine host related variables after brcmf_sdiod_probe()
+ * as func->cur_blksize is properly set and F2 init has been
+ * completed successfully.
+ */
+ func = sdiodev->func[2];
+ host = func->card->host;
+ sdiodev->sg_support = host->max_segs > 1;
+ max_blocks = min_t(uint, host->max_blk_count, 511u);
+ sdiodev->max_request_size = min_t(uint, host->max_req_size,
+ max_blocks * func->cur_blksize);
+ sdiodev->max_segment_count = min_t(uint, host->max_segs,
+ SG_MAX_SINGLE_ALLOC);
+ sdiodev->max_segment_size = host->max_seg_size;
+
+ /* allocate scatter-gather table. sg support
+ * will be disabled upon allocation failure.
+ */
+ brcmf_sdiod_sgtable_alloc(sdiodev);
+
+ ret = brcmf_sdiod_freezer_attach(sdiodev);
+ if (ret)
+ goto out;
+
+ /* try to attach to the target device */
+ sdiodev->bus = brcmf_sdio_probe(sdiodev);
+ if (!sdiodev->bus) {
+ ret = -ENODEV;
+ goto out;
+ }
+ brcmf_sdiod_host_fixup(host);
+out:
+ if (ret)
+ brcmf_sdiod_remove(sdiodev);
+
+ return ret;
+}
+
+#define BRCMF_SDIO_DEVICE(dev_id) \
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)}
+
+/* devices we support, null terminated */
+static const struct sdio_device_id brcmf_sdmmc_ids[] = {
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
+ { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
+
+static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
+
+
+static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev,
+ int val)
+{
+#if IS_ENABLED(CONFIG_ACPI)
+ struct acpi_device *adev;
+
+ adev = ACPI_COMPANION(dev);
+ if (adev)
+ adev->flags.power_manageable = 0;
+#endif
+}
+
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int err;
+ struct brcmf_sdio_dev *sdiodev;
+ struct brcmf_bus *bus_if;
+ struct device *dev;
+
+ brcmf_dbg(SDIO, "Enter\n");
+ brcmf_dbg(SDIO, "Class=%x\n", func->class);
+ brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+ brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+ brcmf_dbg(SDIO, "Function#: %d\n", func->num);
+
+ dev = &func->dev;
+ /* prohibit ACPI power management for this device */
+ brcmf_sdiod_acpi_set_power_manageable(dev, 0);
+
+ /* Consume func num 1 but dont do anything with it. */
+ if (func->num == 1)
+ return 0;
+
+ /* Ignore anything but func 2 */
+ if (func->num != 2)
+ return -ENODEV;
+
+ bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
+ if (!bus_if)
+ return -ENOMEM;
+ sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
+ if (!sdiodev) {
+ kfree(bus_if);
+ return -ENOMEM;
+ }
+
+ /* store refs to functions used. mmc_card does
+ * not hold the F0 function pointer.
+ */
+ sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL);
+ sdiodev->func[0]->num = 0;
+ sdiodev->func[1] = func->card->sdio_func[0];
+ sdiodev->func[2] = func;
+
+ sdiodev->bus_if = bus_if;
+ bus_if->bus_priv.sdio = sdiodev;
+ bus_if->proto_type = BRCMF_PROTO_BCDC;
+ dev_set_drvdata(&func->dev, bus_if);
+ dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
+ sdiodev->dev = &sdiodev->func[1]->dev;
+ sdiodev->pdata = brcmfmac_sdio_pdata;
+
+ if (!sdiodev->pdata)
+ brcmf_of_probe(sdiodev);
+
+#ifdef CONFIG_PM_SLEEP
+ /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
+ * is true or when platform data OOB irq is true).
+ */
+ if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
+ ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
+ (sdiodev->pdata && sdiodev->pdata->oob_irq_supported)))
+ bus_if->wowl_supported = true;
+#endif
+
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
+
+ brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
+ err = brcmf_sdiod_probe(sdiodev);
+ if (err) {
+ brcmf_err("F2 error, probe failed %d...\n", err);
+ goto fail;
+ }
+
+ brcmf_dbg(SDIO, "F2 init completed...\n");
+ return 0;
+
+fail:
+ dev_set_drvdata(&func->dev, NULL);
+ dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
+ kfree(sdiodev->func[0]);
+ kfree(sdiodev);
+ kfree(bus_if);
+ return err;
+}
+
+static void brcmf_ops_sdio_remove(struct sdio_func *func)
+{
+ struct brcmf_bus *bus_if;
+ struct brcmf_sdio_dev *sdiodev;
+
+ brcmf_dbg(SDIO, "Enter\n");
+ brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+ brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+ brcmf_dbg(SDIO, "Function: %d\n", func->num);
+
+ if (func->num != 1)
+ return;
+
+ bus_if = dev_get_drvdata(&func->dev);
+ if (bus_if) {
+ sdiodev = bus_if->bus_priv.sdio;
+ brcmf_sdiod_remove(sdiodev);
+
+ dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
+ dev_set_drvdata(&sdiodev->func[2]->dev, NULL);
+
+ kfree(bus_if);
+ kfree(sdiodev->func[0]);
+ kfree(sdiodev);
+ }
+
+ brcmf_dbg(SDIO, "Exit\n");
+}
+
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
+ sdiodev->wowl_enabled = enabled;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmf_ops_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func;
+ struct brcmf_bus *bus_if;
+ struct brcmf_sdio_dev *sdiodev;
+ mmc_pm_flag_t sdio_flags;
+
+ func = container_of(dev, struct sdio_func, dev);
+ brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+ if (func->num != SDIO_FUNC_1)
+ return 0;
+
+
+ bus_if = dev_get_drvdata(dev);
+ sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_sdiod_freezer_on(sdiodev);
+ brcmf_sdio_wd_timer(sdiodev->bus, 0);
+
+ sdio_flags = MMC_PM_KEEP_POWER;
+ if (sdiodev->wowl_enabled) {
+ if (sdiodev->pdata->oob_irq_supported)
+ enable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ else
+ sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
+ }
+ if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
+ brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
+ return 0;
+}
+
+static int brcmf_ops_sdio_resume(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct sdio_func *func = container_of(dev, struct sdio_func, dev);
+
+ brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+ if (func->num != SDIO_FUNC_2)
+ return 0;
+
+ brcmf_sdiod_freezer_off(sdiodev);
+ return 0;
+}
+
+static const struct dev_pm_ops brcmf_sdio_pm_ops = {
+ .suspend = brcmf_ops_sdio_suspend,
+ .resume = brcmf_ops_sdio_resume,
+};
+#endif /* CONFIG_PM_SLEEP */
+
+static struct sdio_driver brcmf_sdmmc_driver = {
+ .probe = brcmf_ops_sdio_probe,
+ .remove = brcmf_ops_sdio_remove,
+ .name = BRCMFMAC_SDIO_PDATA_NAME,
+ .id_table = brcmf_sdmmc_ids,
+ .drv = {
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &brcmf_sdio_pm_ops,
+#endif /* CONFIG_PM_SLEEP */
+ },
+};
+
+static int __init brcmf_sdio_pd_probe(struct platform_device *pdev)
+{
+ brcmf_dbg(SDIO, "Enter\n");
+
+ brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
+
+ if (brcmfmac_sdio_pdata->power_on)
+ brcmfmac_sdio_pdata->power_on();
+
+ return 0;
+}
+
+static int brcmf_sdio_pd_remove(struct platform_device *pdev)
+{
+ brcmf_dbg(SDIO, "Enter\n");
+
+ if (brcmfmac_sdio_pdata->power_off)
+ brcmfmac_sdio_pdata->power_off();
+
+ sdio_unregister_driver(&brcmf_sdmmc_driver);
+
+ return 0;
+}
+
+static struct platform_driver brcmf_sdio_pd = {
+ .remove = brcmf_sdio_pd_remove,
+ .driver = {
+ .name = BRCMFMAC_SDIO_PDATA_NAME,
+ }
+};
+
+void brcmf_sdio_register(void)
+{
+ int ret;
+
+ ret = sdio_register_driver(&brcmf_sdmmc_driver);
+ if (ret)
+ brcmf_err("sdio_register_driver failed: %d\n", ret);
+}
+
+void brcmf_sdio_exit(void)
+{
+ brcmf_dbg(SDIO, "Enter\n");
+
+ if (brcmfmac_sdio_pdata)
+ platform_driver_unregister(&brcmf_sdio_pd);
+ else
+ sdio_unregister_driver(&brcmf_sdmmc_driver);
+}
+
+void __init brcmf_sdio_init(void)
+{
+ int ret;
+
+ brcmf_dbg(SDIO, "Enter\n");
+
+ ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
+ if (ret == -ENODEV)
+ brcmf_dbg(SDIO, "No platform data available.\n");
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
new file mode 100644
index 000000000000..14a70d4b4e86
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <defs.h>
+#include "core.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "btcoex.h"
+#include "p2p.h"
+#include "cfg80211.h"
+
+/* T1 start SCO/eSCO priority suppression */
+#define BRCMF_BTCOEX_OPPR_WIN_TIME msecs_to_jiffies(2000)
+
+/* BT registers values during DHCP */
+#define BRCMF_BT_DHCP_REG50 0x8022
+#define BRCMF_BT_DHCP_REG51 0
+#define BRCMF_BT_DHCP_REG64 0
+#define BRCMF_BT_DHCP_REG65 0
+#define BRCMF_BT_DHCP_REG71 0
+#define BRCMF_BT_DHCP_REG66 0x2710
+#define BRCMF_BT_DHCP_REG41 0x33
+#define BRCMF_BT_DHCP_REG68 0x190
+
+/* number of samples for SCO detection */
+#define BRCMF_BT_SCO_SAMPLES 12
+
+/**
+* enum brcmf_btcoex_state - BT coex DHCP state machine states
+* @BRCMF_BT_DHCP_IDLE: DCHP is idle
+* @BRCMF_BT_DHCP_START: DHCP started, wait before
+* boosting wifi priority
+* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended,
+* boost wifi priority
+* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end,
+* restore defaults
+*/
+enum brcmf_btcoex_state {
+ BRCMF_BT_DHCP_IDLE,
+ BRCMF_BT_DHCP_START,
+ BRCMF_BT_DHCP_OPPR_WIN,
+ BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT
+};
+
+/**
+ * struct brcmf_btcoex_info - BT coex related information
+ * @vif: interface for which request was done.
+ * @timer: timer for DHCP state machine
+ * @timeout: configured timeout.
+ * @timer_on: DHCP timer active
+ * @dhcp_done: DHCP finished before T1/T2 timer expiration
+ * @bt_state: DHCP state machine state
+ * @work: DHCP state machine work
+ * @cfg: driver private data for cfg80211 interface
+ * @reg66: saved value of btc_params 66
+ * @reg41: saved value of btc_params 41
+ * @reg68: saved value of btc_params 68
+ * @saved_regs_part1: flag indicating regs 66,41,68
+ * have been saved
+ * @reg51: saved value of btc_params 51
+ * @reg64: saved value of btc_params 64
+ * @reg65: saved value of btc_params 65
+ * @reg71: saved value of btc_params 71
+ * @saved_regs_part1: flag indicating regs 50,51,64,65,71
+ * have been saved
+ */
+struct brcmf_btcoex_info {
+ struct brcmf_cfg80211_vif *vif;
+ struct timer_list timer;
+ u16 timeout;
+ bool timer_on;
+ bool dhcp_done;
+ enum brcmf_btcoex_state bt_state;
+ struct work_struct work;
+ struct brcmf_cfg80211_info *cfg;
+ u32 reg66;
+ u32 reg41;
+ u32 reg68;
+ bool saved_regs_part1;
+ u32 reg50;
+ u32 reg51;
+ u32 reg64;
+ u32 reg65;
+ u32 reg71;
+ bool saved_regs_part2;
+};
+
+/**
+ * brcmf_btcoex_params_write() - write btc_params firmware variable
+ * @ifp: interface
+ * @addr: btc_params register number
+ * @data: data to write
+ */
+static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data)
+{
+ struct {
+ __le32 addr;
+ __le32 data;
+ } reg_write;
+
+ reg_write.addr = cpu_to_le32(addr);
+ reg_write.data = cpu_to_le32(data);
+ return brcmf_fil_iovar_data_set(ifp, "btc_params",
+ &reg_write, sizeof(reg_write));
+}
+
+/**
+ * brcmf_btcoex_params_read() - read btc_params firmware variable
+ * @ifp: interface
+ * @addr: btc_params register number
+ * @data: read data
+ */
+static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)
+{
+ *data = addr;
+
+ return brcmf_fil_iovar_int_get(ifp, "btc_params", data);
+}
+
+/**
+ * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters
+ * @btci: BT coex info
+ * @trump_sco:
+ * true - set SCO/eSCO parameters for compatibility
+ * during DHCP window
+ * false - restore saved parameter values
+ *
+ * Enhanced BT COEX settings for eSCO compatibility during DHCP window
+ */
+static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
+ bool trump_sco)
+{
+ struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
+
+ if (trump_sco && !btci->saved_regs_part2) {
+ /* this should reduce eSCO agressive
+ * retransmit w/o breaking it
+ */
+
+ /* save current */
+ brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
+ brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
+ brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
+ brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
+ brcmf_btcoex_params_read(ifp, 65, &btci->reg65);
+ brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
+
+ btci->saved_regs_part2 = true;
+ brcmf_dbg(INFO,
+ "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ btci->reg50, btci->reg51, btci->reg64,
+ btci->reg65, btci->reg71);
+
+ /* pacify the eSco */
+ brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50);
+ brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51);
+ brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64);
+ brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65);
+ brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71);
+
+ } else if (btci->saved_regs_part2) {
+ /* restore previously saved bt params */
+ brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
+ brcmf_btcoex_params_write(ifp, 50, btci->reg50);
+ brcmf_btcoex_params_write(ifp, 51, btci->reg51);
+ brcmf_btcoex_params_write(ifp, 64, btci->reg64);
+ brcmf_btcoex_params_write(ifp, 65, btci->reg65);
+ brcmf_btcoex_params_write(ifp, 71, btci->reg71);
+
+ brcmf_dbg(INFO,
+ "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ btci->reg50, btci->reg51, btci->reg64,
+ btci->reg65, btci->reg71);
+
+ btci->saved_regs_part2 = false;
+ } else {
+ brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
+ }
+}
+
+/**
+ * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active
+ * @ifp: interface
+ *
+ * return: true if SCO/eSCO session is active
+ */
+static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
+{
+ int ioc_res = 0;
+ bool res = false;
+ int sco_id_cnt = 0;
+ u32 param27;
+ int i;
+
+ for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) {
+ ioc_res = brcmf_btcoex_params_read(ifp, 27, &param27);
+
+ if (ioc_res < 0) {
+ brcmf_err("ioc read btc params error\n");
+ break;
+ }
+
+ brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
+
+ if ((param27 & 0x6) == 2) { /* count both sco & esco */
+ sco_id_cnt++;
+ }
+
+ if (sco_id_cnt > 2) {
+ brcmf_dbg(INFO,
+ "sco/esco detected, pkt id_cnt:%d samples:%d\n",
+ sco_id_cnt, i);
+ res = true;
+ break;
+ }
+ }
+ brcmf_dbg(TRACE, "exit: result=%d\n", res);
+ return res;
+}
+
+/**
+ * btcmf_btcoex_save_part1() - save first step parameters.
+ */
+static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp = btci->vif->ifp;
+
+ if (!btci->saved_regs_part1) {
+ /* Retrieve and save original reg value */
+ brcmf_btcoex_params_read(ifp, 66, &btci->reg66);
+ brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
+ brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
+ btci->saved_regs_part1 = true;
+ brcmf_dbg(INFO,
+ "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
+ btci->reg66, btci->reg41,
+ btci->reg68);
+ }
+}
+
+/**
+ * brcmf_btcoex_restore_part1() - restore first step parameters.
+ */
+static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp;
+
+ if (btci->saved_regs_part1) {
+ btci->saved_regs_part1 = false;
+ ifp = btci->vif->ifp;
+ brcmf_btcoex_params_write(ifp, 66, btci->reg66);
+ brcmf_btcoex_params_write(ifp, 41, btci->reg41);
+ brcmf_btcoex_params_write(ifp, 68, btci->reg68);
+ brcmf_dbg(INFO,
+ "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
+ btci->reg66, btci->reg41,
+ btci->reg68);
+ }
+}
+
+/**
+ * brcmf_btcoex_timerfunc() - BT coex timer callback
+ */
+static void brcmf_btcoex_timerfunc(ulong data)
+{
+ struct brcmf_btcoex_info *bt_local = (struct brcmf_btcoex_info *)data;
+ brcmf_dbg(TRACE, "enter\n");
+
+ bt_local->timer_on = false;
+ schedule_work(&bt_local->work);
+}
+
+/**
+ * brcmf_btcoex_handler() - BT coex state machine work handler
+ * @work: work
+ */
+static void brcmf_btcoex_handler(struct work_struct *work)
+{
+ struct brcmf_btcoex_info *btci;
+ btci = container_of(work, struct brcmf_btcoex_info, work);
+ if (btci->timer_on) {
+ btci->timer_on = false;
+ del_timer_sync(&btci->timer);
+ }
+
+ switch (btci->bt_state) {
+ case BRCMF_BT_DHCP_START:
+ /* DHCP started provide OPPORTUNITY window
+ to get DHCP address
+ */
+ brcmf_dbg(INFO, "DHCP started\n");
+ btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
+ if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
+ mod_timer(&btci->timer, btci->timer.expires);
+ } else {
+ btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;
+ mod_timer(&btci->timer,
+ jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME);
+ }
+ btci->timer_on = true;
+ break;
+
+ case BRCMF_BT_DHCP_OPPR_WIN:
+ if (btci->dhcp_done) {
+ brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
+ goto idle;
+ }
+
+ /* DHCP is not over yet, start lowering BT priority */
+ brcmf_dbg(INFO, "DHCP T1:%d expired\n",
+ jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME));
+ brcmf_btcoex_boost_wifi(btci, true);
+
+ btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;
+ mod_timer(&btci->timer, jiffies + btci->timeout);
+ btci->timer_on = true;
+ break;
+
+ case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
+ if (btci->dhcp_done)
+ brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
+ else
+ brcmf_dbg(INFO, "DHCP T2:%d expired\n",
+ BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
+
+ goto idle;
+
+ default:
+ brcmf_err("invalid state=%d !!!\n", btci->bt_state);
+ goto idle;
+ }
+
+ return;
+
+idle:
+ btci->bt_state = BRCMF_BT_DHCP_IDLE;
+ btci->timer_on = false;
+ brcmf_btcoex_boost_wifi(btci, false);
+ cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL);
+ brcmf_btcoex_restore_part1(btci);
+ btci->vif = NULL;
+}
+
+/**
+ * brcmf_btcoex_attach() - initialize BT coex data
+ * @cfg: driver private cfg80211 data
+ *
+ * return: 0 on success
+ */
+int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_btcoex_info *btci = NULL;
+ brcmf_dbg(TRACE, "enter\n");
+
+ btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL);
+ if (!btci)
+ return -ENOMEM;
+
+ btci->bt_state = BRCMF_BT_DHCP_IDLE;
+
+ /* Set up timer for BT */
+ btci->timer_on = false;
+ btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME;
+ init_timer(&btci->timer);
+ btci->timer.data = (ulong)btci;
+ btci->timer.function = brcmf_btcoex_timerfunc;
+ btci->cfg = cfg;
+ btci->saved_regs_part1 = false;
+ btci->saved_regs_part2 = false;
+
+ INIT_WORK(&btci->work, brcmf_btcoex_handler);
+
+ cfg->btcoex = btci;
+ return 0;
+}
+
+/**
+ * brcmf_btcoex_detach - clean BT coex data
+ * @cfg: driver private cfg80211 data
+ */
+void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)
+{
+ brcmf_dbg(TRACE, "enter\n");
+
+ if (!cfg->btcoex)
+ return;
+
+ if (cfg->btcoex->timer_on) {
+ cfg->btcoex->timer_on = false;
+ del_timer_sync(&cfg->btcoex->timer);
+ }
+
+ cancel_work_sync(&cfg->btcoex->work);
+
+ brcmf_btcoex_boost_wifi(cfg->btcoex, false);
+ brcmf_btcoex_restore_part1(cfg->btcoex);
+
+ kfree(cfg->btcoex);
+ cfg->btcoex = NULL;
+}
+
+static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp = btci->vif->ifp;
+
+ btcmf_btcoex_save_part1(btci);
+ /* set new regs values */
+ brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66);
+ brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41);
+ brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68);
+ btci->dhcp_done = false;
+ btci->bt_state = BRCMF_BT_DHCP_START;
+ schedule_work(&btci->work);
+ brcmf_dbg(TRACE, "enable BT DHCP Timer\n");
+}
+
+static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
+{
+ /* Stop any bt timer because DHCP session is done */
+ btci->dhcp_done = true;
+ if (btci->timer_on) {
+ brcmf_dbg(INFO, "disable BT DHCP Timer\n");
+ btci->timer_on = false;
+ del_timer_sync(&btci->timer);
+
+ /* schedule worker if transition to IDLE is needed */
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
+ brcmf_dbg(INFO, "bt_state:%d\n",
+ btci->bt_state);
+ schedule_work(&btci->work);
+ }
+ } else {
+ /* Restore original values */
+ brcmf_btcoex_restore_part1(btci);
+ }
+}
+
+/**
+ * brcmf_btcoex_set_mode - set BT coex mode
+ * @cfg: driver private cfg80211 data
+ * @mode: Wifi-Bluetooth coexistence mode
+ *
+ * return: 0 on success
+ */
+int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
+ enum brcmf_btcoex_mode mode, u16 duration)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
+ struct brcmf_btcoex_info *btci = cfg->btcoex;
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
+
+ switch (mode) {
+ case BRCMF_BTCOEX_DISABLED:
+ brcmf_dbg(INFO, "DHCP session starts\n");
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
+ return -EBUSY;
+ /* Start BT timer only for SCO connection */
+ if (brcmf_btcoex_is_sco_active(ifp)) {
+ btci->timeout = msecs_to_jiffies(duration);
+ btci->vif = vif;
+ brcmf_btcoex_dhcp_start(btci);
+ }
+ break;
+
+ case BRCMF_BTCOEX_ENABLED:
+ brcmf_dbg(INFO, "DHCP session ends\n");
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
+ vif == btci->vif) {
+ brcmf_btcoex_dhcp_end(btci);
+ }
+ break;
+ default:
+ brcmf_dbg(INFO, "Unknown mode, ignored\n");
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h
index 19647c68aa9e..19647c68aa9e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
new file mode 100644
index 000000000000..36093f93bfbe
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_BUS_H
+#define BRCMFMAC_BUS_H
+
+#include "debug.h"
+
+/* IDs of the 6 default common rings of msgbuf protocol */
+#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1
+#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2
+#define BRCMF_D2H_MSGRING_TX_COMPLETE 3
+#define BRCMF_D2H_MSGRING_RX_COMPLETE 4
+
+#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2
+#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3
+#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \
+ BRCMF_NROF_D2H_COMMON_MSGRINGS)
+
+/* The level of bus communication with the dongle */
+enum brcmf_bus_state {
+ BRCMF_BUS_DOWN, /* Not ready for frame transfers */
+ BRCMF_BUS_UP /* Ready for frame transfers */
+};
+
+/* The level of bus communication with the dongle */
+enum brcmf_bus_protocol_type {
+ BRCMF_PROTO_BCDC,
+ BRCMF_PROTO_MSGBUF
+};
+
+struct brcmf_bus_dcmd {
+ char *name;
+ char *param;
+ int param_len;
+ struct list_head list;
+};
+
+/**
+ * struct brcmf_bus_ops - bus callback operations.
+ *
+ * @preinit: execute bus/device specific dongle init commands (optional).
+ * @init: prepare for communication with dongle.
+ * @stop: clear pending frames, disable data flow.
+ * @txdata: send a data frame to the dongle. When the data
+ * has been transferred, the common driver must be
+ * notified using brcmf_txcomplete(). The common
+ * driver calls this function with interrupts
+ * disabled.
+ * @txctl: transmit a control request message to dongle.
+ * @rxctl: receive a control response message from dongle.
+ * @gettxq: obtain a reference of bus transmit queue (optional).
+ * @wowl_config: specify if dongle is configured for wowl when going to suspend
+ * @get_ramsize: obtain size of device memory.
+ * @get_memdump: obtain device memory dump in provided buffer.
+ *
+ * This structure provides an abstract interface towards the
+ * bus specific driver. For control messages to common driver
+ * will assure there is only one active transaction. Unless
+ * indicated otherwise these callbacks are mandatory.
+ */
+struct brcmf_bus_ops {
+ int (*preinit)(struct device *dev);
+ void (*stop)(struct device *dev);
+ int (*txdata)(struct device *dev, struct sk_buff *skb);
+ int (*txctl)(struct device *dev, unsigned char *msg, uint len);
+ int (*rxctl)(struct device *dev, unsigned char *msg, uint len);
+ struct pktq * (*gettxq)(struct device *dev);
+ void (*wowl_config)(struct device *dev, bool enabled);
+ size_t (*get_ramsize)(struct device *dev);
+ int (*get_memdump)(struct device *dev, void *data, size_t len);
+};
+
+
+/**
+ * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf.
+ *
+ * @commonrings: commonrings which are always there.
+ * @flowrings: commonrings which are dynamically created and destroyed for data.
+ * @rx_dataoffset: if set then all rx data has this this offset.
+ * @max_rxbufpost: maximum number of buffers to post for rx.
+ * @nrof_flowrings: number of flowrings.
+ */
+struct brcmf_bus_msgbuf {
+ struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS];
+ struct brcmf_commonring **flowrings;
+ u32 rx_dataoffset;
+ u32 max_rxbufpost;
+ u32 nrof_flowrings;
+};
+
+
+/**
+ * struct brcmf_bus - interface structure between common and bus layer
+ *
+ * @bus_priv: pointer to private bus device.
+ * @proto_type: protocol type, bcdc or msgbuf
+ * @dev: device pointer of bus device.
+ * @drvr: public driver information.
+ * @state: operational state of the bus interface.
+ * @maxctl: maximum size for rxctl request message.
+ * @tx_realloc: number of tx packets realloced for headroom.
+ * @dstats: dongle-based statistical data.
+ * @dcmd_list: bus/device specific dongle initialization commands.
+ * @chip: device identifier of the dongle chip.
+ * @wowl_supported: is wowl supported by bus driver.
+ * @chiprev: revision of the dongle chip.
+ */
+struct brcmf_bus {
+ union {
+ struct brcmf_sdio_dev *sdio;
+ struct brcmf_usbdev *usb;
+ struct brcmf_pciedev *pcie;
+ } bus_priv;
+ enum brcmf_bus_protocol_type proto_type;
+ struct device *dev;
+ struct brcmf_pub *drvr;
+ enum brcmf_bus_state state;
+ uint maxctl;
+ unsigned long tx_realloc;
+ u32 chip;
+ u32 chiprev;
+ bool always_use_fws_queue;
+ bool wowl_supported;
+
+ const struct brcmf_bus_ops *ops;
+ struct brcmf_bus_msgbuf *msgbuf;
+};
+
+/*
+ * callback wrappers
+ */
+static inline int brcmf_bus_preinit(struct brcmf_bus *bus)
+{
+ if (!bus->ops->preinit)
+ return 0;
+ return bus->ops->preinit(bus->dev);
+}
+
+static inline void brcmf_bus_stop(struct brcmf_bus *bus)
+{
+ bus->ops->stop(bus->dev);
+}
+
+static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb)
+{
+ return bus->ops->txdata(bus->dev, skb);
+}
+
+static inline
+int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len)
+{
+ return bus->ops->txctl(bus->dev, msg, len);
+}
+
+static inline
+int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len)
+{
+ return bus->ops->rxctl(bus->dev, msg, len);
+}
+
+static inline
+struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus)
+{
+ if (!bus->ops->gettxq)
+ return ERR_PTR(-ENOENT);
+
+ return bus->ops->gettxq(bus->dev);
+}
+
+static inline
+void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled)
+{
+ if (bus->ops->wowl_config)
+ bus->ops->wowl_config(bus->dev, enabled);
+}
+
+static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus)
+{
+ if (!bus->ops->get_ramsize)
+ return 0;
+
+ return bus->ops->get_ramsize(bus->dev);
+}
+
+static inline
+int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
+{
+ if (!bus->ops->get_memdump)
+ return -EOPNOTSUPP;
+
+ return bus->ops->get_memdump(bus->dev, data, len);
+}
+
+/*
+ * interface functions from common layer
+ */
+
+bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt,
+ int prec);
+
+/* Receive frame for delivery to OS. Callee disposes of rxp. */
+void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
+
+/* Indication from bus module regarding presence/insertion of dongle. */
+int brcmf_attach(struct device *dev);
+/* Indication from bus module regarding removal/absence of dongle */
+void brcmf_detach(struct device *dev);
+/* Indication from bus module that dongle should be reset */
+void brcmf_dev_reset(struct device *dev);
+/* Indication from bus module to change flow-control state */
+void brcmf_txflowblock(struct device *dev, bool state);
+
+/* Notify the bus has transferred the tx packet to firmware */
+void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success);
+
+/* Configure the "global" bus state used by upper layers */
+void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
+
+int brcmf_bus_start(struct device *dev);
+s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
+void brcmf_bus_add_txhdrlen(struct device *dev, uint len);
+
+#ifdef CONFIG_BRCMFMAC_SDIO
+void brcmf_sdio_exit(void);
+void brcmf_sdio_init(void);
+void brcmf_sdio_register(void);
+#endif
+#ifdef CONFIG_BRCMFMAC_USB
+void brcmf_usb_exit(void);
+void brcmf_usb_register(void);
+#endif
+
+#endif /* BRCMFMAC_BUS_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
new file mode 100644
index 000000000000..7b01e4ddb315
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -0,0 +1,6626 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
+
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <net/cfg80211.h>
+#include <net/netlink.h>
+
+#include <brcmu_utils.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "fwil_types.h"
+#include "p2p.h"
+#include "btcoex.h"
+#include "cfg80211.h"
+#include "feature.h"
+#include "fwil.h"
+#include "proto.h"
+#include "vendor.h"
+#include "bus.h"
+#include "common.h"
+
+#define BRCMF_SCAN_IE_LEN_MAX 2048
+#define BRCMF_PNO_VERSION 2
+#define BRCMF_PNO_TIME 30
+#define BRCMF_PNO_REPEAT 4
+#define BRCMF_PNO_FREQ_EXPO_MAX 3
+#define BRCMF_PNO_MAX_PFN_COUNT 16
+#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
+#define BRCMF_PNO_HIDDEN_BIT 2
+#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
+#define BRCMF_PNO_SCAN_COMPLETE 1
+#define BRCMF_PNO_SCAN_INCOMPLETE 0
+
+#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
+#define WPA_OUI_TYPE 1
+#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
+#define WME_OUI_TYPE 2
+#define WPS_OUI_TYPE 4
+
+#define VS_IE_FIXED_HDR_LEN 6
+#define WPA_IE_VERSION_LEN 2
+#define WPA_IE_MIN_OUI_LEN 4
+#define WPA_IE_SUITE_COUNT_LEN 2
+
+#define WPA_CIPHER_NONE 0 /* None */
+#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
+#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
+#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
+#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
+
+#define RSN_AKM_NONE 0 /* None (IBSS) */
+#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
+#define RSN_AKM_PSK 2 /* Pre-shared Key */
+#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
+#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
+
+#define VNDR_IE_CMD_LEN 4 /* length of the set command
+ * string :"add", "del" (+ NUL)
+ */
+#define VNDR_IE_COUNT_OFFSET 4
+#define VNDR_IE_PKTFLAG_OFFSET 8
+#define VNDR_IE_VSIE_OFFSET 12
+#define VNDR_IE_HDR_SIZE 12
+#define VNDR_IE_PARSE_LIMIT 5
+
+#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
+#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
+
+#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
+#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
+#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
+
+#define BRCMF_SCAN_CHANNEL_TIME 40
+#define BRCMF_SCAN_UNASSOC_TIME 40
+#define BRCMF_SCAN_PASSIVE_TIME 120
+
+#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)
+
+#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
+ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
+
+static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
+{
+ if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
+ brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
+ vif->sme_state);
+ return false;
+ }
+ return true;
+}
+
+#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
+#define RATETAB_ENT(_rateid, _flags) \
+ { \
+ .bitrate = RATE_TO_BASE100KBPS(_rateid), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+ }
+
+static struct ieee80211_rate __wl_rates[] = {
+ RATETAB_ENT(BRCM_RATE_1M, 0),
+ RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_6M, 0),
+ RATETAB_ENT(BRCM_RATE_9M, 0),
+ RATETAB_ENT(BRCM_RATE_12M, 0),
+ RATETAB_ENT(BRCM_RATE_18M, 0),
+ RATETAB_ENT(BRCM_RATE_24M, 0),
+ RATETAB_ENT(BRCM_RATE_36M, 0),
+ RATETAB_ENT(BRCM_RATE_48M, 0),
+ RATETAB_ENT(BRCM_RATE_54M, 0),
+};
+
+#define wl_g_rates (__wl_rates + 0)
+#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
+#define wl_a_rates (__wl_rates + 4)
+#define wl_a_rates_size (wl_g_rates_size - 4)
+
+#define CHAN2G(_channel, _freq) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = IEEE80211_CHAN_DISABLED, \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = IEEE80211_CHAN_DISABLED, \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel __wl_2ghz_channels[] = {
+ CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
+ CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
+ CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
+ CHAN2G(13, 2472), CHAN2G(14, 2484)
+};
+
+static struct ieee80211_channel __wl_5ghz_channels[] = {
+ CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
+ CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
+ CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
+ CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
+ CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
+ CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
+};
+
+/* Band templates duplicated per wiphy. The channel info
+ * above is added to the band during setup.
+ */
+static const struct ieee80211_supported_band __wl_band_2ghz = {
+ .band = IEEE80211_BAND_2GHZ,
+ .bitrates = wl_g_rates,
+ .n_bitrates = wl_g_rates_size,
+};
+
+static const struct ieee80211_supported_band __wl_band_5ghz = {
+ .band = IEEE80211_BAND_5GHZ,
+ .bitrates = wl_a_rates,
+ .n_bitrates = wl_a_rates_size,
+};
+
+/* This is to override regulatory domains defined in cfg80211 module (reg.c)
+ * By default world regulatory domain defined in reg.c puts the flags
+ * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
+ * With respect to these flags, wpa_supplicant doesn't * start p2p
+ * operations on 5GHz channels. All the changes in world regulatory
+ * domain are to be done here.
+ */
+static const struct ieee80211_regdomain brcmf_regdom = {
+ .n_reg_rules = 4,
+ .alpha2 = "99",
+ .reg_rules = {
+ /* IEEE 802.11b/g, channels 1..11 */
+ REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
+ /* If any */
+ /* IEEE 802.11 channel 14 - Only JP enables
+ * this and for 802.11b only
+ */
+ REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
+ /* IEEE 802.11a, channel 36..64 */
+ REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
+ /* IEEE 802.11a, channel 100..165 */
+ REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
+};
+
+static const u32 __wl_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+};
+
+/* Vendor specific ie. id = 221, oui and type defines exact ie */
+struct brcmf_vs_tlv {
+ u8 id;
+ u8 len;
+ u8 oui[3];
+ u8 oui_type;
+};
+
+struct parsed_vndr_ie_info {
+ u8 *ie_ptr;
+ u32 ie_len; /* total length including id & length field */
+ struct brcmf_vs_tlv vndrie;
+};
+
+struct parsed_vndr_ies {
+ u32 count;
+ struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
+};
+
+static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct cfg80211_chan_def *ch)
+{
+ struct brcmu_chan ch_inf;
+ s32 primary_offset;
+
+ brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
+ ch->chan->center_freq, ch->center_freq1, ch->width);
+ ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
+ primary_offset = ch->center_freq1 - ch->chan->center_freq;
+ switch (ch->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ch_inf.bw = BRCMU_CHAN_BW_20;
+ WARN_ON(primary_offset != 0);
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ ch_inf.bw = BRCMU_CHAN_BW_40;
+ if (primary_offset < 0)
+ ch_inf.sb = BRCMU_CHAN_SB_U;
+ else
+ ch_inf.sb = BRCMU_CHAN_SB_L;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ ch_inf.bw = BRCMU_CHAN_BW_80;
+ if (primary_offset < 0) {
+ if (primary_offset < -CH_10MHZ_APART)
+ ch_inf.sb = BRCMU_CHAN_SB_UU;
+ else
+ ch_inf.sb = BRCMU_CHAN_SB_UL;
+ } else {
+ if (primary_offset > CH_10MHZ_APART)
+ ch_inf.sb = BRCMU_CHAN_SB_LL;
+ else
+ ch_inf.sb = BRCMU_CHAN_SB_LU;
+ }
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ default:
+ WARN_ON_ONCE(1);
+ }
+ switch (ch->chan->band) {
+ case IEEE80211_BAND_2GHZ:
+ ch_inf.band = BRCMU_CHAN_BAND_2G;
+ break;
+ case IEEE80211_BAND_5GHZ:
+ ch_inf.band = BRCMU_CHAN_BAND_5G;
+ break;
+ case IEEE80211_BAND_60GHZ:
+ default:
+ WARN_ON_ONCE(1);
+ }
+ d11inf->encchspec(&ch_inf);
+
+ return ch_inf.chspec;
+}
+
+u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct ieee80211_channel *ch)
+{
+ struct brcmu_chan ch_inf;
+
+ ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
+ ch_inf.bw = BRCMU_CHAN_BW_20;
+ d11inf->encchspec(&ch_inf);
+
+ return ch_inf.chspec;
+}
+
+/* Traverse a string of 1-byte tag/1-byte length/variable-length value
+ * triples, returning a pointer to the substring whose first element
+ * matches tag
+ */
+const struct brcmf_tlv *
+brcmf_parse_tlvs(const void *buf, int buflen, uint key)
+{
+ const struct brcmf_tlv *elt = buf;
+ int totlen = buflen;
+
+ /* find tagged parameter */
+ while (totlen >= TLV_HDR_LEN) {
+ int len = elt->len;
+
+ /* validate remaining totlen */
+ if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
+ return elt;
+
+ elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
+ totlen -= (len + TLV_HDR_LEN);
+ }
+
+ return NULL;
+}
+
+/* Is any of the tlvs the expected entry? If
+ * not update the tlvs buffer pointer/length.
+ */
+static bool
+brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
+ const u8 *oui, u32 oui_len, u8 type)
+{
+ /* If the contents match the OUI and the type */
+ if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
+ !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
+ type == ie[TLV_BODY_OFF + oui_len]) {
+ return true;
+ }
+
+ if (tlvs == NULL)
+ return false;
+ /* point to the next ie */
+ ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
+ /* calculate the length of the rest of the buffer */
+ *tlvs_len -= (int)(ie - *tlvs);
+ /* update the pointer to the start of the buffer */
+ *tlvs = ie;
+
+ return false;
+}
+
+static struct brcmf_vs_tlv *
+brcmf_find_wpaie(const u8 *parse, u32 len)
+{
+ const struct brcmf_tlv *ie;
+
+ while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
+ if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
+ WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
+ return (struct brcmf_vs_tlv *)ie;
+ }
+ return NULL;
+}
+
+static struct brcmf_vs_tlv *
+brcmf_find_wpsie(const u8 *parse, u32 len)
+{
+ const struct brcmf_tlv *ie;
+
+ while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
+ if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
+ WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
+ return (struct brcmf_vs_tlv *)ie;
+ }
+ return NULL;
+}
+
+static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_cfg80211_vif *vif,
+ enum nl80211_iftype new_type)
+{
+ int iftype_num[NUM_NL80211_IFTYPES];
+ struct brcmf_cfg80211_vif *pos;
+ bool check_combos = false;
+ int ret = 0;
+
+ memset(&iftype_num[0], 0, sizeof(iftype_num));
+ list_for_each_entry(pos, &cfg->vif_list, list)
+ if (pos == vif) {
+ iftype_num[new_type]++;
+ } else {
+ /* concurrent interfaces so need check combinations */
+ check_combos = true;
+ iftype_num[pos->wdev.iftype]++;
+ }
+
+ if (check_combos)
+ ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
+
+ return ret;
+}
+
+static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
+ enum nl80211_iftype new_type)
+{
+ int iftype_num[NUM_NL80211_IFTYPES];
+ struct brcmf_cfg80211_vif *pos;
+
+ memset(&iftype_num[0], 0, sizeof(iftype_num));
+ list_for_each_entry(pos, &cfg->vif_list, list)
+ iftype_num[pos->wdev.iftype]++;
+
+ iftype_num[new_type]++;
+ return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
+}
+
+static void convert_key_from_CPU(struct brcmf_wsec_key *key,
+ struct brcmf_wsec_key_le *key_le)
+{
+ key_le->index = cpu_to_le32(key->index);
+ key_le->len = cpu_to_le32(key->len);
+ key_le->algo = cpu_to_le32(key->algo);
+ key_le->flags = cpu_to_le32(key->flags);
+ key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
+ key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
+ key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
+ memcpy(key_le->data, key->data, sizeof(key->data));
+ memcpy(key_le->ea, key->ea, sizeof(key->ea));
+}
+
+static int
+send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
+{
+ int err;
+ struct brcmf_wsec_key_le key_le;
+
+ convert_key_from_CPU(key, &key_le);
+
+ brcmf_netdev_wait_pend8021x(ifp);
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
+ sizeof(key_le));
+
+ if (err)
+ brcmf_err("wsec_key error (%d)\n", err);
+ return err;
+}
+
+static s32
+brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
+{
+ s32 err;
+ u32 mode;
+
+ if (enable)
+ mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
+ else
+ mode = 0;
+
+ /* Try to set and enable ARP offload feature, this may fail, then it */
+ /* is simply not supported and err 0 will be returned */
+ err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
+ if (err) {
+ brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
+ mode, err);
+ err = 0;
+ } else {
+ err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
+ if (err) {
+ brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
+ enable, err);
+ err = 0;
+ } else
+ brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
+ enable, mode);
+ }
+
+ return err;
+}
+
+static void
+brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ ifp = vif->ifp;
+
+ if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
+ (wdev->iftype == NL80211_IFTYPE_AP) ||
+ (wdev->iftype == NL80211_IFTYPE_P2P_GO))
+ brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
+ ADDR_DIRECT);
+ else
+ brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
+ ADDR_INDIRECT);
+}
+
+static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
+{
+ struct brcmf_mbss_ssid_le mbss_ssid_le;
+ int bsscfgidx;
+ int err;
+
+ memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
+ bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
+ if (bsscfgidx < 0)
+ return bsscfgidx;
+
+ mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
+ mbss_ssid_le.SSID_len = cpu_to_le32(5);
+ sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
+ sizeof(mbss_ssid_le));
+ if (err < 0)
+ brcmf_err("setting ssid failed %d\n", err);
+
+ return err;
+}
+
+/**
+ * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
+ *
+ * @wiphy: wiphy device of new interface.
+ * @name: name of the new interface.
+ * @flags: not used.
+ * @params: contains mac address for AP device.
+ */
+static
+struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
+ u32 *flags, struct vif_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_cfg80211_vif *vif;
+ int err;
+
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ return ERR_PTR(-EBUSY);
+
+ brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
+
+ vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
+ if (IS_ERR(vif))
+ return (struct wireless_dev *)vif;
+
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+
+ err = brcmf_cfg80211_request_ap_if(ifp);
+ if (err) {
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ goto fail;
+ }
+
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (!err) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* interface created in firmware */
+ ifp = vif->ifp;
+ if (!ifp) {
+ brcmf_err("no if pointer provided\n");
+ err = -ENOENT;
+ goto fail;
+ }
+
+ strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
+ err = brcmf_net_attach(ifp, true);
+ if (err) {
+ brcmf_err("Registering netdevice failed\n");
+ goto fail;
+ }
+
+ return &ifp->vif->wdev;
+
+fail:
+ brcmf_free_vif(vif);
+ return ERR_PTR(err);
+}
+
+static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
+{
+ enum nl80211_iftype iftype;
+
+ iftype = vif->wdev.iftype;
+ return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
+}
+
+static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
+{
+ return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
+}
+
+static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params)
+{
+ struct wireless_dev *wdev;
+ int err;
+
+ brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
+ err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
+ if (err) {
+ brcmf_err("iface validation failed: err=%d\n", err);
+ return ERR_PTR(err);
+ }
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_MESH_POINT:
+ return ERR_PTR(-EOPNOTSUPP);
+ case NL80211_IFTYPE_AP:
+ wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
+ if (!IS_ERR(wdev))
+ brcmf_cfg80211_update_proto_addr_mode(wdev);
+ return wdev;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
+ if (!IS_ERR(wdev))
+ brcmf_cfg80211_update_proto_addr_mode(wdev);
+ return wdev;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
+{
+ if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
+ brcmf_set_mpc(ifp, mpc);
+}
+
+void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
+{
+ s32 err = 0;
+
+ if (check_vif_up(ifp->vif)) {
+ err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
+ if (err) {
+ brcmf_err("fail to set mpc\n");
+ return;
+ }
+ brcmf_dbg(INFO, "MPC : %d\n", mpc);
+ }
+}
+
+s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp, bool aborted,
+ bool fw_abort)
+{
+ struct brcmf_scan_params_le params_le;
+ struct cfg80211_scan_request *scan_request;
+ s32 err = 0;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+ /* clear scan request, because the FW abort can cause a second call */
+ /* to this functon and might cause a double cfg80211_scan_done */
+ scan_request = cfg->scan_request;
+ cfg->scan_request = NULL;
+
+ if (timer_pending(&cfg->escan_timeout))
+ del_timer_sync(&cfg->escan_timeout);
+
+ if (fw_abort) {
+ /* Do a scan abort to stop the driver's scan engine */
+ brcmf_dbg(SCAN, "ABORT scan in firmware\n");
+ memset(&params_le, 0, sizeof(params_le));
+ eth_broadcast_addr(params_le.bssid);
+ params_le.bss_type = DOT11_BSSTYPE_ANY;
+ params_le.scan_type = 0;
+ params_le.channel_num = cpu_to_le32(1);
+ params_le.nprobes = cpu_to_le32(1);
+ params_le.active_time = cpu_to_le32(-1);
+ params_le.passive_time = cpu_to_le32(-1);
+ params_le.home_time = cpu_to_le32(-1);
+ /* Scan is aborted by setting channel_list[0] to -1 */
+ params_le.channel_list[0] = cpu_to_le16(-1);
+ /* E-Scan (or anyother type) can be aborted by SCAN */
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
+ &params_le, sizeof(params_le));
+ if (err)
+ brcmf_err("Scan abort failed\n");
+ }
+
+ brcmf_scan_config_mpc(ifp, 1);
+
+ /*
+ * e-scan can be initiated by scheduled scan
+ * which takes precedence.
+ */
+ if (cfg->sched_escan) {
+ brcmf_dbg(SCAN, "scheduled scan completed\n");
+ cfg->sched_escan = false;
+ if (!aborted)
+ cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
+ } else if (scan_request) {
+ brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
+ aborted ? "Aborted" : "Done");
+ cfg80211_scan_done(scan_request, aborted);
+ }
+ if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
+ brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
+
+ return err;
+}
+
+static
+int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct net_device *ndev = wdev->netdev;
+
+ /* vif event pending in firmware */
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ return -EBUSY;
+
+ if (ndev) {
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
+ cfg->escan_info.ifp == netdev_priv(ndev))
+ brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
+ true, true);
+
+ brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
+ }
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_MESH_POINT:
+ return -EOPNOTSUPP;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return brcmf_p2p_del_vif(wiphy, wdev);
+ case NL80211_IFTYPE_UNSPECIFIED:
+ default:
+ return -EINVAL;
+ }
+ return -EOPNOTSUPP;
+}
+
+static s32
+brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_vif *vif = ifp->vif;
+ s32 infra = 0;
+ s32 ap = 0;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
+ type);
+
+ /* WAR: There are a number of p2p interface related problems which
+ * need to be handled initially (before doing the validate).
+ * wpa_supplicant tends to do iface changes on p2p device/client/go
+ * which are not always possible/allowed. However we need to return
+ * OK otherwise the wpa_supplicant wont start. The situation differs
+ * on configuration and setup (p2pon=1 module param). The first check
+ * is to see if the request is a change to station for p2p iface.
+ */
+ if ((type == NL80211_IFTYPE_STATION) &&
+ ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
+ (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
+ (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
+ brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
+ /* Now depending on whether module param p2pon=1 was used the
+ * response needs to be either 0 or EOPNOTSUPP. The reason is
+ * that if p2pon=1 is used, but a newer supplicant is used then
+ * we should return an error, as this combination wont work.
+ * In other situations 0 is returned and supplicant will start
+ * normally. It will give a trace in cfg80211, but it is the
+ * only way to get it working. Unfortunately this will result
+ * in situation where we wont support new supplicant in
+ * combination with module param p2pon=1, but that is the way
+ * it is. If the user tries this then unloading of driver might
+ * fail/lock.
+ */
+ if (cfg->p2p.p2pdev_dynamically)
+ return -EOPNOTSUPP;
+ else
+ return 0;
+ }
+ err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
+ if (err) {
+ brcmf_err("iface validation failed: err=%d\n", err);
+ return err;
+ }
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
+ brcmf_err("type (%d) : currently we do not support this type\n",
+ type);
+ return -EOPNOTSUPP;
+ case NL80211_IFTYPE_ADHOC:
+ infra = 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ infra = 1;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ ap = 1;
+ break;
+ default:
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (ap) {
+ if (type == NL80211_IFTYPE_P2P_GO) {
+ brcmf_dbg(INFO, "IF Type = P2P GO\n");
+ err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
+ }
+ if (!err) {
+ brcmf_dbg(INFO, "IF Type = AP\n");
+ }
+ } else {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
+ if (err) {
+ brcmf_err("WLC_SET_INFRA error (%d)\n", err);
+ err = -EAGAIN;
+ goto done;
+ }
+ brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
+ "Adhoc" : "Infra");
+ }
+ ndev->ieee80211_ptr->iftype = type;
+
+ brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+
+ return err;
+}
+
+static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_scan_params_le *params_le,
+ struct cfg80211_scan_request *request)
+{
+ u32 n_ssids;
+ u32 n_channels;
+ s32 i;
+ s32 offset;
+ u16 chanspec;
+ char *ptr;
+ struct brcmf_ssid_le ssid_le;
+
+ eth_broadcast_addr(params_le->bssid);
+ params_le->bss_type = DOT11_BSSTYPE_ANY;
+ params_le->scan_type = 0;
+ params_le->channel_num = 0;
+ params_le->nprobes = cpu_to_le32(-1);
+ params_le->active_time = cpu_to_le32(-1);
+ params_le->passive_time = cpu_to_le32(-1);
+ params_le->home_time = cpu_to_le32(-1);
+ memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
+
+ /* if request is null exit so it will be all channel broadcast scan */
+ if (!request)
+ return;
+
+ n_ssids = request->n_ssids;
+ n_channels = request->n_channels;
+ /* Copy channel array if applicable */
+ brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
+ n_channels);
+ if (n_channels > 0) {
+ for (i = 0; i < n_channels; i++) {
+ chanspec = channel_to_chanspec(&cfg->d11inf,
+ request->channels[i]);
+ brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
+ request->channels[i]->hw_value, chanspec);
+ params_le->channel_list[i] = cpu_to_le16(chanspec);
+ }
+ } else {
+ brcmf_dbg(SCAN, "Scanning all channels\n");
+ }
+ /* Copy ssid array if applicable */
+ brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
+ if (n_ssids > 0) {
+ offset = offsetof(struct brcmf_scan_params_le, channel_list) +
+ n_channels * sizeof(u16);
+ offset = roundup(offset, sizeof(u32));
+ ptr = (char *)params_le + offset;
+ for (i = 0; i < n_ssids; i++) {
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ ssid_le.SSID_len =
+ cpu_to_le32(request->ssids[i].ssid_len);
+ memcpy(ssid_le.SSID, request->ssids[i].ssid,
+ request->ssids[i].ssid_len);
+ if (!ssid_le.SSID_len)
+ brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
+ else
+ brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
+ i, ssid_le.SSID, ssid_le.SSID_len);
+ memcpy(ptr, &ssid_le, sizeof(ssid_le));
+ ptr += sizeof(ssid_le);
+ }
+ } else {
+ brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
+ if ((request->ssids) && request->ssids->ssid_len) {
+ brcmf_dbg(SCAN, "SSID %s len=%d\n",
+ params_le->ssid_le.SSID,
+ request->ssids->ssid_len);
+ params_le->ssid_le.SSID_len =
+ cpu_to_le32(request->ssids->ssid_len);
+ memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
+ request->ssids->ssid_len);
+ }
+ }
+ /* Adding mask to channel numbers */
+ params_le->channel_num =
+ cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
+ (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
+}
+
+static s32
+brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
+ struct cfg80211_scan_request *request)
+{
+ s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
+ offsetof(struct brcmf_escan_params_le, params_le);
+ struct brcmf_escan_params_le *params;
+ s32 err = 0;
+
+ brcmf_dbg(SCAN, "E-SCAN START\n");
+
+ if (request != NULL) {
+ /* Allocate space for populating ssids in struct */
+ params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
+
+ /* Allocate space for populating ssids in struct */
+ params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
+ }
+
+ params = kzalloc(params_size, GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
+ brcmf_escan_prep(cfg, &params->params_le, request);
+ params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
+ params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
+ params->sync_id = cpu_to_le16(0x1234);
+
+ err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
+ if (err) {
+ if (err == -EBUSY)
+ brcmf_dbg(INFO, "system busy : escan canceled\n");
+ else
+ brcmf_err("error (%d)\n", err);
+ }
+
+ kfree(params);
+exit:
+ return err;
+}
+
+static s32
+brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
+ struct brcmf_if *ifp, struct cfg80211_scan_request *request)
+{
+ s32 err;
+ u32 passive_scan;
+ struct brcmf_scan_results *results;
+ struct escan_info *escan = &cfg->escan_info;
+
+ brcmf_dbg(SCAN, "Enter\n");
+ escan->ifp = ifp;
+ escan->wiphy = wiphy;
+ escan->escan_state = WL_ESCAN_STATE_SCANNING;
+ passive_scan = cfg->active_scan ? 0 : 1;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
+ passive_scan);
+ if (err) {
+ brcmf_err("error (%d)\n", err);
+ return err;
+ }
+ brcmf_scan_config_mpc(ifp, 0);
+ results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
+ results->version = 0;
+ results->count = 0;
+ results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
+
+ err = escan->run(cfg, ifp, request);
+ if (err)
+ brcmf_scan_config_mpc(ifp, 1);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
+ struct cfg80211_scan_request *request,
+ struct cfg80211_ssid *this_ssid)
+{
+ struct brcmf_if *ifp = vif->ifp;
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct cfg80211_ssid *ssids;
+ u32 passive_scan;
+ bool escan_req;
+ bool spec_scan;
+ s32 err;
+ struct brcmf_ssid_le ssid_le;
+ u32 SSID_len;
+
+ brcmf_dbg(SCAN, "START ESCAN\n");
+
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+ brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
+ return -EAGAIN;
+ }
+ if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
+ brcmf_err("Scanning being aborted: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
+ if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
+ brcmf_err("Scanning suppressed: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
+ brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
+ return -EAGAIN;
+ }
+
+ /* If scan req comes for p2p0, send it over primary I/F */
+ if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+
+ escan_req = false;
+ if (request) {
+ /* scan bss */
+ ssids = request->ssids;
+ escan_req = true;
+ } else {
+ /* scan in ibss */
+ /* we don't do escan in ibss */
+ ssids = this_ssid;
+ }
+
+ cfg->scan_request = request;
+ set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ if (escan_req) {
+ cfg->escan_info.run = brcmf_run_escan;
+ err = brcmf_p2p_scan_prep(wiphy, request, vif);
+ if (err)
+ goto scan_out;
+
+ err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
+ if (err)
+ goto scan_out;
+ } else {
+ brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
+ ssids->ssid, ssids->ssid_len);
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
+ ssid_le.SSID_len = cpu_to_le32(0);
+ spec_scan = false;
+ if (SSID_len) {
+ memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
+ ssid_le.SSID_len = cpu_to_le32(SSID_len);
+ spec_scan = true;
+ } else
+ brcmf_dbg(SCAN, "Broadcast scan\n");
+
+ passive_scan = cfg->active_scan ? 0 : 1;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
+ passive_scan);
+ if (err) {
+ brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
+ goto scan_out;
+ }
+ brcmf_scan_config_mpc(ifp, 0);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
+ sizeof(ssid_le));
+ if (err) {
+ if (err == -EBUSY)
+ brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
+ ssid_le.SSID);
+ else
+ brcmf_err("WLC_SCAN error (%d)\n", err);
+
+ brcmf_scan_config_mpc(ifp, 1);
+ goto scan_out;
+ }
+ }
+
+ /* Arm scan timeout timer */
+ mod_timer(&cfg->escan_timeout, jiffies +
+ WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
+
+ return 0;
+
+scan_out:
+ clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ cfg->scan_request = NULL;
+ return err;
+}
+
+static s32
+brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
+{
+ struct brcmf_cfg80211_vif *vif;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
+ if (!check_vif_up(vif))
+ return -EIO;
+
+ err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
+
+ if (err)
+ brcmf_err("scan error (%d)\n", err);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
+{
+ s32 err = 0;
+
+ err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
+ rts_threshold);
+ if (err)
+ brcmf_err("Error (%d)\n", err);
+
+ return err;
+}
+
+static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
+{
+ s32 err = 0;
+
+ err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
+ frag_threshold);
+ if (err)
+ brcmf_err("Error (%d)\n", err);
+
+ return err;
+}
+
+static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
+{
+ s32 err = 0;
+ u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
+
+ err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
+ if (err) {
+ brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
+ return err;
+ }
+ return err;
+}
+
+static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
+ (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
+ cfg->conf->rts_threshold = wiphy->rts_threshold;
+ err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
+ (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
+ cfg->conf->frag_threshold = wiphy->frag_threshold;
+ err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG
+ && (cfg->conf->retry_long != wiphy->retry_long)) {
+ cfg->conf->retry_long = wiphy->retry_long;
+ err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_RETRY_SHORT
+ && (cfg->conf->retry_short != wiphy->retry_short)) {
+ cfg->conf->retry_short = wiphy->retry_short;
+ err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
+ if (!err)
+ goto done;
+ }
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
+{
+ memset(prof, 0, sizeof(*prof));
+}
+
+static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
+{
+ u16 reason;
+
+ switch (e->event_code) {
+ case BRCMF_E_DEAUTH:
+ case BRCMF_E_DEAUTH_IND:
+ case BRCMF_E_DISASSOC_IND:
+ reason = e->reason;
+ break;
+ case BRCMF_E_LINK:
+ default:
+ reason = 0;
+ break;
+ }
+ return reason;
+}
+
+static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
+ brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
+ err = brcmf_fil_cmd_data_set(vif->ifp,
+ BRCMF_C_DISASSOC, NULL, 0);
+ if (err) {
+ brcmf_err("WLC_DISASSOC failed (%d)\n", err);
+ }
+ if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
+ (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
+ cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
+ true, GFP_KERNEL);
+ }
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
+ clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+ brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
+ brcmf_dbg(TRACE, "Exit\n");
+}
+
+static s32
+brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_ibss_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_join_params join_params;
+ size_t join_params_size = 0;
+ s32 err = 0;
+ s32 wsec = 0;
+ s32 bcnprd;
+ u16 chanspec;
+ u32 ssid_len;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (params->ssid)
+ brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
+ else {
+ brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+
+ if (params->bssid)
+ brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
+ else
+ brcmf_dbg(CONN, "No BSSID specified\n");
+
+ if (params->chandef.chan)
+ brcmf_dbg(CONN, "channel: %d\n",
+ params->chandef.chan->center_freq);
+ else
+ brcmf_dbg(CONN, "no channel specified\n");
+
+ if (params->channel_fixed)
+ brcmf_dbg(CONN, "fixed channel required\n");
+ else
+ brcmf_dbg(CONN, "no fixed channel required\n");
+
+ if (params->ie && params->ie_len)
+ brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
+ else
+ brcmf_dbg(CONN, "no ie specified\n");
+
+ if (params->beacon_interval)
+ brcmf_dbg(CONN, "beacon interval: %d\n",
+ params->beacon_interval);
+ else
+ brcmf_dbg(CONN, "no beacon interval specified\n");
+
+ if (params->basic_rates)
+ brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
+ else
+ brcmf_dbg(CONN, "no basic rates specified\n");
+
+ if (params->privacy)
+ brcmf_dbg(CONN, "privacy required\n");
+ else
+ brcmf_dbg(CONN, "no privacy required\n");
+
+ /* Configure Privacy for starter */
+ if (params->privacy)
+ wsec |= WEP_ENABLED;
+
+ err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
+ if (err) {
+ brcmf_err("wsec failed (%d)\n", err);
+ goto done;
+ }
+
+ /* Configure Beacon Interval for starter */
+ if (params->beacon_interval)
+ bcnprd = params->beacon_interval;
+ else
+ bcnprd = 100;
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
+ if (err) {
+ brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
+ goto done;
+ }
+
+ /* Configure required join parameter */
+ memset(&join_params, 0, sizeof(struct brcmf_join_params));
+
+ /* SSID */
+ ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
+ memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
+ join_params_size = sizeof(join_params.ssid_le);
+
+ /* BSSID */
+ if (params->bssid) {
+ memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
+ join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
+ memcpy(profile->bssid, params->bssid, ETH_ALEN);
+ } else {
+ eth_broadcast_addr(join_params.params_le.bssid);
+ eth_zero_addr(profile->bssid);
+ }
+
+ /* Channel */
+ if (params->chandef.chan) {
+ u32 target_channel;
+
+ cfg->channel =
+ ieee80211_frequency_to_channel(
+ params->chandef.chan->center_freq);
+ if (params->channel_fixed) {
+ /* adding chanspec */
+ chanspec = chandef_to_chanspec(&cfg->d11inf,
+ &params->chandef);
+ join_params.params_le.chanspec_list[0] =
+ cpu_to_le16(chanspec);
+ join_params.params_le.chanspec_num = cpu_to_le32(1);
+ join_params_size += sizeof(join_params.params_le);
+ }
+
+ /* set channel for starter */
+ target_channel = cfg->channel;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
+ target_channel);
+ if (err) {
+ brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
+ goto done;
+ }
+ } else
+ cfg->channel = 0;
+
+ cfg->ibss_starter = false;
+
+
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, join_params_size);
+ if (err) {
+ brcmf_err("WLC_SET_SSID failed (%d)\n", err);
+ goto done;
+ }
+
+done:
+ if (err)
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif)) {
+ /* When driver is being unloaded, it can end up here. If an
+ * error is returned then later on a debug trace in the wireless
+ * core module will be printed. To avoid this 0 is returned.
+ */
+ return 0;
+ }
+
+ brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
+ brcmf_net_setcarrier(ifp, false);
+
+ brcmf_dbg(TRACE, "Exit\n");
+
+ return 0;
+}
+
+static s32 brcmf_set_wpa_version(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+ else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+ else
+ val = WPA_AUTH_DISABLED;
+ brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
+ if (err) {
+ brcmf_err("set wpa_auth failed (%d)\n", err);
+ return err;
+ }
+ sec = &profile->sec;
+ sec->wpa_versions = sme->crypto.wpa_versions;
+ return err;
+}
+
+static s32 brcmf_set_auth_type(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ switch (sme->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ val = 0;
+ brcmf_dbg(CONN, "open system\n");
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ val = 1;
+ brcmf_dbg(CONN, "shared key\n");
+ break;
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ val = 2;
+ brcmf_dbg(CONN, "automatic\n");
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ brcmf_dbg(CONN, "network eap\n");
+ default:
+ val = 2;
+ brcmf_err("invalid auth type (%d)\n", sme->auth_type);
+ break;
+ }
+
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
+ if (err) {
+ brcmf_err("set auth failed (%d)\n", err);
+ return err;
+ }
+ sec = &profile->sec;
+ sec->auth_type = sme->auth_type;
+ return err;
+}
+
+static s32
+brcmf_set_wsec_mode(struct net_device *ndev,
+ struct cfg80211_connect_params *sme, bool mfp)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 pval = 0;
+ s32 gval = 0;
+ s32 wsec;
+ s32 err = 0;
+
+ if (sme->crypto.n_ciphers_pairwise) {
+ switch (sme->crypto.ciphers_pairwise[0]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ pval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ pval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ pval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ pval = AES_ENABLED;
+ break;
+ default:
+ brcmf_err("invalid cipher pairwise (%d)\n",
+ sme->crypto.ciphers_pairwise[0]);
+ return -EINVAL;
+ }
+ }
+ if (sme->crypto.cipher_group) {
+ switch (sme->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ gval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ gval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ gval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ gval = AES_ENABLED;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ }
+
+ brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
+ /* In case of privacy, but no security and WPS then simulate */
+ /* setting AES. WPS-2.0 allows no security */
+ if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
+ sme->privacy)
+ pval = AES_ENABLED;
+
+ if (mfp)
+ wsec = pval | gval | MFP_CAPABLE;
+ else
+ wsec = pval | gval;
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
+ if (err) {
+ brcmf_err("error (%d)\n", err);
+ return err;
+ }
+
+ sec = &profile->sec;
+ sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
+ sec->cipher_group = sme->crypto.cipher_group;
+
+ return err;
+}
+
+static s32
+brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.n_akm_suites) {
+ err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
+ "wpa_auth", &val);
+ if (err) {
+ brcmf_err("could not get wpa_auth (%d)\n", err);
+ return err;
+ }
+ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA_AUTH_PSK;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA2_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA2_AUTH_PSK;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ }
+
+ brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
+ "wpa_auth", val);
+ if (err) {
+ brcmf_err("could not set wpa_auth (%d)\n", err);
+ return err;
+ }
+ }
+ sec = &profile->sec;
+ sec->wpa_auth = sme->crypto.akm_suites[0];
+
+ return err;
+}
+
+static s32
+brcmf_set_sharedkey(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ struct brcmf_wsec_key key;
+ s32 val;
+ s32 err = 0;
+
+ brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
+
+ if (sme->key_len == 0)
+ return 0;
+
+ sec = &profile->sec;
+ brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
+ sec->wpa_versions, sec->cipher_pairwise);
+
+ if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
+ return 0;
+
+ if (!(sec->cipher_pairwise &
+ (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
+ return 0;
+
+ memset(&key, 0, sizeof(key));
+ key.len = (u32) sme->key_len;
+ key.index = (u32) sme->key_idx;
+ if (key.len > sizeof(key.data)) {
+ brcmf_err("Too long key length (%u)\n", key.len);
+ return -EINVAL;
+ }
+ memcpy(key.data, sme->key, key.len);
+ key.flags = BRCMF_PRIMARY_KEY;
+ switch (sec->cipher_pairwise) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+ default:
+ brcmf_err("Invalid algorithm (%d)\n",
+ sme->crypto.ciphers_pairwise[0]);
+ return -EINVAL;
+ }
+ /* Set the new key/index */
+ brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
+ key.len, key.index, key.algo);
+ brcmf_dbg(CONN, "key \"%s\"\n", key.data);
+ err = send_key_to_dongle(netdev_priv(ndev), &key);
+ if (err)
+ return err;
+
+ if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
+ brcmf_dbg(CONN, "set auth_type to shared key\n");
+ val = WL_AUTH_SHARED_KEY; /* shared key */
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
+ if (err)
+ brcmf_err("set auth failed (%d)\n", err);
+ }
+ return err;
+}
+
+static
+enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
+ enum nl80211_auth_type type)
+{
+ if (type == NL80211_AUTHTYPE_AUTOMATIC &&
+ brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
+ brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
+ type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ }
+ return type;
+}
+
+static s32
+brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct ieee80211_channel *chan = sme->channel;
+ struct brcmf_join_params join_params;
+ size_t join_params_size;
+ const struct brcmf_tlv *rsn_ie;
+ const struct brcmf_vs_tlv *wpa_ie;
+ const void *ie;
+ u32 ie_len;
+ struct brcmf_ext_join_params_le *ext_join_params;
+ u16 chanspec;
+ s32 err = 0;
+ u32 ssid_len;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (!sme->ssid) {
+ brcmf_err("Invalid ssid\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
+ /* A normal (non P2P) connection request setup. */
+ ie = NULL;
+ ie_len = 0;
+ /* find the WPA_IE */
+ wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
+ if (wpa_ie) {
+ ie = wpa_ie;
+ ie_len = wpa_ie->len + TLV_HDR_LEN;
+ } else {
+ /* find the RSN_IE */
+ rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
+ sme->ie_len,
+ WLAN_EID_RSN);
+ if (rsn_ie) {
+ ie = rsn_ie;
+ ie_len = rsn_ie->len + TLV_HDR_LEN;
+ }
+ }
+ brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
+ }
+
+ err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
+ sme->ie, sme->ie_len);
+ if (err)
+ brcmf_err("Set Assoc REQ IE Failed\n");
+ else
+ brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
+
+ set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+
+ if (chan) {
+ cfg->channel =
+ ieee80211_frequency_to_channel(chan->center_freq);
+ chanspec = channel_to_chanspec(&cfg->d11inf, chan);
+ brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
+ cfg->channel, chan->center_freq, chanspec);
+ } else {
+ cfg->channel = 0;
+ chanspec = 0;
+ }
+
+ brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
+
+ err = brcmf_set_wpa_version(ndev, sme);
+ if (err) {
+ brcmf_err("wl_set_wpa_version failed (%d)\n", err);
+ goto done;
+ }
+
+ sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
+ err = brcmf_set_auth_type(ndev, sme);
+ if (err) {
+ brcmf_err("wl_set_auth_type failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
+ if (err) {
+ brcmf_err("wl_set_set_cipher failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_key_mgmt(ndev, sme);
+ if (err) {
+ brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_sharedkey(ndev, sme);
+ if (err) {
+ brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
+ goto done;
+ }
+
+ /* Join with specific BSSID and cached SSID
+ * If SSID is zero join based on BSSID only
+ */
+ join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
+ offsetof(struct brcmf_assoc_params_le, chanspec_list);
+ if (cfg->channel)
+ join_params_size += sizeof(u16);
+ ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
+ if (ext_join_params == NULL) {
+ err = -ENOMEM;
+ goto done;
+ }
+ ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
+ ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
+ memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
+ if (ssid_len < IEEE80211_MAX_SSID_LEN)
+ brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
+ ext_join_params->ssid_le.SSID, ssid_len);
+
+ /* Set up join scan parameters */
+ ext_join_params->scan_le.scan_type = -1;
+ ext_join_params->scan_le.home_time = cpu_to_le32(-1);
+
+ if (sme->bssid)
+ memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
+ else
+ eth_broadcast_addr(ext_join_params->assoc_le.bssid);
+
+ if (cfg->channel) {
+ ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
+
+ ext_join_params->assoc_le.chanspec_list[0] =
+ cpu_to_le16(chanspec);
+ /* Increase dwell time to receive probe response or detect
+ * beacon from target AP at a noisy air only during connect
+ * command.
+ */
+ ext_join_params->scan_le.active_time =
+ cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
+ ext_join_params->scan_le.passive_time =
+ cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
+ /* To sync with presence period of VSDB GO send probe request
+ * more frequently. Probe request will be stopped when it gets
+ * probe response from target AP/GO.
+ */
+ ext_join_params->scan_le.nprobes =
+ cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
+ BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
+ } else {
+ ext_join_params->scan_le.active_time = cpu_to_le32(-1);
+ ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
+ ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
+ }
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
+ join_params_size);
+ kfree(ext_join_params);
+ if (!err)
+ /* This is it. join command worked, we are done */
+ goto done;
+
+ /* join command failed, fallback to set ssid */
+ memset(&join_params, 0, sizeof(join_params));
+ join_params_size = sizeof(join_params.ssid_le);
+
+ memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
+
+ if (sme->bssid)
+ memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
+ else
+ eth_broadcast_addr(join_params.params_le.bssid);
+
+ if (cfg->channel) {
+ join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
+ join_params.params_le.chanspec_num = cpu_to_le32(1);
+ join_params_size += sizeof(join_params.params_le);
+ }
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, join_params_size);
+ if (err)
+ brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
+
+done:
+ if (err)
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
+ u16 reason_code)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_scb_val_le scbval;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+ cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
+
+ memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
+ scbval.val = cpu_to_le32(reason_code);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
+ &scbval, sizeof(scbval));
+ if (err)
+ brcmf_err("error (%d)\n", err);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, s32 mbm)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+ s32 disable;
+ u32 qdbm = 127;
+
+ brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ case NL80211_TX_POWER_FIXED:
+ if (mbm < 0) {
+ brcmf_err("TX_POWER_FIXED - dbm is negative\n");
+ err = -EINVAL;
+ goto done;
+ }
+ qdbm = MBM_TO_DBM(4 * mbm);
+ if (qdbm > 127)
+ qdbm = 127;
+ qdbm |= WL_TXPWR_OVERRIDE;
+ break;
+ default:
+ brcmf_err("Unsupported type %d\n", type);
+ err = -EINVAL;
+ goto done;
+ }
+ /* Make sure radio is off or on as far as software is concerned */
+ disable = WL_RADIO_SW_DISABLE << 16;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
+ if (err)
+ brcmf_err("WLC_SET_RADIO error (%d)\n", err);
+
+ err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
+ if (err)
+ brcmf_err("qtxpower error (%d)\n", err);
+
+done:
+ brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ s32 *dbm)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 qdbm = 0;
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
+ if (err) {
+ brcmf_err("error (%d)\n", err);
+ goto done;
+ }
+ *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
+
+done:
+ brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool unicast, bool multicast)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ u32 index;
+ u32 wsec;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("WLC_GET_WSEC error (%d)\n", err);
+ goto done;
+ }
+
+ if (wsec & WEP_ENABLED) {
+ /* Just select a new current key */
+ index = key_idx;
+ err = brcmf_fil_cmd_int_set(ifp,
+ BRCMF_C_SET_KEY_PRIMARY, index);
+ if (err)
+ brcmf_err("error (%d)\n", err);
+ }
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, const u8 *mac_addr, struct key_params *params)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_wsec_key key;
+ s32 err = 0;
+ u8 keybuf[8];
+
+ memset(&key, 0, sizeof(key));
+ key.index = (u32) key_idx;
+ /* Instead of bcast for ea address for default wep keys,
+ driver needs it to be Null */
+ if (!is_multicast_ether_addr(mac_addr))
+ memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
+ key.len = (u32) params->key_len;
+ /* check for key index change */
+ if (key.len == 0) {
+ /* key delete */
+ err = send_key_to_dongle(ifp, &key);
+ if (err)
+ brcmf_err("key delete error (%d)\n", err);
+ } else {
+ if (key.len > sizeof(key.data)) {
+ brcmf_err("Invalid key length (%d)\n", key.len);
+ return -EINVAL;
+ }
+
+ brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
+ memcpy(key.data, params->key, key.len);
+
+ if (!brcmf_is_apmode(ifp->vif) &&
+ (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
+ brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
+ memcpy(keybuf, &key.data[24], sizeof(keybuf));
+ memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
+ memcpy(&key.data[16], keybuf, sizeof(keybuf));
+ }
+
+ /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
+ if (params->seq && params->seq_len == 6) {
+ /* rx iv */
+ u8 *ivptr;
+ ivptr = (u8 *) params->seq;
+ key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
+ (ivptr[3] << 8) | ivptr[2];
+ key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+ key.iv_initialized = true;
+ }
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ key.algo = CRYPTO_ALGO_TKIP;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
+ break;
+ default:
+ brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
+ return -EINVAL;
+ }
+ err = send_key_to_dongle(ifp, &key);
+ if (err)
+ brcmf_err("wsec_key error (%d)\n", err);
+ }
+ return err;
+}
+
+static s32
+brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_wsec_key *key;
+ s32 val;
+ s32 wsec;
+ s32 err = 0;
+ u8 keybuf[8];
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
+ /* we ignore this key index in this case */
+ brcmf_err("invalid key index (%d)\n", key_idx);
+ return -EINVAL;
+ }
+
+ if (mac_addr &&
+ (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
+ (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
+ brcmf_dbg(TRACE, "Exit");
+ return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
+ }
+
+ key = &ifp->vif->profile.key[key_idx];
+ memset(key, 0, sizeof(*key));
+
+ if (params->key_len > sizeof(key->data)) {
+ brcmf_err("Too long key length (%u)\n", params->key_len);
+ err = -EINVAL;
+ goto done;
+ }
+ key->len = params->key_len;
+ key->index = key_idx;
+
+ memcpy(key->data, params->key, key->len);
+
+ key->flags = BRCMF_PRIMARY_KEY;
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key->algo = CRYPTO_ALGO_WEP1;
+ val = WEP_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key->algo = CRYPTO_ALGO_WEP128;
+ val = WEP_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (!brcmf_is_apmode(ifp->vif)) {
+ brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
+ memcpy(keybuf, &key->data[24], sizeof(keybuf));
+ memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
+ memcpy(&key->data[16], keybuf, sizeof(keybuf));
+ }
+ key->algo = CRYPTO_ALGO_TKIP;
+ val = TKIP_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ key->algo = CRYPTO_ALGO_AES_CCM;
+ val = AES_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key->algo = CRYPTO_ALGO_AES_CCM;
+ val = AES_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
+ break;
+ default:
+ brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
+ err = -EINVAL;
+ goto done;
+ }
+
+ err = send_key_to_dongle(ifp, key);
+ if (err)
+ goto done;
+
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("get wsec error (%d)\n", err);
+ goto done;
+ }
+ wsec |= val;
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
+ if (err) {
+ brcmf_err("set wsec error (%d)\n", err);
+ goto done;
+ }
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_wsec_key key;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
+ /* we ignore this key index in this case */
+ return -EINVAL;
+ }
+
+ memset(&key, 0, sizeof(key));
+
+ key.index = (u32) key_idx;
+ key.flags = BRCMF_PRIMARY_KEY;
+ key.algo = CRYPTO_ALGO_OFF;
+
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+
+ /* Set the new key/index */
+ err = send_key_to_dongle(ifp, &key);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback) (void *cookie, struct key_params * params))
+{
+ struct key_params params;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_cfg80211_security *sec;
+ s32 wsec;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ memset(&params, 0, sizeof(params));
+
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("WLC_GET_WSEC error (%d)\n", err);
+ /* Ignore this error, may happen during DISASSOC */
+ err = -EAGAIN;
+ goto done;
+ }
+ if (wsec & WEP_ENABLED) {
+ sec = &profile->sec;
+ if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP40;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
+ } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP104;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
+ }
+ } else if (wsec & TKIP_ENABLED) {
+ params.cipher = WLAN_CIPHER_SUITE_TKIP;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
+ } else if (wsec & AES_ENABLED) {
+ params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
+ } else {
+ brcmf_err("Invalid algo (0x%x)\n", wsec);
+ err = -EINVAL;
+ goto done;
+ }
+ callback(cookie, &params);
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
+ struct net_device *ndev, u8 key_idx)
+{
+ brcmf_dbg(INFO, "Not supported\n");
+
+ return -EOPNOTSUPP;
+}
+
+static void
+brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
+{
+ s32 err;
+ u8 key_idx;
+ struct brcmf_wsec_key *key;
+ s32 wsec;
+
+ for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
+ key = &ifp->vif->profile.key[key_idx];
+ if ((key->algo == CRYPTO_ALGO_WEP1) ||
+ (key->algo == CRYPTO_ALGO_WEP128))
+ break;
+ }
+ if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
+ return;
+
+ err = send_key_to_dongle(ifp, key);
+ if (err) {
+ brcmf_err("Setting WEP key failed (%d)\n", err);
+ return;
+ }
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("get wsec error (%d)\n", err);
+ return;
+ }
+ wsec |= WEP_ENABLED;
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
+ if (err)
+ brcmf_err("set wsec error (%d)\n", err);
+}
+
+static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
+{
+ struct nl80211_sta_flag_update *sfu;
+
+ brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
+ si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
+ sfu = &si->sta_flags;
+ sfu->mask = BIT(NL80211_STA_FLAG_WME) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (fw_sta_flags & BRCMF_STA_WME)
+ sfu->set |= BIT(NL80211_STA_FLAG_WME);
+ if (fw_sta_flags & BRCMF_STA_AUTHE)
+ sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (fw_sta_flags & BRCMF_STA_ASSOC)
+ sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ if (fw_sta_flags & BRCMF_STA_AUTHO)
+ sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+}
+
+static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
+{
+ struct {
+ __le32 len;
+ struct brcmf_bss_info_le bss_le;
+ } *buf;
+ u16 capability;
+ int err;
+
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
+ WL_BSS_INFO_MAX);
+ if (err) {
+ brcmf_err("Failed to get bss info (%d)\n", err);
+ return;
+ }
+ si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
+ si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
+ si->bss_param.dtim_period = buf->bss_le.dtim_period;
+ capability = le16_to_cpu(buf->bss_le.capability);
+ if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
+ si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
+ if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+ if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
+ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+}
+
+static s32
+brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
+ struct station_info *sinfo)
+{
+ struct brcmf_scb_val_le scbval;
+ struct brcmf_pktcnt_le pktcnt;
+ s32 err;
+ u32 rate;
+ u32 rssi;
+
+ /* Get the current tx rate */
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
+ return err;
+ }
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->txrate.legacy = rate * 5;
+
+ memset(&scbval, 0, sizeof(scbval));
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
+ sizeof(scbval));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
+ return err;
+ }
+ rssi = le32_to_cpu(scbval.val);
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ sinfo->signal = rssi;
+
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
+ sizeof(pktcnt));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
+ return err;
+ }
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
+ BIT(NL80211_STA_INFO_RX_DROP_MISC) |
+ BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_FAILED);
+ sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
+ sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
+ sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
+ sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
+
+ return 0;
+}
+
+static s32
+brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err = 0;
+ struct brcmf_sta_info_le sta_info_le;
+ u32 sta_flags;
+ u32 is_tdls_peer;
+ s32 total_rssi;
+ s32 count_rssi;
+ u32 i;
+
+ brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (brcmf_is_ibssmode(ifp->vif))
+ return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
+
+ memset(&sta_info_le, 0, sizeof(sta_info_le));
+ memcpy(&sta_info_le, mac, ETH_ALEN);
+ err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
+ &sta_info_le,
+ sizeof(sta_info_le));
+ is_tdls_peer = !err;
+ if (err) {
+ err = brcmf_fil_iovar_data_get(ifp, "sta_info",
+ &sta_info_le,
+ sizeof(sta_info_le));
+ if (err < 0) {
+ brcmf_err("GET STA INFO failed, %d\n", err);
+ goto done;
+ }
+ }
+ brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
+ sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
+ sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
+ sta_flags = le32_to_cpu(sta_info_le.flags);
+ brcmf_convert_sta_flags(sta_flags, sinfo);
+ sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+ if (is_tdls_peer)
+ sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+ else
+ sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+ if (sta_flags & BRCMF_STA_ASSOC) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
+ sinfo->connected_time = le32_to_cpu(sta_info_le.in);
+ brcmf_fill_bss_param(ifp, sinfo);
+ }
+ if (sta_flags & BRCMF_STA_SCBSTATS) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
+ sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
+ sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
+ sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
+ sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
+ sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
+ if (sinfo->tx_packets) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->txrate.legacy =
+ le32_to_cpu(sta_info_le.tx_rate) / 100;
+ }
+ if (sinfo->rx_packets) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
+ sinfo->rxrate.legacy =
+ le32_to_cpu(sta_info_le.rx_rate) / 100;
+ }
+ if (le16_to_cpu(sta_info_le.ver) >= 4) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
+ sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
+ sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
+ }
+ total_rssi = 0;
+ count_rssi = 0;
+ for (i = 0; i < BRCMF_ANT_MAX; i++) {
+ if (sta_info_le.rssi[i]) {
+ sinfo->chain_signal_avg[count_rssi] =
+ sta_info_le.rssi[i];
+ sinfo->chain_signal[count_rssi] =
+ sta_info_le.rssi[i];
+ total_rssi += sta_info_le.rssi[i];
+ count_rssi++;
+ }
+ }
+ if (count_rssi) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
+ sinfo->chains = count_rssi;
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ total_rssi /= count_rssi;
+ sinfo->signal = total_rssi;
+ }
+ }
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static int
+brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
+
+ if (idx == 0) {
+ cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
+ &cfg->assoclist,
+ sizeof(cfg->assoclist));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
+ err);
+ cfg->assoclist.count = 0;
+ return -EOPNOTSUPP;
+ }
+ }
+ if (idx < le32_to_cpu(cfg->assoclist.count)) {
+ memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
+ return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
+ }
+ return -ENOENT;
+}
+
+static s32
+brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
+ bool enabled, s32 timeout)
+{
+ s32 pm;
+ s32 err = 0;
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /*
+ * Powersave enable/disable request is coming from the
+ * cfg80211 even before the interface is up. In that
+ * scenario, driver will be storing the power save
+ * preference in cfg struct to apply this to
+ * FW later while initializing the dongle
+ */
+ cfg->pwr_save = enabled;
+ if (!check_vif_up(ifp->vif)) {
+
+ brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
+ goto done;
+ }
+
+ pm = enabled ? PM_FAST : PM_OFF;
+ /* Do not enable the power save after assoc if it is a p2p interface */
+ if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
+ brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
+ pm = PM_OFF;
+ }
+ brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
+ if (err) {
+ if (err == -ENODEV)
+ brcmf_err("net_device is not ready yet\n");
+ else
+ brcmf_err("error (%d)\n", err);
+ }
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bi)
+{
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ struct ieee80211_channel *notify_channel;
+ struct cfg80211_bss *bss;
+ struct ieee80211_supported_band *band;
+ struct brcmu_chan ch;
+ u16 channel;
+ u32 freq;
+ u16 notify_capability;
+ u16 notify_interval;
+ u8 *notify_ie;
+ size_t notify_ielen;
+ s32 notify_signal;
+
+ if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
+ brcmf_err("Bss info is larger than buffer. Discarding\n");
+ return 0;
+ }
+
+ if (!bi->ctl_ch) {
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ bi->ctl_ch = ch.chnum;
+ }
+ channel = bi->ctl_ch;
+
+ if (channel <= CH_MAX_2G_CHANNEL)
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(channel, band->band);
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ notify_capability = le16_to_cpu(bi->capability);
+ notify_interval = le16_to_cpu(bi->beacon_period);
+ notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+ notify_ielen = le32_to_cpu(bi->ie_length);
+ notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+ brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
+ brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
+ brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
+ brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
+ brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
+
+ bss = cfg80211_inform_bss(wiphy, notify_channel,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ (const u8 *)bi->BSSID,
+ 0, notify_capability,
+ notify_interval, notify_ie,
+ notify_ielen, notify_signal,
+ GFP_KERNEL);
+
+ if (!bss)
+ return -ENOMEM;
+
+ cfg80211_put_bss(wiphy, bss);
+
+ return 0;
+}
+
+static struct brcmf_bss_info_le *
+next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
+{
+ if (bss == NULL)
+ return list->bss_info_le;
+ return (struct brcmf_bss_info_le *)((unsigned long)bss +
+ le32_to_cpu(bss->length));
+}
+
+static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_scan_results *bss_list;
+ struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
+ s32 err = 0;
+ int i;
+
+ bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
+ if (bss_list->count != 0 &&
+ bss_list->version != BRCMF_BSS_INFO_VERSION) {
+ brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
+ bss_list->version);
+ return -EOPNOTSUPP;
+ }
+ brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
+ for (i = 0; i < bss_list->count; i++) {
+ bi = next_bss_le(bss_list, bi);
+ err = brcmf_inform_single_bss(cfg, bi);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev, const u8 *bssid)
+{
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ struct ieee80211_channel *notify_channel;
+ struct brcmf_bss_info_le *bi = NULL;
+ struct ieee80211_supported_band *band;
+ struct cfg80211_bss *bss;
+ struct brcmu_chan ch;
+ u8 *buf = NULL;
+ s32 err = 0;
+ u32 freq;
+ u16 notify_capability;
+ u16 notify_interval;
+ u8 *notify_ie;
+ size_t notify_ielen;
+ s32 notify_signal;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto CleanUp;
+ }
+
+ *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+
+ err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
+ buf, WL_BSS_INFO_MAX);
+ if (err) {
+ brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
+ goto CleanUp;
+ }
+
+ bi = (struct brcmf_bss_info_le *)(buf + 4);
+
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ if (ch.band == BRCMU_CHAN_BAND_2G)
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
+ cfg->channel = freq;
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ notify_capability = le16_to_cpu(bi->capability);
+ notify_interval = le16_to_cpu(bi->beacon_period);
+ notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+ notify_ielen = le32_to_cpu(bi->ie_length);
+ notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+ brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
+ brcmf_dbg(CONN, "capability: %X\n", notify_capability);
+ brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
+ brcmf_dbg(CONN, "signal: %d\n", notify_signal);
+
+ bss = cfg80211_inform_bss(wiphy, notify_channel,
+ CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
+ notify_capability, notify_interval,
+ notify_ie, notify_ielen, notify_signal,
+ GFP_KERNEL);
+
+ if (!bss) {
+ err = -ENOMEM;
+ goto CleanUp;
+ }
+
+ cfg80211_put_bss(wiphy, bss);
+
+CleanUp:
+
+ kfree(buf);
+
+ brcmf_dbg(TRACE, "Exit\n");
+
+ return err;
+}
+
+static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp)
+{
+ struct brcmf_bss_info_le *bi;
+ const struct brcmf_tlv *tim;
+ u16 beacon_interval;
+ u8 dtim_period;
+ size_t ie_len;
+ u8 *ie;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (brcmf_is_ibssmode(ifp->vif))
+ return err;
+
+ *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
+ cfg->extra_buf, WL_EXTRA_BUF_MAX);
+ if (err) {
+ brcmf_err("Could not get bss info %d\n", err);
+ goto update_bss_info_out;
+ }
+
+ bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
+ err = brcmf_inform_single_bss(cfg, bi);
+ if (err)
+ goto update_bss_info_out;
+
+ ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
+ ie_len = le32_to_cpu(bi->ie_length);
+ beacon_interval = le16_to_cpu(bi->beacon_period);
+
+ tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
+ if (tim)
+ dtim_period = tim->data[1];
+ else {
+ /*
+ * active scan was done so we could not get dtim
+ * information out of probe response.
+ * so we speficially query dtim information to dongle.
+ */
+ u32 var;
+ err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
+ if (err) {
+ brcmf_err("wl dtim_assoc failed (%d)\n", err);
+ goto update_bss_info_out;
+ }
+ dtim_period = (u8)var;
+ }
+
+update_bss_info_out:
+ brcmf_dbg(TRACE, "Exit");
+ return err;
+}
+
+void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
+{
+ struct escan_info *escan = &cfg->escan_info;
+
+ set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
+ if (cfg->scan_request) {
+ escan->escan_state = WL_ESCAN_STATE_IDLE;
+ brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
+ }
+ clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
+}
+
+static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
+{
+ struct brcmf_cfg80211_info *cfg =
+ container_of(work, struct brcmf_cfg80211_info,
+ escan_timeout_work);
+
+ brcmf_inform_bss(cfg);
+ brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
+}
+
+static void brcmf_escan_timeout(unsigned long data)
+{
+ struct brcmf_cfg80211_info *cfg =
+ (struct brcmf_cfg80211_info *)data;
+
+ if (cfg->scan_request) {
+ brcmf_err("timer expired\n");
+ schedule_work(&cfg->escan_timeout_work);
+ }
+}
+
+static s32
+brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bss,
+ struct brcmf_bss_info_le *bss_info_le)
+{
+ struct brcmu_chan ch_bss, ch_bss_info_le;
+
+ ch_bss.chspec = le16_to_cpu(bss->chanspec);
+ cfg->d11inf.decchspec(&ch_bss);
+ ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
+ cfg->d11inf.decchspec(&ch_bss_info_le);
+
+ if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
+ ch_bss.band == ch_bss_info_le.band &&
+ bss_info_le->SSID_len == bss->SSID_len &&
+ !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
+ if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
+ (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
+ s16 bss_rssi = le16_to_cpu(bss->RSSI);
+ s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
+
+ /* preserve max RSSI if the measurements are
+ * both on-channel or both off-channel
+ */
+ if (bss_info_rssi > bss_rssi)
+ bss->RSSI = bss_info_le->RSSI;
+ } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
+ (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
+ /* preserve the on-channel rssi measurement
+ * if the new measurement is off channel
+ */
+ bss->RSSI = bss_info_le->RSSI;
+ bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static s32
+brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ s32 status;
+ struct brcmf_escan_result_le *escan_result_le;
+ struct brcmf_bss_info_le *bss_info_le;
+ struct brcmf_bss_info_le *bss = NULL;
+ u32 bi_length;
+ struct brcmf_scan_results *list;
+ u32 i;
+ bool aborted;
+
+ status = e->status;
+
+ if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+ brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
+ return -EPERM;
+ }
+
+ if (status == BRCMF_E_STATUS_PARTIAL) {
+ brcmf_dbg(SCAN, "ESCAN Partial result\n");
+ escan_result_le = (struct brcmf_escan_result_le *) data;
+ if (!escan_result_le) {
+ brcmf_err("Invalid escan result (NULL pointer)\n");
+ goto exit;
+ }
+ if (le16_to_cpu(escan_result_le->bss_count) != 1) {
+ brcmf_err("Invalid bss_count %d: ignoring\n",
+ escan_result_le->bss_count);
+ goto exit;
+ }
+ bss_info_le = &escan_result_le->bss_info_le;
+
+ if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
+ goto exit;
+
+ if (!cfg->scan_request) {
+ brcmf_dbg(SCAN, "result without cfg80211 request\n");
+ goto exit;
+ }
+
+ bi_length = le32_to_cpu(bss_info_le->length);
+ if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
+ WL_ESCAN_RESULTS_FIXED_SIZE)) {
+ brcmf_err("Invalid bss_info length %d: ignoring\n",
+ bi_length);
+ goto exit;
+ }
+
+ if (!(cfg_to_wiphy(cfg)->interface_modes &
+ BIT(NL80211_IFTYPE_ADHOC))) {
+ if (le16_to_cpu(bss_info_le->capability) &
+ WLAN_CAPABILITY_IBSS) {
+ brcmf_err("Ignoring IBSS result\n");
+ goto exit;
+ }
+ }
+
+ list = (struct brcmf_scan_results *)
+ cfg->escan_info.escan_buf;
+ if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
+ brcmf_err("Buffer is too small: ignoring\n");
+ goto exit;
+ }
+
+ for (i = 0; i < list->count; i++) {
+ bss = bss ? (struct brcmf_bss_info_le *)
+ ((unsigned char *)bss +
+ le32_to_cpu(bss->length)) : list->bss_info_le;
+ if (brcmf_compare_update_same_bss(cfg, bss,
+ bss_info_le))
+ goto exit;
+ }
+ memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
+ bss_info_le, bi_length);
+ list->version = le32_to_cpu(bss_info_le->version);
+ list->buflen += bi_length;
+ list->count++;
+ } else {
+ cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
+ if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
+ goto exit;
+ if (cfg->scan_request) {
+ brcmf_inform_bss(cfg);
+ aborted = status != BRCMF_E_STATUS_SUCCESS;
+ brcmf_notify_escan_complete(cfg, ifp, aborted, false);
+ } else
+ brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
+ status);
+ }
+exit:
+ return 0;
+}
+
+static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
+{
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
+ brcmf_cfg80211_escan_handler);
+ cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
+ /* Init scan_timeout timer */
+ init_timer(&cfg->escan_timeout);
+ cfg->escan_timeout.data = (unsigned long) cfg;
+ cfg->escan_timeout.function = brcmf_escan_timeout;
+ INIT_WORK(&cfg->escan_timeout_work,
+ brcmf_cfg80211_escan_timeout_worker);
+}
+
+/* PFN result doesn't have all the info which are required by the supplicant
+ * (For e.g IEs) Do a target Escan so that sched scan results are reported
+ * via wl_inform_single_bss in the required format. Escan does require the
+ * scan request in the form of cfg80211_scan_request. For timebeing, create
+ * cfg80211_scan_request one out of the received PNO event.
+ */
+static s32
+brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
+ struct cfg80211_scan_request *request = NULL;
+ struct cfg80211_ssid *ssid = NULL;
+ struct ieee80211_channel *channel = NULL;
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ int err = 0;
+ int channel_req = 0;
+ int band = 0;
+ struct brcmf_pno_scanresults_le *pfn_result;
+ u32 result_count;
+ u32 status;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+ if (e->event_code == BRCMF_E_PFN_NET_LOST) {
+ brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
+ return 0;
+ }
+
+ pfn_result = (struct brcmf_pno_scanresults_le *)data;
+ result_count = le32_to_cpu(pfn_result->count);
+ status = le32_to_cpu(pfn_result->status);
+
+ /* PFN event is limited to fit 512 bytes so we may get
+ * multiple NET_FOUND events. For now place a warning here.
+ */
+ WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
+ brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
+ if (result_count > 0) {
+ int i;
+
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
+ channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
+ if (!request || !ssid || !channel) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ request->wiphy = wiphy;
+ data += sizeof(struct brcmf_pno_scanresults_le);
+ netinfo_start = (struct brcmf_pno_net_info_le *)data;
+
+ for (i = 0; i < result_count; i++) {
+ netinfo = &netinfo_start[i];
+ if (!netinfo) {
+ brcmf_err("Invalid netinfo ptr. index: %d\n",
+ i);
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
+ netinfo->SSID, netinfo->channel);
+ memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
+ ssid[i].ssid_len = netinfo->SSID_len;
+ request->n_ssids++;
+
+ channel_req = netinfo->channel;
+ if (channel_req <= CH_MAX_2G_CHANNEL)
+ band = NL80211_BAND_2GHZ;
+ else
+ band = NL80211_BAND_5GHZ;
+ channel[i].center_freq =
+ ieee80211_channel_to_frequency(channel_req,
+ band);
+ channel[i].band = band;
+ channel[i].flags |= IEEE80211_CHAN_NO_HT40;
+ request->channels[i] = &channel[i];
+ request->n_channels++;
+ }
+
+ /* assign parsed ssid array */
+ if (request->n_ssids)
+ request->ssids = &ssid[0];
+
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+ /* Abort any on-going scan */
+ brcmf_abort_scanning(cfg);
+ }
+
+ set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ cfg->escan_info.run = brcmf_run_escan;
+ err = brcmf_do_escan(cfg, wiphy, ifp, request);
+ if (err) {
+ clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ goto out_err;
+ }
+ cfg->sched_escan = true;
+ cfg->scan_request = request;
+ } else {
+ brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
+ goto out_err;
+ }
+
+ kfree(ssid);
+ kfree(channel);
+ kfree(request);
+ return 0;
+
+out_err:
+ kfree(ssid);
+ kfree(channel);
+ kfree(request);
+ cfg80211_sched_scan_stopped(wiphy);
+ return err;
+}
+
+static int brcmf_dev_pno_clean(struct net_device *ndev)
+{
+ int ret;
+
+ /* Disable pfn */
+ ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
+ if (ret == 0) {
+ /* clear pfn */
+ ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
+ NULL, 0);
+ }
+ if (ret < 0)
+ brcmf_err("failed code %d\n", ret);
+
+ return ret;
+}
+
+static int brcmf_dev_pno_config(struct brcmf_if *ifp,
+ struct cfg80211_sched_scan_request *request)
+{
+ struct brcmf_pno_param_le pfn_param;
+ struct brcmf_pno_macaddr_le pfn_mac;
+ s32 err;
+ u8 *mac_mask;
+ int i;
+
+ memset(&pfn_param, 0, sizeof(pfn_param));
+ pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
+
+ /* set extra pno params */
+ pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
+ pfn_param.repeat = BRCMF_PNO_REPEAT;
+ pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
+
+ /* set up pno scan fr */
+ pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
+
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
+ sizeof(pfn_param));
+ if (err) {
+ brcmf_err("pfn_set failed, err=%d\n", err);
+ return err;
+ }
+
+ /* Find out if mac randomization should be turned on */
+ if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
+ return 0;
+
+ pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
+ pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
+
+ memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN);
+ mac_mask = request->mac_addr_mask;
+ for (i = 0; i < ETH_ALEN; i++) {
+ pfn_mac.mac[i] &= mac_mask[i];
+ pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
+ }
+ /* Clear multi bit */
+ pfn_mac.mac[0] &= 0xFE;
+ /* Set locally administered */
+ pfn_mac.mac[0] |= 0x02;
+
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
+ sizeof(pfn_mac));
+ if (err)
+ brcmf_err("pfn_macaddr failed, err=%d\n", err);
+
+ return err;
+}
+
+static int
+brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_sched_scan_request *request)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_pno_net_param_le pfn;
+ int i;
+ int ret = 0;
+
+ brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
+ request->n_match_sets, request->n_ssids);
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+ brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
+ return -EAGAIN;
+ }
+ if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
+ brcmf_err("Scanning suppressed: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
+
+ if (!request->n_ssids || !request->n_match_sets) {
+ brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
+ request->n_ssids);
+ return -EINVAL;
+ }
+
+ if (request->n_ssids > 0) {
+ for (i = 0; i < request->n_ssids; i++) {
+ /* Active scan req for ssids */
+ brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
+ request->ssids[i].ssid);
+
+ /* match_set ssids is a supert set of n_ssid list,
+ * so we need not add these set separately.
+ */
+ }
+ }
+
+ if (request->n_match_sets > 0) {
+ /* clean up everything */
+ ret = brcmf_dev_pno_clean(ndev);
+ if (ret < 0) {
+ brcmf_err("failed error=%d\n", ret);
+ return ret;
+ }
+
+ /* configure pno */
+ if (brcmf_dev_pno_config(ifp, request))
+ return -EINVAL;
+
+ /* configure each match set */
+ for (i = 0; i < request->n_match_sets; i++) {
+ struct cfg80211_ssid *ssid;
+ u32 ssid_len;
+
+ ssid = &request->match_sets[i].ssid;
+ ssid_len = ssid->ssid_len;
+
+ if (!ssid_len) {
+ brcmf_err("skip broadcast ssid\n");
+ continue;
+ }
+ pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
+ pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
+ pfn.wsec = cpu_to_le32(0);
+ pfn.infra = cpu_to_le32(1);
+ pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
+ pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
+ memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
+ ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
+ sizeof(pfn));
+ brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
+ ret == 0 ? "set" : "failed", ssid->ssid);
+ }
+ /* Enable the PNO */
+ if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
+ brcmf_err("PNO enable failed!! ret=%d\n", ret);
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
+ struct net_device *ndev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+
+ brcmf_dbg(SCAN, "enter\n");
+ brcmf_dev_pno_clean(ndev);
+ if (cfg->sched_escan)
+ brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
+ return 0;
+}
+
+static __always_inline void brcmf_delay(u32 ms)
+{
+ if (ms < 1000 / HZ) {
+ cond_resched();
+ mdelay(ms);
+ } else {
+ msleep(ms);
+ }
+}
+
+static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
+ u8 *pattern, u32 patternsize, u8 *mask,
+ u32 packet_offset)
+{
+ struct brcmf_fil_wowl_pattern_le *filter;
+ u32 masksize;
+ u32 patternoffset;
+ u8 *buf;
+ u32 bufsize;
+ s32 ret;
+
+ masksize = (patternsize + 7) / 8;
+ patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
+
+ bufsize = sizeof(*filter) + patternsize + masksize;
+ buf = kzalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ filter = (struct brcmf_fil_wowl_pattern_le *)buf;
+
+ memcpy(filter->cmd, cmd, 4);
+ filter->masksize = cpu_to_le32(masksize);
+ filter->offset = cpu_to_le32(packet_offset);
+ filter->patternoffset = cpu_to_le32(patternoffset);
+ filter->patternsize = cpu_to_le32(patternsize);
+ filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
+
+ if ((mask) && (masksize))
+ memcpy(buf + sizeof(*filter), mask, masksize);
+ if ((pattern) && (patternsize))
+ memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
+
+ kfree(buf);
+ return ret;
+}
+
+static s32
+brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_pno_scanresults_le *pfn_result;
+ struct brcmf_pno_net_info_le *netinfo;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+ pfn_result = (struct brcmf_pno_scanresults_le *)data;
+
+ if (e->event_code == BRCMF_E_PFN_NET_LOST) {
+ brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
+ return 0;
+ }
+
+ if (le32_to_cpu(pfn_result->count) < 1) {
+ brcmf_err("Invalid result count, expected 1 (%d)\n",
+ le32_to_cpu(pfn_result->count));
+ return -EINVAL;
+ }
+
+ data += sizeof(struct brcmf_pno_scanresults_le);
+ netinfo = (struct brcmf_pno_net_info_le *)data;
+ memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
+ cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
+ cfg->wowl.nd->n_channels = 1;
+ cfg->wowl.nd->channels[0] =
+ ieee80211_channel_to_frequency(netinfo->channel,
+ netinfo->channel <= CH_MAX_2G_CHANNEL ?
+ NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
+ cfg->wowl.nd_info->n_matches = 1;
+ cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
+
+ /* Inform (the resume task) that the net detect information was recvd */
+ cfg->wowl.nd_data_completed = true;
+ wake_up(&cfg->wowl.nd_data_wait);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_wowl_wakeind_le wake_ind_le;
+ struct cfg80211_wowlan_wakeup wakeup_data;
+ struct cfg80211_wowlan_wakeup *wakeup;
+ u32 wakeind;
+ s32 err;
+ int timeout;
+
+ err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
+ sizeof(wake_ind_le));
+ if (err) {
+ brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
+ return;
+ }
+
+ wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
+ if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
+ BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
+ BRCMF_WOWL_PFN_FOUND)) {
+ wakeup = &wakeup_data;
+ memset(&wakeup_data, 0, sizeof(wakeup_data));
+ wakeup_data.pattern_idx = -1;
+
+ if (wakeind & BRCMF_WOWL_MAGIC) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
+ wakeup_data.magic_pkt = true;
+ }
+ if (wakeind & BRCMF_WOWL_DIS) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_BCN) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_RETR) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_NET) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
+ /* For now always map to pattern 0, no API to get
+ * correct information available at the moment.
+ */
+ wakeup_data.pattern_idx = 0;
+ }
+ if (wakeind & BRCMF_WOWL_PFN_FOUND) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
+ timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
+ cfg->wowl.nd_data_completed,
+ BRCMF_ND_INFO_TIMEOUT);
+ if (!timeout)
+ brcmf_err("No result for wowl net detect\n");
+ else
+ wakeup_data.net_detect = cfg->wowl.nd_info;
+ }
+ } else {
+ wakeup = NULL;
+ }
+ cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
+}
+
+#else
+
+static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+}
+
+#endif /* CONFIG_PM */
+
+static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (cfg->wowl.active) {
+ brcmf_report_wowl_wakeind(wiphy, ifp);
+ brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
+ brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
+ brcmf_configure_arp_offload(ifp, true);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
+ cfg->wowl.pre_pmmode);
+ cfg->wowl.active = false;
+ if (cfg->wowl.nd_enabled) {
+ brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev);
+ brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_notify_sched_scan_results);
+ cfg->wowl.nd_enabled = false;
+ }
+ }
+ return 0;
+}
+
+static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp,
+ struct cfg80211_wowlan *wowl)
+{
+ u32 wowl_config;
+ u32 i;
+
+ brcmf_dbg(TRACE, "Suspend, wowl config.\n");
+
+ brcmf_configure_arp_offload(ifp, false);
+ brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
+
+ wowl_config = 0;
+ if (wowl->disconnect)
+ wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
+ if (wowl->magic_pkt)
+ wowl_config |= BRCMF_WOWL_MAGIC;
+ if ((wowl->patterns) && (wowl->n_patterns)) {
+ wowl_config |= BRCMF_WOWL_NET;
+ for (i = 0; i < wowl->n_patterns; i++) {
+ brcmf_config_wowl_pattern(ifp, "add",
+ (u8 *)wowl->patterns[i].pattern,
+ wowl->patterns[i].pattern_len,
+ (u8 *)wowl->patterns[i].mask,
+ wowl->patterns[i].pkt_offset);
+ }
+ }
+ if (wowl->nd_config) {
+ brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
+ wowl->nd_config);
+ wowl_config |= BRCMF_WOWL_PFN_FOUND;
+
+ cfg->wowl.nd_data_completed = false;
+ cfg->wowl.nd_enabled = true;
+ /* Now reroute the event for PFN to the wowl function. */
+ brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_wowl_nd_results);
+ }
+ if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
+ wowl_config |= BRCMF_WOWL_UNASSOC;
+
+ brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear"));
+ brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
+ brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
+ brcmf_bus_wowl_config(cfg->pub->bus_if, true);
+ cfg->wowl.active = true;
+}
+
+static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowl)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* if the primary net_device is not READY there is nothing
+ * we can do but pray resume goes smoothly.
+ */
+ if (!check_vif_up(ifp->vif))
+ goto exit;
+
+ /* Stop scheduled scan */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
+ brcmf_cfg80211_sched_scan_stop(wiphy, ndev);
+
+ /* end any scanning */
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
+ brcmf_abort_scanning(cfg);
+
+ if (wowl == NULL) {
+ brcmf_bus_wowl_config(cfg->pub->bus_if, false);
+ list_for_each_entry(vif, &cfg->vif_list, list) {
+ if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
+ continue;
+ /* While going to suspend if associated with AP
+ * disassociate from AP to save power while system is
+ * in suspended state
+ */
+ brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
+ /* Make sure WPA_Supplicant receives all the event
+ * generated due to DISASSOC call to the fw to keep
+ * the state fw and WPA_Supplicant state consistent
+ */
+ brcmf_delay(500);
+ }
+ /* Configure MPC */
+ brcmf_set_mpc(ifp, 1);
+
+ } else {
+ /* Configure WOWL paramaters */
+ brcmf_configure_wowl(cfg, ifp, wowl);
+ }
+
+exit:
+ brcmf_dbg(TRACE, "Exit\n");
+ /* clear any scanning activity */
+ cfg->scan_status = 0;
+ return 0;
+}
+
+static __used s32
+brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
+{
+ struct brcmf_pmk_list_le *pmk_list;
+ int i;
+ u32 npmk;
+ s32 err;
+
+ pmk_list = &cfg->pmk_list;
+ npmk = le32_to_cpu(pmk_list->npmk);
+
+ brcmf_dbg(CONN, "No of elements %d\n", npmk);
+ for (i = 0; i < npmk; i++)
+ brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
+
+ err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
+ sizeof(*pmk_list));
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
+ s32 err;
+ u32 npmk, i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ npmk = le32_to_cpu(cfg->pmk_list.npmk);
+ for (i = 0; i < npmk; i++)
+ if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
+ break;
+ if (i < BRCMF_MAXPMKID) {
+ memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
+ memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
+ if (i == npmk) {
+ npmk++;
+ cfg->pmk_list.npmk = cpu_to_le32(npmk);
+ }
+ } else {
+ brcmf_err("Too many PMKSA entries cached %d\n", npmk);
+ return -EINVAL;
+ }
+
+ brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
+ for (i = 0; i < WLAN_PMKID_LEN; i += 4)
+ brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
+ pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
+ pmk[npmk].pmkid[i + 3]);
+
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
+ s32 err;
+ u32 npmk, i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid);
+
+ npmk = le32_to_cpu(cfg->pmk_list.npmk);
+ for (i = 0; i < npmk; i++)
+ if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN))
+ break;
+
+ if ((npmk > 0) && (i < npmk)) {
+ for (; i < (npmk - 1); i++) {
+ memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
+ memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
+ WLAN_PMKID_LEN);
+ }
+ memset(&pmk[i], 0, sizeof(*pmk));
+ cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
+ } else {
+ brcmf_err("Cache entry not found\n");
+ return -EINVAL;
+ }
+
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+
+}
+
+static s32
+brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+
+}
+
+static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
+{
+ s32 err;
+
+ /* set auth */
+ err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
+ if (err < 0) {
+ brcmf_err("auth error %d\n", err);
+ return err;
+ }
+ /* set wsec */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
+ if (err < 0) {
+ brcmf_err("wsec error %d\n", err);
+ return err;
+ }
+ /* set upper-layer auth */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
+ if (err < 0) {
+ brcmf_err("wpa_auth error %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
+{
+ if (is_rsn_ie)
+ return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
+
+ return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
+}
+
+static s32
+brcmf_configure_wpaie(struct brcmf_if *ifp,
+ const struct brcmf_vs_tlv *wpa_ie,
+ bool is_rsn_ie)
+{
+ u32 auth = 0; /* d11 open authentication */
+ u16 count;
+ s32 err = 0;
+ s32 len = 0;
+ u32 i;
+ u32 wsec;
+ u32 pval = 0;
+ u32 gval = 0;
+ u32 wpa_auth = 0;
+ u32 offset;
+ u8 *data;
+ u16 rsn_cap;
+ u32 wme_bss_disable;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (wpa_ie == NULL)
+ goto exit;
+
+ len = wpa_ie->len + TLV_HDR_LEN;
+ data = (u8 *)wpa_ie;
+ offset = TLV_HDR_LEN;
+ if (!is_rsn_ie)
+ offset += VS_IE_FIXED_HDR_LEN;
+ else
+ offset += WPA_IE_VERSION_LEN;
+
+ /* check for multicast cipher suite */
+ if (offset + WPA_IE_MIN_OUI_LEN > len) {
+ err = -EINVAL;
+ brcmf_err("no multicast cipher suite\n");
+ goto exit;
+ }
+
+ if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
+ err = -EINVAL;
+ brcmf_err("ivalid OUI\n");
+ goto exit;
+ }
+ offset += TLV_OUI_LEN;
+
+ /* pick up multicast cipher */
+ switch (data[offset]) {
+ case WPA_CIPHER_NONE:
+ gval = 0;
+ break;
+ case WPA_CIPHER_WEP_40:
+ case WPA_CIPHER_WEP_104:
+ gval = WEP_ENABLED;
+ break;
+ case WPA_CIPHER_TKIP:
+ gval = TKIP_ENABLED;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ gval = AES_ENABLED;
+ break;
+ default:
+ err = -EINVAL;
+ brcmf_err("Invalid multi cast cipher info\n");
+ goto exit;
+ }
+
+ offset++;
+ /* walk thru unicast cipher list and pick up what we recognize */
+ count = data[offset] + (data[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN;
+ /* Check for unicast suite(s) */
+ if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
+ err = -EINVAL;
+ brcmf_err("no unicast cipher suite\n");
+ goto exit;
+ }
+ for (i = 0; i < count; i++) {
+ if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
+ err = -EINVAL;
+ brcmf_err("ivalid OUI\n");
+ goto exit;
+ }
+ offset += TLV_OUI_LEN;
+ switch (data[offset]) {
+ case WPA_CIPHER_NONE:
+ break;
+ case WPA_CIPHER_WEP_40:
+ case WPA_CIPHER_WEP_104:
+ pval |= WEP_ENABLED;
+ break;
+ case WPA_CIPHER_TKIP:
+ pval |= TKIP_ENABLED;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ pval |= AES_ENABLED;
+ break;
+ default:
+ brcmf_err("Ivalid unicast security info\n");
+ }
+ offset++;
+ }
+ /* walk thru auth management suite list and pick up what we recognize */
+ count = data[offset] + (data[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN;
+ /* Check for auth key management suite(s) */
+ if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
+ err = -EINVAL;
+ brcmf_err("no auth key mgmt suite\n");
+ goto exit;
+ }
+ for (i = 0; i < count; i++) {
+ if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
+ err = -EINVAL;
+ brcmf_err("ivalid OUI\n");
+ goto exit;
+ }
+ offset += TLV_OUI_LEN;
+ switch (data[offset]) {
+ case RSN_AKM_NONE:
+ brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
+ wpa_auth |= WPA_AUTH_NONE;
+ break;
+ case RSN_AKM_UNSPECIFIED:
+ brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
+ is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
+ (wpa_auth |= WPA_AUTH_UNSPECIFIED);
+ break;
+ case RSN_AKM_PSK:
+ brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
+ is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
+ (wpa_auth |= WPA_AUTH_PSK);
+ break;
+ default:
+ brcmf_err("Ivalid key mgmt info\n");
+ }
+ offset++;
+ }
+
+ if (is_rsn_ie) {
+ wme_bss_disable = 1;
+ if ((offset + RSN_CAP_LEN) <= len) {
+ rsn_cap = data[offset] + (data[offset + 1] << 8);
+ if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
+ wme_bss_disable = 0;
+ }
+ /* set wme_bss_disable to sync RSN Capabilities */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
+ wme_bss_disable);
+ if (err < 0) {
+ brcmf_err("wme_bss_disable error %d\n", err);
+ goto exit;
+ }
+ }
+ /* FOR WPS , set SES_OW_ENABLED */
+ wsec = (pval | gval | SES_OW_ENABLED);
+
+ /* set auth */
+ err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
+ if (err < 0) {
+ brcmf_err("auth error %d\n", err);
+ goto exit;
+ }
+ /* set wsec */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
+ if (err < 0) {
+ brcmf_err("wsec error %d\n", err);
+ goto exit;
+ }
+ /* set upper-layer auth */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
+ if (err < 0) {
+ brcmf_err("wpa_auth error %d\n", err);
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+static s32
+brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
+ struct parsed_vndr_ies *vndr_ies)
+{
+ struct brcmf_vs_tlv *vndrie;
+ struct brcmf_tlv *ie;
+ struct parsed_vndr_ie_info *parsed_info;
+ s32 remaining_len;
+
+ remaining_len = (s32)vndr_ie_len;
+ memset(vndr_ies, 0, sizeof(*vndr_ies));
+
+ ie = (struct brcmf_tlv *)vndr_ie_buf;
+ while (ie) {
+ if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
+ goto next;
+ vndrie = (struct brcmf_vs_tlv *)ie;
+ /* len should be bigger than OUI length + one */
+ if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
+ brcmf_err("invalid vndr ie. length is too small %d\n",
+ vndrie->len);
+ goto next;
+ }
+ /* if wpa or wme ie, do not add ie */
+ if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
+ ((vndrie->oui_type == WPA_OUI_TYPE) ||
+ (vndrie->oui_type == WME_OUI_TYPE))) {
+ brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
+ goto next;
+ }
+
+ parsed_info = &vndr_ies->ie_info[vndr_ies->count];
+
+ /* save vndr ie information */
+ parsed_info->ie_ptr = (char *)vndrie;
+ parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
+ memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
+
+ vndr_ies->count++;
+
+ brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
+ parsed_info->vndrie.oui[0],
+ parsed_info->vndrie.oui[1],
+ parsed_info->vndrie.oui[2],
+ parsed_info->vndrie.oui_type);
+
+ if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
+ break;
+next:
+ remaining_len -= (ie->len + TLV_HDR_LEN);
+ if (remaining_len <= TLV_HDR_LEN)
+ ie = NULL;
+ else
+ ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
+ TLV_HDR_LEN);
+ }
+ return 0;
+}
+
+static u32
+brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
+{
+
+ strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
+ iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
+
+ put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
+
+ put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
+
+ memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
+
+ return ie_len + VNDR_IE_HDR_SIZE;
+}
+
+s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
+ const u8 *vndr_ie_buf, u32 vndr_ie_len)
+{
+ struct brcmf_if *ifp;
+ struct vif_saved_ie *saved_ie;
+ s32 err = 0;
+ u8 *iovar_ie_buf;
+ u8 *curr_ie_buf;
+ u8 *mgmt_ie_buf = NULL;
+ int mgmt_ie_buf_len;
+ u32 *mgmt_ie_len;
+ u32 del_add_ie_buf_len = 0;
+ u32 total_ie_buf_len = 0;
+ u32 parsed_ie_buf_len = 0;
+ struct parsed_vndr_ies old_vndr_ies;
+ struct parsed_vndr_ies new_vndr_ies;
+ struct parsed_vndr_ie_info *vndrie_info;
+ s32 i;
+ u8 *ptr;
+ int remained_buf_len;
+
+ if (!vif)
+ return -ENODEV;
+ ifp = vif->ifp;
+ saved_ie = &vif->saved_ie;
+
+ brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
+ pktflag);
+ iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+ if (!iovar_ie_buf)
+ return -ENOMEM;
+ curr_ie_buf = iovar_ie_buf;
+ switch (pktflag) {
+ case BRCMF_VNDR_IE_PRBREQ_FLAG:
+ mgmt_ie_buf = saved_ie->probe_req_ie;
+ mgmt_ie_len = &saved_ie->probe_req_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
+ break;
+ case BRCMF_VNDR_IE_PRBRSP_FLAG:
+ mgmt_ie_buf = saved_ie->probe_res_ie;
+ mgmt_ie_len = &saved_ie->probe_res_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
+ break;
+ case BRCMF_VNDR_IE_BEACON_FLAG:
+ mgmt_ie_buf = saved_ie->beacon_ie;
+ mgmt_ie_len = &saved_ie->beacon_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
+ break;
+ case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
+ mgmt_ie_buf = saved_ie->assoc_req_ie;
+ mgmt_ie_len = &saved_ie->assoc_req_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
+ break;
+ default:
+ err = -EPERM;
+ brcmf_err("not suitable type\n");
+ goto exit;
+ }
+
+ if (vndr_ie_len > mgmt_ie_buf_len) {
+ err = -ENOMEM;
+ brcmf_err("extra IE size too big\n");
+ goto exit;
+ }
+
+ /* parse and save new vndr_ie in curr_ie_buff before comparing it */
+ if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
+ ptr = curr_ie_buf;
+ brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
+ for (i = 0; i < new_vndr_ies.count; i++) {
+ vndrie_info = &new_vndr_ies.ie_info[i];
+ memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
+ vndrie_info->ie_len);
+ parsed_ie_buf_len += vndrie_info->ie_len;
+ }
+ }
+
+ if (mgmt_ie_buf && *mgmt_ie_len) {
+ if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
+ (memcmp(mgmt_ie_buf, curr_ie_buf,
+ parsed_ie_buf_len) == 0)) {
+ brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
+ goto exit;
+ }
+
+ /* parse old vndr_ie */
+ brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
+
+ /* make a command to delete old ie */
+ for (i = 0; i < old_vndr_ies.count; i++) {
+ vndrie_info = &old_vndr_ies.ie_info[i];
+
+ brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
+ vndrie_info->vndrie.id,
+ vndrie_info->vndrie.len,
+ vndrie_info->vndrie.oui[0],
+ vndrie_info->vndrie.oui[1],
+ vndrie_info->vndrie.oui[2]);
+
+ del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
+ vndrie_info->ie_ptr,
+ vndrie_info->ie_len,
+ "del");
+ curr_ie_buf += del_add_ie_buf_len;
+ total_ie_buf_len += del_add_ie_buf_len;
+ }
+ }
+
+ *mgmt_ie_len = 0;
+ /* Add if there is any extra IE */
+ if (mgmt_ie_buf && parsed_ie_buf_len) {
+ ptr = mgmt_ie_buf;
+
+ remained_buf_len = mgmt_ie_buf_len;
+
+ /* make a command to add new ie */
+ for (i = 0; i < new_vndr_ies.count; i++) {
+ vndrie_info = &new_vndr_ies.ie_info[i];
+
+ /* verify remained buf size before copy data */
+ if (remained_buf_len < (vndrie_info->vndrie.len +
+ VNDR_IE_VSIE_OFFSET)) {
+ brcmf_err("no space in mgmt_ie_buf: len left %d",
+ remained_buf_len);
+ break;
+ }
+ remained_buf_len -= (vndrie_info->ie_len +
+ VNDR_IE_VSIE_OFFSET);
+
+ brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
+ vndrie_info->vndrie.id,
+ vndrie_info->vndrie.len,
+ vndrie_info->vndrie.oui[0],
+ vndrie_info->vndrie.oui[1],
+ vndrie_info->vndrie.oui[2]);
+
+ del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
+ vndrie_info->ie_ptr,
+ vndrie_info->ie_len,
+ "add");
+
+ /* save the parsed IE in wl struct */
+ memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
+ vndrie_info->ie_len);
+ *mgmt_ie_len += vndrie_info->ie_len;
+
+ curr_ie_buf += del_add_ie_buf_len;
+ total_ie_buf_len += del_add_ie_buf_len;
+ }
+ }
+ if (total_ie_buf_len) {
+ err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
+ total_ie_buf_len);
+ if (err)
+ brcmf_err("vndr ie set error : %d\n", err);
+ }
+
+exit:
+ kfree(iovar_ie_buf);
+ return err;
+}
+
+s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
+{
+ s32 pktflags[] = {
+ BRCMF_VNDR_IE_PRBREQ_FLAG,
+ BRCMF_VNDR_IE_PRBRSP_FLAG,
+ BRCMF_VNDR_IE_BEACON_FLAG
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pktflags); i++)
+ brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
+
+ memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
+ return 0;
+}
+
+static s32
+brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
+ struct cfg80211_beacon_data *beacon)
+{
+ s32 err;
+
+ /* Set Beacon IEs to FW */
+ err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
+ beacon->tail, beacon->tail_len);
+ if (err) {
+ brcmf_err("Set Beacon IE Failed\n");
+ return err;
+ }
+ brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
+
+ /* Set Probe Response IEs to FW */
+ err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
+ beacon->proberesp_ies,
+ beacon->proberesp_ies_len);
+ if (err)
+ brcmf_err("Set Probe Resp IE Failed\n");
+ else
+ brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_ap_settings *settings)
+{
+ s32 ie_offset;
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ const struct brcmf_tlv *ssid_ie;
+ const struct brcmf_tlv *country_ie;
+ struct brcmf_ssid_le ssid_le;
+ s32 err = -EPERM;
+ const struct brcmf_tlv *rsn_ie;
+ const struct brcmf_vs_tlv *wpa_ie;
+ struct brcmf_join_params join_params;
+ enum nl80211_iftype dev_role;
+ struct brcmf_fil_bss_enable_le bss_enable;
+ u16 chanspec;
+ bool mbss;
+ int is_11d;
+
+ brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
+ settings->chandef.chan->hw_value,
+ settings->chandef.center_freq1, settings->chandef.width,
+ settings->beacon_interval, settings->dtim_period);
+ brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
+ settings->ssid, settings->ssid_len, settings->auth_type,
+ settings->inactivity_timeout);
+ dev_role = ifp->vif->wdev.iftype;
+ mbss = ifp->vif->mbss;
+
+ /* store current 11d setting */
+ brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
+ country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
+ settings->beacon.tail_len,
+ WLAN_EID_COUNTRY);
+ is_11d = country_ie ? 1 : 0;
+
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ if (settings->ssid == NULL || settings->ssid_len == 0) {
+ ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
+ ssid_ie = brcmf_parse_tlvs(
+ (u8 *)&settings->beacon.head[ie_offset],
+ settings->beacon.head_len - ie_offset,
+ WLAN_EID_SSID);
+ if (!ssid_ie)
+ return -EINVAL;
+
+ memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
+ ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
+ brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
+ } else {
+ memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
+ ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
+ }
+
+ if (!mbss) {
+ brcmf_set_mpc(ifp, 0);
+ brcmf_configure_arp_offload(ifp, false);
+ }
+
+ /* find the RSN_IE */
+ rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
+ settings->beacon.tail_len, WLAN_EID_RSN);
+
+ /* find the WPA_IE */
+ wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
+ settings->beacon.tail_len);
+
+ if ((wpa_ie != NULL || rsn_ie != NULL)) {
+ brcmf_dbg(TRACE, "WPA(2) IE is found\n");
+ if (wpa_ie != NULL) {
+ /* WPA IE */
+ err = brcmf_configure_wpaie(ifp, wpa_ie, false);
+ if (err < 0)
+ goto exit;
+ } else {
+ struct brcmf_vs_tlv *tmp_ie;
+
+ tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
+
+ /* RSN IE */
+ err = brcmf_configure_wpaie(ifp, tmp_ie, true);
+ if (err < 0)
+ goto exit;
+ }
+ } else {
+ brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
+ brcmf_configure_opensecurity(ifp);
+ }
+
+ brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
+
+ if (!mbss) {
+ chanspec = chandef_to_chanspec(&cfg->d11inf,
+ &settings->chandef);
+ err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
+ if (err < 0) {
+ brcmf_err("Set Channel failed: chspec=%d, %d\n",
+ chanspec, err);
+ goto exit;
+ }
+
+ if (is_11d != ifp->vif->is_11d) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
+ is_11d);
+ if (err < 0) {
+ brcmf_err("Regulatory Set Error, %d\n", err);
+ goto exit;
+ }
+ }
+ if (settings->beacon_interval) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
+ settings->beacon_interval);
+ if (err < 0) {
+ brcmf_err("Beacon Interval Set Error, %d\n",
+ err);
+ goto exit;
+ }
+ }
+ if (settings->dtim_period) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
+ settings->dtim_period);
+ if (err < 0) {
+ brcmf_err("DTIM Interval Set Error, %d\n", err);
+ goto exit;
+ }
+ }
+
+ if ((dev_role == NL80211_IFTYPE_AP) &&
+ ((ifp->ifidx == 0) ||
+ !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_DOWN error %d\n", err);
+ goto exit;
+ }
+ brcmf_fil_iovar_int_set(ifp, "apsta", 0);
+ }
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
+ if (err < 0) {
+ brcmf_err("SET INFRA error %d\n", err);
+ goto exit;
+ }
+ } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
+ /* Multiple-BSS should use same 11d configuration */
+ err = -EINVAL;
+ goto exit;
+ }
+ if (dev_role == NL80211_IFTYPE_AP) {
+ if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
+ brcmf_fil_iovar_int_set(ifp, "mbss", 1);
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
+ if (err < 0) {
+ brcmf_err("setting AP mode failed %d\n", err);
+ goto exit;
+ }
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_UP error (%d)\n", err);
+ goto exit;
+ }
+ /* On DOWN the firmware removes the WEP keys, reconfigure
+ * them if they were set.
+ */
+ brcmf_cfg80211_reconfigure_wep(ifp);
+
+ memset(&join_params, 0, sizeof(join_params));
+ /* join parameters starts with ssid */
+ memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
+ /* create softap */
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, sizeof(join_params));
+ if (err < 0) {
+ brcmf_err("SET SSID error (%d)\n", err);
+ goto exit;
+ }
+ brcmf_dbg(TRACE, "AP mode configuration complete\n");
+ } else {
+ err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
+ sizeof(ssid_le));
+ if (err < 0) {
+ brcmf_err("setting ssid failed %d\n", err);
+ goto exit;
+ }
+ bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
+ bss_enable.enable = cpu_to_le32(1);
+ err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
+ sizeof(bss_enable));
+ if (err < 0) {
+ brcmf_err("bss_enable config failed %d\n", err);
+ goto exit;
+ }
+
+ brcmf_dbg(TRACE, "GO mode configuration complete\n");
+ }
+ set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+ brcmf_net_setcarrier(ifp, true);
+
+exit:
+ if ((err) && (!mbss)) {
+ brcmf_set_mpc(ifp, 1);
+ brcmf_configure_arp_offload(ifp, true);
+ }
+ return err;
+}
+
+static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+ struct brcmf_fil_bss_enable_le bss_enable;
+ struct brcmf_join_params join_params;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
+ /* Due to most likely deauths outstanding we sleep */
+ /* first to make sure they get processed by fw. */
+ msleep(400);
+
+ if (ifp->vif->mbss) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ return err;
+ }
+
+ memset(&join_params, 0, sizeof(join_params));
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, sizeof(join_params));
+ if (err < 0)
+ brcmf_err("SET SSID error (%d)\n", err);
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ if (err < 0)
+ brcmf_err("BRCMF_C_DOWN error %d\n", err);
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
+ if (err < 0)
+ brcmf_err("setting AP mode failed %d\n", err);
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
+ if (err < 0)
+ brcmf_err("setting INFRA mode failed %d\n", err);
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
+ brcmf_fil_iovar_int_set(ifp, "mbss", 0);
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
+ ifp->vif->is_11d);
+ if (err < 0)
+ brcmf_err("restoring REGULATORY setting failed %d\n",
+ err);
+ /* Bring device back up so it can be used again */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+ if (err < 0)
+ brcmf_err("BRCMF_C_UP error %d\n", err);
+ } else {
+ bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
+ bss_enable.enable = cpu_to_le32(0);
+ err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
+ sizeof(bss_enable));
+ if (err < 0)
+ brcmf_err("bss_enable config failed %d\n", err);
+ }
+ brcmf_set_mpc(ifp, 1);
+ brcmf_configure_arp_offload(ifp, true);
+ clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+ brcmf_net_setcarrier(ifp, false);
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_beacon_data *info)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
+
+ return err;
+}
+
+static int
+brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
+ struct station_del_parameters *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_scb_val_le scbval;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ if (!params->mac)
+ return -EFAULT;
+
+ brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
+
+ if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ memcpy(&scbval.ea, params->mac, ETH_ALEN);
+ scbval.val = cpu_to_le32(params->reason_code);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
+ &scbval, sizeof(scbval));
+ if (err)
+ brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static int
+brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
+ const u8 *mac, struct station_parameters *params)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
+ params->sta_flags_mask, params->sta_flags_set);
+
+ /* Ignore all 00 MAC */
+ if (is_zero_ether_addr(mac))
+ return 0;
+
+ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+ return 0;
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
+ (void *)mac, ETH_ALEN);
+ else
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
+ (void *)mac, ETH_ALEN);
+ if (err < 0)
+ brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
+
+ return err;
+}
+
+static void
+brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u16 frame_type, bool reg)
+{
+ struct brcmf_cfg80211_vif *vif;
+ u16 mgmt_type;
+
+ brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
+
+ mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ if (reg)
+ vif->mgmt_rx_reg |= BIT(mgmt_type);
+ else
+ vif->mgmt_rx_reg &= ~BIT(mgmt_type);
+}
+
+
+static int
+brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct ieee80211_channel *chan = params->chan;
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ const struct ieee80211_mgmt *mgmt;
+ struct brcmf_cfg80211_vif *vif;
+ s32 err = 0;
+ s32 ie_offset;
+ s32 ie_len;
+ struct brcmf_fil_action_frame_le *action_frame;
+ struct brcmf_fil_af_params_le *af_params;
+ bool ack;
+ s32 chan_nr;
+ u32 freq;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ *cookie = 0;
+
+ mgmt = (const struct ieee80211_mgmt *)buf;
+
+ if (!ieee80211_is_mgmt(mgmt->frame_control)) {
+ brcmf_err("Driver only allows MGMT packet type\n");
+ 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 */
+ /* wpa_supplicant. Unfortunately the probe is send */
+ /* on primary ndev, while dongle wants it on the p2p */
+ /* vif. Since this is only reason for a probe */
+ /* response to be sent, the vif is taken from cfg. */
+ /* If ever desired to send proberesp for non p2p */
+ /* response then data should be checked for */
+ /* "DIRECT-". Note in future supplicant will take */
+ /* dedicated p2p wdev to do this and then this 'hack'*/
+ /* is not needed anymore. */
+ ie_offset = DOT11_MGMT_HDR_LEN +
+ DOT11_BCN_PRB_FIXED_LEN;
+ ie_len = len - ie_offset;
+ 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,
+ BRCMF_VNDR_IE_PRBRSP_FLAG,
+ &buf[ie_offset],
+ ie_len);
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+ GFP_KERNEL);
+ } else if (ieee80211_is_action(mgmt->frame_control)) {
+ af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
+ if (af_params == NULL) {
+ brcmf_err("unable to allocate frame\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+ action_frame = &af_params->action_frame;
+ /* Add the packet Id */
+ action_frame->packet_id = cpu_to_le32(*cookie);
+ /* Add BSSID */
+ memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
+ 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. 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), freq);
+
+ ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
+ af_params);
+
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
+ GFP_KERNEL);
+ kfree(af_params);
+ } else {
+ brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
+ brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
+ }
+
+exit:
+ return err;
+}
+
+
+static int
+brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+ int err = 0;
+
+ brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
+
+ vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (vif == NULL) {
+ brcmf_err("No p2p device available for probe response\n");
+ err = -ENODEV;
+ goto exit;
+ }
+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+exit:
+ return err;
+}
+
+static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_crit_proto_id proto,
+ u16 duration)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ /* only DHCP support for now */
+ if (proto != NL80211_CRIT_PROTO_DHCP)
+ return -EINVAL;
+
+ /* suppress and abort scanning */
+ set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+ brcmf_abort_scanning(cfg);
+
+ return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
+}
+
+static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
+ clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+}
+
+static s32
+brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ switch (e->reason) {
+ case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
+ brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
+ break;
+ case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
+ brcmf_dbg(TRACE, "TDLS Peer Connected\n");
+ brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+ break;
+ case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
+ brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
+ brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+ break;
+ }
+
+ return 0;
+}
+
+static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
+{
+ int ret;
+
+ switch (oper) {
+ case NL80211_TDLS_DISCOVERY_REQ:
+ ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
+ break;
+ case NL80211_TDLS_SETUP:
+ ret = BRCMF_TDLS_MANUAL_EP_CREATE;
+ break;
+ case NL80211_TDLS_TEARDOWN:
+ ret = BRCMF_TDLS_MANUAL_EP_DELETE;
+ break;
+ default:
+ brcmf_err("unsupported operation: %d\n", oper);
+ ret = -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
+ struct net_device *ndev, const u8 *peer,
+ enum nl80211_tdls_operation oper)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_tdls_iovar_le info;
+ int ret = 0;
+
+ ret = brcmf_convert_nl80211_tdls_oper(oper);
+ if (ret < 0)
+ return ret;
+
+ ifp = netdev_priv(ndev);
+ memset(&info, 0, sizeof(info));
+ info.mode = (u8)ret;
+ if (peer)
+ memcpy(info.ea, peer, ETH_ALEN);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
+ &info, sizeof(info));
+ if (ret < 0)
+ brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
+
+ return ret;
+}
+
+static struct cfg80211_ops wl_cfg80211_ops = {
+ .add_virtual_intf = brcmf_cfg80211_add_iface,
+ .del_virtual_intf = brcmf_cfg80211_del_iface,
+ .change_virtual_intf = brcmf_cfg80211_change_iface,
+ .scan = brcmf_cfg80211_scan,
+ .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
+ .join_ibss = brcmf_cfg80211_join_ibss,
+ .leave_ibss = brcmf_cfg80211_leave_ibss,
+ .get_station = brcmf_cfg80211_get_station,
+ .dump_station = brcmf_cfg80211_dump_station,
+ .set_tx_power = brcmf_cfg80211_set_tx_power,
+ .get_tx_power = brcmf_cfg80211_get_tx_power,
+ .add_key = brcmf_cfg80211_add_key,
+ .del_key = brcmf_cfg80211_del_key,
+ .get_key = brcmf_cfg80211_get_key,
+ .set_default_key = brcmf_cfg80211_config_default_key,
+ .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
+ .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
+ .connect = brcmf_cfg80211_connect,
+ .disconnect = brcmf_cfg80211_disconnect,
+ .suspend = brcmf_cfg80211_suspend,
+ .resume = brcmf_cfg80211_resume,
+ .set_pmksa = brcmf_cfg80211_set_pmksa,
+ .del_pmksa = brcmf_cfg80211_del_pmksa,
+ .flush_pmksa = brcmf_cfg80211_flush_pmksa,
+ .start_ap = brcmf_cfg80211_start_ap,
+ .stop_ap = brcmf_cfg80211_stop_ap,
+ .change_beacon = brcmf_cfg80211_change_beacon,
+ .del_station = brcmf_cfg80211_del_station,
+ .change_station = brcmf_cfg80211_change_station,
+ .sched_scan_start = brcmf_cfg80211_sched_scan_start,
+ .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
+ .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
+ .mgmt_tx = brcmf_cfg80211_mgmt_tx,
+ .remain_on_channel = brcmf_p2p_remain_on_channel,
+ .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
+ .start_p2p_device = brcmf_p2p_start_device,
+ .stop_p2p_device = brcmf_p2p_stop_device,
+ .crit_proto_start = brcmf_cfg80211_crit_proto_start,
+ .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
+ .tdls_oper = brcmf_cfg80211_tdls_oper,
+};
+
+struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
+ enum nl80211_iftype type,
+ bool pm_block)
+{
+ struct brcmf_cfg80211_vif *vif_walk;
+ struct brcmf_cfg80211_vif *vif;
+ bool mbss;
+
+ brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
+ sizeof(*vif));
+ vif = kzalloc(sizeof(*vif), GFP_KERNEL);
+ if (!vif)
+ return ERR_PTR(-ENOMEM);
+
+ vif->wdev.wiphy = cfg->wiphy;
+ vif->wdev.iftype = type;
+
+ vif->pm_block = pm_block;
+
+ brcmf_init_prof(&vif->profile);
+
+ if (type == NL80211_IFTYPE_AP) {
+ mbss = false;
+ list_for_each_entry(vif_walk, &cfg->vif_list, list) {
+ if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
+ mbss = true;
+ break;
+ }
+ }
+ vif->mbss = mbss;
+ }
+
+ list_add_tail(&vif->list, &cfg->vif_list);
+ return vif;
+}
+
+void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
+{
+ list_del(&vif->list);
+ kfree(vif);
+}
+
+void brcmf_cfg80211_free_netdev(struct net_device *ndev)
+{
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+
+ ifp = netdev_priv(ndev);
+ vif = ifp->vif;
+
+ if (vif)
+ brcmf_free_vif(vif);
+ free_netdev(ndev);
+}
+
+static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
+{
+ u32 event = e->event_code;
+ u32 status = e->status;
+
+ if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
+ brcmf_dbg(CONN, "Processing set ssid\n");
+ return true;
+ }
+
+ return false;
+}
+
+static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
+{
+ u32 event = e->event_code;
+ u16 flags = e->flags;
+
+ if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
+ (event == BRCMF_E_DISASSOC_IND) ||
+ ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
+ brcmf_dbg(CONN, "Processing link down\n");
+ return true;
+ }
+ return false;
+}
+
+static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
+ const struct brcmf_event_msg *e)
+{
+ u32 event = e->event_code;
+ u32 status = e->status;
+
+ if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
+ brcmf_dbg(CONN, "Processing Link %s & no network found\n",
+ e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
+ return true;
+ }
+
+ if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
+ brcmf_dbg(CONN, "Processing connecting & no network found\n");
+ return true;
+ }
+
+ return false;
+}
+
+static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+
+ kfree(conn_info->req_ie);
+ conn_info->req_ie = NULL;
+ conn_info->req_ie_len = 0;
+ kfree(conn_info->resp_ie);
+ conn_info->resp_ie = NULL;
+ conn_info->resp_ie_len = 0;
+}
+
+static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+ u32 req_len;
+ u32 resp_len;
+ s32 err = 0;
+
+ brcmf_clear_assoc_ies(cfg);
+
+ err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
+ cfg->extra_buf, WL_ASSOC_INFO_MAX);
+ if (err) {
+ brcmf_err("could not get assoc info (%d)\n", err);
+ return err;
+ }
+ assoc_info =
+ (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
+ req_len = le32_to_cpu(assoc_info->req_len);
+ resp_len = le32_to_cpu(assoc_info->resp_len);
+ if (req_len) {
+ err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
+ cfg->extra_buf,
+ WL_ASSOC_INFO_MAX);
+ if (err) {
+ brcmf_err("could not get assoc req (%d)\n", err);
+ return err;
+ }
+ conn_info->req_ie_len = req_len;
+ conn_info->req_ie =
+ kmemdup(cfg->extra_buf, conn_info->req_ie_len,
+ GFP_KERNEL);
+ } else {
+ conn_info->req_ie_len = 0;
+ conn_info->req_ie = NULL;
+ }
+ if (resp_len) {
+ err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
+ cfg->extra_buf,
+ WL_ASSOC_INFO_MAX);
+ if (err) {
+ brcmf_err("could not get assoc resp (%d)\n", err);
+ return err;
+ }
+ conn_info->resp_ie_len = resp_len;
+ conn_info->resp_ie =
+ kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
+ GFP_KERNEL);
+ } else {
+ conn_info->resp_ie_len = 0;
+ conn_info->resp_ie = NULL;
+ }
+ brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
+ conn_info->req_ie_len, conn_info->resp_ie_len);
+
+ return err;
+}
+
+static s32
+brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ struct ieee80211_channel *notify_channel = NULL;
+ struct ieee80211_supported_band *band;
+ struct brcmf_bss_info_le *bi;
+ struct brcmu_chan ch;
+ u32 freq;
+ s32 err = 0;
+ u8 *buf;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_get_assoc_ies(cfg, ifp);
+ memcpy(profile->bssid, e->addr, ETH_ALEN);
+ brcmf_update_bss_info(cfg, ifp);
+
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ /* data sent to dongle has to be little endian */
+ *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
+ buf, WL_BSS_INFO_MAX);
+
+ if (err)
+ goto done;
+
+ bi = (struct brcmf_bss_info_le *)(buf + 4);
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ if (ch.band == BRCMU_CHAN_BAND_2G)
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+done:
+ kfree(buf);
+ cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
+ conn_info->req_ie, conn_info->req_ie_len,
+ conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
+ brcmf_dbg(CONN, "Report roaming result\n");
+
+ set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev, const struct brcmf_event_msg *e,
+ bool completed)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &ifp->vif->sme_state)) {
+ if (completed) {
+ brcmf_get_assoc_ies(cfg, ifp);
+ memcpy(profile->bssid, e->addr, ETH_ALEN);
+ brcmf_update_bss_info(cfg, ifp);
+ set_bit(BRCMF_VIF_STATUS_CONNECTED,
+ &ifp->vif->sme_state);
+ }
+ cfg80211_connect_result(ndev,
+ (u8 *)profile->bssid,
+ conn_info->req_ie,
+ conn_info->req_ie_len,
+ conn_info->resp_ie,
+ conn_info->resp_ie_len,
+ completed ? WLAN_STATUS_SUCCESS :
+ WLAN_STATUS_AUTH_TIMEOUT,
+ GFP_KERNEL);
+ brcmf_dbg(CONN, "Report connect result - connection %s\n",
+ completed ? "succeeded" : "failed");
+ }
+ brcmf_dbg(TRACE, "Exit\n");
+ return 0;
+}
+
+static s32
+brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ static int generation;
+ u32 event = e->event_code;
+ u32 reason = e->reason;
+ struct station_info sinfo;
+
+ brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
+ if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
+ ndev != cfg_to_ndev(cfg)) {
+ brcmf_dbg(CONN, "AP mode link down\n");
+ complete(&cfg->vif_disabled);
+ if (ifp->vif->mbss)
+ brcmf_remove_interface(ifp);
+ return 0;
+ }
+
+ if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
+ (reason == BRCMF_E_STATUS_SUCCESS)) {
+ memset(&sinfo, 0, sizeof(sinfo));
+ if (!data) {
+ brcmf_err("No IEs present in ASSOC/REASSOC_IND");
+ return -EINVAL;
+ }
+ sinfo.assoc_req_ies = data;
+ sinfo.assoc_req_ies_len = e->datalen;
+ generation++;
+ sinfo.generation = generation;
+ cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
+ } else if ((event == BRCMF_E_DISASSOC_IND) ||
+ (event == BRCMF_E_DEAUTH_IND) ||
+ (event == BRCMF_E_DEAUTH)) {
+ cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
+ }
+ return 0;
+}
+
+static s32
+brcmf_notify_connect_status(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct net_device *ndev = ifp->ndev;
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct ieee80211_channel *chan;
+ s32 err = 0;
+
+ if ((e->event_code == BRCMF_E_DEAUTH) ||
+ (e->event_code == BRCMF_E_DEAUTH_IND) ||
+ (e->event_code == BRCMF_E_DISASSOC_IND) ||
+ ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
+ brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+ }
+
+ if (brcmf_is_apmode(ifp->vif)) {
+ err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
+ } else if (brcmf_is_linkup(e)) {
+ brcmf_dbg(CONN, "Linkup\n");
+ if (brcmf_is_ibssmode(ifp->vif)) {
+ brcmf_inform_ibss(cfg, ndev, e->addr);
+ chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
+ memcpy(profile->bssid, e->addr, ETH_ALEN);
+ cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &ifp->vif->sme_state);
+ set_bit(BRCMF_VIF_STATUS_CONNECTED,
+ &ifp->vif->sme_state);
+ } else
+ brcmf_bss_connect_done(cfg, ndev, e, true);
+ brcmf_net_setcarrier(ifp, true);
+ } else if (brcmf_is_linkdown(e)) {
+ brcmf_dbg(CONN, "Linkdown\n");
+ if (!brcmf_is_ibssmode(ifp->vif)) {
+ brcmf_bss_connect_done(cfg, ndev, e, false);
+ brcmf_link_down(ifp->vif,
+ brcmf_map_fw_linkdown_reason(e));
+ brcmf_init_prof(ndev_to_prof(ndev));
+ if (ndev != cfg_to_ndev(cfg))
+ complete(&cfg->vif_disabled);
+ brcmf_net_setcarrier(ifp, false);
+ }
+ } else if (brcmf_is_nonetwork(cfg, e)) {
+ if (brcmf_is_ibssmode(ifp->vif))
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &ifp->vif->sme_state);
+ else
+ brcmf_bss_connect_done(cfg, ndev, e, false);
+ }
+
+ return err;
+}
+
+static s32
+brcmf_notify_roaming_status(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ u32 event = e->event_code;
+ u32 status = e->status;
+
+ if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
+ brcmf_bss_roaming_done(cfg, ifp->ndev, e);
+ else
+ brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
+ }
+
+ return 0;
+}
+
+static s32
+brcmf_notify_mic_status(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ u16 flags = e->flags;
+ enum nl80211_key_type key_type;
+
+ if (flags & BRCMF_EVENT_MSG_GROUP)
+ key_type = NL80211_KEYTYPE_GROUP;
+ else
+ key_type = NL80211_KEYTYPE_PAIRWISE;
+
+ cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
+ NULL, GFP_KERNEL);
+
+ return 0;
+}
+
+static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
+ ifevent->action, ifevent->flags, ifevent->ifidx,
+ ifevent->bsscfgidx);
+
+ mutex_lock(&event->vif_event_lock);
+ event->action = ifevent->action;
+ vif = event->vif;
+
+ switch (ifevent->action) {
+ case BRCMF_E_IF_ADD:
+ /* waiting process may have timed out */
+ if (!cfg->vif_event.vif) {
+ mutex_unlock(&event->vif_event_lock);
+ return -EBADF;
+ }
+
+ ifp->vif = vif;
+ vif->ifp = ifp;
+ if (ifp->ndev) {
+ vif->wdev.netdev = ifp->ndev;
+ ifp->ndev->ieee80211_ptr = &vif->wdev;
+ SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
+ }
+ mutex_unlock(&event->vif_event_lock);
+ wake_up(&event->vif_wq);
+ return 0;
+
+ case BRCMF_E_IF_DEL:
+ mutex_unlock(&event->vif_event_lock);
+ /* event may not be upon user request */
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ wake_up(&event->vif_wq);
+ return 0;
+
+ case BRCMF_E_IF_CHANGE:
+ mutex_unlock(&event->vif_event_lock);
+ wake_up(&event->vif_wq);
+ return 0;
+
+ default:
+ mutex_unlock(&event->vif_event_lock);
+ break;
+ }
+ return -EINVAL;
+}
+
+static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
+{
+ conf->frag_threshold = (u32)-1;
+ conf->rts_threshold = (u32)-1;
+ conf->retry_short = (u32)-1;
+ conf->retry_long = (u32)-1;
+}
+
+static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
+{
+ brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
+ brcmf_notify_roaming_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
+ brcmf_notify_mic_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_notify_sched_scan_results);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
+ brcmf_notify_vif_event);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
+ brcmf_p2p_notify_rx_mgmt_p2p_probereq);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
+ brcmf_p2p_notify_listen_complete);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
+ brcmf_p2p_notify_action_frame_rx);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
+ brcmf_p2p_notify_action_tx_complete);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
+ brcmf_p2p_notify_action_tx_complete);
+}
+
+static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
+{
+ kfree(cfg->conf);
+ cfg->conf = NULL;
+ kfree(cfg->escan_ioctl_buf);
+ cfg->escan_ioctl_buf = NULL;
+ kfree(cfg->extra_buf);
+ cfg->extra_buf = NULL;
+ kfree(cfg->wowl.nd);
+ cfg->wowl.nd = NULL;
+ kfree(cfg->wowl.nd_info);
+ cfg->wowl.nd_info = NULL;
+}
+
+static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
+{
+ cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
+ if (!cfg->conf)
+ goto init_priv_mem_out;
+ cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+ if (!cfg->escan_ioctl_buf)
+ goto init_priv_mem_out;
+ cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+ if (!cfg->extra_buf)
+ goto init_priv_mem_out;
+ cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
+ if (!cfg->wowl.nd)
+ goto init_priv_mem_out;
+ cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
+ sizeof(struct cfg80211_wowlan_nd_match *),
+ GFP_KERNEL);
+ if (!cfg->wowl.nd_info)
+ goto init_priv_mem_out;
+
+ return 0;
+
+init_priv_mem_out:
+ brcmf_deinit_priv_mem(cfg);
+
+ return -ENOMEM;
+}
+
+static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
+{
+ s32 err = 0;
+
+ cfg->scan_request = NULL;
+ cfg->pwr_save = true;
+ cfg->active_scan = true; /* we do active scan per default */
+ cfg->dongle_up = false; /* dongle is not up yet */
+ err = brcmf_init_priv_mem(cfg);
+ if (err)
+ return err;
+ brcmf_register_event_handlers(cfg);
+ mutex_init(&cfg->usr_sync);
+ brcmf_init_escan(cfg);
+ brcmf_init_conf(cfg->conf);
+ init_completion(&cfg->vif_disabled);
+ return err;
+}
+
+static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
+{
+ cfg->dongle_up = false; /* dongle down */
+ brcmf_abort_scanning(cfg);
+ brcmf_deinit_priv_mem(cfg);
+}
+
+static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
+{
+ init_waitqueue_head(&event->vif_wq);
+ mutex_init(&event->vif_event_lock);
+}
+
+static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
+{
+ s32 err;
+ u32 bcn_timeout;
+ __le32 roamtrigger[2];
+ __le32 roam_delta[2];
+
+ /* Configure beacon timeout value based upon roaming setting */
+ if (ifp->drvr->settings->roamoff)
+ bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
+ else
+ bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
+ err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
+ if (err) {
+ brcmf_err("bcn_timeout error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+ /* Enable/Disable built-in roaming to allow supplicant to take care of
+ * roaming.
+ */
+ brcmf_dbg(INFO, "Internal Roaming = %s\n",
+ ifp->drvr->settings->roamoff ? "Off" : "On");
+ err = brcmf_fil_iovar_int_set(ifp, "roam_off",
+ ifp->drvr->settings->roamoff);
+ if (err) {
+ brcmf_err("roam_off error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+ roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
+ roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
+ (void *)roamtrigger, sizeof(roamtrigger));
+ if (err) {
+ brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+ roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
+ roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
+ (void *)roam_delta, sizeof(roam_delta));
+ if (err) {
+ brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+roam_setup_done:
+ return err;
+}
+
+static s32
+brcmf_dongle_scantime(struct brcmf_if *ifp)
+{
+ s32 err = 0;
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+ BRCMF_SCAN_CHANNEL_TIME);
+ if (err) {
+ brcmf_err("Scan assoc time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+ BRCMF_SCAN_UNASSOC_TIME);
+ if (err) {
+ brcmf_err("Scan unassoc time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
+ BRCMF_SCAN_PASSIVE_TIME);
+ if (err) {
+ brcmf_err("Scan passive time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+
+dongle_scantime_out:
+ return err;
+}
+
+static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
+ struct brcmu_chan *ch)
+{
+ u32 ht40_flag;
+
+ ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
+ if (ch->sb == BRCMU_CHAN_SB_U) {
+ if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+ channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ } else {
+ /* It should be one of
+ * IEEE80211_CHAN_NO_HT40 or
+ * IEEE80211_CHAN_NO_HT40PLUS
+ */
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+ if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+ channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ }
+}
+
+static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
+ u32 bw_cap[])
+{
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct ieee80211_supported_band *band;
+ struct ieee80211_channel *channel;
+ struct wiphy *wiphy;
+ struct brcmf_chanspec_list *list;
+ struct brcmu_chan ch;
+ int err;
+ u8 *pbuf;
+ u32 i, j;
+ u32 total;
+ u32 chaninfo;
+ u32 index;
+
+ pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+ if (pbuf == NULL)
+ return -ENOMEM;
+
+ list = (struct brcmf_chanspec_list *)pbuf;
+
+ err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+ BRCMF_DCMD_MEDLEN);
+ if (err) {
+ brcmf_err("get chanspecs error (%d)\n", err);
+ goto fail_pbuf;
+ }
+
+ wiphy = cfg_to_wiphy(cfg);
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (band)
+ for (i = 0; i < band->n_channels; i++)
+ band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (band)
+ for (i = 0; i < band->n_channels; i++)
+ band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+
+ total = le32_to_cpu(list->count);
+ for (i = 0; i < total; i++) {
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
+
+ if (ch.band == BRCMU_CHAN_BAND_2G) {
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ } else if (ch.band == BRCMU_CHAN_BAND_5G) {
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+ } else {
+ brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
+ continue;
+ }
+ if (!band)
+ continue;
+ if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
+ ch.bw == BRCMU_CHAN_BW_40)
+ continue;
+ if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
+ ch.bw == BRCMU_CHAN_BW_80)
+ continue;
+
+ channel = band->channels;
+ index = band->n_channels;
+ for (j = 0; j < band->n_channels; j++) {
+ if (channel[j].hw_value == ch.chnum) {
+ index = j;
+ break;
+ }
+ }
+ channel[index].center_freq =
+ ieee80211_channel_to_frequency(ch.chnum, band->band);
+ channel[index].hw_value = ch.chnum;
+
+ /* assuming the chanspecs order is HT20,
+ * HT40 upper, HT40 lower, and VHT80.
+ */
+ if (ch.bw == BRCMU_CHAN_BW_80) {
+ channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
+ } else if (ch.bw == BRCMU_CHAN_BW_40) {
+ brcmf_update_bw40_channel_flag(&channel[index], &ch);
+ } else {
+ /* enable the channel and disable other bandwidths
+ * for now as mentioned order assure they are enabled
+ * for subsequent chanspecs.
+ */
+ channel[index].flags = IEEE80211_CHAN_NO_HT40 |
+ IEEE80211_CHAN_NO_80MHZ;
+ ch.bw = BRCMU_CHAN_BW_20;
+ cfg->d11inf.encchspec(&ch);
+ chaninfo = ch.chspec;
+ err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
+ &chaninfo);
+ if (!err) {
+ if (chaninfo & WL_CHAN_RADAR)
+ channel[index].flags |=
+ (IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_NO_IR);
+ if (chaninfo & WL_CHAN_PASSIVE)
+ channel[index].flags |=
+ IEEE80211_CHAN_NO_IR;
+ }
+ }
+ }
+
+fail_pbuf:
+ kfree(pbuf);
+ return err;
+}
+
+static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct ieee80211_supported_band *band;
+ struct brcmf_fil_bwcap_le band_bwcap;
+ struct brcmf_chanspec_list *list;
+ u8 *pbuf;
+ u32 val;
+ int err;
+ struct brcmu_chan ch;
+ u32 num_chan;
+ int i, j;
+
+ /* verify support for bw_cap command */
+ val = WLC_BAND_5G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
+
+ if (!err) {
+ /* only set 2G bandwidth using bw_cap command */
+ band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
+ band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
+ err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
+ sizeof(band_bwcap));
+ } else {
+ brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
+ val = WLC_N_BW_40ALL;
+ err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
+ }
+
+ if (!err) {
+ /* update channel info in 2G band */
+ pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+ if (pbuf == NULL)
+ return -ENOMEM;
+
+ ch.band = BRCMU_CHAN_BAND_2G;
+ ch.bw = BRCMU_CHAN_BW_40;
+ ch.sb = BRCMU_CHAN_SB_NONE;
+ ch.chnum = 0;
+ cfg->d11inf.encchspec(&ch);
+
+ /* pass encoded chanspec in query */
+ *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
+
+ err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+ BRCMF_DCMD_MEDLEN);
+ if (err) {
+ brcmf_err("get chanspecs error (%d)\n", err);
+ kfree(pbuf);
+ return err;
+ }
+
+ band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
+ list = (struct brcmf_chanspec_list *)pbuf;
+ num_chan = le32_to_cpu(list->count);
+ for (i = 0; i < num_chan; i++) {
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
+ if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
+ continue;
+ if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
+ continue;
+ for (j = 0; j < band->n_channels; j++) {
+ if (band->channels[j].hw_value == ch.chnum)
+ break;
+ }
+ if (WARN_ON(j == band->n_channels))
+ continue;
+
+ brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
+ }
+ kfree(pbuf);
+ }
+ return err;
+}
+
+static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
+{
+ u32 band, mimo_bwcap;
+ int err;
+
+ band = WLC_BAND_2G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
+ if (!err) {
+ bw_cap[IEEE80211_BAND_2GHZ] = band;
+ band = WLC_BAND_5G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
+ if (!err) {
+ bw_cap[IEEE80211_BAND_5GHZ] = band;
+ return;
+ }
+ WARN_ON(1);
+ return;
+ }
+ brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
+ mimo_bwcap = 0;
+ err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
+ if (err)
+ /* assume 20MHz if firmware does not give a clue */
+ mimo_bwcap = WLC_N_BW_20ALL;
+
+ switch (mimo_bwcap) {
+ case WLC_N_BW_40ALL:
+ bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
+ /* fall-thru */
+ case WLC_N_BW_20IN2G_40IN5G:
+ bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
+ /* fall-thru */
+ case WLC_N_BW_20ALL:
+ bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
+ bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
+ break;
+ default:
+ brcmf_err("invalid mimo_bw_cap value\n");
+ }
+}
+
+static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
+ u32 bw_cap[2], u32 nchain)
+{
+ band->ht_cap.ht_supported = true;
+ if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+ band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+ memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
+ band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
+{
+ u16 mcs_map;
+ int i;
+
+ for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
+ mcs_map = (mcs_map << 2) | supp;
+
+ return cpu_to_le16(mcs_map);
+}
+
+static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
+ u32 bw_cap[2], u32 nchain, u32 txstreams,
+ u32 txbf_bfe_cap, u32 txbf_bfr_cap)
+{
+ __le16 mcs_map;
+
+ /* not allowed in 2.4G band */
+ if (band->band == IEEE80211_BAND_2GHZ)
+ return;
+
+ band->vht_cap.vht_supported = true;
+ /* 80MHz is mandatory */
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
+ }
+ /* all support 256-QAM */
+ mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
+ band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
+ band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
+
+ /* Beamforming support information */
+ if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
+ band->vht_cap.cap |=
+ (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+ band->vht_cap.cap |= ((txstreams - 1) <<
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
+ band->vht_cap.cap |=
+ IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
+ }
+}
+
+static int brcmf_setup_wiphybands(struct wiphy *wiphy)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ u32 nmode = 0;
+ u32 vhtmode = 0;
+ u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
+ u32 rxchain;
+ u32 nchain;
+ int err;
+ s32 i;
+ struct ieee80211_supported_band *band;
+ u32 txstreams = 0;
+ u32 txbf_bfe_cap = 0;
+ u32 txbf_bfr_cap = 0;
+
+ (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
+ err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
+ if (err) {
+ brcmf_err("nmode error (%d)\n", err);
+ } else {
+ brcmf_get_bwcap(ifp, bw_cap);
+ }
+ brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
+ nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
+ bw_cap[IEEE80211_BAND_5GHZ]);
+
+ err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
+ if (err) {
+ brcmf_err("rxchain error (%d)\n", err);
+ nchain = 1;
+ } else {
+ for (nchain = 0; rxchain; nchain++)
+ rxchain = rxchain & (rxchain - 1);
+ }
+ brcmf_dbg(INFO, "nchain=%d\n", nchain);
+
+ err = brcmf_construct_chaninfo(cfg, bw_cap);
+ if (err) {
+ brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
+ return err;
+ }
+
+ if (vhtmode) {
+ (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
+ (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
+ &txbf_bfe_cap);
+ (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
+ &txbf_bfr_cap);
+ }
+
+ wiphy = cfg_to_wiphy(cfg);
+ for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
+ band = wiphy->bands[i];
+ if (band == NULL)
+ continue;
+
+ if (nmode)
+ brcmf_update_ht_cap(band, bw_cap, nchain);
+ if (vhtmode)
+ brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
+ txbf_bfe_cap, txbf_bfr_cap);
+ }
+
+ return 0;
+}
+
+static const struct ieee80211_txrx_stypes
+brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_P2P_DEVICE] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ }
+};
+
+/**
+ * brcmf_setup_ifmodes() - determine interface modes and combinations.
+ *
+ * @wiphy: wiphy object.
+ * @ifp: interface object needed for feat module api.
+ *
+ * The interface modes and combinations are determined dynamically here
+ * based on firmware functionality.
+ *
+ * no p2p and no mbss:
+ *
+ * #STA <= 1, #AP <= 1, channels = 1, 2 total
+ *
+ * no p2p and mbss:
+ *
+ * #STA <= 1, #AP <= 1, channels = 1, 2 total
+ * #AP <= 4, matching BI, channels = 1, 4 total
+ *
+ * p2p, no mchan, and mbss:
+ *
+ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
+ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
+ * #AP <= 4, matching BI, channels = 1, 4 total
+ *
+ * p2p, mchan, and mbss:
+ *
+ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
+ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
+ * #AP <= 4, matching BI, channels = 1, 4 total
+ */
+static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct ieee80211_iface_combination *combo = NULL;
+ struct ieee80211_iface_limit *c0_limits = NULL;
+ struct ieee80211_iface_limit *p2p_limits = NULL;
+ struct ieee80211_iface_limit *mbss_limits = NULL;
+ bool mbss, p2p;
+ int i, c, n_combos;
+
+ mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
+ p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
+
+ n_combos = 1 + !!p2p + !!mbss;
+ combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
+ if (!combo)
+ goto err;
+
+ c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
+ if (!c0_limits)
+ goto err;
+
+ if (p2p) {
+ p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
+ if (!p2p_limits)
+ goto err;
+ }
+
+ if (mbss) {
+ mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
+ if (!mbss_limits)
+ goto err;
+ }
+
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
+
+ c = 0;
+ i = 0;
+ combo[c].num_different_channels = 1;
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
+ if (p2p) {
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
+ combo[c].num_different_channels = 2;
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE);
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+ } else {
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
+ }
+ combo[c].max_interfaces = i;
+ combo[c].n_limits = i;
+ combo[c].limits = c0_limits;
+
+ if (p2p) {
+ c++;
+ i = 0;
+ combo[c].num_different_channels = 1;
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
+ combo[c].max_interfaces = i;
+ combo[c].n_limits = i;
+ combo[c].limits = p2p_limits;
+ }
+
+ if (mbss) {
+ c++;
+ combo[c].beacon_int_infra_match = true;
+ combo[c].num_different_channels = 1;
+ mbss_limits[0].max = 4;
+ mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
+ combo[c].max_interfaces = 4;
+ combo[c].n_limits = 1;
+ combo[c].limits = mbss_limits;
+ }
+ wiphy->n_iface_combinations = n_combos;
+ wiphy->iface_combinations = combo;
+ return 0;
+
+err:
+ kfree(c0_limits);
+ kfree(p2p_limits);
+ kfree(mbss_limits);
+ kfree(combo);
+ return -ENOMEM;
+}
+
+static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
+{
+ /* scheduled scan settings */
+ wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
+ wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
+ wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+}
+
+#ifdef CONFIG_PM
+static struct wiphy_wowlan_support brcmf_wowlan_support = {
+ .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+ .n_patterns = BRCMF_WOWL_MAXPATTERNS,
+ .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
+ .pattern_min_len = 1,
+ .max_pkt_offset = 1500,
+};
+#endif
+
+static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+#ifdef CONFIG_PM
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ s32 err;
+ u32 wowl_cap;
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
+ err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
+ if (!err) {
+ if (wowl_cap & BRCMF_WOWL_PFN_FOUND) {
+ brcmf_wowlan_support.flags |=
+ WIPHY_WOWLAN_NET_DETECT;
+ init_waitqueue_head(&cfg->wowl.nd_data_wait);
+ }
+ }
+ }
+ wiphy->wowlan = &brcmf_wowlan_support;
+#endif
+}
+
+static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ const struct ieee80211_iface_combination *combo;
+ struct ieee80211_supported_band *band;
+ u16 max_interfaces = 0;
+ __le32 bandlist[3];
+ u32 n_bands;
+ int err, i;
+
+ wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+ wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+ wiphy->max_num_pmkids = BRCMF_MAXPMKID;
+
+ err = brcmf_setup_ifmodes(wiphy, ifp);
+ if (err)
+ return err;
+
+ for (i = 0, combo = wiphy->iface_combinations;
+ i < wiphy->n_iface_combinations; i++, combo++) {
+ max_interfaces = max(max_interfaces, combo->max_interfaces);
+ }
+
+ for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
+ i++) {
+ u8 *addr = drvr->addresses[i].addr;
+
+ memcpy(addr, drvr->mac, ETH_ALEN);
+ if (i) {
+ addr[0] |= BIT(1);
+ addr[ETH_ALEN - 1] ^= i;
+ }
+ }
+ wiphy->addresses = drvr->addresses;
+ wiphy->n_addresses = i;
+
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->cipher_suites = __wl_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+ wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
+ WIPHY_FLAG_OFFCHAN_TX |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+ if (!ifp->drvr->settings->roamoff)
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+ wiphy->mgmt_stypes = brcmf_txrx_stypes;
+ wiphy->max_remain_on_channel_duration = 5000;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
+ brcmf_wiphy_pno_params(wiphy);
+
+ /* vendor commands/events support */
+ wiphy->vendor_commands = brcmf_vendor_cmds;
+ wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
+ brcmf_wiphy_wowl_params(wiphy, ifp);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
+ sizeof(bandlist));
+ if (err) {
+ brcmf_err("could not obtain band info: err=%d\n", err);
+ return err;
+ }
+ /* first entry in bandlist is number of bands */
+ n_bands = le32_to_cpu(bandlist[0]);
+ for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
+ if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
+ band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
+ GFP_KERNEL);
+ if (!band)
+ return -ENOMEM;
+
+ band->channels = kmemdup(&__wl_2ghz_channels,
+ sizeof(__wl_2ghz_channels),
+ GFP_KERNEL);
+ if (!band->channels) {
+ kfree(band);
+ return -ENOMEM;
+ }
+
+ band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
+ wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+ }
+ if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
+ band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
+ GFP_KERNEL);
+ if (!band)
+ return -ENOMEM;
+
+ band->channels = kmemdup(&__wl_5ghz_channels,
+ sizeof(__wl_5ghz_channels),
+ GFP_KERNEL);
+ if (!band->channels) {
+ kfree(band);
+ return -ENOMEM;
+ }
+
+ band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
+ wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+ }
+ }
+ err = brcmf_setup_wiphybands(wiphy);
+ return err;
+}
+
+static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
+{
+ struct net_device *ndev;
+ struct wireless_dev *wdev;
+ struct brcmf_if *ifp;
+ s32 power_mode;
+ s32 err = 0;
+
+ if (cfg->dongle_up)
+ return err;
+
+ ndev = cfg_to_ndev(cfg);
+ wdev = ndev->ieee80211_ptr;
+ ifp = netdev_priv(ndev);
+
+ /* make sure RF is ready for work */
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
+
+ brcmf_dongle_scantime(ifp);
+
+ power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
+ if (err)
+ goto default_conf_out;
+ brcmf_dbg(INFO, "power save set to %s\n",
+ (power_mode ? "enabled" : "disabled"));
+
+ err = brcmf_dongle_roam(ifp);
+ if (err)
+ goto default_conf_out;
+ err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
+ NULL, NULL);
+ if (err)
+ goto default_conf_out;
+
+ brcmf_configure_arp_offload(ifp, true);
+
+ cfg->dongle_up = true;
+default_conf_out:
+
+ return err;
+
+}
+
+static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
+{
+ set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
+
+ return brcmf_config_dongle(ifp->drvr->config);
+}
+
+static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+
+ /*
+ * While going down, if associated with AP disassociate
+ * from AP to save power
+ */
+ if (check_vif_up(ifp->vif)) {
+ brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
+
+ /* Make sure WPA_Supplicant receives all the event
+ generated due to DISASSOC call to the fw to keep
+ the state fw and WPA_Supplicant state consistent
+ */
+ brcmf_delay(500);
+ }
+
+ brcmf_abort_scanning(cfg);
+ clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
+
+ return 0;
+}
+
+s32 brcmf_cfg80211_up(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ s32 err = 0;
+
+ mutex_lock(&cfg->usr_sync);
+ err = __brcmf_cfg80211_up(ifp);
+ mutex_unlock(&cfg->usr_sync);
+
+ return err;
+}
+
+s32 brcmf_cfg80211_down(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ s32 err = 0;
+
+ mutex_lock(&cfg->usr_sync);
+ err = __brcmf_cfg80211_down(ifp);
+ mutex_unlock(&cfg->usr_sync);
+
+ return err;
+}
+
+enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
+{
+ struct wireless_dev *wdev = &ifp->vif->wdev;
+
+ return wdev->iftype;
+}
+
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
+ unsigned long state)
+{
+ struct brcmf_cfg80211_vif *vif;
+
+ list_for_each_entry(vif, &cfg->vif_list, list) {
+ if (test_bit(state, &vif->sme_state))
+ return true;
+ }
+ return false;
+}
+
+static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
+ u8 action)
+{
+ u8 evt_action;
+
+ mutex_lock(&event->vif_event_lock);
+ evt_action = event->action;
+ mutex_unlock(&event->vif_event_lock);
+ return evt_action == action;
+}
+
+void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+
+ mutex_lock(&event->vif_event_lock);
+ event->vif = vif;
+ event->action = 0;
+ mutex_unlock(&event->vif_event_lock);
+}
+
+bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+ bool armed;
+
+ mutex_lock(&event->vif_event_lock);
+ armed = event->vif != NULL;
+ mutex_unlock(&event->vif_event_lock);
+
+ return armed;
+}
+int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
+ u8 action, ulong timeout)
+{
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+
+ return wait_event_timeout(event->vif_wq,
+ vif_event_equals(event, action), timeout);
+}
+
+static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *req)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_fil_country_le ccreq;
+ int i;
+
+ brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
+ req->alpha2[0], req->alpha2[1]);
+
+ /* ignore non-ISO3166 country codes */
+ for (i = 0; i < sizeof(req->alpha2); i++)
+ if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
+ brcmf_err("not a ISO3166 code\n");
+ return;
+ }
+ memset(&ccreq, 0, sizeof(ccreq));
+ ccreq.rev = cpu_to_le32(-1);
+ memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
+ if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
+ brcmf_err("firmware rejected country setting\n");
+ return;
+ }
+ brcmf_setup_wiphybands(wiphy);
+}
+
+static void brcmf_free_wiphy(struct wiphy *wiphy)
+{
+ int i;
+
+ if (!wiphy)
+ return;
+
+ if (wiphy->iface_combinations) {
+ for (i = 0; i < wiphy->n_iface_combinations; i++)
+ kfree(wiphy->iface_combinations[i].limits);
+ }
+ kfree(wiphy->iface_combinations);
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+ }
+ if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
+ kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
+ kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
+ }
+ wiphy_free(wiphy);
+}
+
+struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+ struct device *busdev,
+ bool p2pdev_forced)
+{
+ struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
+ struct brcmf_cfg80211_info *cfg;
+ struct wiphy *wiphy;
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+ s32 err = 0;
+ s32 io_type;
+ u16 *cap = NULL;
+
+ if (!ndev) {
+ brcmf_err("ndev is invalid\n");
+ return NULL;
+ }
+
+ ifp = netdev_priv(ndev);
+ wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
+ if (!wiphy) {
+ brcmf_err("Could not allocate wiphy device\n");
+ return NULL;
+ }
+ memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
+ set_wiphy_dev(wiphy, busdev);
+
+ cfg = wiphy_priv(wiphy);
+ cfg->wiphy = wiphy;
+ cfg->pub = drvr;
+ init_vif_event(&cfg->vif_event);
+ INIT_LIST_HEAD(&cfg->vif_list);
+
+ vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
+ if (IS_ERR(vif))
+ goto wiphy_out;
+
+ vif->ifp = ifp;
+ vif->wdev.netdev = ndev;
+ ndev->ieee80211_ptr = &vif->wdev;
+ SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
+
+ err = wl_init_priv(cfg);
+ if (err) {
+ brcmf_err("Failed to init iwm_priv (%d)\n", err);
+ brcmf_free_vif(vif);
+ goto wiphy_out;
+ }
+ ifp->vif = vif;
+
+ /* determine d11 io type before wiphy setup */
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
+ if (err) {
+ brcmf_err("Failed to get D11 version (%d)\n", err);
+ goto priv_out;
+ }
+ cfg->d11inf.io_type = (u8)io_type;
+ brcmu_d11_attach(&cfg->d11inf);
+
+ err = brcmf_setup_wiphy(wiphy, ifp);
+ if (err < 0)
+ goto priv_out;
+
+ brcmf_dbg(INFO, "Registering custom regulatory\n");
+ wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+ wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
+
+ /* firmware defaults to 40MHz disabled in 2G band. We signal
+ * cfg80211 here that we do and have it decide we can enable
+ * it. But first check if device does support 2G operation.
+ */
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
+ *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ err = wiphy_register(wiphy);
+ if (err < 0) {
+ brcmf_err("Could not register wiphy device (%d)\n", err);
+ goto priv_out;
+ }
+
+ /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
+ * setup 40MHz in 2GHz band and enable OBSS scanning.
+ */
+ if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
+ err = brcmf_enable_bw40_2g(cfg);
+ if (!err)
+ err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
+ BRCMF_OBSS_COEX_AUTO);
+ else
+ *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ /* p2p might require that "if-events" get processed by fweh. So
+ * activate the already registered event handlers now and activate
+ * the rest when initialization has completed. drvr->config needs to
+ * be assigned before activating events.
+ */
+ drvr->config = cfg;
+ err = brcmf_fweh_activate_events(ifp);
+ if (err) {
+ brcmf_err("FWEH activation failed (%d)\n", err);
+ goto wiphy_unreg_out;
+ }
+
+ err = brcmf_p2p_attach(cfg, p2pdev_forced);
+ if (err) {
+ brcmf_err("P2P initilisation failed (%d)\n", err);
+ goto wiphy_unreg_out;
+ }
+ err = brcmf_btcoex_attach(cfg);
+ if (err) {
+ brcmf_err("BT-coex initialisation failed (%d)\n", err);
+ brcmf_p2p_detach(&cfg->p2p);
+ goto wiphy_unreg_out;
+ }
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
+ err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+ if (err) {
+ brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+ wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+ } else {
+ brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
+ brcmf_notify_tdls_peer_event);
+ }
+ }
+
+ /* (re-) activate FWEH event handling */
+ err = brcmf_fweh_activate_events(ifp);
+ if (err) {
+ brcmf_err("FWEH activation failed (%d)\n", err);
+ goto wiphy_unreg_out;
+ }
+
+ /* Fill in some of the advertised nl80211 supported features */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
+ wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+#ifdef CONFIG_PM
+ if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
+ wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+#endif
+ }
+
+ return cfg;
+
+wiphy_unreg_out:
+ wiphy_unregister(cfg->wiphy);
+priv_out:
+ wl_deinit_priv(cfg);
+ brcmf_free_vif(vif);
+ ifp->vif = NULL;
+wiphy_out:
+ brcmf_free_wiphy(wiphy);
+ return NULL;
+}
+
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
+{
+ if (!cfg)
+ return;
+
+ brcmf_btcoex_detach(cfg);
+ wiphy_unregister(cfg->wiphy);
+ wl_deinit_priv(cfg);
+ brcmf_free_wiphy(cfg->wiphy);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
new file mode 100644
index 000000000000..40efb539ac26
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_CFG80211_H
+#define BRCMFMAC_CFG80211_H
+
+/* for brcmu_d11inf */
+#include <brcmu_d11.h>
+
+#define WL_NUM_SCAN_MAX 10
+#define WL_TLV_INFO_MAX 1024
+#define WL_BSS_INFO_MAX 2048
+#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */
+#define WL_EXTRA_BUF_MAX 2048
+#define WL_ROAM_TRIGGER_LEVEL -75
+#define WL_ROAM_DELTA 20
+
+#define WL_ESCAN_BUF_SIZE (1024 * 64)
+#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */
+
+#define WL_ESCAN_ACTION_START 1
+#define WL_ESCAN_ACTION_CONTINUE 2
+#define WL_ESCAN_ACTION_ABORT 3
+
+#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */
+#define IE_MAX_LEN 512
+
+/* IE TLV processing */
+#define TLV_LEN_OFF 1 /* length offset */
+#define TLV_HDR_LEN 2 /* header length */
+#define TLV_BODY_OFF 2 /* body offset */
+#define TLV_OUI_LEN 3 /* oui id length */
+
+/* 802.11 Mgmt Packet flags */
+#define BRCMF_VNDR_IE_BEACON_FLAG 0x1
+#define BRCMF_VNDR_IE_PRBRSP_FLAG 0x2
+#define BRCMF_VNDR_IE_ASSOCRSP_FLAG 0x4
+#define BRCMF_VNDR_IE_AUTHRSP_FLAG 0x8
+#define BRCMF_VNDR_IE_PRBREQ_FLAG 0x10
+#define BRCMF_VNDR_IE_ASSOCREQ_FLAG 0x20
+/* vendor IE in IW advertisement protocol ID field */
+#define BRCMF_VNDR_IE_IWAPID_FLAG 0x40
+/* allow custom IE id */
+#define BRCMF_VNDR_IE_CUSTOM_FLAG 0x100
+
+/* P2P Action Frames flags (spec ordered) */
+#define BRCMF_VNDR_IE_GONREQ_FLAG 0x001000
+#define BRCMF_VNDR_IE_GONRSP_FLAG 0x002000
+#define BRCMF_VNDR_IE_GONCFM_FLAG 0x004000
+#define BRCMF_VNDR_IE_INVREQ_FLAG 0x008000
+#define BRCMF_VNDR_IE_INVRSP_FLAG 0x010000
+#define BRCMF_VNDR_IE_DISREQ_FLAG 0x020000
+#define BRCMF_VNDR_IE_DISRSP_FLAG 0x040000
+#define BRCMF_VNDR_IE_PRDREQ_FLAG 0x080000
+#define BRCMF_VNDR_IE_PRDRSP_FLAG 0x100000
+
+#define BRCMF_VNDR_IE_P2PAF_SHIFT 12
+
+#define BRCMF_MAX_DEFAULT_KEYS 4
+
+/* beacon loss timeout defaults */
+#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2
+#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF 4
+
+#define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500)
+
+/**
+ * enum brcmf_scan_status - scan engine status
+ *
+ * @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle.
+ * @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle.
+ * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver.
+ */
+enum brcmf_scan_status {
+ BRCMF_SCAN_STATUS_BUSY,
+ BRCMF_SCAN_STATUS_ABORT,
+ BRCMF_SCAN_STATUS_SUPPRESS,
+};
+
+/* dongle configuration */
+struct brcmf_cfg80211_conf {
+ u32 frag_threshold;
+ u32 rts_threshold;
+ u32 retry_short;
+ u32 retry_long;
+};
+
+/* security information with currently associated ap */
+struct brcmf_cfg80211_security {
+ u32 wpa_versions;
+ u32 auth_type;
+ u32 cipher_pairwise;
+ u32 cipher_group;
+ u32 wpa_auth;
+};
+
+/**
+ * struct brcmf_cfg80211_profile - profile information.
+ *
+ * @bssid: bssid of joined/joining ibss.
+ * @sec: security information.
+ * @key: key information
+ */
+struct brcmf_cfg80211_profile {
+ u8 bssid[ETH_ALEN];
+ struct brcmf_cfg80211_security sec;
+ struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS];
+};
+
+/**
+ * enum brcmf_vif_status - bit indices for vif status.
+ *
+ * @BRCMF_VIF_STATUS_READY: ready for operation.
+ * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress.
+ * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully.
+ * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress.
+ * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started.
+ */
+enum brcmf_vif_status {
+ BRCMF_VIF_STATUS_READY,
+ BRCMF_VIF_STATUS_CONNECTING,
+ BRCMF_VIF_STATUS_CONNECTED,
+ BRCMF_VIF_STATUS_DISCONNECTING,
+ BRCMF_VIF_STATUS_AP_CREATED
+};
+
+/**
+ * struct vif_saved_ie - holds saved IEs for a virtual interface.
+ *
+ * @probe_req_ie: IE info for probe request.
+ * @probe_res_ie: IE info for probe response.
+ * @beacon_ie: IE info for beacon frame.
+ * @probe_req_ie_len: IE info length for probe request.
+ * @probe_res_ie_len: IE info length for probe response.
+ * @beacon_ie_len: IE info length for beacon frame.
+ */
+struct vif_saved_ie {
+ u8 probe_req_ie[IE_MAX_LEN];
+ u8 probe_res_ie[IE_MAX_LEN];
+ u8 beacon_ie[IE_MAX_LEN];
+ u8 assoc_req_ie[IE_MAX_LEN];
+ u32 probe_req_ie_len;
+ u32 probe_res_ie_len;
+ u32 beacon_ie_len;
+ u32 assoc_req_ie_len;
+};
+
+/**
+ * struct brcmf_cfg80211_vif - virtual interface specific information.
+ *
+ * @ifp: lower layer interface pointer
+ * @wdev: wireless device.
+ * @profile: profile information.
+ * @sme_state: SME state using enum brcmf_vif_status bits.
+ * @pm_block: power-management blocked.
+ * @list: linked list.
+ * @mgmt_rx_reg: registered rx mgmt frame types.
+ * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
+ */
+struct brcmf_cfg80211_vif {
+ struct brcmf_if *ifp;
+ struct wireless_dev wdev;
+ struct brcmf_cfg80211_profile profile;
+ unsigned long sme_state;
+ bool pm_block;
+ struct vif_saved_ie saved_ie;
+ struct list_head list;
+ u16 mgmt_rx_reg;
+ bool mbss;
+ int is_11d;
+};
+
+/* association inform */
+struct brcmf_cfg80211_connect_info {
+ u8 *req_ie;
+ s32 req_ie_len;
+ u8 *resp_ie;
+ s32 resp_ie_len;
+};
+
+/* assoc ie length */
+struct brcmf_cfg80211_assoc_ielen_le {
+ __le32 req_len;
+ __le32 resp_len;
+};
+
+/* dongle escan state */
+enum wl_escan_state {
+ WL_ESCAN_STATE_IDLE,
+ WL_ESCAN_STATE_SCANNING
+};
+
+struct escan_info {
+ u32 escan_state;
+ u8 escan_buf[WL_ESCAN_BUF_SIZE];
+ struct wiphy *wiphy;
+ struct brcmf_if *ifp;
+ s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
+ struct cfg80211_scan_request *request);
+};
+
+/**
+ * struct brcmf_cfg80211_vif_event - virtual interface event information.
+ *
+ * @vif_wq: waitqueue awaiting interface event from firmware.
+ * @vif_event_lock: protects other members in this structure.
+ * @vif_complete: completion for net attach.
+ * @action: either add, change, or delete.
+ * @vif: virtual interface object related to the event.
+ */
+struct brcmf_cfg80211_vif_event {
+ wait_queue_head_t vif_wq;
+ struct mutex vif_event_lock;
+ u8 action;
+ struct brcmf_cfg80211_vif *vif;
+};
+
+/**
+ * struct brcmf_cfg80211_wowl - wowl related information.
+ *
+ * @active: set on suspend, cleared on resume.
+ * @pre_pmmode: firmware PM mode at entering suspend.
+ * @nd: net dectect data.
+ * @nd_info: helper struct to pass to cfg80211.
+ * @nd_data_wait: wait queue to sync net detect data.
+ * @nd_data_completed: completion for net detect data.
+ * @nd_enabled: net detect enabled.
+ */
+struct brcmf_cfg80211_wowl {
+ bool active;
+ u32 pre_pmmode;
+ struct cfg80211_wowlan_nd_match *nd;
+ struct cfg80211_wowlan_nd_info *nd_info;
+ wait_queue_head_t nd_data_wait;
+ bool nd_data_completed;
+ bool nd_enabled;
+};
+
+/**
+ * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
+ *
+ * @wiphy: wiphy object for cfg80211 interface.
+ * @conf: dongle configuration.
+ * @p2p: peer-to-peer specific information.
+ * @btcoex: Bluetooth coexistence information.
+ * @scan_request: cfg80211 scan request object.
+ * @usr_sync: mainly for dongle up/down synchronization.
+ * @bss_list: bss_list holding scanned ap information.
+ * @bss_info: bss information for cfg80211 layer.
+ * @conn_info: association info.
+ * @pmk_list: wpa2 pmk list.
+ * @scan_status: scan activity on the dongle.
+ * @pub: common driver information.
+ * @channel: current channel.
+ * @active_scan: current scan mode.
+ * @sched_escan: e-scan for scheduled scan support running.
+ * @ibss_starter: indicates this sta is ibss starter.
+ * @pwr_save: indicate whether dongle to support power save mode.
+ * @dongle_up: indicate whether dongle up or not.
+ * @roam_on: on/off switch for dongle self-roaming.
+ * @scan_tried: indicates if first scan attempted.
+ * @dcmd_buf: dcmd buffer.
+ * @extra_buf: mainly to grab assoc information.
+ * @debugfsdir: debugfs folder for this device.
+ * @escan_info: escan information.
+ * @escan_timeout: Timer for catch scan timeout.
+ * @escan_timeout_work: scan timeout worker.
+ * @escan_ioctl_buf: dongle command buffer for escan commands.
+ * @vif_list: linked list of vif instances.
+ * @vif_cnt: number of vif instances.
+ * @vif_event: vif event signalling.
+ * @wowl: wowl related information.
+ */
+struct brcmf_cfg80211_info {
+ struct wiphy *wiphy;
+ struct brcmf_cfg80211_conf *conf;
+ struct brcmf_p2p_info p2p;
+ struct brcmf_btcoex_info *btcoex;
+ struct cfg80211_scan_request *scan_request;
+ struct mutex usr_sync;
+ struct wl_cfg80211_bss_info *bss_info;
+ struct brcmf_cfg80211_connect_info conn_info;
+ struct brcmf_pmk_list_le pmk_list;
+ unsigned long scan_status;
+ struct brcmf_pub *pub;
+ u32 channel;
+ bool active_scan;
+ bool sched_escan;
+ bool ibss_starter;
+ bool pwr_save;
+ bool dongle_up;
+ bool scan_tried;
+ u8 *dcmd_buf;
+ u8 *extra_buf;
+ struct dentry *debugfsdir;
+ struct escan_info escan_info;
+ struct timer_list escan_timeout;
+ struct work_struct escan_timeout_work;
+ u8 *escan_ioctl_buf;
+ struct list_head vif_list;
+ struct brcmf_cfg80211_vif_event vif_event;
+ struct completion vif_disabled;
+ struct brcmu_d11inf d11inf;
+ struct brcmf_assoclist_le assoclist;
+ struct brcmf_cfg80211_wowl wowl;
+};
+
+/**
+ * struct brcmf_tlv - tag_ID/length/value_buffer tuple.
+ *
+ * @id: tag identifier.
+ * @len: number of bytes in value buffer.
+ * @data: value buffer.
+ */
+struct brcmf_tlv {
+ u8 id;
+ u8 len;
+ u8 data[1];
+};
+
+static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg)
+{
+ return cfg->wiphy;
+}
+
+static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w)
+{
+ return (struct brcmf_cfg80211_info *)(wiphy_priv(w));
+}
+
+static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd)
+{
+ return (struct brcmf_cfg80211_info *)(wdev_priv(wd));
+}
+
+static inline
+struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_cfg80211_vif *vif;
+ vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list);
+ return vif->wdev.netdev;
+}
+
+static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev)
+{
+ return wdev_to_cfg(ndev->ieee80211_ptr);
+}
+
+static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd)
+{
+ struct brcmf_if *ifp = netdev_priv(nd);
+ return &ifp->vif->profile;
+}
+
+static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ return ifp->vif;
+}
+
+static inline struct
+brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg)
+{
+ return &cfg->conn_info;
+}
+
+struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+ struct device *busdev,
+ bool p2pdev_forced);
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg);
+s32 brcmf_cfg80211_up(struct net_device *ndev);
+s32 brcmf_cfg80211_down(struct net_device *ndev);
+enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp);
+
+struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
+ enum nl80211_iftype type,
+ bool pm_block);
+void brcmf_free_vif(struct brcmf_cfg80211_vif *vif);
+
+s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
+ const u8 *vndr_ie_buf, u32 vndr_ie_len);
+s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif);
+const struct brcmf_tlv *
+brcmf_parse_tlvs(const void *buf, int buflen, uint key);
+u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct ieee80211_channel *ch);
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
+ unsigned long state);
+void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_cfg80211_vif *vif);
+bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
+int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
+ u8 action, ulong timeout);
+s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp, bool aborted,
+ bool fw_abort);
+void brcmf_set_mpc(struct brcmf_if *ndev, int mpc);
+void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
+void brcmf_cfg80211_free_netdev(struct net_device *ndev);
+
+#endif /* BRCMFMAC_CFG80211_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
new file mode 100644
index 000000000000..82e4382eb177
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -0,0 +1,1332 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+
+#include <defs.h>
+#include <soc.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <chipcommon.h>
+#include "debug.h"
+#include "chip.h"
+
+/* SOC Interconnect types (aka chip types) */
+#define SOCI_SB 0
+#define SOCI_AI 1
+
+/* PL-368 DMP definitions */
+#define DMP_DESC_TYPE_MSK 0x0000000F
+#define DMP_DESC_EMPTY 0x00000000
+#define DMP_DESC_VALID 0x00000001
+#define DMP_DESC_COMPONENT 0x00000001
+#define DMP_DESC_MASTER_PORT 0x00000003
+#define DMP_DESC_ADDRESS 0x00000005
+#define DMP_DESC_ADDRSIZE_GT32 0x00000008
+#define DMP_DESC_EOT 0x0000000F
+
+#define DMP_COMP_DESIGNER 0xFFF00000
+#define DMP_COMP_DESIGNER_S 20
+#define DMP_COMP_PARTNUM 0x000FFF00
+#define DMP_COMP_PARTNUM_S 8
+#define DMP_COMP_CLASS 0x000000F0
+#define DMP_COMP_CLASS_S 4
+#define DMP_COMP_REVISION 0xFF000000
+#define DMP_COMP_REVISION_S 24
+#define DMP_COMP_NUM_SWRAP 0x00F80000
+#define DMP_COMP_NUM_SWRAP_S 19
+#define DMP_COMP_NUM_MWRAP 0x0007C000
+#define DMP_COMP_NUM_MWRAP_S 14
+#define DMP_COMP_NUM_SPORT 0x00003E00
+#define DMP_COMP_NUM_SPORT_S 9
+#define DMP_COMP_NUM_MPORT 0x000001F0
+#define DMP_COMP_NUM_MPORT_S 4
+
+#define DMP_MASTER_PORT_UID 0x0000FF00
+#define DMP_MASTER_PORT_UID_S 8
+#define DMP_MASTER_PORT_NUM 0x000000F0
+#define DMP_MASTER_PORT_NUM_S 4
+
+#define DMP_SLAVE_ADDR_BASE 0xFFFFF000
+#define DMP_SLAVE_ADDR_BASE_S 12
+#define DMP_SLAVE_PORT_NUM 0x00000F00
+#define DMP_SLAVE_PORT_NUM_S 8
+#define DMP_SLAVE_TYPE 0x000000C0
+#define DMP_SLAVE_TYPE_S 6
+#define DMP_SLAVE_TYPE_SLAVE 0
+#define DMP_SLAVE_TYPE_BRIDGE 1
+#define DMP_SLAVE_TYPE_SWRAP 2
+#define DMP_SLAVE_TYPE_MWRAP 3
+#define DMP_SLAVE_SIZE_TYPE 0x00000030
+#define DMP_SLAVE_SIZE_TYPE_S 4
+#define DMP_SLAVE_SIZE_4K 0
+#define DMP_SLAVE_SIZE_8K 1
+#define DMP_SLAVE_SIZE_16K 2
+#define DMP_SLAVE_SIZE_DESC 3
+
+/* EROM CompIdentB */
+#define CIB_REV_MASK 0xff000000
+#define CIB_REV_SHIFT 24
+
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
+
+/* D11 core specific control flag bits */
+#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004
+#define D11_BCMA_IOCTL_PHYRESET 0x0008
+
+/* chip core base & ramsize */
+/* bcm4329 */
+/* SDIO device core, ID 0x829 */
+#define BCM4329_CORE_BUS_BASE 0x18011000
+/* internal memory core, ID 0x80e */
+#define BCM4329_CORE_SOCRAM_BASE 0x18003000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM4329_CORE_ARM_BASE 0x18002000
+
+/* Max possibly supported memory size (limited by IO mapped memory) */
+#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024)
+
+#define CORE_SB(base, field) \
+ (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
+#define SBCOREREV(sbidh) \
+ ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
+ ((sbidh) & SSB_IDHIGH_RCLO))
+
+struct sbconfig {
+ u32 PAD[2];
+ u32 sbipsflag; /* initiator port ocp slave flag */
+ u32 PAD[3];
+ u32 sbtpsflag; /* target port ocp slave flag */
+ u32 PAD[11];
+ u32 sbtmerrloga; /* (sonics >= 2.3) */
+ u32 PAD;
+ u32 sbtmerrlog; /* (sonics >= 2.3) */
+ u32 PAD[3];
+ u32 sbadmatch3; /* address match3 */
+ u32 PAD;
+ u32 sbadmatch2; /* address match2 */
+ u32 PAD;
+ u32 sbadmatch1; /* address match1 */
+ u32 PAD[7];
+ u32 sbimstate; /* initiator agent state */
+ u32 sbintvec; /* interrupt mask */
+ u32 sbtmstatelow; /* target state */
+ u32 sbtmstatehigh; /* target state */
+ u32 sbbwa0; /* bandwidth allocation table0 */
+ u32 PAD;
+ u32 sbimconfiglow; /* initiator configuration */
+ u32 sbimconfighigh; /* initiator configuration */
+ u32 sbadmatch0; /* address match0 */
+ u32 PAD;
+ u32 sbtmconfiglow; /* target configuration */
+ u32 sbtmconfighigh; /* target configuration */
+ u32 sbbconfig; /* broadcast configuration */
+ u32 PAD;
+ u32 sbbstate; /* broadcast state */
+ u32 PAD[3];
+ u32 sbactcnfg; /* activate configuration */
+ u32 PAD[3];
+ u32 sbflagst; /* current sbflags */
+ u32 PAD[3];
+ u32 sbidlow; /* identification */
+ u32 sbidhigh; /* identification */
+};
+
+/* bankidx and bankinfo reg defines corerev >= 8 */
+#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000
+#define SOCRAM_BANKINFO_SZMASK 0x0000007f
+#define SOCRAM_BANKIDX_ROM_MASK 0x00000100
+
+#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8
+/* socram bankinfo memtype */
+#define SOCRAM_MEMTYPE_RAM 0
+#define SOCRAM_MEMTYPE_R0M 1
+#define SOCRAM_MEMTYPE_DEVRAM 2
+
+#define SOCRAM_BANKINFO_SZBASE 8192
+#define SRCI_LSS_MASK 0x00f00000
+#define SRCI_LSS_SHIFT 20
+#define SRCI_SRNB_MASK 0xf0
+#define SRCI_SRNB_SHIFT 4
+#define SRCI_SRBSZ_MASK 0xf
+#define SRCI_SRBSZ_SHIFT 0
+#define SR_BSZ_BASE 14
+
+struct sbsocramregs {
+ u32 coreinfo;
+ u32 bwalloc;
+ u32 extracoreinfo;
+ u32 biststat;
+ u32 bankidx;
+ u32 standbyctrl;
+
+ u32 errlogstatus; /* rev 6 */
+ u32 errlogaddr; /* rev 6 */
+ /* used for patching rev 3 & 5 */
+ u32 cambankidx;
+ u32 cambankstandbyctrl;
+ u32 cambankpatchctrl;
+ u32 cambankpatchtblbaseaddr;
+ u32 cambankcmdreg;
+ u32 cambankdatareg;
+ u32 cambankmaskreg;
+ u32 PAD[1];
+ u32 bankinfo; /* corev 8 */
+ u32 bankpda;
+ u32 PAD[14];
+ u32 extmemconfig;
+ u32 extmemparitycsr;
+ u32 extmemparityerrdata;
+ u32 extmemparityerrcnt;
+ u32 extmemwrctrlandsize;
+ u32 PAD[84];
+ u32 workaround;
+ u32 pwrctl; /* corerev >= 2 */
+ u32 PAD[133];
+ u32 sr_control; /* corerev >= 15 */
+ u32 sr_status; /* corerev >= 15 */
+ u32 sr_address; /* corerev >= 15 */
+ u32 sr_data; /* corerev >= 15 */
+};
+
+#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f)
+#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f)
+
+#define ARMCR4_CAP (0x04)
+#define ARMCR4_BANKIDX (0x40)
+#define ARMCR4_BANKINFO (0x44)
+#define ARMCR4_BANKPDA (0x4C)
+
+#define ARMCR4_TCBBNB_MASK 0xf0
+#define ARMCR4_TCBBNB_SHIFT 4
+#define ARMCR4_TCBANB_MASK 0xf
+#define ARMCR4_TCBANB_SHIFT 0
+
+#define ARMCR4_BSZ_MASK 0x3f
+#define ARMCR4_BSZ_MULT 8192
+
+struct brcmf_core_priv {
+ struct brcmf_core pub;
+ u32 wrapbase;
+ struct list_head list;
+ struct brcmf_chip_priv *chip;
+};
+
+struct brcmf_chip_priv {
+ struct brcmf_chip pub;
+ const struct brcmf_buscore_ops *ops;
+ void *ctx;
+ /* assured first core is chipcommon, second core is buscore */
+ struct list_head cores;
+ u16 num_cores;
+
+ bool (*iscoreup)(struct brcmf_core_priv *core);
+ void (*coredisable)(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset);
+ void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset,
+ u32 postreset);
+};
+
+static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci,
+ struct brcmf_core *core)
+{
+ u32 regdata;
+
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh));
+ core->rev = SBCOREREV(regdata);
+}
+
+static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ u32 address;
+
+ ci = core->chip;
+ address = CORE_SB(core->pub.base, sbtmstatelow);
+ regdata = ci->ops->read32(ci->ctx, address);
+ regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
+ SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
+ return SSB_TMSLOW_CLOCK == regdata;
+}
+
+static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ bool ret;
+
+ ci = core->chip;
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+ ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
+
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+ ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
+
+ return ret;
+}
+
+static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core,
+ u32 prereset, u32 reset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 val, base;
+
+ ci = core->chip;
+ base = core->pub.base;
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ if (val & SSB_TMSLOW_RESET)
+ return;
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ if ((val & SSB_TMSLOW_CLOCK) != 0) {
+ /*
+ * set target reject and spin until busy is clear
+ * (preserve core-specific bits)
+ */
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ val | SSB_TMSLOW_REJECT);
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+ SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh))
+ & SSB_TMSHIGH_BUSY), 100000);
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+ if (val & SSB_TMSHIGH_BUSY)
+ brcmf_err("core state still busy\n");
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+ if (val & SSB_IDLOW_INITIATOR) {
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ val |= SSB_IMSTATE_REJECT;
+ ci->ops->write32(ci->ctx,
+ CORE_SB(base, sbimstate), val);
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ udelay(1);
+ SPINWAIT((ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate)) &
+ SSB_IMSTATE_BUSY), 100000);
+ }
+
+ /* set reset and reject while enabling the clocks */
+ val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+ SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val);
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(10);
+
+ /* clear the initiator reject bit */
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+ if (val & SSB_IDLOW_INITIATOR) {
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ val &= ~SSB_IMSTATE_REJECT;
+ ci->ops->write32(ci->ctx,
+ CORE_SB(base, sbimstate), val);
+ }
+ }
+
+ /* leave reset and reject asserted */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET));
+ udelay(1);
+}
+
+static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core,
+ u32 prereset, u32 reset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+
+ ci = core->chip;
+
+ /* if core is already in reset, skip reset */
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+ if ((regdata & BCMA_RESET_CTL_RESET) != 0)
+ goto in_reset_configure;
+
+ /* configure reset */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+
+ /* put in reset */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL,
+ BCMA_RESET_CTL_RESET);
+ usleep_range(10, 20);
+
+ /* wait till reset is 1 */
+ SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) !=
+ BCMA_RESET_CTL_RESET, 300);
+
+in_reset_configure:
+ /* in-reset configure */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset, u32 postreset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ u32 base;
+
+ ci = core->chip;
+ base = core->pub.base;
+ /*
+ * Must do the disable sequence first to work for
+ * arbitrary current core state.
+ */
+ brcmf_chip_sb_coredisable(core, 0, 0);
+
+ /*
+ * Now do the initialization sequence.
+ * set reset while enabling the clock and
+ * forcing them on throughout the core
+ */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+ SSB_TMSLOW_RESET);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+
+ /* clear any serror */
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+ if (regdata & SSB_TMSHIGH_SERR)
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0);
+
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate));
+ if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) {
+ regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO);
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata);
+ }
+
+ /* clear reset and allow it to propagate throughout the core */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+
+ /* leave clock enabled */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_CLOCK);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+}
+
+static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset, u32 postreset)
+{
+ struct brcmf_chip_priv *ci;
+ int count;
+
+ ci = core->chip;
+
+ /* must disable first to work for arbitrary current core state */
+ brcmf_chip_ai_coredisable(core, prereset, reset);
+
+ count = 0;
+ while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
+ BCMA_RESET_CTL_RESET) {
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0);
+ count++;
+ if (count > 50)
+ break;
+ usleep_range(40, 60);
+ }
+
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ postreset | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static char *brcmf_chip_name(uint chipid, char *buf, uint len)
+{
+ const char *fmt;
+
+ fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
+ snprintf(buf, len, fmt, chipid);
+ return buf;
+}
+
+static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci,
+ u16 coreid, u32 base,
+ u32 wrapbase)
+{
+ struct brcmf_core_priv *core;
+
+ core = kzalloc(sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return ERR_PTR(-ENOMEM);
+
+ core->pub.id = coreid;
+ core->pub.base = base;
+ core->chip = ci;
+ core->wrapbase = wrapbase;
+
+ list_add_tail(&core->list, &ci->cores);
+ return &core->pub;
+}
+
+/* safety check for chipinfo */
+static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core_priv *core;
+ bool need_socram = false;
+ bool has_socram = false;
+ bool cpu_found = false;
+ int idx = 1;
+
+ list_for_each_entry(core, &ci->cores, list) {
+ brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
+ idx++, core->pub.id, core->pub.rev, core->pub.base,
+ core->wrapbase);
+
+ switch (core->pub.id) {
+ case BCMA_CORE_ARM_CM3:
+ cpu_found = true;
+ need_socram = true;
+ break;
+ case BCMA_CORE_INTERNAL_MEM:
+ has_socram = true;
+ break;
+ case BCMA_CORE_ARM_CR4:
+ cpu_found = true;
+ break;
+ case BCMA_CORE_ARM_CA7:
+ cpu_found = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!cpu_found) {
+ brcmf_err("CPU core not detected\n");
+ return -ENXIO;
+ }
+ /* check RAM core presence for ARM CM3 core */
+ if (need_socram && !has_socram) {
+ brcmf_err("RAM core not provided with ARM CM3 core\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg)
+{
+ return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg);
+}
+
+static void brcmf_chip_core_write32(struct brcmf_core_priv *core,
+ u16 reg, u32 val)
+{
+ core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val);
+}
+
+static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx,
+ u32 *banksize)
+{
+ u32 bankinfo;
+ u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT);
+
+ bankidx |= idx;
+ brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx);
+ bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo));
+ *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1;
+ *banksize *= SOCRAM_BANKINFO_SZBASE;
+ return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK);
+}
+
+static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
+ u32 *srsize)
+{
+ u32 coreinfo;
+ uint nb, banksize, lss;
+ bool retent;
+ int i;
+
+ *ramsize = 0;
+ *srsize = 0;
+
+ if (WARN_ON(sr->pub.rev < 4))
+ return;
+
+ if (!brcmf_chip_iscoreup(&sr->pub))
+ brcmf_chip_resetcore(&sr->pub, 0, 0, 0);
+
+ /* Get info for determining size */
+ coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo));
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+
+ if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) {
+ banksize = (coreinfo & SRCI_SRBSZ_MASK);
+ lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
+ if (lss != 0)
+ nb--;
+ *ramsize = nb * (1 << (banksize + SR_BSZ_BASE));
+ if (lss != 0)
+ *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE));
+ } else {
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ for (i = 0; i < nb; i++) {
+ retent = brcmf_chip_socram_banksize(sr, i, &banksize);
+ *ramsize += banksize;
+ if (retent)
+ *srsize += banksize;
+ }
+ }
+
+ /* hardcoded save&restore memory sizes */
+ switch (sr->chip->pub.chip) {
+ case BRCM_CC_4334_CHIP_ID:
+ if (sr->chip->pub.chiprev < 2)
+ *srsize = (32 * 1024);
+ break;
+ case BRCM_CC_43430_CHIP_ID:
+ /* assume sr for now as we can not check
+ * firmware sr capability at this point.
+ */
+ *srsize = (64 * 1024);
+ break;
+ default:
+ break;
+ }
+}
+
+/** Return the SYS MEM size */
+static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem)
+{
+ u32 memsize = 0;
+ u32 coreinfo;
+ u32 idx;
+ u32 nb;
+ u32 banksize;
+
+ if (!brcmf_chip_iscoreup(&sysmem->pub))
+ brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0);
+
+ coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo));
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+
+ for (idx = 0; idx < nb; idx++) {
+ brcmf_chip_socram_banksize(sysmem, idx, &banksize);
+ memsize += banksize;
+ }
+
+ return memsize;
+}
+
+/** Return the TCM-RAM size of the ARMCR4 core. */
+static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4)
+{
+ u32 corecap;
+ u32 memsize = 0;
+ u32 nab;
+ u32 nbb;
+ u32 totb;
+ u32 bxinfo;
+ u32 idx;
+
+ corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP);
+
+ nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT;
+ nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT;
+ totb = nab + nbb;
+
+ for (idx = 0; idx < totb; idx++) {
+ brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx);
+ bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO);
+ memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT;
+ }
+
+ return memsize;
+}
+
+static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
+{
+ switch (ci->pub.chip) {
+ case BRCM_CC_4345_CHIP_ID:
+ return 0x198000;
+ case BRCM_CC_4335_CHIP_ID:
+ case BRCM_CC_4339_CHIP_ID:
+ case BRCM_CC_4350_CHIP_ID:
+ case BRCM_CC_4354_CHIP_ID:
+ case BRCM_CC_4356_CHIP_ID:
+ case BRCM_CC_43567_CHIP_ID:
+ case BRCM_CC_43569_CHIP_ID:
+ case BRCM_CC_43570_CHIP_ID:
+ case BRCM_CC_4358_CHIP_ID:
+ case BRCM_CC_4359_CHIP_ID:
+ case BRCM_CC_43602_CHIP_ID:
+ case BRCM_CC_4371_CHIP_ID:
+ return 0x180000;
+ case BRCM_CC_4365_CHIP_ID:
+ case BRCM_CC_4366_CHIP_ID:
+ return 0x200000;
+ default:
+ brcmf_err("unknown chip: %s\n", ci->pub.name);
+ break;
+ }
+ return 0;
+}
+
+static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core_priv *mem_core;
+ struct brcmf_core *mem;
+
+ mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4);
+ if (mem) {
+ mem_core = container_of(mem, struct brcmf_core_priv, pub);
+ ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core);
+ ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
+ if (!ci->pub.rambase) {
+ brcmf_err("RAM base not provided with ARM CR4 core\n");
+ return -EINVAL;
+ }
+ } else {
+ mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM);
+ if (mem) {
+ mem_core = container_of(mem, struct brcmf_core_priv,
+ pub);
+ ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core);
+ ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
+ if (!ci->pub.rambase) {
+ brcmf_err("RAM base not provided with ARM CA7 core\n");
+ return -EINVAL;
+ }
+ } else {
+ mem = brcmf_chip_get_core(&ci->pub,
+ BCMA_CORE_INTERNAL_MEM);
+ if (!mem) {
+ brcmf_err("No memory cores found\n");
+ return -ENOMEM;
+ }
+ mem_core = container_of(mem, struct brcmf_core_priv,
+ pub);
+ brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize,
+ &ci->pub.srsize);
+ }
+ }
+ brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n",
+ ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize,
+ ci->pub.srsize, ci->pub.srsize);
+
+ if (!ci->pub.ramsize) {
+ brcmf_err("RAM size is undetermined\n");
+ return -ENOMEM;
+ }
+
+ if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) {
+ brcmf_err("RAM size is incorrect\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr,
+ u8 *type)
+{
+ u32 val;
+
+ /* read next descriptor */
+ val = ci->ops->read32(ci->ctx, *eromaddr);
+ *eromaddr += 4;
+
+ if (!type)
+ return val;
+
+ /* determine descriptor type */
+ *type = (val & DMP_DESC_TYPE_MSK);
+ if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS)
+ *type = DMP_DESC_ADDRESS;
+
+ return val;
+}
+
+static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
+ u32 *regbase, u32 *wrapbase)
+{
+ u8 desc;
+ u32 val;
+ u8 mpnum = 0;
+ u8 stype, sztype, wraptype;
+
+ *regbase = 0;
+ *wrapbase = 0;
+
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+ if (desc == DMP_DESC_MASTER_PORT) {
+ mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S;
+ wraptype = DMP_SLAVE_TYPE_MWRAP;
+ } else if (desc == DMP_DESC_ADDRESS) {
+ /* revert erom address */
+ *eromaddr -= 4;
+ wraptype = DMP_SLAVE_TYPE_SWRAP;
+ } else {
+ *eromaddr -= 4;
+ return -EILSEQ;
+ }
+
+ do {
+ /* locate address descriptor */
+ do {
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+ /* unexpected table end */
+ if (desc == DMP_DESC_EOT) {
+ *eromaddr -= 4;
+ return -EFAULT;
+ }
+ } while (desc != DMP_DESC_ADDRESS);
+
+ /* skip upper 32-bit address descriptor */
+ if (val & DMP_DESC_ADDRSIZE_GT32)
+ brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+
+ sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S;
+
+ /* next size descriptor can be skipped */
+ if (sztype == DMP_SLAVE_SIZE_DESC) {
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+ /* skip upper size descriptor if present */
+ if (val & DMP_DESC_ADDRSIZE_GT32)
+ brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+ }
+
+ /* only look for 4K register regions */
+ if (sztype != DMP_SLAVE_SIZE_4K)
+ continue;
+
+ stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S;
+
+ /* only regular slave and wrapper */
+ if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE)
+ *regbase = val & DMP_SLAVE_ADDR_BASE;
+ if (*wrapbase == 0 && stype == wraptype)
+ *wrapbase = val & DMP_SLAVE_ADDR_BASE;
+ } while (*regbase == 0 || *wrapbase == 0);
+
+ return 0;
+}
+
+static
+int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core *core;
+ u32 eromaddr;
+ u8 desc_type = 0;
+ u32 val;
+ u16 id;
+ u8 nmp, nsp, nmw, nsw, rev;
+ u32 base, wrap;
+ int err;
+
+ eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr));
+
+ while (desc_type != DMP_DESC_EOT) {
+ val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+ if (!(val & DMP_DESC_VALID))
+ continue;
+
+ if (desc_type == DMP_DESC_EMPTY)
+ continue;
+
+ /* need a component descriptor */
+ if (desc_type != DMP_DESC_COMPONENT)
+ continue;
+
+ id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S;
+
+ /* next descriptor must be component as well */
+ val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+ if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT))
+ return -EFAULT;
+
+ /* only look at cores with master port(s) */
+ nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S;
+ nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S;
+ nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S;
+ nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S;
+ rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
+
+ /* need core with ports */
+ if (nmw + nsw == 0)
+ continue;
+
+ /* try to obtain register address info */
+ err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap);
+ if (err)
+ continue;
+
+ /* finally a core to be added */
+ core = brcmf_chip_add_core(ci, id, base, wrap);
+ if (IS_ERR(core))
+ return PTR_ERR(core);
+
+ core->rev = rev;
+ }
+
+ return 0;
+}
+
+static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core *core;
+ u32 regdata;
+ u32 socitype;
+ int ret;
+
+ /* Get CC core rev
+ * Chipid is assume to be at offset 0 from SI_ENUM_BASE
+ * For different chiptypes or old sdio hosts w/o chipcommon,
+ * other ways of recognition should be added here.
+ */
+ regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid));
+ ci->pub.chip = regdata & CID_ID_MASK;
+ ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
+ socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+
+ brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name));
+ brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n",
+ socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name,
+ ci->pub.chiprev);
+
+ if (socitype == SOCI_SB) {
+ if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) {
+ brcmf_err("SB chip is not supported\n");
+ return -ENODEV;
+ }
+ ci->iscoreup = brcmf_chip_sb_iscoreup;
+ ci->coredisable = brcmf_chip_sb_coredisable;
+ ci->resetcore = brcmf_chip_sb_resetcore;
+
+ core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON,
+ SI_ENUM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV,
+ BCM4329_CORE_BUS_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM,
+ BCM4329_CORE_SOCRAM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3,
+ BCM4329_CORE_ARM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+
+ core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ } else if (socitype == SOCI_AI) {
+ ci->iscoreup = brcmf_chip_ai_iscoreup;
+ ci->coredisable = brcmf_chip_ai_coredisable;
+ ci->resetcore = brcmf_chip_ai_resetcore;
+
+ brcmf_chip_dmp_erom_scan(ci);
+ } else {
+ brcmf_err("chip backplane type %u is not supported\n",
+ socitype);
+ return -ENODEV;
+ }
+
+ ret = brcmf_chip_cores_check(ci);
+ if (ret)
+ return ret;
+
+ /* assure chip is passive for core access */
+ brcmf_chip_set_passive(&ci->pub);
+
+ /* Call bus specific reset function now. Cores have been determined
+ * but further access may require a chip specific reset at this point.
+ */
+ if (ci->ops->reset) {
+ ci->ops->reset(ci->ctx, &ci->pub);
+ brcmf_chip_set_passive(&ci->pub);
+ }
+
+ return brcmf_chip_get_raminfo(ci);
+}
+
+static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
+{
+ struct brcmf_core *core;
+ struct brcmf_core_priv *cpu;
+ u32 val;
+
+
+ core = brcmf_chip_get_core(&chip->pub, id);
+ if (!core)
+ return;
+
+ switch (id) {
+ case BCMA_CORE_ARM_CM3:
+ brcmf_chip_coredisable(core, 0, 0);
+ break;
+ case BCMA_CORE_ARM_CR4:
+ case BCMA_CORE_ARM_CA7:
+ cpu = container_of(core, struct brcmf_core_priv, pub);
+
+ /* clear all IOCTL bits except HALT bit */
+ val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL);
+ val &= ARMCR4_BCMA_IOCTL_CPUHALT;
+ brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT,
+ ARMCR4_BCMA_IOCTL_CPUHALT);
+ break;
+ default:
+ brcmf_err("unknown id: %u\n", id);
+ break;
+ }
+}
+
+static int brcmf_chip_setup(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_chip *pub;
+ struct brcmf_core_priv *cc;
+ u32 base;
+ u32 val;
+ int ret = 0;
+
+ pub = &chip->pub;
+ cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+ base = cc->pub.base;
+
+ /* get chipcommon capabilites */
+ pub->cc_caps = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(base, capabilities));
+
+ /* get pmu caps & rev */
+ if (pub->cc_caps & CC_CAP_PMU) {
+ val = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(base, pmucapabilities));
+ pub->pmurev = val & PCAP_REV_MASK;
+ pub->pmucaps = val;
+ }
+
+ brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n",
+ cc->pub.rev, pub->pmurev, pub->pmucaps);
+
+ /* execute bus core specific setup */
+ if (chip->ops->setup)
+ ret = chip->ops->setup(chip->ctx, pub);
+
+ return ret;
+}
+
+struct brcmf_chip *brcmf_chip_attach(void *ctx,
+ const struct brcmf_buscore_ops *ops)
+{
+ struct brcmf_chip_priv *chip;
+ int err = 0;
+
+ if (WARN_ON(!ops->read32))
+ err = -EINVAL;
+ if (WARN_ON(!ops->write32))
+ err = -EINVAL;
+ if (WARN_ON(!ops->prepare))
+ err = -EINVAL;
+ if (WARN_ON(!ops->activate))
+ err = -EINVAL;
+ if (err < 0)
+ return ERR_PTR(-EINVAL);
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&chip->cores);
+ chip->num_cores = 0;
+ chip->ops = ops;
+ chip->ctx = ctx;
+
+ err = ops->prepare(ctx);
+ if (err < 0)
+ goto fail;
+
+ err = brcmf_chip_recognition(chip);
+ if (err < 0)
+ goto fail;
+
+ err = brcmf_chip_setup(chip);
+ if (err < 0)
+ goto fail;
+
+ return &chip->pub;
+
+fail:
+ brcmf_chip_detach(&chip->pub);
+ return ERR_PTR(err);
+}
+
+void brcmf_chip_detach(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *core;
+ struct brcmf_core_priv *tmp;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ list_for_each_entry_safe(core, tmp, &chip->cores, list) {
+ list_del(&core->list);
+ kfree(core);
+ }
+ kfree(chip);
+}
+
+struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *core;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ list_for_each_entry(core, &chip->cores, list)
+ if (core->pub.id == coreid)
+ return &core->pub;
+
+ return NULL;
+}
+
+struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *cc;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+ if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON))
+ return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON);
+ return &cc->pub;
+}
+
+bool brcmf_chip_iscoreup(struct brcmf_core *pub)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ return core->chip->iscoreup(core);
+}
+
+void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ core->chip->coredisable(core, prereset, reset);
+}
+
+void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset,
+ u32 postreset)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ core->chip->resetcore(core, prereset, reset, postreset);
+}
+
+static void
+brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+ struct brcmf_core_priv *sr;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+
+ /* disable bank #3 remap for this device */
+ if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) {
+ sr = container_of(core, struct brcmf_core_priv, pub);
+ brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3);
+ brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0);
+ }
+}
+
+static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+ if (!brcmf_chip_iscoreup(core)) {
+ brcmf_err("SOCRAM core is down after reset?\n");
+ return false;
+ }
+
+ chip->ops->activate(chip->ctx, &chip->pub, 0);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+
+ return true;
+}
+
+static inline void
+brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+}
+
+static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
+{
+ struct brcmf_core *core;
+
+ chip->ops->activate(chip->ctx, &chip->pub, rstvec);
+
+ /* restore ARM */
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4);
+ brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
+
+ return true;
+}
+
+static inline void
+brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+}
+
+static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
+{
+ struct brcmf_core *core;
+
+ chip->ops->activate(chip->ctx, &chip->pub, rstvec);
+
+ /* restore ARM */
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7);
+ brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
+
+ return true;
+}
+
+void brcmf_chip_set_passive(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core *arm;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+ if (arm) {
+ brcmf_chip_cr4_set_passive(chip);
+ return;
+ }
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
+ if (arm) {
+ brcmf_chip_ca7_set_passive(chip);
+ return;
+ }
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
+ if (arm) {
+ brcmf_chip_cm3_set_passive(chip);
+ return;
+ }
+}
+
+bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core *arm;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+ if (arm)
+ return brcmf_chip_cr4_set_active(chip, rstvec);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
+ if (arm)
+ return brcmf_chip_ca7_set_active(chip, rstvec);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
+ if (arm)
+ return brcmf_chip_cm3_set_active(chip);
+
+ return false;
+}
+
+bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
+{
+ u32 base, addr, reg, pmu_cc3_mask = ~0;
+ struct brcmf_chip_priv *chip;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* old chips with PMU version less than 17 don't support save restore */
+ if (pub->pmurev < 17)
+ return false;
+
+ base = brcmf_chip_get_chipcommon(pub)->base;
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+
+ switch (pub->chip) {
+ case BRCM_CC_4354_CHIP_ID:
+ /* explicitly check SR engine enable bit */
+ pmu_cc3_mask = BIT(2);
+ /* fall-through */
+ case BRCM_CC_43241_CHIP_ID:
+ case BRCM_CC_4335_CHIP_ID:
+ case BRCM_CC_4339_CHIP_ID:
+ /* read PMU chipcontrol register 3 */
+ addr = CORE_CC_REG(base, chipcontrol_addr);
+ chip->ops->write32(chip->ctx, addr, 3);
+ addr = CORE_CC_REG(base, chipcontrol_data);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & pmu_cc3_mask) != 0;
+ case BRCM_CC_43430_CHIP_ID:
+ addr = CORE_CC_REG(base, sr_control1);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return reg != 0;
+ default:
+ addr = CORE_CC_REG(base, pmucapabilities_ext);
+ reg = chip->ops->read32(chip->ctx, addr);
+ if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
+ return false;
+
+ addr = CORE_CC_REG(base, retention_ctl);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+ PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
+ }
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
index f6b5feea23d2..f6b5feea23d2 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
new file mode 100644
index 000000000000..4265b50faa98
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "tracepoint.h"
+#include "common.h"
+
+const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40
+#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40
+
+/* boost value for RSSI_DELTA in preferred join selection */
+#define BRCMF_JOIN_PREF_RSSI_BOOST 8
+
+#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */
+
+static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;
+module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);
+MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]");
+
+/* Debug level configuration. See debug.h for bits, sysfs modifiable */
+int brcmf_msg_level;
+module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(debug, "Level of debug output");
+
+static int brcmf_p2p_enable;
+module_param_named(p2pon, brcmf_p2p_enable, int, 0);
+MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality");
+
+static int brcmf_feature_disable;
+module_param_named(feature_disable, brcmf_feature_disable, int, 0);
+MODULE_PARM_DESC(feature_disable, "Disable features");
+
+static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN];
+module_param_string(alternative_fw_path, brcmf_firmware_path,
+ BRCMF_FW_ALTPATH_LEN, S_IRUSR);
+MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path");
+
+static int brcmf_fcmode;
+module_param_named(fcmode, brcmf_fcmode, int, 0);
+MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control");
+
+static int brcmf_roamoff;
+module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
+MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
+
+#ifdef DEBUG
+/* always succeed brcmf_bus_start() */
+static int brcmf_ignore_probe_fail;
+module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0);
+MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging");
+#endif
+
+struct brcmf_mp_global_t brcmf_mp_global;
+
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
+{
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+ u8 buf[BRCMF_DCMD_SMLEN];
+ struct brcmf_join_pref_params join_pref_params[2];
+ struct brcmf_rev_info_le revinfo;
+ struct brcmf_rev_info *ri;
+ char *ptr;
+ s32 err;
+
+ /* retreive mac address */
+ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
+ sizeof(ifp->mac_addr));
+ if (err < 0) {
+ brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
+ goto done;
+ }
+ memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
+
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,
+ &revinfo, sizeof(revinfo));
+ ri = &ifp->drvr->revinfo;
+ if (err < 0) {
+ brcmf_err("retrieving revision info failed, %d\n", err);
+ } else {
+ ri->vendorid = le32_to_cpu(revinfo.vendorid);
+ ri->deviceid = le32_to_cpu(revinfo.deviceid);
+ ri->radiorev = le32_to_cpu(revinfo.radiorev);
+ ri->chiprev = le32_to_cpu(revinfo.chiprev);
+ ri->corerev = le32_to_cpu(revinfo.corerev);
+ ri->boardid = le32_to_cpu(revinfo.boardid);
+ ri->boardvendor = le32_to_cpu(revinfo.boardvendor);
+ ri->boardrev = le32_to_cpu(revinfo.boardrev);
+ ri->driverrev = le32_to_cpu(revinfo.driverrev);
+ ri->ucoderev = le32_to_cpu(revinfo.ucoderev);
+ ri->bus = le32_to_cpu(revinfo.bus);
+ ri->chipnum = le32_to_cpu(revinfo.chipnum);
+ ri->phytype = le32_to_cpu(revinfo.phytype);
+ ri->phyrev = le32_to_cpu(revinfo.phyrev);
+ ri->anarev = le32_to_cpu(revinfo.anarev);
+ ri->chippkg = le32_to_cpu(revinfo.chippkg);
+ ri->nvramrev = le32_to_cpu(revinfo.nvramrev);
+ }
+ ri->result = err;
+
+ /* query for 'ver' to get version info from firmware */
+ memset(buf, 0, sizeof(buf));
+ strcpy(buf, "ver");
+ err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
+ if (err < 0) {
+ brcmf_err("Retreiving version information failed, %d\n",
+ err);
+ goto done;
+ }
+ ptr = (char *)buf;
+ strsep(&ptr, "\n");
+
+ /* Print fw version info */
+ brcmf_err("Firmware version = %s\n", buf);
+
+ /* locate firmware version number for ethtool */
+ ptr = strrchr(buf, ' ') + 1;
+ strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
+
+ /* set mpc */
+ err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
+ if (err) {
+ brcmf_err("failed setting mpc\n");
+ goto done;
+ }
+
+ /* Setup join_pref to select target by RSSI(with boost on 5GHz) */
+ join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
+ join_pref_params[0].len = 2;
+ join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;
+ join_pref_params[0].band = WLC_BAND_5G;
+ join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;
+ join_pref_params[1].len = 2;
+ join_pref_params[1].rssi_gain = 0;
+ join_pref_params[1].band = 0;
+ err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
+ sizeof(join_pref_params));
+ if (err)
+ brcmf_err("Set join_pref error (%d)\n", err);
+
+ /* Setup event_msgs, enable E_IF */
+ err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ if (err) {
+ brcmf_err("Get event_msgs error (%d)\n", err);
+ goto done;
+ }
+ setbit(eventmask, BRCMF_E_IF);
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ if (err) {
+ brcmf_err("Set event_msgs error (%d)\n", err);
+ goto done;
+ }
+
+ /* Setup default scan channel time */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+ BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
+ if (err) {
+ brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n",
+ err);
+ goto done;
+ }
+
+ /* Setup default scan unassoc time */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+ BRCMF_DEFAULT_SCAN_UNASSOC_TIME);
+ if (err) {
+ brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n",
+ err);
+ goto done;
+ }
+
+ /* Enable tx beamforming, errors can be ignored (not supported) */
+ (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1);
+
+ /* do bus specific preinit here */
+ err = brcmf_bus_preinit(ifp->drvr->bus_if);
+done:
+ return err;
+}
+
+#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG)
+void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ if (brcmf_msg_level & level)
+ pr_debug("%s %pV", func, &vaf);
+ trace_brcmf_dbg(level, func, &vaf);
+ va_end(args);
+}
+#endif
+
+void brcmf_mp_attach(void)
+{
+ strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path,
+ BRCMF_FW_ALTPATH_LEN);
+}
+
+int brcmf_mp_device_attach(struct brcmf_pub *drvr)
+{
+ drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC);
+ if (!drvr->settings) {
+ brcmf_err("Failed to alloca storage space for settings\n");
+ return -ENOMEM;
+ }
+
+ drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz;
+ drvr->settings->p2p_enable = !!brcmf_p2p_enable;
+ drvr->settings->feature_disable = brcmf_feature_disable;
+ drvr->settings->fcmode = brcmf_fcmode;
+ drvr->settings->roamoff = !!brcmf_roamoff;
+#ifdef DEBUG
+ drvr->settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
+#endif
+ return 0;
+}
+
+void brcmf_mp_device_detach(struct brcmf_pub *drvr)
+{
+ kfree(drvr->settings);
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
new file mode 100644
index 000000000000..3b0a63b98e99
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_COMMON_H
+#define BRCMFMAC_COMMON_H
+
+extern const u8 ALLFFMAC[ETH_ALEN];
+
+#define BRCMF_FW_ALTPATH_LEN 256
+
+/* Definitions for the module global and device specific settings are defined
+ * here. Two structs are used for them. brcmf_mp_global_t and brcmf_mp_device.
+ * The mp_global is instantiated once in a global struct and gets initialized
+ * by the common_attach function which should be called before any other
+ * (module) initiliazation takes place. The device specific settings is part
+ * of the drvr struct and should be initialized on every brcmf_attach.
+ */
+
+/**
+ * struct brcmf_mp_global_t - Global module paramaters.
+ *
+ * @firmware_path: Alternative firmware path.
+ */
+struct brcmf_mp_global_t {
+ char firmware_path[BRCMF_FW_ALTPATH_LEN];
+};
+
+extern struct brcmf_mp_global_t brcmf_mp_global;
+
+/**
+ * struct brcmf_mp_device - Device module paramaters.
+ *
+ * @sdiod_txglomsz: SDIO txglom size.
+ * @joinboost_5g_rssi: 5g rssi booost for preferred join selection.
+ * @p2p_enable: Legacy P2P0 enable (old wpa_supplicant).
+ * @feature_disable: Feature_disable bitmask.
+ * @fcmode: FWS flow control.
+ * @roamoff: Firmware roaming off?
+ */
+struct brcmf_mp_device {
+ int sdiod_txglomsz;
+ int joinboost_5g_rssi;
+ bool p2p_enable;
+ int feature_disable;
+ int fcmode;
+ bool roamoff;
+ bool ignore_probe_fail;
+};
+
+void brcmf_mp_attach(void);
+int brcmf_mp_device_attach(struct brcmf_pub *drvr);
+void brcmf_mp_device_detach(struct brcmf_pub *drvr);
+#ifdef DEBUG
+static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
+{
+ return drvr->settings->ignore_probe_fail;
+}
+#else
+static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
+{
+ return false;
+}
+#endif
+
+/* Sets dongle media info (drv_version, mac address). */
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
+
+#endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
index 7b0e52195a85..7b0e52195a85 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h
index b85033611c8d..b85033611c8d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
new file mode 100644
index 000000000000..ed9998b69709
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -0,0 +1,1356 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/inetdevice.h>
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "fwil_types.h"
+#include "p2p.h"
+#include "cfg80211.h"
+#include "fwil.h"
+#include "fwsignal.h"
+#include "feature.h"
+#include "proto.h"
+#include "pcie.h"
+#include "common.h"
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(50)
+
+/* AMPDU rx reordering definitions */
+#define BRCMF_RXREORDER_FLOWID_OFFSET 0
+#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
+#define BRCMF_RXREORDER_FLAGS_OFFSET 4
+#define BRCMF_RXREORDER_CURIDX_OFFSET 6
+#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
+
+#define BRCMF_RXREORDER_DEL_FLOW 0x01
+#define BRCMF_RXREORDER_FLUSH_ALL 0x02
+#define BRCMF_RXREORDER_CURIDX_VALID 0x04
+#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
+#define BRCMF_RXREORDER_NEW_HOLE 0x10
+
+#define BRCMF_BSSIDX_INVALID -1
+
+char *brcmf_ifname(struct brcmf_if *ifp)
+{
+ if (!ifp)
+ return "<if_null>";
+
+ if (ifp->ndev)
+ return ifp->ndev->name;
+
+ return "<if_none>";
+}
+
+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
+{
+ struct brcmf_if *ifp;
+ s32 bsscfgidx;
+
+ if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+ brcmf_err("ifidx %d out of range\n", ifidx);
+ return NULL;
+ }
+
+ ifp = NULL;
+ bsscfgidx = drvr->if2bss[ifidx];
+ if (bsscfgidx >= 0)
+ ifp = drvr->iflist[bsscfgidx];
+
+ return ifp;
+}
+
+static void _brcmf_set_multicast_list(struct work_struct *work)
+{
+ struct brcmf_if *ifp;
+ struct net_device *ndev;
+ struct netdev_hw_addr *ha;
+ u32 cmd_value, cnt;
+ __le32 cnt_le;
+ char *buf, *bufp;
+ u32 buflen;
+ s32 err;
+
+ ifp = container_of(work, struct brcmf_if, multicast_work);
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ ndev = ifp->ndev;
+
+ /* Determine initial value of allmulti flag */
+ cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
+
+ /* Send down the multicast list first. */
+ cnt = netdev_mc_count(ndev);
+ buflen = sizeof(cnt) + (cnt * ETH_ALEN);
+ buf = kmalloc(buflen, GFP_ATOMIC);
+ if (!buf)
+ return;
+ bufp = buf;
+
+ cnt_le = cpu_to_le32(cnt);
+ memcpy(bufp, &cnt_le, sizeof(cnt_le));
+ bufp += sizeof(cnt_le);
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ if (!cnt)
+ break;
+ memcpy(bufp, ha->addr, ETH_ALEN);
+ bufp += ETH_ALEN;
+ cnt--;
+ }
+
+ err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen);
+ if (err < 0) {
+ brcmf_err("Setting mcast_list failed, %d\n", err);
+ cmd_value = cnt ? true : cmd_value;
+ }
+
+ kfree(buf);
+
+ /*
+ * Now send the allmulti setting. This is based on the setting in the
+ * net_device flags, but might be modified above to be turned on if we
+ * were trying to set some addresses and dongle rejected it...
+ */
+ err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value);
+ if (err < 0)
+ brcmf_err("Setting allmulti failed, %d\n", err);
+
+ /*Finally, pick up the PROMISC flag */
+ cmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value);
+ if (err < 0)
+ brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n",
+ err);
+}
+
+static void
+_brcmf_set_mac_address(struct work_struct *work)
+{
+ struct brcmf_if *ifp;
+ s32 err;
+
+ ifp = container_of(work, struct brcmf_if, setmacaddr_work);
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr,
+ ETH_ALEN);
+ if (err < 0) {
+ brcmf_err("Setting cur_etheraddr failed, %d\n", err);
+ } else {
+ brcmf_dbg(TRACE, "MAC address updated to %pM\n",
+ ifp->mac_addr);
+ memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
+ }
+}
+
+static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct sockaddr *sa = (struct sockaddr *)addr;
+
+ memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN);
+ schedule_work(&ifp->setmacaddr_work);
+ return 0;
+}
+
+static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ schedule_work(&ifp->multicast_work);
+}
+
+static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ int ret;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+
+ brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ /* Can the device send data? */
+ if (drvr->bus_if->state != BRCMF_BUS_UP) {
+ brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state);
+ netif_stop_queue(ndev);
+ dev_kfree_skb(skb);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ /* Make sure there's enough room for any header */
+ if (skb_headroom(skb) < drvr->hdrlen) {
+ struct sk_buff *skb2;
+
+ brcmf_dbg(INFO, "%s: insufficient headroom\n",
+ brcmf_ifname(ifp));
+ drvr->bus_if->tx_realloc++;
+ skb2 = skb_realloc_headroom(skb, drvr->hdrlen);
+ dev_kfree_skb(skb);
+ skb = skb2;
+ if (skb == NULL) {
+ brcmf_err("%s: skb_realloc_headroom failed\n",
+ brcmf_ifname(ifp));
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ /* validate length for ether packet */
+ if (skb->len < sizeof(*eh)) {
+ ret = -EINVAL;
+ dev_kfree_skb(skb);
+ goto done;
+ }
+
+ if (eh->h_proto == htons(ETH_P_PAE))
+ atomic_inc(&ifp->pend_8021x_cnt);
+
+ ret = brcmf_fws_process_skb(ifp, skb);
+
+done:
+ if (ret) {
+ ifp->stats.tx_dropped++;
+ } else {
+ ifp->stats.tx_packets++;
+ ifp->stats.tx_bytes += skb->len;
+ }
+
+ /* Return ok: we always eat the packet */
+ return NETDEV_TX_OK;
+}
+
+void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state)
+{
+ unsigned long flags;
+
+ if (!ifp || !ifp->ndev)
+ return;
+
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d stop=0x%X reason=%d state=%d\n",
+ ifp->bsscfgidx, ifp->netif_stop, reason, state);
+
+ spin_lock_irqsave(&ifp->netif_stop_lock, flags);
+ if (state) {
+ if (!ifp->netif_stop)
+ netif_stop_queue(ifp->ndev);
+ ifp->netif_stop |= reason;
+ } else {
+ ifp->netif_stop &= ~reason;
+ 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)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_fws_bus_blocked(drvr, state);
+}
+
+void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+ skb->dev = ifp->ndev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ if (skb->pkt_type == PACKET_MULTICAST)
+ ifp->stats.multicast++;
+
+ /* Process special event packets */
+ brcmf_fweh_process_skb(ifp->drvr, skb);
+
+ if (!(ifp->ndev->flags & IFF_UP)) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+
+ ifp->stats.rx_bytes += skb->len;
+ ifp->stats.rx_packets++;
+
+ brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol));
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ /* If the receive is not processed inside an ISR,
+ * the softirqd must be woken explicitly to service
+ * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
+ */
+ netif_rx_ni(skb);
+}
+
+static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
+ u8 start, u8 end,
+ struct sk_buff_head *skb_list)
+{
+ /* initialize return list */
+ __skb_queue_head_init(skb_list);
+
+ if (rfi->pend_pkts == 0) {
+ brcmf_dbg(INFO, "no packets in reorder queue\n");
+ return;
+ }
+
+ do {
+ if (rfi->pktslots[start]) {
+ __skb_queue_tail(skb_list, rfi->pktslots[start]);
+ rfi->pktslots[start] = NULL;
+ }
+ start++;
+ if (start > rfi->max_idx)
+ start = 0;
+ } while (start != end);
+ rfi->pend_pkts -= skb_queue_len(skb_list);
+}
+
+static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
+ struct sk_buff *pkt)
+{
+ u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
+ struct brcmf_ampdu_rx_reorder *rfi;
+ struct sk_buff_head reorder_list;
+ struct sk_buff *pnext;
+ u8 flags;
+ u32 buf_size;
+
+ flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
+ flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
+
+ /* validate flags and flow id */
+ if (flags == 0xFF) {
+ brcmf_err("invalid flags...so ignore this packet\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ rfi = ifp->drvr->reorder_flows[flow_id];
+ if (flags & BRCMF_RXREORDER_DEL_FLOW) {
+ brcmf_dbg(INFO, "flow-%d: delete\n",
+ flow_id);
+
+ if (rfi == NULL) {
+ brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ flow_id);
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
+ &reorder_list);
+ /* add the last packet */
+ __skb_queue_tail(&reorder_list, pkt);
+ kfree(rfi);
+ ifp->drvr->reorder_flows[flow_id] = NULL;
+ goto netif_rx;
+ }
+ /* from here on we need a flow reorder instance */
+ if (rfi == NULL) {
+ buf_size = sizeof(*rfi);
+ max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+
+ buf_size += (max_idx + 1) * sizeof(pkt);
+
+ /* allocate space for flow reorder info */
+ brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
+ flow_id, max_idx);
+ rfi = kzalloc(buf_size, GFP_ATOMIC);
+ if (rfi == NULL) {
+ brcmf_err("failed to alloc buffer\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ ifp->drvr->reorder_flows[flow_id] = rfi;
+ rfi->pktslots = (struct sk_buff **)(rfi+1);
+ rfi->max_idx = max_idx;
+ }
+ if (flags & BRCMF_RXREORDER_NEW_HOLE) {
+ if (rfi->pend_pkts) {
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
+ rfi->exp_idx,
+ &reorder_list);
+ WARN_ON(rfi->pend_pkts);
+ } else {
+ __skb_queue_head_init(&reorder_list);
+ }
+ rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+ rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+ rfi->pktslots[rfi->cur_idx] = pkt;
+ rfi->pend_pkts++;
+ brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
+ flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
+ } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
+ cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
+ /* still in the current hole */
+ /* enqueue the current on the buffer chain */
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ rfi->cur_idx = cur_idx;
+ brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ /* can return now as there is no reorder
+ * list to process.
+ */
+ return;
+ }
+ if (rfi->exp_idx == cur_idx) {
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "error buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+
+ /* got the expected one. flush from current to expected
+ * and update expected
+ */
+ brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ rfi->cur_idx = cur_idx;
+ rfi->exp_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
+ &reorder_list);
+ brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
+ flow_id, skb_queue_len(&reorder_list),
+ rfi->pend_pkts);
+ } else {
+ u8 end_idx;
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
+ flow_id, flags, rfi->cur_idx, rfi->exp_idx,
+ cur_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ /* flush pkts first */
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+
+ if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
+ __skb_queue_tail(&reorder_list, pkt);
+ } else {
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ }
+ rfi->exp_idx = exp_idx;
+ rfi->cur_idx = cur_idx;
+ }
+ } else {
+ /* explicity window move updating the expected index */
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
+ flow_id, flags, rfi->exp_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+ __skb_queue_tail(&reorder_list, pkt);
+ /* set the new expected idx */
+ rfi->exp_idx = exp_idx;
+ }
+netif_rx:
+ skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ __skb_unlink(pkt, &reorder_list);
+ brcmf_netif_rx(ifp, pkt);
+ }
+}
+
+void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_skb_reorder_data *rd;
+ int ret;
+
+ brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+
+ /* process and remove protocol-specific header */
+ ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
+
+ if (ret || !ifp || !ifp->ndev) {
+ if (ret != -ENODATA && ifp)
+ ifp->stats.rx_errors++;
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ if (rd->reorder)
+ brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
+ else
+ brcmf_netif_rx(ifp, skb);
+}
+
+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
+{
+ struct ethhdr *eh;
+ u16 type;
+
+ eh = (struct ethhdr *)(txp->data);
+ type = ntohs(eh->h_proto);
+
+ if (type == ETH_P_PAE) {
+ atomic_dec(&ifp->pend_8021x_cnt);
+ if (waitqueue_active(&ifp->pend_8021x_wait))
+ wake_up(&ifp->pend_8021x_wait);
+ }
+
+ if (!success)
+ ifp->stats.tx_errors++;
+
+ brcmu_pkt_buf_free_skb(txp);
+}
+
+void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_if *ifp;
+
+ /* await txstatus signal for firmware if active */
+ if (brcmf_fws_fc_active(drvr->fws)) {
+ if (!success)
+ brcmf_fws_bustxfail(drvr->fws, txp);
+ } else {
+ if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
+ brcmu_pkt_buf_free_skb(txp);
+ else
+ brcmf_txfinalize(ifp, txp, success);
+ }
+}
+
+static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ return &ifp->stats;
+}
+
+static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ char drev[BRCMU_DOTREV_LEN] = "n/a";
+
+ if (drvr->revinfo.result == 0)
+ brcmu_dotrev_str(drvr->revinfo.driverrev, drev);
+ strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+ strlcpy(info->version, drev, sizeof(info->version));
+ strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version));
+ strlcpy(info->bus_info, dev_name(drvr->bus_if->dev),
+ sizeof(info->bus_info));
+}
+
+static const struct ethtool_ops brcmf_ethtool_ops = {
+ .get_drvinfo = brcmf_ethtool_get_drvinfo,
+};
+
+static int brcmf_netdev_stop(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ brcmf_cfg80211_down(ndev);
+
+ brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0);
+
+ brcmf_net_setcarrier(ifp, false);
+
+ return 0;
+}
+
+static int brcmf_netdev_open(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_bus *bus_if = drvr->bus_if;
+ u32 toe_ol;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ /* If bus is not ready, can't continue */
+ if (bus_if->state != BRCMF_BUS_UP) {
+ brcmf_err("failed bus is not ready\n");
+ return -EAGAIN;
+ }
+
+ atomic_set(&ifp->pend_8021x_cnt, 0);
+
+ /* Get current TOE mode from dongle */
+ if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0
+ && (toe_ol & TOE_TX_CSUM_OL) != 0)
+ ndev->features |= NETIF_F_IP_CSUM;
+ else
+ ndev->features &= ~NETIF_F_IP_CSUM;
+
+ if (brcmf_cfg80211_up(ndev)) {
+ brcmf_err("failed to bring up cfg80211\n");
+ return -EIO;
+ }
+
+ /* Clear, carrier, set when connected or AP mode. */
+ netif_carrier_off(ndev);
+ return 0;
+}
+
+static const struct net_device_ops brcmf_netdev_ops_pri = {
+ .ndo_open = brcmf_netdev_open,
+ .ndo_stop = brcmf_netdev_stop,
+ .ndo_get_stats = brcmf_netdev_get_stats,
+ .ndo_start_xmit = brcmf_netdev_start_xmit,
+ .ndo_set_mac_address = brcmf_netdev_set_mac_address,
+ .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
+};
+
+int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct net_device *ndev;
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
+ ifp->mac_addr);
+ ndev = ifp->ndev;
+
+ /* set appropriate operations */
+ ndev->netdev_ops = &brcmf_netdev_ops_pri;
+
+ ndev->hard_header_len += drvr->hdrlen;
+ ndev->ethtool_ops = &brcmf_ethtool_ops;
+
+ drvr->rxsz = ndev->mtu + ndev->hard_header_len +
+ drvr->hdrlen;
+
+ /* set the mac address */
+ memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
+
+ INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address);
+ INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
+
+ if (rtnl_locked)
+ err = register_netdevice(ndev);
+ else
+ err = register_netdev(ndev);
+ if (err != 0) {
+ brcmf_err("couldn't register the net device\n");
+ goto fail;
+ }
+
+ brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
+ return 0;
+
+fail:
+ drvr->iflist[ifp->bsscfgidx] = NULL;
+ ndev->netdev_ops = NULL;
+ free_netdev(ndev);
+ return -EBADE;
+}
+
+static void brcmf_net_detach(struct net_device *ndev)
+{
+ if (ndev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(ndev);
+ else
+ brcmf_cfg80211_free_netdev(ndev);
+}
+
+void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
+{
+ struct net_device *ndev;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d carrier=%d\n", ifp->bsscfgidx,
+ on);
+
+ ndev = ifp->ndev;
+ brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on);
+ if (on) {
+ if (!netif_carrier_ok(ndev))
+ netif_carrier_on(ndev);
+
+ } else {
+ if (netif_carrier_ok(ndev))
+ netif_carrier_off(ndev);
+ }
+}
+
+static int brcmf_net_p2p_open(struct net_device *ndev)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ return brcmf_cfg80211_up(ndev);
+}
+
+static int brcmf_net_p2p_stop(struct net_device *ndev)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ return brcmf_cfg80211_down(ndev);
+}
+
+static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ if (skb)
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops brcmf_netdev_ops_p2p = {
+ .ndo_open = brcmf_net_p2p_open,
+ .ndo_stop = brcmf_net_p2p_stop,
+ .ndo_start_xmit = brcmf_net_p2p_start_xmit
+};
+
+static int brcmf_net_p2p_attach(struct brcmf_if *ifp)
+{
+ struct net_device *ndev;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
+ ifp->mac_addr);
+ ndev = ifp->ndev;
+
+ ndev->netdev_ops = &brcmf_netdev_ops_p2p;
+
+ /* set the mac address */
+ memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
+
+ if (register_netdev(ndev) != 0) {
+ brcmf_err("couldn't register the p2p net device\n");
+ goto fail;
+ }
+
+ brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
+
+ return 0;
+
+fail:
+ ifp->drvr->iflist[ifp->bsscfgidx] = NULL;
+ ndev->netdev_ops = NULL;
+ free_netdev(ndev);
+ return -EBADE;
+}
+
+struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
+ bool is_p2pdev, char *name, u8 *mac_addr)
+{
+ struct brcmf_if *ifp;
+ struct net_device *ndev;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx, ifidx);
+
+ ifp = drvr->iflist[bsscfgidx];
+ /*
+ * Delete the existing interface before overwriting it
+ * in case we missed the BRCMF_E_IF_DEL event.
+ */
+ if (ifp) {
+ if (ifidx) {
+ brcmf_err("ERROR: netdev:%s already exists\n",
+ ifp->ndev->name);
+ netif_stop_queue(ifp->ndev);
+ brcmf_net_detach(ifp->ndev);
+ drvr->iflist[bsscfgidx] = NULL;
+ } else {
+ brcmf_dbg(INFO, "netdev:%s ignore IF event\n",
+ ifp->ndev->name);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ if (!drvr->settings->p2p_enable && is_p2pdev) {
+ /* this is P2P_DEVICE interface */
+ brcmf_dbg(INFO, "allocate non-netdev interface\n");
+ ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
+ if (!ifp)
+ return ERR_PTR(-ENOMEM);
+ } else {
+ brcmf_dbg(INFO, "allocate netdev interface\n");
+ /* Allocate netdev, including space for private structure */
+ ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name,
+ NET_NAME_UNKNOWN, ether_setup);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ ndev->destructor = brcmf_cfg80211_free_netdev;
+ ifp = netdev_priv(ndev);
+ ifp->ndev = ndev;
+ /* store mapping ifidx to bsscfgidx */
+ if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID)
+ drvr->if2bss[ifidx] = bsscfgidx;
+ }
+
+ ifp->drvr = drvr;
+ drvr->iflist[bsscfgidx] = ifp;
+ ifp->ifidx = ifidx;
+ ifp->bsscfgidx = bsscfgidx;
+
+ 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);
+
+ brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n",
+ current->pid, name, ifp->mac_addr);
+
+ return ifp;
+}
+
+static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx)
+{
+ struct brcmf_if *ifp;
+
+ ifp = drvr->iflist[bsscfgidx];
+ drvr->iflist[bsscfgidx] = NULL;
+ if (!ifp) {
+ brcmf_err("Null interface, bsscfgidx=%d\n", bsscfgidx);
+ return;
+ }
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx,
+ ifp->ifidx);
+ if (drvr->if2bss[ifp->ifidx] == bsscfgidx)
+ drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID;
+ if (ifp->ndev) {
+ if (bsscfgidx == 0) {
+ if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ rtnl_lock();
+ brcmf_netdev_stop(ifp->ndev);
+ rtnl_unlock();
+ }
+ } else {
+ netif_stop_queue(ifp->ndev);
+ }
+
+ if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ cancel_work_sync(&ifp->setmacaddr_work);
+ cancel_work_sync(&ifp->multicast_work);
+ }
+ brcmf_net_detach(ifp->ndev);
+ } else {
+ /* Only p2p device interfaces which get dynamically created
+ * end up here. In this case the p2p module should be informed
+ * about the removal of the interface within the firmware. If
+ * not then p2p commands towards the firmware will cause some
+ * serious troublesome side effects. The p2p module will clean
+ * up the ifp if needed.
+ */
+ brcmf_p2p_ifp_removed(ifp);
+ kfree(ifp);
+ }
+}
+
+void brcmf_remove_interface(struct brcmf_if *ifp)
+{
+ if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp))
+ return;
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx,
+ ifp->ifidx);
+ brcmf_fws_del_interface(ifp);
+ brcmf_del_if(ifp->drvr, ifp->bsscfgidx);
+}
+
+int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
+{
+ int ifidx;
+ int bsscfgidx;
+ bool available;
+ int highest;
+
+ available = false;
+ bsscfgidx = 2;
+ highest = 2;
+ for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
+ if (drvr->iflist[ifidx]) {
+ if (drvr->iflist[ifidx]->bsscfgidx == bsscfgidx)
+ bsscfgidx = highest + 1;
+ else if (drvr->iflist[ifidx]->bsscfgidx > highest)
+ highest = drvr->iflist[ifidx]->bsscfgidx;
+ } else {
+ available = true;
+ }
+ }
+
+ return available ? bsscfgidx : -ENOMEM;
+}
+
+#ifdef CONFIG_INET
+#define ARPOL_MAX_ENTRIES 8
+static int brcmf_inetaddr_changed(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
+ inetaddr_notifier);
+ struct in_ifaddr *ifa = data;
+ struct net_device *ndev = ifa->ifa_dev->dev;
+ struct brcmf_if *ifp;
+ int idx, i, ret;
+ u32 val;
+ __be32 addr_table[ARPOL_MAX_ENTRIES] = {0};
+
+ /* Find out if the notification is meant for us */
+ for (idx = 0; idx < BRCMF_MAX_IFS; idx++) {
+ ifp = drvr->iflist[idx];
+ if (ifp && ifp->ndev == ndev)
+ break;
+ if (idx == BRCMF_MAX_IFS - 1)
+ return NOTIFY_DONE;
+ }
+
+ /* check if arp offload is supported */
+ ret = brcmf_fil_iovar_int_get(ifp, "arpoe", &val);
+ if (ret)
+ return NOTIFY_OK;
+
+ /* old version only support primary index */
+ ret = brcmf_fil_iovar_int_get(ifp, "arp_version", &val);
+ if (ret)
+ val = 1;
+ if (val == 1)
+ ifp = drvr->iflist[0];
+
+ /* retrieve the table from firmware */
+ ret = brcmf_fil_iovar_data_get(ifp, "arp_hostip", addr_table,
+ sizeof(addr_table));
+ if (ret) {
+ brcmf_err("fail to get arp ip table err:%d\n", ret);
+ return NOTIFY_OK;
+ }
+
+ for (i = 0; i < ARPOL_MAX_ENTRIES; i++)
+ if (ifa->ifa_address == addr_table[i])
+ break;
+
+ switch (action) {
+ case NETDEV_UP:
+ if (i == ARPOL_MAX_ENTRIES) {
+ brcmf_dbg(TRACE, "add %pI4 to arp table\n",
+ &ifa->ifa_address);
+ /* set it directly */
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
+ &ifa->ifa_address, sizeof(ifa->ifa_address));
+ if (ret)
+ brcmf_err("add arp ip err %d\n", ret);
+ }
+ break;
+ case NETDEV_DOWN:
+ if (i < ARPOL_MAX_ENTRIES) {
+ addr_table[i] = 0;
+ brcmf_dbg(TRACE, "remove %pI4 from arp table\n",
+ &ifa->ifa_address);
+ /* clear the table in firmware */
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear",
+ NULL, 0);
+ if (ret) {
+ brcmf_err("fail to clear arp ip table err:%d\n",
+ ret);
+ return NOTIFY_OK;
+ }
+ for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
+ if (addr_table[i] != 0) {
+ brcmf_fil_iovar_data_set(ifp,
+ "arp_hostip", &addr_table[i],
+ sizeof(addr_table[i]));
+ if (ret)
+ brcmf_err("add arp ip err %d\n",
+ ret);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+#endif
+
+int brcmf_attach(struct device *dev)
+{
+ struct brcmf_pub *drvr = NULL;
+ int ret = 0;
+ int i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Allocate primary brcmf_info */
+ drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC);
+ if (!drvr)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++)
+ drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
+
+ mutex_init(&drvr->proto_block);
+
+ /* Link to bus module */
+ drvr->hdrlen = 0;
+ drvr->bus_if = dev_get_drvdata(dev);
+ drvr->bus_if->drvr = drvr;
+
+ /* Initialize device specific settings */
+ if (brcmf_mp_device_attach(drvr))
+ goto fail;
+
+ /* attach debug facilities */
+ brcmf_debug_attach(drvr);
+
+ /* Attach and link in the protocol */
+ ret = brcmf_proto_attach(drvr);
+ if (ret != 0) {
+ brcmf_err("brcmf_prot_attach failed\n");
+ goto fail;
+ }
+
+ /* attach firmware event handler */
+ brcmf_fweh_attach(drvr);
+
+ return ret;
+
+fail:
+ brcmf_detach(dev);
+
+ return ret;
+}
+
+static int brcmf_revinfo_read(struct seq_file *s, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(s->private);
+ struct brcmf_rev_info *ri = &bus_if->drvr->revinfo;
+ char drev[BRCMU_DOTREV_LEN];
+ char brev[BRCMU_BOARDREV_LEN];
+
+ seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid);
+ seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid);
+ seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev));
+ seq_printf(s, "chipnum: %u (%x)\n", ri->chipnum, ri->chipnum);
+ seq_printf(s, "chiprev: %u\n", ri->chiprev);
+ seq_printf(s, "chippkg: %u\n", ri->chippkg);
+ seq_printf(s, "corerev: %u\n", ri->corerev);
+ seq_printf(s, "boardid: 0x%04x\n", ri->boardid);
+ seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor);
+ seq_printf(s, "boardrev: %s\n", brcmu_boardrev_str(ri->boardrev, brev));
+ seq_printf(s, "driverrev: %s\n", brcmu_dotrev_str(ri->driverrev, drev));
+ seq_printf(s, "ucoderev: %u\n", ri->ucoderev);
+ seq_printf(s, "bus: %u\n", ri->bus);
+ seq_printf(s, "phytype: %u\n", ri->phytype);
+ seq_printf(s, "phyrev: %u\n", ri->phyrev);
+ seq_printf(s, "anarev: %u\n", ri->anarev);
+ seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
+
+ return 0;
+}
+
+int brcmf_bus_start(struct device *dev)
+{
+ int ret = -1;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_if *ifp;
+ struct brcmf_if *p2p_ifp;
+
+ brcmf_dbg(TRACE, "\n");
+
+ /* add primary networking interface */
+ ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
+ if (IS_ERR(ifp))
+ return PTR_ERR(ifp);
+
+ p2p_ifp = NULL;
+
+ /* signal bus ready */
+ brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
+
+ /* Bus is ready, do any initialization */
+ ret = brcmf_c_preinit_dcmds(ifp);
+ if (ret < 0)
+ goto fail;
+
+ brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
+
+ /* assure we have chipid before feature attach */
+ if (!bus_if->chip) {
+ bus_if->chip = drvr->revinfo.chipnum;
+ bus_if->chiprev = drvr->revinfo.chiprev;
+ brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n",
+ bus_if->chip, bus_if->chip, bus_if->chiprev);
+ }
+ brcmf_feat_attach(drvr);
+
+ ret = brcmf_fws_init(drvr);
+ if (ret < 0)
+ goto fail;
+
+ brcmf_fws_add_interface(ifp);
+
+ drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev,
+ drvr->settings->p2p_enable);
+ if (drvr->config == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = brcmf_net_attach(ifp, false);
+
+ if ((!ret) && (drvr->settings->p2p_enable)) {
+ p2p_ifp = drvr->iflist[1];
+ if (p2p_ifp)
+ ret = brcmf_net_p2p_attach(p2p_ifp);
+ }
+
+ if (ret)
+ goto fail;
+
+#ifdef CONFIG_INET
+ drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
+ ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
+#endif
+
+fail:
+ if (ret < 0) {
+ brcmf_err("failed: %d\n", ret);
+ if (drvr->config) {
+ brcmf_cfg80211_detach(drvr->config);
+ drvr->config = NULL;
+ }
+ if (drvr->fws) {
+ brcmf_fws_del_interface(ifp);
+ brcmf_fws_deinit(drvr);
+ }
+ if (ifp)
+ brcmf_net_detach(ifp->ndev);
+ if (p2p_ifp)
+ brcmf_net_detach(p2p_ifp->ndev);
+ drvr->iflist[0] = NULL;
+ drvr->iflist[1] = NULL;
+ if (brcmf_ignoring_probe_fail(drvr))
+ ret = 0;
+ return ret;
+ }
+ return 0;
+}
+
+void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ if (drvr) {
+ drvr->hdrlen += len;
+ }
+}
+
+static void brcmf_bus_detach(struct brcmf_pub *drvr)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (drvr) {
+ /* Stop the bus module */
+ brcmf_bus_stop(drvr->bus_if);
+ }
+}
+
+void brcmf_dev_reset(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ if (drvr == NULL)
+ return;
+
+ if (drvr->iflist[0])
+ brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1);
+}
+
+void brcmf_detach(struct device *dev)
+{
+ s32 i;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (drvr == NULL)
+ return;
+
+#ifdef CONFIG_INET
+ unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
+#endif
+
+ /* stop firmware event handling */
+ brcmf_fweh_detach(drvr);
+ if (drvr->config)
+ brcmf_p2p_detach(&drvr->config->p2p);
+
+ brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
+
+ /* make sure primary interface removed last */
+ for (i = BRCMF_MAX_IFS-1; i > -1; i--)
+ brcmf_remove_interface(drvr->iflist[i]);
+
+ brcmf_cfg80211_detach(drvr->config);
+
+ brcmf_fws_deinit(drvr);
+
+ brcmf_bus_detach(drvr);
+
+ brcmf_proto_detach(drvr);
+
+ brcmf_mp_device_detach(drvr);
+
+ brcmf_debug_detach(drvr);
+ bus_if->drvr = NULL;
+ kfree(drvr);
+}
+
+s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_if *ifp = bus_if->drvr->iflist[0];
+
+ return brcmf_fil_iovar_data_set(ifp, name, data, len);
+}
+
+static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp)
+{
+ return atomic_read(&ifp->pend_8021x_cnt);
+}
+
+int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
+{
+ int err;
+
+ err = wait_event_timeout(ifp->pend_8021x_wait,
+ !brcmf_get_pend_8021x_cnt(ifp),
+ MAX_WAIT_FOR_8021X_TX);
+
+ WARN_ON(!err);
+
+ return !err;
+}
+
+void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
+{
+ struct brcmf_pub *drvr = bus->drvr;
+ struct net_device *ndev;
+ int ifidx;
+
+ brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state);
+ bus->state = state;
+
+ if (state == BRCMF_BUS_UP) {
+ for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
+ if ((drvr->iflist[ifidx]) &&
+ (drvr->iflist[ifidx]->ndev)) {
+ ndev = drvr->iflist[ifidx]->ndev;
+ if (netif_queue_stopped(ndev))
+ netif_wake_queue(ndev);
+ }
+ }
+ }
+}
+
+static void brcmf_driver_register(struct work_struct *work)
+{
+#ifdef CONFIG_BRCMFMAC_SDIO
+ brcmf_sdio_register();
+#endif
+#ifdef CONFIG_BRCMFMAC_USB
+ brcmf_usb_register();
+#endif
+#ifdef CONFIG_BRCMFMAC_PCIE
+ brcmf_pcie_register();
+#endif
+}
+static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
+
+static int __init brcmfmac_module_init(void)
+{
+ brcmf_debugfs_init();
+#ifdef CONFIG_BRCMFMAC_SDIO
+ brcmf_sdio_init();
+#endif
+ if (!schedule_work(&brcmf_driver_work))
+ return -EBUSY;
+
+ return 0;
+}
+
+static void __exit brcmfmac_module_exit(void)
+{
+ cancel_work_sync(&brcmf_driver_work);
+
+#ifdef CONFIG_BRCMFMAC_SDIO
+ brcmf_sdio_exit();
+#endif
+#ifdef CONFIG_BRCMFMAC_USB
+ brcmf_usb_exit();
+#endif
+#ifdef CONFIG_BRCMFMAC_PCIE
+ brcmf_pcie_exit();
+#endif
+ brcmf_debugfs_exit();
+}
+
+module_init(brcmfmac_module_init);
+module_exit(brcmfmac_module_exit);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
new file mode 100644
index 000000000000..8f39435f976f
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/****************
+ * Common types *
+ */
+
+#ifndef BRCMFMAC_CORE_H
+#define BRCMFMAC_CORE_H
+
+#include <net/cfg80211.h>
+#include "fweh.h"
+
+#define TOE_TX_CSUM_OL 0x00000001
+#define TOE_RX_CSUM_OL 0x00000002
+
+/* For supporting multiple interfaces */
+#define BRCMF_MAX_IFS 16
+
+/* Small, medium and maximum buffer size for dcmd
+ */
+#define BRCMF_DCMD_SMLEN 256
+#define BRCMF_DCMD_MEDLEN 1536
+#define BRCMF_DCMD_MAXLEN 8192
+
+/* IOCTL from host to device are limited in lenght. A device can only handle
+ * ethernet frame size. This limitation is to be applied by protocol layer.
+ */
+#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN)
+
+#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256
+
+/* Length of firmware version string stored for
+ * ethtool driver info which uses 32 bytes as well.
+ */
+#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32
+
+/**
+ * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
+ *
+ * @pktslots: dynamic allocated array for ordering AMPDU packets.
+ * @flow_id: AMPDU flow identifier.
+ * @cur_idx: last AMPDU index from firmware.
+ * @exp_idx: expected next AMPDU index.
+ * @max_idx: maximum amount of packets per AMPDU.
+ * @pend_pkts: number of packets currently in @pktslots.
+ */
+struct brcmf_ampdu_rx_reorder {
+ struct sk_buff **pktslots;
+ u8 flow_id;
+ u8 cur_idx;
+ u8 exp_idx;
+ u8 max_idx;
+ u8 pend_pkts;
+};
+
+/* Forward decls for struct brcmf_pub (see below) */
+struct brcmf_proto; /* device communication protocol info */
+struct brcmf_fws_info; /* firmware signalling info */
+struct brcmf_mp_device; /* module paramateres, device specific */
+
+/*
+ * struct brcmf_rev_info
+ *
+ * The result field stores the error code of the
+ * revision info request from firmware. For the
+ * other fields see struct brcmf_rev_info_le in
+ * fwil_types.h
+ */
+struct brcmf_rev_info {
+ int result;
+ u32 vendorid;
+ u32 deviceid;
+ u32 radiorev;
+ u32 chiprev;
+ u32 corerev;
+ u32 boardid;
+ u32 boardvendor;
+ u32 boardrev;
+ u32 driverrev;
+ u32 ucoderev;
+ u32 bus;
+ u32 chipnum;
+ u32 phytype;
+ u32 phyrev;
+ u32 anarev;
+ u32 chippkg;
+ u32 nvramrev;
+};
+
+/* Common structure for module and instance linkage */
+struct brcmf_pub {
+ /* Linkage ponters */
+ struct brcmf_bus *bus_if;
+ struct brcmf_proto *proto;
+ struct brcmf_cfg80211_info *config;
+
+ /* Internal brcmf items */
+ uint hdrlen; /* Total BRCMF header length (proto + bus) */
+ uint rxsz; /* Rx buffer size bus module should use */
+
+ /* Dongle media info */
+ char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN];
+ u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */
+
+ /* Multicast data packets sent to dongle */
+ unsigned long tx_multicast;
+
+ struct mac_address addresses[BRCMF_MAX_IFS];
+
+ struct brcmf_if *iflist[BRCMF_MAX_IFS];
+ s32 if2bss[BRCMF_MAX_IFS];
+
+ struct mutex proto_block;
+ unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
+
+ struct brcmf_fweh_info fweh;
+
+ struct brcmf_fws_info *fws;
+
+ struct brcmf_ampdu_rx_reorder
+ *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
+
+ u32 feat_flags;
+ u32 chip_quirks;
+
+ struct brcmf_rev_info revinfo;
+#ifdef DEBUG
+ struct dentry *dbgfs_dir;
+#endif
+
+ struct notifier_block inetaddr_notifier;
+ struct brcmf_mp_device *settings;
+};
+
+/* forward declarations */
+struct brcmf_cfg80211_vif;
+struct brcmf_fws_mac_descriptor;
+
+/**
+ * enum brcmf_netif_stop_reason - reason for stopping netif queue.
+ *
+ * @BRCMF_NETIF_STOP_REASON_FWS_FC:
+ * netif stopped due to firmware signalling flow control.
+ * @BRCMF_NETIF_STOP_REASON_FLOW:
+ * netif stopped due to flowring full.
+ * @BRCMF_NETIF_STOP_REASON_DISCONNECTED:
+ * netif stopped due to not being connected (STA mode).
+ */
+enum brcmf_netif_stop_reason {
+ BRCMF_NETIF_STOP_REASON_FWS_FC = BIT(0),
+ BRCMF_NETIF_STOP_REASON_FLOW = BIT(1),
+ BRCMF_NETIF_STOP_REASON_DISCONNECTED = BIT(2)
+};
+
+/**
+ * struct brcmf_if - interface control information.
+ *
+ * @drvr: points to device related information.
+ * @vif: points to cfg80211 specific interface information.
+ * @ndev: associated network device.
+ * @stats: interface specific network statistics.
+ * @setmacaddr_work: worker object for setting mac address.
+ * @multicast_work: worker object for multicast provisioning.
+ * @fws_desc: interface specific firmware-signalling descriptor.
+ * @ifidx: interface index in device firmware.
+ * @bsscfgidx: 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.
+ */
+struct brcmf_if {
+ struct brcmf_pub *drvr;
+ struct brcmf_cfg80211_vif *vif;
+ struct net_device *ndev;
+ struct net_device_stats stats;
+ struct work_struct setmacaddr_work;
+ struct work_struct multicast_work;
+ struct brcmf_fws_mac_descriptor *fws_desc;
+ int ifidx;
+ s32 bsscfgidx;
+ 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;
+};
+
+struct brcmf_skb_reorder_data {
+ u8 *reorder;
+};
+
+int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
+
+/* Return pointer to interface name */
+char *brcmf_ifname(struct brcmf_if *ifp);
+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
+int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
+ bool is_p2pdev, char *name, u8 *mac_addr);
+void brcmf_remove_interface(struct brcmf_if *ifp);
+int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
+void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state);
+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
+void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+
+#endif /* BRCMFMAC_CORE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
new file mode 100644
index 000000000000..e64557c35553
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/debugfs.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/devcoredump.h>
+
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include "core.h"
+#include "bus.h"
+#include "fweh.h"
+#include "debug.h"
+
+static struct dentry *root_folder;
+
+static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
+ size_t len)
+{
+ void *dump;
+ size_t ramsize;
+
+ ramsize = brcmf_bus_get_ramsize(bus);
+ if (ramsize) {
+ dump = vzalloc(len + ramsize);
+ if (!dump)
+ return -ENOMEM;
+ memcpy(dump, data, len);
+ brcmf_bus_get_memdump(bus, dump + len, ramsize);
+ dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
+ }
+ return 0;
+}
+
+static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data)
+{
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ return brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
+ evtmsg->datalen);
+}
+
+void brcmf_debugfs_init(void)
+{
+ root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (IS_ERR(root_folder))
+ root_folder = NULL;
+}
+
+void brcmf_debugfs_exit(void)
+{
+ if (!root_folder)
+ return;
+
+ debugfs_remove_recursive(root_folder);
+ root_folder = NULL;
+}
+
+int brcmf_debug_attach(struct brcmf_pub *drvr)
+{
+ struct device *dev = drvr->bus_if->dev;
+
+ if (!root_folder)
+ return -ENODEV;
+
+ drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
+ if (IS_ERR(drvr->dbgfs_dir))
+ return PTR_ERR(drvr->dbgfs_dir);
+
+
+ return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
+ brcmf_debug_psm_watchdog_notify);
+}
+
+void brcmf_debug_detach(struct brcmf_pub *drvr)
+{
+ brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);
+
+ if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
+ debugfs_remove_recursive(drvr->dbgfs_dir);
+}
+
+struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
+{
+ return drvr->dbgfs_dir;
+}
+
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data))
+{
+ struct dentry *e;
+
+ e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn,
+ drvr->dbgfs_dir, read_fn);
+ return PTR_ERR_OR_ZERO(e);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
new file mode 100644
index 000000000000..6687812770cc
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_DEBUG_H
+#define BRCMFMAC_DEBUG_H
+
+#include <linux/net.h> /* net_ratelimit() */
+
+/* message levels */
+#define BRCMF_TRACE_VAL 0x00000002
+#define BRCMF_INFO_VAL 0x00000004
+#define BRCMF_DATA_VAL 0x00000008
+#define BRCMF_CTL_VAL 0x00000010
+#define BRCMF_TIMER_VAL 0x00000020
+#define BRCMF_HDRS_VAL 0x00000040
+#define BRCMF_BYTES_VAL 0x00000080
+#define BRCMF_INTR_VAL 0x00000100
+#define BRCMF_GLOM_VAL 0x00000200
+#define BRCMF_EVENT_VAL 0x00000400
+#define BRCMF_BTA_VAL 0x00000800
+#define BRCMF_FIL_VAL 0x00001000
+#define BRCMF_USB_VAL 0x00002000
+#define BRCMF_SCAN_VAL 0x00004000
+#define BRCMF_CONN_VAL 0x00008000
+#define BRCMF_BCDC_VAL 0x00010000
+#define BRCMF_SDIO_VAL 0x00020000
+#define BRCMF_MSGBUF_VAL 0x00040000
+#define BRCMF_PCIE_VAL 0x00080000
+#define BRCMF_FWCON_VAL 0x00100000
+
+/* set default print format */
+#undef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/* Macro for error messages. net_ratelimit() is used when driver
+ * debugging is not selected. When debugging the driver error
+ * messages are as important as other tracing or even more so.
+ */
+#ifndef CONFIG_BRCM_TRACING
+#ifdef CONFIG_BRCMDBG
+#define brcmf_err(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__)
+#else
+#define brcmf_err(fmt, ...) \
+ do { \
+ if (net_ratelimit()) \
+ pr_err("%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+#endif
+#else
+__printf(2, 3)
+void __brcmf_err(const char *func, const char *fmt, ...);
+#define brcmf_err(fmt, ...) \
+ __brcmf_err(__func__, fmt, ##__VA_ARGS__)
+#endif
+
+#if defined(DEBUG) || defined(CONFIG_BRCM_TRACING)
+__printf(3, 4)
+void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...);
+#define brcmf_dbg(level, fmt, ...) \
+do { \
+ __brcmf_dbg(BRCMF_##level##_VAL, __func__, \
+ fmt, ##__VA_ARGS__); \
+} while (0)
+#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL)
+#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL)
+#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL)
+#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL)
+#define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL)
+#define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL)
+#define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL)
+#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL)
+
+#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
+
+#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
+
+#define BRCMF_DATA_ON() 0
+#define BRCMF_CTL_ON() 0
+#define BRCMF_HDRS_ON() 0
+#define BRCMF_BYTES_ON() 0
+#define BRCMF_GLOM_ON() 0
+#define BRCMF_EVENT_ON() 0
+#define BRCMF_FIL_ON() 0
+#define BRCMF_FWCON_ON() 0
+
+#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
+
+#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \
+do { \
+ trace_brcmf_hexdump((void *)data, len); \
+ if (test) \
+ brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \
+} while (0)
+
+extern int brcmf_msg_level;
+
+struct brcmf_pub;
+#ifdef DEBUG
+void brcmf_debugfs_init(void);
+void brcmf_debugfs_exit(void);
+int brcmf_debug_attach(struct brcmf_pub *drvr);
+void brcmf_debug_detach(struct brcmf_pub *drvr);
+struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data));
+#else
+static inline void brcmf_debugfs_init(void)
+{
+}
+static inline void brcmf_debugfs_exit(void)
+{
+}
+static inline int brcmf_debug_attach(struct brcmf_pub *drvr)
+{
+ return 0;
+}
+static inline void brcmf_debug_detach(struct brcmf_pub *drvr)
+{
+}
+static inline
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data))
+{
+ return 0;
+}
+#endif
+
+#endif /* BRCMFMAC_DEBUG_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
new file mode 100644
index 000000000000..1ffa95f1b8d2
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+
+#include <brcm_hw_ids.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "feature.h"
+#include "common.h"
+
+
+/*
+ * expand feature list to array of feature strings.
+ */
+#define BRCMF_FEAT_DEF(_f) \
+ #_f,
+static const char *brcmf_feat_names[] = {
+ BRCMF_FEAT_LIST
+};
+#undef BRCMF_FEAT_DEF
+
+struct brcmf_feat_fwcap {
+ enum brcmf_feat_id feature;
+ const char * const fwcap_id;
+};
+
+static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
+ { BRCMF_FEAT_MBSS, "mbss" },
+ { BRCMF_FEAT_MCHAN, "mchan" },
+ { BRCMF_FEAT_P2P, "p2p" },
+};
+
+#ifdef DEBUG
+/*
+ * expand quirk list to array of quirk strings.
+ */
+#define BRCMF_QUIRK_DEF(_q) \
+ #_q,
+static const char * const brcmf_quirk_names[] = {
+ BRCMF_QUIRK_LIST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_debugfs_read() - expose feature info to debugfs.
+ *
+ * @seq: sequence for debugfs entry.
+ * @data: raw data pointer.
+ */
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ u32 feats = bus_if->drvr->feat_flags;
+ u32 quirks = bus_if->drvr->chip_quirks;
+ int id;
+
+ seq_printf(seq, "Features: %08x\n", feats);
+ for (id = 0; id < BRCMF_FEAT_LAST; id++)
+ if (feats & BIT(id))
+ seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
+ seq_printf(seq, "\nQuirks: %08x\n", quirks);
+ for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
+ if (quirks & BIT(id))
+ seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
+ return 0;
+}
+#else
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+ return 0;
+}
+#endif /* DEBUG */
+
+/**
+ * brcmf_feat_iovar_int_get() - determine feature through iovar query.
+ *
+ * @ifp: interface to query.
+ * @id: feature id.
+ * @name: iovar name.
+ */
+static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
+ enum brcmf_feat_id id, char *name)
+{
+ u32 data;
+ int err;
+
+ err = brcmf_fil_iovar_int_get(ifp, name, &data);
+ if (err == 0) {
+ brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
+ ifp->drvr->feat_flags |= BIT(id);
+ } else {
+ brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+ brcmf_feat_names[id], err);
+ }
+}
+
+static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
+{
+ char caps[256];
+ enum brcmf_feat_id id;
+ int i;
+
+ brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
+ brcmf_dbg(INFO, "[ %s]\n", caps);
+
+ for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
+ if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
+ id = brcmf_fwcap_map[i].feature;
+ brcmf_dbg(INFO, "enabling feature: %s\n",
+ brcmf_feat_names[id]);
+ ifp->drvr->feat_flags |= BIT(id);
+ }
+ }
+}
+
+void brcmf_feat_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+ struct brcmf_pno_macaddr_le pfn_mac;
+ s32 err;
+
+ brcmf_feat_firmware_capabilities(ifp);
+
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
+ if (drvr->bus_if->wowl_supported)
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
+ /* MBSS does not work for 43362 */
+ if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID)
+ ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
+
+ pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
+ err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
+ sizeof(pfn_mac));
+ if (!err)
+ ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
+
+ if (drvr->settings->feature_disable) {
+ brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
+ ifp->drvr->feat_flags,
+ drvr->settings->feature_disable);
+ ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
+ }
+
+ /* set chip related quirks */
+ switch (drvr->bus_if->chip) {
+ case BRCM_CC_43236_CHIP_ID:
+ drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
+ break;
+ case BRCM_CC_4329_CHIP_ID:
+ drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
+ break;
+ default:
+ /* no quirks */
+ break;
+ }
+
+ brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
+}
+
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
+{
+ return (ifp->drvr->feat_flags & BIT(id));
+}
+
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+ enum brcmf_feat_quirk quirk)
+{
+ return (ifp->drvr->chip_quirks & BIT(quirk));
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
new file mode 100644
index 000000000000..2e2479d41337
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _BRCMF_FEATURE_H
+#define _BRCMF_FEATURE_H
+
+/*
+ * Features:
+ *
+ * MBSS: multiple BSSID support (eg. guest network in AP mode).
+ * MCHAN: multi-channel for concurrent P2P.
+ * PNO: preferred network offload.
+ * WOWL: Wake-On-WLAN.
+ * P2P: peer-to-peer
+ * RSDB: Real Simultaneous Dual Band
+ * TDLS: Tunneled Direct Link Setup
+ * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan.
+ */
+#define BRCMF_FEAT_LIST \
+ BRCMF_FEAT_DEF(MBSS) \
+ BRCMF_FEAT_DEF(MCHAN) \
+ BRCMF_FEAT_DEF(PNO) \
+ BRCMF_FEAT_DEF(WOWL) \
+ BRCMF_FEAT_DEF(P2P) \
+ BRCMF_FEAT_DEF(RSDB) \
+ BRCMF_FEAT_DEF(TDLS) \
+ BRCMF_FEAT_DEF(SCAN_RANDOM_MAC)
+
+/*
+ * Quirks:
+ *
+ * AUTO_AUTH: workaround needed for automatic authentication type.
+ * NEED_MPC: driver needs to disable MPC during scanning operation.
+ */
+#define BRCMF_QUIRK_LIST \
+ BRCMF_QUIRK_DEF(AUTO_AUTH) \
+ BRCMF_QUIRK_DEF(NEED_MPC)
+
+#define BRCMF_FEAT_DEF(_f) \
+ BRCMF_FEAT_ ## _f,
+/*
+ * expand feature list to enumeration.
+ */
+enum brcmf_feat_id {
+ BRCMF_FEAT_LIST
+ BRCMF_FEAT_LAST
+};
+#undef BRCMF_FEAT_DEF
+
+#define BRCMF_QUIRK_DEF(_q) \
+ BRCMF_FEAT_QUIRK_ ## _q,
+/*
+ * expand quirk list to enumeration.
+ */
+enum brcmf_feat_quirk {
+ BRCMF_QUIRK_LIST
+ BRCMF_FEAT_QUIRK_LAST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_attach() - determine features and quirks.
+ *
+ * @drvr: driver instance.
+ */
+void brcmf_feat_attach(struct brcmf_pub *drvr);
+
+/**
+ * brcmf_feat_is_enabled() - query feature.
+ *
+ * @ifp: interface instance.
+ * @id: feature id to check.
+ *
+ * Return: true is feature is enabled; otherwise false.
+ */
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id);
+
+/**
+ * brcmf_feat_is_quirk_enabled() - query chip quirk.
+ *
+ * @ifp: interface instance.
+ * @quirk: quirk id to check.
+ *
+ * Return: true is quirk is enabled; otherwise false.
+ */
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+ enum brcmf_feat_quirk quirk);
+
+#endif /* _BRCMF_FEATURE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
new file mode 100644
index 000000000000..1365c12b78fc
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/bcm47xx_nvram.h>
+
+#include "debug.h"
+#include "firmware.h"
+#include "core.h"
+#include "common.h"
+
+#define BRCMF_FW_MAX_NVRAM_SIZE 64000
+#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
+#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
+
+enum nvram_parser_state {
+ IDLE,
+ KEY,
+ VALUE,
+ COMMENT,
+ END
+};
+
+/**
+ * struct nvram_parser - internal info for parser.
+ *
+ * @state: current parser state.
+ * @data: input buffer being parsed.
+ * @nvram: output buffer with parse result.
+ * @nvram_len: lenght of parse result.
+ * @line: current line.
+ * @column: current column in line.
+ * @pos: byte offset in input buffer.
+ * @entry: start position of key,value entry.
+ * @multi_dev_v1: detect pcie multi device v1 (compressed).
+ * @multi_dev_v2: detect pcie multi device v2.
+ */
+struct nvram_parser {
+ enum nvram_parser_state state;
+ const u8 *data;
+ u8 *nvram;
+ u32 nvram_len;
+ u32 line;
+ u32 column;
+ u32 pos;
+ u32 entry;
+ bool multi_dev_v1;
+ bool multi_dev_v2;
+};
+
+/**
+ * is_nvram_char() - check if char is a valid one for NVRAM entry
+ *
+ * It accepts all printable ASCII chars except for '#' which opens a comment.
+ * Please note that ' ' (space) while accepted is not a valid key name char.
+ */
+static bool is_nvram_char(char c)
+{
+ /* comment marker excluded */
+ if (c == '#')
+ return false;
+
+ /* key and value may have any other readable character */
+ return (c >= 0x20 && c < 0x7f);
+}
+
+static bool is_whitespace(char c)
+{
+ return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
+{
+ char c;
+
+ c = nvp->data[nvp->pos];
+ if (c == '\n')
+ return COMMENT;
+ if (is_whitespace(c))
+ goto proceed;
+ if (c == '#')
+ return COMMENT;
+ if (is_nvram_char(c)) {
+ nvp->entry = nvp->pos;
+ return KEY;
+ }
+ brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
+ nvp->line, nvp->column);
+proceed:
+ nvp->column++;
+ nvp->pos++;
+ return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
+{
+ enum nvram_parser_state st = nvp->state;
+ char c;
+
+ c = nvp->data[nvp->pos];
+ if (c == '=') {
+ /* ignore RAW1 by treating as comment */
+ if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
+ st = COMMENT;
+ else
+ st = VALUE;
+ if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
+ nvp->multi_dev_v1 = true;
+ if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
+ nvp->multi_dev_v2 = true;
+ } else if (!is_nvram_char(c) || c == ' ') {
+ brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+ nvp->line, nvp->column);
+ return COMMENT;
+ }
+
+ nvp->column++;
+ nvp->pos++;
+ return st;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_value(struct nvram_parser *nvp)
+{
+ char c;
+ char *skv;
+ char *ekv;
+ u32 cplen;
+
+ c = nvp->data[nvp->pos];
+ if (!is_nvram_char(c)) {
+ /* key,value pair complete */
+ ekv = (u8 *)&nvp->data[nvp->pos];
+ skv = (u8 *)&nvp->data[nvp->entry];
+ cplen = ekv - skv;
+ if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
+ return END;
+ /* copy to output buffer */
+ memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
+ nvp->nvram_len += cplen;
+ nvp->nvram[nvp->nvram_len] = '\0';
+ nvp->nvram_len++;
+ return IDLE;
+ }
+ nvp->pos++;
+ nvp->column++;
+ return VALUE;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_comment(struct nvram_parser *nvp)
+{
+ char *eoc, *sol;
+
+ sol = (char *)&nvp->data[nvp->pos];
+ eoc = strchr(sol, '\n');
+ if (!eoc) {
+ eoc = strchr(sol, '\0');
+ if (!eoc)
+ return END;
+ }
+
+ /* eat all moving to next line */
+ nvp->line++;
+ nvp->column = 1;
+ nvp->pos += (eoc - sol) + 1;
+ return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
+{
+ /* final state */
+ return END;
+}
+
+static enum nvram_parser_state
+(*nv_parser_states[])(struct nvram_parser *nvp) = {
+ brcmf_nvram_handle_idle,
+ brcmf_nvram_handle_key,
+ brcmf_nvram_handle_value,
+ brcmf_nvram_handle_comment,
+ brcmf_nvram_handle_end
+};
+
+static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
+ const u8 *data, size_t data_len)
+{
+ size_t size;
+
+ memset(nvp, 0, sizeof(*nvp));
+ nvp->data = data;
+ /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
+ if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
+ size = BRCMF_FW_MAX_NVRAM_SIZE;
+ else
+ size = data_len;
+ /* Alloc for extra 0 byte + roundup by 4 + length field */
+ size += 1 + 3 + sizeof(u32);
+ nvp->nvram = kzalloc(size, GFP_KERNEL);
+ if (!nvp->nvram)
+ return -ENOMEM;
+
+ nvp->line = 1;
+ nvp->column = 1;
+ return 0;
+}
+
+/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
+ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
+ * which data is to be returned. v1 is the version where nvram is stored
+ * compressed and "devpath" maps to index for valid entries.
+ */
+static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
+ u16 bus_nr)
+{
+ /* Device path with a leading '=' key-value separator */
+ char pci_path[] = "=pci/?/?";
+ size_t pci_len;
+ char pcie_path[] = "=pcie/?/?";
+ size_t pcie_len;
+
+ u32 i, j;
+ bool found;
+ u8 *nvram;
+ u8 id;
+
+ nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
+ if (!nvram)
+ goto fail;
+
+ /* min length: devpath0=pcie/1/4/ + 0:x=y */
+ if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
+ goto fail;
+
+ /* First search for the devpathX and see if it is the configuration
+ * for domain_nr/bus_nr. Search complete nvp
+ */
+ snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
+ bus_nr);
+ pci_len = strlen(pci_path);
+ snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
+ bus_nr);
+ pcie_len = strlen(pcie_path);
+ found = false;
+ i = 0;
+ while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
+ /* Format: devpathX=pcie/Y/Z/
+ * Y = domain_nr, Z = bus_nr, X = virtual ID
+ */
+ if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
+ (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
+ !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
+ id = nvp->nvram[i + 7] - '0';
+ found = true;
+ break;
+ }
+ while (nvp->nvram[i] != 0)
+ i++;
+ i++;
+ }
+ if (!found)
+ goto fail;
+
+ /* Now copy all valid entries, release old nvram and assign new one */
+ i = 0;
+ j = 0;
+ while (i < nvp->nvram_len) {
+ if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
+ i += 2;
+ while (nvp->nvram[i] != 0) {
+ nvram[j] = nvp->nvram[i];
+ i++;
+ j++;
+ }
+ nvram[j] = 0;
+ j++;
+ }
+ while (nvp->nvram[i] != 0)
+ i++;
+ i++;
+ }
+ kfree(nvp->nvram);
+ nvp->nvram = nvram;
+ nvp->nvram_len = j;
+ return;
+
+fail:
+ kfree(nvram);
+ nvp->nvram_len = 0;
+}
+
+/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
+ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
+ * which data is to be returned. v2 is the version where nvram is stored
+ * uncompressed, all relevant valid entries are identified by
+ * pcie/domain_nr/bus_nr:
+ */
+static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
+ u16 bus_nr)
+{
+ char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN];
+ size_t len;
+ u32 i, j;
+ u8 *nvram;
+
+ nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
+ if (!nvram)
+ goto fail;
+
+ /* Copy all valid entries, release old nvram and assign new one.
+ * Valid entries are of type pcie/X/Y/ where X = domain_nr and
+ * Y = bus_nr.
+ */
+ snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr);
+ len = strlen(prefix);
+ i = 0;
+ j = 0;
+ while (i < nvp->nvram_len - len) {
+ if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
+ i += len;
+ while (nvp->nvram[i] != 0) {
+ nvram[j] = nvp->nvram[i];
+ i++;
+ j++;
+ }
+ nvram[j] = 0;
+ j++;
+ }
+ while (nvp->nvram[i] != 0)
+ i++;
+ i++;
+ }
+ kfree(nvp->nvram);
+ nvp->nvram = nvram;
+ nvp->nvram_len = j;
+ return;
+fail:
+ kfree(nvram);
+ nvp->nvram_len = 0;
+}
+
+/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
+ * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
+ * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+ * End of buffer is completed with token identifying length of buffer.
+ */
+static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
+ u32 *new_length, u16 domain_nr, u16 bus_nr)
+{
+ struct nvram_parser nvp;
+ u32 pad;
+ u32 token;
+ __le32 token_le;
+
+ if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
+ return NULL;
+
+ while (nvp.pos < data_len) {
+ nvp.state = nv_parser_states[nvp.state](&nvp);
+ if (nvp.state == END)
+ break;
+ }
+ if (nvp.multi_dev_v1)
+ brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
+ else if (nvp.multi_dev_v2)
+ brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
+
+ if (nvp.nvram_len == 0) {
+ kfree(nvp.nvram);
+ return NULL;
+ }
+
+ pad = nvp.nvram_len;
+ *new_length = roundup(nvp.nvram_len + 1, 4);
+ while (pad != *new_length) {
+ nvp.nvram[pad] = 0;
+ pad++;
+ }
+
+ token = *new_length / 4;
+ token = (~token << 16) | (token & 0x0000FFFF);
+ token_le = cpu_to_le32(token);
+
+ memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
+ *new_length += sizeof(token_le);
+
+ return nvp.nvram;
+}
+
+void brcmf_fw_nvram_free(void *nvram)
+{
+ kfree(nvram);
+}
+
+struct brcmf_fw {
+ struct device *dev;
+ u16 flags;
+ const struct firmware *code;
+ const char *nvram_name;
+ u16 domain_nr;
+ u16 bus_nr;
+ void (*done)(struct device *dev, const struct firmware *fw,
+ void *nvram_image, u32 nvram_len);
+};
+
+static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
+{
+ struct brcmf_fw *fwctx = ctx;
+ u32 nvram_length = 0;
+ void *nvram = NULL;
+ u8 *data = NULL;
+ size_t data_len;
+ bool raw_nvram;
+
+ brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+ if (fw && fw->data) {
+ data = (u8 *)fw->data;
+ data_len = fw->size;
+ raw_nvram = false;
+ } else {
+ data = bcm47xx_nvram_get_contents(&data_len);
+ if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ goto fail;
+ raw_nvram = true;
+ }
+
+ if (data)
+ nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length,
+ fwctx->domain_nr, fwctx->bus_nr);
+
+ if (raw_nvram)
+ bcm47xx_nvram_release_contents(data);
+ release_firmware(fw);
+ if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ goto fail;
+
+ fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
+ kfree(fwctx);
+ return;
+
+fail:
+ brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+ release_firmware(fwctx->code);
+ device_release_driver(fwctx->dev);
+ kfree(fwctx);
+}
+
+static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
+{
+ struct brcmf_fw *fwctx = ctx;
+ int ret;
+
+ brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+ if (!fw)
+ goto fail;
+
+ /* only requested code so done here */
+ if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
+ fwctx->done(fwctx->dev, fw, NULL, 0);
+ kfree(fwctx);
+ return;
+ }
+ fwctx->code = fw;
+ ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
+ fwctx->dev, GFP_KERNEL, fwctx,
+ brcmf_fw_request_nvram_done);
+
+ if (!ret)
+ return;
+
+ brcmf_fw_request_nvram_done(NULL, fwctx);
+ return;
+
+fail:
+ brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+ device_release_driver(fwctx->dev);
+ kfree(fwctx);
+}
+
+int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len),
+ u16 domain_nr, u16 bus_nr)
+{
+ struct brcmf_fw *fwctx;
+
+ brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
+ if (!fw_cb || !code)
+ return -EINVAL;
+
+ if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
+ return -EINVAL;
+
+ fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
+ if (!fwctx)
+ return -ENOMEM;
+
+ fwctx->dev = dev;
+ fwctx->flags = flags;
+ fwctx->done = fw_cb;
+ if (flags & BRCMF_FW_REQUEST_NVRAM)
+ fwctx->nvram_name = nvram;
+ fwctx->domain_nr = domain_nr;
+ fwctx->bus_nr = bus_nr;
+
+ return request_firmware_nowait(THIS_MODULE, true, code, dev,
+ GFP_KERNEL, fwctx,
+ brcmf_fw_request_code_done);
+}
+
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len))
+{
+ return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
+ 0);
+}
+
+int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
+ char nvram_name[BRCMF_FW_NAME_LEN])
+{
+ u32 i;
+ char end;
+
+ for (i = 0; i < table_size; i++) {
+ if (mapping_table[i].chipid == chip &&
+ mapping_table[i].revmask & BIT(chiprev))
+ break;
+ }
+
+ if (i == table_size) {
+ brcmf_err("Unknown chipid %d [%d]\n", chip, chiprev);
+ return -ENODEV;
+ }
+
+ /* check if firmware path is provided by module parameter */
+ if (brcmf_mp_global.firmware_path[0] != '\0') {
+ strlcpy(fw_name, brcmf_mp_global.firmware_path,
+ BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcpy(nvram_name, brcmf_mp_global.firmware_path,
+ BRCMF_FW_NAME_LEN);
+
+ end = brcmf_mp_global.firmware_path[
+ strlen(brcmf_mp_global.firmware_path) - 1];
+ if (end != '/') {
+ strlcat(fw_name, "/", BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcat(nvram_name, "/", BRCMF_FW_NAME_LEN);
+ }
+ }
+ strlcat(fw_name, mapping_table[i].fw, BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcat(nvram_name, mapping_table[i].nvram, BRCMF_FW_NAME_LEN);
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
new file mode 100644
index 000000000000..ef06f57a7a0e
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_FIRMWARE_H
+#define BRCMFMAC_FIRMWARE_H
+
+#define BRCMF_FW_REQUEST 0x000F
+#define BRCMF_FW_REQUEST_NVRAM 0x0001
+#define BRCMF_FW_REQ_FLAGS 0x00F0
+#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
+
+#define BRCMF_FW_NAME_LEN 320
+
+#define BRCMF_FW_DEFAULT_PATH "brcm/"
+
+/**
+ * struct brcmf_firmware_mapping - Used to map chipid/revmask to firmware
+ * filename and nvram filename. Each bus type implementation should create
+ * a table of firmware mappings (using the macros defined below).
+ *
+ * @chipid: ID of chip.
+ * @revmask: bitmask of revisions, e.g. 0x10 means rev 4 only, 0xf means rev 0-3
+ * @fw: name of the firmware file.
+ * @nvram: name of nvram file.
+ */
+struct brcmf_firmware_mapping {
+ u32 chipid;
+ u32 revmask;
+ const char *fw;
+ const char *nvram;
+};
+
+#define BRCMF_FW_NVRAM_DEF(fw_nvram_name, fw, nvram) \
+static const char BRCM_ ## fw_nvram_name ## _FIRMWARE_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH fw; \
+static const char BRCM_ ## fw_nvram_name ## _NVRAM_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH nvram; \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw); \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH nvram)
+
+#define BRCMF_FW_DEF(fw_name, fw) \
+static const char BRCM_ ## fw_name ## _FIRMWARE_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH fw; \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw) \
+
+#define BRCMF_FW_NVRAM_ENTRY(chipid, mask, name) \
+ { chipid, mask, \
+ BRCM_ ## name ## _FIRMWARE_NAME, BRCM_ ## name ## _NVRAM_NAME }
+
+#define BRCMF_FW_ENTRY(chipid, mask, name) \
+ { chipid, mask, BRCM_ ## name ## _FIRMWARE_NAME, NULL }
+
+int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
+ char nvram_name[BRCMF_FW_NAME_LEN]);
+void brcmf_fw_nvram_free(void *nvram);
+/*
+ * Request firmware(s) asynchronously. When the asynchronous request
+ * fails it will not use the callback, but call device_release_driver()
+ * instead which will call the driver .remove() callback.
+ */
+int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len),
+ u16 domain_nr, u16 bus_nr);
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len));
+
+#endif /* BRCMFMAC_FIRMWARE_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
index 2ca783fa50cf..2ca783fa50cf 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
index 95fd1c9675d1..95fd1c9675d1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
new file mode 100644
index 000000000000..7b26fb1b437c
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/netdevice.h>
+
+#include "brcmu_wifi.h"
+#include "brcmu_utils.h"
+
+#include "core.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "fwsignal.h"
+#include "fweh.h"
+#include "fwil.h"
+
+/**
+ * struct brcm_ethhdr - broadcom specific ether header.
+ *
+ * @subtype: subtype for this packet.
+ * @length: TODO: length of appended data.
+ * @version: version indication.
+ * @oui: OUI of this packet.
+ * @usr_subtype: subtype for this OUI.
+ */
+struct brcm_ethhdr {
+ __be16 subtype;
+ __be16 length;
+ u8 version;
+ u8 oui[3];
+ __be16 usr_subtype;
+} __packed;
+
+struct brcmf_event_msg_be {
+ __be16 version;
+ __be16 flags;
+ __be32 event_type;
+ __be32 status;
+ __be32 reason;
+ __be32 auth_type;
+ __be32 datalen;
+ u8 addr[ETH_ALEN];
+ char ifname[IFNAMSIZ];
+ u8 ifidx;
+ u8 bsscfgidx;
+} __packed;
+
+/**
+ * struct brcmf_event - contents of broadcom event packet.
+ *
+ * @eth: standard ether header.
+ * @hdr: broadcom specific ether header.
+ * @msg: common part of the actual event message.
+ */
+struct brcmf_event {
+ struct ethhdr eth;
+ struct brcm_ethhdr hdr;
+ struct brcmf_event_msg_be msg;
+} __packed;
+
+/**
+ * struct brcmf_fweh_queue_item - event item on event queue.
+ *
+ * @q: list element for queuing.
+ * @code: event code.
+ * @ifidx: interface index related to this event.
+ * @ifaddr: ethernet address for interface.
+ * @emsg: common parameters of the firmware event message.
+ * @data: event specific data part of the firmware event.
+ */
+struct brcmf_fweh_queue_item {
+ struct list_head q;
+ enum brcmf_fweh_event_code code;
+ u8 ifidx;
+ u8 ifaddr[ETH_ALEN];
+ struct brcmf_event_msg_be emsg;
+ u8 data[0];
+};
+
+/**
+ * struct brcmf_fweh_event_name - code, name mapping entry.
+ */
+struct brcmf_fweh_event_name {
+ enum brcmf_fweh_event_code code;
+ const char *name;
+};
+
+#ifdef DEBUG
+#define BRCMF_ENUM_DEF(id, val) \
+ { val, #id },
+
+/* array for mapping code to event name */
+static struct brcmf_fweh_event_name fweh_event_names[] = {
+ BRCMF_FWEH_EVENT_ENUM_DEFLIST
+};
+#undef BRCMF_ENUM_DEF
+
+/**
+ * brcmf_fweh_event_name() - returns name for given event code.
+ *
+ * @code: code to lookup.
+ */
+static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) {
+ if (fweh_event_names[i].code == code)
+ return fweh_event_names[i].name;
+ }
+ return "unknown";
+}
+#else
+static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
+{
+ return "nodebug";
+}
+#endif
+
+/**
+ * brcmf_fweh_queue_event() - create and queue event.
+ *
+ * @fweh: firmware event handling info.
+ * @event: event queue entry.
+ */
+static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
+ struct brcmf_fweh_queue_item *event)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&fweh->evt_q_lock, flags);
+ list_add_tail(&event->q, &fweh->event_q);
+ spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
+ schedule_work(&fweh->event_work);
+}
+
+static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp,
+ enum brcmf_fweh_event_code code,
+ struct brcmf_event_msg *emsg,
+ void *data)
+{
+ struct brcmf_fweh_info *fweh;
+ int err = -EINVAL;
+
+ if (ifp) {
+ fweh = &ifp->drvr->fweh;
+
+ /* handle the event if valid interface and handler */
+ if (fweh->evt_handler[code])
+ err = fweh->evt_handler[code](ifp, emsg, data);
+ else
+ brcmf_err("unhandled event %d ignored\n", code);
+ } else {
+ brcmf_err("no interface object\n");
+ }
+ return err;
+}
+
+/**
+ * brcmf_fweh_handle_if_event() - handle IF event.
+ *
+ * @drvr: driver information object.
+ * @item: queue entry.
+ * @ifpp: interface object (may change upon ADD action).
+ */
+static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
+ struct brcmf_event_msg *emsg,
+ void *data)
+{
+ struct brcmf_if_event *ifevent = data;
+ struct brcmf_if *ifp;
+ bool is_p2pdev;
+ int err = 0;
+
+ brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",
+ ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,
+ ifevent->flags, ifevent->role);
+
+ /* The P2P Device interface event must not be ignored contrary to what
+ * firmware tells us. Older firmware uses p2p noif, with sta role.
+ * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
+ * use the same ifevent and should be ignored.
+ */
+ is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
+ (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
+ ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
+ (drvr->fweh.p2pdev_setup_ongoing))));
+ if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
+ brcmf_dbg(EVENT, "event can be ignored\n");
+ return;
+ }
+ if (ifevent->ifidx >= BRCMF_MAX_IFS) {
+ brcmf_err("invalid interface index: %u\n", ifevent->ifidx);
+ return;
+ }
+
+ ifp = drvr->iflist[ifevent->bsscfgidx];
+
+ if (ifevent->action == BRCMF_E_IF_ADD) {
+ brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
+ emsg->addr);
+ ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,
+ is_p2pdev, emsg->ifname, emsg->addr);
+ if (IS_ERR(ifp))
+ return;
+ if (!is_p2pdev)
+ brcmf_fws_add_interface(ifp);
+ if (!drvr->fweh.evt_handler[BRCMF_E_IF])
+ if (brcmf_net_attach(ifp, false) < 0)
+ return;
+ }
+
+ if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
+ brcmf_fws_reset_interface(ifp);
+
+ err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
+
+ if (ifp && ifevent->action == BRCMF_E_IF_DEL)
+ brcmf_remove_interface(ifp);
+}
+
+/**
+ * brcmf_fweh_dequeue_event() - get event from the queue.
+ *
+ * @fweh: firmware event handling info.
+ */
+static struct brcmf_fweh_queue_item *
+brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh)
+{
+ struct brcmf_fweh_queue_item *event = NULL;
+ ulong flags;
+
+ spin_lock_irqsave(&fweh->evt_q_lock, flags);
+ if (!list_empty(&fweh->event_q)) {
+ event = list_first_entry(&fweh->event_q,
+ struct brcmf_fweh_queue_item, q);
+ list_del(&event->q);
+ }
+ spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
+
+ return event;
+}
+
+/**
+ * brcmf_fweh_event_worker() - firmware event worker.
+ *
+ * @work: worker object.
+ */
+static void brcmf_fweh_event_worker(struct work_struct *work)
+{
+ struct brcmf_pub *drvr;
+ struct brcmf_if *ifp;
+ struct brcmf_fweh_info *fweh;
+ struct brcmf_fweh_queue_item *event;
+ int err = 0;
+ struct brcmf_event_msg_be *emsg_be;
+ struct brcmf_event_msg emsg;
+
+ fweh = container_of(work, struct brcmf_fweh_info, event_work);
+ drvr = container_of(fweh, struct brcmf_pub, fweh);
+
+ while ((event = brcmf_fweh_dequeue_event(fweh))) {
+ brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
+ brcmf_fweh_event_name(event->code), event->code,
+ event->emsg.ifidx, event->emsg.bsscfgidx,
+ event->emsg.addr);
+
+ /* convert event message */
+ emsg_be = &event->emsg;
+ emsg.version = be16_to_cpu(emsg_be->version);
+ emsg.flags = be16_to_cpu(emsg_be->flags);
+ emsg.event_code = event->code;
+ emsg.status = be32_to_cpu(emsg_be->status);
+ emsg.reason = be32_to_cpu(emsg_be->reason);
+ emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
+ emsg.datalen = be32_to_cpu(emsg_be->datalen);
+ memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);
+ memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));
+ emsg.ifidx = emsg_be->ifidx;
+ emsg.bsscfgidx = emsg_be->bsscfgidx;
+
+ brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n",
+ emsg.version, emsg.flags, emsg.status, emsg.reason);
+ brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
+ min_t(u32, emsg.datalen, 64),
+ "event payload, len=%d\n", emsg.datalen);
+
+ /* special handling of interface event */
+ if (event->code == BRCMF_E_IF) {
+ brcmf_fweh_handle_if_event(drvr, &emsg, event->data);
+ goto event_free;
+ }
+
+ if (event->code == BRCMF_E_TDLS_PEER_EVENT)
+ ifp = drvr->iflist[0];
+ else
+ ifp = drvr->iflist[emsg.bsscfgidx];
+ err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg,
+ event->data);
+ if (err) {
+ brcmf_err("event handler failed (%d)\n",
+ event->code);
+ err = 0;
+ }
+event_free:
+ kfree(event);
+ }
+}
+
+/**
+ * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
+ *
+ * @ifp: ifp on which setup is taking place or finished.
+ * @ongoing: p2p device setup in progress (or not).
+ */
+void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
+{
+ ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
+}
+
+/**
+ * brcmf_fweh_attach() - initialize firmware event handling.
+ *
+ * @drvr: driver information object.
+ */
+void brcmf_fweh_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_fweh_info *fweh = &drvr->fweh;
+ INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
+ spin_lock_init(&fweh->evt_q_lock);
+ INIT_LIST_HEAD(&fweh->event_q);
+}
+
+/**
+ * brcmf_fweh_detach() - cleanup firmware event handling.
+ *
+ * @drvr: driver information object.
+ */
+void brcmf_fweh_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_fweh_info *fweh = &drvr->fweh;
+ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+
+ if (ifp) {
+ /* clear all events */
+ memset(eventmask, 0, BRCMF_EVENTING_MASK_LEN);
+ (void)brcmf_fil_iovar_data_set(ifp, "event_msgs",
+ eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ }
+ /* cancel the worker */
+ cancel_work_sync(&fweh->event_work);
+ WARN_ON(!list_empty(&fweh->event_q));
+ memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
+}
+
+/**
+ * brcmf_fweh_register() - register handler for given event code.
+ *
+ * @drvr: driver information object.
+ * @code: event code.
+ * @handler: handler for the given event code.
+ */
+int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
+ brcmf_fweh_handler_t handler)
+{
+ if (drvr->fweh.evt_handler[code]) {
+ brcmf_err("event code %d already registered\n", code);
+ return -ENOSPC;
+ }
+ drvr->fweh.evt_handler[code] = handler;
+ brcmf_dbg(TRACE, "event handler registered for %s\n",
+ brcmf_fweh_event_name(code));
+ return 0;
+}
+
+/**
+ * brcmf_fweh_unregister() - remove handler for given code.
+ *
+ * @drvr: driver information object.
+ * @code: event code.
+ */
+void brcmf_fweh_unregister(struct brcmf_pub *drvr,
+ enum brcmf_fweh_event_code code)
+{
+ brcmf_dbg(TRACE, "event handler cleared for %s\n",
+ brcmf_fweh_event_name(code));
+ drvr->fweh.evt_handler[code] = NULL;
+}
+
+/**
+ * brcmf_fweh_activate_events() - enables firmware events registered.
+ *
+ * @ifp: primary interface object.
+ */
+int brcmf_fweh_activate_events(struct brcmf_if *ifp)
+{
+ int i, err;
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+
+ for (i = 0; i < BRCMF_E_LAST; i++) {
+ if (ifp->drvr->fweh.evt_handler[i]) {
+ brcmf_dbg(EVENT, "enable event %s\n",
+ brcmf_fweh_event_name(i));
+ setbit(eventmask, i);
+ }
+ }
+
+ /* want to handle IF event as well */
+ brcmf_dbg(EVENT, "enable event IF\n");
+ setbit(eventmask, BRCMF_E_IF);
+
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
+ eventmask, BRCMF_EVENTING_MASK_LEN);
+ if (err)
+ brcmf_err("Set event_msgs error (%d)\n", err);
+
+ return err;
+}
+
+/**
+ * brcmf_fweh_process_event() - process skb as firmware event.
+ *
+ * @drvr: driver information object.
+ * @event_packet: event packet to process.
+ *
+ * If the packet buffer contains a firmware event message it will
+ * dispatch the event to a registered handler (using worker).
+ */
+void brcmf_fweh_process_event(struct brcmf_pub *drvr,
+ struct brcmf_event *event_packet)
+{
+ enum brcmf_fweh_event_code code;
+ struct brcmf_fweh_info *fweh = &drvr->fweh;
+ struct brcmf_fweh_queue_item *event;
+ gfp_t alloc_flag = GFP_KERNEL;
+ void *data;
+ u32 datalen;
+
+ /* get event info */
+ code = get_unaligned_be32(&event_packet->msg.event_type);
+ datalen = get_unaligned_be32(&event_packet->msg.datalen);
+ data = &event_packet[1];
+
+ if (code >= BRCMF_E_LAST)
+ return;
+
+ if (code != BRCMF_E_IF && !fweh->evt_handler[code])
+ return;
+
+ if (in_interrupt())
+ alloc_flag = GFP_ATOMIC;
+
+ event = kzalloc(sizeof(*event) + datalen, alloc_flag);
+ if (!event)
+ return;
+
+ event->code = code;
+ event->ifidx = event_packet->msg.ifidx;
+
+ /* use memcpy to get aligned event message */
+ memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
+ memcpy(event->data, data, datalen);
+ memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
+
+ brcmf_fweh_queue_event(fweh, event);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
new file mode 100644
index 000000000000..5e39e2a9e388
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef FWEH_H_
+#define FWEH_H_
+
+#include <asm/unaligned.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/if.h>
+
+/* formward declarations */
+struct brcmf_pub;
+struct brcmf_if;
+struct brcmf_cfg80211_info;
+struct brcmf_event;
+
+/* list of firmware events */
+#define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
+ BRCMF_ENUM_DEF(SET_SSID, 0) \
+ BRCMF_ENUM_DEF(JOIN, 1) \
+ BRCMF_ENUM_DEF(START, 2) \
+ BRCMF_ENUM_DEF(AUTH, 3) \
+ BRCMF_ENUM_DEF(AUTH_IND, 4) \
+ BRCMF_ENUM_DEF(DEAUTH, 5) \
+ BRCMF_ENUM_DEF(DEAUTH_IND, 6) \
+ BRCMF_ENUM_DEF(ASSOC, 7) \
+ BRCMF_ENUM_DEF(ASSOC_IND, 8) \
+ BRCMF_ENUM_DEF(REASSOC, 9) \
+ BRCMF_ENUM_DEF(REASSOC_IND, 10) \
+ BRCMF_ENUM_DEF(DISASSOC, 11) \
+ BRCMF_ENUM_DEF(DISASSOC_IND, 12) \
+ BRCMF_ENUM_DEF(QUIET_START, 13) \
+ BRCMF_ENUM_DEF(QUIET_END, 14) \
+ BRCMF_ENUM_DEF(BEACON_RX, 15) \
+ BRCMF_ENUM_DEF(LINK, 16) \
+ BRCMF_ENUM_DEF(MIC_ERROR, 17) \
+ BRCMF_ENUM_DEF(NDIS_LINK, 18) \
+ BRCMF_ENUM_DEF(ROAM, 19) \
+ BRCMF_ENUM_DEF(TXFAIL, 20) \
+ BRCMF_ENUM_DEF(PMKID_CACHE, 21) \
+ BRCMF_ENUM_DEF(RETROGRADE_TSF, 22) \
+ BRCMF_ENUM_DEF(PRUNE, 23) \
+ BRCMF_ENUM_DEF(AUTOAUTH, 24) \
+ BRCMF_ENUM_DEF(EAPOL_MSG, 25) \
+ BRCMF_ENUM_DEF(SCAN_COMPLETE, 26) \
+ BRCMF_ENUM_DEF(ADDTS_IND, 27) \
+ BRCMF_ENUM_DEF(DELTS_IND, 28) \
+ BRCMF_ENUM_DEF(BCNSENT_IND, 29) \
+ BRCMF_ENUM_DEF(BCNRX_MSG, 30) \
+ BRCMF_ENUM_DEF(BCNLOST_MSG, 31) \
+ BRCMF_ENUM_DEF(ROAM_PREP, 32) \
+ BRCMF_ENUM_DEF(PFN_NET_FOUND, 33) \
+ BRCMF_ENUM_DEF(PFN_NET_LOST, 34) \
+ BRCMF_ENUM_DEF(RESET_COMPLETE, 35) \
+ BRCMF_ENUM_DEF(JOIN_START, 36) \
+ BRCMF_ENUM_DEF(ROAM_START, 37) \
+ BRCMF_ENUM_DEF(ASSOC_START, 38) \
+ BRCMF_ENUM_DEF(IBSS_ASSOC, 39) \
+ BRCMF_ENUM_DEF(RADIO, 40) \
+ BRCMF_ENUM_DEF(PSM_WATCHDOG, 41) \
+ BRCMF_ENUM_DEF(PROBREQ_MSG, 44) \
+ BRCMF_ENUM_DEF(SCAN_CONFIRM_IND, 45) \
+ BRCMF_ENUM_DEF(PSK_SUP, 46) \
+ BRCMF_ENUM_DEF(COUNTRY_CODE_CHANGED, 47) \
+ BRCMF_ENUM_DEF(EXCEEDED_MEDIUM_TIME, 48) \
+ BRCMF_ENUM_DEF(ICV_ERROR, 49) \
+ BRCMF_ENUM_DEF(UNICAST_DECODE_ERROR, 50) \
+ BRCMF_ENUM_DEF(MULTICAST_DECODE_ERROR, 51) \
+ BRCMF_ENUM_DEF(TRACE, 52) \
+ BRCMF_ENUM_DEF(IF, 54) \
+ BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \
+ BRCMF_ENUM_DEF(RSSI, 56) \
+ BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \
+ BRCMF_ENUM_DEF(ACTION_FRAME, 59) \
+ BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \
+ BRCMF_ENUM_DEF(PRE_ASSOC_IND, 61) \
+ BRCMF_ENUM_DEF(PRE_REASSOC_IND, 62) \
+ BRCMF_ENUM_DEF(CHANNEL_ADOPTED, 63) \
+ BRCMF_ENUM_DEF(AP_STARTED, 64) \
+ BRCMF_ENUM_DEF(DFS_AP_STOP, 65) \
+ BRCMF_ENUM_DEF(DFS_AP_RESUME, 66) \
+ BRCMF_ENUM_DEF(ESCAN_RESULT, 69) \
+ BRCMF_ENUM_DEF(ACTION_FRAME_OFF_CHAN_COMPLETE, 70) \
+ BRCMF_ENUM_DEF(PROBERESP_MSG, 71) \
+ 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(TDLS_PEER_EVENT, 92) \
+ BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
+
+#define BRCMF_ENUM_DEF(id, val) \
+ BRCMF_E_##id = (val),
+
+/* firmware event codes sent by the dongle */
+enum brcmf_fweh_event_code {
+ BRCMF_FWEH_EVENT_ENUM_DEFLIST
+ /* this determines event mask length which must match
+ * minimum length check in device firmware so it is
+ * hard-coded here.
+ */
+ BRCMF_E_LAST = 139
+};
+#undef BRCMF_ENUM_DEF
+
+#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8)
+
+/* flags field values in struct brcmf_event_msg */
+#define BRCMF_EVENT_MSG_LINK 0x01
+#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
+#define BRCMF_EVENT_MSG_GROUP 0x04
+
+/* status field values in struct brcmf_event_msg */
+#define BRCMF_E_STATUS_SUCCESS 0
+#define BRCMF_E_STATUS_FAIL 1
+#define BRCMF_E_STATUS_TIMEOUT 2
+#define BRCMF_E_STATUS_NO_NETWORKS 3
+#define BRCMF_E_STATUS_ABORT 4
+#define BRCMF_E_STATUS_NO_ACK 5
+#define BRCMF_E_STATUS_UNSOLICITED 6
+#define BRCMF_E_STATUS_ATTEMPT 7
+#define BRCMF_E_STATUS_PARTIAL 8
+#define BRCMF_E_STATUS_NEWSCAN 9
+#define BRCMF_E_STATUS_NEWASSOC 10
+#define BRCMF_E_STATUS_11HQUIET 11
+#define BRCMF_E_STATUS_SUPPRESS 12
+#define BRCMF_E_STATUS_NOCHANS 13
+#define BRCMF_E_STATUS_CS_ABORT 15
+#define BRCMF_E_STATUS_ERROR 16
+
+/* reason field values in struct brcmf_event_msg */
+#define BRCMF_E_REASON_INITIAL_ASSOC 0
+#define BRCMF_E_REASON_LOW_RSSI 1
+#define BRCMF_E_REASON_DEAUTH 2
+#define BRCMF_E_REASON_DISASSOC 3
+#define BRCMF_E_REASON_BCNS_LOST 4
+#define BRCMF_E_REASON_MINTXRATE 9
+#define BRCMF_E_REASON_TXFAIL 10
+
+#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4
+#define BRCMF_E_REASON_FAST_ROAM_FAILED 5
+#define BRCMF_E_REASON_DIRECTED_ROAM 6
+#define BRCMF_E_REASON_TSPEC_REJECTED 7
+#define BRCMF_E_REASON_BETTER_AP 8
+
+#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED 0
+#define BRCMF_E_REASON_TDLS_PEER_CONNECTED 1
+#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED 2
+
+/* action field values for brcmf_ifevent */
+#define BRCMF_E_IF_ADD 1
+#define BRCMF_E_IF_DEL 2
+#define BRCMF_E_IF_CHANGE 3
+
+/* flag field values for brcmf_ifevent */
+#define BRCMF_E_IF_FLAG_NOIF 1
+
+/* role field values for brcmf_ifevent */
+#define BRCMF_E_IF_ROLE_STA 0
+#define BRCMF_E_IF_ROLE_AP 1
+#define BRCMF_E_IF_ROLE_WDS 2
+#define BRCMF_E_IF_ROLE_P2P_GO 3
+#define BRCMF_E_IF_ROLE_P2P_CLIENT 4
+
+/**
+ * definitions for event packet validation.
+ */
+#define BRCMF_EVENT_OUI_OFFSET 19
+#define BRCM_OUI "\x00\x10\x18"
+#define DOT11_OUI_LEN 3
+#define BCMILCP_BCM_SUBTYPE_EVENT 1
+
+
+/**
+ * struct brcmf_event_msg - firmware event message.
+ *
+ * @version: version information.
+ * @flags: event flags.
+ * @event_code: firmware event code.
+ * @status: status information.
+ * @reason: reason code.
+ * @auth_type: authentication type.
+ * @datalen: lenght of event data buffer.
+ * @addr: ether address.
+ * @ifname: interface name.
+ * @ifidx: interface index.
+ * @bsscfgidx: bsscfg index.
+ */
+struct brcmf_event_msg {
+ u16 version;
+ u16 flags;
+ u32 event_code;
+ u32 status;
+ u32 reason;
+ s32 auth_type;
+ u32 datalen;
+ u8 addr[ETH_ALEN];
+ char ifname[IFNAMSIZ];
+ u8 ifidx;
+ u8 bsscfgidx;
+};
+
+struct brcmf_if_event {
+ u8 ifidx;
+ u8 action;
+ u8 flags;
+ u8 bsscfgidx;
+ u8 role;
+};
+
+typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data);
+
+/**
+ * struct brcmf_fweh_info - firmware event handling information.
+ *
+ * @p2pdev_setup_ongoing: P2P device creation in progress.
+ * @event_work: event worker.
+ * @evt_q_lock: lock for event queue protection.
+ * @event_q: event queue.
+ * @evt_handler: registered event handlers.
+ */
+struct brcmf_fweh_info {
+ bool p2pdev_setup_ongoing;
+ struct work_struct event_work;
+ spinlock_t evt_q_lock;
+ struct list_head event_q;
+ int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data);
+};
+
+void brcmf_fweh_attach(struct brcmf_pub *drvr);
+void brcmf_fweh_detach(struct brcmf_pub *drvr);
+int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
+ int (*handler)(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data));
+void brcmf_fweh_unregister(struct brcmf_pub *drvr,
+ enum brcmf_fweh_event_code code);
+int brcmf_fweh_activate_events(struct brcmf_if *ifp);
+void brcmf_fweh_process_event(struct brcmf_pub *drvr,
+ struct brcmf_event *event_packet);
+void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
+
+static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
+ struct sk_buff *skb)
+{
+ struct brcmf_event *event_packet;
+ u8 *data;
+ u16 usr_stype;
+
+ /* only process events when protocol matches */
+ if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
+ return;
+
+ /* check for BRCM oui match */
+ event_packet = (struct brcmf_event *)skb_mac_header(skb);
+ data = (u8 *)event_packet;
+ data += BRCMF_EVENT_OUI_OFFSET;
+ if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN))
+ return;
+
+ /* final match on usr_subtype */
+ data += DOT11_OUI_LEN;
+ usr_stype = get_unaligned_be16(data);
+ if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT)
+ return;
+
+ brcmf_fweh_process_event(drvr, event_packet);
+}
+
+#endif /* FWEH_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
new file mode 100644
index 000000000000..f6a2df94dba7
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* FWIL is the Firmware Interface Layer. In this module the support functions
+ * are located to set and get variables to and from the firmware.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "fwil.h"
+#include "proto.h"
+
+
+#define MAX_HEX_DUMP_LEN 64
+
+#ifdef DEBUG
+static const char * const brcmf_fil_errstr[] = {
+ "BCME_OK",
+ "BCME_ERROR",
+ "BCME_BADARG",
+ "BCME_BADOPTION",
+ "BCME_NOTUP",
+ "BCME_NOTDOWN",
+ "BCME_NOTAP",
+ "BCME_NOTSTA",
+ "BCME_BADKEYIDX",
+ "BCME_RADIOOFF",
+ "BCME_NOTBANDLOCKED",
+ "BCME_NOCLK",
+ "BCME_BADRATESET",
+ "BCME_BADBAND",
+ "BCME_BUFTOOSHORT",
+ "BCME_BUFTOOLONG",
+ "BCME_BUSY",
+ "BCME_NOTASSOCIATED",
+ "BCME_BADSSIDLEN",
+ "BCME_OUTOFRANGECHAN",
+ "BCME_BADCHAN",
+ "BCME_BADADDR",
+ "BCME_NORESOURCE",
+ "BCME_UNSUPPORTED",
+ "BCME_BADLEN",
+ "BCME_NOTREADY",
+ "BCME_EPERM",
+ "BCME_NOMEM",
+ "BCME_ASSOCIATED",
+ "BCME_RANGE",
+ "BCME_NOTFOUND",
+ "BCME_WME_NOT_ENABLED",
+ "BCME_TSPEC_NOTFOUND",
+ "BCME_ACM_NOTSUPPORTED",
+ "BCME_NOT_WME_ASSOCIATION",
+ "BCME_SDIO_ERROR",
+ "BCME_DONGLE_DOWN",
+ "BCME_VERSION",
+ "BCME_TXFAIL",
+ "BCME_RXFAIL",
+ "BCME_NODEVICE",
+ "BCME_NMODE_DISABLED",
+ "BCME_NONRESIDENT",
+ "BCME_SCANREJECT",
+ "BCME_USAGE_ERROR",
+ "BCME_IOCTL_ERROR",
+ "BCME_SERIAL_PORT_ERR",
+ "BCME_DISABLED",
+ "BCME_DECERR",
+ "BCME_ENCERR",
+ "BCME_MICERR",
+ "BCME_REPLAY",
+ "BCME_IE_NOTFOUND",
+};
+
+static const char *brcmf_fil_get_errstr(u32 err)
+{
+ if (err >= ARRAY_SIZE(brcmf_fil_errstr))
+ return "(unknown)";
+
+ return brcmf_fil_errstr[err];
+}
+#else
+static const char *brcmf_fil_get_errstr(u32 err)
+{
+ return "";
+}
+#endif /* DEBUG */
+
+static s32
+brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+
+ if (drvr->bus_if->state != BRCMF_BUS_UP) {
+ brcmf_err("bus is down. we have nothing to do.\n");
+ return -EIO;
+ }
+
+ if (data != NULL)
+ len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
+ if (set)
+ err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len);
+ else
+ err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len);
+
+ if (err >= 0)
+ return 0;
+
+ brcmf_dbg(FIL, "Failed: %s (%d)\n",
+ brcmf_fil_get_errstr((u32)(-err)), err);
+
+ return err;
+}
+
+s32
+brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
+{
+ s32 err;
+
+ mutex_lock(&ifp->drvr->proto_block);
+
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
+ mutex_unlock(&ifp->drvr->proto_block);
+
+ return err;
+}
+
+s32
+brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
+{
+ s32 err;
+
+ mutex_lock(&ifp->drvr->proto_block);
+ err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
+
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ mutex_unlock(&ifp->drvr->proto_block);
+
+ return err;
+}
+
+
+s32
+brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data)
+{
+ s32 err;
+ __le32 data_le = cpu_to_le32(data);
+
+ mutex_lock(&ifp->drvr->proto_block);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
+ err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
+ mutex_unlock(&ifp->drvr->proto_block);
+
+ return err;
+}
+
+s32
+brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
+{
+ s32 err;
+ __le32 data_le = cpu_to_le32(*data);
+
+ mutex_lock(&ifp->drvr->proto_block);
+ err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
+ mutex_unlock(&ifp->drvr->proto_block);
+ *data = le32_to_cpu(data_le);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
+
+ return err;
+}
+
+static u32
+brcmf_create_iovar(char *name, const char *data, u32 datalen,
+ char *buf, u32 buflen)
+{
+ u32 len;
+
+ len = strlen(name) + 1;
+
+ if ((len + datalen) > buflen)
+ return 0;
+
+ memcpy(buf, name, len);
+
+ /* append data onto the end of the name string */
+ if (data && datalen)
+ memcpy(&buf[len], data, datalen);
+
+ return len + datalen;
+}
+
+
+s32
+brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
+ u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
+ sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
+ buflen, true);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating iovar failed\n");
+ }
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+}
+
+s32
+brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
+ u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
+ sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
+ buflen, false);
+ if (err == 0)
+ memcpy(data, drvr->proto_buf, len);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating iovar failed\n");
+ }
+
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+}
+
+s32
+brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data)
+{
+ __le32 data_le = cpu_to_le32(data);
+
+ return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le));
+}
+
+s32
+brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data)
+{
+ __le32 data_le = cpu_to_le32(*data);
+ s32 err;
+
+ err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le));
+ if (err == 0)
+ *data = le32_to_cpu(data_le);
+ return err;
+}
+
+static u32
+brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen,
+ char *buf, u32 buflen)
+{
+ const s8 *prefix = "bsscfg:";
+ s8 *p;
+ u32 prefixlen;
+ u32 namelen;
+ u32 iolen;
+ __le32 bsscfgidx_le;
+
+ if (bsscfgidx == 0)
+ return brcmf_create_iovar(name, data, datalen, buf, buflen);
+
+ prefixlen = strlen(prefix);
+ namelen = strlen(name) + 1; /* lengh of iovar name + null */
+ iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
+
+ if (buflen < iolen) {
+ brcmf_err("buffer is too short\n");
+ return 0;
+ }
+
+ p = buf;
+
+ /* copy prefix, no null */
+ memcpy(p, prefix, prefixlen);
+ p += prefixlen;
+
+ /* copy iovar name including null */
+ memcpy(p, name, namelen);
+ p += namelen;
+
+ /* bss config index as first data */
+ bsscfgidx_le = cpu_to_le32(bsscfgidx);
+ memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
+ p += sizeof(bsscfgidx_le);
+
+ /* parameter buffer follows */
+ if (datalen)
+ memcpy(p, data, datalen);
+
+ return iolen;
+}
+
+s32
+brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
+ void *data, u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bsscfgidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
+ drvr->proto_buf, sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
+ buflen, true);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating bsscfg failed\n");
+ }
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+}
+
+s32
+brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
+ void *data, u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
+ drvr->proto_buf, sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
+ buflen, false);
+ if (err == 0)
+ memcpy(data, drvr->proto_buf, len);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating bsscfg failed\n");
+ }
+ brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bsscfgidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+
+}
+
+s32
+brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data)
+{
+ __le32 data_le = cpu_to_le32(data);
+
+ return brcmf_fil_bsscfg_data_set(ifp, name, &data_le,
+ sizeof(data_le));
+}
+
+s32
+brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data)
+{
+ __le32 data_le = cpu_to_le32(*data);
+ s32 err;
+
+ err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le,
+ sizeof(data_le));
+ if (err == 0)
+ *data = le32_to_cpu(data_le);
+ return err;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
new file mode 100644
index 000000000000..6b72df17744e
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _fwil_h_
+#define _fwil_h_
+
+/*******************************************************************************
+ * Dongle command codes that are interpreted by firmware
+ ******************************************************************************/
+#define BRCMF_C_GET_VERSION 1
+#define BRCMF_C_UP 2
+#define BRCMF_C_DOWN 3
+#define BRCMF_C_SET_PROMISC 10
+#define BRCMF_C_GET_RATE 12
+#define BRCMF_C_GET_INFRA 19
+#define BRCMF_C_SET_INFRA 20
+#define BRCMF_C_GET_AUTH 21
+#define BRCMF_C_SET_AUTH 22
+#define BRCMF_C_GET_BSSID 23
+#define BRCMF_C_GET_SSID 25
+#define BRCMF_C_SET_SSID 26
+#define BRCMF_C_TERMINATED 28
+#define BRCMF_C_GET_CHANNEL 29
+#define BRCMF_C_SET_CHANNEL 30
+#define BRCMF_C_GET_SRL 31
+#define BRCMF_C_SET_SRL 32
+#define BRCMF_C_GET_LRL 33
+#define BRCMF_C_SET_LRL 34
+#define BRCMF_C_GET_RADIO 37
+#define BRCMF_C_SET_RADIO 38
+#define BRCMF_C_GET_PHYTYPE 39
+#define BRCMF_C_SET_KEY 45
+#define BRCMF_C_GET_REGULATORY 46
+#define BRCMF_C_SET_REGULATORY 47
+#define BRCMF_C_SET_PASSIVE_SCAN 49
+#define BRCMF_C_SCAN 50
+#define BRCMF_C_SCAN_RESULTS 51
+#define BRCMF_C_DISASSOC 52
+#define BRCMF_C_REASSOC 53
+#define BRCMF_C_SET_ROAM_TRIGGER 55
+#define BRCMF_C_SET_ROAM_DELTA 57
+#define BRCMF_C_GET_BCNPRD 75
+#define BRCMF_C_SET_BCNPRD 76
+#define BRCMF_C_GET_DTIMPRD 77
+#define BRCMF_C_SET_DTIMPRD 78
+#define BRCMF_C_SET_COUNTRY 84
+#define BRCMF_C_GET_PM 85
+#define BRCMF_C_SET_PM 86
+#define BRCMF_C_GET_REVINFO 98
+#define BRCMF_C_GET_CURR_RATESET 114
+#define BRCMF_C_GET_AP 117
+#define BRCMF_C_SET_AP 118
+#define BRCMF_C_SET_SCB_AUTHORIZE 121
+#define BRCMF_C_SET_SCB_DEAUTHORIZE 122
+#define BRCMF_C_GET_RSSI 127
+#define BRCMF_C_GET_WSEC 133
+#define BRCMF_C_SET_WSEC 134
+#define BRCMF_C_GET_PHY_NOISE 135
+#define BRCMF_C_GET_BSS_INFO 136
+#define BRCMF_C_GET_GET_PKTCNTS 137
+#define BRCMF_C_GET_BANDLIST 140
+#define BRCMF_C_SET_SCB_TIMEOUT 158
+#define BRCMF_C_GET_ASSOCLIST 159
+#define BRCMF_C_GET_PHYLIST 180
+#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185
+#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187
+#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201
+#define BRCMF_C_GET_VALID_CHANNELS 217
+#define BRCMF_C_GET_KEY_PRIMARY 235
+#define BRCMF_C_SET_KEY_PRIMARY 236
+#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258
+#define BRCMF_C_GET_VAR 262
+#define BRCMF_C_SET_VAR 263
+
+s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
+s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
+s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data);
+s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data);
+
+s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
+ u32 len);
+s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
+ u32 len);
+s32 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data);
+s32 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data);
+
+s32 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, void *data,
+ u32 len);
+s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data,
+ u32 len);
+s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data);
+s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data);
+
+#endif /* _fwil_h_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
new file mode 100644
index 000000000000..1afc2ad83b6c
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -0,0 +1,790 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef FWIL_TYPES_H_
+#define FWIL_TYPES_H_
+
+#include <linux/if_ether.h>
+
+
+#define BRCMF_FIL_ACTION_FRAME_SIZE 1800
+
+/* ARP Offload feature flags for arp_ol iovar */
+#define BRCMF_ARP_OL_AGENT 0x00000001
+#define BRCMF_ARP_OL_SNOOP 0x00000002
+#define BRCMF_ARP_OL_HOST_AUTO_REPLY 0x00000004
+#define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008
+
+#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
+#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002
+
+#define BRCMF_STA_WME 0x00000002 /* WMM association */
+#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */
+#define BRCMF_STA_ASSOC 0x00000010 /* Associated */
+#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */
+#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */
+
+/* size of brcmf_scan_params not including variable length array */
+#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
+
+/* masks for channel and ssid count */
+#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff
+#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16
+
+/* primary (ie tx) key */
+#define BRCMF_PRIMARY_KEY (1 << 1)
+#define DOT11_BSSTYPE_ANY 2
+#define BRCMF_ESCAN_REQ_VERSION 1
+
+#define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */
+
+/* OBSS Coex Auto/On/Off */
+#define BRCMF_OBSS_COEX_AUTO (-1)
+#define BRCMF_OBSS_COEX_OFF 0
+#define BRCMF_OBSS_COEX_ON 1
+
+/* WOWL bits */
+/* Wakeup on Magic packet: */
+#define BRCMF_WOWL_MAGIC (1 << 0)
+/* Wakeup on Netpattern */
+#define BRCMF_WOWL_NET (1 << 1)
+/* Wakeup on loss-of-link due to Disassoc/Deauth: */
+#define BRCMF_WOWL_DIS (1 << 2)
+/* Wakeup on retrograde TSF: */
+#define BRCMF_WOWL_RETR (1 << 3)
+/* Wakeup on loss of beacon: */
+#define BRCMF_WOWL_BCN (1 << 4)
+/* Wakeup after test: */
+#define BRCMF_WOWL_TST (1 << 5)
+/* Wakeup after PTK refresh: */
+#define BRCMF_WOWL_M1 (1 << 6)
+/* Wakeup after receipt of EAP-Identity Req: */
+#define BRCMF_WOWL_EAPID (1 << 7)
+/* Wakeind via PME(0) or GPIO(1): */
+#define BRCMF_WOWL_PME_GPIO (1 << 8)
+/* need tkip phase 1 key to be updated by the driver: */
+#define BRCMF_WOWL_NEEDTKIP1 (1 << 9)
+/* enable wakeup if GTK fails: */
+#define BRCMF_WOWL_GTK_FAILURE (1 << 10)
+/* support extended magic packets: */
+#define BRCMF_WOWL_EXTMAGPAT (1 << 11)
+/* support ARP/NS/keepalive offloading: */
+#define BRCMF_WOWL_ARPOFFLOAD (1 << 12)
+/* read protocol version for EAPOL frames: */
+#define BRCMF_WOWL_WPA2 (1 << 13)
+/* If the bit is set, use key rotaton: */
+#define BRCMF_WOWL_KEYROT (1 << 14)
+/* If the bit is set, frm received was bcast frame: */
+#define BRCMF_WOWL_BCAST (1 << 15)
+/* If the bit is set, scan offload is enabled: */
+#define BRCMF_WOWL_SCANOL (1 << 16)
+/* Wakeup on tcpkeep alive timeout: */
+#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17)
+/* Wakeup on mDNS Conflict Resolution: */
+#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18)
+/* Wakeup on mDNS Service Connect: */
+#define BRCMF_WOWL_MDNS_SERVICE (1 << 19)
+/* tcp keepalive got data: */
+#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20)
+/* Firmware died in wowl mode: */
+#define BRCMF_WOWL_FW_HALT (1 << 21)
+/* Enable detection of radio button changes: */
+#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22)
+/* Offloads detected MIC failure(s): */
+#define BRCMF_WOWL_MIC_FAIL (1 << 23)
+/* Wakeup in Unassociated state (Net/Magic Pattern): */
+#define BRCMF_WOWL_UNASSOC (1 << 24)
+/* Wakeup if received matched secured pattern: */
+#define BRCMF_WOWL_SECURE (1 << 25)
+/* Wakeup on finding preferred network */
+#define BRCMF_WOWL_PFN_FOUND (1 << 26)
+/* Link Down indication in WoWL mode: */
+#define BRCMF_WOWL_LINKDOWN (1 << 31)
+
+#define BRCMF_WOWL_MAXPATTERNS 8
+#define BRCMF_WOWL_MAXPATTERNSIZE 128
+
+#define BRCMF_COUNTRY_BUF_SZ 4
+#define BRCMF_ANT_MAX 4
+
+#define BRCMF_MAX_ASSOCLIST 128
+
+#define BRCMF_TXBF_SU_BFE_CAP BIT(0)
+#define BRCMF_TXBF_MU_BFE_CAP BIT(1)
+#define BRCMF_TXBF_SU_BFR_CAP BIT(0)
+#define BRCMF_TXBF_MU_BFR_CAP BIT(1)
+
+#define BRCMF_MAXPMKID 16 /* max # PMKID cache entries */
+
+#define BRCMF_PFN_MACADDR_CFG_VER 1
+#define BRCMF_PFN_MAC_OUI_ONLY BIT(0)
+#define BRCMF_PFN_SET_MAC_UNASSOC BIT(1)
+
+/* join preference types for join_pref iovar */
+enum brcmf_join_pref_types {
+ BRCMF_JOIN_PREF_RSSI = 1,
+ BRCMF_JOIN_PREF_WPA,
+ BRCMF_JOIN_PREF_BAND,
+ BRCMF_JOIN_PREF_RSSI_DELTA,
+};
+
+enum brcmf_fil_p2p_if_types {
+ BRCMF_FIL_P2P_IF_CLIENT,
+ BRCMF_FIL_P2P_IF_GO,
+ BRCMF_FIL_P2P_IF_DYNBCN_GO,
+ BRCMF_FIL_P2P_IF_DEV,
+};
+
+enum brcmf_wowl_pattern_type {
+ BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0,
+ BRCMF_WOWL_PATTERN_TYPE_ARP,
+ BRCMF_WOWL_PATTERN_TYPE_NA
+};
+
+struct brcmf_fil_p2p_if_le {
+ u8 addr[ETH_ALEN];
+ __le16 type;
+ __le16 chspec;
+};
+
+struct brcmf_fil_chan_info_le {
+ __le32 hw_channel;
+ __le32 target_channel;
+ __le32 scan_channel;
+};
+
+struct brcmf_fil_action_frame_le {
+ u8 da[ETH_ALEN];
+ __le16 len;
+ __le32 packet_id;
+ u8 data[BRCMF_FIL_ACTION_FRAME_SIZE];
+};
+
+struct brcmf_fil_af_params_le {
+ __le32 channel;
+ __le32 dwell_time;
+ u8 bssid[ETH_ALEN];
+ u8 pad[2];
+ struct brcmf_fil_action_frame_le action_frame;
+};
+
+struct brcmf_fil_bss_enable_le {
+ __le32 bsscfgidx;
+ __le32 enable;
+};
+
+struct brcmf_fil_bwcap_le {
+ __le32 band;
+ __le32 bw_cap;
+};
+
+/**
+ * struct tdls_iovar - common structure for tdls iovars.
+ *
+ * @ea: ether address of peer station.
+ * @mode: mode value depending on specific tdls iovar.
+ * @chanspec: channel specification.
+ * @pad: unused (for future use).
+ */
+struct brcmf_tdls_iovar_le {
+ u8 ea[ETH_ALEN]; /* Station address */
+ u8 mode; /* mode: depends on iovar */
+ __le16 chanspec;
+ __le32 pad; /* future */
+};
+
+enum brcmf_tdls_manual_ep_ops {
+ BRCMF_TDLS_MANUAL_EP_CREATE = 1,
+ BRCMF_TDLS_MANUAL_EP_DELETE = 3,
+ BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6
+};
+
+/* Pattern matching filter. Specifies an offset within received packets to
+ * start matching, the pattern to match, the size of the pattern, and a bitmask
+ * that indicates which bits within the pattern should be matched.
+ */
+struct brcmf_pkt_filter_pattern_le {
+ /*
+ * Offset within received packet to start pattern matching.
+ * Offset '0' is the first byte of the ethernet header.
+ */
+ __le32 offset;
+ /* Size of the pattern. Bitmask must be the same size.*/
+ __le32 size_bytes;
+ /*
+ * Variable length mask and pattern data. mask starts at offset 0.
+ * Pattern immediately follows mask.
+ */
+ u8 mask_and_pattern[1];
+};
+
+/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */
+struct brcmf_pkt_filter_le {
+ __le32 id; /* Unique filter id, specified by app. */
+ __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */
+ __le32 negate_match; /* Negate the result of filter matches */
+ union { /* Filter definitions */
+ struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */
+ } u;
+};
+
+/* IOVAR "pkt_filter_enable" parameter. */
+struct brcmf_pkt_filter_enable_le {
+ __le32 id; /* Unique filter id */
+ __le32 enable; /* Enable/disable bool */
+};
+
+/* BSS info structure
+ * Applications MUST CHECK ie_offset field and length field to access IEs and
+ * next bss_info structure in a vector (in struct brcmf_scan_results)
+ */
+struct brcmf_bss_info_le {
+ __le32 version; /* version field */
+ __le32 length; /* byte length of data in this record,
+ * starting at version and including IEs
+ */
+ u8 BSSID[ETH_ALEN];
+ __le16 beacon_period; /* units are Kusec */
+ __le16 capability; /* Capability information */
+ u8 SSID_len;
+ u8 SSID[32];
+ struct {
+ __le32 count; /* # rates in this set */
+ u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */
+ } rateset; /* supported rates */
+ __le16 chanspec; /* chanspec for bss */
+ __le16 atim_window; /* units are Kusec */
+ u8 dtim_period; /* DTIM period */
+ __le16 RSSI; /* receive signal strength (in dBm) */
+ s8 phy_noise; /* noise (in dBm) */
+
+ u8 n_cap; /* BSS is 802.11N Capable */
+ /* 802.11N BSS Capabilities (based on HT_CAP_*): */
+ __le32 nbss_cap;
+ u8 ctl_ch; /* 802.11N BSS control channel number */
+ __le32 reserved32[1]; /* Reserved for expansion of BSS properties */
+ u8 flags; /* flags */
+ u8 reserved[3]; /* Reserved for expansion of BSS properties */
+ u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */
+
+ __le16 ie_offset; /* offset at which IEs start, from beginning */
+ __le32 ie_length; /* byte length of Information Elements */
+ __le16 SNR; /* average SNR of during frame reception */
+ /* Add new fields here */
+ /* variable length Information Elements */
+};
+
+struct brcm_rateset_le {
+ /* # rates in this set */
+ __le32 count;
+ /* rates in 500kbps units w/hi bit set if basic */
+ u8 rates[BRCMF_MAXRATES_IN_SET];
+};
+
+struct brcmf_ssid_le {
+ __le32 SSID_len;
+ unsigned char SSID[IEEE80211_MAX_SSID_LEN];
+};
+
+struct brcmf_scan_params_le {
+ struct brcmf_ssid_le ssid_le; /* default: {0, ""} */
+ u8 bssid[ETH_ALEN]; /* default: bcast */
+ s8 bss_type; /* default: any,
+ * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT
+ */
+ u8 scan_type; /* flags, 0 use default */
+ __le32 nprobes; /* -1 use default, number of probes per channel */
+ __le32 active_time; /* -1 use default, dwell time per channel for
+ * active scanning
+ */
+ __le32 passive_time; /* -1 use default, dwell time per channel
+ * for passive scanning
+ */
+ __le32 home_time; /* -1 use default, dwell time for the
+ * home channel between channel scans
+ */
+ __le32 channel_num; /* count of channels and ssids that follow
+ *
+ * low half is count of channels in
+ * channel_list, 0 means default (use all
+ * available channels)
+ *
+ * high half is entries in struct brcmf_ssid
+ * array that follows channel_list, aligned for
+ * s32 (4 bytes) meaning an odd channel count
+ * implies a 2-byte pad between end of
+ * channel_list and first ssid
+ *
+ * if ssid count is zero, single ssid in the
+ * fixed parameter portion is assumed, otherwise
+ * ssid in the fixed portion is ignored
+ */
+ __le16 channel_list[1]; /* list of chanspecs */
+};
+
+struct brcmf_scan_results {
+ u32 buflen;
+ u32 version;
+ u32 count;
+ struct brcmf_bss_info_le bss_info_le[];
+};
+
+struct brcmf_escan_params_le {
+ __le32 version;
+ __le16 action;
+ __le16 sync_id;
+ struct brcmf_scan_params_le params_le;
+};
+
+struct brcmf_escan_result_le {
+ __le32 buflen;
+ __le32 version;
+ __le16 sync_id;
+ __le16 bss_count;
+ struct brcmf_bss_info_le bss_info_le;
+};
+
+#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \
+ sizeof(struct brcmf_bss_info_le))
+
+/* used for association with a specific BSSID and chanspec list */
+struct brcmf_assoc_params_le {
+ /* 00:00:00:00:00:00: broadcast scan */
+ u8 bssid[ETH_ALEN];
+ /* 0: all available channels, otherwise count of chanspecs in
+ * chanspec_list */
+ __le32 chanspec_num;
+ /* list of chanspecs */
+ __le16 chanspec_list[1];
+};
+
+/**
+ * struct join_pref params - parameters for preferred join selection.
+ *
+ * @type: preference type (see enum brcmf_join_pref_types).
+ * @len: length of bytes following (currently always 2).
+ * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA).
+ * @band: band to which selection preference applies.
+ * This is used if @type is BAND or RSSI_DELTA.
+ */
+struct brcmf_join_pref_params {
+ u8 type;
+ u8 len;
+ u8 rssi_gain;
+ u8 band;
+};
+
+/* used for join with or without a specific bssid and channel list */
+struct brcmf_join_params {
+ struct brcmf_ssid_le ssid_le;
+ struct brcmf_assoc_params_le params_le;
+};
+
+/* scan params for extended join */
+struct brcmf_join_scan_params_le {
+ u8 scan_type; /* 0 use default, active or passive scan */
+ __le32 nprobes; /* -1 use default, nr of probes per channel */
+ __le32 active_time; /* -1 use default, dwell time per channel for
+ * active scanning
+ */
+ __le32 passive_time; /* -1 use default, dwell time per channel
+ * for passive scanning
+ */
+ __le32 home_time; /* -1 use default, dwell time for the home
+ * channel between channel scans
+ */
+};
+
+/* extended join params */
+struct brcmf_ext_join_params_le {
+ struct brcmf_ssid_le ssid_le; /* {0, ""}: wildcard scan */
+ struct brcmf_join_scan_params_le scan_le;
+ struct brcmf_assoc_params_le assoc_le;
+};
+
+struct brcmf_wsec_key {
+ u32 index; /* key index */
+ u32 len; /* key length */
+ u8 data[WLAN_MAX_KEY_LEN]; /* key data */
+ u32 pad_1[18];
+ u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+ u32 flags; /* misc flags */
+ u32 pad_2[3];
+ u32 iv_initialized; /* has IV been initialized already? */
+ u32 pad_3;
+ /* Rx IV */
+ struct {
+ u32 hi; /* upper 32 bits of IV */
+ u16 lo; /* lower 16 bits of IV */
+ } rxiv;
+ u32 pad_4[2];
+ u8 ea[ETH_ALEN]; /* per station */
+};
+
+/*
+ * dongle requires same struct as above but with fields in little endian order
+ */
+struct brcmf_wsec_key_le {
+ __le32 index; /* key index */
+ __le32 len; /* key length */
+ u8 data[WLAN_MAX_KEY_LEN]; /* key data */
+ __le32 pad_1[18];
+ __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+ __le32 flags; /* misc flags */
+ __le32 pad_2[3];
+ __le32 iv_initialized; /* has IV been initialized already? */
+ __le32 pad_3;
+ /* Rx IV */
+ struct {
+ __le32 hi; /* upper 32 bits of IV */
+ __le16 lo; /* lower 16 bits of IV */
+ } rxiv;
+ __le32 pad_4[2];
+ u8 ea[ETH_ALEN]; /* per station */
+};
+
+/* Used to get specific STA parameters */
+struct brcmf_scb_val_le {
+ __le32 val;
+ u8 ea[ETH_ALEN];
+};
+
+/* channel encoding */
+struct brcmf_channel_info_le {
+ __le32 hw_channel;
+ __le32 target_channel;
+ __le32 scan_channel;
+};
+
+struct brcmf_sta_info_le {
+ __le16 ver; /* version of this struct */
+ __le16 len; /* length in bytes of this structure */
+ __le16 cap; /* sta's advertised capabilities */
+ __le32 flags; /* flags defined below */
+ __le32 idle; /* time since data pkt rx'd from sta */
+ u8 ea[ETH_ALEN]; /* Station address */
+ __le32 count; /* # rates in this set */
+ u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
+ /* w/hi bit set if basic */
+ __le32 in; /* seconds elapsed since associated */
+ __le32 listen_interval_inms; /* Min Listen interval in ms for STA */
+ __le32 tx_pkts; /* # of packets transmitted */
+ __le32 tx_failures; /* # of packets failed */
+ __le32 rx_ucast_pkts; /* # of unicast packets received */
+ __le32 rx_mcast_pkts; /* # of multicast packets received */
+ __le32 tx_rate; /* Rate of last successful tx frame */
+ __le32 rx_rate; /* Rate of last successful rx frame */
+ __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
+ __le32 rx_decrypt_failures; /* # of packet decrypted failed */
+ __le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */
+ __le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */
+ __le32 tx_mcast_pkts; /* # of mcast pkts txed */
+ __le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */
+ __le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */
+ __le64 tx_ucast_bytes; /* data bytes txed (ucast) */
+ __le64 tx_mcast_bytes; /* # data bytes txed (mcast) */
+ __le64 rx_ucast_bytes; /* data bytes recvd (ucast) */
+ __le64 rx_mcast_bytes; /* data bytes recvd (mcast) */
+ s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */
+ s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */
+ __le16 aid; /* association ID */
+ __le16 ht_capabilities; /* advertised ht caps */
+ __le16 vht_flags; /* converted vht flags */
+ __le32 tx_pkts_retry_cnt; /* # of frames where a retry was
+ * exhausted.
+ */
+ __le32 tx_pkts_retry_exhausted; /* # of user frames where a retry
+ * was exhausted
+ */
+ s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last
+ * received data frame.
+ */
+ /* TX WLAN retry/failure statistics:
+ * Separated for host requested frames and locally generated frames.
+ * Include unicast frame only where the retries/failures can be counted.
+ */
+ __le32 tx_pkts_total; /* # user frames sent successfully */
+ __le32 tx_pkts_retries; /* # user frames retries */
+ __le32 tx_pkts_fw_total; /* # FW generated sent successfully */
+ __le32 tx_pkts_fw_retries; /* # retries for FW generated frames */
+ __le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry
+ * was exhausted
+ */
+ __le32 rx_pkts_retried; /* # rx with retry bit set */
+ __le32 tx_rate_fallback; /* lowest fallback TX rate */
+};
+
+struct brcmf_chanspec_list {
+ __le32 count; /* # of entries */
+ __le32 element[1]; /* variable length uint32 list */
+};
+
+/*
+ * WLC_E_PROBRESP_MSG
+ * WLC_E_P2P_PROBREQ_MSG
+ * WLC_E_ACTION_FRAME_RX
+ */
+struct brcmf_rx_mgmt_data {
+ __be16 version;
+ __be16 chanspec;
+ __be32 rssi;
+ __be32 mactime;
+ __be32 rate;
+};
+
+/**
+ * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct.
+ *
+ * @cmd: "add", "del" or "clr".
+ * @masksize: Size of the mask in #of bytes
+ * @offset: Pattern byte offset in packet
+ * @patternoffset: Offset of start of pattern. Starting from field masksize.
+ * @patternsize: Size of the pattern itself in #of bytes
+ * @id: id
+ * @reasonsize: Size of the wakeup reason code
+ * @type: Type of pattern (enum brcmf_wowl_pattern_type)
+ */
+struct brcmf_fil_wowl_pattern_le {
+ u8 cmd[4];
+ __le32 masksize;
+ __le32 offset;
+ __le32 patternoffset;
+ __le32 patternsize;
+ __le32 id;
+ __le32 reasonsize;
+ __le32 type;
+ /* u8 mask[] - Mask follows the structure above */
+ /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */
+};
+
+struct brcmf_mbss_ssid_le {
+ __le32 bsscfgidx;
+ __le32 SSID_len;
+ unsigned char SSID[32];
+};
+
+/**
+ * struct brcmf_fil_country_le - country configuration structure.
+ *
+ * @country_abbrev: null-terminated country code used in the country IE.
+ * @rev: revision specifier for ccode. on set, -1 indicates unspecified.
+ * @ccode: null-terminated built-in country code.
+ */
+struct brcmf_fil_country_le {
+ char country_abbrev[BRCMF_COUNTRY_BUF_SZ];
+ __le32 rev;
+ char ccode[BRCMF_COUNTRY_BUF_SZ];
+};
+
+/**
+ * struct brcmf_rev_info_le - device revision info.
+ *
+ * @vendorid: PCI vendor id.
+ * @deviceid: device id of chip.
+ * @radiorev: radio revision.
+ * @chiprev: chip revision.
+ * @corerev: core revision.
+ * @boardid: board identifier (usu. PCI sub-device id).
+ * @boardvendor: board vendor (usu. PCI sub-vendor id).
+ * @boardrev: board revision.
+ * @driverrev: driver version.
+ * @ucoderev: microcode version.
+ * @bus: bus type.
+ * @chipnum: chip number.
+ * @phytype: phy type.
+ * @phyrev: phy revision.
+ * @anarev: anacore rev.
+ * @chippkg: chip package info.
+ * @nvramrev: nvram revision number.
+ */
+struct brcmf_rev_info_le {
+ __le32 vendorid;
+ __le32 deviceid;
+ __le32 radiorev;
+ __le32 chiprev;
+ __le32 corerev;
+ __le32 boardid;
+ __le32 boardvendor;
+ __le32 boardrev;
+ __le32 driverrev;
+ __le32 ucoderev;
+ __le32 bus;
+ __le32 chipnum;
+ __le32 phytype;
+ __le32 phyrev;
+ __le32 anarev;
+ __le32 chippkg;
+ __le32 nvramrev;
+};
+
+/**
+ * struct brcmf_assoclist_le - request assoc list.
+ *
+ * @count: indicates number of stations.
+ * @mac: MAC addresses of stations.
+ */
+struct brcmf_assoclist_le {
+ __le32 count;
+ u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN];
+};
+
+/**
+ * struct brcmf_wowl_wakeind_le - Wakeup indicators
+ * Note: note both fields contain same information.
+ *
+ * @pci_wakeind: Whether PCI PMECSR PMEStatus bit was set.
+ * @ucode_wakeind: What wakeup-event indication was set by ucode
+ */
+struct brcmf_wowl_wakeind_le {
+ __le32 pci_wakeind;
+ __le32 ucode_wakeind;
+};
+
+/**
+ * struct brcmf_pmksa - PMK Security Association
+ *
+ * @bssid: The AP's BSSID.
+ * @pmkid: he PMK material itself.
+ */
+struct brcmf_pmksa {
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[WLAN_PMKID_LEN];
+};
+
+/**
+ * struct brcmf_pmk_list_le - List of pmksa's.
+ *
+ * @npmk: Number of pmksa's.
+ * @pmk: PMK SA information.
+ */
+struct brcmf_pmk_list_le {
+ __le32 npmk;
+ struct brcmf_pmksa pmk[BRCMF_MAXPMKID];
+};
+
+/**
+ * struct brcmf_pno_param_le - PNO scan configuration parameters
+ *
+ * @version: PNO parameters version.
+ * @scan_freq: scan frequency.
+ * @lost_network_timeout: #sec. to declare discovered network as lost.
+ * @flags: Bit field to control features of PFN such as sort criteria auto
+ * enable switch and background scan.
+ * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
+ * criteria.
+ * @bestn: number of best networks in each scan.
+ * @mscan: number of scans recorded.
+ * @repeat: minimum number of scan intervals before scan frequency changes
+ * in adaptive scan.
+ * @exp: exponent of 2 for maximum scan interval.
+ * @slow_freq: slow scan period.
+ */
+struct brcmf_pno_param_le {
+ __le32 version;
+ __le32 scan_freq;
+ __le32 lost_network_timeout;
+ __le16 flags;
+ __le16 rssi_margin;
+ u8 bestn;
+ u8 mscan;
+ u8 repeat;
+ u8 exp;
+ __le32 slow_freq;
+};
+
+/**
+ * struct brcmf_pno_net_param_le - scan parameters per preferred network.
+ *
+ * @ssid: ssid name and its length.
+ * @flags: bit2: hidden.
+ * @infra: BSS vs IBSS.
+ * @auth: Open vs Closed.
+ * @wpa_auth: WPA type.
+ * @wsec: wsec value.
+ */
+struct brcmf_pno_net_param_le {
+ struct brcmf_ssid_le ssid;
+ __le32 flags;
+ __le32 infra;
+ __le32 auth;
+ __le32 wpa_auth;
+ __le32 wsec;
+};
+
+/**
+ * struct brcmf_pno_net_info_le - information per found network.
+ *
+ * @bssid: BSS network identifier.
+ * @channel: channel number only.
+ * @SSID_len: length of ssid.
+ * @SSID: ssid characters.
+ * @RSSI: receive signal strength (in dBm).
+ * @timestamp: age in seconds.
+ */
+struct brcmf_pno_net_info_le {
+ u8 bssid[ETH_ALEN];
+ u8 channel;
+ u8 SSID_len;
+ u8 SSID[32];
+ __le16 RSSI;
+ __le16 timestamp;
+};
+
+/**
+ * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
+ *
+ * @version: PNO version identifier.
+ * @status: indicates completion status of PNO scan.
+ * @count: amount of brcmf_pno_net_info_le entries appended.
+ */
+struct brcmf_pno_scanresults_le {
+ __le32 version;
+ __le32 status;
+ __le32 count;
+};
+
+/**
+ * struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization.
+ *
+ * @version: PNO version identifier.
+ * @flags: Flags defining how mac addrss should be used.
+ * @mac: MAC address.
+ */
+struct brcmf_pno_macaddr_le {
+ u8 version;
+ u8 flags;
+ u8 mac[ETH_ALEN];
+};
+
+/**
+ * struct brcmf_pktcnt_le - packet counters.
+ *
+ * @rx_good_pkt: packets (MSDUs & MMPDUs) received from this station
+ * @rx_bad_pkt: failed rx packets
+ * @tx_good_pkt: packets (MSDUs & MMPDUs) transmitted to this station
+ * @tx_bad_pkt: failed tx packets
+ * @rx_ocast_good_pkt: unicast packets destined for others
+ */
+struct brcmf_pktcnt_le {
+ __le32 rx_good_pkt;
+ __le32 rx_bad_pkt;
+ __le32 tx_good_pkt;
+ __le32 tx_bad_pkt;
+ __le32 rx_ocast_good_pkt;
+};
+
+#endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
new file mode 100644
index 000000000000..f82c9ab5480b
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
@@ -0,0 +1,2269 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "debug.h"
+#include "bus.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "fweh.h"
+#include "fwsignal.h"
+#include "p2p.h"
+#include "cfg80211.h"
+#include "proto.h"
+#include "common.h"
+
+/**
+ * DOC: Firmware Signalling
+ *
+ * Firmware can send signals to host and vice versa, which are passed in the
+ * data packets using TLV based header. This signalling layer is on top of the
+ * BDC bus protocol layer.
+ */
+
+/*
+ * single definition for firmware-driver flow control tlv's.
+ *
+ * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).
+ * A length value 0 indicates variable length tlv.
+ */
+#define BRCMF_FWS_TLV_DEFLIST \
+ BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \
+ BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \
+ BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \
+ BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \
+ BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \
+ BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \
+ BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \
+ BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \
+ BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \
+ BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \
+ BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \
+ BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \
+ BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \
+ BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \
+ BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \
+ BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \
+ BRCMF_FWS_TLV_DEF(FILLER, 255, 0)
+
+/*
+ * enum brcmf_fws_tlv_type - definition of tlv identifiers.
+ */
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ BRCMF_FWS_TYPE_ ## name = id,
+enum brcmf_fws_tlv_type {
+ BRCMF_FWS_TLV_DEFLIST
+ BRCMF_FWS_TYPE_INVALID
+};
+#undef BRCMF_FWS_TLV_DEF
+
+/*
+ * enum brcmf_fws_tlv_len - definition of tlv lengths.
+ */
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ BRCMF_FWS_TYPE_ ## name ## _LEN = (len),
+enum brcmf_fws_tlv_len {
+ BRCMF_FWS_TLV_DEFLIST
+};
+#undef BRCMF_FWS_TLV_DEF
+
+#ifdef DEBUG
+/*
+ * brcmf_fws_tlv_names - array of tlv names.
+ */
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ { id, #name },
+static struct {
+ enum brcmf_fws_tlv_type id;
+ const char *name;
+} brcmf_fws_tlv_names[] = {
+ BRCMF_FWS_TLV_DEFLIST
+};
+#undef BRCMF_FWS_TLV_DEF
+
+
+static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)
+ if (brcmf_fws_tlv_names[i].id == id)
+ return brcmf_fws_tlv_names[i].name;
+
+ return "INVALID";
+}
+#else
+static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
+{
+ return "NODEBUG";
+}
+#endif /* DEBUG */
+
+/*
+ * The PKTTAG tlv has additional bytes when firmware-signalling
+ * mode has REUSESEQ flag set.
+ */
+#define BRCMF_FWS_TYPE_SEQ_LEN 2
+
+/*
+ * flags used to enable tlv signalling from firmware.
+ */
+#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001
+#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002
+#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004
+#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008
+#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010
+#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020
+#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040
+
+#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32
+#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff
+
+#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0
+#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1
+#define BRCMF_FWS_FLOWCONTROL_HIWATER 128
+#define BRCMF_FWS_FLOWCONTROL_LOWATER 64
+
+#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
+#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02
+
+#define BRCMF_FWS_RET_OK_NOSCHEDULE 0
+#define BRCMF_FWS_RET_OK_SCHEDULE 1
+
+#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */
+#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \
+ ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \
+ (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))
+#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \
+ (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)
+
+/**
+ * enum brcmf_fws_skb_state - indicates processing state of skb.
+ *
+ * @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_TIM
+};
+
+/**
+ * struct brcmf_skbuff_cb - control buffer associated with skbuff.
+ *
+ * @bus_flags: 2 bytes reserved for bus specific parameters
+ * @if_flags: holds interface index and packet related flags.
+ * @htod: host to device packet identifier (used in PKTTAG tlv).
+ * @htod_seq: this 16-bit is original seq number for every suppress packet.
+ * @state: transmit state of the packet.
+ * @mac: descriptor related to destination for this packet.
+ *
+ * This information is stored in control buffer struct sk_buff::cb, which
+ * provides 48 bytes of storage so this structure should not exceed that.
+ */
+struct brcmf_skbuff_cb {
+ u16 bus_flags;
+ u16 if_flags;
+ u32 htod;
+ u16 htod_seq;
+ enum brcmf_fws_skb_state state;
+ struct brcmf_fws_mac_descriptor *mac;
+};
+
+/*
+ * macro casting skbuff control buffer to struct brcmf_skbuff_cb.
+ */
+#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb))
+
+/*
+ * sk_buff control if flags
+ *
+ * b[11] - packet sent upon firmware request.
+ * b[10] - packet only contains signalling data.
+ * b[9] - packet is a tx packet.
+ * b[8] - packet used requested credit
+ * b[7] - interface in AP mode.
+ * b[3:0] - interface index.
+ */
+#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800
+#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11
+#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400
+#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_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_INDEX_MASK 0x000f
+#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0
+
+#define brcmf_skb_if_flags_set_field(skb, field, value) \
+ brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))
+#define brcmf_skb_if_flags_get_field(skb, field) \
+ brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)
+
+/*
+ * sk_buff control packet identifier
+ *
+ * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
+ *
+ * - Generated at the host (e.g. dhd)
+ * - Seen as a generic sequence number by firmware except for the flags field.
+ *
+ * Generation : b[31] => generation number for this packet [host->fw]
+ * OR, current generation number [fw->host]
+ * Flags : b[30:27] => command, status flags
+ * FIFO-AC : b[26:24] => AC-FIFO id
+ * h-slot : b[23:8] => hanger-slot
+ * freerun : b[7:0] => A free running counter
+ */
+#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000
+#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31
+#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000
+#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27
+#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000
+#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24
+#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_set_field(skb, field, value) \
+ brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))
+#define brcmf_skb_htod_tag_get_field(skb, field) \
+ brcmu_maskget32(brcmf_skbcb(skb)->htod, \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
+
+#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000
+#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13
+#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000
+#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12
+#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff
+#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0
+
+#define brcmf_skb_htod_seq_set_field(skb, field, value) \
+ brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))
+#define brcmf_skb_htod_seq_get_field(skb, field) \
+ brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)
+
+#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000
+#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31
+#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000
+#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27
+#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000
+#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24
+#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00
+#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8
+#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF
+#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0
+
+#define brcmf_txstatus_get_field(txs, field) \
+ brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
+ BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
+
+/* How long to defer borrowing in jiffies */
+#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
+
+/**
+ * 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.
+ * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic.
+ * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only).
+ * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only).
+ * @BRCMF_FWS_FIFO_COUNT: number of fifos.
+ */
+enum brcmf_fws_fifo {
+ 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,
+ BRCMF_FWS_FIFO_BCMC,
+ BRCMF_FWS_FIFO_ATIM,
+ BRCMF_FWS_FIFO_COUNT
+};
+
+/**
+ * enum brcmf_fws_txstatus - txstatus flag values.
+ *
+ * @BRCMF_FWS_TXSTATUS_DISCARD:
+ * host is free to discard the packet.
+ * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS:
+ * 802.11 core suppressed the packet.
+ * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS:
+ * 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_HOST_TOSSED
+};
+
+enum brcmf_fws_fcmode {
+ BRCMF_FWS_FCMODE_NONE,
+ BRCMF_FWS_FCMODE_IMPLIED_CREDIT,
+ BRCMF_FWS_FCMODE_EXPLICIT_CREDIT
+};
+
+enum brcmf_fws_mac_desc_state {
+ BRCMF_FWS_STATE_OPEN = 1,
+ BRCMF_FWS_STATE_CLOSE
+};
+
+/**
+ * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
+ *
+ * @occupied: slot is in use.
+ * @mac_handle: handle for mac entry determined by firmware.
+ * @interface_id: interface index.
+ * @state: current state.
+ * @suppressed: mac entry is suppressed.
+ * @generation: generation bit.
+ * @ac_bitmap: ac queue bitmap.
+ * @requested_credit: credits requested by firmware.
+ * @ea: ethernet address.
+ * @seq: per-node free-running sequence.
+ * @psq: power-save queue.
+ * @transit_count: packet in transit to firmware.
+ */
+struct brcmf_fws_mac_descriptor {
+ char name[16];
+ u8 occupied;
+ u8 mac_handle;
+ u8 interface_id;
+ u8 state;
+ bool suppressed;
+ u8 generation;
+ u8 ac_bitmap;
+ u8 requested_credit;
+ u8 requested_packet;
+ u8 ea[ETH_ALEN];
+ u8 seq[BRCMF_FWS_FIFO_COUNT];
+ struct pktq psq;
+ int transit_count;
+ int suppr_transit_count;
+ bool send_tim_signal;
+ u8 traffic_pending_bmp;
+ u8 traffic_lastreported_bmp;
+};
+
+#define BRCMF_FWS_HANGER_MAXITEMS 1024
+
+/**
+ * enum brcmf_fws_hanger_item_state - state of hanger item.
+ *
+ * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use.
+ * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use.
+ * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed.
+ */
+enum brcmf_fws_hanger_item_state {
+ BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,
+ BRCMF_FWS_HANGER_ITEM_STATE_INUSE,
+ BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED
+};
+
+
+/**
+ * struct brcmf_fws_hanger_item - single entry for tx pending packet.
+ *
+ * @state: entry is either free or occupied.
+ * @pkt: packet itself.
+ */
+struct brcmf_fws_hanger_item {
+ enum brcmf_fws_hanger_item_state state;
+ struct sk_buff *pkt;
+};
+
+/**
+ * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus.
+ *
+ * @pushed: packets pushed to await txstatus.
+ * @popped: packets popped upon handling txstatus.
+ * @failed_to_push: packets that could not be pushed.
+ * @failed_to_pop: packets that could not be popped.
+ * @failed_slotfind: packets for which failed to find an entry.
+ * @slot_pos: last returned item index for a free entry.
+ * @items: array of hanger items.
+ */
+struct brcmf_fws_hanger {
+ u32 pushed;
+ u32 popped;
+ u32 failed_to_push;
+ u32 failed_to_pop;
+ u32 failed_slotfind;
+ u32 slot_pos;
+ struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];
+};
+
+struct brcmf_fws_macdesc_table {
+ struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
+ struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];
+ struct brcmf_fws_mac_descriptor other;
+};
+
+struct brcmf_fws_stats {
+ u32 tlv_parse_failed;
+ u32 tlv_invalid_type;
+ u32 header_only_pkt;
+ u32 header_pulls;
+ u32 pkt2bus;
+ u32 send_pkts[5];
+ u32 requested_sent[5];
+ u32 generic_error;
+ u32 mac_update_failed;
+ u32 mac_ps_update_failed;
+ u32 if_update_failed;
+ u32 packet_request_failed;
+ u32 credit_request_failed;
+ u32 rollback_success;
+ u32 rollback_failed;
+ u32 delayq_full_error;
+ u32 supprq_full_error;
+ u32 txs_indicate;
+ u32 txs_discard;
+ 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_fws_info {
+ struct brcmf_pub *drvr;
+ spinlock_t spinlock;
+ ulong flags;
+ struct brcmf_fws_stats stats;
+ struct brcmf_fws_hanger hanger;
+ enum brcmf_fws_fcmode fcmode;
+ bool fw_signals;
+ bool bcmc_credit_check;
+ struct brcmf_fws_macdesc_table desc;
+ struct workqueue_struct *fws_wq;
+ struct work_struct fws_dequeue_work;
+ u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
+ int fifo_credit[BRCMF_FWS_FIFO_COUNT];
+ int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
+ int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
+ u32 fifo_credit_map;
+ u32 fifo_delay_map;
+ unsigned long borrow_defer_timestamp;
+ bool bus_flow_blocked;
+ bool creditmap_received;
+ u8 mode;
+ bool avoid_queueing;
+};
+
+/*
+ * brcmf_fws_prio2fifo - mapping from 802.1d priority to firmware fifo index.
+ */
+static const int brcmf_fws_prio2fifo[] = {
+ BRCMF_FWS_FIFO_AC_BE,
+ BRCMF_FWS_FIFO_AC_BK,
+ BRCMF_FWS_FIFO_AC_BK,
+ BRCMF_FWS_FIFO_AC_BE,
+ BRCMF_FWS_FIFO_AC_VI,
+ BRCMF_FWS_FIFO_AC_VI,
+ BRCMF_FWS_FIFO_AC_VO,
+ BRCMF_FWS_FIFO_AC_VO
+};
+
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ case BRCMF_FWS_TYPE_ ## name: \
+ return len;
+
+/**
+ * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
+ *
+ * @fws: firmware-signalling information.
+ * @id: identifier of the TLV.
+ *
+ * Return: the specified length for the given TLV; Otherwise -EINVAL.
+ */
+static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
+ enum brcmf_fws_tlv_type id)
+{
+ switch (id) {
+ BRCMF_FWS_TLV_DEFLIST
+ default:
+ fws->stats.tlv_invalid_type++;
+ break;
+ }
+ return -EINVAL;
+}
+#undef BRCMF_FWS_TLV_DEF
+
+static void brcmf_fws_lock(struct brcmf_fws_info *fws)
+ __acquires(&fws->spinlock)
+{
+ spin_lock_irqsave(&fws->spinlock, fws->flags);
+}
+
+static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
+ __releases(&fws->spinlock)
+{
+ spin_unlock_irqrestore(&fws->spinlock, fws->flags);
+}
+
+static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
+{
+ u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
+ return ifidx == *(int *)arg;
+}
+
+static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
+ int ifidx)
+{
+ bool (*matchfn)(struct sk_buff *, void *) = NULL;
+ struct sk_buff *skb;
+ int prec;
+
+ if (ifidx != -1)
+ matchfn = brcmf_fws_ifidx_match;
+ for (prec = 0; prec < q->num_prec; prec++) {
+ skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
+ while (skb) {
+ brcmu_pkt_buf_free_skb(skb);
+ skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
+ }
+ }
+}
+
+static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
+{
+ int i;
+
+ memset(hanger, 0, sizeof(*hanger));
+ for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
+ hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+}
+
+static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
+{
+ u32 i;
+
+ i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
+
+ while (i != h->slot_pos) {
+ if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ h->slot_pos = i;
+ goto done;
+ }
+ i++;
+ if (i == BRCMF_FWS_HANGER_MAXITEMS)
+ i = 0;
+ }
+ brcmf_err("all slots occupied\n");
+ h->failed_slotfind++;
+ i = BRCMF_FWS_HANGER_MAXITEMS;
+done:
+ return i;
+}
+
+static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
+ struct sk_buff *pkt, u32 slot_id)
+{
+ if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+ return -ENOENT;
+
+ if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ brcmf_err("slot is not free\n");
+ h->failed_to_push++;
+ return -EINVAL;
+ }
+
+ h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;
+ h->items[slot_id].pkt = pkt;
+ h->pushed++;
+ return 0;
+}
+
+static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
+ u32 slot_id, struct sk_buff **pktout,
+ bool remove_item)
+{
+ if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+ return -ENOENT;
+
+ if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ brcmf_err("entry not in use\n");
+ h->failed_to_pop++;
+ return -EINVAL;
+ }
+
+ *pktout = h->items[slot_id].pkt;
+ if (remove_item) {
+ h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+ h->items[slot_id].pkt = NULL;
+ h->popped++;
+ }
+ return 0;
+}
+
+static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
+ u32 slot_id)
+{
+ if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+ return -ENOENT;
+
+ if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ brcmf_err("entry not in use\n");
+ return -EINVAL;
+ }
+
+ h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
+ return 0;
+}
+
+static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
+ bool (*fn)(struct sk_buff *, void *),
+ int ifidx)
+{
+ struct brcmf_fws_hanger *h = &fws->hanger;
+ struct sk_buff *skb;
+ int i;
+ enum brcmf_fws_hanger_item_state s;
+
+ for (i = 0; i < ARRAY_SIZE(h->items); i++) {
+ s = h->items[i].state;
+ if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
+ s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
+ skb = h->items[i].pkt;
+ if (fn == NULL || fn(skb, &ifidx)) {
+ /* suppress packets freed from psq */
+ if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)
+ brcmu_pkt_buf_free_skb(skb);
+ h->items[i].state =
+ BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+ }
+ }
+ }
+}
+
+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->bsscfgidx instead */
+ desc->interface_id = ifidx;
+ desc->ac_bitmap = 0xff; /* update this when handling APSD */
+ if (addr)
+ memcpy(&desc->ea[0], addr, ETH_ALEN);
+}
+
+static
+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_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ int i;
+
+ if (ea == NULL)
+ return ERR_PTR(-EINVAL);
+
+ entry = &fws->desc.nodes[0];
+ for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {
+ if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
+ return entry;
+ entry++;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+static struct brcmf_fws_mac_descriptor*
+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;
+
+ multicast = is_multicast_ether_addr(da);
+
+ /* 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.
+ */
+ if (multicast && ifp->fws_desc) {
+ entry = ifp->fws_desc;
+ goto done;
+ }
+
+ entry = brcmf_fws_macdesc_lookup(fws, da);
+ if (IS_ERR(entry))
+ entry = ifp->fws_desc;
+
+done:
+ return entry;
+}
+
+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;
+
+ /* for unique destination entries the related interface
+ * may be closed.
+ */
+ if (entry->mac_handle) {
+ if_entry = &fws->desc.iface[entry->interface_id];
+ if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
+ return true;
+ }
+ /* an entry is closed when the state is closed and
+ * the firmware did not request anything.
+ */
+ closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
+ !entry->requested_credit && !entry->requested_packet;
+
+ /* Or firmware does not allow traffic for given fifo */
+ return closed || !(entry->ac_bitmap & BIT(fifo));
+}
+
+static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
+ struct brcmf_fws_mac_descriptor *entry,
+ int ifidx)
+{
+ if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
+ brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
+ entry->occupied = !!(entry->psq.len);
+ }
+}
+
+static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
+ bool (*fn)(struct sk_buff *, void *),
+ int ifidx)
+{
+ struct brcmf_fws_hanger_item *hi;
+ struct pktq *txq;
+ struct sk_buff *skb;
+ int prec;
+ u32 hslot;
+
+ txq = brcmf_bus_gettxq(fws->drvr->bus_if);
+ if (IS_ERR(txq)) {
+ brcmf_dbg(TRACE, "no txq to clean up\n");
+ return;
+ }
+
+ for (prec = 0; prec < txq->num_prec; prec++) {
+ skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
+ while (skb) {
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+ hi = &fws->hanger.items[hslot];
+ WARN_ON(skb != hi->pkt);
+ hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+ brcmu_pkt_buf_free_skb(skb);
+ skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
+ }
+ }
+}
+
+static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
+{
+ int i;
+ struct brcmf_fws_mac_descriptor *table;
+ bool (*matchfn)(struct sk_buff *, void *) = NULL;
+
+ if (fws == NULL)
+ return;
+
+ if (ifidx != -1)
+ matchfn = brcmf_fws_ifidx_match;
+
+ /* cleanup individual nodes */
+ table = &fws->desc.nodes[0];
+ for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
+ brcmf_fws_macdesc_cleanup(fws, &table[i], 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 u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
+{
+ struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+ u8 *wlh;
+ u16 data_offset = 0;
+ u8 fillers;
+ __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
+ __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);
+
+ brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",
+ entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
+ (le32_to_cpu(pkttag) >> 8) & 0xffff,
+ brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);
+ if (entry->send_tim_signal)
+ data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
+ data_offset += BRCMF_FWS_TYPE_SEQ_LEN;
+ /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
+ data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
+ fillers = round_up(data_offset, 4) - data_offset;
+ 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));
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
+ wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;
+ memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,
+ sizeof(pktseq));
+ }
+ wlh += wlh[1] + 2;
+
+ if (entry->send_tim_signal) {
+ entry->send_tim_signal = 0;
+ 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: handle %d bmp 0x%X\n",
+ entry->mac_handle, 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);
+
+ return (u8)(data_offset >> 2);
+}
+
+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_skbuff_cb *skcb;
+ s32 err;
+ u32 len;
+ u8 data_offset;
+ int ifidx;
+
+ /* check delayedQ and suppressQ in one call using bitmap */
+ if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
+ 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_SEQ_LEN +
+ 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;
+ skcb->htod = 0;
+ skcb->htod_seq = 0;
+ data_offset = brcmf_fws_hdrpush(fws, skb);
+ ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
+ brcmf_fws_unlock(fws);
+ err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
+ brcmf_fws_lock(fws);
+ if (err)
+ brcmu_pkt_buf_free_skb(skb);
+ return true;
+ }
+ return false;
+}
+
+static void
+brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
+ u8 if_id)
+{
+ struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
+
+ if (WARN_ON(!ifp))
+ return;
+
+ 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) {
+ fws->stats.fws_flow_block++;
+ brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
+ }
+ return;
+}
+
+static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
+{
+ brcmf_dbg(CTL, "rssi %d\n", rssi);
+ return 0;
+}
+
+static
+int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry, *existing;
+ u8 mac_handle;
+ u8 ifidx;
+ u8 *addr;
+
+ mac_handle = *data++;
+ ifidx = *data++;
+ addr = data;
+
+ entry = &fws->desc.nodes[mac_handle & 0x1F];
+ if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
+ if (entry->occupied) {
+ brcmf_dbg(TRACE, "deleting %s mac %pM\n",
+ entry->name, addr);
+ brcmf_fws_lock(fws);
+ brcmf_fws_macdesc_cleanup(fws, entry, -1);
+ brcmf_fws_macdesc_deinit(entry);
+ brcmf_fws_unlock(fws);
+ } else
+ fws->stats.mac_update_failed++;
+ return 0;
+ }
+
+ existing = brcmf_fws_macdesc_lookup(fws, addr);
+ if (IS_ERR(existing)) {
+ if (!entry->occupied) {
+ brcmf_fws_lock(fws);
+ entry->mac_handle = mac_handle;
+ brcmf_fws_macdesc_init(entry, addr, ifidx);
+ brcmf_fws_macdesc_set_name(fws, entry);
+ brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ BRCMF_FWS_PSQ_LEN);
+ brcmf_fws_unlock(fws);
+ brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
+ } else {
+ fws->stats.mac_update_failed++;
+ }
+ } else {
+ if (entry != existing) {
+ brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
+ brcmf_fws_lock(fws);
+ memcpy(entry, existing,
+ offsetof(struct brcmf_fws_mac_descriptor, psq));
+ entry->mac_handle = mac_handle;
+ brcmf_fws_macdesc_deinit(existing);
+ brcmf_fws_macdesc_set_name(fws, entry);
+ brcmf_fws_unlock(fws);
+ 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);
+ /* TODO: what should we do here: continue, reinit, .. */
+ }
+ }
+ return 0;
+}
+
+static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
+ u8 type, u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ u8 mac_handle;
+ int ret;
+
+ mac_handle = data[0];
+ entry = &fws->desc.nodes[mac_handle & 0x1F];
+ if (!entry->occupied) {
+ fws->stats.mac_ps_update_failed++;
+ return -ESRCH;
+ }
+ brcmf_fws_lock(fws);
+ /* 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;
+ ret = BRCMF_FWS_RET_OK_SCHEDULE;
+ } else {
+ entry->state = BRCMF_FWS_STATE_CLOSE;
+ 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);
+ ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ }
+ brcmf_fws_unlock(fws);
+ return ret;
+}
+
+static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
+ u8 type, u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ u8 ifidx;
+ int ret;
+
+ ifidx = data[0];
+
+ if (ifidx >= BRCMF_MAX_IFS) {
+ ret = -ERANGE;
+ goto fail;
+ }
+
+ entry = &fws->desc.iface[ifidx];
+ if (!entry->occupied) {
+ ret = -ESRCH;
+ goto fail;
+ }
+
+ brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
+ entry->name);
+ brcmf_fws_lock(fws);
+ switch (type) {
+ case BRCMF_FWS_TYPE_INTERFACE_OPEN:
+ entry->state = BRCMF_FWS_STATE_OPEN;
+ ret = BRCMF_FWS_RET_OK_SCHEDULE;
+ break;
+ case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
+ entry->state = BRCMF_FWS_STATE_CLOSE;
+ ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ break;
+ default:
+ ret = -EINVAL;
+ brcmf_fws_unlock(fws);
+ goto fail;
+ }
+ brcmf_fws_unlock(fws);
+ return ret;
+
+fail:
+ fws->stats.if_update_failed++;
+ return ret;
+}
+
+static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
+ u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+
+ entry = &fws->desc.nodes[data[1] & 0x1F];
+ if (!entry->occupied) {
+ if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
+ fws->stats.credit_request_failed++;
+ else
+ fws->stats.packet_request_failed++;
+ 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]);
+ brcmf_fws_lock(fws);
+ if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
+ entry->requested_credit = data[0];
+ else
+ entry->requested_packet = data[0];
+
+ entry->ac_bitmap = data[2];
+ brcmf_fws_unlock(fws);
+ 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)
+{
+ int lender_ac;
+ int *borrowed;
+ int *fifo_credit;
+
+ 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;
+ lender_ac--) {
+ borrowed = &fws->credits_borrowed[lender_ac];
+ if (*borrowed) {
+ fws->fifo_credit_map |= (1 << lender_ac);
+ fifo_credit = &fws->fifo_credit[lender_ac];
+ if (*borrowed >= credits) {
+ *borrowed -= credits;
+ *fifo_credit += credits;
+ return;
+ } else {
+ credits -= *borrowed;
+ *fifo_credit += *borrowed;
+ *borrowed = 0;
+ }
+ }
+ }
+ }
+
+ fws->fifo_credit[fifo] += credits;
+}
+
+static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
+{
+ /* only schedule dequeue when there are credits for delayed traffic */
+ if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
+ (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
+ queue_work(fws->fws_wq, &fws->fws_dequeue_work);
+}
+
+static int brcmf_fws_enq(struct brcmf_fws_info *fws,
+ enum brcmf_fws_skb_state state, int fifo,
+ struct sk_buff *p)
+{
+ int prec = 2 * fifo;
+ u32 *qfull_stat = &fws->stats.delayq_full_error;
+ struct brcmf_fws_mac_descriptor *entry;
+ struct pktq *pq;
+ struct sk_buff_head *queue;
+ struct sk_buff *p_head;
+ struct sk_buff *p_tail;
+ u32 fr_new;
+ u32 fr_compare;
+
+ entry = brcmf_skbcb(p)->mac;
+ if (entry == NULL) {
+ brcmf_err("no mac descriptor found for skb %p\n", p);
+ return -ENOENT;
+ }
+
+ 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;
+
+ /* Fix out of order delivery of frames. Dont assume frame */
+ /* can be inserted at the end, but look for correct position */
+ pq = &entry->psq;
+ if (pktq_full(pq) || pktq_pfull(pq, prec)) {
+ *qfull_stat += 1;
+ return -ENFILE;
+ }
+ queue = &pq->q[prec].skblist;
+
+ p_head = skb_peek(queue);
+ p_tail = skb_peek_tail(queue);
+ fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);
+
+ while (p_head != p_tail) {
+ fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
+ FREERUN);
+ /* be sure to handle wrap of 256 */
+ if (((fr_new > fr_compare) &&
+ ((fr_new - fr_compare) < 128)) ||
+ ((fr_new < fr_compare) &&
+ ((fr_compare - fr_new) > 128)))
+ break;
+ p_tail = skb_queue_prev(queue, p_tail);
+ }
+ /* Position found. Determine what to do */
+ if (p_tail == NULL) {
+ /* empty list */
+ __skb_queue_tail(queue, p);
+ } else {
+ fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
+ FREERUN);
+ if (((fr_new > fr_compare) &&
+ ((fr_new - fr_compare) < 128)) ||
+ ((fr_new < fr_compare) &&
+ ((fr_compare - fr_new) > 128))) {
+ /* After tail */
+ __skb_queue_after(queue, p_tail, p);
+ } else {
+ /* Before tail */
+ __skb_insert(p, p_tail->prev, p_tail, queue);
+ }
+ }
+
+ /* Complete the counters and statistics */
+ pq->len++;
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (u8) prec;
+ } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {
+ *qfull_stat += 1;
+ return -ENFILE;
+ }
+
+ /* increment total enqueued packet count */
+ fws->fifo_delay_map |= 1 << fifo;
+ fws->fifo_enqpkt[fifo]++;
+
+ /* update the sk_buff state */
+ brcmf_skbcb(p)->state = state;
+
+ /*
+ * A packet has been pushed so update traffic
+ * availability bitmap, if applicable
+ */
+ 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;
+}
+
+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 num_nodes;
+ int node_pos;
+ int prec_out;
+ int pmsk;
+ int i;
+
+ table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
+ num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);
+ node_pos = fws->deq_node_pos[fifo];
+
+ for (i = 0; i < num_nodes; i++) {
+ entry = &table[(node_pos + i) % num_nodes];
+ if (!entry->occupied ||
+ brcmf_fws_macdesc_closed(fws, entry, fifo))
+ continue;
+
+ if (entry->suppressed)
+ pmsk = 2;
+ else
+ pmsk = 3;
+ p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
+ if (p == NULL) {
+ if (entry->suppressed) {
+ if (entry->suppr_transit_count)
+ continue;
+ entry->suppressed = false;
+ p = brcmu_pktq_mdeq(&entry->psq,
+ 1 << (fifo * 2), &prec_out);
+ }
+ }
+ if (p == NULL)
+ continue;
+
+ 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;
+ brcmf_fws_flow_control_check(fws, &entry->psq,
+ brcmf_skb_if_flags_get_field(p,
+ INDEX)
+ );
+ /*
+ * A packet has been picked up, update traffic
+ * availability bitmap, if applicable
+ */
+ brcmf_fws_tim_update(fws, entry, fifo, false);
+
+ /*
+ * decrement total enqueued fifo packets and
+ * clear delay bitmap if done.
+ */
+ fws->fifo_enqpkt[fifo]--;
+ if (fws->fifo_enqpkt[fifo] == 0)
+ fws->fifo_delay_map &= ~(1 << fifo);
+ goto done;
+ }
+ p = NULL;
+done:
+ brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
+ return p;
+}
+
+static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
+ struct sk_buff *skb,
+ u32 genbit, u16 seq)
+{
+ struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+ u32 hslot;
+ int ret;
+
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+
+ /* this packet was suppressed */
+ if (!entry->suppressed) {
+ entry->suppressed = true;
+ entry->suppr_transit_count = entry->transit_count;
+ brcmf_dbg(DATA, "suppress %s: transit %d\n",
+ entry->name, entry->transit_count);
+ }
+
+ entry->generation = genbit;
+
+ brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
+ brcmf_skbcb(skb)->htod_seq = seq;
+ if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
+ brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
+ brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
+ } else {
+ brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
+ }
+ ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
+
+ if (ret != 0) {
+ /* suppress q is full drop this packet */
+ brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
+ } else {
+ /* Mark suppressed to avoid a double free during wlfc cleanup */
+ brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
+ }
+
+ return ret;
+}
+
+static int
+brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
+ u32 genbit, u16 seq)
+{
+ 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;
+ struct brcmf_if *ifp;
+
+ brcmf_dbg(DATA, "flags %d\n", flags);
+
+ if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
+ fws->stats.txs_discard++;
+ else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
+ fws->stats.txs_supp_core++;
+ remove_from_hanger = false;
+ } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
+ fws->stats.txs_supp_ps++;
+ 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");
+
+ ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
+ remove_from_hanger);
+ if (ret != 0) {
+ brcmf_err("no packet in hanger slot: hslot=%d\n", hslot);
+ return ret;
+ }
+
+ 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 %d htod %X seq %X\n", entry->name, flags,
+ skcb->htod, seq);
+
+ /* pick up the implicit credit from this packet */
+ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
+ 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);
+
+ ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
+ if (ret) {
+ brcmu_pkt_buf_free_skb(skb);
+ return -EINVAL;
+ }
+ if (!remove_from_hanger)
+ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
+ genbit, seq);
+ if (remove_from_hanger || ret)
+ brcmf_txfinalize(ifp, skb, true);
+
+ return 0;
+}
+
+static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
+ u8 *data)
+{
+ int i;
+
+ if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
+ brcmf_dbg(INFO, "ignored\n");
+ return BRCMF_FWS_RET_OK_NOSCHEDULE;
+ }
+
+ brcmf_dbg(DATA, "enter: data %pM\n", data);
+ brcmf_fws_lock(fws);
+ for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
+ brcmf_fws_return_credits(fws, i, data[i]);
+
+ brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
+ fws->fifo_delay_map);
+ brcmf_fws_unlock(fws);
+ return BRCMF_FWS_RET_OK_SCHEDULE;
+}
+
+static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
+{
+ __le32 status_le;
+ __le16 seq_le;
+ u32 status;
+ u32 hslot;
+ u32 genbit;
+ u8 flags;
+ u16 seq;
+
+ fws->stats.txs_indicate++;
+ memcpy(&status_le, data, sizeof(status_le));
+ status = le32_to_cpu(status_le);
+ flags = brcmf_txstatus_get_field(status, FLAGS);
+ hslot = brcmf_txstatus_get_field(status, HSLOT);
+ genbit = brcmf_txstatus_get_field(status, GENERATION);
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
+ memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN],
+ sizeof(seq_le));
+ seq = le16_to_cpu(seq_le);
+ } else {
+ seq = 0;
+ }
+
+ brcmf_fws_lock(fws);
+ brcmf_fws_txs_process(fws, flags, hslot, genbit, seq);
+ brcmf_fws_unlock(fws);
+ return BRCMF_FWS_RET_OK_NOSCHEDULE;
+}
+
+static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
+{
+ __le32 timestamp;
+
+ memcpy(&timestamp, &data[2], sizeof(timestamp));
+ brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
+ le32_to_cpu(timestamp));
+ return 0;
+}
+
+static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_fws_info *fws = ifp->drvr->fws;
+ int i;
+ u8 *credits = data;
+
+ if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
+ 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(fws);
+ for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
+ if (*credits)
+ fws->fifo_credit_map |= 1 << i;
+ else
+ fws->fifo_credit_map &= ~(1 << i);
+ fws->fifo_credit[i] = *credits++;
+ }
+ brcmf_fws_schedule_deq(fws);
+ brcmf_fws_unlock(fws);
+ 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;
+
+ if (fws) {
+ brcmf_fws_lock(fws);
+ fws->bcmc_credit_check = true;
+ brcmf_fws_unlock(fws);
+ }
+ return 0;
+}
+
+void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
+{
+ struct brcmf_skb_reorder_data *rd;
+ struct brcmf_fws_info *fws = ifp->drvr->fws;
+ u8 *signal_data;
+ s16 data_len;
+ u8 type;
+ u8 len;
+ u8 *data;
+ s32 status;
+ s32 err;
+
+ brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
+ ifp->ifidx, skb->len, siglen);
+
+ WARN_ON(siglen > skb->len);
+
+ if (!siglen)
+ return;
+ /* if flow control disabled, skip to packet data and leave */
+ if ((!fws) || (!fws->fw_signals)) {
+ skb_pull(skb, siglen);
+ return;
+ }
+
+ fws->stats.header_pulls++;
+ data_len = siglen;
+ signal_data = skb->data;
+
+ status = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ while (data_len > 0) {
+ /* extract tlv info */
+ type = signal_data[0];
+
+ /* FILLER type is actually not a TLV, but
+ * a single byte that can be skipped.
+ */
+ if (type == BRCMF_FWS_TYPE_FILLER) {
+ signal_data += 1;
+ data_len -= 1;
+ continue;
+ }
+ len = signal_data[1];
+ data = signal_data + 2;
+
+ 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))
+ break;
+
+ err = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ switch (type) {
+ case BRCMF_FWS_TYPE_COMP_TXSTATUS:
+ break;
+ case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ rd->reorder = data;
+ break;
+ case BRCMF_FWS_TYPE_MACDESC_ADD:
+ case BRCMF_FWS_TYPE_MACDESC_DEL:
+ brcmf_fws_macdesc_indicate(fws, type, data);
+ break;
+ case BRCMF_FWS_TYPE_MAC_OPEN:
+ case BRCMF_FWS_TYPE_MAC_CLOSE:
+ err = brcmf_fws_macdesc_state_indicate(fws, type, data);
+ break;
+ case BRCMF_FWS_TYPE_INTERFACE_OPEN:
+ case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
+ err = brcmf_fws_interface_state_indicate(fws, type,
+ data);
+ break;
+ case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
+ case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
+ err = brcmf_fws_request_indicate(fws, type, data);
+ break;
+ case BRCMF_FWS_TYPE_TXSTATUS:
+ brcmf_fws_txstatus_indicate(fws, data);
+ break;
+ case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
+ err = brcmf_fws_fifocreditback_indicate(fws, data);
+ break;
+ case BRCMF_FWS_TYPE_RSSI:
+ brcmf_fws_rssi_indicate(fws, *data);
+ break;
+ case BRCMF_FWS_TYPE_TRANS_ID:
+ brcmf_fws_dbg_seqnum_check(fws, data);
+ break;
+ case BRCMF_FWS_TYPE_PKTTAG:
+ case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
+ default:
+ fws->stats.tlv_invalid_type++;
+ break;
+ }
+ if (err == BRCMF_FWS_RET_OK_SCHEDULE)
+ status = BRCMF_FWS_RET_OK_SCHEDULE;
+ signal_data += len + 2;
+ data_len -= len + 2;
+ }
+
+ if (data_len != 0)
+ fws->stats.tlv_parse_failed++;
+
+ if (status == BRCMF_FWS_RET_OK_SCHEDULE)
+ brcmf_fws_schedule_deq(fws);
+
+ /* signalling processing result does
+ * not affect the actual ethernet packet.
+ */
+ skb_pull(skb, siglen);
+
+ /* this may be a signal-only packet
+ */
+ if (skb->len == 0)
+ fws->stats.header_only_pkt++;
+}
+
+static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
+ struct sk_buff *p)
+{
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
+ struct brcmf_fws_mac_descriptor *entry = skcb->mac;
+ u8 flags;
+
+ if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)
+ brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
+ flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
+ if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
+ /*
+ * 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);
+ return brcmf_fws_hdrpush(fws, p);
+}
+
+static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
+ struct sk_buff *skb, int fifo)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ struct sk_buff *pktout;
+ int qidx, hslot;
+ int rc = 0;
+
+ entry = brcmf_skbcb(skb)->mac;
+ if (entry->occupied) {
+ qidx = 2 * fifo;
+ if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
+ qidx++;
+
+ pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
+ if (pktout == NULL) {
+ brcmf_err("%s queue %d full\n", entry->name, qidx);
+ rc = -ENOSPC;
+ }
+ } else {
+ brcmf_err("%s entry removed\n", entry->name);
+ rc = -ENOENT;
+ }
+
+ if (rc) {
+ fws->stats.rollback_failed++;
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+ brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
+ hslot, 0, 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)) {
+ 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]) {
+ fws->credits_borrowed[lender_ac]++;
+ fws->fifo_credit[lender_ac]--;
+ if (fws->fifo_credit[lender_ac] == 0)
+ fws->fifo_credit_map &= ~(1 << 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;
+}
+
+static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
+ struct sk_buff *skb)
+{
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
+ struct brcmf_fws_mac_descriptor *entry;
+ int rc;
+ u8 ifidx;
+ u8 data_offset;
+
+ entry = skcb->mac;
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
+ entry->transit_count++;
+ if (entry->suppressed)
+ entry->suppr_transit_count++;
+ ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
+ brcmf_fws_unlock(fws);
+ rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
+ brcmf_fws_lock(fws);
+ brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
+ skcb->if_flags, skcb->htod, rc);
+ if (rc < 0) {
+ entry->transit_count--;
+ if (entry->suppressed)
+ entry->suppr_transit_count--;
+ (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
+ goto rollback;
+ }
+
+ fws->stats.pkt2bus++;
+ 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, fifo);
+ return rc;
+}
+
+static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
+ int fifo)
+{
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
+ int rc, hslot;
+
+ skcb->htod = 0;
+ skcb->htod_seq = 0;
+ hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
+ brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
+ brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
+ brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
+ rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
+ if (!rc)
+ skcb->mac->seq[fifo]++;
+ else
+ fws->stats.generic_error++;
+ return rc;
+}
+
+int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_fws_info *fws = drvr->fws;
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ int fifo = BRCMF_FWS_FIFO_BCMC;
+ bool multicast = is_multicast_ether_addr(eh->h_dest);
+ int rc = 0;
+
+ brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
+ /* determine the priority */
+ if (!skb->priority)
+ skb->priority = cfg80211_classify8021d(skb, NULL);
+
+ drvr->tx_multicast += !!multicast;
+
+ if (fws->avoid_queueing) {
+ rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
+ if (rc < 0)
+ brcmf_txfinalize(ifp, skb, false);
+ return rc;
+ }
+
+ /* set control buffer information */
+ skcb->if_flags = 0;
+ 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_fws_lock(fws);
+ if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
+ fws->borrow_defer_timestamp = jiffies +
+ BRCMF_FWS_BORROW_DEFER_PERIOD;
+
+ skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
+ brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
+ eh->h_dest, multicast, fifo);
+ if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
+ brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
+ brcmf_fws_schedule_deq(fws);
+ } else {
+ brcmf_err("drop skb: no hanger slot\n");
+ brcmf_txfinalize(ifp, skb, false);
+ rc = -ENOMEM;
+ }
+ brcmf_fws_unlock(fws);
+
+ return rc;
+}
+
+void brcmf_fws_reset_interface(struct brcmf_if *ifp)
+{
+ struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
+
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
+ if (!entry)
+ return;
+
+ brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
+}
+
+void brcmf_fws_add_interface(struct brcmf_if *ifp)
+{
+ struct brcmf_fws_info *fws = ifp->drvr->fws;
+ struct brcmf_fws_mac_descriptor *entry;
+
+ if (!ifp->ndev)
+ return;
+
+ entry = &fws->desc.iface[ifp->ifidx];
+ ifp->fws_desc = entry;
+ 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)
+{
+ struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
+
+ if (!entry)
+ return;
+
+ brcmf_fws_lock(ifp->drvr->fws);
+ ifp->fws_desc = NULL;
+ brcmf_dbg(TRACE, "deleting %s\n", entry->name);
+ brcmf_fws_macdesc_deinit(entry);
+ brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
+ brcmf_fws_unlock(ifp->drvr->fws);
+}
+
+static void brcmf_fws_dequeue_worker(struct work_struct *worker)
+{
+ struct brcmf_fws_info *fws;
+ struct brcmf_pub *drvr;
+ struct sk_buff *skb;
+ int fifo;
+ u32 hslot;
+ u32 ifidx;
+ int ret;
+
+ fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
+ drvr = fws->drvr;
+
+ brcmf_fws_lock(fws);
+ for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
+ fifo--) {
+ if (!brcmf_fws_fc_active(fws)) {
+ while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
+ hslot = brcmf_skb_htod_tag_get_field(skb,
+ HSLOT);
+ brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
+ &skb, true);
+ ifidx = brcmf_skb_if_flags_get_field(skb,
+ INDEX);
+ /* Use proto layer to send data frame */
+ brcmf_fws_unlock(fws);
+ ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
+ brcmf_fws_lock(fws);
+ if (ret < 0)
+ brcmf_txfinalize(brcmf_get_ifp(drvr,
+ ifidx),
+ skb, false);
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ continue;
+ }
+ while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
+ (fifo == BRCMF_FWS_FIFO_BCMC))) {
+ skb = brcmf_fws_deq(fws, fifo);
+ if (!skb)
+ break;
+ fws->fifo_credit[fifo]--;
+ if (brcmf_fws_commit_skb(fws, fifo, skb))
+ break;
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
+ (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))
+ break;
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ }
+ }
+ brcmf_fws_unlock(fws);
+}
+
+#ifdef DEBUG
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats;
+
+ seq_printf(seq,
+ "header_pulls: %u\n"
+ "header_only_pkt: %u\n"
+ "tlv_parse_failed: %u\n"
+ "tlv_invalid_type: %u\n"
+ "mac_update_fails: %u\n"
+ "ps_update_fails: %u\n"
+ "if_update_fails: %u\n"
+ "pkt2bus: %u\n"
+ "generic_error: %u\n"
+ "rollback_success: %u\n"
+ "rollback_failed: %u\n"
+ "delayq_full: %u\n"
+ "supprq_full: %u\n"
+ "txs_indicate: %u\n"
+ "txs_discard: %u\n"
+ "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"
+ "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+ fwstats->header_pulls,
+ fwstats->header_only_pkt,
+ fwstats->tlv_parse_failed,
+ fwstats->tlv_invalid_type,
+ fwstats->mac_update_failed,
+ fwstats->mac_ps_update_failed,
+ fwstats->if_update_failed,
+ fwstats->pkt2bus,
+ fwstats->generic_error,
+ fwstats->rollback_success,
+ fwstats->rollback_failed,
+ fwstats->delayq_full_error,
+ fwstats->supprq_full_error,
+ fwstats->txs_indicate,
+ fwstats->txs_discard,
+ 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->requested_sent[0],
+ fwstats->requested_sent[1],
+ fwstats->requested_sent[2],
+ fwstats->requested_sent[3],
+ fwstats->requested_sent[4]);
+
+ return 0;
+}
+#else
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+ return 0;
+}
+#endif
+
+int brcmf_fws_init(struct brcmf_pub *drvr)
+{
+ struct brcmf_fws_info *fws;
+ struct brcmf_if *ifp;
+ u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
+ int rc;
+ u32 mode;
+
+ drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
+ if (!drvr->fws) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ fws = drvr->fws;
+
+ spin_lock_init(&fws->spinlock);
+
+ /* set linkage back */
+ fws->drvr = drvr;
+ fws->fcmode = drvr->settings->fcmode;
+
+ if ((drvr->bus_if->always_use_fws_queue == false) &&
+ (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {
+ fws->avoid_queueing = true;
+ brcmf_dbg(INFO, "FWS queueing will be avoided\n");
+ return 0;
+ }
+
+ fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
+ if (fws->fws_wq == NULL) {
+ brcmf_err("workqueue creation failed\n");
+ rc = -EBADF;
+ goto fail;
+ }
+ INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
+
+ /* enable firmware signalling if fcmode active */
+ if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
+ tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
+ BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
+ BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
+ BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
+
+ rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
+ brcmf_fws_notify_credit_map);
+ if (rc < 0) {
+ 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
+ * continue. Set mode back to none indicating not enabled.
+ */
+ fws->fw_signals = true;
+ ifp = brcmf_get_ifp(drvr, 0);
+ if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
+ brcmf_err("failed to set bdcv2 tlv signaling\n");
+ fws->fcmode = BRCMF_FWS_FCMODE_NONE;
+ fws->fw_signals = false;
+ }
+
+ if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
+ brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
+
+ /* Enable seq number reuse, if supported */
+ if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
+ mode = 0;
+ BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
+ if (brcmf_fil_iovar_int_set(ifp,
+ "wlfc_mode", mode) == 0) {
+ BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
+ }
+ }
+ }
+
+ brcmf_fws_hanger_init(&fws->hanger);
+ brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
+ brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
+ brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ BRCMF_FWS_PSQ_LEN);
+
+ /* create debugfs file for statistics */
+ brcmf_debugfs_add_entry(drvr, "fws_stats",
+ brcmf_debugfs_fws_stats_read);
+
+ brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
+ fws->fw_signals ? "enabled" : "disabled", tlv);
+ return 0;
+
+fail:
+ brcmf_fws_deinit(drvr);
+ return rc;
+}
+
+void brcmf_fws_deinit(struct brcmf_pub *drvr)
+{
+ struct brcmf_fws_info *fws = drvr->fws;
+
+ if (!fws)
+ return;
+
+ if (drvr->fws->fws_wq)
+ destroy_workqueue(drvr->fws->fws_wq);
+
+ /* cleanup */
+ brcmf_fws_lock(fws);
+ brcmf_fws_cleanup(fws, -1);
+ drvr->fws = NULL;
+ brcmf_fws_unlock(fws);
+
+ /* free top structure */
+ kfree(fws);
+}
+
+bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
+{
+ if (!fws->creditmap_received)
+ return false;
+
+ return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
+}
+
+void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
+{
+ u32 hslot;
+
+ if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+ brcmf_fws_lock(fws);
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+ brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0);
+ brcmf_fws_unlock(fws);
+}
+
+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++;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
index a36bac17eafd..a36bac17eafd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
new file mode 100644
index 000000000000..c2bdb91746cf
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -0,0 +1,1560 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*******************************************************************************
+ * Communicates with the dongle by using dcmd codes.
+ * For certain dcmd codes, the dongle interprets string data from the host.
+ ******************************************************************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "core.h"
+#include "debug.h"
+#include "proto.h"
+#include "msgbuf.h"
+#include "commonring.h"
+#include "flowring.h"
+#include "bus.h"
+#include "tracepoint.h"
+
+
+#define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
+
+#define MSGBUF_TYPE_GEN_STATUS 0x1
+#define MSGBUF_TYPE_RING_STATUS 0x2
+#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3
+#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4
+#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5
+#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6
+#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7
+#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8
+#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9
+#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA
+#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB
+#define MSGBUF_TYPE_IOCTL_CMPLT 0xC
+#define MSGBUF_TYPE_EVENT_BUF_POST 0xD
+#define MSGBUF_TYPE_WL_EVENT 0xE
+#define MSGBUF_TYPE_TX_POST 0xF
+#define MSGBUF_TYPE_TX_STATUS 0x10
+#define MSGBUF_TYPE_RXBUF_POST 0x11
+#define MSGBUF_TYPE_RX_CMPLT 0x12
+#define MSGBUF_TYPE_LPBK_DMAXFER 0x13
+#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14
+
+#define NR_TX_PKTIDS 2048
+#define NR_RX_PKTIDS 1024
+
+#define BRCMF_IOCTL_REQ_PKTID 0xFFFE
+
+#define BRCMF_MSGBUF_MAX_PKT_SIZE 2048
+#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD 32
+#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST 8
+#define BRCMF_MSGBUF_MAX_EVENTBUF_POST 8
+
+#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3 0x01
+#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5
+
+#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32
+#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96
+
+#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96
+#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
+#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
+
+
+struct msgbuf_common_hdr {
+ u8 msgtype;
+ u8 ifidx;
+ u8 flags;
+ u8 rsvd0;
+ __le32 request_id;
+};
+
+struct msgbuf_buf_addr {
+ __le32 low_addr;
+ __le32 high_addr;
+};
+
+struct msgbuf_ioctl_req_hdr {
+ struct msgbuf_common_hdr msg;
+ __le32 cmd;
+ __le16 trans_id;
+ __le16 input_buf_len;
+ __le16 output_buf_len;
+ __le16 rsvd0[3];
+ struct msgbuf_buf_addr req_buf_addr;
+ __le32 rsvd1[2];
+};
+
+struct msgbuf_tx_msghdr {
+ struct msgbuf_common_hdr msg;
+ u8 txhdr[ETH_HLEN];
+ u8 flags;
+ u8 seg_cnt;
+ struct msgbuf_buf_addr metadata_buf_addr;
+ struct msgbuf_buf_addr data_buf_addr;
+ __le16 metadata_buf_len;
+ __le16 data_len;
+ __le32 rsvd0;
+};
+
+struct msgbuf_rx_bufpost {
+ struct msgbuf_common_hdr msg;
+ __le16 metadata_buf_len;
+ __le16 data_buf_len;
+ __le32 rsvd0;
+ struct msgbuf_buf_addr metadata_buf_addr;
+ struct msgbuf_buf_addr data_buf_addr;
+};
+
+struct msgbuf_rx_ioctl_resp_or_event {
+ struct msgbuf_common_hdr msg;
+ __le16 host_buf_len;
+ __le16 rsvd0[3];
+ struct msgbuf_buf_addr host_buf_addr;
+ __le32 rsvd1[4];
+};
+
+struct msgbuf_completion_hdr {
+ __le16 status;
+ __le16 flow_ring_id;
+};
+
+struct msgbuf_rx_event {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 event_data_len;
+ __le16 seqnum;
+ __le16 rsvd0[4];
+};
+
+struct msgbuf_ioctl_resp_hdr {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 resp_len;
+ __le16 trans_id;
+ __le32 cmd;
+ __le32 rsvd0;
+};
+
+struct msgbuf_tx_status {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 metadata_len;
+ __le16 tx_status;
+};
+
+struct msgbuf_rx_complete {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 metadata_len;
+ __le16 data_len;
+ __le16 data_offset;
+ __le16 flags;
+ __le32 rx_status_0;
+ __le32 rx_status_1;
+ __le32 rsvd0;
+};
+
+struct msgbuf_tx_flowring_create_req {
+ struct msgbuf_common_hdr msg;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+ u8 tid;
+ u8 if_flags;
+ __le16 flow_ring_id;
+ u8 tc;
+ u8 priority;
+ __le16 int_vector;
+ __le16 max_items;
+ __le16 len_item;
+ struct msgbuf_buf_addr flow_ring_addr;
+};
+
+struct msgbuf_tx_flowring_delete_req {
+ struct msgbuf_common_hdr msg;
+ __le16 flow_ring_id;
+ __le16 reason;
+ __le32 rsvd0[7];
+};
+
+struct msgbuf_flowring_create_resp {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le32 rsvd0[3];
+};
+
+struct msgbuf_flowring_delete_resp {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le32 rsvd0[3];
+};
+
+struct msgbuf_flowring_flush_resp {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le32 rsvd0[3];
+};
+
+struct brcmf_msgbuf_work_item {
+ struct list_head queue;
+ u32 flowid;
+ int ifidx;
+ u8 sa[ETH_ALEN];
+ u8 da[ETH_ALEN];
+};
+
+struct brcmf_msgbuf {
+ struct brcmf_pub *drvr;
+
+ struct brcmf_commonring **commonrings;
+ struct brcmf_commonring **flowrings;
+ dma_addr_t *flowring_dma_handle;
+ u16 nrof_flowrings;
+
+ u16 rx_dataoffset;
+ u32 max_rxbufpost;
+ u16 rx_metadata_offset;
+ u32 rxbufpost;
+
+ u32 max_ioctlrespbuf;
+ u32 cur_ioctlrespbuf;
+ u32 max_eventbuf;
+ u32 cur_eventbuf;
+
+ void *ioctbuf;
+ dma_addr_t ioctbuf_handle;
+ u32 ioctbuf_phys_hi;
+ u32 ioctbuf_phys_lo;
+ int ioctl_resp_status;
+ u32 ioctl_resp_ret_len;
+ u32 ioctl_resp_pktid;
+
+ u16 data_seq_no;
+ u16 ioctl_seq_no;
+ u32 reqid;
+ wait_queue_head_t ioctl_resp_wait;
+ bool ctl_completed;
+
+ struct brcmf_msgbuf_pktids *tx_pktids;
+ struct brcmf_msgbuf_pktids *rx_pktids;
+ struct brcmf_flowring *flow;
+
+ struct workqueue_struct *txflow_wq;
+ struct work_struct txflow_work;
+ unsigned long *flow_map;
+ unsigned long *txstatus_done_map;
+
+ struct work_struct flowring_work;
+ spinlock_t flowring_work_lock;
+ struct list_head work_queue;
+};
+
+struct brcmf_msgbuf_pktid {
+ atomic_t allocated;
+ u16 data_offset;
+ struct sk_buff *skb;
+ dma_addr_t physaddr;
+};
+
+struct brcmf_msgbuf_pktids {
+ u32 array_size;
+ u32 last_allocated_idx;
+ enum dma_data_direction direction;
+ struct brcmf_msgbuf_pktid *array;
+};
+
+static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf);
+
+
+static struct brcmf_msgbuf_pktids *
+brcmf_msgbuf_init_pktids(u32 nr_array_entries,
+ enum dma_data_direction direction)
+{
+ struct brcmf_msgbuf_pktid *array;
+ struct brcmf_msgbuf_pktids *pktids;
+
+ array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return NULL;
+
+ pktids = kzalloc(sizeof(*pktids), GFP_KERNEL);
+ if (!pktids) {
+ kfree(array);
+ return NULL;
+ }
+ pktids->array = array;
+ pktids->array_size = nr_array_entries;
+
+ return pktids;
+}
+
+
+static int
+brcmf_msgbuf_alloc_pktid(struct device *dev,
+ struct brcmf_msgbuf_pktids *pktids,
+ struct sk_buff *skb, u16 data_offset,
+ dma_addr_t *physaddr, u32 *idx)
+{
+ struct brcmf_msgbuf_pktid *array;
+ u32 count;
+
+ array = pktids->array;
+
+ *physaddr = dma_map_single(dev, skb->data + data_offset,
+ skb->len - data_offset, pktids->direction);
+
+ if (dma_mapping_error(dev, *physaddr)) {
+ brcmf_err("dma_map_single failed !!\n");
+ return -ENOMEM;
+ }
+
+ *idx = pktids->last_allocated_idx;
+
+ count = 0;
+ do {
+ (*idx)++;
+ if (*idx == pktids->array_size)
+ *idx = 0;
+ if (array[*idx].allocated.counter == 0)
+ if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0)
+ break;
+ count++;
+ } while (count < pktids->array_size);
+
+ if (count == pktids->array_size)
+ return -ENOMEM;
+
+ array[*idx].data_offset = data_offset;
+ array[*idx].physaddr = *physaddr;
+ array[*idx].skb = skb;
+
+ pktids->last_allocated_idx = *idx;
+
+ return 0;
+}
+
+
+static struct sk_buff *
+brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids,
+ u32 idx)
+{
+ struct brcmf_msgbuf_pktid *pktid;
+ struct sk_buff *skb;
+
+ if (idx >= pktids->array_size) {
+ brcmf_err("Invalid packet id %d (max %d)\n", idx,
+ pktids->array_size);
+ return NULL;
+ }
+ if (pktids->array[idx].allocated.counter) {
+ pktid = &pktids->array[idx];
+ dma_unmap_single(dev, pktid->physaddr,
+ pktid->skb->len - pktid->data_offset,
+ pktids->direction);
+ skb = pktid->skb;
+ pktid->allocated.counter = 0;
+ return skb;
+ } else {
+ brcmf_err("Invalid packet id %d (not in use)\n", idx);
+ }
+
+ return NULL;
+}
+
+
+static void
+brcmf_msgbuf_release_array(struct device *dev,
+ struct brcmf_msgbuf_pktids *pktids)
+{
+ struct brcmf_msgbuf_pktid *array;
+ struct brcmf_msgbuf_pktid *pktid;
+ u32 count;
+
+ array = pktids->array;
+ count = 0;
+ do {
+ if (array[count].allocated.counter) {
+ pktid = &array[count];
+ dma_unmap_single(dev, pktid->physaddr,
+ pktid->skb->len - pktid->data_offset,
+ pktids->direction);
+ brcmu_pkt_buf_free_skb(pktid->skb);
+ }
+ count++;
+ } while (count < pktids->array_size);
+
+ kfree(array);
+ kfree(pktids);
+}
+
+
+static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf)
+{
+ if (msgbuf->rx_pktids)
+ brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids);
+ if (msgbuf->tx_pktids)
+ brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids);
+}
+
+
+static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_commonring *commonring;
+ struct msgbuf_ioctl_req_hdr *request;
+ u16 buf_len;
+ void *ret_ptr;
+ int err;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_err("Failed to reserve space in commonring\n");
+ brcmf_commonring_unlock(commonring);
+ return -ENOMEM;
+ }
+
+ msgbuf->reqid++;
+
+ request = (struct msgbuf_ioctl_req_hdr *)ret_ptr;
+ request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ;
+ request->msg.ifidx = (u8)ifidx;
+ request->msg.flags = 0;
+ request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID);
+ request->cmd = cpu_to_le32(cmd);
+ request->output_buf_len = cpu_to_le16(len);
+ request->trans_id = cpu_to_le16(msgbuf->reqid);
+
+ buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE);
+ request->input_buf_len = cpu_to_le16(buf_len);
+ request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi);
+ request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo);
+ if (buf)
+ memcpy(msgbuf->ioctbuf, buf, buf_len);
+ else
+ memset(msgbuf->ioctbuf, 0, buf_len);
+
+ err = brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+
+ return err;
+}
+
+
+static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf)
+{
+ return wait_event_timeout(msgbuf->ioctl_resp_wait,
+ msgbuf->ctl_completed,
+ MSGBUF_IOCTL_RESP_TIMEOUT);
+}
+
+
+static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
+{
+ msgbuf->ctl_completed = true;
+ wake_up(&msgbuf->ioctl_resp_wait);
+}
+
+
+static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct sk_buff *skb = NULL;
+ int timeout;
+ int err;
+
+ brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len);
+ msgbuf->ctl_completed = false;
+ err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len);
+ if (err)
+ return err;
+
+ timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf);
+ if (!timeout) {
+ brcmf_err("Timeout on response for query command\n");
+ return -EIO;
+ }
+
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids,
+ msgbuf->ioctl_resp_pktid);
+ if (msgbuf->ioctl_resp_ret_len != 0) {
+ if (!skb)
+ return -EBADF;
+
+ memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
+ len : msgbuf->ioctl_resp_ret_len);
+ }
+ brcmu_pkt_buf_free_skb(skb);
+
+ return msgbuf->ioctl_resp_status;
+}
+
+
+static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len);
+}
+
+
+static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+ struct sk_buff *skb, struct brcmf_if **ifp)
+{
+ return -ENODEV;
+}
+
+
+static void
+brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
+{
+ u32 dma_sz;
+ void *dma_buf;
+
+ brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid);
+
+ dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
+ dma_buf = msgbuf->flowrings[flowid]->buf_addr;
+ dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf,
+ msgbuf->flowring_dma_handle[flowid]);
+
+ brcmf_flowring_delete(msgbuf->flow, flowid);
+}
+
+
+static struct brcmf_msgbuf_work_item *
+brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf)
+{
+ struct brcmf_msgbuf_work_item *work = NULL;
+ ulong flags;
+
+ spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
+ if (!list_empty(&msgbuf->work_queue)) {
+ work = list_first_entry(&msgbuf->work_queue,
+ struct brcmf_msgbuf_work_item, queue);
+ list_del(&work->queue);
+ }
+ spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
+
+ return work;
+}
+
+
+static u32
+brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf,
+ struct brcmf_msgbuf_work_item *work)
+{
+ struct msgbuf_tx_flowring_create_req *create;
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ u32 flowid;
+ void *dma_buf;
+ u32 dma_sz;
+ u64 address;
+ int err;
+
+ flowid = work->flowid;
+ dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
+ dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz,
+ &msgbuf->flowring_dma_handle[flowid],
+ GFP_KERNEL);
+ if (!dma_buf) {
+ brcmf_err("dma_alloc_coherent failed\n");
+ brcmf_flowring_delete(msgbuf->flow, flowid);
+ return BRCMF_FLOWRING_INVALID_ID;
+ }
+
+ brcmf_commonring_config(msgbuf->flowrings[flowid],
+ BRCMF_H2D_TXFLOWRING_MAX_ITEM,
+ BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf);
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_err("Failed to reserve space in commonring\n");
+ brcmf_commonring_unlock(commonring);
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return BRCMF_FLOWRING_INVALID_ID;
+ }
+
+ create = (struct msgbuf_tx_flowring_create_req *)ret_ptr;
+ create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE;
+ create->msg.ifidx = work->ifidx;
+ create->msg.request_id = 0;
+ create->tid = brcmf_flowring_tid(msgbuf->flow, flowid);
+ create->flow_ring_id = cpu_to_le16(flowid +
+ BRCMF_NROF_H2D_COMMON_MSGRINGS);
+ memcpy(create->sa, work->sa, ETH_ALEN);
+ memcpy(create->da, work->da, ETH_ALEN);
+ address = (u64)msgbuf->flowring_dma_handle[flowid];
+ create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32);
+ create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff);
+ create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM);
+ create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE);
+
+ brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n",
+ flowid, work->da, create->tid, work->ifidx);
+
+ err = brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+ if (err) {
+ brcmf_err("Failed to write commonring\n");
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return BRCMF_FLOWRING_INVALID_ID;
+ }
+
+ return flowid;
+}
+
+
+static void brcmf_msgbuf_flowring_worker(struct work_struct *work)
+{
+ struct brcmf_msgbuf *msgbuf;
+ struct brcmf_msgbuf_work_item *create;
+
+ msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work);
+
+ while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) {
+ brcmf_msgbuf_flowring_create_worker(msgbuf, create);
+ kfree(create);
+ }
+}
+
+
+static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx,
+ struct sk_buff *skb)
+{
+ struct brcmf_msgbuf_work_item *create;
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ u32 flowid;
+ ulong flags;
+
+ create = kzalloc(sizeof(*create), GFP_ATOMIC);
+ if (create == NULL)
+ return BRCMF_FLOWRING_INVALID_ID;
+
+ flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest,
+ skb->priority, ifidx);
+ if (flowid == BRCMF_FLOWRING_INVALID_ID) {
+ kfree(create);
+ return flowid;
+ }
+
+ create->flowid = flowid;
+ create->ifidx = ifidx;
+ memcpy(create->sa, eh->h_source, ETH_ALEN);
+ memcpy(create->da, eh->h_dest, ETH_ALEN);
+
+ spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
+ list_add_tail(&create->queue, &msgbuf->work_queue);
+ spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
+ schedule_work(&msgbuf->flowring_work);
+
+ return flowid;
+}
+
+
+static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
+{
+ struct brcmf_flowring *flow = msgbuf->flow;
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ u32 count;
+ struct sk_buff *skb;
+ dma_addr_t physaddr;
+ u32 pktid;
+ struct msgbuf_tx_msghdr *tx_msghdr;
+ u64 address;
+
+ commonring = msgbuf->flowrings[flowid];
+ if (!brcmf_commonring_write_available(commonring))
+ return;
+
+ brcmf_commonring_lock(commonring);
+
+ count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1;
+ while (brcmf_flowring_qlen(flow, flowid)) {
+ skb = brcmf_flowring_dequeue(flow, flowid);
+ if (skb == NULL) {
+ brcmf_err("No SKB, but qlen %d\n",
+ brcmf_flowring_qlen(flow, flowid));
+ break;
+ }
+ skb_orphan(skb);
+ if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids, skb, ETH_HLEN,
+ &physaddr, &pktid)) {
+ brcmf_flowring_reinsert(flow, flowid, skb);
+ brcmf_err("No PKTID available !!\n");
+ break;
+ }
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids, pktid);
+ brcmf_flowring_reinsert(flow, flowid, skb);
+ break;
+ }
+ count++;
+
+ tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr;
+
+ tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST;
+ tx_msghdr->msg.request_id = cpu_to_le32(pktid);
+ tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid);
+ tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3;
+ tx_msghdr->flags |= (skb->priority & 0x07) <<
+ BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT;
+ tx_msghdr->seg_cnt = 1;
+ memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN);
+ tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN);
+ address = (u64)physaddr;
+ tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32);
+ tx_msghdr->data_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+ tx_msghdr->metadata_buf_len = 0;
+ tx_msghdr->metadata_buf_addr.high_addr = 0;
+ tx_msghdr->metadata_buf_addr.low_addr = 0;
+ atomic_inc(&commonring->outstanding_tx);
+ if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) {
+ brcmf_commonring_write_complete(commonring);
+ count = 0;
+ }
+ }
+ if (count)
+ brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+}
+
+
+static void brcmf_msgbuf_txflow_worker(struct work_struct *worker)
+{
+ struct brcmf_msgbuf *msgbuf;
+ u32 flowid;
+
+ msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work);
+ for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) {
+ clear_bit(flowid, msgbuf->flow_map);
+ brcmf_msgbuf_txflow(msgbuf, flowid);
+ }
+}
+
+
+static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid,
+ bool force)
+{
+ struct brcmf_commonring *commonring;
+
+ set_bit(flowid, msgbuf->flow_map);
+ commonring = msgbuf->flowrings[flowid];
+ if ((force) || (atomic_read(&commonring->outstanding_tx) <
+ BRCMF_MSGBUF_DELAY_TXWORKER_THRS))
+ queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
+
+ return 0;
+}
+
+
+static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx,
+ u8 offset, struct sk_buff *skb)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_flowring *flow = msgbuf->flow;
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ u32 flowid;
+ u32 queue_count;
+ bool force;
+
+ flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx);
+ if (flowid == BRCMF_FLOWRING_INVALID_ID) {
+ flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb);
+ if (flowid == BRCMF_FLOWRING_INVALID_ID)
+ return -ENOMEM;
+ }
+ queue_count = brcmf_flowring_enqueue(flow, flowid, skb);
+ force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0);
+ brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force);
+
+ return 0;
+}
+
+
+static void
+brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
+ enum proto_addr_mode addr_mode)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+
+ brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode);
+}
+
+
+static void
+brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+
+ brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer);
+}
+
+
+static void
+brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+
+ brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer);
+}
+
+
+static void
+brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_ioctl_resp_hdr *ioctl_resp;
+
+ ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf;
+
+ msgbuf->ioctl_resp_status =
+ (s16)le16_to_cpu(ioctl_resp->compl_hdr.status);
+ msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len);
+ msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id);
+
+ brcmf_msgbuf_ioctl_resp_wake(msgbuf);
+
+ if (msgbuf->cur_ioctlrespbuf)
+ msgbuf->cur_ioctlrespbuf--;
+ brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
+}
+
+
+static void
+brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct brcmf_commonring *commonring;
+ struct msgbuf_tx_status *tx_status;
+ u32 idx;
+ struct sk_buff *skb;
+ u16 flowid;
+
+ tx_status = (struct msgbuf_tx_status *)buf;
+ idx = le32_to_cpu(tx_status->msg.request_id);
+ flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id);
+ flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids, idx);
+ if (!skb)
+ return;
+
+ set_bit(flowid, msgbuf->txstatus_done_map);
+ commonring = msgbuf->flowrings[flowid];
+ atomic_dec(&commonring->outstanding_tx);
+
+ brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
+ skb, true);
+}
+
+
+static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count)
+{
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ struct sk_buff *skb;
+ u16 alloced;
+ u32 pktlen;
+ dma_addr_t physaddr;
+ struct msgbuf_rx_bufpost *rx_bufpost;
+ u64 address;
+ u32 pktid;
+ u32 i;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
+ ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
+ count,
+ &alloced);
+ if (!ret_ptr) {
+ brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n");
+ return 0;
+ }
+
+ for (i = 0; i < alloced; i++) {
+ rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr;
+ memset(rx_bufpost, 0, sizeof(*rx_bufpost));
+
+ skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);
+
+ if (skb == NULL) {
+ brcmf_err("Failed to alloc SKB\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+
+ pktlen = skb->len;
+ if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, skb, 0,
+ &physaddr, &pktid)) {
+ dev_kfree_skb_any(skb);
+ brcmf_err("No PKTID available !!\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+
+ if (msgbuf->rx_metadata_offset) {
+ address = (u64)physaddr;
+ rx_bufpost->metadata_buf_len =
+ cpu_to_le16(msgbuf->rx_metadata_offset);
+ rx_bufpost->metadata_buf_addr.high_addr =
+ cpu_to_le32(address >> 32);
+ rx_bufpost->metadata_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+
+ skb_pull(skb, msgbuf->rx_metadata_offset);
+ pktlen = skb->len;
+ physaddr += msgbuf->rx_metadata_offset;
+ }
+ rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST;
+ rx_bufpost->msg.request_id = cpu_to_le32(pktid);
+
+ address = (u64)physaddr;
+ rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen);
+ rx_bufpost->data_buf_addr.high_addr =
+ cpu_to_le32(address >> 32);
+ rx_bufpost->data_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+
+ ret_ptr += brcmf_commonring_len_item(commonring);
+ }
+
+ if (i)
+ brcmf_commonring_write_complete(commonring);
+
+ return i;
+}
+
+
+static void
+brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf)
+{
+ u32 fillbufs;
+ u32 retcount;
+
+ fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost;
+
+ while (fillbufs) {
+ retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs);
+ if (!retcount)
+ break;
+ msgbuf->rxbufpost += retcount;
+ fillbufs -= retcount;
+ }
+}
+
+
+static void
+brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt)
+{
+ msgbuf->rxbufpost -= rxcnt;
+ if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost -
+ BRCMF_MSGBUF_RXBUFPOST_THRESHOLD))
+ brcmf_msgbuf_rxbuf_data_fill(msgbuf);
+}
+
+
+static u32
+brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf,
+ u32 count)
+{
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ struct sk_buff *skb;
+ u16 alloced;
+ u32 pktlen;
+ dma_addr_t physaddr;
+ struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost;
+ u64 address;
+ u32 pktid;
+ u32 i;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
+ count,
+ &alloced);
+ if (!ret_ptr) {
+ brcmf_err("Failed to reserve space in commonring\n");
+ brcmf_commonring_unlock(commonring);
+ return 0;
+ }
+
+ for (i = 0; i < alloced; i++) {
+ rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr;
+ memset(rx_bufpost, 0, sizeof(*rx_bufpost));
+
+ skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);
+
+ if (skb == NULL) {
+ brcmf_err("Failed to alloc SKB\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+
+ pktlen = skb->len;
+ if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, skb, 0,
+ &physaddr, &pktid)) {
+ dev_kfree_skb_any(skb);
+ brcmf_err("No PKTID available !!\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+ if (event_buf)
+ rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST;
+ else
+ rx_bufpost->msg.msgtype =
+ MSGBUF_TYPE_IOCTLRESP_BUF_POST;
+ rx_bufpost->msg.request_id = cpu_to_le32(pktid);
+
+ address = (u64)physaddr;
+ rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen);
+ rx_bufpost->host_buf_addr.high_addr =
+ cpu_to_le32(address >> 32);
+ rx_bufpost->host_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+
+ ret_ptr += brcmf_commonring_len_item(commonring);
+ }
+
+ if (i)
+ brcmf_commonring_write_complete(commonring);
+
+ brcmf_commonring_unlock(commonring);
+
+ return i;
+}
+
+
+static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf)
+{
+ u32 count;
+
+ count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf;
+ count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count);
+ msgbuf->cur_ioctlrespbuf += count;
+}
+
+
+static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf)
+{
+ u32 count;
+
+ count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf;
+ count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count);
+ msgbuf->cur_eventbuf += count;
+}
+
+
+static void
+brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
+ u8 ifidx)
+{
+ struct brcmf_if *ifp;
+
+ ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
+ if (!ifp || !ifp->ndev) {
+ brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+ brcmf_netif_rx(ifp, skb);
+}
+
+
+static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_rx_event *event;
+ u32 idx;
+ u16 buflen;
+ struct sk_buff *skb;
+
+ event = (struct msgbuf_rx_event *)buf;
+ idx = le32_to_cpu(event->msg.request_id);
+ buflen = le16_to_cpu(event->event_data_len);
+
+ if (msgbuf->cur_eventbuf)
+ msgbuf->cur_eventbuf--;
+ brcmf_msgbuf_rxbuf_event_post(msgbuf);
+
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, idx);
+ if (!skb)
+ return;
+
+ if (msgbuf->rx_dataoffset)
+ skb_pull(skb, msgbuf->rx_dataoffset);
+
+ skb_trim(skb, buflen);
+
+ brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx);
+}
+
+
+static void
+brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_rx_complete *rx_complete;
+ struct sk_buff *skb;
+ u16 data_offset;
+ u16 buflen;
+ u32 idx;
+
+ brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
+
+ rx_complete = (struct msgbuf_rx_complete *)buf;
+ data_offset = le16_to_cpu(rx_complete->data_offset);
+ buflen = le16_to_cpu(rx_complete->data_len);
+ idx = le32_to_cpu(rx_complete->msg.request_id);
+
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, idx);
+ if (!skb)
+ return;
+
+ if (data_offset)
+ skb_pull(skb, data_offset);
+ else if (msgbuf->rx_dataoffset)
+ skb_pull(skb, msgbuf->rx_dataoffset);
+
+ skb_trim(skb, buflen);
+
+ brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx);
+}
+
+
+static void
+brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf,
+ void *buf)
+{
+ struct msgbuf_flowring_create_resp *flowring_create_resp;
+ u16 status;
+ u16 flowid;
+
+ flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf;
+
+ flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id);
+ flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
+ status = le16_to_cpu(flowring_create_resp->compl_hdr.status);
+
+ if (status) {
+ brcmf_err("Flowring creation failed, code %d\n", status);
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return;
+ }
+ brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid,
+ status);
+
+ brcmf_flowring_open(msgbuf->flow, flowid);
+
+ brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
+}
+
+
+static void
+brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
+ void *buf)
+{
+ struct msgbuf_flowring_delete_resp *flowring_delete_resp;
+ u16 status;
+ u16 flowid;
+
+ flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf;
+
+ flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id);
+ flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
+ status = le16_to_cpu(flowring_delete_resp->compl_hdr.status);
+
+ if (status) {
+ brcmf_err("Flowring deletion failed, code %d\n", status);
+ brcmf_flowring_delete(msgbuf->flow, flowid);
+ return;
+ }
+ brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid,
+ status);
+
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+}
+
+
+static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_common_hdr *msg;
+
+ msg = (struct msgbuf_common_hdr *)buf;
+ switch (msg->msgtype) {
+ case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n");
+ brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n");
+ brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_IOCTLPTR_REQ_ACK:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n");
+ break;
+ case MSGBUF_TYPE_IOCTL_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n");
+ brcmf_msgbuf_process_ioctl_complete(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_WL_EVENT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n");
+ brcmf_msgbuf_process_event(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_TX_STATUS:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n");
+ brcmf_msgbuf_process_txstatus(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_RX_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n");
+ brcmf_msgbuf_process_rx_complete(msgbuf, buf);
+ break;
+ default:
+ brcmf_err("Unsupported msgtype %d\n", msg->msgtype);
+ break;
+ }
+}
+
+
+static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf,
+ struct brcmf_commonring *commonring)
+{
+ void *buf;
+ u16 count;
+ u16 processed;
+
+again:
+ buf = brcmf_commonring_get_read_ptr(commonring, &count);
+ if (buf == NULL)
+ return;
+
+ processed = 0;
+ while (count) {
+ brcmf_msgbuf_process_msgtype(msgbuf,
+ buf + msgbuf->rx_dataoffset);
+ buf += brcmf_commonring_len_item(commonring);
+ processed++;
+ if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
+ brcmf_commonring_read_complete(commonring, processed);
+ processed = 0;
+ }
+ count--;
+ }
+ if (processed)
+ brcmf_commonring_read_complete(commonring, processed);
+
+ if (commonring->r_ptr == 0)
+ goto again;
+}
+
+
+int brcmf_proto_msgbuf_rx_trigger(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_commonring *commonring;
+ void *buf;
+ u32 flowid;
+ int qlen;
+
+ buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
+ brcmf_msgbuf_process_rx(msgbuf, buf);
+ buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
+ brcmf_msgbuf_process_rx(msgbuf, buf);
+ buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
+ brcmf_msgbuf_process_rx(msgbuf, buf);
+
+ for_each_set_bit(flowid, msgbuf->txstatus_done_map,
+ msgbuf->nrof_flowrings) {
+ clear_bit(flowid, msgbuf->txstatus_done_map);
+ commonring = msgbuf->flowrings[flowid];
+ qlen = brcmf_flowring_qlen(msgbuf->flow, flowid);
+ if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) ||
+ ((qlen) && (atomic_read(&commonring->outstanding_tx) <
+ BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS)))
+ brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
+ }
+
+ return 0;
+}
+
+
+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct msgbuf_tx_flowring_delete_req *delete;
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ u8 ifidx;
+ int err;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_err("FW unaware, flowring will be removed !!\n");
+ brcmf_commonring_unlock(commonring);
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return;
+ }
+
+ delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr;
+
+ ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid);
+
+ delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE;
+ delete->msg.ifidx = ifidx;
+ delete->msg.request_id = 0;
+
+ delete->flow_ring_id = cpu_to_le16(flowid +
+ BRCMF_NROF_H2D_COMMON_MSGRINGS);
+ delete->reason = 0;
+
+ brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n",
+ flowid, ifidx);
+
+ err = brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+ if (err) {
+ brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n");
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ }
+}
+
+#ifdef DEBUG
+static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_commonring *commonring;
+ u16 i;
+ struct brcmf_flowring_ring *ring;
+ struct brcmf_flowring_hash *hash;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
+ seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
+ seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
+ seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
+ seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+
+ seq_printf(seq, "\nh2d_flowrings: depth %u\n",
+ BRCMF_H2D_TXFLOWRING_MAX_ITEM);
+ seq_puts(seq, "Active flowrings:\n");
+ hash = msgbuf->flow->hash;
+ for (i = 0; i < msgbuf->flow->nrofrings; i++) {
+ if (!msgbuf->flow->rings[i])
+ continue;
+ ring = msgbuf->flow->rings[i];
+ if (ring->status != RING_OPEN)
+ continue;
+ commonring = msgbuf->flowrings[i];
+ hash = &msgbuf->flow->hash[ring->hash_id];
+ seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n"
+ " ifidx %u, fifo %u, da %pM\n",
+ i, commonring->r_ptr, commonring->w_ptr,
+ skb_queue_len(&ring->skblist), ring->blocked,
+ hash->ifidx, hash->fifo, hash->mac);
+ }
+
+ return 0;
+}
+#else
+static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
+{
+ return 0;
+}
+#endif
+
+int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_bus_msgbuf *if_msgbuf;
+ struct brcmf_msgbuf *msgbuf;
+ u64 address;
+ u32 count;
+
+ if_msgbuf = drvr->bus_if->msgbuf;
+ msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
+ if (!msgbuf)
+ goto fail;
+
+ msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow");
+ if (msgbuf->txflow_wq == NULL) {
+ brcmf_err("workqueue creation failed\n");
+ goto fail;
+ }
+ INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker);
+ count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings);
+ count = count * sizeof(unsigned long);
+ msgbuf->flow_map = kzalloc(count, GFP_KERNEL);
+ if (!msgbuf->flow_map)
+ goto fail;
+
+ msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL);
+ if (!msgbuf->txstatus_done_map)
+ goto fail;
+
+ msgbuf->drvr = drvr;
+ msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev,
+ BRCMF_TX_IOCTL_MAX_MSG_SIZE,
+ &msgbuf->ioctbuf_handle,
+ GFP_KERNEL);
+ if (!msgbuf->ioctbuf)
+ goto fail;
+ address = (u64)msgbuf->ioctbuf_handle;
+ msgbuf->ioctbuf_phys_hi = address >> 32;
+ msgbuf->ioctbuf_phys_lo = address & 0xffffffff;
+
+ drvr->proto->hdrpull = brcmf_msgbuf_hdrpull;
+ drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd;
+ drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd;
+ drvr->proto->txdata = brcmf_msgbuf_txdata;
+ drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
+ drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
+ drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
+ drvr->proto->pd = msgbuf;
+
+ init_waitqueue_head(&msgbuf->ioctl_resp_wait);
+
+ msgbuf->commonrings =
+ (struct brcmf_commonring **)if_msgbuf->commonrings;
+ msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings;
+ msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings;
+ msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings *
+ sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL);
+ if (!msgbuf->flowring_dma_handle)
+ goto fail;
+
+ msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset;
+ msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost;
+
+ msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST;
+ msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST;
+
+ msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS,
+ DMA_TO_DEVICE);
+ if (!msgbuf->tx_pktids)
+ goto fail;
+ msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS,
+ DMA_FROM_DEVICE);
+ if (!msgbuf->rx_pktids)
+ goto fail;
+
+ msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev,
+ if_msgbuf->nrof_flowrings);
+ if (!msgbuf->flow)
+ goto fail;
+
+
+ brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n",
+ msgbuf->max_rxbufpost, msgbuf->max_eventbuf,
+ msgbuf->max_ioctlrespbuf);
+ count = 0;
+ do {
+ brcmf_msgbuf_rxbuf_data_fill(msgbuf);
+ if (msgbuf->max_rxbufpost != msgbuf->rxbufpost)
+ msleep(10);
+ else
+ break;
+ count++;
+ } while (count < 10);
+ brcmf_msgbuf_rxbuf_event_post(msgbuf);
+ brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
+
+ INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker);
+ spin_lock_init(&msgbuf->flowring_work_lock);
+ INIT_LIST_HEAD(&msgbuf->work_queue);
+
+ brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
+
+ return 0;
+
+fail:
+ if (msgbuf) {
+ kfree(msgbuf->flow_map);
+ kfree(msgbuf->txstatus_done_map);
+ brcmf_msgbuf_release_pktids(msgbuf);
+ kfree(msgbuf->flowring_dma_handle);
+ if (msgbuf->ioctbuf)
+ dma_free_coherent(drvr->bus_if->dev,
+ BRCMF_TX_IOCTL_MAX_MSG_SIZE,
+ msgbuf->ioctbuf,
+ msgbuf->ioctbuf_handle);
+ kfree(msgbuf);
+ }
+ return -ENOMEM;
+}
+
+
+void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_msgbuf *msgbuf;
+ struct brcmf_msgbuf_work_item *work;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (drvr->proto->pd) {
+ msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ cancel_work_sync(&msgbuf->flowring_work);
+ while (!list_empty(&msgbuf->work_queue)) {
+ work = list_first_entry(&msgbuf->work_queue,
+ struct brcmf_msgbuf_work_item,
+ queue);
+ list_del(&work->queue);
+ kfree(work);
+ }
+ kfree(msgbuf->flow_map);
+ kfree(msgbuf->txstatus_done_map);
+ if (msgbuf->txflow_wq)
+ destroy_workqueue(msgbuf->txflow_wq);
+
+ brcmf_flowring_detach(msgbuf->flow);
+ dma_free_coherent(drvr->bus_if->dev,
+ BRCMF_TX_IOCTL_MAX_MSG_SIZE,
+ msgbuf->ioctbuf, msgbuf->ioctbuf_handle);
+ brcmf_msgbuf_release_pktids(msgbuf);
+ kfree(msgbuf->flowring_dma_handle);
+ kfree(msgbuf);
+ drvr->proto->pd = NULL;
+ }
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
index 3d513e407e3d..3d513e407e3d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
index 03f35e0c52ca..03f35e0c52ca 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
index 5f7c3550deda..5f7c3550deda 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/of.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
new file mode 100644
index 000000000000..821b6494f9d1
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -0,0 +1,2394 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <defs.h>
+#include "core.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "p2p.h"
+#include "cfg80211.h"
+
+/* parameters used for p2p escan */
+#define P2PAPI_SCAN_NPROBES 1
+#define P2PAPI_SCAN_DWELL_TIME_MS 80
+#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40
+#define P2PAPI_SCAN_HOME_TIME_MS 60
+#define P2PAPI_SCAN_NPROBS_TIME_MS 30
+#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100
+#define WL_SCAN_CONNECT_DWELL_TIME_MS 200
+#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
+
+#define BRCMF_P2P_WILDCARD_SSID "DIRECT-"
+#define BRCMF_P2P_WILDCARD_SSID_LEN (sizeof(BRCMF_P2P_WILDCARD_SSID) - 1)
+
+#define SOCIAL_CHAN_1 1
+#define SOCIAL_CHAN_2 6
+#define SOCIAL_CHAN_3 11
+#define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \
+ (channel == SOCIAL_CHAN_2) || \
+ (channel == SOCIAL_CHAN_3))
+#define BRCMF_P2P_TEMP_CHAN SOCIAL_CHAN_3
+#define SOCIAL_CHAN_CNT 3
+#define AF_PEER_SEARCH_CNT 2
+
+#define BRCMF_SCB_TIMEOUT_VALUE 20
+
+#define P2P_VER 9 /* P2P version: 9=WiFi P2P v1.0 */
+#define P2P_PUB_AF_CATEGORY 0x04
+#define P2P_PUB_AF_ACTION 0x09
+#define P2P_AF_CATEGORY 0x7f
+#define P2P_OUI "\x50\x6F\x9A" /* P2P OUI */
+#define P2P_OUI_LEN 3 /* P2P OUI length */
+
+/* Action Frame Constants */
+#define DOT11_ACTION_HDR_LEN 2 /* action frame category + action */
+#define DOT11_ACTION_CAT_OFF 0 /* category offset */
+#define DOT11_ACTION_ACT_OFF 1 /* action offset */
+
+#define P2P_AF_DWELL_TIME 200
+#define P2P_AF_MIN_DWELL_TIME 100
+#define P2P_AF_MED_DWELL_TIME 400
+#define P2P_AF_LONG_DWELL_TIME 1000
+#define P2P_AF_TX_MAX_RETRY 1
+#define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000)
+#define P2P_INVALID_CHANNEL -1
+#define P2P_CHANNEL_SYNC_RETRY 5
+#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(1500)
+#define P2P_DEFAULT_SLEEP_TIME_VSDB 200
+
+/* WiFi P2P Public Action Frame OUI Subtypes */
+#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */
+#define P2P_PAF_GON_RSP 1 /* Group Owner Negotiation Rsp */
+#define P2P_PAF_GON_CONF 2 /* Group Owner Negotiation Confirm */
+#define P2P_PAF_INVITE_REQ 3 /* P2P Invitation Request */
+#define P2P_PAF_INVITE_RSP 4 /* P2P Invitation Response */
+#define P2P_PAF_DEVDIS_REQ 5 /* Device Discoverability Request */
+#define P2P_PAF_DEVDIS_RSP 6 /* Device Discoverability Response */
+#define P2P_PAF_PROVDIS_REQ 7 /* Provision Discovery Request */
+#define P2P_PAF_PROVDIS_RSP 8 /* Provision Discovery Response */
+#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */
+
+/* WiFi P2P Action Frame OUI Subtypes */
+#define P2P_AF_NOTICE_OF_ABSENCE 0 /* Notice of Absence */
+#define P2P_AF_PRESENCE_REQ 1 /* P2P Presence Request */
+#define P2P_AF_PRESENCE_RSP 2 /* P2P Presence Response */
+#define P2P_AF_GO_DISC_REQ 3 /* GO Discoverability Request */
+
+/* P2P Service Discovery related */
+#define P2PSD_ACTION_CATEGORY 0x04 /* Public action frame */
+#define P2PSD_ACTION_ID_GAS_IREQ 0x0a /* GAS Initial Request AF */
+#define P2PSD_ACTION_ID_GAS_IRESP 0x0b /* GAS Initial Response AF */
+#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */
+#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */
+
+#define BRCMF_P2P_DISABLE_TIMEOUT msecs_to_jiffies(500)
+/**
+ * struct brcmf_p2p_disc_st_le - set discovery state in firmware.
+ *
+ * @state: requested discovery state (see enum brcmf_p2p_disc_state).
+ * @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state.
+ * @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state.
+ */
+struct brcmf_p2p_disc_st_le {
+ u8 state;
+ __le16 chspec;
+ __le16 dwell;
+};
+
+/**
+ * enum brcmf_p2p_disc_state - P2P discovery state values
+ *
+ * @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE.
+ * @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time.
+ * @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE.
+ */
+enum brcmf_p2p_disc_state {
+ WL_P2P_DISC_ST_SCAN,
+ WL_P2P_DISC_ST_LISTEN,
+ WL_P2P_DISC_ST_SEARCH
+};
+
+/**
+ * struct brcmf_p2p_scan_le - P2P specific scan request.
+ *
+ * @type: type of scan method requested (values: 'E' or 'S').
+ * @reserved: reserved (ignored).
+ * @eparams: parameters used for type 'E'.
+ * @sparams: parameters used for type 'S'.
+ */
+struct brcmf_p2p_scan_le {
+ u8 type;
+ u8 reserved[3];
+ union {
+ struct brcmf_escan_params_le eparams;
+ struct brcmf_scan_params_le sparams;
+ };
+};
+
+/**
+ * struct brcmf_p2p_pub_act_frame - WiFi P2P Public Action Frame
+ *
+ * @category: P2P_PUB_AF_CATEGORY
+ * @action: P2P_PUB_AF_ACTION
+ * @oui[3]: P2P_OUI
+ * @oui_type: OUI type - P2P_VER
+ * @subtype: OUI subtype - P2P_TYPE_*
+ * @dialog_token: nonzero, identifies req/rsp transaction
+ * @elts[1]: Variable length information elements.
+ */
+struct brcmf_p2p_pub_act_frame {
+ u8 category;
+ u8 action;
+ u8 oui[3];
+ u8 oui_type;
+ u8 subtype;
+ u8 dialog_token;
+ u8 elts[1];
+};
+
+/**
+ * struct brcmf_p2p_action_frame - WiFi P2P Action Frame
+ *
+ * @category: P2P_AF_CATEGORY
+ * @OUI[3]: OUI - P2P_OUI
+ * @type: OUI Type - P2P_VER
+ * @subtype: OUI Subtype - P2P_AF_*
+ * @dialog_token: nonzero, identifies req/resp tranaction
+ * @elts[1]: Variable length information elements.
+ */
+struct brcmf_p2p_action_frame {
+ u8 category;
+ u8 oui[3];
+ u8 type;
+ u8 subtype;
+ u8 dialog_token;
+ u8 elts[1];
+};
+
+/**
+ * struct brcmf_p2psd_gas_pub_act_frame - Wi-Fi GAS Public Action Frame
+ *
+ * @category: 0x04 Public Action Frame
+ * @action: 0x6c Advertisement Protocol
+ * @dialog_token: nonzero, identifies req/rsp transaction
+ * @query_data[1]: Query Data. SD gas ireq SD gas iresp
+ */
+struct brcmf_p2psd_gas_pub_act_frame {
+ u8 category;
+ u8 action;
+ u8 dialog_token;
+ u8 query_data[1];
+};
+
+/**
+ * struct brcmf_config_af_params - Action Frame Parameters for tx.
+ *
+ * @mpc_onoff: To make sure to send successfully action frame, we have to
+ * turn off mpc 0: off, 1: on, (-1): do nothing
+ * @search_channel: 1: search peer's channel to send af
+ * extra_listen: keep the dwell time to get af response frame.
+ */
+struct brcmf_config_af_params {
+ s32 mpc_onoff;
+ bool search_channel;
+ bool extra_listen;
+};
+
+/**
+ * brcmf_p2p_is_pub_action() - true if p2p public type frame.
+ *
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Determine if action frame is p2p public action type
+ */
+static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len)
+{
+ struct brcmf_p2p_pub_act_frame *pact_frm;
+
+ if (frame == NULL)
+ return false;
+
+ pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
+ if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1)
+ return false;
+
+ if (pact_frm->category == P2P_PUB_AF_CATEGORY &&
+ pact_frm->action == P2P_PUB_AF_ACTION &&
+ pact_frm->oui_type == P2P_VER &&
+ memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
+ return true;
+
+ return false;
+}
+
+/**
+ * brcmf_p2p_is_p2p_action() - true if p2p action type frame.
+ *
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Determine if action frame is p2p action type
+ */
+static bool brcmf_p2p_is_p2p_action(void *frame, u32 frame_len)
+{
+ struct brcmf_p2p_action_frame *act_frm;
+
+ if (frame == NULL)
+ return false;
+
+ act_frm = (struct brcmf_p2p_action_frame *)frame;
+ if (frame_len < sizeof(struct brcmf_p2p_action_frame) - 1)
+ return false;
+
+ if (act_frm->category == P2P_AF_CATEGORY &&
+ act_frm->type == P2P_VER &&
+ memcmp(act_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
+ return true;
+
+ return false;
+}
+
+/**
+ * brcmf_p2p_is_gas_action() - true if p2p gas action type frame.
+ *
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Determine if action frame is p2p gas action type
+ */
+static bool brcmf_p2p_is_gas_action(void *frame, u32 frame_len)
+{
+ struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
+
+ if (frame == NULL)
+ return false;
+
+ sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
+ if (frame_len < sizeof(struct brcmf_p2psd_gas_pub_act_frame) - 1)
+ return false;
+
+ if (sd_act_frm->category != P2PSD_ACTION_CATEGORY)
+ return false;
+
+ if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ ||
+ sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP ||
+ sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ ||
+ sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP)
+ return true;
+
+ return false;
+}
+
+/**
+ * brcmf_p2p_print_actframe() - debug print routine.
+ *
+ * @tx: Received or to be transmitted
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Print information about the p2p action frame
+ */
+
+#ifdef DEBUG
+
+static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
+{
+ struct brcmf_p2p_pub_act_frame *pact_frm;
+ struct brcmf_p2p_action_frame *act_frm;
+ struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
+
+ if (!frame || frame_len <= 2)
+ return;
+
+ if (brcmf_p2p_is_pub_action(frame, frame_len)) {
+ pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
+ switch (pact_frm->subtype) {
+ case P2P_PAF_GON_REQ:
+ brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Req Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_GON_RSP:
+ brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Rsp Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_GON_CONF:
+ brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Confirm Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_INVITE_REQ:
+ brcmf_dbg(TRACE, "%s P2P Invitation Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_INVITE_RSP:
+ brcmf_dbg(TRACE, "%s P2P Invitation Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_DEVDIS_REQ:
+ brcmf_dbg(TRACE, "%s P2P Device Discoverability Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_DEVDIS_RSP:
+ brcmf_dbg(TRACE, "%s P2P Device Discoverability Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_PROVDIS_REQ:
+ brcmf_dbg(TRACE, "%s P2P Provision Discovery Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_PROVDIS_RSP:
+ brcmf_dbg(TRACE, "%s P2P Provision Discovery Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ default:
+ brcmf_dbg(TRACE, "%s Unknown P2P Public Action Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ }
+ } else if (brcmf_p2p_is_p2p_action(frame, frame_len)) {
+ act_frm = (struct brcmf_p2p_action_frame *)frame;
+ switch (act_frm->subtype) {
+ case P2P_AF_NOTICE_OF_ABSENCE:
+ brcmf_dbg(TRACE, "%s P2P Notice of Absence Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_AF_PRESENCE_REQ:
+ brcmf_dbg(TRACE, "%s P2P Presence Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_AF_PRESENCE_RSP:
+ brcmf_dbg(TRACE, "%s P2P Presence Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_AF_GO_DISC_REQ:
+ brcmf_dbg(TRACE, "%s P2P Discoverability Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ default:
+ brcmf_dbg(TRACE, "%s Unknown P2P Action Frame\n",
+ (tx) ? "TX" : "RX");
+ }
+
+ } else if (brcmf_p2p_is_gas_action(frame, frame_len)) {
+ sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
+ switch (sd_act_frm->action) {
+ case P2PSD_ACTION_ID_GAS_IREQ:
+ brcmf_dbg(TRACE, "%s P2P GAS Initial Request\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2PSD_ACTION_ID_GAS_IRESP:
+ brcmf_dbg(TRACE, "%s P2P GAS Initial Response\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2PSD_ACTION_ID_GAS_CREQ:
+ brcmf_dbg(TRACE, "%s P2P GAS Comback Request\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2PSD_ACTION_ID_GAS_CRESP:
+ brcmf_dbg(TRACE, "%s P2P GAS Comback Response\n",
+ (tx) ? "TX" : "RX");
+ break;
+ default:
+ brcmf_dbg(TRACE, "%s Unknown P2P GAS Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ }
+ }
+}
+
+#else
+
+static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
+{
+}
+
+#endif
+
+
+/**
+ * brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation.
+ *
+ * @ifp: ifp to use for iovars (primary).
+ * @p2p_mac: mac address to configure for p2p_da_override
+ */
+static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac)
+{
+ s32 ret = 0;
+
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ brcmf_fil_iovar_int_set(ifp, "apsta", 1);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+
+ /* In case of COB type, firmware has default mac address
+ * After Initializing firmware, we have to set current mac address to
+ * firmware for P2P device address. This must be done with discovery
+ * disabled.
+ */
+ brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac,
+ ETH_ALEN);
+ if (ret)
+ brcmf_err("failed to update device address ret %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P.
+ *
+ * @p2p: P2P specific data.
+ * @dev_addr: optional device address.
+ *
+ * P2P needs mac addresses for P2P device and interface. If no device
+ * address it specified, these are derived from the primary net device, ie.
+ * the permanent ethernet address of the device.
+ */
+static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
+{
+ struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+ bool local_admin = false;
+
+ if (!dev_addr || is_zero_ether_addr(dev_addr)) {
+ dev_addr = pri_ifp->mac_addr;
+ local_admin = true;
+ }
+
+ /* Generate the P2P Device Address. This consists of the device's
+ * primary MAC address with the locally administered bit set.
+ */
+ memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
+ if (local_admin)
+ p2p->dev_addr[0] |= 0x02;
+
+ /* Generate the P2P Interface Address. If the discovery and connection
+ * BSSCFGs need to simultaneously co-exist, then this address must be
+ * different from the P2P Device Address, but also locally administered.
+ */
+ memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN);
+ p2p->int_addr[0] |= 0x02;
+ p2p->int_addr[4] ^= 0x80;
+}
+
+/**
+ * brcmf_p2p_scan_is_p2p_request() - is cfg80211 scan request a P2P scan.
+ *
+ * @request: the scan request as received from cfg80211.
+ *
+ * returns true if one of the ssids in the request matches the
+ * P2P wildcard ssid; otherwise returns false.
+ */
+static bool brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request *request)
+{
+ struct cfg80211_ssid *ssids = request->ssids;
+ int i;
+
+ for (i = 0; i < request->n_ssids; i++) {
+ if (ssids[i].ssid_len != BRCMF_P2P_WILDCARD_SSID_LEN)
+ continue;
+
+ brcmf_dbg(INFO, "comparing ssid \"%s\"", ssids[i].ssid);
+ if (!memcmp(BRCMF_P2P_WILDCARD_SSID, ssids[i].ssid,
+ BRCMF_P2P_WILDCARD_SSID_LEN))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * brcmf_p2p_set_discover_state - set discover state in firmware.
+ *
+ * @ifp: low-level interface object.
+ * @state: discover state to set.
+ * @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only).
+ * @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only).
+ */
+static s32 brcmf_p2p_set_discover_state(struct brcmf_if *ifp, u8 state,
+ u16 chanspec, u16 listen_ms)
+{
+ struct brcmf_p2p_disc_st_le discover_state;
+ s32 ret = 0;
+ brcmf_dbg(TRACE, "enter\n");
+
+ discover_state.state = state;
+ discover_state.chspec = cpu_to_le16(chanspec);
+ discover_state.dwell = cpu_to_le16(listen_ms);
+ ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state,
+ sizeof(discover_state));
+ return ret;
+}
+
+/**
+ * brcmf_p2p_deinit_discovery() - disable P2P device discovery.
+ *
+ * @p2p: P2P specific data.
+ *
+ * Resets the discovery state and disables it in firmware.
+ */
+static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p)
+{
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(TRACE, "enter\n");
+
+ /* Set the discovery state to SCAN */
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ (void)brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
+
+ /* Disable P2P discovery in the firmware */
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+ (void)brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 0);
+
+ return 0;
+}
+
+/**
+ * brcmf_p2p_enable_discovery() - initialize and configure discovery.
+ *
+ * @p2p: P2P specific data.
+ *
+ * Initializes the discovery device and configure the virtual interface.
+ */
+static int brcmf_p2p_enable_discovery(struct brcmf_p2p_info *p2p)
+{
+ struct brcmf_cfg80211_vif *vif;
+ s32 ret = 0;
+
+ brcmf_dbg(TRACE, "enter\n");
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (!vif) {
+ brcmf_err("P2P config device not available\n");
+ ret = -EPERM;
+ goto exit;
+ }
+
+ if (test_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status)) {
+ brcmf_dbg(INFO, "P2P config device already configured\n");
+ goto exit;
+ }
+
+ /* Re-initialize P2P Discovery in the firmware */
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+ ret = brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 1);
+ if (ret < 0) {
+ brcmf_err("set p2p_disc error\n");
+ goto exit;
+ }
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ ret = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
+ if (ret < 0) {
+ brcmf_err("unable to set WL_P2P_DISC_ST_SCAN\n");
+ goto exit;
+ }
+
+ /*
+ * Set wsec to any non-zero value in the discovery bsscfg
+ * to ensure our P2P probe responses have the privacy bit
+ * set in the 802.11 WPA IE. Some peer devices may not
+ * initiate WPS with us if this bit is not set.
+ */
+ ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED);
+ if (ret < 0) {
+ brcmf_err("wsec error %d\n", ret);
+ goto exit;
+ }
+
+ set_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status);
+exit:
+ return ret;
+}
+
+/**
+ * brcmf_p2p_escan() - initiate a P2P scan.
+ *
+ * @p2p: P2P specific data.
+ * @num_chans: number of channels to scan.
+ * @chanspecs: channel parameters for @num_chans channels.
+ * @search_state: P2P discover state to use.
+ * @bss_type: type of P2P bss.
+ */
+static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
+ u16 chanspecs[], s32 search_state,
+ enum p2p_bss_type bss_type)
+{
+ s32 ret = 0;
+ s32 memsize = offsetof(struct brcmf_p2p_scan_le,
+ eparams.params_le.channel_list);
+ s32 nprobes;
+ s32 active;
+ u32 i;
+ u8 *memblk;
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_p2p_scan_le *p2p_params;
+ struct brcmf_scan_params_le *sparams;
+
+ memsize += num_chans * sizeof(__le16);
+ memblk = kzalloc(memsize, GFP_KERNEL);
+ if (!memblk)
+ return -ENOMEM;
+
+ vif = p2p->bss_idx[bss_type].vif;
+ if (vif == NULL) {
+ brcmf_err("no vif for bss type %d\n", bss_type);
+ ret = -EINVAL;
+ goto exit;
+ }
+ p2p_params = (struct brcmf_p2p_scan_le *)memblk;
+ sparams = &p2p_params->eparams.params_le;
+
+ switch (search_state) {
+ case WL_P2P_DISC_ST_SEARCH:
+ /*
+ * If we in SEARCH STATE, we don't need to set SSID explictly
+ * because dongle use P2P WILDCARD internally by default, use
+ * null ssid, which it is already due to kzalloc.
+ */
+ break;
+ case WL_P2P_DISC_ST_SCAN:
+ /*
+ * wpa_supplicant has p2p_find command with type social or
+ * progressive. For progressive, we need to set the ssid to
+ * P2P WILDCARD because we just do broadcast scan unless
+ * setting SSID.
+ */
+ sparams->ssid_le.SSID_len =
+ cpu_to_le32(BRCMF_P2P_WILDCARD_SSID_LEN);
+ memcpy(sparams->ssid_le.SSID, BRCMF_P2P_WILDCARD_SSID,
+ BRCMF_P2P_WILDCARD_SSID_LEN);
+ break;
+ default:
+ brcmf_err(" invalid search state %d\n", search_state);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ brcmf_p2p_set_discover_state(vif->ifp, search_state, 0, 0);
+
+ /*
+ * set p2p scan parameters.
+ */
+ p2p_params->type = 'E';
+
+ /* determine the scan engine parameters */
+ sparams->bss_type = DOT11_BSSTYPE_ANY;
+ if (p2p->cfg->active_scan)
+ sparams->scan_type = 0;
+ else
+ sparams->scan_type = 1;
+
+ eth_broadcast_addr(sparams->bssid);
+ sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS);
+
+ /*
+ * SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan
+ * supported by the supplicant.
+ */
+ if (num_chans == SOCIAL_CHAN_CNT || num_chans == (SOCIAL_CHAN_CNT + 1))
+ active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
+ else if (num_chans == AF_PEER_SEARCH_CNT)
+ active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
+ else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
+ active = -1;
+ else
+ active = P2PAPI_SCAN_DWELL_TIME_MS;
+
+ /* Override scan params to find a peer for a connection */
+ if (num_chans == 1) {
+ active = WL_SCAN_CONNECT_DWELL_TIME_MS;
+ /* WAR to sync with presence period of VSDB GO.
+ * send probe request more frequently
+ */
+ nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS;
+ } else {
+ nprobes = active / P2PAPI_SCAN_NPROBS_TIME_MS;
+ }
+
+ if (nprobes <= 0)
+ nprobes = 1;
+
+ brcmf_dbg(INFO, "nprobes # %d, active_time %d\n", nprobes, active);
+ sparams->active_time = cpu_to_le32(active);
+ sparams->nprobes = cpu_to_le32(nprobes);
+ sparams->passive_time = cpu_to_le32(-1);
+ sparams->channel_num = cpu_to_le32(num_chans &
+ BRCMF_SCAN_PARAMS_COUNT_MASK);
+ for (i = 0; i < num_chans; i++)
+ sparams->channel_list[i] = cpu_to_le16(chanspecs[i]);
+
+ /* set the escan specific parameters */
+ p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
+ p2p_params->eparams.action = cpu_to_le16(WL_ESCAN_ACTION_START);
+ p2p_params->eparams.sync_id = cpu_to_le16(0x1234);
+ /* perform p2p scan on primary device */
+ ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize);
+ if (!ret)
+ set_bit(BRCMF_SCAN_STATUS_BUSY, &p2p->cfg->scan_status);
+exit:
+ kfree(memblk);
+ return ret;
+}
+
+/**
+ * brcmf_p2p_run_escan() - escan callback for peer-to-peer.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @ndev: net device for which scan is requested.
+ * @request: scan request from cfg80211.
+ * @action: scan action.
+ *
+ * Determines the P2P discovery state based to scan request parameters and
+ * validates the channels in the request.
+ */
+static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp,
+ struct cfg80211_scan_request *request)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ s32 err = 0;
+ s32 search_state = WL_P2P_DISC_ST_SCAN;
+ struct brcmf_cfg80211_vif *vif;
+ struct net_device *dev = NULL;
+ int i, num_nodfs = 0;
+ u16 *chanspecs;
+
+ brcmf_dbg(TRACE, "enter\n");
+
+ if (!request) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (request->n_channels) {
+ chanspecs = kcalloc(request->n_channels, sizeof(*chanspecs),
+ GFP_KERNEL);
+ if (!chanspecs) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
+ if (vif)
+ dev = vif->wdev.netdev;
+ if (request->n_channels == 3 &&
+ request->channels[0]->hw_value == SOCIAL_CHAN_1 &&
+ request->channels[1]->hw_value == SOCIAL_CHAN_2 &&
+ request->channels[2]->hw_value == SOCIAL_CHAN_3) {
+ /* SOCIAL CHANNELS 1, 6, 11 */
+ search_state = WL_P2P_DISC_ST_SEARCH;
+ brcmf_dbg(INFO, "P2P SEARCH PHASE START\n");
+ } else if (dev != NULL &&
+ vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+ /* If you are already a GO, then do SEARCH only */
+ brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n");
+ search_state = WL_P2P_DISC_ST_SEARCH;
+ } else {
+ brcmf_dbg(INFO, "P2P SCAN STATE START\n");
+ }
+
+ /*
+ * no P2P scanning on passive or DFS channels.
+ */
+ for (i = 0; i < request->n_channels; i++) {
+ struct ieee80211_channel *chan = request->channels[i];
+
+ if (chan->flags & (IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_NO_IR))
+ continue;
+
+ chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf,
+ chan);
+ brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n",
+ num_nodfs, chan->hw_value, chanspecs[i]);
+ num_nodfs++;
+ }
+ err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state,
+ P2PAPI_BSSCFG_DEVICE);
+ kfree(chanspecs);
+ }
+exit:
+ if (err)
+ brcmf_err("error (%d)\n", err);
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_find_listen_channel() - find listen channel in ie string.
+ *
+ * @ie: string of information elements.
+ * @ie_len: length of string.
+ *
+ * Scan ie for p2p ie and look for attribute 6 channel. If available determine
+ * channel and return it.
+ */
+static s32 brcmf_p2p_find_listen_channel(const u8 *ie, u32 ie_len)
+{
+ u8 channel_ie[5];
+ s32 listen_channel;
+ s32 err;
+
+ err = cfg80211_get_p2p_attr(ie, ie_len,
+ IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
+ channel_ie, sizeof(channel_ie));
+ if (err < 0)
+ return err;
+
+ /* listen channel subel length format: */
+ /* 3(country) + 1(op. class) + 1(chan num) */
+ listen_channel = (s32)channel_ie[3 + 1];
+
+ if (listen_channel == SOCIAL_CHAN_1 ||
+ listen_channel == SOCIAL_CHAN_2 ||
+ listen_channel == SOCIAL_CHAN_3) {
+ brcmf_dbg(INFO, "Found my Listen Channel %d\n", listen_channel);
+ return listen_channel;
+ }
+
+ return -EPERM;
+}
+
+
+/**
+ * brcmf_p2p_scan_prep() - prepare scan based on request.
+ *
+ * @wiphy: wiphy device.
+ * @request: scan request from cfg80211.
+ * @vif: vif on which scan request is to be executed.
+ *
+ * Prepare the scan appropriately for type of scan requested. Overrides the
+ * escan .run() callback for peer-to-peer scanning.
+ */
+int brcmf_p2p_scan_prep(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request,
+ struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ int err = 0;
+
+ if (brcmf_p2p_scan_is_p2p_request(request)) {
+ /* find my listen channel */
+ err = brcmf_p2p_find_listen_channel(request->ie,
+ request->ie_len);
+ if (err < 0)
+ return err;
+
+ p2p->afx_hdl.my_listen_chan = err;
+
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
+
+ err = brcmf_p2p_enable_discovery(p2p);
+ if (err)
+ return err;
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+
+ /* override .run_escan() callback. */
+ cfg->escan_info.run = brcmf_p2p_run_escan;
+ }
+ err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
+ request->ie, request->ie_len);
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_discover_listen() - set firmware to discover listen state.
+ *
+ * @p2p: p2p device.
+ * @channel: channel nr for discover listen.
+ * @duration: time in ms to stay on channel.
+ *
+ */
+static s32
+brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
+{
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmu_chan ch;
+ s32 err = 0;
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (!vif) {
+ brcmf_err("Discovery is not set, so we have nothing to do\n");
+ err = -EPERM;
+ goto exit;
+ }
+
+ if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status)) {
+ brcmf_err("Previous LISTEN is not completed yet\n");
+ /* WAR: prevent cookie mismatch in wpa_supplicant return OK */
+ goto exit;
+ }
+
+ ch.chnum = channel;
+ ch.bw = BRCMU_CHAN_BW_20;
+ p2p->cfg->d11inf.encchspec(&ch);
+ err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN,
+ ch.chspec, (u16)duration);
+ if (!err) {
+ set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status);
+ p2p->remain_on_channel_cookie++;
+ }
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_remain_on_channel() - put device on channel and stay there.
+ *
+ * @wiphy: wiphy device.
+ * @channel: channel to stay on.
+ * @duration: time in ms to remain on channel.
+ *
+ */
+int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *channel,
+ unsigned int duration, u64 *cookie)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ s32 err;
+ u16 channel_nr;
+
+ channel_nr = ieee80211_frequency_to_channel(channel->center_freq);
+ brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr,
+ duration);
+
+ err = brcmf_p2p_enable_discovery(p2p);
+ if (err)
+ goto exit;
+ err = brcmf_p2p_discover_listen(p2p, channel_nr, duration);
+ if (err)
+ goto exit;
+
+ memcpy(&p2p->remain_on_channel, channel, sizeof(*channel));
+ *cookie = p2p->remain_on_channel_cookie;
+ cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL);
+
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_notify_listen_complete() - p2p listen has completed.
+ *
+ * @ifp: interfac control.
+ * @e: event message. Not used, to make it usable for fweh event dispatcher.
+ * @data: payload of message. Not used.
+ *
+ */
+int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN,
+ &p2p->status)) {
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status)) {
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ &p2p->status);
+ brcmf_dbg(INFO, "Listen DONE, wake up wait_next_af\n");
+ complete(&p2p->wait_next_af);
+ }
+
+ cfg80211_remain_on_channel_expired(&ifp->vif->wdev,
+ p2p->remain_on_channel_cookie,
+ &p2p->remain_on_channel,
+ GFP_KERNEL);
+ }
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_cancel_remain_on_channel() - cancel p2p listen state.
+ *
+ * @ifp: interfac control.
+ *
+ */
+void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp)
+{
+ if (!ifp)
+ return;
+ brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
+ brcmf_p2p_notify_listen_complete(ifp, NULL, NULL);
+}
+
+
+/**
+ * brcmf_p2p_act_frm_search() - search function for action frame.
+ *
+ * @p2p: p2p device.
+ * channel: channel on which action frame is to be trasmitted.
+ *
+ * search function to reach at common channel to send action frame. When
+ * channel is 0 then all social channels will be used to send af
+ */
+static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
+{
+ s32 err;
+ u32 channel_cnt;
+ u16 *default_chan_list;
+ u32 i;
+ struct brcmu_chan ch;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (channel)
+ channel_cnt = AF_PEER_SEARCH_CNT;
+ else
+ channel_cnt = SOCIAL_CHAN_CNT;
+ default_chan_list = kzalloc(channel_cnt * sizeof(*default_chan_list),
+ GFP_KERNEL);
+ if (default_chan_list == NULL) {
+ brcmf_err("channel list allocation failed\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+ ch.bw = BRCMU_CHAN_BW_20;
+ if (channel) {
+ ch.chnum = channel;
+ p2p->cfg->d11inf.encchspec(&ch);
+ /* insert same channel to the chan_list */
+ for (i = 0; i < channel_cnt; i++)
+ default_chan_list[i] = ch.chspec;
+ } else {
+ ch.chnum = SOCIAL_CHAN_1;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[0] = ch.chspec;
+ ch.chnum = SOCIAL_CHAN_2;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[1] = ch.chspec;
+ ch.chnum = SOCIAL_CHAN_3;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[2] = ch.chspec;
+ }
+ err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list,
+ WL_P2P_DISC_ST_SEARCH, P2PAPI_BSSCFG_DEVICE);
+ kfree(default_chan_list);
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_afx_handler() - afx worker thread.
+ *
+ * @work:
+ *
+ */
+static void brcmf_p2p_afx_handler(struct work_struct *work)
+{
+ struct afx_hdl *afx_hdl = container_of(work, struct afx_hdl, afx_work);
+ struct brcmf_p2p_info *p2p = container_of(afx_hdl,
+ struct brcmf_p2p_info,
+ afx_hdl);
+ s32 err;
+
+ if (!afx_hdl->is_active)
+ return;
+
+ if (afx_hdl->is_listen && afx_hdl->my_listen_chan)
+ /* 100ms ~ 300ms */
+ err = brcmf_p2p_discover_listen(p2p, afx_hdl->my_listen_chan,
+ 100 * (1 + prandom_u32() % 3));
+ else
+ err = brcmf_p2p_act_frm_search(p2p, afx_hdl->peer_listen_chan);
+
+ if (err) {
+ brcmf_err("ERROR occurred! value is (%d)\n", err);
+ if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status))
+ complete(&afx_hdl->act_frm_scan);
+ }
+}
+
+
+/**
+ * brcmf_p2p_af_searching_channel() - search channel.
+ *
+ * @p2p: p2p device info struct.
+ *
+ */
+static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
+{
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct brcmf_cfg80211_vif *pri_vif;
+ unsigned long duration;
+ s32 retry;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+
+ reinit_completion(&afx_hdl->act_frm_scan);
+ set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
+ afx_hdl->is_active = true;
+ afx_hdl->peer_chan = P2P_INVALID_CHANNEL;
+
+ /* Loop to wait until we find a peer's channel or the
+ * pending action frame tx is cancelled.
+ */
+ retry = 0;
+ duration = msecs_to_jiffies(P2P_AF_FRM_SCAN_MAX_WAIT);
+ while ((retry < P2P_CHANNEL_SYNC_RETRY) &&
+ (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) {
+ afx_hdl->is_listen = false;
+ brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n",
+ retry);
+ /* search peer on peer's listen channel */
+ schedule_work(&afx_hdl->afx_work);
+ wait_for_completion_timeout(&afx_hdl->act_frm_scan, duration);
+ if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
+ (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status)))
+ break;
+
+ if (afx_hdl->my_listen_chan) {
+ brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n",
+ afx_hdl->my_listen_chan);
+ /* listen on my listen channel */
+ afx_hdl->is_listen = true;
+ schedule_work(&afx_hdl->afx_work);
+ wait_for_completion_timeout(&afx_hdl->act_frm_scan,
+ duration);
+ }
+ if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
+ (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status)))
+ break;
+ retry++;
+
+ /* if sta is connected or connecting, sleep for a while before
+ * retry af tx or finding a peer
+ */
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &pri_vif->sme_state) ||
+ test_bit(BRCMF_VIF_STATUS_CONNECTING, &pri_vif->sme_state))
+ msleep(P2P_DEFAULT_SLEEP_TIME_VSDB);
+ }
+
+ brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n",
+ afx_hdl->peer_chan);
+ afx_hdl->is_active = false;
+
+ clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
+
+ return afx_hdl->peer_chan;
+}
+
+
+/**
+ * brcmf_p2p_scan_finding_common_channel() - was escan used for finding channel
+ *
+ * @cfg: common configuration struct.
+ * @bi: bss info struct, result from scan.
+ *
+ */
+bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bi)
+
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct brcmu_chan ch;
+ u8 *ie;
+ s32 err;
+ u8 p2p_dev_addr[ETH_ALEN];
+
+ if (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))
+ return false;
+
+ if (bi == NULL) {
+ brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n");
+ if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)
+ complete(&afx_hdl->act_frm_scan);
+ return true;
+ }
+
+ ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
+ memset(p2p_dev_addr, 0, sizeof(p2p_dev_addr));
+ err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
+ IEEE80211_P2P_ATTR_DEVICE_INFO,
+ p2p_dev_addr, sizeof(p2p_dev_addr));
+ if (err < 0)
+ err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
+ IEEE80211_P2P_ATTR_DEVICE_ID,
+ p2p_dev_addr, sizeof(p2p_dev_addr));
+ if ((err >= 0) &&
+ (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) {
+ if (!bi->ctl_ch) {
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ bi->ctl_ch = ch.chnum;
+ }
+ afx_hdl->peer_chan = bi->ctl_ch;
+ brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n",
+ afx_hdl->tx_dst_addr, afx_hdl->peer_chan);
+ complete(&afx_hdl->act_frm_scan);
+ }
+ return true;
+}
+
+/**
+ * brcmf_p2p_stop_wait_next_action_frame() - finish scan if af tx complete.
+ *
+ * @cfg: common configuration struct.
+ *
+ */
+static void
+brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_if *ifp = cfg->escan_info.ifp;
+
+ if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) &&
+ (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) ||
+ test_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status))) {
+ brcmf_dbg(TRACE, "*** Wake UP ** abort actframe iovar\n");
+ /* if channel is not zero, "actfame" uses off channel scan.
+ * So abort scan for off channel completion.
+ */
+ if (p2p->af_sent_channel)
+ brcmf_notify_escan_complete(cfg, ifp, true, true);
+ } else if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status)) {
+ brcmf_dbg(TRACE, "*** Wake UP ** abort listen for next af frame\n");
+ /* So abort scan to cancel listen */
+ brcmf_notify_escan_complete(cfg, ifp, true, true);
+ }
+}
+
+
+/**
+ * brcmf_p2p_gon_req_collision() - Check if go negotiaton collission
+ *
+ * @p2p: p2p device info struct.
+ *
+ * return true if recevied action frame is to be dropped.
+ */
+static bool
+brcmf_p2p_gon_req_collision(struct brcmf_p2p_info *p2p, u8 *mac)
+{
+ struct brcmf_cfg80211_info *cfg = p2p->cfg;
+ struct brcmf_if *ifp;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (!test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) ||
+ !p2p->gon_req_action)
+ return false;
+
+ brcmf_dbg(TRACE, "GO Negotiation Request COLLISION !!!\n");
+ /* if sa(peer) addr is less than da(my) addr, then this device
+ * process peer's gon request and block to send gon req.
+ * if not (sa addr > da addr),
+ * this device will process gon request and drop gon req of peer.
+ */
+ ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp;
+ if (memcmp(mac, ifp->mac_addr, ETH_ALEN) < 0) {
+ brcmf_dbg(INFO, "Block transmit gon req !!!\n");
+ p2p->block_gon_req_tx = true;
+ /* if we are finding a common channel for sending af,
+ * do not scan more to block to send current gon req
+ */
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status))
+ complete(&p2p->afx_hdl.act_frm_scan);
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ &p2p->status))
+ brcmf_p2p_stop_wait_next_action_frame(cfg);
+ return false;
+ }
+
+ /* drop gon request of peer to process gon request by this device. */
+ brcmf_dbg(INFO, "Drop received gon req !!!\n");
+
+ return true;
+}
+
+
+/**
+ * brcmf_p2p_notify_action_frame_rx() - received action frame.
+ *
+ * @ifp: interfac control.
+ * @e: event message. Not used, to make it usable for fweh event dispatcher.
+ * @data: payload of message, containing action frame data.
+ *
+ */
+int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct wireless_dev *wdev;
+ u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data);
+ struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
+ u8 *frame = (u8 *)(rxframe + 1);
+ struct brcmf_p2p_pub_act_frame *act_frm;
+ struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
+ struct brcmu_chan ch;
+ struct ieee80211_mgmt *mgmt_frame;
+ s32 freq;
+ u16 mgmt_type;
+ u8 action;
+
+ ch.chspec = be16_to_cpu(rxframe->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ /* Check if wpa_supplicant has registered for this frame */
+ brcmf_dbg(INFO, "ifp->vif->mgmt_rx_reg %04x\n", ifp->vif->mgmt_rx_reg);
+ mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4;
+ if ((ifp->vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
+ return 0;
+
+ brcmf_p2p_print_actframe(false, frame, mgmt_frame_len);
+
+ action = P2P_PAF_SUBTYPE_INVALID;
+ if (brcmf_p2p_is_pub_action(frame, mgmt_frame_len)) {
+ act_frm = (struct brcmf_p2p_pub_act_frame *)frame;
+ action = act_frm->subtype;
+ if ((action == P2P_PAF_GON_REQ) &&
+ (brcmf_p2p_gon_req_collision(p2p, (u8 *)e->addr))) {
+ if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status) &&
+ (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
+ afx_hdl->peer_chan = ch.chnum;
+ brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n",
+ afx_hdl->peer_chan);
+ complete(&afx_hdl->act_frm_scan);
+ }
+ return 0;
+ }
+ /* After complete GO Negotiation, roll back to mpc mode */
+ if ((action == P2P_PAF_GON_CONF) ||
+ (action == P2P_PAF_PROVDIS_RSP))
+ brcmf_set_mpc(ifp, 1);
+ if (action == P2P_PAF_GON_CONF) {
+ brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ }
+ } else if (brcmf_p2p_is_gas_action(frame, mgmt_frame_len)) {
+ sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
+ action = sd_act_frm->action;
+ }
+
+ if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
+ (p2p->next_af_subtype == action)) {
+ brcmf_dbg(TRACE, "We got a right next frame! (%d)\n", action);
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ &p2p->status);
+ /* Stop waiting for next AF. */
+ brcmf_p2p_stop_wait_next_action_frame(cfg);
+ }
+
+ mgmt_frame = kzalloc(offsetof(struct ieee80211_mgmt, u) +
+ mgmt_frame_len, GFP_KERNEL);
+ if (!mgmt_frame) {
+ brcmf_err("No memory available for action frame\n");
+ return -ENOMEM;
+ }
+ memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN);
+ brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid,
+ ETH_ALEN);
+ memcpy(mgmt_frame->sa, e->addr, ETH_ALEN);
+ mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION);
+ memcpy(&mgmt_frame->u, frame, mgmt_frame_len);
+ mgmt_frame_len += offsetof(struct ieee80211_mgmt, u);
+
+ freq = ieee80211_channel_to_frequency(ch.chnum,
+ ch.band == BRCMU_CHAN_BAND_2G ?
+ IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ);
+
+ wdev = &ifp->vif->wdev;
+ cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0);
+
+ kfree(mgmt_frame);
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_notify_action_tx_complete() - transmit action frame complete
+ *
+ * @ifp: interfac control.
+ * @e: event message. Not used, to make it usable for fweh event dispatcher.
+ * @data: not used.
+ *
+ */
+int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+
+ brcmf_dbg(INFO, "Enter: event %s, status=%d\n",
+ e->event_code == BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE ?
+ "ACTION_FRAME_OFF_CHAN_COMPLETE" : "ACTION_FRAME_COMPLETE",
+ e->status);
+
+ if (!test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status))
+ return 0;
+
+ if (e->event_code == BRCMF_E_ACTION_FRAME_COMPLETE) {
+ if (e->status == BRCMF_E_STATUS_SUCCESS)
+ set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
+ &p2p->status);
+ else {
+ set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
+ /* If there is no ack, we don't need to wait for
+ * WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE event
+ */
+ brcmf_p2p_stop_wait_next_action_frame(cfg);
+ }
+
+ } else {
+ complete(&p2p->send_af_done);
+ }
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_tx_action_frame() - send action frame over fil.
+ *
+ * @p2p: p2p info struct for vif.
+ * @af_params: action frame data/info.
+ *
+ * Send an action frame immediately without doing channel synchronization.
+ *
+ * This function waits for a completion event before returning.
+ * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action
+ * frame is transmitted.
+ */
+static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
+ struct brcmf_fil_af_params_le *af_params)
+{
+ struct brcmf_cfg80211_vif *vif;
+ s32 err = 0;
+ s32 timeout = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ reinit_completion(&p2p->send_af_done);
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params,
+ sizeof(*af_params));
+ if (err) {
+ brcmf_err(" sending action frame has failed\n");
+ goto exit;
+ }
+
+ p2p->af_sent_channel = le32_to_cpu(af_params->channel);
+ p2p->af_tx_sent_jiffies = jiffies;
+
+ timeout = wait_for_completion_timeout(&p2p->send_af_done,
+ P2P_AF_MAX_WAIT_TIME);
+
+ if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) {
+ brcmf_dbg(TRACE, "TX action frame operation is success\n");
+ } else {
+ err = -EIO;
+ brcmf_dbg(TRACE, "TX action frame operation has failed\n");
+ }
+ /* clear status bit for action tx */
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
+
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_pub_af_tx() - public action frame tx routine.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @af_params: action frame data/info.
+ * @config_af_params: configuration data for action frame.
+ *
+ * routine which transmits ation frame public type.
+ */
+static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_fil_af_params_le *af_params,
+ struct brcmf_config_af_params *config_af_params)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_fil_action_frame_le *action_frame;
+ struct brcmf_p2p_pub_act_frame *act_frm;
+ s32 err = 0;
+ u16 ie_len;
+
+ action_frame = &af_params->action_frame;
+ act_frm = (struct brcmf_p2p_pub_act_frame *)(action_frame->data);
+
+ config_af_params->extra_listen = true;
+
+ switch (act_frm->subtype) {
+ case P2P_PAF_GON_REQ:
+ brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status set\n");
+ set_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ config_af_params->mpc_onoff = 0;
+ config_af_params->search_channel = true;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ p2p->gon_req_action = true;
+ /* increase dwell time to wait for RESP frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_GON_RSP:
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* increase dwell time to wait for CONF frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_GON_CONF:
+ /* If we reached till GO Neg confirmation reset the filter */
+ brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ /* turn on mpc again if go nego is done */
+ config_af_params->mpc_onoff = 1;
+ /* minimize dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ case P2P_PAF_INVITE_REQ:
+ config_af_params->search_channel = true;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* increase dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_INVITE_RSP:
+ /* minimize dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ case P2P_PAF_DEVDIS_REQ:
+ config_af_params->search_channel = true;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* maximize dwell time to wait for RESP frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_LONG_DWELL_TIME);
+ break;
+ case P2P_PAF_DEVDIS_RSP:
+ /* minimize dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ case P2P_PAF_PROVDIS_REQ:
+ ie_len = le16_to_cpu(action_frame->len) -
+ offsetof(struct brcmf_p2p_pub_act_frame, elts);
+ if (cfg80211_get_p2p_attr(&act_frm->elts[0], ie_len,
+ IEEE80211_P2P_ATTR_GROUP_ID,
+ NULL, 0) < 0)
+ config_af_params->search_channel = true;
+ config_af_params->mpc_onoff = 0;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* increase dwell time to wait for RESP frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_PROVDIS_RSP:
+ /* wpa_supplicant send go nego req right after prov disc */
+ p2p->next_af_subtype = P2P_PAF_GON_REQ;
+ /* increase dwell time to MED level */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ default:
+ brcmf_err("Unknown p2p pub act frame subtype: %d\n",
+ act_frm->subtype);
+ err = -EINVAL;
+ }
+ return err;
+}
+
+/**
+ * brcmf_p2p_send_action_frame() - send action frame .
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @ndev: net device to transmit on.
+ * @af_params: configuration data for action frame.
+ */
+bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ struct brcmf_fil_af_params_le *af_params)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_fil_action_frame_le *action_frame;
+ struct brcmf_config_af_params config_af_params;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ u16 action_frame_len;
+ bool ack = false;
+ u8 category;
+ u8 action;
+ s32 tx_retry;
+ s32 extra_listen_time;
+ uint delta_ms;
+
+ action_frame = &af_params->action_frame;
+ action_frame_len = le16_to_cpu(action_frame->len);
+
+ brcmf_p2p_print_actframe(true, action_frame->data, action_frame_len);
+
+ /* Add the default dwell time. Dwell time to stay off-channel */
+ /* to wait for a response action frame after transmitting an */
+ /* GO Negotiation action frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_DWELL_TIME);
+
+ category = action_frame->data[DOT11_ACTION_CAT_OFF];
+ action = action_frame->data[DOT11_ACTION_ACT_OFF];
+
+ /* initialize variables */
+ p2p->next_af_subtype = P2P_PAF_SUBTYPE_INVALID;
+ p2p->gon_req_action = false;
+
+ /* config parameters */
+ config_af_params.mpc_onoff = -1;
+ config_af_params.search_channel = false;
+ config_af_params.extra_listen = false;
+
+ if (brcmf_p2p_is_pub_action(action_frame->data, action_frame_len)) {
+ /* p2p public action frame process */
+ if (brcmf_p2p_pub_af_tx(cfg, af_params, &config_af_params)) {
+ /* Just send unknown subtype frame with */
+ /* default parameters. */
+ brcmf_err("P2P Public action frame, unknown subtype.\n");
+ }
+ } else if (brcmf_p2p_is_gas_action(action_frame->data,
+ action_frame_len)) {
+ /* service discovery process */
+ if (action == P2PSD_ACTION_ID_GAS_IREQ ||
+ action == P2PSD_ACTION_ID_GAS_CREQ) {
+ /* configure service discovery query frame */
+ config_af_params.search_channel = true;
+
+ /* save next af suptype to cancel */
+ /* remaining dwell time */
+ p2p->next_af_subtype = action + 1;
+
+ af_params->dwell_time =
+ cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ } else if (action == P2PSD_ACTION_ID_GAS_IRESP ||
+ action == P2PSD_ACTION_ID_GAS_CRESP) {
+ /* configure service discovery response frame */
+ af_params->dwell_time =
+ cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ } else {
+ brcmf_err("Unknown action type: %d\n", action);
+ goto exit;
+ }
+ } else if (brcmf_p2p_is_p2p_action(action_frame->data,
+ action_frame_len)) {
+ /* do not configure anything. it will be */
+ /* sent with a default configuration */
+ } else {
+ brcmf_err("Unknown Frame: category 0x%x, action 0x%x\n",
+ category, action);
+ return false;
+ }
+
+ /* if connecting on primary iface, sleep for a while before sending
+ * af tx for VSDB
+ */
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->sme_state))
+ msleep(50);
+
+ /* if scan is ongoing, abort current scan. */
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
+ brcmf_abort_scanning(cfg);
+
+ memcpy(afx_hdl->tx_dst_addr, action_frame->da, ETH_ALEN);
+
+ /* To make sure to send successfully action frame, turn off mpc */
+ if (config_af_params.mpc_onoff == 0)
+ brcmf_set_mpc(ifp, 0);
+
+ /* set status and destination address before sending af */
+ if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) {
+ /* set status to cancel the remained dwell time in rx process */
+ set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
+ }
+
+ p2p->af_sent_channel = 0;
+ set_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
+ /* validate channel and p2p ies */
+ if (config_af_params.search_channel &&
+ IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) &&
+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) {
+ afx_hdl = &p2p->afx_hdl;
+ afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel);
+
+ if (brcmf_p2p_af_searching_channel(p2p) ==
+ P2P_INVALID_CHANNEL) {
+ brcmf_err("Couldn't find peer's channel.\n");
+ goto exit;
+ }
+
+ /* Abort scan even for VSDB scenarios. Scan gets aborted in
+ * firmware but after the check of piggyback algorithm. To take
+ * care of current piggback algo, lets abort the scan here
+ * itself.
+ */
+ brcmf_notify_escan_complete(cfg, ifp, true, true);
+
+ /* update channel */
+ af_params->channel = cpu_to_le32(afx_hdl->peer_chan);
+ }
+
+ tx_retry = 0;
+ while (!p2p->block_gon_req_tx &&
+ (ack == false) && (tx_retry < P2P_AF_TX_MAX_RETRY)) {
+ ack = !brcmf_p2p_tx_action_frame(p2p, af_params);
+ tx_retry++;
+ }
+ if (ack == false) {
+ brcmf_err("Failed to send Action Frame(retry %d)\n", tx_retry);
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ }
+
+exit:
+ clear_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
+
+ /* WAR: sometimes dongle does not keep the dwell time of 'actframe'.
+ * if we coundn't get the next action response frame and dongle does
+ * not keep the dwell time, go to listen state again to get next action
+ * response frame.
+ */
+ if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx &&
+ test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
+ p2p->af_sent_channel == afx_hdl->my_listen_chan) {
+ delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies);
+ if (le32_to_cpu(af_params->dwell_time) > delta_ms)
+ extra_listen_time = le32_to_cpu(af_params->dwell_time) -
+ delta_ms;
+ else
+ extra_listen_time = 0;
+ if (extra_listen_time > 50) {
+ set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status);
+ brcmf_dbg(INFO, "Wait more time! actual af time:%d, calculated extra listen:%d\n",
+ le32_to_cpu(af_params->dwell_time),
+ extra_listen_time);
+ extra_listen_time += 100;
+ if (!brcmf_p2p_discover_listen(p2p,
+ p2p->af_sent_channel,
+ extra_listen_time)) {
+ unsigned long duration;
+
+ extra_listen_time += 100;
+ duration = msecs_to_jiffies(extra_listen_time);
+ wait_for_completion_timeout(&p2p->wait_next_af,
+ duration);
+ }
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status);
+ }
+ }
+
+ if (p2p->block_gon_req_tx) {
+ /* if ack is true, supplicant will wait more time(100ms).
+ * so we will return it as a success to get more time .
+ */
+ p2p->block_gon_req_tx = false;
+ ack = true;
+ }
+
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
+ /* if all done, turn mpc on again */
+ if (config_af_params.mpc_onoff == 1)
+ brcmf_set_mpc(ifp, 1);
+
+ return ack;
+}
+
+/**
+ * brcmf_p2p_notify_rx_mgmt_p2p_probereq() - Event handler for p2p probe req.
+ *
+ * @ifp: interface pointer for which event was received.
+ * @e: even message.
+ * @data: payload of event message (probe request).
+ */
+s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct brcmf_cfg80211_vif *vif = ifp->vif;
+ struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
+ u16 chanspec = be16_to_cpu(rxframe->chanspec);
+ struct brcmu_chan ch;
+ u8 *mgmt_frame;
+ u32 mgmt_frame_len;
+ s32 freq;
+ u16 mgmt_type;
+
+ brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
+ e->reason);
+
+ ch.chspec = be16_to_cpu(rxframe->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) &&
+ (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
+ afx_hdl->peer_chan = ch.chnum;
+ brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n",
+ afx_hdl->peer_chan);
+ complete(&afx_hdl->act_frm_scan);
+ }
+
+ /* Firmware sends us two proberesponses for each idx one. At the */
+ /* moment anything but bsscfgidx 0 is passed up to supplicant */
+ if (e->bsscfgidx == 0)
+ return 0;
+
+ /* Filter any P2P probe reqs arriving during the GO-NEG Phase */
+ if (test_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status)) {
+ brcmf_dbg(INFO, "Filtering P2P probe_req in GO-NEG phase\n");
+ return 0;
+ }
+
+ /* Check if wpa_supplicant has registered for this frame */
+ brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg);
+ mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4;
+ if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
+ return 0;
+
+ mgmt_frame = (u8 *)(rxframe + 1);
+ mgmt_frame_len = e->datalen - sizeof(*rxframe);
+ freq = ieee80211_channel_to_frequency(ch.chnum,
+ ch.band == BRCMU_CHAN_BAND_2G ?
+ IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ);
+
+ cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0);
+
+ brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
+ mgmt_frame_len, e->datalen, chanspec, freq);
+
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_get_current_chanspec() - Get current operation channel.
+ *
+ * @p2p: P2P specific data.
+ * @chanspec: chanspec to be returned.
+ */
+static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p,
+ u16 *chanspec)
+{
+ struct brcmf_if *ifp;
+ u8 mac_addr[ETH_ALEN];
+ struct brcmu_chan ch;
+ struct brcmf_bss_info_le *bi;
+ u8 *buf;
+
+ ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+
+ if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mac_addr,
+ ETH_ALEN) == 0) {
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (buf != NULL) {
+ *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+ if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
+ buf, WL_BSS_INFO_MAX) == 0) {
+ bi = (struct brcmf_bss_info_le *)(buf + 4);
+ *chanspec = le16_to_cpu(bi->chanspec);
+ kfree(buf);
+ return;
+ }
+ kfree(buf);
+ }
+ }
+ /* Use default channel for P2P */
+ ch.chnum = BRCMF_P2P_TEMP_CHAN;
+ ch.bw = BRCMU_CHAN_BW_20;
+ p2p->cfg->d11inf.encchspec(&ch);
+ *chanspec = ch.chspec;
+}
+
+/**
+ * Change a P2P Role.
+ * Parameters:
+ * @mac: MAC address of the BSS to change a role
+ * Returns 0 if success.
+ */
+int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
+ enum brcmf_fil_p2p_if_types if_type)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_fil_p2p_if_le if_request;
+ s32 err;
+ u16 chanspec;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+ if (!vif) {
+ brcmf_err("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n");
+ return -EPERM;
+ }
+ brcmf_notify_escan_complete(cfg, vif->ifp, true, true);
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
+ if (!vif) {
+ brcmf_err("vif for P2PAPI_BSSCFG_CONNECTION does not exist\n");
+ return -EPERM;
+ }
+ brcmf_set_mpc(vif->ifp, 0);
+
+ /* In concurrency case, STA may be already associated in a particular */
+ /* channel. so retrieve the current channel of primary interface and */
+ /* then start the virtual interface on that. */
+ brcmf_p2p_get_current_chanspec(p2p, &chanspec);
+
+ if_request.type = cpu_to_le16((u16)if_type);
+ if_request.chspec = cpu_to_le16(chanspec);
+ memcpy(if_request.addr, p2p->int_addr, sizeof(if_request.addr));
+
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+ err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request,
+ sizeof(if_request));
+ if (err) {
+ brcmf_err("p2p_ifupd FAILED, err=%d\n", err);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ return err;
+ }
+ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (!err) {
+ brcmf_err("No BRCMF_E_IF_CHANGE event received\n");
+ return -EIO;
+ }
+
+ err = brcmf_fil_cmd_int_set(vif->ifp, BRCMF_C_SET_SCB_TIMEOUT,
+ BRCMF_SCB_TIMEOUT_VALUE);
+
+ return err;
+}
+
+static int brcmf_p2p_request_p2p_if(struct brcmf_p2p_info *p2p,
+ struct brcmf_if *ifp, u8 ea[ETH_ALEN],
+ enum brcmf_fil_p2p_if_types iftype)
+{
+ struct brcmf_fil_p2p_if_le if_request;
+ int err;
+ u16 chanspec;
+
+ /* we need a default channel */
+ brcmf_p2p_get_current_chanspec(p2p, &chanspec);
+
+ /* fill the firmware request */
+ memcpy(if_request.addr, ea, ETH_ALEN);
+ if_request.type = cpu_to_le16((u16)iftype);
+ if_request.chspec = cpu_to_le16(chanspec);
+
+ err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request,
+ sizeof(if_request));
+ if (err)
+ return err;
+
+ return err;
+}
+
+static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
+ struct net_device *pri_ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(pri_ndev);
+ u8 *addr = vif->wdev.netdev->dev_addr;
+
+ return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN);
+}
+
+static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
+ struct net_device *pri_ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(pri_ndev);
+ u8 *addr = vif->wdev.netdev->dev_addr;
+
+ return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN);
+}
+
+/**
+ * brcmf_p2p_create_p2pdev() - create a P2P_DEVICE virtual interface.
+ *
+ * @p2p: P2P specific data.
+ * @wiphy: wiphy device of new interface.
+ * @addr: mac address for this new interface.
+ */
+static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
+ struct wiphy *wiphy,
+ u8 *addr)
+{
+ struct brcmf_cfg80211_vif *p2p_vif;
+ struct brcmf_if *p2p_ifp;
+ struct brcmf_if *pri_ifp;
+ int err;
+ u32 bsscfgidx;
+
+ if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ return ERR_PTR(-ENOSPC);
+
+ p2p_vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE,
+ false);
+ if (IS_ERR(p2p_vif)) {
+ brcmf_err("could not create discovery vif\n");
+ return (struct wireless_dev *)p2p_vif;
+ }
+
+ pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+ brcmf_p2p_generate_bss_mac(p2p, addr);
+ brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
+
+ brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif);
+ brcmf_fweh_p2pdev_setup(pri_ifp, true);
+
+ /* Initialize P2P Discovery in the firmware */
+ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
+ if (err < 0) {
+ brcmf_err("set p2p_disc error\n");
+ brcmf_fweh_p2pdev_setup(pri_ifp, false);
+ brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
+ goto fail;
+ }
+
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
+ brcmf_fweh_p2pdev_setup(pri_ifp, false);
+ if (!err) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* discovery interface created */
+ p2p_ifp = p2p_vif->ifp;
+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
+ memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
+ memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr));
+
+ /* verify bsscfg index for P2P discovery */
+ err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bsscfgidx);
+ if (err < 0) {
+ brcmf_err("retrieving discover bsscfg index failed\n");
+ goto fail;
+ }
+
+ WARN_ON(p2p_ifp->bsscfgidx != bsscfgidx);
+
+ init_completion(&p2p->send_af_done);
+ INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
+ init_completion(&p2p->afx_hdl.act_frm_scan);
+ init_completion(&p2p->wait_next_af);
+
+ return &p2p_vif->wdev;
+
+fail:
+ brcmf_free_vif(p2p_vif);
+ return ERR_PTR(err);
+}
+
+/**
+ * brcmf_p2p_add_vif() - create a new P2P virtual interface.
+ *
+ * @wiphy: wiphy device of new interface.
+ * @name: name of the new interface.
+ * @name_assign_type: origin of the interface name
+ * @type: nl80211 interface type.
+ * @flags: not used.
+ * @params: contains mac address for P2P device.
+ */
+struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_cfg80211_vif *vif;
+ enum brcmf_fil_p2p_if_types iftype;
+ int err;
+
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ return ERR_PTR(-EBUSY);
+
+ brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type);
+
+ switch (type) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ iftype = BRCMF_FIL_P2P_IF_CLIENT;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ iftype = BRCMF_FIL_P2P_IF_GO;
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy,
+ params->macaddr);
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ vif = brcmf_alloc_vif(cfg, type, false);
+ if (IS_ERR(vif))
+ return (struct wireless_dev *)vif;
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+
+ err = brcmf_p2p_request_p2p_if(&cfg->p2p, ifp, cfg->p2p.int_addr,
+ iftype);
+ if (err) {
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ goto fail;
+ }
+
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (!err) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* interface created in firmware */
+ ifp = vif->ifp;
+ if (!ifp) {
+ brcmf_err("no if pointer provided\n");
+ err = -ENOENT;
+ goto fail;
+ }
+
+ strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
+ ifp->ndev->name_assign_type = name_assign_type;
+ err = brcmf_net_attach(ifp, true);
+ if (err) {
+ brcmf_err("Registering netdevice failed\n");
+ goto fail;
+ }
+
+ cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif;
+ /* Disable firmware roaming for P2P interface */
+ brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
+ if (iftype == BRCMF_FIL_P2P_IF_GO) {
+ /* set station timeout for p2p */
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT,
+ BRCMF_SCB_TIMEOUT_VALUE);
+ }
+ return &ifp->vif->wdev;
+
+fail:
+ brcmf_free_vif(vif);
+ return ERR_PTR(err);
+}
+
+/**
+ * brcmf_p2p_del_vif() - delete a P2P virtual interface.
+ *
+ * @wiphy: wiphy device of interface.
+ * @wdev: wireless device of interface.
+ */
+int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+ bool wait_for_disable = false;
+ int err;
+
+ brcmf_dbg(TRACE, "delete P2P vif\n");
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+ switch (vif->wdev.iftype) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
+ wait_for_disable = true;
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ if (!brcmf_p2p_disable_p2p_if(vif))
+ wait_for_disable = true;
+ break;
+
+ case NL80211_IFTYPE_P2P_DEVICE:
+ if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ return 0;
+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+ brcmf_p2p_deinit_discovery(p2p);
+ default:
+ return -ENOTSUPP;
+ }
+
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
+
+ if (wait_for_disable)
+ wait_for_completion_timeout(&cfg->vif_disabled,
+ BRCMF_P2P_DISABLE_TIMEOUT);
+
+ err = 0;
+ if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) {
+ brcmf_vif_clear_mgmt_ies(vif);
+ err = brcmf_p2p_release_p2p_if(vif);
+ }
+ if (!err) {
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ if (!err)
+ err = -EIO;
+ else
+ err = 0;
+ }
+ if (err)
+ brcmf_remove_interface(vif->ifp);
+
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE)
+ p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
+
+ return err;
+}
+
+void brcmf_p2p_ifp_removed(struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg;
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(INFO, "P2P: device interface removed\n");
+ vif = ifp->vif;
+ cfg = wdev_to_cfg(&vif->wdev);
+ cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+ rtnl_lock();
+ cfg80211_unregister_wdev(&vif->wdev);
+ rtnl_unlock();
+ brcmf_free_vif(vif);
+}
+
+int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+ int err;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ mutex_lock(&cfg->usr_sync);
+ err = brcmf_p2p_enable_discovery(p2p);
+ if (!err)
+ set_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
+ mutex_unlock(&cfg->usr_sync);
+ return err;
+}
+
+void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ /* This call can be result of the unregister_wdev call. In that case
+ * we dont want to do anything anymore. Just return. The config vif
+ * will have been cleared at this point.
+ */
+ if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) {
+ mutex_lock(&cfg->usr_sync);
+ /* Set the discovery state to SCAN */
+ (void)brcmf_p2p_set_discover_state(vif->ifp,
+ WL_P2P_DISC_ST_SCAN, 0, 0);
+ brcmf_abort_scanning(cfg);
+ clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
+ mutex_unlock(&cfg->usr_sync);
+ }
+}
+
+/**
+ * brcmf_p2p_attach() - attach for P2P.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @p2pdev_forced: create p2p device interface at attach.
+ */
+s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced)
+{
+ struct brcmf_p2p_info *p2p;
+ struct brcmf_if *pri_ifp;
+ s32 err = 0;
+ void *err_ptr;
+
+ p2p = &cfg->p2p;
+ p2p->cfg = cfg;
+
+ pri_ifp = brcmf_get_ifp(cfg->pub, 0);
+ p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
+
+ if (p2pdev_forced) {
+ err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL);
+ if (IS_ERR(err_ptr)) {
+ brcmf_err("P2P device creation failed.\n");
+ err = PTR_ERR(err_ptr);
+ }
+ } else {
+ p2p->p2pdev_dynamically = true;
+ }
+ return err;
+}
+
+/**
+ * brcmf_p2p_detach() - detach P2P.
+ *
+ * @p2p: P2P specific data.
+ */
+void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
+{
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (vif != NULL) {
+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+ brcmf_p2p_deinit_discovery(p2p);
+ brcmf_remove_interface(vif->ifp);
+ }
+ /* just set it all to zero */
+ memset(p2p, 0, sizeof(*p2p));
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
new file mode 100644
index 000000000000..a3bd18c2360b
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef WL_CFGP2P_H_
+#define WL_CFGP2P_H_
+
+#include <net/cfg80211.h>
+
+struct brcmf_cfg80211_info;
+
+/**
+ * enum p2p_bss_type - different type of BSS configurations.
+ *
+ * @P2PAPI_BSSCFG_PRIMARY: maps to driver's primary bsscfg.
+ * @P2PAPI_BSSCFG_DEVICE: maps to driver's P2P device discovery bsscfg.
+ * @P2PAPI_BSSCFG_CONNECTION: maps to driver's P2P connection bsscfg.
+ * @P2PAPI_BSSCFG_MAX: used for range checking.
+ */
+enum p2p_bss_type {
+ P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */
+ P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */
+ P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */
+ P2PAPI_BSSCFG_MAX
+};
+
+/**
+ * struct p2p_bss - peer-to-peer bss related information.
+ *
+ * @vif: virtual interface of this P2P bss.
+ * @private_data: TBD
+ */
+struct p2p_bss {
+ struct brcmf_cfg80211_vif *vif;
+ void *private_data;
+};
+
+/**
+ * enum brcmf_p2p_status - P2P specific dongle status.
+ *
+ * @BRCMF_P2P_STATUS_IF_ADD: peer-to-peer vif add sent to dongle.
+ * @BRCMF_P2P_STATUS_IF_DEL: NOT-USED?
+ * @BRCMF_P2P_STATUS_IF_DELETING: peer-to-peer vif delete sent to dongle.
+ * @BRCMF_P2P_STATUS_IF_CHANGING: peer-to-peer vif change sent to dongle.
+ * @BRCMF_P2P_STATUS_IF_CHANGED: peer-to-peer vif change completed on dongle.
+ * @BRCMF_P2P_STATUS_ACTION_TX_COMPLETED: action frame tx completed.
+ * @BRCMF_P2P_STATUS_ACTION_TX_NOACK: action frame tx not acked.
+ * @BRCMF_P2P_STATUS_GO_NEG_PHASE: P2P GO negotiation ongoing.
+ * @BRCMF_P2P_STATUS_DISCOVER_LISTEN: P2P listen, remaining on channel.
+ * @BRCMF_P2P_STATUS_SENDING_ACT_FRAME: In the process of sending action frame.
+ * @BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN: extra listen time for af tx.
+ * @BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME: waiting for action frame response.
+ * @BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL: search channel for AF active.
+ */
+enum brcmf_p2p_status {
+ BRCMF_P2P_STATUS_ENABLED,
+ BRCMF_P2P_STATUS_IF_ADD,
+ BRCMF_P2P_STATUS_IF_DEL,
+ BRCMF_P2P_STATUS_IF_DELETING,
+ BRCMF_P2P_STATUS_IF_CHANGING,
+ BRCMF_P2P_STATUS_IF_CHANGED,
+ BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
+ BRCMF_P2P_STATUS_ACTION_TX_NOACK,
+ BRCMF_P2P_STATUS_GO_NEG_PHASE,
+ BRCMF_P2P_STATUS_DISCOVER_LISTEN,
+ BRCMF_P2P_STATUS_SENDING_ACT_FRAME,
+ BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL
+};
+
+/**
+ * struct afx_hdl - action frame off channel storage.
+ *
+ * @afx_work: worker thread for searching channel
+ * @act_frm_scan: thread synchronizing struct.
+ * @is_active: channel searching active.
+ * @peer_chan: current channel.
+ * @is_listen: sets mode for afx worker.
+ * @my_listen_chan: this peers listen channel.
+ * @peer_listen_chan: remote peers listen channel.
+ * @tx_dst_addr: mac address where tx af should be sent to.
+ */
+struct afx_hdl {
+ struct work_struct afx_work;
+ struct completion act_frm_scan;
+ bool is_active;
+ s32 peer_chan;
+ bool is_listen;
+ u16 my_listen_chan;
+ u16 peer_listen_chan;
+ u8 tx_dst_addr[ETH_ALEN];
+};
+
+/**
+ * struct brcmf_p2p_info - p2p specific driver information.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @status: status of P2P (see enum brcmf_p2p_status).
+ * @dev_addr: P2P device address.
+ * @int_addr: P2P interface address.
+ * @bss_idx: informate for P2P bss types.
+ * @listen_timer: timer for @WL_P2P_DISC_ST_LISTEN discover state.
+ * @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state.
+ * @remain_on_channel: contains copy of struct used by cfg80211.
+ * @remain_on_channel_cookie: cookie counter for remain on channel cmd
+ * @next_af_subtype: expected action frame subtype.
+ * @send_af_done: indication that action frame tx is complete.
+ * @afx_hdl: action frame search handler info.
+ * @af_sent_channel: channel action frame is sent.
+ * @af_tx_sent_jiffies: jiffies time when af tx was transmitted.
+ * @wait_next_af: thread synchronizing struct.
+ * @gon_req_action: about to send go negotiation requets frame.
+ * @block_gon_req_tx: drop tx go negotiation requets frame.
+ * @p2pdev_dynamically: is p2p device if created by module param or supplicant.
+ */
+struct brcmf_p2p_info {
+ struct brcmf_cfg80211_info *cfg;
+ unsigned long status;
+ u8 dev_addr[ETH_ALEN];
+ u8 int_addr[ETH_ALEN];
+ struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX];
+ struct timer_list listen_timer;
+ u8 listen_channel;
+ struct ieee80211_channel remain_on_channel;
+ u32 remain_on_channel_cookie;
+ u8 next_af_subtype;
+ struct completion send_af_done;
+ struct afx_hdl afx_hdl;
+ u32 af_sent_channel;
+ unsigned long af_tx_sent_jiffies;
+ struct completion wait_next_af;
+ bool gon_req_action;
+ bool block_gon_req_tx;
+ bool p2pdev_dynamically;
+};
+
+s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced);
+void brcmf_p2p_detach(struct brcmf_p2p_info *p2p);
+struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params);
+int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev);
+int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
+ enum brcmf_fil_p2p_if_types if_type);
+void brcmf_p2p_ifp_removed(struct brcmf_if *ifp);
+int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev);
+void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev);
+int brcmf_p2p_scan_prep(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request,
+ struct brcmf_cfg80211_vif *vif);
+int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *channel,
+ unsigned int duration, u64 *cookie);
+int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp);
+int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ struct brcmf_fil_af_params_le *af_params);
+bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bi);
+s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+#endif /* WL_CFGP2P_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
new file mode 100644
index 000000000000..0480b70e3eb8
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -0,0 +1,2007 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/bcma/bcma.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>
+
+#include <soc.h>
+#include <chipcommon.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <brcm_hw_ids.h>
+
+#include "debug.h"
+#include "bus.h"
+#include "commonring.h"
+#include "msgbuf.h"
+#include "pcie.h"
+#include "firmware.h"
+#include "chip.h"
+
+
+enum brcmf_pcie_state {
+ BRCMFMAC_PCIE_STATE_DOWN,
+ BRCMFMAC_PCIE_STATE_UP
+};
+
+BRCMF_FW_NVRAM_DEF(43602, "brcmfmac43602-pcie.bin", "brcmfmac43602-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4350, "brcmfmac4350-pcie.bin", "brcmfmac4350-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4350C, "brcmfmac4350c2-pcie.bin", "brcmfmac4350c2-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-pcie.bin", "brcmfmac4356-pcie.txt");
+BRCMF_FW_NVRAM_DEF(43570, "brcmfmac43570-pcie.bin", "brcmfmac43570-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4358, "brcmfmac4358-pcie.bin", "brcmfmac4358-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-pcie.bin", "brcmfmac4359-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4365B, "brcmfmac4365b-pcie.bin", "brcmfmac4365b-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4366B, "brcmfmac4366b-pcie.bin", "brcmfmac4366b-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt");
+
+static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0x000000FF, 4350C),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFF00, 4350),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43567_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43570_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFFF, 4365B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFFF, 4366B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
+};
+
+#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
+
+#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024)
+#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024)
+
+/* backplane addres space accessed by BAR0 */
+#define BRCMF_PCIE_BAR0_WINDOW 0x80
+#define BRCMF_PCIE_BAR0_REG_SIZE 0x1000
+#define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70
+
+#define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000
+#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000
+
+#define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40
+#define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C
+
+#define BRCMF_PCIE_REG_INTSTATUS 0x90
+#define BRCMF_PCIE_REG_INTMASK 0x94
+#define BRCMF_PCIE_REG_SBMBX 0x98
+
+#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC
+
+#define BRCMF_PCIE_PCIE2REG_INTMASK 0x24
+#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48
+#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C
+#define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120
+#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124
+#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140
+
+#define BRCMF_PCIE_GENREV1 1
+#define BRCMF_PCIE_GENREV2 2
+
+#define BRCMF_PCIE2_INTA 0x01
+#define BRCMF_PCIE2_INTB 0x02
+
+#define BRCMF_PCIE_INT_0 0x01
+#define BRCMF_PCIE_INT_1 0x02
+#define BRCMF_PCIE_INT_DEF (BRCMF_PCIE_INT_0 | \
+ BRCMF_PCIE_INT_1)
+
+#define BRCMF_PCIE_MB_INT_FN0_0 0x0100
+#define BRCMF_PCIE_MB_INT_FN0_1 0x0200
+#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000
+#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000
+#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000
+#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000
+#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000
+#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000
+#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000
+#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000
+
+#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H0_DB1 | \
+ BRCMF_PCIE_MB_INT_D2H1_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H1_DB1 | \
+ BRCMF_PCIE_MB_INT_D2H2_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H2_DB1 | \
+ BRCMF_PCIE_MB_INT_D2H3_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H3_DB1)
+
+#define BRCMF_PCIE_MIN_SHARED_VERSION 5
+#define BRCMF_PCIE_MAX_SHARED_VERSION 5
+#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF
+#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000
+#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000
+
+#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000
+#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000
+
+#define BRCMF_SHARED_MAX_RXBUFPOST_OFFSET 34
+#define BRCMF_SHARED_RING_BASE_OFFSET 52
+#define BRCMF_SHARED_RX_DATAOFFSET_OFFSET 36
+#define BRCMF_SHARED_CONSOLE_ADDR_OFFSET 20
+#define BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET 40
+#define BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET 44
+#define BRCMF_SHARED_RING_INFO_ADDR_OFFSET 48
+#define BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET 52
+#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56
+#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64
+#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68
+
+#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0
+#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1
+#define BRCMF_RING_H2D_RING_MEM_OFFSET 4
+#define BRCMF_RING_H2D_RING_STATE_OFFSET 8
+
+#define BRCMF_RING_MEM_BASE_ADDR_OFFSET 8
+#define BRCMF_RING_MAX_ITEM_OFFSET 4
+#define BRCMF_RING_LEN_ITEMS_OFFSET 6
+#define BRCMF_RING_MEM_SZ 16
+#define BRCMF_RING_STATE_SZ 8
+
+#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET 4
+#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8
+#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12
+#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16
+#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20
+#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28
+#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36
+#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44
+#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0
+#define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52
+
+#define BRCMF_DEF_MAX_RXBUFPOST 255
+
+#define BRCMF_CONSOLE_BUFADDR_OFFSET 8
+#define BRCMF_CONSOLE_BUFSIZE_OFFSET 12
+#define BRCMF_CONSOLE_WRITEIDX_OFFSET 16
+
+#define BRCMF_DMA_D2H_SCRATCH_BUF_LEN 8
+#define BRCMF_DMA_D2H_RINGUPD_BUF_LEN 1024
+
+#define BRCMF_D2H_DEV_D3_ACK 0x00000001
+#define BRCMF_D2H_DEV_DS_ENTER_REQ 0x00000002
+#define BRCMF_D2H_DEV_DS_EXIT_NOTE 0x00000004
+
+#define BRCMF_H2D_HOST_D3_INFORM 0x00000001
+#define BRCMF_H2D_HOST_DS_ACK 0x00000002
+#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008
+#define BRCMF_H2D_HOST_D0_INFORM 0x00000010
+
+#define BRCMF_PCIE_MBDATA_TIMEOUT msecs_to_jiffies(2000)
+
+#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4
+#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C
+#define BRCMF_PCIE_CFGREG_MSI_CAP 0x58
+#define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C
+#define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60
+#define BRCMF_PCIE_CFGREG_MSI_DATA 0x64
+#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC
+#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC
+#define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228
+#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248
+#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0
+#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4
+#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3
+
+
+struct brcmf_pcie_console {
+ u32 base_addr;
+ u32 buf_addr;
+ u32 bufsize;
+ u32 read_idx;
+ u8 log_str[256];
+ u8 log_idx;
+};
+
+struct brcmf_pcie_shared_info {
+ u32 tcm_base_address;
+ u32 flags;
+ struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS];
+ struct brcmf_pcie_ringbuf *flowrings;
+ u16 max_rxbufpost;
+ u32 nrof_flowrings;
+ u32 rx_dataoffset;
+ u32 htod_mb_data_addr;
+ u32 dtoh_mb_data_addr;
+ u32 ring_info_addr;
+ struct brcmf_pcie_console console;
+ void *scratch;
+ dma_addr_t scratch_dmahandle;
+ void *ringupd;
+ dma_addr_t ringupd_dmahandle;
+};
+
+struct brcmf_pcie_core_info {
+ u32 base;
+ u32 wrapbase;
+};
+
+struct brcmf_pciedev_info {
+ enum brcmf_pcie_state state;
+ bool in_irq;
+ struct pci_dev *pdev;
+ char fw_name[BRCMF_FW_NAME_LEN];
+ char nvram_name[BRCMF_FW_NAME_LEN];
+ void __iomem *regs;
+ void __iomem *tcm;
+ u32 tcm_size;
+ u32 ram_base;
+ u32 ram_size;
+ struct brcmf_chip *ci;
+ u32 coreid;
+ u32 generic_corerev;
+ struct brcmf_pcie_shared_info shared;
+ void (*ringbell)(struct brcmf_pciedev_info *devinfo);
+ wait_queue_head_t mbdata_resp_wait;
+ bool mbdata_completed;
+ bool irq_allocated;
+ bool wowl_enabled;
+ u8 dma_idx_sz;
+ void *idxbuf;
+ u32 idxbuf_sz;
+ dma_addr_t idxbuf_dmahandle;
+ u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset);
+ void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u16 value);
+};
+
+struct brcmf_pcie_ringbuf {
+ struct brcmf_commonring commonring;
+ dma_addr_t dma_handle;
+ u32 w_idx_addr;
+ u32 r_idx_addr;
+ struct brcmf_pciedev_info *devinfo;
+ u8 id;
+};
+
+
+static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = {
+ BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM,
+ BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM,
+ BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM,
+ BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM,
+ BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM
+};
+
+static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = {
+ BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE,
+ BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE,
+ BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE,
+ BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE,
+ BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE
+};
+
+
+static u32
+brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset)
+{
+ void __iomem *address = devinfo->regs + reg_offset;
+
+ return (ioread32(address));
+}
+
+
+static void
+brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset,
+ u32 value)
+{
+ void __iomem *address = devinfo->regs + reg_offset;
+
+ iowrite32(value, address);
+}
+
+
+static u8
+brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ return (ioread8(address));
+}
+
+
+static u16
+brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ return (ioread16(address));
+}
+
+
+static void
+brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u16 value)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ iowrite16(value, address);
+}
+
+
+static u16
+brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ u16 *address = devinfo->idxbuf + mem_offset;
+
+ return (*(address));
+}
+
+
+static void
+brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u16 value)
+{
+ u16 *address = devinfo->idxbuf + mem_offset;
+
+ *(address) = value;
+}
+
+
+static u32
+brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ return (ioread32(address));
+}
+
+
+static void
+brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u32 value)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ iowrite32(value, address);
+}
+
+
+static u32
+brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset;
+
+ return (ioread32(addr));
+}
+
+
+static void
+brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u32 value)
+{
+ void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset;
+
+ iowrite32(value, addr);
+}
+
+
+static void
+brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ void *srcaddr, u32 len)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+ __le32 *src32;
+ __le16 *src16;
+ u8 *src8;
+
+ if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) {
+ if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) {
+ src8 = (u8 *)srcaddr;
+ while (len) {
+ iowrite8(*src8, address);
+ address++;
+ src8++;
+ len--;
+ }
+ } else {
+ len = len / 2;
+ src16 = (__le16 *)srcaddr;
+ while (len) {
+ iowrite16(le16_to_cpu(*src16), address);
+ address += 2;
+ src16++;
+ len--;
+ }
+ }
+ } else {
+ len = len / 4;
+ src32 = (__le32 *)srcaddr;
+ while (len) {
+ iowrite32(le32_to_cpu(*src32), address);
+ address += 4;
+ src32++;
+ len--;
+ }
+ }
+}
+
+
+static void
+brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ void *dstaddr, u32 len)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+ __le32 *dst32;
+ __le16 *dst16;
+ u8 *dst8;
+
+ if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) {
+ if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) {
+ dst8 = (u8 *)dstaddr;
+ while (len) {
+ *dst8 = ioread8(address);
+ address++;
+ dst8++;
+ len--;
+ }
+ } else {
+ len = len / 2;
+ dst16 = (__le16 *)dstaddr;
+ while (len) {
+ *dst16 = cpu_to_le16(ioread16(address));
+ address += 2;
+ dst16++;
+ len--;
+ }
+ }
+ } else {
+ len = len / 4;
+ dst32 = (__le32 *)dstaddr;
+ while (len) {
+ *dst32 = cpu_to_le32(ioread32(address));
+ address += 4;
+ dst32++;
+ len--;
+ }
+ }
+}
+
+
+#define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \
+ CHIPCREGOFFS(reg), value)
+
+
+static void
+brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid)
+{
+ const struct pci_dev *pdev = devinfo->pdev;
+ struct brcmf_core *core;
+ u32 bar0_win;
+
+ core = brcmf_chip_get_core(devinfo->ci, coreid);
+ if (core) {
+ bar0_win = core->base;
+ pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, bar0_win);
+ if (pci_read_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW,
+ &bar0_win) == 0) {
+ if (bar0_win != core->base) {
+ bar0_win = core->base;
+ pci_write_config_dword(pdev,
+ BRCMF_PCIE_BAR0_WINDOW,
+ bar0_win);
+ }
+ }
+ } else {
+ brcmf_err("Unsupported core selected %x\n", coreid);
+ }
+}
+
+
+static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_core *core;
+ u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD,
+ BRCMF_PCIE_CFGREG_PM_CSR,
+ BRCMF_PCIE_CFGREG_MSI_CAP,
+ BRCMF_PCIE_CFGREG_MSI_ADDR_L,
+ BRCMF_PCIE_CFGREG_MSI_ADDR_H,
+ BRCMF_PCIE_CFGREG_MSI_DATA,
+ BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2,
+ BRCMF_PCIE_CFGREG_RBAR_CTRL,
+ BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1,
+ BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG,
+ BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG };
+ u32 i;
+ u32 val;
+ u32 lsc;
+
+ if (!devinfo->ci)
+ return;
+
+ /* Disable ASPM */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+ &lsc);
+ val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+ val);
+
+ /* Watchdog reset */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON);
+ WRITECC32(devinfo, watchdog, 4);
+ msleep(100);
+
+ /* Restore ASPM */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+ lsc);
+
+ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
+ if (core->rev <= 13) {
+ for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
+ brcmf_pcie_write_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+ cfg_offset[i]);
+ val = brcmf_pcie_read_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+ brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
+ cfg_offset[i], val);
+ brcmf_pcie_write_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGDATA,
+ val);
+ }
+ }
+}
+
+
+static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo)
+{
+ u32 config;
+
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ /* BAR1 window may not be sized properly */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0);
+ config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config);
+
+ device_wakeup_enable(&devinfo->pdev->dev);
+}
+
+
+static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
+ 5);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA,
+ 0);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
+ 7);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA,
+ 0);
+ }
+ return 0;
+}
+
+
+static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo,
+ u32 resetintr)
+{
+ struct brcmf_core *core;
+
+ if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
+ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_INTERNAL_MEM);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+ }
+
+ if (!brcmf_chip_set_active(devinfo->ci, resetintr))
+ return -EINVAL;
+ return 0;
+}
+
+
+static int
+brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
+{
+ struct brcmf_pcie_shared_info *shared;
+ u32 addr;
+ u32 cur_htod_mb_data;
+ u32 i;
+
+ shared = &devinfo->shared;
+ addr = shared->htod_mb_data_addr;
+ cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ if (cur_htod_mb_data != 0)
+ brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n",
+ cur_htod_mb_data);
+
+ i = 0;
+ while (cur_htod_mb_data != 0) {
+ msleep(10);
+ i++;
+ if (i > 100)
+ return -EIO;
+ cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+ }
+
+ brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
+
+ return 0;
+}
+
+
+static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_shared_info *shared;
+ u32 addr;
+ u32 dtoh_mb_data;
+
+ shared = &devinfo->shared;
+ addr = shared->dtoh_mb_data_addr;
+ dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ if (!dtoh_mb_data)
+ return;
+
+ brcmf_pcie_write_tcm32(devinfo, addr, 0);
+
+ brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data);
+ if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) {
+ brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n");
+ brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK);
+ brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n");
+ }
+ if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE)
+ brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n");
+ if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) {
+ brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n");
+ if (waitqueue_active(&devinfo->mbdata_resp_wait)) {
+ devinfo->mbdata_completed = true;
+ wake_up(&devinfo->mbdata_resp_wait);
+ }
+ }
+}
+
+
+static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_shared_info *shared;
+ struct brcmf_pcie_console *console;
+ u32 addr;
+
+ shared = &devinfo->shared;
+ console = &shared->console;
+ addr = shared->tcm_base_address + BRCMF_SHARED_CONSOLE_ADDR_OFFSET;
+ console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET;
+ console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+ addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET;
+ console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n",
+ console->base_addr, console->buf_addr, console->bufsize);
+}
+
+
+static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_console *console;
+ u32 addr;
+ u8 ch;
+ u32 newidx;
+
+ if (!BRCMF_FWCON_ON())
+ return;
+
+ console = &devinfo->shared.console;
+ addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET;
+ newidx = brcmf_pcie_read_tcm32(devinfo, addr);
+ while (newidx != console->read_idx) {
+ addr = console->buf_addr + console->read_idx;
+ ch = brcmf_pcie_read_tcm8(devinfo, addr);
+ console->read_idx++;
+ if (console->read_idx == console->bufsize)
+ console->read_idx = 0;
+ if (ch == '\r')
+ continue;
+ console->log_str[console->log_idx] = ch;
+ console->log_idx++;
+ if ((ch != '\n') &&
+ (console->log_idx == (sizeof(console->log_str) - 2))) {
+ ch = '\n';
+ console->log_str[console->log_idx] = ch;
+ console->log_idx++;
+ }
+ if (ch == '\n') {
+ console->log_str[console->log_idx] = 0;
+ pr_debug("CONSOLE: %s", console->log_str);
+ console->log_idx = 0;
+ }
+ }
+}
+
+
+static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo)
+{
+ u32 reg_value;
+
+ brcmf_dbg(PCIE, "RING !\n");
+ reg_value = brcmf_pcie_read_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ reg_value |= BRCMF_PCIE2_INTB;
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+ reg_value);
+}
+
+
+static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo)
+{
+ brcmf_dbg(PCIE, "RING !\n");
+ /* Any arbitrary value will do, lets use 1 */
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
+}
+
+
+static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
+ 0);
+ else
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
+ 0);
+}
+
+
+static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
+ BRCMF_PCIE_INT_DEF);
+ else
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
+ BRCMF_PCIE_MB_INT_D2H_DB |
+ BRCMF_PCIE_MB_INT_FN0_0 |
+ BRCMF_PCIE_MB_INT_FN0_1);
+}
+
+
+static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+ u32 status;
+
+ status = 0;
+ pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
+ if (status) {
+ brcmf_pcie_intr_disable(devinfo);
+ brcmf_dbg(PCIE, "Enter\n");
+ return IRQ_WAKE_THREAD;
+ }
+ return IRQ_NONE;
+}
+
+
+static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+
+ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) {
+ brcmf_pcie_intr_disable(devinfo);
+ brcmf_dbg(PCIE, "Enter\n");
+ return IRQ_WAKE_THREAD;
+ }
+ return IRQ_NONE;
+}
+
+
+static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+ const struct pci_dev *pdev = devinfo->pdev;
+ u32 status;
+
+ devinfo->in_irq = true;
+ status = 0;
+ pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
+ brcmf_dbg(PCIE, "Enter %x\n", status);
+ if (status) {
+ pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+ brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev);
+ }
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+ brcmf_pcie_intr_enable(devinfo);
+ devinfo->in_irq = false;
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+ u32 status;
+
+ devinfo->in_irq = true;
+ status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ brcmf_dbg(PCIE, "Enter %x\n", status);
+ if (status) {
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+ status);
+ if (status & (BRCMF_PCIE_MB_INT_FN0_0 |
+ BRCMF_PCIE_MB_INT_FN0_1))
+ brcmf_pcie_handle_mb_data(devinfo);
+ if (status & BRCMF_PCIE_MB_INT_D2H_DB) {
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+ brcmf_proto_msgbuf_rx_trigger(
+ &devinfo->pdev->dev);
+ }
+ }
+ brcmf_pcie_bus_console_read(devinfo);
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+ brcmf_pcie_intr_enable(devinfo);
+ devinfo->in_irq = false;
+ return IRQ_HANDLED;
+}
+
+
+static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
+{
+ struct pci_dev *pdev;
+
+ pdev = devinfo->pdev;
+
+ brcmf_pcie_intr_disable(devinfo);
+
+ brcmf_dbg(PCIE, "Enter\n");
+ /* is it a v1 or v2 implementation */
+ pci_enable_msi(pdev);
+ if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
+ if (request_threaded_irq(pdev->irq,
+ brcmf_pcie_quick_check_isr_v1,
+ brcmf_pcie_isr_thread_v1,
+ IRQF_SHARED, "brcmf_pcie_intr",
+ devinfo)) {
+ pci_disable_msi(pdev);
+ brcmf_err("Failed to request IRQ %d\n", pdev->irq);
+ return -EIO;
+ }
+ } else {
+ if (request_threaded_irq(pdev->irq,
+ brcmf_pcie_quick_check_isr_v2,
+ brcmf_pcie_isr_thread_v2,
+ IRQF_SHARED, "brcmf_pcie_intr",
+ devinfo)) {
+ pci_disable_msi(pdev);
+ brcmf_err("Failed to request IRQ %d\n", pdev->irq);
+ return -EIO;
+ }
+ }
+ devinfo->irq_allocated = true;
+ return 0;
+}
+
+
+static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo)
+{
+ struct pci_dev *pdev;
+ u32 status;
+ u32 count;
+
+ if (!devinfo->irq_allocated)
+ return;
+
+ pdev = devinfo->pdev;
+
+ brcmf_pcie_intr_disable(devinfo);
+ free_irq(pdev->irq, devinfo);
+ pci_disable_msi(pdev);
+
+ msleep(50);
+ count = 0;
+ while ((devinfo->in_irq) && (count < 20)) {
+ msleep(50);
+ count++;
+ }
+ if (devinfo->in_irq)
+ brcmf_err("Still in IRQ (processing) !!!\n");
+
+ if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
+ status = 0;
+ pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
+ pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
+ } else {
+ status = brcmf_pcie_read_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+ status);
+ }
+ devinfo->irq_allocated = false;
+}
+
+
+static int brcmf_pcie_ring_mb_write_rptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr,
+ commonring->w_ptr, ring->id);
+
+ devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_write_wptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr,
+ commonring->r_ptr, ring->id);
+
+ devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_ring_bell(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ devinfo->ringbell(devinfo);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_update_rptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr);
+
+ brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr,
+ commonring->w_ptr, ring->id);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_update_wptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr);
+
+ brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr,
+ commonring->r_ptr, ring->id);
+
+ return 0;
+}
+
+
+static void *
+brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo,
+ u32 size, u32 tcm_dma_phys_addr,
+ dma_addr_t *dma_handle)
+{
+ void *ring;
+ u64 address;
+
+ ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle,
+ GFP_KERNEL);
+ if (!ring)
+ return NULL;
+
+ address = (u64)*dma_handle;
+ brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr,
+ address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32);
+
+ memset(ring, 0, size);
+
+ return (ring);
+}
+
+
+static struct brcmf_pcie_ringbuf *
+brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id,
+ u32 tcm_ring_phys_addr)
+{
+ void *dma_buf;
+ dma_addr_t dma_handle;
+ struct brcmf_pcie_ringbuf *ring;
+ u32 size;
+ u32 addr;
+
+ size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id];
+ dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size,
+ tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET,
+ &dma_handle);
+ if (!dma_buf)
+ return NULL;
+
+ addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET;
+ brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]);
+ addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET;
+ brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]);
+
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+ if (!ring) {
+ dma_free_coherent(&devinfo->pdev->dev, size, dma_buf,
+ dma_handle);
+ return NULL;
+ }
+ brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id],
+ brcmf_ring_itemsize[ring_id], dma_buf);
+ ring->dma_handle = dma_handle;
+ ring->devinfo = devinfo;
+ brcmf_commonring_register_cb(&ring->commonring,
+ brcmf_pcie_ring_mb_ring_bell,
+ brcmf_pcie_ring_mb_update_rptr,
+ brcmf_pcie_ring_mb_update_wptr,
+ brcmf_pcie_ring_mb_write_rptr,
+ brcmf_pcie_ring_mb_write_wptr, ring);
+
+ return (ring);
+}
+
+
+static void brcmf_pcie_release_ringbuffer(struct device *dev,
+ struct brcmf_pcie_ringbuf *ring)
+{
+ void *dma_buf;
+ u32 size;
+
+ if (!ring)
+ return;
+
+ dma_buf = ring->commonring.buf_addr;
+ if (dma_buf) {
+ size = ring->commonring.depth * ring->commonring.item_len;
+ dma_free_coherent(dev, size, dma_buf, ring->dma_handle);
+ }
+ kfree(ring);
+}
+
+
+static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ u32 i;
+
+ for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) {
+ brcmf_pcie_release_ringbuffer(&devinfo->pdev->dev,
+ devinfo->shared.commonrings[i]);
+ devinfo->shared.commonrings[i] = NULL;
+ }
+ kfree(devinfo->shared.flowrings);
+ devinfo->shared.flowrings = NULL;
+ if (devinfo->idxbuf) {
+ dma_free_coherent(&devinfo->pdev->dev,
+ devinfo->idxbuf_sz,
+ devinfo->idxbuf,
+ devinfo->idxbuf_dmahandle);
+ devinfo->idxbuf = NULL;
+ }
+}
+
+
+static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_ringbuf *ring;
+ struct brcmf_pcie_ringbuf *rings;
+ u32 ring_addr;
+ u32 d2h_w_idx_ptr;
+ u32 d2h_r_idx_ptr;
+ u32 h2d_w_idx_ptr;
+ u32 h2d_r_idx_ptr;
+ u32 addr;
+ u32 ring_mem_ptr;
+ u32 i;
+ u64 address;
+ u32 bufsz;
+ u16 max_sub_queues;
+ u8 idx_offset;
+
+ ring_addr = devinfo->shared.ring_info_addr;
+ brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr);
+ addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES;
+ max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr);
+
+ if (devinfo->dma_idx_sz != 0) {
+ bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) *
+ devinfo->dma_idx_sz * 2;
+ devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz,
+ &devinfo->idxbuf_dmahandle,
+ GFP_KERNEL);
+ if (!devinfo->idxbuf)
+ devinfo->dma_idx_sz = 0;
+ }
+
+ if (devinfo->dma_idx_sz == 0) {
+ addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET;
+ d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
+ addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET;
+ d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
+ addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET;
+ h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
+ addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET;
+ h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
+ idx_offset = sizeof(u32);
+ devinfo->write_ptr = brcmf_pcie_write_tcm16;
+ devinfo->read_ptr = brcmf_pcie_read_tcm16;
+ brcmf_dbg(PCIE, "Using TCM indices\n");
+ } else {
+ memset(devinfo->idxbuf, 0, bufsz);
+ devinfo->idxbuf_sz = bufsz;
+ idx_offset = devinfo->dma_idx_sz;
+ devinfo->write_ptr = brcmf_pcie_write_idx;
+ devinfo->read_ptr = brcmf_pcie_read_idx;
+
+ h2d_w_idx_ptr = 0;
+ addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET;
+ address = (u64)devinfo->idxbuf_dmahandle;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+
+ h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset;
+ addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET;
+ address += max_sub_queues * idx_offset;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+
+ d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset;
+ addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET;
+ address += max_sub_queues * idx_offset;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+
+ d2h_r_idx_ptr = d2h_w_idx_ptr +
+ BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset;
+ addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET;
+ address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+ brcmf_dbg(PCIE, "Using host memory indices\n");
+ }
+
+ addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET;
+ ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) {
+ ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr);
+ if (!ring)
+ goto fail;
+ ring->w_idx_addr = h2d_w_idx_ptr;
+ ring->r_idx_addr = h2d_r_idx_ptr;
+ ring->id = i;
+ devinfo->shared.commonrings[i] = ring;
+
+ h2d_w_idx_ptr += idx_offset;
+ h2d_r_idx_ptr += idx_offset;
+ ring_mem_ptr += BRCMF_RING_MEM_SZ;
+ }
+
+ for (i = BRCMF_NROF_H2D_COMMON_MSGRINGS;
+ i < BRCMF_NROF_COMMON_MSGRINGS; i++) {
+ ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr);
+ if (!ring)
+ goto fail;
+ ring->w_idx_addr = d2h_w_idx_ptr;
+ ring->r_idx_addr = d2h_r_idx_ptr;
+ ring->id = i;
+ devinfo->shared.commonrings[i] = ring;
+
+ d2h_w_idx_ptr += idx_offset;
+ d2h_r_idx_ptr += idx_offset;
+ ring_mem_ptr += BRCMF_RING_MEM_SZ;
+ }
+
+ devinfo->shared.nrof_flowrings =
+ max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS;
+ rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring),
+ GFP_KERNEL);
+ if (!rings)
+ goto fail;
+
+ brcmf_dbg(PCIE, "Nr of flowrings is %d\n",
+ devinfo->shared.nrof_flowrings);
+
+ for (i = 0; i < devinfo->shared.nrof_flowrings; i++) {
+ ring = &rings[i];
+ ring->devinfo = devinfo;
+ ring->id = i + BRCMF_NROF_COMMON_MSGRINGS;
+ brcmf_commonring_register_cb(&ring->commonring,
+ brcmf_pcie_ring_mb_ring_bell,
+ brcmf_pcie_ring_mb_update_rptr,
+ brcmf_pcie_ring_mb_update_wptr,
+ brcmf_pcie_ring_mb_write_rptr,
+ brcmf_pcie_ring_mb_write_wptr,
+ ring);
+ ring->w_idx_addr = h2d_w_idx_ptr;
+ ring->r_idx_addr = h2d_r_idx_ptr;
+ h2d_w_idx_ptr += idx_offset;
+ h2d_r_idx_ptr += idx_offset;
+ }
+ devinfo->shared.flowrings = rings;
+
+ return 0;
+
+fail:
+ brcmf_err("Allocating ring buffers failed\n");
+ brcmf_pcie_release_ringbuffers(devinfo);
+ return -ENOMEM;
+}
+
+
+static void
+brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->shared.scratch)
+ dma_free_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
+ devinfo->shared.scratch,
+ devinfo->shared.scratch_dmahandle);
+ if (devinfo->shared.ringupd)
+ dma_free_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
+ devinfo->shared.ringupd,
+ devinfo->shared.ringupd_dmahandle);
+}
+
+static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ u64 address;
+ u32 addr;
+
+ devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
+ &devinfo->shared.scratch_dmahandle, GFP_KERNEL);
+ if (!devinfo->shared.scratch)
+ goto fail;
+
+ memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN);
+
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET;
+ address = (u64)devinfo->shared.scratch_dmahandle;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET;
+ brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_SCRATCH_BUF_LEN);
+
+ devinfo->shared.ringupd = dma_alloc_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
+ &devinfo->shared.ringupd_dmahandle, GFP_KERNEL);
+ if (!devinfo->shared.ringupd)
+ goto fail;
+
+ memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN);
+
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET;
+ address = (u64)devinfo->shared.ringupd_dmahandle;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET;
+ brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_RINGUPD_BUF_LEN);
+ return 0;
+
+fail:
+ brcmf_err("Allocating scratch buffers failed\n");
+ brcmf_pcie_release_scratchbuffers(devinfo);
+ return -ENOMEM;
+}
+
+
+static void brcmf_pcie_down(struct device *dev)
+{
+}
+
+
+static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb)
+{
+ return 0;
+}
+
+
+static int brcmf_pcie_tx_ctlpkt(struct device *dev, unsigned char *msg,
+ uint len)
+{
+ return 0;
+}
+
+
+static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg,
+ uint len)
+{
+ return 0;
+}
+
+
+static void brcmf_pcie_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+ brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled);
+ devinfo->wowl_enabled = enabled;
+}
+
+
+static size_t brcmf_pcie_get_ramsize(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+ return devinfo->ci->ramsize - devinfo->ci->srsize;
+}
+
+
+static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+ brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len);
+ brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len);
+ return 0;
+}
+
+
+static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
+ .txdata = brcmf_pcie_tx,
+ .stop = brcmf_pcie_down,
+ .txctl = brcmf_pcie_tx_ctlpkt,
+ .rxctl = brcmf_pcie_rx_ctlpkt,
+ .wowl_config = brcmf_pcie_wowl_config,
+ .get_ramsize = brcmf_pcie_get_ramsize,
+ .get_memdump = brcmf_pcie_get_memdump,
+};
+
+
+static int
+brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
+ u32 sharedram_addr)
+{
+ struct brcmf_pcie_shared_info *shared;
+ u32 addr;
+ u32 version;
+
+ shared = &devinfo->shared;
+ shared->tcm_base_address = sharedram_addr;
+
+ shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr);
+ version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK;
+ brcmf_dbg(PCIE, "PCIe protocol version %d\n", version);
+ if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) ||
+ (version < BRCMF_PCIE_MIN_SHARED_VERSION)) {
+ brcmf_err("Unsupported PCIE version %d\n", version);
+ return -EINVAL;
+ }
+
+ /* check firmware support dma indicies */
+ if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) {
+ if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX)
+ devinfo->dma_idx_sz = sizeof(u16);
+ else
+ devinfo->dma_idx_sz = sizeof(u32);
+ }
+
+ addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET;
+ shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr);
+ if (shared->max_rxbufpost == 0)
+ shared->max_rxbufpost = BRCMF_DEF_MAX_RXBUFPOST;
+
+ addr = sharedram_addr + BRCMF_SHARED_RX_DATAOFFSET_OFFSET;
+ shared->rx_dataoffset = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = sharedram_addr + BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET;
+ shared->htod_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = sharedram_addr + BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET;
+ shared->dtoh_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET;
+ shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n",
+ shared->max_rxbufpost, shared->rx_dataoffset);
+
+ brcmf_pcie_bus_console_init(devinfo);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
+ const struct firmware *fw, void *nvram,
+ u32 nvram_len)
+{
+ u32 sharedram_addr;
+ u32 sharedram_addr_written;
+ u32 loop_counter;
+ int err;
+ u32 address;
+ u32 resetintr;
+
+ devinfo->ringbell = brcmf_pcie_ringbell_v2;
+ devinfo->generic_corerev = BRCMF_PCIE_GENREV2;
+
+ brcmf_dbg(PCIE, "Halt ARM.\n");
+ err = brcmf_pcie_enter_download_state(devinfo);
+ if (err)
+ return err;
+
+ brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name);
+ brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase,
+ (void *)fw->data, fw->size);
+
+ resetintr = get_unaligned_le32(fw->data);
+ release_firmware(fw);
+
+ /* reset last 4 bytes of RAM address. to be used for shared
+ * area. This identifies when FW is running
+ */
+ brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0);
+
+ if (nvram) {
+ brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name);
+ address = devinfo->ci->rambase + devinfo->ci->ramsize -
+ nvram_len;
+ brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len);
+ brcmf_fw_nvram_free(nvram);
+ } else {
+ brcmf_dbg(PCIE, "No matching NVRAM file found %s\n",
+ devinfo->nvram_name);
+ }
+
+ sharedram_addr_written = brcmf_pcie_read_ram32(devinfo,
+ devinfo->ci->ramsize -
+ 4);
+ brcmf_dbg(PCIE, "Bring ARM in running state\n");
+ err = brcmf_pcie_exit_download_state(devinfo, resetintr);
+ if (err)
+ return err;
+
+ brcmf_dbg(PCIE, "Wait for FW init\n");
+ sharedram_addr = sharedram_addr_written;
+ loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50;
+ while ((sharedram_addr == sharedram_addr_written) && (loop_counter)) {
+ msleep(50);
+ sharedram_addr = brcmf_pcie_read_ram32(devinfo,
+ devinfo->ci->ramsize -
+ 4);
+ loop_counter--;
+ }
+ if (sharedram_addr == sharedram_addr_written) {
+ brcmf_err("FW failed to initialize\n");
+ return -ENODEV;
+ }
+ brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr);
+
+ return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr));
+}
+
+
+static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo)
+{
+ struct pci_dev *pdev;
+ int err;
+ phys_addr_t bar0_addr, bar1_addr;
+ ulong bar1_size;
+
+ pdev = devinfo->pdev;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ brcmf_err("pci_enable_device failed err=%d\n", err);
+ return err;
+ }
+
+ pci_set_master(pdev);
+
+ /* Bar-0 mapped address */
+ bar0_addr = pci_resource_start(pdev, 0);
+ /* Bar-1 mapped address */
+ bar1_addr = pci_resource_start(pdev, 2);
+ /* read Bar-1 mapped memory range */
+ bar1_size = pci_resource_len(pdev, 2);
+ if ((bar1_size == 0) || (bar1_addr == 0)) {
+ brcmf_err("BAR1 Not enabled, device size=%ld, addr=%#016llx\n",
+ bar1_size, (unsigned long long)bar1_addr);
+ return -EINVAL;
+ }
+
+ devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE);
+ devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE);
+ devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE;
+
+ if (!devinfo->regs || !devinfo->tcm) {
+ brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs,
+ devinfo->tcm);
+ return -EINVAL;
+ }
+ brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n",
+ devinfo->regs, (unsigned long long)bar0_addr);
+ brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n",
+ devinfo->tcm, (unsigned long long)bar1_addr);
+
+ return 0;
+}
+
+
+static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->tcm)
+ iounmap(devinfo->tcm);
+ if (devinfo->regs)
+ iounmap(devinfo->regs);
+
+ pci_disable_device(devinfo->pdev);
+}
+
+
+static int brcmf_pcie_attach_bus(struct device *dev)
+{
+ int ret;
+
+ /* Attach to the common driver interface */
+ ret = brcmf_attach(dev);
+ if (ret) {
+ brcmf_err("brcmf_attach failed\n");
+ } else {
+ ret = brcmf_bus_start(dev);
+ if (ret)
+ brcmf_err("dongle is not responding\n");
+ }
+
+ return ret;
+}
+
+
+static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr)
+{
+ u32 ret_addr;
+
+ ret_addr = addr & (BRCMF_PCIE_BAR0_REG_SIZE - 1);
+ addr &= ~(BRCMF_PCIE_BAR0_REG_SIZE - 1);
+ pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, addr);
+
+ return ret_addr;
+}
+
+
+static u32 brcmf_pcie_buscore_read32(void *ctx, u32 addr)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+
+ addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr);
+ return brcmf_pcie_read_reg32(devinfo, addr);
+}
+
+
+static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+
+ addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr);
+ brcmf_pcie_write_reg32(devinfo, addr, value);
+}
+
+
+static int brcmf_pcie_buscoreprep(void *ctx)
+{
+ return brcmf_pcie_get_resource(ctx);
+}
+
+
+static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+ u32 val;
+
+ devinfo->ci = chip;
+ brcmf_pcie_reset_device(devinfo);
+
+ val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ if (val != 0xffffffff)
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+ val);
+
+ return 0;
+}
+
+
+static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+
+ brcmf_pcie_write_tcm32(devinfo, 0, rstvec);
+}
+
+
+static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
+ .prepare = brcmf_pcie_buscoreprep,
+ .reset = brcmf_pcie_buscore_reset,
+ .activate = brcmf_pcie_buscore_activate,
+ .read32 = brcmf_pcie_buscore_read32,
+ .write32 = brcmf_pcie_buscore_write32,
+};
+
+static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw,
+ void *nvram, u32 nvram_len)
+{
+ struct brcmf_bus *bus = dev_get_drvdata(dev);
+ struct brcmf_pciedev *pcie_bus_dev = bus->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo;
+ struct brcmf_commonring **flowrings;
+ int ret;
+ u32 i;
+
+ brcmf_pcie_attach(devinfo);
+
+ ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len);
+ if (ret)
+ goto fail;
+
+ devinfo->state = BRCMFMAC_PCIE_STATE_UP;
+
+ ret = brcmf_pcie_init_ringbuffers(devinfo);
+ if (ret)
+ goto fail;
+
+ ret = brcmf_pcie_init_scratchbuffers(devinfo);
+ if (ret)
+ goto fail;
+
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ ret = brcmf_pcie_request_irq(devinfo);
+ if (ret)
+ goto fail;
+
+ /* hook the commonrings in the bus structure. */
+ for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++)
+ bus->msgbuf->commonrings[i] =
+ &devinfo->shared.commonrings[i]->commonring;
+
+ flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings),
+ GFP_KERNEL);
+ if (!flowrings)
+ goto fail;
+
+ for (i = 0; i < devinfo->shared.nrof_flowrings; i++)
+ flowrings[i] = &devinfo->shared.flowrings[i].commonring;
+ bus->msgbuf->flowrings = flowrings;
+
+ bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset;
+ bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost;
+ bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings;
+
+ init_waitqueue_head(&devinfo->mbdata_resp_wait);
+
+ brcmf_pcie_intr_enable(devinfo);
+ if (brcmf_pcie_attach_bus(bus->dev) == 0)
+ return;
+
+ brcmf_pcie_bus_console_read(devinfo);
+
+fail:
+ device_release_driver(dev);
+}
+
+static int
+brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int ret;
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_pciedev *pcie_bus_dev;
+ struct brcmf_bus *bus;
+ u16 domain_nr;
+ u16 bus_nr;
+
+ domain_nr = pci_domain_nr(pdev->bus) + 1;
+ bus_nr = pdev->bus->number;
+ brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device,
+ domain_nr, bus_nr);
+
+ ret = -ENOMEM;
+ devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
+ if (devinfo == NULL)
+ return ret;
+
+ devinfo->pdev = pdev;
+ pcie_bus_dev = NULL;
+ devinfo->ci = brcmf_chip_attach(devinfo, &brcmf_pcie_buscore_ops);
+ if (IS_ERR(devinfo->ci)) {
+ ret = PTR_ERR(devinfo->ci);
+ devinfo->ci = NULL;
+ goto fail;
+ }
+
+ pcie_bus_dev = kzalloc(sizeof(*pcie_bus_dev), GFP_KERNEL);
+ if (pcie_bus_dev == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL);
+ if (!bus->msgbuf) {
+ ret = -ENOMEM;
+ kfree(bus);
+ goto fail;
+ }
+
+ /* hook it all together. */
+ pcie_bus_dev->devinfo = devinfo;
+ pcie_bus_dev->bus = bus;
+ bus->dev = &pdev->dev;
+ bus->bus_priv.pcie = pcie_bus_dev;
+ bus->ops = &brcmf_pcie_bus_ops;
+ bus->proto_type = BRCMF_PROTO_MSGBUF;
+ bus->chip = devinfo->coreid;
+ bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
+ dev_set_drvdata(&pdev->dev, bus);
+
+ ret = brcmf_fw_map_chip_to_name(devinfo->ci->chip, devinfo->ci->chiprev,
+ brcmf_pcie_fwnames,
+ ARRAY_SIZE(brcmf_pcie_fwnames),
+ devinfo->fw_name, devinfo->nvram_name);
+ if (ret)
+ goto fail_bus;
+
+ ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM |
+ BRCMF_FW_REQ_NV_OPTIONAL,
+ devinfo->fw_name, devinfo->nvram_name,
+ brcmf_pcie_setup, domain_nr, bus_nr);
+ if (ret == 0)
+ return 0;
+fail_bus:
+ kfree(bus->msgbuf);
+ kfree(bus);
+fail:
+ brcmf_err("failed %x:%x\n", pdev->vendor, pdev->device);
+ brcmf_pcie_release_resource(devinfo);
+ if (devinfo->ci)
+ brcmf_chip_detach(devinfo->ci);
+ kfree(pcie_bus_dev);
+ kfree(devinfo);
+ return ret;
+}
+
+
+static void
+brcmf_pcie_remove(struct pci_dev *pdev)
+{
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_bus *bus;
+
+ brcmf_dbg(PCIE, "Enter\n");
+
+ bus = dev_get_drvdata(&pdev->dev);
+ if (bus == NULL)
+ return;
+
+ devinfo = bus->bus_priv.pcie->devinfo;
+
+ devinfo->state = BRCMFMAC_PCIE_STATE_DOWN;
+ if (devinfo->ci)
+ brcmf_pcie_intr_disable(devinfo);
+
+ brcmf_detach(&pdev->dev);
+
+ kfree(bus->bus_priv.pcie);
+ kfree(bus->msgbuf->flowrings);
+ kfree(bus->msgbuf);
+ kfree(bus);
+
+ brcmf_pcie_release_irq(devinfo);
+ brcmf_pcie_release_scratchbuffers(devinfo);
+ brcmf_pcie_release_ringbuffers(devinfo);
+ brcmf_pcie_reset_device(devinfo);
+ brcmf_pcie_release_resource(devinfo);
+
+ if (devinfo->ci)
+ brcmf_chip_detach(devinfo->ci);
+
+ kfree(devinfo);
+ dev_set_drvdata(&pdev->dev, NULL);
+}
+
+
+#ifdef CONFIG_PM
+
+
+static int brcmf_pcie_pm_enter_D3(struct device *dev)
+{
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_bus *bus;
+
+ brcmf_dbg(PCIE, "Enter\n");
+
+ bus = dev_get_drvdata(dev);
+ devinfo = bus->bus_priv.pcie->devinfo;
+
+ brcmf_bus_change_state(bus, BRCMF_BUS_DOWN);
+
+ devinfo->mbdata_completed = false;
+ brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM);
+
+ wait_event_timeout(devinfo->mbdata_resp_wait, devinfo->mbdata_completed,
+ BRCMF_PCIE_MBDATA_TIMEOUT);
+ if (!devinfo->mbdata_completed) {
+ brcmf_err("Timeout on response for entering D3 substate\n");
+ return -EIO;
+ }
+
+ devinfo->state = BRCMFMAC_PCIE_STATE_DOWN;
+
+ return 0;
+}
+
+
+static int brcmf_pcie_pm_leave_D3(struct device *dev)
+{
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_bus *bus;
+ struct pci_dev *pdev;
+ int err;
+
+ brcmf_dbg(PCIE, "Enter\n");
+
+ bus = dev_get_drvdata(dev);
+ devinfo = bus->bus_priv.pcie->devinfo;
+ brcmf_dbg(PCIE, "Enter, dev=%p, bus=%p\n", dev, bus);
+
+ /* Check if device is still up and running, if so we are ready */
+ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) {
+ brcmf_dbg(PCIE, "Try to wakeup device....\n");
+ if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM))
+ goto cleanup;
+ brcmf_dbg(PCIE, "Hot resume, continue....\n");
+ devinfo->state = BRCMFMAC_PCIE_STATE_UP;
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ brcmf_bus_change_state(bus, BRCMF_BUS_UP);
+ brcmf_pcie_intr_enable(devinfo);
+ return 0;
+ }
+
+cleanup:
+ brcmf_chip_detach(devinfo->ci);
+ devinfo->ci = NULL;
+ pdev = devinfo->pdev;
+ brcmf_pcie_remove(pdev);
+
+ err = brcmf_pcie_probe(pdev, NULL);
+ if (err)
+ brcmf_err("probe after resume failed, err=%d\n", err);
+
+ return err;
+}
+
+
+static const struct dev_pm_ops brcmf_pciedrvr_pm = {
+ .suspend = brcmf_pcie_pm_enter_D3,
+ .resume = brcmf_pcie_pm_leave_D3,
+ .freeze = brcmf_pcie_pm_enter_D3,
+ .restore = brcmf_pcie_pm_leave_D3,
+};
+
+
+#endif /* CONFIG_PM */
+
+
+#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
+
+static struct pci_device_id brcmf_pcie_devid_table[] = {
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID),
+ { /* end: all zeroes */ }
+};
+
+
+MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table);
+
+
+static struct pci_driver brcmf_pciedrvr = {
+ .node = {},
+ .name = KBUILD_MODNAME,
+ .id_table = brcmf_pcie_devid_table,
+ .probe = brcmf_pcie_probe,
+ .remove = brcmf_pcie_remove,
+#ifdef CONFIG_PM
+ .driver.pm = &brcmf_pciedrvr_pm,
+#endif
+};
+
+
+void brcmf_pcie_register(void)
+{
+ int err;
+
+ brcmf_dbg(PCIE, "Enter\n");
+ err = pci_register_driver(&brcmf_pciedrvr);
+ if (err)
+ brcmf_err("PCIE driver registration failed, err=%d\n", err);
+}
+
+
+void brcmf_pcie_exit(void)
+{
+ brcmf_dbg(PCIE, "Enter\n");
+ pci_unregister_driver(&brcmf_pciedrvr);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
index 6edaaf8ef5ce..6edaaf8ef5ce 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
index 26b68c367f57..26b68c367f57 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
index d55119d36755..d55119d36755 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
new file mode 100644
index 000000000000..dd6614332836
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -0,0 +1,4257 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/printk.h>
+#include <linux/pci_ids.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/semaphore.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/bcma/bcma.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+#include <linux/platform_data/brcmfmac-sdio.h>
+#include <linux/moduleparam.h>
+#include <asm/unaligned.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include <soc.h>
+#include "sdio.h"
+#include "chip.h"
+#include "firmware.h"
+
+#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2000)
+#define CTL_DONE_TIMEOUT msecs_to_jiffies(2000)
+
+#ifdef DEBUG
+
+#define BRCMF_TRAP_INFO_SIZE 80
+
+#define CBUF_LEN (128)
+
+/* Device console log buffer state */
+#define CONSOLE_BUFFER_MAX 2024
+
+struct rte_log_le {
+ __le32 buf; /* Can't be pointer on (64-bit) hosts */
+ __le32 buf_size;
+ __le32 idx;
+ char *_buf_compat; /* Redundant pointer for backward compat. */
+};
+
+struct rte_console {
+ /* Virtual UART
+ * When there is no UART (e.g. Quickturn),
+ * the host should write a complete
+ * input line directly into cbuf and then write
+ * the length into vcons_in.
+ * This may also be used when there is a real UART
+ * (at risk of conflicting with
+ * the real UART). vcons_out is currently unused.
+ */
+ uint vcons_in;
+ uint vcons_out;
+
+ /* Output (logging) buffer
+ * Console output is written to a ring buffer log_buf at index log_idx.
+ * The host may read the output when it sees log_idx advance.
+ * Output will be lost if the output wraps around faster than the host
+ * polls.
+ */
+ struct rte_log_le log_le;
+
+ /* Console input line buffer
+ * Characters are read one at a time into cbuf
+ * until <CR> is received, then
+ * the buffer is processed as a command line.
+ * Also used for virtual UART.
+ */
+ uint cbuf_idx;
+ char cbuf[CBUF_LEN];
+};
+
+#endif /* DEBUG */
+#include <chipcommon.h>
+
+#include "bus.h"
+#include "debug.h"
+#include "tracepoint.h"
+
+#define TXQLEN 2048 /* bulk tx queue length */
+#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
+#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */
+#define PRIOMASK 7
+
+#define TXRETRIES 2 /* # of retries for tx frames */
+
+#define BRCMF_RXBOUND 50 /* Default for max rx frames in
+ one scheduling */
+
+#define BRCMF_TXBOUND 20 /* Default for max tx frames in
+ one scheduling */
+
+#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */
+
+#define MEMBLOCK 2048 /* Block size used for downloading
+ of dongle image */
+#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
+ biggest possible glom */
+
+#define BRCMF_FIRSTREAD (1 << 6)
+
+#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */
+
+/* SBSDIO_DEVICE_CTL */
+
+/* 1: device will assert busy signal when receiving CMD53 */
+#define SBSDIO_DEVCTL_SETBUSY 0x01
+/* 1: assertion of sdio interrupt is synchronous to the sdio clock */
+#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02
+/* 1: mask all interrupts to host except the chipActive (rev 8) */
+#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04
+/* 1: isolate internal sdio signals, put external pads in tri-state; requires
+ * sdio bus power cycle to clear (rev 9) */
+#define SBSDIO_DEVCTL_PADS_ISO 0x08
+/* Force SD->SB reset mapping (rev 11) */
+#define SBSDIO_DEVCTL_SB_RST_CTL 0x30
+/* Determined by CoreControl bit */
+#define SBSDIO_DEVCTL_RST_CORECTL 0x00
+/* Force backplane reset */
+#define SBSDIO_DEVCTL_RST_BPRESET 0x10
+/* Force no backplane reset */
+#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20
+
+/* direct(mapped) cis space */
+
+/* MAPPED common CIS address */
+#define SBSDIO_CIS_BASE_COMMON 0x1000
+/* maximum bytes in one CIS */
+#define SBSDIO_CIS_SIZE_LIMIT 0x200
+/* cis offset addr is < 17 bits */
+#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF
+
+/* manfid tuple length, include tuple, link bytes */
+#define SBSDIO_CIS_MANFID_TUPLE_LEN 6
+
+#define CORE_BUS_REG(base, field) \
+ (base + offsetof(struct sdpcmd_regs, field))
+
+/* SDIO function 1 register CHIPCLKCSR */
+/* Force ALP request to backplane */
+#define SBSDIO_FORCE_ALP 0x01
+/* Force HT request to backplane */
+#define SBSDIO_FORCE_HT 0x02
+/* Force ILP request to backplane */
+#define SBSDIO_FORCE_ILP 0x04
+/* Make ALP ready (power up xtal) */
+#define SBSDIO_ALP_AVAIL_REQ 0x08
+/* Make HT ready (power up PLL) */
+#define SBSDIO_HT_AVAIL_REQ 0x10
+/* Squelch clock requests from HW */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20
+/* Status: ALP is ready */
+#define SBSDIO_ALP_AVAIL 0x40
+/* Status: HT is ready */
+#define SBSDIO_HT_AVAIL 0x80
+#define SBSDIO_CSR_MASK 0x1F
+#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+#define SBSDIO_CLKAV(regval, alponly) \
+ (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
+
+/* intstatus */
+#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */
+#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */
+#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */
+#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */
+#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */
+#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */
+#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */
+#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */
+#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */
+#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */
+#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */
+#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */
+#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */
+#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */
+#define I_PC (1 << 10) /* descriptor error */
+#define I_PD (1 << 11) /* data error */
+#define I_DE (1 << 12) /* Descriptor protocol Error */
+#define I_RU (1 << 13) /* Receive descriptor Underflow */
+#define I_RO (1 << 14) /* Receive fifo Overflow */
+#define I_XU (1 << 15) /* Transmit fifo Underflow */
+#define I_RI (1 << 16) /* Receive Interrupt */
+#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */
+#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */
+#define I_XI (1 << 24) /* Transmit Interrupt */
+#define I_RF_TERM (1 << 25) /* Read Frame Terminate */
+#define I_WF_TERM (1 << 26) /* Write Frame Terminate */
+#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */
+#define I_SBINT (1 << 28) /* sbintstatus Interrupt */
+#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */
+#define I_SRESET (1 << 30) /* CCCR RES interrupt */
+#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */
+#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
+#define I_DMA (I_RI | I_XI | I_ERRORS)
+
+/* corecontrol */
+#define CC_CISRDY (1 << 0) /* CIS Ready */
+#define CC_BPRESEN (1 << 1) /* CCCR RES signal */
+#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */
+#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */
+#define CC_XMTDATAAVAIL_MODE (1 << 4)
+#define CC_XMTDATAAVAIL_CTRL (1 << 5)
+
+/* SDA_FRAMECTRL */
+#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */
+#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */
+#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */
+#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */
+
+/*
+ * Software allocation of To SB Mailbox resources
+ */
+
+/* tosbmailbox bits corresponding to intstatus bits */
+#define SMB_NAK (1 << 0) /* Frame NAK */
+#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
+#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
+#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */
+
+/* tosbmailboxdata */
+#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */
+
+/*
+ * Software allocation of To Host Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */
+#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */
+#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */
+#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */
+
+/* tohostmailboxdata */
+#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */
+#define HMB_DATA_DEVREADY 2 /* talk to host after enable */
+#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */
+#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */
+
+#define HMB_DATA_FCDATA_MASK 0xff000000
+#define HMB_DATA_FCDATA_SHIFT 24
+
+#define HMB_DATA_VERSION_MASK 0x00ff0000
+#define HMB_DATA_VERSION_SHIFT 16
+
+/*
+ * Software-defined protocol header
+ */
+
+/* Current protocol version */
+#define SDPCM_PROT_VERSION 4
+
+/*
+ * Shared structure between dongle and the host.
+ * The structure contains pointers to trap or assert information.
+ */
+#define SDPCM_SHARED_VERSION 0x0003
+#define SDPCM_SHARED_VERSION_MASK 0x00FF
+#define SDPCM_SHARED_ASSERT_BUILT 0x0100
+#define SDPCM_SHARED_ASSERT 0x0200
+#define SDPCM_SHARED_TRAP 0x0400
+
+/* Space for header read, limit for data packets */
+#define MAX_HDR_READ (1 << 6)
+#define MAX_RX_DATASZ 2048
+
+/* Bump up limit on waiting for HT to account for first startup;
+ * if the image is doing a CRC calculation before programming the PMU
+ * for HT availability, it could take a couple hundred ms more, so
+ * max out at a 1 second (1000000us).
+ */
+#undef PMU_MAX_TRANSITION_DLY
+#define PMU_MAX_TRANSITION_DLY 1000000
+
+/* Value for ChipClockCSR during initial setup */
+#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
+ SBSDIO_ALP_AVAIL_REQ)
+
+/* Flags for SDH calls */
+#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
+
+#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change
+ * when idle
+ */
+#define BRCMF_IDLE_INTERVAL 1
+
+#define KSO_WAIT_US 50
+#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
+
+/*
+ * Conversion of 802.1D priority to precedence level
+ */
+static uint prio2prec(u32 prio)
+{
+ return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
+ (prio^2) : prio;
+}
+
+#ifdef DEBUG
+/* Device console log buffer state */
+struct brcmf_console {
+ uint count; /* Poll interval msec counter */
+ uint log_addr; /* Log struct address (fixed) */
+ struct rte_log_le log_le; /* Log struct (host copy) */
+ uint bufsize; /* Size of log buffer */
+ u8 *buf; /* Log buffer (host copy) */
+ uint last; /* Last buffer read index */
+};
+
+struct brcmf_trap_info {
+ __le32 type;
+ __le32 epc;
+ __le32 cpsr;
+ __le32 spsr;
+ __le32 r0; /* a1 */
+ __le32 r1; /* a2 */
+ __le32 r2; /* a3 */
+ __le32 r3; /* a4 */
+ __le32 r4; /* v1 */
+ __le32 r5; /* v2 */
+ __le32 r6; /* v3 */
+ __le32 r7; /* v4 */
+ __le32 r8; /* v5 */
+ __le32 r9; /* sb/v6 */
+ __le32 r10; /* sl/v7 */
+ __le32 r11; /* fp/v8 */
+ __le32 r12; /* ip */
+ __le32 r13; /* sp */
+ __le32 r14; /* lr */
+ __le32 pc; /* r15 */
+};
+#endif /* DEBUG */
+
+struct sdpcm_shared {
+ u32 flags;
+ u32 trap_addr;
+ u32 assert_exp_addr;
+ u32 assert_file_addr;
+ u32 assert_line;
+ u32 console_addr; /* Address of struct rte_console */
+ u32 msgtrace_addr;
+ u8 tag[32];
+ u32 brpt_addr;
+};
+
+struct sdpcm_shared_le {
+ __le32 flags;
+ __le32 trap_addr;
+ __le32 assert_exp_addr;
+ __le32 assert_file_addr;
+ __le32 assert_line;
+ __le32 console_addr; /* Address of struct rte_console */
+ __le32 msgtrace_addr;
+ u8 tag[32];
+ __le32 brpt_addr;
+};
+
+/* dongle SDIO bus specific header info */
+struct brcmf_sdio_hdrinfo {
+ u8 seq_num;
+ u8 channel;
+ u16 len;
+ u16 len_left;
+ u16 len_nxtfrm;
+ u8 dat_offset;
+ bool lastfrm;
+ u16 tail_pad;
+};
+
+/*
+ * hold counter variables
+ */
+struct brcmf_sdio_count {
+ uint intrcount; /* Count of device interrupt callbacks */
+ uint lastintrs; /* Count as of last watchdog timer */
+ uint pollcnt; /* Count of active polls */
+ uint regfails; /* Count of R_REG failures */
+ uint tx_sderrs; /* Count of tx attempts with sd errors */
+ uint fcqueued; /* Tx packets that got queued */
+ uint rxrtx; /* Count of rtx requests (NAK to dongle) */
+ uint rx_toolong; /* Receive frames too long to receive */
+ uint rxc_errors; /* SDIO errors when reading control frames */
+ uint rx_hdrfail; /* SDIO errors on header reads */
+ uint rx_badhdr; /* Bad received headers (roosync?) */
+ uint rx_badseq; /* Mismatched rx sequence number */
+ uint fc_rcvd; /* Number of flow-control events received */
+ uint fc_xoff; /* Number which turned on flow-control */
+ uint fc_xon; /* Number which turned off flow-control */
+ uint rxglomfail; /* Failed deglom attempts */
+ uint rxglomframes; /* Number of glom frames (superframes) */
+ uint rxglompkts; /* Number of packets from glom frames */
+ uint f2rxhdrs; /* Number of header reads */
+ uint f2rxdata; /* Number of frame data reads */
+ uint f2txdata; /* Number of f2 frame writes */
+ uint f1regdata; /* Number of f1 register accesses */
+ uint tickcnt; /* Number of watchdog been schedule */
+ ulong tx_ctlerrs; /* Err of sending ctrl frames */
+ ulong tx_ctlpkts; /* Ctrl frames sent to dongle */
+ ulong rx_ctlerrs; /* Err of processing rx ctrl frames */
+ ulong rx_ctlpkts; /* Ctrl frames processed from dongle */
+ ulong rx_readahead_cnt; /* packets where header read-ahead was used */
+};
+
+/* misc chip info needed by some of the routines */
+/* Private data for SDIO bus interaction */
+struct brcmf_sdio {
+ struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
+ struct brcmf_chip *ci; /* Chip info struct */
+
+ u32 hostintmask; /* Copy of Host Interrupt Mask */
+ atomic_t intstatus; /* Intstatus bits (events) pending */
+ atomic_t fcstate; /* State of dongle flow-control */
+
+ uint blocksize; /* Block size of SDIO transfers */
+ uint roundup; /* Max roundup limit */
+
+ struct pktq txq; /* Queue length used for flow-control */
+ u8 flowcontrol; /* per prio flow control bitmask */
+ u8 tx_seq; /* Transmit sequence number (next) */
+ u8 tx_max; /* Maximum transmit sequence allowed */
+
+ u8 *hdrbuf; /* buffer for handling rx frame */
+ u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
+ u8 rx_seq; /* Receive sequence number (expected) */
+ struct brcmf_sdio_hdrinfo cur_read;
+ /* info of current read frame */
+ bool rxskip; /* Skip receive (awaiting NAK ACK) */
+ bool rxpending; /* Data frame pending in dongle */
+
+ uint rxbound; /* Rx frames to read before resched */
+ uint txbound; /* Tx frames to send before resched */
+ uint txminmax;
+
+ struct sk_buff *glomd; /* Packet containing glomming descriptor */
+ struct sk_buff_head glom; /* Packet list for glommed superframe */
+
+ u8 *rxbuf; /* Buffer for receiving control packets */
+ uint rxblen; /* Allocated length of rxbuf */
+ u8 *rxctl; /* Aligned pointer into rxbuf */
+ u8 *rxctl_orig; /* pointer for freeing rxctl */
+ uint rxlen; /* Length of valid data in buffer */
+ spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */
+
+ u8 sdpcm_ver; /* Bus protocol reported by dongle */
+
+ bool intr; /* Use interrupts */
+ bool poll; /* Use polling */
+ atomic_t ipend; /* Device interrupt is pending */
+ uint spurious; /* Count of spurious interrupts */
+ uint pollrate; /* Ticks between device polls */
+ uint polltick; /* Tick counter */
+
+#ifdef DEBUG
+ uint console_interval;
+ struct brcmf_console console; /* Console output polling support */
+ uint console_addr; /* Console address from shared struct */
+#endif /* DEBUG */
+
+ uint clkstate; /* State of sd and backplane clock(s) */
+ s32 idletime; /* Control for activity timeout */
+ s32 idlecount; /* Activity timeout counter */
+ s32 idleclock; /* How to set bus driver when idle */
+ bool rxflow_mode; /* Rx flow control mode */
+ bool rxflow; /* Is rx flow control on */
+ bool alp_only; /* Don't use HT clock (ALP only) */
+
+ u8 *ctrl_frame_buf;
+ u16 ctrl_frame_len;
+ bool ctrl_frame_stat;
+ int ctrl_frame_err;
+
+ spinlock_t txq_lock; /* protect bus->txq */
+ wait_queue_head_t ctrl_wait;
+ wait_queue_head_t dcmd_resp_wait;
+
+ struct timer_list timer;
+ struct completion watchdog_wait;
+ struct task_struct *watchdog_tsk;
+ bool wd_active;
+
+ struct workqueue_struct *brcmf_wq;
+ struct work_struct datawork;
+ bool dpc_triggered;
+ bool dpc_running;
+
+ bool txoff; /* Transmit flow-controlled */
+ struct brcmf_sdio_count sdcnt;
+ bool sr_enabled; /* SaveRestore enabled */
+ bool sleeping;
+
+ u8 tx_hdrlen; /* sdio bus header length for tx packet */
+ bool txglom; /* host tx glomming enable flag */
+ u16 head_align; /* buffer pointer alignment */
+ u16 sgentry_align; /* scatter-gather buffer alignment */
+};
+
+/* clkstate */
+#define CLK_NONE 0
+#define CLK_SDONLY 1
+#define CLK_PENDING 2
+#define CLK_AVAIL 3
+
+#ifdef DEBUG
+static int qcount[NUMPRIO];
+#endif /* DEBUG */
+
+#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */
+
+#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
+
+/* Retry count for register access failures */
+static const uint retry_limit = 2;
+
+/* Limit on rounding up frames */
+static const uint max_roundup = 512;
+
+#define ALIGNMENT 4
+
+enum brcmf_sdio_frmtype {
+ BRCMF_SDIO_FT_NORMAL,
+ BRCMF_SDIO_FT_SUPER,
+ BRCMF_SDIO_FT_SUB,
+};
+
+#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
+
+/* SDIO Pad drive strength to select value mappings */
+struct sdiod_drive_str {
+ u8 strength; /* Pad Drive Strength in mA */
+ u8 sel; /* Chip-specific select value */
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
+ {32, 0x6},
+ {26, 0x7},
+ {22, 0x4},
+ {16, 0x5},
+ {12, 0x2},
+ {8, 0x3},
+ {4, 0x0},
+ {0, 0x1}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
+static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
+ {6, 0x7},
+ {5, 0x6},
+ {4, 0x5},
+ {3, 0x4},
+ {2, 0x2},
+ {1, 0x1},
+ {0, 0x0}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
+static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
+ {3, 0x3},
+ {2, 0x2},
+ {1, 0x1},
+ {0, 0x0} };
+
+/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
+ {16, 0x7},
+ {12, 0x5},
+ {8, 0x3},
+ {4, 0x1}
+};
+
+BRCMF_FW_NVRAM_DEF(43143, "brcmfmac43143-sdio.bin", "brcmfmac43143-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B0, "brcmfmac43241b0-sdio.bin",
+ "brcmfmac43241b0-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B4, "brcmfmac43241b4-sdio.bin",
+ "brcmfmac43241b4-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B5, "brcmfmac43241b5-sdio.bin",
+ "brcmfmac43241b5-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4329, "brcmfmac4329-sdio.bin", "brcmfmac4329-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4330, "brcmfmac4330-sdio.bin", "brcmfmac4330-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4334, "brcmfmac4334-sdio.bin", "brcmfmac4334-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43340, "brcmfmac43340-sdio.bin", "brcmfmac43340-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4335, "brcmfmac4335-sdio.bin", "brcmfmac4335-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43362, "brcmfmac43362-sdio.bin", "brcmfmac43362-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4339, "brcmfmac4339-sdio.bin", "brcmfmac4339-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43430, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt");
+
+static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, 43241B5),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, 4329),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, 43430),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354)
+};
+
+static void pkt_align(struct sk_buff *p, int len, int align)
+{
+ uint datalign;
+ datalign = (unsigned long)(p->data);
+ datalign = roundup(datalign, (align)) - datalign;
+ if (datalign)
+ skb_pull(p, datalign);
+ __skb_trim(p, len);
+}
+
+/* To check if there's window offered */
+static bool data_ok(struct brcmf_sdio *bus)
+{
+ return (u8)(bus->tx_max - bus->tx_seq) != 0 &&
+ ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0;
+}
+
+/*
+ * Reads a register in the SDIO hardware block. This block occupies a series of
+ * adresses on the 32 bit backplane bus.
+ */
+static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
+{
+ struct brcmf_core *core;
+ int ret;
+
+ core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);
+
+ return ret;
+}
+
+static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
+{
+ struct brcmf_core *core;
+ int ret;
+
+ core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);
+
+ return ret;
+}
+
+static int
+brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
+{
+ u8 wr_val = 0, rd_val, cmp_val, bmask;
+ int err = 0;
+ int try_cnt = 0;
+
+ brcmf_dbg(TRACE, "Enter: on=%d\n", on);
+
+ wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ /* 1st KSO write goes to AOS wake up core if device is asleep */
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ wr_val, &err);
+
+ if (on) {
+ /* device WAKEUP through KSO:
+ * write bit 0 & read back until
+ * both bits 0 (kso bit) & 1 (dev on status) are set
+ */
+ cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
+ SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK;
+ bmask = cmp_val;
+ usleep_range(2000, 3000);
+ } else {
+ /* Put device to sleep, turn off KSO */
+ cmp_val = 0;
+ /* only check for bit0, bit1(dev on status) may not
+ * get cleared right away
+ */
+ bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
+ }
+
+ do {
+ /* reliable KSO bit set/clr:
+ * the sdiod sleep write access is synced to PMU 32khz clk
+ * just one write attempt may fail,
+ * read it back until it matches written value
+ */
+ rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ &err);
+ if (((rd_val & bmask) == cmp_val) && !err)
+ break;
+
+ udelay(KSO_WAIT_US);
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ wr_val, &err);
+ } while (try_cnt++ < MAX_KSO_ATTEMPTS);
+
+ if (try_cnt > 2)
+ brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt,
+ rd_val, err);
+
+ if (try_cnt > MAX_KSO_ATTEMPTS)
+ brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
+
+ return err;
+}
+
+#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
+
+/* Turn backplane clock on or off */
+static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
+{
+ int err;
+ u8 clkctl, clkreq, devctl;
+ unsigned long timeout;
+
+ brcmf_dbg(SDIO, "Enter\n");
+
+ clkctl = 0;
+
+ if (bus->sr_enabled) {
+ bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY);
+ return 0;
+ }
+
+ if (on) {
+ /* Request HT Avail */
+ clkreq =
+ bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
+
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ clkreq, &err);
+ if (err) {
+ brcmf_err("HT Avail request error: %d\n", err);
+ return -EBADE;
+ }
+
+ /* Check current status */
+ clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (err) {
+ brcmf_err("HT Avail read error: %d\n", err);
+ return -EBADE;
+ }
+
+ /* Go to pending and await interrupt if appropriate */
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
+ /* Allow only clock-available interrupt */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ brcmf_err("Devctl error setting CA: %d\n",
+ err);
+ return -EBADE;
+ }
+
+ devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
+ bus->clkstate = CLK_PENDING;
+
+ return 0;
+ } else if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ }
+
+ /* Otherwise, wait here (polling) for HT Avail */
+ timeout = jiffies +
+ msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
+ while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if (time_after(jiffies, timeout))
+ break;
+ else
+ usleep_range(5000, 10000);
+ }
+ if (err) {
+ brcmf_err("HT Avail request error: %d\n", err);
+ return -EBADE;
+ }
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n",
+ PMU_MAX_TRANSITION_DLY, clkctl);
+ return -EBADE;
+ }
+
+ /* Mark clock available */
+ bus->clkstate = CLK_AVAIL;
+ brcmf_dbg(SDIO, "CLKCTL: turned ON\n");
+
+#if defined(DEBUG)
+ if (!bus->alp_only) {
+ if (SBSDIO_ALPONLY(clkctl))
+ brcmf_err("HT Clock should be on\n");
+ }
+#endif /* defined (DEBUG) */
+
+ } else {
+ clkreq = 0;
+
+ if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ }
+
+ bus->clkstate = CLK_SDONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ clkreq, &err);
+ brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
+ if (err) {
+ brcmf_err("Failed access turning clock off: %d\n",
+ err);
+ return -EBADE;
+ }
+ }
+ return 0;
+}
+
+/* Change idle/active SD state */
+static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on)
+{
+ brcmf_dbg(SDIO, "Enter\n");
+
+ if (on)
+ bus->clkstate = CLK_SDONLY;
+ else
+ bus->clkstate = CLK_NONE;
+
+ return 0;
+}
+
+/* Transition SD and backplane clock readiness */
+static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
+{
+#ifdef DEBUG
+ uint oldstate = bus->clkstate;
+#endif /* DEBUG */
+
+ brcmf_dbg(SDIO, "Enter\n");
+
+ /* Early exit if we're already there */
+ if (bus->clkstate == target)
+ return 0;
+
+ switch (target) {
+ case CLK_AVAIL:
+ /* Make sure SD clock is available */
+ if (bus->clkstate == CLK_NONE)
+ brcmf_sdio_sdclk(bus, true);
+ /* Now request HT Avail on the backplane */
+ brcmf_sdio_htclk(bus, true, pendok);
+ break;
+
+ case CLK_SDONLY:
+ /* Remove HT request, or bring up SD clock */
+ if (bus->clkstate == CLK_NONE)
+ brcmf_sdio_sdclk(bus, true);
+ else if (bus->clkstate == CLK_AVAIL)
+ brcmf_sdio_htclk(bus, false, false);
+ else
+ brcmf_err("request for %d -> %d\n",
+ bus->clkstate, target);
+ break;
+
+ case CLK_NONE:
+ /* Make sure to remove HT request */
+ if (bus->clkstate == CLK_AVAIL)
+ brcmf_sdio_htclk(bus, false, false);
+ /* Now remove the SD clock */
+ brcmf_sdio_sdclk(bus, false);
+ break;
+ }
+#ifdef DEBUG
+ brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate);
+#endif /* DEBUG */
+
+ return 0;
+}
+
+static int
+brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
+{
+ int err = 0;
+ u8 clkcsr;
+
+ brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
+ (sleep ? "SLEEP" : "WAKE"),
+ (bus->sleeping ? "SLEEP" : "WAKE"));
+
+ /* If SR is enabled control bus state with KSO */
+ if (bus->sr_enabled) {
+ /* Done if we're already in the requested state */
+ if (sleep == bus->sleeping)
+ goto end;
+
+ /* Going to sleep */
+ if (sleep) {
+ clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if ((clkcsr & SBSDIO_CSR_MASK) == 0) {
+ brcmf_dbg(SDIO, "no clock, set ALP\n");
+ brcmf_sdiod_regwb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_ALP_AVAIL_REQ, &err);
+ }
+ err = brcmf_sdio_kso_control(bus, false);
+ } else {
+ err = brcmf_sdio_kso_control(bus, true);
+ }
+ if (err) {
+ brcmf_err("error while changing bus sleep state %d\n",
+ err);
+ goto done;
+ }
+ }
+
+end:
+ /* control clocks */
+ if (sleep) {
+ if (!bus->sr_enabled)
+ brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
+ } else {
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
+ brcmf_sdio_wd_timer(bus, true);
+ }
+ bus->sleeping = sleep;
+ brcmf_dbg(SDIO, "new state %s\n",
+ (sleep ? "SLEEP" : "WAKE"));
+done:
+ brcmf_dbg(SDIO, "Exit: err=%d\n", err);
+ return err;
+
+}
+
+#ifdef DEBUG
+static inline bool brcmf_sdio_valid_shared_address(u32 addr)
+{
+ return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
+}
+
+static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ u32 addr = 0;
+ int rv;
+ u32 shaddr = 0;
+ struct sdpcm_shared_le sh_le;
+ __le32 addr_le;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_bus_sleep(bus, false, false);
+
+ /*
+ * Read last word in socram to determine
+ * address of sdpcm_shared structure
+ */
+ shaddr = bus->ci->rambase + bus->ci->ramsize - 4;
+ if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci))
+ shaddr -= bus->ci->srsize;
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr,
+ (u8 *)&addr_le, 4);
+ if (rv < 0)
+ goto fail;
+
+ /*
+ * Check if addr is valid.
+ * NVRAM length at the end of memory should have been overwritten.
+ */
+ addr = le32_to_cpu(addr_le);
+ if (!brcmf_sdio_valid_shared_address(addr)) {
+ brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr);
+ rv = -EINVAL;
+ goto fail;
+ }
+
+ brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
+
+ /* Read hndrte_shared structure */
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
+ sizeof(struct sdpcm_shared_le));
+ if (rv < 0)
+ goto fail;
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ /* Endianness */
+ sh->flags = le32_to_cpu(sh_le.flags);
+ sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
+ sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
+ sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
+ sh->assert_line = le32_to_cpu(sh_le.assert_line);
+ sh->console_addr = le32_to_cpu(sh_le.console_addr);
+ sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
+
+ if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
+ brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
+ SDPCM_SHARED_VERSION,
+ sh->flags & SDPCM_SHARED_VERSION_MASK);
+ return -EPROTO;
+ }
+ return 0;
+
+fail:
+ brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n",
+ rv, addr);
+ sdio_release_host(bus->sdiodev->func[1]);
+ return rv;
+}
+
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+ struct sdpcm_shared sh;
+
+ if (brcmf_sdio_readshared(bus, &sh) == 0)
+ bus->console_addr = sh.console_addr;
+}
+#else
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+}
+#endif /* DEBUG */
+
+static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
+{
+ u32 intstatus = 0;
+ u32 hmb_data;
+ u8 fcbits;
+ int ret;
+
+ brcmf_dbg(SDIO, "Enter\n");
+
+ /* Read mailbox data and ack that we did so */
+ ret = r_sdreg32(bus, &hmb_data,
+ offsetof(struct sdpcmd_regs, tohostmailboxdata));
+
+ if (ret == 0)
+ w_sdreg32(bus, SMB_INT_ACK,
+ offsetof(struct sdpcmd_regs, tosbmailbox));
+ bus->sdcnt.f1regdata += 2;
+
+ /* Dongle recomposed rx frames, accept them again */
+ if (hmb_data & HMB_DATA_NAKHANDLED) {
+ brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n",
+ bus->rx_seq);
+ if (!bus->rxskip)
+ brcmf_err("unexpected NAKHANDLED!\n");
+
+ bus->rxskip = false;
+ intstatus |= I_HMB_FRAME_IND;
+ }
+
+ /*
+ * DEVREADY does not occur with gSPI.
+ */
+ if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
+ bus->sdpcm_ver =
+ (hmb_data & HMB_DATA_VERSION_MASK) >>
+ HMB_DATA_VERSION_SHIFT;
+ if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
+ brcmf_err("Version mismatch, dongle reports %d, "
+ "expecting %d\n",
+ bus->sdpcm_ver, SDPCM_PROT_VERSION);
+ else
+ brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
+ bus->sdpcm_ver);
+
+ /*
+ * Retrieve console state address now that firmware should have
+ * updated it.
+ */
+ brcmf_sdio_get_console_addr(bus);
+ }
+
+ /*
+ * Flow Control has been moved into the RX headers and this out of band
+ * method isn't used any more.
+ * remaining backward compatible with older dongles.
+ */
+ if (hmb_data & HMB_DATA_FC) {
+ fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
+ HMB_DATA_FCDATA_SHIFT;
+
+ if (fcbits & ~bus->flowcontrol)
+ bus->sdcnt.fc_xoff++;
+
+ if (bus->flowcontrol & ~fcbits)
+ bus->sdcnt.fc_xon++;
+
+ bus->sdcnt.fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Shouldn't be any others */
+ if (hmb_data & ~(HMB_DATA_DEVREADY |
+ HMB_DATA_NAKHANDLED |
+ HMB_DATA_FC |
+ HMB_DATA_FWREADY |
+ HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
+ brcmf_err("Unknown mailbox data content: 0x%02x\n",
+ hmb_data);
+
+ return intstatus;
+}
+
+static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
+{
+ uint retries = 0;
+ u16 lastrbc;
+ u8 hi, lo;
+ int err;
+
+ brcmf_err("%sterminate frame%s\n",
+ abort ? "abort command, " : "",
+ rtx ? ", send NAK" : "");
+
+ if (abort)
+ brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
+
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
+ SFC_RF_TERM, &err);
+ bus->sdcnt.f1regdata++;
+
+ /* Wait until the packet has been flushed (device/FIFO stable) */
+ for (lastrbc = retries = 0xffff; retries > 0; retries--) {
+ hi = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_RFRAMEBCHI, &err);
+ lo = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_RFRAMEBCLO, &err);
+ bus->sdcnt.f1regdata += 2;
+
+ if ((hi == 0) && (lo == 0))
+ break;
+
+ if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
+ brcmf_err("count growing: last 0x%04x now 0x%04x\n",
+ lastrbc, (hi << 8) + lo);
+ }
+ lastrbc = (hi << 8) + lo;
+ }
+
+ if (!retries)
+ brcmf_err("count never zeroed: last 0x%04x\n", lastrbc);
+ else
+ brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries);
+
+ if (rtx) {
+ bus->sdcnt.rxrtx++;
+ err = w_sdreg32(bus, SMB_NAK,
+ offsetof(struct sdpcmd_regs, tosbmailbox));
+
+ bus->sdcnt.f1regdata++;
+ if (err == 0)
+ bus->rxskip = true;
+ }
+
+ /* Clear partial in any case */
+ bus->cur_read.len = 0;
+}
+
+static void brcmf_sdio_txfail(struct brcmf_sdio *bus)
+{
+ struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
+ u8 i, hi, lo;
+
+ /* On failure, abort the command and terminate the frame */
+ brcmf_err("sdio error, abort command and terminate frame\n");
+ bus->sdcnt.tx_sderrs++;
+
+ brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
+ bus->sdcnt.f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+ lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+ bus->sdcnt.f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+}
+
+/* return total length of buffer chain */
+static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)
+{
+ struct sk_buff *p;
+ uint total;
+
+ total = 0;
+ skb_queue_walk(&bus->glom, p)
+ total += p->len;
+ return total;
+}
+
+static void brcmf_sdio_free_glom(struct brcmf_sdio *bus)
+{
+ struct sk_buff *cur, *next;
+
+ skb_queue_walk_safe(&bus->glom, cur, next) {
+ skb_unlink(cur, &bus->glom);
+ brcmu_pkt_buf_free_skb(cur);
+ }
+}
+
+/**
+ * brcmfmac sdio bus specific header
+ * This is the lowest layer header wrapped on the packets transmitted between
+ * host and WiFi dongle which contains information needed for SDIO core and
+ * firmware
+ *
+ * It consists of 3 parts: hardware header, hardware extension header and
+ * software header
+ * hardware header (frame tag) - 4 bytes
+ * Byte 0~1: Frame length
+ * Byte 2~3: Checksum, bit-wise inverse of frame length
+ * hardware extension header - 8 bytes
+ * Tx glom mode only, N/A for Rx or normal Tx
+ * Byte 0~1: Packet length excluding hw frame tag
+ * Byte 2: Reserved
+ * Byte 3: Frame flags, bit 0: last frame indication
+ * Byte 4~5: Reserved
+ * Byte 6~7: Tail padding length
+ * software header - 8 bytes
+ * Byte 0: Rx/Tx sequence number
+ * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
+ * Byte 2: Length of next data frame, reserved for Tx
+ * Byte 3: Data offset
+ * Byte 4: Flow control bits, reserved for Tx
+ * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
+ * Byte 6~7: Reserved
+ */
+#define SDPCM_HWHDR_LEN 4
+#define SDPCM_HWEXT_LEN 8
+#define SDPCM_SWHDR_LEN 8
+#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
+/* software header */
+#define SDPCM_SEQ_MASK 0x000000ff
+#define SDPCM_SEQ_WRAP 256
+#define SDPCM_CHANNEL_MASK 0x00000f00
+#define SDPCM_CHANNEL_SHIFT 8
+#define SDPCM_CONTROL_CHANNEL 0 /* Control */
+#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */
+#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */
+#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */
+#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */
+#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
+#define SDPCM_NEXTLEN_MASK 0x00ff0000
+#define SDPCM_NEXTLEN_SHIFT 16
+#define SDPCM_DOFFSET_MASK 0xff000000
+#define SDPCM_DOFFSET_SHIFT 24
+#define SDPCM_FCMASK_MASK 0x000000ff
+#define SDPCM_WINDOW_MASK 0x0000ff00
+#define SDPCM_WINDOW_SHIFT 8
+
+static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
+{
+ u32 hdrvalue;
+ hdrvalue = *(u32 *)swheader;
+ return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+}
+
+static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *rd,
+ enum brcmf_sdio_frmtype type)
+{
+ u16 len, checksum;
+ u8 rx_seq, fc, tx_seq_max;
+ u32 swheader;
+
+ trace_brcmf_sdpcm_hdr(SDPCM_RX, header);
+
+ /* hw header */
+ len = get_unaligned_le16(header);
+ checksum = get_unaligned_le16(header + sizeof(u16));
+ /* All zero means no more to read */
+ if (!(len | checksum)) {
+ bus->rxpending = false;
+ return -ENODATA;
+ }
+ if ((u16)(~(len ^ checksum))) {
+ brcmf_err("HW header checksum error\n");
+ bus->sdcnt.rx_badhdr++;
+ brcmf_sdio_rxfail(bus, false, false);
+ return -EIO;
+ }
+ if (len < SDPCM_HDRLEN) {
+ brcmf_err("HW header length error\n");
+ return -EPROTO;
+ }
+ if (type == BRCMF_SDIO_FT_SUPER &&
+ (roundup(len, bus->blocksize) != rd->len)) {
+ brcmf_err("HW superframe header length error\n");
+ return -EPROTO;
+ }
+ if (type == BRCMF_SDIO_FT_SUB && len > rd->len) {
+ brcmf_err("HW subframe header length error\n");
+ return -EPROTO;
+ }
+ rd->len = len;
+
+ /* software header */
+ header += SDPCM_HWHDR_LEN;
+ swheader = le32_to_cpu(*(__le32 *)header);
+ if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
+ brcmf_err("Glom descriptor found in superframe head\n");
+ rd->len = 0;
+ return -EINVAL;
+ }
+ rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
+ rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
+ if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
+ type != BRCMF_SDIO_FT_SUPER) {
+ brcmf_err("HW header length too long\n");
+ bus->sdcnt.rx_toolong++;
+ brcmf_sdio_rxfail(bus, false, false);
+ rd->len = 0;
+ return -EPROTO;
+ }
+ if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) {
+ brcmf_err("Wrong channel for superframe\n");
+ rd->len = 0;
+ return -EINVAL;
+ }
+ if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL &&
+ rd->channel != SDPCM_EVENT_CHANNEL) {
+ brcmf_err("Wrong channel for subframe\n");
+ rd->len = 0;
+ return -EINVAL;
+ }
+ rd->dat_offset = brcmf_sdio_getdatoffset(header);
+ if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
+ brcmf_err("seq %d: bad data offset\n", rx_seq);
+ bus->sdcnt.rx_badhdr++;
+ brcmf_sdio_rxfail(bus, false, false);
+ rd->len = 0;
+ return -ENXIO;
+ }
+ if (rd->seq_num != rx_seq) {
+ brcmf_err("seq %d: sequence number error, expect %d\n",
+ rx_seq, rd->seq_num);
+ bus->sdcnt.rx_badseq++;
+ rd->seq_num = rx_seq;
+ }
+ /* no need to check the reset for subframe */
+ if (type == BRCMF_SDIO_FT_SUB)
+ return 0;
+ rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
+ if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
+ /* only warm for NON glom packet */
+ if (rd->channel != SDPCM_GLOM_CHANNEL)
+ brcmf_err("seq %d: next length error\n", rx_seq);
+ rd->len_nxtfrm = 0;
+ }
+ swheader = le32_to_cpu(*(__le32 *)(header + 4));
+ fc = swheader & SDPCM_FCMASK_MASK;
+ if (bus->flowcontrol != fc) {
+ if (~bus->flowcontrol & fc)
+ bus->sdcnt.fc_xoff++;
+ if (bus->flowcontrol & ~fc)
+ bus->sdcnt.fc_xon++;
+ bus->sdcnt.fc_rcvd++;
+ bus->flowcontrol = fc;
+ }
+ tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
+ if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
+ brcmf_err("seq %d: max tx seq number error\n", rx_seq);
+ tx_seq_max = bus->tx_seq + 2;
+ }
+ bus->tx_max = tx_seq_max;
+
+ return 0;
+}
+
+static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
+{
+ *(__le16 *)header = cpu_to_le16(frm_length);
+ *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
+}
+
+static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *hd_info)
+{
+ u32 hdrval;
+ u8 hdr_offset;
+
+ brcmf_sdio_update_hwhdr(header, hd_info->len);
+ hdr_offset = SDPCM_HWHDR_LEN;
+
+ if (bus->txglom) {
+ hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24);
+ *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
+ hdrval = (u16)hd_info->tail_pad << 16;
+ *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval);
+ hdr_offset += SDPCM_HWEXT_LEN;
+ }
+
+ hdrval = hd_info->seq_num;
+ hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) &
+ SDPCM_CHANNEL_MASK;
+ hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) &
+ SDPCM_DOFFSET_MASK;
+ *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
+ *(((__le32 *)(header + hdr_offset)) + 1) = 0;
+ trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header);
+}
+
+static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
+{
+ u16 dlen, totlen;
+ u8 *dptr, num = 0;
+ u16 sublen;
+ struct sk_buff *pfirst, *pnext;
+
+ int errcode;
+ u8 doff, sfdoff;
+
+ struct brcmf_sdio_hdrinfo rd_new;
+
+ /* If packets, issue read(s) and send up packet chain */
+ /* Return sequence numbers consumed? */
+
+ brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
+ bus->glomd, skb_peek(&bus->glom));
+
+ /* If there's a descriptor, generate the packet chain */
+ if (bus->glomd) {
+ pfirst = pnext = NULL;
+ dlen = (u16) (bus->glomd->len);
+ dptr = bus->glomd->data;
+ if (!dlen || (dlen & 1)) {
+ brcmf_err("bad glomd len(%d), ignore descriptor\n",
+ dlen);
+ dlen = 0;
+ }
+
+ for (totlen = num = 0; dlen; num++) {
+ /* Get (and move past) next length */
+ sublen = get_unaligned_le16(dptr);
+ dlen -= sizeof(u16);
+ dptr += sizeof(u16);
+ if ((sublen < SDPCM_HDRLEN) ||
+ ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
+ brcmf_err("descriptor len %d bad: %d\n",
+ num, sublen);
+ pnext = NULL;
+ break;
+ }
+ if (sublen % bus->sgentry_align) {
+ brcmf_err("sublen %d not multiple of %d\n",
+ sublen, bus->sgentry_align);
+ }
+ totlen += sublen;
+
+ /* For last frame, adjust read len so total
+ is a block multiple */
+ if (!dlen) {
+ sublen +=
+ (roundup(totlen, bus->blocksize) - totlen);
+ totlen = roundup(totlen, bus->blocksize);
+ }
+
+ /* Allocate/chain packet for next subframe */
+ pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align);
+ if (pnext == NULL) {
+ brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
+ num, sublen);
+ break;
+ }
+ skb_queue_tail(&bus->glom, pnext);
+
+ /* Adhere to start alignment requirements */
+ pkt_align(pnext, sublen, bus->sgentry_align);
+ }
+
+ /* If all allocations succeeded, save packet chain
+ in bus structure */
+ if (pnext) {
+ brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
+ totlen, num);
+ if (BRCMF_GLOM_ON() && bus->cur_read.len &&
+ totlen != bus->cur_read.len) {
+ brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
+ bus->cur_read.len, totlen, rxseq);
+ }
+ pfirst = pnext = NULL;
+ } else {
+ brcmf_sdio_free_glom(bus);
+ num = 0;
+ }
+
+ /* Done with descriptor packet */
+ brcmu_pkt_buf_free_skb(bus->glomd);
+ bus->glomd = NULL;
+ bus->cur_read.len = 0;
+ }
+
+ /* Ok -- either we just generated a packet chain,
+ or had one from before */
+ if (!skb_queue_empty(&bus->glom)) {
+ if (BRCMF_GLOM_ON()) {
+ brcmf_dbg(GLOM, "try superframe read, packet chain:\n");
+ skb_queue_walk(&bus->glom, pnext) {
+ brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n",
+ pnext, (u8 *) (pnext->data),
+ pnext->len, pnext->len);
+ }
+ }
+
+ pfirst = skb_peek(&bus->glom);
+ dlen = (u16) brcmf_sdio_glom_len(bus);
+
+ /* Do an SDIO read for the superframe. Configurable iovar to
+ * read directly into the chained packet, or allocate a large
+ * packet and and copy into the chain.
+ */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ errcode = brcmf_sdiod_recv_chain(bus->sdiodev,
+ &bus->glom, dlen);
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->sdcnt.f2rxdata++;
+
+ /* On failure, kill the superframe */
+ if (errcode < 0) {
+ brcmf_err("glom read of %d bytes failed: %d\n",
+ dlen, errcode);
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, true, false);
+ bus->sdcnt.rxglomfail++;
+ brcmf_sdio_free_glom(bus);
+ sdio_release_host(bus->sdiodev->func[1]);
+ return 0;
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pfirst->data, min_t(int, pfirst->len, 48),
+ "SUPERFRAME:\n");
+
+ rd_new.seq_num = rxseq;
+ rd_new.len = dlen;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
+ BRCMF_SDIO_FT_SUPER);
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->cur_read.len = rd_new.len_nxtfrm << 4;
+
+ /* Remove superframe header, remember offset */
+ skb_pull(pfirst, rd_new.dat_offset);
+ sfdoff = rd_new.dat_offset;
+ num = 0;
+
+ /* Validate all the subframe headers */
+ skb_queue_walk(&bus->glom, pnext) {
+ /* leave when invalid subframe is found */
+ if (errcode)
+ break;
+
+ rd_new.len = pnext->len;
+ rd_new.seq_num = rxseq++;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
+ BRCMF_SDIO_FT_SUB);
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pnext->data, 32, "subframe:\n");
+
+ num++;
+ }
+
+ if (errcode) {
+ /* Terminate frame on error */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, true, false);
+ bus->sdcnt.rxglomfail++;
+ brcmf_sdio_free_glom(bus);
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->cur_read.len = 0;
+ return 0;
+ }
+
+ /* Basic SD framing looks ok - process each packet (header) */
+
+ skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
+ dptr = (u8 *) (pfirst->data);
+ sublen = get_unaligned_le16(dptr);
+ doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
+ dptr, pfirst->len,
+ "Rx Subframe Data:\n");
+
+ __skb_trim(pfirst, sublen);
+ skb_pull(pfirst, doff);
+
+ if (pfirst->len == 0) {
+ skb_unlink(pfirst, &bus->glom);
+ brcmu_pkt_buf_free_skb(pfirst);
+ continue;
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pfirst->data,
+ min_t(int, pfirst->len, 32),
+ "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n",
+ bus->glom.qlen, pfirst, pfirst->data,
+ pfirst->len, pfirst->next,
+ pfirst->prev);
+ skb_unlink(pfirst, &bus->glom);
+ brcmf_rx_frame(bus->sdiodev->dev, pfirst);
+ bus->sdcnt.rxglompkts++;
+ }
+
+ bus->sdcnt.rxglomframes++;
+ }
+ return num;
+}
+
+static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
+ bool *pending)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = DCMD_RESP_TIMEOUT;
+
+ /* Wait until control frame is available */
+ add_wait_queue(&bus->dcmd_resp_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (!(*condition) && (!signal_pending(current) && timeout))
+ timeout = schedule_timeout(timeout);
+
+ if (signal_pending(current))
+ *pending = true;
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&bus->dcmd_resp_wait, &wait);
+
+ return timeout;
+}
+
+static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)
+{
+ wake_up_interruptible(&bus->dcmd_resp_wait);
+
+ return 0;
+}
+static void
+brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
+{
+ uint rdlen, pad;
+ u8 *buf = NULL, *rbuf;
+ int sdret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->rxblen)
+ buf = vzalloc(bus->rxblen);
+ if (!buf)
+ goto done;
+
+ rbuf = bus->rxbuf;
+ pad = ((unsigned long)rbuf % bus->head_align);
+ if (pad)
+ rbuf += (bus->head_align - pad);
+
+ /* Copy the already-read portion over */
+ memcpy(buf, hdr, BRCMF_FIRSTREAD);
+ if (len <= BRCMF_FIRSTREAD)
+ goto gotpkt;
+
+ /* Raise rdlen to next SDIO block to avoid tail command */
+ rdlen = len - BRCMF_FIRSTREAD;
+ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+ pad = bus->blocksize - (rdlen % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+ ((len + pad) < bus->sdiodev->bus_if->maxctl))
+ rdlen += pad;
+ } else if (rdlen % bus->head_align) {
+ rdlen += bus->head_align - (rdlen % bus->head_align);
+ }
+
+ /* Drop if the read is too big or it exceeds our maximum */
+ if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {
+ brcmf_err("%d-byte control read exceeds %d-byte buffer\n",
+ rdlen, bus->sdiodev->bus_if->maxctl);
+ brcmf_sdio_rxfail(bus, false, false);
+ goto done;
+ }
+
+ if ((len - doff) > bus->sdiodev->bus_if->maxctl) {
+ brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
+ len, len - doff, bus->sdiodev->bus_if->maxctl);
+ bus->sdcnt.rx_toolong++;
+ brcmf_sdio_rxfail(bus, false, false);
+ goto done;
+ }
+
+ /* Read remain of frame body */
+ sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen);
+ bus->sdcnt.f2rxdata++;
+
+ /* Control frame failures need retransmission */
+ if (sdret < 0) {
+ brcmf_err("read %d control bytes failed: %d\n",
+ rdlen, sdret);
+ bus->sdcnt.rxc_errors++;
+ brcmf_sdio_rxfail(bus, true, true);
+ goto done;
+ } else
+ memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen);
+
+gotpkt:
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
+ buf, len, "RxCtrl:\n");
+
+ /* Point to valid data and indicate its length */
+ spin_lock_bh(&bus->rxctl_lock);
+ if (bus->rxctl) {
+ brcmf_err("last control frame is being processed.\n");
+ spin_unlock_bh(&bus->rxctl_lock);
+ vfree(buf);
+ goto done;
+ }
+ bus->rxctl = buf + doff;
+ bus->rxctl_orig = buf;
+ bus->rxlen = len - doff;
+ spin_unlock_bh(&bus->rxctl_lock);
+
+done:
+ /* Awake any waiters */
+ brcmf_sdio_dcmd_resp_wake(bus);
+}
+
+/* Pad read to blocksize for efficiency */
+static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
+{
+ if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
+ *pad = bus->blocksize - (*rdlen % bus->blocksize);
+ if (*pad <= bus->roundup && *pad < bus->blocksize &&
+ *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)
+ *rdlen += *pad;
+ } else if (*rdlen % bus->head_align) {
+ *rdlen += bus->head_align - (*rdlen % bus->head_align);
+ }
+}
+
+static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
+{
+ struct sk_buff *pkt; /* Packet for event or data frames */
+ u16 pad; /* Number of pad bytes to read */
+ uint rxleft = 0; /* Remaining number of frames allowed */
+ int ret; /* Return code from calls */
+ uint rxcount = 0; /* Total frames read */
+ struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
+ u8 head_read = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Not finished unless we encounter no more frames indication */
+ bus->rxpending = true;
+
+ for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
+ !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA;
+ rd->seq_num++, rxleft--) {
+
+ /* Handle glomming separately */
+ if (bus->glomd || !skb_queue_empty(&bus->glom)) {
+ u8 cnt;
+ brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
+ bus->glomd, skb_peek(&bus->glom));
+ cnt = brcmf_sdio_rxglom(bus, rd->seq_num);
+ brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
+ rd->seq_num += cnt - 1;
+ rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
+ continue;
+ }
+
+ rd->len_left = rd->len;
+ /* read header first for unknow frame length */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (!rd->len) {
+ ret = brcmf_sdiod_recv_buf(bus->sdiodev,
+ bus->rxhdr, BRCMF_FIRSTREAD);
+ bus->sdcnt.f2rxhdrs++;
+ if (ret < 0) {
+ brcmf_err("RXHEADER FAILED: %d\n",
+ ret);
+ bus->sdcnt.rx_hdrfail++;
+ brcmf_sdio_rxfail(bus, true, true);
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
+ bus->rxhdr, SDPCM_HDRLEN,
+ "RxHdr:\n");
+
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
+ BRCMF_SDIO_FT_NORMAL)) {
+ sdio_release_host(bus->sdiodev->func[1]);
+ if (!bus->rxpending)
+ break;
+ else
+ continue;
+ }
+
+ if (rd->channel == SDPCM_CONTROL_CHANNEL) {
+ brcmf_sdio_read_control(bus, bus->rxhdr,
+ rd->len,
+ rd->dat_offset);
+ /* prepare the descriptor for the next read */
+ rd->len = rd->len_nxtfrm << 4;
+ rd->len_nxtfrm = 0;
+ /* treat all packet as event if we don't know */
+ rd->channel = SDPCM_EVENT_CHANNEL;
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+ rd->len_left = rd->len > BRCMF_FIRSTREAD ?
+ rd->len - BRCMF_FIRSTREAD : 0;
+ head_read = BRCMF_FIRSTREAD;
+ }
+
+ brcmf_sdio_pad(bus, &pad, &rd->len_left);
+
+ pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read +
+ bus->head_align);
+ if (!pkt) {
+ /* Give up on data, request rtx of events */
+ brcmf_err("brcmu_pkt_buf_get_skb failed\n");
+ brcmf_sdio_rxfail(bus, false,
+ RETRYCHAN(rd->channel));
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+ skb_pull(pkt, head_read);
+ pkt_align(pkt, rd->len_left, bus->head_align);
+
+ ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt);
+ bus->sdcnt.f2rxdata++;
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ if (ret < 0) {
+ brcmf_err("read %d bytes from channel %d failed: %d\n",
+ rd->len, rd->channel, ret);
+ brcmu_pkt_buf_free_skb(pkt);
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, true,
+ RETRYCHAN(rd->channel));
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+
+ if (head_read) {
+ skb_push(pkt, head_read);
+ memcpy(pkt->data, bus->rxhdr, head_read);
+ head_read = 0;
+ } else {
+ memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
+ rd_new.seq_num = rd->seq_num;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
+ BRCMF_SDIO_FT_NORMAL)) {
+ rd->len = 0;
+ brcmu_pkt_buf_free_skb(pkt);
+ }
+ bus->sdcnt.rx_readahead_cnt++;
+ if (rd->len != roundup(rd_new.len, 16)) {
+ brcmf_err("frame length mismatch:read %d, should be %d\n",
+ rd->len,
+ roundup(rd_new.len, 16) >> 4);
+ rd->len = 0;
+ brcmf_sdio_rxfail(bus, true, true);
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmu_pkt_buf_free_skb(pkt);
+ continue;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ rd->len_nxtfrm = rd_new.len_nxtfrm;
+ rd->channel = rd_new.channel;
+ rd->dat_offset = rd_new.dat_offset;
+
+ brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
+ BRCMF_DATA_ON()) &&
+ BRCMF_HDRS_ON(),
+ bus->rxhdr, SDPCM_HDRLEN,
+ "RxHdr:\n");
+
+ if (rd_new.channel == SDPCM_CONTROL_CHANNEL) {
+ brcmf_err("readahead on control packet %d?\n",
+ rd_new.seq_num);
+ /* Force retry w/normal header read */
+ rd->len = 0;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, false, true);
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmu_pkt_buf_free_skb(pkt);
+ continue;
+ }
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
+ pkt->data, rd->len, "Rx Data:\n");
+
+ /* Save superframe descriptor and allocate packet frame */
+ if (rd->channel == SDPCM_GLOM_CHANNEL) {
+ if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
+ brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
+ rd->len);
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pkt->data, rd->len,
+ "Glom Data:\n");
+ __skb_trim(pkt, rd->len);
+ skb_pull(pkt, SDPCM_HDRLEN);
+ bus->glomd = pkt;
+ } else {
+ brcmf_err("%s: glom superframe w/o "
+ "descriptor!\n", __func__);
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, false, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ /* prepare the descriptor for the next read */
+ rd->len = rd->len_nxtfrm << 4;
+ rd->len_nxtfrm = 0;
+ /* treat all packet as event if we don't know */
+ rd->channel = SDPCM_EVENT_CHANNEL;
+ continue;
+ }
+
+ /* Fill in packet len and prio, deliver upward */
+ __skb_trim(pkt, rd->len);
+ skb_pull(pkt, rd->dat_offset);
+
+ /* prepare the descriptor for the next read */
+ rd->len = rd->len_nxtfrm << 4;
+ rd->len_nxtfrm = 0;
+ /* treat all packet as event if we don't know */
+ rd->channel = SDPCM_EVENT_CHANNEL;
+
+ if (pkt->len == 0) {
+ brcmu_pkt_buf_free_skb(pkt);
+ continue;
+ }
+
+ brcmf_rx_frame(bus->sdiodev->dev, pkt);
+ }
+
+ rxcount = maxframes - rxleft;
+ /* Message if we hit the limit */
+ if (!rxleft)
+ brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes);
+ else
+ brcmf_dbg(DATA, "processed %d frames\n", rxcount);
+ /* Back off rxseq if awaiting rtx, update rx_seq */
+ if (bus->rxskip)
+ rd->seq_num--;
+ bus->rx_seq = rd->seq_num;
+
+ return rxcount;
+}
+
+static void
+brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
+{
+ wake_up_interruptible(&bus->ctrl_wait);
+ return;
+}
+
+static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt)
+{
+ u16 head_pad;
+ u8 *dat_buf;
+
+ dat_buf = (u8 *)(pkt->data);
+
+ /* Check head padding */
+ head_pad = ((unsigned long)dat_buf % bus->head_align);
+ if (head_pad) {
+ if (skb_headroom(pkt) < head_pad) {
+ bus->sdiodev->bus_if->tx_realloc++;
+ head_pad = 0;
+ if (skb_cow(pkt, head_pad))
+ return -ENOMEM;
+ }
+ skb_push(pkt, head_pad);
+ dat_buf = (u8 *)(pkt->data);
+ memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
+ }
+ return head_pad;
+}
+
+/**
+ * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for
+ * bus layer usage.
+ */
+/* flag marking a dummy skb added for DMA alignment requirement */
+#define ALIGN_SKB_FLAG 0x8000
+/* bit mask of data length chopped from the previous packet */
+#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff
+
+static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus,
+ struct sk_buff_head *pktq,
+ struct sk_buff *pkt, u16 total_len)
+{
+ struct brcmf_sdio_dev *sdiodev;
+ struct sk_buff *pkt_pad;
+ u16 tail_pad, tail_chop, chain_pad;
+ unsigned int blksize;
+ bool lastfrm;
+ int ntail, ret;
+
+ sdiodev = bus->sdiodev;
+ blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+ /* sg entry alignment should be a divisor of block size */
+ WARN_ON(blksize % bus->sgentry_align);
+
+ /* Check tail padding */
+ lastfrm = skb_queue_is_last(pktq, pkt);
+ tail_pad = 0;
+ tail_chop = pkt->len % bus->sgentry_align;
+ if (tail_chop)
+ tail_pad = bus->sgentry_align - tail_chop;
+ chain_pad = (total_len + tail_pad) % blksize;
+ if (lastfrm && chain_pad)
+ tail_pad += blksize - chain_pad;
+ if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) {
+ pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop +
+ bus->head_align);
+ if (pkt_pad == NULL)
+ return -ENOMEM;
+ ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad);
+ if (unlikely(ret < 0)) {
+ kfree_skb(pkt_pad);
+ return ret;
+ }
+ memcpy(pkt_pad->data,
+ pkt->data + pkt->len - tail_chop,
+ tail_chop);
+ *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
+ skb_trim(pkt, pkt->len - tail_chop);
+ skb_trim(pkt_pad, tail_pad + tail_chop);
+ __skb_queue_after(pktq, pkt, pkt_pad);
+ } else {
+ ntail = pkt->data_len + tail_pad -
+ (pkt->end - pkt->tail);
+ if (skb_cloned(pkt) || ntail > 0)
+ if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC))
+ return -ENOMEM;
+ if (skb_linearize(pkt))
+ return -ENOMEM;
+ __skb_put(pkt, tail_pad);
+ }
+
+ return tail_pad;
+}
+
+/**
+ * brcmf_sdio_txpkt_prep - packet preparation for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ * @chan: virtual channel to transmit the packet
+ *
+ * Processes to be applied to the packet
+ * - Align data buffer pointer
+ * - Align data buffer length
+ * - Prepare header
+ * Return: negative value if there is error
+ */
+static int
+brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
+ uint chan)
+{
+ u16 head_pad, total_len;
+ struct sk_buff *pkt_next;
+ u8 txseq;
+ int ret;
+ struct brcmf_sdio_hdrinfo hd_info = {0};
+
+ txseq = bus->tx_seq;
+ total_len = 0;
+ skb_queue_walk(pktq, pkt_next) {
+ /* alignment packet inserted in previous
+ * loop cycle can be skipped as it is
+ * already properly aligned and does not
+ * need an sdpcm header.
+ */
+ if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG)
+ continue;
+
+ /* align packet data pointer */
+ ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next);
+ if (ret < 0)
+ return ret;
+ head_pad = (u16)ret;
+ if (head_pad)
+ memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad);
+
+ total_len += pkt_next->len;
+
+ hd_info.len = pkt_next->len;
+ hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next);
+ if (bus->txglom && pktq->qlen > 1) {
+ ret = brcmf_sdio_txpkt_prep_sg(bus, pktq,
+ pkt_next, total_len);
+ if (ret < 0)
+ return ret;
+ hd_info.tail_pad = (u16)ret;
+ total_len += (u16)ret;
+ }
+
+ hd_info.channel = chan;
+ hd_info.dat_offset = head_pad + bus->tx_hdrlen;
+ hd_info.seq_num = txseq++;
+
+ /* Now fill the header */
+ brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info);
+
+ if (BRCMF_BYTES_ON() &&
+ ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
+ (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
+ brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len,
+ "Tx Frame:\n");
+ else if (BRCMF_HDRS_ON())
+ brcmf_dbg_hex_dump(true, pkt_next->data,
+ head_pad + bus->tx_hdrlen,
+ "Tx Header:\n");
+ }
+ /* Hardware length tag of the first packet should be total
+ * length of the chain (including padding)
+ */
+ if (bus->txglom)
+ brcmf_sdio_update_hwhdr(pktq->next->data, total_len);
+ return 0;
+}
+
+/**
+ * brcmf_sdio_txpkt_postp - packet post processing for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ *
+ * Processes to be applied to the packet
+ * - Remove head padding
+ * - Remove tail padding
+ */
+static void
+brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
+{
+ u8 *hdr;
+ u32 dat_offset;
+ u16 tail_pad;
+ u16 dummy_flags, chop_len;
+ struct sk_buff *pkt_next, *tmp, *pkt_prev;
+
+ skb_queue_walk_safe(pktq, pkt_next, tmp) {
+ dummy_flags = *(u16 *)(pkt_next->cb);
+ if (dummy_flags & ALIGN_SKB_FLAG) {
+ chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK;
+ if (chop_len) {
+ pkt_prev = pkt_next->prev;
+ skb_put(pkt_prev, chop_len);
+ }
+ __skb_unlink(pkt_next, pktq);
+ brcmu_pkt_buf_free_skb(pkt_next);
+ } else {
+ hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN;
+ dat_offset = le32_to_cpu(*(__le32 *)hdr);
+ dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
+ SDPCM_DOFFSET_SHIFT;
+ skb_pull(pkt_next, dat_offset);
+ if (bus->txglom) {
+ tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2));
+ skb_trim(pkt_next, pkt_next->len - tail_pad);
+ }
+ }
+ }
+}
+
+/* Writes a HW/SW header into the packet and sends it. */
+/* Assumes: (a) header space already there, (b) caller holds lock */
+static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
+ uint chan)
+{
+ int ret;
+ struct sk_buff *pkt_next, *tmp;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ ret = brcmf_sdio_txpkt_prep(bus, pktq, chan);
+ if (ret)
+ goto done;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq);
+ bus->sdcnt.f2txdata++;
+
+ if (ret < 0)
+ brcmf_sdio_txfail(bus);
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+done:
+ brcmf_sdio_txpkt_postp(bus, pktq);
+ if (ret == 0)
+ bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP;
+ skb_queue_walk_safe(pktq, pkt_next, tmp) {
+ __skb_unlink(pkt_next, pktq);
+ brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0);
+ }
+ return ret;
+}
+
+static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
+{
+ struct sk_buff *pkt;
+ struct sk_buff_head pktq;
+ u32 intstatus = 0;
+ int ret = 0, prec_out, i;
+ uint cnt = 0;
+ u8 tx_prec_map, pkt_num;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ tx_prec_map = ~bus->flowcontrol;
+
+ /* Send frames until the limit or some other event */
+ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
+ pkt_num = 1;
+ if (bus->txglom)
+ pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
+ bus->sdiodev->txglomsz);
+ pkt_num = min_t(u32, pkt_num,
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol));
+ __skb_queue_head_init(&pktq);
+ spin_lock_bh(&bus->txq_lock);
+ for (i = 0; i < pkt_num; i++) {
+ pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map,
+ &prec_out);
+ if (pkt == NULL)
+ break;
+ __skb_queue_tail(&pktq, pkt);
+ }
+ spin_unlock_bh(&bus->txq_lock);
+ if (i == 0)
+ break;
+
+ ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
+
+ cnt += i;
+
+ /* In poll mode, need to check for other events */
+ if (!bus->intr) {
+ /* Check device status, signal pending interrupt */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ ret = r_sdreg32(bus, &intstatus,
+ offsetof(struct sdpcmd_regs,
+ intstatus));
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->sdcnt.f2txdata++;
+ if (ret != 0)
+ break;
+ if (intstatus & bus->hostintmask)
+ atomic_set(&bus->ipend, 1);
+ }
+ }
+
+ /* Deflow-control stack if needed */
+ if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) &&
+ bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
+ bus->txoff = false;
+ brcmf_txflowblock(bus->sdiodev->dev, false);
+ }
+
+ return cnt;
+}
+
+static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len)
+{
+ u8 doff;
+ u16 pad;
+ uint retries = 0;
+ struct brcmf_sdio_hdrinfo hd_info = {0};
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Back the pointer to make room for bus header */
+ frame -= bus->tx_hdrlen;
+ len += bus->tx_hdrlen;
+
+ /* Add alignment padding (optional for ctl frames) */
+ doff = ((unsigned long)frame % bus->head_align);
+ if (doff) {
+ frame -= doff;
+ len += doff;
+ memset(frame + bus->tx_hdrlen, 0, doff);
+ }
+
+ /* Round send length to next SDIO block */
+ pad = 0;
+ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+ pad = bus->blocksize - (len % bus->blocksize);
+ if ((pad > bus->roundup) || (pad >= bus->blocksize))
+ pad = 0;
+ } else if (len % bus->head_align) {
+ pad = bus->head_align - (len % bus->head_align);
+ }
+ len += pad;
+
+ hd_info.len = len - pad;
+ hd_info.channel = SDPCM_CONTROL_CHANNEL;
+ hd_info.dat_offset = doff + bus->tx_hdrlen;
+ hd_info.seq_num = bus->tx_seq;
+ hd_info.lastfrm = true;
+ hd_info.tail_pad = pad;
+ brcmf_sdio_hdpack(bus, frame, &hd_info);
+
+ if (bus->txglom)
+ brcmf_sdio_update_hwhdr(frame, len);
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
+ frame, len, "Tx Frame:\n");
+ brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) &&
+ BRCMF_HDRS_ON(),
+ frame, min_t(u16, len, 16), "TxHdr:\n");
+
+ do {
+ ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len);
+
+ if (ret < 0)
+ brcmf_sdio_txfail(bus);
+ else
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
+ } while (ret < 0 && retries++ < TXRETRIES);
+
+ return ret;
+}
+
+static void brcmf_sdio_bus_stop(struct device *dev)
+{
+ u32 local_hostintmask;
+ u8 saveclk;
+ int err;
+ 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;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->watchdog_tsk) {
+ send_sig(SIGTERM, bus->watchdog_tsk, 1);
+ kthread_stop(bus->watchdog_tsk);
+ bus->watchdog_tsk = NULL;
+ }
+
+ if (sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
+ sdio_claim_host(sdiodev->func[1]);
+
+ /* Enable clock for device interrupts */
+ brcmf_sdio_bus_sleep(bus, false, false);
+
+ /* Disable and clear interrupts at the chip level also */
+ w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask));
+ local_hostintmask = bus->hostintmask;
+ bus->hostintmask = 0;
+
+ /* Force backplane clocks to assure F2 interrupt propagates */
+ saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if (!err)
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ if (err)
+ brcmf_err("Failed to force clock for F2: err %d\n",
+ err);
+
+ /* Turn off the bus (F2), free any pending packets */
+ brcmf_dbg(INTR, "disable SDIO interrupts\n");
+ sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
+
+ /* Clear any pending interrupts now that F2 is disabled */
+ w_sdreg32(bus, local_hostintmask,
+ offsetof(struct sdpcmd_regs, intstatus));
+
+ sdio_release_host(sdiodev->func[1]);
+ }
+ /* Clear the data packet queues */
+ brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
+
+ /* Clear any held glomming stuff */
+ brcmu_pkt_buf_free_skb(bus->glomd);
+ brcmf_sdio_free_glom(bus);
+
+ /* Clear rx control and wake any waiters */
+ spin_lock_bh(&bus->rxctl_lock);
+ bus->rxlen = 0;
+ spin_unlock_bh(&bus->rxctl_lock);
+ brcmf_sdio_dcmd_resp_wake(bus);
+
+ /* Reset some F2 state stuff */
+ bus->rxskip = false;
+ bus->tx_seq = bus->rx_seq = 0;
+}
+
+static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
+{
+ unsigned long flags;
+
+ if (bus->sdiodev->oob_irq_requested) {
+ spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
+ if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
+ enable_irq(bus->sdiodev->pdata->oob_irq_nr);
+ bus->sdiodev->irq_en = true;
+ }
+ spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags);
+ }
+}
+
+static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
+{
+ struct brcmf_core *buscore;
+ u32 addr;
+ unsigned long val;
+ int ret;
+
+ buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus);
+
+ val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
+ bus->sdcnt.f1regdata++;
+ if (ret != 0)
+ return ret;
+
+ val &= bus->hostintmask;
+ atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));
+
+ /* Clear interrupts */
+ if (val) {
+ brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
+ bus->sdcnt.f1regdata++;
+ atomic_or(val, &bus->intstatus);
+ }
+
+ return ret;
+}
+
+static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
+{
+ u32 newstatus = 0;
+ unsigned long intstatus;
+ uint txlimit = bus->txbound; /* Tx frames to send before resched */
+ uint framecnt; /* Temporary counter of tx/rx frames */
+ int err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+
+ /* If waiting for HTAVAIL, check status */
+ if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) {
+ u8 clkctl, devctl = 0;
+
+#ifdef DEBUG
+ /* Check for inconsistent device control */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+#endif /* DEBUG */
+
+ /* Read CSR, if clock on switch to AVAIL, else ignore */
+ clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+ brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
+ devctl, clkctl);
+
+ if (SBSDIO_HTAV(clkctl)) {
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ bus->clkstate = CLK_AVAIL;
+ }
+ }
+
+ /* Make sure backplane clock is on */
+ brcmf_sdio_bus_sleep(bus, false, true);
+
+ /* Pending interrupt indicates new device status */
+ if (atomic_read(&bus->ipend) > 0) {
+ atomic_set(&bus->ipend, 0);
+ err = brcmf_sdio_intr_rstatus(bus);
+ }
+
+ /* Start with leftover status bits */
+ intstatus = atomic_xchg(&bus->intstatus, 0);
+
+ /* Handle flow-control change: read new state in case our ack
+ * crossed another change interrupt. If change still set, assume
+ * FC ON for safety, let next loop through do the debounce.
+ */
+ if (intstatus & I_HMB_FC_CHANGE) {
+ intstatus &= ~I_HMB_FC_CHANGE;
+ err = w_sdreg32(bus, I_HMB_FC_CHANGE,
+ offsetof(struct sdpcmd_regs, intstatus));
+
+ err = r_sdreg32(bus, &newstatus,
+ offsetof(struct sdpcmd_regs, intstatus));
+ bus->sdcnt.f1regdata += 2;
+ atomic_set(&bus->fcstate,
+ !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)));
+ intstatus |= (newstatus & bus->hostintmask);
+ }
+
+ /* Handle host mailbox indication */
+ if (intstatus & I_HMB_HOST_INT) {
+ intstatus &= ~I_HMB_HOST_INT;
+ intstatus |= brcmf_sdio_hostmail(bus);
+ }
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ /* Generally don't ask for these, can get CRC errors... */
+ if (intstatus & I_WR_OOSYNC) {
+ brcmf_err("Dongle reports WR_OOSYNC\n");
+ intstatus &= ~I_WR_OOSYNC;
+ }
+
+ if (intstatus & I_RD_OOSYNC) {
+ brcmf_err("Dongle reports RD_OOSYNC\n");
+ intstatus &= ~I_RD_OOSYNC;
+ }
+
+ if (intstatus & I_SBINT) {
+ brcmf_err("Dongle reports SBINT\n");
+ intstatus &= ~I_SBINT;
+ }
+
+ /* Would be active due to wake-wlan in gSPI */
+ if (intstatus & I_CHIPACTIVE) {
+ brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n");
+ intstatus &= ~I_CHIPACTIVE;
+ }
+
+ /* Ignore frame indications if rxskip is set */
+ if (bus->rxskip)
+ intstatus &= ~I_HMB_FRAME_IND;
+
+ /* On frame indication, read available frames */
+ if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) {
+ brcmf_sdio_readframes(bus, bus->rxbound);
+ if (!bus->rxpending)
+ intstatus &= ~I_HMB_FRAME_IND;
+ }
+
+ /* Keep still-pending events for next scheduling */
+ if (intstatus)
+ atomic_or(intstatus, &bus->intstatus);
+
+ brcmf_sdio_clrintr(bus);
+
+ if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
+ data_ok(bus)) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
+ bus->ctrl_frame_len);
+ bus->ctrl_frame_err = err;
+ wmb();
+ bus->ctrl_frame_stat = false;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wait_event_wakeup(bus);
+ }
+ /* Send queued frames (limit 1 if rx may still be pending) */
+ if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit &&
+ data_ok(bus)) {
+ framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
+ txlimit;
+ brcmf_sdio_sendfromq(bus, framecnt);
+ }
+
+ if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) {
+ brcmf_err("failed backplane access over SDIO, halting operation\n");
+ atomic_set(&bus->intstatus, 0);
+ if (bus->ctrl_frame_stat) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ bus->ctrl_frame_err = -ENODEV;
+ wmb();
+ bus->ctrl_frame_stat = false;
+ brcmf_sdio_wait_event_wakeup(bus);
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ } else if (atomic_read(&bus->intstatus) ||
+ atomic_read(&bus->ipend) > 0 ||
+ (!atomic_read(&bus->fcstate) &&
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
+ data_ok(bus))) {
+ bus->dpc_triggered = true;
+ }
+}
+
+static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ return &bus->txq;
+}
+
+static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec)
+{
+ struct sk_buff *p;
+ int eprec = -1; /* precedence to evict from */
+
+ /* Fast case, precedence queue is not full and we are also not
+ * exceeding total queue length
+ */
+ if (!pktq_pfull(q, prec) && !pktq_full(q)) {
+ brcmu_pktq_penq(q, prec, pkt);
+ return true;
+ }
+
+ /* Determine precedence from which to evict packet, if any */
+ if (pktq_pfull(q, prec)) {
+ eprec = prec;
+ } else if (pktq_full(q)) {
+ p = brcmu_pktq_peek_tail(q, &eprec);
+ if (eprec > prec)
+ return false;
+ }
+
+ /* Evict if needed */
+ if (eprec >= 0) {
+ /* Detect queueing to unconfigured precedence */
+ if (eprec == prec)
+ return false; /* refuse newer (incoming) packet */
+ /* Evict packet according to discard policy */
+ p = brcmu_pktq_pdeq_tail(q, eprec);
+ if (p == NULL)
+ brcmf_err("brcmu_pktq_pdeq_tail() failed\n");
+ brcmu_pkt_buf_free_skb(p);
+ }
+
+ /* Enqueue */
+ p = brcmu_pktq_penq(q, prec, pkt);
+ if (p == NULL)
+ brcmf_err("brcmu_pktq_penq() failed\n");
+
+ return p != NULL;
+}
+
+static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
+{
+ int ret = -EBADE;
+ uint prec;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
+ if (sdiodev->state != BRCMF_SDIOD_DATA)
+ return -EIO;
+
+ /* Add space for the header */
+ skb_push(pkt, bus->tx_hdrlen);
+ /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
+
+ prec = prio2prec((pkt->priority & PRIOMASK));
+
+ /* Check for existing queue, current flow-control,
+ pending event, or pending clock */
+ brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
+ bus->sdcnt.fcqueued++;
+
+ /* Priority based enq */
+ spin_lock_bh(&bus->txq_lock);
+ /* reset bus_flags in packet cb */
+ *(u16 *)(pkt->cb) = 0;
+ if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) {
+ skb_pull(pkt, bus->tx_hdrlen);
+ brcmf_err("out of bus->txq !!!\n");
+ ret = -ENOSR;
+ } else {
+ ret = 0;
+ }
+
+ if (pktq_len(&bus->txq) >= TXHI) {
+ bus->txoff = true;
+ brcmf_txflowblock(dev, true);
+ }
+ spin_unlock_bh(&bus->txq_lock);
+
+#ifdef DEBUG
+ if (pktq_plen(&bus->txq, prec) > qcount[prec])
+ qcount[prec] = pktq_plen(&bus->txq, prec);
+#endif
+
+ brcmf_sdio_trigger_dpc(bus);
+ return ret;
+}
+
+#ifdef DEBUG
+#define CONSOLE_LINE_MAX 192
+
+static int brcmf_sdio_readconsole(struct brcmf_sdio *bus)
+{
+ struct brcmf_console *c = &bus->console;
+ u8 line[CONSOLE_LINE_MAX], ch;
+ u32 n, idx, addr;
+ int rv;
+
+ /* Don't do anything until FWREADY updates console address */
+ if (bus->console_addr == 0)
+ return 0;
+
+ /* Read console log struct */
+ addr = bus->console_addr + offsetof(struct rte_console, log_le);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le,
+ sizeof(c->log_le));
+ if (rv < 0)
+ return rv;
+
+ /* Allocate console buffer (one time only) */
+ if (c->buf == NULL) {
+ c->bufsize = le32_to_cpu(c->log_le.buf_size);
+ c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
+ if (c->buf == NULL)
+ return -ENOMEM;
+ }
+
+ idx = le32_to_cpu(c->log_le.idx);
+
+ /* Protect against corrupt value */
+ if (idx > c->bufsize)
+ return -EBADE;
+
+ /* Skip reading the console buffer if the index pointer
+ has not moved */
+ if (idx == c->last)
+ return 0;
+
+ /* Read the console buffer */
+ addr = le32_to_cpu(c->log_le.buf);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);
+ if (rv < 0)
+ return rv;
+
+ while (c->last != idx) {
+ for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
+ if (c->last == idx) {
+ /* This would output a partial line.
+ * Instead, back up
+ * the buffer pointer and output this
+ * line next time around.
+ */
+ if (c->last >= n)
+ c->last -= n;
+ else
+ c->last = c->bufsize - n;
+ goto break2;
+ }
+ ch = c->buf[c->last];
+ c->last = (c->last + 1) % c->bufsize;
+ if (ch == '\n')
+ break;
+ line[n] = ch;
+ }
+
+ if (n > 0) {
+ if (line[n - 1] == '\r')
+ n--;
+ line[n] = 0;
+ pr_debug("CONSOLE: %s\n", line);
+ }
+ }
+break2:
+
+ return 0;
+}
+#endif /* DEBUG */
+
+static int
+brcmf_sdio_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;
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (sdiodev->state != BRCMF_SDIOD_DATA)
+ return -EIO;
+
+ /* Send from dpc */
+ bus->ctrl_frame_buf = msg;
+ bus->ctrl_frame_len = msglen;
+ wmb();
+ bus->ctrl_frame_stat = true;
+
+ brcmf_sdio_trigger_dpc(bus);
+ wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
+ CTL_DONE_TIMEOUT);
+ ret = 0;
+ if (bus->ctrl_frame_stat) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ brcmf_dbg(SDIO, "ctrl_frame timeout\n");
+ bus->ctrl_frame_stat = false;
+ ret = -ETIMEDOUT;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ if (!ret) {
+ brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n",
+ bus->ctrl_frame_err);
+ rmb();
+ ret = bus->ctrl_frame_err;
+ }
+
+ if (ret)
+ bus->sdcnt.tx_ctlerrs++;
+ else
+ bus->sdcnt.tx_ctlpkts++;
+
+ return ret;
+}
+
+#ifdef DEBUG
+static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ u32 addr, console_ptr, console_size, console_index;
+ char *conbuf = NULL;
+ __le32 sh_val;
+ int rv;
+
+ /* obtain console information from device memory */
+ addr = sh->console_addr + offsetof(struct rte_console, log_le);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_ptr = le32_to_cpu(sh_val);
+
+ addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_size = le32_to_cpu(sh_val);
+
+ addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_index = le32_to_cpu(sh_val);
+
+ /* allocate buffer for console data */
+ if (console_size <= CONSOLE_BUFFER_MAX)
+ conbuf = vzalloc(console_size+1);
+
+ if (!conbuf)
+ return -ENOMEM;
+
+ /* obtain the console data from device */
+ conbuf[console_size] = '\0';
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf,
+ console_size);
+ if (rv < 0)
+ goto done;
+
+ rv = seq_write(seq, conbuf + console_index,
+ console_size - console_index);
+ if (rv < 0)
+ goto done;
+
+ if (console_index > 0)
+ rv = seq_write(seq, conbuf, console_index - 1);
+
+done:
+ vfree(conbuf);
+ return rv;
+}
+
+static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ int error;
+ struct brcmf_trap_info tr;
+
+ if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
+ brcmf_dbg(INFO, "no trap in firmware\n");
+ return 0;
+ }
+
+ error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr,
+ sizeof(struct brcmf_trap_info));
+ if (error < 0)
+ return error;
+
+ seq_printf(seq,
+ "dongle trap info: type 0x%x @ epc 0x%08x\n"
+ " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
+ " lr 0x%08x pc 0x%08x offset 0x%x\n"
+ " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n"
+ " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n",
+ le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
+ le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
+ le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
+ le32_to_cpu(tr.pc), sh->trap_addr,
+ le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
+ le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
+ le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
+ le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
+
+ return 0;
+}
+
+static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ int error = 0;
+ char file[80] = "?";
+ char expr[80] = "<???>";
+
+ if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
+ brcmf_dbg(INFO, "firmware not built with -assert\n");
+ return 0;
+ } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) {
+ brcmf_dbg(INFO, "no assert in dongle\n");
+ return 0;
+ }
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (sh->assert_file_addr != 0) {
+ error = brcmf_sdiod_ramrw(bus->sdiodev, false,
+ sh->assert_file_addr, (u8 *)file, 80);
+ if (error < 0)
+ return error;
+ }
+ if (sh->assert_exp_addr != 0) {
+ error = brcmf_sdiod_ramrw(bus->sdiodev, false,
+ sh->assert_exp_addr, (u8 *)expr, 80);
+ if (error < 0)
+ return error;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n",
+ file, sh->assert_line, expr);
+ return 0;
+}
+
+static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
+{
+ int error;
+ struct sdpcm_shared sh;
+
+ error = brcmf_sdio_readshared(bus, &sh);
+
+ if (error < 0)
+ return error;
+
+ if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0)
+ brcmf_dbg(INFO, "firmware not built with -assert\n");
+ else if (sh.flags & SDPCM_SHARED_ASSERT)
+ brcmf_err("assertion in dongle\n");
+
+ if (sh.flags & SDPCM_SHARED_TRAP)
+ brcmf_err("firmware trap in dongle\n");
+
+ return 0;
+}
+
+static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus)
+{
+ int error = 0;
+ struct sdpcm_shared sh;
+
+ error = brcmf_sdio_readshared(bus, &sh);
+ if (error < 0)
+ goto done;
+
+ error = brcmf_sdio_assert_info(seq, bus, &sh);
+ if (error < 0)
+ goto done;
+
+ error = brcmf_sdio_trap_info(seq, bus, &sh);
+ if (error < 0)
+ goto done;
+
+ error = brcmf_sdio_dump_console(seq, bus, &sh);
+
+done:
+ return error;
+}
+
+static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus;
+
+ return brcmf_sdio_died_dump(seq, bus);
+}
+
+static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt;
+
+ seq_printf(seq,
+ "intrcount: %u\nlastintrs: %u\n"
+ "pollcnt: %u\nregfails: %u\n"
+ "tx_sderrs: %u\nfcqueued: %u\n"
+ "rxrtx: %u\nrx_toolong: %u\n"
+ "rxc_errors: %u\nrx_hdrfail: %u\n"
+ "rx_badhdr: %u\nrx_badseq: %u\n"
+ "fc_rcvd: %u\nfc_xoff: %u\n"
+ "fc_xon: %u\nrxglomfail: %u\n"
+ "rxglomframes: %u\nrxglompkts: %u\n"
+ "f2rxhdrs: %u\nf2rxdata: %u\n"
+ "f2txdata: %u\nf1regdata: %u\n"
+ "tickcnt: %u\ntx_ctlerrs: %lu\n"
+ "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n"
+ "rx_ctlpkts: %lu\nrx_readahead: %lu\n",
+ sdcnt->intrcount, sdcnt->lastintrs,
+ sdcnt->pollcnt, sdcnt->regfails,
+ sdcnt->tx_sderrs, sdcnt->fcqueued,
+ sdcnt->rxrtx, sdcnt->rx_toolong,
+ sdcnt->rxc_errors, sdcnt->rx_hdrfail,
+ sdcnt->rx_badhdr, sdcnt->rx_badseq,
+ sdcnt->fc_rcvd, sdcnt->fc_xoff,
+ sdcnt->fc_xon, sdcnt->rxglomfail,
+ sdcnt->rxglomframes, sdcnt->rxglompkts,
+ sdcnt->f2rxhdrs, sdcnt->f2rxdata,
+ sdcnt->f2txdata, sdcnt->f1regdata,
+ sdcnt->tickcnt, sdcnt->tx_ctlerrs,
+ sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
+ sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
+
+ return 0;
+}
+
+static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
+{
+ struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr;
+ struct dentry *dentry = brcmf_debugfs_get_devdir(drvr);
+
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ bus->console_interval = BRCMF_CONSOLE;
+
+ brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
+ brcmf_debugfs_add_entry(drvr, "counters",
+ brcmf_debugfs_sdio_count_read);
+ debugfs_create_u32("console_interval", 0644, dentry,
+ &bus->console_interval);
+}
+#else
+static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
+{
+ return 0;
+}
+
+static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
+{
+}
+#endif /* DEBUG */
+
+static int
+brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
+{
+ int timeleft;
+ uint rxlen = 0;
+ bool pending;
+ u8 *buf;
+ 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;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (sdiodev->state != BRCMF_SDIOD_DATA)
+ return -EIO;
+
+ /* Wait until control frame is available */
+ timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);
+
+ spin_lock_bh(&bus->rxctl_lock);
+ rxlen = bus->rxlen;
+ memcpy(msg, bus->rxctl, min(msglen, rxlen));
+ bus->rxctl = NULL;
+ buf = bus->rxctl_orig;
+ bus->rxctl_orig = NULL;
+ bus->rxlen = 0;
+ spin_unlock_bh(&bus->rxctl_lock);
+ vfree(buf);
+
+ if (rxlen) {
+ brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n",
+ rxlen, msglen);
+ } else if (timeleft == 0) {
+ brcmf_err("resumed on timeout\n");
+ brcmf_sdio_checkdied(bus);
+ } else if (pending) {
+ brcmf_dbg(CTL, "cancelled\n");
+ return -ERESTARTSYS;
+ } else {
+ brcmf_dbg(CTL, "resumed for unknown reason?\n");
+ brcmf_sdio_checkdied(bus);
+ }
+
+ if (rxlen)
+ bus->sdcnt.rx_ctlpkts++;
+ else
+ bus->sdcnt.rx_ctlerrs++;
+
+ return rxlen ? (int)rxlen : -ETIMEDOUT;
+}
+
+#ifdef DEBUG
+static bool
+brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
+ u8 *ram_data, uint ram_sz)
+{
+ char *ram_cmp;
+ int err;
+ bool ret = true;
+ int address;
+ int offset;
+ int len;
+
+ /* read back and verify */
+ brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr,
+ ram_sz);
+ ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL);
+ /* do not proceed while no memory but */
+ if (!ram_cmp)
+ return true;
+
+ address = ram_addr;
+ offset = 0;
+ while (offset < ram_sz) {
+ len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK :
+ ram_sz - offset;
+ err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len);
+ if (err) {
+ brcmf_err("error %d on reading %d membytes at 0x%08x\n",
+ err, len, address);
+ ret = false;
+ break;
+ } else if (memcmp(ram_cmp, &ram_data[offset], len)) {
+ brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n",
+ offset, len);
+ ret = false;
+ break;
+ }
+ offset += len;
+ address += len;
+ }
+
+ kfree(ram_cmp);
+
+ return ret;
+}
+#else /* DEBUG */
+static bool
+brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
+ u8 *ram_data, uint ram_sz)
+{
+ return true;
+}
+#endif /* DEBUG */
+
+static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
+ const struct firmware *fw)
+{
+ int err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase,
+ (u8 *)fw->data, fw->size);
+ if (err)
+ brcmf_err("error %d on writing %d membytes at 0x%08x\n",
+ err, (int)fw->size, bus->ci->rambase);
+ else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
+ (u8 *)fw->data, fw->size))
+ err = -EIO;
+
+ return err;
+}
+
+static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
+ void *vars, u32 varsz)
+{
+ int address;
+ int err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ address = bus->ci->ramsize - varsz + bus->ci->rambase;
+ err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
+ if (err)
+ brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
+ err, varsz, address);
+ else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
+ err = -EIO;
+
+ return err;
+}
+
+static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
+ const struct firmware *fw,
+ void *nvram, u32 nvlen)
+{
+ int bcmerror = -EFAULT;
+ u32 rstvec;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+
+ rstvec = get_unaligned_le32(fw->data);
+ brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
+
+ bcmerror = brcmf_sdio_download_code_file(bus, fw);
+ release_firmware(fw);
+ if (bcmerror) {
+ brcmf_err("dongle image file download failed\n");
+ brcmf_fw_nvram_free(nvram);
+ goto err;
+ }
+
+ bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen);
+ brcmf_fw_nvram_free(nvram);
+ if (bcmerror) {
+ brcmf_err("dongle nvram file download failed\n");
+ goto err;
+ }
+
+ /* Take arm out of reset */
+ if (!brcmf_chip_set_active(bus->ci, rstvec)) {
+ brcmf_err("error getting out of ARM core reset\n");
+ goto err;
+ }
+
+ /* Allow full data communication using DPC from now on. */
+ brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
+ bcmerror = 0;
+
+err:
+ brcmf_sdio_clkctl(bus, CLK_SDONLY, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ return bcmerror;
+}
+
+static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
+{
+ int err = 0;
+ u8 val;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
+ if (err) {
+ brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
+ return;
+ }
+
+ val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
+ return;
+ }
+
+ /* Add CMD14 Support */
+ brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
+ (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
+ SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
+ &err);
+ if (err) {
+ brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
+ return;
+ }
+
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_FORCE_HT, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
+ return;
+ }
+
+ /* set flag */
+ bus->sr_enabled = true;
+ brcmf_dbg(INFO, "SR enabled\n");
+}
+
+/* enable KSO bit */
+static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
+{
+ u8 val;
+ int err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* KSO bit added in SDIO core rev 12 */
+ if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)
+ return 0;
+
+ val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
+ if (err) {
+ brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");
+ return err;
+ }
+
+ if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
+ val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<
+ SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ val, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+static int brcmf_sdio_bus_preinit(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+ uint pad_size;
+ u32 value;
+ int err;
+
+ /* the commands below use the terms tx and rx from
+ * a device perspective, ie. bus:txglom affects the
+ * bus transfers from device to host.
+ */
+ if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) {
+ /* for sdio core rev < 12, disable txgloming */
+ value = 0;
+ err = brcmf_iovar_data_set(dev, "bus:txglom", &value,
+ sizeof(u32));
+ } else {
+ /* otherwise, set txglomalign */
+ value = 4;
+ if (sdiodev->pdata)
+ value = sdiodev->pdata->sd_sgentry_align;
+ /* SDIO ADMA requires at least 32 bit alignment */
+ value = max_t(u32, value, 4);
+ err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value,
+ sizeof(u32));
+ }
+
+ if (err < 0)
+ goto done;
+
+ bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
+ if (sdiodev->sg_support) {
+ bus->txglom = false;
+ value = 1;
+ pad_size = bus->sdiodev->func[2]->cur_blksize << 1;
+ err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom",
+ &value, sizeof(u32));
+ if (err < 0) {
+ /* bus:rxglom is allowed to fail */
+ err = 0;
+ } else {
+ bus->txglom = true;
+ bus->tx_hdrlen += SDPCM_HWEXT_LEN;
+ }
+ }
+ brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen);
+
+done:
+ return err;
+}
+
+static size_t brcmf_sdio_bus_get_ramsize(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ return bus->ci->ramsize - bus->ci->srsize;
+}
+
+static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data,
+ size_t mem_size)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+ int err;
+ int address;
+ int offset;
+ int len;
+
+ brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase,
+ mem_size);
+
+ address = bus->ci->rambase;
+ offset = err = 0;
+ sdio_claim_host(sdiodev->func[1]);
+ while (offset < mem_size) {
+ len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK :
+ mem_size - offset;
+ err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len);
+ if (err) {
+ brcmf_err("error %d on reading %d membytes at 0x%08x\n",
+ err, len, address);
+ goto done;
+ }
+ data += len;
+ offset += len;
+ address += len;
+ }
+
+done:
+ sdio_release_host(sdiodev->func[1]);
+ return err;
+}
+
+void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
+{
+ if (!bus->dpc_triggered) {
+ bus->dpc_triggered = true;
+ queue_work(bus->brcmf_wq, &bus->datawork);
+ }
+}
+
+void brcmf_sdio_isr(struct brcmf_sdio *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (!bus) {
+ brcmf_err("bus is null pointer, exiting\n");
+ return;
+ }
+
+ /* Count the interrupt call */
+ bus->sdcnt.intrcount++;
+ if (in_interrupt())
+ atomic_set(&bus->ipend, 1);
+ else
+ if (brcmf_sdio_intr_rstatus(bus)) {
+ brcmf_err("failed backplane access\n");
+ }
+
+ /* Disable additional interrupts (is this needed now)? */
+ if (!bus->intr)
+ brcmf_err("isr w/o interrupt configured!\n");
+
+ bus->dpc_triggered = true;
+ queue_work(bus->brcmf_wq, &bus->datawork);
+}
+
+static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
+{
+ brcmf_dbg(TIMER, "Enter\n");
+
+ /* Poll period: check device if appropriate. */
+ if (!bus->sr_enabled &&
+ bus->poll && (++bus->polltick >= bus->pollrate)) {
+ u32 intstatus = 0;
+
+ /* Reset poll tick */
+ bus->polltick = 0;
+
+ /* Check device if no interrupts */
+ if (!bus->intr ||
+ (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
+
+ if (!bus->dpc_triggered) {
+ u8 devpend;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ devpend = brcmf_sdiod_regrb(bus->sdiodev,
+ SDIO_CCCR_INTx,
+ NULL);
+ sdio_release_host(bus->sdiodev->func[1]);
+ intstatus = devpend & (INTR_STATUS_FUNC1 |
+ INTR_STATUS_FUNC2);
+ }
+
+ /* If there is something, make like the ISR and
+ schedule the DPC */
+ if (intstatus) {
+ bus->sdcnt.pollcnt++;
+ atomic_set(&bus->ipend, 1);
+
+ bus->dpc_triggered = true;
+ queue_work(bus->brcmf_wq, &bus->datawork);
+ }
+ }
+
+ /* Update interrupt tracking */
+ bus->sdcnt.lastintrs = bus->sdcnt.intrcount;
+ }
+#ifdef DEBUG
+ /* Poll for console output periodically */
+ if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
+ bus->console_interval != 0) {
+ bus->console.count += jiffies_to_msecs(BRCMF_WD_POLL);
+ if (bus->console.count >= bus->console_interval) {
+ bus->console.count -= bus->console_interval;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ /* Make sure backplane clock is on */
+ brcmf_sdio_bus_sleep(bus, false, false);
+ if (brcmf_sdio_readconsole(bus) < 0)
+ /* stop on error */
+ bus->console_interval = 0;
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ }
+#endif /* DEBUG */
+
+ /* On idle timeout clear activity flag and/or turn off clock */
+ if (!bus->dpc_triggered) {
+ rmb();
+ if ((!bus->dpc_running) && (bus->idletime > 0) &&
+ (bus->clkstate == CLK_AVAIL)) {
+ bus->idlecount++;
+ if (bus->idlecount > bus->idletime) {
+ brcmf_dbg(SDIO, "idle\n");
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wd_timer(bus, false);
+ bus->idlecount = 0;
+ brcmf_sdio_bus_sleep(bus, true, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ } else {
+ bus->idlecount = 0;
+ }
+ } else {
+ bus->idlecount = 0;
+ }
+}
+
+static void brcmf_sdio_dataworker(struct work_struct *work)
+{
+ struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
+ datawork);
+
+ bus->dpc_running = true;
+ wmb();
+ while (ACCESS_ONCE(bus->dpc_triggered)) {
+ bus->dpc_triggered = false;
+ brcmf_sdio_dpc(bus);
+ bus->idlecount = 0;
+ }
+ bus->dpc_running = false;
+ if (brcmf_sdiod_freezing(bus->sdiodev)) {
+ brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN);
+ brcmf_sdiod_try_freeze(bus->sdiodev);
+ brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
+ }
+}
+
+static void
+brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
+ struct brcmf_chip *ci, u32 drivestrength)
+{
+ const struct sdiod_drive_str *str_tab = NULL;
+ u32 str_mask;
+ u32 str_shift;
+ u32 base;
+ u32 i;
+ u32 drivestrength_sel = 0;
+ u32 cc_data_temp;
+ u32 addr;
+
+ if (!(ci->cc_caps & CC_CAP_PMU))
+ return;
+
+ switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
+ case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12):
+ str_tab = sdiod_drvstr_tab1_1v8;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17):
+ str_tab = sdiod_drvstr_tab6_1v8;
+ str_mask = 0x00001800;
+ str_shift = 11;
+ break;
+ case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17):
+ /* note: 43143 does not support tristate */
+ i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
+ if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
+ str_tab = sdiod_drvstr_tab2_3v3;
+ str_mask = 0x00000007;
+ str_shift = 0;
+ } else
+ brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
+ ci->name, drivestrength);
+ break;
+ case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13):
+ str_tab = sdiod_drive_strength_tab5_1v8;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ default:
+ brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
+ ci->name, ci->chiprev, ci->pmurev);
+ break;
+ }
+
+ if (str_tab != NULL) {
+ for (i = 0; str_tab[i].strength != 0; i++) {
+ if (drivestrength >= str_tab[i].strength) {
+ drivestrength_sel = str_tab[i].sel;
+ break;
+ }
+ }
+ base = brcmf_chip_get_chipcommon(ci)->base;
+ addr = CORE_CC_REG(base, chipcontrol_addr);
+ brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
+ cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+ cc_data_temp &= ~str_mask;
+ drivestrength_sel <<= str_shift;
+ cc_data_temp |= drivestrength_sel;
+ brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
+
+ brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
+ str_tab[i].strength, drivestrength, cc_data_temp);
+ }
+}
+
+static int brcmf_sdio_buscoreprep(void *ctx)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ int err = 0;
+ u8 clkval, clkset;
+
+ /* Try forcing SDIO core to do ALPAvail request only */
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ if (err) {
+ brcmf_err("error writing for HT off\n");
+ return err;
+ }
+
+ /* If register supported, wait for ALPAvail and then force ALP */
+ /* This may take up to 15 milliseconds */
+ clkval = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+
+ if ((clkval & ~SBSDIO_AVBITS) != clkset) {
+ brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
+ clkset, clkval);
+ return -EACCES;
+ }
+
+ SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
+ !SBSDIO_ALPAV(clkval)),
+ PMU_MAX_TRANSITION_DLY);
+ if (!SBSDIO_ALPAV(clkval)) {
+ brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
+ clkval);
+ return -EBUSY;
+ }
+
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ udelay(65);
+
+ /* Also, disable the extra SDIO pull-ups */
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+
+ return 0;
+}
+
+static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ struct brcmf_core *core;
+ u32 reg_addr;
+
+ /* clear all interrupts */
+ core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV);
+ reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus);
+ brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+
+ if (rstvec)
+ /* Write reset vector to address 0 */
+ brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
+ sizeof(rstvec));
+}
+
+static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ u32 val, rev;
+
+ val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+ if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
+ addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
+ rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
+ if (rev >= 2) {
+ val &= ~CID_ID_MASK;
+ val |= BRCM_CC_4339_CHIP_ID;
+ }
+ }
+ return val;
+}
+
+static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+
+ brcmf_sdiod_regwl(sdiodev, addr, val, NULL);
+}
+
+static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
+ .prepare = brcmf_sdio_buscoreprep,
+ .activate = brcmf_sdio_buscore_activate,
+ .read32 = brcmf_sdio_buscore_read32,
+ .write32 = brcmf_sdio_buscore_write32,
+};
+
+static bool
+brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
+{
+ u8 clkctl = 0;
+ int err = 0;
+ int reg_addr;
+ u32 reg_val;
+ u32 drivestrength;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+
+ pr_debug("F1 signature read @0x18000000=0x%4x\n",
+ brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
+
+ /*
+ * Force PLL off until brcmf_chip_attach()
+ * programs PLL control regs
+ */
+
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ BRCMF_INIT_CLKCTL1, &err);
+ if (!err)
+ clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+ if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
+ brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
+ err, BRCMF_INIT_CLKCTL1, clkctl);
+ goto fail;
+ }
+
+ bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
+ if (IS_ERR(bus->ci)) {
+ brcmf_err("brcmf_chip_attach failed!\n");
+ bus->ci = NULL;
+ goto fail;
+ }
+
+ if (brcmf_sdio_kso_init(bus)) {
+ brcmf_err("error enabling KSO\n");
+ goto fail;
+ }
+
+ if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength))
+ drivestrength = bus->sdiodev->pdata->drive_strength;
+ else
+ drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
+ brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
+
+ /* Set card control so an SDIO card reset does a WLAN backplane reset */
+ reg_val = brcmf_sdiod_regrb(bus->sdiodev,
+ SDIO_CCCR_BRCM_CARDCTRL, &err);
+ if (err)
+ goto fail;
+
+ reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
+
+ brcmf_sdiod_regwb(bus->sdiodev,
+ SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
+ if (err)
+ goto fail;
+
+ /* set PMUControl so a backplane reset does PMU state reload */
+ reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
+ pmucontrol);
+ reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
+ if (err)
+ goto fail;
+
+ reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
+
+ brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);
+ if (err)
+ goto fail;
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
+
+ /* allocate header buffer */
+ bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL);
+ if (!bus->hdrbuf)
+ return false;
+ /* Locate an appropriately-aligned portion of hdrbuf */
+ bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0],
+ bus->head_align);
+
+ /* Set the poll and/or interrupt flags */
+ bus->intr = true;
+ bus->poll = false;
+ if (bus->poll)
+ bus->pollrate = 1;
+
+ return true;
+
+fail:
+ sdio_release_host(bus->sdiodev->func[1]);
+ return false;
+}
+
+static int
+brcmf_sdio_watchdog_thread(void *data)
+{
+ struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
+ int wait;
+
+ allow_signal(SIGTERM);
+ /* Run until signal received */
+ brcmf_sdiod_freezer_count(bus->sdiodev);
+ while (1) {
+ if (kthread_should_stop())
+ break;
+ brcmf_sdiod_freezer_uncount(bus->sdiodev);
+ wait = wait_for_completion_interruptible(&bus->watchdog_wait);
+ brcmf_sdiod_freezer_count(bus->sdiodev);
+ brcmf_sdiod_try_freeze(bus->sdiodev);
+ if (!wait) {
+ brcmf_sdio_bus_watchdog(bus);
+ /* Count the tick for reference */
+ bus->sdcnt.tickcnt++;
+ reinit_completion(&bus->watchdog_wait);
+ } else
+ break;
+ }
+ return 0;
+}
+
+static void
+brcmf_sdio_watchdog(unsigned long data)
+{
+ struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
+
+ if (bus->watchdog_tsk) {
+ complete(&bus->watchdog_wait);
+ /* Reschedule the watchdog */
+ if (bus->wd_active)
+ mod_timer(&bus->timer,
+ jiffies + BRCMF_WD_POLL);
+ }
+}
+
+static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
+ .stop = brcmf_sdio_bus_stop,
+ .preinit = brcmf_sdio_bus_preinit,
+ .txdata = brcmf_sdio_bus_txdata,
+ .txctl = brcmf_sdio_bus_txctl,
+ .rxctl = brcmf_sdio_bus_rxctl,
+ .gettxq = brcmf_sdio_bus_gettxq,
+ .wowl_config = brcmf_sdio_wowl_config,
+ .get_ramsize = brcmf_sdio_bus_get_ramsize,
+ .get_memdump = brcmf_sdio_bus_get_memdump,
+};
+
+static void brcmf_sdio_firmware_callback(struct device *dev,
+ const struct firmware *code,
+ void *nvram, u32 nvram_len)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+ int err = 0;
+ u8 saveclk;
+
+ brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev));
+
+ if (!bus_if->drvr)
+ return;
+
+ /* try to download image and nvram to the dongle */
+ bus->alp_only = true;
+ err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
+ if (err)
+ goto fail;
+ bus->alp_only = false;
+
+ /* Start the watchdog timer */
+ bus->sdcnt.tickcnt = 0;
+ brcmf_sdio_wd_timer(bus, true);
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ /* Make sure backplane clock is on, needed to generate F2 interrupt */
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+ if (bus->clkstate != CLK_AVAIL)
+ goto release;
+
+ /* Force clocks on backplane to be sure F2 interrupt propagates */
+ saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (!err) {
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ }
+ if (err) {
+ brcmf_err("Failed to force clock for F2: err %d\n", err);
+ goto release;
+ }
+
+ /* Enable function 2 (frame transfers) */
+ w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
+ offsetof(struct sdpcmd_regs, tosbmailboxdata));
+ err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
+
+
+ brcmf_dbg(INFO, "enable F2: err=%d\n", err);
+
+ /* If F2 successfully enabled, set core and enable interrupts */
+ if (!err) {
+ /* Set up the interrupt mask and enable interrupts */
+ bus->hostintmask = HOSTINTMASK;
+ w_sdreg32(bus, bus->hostintmask,
+ offsetof(struct sdpcmd_regs, hostintmask));
+
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
+ } else {
+ /* Disable F2 again */
+ sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
+ goto release;
+ }
+
+ if (brcmf_chip_sr_capable(bus->ci)) {
+ brcmf_sdio_sr_init(bus);
+ } else {
+ /* Restore previous clock setting */
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ saveclk, &err);
+ }
+
+ if (err == 0) {
+ err = brcmf_sdiod_intr_register(sdiodev);
+ if (err != 0)
+ brcmf_err("intr register failed:%d\n", err);
+ }
+
+ /* If we didn't come up, turn off backplane clock */
+ if (err != 0)
+ brcmf_sdio_clkctl(bus, CLK_NONE, false);
+
+ sdio_release_host(sdiodev->func[1]);
+
+ err = brcmf_bus_start(dev);
+ if (err != 0) {
+ brcmf_err("dongle is not responding\n");
+ goto fail;
+ }
+ return;
+
+release:
+ sdio_release_host(sdiodev->func[1]);
+fail:
+ brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
+ device_release_driver(dev);
+}
+
+struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
+{
+ int ret;
+ struct brcmf_sdio *bus;
+ struct workqueue_struct *wq;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Allocate private bus interface state */
+ bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC);
+ if (!bus)
+ goto fail;
+
+ bus->sdiodev = sdiodev;
+ sdiodev->bus = bus;
+ skb_queue_head_init(&bus->glom);
+ bus->txbound = BRCMF_TXBOUND;
+ bus->rxbound = BRCMF_RXBOUND;
+ bus->txminmax = BRCMF_TXMINMAX;
+ bus->tx_seq = SDPCM_SEQ_WRAP - 1;
+
+ /* platform specific configuration:
+ * alignments must be at least 4 bytes for ADMA
+ */
+ bus->head_align = ALIGNMENT;
+ bus->sgentry_align = ALIGNMENT;
+ if (sdiodev->pdata) {
+ if (sdiodev->pdata->sd_head_align > ALIGNMENT)
+ bus->head_align = sdiodev->pdata->sd_head_align;
+ if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT)
+ bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
+ }
+
+ /* single-threaded workqueue */
+ wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
+ dev_name(&sdiodev->func[1]->dev));
+ if (!wq) {
+ brcmf_err("insufficient memory to create txworkqueue\n");
+ goto fail;
+ }
+ brcmf_sdiod_freezer_count(sdiodev);
+ INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
+ bus->brcmf_wq = wq;
+
+ /* attempt to attach to the dongle */
+ if (!(brcmf_sdio_probe_attach(bus))) {
+ brcmf_err("brcmf_sdio_probe_attach failed\n");
+ goto fail;
+ }
+
+ spin_lock_init(&bus->rxctl_lock);
+ spin_lock_init(&bus->txq_lock);
+ init_waitqueue_head(&bus->ctrl_wait);
+ init_waitqueue_head(&bus->dcmd_resp_wait);
+
+ /* Set up the watchdog timer */
+ init_timer(&bus->timer);
+ bus->timer.data = (unsigned long)bus;
+ bus->timer.function = brcmf_sdio_watchdog;
+
+ /* Initialize watchdog thread */
+ init_completion(&bus->watchdog_wait);
+ bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread,
+ bus, "brcmf_wdog/%s",
+ dev_name(&sdiodev->func[1]->dev));
+ if (IS_ERR(bus->watchdog_tsk)) {
+ pr_warn("brcmf_watchdog thread failed to start\n");
+ bus->watchdog_tsk = NULL;
+ }
+ /* Initialize DPC thread */
+ bus->dpc_triggered = false;
+ bus->dpc_running = false;
+
+ /* Assign bus interface call back */
+ bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
+ bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops;
+ bus->sdiodev->bus_if->chip = bus->ci->chip;
+ bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
+
+ /* default sdio bus header length for tx packet */
+ bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
+
+ /* Attach to the common layer, reserve hdr space */
+ ret = brcmf_attach(bus->sdiodev->dev);
+ if (ret != 0) {
+ brcmf_err("brcmf_attach failed\n");
+ goto fail;
+ }
+
+ /* Query the F2 block size, set roundup accordingly */
+ bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
+ bus->roundup = min(max_roundup, bus->blocksize);
+
+ /* Allocate buffers */
+ if (bus->sdiodev->bus_if->maxctl) {
+ bus->sdiodev->bus_if->maxctl += bus->roundup;
+ bus->rxblen =
+ roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
+ ALIGNMENT) + bus->head_align;
+ bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
+ if (!(bus->rxbuf)) {
+ brcmf_err("rxbuf allocation failed\n");
+ goto fail;
+ }
+ }
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+
+ /* Disable F2 to clear any intermediate frame state on the dongle */
+ sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
+
+ bus->rxflow = false;
+
+ /* Done with backplane-dependent accesses, can drop clock... */
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ /* ...and initialize clock/power states */
+ bus->clkstate = CLK_SDONLY;
+ bus->idletime = BRCMF_IDLE_INTERVAL;
+ bus->idleclock = BRCMF_IDLE_ACTIVE;
+
+ /* SR state */
+ bus->sr_enabled = false;
+
+ brcmf_sdio_debugfs_create(bus);
+ brcmf_dbg(INFO, "completed!!\n");
+
+ ret = brcmf_fw_map_chip_to_name(bus->ci->chip, bus->ci->chiprev,
+ brcmf_sdio_fwnames,
+ ARRAY_SIZE(brcmf_sdio_fwnames),
+ sdiodev->fw_name, sdiodev->nvram_name);
+ if (ret)
+ goto fail;
+
+ ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
+ sdiodev->fw_name, sdiodev->nvram_name,
+ brcmf_sdio_firmware_callback);
+ if (ret != 0) {
+ brcmf_err("async firmware request failed: %d\n", ret);
+ goto fail;
+ }
+
+ return bus;
+
+fail:
+ brcmf_sdio_remove(bus);
+ return NULL;
+}
+
+/* Detach and free everything */
+void brcmf_sdio_remove(struct brcmf_sdio *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus) {
+ /* De-register interrupt handler */
+ brcmf_sdiod_intr_unregister(bus->sdiodev);
+
+ brcmf_detach(bus->sdiodev->dev);
+
+ cancel_work_sync(&bus->datawork);
+ if (bus->brcmf_wq)
+ destroy_workqueue(bus->brcmf_wq);
+
+ if (bus->ci) {
+ if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wd_timer(bus, false);
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+ /* Leave the device in state where it is
+ * 'passive'. This is done by resetting all
+ * necessary cores.
+ */
+ msleep(20);
+ brcmf_chip_set_passive(bus->ci);
+ brcmf_sdio_clkctl(bus, CLK_NONE, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ brcmf_chip_detach(bus->ci);
+ }
+
+ kfree(bus->rxbuf);
+ kfree(bus->hdrbuf);
+ kfree(bus);
+ }
+
+ brcmf_dbg(TRACE, "Disconnected\n");
+}
+
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active)
+{
+ /* Totally stop the timer */
+ if (!active && bus->wd_active) {
+ del_timer_sync(&bus->timer);
+ bus->wd_active = false;
+ return;
+ }
+
+ /* don't start the wd until fw is loaded */
+ if (bus->sdiodev->state != BRCMF_SDIOD_DATA)
+ return;
+
+ if (active) {
+ if (!bus->wd_active) {
+ /* Create timer again when watchdog period is
+ dynamically changed or in the first instance
+ */
+ bus->timer.expires = jiffies + BRCMF_WD_POLL;
+ add_timer(&bus->timer);
+ bus->wd_active = true;
+ } else {
+ /* Re arm the timer, at last watchdog period */
+ mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL);
+ }
+ }
+}
+
+int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep)
+{
+ int ret;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ ret = brcmf_sdio_bus_sleep(bus, sleep, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ return ret;
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
new file mode 100644
index 000000000000..5ec7a6d87672
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_SDIO_H
+#define BRCMFMAC_SDIO_H
+
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include "firmware.h"
+
+#define SDIO_FUNC_0 0
+#define SDIO_FUNC_1 1
+#define SDIO_FUNC_2 2
+
+#define SDIOD_FBR_SIZE 0x100
+
+/* io_en */
+#define SDIO_FUNC_ENABLE_1 0x02
+#define SDIO_FUNC_ENABLE_2 0x04
+
+/* io_rdys */
+#define SDIO_FUNC_READY_1 0x02
+#define SDIO_FUNC_READY_2 0x04
+
+/* intr_status */
+#define INTR_STATUS_FUNC1 0x2
+#define INTR_STATUS_FUNC2 0x4
+
+/* Maximum number of I/O funcs */
+#define SDIOD_MAX_IOFUNCS 7
+
+/* mask of register map */
+#define REG_F0_REG_MASK 0x7FF
+#define REG_F1_MISC_MASK 0x1FFFF
+
+/* as of sdiod rev 0, supports 3 functions */
+#define SBSDIO_NUM_FUNCTION 3
+
+/* function 0 vendor specific CCCR registers */
+#define SDIO_CCCR_BRCM_CARDCAP 0xf0
+#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02
+#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04
+#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08
+#define SDIO_CCCR_BRCM_CARDCTRL 0xf1
+#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02
+#define SDIO_CCCR_BRCM_SEPINT 0xf2
+
+#define SDIO_SEPINT_MASK 0x01
+#define SDIO_SEPINT_OE 0x02
+#define SDIO_SEPINT_ACT_HI 0x04
+
+/* function 1 miscellaneous registers */
+
+/* sprom command and status */
+#define SBSDIO_SPROM_CS 0x10000
+/* sprom info register */
+#define SBSDIO_SPROM_INFO 0x10001
+/* sprom indirect access data byte 0 */
+#define SBSDIO_SPROM_DATA_LOW 0x10002
+/* sprom indirect access data byte 1 */
+#define SBSDIO_SPROM_DATA_HIGH 0x10003
+/* sprom indirect access addr byte 0 */
+#define SBSDIO_SPROM_ADDR_LOW 0x10004
+/* gpio select */
+#define SBSDIO_GPIO_SELECT 0x10005
+/* gpio output */
+#define SBSDIO_GPIO_OUT 0x10006
+/* gpio enable */
+#define SBSDIO_GPIO_EN 0x10007
+/* rev < 7, watermark for sdio device */
+#define SBSDIO_WATERMARK 0x10008
+/* control busy signal generation */
+#define SBSDIO_DEVICE_CTL 0x10009
+
+/* SB Address Window Low (b15) */
+#define SBSDIO_FUNC1_SBADDRLOW 0x1000A
+/* SB Address Window Mid (b23:b16) */
+#define SBSDIO_FUNC1_SBADDRMID 0x1000B
+/* SB Address Window High (b31:b24) */
+#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C
+/* Frame Control (frame term/abort) */
+#define SBSDIO_FUNC1_FRAMECTRL 0x1000D
+/* ChipClockCSR (ALP/HT ctl/status) */
+#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E
+/* SdioPullUp (on cmd, d0-d2) */
+#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F
+/* Write Frame Byte Count Low */
+#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019
+/* Write Frame Byte Count High */
+#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A
+/* Read Frame Byte Count Low */
+#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B
+/* Read Frame Byte Count High */
+#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C
+/* MesBusyCtl (rev 11) */
+#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D
+/* Sdio Core Rev 12 */
+#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E
+#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1
+#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0
+#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2
+#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1
+#define SBSDIO_FUNC1_SLEEPCSR 0x1001F
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1
+#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2
+#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1
+
+#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */
+#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */
+
+/* function 1 OCP space */
+
+/* sb offset addr is <= 15 bits, 32k */
+#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF
+#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000
+/* with b15, maps to 32-bit SB access */
+#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000
+
+/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
+
+#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */
+#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */
+#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */
+/* Address bits from SBADDR regs */
+#define SBSDIO_SBWINDOW_MASK 0xffff8000
+
+#define SDIOH_READ 0 /* Read request */
+#define SDIOH_WRITE 1 /* Write request */
+
+#define SDIOH_DATA_FIX 0 /* Fixed addressing */
+#define SDIOH_DATA_INC 1 /* Incremental addressing */
+
+/* internal return code */
+#define SUCCESS 0
+#define ERROR 1
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#define BRCMF_SDALIGN (1 << 6)
+
+/* watchdog polling interval */
+#define BRCMF_WD_POLL msecs_to_jiffies(10)
+
+/**
+ * enum brcmf_sdiod_state - the state of the bus.
+ *
+ * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC.
+ * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled.
+ * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible.
+ */
+enum brcmf_sdiod_state {
+ BRCMF_SDIOD_DOWN,
+ BRCMF_SDIOD_DATA,
+ BRCMF_SDIOD_NOMEDIUM
+};
+
+struct brcmf_sdreg {
+ int func;
+ int offset;
+ int value;
+};
+
+struct brcmf_sdio;
+struct brcmf_sdiod_freezer;
+
+struct brcmf_sdio_dev {
+ struct sdio_func *func[SDIO_MAX_FUNCS];
+ u8 num_funcs; /* Supported funcs on client */
+ u32 sbwad; /* Save backplane window address */
+ struct brcmf_sdio *bus;
+ struct device *dev;
+ struct brcmf_bus *bus_if;
+ struct brcmfmac_sdio_platform_data *pdata;
+ bool oob_irq_requested;
+ bool irq_en; /* irq enable flags */
+ spinlock_t irq_en_lock;
+ bool irq_wake; /* irq wake enable flags */
+ bool sg_support;
+ uint max_request_size;
+ ushort max_segment_count;
+ uint max_segment_size;
+ uint txglomsz;
+ struct sg_table sgtable;
+ char fw_name[BRCMF_FW_NAME_LEN];
+ char nvram_name[BRCMF_FW_NAME_LEN];
+ bool wowl_enabled;
+ enum brcmf_sdiod_state state;
+ struct brcmf_sdiod_freezer *freezer;
+};
+
+/* sdio core registers */
+struct sdpcmd_regs {
+ u32 corecontrol; /* 0x00, rev8 */
+ u32 corestatus; /* rev8 */
+ u32 PAD[1];
+ u32 biststatus; /* rev8 */
+
+ /* PCMCIA access */
+ u16 pcmciamesportaladdr; /* 0x010, rev8 */
+ u16 PAD[1];
+ u16 pcmciamesportalmask; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciawrframebc; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciaunderflowtimer; /* rev8 */
+ u16 PAD[1];
+
+ /* interrupt */
+ u32 intstatus; /* 0x020, rev8 */
+ u32 hostintmask; /* rev8 */
+ u32 intmask; /* rev8 */
+ u32 sbintstatus; /* rev8 */
+ u32 sbintmask; /* rev8 */
+ u32 funcintmask; /* rev4 */
+ u32 PAD[2];
+ u32 tosbmailbox; /* 0x040, rev8 */
+ u32 tohostmailbox; /* rev8 */
+ u32 tosbmailboxdata; /* rev8 */
+ u32 tohostmailboxdata; /* rev8 */
+
+ /* synchronized access to registers in SDIO clock domain */
+ u32 sdioaccess; /* 0x050, rev8 */
+ u32 PAD[3];
+
+ /* PCMCIA frame control */
+ u8 pcmciaframectrl; /* 0x060, rev8 */
+ u8 PAD[3];
+ u8 pcmciawatermark; /* rev8 */
+ u8 PAD[155];
+
+ /* interrupt batching control */
+ u32 intrcvlazy; /* 0x100, rev8 */
+ u32 PAD[3];
+
+ /* counters */
+ u32 cmd52rd; /* 0x110, rev8 */
+ u32 cmd52wr; /* rev8 */
+ u32 cmd53rd; /* rev8 */
+ u32 cmd53wr; /* rev8 */
+ u32 abort; /* rev8 */
+ u32 datacrcerror; /* rev8 */
+ u32 rdoutofsync; /* rev8 */
+ u32 wroutofsync; /* rev8 */
+ u32 writebusy; /* rev8 */
+ u32 readwait; /* rev8 */
+ u32 readterm; /* rev8 */
+ u32 writeterm; /* rev8 */
+ u32 PAD[40];
+ u32 clockctlstatus; /* rev8 */
+ u32 PAD[7];
+
+ u32 PAD[128]; /* DMA engines */
+
+ /* SDIO/PCMCIA CIS region */
+ char cis[512]; /* 0x400-0x5ff, rev6 */
+
+ /* PCMCIA function control registers */
+ char pcmciafcr[256]; /* 0x600-6ff, rev6 */
+ u16 PAD[55];
+
+ /* PCMCIA backplane access */
+ u16 backplanecsr; /* 0x76E, rev6 */
+ u16 backplaneaddr0; /* rev6 */
+ u16 backplaneaddr1; /* rev6 */
+ u16 backplaneaddr2; /* rev6 */
+ u16 backplaneaddr3; /* rev6 */
+ u16 backplanedata0; /* rev6 */
+ u16 backplanedata1; /* rev6 */
+ u16 backplanedata2; /* rev6 */
+ u16 backplanedata3; /* rev6 */
+ u16 PAD[31];
+
+ /* sprom "size" & "blank" info */
+ u16 spromstatus; /* 0x7BE, rev2 */
+ u32 PAD[464];
+
+ u16 PAD[0x80];
+};
+
+/* Register/deregister interrupt handler. */
+int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev);
+
+/* sdio device register access interface */
+u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data,
+ int *ret);
+void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
+ int *ret);
+
+/* Buffer transfer to/from device (client) core via cmd53.
+ * fn: function number
+ * flags: backplane width, address increment, sync/async
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * pkt: pointer to packet associated with buf (if any)
+ * complete: callback function for command completion (async only)
+ * handle: handle for completion callback (first arg in callback)
+ * Returns 0 or error code.
+ * NOTE: Async operation is not currently supported.
+ */
+int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq);
+int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
+
+int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt);
+int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
+int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq, uint totlen);
+
+/* Flags bits */
+
+/* Four-byte target (backplane) width (vs. two-byte) */
+#define SDIO_REQ_4BYTE 0x1
+/* Fixed address (FIFO) (vs. incrementing address) */
+#define SDIO_REQ_FIXED 0x2
+
+/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
+ * rw: read or write (0/1)
+ * addr: direct SDIO address
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * Returns 0 or error code.
+ */
+int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+ u8 *data, uint size);
+
+/* Issue an abort to the specified function */
+int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
+void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
+ enum brcmf_sdiod_state state);
+#ifdef CONFIG_PM_SLEEP
+bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev);
+#else
+static inline bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
+{
+ return false;
+}
+static inline void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
+{
+}
+static inline void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
+{
+}
+static inline void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
+{
+}
+#endif /* CONFIG_PM_SLEEP */
+
+struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdio_remove(struct brcmf_sdio *bus);
+void brcmf_sdio_isr(struct brcmf_sdio *bus);
+
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active);
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
+int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep);
+void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
+
+#endif /* BRCMFMAC_SDIO_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c
index a10f35c5eb3d..a10f35c5eb3d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h
index 4d7d51f95716..4d7d51f95716 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
new file mode 100644
index 000000000000..c72b7b352a77
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -0,0 +1,1497 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_wifi.h>
+#include "bus.h"
+#include "debug.h"
+#include "firmware.h"
+#include "usb.h"
+
+
+#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
+
+#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
+#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10
+
+#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
+ has boot up */
+#define BRCMF_USB_NRXQ 50
+#define BRCMF_USB_NTXQ 50
+
+#define BRCMF_USB_CBCTL_WRITE 0
+#define BRCMF_USB_CBCTL_READ 1
+#define BRCMF_USB_MAX_PKT_SIZE 1600
+
+BRCMF_FW_DEF(43143, "brcmfmac43143.bin");
+BRCMF_FW_DEF(43236B, "brcmfmac43236b.bin");
+BRCMF_FW_DEF(43242A, "brcmfmac43242a.bin");
+BRCMF_FW_DEF(43569, "brcmfmac43569.bin");
+
+static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = {
+ BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
+ BRCMF_FW_ENTRY(BRCM_CC_43235_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43236_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43238_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43242_CHIP_ID, 0xFFFFFFFF, 43242A),
+ BRCMF_FW_ENTRY(BRCM_CC_43566_CHIP_ID, 0xFFFFFFFF, 43569),
+ BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43569)
+};
+
+#define TRX_MAGIC 0x30524448 /* "HDR0" */
+#define TRX_MAX_OFFSET 3 /* Max number of file offsets */
+#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */
+#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */
+#define TRX_OFFSETS_DLFWLEN_IDX 0
+
+/* Control messages: bRequest values */
+#define DL_GETSTATE 0 /* returns the rdl_state_t struct */
+#define DL_CHECK_CRC 1 /* currently unused */
+#define DL_GO 2 /* execute downloaded image */
+#define DL_START 3 /* initialize dl state */
+#define DL_REBOOT 4 /* reboot the device in 2 seconds */
+#define DL_GETVER 5 /* returns the bootrom_id_t struct */
+#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset
+ * event to occur in 2 seconds. It is the
+ * responsibility of the downloaded code to
+ * clear this event
+ */
+#define DL_EXEC 7 /* jump to a supplied address */
+#define DL_RESETCFG 8 /* To support single enum on dongle
+ * - Not used by bootloader
+ */
+#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
+ * if resp unavailable
+ */
+
+/* states */
+#define DL_WAITING 0 /* waiting to rx first pkt */
+#define DL_READY 1 /* hdr was good, waiting for more of the
+ * compressed image
+ */
+#define DL_BAD_HDR 2 /* hdr was corrupted */
+#define DL_BAD_CRC 3 /* compressed image was corrupted */
+#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */
+#define DL_START_FAIL 5 /* failed to initialize correctly */
+#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM
+ * value
+ */
+#define DL_IMAGE_TOOBIG 7 /* firmware image too big */
+
+
+struct trx_header_le {
+ __le32 magic; /* "HDR0" */
+ __le32 len; /* Length of file including header */
+ __le32 crc32; /* CRC from flag_version to end of file */
+ __le32 flag_version; /* 0:15 flags, 16:31 version */
+ __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of
+ * header
+ */
+};
+
+struct rdl_state_le {
+ __le32 state;
+ __le32 bytes;
+};
+
+struct bootrom_id_le {
+ __le32 chip; /* Chip id */
+ __le32 chiprev; /* Chip rev */
+ __le32 ramsize; /* Size of RAM */
+ __le32 remapbase; /* Current remap base address */
+ __le32 boardtype; /* Type of board */
+ __le32 boardrev; /* Board revision */
+};
+
+struct brcmf_usb_image {
+ struct list_head list;
+ s8 *fwname;
+ u8 *image;
+ int image_len;
+};
+
+struct brcmf_usbdev_info {
+ struct brcmf_usbdev bus_pub; /* MUST BE FIRST */
+ spinlock_t qlock;
+ struct list_head rx_freeq;
+ struct list_head rx_postq;
+ struct list_head tx_freeq;
+ struct list_head tx_postq;
+ uint rx_pipe, tx_pipe;
+
+ int rx_low_watermark;
+ int tx_low_watermark;
+ 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;
+
+ char fw_name[BRCMF_FW_NAME_LEN];
+ const u8 *image; /* buffer for combine fw and nvram */
+ int image_len;
+
+ struct usb_device *usbdev;
+ struct device *dev;
+ struct mutex dev_init_lock;
+
+ int ctl_in_pipe, ctl_out_pipe;
+ struct urb *ctl_urb; /* URB for control endpoint */
+ struct usb_ctrlrequest ctl_write;
+ struct usb_ctrlrequest ctl_read;
+ u32 ctl_urb_actual_length;
+ int ctl_urb_status;
+ int ctl_completed;
+ wait_queue_head_t ioctl_resp_wait;
+ ulong ctl_op;
+ u8 ifnum;
+
+ struct urb *bulk_urb; /* used for FW download */
+
+ bool wowl_enabled;
+};
+
+static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
+ struct brcmf_usbreq *req);
+
+static struct brcmf_usbdev *brcmf_usb_get_buspub(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ return bus_if->bus_priv.usb;
+}
+
+static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev)
+{
+ return brcmf_usb_get_buspub(dev)->devinfo;
+}
+
+static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo)
+{
+ return wait_event_timeout(devinfo->ioctl_resp_wait,
+ devinfo->ctl_completed, IOCTL_RESP_TIMEOUT);
+}
+
+static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo)
+{
+ wake_up(&devinfo->ioctl_resp_wait);
+}
+
+static void
+brcmf_usb_ctl_complete(struct brcmf_usbdev_info *devinfo, int type, int status)
+{
+ brcmf_dbg(USB, "Enter, status=%d\n", status);
+
+ if (unlikely(devinfo == NULL))
+ return;
+
+ if (type == BRCMF_USB_CBCTL_READ) {
+ if (status == 0)
+ devinfo->bus_pub.stats.rx_ctlpkts++;
+ else
+ devinfo->bus_pub.stats.rx_ctlerrs++;
+ } else if (type == BRCMF_USB_CBCTL_WRITE) {
+ if (status == 0)
+ devinfo->bus_pub.stats.tx_ctlpkts++;
+ else
+ devinfo->bus_pub.stats.tx_ctlerrs++;
+ }
+
+ devinfo->ctl_urb_status = status;
+ devinfo->ctl_completed = true;
+ brcmf_usb_ioctl_resp_wake(devinfo);
+}
+
+static void
+brcmf_usb_ctlread_complete(struct urb *urb)
+{
+ struct brcmf_usbdev_info *devinfo =
+ (struct brcmf_usbdev_info *)urb->context;
+
+ brcmf_dbg(USB, "Enter\n");
+ devinfo->ctl_urb_actual_length = urb->actual_length;
+ brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_READ,
+ urb->status);
+}
+
+static void
+brcmf_usb_ctlwrite_complete(struct urb *urb)
+{
+ struct brcmf_usbdev_info *devinfo =
+ (struct brcmf_usbdev_info *)urb->context;
+
+ brcmf_dbg(USB, "Enter\n");
+ brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_WRITE,
+ urb->status);
+}
+
+static int
+brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
+{
+ int ret;
+ u16 size;
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo == NULL || buf == NULL ||
+ len == 0 || devinfo->ctl_urb == NULL)
+ return -EINVAL;
+
+ size = len;
+ devinfo->ctl_write.wLength = cpu_to_le16p(&size);
+ devinfo->ctl_urb->transfer_buffer_length = size;
+ devinfo->ctl_urb_status = 0;
+ devinfo->ctl_urb_actual_length = 0;
+
+ usb_fill_control_urb(devinfo->ctl_urb,
+ devinfo->usbdev,
+ devinfo->ctl_out_pipe,
+ (unsigned char *) &devinfo->ctl_write,
+ buf, size,
+ (usb_complete_t)brcmf_usb_ctlwrite_complete,
+ devinfo);
+
+ ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
+ if (ret < 0)
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+
+ return ret;
+}
+
+static int
+brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
+{
+ int ret;
+ u16 size;
+
+ brcmf_dbg(USB, "Enter\n");
+ if ((devinfo == NULL) || (buf == NULL) || (len == 0)
+ || (devinfo->ctl_urb == NULL))
+ return -EINVAL;
+
+ size = len;
+ devinfo->ctl_read.wLength = cpu_to_le16p(&size);
+ devinfo->ctl_urb->transfer_buffer_length = size;
+
+ devinfo->ctl_read.bRequestType = USB_DIR_IN
+ | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ devinfo->ctl_read.bRequest = 1;
+
+ usb_fill_control_urb(devinfo->ctl_urb,
+ devinfo->usbdev,
+ devinfo->ctl_in_pipe,
+ (unsigned char *) &devinfo->ctl_read,
+ buf, size,
+ (usb_complete_t)brcmf_usb_ctlread_complete,
+ devinfo);
+
+ ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
+ if (ret < 0)
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+
+ return ret;
+}
+
+static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len)
+{
+ int err = 0;
+ int timeout = 0;
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
+ return -EIO;
+
+ if (test_and_set_bit(0, &devinfo->ctl_op))
+ return -EIO;
+
+ devinfo->ctl_completed = false;
+ err = brcmf_usb_send_ctl(devinfo, buf, len);
+ if (err) {
+ brcmf_err("fail %d bytes: %d\n", err, len);
+ clear_bit(0, &devinfo->ctl_op);
+ return err;
+ }
+ timeout = brcmf_usb_ioctl_resp_wait(devinfo);
+ clear_bit(0, &devinfo->ctl_op);
+ if (!timeout) {
+ brcmf_err("Txctl wait timed out\n");
+ err = -EIO;
+ }
+ return err;
+}
+
+static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
+{
+ int err = 0;
+ int timeout = 0;
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
+ return -EIO;
+
+ if (test_and_set_bit(0, &devinfo->ctl_op))
+ return -EIO;
+
+ devinfo->ctl_completed = false;
+ err = brcmf_usb_recv_ctl(devinfo, buf, len);
+ if (err) {
+ brcmf_err("fail %d bytes: %d\n", err, len);
+ clear_bit(0, &devinfo->ctl_op);
+ return err;
+ }
+ timeout = brcmf_usb_ioctl_resp_wait(devinfo);
+ err = devinfo->ctl_urb_status;
+ clear_bit(0, &devinfo->ctl_op);
+ if (!timeout) {
+ brcmf_err("rxctl wait timed out\n");
+ err = -EIO;
+ }
+ if (!err)
+ return devinfo->ctl_urb_actual_length;
+ else
+ return err;
+}
+
+static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo,
+ struct list_head *q, int *counter)
+{
+ unsigned long flags;
+ struct brcmf_usbreq *req;
+ spin_lock_irqsave(&devinfo->qlock, flags);
+ if (list_empty(q)) {
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+ return NULL;
+ }
+ req = list_entry(q->next, struct brcmf_usbreq, list);
+ list_del_init(q->next);
+ if (counter)
+ (*counter)--;
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+ return req;
+
+}
+
+static void brcmf_usb_enq(struct brcmf_usbdev_info *devinfo,
+ struct list_head *q, struct brcmf_usbreq *req,
+ int *counter)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&devinfo->qlock, flags);
+ list_add_tail(&req->list, q);
+ if (counter)
+ (*counter)++;
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+}
+
+static struct brcmf_usbreq *
+brcmf_usbdev_qinit(struct list_head *q, int qsize)
+{
+ int i;
+ struct brcmf_usbreq *req, *reqs;
+
+ reqs = kcalloc(qsize, sizeof(struct brcmf_usbreq), GFP_ATOMIC);
+ if (reqs == NULL)
+ return NULL;
+
+ req = reqs;
+
+ for (i = 0; i < qsize; i++) {
+ req->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!req->urb)
+ goto fail;
+
+ INIT_LIST_HEAD(&req->list);
+ list_add_tail(&req->list, q);
+ req++;
+ }
+ return reqs;
+fail:
+ brcmf_err("fail!\n");
+ while (!list_empty(q)) {
+ req = list_entry(q->next, struct brcmf_usbreq, list);
+ if (req)
+ usb_free_urb(req->urb);
+ list_del(q->next);
+ }
+ return NULL;
+
+}
+
+static void brcmf_usb_free_q(struct list_head *q, bool pending)
+{
+ struct brcmf_usbreq *req, *next;
+ int i = 0;
+ list_for_each_entry_safe(req, next, q, list) {
+ if (!req->urb) {
+ brcmf_err("bad req\n");
+ break;
+ }
+ i++;
+ if (pending) {
+ usb_kill_urb(req->urb);
+ } else {
+ usb_free_urb(req->urb);
+ list_del_init(&req->list);
+ }
+ }
+}
+
+static void brcmf_usb_del_fromq(struct brcmf_usbdev_info *devinfo,
+ struct brcmf_usbreq *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&devinfo->qlock, flags);
+ list_del_init(&req->list);
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+}
+
+
+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);
+ brcmf_usb_del_fromq(devinfo, req);
+
+ 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)
+{
+ struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
+ struct brcmf_usbdev_info *devinfo = req->devinfo;
+ struct sk_buff *skb;
+
+ brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
+ brcmf_usb_del_fromq(devinfo, req);
+ skb = req->skb;
+ req->skb = NULL;
+
+ /* zero lenght packets indicate usb "failure". Do not refill */
+ if (urb->status != 0 || !urb->actual_length) {
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ return;
+ }
+
+ if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
+ skb_put(skb, urb->actual_length);
+ brcmf_rx_frame(devinfo->dev, skb);
+ brcmf_usb_rx_refill(devinfo, req);
+ } else {
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ }
+ return;
+
+}
+
+static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
+ struct brcmf_usbreq *req)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ if (!req || !devinfo)
+ return;
+
+ skb = dev_alloc_skb(devinfo->bus_pub.bus_mtu);
+ if (!skb) {
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ return;
+ }
+ req->skb = skb;
+
+ usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe,
+ skb->data, skb_tailroom(skb), brcmf_usb_rx_complete,
+ req);
+ req->devinfo = devinfo;
+ brcmf_usb_enq(devinfo, &devinfo->rx_postq, req, NULL);
+
+ ret = usb_submit_urb(req->urb, GFP_ATOMIC);
+ if (ret) {
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
+ req->skb = NULL;
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ }
+ return;
+}
+
+static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo)
+{
+ struct brcmf_usbreq *req;
+
+ if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
+ brcmf_err("bus is not up=%d\n", devinfo->bus_pub.state);
+ return;
+ }
+ while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL)
+ brcmf_usb_rx_refill(devinfo, req);
+}
+
+static void
+brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
+{
+ struct brcmf_bus *bcmf_bus = devinfo->bus_pub.bus;
+ int old_state;
+
+ brcmf_dbg(USB, "Enter, current state=%d, new state=%d\n",
+ devinfo->bus_pub.state, state);
+
+ if (devinfo->bus_pub.state == state)
+ return;
+
+ old_state = devinfo->bus_pub.state;
+ devinfo->bus_pub.state = state;
+
+ /* update state of upper layer */
+ if (state == BRCMFMAC_USB_STATE_DOWN) {
+ brcmf_dbg(USB, "DBUS is down\n");
+ brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN);
+ } else if (state == BRCMFMAC_USB_STATE_UP) {
+ brcmf_dbg(USB, "DBUS is up\n");
+ brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP);
+ } else {
+ brcmf_dbg(USB, "DBUS current state=%d\n", state);
+ }
+}
+
+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) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq,
+ &devinfo->tx_freecount);
+ if (!req) {
+ brcmf_err("no req to send\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ req->skb = skb;
+ req->devinfo = devinfo;
+ usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
+ skb->data, skb->len, brcmf_usb_tx_complete, req);
+ req->urb->transfer_flags |= URB_ZERO_PACKET;
+ brcmf_usb_enq(devinfo, &devinfo->tx_postq, req, NULL);
+ ret = usb_submit_urb(req->urb, GFP_ATOMIC);
+ if (ret) {
+ brcmf_err("brcmf_usb_tx usb_submit_urb FAILED\n");
+ brcmf_usb_del_fromq(devinfo, req);
+ req->skb = NULL;
+ brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req,
+ &devinfo->tx_freecount);
+ 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:
+ return ret;
+}
+
+
+static int brcmf_usb_up(struct device *dev)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
+ return 0;
+
+ /* Success, indicate devinfo is fully up */
+ brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP);
+
+ if (devinfo->ctl_urb) {
+ devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
+ devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
+
+ /* CTL Write */
+ devinfo->ctl_write.bRequestType =
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ devinfo->ctl_write.bRequest = 0;
+ devinfo->ctl_write.wValue = cpu_to_le16(0);
+ devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum);
+
+ /* CTL Read */
+ devinfo->ctl_read.bRequestType =
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ devinfo->ctl_read.bRequest = 1;
+ devinfo->ctl_read.wValue = cpu_to_le16(0);
+ devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
+ }
+ brcmf_usb_rx_fill_all(devinfo);
+ return 0;
+}
+
+static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo)
+{
+ if (devinfo->ctl_urb)
+ usb_kill_urb(devinfo->ctl_urb);
+ if (devinfo->bulk_urb)
+ usb_kill_urb(devinfo->bulk_urb);
+ brcmf_usb_free_q(&devinfo->tx_postq, true);
+ brcmf_usb_free_q(&devinfo->rx_postq, true);
+}
+
+static void brcmf_usb_down(struct device *dev)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo == NULL)
+ return;
+
+ if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN)
+ return;
+
+ brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
+
+ brcmf_cancel_all_urbs(devinfo);
+}
+
+static void
+brcmf_usb_sync_complete(struct urb *urb)
+{
+ struct brcmf_usbdev_info *devinfo =
+ (struct brcmf_usbdev_info *)urb->context;
+
+ devinfo->ctl_completed = true;
+ brcmf_usb_ioctl_resp_wake(devinfo);
+}
+
+static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
+ void *buffer, int buflen)
+{
+ int ret;
+ char *tmpbuf;
+ u16 size;
+
+ if ((!devinfo) || (devinfo->ctl_urb == NULL))
+ return -EINVAL;
+
+ tmpbuf = kmalloc(buflen, GFP_ATOMIC);
+ if (!tmpbuf)
+ return -ENOMEM;
+
+ size = buflen;
+ devinfo->ctl_urb->transfer_buffer_length = size;
+
+ devinfo->ctl_read.wLength = cpu_to_le16p(&size);
+ devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE;
+ devinfo->ctl_read.bRequest = cmd;
+
+ usb_fill_control_urb(devinfo->ctl_urb,
+ devinfo->usbdev,
+ usb_rcvctrlpipe(devinfo->usbdev, 0),
+ (unsigned char *) &devinfo->ctl_read,
+ (void *) tmpbuf, size,
+ (usb_complete_t)brcmf_usb_sync_complete, devinfo);
+
+ devinfo->ctl_completed = false;
+ ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
+ if (ret < 0) {
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+ goto finalize;
+ }
+
+ if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
+ usb_kill_urb(devinfo->ctl_urb);
+ ret = -ETIMEDOUT;
+ } else {
+ memcpy(buffer, tmpbuf, buflen);
+ }
+
+finalize:
+ kfree(tmpbuf);
+ return ret;
+}
+
+static bool
+brcmf_usb_dlneeded(struct brcmf_usbdev_info *devinfo)
+{
+ struct bootrom_id_le id;
+ u32 chipid, chiprev;
+
+ brcmf_dbg(USB, "Enter\n");
+
+ if (devinfo == NULL)
+ return false;
+
+ /* Check if firmware downloaded already by querying runtime ID */
+ id.chip = cpu_to_le32(0xDEAD);
+ brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+
+ chipid = le32_to_cpu(id.chip);
+ chiprev = le32_to_cpu(id.chiprev);
+
+ if ((chipid & 0x4300) == 0x4300)
+ brcmf_dbg(USB, "chip %x rev 0x%x\n", chipid, chiprev);
+ else
+ brcmf_dbg(USB, "chip %d rev 0x%x\n", chipid, chiprev);
+ if (chipid == BRCMF_POSTBOOT_ID) {
+ brcmf_dbg(USB, "firmware already downloaded\n");
+ brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id));
+ return false;
+ } else {
+ devinfo->bus_pub.devid = chipid;
+ devinfo->bus_pub.chiprev = chiprev;
+ }
+ return true;
+}
+
+static int
+brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
+{
+ struct bootrom_id_le id;
+ u32 loop_cnt;
+ int err;
+
+ brcmf_dbg(USB, "Enter\n");
+
+ loop_cnt = 0;
+ do {
+ mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT);
+ loop_cnt++;
+ id.chip = cpu_to_le32(0xDEAD); /* Get the ID */
+ err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+ if ((err) && (err != -ETIMEDOUT))
+ return err;
+ if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID))
+ break;
+ } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT);
+
+ if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) {
+ brcmf_dbg(USB, "postboot chip 0x%x/rev 0x%x\n",
+ le32_to_cpu(id.chip), le32_to_cpu(id.chiprev));
+
+ brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id));
+ return 0;
+ } else {
+ brcmf_err("Cannot talk to Dongle. Firmware is not UP, %d ms\n",
+ BRCMF_USB_RESET_GETVER_SPINWAIT * loop_cnt);
+ return -EINVAL;
+ }
+}
+
+
+static int
+brcmf_usb_dl_send_bulk(struct brcmf_usbdev_info *devinfo, void *buffer, int len)
+{
+ int ret;
+
+ if ((devinfo == NULL) || (devinfo->bulk_urb == NULL))
+ return -EINVAL;
+
+ /* Prepare the URB */
+ usb_fill_bulk_urb(devinfo->bulk_urb, devinfo->usbdev,
+ devinfo->tx_pipe, buffer, len,
+ (usb_complete_t)brcmf_usb_sync_complete, devinfo);
+
+ devinfo->bulk_urb->transfer_flags |= URB_ZERO_PACKET;
+
+ devinfo->ctl_completed = false;
+ ret = usb_submit_urb(devinfo->bulk_urb, GFP_ATOMIC);
+ if (ret) {
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+ return ret;
+ }
+ ret = brcmf_usb_ioctl_resp_wait(devinfo);
+ return (ret == 0);
+}
+
+static int
+brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
+{
+ unsigned int sendlen, sent, dllen;
+ char *bulkchunk = NULL, *dlpos;
+ struct rdl_state_le state;
+ u32 rdlstate, rdlbytes;
+ int err = 0;
+
+ brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen);
+
+ bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC);
+ if (bulkchunk == NULL) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* 1) Prepare USB boot loader for runtime image */
+ brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
+
+ rdlstate = le32_to_cpu(state.state);
+ rdlbytes = le32_to_cpu(state.bytes);
+
+ /* 2) Check we are in the Waiting state */
+ if (rdlstate != DL_WAITING) {
+ brcmf_err("Failed to DL_START\n");
+ err = -EINVAL;
+ goto fail;
+ }
+ sent = 0;
+ dlpos = fw;
+ dllen = fwlen;
+
+ /* Get chip id and rev */
+ while (rdlbytes != dllen) {
+ /* Wait until the usb device reports it received all
+ * the bytes we sent */
+ if ((rdlbytes == sent) && (rdlbytes != dllen)) {
+ if ((dllen-sent) < TRX_RDL_CHUNK)
+ sendlen = dllen-sent;
+ else
+ sendlen = TRX_RDL_CHUNK;
+
+ /* simply avoid having to send a ZLP by ensuring we
+ * never have an even
+ * multiple of 64
+ */
+ if (!(sendlen % 64))
+ sendlen -= 4;
+
+ /* send data */
+ memcpy(bulkchunk, dlpos, sendlen);
+ if (brcmf_usb_dl_send_bulk(devinfo, bulkchunk,
+ sendlen)) {
+ brcmf_err("send_bulk failed\n");
+ err = -EINVAL;
+ goto fail;
+ }
+
+ dlpos += sendlen;
+ sent += sendlen;
+ }
+ err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
+ sizeof(state));
+ if (err) {
+ brcmf_err("DL_GETSTATE Failed\n");
+ goto fail;
+ }
+
+ rdlstate = le32_to_cpu(state.state);
+ rdlbytes = le32_to_cpu(state.bytes);
+
+ /* restart if an error is reported */
+ if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) {
+ brcmf_err("Bad Hdr or Bad CRC state %d\n",
+ rdlstate);
+ err = -EINVAL;
+ goto fail;
+ }
+ }
+
+fail:
+ kfree(bulkchunk);
+ brcmf_dbg(USB, "Exit, err=%d\n", err);
+ return err;
+}
+
+static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len)
+{
+ int err;
+
+ brcmf_dbg(USB, "Enter\n");
+
+ if (devinfo == NULL)
+ return -EINVAL;
+
+ if (devinfo->bus_pub.devid == 0xDEAD)
+ return -EINVAL;
+
+ err = brcmf_usb_dl_writeimage(devinfo, fw, len);
+ if (err == 0)
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE;
+ else
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_FAIL;
+ brcmf_dbg(USB, "Exit, err=%d\n", err);
+
+ return err;
+}
+
+static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
+{
+ struct rdl_state_le state;
+
+ brcmf_dbg(USB, "Enter\n");
+ if (!devinfo)
+ return -EINVAL;
+
+ if (devinfo->bus_pub.devid == 0xDEAD)
+ return -EINVAL;
+
+ /* Check we are runnable */
+ state.state = 0;
+ brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state));
+
+ /* Start the image */
+ if (state.state == cpu_to_le32(DL_RUNNABLE)) {
+ if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state)))
+ return -ENODEV;
+ if (brcmf_usb_resetcfg(devinfo))
+ return -ENODEV;
+ /* The Dongle may go for re-enumeration. */
+ } else {
+ brcmf_err("Dongle not runnable\n");
+ return -EINVAL;
+ }
+ brcmf_dbg(USB, "Exit\n");
+ return 0;
+}
+
+static int
+brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
+{
+ int err;
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo == NULL)
+ return -ENODEV;
+
+ if (!devinfo->image) {
+ brcmf_err("No firmware!\n");
+ return -ENOENT;
+ }
+
+ err = brcmf_usb_dlstart(devinfo,
+ (u8 *)devinfo->image, devinfo->image_len);
+ if (err == 0)
+ err = brcmf_usb_dlrun(devinfo);
+ return err;
+}
+
+
+static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo)
+{
+ brcmf_dbg(USB, "Enter, devinfo %p\n", devinfo);
+
+ /* free the URBS */
+ brcmf_usb_free_q(&devinfo->rx_freeq, false);
+ brcmf_usb_free_q(&devinfo->tx_freeq, false);
+
+ usb_free_urb(devinfo->ctl_urb);
+ usb_free_urb(devinfo->bulk_urb);
+
+ kfree(devinfo->tx_reqs);
+ kfree(devinfo->rx_reqs);
+}
+
+
+static int check_file(const u8 *headers)
+{
+ struct trx_header_le *trx;
+ int actual_len = -1;
+
+ brcmf_dbg(USB, "Enter\n");
+ /* Extract trx header */
+ trx = (struct trx_header_le *) headers;
+ if (trx->magic != cpu_to_le32(TRX_MAGIC))
+ return -1;
+
+ headers += sizeof(struct trx_header_le);
+
+ if (le32_to_cpu(trx->flag_version) & TRX_UNCOMP_IMAGE) {
+ actual_len = le32_to_cpu(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]);
+ return actual_len + sizeof(struct trx_header_le);
+ }
+ return -1;
+}
+
+
+static
+struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
+ int nrxq, int ntxq)
+{
+ brcmf_dbg(USB, "Enter\n");
+
+ devinfo->bus_pub.nrxq = nrxq;
+ devinfo->rx_low_watermark = nrxq / 2;
+ devinfo->bus_pub.devinfo = devinfo;
+ devinfo->bus_pub.ntxq = ntxq;
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN;
+
+ /* flow control when too many tx urbs posted */
+ devinfo->tx_low_watermark = ntxq / 4;
+ devinfo->tx_high_watermark = devinfo->tx_low_watermark * 3;
+ devinfo->bus_pub.bus_mtu = BRCMF_USB_MAX_PKT_SIZE;
+
+ /* Initialize other structure content */
+ init_waitqueue_head(&devinfo->ioctl_resp_wait);
+
+ /* 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);
+
+ INIT_LIST_HEAD(&devinfo->tx_freeq);
+ INIT_LIST_HEAD(&devinfo->tx_postq);
+
+ devinfo->tx_flowblock = false;
+
+ devinfo->rx_reqs = brcmf_usbdev_qinit(&devinfo->rx_freeq, nrxq);
+ if (!devinfo->rx_reqs)
+ goto error;
+
+ devinfo->tx_reqs = brcmf_usbdev_qinit(&devinfo->tx_freeq, ntxq);
+ if (!devinfo->tx_reqs)
+ goto error;
+ devinfo->tx_freecount = ntxq;
+
+ devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!devinfo->ctl_urb) {
+ brcmf_err("usb_alloc_urb (ctl) failed\n");
+ goto error;
+ }
+ devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!devinfo->bulk_urb) {
+ brcmf_err("usb_alloc_urb (bulk) failed\n");
+ goto error;
+ }
+
+ return &devinfo->bus_pub;
+
+error:
+ brcmf_err("failed!\n");
+ brcmf_usb_detach(devinfo);
+ return NULL;
+}
+
+static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled);
+ devinfo->wowl_enabled = enabled;
+ if (enabled)
+ device_set_wakeup_enable(devinfo->dev, true);
+ else
+ device_set_wakeup_enable(devinfo->dev, false);
+}
+
+static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
+ .txdata = brcmf_usb_tx,
+ .stop = brcmf_usb_down,
+ .txctl = brcmf_usb_tx_ctlpkt,
+ .rxctl = brcmf_usb_rx_ctlpkt,
+ .wowl_config = brcmf_usb_wowl_config,
+};
+
+static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
+{
+ int ret;
+
+ /* Attach to the common driver interface */
+ ret = brcmf_attach(devinfo->dev);
+ if (ret) {
+ brcmf_err("brcmf_attach failed\n");
+ return ret;
+ }
+
+ ret = brcmf_usb_up(devinfo->dev);
+ if (ret)
+ goto fail;
+
+ ret = brcmf_bus_start(devinfo->dev);
+ if (ret)
+ goto fail;
+
+ return 0;
+fail:
+ brcmf_detach(devinfo->dev);
+ return ret;
+}
+
+static void brcmf_usb_probe_phase2(struct device *dev,
+ const struct firmware *fw,
+ void *nvram, u32 nvlen)
+{
+ struct brcmf_bus *bus = dev_get_drvdata(dev);
+ struct brcmf_usbdev_info *devinfo;
+ int ret;
+
+ brcmf_dbg(USB, "Start fw downloading\n");
+
+ devinfo = bus->bus_priv.usb->devinfo;
+ ret = check_file(fw->data);
+ if (ret < 0) {
+ brcmf_err("invalid firmware\n");
+ release_firmware(fw);
+ goto error;
+ }
+
+ devinfo->image = fw->data;
+ devinfo->image_len = fw->size;
+
+ ret = brcmf_usb_fw_download(devinfo);
+ release_firmware(fw);
+ if (ret)
+ goto error;
+
+ ret = brcmf_usb_bus_setup(devinfo);
+ if (ret)
+ goto error;
+
+ mutex_unlock(&devinfo->dev_init_lock);
+ return;
+error:
+ brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
+ mutex_unlock(&devinfo->dev_init_lock);
+ device_release_driver(dev);
+}
+
+static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
+{
+ struct brcmf_bus *bus = NULL;
+ struct brcmf_usbdev *bus_pub = NULL;
+ struct device *dev = devinfo->dev;
+ int ret;
+
+ brcmf_dbg(USB, "Enter\n");
+ bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ);
+ if (!bus_pub)
+ return -ENODEV;
+
+ bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC);
+ if (!bus) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ bus->dev = dev;
+ bus_pub->bus = bus;
+ bus->bus_priv.usb = bus_pub;
+ dev_set_drvdata(dev, bus);
+ bus->ops = &brcmf_usb_bus_ops;
+ bus->proto_type = BRCMF_PROTO_BCDC;
+ bus->always_use_fws_queue = true;
+#ifdef CONFIG_PM
+ bus->wowl_supported = true;
+#endif
+
+ if (!brcmf_usb_dlneeded(devinfo)) {
+ ret = brcmf_usb_bus_setup(devinfo);
+ if (ret)
+ goto fail;
+ /* we are done */
+ mutex_unlock(&devinfo->dev_init_lock);
+ return 0;
+ }
+ bus->chip = bus_pub->devid;
+ bus->chiprev = bus_pub->chiprev;
+
+ ret = brcmf_fw_map_chip_to_name(bus_pub->devid, bus_pub->chiprev,
+ brcmf_usb_fwnames,
+ ARRAY_SIZE(brcmf_usb_fwnames),
+ devinfo->fw_name, NULL);
+ if (ret)
+ goto fail;
+
+ /* request firmware here */
+ ret = brcmf_fw_get_firmwares(dev, 0, devinfo->fw_name, NULL,
+ brcmf_usb_probe_phase2);
+ if (ret) {
+ brcmf_err("firmware request failed: %d\n", ret);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ /* Release resources in reverse order */
+ kfree(bus);
+ brcmf_usb_detach(devinfo);
+ return ret;
+}
+
+static void
+brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
+{
+ if (!devinfo)
+ return;
+ brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo);
+
+ brcmf_detach(devinfo->dev);
+ kfree(devinfo->bus_pub.bus);
+ brcmf_usb_detach(devinfo);
+}
+
+static int
+brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo;
+ struct usb_interface_descriptor *desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int ret = 0;
+ u32 num_of_eps;
+ u8 endpoint_num, ep;
+
+ brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct);
+
+ devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC);
+ if (devinfo == NULL)
+ return -ENOMEM;
+
+ devinfo->usbdev = usb;
+ devinfo->dev = &usb->dev;
+ /* Take an init lock, to protect for disconnect while still loading.
+ * Necessary because of the asynchronous firmware load construction
+ */
+ mutex_init(&devinfo->dev_init_lock);
+ mutex_lock(&devinfo->dev_init_lock);
+
+ usb_set_intfdata(intf, devinfo);
+
+ /* Check that the device supports only one configuration */
+ if (usb->descriptor.bNumConfigurations != 1) {
+ brcmf_err("Number of configurations: %d not supported\n",
+ usb->descriptor.bNumConfigurations);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) &&
+ (usb->descriptor.bDeviceClass != USB_CLASS_MISC) &&
+ (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) {
+ brcmf_err("Device class: 0x%x not supported\n",
+ usb->descriptor.bDeviceClass);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ desc = &intf->altsetting[0].desc;
+ if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+ (desc->bInterfaceSubClass != 2) ||
+ (desc->bInterfaceProtocol != 0xff)) {
+ brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
+ desc->bInterfaceNumber, desc->bInterfaceClass,
+ desc->bInterfaceSubClass, desc->bInterfaceProtocol);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ num_of_eps = desc->bNumEndpoints;
+ for (ep = 0; ep < num_of_eps; ep++) {
+ endpoint = &intf->altsetting[0].endpoint[ep].desc;
+ endpoint_num = usb_endpoint_num(endpoint);
+ if (!usb_endpoint_xfer_bulk(endpoint))
+ continue;
+ if (usb_endpoint_dir_in(endpoint)) {
+ if (!devinfo->rx_pipe)
+ devinfo->rx_pipe =
+ usb_rcvbulkpipe(usb, endpoint_num);
+ } else {
+ if (!devinfo->tx_pipe)
+ devinfo->tx_pipe =
+ usb_sndbulkpipe(usb, endpoint_num);
+ }
+ }
+ if (devinfo->rx_pipe == 0) {
+ brcmf_err("No RX (in) Bulk EP found\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+ if (devinfo->tx_pipe == 0) {
+ brcmf_err("No TX (out) Bulk EP found\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ devinfo->ifnum = desc->bInterfaceNumber;
+
+ if (usb->speed == USB_SPEED_SUPER)
+ brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n");
+ else if (usb->speed == USB_SPEED_HIGH)
+ brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n");
+ else
+ brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n");
+
+ ret = brcmf_usb_probe_cb(devinfo);
+ if (ret)
+ goto fail;
+
+ /* Success */
+ return 0;
+
+fail:
+ mutex_unlock(&devinfo->dev_init_lock);
+ kfree(devinfo);
+ usb_set_intfdata(intf, NULL);
+ return ret;
+}
+
+static void
+brcmf_usb_disconnect(struct usb_interface *intf)
+{
+ struct brcmf_usbdev_info *devinfo;
+
+ brcmf_dbg(USB, "Enter\n");
+ devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
+
+ if (devinfo) {
+ mutex_lock(&devinfo->dev_init_lock);
+ /* Make sure that devinfo still exists. Firmware probe routines
+ * may have released the device and cleared the intfdata.
+ */
+ if (!usb_get_intfdata(intf))
+ goto done;
+
+ brcmf_usb_disconnect_cb(devinfo);
+ kfree(devinfo);
+ }
+done:
+ brcmf_dbg(USB, "Exit\n");
+}
+
+/*
+ * only need to signal the bus being down and update the state.
+ */
+static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
+ if (devinfo->wowl_enabled)
+ brcmf_cancel_all_urbs(devinfo);
+ else
+ brcmf_detach(&usb->dev);
+ return 0;
+}
+
+/*
+ * (re-) start the bus.
+ */
+static int brcmf_usb_resume(struct usb_interface *intf)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (!devinfo->wowl_enabled)
+ return brcmf_usb_bus_setup(devinfo);
+
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP;
+ brcmf_usb_rx_fill_all(devinfo);
+ return 0;
+}
+
+static int brcmf_usb_reset_resume(struct usb_interface *intf)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
+ brcmf_dbg(USB, "Enter\n");
+
+ return brcmf_fw_get_firmwares(&usb->dev, 0, devinfo->fw_name, NULL,
+ brcmf_usb_probe_phase2);
+}
+
+#define BRCMF_USB_DEVICE(dev_id) \
+ { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) }
+
+static struct usb_device_id brcmf_usb_devid_table[] = {
+ BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID),
+ { USB_DEVICE(BRCM_USB_VENDOR_ID_LG, BRCM_USB_43242_LG_DEVICE_ID) },
+ /* special entry for device with firmware loaded and running */
+ BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
+ { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
+
+static struct usb_driver brcmf_usbdrvr = {
+ .name = KBUILD_MODNAME,
+ .probe = brcmf_usb_probe,
+ .disconnect = brcmf_usb_disconnect,
+ .id_table = brcmf_usb_devid_table,
+ .suspend = brcmf_usb_suspend,
+ .resume = brcmf_usb_resume,
+ .reset_resume = brcmf_usb_reset_resume,
+ .disable_hub_initiated_lpm = 1,
+};
+
+static int brcmf_usb_reset_device(struct device *dev, void *notused)
+{
+ /* device past is the usb interface so we
+ * need to use parent here.
+ */
+ brcmf_dev_reset(dev->parent);
+ return 0;
+}
+
+void brcmf_usb_exit(void)
+{
+ struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
+ int ret;
+
+ brcmf_dbg(USB, "Enter\n");
+ ret = driver_for_each_device(drv, NULL, NULL,
+ brcmf_usb_reset_device);
+ usb_deregister(&brcmf_usbdrvr);
+}
+
+void brcmf_usb_register(void)
+{
+ brcmf_dbg(USB, "Enter\n");
+ usb_register(&brcmf_usbdrvr);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h
index f483a8c9945b..f483a8c9945b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
index 8eff2753abad..8eff2753abad 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
index 061b7bfa2e1c..061b7bfa2e1c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
new file mode 100644
index 000000000000..960e6b86bbcb
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
@@ -0,0 +1,48 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver
+#
+# Copyright (c) 2010 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ccflags-y := \
+ -D__CHECK_ENDIAN__ \
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac \
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy \
+ -Idrivers/net/wireless/broadcom/brcm80211/include
+
+brcmsmac-y := \
+ mac80211_if.o \
+ ucode_loader.o \
+ ampdu.o \
+ antsel.o \
+ channel.o \
+ main.o \
+ phy_shim.o \
+ pmu.o \
+ rate.o \
+ stf.o \
+ aiutils.o \
+ phy/phy_cmn.o \
+ phy/phy_lcn.o \
+ phy/phy_n.o \
+ phy/phytbl_lcn.o \
+ phy/phytbl_n.o \
+ phy/phy_qmath.o \
+ dma.o \
+ brcms_trace_events.o \
+ debug.o
+
+brcmsmac-$(CONFIG_BCMA_DRIVER_GPIO) += led.o
+
+obj-$(CONFIG_BRCMSMAC) += brcmsmac.o
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c
index 53365977bfd6..53365977bfd6 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h
index 2d08c155c23b..2d08c155c23b 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c
index fa391e4eb098..fa391e4eb098 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h
index 03bdcf29bd50..03bdcf29bd50 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c
index 54c616919590..54c616919590 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h
index a3d487ab1964..a3d487ab1964 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
index a0da3248b942..a0da3248b942 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
index 0e8a69ab909f..0e8a69ab909f 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
index cf2cc070f1e5..cf2cc070f1e5 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c
index 52fc9eeb5fa5..52fc9eeb5fa5 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h
index cbf2f06436fc..cbf2f06436fc 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
new file mode 100644
index 000000000000..38bd5890bd53
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
@@ -0,0 +1,774 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <net/regulatory.h>
+
+#include <defs.h>
+#include "pub.h"
+#include "phy/phy_hal.h"
+#include "main.h"
+#include "stf.h"
+#include "channel.h"
+#include "mac80211_if.h"
+#include "debug.h"
+
+/* QDB() macro takes a dB value and converts to a quarter dB value */
+#define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR)
+
+#define LOCALE_MIMO_IDX_bn 0
+#define LOCALE_MIMO_IDX_11n 0
+
+/* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */
+#define BRCMS_MAXPWR_MIMO_TBL_SIZE 14
+
+/* maxpwr mapping to 5GHz band channels:
+ * maxpwr[0] - channels [34-48]
+ * maxpwr[1] - channels [52-60]
+ * maxpwr[2] - channels [62-64]
+ * maxpwr[3] - channels [100-140]
+ * maxpwr[4] - channels [149-165]
+ */
+#define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */
+
+#define LC(id) LOCALE_MIMO_IDX_ ## id
+
+#define LOCALES(mimo2, mimo5) \
+ {LC(mimo2), LC(mimo5)}
+
+/* macro to get 5 GHz channel group index for tx power */
+#define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \
+ (((c) < 62) ? 1 : \
+ (((c) < 100) ? 2 : \
+ (((c) < 149) ? 3 : 4))))
+
+#define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0)
+#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \
+ NL80211_RRF_NO_IR)
+
+#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \
+ NL80211_RRF_NO_IR)
+#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \
+ NL80211_RRF_DFS | \
+ NL80211_RRF_NO_IR)
+#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \
+ NL80211_RRF_DFS | \
+ NL80211_RRF_NO_IR)
+#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \
+ NL80211_RRF_NO_IR)
+
+static const struct ieee80211_regdomain brcms_regdom_x2 = {
+ .n_reg_rules = 6,
+ .alpha2 = "X2",
+ .reg_rules = {
+ BRCM_2GHZ_2412_2462,
+ BRCM_2GHZ_2467_2472,
+ BRCM_5GHZ_5180_5240,
+ BRCM_5GHZ_5260_5320,
+ BRCM_5GHZ_5500_5700,
+ BRCM_5GHZ_5745_5825,
+ }
+};
+
+ /* locale per-channel tx power limits for MIMO frames
+ * maxpwr arrays are index by channel for 2.4 GHz limits, and
+ * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel)
+ */
+struct locale_mimo_info {
+ /* tx 20 MHz power limits, qdBm units */
+ s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE];
+ /* tx 40 MHz power limits, qdBm units */
+ s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE];
+};
+
+/* Country names and abbreviations with locale defined from ISO 3166 */
+struct country_info {
+ const u8 locale_mimo_2G; /* 2.4G mimo info */
+ const u8 locale_mimo_5G; /* 5G mimo info */
+};
+
+struct brcms_regd {
+ struct country_info country;
+ const struct ieee80211_regdomain *regdomain;
+};
+
+struct brcms_cm_info {
+ struct brcms_pub *pub;
+ struct brcms_c_info *wlc;
+ const struct brcms_regd *world_regd;
+};
+
+/*
+ * MIMO Locale Definitions - 2.4 GHz
+ */
+static const struct locale_mimo_info locale_bn = {
+ {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+ QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+ QDB(13), QDB(13), QDB(13)},
+ {0, 0, QDB(13), QDB(13), QDB(13),
+ QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+ QDB(13), 0, 0},
+};
+
+static const struct locale_mimo_info *g_mimo_2g_table[] = {
+ &locale_bn
+};
+
+/*
+ * MIMO Locale Definitions - 5 GHz
+ */
+static const struct locale_mimo_info locale_11n = {
+ { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)},
+ {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)},
+};
+
+static const struct locale_mimo_info *g_mimo_5g_table[] = {
+ &locale_11n
+};
+
+static const struct brcms_regd cntry_locales[] = {
+ /* Worldwide RoW 2, must always be at index 0 */
+ {
+ .country = LOCALES(bn, 11n),
+ .regdomain = &brcms_regdom_x2,
+ },
+};
+
+static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx)
+{
+ if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table))
+ return NULL;
+
+ return g_mimo_2g_table[locale_idx];
+}
+
+static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx)
+{
+ if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table))
+ return NULL;
+
+ return g_mimo_5g_table[locale_idx];
+}
+
+/*
+ * Indicates whether the country provided is valid to pass
+ * to cfg80211 or not.
+ *
+ * returns true if valid; false if not.
+ */
+static bool brcms_c_country_valid(const char *ccode)
+{
+ /*
+ * only allow ascii alpha uppercase for the first 2
+ * chars.
+ */
+ if (!((ccode[0] & 0x80) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A &&
+ (ccode[1] & 0x80) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A))
+ return false;
+
+ /*
+ * do not match ISO 3166-1 user assigned country codes
+ * that may be in the driver table
+ */
+ if (!strcmp("AA", ccode) || /* AA */
+ !strcmp("ZZ", ccode) || /* ZZ */
+ ccode[0] == 'X' || /* XA - XZ */
+ (ccode[0] == 'Q' && /* QM - QZ */
+ (ccode[1] >= 'M' && ccode[1] <= 'Z')))
+ return false;
+
+ if (!strcmp("NA", ccode))
+ return false;
+
+ return true;
+}
+
+static const struct brcms_regd *brcms_world_regd(const char *regdom, int len)
+{
+ const struct brcms_regd *regd = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) {
+ if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) {
+ regd = &cntry_locales[i];
+ break;
+ }
+ }
+
+ return regd;
+}
+
+static const struct brcms_regd *brcms_default_world_regd(void)
+{
+ return &cntry_locales[0];
+}
+
+/* JP, J1 - J10 are Japan ccodes */
+static bool brcms_c_japan_ccode(const char *ccode)
+{
+ return (ccode[0] == 'J' &&
+ (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
+}
+
+static void
+brcms_c_channel_min_txpower_limits_with_local_constraint(
+ struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr,
+ u8 local_constraint_qdbm)
+{
+ int j;
+
+ /* CCK Rates */
+ for (j = 0; j < WL_TX_POWER_CCK_NUM; j++)
+ txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm);
+
+ /* 20 MHz Legacy OFDM SISO */
+ for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++)
+ txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm);
+
+ /* 20 MHz Legacy OFDM CDD */
+ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
+ txpwr->ofdm_cdd[j] =
+ min(txpwr->ofdm_cdd[j], local_constraint_qdbm);
+
+ /* 40 MHz Legacy OFDM SISO */
+ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
+ txpwr->ofdm_40_siso[j] =
+ min(txpwr->ofdm_40_siso[j], local_constraint_qdbm);
+
+ /* 40 MHz Legacy OFDM CDD */
+ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
+ txpwr->ofdm_40_cdd[j] =
+ min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm);
+
+ /* 20MHz MCS 0-7 SISO */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
+ txpwr->mcs_20_siso[j] =
+ min(txpwr->mcs_20_siso[j], local_constraint_qdbm);
+
+ /* 20MHz MCS 0-7 CDD */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
+ txpwr->mcs_20_cdd[j] =
+ min(txpwr->mcs_20_cdd[j], local_constraint_qdbm);
+
+ /* 20MHz MCS 0-7 STBC */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
+ txpwr->mcs_20_stbc[j] =
+ min(txpwr->mcs_20_stbc[j], local_constraint_qdbm);
+
+ /* 20MHz MCS 8-15 MIMO */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
+ txpwr->mcs_20_mimo[j] =
+ min(txpwr->mcs_20_mimo[j], local_constraint_qdbm);
+
+ /* 40MHz MCS 0-7 SISO */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
+ txpwr->mcs_40_siso[j] =
+ min(txpwr->mcs_40_siso[j], local_constraint_qdbm);
+
+ /* 40MHz MCS 0-7 CDD */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
+ txpwr->mcs_40_cdd[j] =
+ min(txpwr->mcs_40_cdd[j], local_constraint_qdbm);
+
+ /* 40MHz MCS 0-7 STBC */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
+ txpwr->mcs_40_stbc[j] =
+ min(txpwr->mcs_40_stbc[j], local_constraint_qdbm);
+
+ /* 40MHz MCS 8-15 MIMO */
+ for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
+ txpwr->mcs_40_mimo[j] =
+ min(txpwr->mcs_40_mimo[j], local_constraint_qdbm);
+
+ /* 40MHz MCS 32 */
+ txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm);
+
+}
+
+/*
+ * set the driver's current country and regulatory information
+ * using a country code as the source. Look up built in country
+ * information found with the country code.
+ */
+static void
+brcms_c_set_country(struct brcms_cm_info *wlc_cm,
+ const struct brcms_regd *regd)
+{
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+
+ if ((wlc->pub->_n_enab & SUPPORT_11N) !=
+ wlc->protection->nmode_user)
+ brcms_c_set_nmode(wlc);
+
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+ brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+
+ brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
+
+ return;
+}
+
+struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc)
+{
+ struct brcms_cm_info *wlc_cm;
+ struct brcms_pub *pub = wlc->pub;
+ struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom;
+ const char *ccode = sprom->alpha2;
+ int ccode_len = sizeof(sprom->alpha2);
+
+ wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC);
+ if (wlc_cm == NULL)
+ return NULL;
+ wlc_cm->pub = pub;
+ wlc_cm->wlc = wlc;
+ wlc->cmi = wlc_cm;
+
+ /* store the country code for passing up as a regulatory hint */
+ wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len);
+ if (brcms_c_country_valid(ccode))
+ strncpy(wlc->pub->srom_ccode, ccode, ccode_len);
+
+ /*
+ * If no custom world domain is found in the SROM, use the
+ * default "X2" domain.
+ */
+ if (!wlc_cm->world_regd) {
+ wlc_cm->world_regd = brcms_default_world_regd();
+ ccode = wlc_cm->world_regd->regdomain->alpha2;
+ ccode_len = BRCM_CNTRY_BUF_SZ - 1;
+ }
+
+ /* save default country for exiting 11d regulatory mode */
+ strncpy(wlc->country_default, ccode, ccode_len);
+
+ /* initialize autocountry_default to driver default */
+ strncpy(wlc->autocountry_default, ccode, ccode_len);
+
+ brcms_c_set_country(wlc_cm, wlc_cm->world_regd);
+
+ return wlc_cm;
+}
+
+void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm)
+{
+ kfree(wlc_cm);
+}
+
+void
+brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
+ u8 local_constraint_qdbm)
+{
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+ struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan;
+ struct txpwr_limits txpwr;
+
+ brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
+
+ brcms_c_channel_min_txpower_limits_with_local_constraint(
+ wlc_cm, &txpwr, local_constraint_qdbm
+ );
+
+ /* set or restore gmode as required by regulatory */
+ if (ch->flags & IEEE80211_CHAN_NO_OFDM)
+ brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
+ else
+ brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
+
+ brcms_b_set_chanspec(wlc->hw, chanspec,
+ !!(ch->flags & IEEE80211_CHAN_NO_IR),
+ &txpwr);
+}
+
+void
+brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec,
+ struct txpwr_limits *txpwr)
+{
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+ struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan;
+ uint i;
+ uint chan;
+ int maxpwr;
+ int delta;
+ const struct country_info *country;
+ struct brcms_band *band;
+ int conducted_max = BRCMS_TXPWR_MAX;
+ const struct locale_mimo_info *li_mimo;
+ int maxpwr20, maxpwr40;
+ int maxpwr_idx;
+ uint j;
+
+ memset(txpwr, 0, sizeof(struct txpwr_limits));
+
+ if (WARN_ON(!ch))
+ return;
+
+ country = &wlc_cm->world_regd->country;
+
+ chan = CHSPEC_CHANNEL(chanspec);
+ band = wlc->bandstate[chspec_bandunit(chanspec)];
+ li_mimo = (band->bandtype == BRCM_BAND_5G) ?
+ brcms_c_get_mimo_5g(country->locale_mimo_5G) :
+ brcms_c_get_mimo_2g(country->locale_mimo_2G);
+
+ delta = band->antgain;
+
+ if (band->bandtype == BRCM_BAND_2G)
+ conducted_max = QDB(22);
+
+ maxpwr = QDB(ch->max_power) - delta;
+ maxpwr = max(maxpwr, 0);
+ maxpwr = min(maxpwr, conducted_max);
+
+ /* CCK txpwr limits for 2.4G band */
+ if (band->bandtype == BRCM_BAND_2G) {
+ for (i = 0; i < BRCMS_NUM_RATES_CCK; i++)
+ txpwr->cck[i] = (u8) maxpwr;
+ }
+
+ for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
+ txpwr->ofdm[i] = (u8) maxpwr;
+
+ /*
+ * OFDM 40 MHz SISO has the same power as the corresponding
+ * MCS0-7 rate unless overriden by the locale specific code.
+ * We set this value to 0 as a flag (presumably 0 dBm isn't
+ * a possibility) and then copy the MCS0-7 value to the 40 MHz
+ * value if it wasn't explicitly set.
+ */
+ txpwr->ofdm_40_siso[i] = 0;
+
+ txpwr->ofdm_cdd[i] = (u8) maxpwr;
+
+ txpwr->ofdm_40_cdd[i] = 0;
+ }
+
+ delta = 0;
+ if (band->antgain > QDB(6))
+ delta = band->antgain - QDB(6); /* Excess over 6 dB */
+
+ if (band->bandtype == BRCM_BAND_2G)
+ maxpwr_idx = (chan - 1);
+ else
+ maxpwr_idx = CHANNEL_POWER_IDX_5G(chan);
+
+ maxpwr20 = li_mimo->maxpwr20[maxpwr_idx];
+ maxpwr40 = li_mimo->maxpwr40[maxpwr_idx];
+
+ maxpwr20 = maxpwr20 - delta;
+ maxpwr20 = max(maxpwr20, 0);
+ maxpwr40 = maxpwr40 - delta;
+ maxpwr40 = max(maxpwr40, 0);
+
+ /* Fill in the MCS 0-7 (SISO) rates */
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+
+ /*
+ * 20 MHz has the same power as the corresponding OFDM rate
+ * unless overriden by the locale specific code.
+ */
+ txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
+ txpwr->mcs_40_siso[i] = 0;
+ }
+
+ /* Fill in the MCS 0-7 CDD rates */
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+ txpwr->mcs_20_cdd[i] = (u8) maxpwr20;
+ txpwr->mcs_40_cdd[i] = (u8) maxpwr40;
+ }
+
+ /*
+ * These locales have SISO expressed in the
+ * table and override CDD later
+ */
+ if (li_mimo == &locale_bn) {
+ if (li_mimo == &locale_bn) {
+ maxpwr20 = QDB(16);
+ maxpwr40 = 0;
+
+ if (chan >= 3 && chan <= 11)
+ maxpwr40 = QDB(16);
+ }
+
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+ txpwr->mcs_20_siso[i] = (u8) maxpwr20;
+ txpwr->mcs_40_siso[i] = (u8) maxpwr40;
+ }
+ }
+
+ /* Fill in the MCS 0-7 STBC rates */
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+ txpwr->mcs_20_stbc[i] = 0;
+ txpwr->mcs_40_stbc[i] = 0;
+ }
+
+ /* Fill in the MCS 8-15 SDM rates */
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) {
+ txpwr->mcs_20_mimo[i] = (u8) maxpwr20;
+ txpwr->mcs_40_mimo[i] = (u8) maxpwr40;
+ }
+
+ /* Fill in MCS32 */
+ txpwr->mcs32 = (u8) maxpwr40;
+
+ for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
+ if (txpwr->ofdm_40_cdd[i] == 0)
+ txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
+ if (i == 0) {
+ i = i + 1;
+ if (txpwr->ofdm_40_cdd[i] == 0)
+ txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
+ }
+ }
+
+ /*
+ * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO
+ * value if it wasn't provided explicitly.
+ */
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+ if (txpwr->mcs_40_siso[i] == 0)
+ txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i];
+ }
+
+ for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
+ if (txpwr->ofdm_40_siso[i] == 0)
+ txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
+ if (i == 0) {
+ i = i + 1;
+ if (txpwr->ofdm_40_siso[i] == 0)
+ txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
+ }
+ }
+
+ /*
+ * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding
+ * STBC values if they weren't provided explicitly.
+ */
+ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+ if (txpwr->mcs_20_stbc[i] == 0)
+ txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i];
+
+ if (txpwr->mcs_40_stbc[i] == 0)
+ txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i];
+ }
+
+ return;
+}
+
+/*
+ * Verify the chanspec is using a legal set of parameters, i.e. that the
+ * chanspec specified a band, bw, ctl_sb and channel and that the
+ * combination could be legal given any set of circumstances.
+ * RETURNS: true is the chanspec is malformed, false if it looks good.
+ */
+static bool brcms_c_chspec_malformed(u16 chanspec)
+{
+ /* must be 2G or 5G band */
+ if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))
+ return true;
+ /* must be 20 or 40 bandwidth */
+ if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))
+ return true;
+
+ /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */
+ if (CHSPEC_IS20(chanspec)) {
+ if (!CHSPEC_SB_NONE(chanspec))
+ return true;
+ } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Validate the chanspec for this locale, for 40MHZ we need to also
+ * check that the sidebands are valid 20MZH channels in this locale
+ * and they are also a legal HT combination
+ */
+static bool
+brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec)
+{
+ struct brcms_c_info *wlc = wlc_cm->wlc;
+ u8 channel = CHSPEC_CHANNEL(chspec);
+
+ /* check the chanspec */
+ if (brcms_c_chspec_malformed(chspec)) {
+ brcms_err(wlc->hw->d11core, "wl%d: malformed chanspec 0x%x\n",
+ wlc->pub->unit, chspec);
+ return false;
+ }
+
+ if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) !=
+ chspec_bandunit(chspec))
+ return false;
+
+ return true;
+}
+
+bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec)
+{
+ return brcms_c_valid_chanspec_ext(wlc_cm, chspec);
+}
+
+static bool brcms_is_radar_freq(u16 center_freq)
+{
+ return center_freq >= 5260 && center_freq <= 5700;
+}
+
+static void brcms_reg_apply_radar_flags(struct wiphy *wiphy)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ int i;
+
+ sband = wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ if (!brcms_is_radar_freq(ch->center_freq))
+ continue;
+
+ /*
+ * All channels in this range should be passive and have
+ * DFS enabled.
+ */
+ if (!(ch->flags & IEEE80211_CHAN_DISABLED))
+ ch->flags |= IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_NO_IR;
+ }
+}
+
+static void
+brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
+ enum nl80211_reg_initiator initiator)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ const struct ieee80211_reg_rule *rule;
+ int band, i;
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ if (ch->flags &
+ (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ rule = freq_reg_info(wiphy,
+ MHZ_TO_KHZ(ch->center_freq));
+ if (IS_ERR(rule))
+ continue;
+
+ if (!(rule->flags & NL80211_RRF_NO_IR))
+ ch->flags &= ~IEEE80211_CHAN_NO_IR;
+ } else if (ch->beacon_found) {
+ ch->flags &= ~IEEE80211_CHAN_NO_IR;
+ }
+ }
+ }
+}
+
+static void brcms_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct brcms_info *wl = hw->priv;
+ struct brcms_c_info *wlc = wl->wlc;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ int band, i;
+ bool ch_found = false;
+
+ brcms_reg_apply_radar_flags(wiphy);
+
+ if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
+ brcms_reg_apply_beaconing_flags(wiphy, request->initiator);
+
+ /* Disable radio if all channels disallowed by regulatory */
+ for (band = 0; !ch_found && band < IEEE80211_NUM_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+
+ for (i = 0; !ch_found && i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ if (!(ch->flags & IEEE80211_CHAN_DISABLED))
+ ch_found = true;
+ }
+ }
+
+ if (ch_found) {
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+ } else {
+ mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+ brcms_err(wlc->hw->d11core,
+ "wl%d: %s: no valid channel for \"%s\"\n",
+ wlc->pub->unit, __func__, request->alpha2);
+ }
+
+ if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G)
+ wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
+ brcms_c_japan_ccode(request->alpha2));
+}
+
+void brcms_c_regd_init(struct brcms_c_info *wlc)
+{
+ struct wiphy *wiphy = wlc->wiphy;
+ const struct brcms_regd *regd = wlc->cmi->world_regd;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ struct brcms_chanvec sup_chan;
+ struct brcms_band *band;
+ int band_idx, i;
+
+ /* Disable any channels not supported by the phy */
+ for (band_idx = 0; band_idx < wlc->pub->_nbands; band_idx++) {
+ band = wlc->bandstate[band_idx];
+
+ wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
+ &sup_chan);
+
+ if (band_idx == BAND_2G_INDEX)
+ sband = wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ sband = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+ if (!isset(sup_chan.vec, ch->hw_value))
+ ch->flags |= IEEE80211_CHAN_DISABLED;
+ }
+ }
+
+ wlc->wiphy->reg_notifier = brcms_reg_notifier;
+ wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+ REGULATORY_STRICT_REG;
+ wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain);
+ brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h
index 39dd3a5b2979..39dd3a5b2979 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/d11.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h
index 9035cc4d6ff3..9035cc4d6ff3 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/d11.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
index 7a1fbb2e3a71..7a1fbb2e3a71 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h
index 822781cf15d4..822781cf15d4 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c
index 796f5f9d5d5a..796f5f9d5d5a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h
index ff5b80b09046..ff5b80b09046 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/dma.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c
index 74b17cecb189..74b17cecb189 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/led.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h
index 17a0b1f5dbcf..17a0b1f5dbcf 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/led.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
index bec2dc1ca2e4..bec2dc1ca2e4 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h
index 198053dfc310..198053dfc310 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
index 218cbc8bf3a7..218cbc8bf3a7 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
index c4d135cff04a..c4d135cff04a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c
index 1c4e9dd57960..1c4e9dd57960 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h
index 4d3734f48d9c..4d3734f48d9c 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h
index 4960f7d26804..4960f7d26804 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
index 93d4cde0eb31..93d4cde0eb31 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h
index f4a8ab09da43..f4a8ab09da43 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
index 99dac9b8a082..99dac9b8a082 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c
index faf1ebe76068..faf1ebe76068 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h
index 20e3783f921b..20e3783f921b 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h
index c3a675455ff5..c3a675455ff5 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h
index a97c3a799479..a97c3a799479 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c
index d7fa312214f3..d7fa312214f3 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h
index 489422a36085..489422a36085 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c
index dbf50ef6cd75..dbf50ef6cd75 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h
index dc8a84e85117..dc8a84e85117 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c
index a0de5db0cd64..a0de5db0cd64 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h
index dd8774717ade..dd8774717ade 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c
index 71b80381f3ad..71b80381f3ad 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h
index a014bbc4f935..a014bbc4f935 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h
index 4da38cb4f318..4da38cb4f318 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c
index 0a0c0ad4f96f..0a0c0ad4f96f 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/rate.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h
index 5bb88b78ed64..5bb88b78ed64 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/rate.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/scb.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h
index 3a3d73699f83..3a3d73699f83 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/scb.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c
index dd9162722495..dd9162722495 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/stf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h
index ba9493009a33..ba9493009a33 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/stf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/types.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h
index ae1f3ad40d45..ae1f3ad40d45 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c
index 80e3ccf865e3..80e3ccf865e3 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h
index c87dd89bcb78..c87dd89bcb78 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile
new file mode 100644
index 000000000000..256c91f9ac4b
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver Utilities
+#
+# Copyright (c) 2011 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ccflags-y := \
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmutil \
+ -Idrivers/net/wireless/broadcom/brcm80211/include
+
+obj-$(CONFIG_BRCMUTIL) += brcmutil.o
+brcmutil-objs = utils.o d11.o
diff --git a/drivers/net/wireless/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
index 2b2522bdd8eb..2b2522bdd8eb 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/d11.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c
index 0543607002fd..0543607002fd 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/utils.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
new file mode 100644
index 000000000000..699f2c2782ee
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_HW_IDS_H_
+#define _BRCM_HW_IDS_H_
+
+#include <linux/pci_ids.h>
+#include <linux/mmc/sdio_ids.h>
+
+#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c
+#define BRCM_USB_VENDOR_ID_LG 0x043e
+#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM
+
+/* Chipcommon Core Chip IDs */
+#define BRCM_CC_43143_CHIP_ID 43143
+#define BRCM_CC_43235_CHIP_ID 43235
+#define BRCM_CC_43236_CHIP_ID 43236
+#define BRCM_CC_43238_CHIP_ID 43238
+#define BRCM_CC_43241_CHIP_ID 0x4324
+#define BRCM_CC_43242_CHIP_ID 43242
+#define BRCM_CC_4329_CHIP_ID 0x4329
+#define BRCM_CC_4330_CHIP_ID 0x4330
+#define BRCM_CC_4334_CHIP_ID 0x4334
+#define BRCM_CC_43340_CHIP_ID 43340
+#define BRCM_CC_43362_CHIP_ID 43362
+#define BRCM_CC_4335_CHIP_ID 0x4335
+#define BRCM_CC_4339_CHIP_ID 0x4339
+#define BRCM_CC_43430_CHIP_ID 43430
+#define BRCM_CC_4345_CHIP_ID 0x4345
+#define BRCM_CC_4350_CHIP_ID 0x4350
+#define BRCM_CC_4354_CHIP_ID 0x4354
+#define BRCM_CC_4356_CHIP_ID 0x4356
+#define BRCM_CC_43566_CHIP_ID 43566
+#define BRCM_CC_43567_CHIP_ID 43567
+#define BRCM_CC_43569_CHIP_ID 43569
+#define BRCM_CC_43570_CHIP_ID 43570
+#define BRCM_CC_4358_CHIP_ID 0x4358
+#define BRCM_CC_4359_CHIP_ID 0x4359
+#define BRCM_CC_43602_CHIP_ID 43602
+#define BRCM_CC_4365_CHIP_ID 0x4365
+#define BRCM_CC_4366_CHIP_ID 0x4366
+#define BRCM_CC_4371_CHIP_ID 0x4371
+
+/* USB Device IDs */
+#define BRCM_USB_43143_DEVICE_ID 0xbd1e
+#define BRCM_USB_43236_DEVICE_ID 0xbd17
+#define BRCM_USB_43242_DEVICE_ID 0xbd1f
+#define BRCM_USB_43242_LG_DEVICE_ID 0x3101
+#define BRCM_USB_43569_DEVICE_ID 0xbd27
+#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc
+
+/* PCIE Device IDs */
+#define BRCM_PCIE_4350_DEVICE_ID 0x43a3
+#define BRCM_PCIE_4354_DEVICE_ID 0x43df
+#define BRCM_PCIE_4356_DEVICE_ID 0x43ec
+#define BRCM_PCIE_43567_DEVICE_ID 0x43d3
+#define BRCM_PCIE_43570_DEVICE_ID 0x43d9
+#define BRCM_PCIE_4358_DEVICE_ID 0x43e9
+#define BRCM_PCIE_4359_DEVICE_ID 0x43ef
+#define BRCM_PCIE_43602_DEVICE_ID 0x43ba
+#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb
+#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc
+#define BRCM_PCIE_43602_RAW_DEVICE_ID 43602
+#define BRCM_PCIE_4365_DEVICE_ID 0x43ca
+#define BRCM_PCIE_4365_2G_DEVICE_ID 0x43cb
+#define BRCM_PCIE_4365_5G_DEVICE_ID 0x43cc
+#define BRCM_PCIE_4366_DEVICE_ID 0x43c3
+#define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4
+#define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5
+#define BRCM_PCIE_4371_DEVICE_ID 0x440d
+
+
+/* brcmsmac IDs */
+#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */
+#define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */
+#define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */
+#define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */
+#define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */
+#define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */
+
+#define BCM4313_CHIP_ID 0x4313
+#define BCM43224_CHIP_ID 43224
+
+#endif /* _BRCM_HW_IDS_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
index f9745ea8b3e0..f9745ea8b3e0 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_d11.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h
index 41969527b459..41969527b459 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
new file mode 100644
index 000000000000..3f68dd5ecd11
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMU_WIFI_H_
+#define _BRCMU_WIFI_H_
+
+#include <linux/if_ether.h> /* for ETH_ALEN */
+#include <linux/ieee80211.h> /* for WLAN_PMKID_LEN */
+
+/*
+ * A chanspec (u16) holds the channel number, band, bandwidth and control
+ * sideband
+ */
+
+/* channel defines */
+#define CH_UPPER_SB 0x01
+#define CH_LOWER_SB 0x02
+#define CH_EWA_VALID 0x04
+#define CH_30MHZ_APART 6
+#define CH_20MHZ_APART 4
+#define CH_10MHZ_APART 2
+#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */
+#define CH_MIN_2G_CHANNEL 1
+#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */
+#define CH_MIN_5G_CHANNEL 34
+
+/* bandstate array indices */
+#define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */
+#define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */
+
+/*
+ * max # supported channels. The max channel no is 216, this is that + 1
+ * rounded up to a multiple of NBBY (8). DO NOT MAKE it > 255: channels are
+ * u8's all over
+*/
+#define MAXCHANNEL 224
+
+#define WL_CHANSPEC_CHAN_MASK 0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT 0
+
+#define WL_CHANSPEC_CTL_SB_MASK 0x0300
+#define WL_CHANSPEC_CTL_SB_SHIFT 8
+#define WL_CHANSPEC_CTL_SB_LOWER 0x0100
+#define WL_CHANSPEC_CTL_SB_UPPER 0x0200
+#define WL_CHANSPEC_CTL_SB_NONE 0x0300
+
+#define WL_CHANSPEC_BW_MASK 0x0C00
+#define WL_CHANSPEC_BW_SHIFT 10
+#define WL_CHANSPEC_BW_10 0x0400
+#define WL_CHANSPEC_BW_20 0x0800
+#define WL_CHANSPEC_BW_40 0x0C00
+#define WL_CHANSPEC_BW_80 0x2000
+
+#define WL_CHANSPEC_BAND_MASK 0xf000
+#define WL_CHANSPEC_BAND_SHIFT 12
+#define WL_CHANSPEC_BAND_5G 0x1000
+#define WL_CHANSPEC_BAND_2G 0x2000
+#define INVCHANSPEC 255
+
+#define WL_CHAN_VALID_HW (1 << 0) /* valid with current HW */
+#define WL_CHAN_VALID_SW (1 << 1) /* valid with country sett. */
+#define WL_CHAN_BAND_5G (1 << 2) /* 5GHz-band channel */
+#define WL_CHAN_RADAR (1 << 3) /* radar sensitive channel */
+#define WL_CHAN_INACTIVE (1 << 4) /* inactive due to radar */
+#define WL_CHAN_PASSIVE (1 << 5) /* channel in passive mode */
+#define WL_CHAN_RESTRICTED (1 << 6) /* restricted use channel */
+
+/* values for band specific 40MHz capabilities */
+#define WLC_N_BW_20ALL 0
+#define WLC_N_BW_40ALL 1
+#define WLC_N_BW_20IN2G_40IN5G 2
+
+#define WLC_BW_20MHZ_BIT BIT(0)
+#define WLC_BW_40MHZ_BIT BIT(1)
+#define WLC_BW_80MHZ_BIT BIT(2)
+#define WLC_BW_160MHZ_BIT BIT(3)
+
+/* Bandwidth capabilities */
+#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT| \
+ WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_160MHZ (WLC_BW_160MHZ_BIT|WLC_BW_80MHZ_BIT| \
+ WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_UNRESTRICTED 0xFF
+
+/* band types */
+#define WLC_BAND_AUTO 0 /* auto-select */
+#define WLC_BAND_5G 1 /* 5 Ghz */
+#define WLC_BAND_2G 2 /* 2.4 Ghz */
+#define WLC_BAND_ALL 3 /* all bands */
+
+#define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK)
+
+#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK)
+
+#define CHSPEC_IS10(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+
+#define CHSPEC_IS20(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+
+#define CHSPEC_IS40(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+
+#define CHSPEC_IS80(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80)
+
+#define CHSPEC_IS5G(chspec) \
+ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+
+#define CHSPEC_IS2G(chspec) \
+ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+
+#define CHSPEC_SB_NONE(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE)
+
+#define CHSPEC_SB_UPPER(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER)
+
+#define CHSPEC_SB_LOWER(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER)
+
+#define CHSPEC_CTL_CHAN(chspec) \
+ ((CHSPEC_SB_LOWER(chspec)) ? \
+ (lower_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \
+ (upper_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))))
+
+#define CHSPEC2BAND(chspec) (CHSPEC_IS5G(chspec) ? BRCM_BAND_5G : BRCM_BAND_2G)
+
+#define CHANSPEC_STR_LEN 8
+
+static inline int lower_20_sb(int channel)
+{
+ return channel > CH_10MHZ_APART ? (channel - CH_10MHZ_APART) : 0;
+}
+
+static inline int upper_20_sb(int channel)
+{
+ return (channel < (MAXCHANNEL - CH_10MHZ_APART)) ?
+ channel + CH_10MHZ_APART : 0;
+}
+
+static inline int chspec_bandunit(u16 chspec)
+{
+ return CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX;
+}
+
+static inline u16 ch20mhz_chspec(int channel)
+{
+ u16 rc = channel <= CH_MAX_2G_CHANNEL ?
+ WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G;
+
+ return (u16)((u16)channel | WL_CHANSPEC_BW_20 |
+ WL_CHANSPEC_CTL_SB_NONE | rc);
+}
+
+static inline int next_20mhz_chan(int channel)
+{
+ return channel < (MAXCHANNEL - CH_20MHZ_APART) ?
+ channel + CH_20MHZ_APART : 0;
+}
+
+/* defined rate in 500kbps */
+#define BRCM_MAXRATE 108 /* in 500kbps units */
+#define BRCM_RATE_1M 2 /* in 500kbps units */
+#define BRCM_RATE_2M 4 /* in 500kbps units */
+#define BRCM_RATE_5M5 11 /* in 500kbps units */
+#define BRCM_RATE_11M 22 /* in 500kbps units */
+#define BRCM_RATE_6M 12 /* in 500kbps units */
+#define BRCM_RATE_9M 18 /* in 500kbps units */
+#define BRCM_RATE_12M 24 /* in 500kbps units */
+#define BRCM_RATE_18M 36 /* in 500kbps units */
+#define BRCM_RATE_24M 48 /* in 500kbps units */
+#define BRCM_RATE_36M 72 /* in 500kbps units */
+#define BRCM_RATE_48M 96 /* in 500kbps units */
+#define BRCM_RATE_54M 108 /* in 500kbps units */
+
+#define BRCM_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */
+
+#define MCSSET_LEN 16
+
+static inline bool ac_bitmap_tst(u8 bitmap, int prec)
+{
+ return (bitmap & (1 << (prec))) != 0;
+}
+
+/* Enumerate crypto algorithms */
+#define CRYPTO_ALGO_OFF 0
+#define CRYPTO_ALGO_WEP1 1
+#define CRYPTO_ALGO_TKIP 2
+#define CRYPTO_ALGO_WEP128 3
+#define CRYPTO_ALGO_AES_CCM 4
+#define CRYPTO_ALGO_AES_RESERVED1 5
+#define CRYPTO_ALGO_AES_RESERVED2 6
+#define CRYPTO_ALGO_NALG 7
+
+/* wireless security bitvec */
+
+#define WEP_ENABLED 0x0001
+#define TKIP_ENABLED 0x0002
+#define AES_ENABLED 0x0004
+#define WSEC_SWFLAG 0x0008
+/* to go into transition mode without setting wep */
+#define SES_OW_ENABLED 0x0040
+/* MFP */
+#define MFP_CAPABLE 0x0200
+#define MFP_REQUIRED 0x0400
+
+/* WPA authentication mode bitvec */
+#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */
+#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */
+#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */
+#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */
+#define WPA_AUTH_RESERVED1 0x0008
+#define WPA_AUTH_RESERVED2 0x0010
+
+#define WPA2_AUTH_RESERVED1 0x0020
+#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */
+#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */
+#define WPA2_AUTH_RESERVED3 0x0200
+#define WPA2_AUTH_RESERVED4 0x0400
+#define WPA2_AUTH_RESERVED5 0x0800
+
+#define DOT11_DEFAULT_RTS_LEN 2347
+#define DOT11_DEFAULT_FRAG_LEN 2346
+
+#define DOT11_ICV_AES_LEN 8
+#define DOT11_QOS_LEN 2
+#define DOT11_IV_MAX_LEN 8
+#define DOT11_A4_HDR_LEN 30
+
+#define HT_CAP_RX_STBC_NO 0x0
+#define HT_CAP_RX_STBC_ONE_STREAM 0x1
+
+#endif /* _BRCMU_WIFI_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
index e1fd499930a0..e1fd499930a0 100644
--- a/drivers/net/wireless/brcm80211/include/chipcommon.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
diff --git a/drivers/net/wireless/brcm80211/include/defs.h b/drivers/net/wireless/broadcom/brcm80211/include/defs.h
index 8d1e85e0ed51..8d1e85e0ed51 100644
--- a/drivers/net/wireless/brcm80211/include/defs.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/defs.h
diff --git a/drivers/net/wireless/brcm80211/include/soc.h b/drivers/net/wireless/broadcom/brcm80211/include/soc.h
index 123cfa854a0d..123cfa854a0d 100644
--- a/drivers/net/wireless/brcm80211/include/soc.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/soc.h
diff --git a/drivers/net/wireless/cisco/Kconfig b/drivers/net/wireless/cisco/Kconfig
new file mode 100644
index 000000000000..b22567dff893
--- /dev/null
+++ b/drivers/net/wireless/cisco/Kconfig
@@ -0,0 +1,56 @@
+config WLAN_VENDOR_CISCO
+ bool "Cisco devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_CISCO
+
+config AIRO
+ tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
+ depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN)
+ select WIRELESS_EXT
+ select CRYPTO
+ select WEXT_SPY
+ select WEXT_PRIV
+ ---help---
+ This is the standard Linux driver to support Cisco/Aironet ISA and
+ PCI 802.11 wireless cards.
+ It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
+ - with or without encryption) as well as card before the Cisco
+ acquisition (Aironet 4500, Aironet 4800, Aironet 4800B).
+
+ This driver support both the standard Linux Wireless Extensions
+ and Cisco proprietary API, so both the Linux Wireless Tools and the
+ Cisco Linux utilities can be used to configure the card.
+
+ The driver can be compiled as a module and will be named "airo".
+
+config AIRO_CS
+ tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
+ depends on CFG80211 && PCMCIA && (BROKEN || !M32R)
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
+ select CRYPTO
+ select CRYPTO_AES
+ ---help---
+ This is the standard Linux driver to support Cisco/Aironet PCMCIA
+ 802.11 wireless cards. This driver is the same as the Aironet
+ driver part of the Linux Pcmcia package.
+ It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
+ - with or without encryption) as well as card before the Cisco
+ acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also
+ supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom
+ 802.11b cards.
+
+ This driver support both the standard Linux Wireless Extensions
+ and Cisco proprietary API, so both the Linux Wireless Tools and the
+ Cisco Linux utilities can be used to configure the card.
+
+endif # WLAN_VENDOR_CISCO
diff --git a/drivers/net/wireless/cisco/Makefile b/drivers/net/wireless/cisco/Makefile
new file mode 100644
index 000000000000..d4110b19d6ef
--- /dev/null
+++ b/drivers/net/wireless/cisco/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_AIRO) += airo.o
+obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c
new file mode 100644
index 000000000000..d2353f6e5214
--- /dev/null
+++ b/drivers/net/wireless/cisco/airo.c
@@ -0,0 +1,8224 @@
+/*======================================================================
+
+ Aironet driver for 4500 and 4800 series cards
+
+ This code is released under both the GPL version 2 and BSD licenses.
+ Either license may be used. The respective licenses are found at
+ the end of this file.
+
+ This code was developed by Benjamin Reed <breed@users.sourceforge.net>
+ including portions of which come from the Aironet PC4500
+ Developer's Reference Manual and used with permission. Copyright
+ (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use
+ code in the Developer's manual was granted for this driver by
+ Aironet. Major code contributions were received from Javier Achirica
+ <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>.
+ Code was also integrated from the Cisco Aironet driver for Linux.
+ Support for MPI350 cards was added by Fabrice Bellet
+ <fabrice@bellet.info>.
+
+======================================================================*/
+
+#include <linux/err.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/bitops.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/io.h>
+#include <asm/unaligned.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+
+#include "airo.h"
+
+#define DRV_NAME "airo"
+
+#ifdef CONFIG_PCI
+static const struct pci_device_id card_ids[] = {
+ { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
+ { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, card_ids);
+
+static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
+static void airo_pci_remove(struct pci_dev *);
+static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state);
+static int airo_pci_resume(struct pci_dev *pdev);
+
+static struct pci_driver airo_driver = {
+ .name = DRV_NAME,
+ .id_table = card_ids,
+ .probe = airo_pci_probe,
+ .remove = airo_pci_remove,
+ .suspend = airo_pci_suspend,
+ .resume = airo_pci_resume,
+};
+#endif /* CONFIG_PCI */
+
+/* Include Wireless Extension definition and check version - Jean II */
+#include <linux/wireless.h>
+#define WIRELESS_SPY /* enable iwspy support */
+
+#define CISCO_EXT /* enable Cisco extensions */
+#ifdef CISCO_EXT
+#include <linux/delay.h>
+#endif
+
+/* Hack to do some power saving */
+#define POWER_ON_DOWN
+
+/* As you can see this list is HUGH!
+ I really don't know what a lot of these counts are about, but they
+ are all here for completeness. If the IGNLABEL macro is put in
+ infront of the label, that statistic will not be included in the list
+ of statistics in the /proc filesystem */
+
+#define IGNLABEL(comment) NULL
+static const char *statsLabels[] = {
+ "RxOverrun",
+ IGNLABEL("RxPlcpCrcErr"),
+ IGNLABEL("RxPlcpFormatErr"),
+ IGNLABEL("RxPlcpLengthErr"),
+ "RxMacCrcErr",
+ "RxMacCrcOk",
+ "RxWepErr",
+ "RxWepOk",
+ "RetryLong",
+ "RetryShort",
+ "MaxRetries",
+ "NoAck",
+ "NoCts",
+ "RxAck",
+ "RxCts",
+ "TxAck",
+ "TxRts",
+ "TxCts",
+ "TxMc",
+ "TxBc",
+ "TxUcFrags",
+ "TxUcPackets",
+ "TxBeacon",
+ "RxBeacon",
+ "TxSinColl",
+ "TxMulColl",
+ "DefersNo",
+ "DefersProt",
+ "DefersEngy",
+ "DupFram",
+ "RxFragDisc",
+ "TxAged",
+ "RxAged",
+ "LostSync-MaxRetry",
+ "LostSync-MissedBeacons",
+ "LostSync-ArlExceeded",
+ "LostSync-Deauth",
+ "LostSync-Disassoced",
+ "LostSync-TsfTiming",
+ "HostTxMc",
+ "HostTxBc",
+ "HostTxUc",
+ "HostTxFail",
+ "HostRxMc",
+ "HostRxBc",
+ "HostRxUc",
+ "HostRxDiscard",
+ IGNLABEL("HmacTxMc"),
+ IGNLABEL("HmacTxBc"),
+ IGNLABEL("HmacTxUc"),
+ IGNLABEL("HmacTxFail"),
+ IGNLABEL("HmacRxMc"),
+ IGNLABEL("HmacRxBc"),
+ IGNLABEL("HmacRxUc"),
+ IGNLABEL("HmacRxDiscard"),
+ IGNLABEL("HmacRxAccepted"),
+ "SsidMismatch",
+ "ApMismatch",
+ "RatesMismatch",
+ "AuthReject",
+ "AuthTimeout",
+ "AssocReject",
+ "AssocTimeout",
+ IGNLABEL("ReasonOutsideTable"),
+ IGNLABEL("ReasonStatus1"),
+ IGNLABEL("ReasonStatus2"),
+ IGNLABEL("ReasonStatus3"),
+ IGNLABEL("ReasonStatus4"),
+ IGNLABEL("ReasonStatus5"),
+ IGNLABEL("ReasonStatus6"),
+ IGNLABEL("ReasonStatus7"),
+ IGNLABEL("ReasonStatus8"),
+ IGNLABEL("ReasonStatus9"),
+ IGNLABEL("ReasonStatus10"),
+ IGNLABEL("ReasonStatus11"),
+ IGNLABEL("ReasonStatus12"),
+ IGNLABEL("ReasonStatus13"),
+ IGNLABEL("ReasonStatus14"),
+ IGNLABEL("ReasonStatus15"),
+ IGNLABEL("ReasonStatus16"),
+ IGNLABEL("ReasonStatus17"),
+ IGNLABEL("ReasonStatus18"),
+ IGNLABEL("ReasonStatus19"),
+ "RxMan",
+ "TxMan",
+ "RxRefresh",
+ "TxRefresh",
+ "RxPoll",
+ "TxPoll",
+ "HostRetries",
+ "LostSync-HostReq",
+ "HostTxBytes",
+ "HostRxBytes",
+ "ElapsedUsec",
+ "ElapsedSec",
+ "LostSyncBetterAP",
+ "PrivacyMismatch",
+ "Jammed",
+ "DiscRxNotWepped",
+ "PhyEleMismatch",
+ (char*)-1 };
+#ifndef RUN_AT
+#define RUN_AT(x) (jiffies+(x))
+#endif
+
+
+/* These variables are for insmod, since it seems that the rates
+ can only be set in setup_card. Rates should be a comma separated
+ (no spaces) list of rates (up to 8). */
+
+static int rates[8];
+static char *ssids[3];
+
+static int io[4];
+static int irq[4];
+
+static
+int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
+ 0 means no limit. For old cards this was 4 */
+
+static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
+static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
+ the bap, needed on some older cards and buses. */
+static int adhoc;
+
+static int probe = 1;
+
+static kuid_t proc_kuid;
+static int proc_uid /* = 0 */;
+
+static kgid_t proc_kgid;
+static int proc_gid /* = 0 */;
+
+static int airo_perm = 0555;
+
+static int proc_perm = 0644;
+
+MODULE_AUTHOR("Benjamin Reed");
+MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. "
+ "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs.");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(rates, int, NULL, 0);
+module_param_array(ssids, charp, NULL, 0);
+module_param(auto_wep, int, 0);
+MODULE_PARM_DESC(auto_wep,
+ "If non-zero, the driver will keep looping through the authentication options until an association is made. "
+ "The value of auto_wep is number of the wep keys to check. "
+ "A value of 2 will try using the key at index 0 and index 1.");
+module_param(aux_bap, int, 0);
+MODULE_PARM_DESC(aux_bap,
+ "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. "
+ "Before switching it checks that the switch is needed.");
+module_param(maxencrypt, int, 0);
+MODULE_PARM_DESC(maxencrypt,
+ "The maximum speed that the card can do encryption. "
+ "Units are in 512kbs. "
+ "Zero (default) means there is no limit. "
+ "Older cards used to be limited to 2mbs (4).");
+module_param(adhoc, int, 0);
+MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
+module_param(probe, int, 0);
+MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
+
+module_param(proc_uid, int, 0);
+MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
+module_param(proc_gid, int, 0);
+MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
+module_param(airo_perm, int, 0);
+MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
+module_param(proc_perm, int, 0);
+MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
+
+/* This is a kind of sloppy hack to get this information to OUT4500 and
+ IN4500. I would be extremely interested in the situation where this
+ doesn't work though!!! */
+static int do8bitIO /* = 0 */;
+
+/* Return codes */
+#define SUCCESS 0
+#define ERROR -1
+#define NO_PACKET -2
+
+/* Commands */
+#define NOP2 0x0000
+#define MAC_ENABLE 0x0001
+#define MAC_DISABLE 0x0002
+#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */
+#define CMD_SOFTRESET 0x0004
+#define HOSTSLEEP 0x0005
+#define CMD_MAGIC_PKT 0x0006
+#define CMD_SETWAKEMASK 0x0007
+#define CMD_READCFG 0x0008
+#define CMD_SETMODE 0x0009
+#define CMD_ALLOCATETX 0x000a
+#define CMD_TRANSMIT 0x000b
+#define CMD_DEALLOCATETX 0x000c
+#define NOP 0x0010
+#define CMD_WORKAROUND 0x0011
+#define CMD_ALLOCATEAUX 0x0020
+#define CMD_ACCESS 0x0021
+#define CMD_PCIBAP 0x0022
+#define CMD_PCIAUX 0x0023
+#define CMD_ALLOCBUF 0x0028
+#define CMD_GETTLV 0x0029
+#define CMD_PUTTLV 0x002a
+#define CMD_DELTLV 0x002b
+#define CMD_FINDNEXTTLV 0x002c
+#define CMD_PSPNODES 0x0030
+#define CMD_SETCW 0x0031
+#define CMD_SETPCF 0x0032
+#define CMD_SETPHYREG 0x003e
+#define CMD_TXTEST 0x003f
+#define MAC_ENABLETX 0x0101
+#define CMD_LISTBSS 0x0103
+#define CMD_SAVECFG 0x0108
+#define CMD_ENABLEAUX 0x0111
+#define CMD_WRITERID 0x0121
+#define CMD_USEPSPNODES 0x0130
+#define MAC_ENABLERX 0x0201
+
+/* Command errors */
+#define ERROR_QUALIF 0x00
+#define ERROR_ILLCMD 0x01
+#define ERROR_ILLFMT 0x02
+#define ERROR_INVFID 0x03
+#define ERROR_INVRID 0x04
+#define ERROR_LARGE 0x05
+#define ERROR_NDISABL 0x06
+#define ERROR_ALLOCBSY 0x07
+#define ERROR_NORD 0x0B
+#define ERROR_NOWR 0x0C
+#define ERROR_INVFIDTX 0x0D
+#define ERROR_TESTACT 0x0E
+#define ERROR_TAGNFND 0x12
+#define ERROR_DECODE 0x20
+#define ERROR_DESCUNAV 0x21
+#define ERROR_BADLEN 0x22
+#define ERROR_MODE 0x80
+#define ERROR_HOP 0x81
+#define ERROR_BINTER 0x82
+#define ERROR_RXMODE 0x83
+#define ERROR_MACADDR 0x84
+#define ERROR_RATES 0x85
+#define ERROR_ORDER 0x86
+#define ERROR_SCAN 0x87
+#define ERROR_AUTH 0x88
+#define ERROR_PSMODE 0x89
+#define ERROR_RTYPE 0x8A
+#define ERROR_DIVER 0x8B
+#define ERROR_SSID 0x8C
+#define ERROR_APLIST 0x8D
+#define ERROR_AUTOWAKE 0x8E
+#define ERROR_LEAP 0x8F
+
+/* Registers */
+#define COMMAND 0x00
+#define PARAM0 0x02
+#define PARAM1 0x04
+#define PARAM2 0x06
+#define STATUS 0x08
+#define RESP0 0x0a
+#define RESP1 0x0c
+#define RESP2 0x0e
+#define LINKSTAT 0x10
+#define SELECT0 0x18
+#define OFFSET0 0x1c
+#define RXFID 0x20
+#define TXALLOCFID 0x22
+#define TXCOMPLFID 0x24
+#define DATA0 0x36
+#define EVSTAT 0x30
+#define EVINTEN 0x32
+#define EVACK 0x34
+#define SWS0 0x28
+#define SWS1 0x2a
+#define SWS2 0x2c
+#define SWS3 0x2e
+#define AUXPAGE 0x3A
+#define AUXOFF 0x3C
+#define AUXDATA 0x3E
+
+#define FID_TX 1
+#define FID_RX 2
+/* Offset into aux memory for descriptors */
+#define AUX_OFFSET 0x800
+/* Size of allocated packets */
+#define PKTSIZE 1840
+#define RIDSIZE 2048
+/* Size of the transmit queue */
+#define MAXTXQ 64
+
+/* BAP selectors */
+#define BAP0 0 /* Used for receiving packets */
+#define BAP1 2 /* Used for xmiting packets and working with RIDS */
+
+/* Flags */
+#define COMMAND_BUSY 0x8000
+
+#define BAP_BUSY 0x8000
+#define BAP_ERR 0x4000
+#define BAP_DONE 0x2000
+
+#define PROMISC 0xffff
+#define NOPROMISC 0x0000
+
+#define EV_CMD 0x10
+#define EV_CLEARCOMMANDBUSY 0x4000
+#define EV_RX 0x01
+#define EV_TX 0x02
+#define EV_TXEXC 0x04
+#define EV_ALLOC 0x08
+#define EV_LINK 0x80
+#define EV_AWAKE 0x100
+#define EV_TXCPY 0x400
+#define EV_UNKNOWN 0x800
+#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
+#define EV_AWAKEN 0x2000
+#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
+
+#ifdef CHECK_UNKNOWN_INTS
+#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN)
+#else
+#define IGNORE_INTS (~STATUS_INTS)
+#endif
+
+/* RID TYPES */
+#define RID_RW 0x20
+
+/* The RIDs */
+#define RID_CAPABILITIES 0xFF00
+#define RID_APINFO 0xFF01
+#define RID_RADIOINFO 0xFF02
+#define RID_UNKNOWN3 0xFF03
+#define RID_RSSI 0xFF04
+#define RID_CONFIG 0xFF10
+#define RID_SSID 0xFF11
+#define RID_APLIST 0xFF12
+#define RID_DRVNAME 0xFF13
+#define RID_ETHERENCAP 0xFF14
+#define RID_WEP_TEMP 0xFF15
+#define RID_WEP_PERM 0xFF16
+#define RID_MODULATION 0xFF17
+#define RID_OPTIONS 0xFF18
+#define RID_ACTUALCONFIG 0xFF20 /*readonly*/
+#define RID_FACTORYCONFIG 0xFF21
+#define RID_UNKNOWN22 0xFF22
+#define RID_LEAPUSERNAME 0xFF23
+#define RID_LEAPPASSWORD 0xFF24
+#define RID_STATUS 0xFF50
+#define RID_BEACON_HST 0xFF51
+#define RID_BUSY_HST 0xFF52
+#define RID_RETRIES_HST 0xFF53
+#define RID_UNKNOWN54 0xFF54
+#define RID_UNKNOWN55 0xFF55
+#define RID_UNKNOWN56 0xFF56
+#define RID_MIC 0xFF57
+#define RID_STATS16 0xFF60
+#define RID_STATS16DELTA 0xFF61
+#define RID_STATS16DELTACLEAR 0xFF62
+#define RID_STATS 0xFF68
+#define RID_STATSDELTA 0xFF69
+#define RID_STATSDELTACLEAR 0xFF6A
+#define RID_ECHOTEST_RID 0xFF70
+#define RID_ECHOTEST_RESULTS 0xFF71
+#define RID_BSSLISTFIRST 0xFF72
+#define RID_BSSLISTNEXT 0xFF73
+#define RID_WPA_BSSLISTFIRST 0xFF74
+#define RID_WPA_BSSLISTNEXT 0xFF75
+
+typedef struct {
+ u16 cmd;
+ u16 parm0;
+ u16 parm1;
+ u16 parm2;
+} Cmd;
+
+typedef struct {
+ u16 status;
+ u16 rsp0;
+ u16 rsp1;
+ u16 rsp2;
+} Resp;
+
+/*
+ * Rids and endian-ness: The Rids will always be in cpu endian, since
+ * this all the patches from the big-endian guys end up doing that.
+ * so all rid access should use the read/writeXXXRid routines.
+ */
+
+/* This structure came from an email sent to me from an engineer at
+ aironet for inclusion into this driver */
+typedef struct WepKeyRid WepKeyRid;
+struct WepKeyRid {
+ __le16 len;
+ __le16 kindex;
+ u8 mac[ETH_ALEN];
+ __le16 klen;
+ u8 key[16];
+} __packed;
+
+/* These structures are from the Aironet's PC4500 Developers Manual */
+typedef struct Ssid Ssid;
+struct Ssid {
+ __le16 len;
+ u8 ssid[32];
+} __packed;
+
+typedef struct SsidRid SsidRid;
+struct SsidRid {
+ __le16 len;
+ Ssid ssids[3];
+} __packed;
+
+typedef struct ModulationRid ModulationRid;
+struct ModulationRid {
+ __le16 len;
+ __le16 modulation;
+#define MOD_DEFAULT cpu_to_le16(0)
+#define MOD_CCK cpu_to_le16(1)
+#define MOD_MOK cpu_to_le16(2)
+} __packed;
+
+typedef struct ConfigRid ConfigRid;
+struct ConfigRid {
+ __le16 len; /* sizeof(ConfigRid) */
+ __le16 opmode; /* operating mode */
+#define MODE_STA_IBSS cpu_to_le16(0)
+#define MODE_STA_ESS cpu_to_le16(1)
+#define MODE_AP cpu_to_le16(2)
+#define MODE_AP_RPTR cpu_to_le16(3)
+#define MODE_CFG_MASK cpu_to_le16(0xff)
+#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */
+#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */
+#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */
+#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */
+#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */
+#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */
+#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */
+#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */
+#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */
+ __le16 rmode; /* receive mode */
+#define RXMODE_BC_MC_ADDR cpu_to_le16(0)
+#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */
+#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */
+#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */
+#define RXMODE_RFMON_ANYBSS cpu_to_le16(4)
+#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */
+#define RXMODE_MASK cpu_to_le16(255)
+#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */
+#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER)
+#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */
+ __le16 fragThresh;
+ __le16 rtsThres;
+ u8 macAddr[ETH_ALEN];
+ u8 rates[8];
+ __le16 shortRetryLimit;
+ __le16 longRetryLimit;
+ __le16 txLifetime; /* in kusec */
+ __le16 rxLifetime; /* in kusec */
+ __le16 stationary;
+ __le16 ordering;
+ __le16 u16deviceType; /* for overriding device type */
+ __le16 cfpRate;
+ __le16 cfpDuration;
+ __le16 _reserved1[3];
+ /*---------- Scanning/Associating ----------*/
+ __le16 scanMode;
+#define SCANMODE_ACTIVE cpu_to_le16(0)
+#define SCANMODE_PASSIVE cpu_to_le16(1)
+#define SCANMODE_AIROSCAN cpu_to_le16(2)
+ __le16 probeDelay; /* in kusec */
+ __le16 probeEnergyTimeout; /* in kusec */
+ __le16 probeResponseTimeout;
+ __le16 beaconListenTimeout;
+ __le16 joinNetTimeout;
+ __le16 authTimeout;
+ __le16 authType;
+#define AUTH_OPEN cpu_to_le16(0x1)
+#define AUTH_ENCRYPT cpu_to_le16(0x101)
+#define AUTH_SHAREDKEY cpu_to_le16(0x102)
+#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200)
+ __le16 associationTimeout;
+ __le16 specifiedApTimeout;
+ __le16 offlineScanInterval;
+ __le16 offlineScanDuration;
+ __le16 linkLossDelay;
+ __le16 maxBeaconLostTime;
+ __le16 refreshInterval;
+#define DISABLE_REFRESH cpu_to_le16(0xFFFF)
+ __le16 _reserved1a[1];
+ /*---------- Power save operation ----------*/
+ __le16 powerSaveMode;
+#define POWERSAVE_CAM cpu_to_le16(0)
+#define POWERSAVE_PSP cpu_to_le16(1)
+#define POWERSAVE_PSPCAM cpu_to_le16(2)
+ __le16 sleepForDtims;
+ __le16 listenInterval;
+ __le16 fastListenInterval;
+ __le16 listenDecay;
+ __le16 fastListenDelay;
+ __le16 _reserved2[2];
+ /*---------- Ap/Ibss config items ----------*/
+ __le16 beaconPeriod;
+ __le16 atimDuration;
+ __le16 hopPeriod;
+ __le16 channelSet;
+ __le16 channel;
+ __le16 dtimPeriod;
+ __le16 bridgeDistance;
+ __le16 radioID;
+ /*---------- Radio configuration ----------*/
+ __le16 radioType;
+#define RADIOTYPE_DEFAULT cpu_to_le16(0)
+#define RADIOTYPE_802_11 cpu_to_le16(1)
+#define RADIOTYPE_LEGACY cpu_to_le16(2)
+ u8 rxDiversity;
+ u8 txDiversity;
+ __le16 txPower;
+#define TXPOWER_DEFAULT 0
+ __le16 rssiThreshold;
+#define RSSI_DEFAULT 0
+ __le16 modulation;
+#define PREAMBLE_AUTO cpu_to_le16(0)
+#define PREAMBLE_LONG cpu_to_le16(1)
+#define PREAMBLE_SHORT cpu_to_le16(2)
+ __le16 preamble;
+ __le16 homeProduct;
+ __le16 radioSpecific;
+ /*---------- Aironet Extensions ----------*/
+ u8 nodeName[16];
+ __le16 arlThreshold;
+ __le16 arlDecay;
+ __le16 arlDelay;
+ __le16 _reserved4[1];
+ /*---------- Aironet Extensions ----------*/
+ u8 magicAction;
+#define MAGIC_ACTION_STSCHG 1
+#define MAGIC_ACTION_RESUME 2
+#define MAGIC_IGNORE_MCAST (1<<8)
+#define MAGIC_IGNORE_BCAST (1<<9)
+#define MAGIC_SWITCH_TO_PSP (0<<10)
+#define MAGIC_STAY_IN_CAM (1<<10)
+ u8 magicControl;
+ __le16 autoWake;
+} __packed;
+
+typedef struct StatusRid StatusRid;
+struct StatusRid {
+ __le16 len;
+ u8 mac[ETH_ALEN];
+ __le16 mode;
+ __le16 errorCode;
+ __le16 sigQuality;
+ __le16 SSIDlen;
+ char SSID[32];
+ char apName[16];
+ u8 bssid[4][ETH_ALEN];
+ __le16 beaconPeriod;
+ __le16 dimPeriod;
+ __le16 atimDuration;
+ __le16 hopPeriod;
+ __le16 channelSet;
+ __le16 channel;
+ __le16 hopsToBackbone;
+ __le16 apTotalLoad;
+ __le16 generatedLoad;
+ __le16 accumulatedArl;
+ __le16 signalQuality;
+ __le16 currentXmitRate;
+ __le16 apDevExtensions;
+ __le16 normalizedSignalStrength;
+ __le16 shortPreamble;
+ u8 apIP[4];
+ u8 noisePercent; /* Noise percent in last second */
+ u8 noisedBm; /* Noise dBm in last second */
+ u8 noiseAvePercent; /* Noise percent in last minute */
+ u8 noiseAvedBm; /* Noise dBm in last minute */
+ u8 noiseMaxPercent; /* Highest noise percent in last minute */
+ u8 noiseMaxdBm; /* Highest noise dbm in last minute */
+ __le16 load;
+ u8 carrier[4];
+ __le16 assocStatus;
+#define STAT_NOPACKETS 0
+#define STAT_NOCARRIERSET 10
+#define STAT_GOTCARRIERSET 11
+#define STAT_WRONGSSID 20
+#define STAT_BADCHANNEL 25
+#define STAT_BADBITRATES 30
+#define STAT_BADPRIVACY 35
+#define STAT_APFOUND 40
+#define STAT_APREJECTED 50
+#define STAT_AUTHENTICATING 60
+#define STAT_DEAUTHENTICATED 61
+#define STAT_AUTHTIMEOUT 62
+#define STAT_ASSOCIATING 70
+#define STAT_DEASSOCIATED 71
+#define STAT_ASSOCTIMEOUT 72
+#define STAT_NOTAIROAP 73
+#define STAT_ASSOCIATED 80
+#define STAT_LEAPING 90
+#define STAT_LEAPFAILED 91
+#define STAT_LEAPTIMEDOUT 92
+#define STAT_LEAPCOMPLETE 93
+} __packed;
+
+typedef struct StatsRid StatsRid;
+struct StatsRid {
+ __le16 len;
+ __le16 spacer;
+ __le32 vals[100];
+} __packed;
+
+typedef struct APListRid APListRid;
+struct APListRid {
+ __le16 len;
+ u8 ap[4][ETH_ALEN];
+} __packed;
+
+typedef struct CapabilityRid CapabilityRid;
+struct CapabilityRid {
+ __le16 len;
+ char oui[3];
+ char zero;
+ __le16 prodNum;
+ char manName[32];
+ char prodName[16];
+ char prodVer[8];
+ char factoryAddr[ETH_ALEN];
+ char aironetAddr[ETH_ALEN];
+ __le16 radioType;
+ __le16 country;
+ char callid[ETH_ALEN];
+ char supportedRates[8];
+ char rxDiversity;
+ char txDiversity;
+ __le16 txPowerLevels[8];
+ __le16 hardVer;
+ __le16 hardCap;
+ __le16 tempRange;
+ __le16 softVer;
+ __le16 softSubVer;
+ __le16 interfaceVer;
+ __le16 softCap;
+ __le16 bootBlockVer;
+ __le16 requiredHard;
+ __le16 extSoftCap;
+} __packed;
+
+/* Only present on firmware >= 5.30.17 */
+typedef struct BSSListRidExtra BSSListRidExtra;
+struct BSSListRidExtra {
+ __le16 unknown[4];
+ u8 fixed[12]; /* WLAN management frame */
+ u8 iep[624];
+} __packed;
+
+typedef struct BSSListRid BSSListRid;
+struct BSSListRid {
+ __le16 len;
+ __le16 index; /* First is 0 and 0xffff means end of list */
+#define RADIO_FH 1 /* Frequency hopping radio type */
+#define RADIO_DS 2 /* Direct sequence radio type */
+#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
+ __le16 radioType;
+ u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
+ u8 zero;
+ u8 ssidLen;
+ u8 ssid[32];
+ __le16 dBm;
+#define CAP_ESS cpu_to_le16(1<<0)
+#define CAP_IBSS cpu_to_le16(1<<1)
+#define CAP_PRIVACY cpu_to_le16(1<<4)
+#define CAP_SHORTHDR cpu_to_le16(1<<5)
+ __le16 cap;
+ __le16 beaconInterval;
+ u8 rates[8]; /* Same as rates for config rid */
+ struct { /* For frequency hopping only */
+ __le16 dwell;
+ u8 hopSet;
+ u8 hopPattern;
+ u8 hopIndex;
+ u8 fill;
+ } fh;
+ __le16 dsChannel;
+ __le16 atimWindow;
+
+ /* Only present on firmware >= 5.30.17 */
+ BSSListRidExtra extra;
+} __packed;
+
+typedef struct {
+ BSSListRid bss;
+ struct list_head list;
+} BSSListElement;
+
+typedef struct tdsRssiEntry tdsRssiEntry;
+struct tdsRssiEntry {
+ u8 rssipct;
+ u8 rssidBm;
+} __packed;
+
+typedef struct tdsRssiRid tdsRssiRid;
+struct tdsRssiRid {
+ u16 len;
+ tdsRssiEntry x[256];
+} __packed;
+
+typedef struct MICRid MICRid;
+struct MICRid {
+ __le16 len;
+ __le16 state;
+ __le16 multicastValid;
+ u8 multicast[16];
+ __le16 unicastValid;
+ u8 unicast[16];
+} __packed;
+
+typedef struct MICBuffer MICBuffer;
+struct MICBuffer {
+ __be16 typelen;
+
+ union {
+ u8 snap[8];
+ struct {
+ u8 dsap;
+ u8 ssap;
+ u8 control;
+ u8 orgcode[3];
+ u8 fieldtype[2];
+ } llc;
+ } u;
+ __be32 mic;
+ __be32 seq;
+} __packed;
+
+typedef struct {
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+} etherHead;
+
+#define TXCTL_TXOK (1<<1) /* report if tx is ok */
+#define TXCTL_TXEX (1<<2) /* report if tx fails */
+#define TXCTL_802_3 (0<<3) /* 802.3 packet */
+#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
+#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
+#define TXCTL_LLC (1<<4) /* payload is llc */
+#define TXCTL_RELEASE (0<<5) /* release after completion */
+#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
+
+#define BUSY_FID 0x10000
+
+#ifdef CISCO_EXT
+#define AIROMAGIC 0xa55a
+/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
+#ifdef SIOCIWFIRSTPRIV
+#ifdef SIOCDEVPRIVATE
+#define AIROOLDIOCTL SIOCDEVPRIVATE
+#define AIROOLDIDIFC AIROOLDIOCTL + 1
+#endif /* SIOCDEVPRIVATE */
+#else /* SIOCIWFIRSTPRIV */
+#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
+ * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
+ * only and don't return the modified struct ifreq to the application which
+ * is usually a problem. - Jean II */
+#define AIROIOCTL SIOCIWFIRSTPRIV
+#define AIROIDIFC AIROIOCTL + 1
+
+/* Ioctl constants to be used in airo_ioctl.command */
+
+#define AIROGCAP 0 // Capability rid
+#define AIROGCFG 1 // USED A LOT
+#define AIROGSLIST 2 // System ID list
+#define AIROGVLIST 3 // List of specified AP's
+#define AIROGDRVNAM 4 // NOTUSED
+#define AIROGEHTENC 5 // NOTUSED
+#define AIROGWEPKTMP 6
+#define AIROGWEPKNV 7
+#define AIROGSTAT 8
+#define AIROGSTATSC32 9
+#define AIROGSTATSD32 10
+#define AIROGMICRID 11
+#define AIROGMICSTATS 12
+#define AIROGFLAGS 13
+#define AIROGID 14
+#define AIRORRID 15
+#define AIRORSWVERSION 17
+
+/* Leave gap of 40 commands after AIROGSTATSD32 for future */
+
+#define AIROPCAP AIROGSTATSD32 + 40
+#define AIROPVLIST AIROPCAP + 1
+#define AIROPSLIST AIROPVLIST + 1
+#define AIROPCFG AIROPSLIST + 1
+#define AIROPSIDS AIROPCFG + 1
+#define AIROPAPLIST AIROPSIDS + 1
+#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */
+#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */
+#define AIROPSTCLR AIROPMACOFF + 1
+#define AIROPWEPKEY AIROPSTCLR + 1
+#define AIROPWEPKEYNV AIROPWEPKEY + 1
+#define AIROPLEAPPWD AIROPWEPKEYNV + 1
+#define AIROPLEAPUSR AIROPLEAPPWD + 1
+
+/* Flash codes */
+
+#define AIROFLSHRST AIROPWEPKEYNV + 40
+#define AIROFLSHGCHR AIROFLSHRST + 1
+#define AIROFLSHSTFL AIROFLSHGCHR + 1
+#define AIROFLSHPCHR AIROFLSHSTFL + 1
+#define AIROFLPUTBUF AIROFLSHPCHR + 1
+#define AIRORESTART AIROFLPUTBUF + 1
+
+#define FLASHSIZE 32768
+#define AUXMEMSIZE (256 * 1024)
+
+typedef struct aironet_ioctl {
+ unsigned short command; // What to do
+ unsigned short len; // Len of data
+ unsigned short ridnum; // rid number
+ unsigned char __user *data; // d-data
+} aironet_ioctl;
+
+static const char swversion[] = "2.1";
+#endif /* CISCO_EXT */
+
+#define NUM_MODULES 2
+#define MIC_MSGLEN_MAX 2400
+#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
+#define AIRO_DEF_MTU 2312
+
+typedef struct {
+ u32 size; // size
+ u8 enabled; // MIC enabled or not
+ u32 rxSuccess; // successful packets received
+ u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison
+ u32 rxNotMICed; // pkts dropped due to not being MIC'd
+ u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed
+ u32 rxWrongSequence; // pkts dropped due to sequence number violation
+ u32 reserve[32];
+} mic_statistics;
+
+typedef struct {
+ u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
+ u64 accum; // accumulated mic, reduced to u32 in final()
+ int position; // current position (byte offset) in message
+ union {
+ u8 d8[4];
+ __be32 d32;
+ } part; // saves partial message word across update() calls
+} emmh32_context;
+
+typedef struct {
+ emmh32_context seed; // Context - the seed
+ u32 rx; // Received sequence number
+ u32 tx; // Tx sequence number
+ u32 window; // Start of window
+ u8 valid; // Flag to say if context is valid or not
+ u8 key[16];
+} miccntx;
+
+typedef struct {
+ miccntx mCtx; // Multicast context
+ miccntx uCtx; // Unicast context
+} mic_module;
+
+typedef struct {
+ unsigned int rid: 16;
+ unsigned int len: 15;
+ unsigned int valid: 1;
+ dma_addr_t host_addr;
+} Rid;
+
+typedef struct {
+ unsigned int offset: 15;
+ unsigned int eoc: 1;
+ unsigned int len: 15;
+ unsigned int valid: 1;
+ dma_addr_t host_addr;
+} TxFid;
+
+struct rx_hdr {
+ __le16 status, len;
+ u8 rssi[2];
+ u8 rate;
+ u8 freq;
+ __le16 tmp[4];
+} __packed;
+
+typedef struct {
+ unsigned int ctl: 15;
+ unsigned int rdy: 1;
+ unsigned int len: 15;
+ unsigned int valid: 1;
+ dma_addr_t host_addr;
+} RxFid;
+
+/*
+ * Host receive descriptor
+ */
+typedef struct {
+ unsigned char __iomem *card_ram_off; /* offset into card memory of the
+ desc */
+ RxFid rx_desc; /* card receive descriptor */
+ char *virtual_host_addr; /* virtual address of host receive
+ buffer */
+ int pending;
+} HostRxDesc;
+
+/*
+ * Host transmit descriptor
+ */
+typedef struct {
+ unsigned char __iomem *card_ram_off; /* offset into card memory of the
+ desc */
+ TxFid tx_desc; /* card transmit descriptor */
+ char *virtual_host_addr; /* virtual address of host receive
+ buffer */
+ int pending;
+} HostTxDesc;
+
+/*
+ * Host RID descriptor
+ */
+typedef struct {
+ unsigned char __iomem *card_ram_off; /* offset into card memory of the
+ descriptor */
+ Rid rid_desc; /* card RID descriptor */
+ char *virtual_host_addr; /* virtual address of host receive
+ buffer */
+} HostRidDesc;
+
+typedef struct {
+ u16 sw0;
+ u16 sw1;
+ u16 status;
+ u16 len;
+#define HOST_SET (1 << 0)
+#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
+#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
+#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
+#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
+#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
+#define HOST_CLR_AID (1 << 7) /* clear AID failure */
+#define HOST_RTS (1 << 9) /* Force RTS use */
+#define HOST_SHORT (1 << 10) /* Do short preamble */
+ u16 ctl;
+ u16 aid;
+ u16 retries;
+ u16 fill;
+} TxCtlHdr;
+
+typedef struct {
+ u16 ctl;
+ u16 duration;
+ char addr1[6];
+ char addr2[6];
+ char addr3[6];
+ u16 seq;
+ char addr4[6];
+} WifiHdr;
+
+
+typedef struct {
+ TxCtlHdr ctlhdr;
+ u16 fill1;
+ u16 fill2;
+ WifiHdr wifihdr;
+ u16 gaplen;
+ u16 status;
+} WifiCtlHdr;
+
+static WifiCtlHdr wifictlhdr8023 = {
+ .ctlhdr = {
+ .ctl = HOST_DONT_RLSE,
+ }
+};
+
+// A few details needed for WEP (Wireless Equivalent Privacy)
+#define MAX_KEY_SIZE 13 // 128 (?) bits
+#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP
+typedef struct wep_key_t {
+ u16 len;
+ u8 key[16]; /* 40-bit and 104-bit keys */
+} wep_key_t;
+
+/* List of Wireless Handlers (new API) */
+static const struct iw_handler_def airo_handler_def;
+
+static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
+
+struct airo_info;
+
+static int get_dec_u16( char *buffer, int *start, int limit );
+static void OUT4500( struct airo_info *, u16 register, u16 value );
+static unsigned short IN4500( struct airo_info *, u16 register );
+static u16 setup_card(struct airo_info*, u8 *mac, int lock);
+static int enable_MAC(struct airo_info *ai, int lock);
+static void disable_MAC(struct airo_info *ai, int lock);
+static void enable_interrupts(struct airo_info*);
+static void disable_interrupts(struct airo_info*);
+static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
+static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
+static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
+ int whichbap);
+static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
+ int whichbap);
+static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
+ int whichbap);
+static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
+static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
+static int PC4500_writerid(struct airo_info*, u16 rid, const void
+ *pBuf, int len, int lock);
+static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
+ int len, int dummy );
+static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
+static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
+static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
+
+static int mpi_send_packet (struct net_device *dev);
+static void mpi_unmap_card(struct pci_dev *pci);
+static void mpi_receive_802_3(struct airo_info *ai);
+static void mpi_receive_802_11(struct airo_info *ai);
+static int waitbusy (struct airo_info *ai);
+
+static irqreturn_t airo_interrupt( int irq, void* dev_id);
+static int airo_thread(void *data);
+static void timer_func( struct net_device *dev );
+static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
+static void airo_read_wireless_stats (struct airo_info *local);
+#ifdef CISCO_EXT
+static int readrids(struct net_device *dev, aironet_ioctl *comp);
+static int writerids(struct net_device *dev, aironet_ioctl *comp);
+static int flashcard(struct net_device *dev, aironet_ioctl *comp);
+#endif /* CISCO_EXT */
+static void micinit(struct airo_info *ai);
+static int micsetup(struct airo_info *ai);
+static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
+static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
+
+static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
+static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
+
+static void airo_networks_free(struct airo_info *ai);
+
+struct airo_info {
+ struct net_device *dev;
+ struct list_head dev_list;
+ /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we
+ use the high bit to mark whether it is in use. */
+#define MAX_FIDS 6
+#define MPI_MAX_FIDS 1
+ u32 fids[MAX_FIDS];
+ ConfigRid config;
+ char keyindex; // Used with auto wep
+ char defindex; // Used with auto wep
+ struct proc_dir_entry *proc_entry;
+ spinlock_t aux_lock;
+#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
+#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
+#define FLAG_RADIO_MASK 0x03
+#define FLAG_ENABLED 2
+#define FLAG_ADHOC 3 /* Needed by MIC */
+#define FLAG_MIC_CAPABLE 4
+#define FLAG_UPDATE_MULTI 5
+#define FLAG_UPDATE_UNI 6
+#define FLAG_802_11 7
+#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
+#define FLAG_PENDING_XMIT 9
+#define FLAG_PENDING_XMIT11 10
+#define FLAG_MPI 11
+#define FLAG_REGISTERED 12
+#define FLAG_COMMIT 13
+#define FLAG_RESET 14
+#define FLAG_FLASHING 15
+#define FLAG_WPA_CAPABLE 16
+ unsigned long flags;
+#define JOB_DIE 0
+#define JOB_XMIT 1
+#define JOB_XMIT11 2
+#define JOB_STATS 3
+#define JOB_PROMISC 4
+#define JOB_MIC 5
+#define JOB_EVENT 6
+#define JOB_AUTOWEP 7
+#define JOB_WSTATS 8
+#define JOB_SCAN_RESULTS 9
+ unsigned long jobs;
+ int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
+ int whichbap);
+ unsigned short *flash;
+ tdsRssiEntry *rssi;
+ struct task_struct *list_bss_task;
+ struct task_struct *airo_thread_task;
+ struct semaphore sem;
+ wait_queue_head_t thr_wait;
+ unsigned long expires;
+ struct {
+ struct sk_buff *skb;
+ int fid;
+ } xmit, xmit11;
+ struct net_device *wifidev;
+ struct iw_statistics wstats; // wireless stats
+ unsigned long scan_timeout; /* Time scan should be read */
+ struct iw_spy_data spy_data;
+ struct iw_public_data wireless_data;
+ /* MIC stuff */
+ struct crypto_cipher *tfm;
+ mic_module mod[2];
+ mic_statistics micstats;
+ HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
+ HostTxDesc txfids[MPI_MAX_FIDS];
+ HostRidDesc config_desc;
+ unsigned long ridbus; // phys addr of config_desc
+ struct sk_buff_head txq;// tx queue used by mpi350 code
+ struct pci_dev *pci;
+ unsigned char __iomem *pcimem;
+ unsigned char __iomem *pciaux;
+ unsigned char *shared;
+ dma_addr_t shared_dma;
+ pm_message_t power;
+ SsidRid *SSID;
+ APListRid APList;
+#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
+ char proc_name[IFNAMSIZ];
+
+ int wep_capable;
+ int max_wep_idx;
+ int last_auth;
+
+ /* WPA-related stuff */
+ unsigned int bssListFirst;
+ unsigned int bssListNext;
+ unsigned int bssListRidLen;
+
+ struct list_head network_list;
+ struct list_head network_free_list;
+ BSSListElement *networks;
+};
+
+static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
+ int whichbap)
+{
+ return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
+}
+
+static int setup_proc_entry( struct net_device *dev,
+ struct airo_info *apriv );
+static int takedown_proc_entry( struct net_device *dev,
+ struct airo_info *apriv );
+
+static int cmdreset(struct airo_info *ai);
+static int setflashmode (struct airo_info *ai);
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
+static int flashputbuf(struct airo_info *ai);
+static int flashrestart(struct airo_info *ai,struct net_device *dev);
+
+#define airo_print(type, name, fmt, args...) \
+ printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
+
+#define airo_print_info(name, fmt, args...) \
+ airo_print(KERN_INFO, name, fmt, ##args)
+
+#define airo_print_dbg(name, fmt, args...) \
+ airo_print(KERN_DEBUG, name, fmt, ##args)
+
+#define airo_print_warn(name, fmt, args...) \
+ airo_print(KERN_WARNING, name, fmt, ##args)
+
+#define airo_print_err(name, fmt, args...) \
+ airo_print(KERN_ERR, name, fmt, ##args)
+
+#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
+
+/***********************************************************************
+ * MIC ROUTINES *
+ ***********************************************************************
+ */
+
+static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
+static void MoveWindow(miccntx *context, u32 micSeq);
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
+ struct crypto_cipher *tfm);
+static void emmh32_init(emmh32_context *context);
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
+static void emmh32_final(emmh32_context *context, u8 digest[4]);
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
+
+static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len,
+ struct crypto_cipher *tfm)
+{
+ /* If the current MIC context is valid and its key is the same as
+ * the MIC register, there's nothing to do.
+ */
+ if (cur->valid && (memcmp(cur->key, key, key_len) == 0))
+ return;
+
+ /* Age current mic Context */
+ memcpy(old, cur, sizeof(*cur));
+
+ /* Initialize new context */
+ memcpy(cur->key, key, key_len);
+ cur->window = 33; /* Window always points to the middle */
+ cur->rx = 0; /* Rx Sequence numbers */
+ cur->tx = 0; /* Tx sequence numbers */
+ cur->valid = 1; /* Key is now valid */
+
+ /* Give key to mic seed */
+ emmh32_setseed(&cur->seed, key, key_len, tfm);
+}
+
+/* micinit - Initialize mic seed */
+
+static void micinit(struct airo_info *ai)
+{
+ MICRid mic_rid;
+
+ clear_bit(JOB_MIC, &ai->jobs);
+ PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
+ up(&ai->sem);
+
+ ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0;
+ if (!ai->micstats.enabled) {
+ /* So next time we have a valid key and mic is enabled, we will
+ * update the sequence number if the key is the same as before.
+ */
+ ai->mod[0].uCtx.valid = 0;
+ ai->mod[0].mCtx.valid = 0;
+ return;
+ }
+
+ if (mic_rid.multicastValid) {
+ age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx,
+ mic_rid.multicast, sizeof(mic_rid.multicast),
+ ai->tfm);
+ }
+
+ if (mic_rid.unicastValid) {
+ age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx,
+ mic_rid.unicast, sizeof(mic_rid.unicast),
+ ai->tfm);
+ }
+}
+
+/* micsetup - Get ready for business */
+
+static int micsetup(struct airo_info *ai) {
+ int i;
+
+ if (ai->tfm == NULL)
+ ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+
+ if (IS_ERR(ai->tfm)) {
+ airo_print_err(ai->dev->name, "failed to load transform for AES");
+ ai->tfm = NULL;
+ return ERROR;
+ }
+
+ for (i=0; i < NUM_MODULES; i++) {
+ memset(&ai->mod[i].mCtx,0,sizeof(miccntx));
+ memset(&ai->mod[i].uCtx,0,sizeof(miccntx));
+ }
+ return SUCCESS;
+}
+
+static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
+
+/*===========================================================================
+ * Description: Mic a packet
+ *
+ * Inputs: etherHead * pointer to an 802.3 frame
+ *
+ * Returns: BOOLEAN if successful, otherwise false.
+ * PacketTxLen will be updated with the mic'd packets size.
+ *
+ * Caveats: It is assumed that the frame buffer will already
+ * be big enough to hold the largets mic message possible.
+ * (No memory allocation is done here).
+ *
+ * Author: sbraneky (10/15/01)
+ * Merciless hacks by rwilcher (1/14/02)
+ */
+
+static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen)
+{
+ miccntx *context;
+
+ // Determine correct context
+ // If not adhoc, always use unicast key
+
+ if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
+ context = &ai->mod[0].mCtx;
+ else
+ context = &ai->mod[0].uCtx;
+
+ if (!context->valid)
+ return ERROR;
+
+ mic->typelen = htons(payLen + 16); //Length of Mic'd packet
+
+ memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
+
+ // Add Tx sequence
+ mic->seq = htonl(context->tx);
+ context->tx += 2;
+
+ emmh32_init(&context->seed); // Mic the packet
+ emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA
+ emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap
+ emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ
+ emmh32_update(&context->seed,(u8*)(frame + 1),payLen); //payload
+ emmh32_final(&context->seed, (u8*)&mic->mic);
+
+ /* New Type/length ?????????? */
+ mic->typelen = 0; //Let NIC know it could be an oversized packet
+ return SUCCESS;
+}
+
+typedef enum {
+ NONE,
+ NOMIC,
+ NOMICPLUMMED,
+ SEQUENCE,
+ INCORRECTMIC,
+} mic_error;
+
+/*===========================================================================
+ * Description: Decapsulates a MIC'd packet and returns the 802.3 packet
+ * (removes the MIC stuff) if packet is a valid packet.
+ *
+ * Inputs: etherHead pointer to the 802.3 packet
+ *
+ * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
+ *
+ * Author: sbraneky (10/15/01)
+ * Merciless hacks by rwilcher (1/14/02)
+ *---------------------------------------------------------------------------
+ */
+
+static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
+{
+ int i;
+ u32 micSEQ;
+ miccntx *context;
+ u8 digest[4];
+ mic_error micError = NONE;
+
+ // Check if the packet is a Mic'd packet
+
+ if (!ai->micstats.enabled) {
+ //No Mic set or Mic OFF but we received a MIC'd packet.
+ if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
+ ai->micstats.rxMICPlummed++;
+ return ERROR;
+ }
+ return SUCCESS;
+ }
+
+ if (ntohs(mic->typelen) == 0x888E)
+ return SUCCESS;
+
+ if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
+ // Mic enabled but packet isn't Mic'd
+ ai->micstats.rxMICPlummed++;
+ return ERROR;
+ }
+
+ micSEQ = ntohl(mic->seq); //store SEQ as CPU order
+
+ //At this point we a have a mic'd packet and mic is enabled
+ //Now do the mic error checking.
+
+ //Receive seq must be odd
+ if ( (micSEQ & 1) == 0 ) {
+ ai->micstats.rxWrongSequence++;
+ return ERROR;
+ }
+
+ for (i = 0; i < NUM_MODULES; i++) {
+ int mcast = eth->da[0] & 1;
+ //Determine proper context
+ context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
+
+ //Make sure context is valid
+ if (!context->valid) {
+ if (i == 0)
+ micError = NOMICPLUMMED;
+ continue;
+ }
+ //DeMic it
+
+ if (!mic->typelen)
+ mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
+
+ emmh32_init(&context->seed);
+ emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
+ emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
+ emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq));
+ emmh32_update(&context->seed, (u8 *)(eth + 1),payLen);
+ //Calculate MIC
+ emmh32_final(&context->seed, digest);
+
+ if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
+ //Invalid Mic
+ if (i == 0)
+ micError = INCORRECTMIC;
+ continue;
+ }
+
+ //Check Sequence number if mics pass
+ if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
+ ai->micstats.rxSuccess++;
+ return SUCCESS;
+ }
+ if (i == 0)
+ micError = SEQUENCE;
+ }
+
+ // Update statistics
+ switch (micError) {
+ case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break;
+ case SEQUENCE: ai->micstats.rxWrongSequence++; break;
+ case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
+ case NONE: break;
+ case NOMIC: break;
+ }
+ return ERROR;
+}
+
+/*===========================================================================
+ * Description: Checks the Rx Seq number to make sure it is valid
+ * and hasn't already been received
+ *
+ * Inputs: miccntx - mic context to check seq against
+ * micSeq - the Mic seq number
+ *
+ * Returns: TRUE if valid otherwise FALSE.
+ *
+ * Author: sbraneky (10/15/01)
+ * Merciless hacks by rwilcher (1/14/02)
+ *---------------------------------------------------------------------------
+ */
+
+static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq)
+{
+ u32 seq,index;
+
+ //Allow for the ap being rebooted - if it is then use the next
+ //sequence number of the current sequence number - might go backwards
+
+ if (mcast) {
+ if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
+ clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
+ context->window = (micSeq > 33) ? micSeq : 33;
+ context->rx = 0; // Reset rx
+ }
+ } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
+ clear_bit (FLAG_UPDATE_UNI, &ai->flags);
+ context->window = (micSeq > 33) ? micSeq : 33; // Move window
+ context->rx = 0; // Reset rx
+ }
+
+ //Make sequence number relative to START of window
+ seq = micSeq - (context->window - 33);
+
+ //Too old of a SEQ number to check.
+ if ((s32)seq < 0)
+ return ERROR;
+
+ if ( seq > 64 ) {
+ //Window is infinite forward
+ MoveWindow(context,micSeq);
+ return SUCCESS;
+ }
+
+ // We are in the window. Now check the context rx bit to see if it was already sent
+ seq >>= 1; //divide by 2 because we only have odd numbers
+ index = 1 << seq; //Get an index number
+
+ if (!(context->rx & index)) {
+ //micSEQ falls inside the window.
+ //Add seqence number to the list of received numbers.
+ context->rx |= index;
+
+ MoveWindow(context,micSeq);
+
+ return SUCCESS;
+ }
+ return ERROR;
+}
+
+static void MoveWindow(miccntx *context, u32 micSeq)
+{
+ u32 shift;
+
+ //Move window if seq greater than the middle of the window
+ if (micSeq > context->window) {
+ shift = (micSeq - context->window) >> 1;
+
+ //Shift out old
+ if (shift < 32)
+ context->rx >>= shift;
+ else
+ context->rx = 0;
+
+ context->window = micSeq; //Move window
+ }
+}
+
+/*==============================================*/
+/*========== EMMH ROUTINES ====================*/
+/*==============================================*/
+
+/* mic accumulate */
+#define MIC_ACCUM(val) \
+ context->accum += (u64)(val) * context->coeff[coeff_position++];
+
+static unsigned char aes_counter[16];
+
+/* expand the key to fill the MMH coefficient array */
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
+ struct crypto_cipher *tfm)
+{
+ /* take the keying material, expand if necessary, truncate at 16-bytes */
+ /* run through AES counter mode to generate context->coeff[] */
+
+ int i,j;
+ u32 counter;
+ u8 *cipher, plain[16];
+
+ crypto_cipher_setkey(tfm, pkey, 16);
+ counter = 0;
+ for (i = 0; i < ARRAY_SIZE(context->coeff); ) {
+ aes_counter[15] = (u8)(counter >> 0);
+ aes_counter[14] = (u8)(counter >> 8);
+ aes_counter[13] = (u8)(counter >> 16);
+ aes_counter[12] = (u8)(counter >> 24);
+ counter++;
+ memcpy (plain, aes_counter, 16);
+ crypto_cipher_encrypt_one(tfm, plain, plain);
+ cipher = plain;
+ for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) {
+ context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]);
+ j += 4;
+ }
+ }
+}
+
+/* prepare for calculation of a new mic */
+static void emmh32_init(emmh32_context *context)
+{
+ /* prepare for new mic calculation */
+ context->accum = 0;
+ context->position = 0;
+}
+
+/* add some bytes to the mic calculation */
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
+{
+ int coeff_position, byte_position;
+
+ if (len == 0) return;
+
+ coeff_position = context->position >> 2;
+
+ /* deal with partial 32-bit word left over from last update */
+ byte_position = context->position & 3;
+ if (byte_position) {
+ /* have a partial word in part to deal with */
+ do {
+ if (len == 0) return;
+ context->part.d8[byte_position++] = *pOctets++;
+ context->position++;
+ len--;
+ } while (byte_position < 4);
+ MIC_ACCUM(ntohl(context->part.d32));
+ }
+
+ /* deal with full 32-bit words */
+ while (len >= 4) {
+ MIC_ACCUM(ntohl(*(__be32 *)pOctets));
+ context->position += 4;
+ pOctets += 4;
+ len -= 4;
+ }
+
+ /* deal with partial 32-bit word that will be left over from this update */
+ byte_position = 0;
+ while (len > 0) {
+ context->part.d8[byte_position++] = *pOctets++;
+ context->position++;
+ len--;
+ }
+}
+
+/* mask used to zero empty bytes for final partial word */
+static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
+
+/* calculate the mic */
+static void emmh32_final(emmh32_context *context, u8 digest[4])
+{
+ int coeff_position, byte_position;
+ u32 val;
+
+ u64 sum, utmp;
+ s64 stmp;
+
+ coeff_position = context->position >> 2;
+
+ /* deal with partial 32-bit word left over from last update */
+ byte_position = context->position & 3;
+ if (byte_position) {
+ /* have a partial word in part to deal with */
+ val = ntohl(context->part.d32);
+ MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */
+ }
+
+ /* reduce the accumulated u64 to a 32-bit MIC */
+ sum = context->accum;
+ stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15);
+ utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
+ sum = utmp & 0xffffffffLL;
+ if (utmp > 0x10000000fLL)
+ sum -= 15;
+
+ val = (u32)sum;
+ digest[0] = (val>>24) & 0xFF;
+ digest[1] = (val>>16) & 0xFF;
+ digest[2] = (val>>8) & 0xFF;
+ digest[3] = val & 0xFF;
+}
+
+static int readBSSListRid(struct airo_info *ai, int first,
+ BSSListRid *list)
+{
+ Cmd cmd;
+ Resp rsp;
+
+ if (first == 1) {
+ if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd=CMD_LISTBSS;
+ if (down_interruptible(&ai->sem))
+ return -ERESTARTSYS;
+ ai->list_bss_task = current;
+ issuecommand(ai, &cmd, &rsp);
+ up(&ai->sem);
+ /* Let the command take effect */
+ schedule_timeout_uninterruptible(3 * HZ);
+ ai->list_bss_task = NULL;
+ }
+ return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
+ list, ai->bssListRidLen, 1);
+}
+
+static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
+{
+ return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
+ wkr, sizeof(*wkr), lock);
+}
+
+static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
+{
+ int rc;
+ rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
+ if (rc!=SUCCESS)
+ airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
+ if (perm) {
+ rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
+ if (rc!=SUCCESS)
+ airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
+ }
+ return rc;
+}
+
+static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
+{
+ return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
+}
+
+static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
+{
+ return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
+}
+
+static int readConfigRid(struct airo_info *ai, int lock)
+{
+ int rc;
+ ConfigRid cfg;
+
+ if (ai->config.len)
+ return SUCCESS;
+
+ rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
+ if (rc != SUCCESS)
+ return rc;
+
+ ai->config = cfg;
+ return SUCCESS;
+}
+
+static inline void checkThrottle(struct airo_info *ai)
+{
+ int i;
+/* Old hardware had a limit on encryption speed */
+ if (ai->config.authType != AUTH_OPEN && maxencrypt) {
+ for(i=0; i<8; i++) {
+ if (ai->config.rates[i] > maxencrypt) {
+ ai->config.rates[i] = 0;
+ }
+ }
+ }
+}
+
+static int writeConfigRid(struct airo_info *ai, int lock)
+{
+ ConfigRid cfgr;
+
+ if (!test_bit (FLAG_COMMIT, &ai->flags))
+ return SUCCESS;
+
+ clear_bit (FLAG_COMMIT, &ai->flags);
+ clear_bit (FLAG_RESET, &ai->flags);
+ checkThrottle(ai);
+ cfgr = ai->config;
+
+ if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
+ set_bit(FLAG_ADHOC, &ai->flags);
+ else
+ clear_bit(FLAG_ADHOC, &ai->flags);
+
+ return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
+}
+
+static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
+{
+ return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
+}
+
+static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
+{
+ return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
+}
+
+static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
+{
+ return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
+}
+
+static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
+{
+ return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
+}
+
+static void try_auto_wep(struct airo_info *ai)
+{
+ if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) {
+ ai->expires = RUN_AT(3*HZ);
+ wake_up_interruptible(&ai->thr_wait);
+ }
+}
+
+static int airo_open(struct net_device *dev) {
+ struct airo_info *ai = dev->ml_priv;
+ int rc = 0;
+
+ if (test_bit(FLAG_FLASHING, &ai->flags))
+ return -EIO;
+
+ /* Make sure the card is configured.
+ * Wireless Extensions may postpone config changes until the card
+ * is open (to pipeline changes and speed-up card setup). If
+ * those changes are not yet committed, do it now - Jean II */
+ if (test_bit(FLAG_COMMIT, &ai->flags)) {
+ disable_MAC(ai, 1);
+ writeConfigRid(ai, 1);
+ }
+
+ if (ai->wifidev != dev) {
+ clear_bit(JOB_DIE, &ai->jobs);
+ ai->airo_thread_task = kthread_run(airo_thread, dev, "%s",
+ dev->name);
+ if (IS_ERR(ai->airo_thread_task))
+ return (int)PTR_ERR(ai->airo_thread_task);
+
+ rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
+ dev->name, dev);
+ if (rc) {
+ airo_print_err(dev->name,
+ "register interrupt %d failed, rc %d",
+ dev->irq, rc);
+ set_bit(JOB_DIE, &ai->jobs);
+ kthread_stop(ai->airo_thread_task);
+ return rc;
+ }
+
+ /* Power on the MAC controller (which may have been disabled) */
+ clear_bit(FLAG_RADIO_DOWN, &ai->flags);
+ enable_interrupts(ai);
+
+ try_auto_wep(ai);
+ }
+ enable_MAC(ai, 1);
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+static netdev_tx_t mpi_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ int npacks, pending;
+ unsigned long flags;
+ struct airo_info *ai = dev->ml_priv;
+
+ if (!skb) {
+ airo_print_err(dev->name, "%s: skb == NULL!",__func__);
+ return NETDEV_TX_OK;
+ }
+ npacks = skb_queue_len (&ai->txq);
+
+ if (npacks >= MAXTXQ - 1) {
+ netif_stop_queue (dev);
+ if (npacks > MAXTXQ) {
+ dev->stats.tx_fifo_errors++;
+ return NETDEV_TX_BUSY;
+ }
+ skb_queue_tail (&ai->txq, skb);
+ return NETDEV_TX_OK;
+ }
+
+ spin_lock_irqsave(&ai->aux_lock, flags);
+ skb_queue_tail (&ai->txq, skb);
+ pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
+ spin_unlock_irqrestore(&ai->aux_lock,flags);
+ netif_wake_queue (dev);
+
+ if (pending == 0) {
+ set_bit(FLAG_PENDING_XMIT, &ai->flags);
+ mpi_send_packet (dev);
+ }
+ return NETDEV_TX_OK;
+}
+
+/*
+ * @mpi_send_packet
+ *
+ * Attempt to transmit a packet. Can be called from interrupt
+ * or transmit . return number of packets we tried to send
+ */
+
+static int mpi_send_packet (struct net_device *dev)
+{
+ struct sk_buff *skb;
+ unsigned char *buffer;
+ s16 len;
+ __le16 *payloadLen;
+ struct airo_info *ai = dev->ml_priv;
+ u8 *sendbuf;
+
+ /* get a packet to send */
+
+ if ((skb = skb_dequeue(&ai->txq)) == NULL) {
+ airo_print_err(dev->name,
+ "%s: Dequeue'd zero in send_packet()",
+ __func__);
+ return 0;
+ }
+
+ /* check min length*/
+ len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ buffer = skb->data;
+
+ ai->txfids[0].tx_desc.offset = 0;
+ ai->txfids[0].tx_desc.valid = 1;
+ ai->txfids[0].tx_desc.eoc = 1;
+ ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
+
+/*
+ * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
+ * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
+ * is immediately after it. ------------------------------------------------
+ * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
+ * ------------------------------------------------
+ */
+
+ memcpy(ai->txfids[0].virtual_host_addr,
+ (char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
+
+ payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
+ sizeof(wifictlhdr8023));
+ sendbuf = ai->txfids[0].virtual_host_addr +
+ sizeof(wifictlhdr8023) + 2 ;
+
+ /*
+ * Firmware automatically puts 802 header on so
+ * we don't need to account for it in the length
+ */
+ if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
+ (ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
+ MICBuffer pMic;
+
+ if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
+ return ERROR;
+
+ *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
+ ai->txfids[0].tx_desc.len += sizeof(pMic);
+ /* copy data into airo dma buffer */
+ memcpy (sendbuf, buffer, sizeof(etherHead));
+ buffer += sizeof(etherHead);
+ sendbuf += sizeof(etherHead);
+ memcpy (sendbuf, &pMic, sizeof(pMic));
+ sendbuf += sizeof(pMic);
+ memcpy (sendbuf, buffer, len - sizeof(etherHead));
+ } else {
+ *payloadLen = cpu_to_le16(len - sizeof(etherHead));
+
+ dev->trans_start = jiffies;
+
+ /* copy data into airo dma buffer */
+ memcpy(sendbuf, buffer, len);
+ }
+
+ memcpy_toio(ai->txfids[0].card_ram_off,
+ &ai->txfids[0].tx_desc, sizeof(TxFid));
+
+ OUT4500(ai, EVACK, 8);
+
+ dev_kfree_skb_any(skb);
+ return 1;
+}
+
+static void get_tx_error(struct airo_info *ai, s32 fid)
+{
+ __le16 status;
+
+ if (fid < 0)
+ status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
+ else {
+ if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
+ return;
+ bap_read(ai, &status, 2, BAP0);
+ }
+ if (le16_to_cpu(status) & 2) /* Too many retries */
+ ai->dev->stats.tx_aborted_errors++;
+ if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
+ ai->dev->stats.tx_heartbeat_errors++;
+ if (le16_to_cpu(status) & 8) /* Aid fail */
+ { }
+ if (le16_to_cpu(status) & 0x10) /* MAC disabled */
+ ai->dev->stats.tx_carrier_errors++;
+ if (le16_to_cpu(status) & 0x20) /* Association lost */
+ { }
+ /* We produce a TXDROP event only for retry or lifetime
+ * exceeded, because that's the only status that really mean
+ * that this particular node went away.
+ * Other errors means that *we* screwed up. - Jean II */
+ if ((le16_to_cpu(status) & 2) ||
+ (le16_to_cpu(status) & 4)) {
+ union iwreq_data wrqu;
+ char junk[0x18];
+
+ /* Faster to skip over useless data than to do
+ * another bap_setup(). We are at offset 0x6 and
+ * need to go to 0x18 and read 6 bytes - Jean II */
+ bap_read(ai, (__le16 *) junk, 0x18, BAP0);
+
+ /* Copy 802.11 dest address.
+ * We use the 802.11 header because the frame may
+ * not be 802.3 or may be mangled...
+ * In Ad-Hoc mode, it will be the node address.
+ * In managed mode, it will be most likely the AP addr
+ * User space will figure out how to convert it to
+ * whatever it needs (IP address or else).
+ * - Jean II */
+ memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+
+ /* Send event to user space */
+ wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
+ }
+}
+
+static void airo_end_xmit(struct net_device *dev) {
+ u16 status;
+ int i;
+ struct airo_info *priv = dev->ml_priv;
+ struct sk_buff *skb = priv->xmit.skb;
+ int fid = priv->xmit.fid;
+ u32 *fids = priv->fids;
+
+ clear_bit(JOB_XMIT, &priv->jobs);
+ clear_bit(FLAG_PENDING_XMIT, &priv->flags);
+ status = transmit_802_3_packet (priv, fids[fid], skb->data);
+ up(&priv->sem);
+
+ i = 0;
+ if ( status == SUCCESS ) {
+ dev->trans_start = jiffies;
+ for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
+ } else {
+ priv->fids[fid] &= 0xffff;
+ dev->stats.tx_window_errors++;
+ }
+ if (i < MAX_FIDS / 2)
+ netif_wake_queue(dev);
+ dev_kfree_skb(skb);
+}
+
+static netdev_tx_t airo_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ s16 len;
+ int i, j;
+ struct airo_info *priv = dev->ml_priv;
+ u32 *fids = priv->fids;
+
+ if ( skb == NULL ) {
+ airo_print_err(dev->name, "%s: skb == NULL!", __func__);
+ return NETDEV_TX_OK;
+ }
+
+ /* Find a vacant FID */
+ for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ );
+ for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ );
+
+ if ( j >= MAX_FIDS / 2 ) {
+ netif_stop_queue(dev);
+
+ if (i == MAX_FIDS / 2) {
+ dev->stats.tx_fifo_errors++;
+ return NETDEV_TX_BUSY;
+ }
+ }
+ /* check min length*/
+ len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ /* Mark fid as used & save length for later */
+ fids[i] |= (len << 16);
+ priv->xmit.skb = skb;
+ priv->xmit.fid = i;
+ if (down_trylock(&priv->sem) != 0) {
+ set_bit(FLAG_PENDING_XMIT, &priv->flags);
+ netif_stop_queue(dev);
+ set_bit(JOB_XMIT, &priv->jobs);
+ wake_up_interruptible(&priv->thr_wait);
+ } else
+ airo_end_xmit(dev);
+ return NETDEV_TX_OK;
+}
+
+static void airo_end_xmit11(struct net_device *dev) {
+ u16 status;
+ int i;
+ struct airo_info *priv = dev->ml_priv;
+ struct sk_buff *skb = priv->xmit11.skb;
+ int fid = priv->xmit11.fid;
+ u32 *fids = priv->fids;
+
+ clear_bit(JOB_XMIT11, &priv->jobs);
+ clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
+ status = transmit_802_11_packet (priv, fids[fid], skb->data);
+ up(&priv->sem);
+
+ i = MAX_FIDS / 2;
+ if ( status == SUCCESS ) {
+ dev->trans_start = jiffies;
+ for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
+ } else {
+ priv->fids[fid] &= 0xffff;
+ dev->stats.tx_window_errors++;
+ }
+ if (i < MAX_FIDS)
+ netif_wake_queue(dev);
+ dev_kfree_skb(skb);
+}
+
+static netdev_tx_t airo_start_xmit11(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ s16 len;
+ int i, j;
+ struct airo_info *priv = dev->ml_priv;
+ u32 *fids = priv->fids;
+
+ if (test_bit(FLAG_MPI, &priv->flags)) {
+ /* Not implemented yet for MPI350 */
+ netif_stop_queue(dev);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if ( skb == NULL ) {
+ airo_print_err(dev->name, "%s: skb == NULL!", __func__);
+ return NETDEV_TX_OK;
+ }
+
+ /* Find a vacant FID */
+ for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ );
+ for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ );
+
+ if ( j >= MAX_FIDS ) {
+ netif_stop_queue(dev);
+
+ if (i == MAX_FIDS) {
+ dev->stats.tx_fifo_errors++;
+ return NETDEV_TX_BUSY;
+ }
+ }
+ /* check min length*/
+ len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ /* Mark fid as used & save length for later */
+ fids[i] |= (len << 16);
+ priv->xmit11.skb = skb;
+ priv->xmit11.fid = i;
+ if (down_trylock(&priv->sem) != 0) {
+ set_bit(FLAG_PENDING_XMIT11, &priv->flags);
+ netif_stop_queue(dev);
+ set_bit(JOB_XMIT11, &priv->jobs);
+ wake_up_interruptible(&priv->thr_wait);
+ } else
+ airo_end_xmit11(dev);
+ return NETDEV_TX_OK;
+}
+
+static void airo_read_stats(struct net_device *dev)
+{
+ struct airo_info *ai = dev->ml_priv;
+ StatsRid stats_rid;
+ __le32 *vals = stats_rid.vals;
+
+ clear_bit(JOB_STATS, &ai->jobs);
+ if (ai->power.event) {
+ up(&ai->sem);
+ return;
+ }
+ readStatsRid(ai, &stats_rid, RID_STATS, 0);
+ up(&ai->sem);
+
+ dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
+ le32_to_cpu(vals[45]);
+ dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
+ le32_to_cpu(vals[41]);
+ dev->stats.rx_bytes = le32_to_cpu(vals[92]);
+ dev->stats.tx_bytes = le32_to_cpu(vals[91]);
+ dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
+ le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
+ dev->stats.tx_errors = le32_to_cpu(vals[42]) +
+ dev->stats.tx_fifo_errors;
+ dev->stats.multicast = le32_to_cpu(vals[43]);
+ dev->stats.collisions = le32_to_cpu(vals[89]);
+
+ /* detailed rx_errors: */
+ dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
+ dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
+ dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
+ dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
+}
+
+static struct net_device_stats *airo_get_stats(struct net_device *dev)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ if (!test_bit(JOB_STATS, &local->jobs)) {
+ /* Get stats out of the card if available */
+ if (down_trylock(&local->sem) != 0) {
+ set_bit(JOB_STATS, &local->jobs);
+ wake_up_interruptible(&local->thr_wait);
+ } else
+ airo_read_stats(dev);
+ }
+
+ return &dev->stats;
+}
+
+static void airo_set_promisc(struct airo_info *ai) {
+ Cmd cmd;
+ Resp rsp;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd=CMD_SETMODE;
+ clear_bit(JOB_PROMISC, &ai->jobs);
+ cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
+ issuecommand(ai, &cmd, &rsp);
+ up(&ai->sem);
+}
+
+static void airo_set_multicast_list(struct net_device *dev) {
+ struct airo_info *ai = dev->ml_priv;
+
+ if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
+ change_bit(FLAG_PROMISC, &ai->flags);
+ if (down_trylock(&ai->sem) != 0) {
+ set_bit(JOB_PROMISC, &ai->jobs);
+ wake_up_interruptible(&ai->thr_wait);
+ } else
+ airo_set_promisc(ai);
+ }
+
+ if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
+ /* Turn on multicast. (Should be already setup...) */
+ }
+}
+
+static int airo_set_mac_address(struct net_device *dev, void *p)
+{
+ struct airo_info *ai = dev->ml_priv;
+ struct sockaddr *addr = p;
+
+ readConfigRid(ai, 1);
+ memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ disable_MAC(ai, 1);
+ writeConfigRid (ai, 1);
+ enable_MAC(ai, 1);
+ memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
+ if (ai->wifidev)
+ memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
+ return 0;
+}
+
+static int airo_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if ((new_mtu < 68) || (new_mtu > 2400))
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static LIST_HEAD(airo_devices);
+
+static void add_airo_dev(struct airo_info *ai)
+{
+ /* Upper layers already keep track of PCI devices,
+ * so we only need to remember our non-PCI cards. */
+ if (!ai->pci)
+ list_add_tail(&ai->dev_list, &airo_devices);
+}
+
+static void del_airo_dev(struct airo_info *ai)
+{
+ if (!ai->pci)
+ list_del(&ai->dev_list);
+}
+
+static int airo_close(struct net_device *dev) {
+ struct airo_info *ai = dev->ml_priv;
+
+ netif_stop_queue(dev);
+
+ if (ai->wifidev != dev) {
+#ifdef POWER_ON_DOWN
+ /* Shut power to the card. The idea is that the user can save
+ * power when he doesn't need the card with "ifconfig down".
+ * That's the method that is most friendly towards the network
+ * stack (i.e. the network stack won't try to broadcast
+ * anything on the interface and routes are gone. Jean II */
+ set_bit(FLAG_RADIO_DOWN, &ai->flags);
+ disable_MAC(ai, 1);
+#endif
+ disable_interrupts( ai );
+
+ free_irq(dev->irq, dev);
+
+ set_bit(JOB_DIE, &ai->jobs);
+ kthread_stop(ai->airo_thread_task);
+ }
+ return 0;
+}
+
+void stop_airo_card( struct net_device *dev, int freeres )
+{
+ struct airo_info *ai = dev->ml_priv;
+
+ set_bit(FLAG_RADIO_DOWN, &ai->flags);
+ disable_MAC(ai, 1);
+ disable_interrupts(ai);
+ takedown_proc_entry( dev, ai );
+ if (test_bit(FLAG_REGISTERED, &ai->flags)) {
+ unregister_netdev( dev );
+ if (ai->wifidev) {
+ unregister_netdev(ai->wifidev);
+ free_netdev(ai->wifidev);
+ ai->wifidev = NULL;
+ }
+ clear_bit(FLAG_REGISTERED, &ai->flags);
+ }
+ /*
+ * Clean out tx queue
+ */
+ if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
+ struct sk_buff *skb = NULL;
+ for (;(skb = skb_dequeue(&ai->txq));)
+ dev_kfree_skb(skb);
+ }
+
+ airo_networks_free (ai);
+
+ kfree(ai->flash);
+ kfree(ai->rssi);
+ kfree(ai->SSID);
+ if (freeres) {
+ /* PCMCIA frees this stuff, so only for PCI and ISA */
+ release_region( dev->base_addr, 64 );
+ if (test_bit(FLAG_MPI, &ai->flags)) {
+ if (ai->pci)
+ mpi_unmap_card(ai->pci);
+ if (ai->pcimem)
+ iounmap(ai->pcimem);
+ if (ai->pciaux)
+ iounmap(ai->pciaux);
+ pci_free_consistent(ai->pci, PCI_SHARED_LEN,
+ ai->shared, ai->shared_dma);
+ }
+ }
+ crypto_free_cipher(ai->tfm);
+ del_airo_dev(ai);
+ free_netdev( dev );
+}
+
+EXPORT_SYMBOL(stop_airo_card);
+
+static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
+ return ETH_ALEN;
+}
+
+static void mpi_unmap_card(struct pci_dev *pci)
+{
+ unsigned long mem_start = pci_resource_start(pci, 1);
+ unsigned long mem_len = pci_resource_len(pci, 1);
+ unsigned long aux_start = pci_resource_start(pci, 2);
+ unsigned long aux_len = AUXMEMSIZE;
+
+ release_mem_region(aux_start, aux_len);
+ release_mem_region(mem_start, mem_len);
+}
+
+/*************************************************************
+ * This routine assumes that descriptors have been setup .
+ * Run at insmod time or after reset when the decriptors
+ * have been initialized . Returns 0 if all is well nz
+ * otherwise . Does not allocate memory but sets up card
+ * using previously allocated descriptors.
+ */
+static int mpi_init_descriptors (struct airo_info *ai)
+{
+ Cmd cmd;
+ Resp rsp;
+ int i;
+ int rc = SUCCESS;
+
+ /* Alloc card RX descriptors */
+ netif_stop_queue(ai->dev);
+
+ memset(&rsp,0,sizeof(rsp));
+ memset(&cmd,0,sizeof(cmd));
+
+ cmd.cmd = CMD_ALLOCATEAUX;
+ cmd.parm0 = FID_RX;
+ cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
+ cmd.parm2 = MPI_MAX_FIDS;
+ rc=issuecommand(ai, &cmd, &rsp);
+ if (rc != SUCCESS) {
+ airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
+ return rc;
+ }
+
+ for (i=0; i<MPI_MAX_FIDS; i++) {
+ memcpy_toio(ai->rxfids[i].card_ram_off,
+ &ai->rxfids[i].rx_desc, sizeof(RxFid));
+ }
+
+ /* Alloc card TX descriptors */
+
+ memset(&rsp,0,sizeof(rsp));
+ memset(&cmd,0,sizeof(cmd));
+
+ cmd.cmd = CMD_ALLOCATEAUX;
+ cmd.parm0 = FID_TX;
+ cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
+ cmd.parm2 = MPI_MAX_FIDS;
+
+ for (i=0; i<MPI_MAX_FIDS; i++) {
+ ai->txfids[i].tx_desc.valid = 1;
+ memcpy_toio(ai->txfids[i].card_ram_off,
+ &ai->txfids[i].tx_desc, sizeof(TxFid));
+ }
+ ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
+
+ rc=issuecommand(ai, &cmd, &rsp);
+ if (rc != SUCCESS) {
+ airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
+ return rc;
+ }
+
+ /* Alloc card Rid descriptor */
+ memset(&rsp,0,sizeof(rsp));
+ memset(&cmd,0,sizeof(cmd));
+
+ cmd.cmd = CMD_ALLOCATEAUX;
+ cmd.parm0 = RID_RW;
+ cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
+ cmd.parm2 = 1; /* Magic number... */
+ rc=issuecommand(ai, &cmd, &rsp);
+ if (rc != SUCCESS) {
+ airo_print_err(ai->dev->name, "Couldn't allocate RID");
+ return rc;
+ }
+
+ memcpy_toio(ai->config_desc.card_ram_off,
+ &ai->config_desc.rid_desc, sizeof(Rid));
+
+ return rc;
+}
+
+/*
+ * We are setting up three things here:
+ * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
+ * 2) Map PCI memory for issuing commands.
+ * 3) Allocate memory (shared) to send and receive ethernet frames.
+ */
+static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
+{
+ unsigned long mem_start, mem_len, aux_start, aux_len;
+ int rc = -1;
+ int i;
+ dma_addr_t busaddroff;
+ unsigned char *vpackoff;
+ unsigned char __iomem *pciaddroff;
+
+ mem_start = pci_resource_start(pci, 1);
+ mem_len = pci_resource_len(pci, 1);
+ aux_start = pci_resource_start(pci, 2);
+ aux_len = AUXMEMSIZE;
+
+ if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
+ airo_print_err("", "Couldn't get region %x[%x]",
+ (int)mem_start, (int)mem_len);
+ goto out;
+ }
+ if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
+ airo_print_err("", "Couldn't get region %x[%x]",
+ (int)aux_start, (int)aux_len);
+ goto free_region1;
+ }
+
+ ai->pcimem = ioremap(mem_start, mem_len);
+ if (!ai->pcimem) {
+ airo_print_err("", "Couldn't map region %x[%x]",
+ (int)mem_start, (int)mem_len);
+ goto free_region2;
+ }
+ ai->pciaux = ioremap(aux_start, aux_len);
+ if (!ai->pciaux) {
+ airo_print_err("", "Couldn't map region %x[%x]",
+ (int)aux_start, (int)aux_len);
+ goto free_memmap;
+ }
+
+ /* Reserve PKTSIZE for each fid and 2K for the Rids */
+ ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
+ if (!ai->shared) {
+ airo_print_err("", "Couldn't alloc_consistent %d",
+ PCI_SHARED_LEN);
+ goto free_auxmap;
+ }
+
+ /*
+ * Setup descriptor RX, TX, CONFIG
+ */
+ busaddroff = ai->shared_dma;
+ pciaddroff = ai->pciaux + AUX_OFFSET;
+ vpackoff = ai->shared;
+
+ /* RX descriptor setup */
+ for(i = 0; i < MPI_MAX_FIDS; i++) {
+ ai->rxfids[i].pending = 0;
+ ai->rxfids[i].card_ram_off = pciaddroff;
+ ai->rxfids[i].virtual_host_addr = vpackoff;
+ ai->rxfids[i].rx_desc.host_addr = busaddroff;
+ ai->rxfids[i].rx_desc.valid = 1;
+ ai->rxfids[i].rx_desc.len = PKTSIZE;
+ ai->rxfids[i].rx_desc.rdy = 0;
+
+ pciaddroff += sizeof(RxFid);
+ busaddroff += PKTSIZE;
+ vpackoff += PKTSIZE;
+ }
+
+ /* TX descriptor setup */
+ for(i = 0; i < MPI_MAX_FIDS; i++) {
+ ai->txfids[i].card_ram_off = pciaddroff;
+ ai->txfids[i].virtual_host_addr = vpackoff;
+ ai->txfids[i].tx_desc.valid = 1;
+ ai->txfids[i].tx_desc.host_addr = busaddroff;
+ memcpy(ai->txfids[i].virtual_host_addr,
+ &wifictlhdr8023, sizeof(wifictlhdr8023));
+
+ pciaddroff += sizeof(TxFid);
+ busaddroff += PKTSIZE;
+ vpackoff += PKTSIZE;
+ }
+ ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
+
+ /* Rid descriptor setup */
+ ai->config_desc.card_ram_off = pciaddroff;
+ ai->config_desc.virtual_host_addr = vpackoff;
+ ai->config_desc.rid_desc.host_addr = busaddroff;
+ ai->ridbus = busaddroff;
+ ai->config_desc.rid_desc.rid = 0;
+ ai->config_desc.rid_desc.len = RIDSIZE;
+ ai->config_desc.rid_desc.valid = 1;
+ pciaddroff += sizeof(Rid);
+ busaddroff += RIDSIZE;
+ vpackoff += RIDSIZE;
+
+ /* Tell card about descriptors */
+ if (mpi_init_descriptors (ai) != SUCCESS)
+ goto free_shared;
+
+ return 0;
+ free_shared:
+ pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
+ free_auxmap:
+ iounmap(ai->pciaux);
+ free_memmap:
+ iounmap(ai->pcimem);
+ free_region2:
+ release_mem_region(aux_start, aux_len);
+ free_region1:
+ release_mem_region(mem_start, mem_len);
+ out:
+ return rc;
+}
+
+static const struct header_ops airo_header_ops = {
+ .parse = wll_header_parse,
+};
+
+static const struct net_device_ops airo11_netdev_ops = {
+ .ndo_open = airo_open,
+ .ndo_stop = airo_close,
+ .ndo_start_xmit = airo_start_xmit11,
+ .ndo_get_stats = airo_get_stats,
+ .ndo_set_mac_address = airo_set_mac_address,
+ .ndo_do_ioctl = airo_ioctl,
+ .ndo_change_mtu = airo_change_mtu,
+};
+
+static void wifi_setup(struct net_device *dev)
+{
+ dev->netdev_ops = &airo11_netdev_ops;
+ dev->header_ops = &airo_header_ops;
+ dev->wireless_handlers = &airo_handler_def;
+
+ dev->type = ARPHRD_IEEE80211;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = AIRO_DEF_MTU;
+ dev->addr_len = ETH_ALEN;
+ dev->tx_queue_len = 100;
+
+ eth_broadcast_addr(dev->broadcast);
+
+ dev->flags = IFF_BROADCAST|IFF_MULTICAST;
+}
+
+static struct net_device *init_wifidev(struct airo_info *ai,
+ struct net_device *ethdev)
+{
+ int err;
+ struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN,
+ wifi_setup);
+ if (!dev)
+ return NULL;
+ dev->ml_priv = ethdev->ml_priv;
+ dev->irq = ethdev->irq;
+ dev->base_addr = ethdev->base_addr;
+ dev->wireless_data = ethdev->wireless_data;
+ SET_NETDEV_DEV(dev, ethdev->dev.parent);
+ eth_hw_addr_inherit(dev, ethdev);
+ err = register_netdev(dev);
+ if (err<0) {
+ free_netdev(dev);
+ return NULL;
+ }
+ return dev;
+}
+
+static int reset_card( struct net_device *dev , int lock) {
+ struct airo_info *ai = dev->ml_priv;
+
+ if (lock && down_interruptible(&ai->sem))
+ return -1;
+ waitbusy (ai);
+ OUT4500(ai,COMMAND,CMD_SOFTRESET);
+ msleep(200);
+ waitbusy (ai);
+ msleep(200);
+ if (lock)
+ up(&ai->sem);
+ return 0;
+}
+
+#define AIRO_MAX_NETWORK_COUNT 64
+static int airo_networks_allocate(struct airo_info *ai)
+{
+ if (ai->networks)
+ return 0;
+
+ ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement),
+ GFP_KERNEL);
+ if (!ai->networks) {
+ airo_print_warn("", "Out of memory allocating beacons");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void airo_networks_free(struct airo_info *ai)
+{
+ kfree(ai->networks);
+ ai->networks = NULL;
+}
+
+static void airo_networks_initialize(struct airo_info *ai)
+{
+ int i;
+
+ INIT_LIST_HEAD(&ai->network_free_list);
+ INIT_LIST_HEAD(&ai->network_list);
+ for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
+ list_add_tail(&ai->networks[i].list,
+ &ai->network_free_list);
+}
+
+static const struct net_device_ops airo_netdev_ops = {
+ .ndo_open = airo_open,
+ .ndo_stop = airo_close,
+ .ndo_start_xmit = airo_start_xmit,
+ .ndo_get_stats = airo_get_stats,
+ .ndo_set_rx_mode = airo_set_multicast_list,
+ .ndo_set_mac_address = airo_set_mac_address,
+ .ndo_do_ioctl = airo_ioctl,
+ .ndo_change_mtu = airo_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static const struct net_device_ops mpi_netdev_ops = {
+ .ndo_open = airo_open,
+ .ndo_stop = airo_close,
+ .ndo_start_xmit = mpi_start_xmit,
+ .ndo_get_stats = airo_get_stats,
+ .ndo_set_rx_mode = airo_set_multicast_list,
+ .ndo_set_mac_address = airo_set_mac_address,
+ .ndo_do_ioctl = airo_ioctl,
+ .ndo_change_mtu = airo_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+
+static struct net_device *_init_airo_card( unsigned short irq, int port,
+ int is_pcmcia, struct pci_dev *pci,
+ struct device *dmdev )
+{
+ struct net_device *dev;
+ struct airo_info *ai;
+ int i, rc;
+ CapabilityRid cap_rid;
+
+ /* Create the network device object. */
+ dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup);
+ if (!dev) {
+ airo_print_err("", "Couldn't alloc_etherdev");
+ return NULL;
+ }
+
+ ai = dev->ml_priv = netdev_priv(dev);
+ ai->wifidev = NULL;
+ ai->flags = 1 << FLAG_RADIO_DOWN;
+ ai->jobs = 0;
+ ai->dev = dev;
+ if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
+ airo_print_dbg("", "Found an MPI350 card");
+ set_bit(FLAG_MPI, &ai->flags);
+ }
+ spin_lock_init(&ai->aux_lock);
+ sema_init(&ai->sem, 1);
+ ai->config.len = 0;
+ ai->pci = pci;
+ init_waitqueue_head (&ai->thr_wait);
+ ai->tfm = NULL;
+ add_airo_dev(ai);
+ ai->APList.len = cpu_to_le16(sizeof(struct APListRid));
+
+ if (airo_networks_allocate (ai))
+ goto err_out_free;
+ airo_networks_initialize (ai);
+
+ skb_queue_head_init (&ai->txq);
+
+ /* The Airo-specific entries in the device structure. */
+ if (test_bit(FLAG_MPI,&ai->flags))
+ dev->netdev_ops = &mpi_netdev_ops;
+ else
+ dev->netdev_ops = &airo_netdev_ops;
+ dev->wireless_handlers = &airo_handler_def;
+ ai->wireless_data.spy_data = &ai->spy_data;
+ dev->wireless_data = &ai->wireless_data;
+ dev->irq = irq;
+ dev->base_addr = port;
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+
+ SET_NETDEV_DEV(dev, dmdev);
+
+ reset_card (dev, 1);
+ msleep(400);
+
+ if (!is_pcmcia) {
+ if (!request_region(dev->base_addr, 64, DRV_NAME)) {
+ rc = -EBUSY;
+ airo_print_err(dev->name, "Couldn't request region");
+ goto err_out_nets;
+ }
+ }
+
+ if (test_bit(FLAG_MPI,&ai->flags)) {
+ if (mpi_map_card(ai, pci)) {
+ airo_print_err("", "Could not map memory");
+ goto err_out_res;
+ }
+ }
+
+ if (probe) {
+ if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
+ airo_print_err(dev->name, "MAC could not be enabled" );
+ rc = -EIO;
+ goto err_out_map;
+ }
+ } else if (!test_bit(FLAG_MPI,&ai->flags)) {
+ ai->bap_read = fast_bap_read;
+ set_bit(FLAG_FLASHING, &ai->flags);
+ }
+
+ strcpy(dev->name, "eth%d");
+ rc = register_netdev(dev);
+ if (rc) {
+ airo_print_err(dev->name, "Couldn't register_netdev");
+ goto err_out_map;
+ }
+ ai->wifidev = init_wifidev(ai, dev);
+ if (!ai->wifidev)
+ goto err_out_reg;
+
+ rc = readCapabilityRid(ai, &cap_rid, 1);
+ if (rc != SUCCESS) {
+ rc = -EIO;
+ goto err_out_wifi;
+ }
+ /* WEP capability discovery */
+ ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0;
+ ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0;
+
+ airo_print_info(dev->name, "Firmware version %x.%x.%02d",
+ ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
+ (le16_to_cpu(cap_rid.softVer) & 0xFF),
+ le16_to_cpu(cap_rid.softSubVer));
+
+ /* Test for WPA support */
+ /* Only firmware versions 5.30.17 or better can do WPA */
+ if (le16_to_cpu(cap_rid.softVer) > 0x530
+ || (le16_to_cpu(cap_rid.softVer) == 0x530
+ && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
+ airo_print_info(ai->dev->name, "WPA supported.");
+
+ set_bit(FLAG_WPA_CAPABLE, &ai->flags);
+ ai->bssListFirst = RID_WPA_BSSLISTFIRST;
+ ai->bssListNext = RID_WPA_BSSLISTNEXT;
+ ai->bssListRidLen = sizeof(BSSListRid);
+ } else {
+ airo_print_info(ai->dev->name, "WPA unsupported with firmware "
+ "versions older than 5.30.17.");
+
+ ai->bssListFirst = RID_BSSLISTFIRST;
+ ai->bssListNext = RID_BSSLISTNEXT;
+ ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
+ }
+
+ set_bit(FLAG_REGISTERED,&ai->flags);
+ airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
+
+ /* Allocate the transmit buffers */
+ if (probe && !test_bit(FLAG_MPI,&ai->flags))
+ for( i = 0; i < MAX_FIDS; i++ )
+ ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
+
+ if (setup_proc_entry(dev, dev->ml_priv) < 0)
+ goto err_out_wifi;
+
+ return dev;
+
+err_out_wifi:
+ unregister_netdev(ai->wifidev);
+ free_netdev(ai->wifidev);
+err_out_reg:
+ unregister_netdev(dev);
+err_out_map:
+ if (test_bit(FLAG_MPI,&ai->flags) && pci) {
+ pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
+ iounmap(ai->pciaux);
+ iounmap(ai->pcimem);
+ mpi_unmap_card(ai->pci);
+ }
+err_out_res:
+ if (!is_pcmcia)
+ release_region( dev->base_addr, 64 );
+err_out_nets:
+ airo_networks_free(ai);
+err_out_free:
+ del_airo_dev(ai);
+ free_netdev(dev);
+ return NULL;
+}
+
+struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia,
+ struct device *dmdev)
+{
+ return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev);
+}
+
+EXPORT_SYMBOL(init_airo_card);
+
+static int waitbusy (struct airo_info *ai) {
+ int delay = 0;
+ while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
+ udelay (10);
+ if ((++delay % 20) == 0)
+ OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
+ }
+ return delay < 10000;
+}
+
+int reset_airo_card( struct net_device *dev )
+{
+ int i;
+ struct airo_info *ai = dev->ml_priv;
+
+ if (reset_card (dev, 1))
+ return -1;
+
+ if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
+ airo_print_err(dev->name, "MAC could not be enabled");
+ return -1;
+ }
+ airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
+ /* Allocate the transmit buffers if needed */
+ if (!test_bit(FLAG_MPI,&ai->flags))
+ for( i = 0; i < MAX_FIDS; i++ )
+ ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
+
+ enable_interrupts( ai );
+ netif_wake_queue(dev);
+ return 0;
+}
+
+EXPORT_SYMBOL(reset_airo_card);
+
+static void airo_send_event(struct net_device *dev) {
+ struct airo_info *ai = dev->ml_priv;
+ union iwreq_data wrqu;
+ StatusRid status_rid;
+
+ clear_bit(JOB_EVENT, &ai->jobs);
+ PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
+ up(&ai->sem);
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+ /* Send event to user space */
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void airo_process_scan_results (struct airo_info *ai) {
+ union iwreq_data wrqu;
+ BSSListRid bss;
+ int rc;
+ BSSListElement * loop_net;
+ BSSListElement * tmp_net;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
+ list_move_tail (&loop_net->list, &ai->network_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset (loop_net, 0, sizeof (loop_net->bss));
+ }
+
+ /* Try to read the first entry of the scan result */
+ rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
+ if((rc) || (bss.index == cpu_to_le16(0xffff))) {
+ /* No scan results */
+ goto out;
+ }
+
+ /* Read and parse all entries */
+ tmp_net = NULL;
+ while((!rc) && (bss.index != cpu_to_le16(0xffff))) {
+ /* Grab a network off the free list */
+ if (!list_empty(&ai->network_free_list)) {
+ tmp_net = list_entry(ai->network_free_list.next,
+ BSSListElement, list);
+ list_del(ai->network_free_list.next);
+ }
+
+ if (tmp_net != NULL) {
+ memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
+ list_add_tail(&tmp_net->list, &ai->network_list);
+ tmp_net = NULL;
+ }
+
+ /* Read next entry */
+ rc = PC4500_readrid(ai, ai->bssListNext,
+ &bss, ai->bssListRidLen, 0);
+ }
+
+out:
+ /* write APList back (we cleared it in airo_set_scan) */
+ disable_MAC(ai, 2);
+ writeAPListRid(ai, &ai->APList, 0);
+ enable_MAC(ai, 0);
+
+ ai->scan_timeout = 0;
+ clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
+ up(&ai->sem);
+
+ /* Send an empty event to user space.
+ * We don't send the received data on
+ * the event because it would require
+ * us to do complex transcoding, and
+ * we want to minimise the work done in
+ * the irq handler. Use a request to
+ * extract the data - Jean II */
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+static int airo_thread(void *data) {
+ struct net_device *dev = data;
+ struct airo_info *ai = dev->ml_priv;
+ int locked;
+
+ set_freezable();
+ while(1) {
+ /* make swsusp happy with our thread */
+ try_to_freeze();
+
+ if (test_bit(JOB_DIE, &ai->jobs))
+ break;
+
+ if (ai->jobs) {
+ locked = down_interruptible(&ai->sem);
+ } else {
+ wait_queue_t wait;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&ai->thr_wait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (ai->jobs)
+ break;
+ if (ai->expires || ai->scan_timeout) {
+ if (ai->scan_timeout &&
+ time_after_eq(jiffies,ai->scan_timeout)){
+ set_bit(JOB_SCAN_RESULTS, &ai->jobs);
+ break;
+ } else if (ai->expires &&
+ time_after_eq(jiffies,ai->expires)){
+ set_bit(JOB_AUTOWEP, &ai->jobs);
+ break;
+ }
+ if (!kthread_should_stop() &&
+ !freezing(current)) {
+ unsigned long wake_at;
+ if (!ai->expires || !ai->scan_timeout) {
+ wake_at = max(ai->expires,
+ ai->scan_timeout);
+ } else {
+ wake_at = min(ai->expires,
+ ai->scan_timeout);
+ }
+ schedule_timeout(wake_at - jiffies);
+ continue;
+ }
+ } else if (!kthread_should_stop() &&
+ !freezing(current)) {
+ schedule();
+ continue;
+ }
+ break;
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&ai->thr_wait, &wait);
+ locked = 1;
+ }
+
+ if (locked)
+ continue;
+
+ if (test_bit(JOB_DIE, &ai->jobs)) {
+ up(&ai->sem);
+ break;
+ }
+
+ if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
+ up(&ai->sem);
+ continue;
+ }
+
+ if (test_bit(JOB_XMIT, &ai->jobs))
+ airo_end_xmit(dev);
+ else if (test_bit(JOB_XMIT11, &ai->jobs))
+ airo_end_xmit11(dev);
+ else if (test_bit(JOB_STATS, &ai->jobs))
+ airo_read_stats(dev);
+ else if (test_bit(JOB_WSTATS, &ai->jobs))
+ airo_read_wireless_stats(ai);
+ else if (test_bit(JOB_PROMISC, &ai->jobs))
+ airo_set_promisc(ai);
+ else if (test_bit(JOB_MIC, &ai->jobs))
+ micinit(ai);
+ else if (test_bit(JOB_EVENT, &ai->jobs))
+ airo_send_event(dev);
+ else if (test_bit(JOB_AUTOWEP, &ai->jobs))
+ timer_func(dev);
+ else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
+ airo_process_scan_results(ai);
+ else /* Shouldn't get here, but we make sure to unlock */
+ up(&ai->sem);
+ }
+
+ return 0;
+}
+
+static int header_len(__le16 ctl)
+{
+ u16 fc = le16_to_cpu(ctl);
+ switch (fc & 0xc) {
+ case 4:
+ if ((fc & 0xe0) == 0xc0)
+ return 10; /* one-address control packet */
+ return 16; /* two-address control packet */
+ case 8:
+ if ((fc & 0x300) == 0x300)
+ return 30; /* WDS packet */
+ }
+ return 24;
+}
+
+static void airo_handle_cisco_mic(struct airo_info *ai)
+{
+ if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
+ set_bit(JOB_MIC, &ai->jobs);
+ wake_up_interruptible(&ai->thr_wait);
+ }
+}
+
+/* Airo Status codes */
+#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */
+#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */
+#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/
+#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */
+#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */
+#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */
+#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */
+#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */
+#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */
+#define STAT_ASSOC 0x0400 /* Associated */
+#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */
+
+static void airo_print_status(const char *devname, u16 status)
+{
+ u8 reason = status & 0xFF;
+
+ switch (status & 0xFF00) {
+ case STAT_NOBEACON:
+ switch (status) {
+ case STAT_NOBEACON:
+ airo_print_dbg(devname, "link lost (missed beacons)");
+ break;
+ case STAT_MAXRETRIES:
+ case STAT_MAXARL:
+ airo_print_dbg(devname, "link lost (max retries)");
+ break;
+ case STAT_FORCELOSS:
+ airo_print_dbg(devname, "link lost (local choice)");
+ break;
+ case STAT_TSFSYNC:
+ airo_print_dbg(devname, "link lost (TSF sync lost)");
+ break;
+ default:
+ airo_print_dbg(devname, "unknown status %x\n", status);
+ break;
+ }
+ break;
+ case STAT_DEAUTH:
+ airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
+ break;
+ case STAT_DISASSOC:
+ airo_print_dbg(devname, "disassociated (reason: %d)", reason);
+ break;
+ case STAT_ASSOC_FAIL:
+ airo_print_dbg(devname, "association failed (reason: %d)",
+ reason);
+ break;
+ case STAT_AUTH_FAIL:
+ airo_print_dbg(devname, "authentication failed (reason: %d)",
+ reason);
+ break;
+ case STAT_ASSOC:
+ case STAT_REASSOC:
+ break;
+ default:
+ airo_print_dbg(devname, "unknown status %x\n", status);
+ break;
+ }
+}
+
+static void airo_handle_link(struct airo_info *ai)
+{
+ union iwreq_data wrqu;
+ int scan_forceloss = 0;
+ u16 status;
+
+ /* Get new status and acknowledge the link change */
+ status = le16_to_cpu(IN4500(ai, LINKSTAT));
+ OUT4500(ai, EVACK, EV_LINK);
+
+ if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
+ scan_forceloss = 1;
+
+ airo_print_status(ai->dev->name, status);
+
+ if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
+ if (auto_wep)
+ ai->expires = 0;
+ if (ai->list_bss_task)
+ wake_up_process(ai->list_bss_task);
+ set_bit(FLAG_UPDATE_UNI, &ai->flags);
+ set_bit(FLAG_UPDATE_MULTI, &ai->flags);
+
+ if (down_trylock(&ai->sem) != 0) {
+ set_bit(JOB_EVENT, &ai->jobs);
+ wake_up_interruptible(&ai->thr_wait);
+ } else
+ airo_send_event(ai->dev);
+ netif_carrier_on(ai->dev);
+ } else if (!scan_forceloss) {
+ if (auto_wep && !ai->expires) {
+ ai->expires = RUN_AT(3*HZ);
+ wake_up_interruptible(&ai->thr_wait);
+ }
+
+ /* Send event to user space */
+ eth_zero_addr(wrqu.ap_addr.sa_data);
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
+ netif_carrier_off(ai->dev);
+ } else {
+ netif_carrier_off(ai->dev);
+ }
+}
+
+static void airo_handle_rx(struct airo_info *ai)
+{
+ struct sk_buff *skb = NULL;
+ __le16 fc, v, *buffer, tmpbuf[4];
+ u16 len, hdrlen = 0, gap, fid;
+ struct rx_hdr hdr;
+ int success = 0;
+
+ if (test_bit(FLAG_MPI, &ai->flags)) {
+ if (test_bit(FLAG_802_11, &ai->flags))
+ mpi_receive_802_11(ai);
+ else
+ mpi_receive_802_3(ai);
+ OUT4500(ai, EVACK, EV_RX);
+ return;
+ }
+
+ fid = IN4500(ai, RXFID);
+
+ /* Get the packet length */
+ if (test_bit(FLAG_802_11, &ai->flags)) {
+ bap_setup (ai, fid, 4, BAP0);
+ bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
+ /* Bad CRC. Ignore packet */
+ if (le16_to_cpu(hdr.status) & 2)
+ hdr.len = 0;
+ if (ai->wifidev == NULL)
+ hdr.len = 0;
+ } else {
+ bap_setup(ai, fid, 0x36, BAP0);
+ bap_read(ai, &hdr.len, 2, BAP0);
+ }
+ len = le16_to_cpu(hdr.len);
+
+ if (len > AIRO_DEF_MTU) {
+ airo_print_err(ai->dev->name, "Bad size %d", len);
+ goto done;
+ }
+ if (len == 0)
+ goto done;
+
+ if (test_bit(FLAG_802_11, &ai->flags)) {
+ bap_read(ai, &fc, sizeof (fc), BAP0);
+ hdrlen = header_len(fc);
+ } else
+ hdrlen = ETH_ALEN * 2;
+
+ skb = dev_alloc_skb(len + hdrlen + 2 + 2);
+ if (!skb) {
+ ai->dev->stats.rx_dropped++;
+ goto done;
+ }
+
+ skb_reserve(skb, 2); /* This way the IP header is aligned */
+ buffer = (__le16 *) skb_put(skb, len + hdrlen);
+ if (test_bit(FLAG_802_11, &ai->flags)) {
+ buffer[0] = fc;
+ bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
+ if (hdrlen == 24)
+ bap_read(ai, tmpbuf, 6, BAP0);
+
+ bap_read(ai, &v, sizeof(v), BAP0);
+ gap = le16_to_cpu(v);
+ if (gap) {
+ if (gap <= 8) {
+ bap_read(ai, tmpbuf, gap, BAP0);
+ } else {
+ airo_print_err(ai->dev->name, "gaplen too "
+ "big. Problems will follow...");
+ }
+ }
+ bap_read(ai, buffer + hdrlen/2, len, BAP0);
+ } else {
+ MICBuffer micbuf;
+
+ bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
+ if (ai->micstats.enabled) {
+ bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
+ if (ntohs(micbuf.typelen) > 0x05DC)
+ bap_setup(ai, fid, 0x44, BAP0);
+ else {
+ if (len <= sizeof (micbuf)) {
+ dev_kfree_skb_irq(skb);
+ goto done;
+ }
+
+ len -= sizeof(micbuf);
+ skb_trim(skb, len + hdrlen);
+ }
+ }
+
+ bap_read(ai, buffer + ETH_ALEN, len, BAP0);
+ if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
+ dev_kfree_skb_irq (skb);
+ else
+ success = 1;
+ }
+
+#ifdef WIRELESS_SPY
+ if (success && (ai->spy_data.spy_number > 0)) {
+ char *sa;
+ struct iw_quality wstats;
+
+ /* Prepare spy data : addr + qual */
+ if (!test_bit(FLAG_802_11, &ai->flags)) {
+ sa = (char *) buffer + 6;
+ bap_setup(ai, fid, 8, BAP0);
+ bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
+ } else
+ sa = (char *) buffer + 10;
+ wstats.qual = hdr.rssi[0];
+ if (ai->rssi)
+ wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
+ else
+ wstats.level = (hdr.rssi[1] + 321) / 2;
+ wstats.noise = ai->wstats.qual.noise;
+ wstats.updated = IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_DBM;
+ /* Update spy records */
+ wireless_spy_update(ai->dev, sa, &wstats);
+ }
+#endif /* WIRELESS_SPY */
+
+done:
+ OUT4500(ai, EVACK, EV_RX);
+
+ if (success) {
+ if (test_bit(FLAG_802_11, &ai->flags)) {
+ skb_reset_mac_header(skb);
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->dev = ai->wifidev;
+ skb->protocol = htons(ETH_P_802_2);
+ } else
+ skb->protocol = eth_type_trans(skb, ai->dev);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ netif_rx(skb);
+ }
+}
+
+static void airo_handle_tx(struct airo_info *ai, u16 status)
+{
+ int i, len = 0, index = -1;
+ u16 fid;
+
+ if (test_bit(FLAG_MPI, &ai->flags)) {
+ unsigned long flags;
+
+ if (status & EV_TXEXC)
+ get_tx_error(ai, -1);
+
+ spin_lock_irqsave(&ai->aux_lock, flags);
+ if (!skb_queue_empty(&ai->txq)) {
+ spin_unlock_irqrestore(&ai->aux_lock,flags);
+ mpi_send_packet(ai->dev);
+ } else {
+ clear_bit(FLAG_PENDING_XMIT, &ai->flags);
+ spin_unlock_irqrestore(&ai->aux_lock,flags);
+ netif_wake_queue(ai->dev);
+ }
+ OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
+ return;
+ }
+
+ fid = IN4500(ai, TXCOMPLFID);
+
+ for(i = 0; i < MAX_FIDS; i++) {
+ if ((ai->fids[i] & 0xffff) == fid) {
+ len = ai->fids[i] >> 16;
+ index = i;
+ }
+ }
+
+ if (index != -1) {
+ if (status & EV_TXEXC)
+ get_tx_error(ai, index);
+
+ OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
+
+ /* Set up to be used again */
+ ai->fids[index] &= 0xffff;
+ if (index < MAX_FIDS / 2) {
+ if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
+ netif_wake_queue(ai->dev);
+ } else {
+ if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
+ netif_wake_queue(ai->wifidev);
+ }
+ } else {
+ OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
+ airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
+ }
+}
+
+static irqreturn_t airo_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ u16 status, savedInterrupts = 0;
+ struct airo_info *ai = dev->ml_priv;
+ int handled = 0;
+
+ if (!netif_device_present(dev))
+ return IRQ_NONE;
+
+ for (;;) {
+ status = IN4500(ai, EVSTAT);
+ if (!(status & STATUS_INTS) || (status == 0xffff))
+ break;
+
+ handled = 1;
+
+ if (status & EV_AWAKE) {
+ OUT4500(ai, EVACK, EV_AWAKE);
+ OUT4500(ai, EVACK, EV_AWAKE);
+ }
+
+ if (!savedInterrupts) {
+ savedInterrupts = IN4500(ai, EVINTEN);
+ OUT4500(ai, EVINTEN, 0);
+ }
+
+ if (status & EV_MIC) {
+ OUT4500(ai, EVACK, EV_MIC);
+ airo_handle_cisco_mic(ai);
+ }
+
+ if (status & EV_LINK) {
+ /* Link status changed */
+ airo_handle_link(ai);
+ }
+
+ /* Check to see if there is something to receive */
+ if (status & EV_RX)
+ airo_handle_rx(ai);
+
+ /* Check to see if a packet has been transmitted */
+ if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
+ airo_handle_tx(ai, status);
+
+ if ( status & ~STATUS_INTS & ~IGNORE_INTS ) {
+ airo_print_warn(ai->dev->name, "Got weird status %x",
+ status & ~STATUS_INTS & ~IGNORE_INTS );
+ }
+ }
+
+ if (savedInterrupts)
+ OUT4500(ai, EVINTEN, savedInterrupts);
+
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ * Routines to talk to the card
+ */
+
+/*
+ * This was originally written for the 4500, hence the name
+ * NOTE: If use with 8bit mode and SMP bad things will happen!
+ * Why would some one do 8 bit IO in an SMP machine?!?
+ */
+static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) {
+ if (test_bit(FLAG_MPI,&ai->flags))
+ reg <<= 1;
+ if ( !do8bitIO )
+ outw( val, ai->dev->base_addr + reg );
+ else {
+ outb( val & 0xff, ai->dev->base_addr + reg );
+ outb( val >> 8, ai->dev->base_addr + reg + 1 );
+ }
+}
+
+static u16 IN4500( struct airo_info *ai, u16 reg ) {
+ unsigned short rc;
+
+ if (test_bit(FLAG_MPI,&ai->flags))
+ reg <<= 1;
+ if ( !do8bitIO )
+ rc = inw( ai->dev->base_addr + reg );
+ else {
+ rc = inb( ai->dev->base_addr + reg );
+ rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8;
+ }
+ return rc;
+}
+
+static int enable_MAC(struct airo_info *ai, int lock)
+{
+ int rc;
+ Cmd cmd;
+ Resp rsp;
+
+ /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
+ * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
+ * Note : we could try to use !netif_running(dev) in enable_MAC()
+ * instead of this flag, but I don't trust it *within* the
+ * open/close functions, and testing both flags together is
+ * "cheaper" - Jean II */
+ if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
+
+ if (lock && down_interruptible(&ai->sem))
+ return -ERESTARTSYS;
+
+ if (!test_bit(FLAG_ENABLED, &ai->flags)) {
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd = MAC_ENABLE;
+ rc = issuecommand(ai, &cmd, &rsp);
+ if (rc == SUCCESS)
+ set_bit(FLAG_ENABLED, &ai->flags);
+ } else
+ rc = SUCCESS;
+
+ if (lock)
+ up(&ai->sem);
+
+ if (rc)
+ airo_print_err(ai->dev->name, "Cannot enable MAC");
+ else if ((rsp.status & 0xFF00) != 0) {
+ airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
+ "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
+ rc = ERROR;
+ }
+ return rc;
+}
+
+static void disable_MAC( struct airo_info *ai, int lock ) {
+ Cmd cmd;
+ Resp rsp;
+
+ if (lock == 1 && down_interruptible(&ai->sem))
+ return;
+
+ if (test_bit(FLAG_ENABLED, &ai->flags)) {
+ if (lock != 2) /* lock == 2 means don't disable carrier */
+ netif_carrier_off(ai->dev);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd = MAC_DISABLE; // disable in case already enabled
+ issuecommand(ai, &cmd, &rsp);
+ clear_bit(FLAG_ENABLED, &ai->flags);
+ }
+ if (lock == 1)
+ up(&ai->sem);
+}
+
+static void enable_interrupts( struct airo_info *ai ) {
+ /* Enable the interrupts */
+ OUT4500( ai, EVINTEN, STATUS_INTS );
+}
+
+static void disable_interrupts( struct airo_info *ai ) {
+ OUT4500( ai, EVINTEN, 0 );
+}
+
+static void mpi_receive_802_3(struct airo_info *ai)
+{
+ RxFid rxd;
+ int len = 0;
+ struct sk_buff *skb;
+ char *buffer;
+ int off = 0;
+ MICBuffer micbuf;
+
+ memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
+ /* Make sure we got something */
+ if (rxd.rdy && rxd.valid == 0) {
+ len = rxd.len + 12;
+ if (len < 12 || len > 2048)
+ goto badrx;
+
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ ai->dev->stats.rx_dropped++;
+ goto badrx;
+ }
+ buffer = skb_put(skb,len);
+ memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
+ if (ai->micstats.enabled) {
+ memcpy(&micbuf,
+ ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
+ sizeof(micbuf));
+ if (ntohs(micbuf.typelen) <= 0x05DC) {
+ if (len <= sizeof(micbuf) + ETH_ALEN * 2)
+ goto badmic;
+
+ off = sizeof(micbuf);
+ skb_trim (skb, len - off);
+ }
+ }
+ memcpy(buffer + ETH_ALEN * 2,
+ ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
+ len - ETH_ALEN * 2 - off);
+ if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
+badmic:
+ dev_kfree_skb_irq (skb);
+ goto badrx;
+ }
+#ifdef WIRELESS_SPY
+ if (ai->spy_data.spy_number > 0) {
+ char *sa;
+ struct iw_quality wstats;
+ /* Prepare spy data : addr + qual */
+ sa = buffer + ETH_ALEN;
+ wstats.qual = 0; /* XXX Where do I get that info from ??? */
+ wstats.level = 0;
+ wstats.updated = 0;
+ /* Update spy records */
+ wireless_spy_update(ai->dev, sa, &wstats);
+ }
+#endif /* WIRELESS_SPY */
+
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->protocol = eth_type_trans(skb, ai->dev);
+ netif_rx(skb);
+ }
+badrx:
+ if (rxd.valid == 0) {
+ rxd.valid = 1;
+ rxd.rdy = 0;
+ rxd.len = PKTSIZE;
+ memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
+ }
+}
+
+static void mpi_receive_802_11(struct airo_info *ai)
+{
+ RxFid rxd;
+ struct sk_buff *skb = NULL;
+ u16 len, hdrlen = 0;
+ __le16 fc;
+ struct rx_hdr hdr;
+ u16 gap;
+ u16 *buffer;
+ char *ptr = ai->rxfids[0].virtual_host_addr + 4;
+
+ memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
+ memcpy ((char *)&hdr, ptr, sizeof(hdr));
+ ptr += sizeof(hdr);
+ /* Bad CRC. Ignore packet */
+ if (le16_to_cpu(hdr.status) & 2)
+ hdr.len = 0;
+ if (ai->wifidev == NULL)
+ hdr.len = 0;
+ len = le16_to_cpu(hdr.len);
+ if (len > AIRO_DEF_MTU) {
+ airo_print_err(ai->dev->name, "Bad size %d", len);
+ goto badrx;
+ }
+ if (len == 0)
+ goto badrx;
+
+ fc = get_unaligned((__le16 *)ptr);
+ hdrlen = header_len(fc);
+
+ skb = dev_alloc_skb( len + hdrlen + 2 );
+ if ( !skb ) {
+ ai->dev->stats.rx_dropped++;
+ goto badrx;
+ }
+ buffer = (u16*)skb_put (skb, len + hdrlen);
+ memcpy ((char *)buffer, ptr, hdrlen);
+ ptr += hdrlen;
+ if (hdrlen == 24)
+ ptr += 6;
+ gap = get_unaligned_le16(ptr);
+ ptr += sizeof(__le16);
+ if (gap) {
+ if (gap <= 8)
+ ptr += gap;
+ else
+ airo_print_err(ai->dev->name,
+ "gaplen too big. Problems will follow...");
+ }
+ memcpy ((char *)buffer + hdrlen, ptr, len);
+ ptr += len;
+#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
+ if (ai->spy_data.spy_number > 0) {
+ char *sa;
+ struct iw_quality wstats;
+ /* Prepare spy data : addr + qual */
+ sa = (char*)buffer + 10;
+ wstats.qual = hdr.rssi[0];
+ if (ai->rssi)
+ wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
+ else
+ wstats.level = (hdr.rssi[1] + 321) / 2;
+ wstats.noise = ai->wstats.qual.noise;
+ wstats.updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_DBM;
+ /* Update spy records */
+ wireless_spy_update(ai->dev, sa, &wstats);
+ }
+#endif /* IW_WIRELESS_SPY */
+ skb_reset_mac_header(skb);
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->dev = ai->wifidev;
+ skb->protocol = htons(ETH_P_802_2);
+ skb->ip_summed = CHECKSUM_NONE;
+ netif_rx( skb );
+
+badrx:
+ if (rxd.valid == 0) {
+ rxd.valid = 1;
+ rxd.rdy = 0;
+ rxd.len = PKTSIZE;
+ memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
+ }
+}
+
+static inline void set_auth_type(struct airo_info *local, int auth_type)
+{
+ local->config.authType = auth_type;
+ /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT).
+ * Used by airo_set_auth()
+ */
+ if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT)
+ local->last_auth = auth_type;
+}
+
+static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
+{
+ Cmd cmd;
+ Resp rsp;
+ int status;
+ SsidRid mySsid;
+ __le16 lastindex;
+ WepKeyRid wkr;
+ int rc;
+
+ memset( &mySsid, 0, sizeof( mySsid ) );
+ kfree (ai->flash);
+ ai->flash = NULL;
+
+ /* The NOP is the first step in getting the card going */
+ cmd.cmd = NOP;
+ cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
+ if (lock && down_interruptible(&ai->sem))
+ return ERROR;
+ if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) {
+ if (lock)
+ up(&ai->sem);
+ return ERROR;
+ }
+ disable_MAC( ai, 0);
+
+ // Let's figure out if we need to use the AUX port
+ if (!test_bit(FLAG_MPI,&ai->flags)) {
+ cmd.cmd = CMD_ENABLEAUX;
+ if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
+ if (lock)
+ up(&ai->sem);
+ airo_print_err(ai->dev->name, "Error checking for AUX port");
+ return ERROR;
+ }
+ if (!aux_bap || rsp.status & 0xff00) {
+ ai->bap_read = fast_bap_read;
+ airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
+ } else {
+ ai->bap_read = aux_bap_read;
+ airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
+ }
+ }
+ if (lock)
+ up(&ai->sem);
+ if (ai->config.len == 0) {
+ int i;
+ tdsRssiRid rssi_rid;
+ CapabilityRid cap_rid;
+
+ kfree(ai->SSID);
+ ai->SSID = NULL;
+ // general configuration (read/modify/write)
+ status = readConfigRid(ai, lock);
+ if ( status != SUCCESS ) return ERROR;
+
+ status = readCapabilityRid(ai, &cap_rid, lock);
+ if ( status != SUCCESS ) return ERROR;
+
+ status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
+ if ( status == SUCCESS ) {
+ if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
+ memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
+ }
+ else {
+ kfree(ai->rssi);
+ ai->rssi = NULL;
+ if (cap_rid.softCap & cpu_to_le16(8))
+ ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
+ else
+ airo_print_warn(ai->dev->name, "unknown received signal "
+ "level scale");
+ }
+ ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
+ set_auth_type(ai, AUTH_OPEN);
+ ai->config.modulation = MOD_CCK;
+
+ if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
+ (cap_rid.extSoftCap & cpu_to_le16(1)) &&
+ micsetup(ai) == SUCCESS) {
+ ai->config.opmode |= MODE_MIC;
+ set_bit(FLAG_MIC_CAPABLE, &ai->flags);
+ }
+
+ /* Save off the MAC */
+ for( i = 0; i < ETH_ALEN; i++ ) {
+ mac[i] = ai->config.macAddr[i];
+ }
+
+ /* Check to see if there are any insmod configured
+ rates to add */
+ if ( rates[0] ) {
+ memset(ai->config.rates,0,sizeof(ai->config.rates));
+ for( i = 0; i < 8 && rates[i]; i++ ) {
+ ai->config.rates[i] = rates[i];
+ }
+ }
+ set_bit (FLAG_COMMIT, &ai->flags);
+ }
+
+ /* Setup the SSIDs if present */
+ if ( ssids[0] ) {
+ int i;
+ for( i = 0; i < 3 && ssids[i]; i++ ) {
+ size_t len = strlen(ssids[i]);
+ if (len > 32)
+ len = 32;
+ mySsid.ssids[i].len = cpu_to_le16(len);
+ memcpy(mySsid.ssids[i].ssid, ssids[i], len);
+ }
+ mySsid.len = cpu_to_le16(sizeof(mySsid));
+ }
+
+ status = writeConfigRid(ai, lock);
+ if ( status != SUCCESS ) return ERROR;
+
+ /* Set up the SSID list */
+ if ( ssids[0] ) {
+ status = writeSsidRid(ai, &mySsid, lock);
+ if ( status != SUCCESS ) return ERROR;
+ }
+
+ status = enable_MAC(ai, lock);
+ if (status != SUCCESS)
+ return ERROR;
+
+ /* Grab the initial wep key, we gotta save it for auto_wep */
+ rc = readWepKeyRid(ai, &wkr, 1, lock);
+ if (rc == SUCCESS) do {
+ lastindex = wkr.kindex;
+ if (wkr.kindex == cpu_to_le16(0xffff)) {
+ ai->defindex = wkr.mac[0];
+ }
+ rc = readWepKeyRid(ai, &wkr, 0, lock);
+ } while(lastindex != wkr.kindex);
+
+ try_auto_wep(ai);
+
+ return SUCCESS;
+}
+
+static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
+ // Im really paranoid about letting it run forever!
+ int max_tries = 600000;
+
+ if (IN4500(ai, EVSTAT) & EV_CMD)
+ OUT4500(ai, EVACK, EV_CMD);
+
+ OUT4500(ai, PARAM0, pCmd->parm0);
+ OUT4500(ai, PARAM1, pCmd->parm1);
+ OUT4500(ai, PARAM2, pCmd->parm2);
+ OUT4500(ai, COMMAND, pCmd->cmd);
+
+ while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
+ if ((IN4500(ai, COMMAND)) == pCmd->cmd)
+ // PC4500 didn't notice command, try again
+ OUT4500(ai, COMMAND, pCmd->cmd);
+ if (!in_atomic() && (max_tries & 255) == 0)
+ schedule();
+ }
+
+ if ( max_tries == -1 ) {
+ airo_print_err(ai->dev->name,
+ "Max tries exceeded when issuing command");
+ if (IN4500(ai, COMMAND) & COMMAND_BUSY)
+ OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
+ return ERROR;
+ }
+
+ // command completed
+ pRsp->status = IN4500(ai, STATUS);
+ pRsp->rsp0 = IN4500(ai, RESP0);
+ pRsp->rsp1 = IN4500(ai, RESP1);
+ pRsp->rsp2 = IN4500(ai, RESP2);
+ if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
+ airo_print_err(ai->dev->name,
+ "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
+ pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
+ pRsp->rsp2);
+
+ // clear stuck command busy if necessary
+ if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
+ OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
+ }
+ // acknowledge processing the status/response
+ OUT4500(ai, EVACK, EV_CMD);
+
+ return SUCCESS;
+}
+
+/* Sets up the bap to start exchange data. whichbap should
+ * be one of the BAP0 or BAP1 defines. Locks should be held before
+ * calling! */
+static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
+{
+ int timeout = 50;
+ int max_tries = 3;
+
+ OUT4500(ai, SELECT0+whichbap, rid);
+ OUT4500(ai, OFFSET0+whichbap, offset);
+ while (1) {
+ int status = IN4500(ai, OFFSET0+whichbap);
+ if (status & BAP_BUSY) {
+ /* This isn't really a timeout, but its kinda
+ close */
+ if (timeout--) {
+ continue;
+ }
+ } else if ( status & BAP_ERR ) {
+ /* invalid rid or offset */
+ airo_print_err(ai->dev->name, "BAP error %x %d",
+ status, whichbap );
+ return ERROR;
+ } else if (status & BAP_DONE) { // success
+ return SUCCESS;
+ }
+ if ( !(max_tries--) ) {
+ airo_print_err(ai->dev->name,
+ "BAP setup error too many retries\n");
+ return ERROR;
+ }
+ // -- PC4500 missed it, try again
+ OUT4500(ai, SELECT0+whichbap, rid);
+ OUT4500(ai, OFFSET0+whichbap, offset);
+ timeout = 50;
+ }
+}
+
+/* should only be called by aux_bap_read. This aux function and the
+ following use concepts not documented in the developers guide. I
+ got them from a patch given to my by Aironet */
+static u16 aux_setup(struct airo_info *ai, u16 page,
+ u16 offset, u16 *len)
+{
+ u16 next;
+
+ OUT4500(ai, AUXPAGE, page);
+ OUT4500(ai, AUXOFF, 0);
+ next = IN4500(ai, AUXDATA);
+ *len = IN4500(ai, AUXDATA)&0xff;
+ if (offset != 4) OUT4500(ai, AUXOFF, offset);
+ return next;
+}
+
+/* requires call to bap_setup() first */
+static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
+ int bytelen, int whichbap)
+{
+ u16 len;
+ u16 page;
+ u16 offset;
+ u16 next;
+ int words;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ai->aux_lock, flags);
+ page = IN4500(ai, SWS0+whichbap);
+ offset = IN4500(ai, SWS2+whichbap);
+ next = aux_setup(ai, page, offset, &len);
+ words = (bytelen+1)>>1;
+
+ for (i=0; i<words;) {
+ int count;
+ count = (len>>1) < (words-i) ? (len>>1) : (words-i);
+ if ( !do8bitIO )
+ insw( ai->dev->base_addr+DATA0+whichbap,
+ pu16Dst+i,count );
+ else
+ insb( ai->dev->base_addr+DATA0+whichbap,
+ pu16Dst+i, count << 1 );
+ i += count;
+ if (i<words) {
+ next = aux_setup(ai, next, 4, &len);
+ }
+ }
+ spin_unlock_irqrestore(&ai->aux_lock, flags);
+ return SUCCESS;
+}
+
+
+/* requires call to bap_setup() first */
+static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
+ int bytelen, int whichbap)
+{
+ bytelen = (bytelen + 1) & (~1); // round up to even value
+ if ( !do8bitIO )
+ insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 );
+ else
+ insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen );
+ return SUCCESS;
+}
+
+/* requires call to bap_setup() first */
+static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
+ int bytelen, int whichbap)
+{
+ bytelen = (bytelen + 1) & (~1); // round up to even value
+ if ( !do8bitIO )
+ outsw( ai->dev->base_addr+DATA0+whichbap,
+ pu16Src, bytelen>>1 );
+ else
+ outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen );
+ return SUCCESS;
+}
+
+static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
+{
+ Cmd cmd; /* for issuing commands */
+ Resp rsp; /* response from commands */
+ u16 status;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd = accmd;
+ cmd.parm0 = rid;
+ status = issuecommand(ai, &cmd, &rsp);
+ if (status != 0) return status;
+ if ( (rsp.status & 0x7F00) != 0) {
+ return (accmd << 8) + (rsp.rsp0 & 0xFF);
+ }
+ return 0;
+}
+
+/* Note, that we are using BAP1 which is also used by transmit, so
+ * we must get a lock. */
+static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
+{
+ u16 status;
+ int rc = SUCCESS;
+
+ if (lock) {
+ if (down_interruptible(&ai->sem))
+ return ERROR;
+ }
+ if (test_bit(FLAG_MPI,&ai->flags)) {
+ Cmd cmd;
+ Resp rsp;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&rsp, 0, sizeof(rsp));
+ ai->config_desc.rid_desc.valid = 1;
+ ai->config_desc.rid_desc.len = RIDSIZE;
+ ai->config_desc.rid_desc.rid = 0;
+ ai->config_desc.rid_desc.host_addr = ai->ridbus;
+
+ cmd.cmd = CMD_ACCESS;
+ cmd.parm0 = rid;
+
+ memcpy_toio(ai->config_desc.card_ram_off,
+ &ai->config_desc.rid_desc, sizeof(Rid));
+
+ rc = issuecommand(ai, &cmd, &rsp);
+
+ if (rsp.status & 0x7f00)
+ rc = rsp.rsp0;
+ if (!rc)
+ memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
+ goto done;
+ } else {
+ if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
+ rc = status;
+ goto done;
+ }
+ if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
+ rc = ERROR;
+ goto done;
+ }
+ // read the rid length field
+ bap_read(ai, pBuf, 2, BAP1);
+ // length for remaining part of rid
+ len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
+
+ if ( len <= 2 ) {
+ airo_print_err(ai->dev->name,
+ "Rid %x has a length of %d which is too short",
+ (int)rid, (int)len );
+ rc = ERROR;
+ goto done;
+ }
+ // read remainder of the rid
+ rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
+ }
+done:
+ if (lock)
+ up(&ai->sem);
+ return rc;
+}
+
+/* Note, that we are using BAP1 which is also used by transmit, so
+ * make sure this isn't called when a transmit is happening */
+static int PC4500_writerid(struct airo_info *ai, u16 rid,
+ const void *pBuf, int len, int lock)
+{
+ u16 status;
+ int rc = SUCCESS;
+
+ *(__le16*)pBuf = cpu_to_le16((u16)len);
+
+ if (lock) {
+ if (down_interruptible(&ai->sem))
+ return ERROR;
+ }
+ if (test_bit(FLAG_MPI,&ai->flags)) {
+ Cmd cmd;
+ Resp rsp;
+
+ if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
+ airo_print_err(ai->dev->name,
+ "%s: MAC should be disabled (rid=%04x)",
+ __func__, rid);
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&rsp, 0, sizeof(rsp));
+
+ ai->config_desc.rid_desc.valid = 1;
+ ai->config_desc.rid_desc.len = *((u16 *)pBuf);
+ ai->config_desc.rid_desc.rid = 0;
+
+ cmd.cmd = CMD_WRITERID;
+ cmd.parm0 = rid;
+
+ memcpy_toio(ai->config_desc.card_ram_off,
+ &ai->config_desc.rid_desc, sizeof(Rid));
+
+ if (len < 4 || len > 2047) {
+ airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
+ rc = -1;
+ } else {
+ memcpy(ai->config_desc.virtual_host_addr,
+ pBuf, len);
+
+ rc = issuecommand(ai, &cmd, &rsp);
+ if ((rc & 0xff00) != 0) {
+ airo_print_err(ai->dev->name, "%s: Write rid Error %d",
+ __func__, rc);
+ airo_print_err(ai->dev->name, "%s: Cmd=%04x",
+ __func__, cmd.cmd);
+ }
+
+ if ((rsp.status & 0x7f00))
+ rc = rsp.rsp0;
+ }
+ } else {
+ // --- first access so that we can write the rid data
+ if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
+ rc = status;
+ goto done;
+ }
+ // --- now write the rid data
+ if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
+ rc = ERROR;
+ goto done;
+ }
+ bap_write(ai, pBuf, len, BAP1);
+ // ---now commit the rid data
+ rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
+ }
+done:
+ if (lock)
+ up(&ai->sem);
+ return rc;
+}
+
+/* Allocates a FID to be used for transmitting packets. We only use
+ one for now. */
+static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
+{
+ unsigned int loop = 3000;
+ Cmd cmd;
+ Resp rsp;
+ u16 txFid;
+ __le16 txControl;
+
+ cmd.cmd = CMD_ALLOCATETX;
+ cmd.parm0 = lenPayload;
+ if (down_interruptible(&ai->sem))
+ return ERROR;
+ if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
+ txFid = ERROR;
+ goto done;
+ }
+ if ( (rsp.status & 0xFF00) != 0) {
+ txFid = ERROR;
+ goto done;
+ }
+ /* wait for the allocate event/indication
+ * It makes me kind of nervous that this can just sit here and spin,
+ * but in practice it only loops like four times. */
+ while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
+ if (!loop) {
+ txFid = ERROR;
+ goto done;
+ }
+
+ // get the allocated fid and acknowledge
+ txFid = IN4500(ai, TXALLOCFID);
+ OUT4500(ai, EVACK, EV_ALLOC);
+
+ /* The CARD is pretty cool since it converts the ethernet packet
+ * into 802.11. Also note that we don't release the FID since we
+ * will be using the same one over and over again. */
+ /* We only have to setup the control once since we are not
+ * releasing the fid. */
+ if (raw)
+ txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
+ | TXCTL_ETHERNET | TXCTL_NORELEASE);
+ else
+ txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
+ | TXCTL_ETHERNET | TXCTL_NORELEASE);
+ if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
+ txFid = ERROR;
+ else
+ bap_write(ai, &txControl, sizeof(txControl), BAP1);
+
+done:
+ up(&ai->sem);
+
+ return txFid;
+}
+
+/* In general BAP1 is dedicated to transmiting packets. However,
+ since we need a BAP when accessing RIDs, we also use BAP1 for that.
+ Make sure the BAP1 spinlock is held when this is called. */
+static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
+{
+ __le16 payloadLen;
+ Cmd cmd;
+ Resp rsp;
+ int miclen = 0;
+ u16 txFid = len;
+ MICBuffer pMic;
+
+ len >>= 16;
+
+ if (len <= ETH_ALEN * 2) {
+ airo_print_warn(ai->dev->name, "Short packet %d", len);
+ return ERROR;
+ }
+ len -= ETH_ALEN * 2;
+
+ if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
+ (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
+ if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
+ return ERROR;
+ miclen = sizeof(pMic);
+ }
+ // packet is destination[6], source[6], payload[len-12]
+ // write the payload length and dst/src/payload
+ if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
+ /* The hardware addresses aren't counted as part of the payload, so
+ * we have to subtract the 12 bytes for the addresses off */
+ payloadLen = cpu_to_le16(len + miclen);
+ bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
+ bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
+ if (miclen)
+ bap_write(ai, (__le16*)&pMic, miclen, BAP1);
+ bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
+ // issue the transmit command
+ memset( &cmd, 0, sizeof( cmd ) );
+ cmd.cmd = CMD_TRANSMIT;
+ cmd.parm0 = txFid;
+ if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
+ if ( (rsp.status & 0xFF00) != 0) return ERROR;
+ return SUCCESS;
+}
+
+static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
+{
+ __le16 fc, payloadLen;
+ Cmd cmd;
+ Resp rsp;
+ int hdrlen;
+ static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
+ /* padding of header to full size + le16 gaplen (6) + gaplen bytes */
+ u16 txFid = len;
+ len >>= 16;
+
+ fc = *(__le16*)pPacket;
+ hdrlen = header_len(fc);
+
+ if (len < hdrlen) {
+ airo_print_warn(ai->dev->name, "Short packet %d", len);
+ return ERROR;
+ }
+
+ /* packet is 802.11 header + payload
+ * write the payload length and dst/src/payload */
+ if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
+ /* The 802.11 header aren't counted as part of the payload, so
+ * we have to subtract the header bytes off */
+ payloadLen = cpu_to_le16(len-hdrlen);
+ bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
+ if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
+ bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
+ bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
+
+ bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
+ // issue the transmit command
+ memset( &cmd, 0, sizeof( cmd ) );
+ cmd.cmd = CMD_TRANSMIT;
+ cmd.parm0 = txFid;
+ if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
+ if ( (rsp.status & 0xFF00) != 0) return ERROR;
+ return SUCCESS;
+}
+
+/*
+ * This is the proc_fs routines. It is a bit messier than I would
+ * like! Feel free to clean it up!
+ */
+
+static ssize_t proc_read( struct file *file,
+ char __user *buffer,
+ size_t len,
+ loff_t *offset);
+
+static ssize_t proc_write( struct file *file,
+ const char __user *buffer,
+ size_t len,
+ loff_t *offset );
+static int proc_close( struct inode *inode, struct file *file );
+
+static int proc_stats_open( struct inode *inode, struct file *file );
+static int proc_statsdelta_open( struct inode *inode, struct file *file );
+static int proc_status_open( struct inode *inode, struct file *file );
+static int proc_SSID_open( struct inode *inode, struct file *file );
+static int proc_APList_open( struct inode *inode, struct file *file );
+static int proc_BSSList_open( struct inode *inode, struct file *file );
+static int proc_config_open( struct inode *inode, struct file *file );
+static int proc_wepkey_open( struct inode *inode, struct file *file );
+
+static const struct file_operations proc_statsdelta_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .open = proc_statsdelta_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations proc_stats_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .open = proc_stats_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations proc_status_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .open = proc_status_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations proc_SSID_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .write = proc_write,
+ .open = proc_SSID_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations proc_BSSList_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .write = proc_write,
+ .open = proc_BSSList_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations proc_APList_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .write = proc_write,
+ .open = proc_APList_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations proc_config_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .write = proc_write,
+ .open = proc_config_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations proc_wepkey_ops = {
+ .owner = THIS_MODULE,
+ .read = proc_read,
+ .write = proc_write,
+ .open = proc_wepkey_open,
+ .release = proc_close,
+ .llseek = default_llseek,
+};
+
+static struct proc_dir_entry *airo_entry;
+
+struct proc_data {
+ int release_buffer;
+ int readlen;
+ char *rbuffer;
+ int writelen;
+ int maxwritelen;
+ char *wbuffer;
+ void (*on_close) (struct inode *, struct file *);
+};
+
+static int setup_proc_entry( struct net_device *dev,
+ struct airo_info *apriv ) {
+ struct proc_dir_entry *entry;
+
+ /* First setup the device directory */
+ strcpy(apriv->proc_name,dev->name);
+ apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm,
+ airo_entry);
+ if (!apriv->proc_entry)
+ return -ENOMEM;
+ proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid);
+
+ /* Setup the StatsDelta */
+ entry = proc_create_data("StatsDelta", S_IRUGO & proc_perm,
+ apriv->proc_entry, &proc_statsdelta_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+
+ /* Setup the Stats */
+ entry = proc_create_data("Stats", S_IRUGO & proc_perm,
+ apriv->proc_entry, &proc_stats_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+
+ /* Setup the Status */
+ entry = proc_create_data("Status", S_IRUGO & proc_perm,
+ apriv->proc_entry, &proc_status_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+
+ /* Setup the Config */
+ entry = proc_create_data("Config", proc_perm,
+ apriv->proc_entry, &proc_config_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+
+ /* Setup the SSID */
+ entry = proc_create_data("SSID", proc_perm,
+ apriv->proc_entry, &proc_SSID_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+
+ /* Setup the APList */
+ entry = proc_create_data("APList", proc_perm,
+ apriv->proc_entry, &proc_APList_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+
+ /* Setup the BSSList */
+ entry = proc_create_data("BSSList", proc_perm,
+ apriv->proc_entry, &proc_BSSList_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+
+ /* Setup the WepKey */
+ entry = proc_create_data("WepKey", proc_perm,
+ apriv->proc_entry, &proc_wepkey_ops, dev);
+ if (!entry)
+ goto fail;
+ proc_set_user(entry, proc_kuid, proc_kgid);
+ return 0;
+
+fail:
+ remove_proc_subtree(apriv->proc_name, airo_entry);
+ return -ENOMEM;
+}
+
+static int takedown_proc_entry( struct net_device *dev,
+ struct airo_info *apriv )
+{
+ remove_proc_subtree(apriv->proc_name, airo_entry);
+ return 0;
+}
+
+/*
+ * What we want from the proc_fs is to be able to efficiently read
+ * and write the configuration. To do this, we want to read the
+ * configuration when the file is opened and write it when the file is
+ * closed. So basically we allocate a read buffer at open and fill it
+ * with data, and allocate a write buffer and read it at close.
+ */
+
+/*
+ * The read routine is generic, it relies on the preallocated rbuffer
+ * to supply the data.
+ */
+static ssize_t proc_read( struct file *file,
+ char __user *buffer,
+ size_t len,
+ loff_t *offset )
+{
+ struct proc_data *priv = file->private_data;
+
+ if (!priv->rbuffer)
+ return -EINVAL;
+
+ return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
+ priv->readlen);
+}
+
+/*
+ * The write routine is generic, it fills in a preallocated rbuffer
+ * to supply the data.
+ */
+static ssize_t proc_write( struct file *file,
+ const char __user *buffer,
+ size_t len,
+ loff_t *offset )
+{
+ ssize_t ret;
+ struct proc_data *priv = file->private_data;
+
+ if (!priv->wbuffer)
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset,
+ buffer, len);
+ if (ret > 0)
+ priv->writelen = max_t(int, priv->writelen, *offset);
+
+ return ret;
+}
+
+static int proc_status_open(struct inode *inode, struct file *file)
+{
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *apriv = dev->ml_priv;
+ CapabilityRid cap_rid;
+ StatusRid status_rid;
+ u16 mode;
+ int i;
+
+ if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ data = file->private_data;
+ if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+
+ readStatusRid(apriv, &status_rid, 1);
+ readCapabilityRid(apriv, &cap_rid, 1);
+
+ mode = le16_to_cpu(status_rid.mode);
+
+ i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
+ mode & 1 ? "CFG ": "",
+ mode & 2 ? "ACT ": "",
+ mode & 0x10 ? "SYN ": "",
+ mode & 0x20 ? "LNK ": "",
+ mode & 0x40 ? "LEAP ": "",
+ mode & 0x80 ? "PRIV ": "",
+ mode & 0x100 ? "KEY ": "",
+ mode & 0x200 ? "WEP ": "",
+ mode & 0x8000 ? "ERR ": "");
+ sprintf( data->rbuffer+i, "Mode: %x\n"
+ "Signal Strength: %d\n"
+ "Signal Quality: %d\n"
+ "SSID: %-.*s\n"
+ "AP: %-.16s\n"
+ "Freq: %d\n"
+ "BitRate: %dmbs\n"
+ "Driver Version: %s\n"
+ "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
+ "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
+ "Software Version: %x\nSoftware Subversion: %x\n"
+ "Boot block version: %x\n",
+ le16_to_cpu(status_rid.mode),
+ le16_to_cpu(status_rid.normalizedSignalStrength),
+ le16_to_cpu(status_rid.signalQuality),
+ le16_to_cpu(status_rid.SSIDlen),
+ status_rid.SSID,
+ status_rid.apName,
+ le16_to_cpu(status_rid.channel),
+ le16_to_cpu(status_rid.currentXmitRate) / 2,
+ version,
+ cap_rid.prodName,
+ cap_rid.manName,
+ cap_rid.prodVer,
+ le16_to_cpu(cap_rid.radioType),
+ le16_to_cpu(cap_rid.country),
+ le16_to_cpu(cap_rid.hardVer),
+ le16_to_cpu(cap_rid.softVer),
+ le16_to_cpu(cap_rid.softSubVer),
+ le16_to_cpu(cap_rid.bootBlockVer));
+ data->readlen = strlen( data->rbuffer );
+ return 0;
+}
+
+static int proc_stats_rid_open(struct inode*, struct file*, u16);
+static int proc_statsdelta_open( struct inode *inode,
+ struct file *file ) {
+ if (file->f_mode&FMODE_WRITE) {
+ return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
+ }
+ return proc_stats_rid_open(inode, file, RID_STATSDELTA);
+}
+
+static int proc_stats_open( struct inode *inode, struct file *file ) {
+ return proc_stats_rid_open(inode, file, RID_STATS);
+}
+
+static int proc_stats_rid_open( struct inode *inode,
+ struct file *file,
+ u16 rid )
+{
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *apriv = dev->ml_priv;
+ StatsRid stats;
+ int i, j;
+ __le32 *vals = stats.vals;
+ int len;
+
+ if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ data = file->private_data;
+ if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+
+ readStatsRid(apriv, &stats, rid, 1);
+ len = le16_to_cpu(stats.len);
+
+ j = 0;
+ for(i=0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
+ if (!statsLabels[i]) continue;
+ if (j+strlen(statsLabels[i])+16>4096) {
+ airo_print_warn(apriv->dev->name,
+ "Potentially disastrous buffer overflow averted!");
+ break;
+ }
+ j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
+ le32_to_cpu(vals[i]));
+ }
+ if (i*4 >= len) {
+ airo_print_warn(apriv->dev->name, "Got a short rid");
+ }
+ data->readlen = j;
+ return 0;
+}
+
+static int get_dec_u16( char *buffer, int *start, int limit ) {
+ u16 value;
+ int valid = 0;
+ for (value = 0; *start < limit && buffer[*start] >= '0' &&
+ buffer[*start] <= '9'; (*start)++) {
+ valid = 1;
+ value *= 10;
+ value += buffer[*start] - '0';
+ }
+ if ( !valid ) return -1;
+ return value;
+}
+
+static int airo_config_commit(struct net_device *dev,
+ struct iw_request_info *info, void *zwrq,
+ char *extra);
+
+static inline int sniffing_mode(struct airo_info *ai)
+{
+ return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >=
+ le16_to_cpu(RXMODE_RFMON);
+}
+
+static void proc_config_on_close(struct inode *inode, struct file *file)
+{
+ struct proc_data *data = file->private_data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ char *line;
+
+ if ( !data->writelen ) return;
+
+ readConfigRid(ai, 1);
+ set_bit (FLAG_COMMIT, &ai->flags);
+
+ line = data->wbuffer;
+ while( line[0] ) {
+/*** Mode processing */
+ if ( !strncmp( line, "Mode: ", 6 ) ) {
+ line += 6;
+ if (sniffing_mode(ai))
+ set_bit (FLAG_RESET, &ai->flags);
+ ai->config.rmode &= ~RXMODE_FULL_MASK;
+ clear_bit (FLAG_802_11, &ai->flags);
+ ai->config.opmode &= ~MODE_CFG_MASK;
+ ai->config.scanMode = SCANMODE_ACTIVE;
+ if ( line[0] == 'a' ) {
+ ai->config.opmode |= MODE_STA_IBSS;
+ } else {
+ ai->config.opmode |= MODE_STA_ESS;
+ if ( line[0] == 'r' ) {
+ ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
+ ai->config.scanMode = SCANMODE_PASSIVE;
+ set_bit (FLAG_802_11, &ai->flags);
+ } else if ( line[0] == 'y' ) {
+ ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
+ ai->config.scanMode = SCANMODE_PASSIVE;
+ set_bit (FLAG_802_11, &ai->flags);
+ } else if ( line[0] == 'l' )
+ ai->config.rmode |= RXMODE_LANMON;
+ }
+ set_bit (FLAG_COMMIT, &ai->flags);
+ }
+
+/*** Radio status */
+ else if (!strncmp(line,"Radio: ", 7)) {
+ line += 7;
+ if (!strncmp(line,"off",3)) {
+ set_bit (FLAG_RADIO_OFF, &ai->flags);
+ } else {
+ clear_bit (FLAG_RADIO_OFF, &ai->flags);
+ }
+ }
+/*** NodeName processing */
+ else if ( !strncmp( line, "NodeName: ", 10 ) ) {
+ int j;
+
+ line += 10;
+ memset( ai->config.nodeName, 0, 16 );
+/* Do the name, assume a space between the mode and node name */
+ for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
+ ai->config.nodeName[j] = line[j];
+ }
+ set_bit (FLAG_COMMIT, &ai->flags);
+ }
+
+/*** PowerMode processing */
+ else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
+ line += 11;
+ if ( !strncmp( line, "PSPCAM", 6 ) ) {
+ ai->config.powerSaveMode = POWERSAVE_PSPCAM;
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "PSP", 3 ) ) {
+ ai->config.powerSaveMode = POWERSAVE_PSP;
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else {
+ ai->config.powerSaveMode = POWERSAVE_CAM;
+ set_bit (FLAG_COMMIT, &ai->flags);
+ }
+ } else if ( !strncmp( line, "DataRates: ", 11 ) ) {
+ int v, i = 0, k = 0; /* i is index into line,
+ k is index to rates */
+
+ line += 11;
+ while((v = get_dec_u16(line, &i, 3))!=-1) {
+ ai->config.rates[k++] = (u8)v;
+ line += i + 1;
+ i = 0;
+ }
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "Channel: ", 9 ) ) {
+ int v, i = 0;
+ line += 9;
+ v = get_dec_u16(line, &i, i+3);
+ if ( v != -1 ) {
+ ai->config.channelSet = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ }
+ } else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
+ int v, i = 0;
+ line += 11;
+ v = get_dec_u16(line, &i, i+3);
+ if ( v != -1 ) {
+ ai->config.txPower = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ }
+ } else if ( !strncmp( line, "WEP: ", 5 ) ) {
+ line += 5;
+ switch( line[0] ) {
+ case 's':
+ set_auth_type(ai, AUTH_SHAREDKEY);
+ break;
+ case 'e':
+ set_auth_type(ai, AUTH_ENCRYPT);
+ break;
+ default:
+ set_auth_type(ai, AUTH_OPEN);
+ break;
+ }
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
+ int v, i = 0;
+
+ line += 16;
+ v = get_dec_u16(line, &i, 3);
+ v = (v<0) ? 0 : ((v>255) ? 255 : v);
+ ai->config.longRetryLimit = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
+ int v, i = 0;
+
+ line += 17;
+ v = get_dec_u16(line, &i, 3);
+ v = (v<0) ? 0 : ((v>255) ? 255 : v);
+ ai->config.shortRetryLimit = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
+ int v, i = 0;
+
+ line += 14;
+ v = get_dec_u16(line, &i, 4);
+ v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
+ ai->config.rtsThres = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
+ int v, i = 0;
+
+ line += 16;
+ v = get_dec_u16(line, &i, 5);
+ v = (v<0) ? 0 : v;
+ ai->config.txLifetime = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
+ int v, i = 0;
+
+ line += 16;
+ v = get_dec_u16(line, &i, 5);
+ v = (v<0) ? 0 : v;
+ ai->config.rxLifetime = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
+ ai->config.txDiversity =
+ (line[13]=='l') ? 1 :
+ ((line[13]=='r')? 2: 3);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
+ ai->config.rxDiversity =
+ (line[13]=='l') ? 1 :
+ ((line[13]=='r')? 2: 3);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
+ int v, i = 0;
+
+ line += 15;
+ v = get_dec_u16(line, &i, 4);
+ v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
+ v = v & 0xfffe; /* Make sure its even */
+ ai->config.fragThresh = cpu_to_le16(v);
+ set_bit (FLAG_COMMIT, &ai->flags);
+ } else if (!strncmp(line, "Modulation: ", 12)) {
+ line += 12;
+ switch(*line) {
+ case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
+ case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
+ case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
+ default: airo_print_warn(ai->dev->name, "Unknown modulation");
+ }
+ } else if (!strncmp(line, "Preamble: ", 10)) {
+ line += 10;
+ switch(*line) {
+ case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
+ case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
+ case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
+ default: airo_print_warn(ai->dev->name, "Unknown preamble");
+ }
+ } else {
+ airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
+ }
+ while( line[0] && line[0] != '\n' ) line++;
+ if ( line[0] ) line++;
+ }
+ airo_config_commit(dev, NULL, NULL, NULL);
+}
+
+static const char *get_rmode(__le16 mode)
+{
+ switch(mode & RXMODE_MASK) {
+ case RXMODE_RFMON: return "rfmon";
+ case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
+ case RXMODE_LANMON: return "lanmon";
+ }
+ return "ESS";
+}
+
+static int proc_config_open(struct inode *inode, struct file *file)
+{
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ int i;
+ __le16 mode;
+
+ if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ data = file->private_data;
+ if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) {
+ kfree (data->rbuffer);
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->maxwritelen = 2048;
+ data->on_close = proc_config_on_close;
+
+ readConfigRid(ai, 1);
+
+ mode = ai->config.opmode & MODE_CFG_MASK;
+ i = sprintf( data->rbuffer,
+ "Mode: %s\n"
+ "Radio: %s\n"
+ "NodeName: %-16s\n"
+ "PowerMode: %s\n"
+ "DataRates: %d %d %d %d %d %d %d %d\n"
+ "Channel: %d\n"
+ "XmitPower: %d\n",
+ mode == MODE_STA_IBSS ? "adhoc" :
+ mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
+ mode == MODE_AP ? "AP" :
+ mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
+ test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
+ ai->config.nodeName,
+ ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
+ ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
+ ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
+ "Error",
+ (int)ai->config.rates[0],
+ (int)ai->config.rates[1],
+ (int)ai->config.rates[2],
+ (int)ai->config.rates[3],
+ (int)ai->config.rates[4],
+ (int)ai->config.rates[5],
+ (int)ai->config.rates[6],
+ (int)ai->config.rates[7],
+ le16_to_cpu(ai->config.channelSet),
+ le16_to_cpu(ai->config.txPower)
+ );
+ sprintf( data->rbuffer + i,
+ "LongRetryLimit: %d\n"
+ "ShortRetryLimit: %d\n"
+ "RTSThreshold: %d\n"
+ "TXMSDULifetime: %d\n"
+ "RXMSDULifetime: %d\n"
+ "TXDiversity: %s\n"
+ "RXDiversity: %s\n"
+ "FragThreshold: %d\n"
+ "WEP: %s\n"
+ "Modulation: %s\n"
+ "Preamble: %s\n",
+ le16_to_cpu(ai->config.longRetryLimit),
+ le16_to_cpu(ai->config.shortRetryLimit),
+ le16_to_cpu(ai->config.rtsThres),
+ le16_to_cpu(ai->config.txLifetime),
+ le16_to_cpu(ai->config.rxLifetime),
+ ai->config.txDiversity == 1 ? "left" :
+ ai->config.txDiversity == 2 ? "right" : "both",
+ ai->config.rxDiversity == 1 ? "left" :
+ ai->config.rxDiversity == 2 ? "right" : "both",
+ le16_to_cpu(ai->config.fragThresh),
+ ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
+ ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
+ ai->config.modulation == MOD_DEFAULT ? "default" :
+ ai->config.modulation == MOD_CCK ? "cck" :
+ ai->config.modulation == MOD_MOK ? "mok" : "error",
+ ai->config.preamble == PREAMBLE_AUTO ? "auto" :
+ ai->config.preamble == PREAMBLE_LONG ? "long" :
+ ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
+ );
+ data->readlen = strlen( data->rbuffer );
+ return 0;
+}
+
+static void proc_SSID_on_close(struct inode *inode, struct file *file)
+{
+ struct proc_data *data = file->private_data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ SsidRid SSID_rid;
+ int i;
+ char *p = data->wbuffer;
+ char *end = p + data->writelen;
+
+ if (!data->writelen)
+ return;
+
+ *end = '\n'; /* sentinel; we have space for it */
+
+ memset(&SSID_rid, 0, sizeof(SSID_rid));
+
+ for (i = 0; i < 3 && p < end; i++) {
+ int j = 0;
+ /* copy up to 32 characters from this line */
+ while (*p != '\n' && j < 32)
+ SSID_rid.ssids[i].ssid[j++] = *p++;
+ if (j == 0)
+ break;
+ SSID_rid.ssids[i].len = cpu_to_le16(j);
+ /* skip to the beginning of the next line */
+ while (*p++ != '\n')
+ ;
+ }
+ if (i)
+ SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
+ disable_MAC(ai, 1);
+ writeSsidRid(ai, &SSID_rid, 1);
+ enable_MAC(ai, 1);
+}
+
+static void proc_APList_on_close( struct inode *inode, struct file *file ) {
+ struct proc_data *data = file->private_data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ APListRid *APList_rid = &ai->APList;
+ int i;
+
+ if ( !data->writelen ) return;
+
+ memset(APList_rid, 0, sizeof(*APList_rid));
+ APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
+
+ for (i = 0; i < 4 && data->writelen >= (i + 1) * 6 * 3; i++)
+ mac_pton(data->wbuffer + i * 6 * 3, APList_rid->ap[i]);
+
+ disable_MAC(ai, 1);
+ writeAPListRid(ai, APList_rid, 1);
+ enable_MAC(ai, 1);
+}
+
+/* This function wraps PC4500_writerid with a MAC disable */
+static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
+ int len, int dummy ) {
+ int rc;
+
+ disable_MAC(ai, 1);
+ rc = PC4500_writerid(ai, rid, rid_data, len, 1);
+ enable_MAC(ai, 1);
+ return rc;
+}
+
+/* Returns the WEP key at the specified index, or -1 if that key does
+ * not exist. The buffer is assumed to be at least 16 bytes in length.
+ */
+static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen)
+{
+ WepKeyRid wkr;
+ int rc;
+ __le16 lastindex;
+
+ rc = readWepKeyRid(ai, &wkr, 1, 1);
+ if (rc != SUCCESS)
+ return -1;
+ do {
+ lastindex = wkr.kindex;
+ if (le16_to_cpu(wkr.kindex) == index) {
+ int klen = min_t(int, buflen, le16_to_cpu(wkr.klen));
+ memcpy(buf, wkr.key, klen);
+ return klen;
+ }
+ rc = readWepKeyRid(ai, &wkr, 0, 1);
+ if (rc != SUCCESS)
+ return -1;
+ } while (lastindex != wkr.kindex);
+ return -1;
+}
+
+static int get_wep_tx_idx(struct airo_info *ai)
+{
+ WepKeyRid wkr;
+ int rc;
+ __le16 lastindex;
+
+ rc = readWepKeyRid(ai, &wkr, 1, 1);
+ if (rc != SUCCESS)
+ return -1;
+ do {
+ lastindex = wkr.kindex;
+ if (wkr.kindex == cpu_to_le16(0xffff))
+ return wkr.mac[0];
+ rc = readWepKeyRid(ai, &wkr, 0, 1);
+ if (rc != SUCCESS)
+ return -1;
+ } while (lastindex != wkr.kindex);
+ return -1;
+}
+
+static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
+ u16 keylen, int perm, int lock)
+{
+ static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
+ WepKeyRid wkr;
+ int rc;
+
+ if (WARN_ON(keylen == 0))
+ return -1;
+
+ memset(&wkr, 0, sizeof(wkr));
+ wkr.len = cpu_to_le16(sizeof(wkr));
+ wkr.kindex = cpu_to_le16(index);
+ wkr.klen = cpu_to_le16(keylen);
+ memcpy(wkr.key, key, keylen);
+ memcpy(wkr.mac, macaddr, ETH_ALEN);
+
+ if (perm) disable_MAC(ai, lock);
+ rc = writeWepKeyRid(ai, &wkr, perm, lock);
+ if (perm) enable_MAC(ai, lock);
+ return rc;
+}
+
+static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock)
+{
+ WepKeyRid wkr;
+ int rc;
+
+ memset(&wkr, 0, sizeof(wkr));
+ wkr.len = cpu_to_le16(sizeof(wkr));
+ wkr.kindex = cpu_to_le16(0xffff);
+ wkr.mac[0] = (char)index;
+
+ if (perm) {
+ ai->defindex = (char)index;
+ disable_MAC(ai, lock);
+ }
+
+ rc = writeWepKeyRid(ai, &wkr, perm, lock);
+
+ if (perm)
+ enable_MAC(ai, lock);
+ return rc;
+}
+
+static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ int i, rc;
+ char key[16];
+ u16 index = 0;
+ int j = 0;
+
+ memset(key, 0, sizeof(key));
+
+ data = file->private_data;
+ if ( !data->writelen ) return;
+
+ if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
+ (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
+ index = data->wbuffer[0] - '0';
+ if (data->wbuffer[1] == '\n') {
+ rc = set_wep_tx_idx(ai, index, 1, 1);
+ if (rc < 0) {
+ airo_print_err(ai->dev->name, "failed to set "
+ "WEP transmit index to %d: %d.",
+ index, rc);
+ }
+ return;
+ }
+ j = 2;
+ } else {
+ airo_print_err(ai->dev->name, "WepKey passed invalid key index");
+ return;
+ }
+
+ for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) {
+ switch(i%3) {
+ case 0:
+ key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4;
+ break;
+ case 1:
+ key[i/3] |= hex_to_bin(data->wbuffer[i+j]);
+ break;
+ }
+ }
+
+ rc = set_wep_key(ai, index, key, i/3, 1, 1);
+ if (rc < 0) {
+ airo_print_err(ai->dev->name, "failed to set WEP key at index "
+ "%d: %d.", index, rc);
+ }
+}
+
+static int proc_wepkey_open( struct inode *inode, struct file *file )
+{
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ char *ptr;
+ WepKeyRid wkr;
+ __le16 lastindex;
+ int j=0;
+ int rc;
+
+ if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ memset(&wkr, 0, sizeof(wkr));
+ data = file->private_data;
+ if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) {
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->writelen = 0;
+ data->maxwritelen = 80;
+ if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) {
+ kfree (data->rbuffer);
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->on_close = proc_wepkey_on_close;
+
+ ptr = data->rbuffer;
+ strcpy(ptr, "No wep keys\n");
+ rc = readWepKeyRid(ai, &wkr, 1, 1);
+ if (rc == SUCCESS) do {
+ lastindex = wkr.kindex;
+ if (wkr.kindex == cpu_to_le16(0xffff)) {
+ j += sprintf(ptr+j, "Tx key = %d\n",
+ (int)wkr.mac[0]);
+ } else {
+ j += sprintf(ptr+j, "Key %d set with length = %d\n",
+ le16_to_cpu(wkr.kindex),
+ le16_to_cpu(wkr.klen));
+ }
+ readWepKeyRid(ai, &wkr, 0, 1);
+ } while((lastindex != wkr.kindex) && (j < 180-30));
+
+ data->readlen = strlen( data->rbuffer );
+ return 0;
+}
+
+static int proc_SSID_open(struct inode *inode, struct file *file)
+{
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ int i;
+ char *ptr;
+ SsidRid SSID_rid;
+
+ if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ data = file->private_data;
+ if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->writelen = 0;
+ data->maxwritelen = 33*3;
+ /* allocate maxwritelen + 1; we'll want a sentinel */
+ if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
+ kfree (data->rbuffer);
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->on_close = proc_SSID_on_close;
+
+ readSsidRid(ai, &SSID_rid);
+ ptr = data->rbuffer;
+ for (i = 0; i < 3; i++) {
+ int j;
+ size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
+ if (!len)
+ break;
+ if (len > 32)
+ len = 32;
+ for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
+ *ptr++ = SSID_rid.ssids[i].ssid[j];
+ *ptr++ = '\n';
+ }
+ *ptr = '\0';
+ data->readlen = strlen( data->rbuffer );
+ return 0;
+}
+
+static int proc_APList_open( struct inode *inode, struct file *file ) {
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ int i;
+ char *ptr;
+ APListRid *APList_rid = &ai->APList;
+
+ if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ data = file->private_data;
+ if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->writelen = 0;
+ data->maxwritelen = 4*6*3;
+ if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) {
+ kfree (data->rbuffer);
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->on_close = proc_APList_on_close;
+
+ ptr = data->rbuffer;
+ for( i = 0; i < 4; i++ ) {
+// We end when we find a zero MAC
+ if ( !*(int*)APList_rid->ap[i] &&
+ !*(int*)&APList_rid->ap[i][2]) break;
+ ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]);
+ }
+ if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
+
+ *ptr = '\0';
+ data->readlen = strlen( data->rbuffer );
+ return 0;
+}
+
+static int proc_BSSList_open( struct inode *inode, struct file *file ) {
+ struct proc_data *data;
+ struct net_device *dev = PDE_DATA(inode);
+ struct airo_info *ai = dev->ml_priv;
+ char *ptr;
+ BSSListRid BSSList_rid;
+ int rc;
+ /* If doLoseSync is not 1, we won't do a Lose Sync */
+ int doLoseSync = -1;
+
+ if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ data = file->private_data;
+ if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) {
+ kfree (file->private_data);
+ return -ENOMEM;
+ }
+ data->writelen = 0;
+ data->maxwritelen = 0;
+ data->wbuffer = NULL;
+ data->on_close = NULL;
+
+ if (file->f_mode & FMODE_WRITE) {
+ if (!(file->f_mode & FMODE_READ)) {
+ Cmd cmd;
+ Resp rsp;
+
+ if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd=CMD_LISTBSS;
+ if (down_interruptible(&ai->sem))
+ return -ERESTARTSYS;
+ issuecommand(ai, &cmd, &rsp);
+ up(&ai->sem);
+ data->readlen = 0;
+ return 0;
+ }
+ doLoseSync = 1;
+ }
+ ptr = data->rbuffer;
+ /* There is a race condition here if there are concurrent opens.
+ Since it is a rare condition, we'll just live with it, otherwise
+ we have to add a spin lock... */
+ rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
+ while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
+ ptr += sprintf(ptr, "%pM %*s rssi = %d",
+ BSSList_rid.bssid,
+ (int)BSSList_rid.ssidLen,
+ BSSList_rid.ssid,
+ le16_to_cpu(BSSList_rid.dBm));
+ ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
+ le16_to_cpu(BSSList_rid.dsChannel),
+ BSSList_rid.cap & CAP_ESS ? "ESS" : "",
+ BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
+ BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
+ BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
+ rc = readBSSListRid(ai, 0, &BSSList_rid);
+ }
+ *ptr = '\0';
+ data->readlen = strlen( data->rbuffer );
+ return 0;
+}
+
+static int proc_close( struct inode *inode, struct file *file )
+{
+ struct proc_data *data = file->private_data;
+
+ if (data->on_close != NULL)
+ data->on_close(inode, file);
+ kfree(data->rbuffer);
+ kfree(data->wbuffer);
+ kfree(data);
+ return 0;
+}
+
+/* Since the card doesn't automatically switch to the right WEP mode,
+ we will make it do it. If the card isn't associated, every secs we
+ will switch WEP modes to see if that will help. If the card is
+ associated we will check every minute to see if anything has
+ changed. */
+static void timer_func( struct net_device *dev ) {
+ struct airo_info *apriv = dev->ml_priv;
+
+/* We don't have a link so try changing the authtype */
+ readConfigRid(apriv, 0);
+ disable_MAC(apriv, 0);
+ switch(apriv->config.authType) {
+ case AUTH_ENCRYPT:
+/* So drop to OPEN */
+ apriv->config.authType = AUTH_OPEN;
+ break;
+ case AUTH_SHAREDKEY:
+ if (apriv->keyindex < auto_wep) {
+ set_wep_tx_idx(apriv, apriv->keyindex, 0, 0);
+ apriv->config.authType = AUTH_SHAREDKEY;
+ apriv->keyindex++;
+ } else {
+ /* Drop to ENCRYPT */
+ apriv->keyindex = 0;
+ set_wep_tx_idx(apriv, apriv->defindex, 0, 0);
+ apriv->config.authType = AUTH_ENCRYPT;
+ }
+ break;
+ default: /* We'll escalate to SHAREDKEY */
+ apriv->config.authType = AUTH_SHAREDKEY;
+ }
+ set_bit (FLAG_COMMIT, &apriv->flags);
+ writeConfigRid(apriv, 0);
+ enable_MAC(apriv, 0);
+ up(&apriv->sem);
+
+/* Schedule check to see if the change worked */
+ clear_bit(JOB_AUTOWEP, &apriv->jobs);
+ apriv->expires = RUN_AT(HZ*3);
+}
+
+#ifdef CONFIG_PCI
+static int airo_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pent)
+{
+ struct net_device *dev;
+
+ if (pci_enable_device(pdev))
+ return -ENODEV;
+ pci_set_master(pdev);
+
+ if (pdev->device == 0x5000 || pdev->device == 0xa504)
+ dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
+ else
+ dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
+ if (!dev) {
+ pci_disable_device(pdev);
+ return -ENODEV;
+ }
+
+ pci_set_drvdata(pdev, dev);
+ return 0;
+}
+
+static void airo_pci_remove(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ airo_print_info(dev->name, "Unregistering...");
+ stop_airo_card(dev, 1);
+ pci_disable_device(pdev);
+}
+
+static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct airo_info *ai = dev->ml_priv;
+ Cmd cmd;
+ Resp rsp;
+
+ if (!ai->SSID)
+ ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
+ if (!ai->SSID)
+ return -ENOMEM;
+ readSsidRid(ai, ai->SSID);
+ memset(&cmd, 0, sizeof(cmd));
+ /* the lock will be released at the end of the resume callback */
+ if (down_interruptible(&ai->sem))
+ return -EAGAIN;
+ disable_MAC(ai, 0);
+ netif_device_detach(dev);
+ ai->power = state;
+ cmd.cmd = HOSTSLEEP;
+ issuecommand(ai, &cmd, &rsp);
+
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int airo_pci_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct airo_info *ai = dev->ml_priv;
+ pci_power_t prev_state = pdev->current_state;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ pci_enable_wake(pdev, PCI_D0, 0);
+
+ if (prev_state != PCI_D1) {
+ reset_card(dev, 0);
+ mpi_init_descriptors(ai);
+ setup_card(ai, dev->dev_addr, 0);
+ clear_bit(FLAG_RADIO_OFF, &ai->flags);
+ clear_bit(FLAG_PENDING_XMIT, &ai->flags);
+ } else {
+ OUT4500(ai, EVACK, EV_AWAKEN);
+ OUT4500(ai, EVACK, EV_AWAKEN);
+ msleep(100);
+ }
+
+ set_bit(FLAG_COMMIT, &ai->flags);
+ disable_MAC(ai, 0);
+ msleep(200);
+ if (ai->SSID) {
+ writeSsidRid(ai, ai->SSID, 0);
+ kfree(ai->SSID);
+ ai->SSID = NULL;
+ }
+ writeAPListRid(ai, &ai->APList, 0);
+ writeConfigRid(ai, 0);
+ enable_MAC(ai, 0);
+ ai->power = PMSG_ON;
+ netif_device_attach(dev);
+ netif_wake_queue(dev);
+ enable_interrupts(ai);
+ up(&ai->sem);
+ return 0;
+}
+#endif
+
+static int __init airo_init_module( void )
+{
+ int i;
+
+ proc_kuid = make_kuid(&init_user_ns, proc_uid);
+ proc_kgid = make_kgid(&init_user_ns, proc_gid);
+ if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid))
+ return -EINVAL;
+
+ airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL);
+
+ if (airo_entry)
+ proc_set_user(airo_entry, proc_kuid, proc_kgid);
+
+ for (i = 0; i < 4 && io[i] && irq[i]; i++) {
+ airo_print_info("", "Trying to configure ISA adapter at irq=%d "
+ "io=0x%x", irq[i], io[i] );
+ if (init_airo_card( irq[i], io[i], 0, NULL ))
+ /* do nothing */ ;
+ }
+
+#ifdef CONFIG_PCI
+ airo_print_info("", "Probing for PCI adapters");
+ i = pci_register_driver(&airo_driver);
+ airo_print_info("", "Finished probing for PCI adapters");
+
+ if (i) {
+ remove_proc_entry("driver/aironet", NULL);
+ return i;
+ }
+#endif
+
+ /* Always exit with success, as we are a library module
+ * as well as a driver module
+ */
+ return 0;
+}
+
+static void __exit airo_cleanup_module( void )
+{
+ struct airo_info *ai;
+ while(!list_empty(&airo_devices)) {
+ ai = list_entry(airo_devices.next, struct airo_info, dev_list);
+ airo_print_info(ai->dev->name, "Unregistering...");
+ stop_airo_card(ai->dev, 1);
+ }
+#ifdef CONFIG_PCI
+ pci_unregister_driver(&airo_driver);
+#endif
+ remove_proc_entry("driver/aironet", NULL);
+}
+
+/*
+ * Initial Wireless Extension code for Aironet driver by :
+ * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
+ * Conversion to new driver API by :
+ * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
+ * Javier also did a good amount of work here, adding some new extensions
+ * and fixing my code. Let's just say that without him this code just
+ * would not work at all... - Jean II
+ */
+
+static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
+{
+ if (!rssi_rid)
+ return 0;
+
+ return (0x100 - rssi_rid[rssi].rssidBm);
+}
+
+static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
+{
+ int i;
+
+ if (!rssi_rid)
+ return 0;
+
+ for (i = 0; i < 256; i++)
+ if (rssi_rid[i].rssidBm == dbm)
+ return rssi_rid[i].rssipct;
+
+ return 0;
+}
+
+
+static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
+{
+ int quality = 0;
+ u16 sq;
+
+ if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
+ return 0;
+
+ if (!(cap_rid->hardCap & cpu_to_le16(8)))
+ return 0;
+
+ sq = le16_to_cpu(status_rid->signalQuality);
+ if (memcmp(cap_rid->prodName, "350", 3))
+ if (sq > 0x20)
+ quality = 0;
+ else
+ quality = 0x20 - sq;
+ else
+ if (sq > 0xb0)
+ quality = 0;
+ else if (sq < 0x10)
+ quality = 0xa0;
+ else
+ quality = 0xb0 - sq;
+ return quality;
+}
+
+#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
+#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get protocol name
+ */
+static int airo_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ char *cwrq,
+ char *extra)
+{
+ strcpy(cwrq, "IEEE 802.11-DS");
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set frequency
+ */
+static int airo_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ int rc = -EINPROGRESS; /* Call commit handler */
+
+ /* If setting by frequency, convert to a channel */
+ if(fwrq->e == 1) {
+ int f = fwrq->m / 100000;
+
+ /* Hack to fall through... */
+ fwrq->e = 0;
+ fwrq->m = ieee80211_frequency_to_channel(f);
+ }
+ /* Setting by channel number */
+ if((fwrq->m > 1000) || (fwrq->e > 0))
+ rc = -EOPNOTSUPP;
+ else {
+ int channel = fwrq->m;
+ /* We should do a better check than that,
+ * based on the card capability !!! */
+ if((channel < 1) || (channel > 14)) {
+ airo_print_dbg(dev->name, "New channel value of %d is invalid!",
+ fwrq->m);
+ rc = -EINVAL;
+ } else {
+ readConfigRid(local, 1);
+ /* Yes ! We can set it !!! */
+ local->config.channelSet = cpu_to_le16(channel);
+ set_bit (FLAG_COMMIT, &local->flags);
+ }
+ }
+ return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get frequency
+ */
+static int airo_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ StatusRid status_rid; /* Card status info */
+ int ch;
+
+ readConfigRid(local, 1);
+ if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
+ status_rid.channel = local->config.channelSet;
+ else
+ readStatusRid(local, &status_rid, 1);
+
+ ch = le16_to_cpu(status_rid.channel);
+ if((ch > 0) && (ch < 15)) {
+ fwrq->m = 100000 *
+ ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ);
+ fwrq->e = 1;
+ } else {
+ fwrq->m = ch;
+ fwrq->e = 0;
+ }
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set ESSID
+ */
+static int airo_set_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ SsidRid SSID_rid; /* SSIDs */
+
+ /* Reload the list of current SSID */
+ readSsidRid(local, &SSID_rid);
+
+ /* Check if we asked for `any' */
+ if (dwrq->flags == 0) {
+ /* Just send an empty SSID list */
+ memset(&SSID_rid, 0, sizeof(SSID_rid));
+ } else {
+ unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+ /* Check the size of the string */
+ if (dwrq->length > IW_ESSID_MAX_SIZE)
+ return -E2BIG ;
+
+ /* Check if index is valid */
+ if (index >= ARRAY_SIZE(SSID_rid.ssids))
+ return -EINVAL;
+
+ /* Set the SSID */
+ memset(SSID_rid.ssids[index].ssid, 0,
+ sizeof(SSID_rid.ssids[index].ssid));
+ memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
+ SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
+ }
+ SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
+ /* Write it to the card */
+ disable_MAC(local, 1);
+ writeSsidRid(local, &SSID_rid, 1);
+ enable_MAC(local, 1);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get ESSID
+ */
+static int airo_get_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ StatusRid status_rid; /* Card status info */
+
+ readStatusRid(local, &status_rid, 1);
+
+ /* Note : if dwrq->flags != 0, we should
+ * get the relevant SSID from the SSID list... */
+
+ /* Get the current SSID */
+ memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
+ /* If none, we may want to get the one that was set */
+
+ /* Push it out ! */
+ dwrq->length = le16_to_cpu(status_rid.SSIDlen);
+ dwrq->flags = 1; /* active */
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set AP address
+ */
+static int airo_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ Cmd cmd;
+ Resp rsp;
+ APListRid *APList_rid = &local->APList;
+
+ if (awrq->sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+ else if (is_broadcast_ether_addr(awrq->sa_data) ||
+ is_zero_ether_addr(awrq->sa_data)) {
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd=CMD_LOSE_SYNC;
+ if (down_interruptible(&local->sem))
+ return -ERESTARTSYS;
+ issuecommand(local, &cmd, &rsp);
+ up(&local->sem);
+ } else {
+ memset(APList_rid, 0, sizeof(*APList_rid));
+ APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
+ memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN);
+ disable_MAC(local, 1);
+ writeAPListRid(local, APList_rid, 1);
+ enable_MAC(local, 1);
+ }
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get AP address
+ */
+static int airo_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ StatusRid status_rid; /* Card status info */
+
+ readStatusRid(local, &status_rid, 1);
+
+ /* Tentative. This seems to work, wow, I'm lucky !!! */
+ memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
+ awrq->sa_family = ARPHRD_ETHER;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Nickname
+ */
+static int airo_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ /* Check the size of the string */
+ if(dwrq->length > 16) {
+ return -E2BIG;
+ }
+ readConfigRid(local, 1);
+ memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
+ memcpy(local->config.nodeName, extra, dwrq->length);
+ set_bit (FLAG_COMMIT, &local->flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Nickname
+ */
+static int airo_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ strncpy(extra, local->config.nodeName, 16);
+ extra[16] = '\0';
+ dwrq->length = strlen(extra);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Bit-Rate
+ */
+static int airo_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ CapabilityRid cap_rid; /* Card capability info */
+ u8 brate = 0;
+ int i;
+
+ /* First : get a valid bit rate value */
+ readCapabilityRid(local, &cap_rid, 1);
+
+ /* Which type of value ? */
+ if((vwrq->value < 8) && (vwrq->value >= 0)) {
+ /* Setting by rate index */
+ /* Find value in the magic rate table */
+ brate = cap_rid.supportedRates[vwrq->value];
+ } else {
+ /* Setting by frequency value */
+ u8 normvalue = (u8) (vwrq->value/500000);
+
+ /* Check if rate is valid */
+ for(i = 0 ; i < 8 ; i++) {
+ if(normvalue == cap_rid.supportedRates[i]) {
+ brate = normvalue;
+ break;
+ }
+ }
+ }
+ /* -1 designed the max rate (mostly auto mode) */
+ if(vwrq->value == -1) {
+ /* Get the highest available rate */
+ for(i = 0 ; i < 8 ; i++) {
+ if(cap_rid.supportedRates[i] == 0)
+ break;
+ }
+ if(i != 0)
+ brate = cap_rid.supportedRates[i - 1];
+ }
+ /* Check that it is valid */
+ if(brate == 0) {
+ return -EINVAL;
+ }
+
+ readConfigRid(local, 1);
+ /* Now, check if we want a fixed or auto value */
+ if(vwrq->fixed == 0) {
+ /* Fill all the rates up to this max rate */
+ memset(local->config.rates, 0, 8);
+ for(i = 0 ; i < 8 ; i++) {
+ local->config.rates[i] = cap_rid.supportedRates[i];
+ if(local->config.rates[i] == brate)
+ break;
+ }
+ } else {
+ /* Fixed mode */
+ /* One rate, fixed */
+ memset(local->config.rates, 0, 8);
+ local->config.rates[0] = brate;
+ }
+ set_bit (FLAG_COMMIT, &local->flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Bit-Rate
+ */
+static int airo_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ StatusRid status_rid; /* Card status info */
+
+ readStatusRid(local, &status_rid, 1);
+
+ vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
+ /* If more than one rate, set auto */
+ readConfigRid(local, 1);
+ vwrq->fixed = (local->config.rates[1] == 0);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set RTS threshold
+ */
+static int airo_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ int rthr = vwrq->value;
+
+ if(vwrq->disabled)
+ rthr = AIRO_DEF_MTU;
+ if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
+ return -EINVAL;
+ }
+ readConfigRid(local, 1);
+ local->config.rtsThres = cpu_to_le16(rthr);
+ set_bit (FLAG_COMMIT, &local->flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get RTS threshold
+ */
+static int airo_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ vwrq->value = le16_to_cpu(local->config.rtsThres);
+ vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
+ vwrq->fixed = 1;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Fragmentation threshold
+ */
+static int airo_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ int fthr = vwrq->value;
+
+ if(vwrq->disabled)
+ fthr = AIRO_DEF_MTU;
+ if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
+ return -EINVAL;
+ }
+ fthr &= ~0x1; /* Get an even value - is it really needed ??? */
+ readConfigRid(local, 1);
+ local->config.fragThresh = cpu_to_le16(fthr);
+ set_bit (FLAG_COMMIT, &local->flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Fragmentation threshold
+ */
+static int airo_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ vwrq->value = le16_to_cpu(local->config.fragThresh);
+ vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
+ vwrq->fixed = 1;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Mode of Operation
+ */
+static int airo_set_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ int reset = 0;
+
+ readConfigRid(local, 1);
+ if (sniffing_mode(local))
+ reset = 1;
+
+ switch(*uwrq) {
+ case IW_MODE_ADHOC:
+ local->config.opmode &= ~MODE_CFG_MASK;
+ local->config.opmode |= MODE_STA_IBSS;
+ local->config.rmode &= ~RXMODE_FULL_MASK;
+ local->config.scanMode = SCANMODE_ACTIVE;
+ clear_bit (FLAG_802_11, &local->flags);
+ break;
+ case IW_MODE_INFRA:
+ local->config.opmode &= ~MODE_CFG_MASK;
+ local->config.opmode |= MODE_STA_ESS;
+ local->config.rmode &= ~RXMODE_FULL_MASK;
+ local->config.scanMode = SCANMODE_ACTIVE;
+ clear_bit (FLAG_802_11, &local->flags);
+ break;
+ case IW_MODE_MASTER:
+ local->config.opmode &= ~MODE_CFG_MASK;
+ local->config.opmode |= MODE_AP;
+ local->config.rmode &= ~RXMODE_FULL_MASK;
+ local->config.scanMode = SCANMODE_ACTIVE;
+ clear_bit (FLAG_802_11, &local->flags);
+ break;
+ case IW_MODE_REPEAT:
+ local->config.opmode &= ~MODE_CFG_MASK;
+ local->config.opmode |= MODE_AP_RPTR;
+ local->config.rmode &= ~RXMODE_FULL_MASK;
+ local->config.scanMode = SCANMODE_ACTIVE;
+ clear_bit (FLAG_802_11, &local->flags);
+ break;
+ case IW_MODE_MONITOR:
+ local->config.opmode &= ~MODE_CFG_MASK;
+ local->config.opmode |= MODE_STA_ESS;
+ local->config.rmode &= ~RXMODE_FULL_MASK;
+ local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
+ local->config.scanMode = SCANMODE_PASSIVE;
+ set_bit (FLAG_802_11, &local->flags);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (reset)
+ set_bit (FLAG_RESET, &local->flags);
+ set_bit (FLAG_COMMIT, &local->flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Mode of Operation
+ */
+static int airo_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ /* If not managed, assume it's ad-hoc */
+ switch (local->config.opmode & MODE_CFG_MASK) {
+ case MODE_STA_ESS:
+ *uwrq = IW_MODE_INFRA;
+ break;
+ case MODE_AP:
+ *uwrq = IW_MODE_MASTER;
+ break;
+ case MODE_AP_RPTR:
+ *uwrq = IW_MODE_REPEAT;
+ break;
+ default:
+ *uwrq = IW_MODE_ADHOC;
+ }
+
+ return 0;
+}
+
+static inline int valid_index(struct airo_info *ai, int index)
+{
+ return (index >= 0) && (index <= ai->max_wep_idx);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Encryption Key
+ */
+static int airo_set_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1);
+ __le16 currentAuthType = local->config.authType;
+ int rc = 0;
+
+ if (!local->wep_capable)
+ return -EOPNOTSUPP;
+
+ readConfigRid(local, 1);
+
+ /* Basic checking: do we have a key to set ?
+ * Note : with the new API, it's impossible to get a NULL pointer.
+ * Therefore, we need to check a key size == 0 instead.
+ * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
+ * when no key is present (only change flags), but older versions
+ * don't do it. - Jean II */
+ if (dwrq->length > 0) {
+ wep_key_t key;
+ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ int current_index;
+
+ /* Check the size of the key */
+ if (dwrq->length > MAX_KEY_SIZE) {
+ return -EINVAL;
+ }
+
+ current_index = get_wep_tx_idx(local);
+ if (current_index < 0)
+ current_index = 0;
+
+ /* Check the index (none -> use current) */
+ if (!valid_index(local, index))
+ index = current_index;
+
+ /* Set the length */
+ if (dwrq->length > MIN_KEY_SIZE)
+ key.len = MAX_KEY_SIZE;
+ else
+ key.len = MIN_KEY_SIZE;
+ /* Check if the key is not marked as invalid */
+ if(!(dwrq->flags & IW_ENCODE_NOKEY)) {
+ /* Cleanup */
+ memset(key.key, 0, MAX_KEY_SIZE);
+ /* Copy the key in the driver */
+ memcpy(key.key, extra, dwrq->length);
+ /* Send the key to the card */
+ rc = set_wep_key(local, index, key.key, key.len, perm, 1);
+ if (rc < 0) {
+ airo_print_err(local->dev->name, "failed to set"
+ " WEP key at index %d: %d.",
+ index, rc);
+ return rc;
+ }
+ }
+ /* WE specify that if a valid key is set, encryption
+ * should be enabled (user may turn it off later)
+ * This is also how "iwconfig ethX key on" works */
+ if((index == current_index) && (key.len > 0) &&
+ (local->config.authType == AUTH_OPEN))
+ set_auth_type(local, AUTH_ENCRYPT);
+ } else {
+ /* Do we want to just set the transmit key index ? */
+ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ if (valid_index(local, index)) {
+ rc = set_wep_tx_idx(local, index, perm, 1);
+ if (rc < 0) {
+ airo_print_err(local->dev->name, "failed to set"
+ " WEP transmit index to %d: %d.",
+ index, rc);
+ return rc;
+ }
+ } else {
+ /* Don't complain if only change the mode */
+ if (!(dwrq->flags & IW_ENCODE_MODE))
+ return -EINVAL;
+ }
+ }
+ /* Read the flags */
+ if (dwrq->flags & IW_ENCODE_DISABLED)
+ set_auth_type(local, AUTH_OPEN); /* disable encryption */
+ if(dwrq->flags & IW_ENCODE_RESTRICTED)
+ set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
+ if (dwrq->flags & IW_ENCODE_OPEN)
+ set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */
+ /* Commit the changes to flags if needed */
+ if (local->config.authType != currentAuthType)
+ set_bit (FLAG_COMMIT, &local->flags);
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Encryption Key
+ */
+static int airo_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ int wep_key_len;
+ u8 buf[16];
+
+ if (!local->wep_capable)
+ return -EOPNOTSUPP;
+
+ readConfigRid(local, 1);
+
+ /* Check encryption mode */
+ switch(local->config.authType) {
+ case AUTH_ENCRYPT:
+ dwrq->flags = IW_ENCODE_OPEN;
+ break;
+ case AUTH_SHAREDKEY:
+ dwrq->flags = IW_ENCODE_RESTRICTED;
+ break;
+ default:
+ case AUTH_OPEN:
+ dwrq->flags = IW_ENCODE_DISABLED;
+ break;
+ }
+ /* We can't return the key, so set the proper flag and return zero */
+ dwrq->flags |= IW_ENCODE_NOKEY;
+ memset(extra, 0, 16);
+
+ /* Which key do we want ? -1 -> tx index */
+ if (!valid_index(local, index)) {
+ index = get_wep_tx_idx(local);
+ if (index < 0)
+ index = 0;
+ }
+ dwrq->flags |= index + 1;
+
+ /* Copy the key to the user buffer */
+ wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf));
+ if (wep_key_len < 0) {
+ dwrq->length = 0;
+ } else {
+ dwrq->length = wep_key_len;
+ memcpy(extra, buf, dwrq->length);
+ }
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set extended Encryption parameters
+ */
+static int airo_set_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 );
+ __le16 currentAuthType = local->config.authType;
+ int idx, key_len, alg = ext->alg, set_key = 1, rc;
+ wep_key_t key;
+
+ if (!local->wep_capable)
+ return -EOPNOTSUPP;
+
+ readConfigRid(local, 1);
+
+ /* Determine and validate the key index */
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (!valid_index(local, idx - 1))
+ return -EINVAL;
+ idx--;
+ } else {
+ idx = get_wep_tx_idx(local);
+ if (idx < 0)
+ idx = 0;
+ }
+
+ if (encoding->flags & IW_ENCODE_DISABLED)
+ alg = IW_ENCODE_ALG_NONE;
+
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ /* Only set transmit key index here, actual
+ * key is set below if needed.
+ */
+ rc = set_wep_tx_idx(local, idx, perm, 1);
+ if (rc < 0) {
+ airo_print_err(local->dev->name, "failed to set "
+ "WEP transmit index to %d: %d.",
+ idx, rc);
+ return rc;
+ }
+ set_key = ext->key_len > 0 ? 1 : 0;
+ }
+
+ if (set_key) {
+ /* Set the requested key first */
+ memset(key.key, 0, MAX_KEY_SIZE);
+ switch (alg) {
+ case IW_ENCODE_ALG_NONE:
+ key.len = 0;
+ break;
+ case IW_ENCODE_ALG_WEP:
+ if (ext->key_len > MIN_KEY_SIZE) {
+ key.len = MAX_KEY_SIZE;
+ } else if (ext->key_len > 0) {
+ key.len = MIN_KEY_SIZE;
+ } else {
+ return -EINVAL;
+ }
+ key_len = min (ext->key_len, key.len);
+ memcpy(key.key, ext->key, key_len);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (key.len == 0) {
+ rc = set_wep_tx_idx(local, idx, perm, 1);
+ if (rc < 0) {
+ airo_print_err(local->dev->name,
+ "failed to set WEP transmit index to %d: %d.",
+ idx, rc);
+ return rc;
+ }
+ } else {
+ rc = set_wep_key(local, idx, key.key, key.len, perm, 1);
+ if (rc < 0) {
+ airo_print_err(local->dev->name,
+ "failed to set WEP key at index %d: %d.",
+ idx, rc);
+ return rc;
+ }
+ }
+ }
+
+ /* Read the flags */
+ if (encoding->flags & IW_ENCODE_DISABLED)
+ set_auth_type(local, AUTH_OPEN); /* disable encryption */
+ if(encoding->flags & IW_ENCODE_RESTRICTED)
+ set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
+ if (encoding->flags & IW_ENCODE_OPEN)
+ set_auth_type(local, AUTH_ENCRYPT);
+ /* Commit the changes to flags if needed */
+ if (local->config.authType != currentAuthType)
+ set_bit (FLAG_COMMIT, &local->flags);
+
+ return -EINPROGRESS;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get extended Encryption parameters
+ */
+static int airo_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, max_key_len, wep_key_len;
+ u8 buf[16];
+
+ if (!local->wep_capable)
+ return -EOPNOTSUPP;
+
+ readConfigRid(local, 1);
+
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ return -EINVAL;
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (!valid_index(local, idx - 1))
+ return -EINVAL;
+ idx--;
+ } else {
+ idx = get_wep_tx_idx(local);
+ if (idx < 0)
+ idx = 0;
+ }
+
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ /* Check encryption mode */
+ switch(local->config.authType) {
+ case AUTH_ENCRYPT:
+ encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
+ break;
+ case AUTH_SHAREDKEY:
+ encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
+ break;
+ default:
+ case AUTH_OPEN:
+ encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
+ break;
+ }
+ /* We can't return the key, so set the proper flag and return zero */
+ encoding->flags |= IW_ENCODE_NOKEY;
+ memset(extra, 0, 16);
+
+ /* Copy the key to the user buffer */
+ wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
+ if (wep_key_len < 0) {
+ ext->key_len = 0;
+ } else {
+ ext->key_len = wep_key_len;
+ memcpy(extra, buf, ext->key_len);
+ }
+
+ return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set extended authentication parameters
+ */
+static int airo_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ struct iw_param *param = &wrqu->param;
+ __le16 currentAuthType = local->config.authType;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ /*
+ * airo does not use these parameters
+ */
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ if (param->value) {
+ /* Only change auth type if unencrypted */
+ if (currentAuthType == AUTH_OPEN)
+ set_auth_type(local, AUTH_ENCRYPT);
+ } else {
+ set_auth_type(local, AUTH_OPEN);
+ }
+
+ /* Commit the changes to flags if needed */
+ if (local->config.authType != currentAuthType)
+ set_bit (FLAG_COMMIT, &local->flags);
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG: {
+ if (param->value & IW_AUTH_ALG_SHARED_KEY) {
+ set_auth_type(local, AUTH_SHAREDKEY);
+ } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ /* We don't know here if WEP open system or
+ * unencrypted mode was requested - so use the
+ * last mode (of these two) used last time
+ */
+ set_auth_type(local, local->last_auth);
+ } else
+ return -EINVAL;
+
+ /* Commit the changes to flags if needed */
+ if (local->config.authType != currentAuthType)
+ set_bit (FLAG_COMMIT, &local->flags);
+ break;
+ }
+
+ case IW_AUTH_WPA_ENABLED:
+ /* Silently accept disable of WPA */
+ if (param->value > 0)
+ return -EOPNOTSUPP;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return -EINPROGRESS;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get extended authentication parameters
+ */
+static int airo_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ struct iw_param *param = &wrqu->param;
+ __le16 currentAuthType = local->config.authType;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_DROP_UNENCRYPTED:
+ switch (currentAuthType) {
+ case AUTH_SHAREDKEY:
+ case AUTH_ENCRYPT:
+ param->value = 1;
+ break;
+ default:
+ param->value = 0;
+ break;
+ }
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ switch (currentAuthType) {
+ case AUTH_SHAREDKEY:
+ param->value = IW_AUTH_ALG_SHARED_KEY;
+ break;
+ case AUTH_ENCRYPT:
+ default:
+ param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+ break;
+ }
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = 0;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Tx-Power
+ */
+static int airo_set_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ CapabilityRid cap_rid; /* Card capability info */
+ int i;
+ int rc = -EINVAL;
+ __le16 v = cpu_to_le16(vwrq->value);
+
+ readCapabilityRid(local, &cap_rid, 1);
+
+ if (vwrq->disabled) {
+ set_bit (FLAG_RADIO_OFF, &local->flags);
+ set_bit (FLAG_COMMIT, &local->flags);
+ return -EINPROGRESS; /* Call commit handler */
+ }
+ if (vwrq->flags != IW_TXPOW_MWATT) {
+ return -EINVAL;
+ }
+ clear_bit (FLAG_RADIO_OFF, &local->flags);
+ for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
+ if (v == cap_rid.txPowerLevels[i]) {
+ readConfigRid(local, 1);
+ local->config.txPower = v;
+ set_bit (FLAG_COMMIT, &local->flags);
+ rc = -EINPROGRESS; /* Call commit handler */
+ break;
+ }
+ return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Tx-Power
+ */
+static int airo_get_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ vwrq->value = le16_to_cpu(local->config.txPower);
+ vwrq->fixed = 1; /* No power control */
+ vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
+ vwrq->flags = IW_TXPOW_MWATT;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Retry limits
+ */
+static int airo_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ int rc = -EINVAL;
+
+ if(vwrq->disabled) {
+ return -EINVAL;
+ }
+ readConfigRid(local, 1);
+ if(vwrq->flags & IW_RETRY_LIMIT) {
+ __le16 v = cpu_to_le16(vwrq->value);
+ if(vwrq->flags & IW_RETRY_LONG)
+ local->config.longRetryLimit = v;
+ else if (vwrq->flags & IW_RETRY_SHORT)
+ local->config.shortRetryLimit = v;
+ else {
+ /* No modifier : set both */
+ local->config.longRetryLimit = v;
+ local->config.shortRetryLimit = v;
+ }
+ set_bit (FLAG_COMMIT, &local->flags);
+ rc = -EINPROGRESS; /* Call commit handler */
+ }
+ if(vwrq->flags & IW_RETRY_LIFETIME) {
+ local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
+ set_bit (FLAG_COMMIT, &local->flags);
+ rc = -EINPROGRESS; /* Call commit handler */
+ }
+ return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Retry limits
+ */
+static int airo_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ vwrq->disabled = 0; /* Can't be disabled */
+
+ readConfigRid(local, 1);
+ /* Note : by default, display the min retry number */
+ if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+ vwrq->flags = IW_RETRY_LIFETIME;
+ vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
+ } else if((vwrq->flags & IW_RETRY_LONG)) {
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ vwrq->value = le16_to_cpu(local->config.longRetryLimit);
+ } else {
+ vwrq->flags = IW_RETRY_LIMIT;
+ vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
+ if(local->config.shortRetryLimit != local->config.longRetryLimit)
+ vwrq->flags |= IW_RETRY_SHORT;
+ }
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get range info
+ */
+static int airo_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ struct iw_range *range = (struct iw_range *) extra;
+ CapabilityRid cap_rid; /* Card capability info */
+ int i;
+ int k;
+
+ readCapabilityRid(local, &cap_rid, 1);
+
+ dwrq->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(*range));
+ range->min_nwid = 0x0000;
+ range->max_nwid = 0x0000;
+ range->num_channels = 14;
+ /* Should be based on cap_rid.country to give only
+ * what the current card support */
+ k = 0;
+ for(i = 0; i < 14; i++) {
+ range->freq[k].i = i + 1; /* List index */
+ range->freq[k].m = 100000 *
+ ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ);
+ range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */
+ }
+ range->num_frequency = k;
+
+ range->sensitivity = 65535;
+
+ /* Hum... Should put the right values there */
+ if (local->rssi)
+ range->max_qual.qual = 100; /* % */
+ else
+ range->max_qual.qual = airo_get_max_quality(&cap_rid);
+ range->max_qual.level = 0x100 - 120; /* -120 dBm */
+ range->max_qual.noise = 0x100 - 120; /* -120 dBm */
+
+ /* Experimental measurements - boundary 11/5.5 Mb/s */
+ /* Note : with or without the (local->rssi), results
+ * are somewhat different. - Jean II */
+ if (local->rssi) {
+ range->avg_qual.qual = 50; /* % */
+ range->avg_qual.level = 0x100 - 70; /* -70 dBm */
+ } else {
+ range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
+ range->avg_qual.level = 0x100 - 80; /* -80 dBm */
+ }
+ range->avg_qual.noise = 0x100 - 85; /* -85 dBm */
+
+ for(i = 0 ; i < 8 ; i++) {
+ range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
+ if(range->bitrate[i] == 0)
+ break;
+ }
+ range->num_bitrates = i;
+
+ /* Set an indication of the max TCP throughput
+ * in bit/s that we can expect using this interface.
+ * May be use for QoS stuff... Jean II */
+ if(i > 2)
+ range->throughput = 5000 * 1000;
+ else
+ range->throughput = 1500 * 1000;
+
+ range->min_rts = 0;
+ range->max_rts = AIRO_DEF_MTU;
+ range->min_frag = 256;
+ range->max_frag = AIRO_DEF_MTU;
+
+ if(cap_rid.softCap & cpu_to_le16(2)) {
+ // WEP: RC4 40 bits
+ range->encoding_size[0] = 5;
+ // RC4 ~128 bits
+ if (cap_rid.softCap & cpu_to_le16(0x100)) {
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ } else
+ range->num_encoding_sizes = 1;
+ range->max_encoding_tokens =
+ cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
+ } else {
+ range->num_encoding_sizes = 0;
+ range->max_encoding_tokens = 0;
+ }
+ range->min_pmp = 0;
+ range->max_pmp = 5000000; /* 5 secs */
+ range->min_pmt = 0;
+ range->max_pmt = 65535 * 1024; /* ??? */
+ range->pmp_flags = IW_POWER_PERIOD;
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+
+ /* Transmit Power - values are in mW */
+ for(i = 0 ; i < 8 ; i++) {
+ range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
+ if(range->txpower[i] == 0)
+ break;
+ }
+ range->num_txpower = i;
+ range->txpower_capa = IW_TXPOW_MWATT;
+ range->we_version_source = 19;
+ range->we_version_compiled = WIRELESS_EXT;
+ range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->r_time_flags = IW_RETRY_LIFETIME;
+ range->min_retry = 1;
+ range->max_retry = 65535;
+ range->min_r_time = 1024;
+ range->max_r_time = 65535 * 1024;
+
+ /* Event capability (kernel + driver) */
+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+ IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+ IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+ IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+ range->event_capa[1] = IW_EVENT_CAPA_K_1;
+ range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Power Management
+ */
+static int airo_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ if (vwrq->disabled) {
+ if (sniffing_mode(local))
+ return -EINVAL;
+ local->config.powerSaveMode = POWERSAVE_CAM;
+ local->config.rmode &= ~RXMODE_MASK;
+ local->config.rmode |= RXMODE_BC_MC_ADDR;
+ set_bit (FLAG_COMMIT, &local->flags);
+ return -EINPROGRESS; /* Call commit handler */
+ }
+ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+ local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
+ local->config.powerSaveMode = POWERSAVE_PSPCAM;
+ set_bit (FLAG_COMMIT, &local->flags);
+ } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
+ local->config.fastListenInterval =
+ local->config.listenInterval =
+ cpu_to_le16((vwrq->value + 500) / 1024);
+ local->config.powerSaveMode = POWERSAVE_PSPCAM;
+ set_bit (FLAG_COMMIT, &local->flags);
+ }
+ switch (vwrq->flags & IW_POWER_MODE) {
+ case IW_POWER_UNICAST_R:
+ if (sniffing_mode(local))
+ return -EINVAL;
+ local->config.rmode &= ~RXMODE_MASK;
+ local->config.rmode |= RXMODE_ADDR;
+ set_bit (FLAG_COMMIT, &local->flags);
+ break;
+ case IW_POWER_ALL_R:
+ if (sniffing_mode(local))
+ return -EINVAL;
+ local->config.rmode &= ~RXMODE_MASK;
+ local->config.rmode |= RXMODE_BC_MC_ADDR;
+ set_bit (FLAG_COMMIT, &local->flags);
+ case IW_POWER_ON:
+ /* This is broken, fixme ;-) */
+ break;
+ default:
+ return -EINVAL;
+ }
+ // Note : we may want to factor local->need_commit here
+ // Note2 : may also want to factor RXMODE_RFMON test
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Power Management
+ */
+static int airo_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ __le16 mode;
+
+ readConfigRid(local, 1);
+ mode = local->config.powerSaveMode;
+ if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
+ return 0;
+ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+ vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
+ vwrq->flags = IW_POWER_TIMEOUT;
+ } else {
+ vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
+ vwrq->flags = IW_POWER_PERIOD;
+ }
+ if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
+ vwrq->flags |= IW_POWER_UNICAST_R;
+ else
+ vwrq->flags |= IW_POWER_ALL_R;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Sensitivity
+ */
+static int airo_set_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ local->config.rssiThreshold =
+ cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
+ set_bit (FLAG_COMMIT, &local->flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Sensitivity
+ */
+static int airo_get_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ readConfigRid(local, 1);
+ vwrq->value = le16_to_cpu(local->config.rssiThreshold);
+ vwrq->disabled = (vwrq->value == 0);
+ vwrq->fixed = 1;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get AP List
+ * Note : this is deprecated in favor of IWSCAN
+ */
+static int airo_get_aplist(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *local = dev->ml_priv;
+ struct sockaddr *address = (struct sockaddr *) extra;
+ struct iw_quality *qual;
+ BSSListRid BSSList;
+ int i;
+ int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
+
+ qual = kmalloc(IW_MAX_AP * sizeof(*qual), GFP_KERNEL);
+ if (!qual)
+ return -ENOMEM;
+
+ for (i = 0; i < IW_MAX_AP; i++) {
+ u16 dBm;
+ if (readBSSListRid(local, loseSync, &BSSList))
+ break;
+ loseSync = 0;
+ memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
+ address[i].sa_family = ARPHRD_ETHER;
+ dBm = le16_to_cpu(BSSList.dBm);
+ if (local->rssi) {
+ qual[i].level = 0x100 - dBm;
+ qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
+ qual[i].updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_DBM;
+ } else {
+ qual[i].level = (dBm + 321) / 2;
+ qual[i].qual = 0;
+ qual[i].updated = IW_QUAL_QUAL_INVALID
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_DBM;
+ }
+ qual[i].noise = local->wstats.qual.noise;
+ if (BSSList.index == cpu_to_le16(0xffff))
+ break;
+ }
+ if (!i) {
+ StatusRid status_rid; /* Card status info */
+ readStatusRid(local, &status_rid, 1);
+ for (i = 0;
+ i < min(IW_MAX_AP, 4) &&
+ (status_rid.bssid[i][0]
+ & status_rid.bssid[i][1]
+ & status_rid.bssid[i][2]
+ & status_rid.bssid[i][3]
+ & status_rid.bssid[i][4]
+ & status_rid.bssid[i][5])!=0xff &&
+ (status_rid.bssid[i][0]
+ | status_rid.bssid[i][1]
+ | status_rid.bssid[i][2]
+ | status_rid.bssid[i][3]
+ | status_rid.bssid[i][4]
+ | status_rid.bssid[i][5]);
+ i++) {
+ memcpy(address[i].sa_data,
+ status_rid.bssid[i], ETH_ALEN);
+ address[i].sa_family = ARPHRD_ETHER;
+ }
+ } else {
+ dwrq->flags = 1; /* Should be define'd */
+ memcpy(extra + sizeof(struct sockaddr) * i, qual,
+ sizeof(struct iw_quality) * i);
+ }
+ dwrq->length = i;
+
+ kfree(qual);
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : Initiate Scan
+ */
+static int airo_set_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *ai = dev->ml_priv;
+ Cmd cmd;
+ Resp rsp;
+ int wake = 0;
+ APListRid APList_rid_empty;
+
+ /* Note : you may have realised that, as this is a SET operation,
+ * this is privileged and therefore a normal user can't
+ * perform scanning.
+ * This is not an error, while the device perform scanning,
+ * traffic doesn't flow, so it's a perfect DoS...
+ * Jean II */
+ if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+
+ if (down_interruptible(&ai->sem))
+ return -ERESTARTSYS;
+
+ /* If there's already a scan in progress, don't
+ * trigger another one. */
+ if (ai->scan_timeout > 0)
+ goto out;
+
+ /* Clear APList as it affects scan results */
+ memset(&APList_rid_empty, 0, sizeof(APList_rid_empty));
+ APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty));
+ disable_MAC(ai, 2);
+ writeAPListRid(ai, &APList_rid_empty, 0);
+ enable_MAC(ai, 0);
+
+ /* Initiate a scan command */
+ ai->scan_timeout = RUN_AT(3*HZ);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd=CMD_LISTBSS;
+ issuecommand(ai, &cmd, &rsp);
+ wake = 1;
+
+out:
+ up(&ai->sem);
+ if (wake)
+ wake_up_interruptible(&ai->thr_wait);
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate scan data returned from the card to a card independent
+ * format that the Wireless Tools will understand - Jean II
+ */
+static inline char *airo_translate_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ char *current_ev,
+ char *end_buf,
+ BSSListRid *bss)
+{
+ struct airo_info *ai = dev->ml_priv;
+ struct iw_event iwe; /* Temporary buffer */
+ __le16 capabilities;
+ char * current_val; /* For rates */
+ int i;
+ char * buf;
+ u16 dBm;
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ /* Other entries will be displayed in the order we give them */
+
+ /* Add the ESSID */
+ iwe.u.data.length = bss->ssidLen;
+ if(iwe.u.data.length > 32)
+ iwe.u.data.length = 32;
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, bss->ssid);
+
+ /* Add mode */
+ iwe.cmd = SIOCGIWMODE;
+ capabilities = bss->cap;
+ if(capabilities & (CAP_ESS | CAP_IBSS)) {
+ if(capabilities & CAP_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ /* Add frequency */
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
+ iwe.u.freq.m = 100000 *
+ ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ);
+ iwe.u.freq.e = 1;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+
+ dBm = le16_to_cpu(bss->dBm);
+
+ /* Add quality statistics */
+ iwe.cmd = IWEVQUAL;
+ if (ai->rssi) {
+ iwe.u.qual.level = 0x100 - dBm;
+ iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
+ iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_DBM;
+ } else {
+ iwe.u.qual.level = (dBm + 321) / 2;
+ iwe.u.qual.qual = 0;
+ iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_DBM;
+ }
+ iwe.u.qual.noise = ai->wstats.qual.noise;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if(capabilities & CAP_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, bss->ssid);
+
+ /* Rate : stuffing multiple values in a single event require a bit
+ * more of magic - Jean II */
+ current_val = current_ev + iwe_stream_lcp_len(info);
+
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ /* Max 8 values */
+ for(i = 0 ; i < 8 ; i++) {
+ /* NULL terminated */
+ if(bss->rates[i] == 0)
+ break;
+ /* Bit rate given in 500 kb/s units (+ 0x80) */
+ iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
+ /* Add new value to event */
+ current_val = iwe_stream_add_value(info, current_ev,
+ current_val, end_buf,
+ &iwe, IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if ((current_val - current_ev) > iwe_stream_lcp_len(info))
+ current_ev = current_val;
+
+ /* Beacon interval */
+ buf = kmalloc(30, GFP_KERNEL);
+ if (buf) {
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "bcn_int=%d", bss->beaconInterval);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, buf);
+ kfree(buf);
+ }
+
+ /* Put WPA/RSN Information Elements into the event stream */
+ if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
+ unsigned int num_null_ies = 0;
+ u16 length = sizeof (bss->extra.iep);
+ u8 *ie = (void *)&bss->extra.iep;
+
+ while ((length >= 2) && (num_null_ies < 2)) {
+ if (2 + ie[1] > length) {
+ /* Invalid element, don't continue parsing IE */
+ break;
+ }
+
+ switch (ie[0]) {
+ case WLAN_EID_SSID:
+ /* Two zero-length SSID elements
+ * mean we're done parsing elements */
+ if (!ie[1])
+ num_null_ies++;
+ break;
+
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (ie[1] >= 4 &&
+ ie[2] == 0x00 &&
+ ie[3] == 0x50 &&
+ ie[4] == 0xf2 &&
+ ie[5] == 0x01) {
+ iwe.cmd = IWEVGENIE;
+ /* 64 is an arbitrary cut-off */
+ iwe.u.data.length = min(ie[1] + 2,
+ 64);
+ current_ev = iwe_stream_add_point(
+ info, current_ev,
+ end_buf, &iwe, ie);
+ }
+ break;
+
+ case WLAN_EID_RSN:
+ iwe.cmd = IWEVGENIE;
+ /* 64 is an arbitrary cut-off */
+ iwe.u.data.length = min(ie[1] + 2, 64);
+ current_ev = iwe_stream_add_point(
+ info, current_ev, end_buf,
+ &iwe, ie);
+ break;
+
+ default:
+ break;
+ }
+
+ length -= 2 + ie[1];
+ ie += 2 + ie[1];
+ }
+ }
+ return current_ev;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : Read Scan Results
+ */
+static int airo_get_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra)
+{
+ struct airo_info *ai = dev->ml_priv;
+ BSSListElement *net;
+ int err = 0;
+ char *current_ev = extra;
+
+ /* If a scan is in-progress, return -EAGAIN */
+ if (ai->scan_timeout > 0)
+ return -EAGAIN;
+
+ if (down_interruptible(&ai->sem))
+ return -EAGAIN;
+
+ list_for_each_entry (net, &ai->network_list, list) {
+ /* Translate to WE format this entry */
+ current_ev = airo_translate_scan(dev, info, current_ev,
+ extra + dwrq->length,
+ &net->bss);
+
+ /* Check if there is space for one more entry */
+ if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
+ }
+
+ /* Length of data */
+ dwrq->length = (current_ev - extra);
+ dwrq->flags = 0; /* todo */
+
+out:
+ up(&ai->sem);
+ return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Commit handler : called after a bunch of SET operations
+ */
+static int airo_config_commit(struct net_device *dev,
+ struct iw_request_info *info, /* NULL */
+ void *zwrq, /* NULL */
+ char *extra) /* NULL */
+{
+ struct airo_info *local = dev->ml_priv;
+
+ if (!test_bit (FLAG_COMMIT, &local->flags))
+ return 0;
+
+ /* Some of the "SET" function may have modified some of the
+ * parameters. It's now time to commit them in the card */
+ disable_MAC(local, 1);
+ if (test_bit (FLAG_RESET, &local->flags)) {
+ SsidRid SSID_rid;
+
+ readSsidRid(local, &SSID_rid);
+ if (test_bit(FLAG_MPI,&local->flags))
+ setup_card(local, dev->dev_addr, 1 );
+ else
+ reset_airo_card(dev);
+ disable_MAC(local, 1);
+ writeSsidRid(local, &SSID_rid, 1);
+ writeAPListRid(local, &local->APList, 1);
+ }
+ if (down_interruptible(&local->sem))
+ return -ERESTARTSYS;
+ writeConfigRid(local, 0);
+ enable_MAC(local, 0);
+ if (test_bit (FLAG_RESET, &local->flags))
+ airo_set_promisc(local);
+ else
+ up(&local->sem);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Structures to export the Wireless Handlers
+ */
+
+static const struct iw_priv_args airo_private_args[] = {
+/*{ cmd, set_args, get_args, name } */
+ { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
+ IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
+ { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
+};
+
+static const iw_handler airo_handler[] =
+{
+ (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */
+ (iw_handler) airo_get_name, /* SIOCGIWNAME */
+ (iw_handler) NULL, /* SIOCSIWNWID */
+ (iw_handler) NULL, /* SIOCGIWNWID */
+ (iw_handler) airo_set_freq, /* SIOCSIWFREQ */
+ (iw_handler) airo_get_freq, /* SIOCGIWFREQ */
+ (iw_handler) airo_set_mode, /* SIOCSIWMODE */
+ (iw_handler) airo_get_mode, /* SIOCGIWMODE */
+ (iw_handler) airo_set_sens, /* SIOCSIWSENS */
+ (iw_handler) airo_get_sens, /* SIOCGIWSENS */
+ (iw_handler) NULL, /* SIOCSIWRANGE */
+ (iw_handler) airo_get_range, /* SIOCGIWRANGE */
+ (iw_handler) NULL, /* SIOCSIWPRIV */
+ (iw_handler) NULL, /* SIOCGIWPRIV */
+ (iw_handler) NULL, /* SIOCSIWSTATS */
+ (iw_handler) NULL, /* SIOCGIWSTATS */
+ iw_handler_set_spy, /* SIOCSIWSPY */
+ iw_handler_get_spy, /* SIOCGIWSPY */
+ iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
+ iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
+ (iw_handler) airo_set_wap, /* SIOCSIWAP */
+ (iw_handler) airo_get_wap, /* SIOCGIWAP */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */
+ (iw_handler) airo_set_scan, /* SIOCSIWSCAN */
+ (iw_handler) airo_get_scan, /* SIOCGIWSCAN */
+ (iw_handler) airo_set_essid, /* SIOCSIWESSID */
+ (iw_handler) airo_get_essid, /* SIOCGIWESSID */
+ (iw_handler) airo_set_nick, /* SIOCSIWNICKN */
+ (iw_handler) airo_get_nick, /* SIOCGIWNICKN */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) airo_set_rate, /* SIOCSIWRATE */
+ (iw_handler) airo_get_rate, /* SIOCGIWRATE */
+ (iw_handler) airo_set_rts, /* SIOCSIWRTS */
+ (iw_handler) airo_get_rts, /* SIOCGIWRTS */
+ (iw_handler) airo_set_frag, /* SIOCSIWFRAG */
+ (iw_handler) airo_get_frag, /* SIOCGIWFRAG */
+ (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */
+ (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */
+ (iw_handler) airo_set_retry, /* SIOCSIWRETRY */
+ (iw_handler) airo_get_retry, /* SIOCGIWRETRY */
+ (iw_handler) airo_set_encode, /* SIOCSIWENCODE */
+ (iw_handler) airo_get_encode, /* SIOCGIWENCODE */
+ (iw_handler) airo_set_power, /* SIOCSIWPOWER */
+ (iw_handler) airo_get_power, /* SIOCGIWPOWER */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* SIOCSIWGENIE */
+ (iw_handler) NULL, /* SIOCGIWGENIE */
+ (iw_handler) airo_set_auth, /* SIOCSIWAUTH */
+ (iw_handler) airo_get_auth, /* SIOCGIWAUTH */
+ (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */
+ (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */
+ (iw_handler) NULL, /* SIOCSIWPMKSA */
+};
+
+/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
+ * We want to force the use of the ioctl code, because those can't be
+ * won't work the iw_handler code (because they simultaneously read
+ * and write data and iw_handler can't do that).
+ * Note that it's perfectly legal to read/write on a single ioctl command,
+ * you just can't use iwpriv and need to force it via the ioctl handler.
+ * Jean II */
+static const iw_handler airo_private_handler[] =
+{
+ NULL, /* SIOCIWFIRSTPRIV */
+};
+
+static const struct iw_handler_def airo_handler_def =
+{
+ .num_standard = ARRAY_SIZE(airo_handler),
+ .num_private = ARRAY_SIZE(airo_private_handler),
+ .num_private_args = ARRAY_SIZE(airo_private_args),
+ .standard = airo_handler,
+ .private = airo_private_handler,
+ .private_args = airo_private_args,
+ .get_wireless_stats = airo_get_wireless_stats,
+};
+
+/*
+ * This defines the configuration part of the Wireless Extensions
+ * Note : irq and spinlock protection will occur in the subroutines
+ *
+ * TODO :
+ * o Check input value more carefully and fill correct values in range
+ * o Test and shakeout the bugs (if any)
+ *
+ * Jean II
+ *
+ * Javier Achirica did a great job of merging code from the unnamed CISCO
+ * developer that added support for flashing the card.
+ */
+static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ int rc = 0;
+ struct airo_info *ai = dev->ml_priv;
+
+ if (ai->power.event)
+ return 0;
+
+ switch (cmd) {
+#ifdef CISCO_EXT
+ case AIROIDIFC:
+#ifdef AIROOLDIDIFC
+ case AIROOLDIDIFC:
+#endif
+ {
+ int val = AIROMAGIC;
+ aironet_ioctl com;
+ if (copy_from_user(&com,rq->ifr_data,sizeof(com)))
+ rc = -EFAULT;
+ else if (copy_to_user(com.data,(char *)&val,sizeof(val)))
+ rc = -EFAULT;
+ }
+ break;
+
+ case AIROIOCTL:
+#ifdef AIROOLDIOCTL
+ case AIROOLDIOCTL:
+#endif
+ /* Get the command struct and hand it off for evaluation by
+ * the proper subfunction
+ */
+ {
+ aironet_ioctl com;
+ if (copy_from_user(&com,rq->ifr_data,sizeof(com))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /* Separate R/W functions bracket legality here
+ */
+ if ( com.command == AIRORSWVERSION ) {
+ if (copy_to_user(com.data, swversion, sizeof(swversion)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ }
+ else if ( com.command <= AIRORRID)
+ rc = readrids(dev,&com);
+ else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) )
+ rc = writerids(dev,&com);
+ else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART )
+ rc = flashcard(dev,&com);
+ else
+ rc = -EINVAL; /* Bad command in ioctl */
+ }
+ break;
+#endif /* CISCO_EXT */
+
+ // All other calls are currently unsupported
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ return rc;
+}
+
+/*
+ * Get the Wireless stats out of the driver
+ * Note : irq and spinlock protection will occur in the subroutines
+ *
+ * TODO :
+ * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
+ *
+ * Jean
+ */
+static void airo_read_wireless_stats(struct airo_info *local)
+{
+ StatusRid status_rid;
+ StatsRid stats_rid;
+ CapabilityRid cap_rid;
+ __le32 *vals = stats_rid.vals;
+
+ /* Get stats out of the card */
+ clear_bit(JOB_WSTATS, &local->jobs);
+ if (local->power.event) {
+ up(&local->sem);
+ return;
+ }
+ readCapabilityRid(local, &cap_rid, 0);
+ readStatusRid(local, &status_rid, 0);
+ readStatsRid(local, &stats_rid, RID_STATS, 0);
+ up(&local->sem);
+
+ /* The status */
+ local->wstats.status = le16_to_cpu(status_rid.mode);
+
+ /* Signal quality and co */
+ if (local->rssi) {
+ local->wstats.qual.level =
+ airo_rssi_to_dbm(local->rssi,
+ le16_to_cpu(status_rid.sigQuality));
+ /* normalizedSignalStrength appears to be a percentage */
+ local->wstats.qual.qual =
+ le16_to_cpu(status_rid.normalizedSignalStrength);
+ } else {
+ local->wstats.qual.level =
+ (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
+ local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
+ }
+ if (le16_to_cpu(status_rid.len) >= 124) {
+ local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
+ local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+ } else {
+ local->wstats.qual.noise = 0;
+ local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM;
+ }
+
+ /* Packets discarded in the wireless adapter due to wireless
+ * specific problems */
+ local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
+ le32_to_cpu(vals[57]) +
+ le32_to_cpu(vals[58]); /* SSID Mismatch */
+ local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
+ local->wstats.discard.fragment = le32_to_cpu(vals[30]);
+ local->wstats.discard.retries = le32_to_cpu(vals[10]);
+ local->wstats.discard.misc = le32_to_cpu(vals[1]) +
+ le32_to_cpu(vals[32]);
+ local->wstats.miss.beacon = le32_to_cpu(vals[34]);
+}
+
+static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
+{
+ struct airo_info *local = dev->ml_priv;
+
+ if (!test_bit(JOB_WSTATS, &local->jobs)) {
+ /* Get stats out of the card if available */
+ if (down_trylock(&local->sem) != 0) {
+ set_bit(JOB_WSTATS, &local->jobs);
+ wake_up_interruptible(&local->thr_wait);
+ } else
+ airo_read_wireless_stats(local);
+ }
+
+ return &local->wstats;
+}
+
+#ifdef CISCO_EXT
+/*
+ * This just translates from driver IOCTL codes to the command codes to
+ * feed to the radio's host interface. Things can be added/deleted
+ * as needed. This represents the READ side of control I/O to
+ * the card
+ */
+static int readrids(struct net_device *dev, aironet_ioctl *comp) {
+ unsigned short ridcode;
+ unsigned char *iobuf;
+ int len;
+ struct airo_info *ai = dev->ml_priv;
+
+ if (test_bit(FLAG_FLASHING, &ai->flags))
+ return -EIO;
+
+ switch(comp->command)
+ {
+ case AIROGCAP: ridcode = RID_CAPABILITIES; break;
+ case AIROGCFG: ridcode = RID_CONFIG;
+ if (test_bit(FLAG_COMMIT, &ai->flags)) {
+ disable_MAC (ai, 1);
+ writeConfigRid (ai, 1);
+ enable_MAC(ai, 1);
+ }
+ break;
+ case AIROGSLIST: ridcode = RID_SSID; break;
+ case AIROGVLIST: ridcode = RID_APLIST; break;
+ case AIROGDRVNAM: ridcode = RID_DRVNAME; break;
+ case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
+ case AIROGWEPKTMP: ridcode = RID_WEP_TEMP;
+ /* Only super-user can read WEP keys */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ break;
+ case AIROGWEPKNV: ridcode = RID_WEP_PERM;
+ /* Only super-user can read WEP keys */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ break;
+ case AIROGSTAT: ridcode = RID_STATUS; break;
+ case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
+ case AIROGSTATSC32: ridcode = RID_STATS; break;
+ case AIROGMICSTATS:
+ if (copy_to_user(comp->data, &ai->micstats,
+ min((int)comp->len,(int)sizeof(ai->micstats))))
+ return -EFAULT;
+ return 0;
+ case AIRORRID: ridcode = comp->ridnum; break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1);
+ /* get the count of bytes in the rid docs say 1st 2 bytes is it.
+ * then return it to the user
+ * 9/22/2000 Honor user given length
+ */
+ len = comp->len;
+
+ if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
+ kfree (iobuf);
+ return -EFAULT;
+ }
+ kfree (iobuf);
+ return 0;
+}
+
+/*
+ * Danger Will Robinson write the rids here
+ */
+
+static int writerids(struct net_device *dev, aironet_ioctl *comp) {
+ struct airo_info *ai = dev->ml_priv;
+ int ridcode;
+ int enabled;
+ static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
+ unsigned char *iobuf;
+
+ /* Only super-user can write RIDs */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (test_bit(FLAG_FLASHING, &ai->flags))
+ return -EIO;
+
+ ridcode = 0;
+ writer = do_writerid;
+
+ switch(comp->command)
+ {
+ case AIROPSIDS: ridcode = RID_SSID; break;
+ case AIROPCAP: ridcode = RID_CAPABILITIES; break;
+ case AIROPAPLIST: ridcode = RID_APLIST; break;
+ case AIROPCFG: ai->config.len = 0;
+ clear_bit(FLAG_COMMIT, &ai->flags);
+ ridcode = RID_CONFIG; break;
+ case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
+ case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
+ case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break;
+ case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
+ break;
+ case AIROPLEAPUSR+1: ridcode = 0xFF2A; break;
+ case AIROPLEAPUSR+2: ridcode = 0xFF2B; break;
+
+ /* this is not really a rid but a command given to the card
+ * same with MAC off
+ */
+ case AIROPMACON:
+ if (enable_MAC(ai, 1) != 0)
+ return -EIO;
+ return 0;
+
+ /*
+ * Evidently this code in the airo driver does not get a symbol
+ * as disable_MAC. it's probably so short the compiler does not gen one.
+ */
+ case AIROPMACOFF:
+ disable_MAC(ai, 1);
+ return 0;
+
+ /* This command merely clears the counts does not actually store any data
+ * only reads rid. But as it changes the cards state, I put it in the
+ * writerid routines.
+ */
+ case AIROPSTCLR:
+ if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1);
+
+ enabled = ai->micstats.enabled;
+ memset(&ai->micstats,0,sizeof(ai->micstats));
+ ai->micstats.enabled = enabled;
+
+ if (copy_to_user(comp->data, iobuf,
+ min((int)comp->len, (int)RIDSIZE))) {
+ kfree (iobuf);
+ return -EFAULT;
+ }
+ kfree (iobuf);
+ return 0;
+
+ default:
+ return -EOPNOTSUPP; /* Blarg! */
+ }
+ if(comp->len > RIDSIZE)
+ return -EINVAL;
+
+ if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(iobuf,comp->data,comp->len)) {
+ kfree (iobuf);
+ return -EFAULT;
+ }
+
+ if (comp->command == AIROPCFG) {
+ ConfigRid *cfg = (ConfigRid *)iobuf;
+
+ if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
+ cfg->opmode |= MODE_MIC;
+
+ if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
+ set_bit (FLAG_ADHOC, &ai->flags);
+ else
+ clear_bit (FLAG_ADHOC, &ai->flags);
+ }
+
+ if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
+ kfree (iobuf);
+ return -EIO;
+ }
+ kfree (iobuf);
+ return 0;
+}
+
+/*****************************************************************************
+ * Ancillary flash / mod functions much black magic lurkes here *
+ *****************************************************************************
+ */
+
+/*
+ * Flash command switch table
+ */
+
+static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
+ int z;
+
+ /* Only super-user can modify flash */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch(comp->command)
+ {
+ case AIROFLSHRST:
+ return cmdreset((struct airo_info *)dev->ml_priv);
+
+ case AIROFLSHSTFL:
+ if (!AIRO_FLASH(dev) &&
+ (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ return setflashmode((struct airo_info *)dev->ml_priv);
+
+ case AIROFLSHGCHR: /* Get char from aux */
+ if(comp->len != sizeof(int))
+ return -EINVAL;
+ if (copy_from_user(&z,comp->data,comp->len))
+ return -EFAULT;
+ return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
+
+ case AIROFLSHPCHR: /* Send char to card. */
+ if(comp->len != sizeof(int))
+ return -EINVAL;
+ if (copy_from_user(&z,comp->data,comp->len))
+ return -EFAULT;
+ return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
+
+ case AIROFLPUTBUF: /* Send 32k to card */
+ if (!AIRO_FLASH(dev))
+ return -ENOMEM;
+ if(comp->len > FLASHSIZE)
+ return -EINVAL;
+ if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
+ return -EFAULT;
+
+ flashputbuf((struct airo_info *)dev->ml_priv);
+ return 0;
+
+ case AIRORESTART:
+ if (flashrestart((struct airo_info *)dev->ml_priv, dev))
+ return -EIO;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#define FLASH_COMMAND 0x7e7e
+
+/*
+ * STEP 1)
+ * Disable MAC and do soft reset on
+ * card.
+ */
+
+static int cmdreset(struct airo_info *ai) {
+ disable_MAC(ai, 1);
+
+ if(!waitbusy (ai)){
+ airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
+ return -EBUSY;
+ }
+
+ OUT4500(ai,COMMAND,CMD_SOFTRESET);
+
+ ssleep(1); /* WAS 600 12/7/00 */
+
+ if(!waitbusy (ai)){
+ airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/* STEP 2)
+ * Put the card in legendary flash
+ * mode
+ */
+
+static int setflashmode (struct airo_info *ai) {
+ set_bit (FLAG_FLASHING, &ai->flags);
+
+ OUT4500(ai, SWS0, FLASH_COMMAND);
+ OUT4500(ai, SWS1, FLASH_COMMAND);
+ if (probe) {
+ OUT4500(ai, SWS0, FLASH_COMMAND);
+ OUT4500(ai, COMMAND,0x10);
+ } else {
+ OUT4500(ai, SWS2, FLASH_COMMAND);
+ OUT4500(ai, SWS3, FLASH_COMMAND);
+ OUT4500(ai, COMMAND,0);
+ }
+ msleep(500); /* 500ms delay */
+
+ if(!waitbusy(ai)) {
+ clear_bit (FLAG_FLASHING, &ai->flags);
+ airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
+ return -EIO;
+ }
+ return 0;
+}
+
+/* Put character to SWS0 wait for dwelltime
+ * x 50us for echo .
+ */
+
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
+ int echo;
+ int waittime;
+
+ byte |= 0x8000;
+
+ if(dwelltime == 0 )
+ dwelltime = 200;
+
+ waittime=dwelltime;
+
+ /* Wait for busy bit d15 to go false indicating buffer empty */
+ while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
+ udelay (50);
+ waittime -= 50;
+ }
+
+ /* timeout for busy clear wait */
+ if(waittime <= 0 ){
+ airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
+ return -EBUSY;
+ }
+
+ /* Port is clear now write byte and wait for it to echo back */
+ do {
+ OUT4500(ai,SWS0,byte);
+ udelay(50);
+ dwelltime -= 50;
+ echo = IN4500(ai,SWS1);
+ } while (dwelltime >= 0 && echo != byte);
+
+ OUT4500(ai,SWS1,0);
+
+ return (echo == byte) ? 0 : -EIO;
+}
+
+/*
+ * Get a character from the card matching matchbyte
+ * Step 3)
+ */
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
+ int rchar;
+ unsigned char rbyte=0;
+
+ do {
+ rchar = IN4500(ai,SWS1);
+
+ if(dwelltime && !(0x8000 & rchar)){
+ dwelltime -= 10;
+ mdelay(10);
+ continue;
+ }
+ rbyte = 0xff & rchar;
+
+ if( (rbyte == matchbyte) && (0x8000 & rchar) ){
+ OUT4500(ai,SWS1,0);
+ return 0;
+ }
+ if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
+ break;
+ OUT4500(ai,SWS1,0);
+
+ }while(dwelltime > 0);
+ return -EIO;
+}
+
+/*
+ * Transfer 32k of firmware data from user buffer to our buffer and
+ * send to the card
+ */
+
+static int flashputbuf(struct airo_info *ai){
+ int nwords;
+
+ /* Write stuff */
+ if (test_bit(FLAG_MPI,&ai->flags))
+ memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
+ else {
+ OUT4500(ai,AUXPAGE,0x100);
+ OUT4500(ai,AUXOFF,0);
+
+ for(nwords=0;nwords != FLASHSIZE / 2;nwords++){
+ OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff);
+ }
+ }
+ OUT4500(ai,SWS0,0x8000);
+
+ return 0;
+}
+
+/*
+ *
+ */
+static int flashrestart(struct airo_info *ai,struct net_device *dev){
+ int i,status;
+
+ ssleep(1); /* Added 12/7/00 */
+ clear_bit (FLAG_FLASHING, &ai->flags);
+ if (test_bit(FLAG_MPI, &ai->flags)) {
+ status = mpi_init_descriptors(ai);
+ if (status != SUCCESS)
+ return status;
+ }
+ status = setup_card(ai, dev->dev_addr, 1);
+
+ if (!test_bit(FLAG_MPI,&ai->flags))
+ for( i = 0; i < MAX_FIDS; i++ ) {
+ ai->fids[i] = transmit_allocate
+ ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
+ }
+
+ ssleep(1); /* Added 12/7/00 */
+ return status;
+}
+#endif /* CISCO_EXT */
+
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ In addition:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+module_init(airo_init_module);
+module_exit(airo_cleanup_module);
diff --git a/drivers/net/wireless/airo.h b/drivers/net/wireless/cisco/airo.h
index e480adf86be6..e480adf86be6 100644
--- a/drivers/net/wireless/airo.h
+++ b/drivers/net/wireless/cisco/airo.h
diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/cisco/airo_cs.c
index d9ed22b4cc6b..d9ed22b4cc6b 100644
--- a/drivers/net/wireless/airo_cs.c
+++ b/drivers/net/wireless/cisco/airo_cs.c
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
deleted file mode 100644
index 95a7fdb3cc1c..000000000000
--- a/drivers/net/wireless/cw1200/sta.c
+++ /dev/null
@@ -1,2399 +0,0 @@
-/*
- * Mac80211 STA API for ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/etherdevice.h>
-
-#include "cw1200.h"
-#include "sta.h"
-#include "fwio.h"
-#include "bh.h"
-#include "debug.h"
-
-#ifndef ERP_INFO_BYTE_OFFSET
-#define ERP_INFO_BYTE_OFFSET 2
-#endif
-
-static void cw1200_do_join(struct cw1200_common *priv);
-static void cw1200_do_unjoin(struct cw1200_common *priv);
-
-static int cw1200_upload_beacon(struct cw1200_common *priv);
-static int cw1200_upload_pspoll(struct cw1200_common *priv);
-static int cw1200_upload_null(struct cw1200_common *priv);
-static int cw1200_upload_qosnull(struct cw1200_common *priv);
-static int cw1200_start_ap(struct cw1200_common *priv);
-static int cw1200_update_beaconing(struct cw1200_common *priv);
-static int cw1200_enable_beaconing(struct cw1200_common *priv,
- bool enable);
-static void __cw1200_sta_notify(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- enum sta_notify_cmd notify_cmd,
- int link_id);
-static int __cw1200_flush(struct cw1200_common *priv, bool drop);
-
-static inline void __cw1200_free_event_queue(struct list_head *list)
-{
- struct cw1200_wsm_event *event, *tmp;
- list_for_each_entry_safe(event, tmp, list, link) {
- list_del(&event->link);
- kfree(event);
- }
-}
-
-/* ******************************************************************** */
-/* STA API */
-
-int cw1200_start(struct ieee80211_hw *dev)
-{
- struct cw1200_common *priv = dev->priv;
- int ret = 0;
-
- cw1200_pm_stay_awake(&priv->pm_state, HZ);
-
- mutex_lock(&priv->conf_mutex);
-
- /* default EDCA */
- WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
- WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
- WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
- WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
- ret = wsm_set_edca_params(priv, &priv->edca);
- if (ret)
- goto out;
-
- ret = cw1200_set_uapsd_param(priv, &priv->edca);
- if (ret)
- goto out;
-
- priv->setbssparams_done = false;
-
- memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
- priv->mode = NL80211_IFTYPE_MONITOR;
- priv->wep_default_key_id = -1;
-
- priv->cqm_beacon_loss_count = 10;
-
- ret = cw1200_setup_mac(priv);
- if (ret)
- goto out;
-
-out:
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
-void cw1200_stop(struct ieee80211_hw *dev)
-{
- struct cw1200_common *priv = dev->priv;
- LIST_HEAD(list);
- int i;
-
- wsm_lock_tx(priv);
-
- while (down_trylock(&priv->scan.lock)) {
- /* Scan is in progress. Force it to stop. */
- priv->scan.req = NULL;
- schedule();
- }
- up(&priv->scan.lock);
-
- cancel_delayed_work_sync(&priv->scan.probe_work);
- cancel_delayed_work_sync(&priv->scan.timeout);
- cancel_delayed_work_sync(&priv->clear_recent_scan_work);
- cancel_delayed_work_sync(&priv->join_timeout);
- cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
- cancel_work_sync(&priv->unjoin_work);
- cancel_delayed_work_sync(&priv->link_id_gc_work);
- flush_workqueue(priv->workqueue);
- del_timer_sync(&priv->mcast_timeout);
- mutex_lock(&priv->conf_mutex);
- priv->mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->listening = false;
-
- spin_lock(&priv->event_queue_lock);
- list_splice_init(&priv->event_queue, &list);
- spin_unlock(&priv->event_queue_lock);
- __cw1200_free_event_queue(&list);
-
-
- priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
- priv->join_pending = false;
-
- for (i = 0; i < 4; i++)
- cw1200_queue_clear(&priv->tx_queue[i]);
- mutex_unlock(&priv->conf_mutex);
- tx_policy_clean(priv);
-
- /* HACK! */
- if (atomic_xchg(&priv->tx_lock, 1) != 1)
- pr_debug("[STA] TX is force-unlocked due to stop request.\n");
-
- wsm_unlock_tx(priv);
- atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
-}
-
-static int cw1200_bssloss_mitigation = 1;
-module_param(cw1200_bssloss_mitigation, int, 0644);
-MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
-
-
-void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
- int init, int good, int bad)
-{
- int tx = 0;
-
- priv->delayed_link_loss = 0;
- cancel_work_sync(&priv->bss_params_work);
-
- pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
- priv->bss_loss_state,
- init, good, bad,
- atomic_read(&priv->tx_lock),
- priv->delayed_unjoin);
-
- /* If we have a pending unjoin */
- if (priv->delayed_unjoin)
- return;
-
- if (init) {
- queue_delayed_work(priv->workqueue,
- &priv->bss_loss_work,
- HZ);
- priv->bss_loss_state = 0;
-
- /* Skip the confimration procedure in P2P case */
- if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
- tx = 1;
- } else if (good) {
- cancel_delayed_work_sync(&priv->bss_loss_work);
- priv->bss_loss_state = 0;
- queue_work(priv->workqueue, &priv->bss_params_work);
- } else if (bad) {
- /* XXX Should we just keep going until we time out? */
- if (priv->bss_loss_state < 3)
- tx = 1;
- } else {
- cancel_delayed_work_sync(&priv->bss_loss_work);
- priv->bss_loss_state = 0;
- }
-
- /* Bypass mitigation if it's disabled */
- if (!cw1200_bssloss_mitigation)
- tx = 0;
-
- /* Spit out a NULL packet to our AP if necessary */
- if (tx) {
- struct sk_buff *skb;
-
- priv->bss_loss_state++;
-
- skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
- WARN_ON(!skb);
- if (skb)
- cw1200_tx(priv->hw, NULL, skb);
- }
-}
-
-int cw1200_add_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif)
-{
- int ret;
- struct cw1200_common *priv = dev->priv;
- /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
-
- vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
- IEEE80211_VIF_SUPPORTS_UAPSD |
- IEEE80211_VIF_SUPPORTS_CQM_RSSI;
-
- mutex_lock(&priv->conf_mutex);
-
- if (priv->mode != NL80211_IFTYPE_MONITOR) {
- mutex_unlock(&priv->conf_mutex);
- return -EOPNOTSUPP;
- }
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- case NL80211_IFTYPE_AP:
- priv->mode = vif->type;
- break;
- default:
- mutex_unlock(&priv->conf_mutex);
- return -EOPNOTSUPP;
- }
-
- priv->vif = vif;
- memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
- ret = cw1200_setup_mac(priv);
- /* Enable auto-calibration */
- /* Exception in subsequent channel switch; disabled.
- * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
- * &auto_calibration_mode, sizeof(auto_calibration_mode));
- */
-
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
-void cw1200_remove_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif)
-{
- struct cw1200_common *priv = dev->priv;
- struct wsm_reset reset = {
- .reset_statistics = true,
- };
- int i;
-
- mutex_lock(&priv->conf_mutex);
- switch (priv->join_status) {
- case CW1200_JOIN_STATUS_JOINING:
- case CW1200_JOIN_STATUS_PRE_STA:
- case CW1200_JOIN_STATUS_STA:
- case CW1200_JOIN_STATUS_IBSS:
- wsm_lock_tx(priv);
- if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
- wsm_unlock_tx(priv);
- break;
- case CW1200_JOIN_STATUS_AP:
- for (i = 0; priv->link_id_map; ++i) {
- if (priv->link_id_map & BIT(i)) {
- reset.link_id = i;
- wsm_reset(priv, &reset);
- priv->link_id_map &= ~BIT(i);
- }
- }
- memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
- priv->sta_asleep_mask = 0;
- priv->enable_beacon = false;
- priv->tx_multicast = false;
- priv->aid0_bit_set = false;
- priv->buffered_multicasts = false;
- priv->pspoll_mask = 0;
- reset.link_id = 0;
- wsm_reset(priv, &reset);
- break;
- case CW1200_JOIN_STATUS_MONITOR:
- cw1200_update_listening(priv, false);
- break;
- default:
- break;
- }
- priv->vif = NULL;
- priv->mode = NL80211_IFTYPE_MONITOR;
- eth_zero_addr(priv->mac_addr);
- memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
- cw1200_free_keys(priv);
- cw1200_setup_mac(priv);
- priv->listening = false;
- priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
- if (!__cw1200_flush(priv, true))
- wsm_unlock_tx(priv);
-
- mutex_unlock(&priv->conf_mutex);
-}
-
-int cw1200_change_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- enum nl80211_iftype new_type,
- bool p2p)
-{
- int ret = 0;
- pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
- p2p, vif->type, vif->p2p);
-
- if (new_type != vif->type || vif->p2p != p2p) {
- cw1200_remove_interface(dev, vif);
- vif->type = new_type;
- vif->p2p = p2p;
- ret = cw1200_add_interface(dev, vif);
- }
-
- return ret;
-}
-
-int cw1200_config(struct ieee80211_hw *dev, u32 changed)
-{
- int ret = 0;
- struct cw1200_common *priv = dev->priv;
- struct ieee80211_conf *conf = &dev->conf;
-
- pr_debug("CONFIG CHANGED: %08x\n", changed);
-
- down(&priv->scan.lock);
- mutex_lock(&priv->conf_mutex);
- /* TODO: IEEE80211_CONF_CHANGE_QOS */
- /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
-
- if (changed & IEEE80211_CONF_CHANGE_POWER) {
- priv->output_power = conf->power_level;
- pr_debug("[STA] TX power: %d\n", priv->output_power);
- wsm_set_output_power(priv, priv->output_power * 10);
- }
-
- if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
- (priv->channel != conf->chandef.chan)) {
- struct ieee80211_channel *ch = conf->chandef.chan;
- struct wsm_switch_channel channel = {
- .channel_number = ch->hw_value,
- };
- pr_debug("[STA] Freq %d (wsm ch: %d).\n",
- ch->center_freq, ch->hw_value);
-
- /* __cw1200_flush() implicitly locks tx, if successful */
- if (!__cw1200_flush(priv, false)) {
- if (!wsm_switch_channel(priv, &channel)) {
- ret = wait_event_timeout(priv->channel_switch_done,
- !priv->channel_switch_in_progress,
- 3 * HZ);
- if (ret) {
- /* Already unlocks if successful */
- priv->channel = ch;
- ret = 0;
- } else {
- ret = -ETIMEDOUT;
- }
- } else {
- /* Unlock if switch channel fails */
- wsm_unlock_tx(priv);
- }
- }
- }
-
- if (changed & IEEE80211_CONF_CHANGE_PS) {
- if (!(conf->flags & IEEE80211_CONF_PS))
- priv->powersave_mode.mode = WSM_PSM_ACTIVE;
- else if (conf->dynamic_ps_timeout <= 0)
- priv->powersave_mode.mode = WSM_PSM_PS;
- else
- priv->powersave_mode.mode = WSM_PSM_FAST_PS;
-
- /* Firmware requires that value for this 1-byte field must
- * be specified in units of 500us. Values above the 128ms
- * threshold are not supported.
- */
- if (conf->dynamic_ps_timeout >= 0x80)
- priv->powersave_mode.fast_psm_idle_period = 0xFF;
- else
- priv->powersave_mode.fast_psm_idle_period =
- conf->dynamic_ps_timeout << 1;
-
- if (priv->join_status == CW1200_JOIN_STATUS_STA &&
- priv->bss_params.aid)
- cw1200_set_pm(priv, &priv->powersave_mode);
- }
-
- if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
- /* TBD: It looks like it's transparent
- * there's a monitor interface present -- use this
- * to determine for example whether to calculate
- * timestamps for packets or not, do not use instead
- * of filter flags!
- */
- }
-
- if (changed & IEEE80211_CONF_CHANGE_IDLE) {
- struct wsm_operational_mode mode = {
- .power_mode = cw1200_power_mode,
- .disable_more_flag_usage = true,
- };
-
- wsm_lock_tx(priv);
- /* Disable p2p-dev mode forced by TX request */
- if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
- (conf->flags & IEEE80211_CONF_IDLE) &&
- !priv->listening) {
- cw1200_disable_listening(priv);
- priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
- }
- wsm_set_operational_mode(priv, &mode);
- wsm_unlock_tx(priv);
- }
-
- if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
- pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
- conf->long_frame_max_tx_count,
- conf->short_frame_max_tx_count);
- spin_lock_bh(&priv->tx_policy_cache.lock);
- priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
- priv->short_frame_max_tx_count =
- (conf->short_frame_max_tx_count < 0x0F) ?
- conf->short_frame_max_tx_count : 0x0F;
- priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
- spin_unlock_bh(&priv->tx_policy_cache.lock);
- }
- mutex_unlock(&priv->conf_mutex);
- up(&priv->scan.lock);
- return ret;
-}
-
-void cw1200_update_filtering(struct cw1200_common *priv)
-{
- int ret;
- bool bssid_filtering = !priv->rx_filter.bssid;
- bool is_p2p = priv->vif && priv->vif->p2p;
- bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
-
- static struct wsm_beacon_filter_control bf_ctrl;
- static struct wsm_mib_beacon_filter_table bf_tbl = {
- .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
- .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
- WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
- WSM_BEACON_FILTER_IE_HAS_APPEARED,
- .entry[0].oui[0] = 0x50,
- .entry[0].oui[1] = 0x6F,
- .entry[0].oui[2] = 0x9A,
- .entry[1].ie_id = WLAN_EID_HT_OPERATION,
- .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
- WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
- WSM_BEACON_FILTER_IE_HAS_APPEARED,
- .entry[2].ie_id = WLAN_EID_ERP_INFO,
- .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
- WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
- WSM_BEACON_FILTER_IE_HAS_APPEARED,
- };
-
- if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
- return;
- else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
- bssid_filtering = false;
-
- if (priv->disable_beacon_filter) {
- bf_ctrl.enabled = 0;
- bf_ctrl.bcn_count = 1;
- bf_tbl.num = __cpu_to_le32(0);
- } else if (is_p2p || !is_sta) {
- bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
- WSM_BEACON_FILTER_AUTO_ERP;
- bf_ctrl.bcn_count = 0;
- bf_tbl.num = __cpu_to_le32(2);
- } else {
- bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
- bf_ctrl.bcn_count = 0;
- bf_tbl.num = __cpu_to_le32(3);
- }
-
- /* When acting as p2p client being connected to p2p GO, in order to
- * receive frames from a different p2p device, turn off bssid filter.
- *
- * WARNING: FW dependency!
- * This can only be used with FW WSM371 and its successors.
- * In that FW version even with bssid filter turned off,
- * device will block most of the unwanted frames.
- */
- if (is_p2p)
- bssid_filtering = false;
-
- ret = wsm_set_rx_filter(priv, &priv->rx_filter);
- if (!ret)
- ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
- if (!ret)
- ret = wsm_beacon_filter_control(priv, &bf_ctrl);
- if (!ret)
- ret = wsm_set_bssid_filtering(priv, bssid_filtering);
- if (!ret)
- ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
- if (ret)
- wiphy_err(priv->hw->wiphy,
- "Update filtering failed: %d.\n", ret);
- return;
-}
-
-void cw1200_update_filtering_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common,
- update_filtering_work);
-
- cw1200_update_filtering(priv);
-}
-
-void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common,
- set_beacon_wakeup_period_work);
-
- wsm_set_beacon_wakeup_period(priv,
- priv->beacon_int * priv->join_dtim_period >
- MAX_BEACON_SKIP_TIME_MS ? 1 :
- priv->join_dtim_period, 0);
-}
-
-u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
- struct netdev_hw_addr_list *mc_list)
-{
- static u8 broadcast_ipv6[ETH_ALEN] = {
- 0x33, 0x33, 0x00, 0x00, 0x00, 0x01
- };
- static u8 broadcast_ipv4[ETH_ALEN] = {
- 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
- };
- struct cw1200_common *priv = hw->priv;
- struct netdev_hw_addr *ha;
- int count = 0;
-
- /* Disable multicast filtering */
- priv->has_multicast_subscription = false;
- memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
-
- if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
- return 0;
-
- /* Enable if requested */
- netdev_hw_addr_list_for_each(ha, mc_list) {
- pr_debug("[STA] multicast: %pM\n", ha->addr);
- memcpy(&priv->multicast_filter.macaddrs[count],
- ha->addr, ETH_ALEN);
- if (!ether_addr_equal(ha->addr, broadcast_ipv4) &&
- !ether_addr_equal(ha->addr, broadcast_ipv6))
- priv->has_multicast_subscription = true;
- count++;
- }
-
- if (count) {
- priv->multicast_filter.enable = __cpu_to_le32(1);
- priv->multicast_filter.num_addrs = __cpu_to_le32(count);
- }
-
- return netdev_hw_addr_list_count(mc_list);
-}
-
-void cw1200_configure_filter(struct ieee80211_hw *dev,
- unsigned int changed_flags,
- unsigned int *total_flags,
- u64 multicast)
-{
- struct cw1200_common *priv = dev->priv;
- bool listening = !!(*total_flags &
- (FIF_OTHER_BSS |
- FIF_BCN_PRBRESP_PROMISC |
- FIF_PROBE_REQ));
-
- *total_flags &= FIF_OTHER_BSS |
- FIF_FCSFAIL |
- FIF_BCN_PRBRESP_PROMISC |
- FIF_PROBE_REQ;
-
- down(&priv->scan.lock);
- mutex_lock(&priv->conf_mutex);
-
- priv->rx_filter.promiscuous = 0;
- priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
- FIF_PROBE_REQ)) ? 1 : 0;
- priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
- priv->disable_beacon_filter = !(*total_flags &
- (FIF_BCN_PRBRESP_PROMISC |
- FIF_PROBE_REQ));
- if (priv->listening != listening) {
- priv->listening = listening;
- wsm_lock_tx(priv);
- cw1200_update_listening(priv, listening);
- wsm_unlock_tx(priv);
- }
- cw1200_update_filtering(priv);
- mutex_unlock(&priv->conf_mutex);
- up(&priv->scan.lock);
-}
-
-int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
- u16 queue, const struct ieee80211_tx_queue_params *params)
-{
- struct cw1200_common *priv = dev->priv;
- int ret = 0;
- /* To prevent re-applying PM request OID again and again*/
- bool old_uapsd_flags;
-
- mutex_lock(&priv->conf_mutex);
-
- if (queue < dev->queues) {
- old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags);
-
- WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
- ret = wsm_set_tx_queue_params(priv,
- &priv->tx_queue_params.params[queue], queue);
- if (ret) {
- ret = -EINVAL;
- goto out;
- }
-
- WSM_EDCA_SET(&priv->edca, queue, params->aifs,
- params->cw_min, params->cw_max,
- params->txop, 0xc8,
- params->uapsd);
- ret = wsm_set_edca_params(priv, &priv->edca);
- if (ret) {
- ret = -EINVAL;
- goto out;
- }
-
- if (priv->mode == NL80211_IFTYPE_STATION) {
- ret = cw1200_set_uapsd_param(priv, &priv->edca);
- if (!ret && priv->setbssparams_done &&
- (priv->join_status == CW1200_JOIN_STATUS_STA) &&
- (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags)))
- ret = cw1200_set_pm(priv, &priv->powersave_mode);
- }
- } else {
- ret = -EINVAL;
- }
-
-out:
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
-int cw1200_get_stats(struct ieee80211_hw *dev,
- struct ieee80211_low_level_stats *stats)
-{
- struct cw1200_common *priv = dev->priv;
-
- memcpy(stats, &priv->stats, sizeof(*stats));
- return 0;
-}
-
-int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
-{
- struct wsm_set_pm pm = *arg;
-
- if (priv->uapsd_info.uapsd_flags != 0)
- pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
-
- if (memcmp(&pm, &priv->firmware_ps_mode,
- sizeof(struct wsm_set_pm))) {
- priv->firmware_ps_mode = pm;
- return wsm_set_pm(priv, &pm);
- } else {
- return 0;
- }
-}
-
-int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key)
-{
- int ret = -EOPNOTSUPP;
- struct cw1200_common *priv = dev->priv;
- struct ieee80211_key_seq seq;
-
- mutex_lock(&priv->conf_mutex);
-
- if (cmd == SET_KEY) {
- u8 *peer_addr = NULL;
- int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
- 1 : 0;
- int idx = cw1200_alloc_key(priv);
- struct wsm_add_key *wsm_key = &priv->keys[idx];
-
- if (idx < 0) {
- ret = -EINVAL;
- goto finally;
- }
-
- if (sta)
- peer_addr = sta->addr;
-
- key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
- IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- if (key->keylen > 16) {
- cw1200_free_key(priv, idx);
- ret = -EINVAL;
- goto finally;
- }
-
- if (pairwise) {
- wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
- memcpy(wsm_key->wep_pairwise.peer,
- peer_addr, ETH_ALEN);
- memcpy(wsm_key->wep_pairwise.keydata,
- &key->key[0], key->keylen);
- wsm_key->wep_pairwise.keylen = key->keylen;
- } else {
- wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
- memcpy(wsm_key->wep_group.keydata,
- &key->key[0], key->keylen);
- wsm_key->wep_group.keylen = key->keylen;
- wsm_key->wep_group.keyid = key->keyidx;
- }
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- ieee80211_get_key_rx_seq(key, 0, &seq);
- if (pairwise) {
- wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
- memcpy(wsm_key->tkip_pairwise.peer,
- peer_addr, ETH_ALEN);
- memcpy(wsm_key->tkip_pairwise.keydata,
- &key->key[0], 16);
- memcpy(wsm_key->tkip_pairwise.tx_mic_key,
- &key->key[16], 8);
- memcpy(wsm_key->tkip_pairwise.rx_mic_key,
- &key->key[24], 8);
- } else {
- size_t mic_offset =
- (priv->mode == NL80211_IFTYPE_AP) ?
- 16 : 24;
- wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
- memcpy(wsm_key->tkip_group.keydata,
- &key->key[0], 16);
- memcpy(wsm_key->tkip_group.rx_mic_key,
- &key->key[mic_offset], 8);
-
- wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
- wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
- wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
- wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
- wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
- wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
- wsm_key->tkip_group.rx_seqnum[6] = 0;
- wsm_key->tkip_group.rx_seqnum[7] = 0;
-
- wsm_key->tkip_group.keyid = key->keyidx;
- }
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- ieee80211_get_key_rx_seq(key, 0, &seq);
- if (pairwise) {
- wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
- memcpy(wsm_key->aes_pairwise.peer,
- peer_addr, ETH_ALEN);
- memcpy(wsm_key->aes_pairwise.keydata,
- &key->key[0], 16);
- } else {
- wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
- memcpy(wsm_key->aes_group.keydata,
- &key->key[0], 16);
-
- wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
- wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
- wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
- wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
- wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
- wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
- wsm_key->aes_group.rx_seqnum[6] = 0;
- wsm_key->aes_group.rx_seqnum[7] = 0;
- wsm_key->aes_group.keyid = key->keyidx;
- }
- break;
- case WLAN_CIPHER_SUITE_SMS4:
- if (pairwise) {
- wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
- memcpy(wsm_key->wapi_pairwise.peer,
- peer_addr, ETH_ALEN);
- memcpy(wsm_key->wapi_pairwise.keydata,
- &key->key[0], 16);
- memcpy(wsm_key->wapi_pairwise.mic_key,
- &key->key[16], 16);
- wsm_key->wapi_pairwise.keyid = key->keyidx;
- } else {
- wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
- memcpy(wsm_key->wapi_group.keydata,
- &key->key[0], 16);
- memcpy(wsm_key->wapi_group.mic_key,
- &key->key[16], 16);
- wsm_key->wapi_group.keyid = key->keyidx;
- }
- break;
- default:
- pr_warn("Unhandled key type %d\n", key->cipher);
- cw1200_free_key(priv, idx);
- ret = -EOPNOTSUPP;
- goto finally;
- }
- ret = wsm_add_key(priv, wsm_key);
- if (!ret)
- key->hw_key_idx = idx;
- else
- cw1200_free_key(priv, idx);
- } else if (cmd == DISABLE_KEY) {
- struct wsm_remove_key wsm_key = {
- .index = key->hw_key_idx,
- };
-
- if (wsm_key.index > WSM_KEY_MAX_INDEX) {
- ret = -EINVAL;
- goto finally;
- }
-
- cw1200_free_key(priv, wsm_key.index);
- ret = wsm_remove_key(priv, &wsm_key);
- } else {
- pr_warn("Unhandled key command %d\n", cmd);
- }
-
-finally:
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
-void cw1200_wep_key_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, wep_key_work);
- u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
- struct cw1200_queue *queue = &priv->tx_queue[queue_id];
- __le32 wep_default_key_id = __cpu_to_le32(
- priv->wep_default_key_id);
-
- pr_debug("[STA] Setting default WEP key: %d\n",
- priv->wep_default_key_id);
- wsm_flush_tx(priv);
- wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
- &wep_default_key_id, sizeof(wep_default_key_id));
- cw1200_queue_requeue(queue, priv->pending_frame_id);
- wsm_unlock_tx(priv);
-}
-
-int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
-{
- int ret = 0;
- __le32 val32;
- struct cw1200_common *priv = hw->priv;
-
- if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
- return 0;
-
- if (value != (u32) -1)
- val32 = __cpu_to_le32(value);
- else
- val32 = 0; /* disabled */
-
- if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
- /* device is down, can _not_ set threshold */
- ret = -ENODEV;
- goto out;
- }
-
- if (priv->rts_threshold == value)
- goto out;
-
- pr_debug("[STA] Setting RTS threshold: %d\n",
- priv->rts_threshold);
-
- /* mutex_lock(&priv->conf_mutex); */
- ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
- &val32, sizeof(val32));
- if (!ret)
- priv->rts_threshold = value;
- /* mutex_unlock(&priv->conf_mutex); */
-
-out:
- return ret;
-}
-
-/* If successful, LOCKS the TX queue! */
-static int __cw1200_flush(struct cw1200_common *priv, bool drop)
-{
- int i, ret;
-
- for (;;) {
- /* TODO: correct flush handling is required when dev_stop.
- * Temporary workaround: 2s
- */
- if (drop) {
- for (i = 0; i < 4; ++i)
- cw1200_queue_clear(&priv->tx_queue[i]);
- } else {
- ret = wait_event_timeout(
- priv->tx_queue_stats.wait_link_id_empty,
- cw1200_queue_stats_is_empty(
- &priv->tx_queue_stats, -1),
- 2 * HZ);
- }
-
- if (!drop && ret <= 0) {
- ret = -ETIMEDOUT;
- break;
- } else {
- ret = 0;
- }
-
- wsm_lock_tx(priv);
- if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
- /* Highly unlikely: WSM requeued frames. */
- wsm_unlock_tx(priv);
- continue;
- }
- break;
- }
- return ret;
-}
-
-void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- u32 queues, bool drop)
-{
- struct cw1200_common *priv = hw->priv;
-
- switch (priv->mode) {
- case NL80211_IFTYPE_MONITOR:
- drop = true;
- break;
- case NL80211_IFTYPE_AP:
- if (!priv->enable_beacon)
- drop = true;
- break;
- }
-
- if (!__cw1200_flush(priv, drop))
- wsm_unlock_tx(priv);
-
- return;
-}
-
-/* ******************************************************************** */
-/* WSM callbacks */
-
-void cw1200_free_event_queue(struct cw1200_common *priv)
-{
- LIST_HEAD(list);
-
- spin_lock(&priv->event_queue_lock);
- list_splice_init(&priv->event_queue, &list);
- spin_unlock(&priv->event_queue_lock);
-
- __cw1200_free_event_queue(&list);
-}
-
-void cw1200_event_handler(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, event_handler);
- struct cw1200_wsm_event *event;
- LIST_HEAD(list);
-
- spin_lock(&priv->event_queue_lock);
- list_splice_init(&priv->event_queue, &list);
- spin_unlock(&priv->event_queue_lock);
-
- list_for_each_entry(event, &list, link) {
- switch (event->evt.id) {
- case WSM_EVENT_ERROR:
- pr_err("Unhandled WSM Error from LMAC\n");
- break;
- case WSM_EVENT_BSS_LOST:
- pr_debug("[CQM] BSS lost.\n");
- cancel_work_sync(&priv->unjoin_work);
- if (!down_trylock(&priv->scan.lock)) {
- cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
- up(&priv->scan.lock);
- } else {
- /* Scan is in progress. Delay reporting.
- * Scan complete will trigger bss_loss_work
- */
- priv->delayed_link_loss = 1;
- /* Also start a watchdog. */
- queue_delayed_work(priv->workqueue,
- &priv->bss_loss_work, 5*HZ);
- }
- break;
- case WSM_EVENT_BSS_REGAINED:
- pr_debug("[CQM] BSS regained.\n");
- cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
- cancel_work_sync(&priv->unjoin_work);
- break;
- case WSM_EVENT_RADAR_DETECTED:
- wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
- break;
- case WSM_EVENT_RCPI_RSSI:
- {
- /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
- * RSSI = RCPI / 2 - 110
- */
- int rcpi_rssi = (int)(event->evt.data & 0xFF);
- int cqm_evt;
- if (priv->cqm_use_rssi)
- rcpi_rssi = (s8)rcpi_rssi;
- else
- rcpi_rssi = rcpi_rssi / 2 - 110;
-
- cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
- NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
- NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
- pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
- ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
- GFP_KERNEL);
- break;
- }
- case WSM_EVENT_BT_INACTIVE:
- pr_warn("Unhandled BT INACTIVE from LMAC\n");
- break;
- case WSM_EVENT_BT_ACTIVE:
- pr_warn("Unhandled BT ACTIVE from LMAC\n");
- break;
- }
- }
- __cw1200_free_event_queue(&list);
-}
-
-void cw1200_bss_loss_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, bss_loss_work.work);
-
- pr_debug("[CQM] Reporting connection loss.\n");
- wsm_lock_tx(priv);
- if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
- wsm_unlock_tx(priv);
-}
-
-void cw1200_bss_params_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, bss_params_work);
- mutex_lock(&priv->conf_mutex);
-
- priv->bss_params.reset_beacon_loss = 1;
- wsm_set_bss_params(priv, &priv->bss_params);
- priv->bss_params.reset_beacon_loss = 0;
-
- mutex_unlock(&priv->conf_mutex);
-}
-
-/* ******************************************************************** */
-/* Internal API */
-
-/* This function is called to Parse the SDD file
- * to extract listen_interval and PTA related information
- * sdd is a TLV: u8 id, u8 len, u8 data[]
- */
-static int cw1200_parse_sdd_file(struct cw1200_common *priv)
-{
- const u8 *p = priv->sdd->data;
- int ret = 0;
-
- while (p + 2 <= priv->sdd->data + priv->sdd->size) {
- if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
- pr_warn("Malformed sdd structure\n");
- return -1;
- }
- switch (p[0]) {
- case SDD_PTA_CFG_ELT_ID: {
- u16 v;
- if (p[1] < 4) {
- pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
- ret = -1;
- break;
- }
- v = le16_to_cpu(*((__le16 *)(p + 2)));
- if (!v) /* non-zero means this is enabled */
- break;
-
- v = le16_to_cpu(*((__le16 *)(p + 4)));
- priv->conf_listen_interval = (v >> 7) & 0x1F;
- pr_debug("PTA found; Listen Interval %d\n",
- priv->conf_listen_interval);
- break;
- }
- case SDD_REFERENCE_FREQUENCY_ELT_ID: {
- u16 clk = le16_to_cpu(*((__le16 *)(p + 2)));
- if (clk != priv->hw_refclk)
- pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
- clk, priv->hw_refclk);
- break;
- }
- default:
- break;
- }
- p += p[1] + 2;
- }
-
- if (!priv->bt_present) {
- pr_debug("PTA element NOT found.\n");
- priv->conf_listen_interval = 0;
- }
- return ret;
-}
-
-int cw1200_setup_mac(struct cw1200_common *priv)
-{
- int ret = 0;
-
- /* NOTE: There is a bug in FW: it reports signal
- * as RSSI if RSSI subscription is enabled.
- * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
- *
- * NOTE2: RSSI based reports have been switched to RCPI, since
- * FW has a bug and RSSI reported values are not stable,
- * what can leads to signal level oscilations in user-end applications
- */
- struct wsm_rcpi_rssi_threshold threshold = {
- .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
- WSM_RCPI_RSSI_DONT_USE_UPPER |
- WSM_RCPI_RSSI_DONT_USE_LOWER,
- .rollingAverageCount = 16,
- };
-
- struct wsm_configuration cfg = {
- .dot11StationId = &priv->mac_addr[0],
- };
-
- /* Remember the decission here to make sure, we will handle
- * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
- */
- if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
- priv->cqm_use_rssi = true;
-
- if (!priv->sdd) {
- ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
- if (ret) {
- pr_err("Can't load sdd file %s.\n", priv->sdd_path);
- return ret;
- }
- cw1200_parse_sdd_file(priv);
- }
-
- cfg.dpdData = priv->sdd->data;
- cfg.dpdData_size = priv->sdd->size;
- ret = wsm_configuration(priv, &cfg);
- if (ret)
- return ret;
-
- /* Configure RSSI/SCPI reporting as RSSI. */
- wsm_set_rcpi_rssi_threshold(priv, &threshold);
-
- return 0;
-}
-
-static void cw1200_join_complete(struct cw1200_common *priv)
-{
- pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
-
- priv->join_pending = false;
- if (priv->join_complete_status) {
- priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
- cw1200_update_listening(priv, priv->listening);
- cw1200_do_unjoin(priv);
- ieee80211_connection_loss(priv->vif);
- } else {
- if (priv->mode == NL80211_IFTYPE_ADHOC)
- priv->join_status = CW1200_JOIN_STATUS_IBSS;
- else
- priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
- }
- wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
-}
-
-void cw1200_join_complete_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, join_complete_work);
- mutex_lock(&priv->conf_mutex);
- cw1200_join_complete(priv);
- mutex_unlock(&priv->conf_mutex);
-}
-
-void cw1200_join_complete_cb(struct cw1200_common *priv,
- struct wsm_join_complete *arg)
-{
- pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
- arg->status);
-
- if (cancel_delayed_work(&priv->join_timeout)) {
- priv->join_complete_status = arg->status;
- queue_work(priv->workqueue, &priv->join_complete_work);
- }
-}
-
-/* MUST be called with tx_lock held! It will be unlocked for us. */
-static void cw1200_do_join(struct cw1200_common *priv)
-{
- const u8 *bssid;
- struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
- struct cfg80211_bss *bss = NULL;
- struct wsm_protected_mgmt_policy mgmt_policy;
- struct wsm_join join = {
- .mode = conf->ibss_joined ?
- WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
- .preamble_type = WSM_JOIN_PREAMBLE_LONG,
- .probe_for_join = 1,
- .atim_window = 0,
- .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
- conf->basic_rates),
- };
- if (delayed_work_pending(&priv->join_timeout)) {
- pr_warn("[STA] - Join request already pending, skipping..\n");
- wsm_unlock_tx(priv);
- return;
- }
-
- if (priv->join_status)
- cw1200_do_unjoin(priv);
-
- bssid = priv->vif->bss_conf.bssid;
-
- bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0,
- IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
-
- if (!bss && !conf->ibss_joined) {
- wsm_unlock_tx(priv);
- return;
- }
-
- mutex_lock(&priv->conf_mutex);
-
- /* Under the conf lock: check scan status and
- * bail out if it is in progress.
- */
- if (atomic_read(&priv->scan.in_progress)) {
- wsm_unlock_tx(priv);
- goto done_put;
- }
-
- priv->join_pending = true;
-
- /* Sanity check basic rates */
- if (!join.basic_rate_set)
- join.basic_rate_set = 7;
-
- /* Sanity check beacon interval */
- if (!priv->beacon_int)
- priv->beacon_int = 1;
-
- join.beacon_interval = priv->beacon_int;
-
- /* BT Coex related changes */
- if (priv->bt_present) {
- if (((priv->conf_listen_interval * 100) %
- priv->beacon_int) == 0)
- priv->listen_interval =
- ((priv->conf_listen_interval * 100) /
- priv->beacon_int);
- else
- priv->listen_interval =
- ((priv->conf_listen_interval * 100) /
- priv->beacon_int + 1);
- }
-
- if (priv->hw->conf.ps_dtim_period)
- priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
- join.dtim_period = priv->join_dtim_period;
-
- join.channel_number = priv->channel->hw_value;
- join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
- WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
-
- memcpy(join.bssid, bssid, sizeof(join.bssid));
-
- pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
- join.bssid,
- join.dtim_period, priv->beacon_int);
-
- if (!conf->ibss_joined) {
- const u8 *ssidie;
- rcu_read_lock();
- ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
- if (ssidie) {
- join.ssid_len = ssidie[1];
- memcpy(join.ssid, &ssidie[2], join.ssid_len);
- }
- rcu_read_unlock();
- }
-
- if (priv->vif->p2p) {
- join.flags |= WSM_JOIN_FLAGS_P2P_GO;
- join.basic_rate_set =
- cw1200_rate_mask_to_wsm(priv, 0xFF0);
- }
-
- /* Enable asynchronous join calls */
- if (!conf->ibss_joined) {
- join.flags |= WSM_JOIN_FLAGS_FORCE;
- join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
- }
-
- wsm_flush_tx(priv);
-
- /* Stay Awake for Join and Auth Timeouts and a bit more */
- cw1200_pm_stay_awake(&priv->pm_state,
- CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
-
- cw1200_update_listening(priv, false);
-
- /* Turn on Block ACKs */
- wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
- priv->ba_rx_tid_mask);
-
- /* Set up timeout */
- if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
- priv->join_status = CW1200_JOIN_STATUS_JOINING;
- queue_delayed_work(priv->workqueue,
- &priv->join_timeout,
- CW1200_JOIN_TIMEOUT);
- }
-
- /* 802.11w protected mgmt frames */
- mgmt_policy.protectedMgmtEnable = 0;
- mgmt_policy.unprotectedMgmtFramesAllowed = 1;
- mgmt_policy.encryptionForAuthFrame = 1;
- wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
-
- /* Perform actual join */
- if (wsm_join(priv, &join)) {
- pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
- cancel_delayed_work_sync(&priv->join_timeout);
- cw1200_update_listening(priv, priv->listening);
- /* Tx lock still held, unjoin will clear it. */
- if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
- wsm_unlock_tx(priv);
- } else {
- if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
- cw1200_join_complete(priv); /* Will clear tx_lock */
-
- /* Upload keys */
- cw1200_upload_keys(priv);
-
- /* Due to beacon filtering it is possible that the
- * AP's beacon is not known for the mac80211 stack.
- * Disable filtering temporary to make sure the stack
- * receives at least one
- */
- priv->disable_beacon_filter = true;
- }
- cw1200_update_filtering(priv);
-
-done_put:
- mutex_unlock(&priv->conf_mutex);
- if (bss)
- cfg80211_put_bss(priv->hw->wiphy, bss);
-}
-
-void cw1200_join_timeout(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, join_timeout.work);
- pr_debug("[WSM] Join timed out.\n");
- wsm_lock_tx(priv);
- if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
- wsm_unlock_tx(priv);
-}
-
-static void cw1200_do_unjoin(struct cw1200_common *priv)
-{
- struct wsm_reset reset = {
- .reset_statistics = true,
- };
-
- cancel_delayed_work_sync(&priv->join_timeout);
-
- mutex_lock(&priv->conf_mutex);
- priv->join_pending = false;
-
- if (atomic_read(&priv->scan.in_progress)) {
- if (priv->delayed_unjoin)
- wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
- else
- priv->delayed_unjoin = true;
- goto done;
- }
-
- priv->delayed_link_loss = false;
-
- if (!priv->join_status)
- goto done;
-
- if (priv->join_status == CW1200_JOIN_STATUS_AP)
- goto done;
-
- cancel_work_sync(&priv->update_filtering_work);
- cancel_work_sync(&priv->set_beacon_wakeup_period_work);
- priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-
- /* Unjoin is a reset. */
- wsm_flush_tx(priv);
- wsm_keep_alive_period(priv, 0);
- wsm_reset(priv, &reset);
- wsm_set_output_power(priv, priv->output_power * 10);
- priv->join_dtim_period = 0;
- cw1200_setup_mac(priv);
- cw1200_free_event_queue(priv);
- cancel_work_sync(&priv->event_handler);
- cw1200_update_listening(priv, priv->listening);
- cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
-
- /* Disable Block ACKs */
- wsm_set_block_ack_policy(priv, 0, 0);
-
- priv->disable_beacon_filter = false;
- cw1200_update_filtering(priv);
- memset(&priv->association_mode, 0,
- sizeof(priv->association_mode));
- memset(&priv->bss_params, 0, sizeof(priv->bss_params));
- priv->setbssparams_done = false;
- memset(&priv->firmware_ps_mode, 0,
- sizeof(priv->firmware_ps_mode));
-
- pr_debug("[STA] Unjoin completed.\n");
-
-done:
- mutex_unlock(&priv->conf_mutex);
-}
-
-void cw1200_unjoin_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, unjoin_work);
-
- cw1200_do_unjoin(priv);
-
- /* Tell the stack we're dead */
- ieee80211_connection_loss(priv->vif);
-
- wsm_unlock_tx(priv);
-}
-
-int cw1200_enable_listening(struct cw1200_common *priv)
-{
- struct wsm_start start = {
- .mode = WSM_START_MODE_P2P_DEV,
- .band = WSM_PHY_BAND_2_4G,
- .beacon_interval = 100,
- .dtim_period = 1,
- .probe_delay = 0,
- .basic_rate_set = 0x0F,
- };
-
- if (priv->channel) {
- start.band = priv->channel->band == IEEE80211_BAND_5GHZ ?
- WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
- start.channel_number = priv->channel->hw_value;
- } else {
- start.band = WSM_PHY_BAND_2_4G;
- start.channel_number = 1;
- }
-
- return wsm_start(priv, &start);
-}
-
-int cw1200_disable_listening(struct cw1200_common *priv)
-{
- int ret;
- struct wsm_reset reset = {
- .reset_statistics = true,
- };
- ret = wsm_reset(priv, &reset);
- return ret;
-}
-
-void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
-{
- if (enabled) {
- if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
- if (!cw1200_enable_listening(priv))
- priv->join_status = CW1200_JOIN_STATUS_MONITOR;
- wsm_set_probe_responder(priv, true);
- }
- } else {
- if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
- if (!cw1200_disable_listening(priv))
- priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
- wsm_set_probe_responder(priv, false);
- }
- }
-}
-
-int cw1200_set_uapsd_param(struct cw1200_common *priv,
- const struct wsm_edca_params *arg)
-{
- int ret;
- u16 uapsd_flags = 0;
-
- /* Here's the mapping AC [queue, bit]
- * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
- */
-
- if (arg->uapsd_enable[0])
- uapsd_flags |= 1 << 3;
-
- if (arg->uapsd_enable[1])
- uapsd_flags |= 1 << 2;
-
- if (arg->uapsd_enable[2])
- uapsd_flags |= 1 << 1;
-
- if (arg->uapsd_enable[3])
- uapsd_flags |= 1;
-
- /* Currently pseudo U-APSD operation is not supported, so setting
- * MinAutoTriggerInterval, MaxAutoTriggerInterval and
- * AutoTriggerStep to 0
- */
-
- priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
- priv->uapsd_info.min_auto_trigger_interval = 0;
- priv->uapsd_info.max_auto_trigger_interval = 0;
- priv->uapsd_info.auto_trigger_step = 0;
-
- ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
- return ret;
-}
-
-/* ******************************************************************** */
-/* AP API */
-
-int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct cw1200_common *priv = hw->priv;
- struct cw1200_sta_priv *sta_priv =
- (struct cw1200_sta_priv *)&sta->drv_priv;
- struct cw1200_link_entry *entry;
- struct sk_buff *skb;
-
- if (priv->mode != NL80211_IFTYPE_AP)
- return 0;
-
- sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
- if (WARN_ON(!sta_priv->link_id)) {
- wiphy_info(priv->hw->wiphy,
- "[AP] No more link IDs available.\n");
- return -ENOENT;
- }
-
- entry = &priv->link_id_db[sta_priv->link_id - 1];
- spin_lock_bh(&priv->ps_state_lock);
- if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
- IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
- priv->sta_asleep_mask |= BIT(sta_priv->link_id);
- entry->status = CW1200_LINK_HARD;
- while ((skb = skb_dequeue(&entry->rx_queue)))
- ieee80211_rx_irqsafe(priv->hw, skb);
- spin_unlock_bh(&priv->ps_state_lock);
- return 0;
-}
-
-int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct cw1200_common *priv = hw->priv;
- struct cw1200_sta_priv *sta_priv =
- (struct cw1200_sta_priv *)&sta->drv_priv;
- struct cw1200_link_entry *entry;
-
- if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
- return 0;
-
- entry = &priv->link_id_db[sta_priv->link_id - 1];
- spin_lock_bh(&priv->ps_state_lock);
- entry->status = CW1200_LINK_RESERVE;
- entry->timestamp = jiffies;
- wsm_lock_tx_async(priv);
- if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
- wsm_unlock_tx(priv);
- spin_unlock_bh(&priv->ps_state_lock);
- flush_workqueue(priv->workqueue);
- return 0;
-}
-
-static void __cw1200_sta_notify(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- enum sta_notify_cmd notify_cmd,
- int link_id)
-{
- struct cw1200_common *priv = dev->priv;
- u32 bit, prev;
-
- /* Zero link id means "for all link IDs" */
- if (link_id)
- bit = BIT(link_id);
- else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
- bit = 0;
- else
- bit = priv->link_id_map;
- prev = priv->sta_asleep_mask & bit;
-
- switch (notify_cmd) {
- case STA_NOTIFY_SLEEP:
- if (!prev) {
- if (priv->buffered_multicasts &&
- !priv->sta_asleep_mask)
- queue_work(priv->workqueue,
- &priv->multicast_start_work);
- priv->sta_asleep_mask |= bit;
- }
- break;
- case STA_NOTIFY_AWAKE:
- if (prev) {
- priv->sta_asleep_mask &= ~bit;
- priv->pspoll_mask &= ~bit;
- if (priv->tx_multicast && link_id &&
- !priv->sta_asleep_mask)
- queue_work(priv->workqueue,
- &priv->multicast_stop_work);
- cw1200_bh_wakeup(priv);
- }
- break;
- }
-}
-
-void cw1200_sta_notify(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- enum sta_notify_cmd notify_cmd,
- struct ieee80211_sta *sta)
-{
- struct cw1200_common *priv = dev->priv;
- struct cw1200_sta_priv *sta_priv =
- (struct cw1200_sta_priv *)&sta->drv_priv;
-
- spin_lock_bh(&priv->ps_state_lock);
- __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
- spin_unlock_bh(&priv->ps_state_lock);
-}
-
-static void cw1200_ps_notify(struct cw1200_common *priv,
- int link_id, bool ps)
-{
- if (link_id > CW1200_MAX_STA_IN_AP_MODE)
- return;
-
- pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
- ps ? "Stop" : "Start",
- link_id, priv->sta_asleep_mask);
-
- __cw1200_sta_notify(priv->hw, priv->vif,
- ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
-}
-
-static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
-{
- struct sk_buff *skb;
- struct wsm_update_ie update_ie = {
- .what = WSM_UPDATE_IE_BEACON,
- .count = 1,
- };
- u16 tim_offset, tim_length;
-
- pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
-
- skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
- &tim_offset, &tim_length);
- if (!skb) {
- if (!__cw1200_flush(priv, true))
- wsm_unlock_tx(priv);
- return -ENOENT;
- }
-
- if (tim_offset && tim_length >= 6) {
- /* Ignore DTIM count from mac80211:
- * firmware handles DTIM internally.
- */
- skb->data[tim_offset + 2] = 0;
-
- /* Set/reset aid0 bit */
- if (aid0_bit_set)
- skb->data[tim_offset + 4] |= 1;
- else
- skb->data[tim_offset + 4] &= ~1;
- }
-
- update_ie.ies = &skb->data[tim_offset];
- update_ie.length = tim_length;
- wsm_update_ie(priv, &update_ie);
-
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-void cw1200_set_tim_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, set_tim_work);
- (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
-}
-
-int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
- bool set)
-{
- struct cw1200_common *priv = dev->priv;
- queue_work(priv->workqueue, &priv->set_tim_work);
- return 0;
-}
-
-void cw1200_set_cts_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, set_cts_work);
-
- u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
- struct wsm_update_ie update_ie = {
- .what = WSM_UPDATE_IE_BEACON,
- .count = 1,
- .ies = erp_ie,
- .length = 3,
- };
- u32 erp_info;
- __le32 use_cts_prot;
- mutex_lock(&priv->conf_mutex);
- erp_info = priv->erp_info;
- mutex_unlock(&priv->conf_mutex);
- use_cts_prot =
- erp_info & WLAN_ERP_USE_PROTECTION ?
- __cpu_to_le32(1) : 0;
-
- erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
-
- pr_debug("[STA] ERP information 0x%x\n", erp_info);
-
- wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
- &use_cts_prot, sizeof(use_cts_prot));
- wsm_update_ie(priv, &update_ie);
-
- return;
-}
-
-static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
-{
- struct wsm_override_internal_txrate arg;
- int ret = 0;
-
- if (priv->mode == NL80211_IFTYPE_STATION) {
- /* Plumb PSPOLL and NULL template */
- cw1200_upload_pspoll(priv);
- cw1200_upload_null(priv);
- cw1200_upload_qosnull(priv);
- } else {
- return 0;
- }
-
- memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
-
- if (!priv->vif->p2p) {
- /* STATION mode */
- if (priv->bss_params.operational_rate_set & ~0xF) {
- pr_debug("[STA] STA has ERP rates\n");
- /* G or BG mode */
- arg.internalTxRate = (__ffs(
- priv->bss_params.operational_rate_set & ~0xF));
- } else {
- pr_debug("[STA] STA has non ERP rates\n");
- /* B only mode */
- arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
- }
- arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
- } else {
- /* P2P mode */
- arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
- arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
- }
-
- pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
- priv->mode,
- arg.internalTxRate,
- arg.nonErpInternalTxRate);
-
- ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
- &arg, sizeof(arg));
-
- return ret;
-}
-
-void cw1200_bss_info_changed(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *info,
- u32 changed)
-{
- struct cw1200_common *priv = dev->priv;
- bool do_join = false;
-
- mutex_lock(&priv->conf_mutex);
-
- pr_debug("BSS CHANGED: %08x\n", changed);
-
- /* TODO: BSS_CHANGED_QOS */
- /* TODO: BSS_CHANGED_TXPOWER */
-
- if (changed & BSS_CHANGED_ARP_FILTER) {
- struct wsm_mib_arp_ipv4_filter filter = {0};
- int i;
-
- pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
- info->arp_addr_cnt);
-
- /* Currently only one IP address is supported by firmware.
- * In case of more IPs arp filtering will be disabled.
- */
- if (info->arp_addr_cnt > 0 &&
- info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
- for (i = 0; i < info->arp_addr_cnt; i++) {
- filter.ipv4addrs[i] = info->arp_addr_list[i];
- pr_debug("[STA] addr[%d]: 0x%X\n",
- i, filter.ipv4addrs[i]);
- }
- filter.enable = __cpu_to_le32(1);
- }
-
- pr_debug("[STA] arp ip filter enable: %d\n",
- __le32_to_cpu(filter.enable));
-
- wsm_set_arp_ipv4_filter(priv, &filter);
- }
-
- if (changed &
- (BSS_CHANGED_BEACON |
- BSS_CHANGED_AP_PROBE_RESP |
- BSS_CHANGED_BSSID |
- BSS_CHANGED_SSID |
- BSS_CHANGED_IBSS)) {
- pr_debug("BSS_CHANGED_BEACON\n");
- priv->beacon_int = info->beacon_int;
- cw1200_update_beaconing(priv);
- cw1200_upload_beacon(priv);
- }
-
- if (changed & BSS_CHANGED_BEACON_ENABLED) {
- pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
-
- if (priv->enable_beacon != info->enable_beacon) {
- cw1200_enable_beaconing(priv, info->enable_beacon);
- priv->enable_beacon = info->enable_beacon;
- }
- }
-
- if (changed & BSS_CHANGED_BEACON_INT) {
- pr_debug("CHANGED_BEACON_INT\n");
- if (info->ibss_joined)
- do_join = true;
- else if (priv->join_status == CW1200_JOIN_STATUS_AP)
- cw1200_update_beaconing(priv);
- }
-
- /* assoc/disassoc, or maybe AID changed */
- if (changed & BSS_CHANGED_ASSOC) {
- wsm_lock_tx(priv);
- priv->wep_default_key_id = -1;
- wsm_unlock_tx(priv);
- }
-
- if (changed & BSS_CHANGED_BSSID) {
- pr_debug("BSS_CHANGED_BSSID\n");
- do_join = true;
- }
-
- if (changed &
- (BSS_CHANGED_ASSOC |
- BSS_CHANGED_BSSID |
- BSS_CHANGED_IBSS |
- BSS_CHANGED_BASIC_RATES |
- BSS_CHANGED_HT)) {
- pr_debug("BSS_CHANGED_ASSOC\n");
- if (info->assoc) {
- if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
- ieee80211_connection_loss(vif);
- mutex_unlock(&priv->conf_mutex);
- return;
- } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
- priv->join_status = CW1200_JOIN_STATUS_STA;
- }
- } else {
- do_join = true;
- }
-
- if (info->assoc || info->ibss_joined) {
- struct ieee80211_sta *sta = NULL;
- __le32 htprot = 0;
-
- if (info->dtim_period)
- priv->join_dtim_period = info->dtim_period;
- priv->beacon_int = info->beacon_int;
-
- rcu_read_lock();
-
- if (info->bssid && !info->ibss_joined)
- sta = ieee80211_find_sta(vif, info->bssid);
- if (sta) {
- priv->ht_info.ht_cap = sta->ht_cap;
- priv->bss_params.operational_rate_set =
- cw1200_rate_mask_to_wsm(priv,
- sta->supp_rates[priv->channel->band]);
- priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
- priv->ht_info.operation_mode = info->ht_operation_mode;
- } else {
- memset(&priv->ht_info, 0,
- sizeof(priv->ht_info));
- priv->bss_params.operational_rate_set = -1;
- }
- rcu_read_unlock();
-
- /* Non Greenfield stations present */
- if (priv->ht_info.operation_mode &
- IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
- htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT);
-
- /* Set HT protection method */
- htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2);
-
- /* TODO:
- * STBC_param.dual_cts
- * STBC_param.LSIG_TXOP_FILL
- */
-
- wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
- &htprot, sizeof(htprot));
-
- priv->association_mode.greenfield =
- cw1200_ht_greenfield(&priv->ht_info);
- priv->association_mode.flags =
- WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
- WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
- WSM_ASSOCIATION_MODE_USE_HT_MODE |
- WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
- WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
- priv->association_mode.preamble =
- info->use_short_preamble ?
- WSM_JOIN_PREAMBLE_SHORT :
- WSM_JOIN_PREAMBLE_LONG;
- priv->association_mode.basic_rate_set = __cpu_to_le32(
- cw1200_rate_mask_to_wsm(priv,
- info->basic_rates));
- priv->association_mode.mpdu_start_spacing =
- cw1200_ht_ampdu_density(&priv->ht_info);
-
- cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
- cancel_work_sync(&priv->unjoin_work);
-
- priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
- priv->bss_params.aid = info->aid;
-
- if (priv->join_dtim_period < 1)
- priv->join_dtim_period = 1;
-
- pr_debug("[STA] DTIM %d, interval: %d\n",
- priv->join_dtim_period, priv->beacon_int);
- pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
- priv->association_mode.preamble,
- priv->association_mode.greenfield,
- priv->bss_params.aid,
- priv->bss_params.operational_rate_set,
- priv->association_mode.basic_rate_set);
- wsm_set_association_mode(priv, &priv->association_mode);
-
- if (!info->ibss_joined) {
- wsm_keep_alive_period(priv, 30 /* sec */);
- wsm_set_bss_params(priv, &priv->bss_params);
- priv->setbssparams_done = true;
- cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
- cw1200_set_pm(priv, &priv->powersave_mode);
- }
- if (priv->vif->p2p) {
- pr_debug("[STA] Setting p2p powersave configuration.\n");
- wsm_set_p2p_ps_modeinfo(priv,
- &priv->p2p_ps_modeinfo);
- }
- if (priv->bt_present)
- cw1200_set_btcoexinfo(priv);
- } else {
- memset(&priv->association_mode, 0,
- sizeof(priv->association_mode));
- memset(&priv->bss_params, 0, sizeof(priv->bss_params));
- }
- }
-
- /* ERP Protection */
- if (changed & (BSS_CHANGED_ASSOC |
- BSS_CHANGED_ERP_CTS_PROT |
- BSS_CHANGED_ERP_PREAMBLE)) {
- u32 prev_erp_info = priv->erp_info;
- if (info->use_cts_prot)
- priv->erp_info |= WLAN_ERP_USE_PROTECTION;
- else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
- priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
-
- if (info->use_short_preamble)
- priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
- else
- priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
-
- pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
-
- if (prev_erp_info != priv->erp_info)
- queue_work(priv->workqueue, &priv->set_cts_work);
- }
-
- /* ERP Slottime */
- if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
- __le32 slot_time = info->use_short_slot ?
- __cpu_to_le32(9) : __cpu_to_le32(20);
- pr_debug("[STA] Slot time: %d us.\n",
- __le32_to_cpu(slot_time));
- wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
- &slot_time, sizeof(slot_time));
- }
-
- if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
- struct wsm_rcpi_rssi_threshold threshold = {
- .rollingAverageCount = 8,
- };
- pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
- info->cqm_rssi_thold, info->cqm_rssi_hyst);
- priv->cqm_rssi_thold = info->cqm_rssi_thold;
- priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
-
- if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
- /* RSSI subscription enabled */
- /* TODO: It's not a correct way of setting threshold.
- * Upper and lower must be set equal here and adjusted
- * in callback. However current implementation is much
- * more relaible and stable.
- */
-
- /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
- * RSSI = RCPI / 2 - 110
- */
- if (priv->cqm_use_rssi) {
- threshold.upperThreshold =
- info->cqm_rssi_thold + info->cqm_rssi_hyst;
- threshold.lowerThreshold =
- info->cqm_rssi_thold;
- threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
- } else {
- threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
- threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
- }
- threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
- } else {
- /* There is a bug in FW, see sta.c. We have to enable
- * dummy subscription to get correct RSSI values.
- */
- threshold.rssiRcpiMode |=
- WSM_RCPI_RSSI_THRESHOLD_ENABLE |
- WSM_RCPI_RSSI_DONT_USE_UPPER |
- WSM_RCPI_RSSI_DONT_USE_LOWER;
- if (priv->cqm_use_rssi)
- threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
- }
- wsm_set_rcpi_rssi_threshold(priv, &threshold);
- }
- mutex_unlock(&priv->conf_mutex);
-
- if (do_join) {
- wsm_lock_tx(priv);
- cw1200_do_join(priv); /* Will unlock it for us */
- }
-}
-
-void cw1200_multicast_start_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, multicast_start_work);
- long tmo = priv->join_dtim_period *
- (priv->beacon_int + 20) * HZ / 1024;
-
- cancel_work_sync(&priv->multicast_stop_work);
-
- if (!priv->aid0_bit_set) {
- wsm_lock_tx(priv);
- cw1200_set_tim_impl(priv, true);
- priv->aid0_bit_set = true;
- mod_timer(&priv->mcast_timeout, jiffies + tmo);
- wsm_unlock_tx(priv);
- }
-}
-
-void cw1200_multicast_stop_work(struct work_struct *work)
-{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, multicast_stop_work);
-
- if (priv->aid0_bit_set) {
- del_timer_sync(&priv->mcast_timeout);
- wsm_lock_tx(priv);
- priv->aid0_bit_set = false;
- cw1200_set_tim_impl(priv, false);
- wsm_unlock_tx(priv);
- }
-}
-
-void cw1200_mcast_timeout(unsigned long arg)
-{
- struct cw1200_common *priv =
- (struct cw1200_common *)arg;
-
- wiphy_warn(priv->hw->wiphy,
- "Multicast delivery timeout.\n");
- spin_lock_bh(&priv->ps_state_lock);
- priv->tx_multicast = priv->aid0_bit_set &&
- priv->buffered_multicasts;
- if (priv->tx_multicast)
- cw1200_bh_wakeup(priv);
- spin_unlock_bh(&priv->ps_state_lock);
-}
-
-int cw1200_ampdu_action(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn,
- u8 buf_size, bool amsdu)
-{
- /* Aggregation is implemented fully in firmware,
- * including block ack negotiation. Do not allow
- * mac80211 stack to do anything: it interferes with
- * the firmware.
- */
-
- /* Note that we still need this function stubbed. */
- return -ENOTSUPP;
-}
-
-/* ******************************************************************** */
-/* WSM callback */
-void cw1200_suspend_resume(struct cw1200_common *priv,
- struct wsm_suspend_resume *arg)
-{
- pr_debug("[AP] %s: %s\n",
- arg->stop ? "stop" : "start",
- arg->multicast ? "broadcast" : "unicast");
-
- if (arg->multicast) {
- bool cancel_tmo = false;
- spin_lock_bh(&priv->ps_state_lock);
- if (arg->stop) {
- priv->tx_multicast = false;
- } else {
- /* Firmware sends this indication every DTIM if there
- * is a STA in powersave connected. There is no reason
- * to suspend, following wakeup will consume much more
- * power than it could be saved.
- */
- cw1200_pm_stay_awake(&priv->pm_state,
- priv->join_dtim_period *
- (priv->beacon_int + 20) * HZ / 1024);
- priv->tx_multicast = (priv->aid0_bit_set &&
- priv->buffered_multicasts);
- if (priv->tx_multicast) {
- cancel_tmo = true;
- cw1200_bh_wakeup(priv);
- }
- }
- spin_unlock_bh(&priv->ps_state_lock);
- if (cancel_tmo)
- del_timer_sync(&priv->mcast_timeout);
- } else {
- spin_lock_bh(&priv->ps_state_lock);
- cw1200_ps_notify(priv, arg->link_id, arg->stop);
- spin_unlock_bh(&priv->ps_state_lock);
- if (!arg->stop)
- cw1200_bh_wakeup(priv);
- }
- return;
-}
-
-/* ******************************************************************** */
-/* AP privates */
-
-static int cw1200_upload_beacon(struct cw1200_common *priv)
-{
- int ret = 0;
- struct ieee80211_mgmt *mgmt;
- struct wsm_template_frame frame = {
- .frame_type = WSM_FRAME_TYPE_BEACON,
- };
-
- u16 tim_offset;
- u16 tim_len;
-
- if (priv->mode == NL80211_IFTYPE_STATION ||
- priv->mode == NL80211_IFTYPE_MONITOR ||
- priv->mode == NL80211_IFTYPE_UNSPECIFIED)
- goto done;
-
- if (priv->vif->p2p)
- frame.rate = WSM_TRANSMIT_RATE_6;
-
- frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
- &tim_offset, &tim_len);
- if (!frame.skb)
- return -ENOMEM;
-
- ret = wsm_set_template_frame(priv, &frame);
-
- if (ret)
- goto done;
-
- /* TODO: Distill probe resp; remove TIM
- * and any other beacon-specific IEs
- */
- mgmt = (void *)frame.skb->data;
- mgmt->frame_control =
- __cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_RESP);
-
- frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
- if (priv->vif->p2p) {
- ret = wsm_set_probe_responder(priv, true);
- } else {
- ret = wsm_set_template_frame(priv, &frame);
- wsm_set_probe_responder(priv, false);
- }
-
-done:
- dev_kfree_skb(frame.skb);
-
- return ret;
-}
-
-static int cw1200_upload_pspoll(struct cw1200_common *priv)
-{
- int ret = 0;
- struct wsm_template_frame frame = {
- .frame_type = WSM_FRAME_TYPE_PS_POLL,
- .rate = 0xFF,
- };
-
-
- frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
- if (!frame.skb)
- return -ENOMEM;
-
- ret = wsm_set_template_frame(priv, &frame);
-
- dev_kfree_skb(frame.skb);
-
- return ret;
-}
-
-static int cw1200_upload_null(struct cw1200_common *priv)
-{
- int ret = 0;
- struct wsm_template_frame frame = {
- .frame_type = WSM_FRAME_TYPE_NULL,
- .rate = 0xFF,
- };
-
- frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
- if (!frame.skb)
- return -ENOMEM;
-
- ret = wsm_set_template_frame(priv, &frame);
-
- dev_kfree_skb(frame.skb);
-
- return ret;
-}
-
-static int cw1200_upload_qosnull(struct cw1200_common *priv)
-{
- /* TODO: This needs to be implemented
-
- struct wsm_template_frame frame = {
- .frame_type = WSM_FRAME_TYPE_QOS_NULL,
- .rate = 0xFF,
- };
-
- frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
- if (!frame.skb)
- return -ENOMEM;
-
- ret = wsm_set_template_frame(priv, &frame);
-
- dev_kfree_skb(frame.skb);
-
- */
- return 0;
-}
-
-static int cw1200_enable_beaconing(struct cw1200_common *priv,
- bool enable)
-{
- struct wsm_beacon_transmit transmit = {
- .enable_beaconing = enable,
- };
-
- return wsm_beacon_transmit(priv, &transmit);
-}
-
-static int cw1200_start_ap(struct cw1200_common *priv)
-{
- int ret;
- struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
- struct wsm_start start = {
- .mode = priv->vif->p2p ?
- WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
- .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
- WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
- .channel_number = priv->channel->hw_value,
- .beacon_interval = conf->beacon_int,
- .dtim_period = conf->dtim_period,
- .preamble = conf->use_short_preamble ?
- WSM_JOIN_PREAMBLE_SHORT :
- WSM_JOIN_PREAMBLE_LONG,
- .probe_delay = 100,
- .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
- conf->basic_rates),
- };
- struct wsm_operational_mode mode = {
- .power_mode = cw1200_power_mode,
- .disable_more_flag_usage = true,
- };
-
- memset(start.ssid, 0, sizeof(start.ssid));
- if (!conf->hidden_ssid) {
- start.ssid_len = conf->ssid_len;
- memcpy(start.ssid, conf->ssid, start.ssid_len);
- }
-
- priv->beacon_int = conf->beacon_int;
- priv->join_dtim_period = conf->dtim_period;
-
- memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
-
- pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
- start.channel_number, start.band,
- start.beacon_interval, start.dtim_period,
- start.basic_rate_set,
- start.ssid_len, start.ssid);
- ret = wsm_start(priv, &start);
- if (!ret)
- ret = cw1200_upload_keys(priv);
- if (!ret && priv->vif->p2p) {
- pr_debug("[AP] Setting p2p powersave configuration.\n");
- wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
- }
- if (!ret) {
- wsm_set_block_ack_policy(priv, 0, 0);
- priv->join_status = CW1200_JOIN_STATUS_AP;
- cw1200_update_filtering(priv);
- }
- wsm_set_operational_mode(priv, &mode);
- return ret;
-}
-
-static int cw1200_update_beaconing(struct cw1200_common *priv)
-{
- struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
- struct wsm_reset reset = {
- .link_id = 0,
- .reset_statistics = true,
- };
-
- if (priv->mode == NL80211_IFTYPE_AP) {
- /* TODO: check if changed channel, band */
- if (priv->join_status != CW1200_JOIN_STATUS_AP ||
- priv->beacon_int != conf->beacon_int) {
- pr_debug("ap restarting\n");
- wsm_lock_tx(priv);
- if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
- wsm_reset(priv, &reset);
- priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
- cw1200_start_ap(priv);
- wsm_unlock_tx(priv);
- } else
- pr_debug("ap started join_status: %d\n",
- priv->join_status);
- }
- return 0;
-}
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
deleted file mode 100644
index 50033aa7c7d5..000000000000
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ /dev/null
@@ -1,708 +0,0 @@
-#define PRISM2_PCCARD
-
-#include <linux/module.h>
-#include <linux/if.h>
-#include <linux/slab.h>
-#include <linux/wait.h>
-#include <linux/timer.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/workqueue.h>
-#include <linux/wireless.h>
-#include <net/iw_handler.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-
-#include <asm/io.h>
-
-#include "hostap_wlan.h"
-
-
-static char *dev_info = "hostap_cs";
-
-MODULE_AUTHOR("Jouni Malinen");
-MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
- "cards (PC Card).");
-MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)");
-MODULE_LICENSE("GPL");
-
-
-static int ignore_cis_vcc;
-module_param(ignore_cis_vcc, int, 0444);
-MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
-
-
-/* struct local_info::hw_priv */
-struct hostap_cs_priv {
- struct pcmcia_device *link;
- int sandisk_connectplus;
-};
-
-
-#ifdef PRISM2_IO_DEBUG
-
-static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
-{
- struct hostap_interface *iface;
- local_info_t *local;
- unsigned long flags;
-
- iface = netdev_priv(dev);
- local = iface->local;
- spin_lock_irqsave(&local->lock, flags);
- prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
- outb(v, dev->base_addr + a);
- spin_unlock_irqrestore(&local->lock, flags);
-}
-
-static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
-{
- struct hostap_interface *iface;
- local_info_t *local;
- unsigned long flags;
- u8 v;
-
- iface = netdev_priv(dev);
- local = iface->local;
- spin_lock_irqsave(&local->lock, flags);
- v = inb(dev->base_addr + a);
- prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
- spin_unlock_irqrestore(&local->lock, flags);
- return v;
-}
-
-static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
-{
- struct hostap_interface *iface;
- local_info_t *local;
- unsigned long flags;
-
- iface = netdev_priv(dev);
- local = iface->local;
- spin_lock_irqsave(&local->lock, flags);
- prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
- outw(v, dev->base_addr + a);
- spin_unlock_irqrestore(&local->lock, flags);
-}
-
-static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
-{
- struct hostap_interface *iface;
- local_info_t *local;
- unsigned long flags;
- u16 v;
-
- iface = netdev_priv(dev);
- local = iface->local;
- spin_lock_irqsave(&local->lock, flags);
- v = inw(dev->base_addr + a);
- prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
- spin_unlock_irqrestore(&local->lock, flags);
- return v;
-}
-
-static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
- u8 *buf, int wc)
-{
- struct hostap_interface *iface;
- local_info_t *local;
- unsigned long flags;
-
- iface = netdev_priv(dev);
- local = iface->local;
- spin_lock_irqsave(&local->lock, flags);
- prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
- outsw(dev->base_addr + a, buf, wc);
- spin_unlock_irqrestore(&local->lock, flags);
-}
-
-static inline void hfa384x_insw_debug(struct net_device *dev, int a,
- u8 *buf, int wc)
-{
- struct hostap_interface *iface;
- local_info_t *local;
- unsigned long flags;
-
- iface = netdev_priv(dev);
- local = iface->local;
- spin_lock_irqsave(&local->lock, flags);
- prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
- insw(dev->base_addr + a, buf, wc);
- spin_unlock_irqrestore(&local->lock, flags);
-}
-
-#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
-#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
-#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
-#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
-#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
-#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
-
-#else /* PRISM2_IO_DEBUG */
-
-#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
-#define HFA384X_INB(a) inb(dev->base_addr + (a))
-#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
-#define HFA384X_INW(a) inw(dev->base_addr + (a))
-#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
-#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
-
-#endif /* PRISM2_IO_DEBUG */
-
-
-static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
- int len)
-{
- u16 d_off;
- u16 *pos;
-
- d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
- pos = (u16 *) buf;
-
- if (len / 2)
- HFA384X_INSW(d_off, buf, len / 2);
- pos += len / 2;
-
- if (len & 1)
- *((char *) pos) = HFA384X_INB(d_off);
-
- return 0;
-}
-
-
-static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
-{
- u16 d_off;
- u16 *pos;
-
- d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
- pos = (u16 *) buf;
-
- if (len / 2)
- HFA384X_OUTSW(d_off, buf, len / 2);
- pos += len / 2;
-
- if (len & 1)
- HFA384X_OUTB(*((char *) pos), d_off);
-
- return 0;
-}
-
-
-/* FIX: This might change at some point.. */
-#include "hostap_hw.c"
-
-
-
-static void prism2_detach(struct pcmcia_device *p_dev);
-static void prism2_release(u_long arg);
-static int prism2_config(struct pcmcia_device *link);
-
-
-static int prism2_pccard_card_present(local_info_t *local)
-{
- struct hostap_cs_priv *hw_priv = local->hw_priv;
- if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link))
- return 1;
- return 0;
-}
-
-
-/*
- * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0
- * Document No. 20-10-00058, January 2004
- * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf
- */
-#define SANDISK_WLAN_ACTIVATION_OFF 0x40
-#define SANDISK_HCR_OFF 0x42
-
-
-static void sandisk_set_iobase(local_info_t *local)
-{
- int res;
- struct hostap_cs_priv *hw_priv = local->hw_priv;
-
- res = pcmcia_write_config_byte(hw_priv->link, 0x10,
- hw_priv->link->resource[0]->start & 0x00ff);
- if (res != 0) {
- printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
- " res=%d\n", res);
- }
- udelay(10);
-
- res = pcmcia_write_config_byte(hw_priv->link, 0x12,
- (hw_priv->link->resource[0]->start >> 8) & 0x00ff);
- if (res != 0) {
- printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
- " res=%d\n", res);
- }
-}
-
-
-static void sandisk_write_hcr(local_info_t *local, int hcr)
-{
- struct net_device *dev = local->dev;
- int i;
-
- HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF);
- udelay(50);
- for (i = 0; i < 10; i++) {
- HFA384X_OUTB(hcr, SANDISK_HCR_OFF);
- }
- udelay(55);
- HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF);
-}
-
-
-static int sandisk_enable_wireless(struct net_device *dev)
-{
- int res, ret = 0;
- struct hostap_interface *iface = netdev_priv(dev);
- local_info_t *local = iface->local;
- struct hostap_cs_priv *hw_priv = local->hw_priv;
-
- if (resource_size(hw_priv->link->resource[0]) < 0x42) {
- /* Not enough ports to be SanDisk multi-function card */
- ret = -ENODEV;
- goto done;
- }
-
- if (hw_priv->link->manf_id != 0xd601 || hw_priv->link->card_id != 0x0101) {
- /* No SanDisk manfid found */
- ret = -ENODEV;
- goto done;
- }
-
- if (hw_priv->link->socket->functions < 2) {
- /* No multi-function links found */
- ret = -ENODEV;
- goto done;
- }
-
- printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected"
- " - using vendor-specific initialization\n", dev->name);
- hw_priv->sandisk_connectplus = 1;
-
- res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
- COR_SOFT_RESET);
- if (res != 0) {
- printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
- dev->name, res);
- goto done;
- }
- mdelay(5);
-
- /*
- * Do not enable interrupts here to avoid some bogus events. Interrupts
- * will be enabled during the first cor_sreset call.
- */
- res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
- (COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE |
- COR_FUNC_ENA));
- if (res != 0) {
- printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
- dev->name, res);
- goto done;
- }
- mdelay(5);
-
- sandisk_set_iobase(local);
-
- HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF);
- udelay(10);
- HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF);
- udelay(10);
-
-done:
- return ret;
-}
-
-
-static void prism2_pccard_cor_sreset(local_info_t *local)
-{
- int res;
- u8 val;
- struct hostap_cs_priv *hw_priv = local->hw_priv;
-
- if (!prism2_pccard_card_present(local))
- return;
-
- res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &val);
- if (res != 0) {
- printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
- res);
- return;
- }
- printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n",
- val);
-
- val |= COR_SOFT_RESET;
- res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val);
- if (res != 0) {
- printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
- res);
- return;
- }
-
- mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
-
- val &= ~COR_SOFT_RESET;
- if (hw_priv->sandisk_connectplus)
- val |= COR_IREQ_ENA;
- res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val);
- if (res != 0) {
- printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
- res);
- return;
- }
-
- mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
-
- if (hw_priv->sandisk_connectplus)
- sandisk_set_iobase(local);
-}
-
-
-static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
-{
- int res;
- u8 old_cor;
- struct hostap_cs_priv *hw_priv = local->hw_priv;
-
- if (!prism2_pccard_card_present(local))
- return;
-
- if (hw_priv->sandisk_connectplus) {
- sandisk_write_hcr(local, hcr);
- return;
- }
-
- res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &old_cor);
- if (res != 0) {
- printk(KERN_DEBUG "%s failed 1 (%d)\n", __func__, res);
- return;
- }
- printk(KERN_DEBUG "%s: original COR %02x\n", __func__, old_cor);
-
- res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
- old_cor | COR_SOFT_RESET);
- if (res != 0) {
- printk(KERN_DEBUG "%s failed 2 (%d)\n", __func__, res);
- return;
- }
-
- mdelay(10);
-
- /* Setup Genesis mode */
- res = pcmcia_write_config_byte(hw_priv->link, CISREG_CCSR, hcr);
- if (res != 0) {
- printk(KERN_DEBUG "%s failed 3 (%d)\n", __func__, res);
- return;
- }
- mdelay(10);
-
- res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
- old_cor & ~COR_SOFT_RESET);
- if (res != 0) {
- printk(KERN_DEBUG "%s failed 4 (%d)\n", __func__, res);
- return;
- }
-
- mdelay(10);
-}
-
-
-static struct prism2_helper_functions prism2_pccard_funcs =
-{
- .card_present = prism2_pccard_card_present,
- .cor_sreset = prism2_pccard_cor_sreset,
- .genesis_reset = prism2_pccard_genesis_reset,
- .hw_type = HOSTAP_HW_PCCARD,
-};
-
-
-/* allocate local data and register with CardServices
- * initialize dev_link structure, but do not configure the card yet */
-static int hostap_cs_probe(struct pcmcia_device *p_dev)
-{
- int ret;
-
- PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
-
- ret = prism2_config(p_dev);
- if (ret) {
- PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
- }
-
- return ret;
-}
-
-
-static void prism2_detach(struct pcmcia_device *link)
-{
- PDEBUG(DEBUG_FLOW, "prism2_detach\n");
-
- prism2_release((u_long)link);
-
- /* release net devices */
- if (link->priv) {
- struct hostap_cs_priv *hw_priv;
- struct net_device *dev;
- struct hostap_interface *iface;
- dev = link->priv;
- iface = netdev_priv(dev);
- hw_priv = iface->local->hw_priv;
- prism2_free_local_data(dev);
- kfree(hw_priv);
- }
-}
-
-
-static int prism2_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- if (p_dev->config_index == 0)
- return -EINVAL;
-
- return pcmcia_request_io(p_dev);
-}
-
-static int prism2_config(struct pcmcia_device *link)
-{
- struct net_device *dev;
- struct hostap_interface *iface;
- local_info_t *local;
- int ret = 1;
- struct hostap_cs_priv *hw_priv;
- unsigned long flags;
-
- PDEBUG(DEBUG_FLOW, "prism2_config()\n");
-
- hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
- if (hw_priv == NULL) {
- ret = -ENOMEM;
- goto failed;
- }
-
- /* Look for an appropriate configuration table entry in the CIS */
- link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO |
- CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
- if (ignore_cis_vcc)
- link->config_flags &= ~CONF_AUTO_CHECK_VCC;
- ret = pcmcia_loop_config(link, prism2_config_check, NULL);
- if (ret) {
- if (!ignore_cis_vcc)
- printk(KERN_ERR "GetNextTuple(): No matching "
- "CIS configuration. Maybe you need the "
- "ignore_cis_vcc=1 parameter.\n");
- goto failed;
- }
-
- /* Need to allocate net_device before requesting IRQ handler */
- dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
- &link->dev);
- if (dev == NULL)
- goto failed;
- link->priv = dev;
-
- iface = netdev_priv(dev);
- local = iface->local;
- local->hw_priv = hw_priv;
- hw_priv->link = link;
-
- /*
- * We enable IRQ here, but IRQ handler will not proceed
- * until dev->base_addr is set below. This protect us from
- * receive interrupts when driver is not initialized.
- */
- ret = pcmcia_request_irq(link, prism2_interrupt);
- if (ret)
- goto failed;
-
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- spin_lock_irqsave(&local->irq_init_lock, flags);
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
- spin_unlock_irqrestore(&local->irq_init_lock, flags);
-
- local->shutdown = 0;
-
- sandisk_enable_wireless(dev);
-
- ret = prism2_hw_config(dev, 1);
- if (!ret)
- ret = hostap_hw_ready(dev);
-
- return ret;
-
- failed:
- kfree(hw_priv);
- prism2_release((u_long)link);
- return ret;
-}
-
-
-static void prism2_release(u_long arg)
-{
- struct pcmcia_device *link = (struct pcmcia_device *)arg;
-
- PDEBUG(DEBUG_FLOW, "prism2_release\n");
-
- if (link->priv) {
- struct net_device *dev = link->priv;
- struct hostap_interface *iface;
-
- iface = netdev_priv(dev);
- prism2_hw_shutdown(dev, 0);
- iface->local->shutdown = 1;
- }
-
- pcmcia_disable_device(link);
- PDEBUG(DEBUG_FLOW, "release - done\n");
-}
-
-static int hostap_cs_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = (struct net_device *) link->priv;
- int dev_open = 0;
- struct hostap_interface *iface = NULL;
-
- if (!dev)
- return -ENODEV;
-
- iface = netdev_priv(dev);
-
- PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
- if (iface && iface->local)
- dev_open = iface->local->num_dev_open > 0;
- if (dev_open) {
- netif_stop_queue(dev);
- netif_device_detach(dev);
- }
- prism2_suspend(dev);
-
- return 0;
-}
-
-static int hostap_cs_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = (struct net_device *) link->priv;
- int dev_open = 0;
- struct hostap_interface *iface = NULL;
-
- if (!dev)
- return -ENODEV;
-
- iface = netdev_priv(dev);
-
- PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
-
- if (iface && iface->local)
- dev_open = iface->local->num_dev_open > 0;
-
- prism2_hw_shutdown(dev, 1);
- prism2_hw_config(dev, dev_open ? 0 : 1);
- if (dev_open) {
- netif_device_attach(dev);
- netif_start_queue(dev);
- }
-
- return 0;
-}
-
-static const struct pcmcia_device_id hostap_cs_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100),
- PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300),
- PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777),
- PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000),
- PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002),
- PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3301),
- PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b),
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612),
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613),
- PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002),
- PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002),
- PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001),
- PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001),
- PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300),
-/* PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000), conflict with pcnet_cs */
- PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002),
- PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002),
- PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005),
- PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010),
- PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002),
- PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF",
- 0x2d858104),
- PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL",
- 0x74c5e40d),
- PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil",
- 0x4b801a17),
- PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.02",
- 0x4b74baa0),
- PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus",
- 0x7a954bd9, 0x74be00c6),
- PCMCIA_DEVICE_PROD_ID123(
- "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02",
- 0xe6ec52ce, 0x08649af2, 0x4b74baa0),
- PCMCIA_DEVICE_PROD_ID123(
- "Canon", "Wireless LAN CF Card K30225", "Version 01.00",
- 0x96ef6fe2, 0x263fcbab, 0xa57adb8c),
- PCMCIA_DEVICE_PROD_ID123(
- "D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02",
- 0x71b18589, 0xb6f1b0ab, 0x4b74baa0),
- PCMCIA_DEVICE_PROD_ID123(
- "Instant Wireless ", " Network PC CARD", "Version 01.02",
- 0x11d901af, 0x6e9bd926, 0x4b74baa0),
- PCMCIA_DEVICE_PROD_ID123(
- "SMC", "SMC2632W", "Version 01.02",
- 0xc4f8b18b, 0x474a1f2a, 0x4b74baa0),
- PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G",
- 0x2decece3, 0x82067c18),
- PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card",
- 0x54f7c49c, 0x15a75e5b),
- PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE",
- 0x74c5e40d, 0xdb472a18),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card",
- 0x0733cc81, 0x0c52f395),
- PCMCIA_DEVICE_PROD_ID12(
- "ZoomAir 11Mbps High", "Rate wireless Networking",
- 0x273fe3db, 0x32a1eaee),
- PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card",
- 0xa37434e9, 0x9762e8f1),
- PCMCIA_DEVICE_PROD_ID123(
- "Pretec", "CompactWLAN Card 802.11b", "2.5",
- 0x1cadd3e5, 0xe697636c, 0x7a5bfcf1),
- PCMCIA_DEVICE_PROD_ID123(
- "U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02",
- 0xc7b8df9d, 0x1700d087, 0x4b74baa0),
- PCMCIA_DEVICE_PROD_ID123(
- "Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio",
- "Ver. 1.00",
- 0x5cd01705, 0x4271660f, 0x9d08ee12),
- PCMCIA_DEVICE_PROD_ID123(
- "Wireless LAN" , "11Mbps PC Card", "Version 01.02",
- 0x4b8870ff, 0x70e946d1, 0x4b74baa0),
- PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092),
- PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2),
- PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b),
- PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39),
- PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
-
-
-static struct pcmcia_driver hostap_driver = {
- .name = "hostap_cs",
- .probe = hostap_cs_probe,
- .remove = prism2_detach,
- .owner = THIS_MODULE,
- .id_table = hostap_cs_ids,
- .suspend = hostap_cs_suspend,
- .resume = hostap_cs_resume,
-};
-module_pcmcia_driver(hostap_driver);
diff --git a/drivers/net/wireless/intel/Kconfig b/drivers/net/wireless/intel/Kconfig
new file mode 100644
index 000000000000..5b14f2f64a8a
--- /dev/null
+++ b/drivers/net/wireless/intel/Kconfig
@@ -0,0 +1,18 @@
+config WLAN_VENDOR_INTEL
+ bool "Intel devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_INTEL
+
+source "drivers/net/wireless/intel/ipw2x00/Kconfig"
+source "drivers/net/wireless/intel/iwlegacy/Kconfig"
+source "drivers/net/wireless/intel/iwlwifi/Kconfig"
+
+endif # WLAN_VENDOR_INTEL
diff --git a/drivers/net/wireless/intel/Makefile b/drivers/net/wireless/intel/Makefile
new file mode 100644
index 000000000000..c9cbcc85b569
--- /dev/null
+++ b/drivers/net/wireless/intel/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_IPW2100) += ipw2x00/
+obj-$(CONFIG_IPW2200) += ipw2x00/
+
+obj-$(CONFIG_IWLEGACY) += iwlegacy/
+
+obj-$(CONFIG_IWLWIFI) += iwlwifi/
diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/intel/ipw2x00/Kconfig
index d6ec44d7a391..d6ec44d7a391 100644
--- a/drivers/net/wireless/ipw2x00/Kconfig
+++ b/drivers/net/wireless/intel/ipw2x00/Kconfig
diff --git a/drivers/net/wireless/ipw2x00/Makefile b/drivers/net/wireless/intel/ipw2x00/Makefile
index aecd2cff462b..aecd2cff462b 100644
--- a/drivers/net/wireless/ipw2x00/Makefile
+++ b/drivers/net/wireless/intel/ipw2x00/Makefile
diff --git a/drivers/net/wireless/ipw2x00/ipw.h b/drivers/net/wireless/intel/ipw2x00/ipw.h
index 4007bf5ed6f3..4007bf5ed6f3 100644
--- a/drivers/net/wireless/ipw2x00/ipw.h
+++ b/drivers/net/wireless/intel/ipw2x00/ipw.h
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
new file mode 100644
index 000000000000..f93a7f71c047
--- /dev/null
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -0,0 +1,8646 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ Intel Linux Wireless <ilw@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+ Portions of this file are based on the sample_* files provided by Wireless
+ Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes
+ <jt@hpl.hp.com>
+
+ Portions of this file are based on the Host AP project,
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <j@w1.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+
+ Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
+ ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
+ available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox
+
+******************************************************************************/
+/*
+
+ Initial driver on which this is based was developed by Janusz Gorycki,
+ Maciej Urbaniak, and Maciej Sosnowski.
+
+ Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak.
+
+Theory of Operation
+
+Tx - Commands and Data
+
+Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs)
+Each TBD contains a pointer to the physical (dma_addr_t) address of data being
+sent to the firmware as well as the length of the data.
+
+The host writes to the TBD queue at the WRITE index. The WRITE index points
+to the _next_ packet to be written and is advanced when after the TBD has been
+filled.
+
+The firmware pulls from the TBD queue at the READ index. The READ index points
+to the currently being read entry, and is advanced once the firmware is
+done with a packet.
+
+When data is sent to the firmware, the first TBD is used to indicate to the
+firmware if a Command or Data is being sent. If it is Command, all of the
+command information is contained within the physical address referred to by the
+TBD. If it is Data, the first TBD indicates the type of data packet, number
+of fragments, etc. The next TBD then refers to the actual packet location.
+
+The Tx flow cycle is as follows:
+
+1) ipw2100_tx() is called by kernel with SKB to transmit
+2) Packet is move from the tx_free_list and appended to the transmit pending
+ list (tx_pend_list)
+3) work is scheduled to move pending packets into the shared circular queue.
+4) when placing packet in the circular queue, the incoming SKB is DMA mapped
+ to a physical address. That address is entered into a TBD. Two TBDs are
+ filled out. The first indicating a data packet, the second referring to the
+ actual payload data.
+5) the packet is removed from tx_pend_list and placed on the end of the
+ firmware pending list (fw_pend_list)
+6) firmware is notified that the WRITE index has
+7) Once the firmware has processed the TBD, INTA is triggered.
+8) For each Tx interrupt received from the firmware, the READ index is checked
+ to see which TBDs are done being processed.
+9) For each TBD that has been processed, the ISR pulls the oldest packet
+ from the fw_pend_list.
+10)The packet structure contained in the fw_pend_list is then used
+ to unmap the DMA address and to free the SKB originally passed to the driver
+ from the kernel.
+11)The packet structure is placed onto the tx_free_list
+
+The above steps are the same for commands, only the msg_free_list/msg_pend_list
+are used instead of tx_free_list/tx_pend_list
+
+...
+
+Critical Sections / Locking :
+
+There are two locks utilized. The first is the low level lock (priv->low_lock)
+that protects the following:
+
+- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows:
+
+ tx_free_list : Holds pre-allocated Tx buffers.
+ TAIL modified in __ipw2100_tx_process()
+ HEAD modified in ipw2100_tx()
+
+ tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring
+ TAIL modified ipw2100_tx()
+ HEAD modified by ipw2100_tx_send_data()
+
+ msg_free_list : Holds pre-allocated Msg (Command) buffers
+ TAIL modified in __ipw2100_tx_process()
+ HEAD modified in ipw2100_hw_send_command()
+
+ msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring
+ TAIL modified in ipw2100_hw_send_command()
+ HEAD modified in ipw2100_tx_send_commands()
+
+ The flow of data on the TX side is as follows:
+
+ MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST
+ TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST
+
+ The methods that work on the TBD ring are protected via priv->low_lock.
+
+- The internal data state of the device itself
+- Access to the firmware read/write indexes for the BD queues
+ and associated logic
+
+All external entry functions are locked with the priv->action_lock to ensure
+that only one external action is invoked at a time.
+
+
+*/
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <linux/stringify.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/firmware.h>
+#include <linux/acpi.h>
+#include <linux/ctype.h>
+#include <linux/pm_qos.h>
+
+#include <net/lib80211.h>
+
+#include "ipw2100.h"
+#include "ipw.h"
+
+#define IPW2100_VERSION "git-1.2.2"
+
+#define DRV_NAME "ipw2100"
+#define DRV_VERSION IPW2100_VERSION
+#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
+#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
+
+static struct pm_qos_request ipw2100_pm_qos_req;
+
+/* Debugging stuff */
+#ifdef CONFIG_IPW2100_DEBUG
+#define IPW2100_RX_DEBUG /* Reception debugging */
+#endif
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+static int network_mode = 0;
+static int channel = 0;
+static int associate = 0;
+static int disable = 0;
+#ifdef CONFIG_PM
+static struct ipw2100_fw ipw2100_firmware;
+#endif
+
+#include <linux/moduleparam.h>
+module_param(debug, int, 0444);
+module_param_named(mode, network_mode, int, 0444);
+module_param(channel, int, 0444);
+module_param(associate, int, 0444);
+module_param(disable, int, 0444);
+
+MODULE_PARM_DESC(debug, "debug level");
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
+MODULE_PARM_DESC(channel, "channel");
+MODULE_PARM_DESC(associate, "auto associate when scanning (default off)");
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
+
+static u32 ipw2100_debug_level = IPW_DL_NONE;
+
+#ifdef CONFIG_IPW2100_DEBUG
+#define IPW_DEBUG(level, message...) \
+do { \
+ if (ipw2100_debug_level & (level)) { \
+ printk(KERN_DEBUG "ipw2100: %c %s ", \
+ in_interrupt() ? 'I' : 'U', __func__); \
+ printk(message); \
+ } \
+} while (0)
+#else
+#define IPW_DEBUG(level, message...) do {} while (0)
+#endif /* CONFIG_IPW2100_DEBUG */
+
+#ifdef CONFIG_IPW2100_DEBUG
+static const char *command_types[] = {
+ "undefined",
+ "unused", /* HOST_ATTENTION */
+ "HOST_COMPLETE",
+ "unused", /* SLEEP */
+ "unused", /* HOST_POWER_DOWN */
+ "unused",
+ "SYSTEM_CONFIG",
+ "unused", /* SET_IMR */
+ "SSID",
+ "MANDATORY_BSSID",
+ "AUTHENTICATION_TYPE",
+ "ADAPTER_ADDRESS",
+ "PORT_TYPE",
+ "INTERNATIONAL_MODE",
+ "CHANNEL",
+ "RTS_THRESHOLD",
+ "FRAG_THRESHOLD",
+ "POWER_MODE",
+ "TX_RATES",
+ "BASIC_TX_RATES",
+ "WEP_KEY_INFO",
+ "unused",
+ "unused",
+ "unused",
+ "unused",
+ "WEP_KEY_INDEX",
+ "WEP_FLAGS",
+ "ADD_MULTICAST",
+ "CLEAR_ALL_MULTICAST",
+ "BEACON_INTERVAL",
+ "ATIM_WINDOW",
+ "CLEAR_STATISTICS",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "TX_POWER_INDEX",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "BROADCAST_SCAN",
+ "CARD_DISABLE",
+ "PREFERRED_BSSID",
+ "SET_SCAN_OPTIONS",
+ "SCAN_DWELL_TIME",
+ "SWEEP_TABLE",
+ "AP_OR_STATION_TABLE",
+ "GROUP_ORDINALS",
+ "SHORT_RETRY_LIMIT",
+ "LONG_RETRY_LIMIT",
+ "unused", /* SAVE_CALIBRATION */
+ "unused", /* RESTORE_CALIBRATION */
+ "undefined",
+ "undefined",
+ "undefined",
+ "HOST_PRE_POWER_DOWN",
+ "unused", /* HOST_INTERRUPT_COALESCING */
+ "undefined",
+ "CARD_DISABLE_PHY_OFF",
+ "MSDU_TX_RATES",
+ "undefined",
+ "SET_STATION_STAT_BITS",
+ "CLEAR_STATIONS_STAT_BITS",
+ "LEAP_ROGUE_MODE",
+ "SET_SECURITY_INFORMATION",
+ "DISASSOCIATION_BSSID",
+ "SET_WPA_ASS_IE"
+};
+#endif
+
+static const long ipw2100_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+
+#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies)
+
+static struct ieee80211_rate ipw2100_bg_rates[] = {
+ { .bitrate = 10 },
+ { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+};
+
+#define RATE_COUNT ARRAY_SIZE(ipw2100_bg_rates)
+
+/* Pre-decl until we get the code solid and then we can clean it up */
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv);
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv);
+
+static void ipw2100_queues_initialize(struct ipw2100_priv *priv);
+static void ipw2100_queues_free(struct ipw2100_priv *priv);
+static int ipw2100_queues_allocate(struct ipw2100_priv *priv);
+
+static int ipw2100_fw_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+ size_t max);
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+ size_t max);
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static void ipw2100_wx_event_work(struct work_struct *work);
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev);
+static struct iw_handler_def ipw2100_wx_handler_def;
+
+static inline void read_register(struct net_device *dev, u32 reg, u32 * val)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ *val = ioread32(priv->ioaddr + reg);
+ IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val);
+}
+
+static inline void write_register(struct net_device *dev, u32 reg, u32 val)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ iowrite32(val, priv->ioaddr + reg);
+ IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val);
+}
+
+static inline void read_register_word(struct net_device *dev, u32 reg,
+ u16 * val)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ *val = ioread16(priv->ioaddr + reg);
+ IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val);
+}
+
+static inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ *val = ioread8(priv->ioaddr + reg);
+ IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val);
+}
+
+static inline void write_register_word(struct net_device *dev, u32 reg, u16 val)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ iowrite16(val, priv->ioaddr + reg);
+ IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val);
+}
+
+static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ iowrite8(val, priv->ioaddr + reg);
+ IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val);
+}
+
+static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr)
+{
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+}
+
+static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val)
+{
+ write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val);
+}
+
+static void write_nic_memory(struct net_device *dev, u32 addr, u32 len,
+ const u8 * buf)
+{
+ u32 aligned_addr;
+ u32 aligned_len;
+ u32 dif_len;
+ u32 i;
+
+ /* read first nibble byte by byte */
+ aligned_addr = addr & (~0x3);
+ dif_len = addr - aligned_addr;
+ if (dif_len) {
+ /* Start reading at aligned_addr + dif_len */
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ write_register_byte(dev,
+ IPW_REG_INDIRECT_ACCESS_DATA + i,
+ *buf);
+
+ len -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* read DWs through autoincrement registers */
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
+ aligned_len = len & (~0x3);
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf);
+
+ /* copy the last nibble */
+ dif_len = len - aligned_len;
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i,
+ *buf);
+}
+
+static void read_nic_memory(struct net_device *dev, u32 addr, u32 len,
+ u8 * buf)
+{
+ u32 aligned_addr;
+ u32 aligned_len;
+ u32 dif_len;
+ u32 i;
+
+ /* read first nibble byte by byte */
+ aligned_addr = addr & (~0x3);
+ dif_len = addr - aligned_addr;
+ if (dif_len) {
+ /* Start reading at aligned_addr + dif_len */
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ read_register_byte(dev,
+ IPW_REG_INDIRECT_ACCESS_DATA + i,
+ buf);
+
+ len -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* read DWs through autoincrement registers */
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
+ aligned_len = len & (~0x3);
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf);
+
+ /* copy the last nibble */
+ dif_len = len - aligned_len;
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf);
+}
+
+static bool ipw2100_hw_is_adapter_in_system(struct net_device *dev)
+{
+ u32 dbg;
+
+ read_register(dev, IPW_REG_DOA_DEBUG_AREA_START, &dbg);
+
+ return dbg == IPW_DATA_DOA_DEBUG_VALUE;
+}
+
+static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
+ void *val, u32 * len)
+{
+ struct ipw2100_ordinals *ordinals = &priv->ordinals;
+ u32 addr;
+ u32 field_info;
+ u16 field_len;
+ u16 field_count;
+ u32 total_length;
+
+ if (ordinals->table1_addr == 0) {
+ printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals "
+ "before they have been loaded.\n");
+ return -EINVAL;
+ }
+
+ if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+ if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) {
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ printk(KERN_WARNING DRV_NAME
+ ": ordinal buffer length too small, need %zd\n",
+ IPW_ORD_TAB_1_ENTRY_SIZE);
+
+ return -EINVAL;
+ }
+
+ read_nic_dword(priv->net_dev,
+ ordinals->table1_addr + (ord << 2), &addr);
+ read_nic_dword(priv->net_dev, addr, val);
+
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ return 0;
+ }
+
+ if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) {
+
+ ord -= IPW_START_ORD_TAB_2;
+
+ /* get the address of statistic */
+ read_nic_dword(priv->net_dev,
+ ordinals->table2_addr + (ord << 3), &addr);
+
+ /* get the second DW of statistics ;
+ * two 16-bit words - first is length, second is count */
+ read_nic_dword(priv->net_dev,
+ ordinals->table2_addr + (ord << 3) + sizeof(u32),
+ &field_info);
+
+ /* get each entry length */
+ field_len = *((u16 *) & field_info);
+
+ /* get number of entries */
+ field_count = *(((u16 *) & field_info) + 1);
+
+ /* abort if no enough memory */
+ total_length = field_len * field_count;
+ if (total_length > *len) {
+ *len = total_length;
+ return -EINVAL;
+ }
+
+ *len = total_length;
+ if (!total_length)
+ return 0;
+
+ /* read the ordinal data from the SRAM */
+ read_nic_memory(priv->net_dev, addr, total_length, val);
+
+ return 0;
+ }
+
+ printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor "
+ "in table 2\n", ord);
+
+ return -EINVAL;
+}
+
+static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val,
+ u32 * len)
+{
+ struct ipw2100_ordinals *ordinals = &priv->ordinals;
+ u32 addr;
+
+ if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+ if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) {
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+ IPW_DEBUG_INFO("wrong size\n");
+ return -EINVAL;
+ }
+
+ read_nic_dword(priv->net_dev,
+ ordinals->table1_addr + (ord << 2), &addr);
+
+ write_nic_dword(priv->net_dev, addr, *val);
+
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("wrong table\n");
+ if (IS_ORDINAL_TABLE_TWO(ordinals, ord))
+ return -EINVAL;
+
+ return -EINVAL;
+}
+
+static char *snprint_line(char *buf, size_t count,
+ const u8 * data, u32 len, u32 ofs)
+{
+ int out, i, j, l;
+ char c;
+
+ out = snprintf(buf, count, "%08X", ofs);
+
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++)
+ out += snprintf(buf + out, count - out, "%02X ",
+ data[(i * 8 + j)]);
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ out += snprintf(buf + out, count - out, " ");
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++) {
+ c = data[(i * 8 + j)];
+ if (!isascii(c) || !isprint(c))
+ c = '.';
+
+ out += snprintf(buf + out, count - out, "%c", c);
+ }
+
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ return buf;
+}
+
+static void printk_buf(int level, const u8 * data, u32 len)
+{
+ char line[81];
+ u32 ofs = 0;
+ if (!(ipw2100_debug_level & level))
+ return;
+
+ while (len) {
+ printk(KERN_DEBUG "%s\n",
+ snprint_line(line, sizeof(line), &data[ofs],
+ min(len, 16U), ofs));
+ ofs += 16;
+ len -= min(len, 16U);
+ }
+}
+
+#define MAX_RESET_BACKOFF 10
+
+static void schedule_reset(struct ipw2100_priv *priv)
+{
+ unsigned long now = get_seconds();
+
+ /* If we haven't received a reset request within the backoff period,
+ * then we can reset the backoff interval so this reset occurs
+ * immediately */
+ if (priv->reset_backoff &&
+ (now - priv->last_reset > priv->reset_backoff))
+ priv->reset_backoff = 0;
+
+ priv->last_reset = get_seconds();
+
+ if (!(priv->status & STATUS_RESET_PENDING)) {
+ IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n",
+ priv->net_dev->name, priv->reset_backoff);
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+ priv->status |= STATUS_RESET_PENDING;
+ if (priv->reset_backoff)
+ schedule_delayed_work(&priv->reset_work,
+ priv->reset_backoff * HZ);
+ else
+ schedule_delayed_work(&priv->reset_work, 0);
+
+ if (priv->reset_backoff < MAX_RESET_BACKOFF)
+ priv->reset_backoff++;
+
+ wake_up_interruptible(&priv->wait_command_queue);
+ } else
+ IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n",
+ priv->net_dev->name);
+
+}
+
+#define HOST_COMPLETE_TIMEOUT (2 * HZ)
+static int ipw2100_hw_send_command(struct ipw2100_priv *priv,
+ struct host_command *cmd)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ unsigned long flags;
+ int err = 0;
+
+ IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
+ command_types[cmd->host_command], cmd->host_command,
+ cmd->host_command_length);
+ printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters,
+ cmd->host_command_length);
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->fatal_error) {
+ IPW_DEBUG_INFO
+ ("Attempt to send command while hardware in fatal error condition.\n");
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ if (!(priv->status & STATUS_RUNNING)) {
+ IPW_DEBUG_INFO
+ ("Attempt to send command while hardware is not running.\n");
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ if (priv->status & STATUS_CMD_ACTIVE) {
+ IPW_DEBUG_INFO
+ ("Attempt to send command while another command is pending.\n");
+ err = -EBUSY;
+ goto fail_unlock;
+ }
+
+ if (list_empty(&priv->msg_free_list)) {
+ IPW_DEBUG_INFO("no available msg buffers\n");
+ goto fail_unlock;
+ }
+
+ priv->status |= STATUS_CMD_ACTIVE;
+ priv->messages_sent++;
+
+ element = priv->msg_free_list.next;
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+ packet->jiffy_start = jiffies;
+
+ /* initialize the firmware command packet */
+ packet->info.c_struct.cmd->host_command_reg = cmd->host_command;
+ packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1;
+ packet->info.c_struct.cmd->host_command_len_reg =
+ cmd->host_command_length;
+ packet->info.c_struct.cmd->sequence = cmd->host_command_sequence;
+
+ memcpy(packet->info.c_struct.cmd->host_command_params_reg,
+ cmd->host_command_parameters,
+ sizeof(packet->info.c_struct.cmd->host_command_params_reg));
+
+ list_del(element);
+ DEC_STAT(&priv->msg_free_stat);
+
+ list_add_tail(element, &priv->msg_pend_list);
+ INC_STAT(&priv->msg_pend_stat);
+
+ ipw2100_tx_send_commands(priv);
+ ipw2100_tx_send_data(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ /*
+ * We must wait for this command to complete before another
+ * command can be sent... but if we wait more than 3 seconds
+ * then there is a problem.
+ */
+
+ err =
+ wait_event_interruptible_timeout(priv->wait_command_queue,
+ !(priv->
+ status & STATUS_CMD_ACTIVE),
+ HOST_COMPLETE_TIMEOUT);
+
+ if (err == 0) {
+ IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
+ 1000 * (HOST_COMPLETE_TIMEOUT / HZ));
+ priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT;
+ priv->status &= ~STATUS_CMD_ACTIVE;
+ schedule_reset(priv);
+ return -EIO;
+ }
+
+ if (priv->fatal_error) {
+ printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* !!!!! HACK TEST !!!!!
+ * When lots of debug trace statements are enabled, the driver
+ * doesn't seem to have as many firmware restart cycles...
+ *
+ * As a test, we're sticking in a 1/100s delay here */
+ schedule_timeout_uninterruptible(msecs_to_jiffies(10));
+
+ return 0;
+
+ fail_unlock:
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ return err;
+}
+
+/*
+ * Verify the values and data access of the hardware
+ * No locks needed or used. No functions called.
+ */
+static int ipw2100_verify(struct ipw2100_priv *priv)
+{
+ u32 data1, data2;
+ u32 address;
+
+ u32 val1 = 0x76543210;
+ u32 val2 = 0xFEDCBA98;
+
+ /* Domain 0 check - all values should be DOA_DEBUG */
+ for (address = IPW_REG_DOA_DEBUG_AREA_START;
+ address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) {
+ read_register(priv->net_dev, address, &data1);
+ if (data1 != IPW_DATA_DOA_DEBUG_VALUE)
+ return -EIO;
+ }
+
+ /* Domain 1 check - use arbitrary read/write compare */
+ for (address = 0; address < 5; address++) {
+ /* The memory area is not used now */
+ write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+ val1);
+ write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+ val2);
+ read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+ &data1);
+ read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+ &data2);
+ if (val1 == data1 && val2 == data2)
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/*
+ *
+ * Loop until the CARD_DISABLED bit is the same value as the
+ * supplied parameter
+ *
+ * TODO: See if it would be more efficient to do a wait/wake
+ * cycle and have the completion event trigger the wakeup
+ *
+ */
+#define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli
+static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state)
+{
+ int i;
+ u32 card_state;
+ u32 len = sizeof(card_state);
+ int err;
+
+ for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) {
+ err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED,
+ &card_state, &len);
+ if (err) {
+ IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal "
+ "failed.\n");
+ return 0;
+ }
+
+ /* We'll break out if either the HW state says it is
+ * in the state we want, or if HOST_COMPLETE command
+ * finishes */
+ if ((card_state == state) ||
+ ((priv->status & STATUS_ENABLED) ?
+ IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) {
+ if (state == IPW_HW_STATE_ENABLED)
+ priv->status |= STATUS_ENABLED;
+ else
+ priv->status &= ~STATUS_ENABLED;
+
+ return 0;
+ }
+
+ udelay(50);
+ }
+
+ IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n",
+ state ? "DISABLED" : "ENABLED");
+ return -EIO;
+}
+
+/*********************************************************************
+ Procedure : sw_reset_and_clock
+ Purpose : Asserts s/w reset, asserts clock initialization
+ and waits for clock stabilization
+ ********************************************************************/
+static int sw_reset_and_clock(struct ipw2100_priv *priv)
+{
+ int i;
+ u32 r;
+
+ // assert s/w reset
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ // wait for clock stabilization
+ for (i = 0; i < 1000; i++) {
+ udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY);
+
+ // check clock ready bit
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &r);
+ if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET)
+ break;
+ }
+
+ if (i == 1000)
+ return -EIO; // TODO: better error value
+
+ /* set "initialization complete" bit to move adapter to
+ * D0 state */
+ write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+ IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE);
+
+ /* wait for clock stabilization */
+ for (i = 0; i < 10000; i++) {
+ udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4);
+
+ /* check clock ready bit */
+ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+ if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY)
+ break;
+ }
+
+ if (i == 10000)
+ return -EIO; /* TODO: better error value */
+
+ /* set D0 standby bit */
+ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+ write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+ r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
+
+ return 0;
+}
+
+/*********************************************************************
+ Procedure : ipw2100_download_firmware
+ Purpose : Initiaze adapter after power on.
+ The sequence is:
+ 1. assert s/w reset first!
+ 2. awake clocks & wait for clock stabilization
+ 3. hold ARC (don't ask me why...)
+ 4. load Dino ucode and reset/clock init again
+ 5. zero-out shared mem
+ 6. download f/w
+ *******************************************************************/
+static int ipw2100_download_firmware(struct ipw2100_priv *priv)
+{
+ u32 address;
+ int err;
+
+#ifndef CONFIG_PM
+ /* Fetch the firmware and microcode */
+ struct ipw2100_fw ipw2100_firmware;
+#endif
+
+ if (priv->fatal_error) {
+ IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after "
+ "fatal error %d. Interface must be brought down.\n",
+ priv->net_dev->name, priv->fatal_error);
+ return -EINVAL;
+ }
+#ifdef CONFIG_PM
+ if (!ipw2100_firmware.version) {
+ err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+ priv->net_dev->name, err);
+ priv->fatal_error = IPW2100_ERR_FW_LOAD;
+ goto fail;
+ }
+ }
+#else
+ err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+ priv->net_dev->name, err);
+ priv->fatal_error = IPW2100_ERR_FW_LOAD;
+ goto fail;
+ }
+#endif
+ priv->firmware_version = ipw2100_firmware.version;
+
+ /* s/w reset and clock stabilization */
+ err = sw_reset_and_clock(priv);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ err = ipw2100_verify(priv);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* Hold ARC */
+ write_nic_dword(priv->net_dev,
+ IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000);
+
+ /* allow ARC to run */
+ write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+ /* load microcode */
+ err = ipw2100_ucode_download(priv, &ipw2100_firmware);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* release ARC */
+ write_nic_dword(priv->net_dev,
+ IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000);
+
+ /* s/w reset and clock stabilization (again!!!) */
+ err = sw_reset_and_clock(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: sw_reset_and_clock failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* load f/w */
+ err = ipw2100_fw_download(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+#ifndef CONFIG_PM
+ /*
+ * When the .resume method of the driver is called, the other
+ * part of the system, i.e. the ide driver could still stay in
+ * the suspend stage. This prevents us from loading the firmware
+ * from the disk. --YZ
+ */
+
+ /* free any storage allocated for firmware image */
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+
+ /* zero out Domain 1 area indirectly (Si requirement) */
+ for (address = IPW_HOST_FW_SHARED_AREA0;
+ address < IPW_HOST_FW_SHARED_AREA0_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA1;
+ address < IPW_HOST_FW_SHARED_AREA1_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA2;
+ address < IPW_HOST_FW_SHARED_AREA2_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA3;
+ address < IPW_HOST_FW_SHARED_AREA3_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_INTERRUPT_AREA;
+ address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+
+ return 0;
+
+ fail:
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+ return err;
+}
+
+static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv)
+{
+ if (priv->status & STATUS_INT_ENABLED)
+ return;
+ priv->status |= STATUS_INT_ENABLED;
+ write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK);
+}
+
+static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv)
+{
+ if (!(priv->status & STATUS_INT_ENABLED))
+ return;
+ priv->status &= ~STATUS_INT_ENABLED;
+ write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0);
+}
+
+static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv)
+{
+ struct ipw2100_ordinals *ord = &priv->ordinals;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1,
+ &ord->table1_addr);
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2,
+ &ord->table2_addr);
+
+ read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size);
+ read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size);
+
+ ord->table2_size &= 0x0000FFFF;
+
+ IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size);
+ IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size);
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv)
+{
+ u32 reg = 0;
+ /*
+ * Set GPIO 3 writable by FW; GPIO 1 writable
+ * by driver and enable clock
+ */
+ reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE |
+ IPW_BIT_GPIO_LED_OFF);
+ write_register(priv->net_dev, IPW_REG_GPIO, reg);
+}
+
+static int rf_kill_active(struct ipw2100_priv *priv)
+{
+#define MAX_RF_KILL_CHECKS 5
+#define RF_KILL_CHECK_DELAY 40
+
+ unsigned short value = 0;
+ u32 reg = 0;
+ int i;
+
+ if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
+ priv->status &= ~STATUS_RF_KILL_HW;
+ return 0;
+ }
+
+ for (i = 0; i < MAX_RF_KILL_CHECKS; i++) {
+ udelay(RF_KILL_CHECK_DELAY);
+ read_register(priv->net_dev, IPW_REG_GPIO, &reg);
+ value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
+ }
+
+ if (value == 0) {
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
+ priv->status |= STATUS_RF_KILL_HW;
+ } else {
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
+ priv->status &= ~STATUS_RF_KILL_HW;
+ }
+
+ return (value == 0);
+}
+
+static int ipw2100_get_hw_features(struct ipw2100_priv *priv)
+{
+ u32 addr, len;
+ u32 val;
+
+ /*
+ * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1
+ */
+ len = sizeof(addr);
+ if (ipw2100_get_ordinal
+ (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return -EIO;
+ }
+
+ IPW_DEBUG_INFO("EEPROM address: %08X\n", addr);
+
+ /*
+ * EEPROM version is the byte at offset 0xfd in firmware
+ * We read 4 bytes, then shift out the byte we actually want */
+ read_nic_dword(priv->net_dev, addr + 0xFC, &val);
+ priv->eeprom_version = (val >> 24) & 0xFF;
+ IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version);
+
+ /*
+ * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware
+ *
+ * notice that the EEPROM bit is reverse polarity, i.e.
+ * bit = 0 signifies HW RF kill switch is supported
+ * bit = 1 signifies HW RF kill switch is NOT supported
+ */
+ read_nic_dword(priv->net_dev, addr + 0x20, &val);
+ if (!((val >> 24) & 0x01))
+ priv->hw_features |= HW_FEATURE_RFKILL;
+
+ IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n",
+ (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not ");
+
+ return 0;
+}
+
+/*
+ * Start firmware execution after power on and intialization
+ * The sequence is:
+ * 1. Release ARC
+ * 2. Wait for f/w initialization completes;
+ */
+static int ipw2100_start_adapter(struct ipw2100_priv *priv)
+{
+ int i;
+ u32 inta, inta_mask, gpio;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->status & STATUS_RUNNING)
+ return 0;
+
+ /*
+ * Initialize the hw - drive adapter to DO state by setting
+ * init_done bit. Wait for clk_ready bit and Download
+ * fw & dino ucode
+ */
+ if (ipw2100_download_firmware(priv)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to power on the adapter.\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* Clear the Tx, Rx and Msg queues and the r/w indexes
+ * in the firmware RBD and TBD ring queue */
+ ipw2100_queues_initialize(priv);
+
+ ipw2100_hw_set_gpio(priv);
+
+ /* TODO -- Look at disabling interrupts here to make sure none
+ * get fired during FW initialization */
+
+ /* Release ARC - clear reset bit */
+ write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+ /* wait for f/w intialization complete */
+ IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n");
+ i = 5000;
+ do {
+ schedule_timeout_uninterruptible(msecs_to_jiffies(40));
+ /* Todo... wait for sync command ... */
+
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+ /* check "init done" bit */
+ if (inta & IPW2100_INTA_FW_INIT_DONE) {
+ /* reset "init done" bit */
+ write_register(priv->net_dev, IPW_REG_INTA,
+ IPW2100_INTA_FW_INIT_DONE);
+ break;
+ }
+
+ /* check error conditions : we check these after the firmware
+ * check so that if there is an error, the interrupt handler
+ * will see it and the adapter will be reset */
+ if (inta &
+ (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) {
+ /* clear error conditions */
+ write_register(priv->net_dev, IPW_REG_INTA,
+ IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR);
+ }
+ } while (--i);
+
+ /* Clear out any pending INTAs since we aren't supposed to have
+ * interrupts enabled at this point... */
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+ read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+ inta &= IPW_INTERRUPT_MASK;
+ /* Clear out any pending interrupts */
+ if (inta & inta_mask)
+ write_register(priv->net_dev, IPW_REG_INTA, inta);
+
+ IPW_DEBUG_FW("f/w initialization complete: %s\n",
+ i ? "SUCCESS" : "FAILED");
+
+ if (!i) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Firmware did not initialize.\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* allow firmware to write to GPIO1 & GPIO3 */
+ read_register(priv->net_dev, IPW_REG_GPIO, &gpio);
+
+ gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK);
+
+ write_register(priv->net_dev, IPW_REG_GPIO, gpio);
+
+ /* Ready to receive commands */
+ priv->status |= STATUS_RUNNING;
+
+ /* The adapter has been reset; we are not associated */
+ priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv)
+{
+ if (!priv->fatal_error)
+ return;
+
+ priv->fatal_errors[priv->fatal_index++] = priv->fatal_error;
+ priv->fatal_index %= IPW2100_ERROR_QUEUE;
+ priv->fatal_error = 0;
+}
+
+/* NOTE: Our interrupt is disabled when this method is called */
+static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv)
+{
+ u32 reg;
+ int i;
+
+ IPW_DEBUG_INFO("Power cycling the hardware.\n");
+
+ ipw2100_hw_set_gpio(priv);
+
+ /* Step 1. Stop Master Assert */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+ /* Step 2. Wait for stop Master Assert
+ * (not more than 50us, otherwise ret error */
+ i = 5;
+ do {
+ udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ } while (--i);
+
+ priv->status &= ~STATUS_RESET_PENDING;
+
+ if (!i) {
+ IPW_DEBUG_INFO
+ ("exit - waited too long for master assert stop\n");
+ return -EIO;
+ }
+
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ /* Reset any fatal_error conditions */
+ ipw2100_reset_fatalerror(priv);
+
+ /* At this point, the adapter is now stopped and disabled */
+ priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING |
+ STATUS_ASSOCIATED | STATUS_ENABLED);
+
+ return 0;
+}
+
+/*
+ * Send the CARD_DISABLE_PHY_OFF command to the card to disable it
+ *
+ * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent.
+ *
+ * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of
+ * if STATUS_ASSN_LOST is sent.
+ */
+static int ipw2100_hw_phy_off(struct ipw2100_priv *priv)
+{
+
+#define HW_PHY_OFF_LOOP_DELAY (msecs_to_jiffies(50))
+
+ struct host_command cmd = {
+ .host_command = CARD_DISABLE_PHY_OFF,
+ .host_command_sequence = 0,
+ .host_command_length = 0,
+ };
+ int err, i;
+ u32 val1, val2;
+
+ IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n");
+
+ /* Turn off the radio */
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ for (i = 0; i < 2500; i++) {
+ read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1);
+ read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2);
+
+ if ((val1 & IPW2100_CONTROL_PHY_OFF) &&
+ (val2 & IPW2100_COMMAND_PHY_OFF))
+ return 0;
+
+ schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY);
+ }
+
+ return -EIO;
+}
+
+static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = HOST_COMPLETE,
+ .host_command_sequence = 0,
+ .host_command_length = 0
+ };
+ int err = 0;
+
+ IPW_DEBUG_HC("HOST_COMPLETE\n");
+
+ if (priv->status & STATUS_ENABLED)
+ return 0;
+
+ mutex_lock(&priv->adapter_mutex);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED);
+ if (err) {
+ IPW_DEBUG_INFO("%s: card not responding to init command.\n",
+ priv->net_dev->name);
+ goto fail_up;
+ }
+
+ if (priv->stop_hang_check) {
+ priv->stop_hang_check = 0;
+ schedule_delayed_work(&priv->hang_check, HZ / 2);
+ }
+
+ fail_up:
+ mutex_unlock(&priv->adapter_mutex);
+ return err;
+}
+
+static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
+{
+#define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100))
+
+ struct host_command cmd = {
+ .host_command = HOST_PRE_POWER_DOWN,
+ .host_command_sequence = 0,
+ .host_command_length = 0,
+ };
+ int err, i;
+ u32 reg;
+
+ if (!(priv->status & STATUS_RUNNING))
+ return 0;
+
+ priv->status |= STATUS_STOPPING;
+
+ /* We can only shut down the card if the firmware is operational. So,
+ * if we haven't reset since a fatal_error, then we can not send the
+ * shutdown commands. */
+ if (!priv->fatal_error) {
+ /* First, make sure the adapter is enabled so that the PHY_OFF
+ * command can shut it down */
+ ipw2100_enable_adapter(priv);
+
+ err = ipw2100_hw_phy_off(priv);
+ if (err)
+ printk(KERN_WARNING DRV_NAME
+ ": Error disabling radio %d\n", err);
+
+ /*
+ * If in D0-standby mode going directly to D3 may cause a
+ * PCI bus violation. Therefore we must change out of the D0
+ * state.
+ *
+ * Sending the PREPARE_FOR_POWER_DOWN will restrict the
+ * hardware from going into standby mode and will transition
+ * out of D0-standby if it is already in that state.
+ *
+ * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
+ * driver upon completion. Once received, the driver can
+ * proceed to the D3 state.
+ *
+ * Prepare for power down command to fw. This command would
+ * take HW out of D0-standby and prepare it for D3 state.
+ *
+ * Currently FW does not support event notification for this
+ * event. Therefore, skip waiting for it. Just wait a fixed
+ * 100ms
+ */
+ IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n");
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Power down command failed: Error %d\n",
+ priv->net_dev->name, err);
+ else
+ schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY);
+ }
+
+ priv->status &= ~STATUS_ENABLED;
+
+ /*
+ * Set GPIO 3 writable by FW; GPIO 1 writable
+ * by driver and enable clock
+ */
+ ipw2100_hw_set_gpio(priv);
+
+ /*
+ * Power down adapter. Sequence:
+ * 1. Stop master assert (RESET_REG[9]=1)
+ * 2. Wait for stop master (RESET_REG[8]==1)
+ * 3. S/w reset assert (RESET_REG[7] = 1)
+ */
+
+ /* Stop master assert */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+ /* wait stop master not more than 50 usec.
+ * Otherwise return error. */
+ for (i = 5; i > 0; i--) {
+ udelay(10);
+
+ /* Check master stop bit */
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ }
+
+ if (i == 0)
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Could now power down adapter.\n",
+ priv->net_dev->name);
+
+ /* assert s/w reset */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING);
+
+ return 0;
+}
+
+static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = CARD_DISABLE,
+ .host_command_sequence = 0,
+ .host_command_length = 0
+ };
+ int err = 0;
+
+ IPW_DEBUG_HC("CARD_DISABLE\n");
+
+ if (!(priv->status & STATUS_ENABLED))
+ return 0;
+
+ /* Make sure we clear the associated state */
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+ if (!priv->stop_hang_check) {
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+ }
+
+ mutex_lock(&priv->adapter_mutex);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ ": exit - failed to send CARD_DISABLE command\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ ": exit - card failed to change to DISABLED\n");
+ goto fail_up;
+ }
+
+ IPW_DEBUG_INFO("TODO: implement scan state machine\n");
+
+ fail_up:
+ mutex_unlock(&priv->adapter_mutex);
+ return err;
+}
+
+static int ipw2100_set_scan_options(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = SET_SCAN_OPTIONS,
+ .host_command_sequence = 0,
+ .host_command_length = 8
+ };
+ int err;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ IPW_DEBUG_SCAN("setting scan options\n");
+
+ cmd.host_command_parameters[0] = 0;
+
+ if (!(priv->config & CFG_ASSOCIATE))
+ cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE;
+ if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled)
+ cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL;
+ if (priv->config & CFG_PASSIVE_SCAN)
+ cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE;
+
+ cmd.host_command_parameters[1] = priv->channel_mask;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n",
+ cmd.host_command_parameters[0]);
+
+ return err;
+}
+
+static int ipw2100_start_scan(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = BROADCAST_SCAN,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ IPW_DEBUG_HC("START_SCAN\n");
+
+ cmd.host_command_parameters[0] = 0;
+
+ /* No scanning if in monitor mode */
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ return 1;
+
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG_SCAN("Scan requested while already in scan...\n");
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("enter\n");
+
+ /* Not clearing here; doing so makes iwlist always return nothing...
+ *
+ * We should modify the table logic to use aging tables vs. clearing
+ * the table on each scan start.
+ */
+ IPW_DEBUG_SCAN("starting scan\n");
+
+ priv->status |= STATUS_SCANNING;
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ priv->status &= ~STATUS_SCANNING;
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return err;
+}
+
+static const struct libipw_geo ipw_geos[] = {
+ { /* Restricted */
+ "---",
+ .bg_channels = 14,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}, {2484, 14}},
+ },
+};
+
+static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
+{
+ unsigned long flags;
+ int rc = 0;
+ u32 lock;
+ u32 ord_len = sizeof(lock);
+
+ /* Age scan list entries found before suspend */
+ if (priv->suspend_time) {
+ libipw_networks_age(priv->ieee, priv->suspend_time);
+ priv->suspend_time = 0;
+ }
+
+ /* Quiet if manually disabled. */
+ if (priv->status & STATUS_RF_KILL_SW) {
+ IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable "
+ "switch\n", priv->net_dev->name);
+ return 0;
+ }
+
+ /* the ipw2100 hardware really doesn't want power management delays
+ * longer than 175usec
+ */
+ pm_qos_update_request(&ipw2100_pm_qos_req, 175);
+
+ /* If the interrupt is enabled, turn it off... */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+
+ /* Reset any fatal_error conditions */
+ ipw2100_reset_fatalerror(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ if (priv->status & STATUS_POWERED ||
+ (priv->status & STATUS_RESET_PENDING)) {
+ /* Power cycle the card ... */
+ if (ipw2100_power_cycle_adapter(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Could not cycle adapter.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+ } else
+ priv->status |= STATUS_POWERED;
+
+ /* Load the firmware, start the clocks, etc. */
+ if (ipw2100_start_adapter(priv)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to start the firmware.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ ipw2100_initialize_ordinals(priv);
+
+ /* Determine capabilities of this particular HW configuration */
+ if (ipw2100_get_hw_features(priv)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to determine HW features.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ /* Initialize the geo */
+ libipw_set_geo(priv->ieee, &ipw_geos[0]);
+ priv->ieee->freq_band = LIBIPW_24GHZ_BAND;
+
+ lock = LOCK_NONE;
+ if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to clear ordinal lock.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ priv->status &= ~STATUS_SCANNING;
+
+ if (rf_kill_active(priv)) {
+ printk(KERN_INFO "%s: Radio is disabled by RF switch.\n",
+ priv->net_dev->name);
+
+ if (priv->stop_rf_kill) {
+ priv->stop_rf_kill = 0;
+ schedule_delayed_work(&priv->rf_kill,
+ round_jiffies_relative(HZ));
+ }
+
+ deferred = 1;
+ }
+
+ /* Turn on the interrupt so that commands can be processed */
+ ipw2100_enable_interrupts(priv);
+
+ /* Send all of the commands that must be sent prior to
+ * HOST_COMPLETE */
+ if (ipw2100_adapter_setup(priv)) {
+ printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ if (!deferred) {
+ /* Enable the adapter - sends HOST_COMPLETE */
+ if (ipw2100_enable_adapter(priv)) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: failed in call to enable adapter.\n",
+ priv->net_dev->name);
+ ipw2100_hw_stop_adapter(priv);
+ rc = 1;
+ goto exit;
+ }
+
+ /* Start a scan . . . */
+ ipw2100_set_scan_options(priv);
+ ipw2100_start_scan(priv);
+ }
+
+ exit:
+ return rc;
+}
+
+static void ipw2100_down(struct ipw2100_priv *priv)
+{
+ unsigned long flags;
+ union iwreq_data wrqu = {
+ .ap_addr = {
+ .sa_family = ARPHRD_ETHER}
+ };
+ int associated = priv->status & STATUS_ASSOCIATED;
+
+ /* Kill the RF switch timer */
+ if (!priv->stop_rf_kill) {
+ priv->stop_rf_kill = 1;
+ cancel_delayed_work(&priv->rf_kill);
+ }
+
+ /* Kill the firmware hang check timer */
+ if (!priv->stop_hang_check) {
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+ }
+
+ /* Kill any pending resets */
+ if (priv->status & STATUS_RESET_PENDING)
+ cancel_delayed_work(&priv->reset_work);
+
+ /* Make sure the interrupt is on so that FW commands will be
+ * processed correctly */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_enable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ if (ipw2100_hw_stop_adapter(priv))
+ printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n",
+ priv->net_dev->name);
+
+ /* Do not disable the interrupt until _after_ we disable
+ * the adaptor. Otherwise the CARD_DISABLE command will never
+ * be ack'd by the firmware */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ pm_qos_update_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE);
+
+ /* We have to signal any supplicant if we are disassociating */
+ if (associated)
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+}
+
+static int ipw2100_wdev_init(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
+ struct wireless_dev *wdev = &priv->ieee->wdev;
+ int i;
+
+ memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
+
+ /* fill-out priv->ieee->bg_band */
+ if (geo->bg_channels) {
+ struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band;
+
+ bg_band->band = IEEE80211_BAND_2GHZ;
+ bg_band->n_channels = geo->bg_channels;
+ bg_band->channels = kcalloc(geo->bg_channels,
+ sizeof(struct ieee80211_channel),
+ GFP_KERNEL);
+ if (!bg_band->channels) {
+ ipw2100_down(priv);
+ return -ENOMEM;
+ }
+ /* translate geo->bg to bg_band.channels */
+ for (i = 0; i < geo->bg_channels; i++) {
+ bg_band->channels[i].band = IEEE80211_BAND_2GHZ;
+ bg_band->channels[i].center_freq = geo->bg[i].freq;
+ bg_band->channels[i].hw_value = geo->bg[i].channel;
+ bg_band->channels[i].max_power = geo->bg[i].max_power;
+ if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_NO_IR;
+ if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_NO_IR;
+ if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_RADAR;
+ /* No equivalent for LIBIPW_CH_80211H_RULES,
+ LIBIPW_CH_UNIFORM_SPREADING, or
+ LIBIPW_CH_B_ONLY... */
+ }
+ /* point at bitrate info */
+ bg_band->bitrates = ipw2100_bg_rates;
+ bg_band->n_bitrates = RATE_COUNT;
+
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band;
+ }
+
+ wdev->wiphy->cipher_suites = ipw_cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites);
+
+ set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
+ if (wiphy_register(wdev->wiphy))
+ return -EIO;
+ return 0;
+}
+
+static void ipw2100_reset_adapter(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, reset_work.work);
+ unsigned long flags;
+ union iwreq_data wrqu = {
+ .ap_addr = {
+ .sa_family = ARPHRD_ETHER}
+ };
+ int associated = priv->status & STATUS_ASSOCIATED;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name);
+ priv->resets++;
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+ priv->status |= STATUS_SECURITY_UPDATED;
+
+ /* Force a power cycle even if interface hasn't been opened
+ * yet */
+ cancel_delayed_work(&priv->reset_work);
+ priv->status |= STATUS_RESET_PENDING;
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ mutex_lock(&priv->action_mutex);
+ /* stop timed checks so that they don't interfere with reset */
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+
+ /* We have to signal any supplicant if we are disassociating */
+ if (associated)
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+ ipw2100_up(priv, 0);
+ mutex_unlock(&priv->action_mutex);
+
+}
+
+static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
+{
+
+#define MAC_ASSOCIATION_READ_DELAY (HZ)
+ int ret;
+ unsigned int len, essid_len;
+ char essid[IW_ESSID_MAX_SIZE];
+ u32 txrate;
+ u32 chan;
+ char *txratename;
+ u8 bssid[ETH_ALEN];
+
+ /*
+ * TBD: BSSID is usually 00:00:00:00:00:00 here and not
+ * an actual MAC of the AP. Seems like FW sets this
+ * address too late. Read it later and expose through
+ * /proc or schedule a later task to query and update
+ */
+
+ essid_len = IW_ESSID_MAX_SIZE;
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID,
+ essid, &essid_len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+
+ len = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+
+ len = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+ len = ETH_ALEN;
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid,
+ &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+ memcpy(priv->ieee->bssid, bssid, ETH_ALEN);
+
+ switch (txrate) {
+ case TX_RATE_1_MBIT:
+ txratename = "1Mbps";
+ break;
+ case TX_RATE_2_MBIT:
+ txratename = "2Mbsp";
+ break;
+ case TX_RATE_5_5_MBIT:
+ txratename = "5.5Mbps";
+ break;
+ case TX_RATE_11_MBIT:
+ txratename = "11Mbps";
+ break;
+ default:
+ IPW_DEBUG_INFO("Unknown rate: %d\n", txrate);
+ txratename = "unknown rate";
+ break;
+ }
+
+ IPW_DEBUG_INFO("%s: Associated with '%*pE' at %s, channel %d (BSSID=%pM)\n",
+ priv->net_dev->name, essid_len, essid,
+ txratename, chan, bssid);
+
+ /* now we copy read ssid into dev */
+ if (!(priv->config & CFG_STATIC_ESSID)) {
+ priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE);
+ memcpy(priv->essid, essid, priv->essid_len);
+ }
+ priv->channel = chan;
+ memcpy(priv->bssid, bssid, ETH_ALEN);
+
+ priv->status |= STATUS_ASSOCIATING;
+ priv->connect_start = get_seconds();
+
+ schedule_delayed_work(&priv->wx_event_work, HZ / 10);
+}
+
+static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid,
+ int length, int batch_mode)
+{
+ int ssid_len = min(length, IW_ESSID_MAX_SIZE);
+ struct host_command cmd = {
+ .host_command = SSID,
+ .host_command_sequence = 0,
+ .host_command_length = ssid_len
+ };
+ int err;
+
+ IPW_DEBUG_HC("SSID: '%*pE'\n", ssid_len, essid);
+
+ if (ssid_len)
+ memcpy(cmd.host_command_parameters, essid, ssid_len);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to
+ * disable auto association -- so we cheat by setting a bogus SSID */
+ if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) {
+ int i;
+ u8 *bogus = (u8 *) cmd.host_command_parameters;
+ for (i = 0; i < IW_ESSID_MAX_SIZE; i++)
+ bogus[i] = 0x18 + i;
+ cmd.host_command_length = IW_ESSID_MAX_SIZE;
+ }
+
+ /* NOTE: We always send the SSID command even if the provided ESSID is
+ * the same as what we currently think is set. */
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (!err) {
+ memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len);
+ memcpy(priv->essid, essid, ssid_len);
+ priv->essid_len = ssid_len;
+ }
+
+ if (!batch_mode) {
+ if (ipw2100_enable_adapter(priv))
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "disassociated: '%*pE' %pM\n", priv->essid_len, priv->essid,
+ priv->bssid);
+
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+ if (priv->status & STATUS_STOPPING) {
+ IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n");
+ return;
+ }
+
+ eth_zero_addr(priv->bssid);
+ eth_zero_addr(priv->ieee->bssid);
+
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+
+ if (!(priv->status & STATUS_RUNNING))
+ return;
+
+ if (priv->status & STATUS_SECURITY_UPDATED)
+ schedule_delayed_work(&priv->security_work, 0);
+
+ schedule_delayed_work(&priv->wx_event_work, 0);
+}
+
+static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n",
+ priv->net_dev->name);
+
+ /* RF_KILL is now enabled (else we wouldn't be here) */
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
+ priv->status |= STATUS_RF_KILL_HW;
+
+ /* Make sure the RF Kill check timer is running */
+ priv->stop_rf_kill = 0;
+ mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ));
+}
+
+static void ipw2100_scan_event(struct work_struct *work)
+{
+ struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv,
+ scan_event.work);
+ union iwreq_data wrqu;
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+static void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_SCAN("scan complete\n");
+ /* Age the scan results... */
+ priv->ieee->scans++;
+ priv->status &= ~STATUS_SCANNING;
+
+ /* Only userspace-requested scan completion events go out immediately */
+ if (!priv->user_requested_scan) {
+ schedule_delayed_work(&priv->scan_event,
+ round_jiffies_relative(msecs_to_jiffies(4000)));
+ } else {
+ priv->user_requested_scan = 0;
+ mod_delayed_work(system_wq, &priv->scan_event, 0);
+ }
+}
+
+#ifdef CONFIG_IPW2100_DEBUG
+#define IPW2100_HANDLER(v, f) { v, f, # v }
+struct ipw2100_status_indicator {
+ int status;
+ void (*cb) (struct ipw2100_priv * priv, u32 status);
+ char *name;
+};
+#else
+#define IPW2100_HANDLER(v, f) { v, f }
+struct ipw2100_status_indicator {
+ int status;
+ void (*cb) (struct ipw2100_priv * priv, u32 status);
+};
+#endif /* CONFIG_IPW2100_DEBUG */
+
+static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_SCAN("Scanning...\n");
+ priv->status |= STATUS_SCANNING;
+}
+
+static const struct ipw2100_status_indicator status_handlers[] = {
+ IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL),
+ IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL),
+ IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated),
+ IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost),
+ IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL),
+ IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete),
+ IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL),
+ IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL),
+ IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill),
+ IPW2100_HANDLER(IPW_STATE_DISABLED, NULL),
+ IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL),
+ IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning),
+ IPW2100_HANDLER(-1, NULL)
+};
+
+static void isr_status_change(struct ipw2100_priv *priv, int status)
+{
+ int i;
+
+ if (status == IPW_STATE_SCANNING &&
+ priv->status & STATUS_ASSOCIATED &&
+ !(priv->status & STATUS_SCANNING)) {
+ IPW_DEBUG_INFO("Scan detected while associated, with "
+ "no scan request. Restarting firmware.\n");
+
+ /* Wake up any sleeping jobs */
+ schedule_reset(priv);
+ }
+
+ for (i = 0; status_handlers[i].status != -1; i++) {
+ if (status == status_handlers[i].status) {
+ IPW_DEBUG_NOTIF("Status change: %s\n",
+ status_handlers[i].name);
+ if (status_handlers[i].cb)
+ status_handlers[i].cb(priv, status);
+ priv->wstats.status = status;
+ return;
+ }
+ }
+
+ IPW_DEBUG_NOTIF("unknown status received: %04x\n", status);
+}
+
+static void isr_rx_complete_command(struct ipw2100_priv *priv,
+ struct ipw2100_cmd_header *cmd)
+{
+#ifdef CONFIG_IPW2100_DEBUG
+ if (cmd->host_command_reg < ARRAY_SIZE(command_types)) {
+ IPW_DEBUG_HC("Command completed '%s (%d)'\n",
+ command_types[cmd->host_command_reg],
+ cmd->host_command_reg);
+ }
+#endif
+ if (cmd->host_command_reg == HOST_COMPLETE)
+ priv->status |= STATUS_ENABLED;
+
+ if (cmd->host_command_reg == CARD_DISABLE)
+ priv->status &= ~STATUS_ENABLED;
+
+ priv->status &= ~STATUS_CMD_ACTIVE;
+
+ wake_up_interruptible(&priv->wait_command_queue);
+}
+
+#ifdef CONFIG_IPW2100_DEBUG
+static const char *frame_types[] = {
+ "COMMAND_STATUS_VAL",
+ "STATUS_CHANGE_VAL",
+ "P80211_DATA_VAL",
+ "P8023_DATA_VAL",
+ "HOST_NOTIFICATION_VAL"
+};
+#endif
+
+static int ipw2100_alloc_skb(struct ipw2100_priv *priv,
+ struct ipw2100_rx_packet *packet)
+{
+ packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx));
+ if (!packet->skb)
+ return -ENOMEM;
+
+ packet->rxp = (struct ipw2100_rx *)packet->skb->data;
+ packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pci_dev, packet->dma_addr)) {
+ dev_kfree_skb(packet->skb);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+#define SEARCH_ERROR 0xffffffff
+#define SEARCH_FAIL 0xfffffffe
+#define SEARCH_SUCCESS 0xfffffff0
+#define SEARCH_DISCARD 0
+#define SEARCH_SNAPSHOT 1
+
+#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff))
+static void ipw2100_snapshot_free(struct ipw2100_priv *priv)
+{
+ int i;
+ if (!priv->snapshot[0])
+ return;
+ for (i = 0; i < 0x30; i++)
+ kfree(priv->snapshot[i]);
+ priv->snapshot[0] = NULL;
+}
+
+#ifdef IPW2100_DEBUG_C3
+static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
+{
+ int i;
+ if (priv->snapshot[0])
+ return 1;
+ for (i = 0; i < 0x30; i++) {
+ priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC);
+ if (!priv->snapshot[i]) {
+ IPW_DEBUG_INFO("%s: Error allocating snapshot "
+ "buffer %d\n", priv->net_dev->name, i);
+ while (i > 0)
+ kfree(priv->snapshot[--i]);
+ priv->snapshot[0] = NULL;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf,
+ size_t len, int mode)
+{
+ u32 i, j;
+ u32 tmp;
+ u8 *s, *d;
+ u32 ret;
+
+ s = in_buf;
+ if (mode == SEARCH_SNAPSHOT) {
+ if (!ipw2100_snapshot_alloc(priv))
+ mode = SEARCH_DISCARD;
+ }
+
+ for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) {
+ read_nic_dword(priv->net_dev, i, &tmp);
+ if (mode == SEARCH_SNAPSHOT)
+ *(u32 *) SNAPSHOT_ADDR(i) = tmp;
+ if (ret == SEARCH_FAIL) {
+ d = (u8 *) & tmp;
+ for (j = 0; j < 4; j++) {
+ if (*s != *d) {
+ s = in_buf;
+ continue;
+ }
+
+ s++;
+ d++;
+
+ if ((s - in_buf) == len)
+ ret = (i + j) - len + 1;
+ }
+ } else if (mode == SEARCH_DISCARD)
+ return ret;
+ }
+
+ return ret;
+}
+#endif
+
+/*
+ *
+ * 0) Disconnect the SKB from the firmware (just unmap)
+ * 1) Pack the ETH header into the SKB
+ * 2) Pass the SKB to the network stack
+ *
+ * When packet is provided by the firmware, it contains the following:
+ *
+ * . libipw_hdr
+ * . libipw_snap_hdr
+ *
+ * The size of the constructed ethernet
+ *
+ */
+#ifdef IPW2100_RX_DEBUG
+static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH];
+#endif
+
+static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i)
+{
+#ifdef IPW2100_DEBUG_C3
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ u32 match, reg;
+ int j;
+#endif
+
+ IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n",
+ i * sizeof(struct ipw2100_status));
+
+#ifdef IPW2100_DEBUG_C3
+ /* Halt the firmware so we can get a good image */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+ j = 5;
+ do {
+ udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ } while (j--);
+
+ match = ipw2100_match_buf(priv, (u8 *) status,
+ sizeof(struct ipw2100_status),
+ SEARCH_SNAPSHOT);
+ if (match < SEARCH_SUCCESS)
+ IPW_DEBUG_INFO("%s: DMA status match in Firmware at "
+ "offset 0x%06X, length %d:\n",
+ priv->net_dev->name, match,
+ sizeof(struct ipw2100_status));
+ else
+ IPW_DEBUG_INFO("%s: No DMA status match in "
+ "Firmware.\n", priv->net_dev->name);
+
+ printk_buf((u8 *) priv->status_queue.drv,
+ sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH);
+#endif
+
+ priv->fatal_error = IPW2100_ERR_C3_CORRUPTION;
+ priv->net_dev->stats.rx_errors++;
+ schedule_reset(priv);
+}
+
+static void isr_rx(struct ipw2100_priv *priv, int i,
+ struct libipw_rx_stats *stats)
+{
+ struct net_device *dev = priv->net_dev;
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ IPW_DEBUG_RX("Handler...\n");
+
+ if (unlikely(status->frame_size > skb_tailroom(packet->skb))) {
+ IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+ " Dropping.\n",
+ dev->name,
+ status->frame_size, skb_tailroom(packet->skb));
+ dev->stats.rx_errors++;
+ return;
+ }
+
+ if (unlikely(!netif_running(dev))) {
+ dev->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
+ !(priv->status & STATUS_ASSOCIATED))) {
+ IPW_DEBUG_DROP("Dropping packet while not associated.\n");
+ priv->wstats.discard.misc++;
+ return;
+ }
+
+ pci_unmap_single(priv->pci_dev,
+ packet->dma_addr,
+ sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
+
+ skb_put(packet->skb, status->frame_size);
+
+#ifdef IPW2100_RX_DEBUG
+ /* Make a copy of the frame so we can dump it to the logs if
+ * libipw_rx fails */
+ skb_copy_from_linear_data(packet->skb, packet_data,
+ min_t(u32, status->frame_size,
+ IPW_RX_NIC_BUFFER_LENGTH));
+#endif
+
+ if (!libipw_rx(priv->ieee, packet->skb, stats)) {
+#ifdef IPW2100_RX_DEBUG
+ IPW_DEBUG_DROP("%s: Non consumed packet:\n",
+ dev->name);
+ printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
+#endif
+ dev->stats.rx_errors++;
+
+ /* libipw_rx failed, so it didn't free the SKB */
+ dev_kfree_skb_any(packet->skb);
+ packet->skb = NULL;
+ }
+
+ /* We need to allocate a new SKB and attach it to the RDB. */
+ if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Unable to allocate SKB onto RBD ring - disabling "
+ "adapter.\n", dev->name);
+ /* TODO: schedule adapter shutdown */
+ IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+ }
+
+ /* Update the RDB entry */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+#ifdef CONFIG_IPW2100_MONITOR
+
+static void isr_rx_monitor(struct ipw2100_priv *priv, int i,
+ struct libipw_rx_stats *stats)
+{
+ struct net_device *dev = priv->net_dev;
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ /* Magic struct that slots into the radiotap header -- no reason
+ * to build this manually element by element, we can write it much
+ * more efficiently than we can parse it. ORDER MATTERS HERE */
+ struct ipw_rt_hdr {
+ struct ieee80211_radiotap_header rt_hdr;
+ s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
+ } *ipw_rt;
+
+ IPW_DEBUG_RX("Handler...\n");
+
+ if (unlikely(status->frame_size > skb_tailroom(packet->skb) -
+ sizeof(struct ipw_rt_hdr))) {
+ IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+ " Dropping.\n",
+ dev->name,
+ status->frame_size,
+ skb_tailroom(packet->skb));
+ dev->stats.rx_errors++;
+ return;
+ }
+
+ if (unlikely(!netif_running(dev))) {
+ dev->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ if (unlikely(priv->config & CFG_CRC_CHECK &&
+ status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
+ IPW_DEBUG_RX("CRC error in packet. Dropping.\n");
+ dev->stats.rx_errors++;
+ return;
+ }
+
+ pci_unmap_single(priv->pci_dev, packet->dma_addr,
+ sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
+ memmove(packet->skb->data + sizeof(struct ipw_rt_hdr),
+ packet->skb->data, status->frame_size);
+
+ ipw_rt = (struct ipw_rt_hdr *) packet->skb->data;
+
+ ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+ ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */
+
+ ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+
+ ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM;
+
+ skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr));
+
+ if (!libipw_rx(priv->ieee, packet->skb, stats)) {
+ dev->stats.rx_errors++;
+
+ /* libipw_rx failed, so it didn't free the SKB */
+ dev_kfree_skb_any(packet->skb);
+ packet->skb = NULL;
+ }
+
+ /* We need to allocate a new SKB and attach it to the RDB. */
+ if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+ IPW_DEBUG_WARNING(
+ "%s: Unable to allocate SKB onto RBD ring - disabling "
+ "adapter.\n", dev->name);
+ /* TODO: schedule adapter shutdown */
+ IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+ }
+
+ /* Update the RDB entry */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+#endif
+
+static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
+{
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx *u = priv->rx_buffers[i].rxp;
+ u16 frame_type = status->status_fields & STATUS_TYPE_MASK;
+
+ switch (frame_type) {
+ case COMMAND_STATUS_VAL:
+ return (status->frame_size != sizeof(u->rx_data.command));
+ case STATUS_CHANGE_VAL:
+ return (status->frame_size != sizeof(u->rx_data.status));
+ case HOST_NOTIFICATION_VAL:
+ return (status->frame_size < sizeof(u->rx_data.notification));
+ case P80211_DATA_VAL:
+ case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+ return 0;
+#else
+ switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
+ case IEEE80211_FTYPE_MGMT:
+ case IEEE80211_FTYPE_CTL:
+ return 0;
+ case IEEE80211_FTYPE_DATA:
+ return (status->frame_size >
+ IPW_MAX_802_11_PAYLOAD_LENGTH);
+ }
+#endif
+ }
+
+ return 1;
+}
+
+/*
+ * ipw2100 interrupts are disabled at this point, and the ISR
+ * is the only code that calls this method. So, we do not need
+ * to play with any locks.
+ *
+ * RX Queue works as follows:
+ *
+ * Read index - firmware places packet in entry identified by the
+ * Read index and advances Read index. In this manner,
+ * Read index will always point to the next packet to
+ * be filled--but not yet valid.
+ *
+ * Write index - driver fills this entry with an unused RBD entry.
+ * This entry has not filled by the firmware yet.
+ *
+ * In between the W and R indexes are the RBDs that have been received
+ * but not yet processed.
+ *
+ * The process of handling packets will start at WRITE + 1 and advance
+ * until it reaches the READ index.
+ *
+ * The WRITE index is cached in the variable 'priv->rx_queue.next'.
+ *
+ */
+static void __ipw2100_rx_process(struct ipw2100_priv *priv)
+{
+ struct ipw2100_bd_queue *rxq = &priv->rx_queue;
+ struct ipw2100_status_queue *sq = &priv->status_queue;
+ struct ipw2100_rx_packet *packet;
+ u16 frame_type;
+ u32 r, w, i, s;
+ struct ipw2100_rx *u;
+ struct libipw_rx_stats stats = {
+ .mac_time = jiffies,
+ };
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r);
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w);
+
+ if (r >= rxq->entries) {
+ IPW_DEBUG_RX("exit - bad read index\n");
+ return;
+ }
+
+ i = (rxq->next + 1) % rxq->entries;
+ s = i;
+ while (i != r) {
+ /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n",
+ r, rxq->next, i); */
+
+ packet = &priv->rx_buffers[i];
+
+ /* Sync the DMA for the RX buffer so CPU is sure to get
+ * the correct values */
+ pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+
+ if (unlikely(ipw2100_corruption_check(priv, i))) {
+ ipw2100_corruption_detected(priv, i);
+ goto increment;
+ }
+
+ u = packet->rxp;
+ frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK;
+ stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM;
+ stats.len = sq->drv[i].frame_size;
+
+ stats.mask = 0;
+ if (stats.rssi != 0)
+ stats.mask |= LIBIPW_STATMASK_RSSI;
+ stats.freq = LIBIPW_24GHZ_BAND;
+
+ IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n",
+ priv->net_dev->name, frame_types[frame_type],
+ stats.len);
+
+ switch (frame_type) {
+ case COMMAND_STATUS_VAL:
+ /* Reset Rx watchdog */
+ isr_rx_complete_command(priv, &u->rx_data.command);
+ break;
+
+ case STATUS_CHANGE_VAL:
+ isr_status_change(priv, u->rx_data.status);
+ break;
+
+ case P80211_DATA_VAL:
+ case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ isr_rx_monitor(priv, i, &stats);
+ break;
+ }
+#endif
+ if (stats.len < sizeof(struct libipw_hdr_3addr))
+ break;
+ switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
+ case IEEE80211_FTYPE_MGMT:
+ libipw_rx_mgt(priv->ieee,
+ &u->rx_data.header, &stats);
+ break;
+
+ case IEEE80211_FTYPE_CTL:
+ break;
+
+ case IEEE80211_FTYPE_DATA:
+ isr_rx(priv, i, &stats);
+ break;
+
+ }
+ break;
+ }
+
+ increment:
+ /* clear status field associated with this RBD */
+ rxq->drv[i].status.info.field = 0;
+
+ i = (i + 1) % rxq->entries;
+ }
+
+ if (i != s) {
+ /* backtrack one entry, wrapping to end if at 0 */
+ rxq->next = (i ? i : rxq->entries) - 1;
+
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next);
+ }
+}
+
+/*
+ * __ipw2100_tx_process
+ *
+ * This routine will determine whether the next packet on
+ * the fw_pend_list has been processed by the firmware yet.
+ *
+ * If not, then it does nothing and returns.
+ *
+ * If so, then it removes the item from the fw_pend_list, frees
+ * any associated storage, and places the item back on the
+ * free list of its source (either msg_free_list or tx_free_list)
+ *
+ * TX Queue works as follows:
+ *
+ * Read index - points to the next TBD that the firmware will
+ * process. The firmware will read the data, and once
+ * done processing, it will advance the Read index.
+ *
+ * Write index - driver fills this entry with an constructed TBD
+ * entry. The Write index is not advanced until the
+ * packet has been configured.
+ *
+ * In between the W and R indexes are the TBDs that have NOT been
+ * processed. Lagging behind the R index are packets that have
+ * been processed but have not been freed by the driver.
+ *
+ * In order to free old storage, an internal index will be maintained
+ * that points to the next packet to be freed. When all used
+ * packets have been freed, the oldest index will be the same as the
+ * firmware's read index.
+ *
+ * The OLDEST index is cached in the variable 'priv->tx_queue.oldest'
+ *
+ * Because the TBD structure can not contain arbitrary data, the
+ * driver must keep an internal queue of cached allocations such that
+ * it can put that data back into the tx_free_list and msg_free_list
+ * for use by future command and data packets.
+ *
+ */
+static int __ipw2100_tx_process(struct ipw2100_priv *priv)
+{
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ int descriptors_used;
+ int e, i;
+ u32 r, w, frag_num = 0;
+
+ if (list_empty(&priv->fw_pend_list))
+ return 0;
+
+ element = priv->fw_pend_list.next;
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+ tbd = &txq->drv[packet->index];
+
+ /* Determine how many TBD entries must be finished... */
+ switch (packet->type) {
+ case COMMAND:
+ /* COMMAND uses only one slot; don't advance */
+ descriptors_used = 1;
+ e = txq->oldest;
+ break;
+
+ case DATA:
+ /* DATA uses two slots; advance and loop position. */
+ descriptors_used = tbd->num_fragments;
+ frag_num = tbd->num_fragments - 1;
+ e = txq->oldest + frag_num;
+ e %= txq->entries;
+ break;
+
+ default:
+ printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n",
+ priv->net_dev->name);
+ return 0;
+ }
+
+ /* if the last TBD is not done by NIC yet, then packet is
+ * not ready to be released.
+ *
+ */
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+ &r);
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ &w);
+ if (w != txq->next)
+ printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n",
+ priv->net_dev->name);
+
+ /*
+ * txq->next is the index of the last packet written txq->oldest is
+ * the index of the r is the index of the next packet to be read by
+ * firmware
+ */
+
+ /*
+ * Quick graphic to help you visualize the following
+ * if / else statement
+ *
+ * ===>| s---->|===============
+ * e>|
+ * | a | b | c | d | e | f | g | h | i | j | k | l
+ * r---->|
+ * w
+ *
+ * w - updated by driver
+ * r - updated by firmware
+ * s - start of oldest BD entry (txq->oldest)
+ * e - end of oldest BD entry
+ *
+ */
+ if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) {
+ IPW_DEBUG_TX("exit - no processed packets ready to release.\n");
+ return 0;
+ }
+
+ list_del(element);
+ DEC_STAT(&priv->fw_pend_stat);
+
+#ifdef CONFIG_IPW2100_DEBUG
+ {
+ i = txq->oldest;
+ IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
+ &txq->drv[i],
+ (u32) (txq->nic + i * sizeof(struct ipw2100_bd)),
+ txq->drv[i].host_addr, txq->drv[i].buf_length);
+
+ if (packet->type == DATA) {
+ i = (i + 1) % txq->entries;
+
+ IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
+ &txq->drv[i],
+ (u32) (txq->nic + i *
+ sizeof(struct ipw2100_bd)),
+ (u32) txq->drv[i].host_addr,
+ txq->drv[i].buf_length);
+ }
+ }
+#endif
+
+ switch (packet->type) {
+ case DATA:
+ if (txq->drv[txq->oldest].status.info.fields.txType != 0)
+ printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
+ "Expecting DATA TBD but pulled "
+ "something else: ids %d=%d.\n",
+ priv->net_dev->name, txq->oldest, packet->index);
+
+ /* DATA packet; we have to unmap and free the SKB */
+ for (i = 0; i < frag_num; i++) {
+ tbd = &txq->drv[(packet->index + 1 + i) % txq->entries];
+
+ IPW_DEBUG_TX("TX%d P=%08x L=%d\n",
+ (packet->index + 1 + i) % txq->entries,
+ tbd->host_addr, tbd->buf_length);
+
+ pci_unmap_single(priv->pci_dev,
+ tbd->host_addr,
+ tbd->buf_length, PCI_DMA_TODEVICE);
+ }
+
+ libipw_txb_free(packet->info.d_struct.txb);
+ packet->info.d_struct.txb = NULL;
+
+ list_add_tail(element, &priv->tx_free_list);
+ INC_STAT(&priv->tx_free_stat);
+
+ /* We have a free slot in the Tx queue, so wake up the
+ * transmit layer if it is stopped. */
+ if (priv->status & STATUS_ASSOCIATED)
+ netif_wake_queue(priv->net_dev);
+
+ /* A packet was processed by the hardware, so update the
+ * watchdog */
+ priv->net_dev->trans_start = jiffies;
+
+ break;
+
+ case COMMAND:
+ if (txq->drv[txq->oldest].status.info.fields.txType != 1)
+ printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
+ "Expecting COMMAND TBD but pulled "
+ "something else: ids %d=%d.\n",
+ priv->net_dev->name, txq->oldest, packet->index);
+
+#ifdef CONFIG_IPW2100_DEBUG
+ if (packet->info.c_struct.cmd->host_command_reg <
+ ARRAY_SIZE(command_types))
+ IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n",
+ command_types[packet->info.c_struct.cmd->
+ host_command_reg],
+ packet->info.c_struct.cmd->
+ host_command_reg,
+ packet->info.c_struct.cmd->cmd_status_reg);
+#endif
+
+ list_add_tail(element, &priv->msg_free_list);
+ INC_STAT(&priv->msg_free_stat);
+ break;
+ }
+
+ /* advance oldest used TBD pointer to start of next entry */
+ txq->oldest = (e + 1) % txq->entries;
+ /* increase available TBDs number */
+ txq->available += descriptors_used;
+ SET_STAT(&priv->txq_stat, txq->available);
+
+ IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n",
+ jiffies - packet->jiffy_start);
+
+ return (!list_empty(&priv->fw_pend_list));
+}
+
+static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv)
+{
+ int i = 0;
+
+ while (__ipw2100_tx_process(priv) && i < 200)
+ i++;
+
+ if (i == 200) {
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Driver is running slow (%d iters).\n",
+ priv->net_dev->name, i);
+ }
+}
+
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ int next = txq->next;
+
+ while (!list_empty(&priv->msg_pend_list)) {
+ /* if there isn't enough space in TBD queue, then
+ * don't stuff a new one in.
+ * NOTE: 3 are needed as a command will take one,
+ * and there is a minimum of 2 that must be
+ * maintained between the r and w indexes
+ */
+ if (txq->available <= 3) {
+ IPW_DEBUG_TX("no room in tx_queue\n");
+ break;
+ }
+
+ element = priv->msg_pend_list.next;
+ list_del(element);
+ DEC_STAT(&priv->msg_pend_stat);
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ IPW_DEBUG_TX("using TBD at virt=%p, phys=%04X\n",
+ &txq->drv[txq->next],
+ (u32) (txq->nic + txq->next *
+ sizeof(struct ipw2100_bd)));
+
+ packet->index = txq->next;
+
+ tbd = &txq->drv[txq->next];
+
+ /* initialize TBD */
+ tbd->host_addr = packet->info.c_struct.cmd_phys;
+ tbd->buf_length = sizeof(struct ipw2100_cmd_header);
+ /* not marking number of fragments causes problems
+ * with f/w debug version */
+ tbd->num_fragments = 1;
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_COMMAND |
+ IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+
+ /* update TBD queue counters */
+ txq->next++;
+ txq->next %= txq->entries;
+ txq->available--;
+ DEC_STAT(&priv->txq_stat);
+
+ list_add_tail(element, &priv->fw_pend_list);
+ INC_STAT(&priv->fw_pend_stat);
+ }
+
+ if (txq->next != next) {
+ /* kick off the DMA by notifying firmware the
+ * write index has moved; make sure TBD stores are sync'd */
+ wmb();
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ txq->next);
+ }
+}
+
+/*
+ * ipw2100_tx_send_data
+ *
+ */
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ int next = txq->next;
+ int i = 0;
+ struct ipw2100_data_header *ipw_hdr;
+ struct libipw_hdr_3addr *hdr;
+
+ while (!list_empty(&priv->tx_pend_list)) {
+ /* if there isn't enough space in TBD queue, then
+ * don't stuff a new one in.
+ * NOTE: 4 are needed as a data will take two,
+ * and there is a minimum of 2 that must be
+ * maintained between the r and w indexes
+ */
+ element = priv->tx_pend_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ if (unlikely(1 + packet->info.d_struct.txb->nr_frags >
+ IPW_MAX_BDS)) {
+ /* TODO: Support merging buffers if more than
+ * IPW_MAX_BDS are used */
+ IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded. "
+ "Increase fragmentation level.\n",
+ priv->net_dev->name);
+ }
+
+ if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) {
+ IPW_DEBUG_TX("no room in tx_queue\n");
+ break;
+ }
+
+ list_del(element);
+ DEC_STAT(&priv->tx_pend_stat);
+
+ tbd = &txq->drv[txq->next];
+
+ packet->index = txq->next;
+
+ ipw_hdr = packet->info.d_struct.data;
+ hdr = (struct libipw_hdr_3addr *)packet->info.d_struct.txb->
+ fragments[0]->data;
+
+ if (priv->ieee->iw_mode == IW_MODE_INFRA) {
+ /* To DS: Addr1 = BSSID, Addr2 = SA,
+ Addr3 = DA */
+ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN);
+ } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ /* not From/To DS: Addr1 = DA, Addr2 = SA,
+ Addr3 = BSSID */
+ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN);
+ }
+
+ ipw_hdr->host_command_reg = SEND;
+ ipw_hdr->host_command_reg1 = 0;
+
+ /* For now we only support host based encryption */
+ ipw_hdr->needs_encryption = 0;
+ ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted;
+ if (packet->info.d_struct.txb->nr_frags > 1)
+ ipw_hdr->fragment_size =
+ packet->info.d_struct.txb->frag_size -
+ LIBIPW_3ADDR_LEN;
+ else
+ ipw_hdr->fragment_size = 0;
+
+ tbd->host_addr = packet->info.d_struct.data_phys;
+ tbd->buf_length = sizeof(struct ipw2100_data_header);
+ tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags;
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+ txq->next++;
+ txq->next %= txq->entries;
+
+ IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n",
+ packet->index, tbd->host_addr, tbd->buf_length);
+#ifdef CONFIG_IPW2100_DEBUG
+ if (packet->info.d_struct.txb->nr_frags > 1)
+ IPW_DEBUG_FRAG("fragment Tx: %d frames\n",
+ packet->info.d_struct.txb->nr_frags);
+#endif
+
+ for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) {
+ tbd = &txq->drv[txq->next];
+ if (i == packet->info.d_struct.txb->nr_frags - 1)
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+ else
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+
+ tbd->buf_length = packet->info.d_struct.txb->
+ fragments[i]->len - LIBIPW_3ADDR_LEN;
+
+ tbd->host_addr = pci_map_single(priv->pci_dev,
+ packet->info.d_struct.
+ txb->fragments[i]->
+ data +
+ LIBIPW_3ADDR_LEN,
+ tbd->buf_length,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pci_dev,
+ tbd->host_addr)) {
+ IPW_DEBUG_TX("dma mapping error\n");
+ break;
+ }
+
+ IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n",
+ txq->next, tbd->host_addr,
+ tbd->buf_length);
+
+ pci_dma_sync_single_for_device(priv->pci_dev,
+ tbd->host_addr,
+ tbd->buf_length,
+ PCI_DMA_TODEVICE);
+
+ txq->next++;
+ txq->next %= txq->entries;
+ }
+
+ txq->available -= 1 + packet->info.d_struct.txb->nr_frags;
+ SET_STAT(&priv->txq_stat, txq->available);
+
+ list_add_tail(element, &priv->fw_pend_list);
+ INC_STAT(&priv->fw_pend_stat);
+ }
+
+ if (txq->next != next) {
+ /* kick off the DMA by notifying firmware the
+ * write index has moved; make sure TBD stores are sync'd */
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ txq->next);
+ }
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv)
+{
+ struct net_device *dev = priv->net_dev;
+ unsigned long flags;
+ u32 inta, tmp;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+
+ read_register(dev, IPW_REG_INTA, &inta);
+
+ IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n",
+ (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+ priv->in_isr++;
+ priv->interrupts++;
+
+ /* We do not loop and keep polling for more interrupts as this
+ * is frowned upon and doesn't play nicely with other potentially
+ * chained IRQs */
+ IPW_DEBUG_ISR("INTA: 0x%08lX\n",
+ (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+ if (inta & IPW2100_INTA_FATAL_ERROR) {
+ printk(KERN_WARNING DRV_NAME
+ ": Fatal interrupt. Scheduling firmware restart.\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR);
+
+ read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error);
+ IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n",
+ priv->net_dev->name, priv->fatal_error);
+
+ read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp);
+ IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n",
+ priv->net_dev->name, tmp);
+
+ /* Wake up any sleeping jobs */
+ schedule_reset(priv);
+ }
+
+ if (inta & IPW2100_INTA_PARITY_ERROR) {
+ printk(KERN_ERR DRV_NAME
+ ": ***** PARITY ERROR INTERRUPT !!!!\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR);
+ }
+
+ if (inta & IPW2100_INTA_RX_TRANSFER) {
+ IPW_DEBUG_ISR("RX interrupt\n");
+
+ priv->rx_interrupts++;
+
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER);
+
+ __ipw2100_rx_process(priv);
+ __ipw2100_tx_complete(priv);
+ }
+
+ if (inta & IPW2100_INTA_TX_TRANSFER) {
+ IPW_DEBUG_ISR("TX interrupt\n");
+
+ priv->tx_interrupts++;
+
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER);
+
+ __ipw2100_tx_complete(priv);
+ ipw2100_tx_send_commands(priv);
+ ipw2100_tx_send_data(priv);
+ }
+
+ if (inta & IPW2100_INTA_TX_COMPLETE) {
+ IPW_DEBUG_ISR("TX complete\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE);
+
+ __ipw2100_tx_complete(priv);
+ }
+
+ if (inta & IPW2100_INTA_EVENT_INTERRUPT) {
+ /* ipw2100_handle_event(dev); */
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT);
+ }
+
+ if (inta & IPW2100_INTA_FW_INIT_DONE) {
+ IPW_DEBUG_ISR("FW init done interrupt\n");
+ priv->inta_other++;
+
+ read_register(dev, IPW_REG_INTA, &tmp);
+ if (tmp & (IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR)) {
+ write_register(dev, IPW_REG_INTA,
+ IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR);
+ }
+
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE);
+ }
+
+ if (inta & IPW2100_INTA_STATUS_CHANGE) {
+ IPW_DEBUG_ISR("Status change interrupt\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE);
+ }
+
+ if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) {
+ IPW_DEBUG_ISR("slave host mode interrupt\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA,
+ IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE);
+ }
+
+ priv->in_isr--;
+ ipw2100_enable_interrupts(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ IPW_DEBUG_ISR("exit\n");
+}
+
+static irqreturn_t ipw2100_interrupt(int irq, void *data)
+{
+ struct ipw2100_priv *priv = data;
+ u32 inta, inta_mask;
+
+ if (!data)
+ return IRQ_NONE;
+
+ spin_lock(&priv->low_lock);
+
+ /* We check to see if we should be ignoring interrupts before
+ * we touch the hardware. During ucode load if we try and handle
+ * an interrupt we can cause keyboard problems as well as cause
+ * the ucode to fail to initialize */
+ if (!(priv->status & STATUS_INT_ENABLED)) {
+ /* Shared IRQ */
+ goto none;
+ }
+
+ read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+ if (inta == 0xFFFFFFFF) {
+ /* Hardware disappeared */
+ printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n");
+ goto none;
+ }
+
+ inta &= IPW_INTERRUPT_MASK;
+
+ if (!(inta & inta_mask)) {
+ /* Shared interrupt */
+ goto none;
+ }
+
+ /* We disable the hardware interrupt here just to prevent unneeded
+ * calls to be made. We disable this again within the actual
+ * work tasklet, so if another part of the code re-enables the
+ * interrupt, that is fine */
+ ipw2100_disable_interrupts(priv);
+
+ tasklet_schedule(&priv->irq_tasklet);
+ spin_unlock(&priv->low_lock);
+
+ return IRQ_HANDLED;
+ none:
+ spin_unlock(&priv->low_lock);
+ return IRQ_NONE;
+}
+
+static netdev_tx_t ipw2100_tx(struct libipw_txb *txb,
+ struct net_device *dev, int pri)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_INFO("Can not transmit when not connected.\n");
+ priv->net_dev->stats.tx_carrier_errors++;
+ netif_stop_queue(dev);
+ goto fail_unlock;
+ }
+
+ if (list_empty(&priv->tx_free_list))
+ goto fail_unlock;
+
+ element = priv->tx_free_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ packet->info.d_struct.txb = txb;
+
+ IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len);
+ printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len);
+
+ packet->jiffy_start = jiffies;
+
+ list_del(element);
+ DEC_STAT(&priv->tx_free_stat);
+
+ list_add_tail(element, &priv->tx_pend_list);
+ INC_STAT(&priv->tx_pend_stat);
+
+ ipw2100_tx_send_data(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+ return NETDEV_TX_OK;
+
+fail_unlock:
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+ return NETDEV_TX_BUSY;
+}
+
+static int ipw2100_msg_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+ void *v;
+ dma_addr_t p;
+
+ priv->msg_buffers =
+ kmalloc(IPW_COMMAND_POOL_SIZE * sizeof(struct ipw2100_tx_packet),
+ GFP_KERNEL);
+ if (!priv->msg_buffers)
+ return -ENOMEM;
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+ v = pci_zalloc_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ &p);
+ if (!v) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: PCI alloc failed for msg "
+ "buffers.\n", priv->net_dev->name);
+ err = -ENOMEM;
+ break;
+ }
+
+ priv->msg_buffers[i].type = COMMAND;
+ priv->msg_buffers[i].info.c_struct.cmd =
+ (struct ipw2100_cmd_header *)v;
+ priv->msg_buffers[i].info.c_struct.cmd_phys = p;
+ }
+
+ if (i == IPW_COMMAND_POOL_SIZE)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ priv->msg_buffers[j].info.c_struct.cmd,
+ priv->msg_buffers[j].info.c_struct.
+ cmd_phys);
+ }
+
+ kfree(priv->msg_buffers);
+ priv->msg_buffers = NULL;
+
+ return err;
+}
+
+static int ipw2100_msg_initialize(struct ipw2100_priv *priv)
+{
+ int i;
+
+ INIT_LIST_HEAD(&priv->msg_free_list);
+ INIT_LIST_HEAD(&priv->msg_pend_list);
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++)
+ list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list);
+ SET_STAT(&priv->msg_free_stat, i);
+
+ return 0;
+}
+
+static void ipw2100_msg_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ if (!priv->msg_buffers)
+ return;
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ priv->msg_buffers[i].info.c_struct.cmd,
+ priv->msg_buffers[i].info.c_struct.
+ cmd_phys);
+ }
+
+ kfree(priv->msg_buffers);
+ priv->msg_buffers = NULL;
+}
+
+static ssize_t show_pci(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev);
+ char *out = buf;
+ int i, j;
+ u32 val;
+
+ for (i = 0; i < 16; i++) {
+ out += sprintf(out, "[%08X] ", i * 16);
+ for (j = 0; j < 16; j += 4) {
+ pci_read_config_dword(pci_dev, i * 16 + j, &val);
+ out += sprintf(out, "%08X ", val);
+ }
+ out += sprintf(out, "\n");
+ }
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL);
+
+static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = dev_get_drvdata(d);
+ return sprintf(buf, "0x%08x\n", (int)p->config);
+}
+
+static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+
+static ssize_t show_status(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = dev_get_drvdata(d);
+ return sprintf(buf, "0x%08x\n", (int)p->status);
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_capability(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = dev_get_drvdata(d);
+ return sprintf(buf, "0x%08x\n", (int)p->capability);
+}
+
+static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL);
+
+#define IPW2100_REG(x) { IPW_ ##x, #x }
+static const struct {
+ u32 addr;
+ const char *name;
+} hw_data[] = {
+IPW2100_REG(REG_GP_CNTRL),
+ IPW2100_REG(REG_GPIO),
+ IPW2100_REG(REG_INTA),
+ IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),};
+#define IPW2100_NIC(x, s) { x, #x, s }
+static const struct {
+ u32 addr;
+ const char *name;
+ size_t size;
+} nic_data[] = {
+IPW2100_NIC(IPW2100_CONTROL_REG, 2),
+ IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),};
+#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d }
+static const struct {
+ u8 index;
+ const char *name;
+ const char *desc;
+} ord_data[] = {
+IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_HOST_COMPLETE,
+ "successful Host Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_DIR_DATA,
+ "successful Directed Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_DIR_DATA1,
+ "successful Directed Tx's (MSDU) @ 1MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA2,
+ "successful Directed Tx's (MSDU) @ 2MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA5_5,
+ "successful Directed Tx's (MSDU) @ 5_5MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA11,
+ "successful Directed Tx's (MSDU) @ 11MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA1,
+ "successful Non_Directed Tx's (MSDU) @ 1MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA2,
+ "successful Non_Directed Tx's (MSDU) @ 2MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA5_5,
+ "successful Non_Directed Tx's (MSDU) @ 5.5MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA11,
+ "successful Non_Directed Tx's (MSDU) @ 11MB"),
+ IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"),
+ IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"),
+ IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"),
+ IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"),
+ IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"),
+ IPW2100_ORD(STAT_TX_ASSN_RESP,
+ "successful Association response Tx's"),
+ IPW2100_ORD(STAT_TX_REASSN,
+ "successful Reassociation Tx's"),
+ IPW2100_ORD(STAT_TX_REASSN_RESP,
+ "successful Reassociation response Tx's"),
+ IPW2100_ORD(STAT_TX_PROBE,
+ "probes successfully transmitted"),
+ IPW2100_ORD(STAT_TX_PROBE_RESP,
+ "probe responses successfully transmitted"),
+ IPW2100_ORD(STAT_TX_BEACON, "tx beacon"),
+ IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"),
+ IPW2100_ORD(STAT_TX_DISASSN,
+ "successful Disassociation TX"),
+ IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"),
+ IPW2100_ORD(STAT_TX_DEAUTH,
+ "successful Deauthentication TX"),
+ IPW2100_ORD(STAT_TX_TOTAL_BYTES,
+ "Total successful Tx data bytes"),
+ IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"),
+ IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"),
+ IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"),
+ IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP,
+ "times max tries in a hop failed"),
+ IPW2100_ORD(STAT_TX_DISASSN_FAIL,
+ "times disassociation failed"),
+ IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"),
+ IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"),
+ IPW2100_ORD(STAT_RX_HOST, "packets passed to host"),
+ IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"),
+ IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA5_5,
+ "directed packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA1,
+ "nondirected packets at 1MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA2,
+ "nondirected packets at 2MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA5_5,
+ "nondirected packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA11,
+ "nondirected packets at 11MB"),
+ IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"),
+ IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS,
+ "Rx CTS"),
+ IPW2100_ORD(STAT_RX_ACK, "Rx ACK"),
+ IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"),
+ IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"),
+ IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"),
+ IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"),
+ IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"),
+ IPW2100_ORD(STAT_RX_REASSN_RESP,
+ "Reassociation response Rx's"),
+ IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"),
+ IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"),
+ IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"),
+ IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"),
+ IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"),
+ IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"),
+ IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"),
+ IPW2100_ORD(STAT_RX_TOTAL_BYTES,
+ "Total rx data bytes received"),
+ IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"),
+ IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE1,
+ "duplicate rx packets at 1MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE2,
+ "duplicate rx packets at 2MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE5_5,
+ "duplicate rx packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE11,
+ "duplicate rx packets at 11MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"),
+ IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"),
+ IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"),
+ IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"),
+ IPW2100_ORD(STAT_RX_INVALID_PROTOCOL,
+ "rx frames with invalid protocol"),
+ IPW2100_ORD(SYS_BOOT_TIME, "Boot time"),
+ IPW2100_ORD(STAT_RX_NO_BUFFER,
+ "rx frames rejected due to no buffer"),
+ IPW2100_ORD(STAT_RX_MISSING_FRAG,
+ "rx frames dropped due to missing fragment"),
+ IPW2100_ORD(STAT_RX_ORPHAN_FRAG,
+ "rx frames dropped due to non-sequential fragment"),
+ IPW2100_ORD(STAT_RX_ORPHAN_FRAME,
+ "rx frames dropped due to unmatched 1st frame"),
+ IPW2100_ORD(STAT_RX_FRAG_AGEOUT,
+ "rx frames dropped due to uncompleted frame"),
+ IPW2100_ORD(STAT_RX_ICV_ERRORS,
+ "ICV errors during decryption"),
+ IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"),
+ IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"),
+ IPW2100_ORD(STAT_PSP_POLL_TIMEOUT,
+ "poll response timeouts"),
+ IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT,
+ "timeouts waiting for last {broad,multi}cast pkt"),
+ IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"),
+ IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"),
+ IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"),
+ IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"),
+ IPW2100_ORD(STAT_PERCENT_MISSED_BCNS,
+ "current calculation of % missed beacons"),
+ IPW2100_ORD(STAT_PERCENT_RETRIES,
+ "current calculation of % missed tx retries"),
+ IPW2100_ORD(ASSOCIATED_AP_PTR,
+ "0 if not associated, else pointer to AP table entry"),
+ IPW2100_ORD(AVAILABLE_AP_CNT,
+ "AP's decsribed in the AP table"),
+ IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"),
+ IPW2100_ORD(STAT_AP_ASSNS, "associations"),
+ IPW2100_ORD(STAT_ASSN_FAIL, "association failures"),
+ IPW2100_ORD(STAT_ASSN_RESP_FAIL,
+ "failures due to response fail"),
+ IPW2100_ORD(STAT_FULL_SCANS, "full scans"),
+ IPW2100_ORD(CARD_DISABLED, "Card Disabled"),
+ IPW2100_ORD(STAT_ROAM_INHIBIT,
+ "times roaming was inhibited due to activity"),
+ IPW2100_ORD(RSSI_AT_ASSN,
+ "RSSI of associated AP at time of association"),
+ IPW2100_ORD(STAT_ASSN_CAUSE1,
+ "reassociation: no probe response or TX on hop"),
+ IPW2100_ORD(STAT_ASSN_CAUSE2,
+ "reassociation: poor tx/rx quality"),
+ IPW2100_ORD(STAT_ASSN_CAUSE3,
+ "reassociation: tx/rx quality (excessive AP load"),
+ IPW2100_ORD(STAT_ASSN_CAUSE4,
+ "reassociation: AP RSSI level"),
+ IPW2100_ORD(STAT_ASSN_CAUSE5,
+ "reassociations due to load leveling"),
+ IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"),
+ IPW2100_ORD(STAT_AUTH_RESP_FAIL,
+ "times authentication response failed"),
+ IPW2100_ORD(STATION_TABLE_CNT,
+ "entries in association table"),
+ IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"),
+ IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"),
+ IPW2100_ORD(COUNTRY_CODE,
+ "IEEE country code as recv'd from beacon"),
+ IPW2100_ORD(COUNTRY_CHANNELS,
+ "channels supported by country"),
+ IPW2100_ORD(RESET_CNT, "adapter resets (warm)"),
+ IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"),
+ IPW2100_ORD(ANTENNA_DIVERSITY,
+ "TRUE if antenna diversity is disabled"),
+ IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"),
+ IPW2100_ORD(OUR_FREQ,
+ "current radio freq lower digits - channel ID"),
+ IPW2100_ORD(RTC_TIME, "current RTC time"),
+ IPW2100_ORD(PORT_TYPE, "operating mode"),
+ IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"),
+ IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"),
+ IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"),
+ IPW2100_ORD(BASIC_RATES, "basic tx rates"),
+ IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"),
+ IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"),
+ IPW2100_ORD(CAPABILITIES,
+ "Management frame capability field"),
+ IPW2100_ORD(AUTH_TYPE, "Type of authentication"),
+ IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"),
+ IPW2100_ORD(RTS_THRESHOLD,
+ "Min packet length for RTS handshaking"),
+ IPW2100_ORD(INT_MODE, "International mode"),
+ IPW2100_ORD(FRAGMENTATION_THRESHOLD,
+ "protocol frag threshold"),
+ IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS,
+ "EEPROM offset in SRAM"),
+ IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE,
+ "EEPROM size in SRAM"),
+ IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"),
+ IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS,
+ "EEPROM IBSS 11b channel set"),
+ IPW2100_ORD(MAC_VERSION, "MAC Version"),
+ IPW2100_ORD(MAC_REVISION, "MAC Revision"),
+ IPW2100_ORD(RADIO_VERSION, "Radio Version"),
+ IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"),
+ IPW2100_ORD(UCODE_VERSION, "Ucode Version"),};
+
+static ssize_t show_registers(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ int i;
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char *out = buf;
+ u32 val = 0;
+
+ out += sprintf(out, "%30s [Address ] : Hex\n", "Register");
+
+ for (i = 0; i < ARRAY_SIZE(hw_data); i++) {
+ read_register(dev, hw_data[i].addr, &val);
+ out += sprintf(out, "%30s [%08X] : %08X\n",
+ hw_data[i].name, hw_data[i].addr, val);
+ }
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
+
+static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char *out = buf;
+ int i;
+
+ out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry");
+
+ for (i = 0; i < ARRAY_SIZE(nic_data); i++) {
+ u8 tmp8;
+ u16 tmp16;
+ u32 tmp32;
+
+ switch (nic_data[i].size) {
+ case 1:
+ read_nic_byte(dev, nic_data[i].addr, &tmp8);
+ out += sprintf(out, "%30s [%08X] : %02X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp8);
+ break;
+ case 2:
+ read_nic_word(dev, nic_data[i].addr, &tmp16);
+ out += sprintf(out, "%30s [%08X] : %04X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp16);
+ break;
+ case 4:
+ read_nic_dword(dev, nic_data[i].addr, &tmp32);
+ out += sprintf(out, "%30s [%08X] : %08X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp32);
+ break;
+ }
+ }
+ return out - buf;
+}
+
+static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+
+static ssize_t show_memory(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ static unsigned long loop = 0;
+ int len = 0;
+ u32 buffer[4];
+ int i;
+ char line[81];
+
+ if (loop >= 0x30000)
+ loop = 0;
+
+ /* sysfs provides us PAGE_SIZE buffer */
+ while (len < PAGE_SIZE - 128 && loop < 0x30000) {
+
+ if (priv->snapshot[0])
+ for (i = 0; i < 4; i++)
+ buffer[i] =
+ *(u32 *) SNAPSHOT_ADDR(loop + i * 4);
+ else
+ for (i = 0; i < 4; i++)
+ read_nic_dword(dev, loop + i * 4, &buffer[i]);
+
+ if (priv->dump_raw)
+ len += sprintf(buf + len,
+ "%c%c%c%c"
+ "%c%c%c%c"
+ "%c%c%c%c"
+ "%c%c%c%c",
+ ((u8 *) buffer)[0x0],
+ ((u8 *) buffer)[0x1],
+ ((u8 *) buffer)[0x2],
+ ((u8 *) buffer)[0x3],
+ ((u8 *) buffer)[0x4],
+ ((u8 *) buffer)[0x5],
+ ((u8 *) buffer)[0x6],
+ ((u8 *) buffer)[0x7],
+ ((u8 *) buffer)[0x8],
+ ((u8 *) buffer)[0x9],
+ ((u8 *) buffer)[0xa],
+ ((u8 *) buffer)[0xb],
+ ((u8 *) buffer)[0xc],
+ ((u8 *) buffer)[0xd],
+ ((u8 *) buffer)[0xe],
+ ((u8 *) buffer)[0xf]);
+ else
+ len += sprintf(buf + len, "%s\n",
+ snprint_line(line, sizeof(line),
+ (u8 *) buffer, 16, loop));
+ loop += 16;
+ }
+
+ return len;
+}
+
+static ssize_t store_memory(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ const char *p = buf;
+
+ (void)dev; /* kill unused-var warning for debug-only code */
+
+ if (count < 1)
+ return count;
+
+ if (p[0] == '1' ||
+ (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) {
+ IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n",
+ dev->name);
+ priv->dump_raw = 1;
+
+ } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' &&
+ tolower(p[1]) == 'f')) {
+ IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n",
+ dev->name);
+ priv->dump_raw = 0;
+
+ } else if (tolower(p[0]) == 'r') {
+ IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name);
+ ipw2100_snapshot_free(priv);
+
+ } else
+ IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, "
+ "reset = clear memory snapshot\n", dev->name);
+
+ return count;
+}
+
+static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory);
+
+static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ u32 val = 0;
+ int len = 0;
+ u32 val_len;
+ static int loop = 0;
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return 0;
+
+ if (loop >= ARRAY_SIZE(ord_data))
+ loop = 0;
+
+ /* sysfs provides us PAGE_SIZE buffer */
+ while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) {
+ val_len = sizeof(u32);
+
+ if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val,
+ &val_len))
+ len += sprintf(buf + len, "[0x%02X] = ERROR %s\n",
+ ord_data[loop].index,
+ ord_data[loop].desc);
+ else
+ len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n",
+ ord_data[loop].index, val,
+ ord_data[loop].desc);
+ loop++;
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL);
+
+static ssize_t show_stats(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char *out = buf;
+
+ out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n",
+ priv->interrupts, priv->tx_interrupts,
+ priv->rx_interrupts, priv->inta_other);
+ out += sprintf(out, "firmware resets: %d\n", priv->resets);
+ out += sprintf(out, "firmware hangs: %d\n", priv->hangs);
+#ifdef CONFIG_IPW2100_DEBUG
+ out += sprintf(out, "packet mismatch image: %s\n",
+ priv->snapshot[0] ? "YES" : "NO");
+#endif
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
+
+static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
+{
+ int err;
+
+ if (mode == priv->ieee->iw_mode)
+ return 0;
+
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+
+ switch (mode) {
+ case IW_MODE_INFRA:
+ priv->net_dev->type = ARPHRD_ETHER;
+ break;
+ case IW_MODE_ADHOC:
+ priv->net_dev->type = ARPHRD_ETHER;
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ priv->last_mode = priv->ieee->iw_mode;
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ break;
+#endif /* CONFIG_IPW2100_MONITOR */
+ }
+
+ priv->ieee->iw_mode = mode;
+
+#ifdef CONFIG_PM
+ /* Indicate ipw2100_download_firmware download firmware
+ * from disk instead of memory. */
+ ipw2100_firmware.version = 0;
+#endif
+
+ printk(KERN_INFO "%s: Resetting on mode change.\n", priv->net_dev->name);
+ priv->reset_backoff = 0;
+ schedule_reset(priv);
+
+ return 0;
+}
+
+static ssize_t show_internals(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ int len = 0;
+
+#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x)
+
+ if (priv->status & STATUS_ASSOCIATED)
+ len += sprintf(buf + len, "connected: %lu\n",
+ get_seconds() - priv->connect_start);
+ else
+ len += sprintf(buf + len, "not connected\n");
+
+ DUMP_VAR(ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx], "p");
+ DUMP_VAR(status, "08lx");
+ DUMP_VAR(config, "08lx");
+ DUMP_VAR(capability, "08lx");
+
+ len +=
+ sprintf(buf + len, "last_rtc: %lu\n",
+ (unsigned long)priv->last_rtc);
+
+ DUMP_VAR(fatal_error, "d");
+ DUMP_VAR(stop_hang_check, "d");
+ DUMP_VAR(stop_rf_kill, "d");
+ DUMP_VAR(messages_sent, "d");
+
+ DUMP_VAR(tx_pend_stat.value, "d");
+ DUMP_VAR(tx_pend_stat.hi, "d");
+
+ DUMP_VAR(tx_free_stat.value, "d");
+ DUMP_VAR(tx_free_stat.lo, "d");
+
+ DUMP_VAR(msg_free_stat.value, "d");
+ DUMP_VAR(msg_free_stat.lo, "d");
+
+ DUMP_VAR(msg_pend_stat.value, "d");
+ DUMP_VAR(msg_pend_stat.hi, "d");
+
+ DUMP_VAR(fw_pend_stat.value, "d");
+ DUMP_VAR(fw_pend_stat.hi, "d");
+
+ DUMP_VAR(txq_stat.value, "d");
+ DUMP_VAR(txq_stat.lo, "d");
+
+ DUMP_VAR(ieee->scans, "d");
+ DUMP_VAR(reset_backoff, "d");
+
+ return len;
+}
+
+static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL);
+
+static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char essid[IW_ESSID_MAX_SIZE + 1];
+ u8 bssid[ETH_ALEN];
+ u32 chan = 0;
+ char *out = buf;
+ unsigned int length;
+ int ret;
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return 0;
+
+ memset(essid, 0, sizeof(essid));
+ memset(bssid, 0, sizeof(bssid));
+
+ length = IW_ESSID_MAX_SIZE;
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ length = sizeof(bssid);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+ bssid, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ length = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ out += sprintf(out, "ESSID: %s\n", essid);
+ out += sprintf(out, "BSSID: %pM\n", bssid);
+ out += sprintf(out, "Channel: %d\n", chan);
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL);
+
+#ifdef CONFIG_IPW2100_DEBUG
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
+{
+ return sprintf(buf, "0x%08X\n", ipw2100_debug_level);
+}
+
+static ssize_t store_debug_level(struct device_driver *d,
+ const char *buf, size_t count)
+{
+ u32 val;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &val);
+ if (ret)
+ IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf);
+ else
+ ipw2100_debug_level = val;
+
+ return strnlen(buf, count);
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level,
+ store_debug_level);
+#endif /* CONFIG_IPW2100_DEBUG */
+
+static ssize_t show_fatal_error(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char *out = buf;
+ int i;
+
+ if (priv->fatal_error)
+ out += sprintf(out, "0x%08X\n", priv->fatal_error);
+ else
+ out += sprintf(out, "0\n");
+
+ for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) {
+ if (!priv->fatal_errors[(priv->fatal_index - i) %
+ IPW2100_ERROR_QUEUE])
+ continue;
+
+ out += sprintf(out, "%d. 0x%08X\n", i,
+ priv->fatal_errors[(priv->fatal_index - i) %
+ IPW2100_ERROR_QUEUE]);
+ }
+
+ return out - buf;
+}
+
+static ssize_t store_fatal_error(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ schedule_reset(priv);
+ return count;
+}
+
+static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error,
+ store_fatal_error);
+
+static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "%d\n", priv->ieee->scan_age);
+}
+
+static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ unsigned long val;
+ int ret;
+
+ (void)dev; /* kill unused-var warning for debug-only code */
+
+ IPW_DEBUG_INFO("enter\n");
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret) {
+ IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
+ } else {
+ priv->ieee->scan_age = val;
+ IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
+
+static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ /* 0 - RF kill not enabled
+ 1 - SW based RF kill active (sysfs)
+ 2 - HW based RF kill active
+ 3 - Both HW and SW baed RF kill active */
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
+ (rf_kill_active(priv) ? 0x2 : 0x0);
+ return sprintf(buf, "%i\n", val);
+}
+
+static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
+{
+ if ((disable_radio ? 1 : 0) ==
+ (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
+ return 0;
+
+ IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
+ disable_radio ? "OFF" : "ON");
+
+ mutex_lock(&priv->action_mutex);
+
+ if (disable_radio) {
+ priv->status |= STATUS_RF_KILL_SW;
+ ipw2100_down(priv);
+ } else {
+ priv->status &= ~STATUS_RF_KILL_SW;
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("Can not turn radio back on - "
+ "disabled by HW switch\n");
+ /* Make sure the RF_KILL check timer is running */
+ priv->stop_rf_kill = 0;
+ mod_delayed_work(system_wq, &priv->rf_kill,
+ round_jiffies_relative(HZ));
+ } else
+ schedule_reset(priv);
+ }
+
+ mutex_unlock(&priv->action_mutex);
+ return 1;
+}
+
+static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ ipw_radio_kill_sw(priv, buf[0] == '1');
+ return count;
+}
+
+static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
+
+static struct attribute *ipw2100_sysfs_entries[] = {
+ &dev_attr_hardware.attr,
+ &dev_attr_registers.attr,
+ &dev_attr_ordinals.attr,
+ &dev_attr_pci.attr,
+ &dev_attr_stats.attr,
+ &dev_attr_internals.attr,
+ &dev_attr_bssinfo.attr,
+ &dev_attr_memory.attr,
+ &dev_attr_scan_age.attr,
+ &dev_attr_fatal_error.attr,
+ &dev_attr_rf_kill.attr,
+ &dev_attr_cfg.attr,
+ &dev_attr_status.attr,
+ &dev_attr_capability.attr,
+ NULL,
+};
+
+static struct attribute_group ipw2100_attribute_group = {
+ .attrs = ipw2100_sysfs_entries,
+};
+
+static int status_queue_allocate(struct ipw2100_priv *priv, int entries)
+{
+ struct ipw2100_status_queue *q = &priv->status_queue;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ q->size = entries * sizeof(struct ipw2100_status);
+ q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic);
+ if (!q->drv) {
+ IPW_DEBUG_WARNING("Can not allocate status queue.\n");
+ return -ENOMEM;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void status_queue_free(struct ipw2100_priv *priv)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->status_queue.drv) {
+ pci_free_consistent(priv->pci_dev, priv->status_queue.size,
+ priv->status_queue.drv,
+ priv->status_queue.nic);
+ priv->status_queue.drv = NULL;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int bd_queue_allocate(struct ipw2100_priv *priv,
+ struct ipw2100_bd_queue *q, int entries)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ memset(q, 0, sizeof(struct ipw2100_bd_queue));
+
+ q->entries = entries;
+ q->size = entries * sizeof(struct ipw2100_bd);
+ q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic);
+ if (!q->drv) {
+ IPW_DEBUG_INFO
+ ("can't allocate shared memory for buffer descriptors\n");
+ return -ENOMEM;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ if (!q)
+ return;
+
+ if (q->drv) {
+ pci_free_consistent(priv->pci_dev, q->size, q->drv, q->nic);
+ q->drv = NULL;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void bd_queue_initialize(struct ipw2100_priv *priv,
+ struct ipw2100_bd_queue *q, u32 base, u32 size,
+ u32 r, u32 w)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv,
+ (u32) q->nic);
+
+ write_register(priv->net_dev, base, q->nic);
+ write_register(priv->net_dev, size, q->entries);
+ write_register(priv->net_dev, r, q->oldest);
+ write_register(priv->net_dev, w, q->next);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_kill_works(struct ipw2100_priv *priv)
+{
+ priv->stop_rf_kill = 1;
+ priv->stop_hang_check = 1;
+ cancel_delayed_work_sync(&priv->reset_work);
+ cancel_delayed_work_sync(&priv->security_work);
+ cancel_delayed_work_sync(&priv->wx_event_work);
+ cancel_delayed_work_sync(&priv->hang_check);
+ cancel_delayed_work_sync(&priv->rf_kill);
+ cancel_delayed_work_sync(&priv->scan_event);
+}
+
+static int ipw2100_tx_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+ void *v;
+ dma_addr_t p;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n",
+ priv->net_dev->name);
+ return err;
+ }
+
+ priv->tx_buffers = kmalloc_array(TX_PENDED_QUEUE_LENGTH,
+ sizeof(struct ipw2100_tx_packet),
+ GFP_ATOMIC);
+ if (!priv->tx_buffers) {
+ bd_queue_free(priv, &priv->tx_queue);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ v = pci_alloc_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ &p);
+ if (!v) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: PCI alloc failed for tx " "buffers.\n",
+ priv->net_dev->name);
+ err = -ENOMEM;
+ break;
+ }
+
+ priv->tx_buffers[i].type = DATA;
+ priv->tx_buffers[i].info.d_struct.data =
+ (struct ipw2100_data_header *)v;
+ priv->tx_buffers[i].info.d_struct.data_phys = p;
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+
+ if (i == TX_PENDED_QUEUE_LENGTH)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ priv->tx_buffers[j].info.d_struct.data,
+ priv->tx_buffers[j].info.d_struct.
+ data_phys);
+ }
+
+ kfree(priv->tx_buffers);
+ priv->tx_buffers = NULL;
+
+ return err;
+}
+
+static void ipw2100_tx_initialize(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ /*
+ * reinitialize packet info lists
+ */
+ INIT_LIST_HEAD(&priv->fw_pend_list);
+ INIT_STAT(&priv->fw_pend_stat);
+
+ /*
+ * reinitialize lists
+ */
+ INIT_LIST_HEAD(&priv->tx_pend_list);
+ INIT_LIST_HEAD(&priv->tx_free_list);
+ INIT_STAT(&priv->tx_pend_stat);
+ INIT_STAT(&priv->tx_free_stat);
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ /* We simply drop any SKBs that have been queued for
+ * transmit */
+ if (priv->tx_buffers[i].info.d_struct.txb) {
+ libipw_txb_free(priv->tx_buffers[i].info.d_struct.
+ txb);
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+
+ list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list);
+ }
+
+ SET_STAT(&priv->tx_free_stat, i);
+
+ priv->tx_queue.oldest = 0;
+ priv->tx_queue.available = priv->tx_queue.entries;
+ priv->tx_queue.next = 0;
+ INIT_STAT(&priv->txq_stat);
+ SET_STAT(&priv->txq_stat, priv->tx_queue.available);
+
+ bd_queue_initialize(priv, &priv->tx_queue,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX);
+
+ IPW_DEBUG_INFO("exit\n");
+
+}
+
+static void ipw2100_tx_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ bd_queue_free(priv, &priv->tx_queue);
+
+ if (!priv->tx_buffers)
+ return;
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ if (priv->tx_buffers[i].info.d_struct.txb) {
+ libipw_txb_free(priv->tx_buffers[i].info.d_struct.
+ txb);
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+ if (priv->tx_buffers[i].info.d_struct.data)
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ priv->tx_buffers[i].info.d_struct.
+ data,
+ priv->tx_buffers[i].info.d_struct.
+ data_phys);
+ }
+
+ kfree(priv->tx_buffers);
+ priv->tx_buffers = NULL;
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int ipw2100_rx_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_INFO("failed bd_queue_allocate\n");
+ return err;
+ }
+
+ err = status_queue_allocate(priv, RX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_INFO("failed status_queue_allocate\n");
+ bd_queue_free(priv, &priv->rx_queue);
+ return err;
+ }
+
+ /*
+ * allocate packets
+ */
+ priv->rx_buffers = kmalloc(RX_QUEUE_LENGTH *
+ sizeof(struct ipw2100_rx_packet),
+ GFP_KERNEL);
+ if (!priv->rx_buffers) {
+ IPW_DEBUG_INFO("can't allocate rx packet buffer table\n");
+
+ bd_queue_free(priv, &priv->rx_queue);
+
+ status_queue_free(priv);
+
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ err = ipw2100_alloc_skb(priv, packet);
+ if (unlikely(err)) {
+ err = -ENOMEM;
+ break;
+ }
+
+ /* The BD holds the cache aligned address */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+ priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH;
+ priv->status_queue.drv[i].status_fields = 0;
+ }
+
+ if (i == RX_QUEUE_LENGTH)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr,
+ sizeof(struct ipw2100_rx_packet),
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(priv->rx_buffers[j].skb);
+ }
+
+ kfree(priv->rx_buffers);
+ priv->rx_buffers = NULL;
+
+ bd_queue_free(priv, &priv->rx_queue);
+
+ status_queue_free(priv);
+
+ return err;
+}
+
+static void ipw2100_rx_initialize(struct ipw2100_priv *priv)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ priv->rx_queue.oldest = 0;
+ priv->rx_queue.available = priv->rx_queue.entries - 1;
+ priv->rx_queue.next = priv->rx_queue.entries - 1;
+
+ INIT_STAT(&priv->rxq_stat);
+ SET_STAT(&priv->rxq_stat, priv->rx_queue.available);
+
+ bd_queue_initialize(priv, &priv->rx_queue,
+ IPW_MEM_HOST_SHARED_RX_BD_BASE,
+ IPW_MEM_HOST_SHARED_RX_BD_SIZE,
+ IPW_MEM_HOST_SHARED_RX_READ_INDEX,
+ IPW_MEM_HOST_SHARED_RX_WRITE_INDEX);
+
+ /* set up the status queue */
+ write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE,
+ priv->status_queue.nic);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_rx_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ bd_queue_free(priv, &priv->rx_queue);
+ status_queue_free(priv);
+
+ if (!priv->rx_buffers)
+ return;
+
+ for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+ if (priv->rx_buffers[i].rxp) {
+ pci_unmap_single(priv->pci_dev,
+ priv->rx_buffers[i].dma_addr,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(priv->rx_buffers[i].skb);
+ }
+ }
+
+ kfree(priv->rx_buffers);
+ priv->rx_buffers = NULL;
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int ipw2100_read_mac_address(struct ipw2100_priv *priv)
+{
+ u32 length = ETH_ALEN;
+ u8 addr[ETH_ALEN];
+
+ int err;
+
+ err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length);
+ if (err) {
+ IPW_DEBUG_INFO("MAC address read failed\n");
+ return -EIO;
+ }
+
+ memcpy(priv->net_dev->dev_addr, addr, ETH_ALEN);
+ IPW_DEBUG_INFO("card MAC is %pM\n", priv->net_dev->dev_addr);
+
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Firmware Commands
+ *
+ ********************************************************************/
+
+static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = ADAPTER_ADDRESS,
+ .host_command_sequence = 0,
+ .host_command_length = ETH_ALEN
+ };
+ int err;
+
+ IPW_DEBUG_HC("SET_MAC_ADDRESS\n");
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->config & CFG_CUSTOM_MAC) {
+ memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN);
+ memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+ } else
+ memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr,
+ ETH_ALEN);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ IPW_DEBUG_INFO("exit\n");
+ return err;
+}
+
+static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = PORT_TYPE,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(u32)
+ };
+ int err;
+
+ switch (port_type) {
+ case IW_MODE_INFRA:
+ cmd.host_command_parameters[0] = IPW_BSS;
+ break;
+ case IW_MODE_ADHOC:
+ cmd.host_command_parameters[0] = IPW_IBSS;
+ break;
+ }
+
+ IPW_DEBUG_HC("PORT_TYPE: %s\n",
+ port_type == IPW_IBSS ? "Ad-Hoc" : "Managed");
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = CHANNEL,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(u32)
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = channel;
+
+ IPW_DEBUG_HC("CHANNEL: %d\n", channel);
+
+ /* If BSS then we don't support channel selection */
+ if (priv->ieee->iw_mode == IW_MODE_INFRA)
+ return 0;
+
+ if ((channel != 0) &&
+ ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL)))
+ return -EINVAL;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ IPW_DEBUG_INFO("Failed to set channel to %d", channel);
+ return err;
+ }
+
+ if (channel)
+ priv->config |= CFG_STATIC_CHANNEL;
+ else
+ priv->config &= ~CFG_STATIC_CHANNEL;
+
+ priv->channel = channel;
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SYSTEM_CONFIG,
+ .host_command_sequence = 0,
+ .host_command_length = 12,
+ };
+ u32 ibss_mask, len = sizeof(u32);
+ int err;
+
+ /* Set system configuration */
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START;
+
+ cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK |
+ IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE;
+
+ if (!(priv->config & CFG_LONG_PREAMBLE))
+ cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO;
+
+ err = ipw2100_get_ordinal(priv,
+ IPW_ORD_EEPROM_IBSS_11B_CHANNELS,
+ &ibss_mask, &len);
+ if (err)
+ ibss_mask = IPW_IBSS_11B_DEFAULT_MASK;
+
+ cmd.host_command_parameters[1] = REG_CHANNEL_MASK;
+ cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask;
+
+ /* 11b only */
+ /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+/* If IPv6 is configured in the kernel then we don't want to filter out all
+ * of the multicast packets as IPv6 needs some. */
+#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
+ cmd.host_command = ADD_MULTICAST;
+ cmd.host_command_sequence = 0;
+ cmd.host_command_length = 0;
+
+ ipw2100_hw_send_command(priv, &cmd);
+#endif
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = BASIC_TX_RATES,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = rate & TX_RATE_MASK;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ /* Set BASIC TX Rate first */
+ ipw2100_hw_send_command(priv, &cmd);
+
+ /* Set TX Rate */
+ cmd.host_command = TX_RATES;
+ ipw2100_hw_send_command(priv, &cmd);
+
+ /* Set MSDU TX Rate */
+ cmd.host_command = MSDU_TX_RATES;
+ ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ priv->tx_rates = rate;
+
+ return 0;
+}
+
+static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level)
+{
+ struct host_command cmd = {
+ .host_command = POWER_MODE,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = power_level;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ if (power_level == IPW_POWER_MODE_CAM)
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ else
+ priv->power_mode = IPW_POWER_ENABLED | power_level;
+
+#ifdef IPW2100_TX_POWER
+ if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) {
+ /* Set beacon interval */
+ cmd.host_command = TX_POWER_INDEX;
+ cmd.host_command_parameters[0] = (u32) priv->adhoc_power;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+ }
+#endif
+
+ return 0;
+}
+
+static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold)
+{
+ struct host_command cmd = {
+ .host_command = RTS_THRESHOLD,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ if (threshold & RTS_DISABLED)
+ cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD;
+ else
+ cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->rts_threshold = threshold;
+
+ return 0;
+}
+
+#if 0
+int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv,
+ u32 threshold, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = FRAG_THRESHOLD,
+ .host_command_sequence = 0,
+ .host_command_length = 4,
+ .host_command_parameters[0] = 0,
+ };
+ int err;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (threshold == 0)
+ threshold = DEFAULT_FRAG_THRESHOLD;
+ else {
+ threshold = max(threshold, MIN_FRAG_THRESHOLD);
+ threshold = min(threshold, MAX_FRAG_THRESHOLD);
+ }
+
+ cmd.host_command_parameters[0] = threshold;
+
+ IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ if (!err)
+ priv->frag_threshold = threshold;
+
+ return err;
+}
+#endif
+
+static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry)
+{
+ struct host_command cmd = {
+ .host_command = SHORT_RETRY_LIMIT,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = retry;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->short_retry_limit = retry;
+
+ return 0;
+}
+
+static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry)
+{
+ struct host_command cmd = {
+ .host_command = LONG_RETRY_LIMIT,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = retry;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->long_retry_limit = retry;
+
+ return 0;
+}
+
+static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = MANDATORY_BSSID,
+ .host_command_sequence = 0,
+ .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN
+ };
+ int err;
+
+#ifdef CONFIG_IPW2100_DEBUG
+ if (bssid != NULL)
+ IPW_DEBUG_HC("MANDATORY_BSSID: %pM\n", bssid);
+ else
+ IPW_DEBUG_HC("MANDATORY_BSSID: <clear>\n");
+#endif
+ /* if BSSID is empty then we disable mandatory bssid mode */
+ if (bssid != NULL)
+ memcpy(cmd.host_command_parameters, bssid, ETH_ALEN);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = DISASSOCIATION_BSSID,
+ .host_command_sequence = 0,
+ .host_command_length = ETH_ALEN
+ };
+ int err;
+ int len;
+
+ IPW_DEBUG_HC("DISASSOCIATION_BSSID\n");
+
+ len = ETH_ALEN;
+ /* The Firmware currently ignores the BSSID and just disassociates from
+ * the currently associated AP -- but in the off chance that a future
+ * firmware does use the BSSID provided here, we go ahead and try and
+ * set it to the currently associated AP's BSSID */
+ memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ return err;
+}
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *,
+ struct ipw2100_wpa_assoc_frame *, int)
+ __attribute__ ((unused));
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv,
+ struct ipw2100_wpa_assoc_frame *wpa_frame,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SET_WPA_IE,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame),
+ };
+ int err;
+
+ IPW_DEBUG_HC("SET_WPA_IE\n");
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ memcpy(cmd.host_command_parameters, wpa_frame,
+ sizeof(struct ipw2100_wpa_assoc_frame));
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ if (ipw2100_enable_adapter(priv))
+ err = -EIO;
+ }
+
+ return err;
+}
+
+struct security_info_params {
+ u32 allowed_ciphers;
+ u16 version;
+ u8 auth_mode;
+ u8 replay_counters_number;
+ u8 unicast_using_group;
+} __packed;
+
+static int ipw2100_set_security_information(struct ipw2100_priv *priv,
+ int auth_mode,
+ int security_level,
+ int unicast_using_group,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SET_SECURITY_INFORMATION,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct security_info_params)
+ };
+ struct security_info_params *security =
+ (struct security_info_params *)&cmd.host_command_parameters;
+ int err;
+ memset(security, 0, sizeof(*security));
+
+ /* If shared key AP authentication is turned on, then we need to
+ * configure the firmware to try and use it.
+ *
+ * Actual data encryption/decryption is handled by the host. */
+ security->auth_mode = auth_mode;
+ security->unicast_using_group = unicast_using_group;
+
+ switch (security_level) {
+ default:
+ case SEC_LEVEL_0:
+ security->allowed_ciphers = IPW_NONE_CIPHER;
+ break;
+ case SEC_LEVEL_1:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER;
+ break;
+ case SEC_LEVEL_2:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_TKIP_CIPHER;
+ break;
+ case SEC_LEVEL_2_CKIP:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_CKIP_CIPHER;
+ break;
+ case SEC_LEVEL_3:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER;
+ break;
+ }
+
+ IPW_DEBUG_HC
+ ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n",
+ security->auth_mode, security->allowed_ciphers, security_level);
+
+ security->replay_counters_number = 0;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power)
+{
+ struct host_command cmd = {
+ .host_command = TX_POWER_INDEX,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err = 0;
+ u32 tmp = tx_power;
+
+ if (tx_power != IPW_TX_POWER_DEFAULT)
+ tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 /
+ (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
+
+ cmd.host_command_parameters[0] = tmp;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (!err)
+ priv->tx_power = tx_power;
+
+ return 0;
+}
+
+static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv,
+ u32 interval, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = BEACON_INTERVAL,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = interval;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void ipw2100_queues_initialize(struct ipw2100_priv *priv)
+{
+ ipw2100_tx_initialize(priv);
+ ipw2100_rx_initialize(priv);
+ ipw2100_msg_initialize(priv);
+}
+
+static void ipw2100_queues_free(struct ipw2100_priv *priv)
+{
+ ipw2100_tx_free(priv);
+ ipw2100_rx_free(priv);
+ ipw2100_msg_free(priv);
+}
+
+static int ipw2100_queues_allocate(struct ipw2100_priv *priv)
+{
+ if (ipw2100_tx_allocate(priv) ||
+ ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv))
+ goto fail;
+
+ return 0;
+
+ fail:
+ ipw2100_tx_free(priv);
+ ipw2100_rx_free(priv);
+ ipw2100_msg_free(priv);
+ return -ENOMEM;
+}
+
+#define IPW_PRIVACY_CAPABLE 0x0008
+
+static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = WEP_FLAGS,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = flags;
+
+ IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+struct ipw2100_wep_key {
+ u8 idx;
+ u8 len;
+ u8 key[13];
+};
+
+/* Macros to ease up priting WEP keys */
+#define WEP_FMT_64 "%02X%02X%02X%02X-%02X"
+#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X"
+#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4]
+#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10]
+
+/**
+ * Set a the wep key
+ *
+ * @priv: struct to work on
+ * @idx: index of the key we want to set
+ * @key: ptr to the key data to set
+ * @len: length of the buffer at @key
+ * @batch_mode: FIXME perform the operation in batch mode, not
+ * disabling the device.
+ *
+ * @returns 0 if OK, < 0 errno code on error.
+ *
+ * Fill out a command structure with the new wep key, length an
+ * index and send it down the wire.
+ */
+static int ipw2100_set_key(struct ipw2100_priv *priv,
+ int idx, char *key, int len, int batch_mode)
+{
+ int keylen = len ? (len <= 5 ? 5 : 13) : 0;
+ struct host_command cmd = {
+ .host_command = WEP_KEY_INFO,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct ipw2100_wep_key),
+ };
+ struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters;
+ int err;
+
+ IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n",
+ idx, keylen, len);
+
+ /* NOTE: We don't check cached values in case the firmware was reset
+ * or some other problem is occurring. If the user is setting the key,
+ * then we push the change */
+
+ wep_key->idx = idx;
+ wep_key->len = keylen;
+
+ if (keylen) {
+ memcpy(wep_key->key, key, len);
+ memset(wep_key->key + len, 0, keylen - len);
+ }
+
+ /* Will be optimized out on debug not being configured in */
+ if (keylen == 0)
+ IPW_DEBUG_WEP("%s: Clearing key %d\n",
+ priv->net_dev->name, wep_key->idx);
+ else if (keylen == 5)
+ IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n",
+ priv->net_dev->name, wep_key->idx, wep_key->len,
+ WEP_STR_64(wep_key->key));
+ else
+ IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128
+ "\n",
+ priv->net_dev->name, wep_key->idx, wep_key->len,
+ WEP_STR_128(wep_key->key));
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ int err2 = ipw2100_enable_adapter(priv);
+ if (err == 0)
+ err = err2;
+ }
+ return err;
+}
+
+static int ipw2100_set_key_index(struct ipw2100_priv *priv,
+ int idx, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = WEP_KEY_INDEX,
+ .host_command_sequence = 0,
+ .host_command_length = 4,
+ .host_command_parameters = {idx},
+ };
+ int err;
+
+ IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx);
+
+ if (idx < 0 || idx > 3)
+ return -EINVAL;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode)
+{
+ int i, err, auth_mode, sec_level, use_group;
+
+ if (!(priv->status & STATUS_RUNNING))
+ return 0;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (!priv->ieee->sec.enabled) {
+ err =
+ ipw2100_set_security_information(priv, IPW_AUTH_OPEN,
+ SEC_LEVEL_0, 0, 1);
+ } else {
+ auth_mode = IPW_AUTH_OPEN;
+ if (priv->ieee->sec.flags & SEC_AUTH_MODE) {
+ if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)
+ auth_mode = IPW_AUTH_SHARED;
+ else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)
+ auth_mode = IPW_AUTH_LEAP_CISCO_ID;
+ }
+
+ sec_level = SEC_LEVEL_0;
+ if (priv->ieee->sec.flags & SEC_LEVEL)
+ sec_level = priv->ieee->sec.level;
+
+ use_group = 0;
+ if (priv->ieee->sec.flags & SEC_UNICAST_GROUP)
+ use_group = priv->ieee->sec.unicast_uses_group;
+
+ err =
+ ipw2100_set_security_information(priv, auth_mode, sec_level,
+ use_group, 1);
+ }
+
+ if (err)
+ goto exit;
+
+ if (priv->ieee->sec.enabled) {
+ for (i = 0; i < 4; i++) {
+ if (!(priv->ieee->sec.flags & (1 << i))) {
+ memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN);
+ priv->ieee->sec.key_sizes[i] = 0;
+ } else {
+ err = ipw2100_set_key(priv, i,
+ priv->ieee->sec.keys[i],
+ priv->ieee->sec.
+ key_sizes[i], 1);
+ if (err)
+ goto exit;
+ }
+ }
+
+ ipw2100_set_key_index(priv, priv->ieee->crypt_info.tx_keyidx, 1);
+ }
+
+ /* Always enable privacy so the Host can filter WEP packets if
+ * encrypted data is sent up */
+ err =
+ ipw2100_set_wep_flags(priv,
+ priv->ieee->sec.
+ enabled ? IPW_PRIVACY_CAPABLE : 0, 1);
+ if (err)
+ goto exit;
+
+ priv->status &= ~STATUS_SECURITY_UPDATED;
+
+ exit:
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static void ipw2100_security_work(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, security_work.work);
+
+ /* If we happen to have reconnected before we get a chance to
+ * process this, then update the security settings--which causes
+ * a disassociation to occur */
+ if (!(priv->status & STATUS_ASSOCIATED) &&
+ priv->status & STATUS_SECURITY_UPDATED)
+ ipw2100_configure_security(priv, 0);
+}
+
+static void shim__set_security(struct net_device *dev,
+ struct libipw_security *sec)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int i, force_update = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED))
+ goto done;
+
+ for (i = 0; i < 4; i++) {
+ if (sec->flags & (1 << i)) {
+ priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
+ if (sec->key_sizes[i] == 0)
+ priv->ieee->sec.flags &= ~(1 << i);
+ else
+ memcpy(priv->ieee->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ if (sec->level == SEC_LEVEL_1) {
+ priv->ieee->sec.flags |= (1 << i);
+ priv->status |= STATUS_SECURITY_UPDATED;
+ } else
+ priv->ieee->sec.flags &= ~(1 << i);
+ }
+ }
+
+ if ((sec->flags & SEC_ACTIVE_KEY) &&
+ priv->ieee->sec.active_key != sec->active_key) {
+ if (sec->active_key <= 3) {
+ priv->ieee->sec.active_key = sec->active_key;
+ priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if ((sec->flags & SEC_AUTH_MODE) &&
+ (priv->ieee->sec.auth_mode != sec->auth_mode)) {
+ priv->ieee->sec.auth_mode = sec->auth_mode;
+ priv->ieee->sec.flags |= SEC_AUTH_MODE;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
+ priv->ieee->sec.flags |= SEC_ENABLED;
+ priv->ieee->sec.enabled = sec->enabled;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ force_update = 1;
+ }
+
+ if (sec->flags & SEC_ENCRYPT)
+ priv->ieee->sec.encrypt = sec->encrypt;
+
+ if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
+ priv->ieee->sec.level = sec->level;
+ priv->ieee->sec.flags |= SEC_LEVEL;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n",
+ priv->ieee->sec.flags & (1 << 8) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 7) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 6) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 5) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 4) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 3) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 2) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 1) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 0) ? '1' : '0');
+
+/* As a temporary work around to enable WPA until we figure out why
+ * wpa_supplicant toggles the security capability of the driver, which
+ * forces a disassocation with force_update...
+ *
+ * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/
+ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
+ ipw2100_configure_security(priv, 0);
+ done:
+ mutex_unlock(&priv->action_mutex);
+}
+
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv)
+{
+ int err;
+ int batch_mode = 1;
+ u8 *bssid;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+ if (err)
+ return err;
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+ }
+#endif /* CONFIG_IPW2100_MONITOR */
+
+ err = ipw2100_read_mac_address(priv);
+ if (err)
+ return -EIO;
+
+ err = ipw2100_set_mac_address(priv, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_system_config(priv, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode);
+ if (err)
+ return err;
+
+ /* Default to power mode OFF */
+ err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+ if (err)
+ return err;
+
+ err = ipw2100_set_rts_threshold(priv, priv->rts_threshold);
+ if (err)
+ return err;
+
+ if (priv->config & CFG_STATIC_BSSID)
+ bssid = priv->bssid;
+ else
+ bssid = NULL;
+ err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->config & CFG_STATIC_ESSID)
+ err = ipw2100_set_essid(priv, priv->essid, priv->essid_len,
+ batch_mode);
+ else
+ err = ipw2100_set_essid(priv, NULL, 0, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_configure_security(priv, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ err =
+ ipw2100_set_ibss_beacon_interval(priv,
+ priv->beacon_interval,
+ batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_tx_power(priv, priv->tx_power);
+ if (err)
+ return err;
+ }
+
+ /*
+ err = ipw2100_set_fragmentation_threshold(
+ priv, priv->frag_threshold, batch_mode);
+ if (err)
+ return err;
+ */
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+/*************************************************************************
+ *
+ * EXTERNALLY CALLED METHODS
+ *
+ *************************************************************************/
+
+/* This method is called by the network layer -- not to be confused with
+ * ipw2100_set_mac_address() declared above called by this driver (and this
+ * method as well) to talk to the firmware */
+static int ipw2100_set_address(struct net_device *dev, void *p)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct sockaddr *addr = p;
+ int err = 0;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ mutex_lock(&priv->action_mutex);
+
+ priv->config |= CFG_CUSTOM_MAC;
+ memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
+
+ err = ipw2100_set_mac_address(priv, 0);
+ if (err)
+ goto done;
+
+ priv->reset_backoff = 0;
+ mutex_unlock(&priv->action_mutex);
+ ipw2100_reset_adapter(&priv->reset_work.work);
+ return 0;
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_open(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ unsigned long flags;
+ IPW_DEBUG_INFO("dev->open\n");
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ if (priv->status & STATUS_ASSOCIATED) {
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+ }
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ return 0;
+}
+
+static int ipw2100_close(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ unsigned long flags;
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->status & STATUS_ASSOCIATED)
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ /* Flush the TX queue ... */
+ while (!list_empty(&priv->tx_pend_list)) {
+ element = priv->tx_pend_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ list_del(element);
+ DEC_STAT(&priv->tx_pend_stat);
+
+ libipw_txb_free(packet->info.d_struct.txb);
+ packet->info.d_struct.txb = NULL;
+
+ list_add_tail(element, &priv->tx_free_list);
+ INC_STAT(&priv->tx_free_stat);
+ }
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+/*
+ * TODO: Fix this function... its just wrong
+ */
+static void ipw2100_tx_timeout(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ dev->stats.tx_errors++;
+
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ return;
+#endif
+
+ IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n",
+ dev->name);
+ schedule_reset(priv);
+}
+
+static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value)
+{
+ /* This is called when wpa_supplicant loads and closes the driver
+ * interface. */
+ priv->ieee->wpa_enabled = value;
+ return 0;
+}
+
+static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value)
+{
+
+ struct libipw_device *ieee = priv->ieee;
+ struct libipw_security sec = {
+ .flags = SEC_AUTH_MODE,
+ };
+ int ret = 0;
+
+ if (value & IW_AUTH_ALG_SHARED_KEY) {
+ sec.auth_mode = WLAN_AUTH_SHARED_KEY;
+ ieee->open_wep = 0;
+ } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ sec.auth_mode = WLAN_AUTH_OPEN;
+ ieee->open_wep = 1;
+ } else if (value & IW_AUTH_ALG_LEAP) {
+ sec.auth_mode = WLAN_AUTH_LEAP;
+ ieee->open_wep = 1;
+ } else
+ return -EINVAL;
+
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
+ char *wpa_ie, int wpa_ie_len)
+{
+
+ struct ipw2100_wpa_assoc_frame frame;
+
+ frame.fixed_ie_mask = 0;
+
+ /* copy WPA IE */
+ memcpy(frame.var_ie, wpa_ie, wpa_ie_len);
+ frame.var_ie_len = wpa_ie_len;
+
+ /* make sure WPA is enabled */
+ ipw2100_wpa_enable(priv, 1);
+ ipw2100_set_wpa_ie(priv, &frame, 0);
+}
+
+static void ipw_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ char fw_ver[64], ucode_ver[64];
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+
+ ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver));
+ ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver));
+
+ snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s",
+ fw_ver, priv->eeprom_version, ucode_ver);
+
+ strlcpy(info->bus_info, pci_name(priv->pci_dev),
+ sizeof(info->bus_info));
+}
+
+static u32 ipw2100_ethtool_get_link(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
+}
+
+static const struct ethtool_ops ipw2100_ethtool_ops = {
+ .get_link = ipw2100_ethtool_get_link,
+ .get_drvinfo = ipw_ethtool_get_drvinfo,
+};
+
+static void ipw2100_hang_check(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, hang_check.work);
+ unsigned long flags;
+ u32 rtc = 0xa5a5a5a5;
+ u32 len = sizeof(rtc);
+ int restart = 0;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->fatal_error != 0) {
+ /* If fatal_error is set then we need to restart */
+ IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n",
+ priv->net_dev->name);
+
+ restart = 1;
+ } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) ||
+ (rtc == priv->last_rtc)) {
+ /* Check if firmware is hung */
+ IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n",
+ priv->net_dev->name);
+
+ restart = 1;
+ }
+
+ if (restart) {
+ /* Kill timer */
+ priv->stop_hang_check = 1;
+ priv->hangs++;
+
+ /* Restart the NIC */
+ schedule_reset(priv);
+ }
+
+ priv->last_rtc = rtc;
+
+ if (!priv->stop_hang_check)
+ schedule_delayed_work(&priv->hang_check, HZ / 2);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+static void ipw2100_rf_kill(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, rf_kill.work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
+ if (!priv->stop_rf_kill)
+ schedule_delayed_work(&priv->rf_kill,
+ round_jiffies_relative(HZ));
+ goto exit_unlock;
+ }
+
+ /* RF Kill is now disabled, so bring the device back up */
+
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
+ "device\n");
+ schedule_reset(priv);
+ } else
+ IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
+ "enabled\n");
+
+ exit_unlock:
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv);
+
+static const struct net_device_ops ipw2100_netdev_ops = {
+ .ndo_open = ipw2100_open,
+ .ndo_stop = ipw2100_close,
+ .ndo_start_xmit = libipw_xmit,
+ .ndo_change_mtu = libipw_change_mtu,
+ .ndo_tx_timeout = ipw2100_tx_timeout,
+ .ndo_set_mac_address = ipw2100_set_address,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/* Look into using netdev destructor to shutdown libipw? */
+
+static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
+ void __iomem * ioaddr)
+{
+ struct ipw2100_priv *priv;
+ struct net_device *dev;
+
+ dev = alloc_libipw(sizeof(struct ipw2100_priv), 0);
+ if (!dev)
+ return NULL;
+ priv = libipw_priv(dev);
+ priv->ieee = netdev_priv(dev);
+ priv->pci_dev = pci_dev;
+ priv->net_dev = dev;
+ priv->ioaddr = ioaddr;
+
+ priv->ieee->hard_start_xmit = ipw2100_tx;
+ priv->ieee->set_security = shim__set_security;
+
+ priv->ieee->perfect_rssi = -20;
+ priv->ieee->worst_rssi = -85;
+
+ dev->netdev_ops = &ipw2100_netdev_ops;
+ dev->ethtool_ops = &ipw2100_ethtool_ops;
+ dev->wireless_handlers = &ipw2100_wx_handler_def;
+ priv->wireless_data.libipw = priv->ieee;
+ dev->wireless_data = &priv->wireless_data;
+ dev->watchdog_timeo = 3 * HZ;
+ dev->irq = 0;
+
+ /* NOTE: We don't use the wireless_handlers hook
+ * in dev as the system will start throwing WX requests
+ * to us before we're actually initialized and it just
+ * ends up causing problems. So, we just handle
+ * the WX extensions through the ipw2100_ioctl interface */
+
+ /* memset() puts everything to 0, so we only have explicitly set
+ * those values that need to be something else */
+
+ /* If power management is turned on, default to AUTO mode */
+ priv->power_mode = IPW_POWER_AUTO;
+
+#ifdef CONFIG_IPW2100_MONITOR
+ priv->config |= CFG_CRC_CHECK;
+#endif
+ priv->ieee->wpa_enabled = 0;
+ priv->ieee->drop_unencrypted = 0;
+ priv->ieee->privacy_invoked = 0;
+ priv->ieee->ieee802_1x = 1;
+
+ /* Set module parameters */
+ switch (network_mode) {
+ case 1:
+ priv->ieee->iw_mode = IW_MODE_ADHOC;
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case 2:
+ priv->ieee->iw_mode = IW_MODE_MONITOR;
+ break;
+#endif
+ default:
+ case 0:
+ priv->ieee->iw_mode = IW_MODE_INFRA;
+ break;
+ }
+
+ if (disable == 1)
+ priv->status |= STATUS_RF_KILL_SW;
+
+ if (channel != 0 &&
+ ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) {
+ priv->config |= CFG_STATIC_CHANNEL;
+ priv->channel = channel;
+ }
+
+ if (associate)
+ priv->config |= CFG_ASSOCIATE;
+
+ priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
+ priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
+ priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
+ priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED;
+ priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED;
+ priv->tx_power = IPW_TX_POWER_DEFAULT;
+ priv->tx_rates = DEFAULT_TX_RATES;
+
+ strcpy(priv->nick, "ipw2100");
+
+ spin_lock_init(&priv->low_lock);
+ mutex_init(&priv->action_mutex);
+ mutex_init(&priv->adapter_mutex);
+
+ init_waitqueue_head(&priv->wait_command_queue);
+
+ netif_carrier_off(dev);
+
+ INIT_LIST_HEAD(&priv->msg_free_list);
+ INIT_LIST_HEAD(&priv->msg_pend_list);
+ INIT_STAT(&priv->msg_free_stat);
+ INIT_STAT(&priv->msg_pend_stat);
+
+ INIT_LIST_HEAD(&priv->tx_free_list);
+ INIT_LIST_HEAD(&priv->tx_pend_list);
+ INIT_STAT(&priv->tx_free_stat);
+ INIT_STAT(&priv->tx_pend_stat);
+
+ INIT_LIST_HEAD(&priv->fw_pend_list);
+ INIT_STAT(&priv->fw_pend_stat);
+
+ INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter);
+ INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work);
+ INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work);
+ INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check);
+ INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill);
+ INIT_DELAYED_WORK(&priv->scan_event, ipw2100_scan_event);
+
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+ ipw2100_irq_tasklet, (unsigned long)priv);
+
+ /* NOTE: We do not start the deferred work for status checks yet */
+ priv->stop_rf_kill = 1;
+ priv->stop_hang_check = 1;
+
+ return dev;
+}
+
+static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ void __iomem *ioaddr;
+ struct net_device *dev = NULL;
+ struct ipw2100_priv *priv = NULL;
+ int err = 0;
+ int registered = 0;
+ u32 val;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (!(pci_resource_flags(pci_dev, 0) & IORESOURCE_MEM)) {
+ IPW_DEBUG_INFO("weird - resource type is not memory\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ ioaddr = pci_iomap(pci_dev, 0, 0);
+ if (!ioaddr) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ioremap_nocache.\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* allocate and initialize our net_device */
+ dev = ipw2100_alloc_device(pci_dev, ioaddr);
+ if (!dev) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ipw2100_alloc_device.\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* set up PCI mappings for device */
+ err = pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_enable_device.\n");
+ return err;
+ }
+
+ priv = libipw_priv(dev);
+
+ pci_set_master(pci_dev);
+ pci_set_drvdata(pci_dev, priv);
+
+ err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_set_dma_mask.\n");
+ pci_disable_device(pci_dev);
+ return err;
+ }
+
+ err = pci_request_regions(pci_dev, DRV_NAME);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_request_regions.\n");
+ pci_disable_device(pci_dev);
+ return err;
+ }
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_read_config_dword(pci_dev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+ if (!ipw2100_hw_is_adapter_in_system(dev)) {
+ printk(KERN_WARNING DRV_NAME
+ "Device not found via register read.\n");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ SET_NETDEV_DEV(dev, &pci_dev->dev);
+
+ /* Force interrupts to be shut off on the device */
+ priv->status |= STATUS_INT_ENABLED;
+ ipw2100_disable_interrupts(priv);
+
+ /* Allocate and initialize the Tx/Rx queues and lists */
+ if (ipw2100_queues_allocate(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ipw2100_queues_allocate.\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+ ipw2100_queues_initialize(priv);
+
+ err = request_irq(pci_dev->irq,
+ ipw2100_interrupt, IRQF_SHARED, dev->name, priv);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling request_irq: %d.\n", pci_dev->irq);
+ goto fail;
+ }
+ dev->irq = pci_dev->irq;
+
+ IPW_DEBUG_INFO("Attempting to register device...\n");
+
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2100 Network Connection\n");
+
+ err = ipw2100_up(priv, 1);
+ if (err)
+ goto fail;
+
+ err = ipw2100_wdev_init(dev);
+ if (err)
+ goto fail;
+ registered = 1;
+
+ /* Bring up the interface. Pre 0.46, after we registered the
+ * network device we would call ipw2100_up. This introduced a race
+ * condition with newer hotplug configurations (network was coming
+ * up and making calls before the device was initialized).
+ */
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling register_netdev.\n");
+ goto fail;
+ }
+ registered = 2;
+
+ mutex_lock(&priv->action_mutex);
+
+ IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
+
+ /* perform this after register_netdev so that dev->name is set */
+ err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+ if (err)
+ goto fail_unlock;
+
+ /* If the RF Kill switch is disabled, go ahead and complete the
+ * startup sequence */
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ /* Enable the adapter - sends HOST_COMPLETE */
+ if (ipw2100_enable_adapter(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: failed in call to enable adapter.\n",
+ priv->net_dev->name);
+ ipw2100_hw_stop_adapter(priv);
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ /* Start a scan . . . */
+ ipw2100_set_scan_options(priv);
+ ipw2100_start_scan(priv);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ priv->status |= STATUS_INITIALIZED;
+
+ mutex_unlock(&priv->action_mutex);
+out:
+ return err;
+
+ fail_unlock:
+ mutex_unlock(&priv->action_mutex);
+ fail:
+ if (dev) {
+ if (registered >= 2)
+ unregister_netdev(dev);
+
+ if (registered) {
+ wiphy_unregister(priv->ieee->wdev.wiphy);
+ kfree(priv->ieee->bg_band.channels);
+ }
+
+ ipw2100_hw_stop_adapter(priv);
+
+ ipw2100_disable_interrupts(priv);
+
+ if (dev->irq)
+ free_irq(dev->irq, priv);
+
+ ipw2100_kill_works(priv);
+
+ /* These are safe to call even if they weren't allocated */
+ ipw2100_queues_free(priv);
+ sysfs_remove_group(&pci_dev->dev.kobj,
+ &ipw2100_attribute_group);
+
+ free_libipw(dev, 0);
+ }
+
+ pci_iounmap(pci_dev, ioaddr);
+
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+ goto out;
+}
+
+static void ipw2100_pci_remove_one(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev = priv->net_dev;
+
+ mutex_lock(&priv->action_mutex);
+
+ priv->status &= ~STATUS_INITIALIZED;
+
+ sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+
+#ifdef CONFIG_PM
+ if (ipw2100_firmware.version)
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+ /* Take down the hardware */
+ ipw2100_down(priv);
+
+ /* Release the mutex so that the network subsystem can
+ * complete any needed calls into the driver... */
+ mutex_unlock(&priv->action_mutex);
+
+ /* Unregister the device first - this results in close()
+ * being called if the device is open. If we free storage
+ * first, then close() will crash.
+ * FIXME: remove the comment above. */
+ unregister_netdev(dev);
+
+ ipw2100_kill_works(priv);
+
+ ipw2100_queues_free(priv);
+
+ /* Free potential debugging firmware snapshot */
+ ipw2100_snapshot_free(priv);
+
+ free_irq(dev->irq, priv);
+
+ pci_iounmap(pci_dev, priv->ioaddr);
+
+ /* wiphy_unregister needs to be here, before free_libipw */
+ wiphy_unregister(priv->ieee->wdev.wiphy);
+ kfree(priv->ieee->bg_band.channels);
+ free_libipw(dev, 0);
+
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+#ifdef CONFIG_PM
+static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev = priv->net_dev;
+
+ IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name);
+
+ mutex_lock(&priv->action_mutex);
+ if (priv->status & STATUS_INITIALIZED) {
+ /* Take down the device; powers it off, etc. */
+ ipw2100_down(priv);
+ }
+
+ /* Remove the PRESENT state of the device */
+ netif_device_detach(dev);
+
+ pci_save_state(pci_dev);
+ pci_disable_device(pci_dev);
+ pci_set_power_state(pci_dev, PCI_D3hot);
+
+ priv->suspend_at = get_seconds();
+
+ mutex_unlock(&priv->action_mutex);
+
+ return 0;
+}
+
+static int ipw2100_resume(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev = priv->net_dev;
+ int err;
+ u32 val;
+
+ if (IPW2100_PM_DISABLED)
+ return 0;
+
+ mutex_lock(&priv->action_mutex);
+
+ IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name);
+
+ pci_set_power_state(pci_dev, PCI_D0);
+ err = pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
+ dev->name);
+ mutex_unlock(&priv->action_mutex);
+ return err;
+ }
+ pci_restore_state(pci_dev);
+
+ /*
+ * 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_restore_state won't help
+ * here since it only restores the first 64 bytes pci config header.
+ */
+ pci_read_config_dword(pci_dev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+ /* Set the device back into the PRESENT state; this will also wake
+ * the queue of needed */
+ netif_device_attach(dev);
+
+ priv->suspend_time = get_seconds() - priv->suspend_at;
+
+ /* Bring the device back up */
+ if (!(priv->status & STATUS_RF_KILL_SW))
+ ipw2100_up(priv, 0);
+
+ mutex_unlock(&priv->action_mutex);
+
+ return 0;
+}
+#endif
+
+static void ipw2100_shutdown(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+
+ /* Take down the device; powers it off, etc. */
+ ipw2100_down(priv);
+
+ pci_disable_device(pci_dev);
+}
+
+#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
+
+static const struct pci_device_id ipw2100_pci_id_table[] = {
+ IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */
+ IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */
+
+ IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */
+
+ IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table);
+
+static struct pci_driver ipw2100_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ipw2100_pci_id_table,
+ .probe = ipw2100_pci_init_one,
+ .remove = ipw2100_pci_remove_one,
+#ifdef CONFIG_PM
+ .suspend = ipw2100_suspend,
+ .resume = ipw2100_resume,
+#endif
+ .shutdown = ipw2100_shutdown,
+};
+
+/**
+ * Initialize the ipw2100 driver/module
+ *
+ * @returns 0 if ok, < 0 errno node con error.
+ *
+ * Note: we cannot init the /proc stuff until the PCI driver is there,
+ * or we risk an unlikely race condition on someone accessing
+ * uninitialized data in the PCI dev struct through /proc.
+ */
+static int __init ipw2100_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
+ printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
+
+ pm_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+
+ ret = pci_register_driver(&ipw2100_pci_driver);
+ if (ret)
+ goto out;
+
+#ifdef CONFIG_IPW2100_DEBUG
+ ipw2100_debug_level = debug;
+ ret = driver_create_file(&ipw2100_pci_driver.driver,
+ &driver_attr_debug_level);
+#endif
+
+out:
+ return ret;
+}
+
+/**
+ * Cleanup ipw2100 driver registration
+ */
+static void __exit ipw2100_exit(void)
+{
+ /* FIXME: IPG: check that we have no instances of the devices open */
+#ifdef CONFIG_IPW2100_DEBUG
+ driver_remove_file(&ipw2100_pci_driver.driver,
+ &driver_attr_debug_level);
+#endif
+ pci_unregister_driver(&ipw2100_pci_driver);
+ pm_qos_remove_request(&ipw2100_pm_qos_req);
+}
+
+module_init(ipw2100_init);
+module_exit(ipw2100_exit);
+
+static int ipw2100_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ if (!(priv->status & STATUS_ASSOCIATED))
+ strcpy(wrqu->name, "unassociated");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
+
+ IPW_DEBUG_WX("Name: %s\n", wrqu->name);
+ return 0;
+}
+
+static int ipw2100_wx_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct iw_freq *fwrq = &wrqu->freq;
+ int err = 0;
+
+ if (priv->ieee->iw_mode == IW_MODE_INFRA)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ /* if setting by freq convert to channel */
+ if (fwrq->e == 1) {
+ if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) {
+ int f = fwrq->m / 100000;
+ int c = 0;
+
+ while ((c < REG_MAX_CHANNEL) &&
+ (f != ipw2100_frequencies[c]))
+ c++;
+
+ /* hack to fall through */
+ fwrq->e = 0;
+ fwrq->m = c + 1;
+ }
+ }
+
+ if (fwrq->e > 0 || fwrq->m > 1000) {
+ err = -EOPNOTSUPP;
+ goto done;
+ } else { /* Set the channel */
+ IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m);
+ err = ipw2100_set_channel(priv, fwrq->m, 0);
+ }
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ wrqu->freq.e = 0;
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured CHANNEL then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_CHANNEL ||
+ priv->status & STATUS_ASSOCIATED)
+ wrqu->freq.m = priv->channel;
+ else
+ wrqu->freq.m = 0;
+
+ IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel);
+ return 0;
+
+}
+
+static int ipw2100_wx_set_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err = 0;
+
+ IPW_DEBUG_WX("SET Mode -> %d\n", wrqu->mode);
+
+ if (wrqu->mode == priv->ieee->iw_mode)
+ return 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ switch (wrqu->mode) {
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+ break;
+#endif /* CONFIG_IPW2100_MONITOR */
+ case IW_MODE_ADHOC:
+ err = ipw2100_switch_mode(priv, IW_MODE_ADHOC);
+ break;
+ case IW_MODE_INFRA:
+ case IW_MODE_AUTO:
+ default:
+ err = ipw2100_switch_mode(priv, IW_MODE_INFRA);
+ break;
+ }
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ wrqu->mode = priv->ieee->iw_mode;
+ IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode);
+
+ return 0;
+}
+
+#define POWER_MODES 5
+
+/* Values are in microsecond */
+static const s32 timeout_duration[POWER_MODES] = {
+ 350000,
+ 250000,
+ 75000,
+ 37000,
+ 25000,
+};
+
+static const s32 period_duration[POWER_MODES] = {
+ 400000,
+ 700000,
+ 1000000,
+ 1000000,
+ 1000000
+};
+
+static int ipw2100_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ u16 val;
+ int i, level;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* Let's try to keep this struct in the same order as in
+ * linux/include/wireless.h
+ */
+
+ /* TODO: See what values we can set, and remove the ones we can't
+ * set, or fill them with some default data.
+ */
+
+ /* ~5 Mb/s real (802.11b) */
+ range->throughput = 5 * 1000 * 1000;
+
+// range->sensitivity; /* signal level threshold range */
+
+ range->max_qual.qual = 100;
+ /* TODO: Find real max RSSI and stick here */
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
+ /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+ range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+
+ range->num_bitrates = RATE_COUNT;
+
+ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) {
+ range->bitrate[i] = ipw2100_bg_rates[i].bitrate * 100 * 1000;
+ }
+
+ range->min_rts = MIN_RTS_THRESHOLD;
+ range->max_rts = MAX_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->min_pmp = period_duration[0]; /* Minimal PM period */
+ range->max_pmp = period_duration[POWER_MODES - 1]; /* Maximal PM period */
+ range->min_pmt = timeout_duration[POWER_MODES - 1]; /* Minimal PM timeout */
+ range->max_pmt = timeout_duration[0]; /* Maximal PM timeout */
+
+ /* How to decode max/min PM period */
+ range->pmp_flags = IW_POWER_PERIOD;
+ /* How to decode max/min PM period */
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ /* What PM options are supported */
+ range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13; /* Different token sizes */
+ range->num_encoding_sizes = 2; /* Number of entry in the list */
+ range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */
+// range->encoding_login_index; /* token index for login token */
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ range->txpower_capa = IW_TXPOW_DBM;
+ range->num_txpower = IW_MAX_TXPOWER;
+ for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16);
+ i < IW_MAX_TXPOWER;
+ i++, level -=
+ ((IPW_TX_POWER_MAX_DBM -
+ IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1))
+ range->txpower[i] = level / 16;
+ } else {
+ range->txpower_capa = 0;
+ range->num_txpower = 0;
+ }
+
+ /* Set the Wireless Extension versions */
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 18;
+
+// range->retry_capa; /* What retry options are supported */
+// range->retry_flags; /* How to decode max/min retry limit */
+// range->r_time_flags; /* How to decode max/min retry life */
+// range->min_retry; /* Minimal number of retries */
+// range->max_retry; /* Maximal number of retries */
+// range->min_r_time; /* Minimal retry lifetime */
+// range->max_r_time; /* Maximal retry lifetime */
+
+ range->num_channels = FREQ_COUNT;
+
+ val = 0;
+ for (i = 0; i < FREQ_COUNT; i++) {
+ // TODO: Include only legal frequencies for some countries
+// if (local->channel_mask & (1 << i)) {
+ range->freq[val].i = i + 1;
+ range->freq[val].m = ipw2100_frequencies[i] * 100000;
+ range->freq[val].e = 1;
+ val++;
+// }
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+
+ /* Event capability (kernel + driver) */
+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+ IW_EVENT_CAPA_MASK(SIOCGIWAP));
+ range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+ IPW_DEBUG_WX("GET Range\n");
+
+ return 0;
+}
+
+static int ipw2100_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err = 0;
+
+ // sanity checks
+ if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) ||
+ is_zero_ether_addr(wrqu->ap_addr.sa_data)) {
+ /* we disable mandatory BSSID association */
+ IPW_DEBUG_WX("exit - disable mandatory BSSID\n");
+ priv->config &= ~CFG_STATIC_BSSID;
+ err = ipw2100_set_mandatory_bssid(priv, NULL, 0);
+ goto done;
+ }
+
+ priv->config |= CFG_STATIC_BSSID;
+ memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+ err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0);
+
+ IPW_DEBUG_WX("SET BSSID -> %pM\n", wrqu->ap_addr.sa_data);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured BSSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) {
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ } else
+ eth_zero_addr(wrqu->ap_addr.sa_data);
+
+ IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data);
+ return 0;
+}
+
+static int ipw2100_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ char *essid = ""; /* ANY */
+ int length = 0;
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ length = wrqu->essid.length;
+ essid = extra;
+ }
+
+ if (length == 0) {
+ IPW_DEBUG_WX("Setting ESSID to ANY\n");
+ priv->config &= ~CFG_STATIC_ESSID;
+ err = ipw2100_set_essid(priv, NULL, 0, 0);
+ goto done;
+ }
+
+ length = min(length, IW_ESSID_MAX_SIZE);
+
+ priv->config |= CFG_STATIC_ESSID;
+
+ if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
+ IPW_DEBUG_WX("ESSID set to current ESSID.\n");
+ err = 0;
+ goto done;
+ }
+
+ IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, essid, length);
+
+ priv->essid_len = length;
+ memcpy(priv->essid, essid, priv->essid_len);
+
+ err = ipw2100_set_essid(priv, essid, length, 0);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured ESSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) {
+ IPW_DEBUG_WX("Getting essid: '%*pE'\n",
+ priv->essid_len, priv->essid);
+ memcpy(extra, priv->essid, priv->essid_len);
+ wrqu->essid.length = priv->essid_len;
+ wrqu->essid.flags = 1; /* active */
+ } else {
+ IPW_DEBUG_WX("Getting essid: ANY\n");
+ wrqu->essid.length = 0;
+ wrqu->essid.flags = 0; /* active */
+ }
+
+ return 0;
+}
+
+static int ipw2100_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+
+ wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
+ memset(priv->nick, 0, sizeof(priv->nick));
+ memcpy(priv->nick, extra, wrqu->data.length);
+
+ IPW_DEBUG_WX("SET Nickname -> %s\n", priv->nick);
+
+ return 0;
+}
+
+static int ipw2100_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ wrqu->data.length = strlen(priv->nick);
+ memcpy(extra, priv->nick, wrqu->data.length);
+ wrqu->data.flags = 1; /* active */
+
+ IPW_DEBUG_WX("GET Nickname -> %s\n", extra);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ u32 target_rate = wrqu->bitrate.value;
+ u32 rate;
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ rate = 0;
+
+ if (target_rate == 1000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 1000000))
+ rate |= TX_RATE_1_MBIT;
+ if (target_rate == 2000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 2000000))
+ rate |= TX_RATE_2_MBIT;
+ if (target_rate == 5500000 ||
+ (!wrqu->bitrate.fixed && target_rate > 5500000))
+ rate |= TX_RATE_5_5_MBIT;
+ if (target_rate == 11000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 11000000))
+ rate |= TX_RATE_11_MBIT;
+ if (rate == 0)
+ rate = DEFAULT_TX_RATES;
+
+ err = ipw2100_set_tx_rates(priv, rate, 0);
+
+ IPW_DEBUG_WX("SET Rate -> %04X\n", rate);
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int val;
+ unsigned int len = sizeof(val);
+ int err = 0;
+
+ if (!(priv->status & STATUS_ENABLED) ||
+ priv->status & STATUS_RF_KILL_MASK ||
+ !(priv->status & STATUS_ASSOCIATED)) {
+ wrqu->bitrate.value = 0;
+ return 0;
+ }
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len);
+ if (err) {
+ IPW_DEBUG_WX("failed querying ordinals.\n");
+ goto done;
+ }
+
+ switch (val & TX_RATE_MASK) {
+ case TX_RATE_1_MBIT:
+ wrqu->bitrate.value = 1000000;
+ break;
+ case TX_RATE_2_MBIT:
+ wrqu->bitrate.value = 2000000;
+ break;
+ case TX_RATE_5_5_MBIT:
+ wrqu->bitrate.value = 5500000;
+ break;
+ case TX_RATE_11_MBIT:
+ wrqu->bitrate.value = 11000000;
+ break;
+ default:
+ wrqu->bitrate.value = 0;
+ }
+
+ IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int value, err;
+
+ /* Auto RTS not yet supported */
+ if (wrqu->rts.fixed == 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->rts.disabled)
+ value = priv->rts_threshold | RTS_DISABLED;
+ else {
+ if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) {
+ err = -EINVAL;
+ goto done;
+ }
+ value = wrqu->rts.value;
+ }
+
+ err = ipw2100_set_rts_threshold(priv, value);
+
+ IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X\n", value);
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED;
+ wrqu->rts.fixed = 1; /* no auto select */
+
+ /* If RTS is set to the default value, then it is disabled */
+ wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0;
+
+ IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X\n", wrqu->rts.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err = 0, value;
+
+ if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled))
+ return -EINPROGRESS;
+
+ if (priv->ieee->iw_mode != IW_MODE_ADHOC)
+ return 0;
+
+ if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
+ return -EINVAL;
+
+ if (wrqu->txpower.fixed == 0)
+ value = IPW_TX_POWER_DEFAULT;
+ else {
+ if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM ||
+ wrqu->txpower.value > IPW_TX_POWER_MAX_DBM)
+ return -EINVAL;
+
+ value = wrqu->txpower.value;
+ }
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ err = ipw2100_set_tx_power(priv, value);
+
+ IPW_DEBUG_WX("SET TX Power -> %d\n", value);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
+
+ if (priv->tx_power == IPW_TX_POWER_DEFAULT) {
+ wrqu->txpower.fixed = 0;
+ wrqu->txpower.value = IPW_TX_POWER_MAX_DBM;
+ } else {
+ wrqu->txpower.fixed = 1;
+ wrqu->txpower.value = priv->tx_power;
+ }
+
+ wrqu->txpower.flags = IW_TXPOW_DBM;
+
+ IPW_DEBUG_WX("GET TX Power -> %d\n", wrqu->txpower.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ if (!wrqu->frag.fixed)
+ return -EINVAL;
+
+ if (wrqu->frag.disabled) {
+ priv->frag_threshold |= FRAG_DISABLED;
+ priv->ieee->fts = DEFAULT_FTS;
+ } else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+
+ priv->ieee->fts = wrqu->frag.value & ~0x1;
+ priv->frag_threshold = priv->ieee->fts;
+ }
+
+ IPW_DEBUG_WX("SET Frag Threshold -> %d\n", priv->ieee->fts);
+
+ return 0;
+}
+
+static int ipw2100_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED;
+ wrqu->frag.fixed = 0; /* no auto select */
+ wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0;
+
+ IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err = 0;
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
+ return -EINVAL;
+
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
+ return 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_SHORT) {
+ err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+ IPW_DEBUG_WX("SET Short Retry Limit -> %d\n",
+ wrqu->retry.value);
+ goto done;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+ IPW_DEBUG_WX("SET Long Retry Limit -> %d\n",
+ wrqu->retry.value);
+ goto done;
+ }
+
+ err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+ if (!err)
+ err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+
+ IPW_DEBUG_WX("SET Both Retry Limits -> %d\n", wrqu->retry.value);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ wrqu->retry.disabled = 0; /* can't be disabled */
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
+ return -EINVAL;
+
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ wrqu->retry.value = priv->long_retry_limit;
+ } else {
+ wrqu->retry.flags =
+ (priv->short_retry_limit !=
+ priv->long_retry_limit) ?
+ IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
+
+ wrqu->retry.value = priv->short_retry_limit;
+ }
+
+ IPW_DEBUG_WX("GET Retry -> %d\n", wrqu->retry.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ IPW_DEBUG_WX("Initiating scan...\n");
+
+ priv->user_requested_scan = 1;
+ if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) {
+ IPW_DEBUG_WX("Start scan failed.\n");
+
+ /* TODO: Mark a scan as pending so when hardware initialized
+ * a scan starts */
+ }
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ return libipw_wx_get_scan(priv->ieee, info, wrqu, extra);
+}
+
+/*
+ * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c
+ */
+static int ipw2100_wx_set_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ /*
+ * No check of STATUS_INITIALIZED required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ return libipw_wx_set_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ return libipw_wx_get_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->power.disabled) {
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+ IPW_DEBUG_WX("SET Power Management Mode -> off\n");
+ goto done;
+ }
+
+ switch (wrqu->power.flags & IW_POWER_MODE) {
+ case IW_POWER_ON: /* If not specified */
+ case IW_POWER_MODE: /* If set all mask */
+ case IW_POWER_ALL_R: /* If explicitly state all */
+ break;
+ default: /* Otherwise we don't support it */
+ IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
+ wrqu->power.flags);
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+ /* If the user hasn't specified a power management mode yet, default
+ * to BATTERY */
+ priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+ err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
+
+ IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+
+}
+
+static int ipw2100_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED))
+ wrqu->power.disabled = 1;
+ else {
+ wrqu->power.disabled = 0;
+ wrqu->power.flags = 0;
+ }
+
+ IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
+
+ return 0;
+}
+
+/*
+ * WE-18 WPA support
+ */
+
+/* SIOCSIWGENIE */
+static int ipw2100_wx_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct libipw_device *ieee = priv->ieee;
+ u8 *buf;
+
+ if (!ieee->wpa_enabled)
+ return -EOPNOTSUPP;
+
+ if (wrqu->data.length > MAX_WPA_IE_LEN ||
+ (wrqu->data.length && extra == NULL))
+ return -EINVAL;
+
+ if (wrqu->data.length) {
+ buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = wrqu->data.length;
+ } else {
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+
+ ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
+
+ return 0;
+}
+
+/* SIOCGIWGENIE */
+static int ipw2100_wx_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct libipw_device *ieee = priv->ieee;
+
+ if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
+ wrqu->data.length = 0;
+ return 0;
+ }
+
+ if (wrqu->data.length < ieee->wpa_ie_len)
+ return -E2BIG;
+
+ wrqu->data.length = ieee->wpa_ie_len;
+ memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
+
+ return 0;
+}
+
+/* SIOCSIWAUTH */
+static int ipw2100_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct libipw_device *ieee = priv->ieee;
+ struct iw_param *param = &wrqu->param;
+ struct lib80211_crypt_data *crypt;
+ unsigned long flags;
+ int ret = 0;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * ipw2200 does not use these parameters
+ */
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
+ if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
+ break;
+
+ flags = crypt->ops->get_flags(crypt->priv);
+
+ if (param->value)
+ flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
+ else
+ flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
+
+ crypt->ops->set_flags(flags, crypt->priv);
+
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:{
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ struct libipw_security sec = {
+ .flags = SEC_ENABLED,
+ .enabled = param->value,
+ };
+ priv->ieee->drop_unencrypted = param->value;
+ /* We only change SEC_LEVEL for open mode. Others
+ * are set by ipw_wpa_set_encryption.
+ */
+ if (!param->value) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_0;
+ } else {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ }
+ if (priv->ieee->set_security)
+ priv->ieee->set_security(priv->ieee->dev, &sec);
+ break;
+ }
+
+ case IW_AUTH_80211_AUTH_ALG:
+ ret = ipw2100_wpa_set_auth_algs(priv, param->value);
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ ret = ipw2100_wpa_enable(priv, param->value);
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ieee->ieee802_1x = param->value;
+ break;
+
+ //case IW_AUTH_ROAMING_CONTROL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ ieee->privacy_invoked = param->value;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+/* SIOCGIWAUTH */
+static int ipw2100_wx_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct libipw_device *ieee = priv->ieee;
+ struct lib80211_crypt_data *crypt;
+ struct iw_param *param = &wrqu->param;
+ int ret = 0;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * wpa_supplicant will control these internally
+ */
+ ret = -EOPNOTSUPP;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
+ if (!crypt || !crypt->ops->get_flags) {
+ IPW_DEBUG_WARNING("Can't get TKIP countermeasures: "
+ "crypt not set!\n");
+ break;
+ }
+
+ param->value = (crypt->ops->get_flags(crypt->priv) &
+ IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
+
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ param->value = ieee->drop_unencrypted;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ param->value = priv->ieee->sec.auth_mode;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = ieee->wpa_enabled;
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ param->value = ieee->ieee802_1x;
+ break;
+
+ case IW_AUTH_ROAMING_CONTROL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ param->value = ieee->privacy_invoked;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/* SIOCSIWENCODEEXT */
+static int ipw2100_wx_set_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra);
+}
+
+/* SIOCGIWENCODEEXT */
+static int ipw2100_wx_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra);
+}
+
+/* SIOCSIWMLME */
+static int ipw2100_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ __le16 reason;
+
+ reason = cpu_to_le16(mlme->reason_code);
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ // silently ignore
+ break;
+
+ case IW_MLME_DISASSOC:
+ ipw2100_disassociate_bssid(priv);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/*
+ *
+ * IWPRIV handlers
+ *
+ */
+#ifdef CONFIG_IPW2100_MONITOR
+static int ipw2100_wx_set_promisc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (enable) {
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ err = ipw2100_set_channel(priv, parms[1], 0);
+ goto done;
+ }
+ priv->channel = parms[1];
+ err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+ } else {
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ err = ipw2100_switch_mode(priv, priv->last_mode);
+ }
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ if (priv->status & STATUS_INITIALIZED)
+ schedule_reset(priv);
+ return 0;
+}
+
+#endif
+
+static int ipw2100_wx_set_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err = 0, mode = *(int *)extra;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if ((mode < 0) || (mode > POWER_MODES))
+ mode = IPW_POWER_AUTO;
+
+ if (IPW_POWER_LEVEL(priv->power_mode) != mode)
+ err = ipw2100_set_power_mode(priv, mode);
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+#define MAX_POWER_STRING 80
+static int ipw2100_wx_get_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int level = IPW_POWER_LEVEL(priv->power_mode);
+ s32 timeout, period;
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (Off)", level);
+ } else {
+ switch (level) {
+ case IPW_POWER_MODE_CAM:
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (None)", level);
+ break;
+ case IPW_POWER_AUTO:
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (Auto)", level);
+ break;
+ default:
+ timeout = timeout_duration[level - 1] / 1000;
+ period = period_duration[level - 1] / 1000;
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d "
+ "(Timeout %dms, Period %dms)",
+ level, timeout, period);
+ }
+ }
+
+ wrqu->data.length = strlen(extra) + 1;
+
+ return 0;
+}
+
+static int ipw2100_wx_set_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err, mode = *(int *)extra;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (mode == 1)
+ priv->config |= CFG_LONG_PREAMBLE;
+ else if (mode == 0)
+ priv->config &= ~CFG_LONG_PREAMBLE;
+ else {
+ err = -EINVAL;
+ goto done;
+ }
+
+ err = ipw2100_system_config(priv, 0);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ if (priv->config & CFG_LONG_PREAMBLE)
+ snprintf(wrqu->name, IFNAMSIZ, "long (1)");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
+
+ return 0;
+}
+
+#ifdef CONFIG_IPW2100_MONITOR
+static int ipw2100_wx_set_crc_check(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ int err, mode = *(int *)extra;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (mode == 1)
+ priv->config |= CFG_CRC_CHECK;
+ else if (mode == 0)
+ priv->config &= ~CFG_CRC_CHECK;
+ else {
+ err = -EINVAL;
+ goto done;
+ }
+ err = 0;
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_crc_check(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+
+ if (priv->config & CFG_CRC_CHECK)
+ snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)");
+
+ return 0;
+}
+#endif /* CONFIG_IPW2100_MONITOR */
+
+static iw_handler ipw2100_wx_handlers[] = {
+ IW_HANDLER(SIOCGIWNAME, ipw2100_wx_get_name),
+ IW_HANDLER(SIOCSIWFREQ, ipw2100_wx_set_freq),
+ IW_HANDLER(SIOCGIWFREQ, ipw2100_wx_get_freq),
+ IW_HANDLER(SIOCSIWMODE, ipw2100_wx_set_mode),
+ IW_HANDLER(SIOCGIWMODE, ipw2100_wx_get_mode),
+ IW_HANDLER(SIOCGIWRANGE, ipw2100_wx_get_range),
+ IW_HANDLER(SIOCSIWAP, ipw2100_wx_set_wap),
+ IW_HANDLER(SIOCGIWAP, ipw2100_wx_get_wap),
+ IW_HANDLER(SIOCSIWMLME, ipw2100_wx_set_mlme),
+ IW_HANDLER(SIOCSIWSCAN, ipw2100_wx_set_scan),
+ IW_HANDLER(SIOCGIWSCAN, ipw2100_wx_get_scan),
+ IW_HANDLER(SIOCSIWESSID, ipw2100_wx_set_essid),
+ IW_HANDLER(SIOCGIWESSID, ipw2100_wx_get_essid),
+ IW_HANDLER(SIOCSIWNICKN, ipw2100_wx_set_nick),
+ IW_HANDLER(SIOCGIWNICKN, ipw2100_wx_get_nick),
+ IW_HANDLER(SIOCSIWRATE, ipw2100_wx_set_rate),
+ IW_HANDLER(SIOCGIWRATE, ipw2100_wx_get_rate),
+ IW_HANDLER(SIOCSIWRTS, ipw2100_wx_set_rts),
+ IW_HANDLER(SIOCGIWRTS, ipw2100_wx_get_rts),
+ IW_HANDLER(SIOCSIWFRAG, ipw2100_wx_set_frag),
+ IW_HANDLER(SIOCGIWFRAG, ipw2100_wx_get_frag),
+ IW_HANDLER(SIOCSIWTXPOW, ipw2100_wx_set_txpow),
+ IW_HANDLER(SIOCGIWTXPOW, ipw2100_wx_get_txpow),
+ IW_HANDLER(SIOCSIWRETRY, ipw2100_wx_set_retry),
+ IW_HANDLER(SIOCGIWRETRY, ipw2100_wx_get_retry),
+ IW_HANDLER(SIOCSIWENCODE, ipw2100_wx_set_encode),
+ IW_HANDLER(SIOCGIWENCODE, ipw2100_wx_get_encode),
+ IW_HANDLER(SIOCSIWPOWER, ipw2100_wx_set_power),
+ IW_HANDLER(SIOCGIWPOWER, ipw2100_wx_get_power),
+ IW_HANDLER(SIOCSIWGENIE, ipw2100_wx_set_genie),
+ IW_HANDLER(SIOCGIWGENIE, ipw2100_wx_get_genie),
+ IW_HANDLER(SIOCSIWAUTH, ipw2100_wx_set_auth),
+ IW_HANDLER(SIOCGIWAUTH, ipw2100_wx_get_auth),
+ IW_HANDLER(SIOCSIWENCODEEXT, ipw2100_wx_set_encodeext),
+ IW_HANDLER(SIOCGIWENCODEEXT, ipw2100_wx_get_encodeext),
+};
+
+#define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV
+#define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1
+#define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2
+#define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3
+#define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4
+#define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5
+#define IPW2100_PRIV_SET_CRC_CHECK SIOCIWFIRSTPRIV+6
+#define IPW2100_PRIV_GET_CRC_CHECK SIOCIWFIRSTPRIV+7
+
+static const struct iw_priv_args ipw2100_private_args[] = {
+
+#ifdef CONFIG_IPW2100_MONITOR
+ {
+ IPW2100_PRIV_SET_MONITOR,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
+ {
+ IPW2100_PRIV_RESET,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
+#endif /* CONFIG_IPW2100_MONITOR */
+
+ {
+ IPW2100_PRIV_SET_POWER,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"},
+ {
+ IPW2100_PRIV_GET_POWER,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING,
+ "get_power"},
+ {
+ IPW2100_PRIV_SET_LONGPREAMBLE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
+ {
+ IPW2100_PRIV_GET_LONGPREAMBLE,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"},
+#ifdef CONFIG_IPW2100_MONITOR
+ {
+ IPW2100_PRIV_SET_CRC_CHECK,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"},
+ {
+ IPW2100_PRIV_GET_CRC_CHECK,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"},
+#endif /* CONFIG_IPW2100_MONITOR */
+};
+
+static iw_handler ipw2100_private_handler[] = {
+#ifdef CONFIG_IPW2100_MONITOR
+ ipw2100_wx_set_promisc,
+ ipw2100_wx_reset,
+#else /* CONFIG_IPW2100_MONITOR */
+ NULL,
+ NULL,
+#endif /* CONFIG_IPW2100_MONITOR */
+ ipw2100_wx_set_powermode,
+ ipw2100_wx_get_powermode,
+ ipw2100_wx_set_preamble,
+ ipw2100_wx_get_preamble,
+#ifdef CONFIG_IPW2100_MONITOR
+ ipw2100_wx_set_crc_check,
+ ipw2100_wx_get_crc_check,
+#else /* CONFIG_IPW2100_MONITOR */
+ NULL,
+ NULL,
+#endif /* CONFIG_IPW2100_MONITOR */
+};
+
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ * Also called by SIOCGIWSTATS
+ */
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev)
+{
+ enum {
+ POOR = 30,
+ FAIR = 60,
+ GOOD = 80,
+ VERY_GOOD = 90,
+ EXCELLENT = 95,
+ PERFECT = 100
+ };
+ int rssi_qual;
+ int tx_qual;
+ int beacon_qual;
+ int quality;
+
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ struct iw_statistics *wstats;
+ u32 rssi, tx_retries, missed_beacons, tx_failures;
+ u32 ord_len = sizeof(u32);
+
+ if (!priv)
+ return (struct iw_statistics *)NULL;
+
+ wstats = &priv->wstats;
+
+ /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
+ * ipw2100_wx_wireless_stats seems to be called before fw is
+ * initialized. STATUS_ASSOCIATED will only be set if the hw is up
+ * and associated; if not associcated, the values are all meaningless
+ * anyway, so set them all to NULL and INVALID */
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->miss.beacon = 0;
+ wstats->discard.retries = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ return wstats;
+ }
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS,
+ &missed_beacons, &ord_len))
+ goto fail_get_ordinal;
+
+ /* If we don't have a connection the quality and level is 0 */
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ } else {
+ if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR,
+ &rssi, &ord_len))
+ goto fail_get_ordinal;
+ wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+ if (rssi < 10)
+ rssi_qual = rssi * POOR / 10;
+ else if (rssi < 15)
+ rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR;
+ else if (rssi < 20)
+ rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR;
+ else if (rssi < 30)
+ rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) /
+ 10 + GOOD;
+ else
+ rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) /
+ 10 + VERY_GOOD;
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES,
+ &tx_retries, &ord_len))
+ goto fail_get_ordinal;
+
+ if (tx_retries > 75)
+ tx_qual = (90 - tx_retries) * POOR / 15;
+ else if (tx_retries > 70)
+ tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
+ else if (tx_retries > 65)
+ tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
+ else if (tx_retries > 50)
+ tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
+ 15 + GOOD;
+ else
+ tx_qual = (50 - tx_retries) *
+ (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
+
+ if (missed_beacons > 50)
+ beacon_qual = (60 - missed_beacons) * POOR / 10;
+ else if (missed_beacons > 40)
+ beacon_qual = (50 - missed_beacons) * (FAIR - POOR) /
+ 10 + POOR;
+ else if (missed_beacons > 32)
+ beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) /
+ 18 + FAIR;
+ else if (missed_beacons > 20)
+ beacon_qual = (32 - missed_beacons) *
+ (VERY_GOOD - GOOD) / 20 + GOOD;
+ else
+ beacon_qual = (20 - missed_beacons) *
+ (PERFECT - VERY_GOOD) / 20 + VERY_GOOD;
+
+ quality = min(tx_qual, rssi_qual);
+ quality = min(beacon_qual, quality);
+
+#ifdef CONFIG_IPW2100_DEBUG
+ if (beacon_qual == quality)
+ IPW_DEBUG_WX("Quality clamped by Missed Beacons\n");
+ else if (tx_qual == quality)
+ IPW_DEBUG_WX("Quality clamped by Tx Retries\n");
+ else if (quality != 100)
+ IPW_DEBUG_WX("Quality clamped by Signal Strength\n");
+ else
+ IPW_DEBUG_WX("Quality not clamped.\n");
+#endif
+
+ wstats->qual.qual = quality;
+ wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+ }
+
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
+
+ /* FIXME: this is percent and not a # */
+ wstats->miss.beacon = missed_beacons;
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES,
+ &tx_failures, &ord_len))
+ goto fail_get_ordinal;
+ wstats->discard.retries = tx_failures;
+
+ return wstats;
+
+ fail_get_ordinal:
+ IPW_DEBUG_WX("failed querying ordinals.\n");
+
+ return (struct iw_statistics *)NULL;
+}
+
+static struct iw_handler_def ipw2100_wx_handler_def = {
+ .standard = ipw2100_wx_handlers,
+ .num_standard = ARRAY_SIZE(ipw2100_wx_handlers),
+ .num_private = ARRAY_SIZE(ipw2100_private_handler),
+ .num_private_args = ARRAY_SIZE(ipw2100_private_args),
+ .private = (iw_handler *) ipw2100_private_handler,
+ .private_args = (struct iw_priv_args *)ipw2100_private_args,
+ .get_wireless_stats = ipw2100_wx_wireless_stats,
+};
+
+static void ipw2100_wx_event_work(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, wx_event_work.work);
+ union iwreq_data wrqu;
+ unsigned int len = ETH_ALEN;
+
+ if (priv->status & STATUS_STOPPING)
+ return;
+
+ mutex_lock(&priv->action_mutex);
+
+ IPW_DEBUG_WX("enter\n");
+
+ mutex_unlock(&priv->action_mutex);
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+ /* Fetch BSSID from the hardware */
+ if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) ||
+ priv->status & STATUS_RF_KILL_MASK ||
+ ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+ &priv->bssid, &len)) {
+ eth_zero_addr(wrqu.ap_addr.sa_data);
+ } else {
+ /* We now have the BSSID, so can finish setting to the full
+ * associated state */
+ memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN);
+ priv->status &= ~STATUS_ASSOCIATING;
+ priv->status |= STATUS_ASSOCIATED;
+ netif_carrier_on(priv->net_dev);
+ netif_wake_queue(priv->net_dev);
+ }
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_WX("Configuring ESSID\n");
+ mutex_lock(&priv->action_mutex);
+ /* This is a disassociation event, so kick the firmware to
+ * look for another AP */
+ if (priv->config & CFG_STATIC_ESSID)
+ ipw2100_set_essid(priv, priv->essid, priv->essid_len,
+ 0);
+ else
+ ipw2100_set_essid(priv, NULL, 0, 0);
+ mutex_unlock(&priv->action_mutex);
+ }
+
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+#define IPW2100_FW_MAJOR_VERSION 1
+#define IPW2100_FW_MINOR_VERSION 3
+
+#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8)
+#define IPW2100_FW_MAJOR(x) (x & 0xff)
+
+#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \
+ IPW2100_FW_MAJOR_VERSION)
+
+#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \
+"." __stringify(IPW2100_FW_MINOR_VERSION)
+
+#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw"
+
+/*
+
+BINARY FIRMWARE HEADER FORMAT
+
+offset length desc
+0 2 version
+2 2 mode == 0:BSS,1:IBSS,2:MONITOR
+4 4 fw_len
+8 4 uc_len
+C fw_len firmware data
+12 + fw_len uc_len microcode data
+
+*/
+
+struct ipw2100_fw_header {
+ short version;
+ short mode;
+ unsigned int fw_size;
+ unsigned int uc_size;
+} __packed;
+
+static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
+{
+ struct ipw2100_fw_header *h =
+ (struct ipw2100_fw_header *)fw->fw_entry->data;
+
+ if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
+ printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
+ "(detected version id of %u). "
+ "See Documentation/networking/README.ipw2100\n",
+ h->version);
+ return 1;
+ }
+
+ fw->version = h->version;
+ fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header);
+ fw->fw.size = h->fw_size;
+ fw->uc.data = fw->fw.data + h->fw_size;
+ fw->uc.size = h->uc_size;
+
+ return 0;
+}
+
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ char *fw_name;
+ int rc;
+
+ IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n",
+ priv->net_dev->name);
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ fw_name = IPW2100_FW_NAME("-i");
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ fw_name = IPW2100_FW_NAME("-p");
+ break;
+#endif
+ case IW_MODE_INFRA:
+ default:
+ fw_name = IPW2100_FW_NAME("");
+ break;
+ }
+
+ rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev);
+
+ if (rc < 0) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: Firmware '%s' not available or load failed.\n",
+ priv->net_dev->name, fw_name);
+ return rc;
+ }
+ IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data,
+ fw->fw_entry->size);
+
+ ipw2100_mod_firmware_load(fw);
+
+ return 0;
+}
+
+MODULE_FIRMWARE(IPW2100_FW_NAME("-i"));
+#ifdef CONFIG_IPW2100_MONITOR
+MODULE_FIRMWARE(IPW2100_FW_NAME("-p"));
+#endif
+MODULE_FIRMWARE(IPW2100_FW_NAME(""));
+
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ fw->version = 0;
+ release_firmware(fw->fw_entry);
+ fw->fw_entry = NULL;
+}
+
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+ size_t max)
+{
+ char ver[MAX_FW_VERSION_LEN];
+ u32 len = MAX_FW_VERSION_LEN;
+ u32 tmp;
+ int i;
+ /* firmware version is an ascii string (max len of 14) */
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len))
+ return -EIO;
+ tmp = max;
+ if (len >= max)
+ len = max - 1;
+ for (i = 0; i < len; i++)
+ buf[i] = ver[i];
+ buf[i] = '\0';
+ return tmp;
+}
+
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+ size_t max)
+{
+ u32 ver;
+ u32 len = sizeof(ver);
+ /* microcode version is a 32 bit integer */
+ if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len))
+ return -EIO;
+ return snprintf(buf, max, "%08X", ver);
+}
+
+/*
+ * On exit, the firmware will have been freed from the fw list
+ */
+static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw)
+{
+ /* firmware is constructed of N contiguous entries, each entry is
+ * structured as:
+ *
+ * offset sie desc
+ * 0 4 address to write to
+ * 4 2 length of data run
+ * 6 length data
+ */
+ unsigned int addr;
+ unsigned short len;
+
+ const unsigned char *firmware_data = fw->fw.data;
+ unsigned int firmware_data_left = fw->fw.size;
+
+ while (firmware_data_left > 0) {
+ addr = *(u32 *) (firmware_data);
+ firmware_data += 4;
+ firmware_data_left -= 4;
+
+ len = *(u16 *) (firmware_data);
+ firmware_data += 2;
+ firmware_data_left -= 2;
+
+ if (len > 32) {
+ printk(KERN_ERR DRV_NAME ": "
+ "Invalid firmware run-length of %d bytes\n",
+ len);
+ return -EINVAL;
+ }
+
+ write_nic_memory(priv->net_dev, addr, len, firmware_data);
+ firmware_data += len;
+ firmware_data_left -= len;
+ }
+
+ return 0;
+}
+
+struct symbol_alive_response {
+ u8 cmd_id;
+ u8 seq_num;
+ u8 ucode_rev;
+ u8 eeprom_valid;
+ u16 valid_flags;
+ u8 IEEE_addr[6];
+ u16 flags;
+ u16 pcb_rev;
+ u16 clock_settle_time; // 1us LSB
+ u16 powerup_settle_time; // 1us LSB
+ u16 hop_settle_time; // 1us LSB
+ u8 date[3]; // month, day, year
+ u8 time[2]; // hours, minutes
+ u8 ucode_valid;
+};
+
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ struct net_device *dev = priv->net_dev;
+ const unsigned char *microcode_data = fw->uc.data;
+ unsigned int microcode_data_left = fw->uc.size;
+ void __iomem *reg = priv->ioaddr;
+
+ struct symbol_alive_response response;
+ int i, j;
+ u8 data;
+
+ /* Symbol control */
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+ readl(reg);
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+ readl(reg);
+
+ /* HW config */
+ write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
+ readl(reg);
+ write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
+ readl(reg);
+
+ /* EN_CS_ACCESS bit to reset control store pointer */
+ write_nic_byte(dev, 0x210000, 0x40);
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x40);
+ readl(reg);
+
+ /* copy microcode from buffer into Symbol */
+
+ while (microcode_data_left > 0) {
+ write_nic_byte(dev, 0x210010, *microcode_data++);
+ write_nic_byte(dev, 0x210010, *microcode_data++);
+ microcode_data_left -= 2;
+ }
+
+ /* EN_CS_ACCESS bit to reset the control store pointer */
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl(reg);
+
+ /* Enable System (Reg 0)
+ * first enable causes garbage in RX FIFO */
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x80);
+ readl(reg);
+
+ /* Reset External Baseband Reg */
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+ readl(reg);
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+ readl(reg);
+
+ /* HW Config (Reg 5) */
+ write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
+ readl(reg);
+ write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
+ readl(reg);
+
+ /* Enable System (Reg 0)
+ * second enable should be OK */
+ write_nic_byte(dev, 0x210000, 0x00); // clear enable system
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x80); // set enable system
+
+ /* check Symbol is enabled - upped this from 5 as it wasn't always
+ * catching the update */
+ for (i = 0; i < 10; i++) {
+ udelay(10);
+
+ /* check Dino is enabled bit */
+ read_nic_byte(dev, 0x210000, &data);
+ if (data & 0x1)
+ break;
+ }
+
+ if (i == 10) {
+ printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n",
+ dev->name);
+ return -EIO;
+ }
+
+ /* Get Symbol alive response */
+ for (i = 0; i < 30; i++) {
+ /* Read alive response structure */
+ for (j = 0;
+ j < (sizeof(struct symbol_alive_response) >> 1); j++)
+ read_nic_word(dev, 0x210004, ((u16 *) & response) + j);
+
+ if ((response.cmd_id == 1) && (response.ucode_valid == 0x1))
+ break;
+ udelay(10);
+ }
+
+ if (i == 30) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: No response from Symbol - hw not alive\n",
+ dev->name);
+ printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response));
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/intel/ipw2x00/ipw2100.h
index 193947865efd..193947865efd 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.h
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.h
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
index ed0adaf1eec4..ed0adaf1eec4 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/intel/ipw2x00/ipw2200.h
index aa301d1eee3c..aa301d1eee3c 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.h
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.h
diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/intel/ipw2x00/libipw.h
index b0571618c2ed..b0571618c2ed 100644
--- a/drivers/net/wireless/ipw2x00/libipw.h
+++ b/drivers/net/wireless/intel/ipw2x00/libipw.h
diff --git a/drivers/net/wireless/ipw2x00/libipw_geo.c b/drivers/net/wireless/intel/ipw2x00/libipw_geo.c
index 218f2a32de21..218f2a32de21 100644
--- a/drivers/net/wireless/ipw2x00/libipw_geo.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_geo.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
index 60f28740f6af..60f28740f6af 100644
--- a/drivers/net/wireless/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
index cef7f7d79cd9..cef7f7d79cd9 100644
--- a/drivers/net/wireless/ipw2x00/libipw_rx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/intel/ipw2x00/libipw_tx.c
index e8c039879b05..e8c039879b05 100644
--- a/drivers/net/wireless/ipw2x00/libipw_tx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_tx.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/intel/ipw2x00/libipw_wx.c
index dd29f46d086b..dd29f46d086b 100644
--- a/drivers/net/wireless/ipw2x00/libipw_wx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_wx.c
diff --git a/drivers/net/wireless/iwlegacy/3945-debug.c b/drivers/net/wireless/intel/iwlegacy/3945-debug.c
index c1b4441fb8b2..c1b4441fb8b2 100644
--- a/drivers/net/wireless/iwlegacy/3945-debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-debug.c
diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index af1b3e6839fa..af1b3e6839fa 100644
--- a/drivers/net/wireless/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/intel/iwlegacy/3945-rs.c
index 76b0729ade17..76b0729ade17 100644
--- a/drivers/net/wireless/iwlegacy/3945-rs.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-rs.c
diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/intel/iwlegacy/3945.c
index 93bdf684babe..93bdf684babe 100644
--- a/drivers/net/wireless/iwlegacy/3945.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945.c
diff --git a/drivers/net/wireless/iwlegacy/3945.h b/drivers/net/wireless/intel/iwlegacy/3945.h
index 00030d43a194..00030d43a194 100644
--- a/drivers/net/wireless/iwlegacy/3945.h
+++ b/drivers/net/wireless/intel/iwlegacy/3945.h
diff --git a/drivers/net/wireless/iwlegacy/4965-calib.c b/drivers/net/wireless/intel/iwlegacy/4965-calib.c
index e78bdefb8952..e78bdefb8952 100644
--- a/drivers/net/wireless/iwlegacy/4965-calib.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-calib.c
diff --git a/drivers/net/wireless/iwlegacy/4965-debug.c b/drivers/net/wireless/intel/iwlegacy/4965-debug.c
index e0597bfdddb8..e0597bfdddb8 100644
--- a/drivers/net/wireless/iwlegacy/4965-debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-debug.c
diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
new file mode 100644
index 000000000000..fd38aa0763e4
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
@@ -0,0 +1,6868 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci-aspm.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+
+#include <net/mac80211.h>
+
+#include <asm/div64.h>
+
+#define DRV_NAME "iwl4965"
+
+#include "common.h"
+#include "4965.h"
+
+/******************************************************************************
+ *
+ * module boiler plate
+ *
+ ******************************************************************************/
+
+/*
+ * module name, copyright, version, etc.
+ */
+#define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux"
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+#define VD "d"
+#else
+#define VD
+#endif
+
+#define DRV_VERSION IWLWIFI_VERSION VD
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("iwl4965");
+
+void
+il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status)
+{
+ if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
+ IL_ERR("Tx flush command to flush out all frames\n");
+ if (!test_bit(S_EXIT_PENDING, &il->status))
+ queue_work(il->workqueue, &il->tx_flush);
+ }
+}
+
+/*
+ * EEPROM
+ */
+struct il_mod_params il4965_mod_params = {
+ .restart_fw = 1,
+ /* the rest are 0 by default */
+};
+
+void
+il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq)
+{
+ unsigned long flags;
+ int i;
+ spin_lock_irqsave(&rxq->lock, flags);
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
+ /* In the reset function, these buffers may have been allocated
+ * to an SKB, so we need to unmap and free potential storage */
+ if (rxq->pool[i].page != NULL) {
+ pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << il->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ __il_free_pages(il, rxq->pool[i].page);
+ rxq->pool[i].page = NULL;
+ }
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+ }
+
+ for (i = 0; i < RX_QUEUE_SIZE; i++)
+ rxq->queue[i] = NULL;
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->write_actual = 0;
+ rxq->free_count = 0;
+ spin_unlock_irqrestore(&rxq->lock, flags);
+}
+
+int
+il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq)
+{
+ u32 rb_size;
+ const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
+ u32 rb_timeout = 0;
+
+ if (il->cfg->mod_params->amsdu_size_8K)
+ rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
+ else
+ rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
+
+ /* Stop Rx DMA */
+ il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+
+ /* Reset driver's Rx queue write idx */
+ il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
+
+ /* Tell device where to find RBD circular buffer in DRAM */
+ il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8));
+
+ /* Tell device where in DRAM to update its Rx status */
+ il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4);
+
+ /* Enable Rx DMA
+ * Direct rx interrupts to hosts
+ * Rx buffer size 4 or 8k
+ * RB timeout 0x10
+ * 256 RBDs
+ */
+ il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG,
+ FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
+ FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
+ FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
+ rb_size |
+ (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
+ (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
+
+ /* Set interrupt coalescing timer to default (2048 usecs) */
+ il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF);
+
+ return 0;
+}
+
+static void
+il4965_set_pwr_vmain(struct il_priv *il)
+{
+/*
+ * (for documentation purposes)
+ * to set power to V_AUX, do:
+
+ if (pci_pme_capable(il->pci_dev, PCI_D3cold))
+ il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
+ ~APMG_PS_CTRL_MSK_PWR_SRC);
+ */
+
+ il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
+ ~APMG_PS_CTRL_MSK_PWR_SRC);
+}
+
+int
+il4965_hw_nic_init(struct il_priv *il)
+{
+ unsigned long flags;
+ struct il_rx_queue *rxq = &il->rxq;
+ int ret;
+
+ spin_lock_irqsave(&il->lock, flags);
+ il_apm_init(il);
+ /* Set interrupt coalescing calibration timer to default (512 usecs) */
+ il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF);
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ il4965_set_pwr_vmain(il);
+ il4965_nic_config(il);
+
+ /* Allocate the RX queue, or reset if it is already allocated */
+ if (!rxq->bd) {
+ ret = il_rx_queue_alloc(il);
+ if (ret) {
+ IL_ERR("Unable to initialize Rx queue\n");
+ return -ENOMEM;
+ }
+ } else
+ il4965_rx_queue_reset(il, rxq);
+
+ il4965_rx_replenish(il);
+
+ il4965_rx_init(il, rxq);
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ rxq->need_update = 1;
+ il_rx_queue_update_write_ptr(il, rxq);
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ /* Allocate or reset and init all Tx and Command queues */
+ if (!il->txq) {
+ ret = il4965_txq_ctx_alloc(il);
+ if (ret)
+ return ret;
+ } else
+ il4965_txq_ctx_reset(il);
+
+ set_bit(S_INIT, &il->status);
+
+ return 0;
+}
+
+/**
+ * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
+ */
+static inline __le32
+il4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr)
+{
+ return cpu_to_le32((u32) (dma_addr >> 8));
+}
+
+/**
+ * il4965_rx_queue_restock - refill RX queue from pre-allocated pool
+ *
+ * If there are slots in the RX queue that need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can, pulling from rx_free.
+ *
+ * This moves the 'write' idx forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+void
+il4965_rx_queue_restock(struct il_priv *il)
+{
+ struct il_rx_queue *rxq = &il->rxq;
+ struct list_head *element;
+ struct il_rx_buf *rxb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ while (il_rx_queue_space(rxq) > 0 && rxq->free_count) {
+ /* The overwritten rxb must be a used one */
+ rxb = rxq->queue[rxq->write];
+ BUG_ON(rxb && rxb->page);
+
+ /* Get next free Rx buffer, remove from free list */
+ element = rxq->rx_free.next;
+ rxb = list_entry(element, struct il_rx_buf, list);
+ list_del(element);
+
+ /* Point to Rx buffer via next RBD in circular buffer */
+ rxq->bd[rxq->write] =
+ il4965_dma_addr2rbd_ptr(il, rxb->page_dma);
+ rxq->queue[rxq->write] = rxb;
+ rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
+ rxq->free_count--;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ /* If the pre-allocated buffer pool is dropping low, schedule to
+ * refill it */
+ if (rxq->free_count <= RX_LOW_WATERMARK)
+ queue_work(il->workqueue, &il->rx_replenish);
+
+ /* If we've added more space for the firmware to place data, tell it.
+ * Increment device's write pointer in multiples of 8. */
+ if (rxq->write_actual != (rxq->write & ~0x7)) {
+ spin_lock_irqsave(&rxq->lock, flags);
+ rxq->need_update = 1;
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ il_rx_queue_update_write_ptr(il, rxq);
+ }
+}
+
+/**
+ * il4965_rx_replenish - Move all used packet from rx_used to rx_free
+ *
+ * When moving to rx_free an SKB is allocated for the slot.
+ *
+ * Also restock the Rx queue via il_rx_queue_restock.
+ * This is called as a scheduled work item (except for during initialization)
+ */
+static void
+il4965_rx_allocate(struct il_priv *il, gfp_t priority)
+{
+ struct il_rx_queue *rxq = &il->rxq;
+ struct list_head *element;
+ struct il_rx_buf *rxb;
+ struct page *page;
+ dma_addr_t page_dma;
+ unsigned long flags;
+ gfp_t gfp_mask = priority;
+
+ while (1) {
+ spin_lock_irqsave(&rxq->lock, flags);
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ if (rxq->free_count > RX_LOW_WATERMARK)
+ gfp_mask |= __GFP_NOWARN;
+
+ if (il->hw_params.rx_page_order > 0)
+ gfp_mask |= __GFP_COMP;
+
+ /* Alloc a new receive buffer */
+ page = alloc_pages(gfp_mask, il->hw_params.rx_page_order);
+ if (!page) {
+ if (net_ratelimit())
+ D_INFO("alloc_pages failed, " "order: %d\n",
+ il->hw_params.rx_page_order);
+
+ if (rxq->free_count <= RX_LOW_WATERMARK &&
+ net_ratelimit())
+ IL_ERR("Failed to alloc_pages with %s. "
+ "Only %u free buffers remaining.\n",
+ priority ==
+ GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL",
+ rxq->free_count);
+ /* We don't reschedule replenish work here -- we will
+ * call the restock method and if it still needs
+ * more buffers it will schedule replenish */
+ return;
+ }
+
+ /* Get physical address of the RB */
+ page_dma =
+ pci_map_page(il->pci_dev, page, 0,
+ PAGE_SIZE << il->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) {
+ __free_pages(page, il->hw_params.rx_page_order);
+ break;
+ }
+
+ spin_lock_irqsave(&rxq->lock, flags);
+
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ pci_unmap_page(il->pci_dev, page_dma,
+ PAGE_SIZE << il->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ __free_pages(page, il->hw_params.rx_page_order);
+ return;
+ }
+
+ element = rxq->rx_used.next;
+ rxb = list_entry(element, struct il_rx_buf, list);
+ list_del(element);
+
+ BUG_ON(rxb->page);
+
+ rxb->page = page;
+ rxb->page_dma = page_dma;
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ il->alloc_rxb_page++;
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ }
+}
+
+void
+il4965_rx_replenish(struct il_priv *il)
+{
+ unsigned long flags;
+
+ il4965_rx_allocate(il, GFP_KERNEL);
+
+ spin_lock_irqsave(&il->lock, flags);
+ il4965_rx_queue_restock(il);
+ spin_unlock_irqrestore(&il->lock, flags);
+}
+
+void
+il4965_rx_replenish_now(struct il_priv *il)
+{
+ il4965_rx_allocate(il, GFP_ATOMIC);
+
+ il4965_rx_queue_restock(il);
+}
+
+/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
+ * If an SKB has been detached, the POOL needs to have its SKB set to NULL
+ * This free routine walks the list of POOL entries and if SKB is set to
+ * non NULL it is unmapped and freed
+ */
+void
+il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq)
+{
+ int i;
+ for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
+ if (rxq->pool[i].page != NULL) {
+ pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << il->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ __il_free_pages(il, rxq->pool[i].page);
+ rxq->pool[i].page = NULL;
+ }
+ }
+
+ dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
+ rxq->bd_dma);
+ dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status),
+ rxq->rb_stts, rxq->rb_stts_dma);
+ rxq->bd = NULL;
+ rxq->rb_stts = NULL;
+}
+
+int
+il4965_rxq_stop(struct il_priv *il)
+{
+ int ret;
+
+ _il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+ ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG,
+ FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
+ FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
+ 1000);
+ if (ret < 0)
+ IL_ERR("Can't stop Rx DMA.\n");
+
+ return 0;
+}
+
+int
+il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band)
+{
+ int idx = 0;
+ int band_offset = 0;
+
+ /* HT rate format: mac80211 wants an MCS number, which is just LSB */
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ idx = (rate_n_flags & 0xff);
+ return idx;
+ /* Legacy rate format, search for match in table */
+ } else {
+ if (band == IEEE80211_BAND_5GHZ)
+ band_offset = IL_FIRST_OFDM_RATE;
+ for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++)
+ if (il_rates[idx].plcp == (rate_n_flags & 0xFF))
+ return idx - band_offset;
+ }
+
+ return -1;
+}
+
+static int
+il4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp)
+{
+ /* data from PHY/DSP regarding signal strength, etc.,
+ * contents are always there, not configurable by host. */
+ struct il4965_rx_non_cfg_phy *ncphy =
+ (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
+ u32 agc =
+ (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >>
+ IL49_AGC_DB_POS;
+
+ u32 valid_antennae =
+ (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK)
+ >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET;
+ u8 max_rssi = 0;
+ u32 i;
+
+ /* Find max rssi among 3 possible receivers.
+ * These values are measured by the digital signal processor (DSP).
+ * They should stay fairly constant even as the signal strength varies,
+ * if the radio's automatic gain control (AGC) is working right.
+ * AGC value (see below) will provide the "interesting" info. */
+ for (i = 0; i < 3; i++)
+ if (valid_antennae & (1 << i))
+ max_rssi = max(ncphy->rssi_info[i << 1], max_rssi);
+
+ D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n",
+ ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4],
+ max_rssi, agc);
+
+ /* dBm = max_rssi dB - agc dB - constant.
+ * Higher AGC (higher radio gain) means lower signal. */
+ return max_rssi - agc - IL4965_RSSI_OFFSET;
+}
+
+static u32
+il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
+{
+ u32 decrypt_out = 0;
+
+ if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) ==
+ RX_RES_STATUS_STATION_FOUND)
+ decrypt_out |=
+ (RX_RES_STATUS_STATION_FOUND |
+ RX_RES_STATUS_NO_STATION_INFO_MISMATCH);
+
+ decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK);
+
+ /* packet was not encrypted */
+ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
+ RX_RES_STATUS_SEC_TYPE_NONE)
+ return decrypt_out;
+
+ /* packet was encrypted with unknown alg */
+ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
+ RX_RES_STATUS_SEC_TYPE_ERR)
+ return decrypt_out;
+
+ /* decryption was not done in HW */
+ if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) !=
+ RX_MPDU_RES_STATUS_DEC_DONE_MSK)
+ return decrypt_out;
+
+ switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) {
+
+ case RX_RES_STATUS_SEC_TYPE_CCMP:
+ /* alg is CCM: check MIC only */
+ if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK))
+ /* Bad MIC */
+ decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
+ else
+ decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
+
+ break;
+
+ case RX_RES_STATUS_SEC_TYPE_TKIP:
+ if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) {
+ /* Bad TTAK */
+ decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
+ break;
+ }
+ /* fall through if TTAK OK */
+ default:
+ if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
+ decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
+ else
+ decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
+ break;
+ }
+
+ D_RX("decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out);
+
+ return decrypt_out;
+}
+
+#define SMALL_PACKET_SIZE 256
+
+static void
+il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
+ u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
+ struct ieee80211_rx_status *stats)
+{
+ struct sk_buff *skb;
+ __le16 fc = hdr->frame_control;
+
+ /* We only process data packets if the interface is open */
+ if (unlikely(!il->is_open)) {
+ D_DROP("Dropping packet while interface is not open.\n");
+ 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))
+ return;
+
+ skb = dev_alloc_skb(SMALL_PACKET_SIZE);
+ if (!skb) {
+ IL_ERR("dev_alloc_skb failed\n");
+ return;
+ }
+
+ if (len <= SMALL_PACKET_SIZE) {
+ memcpy(skb_put(skb, len), hdr, len);
+ } else {
+ skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
+ len, PAGE_SIZE << il->hw_params.rx_page_order);
+ il->alloc_rxb_page--;
+ rxb->page = NULL;
+ }
+
+ il_update_stats(il, false, fc, len);
+ memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
+
+ ieee80211_rx(il->hw, skb);
+}
+
+/* Called for N_RX (legacy ABG frames), or
+ * N_RX_MPDU (HT high-throughput N frames). */
+static void
+il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct ieee80211_hdr *header;
+ struct ieee80211_rx_status rx_status = {};
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_rx_phy_res *phy_res;
+ __le32 rx_pkt_status;
+ struct il_rx_mpdu_res_start *amsdu;
+ u32 len;
+ u32 ampdu_status;
+ u32 rate_n_flags;
+
+ /**
+ * N_RX and N_RX_MPDU are handled differently.
+ * N_RX: physical layer info is in this buffer
+ * N_RX_MPDU: physical layer info was sent in separate
+ * command and cached in il->last_phy_res
+ *
+ * Here we set up local variables depending on which command is
+ * received.
+ */
+ if (pkt->hdr.cmd == N_RX) {
+ phy_res = (struct il_rx_phy_res *)pkt->u.raw;
+ header =
+ (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) +
+ phy_res->cfg_phy_cnt);
+
+ len = le16_to_cpu(phy_res->byte_count);
+ rx_pkt_status =
+ *(__le32 *) (pkt->u.raw + sizeof(*phy_res) +
+ phy_res->cfg_phy_cnt + len);
+ ampdu_status = le32_to_cpu(rx_pkt_status);
+ } else {
+ if (!il->_4965.last_phy_res_valid) {
+ IL_ERR("MPDU frame without cached PHY data\n");
+ return;
+ }
+ phy_res = &il->_4965.last_phy_res;
+ amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw;
+ header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu));
+ len = le16_to_cpu(amsdu->byte_count);
+ rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len);
+ ampdu_status =
+ il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status));
+ }
+
+ if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
+ D_DROP("dsp size out of range [0,20]: %d\n",
+ phy_res->cfg_phy_cnt);
+ return;
+ }
+
+ if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) ||
+ !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
+ D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status));
+ return;
+ }
+
+ /* This will be used in several places later */
+ rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);
+
+ /* rx_status carries information about the packet to mac80211 */
+ rx_status.mactime = le64_to_cpu(phy_res->timestamp);
+ rx_status.band =
+ (phy_res->
+ phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ;
+ rx_status.freq =
+ ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel),
+ rx_status.band);
+ rx_status.rate_idx =
+ il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band);
+ rx_status.flag = 0;
+
+ /* TSF isn't reliable. In order to allow smooth user experience,
+ * this W/A doesn't propagate it to the mac80211 */
+ /*rx_status.flag |= RX_FLAG_MACTIME_START; */
+
+ il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp);
+
+ /* Find max signal strength (dBm) among 3 antenna/receiver chains */
+ rx_status.signal = il4965_calc_rssi(il, phy_res);
+
+ D_STATS("Rssi %d, TSF %llu\n", rx_status.signal,
+ (unsigned long long)rx_status.mactime);
+
+ /*
+ * "antenna number"
+ *
+ * It seems that the antenna field in the phy flags value
+ * is actually a bit field. This is undefined by radiotap,
+ * it wants an actual antenna number but I always get "7"
+ * for most legacy frames I receive indicating that the
+ * same frame was received on all three RX chains.
+ *
+ * I think this field should be removed in favor of a
+ * new 802.11n radiotap field "RX chains" that is defined
+ * as a bitmask.
+ */
+ rx_status.antenna =
+ (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >>
+ RX_RES_PHY_FLAGS_ANTENNA_POS;
+
+ /* set the preamble flag if appropriate */
+ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
+ rx_status.flag |= RX_FLAG_SHORTPRE;
+
+ /* Set up the HT phy flags */
+ if (rate_n_flags & RATE_MCS_HT_MSK)
+ rx_status.flag |= RX_FLAG_HT;
+ if (rate_n_flags & RATE_MCS_HT40_MSK)
+ rx_status.flag |= RX_FLAG_40MHZ;
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ rx_status.flag |= RX_FLAG_SHORT_GI;
+
+ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
+ /* We know which subframes of an A-MPDU belong
+ * together since we get a single PHY response
+ * from the firmware for all of them.
+ */
+
+ rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
+ rx_status.ampdu_reference = il->_4965.ampdu_ref;
+ }
+
+ il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb,
+ &rx_status);
+}
+
+/* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY).
+ * This will be used later in il_hdl_rx() for N_RX_MPDU. */
+static void
+il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ il->_4965.last_phy_res_valid = true;
+ il->_4965.ampdu_ref++;
+ memcpy(&il->_4965.last_phy_res, pkt->u.raw,
+ sizeof(struct il_rx_phy_res));
+}
+
+static int
+il4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif,
+ enum ieee80211_band band, u8 is_active,
+ u8 n_probes, struct il_scan_channel *scan_ch)
+{
+ struct ieee80211_channel *chan;
+ const struct ieee80211_supported_band *sband;
+ const struct il_channel_info *ch_info;
+ u16 passive_dwell = 0;
+ u16 active_dwell = 0;
+ int added, i;
+ u16 channel;
+
+ sband = il_get_hw_mode(il, band);
+ if (!sband)
+ return 0;
+
+ active_dwell = il_get_active_dwell_time(il, band, n_probes);
+ passive_dwell = il_get_passive_dwell_time(il, band, vif);
+
+ if (passive_dwell <= active_dwell)
+ passive_dwell = active_dwell + 1;
+
+ for (i = 0, added = 0; i < il->scan_request->n_channels; i++) {
+ chan = il->scan_request->channels[i];
+
+ if (chan->band != band)
+ continue;
+
+ channel = chan->hw_value;
+ scan_ch->channel = cpu_to_le16(channel);
+
+ ch_info = il_get_channel_info(il, band, channel);
+ if (!il_is_channel_valid(ch_info)) {
+ D_SCAN("Channel %d is INVALID for this band.\n",
+ channel);
+ continue;
+ }
+
+ if (!is_active || il_is_channel_passive(ch_info) ||
+ (chan->flags & IEEE80211_CHAN_NO_IR))
+ scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
+ else
+ scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
+
+ if (n_probes)
+ scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes);
+
+ scan_ch->active_dwell = cpu_to_le16(active_dwell);
+ scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
+
+ /* Set txpower levels to defaults */
+ scan_ch->dsp_atten = 110;
+
+ /* NOTE: if we were doing 6Mb OFDM for scans we'd use
+ * power level:
+ * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
+ */
+ if (band == IEEE80211_BAND_5GHZ)
+ scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+ else
+ scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+
+ D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel,
+ le32_to_cpu(scan_ch->type),
+ (scan_ch->
+ type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE",
+ (scan_ch->
+ type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell :
+ passive_dwell);
+
+ scan_ch++;
+ added++;
+ }
+
+ D_SCAN("total channels to scan %d\n", added);
+ return added;
+}
+
+static void
+il4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid)
+{
+ int i;
+ u8 ind = *ant;
+
+ for (i = 0; i < RATE_ANT_NUM - 1; i++) {
+ ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0;
+ if (valid & BIT(ind)) {
+ *ant = ind;
+ return;
+ }
+ }
+}
+
+int
+il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif)
+{
+ struct il_host_cmd cmd = {
+ .id = C_SCAN,
+ .len = sizeof(struct il_scan_cmd),
+ .flags = CMD_SIZE_HUGE,
+ };
+ struct il_scan_cmd *scan;
+ u32 rate_flags = 0;
+ u16 cmd_len;
+ u16 rx_chain = 0;
+ enum ieee80211_band band;
+ u8 n_probes = 0;
+ u8 rx_ant = il->hw_params.valid_rx_ant;
+ u8 rate;
+ bool is_active = false;
+ int chan_mod;
+ u8 active_chains;
+ u8 scan_tx_antennas = il->hw_params.valid_tx_ant;
+ int ret;
+
+ lockdep_assert_held(&il->mutex);
+
+ if (!il->scan_cmd) {
+ il->scan_cmd =
+ kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE,
+ GFP_KERNEL);
+ if (!il->scan_cmd) {
+ D_SCAN("fail to allocate memory for scan\n");
+ return -ENOMEM;
+ }
+ }
+ scan = il->scan_cmd;
+ memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE);
+
+ scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH;
+ scan->quiet_time = IL_ACTIVE_QUIET_TIME;
+
+ if (il_is_any_associated(il)) {
+ u16 interval;
+ u32 extra;
+ u32 suspend_time = 100;
+ u32 scan_suspend_time = 100;
+
+ D_INFO("Scanning while associated...\n");
+ interval = vif->bss_conf.beacon_int;
+
+ scan->suspend_time = 0;
+ scan->max_out_time = cpu_to_le32(200 * 1024);
+ if (!interval)
+ interval = suspend_time;
+
+ extra = (suspend_time / interval) << 22;
+ scan_suspend_time =
+ (extra | ((suspend_time % interval) * 1024));
+ scan->suspend_time = cpu_to_le32(scan_suspend_time);
+ D_SCAN("suspend_time 0x%X beacon interval %d\n",
+ scan_suspend_time, interval);
+ }
+
+ if (il->scan_request->n_ssids) {
+ int i, p = 0;
+ D_SCAN("Kicking off active scan\n");
+ for (i = 0; i < il->scan_request->n_ssids; i++) {
+ /* always does wildcard anyway */
+ if (!il->scan_request->ssids[i].ssid_len)
+ continue;
+ scan->direct_scan[p].id = WLAN_EID_SSID;
+ scan->direct_scan[p].len =
+ il->scan_request->ssids[i].ssid_len;
+ memcpy(scan->direct_scan[p].ssid,
+ il->scan_request->ssids[i].ssid,
+ il->scan_request->ssids[i].ssid_len);
+ n_probes++;
+ p++;
+ }
+ is_active = true;
+ } else
+ D_SCAN("Start passive scan.\n");
+
+ scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
+ scan->tx_cmd.sta_id = il->hw_params.bcast_id;
+ scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+
+ switch (il->scan_band) {
+ case IEEE80211_BAND_2GHZ:
+ scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
+ chan_mod =
+ le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >>
+ RXON_FLG_CHANNEL_MODE_POS;
+ if (chan_mod == CHANNEL_MODE_PURE_40) {
+ rate = RATE_6M_PLCP;
+ } else {
+ rate = RATE_1M_PLCP;
+ rate_flags = RATE_MCS_CCK_MSK;
+ }
+ break;
+ case IEEE80211_BAND_5GHZ:
+ rate = RATE_6M_PLCP;
+ break;
+ default:
+ IL_WARN("Invalid scan band\n");
+ return -EIO;
+ }
+
+ /*
+ * If active scanning is requested but a certain channel is
+ * marked passive, we can do active scanning if we detect
+ * transmissions.
+ *
+ * There is an issue with some firmware versions that triggers
+ * a sysassert on a "good CRC threshold" of zero (== disabled),
+ * on a radar channel even though this means that we should NOT
+ * send probes.
+ *
+ * The "good CRC threshold" is the number of frames that we
+ * need to receive during our dwell time on a channel before
+ * sending out probes -- setting this to a huge value will
+ * mean we never reach it, but at the same time work around
+ * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER
+ * here instead of IL_GOOD_CRC_TH_DISABLED.
+ */
+ scan->good_CRC_th =
+ is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER;
+
+ band = il->scan_band;
+
+ if (il->cfg->scan_rx_antennas[band])
+ rx_ant = il->cfg->scan_rx_antennas[band];
+
+ il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas);
+ rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS;
+ scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags);
+
+ /* In power save mode use one chain, otherwise use all chains */
+ if (test_bit(S_POWER_PMI, &il->status)) {
+ /* rx_ant has been set to all valid chains previously */
+ active_chains =
+ rx_ant & ((u8) (il->chain_noise_data.active_chains));
+ if (!active_chains)
+ active_chains = rx_ant;
+
+ D_SCAN("chain_noise_data.active_chains: %u\n",
+ il->chain_noise_data.active_chains);
+
+ rx_ant = il4965_first_antenna(active_chains);
+ }
+
+ /* MIMO is not used here, but value is required */
+ rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
+ rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
+ scan->rx_chain = cpu_to_le16(rx_chain);
+
+ cmd_len =
+ il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data,
+ vif->addr, il->scan_request->ie,
+ il->scan_request->ie_len,
+ IL_MAX_SCAN_SIZE - sizeof(*scan));
+ scan->tx_cmd.len = cpu_to_le16(cmd_len);
+
+ scan->filter_flags |=
+ (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK);
+
+ scan->channel_count =
+ il4965_get_channels_for_scan(il, vif, band, is_active, n_probes,
+ (void *)&scan->data[cmd_len]);
+ if (scan->channel_count == 0) {
+ D_SCAN("channel count %d\n", scan->channel_count);
+ return -EIO;
+ }
+
+ cmd.len +=
+ le16_to_cpu(scan->tx_cmd.len) +
+ scan->channel_count * sizeof(struct il_scan_channel);
+ cmd.data = scan;
+ scan->len = cpu_to_le16(cmd.len);
+
+ set_bit(S_SCAN_HW, &il->status);
+
+ ret = il_send_cmd_sync(il, &cmd);
+ if (ret)
+ clear_bit(S_SCAN_HW, &il->status);
+
+ return ret;
+}
+
+int
+il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif,
+ bool add)
+{
+ struct il_vif_priv *vif_priv = (void *)vif->drv_priv;
+
+ if (add)
+ return il4965_add_bssid_station(il, vif->bss_conf.bssid,
+ &vif_priv->ibss_bssid_sta_id);
+ return il_remove_station(il, vif_priv->ibss_bssid_sta_id,
+ vif->bss_conf.bssid);
+}
+
+void
+il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed)
+{
+ lockdep_assert_held(&il->sta_lock);
+
+ if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed)
+ il->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+ else {
+ D_TX("free more than tfds_in_queue (%u:%d)\n",
+ il->stations[sta_id].tid[tid].tfds_in_queue, freed);
+ il->stations[sta_id].tid[tid].tfds_in_queue = 0;
+ }
+}
+
+#define IL_TX_QUEUE_MSK 0xfffff
+
+static bool
+il4965_is_single_rx_stream(struct il_priv *il)
+{
+ return il->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
+ il->current_ht_config.single_chain_sufficient;
+}
+
+#define IL_NUM_RX_CHAINS_MULTIPLE 3
+#define IL_NUM_RX_CHAINS_SINGLE 2
+#define IL_NUM_IDLE_CHAINS_DUAL 2
+#define IL_NUM_IDLE_CHAINS_SINGLE 1
+
+/*
+ * Determine how many receiver/antenna chains to use.
+ *
+ * More provides better reception via diversity. Fewer saves power
+ * at the expense of throughput, but only when not in powersave to
+ * start with.
+ *
+ * MIMO (dual stream) requires at least 2, but works better with 3.
+ * This does not determine *which* chains to use, just how many.
+ */
+static int
+il4965_get_active_rx_chain_count(struct il_priv *il)
+{
+ /* # of Rx chains to use when expecting MIMO. */
+ if (il4965_is_single_rx_stream(il))
+ return IL_NUM_RX_CHAINS_SINGLE;
+ else
+ return IL_NUM_RX_CHAINS_MULTIPLE;
+}
+
+/*
+ * When we are in power saving mode, unless device support spatial
+ * multiplexing power save, use the active count for rx chain count.
+ */
+static int
+il4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt)
+{
+ /* # Rx chains when idling, depending on SMPS mode */
+ switch (il->current_ht_config.smps) {
+ case IEEE80211_SMPS_STATIC:
+ case IEEE80211_SMPS_DYNAMIC:
+ return IL_NUM_IDLE_CHAINS_SINGLE;
+ case IEEE80211_SMPS_OFF:
+ return active_cnt;
+ default:
+ WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps);
+ return active_cnt;
+ }
+}
+
+/* up to 4 chains */
+static u8
+il4965_count_chain_bitmap(u32 chain_bitmap)
+{
+ u8 res;
+ res = (chain_bitmap & BIT(0)) >> 0;
+ res += (chain_bitmap & BIT(1)) >> 1;
+ res += (chain_bitmap & BIT(2)) >> 2;
+ res += (chain_bitmap & BIT(3)) >> 3;
+ return res;
+}
+
+/**
+ * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
+ *
+ * Selects how many and which Rx receivers/antennas/chains to use.
+ * This should not be used for scan command ... it puts data in wrong place.
+ */
+void
+il4965_set_rxon_chain(struct il_priv *il)
+{
+ bool is_single = il4965_is_single_rx_stream(il);
+ bool is_cam = !test_bit(S_POWER_PMI, &il->status);
+ u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
+ u32 active_chains;
+ u16 rx_chain;
+
+ /* Tell uCode which antennas are actually connected.
+ * Before first association, we assume all antennas are connected.
+ * Just after first association, il4965_chain_noise_calibration()
+ * checks which antennas actually *are* connected. */
+ if (il->chain_noise_data.active_chains)
+ active_chains = il->chain_noise_data.active_chains;
+ else
+ active_chains = il->hw_params.valid_rx_ant;
+
+ rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
+
+ /* How many receivers should we use? */
+ active_rx_cnt = il4965_get_active_rx_chain_count(il);
+ idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt);
+
+ /* correct rx chain count according hw settings
+ * and chain noise calibration
+ */
+ valid_rx_cnt = il4965_count_chain_bitmap(active_chains);
+ if (valid_rx_cnt < active_rx_cnt)
+ active_rx_cnt = valid_rx_cnt;
+
+ if (valid_rx_cnt < idle_rx_cnt)
+ idle_rx_cnt = valid_rx_cnt;
+
+ rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
+ rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;
+
+ il->staging.rx_chain = cpu_to_le16(rx_chain);
+
+ if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam)
+ il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
+ else
+ il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
+
+ D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain,
+ active_rx_cnt, idle_rx_cnt);
+
+ WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 ||
+ active_rx_cnt < idle_rx_cnt);
+}
+
+static const char *
+il4965_get_fh_string(int cmd)
+{
+ switch (cmd) {
+ IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG);
+ IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG);
+ IL_CMD(FH49_RSCSR_CHNL0_WPTR);
+ IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG);
+ IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG);
+ IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG);
+ IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
+ IL_CMD(FH49_TSSR_TX_STATUS_REG);
+ IL_CMD(FH49_TSSR_TX_ERROR_REG);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+int
+il4965_dump_fh(struct il_priv *il, char **buf, bool display)
+{
+ int i;
+#ifdef CONFIG_IWLEGACY_DEBUG
+ int pos = 0;
+ size_t bufsz = 0;
+#endif
+ static const u32 fh_tbl[] = {
+ FH49_RSCSR_CHNL0_STTS_WPTR_REG,
+ FH49_RSCSR_CHNL0_RBDCB_BASE_REG,
+ FH49_RSCSR_CHNL0_WPTR,
+ FH49_MEM_RCSR_CHNL0_CONFIG_REG,
+ FH49_MEM_RSSR_SHARED_CTRL_REG,
+ FH49_MEM_RSSR_RX_STATUS_REG,
+ FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
+ FH49_TSSR_TX_STATUS_REG,
+ FH49_TSSR_TX_ERROR_REG
+ };
+#ifdef CONFIG_IWLEGACY_DEBUG
+ if (display) {
+ bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+ *buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+ pos +=
+ scnprintf(*buf + pos, bufsz - pos, "FH register values:\n");
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+ pos +=
+ scnprintf(*buf + pos, bufsz - pos,
+ " %34s: 0X%08x\n",
+ il4965_get_fh_string(fh_tbl[i]),
+ il_rd(il, fh_tbl[i]));
+ }
+ return pos;
+ }
+#endif
+ IL_ERR("FH register values:\n");
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+ IL_ERR(" %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]),
+ il_rd(il, fh_tbl[i]));
+ }
+ return 0;
+}
+
+static void
+il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_missed_beacon_notif *missed_beacon;
+
+ missed_beacon = &pkt->u.missed_beacon;
+ if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
+ il->missed_beacon_threshold) {
+ D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n",
+ le32_to_cpu(missed_beacon->consecutive_missed_beacons),
+ le32_to_cpu(missed_beacon->total_missed_becons),
+ le32_to_cpu(missed_beacon->num_recvd_beacons),
+ le32_to_cpu(missed_beacon->num_expected_beacons));
+ if (!test_bit(S_SCANNING, &il->status))
+ il4965_init_sensitivity(il);
+ }
+}
+
+/* Calculate noise level, based on measurements during network silence just
+ * before arriving beacon. This measurement can be done only if we know
+ * exactly when to expect beacons, therefore only when we're associated. */
+static void
+il4965_rx_calc_noise(struct il_priv *il)
+{
+ struct stats_rx_non_phy *rx_info;
+ int num_active_rx = 0;
+ int total_silence = 0;
+ int bcn_silence_a, bcn_silence_b, bcn_silence_c;
+ int last_rx_noise;
+
+ rx_info = &(il->_4965.stats.rx.general);
+ bcn_silence_a =
+ le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
+ bcn_silence_b =
+ le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
+ bcn_silence_c =
+ le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
+
+ if (bcn_silence_a) {
+ total_silence += bcn_silence_a;
+ num_active_rx++;
+ }
+ if (bcn_silence_b) {
+ total_silence += bcn_silence_b;
+ num_active_rx++;
+ }
+ if (bcn_silence_c) {
+ total_silence += bcn_silence_c;
+ num_active_rx++;
+ }
+
+ /* Average among active antennas */
+ if (num_active_rx)
+ last_rx_noise = (total_silence / num_active_rx) - 107;
+ else
+ last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE;
+
+ D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a,
+ bcn_silence_b, bcn_silence_c, last_rx_noise);
+}
+
+#ifdef CONFIG_IWLEGACY_DEBUGFS
+/*
+ * based on the assumption of all stats counter are in DWORD
+ * FIXME: This function is for debugging, do not deal with
+ * the case of counters roll-over.
+ */
+static void
+il4965_accumulative_stats(struct il_priv *il, __le32 * stats)
+{
+ int i, size;
+ __le32 *prev_stats;
+ u32 *accum_stats;
+ u32 *delta, *max_delta;
+ struct stats_general_common *general, *accum_general;
+ struct stats_tx *tx, *accum_tx;
+
+ prev_stats = (__le32 *) &il->_4965.stats;
+ accum_stats = (u32 *) &il->_4965.accum_stats;
+ size = sizeof(struct il_notif_stats);
+ general = &il->_4965.stats.general.common;
+ accum_general = &il->_4965.accum_stats.general.common;
+ tx = &il->_4965.stats.tx;
+ accum_tx = &il->_4965.accum_stats.tx;
+ delta = (u32 *) &il->_4965.delta_stats;
+ max_delta = (u32 *) &il->_4965.max_delta;
+
+ for (i = sizeof(__le32); i < size;
+ i +=
+ sizeof(__le32), stats++, prev_stats++, delta++, max_delta++,
+ accum_stats++) {
+ if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) {
+ *delta =
+ (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats));
+ *accum_stats += *delta;
+ if (*delta > *max_delta)
+ *max_delta = *delta;
+ }
+ }
+
+ /* reset accumulative stats for "no-counter" type stats */
+ accum_general->temperature = general->temperature;
+ accum_general->ttl_timestamp = general->ttl_timestamp;
+}
+#endif
+
+static void
+il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ const int recalib_seconds = 60;
+ bool change;
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+
+ D_RX("Statistics notification received (%d vs %d).\n",
+ (int)sizeof(struct il_notif_stats),
+ le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK);
+
+ change =
+ ((il->_4965.stats.general.common.temperature !=
+ pkt->u.stats.general.common.temperature) ||
+ ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) !=
+ (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)));
+#ifdef CONFIG_IWLEGACY_DEBUGFS
+ il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats);
+#endif
+
+ /* TODO: reading some of stats is unneeded */
+ memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats));
+
+ set_bit(S_STATS, &il->status);
+
+ /*
+ * Reschedule the stats timer to occur in recalib_seconds to ensure
+ * we get a thermal update even if the uCode doesn't give us one
+ */
+ mod_timer(&il->stats_periodic,
+ jiffies + msecs_to_jiffies(recalib_seconds * 1000));
+
+ if (unlikely(!test_bit(S_SCANNING, &il->status)) &&
+ (pkt->hdr.cmd == N_STATS)) {
+ il4965_rx_calc_noise(il);
+ queue_work(il->workqueue, &il->run_time_calib_work);
+ }
+
+ if (change)
+ il4965_temperature_calib(il);
+}
+
+static void
+il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+
+ if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) {
+#ifdef CONFIG_IWLEGACY_DEBUGFS
+ memset(&il->_4965.accum_stats, 0,
+ sizeof(struct il_notif_stats));
+ memset(&il->_4965.delta_stats, 0,
+ sizeof(struct il_notif_stats));
+ memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats));
+#endif
+ D_RX("Statistics have been cleared\n");
+ }
+ il4965_hdl_stats(il, rxb);
+}
+
+
+/*
+ * mac80211 queues, ACs, hardware queues, FIFOs.
+ *
+ * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues
+ *
+ * Mac80211 uses the following numbers, which we get as from it
+ * by way of skb_get_queue_mapping(skb):
+ *
+ * VO 0
+ * VI 1
+ * BE 2
+ * BK 3
+ *
+ *
+ * Regular (not A-MPDU) frames are put into hardware queues corresponding
+ * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their
+ * own queue per aggregation session (RA/TID combination), such queues are
+ * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In
+ * order to map frames to the right queue, we also need an AC->hw queue
+ * mapping. This is implemented here.
+ *
+ * Due to the way hw queues are set up (by the hw specific modules like
+ * 4965.c), the AC->hw queue mapping is the identity
+ * mapping.
+ */
+
+static const u8 tid_to_ac[] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO
+};
+
+static inline int
+il4965_get_ac_from_tid(u16 tid)
+{
+ if (likely(tid < ARRAY_SIZE(tid_to_ac)))
+ return tid_to_ac[tid];
+
+ /* no support for TIDs 8-15 yet */
+ return -EINVAL;
+}
+
+static inline int
+il4965_get_fifo_from_tid(u16 tid)
+{
+ const u8 ac_to_fifo[] = {
+ IL_TX_FIFO_VO,
+ IL_TX_FIFO_VI,
+ IL_TX_FIFO_BE,
+ IL_TX_FIFO_BK,
+ };
+
+ if (likely(tid < ARRAY_SIZE(tid_to_ac)))
+ return ac_to_fifo[tid_to_ac[tid]];
+
+ /* no support for TIDs 8-15 yet */
+ return -EINVAL;
+}
+
+/*
+ * handle build C_TX command notification.
+ */
+static void
+il4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb,
+ struct il_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_hdr *hdr, u8 std_id)
+{
+ __le16 fc = hdr->frame_control;
+ __le32 tx_flags = tx_cmd->tx_flags;
+
+ tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
+ tx_flags |= TX_CMD_FLG_ACK_MSK;
+ if (ieee80211_is_mgmt(fc))
+ tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
+ if (ieee80211_is_probe_resp(fc) &&
+ !(le16_to_cpu(hdr->seq_ctrl) & 0xf))
+ tx_flags |= TX_CMD_FLG_TSF_MSK;
+ } else {
+ tx_flags &= (~TX_CMD_FLG_ACK_MSK);
+ tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
+ }
+
+ if (ieee80211_is_back_req(fc))
+ tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
+
+ tx_cmd->sta_id = std_id;
+ if (ieee80211_has_morefrags(fc))
+ tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
+
+ if (ieee80211_is_data_qos(fc)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+ tx_cmd->tid_tspec = qc[0] & 0xf;
+ tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
+ } else {
+ tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
+ }
+
+ il_tx_cmd_protection(il, info, fc, &tx_flags);
+
+ tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
+ if (ieee80211_is_mgmt(fc)) {
+ if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
+ tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3);
+ else
+ tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2);
+ } else {
+ tx_cmd->timeout.pm_frame_timeout = 0;
+ }
+
+ tx_cmd->driver_txop = 0;
+ tx_cmd->tx_flags = tx_flags;
+ tx_cmd->next_frame_len = 0;
+}
+
+static void
+il4965_tx_cmd_build_rate(struct il_priv *il,
+ struct il_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta,
+ __le16 fc)
+{
+ const u8 rts_retry_limit = 60;
+ u32 rate_flags;
+ int rate_idx;
+ u8 data_retry_limit;
+ u8 rate_plcp;
+
+ /* Set retry limit on DATA packets and Probe Responses */
+ if (ieee80211_is_probe_resp(fc))
+ data_retry_limit = 3;
+ else
+ data_retry_limit = IL4965_DEFAULT_TX_RETRY;
+ tx_cmd->data_retry_limit = data_retry_limit;
+ /* Set retry limit on RTS packets */
+ tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit);
+
+ /* DATA packets will use the uCode station table for rate/antenna
+ * selection */
+ if (ieee80211_is_data(fc)) {
+ tx_cmd->initial_rate_idx = 0;
+ tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
+ return;
+ }
+
+ /**
+ * If the current TX rate stored in mac80211 has the MCS bit set, it's
+ * not really a TX rate. Thus, we use the lowest supported rate for
+ * this band. Also use the lowest supported rate if the stored rate
+ * idx is invalid.
+ */
+ rate_idx = info->control.rates[0].idx;
+ if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0
+ || rate_idx > RATE_COUNT_LEGACY)
+ rate_idx = rate_lowest_index(&il->bands[info->band], sta);
+ /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate_idx += IL_FIRST_OFDM_RATE;
+ /* Get PLCP rate for tx_cmd->rate_n_flags */
+ rate_plcp = il_rates[rate_idx].plcp;
+ /* Zero out flags for this packet */
+ rate_flags = 0;
+
+ /* Set CCK flag as needed */
+ if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE)
+ rate_flags |= RATE_MCS_CCK_MSK;
+
+ /* Set up antennas */
+ il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant);
+ rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS;
+
+ /* Set the rate in the TX cmd */
+ tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags);
+}
+
+static void
+il4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info,
+ struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag,
+ int sta_id)
+{
+ struct ieee80211_key_conf *keyconf = info->control.hw_key;
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
+ memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
+ if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
+ D_TX("tx_cmd with AES hwcrypto\n");
+ break;
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
+ ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
+ D_TX("tx_cmd with tkip hwcrypto\n");
+ break;
+
+ case WLAN_CIPHER_SUITE_WEP104:
+ tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
+ /* fall through */
+ case WLAN_CIPHER_SUITE_WEP40:
+ tx_cmd->sec_ctl |=
+ (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) <<
+ TX_CMD_SEC_SHIFT);
+
+ memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
+
+ D_TX("Configuring packet for WEP encryption " "with key %d\n",
+ keyconf->keyidx);
+ break;
+
+ default:
+ IL_ERR("Unknown encode cipher %x\n", keyconf->cipher);
+ break;
+ }
+}
+
+/*
+ * start C_TX command process
+ */
+int
+il4965_tx_skb(struct il_priv *il,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct il_station_priv *sta_priv = NULL;
+ struct il_tx_queue *txq;
+ struct il_queue *q;
+ struct il_device_cmd *out_cmd;
+ struct il_cmd_meta *out_meta;
+ struct il_tx_cmd *tx_cmd;
+ int txq_id;
+ dma_addr_t phys_addr;
+ dma_addr_t txcmd_phys;
+ dma_addr_t scratch_phys;
+ u16 len, firstlen, secondlen;
+ u16 seq_number = 0;
+ __le16 fc;
+ u8 hdr_len;
+ u8 sta_id;
+ u8 wait_write_ptr = 0;
+ u8 tid = 0;
+ u8 *qc = NULL;
+ unsigned long flags;
+ bool is_agg = false;
+
+ spin_lock_irqsave(&il->lock, flags);
+ if (il_is_rfkill(il)) {
+ D_DROP("Dropping - RF KILL\n");
+ goto drop_unlock;
+ }
+
+ fc = hdr->frame_control;
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+ if (ieee80211_is_auth(fc))
+ D_TX("Sending AUTH frame\n");
+ else if (ieee80211_is_assoc_req(fc))
+ D_TX("Sending ASSOC frame\n");
+ else if (ieee80211_is_reassoc_req(fc))
+ D_TX("Sending REASSOC frame\n");
+#endif
+
+ hdr_len = ieee80211_hdrlen(fc);
+
+ /* For management frames use broadcast id to do not break aggregation */
+ if (!ieee80211_is_data(fc))
+ sta_id = il->hw_params.bcast_id;
+ else {
+ /* Find idx into station table for destination station */
+ sta_id = il_sta_id_or_broadcast(il, sta);
+
+ if (sta_id == IL_INVALID_STATION) {
+ D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1);
+ goto drop_unlock;
+ }
+ }
+
+ D_TX("station Id %d\n", sta_id);
+
+ if (sta)
+ sta_priv = (void *)sta->drv_priv;
+
+ if (sta_priv && sta_priv->asleep &&
+ (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) {
+ /*
+ * This sends an asynchronous command to the device,
+ * but we can rely on it being processed before the
+ * next frame is processed -- and the next frame to
+ * this station is the one that will consume this
+ * counter.
+ * For now set the counter to just 1 since we do not
+ * support uAPSD yet.
+ */
+ il4965_sta_modify_sleep_tx_count(il, sta_id, 1);
+ }
+
+ /* FIXME: remove me ? */
+ WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
+
+ /* Access category (AC) is also the queue number */
+ txq_id = skb_get_queue_mapping(skb);
+
+ /* irqs already disabled/saved above when locking il->lock */
+ spin_lock(&il->sta_lock);
+
+ if (ieee80211_is_data_qos(fc)) {
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+ if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) {
+ spin_unlock(&il->sta_lock);
+ goto drop_unlock;
+ }
+ seq_number = il->stations[sta_id].tid[tid].seq_number;
+ seq_number &= IEEE80211_SCTL_SEQ;
+ hdr->seq_ctrl =
+ hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG);
+ hdr->seq_ctrl |= cpu_to_le16(seq_number);
+ seq_number += 0x10;
+ /* aggregation is on for this <sta,tid> */
+ if (info->flags & IEEE80211_TX_CTL_AMPDU &&
+ il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) {
+ txq_id = il->stations[sta_id].tid[tid].agg.txq_id;
+ is_agg = true;
+ }
+ }
+
+ txq = &il->txq[txq_id];
+ q = &txq->q;
+
+ if (unlikely(il_queue_space(q) < q->high_mark)) {
+ spin_unlock(&il->sta_lock);
+ goto drop_unlock;
+ }
+
+ if (ieee80211_is_data_qos(fc)) {
+ il->stations[sta_id].tid[tid].tfds_in_queue++;
+ if (!ieee80211_has_morefrags(fc))
+ il->stations[sta_id].tid[tid].seq_number = seq_number;
+ }
+
+ spin_unlock(&il->sta_lock);
+
+ txq->skbs[q->write_ptr] = skb;
+
+ /* Set up first empty entry in queue's array of Tx/cmd buffers */
+ out_cmd = txq->cmd[q->write_ptr];
+ out_meta = &txq->meta[q->write_ptr];
+ tx_cmd = &out_cmd->cmd.tx;
+ memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
+ memset(tx_cmd, 0, sizeof(struct il_tx_cmd));
+
+ /*
+ * Set up the Tx-command (not MAC!) header.
+ * Store the chosen Tx queue and TFD idx within the sequence field;
+ * after Tx, uCode's Tx response will return this value so driver can
+ * locate the frame within the tx queue and do post-tx processing.
+ */
+ out_cmd->hdr.cmd = C_TX;
+ out_cmd->hdr.sequence =
+ cpu_to_le16((u16)
+ (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr)));
+
+ /* Copy MAC header from skb into command buffer */
+ memcpy(tx_cmd->hdr, hdr, hdr_len);
+
+ /* Total # bytes to be transmitted */
+ tx_cmd->len = cpu_to_le16((u16) skb->len);
+
+ if (info->control.hw_key)
+ il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id);
+
+ /* TODO need this for burst mode later on */
+ il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id);
+
+ il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc);
+
+ /*
+ * Use the first empty entry in this queue's command buffer array
+ * to contain the Tx command and MAC header concatenated together
+ * (payload data will be in another buffer).
+ * Size of this varies, due to varying MAC header length.
+ * If end is not dword aligned, we'll have 2 extra bytes at the end
+ * of the MAC header (device reads on dword boundaries).
+ * We'll tell device about this padding later.
+ */
+ len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len;
+ firstlen = (len + 3) & ~3;
+
+ /* Tell NIC about any 2-byte padding after MAC header */
+ if (firstlen != len)
+ tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
+
+ /* Physical address of this Tx command's header (not MAC header!),
+ * within command buffer array. */
+ txcmd_phys =
+ pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen,
+ PCI_DMA_BIDIRECTIONAL);
+ if (unlikely(pci_dma_mapping_error(il->pci_dev, txcmd_phys)))
+ goto drop_unlock;
+
+ /* Set up TFD's 2nd entry to point directly to remainder of skb,
+ * if any (802.11 null frames have no payload). */
+ secondlen = skb->len - hdr_len;
+ if (secondlen > 0) {
+ phys_addr =
+ pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen,
+ PCI_DMA_TODEVICE);
+ if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr)))
+ goto drop_unlock;
+ }
+
+ /* Add buffer containing Tx command and MAC(!) header to TFD's
+ * first entry */
+ il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0);
+ dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
+ dma_unmap_len_set(out_meta, len, firstlen);
+ if (secondlen)
+ il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen,
+ 0, 0);
+
+ if (!ieee80211_has_morefrags(hdr->frame_control)) {
+ txq->need_update = 1;
+ } else {
+ wait_write_ptr = 1;
+ txq->need_update = 0;
+ }
+
+ scratch_phys =
+ txcmd_phys + sizeof(struct il_cmd_header) +
+ offsetof(struct il_tx_cmd, scratch);
+
+ /* take back ownership of DMA buffer to enable update */
+ pci_dma_sync_single_for_cpu(il->pci_dev, txcmd_phys, firstlen,
+ PCI_DMA_BIDIRECTIONAL);
+ tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
+ tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys);
+
+ il_update_stats(il, true, fc, skb->len);
+
+ D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence));
+ D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
+ il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd));
+ il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len);
+
+ /* Set up entry for this TFD in Tx byte-count array */
+ if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len));
+
+ pci_dma_sync_single_for_device(il->pci_dev, txcmd_phys, firstlen,
+ PCI_DMA_BIDIRECTIONAL);
+
+ /* Tell device the write idx *just past* this latest filled TFD */
+ q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd);
+ il_txq_update_write_ptr(il, txq);
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ /*
+ * At this point the frame is "transmitted" successfully
+ * and we will get a TX status notification eventually,
+ * regardless of the value of ret. "ret" only indicates
+ * whether or not we should update the write pointer.
+ */
+
+ /*
+ * Avoid atomic ops if it isn't an associated client.
+ * Also, if this is a packet for aggregation, don't
+ * increase the counter because the ucode will stop
+ * aggregation queues when their respective station
+ * goes to sleep.
+ */
+ if (sta_priv && sta_priv->client && !is_agg)
+ atomic_inc(&sta_priv->pending_frames);
+
+ if (il_queue_space(q) < q->high_mark && il->mac80211_registered) {
+ if (wait_write_ptr) {
+ spin_lock_irqsave(&il->lock, flags);
+ txq->need_update = 1;
+ il_txq_update_write_ptr(il, txq);
+ spin_unlock_irqrestore(&il->lock, flags);
+ } else {
+ il_stop_queue(il, txq);
+ }
+ }
+
+ return 0;
+
+drop_unlock:
+ spin_unlock_irqrestore(&il->lock, flags);
+ return -1;
+}
+
+static inline int
+il4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size)
+{
+ ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma,
+ GFP_KERNEL);
+ if (!ptr->addr)
+ return -ENOMEM;
+ ptr->size = size;
+ return 0;
+}
+
+static inline void
+il4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr)
+{
+ if (unlikely(!ptr->addr))
+ return;
+
+ dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma);
+ memset(ptr, 0, sizeof(*ptr));
+}
+
+/**
+ * il4965_hw_txq_ctx_free - Free TXQ Context
+ *
+ * Destroy all TX DMA queues and structures
+ */
+void
+il4965_hw_txq_ctx_free(struct il_priv *il)
+{
+ int txq_id;
+
+ /* Tx queues */
+ if (il->txq) {
+ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
+ if (txq_id == il->cmd_queue)
+ il_cmd_queue_free(il);
+ else
+ il_tx_queue_free(il, txq_id);
+ }
+ il4965_free_dma_ptr(il, &il->kw);
+
+ il4965_free_dma_ptr(il, &il->scd_bc_tbls);
+
+ /* free tx queue structure */
+ il_free_txq_mem(il);
+}
+
+/**
+ * il4965_txq_ctx_alloc - allocate TX queue context
+ * Allocate all Tx DMA structures and initialize them
+ *
+ * @param il
+ * @return error code
+ */
+int
+il4965_txq_ctx_alloc(struct il_priv *il)
+{
+ int ret, txq_id;
+ unsigned long flags;
+
+ /* Free all tx/cmd queues and keep-warm buffer */
+ il4965_hw_txq_ctx_free(il);
+
+ ret =
+ il4965_alloc_dma_ptr(il, &il->scd_bc_tbls,
+ il->hw_params.scd_bc_tbls_size);
+ if (ret) {
+ IL_ERR("Scheduler BC Table allocation failed\n");
+ goto error_bc_tbls;
+ }
+ /* Alloc keep-warm buffer */
+ ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE);
+ if (ret) {
+ IL_ERR("Keep Warm allocation failed\n");
+ goto error_kw;
+ }
+
+ /* allocate tx queue structure */
+ ret = il_alloc_txq_mem(il);
+ if (ret)
+ goto error;
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ /* Turn off all Tx DMA fifos */
+ il4965_txq_set_sched(il, 0);
+
+ /* Tell NIC where to find the "keep warm" buffer */
+ il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ /* Alloc and init all Tx queues, including the command queue (#4/#9) */
+ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) {
+ ret = il_tx_queue_init(il, txq_id);
+ if (ret) {
+ IL_ERR("Tx %d queue init failed\n", txq_id);
+ goto error;
+ }
+ }
+
+ return ret;
+
+error:
+ il4965_hw_txq_ctx_free(il);
+ il4965_free_dma_ptr(il, &il->kw);
+error_kw:
+ il4965_free_dma_ptr(il, &il->scd_bc_tbls);
+error_bc_tbls:
+ return ret;
+}
+
+void
+il4965_txq_ctx_reset(struct il_priv *il)
+{
+ int txq_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ /* Turn off all Tx DMA fifos */
+ il4965_txq_set_sched(il, 0);
+ /* Tell NIC where to find the "keep warm" buffer */
+ il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ /* Alloc and init all Tx queues, including the command queue (#4) */
+ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
+ il_tx_queue_reset(il, txq_id);
+}
+
+static void
+il4965_txq_ctx_unmap(struct il_priv *il)
+{
+ int txq_id;
+
+ if (!il->txq)
+ return;
+
+ /* Unmap DMA from host system and free skb's */
+ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
+ if (txq_id == il->cmd_queue)
+ il_cmd_queue_unmap(il);
+ else
+ il_tx_queue_unmap(il, txq_id);
+}
+
+/**
+ * il4965_txq_ctx_stop - Stop all Tx DMA channels
+ */
+void
+il4965_txq_ctx_stop(struct il_priv *il)
+{
+ int ch, ret;
+
+ _il_wr_prph(il, IL49_SCD_TXFACT, 0);
+
+ /* Stop each Tx DMA channel, and wait for it to be idle */
+ for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) {
+ _il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
+ ret =
+ _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG,
+ FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
+ FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
+ 1000);
+ if (ret < 0)
+ IL_ERR("Timeout stopping DMA channel %d [0x%08x]",
+ ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG));
+ }
+}
+
+/*
+ * Find first available (lowest unused) Tx Queue, mark it "active".
+ * Called only when finding queue for aggregation.
+ * Should never return anything < 7, because they should already
+ * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
+ */
+static int
+il4965_txq_ctx_activate_free(struct il_priv *il)
+{
+ int txq_id;
+
+ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
+ if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk))
+ return txq_id;
+ return -1;
+}
+
+/**
+ * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration
+ */
+static void
+il4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id)
+{
+ /* Simply stop the queue, but don't change any configuration;
+ * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
+ il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id),
+ (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
+ (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
+}
+
+/**
+ * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue
+ */
+static int
+il4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id)
+{
+ u32 tbl_dw_addr;
+ u32 tbl_dw;
+ u16 scd_q2ratid;
+
+ scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;
+
+ tbl_dw_addr =
+ il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
+
+ tbl_dw = il_read_targ_mem(il, tbl_dw_addr);
+
+ if (txq_id & 0x1)
+ tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
+ else
+ tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
+
+ il_write_targ_mem(il, tbl_dw_addr, tbl_dw);
+
+ return 0;
+}
+
+/**
+ * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue
+ *
+ * NOTE: txq_id must be greater than IL49_FIRST_AMPDU_QUEUE,
+ * i.e. it must be one of the higher queues used for aggregation
+ */
+static int
+il4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id,
+ int tid, u16 ssn_idx)
+{
+ unsigned long flags;
+ u16 ra_tid;
+ int ret;
+
+ if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
+ (IL49_FIRST_AMPDU_QUEUE +
+ il->cfg->num_of_ampdu_queues <= txq_id)) {
+ IL_WARN("queue number out of range: %d, must be %d to %d\n",
+ txq_id, IL49_FIRST_AMPDU_QUEUE,
+ IL49_FIRST_AMPDU_QUEUE +
+ il->cfg->num_of_ampdu_queues - 1);
+ return -EINVAL;
+ }
+
+ ra_tid = BUILD_RAxTID(sta_id, tid);
+
+ /* Modify device's station table to Tx this TID */
+ ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ /* Stop this Tx queue before configuring it */
+ il4965_tx_queue_stop_scheduler(il, txq_id);
+
+ /* Map receiver-address / traffic-ID to this queue */
+ il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id);
+
+ /* Set this queue as a chain-building queue */
+ il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
+
+ /* Place first TFD at idx corresponding to start sequence number.
+ * Assumes that ssn_idx is valid (!= 0xFFF) */
+ il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
+ il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
+ il4965_set_wr_ptrs(il, txq_id, ssn_idx);
+
+ /* Set up Tx win size and frame limit for this queue */
+ il_write_targ_mem(il,
+ il->scd_base_addr +
+ IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id),
+ (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS)
+ & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
+
+ il_write_targ_mem(il,
+ il->scd_base_addr +
+ IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
+ (SCD_FRAME_LIMIT <<
+ IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+ IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
+
+ il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));
+
+ /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
+ il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1);
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ return 0;
+}
+
+int
+il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u16 * ssn)
+{
+ int sta_id;
+ int tx_fifo;
+ int txq_id;
+ int ret;
+ unsigned long flags;
+ struct il_tid_data *tid_data;
+
+ /* FIXME: warning if tx fifo not found ? */
+ tx_fifo = il4965_get_fifo_from_tid(tid);
+ if (unlikely(tx_fifo < 0))
+ return tx_fifo;
+
+ D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid);
+
+ sta_id = il_sta_id(sta);
+ if (sta_id == IL_INVALID_STATION) {
+ IL_ERR("Start AGG on invalid station\n");
+ return -ENXIO;
+ }
+ if (unlikely(tid >= MAX_TID_COUNT))
+ return -EINVAL;
+
+ if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) {
+ IL_ERR("Start AGG when state is not IL_AGG_OFF !\n");
+ return -ENXIO;
+ }
+
+ txq_id = il4965_txq_ctx_activate_free(il);
+ if (txq_id == -1) {
+ IL_ERR("No free aggregation queue available\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ tid_data = &il->stations[sta_id].tid[tid];
+ *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
+ tid_data->agg.txq_id = txq_id;
+ il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id);
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ tid_data = &il->stations[sta_id].tid[tid];
+ if (tid_data->tfds_in_queue == 0) {
+ D_HT("HW queue is empty\n");
+ tid_data->agg.state = IL_AGG_ON;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ } else {
+ D_HT("HW queue is NOT empty: %d packets in HW queue\n",
+ tid_data->tfds_in_queue);
+ tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA;
+ }
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+ return ret;
+}
+
+/**
+ * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE
+ * il->lock must be held by the caller
+ */
+static int
+il4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo)
+{
+ if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
+ (IL49_FIRST_AMPDU_QUEUE +
+ il->cfg->num_of_ampdu_queues <= txq_id)) {
+ IL_WARN("queue number out of range: %d, must be %d to %d\n",
+ txq_id, IL49_FIRST_AMPDU_QUEUE,
+ IL49_FIRST_AMPDU_QUEUE +
+ il->cfg->num_of_ampdu_queues - 1);
+ return -EINVAL;
+ }
+
+ il4965_tx_queue_stop_scheduler(il, txq_id);
+
+ il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
+
+ il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
+ il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
+ /* supposes that ssn_idx is valid (!= 0xFFF) */
+ il4965_set_wr_ptrs(il, txq_id, ssn_idx);
+
+ il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));
+ il_txq_ctx_deactivate(il, txq_id);
+ il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0);
+
+ return 0;
+}
+
+int
+il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid)
+{
+ int tx_fifo_id, txq_id, sta_id, ssn;
+ struct il_tid_data *tid_data;
+ int write_ptr, read_ptr;
+ unsigned long flags;
+
+ /* FIXME: warning if tx_fifo_id not found ? */
+ tx_fifo_id = il4965_get_fifo_from_tid(tid);
+ if (unlikely(tx_fifo_id < 0))
+ return tx_fifo_id;
+
+ sta_id = il_sta_id(sta);
+
+ if (sta_id == IL_INVALID_STATION) {
+ IL_ERR("Invalid station for AGG tid %d\n", tid);
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+
+ tid_data = &il->stations[sta_id].tid[tid];
+ ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
+ txq_id = tid_data->agg.txq_id;
+
+ switch (il->stations[sta_id].tid[tid].agg.state) {
+ case IL_EMPTYING_HW_QUEUE_ADDBA:
+ /*
+ * This can happen if the peer stops aggregation
+ * again before we've had a chance to drain the
+ * queue we selected previously, i.e. before the
+ * session was really started completely.
+ */
+ D_HT("AGG stop before setup done\n");
+ goto turn_off;
+ case IL_AGG_ON:
+ break;
+ default:
+ IL_WARN("Stopping AGG while state not ON or starting\n");
+ }
+
+ write_ptr = il->txq[txq_id].q.write_ptr;
+ read_ptr = il->txq[txq_id].q.read_ptr;
+
+ /* The queue is not empty */
+ if (write_ptr != read_ptr) {
+ D_HT("Stopping a non empty AGG HW QUEUE\n");
+ il->stations[sta_id].tid[tid].agg.state =
+ IL_EMPTYING_HW_QUEUE_DELBA;
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+ return 0;
+ }
+
+ D_HT("HW queue is empty\n");
+turn_off:
+ il->stations[sta_id].tid[tid].agg.state = IL_AGG_OFF;
+
+ /* do not restore/save irqs */
+ spin_unlock(&il->sta_lock);
+ spin_lock(&il->lock);
+
+ /*
+ * the only reason this call can fail is queue number out of range,
+ * which can happen if uCode is reloaded and all the station
+ * information are lost. if it is outside the range, there is no need
+ * to deactivate the uCode queue, just return "success" to allow
+ * mac80211 to clean up it own data.
+ */
+ il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo_id);
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+
+ return 0;
+}
+
+int
+il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id)
+{
+ struct il_queue *q = &il->txq[txq_id].q;
+ u8 *addr = il->stations[sta_id].sta.sta.addr;
+ struct il_tid_data *tid_data = &il->stations[sta_id].tid[tid];
+
+ lockdep_assert_held(&il->sta_lock);
+
+ switch (il->stations[sta_id].tid[tid].agg.state) {
+ case IL_EMPTYING_HW_QUEUE_DELBA:
+ /* We are reclaiming the last packet of the */
+ /* aggregated HW queue */
+ if (txq_id == tid_data->agg.txq_id &&
+ q->read_ptr == q->write_ptr) {
+ u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
+ int tx_fifo = il4965_get_fifo_from_tid(tid);
+ D_HT("HW queue empty: continue DELBA flow\n");
+ il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo);
+ tid_data->agg.state = IL_AGG_OFF;
+ ieee80211_stop_tx_ba_cb_irqsafe(il->vif, addr, tid);
+ }
+ break;
+ case IL_EMPTYING_HW_QUEUE_ADDBA:
+ /* We are reclaiming the last packet of the queue */
+ if (tid_data->tfds_in_queue == 0) {
+ D_HT("HW queue empty: continue ADDBA flow\n");
+ tid_data->agg.state = IL_AGG_ON;
+ ieee80211_start_tx_ba_cb_irqsafe(il->vif, addr, tid);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void
+il4965_non_agg_tx_status(struct il_priv *il, const u8 *addr1)
+{
+ struct ieee80211_sta *sta;
+ struct il_station_priv *sta_priv;
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(il->vif, addr1);
+ if (sta) {
+ sta_priv = (void *)sta->drv_priv;
+ /* avoid atomic ops if this isn't a client */
+ if (sta_priv->client &&
+ atomic_dec_return(&sta_priv->pending_frames) == 0)
+ ieee80211_sta_block_awake(il->hw, sta, false);
+ }
+ rcu_read_unlock();
+}
+
+static void
+il4965_tx_status(struct il_priv *il, struct sk_buff *skb, bool is_agg)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!is_agg)
+ il4965_non_agg_tx_status(il, hdr->addr1);
+
+ ieee80211_tx_status_irqsafe(il->hw, skb);
+}
+
+int
+il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx)
+{
+ struct il_tx_queue *txq = &il->txq[txq_id];
+ struct il_queue *q = &txq->q;
+ int nfreed = 0;
+ struct ieee80211_hdr *hdr;
+ struct sk_buff *skb;
+
+ if (idx >= q->n_bd || il_queue_used(q, idx) == 0) {
+ IL_ERR("Read idx for DMA queue txq id (%d), idx %d, "
+ "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd,
+ q->write_ptr, q->read_ptr);
+ return 0;
+ }
+
+ for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
+ q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+
+ skb = txq->skbs[txq->q.read_ptr];
+
+ if (WARN_ON_ONCE(skb == NULL))
+ continue;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ nfreed++;
+
+ il4965_tx_status(il, skb, txq_id >= IL4965_FIRST_AMPDU_QUEUE);
+
+ txq->skbs[txq->q.read_ptr] = NULL;
+ il->ops->txq_free_tfd(il, txq);
+ }
+ return nfreed;
+}
+
+/**
+ * il4965_tx_status_reply_compressed_ba - Update tx status from block-ack
+ *
+ * Go through block-ack's bitmap of ACK'd frames, update driver's record of
+ * ACK vs. not. This gets sent to mac80211, then to rate scaling algo.
+ */
+static int
+il4965_tx_status_reply_compressed_ba(struct il_priv *il, struct il_ht_agg *agg,
+ struct il_compressed_ba_resp *ba_resp)
+{
+ int i, sh, ack;
+ u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl);
+ u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
+ int successes = 0;
+ struct ieee80211_tx_info *info;
+ u64 bitmap, sent_bitmap;
+
+ if (unlikely(!agg->wait_for_ba)) {
+ if (unlikely(ba_resp->bitmap))
+ IL_ERR("Received BA when not expected\n");
+ return -EINVAL;
+ }
+
+ /* Mark that the expected block-ack response arrived */
+ agg->wait_for_ba = 0;
+ D_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl);
+
+ /* Calculate shift to align block-ack bits with our Tx win bits */
+ sh = agg->start_idx - SEQ_TO_IDX(seq_ctl >> 4);
+ if (sh < 0) /* tbw something is wrong with indices */
+ sh += 0x100;
+
+ if (agg->frame_count > (64 - sh)) {
+ D_TX_REPLY("more frames than bitmap size");
+ return -1;
+ }
+
+ /* don't use 64-bit values for now */
+ bitmap = le64_to_cpu(ba_resp->bitmap) >> sh;
+
+ /* check for success or failure according to the
+ * transmitted bitmap and block-ack bitmap */
+ sent_bitmap = bitmap & agg->bitmap;
+
+ /* For each frame attempted in aggregation,
+ * update driver's record of tx frame's status. */
+ i = 0;
+ while (sent_bitmap) {
+ ack = sent_bitmap & 1ULL;
+ successes += ack;
+ D_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack ? "ACK" : "NACK",
+ i, (agg->start_idx + i) & 0xff, agg->start_idx + i);
+ sent_bitmap >>= 1;
+ ++i;
+ }
+
+ D_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap);
+
+ info = IEEE80211_SKB_CB(il->txq[scd_flow].skbs[agg->start_idx]);
+ memset(&info->status, 0, sizeof(info->status));
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ info->flags |= IEEE80211_TX_STAT_AMPDU;
+ info->status.ampdu_ack_len = successes;
+ info->status.ampdu_len = agg->frame_count;
+ il4965_hwrate_to_tx_control(il, agg->rate_n_flags, info);
+
+ return 0;
+}
+
+static inline bool
+il4965_is_tx_success(u32 status)
+{
+ status &= TX_STATUS_MSK;
+ return (status == TX_STATUS_SUCCESS || status == TX_STATUS_DIRECT_DONE);
+}
+
+static u8
+il4965_find_station(struct il_priv *il, const u8 *addr)
+{
+ int i;
+ int start = 0;
+ int ret = IL_INVALID_STATION;
+ unsigned long flags;
+
+ if (il->iw_mode == NL80211_IFTYPE_ADHOC)
+ start = IL_STA_ID;
+
+ if (is_broadcast_ether_addr(addr))
+ return il->hw_params.bcast_id;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ for (i = start; i < il->hw_params.max_stations; i++)
+ if (il->stations[i].used &&
+ ether_addr_equal(il->stations[i].sta.sta.addr, addr)) {
+ ret = i;
+ goto out;
+ }
+
+ D_ASSOC("can not find STA %pM total %d\n", addr, il->num_stations);
+
+out:
+ /*
+ * It may be possible that more commands interacting with stations
+ * arrive before we completed processing the adding of
+ * station
+ */
+ if (ret != IL_INVALID_STATION &&
+ (!(il->stations[ret].used & IL_STA_UCODE_ACTIVE) ||
+ ((il->stations[ret].used & IL_STA_UCODE_ACTIVE) &&
+ (il->stations[ret].used & IL_STA_UCODE_INPROGRESS)))) {
+ IL_ERR("Requested station info for sta %d before ready.\n",
+ ret);
+ ret = IL_INVALID_STATION;
+ }
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+ return ret;
+}
+
+static int
+il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr)
+{
+ if (il->iw_mode == NL80211_IFTYPE_STATION)
+ return IL_AP_ID;
+ else {
+ u8 *da = ieee80211_get_DA(hdr);
+
+ return il4965_find_station(il, da);
+ }
+}
+
+static inline u32
+il4965_get_scd_ssn(struct il4965_tx_resp *tx_resp)
+{
+ return le32_to_cpup(&tx_resp->u.status +
+ tx_resp->frame_count) & IEEE80211_MAX_SN;
+}
+
+static inline u32
+il4965_tx_status_to_mac80211(u32 status)
+{
+ status &= TX_STATUS_MSK;
+
+ switch (status) {
+ case TX_STATUS_SUCCESS:
+ case TX_STATUS_DIRECT_DONE:
+ return IEEE80211_TX_STAT_ACK;
+ case TX_STATUS_FAIL_DEST_PS:
+ return IEEE80211_TX_STAT_TX_FILTERED;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * il4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue
+ */
+static int
+il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg,
+ struct il4965_tx_resp *tx_resp, int txq_id,
+ u16 start_idx)
+{
+ u16 status;
+ struct agg_tx_status *frame_status = tx_resp->u.agg_status;
+ struct ieee80211_tx_info *info = NULL;
+ struct ieee80211_hdr *hdr = NULL;
+ u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
+ int i, sh, idx;
+ u16 seq;
+ if (agg->wait_for_ba)
+ D_TX_REPLY("got tx response w/o block-ack\n");
+
+ agg->frame_count = tx_resp->frame_count;
+ agg->start_idx = start_idx;
+ agg->rate_n_flags = rate_n_flags;
+ agg->bitmap = 0;
+
+ /* num frames attempted by Tx command */
+ if (agg->frame_count == 1) {
+ /* Only one frame was attempted; no block-ack will arrive */
+ status = le16_to_cpu(frame_status[0].status);
+ idx = start_idx;
+
+ D_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n",
+ agg->frame_count, agg->start_idx, idx);
+
+ info = IEEE80211_SKB_CB(il->txq[txq_id].skbs[idx]);
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ info->flags |= il4965_tx_status_to_mac80211(status);
+ il4965_hwrate_to_tx_control(il, rate_n_flags, info);
+
+ D_TX_REPLY("1 Frame 0x%x failure :%d\n", status & 0xff,
+ tx_resp->failure_frame);
+ D_TX_REPLY("Rate Info rate_n_flags=%x\n", rate_n_flags);
+
+ agg->wait_for_ba = 0;
+ } else {
+ /* Two or more frames were attempted; expect block-ack */
+ u64 bitmap = 0;
+ int start = agg->start_idx;
+ struct sk_buff *skb;
+
+ /* Construct bit-map of pending frames within Tx win */
+ for (i = 0; i < agg->frame_count; i++) {
+ u16 sc;
+ status = le16_to_cpu(frame_status[i].status);
+ seq = le16_to_cpu(frame_status[i].sequence);
+ idx = SEQ_TO_IDX(seq);
+ txq_id = SEQ_TO_QUEUE(seq);
+
+ if (status &
+ (AGG_TX_STATE_FEW_BYTES_MSK |
+ AGG_TX_STATE_ABORT_MSK))
+ continue;
+
+ D_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n",
+ agg->frame_count, txq_id, idx);
+
+ skb = il->txq[txq_id].skbs[idx];
+ if (WARN_ON_ONCE(skb == NULL))
+ return -1;
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ sc = le16_to_cpu(hdr->seq_ctrl);
+ if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) {
+ IL_ERR("BUG_ON idx doesn't match seq control"
+ " idx=%d, seq_idx=%d, seq=%d\n", idx,
+ IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl);
+ return -1;
+ }
+
+ D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx,
+ IEEE80211_SEQ_TO_SN(sc));
+
+ sh = idx - start;
+ if (sh > 64) {
+ sh = (start - idx) + 0xff;
+ bitmap = bitmap << sh;
+ sh = 0;
+ start = idx;
+ } else if (sh < -64)
+ sh = 0xff - (start - idx);
+ else if (sh < 0) {
+ sh = start - idx;
+ start = idx;
+ bitmap = bitmap << sh;
+ sh = 0;
+ }
+ bitmap |= 1ULL << sh;
+ D_TX_REPLY("start=%d bitmap=0x%llx\n", start,
+ (unsigned long long)bitmap);
+ }
+
+ agg->bitmap = bitmap;
+ agg->start_idx = start;
+ D_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n",
+ agg->frame_count, agg->start_idx,
+ (unsigned long long)agg->bitmap);
+
+ if (bitmap)
+ agg->wait_for_ba = 1;
+ }
+ return 0;
+}
+
+/**
+ * il4965_hdl_tx - Handle standard (non-aggregation) Tx response
+ */
+static void
+il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ int txq_id = SEQ_TO_QUEUE(sequence);
+ int idx = SEQ_TO_IDX(sequence);
+ struct il_tx_queue *txq = &il->txq[txq_id];
+ struct sk_buff *skb;
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_info *info;
+ struct il4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
+ u32 status = le32_to_cpu(tx_resp->u.status);
+ int uninitialized_var(tid);
+ int sta_id;
+ int freed;
+ u8 *qc = NULL;
+ unsigned long flags;
+
+ if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) {
+ IL_ERR("Read idx for DMA queue txq_id (%d) idx %d "
+ "is out of range [0-%d] %d %d\n", txq_id, idx,
+ txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr);
+ return;
+ }
+
+ txq->time_stamp = jiffies;
+
+ skb = txq->skbs[txq->q.read_ptr];
+ info = IEEE80211_SKB_CB(skb);
+ memset(&info->status, 0, sizeof(info->status));
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & 0xf;
+ }
+
+ sta_id = il4965_get_ra_sta_id(il, hdr);
+ if (txq->sched_retry && unlikely(sta_id == IL_INVALID_STATION)) {
+ IL_ERR("Station not known\n");
+ 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);
+ struct il_ht_agg *agg = NULL;
+ WARN_ON(!qc);
+
+ agg = &il->stations[sta_id].tid[tid].agg;
+
+ il4965_tx_status_reply_tx(il, agg, tx_resp, txq_id, idx);
+
+ /* check if BAR is needed */
+ if (tx_resp->frame_count == 1 &&
+ !il4965_is_tx_success(status))
+ info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+
+ if (txq->q.read_ptr != (scd_ssn & 0xff)) {
+ idx = il_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
+ D_TX_REPLY("Retry scheduler reclaim scd_ssn "
+ "%d idx %d\n", scd_ssn, idx);
+ freed = il4965_tx_queue_reclaim(il, txq_id, idx);
+ if (qc)
+ il4965_free_tfds_in_queue(il, sta_id, tid,
+ freed);
+
+ if (il->mac80211_registered &&
+ il_queue_space(&txq->q) > txq->q.low_mark &&
+ agg->state != IL_EMPTYING_HW_QUEUE_DELBA)
+ il_wake_queue(il, txq);
+ }
+ } else {
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
+ info->flags |= il4965_tx_status_to_mac80211(status);
+ il4965_hwrate_to_tx_control(il,
+ le32_to_cpu(tx_resp->rate_n_flags),
+ info);
+
+ D_TX_REPLY("TXQ %d status %s (0x%08x) "
+ "rate_n_flags 0x%x retries %d\n", txq_id,
+ il4965_get_tx_fail_reason(status), status,
+ le32_to_cpu(tx_resp->rate_n_flags),
+ tx_resp->failure_frame);
+
+ freed = il4965_tx_queue_reclaim(il, txq_id, idx);
+ if (qc && likely(sta_id != IL_INVALID_STATION))
+ il4965_free_tfds_in_queue(il, sta_id, tid, freed);
+ else if (sta_id == IL_INVALID_STATION)
+ D_TX_REPLY("Station not known\n");
+
+ if (il->mac80211_registered &&
+ il_queue_space(&txq->q) > txq->q.low_mark)
+ il_wake_queue(il, txq);
+ }
+ if (qc && likely(sta_id != IL_INVALID_STATION))
+ il4965_txq_check_empty(il, sta_id, tid, txq_id);
+
+ il4965_check_abort_status(il, tx_resp->frame_count, status);
+
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+}
+
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+void
+il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags,
+ struct ieee80211_tx_info *info)
+{
+ struct ieee80211_tx_rate *r = &info->status.rates[0];
+
+ info->status.antenna =
+ ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+ if (rate_n_flags & RATE_MCS_HT_MSK)
+ r->flags |= IEEE80211_TX_RC_MCS;
+ if (rate_n_flags & RATE_MCS_GF_MSK)
+ r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
+ if (rate_n_flags & RATE_MCS_HT40_MSK)
+ r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ if (rate_n_flags & RATE_MCS_DUP_MSK)
+ r->flags |= IEEE80211_TX_RC_DUP_DATA;
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ r->flags |= IEEE80211_TX_RC_SHORT_GI;
+ r->idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, info->band);
+}
+
+/**
+ * il4965_hdl_compressed_ba - Handler for N_COMPRESSED_BA
+ *
+ * Handles block-acknowledge notification from device, which reports success
+ * of frames sent via aggregation.
+ */
+static void
+il4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
+ struct il_tx_queue *txq = NULL;
+ struct il_ht_agg *agg;
+ int idx;
+ int sta_id;
+ int tid;
+ unsigned long flags;
+
+ /* "flow" corresponds to Tx queue */
+ u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
+
+ /* "ssn" is start of block-ack Tx win, corresponds to idx
+ * (in Tx queue's circular buffer) of first TFD/frame in win */
+ u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
+
+ if (scd_flow >= il->hw_params.max_txq_num) {
+ IL_ERR("BUG_ON scd_flow is bigger than number of queues\n");
+ return;
+ }
+
+ txq = &il->txq[scd_flow];
+ sta_id = ba_resp->sta_id;
+ tid = ba_resp->tid;
+ agg = &il->stations[sta_id].tid[tid].agg;
+ if (unlikely(agg->txq_id != scd_flow)) {
+ /*
+ * FIXME: this is a uCode bug which need to be addressed,
+ * log the information and return for now!
+ * since it is possible happen very often and in order
+ * not to fill the syslog, don't enable the logging by default
+ */
+ D_TX_REPLY("BA scd_flow %d does not match txq_id %d\n",
+ scd_flow, agg->txq_id);
+ return;
+ }
+
+ /* Find idx just before block-ack win */
+ idx = il_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+
+ D_TX_REPLY("N_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n",
+ agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32,
+ ba_resp->sta_id);
+ D_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx," "scd_flow = "
+ "%d, scd_ssn = %d\n", ba_resp->tid, ba_resp->seq_ctl,
+ (unsigned long long)le64_to_cpu(ba_resp->bitmap),
+ ba_resp->scd_flow, ba_resp->scd_ssn);
+ D_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx\n", agg->start_idx,
+ (unsigned long long)agg->bitmap);
+
+ /* Update driver's record of ACK vs. not for each frame in win */
+ il4965_tx_status_reply_compressed_ba(il, agg, ba_resp);
+
+ /* Release all TFDs before the SSN, i.e. all TFDs in front of
+ * block-ack win (we assume that they've been successfully
+ * transmitted ... if not, it's too late anyway). */
+ if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
+ /* calculate mac80211 ampdu sw queue to wake */
+ int freed = il4965_tx_queue_reclaim(il, scd_flow, idx);
+ il4965_free_tfds_in_queue(il, sta_id, tid, freed);
+
+ if (il_queue_space(&txq->q) > txq->q.low_mark &&
+ il->mac80211_registered &&
+ agg->state != IL_EMPTYING_HW_QUEUE_DELBA)
+ il_wake_queue(il, txq);
+
+ il4965_txq_check_empty(il, sta_id, tid, scd_flow);
+ }
+
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+}
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+const char *
+il4965_get_tx_fail_reason(u32 status)
+{
+#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
+#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+
+ switch (status & TX_STATUS_MSK) {
+ case TX_STATUS_SUCCESS:
+ return "SUCCESS";
+ TX_STATUS_POSTPONE(DELAY);
+ TX_STATUS_POSTPONE(FEW_BYTES);
+ TX_STATUS_POSTPONE(QUIET_PERIOD);
+ TX_STATUS_POSTPONE(CALC_TTAK);
+ TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
+ TX_STATUS_FAIL(SHORT_LIMIT);
+ TX_STATUS_FAIL(LONG_LIMIT);
+ TX_STATUS_FAIL(FIFO_UNDERRUN);
+ TX_STATUS_FAIL(DRAIN_FLOW);
+ TX_STATUS_FAIL(RFKILL_FLUSH);
+ TX_STATUS_FAIL(LIFE_EXPIRE);
+ TX_STATUS_FAIL(DEST_PS);
+ TX_STATUS_FAIL(HOST_ABORTED);
+ TX_STATUS_FAIL(BT_RETRY);
+ TX_STATUS_FAIL(STA_INVALID);
+ TX_STATUS_FAIL(FRAG_DROPPED);
+ TX_STATUS_FAIL(TID_DISABLE);
+ TX_STATUS_FAIL(FIFO_FLUSHED);
+ TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
+ TX_STATUS_FAIL(PASSIVE_NO_RX);
+ TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
+ }
+
+ return "UNKNOWN";
+
+#undef TX_STATUS_FAIL
+#undef TX_STATUS_POSTPONE
+}
+#endif /* CONFIG_IWLEGACY_DEBUG */
+
+static struct il_link_quality_cmd *
+il4965_sta_alloc_lq(struct il_priv *il, u8 sta_id)
+{
+ int i, r;
+ struct il_link_quality_cmd *link_cmd;
+ u32 rate_flags = 0;
+ __le32 rate_n_flags;
+
+ link_cmd = kzalloc(sizeof(struct il_link_quality_cmd), GFP_KERNEL);
+ if (!link_cmd) {
+ IL_ERR("Unable to allocate memory for LQ cmd.\n");
+ return NULL;
+ }
+ /* Set up the rate scaling to start at selected rate, fall back
+ * all the way down to 1M in IEEE order, and then spin on 1M */
+ if (il->band == IEEE80211_BAND_5GHZ)
+ r = RATE_6M_IDX;
+ else
+ r = RATE_1M_IDX;
+
+ if (r >= IL_FIRST_CCK_RATE && r <= IL_LAST_CCK_RATE)
+ rate_flags |= RATE_MCS_CCK_MSK;
+
+ rate_flags |=
+ il4965_first_antenna(il->hw_params.
+ valid_tx_ant) << RATE_MCS_ANT_POS;
+ rate_n_flags = cpu_to_le32(il_rates[r].plcp | rate_flags);
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
+ link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
+
+ link_cmd->general_params.single_stream_ant_msk =
+ il4965_first_antenna(il->hw_params.valid_tx_ant);
+
+ link_cmd->general_params.dual_stream_ant_msk =
+ il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params.
+ valid_tx_ant);
+ if (!link_cmd->general_params.dual_stream_ant_msk) {
+ link_cmd->general_params.dual_stream_ant_msk = ANT_AB;
+ } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) {
+ link_cmd->general_params.dual_stream_ant_msk =
+ il->hw_params.valid_tx_ant;
+ }
+
+ link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+ link_cmd->agg_params.agg_time_limit =
+ cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+
+ link_cmd->sta_id = sta_id;
+
+ return link_cmd;
+}
+
+/*
+ * il4965_add_bssid_station - Add the special IBSS BSSID station
+ *
+ * Function sleeps.
+ */
+int
+il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r)
+{
+ int ret;
+ u8 sta_id;
+ struct il_link_quality_cmd *link_cmd;
+ unsigned long flags;
+
+ if (sta_id_r)
+ *sta_id_r = IL_INVALID_STATION;
+
+ ret = il_add_station_common(il, addr, 0, NULL, &sta_id);
+ if (ret) {
+ IL_ERR("Unable to add station %pM\n", addr);
+ return ret;
+ }
+
+ if (sta_id_r)
+ *sta_id_r = sta_id;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].used |= IL_STA_LOCAL;
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ /* Set up default rate scaling table in device's station table */
+ link_cmd = il4965_sta_alloc_lq(il, sta_id);
+ if (!link_cmd) {
+ IL_ERR("Unable to initialize rate scaling for station %pM.\n",
+ addr);
+ return -ENOMEM;
+ }
+
+ ret = il_send_lq_cmd(il, link_cmd, CMD_SYNC, true);
+ if (ret)
+ IL_ERR("Link quality command failed (%d)\n", ret);
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].lq = link_cmd;
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return 0;
+}
+
+static int
+il4965_static_wepkey_cmd(struct il_priv *il, bool send_if_empty)
+{
+ int i;
+ u8 buff[sizeof(struct il_wep_cmd) +
+ sizeof(struct il_wep_key) * WEP_KEYS_MAX];
+ struct il_wep_cmd *wep_cmd = (struct il_wep_cmd *)buff;
+ size_t cmd_size = sizeof(struct il_wep_cmd);
+ struct il_host_cmd cmd = {
+ .id = C_WEPKEY,
+ .data = wep_cmd,
+ .flags = CMD_SYNC,
+ };
+ bool not_empty = false;
+
+ might_sleep();
+
+ memset(wep_cmd, 0,
+ cmd_size + (sizeof(struct il_wep_key) * WEP_KEYS_MAX));
+
+ for (i = 0; i < WEP_KEYS_MAX; i++) {
+ u8 key_size = il->_4965.wep_keys[i].key_size;
+
+ wep_cmd->key[i].key_idx = i;
+ if (key_size) {
+ wep_cmd->key[i].key_offset = i;
+ not_empty = true;
+ } else
+ wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET;
+
+ wep_cmd->key[i].key_size = key_size;
+ memcpy(&wep_cmd->key[i].key[3], il->_4965.wep_keys[i].key, key_size);
+ }
+
+ wep_cmd->global_key_type = WEP_KEY_WEP_TYPE;
+ wep_cmd->num_keys = WEP_KEYS_MAX;
+
+ cmd_size += sizeof(struct il_wep_key) * WEP_KEYS_MAX;
+ cmd.len = cmd_size;
+
+ if (not_empty || send_if_empty)
+ return il_send_cmd(il, &cmd);
+ else
+ return 0;
+}
+
+int
+il4965_restore_default_wep_keys(struct il_priv *il)
+{
+ lockdep_assert_held(&il->mutex);
+
+ return il4965_static_wepkey_cmd(il, false);
+}
+
+int
+il4965_remove_default_wep_key(struct il_priv *il,
+ struct ieee80211_key_conf *keyconf)
+{
+ int ret;
+ int idx = keyconf->keyidx;
+
+ lockdep_assert_held(&il->mutex);
+
+ D_WEP("Removing default WEP key: idx=%d\n", idx);
+
+ memset(&il->_4965.wep_keys[idx], 0, sizeof(struct il_wep_key));
+ if (il_is_rfkill(il)) {
+ D_WEP("Not sending C_WEPKEY command due to RFKILL.\n");
+ /* but keys in device are clear anyway so return success */
+ return 0;
+ }
+ ret = il4965_static_wepkey_cmd(il, 1);
+ D_WEP("Remove default WEP key: idx=%d ret=%d\n", idx, ret);
+
+ return ret;
+}
+
+int
+il4965_set_default_wep_key(struct il_priv *il,
+ struct ieee80211_key_conf *keyconf)
+{
+ int ret;
+ int len = keyconf->keylen;
+ int idx = keyconf->keyidx;
+
+ lockdep_assert_held(&il->mutex);
+
+ if (len != WEP_KEY_LEN_128 && len != WEP_KEY_LEN_64) {
+ D_WEP("Bad WEP key length %d\n", keyconf->keylen);
+ return -EINVAL;
+ }
+
+ keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
+ keyconf->hw_key_idx = HW_KEY_DEFAULT;
+ il->stations[IL_AP_ID].keyinfo.cipher = keyconf->cipher;
+
+ il->_4965.wep_keys[idx].key_size = len;
+ memcpy(&il->_4965.wep_keys[idx].key, &keyconf->key, len);
+
+ ret = il4965_static_wepkey_cmd(il, false);
+
+ D_WEP("Set default WEP key: len=%d idx=%d ret=%d\n", len, idx, ret);
+ return ret;
+}
+
+static int
+il4965_set_wep_dynamic_key_info(struct il_priv *il,
+ struct ieee80211_key_conf *keyconf, u8 sta_id)
+{
+ unsigned long flags;
+ __le16 key_flags = 0;
+ struct il_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&il->mutex);
+
+ keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK);
+ key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags &= ~STA_KEY_FLG_INVALID;
+
+ if (keyconf->keylen == WEP_KEY_LEN_128)
+ key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
+
+ if (sta_id == il->hw_params.bcast_id)
+ key_flags |= STA_KEY_MULTICAST_MSK;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+
+ il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
+ il->stations[sta_id].keyinfo.keylen = keyconf->keylen;
+ il->stations[sta_id].keyinfo.keyidx = keyconf->keyidx;
+
+ memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen);
+
+ memcpy(&il->stations[sta_id].sta.key.key[3], keyconf->key,
+ keyconf->keylen);
+
+ if ((il->stations[sta_id].sta.key.
+ key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
+ il->stations[sta_id].sta.key.key_offset =
+ il_get_free_ucode_key_idx(il);
+ /* else, we are overriding an existing key => no need to allocated room
+ * in uCode. */
+
+ WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
+ "no space for a new key");
+
+ il->stations[sta_id].sta.key.key_flags = key_flags;
+ il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+
+ memcpy(&sta_cmd, &il->stations[sta_id].sta,
+ sizeof(struct il_addsta_cmd));
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+}
+
+static int
+il4965_set_ccmp_dynamic_key_info(struct il_priv *il,
+ struct ieee80211_key_conf *keyconf, u8 sta_id)
+{
+ unsigned long flags;
+ __le16 key_flags = 0;
+ struct il_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&il->mutex);
+
+ key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK);
+ key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags &= ~STA_KEY_FLG_INVALID;
+
+ if (sta_id == il->hw_params.bcast_id)
+ key_flags |= STA_KEY_MULTICAST_MSK;
+
+ keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
+ il->stations[sta_id].keyinfo.keylen = keyconf->keylen;
+
+ memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen);
+
+ memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen);
+
+ if ((il->stations[sta_id].sta.key.
+ key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
+ il->stations[sta_id].sta.key.key_offset =
+ il_get_free_ucode_key_idx(il);
+ /* else, we are overriding an existing key => no need to allocated room
+ * in uCode. */
+
+ WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
+ "no space for a new key");
+
+ il->stations[sta_id].sta.key.key_flags = key_flags;
+ il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+
+ memcpy(&sta_cmd, &il->stations[sta_id].sta,
+ sizeof(struct il_addsta_cmd));
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+}
+
+static int
+il4965_set_tkip_dynamic_key_info(struct il_priv *il,
+ struct ieee80211_key_conf *keyconf, u8 sta_id)
+{
+ unsigned long flags;
+ int ret = 0;
+ __le16 key_flags = 0;
+
+ key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
+ key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags &= ~STA_KEY_FLG_INVALID;
+
+ if (sta_id == il->hw_params.bcast_id)
+ key_flags |= STA_KEY_MULTICAST_MSK;
+
+ keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+
+ il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
+ il->stations[sta_id].keyinfo.keylen = 16;
+
+ if ((il->stations[sta_id].sta.key.
+ key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
+ il->stations[sta_id].sta.key.key_offset =
+ il_get_free_ucode_key_idx(il);
+ /* else, we are overriding an existing key => no need to allocated room
+ * in uCode. */
+
+ WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
+ "no space for a new key");
+
+ il->stations[sta_id].sta.key.key_flags = key_flags;
+
+ /* This copy is acutally not needed: we get the key with each TX */
+ memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, 16);
+
+ memcpy(il->stations[sta_id].sta.key.key, keyconf->key, 16);
+
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return ret;
+}
+
+void
+il4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta, u32 iv32, u16 *phase1key)
+{
+ u8 sta_id;
+ unsigned long flags;
+ int i;
+
+ if (il_scan_cancel(il)) {
+ /* cancel scan failed, just live w/ bad key and rely
+ briefly on SW decryption */
+ return;
+ }
+
+ sta_id = il_sta_id_or_broadcast(il, sta);
+ if (sta_id == IL_INVALID_STATION)
+ return;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+
+ il->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32;
+
+ for (i = 0; i < 5; i++)
+ il->stations[sta_id].sta.key.tkip_rx_ttak[i] =
+ cpu_to_le16(phase1key[i]);
+
+ il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+
+ il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC);
+
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+}
+
+int
+il4965_remove_dynamic_key(struct il_priv *il,
+ struct ieee80211_key_conf *keyconf, u8 sta_id)
+{
+ unsigned long flags;
+ u16 key_flags;
+ u8 keyidx;
+ struct il_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&il->mutex);
+
+ il->_4965.key_mapping_keys--;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ key_flags = le16_to_cpu(il->stations[sta_id].sta.key.key_flags);
+ keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3;
+
+ D_WEP("Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id);
+
+ if (keyconf->keyidx != keyidx) {
+ /* We need to remove a key with idx different that the one
+ * in the uCode. This means that the key we need to remove has
+ * been replaced by another one with different idx.
+ * Don't do anything and return ok
+ */
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+ return 0;
+ }
+
+ if (il->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_INVALID) {
+ IL_WARN("Removing wrong key %d 0x%x\n", keyconf->keyidx,
+ key_flags);
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+ return 0;
+ }
+
+ if (!test_and_clear_bit
+ (il->stations[sta_id].sta.key.key_offset, &il->ucode_key_table))
+ IL_ERR("idx %d not used in uCode key table.\n",
+ il->stations[sta_id].sta.key.key_offset);
+ memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key));
+ memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo));
+ il->stations[sta_id].sta.key.key_flags =
+ STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID;
+ il->stations[sta_id].sta.key.key_offset = keyconf->hw_key_idx;
+ il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+
+ if (il_is_rfkill(il)) {
+ D_WEP
+ ("Not sending C_ADD_STA command because RFKILL enabled.\n");
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+ return 0;
+ }
+ memcpy(&sta_cmd, &il->stations[sta_id].sta,
+ sizeof(struct il_addsta_cmd));
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+}
+
+int
+il4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf,
+ u8 sta_id)
+{
+ int ret;
+
+ lockdep_assert_held(&il->mutex);
+
+ il->_4965.key_mapping_keys++;
+ keyconf->hw_key_idx = HW_KEY_DYNAMIC;
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ ret =
+ il4965_set_ccmp_dynamic_key_info(il, keyconf, sta_id);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ ret =
+ il4965_set_tkip_dynamic_key_info(il, keyconf, sta_id);
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ ret = il4965_set_wep_dynamic_key_info(il, keyconf, sta_id);
+ break;
+ default:
+ IL_ERR("Unknown alg: %s cipher = %x\n", __func__,
+ keyconf->cipher);
+ ret = -EINVAL;
+ }
+
+ D_WEP("Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n",
+ keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret);
+
+ return ret;
+}
+
+/**
+ * il4965_alloc_bcast_station - add broadcast station into driver's station table.
+ *
+ * This adds the broadcast station into the driver's station table
+ * and marks it driver active, so that it will be restored to the
+ * device at the next best time.
+ */
+int
+il4965_alloc_bcast_station(struct il_priv *il)
+{
+ struct il_link_quality_cmd *link_cmd;
+ unsigned long flags;
+ u8 sta_id;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ sta_id = il_prep_station(il, il_bcast_addr, false, NULL);
+ if (sta_id == IL_INVALID_STATION) {
+ IL_ERR("Unable to prepare broadcast station\n");
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return -EINVAL;
+ }
+
+ il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE;
+ il->stations[sta_id].used |= IL_STA_BCAST;
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ link_cmd = il4965_sta_alloc_lq(il, sta_id);
+ if (!link_cmd) {
+ IL_ERR
+ ("Unable to initialize rate scaling for bcast station.\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].lq = link_cmd;
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return 0;
+}
+
+/**
+ * il4965_update_bcast_station - update broadcast station's LQ command
+ *
+ * Only used by iwl4965. Placed here to have all bcast station management
+ * code together.
+ */
+static int
+il4965_update_bcast_station(struct il_priv *il)
+{
+ unsigned long flags;
+ struct il_link_quality_cmd *link_cmd;
+ u8 sta_id = il->hw_params.bcast_id;
+
+ link_cmd = il4965_sta_alloc_lq(il, sta_id);
+ if (!link_cmd) {
+ IL_ERR("Unable to initialize rate scaling for bcast sta.\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ if (il->stations[sta_id].lq)
+ kfree(il->stations[sta_id].lq);
+ else
+ D_INFO("Bcast sta rate scaling has not been initialized.\n");
+ il->stations[sta_id].lq = link_cmd;
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return 0;
+}
+
+int
+il4965_update_bcast_stations(struct il_priv *il)
+{
+ return il4965_update_bcast_station(il);
+}
+
+/**
+ * il4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
+ */
+int
+il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid)
+{
+ unsigned long flags;
+ struct il_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&il->mutex);
+
+ /* Remove "disable" flag, to enable Tx for this TID */
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
+ il->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ memcpy(&sta_cmd, &il->stations[sta_id].sta,
+ sizeof(struct il_addsta_cmd));
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+}
+
+int
+il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid,
+ u16 ssn)
+{
+ unsigned long flags;
+ int sta_id;
+ struct il_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&il->mutex);
+
+ sta_id = il_sta_id(sta);
+ if (sta_id == IL_INVALID_STATION)
+ return -ENXIO;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].sta.station_flags_msk = 0;
+ il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
+ il->stations[sta_id].sta.add_immediate_ba_tid = (u8) tid;
+ il->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ memcpy(&sta_cmd, &il->stations[sta_id].sta,
+ sizeof(struct il_addsta_cmd));
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+}
+
+int
+il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid)
+{
+ unsigned long flags;
+ int sta_id;
+ struct il_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&il->mutex);
+
+ sta_id = il_sta_id(sta);
+ if (sta_id == IL_INVALID_STATION) {
+ IL_ERR("Invalid station for AGG tid %d\n", tid);
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].sta.station_flags_msk = 0;
+ il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+ il->stations[sta_id].sta.remove_immediate_ba_tid = (u8) tid;
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ memcpy(&sta_cmd, &il->stations[sta_id].sta,
+ sizeof(struct il_addsta_cmd));
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+}
+
+void
+il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ il->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK;
+ il->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
+ il->stations[sta_id].sta.sta.modify_mask =
+ STA_MODIFY_SLEEP_TX_COUNT_MSK;
+ il->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt);
+ il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC);
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+}
+
+void
+il4965_update_chain_flags(struct il_priv *il)
+{
+ if (il->ops->set_rxon_chain) {
+ il->ops->set_rxon_chain(il);
+ if (il->active.rx_chain != il->staging.rx_chain)
+ il_commit_rxon(il);
+ }
+}
+
+static void
+il4965_clear_free_frames(struct il_priv *il)
+{
+ struct list_head *element;
+
+ D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count);
+
+ while (!list_empty(&il->free_frames)) {
+ element = il->free_frames.next;
+ list_del(element);
+ kfree(list_entry(element, struct il_frame, list));
+ il->frames_count--;
+ }
+
+ if (il->frames_count) {
+ IL_WARN("%d frames still in use. Did we lose one?\n",
+ il->frames_count);
+ il->frames_count = 0;
+ }
+}
+
+static struct il_frame *
+il4965_get_free_frame(struct il_priv *il)
+{
+ struct il_frame *frame;
+ struct list_head *element;
+ if (list_empty(&il->free_frames)) {
+ frame = kzalloc(sizeof(*frame), GFP_KERNEL);
+ if (!frame) {
+ IL_ERR("Could not allocate frame!\n");
+ return NULL;
+ }
+
+ il->frames_count++;
+ return frame;
+ }
+
+ element = il->free_frames.next;
+ list_del(element);
+ return list_entry(element, struct il_frame, list);
+}
+
+static void
+il4965_free_frame(struct il_priv *il, struct il_frame *frame)
+{
+ memset(frame, 0, sizeof(*frame));
+ list_add(&frame->list, &il->free_frames);
+}
+
+static u32
+il4965_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr,
+ int left)
+{
+ lockdep_assert_held(&il->mutex);
+
+ if (!il->beacon_skb)
+ return 0;
+
+ if (il->beacon_skb->len > left)
+ return 0;
+
+ memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len);
+
+ return il->beacon_skb->len;
+}
+
+/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
+static void
+il4965_set_beacon_tim(struct il_priv *il,
+ struct il_tx_beacon_cmd *tx_beacon_cmd, u8 * beacon,
+ u32 frame_size)
+{
+ u16 tim_idx;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
+
+ /*
+ * The idx is relative to frame start but we start looking at the
+ * variable-length part of the beacon.
+ */
+ tim_idx = mgmt->u.beacon.variable - beacon;
+
+ /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+ while ((tim_idx < (frame_size - 2)) &&
+ (beacon[tim_idx] != WLAN_EID_TIM))
+ tim_idx += beacon[tim_idx + 1] + 2;
+
+ /* If TIM field was found, set variables */
+ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
+ tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
+ tx_beacon_cmd->tim_size = beacon[tim_idx + 1];
+ } else
+ IL_WARN("Unable to find TIM Element in beacon\n");
+}
+
+static unsigned int
+il4965_hw_get_beacon_cmd(struct il_priv *il, struct il_frame *frame)
+{
+ struct il_tx_beacon_cmd *tx_beacon_cmd;
+ u32 frame_size;
+ u32 rate_flags;
+ u32 rate;
+ /*
+ * We have to set up the TX command, the TX Beacon command, and the
+ * beacon contents.
+ */
+
+ lockdep_assert_held(&il->mutex);
+
+ if (!il->beacon_enabled) {
+ IL_ERR("Trying to build beacon without beaconing enabled\n");
+ return 0;
+ }
+
+ /* Initialize memory */
+ tx_beacon_cmd = &frame->u.beacon;
+ memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
+
+ /* Set up TX beacon contents */
+ frame_size =
+ il4965_fill_beacon_frame(il, tx_beacon_cmd->frame,
+ sizeof(frame->u) - sizeof(*tx_beacon_cmd));
+ if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE))
+ return 0;
+ if (!frame_size)
+ return 0;
+
+ /* Set up TX command fields */
+ tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size);
+ tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id;
+ tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+ tx_beacon_cmd->tx.tx_flags =
+ TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK |
+ TX_CMD_FLG_STA_RATE_MSK;
+
+ /* Set up TX beacon command fields */
+ il4965_set_beacon_tim(il, tx_beacon_cmd, (u8 *) tx_beacon_cmd->frame,
+ frame_size);
+
+ /* Set up packet rate and flags */
+ rate = il_get_lowest_plcp(il);
+ il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant);
+ rate_flags = BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS;
+ if ((rate >= IL_FIRST_CCK_RATE) && (rate <= IL_LAST_CCK_RATE))
+ rate_flags |= RATE_MCS_CCK_MSK;
+ tx_beacon_cmd->tx.rate_n_flags = cpu_to_le32(rate | rate_flags);
+
+ return sizeof(*tx_beacon_cmd) + frame_size;
+}
+
+int
+il4965_send_beacon_cmd(struct il_priv *il)
+{
+ struct il_frame *frame;
+ unsigned int frame_size;
+ int rc;
+
+ frame = il4965_get_free_frame(il);
+ if (!frame) {
+ IL_ERR("Could not obtain free frame buffer for beacon "
+ "command.\n");
+ return -ENOMEM;
+ }
+
+ frame_size = il4965_hw_get_beacon_cmd(il, frame);
+ if (!frame_size) {
+ IL_ERR("Error configuring the beacon command\n");
+ il4965_free_frame(il, frame);
+ return -EINVAL;
+ }
+
+ rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]);
+
+ il4965_free_frame(il, frame);
+
+ return rc;
+}
+
+static inline dma_addr_t
+il4965_tfd_tb_get_addr(struct il_tfd *tfd, u8 idx)
+{
+ struct il_tfd_tb *tb = &tfd->tbs[idx];
+
+ dma_addr_t addr = get_unaligned_le32(&tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ addr |=
+ ((dma_addr_t) (le16_to_cpu(tb->hi_n_len) & 0xF) << 16) <<
+ 16;
+
+ return addr;
+}
+
+static inline u16
+il4965_tfd_tb_get_len(struct il_tfd *tfd, u8 idx)
+{
+ struct il_tfd_tb *tb = &tfd->tbs[idx];
+
+ return le16_to_cpu(tb->hi_n_len) >> 4;
+}
+
+static inline void
+il4965_tfd_set_tb(struct il_tfd *tfd, u8 idx, dma_addr_t addr, u16 len)
+{
+ struct il_tfd_tb *tb = &tfd->tbs[idx];
+ u16 hi_n_len = len << 4;
+
+ put_unaligned_le32(addr, &tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ hi_n_len |= ((addr >> 16) >> 16) & 0xF;
+
+ tb->hi_n_len = cpu_to_le16(hi_n_len);
+
+ tfd->num_tbs = idx + 1;
+}
+
+static inline u8
+il4965_tfd_get_num_tbs(struct il_tfd *tfd)
+{
+ return tfd->num_tbs & 0x1f;
+}
+
+/**
+ * il4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
+ * @il - driver ilate data
+ * @txq - tx queue
+ *
+ * Does NOT advance any TFD circular buffer read/write idxes
+ * Does NOT free the TFD itself (which is within circular buffer)
+ */
+void
+il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq)
+{
+ struct il_tfd *tfd_tmp = (struct il_tfd *)txq->tfds;
+ struct il_tfd *tfd;
+ struct pci_dev *dev = il->pci_dev;
+ int idx = txq->q.read_ptr;
+ int i;
+ int num_tbs;
+
+ tfd = &tfd_tmp[idx];
+
+ /* Sanity check on number of chunks */
+ num_tbs = il4965_tfd_get_num_tbs(tfd);
+
+ if (num_tbs >= IL_NUM_OF_TBS) {
+ IL_ERR("Too many chunks: %i\n", num_tbs);
+ /* @todo issue fatal error, it is quite serious situation */
+ return;
+ }
+
+ /* Unmap tx_cmd */
+ if (num_tbs)
+ pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping),
+ dma_unmap_len(&txq->meta[idx], len),
+ PCI_DMA_BIDIRECTIONAL);
+
+ /* Unmap chunks, if any. */
+ for (i = 1; i < num_tbs; i++)
+ pci_unmap_single(dev, il4965_tfd_tb_get_addr(tfd, i),
+ il4965_tfd_tb_get_len(tfd, i),
+ PCI_DMA_TODEVICE);
+
+ /* free SKB */
+ if (txq->skbs) {
+ struct sk_buff *skb = txq->skbs[txq->q.read_ptr];
+
+ /* can be called from irqs-disabled context */
+ if (skb) {
+ dev_kfree_skb_any(skb);
+ txq->skbs[txq->q.read_ptr] = NULL;
+ }
+ }
+}
+
+int
+il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq,
+ dma_addr_t addr, u16 len, u8 reset, u8 pad)
+{
+ struct il_queue *q;
+ struct il_tfd *tfd, *tfd_tmp;
+ u32 num_tbs;
+
+ q = &txq->q;
+ tfd_tmp = (struct il_tfd *)txq->tfds;
+ tfd = &tfd_tmp[q->write_ptr];
+
+ if (reset)
+ memset(tfd, 0, sizeof(*tfd));
+
+ num_tbs = il4965_tfd_get_num_tbs(tfd);
+
+ /* Each TFD can point to a maximum 20 Tx buffers */
+ if (num_tbs >= IL_NUM_OF_TBS) {
+ IL_ERR("Error can not send more than %d chunks\n",
+ IL_NUM_OF_TBS);
+ return -EINVAL;
+ }
+
+ BUG_ON(addr & ~DMA_BIT_MASK(36));
+ if (unlikely(addr & ~IL_TX_DMA_MASK))
+ IL_ERR("Unaligned address = %llx\n", (unsigned long long)addr);
+
+ il4965_tfd_set_tb(tfd, num_tbs, addr, len);
+
+ return 0;
+}
+
+/*
+ * Tell nic where to find circular buffer of Tx Frame Descriptors for
+ * given Tx queue, and enable the DMA channel used for that queue.
+ *
+ * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA
+ * channels supported in hardware.
+ */
+int
+il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq)
+{
+ int txq_id = txq->q.id;
+
+ /* Circular buffer (TFD queue in DRAM) physical base address */
+ il_wr(il, FH49_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Generic RX handler implementations
+ *
+ ******************************************************************************/
+static void
+il4965_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_alive_resp *palive;
+ struct delayed_work *pwork;
+
+ palive = &pkt->u.alive_frame;
+
+ D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n",
+ palive->is_valid, palive->ver_type, palive->ver_subtype);
+
+ if (palive->ver_subtype == INITIALIZE_SUBTYPE) {
+ D_INFO("Initialization Alive received.\n");
+ memcpy(&il->card_alive_init, &pkt->u.alive_frame,
+ sizeof(struct il_init_alive_resp));
+ pwork = &il->init_alive_start;
+ } else {
+ D_INFO("Runtime Alive received.\n");
+ memcpy(&il->card_alive, &pkt->u.alive_frame,
+ sizeof(struct il_alive_resp));
+ pwork = &il->alive_start;
+ }
+
+ /* We delay the ALIVE response by 5ms to
+ * give the HW RF Kill time to activate... */
+ if (palive->is_valid == UCODE_VALID_OK)
+ queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5));
+ else
+ IL_WARN("uCode did not respond OK.\n");
+}
+
+/**
+ * il4965_bg_stats_periodic - Timer callback to queue stats
+ *
+ * This callback is provided in order to send a stats request.
+ *
+ * This timer function is continually reset to execute within
+ * 60 seconds since the last N_STATS was received. We need to
+ * ensure we receive the stats in order to update the temperature
+ * used for calibrating the TXPOWER.
+ */
+static void
+il4965_bg_stats_periodic(unsigned long data)
+{
+ struct il_priv *il = (struct il_priv *)data;
+
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return;
+
+ /* dont send host command if rf-kill is on */
+ if (!il_is_ready_rf(il))
+ return;
+
+ il_send_stats_request(il, CMD_ASYNC, false);
+}
+
+static void
+il4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il4965_beacon_notif *beacon =
+ (struct il4965_beacon_notif *)pkt->u.raw;
+#ifdef CONFIG_IWLEGACY_DEBUG
+ u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
+
+ D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n",
+ le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK,
+ beacon->beacon_notify_hdr.failure_frame,
+ le32_to_cpu(beacon->ibss_mgr_status),
+ le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate);
+#endif
+ il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
+}
+
+static void
+il4965_perform_ct_kill_task(struct il_priv *il)
+{
+ unsigned long flags;
+
+ D_POWER("Stop all queues\n");
+
+ if (il->mac80211_registered)
+ ieee80211_stop_queues(il->hw);
+
+ _il_wr(il, CSR_UCODE_DRV_GP1_SET,
+ CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+ _il_rd(il, CSR_UCODE_DRV_GP1);
+
+ spin_lock_irqsave(&il->reg_lock, flags);
+ if (likely(_il_grab_nic_access(il)))
+ _il_release_nic_access(il);
+ spin_unlock_irqrestore(&il->reg_lock, flags);
+}
+
+/* Handle notification from uCode that card's power state is changing
+ * due to software, hardware, or critical temperature RFKILL */
+static void
+il4965_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
+ unsigned long status = il->status;
+
+ D_RF_KILL("Card state received: HW:%s SW:%s CT:%s\n",
+ (flags & HW_CARD_DISABLED) ? "Kill" : "On",
+ (flags & SW_CARD_DISABLED) ? "Kill" : "On",
+ (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached");
+
+ if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) {
+
+ _il_wr(il, CSR_UCODE_DRV_GP1_SET,
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+ il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
+
+ if (!(flags & RXON_CARD_DISABLED)) {
+ _il_wr(il, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+ il_wr(il, HBUS_TARG_MBX_C,
+ HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
+ }
+ }
+
+ if (flags & CT_CARD_DISABLED)
+ il4965_perform_ct_kill_task(il);
+
+ if (flags & HW_CARD_DISABLED)
+ set_bit(S_RFKILL, &il->status);
+ else
+ clear_bit(S_RFKILL, &il->status);
+
+ if (!(flags & RXON_CARD_DISABLED))
+ il_scan_cancel(il);
+
+ if ((test_bit(S_RFKILL, &status) !=
+ test_bit(S_RFKILL, &il->status)))
+ wiphy_rfkill_set_hw_state(il->hw->wiphy,
+ test_bit(S_RFKILL, &il->status));
+ else
+ wake_up(&il->wait_command_queue);
+}
+
+/**
+ * il4965_setup_handlers - Initialize Rx handler callbacks
+ *
+ * Setup the RX handlers for each of the reply types sent from the uCode
+ * to the host.
+ *
+ * This function chains into the hardware specific files for them to setup
+ * any hardware specific handlers as well.
+ */
+static void
+il4965_setup_handlers(struct il_priv *il)
+{
+ il->handlers[N_ALIVE] = il4965_hdl_alive;
+ il->handlers[N_ERROR] = il_hdl_error;
+ il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa;
+ il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement;
+ il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep;
+ il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats;
+ il->handlers[N_BEACON] = il4965_hdl_beacon;
+
+ /*
+ * The same handler is used for both the REPLY to a discrete
+ * stats request from the host as well as for the periodic
+ * stats notifications (after received beacons) from the uCode.
+ */
+ il->handlers[C_STATS] = il4965_hdl_c_stats;
+ il->handlers[N_STATS] = il4965_hdl_stats;
+
+ il_setup_rx_scan_handlers(il);
+
+ /* status change handler */
+ il->handlers[N_CARD_STATE] = il4965_hdl_card_state;
+
+ il->handlers[N_MISSED_BEACONS] = il4965_hdl_missed_beacon;
+ /* Rx handlers */
+ il->handlers[N_RX_PHY] = il4965_hdl_rx_phy;
+ il->handlers[N_RX_MPDU] = il4965_hdl_rx;
+ il->handlers[N_RX] = il4965_hdl_rx;
+ /* block ack */
+ il->handlers[N_COMPRESSED_BA] = il4965_hdl_compressed_ba;
+ /* Tx response */
+ il->handlers[C_TX] = il4965_hdl_tx;
+}
+
+/**
+ * il4965_rx_handle - Main entry function for receiving responses from uCode
+ *
+ * Uses the il->handlers callback function array to invoke
+ * the appropriate handlers, including command responses,
+ * frame-received notifications, and other notifications.
+ */
+void
+il4965_rx_handle(struct il_priv *il)
+{
+ struct il_rx_buf *rxb;
+ struct il_rx_pkt *pkt;
+ struct il_rx_queue *rxq = &il->rxq;
+ u32 r, i;
+ int reclaim;
+ unsigned long flags;
+ u8 fill_rx = 0;
+ u32 count = 8;
+ int total_empty;
+
+ /* uCode's read idx (stored in shared DRAM) indicates the last Rx
+ * buffer that the driver may process (last buffer filled by ucode). */
+ r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF;
+ i = rxq->read;
+
+ /* Rx interrupt, but nothing sent from uCode */
+ if (i == r)
+ D_RX("r = %d, i = %d\n", r, i);
+
+ /* calculate total frames need to be restock after handling RX */
+ total_empty = r - rxq->write_actual;
+ if (total_empty < 0)
+ total_empty += RX_QUEUE_SIZE;
+
+ if (total_empty > (RX_QUEUE_SIZE / 2))
+ fill_rx = 1;
+
+ while (i != r) {
+ int len;
+
+ rxb = rxq->queue[i];
+
+ /* If an RXB doesn't have a Rx queue slot associated with it,
+ * then a bug has been introduced in the queue refilling
+ * routines -- catch it here */
+ BUG_ON(rxb == NULL);
+
+ rxq->queue[i] = NULL;
+
+ pci_unmap_page(il->pci_dev, rxb->page_dma,
+ PAGE_SIZE << il->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ pkt = rxb_addr(rxb);
+
+ len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
+ len += sizeof(u32); /* account for status word */
+
+ reclaim = il_need_reclaim(il, pkt);
+
+ /* Based on type of command response or notification,
+ * handle those that need handling via function in
+ * handlers table. See il4965_setup_handlers() */
+ if (il->handlers[pkt->hdr.cmd]) {
+ D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i,
+ il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
+ il->isr_stats.handlers[pkt->hdr.cmd]++;
+ il->handlers[pkt->hdr.cmd] (il, rxb);
+ } else {
+ /* No handling needed */
+ D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r,
+ i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
+ }
+
+ /*
+ * XXX: After here, we should always check rxb->page
+ * against NULL before touching it or its virtual
+ * memory (pkt). Because some handler might have
+ * already taken or freed the pages.
+ */
+
+ if (reclaim) {
+ /* Invoke any callbacks, transfer the buffer to caller,
+ * and fire off the (possibly) blocking il_send_cmd()
+ * as we reclaim the driver command queue */
+ if (rxb->page)
+ il_tx_cmd_complete(il, rxb);
+ else
+ IL_WARN("Claim null rxb?\n");
+ }
+
+ /* Reuse the page if possible. For notification packets and
+ * SKBs that fail to Rx correctly, add them back into the
+ * rx_free list for reuse later. */
+ spin_lock_irqsave(&rxq->lock, flags);
+ if (rxb->page != NULL) {
+ rxb->page_dma =
+ pci_map_page(il->pci_dev, rxb->page, 0,
+ PAGE_SIZE << il->hw_params.
+ rx_page_order, PCI_DMA_FROMDEVICE);
+
+ if (unlikely(pci_dma_mapping_error(il->pci_dev,
+ rxb->page_dma))) {
+ __il_free_pages(il, rxb->page);
+ rxb->page = NULL;
+ list_add_tail(&rxb->list, &rxq->rx_used);
+ } else {
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ }
+ } else
+ list_add_tail(&rxb->list, &rxq->rx_used);
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ i = (i + 1) & RX_QUEUE_MASK;
+ /* If there are a lot of unused frames,
+ * restock the Rx queue so ucode wont assert. */
+ if (fill_rx) {
+ count++;
+ if (count >= 8) {
+ rxq->read = i;
+ il4965_rx_replenish_now(il);
+ count = 0;
+ }
+ }
+ }
+
+ /* Backtrack one entry */
+ rxq->read = i;
+ if (fill_rx)
+ il4965_rx_replenish_now(il);
+ else
+ il4965_rx_queue_restock(il);
+}
+
+/* call this function to flush any scheduled tasklet */
+static inline void
+il4965_synchronize_irq(struct il_priv *il)
+{
+ /* wait to make sure we flush pending tasklet */
+ synchronize_irq(il->pci_dev->irq);
+ tasklet_kill(&il->irq_tasklet);
+}
+
+static void
+il4965_irq_tasklet(struct il_priv *il)
+{
+ u32 inta, handled = 0;
+ u32 inta_fh;
+ unsigned long flags;
+ u32 i;
+#ifdef CONFIG_IWLEGACY_DEBUG
+ u32 inta_mask;
+#endif
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ /* Ack/clear/reset pending uCode interrupts.
+ * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
+ * and will clear only when CSR_FH_INT_STATUS gets cleared. */
+ inta = _il_rd(il, CSR_INT);
+ _il_wr(il, CSR_INT, inta);
+
+ /* Ack/clear/reset pending flow-handler (DMA) interrupts.
+ * Any new interrupts that happen after this, either while we're
+ * in this tasklet, or later, will show up in next ISR/tasklet. */
+ inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
+ _il_wr(il, CSR_FH_INT_STATUS, inta_fh);
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+ if (il_get_debug_level(il) & IL_DL_ISR) {
+ /* just for debug */
+ inta_mask = _il_rd(il, CSR_INT_MASK);
+ D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta,
+ inta_mask, inta_fh);
+ }
+#endif
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
+ * atomic, make sure that inta covers all the interrupts that
+ * we've discovered, even if FH interrupt came in just after
+ * reading CSR_INT. */
+ if (inta_fh & CSR49_FH_INT_RX_MASK)
+ inta |= CSR_INT_BIT_FH_RX;
+ if (inta_fh & CSR49_FH_INT_TX_MASK)
+ inta |= CSR_INT_BIT_FH_TX;
+
+ /* Now service all interrupt bits discovered above. */
+ if (inta & CSR_INT_BIT_HW_ERR) {
+ IL_ERR("Hardware error detected. Restarting.\n");
+
+ /* Tell the device to stop sending interrupts */
+ il_disable_interrupts(il);
+
+ il->isr_stats.hw++;
+ il_irq_handle_error(il);
+
+ handled |= CSR_INT_BIT_HW_ERR;
+
+ return;
+ }
+#ifdef CONFIG_IWLEGACY_DEBUG
+ if (il_get_debug_level(il) & (IL_DL_ISR)) {
+ /* NIC fires this, but we don't use it, redundant with WAKEUP */
+ if (inta & CSR_INT_BIT_SCD) {
+ D_ISR("Scheduler finished to transmit "
+ "the frame/frames.\n");
+ il->isr_stats.sch++;
+ }
+
+ /* Alive notification via Rx interrupt will do the real work */
+ if (inta & CSR_INT_BIT_ALIVE) {
+ D_ISR("Alive interrupt\n");
+ il->isr_stats.alive++;
+ }
+ }
+#endif
+ /* Safely ignore these bits for debug checks below */
+ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
+
+ /* HW RF KILL switch toggled */
+ if (inta & CSR_INT_BIT_RF_KILL) {
+ int hw_rf_kill = 0;
+
+ if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
+ hw_rf_kill = 1;
+
+ IL_WARN("RF_KILL bit toggled to %s.\n",
+ hw_rf_kill ? "disable radio" : "enable radio");
+
+ il->isr_stats.rfkill++;
+
+ /* driver only loads ucode once setting the interface up.
+ * the driver allows loading the ucode even if the radio
+ * is killed. Hence update the killswitch state here. The
+ * rfkill handler will care about restarting if needed.
+ */
+ if (hw_rf_kill) {
+ set_bit(S_RFKILL, &il->status);
+ } else {
+ clear_bit(S_RFKILL, &il->status);
+ il_force_reset(il, true);
+ }
+ wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill);
+
+ handled |= CSR_INT_BIT_RF_KILL;
+ }
+
+ /* Chip got too hot and stopped itself */
+ if (inta & CSR_INT_BIT_CT_KILL) {
+ IL_ERR("Microcode CT kill error detected.\n");
+ il->isr_stats.ctkill++;
+ handled |= CSR_INT_BIT_CT_KILL;
+ }
+
+ /* Error detected by uCode */
+ if (inta & CSR_INT_BIT_SW_ERR) {
+ IL_ERR("Microcode SW error detected. " " Restarting 0x%X.\n",
+ inta);
+ il->isr_stats.sw++;
+ il_irq_handle_error(il);
+ handled |= CSR_INT_BIT_SW_ERR;
+ }
+
+ /*
+ * uCode wakes up after power-down sleep.
+ * Tell device about any new tx or host commands enqueued,
+ * and about any Rx buffers made available while asleep.
+ */
+ if (inta & CSR_INT_BIT_WAKEUP) {
+ D_ISR("Wakeup interrupt\n");
+ il_rx_queue_update_write_ptr(il, &il->rxq);
+ for (i = 0; i < il->hw_params.max_txq_num; i++)
+ il_txq_update_write_ptr(il, &il->txq[i]);
+ il->isr_stats.wakeup++;
+ handled |= CSR_INT_BIT_WAKEUP;
+ }
+
+ /* All uCode command responses, including Tx command responses,
+ * Rx "responses" (frame-received notification), and other
+ * notifications from uCode come through here*/
+ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
+ il4965_rx_handle(il);
+ il->isr_stats.rx++;
+ handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
+ }
+
+ /* This "Tx" DMA channel is used only for loading uCode */
+ if (inta & CSR_INT_BIT_FH_TX) {
+ D_ISR("uCode load interrupt\n");
+ il->isr_stats.tx++;
+ handled |= CSR_INT_BIT_FH_TX;
+ /* Wake up uCode load routine, now that load is complete */
+ il->ucode_write_complete = 1;
+ wake_up(&il->wait_command_queue);
+ }
+
+ if (inta & ~handled) {
+ IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
+ il->isr_stats.unhandled++;
+ }
+
+ if (inta & ~(il->inta_mask)) {
+ IL_WARN("Disabled INTA bits 0x%08x were pending\n",
+ inta & ~il->inta_mask);
+ IL_WARN(" with FH49_INT = 0x%08x\n", inta_fh);
+ }
+
+ /* Re-enable all interrupts */
+ /* only Re-enable if disabled by irq */
+ if (test_bit(S_INT_ENABLED, &il->status))
+ il_enable_interrupts(il);
+ /* Re-enable RF_KILL if it occurred */
+ else if (handled & CSR_INT_BIT_RF_KILL)
+ il_enable_rfkill_int(il);
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+ if (il_get_debug_level(il) & (IL_DL_ISR)) {
+ inta = _il_rd(il, CSR_INT);
+ inta_mask = _il_rd(il, CSR_INT_MASK);
+ inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
+ D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, "
+ "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
+ }
+#endif
+}
+
+/*****************************************************************************
+ *
+ * sysfs attributes
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+
+/*
+ * The following adds a new attribute to the sysfs representation
+ * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/)
+ * used for controlling the debug level.
+ *
+ * See the level definitions in iwl for details.
+ *
+ * The debug_level being managed using sysfs below is a per device debug
+ * level that is used instead of the global debug level if it (the per
+ * device debug level) is set.
+ */
+static ssize_t
+il4965_show_debug_level(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct il_priv *il = dev_get_drvdata(d);
+ return sprintf(buf, "0x%08X\n", il_get_debug_level(il));
+}
+
+static ssize_t
+il4965_store_debug_level(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct il_priv *il = dev_get_drvdata(d);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ IL_ERR("%s is not in hex or decimal form.\n", buf);
+ else
+ il->debug_level = val;
+
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il4965_show_debug_level,
+ il4965_store_debug_level);
+
+#endif /* CONFIG_IWLEGACY_DEBUG */
+
+static ssize_t
+il4965_show_temperature(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct il_priv *il = dev_get_drvdata(d);
+
+ if (!il_is_alive(il))
+ return -EAGAIN;
+
+ return sprintf(buf, "%d\n", il->temperature);
+}
+
+static DEVICE_ATTR(temperature, S_IRUGO, il4965_show_temperature, NULL);
+
+static ssize_t
+il4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct il_priv *il = dev_get_drvdata(d);
+
+ if (!il_is_ready_rf(il))
+ return sprintf(buf, "off\n");
+ else
+ return sprintf(buf, "%d\n", il->tx_power_user_lmt);
+}
+
+static ssize_t
+il4965_store_tx_power(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct il_priv *il = dev_get_drvdata(d);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ IL_INFO("%s is not in decimal form.\n", buf);
+ else {
+ ret = il_set_tx_power(il, val, false);
+ if (ret)
+ IL_ERR("failed setting tx power (0x%08x).\n", ret);
+ else
+ ret = count;
+ }
+ return ret;
+}
+
+static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il4965_show_tx_power,
+ il4965_store_tx_power);
+
+static struct attribute *il_sysfs_entries[] = {
+ &dev_attr_temperature.attr,
+ &dev_attr_tx_power.attr,
+#ifdef CONFIG_IWLEGACY_DEBUG
+ &dev_attr_debug_level.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group il_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = il_sysfs_entries,
+};
+
+/******************************************************************************
+ *
+ * uCode download functions
+ *
+ ******************************************************************************/
+
+static void
+il4965_dealloc_ucode_pci(struct il_priv *il)
+{
+ il_free_fw_desc(il->pci_dev, &il->ucode_code);
+ il_free_fw_desc(il->pci_dev, &il->ucode_data);
+ il_free_fw_desc(il->pci_dev, &il->ucode_data_backup);
+ il_free_fw_desc(il->pci_dev, &il->ucode_init);
+ il_free_fw_desc(il->pci_dev, &il->ucode_init_data);
+ il_free_fw_desc(il->pci_dev, &il->ucode_boot);
+}
+
+static void
+il4965_nic_start(struct il_priv *il)
+{
+ /* Remove all resets to allow NIC to operate */
+ _il_wr(il, CSR_RESET, 0);
+}
+
+static void il4965_ucode_callback(const struct firmware *ucode_raw,
+ void *context);
+static int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length);
+
+static int __must_check
+il4965_request_firmware(struct il_priv *il, bool first)
+{
+ const char *name_pre = il->cfg->fw_name_pre;
+ char tag[8];
+
+ if (first) {
+ il->fw_idx = il->cfg->ucode_api_max;
+ sprintf(tag, "%d", il->fw_idx);
+ } else {
+ il->fw_idx--;
+ sprintf(tag, "%d", il->fw_idx);
+ }
+
+ if (il->fw_idx < il->cfg->ucode_api_min) {
+ IL_ERR("no suitable firmware found!\n");
+ return -ENOENT;
+ }
+
+ sprintf(il->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
+
+ D_INFO("attempting to load firmware '%s'\n", il->firmware_name);
+
+ return request_firmware_nowait(THIS_MODULE, 1, il->firmware_name,
+ &il->pci_dev->dev, GFP_KERNEL, il,
+ il4965_ucode_callback);
+}
+
+struct il4965_firmware_pieces {
+ const void *inst, *data, *init, *init_data, *boot;
+ size_t inst_size, data_size, init_size, init_data_size, boot_size;
+};
+
+static int
+il4965_load_firmware(struct il_priv *il, const struct firmware *ucode_raw,
+ struct il4965_firmware_pieces *pieces)
+{
+ struct il_ucode_header *ucode = (void *)ucode_raw->data;
+ u32 api_ver, hdr_size;
+ const u8 *src;
+
+ il->ucode_ver = le32_to_cpu(ucode->ver);
+ api_ver = IL_UCODE_API(il->ucode_ver);
+
+ switch (api_ver) {
+ default:
+ case 0:
+ case 1:
+ case 2:
+ hdr_size = 24;
+ if (ucode_raw->size < hdr_size) {
+ IL_ERR("File size too small!\n");
+ return -EINVAL;
+ }
+ pieces->inst_size = le32_to_cpu(ucode->v1.inst_size);
+ pieces->data_size = le32_to_cpu(ucode->v1.data_size);
+ pieces->init_size = le32_to_cpu(ucode->v1.init_size);
+ pieces->init_data_size = le32_to_cpu(ucode->v1.init_data_size);
+ pieces->boot_size = le32_to_cpu(ucode->v1.boot_size);
+ src = ucode->v1.data;
+ break;
+ }
+
+ /* Verify size of file vs. image size info in file's header */
+ if (ucode_raw->size !=
+ hdr_size + pieces->inst_size + pieces->data_size +
+ pieces->init_size + pieces->init_data_size + pieces->boot_size) {
+
+ IL_ERR("uCode file size %d does not match expected size\n",
+ (int)ucode_raw->size);
+ return -EINVAL;
+ }
+
+ pieces->inst = src;
+ src += pieces->inst_size;
+ pieces->data = src;
+ src += pieces->data_size;
+ pieces->init = src;
+ src += pieces->init_size;
+ pieces->init_data = src;
+ src += pieces->init_data_size;
+ pieces->boot = src;
+ src += pieces->boot_size;
+
+ return 0;
+}
+
+/**
+ * il4965_ucode_callback - callback when firmware was loaded
+ *
+ * If loaded successfully, copies the firmware into buffers
+ * for the card to fetch (via DMA).
+ */
+static void
+il4965_ucode_callback(const struct firmware *ucode_raw, void *context)
+{
+ struct il_priv *il = context;
+ struct il_ucode_header *ucode;
+ int err;
+ struct il4965_firmware_pieces pieces;
+ const unsigned int api_max = il->cfg->ucode_api_max;
+ const unsigned int api_min = il->cfg->ucode_api_min;
+ u32 api_ver;
+
+ u32 max_probe_length = 200;
+ u32 standard_phy_calibration_size =
+ IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
+
+ memset(&pieces, 0, sizeof(pieces));
+
+ if (!ucode_raw) {
+ if (il->fw_idx <= il->cfg->ucode_api_max)
+ IL_ERR("request for firmware file '%s' failed.\n",
+ il->firmware_name);
+ goto try_again;
+ }
+
+ D_INFO("Loaded firmware file '%s' (%zd bytes).\n", il->firmware_name,
+ ucode_raw->size);
+
+ /* Make sure that we got at least the API version number */
+ if (ucode_raw->size < 4) {
+ IL_ERR("File size way too small!\n");
+ goto try_again;
+ }
+
+ /* Data from ucode file: header followed by uCode images */
+ ucode = (struct il_ucode_header *)ucode_raw->data;
+
+ err = il4965_load_firmware(il, ucode_raw, &pieces);
+
+ if (err)
+ goto try_again;
+
+ api_ver = IL_UCODE_API(il->ucode_ver);
+
+ /*
+ * api_ver should match the api version forming part of the
+ * firmware filename ... but we don't check for that and only rely
+ * on the API version read from firmware header from here on forward
+ */
+ if (api_ver < api_min || api_ver > api_max) {
+ IL_ERR("Driver unable to support your firmware API. "
+ "Driver supports v%u, firmware is v%u.\n", api_max,
+ api_ver);
+ goto try_again;
+ }
+
+ if (api_ver != api_max)
+ IL_ERR("Firmware has old API version. Expected v%u, "
+ "got v%u. New firmware can be obtained "
+ "from http://www.intellinuxwireless.org.\n", api_max,
+ api_ver);
+
+ IL_INFO("loaded firmware version %u.%u.%u.%u\n",
+ IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver),
+ IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver));
+
+ snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version),
+ "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver),
+ IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver),
+ IL_UCODE_SERIAL(il->ucode_ver));
+
+ /*
+ * For any of the failures below (before allocating pci memory)
+ * we will try to load a version with a smaller API -- maybe the
+ * user just got a corrupted version of the latest API.
+ */
+
+ D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver);
+ D_INFO("f/w package hdr runtime inst size = %Zd\n", pieces.inst_size);
+ D_INFO("f/w package hdr runtime data size = %Zd\n", pieces.data_size);
+ D_INFO("f/w package hdr init inst size = %Zd\n", pieces.init_size);
+ D_INFO("f/w package hdr init data size = %Zd\n", pieces.init_data_size);
+ D_INFO("f/w package hdr boot inst size = %Zd\n", pieces.boot_size);
+
+ /* Verify that uCode images will fit in card's SRAM */
+ if (pieces.inst_size > il->hw_params.max_inst_size) {
+ IL_ERR("uCode instr len %Zd too large to fit in\n",
+ pieces.inst_size);
+ goto try_again;
+ }
+
+ if (pieces.data_size > il->hw_params.max_data_size) {
+ IL_ERR("uCode data len %Zd too large to fit in\n",
+ pieces.data_size);
+ goto try_again;
+ }
+
+ if (pieces.init_size > il->hw_params.max_inst_size) {
+ IL_ERR("uCode init instr len %Zd too large to fit in\n",
+ pieces.init_size);
+ goto try_again;
+ }
+
+ if (pieces.init_data_size > il->hw_params.max_data_size) {
+ IL_ERR("uCode init data len %Zd too large to fit in\n",
+ pieces.init_data_size);
+ goto try_again;
+ }
+
+ if (pieces.boot_size > il->hw_params.max_bsm_size) {
+ IL_ERR("uCode boot instr len %Zd too large to fit in\n",
+ pieces.boot_size);
+ goto try_again;
+ }
+
+ /* Allocate ucode buffers for card's bus-master loading ... */
+
+ /* Runtime instructions and 2 copies of data:
+ * 1) unmodified from disk
+ * 2) backup cache for save/restore during power-downs */
+ il->ucode_code.len = pieces.inst_size;
+ il_alloc_fw_desc(il->pci_dev, &il->ucode_code);
+
+ il->ucode_data.len = pieces.data_size;
+ il_alloc_fw_desc(il->pci_dev, &il->ucode_data);
+
+ il->ucode_data_backup.len = pieces.data_size;
+ il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup);
+
+ if (!il->ucode_code.v_addr || !il->ucode_data.v_addr ||
+ !il->ucode_data_backup.v_addr)
+ goto err_pci_alloc;
+
+ /* Initialization instructions and data */
+ if (pieces.init_size && pieces.init_data_size) {
+ il->ucode_init.len = pieces.init_size;
+ il_alloc_fw_desc(il->pci_dev, &il->ucode_init);
+
+ il->ucode_init_data.len = pieces.init_data_size;
+ il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data);
+
+ if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr)
+ goto err_pci_alloc;
+ }
+
+ /* Bootstrap (instructions only, no data) */
+ if (pieces.boot_size) {
+ il->ucode_boot.len = pieces.boot_size;
+ il_alloc_fw_desc(il->pci_dev, &il->ucode_boot);
+
+ if (!il->ucode_boot.v_addr)
+ goto err_pci_alloc;
+ }
+
+ /* Now that we can no longer fail, copy information */
+
+ il->sta_key_max_num = STA_KEY_MAX_NUM;
+
+ /* Copy images into buffers for card's bus-master reads ... */
+
+ /* Runtime instructions (first block of data in file) */
+ D_INFO("Copying (but not loading) uCode instr len %Zd\n",
+ pieces.inst_size);
+ memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size);
+
+ D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n",
+ il->ucode_code.v_addr, (u32) il->ucode_code.p_addr);
+
+ /*
+ * Runtime data
+ * NOTE: Copy into backup buffer will be done in il_up()
+ */
+ D_INFO("Copying (but not loading) uCode data len %Zd\n",
+ pieces.data_size);
+ memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size);
+ memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size);
+
+ /* Initialization instructions */
+ if (pieces.init_size) {
+ D_INFO("Copying (but not loading) init instr len %Zd\n",
+ pieces.init_size);
+ memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size);
+ }
+
+ /* Initialization data */
+ if (pieces.init_data_size) {
+ D_INFO("Copying (but not loading) init data len %Zd\n",
+ pieces.init_data_size);
+ memcpy(il->ucode_init_data.v_addr, pieces.init_data,
+ pieces.init_data_size);
+ }
+
+ /* Bootstrap instructions */
+ D_INFO("Copying (but not loading) boot instr len %Zd\n",
+ pieces.boot_size);
+ memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size);
+
+ /*
+ * figure out the offset of chain noise reset and gain commands
+ * base on the size of standard phy calibration commands table size
+ */
+ il->_4965.phy_calib_chain_noise_reset_cmd =
+ standard_phy_calibration_size;
+ il->_4965.phy_calib_chain_noise_gain_cmd =
+ standard_phy_calibration_size + 1;
+
+ /**************************************************
+ * This is still part of probe() in a sense...
+ *
+ * 9. Setup and register with mac80211 and debugfs
+ **************************************************/
+ err = il4965_mac_setup_register(il, max_probe_length);
+ if (err)
+ goto out_unbind;
+
+ err = il_dbgfs_register(il, DRV_NAME);
+ if (err)
+ IL_ERR("failed to create debugfs files. Ignoring error: %d\n",
+ err);
+
+ err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group);
+ if (err) {
+ IL_ERR("failed to create sysfs device attributes\n");
+ goto out_unbind;
+ }
+
+ /* We have our copies now, allow OS release its copies */
+ release_firmware(ucode_raw);
+ complete(&il->_4965.firmware_loading_complete);
+ return;
+
+try_again:
+ /* try next, if any */
+ if (il4965_request_firmware(il, false))
+ goto out_unbind;
+ release_firmware(ucode_raw);
+ return;
+
+err_pci_alloc:
+ IL_ERR("failed to allocate pci memory\n");
+ il4965_dealloc_ucode_pci(il);
+out_unbind:
+ complete(&il->_4965.firmware_loading_complete);
+ device_release_driver(&il->pci_dev->dev);
+ release_firmware(ucode_raw);
+}
+
+static const char *const desc_lookup_text[] = {
+ "OK",
+ "FAIL",
+ "BAD_PARAM",
+ "BAD_CHECKSUM",
+ "NMI_INTERRUPT_WDG",
+ "SYSASSERT",
+ "FATAL_ERROR",
+ "BAD_COMMAND",
+ "HW_ERROR_TUNE_LOCK",
+ "HW_ERROR_TEMPERATURE",
+ "ILLEGAL_CHAN_FREQ",
+ "VCC_NOT_STBL",
+ "FH49_ERROR",
+ "NMI_INTERRUPT_HOST",
+ "NMI_INTERRUPT_ACTION_PT",
+ "NMI_INTERRUPT_UNKNOWN",
+ "UCODE_VERSION_MISMATCH",
+ "HW_ERROR_ABS_LOCK",
+ "HW_ERROR_CAL_LOCK_FAIL",
+ "NMI_INTERRUPT_INST_ACTION_PT",
+ "NMI_INTERRUPT_DATA_ACTION_PT",
+ "NMI_TRM_HW_ER",
+ "NMI_INTERRUPT_TRM",
+ "NMI_INTERRUPT_BREAK_POINT",
+ "DEBUG_0",
+ "DEBUG_1",
+ "DEBUG_2",
+ "DEBUG_3",
+};
+
+static struct {
+ char *name;
+ u8 num;
+} advanced_lookup[] = {
+ {
+ "NMI_INTERRUPT_WDG", 0x34}, {
+ "SYSASSERT", 0x35}, {
+ "UCODE_VERSION_MISMATCH", 0x37}, {
+ "BAD_COMMAND", 0x38}, {
+ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C}, {
+ "FATAL_ERROR", 0x3D}, {
+ "NMI_TRM_HW_ERR", 0x46}, {
+ "NMI_INTERRUPT_TRM", 0x4C}, {
+ "NMI_INTERRUPT_BREAK_POINT", 0x54}, {
+ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C}, {
+ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64}, {
+ "NMI_INTERRUPT_HOST", 0x66}, {
+ "NMI_INTERRUPT_ACTION_PT", 0x7C}, {
+ "NMI_INTERRUPT_UNKNOWN", 0x84}, {
+ "NMI_INTERRUPT_INST_ACTION_PT", 0x86}, {
+"ADVANCED_SYSASSERT", 0},};
+
+static const char *
+il4965_desc_lookup(u32 num)
+{
+ int i;
+ int max = ARRAY_SIZE(desc_lookup_text);
+
+ if (num < max)
+ return desc_lookup_text[num];
+
+ max = ARRAY_SIZE(advanced_lookup) - 1;
+ for (i = 0; i < max; i++) {
+ if (advanced_lookup[i].num == num)
+ break;
+ }
+ return advanced_lookup[i].name;
+}
+
+#define ERROR_START_OFFSET (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+
+void
+il4965_dump_nic_error_log(struct il_priv *il)
+{
+ u32 data2, line;
+ u32 desc, time, count, base, data1;
+ u32 blink1, blink2, ilink1, ilink2;
+ u32 pc, hcmd;
+
+ if (il->ucode_type == UCODE_INIT)
+ base = le32_to_cpu(il->card_alive_init.error_event_table_ptr);
+ else
+ base = le32_to_cpu(il->card_alive.error_event_table_ptr);
+
+ if (!il->ops->is_valid_rtc_data_addr(base)) {
+ IL_ERR("Not valid error log pointer 0x%08X for %s uCode\n",
+ base, (il->ucode_type == UCODE_INIT) ? "Init" : "RT");
+ return;
+ }
+
+ count = il_read_targ_mem(il, base);
+
+ if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
+ IL_ERR("Start IWL Error Log Dump:\n");
+ IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count);
+ }
+
+ desc = il_read_targ_mem(il, base + 1 * sizeof(u32));
+ il->isr_stats.err_code = desc;
+ pc = il_read_targ_mem(il, base + 2 * sizeof(u32));
+ blink1 = il_read_targ_mem(il, base + 3 * sizeof(u32));
+ blink2 = il_read_targ_mem(il, base + 4 * sizeof(u32));
+ ilink1 = il_read_targ_mem(il, base + 5 * sizeof(u32));
+ ilink2 = il_read_targ_mem(il, base + 6 * sizeof(u32));
+ data1 = il_read_targ_mem(il, base + 7 * sizeof(u32));
+ data2 = il_read_targ_mem(il, base + 8 * sizeof(u32));
+ line = il_read_targ_mem(il, base + 9 * sizeof(u32));
+ time = il_read_targ_mem(il, base + 11 * sizeof(u32));
+ hcmd = il_read_targ_mem(il, base + 22 * sizeof(u32));
+
+ IL_ERR("Desc Time "
+ "data1 data2 line\n");
+ IL_ERR("%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n",
+ il4965_desc_lookup(desc), desc, time, data1, data2, line);
+ IL_ERR("pc blink1 blink2 ilink1 ilink2 hcmd\n");
+ IL_ERR("0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", pc, blink1,
+ blink2, ilink1, ilink2, hcmd);
+}
+
+static void
+il4965_rf_kill_ct_config(struct il_priv *il)
+{
+ struct il_ct_kill_config cmd;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&il->lock, flags);
+ _il_wr(il, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ cmd.critical_temperature_R =
+ cpu_to_le32(il->hw_params.ct_kill_threshold);
+
+ ret = il_send_cmd_pdu(il, C_CT_KILL_CONFIG, sizeof(cmd), &cmd);
+ if (ret)
+ IL_ERR("C_CT_KILL_CONFIG failed\n");
+ else
+ D_INFO("C_CT_KILL_CONFIG " "succeeded, "
+ "critical temperature is %d\n",
+ il->hw_params.ct_kill_threshold);
+}
+
+static const s8 default_queue_to_tx_fifo[] = {
+ IL_TX_FIFO_VO,
+ IL_TX_FIFO_VI,
+ IL_TX_FIFO_BE,
+ IL_TX_FIFO_BK,
+ IL49_CMD_FIFO_NUM,
+ IL_TX_FIFO_UNUSED,
+ IL_TX_FIFO_UNUSED,
+};
+
+#define IL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
+
+static int
+il4965_alive_notify(struct il_priv *il)
+{
+ u32 a;
+ unsigned long flags;
+ int i, chan;
+ u32 reg_val;
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ /* Clear 4965's internal Tx Scheduler data base */
+ il->scd_base_addr = il_rd_prph(il, IL49_SCD_SRAM_BASE_ADDR);
+ a = il->scd_base_addr + IL49_SCD_CONTEXT_DATA_OFFSET;
+ for (; a < il->scd_base_addr + IL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4)
+ il_write_targ_mem(il, a, 0);
+ for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET; a += 4)
+ il_write_targ_mem(il, a, 0);
+ for (;
+ a <
+ il->scd_base_addr +
+ IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(il->hw_params.max_txq_num);
+ a += 4)
+ il_write_targ_mem(il, a, 0);
+
+ /* Tel 4965 where to find Tx byte count tables */
+ il_wr_prph(il, IL49_SCD_DRAM_BASE_ADDR, il->scd_bc_tbls.dma >> 10);
+
+ /* Enable DMA channel */
+ for (chan = 0; chan < FH49_TCSR_CHNL_NUM; chan++)
+ il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(chan),
+ FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
+
+ /* Update FH chicken bits */
+ reg_val = il_rd(il, FH49_TX_CHICKEN_BITS_REG);
+ il_wr(il, FH49_TX_CHICKEN_BITS_REG,
+ reg_val | FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
+
+ /* Disable chain mode for all queues */
+ il_wr_prph(il, IL49_SCD_QUEUECHAIN_SEL, 0);
+
+ /* Initialize each Tx queue (including the command queue) */
+ for (i = 0; i < il->hw_params.max_txq_num; i++) {
+
+ /* TFD circular buffer read/write idxes */
+ il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(i), 0);
+ il_wr(il, HBUS_TARG_WRPTR, 0 | (i << 8));
+
+ /* Max Tx Window size for Scheduler-ACK mode */
+ il_write_targ_mem(il,
+ il->scd_base_addr +
+ IL49_SCD_CONTEXT_QUEUE_OFFSET(i),
+ (SCD_WIN_SIZE <<
+ IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) &
+ IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
+
+ /* Frame limit */
+ il_write_targ_mem(il,
+ il->scd_base_addr +
+ IL49_SCD_CONTEXT_QUEUE_OFFSET(i) +
+ sizeof(u32),
+ (SCD_FRAME_LIMIT <<
+ IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+ IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
+
+ }
+ il_wr_prph(il, IL49_SCD_INTERRUPT_MASK,
+ (1 << il->hw_params.max_txq_num) - 1);
+
+ /* Activate all Tx DMA/FIFO channels */
+ il4965_txq_set_sched(il, IL_MASK(0, 6));
+
+ il4965_set_wr_ptrs(il, IL_DEFAULT_CMD_QUEUE_NUM, 0);
+
+ /* make sure all queue are not stopped */
+ memset(&il->queue_stopped[0], 0, sizeof(il->queue_stopped));
+ for (i = 0; i < 4; i++)
+ atomic_set(&il->queue_stop_count[i], 0);
+
+ /* reset to 0 to enable all the queue first */
+ il->txq_ctx_active_msk = 0;
+ /* Map each Tx/cmd queue to its corresponding fifo */
+ BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7);
+
+ for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) {
+ int ac = default_queue_to_tx_fifo[i];
+
+ il_txq_ctx_activate(il, i);
+
+ if (ac == IL_TX_FIFO_UNUSED)
+ continue;
+
+ il4965_tx_queue_set_status(il, &il->txq[i], ac, 0);
+ }
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ return 0;
+}
+
+/**
+ * il4965_alive_start - called after N_ALIVE notification received
+ * from protocol/runtime uCode (initialization uCode's
+ * Alive gets handled by il_init_alive_start()).
+ */
+static void
+il4965_alive_start(struct il_priv *il)
+{
+ int ret = 0;
+
+ D_INFO("Runtime Alive received.\n");
+
+ if (il->card_alive.is_valid != UCODE_VALID_OK) {
+ /* We had an error bringing up the hardware, so take it
+ * all the way back down so we can try again */
+ D_INFO("Alive failed.\n");
+ goto restart;
+ }
+
+ /* Initialize uCode has loaded Runtime uCode ... verify inst image.
+ * This is a paranoid check, because we would not have gotten the
+ * "runtime" alive if code weren't properly loaded. */
+ if (il4965_verify_ucode(il)) {
+ /* Runtime instruction load was bad;
+ * take it all the way back down so we can try again */
+ D_INFO("Bad runtime uCode load.\n");
+ goto restart;
+ }
+
+ ret = il4965_alive_notify(il);
+ if (ret) {
+ IL_WARN("Could not complete ALIVE transition [ntf]: %d\n", ret);
+ goto restart;
+ }
+
+ /* After the ALIVE response, we can send host commands to the uCode */
+ set_bit(S_ALIVE, &il->status);
+
+ /* Enable watchdog to monitor the driver tx queues */
+ il_setup_watchdog(il);
+
+ if (il_is_rfkill(il))
+ return;
+
+ ieee80211_wake_queues(il->hw);
+
+ il->active_rate = RATES_MASK;
+
+ il_power_update_mode(il, true);
+ D_INFO("Updated power mode\n");
+
+ if (il_is_associated(il)) {
+ struct il_rxon_cmd *active_rxon =
+ (struct il_rxon_cmd *)&il->active;
+ /* apply any changes in staging */
+ il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
+ active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ } else {
+ /* Initialize our rx_config data */
+ il_connection_init_rx_config(il);
+
+ if (il->ops->set_rxon_chain)
+ il->ops->set_rxon_chain(il);
+ }
+
+ /* Configure bluetooth coexistence if enabled */
+ il_send_bt_config(il);
+
+ il4965_reset_run_time_calib(il);
+
+ set_bit(S_READY, &il->status);
+
+ /* Configure the adapter for unassociated operation */
+ il_commit_rxon(il);
+
+ /* At this point, the NIC is initialized and operational */
+ il4965_rf_kill_ct_config(il);
+
+ D_INFO("ALIVE processing complete.\n");
+ wake_up(&il->wait_command_queue);
+
+ return;
+
+restart:
+ queue_work(il->workqueue, &il->restart);
+}
+
+static void il4965_cancel_deferred_work(struct il_priv *il);
+
+static void
+__il4965_down(struct il_priv *il)
+{
+ unsigned long flags;
+ int exit_pending;
+
+ D_INFO(DRV_NAME " is going down\n");
+
+ il_scan_cancel_timeout(il, 200);
+
+ exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status);
+
+ /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set
+ * to prevent rearm timer */
+ del_timer_sync(&il->watchdog);
+
+ il_clear_ucode_stations(il);
+
+ /* FIXME: race conditions ? */
+ spin_lock_irq(&il->sta_lock);
+ /*
+ * Remove all key information that is not stored as part
+ * of station information since mac80211 may not have had
+ * a chance to remove all the keys. When device is
+ * reconfigured by mac80211 after an error all keys will
+ * be reconfigured.
+ */
+ memset(il->_4965.wep_keys, 0, sizeof(il->_4965.wep_keys));
+ il->_4965.key_mapping_keys = 0;
+ spin_unlock_irq(&il->sta_lock);
+
+ il_dealloc_bcast_stations(il);
+ il_clear_driver_stations(il);
+
+ /* Unblock any waiting calls */
+ wake_up_all(&il->wait_command_queue);
+
+ /* Wipe out the EXIT_PENDING status bit if we are not actually
+ * exiting the module */
+ if (!exit_pending)
+ clear_bit(S_EXIT_PENDING, &il->status);
+
+ /* stop and reset the on-board processor */
+ _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+
+ /* tell the device to stop sending interrupts */
+ spin_lock_irqsave(&il->lock, flags);
+ il_disable_interrupts(il);
+ spin_unlock_irqrestore(&il->lock, flags);
+ il4965_synchronize_irq(il);
+
+ if (il->mac80211_registered)
+ ieee80211_stop_queues(il->hw);
+
+ /* If we have not previously called il_init() then
+ * clear all bits but the RF Kill bit and return */
+ if (!il_is_init(il)) {
+ il->status =
+ test_bit(S_RFKILL, &il->status) << S_RFKILL |
+ test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED |
+ test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING;
+ goto exit;
+ }
+
+ /* ...otherwise clear out all the status bits but the RF Kill
+ * bit and continue taking the NIC down. */
+ il->status &=
+ test_bit(S_RFKILL, &il->status) << S_RFKILL |
+ test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED |
+ test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR |
+ test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING;
+
+ /*
+ * We disabled and synchronized interrupt, and priv->mutex is taken, so
+ * here is the only thread which will program device registers, but
+ * still have lockdep assertions, so we are taking reg_lock.
+ */
+ spin_lock_irq(&il->reg_lock);
+ /* FIXME: il_grab_nic_access if rfkill is off ? */
+
+ il4965_txq_ctx_stop(il);
+ il4965_rxq_stop(il);
+ /* Power-down device's busmaster DMA clocks */
+ _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(5);
+ /* Make sure (redundant) we've released our request to stay awake */
+ _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ /* Stop the device, and put it in low power state */
+ _il_apm_stop(il);
+
+ spin_unlock_irq(&il->reg_lock);
+
+ il4965_txq_ctx_unmap(il);
+exit:
+ memset(&il->card_alive, 0, sizeof(struct il_alive_resp));
+
+ dev_kfree_skb(il->beacon_skb);
+ il->beacon_skb = NULL;
+
+ /* clear out any free frames */
+ il4965_clear_free_frames(il);
+}
+
+static void
+il4965_down(struct il_priv *il)
+{
+ mutex_lock(&il->mutex);
+ __il4965_down(il);
+ mutex_unlock(&il->mutex);
+
+ il4965_cancel_deferred_work(il);
+}
+
+
+static void
+il4965_set_hw_ready(struct il_priv *il)
+{
+ int ret;
+
+ il_set_bit(il, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
+
+ /* See if we got it */
+ ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+ 100);
+ if (ret >= 0)
+ il->hw_ready = true;
+
+ D_INFO("hardware %s ready\n", (il->hw_ready) ? "" : "not");
+}
+
+static void
+il4965_prepare_card_hw(struct il_priv *il)
+{
+ int ret;
+
+ il->hw_ready = false;
+
+ il4965_set_hw_ready(il);
+ if (il->hw_ready)
+ return;
+
+ /* If HW is not ready, prepare the conditions to check again */
+ il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE);
+
+ ret =
+ _il_poll_bit(il, CSR_HW_IF_CONFIG_REG,
+ ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000);
+
+ /* HW should be ready by now, check again. */
+ if (ret != -ETIMEDOUT)
+ il4965_set_hw_ready(il);
+}
+
+#define MAX_HW_RESTARTS 5
+
+static int
+__il4965_up(struct il_priv *il)
+{
+ int i;
+ int ret;
+
+ if (test_bit(S_EXIT_PENDING, &il->status)) {
+ IL_WARN("Exit pending; will not bring the NIC up\n");
+ return -EIO;
+ }
+
+ if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) {
+ IL_ERR("ucode not available for device bringup\n");
+ return -EIO;
+ }
+
+ ret = il4965_alloc_bcast_station(il);
+ if (ret) {
+ il_dealloc_bcast_stations(il);
+ return ret;
+ }
+
+ il4965_prepare_card_hw(il);
+ if (!il->hw_ready) {
+ IL_ERR("HW not ready\n");
+ return -EIO;
+ }
+
+ /* If platform's RF_KILL switch is NOT set to KILL */
+ if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
+ clear_bit(S_RFKILL, &il->status);
+ else {
+ set_bit(S_RFKILL, &il->status);
+ wiphy_rfkill_set_hw_state(il->hw->wiphy, true);
+
+ il_enable_rfkill_int(il);
+ IL_WARN("Radio disabled by HW RF Kill switch\n");
+ return 0;
+ }
+
+ _il_wr(il, CSR_INT, 0xFFFFFFFF);
+
+ /* must be initialised before il_hw_nic_init */
+ il->cmd_queue = IL_DEFAULT_CMD_QUEUE_NUM;
+
+ ret = il4965_hw_nic_init(il);
+ if (ret) {
+ IL_ERR("Unable to init nic\n");
+ return ret;
+ }
+
+ /* make sure rfkill handshake bits are cleared */
+ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+ /* clear (again), then enable host interrupts */
+ _il_wr(il, CSR_INT, 0xFFFFFFFF);
+ il_enable_interrupts(il);
+
+ /* really make sure rfkill handshake bits are cleared */
+ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+
+ /* Copy original ucode data image from disk into backup cache.
+ * This will be used to initialize the on-board processor's
+ * data SRAM for a clean start when the runtime program first loads. */
+ memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr,
+ il->ucode_data.len);
+
+ for (i = 0; i < MAX_HW_RESTARTS; i++) {
+
+ /* load bootstrap state machine,
+ * load bootstrap program into processor's memory,
+ * prepare to load the "initialize" uCode */
+ ret = il->ops->load_ucode(il);
+
+ if (ret) {
+ IL_ERR("Unable to set up bootstrap uCode: %d\n", ret);
+ continue;
+ }
+
+ /* start card; "initialize" will load runtime ucode */
+ il4965_nic_start(il);
+
+ D_INFO(DRV_NAME " is coming up\n");
+
+ return 0;
+ }
+
+ set_bit(S_EXIT_PENDING, &il->status);
+ __il4965_down(il);
+ clear_bit(S_EXIT_PENDING, &il->status);
+
+ /* tried to restart and config the device for as long as our
+ * patience could withstand */
+ IL_ERR("Unable to initialize device after %d attempts.\n", i);
+ return -EIO;
+}
+
+/*****************************************************************************
+ *
+ * Workqueue callbacks
+ *
+ *****************************************************************************/
+
+static void
+il4965_bg_init_alive_start(struct work_struct *data)
+{
+ struct il_priv *il =
+ container_of(data, struct il_priv, init_alive_start.work);
+
+ mutex_lock(&il->mutex);
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ goto out;
+
+ il->ops->init_alive_start(il);
+out:
+ mutex_unlock(&il->mutex);
+}
+
+static void
+il4965_bg_alive_start(struct work_struct *data)
+{
+ struct il_priv *il =
+ container_of(data, struct il_priv, alive_start.work);
+
+ mutex_lock(&il->mutex);
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ goto out;
+
+ il4965_alive_start(il);
+out:
+ mutex_unlock(&il->mutex);
+}
+
+static void
+il4965_bg_run_time_calib_work(struct work_struct *work)
+{
+ struct il_priv *il = container_of(work, struct il_priv,
+ run_time_calib_work);
+
+ mutex_lock(&il->mutex);
+
+ if (test_bit(S_EXIT_PENDING, &il->status) ||
+ test_bit(S_SCANNING, &il->status)) {
+ mutex_unlock(&il->mutex);
+ return;
+ }
+
+ if (il->start_calib) {
+ il4965_chain_noise_calibration(il, (void *)&il->_4965.stats);
+ il4965_sensitivity_calibration(il, (void *)&il->_4965.stats);
+ }
+
+ mutex_unlock(&il->mutex);
+}
+
+static void
+il4965_bg_restart(struct work_struct *data)
+{
+ struct il_priv *il = container_of(data, struct il_priv, restart);
+
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return;
+
+ if (test_and_clear_bit(S_FW_ERROR, &il->status)) {
+ mutex_lock(&il->mutex);
+ il->is_open = 0;
+
+ __il4965_down(il);
+
+ mutex_unlock(&il->mutex);
+ il4965_cancel_deferred_work(il);
+ ieee80211_restart_hw(il->hw);
+ } else {
+ il4965_down(il);
+
+ mutex_lock(&il->mutex);
+ if (test_bit(S_EXIT_PENDING, &il->status)) {
+ mutex_unlock(&il->mutex);
+ return;
+ }
+
+ __il4965_up(il);
+ mutex_unlock(&il->mutex);
+ }
+}
+
+static void
+il4965_bg_rx_replenish(struct work_struct *data)
+{
+ struct il_priv *il = container_of(data, struct il_priv, rx_replenish);
+
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return;
+
+ mutex_lock(&il->mutex);
+ il4965_rx_replenish(il);
+ mutex_unlock(&il->mutex);
+}
+
+/*****************************************************************************
+ *
+ * mac80211 entry point functions
+ *
+ *****************************************************************************/
+
+#define UCODE_READY_TIMEOUT (4 * HZ)
+
+/*
+ * Not a mac80211 entry point function, but it fits in with all the
+ * other mac80211 functions grouped here.
+ */
+static int
+il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
+{
+ int ret;
+ struct ieee80211_hw *hw = il->hw;
+
+ hw->rate_control_algorithm = "iwl-4965-rs";
+
+ /* Tell mac80211 our characteristics */
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+ ieee80211_hw_set(hw, SPECTRUM_MGMT);
+ ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ if (il->cfg->sku & IL_SKU_N)
+ hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS |
+ NL80211_FEATURE_STATIC_SMPS;
+
+ hw->sta_data_size = sizeof(struct il_station_priv);
+ hw->vif_data_size = sizeof(struct il_vif_priv);
+
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
+
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+ REGULATORY_DISABLE_BEACON_HINTS;
+
+ /*
+ * For now, disable PS by default because it affects
+ * RX performance significantly.
+ */
+ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+ /* we create the 802.11 header and a zero-length SSID element */
+ hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2;
+
+ /* Default value; 4 EDCA QOS priorities */
+ hw->queues = 4;
+
+ hw->max_listen_interval = IL_CONN_MAX_LISTEN_INTERVAL;
+
+ if (il->bands[IEEE80211_BAND_2GHZ].n_channels)
+ il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &il->bands[IEEE80211_BAND_2GHZ];
+ if (il->bands[IEEE80211_BAND_5GHZ].n_channels)
+ il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &il->bands[IEEE80211_BAND_5GHZ];
+
+ il_leds_init(il);
+
+ ret = ieee80211_register_hw(il->hw);
+ if (ret) {
+ IL_ERR("Failed to register hw (error %d)\n", ret);
+ return ret;
+ }
+ il->mac80211_registered = 1;
+
+ return 0;
+}
+
+int
+il4965_mac_start(struct ieee80211_hw *hw)
+{
+ struct il_priv *il = hw->priv;
+ int ret;
+
+ D_MAC80211("enter\n");
+
+ /* we should be verifying the device is ready to be opened */
+ mutex_lock(&il->mutex);
+ ret = __il4965_up(il);
+ mutex_unlock(&il->mutex);
+
+ if (ret)
+ return ret;
+
+ if (il_is_rfkill(il))
+ goto out;
+
+ D_INFO("Start UP work done.\n");
+
+ /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from
+ * mac80211 will not be run successfully. */
+ ret = wait_event_timeout(il->wait_command_queue,
+ test_bit(S_READY, &il->status),
+ UCODE_READY_TIMEOUT);
+ if (!ret) {
+ if (!test_bit(S_READY, &il->status)) {
+ IL_ERR("START_ALIVE timeout after %dms.\n",
+ jiffies_to_msecs(UCODE_READY_TIMEOUT));
+ return -ETIMEDOUT;
+ }
+ }
+
+ il4965_led_enable(il);
+
+out:
+ il->is_open = 1;
+ D_MAC80211("leave\n");
+ return 0;
+}
+
+void
+il4965_mac_stop(struct ieee80211_hw *hw)
+{
+ struct il_priv *il = hw->priv;
+
+ D_MAC80211("enter\n");
+
+ if (!il->is_open)
+ return;
+
+ il->is_open = 0;
+
+ il4965_down(il);
+
+ flush_workqueue(il->workqueue);
+
+ /* User space software may expect getting rfkill changes
+ * even if interface is down */
+ _il_wr(il, CSR_INT, 0xFFFFFFFF);
+ il_enable_rfkill_int(il);
+
+ D_MAC80211("leave\n");
+}
+
+void
+il4965_mac_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct il_priv *il = hw->priv;
+
+ D_MACDUMP("enter\n");
+
+ D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
+ ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate);
+
+ if (il4965_tx_skb(il, control->sta, skb))
+ dev_kfree_skb_any(skb);
+
+ D_MACDUMP("leave\n");
+}
+
+void
+il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta, u32 iv32, u16 * phase1key)
+{
+ struct il_priv *il = hw->priv;
+
+ D_MAC80211("enter\n");
+
+ il4965_update_tkip_key(il, keyconf, sta, iv32, phase1key);
+
+ D_MAC80211("leave\n");
+}
+
+int
+il4965_mac_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 il_priv *il = hw->priv;
+ int ret;
+ u8 sta_id;
+ bool is_default_wep_key = false;
+
+ D_MAC80211("enter\n");
+
+ if (il->cfg->mod_params->sw_crypto) {
+ D_MAC80211("leave - hwcrypto disabled\n");
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * To support IBSS RSN, don't program group keys in IBSS, the
+ * hardware will then not attempt to decrypt the frames.
+ */
+ if (vif->type == NL80211_IFTYPE_ADHOC &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ D_MAC80211("leave - ad-hoc group key\n");
+ return -EOPNOTSUPP;
+ }
+
+ sta_id = il_sta_id_or_broadcast(il, sta);
+ if (sta_id == IL_INVALID_STATION)
+ return -EINVAL;
+
+ mutex_lock(&il->mutex);
+ il_scan_cancel_timeout(il, 100);
+
+ /*
+ * If we are getting WEP group key and we didn't receive any key mapping
+ * so far, we are in legacy wep mode (group key only), otherwise we are
+ * in 1X mode.
+ * In legacy wep mode, we use another host command to the uCode.
+ */
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) {
+ if (cmd == SET_KEY)
+ is_default_wep_key = !il->_4965.key_mapping_keys;
+ else
+ is_default_wep_key =
+ (key->hw_key_idx == HW_KEY_DEFAULT);
+ }
+
+ switch (cmd) {
+ case SET_KEY:
+ if (is_default_wep_key)
+ ret = il4965_set_default_wep_key(il, key);
+ else
+ ret = il4965_set_dynamic_key(il, key, sta_id);
+
+ D_MAC80211("enable hwcrypto key\n");
+ break;
+ case DISABLE_KEY:
+ if (is_default_wep_key)
+ ret = il4965_remove_default_wep_key(il, key);
+ else
+ ret = il4965_remove_dynamic_key(il, key, sta_id);
+
+ D_MAC80211("disable hwcrypto key\n");
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&il->mutex);
+ D_MAC80211("leave\n");
+
+ return ret;
+}
+
+int
+il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 * ssn,
+ u8 buf_size, bool amsdu)
+{
+ struct il_priv *il = hw->priv;
+ int ret = -EINVAL;
+
+ D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid);
+
+ if (!(il->cfg->sku & IL_SKU_N))
+ return -EACCES;
+
+ mutex_lock(&il->mutex);
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ D_HT("start Rx\n");
+ ret = il4965_sta_rx_agg_start(il, sta, tid, *ssn);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ D_HT("stop Rx\n");
+ ret = il4965_sta_rx_agg_stop(il, sta, tid);
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ ret = 0;
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ D_HT("start Tx\n");
+ ret = il4965_tx_agg_start(il, vif, sta, tid, ssn);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ D_HT("stop Tx\n");
+ ret = il4965_tx_agg_stop(il, vif, sta, tid);
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ ret = 0;
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ ret = 0;
+ break;
+ }
+ mutex_unlock(&il->mutex);
+
+ return ret;
+}
+
+int
+il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct il_priv *il = hw->priv;
+ struct il_station_priv *sta_priv = (void *)sta->drv_priv;
+ bool is_ap = vif->type == NL80211_IFTYPE_STATION;
+ int ret;
+ u8 sta_id;
+
+ D_INFO("received request to add station %pM\n", sta->addr);
+ mutex_lock(&il->mutex);
+ D_INFO("proceeding to add station %pM\n", sta->addr);
+ sta_priv->common.sta_id = IL_INVALID_STATION;
+
+ atomic_set(&sta_priv->pending_frames, 0);
+
+ ret =
+ il_add_station_common(il, sta->addr, is_ap, sta, &sta_id);
+ if (ret) {
+ IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret);
+ /* Should we return success if return code is EEXIST ? */
+ mutex_unlock(&il->mutex);
+ return ret;
+ }
+
+ sta_priv->common.sta_id = sta_id;
+
+ /* Initialize rate scaling */
+ D_INFO("Initializing rate scaling for station %pM\n", sta->addr);
+ il4965_rs_rate_init(il, sta, sta_id);
+ mutex_unlock(&il->mutex);
+
+ return 0;
+}
+
+void
+il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct il_priv *il = hw->priv;
+ const struct il_channel_info *ch_info;
+ struct ieee80211_conf *conf = &hw->conf;
+ struct ieee80211_channel *channel = ch_switch->chandef.chan;
+ struct il_ht_config *ht_conf = &il->current_ht_config;
+ u16 ch;
+
+ D_MAC80211("enter\n");
+
+ mutex_lock(&il->mutex);
+
+ if (il_is_rfkill(il))
+ goto out;
+
+ if (test_bit(S_EXIT_PENDING, &il->status) ||
+ test_bit(S_SCANNING, &il->status) ||
+ test_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
+ goto out;
+
+ if (!il_is_associated(il))
+ goto out;
+
+ if (!il->ops->set_channel_switch)
+ goto out;
+
+ ch = channel->hw_value;
+ if (le16_to_cpu(il->active.channel) == ch)
+ goto out;
+
+ ch_info = il_get_channel_info(il, channel->band, ch);
+ if (!il_is_channel_valid(ch_info)) {
+ D_MAC80211("invalid channel\n");
+ goto out;
+ }
+
+ spin_lock_irq(&il->lock);
+
+ il->current_ht_config.smps = conf->smps_mode;
+
+ /* Configure HT40 channels */
+ switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ il->ht.is_40mhz = false;
+ il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ il->ht.is_40mhz = true;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ il->ht.is_40mhz = true;
+ break;
+ }
+
+ if ((le16_to_cpu(il->staging.channel) != ch))
+ il->staging.flags = 0;
+
+ il_set_rxon_channel(il, channel);
+ il_set_rxon_ht(il, ht_conf);
+ il_set_flags_for_band(il, channel->band, il->vif);
+
+ spin_unlock_irq(&il->lock);
+
+ il_set_rate(il);
+ /*
+ * at this point, staging_rxon has the
+ * configuration for channel switch
+ */
+ set_bit(S_CHANNEL_SWITCH_PENDING, &il->status);
+ il->switch_channel = cpu_to_le16(ch);
+ if (il->ops->set_channel_switch(il, ch_switch)) {
+ clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status);
+ il->switch_channel = 0;
+ ieee80211_chswitch_done(il->vif, false);
+ }
+
+out:
+ mutex_unlock(&il->mutex);
+ D_MAC80211("leave\n");
+}
+
+void
+il4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
+ unsigned int *total_flags, u64 multicast)
+{
+ struct il_priv *il = hw->priv;
+ __le32 filter_or = 0, filter_nand = 0;
+
+#define CHK(test, flag) do { \
+ if (*total_flags & (test)) \
+ filter_or |= (flag); \
+ else \
+ filter_nand |= (flag); \
+ } while (0)
+
+ D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags,
+ *total_flags);
+
+ CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK);
+ /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */
+ CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK);
+ CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK);
+
+#undef CHK
+
+ mutex_lock(&il->mutex);
+
+ il->staging.filter_flags &= ~filter_nand;
+ il->staging.filter_flags |= filter_or;
+
+ /*
+ * Not committing directly because hardware can perform a scan,
+ * but we'll eventually commit the filter flags change anyway.
+ */
+
+ mutex_unlock(&il->mutex);
+
+ /*
+ * Receiving all multicast frames is always enabled by the
+ * default flags setup in il_connection_init_rx_config()
+ * since we currently do not support programming multicast
+ * filters into the device.
+ */
+ *total_flags &=
+ FIF_OTHER_BSS | FIF_ALLMULTI |
+ FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
+}
+
+/*****************************************************************************
+ *
+ * driver setup and teardown
+ *
+ *****************************************************************************/
+
+static void
+il4965_bg_txpower_work(struct work_struct *work)
+{
+ struct il_priv *il = container_of(work, struct il_priv,
+ txpower_work);
+
+ mutex_lock(&il->mutex);
+
+ /* If a scan happened to start before we got here
+ * then just return; the stats notification will
+ * kick off another scheduled work to compensate for
+ * any temperature delta we missed here. */
+ if (test_bit(S_EXIT_PENDING, &il->status) ||
+ test_bit(S_SCANNING, &il->status))
+ goto out;
+
+ /* Regardless of if we are associated, we must reconfigure the
+ * TX power since frames can be sent on non-radar channels while
+ * not associated */
+ il->ops->send_tx_power(il);
+
+ /* Update last_temperature to keep is_calib_needed from running
+ * when it isn't needed... */
+ il->last_temperature = il->temperature;
+out:
+ mutex_unlock(&il->mutex);
+}
+
+static void
+il4965_setup_deferred_work(struct il_priv *il)
+{
+ il->workqueue = create_singlethread_workqueue(DRV_NAME);
+
+ init_waitqueue_head(&il->wait_command_queue);
+
+ INIT_WORK(&il->restart, il4965_bg_restart);
+ INIT_WORK(&il->rx_replenish, il4965_bg_rx_replenish);
+ INIT_WORK(&il->run_time_calib_work, il4965_bg_run_time_calib_work);
+ INIT_DELAYED_WORK(&il->init_alive_start, il4965_bg_init_alive_start);
+ INIT_DELAYED_WORK(&il->alive_start, il4965_bg_alive_start);
+
+ il_setup_scan_deferred_work(il);
+
+ INIT_WORK(&il->txpower_work, il4965_bg_txpower_work);
+
+ setup_timer(&il->stats_periodic, il4965_bg_stats_periodic,
+ (unsigned long)il);
+
+ setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il);
+
+ tasklet_init(&il->irq_tasklet,
+ (void (*)(unsigned long))il4965_irq_tasklet,
+ (unsigned long)il);
+}
+
+static void
+il4965_cancel_deferred_work(struct il_priv *il)
+{
+ cancel_work_sync(&il->txpower_work);
+ cancel_delayed_work_sync(&il->init_alive_start);
+ cancel_delayed_work(&il->alive_start);
+ cancel_work_sync(&il->run_time_calib_work);
+
+ il_cancel_scan_deferred_work(il);
+
+ del_timer_sync(&il->stats_periodic);
+}
+
+static void
+il4965_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates)
+{
+ int i;
+
+ for (i = 0; i < RATE_COUNT_LEGACY; i++) {
+ rates[i].bitrate = il_rates[i].ieee * 5;
+ rates[i].hw_value = i; /* Rate scaling will work on idxes */
+ rates[i].hw_value_short = i;
+ rates[i].flags = 0;
+ if ((i >= IL_FIRST_CCK_RATE) && (i <= IL_LAST_CCK_RATE)) {
+ /*
+ * If CCK != 1M then set short preamble rate flag.
+ */
+ rates[i].flags |=
+ (il_rates[i].plcp ==
+ RATE_1M_PLCP) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE;
+ }
+ }
+}
+
+/*
+ * Acquire il->lock before calling this function !
+ */
+void
+il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx)
+{
+ il_wr(il, HBUS_TARG_WRPTR, (idx & 0xff) | (txq_id << 8));
+ il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(txq_id), idx);
+}
+
+void
+il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq,
+ int tx_fifo_id, int scd_retry)
+{
+ int txq_id = txq->q.id;
+
+ /* Find out whether to activate Tx queue */
+ int active = test_bit(txq_id, &il->txq_ctx_active_msk) ? 1 : 0;
+
+ /* Set up and activate */
+ il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id),
+ (active << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
+ (tx_fifo_id << IL49_SCD_QUEUE_STTS_REG_POS_TXF) |
+ (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_WSL) |
+ (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) |
+ IL49_SCD_QUEUE_STTS_REG_MSK);
+
+ txq->sched_retry = scd_retry;
+
+ D_INFO("%s %s Queue %d on AC %d\n", active ? "Activate" : "Deactivate",
+ scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
+}
+
+static const struct ieee80211_ops il4965_mac_ops = {
+ .tx = il4965_mac_tx,
+ .start = il4965_mac_start,
+ .stop = il4965_mac_stop,
+ .add_interface = il_mac_add_interface,
+ .remove_interface = il_mac_remove_interface,
+ .change_interface = il_mac_change_interface,
+ .config = il_mac_config,
+ .configure_filter = il4965_configure_filter,
+ .set_key = il4965_mac_set_key,
+ .update_tkip_key = il4965_mac_update_tkip_key,
+ .conf_tx = il_mac_conf_tx,
+ .reset_tsf = il_mac_reset_tsf,
+ .bss_info_changed = il_mac_bss_info_changed,
+ .ampdu_action = il4965_mac_ampdu_action,
+ .hw_scan = il_mac_hw_scan,
+ .sta_add = il4965_mac_sta_add,
+ .sta_remove = il_mac_sta_remove,
+ .channel_switch = il4965_mac_channel_switch,
+ .tx_last_beacon = il_mac_tx_last_beacon,
+ .flush = il_mac_flush,
+};
+
+static int
+il4965_init_drv(struct il_priv *il)
+{
+ int ret;
+
+ spin_lock_init(&il->sta_lock);
+ spin_lock_init(&il->hcmd_lock);
+
+ INIT_LIST_HEAD(&il->free_frames);
+
+ mutex_init(&il->mutex);
+
+ il->ieee_channels = NULL;
+ il->ieee_rates = NULL;
+ il->band = IEEE80211_BAND_2GHZ;
+
+ il->iw_mode = NL80211_IFTYPE_STATION;
+ il->current_ht_config.smps = IEEE80211_SMPS_STATIC;
+ il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF;
+
+ /* initialize force reset */
+ il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD;
+
+ /* Choose which receivers/antennas to use */
+ if (il->ops->set_rxon_chain)
+ il->ops->set_rxon_chain(il);
+
+ il_init_scan_params(il);
+
+ ret = il_init_channel_map(il);
+ if (ret) {
+ IL_ERR("initializing regulatory failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = il_init_geos(il);
+ if (ret) {
+ IL_ERR("initializing geos failed: %d\n", ret);
+ goto err_free_channel_map;
+ }
+ il4965_init_hw_rates(il, il->ieee_rates);
+
+ return 0;
+
+err_free_channel_map:
+ il_free_channel_map(il);
+err:
+ return ret;
+}
+
+static void
+il4965_uninit_drv(struct il_priv *il)
+{
+ il_free_geos(il);
+ il_free_channel_map(il);
+ kfree(il->scan_cmd);
+}
+
+static void
+il4965_hw_detect(struct il_priv *il)
+{
+ il->hw_rev = _il_rd(il, CSR_HW_REV);
+ il->hw_wa_rev = _il_rd(il, CSR_HW_REV_WA_REG);
+ il->rev_id = il->pci_dev->revision;
+ D_INFO("HW Revision ID = 0x%X\n", il->rev_id);
+}
+
+static const struct il_sensitivity_ranges il4965_sensitivity = {
+ .min_nrg_cck = 97,
+ .max_nrg_cck = 0, /* not used, set to 0 */
+
+ .auto_corr_min_ofdm = 85,
+ .auto_corr_min_ofdm_mrc = 170,
+ .auto_corr_min_ofdm_x1 = 105,
+ .auto_corr_min_ofdm_mrc_x1 = 220,
+
+ .auto_corr_max_ofdm = 120,
+ .auto_corr_max_ofdm_mrc = 210,
+ .auto_corr_max_ofdm_x1 = 140,
+ .auto_corr_max_ofdm_mrc_x1 = 270,
+
+ .auto_corr_min_cck = 125,
+ .auto_corr_max_cck = 200,
+ .auto_corr_min_cck_mrc = 200,
+ .auto_corr_max_cck_mrc = 400,
+
+ .nrg_th_cck = 100,
+ .nrg_th_ofdm = 100,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
+};
+
+static void
+il4965_set_hw_params(struct il_priv *il)
+{
+ il->hw_params.bcast_id = IL4965_BROADCAST_ID;
+ il->hw_params.max_rxq_size = RX_QUEUE_SIZE;
+ il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
+ if (il->cfg->mod_params->amsdu_size_8K)
+ il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_8K);
+ else
+ il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_4K);
+
+ il->hw_params.max_beacon_itrvl = IL_MAX_UCODE_BEACON_INTERVAL;
+
+ if (il->cfg->mod_params->disable_11n)
+ il->cfg->sku &= ~IL_SKU_N;
+
+ if (il->cfg->mod_params->num_of_queues >= IL_MIN_NUM_QUEUES &&
+ il->cfg->mod_params->num_of_queues <= IL49_NUM_QUEUES)
+ il->cfg->num_of_queues =
+ il->cfg->mod_params->num_of_queues;
+
+ il->hw_params.max_txq_num = il->cfg->num_of_queues;
+ il->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM;
+ il->hw_params.scd_bc_tbls_size =
+ il->cfg->num_of_queues *
+ sizeof(struct il4965_scd_bc_tbl);
+
+ il->hw_params.tfd_size = sizeof(struct il_tfd);
+ il->hw_params.max_stations = IL4965_STATION_COUNT;
+ il->hw_params.max_data_size = IL49_RTC_DATA_SIZE;
+ il->hw_params.max_inst_size = IL49_RTC_INST_SIZE;
+ il->hw_params.max_bsm_size = BSM_SRAM_SIZE;
+ il->hw_params.ht40_channel = BIT(IEEE80211_BAND_5GHZ);
+
+ il->hw_params.rx_wrt_ptr_reg = FH49_RSCSR_CHNL0_WPTR;
+
+ il->hw_params.tx_chains_num = il4965_num_of_ant(il->cfg->valid_tx_ant);
+ il->hw_params.rx_chains_num = il4965_num_of_ant(il->cfg->valid_rx_ant);
+ il->hw_params.valid_tx_ant = il->cfg->valid_tx_ant;
+ il->hw_params.valid_rx_ant = il->cfg->valid_rx_ant;
+
+ il->hw_params.ct_kill_threshold =
+ CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY);
+
+ il->hw_params.sens = &il4965_sensitivity;
+ il->hw_params.beacon_time_tsf_bits = IL4965_EXT_BEACON_TIME_POS;
+}
+
+static int
+il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int err = 0;
+ struct il_priv *il;
+ struct ieee80211_hw *hw;
+ struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data);
+ unsigned long flags;
+ u16 pci_cmd;
+
+ /************************
+ * 1. Allocating HW data
+ ************************/
+
+ hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il4965_mac_ops);
+ if (!hw) {
+ err = -ENOMEM;
+ goto out;
+ }
+ il = hw->priv;
+ il->hw = hw;
+ SET_IEEE80211_DEV(hw, &pdev->dev);
+
+ D_INFO("*** LOAD DRIVER ***\n");
+ il->cfg = cfg;
+ il->ops = &il4965_ops;
+#ifdef CONFIG_IWLEGACY_DEBUGFS
+ il->debugfs_ops = &il4965_debugfs_ops;
+#endif
+ il->pci_dev = pdev;
+ il->inta_mask = CSR_INI_SET_MASK;
+
+ /**************************
+ * 2. Initializing PCI bus
+ **************************/
+ pci_disable_link_state(pdev,
+ PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
+ PCIE_LINK_STATE_CLKPM);
+
+ if (pci_enable_device(pdev)) {
+ err = -ENODEV;
+ goto out_ieee80211_free_hw;
+ }
+
+ pci_set_master(pdev);
+
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
+ if (!err)
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
+ if (err) {
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (!err)
+ err =
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ /* both attempts failed: */
+ if (err) {
+ IL_WARN("No suitable DMA available.\n");
+ goto out_pci_disable_device;
+ }
+ }
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto out_pci_disable_device;
+
+ pci_set_drvdata(pdev, il);
+
+ /***********************
+ * 3. Read REV register
+ ***********************/
+ il->hw_base = pci_ioremap_bar(pdev, 0);
+ if (!il->hw_base) {
+ err = -ENODEV;
+ goto out_pci_release_regions;
+ }
+
+ D_INFO("pci_resource_len = 0x%08llx\n",
+ (unsigned long long)pci_resource_len(pdev, 0));
+ D_INFO("pci_resource_base = %p\n", il->hw_base);
+
+ /* these spin locks will be used in apm_ops.init and EEPROM access
+ * we should init now
+ */
+ spin_lock_init(&il->reg_lock);
+ spin_lock_init(&il->lock);
+
+ /*
+ * stop and reset the on-board processor just in case it is in a
+ * strange state ... like being left stranded by a primary kernel
+ * and this is now the kdump kernel trying to start up
+ */
+ _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+
+ il4965_hw_detect(il);
+ IL_INFO("Detected %s, REV=0x%X\n", il->cfg->name, il->hw_rev);
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
+ il4965_prepare_card_hw(il);
+ if (!il->hw_ready) {
+ IL_WARN("Failed, HW not ready\n");
+ err = -EIO;
+ goto out_iounmap;
+ }
+
+ /*****************
+ * 4. Read EEPROM
+ *****************/
+ /* Read the EEPROM */
+ err = il_eeprom_init(il);
+ if (err) {
+ IL_ERR("Unable to init EEPROM\n");
+ goto out_iounmap;
+ }
+ err = il4965_eeprom_check_version(il);
+ if (err)
+ goto out_free_eeprom;
+
+ /* extract MAC Address */
+ il4965_eeprom_get_mac(il, il->addresses[0].addr);
+ D_INFO("MAC address: %pM\n", il->addresses[0].addr);
+ il->hw->wiphy->addresses = il->addresses;
+ il->hw->wiphy->n_addresses = 1;
+
+ /************************
+ * 5. Setup HW constants
+ ************************/
+ il4965_set_hw_params(il);
+
+ /*******************
+ * 6. Setup il
+ *******************/
+
+ err = il4965_init_drv(il);
+ if (err)
+ goto out_free_eeprom;
+ /* At this point both hw and il are initialized. */
+
+ /********************
+ * 7. Setup services
+ ********************/
+ spin_lock_irqsave(&il->lock, flags);
+ il_disable_interrupts(il);
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ pci_enable_msi(il->pci_dev);
+
+ err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il);
+ if (err) {
+ IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq);
+ goto out_disable_msi;
+ }
+
+ il4965_setup_deferred_work(il);
+ il4965_setup_handlers(il);
+
+ /*********************************************
+ * 8. Enable interrupts and read RFKILL state
+ *********************************************/
+
+ /* enable rfkill interrupt: hw bug w/a */
+ pci_read_config_word(il->pci_dev, PCI_COMMAND, &pci_cmd);
+ if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
+ pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(il->pci_dev, PCI_COMMAND, pci_cmd);
+ }
+
+ il_enable_rfkill_int(il);
+
+ /* If platform's RF_KILL switch is NOT set to KILL */
+ if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
+ clear_bit(S_RFKILL, &il->status);
+ else
+ set_bit(S_RFKILL, &il->status);
+
+ wiphy_rfkill_set_hw_state(il->hw->wiphy,
+ test_bit(S_RFKILL, &il->status));
+
+ il_power_initialize(il);
+
+ init_completion(&il->_4965.firmware_loading_complete);
+
+ err = il4965_request_firmware(il, true);
+ if (err)
+ goto out_destroy_workqueue;
+
+ return 0;
+
+out_destroy_workqueue:
+ destroy_workqueue(il->workqueue);
+ il->workqueue = NULL;
+ free_irq(il->pci_dev->irq, il);
+out_disable_msi:
+ pci_disable_msi(il->pci_dev);
+ il4965_uninit_drv(il);
+out_free_eeprom:
+ il_eeprom_free(il);
+out_iounmap:
+ iounmap(il->hw_base);
+out_pci_release_regions:
+ pci_release_regions(pdev);
+out_pci_disable_device:
+ pci_disable_device(pdev);
+out_ieee80211_free_hw:
+ ieee80211_free_hw(il->hw);
+out:
+ return err;
+}
+
+static void
+il4965_pci_remove(struct pci_dev *pdev)
+{
+ struct il_priv *il = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ if (!il)
+ return;
+
+ wait_for_completion(&il->_4965.firmware_loading_complete);
+
+ D_INFO("*** UNLOAD DRIVER ***\n");
+
+ il_dbgfs_unregister(il);
+ sysfs_remove_group(&pdev->dev.kobj, &il_attribute_group);
+
+ /* ieee80211_unregister_hw call wil cause il_mac_stop to
+ * to be called and il4965_down since we are removing the device
+ * we need to set S_EXIT_PENDING bit.
+ */
+ set_bit(S_EXIT_PENDING, &il->status);
+
+ il_leds_exit(il);
+
+ if (il->mac80211_registered) {
+ ieee80211_unregister_hw(il->hw);
+ il->mac80211_registered = 0;
+ } else {
+ il4965_down(il);
+ }
+
+ /*
+ * Make sure device is reset to low power before unloading driver.
+ * This may be redundant with il4965_down(), but there are paths to
+ * run il4965_down() without calling apm_ops.stop(), and there are
+ * paths to avoid running il4965_down() at all before leaving driver.
+ * This (inexpensive) call *makes sure* device is reset.
+ */
+ il_apm_stop(il);
+
+ /* make sure we flush any pending irq or
+ * tasklet for the driver
+ */
+ spin_lock_irqsave(&il->lock, flags);
+ il_disable_interrupts(il);
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ il4965_synchronize_irq(il);
+
+ il4965_dealloc_ucode_pci(il);
+
+ if (il->rxq.bd)
+ il4965_rx_queue_free(il, &il->rxq);
+ il4965_hw_txq_ctx_free(il);
+
+ il_eeprom_free(il);
+
+ /*netif_stop_queue(dev); */
+ flush_workqueue(il->workqueue);
+
+ /* ieee80211_unregister_hw calls il_mac_stop, which flushes
+ * il->workqueue... so we can't take down the workqueue
+ * until now... */
+ destroy_workqueue(il->workqueue);
+ il->workqueue = NULL;
+
+ free_irq(il->pci_dev->irq, il);
+ pci_disable_msi(il->pci_dev);
+ iounmap(il->hw_base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+
+ il4965_uninit_drv(il);
+
+ dev_kfree_skb(il->beacon_skb);
+
+ ieee80211_free_hw(il->hw);
+}
+
+/*
+ * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
+ * must be called under il->lock and mac access
+ */
+void
+il4965_txq_set_sched(struct il_priv *il, u32 mask)
+{
+ il_wr_prph(il, IL49_SCD_TXFACT, mask);
+}
+
+/*****************************************************************************
+ *
+ * driver and module entry point
+ *
+ *****************************************************************************/
+
+/* Hardware specific file defines the PCI IDs table for that hardware module */
+static const struct pci_device_id il4965_hw_card_ids[] = {
+ {IL_PCI_DEVICE(0x4229, PCI_ANY_ID, il4965_cfg)},
+ {IL_PCI_DEVICE(0x4230, PCI_ANY_ID, il4965_cfg)},
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, il4965_hw_card_ids);
+
+static struct pci_driver il4965_driver = {
+ .name = DRV_NAME,
+ .id_table = il4965_hw_card_ids,
+ .probe = il4965_pci_probe,
+ .remove = il4965_pci_remove,
+ .driver.pm = IL_LEGACY_PM_OPS,
+};
+
+static int __init
+il4965_init(void)
+{
+
+ int ret;
+ pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
+ pr_info(DRV_COPYRIGHT "\n");
+
+ ret = il4965_rate_control_register();
+ if (ret) {
+ pr_err("Unable to register rate control algorithm: %d\n", ret);
+ return ret;
+ }
+
+ ret = pci_register_driver(&il4965_driver);
+ if (ret) {
+ pr_err("Unable to initialize PCI module\n");
+ goto error_register;
+ }
+
+ return ret;
+
+error_register:
+ il4965_rate_control_unregister();
+ return ret;
+}
+
+static void __exit
+il4965_exit(void)
+{
+ pci_unregister_driver(&il4965_driver);
+ il4965_rate_control_unregister();
+}
+
+module_exit(il4965_exit);
+module_init(il4965_init);
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "debug output mask");
+#endif
+
+module_param_named(swcrypto, il4965_mod_params.sw_crypto, int, S_IRUGO);
+MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
+module_param_named(queues_num, il4965_mod_params.num_of_queues, int, S_IRUGO);
+MODULE_PARM_DESC(queues_num, "number of hw queues.");
+module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO);
+MODULE_PARM_DESC(11n_disable, "disable 11n functionality");
+module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int,
+ S_IRUGO);
+MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])");
+module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO);
+MODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
index bac60b2bc3f0..bac60b2bc3f0 100644
--- a/drivers/net/wireless/iwlegacy/4965-rs.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
diff --git a/drivers/net/wireless/iwlegacy/4965.c b/drivers/net/wireless/intel/iwlegacy/4965.c
index fe47db9c20cd..fe47db9c20cd 100644
--- a/drivers/net/wireless/iwlegacy/4965.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965.c
diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/intel/iwlegacy/4965.h
index 8ab8706f9422..8ab8706f9422 100644
--- a/drivers/net/wireless/iwlegacy/4965.h
+++ b/drivers/net/wireless/intel/iwlegacy/4965.h
diff --git a/drivers/net/wireless/iwlegacy/Kconfig b/drivers/net/wireless/intel/iwlegacy/Kconfig
index fb919727b8bb..fb919727b8bb 100644
--- a/drivers/net/wireless/iwlegacy/Kconfig
+++ b/drivers/net/wireless/intel/iwlegacy/Kconfig
diff --git a/drivers/net/wireless/iwlegacy/Makefile b/drivers/net/wireless/intel/iwlegacy/Makefile
index c985a01a0731..c985a01a0731 100644
--- a/drivers/net/wireless/iwlegacy/Makefile
+++ b/drivers/net/wireless/intel/iwlegacy/Makefile
diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/intel/iwlegacy/commands.h
index dd744135c956..dd744135c956 100644
--- a/drivers/net/wireless/iwlegacy/commands.h
+++ b/drivers/net/wireless/intel/iwlegacy/commands.h
diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c
new file mode 100644
index 000000000000..eb5cb603bc52
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlegacy/common.c
@@ -0,0 +1,5586 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/lockdep.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <net/mac80211.h>
+
+#include "common.h"
+
+int
+_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout)
+{
+ const int interval = 10; /* microseconds */
+ int t = 0;
+
+ do {
+ if ((_il_rd(il, addr) & mask) == (bits & mask))
+ return t;
+ udelay(interval);
+ t += interval;
+ } while (t < timeout);
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL(_il_poll_bit);
+
+void
+il_set_bit(struct il_priv *p, u32 r, u32 m)
+{
+ unsigned long reg_flags;
+
+ spin_lock_irqsave(&p->reg_lock, reg_flags);
+ _il_set_bit(p, r, m);
+ spin_unlock_irqrestore(&p->reg_lock, reg_flags);
+}
+EXPORT_SYMBOL(il_set_bit);
+
+void
+il_clear_bit(struct il_priv *p, u32 r, u32 m)
+{
+ unsigned long reg_flags;
+
+ spin_lock_irqsave(&p->reg_lock, reg_flags);
+ _il_clear_bit(p, r, m);
+ spin_unlock_irqrestore(&p->reg_lock, reg_flags);
+}
+EXPORT_SYMBOL(il_clear_bit);
+
+bool
+_il_grab_nic_access(struct il_priv *il)
+{
+ int ret;
+ u32 val;
+
+ /* this bit wakes up the NIC */
+ _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ /*
+ * These bits say the device is running, and should keep running for
+ * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
+ * but they do not indicate that embedded SRAM is restored yet;
+ * 3945 and 4965 have volatile SRAM, and must save/restore contents
+ * to/from host DRAM when sleeping/waking for power-saving.
+ * Each direction takes approximately 1/4 millisecond; with this
+ * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
+ * series of register accesses are expected (e.g. reading Event Log),
+ * to keep device from sleeping.
+ *
+ * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
+ * SRAM is okay/restored. We don't check that here because this call
+ * is just for hardware register access; but GP1 MAC_SLEEP check is a
+ * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
+ *
+ */
+ ret =
+ _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+ (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+ CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
+ if (unlikely(ret < 0)) {
+ val = _il_rd(il, CSR_GP_CNTRL);
+ WARN_ONCE(1, "Timeout waiting for ucode processor access "
+ "(CSR_GP_CNTRL 0x%08x)\n", val);
+ _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(_il_grab_nic_access);
+
+int
+il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout)
+{
+ const int interval = 10; /* microseconds */
+ int t = 0;
+
+ do {
+ if ((il_rd(il, addr) & mask) == mask)
+ return t;
+ udelay(interval);
+ t += interval;
+ } while (t < timeout);
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL(il_poll_bit);
+
+u32
+il_rd_prph(struct il_priv *il, u32 reg)
+{
+ unsigned long reg_flags;
+ u32 val;
+
+ spin_lock_irqsave(&il->reg_lock, reg_flags);
+ _il_grab_nic_access(il);
+ val = _il_rd_prph(il, reg);
+ _il_release_nic_access(il);
+ spin_unlock_irqrestore(&il->reg_lock, reg_flags);
+ return val;
+}
+EXPORT_SYMBOL(il_rd_prph);
+
+void
+il_wr_prph(struct il_priv *il, u32 addr, u32 val)
+{
+ unsigned long reg_flags;
+
+ spin_lock_irqsave(&il->reg_lock, reg_flags);
+ if (likely(_il_grab_nic_access(il))) {
+ _il_wr_prph(il, addr, val);
+ _il_release_nic_access(il);
+ }
+ spin_unlock_irqrestore(&il->reg_lock, reg_flags);
+}
+EXPORT_SYMBOL(il_wr_prph);
+
+u32
+il_read_targ_mem(struct il_priv *il, u32 addr)
+{
+ unsigned long reg_flags;
+ u32 value;
+
+ spin_lock_irqsave(&il->reg_lock, reg_flags);
+ _il_grab_nic_access(il);
+
+ _il_wr(il, HBUS_TARG_MEM_RADDR, addr);
+ value = _il_rd(il, HBUS_TARG_MEM_RDAT);
+
+ _il_release_nic_access(il);
+ spin_unlock_irqrestore(&il->reg_lock, reg_flags);
+ return value;
+}
+EXPORT_SYMBOL(il_read_targ_mem);
+
+void
+il_write_targ_mem(struct il_priv *il, u32 addr, u32 val)
+{
+ unsigned long reg_flags;
+
+ spin_lock_irqsave(&il->reg_lock, reg_flags);
+ if (likely(_il_grab_nic_access(il))) {
+ _il_wr(il, HBUS_TARG_MEM_WADDR, addr);
+ _il_wr(il, HBUS_TARG_MEM_WDAT, val);
+ _il_release_nic_access(il);
+ }
+ spin_unlock_irqrestore(&il->reg_lock, reg_flags);
+}
+EXPORT_SYMBOL(il_write_targ_mem);
+
+const char *
+il_get_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ IL_CMD(N_ALIVE);
+ IL_CMD(N_ERROR);
+ IL_CMD(C_RXON);
+ IL_CMD(C_RXON_ASSOC);
+ IL_CMD(C_QOS_PARAM);
+ IL_CMD(C_RXON_TIMING);
+ IL_CMD(C_ADD_STA);
+ IL_CMD(C_REM_STA);
+ IL_CMD(C_WEPKEY);
+ IL_CMD(N_3945_RX);
+ IL_CMD(C_TX);
+ IL_CMD(C_RATE_SCALE);
+ IL_CMD(C_LEDS);
+ IL_CMD(C_TX_LINK_QUALITY_CMD);
+ IL_CMD(C_CHANNEL_SWITCH);
+ IL_CMD(N_CHANNEL_SWITCH);
+ IL_CMD(C_SPECTRUM_MEASUREMENT);
+ IL_CMD(N_SPECTRUM_MEASUREMENT);
+ IL_CMD(C_POWER_TBL);
+ IL_CMD(N_PM_SLEEP);
+ IL_CMD(N_PM_DEBUG_STATS);
+ IL_CMD(C_SCAN);
+ IL_CMD(C_SCAN_ABORT);
+ IL_CMD(N_SCAN_START);
+ IL_CMD(N_SCAN_RESULTS);
+ IL_CMD(N_SCAN_COMPLETE);
+ IL_CMD(N_BEACON);
+ IL_CMD(C_TX_BEACON);
+ IL_CMD(C_TX_PWR_TBL);
+ IL_CMD(C_BT_CONFIG);
+ IL_CMD(C_STATS);
+ IL_CMD(N_STATS);
+ IL_CMD(N_CARD_STATE);
+ IL_CMD(N_MISSED_BEACONS);
+ IL_CMD(C_CT_KILL_CONFIG);
+ IL_CMD(C_SENSITIVITY);
+ IL_CMD(C_PHY_CALIBRATION);
+ IL_CMD(N_RX_PHY);
+ IL_CMD(N_RX_MPDU);
+ IL_CMD(N_RX);
+ IL_CMD(N_COMPRESSED_BA);
+ default:
+ return "UNKNOWN";
+
+ }
+}
+EXPORT_SYMBOL(il_get_cmd_string);
+
+#define HOST_COMPLETE_TIMEOUT (HZ / 2)
+
+static void
+il_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd,
+ struct il_rx_pkt *pkt)
+{
+ if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
+ IL_ERR("Bad return from %s (0x%08X)\n",
+ il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
+ return;
+ }
+#ifdef CONFIG_IWLEGACY_DEBUG
+ switch (cmd->hdr.cmd) {
+ case C_TX_LINK_QUALITY_CMD:
+ case C_SENSITIVITY:
+ D_HC_DUMP("back from %s (0x%08X)\n",
+ il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
+ break;
+ default:
+ D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd),
+ pkt->hdr.flags);
+ }
+#endif
+}
+
+static int
+il_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd)
+{
+ int ret;
+
+ BUG_ON(!(cmd->flags & CMD_ASYNC));
+
+ /* An asynchronous command can not expect an SKB to be set. */
+ BUG_ON(cmd->flags & CMD_WANT_SKB);
+
+ /* Assign a generic callback if one is not provided */
+ if (!cmd->callback)
+ cmd->callback = il_generic_cmd_callback;
+
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return -EBUSY;
+
+ ret = il_enqueue_hcmd(il, cmd);
+ if (ret < 0) {
+ IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
+ il_get_cmd_string(cmd->id), ret);
+ return ret;
+ }
+ return 0;
+}
+
+int
+il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd)
+{
+ int cmd_idx;
+ int ret;
+
+ lockdep_assert_held(&il->mutex);
+
+ BUG_ON(cmd->flags & CMD_ASYNC);
+
+ /* A synchronous command can not have a callback set. */
+ BUG_ON(cmd->callback);
+
+ D_INFO("Attempting to send sync command %s\n",
+ il_get_cmd_string(cmd->id));
+
+ set_bit(S_HCMD_ACTIVE, &il->status);
+ D_INFO("Setting HCMD_ACTIVE for command %s\n",
+ il_get_cmd_string(cmd->id));
+
+ cmd_idx = il_enqueue_hcmd(il, cmd);
+ if (cmd_idx < 0) {
+ ret = cmd_idx;
+ IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
+ il_get_cmd_string(cmd->id), ret);
+ goto out;
+ }
+
+ ret = wait_event_timeout(il->wait_command_queue,
+ !test_bit(S_HCMD_ACTIVE, &il->status),
+ HOST_COMPLETE_TIMEOUT);
+ if (!ret) {
+ if (test_bit(S_HCMD_ACTIVE, &il->status)) {
+ IL_ERR("Error sending %s: time out after %dms.\n",
+ il_get_cmd_string(cmd->id),
+ jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
+
+ clear_bit(S_HCMD_ACTIVE, &il->status);
+ D_INFO("Clearing HCMD_ACTIVE for command %s\n",
+ il_get_cmd_string(cmd->id));
+ ret = -ETIMEDOUT;
+ goto cancel;
+ }
+ }
+
+ if (test_bit(S_RFKILL, &il->status)) {
+ IL_ERR("Command %s aborted: RF KILL Switch\n",
+ il_get_cmd_string(cmd->id));
+ ret = -ECANCELED;
+ goto fail;
+ }
+ if (test_bit(S_FW_ERROR, &il->status)) {
+ IL_ERR("Command %s failed: FW Error\n",
+ il_get_cmd_string(cmd->id));
+ ret = -EIO;
+ goto fail;
+ }
+ if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
+ IL_ERR("Error: Response NULL in '%s'\n",
+ il_get_cmd_string(cmd->id));
+ ret = -EIO;
+ goto cancel;
+ }
+
+ ret = 0;
+ goto out;
+
+cancel:
+ if (cmd->flags & CMD_WANT_SKB) {
+ /*
+ * Cancel the CMD_WANT_SKB flag for the cmd in the
+ * TX cmd queue. Otherwise in case the cmd comes
+ * in later, it will possibly set an invalid
+ * address (cmd->meta.source).
+ */
+ il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB;
+ }
+fail:
+ if (cmd->reply_page) {
+ il_free_pages(il, cmd->reply_page);
+ cmd->reply_page = 0;
+ }
+out:
+ return ret;
+}
+EXPORT_SYMBOL(il_send_cmd_sync);
+
+int
+il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd)
+{
+ if (cmd->flags & CMD_ASYNC)
+ return il_send_cmd_async(il, cmd);
+
+ return il_send_cmd_sync(il, cmd);
+}
+EXPORT_SYMBOL(il_send_cmd);
+
+int
+il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data)
+{
+ struct il_host_cmd cmd = {
+ .id = id,
+ .len = len,
+ .data = data,
+ };
+
+ return il_send_cmd_sync(il, &cmd);
+}
+EXPORT_SYMBOL(il_send_cmd_pdu);
+
+int
+il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data,
+ void (*callback) (struct il_priv *il,
+ struct il_device_cmd *cmd,
+ struct il_rx_pkt *pkt))
+{
+ struct il_host_cmd cmd = {
+ .id = id,
+ .len = len,
+ .data = data,
+ };
+
+ cmd.flags |= CMD_ASYNC;
+ cmd.callback = callback;
+
+ return il_send_cmd_async(il, &cmd);
+}
+EXPORT_SYMBOL(il_send_cmd_pdu_async);
+
+/* default: IL_LED_BLINK(0) using blinking idx table */
+static int led_mode;
+module_param(led_mode, int, S_IRUGO);
+MODULE_PARM_DESC(led_mode,
+ "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking");
+
+/* Throughput OFF time(ms) ON time (ms)
+ * >300 25 25
+ * >200 to 300 40 40
+ * >100 to 200 55 55
+ * >70 to 100 65 65
+ * >50 to 70 75 75
+ * >20 to 50 85 85
+ * >10 to 20 95 95
+ * >5 to 10 110 110
+ * >1 to 5 130 130
+ * >0 to 1 167 167
+ * <=0 SOLID ON
+ */
+static const struct ieee80211_tpt_blink il_blink[] = {
+ {.throughput = 0, .blink_time = 334},
+ {.throughput = 1 * 1024 - 1, .blink_time = 260},
+ {.throughput = 5 * 1024 - 1, .blink_time = 220},
+ {.throughput = 10 * 1024 - 1, .blink_time = 190},
+ {.throughput = 20 * 1024 - 1, .blink_time = 170},
+ {.throughput = 50 * 1024 - 1, .blink_time = 150},
+ {.throughput = 70 * 1024 - 1, .blink_time = 130},
+ {.throughput = 100 * 1024 - 1, .blink_time = 110},
+ {.throughput = 200 * 1024 - 1, .blink_time = 80},
+ {.throughput = 300 * 1024 - 1, .blink_time = 50},
+};
+
+/*
+ * Adjust led blink rate to compensate on a MAC Clock difference on every HW
+ * Led blink rate analysis showed an average deviation of 0% on 3945,
+ * 5% on 4965 HW.
+ * Need to compensate on the led on/off time per HW according to the deviation
+ * to achieve the desired led frequency
+ * The calculation is: (100-averageDeviation)/100 * blinkTime
+ * For code efficiency the calculation will be:
+ * compensation = (100 - averageDeviation) * 64 / 100
+ * NewBlinkTime = (compensation * BlinkTime) / 64
+ */
+static inline u8
+il_blink_compensation(struct il_priv *il, u8 time, u16 compensation)
+{
+ if (!compensation) {
+ IL_ERR("undefined blink compensation: "
+ "use pre-defined blinking time\n");
+ return time;
+ }
+
+ return (u8) ((time * compensation) >> 6);
+}
+
+/* Set led pattern command */
+static int
+il_led_cmd(struct il_priv *il, unsigned long on, unsigned long off)
+{
+ struct il_led_cmd led_cmd = {
+ .id = IL_LED_LINK,
+ .interval = IL_DEF_LED_INTRVL
+ };
+ int ret;
+
+ if (!test_bit(S_READY, &il->status))
+ return -EBUSY;
+
+ if (il->blink_on == on && il->blink_off == off)
+ return 0;
+
+ if (off == 0) {
+ /* led is SOLID_ON */
+ on = IL_LED_SOLID;
+ }
+
+ D_LED("Led blink time compensation=%u\n",
+ il->cfg->led_compensation);
+ led_cmd.on =
+ il_blink_compensation(il, on,
+ il->cfg->led_compensation);
+ led_cmd.off =
+ il_blink_compensation(il, off,
+ il->cfg->led_compensation);
+
+ ret = il->ops->send_led_cmd(il, &led_cmd);
+ if (!ret) {
+ il->blink_on = on;
+ il->blink_off = off;
+ }
+ return ret;
+}
+
+static void
+il_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct il_priv *il = container_of(led_cdev, struct il_priv, led);
+ unsigned long on = 0;
+
+ if (brightness > 0)
+ on = IL_LED_SOLID;
+
+ il_led_cmd(il, on, 0);
+}
+
+static int
+il_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct il_priv *il = container_of(led_cdev, struct il_priv, led);
+
+ return il_led_cmd(il, *delay_on, *delay_off);
+}
+
+void
+il_leds_init(struct il_priv *il)
+{
+ int mode = led_mode;
+ int ret;
+
+ if (mode == IL_LED_DEFAULT)
+ mode = il->cfg->led_mode;
+
+ il->led.name =
+ kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy));
+ il->led.brightness_set = il_led_brightness_set;
+ il->led.blink_set = il_led_blink_set;
+ il->led.max_brightness = 1;
+
+ switch (mode) {
+ case IL_LED_DEFAULT:
+ WARN_ON(1);
+ break;
+ case IL_LED_BLINK:
+ il->led.default_trigger =
+ ieee80211_create_tpt_led_trigger(il->hw,
+ IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
+ il_blink,
+ ARRAY_SIZE(il_blink));
+ break;
+ case IL_LED_RF_STATE:
+ il->led.default_trigger = ieee80211_get_radio_led_name(il->hw);
+ break;
+ }
+
+ ret = led_classdev_register(&il->pci_dev->dev, &il->led);
+ if (ret) {
+ kfree(il->led.name);
+ return;
+ }
+
+ il->led_registered = true;
+}
+EXPORT_SYMBOL(il_leds_init);
+
+void
+il_leds_exit(struct il_priv *il)
+{
+ if (!il->led_registered)
+ return;
+
+ led_classdev_unregister(&il->led);
+ kfree(il->led.name);
+}
+EXPORT_SYMBOL(il_leds_exit);
+
+/************************** EEPROM BANDS ****************************
+ *
+ * The il_eeprom_band definitions below provide the mapping from the
+ * EEPROM contents to the specific channel number supported for each
+ * band.
+ *
+ * For example, il_priv->eeprom.band_3_channels[4] from the band_3
+ * definition below maps to physical channel 42 in the 5.2GHz spectrum.
+ * The specific geography and calibration information for that channel
+ * is contained in the eeprom map itself.
+ *
+ * During init, we copy the eeprom information and channel map
+ * information into il->channel_info_24/52 and il->channel_map_24/52
+ *
+ * channel_map_24/52 provides the idx in the channel_info array for a
+ * given channel. We have to have two separate maps as there is channel
+ * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and
+ * band_2
+ *
+ * A value of 0xff stored in the channel_map indicates that the channel
+ * is not supported by the hardware at all.
+ *
+ * A value of 0xfe in the channel_map indicates that the channel is not
+ * valid for Tx with the current hardware. This means that
+ * while the system can tune and receive on a given channel, it may not
+ * be able to associate or transmit any frames on that
+ * channel. There is no corresponding channel information for that
+ * entry.
+ *
+ *********************************************************************/
+
+/* 2.4 GHz */
+const u8 il_eeprom_band_1[14] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
+};
+
+/* 5.2 GHz bands */
+static const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */
+ 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16
+};
+
+static const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */
+ 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
+};
+
+static const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */
+ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
+};
+
+static const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */
+ 145, 149, 153, 157, 161, 165
+};
+
+static const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */
+ 1, 2, 3, 4, 5, 6, 7
+};
+
+static const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */
+ 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
+};
+
+/******************************************************************************
+ *
+ * EEPROM related functions
+ *
+******************************************************************************/
+
+static int
+il_eeprom_verify_signature(struct il_priv *il)
+{
+ u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
+ int ret = 0;
+
+ D_EEPROM("EEPROM signature=0x%08x\n", gp);
+ switch (gp) {
+ case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
+ case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
+ break;
+ default:
+ IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp);
+ ret = -ENOENT;
+ break;
+ }
+ return ret;
+}
+
+const u8 *
+il_eeprom_query_addr(const struct il_priv *il, size_t offset)
+{
+ BUG_ON(offset >= il->cfg->eeprom_size);
+ return &il->eeprom[offset];
+}
+EXPORT_SYMBOL(il_eeprom_query_addr);
+
+u16
+il_eeprom_query16(const struct il_priv *il, size_t offset)
+{
+ if (!il->eeprom)
+ return 0;
+ return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8);
+}
+EXPORT_SYMBOL(il_eeprom_query16);
+
+/**
+ * il_eeprom_init - read EEPROM contents
+ *
+ * Load the EEPROM contents from adapter into il->eeprom
+ *
+ * NOTE: This routine uses the non-debug IO access functions.
+ */
+int
+il_eeprom_init(struct il_priv *il)
+{
+ __le16 *e;
+ u32 gp = _il_rd(il, CSR_EEPROM_GP);
+ int sz;
+ int ret;
+ u16 addr;
+
+ /* allocate eeprom */
+ sz = il->cfg->eeprom_size;
+ D_EEPROM("NVM size = %d\n", sz);
+ il->eeprom = kzalloc(sz, GFP_KERNEL);
+ if (!il->eeprom) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+ e = (__le16 *) il->eeprom;
+
+ il->ops->apm_init(il);
+
+ ret = il_eeprom_verify_signature(il);
+ if (ret < 0) {
+ IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp);
+ ret = -ENOENT;
+ goto err;
+ }
+
+ /* Make sure driver (instead of uCode) is allowed to read EEPROM */
+ ret = il->ops->eeprom_acquire_semaphore(il);
+ if (ret < 0) {
+ IL_ERR("Failed to acquire EEPROM semaphore.\n");
+ ret = -ENOENT;
+ goto err;
+ }
+
+ /* eeprom is an array of 16bit values */
+ for (addr = 0; addr < sz; addr += sizeof(u16)) {
+ u32 r;
+
+ _il_wr(il, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
+
+ ret =
+ _il_poll_bit(il, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ IL_EEPROM_ACCESS_TIMEOUT);
+ if (ret < 0) {
+ IL_ERR("Time out reading EEPROM[%d]\n", addr);
+ goto done;
+ }
+ r = _il_rd(il, CSR_EEPROM_REG);
+ e[addr / 2] = cpu_to_le16(r >> 16);
+ }
+
+ D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM",
+ il_eeprom_query16(il, EEPROM_VERSION));
+
+ ret = 0;
+done:
+ il->ops->eeprom_release_semaphore(il);
+
+err:
+ if (ret)
+ il_eeprom_free(il);
+ /* Reset chip to save power until we load uCode during "up". */
+ il_apm_stop(il);
+alloc_err:
+ return ret;
+}
+EXPORT_SYMBOL(il_eeprom_init);
+
+void
+il_eeprom_free(struct il_priv *il)
+{
+ kfree(il->eeprom);
+ il->eeprom = NULL;
+}
+EXPORT_SYMBOL(il_eeprom_free);
+
+static void
+il_init_band_reference(const struct il_priv *il, int eep_band,
+ int *eeprom_ch_count,
+ const struct il_eeprom_channel **eeprom_ch_info,
+ const u8 **eeprom_ch_idx)
+{
+ u32 offset = il->cfg->regulatory_bands[eep_band - 1];
+
+ switch (eep_band) {
+ case 1: /* 2.4GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1);
+ *eeprom_ch_info =
+ (struct il_eeprom_channel *)il_eeprom_query_addr(il,
+ offset);
+ *eeprom_ch_idx = il_eeprom_band_1;
+ break;
+ case 2: /* 4.9GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2);
+ *eeprom_ch_info =
+ (struct il_eeprom_channel *)il_eeprom_query_addr(il,
+ offset);
+ *eeprom_ch_idx = il_eeprom_band_2;
+ break;
+ case 3: /* 5.2GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3);
+ *eeprom_ch_info =
+ (struct il_eeprom_channel *)il_eeprom_query_addr(il,
+ offset);
+ *eeprom_ch_idx = il_eeprom_band_3;
+ break;
+ case 4: /* 5.5GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4);
+ *eeprom_ch_info =
+ (struct il_eeprom_channel *)il_eeprom_query_addr(il,
+ offset);
+ *eeprom_ch_idx = il_eeprom_band_4;
+ break;
+ case 5: /* 5.7GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5);
+ *eeprom_ch_info =
+ (struct il_eeprom_channel *)il_eeprom_query_addr(il,
+ offset);
+ *eeprom_ch_idx = il_eeprom_band_5;
+ break;
+ case 6: /* 2.4GHz ht40 channels */
+ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6);
+ *eeprom_ch_info =
+ (struct il_eeprom_channel *)il_eeprom_query_addr(il,
+ offset);
+ *eeprom_ch_idx = il_eeprom_band_6;
+ break;
+ case 7: /* 5 GHz ht40 channels */
+ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7);
+ *eeprom_ch_info =
+ (struct il_eeprom_channel *)il_eeprom_query_addr(il,
+ offset);
+ *eeprom_ch_idx = il_eeprom_band_7;
+ break;
+ default:
+ BUG();
+ }
+}
+
+#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \
+ ? # x " " : "")
+/**
+ * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il.
+ *
+ * Does not set up a command, or touch hardware.
+ */
+static int
+il_mod_ht40_chan_info(struct il_priv *il, enum ieee80211_band band, u16 channel,
+ const struct il_eeprom_channel *eeprom_ch,
+ u8 clear_ht40_extension_channel)
+{
+ struct il_channel_info *ch_info;
+
+ ch_info =
+ (struct il_channel_info *)il_get_channel_info(il, band, channel);
+
+ if (!il_is_channel_valid(ch_info))
+ return -1;
+
+ D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):"
+ " Ad-Hoc %ssupported\n", ch_info->channel,
+ il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
+ CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE),
+ CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE),
+ CHECK_AND_PRINT(DFS), eeprom_ch->flags,
+ eeprom_ch->max_power_avg,
+ ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) &&
+ !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not ");
+
+ ch_info->ht40_eeprom = *eeprom_ch;
+ ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg;
+ ch_info->ht40_flags = eeprom_ch->flags;
+ if (eeprom_ch->flags & EEPROM_CHANNEL_VALID)
+ ch_info->ht40_extension_channel &=
+ ~clear_ht40_extension_channel;
+
+ return 0;
+}
+
+#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \
+ ? # x " " : "")
+
+/**
+ * il_init_channel_map - Set up driver's info for all possible channels
+ */
+int
+il_init_channel_map(struct il_priv *il)
+{
+ int eeprom_ch_count = 0;
+ const u8 *eeprom_ch_idx = NULL;
+ const struct il_eeprom_channel *eeprom_ch_info = NULL;
+ int band, ch;
+ struct il_channel_info *ch_info;
+
+ if (il->channel_count) {
+ D_EEPROM("Channel map already initialized.\n");
+ return 0;
+ }
+
+ D_EEPROM("Initializing regulatory info from EEPROM\n");
+
+ il->channel_count =
+ ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) +
+ ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) +
+ ARRAY_SIZE(il_eeprom_band_5);
+
+ D_EEPROM("Parsing data for %d channels.\n", il->channel_count);
+
+ il->channel_info =
+ kzalloc(sizeof(struct il_channel_info) * il->channel_count,
+ GFP_KERNEL);
+ if (!il->channel_info) {
+ IL_ERR("Could not allocate channel_info\n");
+ il->channel_count = 0;
+ return -ENOMEM;
+ }
+
+ ch_info = il->channel_info;
+
+ /* Loop through the 5 EEPROM bands adding them in order to the
+ * channel map we maintain (that contains additional information than
+ * what just in the EEPROM) */
+ for (band = 1; band <= 5; band++) {
+
+ il_init_band_reference(il, band, &eeprom_ch_count,
+ &eeprom_ch_info, &eeprom_ch_idx);
+
+ /* Loop through each band adding each of the channels */
+ for (ch = 0; ch < eeprom_ch_count; ch++) {
+ ch_info->channel = eeprom_ch_idx[ch];
+ ch_info->band =
+ (band ==
+ 1) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+
+ /* permanently store EEPROM's channel regulatory flags
+ * and max power in channel info database. */
+ ch_info->eeprom = eeprom_ch_info[ch];
+
+ /* Copy the run-time flags so they are there even on
+ * invalid channels */
+ ch_info->flags = eeprom_ch_info[ch].flags;
+ /* First write that ht40 is not enabled, and then enable
+ * one by one */
+ ch_info->ht40_extension_channel =
+ IEEE80211_CHAN_NO_HT40;
+
+ if (!(il_is_channel_valid(ch_info))) {
+ D_EEPROM("Ch. %d Flags %x [%sGHz] - "
+ "No traffic\n", ch_info->channel,
+ ch_info->flags,
+ il_is_channel_a_band(ch_info) ? "5.2" :
+ "2.4");
+ ch_info++;
+ continue;
+ }
+
+ /* Initialize regulatory-based run-time data */
+ ch_info->max_power_avg = ch_info->curr_txpow =
+ eeprom_ch_info[ch].max_power_avg;
+ ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
+ ch_info->min_power = 0;
+
+ D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):"
+ " Ad-Hoc %ssupported\n", ch_info->channel,
+ il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
+ CHECK_AND_PRINT_I(VALID),
+ CHECK_AND_PRINT_I(IBSS),
+ CHECK_AND_PRINT_I(ACTIVE),
+ CHECK_AND_PRINT_I(RADAR),
+ CHECK_AND_PRINT_I(WIDE),
+ CHECK_AND_PRINT_I(DFS),
+ eeprom_ch_info[ch].flags,
+ eeprom_ch_info[ch].max_power_avg,
+ ((eeprom_ch_info[ch].
+ flags & EEPROM_CHANNEL_IBSS) &&
+ !(eeprom_ch_info[ch].
+ flags & EEPROM_CHANNEL_RADAR)) ? "" :
+ "not ");
+
+ ch_info++;
+ }
+ }
+
+ /* Check if we do have HT40 channels */
+ if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 &&
+ il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40)
+ return 0;
+
+ /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */
+ for (band = 6; band <= 7; band++) {
+ enum ieee80211_band ieeeband;
+
+ il_init_band_reference(il, band, &eeprom_ch_count,
+ &eeprom_ch_info, &eeprom_ch_idx);
+
+ /* EEPROM band 6 is 2.4, band 7 is 5 GHz */
+ ieeeband =
+ (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+
+ /* Loop through each band adding each of the channels */
+ for (ch = 0; ch < eeprom_ch_count; ch++) {
+ /* Set up driver's info for lower half */
+ il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch],
+ &eeprom_ch_info[ch],
+ IEEE80211_CHAN_NO_HT40PLUS);
+
+ /* Set up driver's info for upper half */
+ il_mod_ht40_chan_info(il, ieeeband,
+ eeprom_ch_idx[ch] + 4,
+ &eeprom_ch_info[ch],
+ IEEE80211_CHAN_NO_HT40MINUS);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(il_init_channel_map);
+
+/*
+ * il_free_channel_map - undo allocations in il_init_channel_map
+ */
+void
+il_free_channel_map(struct il_priv *il)
+{
+ kfree(il->channel_info);
+ il->channel_count = 0;
+}
+EXPORT_SYMBOL(il_free_channel_map);
+
+/**
+ * il_get_channel_info - Find driver's ilate channel info
+ *
+ * Based on band and channel number.
+ */
+const struct il_channel_info *
+il_get_channel_info(const struct il_priv *il, enum ieee80211_band band,
+ u16 channel)
+{
+ int i;
+
+ switch (band) {
+ case IEEE80211_BAND_5GHZ:
+ for (i = 14; i < il->channel_count; i++) {
+ if (il->channel_info[i].channel == channel)
+ return &il->channel_info[i];
+ }
+ break;
+ case IEEE80211_BAND_2GHZ:
+ if (channel >= 1 && channel <= 14)
+ return &il->channel_info[channel - 1];
+ break;
+ default:
+ BUG();
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(il_get_channel_info);
+
+/*
+ * Setting power level allows the card to go to sleep when not busy.
+ *
+ * We calculate a sleep command based on the required latency, which
+ * we get from mac80211.
+ */
+
+#define SLP_VEC(X0, X1, X2, X3, X4) { \
+ cpu_to_le32(X0), \
+ cpu_to_le32(X1), \
+ cpu_to_le32(X2), \
+ cpu_to_le32(X3), \
+ cpu_to_le32(X4) \
+}
+
+static void
+il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd)
+{
+ const __le32 interval[3][IL_POWER_VEC_SIZE] = {
+ SLP_VEC(2, 2, 4, 6, 0xFF),
+ SLP_VEC(2, 4, 7, 10, 10),
+ SLP_VEC(4, 7, 10, 10, 0xFF)
+ };
+ int i, dtim_period, no_dtim;
+ u32 max_sleep;
+ bool skip;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (il->power_data.pci_pm)
+ cmd->flags |= IL_POWER_PCI_PM_MSK;
+
+ /* if no Power Save, we are done */
+ if (il->power_data.ps_disabled)
+ return;
+
+ cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK;
+ cmd->keep_alive_seconds = 0;
+ cmd->debug_flags = 0;
+ cmd->rx_data_timeout = cpu_to_le32(25 * 1024);
+ cmd->tx_data_timeout = cpu_to_le32(25 * 1024);
+ cmd->keep_alive_beacons = 0;
+
+ dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0;
+
+ if (dtim_period <= 2) {
+ memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0]));
+ no_dtim = 2;
+ } else if (dtim_period <= 10) {
+ memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1]));
+ no_dtim = 2;
+ } else {
+ memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2]));
+ no_dtim = 0;
+ }
+
+ if (dtim_period == 0) {
+ dtim_period = 1;
+ skip = false;
+ } else {
+ skip = !!no_dtim;
+ }
+
+ if (skip) {
+ __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1];
+
+ max_sleep = le32_to_cpu(tmp);
+ if (max_sleep == 0xFF)
+ max_sleep = dtim_period * (skip + 1);
+ else if (max_sleep > dtim_period)
+ max_sleep = (max_sleep / dtim_period) * dtim_period;
+ cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK;
+ } else {
+ max_sleep = dtim_period;
+ cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK;
+ }
+
+ for (i = 0; i < IL_POWER_VEC_SIZE; i++)
+ if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
+ cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
+}
+
+static int
+il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd)
+{
+ D_POWER("Sending power/sleep command\n");
+ D_POWER("Flags value = 0x%08X\n", cmd->flags);
+ D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
+ D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
+ D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n",
+ le32_to_cpu(cmd->sleep_interval[0]),
+ le32_to_cpu(cmd->sleep_interval[1]),
+ le32_to_cpu(cmd->sleep_interval[2]),
+ le32_to_cpu(cmd->sleep_interval[3]),
+ le32_to_cpu(cmd->sleep_interval[4]));
+
+ return il_send_cmd_pdu(il, C_POWER_TBL,
+ sizeof(struct il_powertable_cmd), cmd);
+}
+
+static int
+il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force)
+{
+ int ret;
+ bool update_chains;
+
+ lockdep_assert_held(&il->mutex);
+
+ /* Don't update the RX chain when chain noise calibration is running */
+ update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE ||
+ il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE;
+
+ if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
+ return 0;
+
+ if (!il_is_ready_rf(il))
+ return -EIO;
+
+ /* scan complete use sleep_power_next, need to be updated */
+ memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
+ if (test_bit(S_SCANNING, &il->status) && !force) {
+ D_INFO("Defer power set mode while scanning\n");
+ return 0;
+ }
+
+ if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)
+ set_bit(S_POWER_PMI, &il->status);
+
+ ret = il_set_power(il, cmd);
+ if (!ret) {
+ if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK))
+ clear_bit(S_POWER_PMI, &il->status);
+
+ if (il->ops->update_chain_flags && update_chains)
+ il->ops->update_chain_flags(il);
+ else if (il->ops->update_chain_flags)
+ D_POWER("Cannot update the power, chain noise "
+ "calibration running: %d\n",
+ il->chain_noise_data.state);
+
+ memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd));
+ } else
+ IL_ERR("set power fail, ret = %d", ret);
+
+ return ret;
+}
+
+int
+il_power_update_mode(struct il_priv *il, bool force)
+{
+ struct il_powertable_cmd cmd;
+
+ il_build_powertable_cmd(il, &cmd);
+
+ return il_power_set_mode(il, &cmd, force);
+}
+EXPORT_SYMBOL(il_power_update_mode);
+
+/* initialize to default */
+void
+il_power_initialize(struct il_priv *il)
+{
+ u16 lctl;
+
+ pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
+ il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
+
+ il->power_data.debug_sleep_level_override = -1;
+
+ memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd));
+}
+EXPORT_SYMBOL(il_power_initialize);
+
+/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
+ * sending probe req. This should be set long enough to hear probe responses
+ * from more than one AP. */
+#define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */
+#define IL_ACTIVE_DWELL_TIME_52 (20)
+
+#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3)
+#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2)
+
+/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
+ * Must be set longer than active dwell time.
+ * For the most reliable scan, set > AP beacon interval (typically 100msec). */
+#define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */
+#define IL_PASSIVE_DWELL_TIME_52 (10)
+#define IL_PASSIVE_DWELL_BASE (100)
+#define IL_CHANNEL_TUNE_TIME 5
+
+static int
+il_send_scan_abort(struct il_priv *il)
+{
+ int ret;
+ struct il_rx_pkt *pkt;
+ struct il_host_cmd cmd = {
+ .id = C_SCAN_ABORT,
+ .flags = CMD_WANT_SKB,
+ };
+
+ /* Exit instantly with error when device is not ready
+ * to receive scan abort command or it does not perform
+ * hardware scan currently */
+ if (!test_bit(S_READY, &il->status) ||
+ !test_bit(S_GEO_CONFIGURED, &il->status) ||
+ !test_bit(S_SCAN_HW, &il->status) ||
+ test_bit(S_FW_ERROR, &il->status) ||
+ test_bit(S_EXIT_PENDING, &il->status))
+ return -EIO;
+
+ ret = il_send_cmd_sync(il, &cmd);
+ if (ret)
+ return ret;
+
+ pkt = (struct il_rx_pkt *)cmd.reply_page;
+ if (pkt->u.status != CAN_ABORT_STATUS) {
+ /* The scan abort will return 1 for success or
+ * 2 for "failure". A failure condition can be
+ * due to simply not being in an active scan which
+ * can occur if we send the scan abort before we
+ * the microcode has notified us that a scan is
+ * completed. */
+ D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status);
+ ret = -EIO;
+ }
+
+ il_free_pages(il, cmd.reply_page);
+ return ret;
+}
+
+static void
+il_complete_scan(struct il_priv *il, bool aborted)
+{
+ /* check if scan was requested from mac80211 */
+ if (il->scan_request) {
+ D_SCAN("Complete scan in mac80211\n");
+ ieee80211_scan_completed(il->hw, aborted);
+ }
+
+ il->scan_vif = NULL;
+ il->scan_request = NULL;
+}
+
+void
+il_force_scan_end(struct il_priv *il)
+{
+ lockdep_assert_held(&il->mutex);
+
+ if (!test_bit(S_SCANNING, &il->status)) {
+ D_SCAN("Forcing scan end while not scanning\n");
+ return;
+ }
+
+ D_SCAN("Forcing scan end\n");
+ clear_bit(S_SCANNING, &il->status);
+ clear_bit(S_SCAN_HW, &il->status);
+ clear_bit(S_SCAN_ABORTING, &il->status);
+ il_complete_scan(il, true);
+}
+
+static void
+il_do_scan_abort(struct il_priv *il)
+{
+ int ret;
+
+ lockdep_assert_held(&il->mutex);
+
+ if (!test_bit(S_SCANNING, &il->status)) {
+ D_SCAN("Not performing scan to abort\n");
+ return;
+ }
+
+ if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) {
+ D_SCAN("Scan abort in progress\n");
+ return;
+ }
+
+ ret = il_send_scan_abort(il);
+ if (ret) {
+ D_SCAN("Send scan abort failed %d\n", ret);
+ il_force_scan_end(il);
+ } else
+ D_SCAN("Successfully send scan abort\n");
+}
+
+/**
+ * il_scan_cancel - Cancel any currently executing HW scan
+ */
+int
+il_scan_cancel(struct il_priv *il)
+{
+ D_SCAN("Queuing abort scan\n");
+ queue_work(il->workqueue, &il->abort_scan);
+ return 0;
+}
+EXPORT_SYMBOL(il_scan_cancel);
+
+/**
+ * il_scan_cancel_timeout - Cancel any currently executing HW scan
+ * @ms: amount of time to wait (in milliseconds) for scan to abort
+ *
+ */
+int
+il_scan_cancel_timeout(struct il_priv *il, unsigned long ms)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(ms);
+
+ lockdep_assert_held(&il->mutex);
+
+ D_SCAN("Scan cancel timeout\n");
+
+ il_do_scan_abort(il);
+
+ while (time_before_eq(jiffies, timeout)) {
+ if (!test_bit(S_SCAN_HW, &il->status))
+ break;
+ msleep(20);
+ }
+
+ return test_bit(S_SCAN_HW, &il->status);
+}
+EXPORT_SYMBOL(il_scan_cancel_timeout);
+
+/* Service response to C_SCAN (0x80) */
+static void
+il_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb)
+{
+#ifdef CONFIG_IWLEGACY_DEBUG
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_scanreq_notification *notif =
+ (struct il_scanreq_notification *)pkt->u.raw;
+
+ D_SCAN("Scan request status = 0x%x\n", notif->status);
+#endif
+}
+
+/* Service N_SCAN_START (0x82) */
+static void
+il_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_scanstart_notification *notif =
+ (struct il_scanstart_notification *)pkt->u.raw;
+ il->scan_start_tsf = le32_to_cpu(notif->tsf_low);
+ D_SCAN("Scan start: " "%d [802.11%s] "
+ "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel,
+ notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high),
+ le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer);
+}
+
+/* Service N_SCAN_RESULTS (0x83) */
+static void
+il_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb)
+{
+#ifdef CONFIG_IWLEGACY_DEBUG
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_scanresults_notification *notif =
+ (struct il_scanresults_notification *)pkt->u.raw;
+
+ D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d "
+ "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a",
+ le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low),
+ le32_to_cpu(notif->stats[0]),
+ le32_to_cpu(notif->tsf_low) - il->scan_start_tsf);
+#endif
+}
+
+/* Service N_SCAN_COMPLETE (0x84) */
+static void
+il_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb)
+{
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
+#endif
+
+ D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
+ scan_notif->scanned_channels, scan_notif->tsf_low,
+ scan_notif->tsf_high, scan_notif->status);
+
+ /* The HW is no longer scanning */
+ clear_bit(S_SCAN_HW, &il->status);
+
+ D_SCAN("Scan on %sGHz took %dms\n",
+ (il->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
+ jiffies_to_msecs(jiffies - il->scan_start));
+
+ queue_work(il->workqueue, &il->scan_completed);
+}
+
+void
+il_setup_rx_scan_handlers(struct il_priv *il)
+{
+ /* scan handlers */
+ il->handlers[C_SCAN] = il_hdl_scan;
+ il->handlers[N_SCAN_START] = il_hdl_scan_start;
+ il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results;
+ il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete;
+}
+EXPORT_SYMBOL(il_setup_rx_scan_handlers);
+
+u16
+il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band,
+ u8 n_probes)
+{
+ if (band == IEEE80211_BAND_5GHZ)
+ return IL_ACTIVE_DWELL_TIME_52 +
+ IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1);
+ else
+ return IL_ACTIVE_DWELL_TIME_24 +
+ IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1);
+}
+EXPORT_SYMBOL(il_get_active_dwell_time);
+
+u16
+il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band,
+ struct ieee80211_vif *vif)
+{
+ u16 value;
+
+ u16 passive =
+ (band ==
+ IEEE80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE +
+ IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE +
+ IL_PASSIVE_DWELL_TIME_52;
+
+ if (il_is_any_associated(il)) {
+ /*
+ * If we're associated, we clamp the maximum passive
+ * dwell time to be 98% of the smallest beacon interval
+ * (minus 2 * channel tune time)
+ */
+ value = il->vif ? il->vif->bss_conf.beacon_int : 0;
+ if (value > IL_PASSIVE_DWELL_BASE || !value)
+ value = IL_PASSIVE_DWELL_BASE;
+ value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2;
+ passive = min(value, passive);
+ }
+
+ return passive;
+}
+EXPORT_SYMBOL(il_get_passive_dwell_time);
+
+void
+il_init_scan_params(struct il_priv *il)
+{
+ u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1;
+ if (!il->scan_tx_ant[IEEE80211_BAND_5GHZ])
+ il->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx;
+ if (!il->scan_tx_ant[IEEE80211_BAND_2GHZ])
+ il->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
+}
+EXPORT_SYMBOL(il_init_scan_params);
+
+static int
+il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
+{
+ int ret;
+
+ lockdep_assert_held(&il->mutex);
+
+ cancel_delayed_work(&il->scan_check);
+
+ if (!il_is_ready_rf(il)) {
+ IL_WARN("Request scan called when driver not ready.\n");
+ return -EIO;
+ }
+
+ if (test_bit(S_SCAN_HW, &il->status)) {
+ D_SCAN("Multiple concurrent scan requests in parallel.\n");
+ return -EBUSY;
+ }
+
+ if (test_bit(S_SCAN_ABORTING, &il->status)) {
+ D_SCAN("Scan request while abort pending.\n");
+ return -EBUSY;
+ }
+
+ D_SCAN("Starting scan...\n");
+
+ set_bit(S_SCANNING, &il->status);
+ il->scan_start = jiffies;
+
+ ret = il->ops->request_scan(il, vif);
+ if (ret) {
+ clear_bit(S_SCANNING, &il->status);
+ return ret;
+ }
+
+ queue_delayed_work(il->workqueue, &il->scan_check,
+ IL_SCAN_CHECK_WATCHDOG);
+
+ return 0;
+}
+
+int
+il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+{
+ struct cfg80211_scan_request *req = &hw_req->req;
+ struct il_priv *il = hw->priv;
+ int ret;
+
+ if (req->n_channels == 0) {
+ IL_ERR("Can not scan on no channels.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter\n");
+
+ if (test_bit(S_SCANNING, &il->status)) {
+ D_SCAN("Scan already in progress.\n");
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
+
+ /* mac80211 will only ask for one band at a time */
+ il->scan_request = req;
+ il->scan_vif = vif;
+ il->scan_band = req->channels[0]->band;
+
+ ret = il_scan_initiate(il, vif);
+
+out_unlock:
+ D_MAC80211("leave ret %d\n", ret);
+ mutex_unlock(&il->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(il_mac_hw_scan);
+
+static void
+il_bg_scan_check(struct work_struct *data)
+{
+ struct il_priv *il =
+ container_of(data, struct il_priv, scan_check.work);
+
+ D_SCAN("Scan check work\n");
+
+ /* Since we are here firmware does not finish scan and
+ * most likely is in bad shape, so we don't bother to
+ * send abort command, just force scan complete to mac80211 */
+ mutex_lock(&il->mutex);
+ il_force_scan_end(il);
+ mutex_unlock(&il->mutex);
+}
+
+/**
+ * il_fill_probe_req - fill in all required fields and IE for probe request
+ */
+
+u16
+il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
+ const u8 *ta, const u8 *ies, int ie_len, int left)
+{
+ int len = 0;
+ u8 *pos = NULL;
+
+ /* Make sure there is enough space for the probe request,
+ * two mandatory IEs and the data */
+ left -= 24;
+ if (left < 0)
+ return 0;
+
+ frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+ eth_broadcast_addr(frame->da);
+ memcpy(frame->sa, ta, ETH_ALEN);
+ eth_broadcast_addr(frame->bssid);
+ frame->seq_ctrl = 0;
+
+ len += 24;
+
+ /* ...next IE... */
+ pos = &frame->u.probe_req.variable[0];
+
+ /* fill in our indirect SSID IE */
+ left -= 2;
+ if (left < 0)
+ return 0;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = 0;
+
+ len += 2;
+
+ if (WARN_ON(left < ie_len))
+ return len;
+
+ if (ies && ie_len) {
+ memcpy(pos, ies, ie_len);
+ len += ie_len;
+ }
+
+ return (u16) len;
+}
+EXPORT_SYMBOL(il_fill_probe_req);
+
+static void
+il_bg_abort_scan(struct work_struct *work)
+{
+ struct il_priv *il = container_of(work, struct il_priv, abort_scan);
+
+ D_SCAN("Abort scan work\n");
+
+ /* We keep scan_check work queued in case when firmware will not
+ * report back scan completed notification */
+ mutex_lock(&il->mutex);
+ il_scan_cancel_timeout(il, 200);
+ mutex_unlock(&il->mutex);
+}
+
+static void
+il_bg_scan_completed(struct work_struct *work)
+{
+ struct il_priv *il = container_of(work, struct il_priv, scan_completed);
+ bool aborted;
+
+ D_SCAN("Completed scan.\n");
+
+ cancel_delayed_work(&il->scan_check);
+
+ mutex_lock(&il->mutex);
+
+ aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status);
+ if (aborted)
+ D_SCAN("Aborted scan completed.\n");
+
+ if (!test_and_clear_bit(S_SCANNING, &il->status)) {
+ D_SCAN("Scan already completed.\n");
+ goto out_settings;
+ }
+
+ il_complete_scan(il, aborted);
+
+out_settings:
+ /* Can we still talk to firmware ? */
+ if (!il_is_ready_rf(il))
+ goto out;
+
+ /*
+ * We do not commit power settings while scan is pending,
+ * do it now if the settings changed.
+ */
+ il_power_set_mode(il, &il->power_data.sleep_cmd_next, false);
+ il_set_tx_power(il, il->tx_power_next, false);
+
+ il->ops->post_scan(il);
+
+out:
+ mutex_unlock(&il->mutex);
+}
+
+void
+il_setup_scan_deferred_work(struct il_priv *il)
+{
+ INIT_WORK(&il->scan_completed, il_bg_scan_completed);
+ INIT_WORK(&il->abort_scan, il_bg_abort_scan);
+ INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check);
+}
+EXPORT_SYMBOL(il_setup_scan_deferred_work);
+
+void
+il_cancel_scan_deferred_work(struct il_priv *il)
+{
+ cancel_work_sync(&il->abort_scan);
+ cancel_work_sync(&il->scan_completed);
+
+ if (cancel_delayed_work_sync(&il->scan_check)) {
+ mutex_lock(&il->mutex);
+ il_force_scan_end(il);
+ mutex_unlock(&il->mutex);
+ }
+}
+EXPORT_SYMBOL(il_cancel_scan_deferred_work);
+
+/* il->sta_lock must be held */
+static void
+il_sta_ucode_activate(struct il_priv *il, u8 sta_id)
+{
+
+ if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE))
+ IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n",
+ sta_id, il->stations[sta_id].sta.sta.addr);
+
+ if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) {
+ D_ASSOC("STA id %u addr %pM already present"
+ " in uCode (according to driver)\n", sta_id,
+ il->stations[sta_id].sta.sta.addr);
+ } else {
+ il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE;
+ D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id,
+ il->stations[sta_id].sta.sta.addr);
+ }
+}
+
+static int
+il_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta,
+ struct il_rx_pkt *pkt, bool sync)
+{
+ u8 sta_id = addsta->sta.sta_id;
+ unsigned long flags;
+ int ret = -EIO;
+
+ if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
+ IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags);
+ return ret;
+ }
+
+ D_INFO("Processing response for adding station %u\n", sta_id);
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+
+ switch (pkt->u.add_sta.status) {
+ case ADD_STA_SUCCESS_MSK:
+ D_INFO("C_ADD_STA PASSED\n");
+ il_sta_ucode_activate(il, sta_id);
+ ret = 0;
+ break;
+ case ADD_STA_NO_ROOM_IN_TBL:
+ IL_ERR("Adding station %d failed, no room in table.\n", sta_id);
+ break;
+ case ADD_STA_NO_BLOCK_ACK_RESOURCE:
+ IL_ERR("Adding station %d failed, no block ack resource.\n",
+ sta_id);
+ break;
+ case ADD_STA_MODIFY_NON_EXIST_STA:
+ IL_ERR("Attempting to modify non-existing station %d\n",
+ sta_id);
+ break;
+ default:
+ D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status);
+ break;
+ }
+
+ D_INFO("%s station id %u addr %pM\n",
+ il->stations[sta_id].sta.mode ==
+ STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id,
+ il->stations[sta_id].sta.sta.addr);
+
+ /*
+ * XXX: The MAC address in the command buffer is often changed from
+ * the original sent to the device. That is, the MAC address
+ * written to the command buffer often is not the same MAC address
+ * read from the command buffer when the command returns. This
+ * issue has not yet been resolved and this debugging is left to
+ * observe the problem.
+ */
+ D_INFO("%s station according to cmd buffer %pM\n",
+ il->stations[sta_id].sta.mode ==
+ STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr);
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return ret;
+}
+
+static void
+il_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd,
+ struct il_rx_pkt *pkt)
+{
+ struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload;
+
+ il_process_add_sta_resp(il, addsta, pkt, false);
+
+}
+
+int
+il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags)
+{
+ struct il_rx_pkt *pkt = NULL;
+ int ret = 0;
+ u8 data[sizeof(*sta)];
+ struct il_host_cmd cmd = {
+ .id = C_ADD_STA,
+ .flags = flags,
+ .data = data,
+ };
+ u8 sta_id __maybe_unused = sta->sta.sta_id;
+
+ D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr,
+ flags & CMD_ASYNC ? "a" : "");
+
+ if (flags & CMD_ASYNC)
+ cmd.callback = il_add_sta_callback;
+ else {
+ cmd.flags |= CMD_WANT_SKB;
+ might_sleep();
+ }
+
+ cmd.len = il->ops->build_addsta_hcmd(sta, data);
+ ret = il_send_cmd(il, &cmd);
+ if (ret)
+ return ret;
+ if (flags & CMD_ASYNC)
+ return 0;
+
+ pkt = (struct il_rx_pkt *)cmd.reply_page;
+ ret = il_process_add_sta_resp(il, sta, pkt, true);
+
+ il_free_pages(il, cmd.reply_page);
+
+ return ret;
+}
+EXPORT_SYMBOL(il_send_add_sta);
+
+static void
+il_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta)
+{
+ struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap;
+ __le32 sta_flags;
+
+ if (!sta || !sta_ht_inf->ht_supported)
+ goto done;
+
+ D_ASSOC("spatial multiplexing power save mode: %s\n",
+ (sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" :
+ (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" :
+ "disabled");
+
+ sta_flags = il->stations[idx].sta.station_flags;
+
+ sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
+
+ switch (sta->smps_mode) {
+ case IEEE80211_SMPS_STATIC:
+ sta_flags |= STA_FLG_MIMO_DIS_MSK;
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
+ break;
+ case IEEE80211_SMPS_OFF:
+ break;
+ default:
+ IL_WARN("Invalid MIMO PS mode %d\n", sta->smps_mode);
+ break;
+ }
+
+ sta_flags |=
+ cpu_to_le32((u32) sta_ht_inf->
+ ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
+
+ sta_flags |=
+ cpu_to_le32((u32) sta_ht_inf->
+ ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
+
+ if (il_is_ht40_tx_allowed(il, &sta->ht_cap))
+ sta_flags |= STA_FLG_HT40_EN_MSK;
+ else
+ sta_flags &= ~STA_FLG_HT40_EN_MSK;
+
+ il->stations[idx].sta.station_flags = sta_flags;
+done:
+ return;
+}
+
+/**
+ * il_prep_station - Prepare station information for addition
+ *
+ * should be called with sta_lock held
+ */
+u8
+il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap,
+ struct ieee80211_sta *sta)
+{
+ struct il_station_entry *station;
+ int i;
+ u8 sta_id = IL_INVALID_STATION;
+ u16 rate;
+
+ if (is_ap)
+ sta_id = IL_AP_ID;
+ else if (is_broadcast_ether_addr(addr))
+ sta_id = il->hw_params.bcast_id;
+ else
+ for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) {
+ if (ether_addr_equal(il->stations[i].sta.sta.addr,
+ addr)) {
+ sta_id = i;
+ break;
+ }
+
+ if (!il->stations[i].used &&
+ sta_id == IL_INVALID_STATION)
+ sta_id = i;
+ }
+
+ /*
+ * These two conditions have the same outcome, but keep them
+ * separate
+ */
+ if (unlikely(sta_id == IL_INVALID_STATION))
+ return sta_id;
+
+ /*
+ * uCode is not able to deal with multiple requests to add a
+ * station. Keep track if one is in progress so that we do not send
+ * another.
+ */
+ if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
+ D_INFO("STA %d already in process of being added.\n", sta_id);
+ return sta_id;
+ }
+
+ if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
+ (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) &&
+ ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) {
+ D_ASSOC("STA %d (%pM) already added, not adding again.\n",
+ sta_id, addr);
+ return sta_id;
+ }
+
+ station = &il->stations[sta_id];
+ station->used = IL_STA_DRIVER_ACTIVE;
+ D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr);
+ il->num_stations++;
+
+ /* Set up the C_ADD_STA command to send to device */
+ memset(&station->sta, 0, sizeof(struct il_addsta_cmd));
+ memcpy(station->sta.sta.addr, addr, ETH_ALEN);
+ station->sta.mode = 0;
+ station->sta.sta.sta_id = sta_id;
+ station->sta.station_flags = 0;
+
+ /*
+ * OK to call unconditionally, since local stations (IBSS BSSID
+ * STA and broadcast STA) pass in a NULL sta, and mac80211
+ * doesn't allow HT IBSS.
+ */
+ il_set_ht_add_station(il, sta_id, sta);
+
+ /* 3945 only */
+ rate = (il->band == IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP;
+ /* Turn on both antennas for the station... */
+ station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
+
+ return sta_id;
+
+}
+EXPORT_SYMBOL_GPL(il_prep_station);
+
+#define STA_WAIT_TIMEOUT (HZ/2)
+
+/**
+ * il_add_station_common -
+ */
+int
+il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap,
+ struct ieee80211_sta *sta, u8 *sta_id_r)
+{
+ unsigned long flags_spin;
+ int ret = 0;
+ u8 sta_id;
+ struct il_addsta_cmd sta_cmd;
+
+ *sta_id_r = 0;
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ sta_id = il_prep_station(il, addr, is_ap, sta);
+ if (sta_id == IL_INVALID_STATION) {
+ IL_ERR("Unable to prepare station %pM for addition\n", addr);
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ return -EINVAL;
+ }
+
+ /*
+ * uCode is not able to deal with multiple requests to add a
+ * station. Keep track if one is in progress so that we do not send
+ * another.
+ */
+ if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
+ D_INFO("STA %d already in process of being added.\n", sta_id);
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ return -EEXIST;
+ }
+
+ if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
+ (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
+ D_ASSOC("STA %d (%pM) already added, not adding again.\n",
+ sta_id, addr);
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ return -EEXIST;
+ }
+
+ il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS;
+ memcpy(&sta_cmd, &il->stations[sta_id].sta,
+ sizeof(struct il_addsta_cmd));
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+
+ /* Add station to device's station table */
+ ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+ if (ret) {
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ IL_ERR("Adding station %pM failed.\n",
+ il->stations[sta_id].sta.sta.addr);
+ il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
+ il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ }
+ *sta_id_r = sta_id;
+ return ret;
+}
+EXPORT_SYMBOL(il_add_station_common);
+
+/**
+ * il_sta_ucode_deactivate - deactivate ucode status for a station
+ *
+ * il->sta_lock must be held
+ */
+static void
+il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id)
+{
+ /* Ucode must be active and driver must be non active */
+ if ((il->stations[sta_id].
+ used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) !=
+ IL_STA_UCODE_ACTIVE)
+ IL_ERR("removed non active STA %u\n", sta_id);
+
+ il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE;
+
+ memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry));
+ D_ASSOC("Removed STA %u\n", sta_id);
+}
+
+static int
+il_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id,
+ bool temporary)
+{
+ struct il_rx_pkt *pkt;
+ int ret;
+
+ unsigned long flags_spin;
+ struct il_rem_sta_cmd rm_sta_cmd;
+
+ struct il_host_cmd cmd = {
+ .id = C_REM_STA,
+ .len = sizeof(struct il_rem_sta_cmd),
+ .flags = CMD_SYNC,
+ .data = &rm_sta_cmd,
+ };
+
+ memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
+ rm_sta_cmd.num_sta = 1;
+ memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
+
+ cmd.flags |= CMD_WANT_SKB;
+
+ ret = il_send_cmd(il, &cmd);
+
+ if (ret)
+ return ret;
+
+ pkt = (struct il_rx_pkt *)cmd.reply_page;
+ if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
+ IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags);
+ ret = -EIO;
+ }
+
+ if (!ret) {
+ switch (pkt->u.rem_sta.status) {
+ case REM_STA_SUCCESS_MSK:
+ if (!temporary) {
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ il_sta_ucode_deactivate(il, sta_id);
+ spin_unlock_irqrestore(&il->sta_lock,
+ flags_spin);
+ }
+ D_ASSOC("C_REM_STA PASSED\n");
+ break;
+ default:
+ ret = -EIO;
+ IL_ERR("C_REM_STA failed\n");
+ break;
+ }
+ }
+ il_free_pages(il, cmd.reply_page);
+
+ return ret;
+}
+
+/**
+ * il_remove_station - Remove driver's knowledge of station.
+ */
+int
+il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr)
+{
+ unsigned long flags;
+
+ if (!il_is_ready(il)) {
+ D_INFO("Unable to remove station %pM, device not ready.\n",
+ addr);
+ /*
+ * It is typical for stations to be removed when we are
+ * going down. Return success since device will be down
+ * soon anyway
+ */
+ return 0;
+ }
+
+ D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr);
+
+ if (WARN_ON(sta_id == IL_INVALID_STATION))
+ return -EINVAL;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+
+ if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) {
+ D_INFO("Removing %pM but non DRIVER active\n", addr);
+ goto out_err;
+ }
+
+ if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
+ D_INFO("Removing %pM but non UCODE active\n", addr);
+ goto out_err;
+ }
+
+ if (il->stations[sta_id].used & IL_STA_LOCAL) {
+ kfree(il->stations[sta_id].lq);
+ il->stations[sta_id].lq = NULL;
+ }
+
+ il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
+
+ il->num_stations--;
+
+ BUG_ON(il->num_stations < 0);
+
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+
+ return il_send_remove_station(il, addr, sta_id, false);
+out_err:
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(il_remove_station);
+
+/**
+ * il_clear_ucode_stations - clear ucode station table bits
+ *
+ * This function clears all the bits in the driver indicating
+ * which stations are active in the ucode. Call when something
+ * other than explicit station management would cause this in
+ * the ucode, e.g. unassociated RXON.
+ */
+void
+il_clear_ucode_stations(struct il_priv *il)
+{
+ int i;
+ unsigned long flags_spin;
+ bool cleared = false;
+
+ D_INFO("Clearing ucode stations in driver\n");
+
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ for (i = 0; i < il->hw_params.max_stations; i++) {
+ if (il->stations[i].used & IL_STA_UCODE_ACTIVE) {
+ D_INFO("Clearing ucode active for station %d\n", i);
+ il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
+ cleared = true;
+ }
+ }
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+
+ if (!cleared)
+ D_INFO("No active stations found to be cleared\n");
+}
+EXPORT_SYMBOL(il_clear_ucode_stations);
+
+/**
+ * il_restore_stations() - Restore driver known stations to device
+ *
+ * All stations considered active by driver, but not present in ucode, is
+ * restored.
+ *
+ * Function sleeps.
+ */
+void
+il_restore_stations(struct il_priv *il)
+{
+ struct il_addsta_cmd sta_cmd;
+ struct il_link_quality_cmd lq;
+ unsigned long flags_spin;
+ int i;
+ bool found = false;
+ int ret;
+ bool send_lq;
+
+ if (!il_is_ready(il)) {
+ D_INFO("Not ready yet, not restoring any stations.\n");
+ return;
+ }
+
+ D_ASSOC("Restoring all known stations ... start.\n");
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ for (i = 0; i < il->hw_params.max_stations; i++) {
+ if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) &&
+ !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) {
+ D_ASSOC("Restoring sta %pM\n",
+ il->stations[i].sta.sta.addr);
+ il->stations[i].sta.mode = 0;
+ il->stations[i].used |= IL_STA_UCODE_INPROGRESS;
+ found = true;
+ }
+ }
+
+ for (i = 0; i < il->hw_params.max_stations; i++) {
+ if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) {
+ memcpy(&sta_cmd, &il->stations[i].sta,
+ sizeof(struct il_addsta_cmd));
+ send_lq = false;
+ if (il->stations[i].lq) {
+ memcpy(&lq, il->stations[i].lq,
+ sizeof(struct il_link_quality_cmd));
+ send_lq = true;
+ }
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
+ if (ret) {
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ IL_ERR("Adding station %pM failed.\n",
+ il->stations[i].sta.sta.addr);
+ il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE;
+ il->stations[i].used &=
+ ~IL_STA_UCODE_INPROGRESS;
+ spin_unlock_irqrestore(&il->sta_lock,
+ flags_spin);
+ }
+ /*
+ * Rate scaling has already been initialized, send
+ * current LQ command
+ */
+ if (send_lq)
+ il_send_lq_cmd(il, &lq, CMD_SYNC, true);
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS;
+ }
+ }
+
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ if (!found)
+ D_INFO("Restoring all known stations"
+ " .... no stations to be restored.\n");
+ else
+ D_INFO("Restoring all known stations" " .... complete.\n");
+}
+EXPORT_SYMBOL(il_restore_stations);
+
+int
+il_get_free_ucode_key_idx(struct il_priv *il)
+{
+ int i;
+
+ for (i = 0; i < il->sta_key_max_num; i++)
+ if (!test_and_set_bit(i, &il->ucode_key_table))
+ return i;
+
+ return WEP_INVALID_OFFSET;
+}
+EXPORT_SYMBOL(il_get_free_ucode_key_idx);
+
+void
+il_dealloc_bcast_stations(struct il_priv *il)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&il->sta_lock, flags);
+ for (i = 0; i < il->hw_params.max_stations; i++) {
+ if (!(il->stations[i].used & IL_STA_BCAST))
+ continue;
+
+ il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
+ il->num_stations--;
+ BUG_ON(il->num_stations < 0);
+ kfree(il->stations[i].lq);
+ il->stations[i].lq = NULL;
+ }
+ spin_unlock_irqrestore(&il->sta_lock, flags);
+}
+EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations);
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+static void
+il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
+{
+ int i;
+ D_RATE("lq station id 0x%x\n", lq->sta_id);
+ D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk,
+ lq->general_params.dual_stream_ant_msk);
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
+ D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags);
+}
+#else
+static inline void
+il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
+{
+}
+#endif
+
+/**
+ * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity
+ *
+ * It sometimes happens when a HT rate has been in use and we
+ * loose connectivity with AP then mac80211 will first tell us that the
+ * current channel is not HT anymore before removing the station. In such a
+ * scenario the RXON flags will be updated to indicate we are not
+ * communicating HT anymore, but the LQ command may still contain HT rates.
+ * Test for this to prevent driver from sending LQ command between the time
+ * RXON flags are updated and when LQ command is updated.
+ */
+static bool
+il_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq)
+{
+ int i;
+
+ if (il->ht.enabled)
+ return true;
+
+ D_INFO("Channel %u is not an HT channel\n", il->active.channel);
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) {
+ D_INFO("idx %d of LQ expects HT channel\n", i);
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * il_send_lq_cmd() - Send link quality command
+ * @init: This command is sent as part of station initialization right
+ * after station has been added.
+ *
+ * The link quality command is sent as the last step of station creation.
+ * This is the special case in which init is set and we call a callback in
+ * this case to clear the state indicating that station creation is in
+ * progress.
+ */
+int
+il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq,
+ u8 flags, bool init)
+{
+ int ret = 0;
+ unsigned long flags_spin;
+
+ struct il_host_cmd cmd = {
+ .id = C_TX_LINK_QUALITY_CMD,
+ .len = sizeof(struct il_link_quality_cmd),
+ .flags = flags,
+ .data = lq,
+ };
+
+ if (WARN_ON(lq->sta_id == IL_INVALID_STATION))
+ return -EINVAL;
+
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) {
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+
+ il_dump_lq_cmd(il, lq);
+ BUG_ON(init && (cmd.flags & CMD_ASYNC));
+
+ if (il_is_lq_table_valid(il, lq))
+ ret = il_send_cmd(il, &cmd);
+ else
+ ret = -EINVAL;
+
+ if (cmd.flags & CMD_ASYNC)
+ return ret;
+
+ if (init) {
+ D_INFO("init LQ command complete,"
+ " clearing sta addition status for sta %d\n",
+ lq->sta_id);
+ spin_lock_irqsave(&il->sta_lock, flags_spin);
+ il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
+ spin_unlock_irqrestore(&il->sta_lock, flags_spin);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(il_send_lq_cmd);
+
+int
+il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct il_priv *il = hw->priv;
+ struct il_station_priv_common *sta_common = (void *)sta->drv_priv;
+ int ret;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter station %pM\n", sta->addr);
+
+ ret = il_remove_station(il, sta_common->sta_id, sta->addr);
+ if (ret)
+ IL_ERR("Error removing station %pM\n", sta->addr);
+
+ D_MAC80211("leave ret %d\n", ret);
+ mutex_unlock(&il->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(il_mac_sta_remove);
+
+/************************** RX-FUNCTIONS ****************************/
+/*
+ * Rx theory of operation
+ *
+ * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
+ * each of which point to Receive Buffers to be filled by the NIC. These get
+ * used not only for Rx frames, but for any command response or notification
+ * from the NIC. The driver and NIC manage the Rx buffers by means
+ * of idxes into the circular buffer.
+ *
+ * Rx Queue Indexes
+ * The host/firmware share two idx registers for managing the Rx buffers.
+ *
+ * The READ idx maps to the first position that the firmware may be writing
+ * to -- the driver can read up to (but not including) this position and get
+ * good data.
+ * The READ idx is managed by the firmware once the card is enabled.
+ *
+ * The WRITE idx maps to the last position the driver has read from -- the
+ * position preceding WRITE is the last slot the firmware can place a packet.
+ *
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
+ * WRITE = READ.
+ *
+ * During initialization, the host sets up the READ queue position to the first
+ * IDX position, and WRITE to the last (READ - 1 wrapped)
+ *
+ * When the firmware places a packet in a buffer, it will advance the READ idx
+ * and fire the RX interrupt. The driver can then query the READ idx and
+ * process as many packets as possible, moving the WRITE idx forward as it
+ * resets the Rx queue buffers with new memory.
+ *
+ * The management in the driver is as follows:
+ * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When
+ * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
+ * to replenish the iwl->rxq->rx_free.
+ * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the
+ * iwl->rxq is replenished and the READ IDX is updated (updating the
+ * 'processed' and 'read' driver idxes as well)
+ * + A received packet is processed and handed to the kernel network stack,
+ * detached from the iwl->rxq. The driver 'processed' idx is updated.
+ * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free
+ * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ
+ * IDX is not incremented and iwl->status(RX_STALLED) is set. If there
+ * were enough free buffers and RX_STALLED is set it is cleared.
+ *
+ *
+ * Driver sequence:
+ *
+ * il_rx_queue_alloc() Allocates rx_free
+ * il_rx_replenish() Replenishes rx_free list from rx_used, and calls
+ * il_rx_queue_restock
+ * il_rx_queue_restock() Moves available buffers from rx_free into Rx
+ * queue, updates firmware pointers, and updates
+ * the WRITE idx. If insufficient rx_free buffers
+ * are available, schedules il_rx_replenish
+ *
+ * -- enable interrupts --
+ * ISR - il_rx() Detach il_rx_bufs from pool up to the
+ * READ IDX, detaching the SKB from the pool.
+ * Moves the packet buffer from queue to rx_used.
+ * Calls il_rx_queue_restock to refill any empty
+ * slots.
+ * ...
+ *
+ */
+
+/**
+ * il_rx_queue_space - Return number of free slots available in queue.
+ */
+int
+il_rx_queue_space(const struct il_rx_queue *q)
+{
+ int s = q->read - q->write;
+ if (s <= 0)
+ s += RX_QUEUE_SIZE;
+ /* keep some buffer to not confuse full and empty queue */
+ s -= 2;
+ if (s < 0)
+ s = 0;
+ return s;
+}
+EXPORT_SYMBOL(il_rx_queue_space);
+
+/**
+ * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue
+ */
+void
+il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q)
+{
+ unsigned long flags;
+ u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg;
+ u32 reg;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ if (q->need_update == 0)
+ goto exit_unlock;
+
+ /* If power-saving is in use, make sure device is awake */
+ if (test_bit(S_POWER_PMI, &il->status)) {
+ reg = _il_rd(il, CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n",
+ reg);
+ il_set_bit(il, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ goto exit_unlock;
+ }
+
+ q->write_actual = (q->write & ~0x7);
+ il_wr(il, rx_wrt_ptr_reg, q->write_actual);
+
+ /* Else device is assumed to be awake */
+ } else {
+ /* Device expects a multiple of 8 */
+ q->write_actual = (q->write & ~0x7);
+ il_wr(il, rx_wrt_ptr_reg, q->write_actual);
+ }
+
+ q->need_update = 0;
+
+exit_unlock:
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+EXPORT_SYMBOL(il_rx_queue_update_write_ptr);
+
+int
+il_rx_queue_alloc(struct il_priv *il)
+{
+ struct il_rx_queue *rxq = &il->rxq;
+ struct device *dev = &il->pci_dev->dev;
+ int i;
+
+ spin_lock_init(&rxq->lock);
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+
+ /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */
+ rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma,
+ GFP_KERNEL);
+ if (!rxq->bd)
+ goto err_bd;
+
+ rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status),
+ &rxq->rb_stts_dma, GFP_KERNEL);
+ if (!rxq->rb_stts)
+ goto err_rb;
+
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->write_actual = 0;
+ rxq->free_count = 0;
+ rxq->need_update = 0;
+ return 0;
+
+err_rb:
+ dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
+ rxq->bd_dma);
+err_bd:
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(il_rx_queue_alloc);
+
+void
+il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_spectrum_notification *report = &(pkt->u.spectrum_notif);
+
+ if (!report->state) {
+ D_11H("Spectrum Measure Notification: Start\n");
+ return;
+ }
+
+ memcpy(&il->measure_report, report, sizeof(*report));
+ il->measurement_status |= MEASUREMENT_READY;
+}
+EXPORT_SYMBOL(il_hdl_spectrum_measurement);
+
+/*
+ * returns non-zero if packet should be dropped
+ */
+int
+il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
+ u32 decrypt_res, struct ieee80211_rx_status *stats)
+{
+ u16 fc = le16_to_cpu(hdr->frame_control);
+
+ /*
+ * All contexts have the same setting here due to it being
+ * a module parameter, so OK to check any context.
+ */
+ if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK)
+ return 0;
+
+ if (!(fc & IEEE80211_FCTL_PROTECTED))
+ return 0;
+
+ D_RX("decrypt_res:0x%x\n", decrypt_res);
+ switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
+ case RX_RES_STATUS_SEC_TYPE_TKIP:
+ /* The uCode has got a bad phase 1 Key, pushes the packet.
+ * Decryption will be done in SW. */
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+ RX_RES_STATUS_BAD_KEY_TTAK)
+ break;
+
+ case RX_RES_STATUS_SEC_TYPE_WEP:
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+ RX_RES_STATUS_BAD_ICV_MIC) {
+ /* bad ICV, the packet is destroyed since the
+ * decryption is inplace, drop it */
+ D_RX("Packet destroyed\n");
+ return -1;
+ }
+ case RX_RES_STATUS_SEC_TYPE_CCMP:
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+ RX_RES_STATUS_DECRYPT_OK) {
+ D_RX("hw decrypt successfully!!!\n");
+ stats->flag |= RX_FLAG_DECRYPTED;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(il_set_decrypted_flag);
+
+/**
+ * il_txq_update_write_ptr - Send new write idx to hardware
+ */
+void
+il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq)
+{
+ u32 reg = 0;
+ int txq_id = txq->q.id;
+
+ if (txq->need_update == 0)
+ return;
+
+ /* if we're trying to save power */
+ if (test_bit(S_POWER_PMI, &il->status)) {
+ /* wake up nic if it's powered down ...
+ * uCode will wake up, and interrupt us again, so next
+ * time we'll skip this part. */
+ reg = _il_rd(il, CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n",
+ txq_id, reg);
+ il_set_bit(il, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ return;
+ }
+
+ il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
+
+ /*
+ * else not in power-save mode,
+ * uCode will never sleep when we're
+ * trying to tx (during RFKILL, we're not trying to tx).
+ */
+ } else
+ _il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
+ txq->need_update = 0;
+}
+EXPORT_SYMBOL(il_txq_update_write_ptr);
+
+/**
+ * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's
+ */
+void
+il_tx_queue_unmap(struct il_priv *il, int txq_id)
+{
+ struct il_tx_queue *txq = &il->txq[txq_id];
+ struct il_queue *q = &txq->q;
+
+ if (q->n_bd == 0)
+ return;
+
+ while (q->write_ptr != q->read_ptr) {
+ il->ops->txq_free_tfd(il, txq);
+ q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd);
+ }
+}
+EXPORT_SYMBOL(il_tx_queue_unmap);
+
+/**
+ * il_tx_queue_free - Deallocate DMA queue.
+ * @txq: Transmit queue to deallocate.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ * 0-fill, but do not free "txq" descriptor structure.
+ */
+void
+il_tx_queue_free(struct il_priv *il, int txq_id)
+{
+ struct il_tx_queue *txq = &il->txq[txq_id];
+ struct device *dev = &il->pci_dev->dev;
+ int i;
+
+ il_tx_queue_unmap(il, txq_id);
+
+ /* De-alloc array of command/tx buffers */
+ for (i = 0; i < TFD_TX_CMD_SLOTS; i++)
+ kfree(txq->cmd[i]);
+
+ /* De-alloc circular buffer of TFDs */
+ if (txq->q.n_bd)
+ dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd,
+ txq->tfds, txq->q.dma_addr);
+
+ /* De-alloc array of per-TFD driver data */
+ kfree(txq->skbs);
+ txq->skbs = NULL;
+
+ /* deallocate arrays */
+ kfree(txq->cmd);
+ kfree(txq->meta);
+ txq->cmd = NULL;
+ txq->meta = NULL;
+
+ /* 0-fill queue descriptor structure */
+ memset(txq, 0, sizeof(*txq));
+}
+EXPORT_SYMBOL(il_tx_queue_free);
+
+/**
+ * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue
+ */
+void
+il_cmd_queue_unmap(struct il_priv *il)
+{
+ struct il_tx_queue *txq = &il->txq[il->cmd_queue];
+ struct il_queue *q = &txq->q;
+ int i;
+
+ if (q->n_bd == 0)
+ return;
+
+ while (q->read_ptr != q->write_ptr) {
+ i = il_get_cmd_idx(q, q->read_ptr, 0);
+
+ if (txq->meta[i].flags & CMD_MAPPED) {
+ pci_unmap_single(il->pci_dev,
+ dma_unmap_addr(&txq->meta[i], mapping),
+ dma_unmap_len(&txq->meta[i], len),
+ PCI_DMA_BIDIRECTIONAL);
+ txq->meta[i].flags = 0;
+ }
+
+ q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd);
+ }
+
+ i = q->n_win;
+ if (txq->meta[i].flags & CMD_MAPPED) {
+ pci_unmap_single(il->pci_dev,
+ dma_unmap_addr(&txq->meta[i], mapping),
+ dma_unmap_len(&txq->meta[i], len),
+ PCI_DMA_BIDIRECTIONAL);
+ txq->meta[i].flags = 0;
+ }
+}
+EXPORT_SYMBOL(il_cmd_queue_unmap);
+
+/**
+ * il_cmd_queue_free - Deallocate DMA queue.
+ * @txq: Transmit queue to deallocate.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ * 0-fill, but do not free "txq" descriptor structure.
+ */
+void
+il_cmd_queue_free(struct il_priv *il)
+{
+ struct il_tx_queue *txq = &il->txq[il->cmd_queue];
+ struct device *dev = &il->pci_dev->dev;
+ int i;
+
+ il_cmd_queue_unmap(il);
+
+ /* De-alloc array of command/tx buffers */
+ for (i = 0; i <= TFD_CMD_SLOTS; i++)
+ kfree(txq->cmd[i]);
+
+ /* De-alloc circular buffer of TFDs */
+ if (txq->q.n_bd)
+ dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd,
+ txq->tfds, txq->q.dma_addr);
+
+ /* deallocate arrays */
+ kfree(txq->cmd);
+ kfree(txq->meta);
+ txq->cmd = NULL;
+ txq->meta = NULL;
+
+ /* 0-fill queue descriptor structure */
+ memset(txq, 0, sizeof(*txq));
+}
+EXPORT_SYMBOL(il_cmd_queue_free);
+
+/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
+ * DMA services
+ *
+ * Theory of operation
+ *
+ * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer
+ * of buffer descriptors, each of which points to one or more data buffers for
+ * the device to read from or fill. Driver and device exchange status of each
+ * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty
+ * entries in each circular buffer, to protect against confusing empty and full
+ * queue states.
+ *
+ * The device reads or writes the data in the queues via the device's several
+ * DMA/FIFO channels. Each queue is mapped to a single DMA channel.
+ *
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
+ * Tx queue resumed.
+ *
+ * See more detailed info in 4965.h.
+ ***************************************************/
+
+int
+il_queue_space(const struct il_queue *q)
+{
+ int s = q->read_ptr - q->write_ptr;
+
+ if (q->read_ptr > q->write_ptr)
+ s -= q->n_bd;
+
+ if (s <= 0)
+ s += q->n_win;
+ /* keep some reserve to not confuse empty and full situations */
+ s -= 2;
+ if (s < 0)
+ s = 0;
+ return s;
+}
+EXPORT_SYMBOL(il_queue_space);
+
+
+/**
+ * il_queue_init - Initialize queue's high/low-water and read/write idxes
+ */
+static int
+il_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id)
+{
+ /*
+ * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
+ * il_queue_inc_wrap and il_queue_dec_wrap are broken.
+ */
+ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
+ /* FIXME: remove q->n_bd */
+ q->n_bd = TFD_QUEUE_SIZE_MAX;
+
+ q->n_win = slots;
+ q->id = id;
+
+ /* slots_must be power-of-two size, otherwise
+ * il_get_cmd_idx is broken. */
+ BUG_ON(!is_power_of_2(slots));
+
+ q->low_mark = q->n_win / 4;
+ if (q->low_mark < 4)
+ q->low_mark = 4;
+
+ q->high_mark = q->n_win / 8;
+ if (q->high_mark < 2)
+ q->high_mark = 2;
+
+ q->write_ptr = q->read_ptr = 0;
+
+ return 0;
+}
+
+/**
+ * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue
+ */
+static int
+il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id)
+{
+ struct device *dev = &il->pci_dev->dev;
+ size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX;
+
+ /* Driver ilate data, only for Tx (not command) queues,
+ * not shared with device. */
+ if (id != il->cmd_queue) {
+ txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX,
+ sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!txq->skbs) {
+ IL_ERR("Fail to alloc skbs\n");
+ goto error;
+ }
+ } else
+ txq->skbs = NULL;
+
+ /* Circular buffer of transmit frame descriptors (TFDs),
+ * shared with device */
+ txq->tfds =
+ dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL);
+ if (!txq->tfds)
+ goto error;
+
+ txq->q.id = id;
+
+ return 0;
+
+error:
+ kfree(txq->skbs);
+ txq->skbs = NULL;
+
+ return -ENOMEM;
+}
+
+/**
+ * il_tx_queue_init - Allocate and initialize one tx/cmd queue
+ */
+int
+il_tx_queue_init(struct il_priv *il, u32 txq_id)
+{
+ int i, len, ret;
+ int slots, actual_slots;
+ struct il_tx_queue *txq = &il->txq[txq_id];
+
+ /*
+ * Alloc buffer array for commands (Tx or other types of commands).
+ * For the command queue (#4/#9), allocate command space + one big
+ * command for scan, since scan command is very huge; the system will
+ * not have two scans at the same time, so only one is needed.
+ * For normal Tx queues (all other queues), no super-size command
+ * space is needed.
+ */
+ if (txq_id == il->cmd_queue) {
+ slots = TFD_CMD_SLOTS;
+ actual_slots = slots + 1;
+ } else {
+ slots = TFD_TX_CMD_SLOTS;
+ actual_slots = slots;
+ }
+
+ txq->meta =
+ kzalloc(sizeof(struct il_cmd_meta) * actual_slots, GFP_KERNEL);
+ txq->cmd =
+ kzalloc(sizeof(struct il_device_cmd *) * actual_slots, GFP_KERNEL);
+
+ if (!txq->meta || !txq->cmd)
+ goto out_free_arrays;
+
+ len = sizeof(struct il_device_cmd);
+ for (i = 0; i < actual_slots; i++) {
+ /* only happens for cmd queue */
+ if (i == slots)
+ len = IL_MAX_CMD_SIZE;
+
+ txq->cmd[i] = kmalloc(len, GFP_KERNEL);
+ if (!txq->cmd[i])
+ goto err;
+ }
+
+ /* Alloc driver data array and TFD circular buffer */
+ ret = il_tx_queue_alloc(il, txq, txq_id);
+ if (ret)
+ goto err;
+
+ txq->need_update = 0;
+
+ /*
+ * For the default queues 0-3, set up the swq_id
+ * already -- all others need to get one later
+ * (if they need one at all).
+ */
+ if (txq_id < 4)
+ il_set_swq_id(txq, txq_id, txq_id);
+
+ /* Initialize queue's high/low-water marks, and head/tail idxes */
+ il_queue_init(il, &txq->q, slots, txq_id);
+
+ /* Tell device where to find queue */
+ il->ops->txq_init(il, txq);
+
+ return 0;
+err:
+ for (i = 0; i < actual_slots; i++)
+ kfree(txq->cmd[i]);
+out_free_arrays:
+ kfree(txq->meta);
+ kfree(txq->cmd);
+
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(il_tx_queue_init);
+
+void
+il_tx_queue_reset(struct il_priv *il, u32 txq_id)
+{
+ int slots, actual_slots;
+ struct il_tx_queue *txq = &il->txq[txq_id];
+
+ if (txq_id == il->cmd_queue) {
+ slots = TFD_CMD_SLOTS;
+ actual_slots = TFD_CMD_SLOTS + 1;
+ } else {
+ slots = TFD_TX_CMD_SLOTS;
+ actual_slots = TFD_TX_CMD_SLOTS;
+ }
+
+ memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots);
+ txq->need_update = 0;
+
+ /* Initialize queue's high/low-water marks, and head/tail idxes */
+ il_queue_init(il, &txq->q, slots, txq_id);
+
+ /* Tell device where to find queue */
+ il->ops->txq_init(il, txq);
+}
+EXPORT_SYMBOL(il_tx_queue_reset);
+
+/*************** HOST COMMAND QUEUE FUNCTIONS *****/
+
+/**
+ * il_enqueue_hcmd - enqueue a uCode command
+ * @il: device ilate data point
+ * @cmd: a point to the ucode command structure
+ *
+ * The function returns < 0 values to indicate the operation is
+ * failed. On success, it turns the idx (> 0) of command in the
+ * command queue.
+ */
+int
+il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd)
+{
+ struct il_tx_queue *txq = &il->txq[il->cmd_queue];
+ struct il_queue *q = &txq->q;
+ struct il_device_cmd *out_cmd;
+ struct il_cmd_meta *out_meta;
+ dma_addr_t phys_addr;
+ unsigned long flags;
+ int len;
+ u32 idx;
+ u16 fix_size;
+
+ cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len);
+ fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr));
+
+ /* If any of the command structures end up being larger than
+ * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then
+ * we will need to increase the size of the TFD entries
+ * Also, check to see if command buffer should not exceed the size
+ * of device_cmd and max_cmd_size. */
+ BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) &&
+ !(cmd->flags & CMD_SIZE_HUGE));
+ BUG_ON(fix_size > IL_MAX_CMD_SIZE);
+
+ if (il_is_rfkill(il) || il_is_ctkill(il)) {
+ IL_WARN("Not sending command - %s KILL\n",
+ il_is_rfkill(il) ? "RF" : "CT");
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&il->hcmd_lock, flags);
+
+ if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
+ spin_unlock_irqrestore(&il->hcmd_lock, flags);
+
+ IL_ERR("Restarting adapter due to command queue full\n");
+ queue_work(il->workqueue, &il->restart);
+ return -ENOSPC;
+ }
+
+ idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE);
+ out_cmd = txq->cmd[idx];
+ out_meta = &txq->meta[idx];
+
+ if (WARN_ON(out_meta->flags & CMD_MAPPED)) {
+ spin_unlock_irqrestore(&il->hcmd_lock, flags);
+ return -ENOSPC;
+ }
+
+ memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */
+ out_meta->flags = cmd->flags | CMD_MAPPED;
+ if (cmd->flags & CMD_WANT_SKB)
+ out_meta->source = cmd;
+ if (cmd->flags & CMD_ASYNC)
+ out_meta->callback = cmd->callback;
+
+ out_cmd->hdr.cmd = cmd->id;
+ memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len);
+
+ /* At this point, the out_cmd now has all of the incoming cmd
+ * information */
+
+ out_cmd->hdr.flags = 0;
+ out_cmd->hdr.sequence =
+ cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr));
+ if (cmd->flags & CMD_SIZE_HUGE)
+ out_cmd->hdr.sequence |= SEQ_HUGE_FRAME;
+ len = sizeof(struct il_device_cmd);
+ if (idx == TFD_CMD_SLOTS)
+ len = IL_MAX_CMD_SIZE;
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+ switch (out_cmd->hdr.cmd) {
+ case C_TX_LINK_QUALITY_CMD:
+ case C_SENSITIVITY:
+ D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, "
+ "%d bytes at %d[%d]:%d\n",
+ il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd,
+ le16_to_cpu(out_cmd->hdr.sequence), fix_size,
+ q->write_ptr, idx, il->cmd_queue);
+ break;
+ default:
+ D_HC("Sending command %s (#%x), seq: 0x%04X, "
+ "%d bytes at %d[%d]:%d\n",
+ il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd,
+ le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr,
+ idx, il->cmd_queue);
+ }
+#endif
+
+ phys_addr =
+ pci_map_single(il->pci_dev, &out_cmd->hdr, fix_size,
+ PCI_DMA_BIDIRECTIONAL);
+ if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) {
+ idx = -ENOMEM;
+ goto out;
+ }
+ dma_unmap_addr_set(out_meta, mapping, phys_addr);
+ dma_unmap_len_set(out_meta, len, fix_size);
+
+ txq->need_update = 1;
+
+ if (il->ops->txq_update_byte_cnt_tbl)
+ /* Set up entry in queue's byte count circular buffer */
+ il->ops->txq_update_byte_cnt_tbl(il, txq, 0);
+
+ il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1,
+ U32_PAD(cmd->len));
+
+ /* Increment and update queue's write idx */
+ q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd);
+ il_txq_update_write_ptr(il, txq);
+
+out:
+ spin_unlock_irqrestore(&il->hcmd_lock, flags);
+ return idx;
+}
+
+/**
+ * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd
+ *
+ * When FW advances 'R' idx, all entries between old and new 'R' idx
+ * need to be reclaimed. As result, some free space forms. If there is
+ * enough free space (> low mark), wake the stack that feeds us.
+ */
+static void
+il_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx)
+{
+ struct il_tx_queue *txq = &il->txq[txq_id];
+ struct il_queue *q = &txq->q;
+ int nfreed = 0;
+
+ if (idx >= q->n_bd || il_queue_used(q, idx) == 0) {
+ IL_ERR("Read idx for DMA queue txq id (%d), idx %d, "
+ "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd,
+ q->write_ptr, q->read_ptr);
+ return;
+ }
+
+ for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
+ q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+
+ if (nfreed++ > 0) {
+ IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx,
+ q->write_ptr, q->read_ptr);
+ queue_work(il->workqueue, &il->restart);
+ }
+
+ }
+}
+
+/**
+ * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
+ * @rxb: Rx buffer to reclaim
+ *
+ * If an Rx buffer has an async callback associated with it the callback
+ * will be executed. The attached skb (if present) will only be freed
+ * if the callback returns 1
+ */
+void
+il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ int txq_id = SEQ_TO_QUEUE(sequence);
+ int idx = SEQ_TO_IDX(sequence);
+ int cmd_idx;
+ bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME);
+ struct il_device_cmd *cmd;
+ struct il_cmd_meta *meta;
+ struct il_tx_queue *txq = &il->txq[il->cmd_queue];
+ unsigned long flags;
+
+ /* If a Tx command is being handled and it isn't in the actual
+ * command queue then there a command routing bug has been introduced
+ * in the queue management code. */
+ if (WARN
+ (txq_id != il->cmd_queue,
+ "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
+ txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr,
+ il->txq[il->cmd_queue].q.write_ptr)) {
+ il_print_hex_error(il, pkt, 32);
+ return;
+ }
+
+ cmd_idx = il_get_cmd_idx(&txq->q, idx, huge);
+ cmd = txq->cmd[cmd_idx];
+ meta = &txq->meta[cmd_idx];
+
+ txq->time_stamp = jiffies;
+
+ pci_unmap_single(il->pci_dev, dma_unmap_addr(meta, mapping),
+ dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL);
+
+ /* Input error checking is done when commands are added to queue. */
+ if (meta->flags & CMD_WANT_SKB) {
+ meta->source->reply_page = (unsigned long)rxb_addr(rxb);
+ rxb->page = NULL;
+ } else if (meta->callback)
+ meta->callback(il, cmd, pkt);
+
+ spin_lock_irqsave(&il->hcmd_lock, flags);
+
+ il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx);
+
+ if (!(meta->flags & CMD_ASYNC)) {
+ clear_bit(S_HCMD_ACTIVE, &il->status);
+ D_INFO("Clearing HCMD_ACTIVE for command %s\n",
+ il_get_cmd_string(cmd->hdr.cmd));
+ wake_up(&il->wait_command_queue);
+ }
+
+ /* Mark as unmapped */
+ meta->flags = 0;
+
+ spin_unlock_irqrestore(&il->hcmd_lock, flags);
+}
+EXPORT_SYMBOL(il_tx_cmd_complete);
+
+MODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965");
+MODULE_VERSION(IWLWIFI_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
+/*
+ * set bt_coex_active to true, uCode will do kill/defer
+ * every time the priority line is asserted (BT is sending signals on the
+ * priority line in the PCIx).
+ * set bt_coex_active to false, uCode will ignore the BT activity and
+ * perform the normal operation
+ *
+ * User might experience transmit issue on some platform due to WiFi/BT
+ * co-exist problem. The possible behaviors are:
+ * Able to scan and finding all the available AP
+ * Not able to associate with any AP
+ * On those platforms, WiFi communication can be restored by set
+ * "bt_coex_active" module parameter to "false"
+ *
+ * default: bt_coex_active = true (BT_COEX_ENABLE)
+ */
+static bool bt_coex_active = true;
+module_param(bt_coex_active, bool, S_IRUGO);
+MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist");
+
+u32 il_debug_level;
+EXPORT_SYMBOL(il_debug_level);
+
+const u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+EXPORT_SYMBOL(il_bcast_addr);
+
+#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
+#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
+static void
+il_init_ht_hw_capab(const struct il_priv *il,
+ struct ieee80211_sta_ht_cap *ht_info,
+ enum ieee80211_band band)
+{
+ u16 max_bit_rate = 0;
+ u8 rx_chains_num = il->hw_params.rx_chains_num;
+ u8 tx_chains_num = il->hw_params.tx_chains_num;
+
+ ht_info->cap = 0;
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+
+ ht_info->ht_supported = true;
+
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+ max_bit_rate = MAX_BIT_RATE_20_MHZ;
+ if (il->hw_params.ht40_channel & BIT(band)) {
+ ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
+ ht_info->mcs.rx_mask[4] = 0x01;
+ max_bit_rate = MAX_BIT_RATE_40_MHZ;
+ }
+
+ if (il->cfg->mod_params->amsdu_size_8K)
+ ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+ ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
+ ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF;
+
+ ht_info->mcs.rx_mask[0] = 0xFF;
+ if (rx_chains_num >= 2)
+ ht_info->mcs.rx_mask[1] = 0xFF;
+ if (rx_chains_num >= 3)
+ ht_info->mcs.rx_mask[2] = 0xFF;
+
+ /* Highest supported Rx data rate */
+ max_bit_rate *= rx_chains_num;
+ WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
+ ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
+
+ /* Tx MCS capabilities */
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ if (tx_chains_num != rx_chains_num) {
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
+ ht_info->mcs.tx_params |=
+ ((tx_chains_num -
+ 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
+ }
+}
+
+/**
+ * il_init_geos - Initialize mac80211's geo/channel info based from eeprom
+ */
+int
+il_init_geos(struct il_priv *il)
+{
+ struct il_channel_info *ch;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *channels;
+ struct ieee80211_channel *geo_ch;
+ struct ieee80211_rate *rates;
+ int i = 0;
+ s8 max_tx_power = 0;
+
+ if (il->bands[IEEE80211_BAND_2GHZ].n_bitrates ||
+ il->bands[IEEE80211_BAND_5GHZ].n_bitrates) {
+ D_INFO("Geography modes already initialized.\n");
+ set_bit(S_GEO_CONFIGURED, &il->status);
+ return 0;
+ }
+
+ channels =
+ kzalloc(sizeof(struct ieee80211_channel) * il->channel_count,
+ GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ rates =
+ kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY),
+ GFP_KERNEL);
+ if (!rates) {
+ kfree(channels);
+ return -ENOMEM;
+ }
+
+ /* 5.2GHz channels start after the 2.4GHz channels */
+ sband = &il->bands[IEEE80211_BAND_5GHZ];
+ sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)];
+ /* just OFDM */
+ sband->bitrates = &rates[IL_FIRST_OFDM_RATE];
+ sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE;
+
+ if (il->cfg->sku & IL_SKU_N)
+ il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_5GHZ);
+
+ sband = &il->bands[IEEE80211_BAND_2GHZ];
+ sband->channels = channels;
+ /* OFDM & CCK */
+ sband->bitrates = rates;
+ sband->n_bitrates = RATE_COUNT_LEGACY;
+
+ if (il->cfg->sku & IL_SKU_N)
+ il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_2GHZ);
+
+ il->ieee_channels = channels;
+ il->ieee_rates = rates;
+
+ for (i = 0; i < il->channel_count; i++) {
+ ch = &il->channel_info[i];
+
+ if (!il_is_channel_valid(ch))
+ continue;
+
+ sband = &il->bands[ch->band];
+
+ geo_ch = &sband->channels[sband->n_channels++];
+
+ geo_ch->center_freq =
+ ieee80211_channel_to_frequency(ch->channel, ch->band);
+ geo_ch->max_power = ch->max_power_avg;
+ geo_ch->max_antenna_gain = 0xff;
+ geo_ch->hw_value = ch->channel;
+
+ if (il_is_channel_valid(ch)) {
+ if (!(ch->flags & EEPROM_CHANNEL_IBSS))
+ geo_ch->flags |= IEEE80211_CHAN_NO_IR;
+
+ if (!(ch->flags & EEPROM_CHANNEL_ACTIVE))
+ geo_ch->flags |= IEEE80211_CHAN_NO_IR;
+
+ if (ch->flags & EEPROM_CHANNEL_RADAR)
+ geo_ch->flags |= IEEE80211_CHAN_RADAR;
+
+ geo_ch->flags |= ch->ht40_extension_channel;
+
+ if (ch->max_power_avg > max_tx_power)
+ max_tx_power = ch->max_power_avg;
+ } else {
+ geo_ch->flags |= IEEE80211_CHAN_DISABLED;
+ }
+
+ D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel,
+ geo_ch->center_freq,
+ il_is_channel_a_band(ch) ? "5.2" : "2.4",
+ geo_ch->
+ flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid",
+ geo_ch->flags);
+ }
+
+ il->tx_power_device_lmt = max_tx_power;
+ il->tx_power_user_lmt = max_tx_power;
+ il->tx_power_next = max_tx_power;
+
+ if (il->bands[IEEE80211_BAND_5GHZ].n_channels == 0 &&
+ (il->cfg->sku & IL_SKU_A)) {
+ IL_INFO("Incorrectly detected BG card as ABG. "
+ "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n",
+ il->pci_dev->device, il->pci_dev->subsystem_device);
+ il->cfg->sku &= ~IL_SKU_A;
+ }
+
+ IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n",
+ il->bands[IEEE80211_BAND_2GHZ].n_channels,
+ il->bands[IEEE80211_BAND_5GHZ].n_channels);
+
+ set_bit(S_GEO_CONFIGURED, &il->status);
+
+ return 0;
+}
+EXPORT_SYMBOL(il_init_geos);
+
+/*
+ * il_free_geos - undo allocations in il_init_geos
+ */
+void
+il_free_geos(struct il_priv *il)
+{
+ kfree(il->ieee_channels);
+ kfree(il->ieee_rates);
+ clear_bit(S_GEO_CONFIGURED, &il->status);
+}
+EXPORT_SYMBOL(il_free_geos);
+
+static bool
+il_is_channel_extension(struct il_priv *il, enum ieee80211_band band,
+ u16 channel, u8 extension_chan_offset)
+{
+ const struct il_channel_info *ch_info;
+
+ ch_info = il_get_channel_info(il, band, channel);
+ if (!il_is_channel_valid(ch_info))
+ return false;
+
+ if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
+ return !(ch_info->
+ ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS);
+ else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)
+ return !(ch_info->
+ ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS);
+
+ return false;
+}
+
+bool
+il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap)
+{
+ if (!il->ht.enabled || !il->ht.is_40mhz)
+ return false;
+
+ /*
+ * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
+ * the bit will not set if it is pure 40MHz case
+ */
+ if (ht_cap && !ht_cap->ht_supported)
+ return false;
+
+#ifdef CONFIG_IWLEGACY_DEBUGFS
+ if (il->disable_ht40)
+ return false;
+#endif
+
+ return il_is_channel_extension(il, il->band,
+ le16_to_cpu(il->staging.channel),
+ il->ht.extension_chan_offset);
+}
+EXPORT_SYMBOL(il_is_ht40_tx_allowed);
+
+static u16 noinline
+il_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
+{
+ u16 new_val;
+ u16 beacon_factor;
+
+ /*
+ * If mac80211 hasn't given us a beacon interval, program
+ * the default into the device.
+ */
+ if (!beacon_val)
+ return DEFAULT_BEACON_INTERVAL;
+
+ /*
+ * If the beacon interval we obtained from the peer
+ * is too large, we'll have to wake up more often
+ * (and in IBSS case, we'll beacon too much)
+ *
+ * For example, if max_beacon_val is 4096, and the
+ * requested beacon interval is 7000, we'll have to
+ * use 3500 to be able to wake up on the beacons.
+ *
+ * This could badly influence beacon detection stats.
+ */
+
+ beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
+ new_val = beacon_val / beacon_factor;
+
+ if (!new_val)
+ new_val = max_beacon_val;
+
+ return new_val;
+}
+
+int
+il_send_rxon_timing(struct il_priv *il)
+{
+ u64 tsf;
+ s32 interval_tm, rem;
+ struct ieee80211_conf *conf = NULL;
+ u16 beacon_int;
+ struct ieee80211_vif *vif = il->vif;
+
+ conf = &il->hw->conf;
+
+ lockdep_assert_held(&il->mutex);
+
+ memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd));
+
+ il->timing.timestamp = cpu_to_le64(il->timestamp);
+ il->timing.listen_interval = cpu_to_le16(conf->listen_interval);
+
+ beacon_int = vif ? vif->bss_conf.beacon_int : 0;
+
+ /*
+ * TODO: For IBSS we need to get atim_win from mac80211,
+ * for now just always use 0
+ */
+ il->timing.atim_win = 0;
+
+ beacon_int =
+ il_adjust_beacon_interval(beacon_int,
+ il->hw_params.max_beacon_itrvl *
+ TIME_UNIT);
+ il->timing.beacon_interval = cpu_to_le16(beacon_int);
+
+ tsf = il->timestamp; /* tsf is modifed by do_div: copy it */
+ interval_tm = beacon_int * TIME_UNIT;
+ rem = do_div(tsf, interval_tm);
+ il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
+
+ il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1;
+
+ D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n",
+ le16_to_cpu(il->timing.beacon_interval),
+ le32_to_cpu(il->timing.beacon_init_val),
+ le16_to_cpu(il->timing.atim_win));
+
+ return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing),
+ &il->timing);
+}
+EXPORT_SYMBOL(il_send_rxon_timing);
+
+void
+il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt)
+{
+ struct il_rxon_cmd *rxon = &il->staging;
+
+ if (hw_decrypt)
+ rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
+ else
+ rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
+
+}
+EXPORT_SYMBOL(il_set_rxon_hwcrypto);
+
+/* validate RXON structure is valid */
+int
+il_check_rxon_cmd(struct il_priv *il)
+{
+ struct il_rxon_cmd *rxon = &il->staging;
+ bool error = false;
+
+ if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
+ if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
+ IL_WARN("check 2.4G: wrong narrow\n");
+ error = true;
+ }
+ if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
+ IL_WARN("check 2.4G: wrong radar\n");
+ error = true;
+ }
+ } else {
+ if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
+ IL_WARN("check 5.2G: not short slot!\n");
+ error = true;
+ }
+ if (rxon->flags & RXON_FLG_CCK_MSK) {
+ IL_WARN("check 5.2G: CCK!\n");
+ error = true;
+ }
+ }
+ if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
+ IL_WARN("mac/bssid mcast!\n");
+ error = true;
+ }
+
+ /* make sure basic rates 6Mbps and 1Mbps are supported */
+ if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 &&
+ (rxon->cck_basic_rates & RATE_1M_MASK) == 0) {
+ IL_WARN("neither 1 nor 6 are basic\n");
+ error = true;
+ }
+
+ if (le16_to_cpu(rxon->assoc_id) > 2007) {
+ IL_WARN("aid > 2007\n");
+ error = true;
+ }
+
+ if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) ==
+ (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
+ IL_WARN("CCK and short slot\n");
+ error = true;
+ }
+
+ if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) ==
+ (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
+ IL_WARN("CCK and auto detect");
+ error = true;
+ }
+
+ if ((rxon->
+ flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) ==
+ RXON_FLG_TGG_PROTECT_MSK) {
+ IL_WARN("TGg but no auto-detect\n");
+ error = true;
+ }
+
+ if (error)
+ IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel));
+
+ if (error) {
+ IL_ERR("Invalid RXON\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(il_check_rxon_cmd);
+
+/**
+ * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
+ * @il: staging_rxon is compared to active_rxon
+ *
+ * If the RXON structure is changing enough to require a new tune,
+ * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
+ * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
+ */
+int
+il_full_rxon_required(struct il_priv *il)
+{
+ const struct il_rxon_cmd *staging = &il->staging;
+ const struct il_rxon_cmd *active = &il->active;
+
+#define CHK(cond) \
+ if ((cond)) { \
+ D_INFO("need full RXON - " #cond "\n"); \
+ return 1; \
+ }
+
+#define CHK_NEQ(c1, c2) \
+ if ((c1) != (c2)) { \
+ D_INFO("need full RXON - " \
+ #c1 " != " #c2 " - %d != %d\n", \
+ (c1), (c2)); \
+ return 1; \
+ }
+
+ /* These items are only settable from the full RXON command */
+ CHK(!il_is_associated(il));
+ CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr));
+ CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr));
+ CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr,
+ active->wlap_bssid_addr));
+ CHK_NEQ(staging->dev_type, active->dev_type);
+ CHK_NEQ(staging->channel, active->channel);
+ CHK_NEQ(staging->air_propagation, active->air_propagation);
+ CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
+ active->ofdm_ht_single_stream_basic_rates);
+ CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
+ active->ofdm_ht_dual_stream_basic_rates);
+ CHK_NEQ(staging->assoc_id, active->assoc_id);
+
+ /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
+ * be updated with the RXON_ASSOC command -- however only some
+ * flag transitions are allowed using RXON_ASSOC */
+
+ /* Check if we are not switching bands */
+ CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
+ active->flags & RXON_FLG_BAND_24G_MSK);
+
+ /* Check if we are switching association toggle */
+ CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
+ active->filter_flags & RXON_FILTER_ASSOC_MSK);
+
+#undef CHK
+#undef CHK_NEQ
+
+ return 0;
+}
+EXPORT_SYMBOL(il_full_rxon_required);
+
+u8
+il_get_lowest_plcp(struct il_priv *il)
+{
+ /*
+ * Assign the lowest rate -- should really get this from
+ * the beacon skb from mac80211.
+ */
+ if (il->staging.flags & RXON_FLG_BAND_24G_MSK)
+ return RATE_1M_PLCP;
+ else
+ return RATE_6M_PLCP;
+}
+EXPORT_SYMBOL(il_get_lowest_plcp);
+
+static void
+_il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf)
+{
+ struct il_rxon_cmd *rxon = &il->staging;
+
+ if (!il->ht.enabled) {
+ rxon->flags &=
+ ~(RXON_FLG_CHANNEL_MODE_MSK |
+ RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK
+ | RXON_FLG_HT_PROT_MSK);
+ return;
+ }
+
+ rxon->flags |=
+ cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS);
+
+ /* Set up channel bandwidth:
+ * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
+ /* clear the HT channel mode before set the mode */
+ rxon->flags &=
+ ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
+ if (il_is_ht40_tx_allowed(il, NULL)) {
+ /* pure ht40 */
+ if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
+ /* Note: control channel is opposite of extension channel */
+ switch (il->ht.extension_chan_offset) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ rxon->flags &=
+ ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+ break;
+ }
+ } else {
+ /* Note: control channel is opposite of extension channel */
+ switch (il->ht.extension_chan_offset) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ rxon->flags &=
+ ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+ default:
+ /* channel location only valid if in Mixed mode */
+ IL_ERR("invalid extension channel offset\n");
+ break;
+ }
+ }
+ } else {
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
+ }
+
+ if (il->ops->set_rxon_chain)
+ il->ops->set_rxon_chain(il);
+
+ D_ASSOC("rxon flags 0x%X operation mode :0x%X "
+ "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags),
+ il->ht.protection, il->ht.extension_chan_offset);
+}
+
+void
+il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf)
+{
+ _il_set_rxon_ht(il, ht_conf);
+}
+EXPORT_SYMBOL(il_set_rxon_ht);
+
+/* Return valid, unused, channel for a passive scan to reset the RF */
+u8
+il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band)
+{
+ const struct il_channel_info *ch_info;
+ int i;
+ u8 channel = 0;
+ u8 min, max;
+
+ if (band == IEEE80211_BAND_5GHZ) {
+ min = 14;
+ max = il->channel_count;
+ } else {
+ min = 0;
+ max = 14;
+ }
+
+ for (i = min; i < max; i++) {
+ channel = il->channel_info[i].channel;
+ if (channel == le16_to_cpu(il->staging.channel))
+ continue;
+
+ ch_info = il_get_channel_info(il, band, channel);
+ if (il_is_channel_valid(ch_info))
+ break;
+ }
+
+ return channel;
+}
+EXPORT_SYMBOL(il_get_single_channel_number);
+
+/**
+ * il_set_rxon_channel - Set the band and channel values in staging RXON
+ * @ch: requested channel as a pointer to struct ieee80211_channel
+
+ * NOTE: Does not commit to the hardware; it sets appropriate bit fields
+ * in the staging RXON flag structure based on the ch->band
+ */
+int
+il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch)
+{
+ enum ieee80211_band band = ch->band;
+ u16 channel = ch->hw_value;
+
+ if (le16_to_cpu(il->staging.channel) == channel && il->band == band)
+ return 0;
+
+ il->staging.channel = cpu_to_le16(channel);
+ if (band == IEEE80211_BAND_5GHZ)
+ il->staging.flags &= ~RXON_FLG_BAND_24G_MSK;
+ else
+ il->staging.flags |= RXON_FLG_BAND_24G_MSK;
+
+ il->band = band;
+
+ D_INFO("Staging channel set to %d [%d]\n", channel, band);
+
+ return 0;
+}
+EXPORT_SYMBOL(il_set_rxon_channel);
+
+void
+il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band,
+ struct ieee80211_vif *vif)
+{
+ if (band == IEEE80211_BAND_5GHZ) {
+ il->staging.flags &=
+ ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK |
+ RXON_FLG_CCK_MSK);
+ il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
+ } else {
+ /* Copied from il_post_associate() */
+ if (vif && vif->bss_conf.use_short_slot)
+ il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
+ else
+ il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
+
+ il->staging.flags |= RXON_FLG_BAND_24G_MSK;
+ il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK;
+ il->staging.flags &= ~RXON_FLG_CCK_MSK;
+ }
+}
+EXPORT_SYMBOL(il_set_flags_for_band);
+
+/*
+ * initialize rxon structure with default values from eeprom
+ */
+void
+il_connection_init_rx_config(struct il_priv *il)
+{
+ const struct il_channel_info *ch_info;
+
+ memset(&il->staging, 0, sizeof(il->staging));
+
+ switch (il->iw_mode) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ il->staging.dev_type = RXON_DEV_TYPE_ESS;
+ break;
+ case NL80211_IFTYPE_STATION:
+ il->staging.dev_type = RXON_DEV_TYPE_ESS;
+ il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ il->staging.dev_type = RXON_DEV_TYPE_IBSS;
+ il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
+ il->staging.filter_flags =
+ RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
+ break;
+ default:
+ IL_ERR("Unsupported interface type %d\n", il->vif->type);
+ return;
+ }
+
+#if 0
+ /* TODO: Figure out when short_preamble would be set and cache from
+ * that */
+ if (!hw_to_local(il->hw)->short_preamble)
+ il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
+ else
+ il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
+#endif
+
+ ch_info =
+ il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel));
+
+ if (!ch_info)
+ ch_info = &il->channel_info[0];
+
+ il->staging.channel = cpu_to_le16(ch_info->channel);
+ il->band = ch_info->band;
+
+ il_set_flags_for_band(il, il->band, il->vif);
+
+ il->staging.ofdm_basic_rates =
+ (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF;
+ il->staging.cck_basic_rates =
+ (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF;
+
+ /* clear both MIX and PURE40 mode flag */
+ il->staging.flags &=
+ ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40);
+ if (il->vif)
+ memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN);
+
+ il->staging.ofdm_ht_single_stream_basic_rates = 0xff;
+ il->staging.ofdm_ht_dual_stream_basic_rates = 0xff;
+}
+EXPORT_SYMBOL(il_connection_init_rx_config);
+
+void
+il_set_rate(struct il_priv *il)
+{
+ const struct ieee80211_supported_band *hw = NULL;
+ struct ieee80211_rate *rate;
+ int i;
+
+ hw = il_get_hw_mode(il, il->band);
+ if (!hw) {
+ IL_ERR("Failed to set rate: unable to get hw mode\n");
+ return;
+ }
+
+ il->active_rate = 0;
+
+ for (i = 0; i < hw->n_bitrates; i++) {
+ rate = &(hw->bitrates[i]);
+ if (rate->hw_value < RATE_COUNT_LEGACY)
+ il->active_rate |= (1 << rate->hw_value);
+ }
+
+ D_RATE("Set active_rate = %0x\n", il->active_rate);
+
+ il->staging.cck_basic_rates =
+ (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF;
+
+ il->staging.ofdm_basic_rates =
+ (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF;
+}
+EXPORT_SYMBOL(il_set_rate);
+
+void
+il_chswitch_done(struct il_priv *il, bool is_success)
+{
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return;
+
+ if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
+ ieee80211_chswitch_done(il->vif, is_success);
+}
+EXPORT_SYMBOL(il_chswitch_done);
+
+void
+il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_csa_notification *csa = &(pkt->u.csa_notif);
+ struct il_rxon_cmd *rxon = (void *)&il->active;
+
+ if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
+ return;
+
+ if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) {
+ rxon->channel = csa->channel;
+ il->staging.channel = csa->channel;
+ D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel));
+ il_chswitch_done(il, true);
+ } else {
+ IL_ERR("CSA notif (fail) : channel %d\n",
+ le16_to_cpu(csa->channel));
+ il_chswitch_done(il, false);
+ }
+}
+EXPORT_SYMBOL(il_hdl_csa);
+
+#ifdef CONFIG_IWLEGACY_DEBUG
+void
+il_print_rx_config_cmd(struct il_priv *il)
+{
+ struct il_rxon_cmd *rxon = &il->staging;
+
+ D_RADIO("RX CONFIG:\n");
+ il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
+ D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel));
+ D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags));
+ D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags));
+ D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type);
+ D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates);
+ D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates);
+ D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr);
+ D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
+ D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
+}
+EXPORT_SYMBOL(il_print_rx_config_cmd);
+#endif
+/**
+ * il_irq_handle_error - called for HW or SW error interrupt from card
+ */
+void
+il_irq_handle_error(struct il_priv *il)
+{
+ /* Set the FW error flag -- cleared on il_down */
+ set_bit(S_FW_ERROR, &il->status);
+
+ /* Cancel currently queued command. */
+ clear_bit(S_HCMD_ACTIVE, &il->status);
+
+ IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version);
+
+ il->ops->dump_nic_error_log(il);
+ if (il->ops->dump_fh)
+ il->ops->dump_fh(il, NULL, false);
+#ifdef CONFIG_IWLEGACY_DEBUG
+ if (il_get_debug_level(il) & IL_DL_FW_ERRORS)
+ il_print_rx_config_cmd(il);
+#endif
+
+ wake_up(&il->wait_command_queue);
+
+ /* Keep the restart process from trying to send host
+ * commands by clearing the INIT status bit */
+ clear_bit(S_READY, &il->status);
+
+ if (!test_bit(S_EXIT_PENDING, &il->status)) {
+ IL_DBG(IL_DL_FW_ERRORS,
+ "Restarting adapter due to uCode error.\n");
+
+ if (il->cfg->mod_params->restart_fw)
+ queue_work(il->workqueue, &il->restart);
+ }
+}
+EXPORT_SYMBOL(il_irq_handle_error);
+
+static int
+_il_apm_stop_master(struct il_priv *il)
+{
+ int ret = 0;
+
+ /* stop device's busmaster DMA activity */
+ _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
+
+ ret =
+ _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED,
+ CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
+ if (ret < 0)
+ IL_WARN("Master Disable Timed Out, 100 usec\n");
+
+ D_INFO("stop master\n");
+
+ return ret;
+}
+
+void
+_il_apm_stop(struct il_priv *il)
+{
+ lockdep_assert_held(&il->reg_lock);
+
+ D_INFO("Stop card, put in low power state\n");
+
+ /* Stop device's DMA activity */
+ _il_apm_stop_master(il);
+
+ /* Reset the entire device */
+ _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ udelay(10);
+
+ /*
+ * Clear "initialization complete" bit to move adapter from
+ * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
+ */
+ _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+}
+EXPORT_SYMBOL(_il_apm_stop);
+
+void
+il_apm_stop(struct il_priv *il)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&il->reg_lock, flags);
+ _il_apm_stop(il);
+ spin_unlock_irqrestore(&il->reg_lock, flags);
+}
+EXPORT_SYMBOL(il_apm_stop);
+
+/*
+ * Start up NIC's basic functionality after it has been reset
+ * (e.g. after platform boot, or shutdown via il_apm_stop())
+ * NOTE: This does not load uCode nor start the embedded processor
+ */
+int
+il_apm_init(struct il_priv *il)
+{
+ int ret = 0;
+ u16 lctl;
+
+ D_INFO("Init card's basic functions\n");
+
+ /*
+ * Use "set_bit" below rather than "write", to preserve any hardware
+ * bits already set by default after reset.
+ */
+
+ /* Disable L0S exit timer (platform NMI Work/Around) */
+ il_set_bit(il, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+
+ /*
+ * Disable L0s without affecting L1;
+ * don't wait for ICH L0s (ICH bug W/A)
+ */
+ il_set_bit(il, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
+
+ /* Set FH wait threshold to maximum (HW error during stress W/A) */
+ il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
+
+ /*
+ * Enable HAP INTA (interrupt from management bus) to
+ * wake device's PCI Express link L1a -> L0s
+ * NOTE: This is no-op for 3945 (non-existent bit)
+ */
+ il_set_bit(il, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
+
+ /*
+ * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition.
+ * Check if BIOS (or OS) enabled L1-ASPM on this device.
+ * If so (likely), disable L0S, so device moves directly L0->L1;
+ * costs negligible amount of power savings.
+ * If not (unlikely), enable L0S, so there is at least some
+ * power savings, even without L1.
+ */
+ if (il->cfg->set_l0s) {
+ pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
+ if (lctl & PCI_EXP_LNKCTL_ASPM_L1) {
+ /* L1-ASPM enabled; disable(!) L0S */
+ il_set_bit(il, CSR_GIO_REG,
+ CSR_GIO_REG_VAL_L0S_ENABLED);
+ D_POWER("L1 Enabled; Disabling L0S\n");
+ } else {
+ /* L1-ASPM disabled; enable(!) L0S */
+ il_clear_bit(il, CSR_GIO_REG,
+ CSR_GIO_REG_VAL_L0S_ENABLED);
+ D_POWER("L1 Disabled; Enabling L0S\n");
+ }
+ }
+
+ /* Configure analog phase-lock-loop before activating to D0A */
+ if (il->cfg->pll_cfg_val)
+ il_set_bit(il, CSR_ANA_PLL_CFG,
+ il->cfg->pll_cfg_val);
+
+ /*
+ * Set "initialization complete" bit to move adapter from
+ * D0U* --> D0A* (powered-up active) state.
+ */
+ il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /*
+ * Wait for clock stabilization; once stabilized, access to
+ * device-internal resources is supported, e.g. il_wr_prph()
+ * and accesses to uCode SRAM.
+ */
+ ret =
+ _il_poll_bit(il, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ if (ret < 0) {
+ D_INFO("Failed to init the card\n");
+ goto out;
+ }
+
+ /*
+ * Enable DMA and BSM (if used) clocks, wait for them to stabilize.
+ * BSM (Boostrap State Machine) is only in 3945 and 4965.
+ *
+ * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
+ * do not disable clocks. This preserves any hardware bits already
+ * set by default in "CLK_CTRL_REG" after reset.
+ */
+ if (il->cfg->use_bsm)
+ il_wr_prph(il, APMG_CLK_EN_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT);
+ else
+ il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(20);
+
+ /* Disable L1-Active */
+ il_set_bits_prph(il, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(il_apm_init);
+
+int
+il_set_tx_power(struct il_priv *il, s8 tx_power, bool force)
+{
+ int ret;
+ s8 prev_tx_power;
+ bool defer;
+
+ lockdep_assert_held(&il->mutex);
+
+ if (il->tx_power_user_lmt == tx_power && !force)
+ return 0;
+
+ if (!il->ops->send_tx_power)
+ return -EOPNOTSUPP;
+
+ /* 0 dBm mean 1 milliwatt */
+ if (tx_power < 0) {
+ IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power);
+ return -EINVAL;
+ }
+
+ if (tx_power > il->tx_power_device_lmt) {
+ IL_WARN("Requested user TXPOWER %d above upper limit %d.\n",
+ tx_power, il->tx_power_device_lmt);
+ return -EINVAL;
+ }
+
+ if (!il_is_ready_rf(il))
+ return -EIO;
+
+ /* scan complete and commit_rxon use tx_power_next value,
+ * it always need to be updated for newest request */
+ il->tx_power_next = tx_power;
+
+ /* do not set tx power when scanning or channel changing */
+ defer = test_bit(S_SCANNING, &il->status) ||
+ memcmp(&il->active, &il->staging, sizeof(il->staging));
+ if (defer && !force) {
+ D_INFO("Deferring tx power set\n");
+ return 0;
+ }
+
+ prev_tx_power = il->tx_power_user_lmt;
+ il->tx_power_user_lmt = tx_power;
+
+ ret = il->ops->send_tx_power(il);
+
+ /* if fail to set tx_power, restore the orig. tx power */
+ if (ret) {
+ il->tx_power_user_lmt = prev_tx_power;
+ il->tx_power_next = prev_tx_power;
+ }
+ return ret;
+}
+EXPORT_SYMBOL(il_set_tx_power);
+
+void
+il_send_bt_config(struct il_priv *il)
+{
+ struct il_bt_cmd bt_cmd = {
+ .lead_time = BT_LEAD_TIME_DEF,
+ .max_kill = BT_MAX_KILL_DEF,
+ .kill_ack_mask = 0,
+ .kill_cts_mask = 0,
+ };
+
+ if (!bt_coex_active)
+ bt_cmd.flags = BT_COEX_DISABLE;
+ else
+ bt_cmd.flags = BT_COEX_ENABLE;
+
+ D_INFO("BT coex %s\n",
+ (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
+
+ if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd))
+ IL_ERR("failed to send BT Coex Config\n");
+}
+EXPORT_SYMBOL(il_send_bt_config);
+
+int
+il_send_stats_request(struct il_priv *il, u8 flags, bool clear)
+{
+ struct il_stats_cmd stats_cmd = {
+ .configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0,
+ };
+
+ if (flags & CMD_ASYNC)
+ return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd),
+ &stats_cmd, NULL);
+ else
+ return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd),
+ &stats_cmd);
+}
+EXPORT_SYMBOL(il_send_stats_request);
+
+void
+il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb)
+{
+#ifdef CONFIG_IWLEGACY_DEBUG
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ struct il_sleep_notification *sleep = &(pkt->u.sleep_notif);
+ D_RX("sleep mode: %d, src: %d\n",
+ sleep->pm_sleep_mode, sleep->pm_wakeup_src);
+#endif
+}
+EXPORT_SYMBOL(il_hdl_pm_sleep);
+
+void
+il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+ u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
+ D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len,
+ il_get_cmd_string(pkt->hdr.cmd));
+ il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len);
+}
+EXPORT_SYMBOL(il_hdl_pm_debug_stats);
+
+void
+il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb)
+{
+ struct il_rx_pkt *pkt = rxb_addr(rxb);
+
+ IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) "
+ "seq 0x%04X ser 0x%08X\n",
+ le32_to_cpu(pkt->u.err_resp.error_type),
+ il_get_cmd_string(pkt->u.err_resp.cmd_id),
+ pkt->u.err_resp.cmd_id,
+ le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num),
+ le32_to_cpu(pkt->u.err_resp.error_info));
+}
+EXPORT_SYMBOL(il_hdl_error);
+
+void
+il_clear_isr_stats(struct il_priv *il)
+{
+ memset(&il->isr_stats, 0, sizeof(il->isr_stats));
+}
+
+int
+il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct il_priv *il = hw->priv;
+ unsigned long flags;
+ int q;
+
+ D_MAC80211("enter\n");
+
+ if (!il_is_ready_rf(il)) {
+ D_MAC80211("leave - RF not ready\n");
+ return -EIO;
+ }
+
+ if (queue >= AC_NUM) {
+ D_MAC80211("leave - queue >= AC_NUM %d\n", queue);
+ return 0;
+ }
+
+ q = AC_NUM - 1 - queue;
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ il->qos_data.def_qos_parm.ac[q].cw_min =
+ cpu_to_le16(params->cw_min);
+ il->qos_data.def_qos_parm.ac[q].cw_max =
+ cpu_to_le16(params->cw_max);
+ il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs;
+ il->qos_data.def_qos_parm.ac[q].edca_txop =
+ cpu_to_le16((params->txop * 32));
+
+ il->qos_data.def_qos_parm.ac[q].reserved1 = 0;
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ D_MAC80211("leave\n");
+ return 0;
+}
+EXPORT_SYMBOL(il_mac_conf_tx);
+
+int
+il_mac_tx_last_beacon(struct ieee80211_hw *hw)
+{
+ struct il_priv *il = hw->priv;
+ int ret;
+
+ D_MAC80211("enter\n");
+
+ ret = (il->ibss_manager == IL_IBSS_MANAGER);
+
+ D_MAC80211("leave ret %d\n", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(il_mac_tx_last_beacon);
+
+static int
+il_set_mode(struct il_priv *il)
+{
+ il_connection_init_rx_config(il);
+
+ if (il->ops->set_rxon_chain)
+ il->ops->set_rxon_chain(il);
+
+ return il_commit_rxon(il);
+}
+
+int
+il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct il_priv *il = hw->priv;
+ int err;
+ bool reset;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
+
+ if (!il_is_ready_rf(il)) {
+ IL_WARN("Try to add interface when device not ready\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * We do not support multiple virtual interfaces, but on hardware reset
+ * we have to add the same interface again.
+ */
+ reset = (il->vif == vif);
+ if (il->vif && !reset) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ il->vif = vif;
+ il->iw_mode = vif->type;
+
+ err = il_set_mode(il);
+ if (err) {
+ IL_WARN("Fail to set mode %d\n", vif->type);
+ if (!reset) {
+ il->vif = NULL;
+ il->iw_mode = NL80211_IFTYPE_STATION;
+ }
+ }
+
+out:
+ D_MAC80211("leave err %d\n", err);
+ mutex_unlock(&il->mutex);
+
+ return err;
+}
+EXPORT_SYMBOL(il_mac_add_interface);
+
+static void
+il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif)
+{
+ lockdep_assert_held(&il->mutex);
+
+ if (il->scan_vif == vif) {
+ il_scan_cancel_timeout(il, 200);
+ il_force_scan_end(il);
+ }
+
+ il_set_mode(il);
+}
+
+void
+il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct il_priv *il = hw->priv;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
+
+ WARN_ON(il->vif != vif);
+ il->vif = NULL;
+ il->iw_mode = NL80211_IFTYPE_UNSPECIFIED;
+ il_teardown_interface(il, vif);
+ eth_zero_addr(il->bssid);
+
+ D_MAC80211("leave\n");
+ mutex_unlock(&il->mutex);
+}
+EXPORT_SYMBOL(il_mac_remove_interface);
+
+int
+il_alloc_txq_mem(struct il_priv *il)
+{
+ if (!il->txq)
+ il->txq =
+ kzalloc(sizeof(struct il_tx_queue) *
+ il->cfg->num_of_queues, GFP_KERNEL);
+ if (!il->txq) {
+ IL_ERR("Not enough memory for txq\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(il_alloc_txq_mem);
+
+void
+il_free_txq_mem(struct il_priv *il)
+{
+ kfree(il->txq);
+ il->txq = NULL;
+}
+EXPORT_SYMBOL(il_free_txq_mem);
+
+int
+il_force_reset(struct il_priv *il, bool external)
+{
+ struct il_force_reset *force_reset;
+
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return -EINVAL;
+
+ force_reset = &il->force_reset;
+ force_reset->reset_request_count++;
+ if (!external) {
+ if (force_reset->last_force_reset_jiffies &&
+ time_after(force_reset->last_force_reset_jiffies +
+ force_reset->reset_duration, jiffies)) {
+ D_INFO("force reset rejected\n");
+ force_reset->reset_reject_count++;
+ return -EAGAIN;
+ }
+ }
+ force_reset->reset_success_count++;
+ force_reset->last_force_reset_jiffies = jiffies;
+
+ /*
+ * if the request is from external(ex: debugfs),
+ * then always perform the request in regardless the module
+ * parameter setting
+ * if the request is from internal (uCode error or driver
+ * detect failure), then fw_restart module parameter
+ * need to be check before performing firmware reload
+ */
+
+ if (!external && !il->cfg->mod_params->restart_fw) {
+ D_INFO("Cancel firmware reload based on "
+ "module parameter setting\n");
+ return 0;
+ }
+
+ IL_ERR("On demand firmware reload\n");
+
+ /* Set the FW error flag -- cleared on il_down */
+ set_bit(S_FW_ERROR, &il->status);
+ wake_up(&il->wait_command_queue);
+ /*
+ * Keep the restart process from trying to send host
+ * commands by clearing the INIT status bit
+ */
+ clear_bit(S_READY, &il->status);
+ queue_work(il->workqueue, &il->restart);
+
+ return 0;
+}
+EXPORT_SYMBOL(il_force_reset);
+
+int
+il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ enum nl80211_iftype newtype, bool newp2p)
+{
+ struct il_priv *il = hw->priv;
+ int err;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n",
+ vif->type, vif->addr, newtype, newp2p);
+
+ if (newp2p) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!il->vif || !il_is_ready_rf(il)) {
+ /*
+ * Huh? But wait ... this can maybe happen when
+ * we're in the middle of a firmware restart!
+ */
+ err = -EBUSY;
+ goto out;
+ }
+
+ /* success */
+ vif->type = newtype;
+ vif->p2p = false;
+ il->iw_mode = newtype;
+ il_teardown_interface(il, vif);
+ err = 0;
+
+out:
+ D_MAC80211("leave err %d\n", err);
+ mutex_unlock(&il->mutex);
+
+ return err;
+}
+EXPORT_SYMBOL(il_mac_change_interface);
+
+void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
+{
+ struct il_priv *il = hw->priv;
+ unsigned long timeout = jiffies + msecs_to_jiffies(500);
+ int i;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter\n");
+
+ if (il->txq == NULL)
+ goto out;
+
+ for (i = 0; i < il->hw_params.max_txq_num; i++) {
+ struct il_queue *q;
+
+ if (i == il->cmd_queue)
+ continue;
+
+ q = &il->txq[i].q;
+ if (q->read_ptr == q->write_ptr)
+ continue;
+
+ if (time_after(jiffies, timeout)) {
+ IL_ERR("Failed to flush queue %d\n", q->id);
+ break;
+ }
+
+ msleep(20);
+ }
+out:
+ D_MAC80211("leave\n");
+ mutex_unlock(&il->mutex);
+}
+EXPORT_SYMBOL(il_mac_flush);
+
+/*
+ * On every watchdog tick we check (latest) time stamp. If it does not
+ * change during timeout period and queue is not empty we reset firmware.
+ */
+static int
+il_check_stuck_queue(struct il_priv *il, int cnt)
+{
+ struct il_tx_queue *txq = &il->txq[cnt];
+ struct il_queue *q = &txq->q;
+ unsigned long timeout;
+ unsigned long now = jiffies;
+ int ret;
+
+ if (q->read_ptr == q->write_ptr) {
+ txq->time_stamp = now;
+ return 0;
+ }
+
+ timeout =
+ txq->time_stamp +
+ msecs_to_jiffies(il->cfg->wd_timeout);
+
+ if (time_after(now, timeout)) {
+ IL_ERR("Queue %d stuck for %u ms.\n", q->id,
+ jiffies_to_msecs(now - txq->time_stamp));
+ ret = il_force_reset(il, false);
+ return (ret == -EAGAIN) ? 0 : 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Making watchdog tick be a quarter of timeout assure we will
+ * discover the queue hung between timeout and 1.25*timeout
+ */
+#define IL_WD_TICK(timeout) ((timeout) / 4)
+
+/*
+ * Watchdog timer callback, we check each tx queue for stuck, if if hung
+ * we reset the firmware. If everything is fine just rearm the timer.
+ */
+void
+il_bg_watchdog(unsigned long data)
+{
+ struct il_priv *il = (struct il_priv *)data;
+ int cnt;
+ unsigned long timeout;
+
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return;
+
+ timeout = il->cfg->wd_timeout;
+ if (timeout == 0)
+ return;
+
+ /* monitor and check for stuck cmd queue */
+ if (il_check_stuck_queue(il, il->cmd_queue))
+ return;
+
+ /* monitor and check for other stuck queues */
+ for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) {
+ /* skip as we already checked the command queue */
+ if (cnt == il->cmd_queue)
+ continue;
+ if (il_check_stuck_queue(il, cnt))
+ return;
+ }
+
+ mod_timer(&il->watchdog,
+ jiffies + msecs_to_jiffies(IL_WD_TICK(timeout)));
+}
+EXPORT_SYMBOL(il_bg_watchdog);
+
+void
+il_setup_watchdog(struct il_priv *il)
+{
+ unsigned int timeout = il->cfg->wd_timeout;
+
+ if (timeout)
+ mod_timer(&il->watchdog,
+ jiffies + msecs_to_jiffies(IL_WD_TICK(timeout)));
+ else
+ del_timer(&il->watchdog);
+}
+EXPORT_SYMBOL(il_setup_watchdog);
+
+/*
+ * extended beacon time format
+ * time in usec will be changed into a 32-bit value in extended:internal format
+ * the extended part is the beacon counts
+ * the internal part is the time in usec within one beacon interval
+ */
+u32
+il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval)
+{
+ u32 quot;
+ u32 rem;
+ u32 interval = beacon_interval * TIME_UNIT;
+
+ if (!interval || !usec)
+ return 0;
+
+ quot =
+ (usec /
+ interval) & (il_beacon_time_mask_high(il,
+ il->hw_params.
+ beacon_time_tsf_bits) >> il->
+ hw_params.beacon_time_tsf_bits);
+ rem =
+ (usec % interval) & il_beacon_time_mask_low(il,
+ il->hw_params.
+ beacon_time_tsf_bits);
+
+ return (quot << il->hw_params.beacon_time_tsf_bits) + rem;
+}
+EXPORT_SYMBOL(il_usecs_to_beacons);
+
+/* base is usually what we get from ucode with each received frame,
+ * the same as HW timer counter counting down
+ */
+__le32
+il_add_beacon_time(struct il_priv *il, u32 base, u32 addon,
+ u32 beacon_interval)
+{
+ u32 base_low = base & il_beacon_time_mask_low(il,
+ il->hw_params.
+ beacon_time_tsf_bits);
+ u32 addon_low = addon & il_beacon_time_mask_low(il,
+ il->hw_params.
+ beacon_time_tsf_bits);
+ u32 interval = beacon_interval * TIME_UNIT;
+ u32 res = (base & il_beacon_time_mask_high(il,
+ il->hw_params.
+ beacon_time_tsf_bits)) +
+ (addon & il_beacon_time_mask_high(il,
+ il->hw_params.
+ beacon_time_tsf_bits));
+
+ if (base_low > addon_low)
+ res += base_low - addon_low;
+ else if (base_low < addon_low) {
+ res += interval + base_low - addon_low;
+ res += (1 << il->hw_params.beacon_time_tsf_bits);
+ } else
+ res += (1 << il->hw_params.beacon_time_tsf_bits);
+
+ return cpu_to_le32(res);
+}
+EXPORT_SYMBOL(il_add_beacon_time);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int
+il_pci_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct il_priv *il = pci_get_drvdata(pdev);
+
+ /*
+ * This function is called when system goes into suspend state
+ * mac80211 will call il_mac_stop() from the mac80211 suspend function
+ * first but since il_mac_stop() has no knowledge of who the caller is,
+ * it will not call apm_ops.stop() to stop the DMA operation.
+ * Calling apm_ops.stop here to make sure we stop the DMA.
+ */
+ il_apm_stop(il);
+
+ return 0;
+}
+
+static int
+il_pci_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct il_priv *il = pci_get_drvdata(pdev);
+ bool hw_rfkill = false;
+
+ /*
+ * We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state.
+ */
+ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
+ il_enable_interrupts(il);
+
+ if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
+ hw_rfkill = true;
+
+ if (hw_rfkill)
+ set_bit(S_RFKILL, &il->status);
+ else
+ clear_bit(S_RFKILL, &il->status);
+
+ wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill);
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume);
+EXPORT_SYMBOL(il_pm_ops);
+
+#endif /* CONFIG_PM_SLEEP */
+
+static void
+il_update_qos(struct il_priv *il)
+{
+ if (test_bit(S_EXIT_PENDING, &il->status))
+ return;
+
+ il->qos_data.def_qos_parm.qos_flags = 0;
+
+ if (il->qos_data.qos_active)
+ il->qos_data.def_qos_parm.qos_flags |=
+ QOS_PARAM_FLG_UPDATE_EDCA_MSK;
+
+ if (il->ht.enabled)
+ il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
+
+ D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n",
+ il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags);
+
+ il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd),
+ &il->qos_data.def_qos_parm, NULL);
+}
+
+/**
+ * il_mac_config - mac80211 config callback
+ */
+int
+il_mac_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct il_priv *il = hw->priv;
+ const struct il_channel_info *ch_info;
+ struct ieee80211_conf *conf = &hw->conf;
+ struct ieee80211_channel *channel = conf->chandef.chan;
+ struct il_ht_config *ht_conf = &il->current_ht_config;
+ unsigned long flags = 0;
+ int ret = 0;
+ u16 ch;
+ int scan_active = 0;
+ bool ht_changed = false;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value,
+ changed);
+
+ if (unlikely(test_bit(S_SCANNING, &il->status))) {
+ scan_active = 1;
+ D_MAC80211("scan active\n");
+ }
+
+ if (changed &
+ (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) {
+ /* mac80211 uses static for non-HT which is what we want */
+ il->current_ht_config.smps = conf->smps_mode;
+
+ /*
+ * Recalculate chain counts.
+ *
+ * If monitor mode is enabled then mac80211 will
+ * set up the SM PS mode to OFF if an HT channel is
+ * configured.
+ */
+ if (il->ops->set_rxon_chain)
+ il->ops->set_rxon_chain(il);
+ }
+
+ /* during scanning mac80211 will delay channel setting until
+ * scan finish with changed = 0
+ */
+ if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+
+ if (scan_active)
+ goto set_ch_out;
+
+ ch = channel->hw_value;
+ ch_info = il_get_channel_info(il, channel->band, ch);
+ if (!il_is_channel_valid(ch_info)) {
+ D_MAC80211("leave - invalid channel\n");
+ ret = -EINVAL;
+ goto set_ch_out;
+ }
+
+ if (il->iw_mode == NL80211_IFTYPE_ADHOC &&
+ !il_is_channel_ibss(ch_info)) {
+ D_MAC80211("leave - not IBSS channel\n");
+ ret = -EINVAL;
+ goto set_ch_out;
+ }
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ /* Configure HT40 channels */
+ if (il->ht.enabled != conf_is_ht(conf)) {
+ il->ht.enabled = conf_is_ht(conf);
+ ht_changed = true;
+ }
+ if (il->ht.enabled) {
+ if (conf_is_ht40_minus(conf)) {
+ il->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ il->ht.is_40mhz = true;
+ } else if (conf_is_ht40_plus(conf)) {
+ il->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ il->ht.is_40mhz = true;
+ } else {
+ il->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ il->ht.is_40mhz = false;
+ }
+ } else
+ il->ht.is_40mhz = false;
+
+ /*
+ * Default to no protection. Protection mode will
+ * later be set from BSS config in il_ht_conf
+ */
+ il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
+
+ /* if we are switching from ht to 2.4 clear flags
+ * from any ht related info since 2.4 does not
+ * support ht */
+ if ((le16_to_cpu(il->staging.channel) != ch))
+ il->staging.flags = 0;
+
+ il_set_rxon_channel(il, channel);
+ il_set_rxon_ht(il, ht_conf);
+
+ il_set_flags_for_band(il, channel->band, il->vif);
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ if (il->ops->update_bcast_stations)
+ ret = il->ops->update_bcast_stations(il);
+
+set_ch_out:
+ /* The list of supported rates and rate mask can be different
+ * for each band; since the band may have changed, reset
+ * the rate mask to what mac80211 lists */
+ il_set_rate(il);
+ }
+
+ if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) {
+ il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS);
+ ret = il_power_update_mode(il, false);
+ if (ret)
+ D_MAC80211("Error setting sleep level\n");
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt,
+ conf->power_level);
+
+ il_set_tx_power(il, conf->power_level, false);
+ }
+
+ if (!il_is_ready(il)) {
+ D_MAC80211("leave - not ready\n");
+ goto out;
+ }
+
+ if (scan_active)
+ goto out;
+
+ if (memcmp(&il->active, &il->staging, sizeof(il->staging)))
+ il_commit_rxon(il);
+ else
+ D_INFO("Not re-sending same RXON configuration.\n");
+ if (ht_changed)
+ il_update_qos(il);
+
+out:
+ D_MAC80211("leave ret %d\n", ret);
+ mutex_unlock(&il->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(il_mac_config);
+
+void
+il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct il_priv *il = hw->priv;
+ unsigned long flags;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ memset(&il->current_ht_config, 0, sizeof(struct il_ht_config));
+
+ /* new association get rid of ibss beacon skb */
+ if (il->beacon_skb)
+ dev_kfree_skb(il->beacon_skb);
+ il->beacon_skb = NULL;
+ il->timestamp = 0;
+
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ il_scan_cancel_timeout(il, 100);
+ if (!il_is_ready_rf(il)) {
+ D_MAC80211("leave - not ready\n");
+ mutex_unlock(&il->mutex);
+ return;
+ }
+
+ /* we are restarting association process */
+ il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ il_commit_rxon(il);
+
+ il_set_rate(il);
+
+ D_MAC80211("leave\n");
+ mutex_unlock(&il->mutex);
+}
+EXPORT_SYMBOL(il_mac_reset_tsf);
+
+static void
+il_ht_conf(struct il_priv *il, struct ieee80211_vif *vif)
+{
+ struct il_ht_config *ht_conf = &il->current_ht_config;
+ struct ieee80211_sta *sta;
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+
+ D_ASSOC("enter:\n");
+
+ if (!il->ht.enabled)
+ return;
+
+ il->ht.protection =
+ bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
+ il->ht.non_gf_sta_present =
+ !!(bss_conf->
+ ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+
+ ht_conf->single_chain_sufficient = false;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ rcu_read_lock();
+ sta = ieee80211_find_sta(vif, bss_conf->bssid);
+ if (sta) {
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ int maxstreams;
+
+ maxstreams =
+ (ht_cap->mcs.
+ tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+ >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
+ maxstreams += 1;
+
+ if (ht_cap->mcs.rx_mask[1] == 0 &&
+ ht_cap->mcs.rx_mask[2] == 0)
+ ht_conf->single_chain_sufficient = true;
+ if (maxstreams <= 1)
+ ht_conf->single_chain_sufficient = true;
+ } else {
+ /*
+ * If at all, this can only happen through a race
+ * when the AP disconnects us while we're still
+ * setting up the connection, in that case mac80211
+ * will soon tell us about that.
+ */
+ ht_conf->single_chain_sufficient = true;
+ }
+ rcu_read_unlock();
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ ht_conf->single_chain_sufficient = true;
+ break;
+ default:
+ break;
+ }
+
+ D_ASSOC("leave\n");
+}
+
+static inline void
+il_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif)
+{
+ /*
+ * inform the ucode that there is no longer an
+ * association and that no more packets should be
+ * sent
+ */
+ il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ il->staging.assoc_id = 0;
+ il_commit_rxon(il);
+}
+
+static void
+il_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct il_priv *il = hw->priv;
+ unsigned long flags;
+ __le64 timestamp;
+ struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
+
+ if (!skb)
+ return;
+
+ D_MAC80211("enter\n");
+
+ lockdep_assert_held(&il->mutex);
+
+ if (!il->beacon_enabled) {
+ IL_ERR("update beacon with no beaconing enabled\n");
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ if (il->beacon_skb)
+ dev_kfree_skb(il->beacon_skb);
+
+ il->beacon_skb = skb;
+
+ timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
+ il->timestamp = le64_to_cpu(timestamp);
+
+ D_MAC80211("leave\n");
+ spin_unlock_irqrestore(&il->lock, flags);
+
+ if (!il_is_ready_rf(il)) {
+ D_MAC80211("leave - RF not ready\n");
+ return;
+ }
+
+ il->ops->post_associate(il);
+}
+
+void
+il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf, u32 changes)
+{
+ struct il_priv *il = hw->priv;
+ int ret;
+
+ mutex_lock(&il->mutex);
+ D_MAC80211("enter: changes 0x%x\n", changes);
+
+ if (!il_is_alive(il)) {
+ D_MAC80211("leave - not alive\n");
+ mutex_unlock(&il->mutex);
+ return;
+ }
+
+ if (changes & BSS_CHANGED_QOS) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&il->lock, flags);
+ il->qos_data.qos_active = bss_conf->qos;
+ il_update_qos(il);
+ spin_unlock_irqrestore(&il->lock, flags);
+ }
+
+ if (changes & BSS_CHANGED_BEACON_ENABLED) {
+ /* FIXME: can we remove beacon_enabled ? */
+ if (vif->bss_conf.enable_beacon)
+ il->beacon_enabled = true;
+ else
+ il->beacon_enabled = false;
+ }
+
+ 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
+ * able to authenticate (FIXME: why ?)
+ */
+ if (il_scan_cancel_timeout(il, 100)) {
+ D_MAC80211("leave - scan abort failed\n");
+ mutex_unlock(&il->mutex);
+ return;
+ }
+
+ /* mac80211 only sets assoc when in STATION mode */
+ memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
+
+ /* FIXME: currently needed in a few places */
+ memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
+ }
+
+ /*
+ * This needs to be after setting the BSSID in case
+ * mac80211 decides to do both changes at once because
+ * it will invoke post_associate.
+ */
+ if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON))
+ il_beacon_update(hw, vif);
+
+ if (changes & BSS_CHANGED_ERP_PREAMBLE) {
+ D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble);
+ if (bss_conf->use_short_preamble)
+ il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
+ else
+ il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
+ }
+
+ if (changes & BSS_CHANGED_ERP_CTS_PROT) {
+ D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot);
+ if (bss_conf->use_cts_prot && il->band != IEEE80211_BAND_5GHZ)
+ il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
+ else
+ il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
+ if (bss_conf->use_cts_prot)
+ il->staging.flags |= RXON_FLG_SELF_CTS_EN;
+ else
+ il->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
+ }
+
+ if (changes & BSS_CHANGED_BASIC_RATES) {
+ /* XXX use this information
+ *
+ * To do that, remove code from il_set_rate() and put something
+ * like this here:
+ *
+ if (A-band)
+ il->staging.ofdm_basic_rates =
+ bss_conf->basic_rates;
+ else
+ il->staging.ofdm_basic_rates =
+ bss_conf->basic_rates >> 4;
+ il->staging.cck_basic_rates =
+ bss_conf->basic_rates & 0xF;
+ */
+ }
+
+ if (changes & BSS_CHANGED_HT) {
+ il_ht_conf(il, vif);
+
+ if (il->ops->set_rxon_chain)
+ il->ops->set_rxon_chain(il);
+ }
+
+ if (changes & BSS_CHANGED_ASSOC) {
+ D_MAC80211("ASSOC %d\n", bss_conf->assoc);
+ if (bss_conf->assoc) {
+ il->timestamp = bss_conf->sync_tsf;
+
+ if (!il_is_rfkill(il))
+ il->ops->post_associate(il);
+ } else
+ il_set_no_assoc(il, vif);
+ }
+
+ if (changes && il_is_associated(il) && bss_conf->aid) {
+ D_MAC80211("Changes (%#x) while associated\n", changes);
+ ret = il_send_rxon_assoc(il);
+ if (!ret) {
+ /* Sync active_rxon with latest change. */
+ memcpy((void *)&il->active, &il->staging,
+ sizeof(struct il_rxon_cmd));
+ }
+ }
+
+ if (changes & BSS_CHANGED_BEACON_ENABLED) {
+ if (vif->bss_conf.enable_beacon) {
+ memcpy(il->staging.bssid_addr, bss_conf->bssid,
+ ETH_ALEN);
+ memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
+ il->ops->config_ap(il);
+ } else
+ il_set_no_assoc(il, vif);
+ }
+
+ if (changes & BSS_CHANGED_IBSS) {
+ ret = il->ops->manage_ibss_station(il, vif,
+ bss_conf->ibss_joined);
+ if (ret)
+ IL_ERR("failed to %s IBSS station %pM\n",
+ bss_conf->ibss_joined ? "add" : "remove",
+ bss_conf->bssid);
+ }
+
+ D_MAC80211("leave\n");
+ mutex_unlock(&il->mutex);
+}
+EXPORT_SYMBOL(il_mac_bss_info_changed);
+
+irqreturn_t
+il_isr(int irq, void *data)
+{
+ struct il_priv *il = data;
+ u32 inta, inta_mask;
+ u32 inta_fh;
+ unsigned long flags;
+ if (!il)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&il->lock, flags);
+
+ /* Disable (but don't clear!) interrupts here to avoid
+ * back-to-back ISRs and sporadic interrupts from our NIC.
+ * If we have something to service, the tasklet will re-enable ints.
+ * If we *don't* have something, we'll re-enable before leaving here. */
+ inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */
+ _il_wr(il, CSR_INT_MASK, 0x00000000);
+
+ /* Discover which interrupts are active/pending */
+ inta = _il_rd(il, CSR_INT);
+ inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
+
+ /* Ignore interrupt if there's nothing in NIC to service.
+ * This may be due to IRQ shared with another device,
+ * or due to sporadic interrupts thrown from our NIC. */
+ if (!inta && !inta_fh) {
+ D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n");
+ goto none;
+ }
+
+ if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) {
+ /* Hardware disappeared. It might have already raised
+ * an interrupt */
+ IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta);
+ goto unplugged;
+ }
+
+ D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask,
+ inta_fh);
+
+ inta &= ~CSR_INT_BIT_SCD;
+
+ /* il_irq_tasklet() will service interrupts and re-enable them */
+ if (likely(inta || inta_fh))
+ tasklet_schedule(&il->irq_tasklet);
+
+unplugged:
+ spin_unlock_irqrestore(&il->lock, flags);
+ return IRQ_HANDLED;
+
+none:
+ /* re-enable interrupts here since we don't have anything to service. */
+ /* only Re-enable if disabled by irq */
+ if (test_bit(S_INT_ENABLED, &il->status))
+ il_enable_interrupts(il);
+ spin_unlock_irqrestore(&il->lock, flags);
+ return IRQ_NONE;
+}
+EXPORT_SYMBOL(il_isr);
+
+/*
+ * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this
+ * function.
+ */
+void
+il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info,
+ __le16 fc, __le32 *tx_flags)
+{
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ *tx_flags |= TX_CMD_FLG_RTS_MSK;
+ *tx_flags &= ~TX_CMD_FLG_CTS_MSK;
+ *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
+
+ if (!ieee80211_is_mgmt(fc))
+ return;
+
+ switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
+ case cpu_to_le16(IEEE80211_STYPE_AUTH):
+ case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
+ case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
+ case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
+ *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+ *tx_flags |= TX_CMD_FLG_CTS_MSK;
+ break;
+ }
+ } else if (info->control.rates[0].
+ flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+ *tx_flags |= TX_CMD_FLG_CTS_MSK;
+ *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
+ }
+}
+EXPORT_SYMBOL(il_tx_cmd_protection);
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h
index ce52cf114fde..ce52cf114fde 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/intel/iwlegacy/common.h
diff --git a/drivers/net/wireless/iwlegacy/csr.h b/drivers/net/wireless/intel/iwlegacy/csr.h
index 9138e15004fa..9138e15004fa 100644
--- a/drivers/net/wireless/iwlegacy/csr.h
+++ b/drivers/net/wireless/intel/iwlegacy/csr.h
diff --git a/drivers/net/wireless/iwlegacy/debug.c b/drivers/net/wireless/intel/iwlegacy/debug.c
index 908b9f4fef6f..908b9f4fef6f 100644
--- a/drivers/net/wireless/iwlegacy/debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/debug.c
diff --git a/drivers/net/wireless/iwlegacy/iwl-spectrum.h b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h
index 85fe48e520f9..85fe48e520f9 100644
--- a/drivers/net/wireless/iwlegacy/iwl-spectrum.h
+++ b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h
diff --git a/drivers/net/wireless/iwlegacy/prph.h b/drivers/net/wireless/intel/iwlegacy/prph.h
index ffec4b4a248a..ffec4b4a248a 100644
--- a/drivers/net/wireless/iwlegacy/prph.h
+++ b/drivers/net/wireless/intel/iwlegacy/prph.h
diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig
new file mode 100644
index 000000000000..866067789330
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/Kconfig
@@ -0,0 +1,161 @@
+config IWLWIFI
+ tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
+ depends on PCI && MAC80211 && HAS_IOMEM
+ select FW_LOADER
+ ---help---
+ Select to build the driver supporting the:
+
+ Intel Wireless WiFi Link Next-Gen AGN
+
+ This option enables support for use with the following hardware:
+ Intel Wireless WiFi Link 6250AGN Adapter
+ Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN)
+ Intel WiFi Link 1000BGN
+ Intel Wireless WiFi 5150AGN
+ Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN
+ Intel 6005 Series Wi-Fi Adapters
+ Intel 6030 Series Wi-Fi Adapters
+ Intel Wireless WiFi Link 6150BGN 2 Adapter
+ Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN)
+ Intel 2000 Series Wi-Fi Adapters
+ Intel 7260 Wi-Fi Adapter
+ Intel 3160 Wi-Fi Adapter
+ Intel 7265 Wi-Fi Adapter
+ Intel 8260 Wi-Fi Adapter
+ Intel 3165 Wi-Fi Adapter
+
+
+ This driver uses the kernel's mac80211 subsystem.
+
+ In order to use this driver, you will need a firmware
+ image for it. You can obtain the microcode from:
+
+ <http://wireless.kernel.org/en/users/Drivers/iwlwifi>.
+
+ The firmware is typically installed in /lib/firmware. You can
+ look in the hotplug script /etc/hotplug/firmware.agent to
+ determine which directory FIRMWARE_DIR is set to when the script
+ runs.
+
+ If you want to compile the driver as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/kbuild/modules.txt>. The
+ module will be called iwlwifi.
+
+if IWLWIFI
+
+config IWLWIFI_LEDS
+ bool
+ depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
+ select LEDS_TRIGGERS
+ select MAC80211_LEDS
+ default y
+
+config IWLDVM
+ tristate "Intel Wireless WiFi DVM Firmware support"
+ depends on m
+ help
+ This is the driver that supports the DVM firmware. The list
+ of the devices that use this firmware is available here:
+ https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
+
+config IWLMVM
+ tristate "Intel Wireless WiFi MVM Firmware support"
+ select WANT_DEV_COREDUMP
+ help
+ This is the driver that supports the MVM firmware. The list
+ of the devices that use this firmware is available here:
+ https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
+
+# don't call it _MODULE -- will confuse Kconfig/fixdep/...
+config IWLWIFI_OPMODE_MODULAR
+ bool
+ default y if IWLDVM=m
+ default y if IWLMVM=m
+
+comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
+ depends on IWLDVM=n && IWLMVM=n
+
+config IWLWIFI_BCAST_FILTERING
+ bool "Enable broadcast filtering"
+ depends on IWLMVM
+ help
+ Say Y here to enable default bcast filtering configuration.
+
+ Enabling broadcast filtering will drop any incoming wireless
+ broadcast frames, except some very specific predefined
+ patterns (e.g. incoming arp requests).
+
+ If unsure, don't enable this option, as some programs might
+ expect incoming broadcasts for their normal operations.
+
+config IWLWIFI_UAPSD
+ bool "enable U-APSD by default"
+ depends on IWLMVM
+ help
+ Say Y here to enable U-APSD by default. This may cause
+ interoperability problems with some APs, manifesting in lower than
+ expected throughput due to those APs not enabling aggregation
+
+ If unsure, say N.
+
+menu "Debugging Options"
+
+config IWLWIFI_DEBUG
+ bool "Enable full debugging output in the iwlwifi driver"
+ ---help---
+ This option will enable debug tracing output for the iwlwifi drivers
+
+ This will result in the kernel module being ~100k larger. You can
+ control which debug output is sent to the kernel log by setting the
+ value in
+
+ /sys/module/iwlwifi/parameters/debug
+
+ This entry will only exist if this option is enabled.
+
+ To set a value, simply echo an 8-byte hex value to the same file:
+
+ % echo 0x43fff > /sys/module/iwlwifi/parameters/debug
+
+ You can find the list of debug mask values in:
+ drivers/net/wireless/iwlwifi/iwl-debug.h
+
+ If this is your first time using this driver, you should say Y here
+ as the debug information can assist others in helping you resolve
+ any problems you may encounter.
+
+config IWLWIFI_DEBUGFS
+ bool "iwlwifi debugfs support"
+ depends on MAC80211_DEBUGFS
+ ---help---
+ Enable creation of debugfs files for the iwlwifi drivers. This
+ is a low-impact option that allows getting insight into the
+ driver's state at runtime.
+
+config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
+ bool "Experimental uCode support"
+ depends on IWLWIFI_DEBUG
+ ---help---
+ Enable use of experimental ucode for testing and debugging.
+
+config IWLWIFI_DEVICE_TRACING
+ bool "iwlwifi device access tracing"
+ depends on EVENT_TRACING
+ default y
+ help
+ Say Y here to trace all commands, including TX frames and IO
+ accesses, sent to the device. If you say yes, iwlwifi will
+ register with the ftrace framework for event tracing and dump
+ all this information to the ringbuffer, you may need to
+ increase the ringbuffer size. See the ftrace documentation
+ for more information.
+
+ When tracing is not enabled, this option still has some
+ (though rather small) overhead.
+
+ If unsure, say Y so we can help you better when problems
+ occur.
+endmenu
+
+endif
diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile
new file mode 100644
index 000000000000..05828c61d1ab
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/Makefile
@@ -0,0 +1,23 @@
+# common
+obj-$(CONFIG_IWLWIFI) += iwlwifi.o
+iwlwifi-objs += iwl-io.o
+iwlwifi-objs += iwl-drv.o
+iwlwifi-objs += iwl-debug.o
+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-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
+iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwl-9000.o
+iwlwifi-objs += iwl-trans.o
+
+iwlwifi-objs += $(iwlwifi-m)
+
+iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
+
+ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
+
+obj-$(CONFIG_IWLDVM) += dvm/
+obj-$(CONFIG_IWLMVM) += mvm/
+
+CFLAGS_iwl-devtrace.o := -I$(src)
diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile
index 4d19685f31c3..4d19685f31c3 100644
--- a/drivers/net/wireless/iwlwifi/dvm/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h
new file mode 100644
index 000000000000..9de277c6c420
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h
@@ -0,0 +1,476 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_agn_h__
+#define __iwl_agn_h__
+
+#include "iwl-config.h"
+
+#include "dev.h"
+
+/* The first 11 queues (0-10) are used otherwise */
+#define IWLAGN_FIRST_AMPDU_QUEUE 11
+
+/* AUX (TX during scan dwell) queue */
+#define IWL_AUX_QUEUE 10
+
+#define IWL_INVALID_STATION 255
+
+/* device operations */
+extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_105_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
+
+
+#define TIME_UNIT 1024
+
+/*****************************************************
+* DRIVER STATUS FUNCTIONS
+******************************************************/
+#define STATUS_RF_KILL_HW 0
+#define STATUS_CT_KILL 1
+#define STATUS_ALIVE 2
+#define STATUS_READY 3
+#define STATUS_EXIT_PENDING 5
+#define STATUS_STATISTICS 6
+#define STATUS_SCANNING 7
+#define STATUS_SCAN_ABORTING 8
+#define STATUS_SCAN_HW 9
+#define STATUS_FW_ERROR 10
+#define STATUS_CHANNEL_SWITCH_PENDING 11
+#define STATUS_SCAN_COMPLETE 12
+#define STATUS_POWER_PMI 13
+
+struct iwl_ucode_capabilities;
+
+extern const struct ieee80211_ops iwlagn_hw_ops;
+
+static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd)
+{
+ hdr->op_code = cmd;
+ hdr->first_group = 0;
+ hdr->groups_num = 1;
+ hdr->data_valid = 1;
+}
+
+void iwl_down(struct iwl_priv *priv);
+void iwl_cancel_deferred_work(struct iwl_priv *priv);
+void iwlagn_prepare_restart(struct iwl_priv *priv);
+void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb);
+
+bool iwl_check_for_ct_kill(struct iwl_priv *priv);
+
+void iwlagn_lift_passive_no_rx(struct iwl_priv *priv);
+
+/* MAC80211 */
+struct ieee80211_hw *iwl_alloc_all(void);
+int iwlagn_mac_setup_register(struct iwl_priv *priv,
+ const struct iwl_ucode_capabilities *capa);
+void iwlagn_mac_unregister(struct iwl_priv *priv);
+
+/* commands */
+int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
+int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id,
+ u32 flags, u16 len, const void *data);
+
+/* RXON */
+void iwl_connection_init_rx_config(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx);
+int iwlagn_set_pan_params(struct iwl_priv *priv);
+int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
+void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
+int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed);
+void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changes);
+void iwlagn_config_ht40(struct ieee80211_conf *conf,
+ struct iwl_rxon_context *ctx);
+void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf);
+void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch,
+ struct iwl_rxon_context *ctx);
+void iwl_set_flags_for_band(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ enum ieee80211_band band,
+ struct ieee80211_vif *vif);
+
+/* uCode */
+int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type);
+void iwl_send_prio_tbl(struct iwl_priv *priv);
+int iwl_init_alive_start(struct iwl_priv *priv);
+int iwl_run_init_ucode(struct iwl_priv *priv);
+int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
+ enum iwl_ucode_type ucode_type);
+int iwl_send_calib_results(struct iwl_priv *priv);
+int iwl_calib_set(struct iwl_priv *priv,
+ const struct iwl_calib_hdr *cmd, int len);
+void iwl_calib_free_results(struct iwl_priv *priv);
+int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+ char **buf);
+int iwlagn_hw_valid_rtc_data_addr(u32 addr);
+
+/* lib */
+int iwlagn_send_tx_power(struct iwl_priv *priv);
+void iwlagn_temperature(struct iwl_priv *priv);
+int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk);
+void iwlagn_dev_txfifo_flush(struct iwl_priv *priv);
+int iwlagn_send_beacon_cmd(struct iwl_priv *priv);
+int iwl_send_statistics_request(struct iwl_priv *priv,
+ u8 flags, bool clear);
+
+static inline const struct ieee80211_supported_band *iwl_get_hw_mode(
+ struct iwl_priv *priv, enum ieee80211_band band)
+{
+ return priv->hw->wiphy->bands[band];
+}
+
+#ifdef CONFIG_PM_SLEEP
+int iwlagn_send_patterns(struct iwl_priv *priv,
+ struct cfg80211_wowlan *wowlan);
+int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan);
+#endif
+
+/* rx */
+int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band);
+void iwl_setup_rx_handlers(struct iwl_priv *priv);
+void iwl_chswitch_done(struct iwl_priv *priv, bool is_success);
+
+
+/* tx */
+int iwlagn_tx_skb(struct iwl_priv *priv,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb);
+int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u8 buf_size);
+int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid);
+int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid);
+void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb);
+
+static inline u32 iwl_tx_status_to_mac80211(u32 status)
+{
+ status &= TX_STATUS_MSK;
+
+ switch (status) {
+ case TX_STATUS_SUCCESS:
+ case TX_STATUS_DIRECT_DONE:
+ return IEEE80211_TX_STAT_ACK;
+ case TX_STATUS_FAIL_DEST_PS:
+ case TX_STATUS_FAIL_PASSIVE_NO_RX:
+ return IEEE80211_TX_STAT_TX_FILTERED;
+ default:
+ return 0;
+ }
+}
+
+static inline bool iwl_is_tx_success(u32 status)
+{
+ status &= TX_STATUS_MSK;
+ return (status == TX_STATUS_SUCCESS) ||
+ (status == TX_STATUS_DIRECT_DONE);
+}
+
+u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid);
+
+/* scan */
+void iwlagn_post_scan(struct iwl_priv *priv);
+int iwl_force_rf_reset(struct iwl_priv *priv, bool external);
+void iwl_init_scan_params(struct iwl_priv *priv);
+int iwl_scan_cancel(struct iwl_priv *priv);
+void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
+void iwl_force_scan_end(struct iwl_priv *priv);
+void iwl_internal_short_hw_scan(struct iwl_priv *priv);
+void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
+void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
+void iwl_cancel_scan_deferred_work(struct iwl_priv *priv);
+int __must_check iwl_scan_initiate(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ enum iwl_scan_type scan_type,
+ enum ieee80211_band band);
+
+/* For faster active scanning, scan will move to the next channel if fewer than
+ * PLCP_QUIET_THRESH packets are heard on this channel within
+ * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell
+ * time if it's a quiet channel (nothing responded to our probe, and there's
+ * no other traffic).
+ * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */
+#define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */
+#define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */
+
+#define IWL_SCAN_CHECK_WATCHDOG (HZ * 15)
+
+
+/* bt coex */
+void iwlagn_send_advance_bt_config(struct iwl_priv *priv);
+void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv);
+void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv);
+void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv);
+void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv);
+void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena);
+
+static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv)
+{
+ return priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist;
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_get_tx_fail_reason(u32 status);
+const char *iwl_get_agg_tx_fail_reason(u16 status);
+#else
+static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; }
+static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; }
+#endif
+
+
+/* station management */
+int iwlagn_manage_ibss_station(struct iwl_priv *priv,
+ struct ieee80211_vif *vif, bool add);
+#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
+#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
+#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of
+ being activated */
+#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211;
+ (this is for the IBSS BSSID stations) */
+#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */
+
+
+void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
+void iwl_clear_ucode_stations(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx);
+void iwl_dealloc_bcast_stations(struct iwl_priv *priv);
+int iwl_get_free_ucode_key_offset(struct iwl_priv *priv);
+int iwl_send_add_sta(struct iwl_priv *priv,
+ struct iwl_addsta_cmd *sta, u8 flags);
+int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap,
+ struct ieee80211_sta *sta, u8 *sta_id_r);
+int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
+ const u8 *addr);
+void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id,
+ const u8 *addr);
+u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap, struct ieee80211_sta *sta);
+
+int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct iwl_link_quality_cmd *lq, u8 flags, bool init);
+void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb);
+int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct ieee80211_sta *sta);
+
+bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_sta *sta);
+
+static inline int iwl_sta_id(struct ieee80211_sta *sta)
+{
+ if (WARN_ON(!sta))
+ return IWL_INVALID_STATION;
+
+ return ((struct iwl_station_priv *)sta->drv_priv)->sta_id;
+}
+
+int iwlagn_alloc_bcast_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx);
+int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, u8 *sta_id_r);
+int iwl_remove_default_wep_key(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *key);
+int iwl_set_default_wep_key(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *key);
+int iwl_restore_default_wep_keys(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx);
+int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *key,
+ struct ieee80211_sta *sta);
+int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *key,
+ struct ieee80211_sta *sta);
+void iwl_update_tkip_key(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta, u32 iv32, u16 *phase1key);
+int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
+int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
+ int tid, u16 ssn);
+int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
+ int tid);
+void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt);
+int iwl_update_bcast_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx);
+int iwl_update_bcast_stations(struct iwl_priv *priv);
+
+/* rate */
+static inline u32 iwl_ant_idx_to_flags(u8 ant_idx)
+{
+ return BIT(ant_idx) << RATE_MCS_ANT_POS;
+}
+
+static inline u8 iwl_hw_get_rate(__le32 rate_n_flags)
+{
+ return le32_to_cpu(rate_n_flags) & RATE_MCS_RATE_MSK;
+}
+
+static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
+{
+ return cpu_to_le32(flags|(u32)rate);
+}
+
+int iwl_alive_start(struct iwl_priv *priv);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+void iwl_print_rx_config_cmd(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctxid);
+#else
+static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctxid)
+{
+}
+#endif
+
+/* status checks */
+
+static inline int iwl_is_ready(struct iwl_priv *priv)
+{
+ /* The adapter is 'ready' if READY EXIT_PENDING is not set */
+ return test_bit(STATUS_READY, &priv->status) &&
+ !test_bit(STATUS_EXIT_PENDING, &priv->status);
+}
+
+static inline int iwl_is_alive(struct iwl_priv *priv)
+{
+ return test_bit(STATUS_ALIVE, &priv->status);
+}
+
+static inline int iwl_is_rfkill(struct iwl_priv *priv)
+{
+ return test_bit(STATUS_RF_KILL_HW, &priv->status);
+}
+
+static inline int iwl_is_ctkill(struct iwl_priv *priv)
+{
+ return test_bit(STATUS_CT_KILL, &priv->status);
+}
+
+static inline int iwl_is_ready_rf(struct iwl_priv *priv)
+{
+ if (iwl_is_rfkill(priv))
+ return 0;
+
+ return iwl_is_ready(priv);
+}
+
+static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state)
+{
+ if (state)
+ set_bit(STATUS_POWER_PMI, &priv->status);
+ else
+ clear_bit(STATUS_POWER_PMI, &priv->status);
+ iwl_trans_set_pmi(priv->trans, state);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir);
+#else
+static inline int iwl_dbgfs_register(struct iwl_priv *priv,
+ struct dentry *dbgfs_dir)
+{
+ return 0;
+}
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \
+do { \
+ if (!iwl_is_rfkill((m))) \
+ IWL_ERR(m, fmt, ##args); \
+ else \
+ __iwl_err((m)->dev, true, \
+ !iwl_have_debug_level(IWL_DL_RADIO), \
+ fmt, ##args); \
+} while (0)
+#else
+#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \
+do { \
+ if (!iwl_is_rfkill((m))) \
+ IWL_ERR(m, fmt, ##args); \
+ else \
+ __iwl_err((m)->dev, true, true, fmt, ##args); \
+} while (0)
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+#endif /* __iwl_agn_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c
new file mode 100644
index 000000000000..07a4c644fb9b
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c
@@ -0,0 +1,1113 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include "iwl-trans.h"
+
+#include "dev.h"
+#include "calib.h"
+#include "agn.h"
+
+/*****************************************************************************
+ * INIT calibrations framework
+ *****************************************************************************/
+
+/* Opaque calibration results */
+struct iwl_calib_result {
+ struct list_head list;
+ size_t cmd_len;
+ struct iwl_calib_hdr hdr;
+ /* data follows */
+};
+
+struct statistics_general_data {
+ u32 beacon_silence_rssi_a;
+ u32 beacon_silence_rssi_b;
+ u32 beacon_silence_rssi_c;
+ u32 beacon_energy_a;
+ u32 beacon_energy_b;
+ u32 beacon_energy_c;
+};
+
+int iwl_send_calib_results(struct iwl_priv *priv)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = REPLY_PHY_CALIBRATION_CMD,
+ };
+ struct iwl_calib_result *res;
+
+ list_for_each_entry(res, &priv->calib_results, list) {
+ int ret;
+
+ hcmd.len[0] = res->cmd_len;
+ hcmd.data[0] = &res->hdr;
+ hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+ ret = iwl_dvm_send_cmd(priv, &hcmd);
+ if (ret) {
+ IWL_ERR(priv, "Error %d on calib cmd %d\n",
+ ret, res->hdr.op_code);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int iwl_calib_set(struct iwl_priv *priv,
+ const struct iwl_calib_hdr *cmd, int len)
+{
+ struct iwl_calib_result *res, *tmp;
+
+ res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr),
+ GFP_ATOMIC);
+ if (!res)
+ return -ENOMEM;
+ memcpy(&res->hdr, cmd, len);
+ res->cmd_len = len;
+
+ list_for_each_entry(tmp, &priv->calib_results, list) {
+ if (tmp->hdr.op_code == res->hdr.op_code) {
+ list_replace(&tmp->list, &res->list);
+ kfree(tmp);
+ return 0;
+ }
+ }
+
+ /* wasn't in list already */
+ list_add_tail(&res->list, &priv->calib_results);
+
+ return 0;
+}
+
+void iwl_calib_free_results(struct iwl_priv *priv)
+{
+ struct iwl_calib_result *res, *tmp;
+
+ list_for_each_entry_safe(res, tmp, &priv->calib_results, list) {
+ list_del(&res->list);
+ kfree(res);
+ }
+}
+
+/*****************************************************************************
+ * RUNTIME calibrations framework
+ *****************************************************************************/
+
+/* "false alarms" are signals that our DSP tries to lock onto,
+ * but then determines that they are either noise, or transmissions
+ * from a distant wireless network (also "noise", really) that get
+ * "stepped on" by stronger transmissions within our own network.
+ * This algorithm attempts to set a sensitivity level that is high
+ * enough to receive all of our own network traffic, but not so
+ * high that our DSP gets too busy trying to lock onto non-network
+ * activity/noise. */
+static int iwl_sens_energy_cck(struct iwl_priv *priv,
+ u32 norm_fa,
+ u32 rx_enable_time,
+ struct statistics_general_data *rx_info)
+{
+ u32 max_nrg_cck = 0;
+ int i = 0;
+ u8 max_silence_rssi = 0;
+ u32 silence_ref = 0;
+ u8 silence_rssi_a = 0;
+ u8 silence_rssi_b = 0;
+ u8 silence_rssi_c = 0;
+ u32 val;
+
+ /* "false_alarms" values below are cross-multiplications to assess the
+ * numbers of false alarms within the measured period of actual Rx
+ * (Rx is off when we're txing), vs the min/max expected false alarms
+ * (some should be expected if rx is sensitive enough) in a
+ * hypothetical listening period of 200 time units (TU), 204.8 msec:
+ *
+ * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
+ *
+ * */
+ u32 false_alarms = norm_fa * 200 * 1024;
+ u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
+ u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
+ struct iwl_sensitivity_data *data = NULL;
+ const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
+
+ data = &(priv->sensitivity_data);
+
+ data->nrg_auto_corr_silence_diff = 0;
+
+ /* Find max silence rssi among all 3 receivers.
+ * This is background noise, which may include transmissions from other
+ * networks, measured during silence before our network's beacon */
+ silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
+ ALL_BAND_FILTER) >> 8);
+ silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
+ ALL_BAND_FILTER) >> 8);
+ silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
+ ALL_BAND_FILTER) >> 8);
+
+ val = max(silence_rssi_b, silence_rssi_c);
+ max_silence_rssi = max(silence_rssi_a, (u8) val);
+
+ /* Store silence rssi in 20-beacon history table */
+ data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
+ data->nrg_silence_idx++;
+ if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
+ data->nrg_silence_idx = 0;
+
+ /* Find max silence rssi across 20 beacon history */
+ for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
+ val = data->nrg_silence_rssi[i];
+ silence_ref = max(silence_ref, val);
+ }
+ IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n",
+ silence_rssi_a, silence_rssi_b, silence_rssi_c,
+ silence_ref);
+
+ /* Find max rx energy (min value!) among all 3 receivers,
+ * measured during beacon frame.
+ * Save it in 10-beacon history table. */
+ i = data->nrg_energy_idx;
+ val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
+ data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
+
+ data->nrg_energy_idx++;
+ if (data->nrg_energy_idx >= 10)
+ data->nrg_energy_idx = 0;
+
+ /* Find min rx energy (max value) across 10 beacon history.
+ * This is the minimum signal level that we want to receive well.
+ * Add backoff (margin so we don't miss slightly lower energy frames).
+ * This establishes an upper bound (min value) for energy threshold. */
+ max_nrg_cck = data->nrg_value[0];
+ for (i = 1; i < 10; i++)
+ max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
+ max_nrg_cck += 6;
+
+ IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
+ rx_info->beacon_energy_a, rx_info->beacon_energy_b,
+ rx_info->beacon_energy_c, max_nrg_cck - 6);
+
+ /* Count number of consecutive beacons with fewer-than-desired
+ * false alarms. */
+ if (false_alarms < min_false_alarms)
+ data->num_in_cck_no_fa++;
+ else
+ data->num_in_cck_no_fa = 0;
+ IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n",
+ data->num_in_cck_no_fa);
+
+ /* If we got too many false alarms this time, reduce sensitivity */
+ if ((false_alarms > max_false_alarms) &&
+ (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) {
+ IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n",
+ false_alarms, max_false_alarms);
+ IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n");
+ data->nrg_curr_state = IWL_FA_TOO_MANY;
+ /* Store for "fewer than desired" on later beacon */
+ data->nrg_silence_ref = silence_ref;
+
+ /* increase energy threshold (reduce nrg value)
+ * to decrease sensitivity */
+ data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK;
+ /* Else if we got fewer than desired, increase sensitivity */
+ } else if (false_alarms < min_false_alarms) {
+ data->nrg_curr_state = IWL_FA_TOO_FEW;
+
+ /* Compare silence level with silence level for most recent
+ * healthy number or too many false alarms */
+ data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
+ (s32)silence_ref;
+
+ IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n",
+ false_alarms, min_false_alarms,
+ data->nrg_auto_corr_silence_diff);
+
+ /* Increase value to increase sensitivity, but only if:
+ * 1a) previous beacon did *not* have *too many* false alarms
+ * 1b) AND there's a significant difference in Rx levels
+ * from a previous beacon with too many, or healthy # FAs
+ * OR 2) We've seen a lot of beacons (100) with too few
+ * false alarms */
+ if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
+ ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
+ (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
+
+ IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n");
+ /* Increase nrg value to increase sensitivity */
+ val = data->nrg_th_cck + NRG_STEP_CCK;
+ data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val);
+ } else {
+ IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n");
+ }
+
+ /* Else we got a healthy number of false alarms, keep status quo */
+ } else {
+ IWL_DEBUG_CALIB(priv, " FA in safe zone\n");
+ data->nrg_curr_state = IWL_FA_GOOD_RANGE;
+
+ /* Store for use in "fewer than desired" with later beacon */
+ data->nrg_silence_ref = silence_ref;
+
+ /* If previous beacon had too many false alarms,
+ * give it some extra margin by reducing sensitivity again
+ * (but don't go below measured energy of desired Rx) */
+ if (data->nrg_prev_state == IWL_FA_TOO_MANY) {
+ IWL_DEBUG_CALIB(priv, "... increasing margin\n");
+ if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
+ data->nrg_th_cck -= NRG_MARGIN;
+ else
+ data->nrg_th_cck = max_nrg_cck;
+ }
+ }
+
+ /* Make sure the energy threshold does not go above the measured
+ * energy of the desired Rx signals (reduced by backoff margin),
+ * or else we might start missing Rx frames.
+ * Lower value is higher energy, so we use max()!
+ */
+ data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
+ IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck);
+
+ data->nrg_prev_state = data->nrg_curr_state;
+
+ /* Auto-correlation CCK algorithm */
+ if (false_alarms > min_false_alarms) {
+
+ /* increase auto_corr values to decrease sensitivity
+ * so the DSP won't be disturbed by the noise
+ */
+ if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
+ data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
+ else {
+ val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
+ data->auto_corr_cck =
+ min((u32)ranges->auto_corr_max_cck, val);
+ }
+ val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
+ data->auto_corr_cck_mrc =
+ min((u32)ranges->auto_corr_max_cck_mrc, val);
+ } else if ((false_alarms < min_false_alarms) &&
+ ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
+ (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
+
+ /* Decrease auto_corr values to increase sensitivity */
+ val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
+ data->auto_corr_cck =
+ max((u32)ranges->auto_corr_min_cck, val);
+ val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
+ data->auto_corr_cck_mrc =
+ max((u32)ranges->auto_corr_min_cck_mrc, val);
+ }
+
+ return 0;
+}
+
+
+static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
+ u32 norm_fa,
+ u32 rx_enable_time)
+{
+ u32 val;
+ u32 false_alarms = norm_fa * 200 * 1024;
+ u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
+ u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
+ struct iwl_sensitivity_data *data = NULL;
+ const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
+
+ data = &(priv->sensitivity_data);
+
+ /* If we got too many false alarms this time, reduce sensitivity */
+ if (false_alarms > max_false_alarms) {
+
+ IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n",
+ false_alarms, max_false_alarms);
+
+ val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm =
+ min((u32)ranges->auto_corr_max_ofdm, val);
+
+ val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm_mrc =
+ min((u32)ranges->auto_corr_max_ofdm_mrc, val);
+
+ val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm_x1 =
+ min((u32)ranges->auto_corr_max_ofdm_x1, val);
+
+ val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm_mrc_x1 =
+ min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val);
+ }
+
+ /* Else if we got fewer than desired, increase sensitivity */
+ else if (false_alarms < min_false_alarms) {
+
+ IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n",
+ false_alarms, min_false_alarms);
+
+ val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm =
+ max((u32)ranges->auto_corr_min_ofdm, val);
+
+ val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm_mrc =
+ max((u32)ranges->auto_corr_min_ofdm_mrc, val);
+
+ val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm_x1 =
+ max((u32)ranges->auto_corr_min_ofdm_x1, val);
+
+ val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
+ data->auto_corr_ofdm_mrc_x1 =
+ max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val);
+ } else {
+ IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n",
+ min_false_alarms, false_alarms, max_false_alarms);
+ }
+ return 0;
+}
+
+static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv,
+ struct iwl_sensitivity_data *data,
+ __le16 *tbl)
+{
+ tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
+ cpu_to_le16((u16)data->auto_corr_ofdm);
+ tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
+ cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
+ tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
+ cpu_to_le16((u16)data->auto_corr_ofdm_x1);
+ tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
+ cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
+
+ tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
+ cpu_to_le16((u16)data->auto_corr_cck);
+ tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
+ cpu_to_le16((u16)data->auto_corr_cck_mrc);
+
+ tbl[HD_MIN_ENERGY_CCK_DET_INDEX] =
+ cpu_to_le16((u16)data->nrg_th_cck);
+ tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] =
+ cpu_to_le16((u16)data->nrg_th_ofdm);
+
+ tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
+ cpu_to_le16(data->barker_corr_th_min);
+ tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
+ cpu_to_le16(data->barker_corr_th_min_mrc);
+ tbl[HD_OFDM_ENERGY_TH_IN_INDEX] =
+ cpu_to_le16(data->nrg_th_cca);
+
+ IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
+ data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
+ data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
+ data->nrg_th_ofdm);
+
+ IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n",
+ data->auto_corr_cck, data->auto_corr_cck_mrc,
+ data->nrg_th_cck);
+}
+
+/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
+static int iwl_sensitivity_write(struct iwl_priv *priv)
+{
+ struct iwl_sensitivity_cmd cmd;
+ struct iwl_sensitivity_data *data = NULL;
+ struct iwl_host_cmd cmd_out = {
+ .id = SENSITIVITY_CMD,
+ .len = { sizeof(struct iwl_sensitivity_cmd), },
+ .flags = CMD_ASYNC,
+ .data = { &cmd, },
+ };
+
+ data = &(priv->sensitivity_data);
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]);
+
+ /* Update uCode's "work" table, and copy it to DSP */
+ cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
+
+ /* Don't send command to uCode if nothing has changed */
+ if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
+ sizeof(u16)*HD_TABLE_SIZE)) {
+ IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
+ return 0;
+ }
+
+ /* Copy table for comparison next time */
+ memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
+ sizeof(u16)*HD_TABLE_SIZE);
+
+ return iwl_dvm_send_cmd(priv, &cmd_out);
+}
+
+/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
+static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
+{
+ struct iwl_enhance_sensitivity_cmd cmd;
+ struct iwl_sensitivity_data *data = NULL;
+ struct iwl_host_cmd cmd_out = {
+ .id = SENSITIVITY_CMD,
+ .len = { sizeof(struct iwl_enhance_sensitivity_cmd), },
+ .flags = CMD_ASYNC,
+ .data = { &cmd, },
+ };
+
+ data = &(priv->sensitivity_data);
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
+
+ if (priv->lib->hd_v2) {
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
+ HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
+ HD_INA_NON_SQUARE_DET_CCK_DATA_V2;
+ cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
+ HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2;
+ } else {
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
+ HD_INA_NON_SQUARE_DET_OFDM_DATA_V1;
+ cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
+ HD_INA_NON_SQUARE_DET_CCK_DATA_V1;
+ cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
+ HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1;
+ cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
+ HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1;
+ cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
+ HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1;
+ }
+
+ /* Update uCode's "work" table, and copy it to DSP */
+ cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
+
+ /* Don't send command to uCode if nothing has changed */
+ if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]),
+ sizeof(u16)*HD_TABLE_SIZE) &&
+ !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX],
+ &(priv->enhance_sensitivity_tbl[0]),
+ sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) {
+ IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
+ return 0;
+ }
+
+ /* Copy table for comparison next time */
+ memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]),
+ sizeof(u16)*HD_TABLE_SIZE);
+ memcpy(&(priv->enhance_sensitivity_tbl[0]),
+ &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]),
+ sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES);
+
+ return iwl_dvm_send_cmd(priv, &cmd_out);
+}
+
+void iwl_init_sensitivity(struct iwl_priv *priv)
+{
+ int ret = 0;
+ int i;
+ struct iwl_sensitivity_data *data = NULL;
+ const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
+
+ if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
+ return;
+
+ IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n");
+
+ /* Clear driver's sensitivity algo data */
+ data = &(priv->sensitivity_data);
+
+ if (ranges == NULL)
+ return;
+
+ memset(data, 0, sizeof(struct iwl_sensitivity_data));
+
+ data->num_in_cck_no_fa = 0;
+ data->nrg_curr_state = IWL_FA_TOO_MANY;
+ data->nrg_prev_state = IWL_FA_TOO_MANY;
+ data->nrg_silence_ref = 0;
+ data->nrg_silence_idx = 0;
+ data->nrg_energy_idx = 0;
+
+ for (i = 0; i < 10; i++)
+ data->nrg_value[i] = 0;
+
+ for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
+ data->nrg_silence_rssi[i] = 0;
+
+ data->auto_corr_ofdm = ranges->auto_corr_min_ofdm;
+ data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
+ data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1;
+ data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
+ data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
+ data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
+ data->nrg_th_cck = ranges->nrg_th_cck;
+ data->nrg_th_ofdm = ranges->nrg_th_ofdm;
+ data->barker_corr_th_min = ranges->barker_corr_th_min;
+ data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
+ data->nrg_th_cca = ranges->nrg_th_cca;
+
+ data->last_bad_plcp_cnt_ofdm = 0;
+ data->last_fa_cnt_ofdm = 0;
+ data->last_bad_plcp_cnt_cck = 0;
+ data->last_fa_cnt_cck = 0;
+
+ if (priv->fw->enhance_sensitivity_table)
+ ret |= iwl_enhance_sensitivity_write(priv);
+ else
+ ret |= iwl_sensitivity_write(priv);
+ IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret);
+}
+
+void iwl_sensitivity_calibration(struct iwl_priv *priv)
+{
+ u32 rx_enable_time;
+ u32 fa_cck;
+ u32 fa_ofdm;
+ u32 bad_plcp_cck;
+ u32 bad_plcp_ofdm;
+ u32 norm_fa_ofdm;
+ u32 norm_fa_cck;
+ struct iwl_sensitivity_data *data = NULL;
+ struct statistics_rx_non_phy *rx_info;
+ struct statistics_rx_phy *ofdm, *cck;
+ struct statistics_general_data statis;
+
+ if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
+ return;
+
+ data = &(priv->sensitivity_data);
+
+ if (!iwl_is_any_associated(priv)) {
+ IWL_DEBUG_CALIB(priv, "<< - not associated\n");
+ return;
+ }
+
+ spin_lock_bh(&priv->statistics.lock);
+ rx_info = &priv->statistics.rx_non_phy;
+ ofdm = &priv->statistics.rx_ofdm;
+ cck = &priv->statistics.rx_cck;
+ if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
+ IWL_DEBUG_CALIB(priv, "<< invalid data.\n");
+ spin_unlock_bh(&priv->statistics.lock);
+ return;
+ }
+
+ /* Extract Statistics: */
+ rx_enable_time = le32_to_cpu(rx_info->channel_load);
+ fa_cck = le32_to_cpu(cck->false_alarm_cnt);
+ fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
+ bad_plcp_cck = le32_to_cpu(cck->plcp_err);
+ bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);
+
+ statis.beacon_silence_rssi_a =
+ le32_to_cpu(rx_info->beacon_silence_rssi_a);
+ statis.beacon_silence_rssi_b =
+ le32_to_cpu(rx_info->beacon_silence_rssi_b);
+ statis.beacon_silence_rssi_c =
+ le32_to_cpu(rx_info->beacon_silence_rssi_c);
+ statis.beacon_energy_a =
+ le32_to_cpu(rx_info->beacon_energy_a);
+ statis.beacon_energy_b =
+ le32_to_cpu(rx_info->beacon_energy_b);
+ statis.beacon_energy_c =
+ le32_to_cpu(rx_info->beacon_energy_c);
+
+ spin_unlock_bh(&priv->statistics.lock);
+
+ IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time);
+
+ if (!rx_enable_time) {
+ IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n");
+ return;
+ }
+
+ /* These statistics increase monotonically, and do not reset
+ * at each beacon. Calculate difference from last value, or just
+ * use the new statistics value if it has reset or wrapped around. */
+ if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
+ data->last_bad_plcp_cnt_cck = bad_plcp_cck;
+ else {
+ bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
+ data->last_bad_plcp_cnt_cck += bad_plcp_cck;
+ }
+
+ if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
+ data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
+ else {
+ bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
+ data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
+ }
+
+ if (data->last_fa_cnt_ofdm > fa_ofdm)
+ data->last_fa_cnt_ofdm = fa_ofdm;
+ else {
+ fa_ofdm -= data->last_fa_cnt_ofdm;
+ data->last_fa_cnt_ofdm += fa_ofdm;
+ }
+
+ if (data->last_fa_cnt_cck > fa_cck)
+ data->last_fa_cnt_cck = fa_cck;
+ else {
+ fa_cck -= data->last_fa_cnt_cck;
+ data->last_fa_cnt_cck += fa_cck;
+ }
+
+ /* Total aborted signal locks */
+ norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
+ norm_fa_cck = fa_cck + bad_plcp_cck;
+
+ IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck,
+ bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
+
+ iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
+ iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
+ if (priv->fw->enhance_sensitivity_table)
+ iwl_enhance_sensitivity_write(priv);
+ else
+ iwl_sensitivity_write(priv);
+}
+
+static inline u8 find_first_chain(u8 mask)
+{
+ if (mask & ANT_A)
+ return CHAIN_A;
+ if (mask & ANT_B)
+ return CHAIN_B;
+ return CHAIN_C;
+}
+
+/**
+ * Run disconnected antenna algorithm to find out which antennas are
+ * disconnected.
+ */
+static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
+ struct iwl_chain_noise_data *data)
+{
+ u32 active_chains = 0;
+ u32 max_average_sig;
+ u16 max_average_sig_antenna_i;
+ u8 num_tx_chains;
+ u8 first_chain;
+ u16 i = 0;
+
+ average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS;
+ average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS;
+ average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS;
+
+ if (average_sig[0] >= average_sig[1]) {
+ max_average_sig = average_sig[0];
+ max_average_sig_antenna_i = 0;
+ active_chains = (1 << max_average_sig_antenna_i);
+ } else {
+ max_average_sig = average_sig[1];
+ max_average_sig_antenna_i = 1;
+ active_chains = (1 << max_average_sig_antenna_i);
+ }
+
+ if (average_sig[2] >= max_average_sig) {
+ max_average_sig = average_sig[2];
+ max_average_sig_antenna_i = 2;
+ active_chains = (1 << max_average_sig_antenna_i);
+ }
+
+ IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n",
+ average_sig[0], average_sig[1], average_sig[2]);
+ IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n",
+ max_average_sig, max_average_sig_antenna_i);
+
+ /* Compare signal strengths for all 3 receivers. */
+ for (i = 0; i < NUM_RX_CHAINS; i++) {
+ if (i != max_average_sig_antenna_i) {
+ s32 rssi_delta = (max_average_sig - average_sig[i]);
+
+ /* If signal is very weak, compared with
+ * strongest, mark it as disconnected. */
+ if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
+ data->disconn_array[i] = 1;
+ else
+ active_chains |= (1 << i);
+ IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d "
+ "disconn_array[i] = %d\n",
+ i, rssi_delta, data->disconn_array[i]);
+ }
+ }
+
+ /*
+ * The above algorithm sometimes fails when the ucode
+ * reports 0 for all chains. It's not clear why that
+ * happens to start with, but it is then causing trouble
+ * because this can make us enable more chains than the
+ * hardware really has.
+ *
+ * To be safe, simply mask out any chains that we know
+ * are not on the device.
+ */
+ active_chains &= priv->nvm_data->valid_rx_ant;
+
+ num_tx_chains = 0;
+ for (i = 0; i < NUM_RX_CHAINS; i++) {
+ /* loops on all the bits of
+ * priv->hw_setting.valid_tx_ant */
+ u8 ant_msk = (1 << i);
+ if (!(priv->nvm_data->valid_tx_ant & ant_msk))
+ continue;
+
+ num_tx_chains++;
+ if (data->disconn_array[i] == 0)
+ /* there is a Tx antenna connected */
+ break;
+ if (num_tx_chains == priv->hw_params.tx_chains_num &&
+ data->disconn_array[i]) {
+ /*
+ * If all chains are disconnected
+ * connect the first valid tx chain
+ */
+ first_chain =
+ find_first_chain(priv->nvm_data->valid_tx_ant);
+ data->disconn_array[first_chain] = 0;
+ active_chains |= BIT(first_chain);
+ IWL_DEBUG_CALIB(priv,
+ "All Tx chains are disconnected W/A - declare %d as connected\n",
+ first_chain);
+ break;
+ }
+ }
+
+ if (active_chains != priv->nvm_data->valid_rx_ant &&
+ active_chains != priv->chain_noise_data.active_chains)
+ IWL_DEBUG_CALIB(priv,
+ "Detected that not all antennas are connected! "
+ "Connected: %#x, valid: %#x.\n",
+ active_chains,
+ priv->nvm_data->valid_rx_ant);
+
+ /* Save for use within RXON, TX, SCAN commands, etc. */
+ data->active_chains = active_chains;
+ IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n",
+ active_chains);
+}
+
+static void iwlagn_gain_computation(struct iwl_priv *priv,
+ u32 average_noise[NUM_RX_CHAINS],
+ u8 default_chain)
+{
+ int i;
+ s32 delta_g;
+ struct iwl_chain_noise_data *data = &priv->chain_noise_data;
+
+ /*
+ * Find Gain Code for the chains based on "default chain"
+ */
+ for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
+ if ((data->disconn_array[i])) {
+ data->delta_gain_code[i] = 0;
+ continue;
+ }
+
+ delta_g = (priv->lib->chain_noise_scale *
+ ((s32)average_noise[default_chain] -
+ (s32)average_noise[i])) / 1500;
+
+ /* bound gain by 2 bits value max, 3rd bit is sign */
+ data->delta_gain_code[i] =
+ min(abs(delta_g),
+ (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
+
+ if (delta_g < 0)
+ /*
+ * set negative sign ...
+ * note to Intel developers: This is uCode API format,
+ * not the format of any internal device registers.
+ * Do not change this format for e.g. 6050 or similar
+ * devices. Change format only if more resolution
+ * (i.e. more than 2 bits magnitude) is needed.
+ */
+ data->delta_gain_code[i] |= (1 << 2);
+ }
+
+ IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n",
+ data->delta_gain_code[1], data->delta_gain_code[2]);
+
+ if (!data->radio_write) {
+ struct iwl_calib_chain_noise_gain_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ iwl_set_calib_hdr(&cmd.hdr,
+ priv->phy_calib_chain_noise_gain_cmd);
+ cmd.delta_gain_1 = data->delta_gain_code[1];
+ cmd.delta_gain_2 = data->delta_gain_code[2];
+ iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+ CMD_ASYNC, sizeof(cmd), &cmd);
+
+ data->radio_write = 1;
+ data->state = IWL_CHAIN_NOISE_CALIBRATED;
+ }
+}
+
+/*
+ * Accumulate 16 beacons of signal and noise statistics for each of
+ * 3 receivers/antennas/rx-chains, then figure out:
+ * 1) Which antennas are connected.
+ * 2) Differential rx gain settings to balance the 3 receivers.
+ */
+void iwl_chain_noise_calibration(struct iwl_priv *priv)
+{
+ struct iwl_chain_noise_data *data = NULL;
+
+ u32 chain_noise_a;
+ u32 chain_noise_b;
+ u32 chain_noise_c;
+ u32 chain_sig_a;
+ u32 chain_sig_b;
+ u32 chain_sig_c;
+ u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
+ u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
+ u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
+ u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
+ u16 i = 0;
+ u16 rxon_chnum = INITIALIZATION_VALUE;
+ u16 stat_chnum = INITIALIZATION_VALUE;
+ u8 rxon_band24;
+ u8 stat_band24;
+ struct statistics_rx_non_phy *rx_info;
+
+ /*
+ * MULTI-FIXME:
+ * When we support multiple interfaces on different channels,
+ * this must be modified/fixed.
+ */
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+
+ if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
+ return;
+
+ data = &(priv->chain_noise_data);
+
+ /*
+ * Accumulate just the first "chain_noise_num_beacons" after
+ * the first association, then we're done forever.
+ */
+ if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
+ if (data->state == IWL_CHAIN_NOISE_ALIVE)
+ IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n");
+ return;
+ }
+
+ spin_lock_bh(&priv->statistics.lock);
+
+ rx_info = &priv->statistics.rx_non_phy;
+
+ if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
+ IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n");
+ spin_unlock_bh(&priv->statistics.lock);
+ return;
+ }
+
+ rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK);
+ rxon_chnum = le16_to_cpu(ctx->staging.channel);
+ stat_band24 =
+ !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK);
+ stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16;
+
+ /* Make sure we accumulate data for just the associated channel
+ * (even if scanning). */
+ if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) {
+ IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n",
+ rxon_chnum, rxon_band24);
+ spin_unlock_bh(&priv->statistics.lock);
+ return;
+ }
+
+ /*
+ * Accumulate beacon statistics values across
+ * "chain_noise_num_beacons"
+ */
+ chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
+ IN_BAND_FILTER;
+ chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
+ IN_BAND_FILTER;
+ chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
+ IN_BAND_FILTER;
+
+ chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
+ chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
+ chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
+
+ spin_unlock_bh(&priv->statistics.lock);
+
+ data->beacon_count++;
+
+ data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
+ data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
+ data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
+
+ data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
+ data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
+ data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
+
+ IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n",
+ rxon_chnum, rxon_band24, data->beacon_count);
+ IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n",
+ chain_sig_a, chain_sig_b, chain_sig_c);
+ IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n",
+ chain_noise_a, chain_noise_b, chain_noise_c);
+
+ /* If this is the "chain_noise_num_beacons", determine:
+ * 1) Disconnected antennas (using signal strengths)
+ * 2) Differential gain (using silence noise) to balance receivers */
+ if (data->beacon_count != IWL_CAL_NUM_BEACONS)
+ return;
+
+ /* Analyze signal for disconnected antenna */
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
+ /* Disable disconnected antenna algorithm for advanced
+ bt coex, assuming valid antennas are connected */
+ data->active_chains = priv->nvm_data->valid_rx_ant;
+ for (i = 0; i < NUM_RX_CHAINS; i++)
+ if (!(data->active_chains & (1<<i)))
+ data->disconn_array[i] = 1;
+ } else
+ iwl_find_disconn_antenna(priv, average_sig, data);
+
+ /* Analyze noise for rx balance */
+ average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS;
+ average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS;
+ average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS;
+
+ for (i = 0; i < NUM_RX_CHAINS; i++) {
+ if (!(data->disconn_array[i]) &&
+ (average_noise[i] <= min_average_noise)) {
+ /* This means that chain i is active and has
+ * lower noise values so far: */
+ min_average_noise = average_noise[i];
+ min_average_noise_antenna_i = i;
+ }
+ }
+
+ IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n",
+ average_noise[0], average_noise[1],
+ average_noise[2]);
+
+ IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n",
+ min_average_noise, min_average_noise_antenna_i);
+
+ iwlagn_gain_computation(
+ priv, average_noise,
+ find_first_chain(priv->nvm_data->valid_rx_ant));
+
+ /* Some power changes may have been made during the calibration.
+ * Update and commit the RXON
+ */
+ iwl_update_chain_flags(priv);
+
+ data->state = IWL_CHAIN_NOISE_DONE;
+ iwl_power_update_mode(priv, false);
+}
+
+void iwl_reset_run_time_calib(struct iwl_priv *priv)
+{
+ int i;
+ memset(&(priv->sensitivity_data), 0,
+ sizeof(struct iwl_sensitivity_data));
+ memset(&(priv->chain_noise_data), 0,
+ sizeof(struct iwl_chain_noise_data));
+ for (i = 0; i < NUM_RX_CHAINS; i++)
+ priv->chain_noise_data.delta_gain_code[i] =
+ CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
+
+ /* Ask for statistics now, the uCode will send notification
+ * periodically after association */
+ iwl_send_statistics_request(priv, CMD_ASYNC, true);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.h b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h
new file mode 100644
index 000000000000..099e3ce80ffc
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h
@@ -0,0 +1,74 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#ifndef __iwl_calib_h__
+#define __iwl_calib_h__
+
+#include "dev.h"
+#include "commands.h"
+
+void iwl_chain_noise_calibration(struct iwl_priv *priv);
+void iwl_sensitivity_calibration(struct iwl_priv *priv);
+
+void iwl_init_sensitivity(struct iwl_priv *priv);
+void iwl_reset_run_time_calib(struct iwl_priv *priv);
+
+#endif /* __iwl_calib_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h
new file mode 100644
index 000000000000..2ab2773655a8
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h
@@ -0,0 +1,4008 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+/*
+ * Please use this file (commands.h) only for uCode API definitions.
+ * Please use iwl-xxxx-hw.h for hardware-related definitions.
+ * Please use dev.h for driver implementation definitions.
+ */
+
+#ifndef __iwl_commands_h__
+#define __iwl_commands_h__
+
+#include <linux/ieee80211.h>
+#include <linux/types.h>
+
+
+enum {
+ REPLY_ALIVE = 0x1,
+ REPLY_ERROR = 0x2,
+ REPLY_ECHO = 0x3, /* test command */
+
+ /* RXON and QOS commands */
+ REPLY_RXON = 0x10,
+ REPLY_RXON_ASSOC = 0x11,
+ REPLY_QOS_PARAM = 0x13,
+ REPLY_RXON_TIMING = 0x14,
+
+ /* Multi-Station support */
+ REPLY_ADD_STA = 0x18,
+ REPLY_REMOVE_STA = 0x19,
+ REPLY_REMOVE_ALL_STA = 0x1a, /* not used */
+ REPLY_TXFIFO_FLUSH = 0x1e,
+
+ /* Security */
+ REPLY_WEPKEY = 0x20,
+
+ /* RX, TX, LEDs */
+ REPLY_TX = 0x1c,
+ REPLY_LEDS_CMD = 0x48,
+ REPLY_TX_LINK_QUALITY_CMD = 0x4e,
+
+ /* WiMAX coexistence */
+ COEX_PRIORITY_TABLE_CMD = 0x5a,
+ COEX_MEDIUM_NOTIFICATION = 0x5b,
+ COEX_EVENT_CMD = 0x5c,
+
+ /* Calibration */
+ TEMPERATURE_NOTIFICATION = 0x62,
+ CALIBRATION_CFG_CMD = 0x65,
+ CALIBRATION_RES_NOTIFICATION = 0x66,
+ CALIBRATION_COMPLETE_NOTIFICATION = 0x67,
+
+ /* 802.11h related */
+ REPLY_QUIET_CMD = 0x71, /* not used */
+ REPLY_CHANNEL_SWITCH = 0x72,
+ CHANNEL_SWITCH_NOTIFICATION = 0x73,
+ REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74,
+ SPECTRUM_MEASURE_NOTIFICATION = 0x75,
+
+ /* Power Management */
+ POWER_TABLE_CMD = 0x77,
+ PM_SLEEP_NOTIFICATION = 0x7A,
+ PM_DEBUG_STATISTIC_NOTIFIC = 0x7B,
+
+ /* Scan commands and notifications */
+ REPLY_SCAN_CMD = 0x80,
+ REPLY_SCAN_ABORT_CMD = 0x81,
+ SCAN_START_NOTIFICATION = 0x82,
+ SCAN_RESULTS_NOTIFICATION = 0x83,
+ SCAN_COMPLETE_NOTIFICATION = 0x84,
+
+ /* IBSS/AP commands */
+ BEACON_NOTIFICATION = 0x90,
+ REPLY_TX_BEACON = 0x91,
+ WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */
+
+ /* Miscellaneous commands */
+ REPLY_TX_POWER_DBM_CMD = 0x95,
+ QUIET_NOTIFICATION = 0x96, /* not used */
+ REPLY_TX_PWR_TABLE_CMD = 0x97,
+ REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */
+ TX_ANT_CONFIGURATION_CMD = 0x98,
+ MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */
+
+ /* Bluetooth device coexistence config command */
+ REPLY_BT_CONFIG = 0x9b,
+
+ /* Statistics */
+ REPLY_STATISTICS_CMD = 0x9c,
+ STATISTICS_NOTIFICATION = 0x9d,
+
+ /* RF-KILL commands and notifications */
+ REPLY_CARD_STATE_CMD = 0xa0,
+ CARD_STATE_NOTIFICATION = 0xa1,
+
+ /* Missed beacons notification */
+ MISSED_BEACONS_NOTIFICATION = 0xa2,
+
+ REPLY_CT_KILL_CONFIG_CMD = 0xa4,
+ SENSITIVITY_CMD = 0xa8,
+ REPLY_PHY_CALIBRATION_CMD = 0xb0,
+ REPLY_RX_PHY_CMD = 0xc0,
+ REPLY_RX_MPDU_CMD = 0xc1,
+ REPLY_RX = 0xc3,
+ REPLY_COMPRESSED_BA = 0xc5,
+
+ /* BT Coex */
+ REPLY_BT_COEX_PRIO_TABLE = 0xcc,
+ REPLY_BT_COEX_PROT_ENV = 0xcd,
+ REPLY_BT_COEX_PROFILE_NOTIF = 0xce,
+
+ /* PAN commands */
+ REPLY_WIPAN_PARAMS = 0xb2,
+ REPLY_WIPAN_RXON = 0xb3, /* use REPLY_RXON structure */
+ REPLY_WIPAN_RXON_TIMING = 0xb4, /* use REPLY_RXON_TIMING structure */
+ REPLY_WIPAN_RXON_ASSOC = 0xb6, /* use REPLY_RXON_ASSOC structure */
+ REPLY_WIPAN_QOS_PARAM = 0xb7, /* use REPLY_QOS_PARAM structure */
+ REPLY_WIPAN_WEPKEY = 0xb8, /* use REPLY_WEPKEY structure */
+ REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9,
+ REPLY_WIPAN_NOA_NOTIFICATION = 0xbc,
+ REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd,
+
+ REPLY_WOWLAN_PATTERNS = 0xe0,
+ REPLY_WOWLAN_WAKEUP_FILTER = 0xe1,
+ REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2,
+ REPLY_WOWLAN_TKIP_PARAMS = 0xe3,
+ REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4,
+ REPLY_WOWLAN_GET_STATUS = 0xe5,
+ REPLY_D3_CONFIG = 0xd3,
+
+ REPLY_MAX = 0xff
+};
+
+/*
+ * Minimum number of queues. MAX_NUM is defined in hw specific files.
+ * Set the minimum to accommodate
+ * - 4 standard TX queues
+ * - the command queue
+ * - 4 PAN TX queues
+ * - the PAN multicast queue, and
+ * - the AUX (TX during scan dwell) queue.
+ */
+#define IWL_MIN_NUM_QUEUES 11
+
+/*
+ * Command queue depends on iPAN support.
+ */
+#define IWL_DEFAULT_CMD_QUEUE_NUM 4
+#define IWL_IPAN_CMD_QUEUE_NUM 9
+
+#define IWL_TX_FIFO_BK 0 /* shared */
+#define IWL_TX_FIFO_BE 1
+#define IWL_TX_FIFO_VI 2 /* shared */
+#define IWL_TX_FIFO_VO 3
+#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK
+#define IWL_TX_FIFO_BE_IPAN 4
+#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI
+#define IWL_TX_FIFO_VO_IPAN 5
+/* re-uses the VO FIFO, uCode will properly flush/schedule */
+#define IWL_TX_FIFO_AUX 5
+#define IWL_TX_FIFO_UNUSED 255
+
+#define IWLAGN_CMD_FIFO_NUM 7
+
+/*
+ * This queue number is required for proper operation
+ * because the ucode will stop/start the scheduler as
+ * required.
+ */
+#define IWL_IPAN_MCAST_QUEUE 8
+
+/******************************************************************************
+ * (0)
+ * Commonly used structures and definitions:
+ * Command header, rate_n_flags, txpower
+ *
+ *****************************************************************************/
+
+/**
+ * iwlagn rate_n_flags bit fields
+ *
+ * rate_n_flags format is used in following iwlagn commands:
+ * REPLY_RX (response only)
+ * REPLY_RX_MPDU (response only)
+ * REPLY_TX (both command and response)
+ * REPLY_TX_LINK_QUALITY_CMD
+ *
+ * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"):
+ * 2-0: 0) 6 Mbps
+ * 1) 12 Mbps
+ * 2) 18 Mbps
+ * 3) 24 Mbps
+ * 4) 36 Mbps
+ * 5) 48 Mbps
+ * 6) 54 Mbps
+ * 7) 60 Mbps
+ *
+ * 4-3: 0) Single stream (SISO)
+ * 1) Dual stream (MIMO)
+ * 2) Triple stream (MIMO)
+ *
+ * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data
+ *
+ * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"):
+ * 3-0: 0xD) 6 Mbps
+ * 0xF) 9 Mbps
+ * 0x5) 12 Mbps
+ * 0x7) 18 Mbps
+ * 0x9) 24 Mbps
+ * 0xB) 36 Mbps
+ * 0x1) 48 Mbps
+ * 0x3) 54 Mbps
+ *
+ * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"):
+ * 6-0: 10) 1 Mbps
+ * 20) 2 Mbps
+ * 55) 5.5 Mbps
+ * 110) 11 Mbps
+ */
+#define RATE_MCS_CODE_MSK 0x7
+#define RATE_MCS_SPATIAL_POS 3
+#define RATE_MCS_SPATIAL_MSK 0x18
+#define RATE_MCS_HT_DUP_POS 5
+#define RATE_MCS_HT_DUP_MSK 0x20
+/* Both legacy and HT use bits 7:0 as the CCK/OFDM rate or HT MCS */
+#define RATE_MCS_RATE_MSK 0xff
+
+/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */
+#define RATE_MCS_FLAGS_POS 8
+#define RATE_MCS_HT_POS 8
+#define RATE_MCS_HT_MSK 0x100
+
+/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */
+#define RATE_MCS_CCK_POS 9
+#define RATE_MCS_CCK_MSK 0x200
+
+/* Bit 10: (1) Use Green Field preamble */
+#define RATE_MCS_GF_POS 10
+#define RATE_MCS_GF_MSK 0x400
+
+/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */
+#define RATE_MCS_HT40_POS 11
+#define RATE_MCS_HT40_MSK 0x800
+
+/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */
+#define RATE_MCS_DUP_POS 12
+#define RATE_MCS_DUP_MSK 0x1000
+
+/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */
+#define RATE_MCS_SGI_POS 13
+#define RATE_MCS_SGI_MSK 0x2000
+
+/**
+ * rate_n_flags Tx antenna masks
+ * 4965 has 2 transmitters
+ * 5100 has 1 transmitter B
+ * 5150 has 1 transmitter A
+ * 5300 has 3 transmitters
+ * 5350 has 3 transmitters
+ * bit14:16
+ */
+#define RATE_MCS_ANT_POS 14
+#define RATE_MCS_ANT_A_MSK 0x04000
+#define RATE_MCS_ANT_B_MSK 0x08000
+#define RATE_MCS_ANT_C_MSK 0x10000
+#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK)
+#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK)
+#define RATE_ANT_NUM 3
+
+#define POWER_TABLE_NUM_ENTRIES 33
+#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32
+#define POWER_TABLE_CCK_ENTRY 32
+
+#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24
+#define IWL_PWR_CCK_ENTRIES 2
+
+/**
+ * struct tx_power_dual_stream
+ *
+ * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH
+ *
+ * Same format as iwl_tx_power_dual_stream, but __le32
+ */
+struct tx_power_dual_stream {
+ __le32 dw;
+} __packed;
+
+/**
+ * Command REPLY_TX_POWER_DBM_CMD = 0x98
+ * struct iwlagn_tx_power_dbm_cmd
+ */
+#define IWLAGN_TX_POWER_AUTO 0x7f
+#define IWLAGN_TX_POWER_NO_CLOSED (0x1 << 6)
+
+struct iwlagn_tx_power_dbm_cmd {
+ s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */
+ u8 flags;
+ s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */
+ u8 reserved;
+} __packed;
+
+/**
+ * Command TX_ANT_CONFIGURATION_CMD = 0x98
+ * This command is used to configure valid Tx antenna.
+ * By default uCode concludes the valid antenna according to the radio flavor.
+ * This command enables the driver to override/modify this conclusion.
+ */
+struct iwl_tx_ant_config_cmd {
+ __le32 valid;
+} __packed;
+
+/******************************************************************************
+ * (0a)
+ * Alive and Error Commands & Responses:
+ *
+ *****************************************************************************/
+
+#define UCODE_VALID_OK cpu_to_le32(0x1)
+
+/**
+ * REPLY_ALIVE = 0x1 (response only, not a command)
+ *
+ * uCode issues this "alive" notification once the runtime image is ready
+ * to receive commands from the driver. This is the *second* "alive"
+ * notification that the driver will receive after rebooting uCode;
+ * this "alive" is indicated by subtype field != 9.
+ *
+ * See comments documenting "BSM" (bootstrap state machine).
+ *
+ * This response includes two pointers to structures within the device's
+ * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging:
+ *
+ * 1) log_event_table_ptr indicates base of the event log. This traces
+ * a 256-entry history of uCode execution within a circular buffer.
+ * Its header format is:
+ *
+ * __le32 log_size; log capacity (in number of entries)
+ * __le32 type; (1) timestamp with each entry, (0) no timestamp
+ * __le32 wraps; # times uCode has wrapped to top of circular buffer
+ * __le32 write_index; next circular buffer entry that uCode would fill
+ *
+ * The header is followed by the circular buffer of log entries. Entries
+ * with timestamps have the following format:
+ *
+ * __le32 event_id; range 0 - 1500
+ * __le32 timestamp; low 32 bits of TSF (of network, if associated)
+ * __le32 data; event_id-specific data value
+ *
+ * Entries without timestamps contain only event_id and data.
+ *
+ *
+ * 2) error_event_table_ptr indicates base of the error log. This contains
+ * information about any uCode error that occurs. For agn, the format
+ * of the error log is defined by struct iwl_error_event_table.
+ *
+ * The Linux driver can print both logs to the system log when a uCode error
+ * occurs.
+ */
+
+/*
+ * Note: This structure is read from the device with IO accesses,
+ * and the reading already does the endian conversion. As it is
+ * read with u32-sized accesses, any members with a different size
+ * need to be ordered correctly though!
+ */
+struct iwl_error_event_table {
+ u32 valid; /* (nonzero) valid, (0) log is empty */
+ u32 error_id; /* type of error */
+ u32 pc; /* program counter */
+ u32 blink1; /* branch link */
+ u32 blink2; /* branch link */
+ u32 ilink1; /* interrupt link */
+ u32 ilink2; /* interrupt link */
+ u32 data1; /* error-specific data */
+ u32 data2; /* error-specific data */
+ u32 line; /* source code line of error */
+ u32 bcon_time; /* beacon timer */
+ u32 tsf_low; /* network timestamp function timer */
+ u32 tsf_hi; /* network timestamp function timer */
+ u32 gp1; /* GP1 timer register */
+ u32 gp2; /* GP2 timer register */
+ u32 gp3; /* GP3 timer register */
+ u32 ucode_ver; /* uCode version */
+ u32 hw_ver; /* HW Silicon version */
+ u32 brd_ver; /* HW board version */
+ u32 log_pc; /* log program counter */
+ u32 frame_ptr; /* frame pointer */
+ u32 stack_ptr; /* stack pointer */
+ u32 hcmd; /* last host command header */
+ u32 isr0; /* isr status register LMPM_NIC_ISR0:
+ * rxtx_flag */
+ u32 isr1; /* isr status register LMPM_NIC_ISR1:
+ * host_flag */
+ u32 isr2; /* isr status register LMPM_NIC_ISR2:
+ * enc_flag */
+ u32 isr3; /* isr status register LMPM_NIC_ISR3:
+ * time_flag */
+ u32 isr4; /* isr status register LMPM_NIC_ISR4:
+ * wico interrupt */
+ u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
+ u32 wait_event; /* wait event() caller address */
+ u32 l2p_control; /* L2pControlField */
+ u32 l2p_duration; /* L2pDurationField */
+ u32 l2p_mhvalid; /* L2pMhValidBits */
+ u32 l2p_addr_match; /* L2pAddrMatchStat */
+ u32 lmpm_pmg_sel; /* indicate which clocks are turned on
+ * (LMPM_PMG_SEL) */
+ u32 u_timestamp; /* indicate when the date and time of the
+ * compilation */
+ u32 flow_handler; /* FH read/write pointers, RX credit */
+} __packed;
+
+struct iwl_alive_resp {
+ u8 ucode_minor;
+ u8 ucode_major;
+ __le16 reserved1;
+ u8 sw_rev[8];
+ u8 ver_type;
+ u8 ver_subtype; /* not "9" for runtime alive */
+ __le16 reserved2;
+ __le32 log_event_table_ptr; /* SRAM address for event log */
+ __le32 error_event_table_ptr; /* SRAM address for error log */
+ __le32 timestamp;
+ __le32 is_valid;
+} __packed;
+
+/*
+ * REPLY_ERROR = 0x2 (response only, not a command)
+ */
+struct iwl_error_resp {
+ __le32 error_type;
+ u8 cmd_id;
+ u8 reserved1;
+ __le16 bad_cmd_seq_num;
+ __le32 error_info;
+ __le64 timestamp;
+} __packed;
+
+/******************************************************************************
+ * (1)
+ * RXON Commands & Responses:
+ *
+ *****************************************************************************/
+
+/*
+ * Rx config defines & structure
+ */
+/* rx_config device types */
+enum {
+ RXON_DEV_TYPE_AP = 1,
+ RXON_DEV_TYPE_ESS = 3,
+ RXON_DEV_TYPE_IBSS = 4,
+ RXON_DEV_TYPE_SNIFFER = 6,
+ RXON_DEV_TYPE_CP = 7,
+ RXON_DEV_TYPE_2STA = 8,
+ RXON_DEV_TYPE_P2P = 9,
+};
+
+
+#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0)
+#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0)
+#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1)
+#define RXON_RX_CHAIN_VALID_POS (1)
+#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4)
+#define RXON_RX_CHAIN_FORCE_SEL_POS (4)
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7)
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
+#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10)
+#define RXON_RX_CHAIN_CNT_POS (10)
+#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12)
+#define RXON_RX_CHAIN_MIMO_CNT_POS (12)
+#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14)
+#define RXON_RX_CHAIN_MIMO_FORCE_POS (14)
+
+/* rx_config flags */
+/* band & modulation selection */
+#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0)
+#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1)
+/* auto detection enable */
+#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2)
+/* TGg protection when tx */
+#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3)
+/* cck short slot & preamble */
+#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4)
+#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5)
+/* antenna selection */
+#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7)
+#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00)
+#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8)
+#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9)
+/* radar detection enable */
+#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12)
+#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13)
+/* rx response to host with 8-byte TSF
+* (according to ON_AIR deassertion) */
+#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15)
+
+
+/* HT flags */
+#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22)
+#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22)
+
+#define RXON_FLG_HT_OPERATING_MODE_POS (23)
+
+#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23)
+#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23)
+
+#define RXON_FLG_CHANNEL_MODE_POS (25)
+#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25)
+
+/* channel mode */
+enum {
+ CHANNEL_MODE_LEGACY = 0,
+ CHANNEL_MODE_PURE_40 = 1,
+ CHANNEL_MODE_MIXED = 2,
+ CHANNEL_MODE_RESERVED = 3,
+};
+#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS)
+#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS)
+#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS)
+
+/* CTS to self (if spec allows) flag */
+#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30)
+
+/* rx_config filter flags */
+/* accept all data frames */
+#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0)
+/* pass control & management to host */
+#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1)
+/* accept multi-cast */
+#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2)
+/* don't decrypt uni-cast frames */
+#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3)
+/* don't decrypt multi-cast frames */
+#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4)
+/* STA is associated */
+#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5)
+/* transfer to host non bssid beacons in associated state */
+#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6)
+
+/**
+ * REPLY_RXON = 0x10 (command, has simple generic response)
+ *
+ * RXON tunes the radio tuner to a service channel, and sets up a number
+ * of parameters that are used primarily for Rx, but also for Tx operations.
+ *
+ * NOTE: When tuning to a new channel, driver must set the
+ * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent
+ * info within the device, including the station tables, tx retry
+ * rate tables, and txpower tables. Driver must build a new station
+ * table and txpower table before transmitting anything on the RXON
+ * channel.
+ *
+ * NOTE: All RXONs wipe clean the internal txpower table. Driver must
+ * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10),
+ * regardless of whether RXON_FILTER_ASSOC_MSK is set.
+ */
+
+struct iwl_rxon_cmd {
+ u8 node_addr[6];
+ __le16 reserved1;
+ u8 bssid_addr[6];
+ __le16 reserved2;
+ u8 wlap_bssid_addr[6];
+ __le16 reserved3;
+ u8 dev_type;
+ u8 air_propagation;
+ __le16 rx_chain;
+ u8 ofdm_basic_rates;
+ u8 cck_basic_rates;
+ __le16 assoc_id;
+ __le32 flags;
+ __le32 filter_flags;
+ __le16 channel;
+ u8 ofdm_ht_single_stream_basic_rates;
+ u8 ofdm_ht_dual_stream_basic_rates;
+ u8 ofdm_ht_triple_stream_basic_rates;
+ u8 reserved5;
+ __le16 acquisition_data;
+ __le16 reserved6;
+} __packed;
+
+/*
+ * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response)
+ */
+struct iwl_rxon_assoc_cmd {
+ __le32 flags;
+ __le32 filter_flags;
+ u8 ofdm_basic_rates;
+ u8 cck_basic_rates;
+ __le16 reserved1;
+ u8 ofdm_ht_single_stream_basic_rates;
+ u8 ofdm_ht_dual_stream_basic_rates;
+ u8 ofdm_ht_triple_stream_basic_rates;
+ u8 reserved2;
+ __le16 rx_chain_select_flags;
+ __le16 acquisition_data;
+ __le32 reserved3;
+} __packed;
+
+#define IWL_CONN_MAX_LISTEN_INTERVAL 10
+#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */
+
+/*
+ * REPLY_RXON_TIMING = 0x14 (command, has simple generic response)
+ */
+struct iwl_rxon_time_cmd {
+ __le64 timestamp;
+ __le16 beacon_interval;
+ __le16 atim_window;
+ __le32 beacon_init_val;
+ __le16 listen_interval;
+ u8 dtim_period;
+ u8 delta_cp_bss_tbtts;
+} __packed;
+
+/*
+ * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response)
+ */
+/**
+ * struct iwl5000_channel_switch_cmd
+ * @band: 0- 5.2GHz, 1- 2.4GHz
+ * @expect_beacon: 0- resume transmits after channel switch
+ * 1- wait for beacon to resume transmits
+ * @channel: new channel number
+ * @rxon_flags: Rx on flags
+ * @rxon_filter_flags: filtering parameters
+ * @switch_time: switch time in extended beacon format
+ * @reserved: reserved bytes
+ */
+struct iwl5000_channel_switch_cmd {
+ u8 band;
+ u8 expect_beacon;
+ __le16 channel;
+ __le32 rxon_flags;
+ __le32 rxon_filter_flags;
+ __le32 switch_time;
+ __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES];
+} __packed;
+
+/**
+ * struct iwl6000_channel_switch_cmd
+ * @band: 0- 5.2GHz, 1- 2.4GHz
+ * @expect_beacon: 0- resume transmits after channel switch
+ * 1- wait for beacon to resume transmits
+ * @channel: new channel number
+ * @rxon_flags: Rx on flags
+ * @rxon_filter_flags: filtering parameters
+ * @switch_time: switch time in extended beacon format
+ * @reserved: reserved bytes
+ */
+struct iwl6000_channel_switch_cmd {
+ u8 band;
+ u8 expect_beacon;
+ __le16 channel;
+ __le32 rxon_flags;
+ __le32 rxon_filter_flags;
+ __le32 switch_time;
+ __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES];
+} __packed;
+
+/*
+ * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command)
+ */
+struct iwl_csa_notification {
+ __le16 band;
+ __le16 channel;
+ __le32 status; /* 0 - OK, 1 - fail */
+} __packed;
+
+/******************************************************************************
+ * (2)
+ * Quality-of-Service (QOS) Commands & Responses:
+ *
+ *****************************************************************************/
+
+/**
+ * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM
+ * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd
+ *
+ * @cw_min: Contention window, start value in numbers of slots.
+ * Should be a power-of-2, minus 1. Device's default is 0x0f.
+ * @cw_max: Contention window, max value in numbers of slots.
+ * Should be a power-of-2, minus 1. Device's default is 0x3f.
+ * @aifsn: Number of slots in Arbitration Interframe Space (before
+ * performing random backoff timing prior to Tx). Device default 1.
+ * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0.
+ *
+ * Device will automatically increase contention window by (2*CW) + 1 for each
+ * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW
+ * value, to cap the CW value.
+ */
+struct iwl_ac_qos {
+ __le16 cw_min;
+ __le16 cw_max;
+ u8 aifsn;
+ u8 reserved1;
+ __le16 edca_txop;
+} __packed;
+
+/* QoS flags defines */
+#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01)
+#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02)
+#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10)
+
+/* Number of Access Categories (AC) (EDCA), queues 0..3 */
+#define AC_NUM 4
+
+/*
+ * REPLY_QOS_PARAM = 0x13 (command, has simple generic response)
+ *
+ * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs
+ * 0: Background, 1: Best Effort, 2: Video, 3: Voice.
+ */
+struct iwl_qosparam_cmd {
+ __le32 qos_flags;
+ struct iwl_ac_qos ac[AC_NUM];
+} __packed;
+
+/******************************************************************************
+ * (3)
+ * Add/Modify Stations Commands & Responses:
+ *
+ *****************************************************************************/
+/*
+ * Multi station support
+ */
+
+/* Special, dedicated locations within device's station table */
+#define IWL_AP_ID 0
+#define IWL_AP_ID_PAN 1
+#define IWL_STA_ID 2
+#define IWLAGN_PAN_BCAST_ID 14
+#define IWLAGN_BROADCAST_ID 15
+#define IWLAGN_STATION_COUNT 16
+
+#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT
+
+#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2)
+#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8)
+#define STA_FLG_PAN_STATION cpu_to_le32(1 << 13)
+#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17)
+#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18)
+#define STA_FLG_MAX_AGG_SIZE_POS (19)
+#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19)
+#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21)
+#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22)
+#define STA_FLG_AGG_MPDU_DENSITY_POS (23)
+#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23)
+
+/* Use in mode field. 1: modify existing entry, 0: add new station entry */
+#define STA_CONTROL_MODIFY_MSK 0x01
+
+/* key flags __le16*/
+#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007)
+#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000)
+#define STA_KEY_FLG_WEP cpu_to_le16(0x0001)
+#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002)
+#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003)
+
+#define STA_KEY_FLG_KEYID_POS 8
+#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800)
+/* wep key is either from global key (0) or from station info array (1) */
+#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008)
+
+/* wep key in STA: 5-bytes (0) or 13-bytes (1) */
+#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000)
+#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000)
+#define STA_KEY_MAX_NUM 8
+#define STA_KEY_MAX_NUM_PAN 16
+/* must not match WEP_INVALID_OFFSET */
+#define IWLAGN_HW_KEY_DEFAULT 0xfe
+
+/* Flags indicate whether to modify vs. don't change various station params */
+#define STA_MODIFY_KEY_MASK 0x01
+#define STA_MODIFY_TID_DISABLE_TX 0x02
+#define STA_MODIFY_TX_RATE_MSK 0x04
+#define STA_MODIFY_ADDBA_TID_MSK 0x08
+#define STA_MODIFY_DELBA_TID_MSK 0x10
+#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20
+
+/* agn */
+struct iwl_keyinfo {
+ __le16 key_flags;
+ u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */
+ u8 reserved1;
+ __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */
+ u8 key_offset;
+ u8 reserved2;
+ u8 key[16]; /* 16-byte unicast decryption key */
+ __le64 tx_secur_seq_cnt;
+ __le64 hw_tkip_mic_rx_key;
+ __le64 hw_tkip_mic_tx_key;
+} __packed;
+
+/**
+ * struct sta_id_modify
+ * @addr[ETH_ALEN]: station's MAC address
+ * @sta_id: index of station in uCode's station table
+ * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change
+ *
+ * Driver selects unused table index when adding new station,
+ * or the index to a pre-existing station entry when modifying that station.
+ * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP).
+ *
+ * modify_mask flags select which parameters to modify vs. leave alone.
+ */
+struct sta_id_modify {
+ u8 addr[ETH_ALEN];
+ __le16 reserved1;
+ u8 sta_id;
+ u8 modify_mask;
+ __le16 reserved2;
+} __packed;
+
+/*
+ * REPLY_ADD_STA = 0x18 (command)
+ *
+ * The device contains an internal table of per-station information,
+ * with info on security keys, aggregation parameters, and Tx rates for
+ * initial Tx attempt and any retries (agn devices uses
+ * REPLY_TX_LINK_QUALITY_CMD,
+ *
+ * REPLY_ADD_STA sets up the table entry for one station, either creating
+ * a new entry, or modifying a pre-existing one.
+ *
+ * NOTE: RXON command (without "associated" bit set) wipes the station table
+ * clean. Moving into RF_KILL state does this also. Driver must set up
+ * new station table before transmitting anything on the RXON channel
+ * (except active scans or active measurements; those commands carry
+ * their own txpower/rate setup data).
+ *
+ * When getting started on a new channel, driver must set up the
+ * IWL_BROADCAST_ID entry (last entry in the table). For a client
+ * station in a BSS, once an AP is selected, driver sets up the AP STA
+ * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP
+ * are all that are needed for a BSS client station. If the device is
+ * used as AP, or in an IBSS network, driver must set up station table
+ * entries for all STAs in network, starting with index IWL_STA_ID.
+ */
+
+struct iwl_addsta_cmd {
+ u8 mode; /* 1: modify existing, 0: add new station */
+ u8 reserved[3];
+ struct sta_id_modify sta;
+ struct iwl_keyinfo key;
+ __le32 station_flags; /* STA_FLG_* */
+ __le32 station_flags_msk; /* STA_FLG_* */
+
+ /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID)
+ * corresponding to bit (e.g. bit 5 controls TID 5).
+ * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */
+ __le16 tid_disable_tx;
+ __le16 legacy_reserved;
+
+ /* TID for which to add block-ack support.
+ * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */
+ u8 add_immediate_ba_tid;
+
+ /* TID for which to remove block-ack support.
+ * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */
+ u8 remove_immediate_ba_tid;
+
+ /* Starting Sequence Number for added block-ack support.
+ * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */
+ __le16 add_immediate_ba_ssn;
+
+ /*
+ * Number of packets OK to transmit to station even though
+ * it is asleep -- used to synchronise PS-poll and u-APSD
+ * responses while ucode keeps track of STA sleep state.
+ */
+ __le16 sleep_tx_count;
+
+ __le16 reserved2;
+} __packed;
+
+
+#define ADD_STA_SUCCESS_MSK 0x1
+#define ADD_STA_NO_ROOM_IN_TABLE 0x2
+#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4
+#define ADD_STA_MODIFY_NON_EXIST_STA 0x8
+/*
+ * REPLY_ADD_STA = 0x18 (response)
+ */
+struct iwl_add_sta_resp {
+ u8 status; /* ADD_STA_* */
+} __packed;
+
+#define REM_STA_SUCCESS_MSK 0x1
+/*
+ * REPLY_REM_STA = 0x19 (response)
+ */
+struct iwl_rem_sta_resp {
+ u8 status;
+} __packed;
+
+/*
+ * REPLY_REM_STA = 0x19 (command)
+ */
+struct iwl_rem_sta_cmd {
+ u8 num_sta; /* number of removed stations */
+ u8 reserved[3];
+ u8 addr[ETH_ALEN]; /* MAC addr of the first station */
+ u8 reserved2[2];
+} __packed;
+
+
+/* WiFi queues mask */
+#define IWL_SCD_BK_MSK BIT(0)
+#define IWL_SCD_BE_MSK BIT(1)
+#define IWL_SCD_VI_MSK BIT(2)
+#define IWL_SCD_VO_MSK BIT(3)
+#define IWL_SCD_MGMT_MSK BIT(3)
+
+/* PAN queues mask */
+#define IWL_PAN_SCD_BK_MSK BIT(4)
+#define IWL_PAN_SCD_BE_MSK BIT(5)
+#define IWL_PAN_SCD_VI_MSK BIT(6)
+#define IWL_PAN_SCD_VO_MSK BIT(7)
+#define IWL_PAN_SCD_MGMT_MSK BIT(7)
+#define IWL_PAN_SCD_MULTICAST_MSK BIT(8)
+
+#define IWL_AGG_TX_QUEUE_MSK 0xffc00
+
+#define IWL_DROP_ALL BIT(1)
+
+/*
+ * REPLY_TXFIFO_FLUSH = 0x1e(command and response)
+ *
+ * When using full FIFO flush this command checks the scheduler HW block WR/RD
+ * pointers to check if all the frames were transferred by DMA into the
+ * relevant TX FIFO queue. Only when the DMA is finished and the queue is
+ * empty the command can finish.
+ * This command is used to flush the TXFIFO from transmit commands, it may
+ * operate on single or multiple queues, the command queue can't be flushed by
+ * this command. The command response is returned when all the queue flush
+ * operations are done. Each TX command flushed return response with the FLUSH
+ * status set in the TX response status. When FIFO flush operation is used,
+ * the flush operation ends when both the scheduler DMA done and TXFIFO empty
+ * are set.
+ *
+ * @queue_control: bit mask for which queues to flush
+ * @flush_control: flush controls
+ * 0: Dump single MSDU
+ * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable.
+ * 2: Dump all FIFO
+ */
+struct iwl_txfifo_flush_cmd_v3 {
+ __le32 queue_control;
+ __le16 flush_control;
+ __le16 reserved;
+} __packed;
+
+struct iwl_txfifo_flush_cmd_v2 {
+ __le16 queue_control;
+ __le16 flush_control;
+} __packed;
+
+/*
+ * REPLY_WEP_KEY = 0x20
+ */
+struct iwl_wep_key {
+ u8 key_index;
+ u8 key_offset;
+ u8 reserved1[2];
+ u8 key_size;
+ u8 reserved2[3];
+ u8 key[16];
+} __packed;
+
+struct iwl_wep_cmd {
+ u8 num_keys;
+ u8 global_key_type;
+ u8 flags;
+ u8 reserved;
+ struct iwl_wep_key key[0];
+} __packed;
+
+#define WEP_KEY_WEP_TYPE 1
+#define WEP_KEYS_MAX 4
+#define WEP_INVALID_OFFSET 0xff
+#define WEP_KEY_LEN_64 5
+#define WEP_KEY_LEN_128 13
+
+/******************************************************************************
+ * (4)
+ * Rx Responses:
+ *
+ *****************************************************************************/
+
+#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0)
+#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1)
+
+#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0)
+#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1)
+#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2)
+#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3)
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70
+#define RX_RES_PHY_FLAGS_ANTENNA_POS 4
+#define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7)
+
+#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
+#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
+#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8)
+#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8)
+#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8)
+#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8)
+
+#define RX_RES_STATUS_STATION_FOUND (1<<6)
+#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7)
+
+#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11)
+#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11)
+#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11)
+#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11)
+#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11)
+
+#define RX_MPDU_RES_STATUS_ICV_OK (0x20)
+#define RX_MPDU_RES_STATUS_MIC_OK (0x40)
+#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7)
+#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800)
+
+
+#define IWLAGN_RX_RES_PHY_CNT 8
+#define IWLAGN_RX_RES_AGC_IDX 1
+#define IWLAGN_RX_RES_RSSI_AB_IDX 2
+#define IWLAGN_RX_RES_RSSI_C_IDX 3
+#define IWLAGN_OFDM_AGC_MSK 0xfe00
+#define IWLAGN_OFDM_AGC_BIT_POS 9
+#define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff
+#define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00
+#define IWLAGN_OFDM_RSSI_A_BIT_POS 0
+#define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000
+#define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000
+#define IWLAGN_OFDM_RSSI_B_BIT_POS 16
+#define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff
+#define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00
+#define IWLAGN_OFDM_RSSI_C_BIT_POS 0
+
+struct iwlagn_non_cfg_phy {
+ __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */
+} __packed;
+
+
+/*
+ * REPLY_RX = 0xc3 (response only, not a command)
+ * Used only for legacy (non 11n) frames.
+ */
+struct iwl_rx_phy_res {
+ u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */
+ u8 cfg_phy_cnt; /* configurable DSP phy data byte count */
+ u8 stat_id; /* configurable DSP phy data set ID */
+ u8 reserved1;
+ __le64 timestamp; /* TSF at on air rise */
+ __le32 beacon_time_stamp; /* beacon at on-air rise */
+ __le16 phy_flags; /* general phy flags: band, modulation, ... */
+ __le16 channel; /* channel number */
+ u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */
+ __le32 rate_n_flags; /* RATE_MCS_* */
+ __le16 byte_count; /* frame's byte-count */
+ __le16 frame_time; /* frame's time on the air */
+} __packed;
+
+struct iwl_rx_mpdu_res_start {
+ __le16 byte_count;
+ __le16 reserved;
+} __packed;
+
+
+/******************************************************************************
+ * (5)
+ * Tx Commands & Responses:
+ *
+ * Driver must place each REPLY_TX command into one of the prioritized Tx
+ * queues in host DRAM, shared between driver and device (see comments for
+ * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode
+ * are preparing to transmit, the device pulls the Tx command over the PCI
+ * bus via one of the device's Tx DMA channels, to fill an internal FIFO
+ * from which data will be transmitted.
+ *
+ * uCode handles all timing and protocol related to control frames
+ * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler
+ * handle reception of block-acks; uCode updates the host driver via
+ * REPLY_COMPRESSED_BA.
+ *
+ * uCode handles retrying Tx when an ACK is expected but not received.
+ * This includes trying lower data rates than the one requested in the Tx
+ * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn).
+ *
+ * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD.
+ * This command must be executed after every RXON command, before Tx can occur.
+ *****************************************************************************/
+
+/* REPLY_TX Tx flags field */
+
+/*
+ * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it
+ * before this frame. if CTS-to-self required check
+ * RXON_FLG_SELF_CTS_EN status.
+ */
+#define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0)
+
+/* 1: Expect ACK from receiving station
+ * 0: Don't expect ACK (MAC header's duration field s/b 0)
+ * Set this for unicast frames, but not broadcast/multicast. */
+#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3)
+
+/* For agn devices:
+ * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD).
+ * Tx command's initial_rate_index indicates first rate to try;
+ * uCode walks through table for additional Tx attempts.
+ * 0: Use Tx rate/MCS from Tx command's rate_n_flags field.
+ * This rate will be used for all Tx attempts; it will not be scaled. */
+#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4)
+
+/* 1: Expect immediate block-ack.
+ * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */
+#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6)
+
+/* Tx antenna selection field; reserved (0) for agn devices. */
+#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00)
+
+/* 1: Ignore Bluetooth priority for this frame.
+ * 0: Delay Tx until Bluetooth device is done (normal usage). */
+#define TX_CMD_FLG_IGNORE_BT cpu_to_le32(1 << 12)
+
+/* 1: uCode overrides sequence control field in MAC header.
+ * 0: Driver provides sequence control field in MAC header.
+ * Set this for management frames, non-QOS data frames, non-unicast frames,
+ * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */
+#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13)
+
+/* 1: This frame is non-last MPDU; more fragments are coming.
+ * 0: Last fragment, or not using fragmentation. */
+#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14)
+
+/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame.
+ * 0: No TSF required in outgoing frame.
+ * Set this for transmitting beacons and probe responses. */
+#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16)
+
+/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword
+ * alignment of frame's payload data field.
+ * 0: No pad
+ * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4
+ * field (but not both). Driver must align frame data (i.e. data following
+ * MAC header) to DWORD boundary. */
+#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20)
+
+/* accelerate aggregation support
+ * 0 - no CCMP encryption; 1 - CCMP encryption */
+#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22)
+
+/* HCCA-AP - disable duration overwriting. */
+#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25)
+
+
+/*
+ * TX command security control
+ */
+#define TX_CMD_SEC_WEP 0x01
+#define TX_CMD_SEC_CCM 0x02
+#define TX_CMD_SEC_TKIP 0x03
+#define TX_CMD_SEC_MSK 0x03
+#define TX_CMD_SEC_SHIFT 6
+#define TX_CMD_SEC_KEY128 0x08
+
+/*
+ * REPLY_TX = 0x1c (command)
+ */
+
+/*
+ * 4965 uCode updates these Tx attempt count values in host DRAM.
+ * Used for managing Tx retries when expecting block-acks.
+ * Driver should set these fields to 0.
+ */
+struct iwl_dram_scratch {
+ u8 try_cnt; /* Tx attempts */
+ u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */
+ __le16 reserved;
+} __packed;
+
+struct iwl_tx_cmd {
+ /*
+ * MPDU byte count:
+ * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size,
+ * + 8 byte IV for CCM or TKIP (not used for WEP)
+ * + Data payload
+ * + 8-byte MIC (not used for CCM/WEP)
+ * NOTE: Does not include Tx command bytes, post-MAC pad bytes,
+ * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i
+ * Range: 14-2342 bytes.
+ */
+ __le16 len;
+
+ /*
+ * MPDU or MSDU byte count for next frame.
+ * Used for fragmentation and bursting, but not 11n aggregation.
+ * Same as "len", but for next frame. Set to 0 if not applicable.
+ */
+ __le16 next_frame_len;
+
+ __le32 tx_flags; /* TX_CMD_FLG_* */
+
+ /* uCode may modify this field of the Tx command (in host DRAM!).
+ * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */
+ struct iwl_dram_scratch scratch;
+
+ /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */
+ __le32 rate_n_flags; /* RATE_MCS_* */
+
+ /* Index of destination station in uCode's station table */
+ u8 sta_id;
+
+ /* Type of security encryption: CCM or TKIP */
+ u8 sec_ctl; /* TX_CMD_SEC_* */
+
+ /*
+ * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial
+ * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for
+ * data frames, this field may be used to selectively reduce initial
+ * rate (via non-0 value) for special frames (e.g. management), while
+ * still supporting rate scaling for all frames.
+ */
+ u8 initial_rate_index;
+ u8 reserved;
+ u8 key[16];
+ __le16 next_frame_flags;
+ __le16 reserved2;
+ union {
+ __le32 life_time;
+ __le32 attempt;
+ } stop_time;
+
+ /* Host DRAM physical address pointer to "scratch" in this command.
+ * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */
+ __le32 dram_lsb_ptr;
+ u8 dram_msb_ptr;
+
+ u8 rts_retry_limit; /*byte 50 */
+ u8 data_retry_limit; /*byte 51 */
+ u8 tid_tspec;
+ union {
+ __le16 pm_frame_timeout;
+ __le16 attempt_duration;
+ } timeout;
+
+ /*
+ * Duration of EDCA burst Tx Opportunity, in 32-usec units.
+ * Set this if txop time is not specified by HCCA protocol (e.g. by AP).
+ */
+ __le16 driver_txop;
+
+ /*
+ * MAC header goes here, followed by 2 bytes padding if MAC header
+ * length is 26 or 30 bytes, followed by payload data
+ */
+ u8 payload[0];
+ struct ieee80211_hdr hdr[0];
+} __packed;
+
+/*
+ * TX command response is sent after *agn* transmission attempts.
+ *
+ * both postpone and abort status are expected behavior from uCode. there is
+ * no special operation required from driver; except for RFKILL_FLUSH,
+ * which required tx flush host command to flush all the tx frames in queues
+ */
+enum {
+ TX_STATUS_SUCCESS = 0x01,
+ TX_STATUS_DIRECT_DONE = 0x02,
+ /* postpone TX */
+ TX_STATUS_POSTPONE_DELAY = 0x40,
+ TX_STATUS_POSTPONE_FEW_BYTES = 0x41,
+ TX_STATUS_POSTPONE_BT_PRIO = 0x42,
+ TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43,
+ TX_STATUS_POSTPONE_CALC_TTAK = 0x44,
+ /* abort TX */
+ TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81,
+ TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
+ TX_STATUS_FAIL_LONG_LIMIT = 0x83,
+ TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84,
+ TX_STATUS_FAIL_DRAIN_FLOW = 0x85,
+ TX_STATUS_FAIL_RFKILL_FLUSH = 0x86,
+ TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
+ TX_STATUS_FAIL_DEST_PS = 0x88,
+ TX_STATUS_FAIL_HOST_ABORTED = 0x89,
+ TX_STATUS_FAIL_BT_RETRY = 0x8a,
+ TX_STATUS_FAIL_STA_INVALID = 0x8b,
+ TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
+ TX_STATUS_FAIL_TID_DISABLE = 0x8d,
+ TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
+ TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f,
+ TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90,
+ TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91,
+};
+
+#define TX_PACKET_MODE_REGULAR 0x0000
+#define TX_PACKET_MODE_BURST_SEQ 0x0100
+#define TX_PACKET_MODE_BURST_FIRST 0x0200
+
+enum {
+ TX_POWER_PA_NOT_ACTIVE = 0x0,
+};
+
+enum {
+ TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */
+ TX_STATUS_DELAY_MSK = 0x00000040,
+ TX_STATUS_ABORT_MSK = 0x00000080,
+ TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */
+ TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */
+ TX_RESERVED = 0x00780000, /* bits 19:22 */
+ TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */
+ TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */
+};
+
+/* *******************************
+ * TX aggregation status
+ ******************************* */
+
+enum {
+ AGG_TX_STATE_TRANSMITTED = 0x00,
+ AGG_TX_STATE_UNDERRUN_MSK = 0x01,
+ AGG_TX_STATE_BT_PRIO_MSK = 0x02,
+ AGG_TX_STATE_FEW_BYTES_MSK = 0x04,
+ AGG_TX_STATE_ABORT_MSK = 0x08,
+ AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10,
+ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20,
+ AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40,
+ AGG_TX_STATE_SCD_QUERY_MSK = 0x80,
+ AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100,
+ AGG_TX_STATE_RESPONSE_MSK = 0x1ff,
+ AGG_TX_STATE_DUMP_TX_MSK = 0x200,
+ AGG_TX_STATE_DELAY_TX_MSK = 0x400
+};
+
+#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */
+#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */
+#define AGG_TX_TRY_POS 12
+
+#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \
+ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \
+ AGG_TX_STATE_LAST_SENT_BT_KILL_MSK)
+
+/* # tx attempts for first frame in aggregation */
+#define AGG_TX_STATE_TRY_CNT_POS 12
+#define AGG_TX_STATE_TRY_CNT_MSK 0xf000
+
+/* Command ID and sequence number of Tx command for this frame */
+#define AGG_TX_STATE_SEQ_NUM_POS 16
+#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000
+
+/*
+ * REPLY_TX = 0x1c (response)
+ *
+ * This response may be in one of two slightly different formats, indicated
+ * by the frame_count field:
+ *
+ * 1) No aggregation (frame_count == 1). This reports Tx results for
+ * a single frame. Multiple attempts, at various bit rates, may have
+ * been made for this frame.
+ *
+ * 2) Aggregation (frame_count > 1). This reports Tx results for
+ * 2 or more frames that used block-acknowledge. All frames were
+ * transmitted at same rate. Rate scaling may have been used if first
+ * frame in this new agg block failed in previous agg block(s).
+ *
+ * Note that, for aggregation, ACK (block-ack) status is not delivered here;
+ * block-ack has not been received by the time the agn device records
+ * this status.
+ * This status relates to reasons the tx might have been blocked or aborted
+ * within the sending station (this agn device), rather than whether it was
+ * received successfully by the destination station.
+ */
+struct agg_tx_status {
+ __le16 status;
+ __le16 sequence;
+} __packed;
+
+/*
+ * definitions for initial rate index field
+ * bits [3:0] initial rate index
+ * bits [6:4] rate table color, used for the initial rate
+ * bit-7 invalid rate indication
+ * i.e. rate was not chosen from rate table
+ * or rate table color was changed during frame retries
+ * refer tlc rate info
+ */
+
+#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0
+#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f
+#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4
+#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70
+#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80
+
+/* refer to ra_tid */
+#define IWLAGN_TX_RES_TID_POS 0
+#define IWLAGN_TX_RES_TID_MSK 0x0f
+#define IWLAGN_TX_RES_RA_POS 4
+#define IWLAGN_TX_RES_RA_MSK 0xf0
+
+struct iwlagn_tx_resp {
+ u8 frame_count; /* 1 no aggregation, >1 aggregation */
+ u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */
+ u8 failure_rts; /* # failures due to unsuccessful RTS */
+ u8 failure_frame; /* # failures due to no ACK (unused for agg) */
+
+ /* For non-agg: Rate at which frame was successful.
+ * For agg: Rate at which all frames were transmitted. */
+ __le32 rate_n_flags; /* RATE_MCS_* */
+
+ /* For non-agg: RTS + CTS + frame tx attempts time + ACK.
+ * For agg: RTS + CTS + aggregation tx time + block-ack time. */
+ __le16 wireless_media_time; /* uSecs */
+
+ u8 pa_status; /* RF power amplifier measurement (not used) */
+ u8 pa_integ_res_a[3];
+ u8 pa_integ_res_b[3];
+ u8 pa_integ_res_C[3];
+
+ __le32 tfd_info;
+ __le16 seq_ctl;
+ __le16 byte_cnt;
+ u8 tlc_info;
+ u8 ra_tid; /* tid (0:3), sta_id (4:7) */
+ __le16 frame_ctrl;
+ /*
+ * For non-agg: frame status TX_STATUS_*
+ * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status
+ * fields follow this one, up to frame_count.
+ * Bit fields:
+ * 11- 0: AGG_TX_STATE_* status code
+ * 15-12: Retry count for 1st frame in aggregation (retries
+ * occur if tx failed for this frame when it was a
+ * member of a previous aggregation block). If rate
+ * scaling is used, retry count indicates the rate
+ * table entry used for all frames in the new agg.
+ * 31-16: Sequence # for this frame's Tx cmd (not SSN!)
+ */
+ struct agg_tx_status status; /* TX status (in aggregation -
+ * status of 1st frame) */
+} __packed;
+/*
+ * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command)
+ *
+ * Reports Block-Acknowledge from recipient station
+ */
+struct iwl_compressed_ba_resp {
+ __le32 sta_addr_lo32;
+ __le16 sta_addr_hi16;
+ __le16 reserved;
+
+ /* Index of recipient (BA-sending) station in uCode's station table */
+ u8 sta_id;
+ u8 tid;
+ __le16 seq_ctl;
+ __le64 bitmap;
+ __le16 scd_flow;
+ __le16 scd_ssn;
+ u8 txed; /* number of frames sent */
+ u8 txed_2_done; /* number of frames acked */
+ __le16 reserved1;
+} __packed;
+
+/*
+ * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response)
+ *
+ */
+
+/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */
+#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0)
+
+/* # of EDCA prioritized tx fifos */
+#define LINK_QUAL_AC_NUM AC_NUM
+
+/* # entries in rate scale table to support Tx retries */
+#define LINK_QUAL_MAX_RETRY_NUM 16
+
+/* Tx antenna selection values */
+#define LINK_QUAL_ANT_A_MSK (1 << 0)
+#define LINK_QUAL_ANT_B_MSK (1 << 1)
+#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK)
+
+
+/**
+ * struct iwl_link_qual_general_params
+ *
+ * Used in REPLY_TX_LINK_QUALITY_CMD
+ */
+struct iwl_link_qual_general_params {
+ u8 flags;
+
+ /* No entries at or above this (driver chosen) index contain MIMO */
+ u8 mimo_delimiter;
+
+ /* Best single antenna to use for single stream (legacy, SISO). */
+ u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */
+
+ /* Best antennas to use for MIMO (unused for 4965, assumes both). */
+ u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */
+
+ /*
+ * If driver needs to use different initial rates for different
+ * EDCA QOS access categories (as implemented by tx fifos 0-3),
+ * this table will set that up, by indicating the indexes in the
+ * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start.
+ * Otherwise, driver should set all entries to 0.
+ *
+ * Entry usage:
+ * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice
+ * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3.
+ */
+ u8 start_rate_index[LINK_QUAL_AC_NUM];
+} __packed;
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */
+#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000)
+#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100)
+
+#define LINK_QUAL_AGG_DISABLE_START_DEF (3)
+#define LINK_QUAL_AGG_DISABLE_START_MAX (255)
+#define LINK_QUAL_AGG_DISABLE_START_MIN (0)
+
+#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0)
+
+/**
+ * struct iwl_link_qual_agg_params
+ *
+ * Used in REPLY_TX_LINK_QUALITY_CMD
+ */
+struct iwl_link_qual_agg_params {
+
+ /*
+ *Maximum number of uSec in aggregation.
+ * default set to 4000 (4 milliseconds) if not configured in .cfg
+ */
+ __le16 agg_time_limit;
+
+ /*
+ * Number of Tx retries allowed for a frame, before that frame will
+ * no longer be considered for the start of an aggregation sequence
+ * (scheduler will then try to tx it as single frame).
+ * Driver should set this to 3.
+ */
+ u8 agg_dis_start_th;
+
+ /*
+ * Maximum number of frames in aggregation.
+ * 0 = no limit (default). 1 = no aggregation.
+ * Other values = max # frames in aggregation.
+ */
+ u8 agg_frame_cnt_limit;
+
+ __le32 reserved;
+} __packed;
+
+/*
+ * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response)
+ *
+ * For agn devices
+ *
+ * Each station in the agn device's internal station table has its own table
+ * of 16
+ * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when
+ * an ACK is not received. This command replaces the entire table for
+ * one station.
+ *
+ * NOTE: Station must already be in agn device's station table.
+ * Use REPLY_ADD_STA.
+ *
+ * The rate scaling procedures described below work well. Of course, other
+ * procedures are possible, and may work better for particular environments.
+ *
+ *
+ * FILLING THE RATE TABLE
+ *
+ * Given a particular initial rate and mode, as determined by the rate
+ * scaling algorithm described below, the Linux driver uses the following
+ * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the
+ * Link Quality command:
+ *
+ *
+ * 1) If using High-throughput (HT) (SISO or MIMO) initial rate:
+ * a) Use this same initial rate for first 3 entries.
+ * b) Find next lower available rate using same mode (SISO or MIMO),
+ * use for next 3 entries. If no lower rate available, switch to
+ * legacy mode (no HT40 channel, no MIMO, no short guard interval).
+ * c) If using MIMO, set command's mimo_delimiter to number of entries
+ * using MIMO (3 or 6).
+ * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel,
+ * no MIMO, no short guard interval), at the next lower bit rate
+ * (e.g. if second HT bit rate was 54, try 48 legacy), and follow
+ * legacy procedure for remaining table entries.
+ *
+ * 2) If using legacy initial rate:
+ * a) Use the initial rate for only one entry.
+ * b) For each following entry, reduce the rate to next lower available
+ * rate, until reaching the lowest available rate.
+ * c) When reducing rate, also switch antenna selection.
+ * d) Once lowest available rate is reached, repeat this rate until
+ * rate table is filled (16 entries), switching antenna each entry.
+ *
+ *
+ * ACCUMULATING HISTORY
+ *
+ * The rate scaling algorithm for agn devices, as implemented in Linux driver,
+ * uses two sets of frame Tx success history: One for the current/active
+ * modulation mode, and one for a speculative/search mode that is being
+ * attempted. If the speculative mode turns out to be more effective (i.e.
+ * actual transfer rate is better), then the driver continues to use the
+ * speculative mode as the new current active mode.
+ *
+ * Each history set contains, separately for each possible rate, data for a
+ * sliding window of the 62 most recent tx attempts at that rate. The data
+ * includes a shifting bitmap of success(1)/failure(0), and sums of successful
+ * and attempted frames, from which the driver can additionally calculate a
+ * success ratio (success / attempted) and number of failures
+ * (attempted - success), and control the size of the window (attempted).
+ * The driver uses the bit map to remove successes from the success sum, as
+ * the oldest tx attempts fall out of the window.
+ *
+ * When the agn device makes multiple tx attempts for a given frame, each
+ * attempt might be at a different rate, and have different modulation
+ * characteristics (e.g. antenna, fat channel, short guard interval), as set
+ * up in the rate scaling table in the Link Quality command. The driver must
+ * determine which rate table entry was used for each tx attempt, to determine
+ * which rate-specific history to update, and record only those attempts that
+ * match the modulation characteristics of the history set.
+ *
+ * When using block-ack (aggregation), all frames are transmitted at the same
+ * rate, since there is no per-attempt acknowledgment from the destination
+ * station. The Tx response struct iwl_tx_resp indicates the Tx rate in
+ * rate_n_flags field. After receiving a block-ack, the driver can update
+ * history for the entire block all at once.
+ *
+ *
+ * FINDING BEST STARTING RATE:
+ *
+ * When working with a selected initial modulation mode (see below), the
+ * driver attempts to find a best initial rate. The initial rate is the
+ * first entry in the Link Quality command's rate table.
+ *
+ * 1) Calculate actual throughput (success ratio * expected throughput, see
+ * table below) for current initial rate. Do this only if enough frames
+ * have been attempted to make the value meaningful: at least 6 failed
+ * tx attempts, or at least 8 successes. If not enough, don't try rate
+ * scaling yet.
+ *
+ * 2) Find available rates adjacent to current initial rate. Available means:
+ * a) supported by hardware &&
+ * b) supported by association &&
+ * c) within any constraints selected by user
+ *
+ * 3) Gather measured throughputs for adjacent rates. These might not have
+ * enough history to calculate a throughput. That's okay, we might try
+ * using one of them anyway!
+ *
+ * 4) Try decreasing rate if, for current rate:
+ * a) success ratio is < 15% ||
+ * b) lower adjacent rate has better measured throughput ||
+ * c) higher adjacent rate has worse throughput, and lower is unmeasured
+ *
+ * As a sanity check, if decrease was determined above, leave rate
+ * unchanged if:
+ * a) lower rate unavailable
+ * b) success ratio at current rate > 85% (very good)
+ * c) current measured throughput is better than expected throughput
+ * of lower rate (under perfect 100% tx conditions, see table below)
+ *
+ * 5) Try increasing rate if, for current rate:
+ * a) success ratio is < 15% ||
+ * b) both adjacent rates' throughputs are unmeasured (try it!) ||
+ * b) higher adjacent rate has better measured throughput ||
+ * c) lower adjacent rate has worse throughput, and higher is unmeasured
+ *
+ * As a sanity check, if increase was determined above, leave rate
+ * unchanged if:
+ * a) success ratio at current rate < 70%. This is not particularly
+ * good performance; higher rate is sure to have poorer success.
+ *
+ * 6) Re-evaluate the rate after each tx frame. If working with block-
+ * acknowledge, history and statistics may be calculated for the entire
+ * block (including prior history that fits within the history windows),
+ * before re-evaluation.
+ *
+ * FINDING BEST STARTING MODULATION MODE:
+ *
+ * After working with a modulation mode for a "while" (and doing rate scaling),
+ * the driver searches for a new initial mode in an attempt to improve
+ * throughput. The "while" is measured by numbers of attempted frames:
+ *
+ * For legacy mode, search for new mode after:
+ * 480 successful frames, or 160 failed frames
+ * For high-throughput modes (SISO or MIMO), search for new mode after:
+ * 4500 successful frames, or 400 failed frames
+ *
+ * Mode switch possibilities are (3 for each mode):
+ *
+ * For legacy:
+ * Change antenna, try SISO (if HT association), try MIMO (if HT association)
+ * For SISO:
+ * Change antenna, try MIMO, try shortened guard interval (SGI)
+ * For MIMO:
+ * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI)
+ *
+ * When trying a new mode, use the same bit rate as the old/current mode when
+ * trying antenna switches and shortened guard interval. When switching to
+ * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate
+ * for which the expected throughput (under perfect conditions) is about the
+ * same or slightly better than the actual measured throughput delivered by
+ * the old/current mode.
+ *
+ * Actual throughput can be estimated by multiplying the expected throughput
+ * by the success ratio (successful / attempted tx frames). Frame size is
+ * not considered in this calculation; it assumes that frame size will average
+ * out to be fairly consistent over several samples. The following are
+ * metric values for expected throughput assuming 100% success ratio.
+ * Only G band has support for CCK rates:
+ *
+ * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60
+ *
+ * G: 7 13 35 58 40 57 72 98 121 154 177 186 186
+ * A: 0 0 0 0 40 57 72 98 121 154 177 186 186
+ * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202
+ * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211
+ * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251
+ * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257
+ * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257
+ * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264
+ * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289
+ * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293
+ *
+ * After the new mode has been tried for a short while (minimum of 6 failed
+ * frames or 8 successful frames), compare success ratio and actual throughput
+ * estimate of the new mode with the old. If either is better with the new
+ * mode, continue to use the new mode.
+ *
+ * Continue comparing modes until all 3 possibilities have been tried.
+ * If moving from legacy to HT, try all 3 possibilities from the new HT
+ * mode. After trying all 3, a best mode is found. Continue to use this mode
+ * for the longer "while" described above (e.g. 480 successful frames for
+ * legacy), and then repeat the search process.
+ *
+ */
+struct iwl_link_quality_cmd {
+
+ /* Index of destination/recipient station in uCode's station table */
+ u8 sta_id;
+ u8 reserved1;
+ __le16 control; /* not used */
+ struct iwl_link_qual_general_params general_params;
+ struct iwl_link_qual_agg_params agg_params;
+
+ /*
+ * Rate info; when using rate-scaling, Tx command's initial_rate_index
+ * specifies 1st Tx rate attempted, via index into this table.
+ * agn devices works its way through table when retrying Tx.
+ */
+ struct {
+ __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */
+ } rs_table[LINK_QUAL_MAX_RETRY_NUM];
+ __le32 reserved2;
+} __packed;
+
+/*
+ * BT configuration enable flags:
+ * bit 0 - 1: BT channel announcement enabled
+ * 0: disable
+ * bit 1 - 1: priority of BT device enabled
+ * 0: disable
+ * bit 2 - 1: BT 2 wire support enabled
+ * 0: disable
+ */
+#define BT_COEX_DISABLE (0x0)
+#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0)
+#define BT_ENABLE_PRIORITY BIT(1)
+#define BT_ENABLE_2_WIRE BIT(2)
+
+#define BT_COEX_DISABLE (0x0)
+#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY)
+
+#define BT_LEAD_TIME_MIN (0x0)
+#define BT_LEAD_TIME_DEF (0x1E)
+#define BT_LEAD_TIME_MAX (0xFF)
+
+#define BT_MAX_KILL_MIN (0x1)
+#define BT_MAX_KILL_DEF (0x5)
+#define BT_MAX_KILL_MAX (0xFF)
+
+#define BT_DURATION_LIMIT_DEF 625
+#define BT_DURATION_LIMIT_MAX 1250
+#define BT_DURATION_LIMIT_MIN 625
+
+#define BT_ON_THRESHOLD_DEF 4
+#define BT_ON_THRESHOLD_MAX 1000
+#define BT_ON_THRESHOLD_MIN 1
+
+#define BT_FRAG_THRESHOLD_DEF 0
+#define BT_FRAG_THRESHOLD_MAX 0
+#define BT_FRAG_THRESHOLD_MIN 0
+
+#define BT_AGG_THRESHOLD_DEF 1200
+#define BT_AGG_THRESHOLD_MAX 8000
+#define BT_AGG_THRESHOLD_MIN 400
+
+/*
+ * REPLY_BT_CONFIG = 0x9b (command, has simple generic response)
+ *
+ * agn devices support hardware handshake with Bluetooth device on
+ * same platform. Bluetooth device alerts wireless device when it will Tx;
+ * wireless device can delay or kill its own Tx to accommodate.
+ */
+struct iwl_bt_cmd {
+ u8 flags;
+ u8 lead_time;
+ u8 max_kill;
+ u8 reserved;
+ __le32 kill_ack_mask;
+ __le32 kill_cts_mask;
+} __packed;
+
+#define IWLAGN_BT_FLAG_CHANNEL_INHIBITION BIT(0)
+
+#define IWLAGN_BT_FLAG_COEX_MODE_MASK (BIT(3)|BIT(4)|BIT(5))
+#define IWLAGN_BT_FLAG_COEX_MODE_SHIFT 3
+#define IWLAGN_BT_FLAG_COEX_MODE_DISABLED 0
+#define IWLAGN_BT_FLAG_COEX_MODE_LEGACY_2W 1
+#define IWLAGN_BT_FLAG_COEX_MODE_3W 2
+#define IWLAGN_BT_FLAG_COEX_MODE_4W 3
+
+#define IWLAGN_BT_FLAG_UCODE_DEFAULT BIT(6)
+/* Disable Sync PSPoll on SCO/eSCO */
+#define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7)
+
+#define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */
+#define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */
+
+#define IWLAGN_BT_PRIO_BOOST_MAX 0xFF
+#define IWLAGN_BT_PRIO_BOOST_MIN 0x00
+#define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0
+#define IWLAGN_BT_PRIO_BOOST_DEFAULT32 0xF0F0F0F0
+
+#define IWLAGN_BT_MAX_KILL_DEFAULT 5
+
+#define IWLAGN_BT3_T7_DEFAULT 1
+
+enum iwl_bt_kill_idx {
+ IWL_BT_KILL_DEFAULT = 0,
+ IWL_BT_KILL_OVERRIDE = 1,
+ IWL_BT_KILL_REDUCE = 2,
+};
+
+#define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000)
+#define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000)
+#define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff)
+#define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0)
+
+#define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2
+
+#define IWLAGN_BT3_T2_DEFAULT 0xc
+
+#define IWLAGN_BT_VALID_ENABLE_FLAGS cpu_to_le16(BIT(0))
+#define IWLAGN_BT_VALID_BOOST cpu_to_le16(BIT(1))
+#define IWLAGN_BT_VALID_MAX_KILL cpu_to_le16(BIT(2))
+#define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3))
+#define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4))
+#define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5))
+#define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6))
+#define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7))
+
+#define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \
+ IWLAGN_BT_VALID_BOOST | \
+ IWLAGN_BT_VALID_MAX_KILL | \
+ IWLAGN_BT_VALID_3W_TIMERS | \
+ IWLAGN_BT_VALID_KILL_ACK_MASK | \
+ IWLAGN_BT_VALID_KILL_CTS_MASK | \
+ IWLAGN_BT_VALID_REDUCED_TX_PWR | \
+ IWLAGN_BT_VALID_3W_LUT)
+
+#define IWLAGN_BT_REDUCED_TX_PWR BIT(0)
+
+#define IWLAGN_BT_DECISION_LUT_SIZE 12
+
+struct iwl_basic_bt_cmd {
+ u8 flags;
+ u8 ledtime; /* unused */
+ u8 max_kill;
+ u8 bt3_timer_t7_value;
+ __le32 kill_ack_mask;
+ __le32 kill_cts_mask;
+ u8 bt3_prio_sample_time;
+ u8 bt3_timer_t2_value;
+ __le16 bt4_reaction_time; /* unused */
+ __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE];
+ /*
+ * bit 0: use reduced tx power for control frame
+ * bit 1 - 7: reserved
+ */
+ u8 reduce_txpower;
+ u8 reserved;
+ __le16 valid;
+};
+
+struct iwl_bt_cmd_v1 {
+ struct iwl_basic_bt_cmd basic;
+ u8 prio_boost;
+ /*
+ * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask
+ * if configure the following patterns
+ */
+ u8 tx_prio_boost; /* SW boost of WiFi tx priority */
+ __le16 rx_prio_boost; /* SW boost of WiFi rx priority */
+};
+
+struct iwl_bt_cmd_v2 {
+ struct iwl_basic_bt_cmd basic;
+ __le32 prio_boost;
+ /*
+ * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask
+ * if configure the following patterns
+ */
+ u8 reserved;
+ u8 tx_prio_boost; /* SW boost of WiFi tx priority */
+ __le16 rx_prio_boost; /* SW boost of WiFi rx priority */
+};
+
+#define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0))
+
+struct iwlagn_bt_sco_cmd {
+ __le32 flags;
+};
+
+/******************************************************************************
+ * (6)
+ * Spectrum Management (802.11h) Commands, Responses, Notifications:
+ *
+ *****************************************************************************/
+
+/*
+ * Spectrum Management
+ */
+#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \
+ RXON_FILTER_CTL2HOST_MSK | \
+ RXON_FILTER_ACCEPT_GRP_MSK | \
+ RXON_FILTER_DIS_DECRYPT_MSK | \
+ RXON_FILTER_DIS_GRP_DECRYPT_MSK | \
+ RXON_FILTER_ASSOC_MSK | \
+ RXON_FILTER_BCON_AWARE_MSK)
+
+struct iwl_measure_channel {
+ __le32 duration; /* measurement duration in extended beacon
+ * format */
+ u8 channel; /* channel to measure */
+ u8 type; /* see enum iwl_measure_type */
+ __le16 reserved;
+} __packed;
+
+/*
+ * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command)
+ */
+struct iwl_spectrum_cmd {
+ __le16 len; /* number of bytes starting from token */
+ u8 token; /* token id */
+ u8 id; /* measurement id -- 0 or 1 */
+ u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */
+ u8 periodic; /* 1 = periodic */
+ __le16 path_loss_timeout;
+ __le32 start_time; /* start time in extended beacon format */
+ __le32 reserved2;
+ __le32 flags; /* rxon flags */
+ __le32 filter_flags; /* rxon filter flags */
+ __le16 channel_count; /* minimum 1, maximum 10 */
+ __le16 reserved3;
+ struct iwl_measure_channel channels[10];
+} __packed;
+
+/*
+ * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response)
+ */
+struct iwl_spectrum_resp {
+ u8 token;
+ u8 id; /* id of the prior command replaced, or 0xff */
+ __le16 status; /* 0 - command will be handled
+ * 1 - cannot handle (conflicts with another
+ * measurement) */
+} __packed;
+
+enum iwl_measurement_state {
+ IWL_MEASUREMENT_START = 0,
+ IWL_MEASUREMENT_STOP = 1,
+};
+
+enum iwl_measurement_status {
+ IWL_MEASUREMENT_OK = 0,
+ IWL_MEASUREMENT_CONCURRENT = 1,
+ IWL_MEASUREMENT_CSA_CONFLICT = 2,
+ IWL_MEASUREMENT_TGH_CONFLICT = 3,
+ /* 4-5 reserved */
+ IWL_MEASUREMENT_STOPPED = 6,
+ IWL_MEASUREMENT_TIMEOUT = 7,
+ IWL_MEASUREMENT_PERIODIC_FAILED = 8,
+};
+
+#define NUM_ELEMENTS_IN_HISTOGRAM 8
+
+struct iwl_measurement_histogram {
+ __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */
+ __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */
+} __packed;
+
+/* clear channel availability counters */
+struct iwl_measurement_cca_counters {
+ __le32 ofdm;
+ __le32 cck;
+} __packed;
+
+enum iwl_measure_type {
+ IWL_MEASURE_BASIC = (1 << 0),
+ IWL_MEASURE_CHANNEL_LOAD = (1 << 1),
+ IWL_MEASURE_HISTOGRAM_RPI = (1 << 2),
+ IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3),
+ IWL_MEASURE_FRAME = (1 << 4),
+ /* bits 5:6 are reserved */
+ IWL_MEASURE_IDLE = (1 << 7),
+};
+
+/*
+ * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command)
+ */
+struct iwl_spectrum_notification {
+ u8 id; /* measurement id -- 0 or 1 */
+ u8 token;
+ u8 channel_index; /* index in measurement channel list */
+ u8 state; /* 0 - start, 1 - stop */
+ __le32 start_time; /* lower 32-bits of TSF */
+ u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */
+ u8 channel;
+ u8 type; /* see enum iwl_measurement_type */
+ u8 reserved1;
+ /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only
+ * valid if applicable for measurement type requested. */
+ __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */
+ __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */
+ __le32 cca_time; /* channel load time in usecs */
+ u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 -
+ * unidentified */
+ u8 reserved2[3];
+ struct iwl_measurement_histogram histogram;
+ __le32 stop_time; /* lower 32-bits of TSF */
+ __le32 status; /* see iwl_measurement_status */
+} __packed;
+
+/******************************************************************************
+ * (7)
+ * Power Management Commands, Responses, Notifications:
+ *
+ *****************************************************************************/
+
+/**
+ * struct iwl_powertable_cmd - Power Table Command
+ * @flags: See below:
+ *
+ * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
+ *
+ * PM allow:
+ * bit 0 - '0' Driver not allow power management
+ * '1' Driver allow PM (use rest of parameters)
+ *
+ * uCode send sleep notifications:
+ * bit 1 - '0' Don't send sleep notification
+ * '1' send sleep notification (SEND_PM_NOTIFICATION)
+ *
+ * Sleep over DTIM
+ * bit 2 - '0' PM have to walk up every DTIM
+ * '1' PM could sleep over DTIM till listen Interval.
+ *
+ * PCI power managed
+ * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1)
+ * '1' !(PCI_CFG_LINK_CTRL & 0x1)
+ *
+ * Fast PD
+ * bit 4 - '1' Put radio to sleep when receiving frame for others
+ *
+ * Force sleep Modes
+ * bit 31/30- '00' use both mac/xtal sleeps
+ * '01' force Mac sleep
+ * '10' force xtal sleep
+ * '11' Illegal set
+ *
+ * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then
+ * ucode assume sleep over DTIM is allowed and we don't need to wake up
+ * for every DTIM.
+ */
+#define IWL_POWER_VEC_SIZE 5
+
+#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0))
+#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0))
+#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1))
+#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2))
+#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3))
+#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4))
+#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5))
+#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6))
+#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7))
+#define IWL_POWER_BT_SCO_ENA cpu_to_le16(BIT(8))
+#define IWL_POWER_ADVANCE_PM_ENA_MSK cpu_to_le16(BIT(9))
+
+struct iwl_powertable_cmd {
+ __le16 flags;
+ u8 keep_alive_seconds;
+ u8 debug_flags;
+ __le32 rx_data_timeout;
+ __le32 tx_data_timeout;
+ __le32 sleep_interval[IWL_POWER_VEC_SIZE];
+ __le32 keep_alive_beacons;
+} __packed;
+
+/*
+ * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command)
+ * all devices identical.
+ */
+struct iwl_sleep_notification {
+ u8 pm_sleep_mode;
+ u8 pm_wakeup_src;
+ __le16 reserved;
+ __le32 sleep_time;
+ __le32 tsf_low;
+ __le32 bcon_timer;
+} __packed;
+
+/* Sleep states. all devices identical. */
+enum {
+ IWL_PM_NO_SLEEP = 0,
+ IWL_PM_SLP_MAC = 1,
+ IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2,
+ IWL_PM_SLP_FULL_MAC_CARD_STATE = 3,
+ IWL_PM_SLP_PHY = 4,
+ IWL_PM_SLP_REPENT = 5,
+ IWL_PM_WAKEUP_BY_TIMER = 6,
+ IWL_PM_WAKEUP_BY_DRIVER = 7,
+ IWL_PM_WAKEUP_BY_RFKILL = 8,
+ /* 3 reserved */
+ IWL_PM_NUM_OF_MODES = 12,
+};
+
+/*
+ * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response)
+ */
+#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */
+#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */
+#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */
+struct iwl_card_state_cmd {
+ __le32 status; /* CARD_STATE_CMD_* request new power state */
+} __packed;
+
+/*
+ * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command)
+ */
+struct iwl_card_state_notif {
+ __le32 flags;
+} __packed;
+
+#define HW_CARD_DISABLED 0x01
+#define SW_CARD_DISABLED 0x02
+#define CT_CARD_DISABLED 0x04
+#define RXON_CARD_DISABLED 0x10
+
+struct iwl_ct_kill_config {
+ __le32 reserved;
+ __le32 critical_temperature_M;
+ __le32 critical_temperature_R;
+} __packed;
+
+/* 1000, and 6x00 */
+struct iwl_ct_kill_throttling_config {
+ __le32 critical_temperature_exit;
+ __le32 reserved;
+ __le32 critical_temperature_enter;
+} __packed;
+
+/******************************************************************************
+ * (8)
+ * Scan Commands, Responses, Notifications:
+ *
+ *****************************************************************************/
+
+#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0)
+#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1)
+
+/**
+ * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
+ *
+ * One for each channel in the scan list.
+ * Each channel can independently select:
+ * 1) SSID for directed active scans
+ * 2) Txpower setting (for rate specified within Tx command)
+ * 3) How long to stay on-channel (behavior may be modified by quiet_time,
+ * quiet_plcp_th, good_CRC_th)
+ *
+ * To avoid uCode errors, make sure the following are true (see comments
+ * under struct iwl_scan_cmd about max_out_time and quiet_time):
+ * 1) If using passive_dwell (i.e. passive_dwell != 0):
+ * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0)
+ * 2) quiet_time <= active_dwell
+ * 3) If restricting off-channel time (i.e. max_out_time !=0):
+ * passive_dwell < max_out_time
+ * active_dwell < max_out_time
+ */
+
+struct iwl_scan_channel {
+ /*
+ * type is defined as:
+ * 0:0 1 = active, 0 = passive
+ * 1:20 SSID direct bit map; if a bit is set, then corresponding
+ * SSID IE is transmitted in probe request.
+ * 21:31 reserved
+ */
+ __le32 type;
+ __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */
+ u8 tx_gain; /* gain for analog radio */
+ u8 dsp_atten; /* gain for DSP */
+ __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */
+ __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */
+} __packed;
+
+/* set number of direct probes __le32 type */
+#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1))))
+
+/**
+ * struct iwl_ssid_ie - directed scan network information element
+ *
+ * Up to 20 of these may appear in REPLY_SCAN_CMD,
+ * selected by "type" bit field in struct iwl_scan_channel;
+ * each channel may select different ssids from among the 20 entries.
+ * SSID IEs get transmitted in reverse order of entry.
+ */
+struct iwl_ssid_ie {
+ u8 id;
+ u8 len;
+ u8 ssid[32];
+} __packed;
+
+#define PROBE_OPTION_MAX 20
+#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF)
+#define IWL_GOOD_CRC_TH_DISABLED 0
+#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1)
+#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff)
+#define IWL_MAX_CMD_SIZE 4096
+
+/*
+ * REPLY_SCAN_CMD = 0x80 (command)
+ *
+ * The hardware scan command is very powerful; the driver can set it up to
+ * maintain (relatively) normal network traffic while doing a scan in the
+ * background. The max_out_time and suspend_time control the ratio of how
+ * long the device stays on an associated network channel ("service channel")
+ * vs. how long it's away from the service channel, i.e. tuned to other channels
+ * for scanning.
+ *
+ * max_out_time is the max time off-channel (in usec), and suspend_time
+ * is how long (in "extended beacon" format) that the scan is "suspended"
+ * after returning to the service channel. That is, suspend_time is the
+ * time that we stay on the service channel, doing normal work, between
+ * scan segments. The driver may set these parameters differently to support
+ * scanning when associated vs. not associated, and light vs. heavy traffic
+ * loads when associated.
+ *
+ * After receiving this command, the device's scan engine does the following;
+ *
+ * 1) Sends SCAN_START notification to driver
+ * 2) Checks to see if it has time to do scan for one channel
+ * 3) Sends NULL packet, with power-save (PS) bit set to 1,
+ * to tell AP that we're going off-channel
+ * 4) Tunes to first channel in scan list, does active or passive scan
+ * 5) Sends SCAN_RESULT notification to driver
+ * 6) Checks to see if it has time to do scan on *next* channel in list
+ * 7) Repeats 4-6 until it no longer has time to scan the next channel
+ * before max_out_time expires
+ * 8) Returns to service channel
+ * 9) Sends NULL packet with PS=0 to tell AP that we're back
+ * 10) Stays on service channel until suspend_time expires
+ * 11) Repeats entire process 2-10 until list is complete
+ * 12) Sends SCAN_COMPLETE notification
+ *
+ * For fast, efficient scans, the scan command also has support for staying on
+ * a channel for just a short time, if doing active scanning and getting no
+ * responses to the transmitted probe request. This time is controlled by
+ * quiet_time, and the number of received packets below which a channel is
+ * considered "quiet" is controlled by quiet_plcp_threshold.
+ *
+ * For active scanning on channels that have regulatory restrictions against
+ * blindly transmitting, the scan can listen before transmitting, to make sure
+ * that there is already legitimate activity on the channel. If enough
+ * packets are cleanly received on the channel (controlled by good_CRC_th,
+ * typical value 1), the scan engine starts transmitting probe requests.
+ *
+ * Driver must use separate scan commands for 2.4 vs. 5 GHz bands.
+ *
+ * To avoid uCode errors, see timing restrictions described under
+ * struct iwl_scan_channel.
+ */
+
+enum iwl_scan_flags {
+ /* BIT(0) currently unused */
+ IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1),
+ /* bits 2-7 reserved */
+};
+
+struct iwl_scan_cmd {
+ __le16 len;
+ u8 scan_flags; /* scan flags: see enum iwl_scan_flags */
+ u8 channel_count; /* # channels in channel list */
+ __le16 quiet_time; /* dwell only this # millisecs on quiet channel
+ * (only for active scan) */
+ __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */
+ __le16 good_CRC_th; /* passive -> active promotion threshold */
+ __le16 rx_chain; /* RXON_RX_CHAIN_* */
+ __le32 max_out_time; /* max usec to be away from associated (service)
+ * channel */
+ __le32 suspend_time; /* pause scan this long (in "extended beacon
+ * format") when returning to service chnl:
+ */
+ __le32 flags; /* RXON_FLG_* */
+ __le32 filter_flags; /* RXON_FILTER_* */
+
+ /* For active scans (set to all-0s for passive scans).
+ * Does not include payload. Must specify Tx rate; no rate scaling. */
+ struct iwl_tx_cmd tx_cmd;
+
+ /* For directed active scans (set to all-0s otherwise) */
+ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+
+ /*
+ * Probe request frame, followed by channel list.
+ *
+ * Size of probe request frame is specified by byte count in tx_cmd.
+ * Channel list follows immediately after probe request frame.
+ * Number of channels in list is specified by channel_count.
+ * Each channel in list is of type:
+ *
+ * struct iwl_scan_channel channels[0];
+ *
+ * NOTE: Only one band of channels can be scanned per pass. You
+ * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
+ * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
+ * before requesting another scan.
+ */
+ u8 data[0];
+} __packed;
+
+/* Can abort will notify by complete notification with abort status. */
+#define CAN_ABORT_STATUS cpu_to_le32(0x1)
+/* complete notification statuses */
+#define ABORT_STATUS 0x2
+
+/*
+ * REPLY_SCAN_CMD = 0x80 (response)
+ */
+struct iwl_scanreq_notification {
+ __le32 status; /* 1: okay, 2: cannot fulfill request */
+} __packed;
+
+/*
+ * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command)
+ */
+struct iwl_scanstart_notification {
+ __le32 tsf_low;
+ __le32 tsf_high;
+ __le32 beacon_timer;
+ u8 channel;
+ u8 band;
+ u8 reserved[2];
+ __le32 status;
+} __packed;
+
+#define SCAN_OWNER_STATUS 0x1
+#define MEASURE_OWNER_STATUS 0x2
+
+#define IWL_PROBE_STATUS_OK 0
+#define IWL_PROBE_STATUS_TX_FAILED BIT(0)
+/* error statuses combined with TX_FAILED */
+#define IWL_PROBE_STATUS_FAIL_TTL BIT(1)
+#define IWL_PROBE_STATUS_FAIL_BT BIT(2)
+
+#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */
+/*
+ * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command)
+ */
+struct iwl_scanresults_notification {
+ u8 channel;
+ u8 band;
+ u8 probe_status;
+ u8 num_probe_not_sent; /* not enough time to send */
+ __le32 tsf_low;
+ __le32 tsf_high;
+ __le32 statistics[NUMBER_OF_STATISTICS];
+} __packed;
+
+/*
+ * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command)
+ */
+struct iwl_scancomplete_notification {
+ u8 scanned_channels;
+ u8 status;
+ u8 bt_status; /* BT On/Off status */
+ u8 last_channel;
+ __le32 tsf_low;
+ __le32 tsf_high;
+} __packed;
+
+
+/******************************************************************************
+ * (9)
+ * IBSS/AP Commands and Notifications:
+ *
+ *****************************************************************************/
+
+enum iwl_ibss_manager {
+ IWL_NOT_IBSS_MANAGER = 0,
+ IWL_IBSS_MANAGER = 1,
+};
+
+/*
+ * BEACON_NOTIFICATION = 0x90 (notification only, not a command)
+ */
+
+struct iwlagn_beacon_notif {
+ struct iwlagn_tx_resp beacon_notify_hdr;
+ __le32 low_tsf;
+ __le32 high_tsf;
+ __le32 ibss_mgr_status;
+} __packed;
+
+/*
+ * REPLY_TX_BEACON = 0x91 (command, has simple generic response)
+ */
+
+struct iwl_tx_beacon_cmd {
+ struct iwl_tx_cmd tx;
+ __le16 tim_idx;
+ u8 tim_size;
+ u8 reserved1;
+ struct ieee80211_hdr frame[0]; /* beacon frame */
+} __packed;
+
+/******************************************************************************
+ * (10)
+ * Statistics Commands and Notifications:
+ *
+ *****************************************************************************/
+
+#define IWL_TEMP_CONVERT 260
+
+#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
+#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
+#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
+
+/* Used for passing to driver number of successes and failures per rate */
+struct rate_histogram {
+ union {
+ __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+ __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+ __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+ } success;
+ union {
+ __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+ __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+ __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+ } failed;
+} __packed;
+
+/* statistics command response */
+
+struct statistics_dbg {
+ __le32 burst_check;
+ __le32 burst_count;
+ __le32 wait_for_silence_timeout_cnt;
+ __le32 reserved[3];
+} __packed;
+
+struct statistics_rx_phy {
+ __le32 ina_cnt;
+ __le32 fina_cnt;
+ __le32 plcp_err;
+ __le32 crc32_err;
+ __le32 overrun_err;
+ __le32 early_overrun_err;
+ __le32 crc32_good;
+ __le32 false_alarm_cnt;
+ __le32 fina_sync_err_cnt;
+ __le32 sfd_timeout;
+ __le32 fina_timeout;
+ __le32 unresponded_rts;
+ __le32 rxe_frame_limit_overrun;
+ __le32 sent_ack_cnt;
+ __le32 sent_cts_cnt;
+ __le32 sent_ba_rsp_cnt;
+ __le32 dsp_self_kill;
+ __le32 mh_format_err;
+ __le32 re_acq_main_rssi_sum;
+ __le32 reserved3;
+} __packed;
+
+struct statistics_rx_ht_phy {
+ __le32 plcp_err;
+ __le32 overrun_err;
+ __le32 early_overrun_err;
+ __le32 crc32_good;
+ __le32 crc32_err;
+ __le32 mh_format_err;
+ __le32 agg_crc32_good;
+ __le32 agg_mpdu_cnt;
+ __le32 agg_cnt;
+ __le32 unsupport_mcs;
+} __packed;
+
+#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1)
+
+struct statistics_rx_non_phy {
+ __le32 bogus_cts; /* CTS received when not expecting CTS */
+ __le32 bogus_ack; /* ACK received when not expecting ACK */
+ __le32 non_bssid_frames; /* number of frames with BSSID that
+ * doesn't belong to the STA BSSID */
+ __le32 filtered_frames; /* count frames that were dumped in the
+ * filtering process */
+ __le32 non_channel_beacons; /* beacons with our bss id but not on
+ * our serving channel */
+ __le32 channel_beacons; /* beacons with our bss id and in our
+ * serving channel */
+ __le32 num_missed_bcon; /* number of missed beacons */
+ __le32 adc_rx_saturation_time; /* count in 0.8us units the time the
+ * ADC was in saturation */
+ __le32 ina_detection_search_time;/* total time (in 0.8us) searched
+ * for INA */
+ __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */
+ __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */
+ __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */
+ __le32 interference_data_flag; /* flag for interference data
+ * availability. 1 when data is
+ * available. */
+ __le32 channel_load; /* counts RX Enable time in uSec */
+ __le32 dsp_false_alarms; /* DSP false alarm (both OFDM
+ * and CCK) counter */
+ __le32 beacon_rssi_a;
+ __le32 beacon_rssi_b;
+ __le32 beacon_rssi_c;
+ __le32 beacon_energy_a;
+ __le32 beacon_energy_b;
+ __le32 beacon_energy_c;
+} __packed;
+
+struct statistics_rx_non_phy_bt {
+ struct statistics_rx_non_phy common;
+ /* additional stats for bt */
+ __le32 num_bt_kills;
+ __le32 reserved[2];
+} __packed;
+
+struct statistics_rx {
+ struct statistics_rx_phy ofdm;
+ struct statistics_rx_phy cck;
+ struct statistics_rx_non_phy general;
+ struct statistics_rx_ht_phy ofdm_ht;
+} __packed;
+
+struct statistics_rx_bt {
+ struct statistics_rx_phy ofdm;
+ struct statistics_rx_phy cck;
+ struct statistics_rx_non_phy_bt general;
+ struct statistics_rx_ht_phy ofdm_ht;
+} __packed;
+
+/**
+ * struct statistics_tx_power - current tx power
+ *
+ * @ant_a: current tx power on chain a in 1/2 dB step
+ * @ant_b: current tx power on chain b in 1/2 dB step
+ * @ant_c: current tx power on chain c in 1/2 dB step
+ */
+struct statistics_tx_power {
+ u8 ant_a;
+ u8 ant_b;
+ u8 ant_c;
+ u8 reserved;
+} __packed;
+
+struct statistics_tx_non_phy_agg {
+ __le32 ba_timeout;
+ __le32 ba_reschedule_frames;
+ __le32 scd_query_agg_frame_cnt;
+ __le32 scd_query_no_agg;
+ __le32 scd_query_agg;
+ __le32 scd_query_mismatch;
+ __le32 frame_not_ready;
+ __le32 underrun;
+ __le32 bt_prio_kill;
+ __le32 rx_ba_rsp_cnt;
+} __packed;
+
+struct statistics_tx {
+ __le32 preamble_cnt;
+ __le32 rx_detected_cnt;
+ __le32 bt_prio_defer_cnt;
+ __le32 bt_prio_kill_cnt;
+ __le32 few_bytes_cnt;
+ __le32 cts_timeout;
+ __le32 ack_timeout;
+ __le32 expected_ack_cnt;
+ __le32 actual_ack_cnt;
+ __le32 dump_msdu_cnt;
+ __le32 burst_abort_next_frame_mismatch_cnt;
+ __le32 burst_abort_missing_next_frame_cnt;
+ __le32 cts_timeout_collision;
+ __le32 ack_or_ba_timeout_collision;
+ struct statistics_tx_non_phy_agg agg;
+ /*
+ * "tx_power" are optional parameters provided by uCode,
+ * 6000 series is the only device provide the information,
+ * Those are reserved fields for all the other devices
+ */
+ struct statistics_tx_power tx_power;
+ __le32 reserved1;
+} __packed;
+
+
+struct statistics_div {
+ __le32 tx_on_a;
+ __le32 tx_on_b;
+ __le32 exec_time;
+ __le32 probe_time;
+ __le32 reserved1;
+ __le32 reserved2;
+} __packed;
+
+struct statistics_general_common {
+ __le32 temperature; /* radio temperature */
+ __le32 temperature_m; /* radio voltage */
+ struct statistics_dbg dbg;
+ __le32 sleep_time;
+ __le32 slots_out;
+ __le32 slots_idle;
+ __le32 ttl_timestamp;
+ struct statistics_div div;
+ __le32 rx_enable_counter;
+ /*
+ * num_of_sos_states:
+ * count the number of times we have to re-tune
+ * in order to get out of bad PHY status
+ */
+ __le32 num_of_sos_states;
+} __packed;
+
+struct statistics_bt_activity {
+ /* Tx statistics */
+ __le32 hi_priority_tx_req_cnt;
+ __le32 hi_priority_tx_denied_cnt;
+ __le32 lo_priority_tx_req_cnt;
+ __le32 lo_priority_tx_denied_cnt;
+ /* Rx statistics */
+ __le32 hi_priority_rx_req_cnt;
+ __le32 hi_priority_rx_denied_cnt;
+ __le32 lo_priority_rx_req_cnt;
+ __le32 lo_priority_rx_denied_cnt;
+} __packed;
+
+struct statistics_general {
+ struct statistics_general_common common;
+ __le32 reserved2;
+ __le32 reserved3;
+} __packed;
+
+struct statistics_general_bt {
+ struct statistics_general_common common;
+ struct statistics_bt_activity activity;
+ __le32 reserved2;
+ __le32 reserved3;
+} __packed;
+
+#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0)
+#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1)
+#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2)
+
+/*
+ * REPLY_STATISTICS_CMD = 0x9c,
+ * all devices identical.
+ *
+ * This command triggers an immediate response containing uCode statistics.
+ * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below.
+ *
+ * If the CLEAR_STATS configuration flag is set, uCode will clear its
+ * internal copy of the statistics (counters) after issuing the response.
+ * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below).
+ *
+ * If the DISABLE_NOTIF configuration flag is set, uCode will not issue
+ * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag
+ * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself.
+ */
+#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */
+#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */
+struct iwl_statistics_cmd {
+ __le32 configuration_flags; /* IWL_STATS_CONF_* */
+} __packed;
+
+/*
+ * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
+ *
+ * By default, uCode issues this notification after receiving a beacon
+ * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
+ * REPLY_STATISTICS_CMD 0x9c, above.
+ *
+ * Statistics counters continue to increment beacon after beacon, but are
+ * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
+ * 0x9c with CLEAR_STATS bit set (see above).
+ *
+ * uCode also issues this notification during scans. uCode clears statistics
+ * appropriately so that each notification contains statistics for only the
+ * one channel that has just been scanned.
+ */
+#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2)
+#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8)
+
+struct iwl_notif_statistics {
+ __le32 flag;
+ struct statistics_rx rx;
+ struct statistics_tx tx;
+ struct statistics_general general;
+} __packed;
+
+struct iwl_bt_notif_statistics {
+ __le32 flag;
+ struct statistics_rx_bt rx;
+ struct statistics_tx tx;
+ struct statistics_general_bt general;
+} __packed;
+
+/*
+ * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command)
+ *
+ * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed
+ * in regardless of how many missed beacons, which mean when driver receive the
+ * notification, inside the command, it can find all the beacons information
+ * which include number of total missed beacons, number of consecutive missed
+ * beacons, number of beacons received and number of beacons expected to
+ * receive.
+ *
+ * If uCode detected consecutive_missed_beacons > 5, it will reset the radio
+ * in order to bring the radio/PHY back to working state; which has no relation
+ * to when driver will perform sensitivity calibration.
+ *
+ * Driver should set it own missed_beacon_threshold to decide when to perform
+ * sensitivity calibration based on number of consecutive missed beacons in
+ * order to improve overall performance, especially in noisy environment.
+ *
+ */
+
+#define IWL_MISSED_BEACON_THRESHOLD_MIN (1)
+#define IWL_MISSED_BEACON_THRESHOLD_DEF (5)
+#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF
+
+struct iwl_missed_beacon_notif {
+ __le32 consecutive_missed_beacons;
+ __le32 total_missed_becons;
+ __le32 num_expected_beacons;
+ __le32 num_recvd_beacons;
+} __packed;
+
+
+/******************************************************************************
+ * (11)
+ * Rx Calibration Commands:
+ *
+ * With the uCode used for open source drivers, most Tx calibration (except
+ * for Tx Power) and most Rx calibration is done by uCode during the
+ * "initialize" phase of uCode boot. Driver must calibrate only:
+ *
+ * 1) Tx power (depends on temperature), described elsewhere
+ * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas)
+ * 3) Receiver sensitivity (to optimize signal detection)
+ *
+ *****************************************************************************/
+
+/**
+ * SENSITIVITY_CMD = 0xa8 (command, has simple generic response)
+ *
+ * This command sets up the Rx signal detector for a sensitivity level that
+ * is high enough to lock onto all signals within the associated network,
+ * but low enough to ignore signals that are below a certain threshold, so as
+ * not to have too many "false alarms". False alarms are signals that the
+ * Rx DSP tries to lock onto, but then discards after determining that they
+ * are noise.
+ *
+ * The optimum number of false alarms is between 5 and 50 per 200 TUs
+ * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e.
+ * time listening, not transmitting). Driver must adjust sensitivity so that
+ * the ratio of actual false alarms to actual Rx time falls within this range.
+ *
+ * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each
+ * received beacon. These provide information to the driver to analyze the
+ * sensitivity. Don't analyze statistics that come in from scanning, or any
+ * other non-associated-network source. Pertinent statistics include:
+ *
+ * From "general" statistics (struct statistics_rx_non_phy):
+ *
+ * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level)
+ * Measure of energy of desired signal. Used for establishing a level
+ * below which the device does not detect signals.
+ *
+ * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB)
+ * Measure of background noise in silent period after beacon.
+ *
+ * channel_load
+ * uSecs of actual Rx time during beacon period (varies according to
+ * how much time was spent transmitting).
+ *
+ * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately:
+ *
+ * false_alarm_cnt
+ * Signal locks abandoned early (before phy-level header).
+ *
+ * plcp_err
+ * Signal locks abandoned late (during phy-level header).
+ *
+ * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from
+ * beacon to beacon, i.e. each value is an accumulation of all errors
+ * before and including the latest beacon. Values will wrap around to 0
+ * after counting up to 2^32 - 1. Driver must differentiate vs.
+ * previous beacon's values to determine # false alarms in the current
+ * beacon period.
+ *
+ * Total number of false alarms = false_alarms + plcp_errs
+ *
+ * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd
+ * (notice that the start points for OFDM are at or close to settings for
+ * maximum sensitivity):
+ *
+ * START / MIN / MAX
+ * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120
+ * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210
+ * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140
+ * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270
+ *
+ * If actual rate of OFDM false alarms (+ plcp_errors) is too high
+ * (greater than 50 for each 204.8 msecs listening), reduce sensitivity
+ * by *adding* 1 to all 4 of the table entries above, up to the max for
+ * each entry. Conversely, if false alarm rate is too low (less than 5
+ * for each 204.8 msecs listening), *subtract* 1 from each entry to
+ * increase sensitivity.
+ *
+ * For CCK sensitivity, keep track of the following:
+ *
+ * 1). 20-beacon history of maximum background noise, indicated by
+ * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the
+ * 3 receivers. For any given beacon, the "silence reference" is
+ * the maximum of last 60 samples (20 beacons * 3 receivers).
+ *
+ * 2). 10-beacon history of strongest signal level, as indicated
+ * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers,
+ * i.e. the strength of the signal through the best receiver at the
+ * moment. These measurements are "upside down", with lower values
+ * for stronger signals, so max energy will be *minimum* value.
+ *
+ * Then for any given beacon, the driver must determine the *weakest*
+ * of the strongest signals; this is the minimum level that needs to be
+ * successfully detected, when using the best receiver at the moment.
+ * "Max cck energy" is the maximum (higher value means lower energy!)
+ * of the last 10 minima. Once this is determined, driver must add
+ * a little margin by adding "6" to it.
+ *
+ * 3). Number of consecutive beacon periods with too few false alarms.
+ * Reset this to 0 at the first beacon period that falls within the
+ * "good" range (5 to 50 false alarms per 204.8 milliseconds rx).
+ *
+ * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd
+ * (notice that the start points for CCK are at maximum sensitivity):
+ *
+ * START / MIN / MAX
+ * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200
+ * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400
+ * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100
+ *
+ * If actual rate of CCK false alarms (+ plcp_errors) is too high
+ * (greater than 50 for each 204.8 msecs listening), method for reducing
+ * sensitivity is:
+ *
+ * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX,
+ * up to max 400.
+ *
+ * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160,
+ * sensitivity has been reduced a significant amount; bring it up to
+ * a moderate 161. Otherwise, *add* 3, up to max 200.
+ *
+ * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160,
+ * sensitivity has been reduced only a moderate or small amount;
+ * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX,
+ * down to min 0. Otherwise (if gain has been significantly reduced),
+ * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value.
+ *
+ * b) Save a snapshot of the "silence reference".
+ *
+ * If actual rate of CCK false alarms (+ plcp_errors) is too low
+ * (less than 5 for each 204.8 msecs listening), method for increasing
+ * sensitivity is used only if:
+ *
+ * 1a) Previous beacon did not have too many false alarms
+ * 1b) AND difference between previous "silence reference" and current
+ * "silence reference" (prev - current) is 2 or more,
+ * OR 2) 100 or more consecutive beacon periods have had rate of
+ * less than 5 false alarms per 204.8 milliseconds rx time.
+ *
+ * Method for increasing sensitivity:
+ *
+ * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX,
+ * down to min 125.
+ *
+ * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX,
+ * down to min 200.
+ *
+ * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100.
+ *
+ * If actual rate of CCK false alarms (+ plcp_errors) is within good range
+ * (between 5 and 50 for each 204.8 msecs listening):
+ *
+ * 1) Save a snapshot of the silence reference.
+ *
+ * 2) If previous beacon had too many CCK false alarms (+ plcp_errors),
+ * give some extra margin to energy threshold by *subtracting* 8
+ * from value in HD_MIN_ENERGY_CCK_DET_INDEX.
+ *
+ * For all cases (too few, too many, good range), make sure that the CCK
+ * detection threshold (energy) is below the energy level for robust
+ * detection over the past 10 beacon periods, the "Max cck energy".
+ * Lower values mean higher energy; this means making sure that the value
+ * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy".
+ *
+ */
+
+/*
+ * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd)
+ */
+#define HD_TABLE_SIZE (11) /* number of entries */
+#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */
+#define HD_MIN_ENERGY_OFDM_DET_INDEX (1)
+#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2)
+#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3)
+#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4)
+#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5)
+#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6)
+#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7)
+#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8)
+#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9)
+#define HD_OFDM_ENERGY_TH_IN_INDEX (10)
+
+/*
+ * Additional table entries in enhance SENSITIVITY_CMD
+ */
+#define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11)
+#define HD_INA_NON_SQUARE_DET_CCK_INDEX (12)
+#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21)
+#define HD_RESERVED (22)
+
+/* number of entries for enhanced tbl */
+#define ENHANCE_HD_TABLE_SIZE (23)
+
+/* number of additional entries for enhanced tbl */
+#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE)
+
+#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0)
+#define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0)
+#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99)
+
+#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1)
+#define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1)
+#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40)
+#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486)
+#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60)
+#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476)
+#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99)
+
+
+/* Control field in struct iwl_sensitivity_cmd */
+#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0)
+#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1)
+
+/**
+ * struct iwl_sensitivity_cmd
+ * @control: (1) updates working table, (0) updates default table
+ * @table: energy threshold values, use HD_* as index into table
+ *
+ * Always use "1" in "control" to update uCode's working table and DSP.
+ */
+struct iwl_sensitivity_cmd {
+ __le16 control; /* always use "1" */
+ __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */
+} __packed;
+
+/*
+ *
+ */
+struct iwl_enhance_sensitivity_cmd {
+ __le16 control; /* always use "1" */
+ __le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */
+} __packed;
+
+
+/**
+ * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response)
+ *
+ * This command sets the relative gains of agn device's 3 radio receiver chains.
+ *
+ * After the first association, driver should accumulate signal and noise
+ * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20
+ * beacons from the associated network (don't collect statistics that come
+ * in from scanning, or any other non-network source).
+ *
+ * DISCONNECTED ANTENNA:
+ *
+ * Driver should determine which antennas are actually connected, by comparing
+ * average beacon signal levels for the 3 Rx chains. Accumulate (add) the
+ * following values over 20 beacons, one accumulator for each of the chains
+ * a/b/c, from struct statistics_rx_non_phy:
+ *
+ * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB)
+ *
+ * Find the strongest signal from among a/b/c. Compare the other two to the
+ * strongest. If any signal is more than 15 dB (times 20, unless you
+ * divide the accumulated values by 20) below the strongest, the driver
+ * considers that antenna to be disconnected, and should not try to use that
+ * antenna/chain for Rx or Tx. If both A and B seem to be disconnected,
+ * driver should declare the stronger one as connected, and attempt to use it
+ * (A and B are the only 2 Tx chains!).
+ *
+ *
+ * RX BALANCE:
+ *
+ * Driver should balance the 3 receivers (but just the ones that are connected
+ * to antennas, see above) for gain, by comparing the average signal levels
+ * detected during the silence after each beacon (background noise).
+ * Accumulate (add) the following values over 20 beacons, one accumulator for
+ * each of the chains a/b/c, from struct statistics_rx_non_phy:
+ *
+ * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB)
+ *
+ * Find the weakest background noise level from among a/b/c. This Rx chain
+ * will be the reference, with 0 gain adjustment. Attenuate other channels by
+ * finding noise difference:
+ *
+ * (accum_noise[i] - accum_noise[reference]) / 30
+ *
+ * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB.
+ * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the
+ * driver should limit the difference results to a range of 0-3 (0-4.5 dB),
+ * and set bit 2 to indicate "reduce gain". The value for the reference
+ * (weakest) chain should be "0".
+ *
+ * diff_gain_[abc] bit fields:
+ * 2: (1) reduce gain, (0) increase gain
+ * 1-0: amount of gain, units of 1.5 dB
+ */
+
+/* Phy calibration command for series */
+enum {
+ IWL_PHY_CALIBRATE_DC_CMD = 8,
+ IWL_PHY_CALIBRATE_LO_CMD = 9,
+ IWL_PHY_CALIBRATE_TX_IQ_CMD = 11,
+ IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15,
+ IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16,
+ IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17,
+ IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD = 18,
+};
+
+/* This enum defines the bitmap of various calibrations to enable in both
+ * init ucode and runtime ucode through CALIBRATION_CFG_CMD.
+ */
+enum iwl_ucode_calib_cfg {
+ IWL_CALIB_CFG_RX_BB_IDX = BIT(0),
+ IWL_CALIB_CFG_DC_IDX = BIT(1),
+ IWL_CALIB_CFG_LO_IDX = BIT(2),
+ IWL_CALIB_CFG_TX_IQ_IDX = BIT(3),
+ IWL_CALIB_CFG_RX_IQ_IDX = BIT(4),
+ IWL_CALIB_CFG_NOISE_IDX = BIT(5),
+ IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6),
+ IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7),
+ IWL_CALIB_CFG_PAPD_IDX = BIT(8),
+ IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9),
+ IWL_CALIB_CFG_TX_PWR_IDX = BIT(10),
+};
+
+#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \
+ IWL_CALIB_CFG_DC_IDX | \
+ IWL_CALIB_CFG_LO_IDX | \
+ IWL_CALIB_CFG_TX_IQ_IDX | \
+ IWL_CALIB_CFG_RX_IQ_IDX | \
+ IWL_CALIB_CFG_CRYSTAL_IDX)
+
+#define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \
+ IWL_CALIB_CFG_DC_IDX | \
+ IWL_CALIB_CFG_LO_IDX | \
+ IWL_CALIB_CFG_TX_IQ_IDX | \
+ IWL_CALIB_CFG_RX_IQ_IDX | \
+ IWL_CALIB_CFG_TEMPERATURE_IDX | \
+ IWL_CALIB_CFG_PAPD_IDX | \
+ IWL_CALIB_CFG_TX_PWR_IDX | \
+ IWL_CALIB_CFG_CRYSTAL_IDX)
+
+#define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0))
+
+struct iwl_calib_cfg_elmnt_s {
+ __le32 is_enable;
+ __le32 start;
+ __le32 send_res;
+ __le32 apply_res;
+ __le32 reserved;
+} __packed;
+
+struct iwl_calib_cfg_status_s {
+ struct iwl_calib_cfg_elmnt_s once;
+ struct iwl_calib_cfg_elmnt_s perd;
+ __le32 flags;
+} __packed;
+
+struct iwl_calib_cfg_cmd {
+ struct iwl_calib_cfg_status_s ucd_calib_cfg;
+ struct iwl_calib_cfg_status_s drv_calib_cfg;
+ __le32 reserved1;
+} __packed;
+
+struct iwl_calib_hdr {
+ u8 op_code;
+ u8 first_group;
+ u8 groups_num;
+ u8 data_valid;
+} __packed;
+
+struct iwl_calib_cmd {
+ struct iwl_calib_hdr hdr;
+ u8 data[0];
+} __packed;
+
+struct iwl_calib_xtal_freq_cmd {
+ struct iwl_calib_hdr hdr;
+ u8 cap_pin1;
+ u8 cap_pin2;
+ u8 pad[2];
+} __packed;
+
+#define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700)
+struct iwl_calib_temperature_offset_cmd {
+ struct iwl_calib_hdr hdr;
+ __le16 radio_sensor_offset;
+ __le16 reserved;
+} __packed;
+
+struct iwl_calib_temperature_offset_v2_cmd {
+ struct iwl_calib_hdr hdr;
+ __le16 radio_sensor_offset_high;
+ __le16 radio_sensor_offset_low;
+ __le16 burntVoltageRef;
+ __le16 reserved;
+} __packed;
+
+/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
+struct iwl_calib_chain_noise_reset_cmd {
+ struct iwl_calib_hdr hdr;
+ u8 data[0];
+};
+
+/* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */
+struct iwl_calib_chain_noise_gain_cmd {
+ struct iwl_calib_hdr hdr;
+ u8 delta_gain_1;
+ u8 delta_gain_2;
+ u8 pad[2];
+} __packed;
+
+/******************************************************************************
+ * (12)
+ * Miscellaneous Commands:
+ *
+ *****************************************************************************/
+
+/*
+ * LEDs Command & Response
+ * REPLY_LEDS_CMD = 0x48 (command, has simple generic response)
+ *
+ * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field),
+ * this command turns it on or off, or sets up a periodic blinking cycle.
+ */
+struct iwl_led_cmd {
+ __le32 interval; /* "interval" in uSec */
+ u8 id; /* 1: Activity, 2: Link, 3: Tech */
+ u8 off; /* # intervals off while blinking;
+ * "0", with >0 "on" value, turns LED on */
+ u8 on; /* # intervals on while blinking;
+ * "0", regardless of "off", turns LED off */
+ u8 reserved;
+} __packed;
+
+/*
+ * station priority table entries
+ * also used as potential "events" value for both
+ * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD
+ */
+
+/*
+ * COEX events entry flag masks
+ * RP - Requested Priority
+ * WP - Win Medium Priority: priority assigned when the contention has been won
+ */
+#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1)
+#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2)
+#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4)
+
+#define COEX_CU_UNASSOC_IDLE_RP 4
+#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4
+#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4
+#define COEX_CU_CALIBRATION_RP 4
+#define COEX_CU_PERIODIC_CALIBRATION_RP 4
+#define COEX_CU_CONNECTION_ESTAB_RP 4
+#define COEX_CU_ASSOCIATED_IDLE_RP 4
+#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4
+#define COEX_CU_ASSOC_AUTO_SCAN_RP 4
+#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4
+#define COEX_CU_RF_ON_RP 6
+#define COEX_CU_RF_OFF_RP 4
+#define COEX_CU_STAND_ALONE_DEBUG_RP 6
+#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4
+#define COEX_CU_RSRVD1_RP 4
+#define COEX_CU_RSRVD2_RP 4
+
+#define COEX_CU_UNASSOC_IDLE_WP 3
+#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3
+#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3
+#define COEX_CU_CALIBRATION_WP 3
+#define COEX_CU_PERIODIC_CALIBRATION_WP 3
+#define COEX_CU_CONNECTION_ESTAB_WP 3
+#define COEX_CU_ASSOCIATED_IDLE_WP 3
+#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3
+#define COEX_CU_ASSOC_AUTO_SCAN_WP 3
+#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3
+#define COEX_CU_RF_ON_WP 3
+#define COEX_CU_RF_OFF_WP 3
+#define COEX_CU_STAND_ALONE_DEBUG_WP 6
+#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3
+#define COEX_CU_RSRVD1_WP 3
+#define COEX_CU_RSRVD2_WP 3
+
+#define COEX_UNASSOC_IDLE_FLAGS 0
+#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_UNASSOC_AUTO_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_CALIBRATION_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_PERIODIC_CALIBRATION_FLAGS 0
+/*
+ * COEX_CONNECTION_ESTAB:
+ * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network.
+ */
+#define COEX_CONNECTION_ESTAB_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
+ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
+#define COEX_ASSOCIATED_IDLE_FLAGS 0
+#define COEX_ASSOC_MANUAL_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_ASSOC_AUTO_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0
+#define COEX_RF_ON_FLAGS 0
+#define COEX_RF_OFF_FLAGS 0
+#define COEX_STAND_ALONE_DEBUG_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_IPAN_ASSOC_LEVEL_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
+ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
+#define COEX_RSRVD1_FLAGS 0
+#define COEX_RSRVD2_FLAGS 0
+/*
+ * COEX_CU_RF_ON is the event wrapping all radio ownership.
+ * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network.
+ */
+#define COEX_CU_RF_ON_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
+ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
+
+
+enum {
+ /* un-association part */
+ COEX_UNASSOC_IDLE = 0,
+ COEX_UNASSOC_MANUAL_SCAN = 1,
+ COEX_UNASSOC_AUTO_SCAN = 2,
+ /* calibration */
+ COEX_CALIBRATION = 3,
+ COEX_PERIODIC_CALIBRATION = 4,
+ /* connection */
+ COEX_CONNECTION_ESTAB = 5,
+ /* association part */
+ COEX_ASSOCIATED_IDLE = 6,
+ COEX_ASSOC_MANUAL_SCAN = 7,
+ COEX_ASSOC_AUTO_SCAN = 8,
+ COEX_ASSOC_ACTIVE_LEVEL = 9,
+ /* RF ON/OFF */
+ COEX_RF_ON = 10,
+ COEX_RF_OFF = 11,
+ COEX_STAND_ALONE_DEBUG = 12,
+ /* IPAN */
+ COEX_IPAN_ASSOC_LEVEL = 13,
+ /* reserved */
+ COEX_RSRVD1 = 14,
+ COEX_RSRVD2 = 15,
+ COEX_NUM_OF_EVENTS = 16
+};
+
+/*
+ * Coexistence WIFI/WIMAX Command
+ * COEX_PRIORITY_TABLE_CMD = 0x5a
+ *
+ */
+struct iwl_wimax_coex_event_entry {
+ u8 request_prio;
+ u8 win_medium_prio;
+ u8 reserved;
+ u8 flags;
+} __packed;
+
+/* COEX flag masks */
+
+/* Station table is valid */
+#define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1)
+/* UnMask wake up src at unassociated sleep */
+#define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4)
+/* UnMask wake up src at associated sleep */
+#define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8)
+/* Enable CoEx feature. */
+#define COEX_FLAGS_COEX_ENABLE_MSK (0x80)
+
+struct iwl_wimax_coex_cmd {
+ u8 flags;
+ u8 reserved[3];
+ struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS];
+} __packed;
+
+/*
+ * Coexistence MEDIUM NOTIFICATION
+ * COEX_MEDIUM_NOTIFICATION = 0x5b
+ *
+ * notification from uCode to host to indicate medium changes
+ *
+ */
+/*
+ * status field
+ * bit 0 - 2: medium status
+ * bit 3: medium change indication
+ * bit 4 - 31: reserved
+ */
+/* status option values, (0 - 2 bits) */
+#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */
+#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */
+#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */
+#define COEX_MEDIUM_MSK (0x7)
+
+/* send notification status (1 bit) */
+#define COEX_MEDIUM_CHANGED (0x8)
+#define COEX_MEDIUM_CHANGED_MSK (0x8)
+#define COEX_MEDIUM_SHIFT (3)
+
+struct iwl_coex_medium_notification {
+ __le32 status;
+ __le32 events;
+} __packed;
+
+/*
+ * Coexistence EVENT Command
+ * COEX_EVENT_CMD = 0x5c
+ *
+ * send from host to uCode for coex event request.
+ */
+/* flags options */
+#define COEX_EVENT_REQUEST_MSK (0x1)
+
+struct iwl_coex_event_cmd {
+ u8 flags;
+ u8 event;
+ __le16 reserved;
+} __packed;
+
+struct iwl_coex_event_resp {
+ __le32 status;
+} __packed;
+
+
+/******************************************************************************
+ * Bluetooth Coexistence commands
+ *
+ *****************************************************************************/
+
+/*
+ * BT Status notification
+ * REPLY_BT_COEX_PROFILE_NOTIF = 0xce
+ */
+enum iwl_bt_coex_profile_traffic_load {
+ IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0,
+ IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1,
+ IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2,
+ IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3,
+/*
+ * There are no more even though below is a u8, the
+ * indication from the BT device only has two bits.
+ */
+};
+
+#define BT_SESSION_ACTIVITY_1_UART_MSG 0x1
+#define BT_SESSION_ACTIVITY_2_UART_MSG 0x2
+
+/* BT UART message - Share Part (BT -> WiFi) */
+#define BT_UART_MSG_FRAME1MSGTYPE_POS (0)
+#define BT_UART_MSG_FRAME1MSGTYPE_MSK \
+ (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS)
+#define BT_UART_MSG_FRAME1SSN_POS (3)
+#define BT_UART_MSG_FRAME1SSN_MSK \
+ (0x3 << BT_UART_MSG_FRAME1SSN_POS)
+#define BT_UART_MSG_FRAME1UPDATEREQ_POS (5)
+#define BT_UART_MSG_FRAME1UPDATEREQ_MSK \
+ (0x1 << BT_UART_MSG_FRAME1UPDATEREQ_POS)
+#define BT_UART_MSG_FRAME1RESERVED_POS (6)
+#define BT_UART_MSG_FRAME1RESERVED_MSK \
+ (0x3 << BT_UART_MSG_FRAME1RESERVED_POS)
+
+#define BT_UART_MSG_FRAME2OPENCONNECTIONS_POS (0)
+#define BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK \
+ (0x3 << BT_UART_MSG_FRAME2OPENCONNECTIONS_POS)
+#define BT_UART_MSG_FRAME2TRAFFICLOAD_POS (2)
+#define BT_UART_MSG_FRAME2TRAFFICLOAD_MSK \
+ (0x3 << BT_UART_MSG_FRAME2TRAFFICLOAD_POS)
+#define BT_UART_MSG_FRAME2CHLSEQN_POS (4)
+#define BT_UART_MSG_FRAME2CHLSEQN_MSK \
+ (0x1 << BT_UART_MSG_FRAME2CHLSEQN_POS)
+#define BT_UART_MSG_FRAME2INBAND_POS (5)
+#define BT_UART_MSG_FRAME2INBAND_MSK \
+ (0x1 << BT_UART_MSG_FRAME2INBAND_POS)
+#define BT_UART_MSG_FRAME2RESERVED_POS (6)
+#define BT_UART_MSG_FRAME2RESERVED_MSK \
+ (0x3 << BT_UART_MSG_FRAME2RESERVED_POS)
+
+#define BT_UART_MSG_FRAME3SCOESCO_POS (0)
+#define BT_UART_MSG_FRAME3SCOESCO_MSK \
+ (0x1 << BT_UART_MSG_FRAME3SCOESCO_POS)
+#define BT_UART_MSG_FRAME3SNIFF_POS (1)
+#define BT_UART_MSG_FRAME3SNIFF_MSK \
+ (0x1 << BT_UART_MSG_FRAME3SNIFF_POS)
+#define BT_UART_MSG_FRAME3A2DP_POS (2)
+#define BT_UART_MSG_FRAME3A2DP_MSK \
+ (0x1 << BT_UART_MSG_FRAME3A2DP_POS)
+#define BT_UART_MSG_FRAME3ACL_POS (3)
+#define BT_UART_MSG_FRAME3ACL_MSK \
+ (0x1 << BT_UART_MSG_FRAME3ACL_POS)
+#define BT_UART_MSG_FRAME3MASTER_POS (4)
+#define BT_UART_MSG_FRAME3MASTER_MSK \
+ (0x1 << BT_UART_MSG_FRAME3MASTER_POS)
+#define BT_UART_MSG_FRAME3OBEX_POS (5)
+#define BT_UART_MSG_FRAME3OBEX_MSK \
+ (0x1 << BT_UART_MSG_FRAME3OBEX_POS)
+#define BT_UART_MSG_FRAME3RESERVED_POS (6)
+#define BT_UART_MSG_FRAME3RESERVED_MSK \
+ (0x3 << BT_UART_MSG_FRAME3RESERVED_POS)
+
+#define BT_UART_MSG_FRAME4IDLEDURATION_POS (0)
+#define BT_UART_MSG_FRAME4IDLEDURATION_MSK \
+ (0x3F << BT_UART_MSG_FRAME4IDLEDURATION_POS)
+#define BT_UART_MSG_FRAME4RESERVED_POS (6)
+#define BT_UART_MSG_FRAME4RESERVED_MSK \
+ (0x3 << BT_UART_MSG_FRAME4RESERVED_POS)
+
+#define BT_UART_MSG_FRAME5TXACTIVITY_POS (0)
+#define BT_UART_MSG_FRAME5TXACTIVITY_MSK \
+ (0x3 << BT_UART_MSG_FRAME5TXACTIVITY_POS)
+#define BT_UART_MSG_FRAME5RXACTIVITY_POS (2)
+#define BT_UART_MSG_FRAME5RXACTIVITY_MSK \
+ (0x3 << BT_UART_MSG_FRAME5RXACTIVITY_POS)
+#define BT_UART_MSG_FRAME5ESCORETRANSMIT_POS (4)
+#define BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK \
+ (0x3 << BT_UART_MSG_FRAME5ESCORETRANSMIT_POS)
+#define BT_UART_MSG_FRAME5RESERVED_POS (6)
+#define BT_UART_MSG_FRAME5RESERVED_MSK \
+ (0x3 << BT_UART_MSG_FRAME5RESERVED_POS)
+
+#define BT_UART_MSG_FRAME6SNIFFINTERVAL_POS (0)
+#define BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK \
+ (0x1F << BT_UART_MSG_FRAME6SNIFFINTERVAL_POS)
+#define BT_UART_MSG_FRAME6DISCOVERABLE_POS (5)
+#define BT_UART_MSG_FRAME6DISCOVERABLE_MSK \
+ (0x1 << BT_UART_MSG_FRAME6DISCOVERABLE_POS)
+#define BT_UART_MSG_FRAME6RESERVED_POS (6)
+#define BT_UART_MSG_FRAME6RESERVED_MSK \
+ (0x3 << BT_UART_MSG_FRAME6RESERVED_POS)
+
+#define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0)
+#define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \
+ (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS)
+#define BT_UART_MSG_FRAME7PAGE_POS (3)
+#define BT_UART_MSG_FRAME7PAGE_MSK \
+ (0x1 << BT_UART_MSG_FRAME7PAGE_POS)
+#define BT_UART_MSG_FRAME7INQUIRY_POS (4)
+#define BT_UART_MSG_FRAME7INQUIRY_MSK \
+ (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS)
+#define BT_UART_MSG_FRAME7CONNECTABLE_POS (5)
+#define BT_UART_MSG_FRAME7CONNECTABLE_MSK \
+ (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS)
+#define BT_UART_MSG_FRAME7RESERVED_POS (6)
+#define BT_UART_MSG_FRAME7RESERVED_MSK \
+ (0x3 << BT_UART_MSG_FRAME7RESERVED_POS)
+
+/* BT Session Activity 2 UART message (BT -> WiFi) */
+#define BT_UART_MSG_2_FRAME1RESERVED1_POS (5)
+#define BT_UART_MSG_2_FRAME1RESERVED1_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME1RESERVED1_POS)
+#define BT_UART_MSG_2_FRAME1RESERVED2_POS (6)
+#define BT_UART_MSG_2_FRAME1RESERVED2_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME1RESERVED2_POS)
+
+#define BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_POS (0)
+#define BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_MSK \
+ (0x3F<<BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_POS)
+#define BT_UART_MSG_2_FRAME2RESERVED_POS (6)
+#define BT_UART_MSG_2_FRAME2RESERVED_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME2RESERVED_POS)
+
+#define BT_UART_MSG_2_FRAME3BRLASTTXPOWER_POS (0)
+#define BT_UART_MSG_2_FRAME3BRLASTTXPOWER_MSK \
+ (0xF<<BT_UART_MSG_2_FRAME3BRLASTTXPOWER_POS)
+#define BT_UART_MSG_2_FRAME3INQPAGESRMODE_POS (4)
+#define BT_UART_MSG_2_FRAME3INQPAGESRMODE_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME3INQPAGESRMODE_POS)
+#define BT_UART_MSG_2_FRAME3LEMASTER_POS (5)
+#define BT_UART_MSG_2_FRAME3LEMASTER_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME3LEMASTER_POS)
+#define BT_UART_MSG_2_FRAME3RESERVED_POS (6)
+#define BT_UART_MSG_2_FRAME3RESERVED_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME3RESERVED_POS)
+
+#define BT_UART_MSG_2_FRAME4LELASTTXPOWER_POS (0)
+#define BT_UART_MSG_2_FRAME4LELASTTXPOWER_MSK \
+ (0xF<<BT_UART_MSG_2_FRAME4LELASTTXPOWER_POS)
+#define BT_UART_MSG_2_FRAME4NUMLECONN_POS (4)
+#define BT_UART_MSG_2_FRAME4NUMLECONN_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME4NUMLECONN_POS)
+#define BT_UART_MSG_2_FRAME4RESERVED_POS (6)
+#define BT_UART_MSG_2_FRAME4RESERVED_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME4RESERVED_POS)
+
+#define BT_UART_MSG_2_FRAME5BTMINRSSI_POS (0)
+#define BT_UART_MSG_2_FRAME5BTMINRSSI_MSK \
+ (0xF<<BT_UART_MSG_2_FRAME5BTMINRSSI_POS)
+#define BT_UART_MSG_2_FRAME5LESCANINITMODE_POS (4)
+#define BT_UART_MSG_2_FRAME5LESCANINITMODE_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME5LESCANINITMODE_POS)
+#define BT_UART_MSG_2_FRAME5LEADVERMODE_POS (5)
+#define BT_UART_MSG_2_FRAME5LEADVERMODE_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME5LEADVERMODE_POS)
+#define BT_UART_MSG_2_FRAME5RESERVED_POS (6)
+#define BT_UART_MSG_2_FRAME5RESERVED_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME5RESERVED_POS)
+
+#define BT_UART_MSG_2_FRAME6LECONNINTERVAL_POS (0)
+#define BT_UART_MSG_2_FRAME6LECONNINTERVAL_MSK \
+ (0x1F<<BT_UART_MSG_2_FRAME6LECONNINTERVAL_POS)
+#define BT_UART_MSG_2_FRAME6RFU_POS (5)
+#define BT_UART_MSG_2_FRAME6RFU_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME6RFU_POS)
+#define BT_UART_MSG_2_FRAME6RESERVED_POS (6)
+#define BT_UART_MSG_2_FRAME6RESERVED_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME6RESERVED_POS)
+
+#define BT_UART_MSG_2_FRAME7LECONNSLAVELAT_POS (0)
+#define BT_UART_MSG_2_FRAME7LECONNSLAVELAT_MSK \
+ (0x7<<BT_UART_MSG_2_FRAME7LECONNSLAVELAT_POS)
+#define BT_UART_MSG_2_FRAME7LEPROFILE1_POS (3)
+#define BT_UART_MSG_2_FRAME7LEPROFILE1_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME7LEPROFILE1_POS)
+#define BT_UART_MSG_2_FRAME7LEPROFILE2_POS (4)
+#define BT_UART_MSG_2_FRAME7LEPROFILE2_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME7LEPROFILE2_POS)
+#define BT_UART_MSG_2_FRAME7LEPROFILEOTHER_POS (5)
+#define BT_UART_MSG_2_FRAME7LEPROFILEOTHER_MSK \
+ (0x1<<BT_UART_MSG_2_FRAME7LEPROFILEOTHER_POS)
+#define BT_UART_MSG_2_FRAME7RESERVED_POS (6)
+#define BT_UART_MSG_2_FRAME7RESERVED_MSK \
+ (0x3<<BT_UART_MSG_2_FRAME7RESERVED_POS)
+
+
+#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
+#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
+
+struct iwl_bt_uart_msg {
+ u8 header;
+ u8 frame1;
+ u8 frame2;
+ u8 frame3;
+ u8 frame4;
+ u8 frame5;
+ u8 frame6;
+ u8 frame7;
+} __packed;
+
+struct iwl_bt_coex_profile_notif {
+ struct iwl_bt_uart_msg last_bt_uart_msg;
+ u8 bt_status; /* 0 - off, 1 - on */
+ u8 bt_traffic_load; /* 0 .. 3? */
+ u8 bt_ci_compliance; /* 0 - not complied, 1 - complied */
+ u8 reserved;
+} __packed;
+
+#define IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS 0
+#define IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_MSK 0x1
+#define IWL_BT_COEX_PRIO_TBL_PRIO_POS 1
+#define IWL_BT_COEX_PRIO_TBL_PRIO_MASK 0x0e
+#define IWL_BT_COEX_PRIO_TBL_RESERVED_POS 4
+#define IWL_BT_COEX_PRIO_TBL_RESERVED_MASK 0xf0
+#define IWL_BT_COEX_PRIO_TBL_PRIO_SHIFT 1
+
+/*
+ * BT Coexistence Priority table
+ * REPLY_BT_COEX_PRIO_TABLE = 0xcc
+ */
+enum bt_coex_prio_table_events {
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3, /* DC calib */
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5,
+ BT_COEX_PRIO_TBL_EVT_DTIM = 6,
+ BT_COEX_PRIO_TBL_EVT_SCAN52 = 7,
+ BT_COEX_PRIO_TBL_EVT_SCAN24 = 8,
+ BT_COEX_PRIO_TBL_EVT_RESERVED0 = 9,
+ BT_COEX_PRIO_TBL_EVT_RESERVED1 = 10,
+ BT_COEX_PRIO_TBL_EVT_RESERVED2 = 11,
+ BT_COEX_PRIO_TBL_EVT_RESERVED3 = 12,
+ BT_COEX_PRIO_TBL_EVT_RESERVED4 = 13,
+ BT_COEX_PRIO_TBL_EVT_RESERVED5 = 14,
+ BT_COEX_PRIO_TBL_EVT_RESERVED6 = 15,
+ /* BT_COEX_PRIO_TBL_EVT_MAX should always be last */
+ BT_COEX_PRIO_TBL_EVT_MAX,
+};
+
+enum bt_coex_prio_table_priorities {
+ BT_COEX_PRIO_TBL_DISABLED = 0,
+ BT_COEX_PRIO_TBL_PRIO_LOW = 1,
+ BT_COEX_PRIO_TBL_PRIO_HIGH = 2,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS = 3,
+ BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4,
+ BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5,
+ BT_COEX_PRIO_TBL_PRIO_RSRVD1 = 6,
+ BT_COEX_PRIO_TBL_PRIO_RSRVD2 = 7,
+ BT_COEX_PRIO_TBL_MAX,
+};
+
+struct iwl_bt_coex_prio_table_cmd {
+ u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
+} __packed;
+
+#define IWL_BT_COEX_ENV_CLOSE 0
+#define IWL_BT_COEX_ENV_OPEN 1
+/*
+ * BT Protection Envelope
+ * REPLY_BT_COEX_PROT_ENV = 0xcd
+ */
+struct iwl_bt_coex_prot_env_cmd {
+ u8 action; /* 0 = closed, 1 = open */
+ u8 type; /* 0 .. 15 */
+ u8 reserved[2];
+} __packed;
+
+/*
+ * REPLY_D3_CONFIG
+ */
+enum iwlagn_d3_wakeup_filters {
+ IWLAGN_D3_WAKEUP_RFKILL = BIT(0),
+ IWLAGN_D3_WAKEUP_SYSASSERT = BIT(1),
+};
+
+struct iwlagn_d3_config_cmd {
+ __le32 min_sleep_time;
+ __le32 wakeup_flags;
+} __packed;
+
+/*
+ * REPLY_WOWLAN_PATTERNS
+ */
+#define IWLAGN_WOWLAN_MIN_PATTERN_LEN 16
+#define IWLAGN_WOWLAN_MAX_PATTERN_LEN 128
+
+struct iwlagn_wowlan_pattern {
+ u8 mask[IWLAGN_WOWLAN_MAX_PATTERN_LEN / 8];
+ u8 pattern[IWLAGN_WOWLAN_MAX_PATTERN_LEN];
+ u8 mask_size;
+ u8 pattern_size;
+ __le16 reserved;
+} __packed;
+
+#define IWLAGN_WOWLAN_MAX_PATTERNS 20
+
+struct iwlagn_wowlan_patterns_cmd {
+ __le32 n_patterns;
+ struct iwlagn_wowlan_pattern patterns[];
+} __packed;
+
+/*
+ * REPLY_WOWLAN_WAKEUP_FILTER
+ */
+enum iwlagn_wowlan_wakeup_filters {
+ IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0),
+ IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1),
+ IWLAGN_WOWLAN_WAKEUP_BEACON_MISS = BIT(2),
+ IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3),
+ IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4),
+ IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5),
+ IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6),
+ IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(7),
+ IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(8),
+};
+
+struct iwlagn_wowlan_wakeup_filter_cmd {
+ __le32 enabled;
+ __le16 non_qos_seq;
+ __le16 reserved;
+ __le16 qos_seq[8];
+};
+
+/*
+ * REPLY_WOWLAN_TSC_RSC_PARAMS
+ */
+#define IWLAGN_NUM_RSC 16
+
+struct tkip_sc {
+ __le16 iv16;
+ __le16 pad;
+ __le32 iv32;
+} __packed;
+
+struct iwlagn_tkip_rsc_tsc {
+ struct tkip_sc unicast_rsc[IWLAGN_NUM_RSC];
+ struct tkip_sc multicast_rsc[IWLAGN_NUM_RSC];
+ struct tkip_sc tsc;
+} __packed;
+
+struct aes_sc {
+ __le64 pn;
+} __packed;
+
+struct iwlagn_aes_rsc_tsc {
+ struct aes_sc unicast_rsc[IWLAGN_NUM_RSC];
+ struct aes_sc multicast_rsc[IWLAGN_NUM_RSC];
+ struct aes_sc tsc;
+} __packed;
+
+union iwlagn_all_tsc_rsc {
+ struct iwlagn_tkip_rsc_tsc tkip;
+ struct iwlagn_aes_rsc_tsc aes;
+};
+
+struct iwlagn_wowlan_rsc_tsc_params_cmd {
+ union iwlagn_all_tsc_rsc all_tsc_rsc;
+} __packed;
+
+/*
+ * REPLY_WOWLAN_TKIP_PARAMS
+ */
+#define IWLAGN_MIC_KEY_SIZE 8
+#define IWLAGN_P1K_SIZE 5
+struct iwlagn_mic_keys {
+ u8 tx[IWLAGN_MIC_KEY_SIZE];
+ u8 rx_unicast[IWLAGN_MIC_KEY_SIZE];
+ u8 rx_mcast[IWLAGN_MIC_KEY_SIZE];
+} __packed;
+
+struct iwlagn_p1k_cache {
+ __le16 p1k[IWLAGN_P1K_SIZE];
+} __packed;
+
+#define IWLAGN_NUM_RX_P1K_CACHE 2
+
+struct iwlagn_wowlan_tkip_params_cmd {
+ struct iwlagn_mic_keys mic_keys;
+ struct iwlagn_p1k_cache tx;
+ struct iwlagn_p1k_cache rx_uni[IWLAGN_NUM_RX_P1K_CACHE];
+ struct iwlagn_p1k_cache rx_multi[IWLAGN_NUM_RX_P1K_CACHE];
+} __packed;
+
+/*
+ * REPLY_WOWLAN_KEK_KCK_MATERIAL
+ */
+
+#define IWLAGN_KCK_MAX_SIZE 32
+#define IWLAGN_KEK_MAX_SIZE 32
+
+struct iwlagn_wowlan_kek_kck_material_cmd {
+ u8 kck[IWLAGN_KCK_MAX_SIZE];
+ u8 kek[IWLAGN_KEK_MAX_SIZE];
+ __le16 kck_len;
+ __le16 kek_len;
+ __le64 replay_ctr;
+} __packed;
+
+#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87
+
+/*
+ * REPLY_WOWLAN_GET_STATUS = 0xe5
+ */
+struct iwlagn_wowlan_status {
+ __le64 replay_ctr;
+ __le32 rekey_status;
+ __le32 wakeup_reason;
+ u8 pattern_number;
+ u8 reserved1;
+ __le16 qos_seq_ctr[8];
+ __le16 non_qos_seq_ctr;
+ __le16 reserved2;
+ union iwlagn_all_tsc_rsc tsc_rsc;
+ __le16 reserved3;
+} __packed;
+
+/*
+ * REPLY_WIPAN_PARAMS = 0xb2 (Commands and Notification)
+ */
+
+/*
+ * Minimum slot time in TU
+ */
+#define IWL_MIN_SLOT_TIME 20
+
+/**
+ * struct iwl_wipan_slot
+ * @width: Time in TU
+ * @type:
+ * 0 - BSS
+ * 1 - PAN
+ */
+struct iwl_wipan_slot {
+ __le16 width;
+ u8 type;
+ u8 reserved;
+} __packed;
+
+#define IWL_WIPAN_PARAMS_FLG_LEAVE_CHANNEL_CTS BIT(1) /* reserved */
+#define IWL_WIPAN_PARAMS_FLG_LEAVE_CHANNEL_QUIET BIT(2) /* reserved */
+#define IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE BIT(3) /* reserved */
+#define IWL_WIPAN_PARAMS_FLG_FILTER_BEACON_NOTIF BIT(4)
+#define IWL_WIPAN_PARAMS_FLG_FULL_SLOTTED_MODE BIT(5)
+
+/**
+ * struct iwl_wipan_params_cmd
+ * @flags:
+ * bit0: reserved
+ * bit1: CP leave channel with CTS
+ * bit2: CP leave channel qith Quiet
+ * bit3: slotted mode
+ * 1 - work in slotted mode
+ * 0 - work in non slotted mode
+ * bit4: filter beacon notification
+ * bit5: full tx slotted mode. if this flag is set,
+ * uCode will perform leaving channel methods in context switch
+ * also when working in same channel mode
+ * @num_slots: 1 - 10
+ */
+struct iwl_wipan_params_cmd {
+ __le16 flags;
+ u8 reserved;
+ u8 num_slots;
+ struct iwl_wipan_slot slots[10];
+} __packed;
+
+/*
+ * REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9
+ *
+ * TODO: Figure out what this is used for,
+ * it can only switch between 2.4 GHz
+ * channels!!
+ */
+
+struct iwl_wipan_p2p_channel_switch_cmd {
+ __le16 channel;
+ __le16 reserved;
+};
+
+/*
+ * REPLY_WIPAN_NOA_NOTIFICATION = 0xbc
+ *
+ * This is used by the device to notify us of the
+ * NoA schedule it determined so we can forward it
+ * to userspace for inclusion in probe responses.
+ *
+ * In beacons, the NoA schedule is simply appended
+ * to the frame we give the device.
+ */
+
+struct iwl_wipan_noa_descriptor {
+ u8 count;
+ __le32 duration;
+ __le32 interval;
+ __le32 starttime;
+} __packed;
+
+struct iwl_wipan_noa_attribute {
+ u8 id;
+ __le16 length;
+ u8 index;
+ u8 ct_window;
+ struct iwl_wipan_noa_descriptor descr0, descr1;
+ u8 reserved;
+} __packed;
+
+struct iwl_wipan_noa_notification {
+ u32 noa_active;
+ struct iwl_wipan_noa_attribute noa_attribute;
+} __packed;
+
+#endif /* __iwl_commands_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
new file mode 100644
index 000000000000..74c51615244e
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
@@ -0,0 +1,2443 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/ieee80211.h>
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "iwl-trans.h"
+#include "iwl-io.h"
+#include "dev.h"
+#include "agn.h"
+
+/* create and remove of files */
+#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
+ if (!debugfs_create_file(#name, mode, parent, priv, \
+ &iwl_dbgfs_##name##_ops)) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_X32(name, parent, ptr) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_u32(#name, mode, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
+/* file operation */
+#define DEBUGFS_READ_FILE_OPS(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = iwl_dbgfs_##name##_write, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = iwl_dbgfs_##name##_write, \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+static ssize_t iwl_dbgfs_sram_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ u32 val = 0;
+ char *buf;
+ ssize_t ret;
+ int i = 0;
+ bool device_format = false;
+ int offset = 0;
+ int len = 0;
+ int pos = 0;
+ int sram;
+ struct iwl_priv *priv = file->private_data;
+ const struct fw_img *img;
+ size_t bufsz;
+
+ if (!iwl_is_ready_rf(priv))
+ return -EAGAIN;
+
+ /* default is to dump the entire data segment */
+ if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) {
+ priv->dbgfs_sram_offset = 0x800000;
+ if (!priv->ucode_loaded)
+ return -EINVAL;
+ img = &priv->fw->img[priv->cur_ucode];
+ priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ }
+ len = priv->dbgfs_sram_len;
+
+ if (len == -4) {
+ device_format = true;
+ len = 4;
+ }
+
+ bufsz = 50 + len * 4;
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n",
+ len);
+ pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
+ priv->dbgfs_sram_offset);
+
+ /* adjust sram address since reads are only on even u32 boundaries */
+ offset = priv->dbgfs_sram_offset & 0x3;
+ sram = priv->dbgfs_sram_offset & ~0x3;
+
+ /* read the first u32 from sram */
+ val = iwl_trans_read_mem32(priv->trans, sram);
+
+ for (; len; len--) {
+ /* put the address at the start of every line */
+ if (i == 0)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%08X: ", sram + offset);
+
+ if (device_format)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%02x", (val >> (8 * (3 - offset))) & 0xff);
+ else
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%02x ", (val >> (8 * offset)) & 0xff);
+
+ /* if all bytes processed, read the next u32 from sram */
+ if (++offset == 4) {
+ sram += 4;
+ offset = 0;
+ val = iwl_trans_read_mem32(priv->trans, sram);
+ }
+
+ /* put in extra spaces and split lines for human readability */
+ if (++i == 16) {
+ i = 0;
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ } else if (!(i & 7)) {
+ pos += scnprintf(buf + pos, bufsz - pos, " ");
+ } else if (!(i & 3)) {
+ pos += scnprintf(buf + pos, bufsz - pos, " ");
+ }
+ }
+ if (i)
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_sram_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[64];
+ int buf_size;
+ u32 offset, len;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
+ priv->dbgfs_sram_offset = offset;
+ priv->dbgfs_sram_len = len;
+ } else if (sscanf(buf, "%x", &offset) == 1) {
+ priv->dbgfs_sram_offset = offset;
+ priv->dbgfs_sram_len = -4;
+ } else {
+ priv->dbgfs_sram_offset = 0;
+ priv->dbgfs_sram_len = 0;
+ }
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN];
+
+ if (!priv->wowlan_sram)
+ return -ENODATA;
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ priv->wowlan_sram,
+ img->sec[IWL_UCODE_SECTION_DATA].len);
+}
+static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ struct iwl_station_entry *station;
+ struct iwl_tid_data *tid_data;
+ char *buf;
+ int i, j, pos = 0;
+ ssize_t ret;
+ /* Add 30 for initial string */
+ const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations);
+
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n",
+ priv->num_stations);
+
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ station = &priv->stations[i];
+ if (!station->used)
+ continue;
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "station %d - addr: %pM, flags: %#x\n",
+ i, station->sta.sta.addr,
+ station->sta.station_flags_msk);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "TID seqno next_rclmd "
+ "rate_n_flags state txq\n");
+
+ for (j = 0; j < IWL_MAX_TID_COUNT; j++) {
+ tid_data = &priv->tid_data[i][j];
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%d: 0x%.4x 0x%.4x 0x%.8x "
+ "%d %.2d",
+ j, tid_data->seq_number,
+ tid_data->next_reclaimed,
+ tid_data->agg.rate_n_flags,
+ tid_data->agg.state,
+ tid_data->agg.txq_id);
+
+ if (tid_data->agg.wait_for_ba)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ " - waitforba");
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ }
+
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_nvm_read(struct file *file,
+ char __user *user_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ ssize_t ret;
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0, ofs = 0, buf_size = 0;
+ const u8 *ptr;
+ char *buf;
+ u16 nvm_ver;
+ size_t eeprom_len = priv->eeprom_blob_size;
+ buf_size = 4 * eeprom_len + 256;
+
+ if (eeprom_len % 16)
+ return -ENODATA;
+
+ ptr = priv->eeprom_blob;
+ if (!ptr)
+ return -ENOMEM;
+
+ /* 4 characters for byte 0xYY */
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ nvm_ver = priv->nvm_data->nvm_version;
+ pos += scnprintf(buf + pos, buf_size - pos,
+ "NVM version: 0x%x\n", nvm_ver);
+ for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) {
+ pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x %16ph\n",
+ ofs, ptr + ofs);
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ struct ieee80211_channel *channels = NULL;
+ const struct ieee80211_supported_band *supp_band = NULL;
+ int pos = 0, i, bufsz = PAGE_SIZE;
+ char *buf;
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ);
+ if (supp_band) {
+ channels = supp_band->channels;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Displaying %d channels in 2.4GHz band 802.11bg):\n",
+ supp_band->n_channels);
+
+ for (i = 0; i < supp_band->n_channels; i++)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%d: %ddBm: BSS%s%s, %s.\n",
+ channels[i].hw_value,
+ channels[i].max_power,
+ channels[i].flags & IEEE80211_CHAN_RADAR ?
+ " (IEEE 802.11h required)" : "",
+ ((channels[i].flags & IEEE80211_CHAN_NO_IR)
+ || (channels[i].flags &
+ IEEE80211_CHAN_RADAR)) ? "" :
+ ", IBSS",
+ channels[i].flags &
+ IEEE80211_CHAN_NO_IR ?
+ "passive only" : "active/passive");
+ }
+ supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ);
+ if (supp_band) {
+ channels = supp_band->channels;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Displaying %d channels in 5.2GHz band (802.11a)\n",
+ supp_band->n_channels);
+
+ for (i = 0; i < supp_band->n_channels; i++)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%d: %ddBm: BSS%s%s, %s.\n",
+ channels[i].hw_value,
+ channels[i].max_power,
+ channels[i].flags & IEEE80211_CHAN_RADAR ?
+ " (IEEE 802.11h required)" : "",
+ ((channels[i].flags & IEEE80211_CHAN_NO_IR)
+ || (channels[i].flags &
+ IEEE80211_CHAN_RADAR)) ? "" :
+ ", IBSS",
+ channels[i].flags &
+ IEEE80211_CHAN_NO_IR ?
+ "passive only" : "active/passive");
+ }
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_status_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ char buf[512];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
+ test_bit(STATUS_RF_KILL_HW, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n",
+ test_bit(STATUS_CT_KILL, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n",
+ test_bit(STATUS_ALIVE, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n",
+ test_bit(STATUS_READY, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n",
+ test_bit(STATUS_EXIT_PENDING, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n",
+ test_bit(STATUS_STATISTICS, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n",
+ test_bit(STATUS_SCANNING, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n",
+ test_bit(STATUS_SCAN_ABORTING, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n",
+ test_bit(STATUS_SCAN_HW, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n",
+ test_bit(STATUS_POWER_PMI, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n",
+ test_bit(STATUS_FW_ERROR, &priv->status));
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+
+ int pos = 0;
+ int cnt = 0;
+ char *buf;
+ int bufsz = 24 * 64; /* 24 items * 64 char per item */
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (cnt = 0; cnt < REPLY_MAX; cnt++) {
+ if (priv->rx_handlers_stats[cnt] > 0)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tRx handler[%36s]:\t\t %u\n",
+ iwl_get_cmd_string(priv->trans, (u32)cnt),
+ priv->rx_handlers_stats[cnt]);
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+
+ char buf[8];
+ int buf_size;
+ u32 reset_flag;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%x", &reset_flag) != 1)
+ return -EFAULT;
+ if (reset_flag == 0)
+ memset(&priv->rx_handlers_stats[0], 0,
+ sizeof(priv->rx_handlers_stats));
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ struct iwl_rxon_context *ctx;
+ int pos = 0, i;
+ char buf[256 * NUM_IWL_RXON_CTX];
+ const size_t bufsz = sizeof(buf);
+
+ for_each_context(priv, ctx) {
+ pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n",
+ ctx->ctxid);
+ for (i = 0; i < AC_NUM; i++) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tcw_min\tcw_max\taifsn\ttxop\n");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "AC[%d]\t%u\t%u\t%u\t%u\n", i,
+ ctx->qos_data.def_qos_parm.ac[i].cw_min,
+ ctx->qos_data.def_qos_parm.ac[i].cw_max,
+ ctx->qos_data.def_qos_parm.ac[i].aifsn,
+ ctx->qos_data.def_qos_parm.ac[i].edca_txop);
+ }
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ }
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ struct iwl_tt_restriction *restriction;
+ char buf[100];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Thermal Throttling Mode: %s\n",
+ tt->advanced_tt ? "Advance" : "Legacy");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Thermal Throttling State: %d\n",
+ tt->state);
+ if (tt->advanced_tt) {
+ restriction = tt->restriction + tt->state;
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Tx mode: %d\n",
+ restriction->tx_stream);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Rx mode: %d\n",
+ restriction->rx_stream);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "HT mode: %d\n",
+ restriction->is_ht);
+ }
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int ht40;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &ht40) != 1)
+ return -EFAULT;
+ if (!iwl_is_any_associated(priv))
+ priv->disable_ht40 = ht40 ? true : false;
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[100];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "11n 40MHz Mode: %s\n",
+ priv->disable_ht40 ? "Disabled" : "Enabled");
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_temperature_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+
+static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int value;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (sscanf(buf, "%d", &value) != 1)
+ return -EINVAL;
+
+ /*
+ * Our users expect 0 to be "CAM", but 0 isn't actually
+ * valid here. However, let's not confuse them and present
+ * IWL_POWER_INDEX_1 as "1", not "0".
+ */
+ if (value == 0)
+ return -EINVAL;
+ else if (value > 0)
+ value -= 1;
+
+ if (value != -1 && (value < 0 || value >= IWL_POWER_NUM))
+ return -EINVAL;
+
+ if (!iwl_is_ready_rf(priv))
+ return -EAGAIN;
+
+ priv->power_data.debug_sleep_level_override = value;
+
+ mutex_lock(&priv->mutex);
+ iwl_power_update_mode(priv, true);
+ mutex_unlock(&priv->mutex);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[10];
+ int pos, value;
+ const size_t bufsz = sizeof(buf);
+
+ /* see the write function */
+ value = priv->power_data.debug_sleep_level_override;
+ if (value >= 0)
+ value += 1;
+
+ pos = scnprintf(buf, bufsz, "%d\n", value);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[200];
+ int pos = 0, i;
+ const size_t bufsz = sizeof(buf);
+ struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "flags: %#.2x\n", le16_to_cpu(cmd->flags));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "RX/TX timeout: %d/%d usec\n",
+ le32_to_cpu(cmd->rx_data_timeout),
+ le32_to_cpu(cmd->tx_data_timeout));
+ for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sleep_interval[%d]: %d\n", i,
+ le32_to_cpu(cmd->sleep_interval[i]));
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(sram);
+DEBUGFS_READ_FILE_OPS(wowlan_sram);
+DEBUGFS_READ_FILE_OPS(nvm);
+DEBUGFS_READ_FILE_OPS(stations);
+DEBUGFS_READ_FILE_OPS(channels);
+DEBUGFS_READ_FILE_OPS(status);
+DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers);
+DEBUGFS_READ_FILE_OPS(qos);
+DEBUGFS_READ_FILE_OPS(thermal_throttling);
+DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40);
+DEBUGFS_READ_FILE_OPS(temperature);
+DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override);
+DEBUGFS_READ_FILE_OPS(current_sleep_command);
+
+static const char *fmt_value = " %-30s %10u\n";
+static const char *fmt_hex = " %-30s 0x%02X\n";
+static const char *fmt_table = " %-30s %10u %10u %10u %10u\n";
+static const char *fmt_header =
+ "%-32s current cumulative delta max\n";
+
+static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz)
+{
+ int p = 0;
+ u32 flag;
+
+ lockdep_assert_held(&priv->statistics.lock);
+
+ flag = le32_to_cpu(priv->statistics.flag);
+
+ p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag);
+ if (flag & UCODE_STATISTICS_CLEAR_MSK)
+ p += scnprintf(buf + p, bufsz - p,
+ "\tStatistics have been cleared\n");
+ p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n",
+ (flag & UCODE_STATISTICS_FREQUENCY_MSK)
+ ? "2.4 GHz" : "5.2 GHz");
+ p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n",
+ (flag & UCODE_STATISTICS_NARROW_BAND_MSK)
+ ? "enabled" : "disabled");
+
+ return p;
+}
+
+static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ char *buf;
+ int bufsz = sizeof(struct statistics_rx_phy) * 40 +
+ sizeof(struct statistics_rx_non_phy) * 40 +
+ sizeof(struct statistics_rx_ht_phy) * 40 + 400;
+ ssize_t ret;
+ struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm;
+ struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck;
+ struct statistics_rx_non_phy *general, *accum_general;
+ struct statistics_rx_non_phy *delta_general, *max_general;
+ struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht;
+
+ if (!iwl_is_alive(priv))
+ return -EAGAIN;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /*
+ * the statistic information display here is based on
+ * the last statistics notification from uCode
+ * might not reflect the current uCode activity
+ */
+ spin_lock_bh(&priv->statistics.lock);
+ ofdm = &priv->statistics.rx_ofdm;
+ cck = &priv->statistics.rx_cck;
+ general = &priv->statistics.rx_non_phy;
+ ht = &priv->statistics.rx_ofdm_ht;
+ accum_ofdm = &priv->accum_stats.rx_ofdm;
+ accum_cck = &priv->accum_stats.rx_cck;
+ accum_general = &priv->accum_stats.rx_non_phy;
+ accum_ht = &priv->accum_stats.rx_ofdm_ht;
+ delta_ofdm = &priv->delta_stats.rx_ofdm;
+ delta_cck = &priv->delta_stats.rx_cck;
+ delta_general = &priv->delta_stats.rx_non_phy;
+ delta_ht = &priv->delta_stats.rx_ofdm_ht;
+ max_ofdm = &priv->max_delta_stats.rx_ofdm;
+ max_cck = &priv->max_delta_stats.rx_cck;
+ max_general = &priv->max_delta_stats.rx_non_phy;
+ max_ht = &priv->max_delta_stats.rx_ofdm_ht;
+
+ pos += iwl_statistics_flag(priv, buf, bufsz);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_header, "Statistics_Rx - OFDM:");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "ina_cnt:",
+ le32_to_cpu(ofdm->ina_cnt),
+ accum_ofdm->ina_cnt,
+ delta_ofdm->ina_cnt, max_ofdm->ina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "fina_cnt:",
+ le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt,
+ delta_ofdm->fina_cnt, max_ofdm->fina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "plcp_err:",
+ le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err,
+ delta_ofdm->plcp_err, max_ofdm->plcp_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "crc32_err:",
+ le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err,
+ delta_ofdm->crc32_err, max_ofdm->crc32_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "overrun_err:",
+ le32_to_cpu(ofdm->overrun_err),
+ accum_ofdm->overrun_err, delta_ofdm->overrun_err,
+ max_ofdm->overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "early_overrun_err:",
+ le32_to_cpu(ofdm->early_overrun_err),
+ accum_ofdm->early_overrun_err,
+ delta_ofdm->early_overrun_err,
+ max_ofdm->early_overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "crc32_good:",
+ le32_to_cpu(ofdm->crc32_good),
+ accum_ofdm->crc32_good, delta_ofdm->crc32_good,
+ max_ofdm->crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "false_alarm_cnt:",
+ le32_to_cpu(ofdm->false_alarm_cnt),
+ accum_ofdm->false_alarm_cnt,
+ delta_ofdm->false_alarm_cnt,
+ max_ofdm->false_alarm_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "fina_sync_err_cnt:",
+ le32_to_cpu(ofdm->fina_sync_err_cnt),
+ accum_ofdm->fina_sync_err_cnt,
+ delta_ofdm->fina_sync_err_cnt,
+ max_ofdm->fina_sync_err_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sfd_timeout:",
+ le32_to_cpu(ofdm->sfd_timeout),
+ accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout,
+ max_ofdm->sfd_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "fina_timeout:",
+ le32_to_cpu(ofdm->fina_timeout),
+ accum_ofdm->fina_timeout, delta_ofdm->fina_timeout,
+ max_ofdm->fina_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "unresponded_rts:",
+ le32_to_cpu(ofdm->unresponded_rts),
+ accum_ofdm->unresponded_rts,
+ delta_ofdm->unresponded_rts,
+ max_ofdm->unresponded_rts);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "rxe_frame_lmt_ovrun:",
+ le32_to_cpu(ofdm->rxe_frame_limit_overrun),
+ accum_ofdm->rxe_frame_limit_overrun,
+ delta_ofdm->rxe_frame_limit_overrun,
+ max_ofdm->rxe_frame_limit_overrun);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sent_ack_cnt:",
+ le32_to_cpu(ofdm->sent_ack_cnt),
+ accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt,
+ max_ofdm->sent_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sent_cts_cnt:",
+ le32_to_cpu(ofdm->sent_cts_cnt),
+ accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt,
+ max_ofdm->sent_cts_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sent_ba_rsp_cnt:",
+ le32_to_cpu(ofdm->sent_ba_rsp_cnt),
+ accum_ofdm->sent_ba_rsp_cnt,
+ delta_ofdm->sent_ba_rsp_cnt,
+ max_ofdm->sent_ba_rsp_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "dsp_self_kill:",
+ le32_to_cpu(ofdm->dsp_self_kill),
+ accum_ofdm->dsp_self_kill,
+ delta_ofdm->dsp_self_kill,
+ max_ofdm->dsp_self_kill);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "mh_format_err:",
+ le32_to_cpu(ofdm->mh_format_err),
+ accum_ofdm->mh_format_err,
+ delta_ofdm->mh_format_err,
+ max_ofdm->mh_format_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "re_acq_main_rssi_sum:",
+ le32_to_cpu(ofdm->re_acq_main_rssi_sum),
+ accum_ofdm->re_acq_main_rssi_sum,
+ delta_ofdm->re_acq_main_rssi_sum,
+ max_ofdm->re_acq_main_rssi_sum);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_header, "Statistics_Rx - CCK:");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "ina_cnt:",
+ le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt,
+ delta_cck->ina_cnt, max_cck->ina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "fina_cnt:",
+ le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt,
+ delta_cck->fina_cnt, max_cck->fina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "plcp_err:",
+ le32_to_cpu(cck->plcp_err), accum_cck->plcp_err,
+ delta_cck->plcp_err, max_cck->plcp_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "crc32_err:",
+ le32_to_cpu(cck->crc32_err), accum_cck->crc32_err,
+ delta_cck->crc32_err, max_cck->crc32_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "overrun_err:",
+ le32_to_cpu(cck->overrun_err),
+ accum_cck->overrun_err, delta_cck->overrun_err,
+ max_cck->overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "early_overrun_err:",
+ le32_to_cpu(cck->early_overrun_err),
+ accum_cck->early_overrun_err,
+ delta_cck->early_overrun_err,
+ max_cck->early_overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "crc32_good:",
+ le32_to_cpu(cck->crc32_good), accum_cck->crc32_good,
+ delta_cck->crc32_good, max_cck->crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "false_alarm_cnt:",
+ le32_to_cpu(cck->false_alarm_cnt),
+ accum_cck->false_alarm_cnt,
+ delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "fina_sync_err_cnt:",
+ le32_to_cpu(cck->fina_sync_err_cnt),
+ accum_cck->fina_sync_err_cnt,
+ delta_cck->fina_sync_err_cnt,
+ max_cck->fina_sync_err_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sfd_timeout:",
+ le32_to_cpu(cck->sfd_timeout),
+ accum_cck->sfd_timeout, delta_cck->sfd_timeout,
+ max_cck->sfd_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "fina_timeout:",
+ le32_to_cpu(cck->fina_timeout),
+ accum_cck->fina_timeout, delta_cck->fina_timeout,
+ max_cck->fina_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "unresponded_rts:",
+ le32_to_cpu(cck->unresponded_rts),
+ accum_cck->unresponded_rts, delta_cck->unresponded_rts,
+ max_cck->unresponded_rts);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "rxe_frame_lmt_ovrun:",
+ le32_to_cpu(cck->rxe_frame_limit_overrun),
+ accum_cck->rxe_frame_limit_overrun,
+ delta_cck->rxe_frame_limit_overrun,
+ max_cck->rxe_frame_limit_overrun);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sent_ack_cnt:",
+ le32_to_cpu(cck->sent_ack_cnt),
+ accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt,
+ max_cck->sent_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sent_cts_cnt:",
+ le32_to_cpu(cck->sent_cts_cnt),
+ accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt,
+ max_cck->sent_cts_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sent_ba_rsp_cnt:",
+ le32_to_cpu(cck->sent_ba_rsp_cnt),
+ accum_cck->sent_ba_rsp_cnt,
+ delta_cck->sent_ba_rsp_cnt,
+ max_cck->sent_ba_rsp_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "dsp_self_kill:",
+ le32_to_cpu(cck->dsp_self_kill),
+ accum_cck->dsp_self_kill, delta_cck->dsp_self_kill,
+ max_cck->dsp_self_kill);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "mh_format_err:",
+ le32_to_cpu(cck->mh_format_err),
+ accum_cck->mh_format_err, delta_cck->mh_format_err,
+ max_cck->mh_format_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "re_acq_main_rssi_sum:",
+ le32_to_cpu(cck->re_acq_main_rssi_sum),
+ accum_cck->re_acq_main_rssi_sum,
+ delta_cck->re_acq_main_rssi_sum,
+ max_cck->re_acq_main_rssi_sum);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_header, "Statistics_Rx - GENERAL:");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "bogus_cts:",
+ le32_to_cpu(general->bogus_cts),
+ accum_general->bogus_cts, delta_general->bogus_cts,
+ max_general->bogus_cts);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "bogus_ack:",
+ le32_to_cpu(general->bogus_ack),
+ accum_general->bogus_ack, delta_general->bogus_ack,
+ max_general->bogus_ack);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "non_bssid_frames:",
+ le32_to_cpu(general->non_bssid_frames),
+ accum_general->non_bssid_frames,
+ delta_general->non_bssid_frames,
+ max_general->non_bssid_frames);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "filtered_frames:",
+ le32_to_cpu(general->filtered_frames),
+ accum_general->filtered_frames,
+ delta_general->filtered_frames,
+ max_general->filtered_frames);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "non_channel_beacons:",
+ le32_to_cpu(general->non_channel_beacons),
+ accum_general->non_channel_beacons,
+ delta_general->non_channel_beacons,
+ max_general->non_channel_beacons);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "channel_beacons:",
+ le32_to_cpu(general->channel_beacons),
+ accum_general->channel_beacons,
+ delta_general->channel_beacons,
+ max_general->channel_beacons);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "num_missed_bcon:",
+ le32_to_cpu(general->num_missed_bcon),
+ accum_general->num_missed_bcon,
+ delta_general->num_missed_bcon,
+ max_general->num_missed_bcon);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "adc_rx_saturation_time:",
+ le32_to_cpu(general->adc_rx_saturation_time),
+ accum_general->adc_rx_saturation_time,
+ delta_general->adc_rx_saturation_time,
+ max_general->adc_rx_saturation_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "ina_detect_search_tm:",
+ le32_to_cpu(general->ina_detection_search_time),
+ accum_general->ina_detection_search_time,
+ delta_general->ina_detection_search_time,
+ max_general->ina_detection_search_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_silence_rssi_a:",
+ le32_to_cpu(general->beacon_silence_rssi_a),
+ accum_general->beacon_silence_rssi_a,
+ delta_general->beacon_silence_rssi_a,
+ max_general->beacon_silence_rssi_a);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_silence_rssi_b:",
+ le32_to_cpu(general->beacon_silence_rssi_b),
+ accum_general->beacon_silence_rssi_b,
+ delta_general->beacon_silence_rssi_b,
+ max_general->beacon_silence_rssi_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_silence_rssi_c:",
+ le32_to_cpu(general->beacon_silence_rssi_c),
+ accum_general->beacon_silence_rssi_c,
+ delta_general->beacon_silence_rssi_c,
+ max_general->beacon_silence_rssi_c);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "interference_data_flag:",
+ le32_to_cpu(general->interference_data_flag),
+ accum_general->interference_data_flag,
+ delta_general->interference_data_flag,
+ max_general->interference_data_flag);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "channel_load:",
+ le32_to_cpu(general->channel_load),
+ accum_general->channel_load,
+ delta_general->channel_load,
+ max_general->channel_load);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "dsp_false_alarms:",
+ le32_to_cpu(general->dsp_false_alarms),
+ accum_general->dsp_false_alarms,
+ delta_general->dsp_false_alarms,
+ max_general->dsp_false_alarms);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_rssi_a:",
+ le32_to_cpu(general->beacon_rssi_a),
+ accum_general->beacon_rssi_a,
+ delta_general->beacon_rssi_a,
+ max_general->beacon_rssi_a);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_rssi_b:",
+ le32_to_cpu(general->beacon_rssi_b),
+ accum_general->beacon_rssi_b,
+ delta_general->beacon_rssi_b,
+ max_general->beacon_rssi_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_rssi_c:",
+ le32_to_cpu(general->beacon_rssi_c),
+ accum_general->beacon_rssi_c,
+ delta_general->beacon_rssi_c,
+ max_general->beacon_rssi_c);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_energy_a:",
+ le32_to_cpu(general->beacon_energy_a),
+ accum_general->beacon_energy_a,
+ delta_general->beacon_energy_a,
+ max_general->beacon_energy_a);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_energy_b:",
+ le32_to_cpu(general->beacon_energy_b),
+ accum_general->beacon_energy_b,
+ delta_general->beacon_energy_b,
+ max_general->beacon_energy_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "beacon_energy_c:",
+ le32_to_cpu(general->beacon_energy_c),
+ accum_general->beacon_energy_c,
+ delta_general->beacon_energy_c,
+ max_general->beacon_energy_c);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_header, "Statistics_Rx - OFDM_HT:");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "plcp_err:",
+ le32_to_cpu(ht->plcp_err), accum_ht->plcp_err,
+ delta_ht->plcp_err, max_ht->plcp_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "overrun_err:",
+ le32_to_cpu(ht->overrun_err), accum_ht->overrun_err,
+ delta_ht->overrun_err, max_ht->overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "early_overrun_err:",
+ le32_to_cpu(ht->early_overrun_err),
+ accum_ht->early_overrun_err,
+ delta_ht->early_overrun_err,
+ max_ht->early_overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "crc32_good:",
+ le32_to_cpu(ht->crc32_good), accum_ht->crc32_good,
+ delta_ht->crc32_good, max_ht->crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "crc32_err:",
+ le32_to_cpu(ht->crc32_err), accum_ht->crc32_err,
+ delta_ht->crc32_err, max_ht->crc32_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "mh_format_err:",
+ le32_to_cpu(ht->mh_format_err),
+ accum_ht->mh_format_err,
+ delta_ht->mh_format_err, max_ht->mh_format_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg_crc32_good:",
+ le32_to_cpu(ht->agg_crc32_good),
+ accum_ht->agg_crc32_good,
+ delta_ht->agg_crc32_good, max_ht->agg_crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg_mpdu_cnt:",
+ le32_to_cpu(ht->agg_mpdu_cnt),
+ accum_ht->agg_mpdu_cnt,
+ delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg_cnt:",
+ le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt,
+ delta_ht->agg_cnt, max_ht->agg_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "unsupport_mcs:",
+ le32_to_cpu(ht->unsupport_mcs),
+ accum_ht->unsupport_mcs,
+ delta_ht->unsupport_mcs, max_ht->unsupport_mcs);
+
+ spin_unlock_bh(&priv->statistics.lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ char *buf;
+ int bufsz = (sizeof(struct statistics_tx) * 48) + 250;
+ ssize_t ret;
+ struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx;
+
+ if (!iwl_is_alive(priv))
+ return -EAGAIN;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* the statistic information display here is based on
+ * the last statistics notification from uCode
+ * might not reflect the current uCode activity
+ */
+ spin_lock_bh(&priv->statistics.lock);
+
+ tx = &priv->statistics.tx;
+ accum_tx = &priv->accum_stats.tx;
+ delta_tx = &priv->delta_stats.tx;
+ max_tx = &priv->max_delta_stats.tx;
+
+ pos += iwl_statistics_flag(priv, buf, bufsz);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_header, "Statistics_Tx:");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "preamble:",
+ le32_to_cpu(tx->preamble_cnt),
+ accum_tx->preamble_cnt,
+ delta_tx->preamble_cnt, max_tx->preamble_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "rx_detected_cnt:",
+ le32_to_cpu(tx->rx_detected_cnt),
+ accum_tx->rx_detected_cnt,
+ delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "bt_prio_defer_cnt:",
+ le32_to_cpu(tx->bt_prio_defer_cnt),
+ accum_tx->bt_prio_defer_cnt,
+ delta_tx->bt_prio_defer_cnt,
+ max_tx->bt_prio_defer_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "bt_prio_kill_cnt:",
+ le32_to_cpu(tx->bt_prio_kill_cnt),
+ accum_tx->bt_prio_kill_cnt,
+ delta_tx->bt_prio_kill_cnt,
+ max_tx->bt_prio_kill_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "few_bytes_cnt:",
+ le32_to_cpu(tx->few_bytes_cnt),
+ accum_tx->few_bytes_cnt,
+ delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "cts_timeout:",
+ le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout,
+ delta_tx->cts_timeout, max_tx->cts_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "ack_timeout:",
+ le32_to_cpu(tx->ack_timeout),
+ accum_tx->ack_timeout,
+ delta_tx->ack_timeout, max_tx->ack_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "expected_ack_cnt:",
+ le32_to_cpu(tx->expected_ack_cnt),
+ accum_tx->expected_ack_cnt,
+ delta_tx->expected_ack_cnt,
+ max_tx->expected_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "actual_ack_cnt:",
+ le32_to_cpu(tx->actual_ack_cnt),
+ accum_tx->actual_ack_cnt,
+ delta_tx->actual_ack_cnt,
+ max_tx->actual_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "dump_msdu_cnt:",
+ le32_to_cpu(tx->dump_msdu_cnt),
+ accum_tx->dump_msdu_cnt,
+ delta_tx->dump_msdu_cnt,
+ max_tx->dump_msdu_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "abort_nxt_frame_mismatch:",
+ le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt),
+ accum_tx->burst_abort_next_frame_mismatch_cnt,
+ delta_tx->burst_abort_next_frame_mismatch_cnt,
+ max_tx->burst_abort_next_frame_mismatch_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "abort_missing_nxt_frame:",
+ le32_to_cpu(tx->burst_abort_missing_next_frame_cnt),
+ accum_tx->burst_abort_missing_next_frame_cnt,
+ delta_tx->burst_abort_missing_next_frame_cnt,
+ max_tx->burst_abort_missing_next_frame_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "cts_timeout_collision:",
+ le32_to_cpu(tx->cts_timeout_collision),
+ accum_tx->cts_timeout_collision,
+ delta_tx->cts_timeout_collision,
+ max_tx->cts_timeout_collision);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "ack_ba_timeout_collision:",
+ le32_to_cpu(tx->ack_or_ba_timeout_collision),
+ accum_tx->ack_or_ba_timeout_collision,
+ delta_tx->ack_or_ba_timeout_collision,
+ max_tx->ack_or_ba_timeout_collision);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg ba_timeout:",
+ le32_to_cpu(tx->agg.ba_timeout),
+ accum_tx->agg.ba_timeout,
+ delta_tx->agg.ba_timeout,
+ max_tx->agg.ba_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg ba_resched_frames:",
+ le32_to_cpu(tx->agg.ba_reschedule_frames),
+ accum_tx->agg.ba_reschedule_frames,
+ delta_tx->agg.ba_reschedule_frames,
+ max_tx->agg.ba_reschedule_frames);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg scd_query_agg_frame:",
+ le32_to_cpu(tx->agg.scd_query_agg_frame_cnt),
+ accum_tx->agg.scd_query_agg_frame_cnt,
+ delta_tx->agg.scd_query_agg_frame_cnt,
+ max_tx->agg.scd_query_agg_frame_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg scd_query_no_agg:",
+ le32_to_cpu(tx->agg.scd_query_no_agg),
+ accum_tx->agg.scd_query_no_agg,
+ delta_tx->agg.scd_query_no_agg,
+ max_tx->agg.scd_query_no_agg);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg scd_query_agg:",
+ le32_to_cpu(tx->agg.scd_query_agg),
+ accum_tx->agg.scd_query_agg,
+ delta_tx->agg.scd_query_agg,
+ max_tx->agg.scd_query_agg);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg scd_query_mismatch:",
+ le32_to_cpu(tx->agg.scd_query_mismatch),
+ accum_tx->agg.scd_query_mismatch,
+ delta_tx->agg.scd_query_mismatch,
+ max_tx->agg.scd_query_mismatch);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg frame_not_ready:",
+ le32_to_cpu(tx->agg.frame_not_ready),
+ accum_tx->agg.frame_not_ready,
+ delta_tx->agg.frame_not_ready,
+ max_tx->agg.frame_not_ready);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg underrun:",
+ le32_to_cpu(tx->agg.underrun),
+ accum_tx->agg.underrun,
+ delta_tx->agg.underrun, max_tx->agg.underrun);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg bt_prio_kill:",
+ le32_to_cpu(tx->agg.bt_prio_kill),
+ accum_tx->agg.bt_prio_kill,
+ delta_tx->agg.bt_prio_kill,
+ max_tx->agg.bt_prio_kill);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "agg rx_ba_rsp_cnt:",
+ le32_to_cpu(tx->agg.rx_ba_rsp_cnt),
+ accum_tx->agg.rx_ba_rsp_cnt,
+ delta_tx->agg.rx_ba_rsp_cnt,
+ max_tx->agg.rx_ba_rsp_cnt);
+
+ if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "tx power: (1/2 dB step)\n");
+ if ((priv->nvm_data->valid_tx_ant & ANT_A) &&
+ tx->tx_power.ant_a)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_hex, "antenna A:",
+ tx->tx_power.ant_a);
+ if ((priv->nvm_data->valid_tx_ant & ANT_B) &&
+ tx->tx_power.ant_b)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_hex, "antenna B:",
+ tx->tx_power.ant_b);
+ if ((priv->nvm_data->valid_tx_ant & ANT_C) &&
+ tx->tx_power.ant_c)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_hex, "antenna C:",
+ tx->tx_power.ant_c);
+ }
+
+ spin_unlock_bh(&priv->statistics.lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ char *buf;
+ int bufsz = sizeof(struct statistics_general) * 10 + 300;
+ ssize_t ret;
+ struct statistics_general_common *general, *accum_general;
+ struct statistics_general_common *delta_general, *max_general;
+ struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg;
+ struct statistics_div *div, *accum_div, *delta_div, *max_div;
+
+ if (!iwl_is_alive(priv))
+ return -EAGAIN;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* the statistic information display here is based on
+ * the last statistics notification from uCode
+ * might not reflect the current uCode activity
+ */
+
+ spin_lock_bh(&priv->statistics.lock);
+
+ general = &priv->statistics.common;
+ dbg = &priv->statistics.common.dbg;
+ div = &priv->statistics.common.div;
+ accum_general = &priv->accum_stats.common;
+ accum_dbg = &priv->accum_stats.common.dbg;
+ accum_div = &priv->accum_stats.common.div;
+ delta_general = &priv->delta_stats.common;
+ max_general = &priv->max_delta_stats.common;
+ delta_dbg = &priv->delta_stats.common.dbg;
+ max_dbg = &priv->max_delta_stats.common.dbg;
+ delta_div = &priv->delta_stats.common.div;
+ max_div = &priv->max_delta_stats.common.div;
+
+ pos += iwl_statistics_flag(priv, buf, bufsz);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_header, "Statistics_General:");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_value, "temperature:",
+ le32_to_cpu(general->temperature));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_value, "temperature_m:",
+ le32_to_cpu(general->temperature_m));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_value, "ttl_timestamp:",
+ le32_to_cpu(general->ttl_timestamp));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "burst_check:",
+ le32_to_cpu(dbg->burst_check),
+ accum_dbg->burst_check,
+ delta_dbg->burst_check, max_dbg->burst_check);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "burst_count:",
+ le32_to_cpu(dbg->burst_count),
+ accum_dbg->burst_count,
+ delta_dbg->burst_count, max_dbg->burst_count);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "wait_for_silence_timeout_count:",
+ le32_to_cpu(dbg->wait_for_silence_timeout_cnt),
+ accum_dbg->wait_for_silence_timeout_cnt,
+ delta_dbg->wait_for_silence_timeout_cnt,
+ max_dbg->wait_for_silence_timeout_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "sleep_time:",
+ le32_to_cpu(general->sleep_time),
+ accum_general->sleep_time,
+ delta_general->sleep_time, max_general->sleep_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "slots_out:",
+ le32_to_cpu(general->slots_out),
+ accum_general->slots_out,
+ delta_general->slots_out, max_general->slots_out);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "slots_idle:",
+ le32_to_cpu(general->slots_idle),
+ accum_general->slots_idle,
+ delta_general->slots_idle, max_general->slots_idle);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "tx_on_a:",
+ le32_to_cpu(div->tx_on_a), accum_div->tx_on_a,
+ delta_div->tx_on_a, max_div->tx_on_a);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "tx_on_b:",
+ le32_to_cpu(div->tx_on_b), accum_div->tx_on_b,
+ delta_div->tx_on_b, max_div->tx_on_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "exec_time:",
+ le32_to_cpu(div->exec_time), accum_div->exec_time,
+ delta_div->exec_time, max_div->exec_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "probe_time:",
+ le32_to_cpu(div->probe_time), accum_div->probe_time,
+ delta_div->probe_time, max_div->probe_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "rx_enable_counter:",
+ le32_to_cpu(general->rx_enable_counter),
+ accum_general->rx_enable_counter,
+ delta_general->rx_enable_counter,
+ max_general->rx_enable_counter);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ fmt_table, "num_of_sos_states:",
+ le32_to_cpu(general->num_of_sos_states),
+ accum_general->num_of_sos_states,
+ delta_general->num_of_sos_states,
+ max_general->num_of_sos_states);
+
+ spin_unlock_bh(&priv->statistics.lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+ int pos = 0;
+ char *buf;
+ int bufsz = (sizeof(struct statistics_bt_activity) * 24) + 200;
+ ssize_t ret;
+ struct statistics_bt_activity *bt, *accum_bt;
+
+ if (!iwl_is_alive(priv))
+ return -EAGAIN;
+
+ if (!priv->bt_enable_flag)
+ return -EINVAL;
+
+ /* make request to uCode to retrieve statistics information */
+ mutex_lock(&priv->mutex);
+ ret = iwl_send_statistics_request(priv, 0, false);
+ mutex_unlock(&priv->mutex);
+
+ if (ret)
+ return -EAGAIN;
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /*
+ * the statistic information display here is based on
+ * the last statistics notification from uCode
+ * might not reflect the current uCode activity
+ */
+
+ spin_lock_bh(&priv->statistics.lock);
+
+ bt = &priv->statistics.bt_activity;
+ accum_bt = &priv->accum_stats.bt_activity;
+
+ pos += iwl_statistics_flag(priv, buf, bufsz);
+ pos += scnprintf(buf + pos, bufsz - pos, "Statistics_BT:\n");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t\t\tcurrent\t\t\taccumulative\n");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "hi_priority_tx_req_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->hi_priority_tx_req_cnt),
+ accum_bt->hi_priority_tx_req_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "hi_priority_tx_denied_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->hi_priority_tx_denied_cnt),
+ accum_bt->hi_priority_tx_denied_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "lo_priority_tx_req_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->lo_priority_tx_req_cnt),
+ accum_bt->lo_priority_tx_req_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->lo_priority_tx_denied_cnt),
+ accum_bt->lo_priority_tx_denied_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "hi_priority_rx_req_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->hi_priority_rx_req_cnt),
+ accum_bt->hi_priority_rx_req_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "hi_priority_rx_denied_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->hi_priority_rx_denied_cnt),
+ accum_bt->hi_priority_rx_denied_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "lo_priority_rx_req_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->lo_priority_rx_req_cnt),
+ accum_bt->lo_priority_rx_req_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(bt->lo_priority_rx_denied_cnt),
+ accum_bt->lo_priority_rx_denied_cnt);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "(rx)num_bt_kills:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(priv->statistics.num_bt_kills),
+ priv->statistics.accum_num_bt_kills);
+
+ spin_unlock_bh(&priv->statistics.lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+ int pos = 0;
+ char *buf;
+ int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) +
+ (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200;
+ ssize_t ret;
+
+ if (!iwl_is_alive(priv))
+ return -EAGAIN;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY),
+ priv->reply_tx_stats.pp_delay);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES),
+ priv->reply_tx_stats.pp_few_bytes);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO),
+ priv->reply_tx_stats.pp_bt_prio);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD),
+ priv->reply_tx_stats.pp_quiet_period);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK),
+ priv->reply_tx_stats.pp_calc_ttak);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+ iwl_get_tx_fail_reason(
+ TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY),
+ priv->reply_tx_stats.int_crossed_retry);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT),
+ priv->reply_tx_stats.short_limit);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT),
+ priv->reply_tx_stats.long_limit);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN),
+ priv->reply_tx_stats.fifo_underrun);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW),
+ priv->reply_tx_stats.drain_flow);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH),
+ priv->reply_tx_stats.rfkill_flush);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE),
+ priv->reply_tx_stats.life_expire);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS),
+ priv->reply_tx_stats.dest_ps);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED),
+ priv->reply_tx_stats.host_abort);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY),
+ priv->reply_tx_stats.pp_delay);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID),
+ priv->reply_tx_stats.sta_invalid);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED),
+ priv->reply_tx_stats.frag_drop);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE),
+ priv->reply_tx_stats.tid_disable);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED),
+ priv->reply_tx_stats.fifo_flush);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+ iwl_get_tx_fail_reason(
+ TX_STATUS_FAIL_INSUFFICIENT_CF_POLL),
+ priv->reply_tx_stats.insuff_cf_poll);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX),
+ priv->reply_tx_stats.fail_hw_drop);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+ iwl_get_tx_fail_reason(
+ TX_STATUS_FAIL_NO_BEACON_ON_RADAR),
+ priv->reply_tx_stats.sta_color_mismatch);
+ pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
+ priv->reply_tx_stats.unknown);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\nStatistics_Agg_TX_Error:\n");
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK),
+ priv->reply_agg_tx_stats.underrun);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK),
+ priv->reply_agg_tx_stats.bt_prio);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK),
+ priv->reply_agg_tx_stats.few_bytes);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK),
+ priv->reply_agg_tx_stats.abort);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(
+ AGG_TX_STATE_LAST_SENT_TTL_MSK),
+ priv->reply_agg_tx_stats.last_sent_ttl);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(
+ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK),
+ priv->reply_agg_tx_stats.last_sent_try);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(
+ AGG_TX_STATE_LAST_SENT_BT_KILL_MSK),
+ priv->reply_agg_tx_stats.last_sent_bt_kill);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK),
+ priv->reply_agg_tx_stats.scd_query);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(
+ AGG_TX_STATE_TEST_BAD_CRC32_MSK),
+ priv->reply_agg_tx_stats.bad_crc32);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK),
+ priv->reply_agg_tx_stats.response);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK),
+ priv->reply_agg_tx_stats.dump_tx);
+ pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+ iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK),
+ priv->reply_agg_tx_stats.delay_tx);
+ pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
+ priv->reply_agg_tx_stats.unknown);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_sensitivity_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ int cnt = 0;
+ char *buf;
+ int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100;
+ ssize_t ret;
+ struct iwl_sensitivity_data *data;
+
+ data = &priv->sensitivity_data;
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n",
+ data->auto_corr_ofdm);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "auto_corr_ofdm_mrc:\t\t %u\n",
+ data->auto_corr_ofdm_mrc);
+ pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n",
+ data->auto_corr_ofdm_x1);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "auto_corr_ofdm_mrc_x1:\t\t %u\n",
+ data->auto_corr_ofdm_mrc_x1);
+ pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n",
+ data->auto_corr_cck);
+ pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n",
+ data->auto_corr_cck_mrc);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "last_bad_plcp_cnt_ofdm:\t\t %u\n",
+ data->last_bad_plcp_cnt_ofdm);
+ pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n",
+ data->last_fa_cnt_ofdm);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "last_bad_plcp_cnt_cck:\t\t %u\n",
+ data->last_bad_plcp_cnt_cck);
+ pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n",
+ data->last_fa_cnt_cck);
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n",
+ data->nrg_curr_state);
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n",
+ data->nrg_prev_state);
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t");
+ for (cnt = 0; cnt < 10; cnt++) {
+ pos += scnprintf(buf + pos, bufsz - pos, " %u",
+ data->nrg_value[cnt]);
+ }
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t");
+ for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) {
+ pos += scnprintf(buf + pos, bufsz - pos, " %u",
+ data->nrg_silence_rssi[cnt]);
+ }
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n",
+ data->nrg_silence_ref);
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n",
+ data->nrg_energy_idx);
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n",
+ data->nrg_silence_idx);
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n",
+ data->nrg_th_cck);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "nrg_auto_corr_silence_diff:\t %u\n",
+ data->nrg_auto_corr_silence_diff);
+ pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n",
+ data->num_in_cck_no_fa);
+ pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n",
+ data->nrg_th_ofdm);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+
+static ssize_t iwl_dbgfs_chain_noise_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ int cnt = 0;
+ char *buf;
+ int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100;
+ ssize_t ret;
+ struct iwl_chain_noise_data *data;
+
+ data = &priv->chain_noise_data;
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n",
+ data->active_chains);
+ pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n",
+ data->chain_noise_a);
+ pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n",
+ data->chain_noise_b);
+ pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n",
+ data->chain_noise_c);
+ pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n",
+ data->chain_signal_a);
+ pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n",
+ data->chain_signal_b);
+ pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n",
+ data->chain_signal_c);
+ pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n",
+ data->beacon_count);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t");
+ for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) {
+ pos += scnprintf(buf + pos, bufsz - pos, " %u",
+ data->disconn_array[cnt]);
+ }
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t");
+ for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) {
+ pos += scnprintf(buf + pos, bufsz - pos, " %u",
+ data->delta_gain_code[cnt]);
+ }
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n",
+ data->radio_write);
+ pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n",
+ data->state);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_power_save_status_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[60];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ u32 pwrsave_status;
+
+ pwrsave_status = iwl_read32(priv->trans, CSR_GP_CNTRL) &
+ CSR_GP_REG_POWER_SAVE_STATUS_MSK;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: ");
+ pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
+ (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" :
+ (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" :
+ (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" :
+ "error");
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int clear;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &clear) != 1)
+ return -EFAULT;
+
+ /* make request to uCode to retrieve statistics information */
+ mutex_lock(&priv->mutex);
+ iwl_send_statistics_request(priv, 0, true);
+ mutex_unlock(&priv->mutex);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ char buf[128];
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n",
+ priv->event_log.ucode_trace ? "On" : "Off");
+ pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n",
+ priv->event_log.non_wraps_count);
+ pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n",
+ priv->event_log.wraps_once_count);
+ pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n",
+ priv->event_log.wraps_more_count);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int trace;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &trace) != 1)
+ return -EFAULT;
+
+ if (trace) {
+ priv->event_log.ucode_trace = true;
+ if (iwl_is_alive(priv)) {
+ /* start collecting data now */
+ mod_timer(&priv->ucode_trace, jiffies);
+ }
+ } else {
+ priv->event_log.ucode_trace = false;
+ del_timer_sync(&priv->ucode_trace);
+ }
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_rxon_flags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int len = 0;
+ char buf[20];
+
+ len = sprintf(buf, "0x%04X\n",
+ le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags));
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int len = 0;
+ char buf[20];
+
+ len = sprintf(buf, "0x%04X\n",
+ le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags));
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ char buf[12];
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%d\n",
+ priv->missed_beacon_threshold);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int missed;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &missed) != 1)
+ return -EINVAL;
+
+ if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN ||
+ missed > IWL_MISSED_BEACON_THRESHOLD_MAX)
+ priv->missed_beacon_threshold =
+ IWL_MISSED_BEACON_THRESHOLD_DEF;
+ else
+ priv->missed_beacon_threshold = missed;
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ char buf[12];
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%u\n",
+ priv->plcp_delta_threshold);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int plcp;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &plcp) != 1)
+ return -EINVAL;
+ if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) ||
+ (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX))
+ priv->plcp_delta_threshold =
+ IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE;
+ else
+ priv->plcp_delta_threshold = plcp;
+ return count;
+}
+
+static ssize_t iwl_dbgfs_rf_reset_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ int pos = 0;
+ char buf[300];
+ const size_t bufsz = sizeof(buf);
+ struct iwl_rf_reset *rf_reset = &priv->rf_reset;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "RF reset statistics\n");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tnumber of reset request: %d\n",
+ rf_reset->reset_request_count);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tnumber of reset request success: %d\n",
+ rf_reset->reset_success_count);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tnumber of reset request reject: %d\n",
+ rf_reset->reset_reject_count);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_rf_reset_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ int ret;
+
+ ret = iwl_force_rf_reset(priv, true);
+ return ret ? ret : count;
+}
+
+static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int flush;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &flush) != 1)
+ return -EINVAL;
+
+ if (iwl_is_rfkill(priv))
+ return -EFAULT;
+
+ iwlagn_dev_txfifo_flush(priv);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+ int pos = 0;
+ char buf[200];
+ const size_t bufsz = sizeof(buf);
+
+ if (!priv->bt_enable_flag) {
+ pos += scnprintf(buf + pos, bufsz - pos, "BT coex disabled\n");
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ }
+ pos += scnprintf(buf + pos, bufsz - pos, "BT enable flag: 0x%x\n",
+ priv->bt_enable_flag);
+ pos += scnprintf(buf + pos, bufsz - pos, "BT in %s mode\n",
+ priv->bt_full_concurrent ? "full concurrency" : "3-wire");
+ pos += scnprintf(buf + pos, bufsz - pos, "BT status: %s, "
+ "last traffic notif: %d\n",
+ priv->bt_status ? "On" : "Off", priv->last_bt_traffic_load);
+ pos += scnprintf(buf + pos, bufsz - pos, "ch_announcement: %d, "
+ "kill_ack_mask: %x, kill_cts_mask: %x\n",
+ priv->bt_ch_announce, priv->kill_ack_mask,
+ priv->kill_cts_mask);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "bluetooth traffic load: ");
+ switch (priv->bt_traffic_load) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ pos += scnprintf(buf + pos, bufsz - pos, "Continuous\n");
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ pos += scnprintf(buf + pos, bufsz - pos, "High\n");
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ pos += scnprintf(buf + pos, bufsz - pos, "Low\n");
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ default:
+ pos += scnprintf(buf + pos, bufsz - pos, "None\n");
+ break;
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_protection_mode_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+
+ int pos = 0;
+ char buf[40];
+ const size_t bufsz = sizeof(buf);
+
+ if (priv->cfg->ht_params)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "use %s for aggregation\n",
+ (priv->hw_params.use_rts_for_aggregation) ?
+ "rts/cts" : "cts-to-self");
+ else
+ pos += scnprintf(buf + pos, bufsz - pos, "N/A");
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_protection_mode_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int rts;
+
+ if (!priv->cfg->ht_params)
+ return -EINVAL;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &rts) != 1)
+ return -EINVAL;
+ if (rts)
+ priv->hw_params.use_rts_for_aggregation = true;
+ else
+ priv->hw_params.use_rts_for_aggregation = false;
+ return count;
+}
+
+static int iwl_cmd_echo_test(struct iwl_priv *priv)
+{
+ int ret;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_ECHO,
+ .len = { 0 },
+ };
+
+ ret = iwl_dvm_send_cmd(priv, &cmd);
+ if (ret)
+ IWL_ERR(priv, "echo testing fail: 0X%x\n", ret);
+ else
+ IWL_DEBUG_INFO(priv, "echo testing pass\n");
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_echo_test_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ iwl_cmd_echo_test(priv);
+ return count;
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+static ssize_t iwl_dbgfs_log_event_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char *buf = NULL;
+ ssize_t ret;
+
+ ret = iwl_dump_nic_event_log(priv, true, &buf);
+ if (ret > 0)
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_log_event_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ u32 event_log_flag;
+ char buf[8];
+ int buf_size;
+
+ /* check that the interface is up */
+ if (!iwl_is_ready(priv))
+ return -EAGAIN;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &event_log_flag) != 1)
+ return -EFAULT;
+ if (event_log_flag == 1)
+ iwl_dump_nic_event_log(priv, true, NULL);
+
+ return count;
+}
+#endif
+
+static ssize_t iwl_dbgfs_calib_disabled_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[120];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Sensitivity calibrations %s\n",
+ (priv->calib_disabled &
+ IWL_SENSITIVITY_CALIB_DISABLED) ?
+ "DISABLED" : "ENABLED");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Chain noise calibrations %s\n",
+ (priv->calib_disabled &
+ IWL_CHAIN_NOISE_CALIB_DISABLED) ?
+ "DISABLED" : "ENABLED");
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Tx power calibrations %s\n",
+ (priv->calib_disabled &
+ IWL_TX_POWER_CALIB_DISABLED) ?
+ "DISABLED" : "ENABLED");
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ u32 calib_disabled;
+ int buf_size;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%x", &calib_disabled) != 1)
+ return -EFAULT;
+
+ priv->calib_disabled = calib_disabled;
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ bool restart_fw = iwlwifi_mod_params.restart_fw;
+ int ret;
+
+ iwlwifi_mod_params.restart_fw = true;
+
+ mutex_lock(&priv->mutex);
+
+ /* take the return value to make compiler happy - it will fail anyway */
+ ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL);
+
+ mutex_unlock(&priv->mutex);
+
+ iwlwifi_mod_params.restart_fw = restart_fw;
+
+ return count;
+}
+
+DEBUGFS_READ_FILE_OPS(ucode_rx_stats);
+DEBUGFS_READ_FILE_OPS(ucode_tx_stats);
+DEBUGFS_READ_FILE_OPS(ucode_general_stats);
+DEBUGFS_READ_FILE_OPS(sensitivity);
+DEBUGFS_READ_FILE_OPS(chain_noise);
+DEBUGFS_READ_FILE_OPS(power_save_status);
+DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics);
+DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing);
+DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon);
+DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta);
+DEBUGFS_READ_WRITE_FILE_OPS(rf_reset);
+DEBUGFS_READ_FILE_OPS(rxon_flags);
+DEBUGFS_READ_FILE_OPS(rxon_filter_flags);
+DEBUGFS_WRITE_FILE_OPS(txfifo_flush);
+DEBUGFS_READ_FILE_OPS(ucode_bt_stats);
+DEBUGFS_READ_FILE_OPS(bt_traffic);
+DEBUGFS_READ_WRITE_FILE_OPS(protection_mode);
+DEBUGFS_READ_FILE_OPS(reply_tx_error);
+DEBUGFS_WRITE_FILE_OPS(echo_test);
+DEBUGFS_WRITE_FILE_OPS(fw_restart);
+#ifdef CONFIG_IWLWIFI_DEBUG
+DEBUGFS_READ_WRITE_FILE_OPS(log_event);
+#endif
+DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled);
+
+/*
+ * Create the debugfs files and directories
+ *
+ */
+int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir)
+{
+ struct dentry *dir_data, *dir_rf, *dir_debug;
+
+ priv->debugfs_dir = dbgfs_dir;
+
+ dir_data = debugfs_create_dir("data", dbgfs_dir);
+ if (!dir_data)
+ goto err;
+ dir_rf = debugfs_create_dir("rf", dbgfs_dir);
+ if (!dir_rf)
+ goto err;
+ dir_debug = debugfs_create_dir("debug", dbgfs_dir);
+ if (!dir_debug)
+ goto err;
+
+ DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR);
+ DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR);
+
+ DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(fw_restart, dir_debug, S_IWUSR);
+#ifdef CONFIG_IWLWIFI_DEBUG
+ DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR);
+#endif
+
+ if (iwl_advanced_bt_coexist(priv))
+ DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR);
+
+ /* Calibrations disabled/enabled status*/
+ DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR);
+
+ /*
+ * Create a symlink with mac80211. This is not very robust, as it does
+ * not remove the symlink created. The implicit assumption is that
+ * when the opmode exits, mac80211 will also exit, and will remove
+ * this symlink as part of its cleanup.
+ */
+ if (priv->mac80211_registered) {
+ char buf[100];
+ struct dentry *mac80211_dir, *dev_dir, *root_dir;
+
+ dev_dir = dbgfs_dir->d_parent;
+ root_dir = dev_dir->d_parent;
+ mac80211_dir = priv->hw->wiphy->debugfsdir;
+
+ snprintf(buf, 100, "../../%s/%s", root_dir->d_name.name,
+ dev_dir->d_name.name);
+
+ if (!debugfs_create_symlink("iwlwifi", mac80211_dir, buf))
+ goto err;
+ }
+
+ return 0;
+
+err:
+ IWL_ERR(priv, "failed to create the dvm debugfs entries\n");
+ return -ENOMEM;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h
new file mode 100644
index 000000000000..1a7ead753eee
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h
@@ -0,0 +1,949 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+/*
+ * Please use this file (dev.h) for driver implementation definitions.
+ * Please use commands.h for uCode API definitions.
+ */
+
+#ifndef __iwl_dev_h__
+#define __iwl_dev_h__
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include "iwl-fw.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-csr.h"
+#include "iwl-debug.h"
+#include "iwl-agn-hw.h"
+#include "iwl-op-mode.h"
+#include "iwl-notif-wait.h"
+#include "iwl-trans.h"
+
+#include "led.h"
+#include "power.h"
+#include "rs.h"
+#include "tt.h"
+
+/* CT-KILL constants */
+#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */
+#define CT_KILL_THRESHOLD 114 /* in Celsius */
+#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */
+
+/* Default noise level to report when noise measurement is not available.
+ * This may be because we're:
+ * 1) Not associated no beacon statistics being sent to driver)
+ * 2) Scanning (noise measurement does not apply to associated channel)
+ * Use default noise value of -127 ... this is below the range of measurable
+ * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user.
+ * Also, -127 works better than 0 when averaging frames with/without
+ * noise info (e.g. averaging might be done in app); measured dBm values are
+ * always negative ... using a negative value as the default keeps all
+ * averages within an s8's (used in some apps) range of negative values. */
+#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127)
+
+/*
+ * RTS threshold here is total size [2347] minus 4 FCS bytes
+ * Per spec:
+ * a value of 0 means RTS on all data/management packets
+ * a value > max MSDU size means no RTS
+ * else RTS for data/management frames where MPDU is larger
+ * than RTS value.
+ */
+#define DEFAULT_RTS_THRESHOLD 2347U
+#define MIN_RTS_THRESHOLD 0U
+#define MAX_RTS_THRESHOLD 2347U
+#define MAX_MSDU_SIZE 2304U
+#define MAX_MPDU_SIZE 2346U
+#define DEFAULT_BEACON_INTERVAL 200U
+#define DEFAULT_SHORT_RETRY_LIMIT 7U
+#define DEFAULT_LONG_RETRY_LIMIT 4U
+
+#define IWL_NUM_SCAN_RATES (2)
+
+
+#define IEEE80211_DATA_LEN 2304
+#define IEEE80211_4ADDR_LEN 30
+#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN)
+#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
+
+#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
+#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
+#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
+
+#define IWL_SUPPORTED_RATES_IE_LEN 8
+
+#define IWL_INVALID_RATE 0xFF
+#define IWL_INVALID_VALUE -1
+
+union iwl_ht_rate_supp {
+ u16 rates;
+ struct {
+ u8 siso_rate;
+ u8 mimo_rate;
+ };
+};
+
+struct iwl_ht_config {
+ bool single_chain_sufficient;
+ enum ieee80211_smps_mode smps; /* current smps mode */
+};
+
+/* QoS structures */
+struct iwl_qos_info {
+ int qos_active;
+ struct iwl_qosparam_cmd def_qos_parm;
+};
+
+/**
+ * enum iwl_agg_state
+ *
+ * The state machine of the BA agreement establishment / tear down.
+ * These states relate to a specific RA / TID.
+ *
+ * @IWL_AGG_OFF: aggregation is not used
+ * @IWL_AGG_STARTING: aggregation are starting (between start and oper)
+ * @IWL_AGG_ON: aggregation session is up
+ * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
+ * HW queue to be empty from packets for this RA /TID.
+ * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
+ * HW queue to be empty from packets for this RA /TID.
+ */
+enum iwl_agg_state {
+ IWL_AGG_OFF = 0,
+ IWL_AGG_STARTING,
+ IWL_AGG_ON,
+ IWL_EMPTYING_HW_QUEUE_ADDBA,
+ IWL_EMPTYING_HW_QUEUE_DELBA,
+};
+
+/**
+ * struct iwl_ht_agg - aggregation state machine
+
+ * This structs holds the states for the BA agreement establishment and tear
+ * down. It also holds the state during the BA session itself. This struct is
+ * duplicated for each RA / TID.
+
+ * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
+ * Tx response (REPLY_TX), and the block ack notification
+ * (REPLY_COMPRESSED_BA).
+ * @state: state of the BA agreement establishment / tear down.
+ * @txq_id: Tx queue used by the BA session
+ * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
+ * 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_ht_agg {
+ u32 rate_n_flags;
+ enum iwl_agg_state state;
+ u16 txq_id;
+ u16 ssn;
+ bool wait_for_ba;
+};
+
+/**
+ * struct iwl_tid_data - one for each RA / TID
+
+ * This structs holds the states for each RA / TID.
+
+ * @seq_number: the next WiFi sequence number to use
+ * @next_reclaimed: the WiFi sequence number of the next packet to be acked.
+ * This is basically (last acked packet++).
+ * @agg: aggregation state machine
+ */
+struct iwl_tid_data {
+ u16 seq_number;
+ u16 next_reclaimed;
+ struct iwl_ht_agg agg;
+};
+
+/*
+ * Structure should be accessed with sta_lock held. When station addition
+ * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only
+ * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock
+ * held.
+ */
+struct iwl_station_entry {
+ struct iwl_addsta_cmd sta;
+ u8 used, ctxid;
+ struct iwl_link_quality_cmd *lq;
+};
+
+/*
+ * iwl_station_priv: Driver's private station information
+ *
+ * When mac80211 creates a station it reserves some space (hw->sta_data_size)
+ * in the structure for use by driver. This structure is places in that
+ * space.
+ */
+struct iwl_station_priv {
+ struct iwl_rxon_context *ctx;
+ struct iwl_lq_sta lq_sta;
+ atomic_t pending_frames;
+ bool client;
+ bool asleep;
+ u8 max_agg_bufsize;
+ u8 sta_id;
+};
+
+/**
+ * struct iwl_vif_priv - driver's private per-interface information
+ *
+ * When mac80211 allocates a virtual interface, it can allocate
+ * space for us to put data into.
+ */
+struct iwl_vif_priv {
+ struct iwl_rxon_context *ctx;
+ u8 ibss_bssid_sta_id;
+};
+
+struct iwl_sensitivity_ranges {
+ u16 min_nrg_cck;
+
+ u16 nrg_th_cck;
+ u16 nrg_th_ofdm;
+
+ u16 auto_corr_min_ofdm;
+ u16 auto_corr_min_ofdm_mrc;
+ u16 auto_corr_min_ofdm_x1;
+ u16 auto_corr_min_ofdm_mrc_x1;
+
+ u16 auto_corr_max_ofdm;
+ u16 auto_corr_max_ofdm_mrc;
+ u16 auto_corr_max_ofdm_x1;
+ u16 auto_corr_max_ofdm_mrc_x1;
+
+ u16 auto_corr_max_cck;
+ u16 auto_corr_max_cck_mrc;
+ u16 auto_corr_min_cck;
+ u16 auto_corr_min_cck_mrc;
+
+ u16 barker_corr_th_min;
+ u16 barker_corr_th_min_mrc;
+ u16 nrg_th_cca;
+};
+
+
+#define KELVIN_TO_CELSIUS(x) ((x)-273)
+#define CELSIUS_TO_KELVIN(x) ((x)+273)
+
+
+/******************************************************************************
+ *
+ * Functions implemented in core module which are forward declared here
+ * for use by iwl-[4-5].c
+ *
+ * NOTE: The implementation of these functions are not hardware specific
+ * which is why they are in the core module files.
+ *
+ * Naming convention --
+ * iwl_ <-- Is part of iwlwifi
+ * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX)
+ *
+ ****************************************************************************/
+void iwl_update_chain_flags(struct iwl_priv *priv);
+extern const u8 iwl_bcast_addr[ETH_ALEN];
+
+#define IWL_OPERATION_MODE_AUTO 0
+#define IWL_OPERATION_MODE_HT_ONLY 1
+#define IWL_OPERATION_MODE_MIXED 2
+#define IWL_OPERATION_MODE_20MHZ 3
+
+#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000
+
+/* Sensitivity and chain noise calibration */
+#define INITIALIZATION_VALUE 0xFFFF
+#define IWL_CAL_NUM_BEACONS 16
+#define MAXIMUM_ALLOWED_PATHLOSS 15
+
+#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3
+
+#define MAX_FA_OFDM 50
+#define MIN_FA_OFDM 5
+#define MAX_FA_CCK 50
+#define MIN_FA_CCK 5
+
+#define AUTO_CORR_STEP_OFDM 1
+
+#define AUTO_CORR_STEP_CCK 3
+#define AUTO_CORR_MAX_TH_CCK 160
+
+#define NRG_DIFF 2
+#define NRG_STEP_CCK 2
+#define NRG_MARGIN 8
+#define MAX_NUMBER_CCK_NO_FA 100
+
+#define AUTO_CORR_CCK_MIN_VAL_DEF (125)
+
+#define CHAIN_A 0
+#define CHAIN_B 1
+#define CHAIN_C 2
+#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4
+#define ALL_BAND_FILTER 0xFF00
+#define IN_BAND_FILTER 0xFF
+#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF
+
+#define NRG_NUM_PREV_STAT_L 20
+#define NUM_RX_CHAINS 3
+
+enum iwlagn_false_alarm_state {
+ IWL_FA_TOO_MANY = 0,
+ IWL_FA_TOO_FEW = 1,
+ IWL_FA_GOOD_RANGE = 2,
+};
+
+enum iwlagn_chain_noise_state {
+ IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */
+ IWL_CHAIN_NOISE_ACCUMULATE,
+ IWL_CHAIN_NOISE_CALIBRATED,
+ IWL_CHAIN_NOISE_DONE,
+};
+
+/* Sensitivity calib data */
+struct iwl_sensitivity_data {
+ u32 auto_corr_ofdm;
+ u32 auto_corr_ofdm_mrc;
+ u32 auto_corr_ofdm_x1;
+ u32 auto_corr_ofdm_mrc_x1;
+ u32 auto_corr_cck;
+ u32 auto_corr_cck_mrc;
+
+ u32 last_bad_plcp_cnt_ofdm;
+ u32 last_fa_cnt_ofdm;
+ u32 last_bad_plcp_cnt_cck;
+ u32 last_fa_cnt_cck;
+
+ u32 nrg_curr_state;
+ u32 nrg_prev_state;
+ u32 nrg_value[10];
+ u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L];
+ u32 nrg_silence_ref;
+ u32 nrg_energy_idx;
+ u32 nrg_silence_idx;
+ u32 nrg_th_cck;
+ s32 nrg_auto_corr_silence_diff;
+ u32 num_in_cck_no_fa;
+ u32 nrg_th_ofdm;
+
+ u16 barker_corr_th_min;
+ u16 barker_corr_th_min_mrc;
+ u16 nrg_th_cca;
+};
+
+/* Chain noise (differential Rx gain) calib data */
+struct iwl_chain_noise_data {
+ u32 active_chains;
+ u32 chain_noise_a;
+ u32 chain_noise_b;
+ u32 chain_noise_c;
+ u32 chain_signal_a;
+ u32 chain_signal_b;
+ u32 chain_signal_c;
+ u16 beacon_count;
+ u8 disconn_array[NUM_RX_CHAINS];
+ u8 delta_gain_code[NUM_RX_CHAINS];
+ u8 radio_write;
+ u8 state;
+};
+
+enum {
+ MEASUREMENT_READY = (1 << 0),
+ MEASUREMENT_ACTIVE = (1 << 1),
+};
+
+/* reply_tx_statistics (for _agn devices) */
+struct reply_tx_error_statistics {
+ u32 pp_delay;
+ u32 pp_few_bytes;
+ u32 pp_bt_prio;
+ u32 pp_quiet_period;
+ u32 pp_calc_ttak;
+ u32 int_crossed_retry;
+ u32 short_limit;
+ u32 long_limit;
+ u32 fifo_underrun;
+ u32 drain_flow;
+ u32 rfkill_flush;
+ u32 life_expire;
+ u32 dest_ps;
+ u32 host_abort;
+ u32 bt_retry;
+ u32 sta_invalid;
+ u32 frag_drop;
+ u32 tid_disable;
+ u32 fifo_flush;
+ u32 insuff_cf_poll;
+ u32 fail_hw_drop;
+ u32 sta_color_mismatch;
+ u32 unknown;
+};
+
+/* reply_agg_tx_statistics (for _agn devices) */
+struct reply_agg_tx_error_statistics {
+ u32 underrun;
+ u32 bt_prio;
+ u32 few_bytes;
+ u32 abort;
+ u32 last_sent_ttl;
+ u32 last_sent_try;
+ u32 last_sent_bt_kill;
+ u32 scd_query;
+ u32 bad_crc32;
+ u32 response;
+ u32 dump_tx;
+ u32 delay_tx;
+ u32 unknown;
+};
+
+/*
+ * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds
+ * to perform continuous uCode event logging operation if enabled
+ */
+#define UCODE_TRACE_PERIOD (10)
+
+/*
+ * iwl_event_log: current uCode event log position
+ *
+ * @ucode_trace: enable/disable ucode continuous trace timer
+ * @num_wraps: how many times the event buffer wraps
+ * @next_entry: the entry just before the next one that uCode would fill
+ * @non_wraps_count: counter for no wrap detected when dump ucode events
+ * @wraps_once_count: counter for wrap once detected when dump ucode events
+ * @wraps_more_count: counter for wrap more than once detected
+ * when dump ucode events
+ */
+struct iwl_event_log {
+ bool ucode_trace;
+ u32 num_wraps;
+ u32 next_entry;
+ int non_wraps_count;
+ int wraps_once_count;
+ int wraps_more_count;
+};
+
+#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
+
+/* BT Antenna Coupling Threshold (dB) */
+#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35)
+
+/* Firmware reload counter and Timestamp */
+#define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */
+#define IWL_MAX_CONTINUE_RELOAD_CNT 4
+
+
+struct iwl_rf_reset {
+ int reset_request_count;
+ int reset_success_count;
+ int reset_reject_count;
+ unsigned long last_reset_jiffies;
+};
+
+enum iwl_rxon_context_id {
+ IWL_RXON_CTX_BSS,
+ IWL_RXON_CTX_PAN,
+
+ NUM_IWL_RXON_CTX
+};
+
+/* extend beacon time format bit shifting */
+/*
+ * for _agn devices
+ * bits 31:22 - extended
+ * bits 21:0 - interval
+ */
+#define IWLAGN_EXT_BEACON_TIME_POS 22
+
+struct iwl_rxon_context {
+ struct ieee80211_vif *vif;
+
+ u8 mcast_queue;
+ u8 ac_to_queue[IEEE80211_NUM_ACS];
+ u8 ac_to_fifo[IEEE80211_NUM_ACS];
+
+ /*
+ * We could use the vif to indicate active, but we
+ * also need it to be active during disabling when
+ * we already removed the vif for type setting.
+ */
+ bool always_active, is_active;
+
+ bool ht_need_multiple_chains;
+
+ enum iwl_rxon_context_id ctxid;
+
+ u32 interface_modes, exclusive_interface_modes;
+ u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype;
+
+ /*
+ * We declare this const so it can only be
+ * changed via explicit cast within the
+ * routines that actually update the physical
+ * hardware.
+ */
+ const struct iwl_rxon_cmd active;
+ struct iwl_rxon_cmd staging;
+
+ struct iwl_rxon_time_cmd timing;
+
+ struct iwl_qos_info qos_data;
+
+ u8 bcast_sta_id, ap_sta_id;
+
+ u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd;
+ u8 qos_cmd;
+ u8 wep_key_cmd;
+
+ struct iwl_wep_key wep_keys[WEP_KEYS_MAX];
+ u8 key_mapping_keys;
+
+ __le32 station_flags;
+
+ int beacon_int;
+
+ struct {
+ bool non_gf_sta_present;
+ u8 protection;
+ bool enabled, is_40mhz;
+ u8 extension_chan_offset;
+ } ht;
+};
+
+enum iwl_scan_type {
+ IWL_SCAN_NORMAL,
+ IWL_SCAN_RADIO_RESET,
+};
+
+/**
+ * struct iwl_hw_params
+ *
+ * Holds the module parameters
+ *
+ * @tx_chains_num: Number of TX chains
+ * @rx_chains_num: Number of RX chains
+ * @ct_kill_threshold: temperature threshold - in hw dependent unit
+ * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit
+ * relevant for 1000, 6000 and up
+ * @struct iwl_sensitivity_ranges: range of sensitivity values
+ * @use_rts_for_aggregation: use rts/cts protection for HT traffic
+ */
+struct iwl_hw_params {
+ u8 tx_chains_num;
+ u8 rx_chains_num;
+ bool use_rts_for_aggregation;
+ u32 ct_kill_threshold;
+ u32 ct_kill_exit_threshold;
+
+ const struct iwl_sensitivity_ranges *sens;
+};
+
+/**
+ * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters
+ * @advanced_bt_coexist: support advanced bt coexist
+ * @bt_init_traffic_load: specify initial bt traffic load
+ * @bt_prio_boost: default bt priority boost value
+ * @agg_time_limit: maximum number of uSec in aggregation
+ * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
+ */
+struct iwl_dvm_bt_params {
+ bool advanced_bt_coexist;
+ u8 bt_init_traffic_load;
+ u32 bt_prio_boost;
+ u16 agg_time_limit;
+ bool bt_sco_disable;
+ bool bt_session_2;
+};
+
+/**
+ * struct iwl_dvm_cfg - DVM firmware specific device configuration
+ * @set_hw_params: set hardware parameters
+ * @set_channel_switch: send channel switch command
+ * @nic_config: apply device specific configuration
+ * @temperature: read temperature
+ * @adv_thermal_throttle: support advance thermal throttle
+ * @support_ct_kill_exit: support ct kill exit condition
+ * @plcp_delta_threshold: plcp error rate threshold used to trigger
+ * radio tuning when there is a high receiving plcp error rate
+ * @chain_noise_scale: default chain noise scale used for gain computation
+ * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
+ * @no_idle_support: do not support idle mode
+ * @bt_params: pointer to BT parameters
+ * @need_temp_offset_calib: need to perform temperature offset calibration
+ * @no_xtal_calib: some devices do not need crystal calibration data,
+ * don't send it to those
+ * @temp_offset_v2: support v2 of temperature offset calibration
+ * @adv_pm: advanced power management
+ */
+struct iwl_dvm_cfg {
+ void (*set_hw_params)(struct iwl_priv *priv);
+ int (*set_channel_switch)(struct iwl_priv *priv,
+ struct ieee80211_channel_switch *ch_switch);
+ void (*nic_config)(struct iwl_priv *priv);
+ void (*temperature)(struct iwl_priv *priv);
+
+ const struct iwl_dvm_bt_params *bt_params;
+ s32 chain_noise_scale;
+ u8 plcp_delta_threshold;
+ bool adv_thermal_throttle;
+ bool support_ct_kill_exit;
+ bool hd_v2;
+ bool no_idle_support;
+ bool need_temp_offset_calib;
+ bool no_xtal_calib;
+ bool temp_offset_v2;
+ bool adv_pm;
+};
+
+struct iwl_wipan_noa_data {
+ struct rcu_head rcu_head;
+ u32 length;
+ u8 data[];
+};
+
+/* Calibration disabling bit mask */
+enum {
+ IWL_CALIB_ENABLE_ALL = 0,
+
+ IWL_SENSITIVITY_CALIB_DISABLED = BIT(0),
+ IWL_CHAIN_NOISE_CALIB_DISABLED = BIT(1),
+ IWL_TX_POWER_CALIB_DISABLED = BIT(2),
+
+ IWL_CALIB_DISABLE_ALL = 0xFFFFFFFF,
+};
+
+#define IWL_OP_MODE_GET_DVM(_iwl_op_mode) \
+ ((struct iwl_priv *) ((_iwl_op_mode)->op_mode_specific))
+
+#define IWL_MAC80211_GET_DVM(_hw) \
+ ((struct iwl_priv *) ((struct iwl_op_mode *) \
+ (_hw)->priv)->op_mode_specific)
+
+struct iwl_priv {
+
+ struct iwl_trans *trans;
+ struct device *dev; /* for debug prints only */
+ const struct iwl_cfg *cfg;
+ const struct iwl_fw *fw;
+ const struct iwl_dvm_cfg *lib;
+ unsigned long status;
+
+ spinlock_t sta_lock;
+ struct mutex mutex;
+
+ unsigned long transport_queue_stop;
+ bool passive_no_rx;
+#define IWL_INVALID_MAC80211_QUEUE 0xff
+ u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
+ atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
+
+ unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
+
+ /* ieee device used by generic ieee processing code */
+ struct ieee80211_hw *hw;
+
+ struct napi_struct *napi;
+
+ struct list_head calib_results;
+
+ struct workqueue_struct *workqueue;
+
+ struct iwl_hw_params hw_params;
+
+ enum ieee80211_band band;
+ u8 valid_contexts;
+
+ void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb);
+
+ struct iwl_notif_wait_data notif_wait;
+
+ /* spectrum measurement report caching */
+ struct iwl_spectrum_notification measure_report;
+ u8 measurement_status;
+
+ /* ucode beacon time */
+ u32 ucode_beacon_time;
+ int missed_beacon_threshold;
+
+ /* track IBSS manager (last beacon) status */
+ u32 ibss_manager;
+
+ /* jiffies when last recovery from statistics was performed */
+ unsigned long rx_statistics_jiffies;
+
+ /*counters */
+ u32 rx_handlers_stats[REPLY_MAX];
+
+ /* rf reset */
+ struct iwl_rf_reset rf_reset;
+
+ /* firmware reload counter and timestamp */
+ unsigned long reload_jiffies;
+ int reload_count;
+ bool ucode_loaded;
+
+ u8 plcp_delta_threshold;
+
+ /* thermal calibration */
+ s32 temperature; /* Celsius */
+ s32 last_temperature;
+
+ struct iwl_wipan_noa_data __rcu *noa_data;
+
+ /* Scan related variables */
+ unsigned long scan_start;
+ unsigned long scan_start_tsf;
+ void *scan_cmd;
+ enum ieee80211_band scan_band;
+ struct cfg80211_scan_request *scan_request;
+ struct ieee80211_vif *scan_vif;
+ enum iwl_scan_type scan_type;
+ u8 scan_tx_ant[IEEE80211_NUM_BANDS];
+ u8 mgmt_tx_ant;
+
+ /* max number of station keys */
+ u8 sta_key_max_num;
+
+ bool new_scan_threshold_behaviour;
+
+ bool wowlan;
+
+ /* EEPROM MAC addresses */
+ struct mac_address addresses[2];
+
+ struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX];
+
+ __le16 switch_channel;
+
+ u8 start_calib;
+ struct iwl_sensitivity_data sensitivity_data;
+ struct iwl_chain_noise_data chain_noise_data;
+ __le16 sensitivity_tbl[HD_TABLE_SIZE];
+ __le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES];
+
+ struct iwl_ht_config current_ht_config;
+
+ /* Rate scaling data */
+ u8 retry_rate;
+
+ int activity_timer_active;
+
+ struct iwl_power_mgr power_data;
+ struct iwl_tt_mgmt thermal_throttle;
+
+ /* station table variables */
+ int num_stations;
+ struct iwl_station_entry stations[IWLAGN_STATION_COUNT];
+ unsigned long ucode_key_table;
+ struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT];
+ atomic_t num_aux_in_flight;
+
+ u8 mac80211_registered;
+
+ /* Indication if ieee80211_ops->open has been called */
+ u8 is_open;
+
+ enum nl80211_iftype iw_mode;
+
+ /* Last Rx'd beacon timestamp */
+ u64 timestamp;
+
+ struct {
+ __le32 flag;
+ struct statistics_general_common common;
+ struct statistics_rx_non_phy rx_non_phy;
+ struct statistics_rx_phy rx_ofdm;
+ struct statistics_rx_ht_phy rx_ofdm_ht;
+ struct statistics_rx_phy rx_cck;
+ struct statistics_tx tx;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct statistics_bt_activity bt_activity;
+ __le32 num_bt_kills, accum_num_bt_kills;
+#endif
+ spinlock_t lock;
+ } statistics;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct {
+ struct statistics_general_common common;
+ struct statistics_rx_non_phy rx_non_phy;
+ struct statistics_rx_phy rx_ofdm;
+ struct statistics_rx_ht_phy rx_ofdm_ht;
+ struct statistics_rx_phy rx_cck;
+ struct statistics_tx tx;
+ struct statistics_bt_activity bt_activity;
+ } accum_stats, delta_stats, max_delta_stats;
+#endif
+
+ /*
+ * reporting the number of tids has AGG on. 0 means
+ * no AGGREGATION
+ */
+ u8 agg_tids_count;
+
+ struct iwl_rx_phy_res last_phy_res;
+ u32 ampdu_ref;
+ bool last_phy_res_valid;
+
+ /*
+ * chain noise reset and gain commands are the
+ * two extra calibration commands follows the standard
+ * phy calibration commands
+ */
+ u8 phy_calib_chain_noise_reset_cmd;
+ u8 phy_calib_chain_noise_gain_cmd;
+
+ /* counts reply_tx error */
+ struct reply_tx_error_statistics reply_tx_stats;
+ struct reply_agg_tx_error_statistics reply_agg_tx_stats;
+
+ /* bt coex */
+ u8 bt_enable_flag;
+ u8 bt_status;
+ u8 bt_traffic_load, last_bt_traffic_load;
+ bool bt_ch_announce;
+ bool bt_full_concurrent;
+ bool bt_ant_couple_ok;
+ __le32 kill_ack_mask;
+ __le32 kill_cts_mask;
+ __le16 bt_valid;
+ bool reduced_txpower;
+ u16 bt_on_thresh;
+ u16 bt_duration;
+ u16 dynamic_frag_thresh;
+ u8 bt_ci_compliance;
+ struct work_struct bt_traffic_change_work;
+ bool bt_enable_pspoll;
+ struct iwl_rxon_context *cur_rssi_ctx;
+ bool bt_is_sco;
+
+ struct work_struct restart;
+ struct work_struct scan_completed;
+ struct work_struct abort_scan;
+
+ struct work_struct beacon_update;
+ struct iwl_rxon_context *beacon_ctx;
+ struct sk_buff *beacon_skb;
+ void *beacon_cmd;
+
+ struct work_struct tt_work;
+ struct work_struct ct_enter;
+ struct work_struct ct_exit;
+ struct work_struct start_internal_scan;
+ struct work_struct tx_flush;
+ struct work_struct bt_full_concurrency;
+ struct work_struct bt_runtime_config;
+
+ struct delayed_work scan_check;
+
+ /* TX Power settings */
+ s8 tx_power_user_lmt;
+ s8 tx_power_next;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ /* debugfs */
+ struct dentry *debugfs_dir;
+ u32 dbgfs_sram_offset, dbgfs_sram_len;
+ bool disable_ht40;
+ void *wowlan_sram;
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+
+ struct iwl_nvm_data *nvm_data;
+ /* eeprom blob for debugfs */
+ u8 *eeprom_blob;
+ size_t eeprom_blob_size;
+
+ struct work_struct txpower_work;
+ u32 calib_disabled;
+ struct work_struct run_time_calib_work;
+ struct timer_list statistics_periodic;
+ struct timer_list ucode_trace;
+
+ struct iwl_event_log event_log;
+
+#ifdef CONFIG_IWLWIFI_LEDS
+ struct led_classdev led;
+ unsigned long blink_on, blink_off;
+ bool led_registered;
+#endif
+
+ /* WoWLAN GTK rekey data */
+ u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
+ __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 {
+ u32 error_event_table;
+ u32 log_event_table;
+ } device_pointers;
+
+ /* indicator of loaded ucode image */
+ enum iwl_ucode_type cur_ucode;
+}; /*iwl_priv */
+
+static inline struct iwl_rxon_context *
+iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif)
+{
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+
+ return vif_priv->ctx;
+}
+
+#define for_each_context(priv, ctx) \
+ for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \
+ ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \
+ if (priv->valid_contexts & BIT(ctx->ctxid))
+
+static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx)
+{
+ return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0;
+}
+
+static inline int iwl_is_associated(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctxid)
+{
+ return iwl_is_associated_ctx(&priv->contexts[ctxid]);
+}
+
+static inline int iwl_is_any_associated(struct iwl_priv *priv)
+{
+ struct iwl_rxon_context *ctx;
+ for_each_context(priv, ctx)
+ if (iwl_is_associated_ctx(ctx))
+ return true;
+ return false;
+}
+
+#endif /* __iwl_dev_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c
new file mode 100644
index 000000000000..cc13c04063a5
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c
@@ -0,0 +1,690 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+/*
+ * DVM device-specific data & functions
+ */
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "iwl-eeprom-parse.h"
+
+#include "agn.h"
+#include "dev.h"
+#include "commands.h"
+
+
+/*
+ * 1000 series
+ * ===========
+ */
+
+/*
+ * For 1000, use advance thermal throttling critical temperature threshold,
+ * but legacy thermal management implementation for now.
+ * This is for the reason of 1000 uCode using advance thermal throttling API
+ * but not implement ct_kill_exit based on ct_kill exit temperature
+ * so the thermal throttling will still based on legacy thermal throttling
+ * management.
+ * The code here need to be modified once 1000 uCode has the advanced thermal
+ * throttling algorithm in place
+ */
+static void iwl1000_set_ct_threshold(struct iwl_priv *priv)
+{
+ /* want Celsius */
+ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
+ priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
+}
+
+/* NIC configuration for 1000 series */
+static void iwl1000_nic_config(struct iwl_priv *priv)
+{
+ /* Setting digital SVR for 1000 card to 1.32V */
+ /* locking is acquired in iwl_set_bits_mask_prph() function */
+ iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG,
+ APMG_SVR_DIGITAL_VOLTAGE_1_32,
+ ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK);
+}
+
+/**
+ * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time
+ * @priv -- pointer to iwl_priv data structure
+ * @tsf_bits -- number of bits need to shift for masking)
+ */
+static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv,
+ u16 tsf_bits)
+{
+ return (1 << tsf_bits) - 1;
+}
+
+/**
+ * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time
+ * @priv -- pointer to iwl_priv data structure
+ * @tsf_bits -- number of bits need to shift for masking)
+ */
+static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv,
+ u16 tsf_bits)
+{
+ return ((1 << (32 - tsf_bits)) - 1) << tsf_bits;
+}
+
+/*
+ * extended beacon time format
+ * time in usec will be changed into a 32-bit value in extended:internal format
+ * the extended part is the beacon counts
+ * the internal part is the time in usec within one beacon interval
+ */
+static u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec,
+ u32 beacon_interval)
+{
+ u32 quot;
+ u32 rem;
+ u32 interval = beacon_interval * TIME_UNIT;
+
+ if (!interval || !usec)
+ return 0;
+
+ quot = (usec / interval) &
+ (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >>
+ IWLAGN_EXT_BEACON_TIME_POS);
+ rem = (usec % interval) & iwl_beacon_time_mask_low(priv,
+ IWLAGN_EXT_BEACON_TIME_POS);
+
+ return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem;
+}
+
+/* base is usually what we get from ucode with each received frame,
+ * the same as HW timer counter counting down
+ */
+static __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
+ u32 addon, u32 beacon_interval)
+{
+ u32 base_low = base & iwl_beacon_time_mask_low(priv,
+ IWLAGN_EXT_BEACON_TIME_POS);
+ u32 addon_low = addon & iwl_beacon_time_mask_low(priv,
+ IWLAGN_EXT_BEACON_TIME_POS);
+ u32 interval = beacon_interval * TIME_UNIT;
+ u32 res = (base & iwl_beacon_time_mask_high(priv,
+ IWLAGN_EXT_BEACON_TIME_POS)) +
+ (addon & iwl_beacon_time_mask_high(priv,
+ IWLAGN_EXT_BEACON_TIME_POS));
+
+ if (base_low > addon_low)
+ res += base_low - addon_low;
+ else if (base_low < addon_low) {
+ res += interval + base_low - addon_low;
+ res += (1 << IWLAGN_EXT_BEACON_TIME_POS);
+ } else
+ res += (1 << IWLAGN_EXT_BEACON_TIME_POS);
+
+ return cpu_to_le32(res);
+}
+
+static const struct iwl_sensitivity_ranges iwl1000_sensitivity = {
+ .min_nrg_cck = 95,
+ .auto_corr_min_ofdm = 90,
+ .auto_corr_min_ofdm_mrc = 170,
+ .auto_corr_min_ofdm_x1 = 120,
+ .auto_corr_min_ofdm_mrc_x1 = 240,
+
+ .auto_corr_max_ofdm = 120,
+ .auto_corr_max_ofdm_mrc = 210,
+ .auto_corr_max_ofdm_x1 = 155,
+ .auto_corr_max_ofdm_mrc_x1 = 290,
+
+ .auto_corr_min_cck = 125,
+ .auto_corr_max_cck = 200,
+ .auto_corr_min_cck_mrc = 170,
+ .auto_corr_max_cck_mrc = 400,
+ .nrg_th_cck = 95,
+ .nrg_th_ofdm = 95,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
+};
+
+static void iwl1000_hw_set_hw_params(struct iwl_priv *priv)
+{
+ iwl1000_set_ct_threshold(priv);
+
+ /* Set initial sensitivity parameters */
+ priv->hw_params.sens = &iwl1000_sensitivity;
+}
+
+const struct iwl_dvm_cfg iwl_dvm_1000_cfg = {
+ .set_hw_params = iwl1000_hw_set_hw_params,
+ .nic_config = iwl1000_nic_config,
+ .temperature = iwlagn_temperature,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+};
+
+
+/*
+ * 2000 series
+ * ===========
+ */
+
+static void iwl2000_set_ct_threshold(struct iwl_priv *priv)
+{
+ /* want Celsius */
+ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
+ priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
+}
+
+/* NIC configuration for 2000 series */
+static void iwl2000_nic_config(struct iwl_priv *priv)
+{
+ iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
+ CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER);
+}
+
+static const struct iwl_sensitivity_ranges iwl2000_sensitivity = {
+ .min_nrg_cck = 97,
+ .auto_corr_min_ofdm = 80,
+ .auto_corr_min_ofdm_mrc = 128,
+ .auto_corr_min_ofdm_x1 = 105,
+ .auto_corr_min_ofdm_mrc_x1 = 192,
+
+ .auto_corr_max_ofdm = 145,
+ .auto_corr_max_ofdm_mrc = 232,
+ .auto_corr_max_ofdm_x1 = 110,
+ .auto_corr_max_ofdm_mrc_x1 = 232,
+
+ .auto_corr_min_cck = 125,
+ .auto_corr_max_cck = 175,
+ .auto_corr_min_cck_mrc = 160,
+ .auto_corr_max_cck_mrc = 310,
+ .nrg_th_cck = 97,
+ .nrg_th_ofdm = 100,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
+};
+
+static void iwl2000_hw_set_hw_params(struct iwl_priv *priv)
+{
+ iwl2000_set_ct_threshold(priv);
+
+ /* Set initial sensitivity parameters */
+ priv->hw_params.sens = &iwl2000_sensitivity;
+}
+
+const struct iwl_dvm_cfg iwl_dvm_2000_cfg = {
+ .set_hw_params = iwl2000_hw_set_hw_params,
+ .nic_config = iwl2000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .hd_v2 = true,
+ .need_temp_offset_calib = true,
+ .temp_offset_v2 = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_105_cfg = {
+ .set_hw_params = iwl2000_hw_set_hw_params,
+ .nic_config = iwl2000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .hd_v2 = true,
+ .need_temp_offset_calib = true,
+ .temp_offset_v2 = true,
+ .adv_pm = true,
+};
+
+static const struct iwl_dvm_bt_params iwl2030_bt_params = {
+ /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
+ .advanced_bt_coexist = true,
+ .agg_time_limit = BT_AGG_THRESHOLD_DEF,
+ .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
+ .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
+ .bt_sco_disable = true,
+ .bt_session_2 = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_2030_cfg = {
+ .set_hw_params = iwl2000_hw_set_hw_params,
+ .nic_config = iwl2000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .hd_v2 = true,
+ .bt_params = &iwl2030_bt_params,
+ .need_temp_offset_calib = true,
+ .temp_offset_v2 = true,
+ .adv_pm = true,
+};
+
+/*
+ * 5000 series
+ * ===========
+ */
+
+/* NIC configuration for 5000 series */
+static const struct iwl_sensitivity_ranges iwl5000_sensitivity = {
+ .min_nrg_cck = 100,
+ .auto_corr_min_ofdm = 90,
+ .auto_corr_min_ofdm_mrc = 170,
+ .auto_corr_min_ofdm_x1 = 105,
+ .auto_corr_min_ofdm_mrc_x1 = 220,
+
+ .auto_corr_max_ofdm = 120,
+ .auto_corr_max_ofdm_mrc = 210,
+ .auto_corr_max_ofdm_x1 = 120,
+ .auto_corr_max_ofdm_mrc_x1 = 240,
+
+ .auto_corr_min_cck = 125,
+ .auto_corr_max_cck = 200,
+ .auto_corr_min_cck_mrc = 200,
+ .auto_corr_max_cck_mrc = 400,
+ .nrg_th_cck = 100,
+ .nrg_th_ofdm = 100,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
+};
+
+static const struct iwl_sensitivity_ranges iwl5150_sensitivity = {
+ .min_nrg_cck = 95,
+ .auto_corr_min_ofdm = 90,
+ .auto_corr_min_ofdm_mrc = 170,
+ .auto_corr_min_ofdm_x1 = 105,
+ .auto_corr_min_ofdm_mrc_x1 = 220,
+
+ .auto_corr_max_ofdm = 120,
+ .auto_corr_max_ofdm_mrc = 210,
+ /* max = min for performance bug in 5150 DSP */
+ .auto_corr_max_ofdm_x1 = 105,
+ .auto_corr_max_ofdm_mrc_x1 = 220,
+
+ .auto_corr_min_cck = 125,
+ .auto_corr_max_cck = 200,
+ .auto_corr_min_cck_mrc = 170,
+ .auto_corr_max_cck_mrc = 400,
+ .nrg_th_cck = 95,
+ .nrg_th_ofdm = 95,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
+};
+
+#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5)
+
+static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv)
+{
+ u16 temperature, voltage;
+
+ temperature = le16_to_cpu(priv->nvm_data->kelvin_temperature);
+ voltage = le16_to_cpu(priv->nvm_data->kelvin_voltage);
+
+ /* offset = temp - volt / coeff */
+ return (s32)(temperature -
+ voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF);
+}
+
+static void iwl5150_set_ct_threshold(struct iwl_priv *priv)
+{
+ const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF;
+ s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) -
+ iwl_temp_calib_to_offset(priv);
+
+ priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef;
+}
+
+static void iwl5000_set_ct_threshold(struct iwl_priv *priv)
+{
+ /* want Celsius */
+ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
+}
+
+static void iwl5000_hw_set_hw_params(struct iwl_priv *priv)
+{
+ iwl5000_set_ct_threshold(priv);
+
+ /* Set initial sensitivity parameters */
+ priv->hw_params.sens = &iwl5000_sensitivity;
+}
+
+static void iwl5150_hw_set_hw_params(struct iwl_priv *priv)
+{
+ iwl5150_set_ct_threshold(priv);
+
+ /* Set initial sensitivity parameters */
+ priv->hw_params.sens = &iwl5150_sensitivity;
+}
+
+static void iwl5150_temperature(struct iwl_priv *priv)
+{
+ u32 vt = 0;
+ s32 offset = iwl_temp_calib_to_offset(priv);
+
+ vt = le32_to_cpu(priv->statistics.common.temperature);
+ vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset;
+ /* now vt hold the temperature in Kelvin */
+ priv->temperature = KELVIN_TO_CELSIUS(vt);
+ iwl_tt_handler(priv);
+}
+
+static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ /*
+ * MULTI-FIXME
+ * See iwlagn_mac_channel_switch.
+ */
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct iwl5000_channel_switch_cmd cmd;
+ u32 switch_time_in_usec, ucode_switch_time;
+ u16 ch;
+ u32 tsf_low;
+ u8 switch_count;
+ u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval);
+ struct ieee80211_vif *vif = ctx->vif;
+ struct iwl_host_cmd hcmd = {
+ .id = REPLY_CHANNEL_SWITCH,
+ .len = { sizeof(cmd), },
+ .data = { &cmd, },
+ };
+
+ cmd.band = priv->band == IEEE80211_BAND_2GHZ;
+ ch = ch_switch->chandef.chan->hw_value;
+ IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
+ ctx->active.channel, ch);
+ cmd.channel = cpu_to_le16(ch);
+ cmd.rxon_flags = ctx->staging.flags;
+ cmd.rxon_filter_flags = ctx->staging.filter_flags;
+ switch_count = ch_switch->count;
+ tsf_low = ch_switch->timestamp & 0x0ffffffff;
+ /*
+ * calculate the ucode channel switch time
+ * adding TSF as one of the factor for when to switch
+ */
+ if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
+ if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
+ beacon_interval)) {
+ switch_count -= (priv->ucode_beacon_time -
+ tsf_low) / beacon_interval;
+ } else
+ switch_count = 0;
+ }
+ if (switch_count <= 1)
+ cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
+ else {
+ switch_time_in_usec =
+ vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
+ ucode_switch_time = iwl_usecs_to_beacons(priv,
+ switch_time_in_usec,
+ beacon_interval);
+ cmd.switch_time = iwl_add_beacon_time(priv,
+ priv->ucode_beacon_time,
+ ucode_switch_time,
+ beacon_interval);
+ }
+ IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
+ cmd.switch_time);
+ cmd.expect_beacon =
+ ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR;
+
+ return iwl_dvm_send_cmd(priv, &hcmd);
+}
+
+const struct iwl_dvm_cfg iwl_dvm_5000_cfg = {
+ .set_hw_params = iwl5000_hw_set_hw_params,
+ .set_channel_switch = iwl5000_hw_channel_switch,
+ .temperature = iwlagn_temperature,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .no_idle_support = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_5150_cfg = {
+ .set_hw_params = iwl5150_hw_set_hw_params,
+ .set_channel_switch = iwl5000_hw_channel_switch,
+ .temperature = iwl5150_temperature,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .no_idle_support = true,
+ .no_xtal_calib = true,
+};
+
+
+
+/*
+ * 6000 series
+ * ===========
+ */
+
+static void iwl6000_set_ct_threshold(struct iwl_priv *priv)
+{
+ /* want Celsius */
+ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
+ priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
+}
+
+/* NIC configuration for 6000 series */
+static void iwl6000_nic_config(struct iwl_priv *priv)
+{
+ switch (priv->cfg->device_family) {
+ case IWL_DEVICE_FAMILY_6005:
+ case IWL_DEVICE_FAMILY_6030:
+ case IWL_DEVICE_FAMILY_6000:
+ break;
+ case IWL_DEVICE_FAMILY_6000i:
+ /* 2x2 IPA phy type */
+ iwl_write32(priv->trans, CSR_GP_DRIVER_REG,
+ CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA);
+ break;
+ case IWL_DEVICE_FAMILY_6050:
+ /* Indicate calibration version to uCode. */
+ if (priv->nvm_data->calib_version >= 6)
+ iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
+ CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6);
+ break;
+ case IWL_DEVICE_FAMILY_6150:
+ /* Indicate calibration version to uCode. */
+ if (priv->nvm_data->calib_version >= 6)
+ iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
+ CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6);
+ iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
+ CSR_GP_DRIVER_REG_BIT_6050_1x2);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+static const struct iwl_sensitivity_ranges iwl6000_sensitivity = {
+ .min_nrg_cck = 110,
+ .auto_corr_min_ofdm = 80,
+ .auto_corr_min_ofdm_mrc = 128,
+ .auto_corr_min_ofdm_x1 = 105,
+ .auto_corr_min_ofdm_mrc_x1 = 192,
+
+ .auto_corr_max_ofdm = 145,
+ .auto_corr_max_ofdm_mrc = 232,
+ .auto_corr_max_ofdm_x1 = 110,
+ .auto_corr_max_ofdm_mrc_x1 = 232,
+
+ .auto_corr_min_cck = 125,
+ .auto_corr_max_cck = 175,
+ .auto_corr_min_cck_mrc = 160,
+ .auto_corr_max_cck_mrc = 310,
+ .nrg_th_cck = 110,
+ .nrg_th_ofdm = 110,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 336,
+ .nrg_th_cca = 62,
+};
+
+static void iwl6000_hw_set_hw_params(struct iwl_priv *priv)
+{
+ iwl6000_set_ct_threshold(priv);
+
+ /* Set initial sensitivity parameters */
+ priv->hw_params.sens = &iwl6000_sensitivity;
+
+}
+
+static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ /*
+ * MULTI-FIXME
+ * See iwlagn_mac_channel_switch.
+ */
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct iwl6000_channel_switch_cmd *cmd;
+ u32 switch_time_in_usec, ucode_switch_time;
+ u16 ch;
+ u32 tsf_low;
+ u8 switch_count;
+ u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval);
+ struct ieee80211_vif *vif = ctx->vif;
+ struct iwl_host_cmd hcmd = {
+ .id = REPLY_CHANNEL_SWITCH,
+ .len = { sizeof(*cmd), },
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+ int err;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ hcmd.data[0] = cmd;
+
+ cmd->band = priv->band == IEEE80211_BAND_2GHZ;
+ ch = ch_switch->chandef.chan->hw_value;
+ IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
+ ctx->active.channel, ch);
+ cmd->channel = cpu_to_le16(ch);
+ cmd->rxon_flags = ctx->staging.flags;
+ cmd->rxon_filter_flags = ctx->staging.filter_flags;
+ switch_count = ch_switch->count;
+ tsf_low = ch_switch->timestamp & 0x0ffffffff;
+ /*
+ * calculate the ucode channel switch time
+ * adding TSF as one of the factor for when to switch
+ */
+ if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
+ if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
+ beacon_interval)) {
+ switch_count -= (priv->ucode_beacon_time -
+ tsf_low) / beacon_interval;
+ } else
+ switch_count = 0;
+ }
+ if (switch_count <= 1)
+ cmd->switch_time = cpu_to_le32(priv->ucode_beacon_time);
+ else {
+ switch_time_in_usec =
+ vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
+ ucode_switch_time = iwl_usecs_to_beacons(priv,
+ switch_time_in_usec,
+ beacon_interval);
+ cmd->switch_time = iwl_add_beacon_time(priv,
+ priv->ucode_beacon_time,
+ ucode_switch_time,
+ beacon_interval);
+ }
+ IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
+ cmd->switch_time);
+ cmd->expect_beacon =
+ ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR;
+
+ err = iwl_dvm_send_cmd(priv, &hcmd);
+ kfree(cmd);
+ return err;
+}
+
+const struct iwl_dvm_cfg iwl_dvm_6000_cfg = {
+ .set_hw_params = iwl6000_hw_set_hw_params,
+ .set_channel_switch = iwl6000_hw_channel_switch,
+ .nic_config = iwl6000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_6005_cfg = {
+ .set_hw_params = iwl6000_hw_set_hw_params,
+ .set_channel_switch = iwl6000_hw_channel_switch,
+ .nic_config = iwl6000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .need_temp_offset_calib = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_6050_cfg = {
+ .set_hw_params = iwl6000_hw_set_hw_params,
+ .set_channel_switch = iwl6000_hw_channel_switch,
+ .nic_config = iwl6000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1500,
+};
+
+static const struct iwl_dvm_bt_params iwl6000_bt_params = {
+ /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
+ .advanced_bt_coexist = true,
+ .agg_time_limit = BT_AGG_THRESHOLD_DEF,
+ .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
+ .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
+ .bt_sco_disable = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_6030_cfg = {
+ .set_hw_params = iwl6000_hw_set_hw_params,
+ .set_channel_switch = iwl6000_hw_channel_switch,
+ .nic_config = iwl6000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .bt_params = &iwl6000_bt_params,
+ .need_temp_offset_calib = true,
+ .adv_pm = true,
+};
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
new file mode 100644
index 000000000000..1aabb5ec096f
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
@@ -0,0 +1,221 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <asm/unaligned.h>
+#include "iwl-io.h"
+#include "iwl-trans.h"
+#include "iwl-modparams.h"
+#include "dev.h"
+#include "agn.h"
+
+/* Throughput OFF time(ms) ON time (ms)
+ * >300 25 25
+ * >200 to 300 40 40
+ * >100 to 200 55 55
+ * >70 to 100 65 65
+ * >50 to 70 75 75
+ * >20 to 50 85 85
+ * >10 to 20 95 95
+ * >5 to 10 110 110
+ * >1 to 5 130 130
+ * >0 to 1 167 167
+ * <=0 SOLID ON
+ */
+static const struct ieee80211_tpt_blink iwl_blink[] = {
+ { .throughput = 0, .blink_time = 334 },
+ { .throughput = 1 * 1024 - 1, .blink_time = 260 },
+ { .throughput = 5 * 1024 - 1, .blink_time = 220 },
+ { .throughput = 10 * 1024 - 1, .blink_time = 190 },
+ { .throughput = 20 * 1024 - 1, .blink_time = 170 },
+ { .throughput = 50 * 1024 - 1, .blink_time = 150 },
+ { .throughput = 70 * 1024 - 1, .blink_time = 130 },
+ { .throughput = 100 * 1024 - 1, .blink_time = 110 },
+ { .throughput = 200 * 1024 - 1, .blink_time = 80 },
+ { .throughput = 300 * 1024 - 1, .blink_time = 50 },
+};
+
+/* Set led register off */
+void iwlagn_led_enable(struct iwl_priv *priv)
+{
+ iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
+}
+
+/*
+ * Adjust led blink rate to compensate on a MAC Clock difference on every HW
+ * Led blink rate analysis showed an average deviation of 20% on 5000 series
+ * and up.
+ * Need to compensate on the led on/off time per HW according to the deviation
+ * to achieve the desired led frequency
+ * The calculation is: (100-averageDeviation)/100 * blinkTime
+ * For code efficiency the calculation will be:
+ * compensation = (100 - averageDeviation) * 64 / 100
+ * NewBlinkTime = (compensation * BlinkTime) / 64
+ */
+static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
+ u8 time, u16 compensation)
+{
+ if (!compensation) {
+ IWL_ERR(priv, "undefined blink compensation: "
+ "use pre-defined blinking time\n");
+ return time;
+ }
+
+ return (u8)((time * compensation) >> 6);
+}
+
+static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd)
+{
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_LEDS_CMD,
+ .len = { sizeof(struct iwl_led_cmd), },
+ .data = { led_cmd, },
+ .flags = CMD_ASYNC,
+ };
+ u32 reg;
+
+ reg = iwl_read32(priv->trans, CSR_LED_REG);
+ if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
+ iwl_write32(priv->trans, CSR_LED_REG,
+ reg & CSR_LED_BSM_CTRL_MSK);
+
+ return iwl_dvm_send_cmd(priv, &cmd);
+}
+
+/* Set led pattern command */
+static int iwl_led_cmd(struct iwl_priv *priv,
+ unsigned long on,
+ unsigned long off)
+{
+ struct iwl_led_cmd led_cmd = {
+ .id = IWL_LED_LINK,
+ .interval = IWL_DEF_LED_INTRVL
+ };
+ int ret;
+
+ if (!test_bit(STATUS_READY, &priv->status))
+ return -EBUSY;
+
+ if (priv->blink_on == on && priv->blink_off == off)
+ return 0;
+
+ if (off == 0) {
+ /* led is SOLID_ON */
+ on = IWL_LED_SOLID;
+ }
+
+ led_cmd.on = iwl_blink_compensation(priv, on,
+ priv->cfg->base_params->led_compensation);
+ led_cmd.off = iwl_blink_compensation(priv, off,
+ priv->cfg->base_params->led_compensation);
+
+ ret = iwl_send_led_cmd(priv, &led_cmd);
+ if (!ret) {
+ priv->blink_on = on;
+ priv->blink_off = off;
+ }
+ return ret;
+}
+
+static void iwl_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
+ unsigned long on = 0;
+
+ if (brightness > 0)
+ on = IWL_LED_SOLID;
+
+ iwl_led_cmd(priv, on, 0);
+}
+
+static int iwl_led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
+
+ return iwl_led_cmd(priv, *delay_on, *delay_off);
+}
+
+void iwl_leds_init(struct iwl_priv *priv)
+{
+ int mode = iwlwifi_mod_params.led_mode;
+ int ret;
+
+ if (mode == IWL_LED_DISABLE) {
+ IWL_INFO(priv, "Led disabled\n");
+ return;
+ }
+ if (mode == IWL_LED_DEFAULT)
+ mode = priv->cfg->led_mode;
+
+ priv->led.name = kasprintf(GFP_KERNEL, "%s-led",
+ wiphy_name(priv->hw->wiphy));
+ priv->led.brightness_set = iwl_led_brightness_set;
+ priv->led.blink_set = iwl_led_blink_set;
+ priv->led.max_brightness = 1;
+
+ switch (mode) {
+ case IWL_LED_DEFAULT:
+ WARN_ON(1);
+ break;
+ case IWL_LED_BLINK:
+ priv->led.default_trigger =
+ ieee80211_create_tpt_led_trigger(priv->hw,
+ IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
+ iwl_blink, ARRAY_SIZE(iwl_blink));
+ break;
+ case IWL_LED_RF_STATE:
+ priv->led.default_trigger =
+ ieee80211_get_radio_led_name(priv->hw);
+ break;
+ }
+
+ ret = led_classdev_register(priv->trans->dev, &priv->led);
+ if (ret) {
+ kfree(priv->led.name);
+ return;
+ }
+
+ priv->led_registered = true;
+}
+
+void iwl_leds_exit(struct iwl_priv *priv)
+{
+ if (!priv->led_registered)
+ return;
+
+ led_classdev_unregister(&priv->led);
+ kfree(priv->led.name);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.h b/drivers/net/wireless/intel/iwlwifi/dvm/led.h
new file mode 100644
index 000000000000..75f74edd018f
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_leds_h__
+#define __iwl_leds_h__
+
+
+struct iwl_priv;
+
+#define IWL_LED_SOLID 11
+#define IWL_DEF_LED_INTRVL cpu_to_le32(1000)
+
+#define IWL_LED_ACTIVITY (0<<1)
+#define IWL_LED_LINK (1<<1)
+
+#ifdef CONFIG_IWLWIFI_LEDS
+void iwlagn_led_enable(struct iwl_priv *priv);
+void iwl_leds_init(struct iwl_priv *priv);
+void iwl_leds_exit(struct iwl_priv *priv);
+#else
+static inline void iwlagn_led_enable(struct iwl_priv *priv)
+{
+}
+static inline void iwl_leds_init(struct iwl_priv *priv)
+{
+}
+static inline void iwl_leds_exit(struct iwl_priv *priv)
+{
+}
+#endif
+
+#endif /* __iwl_leds_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c
new file mode 100644
index 000000000000..4841be2aa499
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c
@@ -0,0 +1,1303 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+
+#include "iwl-io.h"
+#include "iwl-agn-hw.h"
+#include "iwl-trans.h"
+#include "iwl-modparams.h"
+
+#include "dev.h"
+#include "agn.h"
+
+int iwlagn_hw_valid_rtc_data_addr(u32 addr)
+{
+ return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) &&
+ (addr < IWLAGN_RTC_DATA_UPPER_BOUND);
+}
+
+int iwlagn_send_tx_power(struct iwl_priv *priv)
+{
+ struct iwlagn_tx_power_dbm_cmd tx_power_cmd;
+ u8 tx_ant_cfg_cmd;
+
+ if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status),
+ "TX Power requested while scanning!\n"))
+ return -EAGAIN;
+
+ /* half dBm need to multiply */
+ tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt);
+
+ if (tx_power_cmd.global_lmt > priv->nvm_data->max_tx_pwr_half_dbm) {
+ /*
+ * For the newer devices which using enhanced/extend tx power
+ * table in EEPROM, the format is in half dBm. driver need to
+ * convert to dBm format before report to mac80211.
+ * By doing so, there is a possibility of 1/2 dBm resolution
+ * lost. driver will perform "round-up" operation before
+ * reporting, but it will cause 1/2 dBm tx power over the
+ * regulatory limit. Perform the checking here, if the
+ * "tx_power_user_lmt" is higher than EEPROM value (in
+ * half-dBm format), lower the tx power based on EEPROM
+ */
+ tx_power_cmd.global_lmt =
+ priv->nvm_data->max_tx_pwr_half_dbm;
+ }
+ tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED;
+ tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO;
+
+ if (IWL_UCODE_API(priv->fw->ucode_ver) == 1)
+ tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1;
+ else
+ tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD;
+
+ return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0,
+ sizeof(tx_power_cmd), &tx_power_cmd);
+}
+
+void iwlagn_temperature(struct iwl_priv *priv)
+{
+ lockdep_assert_held(&priv->statistics.lock);
+
+ /* store temperature from correct statistics (in Celsius) */
+ priv->temperature = le32_to_cpu(priv->statistics.common.temperature);
+ iwl_tt_handler(priv);
+}
+
+int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band)
+{
+ int idx = 0;
+ int band_offset = 0;
+
+ /* HT rate format: mac80211 wants an MCS number, which is just LSB */
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ idx = (rate_n_flags & 0xff);
+ return idx;
+ /* Legacy rate format, search for match in table */
+ } else {
+ if (band == IEEE80211_BAND_5GHZ)
+ band_offset = IWL_FIRST_OFDM_RATE;
+ for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
+ if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF))
+ return idx - band_offset;
+ }
+
+ return -1;
+}
+
+int iwlagn_manage_ibss_station(struct iwl_priv *priv,
+ struct ieee80211_vif *vif, bool add)
+{
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+
+ if (add)
+ return iwlagn_add_bssid_station(priv, vif_priv->ctx,
+ vif->bss_conf.bssid,
+ &vif_priv->ibss_bssid_sta_id);
+ return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id,
+ vif->bss_conf.bssid);
+}
+
+/**
+ * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode
+ *
+ * pre-requirements:
+ * 1. acquire mutex before calling
+ * 2. make sure rf is on and not in exit state
+ */
+int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
+{
+ struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = {
+ .flush_control = cpu_to_le16(IWL_DROP_ALL),
+ };
+ struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = {
+ .flush_control = cpu_to_le16(IWL_DROP_ALL),
+ };
+
+ u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
+ IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK;
+
+ if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
+ queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK |
+ IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK |
+ IWL_PAN_SCD_MGMT_MSK |
+ IWL_PAN_SCD_MULTICAST_MSK;
+
+ if (priv->nvm_data->sku_cap_11n_enable)
+ queue_control |= IWL_AGG_TX_QUEUE_MSK;
+
+ if (scd_q_msk)
+ queue_control = scd_q_msk;
+
+ IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control);
+ flush_cmd_v3.queue_control = cpu_to_le32(queue_control);
+ flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control);
+
+ if (IWL_UCODE_API(priv->fw->ucode_ver) > 2)
+ return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+ sizeof(flush_cmd_v3),
+ &flush_cmd_v3);
+ return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+ sizeof(flush_cmd_v2), &flush_cmd_v2);
+}
+
+void iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
+{
+ mutex_lock(&priv->mutex);
+ ieee80211_stop_queues(priv->hw);
+ if (iwlagn_txfifo_flush(priv, 0)) {
+ IWL_ERR(priv, "flush request fail\n");
+ goto done;
+ }
+ IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
+ iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
+done:
+ ieee80211_wake_queues(priv->hw);
+ mutex_unlock(&priv->mutex);
+}
+
+/*
+ * BT coex
+ */
+/* Notmal TDM */
+static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0x00004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+};
+
+
+/* Loose Coex */
+static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+};
+
+/* Full concurrency */
+static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+};
+
+void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
+{
+ struct iwl_basic_bt_cmd basic = {
+ .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT,
+ .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT,
+ .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT,
+ .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT,
+ };
+ struct iwl_bt_cmd_v1 bt_cmd_v1;
+ struct iwl_bt_cmd_v2 bt_cmd_v2;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
+ sizeof(basic.bt3_lookup_table));
+
+ if (priv->lib->bt_params) {
+ /*
+ * newer generation of devices (2000 series and newer)
+ * use the version 2 of the bt command
+ * we need to make sure sending the host command
+ * with correct data structure to avoid uCode assert
+ */
+ if (priv->lib->bt_params->bt_session_2) {
+ bt_cmd_v2.prio_boost = cpu_to_le32(
+ priv->lib->bt_params->bt_prio_boost);
+ bt_cmd_v2.tx_prio_boost = 0;
+ bt_cmd_v2.rx_prio_boost = 0;
+ } else {
+ /* older version only has 8 bits */
+ WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF);
+ bt_cmd_v1.prio_boost =
+ priv->lib->bt_params->bt_prio_boost;
+ bt_cmd_v1.tx_prio_boost = 0;
+ bt_cmd_v1.rx_prio_boost = 0;
+ }
+ } else {
+ IWL_ERR(priv, "failed to construct BT Coex Config\n");
+ return;
+ }
+
+ /*
+ * Possible situations when BT needs to take over for receive,
+ * at the same time where STA needs to response to AP's frame(s),
+ * reduce the tx power of the required response frames, by that,
+ * allow the concurrent BT receive & WiFi transmit
+ * (BT - ANT A, WiFi -ANT B), without interference to one another
+ *
+ * Reduced tx power apply to control frames only (ACK/Back/CTS)
+ * when indicated by the BT config command
+ */
+ basic.kill_ack_mask = priv->kill_ack_mask;
+ basic.kill_cts_mask = priv->kill_cts_mask;
+ if (priv->reduced_txpower)
+ basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR;
+ basic.valid = priv->bt_valid;
+
+ /*
+ * Configure BT coex mode to "no coexistence" when the
+ * user disabled BT coexistence, we have no interface
+ * (might be in monitor mode), or the interface is in
+ * IBSS mode (no proper uCode support for coex then).
+ */
+ if (!iwlwifi_mod_params.bt_coex_active ||
+ priv->iw_mode == NL80211_IFTYPE_ADHOC) {
+ basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED;
+ } else {
+ basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W <<
+ IWLAGN_BT_FLAG_COEX_MODE_SHIFT;
+
+ if (!priv->bt_enable_pspoll)
+ basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
+ else
+ basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
+
+ if (priv->bt_ch_announce)
+ basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION;
+ IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags);
+ }
+ priv->bt_enable_flag = basic.flags;
+ if (priv->bt_full_concurrent)
+ memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup,
+ sizeof(iwlagn_concurrent_lookup));
+ else
+ memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup,
+ sizeof(iwlagn_def_3w_lookup));
+
+ IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n",
+ basic.flags ? "active" : "disabled",
+ priv->bt_full_concurrent ?
+ "full concurrency" : "3-wire");
+
+ if (priv->lib->bt_params->bt_session_2) {
+ memcpy(&bt_cmd_v2.basic, &basic,
+ sizeof(basic));
+ ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
+ 0, sizeof(bt_cmd_v2), &bt_cmd_v2);
+ } else {
+ memcpy(&bt_cmd_v1.basic, &basic,
+ sizeof(basic));
+ ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
+ 0, sizeof(bt_cmd_v1), &bt_cmd_v1);
+ }
+ if (ret)
+ IWL_ERR(priv, "failed to send BT Coex Config\n");
+
+}
+
+void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena)
+{
+ struct iwl_rxon_context *ctx, *found_ctx = NULL;
+ bool found_ap = false;
+
+ lockdep_assert_held(&priv->mutex);
+
+ /* Check whether AP or GO mode is active. */
+ if (rssi_ena) {
+ for_each_context(priv, ctx) {
+ if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP &&
+ iwl_is_associated_ctx(ctx)) {
+ found_ap = true;
+ break;
+ }
+ }
+ }
+
+ /*
+ * If disable was received or If GO/AP mode, disable RSSI
+ * measurements.
+ */
+ if (!rssi_ena || found_ap) {
+ if (priv->cur_rssi_ctx) {
+ ctx = priv->cur_rssi_ctx;
+ ieee80211_disable_rssi_reports(ctx->vif);
+ priv->cur_rssi_ctx = NULL;
+ }
+ return;
+ }
+
+ /*
+ * If rssi measurements need to be enabled, consider all cases now.
+ * Figure out how many contexts are active.
+ */
+ for_each_context(priv, ctx) {
+ if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION &&
+ iwl_is_associated_ctx(ctx)) {
+ found_ctx = ctx;
+ break;
+ }
+ }
+
+ /*
+ * rssi monitor already enabled for the correct interface...nothing
+ * to do.
+ */
+ if (found_ctx == priv->cur_rssi_ctx)
+ return;
+
+ /*
+ * Figure out if rssi monitor is currently enabled, and needs
+ * to be changed. If rssi monitor is already enabled, disable
+ * it first else just enable rssi measurements on the
+ * interface found above.
+ */
+ if (priv->cur_rssi_ctx) {
+ ctx = priv->cur_rssi_ctx;
+ if (ctx->vif)
+ ieee80211_disable_rssi_reports(ctx->vif);
+ }
+
+ priv->cur_rssi_ctx = found_ctx;
+
+ if (!found_ctx)
+ return;
+
+ ieee80211_enable_rssi_reports(found_ctx->vif,
+ IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD,
+ IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD);
+}
+
+static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg)
+{
+ return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3SCOESCO_POS;
+}
+
+static void iwlagn_bt_traffic_change_work(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, bt_traffic_change_work);
+ struct iwl_rxon_context *ctx;
+ int smps_request = -1;
+
+ if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) {
+ /* bt coex disabled */
+ return;
+ }
+
+ /*
+ * Note: bt_traffic_load can be overridden by scan complete and
+ * coex profile notifications. Ignore that since only bad consequence
+ * can be not matching debug print with actual state.
+ */
+ IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n",
+ priv->bt_traffic_load);
+
+ switch (priv->bt_traffic_load) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ if (priv->bt_status)
+ smps_request = IEEE80211_SMPS_DYNAMIC;
+ else
+ smps_request = IEEE80211_SMPS_AUTOMATIC;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ smps_request = IEEE80211_SMPS_DYNAMIC;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ smps_request = IEEE80211_SMPS_STATIC;
+ break;
+ default:
+ IWL_ERR(priv, "Invalid BT traffic load: %d\n",
+ priv->bt_traffic_load);
+ break;
+ }
+
+ mutex_lock(&priv->mutex);
+
+ /*
+ * We can not send command to firmware while scanning. When the scan
+ * complete we will schedule this work again. We do check with mutex
+ * locked to prevent new scan request to arrive. We do not check
+ * STATUS_SCANNING to avoid race when queue_work two times from
+ * different notifications, but quit and not perform any work at all.
+ */
+ if (test_bit(STATUS_SCAN_HW, &priv->status))
+ goto out;
+
+ iwl_update_chain_flags(priv);
+
+ if (smps_request != -1) {
+ priv->current_ht_config.smps = smps_request;
+ for_each_context(priv, ctx) {
+ if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION)
+ ieee80211_request_smps(ctx->vif, smps_request);
+ }
+ }
+
+ /*
+ * Dynamic PS poll related functionality. Adjust RSSI measurements if
+ * necessary.
+ */
+ iwlagn_bt_coex_rssi_monitor(priv);
+out:
+ mutex_unlock(&priv->mutex);
+}
+
+/*
+ * If BT sco traffic, and RSSI monitor is enabled, move measurements to the
+ * correct interface or disable it if this is the last interface to be
+ * removed.
+ */
+void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv)
+{
+ if (priv->bt_is_sco &&
+ priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS)
+ iwlagn_bt_adjust_rssi_monitor(priv, true);
+ else
+ iwlagn_bt_adjust_rssi_monitor(priv, false);
+}
+
+static void iwlagn_print_uartmsg(struct iwl_priv *priv,
+ struct iwl_bt_uart_msg *uart_msg)
+{
+ IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, "
+ "Update Req = 0x%X\n",
+ (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >>
+ BT_UART_MSG_FRAME1MSGTYPE_POS,
+ (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >>
+ BT_UART_MSG_FRAME1SSN_POS,
+ (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >>
+ BT_UART_MSG_FRAME1UPDATEREQ_POS);
+
+ IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, "
+ "Chl_SeqN = 0x%X, In band = 0x%X\n",
+ (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >>
+ BT_UART_MSG_FRAME2OPENCONNECTIONS_POS,
+ (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >>
+ BT_UART_MSG_FRAME2TRAFFICLOAD_POS,
+ (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >>
+ BT_UART_MSG_FRAME2CHLSEQN_POS,
+ (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >>
+ BT_UART_MSG_FRAME2INBAND_POS);
+
+ IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, "
+ "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n",
+ (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3SCOESCO_POS,
+ (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3SNIFF_POS,
+ (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3A2DP_POS,
+ (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3ACL_POS,
+ (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3MASTER_POS,
+ (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3OBEX_POS);
+
+ IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n",
+ (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >>
+ BT_UART_MSG_FRAME4IDLEDURATION_POS);
+
+ IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, "
+ "eSCO Retransmissions = 0x%X\n",
+ (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >>
+ BT_UART_MSG_FRAME5TXACTIVITY_POS,
+ (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >>
+ BT_UART_MSG_FRAME5RXACTIVITY_POS,
+ (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >>
+ BT_UART_MSG_FRAME5ESCORETRANSMIT_POS);
+
+ IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n",
+ (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >>
+ BT_UART_MSG_FRAME6SNIFFINTERVAL_POS,
+ (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >>
+ BT_UART_MSG_FRAME6DISCOVERABLE_POS);
+
+ IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = "
+ "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n",
+ (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >>
+ BT_UART_MSG_FRAME7SNIFFACTIVITY_POS,
+ (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >>
+ BT_UART_MSG_FRAME7PAGE_POS,
+ (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >>
+ BT_UART_MSG_FRAME7INQUIRY_POS,
+ (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >>
+ BT_UART_MSG_FRAME7CONNECTABLE_POS);
+}
+
+static bool iwlagn_set_kill_msk(struct iwl_priv *priv,
+ struct iwl_bt_uart_msg *uart_msg)
+{
+ bool need_update = false;
+ u8 kill_msk = IWL_BT_KILL_REDUCE;
+ static const __le32 bt_kill_ack_msg[3] = {
+ IWLAGN_BT_KILL_ACK_MASK_DEFAULT,
+ IWLAGN_BT_KILL_ACK_CTS_MASK_SCO,
+ IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE};
+ static const __le32 bt_kill_cts_msg[3] = {
+ IWLAGN_BT_KILL_CTS_MASK_DEFAULT,
+ IWLAGN_BT_KILL_ACK_CTS_MASK_SCO,
+ IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE};
+
+ if (!priv->reduced_txpower)
+ kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3)
+ ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT;
+ if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] ||
+ priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) {
+ priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK;
+ priv->kill_ack_mask = bt_kill_ack_msg[kill_msk];
+ priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK;
+ priv->kill_cts_mask = bt_kill_cts_msg[kill_msk];
+ need_update = true;
+ }
+ return need_update;
+}
+
+/*
+ * Upon RSSI changes, sends a bt config command with following changes
+ * 1. enable/disable "reduced control frames tx power
+ * 2. update the "kill)ack_mask" and "kill_cts_mask"
+ *
+ * If "reduced tx power" is enabled, uCode shall
+ * 1. ACK/Back/CTS rate shall reduced to 6Mbps
+ * 2. not use duplciate 20/40MHz mode
+ */
+static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv,
+ struct iwl_bt_uart_msg *uart_msg)
+{
+ bool need_update = false;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ int ave_rssi;
+
+ if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) {
+ IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n");
+ return false;
+ }
+
+ ave_rssi = ieee80211_ave_rssi(ctx->vif);
+ if (!ave_rssi) {
+ /* no rssi data, no changes to reduce tx power */
+ IWL_DEBUG_COEX(priv, "no rssi data available\n");
+ return need_update;
+ }
+ if (!priv->reduced_txpower &&
+ !iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
+ (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) &&
+ (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK |
+ BT_UART_MSG_FRAME3OBEX_MSK)) &&
+ !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK |
+ BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) {
+ /* enabling reduced tx power */
+ priv->reduced_txpower = true;
+ priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR;
+ need_update = true;
+ } else if (priv->reduced_txpower &&
+ (iwl_is_associated(priv, IWL_RXON_CTX_PAN) ||
+ (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) ||
+ (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK |
+ BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) ||
+ !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK |
+ BT_UART_MSG_FRAME3OBEX_MSK)))) {
+ /* disable reduced tx power */
+ priv->reduced_txpower = false;
+ priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR;
+ need_update = true;
+ }
+
+ return need_update;
+}
+
+static void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data;
+ struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg;
+
+ if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) {
+ /* bt coex disabled */
+ return;
+ }
+
+ IWL_DEBUG_COEX(priv, "BT Coex notification:\n");
+ IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status);
+ IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load);
+ IWL_DEBUG_COEX(priv, " CI compliance: %d\n",
+ coex->bt_ci_compliance);
+ iwlagn_print_uartmsg(priv, uart_msg);
+
+ priv->last_bt_traffic_load = priv->bt_traffic_load;
+ priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg);
+
+ if (priv->iw_mode != NL80211_IFTYPE_ADHOC) {
+ if (priv->bt_status != coex->bt_status ||
+ priv->last_bt_traffic_load != coex->bt_traffic_load) {
+ if (coex->bt_status) {
+ /* BT on */
+ if (!priv->bt_ch_announce)
+ priv->bt_traffic_load =
+ IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
+ else
+ priv->bt_traffic_load =
+ coex->bt_traffic_load;
+ } else {
+ /* BT off */
+ priv->bt_traffic_load =
+ IWL_BT_COEX_TRAFFIC_LOAD_NONE;
+ }
+ priv->bt_status = coex->bt_status;
+ queue_work(priv->workqueue,
+ &priv->bt_traffic_change_work);
+ }
+ }
+
+ /* schedule to send runtime bt_config */
+ /* check reduce power before change ack/cts kill mask */
+ if (iwlagn_fill_txpower_mode(priv, uart_msg) ||
+ iwlagn_set_kill_msk(priv, uart_msg))
+ queue_work(priv->workqueue, &priv->bt_runtime_config);
+
+
+ /* FIXME: based on notification, adjust the prio_boost */
+
+ priv->bt_ci_compliance = coex->bt_ci_compliance;
+}
+
+void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv)
+{
+ priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] =
+ iwlagn_bt_coex_profile_notif;
+}
+
+void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv)
+{
+ INIT_WORK(&priv->bt_traffic_change_work,
+ iwlagn_bt_traffic_change_work);
+}
+
+void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv)
+{
+ cancel_work_sync(&priv->bt_traffic_change_work);
+}
+
+static bool is_single_rx_stream(struct iwl_priv *priv)
+{
+ return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
+ priv->current_ht_config.single_chain_sufficient;
+}
+
+#define IWL_NUM_RX_CHAINS_MULTIPLE 3
+#define IWL_NUM_RX_CHAINS_SINGLE 2
+#define IWL_NUM_IDLE_CHAINS_DUAL 2
+#define IWL_NUM_IDLE_CHAINS_SINGLE 1
+
+/*
+ * Determine how many receiver/antenna chains to use.
+ *
+ * More provides better reception via diversity. Fewer saves power
+ * at the expense of throughput, but only when not in powersave to
+ * start with.
+ *
+ * MIMO (dual stream) requires at least 2, but works better with 3.
+ * This does not determine *which* chains to use, just how many.
+ */
+static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
+{
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
+ (priv->bt_full_concurrent ||
+ priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
+ /*
+ * only use chain 'A' in bt high traffic load or
+ * full concurrency mode
+ */
+ return IWL_NUM_RX_CHAINS_SINGLE;
+ }
+ /* # of Rx chains to use when expecting MIMO. */
+ if (is_single_rx_stream(priv))
+ return IWL_NUM_RX_CHAINS_SINGLE;
+ else
+ return IWL_NUM_RX_CHAINS_MULTIPLE;
+}
+
+/*
+ * When we are in power saving mode, unless device support spatial
+ * multiplexing power save, use the active count for rx chain count.
+ */
+static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
+{
+ /* # Rx chains when idling, depending on SMPS mode */
+ switch (priv->current_ht_config.smps) {
+ case IEEE80211_SMPS_STATIC:
+ case IEEE80211_SMPS_DYNAMIC:
+ return IWL_NUM_IDLE_CHAINS_SINGLE;
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_OFF:
+ return active_cnt;
+ default:
+ WARN(1, "invalid SMPS mode %d",
+ priv->current_ht_config.smps);
+ return active_cnt;
+ }
+}
+
+/* up to 4 chains */
+static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
+{
+ u8 res;
+ res = (chain_bitmap & BIT(0)) >> 0;
+ res += (chain_bitmap & BIT(1)) >> 1;
+ res += (chain_bitmap & BIT(2)) >> 2;
+ res += (chain_bitmap & BIT(3)) >> 3;
+ return res;
+}
+
+/**
+ * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
+ *
+ * Selects how many and which Rx receivers/antennas/chains to use.
+ * This should not be used for scan command ... it puts data in wrong place.
+ */
+void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
+{
+ bool is_single = is_single_rx_stream(priv);
+ bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
+ u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
+ u32 active_chains;
+ u16 rx_chain;
+
+ /* Tell uCode which antennas are actually connected.
+ * Before first association, we assume all antennas are connected.
+ * Just after first association, iwl_chain_noise_calibration()
+ * checks which antennas actually *are* connected. */
+ if (priv->chain_noise_data.active_chains)
+ active_chains = priv->chain_noise_data.active_chains;
+ else
+ active_chains = priv->nvm_data->valid_rx_ant;
+
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
+ (priv->bt_full_concurrent ||
+ priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
+ /*
+ * only use chain 'A' in bt high traffic load or
+ * full concurrency mode
+ */
+ active_chains = first_antenna(active_chains);
+ }
+
+ rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
+
+ /* How many receivers should we use? */
+ active_rx_cnt = iwl_get_active_rx_chain_count(priv);
+ idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt);
+
+
+ /* correct rx chain count according hw settings
+ * and chain noise calibration
+ */
+ valid_rx_cnt = iwl_count_chain_bitmap(active_chains);
+ if (valid_rx_cnt < active_rx_cnt)
+ active_rx_cnt = valid_rx_cnt;
+
+ if (valid_rx_cnt < idle_rx_cnt)
+ idle_rx_cnt = valid_rx_cnt;
+
+ rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
+ rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;
+
+ ctx->staging.rx_chain = cpu_to_le16(rx_chain);
+
+ if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam)
+ ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
+ else
+ ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
+
+ IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n",
+ ctx->staging.rx_chain,
+ active_rx_cnt, idle_rx_cnt);
+
+ WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 ||
+ active_rx_cnt < idle_rx_cnt);
+}
+
+u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid)
+{
+ int i;
+ u8 ind = ant;
+
+ if (priv->band == IEEE80211_BAND_2GHZ &&
+ priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
+ return 0;
+
+ for (i = 0; i < RATE_ANT_NUM - 1; i++) {
+ ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0;
+ if (valid & BIT(ind))
+ return ind;
+ }
+ return ant;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void iwlagn_convert_p1k(u16 *p1k, __le16 *out)
+{
+ int i;
+
+ for (i = 0; i < IWLAGN_P1K_SIZE; i++)
+ out[i] = cpu_to_le16(p1k[i]);
+}
+
+struct wowlan_key_data {
+ struct iwl_rxon_context *ctx;
+ struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc;
+ struct iwlagn_wowlan_tkip_params_cmd *tkip;
+ const u8 *bssid;
+ bool error, use_rsc_tsc, use_tkip;
+};
+
+
+static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct wowlan_key_data *data = _data;
+ struct iwl_rxon_context *ctx = data->ctx;
+ struct aes_sc *aes_sc, *aes_tx_sc = NULL;
+ struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
+ struct iwlagn_p1k_cache *rx_p1ks;
+ u8 *rx_mic_key;
+ struct ieee80211_key_seq seq;
+ u32 cur_rx_iv32 = 0;
+ u16 p1k[IWLAGN_P1K_SIZE];
+ int ret, i;
+
+ mutex_lock(&priv->mutex);
+
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
+ !sta && !ctx->key_mapping_keys)
+ ret = iwl_set_default_wep_key(priv, ctx, key);
+ else
+ ret = iwl_set_dynamic_key(priv, ctx, key, sta);
+
+ if (ret) {
+ IWL_ERR(priv, "Error setting key during suspend!\n");
+ data->error = true;
+ }
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (sta) {
+ tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
+ tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
+
+ rx_p1ks = data->tkip->rx_uni;
+
+ ieee80211_get_key_tx_seq(key, &seq);
+ tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
+ tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
+
+ ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
+ iwlagn_convert_p1k(p1k, data->tkip->tx.p1k);
+
+ memcpy(data->tkip->mic_keys.tx,
+ &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
+ IWLAGN_MIC_KEY_SIZE);
+
+ rx_mic_key = data->tkip->mic_keys.rx_unicast;
+ } else {
+ tkip_sc =
+ data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
+ rx_p1ks = data->tkip->rx_multi;
+ rx_mic_key = data->tkip->mic_keys.rx_mcast;
+ }
+
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211 use TID 0 (as they need to to avoid replay attacks)
+ * for checking the IV in the frames.
+ */
+ for (i = 0; i < IWLAGN_NUM_RSC; i++) {
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
+ tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
+ /* wrapping isn't allowed, AP must rekey */
+ if (seq.tkip.iv32 > cur_rx_iv32)
+ cur_rx_iv32 = seq.tkip.iv32;
+ }
+
+ ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k);
+ iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k);
+ ieee80211_get_tkip_rx_p1k(key, data->bssid,
+ cur_rx_iv32 + 1, p1k);
+ iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k);
+
+ memcpy(rx_mic_key,
+ &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
+ IWLAGN_MIC_KEY_SIZE);
+
+ data->use_tkip = true;
+ data->use_rsc_tsc = true;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (sta) {
+ u8 *pn = seq.ccmp.pn;
+
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
+ aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
+
+ ieee80211_get_key_tx_seq(key, &seq);
+ aes_tx_sc->pn = cpu_to_le64(
+ (u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ } else
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
+
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211 use TID 0 for checking the IV in the frames.
+ */
+ for (i = 0; i < IWLAGN_NUM_RSC; i++) {
+ u8 *pn = seq.ccmp.pn;
+
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ aes_sc[i].pn = cpu_to_le64(
+ (u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+ data->use_rsc_tsc = true;
+ break;
+ }
+
+ mutex_unlock(&priv->mutex);
+}
+
+int iwlagn_send_patterns(struct iwl_priv *priv,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct iwlagn_wowlan_patterns_cmd *pattern_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_WOWLAN_PATTERNS,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+ int i, err;
+
+ if (!wowlan->n_patterns)
+ return 0;
+
+ cmd.len[0] = sizeof(*pattern_cmd) +
+ wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern);
+
+ pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
+ if (!pattern_cmd)
+ return -ENOMEM;
+
+ pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
+
+ for (i = 0; i < wowlan->n_patterns; i++) {
+ int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
+
+ memcpy(&pattern_cmd->patterns[i].mask,
+ wowlan->patterns[i].mask, mask_len);
+ memcpy(&pattern_cmd->patterns[i].pattern,
+ wowlan->patterns[i].pattern,
+ wowlan->patterns[i].pattern_len);
+ pattern_cmd->patterns[i].mask_size = mask_len;
+ pattern_cmd->patterns[i].pattern_size =
+ wowlan->patterns[i].pattern_len;
+ }
+
+ cmd.data[0] = pattern_cmd;
+ err = iwl_dvm_send_cmd(priv, &cmd);
+ kfree(pattern_cmd);
+ return err;
+}
+
+int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
+{
+ struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd;
+ struct iwl_rxon_cmd rxon;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd;
+ struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {};
+ struct iwlagn_d3_config_cmd d3_cfg_cmd = {
+ /*
+ * Program the minimum sleep time to 10 seconds, as many
+ * platforms have issues processing a wakeup signal while
+ * still being in the process of suspending.
+ */
+ .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
+ };
+ struct wowlan_key_data key_data = {
+ .ctx = ctx,
+ .bssid = ctx->active.bssid_addr,
+ .use_rsc_tsc = false,
+ .tkip = &tkip_cmd,
+ .use_tkip = false,
+ };
+ int ret, i;
+ u16 seq;
+
+ key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+ if (!key_data.rsc_tsc)
+ return -ENOMEM;
+
+ memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd));
+
+ /*
+ * We know the last used seqno, and the uCode expects to know that
+ * one, it will increment before TX.
+ */
+ seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ;
+ wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq);
+
+ /*
+ * For QoS counters, we store the one to use next, so subtract 0x10
+ * since the uCode will add 0x10 before using the value.
+ */
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ seq = priv->tid_data[IWL_AP_ID][i].seq_number;
+ seq -= 0x10;
+ wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq);
+ }
+
+ if (wowlan->disconnect)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
+ IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE);
+ if (wowlan->magic_pkt)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET);
+ if (wowlan->gtk_rekey_failure)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
+ if (wowlan->eap_identity_req)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ);
+ if (wowlan->four_way_handshake)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
+ if (wowlan->n_patterns)
+ wakeup_filter_cmd.enabled |=
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH);
+
+ if (wowlan->rfkill_release)
+ d3_cfg_cmd.wakeup_flags |=
+ cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL);
+
+ iwl_scan_cancel_timeout(priv, 200);
+
+ memcpy(&rxon, &ctx->active, sizeof(rxon));
+
+ priv->ucode_loaded = false;
+ iwl_trans_stop_device(priv->trans);
+ ret = iwl_trans_start_hw(priv->trans);
+ if (ret)
+ goto out;
+
+ priv->wowlan = true;
+
+ ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
+ if (ret)
+ goto out;
+
+ /* now configure WoWLAN ucode */
+ ret = iwl_alive_start(priv);
+ if (ret)
+ goto out;
+
+ memcpy(&ctx->staging, &rxon, sizeof(rxon));
+ ret = iwlagn_commit_rxon(priv, ctx);
+ if (ret)
+ goto out;
+
+ ret = iwl_power_update_mode(priv, true);
+ if (ret)
+ goto out;
+
+ if (!iwlwifi_mod_params.sw_crypto) {
+ /* mark all keys clear */
+ priv->ucode_key_table = 0;
+ ctx->key_mapping_keys = 0;
+
+ /*
+ * This needs to be unlocked due to lock ordering
+ * constraints. Since we're in the suspend path
+ * that isn't really a problem though.
+ */
+ mutex_unlock(&priv->mutex);
+ ieee80211_iter_keys(priv->hw, ctx->vif,
+ iwlagn_wowlan_program_keys,
+ &key_data);
+ mutex_lock(&priv->mutex);
+ if (key_data.error) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (key_data.use_rsc_tsc) {
+ struct iwl_host_cmd rsc_tsc_cmd = {
+ .id = REPLY_WOWLAN_TSC_RSC_PARAMS,
+ .data[0] = key_data.rsc_tsc,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ .len[0] = sizeof(*key_data.rsc_tsc),
+ };
+
+ ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd);
+ if (ret)
+ goto out;
+ }
+
+ if (key_data.use_tkip) {
+ ret = iwl_dvm_send_cmd_pdu(priv,
+ REPLY_WOWLAN_TKIP_PARAMS,
+ 0, sizeof(tkip_cmd),
+ &tkip_cmd);
+ if (ret)
+ goto out;
+ }
+
+ if (priv->have_rekey_data) {
+ memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
+ memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN);
+ kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
+ memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN);
+ kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
+ kek_kck_cmd.replay_ctr = priv->replay_ctr;
+
+ ret = iwl_dvm_send_cmd_pdu(priv,
+ REPLY_WOWLAN_KEK_KCK_MATERIAL,
+ 0, sizeof(kek_kck_cmd),
+ &kek_kck_cmd);
+ if (ret)
+ goto out;
+ }
+ }
+
+ ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0,
+ sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+ if (ret)
+ goto out;
+
+ ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER,
+ 0, sizeof(wakeup_filter_cmd),
+ &wakeup_filter_cmd);
+ if (ret)
+ goto out;
+
+ ret = iwlagn_send_patterns(priv, wowlan);
+ out:
+ kfree(key_data.rsc_tsc);
+ return ret;
+}
+#endif
+
+int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
+{
+ if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) {
+ IWL_WARN(priv, "Not sending command - %s KILL\n",
+ iwl_is_rfkill(priv) ? "RF" : "CT");
+ return -EIO;
+ }
+
+ if (test_bit(STATUS_FW_ERROR, &priv->status)) {
+ IWL_ERR(priv, "Command %s failed: FW Error\n",
+ iwl_get_cmd_string(priv->trans, cmd->id));
+ return -EIO;
+ }
+
+ /*
+ * This can happen upon FW ASSERT: we clear the STATUS_FW_ERROR flag
+ * in iwl_down but cancel the workers only later.
+ */
+ if (!priv->ucode_loaded) {
+ IWL_ERR(priv, "Fw not loaded - dropping CMD: %x\n", cmd->id);
+ return -EIO;
+ }
+
+ /*
+ * Synchronous commands from this op-mode must hold
+ * the mutex, this ensures we don't try to send two
+ * (or more) synchronous commands at a time.
+ */
+ if (!(cmd->flags & CMD_ASYNC))
+ lockdep_assert_held(&priv->mutex);
+
+ return iwl_trans_send_cmd(priv->trans, cmd);
+}
+
+int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id,
+ u32 flags, u16 len, const void *data)
+{
+ struct iwl_host_cmd cmd = {
+ .id = id,
+ .len = { len, },
+ .data = { data, },
+ .flags = flags,
+ };
+
+ return iwl_dvm_send_cmd(priv, &cmd);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
new file mode 100644
index 000000000000..29ea1c6705b4
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
@@ -0,0 +1,1652 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+
+#include <net/ieee80211_radiotap.h>
+#include <net/mac80211.h>
+
+#include <asm/div64.h>
+
+#include "iwl-io.h"
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-modparams.h"
+
+#include "dev.h"
+#include "calib.h"
+#include "agn.h"
+
+/*****************************************************************************
+ *
+ * mac80211 entry point functions
+ *
+ *****************************************************************************/
+
+static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP),
+ },
+};
+
+static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+};
+
+static const struct ieee80211_iface_combination
+iwlagn_iface_combinations_dualmode[] = {
+ { .num_different_channels = 1,
+ .max_interfaces = 2,
+ .beacon_int_infra_match = true,
+ .limits = iwlagn_sta_ap_limits,
+ .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits),
+ },
+ { .num_different_channels = 1,
+ .max_interfaces = 2,
+ .limits = iwlagn_2sta_limits,
+ .n_limits = ARRAY_SIZE(iwlagn_2sta_limits),
+ },
+};
+
+/*
+ * Not a mac80211 entry point function, but it fits in with all the
+ * other mac80211 functions grouped here.
+ */
+int iwlagn_mac_setup_register(struct iwl_priv *priv,
+ const struct iwl_ucode_capabilities *capa)
+{
+ int ret;
+ struct ieee80211_hw *hw = priv->hw;
+ struct iwl_rxon_context *ctx;
+
+ hw->rate_control_algorithm = "iwl-agn-rs";
+
+ /* Tell mac80211 our characteristics */
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
+ ieee80211_hw_set(hw, SPECTRUM_MGMT);
+ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+ ieee80211_hw_set(hw, QUEUE_CONTROL);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+ ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+
+ if (priv->trans->max_skb_frags)
+ hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
+
+ hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE;
+ hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT;
+
+ /*
+ * Including the following line will crash some AP's. This
+ * workaround removes the stimulus which causes the crash until
+ * the AP software can be fixed.
+ hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+ */
+
+ if (priv->nvm_data->sku_cap_11n_enable)
+ hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS |
+ NL80211_FEATURE_STATIC_SMPS;
+
+ /*
+ * Enable 11w if advertised by firmware and software crypto
+ * is not enabled (as the firmware will interpret some mgmt
+ * packets, so enabling it with software crypto isn't safe)
+ */
+ if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP &&
+ !iwlwifi_mod_params.sw_crypto)
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+
+ hw->sta_data_size = sizeof(struct iwl_station_priv);
+ hw->vif_data_size = sizeof(struct iwl_vif_priv);
+
+ for_each_context(priv, ctx) {
+ hw->wiphy->interface_modes |= ctx->interface_modes;
+ hw->wiphy->interface_modes |= ctx->exclusive_interface_modes;
+ }
+
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+
+ if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
+ hw->wiphy->iface_combinations =
+ iwlagn_iface_combinations_dualmode;
+ hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(iwlagn_iface_combinations_dualmode);
+ }
+
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+ REGULATORY_DISABLE_BEACON_HINTS;
+
+#ifdef CONFIG_PM_SLEEP
+ if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+ priv->trans->ops->d3_suspend &&
+ priv->trans->ops->d3_resume &&
+ device_can_wakeup(priv->trans->dev)) {
+ 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)
+ priv->wowlan_support.flags |=
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE;
+
+ priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
+ priv->wowlan_support.pattern_min_len =
+ IWLAGN_WOWLAN_MIN_PATTERN_LEN;
+ priv->wowlan_support.pattern_max_len =
+ IWLAGN_WOWLAN_MAX_PATTERN_LEN;
+ hw->wiphy->wowlan = &priv->wowlan_support;
+ }
+#endif
+
+ if (iwlwifi_mod_params.power_save)
+ hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ else
+ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+ /* we create the 802.11 header and a max-length SSID element */
+ hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34;
+
+ /*
+ * We don't use all queues: 4 and 9 are unused and any
+ * aggregation queue gets mapped down to the AC queue.
+ */
+ hw->queues = IWLAGN_FIRST_AMPDU_QUEUE;
+
+ hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+
+ if (priv->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &priv->nvm_data->bands[IEEE80211_BAND_2GHZ];
+ if (priv->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels)
+ priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &priv->nvm_data->bands[IEEE80211_BAND_5GHZ];
+
+ hw->wiphy->hw_version = priv->trans->hw_id;
+
+ iwl_leds_init(priv);
+
+ ret = ieee80211_register_hw(priv->hw);
+ if (ret) {
+ IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
+ iwl_leds_exit(priv);
+ return ret;
+ }
+ priv->mac80211_registered = 1;
+
+ return 0;
+}
+
+void iwlagn_mac_unregister(struct iwl_priv *priv)
+{
+ if (!priv->mac80211_registered)
+ return;
+ iwl_leds_exit(priv);
+ ieee80211_unregister_hw(priv->hw);
+ priv->mac80211_registered = 0;
+}
+
+static int __iwl_up(struct iwl_priv *priv)
+{
+ struct iwl_rxon_context *ctx;
+ int ret;
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
+ IWL_WARN(priv, "Exit pending; will not bring the NIC up\n");
+ return -EIO;
+ }
+
+ for_each_context(priv, ctx) {
+ ret = iwlagn_alloc_bcast_station(priv, ctx);
+ if (ret) {
+ iwl_dealloc_bcast_stations(priv);
+ return ret;
+ }
+ }
+
+ ret = iwl_trans_start_hw(priv->trans);
+ if (ret) {
+ IWL_ERR(priv, "Failed to start HW: %d\n", ret);
+ goto error;
+ }
+
+ ret = iwl_run_init_ucode(priv);
+ if (ret) {
+ IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret);
+ goto error;
+ }
+
+ ret = iwl_trans_start_hw(priv->trans);
+ if (ret) {
+ IWL_ERR(priv, "Failed to start HW: %d\n", ret);
+ goto error;
+ }
+
+ ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR);
+ if (ret) {
+ IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret);
+ goto error;
+ }
+
+ ret = iwl_alive_start(priv);
+ if (ret)
+ goto error;
+ return 0;
+
+ error:
+ set_bit(STATUS_EXIT_PENDING, &priv->status);
+ iwl_down(priv);
+ clear_bit(STATUS_EXIT_PENDING, &priv->status);
+
+ IWL_ERR(priv, "Unable to initialize device.\n");
+ return ret;
+}
+
+static int iwlagn_mac_start(struct ieee80211_hw *hw)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ int ret;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ /* we should be verifying the device is ready to be opened */
+ mutex_lock(&priv->mutex);
+ ret = __iwl_up(priv);
+ mutex_unlock(&priv->mutex);
+ if (ret)
+ return ret;
+
+ IWL_DEBUG_INFO(priv, "Start UP work done.\n");
+
+ /* Now we should be done, and the READY bit should be set. */
+ if (WARN_ON(!test_bit(STATUS_READY, &priv->status)))
+ ret = -EIO;
+
+ iwlagn_led_enable(priv);
+
+ priv->is_open = 1;
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+ return 0;
+}
+
+static void iwlagn_mac_stop(struct ieee80211_hw *hw)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ if (!priv->is_open)
+ return;
+
+ priv->is_open = 0;
+
+ mutex_lock(&priv->mutex);
+ iwl_down(priv);
+ mutex_unlock(&priv->mutex);
+
+ iwl_cancel_deferred_work(priv);
+
+ flush_workqueue(priv->workqueue);
+
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ if (iwlwifi_mod_params.sw_crypto)
+ return;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->mutex);
+
+ if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif)
+ goto out;
+
+ memcpy(priv->kek, data->kek, NL80211_KEK_LEN);
+ memcpy(priv->kck, data->kck, NL80211_KCK_LEN);
+ priv->replay_ctr =
+ cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
+ priv->have_rekey_data = true;
+
+ out:
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ int ret;
+
+ if (WARN_ON(!wowlan))
+ return -EINVAL;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->mutex);
+
+ /* Don't attempt WoWLAN when not associated, tear down instead. */
+ if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION ||
+ !iwl_is_associated_ctx(ctx)) {
+ ret = 1;
+ goto out;
+ }
+
+ ret = iwlagn_suspend(priv, wowlan);
+ if (ret)
+ goto error;
+
+ /* let the ucode operate on its own */
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
+ iwl_trans_d3_suspend(priv->trans, false);
+
+ goto out;
+
+ error:
+ priv->wowlan = false;
+ iwlagn_prepare_restart(priv);
+ ieee80211_restart_hw(priv->hw);
+ out:
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return ret;
+}
+
+struct iwl_resume_data {
+ struct iwl_priv *priv;
+ struct iwlagn_wowlan_status *cmd;
+ bool valid;
+};
+
+static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_resume_data *resume_data = data;
+ struct iwl_priv *priv = resume_data->priv;
+
+ if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) {
+ IWL_ERR(priv, "rx wrong size data\n");
+ return true;
+ }
+ memcpy(resume_data->cmd, pkt->data, sizeof(*resume_data->cmd));
+ resume_data->valid = true;
+
+ return true;
+}
+
+static int iwlagn_mac_resume(struct ieee80211_hw *hw)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct ieee80211_vif *vif;
+ u32 base;
+ int ret;
+ enum iwl_d3_status d3_status;
+ struct error_table_start {
+ /* cf. struct iwl_error_event_table */
+ u32 valid;
+ u32 error_id;
+ } err_info;
+ struct iwl_notification_wait status_wait;
+ static const u16 status_cmd[] = {
+ REPLY_WOWLAN_GET_STATUS,
+ };
+ struct iwlagn_wowlan_status status_data = {};
+ struct iwl_resume_data resume_data = {
+ .priv = priv,
+ .cmd = &status_data,
+ .valid = false,
+ };
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .pattern_idx = -1,
+ };
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ const struct fw_img *img;
+#endif
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->mutex);
+
+ /* we'll clear ctx->vif during iwlagn_prepare_restart() */
+ vif = ctx->vif;
+
+ ret = iwl_trans_d3_resume(priv->trans, &d3_status, false);
+ if (ret)
+ goto out_unlock;
+
+ if (d3_status != IWL_D3_STATUS_ALIVE) {
+ IWL_INFO(priv, "Device was reset during suspend\n");
+ goto out_unlock;
+ }
+
+ /* uCode is no longer operating by itself */
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
+ base = priv->device_pointers.error_event_table;
+ if (!iwlagn_hw_valid_rtc_data_addr(base)) {
+ IWL_WARN(priv, "Invalid error table during resume!\n");
+ goto out_unlock;
+ }
+
+ iwl_trans_read_mem_bytes(priv->trans, base,
+ &err_info, sizeof(err_info));
+
+ if (err_info.valid) {
+ IWL_INFO(priv, "error table is valid (%d, 0x%x)\n",
+ err_info.valid, err_info.error_id);
+ if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
+ wakeup.rfkill_release = true;
+ ieee80211_report_wowlan_wakeup(vif, &wakeup,
+ GFP_KERNEL);
+ }
+ goto out_unlock;
+ }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ img = &priv->fw->img[IWL_UCODE_WOWLAN];
+ if (!priv->wowlan_sram)
+ priv->wowlan_sram =
+ kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len,
+ GFP_KERNEL);
+
+ if (priv->wowlan_sram)
+ iwl_trans_read_mem(priv->trans, 0x800000,
+ priv->wowlan_sram,
+ img->sec[IWL_UCODE_SECTION_DATA].len / 4);
+#endif
+
+ /*
+ * This is very strange. The GET_STATUS command is sent but the device
+ * doesn't reply properly, it seems it doesn't close the RBD so one is
+ * always left open ... As a result, we need to send another command
+ * and have to reset the driver afterwards. As we need to switch to
+ * runtime firmware again that'll happen.
+ */
+
+ iwl_init_notification_wait(&priv->notif_wait, &status_wait, status_cmd,
+ ARRAY_SIZE(status_cmd), iwl_resume_status_fn,
+ &resume_data);
+
+ iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_GET_STATUS, CMD_ASYNC, 0, NULL);
+ iwl_dvm_send_cmd_pdu(priv, REPLY_ECHO, CMD_ASYNC, 0, NULL);
+ /* an RBD is left open in the firmware now! */
+
+ ret = iwl_wait_notification(&priv->notif_wait, &status_wait, HZ/5);
+ if (ret)
+ goto out_unlock;
+
+ if (resume_data.valid && priv->contexts[IWL_RXON_CTX_BSS].vif) {
+ u32 reasons = le32_to_cpu(status_data.wakeup_reason);
+ struct cfg80211_wowlan_wakeup *wakeup_report;
+
+ IWL_INFO(priv, "WoWLAN wakeup reason(s): 0x%.8x\n", reasons);
+
+ if (reasons) {
+ if (reasons & IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET)
+ wakeup.magic_pkt = true;
+ if (reasons & IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH)
+ wakeup.pattern_idx = status_data.pattern_number;
+ if (reasons & (IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
+ IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE))
+ wakeup.disconnect = true;
+ if (reasons & IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL)
+ wakeup.gtk_rekey_failure = true;
+ if (reasons & IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ)
+ wakeup.eap_identity_req = true;
+ if (reasons & IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE)
+ wakeup.four_way_handshake = true;
+ wakeup_report = &wakeup;
+ } else {
+ wakeup_report = NULL;
+ }
+
+ ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+ }
+
+ priv->wowlan = false;
+
+ iwlagn_prepare_restart(priv);
+
+ memset((void *)&ctx->active, 0, sizeof(ctx->active));
+ iwl_connection_init_rx_config(priv, ctx);
+ iwlagn_set_rxon_chain(priv, ctx);
+
+ out_unlock:
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ ieee80211_resume_disconnect(vif);
+
+ return 1;
+}
+
+static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ device_set_wakeup_enable(priv->trans->dev, enabled);
+}
+#endif
+
+static void iwlagn_mac_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ if (iwlagn_tx_skb(priv, control->sta, skb))
+ ieee80211_free_txskb(hw, skb);
+}
+
+static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta,
+ u32 iv32, u16 *phase1key)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key);
+}
+
+static int iwlagn_mac_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 iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ struct iwl_rxon_context *ctx = vif_priv->ctx;
+ int ret;
+ bool is_default_wep_key = false;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ if (iwlwifi_mod_params.sw_crypto) {
+ IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n");
+ return -EOPNOTSUPP;
+ }
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ /* fall through */
+ case WLAN_CIPHER_SUITE_CCMP:
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * We could program these keys into the hardware as well, but we
+ * don't expect much multicast traffic in IBSS and having keys
+ * for more stations is probably more useful.
+ *
+ * Mark key TX-only and return 0.
+ */
+ if (vif->type == NL80211_IFTYPE_ADHOC &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ key->hw_key_idx = WEP_INVALID_OFFSET;
+ return 0;
+ }
+
+ /* If they key was TX-only, accept deletion */
+ if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET)
+ return 0;
+
+ mutex_lock(&priv->mutex);
+ iwl_scan_cancel_timeout(priv, 100);
+
+ BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT);
+
+ /*
+ * If we are getting WEP group key and we didn't receive any key mapping
+ * so far, we are in legacy wep mode (group key only), otherwise we are
+ * in 1X mode.
+ * In legacy wep mode, we use another host command to the uCode.
+ */
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) {
+ if (cmd == SET_KEY)
+ is_default_wep_key = !ctx->key_mapping_keys;
+ else
+ is_default_wep_key =
+ key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT;
+ }
+
+
+ switch (cmd) {
+ case SET_KEY:
+ if (is_default_wep_key) {
+ ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key);
+ break;
+ }
+ ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta);
+ if (ret) {
+ /*
+ * can't add key for RX, but we don't need it
+ * in the device for TX so still return 0
+ */
+ ret = 0;
+ key->hw_key_idx = WEP_INVALID_OFFSET;
+ }
+
+ IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n");
+ break;
+ case DISABLE_KEY:
+ if (is_default_wep_key)
+ ret = iwl_remove_default_wep_key(priv, ctx, key);
+ else
+ ret = iwl_remove_dynamic_key(priv, ctx, key, sta);
+
+ IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n");
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return ret;
+}
+
+static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg)
+{
+ if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
+ return false;
+ return true;
+}
+
+static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
+{
+ if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
+ return false;
+ if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
+ return true;
+
+ /* disabled by default */
+ return false;
+}
+
+static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size, bool amsdu)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ int ret = -EINVAL;
+ struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
+
+ IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
+ sta->addr, tid);
+
+ if (!(priv->nvm_data->sku_cap_11n_enable))
+ return -EACCES;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+ mutex_lock(&priv->mutex);
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ if (!iwl_enable_rx_ampdu(priv->cfg))
+ break;
+ IWL_DEBUG_HT(priv, "start Rx\n");
+ ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ IWL_DEBUG_HT(priv, "stop Rx\n");
+ ret = iwl_sta_rx_agg_stop(priv, sta, tid);
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ if (!priv->trans->ops->txq_enable)
+ break;
+ if (!iwl_enable_tx_ampdu(priv->cfg))
+ break;
+ IWL_DEBUG_HT(priv, "start Tx\n");
+ ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ IWL_DEBUG_HT(priv, "Flush Tx\n");
+ ret = iwlagn_tx_agg_flush(priv, vif, sta, tid);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ IWL_DEBUG_HT(priv, "stop Tx\n");
+ ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
+ if ((ret == 0) && (priv->agg_tids_count > 0)) {
+ priv->agg_tids_count--;
+ IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n",
+ priv->agg_tids_count);
+ }
+ if (!priv->agg_tids_count &&
+ priv->hw_params.use_rts_for_aggregation) {
+ /*
+ * switch off RTS/CTS if it was previously enabled
+ */
+ sta_priv->lq_sta.lq.general_params.flags &=
+ ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
+ iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif),
+ &sta_priv->lq_sta.lq, CMD_ASYNC, false);
+ }
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size);
+ break;
+ }
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+ return ret;
+}
+
+static int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ bool is_ap = vif->type == NL80211_IFTYPE_STATION;
+ int ret;
+ u8 sta_id;
+
+ IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n",
+ sta->addr);
+ sta_priv->sta_id = IWL_INVALID_STATION;
+
+ atomic_set(&sta_priv->pending_frames, 0);
+ if (vif->type == NL80211_IFTYPE_AP)
+ sta_priv->client = true;
+
+ ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr,
+ is_ap, sta, &sta_id);
+ if (ret) {
+ IWL_ERR(priv, "Unable to add station %pM (%d)\n",
+ sta->addr, ret);
+ /* Should we return success if return code is EEXIST ? */
+ return ret;
+ }
+
+ sta_priv->sta_id = sta_id;
+
+ return 0;
+}
+
+static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ int ret;
+
+ IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr);
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ /*
+ * Station will be removed from device when the RXON
+ * is set to unassociated -- just deactivate it here
+ * to avoid re-programming it.
+ */
+ ret = 0;
+ iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr);
+ } else {
+ ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr);
+ if (ret)
+ IWL_DEBUG_QUIET_RFKILL(priv,
+ "Error removing station %pM\n", sta->addr);
+ }
+ return ret;
+}
+
+static int iwlagn_mac_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 iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ enum {
+ NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT,
+ } op = NONE;
+ int ret;
+
+ IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n",
+ sta->addr, old_state, new_state);
+
+ mutex_lock(&priv->mutex);
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE)
+ op = ADD;
+ else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST)
+ op = REMOVE;
+ else if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC)
+ op = HT_RATE_INIT;
+ } else {
+ if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC)
+ op = ADD_RATE_INIT;
+ else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTH)
+ op = REMOVE;
+ }
+
+ switch (op) {
+ case ADD:
+ ret = iwlagn_mac_sta_add(hw, vif, sta);
+ if (ret)
+ break;
+ /*
+ * Clear the in-progress flag, the AP station entry was added
+ * but we'll initialize LQ only when we've associated (which
+ * would also clear the in-progress flag). This is necessary
+ * in case we never initialize LQ because association fails.
+ */
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[iwl_sta_id(sta)].used &=
+ ~IWL_STA_UCODE_INPROGRESS;
+ spin_unlock_bh(&priv->sta_lock);
+ break;
+ case REMOVE:
+ ret = iwlagn_mac_sta_remove(hw, vif, sta);
+ break;
+ case ADD_RATE_INIT:
+ ret = iwlagn_mac_sta_add(hw, vif, sta);
+ if (ret)
+ break;
+ /* Initialize rate scaling */
+ IWL_DEBUG_INFO(priv,
+ "Initializing rate scaling for station %pM\n",
+ sta->addr);
+ iwl_rs_rate_init(priv, sta, iwl_sta_id(sta));
+ ret = 0;
+ break;
+ case HT_RATE_INIT:
+ /* Initialize rate scaling */
+ ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta);
+ if (ret)
+ break;
+ IWL_DEBUG_INFO(priv,
+ "Initializing rate scaling for station %pM\n",
+ sta->addr);
+ iwl_rs_rate_init(priv, sta, iwl_sta_id(sta));
+ ret = 0;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ /*
+ * mac80211 might WARN if we fail, but due the way we
+ * (badly) handle hard rfkill, we might fail here
+ */
+ if (iwl_is_rfkill(priv))
+ ret = 0;
+
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return ret;
+}
+
+static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct ieee80211_conf *conf = &hw->conf;
+ struct ieee80211_channel *channel = ch_switch->chandef.chan;
+ struct iwl_ht_config *ht_conf = &priv->current_ht_config;
+ /*
+ * MULTI-FIXME
+ * When we add support for multiple interfaces, we need to
+ * revisit this. The channel switch command in the device
+ * only affects the BSS context, but what does that really
+ * mean? And what if we get a CSA on the second interface?
+ * This needs a lot of work.
+ */
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ u16 ch;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ mutex_lock(&priv->mutex);
+
+ if (iwl_is_rfkill(priv))
+ goto out;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
+ test_bit(STATUS_SCANNING, &priv->status) ||
+ test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+ goto out;
+
+ if (!iwl_is_associated_ctx(ctx))
+ goto out;
+
+ if (!priv->lib->set_channel_switch)
+ goto out;
+
+ ch = channel->hw_value;
+ if (le16_to_cpu(ctx->active.channel) == ch)
+ goto out;
+
+ priv->current_ht_config.smps = conf->smps_mode;
+
+ /* Configure HT40 channels */
+ switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ ctx->ht.is_40mhz = false;
+ ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ ctx->ht.is_40mhz = true;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ctx->ht.is_40mhz = true;
+ break;
+ }
+
+ if ((le16_to_cpu(ctx->staging.channel) != ch))
+ ctx->staging.flags = 0;
+
+ iwl_set_rxon_channel(priv, channel, ctx);
+ iwl_set_rxon_ht(priv, ht_conf);
+ iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif);
+
+ /*
+ * at this point, staging_rxon has the
+ * configuration for channel switch
+ */
+ set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
+ priv->switch_channel = cpu_to_le16(ch);
+ if (priv->lib->set_channel_switch(priv, ch_switch)) {
+ clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
+ priv->switch_channel = 0;
+ ieee80211_chswitch_done(ctx->vif, false);
+ }
+
+out:
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+void iwl_chswitch_done(struct iwl_priv *priv, bool is_success)
+{
+ /*
+ * MULTI-FIXME
+ * See iwlagn_mac_channel_switch.
+ */
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+ return;
+
+ if (ctx->vif)
+ ieee80211_chswitch_done(ctx->vif, is_success);
+}
+
+static void iwlagn_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ __le32 filter_or = 0, filter_nand = 0;
+ struct iwl_rxon_context *ctx;
+
+#define CHK(test, flag) do { \
+ if (*total_flags & (test)) \
+ filter_or |= (flag); \
+ else \
+ filter_nand |= (flag); \
+ } while (0)
+
+ IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n",
+ changed_flags, *total_flags);
+
+ CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK);
+ /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */
+ CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK);
+ CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK);
+
+#undef CHK
+
+ mutex_lock(&priv->mutex);
+
+ for_each_context(priv, ctx) {
+ ctx->staging.filter_flags &= ~filter_nand;
+ ctx->staging.filter_flags |= filter_or;
+
+ /*
+ * Not committing directly because hardware can perform a scan,
+ * but we'll eventually commit the filter flags change anyway.
+ */
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ /*
+ * Receiving all multicast frames is always enabled by the
+ * default flags setup in iwl_connection_init_rx_config()
+ * since we currently do not support programming multicast
+ * filters into the device.
+ */
+ *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI |
+ FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
+}
+
+static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ u32 scd_queues;
+
+ mutex_lock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
+ IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n");
+ goto done;
+ }
+ if (iwl_is_rfkill(priv)) {
+ IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n");
+ goto done;
+ }
+
+ scd_queues = BIT(priv->cfg->base_params->num_of_queues) - 1;
+ scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) |
+ BIT(IWL_DEFAULT_CMD_QUEUE_NUM));
+
+ if (drop) {
+ IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n",
+ scd_queues);
+ if (iwlagn_txfifo_flush(priv, scd_queues)) {
+ IWL_ERR(priv, "flush request fail\n");
+ goto done;
+ }
+ }
+
+ IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n");
+ iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues);
+done:
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+static void iwlagn_mac_event_callback(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ if (event->type != RSSI_EVENT)
+ return;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
+ if (event->u.rssi.data == RSSI_EVENT_LOW)
+ priv->bt_enable_pspoll = true;
+ else if (event->u.rssi.data == RSSI_EVENT_HIGH)
+ priv->bt_enable_pspoll = false;
+
+ queue_work(priv->workqueue, &priv->bt_runtime_config);
+ } else {
+ IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled,"
+ "ignoring RSSI callback\n");
+ }
+
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+static int iwlagn_mac_set_tim(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, bool set)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ queue_work(priv->workqueue, &priv->beacon_update);
+
+ return 0;
+}
+
+static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ struct iwl_rxon_context *ctx = vif_priv->ctx;
+ int q;
+
+ if (WARN_ON(!ctx))
+ return -EINVAL;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ if (!iwl_is_ready_rf(priv)) {
+ IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
+ return -EIO;
+ }
+
+ if (queue >= AC_NUM) {
+ IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue);
+ return 0;
+ }
+
+ q = AC_NUM - 1 - queue;
+
+ mutex_lock(&priv->mutex);
+
+ ctx->qos_data.def_qos_parm.ac[q].cw_min =
+ cpu_to_le16(params->cw_min);
+ ctx->qos_data.def_qos_parm.ac[q].cw_max =
+ cpu_to_le16(params->cw_max);
+ ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs;
+ ctx->qos_data.def_qos_parm.ac[q].edca_txop =
+ cpu_to_le16((params->txop * 32));
+
+ ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0;
+
+ mutex_unlock(&priv->mutex);
+
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+ return 0;
+}
+
+static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+
+ return priv->ibss_manager == IWL_IBSS_MANAGER;
+}
+
+static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
+{
+ iwl_connection_init_rx_config(priv, ctx);
+
+ iwlagn_set_rxon_chain(priv, ctx);
+
+ return iwlagn_commit_rxon(priv, ctx);
+}
+
+static int iwl_setup_interface(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ struct ieee80211_vif *vif = ctx->vif;
+ int err, ac;
+
+ lockdep_assert_held(&priv->mutex);
+
+ /*
+ * This variable will be correct only when there's just
+ * a single context, but all code using it is for hardware
+ * that supports only one context.
+ */
+ priv->iw_mode = vif->type;
+
+ ctx->is_active = true;
+
+ err = iwl_set_mode(priv, ctx);
+ if (err) {
+ if (!ctx->always_active)
+ ctx->is_active = false;
+ return err;
+ }
+
+ if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist &&
+ vif->type == NL80211_IFTYPE_ADHOC) {
+ /*
+ * pretend to have high BT traffic as long as we
+ * are operating in IBSS mode, as this will cause
+ * the rate scaling etc. to behave as intended.
+ */
+ priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
+ }
+
+ /* set up queue mappings */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ vif->hw_queue[ac] = ctx->ac_to_queue[ac];
+
+ if (vif->type == NL80211_IFTYPE_AP)
+ vif->cab_queue = ctx->mcast_queue;
+ else
+ vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+ return 0;
+}
+
+static int iwlagn_mac_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+ struct iwl_rxon_context *tmp, *ctx = NULL;
+ int err;
+ enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif);
+ bool reset = false;
+
+ IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
+ viftype, vif->addr);
+
+ mutex_lock(&priv->mutex);
+
+ if (!iwl_is_ready_rf(priv)) {
+ IWL_WARN(priv, "Try to add interface when device not ready\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ for_each_context(priv, tmp) {
+ u32 possible_modes =
+ tmp->interface_modes | tmp->exclusive_interface_modes;
+
+ if (tmp->vif) {
+ /* On reset we need to add the same interface again */
+ if (tmp->vif == vif) {
+ reset = true;
+ ctx = tmp;
+ break;
+ }
+
+ /* check if this busy context is exclusive */
+ if (tmp->exclusive_interface_modes &
+ BIT(tmp->vif->type)) {
+ err = -EINVAL;
+ goto out;
+ }
+ continue;
+ }
+
+ if (!(possible_modes & BIT(viftype)))
+ continue;
+
+ /* have maybe usable context w/o interface */
+ ctx = tmp;
+ break;
+ }
+
+ if (!ctx) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ vif_priv->ctx = ctx;
+ ctx->vif = vif;
+
+ /*
+ * In SNIFFER device type, the firmware reports the FCS to
+ * the host, rather than snipping it off. Unfortunately,
+ * mac80211 doesn't (yet) provide a per-packet flag for
+ * this, so that we have to set the hardware flag based
+ * on the interfaces added. As the monitor interface can
+ * only be present by itself, and will be removed before
+ * other interfaces are added, this is safe.
+ */
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+ ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS);
+ else
+ __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, priv->hw->flags);
+
+ err = iwl_setup_interface(priv, ctx);
+ if (!err || reset)
+ goto out;
+
+ ctx->vif = NULL;
+ priv->iw_mode = NL80211_IFTYPE_STATION;
+ out:
+ mutex_unlock(&priv->mutex);
+
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+ return err;
+}
+
+static void iwl_teardown_interface(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ bool mode_change)
+{
+ struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (priv->scan_vif == vif) {
+ iwl_scan_cancel_timeout(priv, 200);
+ iwl_force_scan_end(priv);
+ }
+
+ if (!mode_change) {
+ iwl_set_mode(priv, ctx);
+ if (!ctx->always_active)
+ ctx->is_active = false;
+ }
+
+ /*
+ * When removing the IBSS interface, overwrite the
+ * BT traffic load with the stored one from the last
+ * notification, if any. If this is a device that
+ * doesn't implement this, this has no effect since
+ * both values are the same and zero.
+ */
+ if (vif->type == NL80211_IFTYPE_ADHOC)
+ priv->bt_traffic_load = priv->last_bt_traffic_load;
+}
+
+static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ mutex_lock(&priv->mutex);
+
+ WARN_ON(ctx->vif != vif);
+ ctx->vif = NULL;
+
+ iwl_teardown_interface(priv, vif, false);
+
+ mutex_unlock(&priv->mutex);
+
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+}
+
+static int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype newtype, bool newp2p)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_rxon_context *ctx, *tmp;
+ enum nl80211_iftype newviftype = newtype;
+ u32 interface_modes;
+ int err;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ newtype = ieee80211_iftype_p2p(newtype, newp2p);
+
+ mutex_lock(&priv->mutex);
+
+ ctx = iwl_rxon_ctx_from_vif(vif);
+
+ /*
+ * To simplify this code, only support changes on the
+ * BSS context. The PAN context is usually reassigned
+ * by creating/removing P2P interfaces anyway.
+ */
+ if (ctx->ctxid != IWL_RXON_CTX_BSS) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (!ctx->vif || !iwl_is_ready_rf(priv)) {
+ /*
+ * Huh? But wait ... this can maybe happen when
+ * we're in the middle of a firmware restart!
+ */
+ err = -EBUSY;
+ goto out;
+ }
+
+ /* Check if the switch is supported in the same context */
+ interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes;
+ if (!(interface_modes & BIT(newtype))) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (ctx->exclusive_interface_modes & BIT(newtype)) {
+ for_each_context(priv, tmp) {
+ if (ctx == tmp)
+ continue;
+
+ if (!tmp->is_active)
+ continue;
+
+ /*
+ * The current mode switch would be exclusive, but
+ * another context is active ... refuse the switch.
+ */
+ err = -EBUSY;
+ goto out;
+ }
+ }
+
+ /* success */
+ iwl_teardown_interface(priv, vif, true);
+ vif->type = newviftype;
+ vif->p2p = newp2p;
+ err = iwl_setup_interface(priv, ctx);
+ WARN_ON(err);
+ /*
+ * We've switched internally, but submitting to the
+ * device may have failed for some reason. Mask this
+ * error, because otherwise mac80211 will not switch
+ * (and set the interface type back) and we'll be
+ * out of sync with it.
+ */
+ err = 0;
+
+ out:
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return err;
+}
+
+static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct cfg80211_scan_request *req = &hw_req->req;
+ int ret;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ if (req->n_channels == 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->mutex);
+
+ /*
+ * If an internal scan is in progress, just set
+ * up the scan_request as per above.
+ */
+ if (priv->scan_type != IWL_SCAN_NORMAL) {
+ IWL_DEBUG_SCAN(priv,
+ "SCAN request during internal scan - defer\n");
+ priv->scan_request = req;
+ priv->scan_vif = vif;
+ ret = 0;
+ } else {
+ priv->scan_request = req;
+ priv->scan_vif = vif;
+ /*
+ * mac80211 will only ask for one band at a time
+ * so using channels[0] here is ok
+ */
+ ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL,
+ req->channels[0]->band);
+ if (ret) {
+ priv->scan_request = NULL;
+ priv->scan_vif = NULL;
+ }
+ }
+
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+
+static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
+{
+ struct iwl_addsta_cmd cmd = {
+ .mode = STA_CONTROL_MODIFY_MSK,
+ .station_flags_msk = STA_FLG_PWR_SAVE_MSK,
+ .sta.sta_id = sta_id,
+ };
+
+ iwl_send_add_sta(priv, &cmd, CMD_ASYNC);
+}
+
+static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ int sta_id;
+
+ IWL_DEBUG_MAC80211(priv, "enter\n");
+
+ switch (cmd) {
+ case STA_NOTIFY_SLEEP:
+ WARN_ON(!sta_priv->client);
+ sta_priv->asleep = true;
+ if (atomic_read(&sta_priv->pending_frames) > 0)
+ ieee80211_sta_block_awake(hw, sta, true);
+ break;
+ case STA_NOTIFY_AWAKE:
+ WARN_ON(!sta_priv->client);
+ if (!sta_priv->asleep)
+ break;
+ sta_priv->asleep = false;
+ sta_id = iwl_sta_id(sta);
+ if (sta_id != IWL_INVALID_STATION)
+ iwl_sta_modify_ps_wake(priv, sta_id);
+ break;
+ default:
+ break;
+ }
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+}
+
+const struct ieee80211_ops iwlagn_hw_ops = {
+ .tx = iwlagn_mac_tx,
+ .start = iwlagn_mac_start,
+ .stop = iwlagn_mac_stop,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = iwlagn_mac_suspend,
+ .resume = iwlagn_mac_resume,
+ .set_wakeup = iwlagn_mac_set_wakeup,
+#endif
+ .add_interface = iwlagn_mac_add_interface,
+ .remove_interface = iwlagn_mac_remove_interface,
+ .change_interface = iwlagn_mac_change_interface,
+ .config = iwlagn_mac_config,
+ .configure_filter = iwlagn_configure_filter,
+ .set_key = iwlagn_mac_set_key,
+ .update_tkip_key = iwlagn_mac_update_tkip_key,
+ .set_rekey_data = iwlagn_mac_set_rekey_data,
+ .conf_tx = iwlagn_mac_conf_tx,
+ .bss_info_changed = iwlagn_bss_info_changed,
+ .ampdu_action = iwlagn_mac_ampdu_action,
+ .hw_scan = iwlagn_mac_hw_scan,
+ .sta_notify = iwlagn_mac_sta_notify,
+ .sta_state = iwlagn_mac_sta_state,
+ .channel_switch = iwlagn_mac_channel_switch,
+ .flush = iwlagn_mac_flush,
+ .tx_last_beacon = iwlagn_mac_tx_last_beacon,
+ .event_callback = iwlagn_mac_event_callback,
+ .set_tim = iwlagn_mac_set_tim,
+};
+
+/* This function both allocates and initializes hw and priv. */
+struct ieee80211_hw *iwl_alloc_all(void)
+{
+ struct iwl_priv *priv;
+ struct iwl_op_mode *op_mode;
+ /* mac80211 allocates memory for this device instance, including
+ * space for this driver's private structure */
+ struct ieee80211_hw *hw;
+
+ hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) +
+ sizeof(struct iwl_op_mode), &iwlagn_hw_ops);
+ if (!hw)
+ goto out;
+
+ op_mode = hw->priv;
+ priv = IWL_OP_MODE_GET_DVM(op_mode);
+ priv->hw = hw;
+
+out:
+ return hw;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
new file mode 100644
index 000000000000..f62c2d727ddb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
@@ -0,0 +1,2183 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+
+#include <net/mac80211.h>
+
+#include <asm/div64.h>
+
+#include "iwl-eeprom-read.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-io.h"
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-drv.h"
+#include "iwl-modparams.h"
+#include "iwl-prph.h"
+
+#include "dev.h"
+#include "calib.h"
+#include "agn.h"
+
+
+/******************************************************************************
+ *
+ * module boiler plate
+ *
+ ******************************************************************************/
+
+#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux"
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search.
+ * A warning will be triggered on violation.
+ */
+static const struct iwl_hcmd_names iwl_dvm_cmd_names[] = {
+ HCMD_NAME(REPLY_ALIVE),
+ HCMD_NAME(REPLY_ERROR),
+ HCMD_NAME(REPLY_ECHO),
+ HCMD_NAME(REPLY_RXON),
+ HCMD_NAME(REPLY_RXON_ASSOC),
+ HCMD_NAME(REPLY_QOS_PARAM),
+ HCMD_NAME(REPLY_RXON_TIMING),
+ HCMD_NAME(REPLY_ADD_STA),
+ HCMD_NAME(REPLY_REMOVE_STA),
+ HCMD_NAME(REPLY_REMOVE_ALL_STA),
+ HCMD_NAME(REPLY_TX),
+ HCMD_NAME(REPLY_TXFIFO_FLUSH),
+ HCMD_NAME(REPLY_WEPKEY),
+ HCMD_NAME(REPLY_LEDS_CMD),
+ HCMD_NAME(REPLY_TX_LINK_QUALITY_CMD),
+ HCMD_NAME(COEX_PRIORITY_TABLE_CMD),
+ HCMD_NAME(COEX_MEDIUM_NOTIFICATION),
+ HCMD_NAME(COEX_EVENT_CMD),
+ HCMD_NAME(TEMPERATURE_NOTIFICATION),
+ HCMD_NAME(CALIBRATION_CFG_CMD),
+ HCMD_NAME(CALIBRATION_RES_NOTIFICATION),
+ HCMD_NAME(CALIBRATION_COMPLETE_NOTIFICATION),
+ HCMD_NAME(REPLY_QUIET_CMD),
+ HCMD_NAME(REPLY_CHANNEL_SWITCH),
+ HCMD_NAME(CHANNEL_SWITCH_NOTIFICATION),
+ HCMD_NAME(REPLY_SPECTRUM_MEASUREMENT_CMD),
+ HCMD_NAME(SPECTRUM_MEASURE_NOTIFICATION),
+ HCMD_NAME(POWER_TABLE_CMD),
+ HCMD_NAME(PM_SLEEP_NOTIFICATION),
+ HCMD_NAME(PM_DEBUG_STATISTIC_NOTIFIC),
+ HCMD_NAME(REPLY_SCAN_CMD),
+ HCMD_NAME(REPLY_SCAN_ABORT_CMD),
+ HCMD_NAME(SCAN_START_NOTIFICATION),
+ HCMD_NAME(SCAN_RESULTS_NOTIFICATION),
+ HCMD_NAME(SCAN_COMPLETE_NOTIFICATION),
+ HCMD_NAME(BEACON_NOTIFICATION),
+ HCMD_NAME(REPLY_TX_BEACON),
+ HCMD_NAME(WHO_IS_AWAKE_NOTIFICATION),
+ HCMD_NAME(REPLY_TX_POWER_DBM_CMD),
+ HCMD_NAME(QUIET_NOTIFICATION),
+ HCMD_NAME(REPLY_TX_PWR_TABLE_CMD),
+ HCMD_NAME(REPLY_TX_POWER_DBM_CMD_V1),
+ HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
+ HCMD_NAME(MEASURE_ABORT_NOTIFICATION),
+ HCMD_NAME(REPLY_BT_CONFIG),
+ HCMD_NAME(REPLY_STATISTICS_CMD),
+ HCMD_NAME(STATISTICS_NOTIFICATION),
+ HCMD_NAME(REPLY_CARD_STATE_CMD),
+ HCMD_NAME(CARD_STATE_NOTIFICATION),
+ HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
+ HCMD_NAME(REPLY_CT_KILL_CONFIG_CMD),
+ HCMD_NAME(SENSITIVITY_CMD),
+ HCMD_NAME(REPLY_PHY_CALIBRATION_CMD),
+ HCMD_NAME(REPLY_WIPAN_PARAMS),
+ HCMD_NAME(REPLY_WIPAN_RXON),
+ HCMD_NAME(REPLY_WIPAN_RXON_TIMING),
+ HCMD_NAME(REPLY_WIPAN_RXON_ASSOC),
+ HCMD_NAME(REPLY_WIPAN_QOS_PARAM),
+ HCMD_NAME(REPLY_WIPAN_WEPKEY),
+ HCMD_NAME(REPLY_WIPAN_P2P_CHANNEL_SWITCH),
+ HCMD_NAME(REPLY_WIPAN_NOA_NOTIFICATION),
+ HCMD_NAME(REPLY_WIPAN_DEACTIVATION_COMPLETE),
+ HCMD_NAME(REPLY_RX_PHY_CMD),
+ HCMD_NAME(REPLY_RX_MPDU_CMD),
+ HCMD_NAME(REPLY_RX),
+ HCMD_NAME(REPLY_COMPRESSED_BA),
+ HCMD_NAME(REPLY_BT_COEX_PRIO_TABLE),
+ HCMD_NAME(REPLY_BT_COEX_PROT_ENV),
+ HCMD_NAME(REPLY_BT_COEX_PROFILE_NOTIF),
+ HCMD_NAME(REPLY_D3_CONFIG),
+ HCMD_NAME(REPLY_WOWLAN_PATTERNS),
+ HCMD_NAME(REPLY_WOWLAN_WAKEUP_FILTER),
+ HCMD_NAME(REPLY_WOWLAN_TSC_RSC_PARAMS),
+ HCMD_NAME(REPLY_WOWLAN_TKIP_PARAMS),
+ HCMD_NAME(REPLY_WOWLAN_KEK_KCK_MATERIAL),
+ HCMD_NAME(REPLY_WOWLAN_GET_STATUS),
+};
+
+static const struct iwl_hcmd_arr iwl_dvm_groups[] = {
+ [0x0] = HCMD_ARR(iwl_dvm_cmd_names),
+};
+
+static const struct iwl_op_mode_ops iwl_dvm_ops;
+
+void iwl_update_chain_flags(struct iwl_priv *priv)
+{
+ struct iwl_rxon_context *ctx;
+
+ for_each_context(priv, ctx) {
+ iwlagn_set_rxon_chain(priv, ctx);
+ if (ctx->active.rx_chain != ctx->staging.rx_chain)
+ iwlagn_commit_rxon(priv, ctx);
+ }
+}
+
+/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
+static void iwl_set_beacon_tim(struct iwl_priv *priv,
+ struct iwl_tx_beacon_cmd *tx_beacon_cmd,
+ u8 *beacon, u32 frame_size)
+{
+ u16 tim_idx;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
+
+ /*
+ * The index is relative to frame start but we start looking at the
+ * variable-length part of the beacon.
+ */
+ tim_idx = mgmt->u.beacon.variable - beacon;
+
+ /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+ while ((tim_idx < (frame_size - 2)) &&
+ (beacon[tim_idx] != WLAN_EID_TIM))
+ tim_idx += beacon[tim_idx+1] + 2;
+
+ /* If TIM field was found, set variables */
+ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
+ tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
+ tx_beacon_cmd->tim_size = beacon[tim_idx+1];
+ } else
+ IWL_WARN(priv, "Unable to find TIM Element in beacon\n");
+}
+
+int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
+{
+ struct iwl_tx_beacon_cmd *tx_beacon_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_TX_BEACON,
+ };
+ struct ieee80211_tx_info *info;
+ u32 frame_size;
+ u32 rate_flags;
+ u32 rate;
+
+ /*
+ * We have to set up the TX command, the TX Beacon command, and the
+ * beacon contents.
+ */
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (!priv->beacon_ctx) {
+ IWL_ERR(priv, "trying to build beacon w/o beacon context!\n");
+ return 0;
+ }
+
+ if (WARN_ON(!priv->beacon_skb))
+ return -EINVAL;
+
+ /* Allocate beacon command */
+ if (!priv->beacon_cmd)
+ priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL);
+ tx_beacon_cmd = priv->beacon_cmd;
+ if (!tx_beacon_cmd)
+ return -ENOMEM;
+
+ frame_size = priv->beacon_skb->len;
+
+ /* Set up TX command fields */
+ tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
+ tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id;
+ tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+ tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
+ TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK;
+
+ /* Set up TX beacon command fields */
+ iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data,
+ frame_size);
+
+ /* Set up packet rate and flags */
+ info = IEEE80211_SKB_CB(priv->beacon_skb);
+
+ /*
+ * Let's set up the rate at least somewhat correctly;
+ * it will currently not actually be used by the uCode,
+ * it uses the broadcast station's rate instead.
+ */
+ if (info->control.rates[0].idx < 0 ||
+ info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
+ rate = 0;
+ else
+ rate = info->control.rates[0].idx;
+
+ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
+ priv->nvm_data->valid_tx_ant);
+ rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
+
+ /* In mac80211, rates for 5 GHz start at 0 */
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate += IWL_FIRST_OFDM_RATE;
+ else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE)
+ rate_flags |= RATE_MCS_CCK_MSK;
+
+ tx_beacon_cmd->tx.rate_n_flags =
+ iwl_hw_set_rate_n_flags(rate, rate_flags);
+
+ /* Submit command */
+ cmd.len[0] = sizeof(*tx_beacon_cmd);
+ cmd.data[0] = tx_beacon_cmd;
+ cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+ cmd.len[1] = frame_size;
+ cmd.data[1] = priv->beacon_skb->data;
+ cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
+
+ return iwl_dvm_send_cmd(priv, &cmd);
+}
+
+static void iwl_bg_beacon_update(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, beacon_update);
+ struct sk_buff *beacon;
+
+ mutex_lock(&priv->mutex);
+ if (!priv->beacon_ctx) {
+ IWL_ERR(priv, "updating beacon w/o beacon context!\n");
+ goto out;
+ }
+
+ if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) {
+ /*
+ * The ucode will send beacon notifications even in
+ * IBSS mode, but we don't want to process them. But
+ * we need to defer the type check to here due to
+ * requiring locking around the beacon_ctx access.
+ */
+ goto out;
+ }
+
+ /* Pull updated AP beacon from mac80211. will fail if not in AP mode */
+ beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif);
+ if (!beacon) {
+ IWL_ERR(priv, "update beacon failed -- keeping old\n");
+ goto out;
+ }
+
+ /* new beacon skb is allocated every time; dispose previous.*/
+ dev_kfree_skb(priv->beacon_skb);
+
+ priv->beacon_skb = beacon;
+
+ iwlagn_send_beacon_cmd(priv);
+ out:
+ mutex_unlock(&priv->mutex);
+}
+
+static void iwl_bg_bt_runtime_config(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, bt_runtime_config);
+
+ mutex_lock(&priv->mutex);
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ goto out;
+
+ /* dont send host command if rf-kill is on */
+ if (!iwl_is_ready_rf(priv))
+ goto out;
+
+ iwlagn_send_advance_bt_config(priv);
+out:
+ mutex_unlock(&priv->mutex);
+}
+
+static void iwl_bg_bt_full_concurrency(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, bt_full_concurrency);
+ struct iwl_rxon_context *ctx;
+
+ mutex_lock(&priv->mutex);
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ goto out;
+
+ /* dont send host command if rf-kill is on */
+ if (!iwl_is_ready_rf(priv))
+ goto out;
+
+ IWL_DEBUG_INFO(priv, "BT coex in %s mode\n",
+ priv->bt_full_concurrent ?
+ "full concurrency" : "3-wire");
+
+ /*
+ * LQ & RXON updated cmds must be sent before BT Config cmd
+ * to avoid 3-wire collisions
+ */
+ for_each_context(priv, ctx) {
+ iwlagn_set_rxon_chain(priv, ctx);
+ iwlagn_commit_rxon(priv, ctx);
+ }
+
+ iwlagn_send_advance_bt_config(priv);
+out:
+ mutex_unlock(&priv->mutex);
+}
+
+int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
+{
+ struct iwl_statistics_cmd statistics_cmd = {
+ .configuration_flags =
+ clear ? IWL_STATS_CONF_CLEAR_STATS : 0,
+ };
+
+ if (flags & CMD_ASYNC)
+ return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
+ CMD_ASYNC,
+ sizeof(struct iwl_statistics_cmd),
+ &statistics_cmd);
+ else
+ return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0,
+ sizeof(struct iwl_statistics_cmd),
+ &statistics_cmd);
+}
+
+/**
+ * iwl_bg_statistics_periodic - Timer callback to queue statistics
+ *
+ * This callback is provided in order to send a statistics request.
+ *
+ * This timer function is continually reset to execute within
+ * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION
+ * was received. We need to ensure we receive the statistics in order
+ * to update the temperature used for calibrating the TXPOWER.
+ */
+static void iwl_bg_statistics_periodic(unsigned long data)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)data;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ /* dont send host command if rf-kill is on */
+ if (!iwl_is_ready_rf(priv))
+ return;
+
+ iwl_send_statistics_request(priv, CMD_ASYNC, false);
+}
+
+
+static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
+ u32 start_idx, u32 num_events,
+ u32 capacity, u32 mode)
+{
+ u32 i;
+ u32 ptr; /* SRAM byte address of log data */
+ u32 ev, time, data; /* event log data */
+ unsigned long reg_flags;
+
+ if (mode == 0)
+ ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32));
+ else
+ ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
+
+ /* Make sure device is powered up for SRAM reads */
+ if (!iwl_trans_grab_nic_access(priv->trans, &reg_flags))
+ return;
+
+ /* Set starting address; reads will auto-increment */
+ iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr);
+
+ /*
+ * Refuse to read more than would have fit into the log from
+ * the current start_idx. This used to happen due to the race
+ * described below, but now WARN because the code below should
+ * prevent it from happening here.
+ */
+ if (WARN_ON(num_events > capacity - start_idx))
+ num_events = capacity - start_idx;
+
+ /*
+ * "time" is actually "data" for mode 0 (no timestamp).
+ * place event id # at far right for easier visual parsing.
+ */
+ for (i = 0; i < num_events; i++) {
+ ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
+ time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
+ if (mode == 0) {
+ trace_iwlwifi_dev_ucode_cont_event(
+ priv->trans->dev, 0, time, ev);
+ } else {
+ data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
+ trace_iwlwifi_dev_ucode_cont_event(
+ priv->trans->dev, time, data, ev);
+ }
+ }
+ /* Allow device to power down */
+ iwl_trans_release_nic_access(priv->trans, &reg_flags);
+}
+
+static void iwl_continuous_event_trace(struct iwl_priv *priv)
+{
+ u32 capacity; /* event log capacity in # entries */
+ struct {
+ u32 capacity;
+ u32 mode;
+ u32 wrap_counter;
+ u32 write_counter;
+ } __packed read;
+ u32 base; /* SRAM byte address of event log header */
+ u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
+ u32 num_wraps; /* # times uCode wrapped to top of log */
+ u32 next_entry; /* index of next entry to be written by uCode */
+
+ base = priv->device_pointers.log_event_table;
+ if (iwlagn_hw_valid_rtc_data_addr(base)) {
+ iwl_trans_read_mem_bytes(priv->trans, base,
+ &read, sizeof(read));
+ capacity = read.capacity;
+ mode = read.mode;
+ num_wraps = read.wrap_counter;
+ next_entry = read.write_counter;
+ } else
+ return;
+
+ /*
+ * Unfortunately, the uCode doesn't use temporary variables.
+ * Therefore, it can happen that we read next_entry == capacity,
+ * which really means next_entry == 0.
+ */
+ if (unlikely(next_entry == capacity))
+ next_entry = 0;
+ /*
+ * Additionally, the uCode increases the write pointer before
+ * the wraps counter, so if the write pointer is smaller than
+ * the old write pointer (wrap occurred) but we read that no
+ * wrap occurred, we actually read between the next_entry and
+ * num_wraps update (this does happen in practice!!) -- take
+ * that into account by increasing num_wraps.
+ */
+ if (unlikely(next_entry < priv->event_log.next_entry &&
+ num_wraps == priv->event_log.num_wraps))
+ num_wraps++;
+
+ if (num_wraps == priv->event_log.num_wraps) {
+ iwl_print_cont_event_trace(
+ priv, base, priv->event_log.next_entry,
+ next_entry - priv->event_log.next_entry,
+ capacity, mode);
+
+ priv->event_log.non_wraps_count++;
+ } else {
+ if (num_wraps - priv->event_log.num_wraps > 1)
+ priv->event_log.wraps_more_count++;
+ else
+ priv->event_log.wraps_once_count++;
+
+ trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev,
+ num_wraps - priv->event_log.num_wraps,
+ next_entry, priv->event_log.next_entry);
+
+ if (next_entry < priv->event_log.next_entry) {
+ iwl_print_cont_event_trace(
+ priv, base, priv->event_log.next_entry,
+ capacity - priv->event_log.next_entry,
+ capacity, mode);
+
+ iwl_print_cont_event_trace(
+ priv, base, 0, next_entry, capacity, mode);
+ } else {
+ iwl_print_cont_event_trace(
+ priv, base, next_entry,
+ capacity - next_entry,
+ capacity, mode);
+
+ iwl_print_cont_event_trace(
+ priv, base, 0, next_entry, capacity, mode);
+ }
+ }
+
+ priv->event_log.num_wraps = num_wraps;
+ priv->event_log.next_entry = next_entry;
+}
+
+/**
+ * iwl_bg_ucode_trace - Timer callback to log ucode event
+ *
+ * The timer is continually set to execute every
+ * UCODE_TRACE_PERIOD milliseconds after the last timer expired
+ * this function is to perform continuous uCode event logging operation
+ * if enabled
+ */
+static void iwl_bg_ucode_trace(unsigned long data)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)data;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ if (priv->event_log.ucode_trace) {
+ iwl_continuous_event_trace(priv);
+ /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */
+ mod_timer(&priv->ucode_trace,
+ jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
+ }
+}
+
+static void iwl_bg_tx_flush(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, tx_flush);
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ /* do nothing if rf-kill is on */
+ if (!iwl_is_ready_rf(priv))
+ return;
+
+ IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n");
+ iwlagn_dev_txfifo_flush(priv);
+}
+
+/*
+ * queue/FIFO/AC mapping definitions
+ */
+
+static const u8 iwlagn_bss_ac_to_fifo[] = {
+ IWL_TX_FIFO_VO,
+ IWL_TX_FIFO_VI,
+ IWL_TX_FIFO_BE,
+ IWL_TX_FIFO_BK,
+};
+
+static const u8 iwlagn_bss_ac_to_queue[] = {
+ 0, 1, 2, 3,
+};
+
+static const u8 iwlagn_pan_ac_to_fifo[] = {
+ IWL_TX_FIFO_VO_IPAN,
+ IWL_TX_FIFO_VI_IPAN,
+ IWL_TX_FIFO_BE_IPAN,
+ IWL_TX_FIFO_BK_IPAN,
+};
+
+static const u8 iwlagn_pan_ac_to_queue[] = {
+ 7, 6, 5, 4,
+};
+
+static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
+{
+ int i;
+
+ /*
+ * The default context is always valid,
+ * the PAN context depends on uCode.
+ */
+ priv->valid_contexts = BIT(IWL_RXON_CTX_BSS);
+ if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN)
+ priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
+
+ for (i = 0; i < NUM_IWL_RXON_CTX; i++)
+ priv->contexts[i].ctxid = i;
+
+ priv->contexts[IWL_RXON_CTX_BSS].always_active = true;
+ priv->contexts[IWL_RXON_CTX_BSS].is_active = true;
+ priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON;
+ priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING;
+ priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC;
+ priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM;
+ priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID;
+ priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY;
+ priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
+ priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes =
+ BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR);
+ priv->contexts[IWL_RXON_CTX_BSS].interface_modes =
+ BIT(NL80211_IFTYPE_STATION);
+ priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP;
+ priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS;
+ priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS;
+ priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS;
+ memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue,
+ iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue));
+ memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo,
+ iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo));
+
+ priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON;
+ priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd =
+ REPLY_WIPAN_RXON_TIMING;
+ priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd =
+ REPLY_WIPAN_RXON_ASSOC;
+ priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM;
+ priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN;
+ priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY;
+ priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID;
+ priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION;
+ priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
+ BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
+
+ priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
+ priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
+ priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
+ memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue,
+ iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue));
+ memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo,
+ iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo));
+ priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE;
+
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+}
+
+static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
+{
+ struct iwl_ct_kill_config cmd;
+ struct iwl_ct_kill_throttling_config adv_cmd;
+ int ret = 0;
+
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+
+ priv->thermal_throttle.ct_kill_toggle = false;
+
+ if (priv->lib->support_ct_kill_exit) {
+ adv_cmd.critical_temperature_enter =
+ cpu_to_le32(priv->hw_params.ct_kill_threshold);
+ adv_cmd.critical_temperature_exit =
+ cpu_to_le32(priv->hw_params.ct_kill_exit_threshold);
+
+ ret = iwl_dvm_send_cmd_pdu(priv,
+ REPLY_CT_KILL_CONFIG_CMD,
+ 0, sizeof(adv_cmd), &adv_cmd);
+ if (ret)
+ IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
+ else
+ IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
+ "succeeded, critical temperature enter is %d,"
+ "exit is %d\n",
+ priv->hw_params.ct_kill_threshold,
+ priv->hw_params.ct_kill_exit_threshold);
+ } else {
+ cmd.critical_temperature_R =
+ cpu_to_le32(priv->hw_params.ct_kill_threshold);
+
+ ret = iwl_dvm_send_cmd_pdu(priv,
+ REPLY_CT_KILL_CONFIG_CMD,
+ 0, sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
+ else
+ IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
+ "succeeded, "
+ "critical temperature is %d\n",
+ priv->hw_params.ct_kill_threshold);
+ }
+}
+
+static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg)
+{
+ struct iwl_calib_cfg_cmd calib_cfg_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = CALIBRATION_CFG_CMD,
+ .len = { sizeof(struct iwl_calib_cfg_cmd), },
+ .data = { &calib_cfg_cmd, },
+ };
+
+ memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
+ calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL;
+ calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg);
+
+ return iwl_dvm_send_cmd(priv, &cmd);
+}
+
+
+static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
+{
+ struct iwl_tx_ant_config_cmd tx_ant_cmd = {
+ .valid = cpu_to_le32(valid_tx_ant),
+ };
+
+ if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) {
+ IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
+ return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0,
+ sizeof(struct iwl_tx_ant_config_cmd),
+ &tx_ant_cmd);
+ } else {
+ IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n");
+ return -EOPNOTSUPP;
+ }
+}
+
+static void iwl_send_bt_config(struct iwl_priv *priv)
+{
+ struct iwl_bt_cmd bt_cmd = {
+ .lead_time = BT_LEAD_TIME_DEF,
+ .max_kill = BT_MAX_KILL_DEF,
+ .kill_ack_mask = 0,
+ .kill_cts_mask = 0,
+ };
+
+ if (!iwlwifi_mod_params.bt_coex_active)
+ bt_cmd.flags = BT_COEX_DISABLE;
+ else
+ bt_cmd.flags = BT_COEX_ENABLE;
+
+ priv->bt_enable_flag = bt_cmd.flags;
+ IWL_DEBUG_INFO(priv, "BT coex %s\n",
+ (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
+
+ if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
+ 0, sizeof(struct iwl_bt_cmd), &bt_cmd))
+ IWL_ERR(priv, "failed to send BT Coex Config\n");
+}
+
+/**
+ * iwl_alive_start - called after REPLY_ALIVE notification received
+ * from protocol/runtime uCode (initialization uCode's
+ * Alive gets handled by iwl_init_alive_start()).
+ */
+int iwl_alive_start(struct iwl_priv *priv)
+{
+ int ret = 0;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+
+ IWL_DEBUG_INFO(priv, "Runtime Alive received.\n");
+
+ /* After the ALIVE response, we can send host commands to the uCode */
+ set_bit(STATUS_ALIVE, &priv->status);
+
+ if (iwl_is_rfkill(priv))
+ return -ERFKILL;
+
+ if (priv->event_log.ucode_trace) {
+ /* start collecting data now */
+ mod_timer(&priv->ucode_trace, jiffies);
+ }
+
+ /* download priority table before any calibration request */
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
+ /* Configure Bluetooth device coexistence support */
+ if (priv->lib->bt_params->bt_sco_disable)
+ priv->bt_enable_pspoll = false;
+ else
+ priv->bt_enable_pspoll = true;
+
+ priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
+ priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
+ priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
+ iwlagn_send_advance_bt_config(priv);
+ priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
+ priv->cur_rssi_ctx = NULL;
+
+ iwl_send_prio_tbl(priv);
+
+ /* FIXME: w/a to force change uCode BT state machine */
+ ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+ if (ret)
+ return ret;
+ ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+ if (ret)
+ return ret;
+ } else if (priv->lib->bt_params) {
+ /*
+ * default is 2-wire BT coexexistence support
+ */
+ iwl_send_bt_config(priv);
+ }
+
+ /*
+ * Perform runtime calibrations, including DC calibration.
+ */
+ iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX);
+
+ ieee80211_wake_queues(priv->hw);
+
+ /* Configure Tx antenna selection based on H/W config */
+ iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant);
+
+ if (iwl_is_associated_ctx(ctx) && !priv->wowlan) {
+ struct iwl_rxon_cmd *active_rxon =
+ (struct iwl_rxon_cmd *)&ctx->active;
+ /* apply any changes in staging */
+ ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
+ active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ } else {
+ struct iwl_rxon_context *tmp;
+ /* Initialize our rx_config data */
+ for_each_context(priv, tmp)
+ iwl_connection_init_rx_config(priv, tmp);
+
+ iwlagn_set_rxon_chain(priv, ctx);
+ }
+
+ if (!priv->wowlan) {
+ /* WoWLAN ucode will not reply in the same way, skip it */
+ iwl_reset_run_time_calib(priv);
+ }
+
+ set_bit(STATUS_READY, &priv->status);
+
+ /* Configure the adapter for unassociated operation */
+ ret = iwlagn_commit_rxon(priv, ctx);
+ if (ret)
+ return ret;
+
+ /* At this point, the NIC is initialized and operational */
+ iwl_rf_kill_ct_config(priv);
+
+ IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
+
+ return iwl_power_update_mode(priv, true);
+}
+
+/**
+ * iwl_clear_driver_stations - clear knowledge of all stations from driver
+ * @priv: iwl priv struct
+ *
+ * This is called during iwl_down() to make sure that in the case
+ * we're coming there from a hardware restart mac80211 will be
+ * able to reconfigure stations -- if we're getting there in the
+ * normal down flow then the stations will already be cleared.
+ */
+static void iwl_clear_driver_stations(struct iwl_priv *priv)
+{
+ struct iwl_rxon_context *ctx;
+
+ spin_lock_bh(&priv->sta_lock);
+ memset(priv->stations, 0, sizeof(priv->stations));
+ priv->num_stations = 0;
+
+ priv->ucode_key_table = 0;
+
+ for_each_context(priv, ctx) {
+ /*
+ * Remove all key information that is not stored as part
+ * of station information since mac80211 may not have had
+ * a chance to remove all the keys. When device is
+ * reconfigured by mac80211 after an error all keys will
+ * be reconfigured.
+ */
+ memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys));
+ ctx->key_mapping_keys = 0;
+ }
+
+ spin_unlock_bh(&priv->sta_lock);
+}
+
+void iwl_down(struct iwl_priv *priv)
+{
+ int exit_pending;
+
+ IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
+
+ lockdep_assert_held(&priv->mutex);
+
+ iwl_scan_cancel_timeout(priv, 200);
+
+ exit_pending =
+ test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
+
+ iwl_clear_ucode_stations(priv, NULL);
+ iwl_dealloc_bcast_stations(priv);
+ iwl_clear_driver_stations(priv);
+
+ /* reset BT coex data */
+ priv->bt_status = 0;
+ priv->cur_rssi_ctx = NULL;
+ priv->bt_is_sco = 0;
+ if (priv->lib->bt_params)
+ priv->bt_traffic_load =
+ priv->lib->bt_params->bt_init_traffic_load;
+ else
+ priv->bt_traffic_load = 0;
+ priv->bt_full_concurrent = false;
+ priv->bt_ci_compliance = 0;
+
+ /* Wipe out the EXIT_PENDING status bit if we are not actually
+ * exiting the module */
+ if (!exit_pending)
+ clear_bit(STATUS_EXIT_PENDING, &priv->status);
+
+ if (priv->mac80211_registered)
+ ieee80211_stop_queues(priv->hw);
+
+ priv->ucode_loaded = false;
+ iwl_trans_stop_device(priv->trans);
+
+ /* Set num_aux_in_flight must be done after the transport is stopped */
+ atomic_set(&priv->num_aux_in_flight, 0);
+
+ /* Clear out all status bits but a few that are stable across reset */
+ priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) <<
+ STATUS_RF_KILL_HW |
+ test_bit(STATUS_FW_ERROR, &priv->status) <<
+ STATUS_FW_ERROR |
+ test_bit(STATUS_EXIT_PENDING, &priv->status) <<
+ STATUS_EXIT_PENDING;
+
+ dev_kfree_skb(priv->beacon_skb);
+ priv->beacon_skb = NULL;
+}
+
+/*****************************************************************************
+ *
+ * Workqueue callbacks
+ *
+ *****************************************************************************/
+
+static void iwl_bg_run_time_calib_work(struct work_struct *work)
+{
+ struct iwl_priv *priv = container_of(work, struct iwl_priv,
+ run_time_calib_work);
+
+ mutex_lock(&priv->mutex);
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
+ test_bit(STATUS_SCANNING, &priv->status)) {
+ mutex_unlock(&priv->mutex);
+ return;
+ }
+
+ if (priv->start_calib) {
+ iwl_chain_noise_calibration(priv);
+ iwl_sensitivity_calibration(priv);
+ }
+
+ mutex_unlock(&priv->mutex);
+}
+
+void iwlagn_prepare_restart(struct iwl_priv *priv)
+{
+ bool bt_full_concurrent;
+ u8 bt_ci_compliance;
+ u8 bt_load;
+ u8 bt_status;
+ bool bt_is_sco;
+ int i;
+
+ lockdep_assert_held(&priv->mutex);
+
+ priv->is_open = 0;
+
+ /*
+ * __iwl_down() will clear the BT status variables,
+ * which is correct, but when we restart we really
+ * want to keep them so restore them afterwards.
+ *
+ * The restart process will later pick them up and
+ * re-configure the hw when we reconfigure the BT
+ * command.
+ */
+ bt_full_concurrent = priv->bt_full_concurrent;
+ bt_ci_compliance = priv->bt_ci_compliance;
+ bt_load = priv->bt_traffic_load;
+ bt_status = priv->bt_status;
+ bt_is_sco = priv->bt_is_sco;
+
+ iwl_down(priv);
+
+ priv->bt_full_concurrent = bt_full_concurrent;
+ priv->bt_ci_compliance = bt_ci_compliance;
+ priv->bt_traffic_load = bt_load;
+ priv->bt_status = bt_status;
+ priv->bt_is_sco = bt_is_sco;
+
+ /* reset aggregation queues */
+ for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++)
+ priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
+ /* and stop counts */
+ for (i = 0; i < IWL_MAX_HW_QUEUES; i++)
+ atomic_set(&priv->queue_stop_count[i], 0);
+
+ memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc));
+}
+
+static void iwl_bg_restart(struct work_struct *data)
+{
+ struct iwl_priv *priv = container_of(data, struct iwl_priv, restart);
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
+ mutex_lock(&priv->mutex);
+ iwlagn_prepare_restart(priv);
+ mutex_unlock(&priv->mutex);
+ iwl_cancel_deferred_work(priv);
+ if (priv->mac80211_registered)
+ ieee80211_restart_hw(priv->hw);
+ else
+ IWL_ERR(priv,
+ "Cannot request restart before registrating with mac80211\n");
+ } else {
+ WARN_ON(1);
+ }
+}
+
+/*****************************************************************************
+ *
+ * driver setup and teardown
+ *
+ *****************************************************************************/
+
+static void iwl_setup_deferred_work(struct iwl_priv *priv)
+{
+ priv->workqueue = create_singlethread_workqueue(DRV_NAME);
+
+ INIT_WORK(&priv->restart, iwl_bg_restart);
+ INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
+ INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
+ INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
+ INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
+ INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
+
+ iwl_setup_scan_deferred_work(priv);
+
+ if (priv->lib->bt_params)
+ iwlagn_bt_setup_deferred_work(priv);
+
+ setup_timer(&priv->statistics_periodic, iwl_bg_statistics_periodic,
+ (unsigned long)priv);
+
+ setup_timer(&priv->ucode_trace, iwl_bg_ucode_trace,
+ (unsigned long)priv);
+}
+
+void iwl_cancel_deferred_work(struct iwl_priv *priv)
+{
+ if (priv->lib->bt_params)
+ iwlagn_bt_cancel_deferred_work(priv);
+
+ cancel_work_sync(&priv->run_time_calib_work);
+ cancel_work_sync(&priv->beacon_update);
+
+ iwl_cancel_scan_deferred_work(priv);
+
+ cancel_work_sync(&priv->bt_full_concurrency);
+ cancel_work_sync(&priv->bt_runtime_config);
+
+ del_timer_sync(&priv->statistics_periodic);
+ del_timer_sync(&priv->ucode_trace);
+}
+
+static int iwl_init_drv(struct iwl_priv *priv)
+{
+ spin_lock_init(&priv->sta_lock);
+
+ mutex_init(&priv->mutex);
+
+ INIT_LIST_HEAD(&priv->calib_results);
+
+ priv->band = IEEE80211_BAND_2GHZ;
+
+ priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold;
+
+ priv->iw_mode = NL80211_IFTYPE_STATION;
+ priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
+ priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
+ priv->agg_tids_count = 0;
+
+ priv->rx_statistics_jiffies = jiffies;
+
+ /* Choose which receivers/antennas to use */
+ iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]);
+
+ iwl_init_scan_params(priv);
+
+ /* init bt coex */
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
+ priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
+ priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
+ priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
+ priv->bt_on_thresh = BT_ON_THRESHOLD_DEF;
+ priv->bt_duration = BT_DURATION_LIMIT_DEF;
+ priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF;
+ }
+
+ return 0;
+}
+
+static void iwl_uninit_drv(struct iwl_priv *priv)
+{
+ kfree(priv->scan_cmd);
+ kfree(priv->beacon_cmd);
+ kfree(rcu_dereference_raw(priv->noa_data));
+ iwl_calib_free_results(priv);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ kfree(priv->wowlan_sram);
+#endif
+}
+
+static void iwl_set_hw_params(struct iwl_priv *priv)
+{
+ if (priv->cfg->ht_params)
+ priv->hw_params.use_rts_for_aggregation =
+ priv->cfg->ht_params->use_rts_for_aggregation;
+
+ /* Device-specific setup */
+ priv->lib->set_hw_params(priv);
+}
+
+
+
+/* show what optional capabilities we have */
+static void iwl_option_config(struct iwl_priv *priv)
+{
+#ifdef CONFIG_IWLWIFI_DEBUG
+ IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n");
+#else
+ IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n");
+#endif
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n");
+#else
+ IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n");
+#endif
+
+#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
+ IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n");
+#else
+ IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n");
+#endif
+}
+
+static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
+{
+ struct iwl_nvm_data *data = priv->nvm_data;
+
+ if (data->sku_cap_11n_enable &&
+ !priv->cfg->ht_params) {
+ IWL_ERR(priv, "Invalid 11n configuration\n");
+ return -EINVAL;
+ }
+
+ if (!data->sku_cap_11n_enable && !data->sku_cap_band_24GHz_enable &&
+ !data->sku_cap_band_52GHz_enable) {
+ IWL_ERR(priv, "Invalid device sku\n");
+ return -EINVAL;
+ }
+
+ IWL_DEBUG_INFO(priv,
+ "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n",
+ data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled",
+ data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled",
+ data->sku_cap_11n_enable ? "" : "NOT", "enabled");
+
+ priv->hw_params.tx_chains_num =
+ num_of_ant(data->valid_tx_ant);
+ if (priv->cfg->rx_with_siso_diversity)
+ priv->hw_params.rx_chains_num = 1;
+ else
+ priv->hw_params.rx_chains_num =
+ num_of_ant(data->valid_rx_ant);
+
+ IWL_DEBUG_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n",
+ data->valid_tx_ant,
+ data->valid_rx_ant);
+
+ return 0;
+}
+
+static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
+ const struct iwl_cfg *cfg,
+ const struct iwl_fw *fw,
+ struct dentry *dbgfs_dir)
+{
+ struct iwl_priv *priv;
+ struct ieee80211_hw *hw;
+ struct iwl_op_mode *op_mode;
+ u16 num_mac;
+ u32 ucode_flags;
+ struct iwl_trans_config trans_cfg = {};
+ static const u8 no_reclaim_cmds[] = {
+ REPLY_RX_PHY_CMD,
+ REPLY_RX_MPDU_CMD,
+ REPLY_COMPRESSED_BA,
+ STATISTICS_NOTIFICATION,
+ REPLY_TX,
+ };
+ int i;
+
+ /************************
+ * 1. Allocating HW data
+ ************************/
+ hw = iwl_alloc_all();
+ if (!hw) {
+ pr_err("%s: Cannot allocate network device\n", cfg->name);
+ goto out;
+ }
+
+ op_mode = hw->priv;
+ op_mode->ops = &iwl_dvm_ops;
+ priv = IWL_OP_MODE_GET_DVM(op_mode);
+ priv->trans = trans;
+ priv->dev = trans->dev;
+ priv->cfg = cfg;
+ priv->fw = fw;
+
+ switch (priv->cfg->device_family) {
+ case IWL_DEVICE_FAMILY_1000:
+ case IWL_DEVICE_FAMILY_100:
+ priv->lib = &iwl_dvm_1000_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_2000:
+ priv->lib = &iwl_dvm_2000_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_105:
+ priv->lib = &iwl_dvm_105_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_2030:
+ case IWL_DEVICE_FAMILY_135:
+ priv->lib = &iwl_dvm_2030_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_5000:
+ priv->lib = &iwl_dvm_5000_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_5150:
+ priv->lib = &iwl_dvm_5150_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_6000:
+ case IWL_DEVICE_FAMILY_6000i:
+ priv->lib = &iwl_dvm_6000_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_6005:
+ priv->lib = &iwl_dvm_6005_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_6050:
+ case IWL_DEVICE_FAMILY_6150:
+ priv->lib = &iwl_dvm_6050_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_6030:
+ priv->lib = &iwl_dvm_6030_cfg;
+ break;
+ default:
+ break;
+ }
+
+ if (WARN_ON(!priv->lib))
+ goto out_free_hw;
+
+ /*
+ * Populate the state variables that the transport layer needs
+ * to know about.
+ */
+ trans_cfg.op_mode = op_mode;
+ trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
+ trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
+
+ switch (iwlwifi_mod_params.amsdu_size) {
+ case IWL_AMSDU_4K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ break;
+ case IWL_AMSDU_8K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_8K;
+ break;
+ case IWL_AMSDU_12K:
+ default:
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ pr_err("Unsupported amsdu_size: %d\n",
+ iwlwifi_mod_params.amsdu_size);
+ }
+
+ trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED;
+
+ trans_cfg.command_groups = iwl_dvm_groups;
+ trans_cfg.command_groups_size = ARRAY_SIZE(iwl_dvm_groups);
+
+ trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM;
+
+ WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE <
+ priv->cfg->base_params->num_of_queues);
+
+ ucode_flags = fw->ucode_capa.flags;
+
+ if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
+ priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
+ trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
+ } else {
+ priv->sta_key_max_num = STA_KEY_MAX_NUM;
+ trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
+ }
+
+ /* Configure transport layer */
+ iwl_trans_configure(priv->trans, &trans_cfg);
+
+ trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
+ trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+ trans->command_groups = trans_cfg.command_groups;
+ trans->command_groups_size = trans_cfg.command_groups_size;
+
+ /* At this point both hw and priv are allocated. */
+
+ SET_IEEE80211_DEV(priv->hw, priv->trans->dev);
+
+ iwl_option_config(priv);
+
+ IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
+
+ /* is antenna coupling more than 35dB ? */
+ priv->bt_ant_couple_ok =
+ (iwlwifi_mod_params.ant_coupling >
+ IWL_BT_ANTENNA_COUPLING_THRESHOLD) ?
+ true : false;
+
+ /* bt channel inhibition enabled*/
+ priv->bt_ch_announce = true;
+ IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n",
+ (priv->bt_ch_announce) ? "On" : "Off");
+
+ /* these spin locks will be used in apm_ops.init and EEPROM access
+ * we should init now
+ */
+ spin_lock_init(&priv->statistics.lock);
+
+ /***********************
+ * 2. Read REV register
+ ***********************/
+ IWL_INFO(priv, "Detected %s, REV=0x%X\n",
+ priv->cfg->name, priv->trans->hw_rev);
+
+ if (iwl_trans_start_hw(priv->trans))
+ goto out_free_hw;
+
+ /* Read the EEPROM */
+ if (iwl_read_eeprom(priv->trans, &priv->eeprom_blob,
+ &priv->eeprom_blob_size)) {
+ IWL_ERR(priv, "Unable to init EEPROM\n");
+ goto out_free_hw;
+ }
+
+ /* Reset chip to save power until we load uCode during "up". */
+ iwl_trans_stop_device(priv->trans);
+
+ priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg,
+ priv->eeprom_blob,
+ priv->eeprom_blob_size);
+ if (!priv->nvm_data)
+ goto out_free_eeprom_blob;
+
+ if (iwl_nvm_check_version(priv->nvm_data, priv->trans))
+ goto out_free_eeprom;
+
+ if (iwl_eeprom_init_hw_params(priv))
+ goto out_free_eeprom;
+
+ /* extract MAC Address */
+ memcpy(priv->addresses[0].addr, priv->nvm_data->hw_addr, ETH_ALEN);
+ IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr);
+ priv->hw->wiphy->addresses = priv->addresses;
+ priv->hw->wiphy->n_addresses = 1;
+ num_mac = priv->nvm_data->n_hw_addrs;
+ if (num_mac > 1) {
+ memcpy(priv->addresses[1].addr, priv->addresses[0].addr,
+ ETH_ALEN);
+ priv->addresses[1].addr[5]++;
+ priv->hw->wiphy->n_addresses++;
+ }
+
+ /************************
+ * 4. Setup HW constants
+ ************************/
+ iwl_set_hw_params(priv);
+
+ if (!(priv->nvm_data->sku_cap_ipan_enable)) {
+ IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n");
+ ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN;
+ /*
+ * if not PAN, then don't support P2P -- might be a uCode
+ * packaging bug or due to the eeprom check above
+ */
+ priv->sta_key_max_num = STA_KEY_MAX_NUM;
+ trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
+
+ /* Configure transport layer again*/
+ iwl_trans_configure(priv->trans, &trans_cfg);
+ }
+
+ /*******************
+ * 5. Setup priv
+ *******************/
+ for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
+ priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
+ if (i < IWLAGN_FIRST_AMPDU_QUEUE &&
+ i != IWL_DEFAULT_CMD_QUEUE_NUM &&
+ i != IWL_IPAN_CMD_QUEUE_NUM)
+ priv->queue_to_mac80211[i] = i;
+ atomic_set(&priv->queue_stop_count[i], 0);
+ }
+
+ if (iwl_init_drv(priv))
+ goto out_free_eeprom;
+
+ /* At this point both hw and priv are initialized. */
+
+ /********************
+ * 6. Setup services
+ ********************/
+ iwl_setup_deferred_work(priv);
+ iwl_setup_rx_handlers(priv);
+
+ iwl_power_initialize(priv);
+ iwl_tt_initialize(priv);
+
+ snprintf(priv->hw->wiphy->fw_version,
+ sizeof(priv->hw->wiphy->fw_version),
+ "%s", fw->fw_version);
+
+ priv->new_scan_threshold_behaviour =
+ !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN);
+
+ priv->phy_calib_chain_noise_reset_cmd =
+ fw->ucode_capa.standard_phy_calibration_size;
+ priv->phy_calib_chain_noise_gain_cmd =
+ fw->ucode_capa.standard_phy_calibration_size + 1;
+
+ /* initialize all valid contexts */
+ iwl_init_context(priv, ucode_flags);
+
+ /**************************************************
+ * This is still part of probe() in a sense...
+ *
+ * 7. Setup and register with mac80211 and debugfs
+ **************************************************/
+ if (iwlagn_mac_setup_register(priv, &fw->ucode_capa))
+ goto out_destroy_workqueue;
+
+ if (iwl_dbgfs_register(priv, dbgfs_dir))
+ goto out_mac80211_unregister;
+
+ return op_mode;
+
+out_mac80211_unregister:
+ iwlagn_mac_unregister(priv);
+out_destroy_workqueue:
+ iwl_tt_exit(priv);
+ iwl_cancel_deferred_work(priv);
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+ iwl_uninit_drv(priv);
+out_free_eeprom_blob:
+ kfree(priv->eeprom_blob);
+out_free_eeprom:
+ iwl_free_nvm_data(priv->nvm_data);
+out_free_hw:
+ ieee80211_free_hw(priv->hw);
+out:
+ op_mode = NULL;
+ return op_mode;
+}
+
+static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n");
+
+ iwlagn_mac_unregister(priv);
+
+ iwl_tt_exit(priv);
+
+ kfree(priv->eeprom_blob);
+ iwl_free_nvm_data(priv->nvm_data);
+
+ /*netif_stop_queue(dev); */
+ flush_workqueue(priv->workqueue);
+
+ /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes
+ * priv->workqueue... so we can't take down the workqueue
+ * until now... */
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+
+ iwl_uninit_drv(priv);
+
+ dev_kfree_skb(priv->beacon_skb);
+
+ iwl_trans_op_mode_leave(priv->trans);
+ ieee80211_free_hw(priv->hw);
+}
+
+static const char * const desc_lookup_text[] = {
+ "OK",
+ "FAIL",
+ "BAD_PARAM",
+ "BAD_CHECKSUM",
+ "NMI_INTERRUPT_WDG",
+ "SYSASSERT",
+ "FATAL_ERROR",
+ "BAD_COMMAND",
+ "HW_ERROR_TUNE_LOCK",
+ "HW_ERROR_TEMPERATURE",
+ "ILLEGAL_CHAN_FREQ",
+ "VCC_NOT_STABLE",
+ "FH_ERROR",
+ "NMI_INTERRUPT_HOST",
+ "NMI_INTERRUPT_ACTION_PT",
+ "NMI_INTERRUPT_UNKNOWN",
+ "UCODE_VERSION_MISMATCH",
+ "HW_ERROR_ABS_LOCK",
+ "HW_ERROR_CAL_LOCK_FAIL",
+ "NMI_INTERRUPT_INST_ACTION_PT",
+ "NMI_INTERRUPT_DATA_ACTION_PT",
+ "NMI_TRM_HW_ER",
+ "NMI_INTERRUPT_TRM",
+ "NMI_INTERRUPT_BREAK_POINT",
+ "DEBUG_0",
+ "DEBUG_1",
+ "DEBUG_2",
+ "DEBUG_3",
+};
+
+static struct { char *name; u8 num; } advanced_lookup[] = {
+ { "NMI_INTERRUPT_WDG", 0x34 },
+ { "SYSASSERT", 0x35 },
+ { "UCODE_VERSION_MISMATCH", 0x37 },
+ { "BAD_COMMAND", 0x38 },
+ { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
+ { "FATAL_ERROR", 0x3D },
+ { "NMI_TRM_HW_ERR", 0x46 },
+ { "NMI_INTERRUPT_TRM", 0x4C },
+ { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
+ { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
+ { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
+ { "NMI_INTERRUPT_HOST", 0x66 },
+ { "NMI_INTERRUPT_ACTION_PT", 0x7C },
+ { "NMI_INTERRUPT_UNKNOWN", 0x84 },
+ { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
+ { "ADVANCED_SYSASSERT", 0 },
+};
+
+static const char *desc_lookup(u32 num)
+{
+ int i;
+ int max = ARRAY_SIZE(desc_lookup_text);
+
+ if (num < max)
+ return desc_lookup_text[num];
+
+ max = ARRAY_SIZE(advanced_lookup) - 1;
+ for (i = 0; i < max; i++) {
+ if (advanced_lookup[i].num == num)
+ break;
+ }
+ return advanced_lookup[i].name;
+}
+
+#define ERROR_START_OFFSET (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+
+static void iwl_dump_nic_error_log(struct iwl_priv *priv)
+{
+ struct iwl_trans *trans = priv->trans;
+ u32 base;
+ struct iwl_error_event_table table;
+
+ base = priv->device_pointers.error_event_table;
+ if (priv->cur_ucode == IWL_UCODE_INIT) {
+ if (!base)
+ base = priv->fw->init_errlog_ptr;
+ } else {
+ if (!base)
+ base = priv->fw->inst_errlog_ptr;
+ }
+
+ if (!iwlagn_hw_valid_rtc_data_addr(base)) {
+ IWL_ERR(priv,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base,
+ (priv->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return;
+ }
+
+ /*TODO: Update dbgfs with ISR error stats obtained below */
+ iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+ if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+ IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+ IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+ priv->status, table.valid);
+ }
+
+ trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
+ table.data1, table.data2, table.line,
+ table.blink1, table.blink2, table.ilink1,
+ table.ilink2, table.bcon_time, table.gp1,
+ table.gp2, table.gp3, table.ucode_ver,
+ table.hw_ver, 0, table.brd_ver);
+ IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
+ desc_lookup(table.error_id));
+ IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
+ IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1);
+ IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2);
+ IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1);
+ IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2);
+ IWL_ERR(priv, "0x%08X | data1\n", table.data1);
+ IWL_ERR(priv, "0x%08X | data2\n", table.data2);
+ IWL_ERR(priv, "0x%08X | line\n", table.line);
+ IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time);
+ IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low);
+ IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi);
+ IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1);
+ IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2);
+ IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3);
+ IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver);
+ IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver);
+ IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver);
+ IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd);
+ IWL_ERR(priv, "0x%08X | isr0\n", table.isr0);
+ IWL_ERR(priv, "0x%08X | isr1\n", table.isr1);
+ IWL_ERR(priv, "0x%08X | isr2\n", table.isr2);
+ IWL_ERR(priv, "0x%08X | isr3\n", table.isr3);
+ IWL_ERR(priv, "0x%08X | isr4\n", table.isr4);
+ IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref);
+ IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event);
+ IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control);
+ IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration);
+ IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
+ IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
+ IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
+ IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp);
+ IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler);
+}
+
+#define EVENT_START_OFFSET (4 * sizeof(u32))
+
+/**
+ * iwl_print_event_log - Dump error event log to syslog
+ *
+ */
+static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
+ u32 num_events, u32 mode,
+ int pos, char **buf, size_t bufsz)
+{
+ u32 i;
+ u32 base; /* SRAM byte address of event log header */
+ u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
+ u32 ptr; /* SRAM byte address of log data */
+ u32 ev, time, data; /* event log data */
+ unsigned long reg_flags;
+
+ struct iwl_trans *trans = priv->trans;
+
+ if (num_events == 0)
+ return pos;
+
+ base = priv->device_pointers.log_event_table;
+ if (priv->cur_ucode == IWL_UCODE_INIT) {
+ if (!base)
+ base = priv->fw->init_evtlog_ptr;
+ } else {
+ if (!base)
+ base = priv->fw->inst_evtlog_ptr;
+ }
+
+ if (mode == 0)
+ event_size = 2 * sizeof(u32);
+ else
+ event_size = 3 * sizeof(u32);
+
+ ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
+
+ /* Make sure device is powered up for SRAM reads */
+ if (!iwl_trans_grab_nic_access(trans, &reg_flags))
+ return pos;
+
+ /* Set starting address; reads will auto-increment */
+ iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
+
+ /* "time" is actually "data" for mode 0 (no timestamp).
+ * place event id # at far right for easier visual parsing. */
+ for (i = 0; i < num_events; i++) {
+ ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+ time = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+ if (mode == 0) {
+ /* data, ev */
+ if (bufsz) {
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "EVT_LOG:0x%08x:%04u\n",
+ time, ev);
+ } else {
+ trace_iwlwifi_dev_ucode_event(trans->dev, 0,
+ time, ev);
+ IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
+ time, ev);
+ }
+ } else {
+ data = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+ if (bufsz) {
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "EVT_LOGT:%010u:0x%08x:%04u\n",
+ time, data, ev);
+ } else {
+ IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
+ time, data, ev);
+ trace_iwlwifi_dev_ucode_event(trans->dev, time,
+ data, ev);
+ }
+ }
+ }
+
+ /* Allow device to power down */
+ iwl_trans_release_nic_access(trans, &reg_flags);
+ return pos;
+}
+
+/**
+ * iwl_print_last_event_logs - Dump the newest # of event log to syslog
+ */
+static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+ u32 num_wraps, u32 next_entry,
+ u32 size, u32 mode,
+ int pos, char **buf, size_t bufsz)
+{
+ /*
+ * display the newest DEFAULT_LOG_ENTRIES entries
+ * i.e the entries just before the next ont that uCode would fill.
+ */
+ if (num_wraps) {
+ if (next_entry < size) {
+ pos = iwl_print_event_log(priv,
+ capacity - (size - next_entry),
+ size - next_entry, mode,
+ pos, buf, bufsz);
+ pos = iwl_print_event_log(priv, 0,
+ next_entry, mode,
+ pos, buf, bufsz);
+ } else
+ pos = iwl_print_event_log(priv, next_entry - size,
+ size, mode, pos, buf, bufsz);
+ } else {
+ if (next_entry < size) {
+ pos = iwl_print_event_log(priv, 0, next_entry,
+ mode, pos, buf, bufsz);
+ } else {
+ pos = iwl_print_event_log(priv, next_entry - size,
+ size, mode, pos, buf, bufsz);
+ }
+ }
+ return pos;
+}
+
+#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
+
+int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+ char **buf)
+{
+ u32 base; /* SRAM byte address of event log header */
+ u32 capacity; /* event log capacity in # entries */
+ u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
+ u32 num_wraps; /* # times uCode wrapped to top of log */
+ u32 next_entry; /* index of next entry to be written by uCode */
+ u32 size; /* # entries that we'll print */
+ u32 logsize;
+ int pos = 0;
+ size_t bufsz = 0;
+ struct iwl_trans *trans = priv->trans;
+
+ base = priv->device_pointers.log_event_table;
+ if (priv->cur_ucode == IWL_UCODE_INIT) {
+ logsize = priv->fw->init_evtlog_size;
+ if (!base)
+ base = priv->fw->init_evtlog_ptr;
+ } else {
+ logsize = priv->fw->inst_evtlog_size;
+ if (!base)
+ base = priv->fw->inst_evtlog_ptr;
+ }
+
+ if (!iwlagn_hw_valid_rtc_data_addr(base)) {
+ IWL_ERR(priv,
+ "Invalid event log pointer 0x%08X for %s uCode\n",
+ base,
+ (priv->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return -EINVAL;
+ }
+
+ /* event log header */
+ capacity = iwl_trans_read_mem32(trans, base);
+ mode = iwl_trans_read_mem32(trans, base + (1 * sizeof(u32)));
+ num_wraps = iwl_trans_read_mem32(trans, base + (2 * sizeof(u32)));
+ next_entry = iwl_trans_read_mem32(trans, base + (3 * sizeof(u32)));
+
+ if (capacity > logsize) {
+ IWL_ERR(priv, "Log capacity %d is bogus, limit to %d "
+ "entries\n", capacity, logsize);
+ capacity = logsize;
+ }
+
+ if (next_entry > logsize) {
+ IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
+ next_entry, logsize);
+ next_entry = logsize;
+ }
+
+ size = num_wraps ? capacity : next_entry;
+
+ /* bail out if nothing in log */
+ if (size == 0) {
+ IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n");
+ return pos;
+ }
+
+ if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log)
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+ IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
+ size);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (buf) {
+ if (full_log)
+ bufsz = capacity * 48;
+ else
+ bufsz = size * 48;
+ *buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+ }
+ if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) {
+ /*
+ * if uCode has wrapped back to top of log,
+ * start at the oldest entry,
+ * i.e the next one that uCode would fill.
+ */
+ if (num_wraps)
+ pos = iwl_print_event_log(priv, next_entry,
+ capacity - next_entry, mode,
+ pos, buf, bufsz);
+ /* (then/else) start at top of log */
+ pos = iwl_print_event_log(priv, 0,
+ next_entry, mode, pos, buf, bufsz);
+ } else
+ pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode,
+ pos, buf, bufsz);
+#else
+ pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode,
+ pos, buf, bufsz);
+#endif
+ return pos;
+}
+
+static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
+{
+ unsigned int reload_msec;
+ unsigned long reload_jiffies;
+
+ if (iwl_have_debug_level(IWL_DL_FW_ERRORS))
+ iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS);
+
+ /* uCode is no longer loaded. */
+ priv->ucode_loaded = false;
+
+ /* Set the FW error flag -- cleared on iwl_down */
+ set_bit(STATUS_FW_ERROR, &priv->status);
+
+ iwl_abort_notification_waits(&priv->notif_wait);
+
+ /* Keep the restart process from trying to send host
+ * commands by clearing the ready bit */
+ clear_bit(STATUS_READY, &priv->status);
+
+ if (!ondemand) {
+ /*
+ * If firmware keep reloading, then it indicate something
+ * serious wrong and firmware having problem to recover
+ * from it. Instead of keep trying which will fill the syslog
+ * and hang the system, let's just stop it
+ */
+ reload_jiffies = jiffies;
+ reload_msec = jiffies_to_msecs((long) reload_jiffies -
+ (long) priv->reload_jiffies);
+ priv->reload_jiffies = reload_jiffies;
+ if (reload_msec <= IWL_MIN_RELOAD_DURATION) {
+ priv->reload_count++;
+ if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) {
+ IWL_ERR(priv, "BUG_ON, Stop restarting\n");
+ return;
+ }
+ } else
+ priv->reload_count = 0;
+ }
+
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) {
+ if (iwlwifi_mod_params.restart_fw) {
+ IWL_DEBUG_FW_ERRORS(priv,
+ "Restarting adapter due to uCode error.\n");
+ queue_work(priv->workqueue, &priv->restart);
+ } else
+ IWL_DEBUG_FW_ERRORS(priv,
+ "Detected FW error, but not restarting\n");
+ }
+}
+
+static void iwl_nic_error(struct iwl_op_mode *op_mode)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ IWL_ERR(priv, "Loaded firmware version: %s\n",
+ priv->fw->fw_version);
+
+ iwl_dump_nic_error_log(priv);
+ iwl_dump_nic_event_log(priv, false, NULL);
+
+ iwlagn_fw_error(priv, false);
+}
+
+static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ if (!iwl_check_for_ct_kill(priv)) {
+ IWL_ERR(priv, "Restarting adapter queue is full\n");
+ iwlagn_fw_error(priv, false);
+ }
+}
+
+#define EEPROM_RF_CONFIG_TYPE_MAX 0x3
+
+static void iwl_nic_config(struct iwl_op_mode *op_mode)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ /* SKU Control */
+ iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
+ CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP,
+ (CSR_HW_REV_STEP(priv->trans->hw_rev) <<
+ CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) |
+ (CSR_HW_REV_DASH(priv->trans->hw_rev) <<
+ CSR_HW_IF_CONFIG_REG_POS_MAC_DASH));
+
+ /* write radio config values to register */
+ if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) {
+ u32 reg_val =
+ priv->nvm_data->radio_cfg_type <<
+ CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE |
+ priv->nvm_data->radio_cfg_step <<
+ CSR_HW_IF_CONFIG_REG_POS_PHY_STEP |
+ priv->nvm_data->radio_cfg_dash <<
+ CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
+
+ iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
+ CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
+ CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH,
+ reg_val);
+
+ IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n",
+ priv->nvm_data->radio_cfg_type,
+ priv->nvm_data->radio_cfg_step,
+ priv->nvm_data->radio_cfg_dash);
+ } else {
+ WARN_ON(1);
+ }
+
+ /* set CSR_HW_CONFIG_REG for uCode use */
+ iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+ CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+
+ /* W/A : NIC is stuck in a reset state after Early PCIe power off
+ * (PCIe power is lost before PERST# is asserted),
+ * causing ME FW to lose ownership and not being able to obtain it back.
+ */
+ iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
+ ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
+
+ if (priv->lib->nic_config)
+ priv->lib->nic_config(priv);
+}
+
+static void iwl_wimax_active(struct iwl_op_mode *op_mode)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ clear_bit(STATUS_READY, &priv->status);
+ IWL_ERR(priv, "RF is used by WiMAX\n");
+}
+
+static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ int mq = priv->queue_to_mac80211[queue];
+
+ if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
+ return;
+
+ if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "queue %d (mac80211 %d) already stopped\n",
+ queue, mq);
+ return;
+ }
+
+ set_bit(mq, &priv->transport_queue_stop);
+ ieee80211_stop_queue(priv->hw, mq);
+}
+
+static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ int mq = priv->queue_to_mac80211[queue];
+
+ if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
+ return;
+
+ if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "queue %d (mac80211 %d) already awake\n",
+ queue, mq);
+ return;
+ }
+
+ clear_bit(mq, &priv->transport_queue_stop);
+
+ if (!priv->passive_no_rx)
+ ieee80211_wake_queue(priv->hw, mq);
+}
+
+void iwlagn_lift_passive_no_rx(struct iwl_priv *priv)
+{
+ int mq;
+
+ if (!priv->passive_no_rx)
+ return;
+
+ for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) {
+ if (!test_bit(mq, &priv->transport_queue_stop)) {
+ IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq);
+ ieee80211_wake_queue(priv->hw, mq);
+ } else {
+ IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq);
+ }
+ }
+
+ priv->passive_no_rx = false;
+}
+
+static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ struct ieee80211_tx_info *info;
+
+ info = IEEE80211_SKB_CB(skb);
+ iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]);
+ ieee80211_free_txskb(priv->hw, skb);
+}
+
+static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ if (state)
+ set_bit(STATUS_RF_KILL_HW, &priv->status);
+ else
+ clear_bit(STATUS_RF_KILL_HW, &priv->status);
+
+ wiphy_rfkill_set_hw_state(priv->hw->wiphy, state);
+
+ return false;
+}
+
+static const struct iwl_op_mode_ops iwl_dvm_ops = {
+ .start = iwl_op_mode_dvm_start,
+ .stop = iwl_op_mode_dvm_stop,
+ .rx = iwl_rx_dispatch,
+ .queue_full = iwl_stop_sw_queue,
+ .queue_not_full = iwl_wake_sw_queue,
+ .hw_rf_kill = iwl_set_hw_rfkill_state,
+ .free_skb = iwl_free_skb,
+ .nic_error = iwl_nic_error,
+ .cmd_queue_full = iwl_cmd_queue_full,
+ .nic_config = iwl_nic_config,
+ .wimax_active = iwl_wimax_active,
+};
+
+/*****************************************************************************
+ *
+ * driver and module entry point
+ *
+ *****************************************************************************/
+static int __init iwl_init(void)
+{
+
+ int ret;
+
+ ret = iwlagn_rate_control_register();
+ if (ret) {
+ pr_err("Unable to register rate control algorithm: %d\n", ret);
+ return ret;
+ }
+
+ ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops);
+ if (ret) {
+ pr_err("Unable to register op_mode: %d\n", ret);
+ iwlagn_rate_control_unregister();
+ }
+
+ return ret;
+}
+module_init(iwl_init);
+
+static void __exit iwl_exit(void)
+{
+ iwl_opmode_deregister("iwldvm");
+ iwlagn_rate_control_unregister();
+}
+module_exit(iwl_exit);
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c
new file mode 100644
index 000000000000..0ad557c89514
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c
@@ -0,0 +1,395 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <net/mac80211.h>
+#include "iwl-io.h"
+#include "iwl-debug.h"
+#include "iwl-trans.h"
+#include "iwl-modparams.h"
+#include "dev.h"
+#include "agn.h"
+#include "commands.h"
+#include "power.h"
+
+static bool force_cam = true;
+module_param(force_cam, bool, 0644);
+MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");
+
+/*
+ * Setting power level allows the card to go to sleep when not busy.
+ *
+ * We calculate a sleep command based on the required latency, which
+ * we get from mac80211. In order to handle thermal throttling, we can
+ * also use pre-defined power levels.
+ */
+
+/*
+ * This defines the old power levels. They are still used by default
+ * (level 1) and for thermal throttle (levels 3 through 5)
+ */
+
+struct iwl_power_vec_entry {
+ struct iwl_powertable_cmd cmd;
+ u8 no_dtim; /* number of skip dtim */
+};
+
+#define IWL_DTIM_RANGE_0_MAX 2
+#define IWL_DTIM_RANGE_1_MAX 10
+
+#define NOSLP cpu_to_le16(0), 0, 0
+#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0
+#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \
+ IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \
+ IWL_POWER_ADVANCE_PM_ENA_MSK)
+#define ASLP_TOUT(T) cpu_to_le32(T)
+#define TU_TO_USEC 1024
+#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC)
+#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \
+ cpu_to_le32(X1), \
+ cpu_to_le32(X2), \
+ cpu_to_le32(X3), \
+ cpu_to_le32(X4)}
+/* default power management (not Tx power) table values */
+/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */
+/* DTIM 0 - 2 */
+static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = {
+ {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1},
+ {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2}
+};
+
+
+/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
+/* DTIM 3 - 10 */
+static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = {
+ {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
+ {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1},
+ {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2}
+};
+
+/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
+/* DTIM 11 - */
+static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = {
+ {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}
+};
+
+/* advance power management */
+/* DTIM 0 - 2 */
+static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = {
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
+};
+
+
+/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
+/* DTIM 3 - 10 */
+static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = {
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2}
+};
+
+/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
+/* DTIM 11 - */
+static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = {
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+ {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+ SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
+};
+
+static void iwl_static_sleep_cmd(struct iwl_priv *priv,
+ struct iwl_powertable_cmd *cmd,
+ enum iwl_power_level lvl, int period)
+{
+ const struct iwl_power_vec_entry *table;
+ int max_sleep[IWL_POWER_VEC_SIZE] = { 0 };
+ int i;
+ u8 skip;
+ u32 slp_itrvl;
+
+ if (priv->lib->adv_pm) {
+ table = apm_range_2;
+ if (period <= IWL_DTIM_RANGE_1_MAX)
+ table = apm_range_1;
+ if (period <= IWL_DTIM_RANGE_0_MAX)
+ table = apm_range_0;
+ } else {
+ table = range_2;
+ if (period <= IWL_DTIM_RANGE_1_MAX)
+ table = range_1;
+ if (period <= IWL_DTIM_RANGE_0_MAX)
+ table = range_0;
+ }
+
+ if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM))
+ memset(cmd, 0, sizeof(*cmd));
+ else
+ *cmd = table[lvl].cmd;
+
+ if (period == 0) {
+ skip = 0;
+ period = 1;
+ for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
+ max_sleep[i] = 1;
+
+ } else {
+ skip = table[lvl].no_dtim;
+ for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
+ max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]);
+ max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1;
+ }
+
+ slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
+ /* figure out the listen interval based on dtim period and skip */
+ if (slp_itrvl == 0xFF)
+ cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
+ cpu_to_le32(period * (skip + 1));
+
+ slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
+ if (slp_itrvl > period)
+ cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
+ cpu_to_le32((slp_itrvl / period) * period);
+
+ if (skip)
+ cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK;
+ else
+ cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
+
+ if (priv->cfg->base_params->shadow_reg_enable)
+ cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
+ else
+ cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
+
+ if (iwl_advanced_bt_coexist(priv)) {
+ if (!priv->lib->bt_params->bt_sco_disable)
+ cmd->flags |= IWL_POWER_BT_SCO_ENA;
+ else
+ cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
+ }
+
+
+ slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
+ if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL)
+ cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
+ cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL);
+
+ /* enforce max sleep interval */
+ for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) {
+ if (le32_to_cpu(cmd->sleep_interval[i]) >
+ (max_sleep[i] * period))
+ cmd->sleep_interval[i] =
+ cpu_to_le32(max_sleep[i] * period);
+ if (i != (IWL_POWER_VEC_SIZE - 1)) {
+ if (le32_to_cpu(cmd->sleep_interval[i]) >
+ le32_to_cpu(cmd->sleep_interval[i+1]))
+ cmd->sleep_interval[i] =
+ cmd->sleep_interval[i+1];
+ }
+ }
+
+ if (priv->power_data.bus_pm)
+ cmd->flags |= IWL_POWER_PCI_PM_MSK;
+ else
+ cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
+
+ IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n",
+ skip, period);
+ /* The power level here is 0-4 (used as array index), but user expects
+ to see 1-5 (according to spec). */
+ IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1);
+}
+
+static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv,
+ struct iwl_powertable_cmd *cmd)
+{
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (priv->power_data.bus_pm)
+ cmd->flags |= IWL_POWER_PCI_PM_MSK;
+
+ IWL_DEBUG_POWER(priv, "Sleep command for CAM\n");
+}
+
+static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
+{
+ IWL_DEBUG_POWER(priv, "Sending power/sleep command\n");
+ IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags);
+ IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
+ IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
+ IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n",
+ le32_to_cpu(cmd->sleep_interval[0]),
+ le32_to_cpu(cmd->sleep_interval[1]),
+ le32_to_cpu(cmd->sleep_interval[2]),
+ le32_to_cpu(cmd->sleep_interval[3]),
+ le32_to_cpu(cmd->sleep_interval[4]));
+
+ return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0,
+ sizeof(struct iwl_powertable_cmd), cmd);
+}
+
+static void iwl_power_build_cmd(struct iwl_priv *priv,
+ struct iwl_powertable_cmd *cmd)
+{
+ bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
+ int dtimper;
+
+ if (force_cam) {
+ iwl_power_sleep_cam_cmd(priv, cmd);
+ return;
+ }
+
+ dtimper = priv->hw->conf.ps_dtim_period ?: 1;
+
+ if (priv->wowlan)
+ iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
+ else if (!priv->lib->no_idle_support &&
+ priv->hw->conf.flags & IEEE80211_CONF_IDLE)
+ iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
+ else if (iwl_tt_is_low_power_state(priv)) {
+ /* in thermal throttling low power state */
+ iwl_static_sleep_cmd(priv, cmd,
+ iwl_tt_current_power_mode(priv), dtimper);
+ } else if (!enabled)
+ iwl_power_sleep_cam_cmd(priv, cmd);
+ else if (priv->power_data.debug_sleep_level_override >= 0)
+ iwl_static_sleep_cmd(priv, cmd,
+ priv->power_data.debug_sleep_level_override,
+ dtimper);
+ else {
+ /* Note that the user parameter is 1-5 (according to spec),
+ but we pass 0-4 because it acts as an array index. */
+ if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 &&
+ iwlwifi_mod_params.power_level <= IWL_POWER_NUM)
+ iwl_static_sleep_cmd(priv, cmd,
+ iwlwifi_mod_params.power_level - 1, dtimper);
+ else
+ iwl_static_sleep_cmd(priv, cmd,
+ IWL_POWER_INDEX_1, dtimper);
+ }
+}
+
+int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
+ bool force)
+{
+ int ret;
+ bool update_chains;
+
+ lockdep_assert_held(&priv->mutex);
+
+ /* Don't update the RX chain when chain noise calibration is running */
+ update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
+ priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
+
+ if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
+ return 0;
+
+ if (!iwl_is_ready_rf(priv))
+ return -EIO;
+
+ /* scan complete use sleep_power_next, need to be updated */
+ memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
+ if (test_bit(STATUS_SCANNING, &priv->status) && !force) {
+ IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n");
+ return 0;
+ }
+
+ if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
+ iwl_dvm_set_pmi(priv, true);
+
+ ret = iwl_set_power(priv, cmd);
+ if (!ret) {
+ if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
+ iwl_dvm_set_pmi(priv, false);
+
+ if (update_chains)
+ iwl_update_chain_flags(priv);
+ else
+ IWL_DEBUG_POWER(priv,
+ "Cannot update the power, chain noise "
+ "calibration running: %d\n",
+ priv->chain_noise_data.state);
+
+ memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));
+ } else
+ IWL_ERR(priv, "set power fail, ret = %d\n", ret);
+
+ return ret;
+}
+
+int iwl_power_update_mode(struct iwl_priv *priv, bool force)
+{
+ struct iwl_powertable_cmd cmd;
+
+ iwl_power_build_cmd(priv, &cmd);
+ return iwl_power_set_mode(priv, &cmd, force);
+}
+
+/* initialize to default */
+void iwl_power_initialize(struct iwl_priv *priv)
+{
+ priv->power_data.bus_pm = priv->trans->pm_support;
+
+ priv->power_data.debug_sleep_level_override = -1;
+
+ memset(&priv->power_data.sleep_cmd, 0,
+ sizeof(priv->power_data.sleep_cmd));
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.h b/drivers/net/wireless/intel/iwlwifi/dvm/power.h
new file mode 100644
index 000000000000..2fd9b43adafd
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+#ifndef __iwl_power_setting_h__
+#define __iwl_power_setting_h__
+
+#include "commands.h"
+
+struct iwl_power_mgr {
+ struct iwl_powertable_cmd sleep_cmd;
+ struct iwl_powertable_cmd sleep_cmd_next;
+ int debug_sleep_level_override;
+ bool bus_pm;
+};
+
+int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
+ bool force);
+int iwl_power_update_mode(struct iwl_priv *priv, bool force);
+void iwl_power_initialize(struct iwl_priv *priv);
+
+extern bool no_sleep_autoadjust;
+
+#endif /* __iwl_power_setting_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c
new file mode 100644
index 000000000000..ee7505537c96
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c
@@ -0,0 +1,3338 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include <linux/workqueue.h>
+
+#include "dev.h"
+#include "agn.h"
+
+#define RS_NAME "iwl-agn-rs"
+
+#define NUM_TRY_BEFORE_ANT_TOGGLE 1
+#define IWL_NUMBER_TRY 1
+#define IWL_HT_NUMBER_TRY 3
+
+#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */
+#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */
+#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */
+
+/* max allowed rate miss before sync LQ cmd */
+#define IWL_MISSED_RATE_MAX 15
+/* max time to accum history 2 seconds */
+#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ)
+
+static u8 rs_ht_to_legacy[] = {
+ IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+ IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+ IWL_RATE_6M_INDEX,
+ IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
+ IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
+ IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
+ IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
+};
+
+static const u8 ant_toggle_lookup[] = {
+ /*ANT_NONE -> */ ANT_NONE,
+ /*ANT_A -> */ ANT_B,
+ /*ANT_B -> */ ANT_C,
+ /*ANT_AB -> */ ANT_BC,
+ /*ANT_C -> */ ANT_A,
+ /*ANT_AC -> */ ANT_AB,
+ /*ANT_BC -> */ ANT_AC,
+ /*ANT_ABC -> */ ANT_ABC,
+};
+
+#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \
+ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
+ IWL_RATE_SISO_##s##M_PLCP, \
+ IWL_RATE_MIMO2_##s##M_PLCP,\
+ IWL_RATE_MIMO3_##s##M_PLCP,\
+ IWL_RATE_##r##M_IEEE, \
+ IWL_RATE_##ip##M_INDEX, \
+ IWL_RATE_##in##M_INDEX, \
+ IWL_RATE_##rp##M_INDEX, \
+ IWL_RATE_##rn##M_INDEX, \
+ IWL_RATE_##pp##M_INDEX, \
+ IWL_RATE_##np##M_INDEX }
+
+/*
+ * Parameter order:
+ * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
+ *
+ * If there isn't a valid next or previous rate then INV is used which
+ * maps to IWL_RATE_INVALID
+ *
+ */
+const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = {
+ IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */
+ IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */
+ IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */
+ IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */
+ IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */
+ IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */
+ IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */
+ IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */
+ IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */
+ IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */
+ IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */
+ IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
+ IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
+ /* FIXME:RS: ^^ should be INV (legacy) */
+};
+
+static inline u8 rs_extract_rate(u32 rate_n_flags)
+{
+ return (u8)(rate_n_flags & RATE_MCS_RATE_MSK);
+}
+
+static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
+{
+ int idx = 0;
+
+ /* HT rate format */
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ idx = rs_extract_rate(rate_n_flags);
+
+ if (idx >= IWL_RATE_MIMO3_6M_PLCP)
+ idx = idx - IWL_RATE_MIMO3_6M_PLCP;
+ else if (idx >= IWL_RATE_MIMO2_6M_PLCP)
+ idx = idx - IWL_RATE_MIMO2_6M_PLCP;
+
+ idx += IWL_FIRST_OFDM_RATE;
+ /* skip 9M not supported in ht*/
+ if (idx >= IWL_RATE_9M_INDEX)
+ idx += 1;
+ if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
+ return idx;
+
+ /* legacy rate format, search for match in table */
+ } else {
+ for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
+ if (iwl_rates[idx].plcp ==
+ rs_extract_rate(rate_n_flags))
+ return idx;
+ }
+
+ return -1;
+}
+
+static void rs_rate_scale_perform(struct iwl_priv *priv,
+ struct sk_buff *skb,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta);
+static void rs_fill_link_cmd(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
+
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+ u32 *rate_n_flags, int index);
+#else
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+ u32 *rate_n_flags, int index)
+{}
+#endif
+
+/**
+ * The following tables contain the expected throughput metrics for all rates
+ *
+ * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
+ *
+ * where invalid entries are zeros.
+ *
+ * CCK rates are only valid in legacy table and will only be used in G
+ * (2.4 GHz) band.
+ */
+
+static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
+ 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
+};
+
+static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */
+ {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */
+ {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */
+ {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
+};
+
+static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
+ {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
+ {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
+ {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
+};
+
+static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
+ {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
+ {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
+ {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
+};
+
+static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
+ {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
+ {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
+ {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
+};
+
+static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
+ {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
+ {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
+ {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
+};
+
+static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */
+ {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */
+ {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */
+ {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */
+};
+
+/* mbps, mcs */
+static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
+ { "1", "BPSK DSSS"},
+ { "2", "QPSK DSSS"},
+ {"5.5", "BPSK CCK"},
+ { "11", "QPSK CCK"},
+ { "6", "BPSK 1/2"},
+ { "9", "BPSK 1/2"},
+ { "12", "QPSK 1/2"},
+ { "18", "QPSK 3/4"},
+ { "24", "16QAM 1/2"},
+ { "36", "16QAM 3/4"},
+ { "48", "64QAM 2/3"},
+ { "54", "64QAM 3/4"},
+ { "60", "64QAM 5/6"},
+};
+
+#define MCS_INDEX_PER_STREAM (8)
+
+static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
+{
+ window->data = 0;
+ window->success_counter = 0;
+ window->success_ratio = IWL_INVALID_VALUE;
+ window->counter = 0;
+ window->average_tpt = IWL_INVALID_VALUE;
+ window->stamp = 0;
+}
+
+static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
+{
+ return (ant_type & valid_antenna) == ant_type;
+}
+
+/*
+ * removes the old data from the statistics. All data that is older than
+ * TID_MAX_TIME_DIFF, will be deleted.
+ */
+static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time)
+{
+ /* The oldest age we want to keep */
+ u32 oldest_time = curr_time - TID_MAX_TIME_DIFF;
+
+ while (tl->queue_count &&
+ (tl->time_stamp < oldest_time)) {
+ tl->total -= tl->packet_count[tl->head];
+ tl->packet_count[tl->head] = 0;
+ tl->time_stamp += TID_QUEUE_CELL_SPACING;
+ tl->queue_count--;
+ tl->head++;
+ if (tl->head >= TID_QUEUE_MAX_SIZE)
+ tl->head = 0;
+ }
+}
+
+/*
+ * increment traffic load value for tid and also remove
+ * any old values if passed the certain time period
+ */
+static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
+ struct ieee80211_hdr *hdr)
+{
+ u32 curr_time = jiffies_to_msecs(jiffies);
+ u32 time_diff;
+ s32 index;
+ struct iwl_traffic_load *tl = NULL;
+ u8 tid;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & 0xf;
+ } else
+ return IWL_MAX_TID_COUNT;
+
+ if (unlikely(tid >= IWL_MAX_TID_COUNT))
+ return IWL_MAX_TID_COUNT;
+
+ tl = &lq_data->load[tid];
+
+ curr_time -= curr_time % TID_ROUND_VALUE;
+
+ /* Happens only for the first packet. Initialize the data */
+ if (!(tl->queue_count)) {
+ tl->total = 1;
+ tl->time_stamp = curr_time;
+ tl->queue_count = 1;
+ tl->head = 0;
+ tl->packet_count[0] = 1;
+ return IWL_MAX_TID_COUNT;
+ }
+
+ time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+ index = time_diff / TID_QUEUE_CELL_SPACING;
+
+ /* The history is too long: remove data that is older than */
+ /* TID_MAX_TIME_DIFF */
+ if (index >= TID_QUEUE_MAX_SIZE)
+ rs_tl_rm_old_stats(tl, curr_time);
+
+ index = (tl->head + index) % TID_QUEUE_MAX_SIZE;
+ tl->packet_count[index] = tl->packet_count[index] + 1;
+ tl->total = tl->total + 1;
+
+ if ((index + 1) > tl->queue_count)
+ tl->queue_count = index + 1;
+
+ return tid;
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+/**
+ * Program the device to use fixed rate for frame transmit
+ * This is for debugging/testing only
+ * once the device start use fixed rate, we need to reload the module
+ * to being back the normal operation.
+ */
+static void rs_program_fix_rate(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta)
+{
+ struct iwl_station_priv *sta_priv =
+ container_of(lq_sta, struct iwl_station_priv, lq_sta);
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
+
+ lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */
+ lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
+ lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
+ lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
+
+ IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n",
+ lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
+
+ if (lq_sta->dbg_fixed_rate) {
+ rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
+ iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC,
+ false);
+ }
+}
+#endif
+
+/*
+ get the traffic load value for tid
+*/
+static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid)
+{
+ u32 curr_time = jiffies_to_msecs(jiffies);
+ u32 time_diff;
+ s32 index;
+ struct iwl_traffic_load *tl = NULL;
+
+ if (tid >= IWL_MAX_TID_COUNT)
+ return 0;
+
+ tl = &(lq_data->load[tid]);
+
+ curr_time -= curr_time % TID_ROUND_VALUE;
+
+ if (!(tl->queue_count))
+ return 0;
+
+ time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+ index = time_diff / TID_QUEUE_CELL_SPACING;
+
+ /* The history is too long: remove data that is older than */
+ /* TID_MAX_TIME_DIFF */
+ if (index >= TID_QUEUE_MAX_SIZE)
+ rs_tl_rm_old_stats(tl, curr_time);
+
+ return tl->total;
+}
+
+static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_data, u8 tid,
+ struct ieee80211_sta *sta)
+{
+ int ret = -EAGAIN;
+ u32 load;
+
+ /*
+ * Don't create TX aggregation sessions when in high
+ * BT traffic, as they would just be disrupted by BT.
+ */
+ if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) {
+ IWL_DEBUG_COEX(priv,
+ "BT traffic (%d), no aggregation allowed\n",
+ priv->bt_traffic_load);
+ return ret;
+ }
+
+ load = rs_tl_get_load(lq_data, tid);
+
+ IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n",
+ sta->addr, tid);
+ ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
+ if (ret == -EAGAIN) {
+ /*
+ * driver and mac80211 is out of sync
+ * this might be cause by reloading firmware
+ * stop the tx ba session here
+ */
+ IWL_ERR(priv, "Fail start Tx agg on tid: %d\n",
+ tid);
+ ieee80211_stop_tx_ba_session(sta, tid);
+ }
+ return ret;
+}
+
+static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid,
+ struct iwl_lq_sta *lq_data,
+ struct ieee80211_sta *sta)
+{
+ if (tid < IWL_MAX_TID_COUNT)
+ rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta);
+ else
+ IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n",
+ tid, IWL_MAX_TID_COUNT);
+}
+
+static inline int get_num_of_ant_from_rate(u32 rate_n_flags)
+{
+ return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) +
+ !!(rate_n_flags & RATE_MCS_ANT_B_MSK) +
+ !!(rate_n_flags & RATE_MCS_ANT_C_MSK);
+}
+
+/*
+ * Static function to get the expected throughput from an iwl_scale_tbl_info
+ * that wraps a NULL pointer check
+ */
+static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
+{
+ if (tbl->expected_tpt)
+ return tbl->expected_tpt[rs_index];
+ return 0;
+}
+
+/**
+ * rs_collect_tx_data - Update the success/failure sliding window
+ *
+ * We keep a sliding window of the last 62 packets transmitted
+ * at this rate. window->data contains the bitmask of successful
+ * packets.
+ */
+static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
+ int scale_index, int attempts, int successes)
+{
+ struct iwl_rate_scale_data *window = NULL;
+ static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1));
+ s32 fail_count, tpt;
+
+ if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
+ return -EINVAL;
+
+ /* Select window for current tx bit rate */
+ window = &(tbl->win[scale_index]);
+
+ /* Get expected throughput */
+ tpt = get_expected_tpt(tbl, scale_index);
+
+ /*
+ * Keep track of only the latest 62 tx frame attempts in this rate's
+ * history window; anything older isn't really relevant any more.
+ * If we have filled up the sliding window, drop the oldest attempt;
+ * if the oldest attempt (highest bit in bitmap) shows "success",
+ * subtract "1" from the success counter (this is the main reason
+ * we keep these bitmaps!).
+ */
+ while (attempts > 0) {
+ if (window->counter >= IWL_RATE_MAX_WINDOW) {
+
+ /* remove earliest */
+ window->counter = IWL_RATE_MAX_WINDOW - 1;
+
+ if (window->data & mask) {
+ window->data &= ~mask;
+ window->success_counter--;
+ }
+ }
+
+ /* Increment frames-attempted counter */
+ window->counter++;
+
+ /* Shift bitmap by one frame to throw away oldest history */
+ window->data <<= 1;
+
+ /* Mark the most recent #successes attempts as successful */
+ if (successes > 0) {
+ window->success_counter++;
+ window->data |= 0x1;
+ successes--;
+ }
+
+ attempts--;
+ }
+
+ /* Calculate current success ratio, avoid divide-by-0! */
+ if (window->counter > 0)
+ window->success_ratio = 128 * (100 * window->success_counter)
+ / window->counter;
+ else
+ window->success_ratio = IWL_INVALID_VALUE;
+
+ fail_count = window->counter - window->success_counter;
+
+ /* Calculate average throughput, if we have enough history. */
+ if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
+ (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
+ window->average_tpt = (window->success_ratio * tpt + 64) / 128;
+ else
+ window->average_tpt = IWL_INVALID_VALUE;
+
+ /* Tag this window as having been updated */
+ window->stamp = jiffies;
+
+ return 0;
+}
+
+/*
+ * Fill uCode API rate_n_flags field, based on "search" or "active" table.
+ */
+/* FIXME:RS:remove this function and put the flags statically in the table */
+static u32 rate_n_flags_from_tbl(struct iwl_priv *priv,
+ struct iwl_scale_tbl_info *tbl,
+ int index, u8 use_green)
+{
+ u32 rate_n_flags = 0;
+
+ if (is_legacy(tbl->lq_type)) {
+ rate_n_flags = iwl_rates[index].plcp;
+ if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
+ rate_n_flags |= RATE_MCS_CCK_MSK;
+
+ } else if (is_Ht(tbl->lq_type)) {
+ if (index > IWL_LAST_OFDM_RATE) {
+ IWL_ERR(priv, "Invalid HT rate index %d\n", index);
+ index = IWL_LAST_OFDM_RATE;
+ }
+ rate_n_flags = RATE_MCS_HT_MSK;
+
+ if (is_siso(tbl->lq_type))
+ rate_n_flags |= iwl_rates[index].plcp_siso;
+ else if (is_mimo2(tbl->lq_type))
+ rate_n_flags |= iwl_rates[index].plcp_mimo2;
+ else
+ rate_n_flags |= iwl_rates[index].plcp_mimo3;
+ } else {
+ IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type);
+ }
+
+ rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
+ RATE_MCS_ANT_ABC_MSK);
+
+ if (is_Ht(tbl->lq_type)) {
+ if (tbl->is_ht40) {
+ if (tbl->is_dup)
+ rate_n_flags |= RATE_MCS_DUP_MSK;
+ else
+ rate_n_flags |= RATE_MCS_HT40_MSK;
+ }
+ if (tbl->is_SGI)
+ rate_n_flags |= RATE_MCS_SGI_MSK;
+
+ if (use_green) {
+ rate_n_flags |= RATE_MCS_GF_MSK;
+ if (is_siso(tbl->lq_type) && tbl->is_SGI) {
+ rate_n_flags &= ~RATE_MCS_SGI_MSK;
+ IWL_ERR(priv, "GF was set with SGI:SISO\n");
+ }
+ }
+ }
+ return rate_n_flags;
+}
+
+/*
+ * Interpret uCode API's rate_n_flags format,
+ * fill "search" or "active" tx mode table.
+ */
+static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
+ enum ieee80211_band band,
+ struct iwl_scale_tbl_info *tbl,
+ int *rate_idx)
+{
+ u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK);
+ u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
+ u8 mcs;
+
+ memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
+ *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
+
+ if (*rate_idx == IWL_RATE_INVALID) {
+ *rate_idx = -1;
+ return -EINVAL;
+ }
+ tbl->is_SGI = 0; /* default legacy setup */
+ tbl->is_ht40 = 0;
+ tbl->is_dup = 0;
+ tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS);
+ tbl->lq_type = LQ_NONE;
+ tbl->max_search = IWL_MAX_SEARCH;
+
+ /* legacy rate format */
+ if (!(rate_n_flags & RATE_MCS_HT_MSK)) {
+ if (num_of_ant == 1) {
+ if (band == IEEE80211_BAND_5GHZ)
+ tbl->lq_type = LQ_A;
+ else
+ tbl->lq_type = LQ_G;
+ }
+ /* HT rate format */
+ } else {
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ tbl->is_SGI = 1;
+
+ if ((rate_n_flags & RATE_MCS_HT40_MSK) ||
+ (rate_n_flags & RATE_MCS_DUP_MSK))
+ tbl->is_ht40 = 1;
+
+ if (rate_n_flags & RATE_MCS_DUP_MSK)
+ tbl->is_dup = 1;
+
+ mcs = rs_extract_rate(rate_n_flags);
+
+ /* SISO */
+ if (mcs <= IWL_RATE_SISO_60M_PLCP) {
+ if (num_of_ant == 1)
+ tbl->lq_type = LQ_SISO; /*else NONE*/
+ /* MIMO2 */
+ } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
+ if (num_of_ant == 2)
+ tbl->lq_type = LQ_MIMO2;
+ /* MIMO3 */
+ } else {
+ if (num_of_ant == 3) {
+ tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
+ tbl->lq_type = LQ_MIMO3;
+ }
+ }
+ }
+ return 0;
+}
+
+/* switch to another antenna/antennas and return 1 */
+/* if no other valid antenna found, return 0 */
+static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
+ struct iwl_scale_tbl_info *tbl)
+{
+ u8 new_ant_type;
+
+ if (!tbl->ant_type || tbl->ant_type > ANT_ABC)
+ return 0;
+
+ if (!rs_is_valid_ant(valid_ant, tbl->ant_type))
+ return 0;
+
+ new_ant_type = ant_toggle_lookup[tbl->ant_type];
+
+ while ((new_ant_type != tbl->ant_type) &&
+ !rs_is_valid_ant(valid_ant, new_ant_type))
+ new_ant_type = ant_toggle_lookup[new_ant_type];
+
+ if (new_ant_type == tbl->ant_type)
+ return 0;
+
+ tbl->ant_type = new_ant_type;
+ *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK;
+ *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS;
+ return 1;
+}
+
+/**
+ * Green-field mode is valid if the station supports it and
+ * there are no non-GF stations present in the BSS.
+ */
+static bool rs_use_green(struct ieee80211_sta *sta)
+{
+ /*
+ * There's a bug somewhere in this code that causes the
+ * scaling to get stuck because GF+SGI can't be combined
+ * in SISO rates. Until we find that bug, disable GF, it
+ * has only limited benefit and we still interoperate with
+ * GF APs since we can always receive GF transmissions.
+ */
+ return false;
+}
+
+/**
+ * rs_get_supported_rates - get the available rates
+ *
+ * if management frame or broadcast frame only return
+ * basic available rates.
+ *
+ */
+static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
+ struct ieee80211_hdr *hdr,
+ enum iwl_table_type rate_type)
+{
+ if (is_legacy(rate_type)) {
+ return lq_sta->active_legacy_rate;
+ } else {
+ if (is_siso(rate_type))
+ return lq_sta->active_siso_rate;
+ else if (is_mimo2(rate_type))
+ return lq_sta->active_mimo2_rate;
+ else
+ return lq_sta->active_mimo3_rate;
+ }
+}
+
+static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask,
+ int rate_type)
+{
+ u8 high = IWL_RATE_INVALID;
+ u8 low = IWL_RATE_INVALID;
+
+ /* 802.11A or ht walks to the next literal adjacent rate in
+ * the rate table */
+ if (is_a_band(rate_type) || !is_legacy(rate_type)) {
+ int i;
+ u32 mask;
+
+ /* Find the previous rate that is in the rate mask */
+ i = index - 1;
+ for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
+ if (rate_mask & mask) {
+ low = i;
+ break;
+ }
+ }
+
+ /* Find the next rate that is in the rate mask */
+ i = index + 1;
+ for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
+ if (rate_mask & mask) {
+ high = i;
+ break;
+ }
+ }
+
+ return (high << 8) | low;
+ }
+
+ low = index;
+ while (low != IWL_RATE_INVALID) {
+ low = iwl_rates[low].prev_rs;
+ if (low == IWL_RATE_INVALID)
+ break;
+ if (rate_mask & (1 << low))
+ break;
+ IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low);
+ }
+
+ high = index;
+ while (high != IWL_RATE_INVALID) {
+ high = iwl_rates[high].next_rs;
+ if (high == IWL_RATE_INVALID)
+ break;
+ if (rate_mask & (1 << high))
+ break;
+ IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high);
+ }
+
+ return (high << 8) | low;
+}
+
+static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl,
+ u8 scale_index, u8 ht_possible)
+{
+ s32 low;
+ u16 rate_mask;
+ u16 high_low;
+ u8 switch_to_legacy = 0;
+ u8 is_green = lq_sta->is_green;
+ struct iwl_priv *priv = lq_sta->drv;
+
+ /* check if we need to switch from HT to legacy rates.
+ * assumption is that mandatory rates (1Mbps or 6Mbps)
+ * are always supported (spec demand) */
+ if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) {
+ switch_to_legacy = 1;
+ scale_index = rs_ht_to_legacy[scale_index];
+ if (lq_sta->band == IEEE80211_BAND_5GHZ)
+ tbl->lq_type = LQ_A;
+ else
+ tbl->lq_type = LQ_G;
+
+ if (num_of_ant(tbl->ant_type) > 1)
+ tbl->ant_type =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+
+ tbl->is_ht40 = 0;
+ tbl->is_SGI = 0;
+ tbl->max_search = IWL_MAX_SEARCH;
+ }
+
+ rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type);
+
+ /* Mask with station rate restriction */
+ if (is_legacy(tbl->lq_type)) {
+ /* supp_rates has no CCK bits in A mode */
+ if (lq_sta->band == IEEE80211_BAND_5GHZ)
+ rate_mask = (u16)(rate_mask &
+ (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
+ else
+ rate_mask = (u16)(rate_mask & lq_sta->supp_rates);
+ }
+
+ /* If we switched from HT to legacy, check current rate */
+ if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
+ low = scale_index;
+ goto out;
+ }
+
+ high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
+ tbl->lq_type);
+ low = high_low & 0xff;
+
+ if (low == IWL_RATE_INVALID)
+ low = scale_index;
+
+out:
+ return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green);
+}
+
+/*
+ * Simple function to compare two rate scale table types
+ */
+static bool table_type_matches(struct iwl_scale_tbl_info *a,
+ struct iwl_scale_tbl_info *b)
+{
+ return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) &&
+ (a->is_SGI == b->is_SGI);
+}
+
+static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct iwl_lq_sta *lq_sta)
+{
+ struct iwl_scale_tbl_info *tbl;
+ bool full_concurrent = priv->bt_full_concurrent;
+
+ if (priv->bt_ant_couple_ok) {
+ /*
+ * Is there a need to switch between
+ * full concurrency and 3-wire?
+ */
+ if (priv->bt_ci_compliance && priv->bt_ant_couple_ok)
+ full_concurrent = true;
+ else
+ full_concurrent = false;
+ }
+ if ((priv->bt_traffic_load != priv->last_bt_traffic_load) ||
+ (priv->bt_full_concurrent != full_concurrent)) {
+ priv->bt_full_concurrent = full_concurrent;
+ priv->last_bt_traffic_load = priv->bt_traffic_load;
+
+ /* Update uCode's rate table. */
+ tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
+ iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
+
+ queue_work(priv->workqueue, &priv->bt_full_concurrency);
+ }
+}
+
+/*
+ * mac80211 sends us Tx status
+ */
+static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb)
+{
+ int legacy_success;
+ int retries;
+ int rs_index, mac_index, i;
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ struct iwl_link_quality_cmd *table;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r;
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ enum mac80211_rate_control_flags mac_flags;
+ u32 tx_rate;
+ struct iwl_scale_tbl_info tbl_type;
+ struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
+
+ IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
+
+ /* Treat uninitialized rate scaling data same as non-existing. */
+ if (!lq_sta) {
+ IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n");
+ return;
+ } else if (!lq_sta->drv) {
+ IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
+ return;
+ }
+
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ info->flags & IEEE80211_TX_CTL_NO_ACK)
+ return;
+
+ /* This packet was aggregated but doesn't carry status info */
+ if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+ !(info->flags & IEEE80211_TX_STAT_AMPDU))
+ return;
+
+ /*
+ * Ignore this Tx frame response if its initial rate doesn't match
+ * that of latest Link Quality command. There may be stragglers
+ * from a previous Link Quality command, but we're no longer interested
+ * in those; they're either from the "active" mode while we're trying
+ * to check "search" mode, or a prior "search" mode after we've moved
+ * to a new "search" mode (which might become the new "active" mode).
+ */
+ table = &lq_sta->lq;
+ tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
+ rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index);
+ if (priv->band == IEEE80211_BAND_5GHZ)
+ rs_index -= IWL_FIRST_OFDM_RATE;
+ mac_flags = info->status.rates[0].flags;
+ mac_index = info->status.rates[0].idx;
+ /* For HT packets, map MCS to PLCP */
+ if (mac_flags & IEEE80211_TX_RC_MCS) {
+ mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */
+ if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
+ mac_index++;
+ /*
+ * mac80211 HT index is always zero-indexed; we need to move
+ * HT OFDM rates after CCK rates in 2.4 GHz band
+ */
+ if (priv->band == IEEE80211_BAND_2GHZ)
+ mac_index += IWL_FIRST_OFDM_RATE;
+ }
+ /* Here we actually compare this rate to the latest LQ command */
+ if ((mac_index < 0) ||
+ (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
+ (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
+ (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) ||
+ (tbl_type.ant_type != info->status.antenna) ||
+ (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
+ (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
+ (rs_index != mac_index)) {
+ IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate);
+ /*
+ * Since rates mis-match, the last LQ command may have failed.
+ * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
+ * ... driver.
+ */
+ lq_sta->missed_rate_counter++;
+ if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
+ lq_sta->missed_rate_counter = 0;
+ iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
+ }
+ /* Regardless, ignore this status info for outdated rate */
+ return;
+ } else
+ /* Rate did match, so reset the missed_rate_counter */
+ lq_sta->missed_rate_counter = 0;
+
+ /* Figure out if rate scale algorithm is in active or search table */
+ if (table_type_matches(&tbl_type,
+ &(lq_sta->lq_info[lq_sta->active_tbl]))) {
+ curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ } else if (table_type_matches(&tbl_type,
+ &lq_sta->lq_info[1 - lq_sta->active_tbl])) {
+ curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ } else {
+ IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n");
+ tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n",
+ tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI);
+ tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n",
+ tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI);
+ IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n",
+ tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI);
+ /*
+ * no matching table found, let's by-pass the data collection
+ * and continue to perform rate scale to find the rate table
+ */
+ rs_stay_in_table(lq_sta, true);
+ goto done;
+ }
+
+ /*
+ * Updating the frame history depends on whether packets were
+ * aggregated.
+ *
+ * For aggregation, all packets were transmitted at the same rate, the
+ * first index into rate scale table.
+ */
+ if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+ tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
+ rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type,
+ &rs_index);
+ rs_collect_tx_data(curr_tbl, rs_index,
+ info->status.ampdu_len,
+ info->status.ampdu_ack_len);
+
+ /* Update success/fail counts if not searching for new mode */
+ if (lq_sta->stay_in_tbl) {
+ lq_sta->total_success += info->status.ampdu_ack_len;
+ lq_sta->total_failed += (info->status.ampdu_len -
+ info->status.ampdu_ack_len);
+ }
+ } else {
+ /*
+ * For legacy, update frame history with for each Tx retry.
+ */
+ retries = info->status.rates[0].count - 1;
+ /* HW doesn't send more than 15 retries */
+ retries = min(retries, 15);
+
+ /* The last transmission may have been successful */
+ legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ /* Collect data for each rate used during failed TX attempts */
+ for (i = 0; i <= retries; ++i) {
+ tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags);
+ rs_get_tbl_info_from_mcs(tx_rate, priv->band,
+ &tbl_type, &rs_index);
+ /*
+ * Only collect stats if retried rate is in the same RS
+ * table as active/search.
+ */
+ if (table_type_matches(&tbl_type, curr_tbl))
+ tmp_tbl = curr_tbl;
+ else if (table_type_matches(&tbl_type, other_tbl))
+ tmp_tbl = other_tbl;
+ else
+ continue;
+ rs_collect_tx_data(tmp_tbl, rs_index, 1,
+ i < retries ? 0 : legacy_success);
+ }
+
+ /* Update success/fail counts if not searching for new mode */
+ if (lq_sta->stay_in_tbl) {
+ lq_sta->total_success += legacy_success;
+ lq_sta->total_failed += retries + (1 - legacy_success);
+ }
+ }
+ /* The last TX rate is cached in lq_sta; it's set in if/else above */
+ lq_sta->last_rate_n_flags = tx_rate;
+done:
+ /* See if there's a better rate or modulation mode to try. */
+ if (sta && sta->supp_rates[sband->band])
+ rs_rate_scale_perform(priv, skb, sta, lq_sta);
+
+ if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist)
+ rs_bt_update_lq(priv, ctx, lq_sta);
+}
+
+/*
+ * Begin a period of staying with a selected modulation mode.
+ * Set "stay_in_tbl" flag to prevent any mode switches.
+ * Set frame tx success limits according to legacy vs. high-throughput,
+ * and reset overall (spanning all rates) tx success history statistics.
+ * These control how long we stay using same modulation mode before
+ * searching for a new mode.
+ */
+static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy,
+ struct iwl_lq_sta *lq_sta)
+{
+ IWL_DEBUG_RATE(priv, "we are staying in the same table\n");
+ lq_sta->stay_in_tbl = 1; /* only place this gets set */
+ if (is_legacy) {
+ lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT;
+ lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT;
+ lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT;
+ } else {
+ lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT;
+ lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT;
+ lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT;
+ }
+ lq_sta->table_count = 0;
+ lq_sta->total_failed = 0;
+ lq_sta->total_success = 0;
+ lq_sta->flush_timer = jiffies;
+ lq_sta->action_counter = 0;
+}
+
+/*
+ * Find correct throughput table for given mode of modulation
+ */
+static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl)
+{
+ /* Used to choose among HT tables */
+ const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+
+ /* Check for invalid LQ type */
+ if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
+ tbl->expected_tpt = expected_tpt_legacy;
+ return;
+ }
+
+ /* Legacy rates have only one table */
+ if (is_legacy(tbl->lq_type)) {
+ tbl->expected_tpt = expected_tpt_legacy;
+ return;
+ }
+
+ /* Choose among many HT tables depending on number of streams
+ * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation
+ * status */
+ if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
+ ht_tbl_pointer = expected_tpt_siso20MHz;
+ else if (is_siso(tbl->lq_type))
+ ht_tbl_pointer = expected_tpt_siso40MHz;
+ else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
+ ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+ else if (is_mimo2(tbl->lq_type))
+ ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+ else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
+ ht_tbl_pointer = expected_tpt_mimo3_20MHz;
+ else /* if (is_mimo3(tbl->lq_type)) <-- must be true */
+ ht_tbl_pointer = expected_tpt_mimo3_40MHz;
+
+ if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */
+ tbl->expected_tpt = ht_tbl_pointer[0];
+ else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */
+ tbl->expected_tpt = ht_tbl_pointer[1];
+ else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */
+ tbl->expected_tpt = ht_tbl_pointer[2];
+ else /* AGG+SGI */
+ tbl->expected_tpt = ht_tbl_pointer[3];
+}
+
+/*
+ * Find starting rate for new "search" high-throughput mode of modulation.
+ * Goal is to find lowest expected rate (under perfect conditions) that is
+ * above the current measured throughput of "active" mode, to give new mode
+ * a fair chance to prove itself without too many challenges.
+ *
+ * This gets called when transitioning to more aggressive modulation
+ * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
+ * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need
+ * to decrease to match "active" throughput. When moving from MIMO to SISO,
+ * bit rate will typically need to increase, but not if performance was bad.
+ */
+static s32 rs_get_best_rate(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl, /* "search" */
+ u16 rate_mask, s8 index)
+{
+ /* "active" values */
+ struct iwl_scale_tbl_info *active_tbl =
+ &(lq_sta->lq_info[lq_sta->active_tbl]);
+ s32 active_sr = active_tbl->win[index].success_ratio;
+ s32 active_tpt = active_tbl->expected_tpt[index];
+ /* expected "search" throughput */
+ const u16 *tpt_tbl = tbl->expected_tpt;
+
+ s32 new_rate, high, low, start_hi;
+ u16 high_low;
+ s8 rate = index;
+
+ new_rate = high = low = start_hi = IWL_RATE_INVALID;
+
+ for (; ;) {
+ high_low = rs_get_adjacent_rate(priv, rate, rate_mask,
+ tbl->lq_type);
+
+ low = high_low & 0xff;
+ high = (high_low >> 8) & 0xff;
+
+ /*
+ * Lower the "search" bit rate, to give new "search" mode
+ * approximately the same throughput as "active" if:
+ *
+ * 1) "Active" mode has been working modestly well (but not
+ * great), and expected "search" throughput (under perfect
+ * conditions) at candidate rate is above the actual
+ * measured "active" throughput (but less than expected
+ * "active" throughput under perfect conditions).
+ * OR
+ * 2) "Active" mode has been working perfectly or very well
+ * and expected "search" throughput (under perfect
+ * conditions) at candidate rate is above expected
+ * "active" throughput (under perfect conditions).
+ */
+ if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
+ ((active_sr > IWL_RATE_DECREASE_TH) &&
+ (active_sr <= IWL_RATE_HIGH_TH) &&
+ (tpt_tbl[rate] <= active_tpt))) ||
+ ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
+ (tpt_tbl[rate] > active_tpt))) {
+
+ /* (2nd or later pass)
+ * If we've already tried to raise the rate, and are
+ * now trying to lower it, use the higher rate. */
+ if (start_hi != IWL_RATE_INVALID) {
+ new_rate = start_hi;
+ break;
+ }
+
+ new_rate = rate;
+
+ /* Loop again with lower rate */
+ if (low != IWL_RATE_INVALID)
+ rate = low;
+
+ /* Lower rate not available, use the original */
+ else
+ break;
+
+ /* Else try to raise the "search" rate to match "active" */
+ } else {
+ /* (2nd or later pass)
+ * If we've already tried to lower the rate, and are
+ * now trying to raise it, use the lower rate. */
+ if (new_rate != IWL_RATE_INVALID)
+ break;
+
+ /* Loop again with higher rate */
+ else if (high != IWL_RATE_INVALID) {
+ start_hi = high;
+ rate = high;
+
+ /* Higher rate not available, use the original */
+ } else {
+ new_rate = rate;
+ break;
+ }
+ }
+ }
+
+ return new_rate;
+}
+
+/*
+ * Set up search table for MIMO2
+ */
+static int rs_switch_to_mimo2(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_conf *conf,
+ struct ieee80211_sta *sta,
+ struct iwl_scale_tbl_info *tbl, int index)
+{
+ u16 rate_mask;
+ s32 rate;
+ s8 is_green = lq_sta->is_green;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
+
+ if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
+ return -1;
+
+ if (sta->smps_mode == IEEE80211_SMPS_STATIC)
+ return -1;
+
+ /* Need both Tx chains/antennas to support MIMO */
+ if (priv->hw_params.tx_chains_num < 2)
+ return -1;
+
+ IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n");
+
+ tbl->lq_type = LQ_MIMO2;
+ tbl->is_dup = lq_sta->is_dup;
+ tbl->action = 0;
+ tbl->max_search = IWL_MAX_SEARCH;
+ rate_mask = lq_sta->active_mimo2_rate;
+
+ if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
+ tbl->is_ht40 = 1;
+ else
+ tbl->is_ht40 = 0;
+
+ rs_set_expected_tpt_table(lq_sta, tbl);
+
+ rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index);
+
+ IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask);
+ if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+ IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n",
+ rate, rate_mask);
+ return -1;
+ }
+ tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green);
+
+ IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n",
+ tbl->current_rate, is_green);
+ return 0;
+}
+
+/*
+ * Set up search table for MIMO3
+ */
+static int rs_switch_to_mimo3(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_conf *conf,
+ struct ieee80211_sta *sta,
+ struct iwl_scale_tbl_info *tbl, int index)
+{
+ u16 rate_mask;
+ s32 rate;
+ s8 is_green = lq_sta->is_green;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
+
+ if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
+ return -1;
+
+ if (sta->smps_mode == IEEE80211_SMPS_STATIC)
+ return -1;
+
+ /* Need both Tx chains/antennas to support MIMO */
+ if (priv->hw_params.tx_chains_num < 3)
+ return -1;
+
+ IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n");
+
+ tbl->lq_type = LQ_MIMO3;
+ tbl->is_dup = lq_sta->is_dup;
+ tbl->action = 0;
+ tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
+ rate_mask = lq_sta->active_mimo3_rate;
+
+ if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
+ tbl->is_ht40 = 1;
+ else
+ tbl->is_ht40 = 0;
+
+ rs_set_expected_tpt_table(lq_sta, tbl);
+
+ rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index);
+
+ IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n",
+ rate, rate_mask);
+ if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+ IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n",
+ rate, rate_mask);
+ return -1;
+ }
+ tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green);
+
+ IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n",
+ tbl->current_rate, is_green);
+ return 0;
+}
+
+/*
+ * Set up search table for SISO
+ */
+static int rs_switch_to_siso(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_conf *conf,
+ struct ieee80211_sta *sta,
+ struct iwl_scale_tbl_info *tbl, int index)
+{
+ u16 rate_mask;
+ u8 is_green = lq_sta->is_green;
+ s32 rate;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
+
+ if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
+ return -1;
+
+ IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n");
+
+ tbl->is_dup = lq_sta->is_dup;
+ tbl->lq_type = LQ_SISO;
+ tbl->action = 0;
+ tbl->max_search = IWL_MAX_SEARCH;
+ rate_mask = lq_sta->active_siso_rate;
+
+ if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
+ tbl->is_ht40 = 1;
+ else
+ tbl->is_ht40 = 0;
+
+ if (is_green)
+ tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/
+
+ rs_set_expected_tpt_table(lq_sta, tbl);
+ rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index);
+
+ IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask);
+ if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+ IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n",
+ rate, rate_mask);
+ return -1;
+ }
+ tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green);
+ IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n",
+ tbl->current_rate, is_green);
+ return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from legacy
+ */
+static void rs_move_legacy_other(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_conf *conf,
+ struct ieee80211_sta *sta,
+ int index)
+{
+ struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ struct iwl_scale_tbl_info *search_tbl =
+ &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+ struct iwl_rate_scale_data *window = &(tbl->win[index]);
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+ u8 start_action;
+ u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
+ u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ int ret = 0;
+ u8 update_search_tbl_counter = 0;
+
+ switch (priv->bt_traffic_load) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ /* nothing */
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ /* avoid antenna B unless MIMO */
+ if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2)
+ tbl->action = IWL_LEGACY_SWITCH_SISO;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ /* avoid antenna B and MIMO */
+ valid_tx_ant =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+ if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 &&
+ tbl->action != IWL_LEGACY_SWITCH_SISO)
+ tbl->action = IWL_LEGACY_SWITCH_SISO;
+ break;
+ default:
+ IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
+ break;
+ }
+
+ if (!iwl_ht_enabled(priv))
+ /* stay in Legacy */
+ tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+ else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE &&
+ tbl->action > IWL_LEGACY_SWITCH_SISO)
+ tbl->action = IWL_LEGACY_SWITCH_SISO;
+
+ /* configure as 1x1 if bt full concurrency */
+ if (priv->bt_full_concurrent) {
+ if (!iwl_ht_enabled(priv))
+ tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+ else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
+ tbl->action = IWL_LEGACY_SWITCH_SISO;
+ valid_tx_ant =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+ }
+
+ start_action = tbl->action;
+ for (; ;) {
+ lq_sta->action_counter++;
+ switch (tbl->action) {
+ case IWL_LEGACY_SWITCH_ANTENNA1:
+ case IWL_LEGACY_SWITCH_ANTENNA2:
+ IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n");
+
+ if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
+ tx_chains_num <= 1) ||
+ (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
+ tx_chains_num <= 2))
+ break;
+
+ /* Don't change antenna if success has been great */
+ if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
+ !priv->bt_full_concurrent &&
+ priv->bt_traffic_load ==
+ IWL_BT_COEX_TRAFFIC_LOAD_NONE)
+ break;
+
+ /* Set up search table to try other antenna */
+ memcpy(search_tbl, tbl, sz);
+
+ if (rs_toggle_antenna(valid_tx_ant,
+ &search_tbl->current_rate, search_tbl)) {
+ update_search_tbl_counter = 1;
+ rs_set_expected_tpt_table(lq_sta, search_tbl);
+ goto out;
+ }
+ break;
+ case IWL_LEGACY_SWITCH_SISO:
+ IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n");
+
+ /* Set up search table to try SISO */
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = 0;
+ ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret) {
+ lq_sta->action_counter = 0;
+ goto out;
+ }
+
+ break;
+ case IWL_LEGACY_SWITCH_MIMO2_AB:
+ case IWL_LEGACY_SWITCH_MIMO2_AC:
+ case IWL_LEGACY_SWITCH_MIMO2_BC:
+ IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n");
+
+ /* Set up search table to try MIMO */
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = 0;
+
+ if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
+ search_tbl->ant_type = ANT_AB;
+ else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
+ search_tbl->ant_type = ANT_AC;
+ else
+ search_tbl->ant_type = ANT_BC;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret) {
+ lq_sta->action_counter = 0;
+ goto out;
+ }
+ break;
+
+ case IWL_LEGACY_SWITCH_MIMO3_ABC:
+ IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n");
+
+ /* Set up search table to try MIMO3 */
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = 0;
+
+ search_tbl->ant_type = ANT_ABC;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret) {
+ lq_sta->action_counter = 0;
+ goto out;
+ }
+ break;
+ }
+ tbl->action++;
+ if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+ tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+
+ if (tbl->action == start_action)
+ break;
+
+ }
+ search_tbl->lq_type = LQ_NONE;
+ return;
+
+out:
+ lq_sta->search_better_tbl = 1;
+ tbl->action++;
+ if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+ tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+ if (update_search_tbl_counter)
+ search_tbl->action = tbl->action;
+}
+
+/*
+ * Try to switch to new modulation mode from SISO
+ */
+static void rs_move_siso_to_other(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_conf *conf,
+ struct ieee80211_sta *sta, int index)
+{
+ u8 is_green = lq_sta->is_green;
+ struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ struct iwl_scale_tbl_info *search_tbl =
+ &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+ struct iwl_rate_scale_data *window = &(tbl->win[index]);
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+ u8 start_action;
+ u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
+ u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ u8 update_search_tbl_counter = 0;
+ int ret;
+
+ switch (priv->bt_traffic_load) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ /* nothing */
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ /* avoid antenna B unless MIMO */
+ if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
+ tbl->action = IWL_SISO_SWITCH_MIMO2_AB;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ /* avoid antenna B and MIMO */
+ valid_tx_ant =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+ if (tbl->action != IWL_SISO_SWITCH_ANTENNA1)
+ tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+ break;
+ default:
+ IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
+ break;
+ }
+
+ if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE &&
+ tbl->action > IWL_SISO_SWITCH_ANTENNA2) {
+ /* stay in SISO */
+ tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+ }
+
+ /* configure as 1x1 if bt full concurrency */
+ if (priv->bt_full_concurrent) {
+ valid_tx_ant =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+ if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
+ tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+ }
+
+ start_action = tbl->action;
+ for (;;) {
+ lq_sta->action_counter++;
+ switch (tbl->action) {
+ case IWL_SISO_SWITCH_ANTENNA1:
+ case IWL_SISO_SWITCH_ANTENNA2:
+ IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n");
+ if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
+ tx_chains_num <= 1) ||
+ (tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
+ tx_chains_num <= 2))
+ break;
+
+ if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
+ !priv->bt_full_concurrent &&
+ priv->bt_traffic_load ==
+ IWL_BT_COEX_TRAFFIC_LOAD_NONE)
+ break;
+
+ memcpy(search_tbl, tbl, sz);
+ if (rs_toggle_antenna(valid_tx_ant,
+ &search_tbl->current_rate, search_tbl)) {
+ update_search_tbl_counter = 1;
+ goto out;
+ }
+ break;
+ case IWL_SISO_SWITCH_MIMO2_AB:
+ case IWL_SISO_SWITCH_MIMO2_AC:
+ case IWL_SISO_SWITCH_MIMO2_BC:
+ IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n");
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = 0;
+
+ if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
+ search_tbl->ant_type = ANT_AB;
+ else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
+ search_tbl->ant_type = ANT_AC;
+ else
+ search_tbl->ant_type = ANT_BC;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret)
+ goto out;
+ break;
+ case IWL_SISO_SWITCH_GI:
+ if (!tbl->is_ht40 && !(ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_20))
+ break;
+ if (tbl->is_ht40 && !(ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_40))
+ break;
+
+ IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n");
+
+ memcpy(search_tbl, tbl, sz);
+ if (is_green) {
+ if (!tbl->is_SGI)
+ break;
+ else
+ IWL_ERR(priv,
+ "SGI was set in GF+SISO\n");
+ }
+ search_tbl->is_SGI = !tbl->is_SGI;
+ rs_set_expected_tpt_table(lq_sta, search_tbl);
+ if (tbl->is_SGI) {
+ s32 tpt = lq_sta->last_tpt / 100;
+ if (tpt >= search_tbl->expected_tpt[index])
+ break;
+ }
+ search_tbl->current_rate =
+ rate_n_flags_from_tbl(priv, search_tbl,
+ index, is_green);
+ update_search_tbl_counter = 1;
+ goto out;
+ case IWL_SISO_SWITCH_MIMO3_ABC:
+ IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n");
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = 0;
+ search_tbl->ant_type = ANT_ABC;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret)
+ goto out;
+ break;
+ }
+ tbl->action++;
+ if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+ tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+
+ if (tbl->action == start_action)
+ break;
+ }
+ search_tbl->lq_type = LQ_NONE;
+ return;
+
+ out:
+ lq_sta->search_better_tbl = 1;
+ tbl->action++;
+ if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC)
+ tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+ if (update_search_tbl_counter)
+ search_tbl->action = tbl->action;
+}
+
+/*
+ * Try to switch to new modulation mode from MIMO2
+ */
+static void rs_move_mimo2_to_other(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_conf *conf,
+ struct ieee80211_sta *sta, int index)
+{
+ s8 is_green = lq_sta->is_green;
+ struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ struct iwl_scale_tbl_info *search_tbl =
+ &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+ struct iwl_rate_scale_data *window = &(tbl->win[index]);
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+ u8 start_action;
+ u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
+ u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ u8 update_search_tbl_counter = 0;
+ int ret;
+
+ switch (priv->bt_traffic_load) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ /* nothing */
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ /* avoid antenna B and MIMO */
+ if (tbl->action != IWL_MIMO2_SWITCH_SISO_A)
+ tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ /* avoid antenna B unless MIMO */
+ if (tbl->action == IWL_MIMO2_SWITCH_SISO_B ||
+ tbl->action == IWL_MIMO2_SWITCH_SISO_C)
+ tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+ break;
+ default:
+ IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
+ break;
+ }
+
+ if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) &&
+ (tbl->action < IWL_MIMO2_SWITCH_SISO_A ||
+ tbl->action > IWL_MIMO2_SWITCH_SISO_C)) {
+ /* switch in SISO */
+ tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+ }
+
+ /* configure as 1x1 if bt full concurrency */
+ if (priv->bt_full_concurrent &&
+ (tbl->action < IWL_MIMO2_SWITCH_SISO_A ||
+ tbl->action > IWL_MIMO2_SWITCH_SISO_C))
+ tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+
+ start_action = tbl->action;
+ for (;;) {
+ lq_sta->action_counter++;
+ switch (tbl->action) {
+ case IWL_MIMO2_SWITCH_ANTENNA1:
+ case IWL_MIMO2_SWITCH_ANTENNA2:
+ IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n");
+
+ if (tx_chains_num <= 2)
+ break;
+
+ if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+ break;
+
+ memcpy(search_tbl, tbl, sz);
+ if (rs_toggle_antenna(valid_tx_ant,
+ &search_tbl->current_rate, search_tbl)) {
+ update_search_tbl_counter = 1;
+ goto out;
+ }
+ break;
+ case IWL_MIMO2_SWITCH_SISO_A:
+ case IWL_MIMO2_SWITCH_SISO_B:
+ case IWL_MIMO2_SWITCH_SISO_C:
+ IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n");
+
+ /* Set up new search table for SISO */
+ memcpy(search_tbl, tbl, sz);
+
+ if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
+ search_tbl->ant_type = ANT_A;
+ else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
+ search_tbl->ant_type = ANT_B;
+ else
+ search_tbl->ant_type = ANT_C;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret)
+ goto out;
+
+ break;
+
+ case IWL_MIMO2_SWITCH_GI:
+ if (!tbl->is_ht40 && !(ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_20))
+ break;
+ if (tbl->is_ht40 && !(ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_40))
+ break;
+
+ IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n");
+
+ /* Set up new search table for MIMO2 */
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = !tbl->is_SGI;
+ rs_set_expected_tpt_table(lq_sta, search_tbl);
+ /*
+ * If active table already uses the fastest possible
+ * modulation (dual stream with short guard interval),
+ * and it's working well, there's no need to look
+ * for a better type of modulation!
+ */
+ if (tbl->is_SGI) {
+ s32 tpt = lq_sta->last_tpt / 100;
+ if (tpt >= search_tbl->expected_tpt[index])
+ break;
+ }
+ search_tbl->current_rate =
+ rate_n_flags_from_tbl(priv, search_tbl,
+ index, is_green);
+ update_search_tbl_counter = 1;
+ goto out;
+
+ case IWL_MIMO2_SWITCH_MIMO3_ABC:
+ IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n");
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = 0;
+ search_tbl->ant_type = ANT_ABC;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret)
+ goto out;
+
+ break;
+ }
+ tbl->action++;
+ if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+ tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+
+ if (tbl->action == start_action)
+ break;
+ }
+ search_tbl->lq_type = LQ_NONE;
+ return;
+ out:
+ lq_sta->search_better_tbl = 1;
+ tbl->action++;
+ if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+ tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+ if (update_search_tbl_counter)
+ search_tbl->action = tbl->action;
+
+}
+
+/*
+ * Try to switch to new modulation mode from MIMO3
+ */
+static void rs_move_mimo3_to_other(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_conf *conf,
+ struct ieee80211_sta *sta, int index)
+{
+ s8 is_green = lq_sta->is_green;
+ struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ struct iwl_scale_tbl_info *search_tbl =
+ &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+ struct iwl_rate_scale_data *window = &(tbl->win[index]);
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+ u8 start_action;
+ u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
+ u8 tx_chains_num = priv->hw_params.tx_chains_num;
+ int ret;
+ u8 update_search_tbl_counter = 0;
+
+ switch (priv->bt_traffic_load) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ /* nothing */
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ /* avoid antenna B and MIMO */
+ if (tbl->action != IWL_MIMO3_SWITCH_SISO_A)
+ tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ /* avoid antenna B unless MIMO */
+ if (tbl->action == IWL_MIMO3_SWITCH_SISO_B ||
+ tbl->action == IWL_MIMO3_SWITCH_SISO_C)
+ tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+ break;
+ default:
+ IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
+ break;
+ }
+
+ if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) &&
+ (tbl->action < IWL_MIMO3_SWITCH_SISO_A ||
+ tbl->action > IWL_MIMO3_SWITCH_SISO_C)) {
+ /* switch in SISO */
+ tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+ }
+
+ /* configure as 1x1 if bt full concurrency */
+ if (priv->bt_full_concurrent &&
+ (tbl->action < IWL_MIMO3_SWITCH_SISO_A ||
+ tbl->action > IWL_MIMO3_SWITCH_SISO_C))
+ tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+
+ start_action = tbl->action;
+ for (;;) {
+ lq_sta->action_counter++;
+ switch (tbl->action) {
+ case IWL_MIMO3_SWITCH_ANTENNA1:
+ case IWL_MIMO3_SWITCH_ANTENNA2:
+ IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n");
+
+ if (tx_chains_num <= 3)
+ break;
+
+ if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+ break;
+
+ memcpy(search_tbl, tbl, sz);
+ if (rs_toggle_antenna(valid_tx_ant,
+ &search_tbl->current_rate, search_tbl))
+ goto out;
+ break;
+ case IWL_MIMO3_SWITCH_SISO_A:
+ case IWL_MIMO3_SWITCH_SISO_B:
+ case IWL_MIMO3_SWITCH_SISO_C:
+ IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n");
+
+ /* Set up new search table for SISO */
+ memcpy(search_tbl, tbl, sz);
+
+ if (tbl->action == IWL_MIMO3_SWITCH_SISO_A)
+ search_tbl->ant_type = ANT_A;
+ else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B)
+ search_tbl->ant_type = ANT_B;
+ else
+ search_tbl->ant_type = ANT_C;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret)
+ goto out;
+
+ break;
+
+ case IWL_MIMO3_SWITCH_MIMO2_AB:
+ case IWL_MIMO3_SWITCH_MIMO2_AC:
+ case IWL_MIMO3_SWITCH_MIMO2_BC:
+ IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n");
+
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = 0;
+ if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB)
+ search_tbl->ant_type = ANT_AB;
+ else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC)
+ search_tbl->ant_type = ANT_AC;
+ else
+ search_tbl->ant_type = ANT_BC;
+
+ if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+ break;
+
+ ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
+ search_tbl, index);
+ if (!ret)
+ goto out;
+
+ break;
+
+ case IWL_MIMO3_SWITCH_GI:
+ if (!tbl->is_ht40 && !(ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_20))
+ break;
+ if (tbl->is_ht40 && !(ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_40))
+ break;
+
+ IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n");
+
+ /* Set up new search table for MIMO */
+ memcpy(search_tbl, tbl, sz);
+ search_tbl->is_SGI = !tbl->is_SGI;
+ rs_set_expected_tpt_table(lq_sta, search_tbl);
+ /*
+ * If active table already uses the fastest possible
+ * modulation (dual stream with short guard interval),
+ * and it's working well, there's no need to look
+ * for a better type of modulation!
+ */
+ if (tbl->is_SGI) {
+ s32 tpt = lq_sta->last_tpt / 100;
+ if (tpt >= search_tbl->expected_tpt[index])
+ break;
+ }
+ search_tbl->current_rate =
+ rate_n_flags_from_tbl(priv, search_tbl,
+ index, is_green);
+ update_search_tbl_counter = 1;
+ goto out;
+ }
+ tbl->action++;
+ if (tbl->action > IWL_MIMO3_SWITCH_GI)
+ tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
+
+ if (tbl->action == start_action)
+ break;
+ }
+ search_tbl->lq_type = LQ_NONE;
+ return;
+ out:
+ lq_sta->search_better_tbl = 1;
+ tbl->action++;
+ if (tbl->action > IWL_MIMO3_SWITCH_GI)
+ tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
+ if (update_search_tbl_counter)
+ search_tbl->action = tbl->action;
+}
+
+/*
+ * Check whether we should continue using same modulation mode, or
+ * begin search for a new mode, based on:
+ * 1) # tx successes or failures while using this mode
+ * 2) # times calling this function
+ * 3) elapsed time in this mode (not used, for now)
+ */
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
+{
+ struct iwl_scale_tbl_info *tbl;
+ int i;
+ int active_tbl;
+ int flush_interval_passed = 0;
+ struct iwl_priv *priv;
+
+ priv = lq_sta->drv;
+ active_tbl = lq_sta->active_tbl;
+
+ tbl = &(lq_sta->lq_info[active_tbl]);
+
+ /* If we've been disallowing search, see if we should now allow it */
+ if (lq_sta->stay_in_tbl) {
+
+ /* Elapsed time using current modulation mode */
+ if (lq_sta->flush_timer)
+ flush_interval_passed =
+ time_after(jiffies,
+ (unsigned long)(lq_sta->flush_timer +
+ IWL_RATE_SCALE_FLUSH_INTVL));
+
+ /*
+ * Check if we should allow search for new modulation mode.
+ * If many frames have failed or succeeded, or we've used
+ * this same modulation for a long time, allow search, and
+ * reset history stats that keep track of whether we should
+ * allow a new search. Also (below) reset all bitmaps and
+ * stats in active history.
+ */
+ if (force_search ||
+ (lq_sta->total_failed > lq_sta->max_failure_limit) ||
+ (lq_sta->total_success > lq_sta->max_success_limit) ||
+ ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer)
+ && (flush_interval_passed))) {
+ IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n",
+ lq_sta->total_failed,
+ lq_sta->total_success,
+ flush_interval_passed);
+
+ /* Allow search for new mode */
+ lq_sta->stay_in_tbl = 0; /* only place reset */
+ lq_sta->total_failed = 0;
+ lq_sta->total_success = 0;
+ lq_sta->flush_timer = 0;
+
+ /*
+ * Else if we've used this modulation mode enough repetitions
+ * (regardless of elapsed time or success/failure), reset
+ * history bitmaps and rate-specific stats for all rates in
+ * active table.
+ */
+ } else {
+ lq_sta->table_count++;
+ if (lq_sta->table_count >=
+ lq_sta->table_count_limit) {
+ lq_sta->table_count = 0;
+
+ IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n");
+ for (i = 0; i < IWL_RATE_COUNT; i++)
+ rs_rate_scale_clear_window(
+ &(tbl->win[i]));
+ }
+ }
+
+ /* If transitioning to allow "search", reset all history
+ * bitmaps and stats in active table (this will become the new
+ * "search" table). */
+ if (!lq_sta->stay_in_tbl) {
+ for (i = 0; i < IWL_RATE_COUNT; i++)
+ rs_rate_scale_clear_window(&(tbl->win[i]));
+ }
+ }
+}
+
+/*
+ * setup rate table in uCode
+ */
+static void rs_update_rate_tbl(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl,
+ int index, u8 is_green)
+{
+ u32 rate;
+
+ /* Update uCode's rate table. */
+ rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
+ rs_fill_link_cmd(priv, lq_sta, rate);
+ iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
+}
+
+/*
+ * Do rate scaling and search for new modulation mode.
+ */
+static void rs_rate_scale_perform(struct iwl_priv *priv,
+ struct sk_buff *skb,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ struct ieee80211_conf *conf = &hw->conf;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ int low = IWL_RATE_INVALID;
+ int high = IWL_RATE_INVALID;
+ int index;
+ int i;
+ struct iwl_rate_scale_data *window = NULL;
+ int current_tpt = IWL_INVALID_VALUE;
+ int low_tpt = IWL_INVALID_VALUE;
+ int high_tpt = IWL_INVALID_VALUE;
+ u32 fail_count;
+ s8 scale_action = 0;
+ u16 rate_mask;
+ u8 update_lq = 0;
+ struct iwl_scale_tbl_info *tbl, *tbl1;
+ u16 rate_scale_index_msk = 0;
+ u8 is_green = 0;
+ u8 active_tbl = 0;
+ u8 done_search = 0;
+ u16 high_low;
+ s32 sr;
+ u8 tid = IWL_MAX_TID_COUNT;
+ struct iwl_tid_data *tid_data;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ struct iwl_rxon_context *ctx = sta_priv->ctx;
+
+ IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n");
+
+ /* Send management frames and NO_ACK data using lowest rate. */
+ /* TODO: this could probably be improved.. */
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ info->flags & IEEE80211_TX_CTL_NO_ACK)
+ return;
+
+ lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
+
+ tid = rs_tl_add_packet(lq_sta, hdr);
+ if ((tid != IWL_MAX_TID_COUNT) &&
+ (lq_sta->tx_agg_tid_en & (1 << tid))) {
+ tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid];
+ if (tid_data->agg.state == IWL_AGG_OFF)
+ lq_sta->is_agg = 0;
+ else
+ lq_sta->is_agg = 1;
+ } else
+ lq_sta->is_agg = 0;
+
+ /*
+ * Select rate-scale / modulation-mode table to work with in
+ * the rest of this function: "search" if searching for better
+ * modulation mode, or "active" if doing rate scaling within a mode.
+ */
+ if (!lq_sta->search_better_tbl)
+ active_tbl = lq_sta->active_tbl;
+ else
+ active_tbl = 1 - lq_sta->active_tbl;
+
+ tbl = &(lq_sta->lq_info[active_tbl]);
+ if (is_legacy(tbl->lq_type))
+ lq_sta->is_green = 0;
+ else
+ lq_sta->is_green = rs_use_green(sta);
+ is_green = lq_sta->is_green;
+
+ /* current tx rate */
+ index = lq_sta->last_txrate_idx;
+
+ IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index,
+ tbl->lq_type);
+
+ /* rates available for this association, and for modulation mode */
+ rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type);
+
+ IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask);
+
+ /* mask with station rate restriction */
+ if (is_legacy(tbl->lq_type)) {
+ if (lq_sta->band == IEEE80211_BAND_5GHZ)
+ /* supp_rates has no CCK bits in A mode */
+ rate_scale_index_msk = (u16) (rate_mask &
+ (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
+ else
+ rate_scale_index_msk = (u16) (rate_mask &
+ lq_sta->supp_rates);
+
+ } else
+ rate_scale_index_msk = rate_mask;
+
+ if (!rate_scale_index_msk)
+ rate_scale_index_msk = rate_mask;
+
+ if (!((1 << index) & rate_scale_index_msk)) {
+ IWL_ERR(priv, "Current Rate is not valid\n");
+ if (lq_sta->search_better_tbl) {
+ /* revert to active table if search table is not valid*/
+ tbl->lq_type = LQ_NONE;
+ lq_sta->search_better_tbl = 0;
+ tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ /* get "active" rate info */
+ index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+ rs_update_rate_tbl(priv, ctx, lq_sta, tbl,
+ index, is_green);
+ }
+ return;
+ }
+
+ /* Get expected throughput table and history window for current rate */
+ if (!tbl->expected_tpt) {
+ IWL_ERR(priv, "tbl->expected_tpt is NULL\n");
+ return;
+ }
+
+ /* force user max rate if set by user */
+ if ((lq_sta->max_rate_idx != -1) &&
+ (lq_sta->max_rate_idx < index)) {
+ index = lq_sta->max_rate_idx;
+ update_lq = 1;
+ window = &(tbl->win[index]);
+ goto lq_update;
+ }
+
+ window = &(tbl->win[index]);
+
+ /*
+ * If there is not enough history to calculate actual average
+ * throughput, keep analyzing results of more tx frames, without
+ * changing rate or mode (bypass most of the rest of this function).
+ * Set up new rate table in uCode only if old rate is not supported
+ * in current association (use new rate found above).
+ */
+ fail_count = window->counter - window->success_counter;
+ if ((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
+ (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) {
+ IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d "
+ "for index %d\n",
+ window->success_counter, window->counter, index);
+
+ /* Can't calculate this yet; not enough history */
+ window->average_tpt = IWL_INVALID_VALUE;
+
+ /* Should we stay with this modulation mode,
+ * or search for a new one? */
+ rs_stay_in_table(lq_sta, false);
+
+ goto out;
+ }
+ /* Else we have enough samples; calculate estimate of
+ * actual average throughput */
+ if (window->average_tpt != ((window->success_ratio *
+ tbl->expected_tpt[index] + 64) / 128)) {
+ IWL_ERR(priv, "expected_tpt should have been calculated by now\n");
+ window->average_tpt = ((window->success_ratio *
+ tbl->expected_tpt[index] + 64) / 128);
+ }
+
+ /* If we are searching for better modulation mode, check success. */
+ if (lq_sta->search_better_tbl &&
+ (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) {
+ /* If good success, continue using the "search" mode;
+ * no need to send new link quality command, since we're
+ * continuing to use the setup that we've been trying. */
+ if (window->average_tpt > lq_sta->last_tpt) {
+
+ IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE "
+ "suc=%d cur-tpt=%d old-tpt=%d\n",
+ window->success_ratio,
+ window->average_tpt,
+ lq_sta->last_tpt);
+
+ if (!is_legacy(tbl->lq_type))
+ lq_sta->enable_counter = 1;
+
+ /* Swap tables; "search" becomes "active" */
+ lq_sta->active_tbl = active_tbl;
+ current_tpt = window->average_tpt;
+
+ /* Else poor success; go back to mode in "active" table */
+ } else {
+
+ IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE "
+ "suc=%d cur-tpt=%d old-tpt=%d\n",
+ window->success_ratio,
+ window->average_tpt,
+ lq_sta->last_tpt);
+
+ /* Nullify "search" table */
+ tbl->lq_type = LQ_NONE;
+
+ /* Revert to "active" table */
+ active_tbl = lq_sta->active_tbl;
+ tbl = &(lq_sta->lq_info[active_tbl]);
+
+ /* Revert to "active" rate and throughput info */
+ index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+ current_tpt = lq_sta->last_tpt;
+
+ /* Need to set up a new rate table in uCode */
+ update_lq = 1;
+ }
+
+ /* Either way, we've made a decision; modulation mode
+ * search is done, allow rate adjustment next time. */
+ lq_sta->search_better_tbl = 0;
+ done_search = 1; /* Don't switch modes below! */
+ goto lq_update;
+ }
+
+ /* (Else) not in search of better modulation mode, try for better
+ * starting rate, while staying in this mode. */
+ high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk,
+ tbl->lq_type);
+ low = high_low & 0xff;
+ high = (high_low >> 8) & 0xff;
+
+ /* If user set max rate, dont allow higher than user constrain */
+ if ((lq_sta->max_rate_idx != -1) &&
+ (lq_sta->max_rate_idx < high))
+ high = IWL_RATE_INVALID;
+
+ sr = window->success_ratio;
+
+ /* Collect measured throughputs for current and adjacent rates */
+ current_tpt = window->average_tpt;
+ if (low != IWL_RATE_INVALID)
+ low_tpt = tbl->win[low].average_tpt;
+ if (high != IWL_RATE_INVALID)
+ high_tpt = tbl->win[high].average_tpt;
+
+ scale_action = 0;
+
+ /* Too many failures, decrease rate */
+ if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) {
+ IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n");
+ scale_action = -1;
+
+ /* No throughput measured yet for adjacent rates; try increase. */
+ } else if ((low_tpt == IWL_INVALID_VALUE) &&
+ (high_tpt == IWL_INVALID_VALUE)) {
+
+ if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH)
+ scale_action = 1;
+ else if (low != IWL_RATE_INVALID)
+ scale_action = 0;
+ }
+
+ /* Both adjacent throughputs are measured, but neither one has better
+ * throughput; we're using the best rate, don't change it! */
+ else if ((low_tpt != IWL_INVALID_VALUE) &&
+ (high_tpt != IWL_INVALID_VALUE) &&
+ (low_tpt < current_tpt) &&
+ (high_tpt < current_tpt))
+ scale_action = 0;
+
+ /* At least one adjacent rate's throughput is measured,
+ * and may have better performance. */
+ else {
+ /* Higher adjacent rate's throughput is measured */
+ if (high_tpt != IWL_INVALID_VALUE) {
+ /* Higher rate has better throughput */
+ if (high_tpt > current_tpt &&
+ sr >= IWL_RATE_INCREASE_TH) {
+ scale_action = 1;
+ } else {
+ scale_action = 0;
+ }
+
+ /* Lower adjacent rate's throughput is measured */
+ } else if (low_tpt != IWL_INVALID_VALUE) {
+ /* Lower rate has better throughput */
+ if (low_tpt > current_tpt) {
+ IWL_DEBUG_RATE(priv,
+ "decrease rate because of low tpt\n");
+ scale_action = -1;
+ } else if (sr >= IWL_RATE_INCREASE_TH) {
+ scale_action = 1;
+ }
+ }
+ }
+
+ /* Sanity check; asked for decrease, but success rate or throughput
+ * has been good at old rate. Don't change it. */
+ if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
+ ((sr > IWL_RATE_HIGH_TH) ||
+ (current_tpt > (100 * tbl->expected_tpt[low]))))
+ scale_action = 0;
+ if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type))
+ scale_action = -1;
+ if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI &&
+ (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type)))
+ scale_action = -1;
+
+ if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
+ (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+ if (lq_sta->last_bt_traffic > priv->bt_traffic_load) {
+ /*
+ * don't set scale_action, don't want to scale up if
+ * the rate scale doesn't otherwise think that is a
+ * good idea.
+ */
+ } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) {
+ scale_action = -1;
+ }
+ }
+ lq_sta->last_bt_traffic = priv->bt_traffic_load;
+
+ if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
+ (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+ /* search for a new modulation */
+ rs_stay_in_table(lq_sta, true);
+ goto lq_update;
+ }
+
+ switch (scale_action) {
+ case -1:
+ /* Decrease starting rate, update uCode's rate table */
+ if (low != IWL_RATE_INVALID) {
+ update_lq = 1;
+ index = low;
+ }
+
+ break;
+ case 1:
+ /* Increase starting rate, update uCode's rate table */
+ if (high != IWL_RATE_INVALID) {
+ update_lq = 1;
+ index = high;
+ }
+
+ break;
+ case 0:
+ /* No change */
+ default:
+ break;
+ }
+
+ IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d "
+ "high %d type %d\n",
+ index, scale_action, low, high, tbl->lq_type);
+
+lq_update:
+ /* Replace uCode's rate table for the destination station. */
+ if (update_lq)
+ rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green);
+
+ if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) {
+ /* Should we stay with this modulation mode,
+ * or search for a new one? */
+ rs_stay_in_table(lq_sta, false);
+ }
+ /*
+ * Search for new modulation mode if we're:
+ * 1) Not changing rates right now
+ * 2) Not just finishing up a search
+ * 3) Allowing a new search
+ */
+ if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) {
+ /* Save current throughput to compare with "search" throughput*/
+ lq_sta->last_tpt = current_tpt;
+
+ /* Select a new "search" modulation mode to try.
+ * If one is found, set up the new "search" table. */
+ if (is_legacy(tbl->lq_type))
+ rs_move_legacy_other(priv, lq_sta, conf, sta, index);
+ else if (is_siso(tbl->lq_type))
+ rs_move_siso_to_other(priv, lq_sta, conf, sta, index);
+ else if (is_mimo2(tbl->lq_type))
+ rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index);
+ else
+ rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index);
+
+ /* If new "search" mode was selected, set up in uCode table */
+ if (lq_sta->search_better_tbl) {
+ /* Access the "search" table, clear its history. */
+ tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+ for (i = 0; i < IWL_RATE_COUNT; i++)
+ rs_rate_scale_clear_window(&(tbl->win[i]));
+
+ /* Use new "search" start rate */
+ index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+
+ IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n",
+ tbl->current_rate, index);
+ rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
+ iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
+ } else
+ done_search = 1;
+ }
+
+ if (done_search && !lq_sta->stay_in_tbl) {
+ /* If the "active" (non-search) mode was legacy,
+ * and we've tried switching antennas,
+ * but we haven't been able to try HT modes (not available),
+ * stay with best antenna legacy modulation for a while
+ * before next round of mode comparisons. */
+ tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) &&
+ lq_sta->action_counter > tbl1->max_search) {
+ IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n");
+ rs_set_stay_in_table(priv, 1, lq_sta);
+ }
+
+ /* If we're in an HT mode, and all 3 mode switch actions
+ * have been tried and compared, stay in this best modulation
+ * mode for a while before next round of mode comparisons. */
+ if (lq_sta->enable_counter &&
+ (lq_sta->action_counter >= tbl1->max_search) &&
+ iwl_ht_enabled(priv)) {
+ if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
+ (lq_sta->tx_agg_tid_en & (1 << tid)) &&
+ (tid != IWL_MAX_TID_COUNT)) {
+ u8 sta_id = lq_sta->lq.sta_id;
+ tid_data = &priv->tid_data[sta_id][tid];
+ if (tid_data->agg.state == IWL_AGG_OFF) {
+ IWL_DEBUG_RATE(priv,
+ "try to aggregate tid %d\n",
+ tid);
+ rs_tl_turn_on_agg(priv, tid,
+ lq_sta, sta);
+ }
+ }
+ rs_set_stay_in_table(priv, 0, lq_sta);
+ }
+ }
+
+out:
+ tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
+ lq_sta->last_txrate_idx = index;
+}
+
+/**
+ * rs_initialize_lq - Initialize a station's hardware rate table
+ *
+ * The uCode's station table contains a table of fallback rates
+ * for automatic fallback during transmission.
+ *
+ * NOTE: This sets up a default set of values. These will be replaced later
+ * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
+ * rc80211_simple.
+ *
+ * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
+ * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
+ * which requires station table entry to exist).
+ */
+static void rs_initialize_lq(struct iwl_priv *priv,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta)
+{
+ struct iwl_scale_tbl_info *tbl;
+ int rate_idx;
+ int i;
+ u32 rate;
+ u8 use_green = rs_use_green(sta);
+ u8 active_tbl = 0;
+ u8 valid_tx_ant;
+ struct iwl_station_priv *sta_priv;
+ struct iwl_rxon_context *ctx;
+
+ if (!sta || !lq_sta)
+ return;
+
+ sta_priv = (void *)sta->drv_priv;
+ ctx = sta_priv->ctx;
+
+ i = lq_sta->last_txrate_idx;
+
+ valid_tx_ant = priv->nvm_data->valid_tx_ant;
+
+ if (!lq_sta->search_better_tbl)
+ active_tbl = lq_sta->active_tbl;
+ else
+ active_tbl = 1 - lq_sta->active_tbl;
+
+ tbl = &(lq_sta->lq_info[active_tbl]);
+
+ if ((i < 0) || (i >= IWL_RATE_COUNT))
+ i = 0;
+
+ rate = iwl_rates[i].plcp;
+ tbl->ant_type = first_antenna(valid_tx_ant);
+ rate |= tbl->ant_type << RATE_MCS_ANT_POS;
+
+ if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE)
+ rate |= RATE_MCS_CCK_MSK;
+
+ rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx);
+ if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type))
+ rs_toggle_antenna(valid_tx_ant, &rate, tbl);
+
+ rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green);
+ tbl->current_rate = rate;
+ rs_set_expected_tpt_table(lq_sta, tbl);
+ rs_fill_link_cmd(NULL, lq_sta, rate);
+ priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq;
+ iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true);
+}
+
+static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
+{
+
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
+ struct iwl_op_mode *op_mode __maybe_unused =
+ (struct iwl_op_mode *)priv_r;
+ struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ int rate_idx;
+
+ IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n");
+
+ /* Get max rate if user set max rate */
+ if (lq_sta) {
+ lq_sta->max_rate_idx = txrc->max_rate_idx;
+ if ((sband->band == IEEE80211_BAND_5GHZ) &&
+ (lq_sta->max_rate_idx != -1))
+ lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE;
+ if ((lq_sta->max_rate_idx < 0) ||
+ (lq_sta->max_rate_idx >= IWL_RATE_COUNT))
+ lq_sta->max_rate_idx = -1;
+ }
+
+ /* Treat uninitialized rate scaling data same as non-existing. */
+ if (lq_sta && !lq_sta->drv) {
+ IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
+ priv_sta = NULL;
+ }
+
+ /* Send management frames and NO_ACK data using lowest rate. */
+ if (rate_control_send_low(sta, priv_sta, txrc))
+ return;
+
+ rate_idx = lq_sta->last_txrate_idx;
+
+ if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
+ rate_idx -= IWL_FIRST_OFDM_RATE;
+ /* 6M and 9M shared same MCS index */
+ rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
+ if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+ IWL_RATE_MIMO3_6M_PLCP)
+ rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
+ else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+ IWL_RATE_MIMO2_6M_PLCP)
+ rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
+ info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
+ if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
+ info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
+ if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK)
+ info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA;
+ if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK)
+ info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK)
+ info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
+ } else {
+ /* Check for invalid rates */
+ if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) ||
+ ((sband->band == IEEE80211_BAND_5GHZ) &&
+ (rate_idx < IWL_FIRST_OFDM_RATE)))
+ rate_idx = rate_lowest_index(sband, sta);
+ /* On valid 5 GHz rate, adjust index */
+ else if (sband->band == IEEE80211_BAND_5GHZ)
+ rate_idx -= IWL_FIRST_OFDM_RATE;
+ info->control.rates[0].flags = 0;
+ }
+ info->control.rates[0].idx = rate_idx;
+ info->control.rates[0].count = 1;
+}
+
+static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
+ gfp_t gfp)
+{
+ struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv;
+ struct iwl_op_mode *op_mode __maybe_unused =
+ (struct iwl_op_mode *)priv_rate;
+ struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode);
+
+ IWL_DEBUG_RATE(priv, "create station rate scale window\n");
+
+ return &sta_priv->lq_sta;
+}
+
+/*
+ * Called after adding a new station to initialize rate scaling
+ */
+void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id)
+{
+ int i, j;
+ struct ieee80211_hw *hw = priv->hw;
+ struct ieee80211_conf *conf = &priv->hw->conf;
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ struct iwl_station_priv *sta_priv;
+ struct iwl_lq_sta *lq_sta;
+ struct ieee80211_supported_band *sband;
+ unsigned long supp; /* must be unsigned long for for_each_set_bit */
+
+ sta_priv = (struct iwl_station_priv *) sta->drv_priv;
+ lq_sta = &sta_priv->lq_sta;
+ sband = hw->wiphy->bands[conf->chandef.chan->band];
+
+
+ lq_sta->lq.sta_id = sta_id;
+
+ for (j = 0; j < LQ_SIZE; j++)
+ for (i = 0; i < IWL_RATE_COUNT; i++)
+ rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+
+ lq_sta->flush_timer = 0;
+ lq_sta->supp_rates = sta->supp_rates[sband->band];
+
+ IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n",
+ sta_id);
+ /* TODO: what is a good starting rate for STA? About middle? Maybe not
+ * the lowest or the highest rate.. Could consider using RSSI from
+ * previous packets? Need to have IEEE 802.1X auth succeed immediately
+ * after assoc.. */
+
+ lq_sta->is_dup = 0;
+ lq_sta->max_rate_idx = -1;
+ lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
+ lq_sta->is_green = rs_use_green(sta);
+ lq_sta->band = sband->band;
+ /*
+ * active legacy rates as per supported rates bitmap
+ */
+ supp = sta->supp_rates[sband->band];
+ lq_sta->active_legacy_rate = 0;
+ for_each_set_bit(i, &supp, BITS_PER_LONG)
+ lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
+
+ /*
+ * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3),
+ * supp_rates[] does not; shift to convert format, force 9 MBits off.
+ */
+ lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
+ lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
+ lq_sta->active_siso_rate &= ~((u16)0x2);
+ lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+
+ /* Same here */
+ lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
+ lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
+ lq_sta->active_mimo2_rate &= ~((u16)0x2);
+ lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+
+ lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1;
+ lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1;
+ lq_sta->active_mimo3_rate &= ~((u16)0x2);
+ lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE;
+
+ IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n",
+ lq_sta->active_siso_rate,
+ lq_sta->active_mimo2_rate,
+ lq_sta->active_mimo3_rate);
+
+ /* These values will be overridden later */
+ lq_sta->lq.general_params.single_stream_ant_msk =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+ lq_sta->lq.general_params.dual_stream_ant_msk =
+ priv->nvm_data->valid_tx_ant &
+ ~first_antenna(priv->nvm_data->valid_tx_ant);
+ if (!lq_sta->lq.general_params.dual_stream_ant_msk) {
+ lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB;
+ } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) {
+ lq_sta->lq.general_params.dual_stream_ant_msk =
+ priv->nvm_data->valid_tx_ant;
+ }
+
+ /* as default allow aggregation for all tids */
+ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
+ lq_sta->drv = priv;
+
+ /* Set last_txrate_idx to lowest rate */
+ lq_sta->last_txrate_idx = rate_lowest_index(sband, sta);
+ if (sband->band == IEEE80211_BAND_5GHZ)
+ lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
+ lq_sta->is_agg = 0;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ lq_sta->dbg_fixed_rate = 0;
+#endif
+
+ rs_initialize_lq(priv, sta, lq_sta);
+}
+
+static void rs_fill_link_cmd(struct iwl_priv *priv,
+ struct iwl_lq_sta *lq_sta, u32 new_rate)
+{
+ struct iwl_scale_tbl_info tbl_type;
+ int index = 0;
+ int rate_idx;
+ int repeat_rate = 0;
+ u8 ant_toggle_cnt = 0;
+ u8 use_ht_possible = 1;
+ u8 valid_tx_ant = 0;
+ struct iwl_station_priv *sta_priv =
+ container_of(lq_sta, struct iwl_station_priv, lq_sta);
+ struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq;
+
+ /* Override starting rate (index 0) if needed for debug purposes */
+ rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+ /* Interpret new_rate (rate_n_flags) */
+ rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
+ &tbl_type, &rate_idx);
+
+ if (priv && priv->bt_full_concurrent) {
+ /* 1x1 only */
+ tbl_type.ant_type =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+ }
+
+ /* How many times should we repeat the initial rate? */
+ if (is_legacy(tbl_type.lq_type)) {
+ ant_toggle_cnt = 1;
+ repeat_rate = IWL_NUMBER_TRY;
+ } else {
+ repeat_rate = min(IWL_HT_NUMBER_TRY,
+ LINK_QUAL_AGG_DISABLE_START_DEF - 1);
+ }
+
+ lq_cmd->general_params.mimo_delimiter =
+ is_mimo(tbl_type.lq_type) ? 1 : 0;
+
+ /* Fill 1st table entry (index 0) */
+ lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate);
+
+ if (num_of_ant(tbl_type.ant_type) == 1) {
+ lq_cmd->general_params.single_stream_ant_msk =
+ tbl_type.ant_type;
+ } else if (num_of_ant(tbl_type.ant_type) == 2) {
+ lq_cmd->general_params.dual_stream_ant_msk =
+ tbl_type.ant_type;
+ } /* otherwise we don't modify the existing value */
+
+ index++;
+ repeat_rate--;
+ if (priv) {
+ if (priv->bt_full_concurrent)
+ valid_tx_ant = ANT_A;
+ else
+ valid_tx_ant = priv->nvm_data->valid_tx_ant;
+ }
+
+ /* Fill rest of rate table */
+ while (index < LINK_QUAL_MAX_RETRY_NUM) {
+ /* Repeat initial/next rate.
+ * For legacy IWL_NUMBER_TRY == 1, this loop will not execute.
+ * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */
+ while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) {
+ if (is_legacy(tbl_type.lq_type)) {
+ if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
+ ant_toggle_cnt++;
+ else if (priv &&
+ rs_toggle_antenna(valid_tx_ant,
+ &new_rate, &tbl_type))
+ ant_toggle_cnt = 1;
+ }
+
+ /* Override next rate if needed for debug purposes */
+ rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+ /* Fill next table entry */
+ lq_cmd->rs_table[index].rate_n_flags =
+ cpu_to_le32(new_rate);
+ repeat_rate--;
+ index++;
+ }
+
+ rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type,
+ &rate_idx);
+
+ if (priv && priv->bt_full_concurrent) {
+ /* 1x1 only */
+ tbl_type.ant_type =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+ }
+
+ /* Indicate to uCode which entries might be MIMO.
+ * If initial rate was MIMO, this will finally end up
+ * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
+ if (is_mimo(tbl_type.lq_type))
+ lq_cmd->general_params.mimo_delimiter = index;
+
+ /* Get next rate */
+ new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx,
+ use_ht_possible);
+
+ /* How many times should we repeat the next rate? */
+ if (is_legacy(tbl_type.lq_type)) {
+ if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
+ ant_toggle_cnt++;
+ else if (priv &&
+ rs_toggle_antenna(valid_tx_ant,
+ &new_rate, &tbl_type))
+ ant_toggle_cnt = 1;
+
+ repeat_rate = IWL_NUMBER_TRY;
+ } else {
+ repeat_rate = IWL_HT_NUMBER_TRY;
+ }
+
+ /* Don't allow HT rates after next pass.
+ * rs_get_lower_rate() will change type to LQ_A or LQ_G. */
+ use_ht_possible = 0;
+
+ /* Override next rate if needed for debug purposes */
+ rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+ /* Fill next table entry */
+ lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate);
+
+ index++;
+ repeat_rate--;
+ }
+
+ lq_cmd->agg_params.agg_frame_cnt_limit =
+ sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+ lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+
+ lq_cmd->agg_params.agg_time_limit =
+ cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+ /*
+ * overwrite if needed, pass aggregation time limit
+ * to uCode in uSec
+ */
+ if (priv && priv->lib->bt_params &&
+ priv->lib->bt_params->agg_time_limit &&
+ priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
+ lq_cmd->agg_params.agg_time_limit =
+ cpu_to_le16(priv->lib->bt_params->agg_time_limit);
+}
+
+static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+{
+ return hw->priv;
+}
+/* rate scale requires free function to be implemented */
+static void rs_free(void *priv_rate)
+{
+ return;
+}
+
+static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta,
+ void *priv_sta)
+{
+ struct iwl_op_mode *op_mode __maybe_unused = priv_r;
+ struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode);
+
+ IWL_DEBUG_RATE(priv, "enter\n");
+ IWL_DEBUG_RATE(priv, "leave\n");
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+ u32 *rate_n_flags, int index)
+{
+ struct iwl_priv *priv;
+ u8 valid_tx_ant;
+ u8 ant_sel_tx;
+
+ priv = lq_sta->drv;
+ valid_tx_ant = priv->nvm_data->valid_tx_ant;
+ if (lq_sta->dbg_fixed_rate) {
+ ant_sel_tx =
+ ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
+ >> RATE_MCS_ANT_POS);
+ if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) {
+ *rate_n_flags = lq_sta->dbg_fixed_rate;
+ IWL_DEBUG_RATE(priv, "Fixed rate ON\n");
+ } else {
+ lq_sta->dbg_fixed_rate = 0;
+ IWL_ERR(priv,
+ "Invalid antenna selection 0x%X, Valid is 0x%X\n",
+ ant_sel_tx, valid_tx_ant);
+ IWL_DEBUG_RATE(priv, "Fixed rate OFF\n");
+ }
+ } else {
+ IWL_DEBUG_RATE(priv, "Fixed rate OFF\n");
+ }
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ struct iwl_priv *priv;
+ char buf[64];
+ size_t buf_size;
+ u32 parsed_rate;
+
+
+ priv = lq_sta->drv;
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (sscanf(buf, "%x", &parsed_rate) == 1)
+ lq_sta->dbg_fixed_rate = parsed_rate;
+ else
+ lq_sta->dbg_fixed_rate = 0;
+
+ rs_program_fix_rate(priv, lq_sta);
+
+ return count;
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char *buff;
+ int desc = 0;
+ int i = 0;
+ int index = 0;
+ ssize_t ret;
+
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ struct iwl_priv *priv;
+ struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+
+ priv = lq_sta->drv;
+ buff = kmalloc(1024, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
+ desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n",
+ lq_sta->total_failed, lq_sta->total_success,
+ lq_sta->active_legacy_rate);
+ desc += sprintf(buff+desc, "fixed rate 0x%X\n",
+ lq_sta->dbg_fixed_rate);
+ desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
+ (priv->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "",
+ (priv->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "",
+ (priv->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : "");
+ desc += sprintf(buff+desc, "lq type %s\n",
+ (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
+ if (is_Ht(tbl->lq_type)) {
+ desc += sprintf(buff + desc, " %s",
+ (is_siso(tbl->lq_type)) ? "SISO" :
+ ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
+ desc += sprintf(buff + desc, " %s",
+ (tbl->is_ht40) ? "40MHz" : "20MHz");
+ desc += sprintf(buff + desc, " %s %s %s\n",
+ (tbl->is_SGI) ? "SGI" : "",
+ (lq_sta->is_green) ? "GF enabled" : "",
+ (lq_sta->is_agg) ? "AGG on" : "");
+ }
+ desc += sprintf(buff+desc, "last tx rate=0x%X\n",
+ lq_sta->last_rate_n_flags);
+ desc += sprintf(buff+desc, "general:"
+ "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n",
+ lq_sta->lq.general_params.flags,
+ lq_sta->lq.general_params.mimo_delimiter,
+ lq_sta->lq.general_params.single_stream_ant_msk,
+ lq_sta->lq.general_params.dual_stream_ant_msk);
+
+ desc += sprintf(buff+desc, "agg:"
+ "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n",
+ le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit),
+ lq_sta->lq.agg_params.agg_dis_start_th,
+ lq_sta->lq.agg_params.agg_frame_cnt_limit);
+
+ desc += sprintf(buff+desc,
+ "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
+ lq_sta->lq.general_params.start_rate_index[0],
+ lq_sta->lq.general_params.start_rate_index[1],
+ lq_sta->lq.general_params.start_rate_index[2],
+ lq_sta->lq.general_params.start_rate_index[3]);
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ index = iwl_hwrate_to_plcp_idx(
+ le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags));
+ if (is_legacy(tbl->lq_type)) {
+ desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n",
+ i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags),
+ iwl_rate_mcs[index].mbps);
+ } else {
+ desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n",
+ i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags),
+ iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs);
+ }
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+ kfree(buff);
+ return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
+ .write = rs_sta_dbgfs_scale_table_write,
+ .read = rs_sta_dbgfs_scale_table_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char *buff;
+ int desc = 0;
+ int i, j;
+ ssize_t ret;
+
+ struct iwl_lq_sta *lq_sta = file->private_data;
+
+ buff = kmalloc(1024, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ for (i = 0; i < LQ_SIZE; i++) {
+ desc += sprintf(buff+desc,
+ "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n"
+ "rate=0x%X\n",
+ lq_sta->active_tbl == i ? "*" : "x",
+ lq_sta->lq_info[i].lq_type,
+ lq_sta->lq_info[i].is_SGI,
+ lq_sta->lq_info[i].is_ht40,
+ lq_sta->lq_info[i].is_dup,
+ lq_sta->is_green,
+ lq_sta->lq_info[i].current_rate);
+ for (j = 0; j < IWL_RATE_COUNT; j++) {
+ desc += sprintf(buff+desc,
+ "counter=%d success=%d %%=%d\n",
+ lq_sta->lq_info[i].win[j].counter,
+ lq_sta->lq_info[i].win[j].success_counter,
+ lq_sta->lq_info[i].win[j].success_ratio);
+ }
+ }
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+ kfree(buff);
+ return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
+ .read = rs_sta_dbgfs_stats_table_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
+ char buff[120];
+ int desc = 0;
+
+ if (is_Ht(tbl->lq_type))
+ desc += sprintf(buff+desc,
+ "Bit Rate= %d Mb/s\n",
+ tbl->expected_tpt[lq_sta->last_txrate_idx]);
+ else
+ desc += sprintf(buff+desc,
+ "Bit Rate= %d Mb/s\n",
+ iwl_rates[lq_sta->last_txrate_idx].ieee >> 1);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+}
+
+static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
+ .read = rs_sta_dbgfs_rate_scale_data_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static void rs_add_debugfs(void *priv, void *priv_sta,
+ struct dentry *dir)
+{
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ lq_sta->rs_sta_dbgfs_scale_table_file =
+ debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+ lq_sta, &rs_sta_dbgfs_scale_table_ops);
+ lq_sta->rs_sta_dbgfs_stats_table_file =
+ debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+ lq_sta, &rs_sta_dbgfs_stats_table_ops);
+ lq_sta->rs_sta_dbgfs_rate_scale_data_file =
+ debugfs_create_file("rate_scale_data", S_IRUSR, dir,
+ lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
+ lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
+ debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+ &lq_sta->tx_agg_tid_en);
+
+}
+
+static void rs_remove_debugfs(void *priv, void *priv_sta)
+{
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
+ debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
+ debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file);
+ debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
+}
+#endif
+
+/*
+ * Initialization of rate scaling information is done by driver after
+ * the station is added. Since mac80211 calls this function before a
+ * station is added we ignore it.
+ */
+static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *priv_sta)
+{
+}
+
+static const struct rate_control_ops rs_ops = {
+ .name = RS_NAME,
+ .tx_status = rs_tx_status,
+ .get_rate = rs_get_rate,
+ .rate_init = rs_rate_init_stub,
+ .alloc = rs_alloc,
+ .free = rs_free,
+ .alloc_sta = rs_alloc_sta,
+ .free_sta = rs_free_sta,
+#ifdef CONFIG_MAC80211_DEBUGFS
+ .add_sta_debugfs = rs_add_debugfs,
+ .remove_sta_debugfs = rs_remove_debugfs,
+#endif
+};
+
+int iwlagn_rate_control_register(void)
+{
+ return ieee80211_rate_control_register(&rs_ops);
+}
+
+void iwlagn_rate_control_unregister(void)
+{
+ ieee80211_rate_control_unregister(&rs_ops);
+}
+
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.h b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h
new file mode 100644
index 000000000000..c5fe44584613
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h
@@ -0,0 +1,426 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_agn_rs_h__
+#define __iwl_agn_rs_h__
+
+#include <net/mac80211.h>
+
+#include "iwl-config.h"
+
+#include "commands.h"
+
+struct iwl_rate_info {
+ u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
+ u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
+ u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
+ u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */
+ u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
+ u8 prev_ieee; /* previous rate in IEEE speeds */
+ u8 next_ieee; /* next rate in IEEE speeds */
+ u8 prev_rs; /* previous rate used in rs algo */
+ u8 next_rs; /* next rate used in rs algo */
+ u8 prev_rs_tgg; /* previous rate used in TGG rs algo */
+ u8 next_rs_tgg; /* next rate used in TGG rs algo */
+};
+
+/*
+ * These serve as indexes into
+ * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
+ */
+enum {
+ IWL_RATE_1M_INDEX = 0,
+ IWL_RATE_2M_INDEX,
+ IWL_RATE_5M_INDEX,
+ IWL_RATE_11M_INDEX,
+ IWL_RATE_6M_INDEX,
+ IWL_RATE_9M_INDEX,
+ IWL_RATE_12M_INDEX,
+ IWL_RATE_18M_INDEX,
+ IWL_RATE_24M_INDEX,
+ IWL_RATE_36M_INDEX,
+ IWL_RATE_48M_INDEX,
+ IWL_RATE_54M_INDEX,
+ IWL_RATE_60M_INDEX,
+ IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/
+ IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */
+ IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
+ IWL_RATE_INVALID = IWL_RATE_COUNT,
+};
+
+enum {
+ IWL_RATE_6M_INDEX_TABLE = 0,
+ IWL_RATE_9M_INDEX_TABLE,
+ IWL_RATE_12M_INDEX_TABLE,
+ IWL_RATE_18M_INDEX_TABLE,
+ IWL_RATE_24M_INDEX_TABLE,
+ IWL_RATE_36M_INDEX_TABLE,
+ IWL_RATE_48M_INDEX_TABLE,
+ IWL_RATE_54M_INDEX_TABLE,
+ IWL_RATE_1M_INDEX_TABLE,
+ IWL_RATE_2M_INDEX_TABLE,
+ IWL_RATE_5M_INDEX_TABLE,
+ IWL_RATE_11M_INDEX_TABLE,
+ IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1,
+};
+
+enum {
+ IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+ IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
+ IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
+ IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
+};
+
+/* #define vs. enum to keep from defaulting to 'large integer' */
+#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX)
+#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX)
+#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX)
+#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX)
+#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX)
+#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX)
+#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX)
+#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX)
+#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX)
+#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX)
+#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX)
+#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX)
+#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX)
+
+/* uCode API values for legacy bit rates, both OFDM and CCK */
+enum {
+ IWL_RATE_6M_PLCP = 13,
+ IWL_RATE_9M_PLCP = 15,
+ IWL_RATE_12M_PLCP = 5,
+ IWL_RATE_18M_PLCP = 7,
+ IWL_RATE_24M_PLCP = 9,
+ IWL_RATE_36M_PLCP = 11,
+ IWL_RATE_48M_PLCP = 1,
+ IWL_RATE_54M_PLCP = 3,
+ IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/
+ IWL_RATE_1M_PLCP = 10,
+ IWL_RATE_2M_PLCP = 20,
+ IWL_RATE_5M_PLCP = 55,
+ IWL_RATE_11M_PLCP = 110,
+ /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */
+ /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/
+};
+
+/* uCode API values for OFDM high-throughput (HT) bit rates */
+enum {
+ IWL_RATE_SISO_6M_PLCP = 0,
+ IWL_RATE_SISO_12M_PLCP = 1,
+ IWL_RATE_SISO_18M_PLCP = 2,
+ IWL_RATE_SISO_24M_PLCP = 3,
+ IWL_RATE_SISO_36M_PLCP = 4,
+ IWL_RATE_SISO_48M_PLCP = 5,
+ IWL_RATE_SISO_54M_PLCP = 6,
+ IWL_RATE_SISO_60M_PLCP = 7,
+ IWL_RATE_MIMO2_6M_PLCP = 0x8,
+ IWL_RATE_MIMO2_12M_PLCP = 0x9,
+ IWL_RATE_MIMO2_18M_PLCP = 0xa,
+ IWL_RATE_MIMO2_24M_PLCP = 0xb,
+ IWL_RATE_MIMO2_36M_PLCP = 0xc,
+ IWL_RATE_MIMO2_48M_PLCP = 0xd,
+ IWL_RATE_MIMO2_54M_PLCP = 0xe,
+ IWL_RATE_MIMO2_60M_PLCP = 0xf,
+ IWL_RATE_MIMO3_6M_PLCP = 0x10,
+ IWL_RATE_MIMO3_12M_PLCP = 0x11,
+ IWL_RATE_MIMO3_18M_PLCP = 0x12,
+ IWL_RATE_MIMO3_24M_PLCP = 0x13,
+ IWL_RATE_MIMO3_36M_PLCP = 0x14,
+ IWL_RATE_MIMO3_48M_PLCP = 0x15,
+ IWL_RATE_MIMO3_54M_PLCP = 0x16,
+ IWL_RATE_MIMO3_60M_PLCP = 0x17,
+ IWL_RATE_SISO_INVM_PLCP,
+ IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+ IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+};
+
+/* MAC header values for bit rates */
+enum {
+ IWL_RATE_6M_IEEE = 12,
+ IWL_RATE_9M_IEEE = 18,
+ IWL_RATE_12M_IEEE = 24,
+ IWL_RATE_18M_IEEE = 36,
+ IWL_RATE_24M_IEEE = 48,
+ IWL_RATE_36M_IEEE = 72,
+ IWL_RATE_48M_IEEE = 96,
+ IWL_RATE_54M_IEEE = 108,
+ IWL_RATE_60M_IEEE = 120,
+ IWL_RATE_1M_IEEE = 2,
+ IWL_RATE_2M_IEEE = 4,
+ IWL_RATE_5M_IEEE = 11,
+ IWL_RATE_11M_IEEE = 22,
+};
+
+#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
+
+#define IWL_INVALID_VALUE -1
+
+#define IWL_MIN_RSSI_VAL -100
+#define IWL_MAX_RSSI_VAL 0
+
+/* These values specify how many Tx frame attempts before
+ * searching for a new modulation mode */
+#define IWL_LEGACY_FAILURE_LIMIT 160
+#define IWL_LEGACY_SUCCESS_LIMIT 480
+#define IWL_LEGACY_TABLE_COUNT 160
+
+#define IWL_NONE_LEGACY_FAILURE_LIMIT 400
+#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500
+#define IWL_NONE_LEGACY_TABLE_COUNT 1500
+
+/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */
+#define IWL_RS_GOOD_RATIO 12800 /* 100% */
+#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */
+#define IWL_RATE_HIGH_TH 10880 /* 85% */
+#define IWL_RATE_INCREASE_TH 6400 /* 50% */
+#define IWL_RATE_DECREASE_TH 1920 /* 15% */
+
+/* possible actions when in legacy mode */
+#define IWL_LEGACY_SWITCH_ANTENNA1 0
+#define IWL_LEGACY_SWITCH_ANTENNA2 1
+#define IWL_LEGACY_SWITCH_SISO 2
+#define IWL_LEGACY_SWITCH_MIMO2_AB 3
+#define IWL_LEGACY_SWITCH_MIMO2_AC 4
+#define IWL_LEGACY_SWITCH_MIMO2_BC 5
+#define IWL_LEGACY_SWITCH_MIMO3_ABC 6
+
+/* possible actions when in siso mode */
+#define IWL_SISO_SWITCH_ANTENNA1 0
+#define IWL_SISO_SWITCH_ANTENNA2 1
+#define IWL_SISO_SWITCH_MIMO2_AB 2
+#define IWL_SISO_SWITCH_MIMO2_AC 3
+#define IWL_SISO_SWITCH_MIMO2_BC 4
+#define IWL_SISO_SWITCH_GI 5
+#define IWL_SISO_SWITCH_MIMO3_ABC 6
+
+
+/* possible actions when in mimo mode */
+#define IWL_MIMO2_SWITCH_ANTENNA1 0
+#define IWL_MIMO2_SWITCH_ANTENNA2 1
+#define IWL_MIMO2_SWITCH_SISO_A 2
+#define IWL_MIMO2_SWITCH_SISO_B 3
+#define IWL_MIMO2_SWITCH_SISO_C 4
+#define IWL_MIMO2_SWITCH_GI 5
+#define IWL_MIMO2_SWITCH_MIMO3_ABC 6
+
+
+/* possible actions when in mimo3 mode */
+#define IWL_MIMO3_SWITCH_ANTENNA1 0
+#define IWL_MIMO3_SWITCH_ANTENNA2 1
+#define IWL_MIMO3_SWITCH_SISO_A 2
+#define IWL_MIMO3_SWITCH_SISO_B 3
+#define IWL_MIMO3_SWITCH_SISO_C 4
+#define IWL_MIMO3_SWITCH_MIMO2_AB 5
+#define IWL_MIMO3_SWITCH_MIMO2_AC 6
+#define IWL_MIMO3_SWITCH_MIMO2_BC 7
+#define IWL_MIMO3_SWITCH_GI 8
+
+
+#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI
+#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC
+
+/*FIXME:RS:add possible actions for MIMO3*/
+
+#define IWL_ACTION_LIMIT 3 /* # possible actions */
+
+#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */
+
+/* load per tid defines for A-MPDU activation */
+#define IWL_AGG_TPT_THREHOLD 0
+#define IWL_AGG_LOAD_THRESHOLD 10
+#define IWL_AGG_ALL_TID 0xff
+#define TID_QUEUE_CELL_SPACING 50 /*mS */
+#define TID_QUEUE_MAX_SIZE 20
+#define TID_ROUND_VALUE 5 /* mS */
+
+#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
+#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
+
+extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
+
+enum iwl_table_type {
+ LQ_NONE,
+ LQ_G, /* legacy types */
+ LQ_A,
+ LQ_SISO, /* high-throughput types */
+ LQ_MIMO2,
+ LQ_MIMO3,
+ LQ_MAX,
+};
+
+#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
+#define is_siso(tbl) ((tbl) == LQ_SISO)
+#define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
+#define is_mimo3(tbl) ((tbl) == LQ_MIMO3)
+#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl))
+#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
+#define is_a_band(tbl) ((tbl) == LQ_A)
+#define is_g_and(tbl) ((tbl) == LQ_G)
+
+#define IWL_MAX_MCS_DISPLAY_SIZE 12
+
+struct iwl_rate_mcs_info {
+ char mbps[IWL_MAX_MCS_DISPLAY_SIZE];
+ char mcs[IWL_MAX_MCS_DISPLAY_SIZE];
+};
+
+/**
+ * struct iwl_rate_scale_data -- tx success history for one rate
+ */
+struct iwl_rate_scale_data {
+ u64 data; /* bitmap of successful frames */
+ s32 success_counter; /* number of frames successful */
+ s32 success_ratio; /* per-cent * 128 */
+ s32 counter; /* number of frames attempted */
+ s32 average_tpt; /* success ratio * expected throughput */
+ unsigned long stamp;
+};
+
+/**
+ * struct iwl_scale_tbl_info -- tx params and success history for all rates
+ *
+ * There are two of these in struct iwl_lq_sta,
+ * one for "active", and one for "search".
+ */
+struct iwl_scale_tbl_info {
+ enum iwl_table_type lq_type;
+ u8 ant_type;
+ u8 is_SGI; /* 1 = short guard interval */
+ u8 is_ht40; /* 1 = 40 MHz channel width */
+ u8 is_dup; /* 1 = duplicated data streams */
+ u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
+ u8 max_search; /* maximun number of tables we can search */
+ const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
+ u32 current_rate; /* rate_n_flags, uCode API format */
+ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
+};
+
+struct iwl_traffic_load {
+ unsigned long time_stamp; /* age of the oldest statistics */
+ u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time
+ * slice */
+ u32 total; /* total num of packets during the
+ * last TID_MAX_TIME_DIFF */
+ u8 queue_count; /* number of queues that has
+ * been used since the last cleanup */
+ u8 head; /* start of the circular buffer */
+};
+
+/**
+ * struct iwl_lq_sta -- driver's rate scaling private structure
+ *
+ * Pointer to this gets passed back and forth between driver and mac80211.
+ */
+struct iwl_lq_sta {
+ u8 active_tbl; /* index of active table, range 0-1 */
+ u8 enable_counter; /* indicates HT mode */
+ u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */
+ u8 search_better_tbl; /* 1: currently trying alternate mode */
+ s32 last_tpt;
+
+ /* The following determine when to search for a new mode */
+ u32 table_count_limit;
+ u32 max_failure_limit; /* # failed frames before new search */
+ u32 max_success_limit; /* # successful frames before new search */
+ u32 table_count;
+ u32 total_failed; /* total failed frames, any/all rates */
+ u32 total_success; /* total successful frames, any/all rates */
+ u64 flush_timer; /* time staying in mode before new search */
+
+ u8 action_counter; /* # mode-switch actions tried */
+ u8 is_green;
+ u8 is_dup;
+ enum ieee80211_band band;
+
+ /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
+ u32 supp_rates;
+ u16 active_legacy_rate;
+ u16 active_siso_rate;
+ u16 active_mimo2_rate;
+ u16 active_mimo3_rate;
+ s8 max_rate_idx; /* Max rate set by user */
+ u8 missed_rate_counter;
+
+ struct iwl_link_quality_cmd lq;
+ struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
+ struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
+ u8 tx_agg_tid_en;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct dentry *rs_sta_dbgfs_scale_table_file;
+ struct dentry *rs_sta_dbgfs_stats_table_file;
+ struct dentry *rs_sta_dbgfs_rate_scale_data_file;
+ struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
+ u32 dbg_fixed_rate;
+#endif
+ struct iwl_priv *drv;
+
+ /* used to be in sta_info */
+ int last_txrate_idx;
+ /* last tx rate_n_flags */
+ u32 last_rate_n_flags;
+ /* packets destined for this STA are aggregated */
+ u8 is_agg;
+ /* BT traffic this sta was last updated in */
+ u8 last_bt_traffic;
+};
+
+static inline u8 first_antenna(u8 mask)
+{
+ if (mask & ANT_A)
+ return ANT_A;
+ if (mask & ANT_B)
+ return ANT_B;
+ return ANT_C;
+}
+
+
+/* Initialize station's rate scaling information after adding station */
+void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta,
+ u8 sta_id);
+
+/**
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
+ *
+ * Since the rate control algorithm is hardware specific, there is no need
+ * or reason to place it as a stand alone module. The driver can call
+ * iwl_rate_control_register in order to register the rate control callbacks
+ * with the mac80211 subsystem. This should be performed prior to calling
+ * ieee80211_register_hw
+ *
+ */
+int iwlagn_rate_control_register(void);
+
+/**
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
+ *
+ * This should be called after calling ieee80211_unregister_hw, but before
+ * the driver is unloaded.
+ */
+void iwlagn_rate_control_unregister(void);
+
+#endif /* __iwl_agn__rs__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c
new file mode 100644
index 000000000000..52ab1e012e8f
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c
@@ -0,0 +1,1026 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portionhelp of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "iwl-trans.h"
+#include "iwl-io.h"
+#include "dev.h"
+#include "calib.h"
+#include "agn.h"
+
+/******************************************************************************
+ *
+ * Generic RX handler implementations
+ *
+ ******************************************************************************/
+
+static void iwlagn_rx_reply_error(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_error_resp *err_resp = (void *)pkt->data;
+
+ IWL_ERR(priv, "Error Reply type 0x%08X cmd REPLY_ERROR (0x%02X) "
+ "seq 0x%04X ser 0x%08X\n",
+ le32_to_cpu(err_resp->error_type),
+ err_resp->cmd_id,
+ le16_to_cpu(err_resp->bad_cmd_seq_num),
+ le32_to_cpu(err_resp->error_info));
+}
+
+static void iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_csa_notification *csa = (void *)pkt->data;
+ /*
+ * MULTI-FIXME
+ * See iwlagn_mac_channel_switch.
+ */
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct iwl_rxon_cmd *rxon = (void *)&ctx->active;
+
+ if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+ return;
+
+ if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) {
+ rxon->channel = csa->channel;
+ ctx->staging.channel = csa->channel;
+ IWL_DEBUG_11H(priv, "CSA notif: channel %d\n",
+ le16_to_cpu(csa->channel));
+ iwl_chswitch_done(priv, true);
+ } else {
+ IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
+ le16_to_cpu(csa->channel));
+ iwl_chswitch_done(priv, false);
+ }
+}
+
+
+static void iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_spectrum_notification *report = (void *)pkt->data;
+
+ if (!report->state) {
+ IWL_DEBUG_11H(priv,
+ "Spectrum Measure Notification: Start\n");
+ return;
+ }
+
+ memcpy(&priv->measure_report, report, sizeof(*report));
+ priv->measurement_status |= MEASUREMENT_READY;
+}
+
+static void iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+#ifdef CONFIG_IWLWIFI_DEBUG
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_sleep_notification *sleep = (void *)pkt->data;
+ IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n",
+ sleep->pm_sleep_mode, sleep->pm_wakeup_src);
+#endif
+}
+
+static void iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u32 __maybe_unused len = iwl_rx_packet_len(pkt);
+ IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled "
+ "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len);
+ iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len);
+}
+
+static void iwlagn_rx_beacon_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwlagn_beacon_notif *beacon = (void *)pkt->data;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ u16 status = le16_to_cpu(beacon->beacon_notify_hdr.status.status);
+ u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
+
+ IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d "
+ "tsf:0x%.8x%.8x rate:%d\n",
+ status & TX_STATUS_MSK,
+ beacon->beacon_notify_hdr.failure_frame,
+ le32_to_cpu(beacon->ibss_mgr_status),
+ le32_to_cpu(beacon->high_tsf),
+ le32_to_cpu(beacon->low_tsf), rate);
+#endif
+
+ priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
+}
+
+/**
+ * iwl_good_plcp_health - checks for plcp error.
+ *
+ * When the plcp error is exceeding the thresholds, reset the radio
+ * to improve the throughput.
+ */
+static bool iwlagn_good_plcp_health(struct iwl_priv *priv,
+ struct statistics_rx_phy *cur_ofdm,
+ struct statistics_rx_ht_phy *cur_ofdm_ht,
+ unsigned int msecs)
+{
+ int delta;
+ int threshold = priv->plcp_delta_threshold;
+
+ if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) {
+ IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n");
+ return true;
+ }
+
+ delta = le32_to_cpu(cur_ofdm->plcp_err) -
+ le32_to_cpu(priv->statistics.rx_ofdm.plcp_err) +
+ le32_to_cpu(cur_ofdm_ht->plcp_err) -
+ le32_to_cpu(priv->statistics.rx_ofdm_ht.plcp_err);
+
+ /* Can be negative if firmware reset statistics */
+ if (delta <= 0)
+ return true;
+
+ if ((delta * 100 / msecs) > threshold) {
+ IWL_DEBUG_RADIO(priv,
+ "plcp health threshold %u delta %d msecs %u\n",
+ threshold, delta, msecs);
+ return false;
+ }
+
+ return true;
+}
+
+int iwl_force_rf_reset(struct iwl_priv *priv, bool external)
+{
+ struct iwl_rf_reset *rf_reset;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return -EAGAIN;
+
+ if (!iwl_is_any_associated(priv)) {
+ IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n");
+ return -ENOLINK;
+ }
+
+ rf_reset = &priv->rf_reset;
+ rf_reset->reset_request_count++;
+ if (!external && rf_reset->last_reset_jiffies &&
+ time_after(rf_reset->last_reset_jiffies +
+ IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) {
+ IWL_DEBUG_INFO(priv, "RF reset rejected\n");
+ rf_reset->reset_reject_count++;
+ return -EAGAIN;
+ }
+ rf_reset->reset_success_count++;
+ rf_reset->last_reset_jiffies = jiffies;
+
+ /*
+ * There is no easy and better way to force reset the radio,
+ * the only known method is switching channel which will force to
+ * reset and tune the radio.
+ * Use internal short scan (single channel) operation to should
+ * achieve this objective.
+ * Driver should reset the radio when number of consecutive missed
+ * beacon, or any other uCode error condition detected.
+ */
+ IWL_DEBUG_INFO(priv, "perform radio reset.\n");
+ iwl_internal_short_hw_scan(priv);
+ return 0;
+}
+
+
+static void iwlagn_recover_from_statistics(struct iwl_priv *priv,
+ struct statistics_rx_phy *cur_ofdm,
+ struct statistics_rx_ht_phy *cur_ofdm_ht,
+ struct statistics_tx *tx,
+ unsigned long stamp)
+{
+ unsigned int msecs;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies);
+
+ /* Only gather statistics and update time stamp when not associated */
+ if (!iwl_is_any_associated(priv))
+ return;
+
+ /* Do not check/recover when do not have enough statistics data */
+ if (msecs < 99)
+ return;
+
+ if (!iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs))
+ iwl_force_rf_reset(priv, false);
+}
+
+/* Calculate noise level, based on measurements during network silence just
+ * before arriving beacon. This measurement can be done only if we know
+ * exactly when to expect beacons, therefore only when we're associated. */
+static void iwlagn_rx_calc_noise(struct iwl_priv *priv)
+{
+ struct statistics_rx_non_phy *rx_info;
+ int num_active_rx = 0;
+ int total_silence = 0;
+ int bcn_silence_a, bcn_silence_b, bcn_silence_c;
+ int last_rx_noise;
+
+ rx_info = &priv->statistics.rx_non_phy;
+
+ bcn_silence_a =
+ le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
+ bcn_silence_b =
+ le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
+ bcn_silence_c =
+ le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
+
+ if (bcn_silence_a) {
+ total_silence += bcn_silence_a;
+ num_active_rx++;
+ }
+ if (bcn_silence_b) {
+ total_silence += bcn_silence_b;
+ num_active_rx++;
+ }
+ if (bcn_silence_c) {
+ total_silence += bcn_silence_c;
+ num_active_rx++;
+ }
+
+ /* Average among active antennas */
+ if (num_active_rx)
+ last_rx_noise = (total_silence / num_active_rx) - 107;
+ else
+ last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
+
+ IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n",
+ bcn_silence_a, bcn_silence_b, bcn_silence_c,
+ last_rx_noise);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+/*
+ * based on the assumption of all statistics counter are in DWORD
+ * FIXME: This function is for debugging, do not deal with
+ * the case of counters roll-over.
+ */
+static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta,
+ __le32 *max_delta, __le32 *accum, int size)
+{
+ int i;
+
+ for (i = 0;
+ i < size / sizeof(__le32);
+ i++, prev++, cur++, delta++, max_delta++, accum++) {
+ if (le32_to_cpu(*cur) > le32_to_cpu(*prev)) {
+ *delta = cpu_to_le32(
+ le32_to_cpu(*cur) - le32_to_cpu(*prev));
+ le32_add_cpu(accum, le32_to_cpu(*delta));
+ if (le32_to_cpu(*delta) > le32_to_cpu(*max_delta))
+ *max_delta = *delta;
+ }
+ }
+}
+
+static void
+iwlagn_accumulative_statistics(struct iwl_priv *priv,
+ struct statistics_general_common *common,
+ struct statistics_rx_non_phy *rx_non_phy,
+ struct statistics_rx_phy *rx_ofdm,
+ struct statistics_rx_ht_phy *rx_ofdm_ht,
+ struct statistics_rx_phy *rx_cck,
+ struct statistics_tx *tx,
+ struct statistics_bt_activity *bt_activity)
+{
+#define ACCUM(_name) \
+ accum_stats((__le32 *)&priv->statistics._name, \
+ (__le32 *)_name, \
+ (__le32 *)&priv->delta_stats._name, \
+ (__le32 *)&priv->max_delta_stats._name, \
+ (__le32 *)&priv->accum_stats._name, \
+ sizeof(*_name));
+
+ ACCUM(common);
+ ACCUM(rx_non_phy);
+ ACCUM(rx_ofdm);
+ ACCUM(rx_ofdm_ht);
+ ACCUM(rx_cck);
+ ACCUM(tx);
+ if (bt_activity)
+ ACCUM(bt_activity);
+#undef ACCUM
+}
+#else
+static inline void
+iwlagn_accumulative_statistics(struct iwl_priv *priv,
+ struct statistics_general_common *common,
+ struct statistics_rx_non_phy *rx_non_phy,
+ struct statistics_rx_phy *rx_ofdm,
+ struct statistics_rx_ht_phy *rx_ofdm_ht,
+ struct statistics_rx_phy *rx_cck,
+ struct statistics_tx *tx,
+ struct statistics_bt_activity *bt_activity)
+{
+}
+#endif
+
+static void iwlagn_rx_statistics(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ unsigned long stamp = jiffies;
+ const int reg_recalib_period = 60;
+ int change;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u32 len = iwl_rx_packet_payload_len(pkt);
+ __le32 *flag;
+ struct statistics_general_common *common;
+ struct statistics_rx_non_phy *rx_non_phy;
+ struct statistics_rx_phy *rx_ofdm;
+ struct statistics_rx_ht_phy *rx_ofdm_ht;
+ struct statistics_rx_phy *rx_cck;
+ struct statistics_tx *tx;
+ struct statistics_bt_activity *bt_activity;
+
+ IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n",
+ len);
+
+ spin_lock(&priv->statistics.lock);
+
+ if (len == sizeof(struct iwl_bt_notif_statistics)) {
+ struct iwl_bt_notif_statistics *stats;
+ stats = (void *)&pkt->data;
+ flag = &stats->flag;
+ common = &stats->general.common;
+ rx_non_phy = &stats->rx.general.common;
+ rx_ofdm = &stats->rx.ofdm;
+ rx_ofdm_ht = &stats->rx.ofdm_ht;
+ rx_cck = &stats->rx.cck;
+ tx = &stats->tx;
+ bt_activity = &stats->general.activity;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ /* handle this exception directly */
+ priv->statistics.num_bt_kills = stats->rx.general.num_bt_kills;
+ le32_add_cpu(&priv->statistics.accum_num_bt_kills,
+ le32_to_cpu(stats->rx.general.num_bt_kills));
+#endif
+ } else if (len == sizeof(struct iwl_notif_statistics)) {
+ struct iwl_notif_statistics *stats;
+ stats = (void *)&pkt->data;
+ flag = &stats->flag;
+ common = &stats->general.common;
+ rx_non_phy = &stats->rx.general;
+ rx_ofdm = &stats->rx.ofdm;
+ rx_ofdm_ht = &stats->rx.ofdm_ht;
+ rx_cck = &stats->rx.cck;
+ tx = &stats->tx;
+ bt_activity = NULL;
+ } else {
+ WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n",
+ len, sizeof(struct iwl_bt_notif_statistics),
+ sizeof(struct iwl_notif_statistics));
+ spin_unlock(&priv->statistics.lock);
+ return;
+ }
+
+ change = common->temperature != priv->statistics.common.temperature ||
+ (*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
+ (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK);
+
+ iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm,
+ rx_ofdm_ht, rx_cck, tx, bt_activity);
+
+ iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp);
+
+ priv->statistics.flag = *flag;
+ memcpy(&priv->statistics.common, common, sizeof(*common));
+ memcpy(&priv->statistics.rx_non_phy, rx_non_phy, sizeof(*rx_non_phy));
+ memcpy(&priv->statistics.rx_ofdm, rx_ofdm, sizeof(*rx_ofdm));
+ memcpy(&priv->statistics.rx_ofdm_ht, rx_ofdm_ht, sizeof(*rx_ofdm_ht));
+ memcpy(&priv->statistics.rx_cck, rx_cck, sizeof(*rx_cck));
+ memcpy(&priv->statistics.tx, tx, sizeof(*tx));
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (bt_activity)
+ memcpy(&priv->statistics.bt_activity, bt_activity,
+ sizeof(*bt_activity));
+#endif
+
+ priv->rx_statistics_jiffies = stamp;
+
+ set_bit(STATUS_STATISTICS, &priv->status);
+
+ /* Reschedule the statistics timer to occur in
+ * reg_recalib_period seconds to ensure we get a
+ * thermal update even if the uCode doesn't give
+ * us one */
+ mod_timer(&priv->statistics_periodic, jiffies +
+ msecs_to_jiffies(reg_recalib_period * 1000));
+
+ if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
+ (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {
+ iwlagn_rx_calc_noise(priv);
+ queue_work(priv->workqueue, &priv->run_time_calib_work);
+ }
+ if (priv->lib->temperature && change)
+ priv->lib->temperature(priv);
+
+ spin_unlock(&priv->statistics.lock);
+}
+
+static void iwlagn_rx_reply_statistics(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_notif_statistics *stats = (void *)pkt->data;
+
+ if (le32_to_cpu(stats->flag) & UCODE_STATISTICS_CLEAR_MSK) {
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ memset(&priv->accum_stats, 0,
+ sizeof(priv->accum_stats));
+ memset(&priv->delta_stats, 0,
+ sizeof(priv->delta_stats));
+ memset(&priv->max_delta_stats, 0,
+ sizeof(priv->max_delta_stats));
+#endif
+ IWL_DEBUG_RX(priv, "Statistics have been cleared\n");
+ }
+
+ iwlagn_rx_statistics(priv, rxb);
+}
+
+/* Handle notification from uCode that card's power state is changing
+ * due to software, hardware, or critical temperature RFKILL */
+static void iwlagn_rx_card_state_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_card_state_notif *card_state_notif = (void *)pkt->data;
+ u32 flags = le32_to_cpu(card_state_notif->flags);
+ unsigned long status = priv->status;
+
+ IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n",
+ (flags & HW_CARD_DISABLED) ? "Kill" : "On",
+ (flags & SW_CARD_DISABLED) ? "Kill" : "On",
+ (flags & CT_CARD_DISABLED) ?
+ "Reached" : "Not reached");
+
+ if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED |
+ CT_CARD_DISABLED)) {
+
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+ iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C,
+ HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
+
+ if (!(flags & RXON_CARD_DISABLED)) {
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+ iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C,
+ HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
+ }
+ if (flags & CT_CARD_DISABLED)
+ iwl_tt_enter_ct_kill(priv);
+ }
+ if (!(flags & CT_CARD_DISABLED))
+ iwl_tt_exit_ct_kill(priv);
+
+ if (flags & HW_CARD_DISABLED)
+ set_bit(STATUS_RF_KILL_HW, &priv->status);
+ else
+ clear_bit(STATUS_RF_KILL_HW, &priv->status);
+
+
+ if (!(flags & RXON_CARD_DISABLED))
+ iwl_scan_cancel(priv);
+
+ if ((test_bit(STATUS_RF_KILL_HW, &status) !=
+ test_bit(STATUS_RF_KILL_HW, &priv->status)))
+ wiphy_rfkill_set_hw_state(priv->hw->wiphy,
+ test_bit(STATUS_RF_KILL_HW, &priv->status));
+}
+
+static void iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_missed_beacon_notif *missed_beacon = (void *)pkt->data;
+
+ if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
+ priv->missed_beacon_threshold) {
+ IWL_DEBUG_CALIB(priv,
+ "missed bcn cnsq %d totl %d rcd %d expctd %d\n",
+ le32_to_cpu(missed_beacon->consecutive_missed_beacons),
+ le32_to_cpu(missed_beacon->total_missed_becons),
+ le32_to_cpu(missed_beacon->num_recvd_beacons),
+ le32_to_cpu(missed_beacon->num_expected_beacons));
+ if (!test_bit(STATUS_SCANNING, &priv->status))
+ iwl_init_sensitivity(priv);
+ }
+}
+
+/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD).
+ * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */
+static void iwlagn_rx_reply_rx_phy(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+ priv->last_phy_res_valid = true;
+ priv->ampdu_ref++;
+ memcpy(&priv->last_phy_res, pkt->data,
+ sizeof(struct iwl_rx_phy_res));
+}
+
+/*
+ * returns non-zero if packet should be dropped
+ */
+static int iwlagn_set_decrypted_flag(struct iwl_priv *priv,
+ struct ieee80211_hdr *hdr,
+ u32 decrypt_res,
+ struct ieee80211_rx_status *stats)
+{
+ u16 fc = le16_to_cpu(hdr->frame_control);
+
+ /*
+ * All contexts have the same setting here due to it being
+ * a module parameter, so OK to check any context.
+ */
+ if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags &
+ RXON_FILTER_DIS_DECRYPT_MSK)
+ return 0;
+
+ if (!(fc & IEEE80211_FCTL_PROTECTED))
+ return 0;
+
+ IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res);
+ switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
+ case RX_RES_STATUS_SEC_TYPE_TKIP:
+ /* The uCode has got a bad phase 1 Key, pushes the packet.
+ * Decryption will be done in SW. */
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+ RX_RES_STATUS_BAD_KEY_TTAK)
+ break;
+
+ case RX_RES_STATUS_SEC_TYPE_WEP:
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+ RX_RES_STATUS_BAD_ICV_MIC) {
+ /* bad ICV, the packet is destroyed since the
+ * decryption is inplace, drop it */
+ IWL_DEBUG_RX(priv, "Packet destroyed\n");
+ return -1;
+ }
+ case RX_RES_STATUS_SEC_TYPE_CCMP:
+ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+ RX_RES_STATUS_DECRYPT_OK) {
+ IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n");
+ stats->flag |= RX_FLAG_DECRYPTED;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
+ struct ieee80211_hdr *hdr,
+ u16 len,
+ u32 ampdu_status,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct ieee80211_rx_status *stats)
+{
+ struct sk_buff *skb;
+ __le16 fc = hdr->frame_control;
+ struct iwl_rxon_context *ctx;
+ unsigned int hdrlen, fraglen;
+
+ /* We only process data packets if the interface is open */
+ if (unlikely(!priv->is_open)) {
+ IWL_DEBUG_DROP_LIMIT(priv,
+ "Dropping packet while interface is not open.\n");
+ return;
+ }
+
+ /* In case of HW accelerated crypto and bad decryption, drop */
+ if (!iwlwifi_mod_params.sw_crypto &&
+ iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats))
+ return;
+
+ /* Dont use dev_alloc_skb(), we'll have enough headroom once
+ * ieee80211_hdr pulled.
+ */
+ skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(priv, "alloc_skb failed\n");
+ return;
+ }
+ /* If frame is small enough to fit in skb->head, pull it completely.
+ * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
+ * are more efficient.
+ */
+ hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
+
+ memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
+ fraglen = len - hdrlen;
+
+ if (fraglen) {
+ int offset = (void *)hdr + hdrlen -
+ rxb_addr(rxb) + rxb_offset(rxb);
+
+ skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+ fraglen, rxb->truesize);
+ }
+
+ /*
+ * Wake any queues that were stopped due to a passive channel tx
+ * failure. This can happen because the regulatory enforcement in
+ * the device waits for a beacon before allowing transmission,
+ * sometimes even after already having transmitted frames for the
+ * association because the new RXON may reset the information.
+ */
+ if (unlikely(ieee80211_is_beacon(fc) && priv->passive_no_rx)) {
+ for_each_context(priv, ctx) {
+ if (!ether_addr_equal(hdr->addr3,
+ ctx->active.bssid_addr))
+ continue;
+ iwlagn_lift_passive_no_rx(priv);
+ }
+ }
+
+ memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
+
+ ieee80211_rx_napi(priv->hw, skb, priv->napi);
+}
+
+static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
+{
+ u32 decrypt_out = 0;
+
+ if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) ==
+ RX_RES_STATUS_STATION_FOUND)
+ decrypt_out |= (RX_RES_STATUS_STATION_FOUND |
+ RX_RES_STATUS_NO_STATION_INFO_MISMATCH);
+
+ decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK);
+
+ /* packet was not encrypted */
+ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
+ RX_RES_STATUS_SEC_TYPE_NONE)
+ return decrypt_out;
+
+ /* packet was encrypted with unknown alg */
+ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
+ RX_RES_STATUS_SEC_TYPE_ERR)
+ return decrypt_out;
+
+ /* decryption was not done in HW */
+ if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) !=
+ RX_MPDU_RES_STATUS_DEC_DONE_MSK)
+ return decrypt_out;
+
+ switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) {
+
+ case RX_RES_STATUS_SEC_TYPE_CCMP:
+ /* alg is CCM: check MIC only */
+ if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK))
+ /* Bad MIC */
+ decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
+ else
+ decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
+
+ break;
+
+ case RX_RES_STATUS_SEC_TYPE_TKIP:
+ if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) {
+ /* Bad TTAK */
+ decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
+ break;
+ }
+ /* fall through if TTAK OK */
+ default:
+ if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
+ decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
+ else
+ decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
+ break;
+ }
+
+ IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n",
+ decrypt_in, decrypt_out);
+
+ return decrypt_out;
+}
+
+/* Calc max signal level (dBm) among 3 possible receivers */
+static int iwlagn_calc_rssi(struct iwl_priv *priv,
+ struct iwl_rx_phy_res *rx_resp)
+{
+ /* data from PHY/DSP regarding signal strength, etc.,
+ * contents are always there, not configurable by host
+ */
+ struct iwlagn_non_cfg_phy *ncphy =
+ (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
+ u32 val, rssi_a, rssi_b, rssi_c, max_rssi;
+ u8 agc;
+
+ val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]);
+ agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS;
+
+ /* Find max rssi among 3 possible receivers.
+ * These values are measured by the digital signal processor (DSP).
+ * They should stay fairly constant even as the signal strength varies,
+ * if the radio's automatic gain control (AGC) is working right.
+ * AGC value (see below) will provide the "interesting" info.
+ */
+ val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]);
+ rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >>
+ IWLAGN_OFDM_RSSI_A_BIT_POS;
+ rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >>
+ IWLAGN_OFDM_RSSI_B_BIT_POS;
+ val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]);
+ rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >>
+ IWLAGN_OFDM_RSSI_C_BIT_POS;
+
+ max_rssi = max_t(u32, rssi_a, rssi_b);
+ max_rssi = max_t(u32, max_rssi, rssi_c);
+
+ IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
+ rssi_a, rssi_b, rssi_c, max_rssi, agc);
+
+ /* dBm = max_rssi dB - agc dB - constant.
+ * Higher AGC (higher radio gain) means lower signal. */
+ return max_rssi - agc - IWLAGN_RSSI_OFFSET;
+}
+
+/* Called for REPLY_RX_MPDU_CMD */
+static void iwlagn_rx_reply_rx(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct ieee80211_hdr *header;
+ struct ieee80211_rx_status rx_status = {};
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_rx_phy_res *phy_res;
+ __le32 rx_pkt_status;
+ struct iwl_rx_mpdu_res_start *amsdu;
+ u32 len;
+ u32 ampdu_status;
+ u32 rate_n_flags;
+
+ if (!priv->last_phy_res_valid) {
+ IWL_ERR(priv, "MPDU frame without cached PHY data\n");
+ return;
+ }
+ phy_res = &priv->last_phy_res;
+ amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data;
+ header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu));
+ len = le16_to_cpu(amsdu->byte_count);
+ rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len);
+ ampdu_status = iwlagn_translate_rx_status(priv,
+ le32_to_cpu(rx_pkt_status));
+
+ if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
+ IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n",
+ phy_res->cfg_phy_cnt);
+ return;
+ }
+
+ if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) ||
+ !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
+ IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n",
+ le32_to_cpu(rx_pkt_status));
+ return;
+ }
+
+ /* This will be used in several places later */
+ rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);
+
+ /* rx_status carries information about the packet to mac80211 */
+ rx_status.mactime = le64_to_cpu(phy_res->timestamp);
+ rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
+ IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+ rx_status.freq =
+ ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel),
+ rx_status.band);
+ rx_status.rate_idx =
+ iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band);
+ rx_status.flag = 0;
+
+ /* TSF isn't reliable. In order to allow smooth user experience,
+ * this W/A doesn't propagate it to the mac80211 */
+ /*rx_status.flag |= RX_FLAG_MACTIME_START;*/
+
+ priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp);
+
+ /* Find max signal strength (dBm) among 3 antenna/receiver chains */
+ rx_status.signal = iwlagn_calc_rssi(priv, phy_res);
+
+ IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n",
+ rx_status.signal, (unsigned long long)rx_status.mactime);
+
+ /*
+ * "antenna number"
+ *
+ * It seems that the antenna field in the phy flags value
+ * is actually a bit field. This is undefined by radiotap,
+ * it wants an actual antenna number but I always get "7"
+ * for most legacy frames I receive indicating that the
+ * same frame was received on all three RX chains.
+ *
+ * I think this field should be removed in favor of a
+ * new 802.11n radiotap field "RX chains" that is defined
+ * as a bitmask.
+ */
+ rx_status.antenna =
+ (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK)
+ >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+
+ /* set the preamble flag if appropriate */
+ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
+ rx_status.flag |= RX_FLAG_SHORTPRE;
+
+ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
+ /*
+ * We know which subframes of an A-MPDU belong
+ * together since we get a single PHY response
+ * from the firmware for all of them
+ */
+ rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
+ rx_status.ampdu_reference = priv->ampdu_ref;
+ }
+
+ /* Set up the HT phy flags */
+ if (rate_n_flags & RATE_MCS_HT_MSK)
+ rx_status.flag |= RX_FLAG_HT;
+ if (rate_n_flags & RATE_MCS_HT40_MSK)
+ rx_status.flag |= RX_FLAG_40MHZ;
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ rx_status.flag |= RX_FLAG_SHORT_GI;
+ if (rate_n_flags & RATE_MCS_GF_MSK)
+ rx_status.flag |= RX_FLAG_HT_GF;
+
+ iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status,
+ rxb, &rx_status);
+}
+
+static void iwlagn_rx_noa_notification(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_wipan_noa_data *new_data, *old_data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->data;
+
+ /* no condition -- we're in softirq */
+ old_data = rcu_dereference_protected(priv->noa_data, true);
+
+ if (noa_notif->noa_active) {
+ u32 len = le16_to_cpu(noa_notif->noa_attribute.length);
+ u32 copylen = len;
+
+ /* EID, len, OUI, subtype */
+ len += 1 + 1 + 3 + 1;
+ /* P2P id, P2P length */
+ len += 1 + 2;
+ copylen += 1 + 2;
+
+ new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC);
+ if (new_data) {
+ new_data->length = len;
+ new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC;
+ new_data->data[1] = len - 2; /* not counting EID, len */
+ new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
+ new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
+ new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
+ new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P;
+ memcpy(&new_data->data[6], &noa_notif->noa_attribute,
+ copylen);
+ }
+ } else
+ new_data = NULL;
+
+ rcu_assign_pointer(priv->noa_data, new_data);
+
+ if (old_data)
+ kfree_rcu(old_data, rcu_head);
+}
+
+/**
+ * iwl_setup_rx_handlers - Initialize Rx handler callbacks
+ *
+ * Setup the RX handlers for each of the reply types sent from the uCode
+ * to the host.
+ */
+void iwl_setup_rx_handlers(struct iwl_priv *priv)
+{
+ void (**handlers)(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb);
+
+ handlers = priv->rx_handlers;
+
+ handlers[REPLY_ERROR] = iwlagn_rx_reply_error;
+ handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa;
+ handlers[SPECTRUM_MEASURE_NOTIFICATION] =
+ iwlagn_rx_spectrum_measure_notif;
+ handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif;
+ handlers[PM_DEBUG_STATISTIC_NOTIFIC] =
+ iwlagn_rx_pm_debug_statistics_notif;
+ handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif;
+ handlers[REPLY_ADD_STA] = iwl_add_sta_callback;
+
+ handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification;
+
+ /*
+ * The same handler is used for both the REPLY to a discrete
+ * statistics request from the host as well as for the periodic
+ * statistics notifications (after received beacons) from the uCode.
+ */
+ handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics;
+ handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics;
+
+ iwl_setup_rx_scan_handlers(priv);
+
+ handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif;
+ handlers[MISSED_BEACONS_NOTIFICATION] =
+ iwlagn_rx_missed_beacon_notif;
+
+ /* Rx handlers */
+ handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy;
+ handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx;
+
+ /* block ack */
+ handlers[REPLY_COMPRESSED_BA] =
+ iwlagn_rx_reply_compressed_ba;
+
+ priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
+
+ /* set up notification wait support */
+ iwl_notification_wait_init(&priv->notif_wait);
+
+ /* Set up BT Rx handlers */
+ if (priv->lib->bt_params)
+ iwlagn_bt_rx_handler_setup(priv);
+}
+
+void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ /*
+ * Do the notification wait before RX handlers so
+ * even if the RX handler consumes the RXB we have
+ * access to it in the notification wait entry.
+ */
+ iwl_notification_wait_notify(&priv->notif_wait, pkt);
+
+ /* Based on type of command response or notification,
+ * handle those that need handling via function in
+ * rx_handlers table. See iwl_setup_rx_handlers() */
+ if (priv->rx_handlers[pkt->hdr.cmd]) {
+ priv->rx_handlers_stats[pkt->hdr.cmd]++;
+ priv->rx_handlers[pkt->hdr.cmd](priv, rxb);
+ } else {
+ /* No handling needed */
+ IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n",
+ iwl_get_cmd_string(priv->trans,
+ iwl_cmd_id(pkt->hdr.cmd,
+ 0, 0)),
+ pkt->hdr.cmd);
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c
new file mode 100644
index 000000000000..2d47cb24c48b
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c
@@ -0,0 +1,1572 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include "iwl-trans.h"
+#include "iwl-modparams.h"
+#include "dev.h"
+#include "agn.h"
+#include "calib.h"
+
+/*
+ * initialize rxon structure with default values from eeprom
+ */
+void iwl_connection_init_rx_config(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ memset(&ctx->staging, 0, sizeof(ctx->staging));
+
+ if (!ctx->vif) {
+ ctx->staging.dev_type = ctx->unused_devtype;
+ } else
+ switch (ctx->vif->type) {
+ case NL80211_IFTYPE_AP:
+ ctx->staging.dev_type = ctx->ap_devtype;
+ break;
+
+ case NL80211_IFTYPE_STATION:
+ ctx->staging.dev_type = ctx->station_devtype;
+ ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
+ break;
+
+ case NL80211_IFTYPE_ADHOC:
+ ctx->staging.dev_type = ctx->ibss_devtype;
+ ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
+ ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK |
+ RXON_FILTER_ACCEPT_GRP_MSK;
+ break;
+
+ case NL80211_IFTYPE_MONITOR:
+ ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER;
+ break;
+
+ default:
+ IWL_ERR(priv, "Unsupported interface type %d\n",
+ ctx->vif->type);
+ break;
+ }
+
+#if 0
+ /* TODO: Figure out when short_preamble would be set and cache from
+ * that */
+ if (!hw_to_local(priv->hw)->short_preamble)
+ ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
+ else
+ ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
+#endif
+
+ ctx->staging.channel =
+ cpu_to_le16(priv->hw->conf.chandef.chan->hw_value);
+ priv->band = priv->hw->conf.chandef.chan->band;
+
+ iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif);
+
+ /* clear both MIX and PURE40 mode flag */
+ ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED |
+ RXON_FLG_CHANNEL_MODE_PURE_40);
+ if (ctx->vif)
+ memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN);
+
+ ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff;
+ ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff;
+ ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff;
+}
+
+static int iwlagn_disable_bss(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct iwl_rxon_cmd *send)
+{
+ __le32 old_filter = send->filter_flags;
+ int ret;
+
+ send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
+ 0, sizeof(*send), send);
+
+ send->filter_flags = old_filter;
+
+ if (ret)
+ IWL_DEBUG_QUIET_RFKILL(priv,
+ "Error clearing ASSOC_MSK on BSS (%d)\n", ret);
+
+ return ret;
+}
+
+static int iwlagn_disable_pan(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct iwl_rxon_cmd *send)
+{
+ struct iwl_notification_wait disable_wait;
+ __le32 old_filter = send->filter_flags;
+ u8 old_dev_type = send->dev_type;
+ int ret;
+ static const u16 deactivate_cmd[] = {
+ REPLY_WIPAN_DEACTIVATION_COMPLETE
+ };
+
+ iwl_init_notification_wait(&priv->notif_wait, &disable_wait,
+ deactivate_cmd, ARRAY_SIZE(deactivate_cmd),
+ NULL, NULL);
+
+ send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ send->dev_type = RXON_DEV_TYPE_P2P;
+ ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
+ 0, sizeof(*send), send);
+
+ send->filter_flags = old_filter;
+ send->dev_type = old_dev_type;
+
+ if (ret) {
+ IWL_ERR(priv, "Error disabling PAN (%d)\n", ret);
+ iwl_remove_notification(&priv->notif_wait, &disable_wait);
+ } else {
+ ret = iwl_wait_notification(&priv->notif_wait,
+ &disable_wait, HZ);
+ if (ret)
+ IWL_ERR(priv, "Timed out waiting for PAN disable\n");
+ }
+
+ return ret;
+}
+
+static int iwlagn_disconn_pan(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct iwl_rxon_cmd *send)
+{
+ __le32 old_filter = send->filter_flags;
+ int ret;
+
+ send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
+ sizeof(*send), send);
+
+ send->filter_flags = old_filter;
+
+ return ret;
+}
+
+static void iwlagn_update_qos(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ int ret;
+
+ if (!ctx->is_active)
+ return;
+
+ ctx->qos_data.def_qos_parm.qos_flags = 0;
+
+ if (ctx->qos_data.qos_active)
+ ctx->qos_data.def_qos_parm.qos_flags |=
+ QOS_PARAM_FLG_UPDATE_EDCA_MSK;
+
+ if (ctx->ht.enabled)
+ ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
+
+ IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
+ ctx->qos_data.qos_active,
+ ctx->qos_data.def_qos_parm.qos_flags);
+
+ ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0,
+ sizeof(struct iwl_qosparam_cmd),
+ &ctx->qos_data.def_qos_parm);
+ if (ret)
+ IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n");
+}
+
+static int iwlagn_update_beacon(struct iwl_priv *priv,
+ struct ieee80211_vif *vif)
+{
+ lockdep_assert_held(&priv->mutex);
+
+ dev_kfree_skb(priv->beacon_skb);
+ priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif);
+ if (!priv->beacon_skb)
+ return -ENOMEM;
+ return iwlagn_send_beacon_cmd(priv);
+}
+
+static int iwlagn_send_rxon_assoc(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ int ret = 0;
+ struct iwl_rxon_assoc_cmd rxon_assoc;
+ const struct iwl_rxon_cmd *rxon1 = &ctx->staging;
+ const struct iwl_rxon_cmd *rxon2 = &ctx->active;
+
+ if ((rxon1->flags == rxon2->flags) &&
+ (rxon1->filter_flags == rxon2->filter_flags) &&
+ (rxon1->cck_basic_rates == rxon2->cck_basic_rates) &&
+ (rxon1->ofdm_ht_single_stream_basic_rates ==
+ rxon2->ofdm_ht_single_stream_basic_rates) &&
+ (rxon1->ofdm_ht_dual_stream_basic_rates ==
+ rxon2->ofdm_ht_dual_stream_basic_rates) &&
+ (rxon1->ofdm_ht_triple_stream_basic_rates ==
+ rxon2->ofdm_ht_triple_stream_basic_rates) &&
+ (rxon1->acquisition_data == rxon2->acquisition_data) &&
+ (rxon1->rx_chain == rxon2->rx_chain) &&
+ (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) {
+ IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC. Not resending.\n");
+ return 0;
+ }
+
+ rxon_assoc.flags = ctx->staging.flags;
+ rxon_assoc.filter_flags = ctx->staging.filter_flags;
+ rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates;
+ rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates;
+ rxon_assoc.reserved1 = 0;
+ rxon_assoc.reserved2 = 0;
+ rxon_assoc.reserved3 = 0;
+ rxon_assoc.ofdm_ht_single_stream_basic_rates =
+ ctx->staging.ofdm_ht_single_stream_basic_rates;
+ rxon_assoc.ofdm_ht_dual_stream_basic_rates =
+ ctx->staging.ofdm_ht_dual_stream_basic_rates;
+ rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain;
+ rxon_assoc.ofdm_ht_triple_stream_basic_rates =
+ ctx->staging.ofdm_ht_triple_stream_basic_rates;
+ rxon_assoc.acquisition_data = ctx->staging.acquisition_data;
+
+ ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd,
+ CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc);
+ return ret;
+}
+
+static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
+{
+ u16 new_val;
+ u16 beacon_factor;
+
+ /*
+ * If mac80211 hasn't given us a beacon interval, program
+ * the default into the device (not checking this here
+ * would cause the adjustment below to return the maximum
+ * value, which may break PAN.)
+ */
+ if (!beacon_val)
+ return DEFAULT_BEACON_INTERVAL;
+
+ /*
+ * If the beacon interval we obtained from the peer
+ * is too large, we'll have to wake up more often
+ * (and in IBSS case, we'll beacon too much)
+ *
+ * For example, if max_beacon_val is 4096, and the
+ * requested beacon interval is 7000, we'll have to
+ * use 3500 to be able to wake up on the beacons.
+ *
+ * This could badly influence beacon detection stats.
+ */
+
+ beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
+ new_val = beacon_val / beacon_factor;
+
+ if (!new_val)
+ new_val = max_beacon_val;
+
+ return new_val;
+}
+
+static int iwl_send_rxon_timing(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ u64 tsf;
+ s32 interval_tm, rem;
+ struct ieee80211_conf *conf = NULL;
+ u16 beacon_int;
+ struct ieee80211_vif *vif = ctx->vif;
+
+ conf = &priv->hw->conf;
+
+ lockdep_assert_held(&priv->mutex);
+
+ memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd));
+
+ ctx->timing.timestamp = cpu_to_le64(priv->timestamp);
+ ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval);
+
+ beacon_int = vif ? vif->bss_conf.beacon_int : 0;
+
+ /*
+ * TODO: For IBSS we need to get atim_window from mac80211,
+ * for now just always use 0
+ */
+ ctx->timing.atim_window = 0;
+
+ if (ctx->ctxid == IWL_RXON_CTX_PAN &&
+ (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) &&
+ iwl_is_associated(priv, IWL_RXON_CTX_BSS) &&
+ priv->contexts[IWL_RXON_CTX_BSS].vif &&
+ priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) {
+ ctx->timing.beacon_interval =
+ priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval;
+ beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
+ } else if (ctx->ctxid == IWL_RXON_CTX_BSS &&
+ iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
+ priv->contexts[IWL_RXON_CTX_PAN].vif &&
+ priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int &&
+ (!iwl_is_associated_ctx(ctx) || !ctx->vif ||
+ !ctx->vif->bss_conf.beacon_int)) {
+ ctx->timing.beacon_interval =
+ priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval;
+ beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
+ } else {
+ beacon_int = iwl_adjust_beacon_interval(beacon_int,
+ IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT);
+ ctx->timing.beacon_interval = cpu_to_le16(beacon_int);
+ }
+
+ ctx->beacon_int = beacon_int;
+
+ tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */
+ interval_tm = beacon_int * TIME_UNIT;
+ rem = do_div(tsf, interval_tm);
+ ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
+
+ ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1;
+
+ IWL_DEBUG_ASSOC(priv,
+ "beacon interval %d beacon timer %d beacon tim %d\n",
+ le16_to_cpu(ctx->timing.beacon_interval),
+ le32_to_cpu(ctx->timing.beacon_init_val),
+ le16_to_cpu(ctx->timing.atim_window));
+
+ return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
+ 0, sizeof(ctx->timing), &ctx->timing);
+}
+
+static int iwlagn_rxon_disconn(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ int ret;
+ struct iwl_rxon_cmd *active = (void *)&ctx->active;
+
+ if (ctx->ctxid == IWL_RXON_CTX_BSS) {
+ ret = iwlagn_disable_bss(priv, ctx, &ctx->staging);
+ } else {
+ ret = iwlagn_disable_pan(priv, ctx, &ctx->staging);
+ if (ret)
+ return ret;
+ if (ctx->vif) {
+ ret = iwl_send_rxon_timing(priv, ctx);
+ if (ret) {
+ IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
+ return ret;
+ }
+ ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging);
+ }
+ }
+ if (ret)
+ return ret;
+
+ /*
+ * Un-assoc RXON clears the station table and WEP
+ * keys, so we have to restore those afterwards.
+ */
+ iwl_clear_ucode_stations(priv, ctx);
+ /* update -- might need P2P now */
+ iwl_update_bcast_station(priv, ctx);
+ iwl_restore_stations(priv, ctx);
+ ret = iwl_restore_default_wep_keys(priv, ctx);
+ if (ret) {
+ IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret);
+ return ret;
+ }
+
+ memcpy(active, &ctx->staging, sizeof(*active));
+ return 0;
+}
+
+static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
+{
+ int ret;
+ s8 prev_tx_power;
+ bool defer;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+
+ if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED)
+ return 0;
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (priv->tx_power_user_lmt == tx_power && !force)
+ return 0;
+
+ if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) {
+ IWL_WARN(priv,
+ "Requested user TXPOWER %d below lower limit %d.\n",
+ tx_power,
+ IWLAGN_TX_POWER_TARGET_POWER_MIN);
+ return -EINVAL;
+ }
+
+ if (tx_power > DIV_ROUND_UP(priv->nvm_data->max_tx_pwr_half_dbm, 2)) {
+ IWL_WARN(priv,
+ "Requested user TXPOWER %d above upper limit %d.\n",
+ tx_power, priv->nvm_data->max_tx_pwr_half_dbm);
+ return -EINVAL;
+ }
+
+ if (!iwl_is_ready_rf(priv))
+ return -EIO;
+
+ /* scan complete and commit_rxon use tx_power_next value,
+ * it always need to be updated for newest request */
+ priv->tx_power_next = tx_power;
+
+ /* do not set tx power when scanning or channel changing */
+ defer = test_bit(STATUS_SCANNING, &priv->status) ||
+ memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging));
+ if (defer && !force) {
+ IWL_DEBUG_INFO(priv, "Deferring tx power set\n");
+ return 0;
+ }
+
+ prev_tx_power = priv->tx_power_user_lmt;
+ priv->tx_power_user_lmt = tx_power;
+
+ ret = iwlagn_send_tx_power(priv);
+
+ /* if fail to set tx_power, restore the orig. tx power */
+ if (ret) {
+ priv->tx_power_user_lmt = prev_tx_power;
+ priv->tx_power_next = prev_tx_power;
+ }
+ return ret;
+}
+
+static int iwlagn_rxon_connect(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ int ret;
+ struct iwl_rxon_cmd *active = (void *)&ctx->active;
+
+ /* RXON timing must be before associated RXON */
+ if (ctx->ctxid == IWL_RXON_CTX_BSS) {
+ ret = iwl_send_rxon_timing(priv, ctx);
+ if (ret) {
+ IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
+ return ret;
+ }
+ }
+ /* QoS info may be cleared by previous un-assoc RXON */
+ iwlagn_update_qos(priv, ctx);
+
+ /*
+ * We'll run into this code path when beaconing is
+ * enabled, but then we also need to send the beacon
+ * to the device.
+ */
+ if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) {
+ ret = iwlagn_update_beacon(priv, ctx->vif);
+ if (ret) {
+ IWL_ERR(priv,
+ "Error sending required beacon (%d)!\n",
+ ret);
+ return ret;
+ }
+ }
+
+ priv->start_calib = 0;
+ /*
+ * Apply the new configuration.
+ *
+ * Associated RXON doesn't clear the station table in uCode,
+ * so we don't need to restore stations etc. after this.
+ */
+ ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
+ sizeof(struct iwl_rxon_cmd), &ctx->staging);
+ if (ret) {
+ IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
+ return ret;
+ }
+ memcpy(active, &ctx->staging, sizeof(*active));
+
+ /* IBSS beacon needs to be sent after setting assoc */
+ if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC))
+ if (iwlagn_update_beacon(priv, ctx->vif))
+ IWL_ERR(priv, "Error sending IBSS beacon\n");
+ iwl_init_sensitivity(priv);
+
+ /*
+ * If we issue a new RXON command which required a tune then
+ * we must send a new TXPOWER command or we won't be able to
+ * Tx any frames.
+ *
+ * It's expected we set power here if channel is changing.
+ */
+ ret = iwl_set_tx_power(priv, priv->tx_power_next, true);
+ if (ret) {
+ IWL_ERR(priv, "Error sending TX power (%d)\n", ret);
+ return ret;
+ }
+
+ if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION &&
+ priv->cfg->ht_params && priv->cfg->ht_params->smps_mode)
+ ieee80211_request_smps(ctx->vif,
+ priv->cfg->ht_params->smps_mode);
+
+ return 0;
+}
+
+int iwlagn_set_pan_params(struct iwl_priv *priv)
+{
+ struct iwl_wipan_params_cmd cmd;
+ struct iwl_rxon_context *ctx_bss, *ctx_pan;
+ int slot0 = 300, slot1 = 0;
+ int ret;
+
+ if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS))
+ return 0;
+
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+
+ lockdep_assert_held(&priv->mutex);
+
+ ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS];
+ ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN];
+
+ /*
+ * If the PAN context is inactive, then we don't need
+ * to update the PAN parameters, the last thing we'll
+ * have done before it goes inactive is making the PAN
+ * parameters be WLAN-only.
+ */
+ if (!ctx_pan->is_active)
+ return 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ /* only 2 slots are currently allowed */
+ cmd.num_slots = 2;
+
+ cmd.slots[0].type = 0; /* BSS */
+ cmd.slots[1].type = 1; /* PAN */
+
+ if (ctx_bss->vif && ctx_pan->vif) {
+ int bcnint = ctx_pan->beacon_int;
+ int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
+
+ /* should be set, but seems unused?? */
+ cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE);
+
+ if (ctx_pan->vif->type == NL80211_IFTYPE_AP &&
+ bcnint &&
+ bcnint != ctx_bss->beacon_int) {
+ IWL_ERR(priv,
+ "beacon intervals don't match (%d, %d)\n",
+ ctx_bss->beacon_int, ctx_pan->beacon_int);
+ } else
+ bcnint = max_t(int, bcnint,
+ ctx_bss->beacon_int);
+ if (!bcnint)
+ bcnint = DEFAULT_BEACON_INTERVAL;
+ slot0 = bcnint / 2;
+ slot1 = bcnint - slot0;
+
+ if (test_bit(STATUS_SCAN_HW, &priv->status) ||
+ (!ctx_bss->vif->bss_conf.idle &&
+ !ctx_bss->vif->bss_conf.assoc)) {
+ slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
+ slot1 = IWL_MIN_SLOT_TIME;
+ } else if (!ctx_pan->vif->bss_conf.idle &&
+ !ctx_pan->vif->bss_conf.assoc) {
+ slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
+ slot0 = IWL_MIN_SLOT_TIME;
+ }
+ } else if (ctx_pan->vif) {
+ slot0 = 0;
+ slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) *
+ ctx_pan->beacon_int;
+ slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1);
+
+ if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+ slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME;
+ slot1 = IWL_MIN_SLOT_TIME;
+ }
+ }
+
+ cmd.slots[0].width = cpu_to_le16(slot0);
+ cmd.slots[1].width = cpu_to_le16(slot1);
+
+ ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0,
+ sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret);
+
+ return ret;
+}
+
+static void _iwl_set_rxon_ht(struct iwl_priv *priv,
+ struct iwl_ht_config *ht_conf,
+ struct iwl_rxon_context *ctx)
+{
+ struct iwl_rxon_cmd *rxon = &ctx->staging;
+
+ if (!ctx->ht.enabled) {
+ rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
+ RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
+ RXON_FLG_HT40_PROT_MSK |
+ RXON_FLG_HT_PROT_MSK);
+ return;
+ }
+
+ /* FIXME: if the definition of ht.protection changed, the "translation"
+ * will be needed for rxon->flags
+ */
+ rxon->flags |= cpu_to_le32(ctx->ht.protection <<
+ RXON_FLG_HT_OPERATING_MODE_POS);
+
+ /* Set up channel bandwidth:
+ * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
+ /* clear the HT channel mode before set the mode */
+ rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
+ RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
+ if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) {
+ /* pure ht40 */
+ if (ctx->ht.protection ==
+ IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
+ /*
+ * Note: control channel is opposite of extension
+ * channel
+ */
+ switch (ctx->ht.extension_chan_offset) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ rxon->flags &=
+ ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ rxon->flags |=
+ RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+ break;
+ }
+ } else {
+ /*
+ * Note: control channel is opposite of extension
+ * channel
+ */
+ switch (ctx->ht.extension_chan_offset) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ rxon->flags &=
+ ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+ default:
+ /*
+ * channel location only valid if in Mixed
+ * mode
+ */
+ IWL_ERR(priv,
+ "invalid extension channel offset\n");
+ break;
+ }
+ }
+ } else {
+ rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
+ }
+
+ iwlagn_set_rxon_chain(priv, ctx);
+
+ IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X "
+ "extension channel offset 0x%x\n",
+ le32_to_cpu(rxon->flags), ctx->ht.protection,
+ ctx->ht.extension_chan_offset);
+}
+
+void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf)
+{
+ struct iwl_rxon_context *ctx;
+
+ for_each_context(priv, ctx)
+ _iwl_set_rxon_ht(priv, ht_conf, ctx);
+}
+
+/**
+ * iwl_set_rxon_channel - Set the band and channel values in staging RXON
+ * @ch: requested channel as a pointer to struct ieee80211_channel
+
+ * NOTE: Does not commit to the hardware; it sets appropriate bit fields
+ * in the staging RXON flag structure based on the ch->band
+ */
+void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch,
+ struct iwl_rxon_context *ctx)
+{
+ enum ieee80211_band band = ch->band;
+ u16 channel = ch->hw_value;
+
+ if ((le16_to_cpu(ctx->staging.channel) == channel) &&
+ (priv->band == band))
+ return;
+
+ ctx->staging.channel = cpu_to_le16(channel);
+ if (band == IEEE80211_BAND_5GHZ)
+ ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK;
+ else
+ ctx->staging.flags |= RXON_FLG_BAND_24G_MSK;
+
+ priv->band = band;
+
+ IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band);
+
+}
+
+void iwl_set_flags_for_band(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ enum ieee80211_band band,
+ struct ieee80211_vif *vif)
+{
+ if (band == IEEE80211_BAND_5GHZ) {
+ ctx->staging.flags &=
+ ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK
+ | RXON_FLG_CCK_MSK);
+ ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
+ } else {
+ /* Copied from iwl_post_associate() */
+ if (vif && vif->bss_conf.use_short_slot)
+ ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
+ else
+ ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
+
+ ctx->staging.flags |= RXON_FLG_BAND_24G_MSK;
+ ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK;
+ ctx->staging.flags &= ~RXON_FLG_CCK_MSK;
+ }
+}
+
+static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx, int hw_decrypt)
+{
+ struct iwl_rxon_cmd *rxon = &ctx->staging;
+
+ if (hw_decrypt)
+ rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
+ else
+ rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
+
+}
+
+/* validate RXON structure is valid */
+static int iwl_check_rxon_cmd(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ struct iwl_rxon_cmd *rxon = &ctx->staging;
+ u32 errors = 0;
+
+ if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
+ if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
+ IWL_WARN(priv, "check 2.4G: wrong narrow\n");
+ errors |= BIT(0);
+ }
+ if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
+ IWL_WARN(priv, "check 2.4G: wrong radar\n");
+ errors |= BIT(1);
+ }
+ } else {
+ if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
+ IWL_WARN(priv, "check 5.2G: not short slot!\n");
+ errors |= BIT(2);
+ }
+ if (rxon->flags & RXON_FLG_CCK_MSK) {
+ IWL_WARN(priv, "check 5.2G: CCK!\n");
+ errors |= BIT(3);
+ }
+ }
+ if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
+ IWL_WARN(priv, "mac/bssid mcast!\n");
+ errors |= BIT(4);
+ }
+
+ /* make sure basic rates 6Mbps and 1Mbps are supported */
+ if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 &&
+ (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) {
+ IWL_WARN(priv, "neither 1 nor 6 are basic\n");
+ errors |= BIT(5);
+ }
+
+ if (le16_to_cpu(rxon->assoc_id) > 2007) {
+ IWL_WARN(priv, "aid > 2007\n");
+ errors |= BIT(6);
+ }
+
+ if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
+ == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
+ IWL_WARN(priv, "CCK and short slot\n");
+ errors |= BIT(7);
+ }
+
+ if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
+ == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
+ IWL_WARN(priv, "CCK and auto detect\n");
+ errors |= BIT(8);
+ }
+
+ if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
+ RXON_FLG_TGG_PROTECT_MSK)) ==
+ RXON_FLG_TGG_PROTECT_MSK) {
+ IWL_WARN(priv, "TGg but no auto-detect\n");
+ errors |= BIT(9);
+ }
+
+ if (rxon->channel == 0) {
+ IWL_WARN(priv, "zero channel is invalid\n");
+ errors |= BIT(10);
+ }
+
+ WARN(errors, "Invalid RXON (%#x), channel %d",
+ errors, le16_to_cpu(rxon->channel));
+
+ return errors ? -EINVAL : 0;
+}
+
+/**
+ * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
+ * @priv: staging_rxon is compared to active_rxon
+ *
+ * If the RXON structure is changing enough to require a new tune,
+ * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
+ * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
+ */
+static int iwl_full_rxon_required(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ const struct iwl_rxon_cmd *staging = &ctx->staging;
+ const struct iwl_rxon_cmd *active = &ctx->active;
+
+#define CHK(cond) \
+ if ((cond)) { \
+ IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \
+ return 1; \
+ }
+
+#define CHK_NEQ(c1, c2) \
+ if ((c1) != (c2)) { \
+ IWL_DEBUG_INFO(priv, "need full RXON - " \
+ #c1 " != " #c2 " - %d != %d\n", \
+ (c1), (c2)); \
+ return 1; \
+ }
+
+ /* These items are only settable from the full RXON command */
+ CHK(!iwl_is_associated_ctx(ctx));
+ CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr));
+ CHK(!ether_addr_equal(staging->node_addr, active->node_addr));
+ CHK(!ether_addr_equal(staging->wlap_bssid_addr,
+ active->wlap_bssid_addr));
+ CHK_NEQ(staging->dev_type, active->dev_type);
+ CHK_NEQ(staging->channel, active->channel);
+ CHK_NEQ(staging->air_propagation, active->air_propagation);
+ CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
+ active->ofdm_ht_single_stream_basic_rates);
+ CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
+ active->ofdm_ht_dual_stream_basic_rates);
+ CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates,
+ active->ofdm_ht_triple_stream_basic_rates);
+ CHK_NEQ(staging->assoc_id, active->assoc_id);
+
+ /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
+ * be updated with the RXON_ASSOC command -- however only some
+ * flag transitions are allowed using RXON_ASSOC */
+
+ /* Check if we are not switching bands */
+ CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
+ active->flags & RXON_FLG_BAND_24G_MSK);
+
+ /* Check if we are switching association toggle */
+ CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
+ active->filter_flags & RXON_FILTER_ASSOC_MSK);
+
+#undef CHK
+#undef CHK_NEQ
+
+ return 0;
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+void iwl_print_rx_config_cmd(struct iwl_priv *priv,
+ enum iwl_rxon_context_id ctxid)
+{
+ struct iwl_rxon_context *ctx = &priv->contexts[ctxid];
+ struct iwl_rxon_cmd *rxon = &ctx->staging;
+
+ IWL_DEBUG_RADIO(priv, "RX CONFIG:\n");
+ iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
+ IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n",
+ le16_to_cpu(rxon->channel));
+ IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n",
+ le32_to_cpu(rxon->flags));
+ IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n",
+ le32_to_cpu(rxon->filter_flags));
+ IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type);
+ IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n",
+ rxon->ofdm_basic_rates);
+ IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n",
+ rxon->cck_basic_rates);
+ IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr);
+ IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
+ IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n",
+ le16_to_cpu(rxon->assoc_id));
+}
+#endif
+
+static void iwl_calc_basic_rates(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ int lowest_present_ofdm = 100;
+ int lowest_present_cck = 100;
+ u8 cck = 0;
+ u8 ofdm = 0;
+
+ if (ctx->vif) {
+ struct ieee80211_supported_band *sband;
+ unsigned long basic = ctx->vif->bss_conf.basic_rates;
+ int i;
+
+ sband = priv->hw->wiphy->bands[priv->hw->conf.chandef.chan->band];
+
+ for_each_set_bit(i, &basic, BITS_PER_LONG) {
+ int hw = sband->bitrates[i].hw_value;
+ if (hw >= IWL_FIRST_OFDM_RATE) {
+ ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
+ if (lowest_present_ofdm > hw)
+ lowest_present_ofdm = hw;
+ } else {
+ BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+ cck |= BIT(hw);
+ if (lowest_present_cck > hw)
+ lowest_present_cck = hw;
+ }
+ }
+ }
+
+ /*
+ * Now we've got the basic rates as bitmaps in the ofdm and cck
+ * variables. This isn't sufficient though, as there might not
+ * be all the right rates in the bitmap. E.g. if the only basic
+ * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
+ * and 6 Mbps because the 802.11-2007 standard says in 9.6:
+ *
+ * [...] a STA responding to a received frame shall transmit
+ * its Control Response frame [...] at the highest rate in the
+ * BSSBasicRateSet parameter that is less than or equal to the
+ * rate of the immediately previous frame in the frame exchange
+ * sequence ([...]) and that is of the same modulation class
+ * ([...]) as the received frame. If no rate contained in the
+ * BSSBasicRateSet parameter meets these conditions, then the
+ * control frame sent in response to a received frame shall be
+ * transmitted at the highest mandatory rate of the PHY that is
+ * less than or equal to the rate of the received frame, and
+ * that is of the same modulation class as the received frame.
+ *
+ * As a consequence, we need to add all mandatory rates that are
+ * lower than all of the basic rates to these bitmaps.
+ */
+
+ if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
+ ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE;
+ if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
+ ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE;
+ /* 6M already there or needed so always add */
+ ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE;
+
+ /*
+ * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
+ * Note, however:
+ * - if no CCK rates are basic, it must be ERP since there must
+ * be some basic rates at all, so they're OFDM => ERP PHY
+ * (or we're in 5 GHz, and the cck bitmap will never be used)
+ * - if 11M is a basic rate, it must be ERP as well, so add 5.5M
+ * - if 5.5M is basic, 1M and 2M are mandatory
+ * - if 2M is basic, 1M is mandatory
+ * - if 1M is basic, that's the only valid ACK rate.
+ * As a consequence, it's not as complicated as it sounds, just add
+ * any lower rates to the ACK rate bitmap.
+ */
+ if (IWL_RATE_11M_INDEX < lowest_present_cck)
+ cck |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE;
+ if (IWL_RATE_5M_INDEX < lowest_present_cck)
+ cck |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE;
+ if (IWL_RATE_2M_INDEX < lowest_present_cck)
+ cck |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE;
+ /* 1M already there or needed so always add */
+ cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE;
+
+ IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n",
+ cck, ofdm);
+
+ /* "basic_rates" is a misnomer here -- should be called ACK rates */
+ ctx->staging.cck_basic_rates = cck;
+ ctx->staging.ofdm_basic_rates = ofdm;
+}
+
+/**
+ * iwlagn_commit_rxon - commit staging_rxon to hardware
+ *
+ * The RXON command in staging_rxon is committed to the hardware and
+ * the active_rxon structure is updated with the new data. This
+ * function correctly transitions out of the RXON_ASSOC_MSK state if
+ * a HW tune is required based on the RXON structure changes.
+ *
+ * The connect/disconnect flow should be as the following:
+ *
+ * 1. make sure send RXON command with association bit unset if not connect
+ * this should include the channel and the band for the candidate
+ * to be connected to
+ * 2. Add Station before RXON association with the AP
+ * 3. RXON_timing has to send before RXON for connection
+ * 4. full RXON command - associated bit set
+ * 5. use RXON_ASSOC command to update any flags changes
+ */
+int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
+{
+ /* cast away the const for active_rxon in this function */
+ struct iwl_rxon_cmd *active = (void *)&ctx->active;
+ bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK);
+ int ret;
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (!iwl_is_alive(priv))
+ return -EBUSY;
+
+ /* This function hardcodes a bunch of dual-mode assumptions */
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+
+ if (!ctx->is_active)
+ return 0;
+
+ /* always get timestamp with Rx frame */
+ ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
+
+ /* recalculate basic rates */
+ iwl_calc_basic_rates(priv, ctx);
+
+ /*
+ * force CTS-to-self frames protection if RTS-CTS is not preferred
+ * one aggregation protection method
+ */
+ if (!priv->hw_params.use_rts_for_aggregation)
+ ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
+
+ if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
+ !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
+ ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
+ else
+ ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
+
+ iwl_print_rx_config_cmd(priv, ctx->ctxid);
+ ret = iwl_check_rxon_cmd(priv, ctx);
+ if (ret) {
+ IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * receive commit_rxon request
+ * abort any previous channel switch if still in process
+ */
+ if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) &&
+ (priv->switch_channel != ctx->staging.channel)) {
+ IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
+ le16_to_cpu(priv->switch_channel));
+ iwl_chswitch_done(priv, false);
+ }
+
+ /*
+ * If we don't need to send a full RXON, we can use
+ * iwl_rxon_assoc_cmd which is used to reconfigure filter
+ * and other flags for the current radio configuration.
+ */
+ if (!iwl_full_rxon_required(priv, ctx)) {
+ ret = iwlagn_send_rxon_assoc(priv, ctx);
+ if (ret) {
+ IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret);
+ return ret;
+ }
+
+ memcpy(active, &ctx->staging, sizeof(*active));
+ /*
+ * We do not commit tx power settings while channel changing,
+ * do it now if after settings changed.
+ */
+ iwl_set_tx_power(priv, priv->tx_power_next, false);
+
+ /* make sure we are in the right PS state */
+ iwl_power_update_mode(priv, true);
+
+ return 0;
+ }
+
+ iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.sw_crypto);
+
+ IWL_DEBUG_INFO(priv,
+ "Going to commit RXON\n"
+ " * with%s RXON_FILTER_ASSOC_MSK\n"
+ " * channel = %d\n"
+ " * bssid = %pM\n",
+ (new_assoc ? "" : "out"),
+ le16_to_cpu(ctx->staging.channel),
+ ctx->staging.bssid_addr);
+
+ /*
+ * Always clear associated first, but with the correct config.
+ * This is required as for example station addition for the
+ * AP station must be done after the BSSID is set to correctly
+ * set up filters in the device.
+ */
+ ret = iwlagn_rxon_disconn(priv, ctx);
+ if (ret)
+ return ret;
+
+ ret = iwlagn_set_pan_params(priv);
+ if (ret)
+ return ret;
+
+ if (new_assoc)
+ return iwlagn_rxon_connect(priv, ctx);
+
+ return 0;
+}
+
+void iwlagn_config_ht40(struct ieee80211_conf *conf,
+ struct iwl_rxon_context *ctx)
+{
+ if (conf_is_ht40_minus(conf)) {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ ctx->ht.is_40mhz = true;
+ } else if (conf_is_ht40_plus(conf)) {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ctx->ht.is_40mhz = true;
+ } else {
+ ctx->ht.extension_chan_offset =
+ IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ ctx->ht.is_40mhz = false;
+ }
+}
+
+int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_rxon_context *ctx;
+ struct ieee80211_conf *conf = &hw->conf;
+ struct ieee80211_channel *channel = conf->chandef.chan;
+ int ret = 0;
+
+ IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed);
+
+ mutex_lock(&priv->mutex);
+
+ if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
+ IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
+ goto out;
+ }
+
+ if (!iwl_is_ready(priv)) {
+ IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
+ goto out;
+ }
+
+ if (changed & (IEEE80211_CONF_CHANGE_SMPS |
+ IEEE80211_CONF_CHANGE_CHANNEL)) {
+ /* mac80211 uses static for non-HT which is what we want */
+ priv->current_ht_config.smps = conf->smps_mode;
+
+ /*
+ * Recalculate chain counts.
+ *
+ * If monitor mode is enabled then mac80211 will
+ * set up the SM PS mode to OFF if an HT channel is
+ * configured.
+ */
+ for_each_context(priv, ctx)
+ iwlagn_set_rxon_chain(priv, ctx);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ for_each_context(priv, ctx) {
+ /* Configure HT40 channels */
+ if (ctx->ht.enabled != conf_is_ht(conf))
+ ctx->ht.enabled = conf_is_ht(conf);
+
+ if (ctx->ht.enabled) {
+ /* if HT40 is used, it should not change
+ * after associated except channel switch */
+ if (!ctx->ht.is_40mhz ||
+ !iwl_is_associated_ctx(ctx))
+ iwlagn_config_ht40(conf, ctx);
+ } else
+ ctx->ht.is_40mhz = false;
+
+ /*
+ * Default to no protection. Protection mode will
+ * later be set from BSS config in iwl_ht_conf
+ */
+ ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
+
+ /* if we are switching from ht to 2.4 clear flags
+ * from any ht related info since 2.4 does not
+ * support ht */
+ if (le16_to_cpu(ctx->staging.channel) !=
+ channel->hw_value)
+ ctx->staging.flags = 0;
+
+ iwl_set_rxon_channel(priv, channel, ctx);
+ iwl_set_rxon_ht(priv, &priv->current_ht_config);
+
+ iwl_set_flags_for_band(priv, ctx, channel->band,
+ ctx->vif);
+ }
+
+ iwl_update_bcast_stations(priv);
+ }
+
+ if (changed & (IEEE80211_CONF_CHANGE_PS |
+ IEEE80211_CONF_CHANGE_IDLE)) {
+ ret = iwl_power_update_mode(priv, false);
+ if (ret)
+ IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
+ priv->tx_power_user_lmt, conf->power_level);
+
+ iwl_set_tx_power(priv, conf->power_level, false);
+ }
+
+ for_each_context(priv, ctx) {
+ if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
+ continue;
+ iwlagn_commit_rxon(priv, ctx);
+ }
+ out:
+ mutex_unlock(&priv->mutex);
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+
+ return ret;
+}
+
+static void iwlagn_check_needed_chains(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ struct ieee80211_vif *vif = ctx->vif;
+ struct iwl_rxon_context *tmp;
+ struct ieee80211_sta *sta;
+ struct iwl_ht_config *ht_conf = &priv->current_ht_config;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ bool need_multiple;
+
+ lockdep_assert_held(&priv->mutex);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ rcu_read_lock();
+ sta = ieee80211_find_sta(vif, bss_conf->bssid);
+ if (!sta) {
+ /*
+ * If at all, this can only happen through a race
+ * when the AP disconnects us while we're still
+ * setting up the connection, in that case mac80211
+ * will soon tell us about that.
+ */
+ need_multiple = false;
+ rcu_read_unlock();
+ break;
+ }
+
+ ht_cap = &sta->ht_cap;
+
+ need_multiple = true;
+
+ /*
+ * If the peer advertises no support for receiving 2 and 3
+ * stream MCS rates, it can't be transmitting them either.
+ */
+ if (ht_cap->mcs.rx_mask[1] == 0 &&
+ ht_cap->mcs.rx_mask[2] == 0) {
+ need_multiple = false;
+ } else if (!(ht_cap->mcs.tx_params &
+ IEEE80211_HT_MCS_TX_DEFINED)) {
+ /* If it can't TX MCS at all ... */
+ need_multiple = false;
+ } else if (ht_cap->mcs.tx_params &
+ IEEE80211_HT_MCS_TX_RX_DIFF) {
+ int maxstreams;
+
+ /*
+ * But if it can receive them, it might still not
+ * be able to transmit them, which is what we need
+ * to check here -- so check the number of streams
+ * it advertises for TX (if different from RX).
+ */
+
+ maxstreams = (ht_cap->mcs.tx_params &
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK);
+ maxstreams >>=
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
+ maxstreams += 1;
+
+ if (maxstreams <= 1)
+ need_multiple = false;
+ }
+
+ rcu_read_unlock();
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ /* currently */
+ need_multiple = false;
+ break;
+ default:
+ /* only AP really */
+ need_multiple = true;
+ break;
+ }
+
+ ctx->ht_need_multiple_chains = need_multiple;
+
+ if (!need_multiple) {
+ /* check all contexts */
+ for_each_context(priv, tmp) {
+ if (!tmp->vif)
+ continue;
+ if (tmp->ht_need_multiple_chains) {
+ need_multiple = true;
+ break;
+ }
+ }
+ }
+
+ ht_conf->single_chain_sufficient = !need_multiple;
+}
+
+static void iwlagn_chain_noise_reset(struct iwl_priv *priv)
+{
+ struct iwl_chain_noise_data *data = &priv->chain_noise_data;
+ int ret;
+
+ if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
+ return;
+
+ if ((data->state == IWL_CHAIN_NOISE_ALIVE) &&
+ iwl_is_any_associated(priv)) {
+ struct iwl_calib_chain_noise_reset_cmd cmd;
+
+ /* clear data for chain noise calibration algorithm */
+ data->chain_noise_a = 0;
+ data->chain_noise_b = 0;
+ data->chain_noise_c = 0;
+ data->chain_signal_a = 0;
+ data->chain_signal_b = 0;
+ data->chain_signal_c = 0;
+ data->beacon_count = 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+ iwl_set_calib_hdr(&cmd.hdr,
+ priv->phy_calib_chain_noise_reset_cmd);
+ ret = iwl_dvm_send_cmd_pdu(priv,
+ REPLY_PHY_CALIBRATION_CMD,
+ 0, sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(priv,
+ "Could not send REPLY_PHY_CALIBRATION_CMD\n");
+ data->state = IWL_CHAIN_NOISE_ACCUMULATE;
+ IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n");
+ }
+}
+
+void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changes)
+{
+ struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+ int ret;
+ bool force = false;
+
+ mutex_lock(&priv->mutex);
+
+ if (changes & BSS_CHANGED_IDLE && bss_conf->idle) {
+ /*
+ * If we go idle, then clearly no "passive-no-rx"
+ * workaround is needed any more, this is a reset.
+ */
+ iwlagn_lift_passive_no_rx(priv);
+ }
+
+ if (unlikely(!iwl_is_ready(priv))) {
+ IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
+ mutex_unlock(&priv->mutex);
+ return;
+ }
+
+ if (unlikely(!ctx->vif)) {
+ IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n");
+ mutex_unlock(&priv->mutex);
+ return;
+ }
+
+ if (changes & BSS_CHANGED_BEACON_INT)
+ force = true;
+
+ if (changes & BSS_CHANGED_QOS) {
+ ctx->qos_data.qos_active = bss_conf->qos;
+ iwlagn_update_qos(priv, ctx);
+ }
+
+ ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid);
+ if (vif->bss_conf.use_short_preamble)
+ ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
+ else
+ ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
+
+ if (changes & BSS_CHANGED_ASSOC) {
+ if (bss_conf->assoc) {
+ priv->timestamp = bss_conf->sync_tsf;
+ ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
+ } else {
+ ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+
+ if (ctx->ctxid == IWL_RXON_CTX_BSS)
+ priv->have_rekey_data = false;
+ }
+
+ iwlagn_bt_coex_rssi_monitor(priv);
+ }
+
+ if (ctx->ht.enabled) {
+ ctx->ht.protection = bss_conf->ht_operation_mode &
+ IEEE80211_HT_OP_MODE_PROTECTION;
+ ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+ iwlagn_check_needed_chains(priv, ctx, bss_conf);
+ iwl_set_rxon_ht(priv, &priv->current_ht_config);
+ }
+
+ iwlagn_set_rxon_chain(priv, ctx);
+
+ if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ))
+ ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
+ else
+ ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
+
+ if (bss_conf->use_cts_prot)
+ ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
+ else
+ ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
+
+ memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
+
+ if (vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_ADHOC) {
+ if (vif->bss_conf.enable_beacon) {
+ ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
+ priv->beacon_ctx = ctx;
+ } else {
+ ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ priv->beacon_ctx = NULL;
+ }
+ }
+
+ /*
+ * If the ucode decides to do beacon filtering before
+ * association, it will lose beacons that are needed
+ * before sending frames out on passive channels. This
+ * causes association failures on those channels. Enable
+ * receiving beacons in such cases.
+ */
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (!bss_conf->assoc)
+ ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK;
+ else
+ ctx->staging.filter_flags &=
+ ~RXON_FILTER_BCON_AWARE_MSK;
+ }
+
+ if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
+ iwlagn_commit_rxon(priv, ctx);
+
+ if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) {
+ /*
+ * The chain noise calibration will enable PM upon
+ * completion. If calibration has already been run
+ * then we need to enable power management here.
+ */
+ if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE)
+ iwl_power_update_mode(priv, false);
+
+ /* Enable RX differential gain and sensitivity calibrations */
+ iwlagn_chain_noise_reset(priv);
+ priv->start_calib = 1;
+ }
+
+ if (changes & BSS_CHANGED_IBSS) {
+ ret = iwlagn_manage_ibss_station(priv, vif,
+ bss_conf->ibss_joined);
+ if (ret)
+ IWL_ERR(priv, "failed to %s IBSS station %pM\n",
+ bss_conf->ibss_joined ? "add" : "remove",
+ bss_conf->bssid);
+ }
+
+ if (changes & BSS_CHANGED_BEACON && priv->beacon_ctx == ctx) {
+ if (iwlagn_update_beacon(priv, vif))
+ IWL_ERR(priv, "Error updating beacon\n");
+ }
+
+ mutex_unlock(&priv->mutex);
+}
+
+void iwlagn_post_scan(struct iwl_priv *priv)
+{
+ struct iwl_rxon_context *ctx;
+
+ /*
+ * We do not commit power settings while scan is pending,
+ * do it now if the settings changed.
+ */
+ iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false);
+ iwl_set_tx_power(priv, priv->tx_power_next, false);
+
+ /*
+ * Since setting the RXON may have been deferred while
+ * performing the scan, fire one off if needed
+ */
+ for_each_context(priv, ctx)
+ if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
+ iwlagn_commit_rxon(priv, ctx);
+
+ iwlagn_set_pan_params(priv);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c
new file mode 100644
index 000000000000..81a2ddbe9569
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c
@@ -0,0 +1,1075 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "dev.h"
+#include "agn.h"
+
+/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
+ * sending probe req. This should be set long enough to hear probe responses
+ * from more than one AP. */
+#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */
+#define IWL_ACTIVE_DWELL_TIME_52 (20)
+
+#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3)
+#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2)
+
+/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
+ * Must be set longer than active dwell time.
+ * For the most reliable scan, set > AP beacon interval (typically 100msec). */
+#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */
+#define IWL_PASSIVE_DWELL_TIME_52 (10)
+#define IWL_PASSIVE_DWELL_BASE (100)
+#define IWL_CHANNEL_TUNE_TIME 5
+#define MAX_SCAN_CHANNEL 50
+
+/* For reset radio, need minimal dwell time only */
+#define IWL_RADIO_RESET_DWELL_TIME 5
+
+static int iwl_send_scan_abort(struct iwl_priv *priv)
+{
+ int ret;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_SCAN_ABORT_CMD,
+ .flags = CMD_WANT_SKB,
+ };
+ __le32 *status;
+
+ /* Exit instantly with error when device is not ready
+ * to receive scan abort command or it does not perform
+ * hardware scan currently */
+ if (!test_bit(STATUS_READY, &priv->status) ||
+ !test_bit(STATUS_SCAN_HW, &priv->status) ||
+ test_bit(STATUS_FW_ERROR, &priv->status))
+ return -EIO;
+
+ ret = iwl_dvm_send_cmd(priv, &cmd);
+ if (ret)
+ return ret;
+
+ status = (void *)cmd.resp_pkt->data;
+ if (*status != CAN_ABORT_STATUS) {
+ /* The scan abort will return 1 for success or
+ * 2 for "failure". A failure condition can be
+ * due to simply not being in an active scan which
+ * can occur if we send the scan abort before we
+ * the microcode has notified us that a scan is
+ * completed. */
+ IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n",
+ le32_to_cpu(*status));
+ ret = -EIO;
+ }
+
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
+{
+ /* check if scan was requested from mac80211 */
+ if (priv->scan_request) {
+ IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n");
+ ieee80211_scan_completed(priv->hw, aborted);
+ }
+
+ priv->scan_type = IWL_SCAN_NORMAL;
+ priv->scan_vif = NULL;
+ priv->scan_request = NULL;
+}
+
+static void iwl_process_scan_complete(struct iwl_priv *priv)
+{
+ bool aborted;
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status))
+ return;
+
+ IWL_DEBUG_SCAN(priv, "Completed scan.\n");
+
+ cancel_delayed_work(&priv->scan_check);
+
+ aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+ if (aborted)
+ IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n");
+
+ if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Scan already completed.\n");
+ goto out_settings;
+ }
+
+ if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
+ int err;
+
+ /* Check if mac80211 requested scan during our internal scan */
+ if (priv->scan_request == NULL)
+ goto out_complete;
+
+ /* If so request a new scan */
+ err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL,
+ priv->scan_request->channels[0]->band);
+ if (err) {
+ IWL_DEBUG_SCAN(priv,
+ "failed to initiate pending scan: %d\n", err);
+ aborted = true;
+ goto out_complete;
+ }
+
+ return;
+ }
+
+out_complete:
+ iwl_complete_scan(priv, aborted);
+
+out_settings:
+ /* Can we still talk to firmware ? */
+ if (!iwl_is_ready_rf(priv))
+ return;
+
+ iwlagn_post_scan(priv);
+}
+
+void iwl_force_scan_end(struct iwl_priv *priv)
+{
+ lockdep_assert_held(&priv->mutex);
+
+ if (!test_bit(STATUS_SCANNING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n");
+ return;
+ }
+
+ IWL_DEBUG_SCAN(priv, "Forcing scan end\n");
+ clear_bit(STATUS_SCANNING, &priv->status);
+ clear_bit(STATUS_SCAN_HW, &priv->status);
+ clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+ clear_bit(STATUS_SCAN_COMPLETE, &priv->status);
+ iwl_complete_scan(priv, true);
+}
+
+static void iwl_do_scan_abort(struct iwl_priv *priv)
+{
+ int ret;
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (!test_bit(STATUS_SCANNING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n");
+ return;
+ }
+
+ if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Scan abort in progress\n");
+ return;
+ }
+
+ ret = iwl_send_scan_abort(priv);
+ if (ret) {
+ IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret);
+ iwl_force_scan_end(priv);
+ } else
+ IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n");
+}
+
+/**
+ * iwl_scan_cancel - Cancel any currently executing HW scan
+ */
+int iwl_scan_cancel(struct iwl_priv *priv)
+{
+ IWL_DEBUG_SCAN(priv, "Queuing abort scan\n");
+ queue_work(priv->workqueue, &priv->abort_scan);
+ return 0;
+}
+
+/**
+ * iwl_scan_cancel_timeout - Cancel any currently executing HW scan
+ * @ms: amount of time to wait (in milliseconds) for scan to abort
+ *
+ */
+void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(ms);
+
+ lockdep_assert_held(&priv->mutex);
+
+ IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n");
+
+ iwl_do_scan_abort(priv);
+
+ while (time_before_eq(jiffies, timeout)) {
+ if (!test_bit(STATUS_SCAN_HW, &priv->status))
+ goto finished;
+ msleep(20);
+ }
+
+ return;
+
+ finished:
+ /*
+ * Now STATUS_SCAN_HW is clear. This means that the
+ * device finished, but the background work is going
+ * to execute at best as soon as we release the mutex.
+ * Since we need to be able to issue a new scan right
+ * after this function returns, run the complete here.
+ * The STATUS_SCAN_COMPLETE bit will then be cleared
+ * and prevent the background work from "completing"
+ * a possible new scan.
+ */
+ iwl_process_scan_complete(priv);
+}
+
+/* Service response to REPLY_SCAN_CMD (0x80) */
+static void iwl_rx_reply_scan(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+#ifdef CONFIG_IWLWIFI_DEBUG
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_scanreq_notification *notif = (void *)pkt->data;
+
+ IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status);
+#endif
+}
+
+/* Service SCAN_START_NOTIFICATION (0x82) */
+static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_scanstart_notification *notif = (void *)pkt->data;
+
+ priv->scan_start_tsf = le32_to_cpu(notif->tsf_low);
+ IWL_DEBUG_SCAN(priv, "Scan start: "
+ "%d [802.11%s] "
+ "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n",
+ notif->channel,
+ notif->band ? "bg" : "a",
+ le32_to_cpu(notif->tsf_high),
+ le32_to_cpu(notif->tsf_low),
+ notif->status, notif->beacon_timer);
+}
+
+/* Service SCAN_RESULTS_NOTIFICATION (0x83) */
+static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+#ifdef CONFIG_IWLWIFI_DEBUG
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_scanresults_notification *notif = (void *)pkt->data;
+
+ IWL_DEBUG_SCAN(priv, "Scan ch.res: "
+ "%d [802.11%s] "
+ "probe status: %u:%u "
+ "(TSF: 0x%08X:%08X) - %d "
+ "elapsed=%lu usec\n",
+ notif->channel,
+ notif->band ? "bg" : "a",
+ notif->probe_status, notif->num_probe_not_sent,
+ le32_to_cpu(notif->tsf_high),
+ le32_to_cpu(notif->tsf_low),
+ le32_to_cpu(notif->statistics[0]),
+ le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf);
+#endif
+}
+
+/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
+static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data;
+
+ IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
+ scan_notif->scanned_channels,
+ scan_notif->tsf_low,
+ scan_notif->tsf_high, scan_notif->status);
+
+ IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n",
+ (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
+ jiffies_to_msecs(jiffies - priv->scan_start));
+
+ /*
+ * When aborting, we run the scan completed background work inline
+ * and the background work must then do nothing. The SCAN_COMPLETE
+ * bit helps implement that logic and thus needs to be set before
+ * queueing the work. Also, since the scan abort waits for SCAN_HW
+ * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW
+ * to avoid a race there.
+ */
+ set_bit(STATUS_SCAN_COMPLETE, &priv->status);
+ clear_bit(STATUS_SCAN_HW, &priv->status);
+ queue_work(priv->workqueue, &priv->scan_completed);
+
+ if (priv->iw_mode != NL80211_IFTYPE_ADHOC &&
+ iwl_advanced_bt_coexist(priv) &&
+ priv->bt_status != scan_notif->bt_status) {
+ if (scan_notif->bt_status) {
+ /* BT on */
+ if (!priv->bt_ch_announce)
+ priv->bt_traffic_load =
+ IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
+ /*
+ * otherwise, no traffic load information provided
+ * no changes made
+ */
+ } else {
+ /* BT off */
+ priv->bt_traffic_load =
+ IWL_BT_COEX_TRAFFIC_LOAD_NONE;
+ }
+ priv->bt_status = scan_notif->bt_status;
+ queue_work(priv->workqueue,
+ &priv->bt_traffic_change_work);
+ }
+}
+
+void iwl_setup_rx_scan_handlers(struct iwl_priv *priv)
+{
+ /* scan handlers */
+ priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan;
+ priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif;
+ priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] =
+ iwl_rx_scan_results_notif;
+ priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
+ iwl_rx_scan_complete_notif;
+}
+
+static u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
+ enum ieee80211_band band, u8 n_probes)
+{
+ if (band == IEEE80211_BAND_5GHZ)
+ return IWL_ACTIVE_DWELL_TIME_52 +
+ IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1);
+ else
+ return IWL_ACTIVE_DWELL_TIME_24 +
+ IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1);
+}
+
+static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time)
+{
+ struct iwl_rxon_context *ctx;
+ int limits[NUM_IWL_RXON_CTX] = {};
+ int n_active = 0;
+ u16 limit;
+
+ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
+
+ /*
+ * If we're associated, we clamp the dwell time 98%
+ * of the beacon interval (minus 2 * channel tune time)
+ * If both contexts are active, we have to restrict to
+ * 1/2 of the minimum of them, because they might be in
+ * lock-step with the time inbetween only half of what
+ * time we'd have in each of them.
+ */
+ for_each_context(priv, ctx) {
+ switch (ctx->staging.dev_type) {
+ case RXON_DEV_TYPE_P2P:
+ /* no timing constraints */
+ continue;
+ case RXON_DEV_TYPE_ESS:
+ default:
+ /* timing constraints if associated */
+ if (!iwl_is_associated_ctx(ctx))
+ continue;
+ break;
+ case RXON_DEV_TYPE_CP:
+ case RXON_DEV_TYPE_2STA:
+ /*
+ * These seem to always have timers for TBTT
+ * active in uCode even when not associated yet.
+ */
+ break;
+ }
+
+ limits[n_active++] = ctx->beacon_int ?: IWL_PASSIVE_DWELL_BASE;
+ }
+
+ switch (n_active) {
+ case 0:
+ return dwell_time;
+ case 2:
+ limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
+ limit /= 2;
+ dwell_time = min(limit, dwell_time);
+ /* fall through to limit further */
+ case 1:
+ limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
+ limit /= n_active;
+ return min(limit, dwell_time);
+ default:
+ WARN_ON_ONCE(1);
+ return dwell_time;
+ }
+}
+
+static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
+ enum ieee80211_band band)
+{
+ u16 passive = (band == IEEE80211_BAND_2GHZ) ?
+ IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 :
+ IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52;
+
+ return iwl_limit_dwell(priv, passive);
+}
+
+/* Return valid, unused, channel for a passive scan to reset the RF */
+static u8 iwl_get_single_channel_number(struct iwl_priv *priv,
+ enum ieee80211_band band)
+{
+ struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band];
+ struct iwl_rxon_context *ctx;
+ int i;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ bool busy = false;
+
+ for_each_context(priv, ctx) {
+ busy = sband->channels[i].hw_value ==
+ le16_to_cpu(ctx->staging.channel);
+ if (busy)
+ break;
+ }
+
+ if (busy)
+ continue;
+
+ if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED))
+ return sband->channels[i].hw_value;
+ }
+
+ return 0;
+}
+
+static int iwl_get_channel_for_reset_scan(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ enum ieee80211_band band,
+ struct iwl_scan_channel *scan_ch)
+{
+ const struct ieee80211_supported_band *sband;
+ u16 channel;
+
+ sband = iwl_get_hw_mode(priv, band);
+ if (!sband) {
+ IWL_ERR(priv, "invalid band\n");
+ return 0;
+ }
+
+ channel = iwl_get_single_channel_number(priv, band);
+ if (channel) {
+ scan_ch->channel = cpu_to_le16(channel);
+ scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
+ scan_ch->active_dwell =
+ cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME);
+ scan_ch->passive_dwell =
+ cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME);
+ /* Set txpower levels to defaults */
+ scan_ch->dsp_atten = 110;
+ if (band == IEEE80211_BAND_5GHZ)
+ scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+ else
+ scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+ return 1;
+ }
+
+ IWL_ERR(priv, "no valid channel found\n");
+ return 0;
+}
+
+static int iwl_get_channels_for_scan(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ enum ieee80211_band band,
+ u8 is_active, u8 n_probes,
+ struct iwl_scan_channel *scan_ch)
+{
+ struct ieee80211_channel *chan;
+ const struct ieee80211_supported_band *sband;
+ u16 passive_dwell = 0;
+ u16 active_dwell = 0;
+ int added, i;
+ u16 channel;
+
+ sband = iwl_get_hw_mode(priv, band);
+ if (!sband)
+ return 0;
+
+ active_dwell = iwl_get_active_dwell_time(priv, band, n_probes);
+ passive_dwell = iwl_get_passive_dwell_time(priv, band);
+
+ if (passive_dwell <= active_dwell)
+ passive_dwell = active_dwell + 1;
+
+ for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) {
+ chan = priv->scan_request->channels[i];
+
+ if (chan->band != band)
+ continue;
+
+ channel = chan->hw_value;
+ scan_ch->channel = cpu_to_le16(channel);
+
+ if (!is_active || (chan->flags & IEEE80211_CHAN_NO_IR))
+ scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
+ else
+ scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
+
+ if (n_probes)
+ scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes);
+
+ scan_ch->active_dwell = cpu_to_le16(active_dwell);
+ scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
+
+ /* Set txpower levels to defaults */
+ scan_ch->dsp_atten = 110;
+
+ /* NOTE: if we were doing 6Mb OFDM for scans we'd use
+ * power level:
+ * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
+ */
+ if (band == IEEE80211_BAND_5GHZ)
+ scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+ else
+ scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+
+ IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n",
+ channel, le32_to_cpu(scan_ch->type),
+ (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
+ "ACTIVE" : "PASSIVE",
+ (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
+ active_dwell : passive_dwell);
+
+ scan_ch++;
+ added++;
+ }
+
+ IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added);
+ return added;
+}
+
+/**
+ * iwl_fill_probe_req - fill in all required fields and IE for probe request
+ */
+
+static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
+ const u8 *ies, int ie_len, const u8 *ssid,
+ u8 ssid_len, int left)
+{
+ int len = 0;
+ u8 *pos = NULL;
+
+ /* Make sure there is enough space for the probe request,
+ * two mandatory IEs and the data */
+ left -= 24;
+ if (left < 0)
+ return 0;
+
+ frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+ eth_broadcast_addr(frame->da);
+ memcpy(frame->sa, ta, ETH_ALEN);
+ eth_broadcast_addr(frame->bssid);
+ frame->seq_ctrl = 0;
+
+ len += 24;
+
+ /* ...next IE... */
+ pos = &frame->u.probe_req.variable[0];
+
+ /* fill in our SSID IE */
+ left -= ssid_len + 2;
+ if (left < 0)
+ return 0;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ssid_len;
+ if (ssid && ssid_len) {
+ memcpy(pos, ssid, ssid_len);
+ pos += ssid_len;
+ }
+
+ len += ssid_len + 2;
+
+ if (WARN_ON(left < ie_len))
+ return len;
+
+ if (ies && ie_len) {
+ memcpy(pos, ies, ie_len);
+ len += ie_len;
+ }
+
+ return (u16)len;
+}
+
+static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+{
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_SCAN_CMD,
+ .len = { sizeof(struct iwl_scan_cmd), },
+ };
+ struct iwl_scan_cmd *scan;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ u32 rate_flags = 0;
+ u16 cmd_len = 0;
+ u16 rx_chain = 0;
+ enum ieee80211_band band;
+ u8 n_probes = 0;
+ u8 rx_ant = priv->nvm_data->valid_rx_ant;
+ u8 rate;
+ bool is_active = false;
+ int chan_mod;
+ u8 active_chains;
+ u8 scan_tx_antennas = priv->nvm_data->valid_tx_ant;
+ int ret;
+ int scan_cmd_size = sizeof(struct iwl_scan_cmd) +
+ MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) +
+ priv->fw->ucode_capa.max_probe_length;
+ const u8 *ssid = NULL;
+ u8 ssid_len = 0;
+
+ if (WARN_ON(priv->scan_type == IWL_SCAN_NORMAL &&
+ (!priv->scan_request ||
+ priv->scan_request->n_channels > MAX_SCAN_CHANNEL)))
+ return -EINVAL;
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (vif)
+ ctx = iwl_rxon_ctx_from_vif(vif);
+
+ if (!priv->scan_cmd) {
+ priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL);
+ if (!priv->scan_cmd) {
+ IWL_DEBUG_SCAN(priv,
+ "fail to allocate memory for scan\n");
+ return -ENOMEM;
+ }
+ }
+ scan = priv->scan_cmd;
+ memset(scan, 0, scan_cmd_size);
+
+ scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
+ scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
+
+ if (iwl_is_any_associated(priv)) {
+ u16 interval = 0;
+ u32 extra;
+ u32 suspend_time = 100;
+ u32 scan_suspend_time = 100;
+
+ IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
+ switch (priv->scan_type) {
+ case IWL_SCAN_RADIO_RESET:
+ interval = 0;
+ break;
+ case IWL_SCAN_NORMAL:
+ interval = vif->bss_conf.beacon_int;
+ break;
+ }
+
+ scan->suspend_time = 0;
+ scan->max_out_time = cpu_to_le32(200 * 1024);
+ if (!interval)
+ interval = suspend_time;
+
+ extra = (suspend_time / interval) << 22;
+ scan_suspend_time = (extra |
+ ((suspend_time % interval) * 1024));
+ scan->suspend_time = cpu_to_le32(scan_suspend_time);
+ IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
+ scan_suspend_time, interval);
+ }
+
+ switch (priv->scan_type) {
+ case IWL_SCAN_RADIO_RESET:
+ IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
+ /*
+ * Override quiet time as firmware checks that active
+ * dwell is >= quiet; since we use passive scan it'll
+ * not actually be used.
+ */
+ scan->quiet_time = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME);
+ break;
+ case IWL_SCAN_NORMAL:
+ if (priv->scan_request->n_ssids) {
+ int i, p = 0;
+ IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
+ /*
+ * The highest priority SSID is inserted to the
+ * probe request template.
+ */
+ ssid_len = priv->scan_request->ssids[0].ssid_len;
+ ssid = priv->scan_request->ssids[0].ssid;
+
+ /*
+ * Invert the order of ssids, the firmware will invert
+ * it back.
+ */
+ for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) {
+ scan->direct_scan[p].id = WLAN_EID_SSID;
+ scan->direct_scan[p].len =
+ priv->scan_request->ssids[i].ssid_len;
+ memcpy(scan->direct_scan[p].ssid,
+ priv->scan_request->ssids[i].ssid,
+ priv->scan_request->ssids[i].ssid_len);
+ n_probes++;
+ p++;
+ }
+ is_active = true;
+ } else
+ IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
+ break;
+ }
+
+ scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
+ scan->tx_cmd.sta_id = ctx->bcast_sta_id;
+ scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+
+ switch (priv->scan_band) {
+ case IEEE80211_BAND_2GHZ:
+ scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
+ chan_mod = le32_to_cpu(
+ priv->contexts[IWL_RXON_CTX_BSS].active.flags &
+ RXON_FLG_CHANNEL_MODE_MSK)
+ >> RXON_FLG_CHANNEL_MODE_POS;
+ if ((priv->scan_request && priv->scan_request->no_cck) ||
+ chan_mod == CHANNEL_MODE_PURE_40) {
+ rate = IWL_RATE_6M_PLCP;
+ } else {
+ rate = IWL_RATE_1M_PLCP;
+ rate_flags = RATE_MCS_CCK_MSK;
+ }
+ /*
+ * Internal scans are passive, so we can indiscriminately set
+ * the BT ignore flag on 2.4 GHz since it applies to TX only.
+ */
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist)
+ scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
+ break;
+ case IEEE80211_BAND_5GHZ:
+ rate = IWL_RATE_6M_PLCP;
+ break;
+ default:
+ IWL_WARN(priv, "Invalid scan band\n");
+ return -EIO;
+ }
+
+ /*
+ * If active scanning is requested but a certain channel is
+ * marked passive, we can do active scanning if we detect
+ * transmissions.
+ *
+ * There is an issue with some firmware versions that triggers
+ * a sysassert on a "good CRC threshold" of zero (== disabled),
+ * on a radar channel even though this means that we should NOT
+ * send probes.
+ *
+ * The "good CRC threshold" is the number of frames that we
+ * need to receive during our dwell time on a channel before
+ * sending out probes -- setting this to a huge value will
+ * mean we never reach it, but at the same time work around
+ * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER
+ * here instead of IWL_GOOD_CRC_TH_DISABLED.
+ *
+ * This was fixed in later versions along with some other
+ * scan changes, and the threshold behaves as a flag in those
+ * versions.
+ */
+ if (priv->new_scan_threshold_behaviour)
+ scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+ IWL_GOOD_CRC_TH_DISABLED;
+ else
+ scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+ IWL_GOOD_CRC_TH_NEVER;
+
+ band = priv->scan_band;
+
+ if (band == IEEE80211_BAND_2GHZ &&
+ priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
+ /* transmit 2.4 GHz probes only on first antenna */
+ scan_tx_antennas = first_antenna(scan_tx_antennas);
+ }
+
+ priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv,
+ priv->scan_tx_ant[band],
+ scan_tx_antennas);
+ rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
+ scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
+
+ /*
+ * In power save mode while associated use one chain,
+ * otherwise use all chains
+ */
+ if (test_bit(STATUS_POWER_PMI, &priv->status) &&
+ !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) {
+ /* rx_ant has been set to all valid chains previously */
+ active_chains = rx_ant &
+ ((u8)(priv->chain_noise_data.active_chains));
+ if (!active_chains)
+ active_chains = rx_ant;
+
+ IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n",
+ priv->chain_noise_data.active_chains);
+
+ rx_ant = first_antenna(active_chains);
+ }
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
+ priv->bt_full_concurrent) {
+ /* operated as 1x1 in full concurrency mode */
+ rx_ant = first_antenna(rx_ant);
+ }
+
+ /* MIMO is not used here, but value is required */
+ rx_chain |=
+ priv->nvm_data->valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
+ rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
+ scan->rx_chain = cpu_to_le16(rx_chain);
+ switch (priv->scan_type) {
+ case IWL_SCAN_NORMAL:
+ cmd_len = iwl_fill_probe_req(
+ (struct ieee80211_mgmt *)scan->data,
+ vif->addr,
+ priv->scan_request->ie,
+ priv->scan_request->ie_len,
+ ssid, ssid_len,
+ scan_cmd_size - sizeof(*scan));
+ break;
+ case IWL_SCAN_RADIO_RESET:
+ /* use bcast addr, will not be transmitted but must be valid */
+ cmd_len = iwl_fill_probe_req(
+ (struct ieee80211_mgmt *)scan->data,
+ iwl_bcast_addr, NULL, 0,
+ NULL, 0,
+ scan_cmd_size - sizeof(*scan));
+ break;
+ default:
+ BUG();
+ }
+ scan->tx_cmd.len = cpu_to_le16(cmd_len);
+
+ scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
+ RXON_FILTER_BCON_AWARE_MSK);
+
+ switch (priv->scan_type) {
+ case IWL_SCAN_RADIO_RESET:
+ scan->channel_count =
+ iwl_get_channel_for_reset_scan(priv, vif, band,
+ (void *)&scan->data[cmd_len]);
+ break;
+ case IWL_SCAN_NORMAL:
+ scan->channel_count =
+ iwl_get_channels_for_scan(priv, vif, band,
+ is_active, n_probes,
+ (void *)&scan->data[cmd_len]);
+ break;
+ }
+
+ if (scan->channel_count == 0) {
+ IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
+ return -EIO;
+ }
+
+ cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) +
+ scan->channel_count * sizeof(struct iwl_scan_channel);
+ cmd.data[0] = scan;
+ cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+ scan->len = cpu_to_le16(cmd.len[0]);
+
+ /* set scan bit here for PAN params */
+ set_bit(STATUS_SCAN_HW, &priv->status);
+
+ ret = iwlagn_set_pan_params(priv);
+ if (ret) {
+ clear_bit(STATUS_SCAN_HW, &priv->status);
+ return ret;
+ }
+
+ ret = iwl_dvm_send_cmd(priv, &cmd);
+ if (ret) {
+ clear_bit(STATUS_SCAN_HW, &priv->status);
+ iwlagn_set_pan_params(priv);
+ }
+
+ return ret;
+}
+
+void iwl_init_scan_params(struct iwl_priv *priv)
+{
+ u8 ant_idx = fls(priv->nvm_data->valid_tx_ant) - 1;
+ if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ])
+ priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx;
+ if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ])
+ priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
+}
+
+int __must_check iwl_scan_initiate(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ enum iwl_scan_type scan_type,
+ enum ieee80211_band band)
+{
+ int ret;
+
+ lockdep_assert_held(&priv->mutex);
+
+ cancel_delayed_work(&priv->scan_check);
+
+ if (!iwl_is_ready_rf(priv)) {
+ IWL_WARN(priv, "Request scan called when driver not ready.\n");
+ return -EIO;
+ }
+
+ if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+ IWL_DEBUG_SCAN(priv,
+ "Multiple concurrent scan requests in parallel.\n");
+ return -EBUSY;
+ }
+
+ if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n");
+ return -EBUSY;
+ }
+
+ IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
+ scan_type == IWL_SCAN_NORMAL ? "" :
+ "internal short ");
+
+ set_bit(STATUS_SCANNING, &priv->status);
+ priv->scan_type = scan_type;
+ priv->scan_start = jiffies;
+ priv->scan_band = band;
+
+ ret = iwlagn_request_scan(priv, vif);
+ if (ret) {
+ clear_bit(STATUS_SCANNING, &priv->status);
+ priv->scan_type = IWL_SCAN_NORMAL;
+ return ret;
+ }
+
+ queue_delayed_work(priv->workqueue, &priv->scan_check,
+ IWL_SCAN_CHECK_WATCHDOG);
+
+ return 0;
+}
+
+
+/*
+ * internal short scan, this function should only been called while associated.
+ * It will reset and tune the radio to prevent possible RF related problem
+ */
+void iwl_internal_short_hw_scan(struct iwl_priv *priv)
+{
+ queue_work(priv->workqueue, &priv->start_internal_scan);
+}
+
+static void iwl_bg_start_internal_scan(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, start_internal_scan);
+
+ IWL_DEBUG_SCAN(priv, "Start internal scan\n");
+
+ mutex_lock(&priv->mutex);
+
+ if (priv->scan_type == IWL_SCAN_RADIO_RESET) {
+ IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
+ goto unlock;
+ }
+
+ if (test_bit(STATUS_SCANNING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
+ goto unlock;
+ }
+
+ if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band))
+ IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n");
+ unlock:
+ mutex_unlock(&priv->mutex);
+}
+
+static void iwl_bg_scan_check(struct work_struct *data)
+{
+ struct iwl_priv *priv =
+ container_of(data, struct iwl_priv, scan_check.work);
+
+ IWL_DEBUG_SCAN(priv, "Scan check work\n");
+
+ /* Since we are here firmware does not finish scan and
+ * most likely is in bad shape, so we don't bother to
+ * send abort command, just force scan complete to mac80211 */
+ mutex_lock(&priv->mutex);
+ iwl_force_scan_end(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void iwl_bg_abort_scan(struct work_struct *work)
+{
+ struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);
+
+ IWL_DEBUG_SCAN(priv, "Abort scan work\n");
+
+ /* We keep scan_check work queued in case when firmware will not
+ * report back scan completed notification */
+ mutex_lock(&priv->mutex);
+ iwl_scan_cancel_timeout(priv, 200);
+ mutex_unlock(&priv->mutex);
+}
+
+static void iwl_bg_scan_completed(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, scan_completed);
+
+ mutex_lock(&priv->mutex);
+ iwl_process_scan_complete(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
+{
+ INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed);
+ INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
+ INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan);
+ INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
+}
+
+void iwl_cancel_scan_deferred_work(struct iwl_priv *priv)
+{
+ cancel_work_sync(&priv->start_internal_scan);
+ cancel_work_sync(&priv->abort_scan);
+ cancel_work_sync(&priv->scan_completed);
+
+ if (cancel_delayed_work_sync(&priv->scan_check)) {
+ mutex_lock(&priv->mutex);
+ iwl_force_scan_end(priv);
+ mutex_unlock(&priv->mutex);
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c
new file mode 100644
index 000000000000..8e9768a553e4
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c
@@ -0,0 +1,1442 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "iwl-trans.h"
+#include "dev.h"
+#include "agn.h"
+
+const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
+{
+ lockdep_assert_held(&priv->sta_lock);
+
+ if (sta_id >= IWLAGN_STATION_COUNT) {
+ IWL_ERR(priv, "invalid sta_id %u\n", sta_id);
+ return -EINVAL;
+ }
+ if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
+ IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u "
+ "addr %pM\n",
+ sta_id, priv->stations[sta_id].sta.sta.addr);
+
+ if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) {
+ IWL_DEBUG_ASSOC(priv,
+ "STA id %u addr %pM already present in uCode "
+ "(according to driver)\n",
+ sta_id, priv->stations[sta_id].sta.sta.addr);
+ } else {
+ priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
+ IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n",
+ sta_id, priv->stations[sta_id].sta.sta.addr);
+ }
+ return 0;
+}
+
+static void iwl_process_add_sta_resp(struct iwl_priv *priv,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data;
+
+ IWL_DEBUG_INFO(priv, "Processing response for adding station\n");
+
+ spin_lock_bh(&priv->sta_lock);
+
+ switch (add_sta_resp->status) {
+ case ADD_STA_SUCCESS_MSK:
+ IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n");
+ break;
+ case ADD_STA_NO_ROOM_IN_TABLE:
+ IWL_ERR(priv, "Adding station failed, no room in table.\n");
+ break;
+ case ADD_STA_NO_BLOCK_ACK_RESOURCE:
+ IWL_ERR(priv,
+ "Adding station failed, no block ack resource.\n");
+ break;
+ case ADD_STA_MODIFY_NON_EXIST_STA:
+ IWL_ERR(priv, "Attempting to modify non-existing station\n");
+ break;
+ default:
+ IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n",
+ add_sta_resp->status);
+ break;
+ }
+
+ spin_unlock_bh(&priv->sta_lock);
+}
+
+void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+ iwl_process_add_sta_resp(priv, pkt);
+}
+
+int iwl_send_add_sta(struct iwl_priv *priv,
+ struct iwl_addsta_cmd *sta, u8 flags)
+{
+ int ret = 0;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_ADD_STA,
+ .flags = flags,
+ .data = { sta, },
+ .len = { sizeof(*sta), },
+ };
+ u8 sta_id __maybe_unused = sta->sta.sta_id;
+ struct iwl_rx_packet *pkt;
+ struct iwl_add_sta_resp *add_sta_resp;
+
+ IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n",
+ sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : "");
+
+ if (!(flags & CMD_ASYNC)) {
+ cmd.flags |= CMD_WANT_SKB;
+ might_sleep();
+ }
+
+ ret = iwl_dvm_send_cmd(priv, &cmd);
+
+ if (ret || (flags & CMD_ASYNC))
+ return ret;
+
+ pkt = cmd.resp_pkt;
+ add_sta_resp = (void *)pkt->data;
+
+ /* debug messages are printed in the handler */
+ if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) {
+ spin_lock_bh(&priv->sta_lock);
+ ret = iwl_sta_ucode_activate(priv, sta_id);
+ spin_unlock_bh(&priv->sta_lock);
+ } else {
+ ret = -EIO;
+ }
+
+ iwl_free_resp(&cmd);
+
+ return ret;
+}
+
+bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_sta *sta)
+{
+ if (!ctx->ht.enabled || !ctx->ht.is_40mhz)
+ return false;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (priv->disable_ht40)
+ return false;
+#endif
+
+ /* special case for RXON */
+ if (!sta)
+ return true;
+
+ return sta->bandwidth >= IEEE80211_STA_RX_BW_40;
+}
+
+static void iwl_sta_calc_ht_flags(struct iwl_priv *priv,
+ struct ieee80211_sta *sta,
+ struct iwl_rxon_context *ctx,
+ __le32 *flags, __le32 *mask)
+{
+ struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap;
+
+ *mask = STA_FLG_RTS_MIMO_PROT_MSK |
+ STA_FLG_MIMO_DIS_MSK |
+ STA_FLG_HT40_EN_MSK |
+ STA_FLG_MAX_AGG_SIZE_MSK |
+ STA_FLG_AGG_MPDU_DENSITY_MSK;
+ *flags = 0;
+
+ if (!sta || !sta_ht_inf->ht_supported)
+ return;
+
+ IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n",
+ sta->addr,
+ (sta->smps_mode == IEEE80211_SMPS_STATIC) ?
+ "static" :
+ (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ?
+ "dynamic" : "disabled");
+
+ switch (sta->smps_mode) {
+ case IEEE80211_SMPS_STATIC:
+ *flags |= STA_FLG_MIMO_DIS_MSK;
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ *flags |= STA_FLG_RTS_MIMO_PROT_MSK;
+ break;
+ case IEEE80211_SMPS_OFF:
+ break;
+ default:
+ IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->smps_mode);
+ break;
+ }
+
+ *flags |= cpu_to_le32(
+ (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
+
+ *flags |= cpu_to_le32(
+ (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
+
+ if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
+ *flags |= STA_FLG_HT40_EN_MSK;
+}
+
+int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct ieee80211_sta *sta)
+{
+ u8 sta_id = iwl_sta_id(sta);
+ __le32 flags, mask;
+ struct iwl_addsta_cmd cmd;
+
+ if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
+ return -EINVAL;
+
+ iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask);
+
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[sta_id].sta.station_flags &= ~mask;
+ priv->stations[sta_id].sta.station_flags |= flags;
+ spin_unlock_bh(&priv->sta_lock);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.mode = STA_CONTROL_MODIFY_MSK;
+ cmd.station_flags_msk = mask;
+ cmd.station_flags = flags;
+ cmd.sta.sta_id = sta_id;
+
+ return iwl_send_add_sta(priv, &cmd, 0);
+}
+
+static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
+ struct ieee80211_sta *sta,
+ struct iwl_rxon_context *ctx)
+{
+ __le32 flags, mask;
+
+ iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask);
+
+ lockdep_assert_held(&priv->sta_lock);
+ priv->stations[index].sta.station_flags &= ~mask;
+ priv->stations[index].sta.station_flags |= flags;
+}
+
+/**
+ * iwl_prep_station - Prepare station information for addition
+ *
+ * should be called with sta_lock held
+ */
+u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
+{
+ struct iwl_station_entry *station;
+ int i;
+ u8 sta_id = IWL_INVALID_STATION;
+
+ if (is_ap)
+ sta_id = ctx->ap_sta_id;
+ else if (is_broadcast_ether_addr(addr))
+ sta_id = ctx->bcast_sta_id;
+ else
+ for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) {
+ if (ether_addr_equal(priv->stations[i].sta.sta.addr,
+ addr)) {
+ sta_id = i;
+ break;
+ }
+
+ if (!priv->stations[i].used &&
+ sta_id == IWL_INVALID_STATION)
+ sta_id = i;
+ }
+
+ /*
+ * These two conditions have the same outcome, but keep them
+ * separate
+ */
+ if (unlikely(sta_id == IWL_INVALID_STATION))
+ return sta_id;
+
+ /*
+ * uCode is not able to deal with multiple requests to add a
+ * station. Keep track if one is in progress so that we do not send
+ * another.
+ */
+ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
+ IWL_DEBUG_INFO(priv, "STA %d already in process of being "
+ "added.\n", sta_id);
+ return sta_id;
+ }
+
+ if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
+ (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) &&
+ ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) {
+ IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
+ "adding again.\n", sta_id, addr);
+ return sta_id;
+ }
+
+ station = &priv->stations[sta_id];
+ station->used = IWL_STA_DRIVER_ACTIVE;
+ IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
+ sta_id, addr);
+ priv->num_stations++;
+
+ /* Set up the REPLY_ADD_STA command to send to device */
+ memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
+ memcpy(station->sta.sta.addr, addr, ETH_ALEN);
+ station->sta.mode = 0;
+ station->sta.sta.sta_id = sta_id;
+ station->sta.station_flags = ctx->station_flags;
+ station->ctxid = ctx->ctxid;
+
+ if (sta) {
+ struct iwl_station_priv *sta_priv;
+
+ sta_priv = (void *)sta->drv_priv;
+ sta_priv->ctx = ctx;
+ }
+
+ /*
+ * OK to call unconditionally, since local stations (IBSS BSSID
+ * STA and broadcast STA) pass in a NULL sta, and mac80211
+ * doesn't allow HT IBSS.
+ */
+ iwl_set_ht_add_station(priv, sta_id, sta, ctx);
+
+ return sta_id;
+
+}
+
+#define STA_WAIT_TIMEOUT (HZ/2)
+
+/**
+ * iwl_add_station_common -
+ */
+int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ const u8 *addr, bool is_ap,
+ struct ieee80211_sta *sta, u8 *sta_id_r)
+{
+ int ret = 0;
+ u8 sta_id;
+ struct iwl_addsta_cmd sta_cmd;
+
+ *sta_id_r = 0;
+ spin_lock_bh(&priv->sta_lock);
+ sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Unable to prepare station %pM for addition\n",
+ addr);
+ spin_unlock_bh(&priv->sta_lock);
+ return -EINVAL;
+ }
+
+ /*
+ * uCode is not able to deal with multiple requests to add a
+ * station. Keep track if one is in progress so that we do not send
+ * another.
+ */
+ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
+ IWL_DEBUG_INFO(priv, "STA %d already in process of being "
+ "added.\n", sta_id);
+ spin_unlock_bh(&priv->sta_lock);
+ return -EEXIST;
+ }
+
+ if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
+ (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
+ IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
+ "adding again.\n", sta_id, addr);
+ spin_unlock_bh(&priv->sta_lock);
+ return -EEXIST;
+ }
+
+ priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS;
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta,
+ sizeof(struct iwl_addsta_cmd));
+ spin_unlock_bh(&priv->sta_lock);
+
+ /* Add station to device's station table */
+ ret = iwl_send_add_sta(priv, &sta_cmd, 0);
+ if (ret) {
+ spin_lock_bh(&priv->sta_lock);
+ IWL_ERR(priv, "Adding station %pM failed.\n",
+ priv->stations[sta_id].sta.sta.addr);
+ priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
+ priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+ spin_unlock_bh(&priv->sta_lock);
+ }
+ *sta_id_r = sta_id;
+ return ret;
+}
+
+/**
+ * iwl_sta_ucode_deactivate - deactivate ucode status for a station
+ */
+static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id)
+{
+ lockdep_assert_held(&priv->sta_lock);
+
+ /* Ucode must be active and driver must be non active */
+ if ((priv->stations[sta_id].used &
+ (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) !=
+ IWL_STA_UCODE_ACTIVE)
+ IWL_ERR(priv, "removed non active STA %u\n", sta_id);
+
+ priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
+
+ memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
+ IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id);
+}
+
+static int iwl_send_remove_station(struct iwl_priv *priv,
+ const u8 *addr, int sta_id,
+ bool temporary)
+{
+ struct iwl_rx_packet *pkt;
+ int ret;
+ struct iwl_rem_sta_cmd rm_sta_cmd;
+ struct iwl_rem_sta_resp *rem_sta_resp;
+
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_REMOVE_STA,
+ .len = { sizeof(struct iwl_rem_sta_cmd), },
+ .data = { &rm_sta_cmd, },
+ };
+
+ memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
+ rm_sta_cmd.num_sta = 1;
+ memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
+
+ cmd.flags |= CMD_WANT_SKB;
+
+ ret = iwl_dvm_send_cmd(priv, &cmd);
+
+ if (ret)
+ return ret;
+
+ pkt = cmd.resp_pkt;
+ rem_sta_resp = (void *)pkt->data;
+
+ switch (rem_sta_resp->status) {
+ case REM_STA_SUCCESS_MSK:
+ if (!temporary) {
+ spin_lock_bh(&priv->sta_lock);
+ iwl_sta_ucode_deactivate(priv, sta_id);
+ spin_unlock_bh(&priv->sta_lock);
+ }
+ IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(priv, "REPLY_REMOVE_STA failed\n");
+ break;
+ }
+
+ iwl_free_resp(&cmd);
+
+ return ret;
+}
+
+/**
+ * iwl_remove_station - Remove driver's knowledge of station.
+ */
+int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
+ const u8 *addr)
+{
+ u8 tid;
+
+ if (!iwl_is_ready(priv)) {
+ IWL_DEBUG_INFO(priv,
+ "Unable to remove station %pM, device not ready.\n",
+ addr);
+ /*
+ * It is typical for stations to be removed when we are
+ * going down. Return success since device will be down
+ * soon anyway
+ */
+ return 0;
+ }
+
+ IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n",
+ sta_id, addr);
+
+ if (WARN_ON(sta_id == IWL_INVALID_STATION))
+ return -EINVAL;
+
+ spin_lock_bh(&priv->sta_lock);
+
+ if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
+ IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
+ addr);
+ goto out_err;
+ }
+
+ if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
+ IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
+ addr);
+ goto out_err;
+ }
+
+ if (priv->stations[sta_id].used & IWL_STA_LOCAL) {
+ kfree(priv->stations[sta_id].lq);
+ priv->stations[sta_id].lq = NULL;
+ }
+
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
+ memset(&priv->tid_data[sta_id][tid], 0,
+ sizeof(priv->tid_data[sta_id][tid]));
+
+ priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
+
+ priv->num_stations--;
+
+ if (WARN_ON(priv->num_stations < 0))
+ priv->num_stations = 0;
+
+ spin_unlock_bh(&priv->sta_lock);
+
+ return iwl_send_remove_station(priv, addr, sta_id, false);
+out_err:
+ spin_unlock_bh(&priv->sta_lock);
+ return -EINVAL;
+}
+
+void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id,
+ const u8 *addr)
+{
+ u8 tid;
+
+ if (!iwl_is_ready(priv)) {
+ IWL_DEBUG_INFO(priv,
+ "Unable to remove station %pM, device not ready.\n",
+ addr);
+ return;
+ }
+
+ IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id);
+
+ if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
+ return;
+
+ spin_lock_bh(&priv->sta_lock);
+
+ WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE));
+
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
+ memset(&priv->tid_data[sta_id][tid], 0,
+ sizeof(priv->tid_data[sta_id][tid]));
+
+ priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
+ priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+
+ priv->num_stations--;
+
+ if (WARN_ON_ONCE(priv->num_stations < 0))
+ priv->num_stations = 0;
+
+ spin_unlock_bh(&priv->sta_lock);
+}
+
+static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ u8 sta_id, struct iwl_link_quality_cmd *link_cmd)
+{
+ int i, r;
+ u32 rate_flags = 0;
+ __le32 rate_n_flags;
+
+ lockdep_assert_held(&priv->mutex);
+
+ memset(link_cmd, 0, sizeof(*link_cmd));
+
+ /* Set up the rate scaling to start at selected rate, fall back
+ * all the way down to 1M in IEEE order, and then spin on 1M */
+ if (priv->band == IEEE80211_BAND_5GHZ)
+ r = IWL_RATE_6M_INDEX;
+ else if (ctx && ctx->vif && ctx->vif->p2p)
+ r = IWL_RATE_6M_INDEX;
+ else
+ r = IWL_RATE_1M_INDEX;
+
+ if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
+ rate_flags |= RATE_MCS_CCK_MSK;
+
+ rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) <<
+ RATE_MCS_ANT_POS;
+ rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
+ link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
+
+ link_cmd->general_params.single_stream_ant_msk =
+ first_antenna(priv->nvm_data->valid_tx_ant);
+
+ link_cmd->general_params.dual_stream_ant_msk =
+ priv->nvm_data->valid_tx_ant &
+ ~first_antenna(priv->nvm_data->valid_tx_ant);
+ if (!link_cmd->general_params.dual_stream_ant_msk) {
+ link_cmd->general_params.dual_stream_ant_msk = ANT_AB;
+ } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) {
+ link_cmd->general_params.dual_stream_ant_msk =
+ priv->nvm_data->valid_tx_ant;
+ }
+
+ link_cmd->agg_params.agg_dis_start_th =
+ LINK_QUAL_AGG_DISABLE_START_DEF;
+ link_cmd->agg_params.agg_time_limit =
+ cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+
+ link_cmd->sta_id = sta_id;
+}
+
+/**
+ * iwl_clear_ucode_stations - clear ucode station table bits
+ *
+ * This function clears all the bits in the driver indicating
+ * which stations are active in the ucode. Call when something
+ * other than explicit station management would cause this in
+ * the ucode, e.g. unassociated RXON.
+ */
+void iwl_clear_ucode_stations(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ int i;
+ bool cleared = false;
+
+ IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n");
+
+ spin_lock_bh(&priv->sta_lock);
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if (ctx && ctx->ctxid != priv->stations[i].ctxid)
+ continue;
+
+ if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) {
+ IWL_DEBUG_INFO(priv,
+ "Clearing ucode active for station %d\n", i);
+ priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
+ cleared = true;
+ }
+ }
+ spin_unlock_bh(&priv->sta_lock);
+
+ if (!cleared)
+ IWL_DEBUG_INFO(priv,
+ "No active stations found to be cleared\n");
+}
+
+/**
+ * iwl_restore_stations() - Restore driver known stations to device
+ *
+ * All stations considered active by driver, but not present in ucode, is
+ * restored.
+ *
+ * Function sleeps.
+ */
+void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
+{
+ struct iwl_addsta_cmd sta_cmd;
+ static const struct iwl_link_quality_cmd zero_lq = {};
+ struct iwl_link_quality_cmd lq;
+ int i;
+ bool found = false;
+ int ret;
+ bool send_lq;
+
+ if (!iwl_is_ready(priv)) {
+ IWL_DEBUG_INFO(priv,
+ "Not ready yet, not restoring any stations.\n");
+ return;
+ }
+
+ IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
+ spin_lock_bh(&priv->sta_lock);
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if (ctx->ctxid != priv->stations[i].ctxid)
+ continue;
+ if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
+ !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
+ IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
+ priv->stations[i].sta.sta.addr);
+ priv->stations[i].sta.mode = 0;
+ priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
+ found = true;
+ }
+ }
+
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
+ memcpy(&sta_cmd, &priv->stations[i].sta,
+ sizeof(struct iwl_addsta_cmd));
+ send_lq = false;
+ if (priv->stations[i].lq) {
+ if (priv->wowlan)
+ iwl_sta_fill_lq(priv, ctx, i, &lq);
+ else
+ memcpy(&lq, priv->stations[i].lq,
+ sizeof(struct iwl_link_quality_cmd));
+
+ if (memcmp(&lq, &zero_lq, sizeof(lq)))
+ send_lq = true;
+ }
+ spin_unlock_bh(&priv->sta_lock);
+ ret = iwl_send_add_sta(priv, &sta_cmd, 0);
+ if (ret) {
+ spin_lock_bh(&priv->sta_lock);
+ IWL_ERR(priv, "Adding station %pM failed.\n",
+ priv->stations[i].sta.sta.addr);
+ priv->stations[i].used &=
+ ~IWL_STA_DRIVER_ACTIVE;
+ priv->stations[i].used &=
+ ~IWL_STA_UCODE_INPROGRESS;
+ continue;
+ }
+ /*
+ * Rate scaling has already been initialized, send
+ * current LQ command
+ */
+ if (send_lq)
+ iwl_send_lq_cmd(priv, ctx, &lq, 0, true);
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
+ }
+ }
+
+ spin_unlock_bh(&priv->sta_lock);
+ if (!found)
+ IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
+ "no stations to be restored.\n");
+ else
+ IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
+ "complete.\n");
+}
+
+int iwl_get_free_ucode_key_offset(struct iwl_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < priv->sta_key_max_num; i++)
+ if (!test_and_set_bit(i, &priv->ucode_key_table))
+ return i;
+
+ return WEP_INVALID_OFFSET;
+}
+
+void iwl_dealloc_bcast_stations(struct iwl_priv *priv)
+{
+ int i;
+
+ spin_lock_bh(&priv->sta_lock);
+ for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
+ if (!(priv->stations[i].used & IWL_STA_BCAST))
+ continue;
+
+ priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
+ priv->num_stations--;
+ if (WARN_ON(priv->num_stations < 0))
+ priv->num_stations = 0;
+ kfree(priv->stations[i].lq);
+ priv->stations[i].lq = NULL;
+ }
+ spin_unlock_bh(&priv->sta_lock);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+static void iwl_dump_lq_cmd(struct iwl_priv *priv,
+ struct iwl_link_quality_cmd *lq)
+{
+ int i;
+ IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id);
+ IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n",
+ lq->general_params.single_stream_ant_msk,
+ lq->general_params.dual_stream_ant_msk);
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
+ IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n",
+ i, lq->rs_table[i].rate_n_flags);
+}
+#else
+static inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
+ struct iwl_link_quality_cmd *lq)
+{
+}
+#endif
+
+/**
+ * is_lq_table_valid() - Test one aspect of LQ cmd for validity
+ *
+ * It sometimes happens when a HT rate has been in use and we
+ * loose connectivity with AP then mac80211 will first tell us that the
+ * current channel is not HT anymore before removing the station. In such a
+ * scenario the RXON flags will be updated to indicate we are not
+ * communicating HT anymore, but the LQ command may still contain HT rates.
+ * Test for this to prevent driver from sending LQ command between the time
+ * RXON flags are updated and when LQ command is updated.
+ */
+static bool is_lq_table_valid(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct iwl_link_quality_cmd *lq)
+{
+ int i;
+
+ if (ctx->ht.enabled)
+ return true;
+
+ IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n",
+ ctx->active.channel);
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ if (le32_to_cpu(lq->rs_table[i].rate_n_flags) &
+ RATE_MCS_HT_MSK) {
+ IWL_DEBUG_INFO(priv,
+ "index %d of LQ expects HT channel\n",
+ i);
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * iwl_send_lq_cmd() - Send link quality command
+ * @init: This command is sent as part of station initialization right
+ * after station has been added.
+ *
+ * The link quality command is sent as the last step of station creation.
+ * This is the special case in which init is set and we call a callback in
+ * this case to clear the state indicating that station creation is in
+ * progress.
+ */
+int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ struct iwl_link_quality_cmd *lq, u8 flags, bool init)
+{
+ int ret = 0;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_TX_LINK_QUALITY_CMD,
+ .len = { sizeof(struct iwl_link_quality_cmd), },
+ .flags = flags,
+ .data = { lq, },
+ };
+
+ if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
+ return -EINVAL;
+
+
+ spin_lock_bh(&priv->sta_lock);
+ if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
+ spin_unlock_bh(&priv->sta_lock);
+ return -EINVAL;
+ }
+ spin_unlock_bh(&priv->sta_lock);
+
+ iwl_dump_lq_cmd(priv, lq);
+ if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
+ return -EINVAL;
+
+ if (is_lq_table_valid(priv, ctx, lq))
+ ret = iwl_dvm_send_cmd(priv, &cmd);
+ else
+ ret = -EINVAL;
+
+ if (cmd.flags & CMD_ASYNC)
+ return ret;
+
+ if (init) {
+ IWL_DEBUG_INFO(priv, "init LQ command complete, "
+ "clearing sta addition status for sta %d\n",
+ lq->sta_id);
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+ spin_unlock_bh(&priv->sta_lock);
+ }
+ return ret;
+}
+
+
+static struct iwl_link_quality_cmd *
+iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
+ u8 sta_id)
+{
+ struct iwl_link_quality_cmd *link_cmd;
+
+ link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL);
+ if (!link_cmd) {
+ IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n");
+ return NULL;
+ }
+
+ iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd);
+
+ return link_cmd;
+}
+
+/*
+ * iwlagn_add_bssid_station - Add the special IBSS BSSID station
+ *
+ * Function sleeps.
+ */
+int iwlagn_add_bssid_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ const u8 *addr, u8 *sta_id_r)
+{
+ int ret;
+ u8 sta_id;
+ struct iwl_link_quality_cmd *link_cmd;
+
+ if (sta_id_r)
+ *sta_id_r = IWL_INVALID_STATION;
+
+ ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id);
+ if (ret) {
+ IWL_ERR(priv, "Unable to add station %pM\n", addr);
+ return ret;
+ }
+
+ if (sta_id_r)
+ *sta_id_r = sta_id;
+
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[sta_id].used |= IWL_STA_LOCAL;
+ spin_unlock_bh(&priv->sta_lock);
+
+ /* Set up default rate scaling table in device's station table */
+ link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
+ if (!link_cmd) {
+ IWL_ERR(priv,
+ "Unable to initialize rate scaling for station %pM.\n",
+ addr);
+ return -ENOMEM;
+ }
+
+ ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true);
+ if (ret)
+ IWL_ERR(priv, "Link quality command failed (%d)\n", ret);
+
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[sta_id].lq = link_cmd;
+ spin_unlock_bh(&priv->sta_lock);
+
+ return 0;
+}
+
+/*
+ * static WEP keys
+ *
+ * For each context, the device has a table of 4 static WEP keys
+ * (one for each key index) that is updated with the following
+ * commands.
+ */
+
+static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ bool send_if_empty)
+{
+ int i, not_empty = 0;
+ u8 buff[sizeof(struct iwl_wep_cmd) +
+ sizeof(struct iwl_wep_key) * WEP_KEYS_MAX];
+ struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff;
+ size_t cmd_size = sizeof(struct iwl_wep_cmd);
+ struct iwl_host_cmd cmd = {
+ .id = ctx->wep_key_cmd,
+ .data = { wep_cmd, },
+ };
+
+ might_sleep();
+
+ memset(wep_cmd, 0, cmd_size +
+ (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX));
+
+ for (i = 0; i < WEP_KEYS_MAX ; i++) {
+ wep_cmd->key[i].key_index = i;
+ if (ctx->wep_keys[i].key_size) {
+ wep_cmd->key[i].key_offset = i;
+ not_empty = 1;
+ } else {
+ wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET;
+ }
+
+ wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size;
+ memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key,
+ ctx->wep_keys[i].key_size);
+ }
+
+ wep_cmd->global_key_type = WEP_KEY_WEP_TYPE;
+ wep_cmd->num_keys = WEP_KEYS_MAX;
+
+ cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX;
+
+ cmd.len[0] = cmd_size;
+
+ if (not_empty || send_if_empty)
+ return iwl_dvm_send_cmd(priv, &cmd);
+ else
+ return 0;
+}
+
+int iwl_restore_default_wep_keys(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ lockdep_assert_held(&priv->mutex);
+
+ return iwl_send_static_wepkey_cmd(priv, ctx, false);
+}
+
+int iwl_remove_default_wep_key(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *keyconf)
+{
+ int ret;
+
+ lockdep_assert_held(&priv->mutex);
+
+ IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n",
+ keyconf->keyidx);
+
+ memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0]));
+ if (iwl_is_rfkill(priv)) {
+ IWL_DEBUG_WEP(priv,
+ "Not sending REPLY_WEPKEY command due to RFKILL.\n");
+ /* but keys in device are clear anyway so return success */
+ return 0;
+ }
+ ret = iwl_send_static_wepkey_cmd(priv, ctx, 1);
+ IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n",
+ keyconf->keyidx, ret);
+
+ return ret;
+}
+
+int iwl_set_default_wep_key(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *keyconf)
+{
+ int ret;
+
+ lockdep_assert_held(&priv->mutex);
+
+ if (keyconf->keylen != WEP_KEY_LEN_128 &&
+ keyconf->keylen != WEP_KEY_LEN_64) {
+ IWL_DEBUG_WEP(priv,
+ "Bad WEP key length %d\n", keyconf->keylen);
+ return -EINVAL;
+ }
+
+ keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT;
+
+ ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen;
+ memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key,
+ keyconf->keylen);
+
+ ret = iwl_send_static_wepkey_cmd(priv, ctx, false);
+ IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n",
+ keyconf->keylen, keyconf->keyidx, ret);
+
+ return ret;
+}
+
+/*
+ * dynamic (per-station) keys
+ *
+ * The dynamic keys are a little more complicated. The device has
+ * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys.
+ * These are linked to stations by a table that contains an index
+ * into the key table for each station/key index/{mcast,unicast},
+ * i.e. it's basically an array of pointers like this:
+ * key_offset_t key_mapping[NUM_STATIONS][4][2];
+ * (it really works differently, but you can think of it as such)
+ *
+ * The key uploading and linking happens in the same command, the
+ * add station command with STA_MODIFY_KEY_MASK.
+ */
+
+static u8 iwlagn_key_sta_id(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+
+ if (sta)
+ return iwl_sta_id(sta);
+
+ /*
+ * The device expects GTKs for station interfaces to be
+ * installed as GTKs for the AP station. If we have no
+ * station ID, then use the ap_sta_id in that case.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx)
+ return vif_priv->ctx->ap_sta_id;
+
+ return IWL_INVALID_STATION;
+}
+
+static int iwlagn_send_sta_key(struct iwl_priv *priv,
+ struct ieee80211_key_conf *keyconf,
+ u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
+ u32 cmd_flags)
+{
+ __le16 key_flags;
+ struct iwl_addsta_cmd sta_cmd;
+ int i;
+
+ spin_lock_bh(&priv->sta_lock);
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
+ spin_unlock_bh(&priv->sta_lock);
+
+ key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags |= STA_KEY_FLG_MAP_KEY_MSK;
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ key_flags |= STA_KEY_FLG_CCMP;
+ memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ key_flags |= STA_KEY_FLG_TKIP;
+ sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
+ for (i = 0; i < 5; i++)
+ sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
+ memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen);
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
+ /* fall through */
+ case WLAN_CIPHER_SUITE_WEP40:
+ key_flags |= STA_KEY_FLG_WEP;
+ memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen);
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ key_flags |= STA_KEY_MULTICAST_MSK;
+
+ /* key pointer (offset) */
+ sta_cmd.key.key_offset = keyconf->hw_key_idx;
+
+ sta_cmd.key.key_flags = key_flags;
+ sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
+ sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
+
+ return iwl_send_add_sta(priv, &sta_cmd, cmd_flags);
+}
+
+void iwl_update_tkip_key(struct iwl_priv *priv,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta, u32 iv32, u16 *phase1key)
+{
+ u8 sta_id = iwlagn_key_sta_id(priv, vif, sta);
+
+ if (sta_id == IWL_INVALID_STATION)
+ return;
+
+ if (iwl_scan_cancel(priv)) {
+ /* cancel scan failed, just live w/ bad key and rely
+ briefly on SW decryption */
+ return;
+ }
+
+ iwlagn_send_sta_key(priv, keyconf, sta_id,
+ iv32, phase1key, CMD_ASYNC);
+}
+
+int iwl_remove_dynamic_key(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_addsta_cmd sta_cmd;
+ u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
+ __le16 key_flags;
+
+ /* if station isn't there, neither is the key */
+ if (sta_id == IWL_INVALID_STATION)
+ return -ENOENT;
+
+ spin_lock_bh(&priv->sta_lock);
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
+ if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE))
+ sta_id = IWL_INVALID_STATION;
+ spin_unlock_bh(&priv->sta_lock);
+
+ if (sta_id == IWL_INVALID_STATION)
+ return 0;
+
+ lockdep_assert_held(&priv->mutex);
+
+ ctx->key_mapping_keys--;
+
+ IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n",
+ keyconf->keyidx, sta_id);
+
+ if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table))
+ IWL_ERR(priv, "offset %d not used in uCode key table.\n",
+ keyconf->hw_key_idx);
+
+ key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC |
+ STA_KEY_FLG_INVALID;
+
+ if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ key_flags |= STA_KEY_MULTICAST_MSK;
+
+ sta_cmd.key.key_flags = key_flags;
+ sta_cmd.key.key_offset = keyconf->hw_key_idx;
+ sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
+ sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
+
+ return iwl_send_add_sta(priv, &sta_cmd, 0);
+}
+
+int iwl_set_dynamic_key(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta)
+{
+ struct ieee80211_key_seq seq;
+ u16 p1k[5];
+ int ret;
+ u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
+ const u8 *addr;
+
+ if (sta_id == IWL_INVALID_STATION)
+ return -EINVAL;
+
+ lockdep_assert_held(&priv->mutex);
+
+ keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv);
+ if (keyconf->hw_key_idx == WEP_INVALID_OFFSET)
+ return -ENOSPC;
+
+ ctx->key_mapping_keys++;
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (sta)
+ addr = sta->addr;
+ else /* station mode case only */
+ addr = ctx->active.bssid_addr;
+
+ /* pre-fill phase 1 key into device cache */
+ ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+ ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+ ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
+ seq.tkip.iv32, p1k, 0);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
+ 0, NULL, 0);
+ break;
+ default:
+ IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher);
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ ctx->key_mapping_keys--;
+ clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table);
+ }
+
+ IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
+ keyconf->cipher, keyconf->keylen, keyconf->keyidx,
+ sta ? sta->addr : NULL, ret);
+
+ return ret;
+}
+
+/**
+ * iwlagn_alloc_bcast_station - add broadcast station into driver's station table.
+ *
+ * This adds the broadcast station into the driver's station table
+ * and marks it driver active, so that it will be restored to the
+ * device at the next best time.
+ */
+int iwlagn_alloc_bcast_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ struct iwl_link_quality_cmd *link_cmd;
+ u8 sta_id;
+
+ spin_lock_bh(&priv->sta_lock);
+ sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Unable to prepare broadcast station\n");
+ spin_unlock_bh(&priv->sta_lock);
+
+ return -EINVAL;
+ }
+
+ priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE;
+ priv->stations[sta_id].used |= IWL_STA_BCAST;
+ spin_unlock_bh(&priv->sta_lock);
+
+ link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
+ if (!link_cmd) {
+ IWL_ERR(priv,
+ "Unable to initialize rate scaling for bcast station.\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[sta_id].lq = link_cmd;
+ spin_unlock_bh(&priv->sta_lock);
+
+ return 0;
+}
+
+/**
+ * iwl_update_bcast_station - update broadcast station's LQ command
+ *
+ * Only used by iwlagn. Placed here to have all bcast station management
+ * code together.
+ */
+int iwl_update_bcast_station(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ struct iwl_link_quality_cmd *link_cmd;
+ u8 sta_id = ctx->bcast_sta_id;
+
+ link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
+ if (!link_cmd) {
+ IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_bh(&priv->sta_lock);
+ if (priv->stations[sta_id].lq)
+ kfree(priv->stations[sta_id].lq);
+ else
+ IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n");
+ priv->stations[sta_id].lq = link_cmd;
+ spin_unlock_bh(&priv->sta_lock);
+
+ return 0;
+}
+
+int iwl_update_bcast_stations(struct iwl_priv *priv)
+{
+ struct iwl_rxon_context *ctx;
+ int ret = 0;
+
+ for_each_context(priv, ctx) {
+ ret = iwl_update_bcast_station(priv, ctx);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
+ */
+int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
+{
+ struct iwl_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&priv->mutex);
+
+ /* Remove "disable" flag, to enable Tx for this TID */
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
+ priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
+ spin_unlock_bh(&priv->sta_lock);
+
+ return iwl_send_add_sta(priv, &sta_cmd, 0);
+}
+
+int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
+ int tid, u16 ssn)
+{
+ int sta_id;
+ struct iwl_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&priv->mutex);
+
+ sta_id = iwl_sta_id(sta);
+ if (sta_id == IWL_INVALID_STATION)
+ return -ENXIO;
+
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[sta_id].sta.station_flags_msk = 0;
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
+ priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
+ priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
+ spin_unlock_bh(&priv->sta_lock);
+
+ return iwl_send_add_sta(priv, &sta_cmd, 0);
+}
+
+int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
+ int tid)
+{
+ int sta_id;
+ struct iwl_addsta_cmd sta_cmd;
+
+ lockdep_assert_held(&priv->mutex);
+
+ sta_id = iwl_sta_id(sta);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
+ return -ENXIO;
+ }
+
+ spin_lock_bh(&priv->sta_lock);
+ priv->stations[sta_id].sta.station_flags_msk = 0;
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+ priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
+ spin_unlock_bh(&priv->sta_lock);
+
+ return iwl_send_add_sta(priv, &sta_cmd, 0);
+}
+
+
+
+void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
+{
+ struct iwl_addsta_cmd cmd = {
+ .mode = STA_CONTROL_MODIFY_MSK,
+ .station_flags = STA_FLG_PWR_SAVE_MSK,
+ .station_flags_msk = STA_FLG_PWR_SAVE_MSK,
+ .sta.sta_id = sta_id,
+ .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK,
+ .sleep_tx_count = cpu_to_le16(cnt),
+ };
+
+ iwl_send_add_sta(priv, &cmd, CMD_ASYNC);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c
new file mode 100644
index 000000000000..5b73492e7ff7
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c
@@ -0,0 +1,685 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <net/mac80211.h>
+#include "iwl-io.h"
+#include "iwl-modparams.h"
+#include "iwl-debug.h"
+#include "agn.h"
+#include "dev.h"
+#include "commands.h"
+#include "tt.h"
+
+/* default Thermal Throttling transaction table
+ * Current state | Throttling Down | Throttling Up
+ *=============================================================================
+ * Condition Nxt State Condition Nxt State Condition Nxt State
+ *-----------------------------------------------------------------------------
+ * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
+ * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
+ * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
+ * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
+ *=============================================================================
+ */
+static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = {
+ {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104},
+ {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1},
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
+};
+static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = {
+ {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95},
+ {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1},
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
+};
+static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = {
+ {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100},
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX},
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
+};
+static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = {
+ {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD},
+ {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX},
+ {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}
+};
+
+/* Advance Thermal Throttling default restriction table */
+static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = {
+ {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true },
+ {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true },
+ {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false },
+ {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false }
+};
+
+bool iwl_tt_is_low_power_state(struct iwl_priv *priv)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+
+ if (tt->state >= IWL_TI_1)
+ return true;
+ return false;
+}
+
+u8 iwl_tt_current_power_mode(struct iwl_priv *priv)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+
+ return tt->tt_power_mode;
+}
+
+bool iwl_ht_enabled(struct iwl_priv *priv)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ struct iwl_tt_restriction *restriction;
+
+ if (!priv->thermal_throttle.advanced_tt)
+ return true;
+ restriction = tt->restriction + tt->state;
+ return restriction->is_ht;
+}
+
+static bool iwl_within_ct_kill_margin(struct iwl_priv *priv)
+{
+ s32 temp = priv->temperature; /* degrees CELSIUS except specified */
+ bool within_margin = false;
+
+ if (!priv->thermal_throttle.advanced_tt)
+ within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
+ CT_KILL_THRESHOLD_LEGACY) ? true : false;
+ else
+ within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
+ CT_KILL_THRESHOLD) ? true : false;
+ return within_margin;
+}
+
+bool iwl_check_for_ct_kill(struct iwl_priv *priv)
+{
+ bool is_ct_kill = false;
+
+ if (iwl_within_ct_kill_margin(priv)) {
+ iwl_tt_enter_ct_kill(priv);
+ is_ct_kill = true;
+ }
+ return is_ct_kill;
+}
+
+enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ struct iwl_tt_restriction *restriction;
+
+ if (!priv->thermal_throttle.advanced_tt)
+ return IWL_ANT_OK_MULTI;
+ restriction = tt->restriction + tt->state;
+ return restriction->tx_stream;
+}
+
+enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ struct iwl_tt_restriction *restriction;
+
+ if (!priv->thermal_throttle.advanced_tt)
+ return IWL_ANT_OK_MULTI;
+ restriction = tt->restriction + tt->state;
+ return restriction->rx_stream;
+}
+
+#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
+#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */
+
+/*
+ * toggle the bit to wake up uCode and check the temperature
+ * if the temperature is below CT, uCode will stay awake and send card
+ * state notification with CT_KILL bit clear to inform Thermal Throttling
+ * Management to change state. Otherwise, uCode will go back to sleep
+ * without doing anything, driver should continue the 5 seconds timer
+ * to wake up uCode for temperature check until temperature drop below CT
+ */
+static void iwl_tt_check_exit_ct_kill(unsigned long data)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)data;
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ unsigned long flags;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ if (tt->state == IWL_TI_CT_KILL) {
+ if (priv->thermal_throttle.ct_kill_toggle) {
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+ priv->thermal_throttle.ct_kill_toggle = false;
+ } else {
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
+ CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+ priv->thermal_throttle.ct_kill_toggle = true;
+ }
+ iwl_read32(priv->trans, CSR_UCODE_DRV_GP1);
+ if (iwl_trans_grab_nic_access(priv->trans, &flags))
+ iwl_trans_release_nic_access(priv->trans, &flags);
+
+ /* Reschedule the ct_kill timer to occur in
+ * CT_KILL_EXIT_DURATION seconds to ensure we get a
+ * thermal update */
+ IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n");
+ mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
+ jiffies + CT_KILL_EXIT_DURATION * HZ);
+ }
+}
+
+static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
+ bool stop)
+{
+ if (stop) {
+ IWL_DEBUG_TEMP(priv, "Stop all queues\n");
+ if (priv->mac80211_registered)
+ ieee80211_stop_queues(priv->hw);
+ IWL_DEBUG_TEMP(priv,
+ "Schedule 5 seconds CT_KILL Timer\n");
+ mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
+ jiffies + CT_KILL_EXIT_DURATION * HZ);
+ } else {
+ IWL_DEBUG_TEMP(priv, "Wake all queues\n");
+ if (priv->mac80211_registered)
+ ieee80211_wake_queues(priv->hw);
+ }
+}
+
+static void iwl_tt_ready_for_ct_kill(unsigned long data)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)data;
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ /* temperature timer expired, ready to go into CT_KILL state */
+ if (tt->state != IWL_TI_CT_KILL) {
+ IWL_DEBUG_TEMP(priv, "entering CT_KILL state when "
+ "temperature timer expired\n");
+ tt->state = IWL_TI_CT_KILL;
+ set_bit(STATUS_CT_KILL, &priv->status);
+ iwl_perform_ct_kill_task(priv, true);
+ }
+}
+
+static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
+{
+ IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n");
+ /* make request to retrieve statistics information */
+ iwl_send_statistics_request(priv, 0, false);
+ /* Reschedule the ct_kill wait timer */
+ mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
+ jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION));
+}
+
+#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY)
+#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100)
+#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90)
+
+/*
+ * Legacy thermal throttling
+ * 1) Avoid NIC destruction due to high temperatures
+ * Chip will identify dangerously high temperatures that can
+ * harm the device and will power down
+ * 2) Avoid the NIC power down due to high temperature
+ * Throttle early enough to lower the power consumption before
+ * drastic steps are needed
+ */
+static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ enum iwl_tt_state old_state;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if ((tt->tt_previous_temp) &&
+ (temp > tt->tt_previous_temp) &&
+ ((temp - tt->tt_previous_temp) >
+ IWL_TT_INCREASE_MARGIN)) {
+ IWL_DEBUG_TEMP(priv,
+ "Temperature increase %d degree Celsius\n",
+ (temp - tt->tt_previous_temp));
+ }
+#endif
+ old_state = tt->state;
+ /* in Celsius */
+ if (temp >= IWL_MINIMAL_POWER_THRESHOLD)
+ tt->state = IWL_TI_CT_KILL;
+ else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2)
+ tt->state = IWL_TI_2;
+ else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1)
+ tt->state = IWL_TI_1;
+ else
+ tt->state = IWL_TI_0;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ tt->tt_previous_temp = temp;
+#endif
+ /* stop ct_kill_waiting_tm timer */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
+ if (tt->state != old_state) {
+ switch (tt->state) {
+ case IWL_TI_0:
+ /*
+ * When the system is ready to go back to IWL_TI_0
+ * we only have to call iwl_power_update_mode() to
+ * do so.
+ */
+ break;
+ case IWL_TI_1:
+ tt->tt_power_mode = IWL_POWER_INDEX_3;
+ break;
+ case IWL_TI_2:
+ tt->tt_power_mode = IWL_POWER_INDEX_4;
+ break;
+ default:
+ tt->tt_power_mode = IWL_POWER_INDEX_5;
+ break;
+ }
+ mutex_lock(&priv->mutex);
+ if (old_state == IWL_TI_CT_KILL)
+ clear_bit(STATUS_CT_KILL, &priv->status);
+ if (tt->state != IWL_TI_CT_KILL &&
+ iwl_power_update_mode(priv, true)) {
+ /* TT state not updated
+ * try again during next temperature read
+ */
+ if (old_state == IWL_TI_CT_KILL)
+ set_bit(STATUS_CT_KILL, &priv->status);
+ tt->state = old_state;
+ IWL_ERR(priv, "Cannot update power mode, "
+ "TT state not updated\n");
+ } else {
+ if (tt->state == IWL_TI_CT_KILL) {
+ if (force) {
+ set_bit(STATUS_CT_KILL, &priv->status);
+ iwl_perform_ct_kill_task(priv, true);
+ } else {
+ iwl_prepare_ct_kill_task(priv);
+ tt->state = old_state;
+ }
+ } else if (old_state == IWL_TI_CT_KILL &&
+ tt->state != IWL_TI_CT_KILL)
+ iwl_perform_ct_kill_task(priv, false);
+ IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n",
+ tt->state);
+ IWL_DEBUG_TEMP(priv, "Power Index change to %u\n",
+ tt->tt_power_mode);
+ }
+ mutex_unlock(&priv->mutex);
+ }
+}
+
+/*
+ * Advance thermal throttling
+ * 1) Avoid NIC destruction due to high temperatures
+ * Chip will identify dangerously high temperatures that can
+ * harm the device and will power down
+ * 2) Avoid the NIC power down due to high temperature
+ * Throttle early enough to lower the power consumption before
+ * drastic steps are needed
+ * Actions include relaxing the power down sleep thresholds and
+ * decreasing the number of TX streams
+ * 3) Avoid throughput performance impact as much as possible
+ *
+ *=============================================================================
+ * Condition Nxt State Condition Nxt State Condition Nxt State
+ *-----------------------------------------------------------------------------
+ * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
+ * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
+ * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
+ * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
+ *=============================================================================
+ */
+static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ int i;
+ bool changed = false;
+ enum iwl_tt_state old_state;
+ struct iwl_tt_trans *transaction;
+
+ old_state = tt->state;
+ for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) {
+ /* based on the current TT state,
+ * find the curresponding transaction table
+ * each table has (IWL_TI_STATE_MAX - 1) entries
+ * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1))
+ * will advance to the correct table.
+ * then based on the current temperature
+ * find the next state need to transaction to
+ * go through all the possible (IWL_TI_STATE_MAX - 1) entries
+ * in the current table to see if transaction is needed
+ */
+ transaction = tt->transaction +
+ ((old_state * (IWL_TI_STATE_MAX - 1)) + i);
+ if (temp >= transaction->tt_low &&
+ temp <= transaction->tt_high) {
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if ((tt->tt_previous_temp) &&
+ (temp > tt->tt_previous_temp) &&
+ ((temp - tt->tt_previous_temp) >
+ IWL_TT_INCREASE_MARGIN)) {
+ IWL_DEBUG_TEMP(priv,
+ "Temperature increase %d "
+ "degree Celsius\n",
+ (temp - tt->tt_previous_temp));
+ }
+ tt->tt_previous_temp = temp;
+#endif
+ if (old_state !=
+ transaction->next_state) {
+ changed = true;
+ tt->state =
+ transaction->next_state;
+ }
+ break;
+ }
+ }
+ /* stop ct_kill_waiting_tm timer */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
+ if (changed) {
+ if (tt->state >= IWL_TI_1) {
+ /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */
+ tt->tt_power_mode = IWL_POWER_INDEX_5;
+
+ if (!iwl_ht_enabled(priv)) {
+ struct iwl_rxon_context *ctx;
+
+ for_each_context(priv, ctx) {
+ struct iwl_rxon_cmd *rxon;
+
+ rxon = &ctx->staging;
+
+ /* disable HT */
+ rxon->flags &= ~(
+ RXON_FLG_CHANNEL_MODE_MSK |
+ RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
+ RXON_FLG_HT40_PROT_MSK |
+ RXON_FLG_HT_PROT_MSK);
+ }
+ } else {
+ /* check HT capability and set
+ * according to the system HT capability
+ * in case get disabled before */
+ iwl_set_rxon_ht(priv, &priv->current_ht_config);
+ }
+
+ } else {
+ /*
+ * restore system power setting -- it will be
+ * recalculated automatically.
+ */
+
+ /* check HT capability and set
+ * according to the system HT capability
+ * in case get disabled before */
+ iwl_set_rxon_ht(priv, &priv->current_ht_config);
+ }
+ mutex_lock(&priv->mutex);
+ if (old_state == IWL_TI_CT_KILL)
+ clear_bit(STATUS_CT_KILL, &priv->status);
+ if (tt->state != IWL_TI_CT_KILL &&
+ iwl_power_update_mode(priv, true)) {
+ /* TT state not updated
+ * try again during next temperature read
+ */
+ IWL_ERR(priv, "Cannot update power mode, "
+ "TT state not updated\n");
+ if (old_state == IWL_TI_CT_KILL)
+ set_bit(STATUS_CT_KILL, &priv->status);
+ tt->state = old_state;
+ } else {
+ IWL_DEBUG_TEMP(priv,
+ "Thermal Throttling to new state: %u\n",
+ tt->state);
+ if (old_state != IWL_TI_CT_KILL &&
+ tt->state == IWL_TI_CT_KILL) {
+ if (force) {
+ IWL_DEBUG_TEMP(priv,
+ "Enter IWL_TI_CT_KILL\n");
+ set_bit(STATUS_CT_KILL, &priv->status);
+ iwl_perform_ct_kill_task(priv, true);
+ } else {
+ tt->state = old_state;
+ iwl_prepare_ct_kill_task(priv);
+ }
+ } else if (old_state == IWL_TI_CT_KILL &&
+ tt->state != IWL_TI_CT_KILL) {
+ IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n");
+ iwl_perform_ct_kill_task(priv, false);
+ }
+ }
+ mutex_unlock(&priv->mutex);
+ }
+}
+
+/* Card State Notification indicated reach critical temperature
+ * if PSP not enable, no Thermal Throttling function will be performed
+ * just set the GP1 bit to acknowledge the event
+ * otherwise, go into IWL_TI_CT_KILL state
+ * since Card State Notification will not provide any temperature reading
+ * for Legacy mode
+ * so just pass the CT_KILL temperature to iwl_legacy_tt_handler()
+ * for advance mode
+ * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state
+ */
+static void iwl_bg_ct_enter(struct work_struct *work)
+{
+ struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter);
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ if (!iwl_is_ready(priv))
+ return;
+
+ if (tt->state != IWL_TI_CT_KILL) {
+ IWL_ERR(priv, "Device reached critical temperature "
+ "- ucode going to sleep!\n");
+ if (!priv->thermal_throttle.advanced_tt)
+ iwl_legacy_tt_handler(priv,
+ IWL_MINIMAL_POWER_THRESHOLD,
+ true);
+ else
+ iwl_advance_tt_handler(priv,
+ CT_KILL_THRESHOLD + 1, true);
+ }
+}
+
+/* Card State Notification indicated out of critical temperature
+ * since Card State Notification will not provide any temperature reading
+ * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
+ * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
+ */
+static void iwl_bg_ct_exit(struct work_struct *work)
+{
+ struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit);
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ if (!iwl_is_ready(priv))
+ return;
+
+ /* stop ct_kill_exit_tm timer */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
+
+ if (tt->state == IWL_TI_CT_KILL) {
+ IWL_ERR(priv,
+ "Device temperature below critical"
+ "- ucode awake!\n");
+ /*
+ * exit from CT_KILL state
+ * reset the current temperature reading
+ */
+ priv->temperature = 0;
+ if (!priv->thermal_throttle.advanced_tt)
+ iwl_legacy_tt_handler(priv,
+ IWL_REDUCED_PERFORMANCE_THRESHOLD_2,
+ true);
+ else
+ iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD,
+ true);
+ }
+}
+
+void iwl_tt_enter_ct_kill(struct iwl_priv *priv)
+{
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n");
+ queue_work(priv->workqueue, &priv->ct_enter);
+}
+
+void iwl_tt_exit_ct_kill(struct iwl_priv *priv)
+{
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n");
+ queue_work(priv->workqueue, &priv->ct_exit);
+}
+
+static void iwl_bg_tt_work(struct work_struct *work)
+{
+ struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work);
+ s32 temp = priv->temperature; /* degrees CELSIUS except specified */
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ if (!priv->thermal_throttle.advanced_tt)
+ iwl_legacy_tt_handler(priv, temp, false);
+ else
+ iwl_advance_tt_handler(priv, temp, false);
+}
+
+void iwl_tt_handler(struct iwl_priv *priv)
+{
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n");
+ queue_work(priv->workqueue, &priv->tt_work);
+}
+
+/* Thermal throttling initialization
+ * For advance thermal throttling:
+ * Initialize Thermal Index and temperature threshold table
+ * Initialize thermal throttling restriction table
+ */
+void iwl_tt_initialize(struct iwl_priv *priv)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+ int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1);
+ struct iwl_tt_trans *transaction;
+
+ IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n");
+
+ memset(tt, 0, sizeof(struct iwl_tt_mgmt));
+
+ tt->state = IWL_TI_0;
+ setup_timer(&priv->thermal_throttle.ct_kill_exit_tm,
+ iwl_tt_check_exit_ct_kill, (unsigned long)priv);
+ setup_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
+ iwl_tt_ready_for_ct_kill, (unsigned long)priv);
+ /* setup deferred ct kill work */
+ INIT_WORK(&priv->tt_work, iwl_bg_tt_work);
+ INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
+ INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
+
+ if (priv->lib->adv_thermal_throttle) {
+ IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n");
+ tt->restriction = kcalloc(IWL_TI_STATE_MAX,
+ sizeof(struct iwl_tt_restriction),
+ GFP_KERNEL);
+ tt->transaction = kcalloc(IWL_TI_STATE_MAX *
+ (IWL_TI_STATE_MAX - 1),
+ sizeof(struct iwl_tt_trans),
+ GFP_KERNEL);
+ if (!tt->restriction || !tt->transaction) {
+ IWL_ERR(priv, "Fallback to Legacy Throttling\n");
+ priv->thermal_throttle.advanced_tt = false;
+ kfree(tt->restriction);
+ tt->restriction = NULL;
+ kfree(tt->transaction);
+ tt->transaction = NULL;
+ } else {
+ transaction = tt->transaction +
+ (IWL_TI_0 * (IWL_TI_STATE_MAX - 1));
+ memcpy(transaction, &tt_range_0[0], size);
+ transaction = tt->transaction +
+ (IWL_TI_1 * (IWL_TI_STATE_MAX - 1));
+ memcpy(transaction, &tt_range_1[0], size);
+ transaction = tt->transaction +
+ (IWL_TI_2 * (IWL_TI_STATE_MAX - 1));
+ memcpy(transaction, &tt_range_2[0], size);
+ transaction = tt->transaction +
+ (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1));
+ memcpy(transaction, &tt_range_3[0], size);
+ size = sizeof(struct iwl_tt_restriction) *
+ IWL_TI_STATE_MAX;
+ memcpy(tt->restriction,
+ &restriction_range[0], size);
+ priv->thermal_throttle.advanced_tt = true;
+ }
+ } else {
+ IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n");
+ priv->thermal_throttle.advanced_tt = false;
+ }
+}
+
+/* cleanup thermal throttling management related memory and timer */
+void iwl_tt_exit(struct iwl_priv *priv)
+{
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+
+ /* stop ct_kill_exit_tm timer if activated */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
+ /* stop ct_kill_waiting_tm timer if activated */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
+ cancel_work_sync(&priv->tt_work);
+ cancel_work_sync(&priv->ct_enter);
+ cancel_work_sync(&priv->ct_exit);
+
+ if (priv->thermal_throttle.advanced_tt) {
+ /* free advance thermal throttling memory */
+ kfree(tt->restriction);
+ tt->restriction = NULL;
+ kfree(tt->transaction);
+ tt->transaction = NULL;
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h
new file mode 100644
index 000000000000..d324e9be9cbf
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h
@@ -0,0 +1,128 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+#ifndef __iwl_tt_setting_h__
+#define __iwl_tt_setting_h__
+
+#include "commands.h"
+
+#define IWL_ABSOLUTE_ZERO 0
+#define IWL_ABSOLUTE_MAX 0xFFFFFFFF
+#define IWL_TT_INCREASE_MARGIN 5
+#define IWL_TT_CT_KILL_MARGIN 3
+
+enum iwl_antenna_ok {
+ IWL_ANT_OK_NONE,
+ IWL_ANT_OK_SINGLE,
+ IWL_ANT_OK_MULTI,
+};
+
+/* Thermal Throttling State Machine states */
+enum iwl_tt_state {
+ IWL_TI_0, /* normal temperature, system power state */
+ IWL_TI_1, /* high temperature detect, low power state */
+ IWL_TI_2, /* higher temperature detected, lower power state */
+ IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */
+ IWL_TI_STATE_MAX
+};
+
+/**
+ * struct iwl_tt_restriction - Thermal Throttling restriction table
+ * @tx_stream: number of tx stream allowed
+ * @is_ht: ht enable/disable
+ * @rx_stream: number of rx stream allowed
+ *
+ * This table is used by advance thermal throttling management
+ * based on the current thermal throttling state, and determines
+ * the number of tx/rx streams and the status of HT operation.
+ */
+struct iwl_tt_restriction {
+ enum iwl_antenna_ok tx_stream;
+ enum iwl_antenna_ok rx_stream;
+ bool is_ht;
+};
+
+/**
+ * struct iwl_tt_trans - Thermal Throttling transaction table
+ * @next_state: next thermal throttling mode
+ * @tt_low: low temperature threshold to change state
+ * @tt_high: high temperature threshold to change state
+ *
+ * This is used by the advanced thermal throttling algorithm
+ * to determine the next thermal state to go based on the
+ * current temperature.
+ */
+struct iwl_tt_trans {
+ enum iwl_tt_state next_state;
+ u32 tt_low;
+ u32 tt_high;
+};
+
+/**
+ * struct iwl_tt_mgnt - Thermal Throttling Management structure
+ * @advanced_tt: advanced thermal throttle required
+ * @state: current Thermal Throttling state
+ * @tt_power_mode: Thermal Throttling power mode index
+ * being used to set power level when
+ * when thermal throttling state != IWL_TI_0
+ * the tt_power_mode should set to different
+ * power mode based on the current tt state
+ * @tt_previous_temperature: last measured temperature
+ * @iwl_tt_restriction: ptr to restriction tbl, used by advance
+ * thermal throttling to determine how many tx/rx streams
+ * should be used in tt state; and can HT be enabled or not
+ * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling
+ * state transaction
+ * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature
+ * @ct_kill_exit_tm: timer to exit thermal kill
+ */
+struct iwl_tt_mgmt {
+ enum iwl_tt_state state;
+ bool advanced_tt;
+ u8 tt_power_mode;
+ bool ct_kill_toggle;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ s32 tt_previous_temp;
+#endif
+ struct iwl_tt_restriction *restriction;
+ struct iwl_tt_trans *transaction;
+ struct timer_list ct_kill_exit_tm;
+ struct timer_list ct_kill_waiting_tm;
+};
+
+u8 iwl_tt_current_power_mode(struct iwl_priv *priv);
+bool iwl_tt_is_low_power_state(struct iwl_priv *priv);
+bool iwl_ht_enabled(struct iwl_priv *priv);
+enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
+enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
+void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
+void iwl_tt_exit_ct_kill(struct iwl_priv *priv);
+void iwl_tt_handler(struct iwl_priv *priv);
+void iwl_tt_initialize(struct iwl_priv *priv);
+void iwl_tt_exit(struct iwl_priv *priv);
+
+#endif /* __iwl_tt_setting_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
new file mode 100644
index 000000000000..59e2001c39f8
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
@@ -0,0 +1,1413 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ieee80211.h>
+#include "iwl-io.h"
+#include "iwl-trans.h"
+#include "iwl-agn-hw.h"
+#include "dev.h"
+#include "agn.h"
+
+static const u8 tid_to_ac[] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO,
+};
+
+static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ __le16 fc, __le32 *tx_flags)
+{
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS ||
+ info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT ||
+ info->flags & IEEE80211_TX_CTL_AMPDU)
+ *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK;
+}
+
+/*
+ * handle build REPLY_TX command notification.
+ */
+static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
+ struct sk_buff *skb,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_hdr *hdr, u8 sta_id)
+{
+ __le16 fc = hdr->frame_control;
+ __le32 tx_flags = tx_cmd->tx_flags;
+
+ tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+ tx_flags |= TX_CMD_FLG_ACK_MSK;
+ else
+ tx_flags &= ~TX_CMD_FLG_ACK_MSK;
+
+ if (ieee80211_is_probe_resp(fc))
+ tx_flags |= TX_CMD_FLG_TSF_MSK;
+ else if (ieee80211_is_back_req(fc))
+ tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
+ else if (info->band == IEEE80211_BAND_2GHZ &&
+ priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
+ (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
+ ieee80211_is_reassoc_req(fc) ||
+ info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
+ tx_flags |= TX_CMD_FLG_IGNORE_BT;
+
+
+ tx_cmd->sta_id = sta_id;
+ if (ieee80211_has_morefrags(fc))
+ tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
+
+ if (ieee80211_is_data_qos(fc)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+ tx_cmd->tid_tspec = qc[0] & 0xf;
+ tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
+ } else {
+ tx_cmd->tid_tspec = IWL_TID_NON_QOS;
+ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+ tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
+ else
+ tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
+ }
+
+ iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags);
+
+ tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
+ if (ieee80211_is_mgmt(fc)) {
+ if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
+ tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3);
+ else
+ tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2);
+ } else {
+ tx_cmd->timeout.pm_frame_timeout = 0;
+ }
+
+ tx_cmd->driver_txop = 0;
+ tx_cmd->tx_flags = tx_flags;
+ tx_cmd->next_frame_len = 0;
+}
+
+static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta,
+ __le16 fc)
+{
+ u32 rate_flags;
+ int rate_idx;
+ u8 rts_retry_limit;
+ u8 data_retry_limit;
+ u8 rate_plcp;
+
+ if (priv->wowlan) {
+ rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
+ data_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
+ } else {
+ /* Set retry limit on RTS packets */
+ rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT;
+
+ /* Set retry limit on DATA packets and Probe Responses*/
+ if (ieee80211_is_probe_resp(fc)) {
+ data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT;
+ rts_retry_limit =
+ min(data_retry_limit, rts_retry_limit);
+ } else if (ieee80211_is_back_req(fc))
+ data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT;
+ else
+ data_retry_limit = IWLAGN_DEFAULT_TX_RETRY;
+ }
+
+ tx_cmd->data_retry_limit = data_retry_limit;
+ tx_cmd->rts_retry_limit = rts_retry_limit;
+
+ /* DATA packets will use the uCode station table for rate/antenna
+ * selection */
+ if (ieee80211_is_data(fc)) {
+ tx_cmd->initial_rate_index = 0;
+ tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
+ return;
+ } else if (ieee80211_is_back_req(fc))
+ tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
+
+ /**
+ * If the current TX rate stored in mac80211 has the MCS bit set, it's
+ * not really a TX rate. Thus, we use the lowest supported rate for
+ * this band. Also use the lowest supported rate if the stored rate
+ * index is invalid.
+ */
+ rate_idx = info->control.rates[0].idx;
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS ||
+ (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY))
+ rate_idx = rate_lowest_index(
+ &priv->nvm_data->bands[info->band], sta);
+ /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate_idx += IWL_FIRST_OFDM_RATE;
+ /* Get PLCP rate for tx_cmd->rate_n_flags */
+ rate_plcp = iwl_rates[rate_idx].plcp;
+ /* Zero out flags for this packet */
+ rate_flags = 0;
+
+ /* Set CCK flag as needed */
+ if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
+ rate_flags |= RATE_MCS_CCK_MSK;
+
+ /* Set up antennas */
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
+ priv->bt_full_concurrent) {
+ /* operated as 1x1 in full concurrency mode */
+ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
+ first_antenna(priv->nvm_data->valid_tx_ant));
+ } else
+ priv->mgmt_tx_ant = iwl_toggle_tx_ant(
+ priv, priv->mgmt_tx_ant,
+ priv->nvm_data->valid_tx_ant);
+ rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
+
+ /* Set the rate in the TX cmd */
+ tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags);
+}
+
+static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ struct iwl_tx_cmd *tx_cmd,
+ struct sk_buff *skb_frag)
+{
+ struct ieee80211_key_conf *keyconf = info->control.hw_key;
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
+ memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
+ if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
+ break;
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
+ ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
+ break;
+
+ case WLAN_CIPHER_SUITE_WEP104:
+ tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
+ /* fall through */
+ case WLAN_CIPHER_SUITE_WEP40:
+ tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP |
+ (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT);
+
+ memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
+
+ IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption "
+ "with key %d\n", keyconf->keyidx);
+ break;
+
+ default:
+ IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher);
+ break;
+ }
+}
+
+/**
+ * iwl_sta_id_or_broadcast - return sta_id or broadcast sta
+ * @context: the current context
+ * @sta: mac80211 station
+ *
+ * In certain circumstances mac80211 passes a station pointer
+ * that may be %NULL, for example during TX or key setup. In
+ * that case, we need to use the broadcast station, so this
+ * inline wraps that pattern.
+ */
+static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context,
+ struct ieee80211_sta *sta)
+{
+ int sta_id;
+
+ if (!sta)
+ return context->bcast_sta_id;
+
+ sta_id = iwl_sta_id(sta);
+
+ /*
+ * mac80211 should not be passing a partially
+ * initialised station!
+ */
+ WARN_ON(sta_id == IWL_INVALID_STATION);
+
+ return sta_id;
+}
+
+/*
+ * start REPLY_TX command process
+ */
+int iwlagn_tx_skb(struct iwl_priv *priv,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_station_priv *sta_priv = NULL;
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+ struct iwl_device_cmd *dev_cmd;
+ struct iwl_tx_cmd *tx_cmd;
+ __le16 fc;
+ u8 hdr_len;
+ u16 len, seq_number = 0;
+ u8 sta_id, tid = IWL_MAX_TID_COUNT;
+ bool is_agg = false, is_data_qos = false;
+ int txq_id;
+
+ if (info->control.vif)
+ ctx = iwl_rxon_ctx_from_vif(info->control.vif);
+
+ if (iwl_is_rfkill(priv)) {
+ IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n");
+ goto drop_unlock_priv;
+ }
+
+ fc = hdr->frame_control;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (ieee80211_is_auth(fc))
+ IWL_DEBUG_TX(priv, "Sending AUTH frame\n");
+ else if (ieee80211_is_assoc_req(fc))
+ IWL_DEBUG_TX(priv, "Sending ASSOC frame\n");
+ else if (ieee80211_is_reassoc_req(fc))
+ IWL_DEBUG_TX(priv, "Sending REASSOC frame\n");
+#endif
+
+ if (unlikely(ieee80211_is_probe_resp(fc))) {
+ struct iwl_wipan_noa_data *noa_data =
+ rcu_dereference(priv->noa_data);
+
+ if (noa_data &&
+ pskb_expand_head(skb, 0, noa_data->length,
+ GFP_ATOMIC) == 0) {
+ memcpy(skb_put(skb, noa_data->length),
+ noa_data->data, noa_data->length);
+ hdr = (struct ieee80211_hdr *)skb->data;
+ }
+ }
+
+ hdr_len = ieee80211_hdrlen(fc);
+
+ /* For management frames use broadcast id to do not break aggregation */
+ if (!ieee80211_is_data(fc))
+ sta_id = ctx->bcast_sta_id;
+ else {
+ /* Find index into station table for destination station */
+ sta_id = iwl_sta_id_or_broadcast(ctx, sta);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
+ hdr->addr1);
+ goto drop_unlock_priv;
+ }
+ }
+
+ if (sta)
+ sta_priv = (void *)sta->drv_priv;
+
+ if (sta_priv && sta_priv->asleep &&
+ (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) {
+ /*
+ * This sends an asynchronous command to the device,
+ * but we can rely on it being processed before the
+ * next frame is processed -- and the next frame to
+ * this station is the one that will consume this
+ * counter.
+ * For now set the counter to just 1 since we do not
+ * support uAPSD yet.
+ *
+ * FIXME: If we get two non-bufferable frames one
+ * after the other, we might only send out one of
+ * them because this is racy.
+ */
+ iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
+ }
+
+ dev_cmd = iwl_trans_alloc_tx_cmd(priv->trans);
+
+ if (unlikely(!dev_cmd))
+ goto drop_unlock_priv;
+
+ memset(dev_cmd, 0, sizeof(*dev_cmd));
+ dev_cmd->hdr.cmd = REPLY_TX;
+ tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
+
+ /* Total # bytes to be transmitted */
+ len = (u16)skb->len;
+ tx_cmd->len = cpu_to_le16(len);
+
+ if (info->control.hw_key)
+ iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb);
+
+ /* TODO need this for burst mode later on */
+ iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id);
+
+ iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc);
+
+ memset(&info->status, 0, sizeof(info->status));
+ memset(info->driver_data, 0, sizeof(info->driver_data));
+
+ info->driver_data[0] = ctx;
+ info->driver_data[1] = dev_cmd;
+ /* From now on, we cannot access info->control */
+
+ spin_lock(&priv->sta_lock);
+
+ if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
+ u8 *qc = NULL;
+ struct iwl_tid_data *tid_data;
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ goto drop_unlock_sta;
+ tid_data = &priv->tid_data[sta_id][tid];
+
+ /* aggregation is on for this <sta,tid> */
+ if (info->flags & IEEE80211_TX_CTL_AMPDU &&
+ tid_data->agg.state != IWL_AGG_ON) {
+ IWL_ERR(priv,
+ "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n",
+ info->flags, tid_data->agg.state);
+ IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n",
+ sta_id, tid,
+ IEEE80211_SEQ_TO_SN(tid_data->seq_number));
+ goto drop_unlock_sta;
+ }
+
+ /* We can receive packets from the stack in IWL_AGG_{ON,OFF}
+ * only. Check this here.
+ */
+ if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON &&
+ tid_data->agg.state != IWL_AGG_OFF,
+ "Tx while agg.state = %d\n", tid_data->agg.state))
+ goto drop_unlock_sta;
+
+ seq_number = tid_data->seq_number;
+ seq_number &= IEEE80211_SCTL_SEQ;
+ hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+ hdr->seq_ctrl |= cpu_to_le16(seq_number);
+ seq_number += 0x10;
+
+ if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ is_agg = true;
+ is_data_qos = true;
+ }
+
+ /* Copy MAC header from skb into command buffer */
+ memcpy(tx_cmd->hdr, hdr, hdr_len);
+
+ txq_id = info->hw_queue;
+
+ if (is_agg)
+ txq_id = priv->tid_data[sta_id][tid].agg.txq_id;
+ else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ /*
+ * The microcode will clear the more data
+ * bit in the last frame it transmits.
+ */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
+
+ WARN_ON_ONCE(is_agg &&
+ priv->queue_to_mac80211[txq_id] != info->hw_queue);
+
+ IWL_DEBUG_TX(priv, "TX to [%d|%d] Q:%d - seq: 0x%x\n", sta_id, tid,
+ txq_id, seq_number);
+
+ if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id))
+ goto drop_unlock_sta;
+
+ if (is_data_qos && !ieee80211_has_morefrags(fc))
+ priv->tid_data[sta_id][tid].seq_number = seq_number;
+
+ spin_unlock(&priv->sta_lock);
+
+ /*
+ * Avoid atomic ops if it isn't an associated client.
+ * Also, if this is a packet for aggregation, don't
+ * increase the counter because the ucode will stop
+ * aggregation queues when their respective station
+ * goes to sleep.
+ */
+ if (sta_priv && sta_priv->client && !is_agg)
+ atomic_inc(&sta_priv->pending_frames);
+
+ return 0;
+
+drop_unlock_sta:
+ if (dev_cmd)
+ iwl_trans_free_tx_cmd(priv->trans, dev_cmd);
+ spin_unlock(&priv->sta_lock);
+drop_unlock_priv:
+ return -1;
+}
+
+static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq)
+{
+ int q;
+
+ for (q = IWLAGN_FIRST_AMPDU_QUEUE;
+ q < priv->cfg->base_params->num_of_queues; q++) {
+ if (!test_and_set_bit(q, priv->agg_q_alloc)) {
+ priv->queue_to_mac80211[q] = mq;
+ return q;
+ }
+ }
+
+ return -ENOSPC;
+}
+
+static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q)
+{
+ clear_bit(q, priv->agg_q_alloc);
+ priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE;
+}
+
+int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid)
+{
+ struct iwl_tid_data *tid_data;
+ int sta_id, txq_id;
+ enum iwl_agg_state agg_state;
+
+ sta_id = iwl_sta_id(sta);
+
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
+ return -ENXIO;
+ }
+
+ spin_lock_bh(&priv->sta_lock);
+
+ tid_data = &priv->tid_data[sta_id][tid];
+ txq_id = tid_data->agg.txq_id;
+
+ switch (tid_data->agg.state) {
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ /*
+ * This can happen if the peer stops aggregation
+ * again before we've had a chance to drain the
+ * queue we selected previously, i.e. before the
+ * session was really started completely.
+ */
+ IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
+ goto turn_off;
+ case IWL_AGG_STARTING:
+ /*
+ * This can happen when the session is stopped before
+ * we receive ADDBA response
+ */
+ IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n");
+ goto turn_off;
+ case IWL_AGG_ON:
+ break;
+ default:
+ IWL_WARN(priv,
+ "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
+ sta_id, tid, tid_data->agg.state);
+ spin_unlock_bh(&priv->sta_lock);
+ return 0;
+ }
+
+ tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
+
+ /* There are still packets for this RA / TID in the HW */
+ if (!test_bit(txq_id, priv->agg_q_alloc)) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "stopping AGG on STA/TID %d/%d but hwq %d not used\n",
+ sta_id, tid, txq_id);
+ } else if (tid_data->agg.ssn != tid_data->next_reclaimed) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "Can't proceed: ssn %d, next_recl = %d\n",
+ tid_data->agg.ssn,
+ tid_data->next_reclaimed);
+ tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_DELBA;
+ spin_unlock_bh(&priv->sta_lock);
+ return 0;
+ }
+
+ IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
+ tid_data->agg.ssn);
+turn_off:
+ agg_state = tid_data->agg.state;
+ tid_data->agg.state = IWL_AGG_OFF;
+
+ spin_unlock_bh(&priv->sta_lock);
+
+ if (test_bit(txq_id, priv->agg_q_alloc)) {
+ /*
+ * If the transport didn't know that we wanted to start
+ * agreggation, don't tell it that we want to stop them.
+ * This can happen when we don't get the addBA response on
+ * time, or we hadn't time to drain the AC queues.
+ */
+ if (agg_state == IWL_AGG_ON)
+ iwl_trans_txq_disable(priv->trans, txq_id, true);
+ else
+ IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n",
+ agg_state);
+ iwlagn_dealloc_agg_txq(priv, txq_id);
+ }
+
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+
+ return 0;
+}
+
+int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+ struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+ struct iwl_tid_data *tid_data;
+ int sta_id, txq_id, ret;
+
+ IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n",
+ sta->addr, tid);
+
+ sta_id = iwl_sta_id(sta);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Start AGG on invalid station\n");
+ return -ENXIO;
+ }
+ if (unlikely(tid >= IWL_MAX_TID_COUNT))
+ return -EINVAL;
+
+ if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) {
+ IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n");
+ return -ENXIO;
+ }
+
+ txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]);
+ if (txq_id < 0) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "No free aggregation queue for %pM/%d\n",
+ sta->addr, tid);
+ return txq_id;
+ }
+
+ ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
+ if (ret)
+ return ret;
+
+ spin_lock_bh(&priv->sta_lock);
+ tid_data = &priv->tid_data[sta_id][tid];
+ tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
+ tid_data->agg.txq_id = txq_id;
+
+ *ssn = tid_data->agg.ssn;
+
+ if (*ssn == tid_data->next_reclaimed) {
+ IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
+ tid_data->agg.ssn);
+ tid_data->agg.state = IWL_AGG_STARTING;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ } else {
+ IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
+ "next_reclaimed = %d\n",
+ tid_data->agg.ssn,
+ tid_data->next_reclaimed);
+ tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+ }
+ spin_unlock_bh(&priv->sta_lock);
+
+ return ret;
+}
+
+int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid)
+{
+ struct iwl_tid_data *tid_data;
+ enum iwl_agg_state agg_state;
+ int sta_id, txq_id;
+ sta_id = iwl_sta_id(sta);
+
+ /*
+ * First set the agg state to OFF to avoid calling
+ * ieee80211_stop_tx_ba_cb in iwlagn_check_ratid_empty.
+ */
+ spin_lock_bh(&priv->sta_lock);
+
+ tid_data = &priv->tid_data[sta_id][tid];
+ txq_id = tid_data->agg.txq_id;
+ agg_state = tid_data->agg.state;
+ IWL_DEBUG_TX_QUEUES(priv, "Flush AGG: sta %d tid %d q %d state %d\n",
+ sta_id, tid, txq_id, tid_data->agg.state);
+
+ tid_data->agg.state = IWL_AGG_OFF;
+
+ spin_unlock_bh(&priv->sta_lock);
+
+ if (iwlagn_txfifo_flush(priv, BIT(txq_id)))
+ IWL_ERR(priv, "Couldn't flush the AGG queue\n");
+
+ if (test_bit(txq_id, priv->agg_q_alloc)) {
+ /*
+ * If the transport didn't know that we wanted to start
+ * agreggation, don't tell it that we want to stop them.
+ * This can happen when we don't get the addBA response on
+ * time, or we hadn't time to drain the AC queues.
+ */
+ if (agg_state == IWL_AGG_ON)
+ iwl_trans_txq_disable(priv->trans, txq_id, true);
+ else
+ IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n",
+ agg_state);
+ iwlagn_dealloc_agg_txq(priv, txq_id);
+ }
+
+ return 0;
+}
+
+int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u8 buf_size)
+{
+ struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
+ struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+ int q, fifo;
+ u16 ssn;
+
+ buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
+
+ spin_lock_bh(&priv->sta_lock);
+ ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn;
+ q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id;
+ priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON;
+ spin_unlock_bh(&priv->sta_lock);
+
+ fifo = ctx->ac_to_fifo[tid_to_ac[tid]];
+
+ iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid,
+ buf_size, ssn, 0);
+
+ /*
+ * If the limit is 0, then it wasn't initialised yet,
+ * use the default. We can do that since we take the
+ * minimum below, and we don't want to go above our
+ * default due to hardware restrictions.
+ */
+ if (sta_priv->max_agg_bufsize == 0)
+ sta_priv->max_agg_bufsize =
+ LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+
+ /*
+ * Even though in theory the peer could have different
+ * aggregation reorder buffer sizes for different sessions,
+ * our ucode doesn't allow for that and has a global limit
+ * for each station. Therefore, use the minimum of all the
+ * aggregation sessions and our default value.
+ */
+ sta_priv->max_agg_bufsize =
+ min(sta_priv->max_agg_bufsize, buf_size);
+
+ if (priv->hw_params.use_rts_for_aggregation) {
+ /*
+ * switch to RTS/CTS if it is the prefer protection
+ * method for HT traffic
+ */
+
+ sta_priv->lq_sta.lq.general_params.flags |=
+ LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
+ }
+ priv->agg_tids_count++;
+ IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n",
+ priv->agg_tids_count);
+
+ sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit =
+ sta_priv->max_agg_bufsize;
+
+ IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n",
+ sta->addr, tid);
+
+ return iwl_send_lq_cmd(priv, ctx,
+ &sta_priv->lq_sta.lq, CMD_ASYNC, false);
+}
+
+static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
+{
+ struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid];
+ enum iwl_rxon_context_id ctx;
+ struct ieee80211_vif *vif;
+ u8 *addr;
+
+ lockdep_assert_held(&priv->sta_lock);
+
+ addr = priv->stations[sta_id].sta.sta.addr;
+ ctx = priv->stations[sta_id].ctxid;
+ vif = priv->contexts[ctx].vif;
+
+ switch (priv->tid_data[sta_id][tid].agg.state) {
+ case IWL_EMPTYING_HW_QUEUE_DELBA:
+ /* There are no packets for this RA / TID in the HW any more */
+ if (tid_data->agg.ssn == tid_data->next_reclaimed) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "Can continue DELBA flow ssn = next_recl = %d\n",
+ tid_data->next_reclaimed);
+ iwl_trans_txq_disable(priv->trans,
+ tid_data->agg.txq_id, true);
+ iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id);
+ tid_data->agg.state = IWL_AGG_OFF;
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
+ }
+ break;
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ /* There are no packets for this RA / TID in the HW any more */
+ if (tid_data->agg.ssn == tid_data->next_reclaimed) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "Can continue ADDBA flow ssn = next_recl = %d\n",
+ tid_data->next_reclaimed);
+ tid_data->agg.state = IWL_AGG_STARTING;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx,
+ const u8 *addr1)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_station_priv *sta_priv;
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(ctx->vif, addr1);
+ if (sta) {
+ sta_priv = (void *)sta->drv_priv;
+ /* avoid atomic ops if this isn't a client */
+ if (sta_priv->client &&
+ atomic_dec_return(&sta_priv->pending_frames) == 0)
+ ieee80211_sta_block_awake(priv->hw, sta, false);
+ }
+ rcu_read_unlock();
+}
+
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
+ struct ieee80211_tx_info *info)
+{
+ struct ieee80211_tx_rate *r = &info->status.rates[0];
+
+ info->status.antenna =
+ ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+ if (rate_n_flags & RATE_MCS_HT_MSK)
+ r->flags |= IEEE80211_TX_RC_MCS;
+ if (rate_n_flags & RATE_MCS_GF_MSK)
+ r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
+ if (rate_n_flags & RATE_MCS_HT40_MSK)
+ r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ if (rate_n_flags & RATE_MCS_DUP_MSK)
+ r->flags |= IEEE80211_TX_RC_DUP_DATA;
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ r->flags |= IEEE80211_TX_RC_SHORT_GI;
+ r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_get_tx_fail_reason(u32 status)
+{
+#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
+#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+
+ switch (status & TX_STATUS_MSK) {
+ case TX_STATUS_SUCCESS:
+ return "SUCCESS";
+ TX_STATUS_POSTPONE(DELAY);
+ TX_STATUS_POSTPONE(FEW_BYTES);
+ TX_STATUS_POSTPONE(BT_PRIO);
+ TX_STATUS_POSTPONE(QUIET_PERIOD);
+ TX_STATUS_POSTPONE(CALC_TTAK);
+ TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
+ TX_STATUS_FAIL(SHORT_LIMIT);
+ TX_STATUS_FAIL(LONG_LIMIT);
+ TX_STATUS_FAIL(FIFO_UNDERRUN);
+ TX_STATUS_FAIL(DRAIN_FLOW);
+ TX_STATUS_FAIL(RFKILL_FLUSH);
+ TX_STATUS_FAIL(LIFE_EXPIRE);
+ TX_STATUS_FAIL(DEST_PS);
+ TX_STATUS_FAIL(HOST_ABORTED);
+ TX_STATUS_FAIL(BT_RETRY);
+ TX_STATUS_FAIL(STA_INVALID);
+ TX_STATUS_FAIL(FRAG_DROPPED);
+ TX_STATUS_FAIL(TID_DISABLE);
+ TX_STATUS_FAIL(FIFO_FLUSHED);
+ TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
+ TX_STATUS_FAIL(PASSIVE_NO_RX);
+ TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
+ }
+
+ return "UNKNOWN";
+
+#undef TX_STATUS_FAIL
+#undef TX_STATUS_POSTPONE
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
+{
+ status &= AGG_TX_STATUS_MSK;
+
+ switch (status) {
+ case AGG_TX_STATE_UNDERRUN_MSK:
+ priv->reply_agg_tx_stats.underrun++;
+ break;
+ case AGG_TX_STATE_BT_PRIO_MSK:
+ priv->reply_agg_tx_stats.bt_prio++;
+ break;
+ case AGG_TX_STATE_FEW_BYTES_MSK:
+ priv->reply_agg_tx_stats.few_bytes++;
+ break;
+ case AGG_TX_STATE_ABORT_MSK:
+ priv->reply_agg_tx_stats.abort++;
+ break;
+ case AGG_TX_STATE_LAST_SENT_TTL_MSK:
+ priv->reply_agg_tx_stats.last_sent_ttl++;
+ break;
+ case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK:
+ priv->reply_agg_tx_stats.last_sent_try++;
+ break;
+ case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK:
+ priv->reply_agg_tx_stats.last_sent_bt_kill++;
+ break;
+ case AGG_TX_STATE_SCD_QUERY_MSK:
+ priv->reply_agg_tx_stats.scd_query++;
+ break;
+ case AGG_TX_STATE_TEST_BAD_CRC32_MSK:
+ priv->reply_agg_tx_stats.bad_crc32++;
+ break;
+ case AGG_TX_STATE_RESPONSE_MSK:
+ priv->reply_agg_tx_stats.response++;
+ break;
+ case AGG_TX_STATE_DUMP_TX_MSK:
+ priv->reply_agg_tx_stats.dump_tx++;
+ break;
+ case AGG_TX_STATE_DELAY_TX_MSK:
+ priv->reply_agg_tx_stats.delay_tx++;
+ break;
+ default:
+ priv->reply_agg_tx_stats.unknown++;
+ break;
+ }
+}
+
+static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp)
+{
+ return le32_to_cpup((__le32 *)&tx_resp->status +
+ tx_resp->frame_count) & IEEE80211_MAX_SN;
+}
+
+static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
+ struct iwlagn_tx_resp *tx_resp)
+{
+ struct agg_tx_status *frame_status = &tx_resp->status;
+ int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
+ IWLAGN_TX_RES_TID_POS;
+ int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
+ IWLAGN_TX_RES_RA_POS;
+ struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg;
+ u32 status = le16_to_cpu(tx_resp->status.status);
+ int i;
+
+ WARN_ON(tid == IWL_TID_NON_QOS);
+
+ if (agg->wait_for_ba)
+ IWL_DEBUG_TX_REPLY(priv,
+ "got tx response w/o block-ack\n");
+
+ agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
+ agg->wait_for_ba = (tx_resp->frame_count > 1);
+
+ /*
+ * If the BT kill count is non-zero, we'll get this
+ * notification again.
+ */
+ if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
+ priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
+ IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
+ }
+
+ if (tx_resp->frame_count == 1)
+ return;
+
+ IWL_DEBUG_TX_REPLY(priv, "TXQ %d initial_rate 0x%x ssn %d frm_cnt %d\n",
+ agg->txq_id,
+ le32_to_cpu(tx_resp->rate_n_flags),
+ iwlagn_get_scd_ssn(tx_resp), tx_resp->frame_count);
+
+ /* Construct bit-map of pending frames within Tx window */
+ for (i = 0; i < tx_resp->frame_count; i++) {
+ u16 fstatus = le16_to_cpu(frame_status[i].status);
+ u8 retry_cnt = (fstatus & AGG_TX_TRY_MSK) >> AGG_TX_TRY_POS;
+
+ if (status & AGG_TX_STATUS_MSK)
+ iwlagn_count_agg_tx_err_status(priv, fstatus);
+
+ if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
+ AGG_TX_STATE_ABORT_MSK))
+ continue;
+
+ if (status & AGG_TX_STATUS_MSK || retry_cnt > 1)
+ IWL_DEBUG_TX_REPLY(priv,
+ "%d: status %s (0x%04x), try-count (0x%01x)\n",
+ i,
+ iwl_get_agg_tx_fail_reason(fstatus),
+ fstatus & AGG_TX_STATUS_MSK,
+ retry_cnt);
+ }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x
+
+const char *iwl_get_agg_tx_fail_reason(u16 status)
+{
+ status &= AGG_TX_STATUS_MSK;
+ switch (status) {
+ case AGG_TX_STATE_TRANSMITTED:
+ return "SUCCESS";
+ AGG_TX_STATE_FAIL(UNDERRUN_MSK);
+ AGG_TX_STATE_FAIL(BT_PRIO_MSK);
+ AGG_TX_STATE_FAIL(FEW_BYTES_MSK);
+ AGG_TX_STATE_FAIL(ABORT_MSK);
+ AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK);
+ AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK);
+ AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK);
+ AGG_TX_STATE_FAIL(SCD_QUERY_MSK);
+ AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK);
+ AGG_TX_STATE_FAIL(RESPONSE_MSK);
+ AGG_TX_STATE_FAIL(DUMP_TX_MSK);
+ AGG_TX_STATE_FAIL(DELAY_TX_MSK);
+ }
+
+ return "UNKNOWN";
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status)
+{
+ status &= TX_STATUS_MSK;
+
+ switch (status) {
+ case TX_STATUS_POSTPONE_DELAY:
+ priv->reply_tx_stats.pp_delay++;
+ break;
+ case TX_STATUS_POSTPONE_FEW_BYTES:
+ priv->reply_tx_stats.pp_few_bytes++;
+ break;
+ case TX_STATUS_POSTPONE_BT_PRIO:
+ priv->reply_tx_stats.pp_bt_prio++;
+ break;
+ case TX_STATUS_POSTPONE_QUIET_PERIOD:
+ priv->reply_tx_stats.pp_quiet_period++;
+ break;
+ case TX_STATUS_POSTPONE_CALC_TTAK:
+ priv->reply_tx_stats.pp_calc_ttak++;
+ break;
+ case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
+ priv->reply_tx_stats.int_crossed_retry++;
+ break;
+ case TX_STATUS_FAIL_SHORT_LIMIT:
+ priv->reply_tx_stats.short_limit++;
+ break;
+ case TX_STATUS_FAIL_LONG_LIMIT:
+ priv->reply_tx_stats.long_limit++;
+ break;
+ case TX_STATUS_FAIL_FIFO_UNDERRUN:
+ priv->reply_tx_stats.fifo_underrun++;
+ break;
+ case TX_STATUS_FAIL_DRAIN_FLOW:
+ priv->reply_tx_stats.drain_flow++;
+ break;
+ case TX_STATUS_FAIL_RFKILL_FLUSH:
+ priv->reply_tx_stats.rfkill_flush++;
+ break;
+ case TX_STATUS_FAIL_LIFE_EXPIRE:
+ priv->reply_tx_stats.life_expire++;
+ break;
+ case TX_STATUS_FAIL_DEST_PS:
+ priv->reply_tx_stats.dest_ps++;
+ break;
+ case TX_STATUS_FAIL_HOST_ABORTED:
+ priv->reply_tx_stats.host_abort++;
+ break;
+ case TX_STATUS_FAIL_BT_RETRY:
+ priv->reply_tx_stats.bt_retry++;
+ break;
+ case TX_STATUS_FAIL_STA_INVALID:
+ priv->reply_tx_stats.sta_invalid++;
+ break;
+ case TX_STATUS_FAIL_FRAG_DROPPED:
+ priv->reply_tx_stats.frag_drop++;
+ break;
+ case TX_STATUS_FAIL_TID_DISABLE:
+ priv->reply_tx_stats.tid_disable++;
+ break;
+ case TX_STATUS_FAIL_FIFO_FLUSHED:
+ priv->reply_tx_stats.fifo_flush++;
+ break;
+ case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL:
+ priv->reply_tx_stats.insuff_cf_poll++;
+ break;
+ case TX_STATUS_FAIL_PASSIVE_NO_RX:
+ priv->reply_tx_stats.fail_hw_drop++;
+ break;
+ case TX_STATUS_FAIL_NO_BEACON_ON_RADAR:
+ priv->reply_tx_stats.sta_color_mismatch++;
+ break;
+ default:
+ priv->reply_tx_stats.unknown++;
+ break;
+ }
+}
+
+static void iwlagn_set_tx_status(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ struct iwlagn_tx_resp *tx_resp)
+{
+ u16 status = le16_to_cpu(tx_resp->status.status);
+
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
+ info->flags |= iwl_tx_status_to_mac80211(status);
+ iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
+ info);
+ if (!iwl_is_tx_success(status))
+ iwlagn_count_tx_err_status(priv, status);
+}
+
+static void iwl_check_abort_status(struct iwl_priv *priv,
+ u8 frame_count, u32 status)
+{
+ if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
+ IWL_ERR(priv, "Tx flush command to flush out all frames\n");
+ if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
+ queue_work(priv->workqueue, &priv->tx_flush);
+ }
+}
+
+void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ int txq_id = SEQ_TO_QUEUE(sequence);
+ int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence);
+ struct iwlagn_tx_resp *tx_resp = (void *)pkt->data;
+ struct ieee80211_hdr *hdr;
+ u32 status = le16_to_cpu(tx_resp->status.status);
+ u16 ssn = iwlagn_get_scd_ssn(tx_resp);
+ int tid;
+ int sta_id;
+ int freed;
+ struct ieee80211_tx_info *info;
+ struct sk_buff_head skbs;
+ struct sk_buff *skb;
+ struct iwl_rxon_context *ctx;
+ bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
+
+ tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
+ IWLAGN_TX_RES_TID_POS;
+ sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
+ IWLAGN_TX_RES_RA_POS;
+
+ spin_lock_bh(&priv->sta_lock);
+
+ if (is_agg) {
+ WARN_ON_ONCE(sta_id >= IWLAGN_STATION_COUNT ||
+ tid >= IWL_MAX_TID_COUNT);
+ if (txq_id != priv->tid_data[sta_id][tid].agg.txq_id)
+ IWL_ERR(priv, "txq_id mismatch: %d %d\n", txq_id,
+ priv->tid_data[sta_id][tid].agg.txq_id);
+ iwl_rx_reply_tx_agg(priv, tx_resp);
+ }
+
+ __skb_queue_head_init(&skbs);
+
+ if (tx_resp->frame_count == 1) {
+ u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
+ next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10);
+
+ if (is_agg) {
+ /* If this is an aggregation queue, we can rely on the
+ * ssn since the wifi sequence number corresponds to
+ * the index in the TFD ring (%256).
+ * The seq_ctl is the sequence control of the packet
+ * to which this Tx response relates. But if there is a
+ * hole in the bitmap of the BA we received, this Tx
+ * response may allow to reclaim the hole and all the
+ * subsequent packets that were already acked.
+ * In that case, seq_ctl != ssn, and the next packet
+ * to be reclaimed will be ssn and not seq_ctl.
+ */
+ next_reclaimed = ssn;
+ }
+
+ if (tid != IWL_TID_NON_QOS) {
+ priv->tid_data[sta_id][tid].next_reclaimed =
+ next_reclaimed;
+ IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
+ next_reclaimed);
+ }
+
+ iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs);
+
+ iwlagn_check_ratid_empty(priv, sta_id, tid);
+ freed = 0;
+
+ /* process frames */
+ skb_queue_walk(&skbs, skb) {
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ priv->last_seq_ctl = tx_resp->seq_ctl;
+
+ info = IEEE80211_SKB_CB(skb);
+ ctx = info->driver_data[0];
+ iwl_trans_free_tx_cmd(priv->trans,
+ info->driver_data[1]);
+
+ memset(&info->status, 0, sizeof(info->status));
+
+ if (status == TX_STATUS_FAIL_PASSIVE_NO_RX &&
+ ctx->vif &&
+ ctx->vif->type == NL80211_IFTYPE_STATION) {
+ /* block and stop all queues */
+ priv->passive_no_rx = true;
+ IWL_DEBUG_TX_QUEUES(priv,
+ "stop all queues: passive channel\n");
+ ieee80211_stop_queues(priv->hw);
+
+ IWL_DEBUG_TX_REPLY(priv,
+ "TXQ %d status %s (0x%08x) "
+ "rate_n_flags 0x%x retries %d\n",
+ txq_id,
+ iwl_get_tx_fail_reason(status),
+ status,
+ le32_to_cpu(tx_resp->rate_n_flags),
+ tx_resp->failure_frame);
+
+ IWL_DEBUG_TX_REPLY(priv,
+ "FrameCnt = %d, idx=%d\n",
+ tx_resp->frame_count, cmd_index);
+ }
+
+ /* check if BAR is needed */
+ if (is_agg && !iwl_is_tx_success(status))
+ info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+ iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb),
+ tx_resp);
+ if (!is_agg)
+ iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
+
+ freed++;
+ }
+
+ if (tid != IWL_TID_NON_QOS) {
+ priv->tid_data[sta_id][tid].next_reclaimed =
+ next_reclaimed;
+ IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
+ next_reclaimed);
+ }
+
+ if (!is_agg && freed != 1)
+ IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed);
+
+ IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id,
+ iwl_get_tx_fail_reason(status), status);
+
+ IWL_DEBUG_TX_REPLY(priv,
+ "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n",
+ le32_to_cpu(tx_resp->rate_n_flags),
+ tx_resp->failure_frame,
+ SEQ_TO_INDEX(sequence), ssn,
+ le16_to_cpu(tx_resp->seq_ctl));
+ }
+
+ iwl_check_abort_status(priv, tx_resp->frame_count, status);
+ spin_unlock_bh(&priv->sta_lock);
+
+ while (!skb_queue_empty(&skbs)) {
+ skb = __skb_dequeue(&skbs);
+ ieee80211_tx_status(priv->hw, skb);
+ }
+}
+
+/**
+ * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA
+ *
+ * Handles block-acknowledge notification from device, which reports success
+ * of frames sent via aggregation.
+ */
+void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data;
+ struct iwl_ht_agg *agg;
+ struct sk_buff_head reclaimed_skbs;
+ struct sk_buff *skb;
+ int sta_id;
+ int tid;
+ int freed;
+
+ /* "flow" corresponds to Tx queue */
+ u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
+
+ /* "ssn" is start of block-ack Tx window, corresponds to index
+ * (in Tx queue's circular buffer) of first TFD/frame in window */
+ u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
+
+ if (scd_flow >= priv->cfg->base_params->num_of_queues) {
+ IWL_ERR(priv,
+ "BUG_ON scd_flow is bigger than number of queues\n");
+ return;
+ }
+
+ sta_id = ba_resp->sta_id;
+ tid = ba_resp->tid;
+ agg = &priv->tid_data[sta_id][tid].agg;
+
+ spin_lock_bh(&priv->sta_lock);
+
+ if (unlikely(!agg->wait_for_ba)) {
+ if (unlikely(ba_resp->bitmap))
+ IWL_ERR(priv, "Received BA when not expected\n");
+ spin_unlock_bh(&priv->sta_lock);
+ return;
+ }
+
+ if (unlikely(scd_flow != agg->txq_id)) {
+ /*
+ * FIXME: this is a uCode bug which need to be addressed,
+ * log the information and return for now.
+ * Since it is can possibly happen very often and in order
+ * not to fill the syslog, don't use IWL_ERR or IWL_WARN
+ */
+ IWL_DEBUG_TX_QUEUES(priv,
+ "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n",
+ scd_flow, sta_id, tid, agg->txq_id);
+ spin_unlock_bh(&priv->sta_lock);
+ return;
+ }
+
+ __skb_queue_head_init(&reclaimed_skbs);
+
+ /* Release all TFDs before the SSN, i.e. all TFDs in front of
+ * block-ack window (we assume that they've been successfully
+ * transmitted ... if not, it's too late anyway). */
+ iwl_trans_reclaim(priv->trans, scd_flow, ba_resp_scd_ssn,
+ &reclaimed_skbs);
+
+ IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, "
+ "sta_id = %d\n",
+ agg->wait_for_ba,
+ (u8 *) &ba_resp->sta_addr_lo32,
+ ba_resp->sta_id);
+ IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, "
+ "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
+ ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl),
+ (unsigned long long)le64_to_cpu(ba_resp->bitmap),
+ scd_flow, ba_resp_scd_ssn, ba_resp->txed,
+ ba_resp->txed_2_done);
+
+ /* Mark that the expected block-ack response arrived */
+ agg->wait_for_ba = false;
+
+ /* Sanity check values reported by uCode */
+ if (ba_resp->txed_2_done > ba_resp->txed) {
+ IWL_DEBUG_TX_REPLY(priv,
+ "bogus sent(%d) and ack(%d) count\n",
+ ba_resp->txed, ba_resp->txed_2_done);
+ /*
+ * set txed_2_done = txed,
+ * so it won't impact rate scale
+ */
+ ba_resp->txed = ba_resp->txed_2_done;
+ }
+
+ priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn;
+
+ iwlagn_check_ratid_empty(priv, sta_id, tid);
+ freed = 0;
+
+ skb_queue_walk(&reclaimed_skbs, skb) {
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ freed++;
+ else
+ WARN_ON_ONCE(1);
+
+ iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]);
+
+ memset(&info->status, 0, sizeof(info->status));
+ /* Packet was transmitted successfully, failures come as single
+ * frames because before failing a frame the firmware transmits
+ * it without aggregation at least once.
+ */
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ if (freed == 1) {
+ /* this is the first skb we deliver in this batch */
+ /* put the rate scaling data there */
+ info = IEEE80211_SKB_CB(skb);
+ memset(&info->status, 0, sizeof(info->status));
+ info->flags |= IEEE80211_TX_STAT_AMPDU;
+ info->status.ampdu_ack_len = ba_resp->txed_2_done;
+ info->status.ampdu_len = ba_resp->txed;
+ iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags,
+ info);
+ }
+ }
+
+ spin_unlock_bh(&priv->sta_lock);
+
+ while (!skb_queue_empty(&reclaimed_skbs)) {
+ skb = __skb_dequeue(&reclaimed_skbs);
+ ieee80211_tx_status(priv->hw, skb);
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c
new file mode 100644
index 000000000000..b662cf35b033
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c
@@ -0,0 +1,452 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+
+#include "iwl-io.h"
+#include "iwl-agn-hw.h"
+#include "iwl-trans.h"
+#include "iwl-fh.h"
+#include "iwl-op-mode.h"
+
+#include "dev.h"
+#include "agn.h"
+#include "calib.h"
+
+/******************************************************************************
+ *
+ * uCode download functions
+ *
+ ******************************************************************************/
+
+static inline const struct fw_img *
+iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type)
+{
+ if (ucode_type >= IWL_UCODE_TYPE_MAX)
+ return NULL;
+
+ return &priv->fw->img[ucode_type];
+}
+
+/*
+ * Calibration
+ */
+static int iwl_set_Xtal_calib(struct iwl_priv *priv)
+{
+ struct iwl_calib_xtal_freq_cmd cmd;
+ __le16 *xtal_calib = priv->nvm_data->xtal_calib;
+
+ iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD);
+ cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]);
+ cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]);
+ return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
+}
+
+static int iwl_set_temperature_offset_calib(struct iwl_priv *priv)
+{
+ struct iwl_calib_temperature_offset_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
+ cmd.radio_sensor_offset = priv->nvm_data->raw_temperature;
+ if (!(cmd.radio_sensor_offset))
+ cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET;
+
+ IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n",
+ le16_to_cpu(cmd.radio_sensor_offset));
+ return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
+}
+
+static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv)
+{
+ struct iwl_calib_temperature_offset_v2_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
+ cmd.radio_sensor_offset_high = priv->nvm_data->kelvin_temperature;
+ cmd.radio_sensor_offset_low = priv->nvm_data->raw_temperature;
+ if (!cmd.radio_sensor_offset_low) {
+ IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n");
+ cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET;
+ cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET;
+ }
+ cmd.burntVoltageRef = priv->nvm_data->calib_voltage;
+
+ IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n",
+ le16_to_cpu(cmd.radio_sensor_offset_high));
+ IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n",
+ le16_to_cpu(cmd.radio_sensor_offset_low));
+ IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n",
+ le16_to_cpu(cmd.burntVoltageRef));
+
+ return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
+}
+
+static int iwl_send_calib_cfg(struct iwl_priv *priv)
+{
+ struct iwl_calib_cfg_cmd calib_cfg_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = CALIBRATION_CFG_CMD,
+ .len = { sizeof(struct iwl_calib_cfg_cmd), },
+ .data = { &calib_cfg_cmd, },
+ };
+
+ memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
+ calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL;
+ calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL;
+ calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL;
+ calib_cfg_cmd.ucd_calib_cfg.flags =
+ IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK;
+
+ return iwl_dvm_send_cmd(priv, &cmd);
+}
+
+int iwl_init_alive_start(struct iwl_priv *priv)
+{
+ int ret;
+
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
+ /*
+ * Tell uCode we are ready to perform calibration
+ * need to perform this before any calibration
+ * no need to close the envlope since we are going
+ * to load the runtime uCode later.
+ */
+ ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+ if (ret)
+ return ret;
+
+ }
+
+ ret = iwl_send_calib_cfg(priv);
+ if (ret)
+ return ret;
+
+ /**
+ * temperature offset calibration is only needed for runtime ucode,
+ * so prepare the value now.
+ */
+ if (priv->lib->need_temp_offset_calib) {
+ if (priv->lib->temp_offset_v2)
+ return iwl_set_temperature_offset_calib_v2(priv);
+ else
+ return iwl_set_temperature_offset_calib(priv);
+ }
+
+ return 0;
+}
+
+static int iwl_send_wimax_coex(struct iwl_priv *priv)
+{
+ struct iwl_wimax_coex_cmd coex_cmd;
+
+ /* coexistence is disabled */
+ memset(&coex_cmd, 0, sizeof(coex_cmd));
+
+ return iwl_dvm_send_cmd_pdu(priv,
+ COEX_PRIORITY_TABLE_CMD, 0,
+ sizeof(coex_cmd), &coex_cmd);
+}
+
+static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
+ ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ ((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
+ (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
+ 0, 0, 0, 0, 0, 0, 0
+};
+
+void iwl_send_prio_tbl(struct iwl_priv *priv)
+{
+ struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd;
+
+ memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl,
+ sizeof(iwl_bt_prio_tbl));
+ if (iwl_dvm_send_cmd_pdu(priv,
+ REPLY_BT_COEX_PRIO_TABLE, 0,
+ sizeof(prio_tbl_cmd), &prio_tbl_cmd))
+ IWL_ERR(priv, "failed to send BT prio tbl command\n");
+}
+
+int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
+{
+ struct iwl_bt_coex_prot_env_cmd env_cmd;
+ int ret;
+
+ env_cmd.action = action;
+ env_cmd.type = type;
+ ret = iwl_dvm_send_cmd_pdu(priv,
+ REPLY_BT_COEX_PROT_ENV, 0,
+ sizeof(env_cmd), &env_cmd);
+ if (ret)
+ IWL_ERR(priv, "failed to send BT env command\n");
+ return ret;
+}
+
+static const u8 iwlagn_default_queue_to_tx_fifo[] = {
+ IWL_TX_FIFO_VO,
+ IWL_TX_FIFO_VI,
+ IWL_TX_FIFO_BE,
+ IWL_TX_FIFO_BK,
+};
+
+static const u8 iwlagn_ipan_queue_to_tx_fifo[] = {
+ IWL_TX_FIFO_VO,
+ IWL_TX_FIFO_VI,
+ IWL_TX_FIFO_BE,
+ IWL_TX_FIFO_BK,
+ IWL_TX_FIFO_BK_IPAN,
+ IWL_TX_FIFO_BE_IPAN,
+ IWL_TX_FIFO_VI_IPAN,
+ IWL_TX_FIFO_VO_IPAN,
+ IWL_TX_FIFO_BE_IPAN,
+ IWL_TX_FIFO_UNUSED,
+ IWL_TX_FIFO_AUX,
+};
+
+static int iwl_alive_notify(struct iwl_priv *priv)
+{
+ const u8 *queue_to_txf;
+ u8 n_queues;
+ int ret;
+ int i;
+
+ iwl_trans_fw_alive(priv->trans, 0);
+
+ if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN &&
+ priv->nvm_data->sku_cap_ipan_enable) {
+ n_queues = ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo);
+ queue_to_txf = iwlagn_ipan_queue_to_tx_fifo;
+ } else {
+ n_queues = ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo);
+ queue_to_txf = iwlagn_default_queue_to_tx_fifo;
+ }
+
+ for (i = 0; i < n_queues; i++)
+ if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED)
+ iwl_trans_ac_txq_enable(priv->trans, i,
+ queue_to_txf[i], 0);
+
+ priv->passive_no_rx = false;
+ priv->transport_queue_stop = 0;
+
+ ret = iwl_send_wimax_coex(priv);
+ if (ret)
+ return ret;
+
+ if (!priv->lib->no_xtal_calib) {
+ ret = iwl_set_Xtal_calib(priv);
+ if (ret)
+ return ret;
+ }
+
+ return iwl_send_calib_results(priv);
+}
+
+struct iwl_alive_data {
+ bool valid;
+ u8 subtype;
+};
+
+static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_priv *priv =
+ container_of(notif_wait, struct iwl_priv, notif_wait);
+ struct iwl_alive_data *alive_data = data;
+ struct iwl_alive_resp *palive;
+
+ palive = (void *)pkt->data;
+
+ IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision "
+ "0x%01X 0x%01X\n",
+ palive->is_valid, palive->ver_type,
+ palive->ver_subtype);
+
+ priv->device_pointers.error_event_table =
+ le32_to_cpu(palive->error_event_table_ptr);
+ priv->device_pointers.log_event_table =
+ le32_to_cpu(palive->log_event_table_ptr);
+
+ alive_data->subtype = palive->ver_subtype;
+ alive_data->valid = palive->is_valid == UCODE_VALID_OK;
+
+ return true;
+}
+
+#define UCODE_ALIVE_TIMEOUT HZ
+#define UCODE_CALIB_TIMEOUT (2*HZ)
+
+int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
+ enum iwl_ucode_type ucode_type)
+{
+ struct iwl_notification_wait alive_wait;
+ struct iwl_alive_data alive_data;
+ const struct fw_img *fw;
+ int ret;
+ enum iwl_ucode_type old_type;
+ static const u16 alive_cmd[] = { REPLY_ALIVE };
+
+ fw = iwl_get_ucode_image(priv, ucode_type);
+ if (WARN_ON(!fw))
+ return -EINVAL;
+
+ old_type = priv->cur_ucode;
+ priv->cur_ucode = ucode_type;
+ priv->ucode_loaded = false;
+
+ iwl_init_notification_wait(&priv->notif_wait, &alive_wait,
+ alive_cmd, ARRAY_SIZE(alive_cmd),
+ iwl_alive_fn, &alive_data);
+
+ ret = iwl_trans_start_fw(priv->trans, fw, false);
+ if (ret) {
+ priv->cur_ucode = old_type;
+ iwl_remove_notification(&priv->notif_wait, &alive_wait);
+ return ret;
+ }
+
+ /*
+ * Some things may run in the background now, but we
+ * just wait for the ALIVE notification here.
+ */
+ ret = iwl_wait_notification(&priv->notif_wait, &alive_wait,
+ UCODE_ALIVE_TIMEOUT);
+ if (ret) {
+ priv->cur_ucode = old_type;
+ return ret;
+ }
+
+ if (!alive_data.valid) {
+ IWL_ERR(priv, "Loaded ucode is not valid!\n");
+ priv->cur_ucode = old_type;
+ return -EIO;
+ }
+
+ priv->ucode_loaded = true;
+
+ if (ucode_type != IWL_UCODE_WOWLAN) {
+ /* delay a bit to give rfkill time to run */
+ msleep(5);
+ }
+
+ ret = iwl_alive_notify(priv);
+ if (ret) {
+ IWL_WARN(priv,
+ "Could not complete ALIVE transition: %d\n", ret);
+ priv->cur_ucode = old_type;
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_priv *priv = data;
+ struct iwl_calib_hdr *hdr;
+
+ if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) {
+ WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION);
+ return true;
+ }
+
+ hdr = (struct iwl_calib_hdr *)pkt->data;
+
+ if (iwl_calib_set(priv, hdr, iwl_rx_packet_payload_len(pkt)))
+ IWL_ERR(priv, "Failed to record calibration data %d\n",
+ hdr->op_code);
+
+ return false;
+}
+
+int iwl_run_init_ucode(struct iwl_priv *priv)
+{
+ struct iwl_notification_wait calib_wait;
+ static const u16 calib_complete[] = {
+ CALIBRATION_RES_NOTIFICATION,
+ CALIBRATION_COMPLETE_NOTIFICATION
+ };
+ int ret;
+
+ lockdep_assert_held(&priv->mutex);
+
+ /* No init ucode required? Curious, but maybe ok */
+ if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len)
+ return 0;
+
+ iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
+ calib_complete, ARRAY_SIZE(calib_complete),
+ iwlagn_wait_calib, priv);
+
+ /* Will also start the device */
+ ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
+ if (ret)
+ goto error;
+
+ ret = iwl_init_alive_start(priv);
+ if (ret)
+ goto error;
+
+ /*
+ * Some things may run in the background now, but we
+ * just wait for the calibration complete notification.
+ */
+ ret = iwl_wait_notification(&priv->notif_wait, &calib_wait,
+ UCODE_CALIB_TIMEOUT);
+
+ goto out;
+
+ error:
+ iwl_remove_notification(&priv->notif_wait, &calib_wait);
+ out:
+ /* Whatever happened, stop the device */
+ iwl_trans_stop_device(priv->trans);
+ priv->ucode_loaded = false;
+
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-1000.c b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c
new file mode 100644
index 000000000000..a90dbab6bbbe
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-csr.h"
+#include "iwl-agn-hw.h"
+
+/* Highest firmware API version supported */
+#define IWL1000_UCODE_API_MAX 5
+#define IWL100_UCODE_API_MAX 5
+
+/* Oldest version we won't warn about */
+#define IWL1000_UCODE_API_OK 5
+#define IWL100_UCODE_API_OK 5
+
+/* Lowest firmware API version supported */
+#define IWL1000_UCODE_API_MIN 1
+#define IWL100_UCODE_API_MIN 5
+
+/* EEPROM version */
+#define EEPROM_1000_TX_POWER_VERSION (4)
+#define EEPROM_1000_EEPROM_VERSION (0x15C)
+
+#define IWL1000_FW_PRE "iwlwifi-1000-"
+#define IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE __stringify(api) ".ucode"
+
+#define IWL100_FW_PRE "iwlwifi-100-"
+#define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE __stringify(api) ".ucode"
+
+
+static const struct iwl_base_params iwl1000_base_params = {
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .max_ll_items = OTP_MAX_LL_ITEMS_1000,
+ .shadow_ram_support = false,
+ .led_compensation = 51,
+ .wd_timeout = IWL_WATCHDOG_DISABLED,
+ .max_event_log_size = 128,
+ .scd_chain_ext_wa = true,
+};
+
+static const struct iwl_ht_params iwl1000_ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ),
+};
+
+static const struct iwl_eeprom_params iwl1000_eeprom_params = {
+ .regulatory_bands = {
+ EEPROM_REG_BAND_1_CHANNELS,
+ EEPROM_REG_BAND_2_CHANNELS,
+ EEPROM_REG_BAND_3_CHANNELS,
+ EEPROM_REG_BAND_4_CHANNELS,
+ EEPROM_REG_BAND_5_CHANNELS,
+ EEPROM_REG_BAND_24_HT40_CHANNELS,
+ EEPROM_REGULATORY_BAND_NO_HT40,
+ }
+};
+
+#define IWL_DEVICE_1000 \
+ .fw_name_pre = IWL1000_FW_PRE, \
+ .ucode_api_max = IWL1000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL1000_UCODE_API_OK, \
+ .ucode_api_min = IWL1000_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_1000, \
+ .max_inst_size = IWLAGN_RTC_INST_SIZE, \
+ .max_data_size = IWLAGN_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_1000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
+ .base_params = &iwl1000_base_params, \
+ .eeprom_params = &iwl1000_eeprom_params, \
+ .led_mode = IWL_LED_BLINK, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl1000_bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
+ IWL_DEVICE_1000,
+ .ht_params = &iwl1000_ht_params,
+};
+
+const struct iwl_cfg iwl1000_bg_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 1000 BG",
+ IWL_DEVICE_1000,
+};
+
+#define IWL_DEVICE_100 \
+ .fw_name_pre = IWL100_FW_PRE, \
+ .ucode_api_max = IWL100_UCODE_API_MAX, \
+ .ucode_api_ok = IWL100_UCODE_API_OK, \
+ .ucode_api_min = IWL100_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_100, \
+ .max_inst_size = IWLAGN_RTC_INST_SIZE, \
+ .max_data_size = IWLAGN_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_1000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
+ .base_params = &iwl1000_base_params, \
+ .eeprom_params = &iwl1000_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .rx_with_siso_diversity = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl100_bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 100 BGN",
+ IWL_DEVICE_100,
+ .ht_params = &iwl1000_ht_params,
+};
+
+const struct iwl_cfg iwl100_bg_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 100 BG",
+ IWL_DEVICE_100,
+};
+
+MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK));
+MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK));
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-2000.c b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c
new file mode 100644
index 000000000000..a6da9594c4a5
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c
@@ -0,0 +1,216 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+#include "dvm/commands.h" /* needed for BT for now */
+
+/* Highest firmware API version supported */
+#define IWL2030_UCODE_API_MAX 6
+#define IWL2000_UCODE_API_MAX 6
+#define IWL105_UCODE_API_MAX 6
+#define IWL135_UCODE_API_MAX 6
+
+/* Oldest version we won't warn about */
+#define IWL2030_UCODE_API_OK 6
+#define IWL2000_UCODE_API_OK 6
+#define IWL105_UCODE_API_OK 6
+#define IWL135_UCODE_API_OK 6
+
+/* Lowest firmware API version supported */
+#define IWL2030_UCODE_API_MIN 5
+#define IWL2000_UCODE_API_MIN 5
+#define IWL105_UCODE_API_MIN 5
+#define IWL135_UCODE_API_MIN 5
+
+/* EEPROM version */
+#define EEPROM_2000_TX_POWER_VERSION (6)
+#define EEPROM_2000_EEPROM_VERSION (0x805)
+
+
+#define IWL2030_FW_PRE "iwlwifi-2030-"
+#define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode"
+
+#define IWL2000_FW_PRE "iwlwifi-2000-"
+#define IWL2000_MODULE_FIRMWARE(api) IWL2000_FW_PRE __stringify(api) ".ucode"
+
+#define IWL105_FW_PRE "iwlwifi-105-"
+#define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode"
+
+#define IWL135_FW_PRE "iwlwifi-135-"
+#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode"
+
+static const struct iwl_base_params iwl2000_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .pll_cfg_val = 0,
+ .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
+ .shadow_ram_support = true,
+ .led_compensation = 51,
+ .wd_timeout = IWL_DEF_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+ .scd_chain_ext_wa = true,
+};
+
+
+static const struct iwl_base_params iwl2030_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .pll_cfg_val = 0,
+ .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
+ .shadow_ram_support = true,
+ .led_compensation = 57,
+ .wd_timeout = IWL_LONG_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+ .scd_chain_ext_wa = true,
+};
+
+static const struct iwl_ht_params iwl2000_ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ),
+};
+
+static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
+ .regulatory_bands = {
+ EEPROM_REG_BAND_1_CHANNELS,
+ EEPROM_REG_BAND_2_CHANNELS,
+ EEPROM_REG_BAND_3_CHANNELS,
+ EEPROM_REG_BAND_4_CHANNELS,
+ EEPROM_REG_BAND_5_CHANNELS,
+ EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
+ EEPROM_REGULATORY_BAND_NO_HT40,
+ },
+ .enhanced_txpower = true,
+};
+
+#define IWL_DEVICE_2000 \
+ .fw_name_pre = IWL2000_FW_PRE, \
+ .ucode_api_max = IWL2000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL2000_UCODE_API_OK, \
+ .ucode_api_min = IWL2000_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_2000, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
+ .base_params = &iwl2000_base_params, \
+ .eeprom_params = &iwl20x0_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+
+const struct iwl_cfg iwl2000_2bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN",
+ IWL_DEVICE_2000,
+ .ht_params = &iwl2000_ht_params,
+};
+
+const struct iwl_cfg iwl2000_2bgn_d_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN",
+ IWL_DEVICE_2000,
+ .ht_params = &iwl2000_ht_params,
+};
+
+#define IWL_DEVICE_2030 \
+ .fw_name_pre = IWL2030_FW_PRE, \
+ .ucode_api_max = IWL2030_UCODE_API_MAX, \
+ .ucode_api_ok = IWL2030_UCODE_API_OK, \
+ .ucode_api_min = IWL2030_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_2030, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
+ .base_params = &iwl2030_base_params, \
+ .eeprom_params = &iwl20x0_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl2030_2bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
+ IWL_DEVICE_2030,
+ .ht_params = &iwl2000_ht_params,
+};
+
+#define IWL_DEVICE_105 \
+ .fw_name_pre = IWL105_FW_PRE, \
+ .ucode_api_max = IWL105_UCODE_API_MAX, \
+ .ucode_api_ok = IWL105_UCODE_API_OK, \
+ .ucode_api_min = IWL105_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_105, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
+ .base_params = &iwl2000_base_params, \
+ .eeprom_params = &iwl20x0_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .rx_with_siso_diversity = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl105_bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 105 BGN",
+ IWL_DEVICE_105,
+ .ht_params = &iwl2000_ht_params,
+};
+
+const struct iwl_cfg iwl105_bgn_d_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 105D BGN",
+ IWL_DEVICE_105,
+ .ht_params = &iwl2000_ht_params,
+};
+
+#define IWL_DEVICE_135 \
+ .fw_name_pre = IWL135_FW_PRE, \
+ .ucode_api_max = IWL135_UCODE_API_MAX, \
+ .ucode_api_ok = IWL135_UCODE_API_OK, \
+ .ucode_api_min = IWL135_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_135, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
+ .base_params = &iwl2030_base_params, \
+ .eeprom_params = &iwl20x0_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .rx_with_siso_diversity = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl135_bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 135 BGN",
+ IWL_DEVICE_135,
+ .ht_params = &iwl2000_ht_params,
+};
+
+MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK));
+MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK));
+MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK));
+MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK));
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-5000.c b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c
new file mode 100644
index 000000000000..8b5afdef2d83
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c
@@ -0,0 +1,178 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+#include "iwl-csr.h"
+
+/* Highest firmware API version supported */
+#define IWL5000_UCODE_API_MAX 5
+#define IWL5150_UCODE_API_MAX 2
+
+/* Oldest version we won't warn about */
+#define IWL5000_UCODE_API_OK 5
+#define IWL5150_UCODE_API_OK 2
+
+/* Lowest firmware API version supported */
+#define IWL5000_UCODE_API_MIN 1
+#define IWL5150_UCODE_API_MIN 1
+
+/* EEPROM versions */
+#define EEPROM_5000_TX_POWER_VERSION (4)
+#define EEPROM_5000_EEPROM_VERSION (0x11A)
+#define EEPROM_5050_TX_POWER_VERSION (4)
+#define EEPROM_5050_EEPROM_VERSION (0x21E)
+
+#define IWL5000_FW_PRE "iwlwifi-5000-"
+#define IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE __stringify(api) ".ucode"
+
+#define IWL5150_FW_PRE "iwlwifi-5150-"
+#define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE __stringify(api) ".ucode"
+
+static const struct iwl_base_params iwl5000_base_params = {
+ .eeprom_size = IWLAGN_EEPROM_IMG_SIZE,
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .led_compensation = 51,
+ .wd_timeout = IWL_WATCHDOG_DISABLED,
+ .max_event_log_size = 512,
+ .scd_chain_ext_wa = true,
+};
+
+static const struct iwl_ht_params iwl5000_ht_params = {
+ .ht_greenfield_support = true,
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+static const struct iwl_eeprom_params iwl5000_eeprom_params = {
+ .regulatory_bands = {
+ EEPROM_REG_BAND_1_CHANNELS,
+ EEPROM_REG_BAND_2_CHANNELS,
+ EEPROM_REG_BAND_3_CHANNELS,
+ EEPROM_REG_BAND_4_CHANNELS,
+ EEPROM_REG_BAND_5_CHANNELS,
+ EEPROM_REG_BAND_24_HT40_CHANNELS,
+ EEPROM_REG_BAND_52_HT40_CHANNELS
+ },
+};
+
+#define IWL_DEVICE_5000 \
+ .fw_name_pre = IWL5000_FW_PRE, \
+ .ucode_api_max = IWL5000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL5000_UCODE_API_OK, \
+ .ucode_api_min = IWL5000_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_5000, \
+ .max_inst_size = IWLAGN_RTC_INST_SIZE, \
+ .max_data_size = IWLAGN_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_5000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \
+ .base_params = &iwl5000_base_params, \
+ .eeprom_params = &iwl5000_eeprom_params, \
+ .led_mode = IWL_LED_BLINK, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl5300_agn_cfg = {
+ .name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
+ IWL_DEVICE_5000,
+ /* at least EEPROM 0x11A has wrong info */
+ .valid_tx_ant = ANT_ABC, /* .cfg overwrite */
+ .valid_rx_ant = ANT_ABC, /* .cfg overwrite */
+ .ht_params = &iwl5000_ht_params,
+};
+
+const struct iwl_cfg iwl5100_bgn_cfg = {
+ .name = "Intel(R) WiFi Link 5100 BGN",
+ IWL_DEVICE_5000,
+ .valid_tx_ant = ANT_B, /* .cfg overwrite */
+ .valid_rx_ant = ANT_AB, /* .cfg overwrite */
+ .ht_params = &iwl5000_ht_params,
+};
+
+const struct iwl_cfg iwl5100_abg_cfg = {
+ .name = "Intel(R) WiFi Link 5100 ABG",
+ IWL_DEVICE_5000,
+ .valid_tx_ant = ANT_B, /* .cfg overwrite */
+ .valid_rx_ant = ANT_AB, /* .cfg overwrite */
+};
+
+const struct iwl_cfg iwl5100_agn_cfg = {
+ .name = "Intel(R) WiFi Link 5100 AGN",
+ IWL_DEVICE_5000,
+ .valid_tx_ant = ANT_B, /* .cfg overwrite */
+ .valid_rx_ant = ANT_AB, /* .cfg overwrite */
+ .ht_params = &iwl5000_ht_params,
+};
+
+const struct iwl_cfg iwl5350_agn_cfg = {
+ .name = "Intel(R) WiMAX/WiFi Link 5350 AGN",
+ .fw_name_pre = IWL5000_FW_PRE,
+ .ucode_api_max = IWL5000_UCODE_API_MAX,
+ .ucode_api_ok = IWL5000_UCODE_API_OK,
+ .ucode_api_min = IWL5000_UCODE_API_MIN,
+ .device_family = IWL_DEVICE_FAMILY_5000,
+ .max_inst_size = IWLAGN_RTC_INST_SIZE,
+ .max_data_size = IWLAGN_RTC_DATA_SIZE,
+ .nvm_ver = EEPROM_5050_EEPROM_VERSION,
+ .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION,
+ .base_params = &iwl5000_base_params,
+ .eeprom_params = &iwl5000_eeprom_params,
+ .ht_params = &iwl5000_ht_params,
+ .led_mode = IWL_LED_BLINK,
+ .internal_wimax_coex = true,
+};
+
+#define IWL_DEVICE_5150 \
+ .fw_name_pre = IWL5150_FW_PRE, \
+ .ucode_api_max = IWL5150_UCODE_API_MAX, \
+ .ucode_api_ok = IWL5150_UCODE_API_OK, \
+ .ucode_api_min = IWL5150_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_5150, \
+ .max_inst_size = IWLAGN_RTC_INST_SIZE, \
+ .max_data_size = IWLAGN_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_5050_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \
+ .base_params = &iwl5000_base_params, \
+ .eeprom_params = &iwl5000_eeprom_params, \
+ .led_mode = IWL_LED_BLINK, \
+ .internal_wimax_coex = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl5150_agn_cfg = {
+ .name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
+ IWL_DEVICE_5150,
+ .ht_params = &iwl5000_ht_params,
+
+};
+
+const struct iwl_cfg iwl5150_abg_cfg = {
+ .name = "Intel(R) WiMAX/WiFi Link 5150 ABG",
+ IWL_DEVICE_5150,
+};
+
+MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK));
+MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK));
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-6000.c b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c
new file mode 100644
index 000000000000..0b4ba781b631
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c
@@ -0,0 +1,389 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+#include "dvm/commands.h" /* needed for BT for now */
+
+/* Highest firmware API version supported */
+#define IWL6000_UCODE_API_MAX 6
+#define IWL6050_UCODE_API_MAX 5
+#define IWL6000G2_UCODE_API_MAX 6
+#define IWL6035_UCODE_API_MAX 6
+
+/* Oldest version we won't warn about */
+#define IWL6000_UCODE_API_OK 4
+#define IWL6000G2_UCODE_API_OK 5
+#define IWL6050_UCODE_API_OK 5
+#define IWL6000G2B_UCODE_API_OK 6
+#define IWL6035_UCODE_API_OK 6
+
+/* Lowest firmware API version supported */
+#define IWL6000_UCODE_API_MIN 4
+#define IWL6050_UCODE_API_MIN 4
+#define IWL6000G2_UCODE_API_MIN 5
+#define IWL6035_UCODE_API_MIN 6
+
+/* EEPROM versions */
+#define EEPROM_6000_TX_POWER_VERSION (4)
+#define EEPROM_6000_EEPROM_VERSION (0x423)
+#define EEPROM_6050_TX_POWER_VERSION (4)
+#define EEPROM_6050_EEPROM_VERSION (0x532)
+#define EEPROM_6150_TX_POWER_VERSION (6)
+#define EEPROM_6150_EEPROM_VERSION (0x553)
+#define EEPROM_6005_TX_POWER_VERSION (6)
+#define EEPROM_6005_EEPROM_VERSION (0x709)
+#define EEPROM_6030_TX_POWER_VERSION (6)
+#define EEPROM_6030_EEPROM_VERSION (0x709)
+#define EEPROM_6035_TX_POWER_VERSION (6)
+#define EEPROM_6035_EEPROM_VERSION (0x753)
+
+#define IWL6000_FW_PRE "iwlwifi-6000-"
+#define IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE __stringify(api) ".ucode"
+
+#define IWL6050_FW_PRE "iwlwifi-6050-"
+#define IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE __stringify(api) ".ucode"
+
+#define IWL6005_FW_PRE "iwlwifi-6000g2a-"
+#define IWL6005_MODULE_FIRMWARE(api) IWL6005_FW_PRE __stringify(api) ".ucode"
+
+#define IWL6030_FW_PRE "iwlwifi-6000g2b-"
+#define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode"
+
+static const struct iwl_base_params iwl6000_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .pll_cfg_val = 0,
+ .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
+ .shadow_ram_support = true,
+ .led_compensation = 51,
+ .wd_timeout = IWL_DEF_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+ .scd_chain_ext_wa = true,
+};
+
+static const struct iwl_base_params iwl6050_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .pll_cfg_val = 0,
+ .max_ll_items = OTP_MAX_LL_ITEMS_6x50,
+ .shadow_ram_support = true,
+ .led_compensation = 51,
+ .wd_timeout = IWL_DEF_WD_TIMEOUT,
+ .max_event_log_size = 1024,
+ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+ .scd_chain_ext_wa = true,
+};
+
+static const struct iwl_base_params iwl6000_g2_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .pll_cfg_val = 0,
+ .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
+ .shadow_ram_support = true,
+ .led_compensation = 57,
+ .wd_timeout = IWL_LONG_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+ .scd_chain_ext_wa = true,
+};
+
+static const struct iwl_ht_params iwl6000_ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+static const struct iwl_eeprom_params iwl6000_eeprom_params = {
+ .regulatory_bands = {
+ EEPROM_REG_BAND_1_CHANNELS,
+ EEPROM_REG_BAND_2_CHANNELS,
+ EEPROM_REG_BAND_3_CHANNELS,
+ EEPROM_REG_BAND_4_CHANNELS,
+ EEPROM_REG_BAND_5_CHANNELS,
+ EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
+ EEPROM_REG_BAND_52_HT40_CHANNELS
+ },
+ .enhanced_txpower = true,
+};
+
+#define IWL_DEVICE_6005 \
+ .fw_name_pre = IWL6005_FW_PRE, \
+ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \
+ .ucode_api_ok = IWL6000G2_UCODE_API_OK, \
+ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_6005, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_6005_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
+ .base_params = &iwl6000_g2_base_params, \
+ .eeprom_params = &iwl6000_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl6005_2agn_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN",
+ IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6005_2abg_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG",
+ IWL_DEVICE_6005,
+};
+
+const struct iwl_cfg iwl6005_2bg_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6205 BG",
+ IWL_DEVICE_6005,
+};
+
+const struct iwl_cfg iwl6005_2agn_sff_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN",
+ IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6005_2agn_d_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN",
+ IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6005_2agn_mow1_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN",
+ IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN",
+ IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
+};
+
+#define IWL_DEVICE_6030 \
+ .fw_name_pre = IWL6030_FW_PRE, \
+ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \
+ .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \
+ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_6030, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_6030_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
+ .base_params = &iwl6000_g2_base_params, \
+ .eeprom_params = &iwl6000_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl6030_2agn_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
+ IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6030_2abg_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG",
+ IWL_DEVICE_6030,
+};
+
+const struct iwl_cfg iwl6030_2bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN",
+ IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6030_2bg_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6230 BG",
+ IWL_DEVICE_6030,
+};
+
+#define IWL_DEVICE_6035 \
+ .fw_name_pre = IWL6030_FW_PRE, \
+ .ucode_api_max = IWL6035_UCODE_API_MAX, \
+ .ucode_api_ok = IWL6035_UCODE_API_OK, \
+ .ucode_api_min = IWL6035_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_6030, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_6030_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
+ .base_params = &iwl6000_g2_base_params, \
+ .eeprom_params = &iwl6000_eeprom_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl6035_2agn_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
+ IWL_DEVICE_6035,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6035_2agn_sff_cfg = {
+ .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN",
+ IWL_DEVICE_6035,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl1030_bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN",
+ IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl1030_bg_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 1030 BG",
+ IWL_DEVICE_6030,
+};
+
+const struct iwl_cfg iwl130_bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 130 BGN",
+ IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
+ .rx_with_siso_diversity = true,
+};
+
+const struct iwl_cfg iwl130_bg_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N 130 BG",
+ IWL_DEVICE_6030,
+ .rx_with_siso_diversity = true,
+};
+
+/*
+ * "i": Internal configuration, use internal Power Amplifier
+ */
+#define IWL_DEVICE_6000i \
+ .fw_name_pre = IWL6000_FW_PRE, \
+ .ucode_api_max = IWL6000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL6000_UCODE_API_OK, \
+ .ucode_api_min = IWL6000_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_6000i, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \
+ .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \
+ .nvm_ver = EEPROM_6000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \
+ .base_params = &iwl6000_base_params, \
+ .eeprom_params = &iwl6000_eeprom_params, \
+ .led_mode = IWL_LED_BLINK, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl6000i_2agn_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
+ IWL_DEVICE_6000i,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6000i_2abg_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG",
+ IWL_DEVICE_6000i,
+};
+
+const struct iwl_cfg iwl6000i_2bg_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N 6200 BG",
+ IWL_DEVICE_6000i,
+};
+
+#define IWL_DEVICE_6050 \
+ .fw_name_pre = IWL6050_FW_PRE, \
+ .ucode_api_max = IWL6050_UCODE_API_MAX, \
+ .ucode_api_min = IWL6050_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_6050, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \
+ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \
+ .nvm_ver = EEPROM_6050_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_6050_TX_POWER_VERSION, \
+ .base_params = &iwl6050_base_params, \
+ .eeprom_params = &iwl6000_eeprom_params, \
+ .led_mode = IWL_LED_BLINK, \
+ .internal_wimax_coex = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl6050_2agn_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
+ IWL_DEVICE_6050,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6050_2abg_cfg = {
+ .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG",
+ IWL_DEVICE_6050,
+};
+
+#define IWL_DEVICE_6150 \
+ .fw_name_pre = IWL6050_FW_PRE, \
+ .ucode_api_max = IWL6050_UCODE_API_MAX, \
+ .ucode_api_min = IWL6050_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_6150, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .nvm_ver = EEPROM_6150_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_6150_TX_POWER_VERSION, \
+ .base_params = &iwl6050_base_params, \
+ .eeprom_params = &iwl6000_eeprom_params, \
+ .led_mode = IWL_LED_BLINK, \
+ .internal_wimax_coex = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
+const struct iwl_cfg iwl6150_bgn_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN",
+ IWL_DEVICE_6150,
+ .ht_params = &iwl6000_ht_params,
+};
+
+const struct iwl_cfg iwl6150_bg_cfg = {
+ .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG",
+ IWL_DEVICE_6150,
+};
+
+const struct iwl_cfg iwl6000_3agn_cfg = {
+ .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN",
+ .fw_name_pre = IWL6000_FW_PRE,
+ .ucode_api_max = IWL6000_UCODE_API_MAX,
+ .ucode_api_ok = IWL6000_UCODE_API_OK,
+ .ucode_api_min = IWL6000_UCODE_API_MIN,
+ .device_family = IWL_DEVICE_FAMILY_6000,
+ .max_inst_size = IWL60_RTC_INST_SIZE,
+ .max_data_size = IWL60_RTC_DATA_SIZE,
+ .nvm_ver = EEPROM_6000_EEPROM_VERSION,
+ .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION,
+ .base_params = &iwl6000_base_params,
+ .eeprom_params = &iwl6000_eeprom_params,
+ .ht_params = &iwl6000_ht_params,
+ .led_mode = IWL_LED_BLINK,
+};
+
+MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK));
+MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK));
+MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK));
+MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK));
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
new file mode 100644
index 000000000000..e60cf141ed79
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
@@ -0,0 +1,380 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+
+/* Highest firmware API version supported */
+#define IWL7260_UCODE_API_MAX 17
+#define IWL7265_UCODE_API_MAX 17
+#define IWL7265D_UCODE_API_MAX 20
+
+/* Oldest version we won't warn about */
+#define IWL7260_UCODE_API_OK 13
+#define IWL7265_UCODE_API_OK 13
+#define IWL7265D_UCODE_API_OK 13
+
+/* Lowest firmware API version supported */
+#define IWL7260_UCODE_API_MIN 13
+#define IWL7265_UCODE_API_MIN 13
+#define IWL7265D_UCODE_API_MIN 13
+
+/* NVM versions */
+#define IWL7260_NVM_VERSION 0x0a1d
+#define IWL7260_TX_POWER_VERSION 0xffff /* meaningless */
+#define IWL3160_NVM_VERSION 0x709
+#define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */
+#define IWL3165_NVM_VERSION 0x709
+#define IWL3165_TX_POWER_VERSION 0xffff /* meaningless */
+#define IWL7265_NVM_VERSION 0x0a1d
+#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */
+#define IWL7265D_NVM_VERSION 0x0c11
+#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */
+
+/* DCCM offsets and lengths */
+#define IWL7000_DCCM_OFFSET 0x800000
+#define IWL7260_DCCM_LEN 0x14000
+#define IWL3160_DCCM_LEN 0x10000
+#define IWL7265_DCCM_LEN 0x17A00
+
+#define IWL7260_FW_PRE "iwlwifi-7260-"
+#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
+
+#define IWL3160_FW_PRE "iwlwifi-3160-"
+#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode"
+
+#define IWL7265_FW_PRE "iwlwifi-7265-"
+#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+
+#define IWL7265D_FW_PRE "iwlwifi-7265D-"
+#define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE __stringify(api) ".ucode"
+
+#define NVM_HW_SECTION_NUM_FAMILY_7000 0
+
+static const struct iwl_base_params iwl7000_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
+ .num_of_queues = 31,
+ .pll_cfg_val = 0,
+ .shadow_ram_support = true,
+ .led_compensation = 57,
+ .wd_timeout = IWL_LONG_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = true,
+ .pcie_l1_allowed = true,
+ .apmg_wake_up_wa = true,
+};
+
+static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
+ .ct_kill_entry = 118,
+ .ct_kill_exit = 96,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 114,
+ .dynamic_smps_exit = 110,
+ .tx_protection_entry = 114,
+ .tx_protection_exit = 108,
+ .tx_backoff = {
+ {.temperature = 112, .backoff = 300},
+ {.temperature = 113, .backoff = 800},
+ {.temperature = 114, .backoff = 1500},
+ {.temperature = 115, .backoff = 3000},
+ {.temperature = 116, .backoff = 5000},
+ {.temperature = 117, .backoff = 10000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
+static const struct iwl_ht_params iwl7000_ht_params = {
+ .stbc = true,
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+#define IWL_DEVICE_7000_COMMON \
+ .device_family = IWL_DEVICE_FAMILY_7000, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .base_params = &iwl7000_base_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \
+ .non_shared_ant = ANT_A, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \
+ .dccm_offset = IWL7000_DCCM_OFFSET
+
+#define IWL_DEVICE_7000 \
+ IWL_DEVICE_7000_COMMON, \
+ .ucode_api_max = IWL7260_UCODE_API_MAX, \
+ .ucode_api_ok = IWL7260_UCODE_API_OK, \
+ .ucode_api_min = IWL7260_UCODE_API_MIN
+
+#define IWL_DEVICE_7005 \
+ IWL_DEVICE_7000_COMMON, \
+ .ucode_api_max = IWL7265_UCODE_API_MAX, \
+ .ucode_api_ok = IWL7265_UCODE_API_OK, \
+ .ucode_api_min = IWL7265_UCODE_API_MIN
+
+#define IWL_DEVICE_7005D \
+ IWL_DEVICE_7000_COMMON, \
+ .ucode_api_max = IWL7265D_UCODE_API_MAX, \
+ .ucode_api_ok = IWL7265D_UCODE_API_OK, \
+ .ucode_api_min = IWL7265D_UCODE_API_MIN
+
+const struct iwl_cfg iwl7260_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+ .host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
+ .dccm_len = IWL7260_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
+ .name = "Intel(R) Dual Band Wireless AC 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+ .high_temp = true,
+ .host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
+ .dccm_len = IWL7260_DCCM_LEN,
+ .thermal_params = &iwl7000_high_temp_tt_params,
+};
+
+const struct iwl_cfg iwl7260_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+ .host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
+ .dccm_len = IWL7260_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7260_n_cfg = {
+ .name = "Intel(R) Wireless N 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+ .host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
+ .dccm_len = IWL7260_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl3160_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 3160",
+ .fw_name_pre = IWL3160_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3160_NVM_VERSION,
+ .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+ .host_interrupt_operation_mode = true,
+ .dccm_len = IWL3160_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl3160_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 3160",
+ .fw_name_pre = IWL3160_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3160_NVM_VERSION,
+ .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+ .host_interrupt_operation_mode = true,
+ .dccm_len = IWL3160_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl3160_n_cfg = {
+ .name = "Intel(R) Wireless N 3160",
+ .fw_name_pre = IWL3160_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3160_NVM_VERSION,
+ .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+ .host_interrupt_operation_mode = true,
+ .dccm_len = IWL3160_DCCM_LEN,
+};
+
+static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = {
+ {.pwr = 1600, .backoff = 0},
+ {.pwr = 1300, .backoff = 467},
+ {.pwr = 900, .backoff = 1900},
+ {.pwr = 800, .backoff = 2630},
+ {.pwr = 700, .backoff = 3720},
+ {.pwr = 600, .backoff = 5550},
+ {.pwr = 500, .backoff = 9350},
+ {0},
+};
+
+static const struct iwl_ht_params iwl7265_ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+const struct iwl_cfg iwl3165_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 3165",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7005D,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3165_NVM_VERSION,
+ .nvm_calib_ver = IWL3165_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl3168_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 3168",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3165_NVM_VERSION,
+ .nvm_calib_ver = IWL3165_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7265_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 7265",
+ .fw_name_pre = IWL7265_FW_PRE,
+ IWL_DEVICE_7005,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7265_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 7265",
+ .fw_name_pre = IWL7265_FW_PRE,
+ IWL_DEVICE_7005,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7265_n_cfg = {
+ .name = "Intel(R) Wireless N 7265",
+ .fw_name_pre = IWL7265_FW_PRE,
+ IWL_DEVICE_7005,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7265d_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 7265",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7005D,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265D_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7265d_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 7265",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7005D,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265D_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl7265d_n_cfg = {
+ .name = "Intel(R) Wireless N 7265",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7005D,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265D_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7265_UCODE_API_OK));
+MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7265D_UCODE_API_OK));
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
new file mode 100644
index 000000000000..c84a0299d43e
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
@@ -0,0 +1,238 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+
+/* Highest firmware API version supported */
+#define IWL8000_UCODE_API_MAX 20
+
+/* Oldest version we won't warn about */
+#define IWL8000_UCODE_API_OK 13
+
+/* Lowest firmware API version supported */
+#define IWL8000_UCODE_API_MIN 13
+
+/* NVM versions */
+#define IWL8000_NVM_VERSION 0x0a1d
+#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */
+
+/* Memory offsets and lengths */
+#define IWL8260_DCCM_OFFSET 0x800000
+#define IWL8260_DCCM_LEN 0x18000
+#define IWL8260_DCCM2_OFFSET 0x880000
+#define IWL8260_DCCM2_LEN 0x8000
+#define IWL8260_SMEM_OFFSET 0x400000
+#define IWL8260_SMEM_LEN 0x68000
+
+#define IWL8000_FW_PRE "iwlwifi-8000"
+#define IWL8000_MODULE_FIRMWARE(api) \
+ IWL8000_FW_PRE "-" __stringify(api) ".ucode"
+
+#define NVM_HW_SECTION_NUM_FAMILY_8000 10
+#define DEFAULT_NVM_FILE_FAMILY_8000B "nvmData-8000B"
+#define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C"
+
+/* Max SDIO RX/TX aggregation sizes of the ADDBA request/response */
+#define MAX_RX_AGG_SIZE_8260_SDIO 21
+#define MAX_TX_AGG_SIZE_8260_SDIO 40
+
+/* Max A-MPDU exponent for HT and VHT */
+#define MAX_HT_AMPDU_EXPONENT_8260_SDIO IEEE80211_HT_MAX_AMPDU_32K
+#define MAX_VHT_AMPDU_EXPONENT_8260_SDIO IEEE80211_VHT_MAX_AMPDU_32K
+
+static const struct iwl_base_params iwl8000_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
+ .num_of_queues = 31,
+ .pll_cfg_val = 0,
+ .shadow_ram_support = true,
+ .led_compensation = 57,
+ .wd_timeout = IWL_LONG_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = true,
+ .pcie_l1_allowed = true,
+};
+
+static const struct iwl_ht_params iwl8000_ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+static const struct iwl_tt_params iwl8000_tt_params = {
+ .ct_kill_entry = 115,
+ .ct_kill_exit = 93,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 111,
+ .dynamic_smps_exit = 107,
+ .tx_protection_entry = 112,
+ .tx_protection_exit = 105,
+ .tx_backoff = {
+ {.temperature = 110, .backoff = 200},
+ {.temperature = 111, .backoff = 600},
+ {.temperature = 112, .backoff = 1200},
+ {.temperature = 113, .backoff = 2000},
+ {.temperature = 114, .backoff = 4000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
+#define IWL_DEVICE_8000 \
+ .ucode_api_max = IWL8000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL8000_UCODE_API_OK, \
+ .ucode_api_min = IWL8000_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_8000, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .base_params = &iwl8000_base_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \
+ .features = NETIF_F_RXCSUM, \
+ .non_shared_ant = ANT_A, \
+ .dccm_offset = IWL8260_DCCM_OFFSET, \
+ .dccm_len = IWL8260_DCCM_LEN, \
+ .dccm2_offset = IWL8260_DCCM2_OFFSET, \
+ .dccm2_len = IWL8260_DCCM2_LEN, \
+ .smem_offset = IWL8260_SMEM_OFFSET, \
+ .smem_len = IWL8260_SMEM_LEN, \
+ .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B, \
+ .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C, \
+ .thermal_params = &iwl8000_tt_params, \
+ .apmg_not_supported = true
+
+const struct iwl_cfg iwl8260_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 8260",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl8260_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 8260",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
+const struct iwl_cfg iwl8265_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 8265",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
+const struct iwl_cfg iwl4165_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 4165",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
+const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
+ .name = "Intel(R) Dual Band Wireless-AC 8260",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
+ .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO,
+ .disable_dummy_notification = true,
+ .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+ .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
+};
+
+const struct iwl_cfg iwl4165_2ac_sdio_cfg = {
+ .name = "Intel(R) Dual Band Wireless-AC 4165",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
+ .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO,
+ .bt_shared_single_ant = true,
+ .disable_dummy_notification = true,
+ .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+ .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
+};
+
+MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c
new file mode 100644
index 000000000000..ecbf4822cd69
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c
@@ -0,0 +1,163 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+
+/* Highest firmware API version supported */
+#define IWL9000_UCODE_API_MAX 20
+
+/* Oldest version we won't warn about */
+#define IWL9000_UCODE_API_OK 13
+
+/* Lowest firmware API version supported */
+#define IWL9000_UCODE_API_MIN 13
+
+/* NVM versions */
+#define IWL9000_NVM_VERSION 0x0a1d
+#define IWL9000_TX_POWER_VERSION 0xffff /* meaningless */
+
+/* Memory offsets and lengths */
+#define IWL9000_DCCM_OFFSET 0x800000
+#define IWL9000_DCCM_LEN 0x18000
+#define IWL9000_DCCM2_OFFSET 0x880000
+#define IWL9000_DCCM2_LEN 0x8000
+#define IWL9000_SMEM_OFFSET 0x400000
+#define IWL9000_SMEM_LEN 0x68000
+
+#define IWL9000_FW_PRE "iwlwifi-9000-"
+#define IWL9000_MODULE_FIRMWARE(api) \
+ IWL9000_FW_PRE "-" __stringify(api) ".ucode"
+
+#define NVM_HW_SECTION_NUM_FAMILY_9000 10
+
+static const struct iwl_base_params iwl9000_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_9000,
+ .num_of_queues = 31,
+ .pll_cfg_val = 0,
+ .shadow_ram_support = true,
+ .led_compensation = 57,
+ .wd_timeout = IWL_LONG_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = true,
+ .pcie_l1_allowed = true,
+};
+
+static const struct iwl_ht_params iwl9000_ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+static const struct iwl_tt_params iwl9000_tt_params = {
+ .ct_kill_entry = 115,
+ .ct_kill_exit = 93,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 111,
+ .dynamic_smps_exit = 107,
+ .tx_protection_entry = 112,
+ .tx_protection_exit = 105,
+ .tx_backoff = {
+ {.temperature = 110, .backoff = 200},
+ {.temperature = 111, .backoff = 600},
+ {.temperature = 112, .backoff = 1200},
+ {.temperature = 113, .backoff = 2000},
+ {.temperature = 114, .backoff = 4000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
+#define IWL_DEVICE_9000 \
+ .ucode_api_max = IWL9000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL9000_UCODE_API_OK, \
+ .ucode_api_min = IWL9000_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_8000, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .base_params = &iwl9000_base_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_9000, \
+ .non_shared_ant = ANT_A, \
+ .dccm_offset = IWL9000_DCCM_OFFSET, \
+ .dccm_len = IWL9000_DCCM_LEN, \
+ .dccm2_offset = IWL9000_DCCM2_OFFSET, \
+ .dccm2_len = IWL9000_DCCM2_LEN, \
+ .smem_offset = IWL9000_SMEM_OFFSET, \
+ .smem_len = IWL9000_SMEM_LEN, \
+ .thermal_params = &iwl9000_tt_params, \
+ .apmg_not_supported = true
+
+const struct iwl_cfg iwl9260_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 9260",
+ .fw_name_pre = IWL9000_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
+const struct iwl_cfg iwl5165_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 5165",
+ .fw_name_pre = IWL9000_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
+MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_OK));
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h
new file mode 100644
index 000000000000..ee9347a54cdc
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h
@@ -0,0 +1,117 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+/*
+ * Please use this file (iwl-agn-hw.h) only for hardware-related definitions.
+ */
+
+#ifndef __iwl_agn_hw_h__
+#define __iwl_agn_hw_h__
+
+#define IWLAGN_RTC_INST_LOWER_BOUND (0x000000)
+#define IWLAGN_RTC_INST_UPPER_BOUND (0x020000)
+
+#define IWLAGN_RTC_DATA_LOWER_BOUND (0x800000)
+#define IWLAGN_RTC_DATA_UPPER_BOUND (0x80C000)
+
+#define IWLAGN_RTC_INST_SIZE (IWLAGN_RTC_INST_UPPER_BOUND - \
+ IWLAGN_RTC_INST_LOWER_BOUND)
+#define IWLAGN_RTC_DATA_SIZE (IWLAGN_RTC_DATA_UPPER_BOUND - \
+ IWLAGN_RTC_DATA_LOWER_BOUND)
+
+#define IWL60_RTC_INST_LOWER_BOUND (0x000000)
+#define IWL60_RTC_INST_UPPER_BOUND (0x040000)
+#define IWL60_RTC_DATA_LOWER_BOUND (0x800000)
+#define IWL60_RTC_DATA_UPPER_BOUND (0x814000)
+#define IWL60_RTC_INST_SIZE \
+ (IWL60_RTC_INST_UPPER_BOUND - IWL60_RTC_INST_LOWER_BOUND)
+#define IWL60_RTC_DATA_SIZE \
+ (IWL60_RTC_DATA_UPPER_BOUND - IWL60_RTC_DATA_LOWER_BOUND)
+
+/* RSSI to dBm */
+#define IWLAGN_RSSI_OFFSET 44
+
+#define IWLAGN_DEFAULT_TX_RETRY 15
+#define IWLAGN_MGMT_DFAULT_RETRY_LIMIT 3
+#define IWLAGN_RTS_DFAULT_RETRY_LIMIT 60
+#define IWLAGN_BAR_DFAULT_RETRY_LIMIT 60
+#define IWLAGN_LOW_RETRY_LIMIT 7
+
+/* Limit range of txpower output target to be between these values */
+#define IWLAGN_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */
+#define IWLAGN_TX_POWER_TARGET_POWER_MAX (16) /* 16 dBm */
+
+/* EEPROM */
+#define IWLAGN_EEPROM_IMG_SIZE 2048
+
+/* high blocks contain PAPD data */
+#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */
+#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */
+#define OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */
+#define OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */
+#define OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */
+#define OTP_MAX_LL_ITEMS_2x00 (4) /* OTP blocks for 2x00 */
+
+
+#define IWLAGN_NUM_QUEUES 20
+
+#endif /* __iwl_agn_hw_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
new file mode 100644
index 000000000000..f99048135fb9
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -0,0 +1,440 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __IWL_CONFIG_H__
+#define __IWL_CONFIG_H__
+
+#include <linux/types.h>
+#include <net/mac80211.h>
+
+
+enum iwl_device_family {
+ IWL_DEVICE_FAMILY_UNDEFINED,
+ IWL_DEVICE_FAMILY_1000,
+ IWL_DEVICE_FAMILY_100,
+ IWL_DEVICE_FAMILY_2000,
+ IWL_DEVICE_FAMILY_2030,
+ IWL_DEVICE_FAMILY_105,
+ IWL_DEVICE_FAMILY_135,
+ IWL_DEVICE_FAMILY_5000,
+ IWL_DEVICE_FAMILY_5150,
+ IWL_DEVICE_FAMILY_6000,
+ IWL_DEVICE_FAMILY_6000i,
+ IWL_DEVICE_FAMILY_6005,
+ IWL_DEVICE_FAMILY_6030,
+ IWL_DEVICE_FAMILY_6050,
+ IWL_DEVICE_FAMILY_6150,
+ IWL_DEVICE_FAMILY_7000,
+ IWL_DEVICE_FAMILY_8000,
+};
+
+static inline bool iwl_has_secure_boot(u32 hw_rev,
+ enum iwl_device_family family)
+{
+ /* return 1 only for family 8000 B0 */
+ if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC))
+ return true;
+
+ return false;
+}
+
+/*
+ * LED mode
+ * IWL_LED_DEFAULT: use device default
+ * IWL_LED_RF_STATE: turn LED on/off based on RF state
+ * LED ON = RF ON
+ * LED OFF = RF OFF
+ * IWL_LED_BLINK: adjust led blink rate based on blink table
+ * IWL_LED_DISABLE: led disabled
+ */
+enum iwl_led_mode {
+ IWL_LED_DEFAULT,
+ IWL_LED_RF_STATE,
+ IWL_LED_BLINK,
+ IWL_LED_DISABLE,
+};
+
+/*
+ * This is the threshold value of plcp error rate per 100mSecs. It is
+ * used to set and check for the validity of plcp_delta.
+ */
+#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN 1
+#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF 50
+#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF 100
+#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF 200
+#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX 255
+#define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE 0
+
+/* TX queue watchdog timeouts in mSecs */
+#define IWL_WATCHDOG_DISABLED 0
+#define IWL_DEF_WD_TIMEOUT 2500
+#define IWL_LONG_WD_TIMEOUT 10000
+#define IWL_MAX_WD_TIMEOUT 120000
+
+#define IWL_DEFAULT_MAX_TX_POWER 22
+
+/* Antenna presence definitions */
+#define ANT_NONE 0x0
+#define ANT_A BIT(0)
+#define ANT_B BIT(1)
+#define ANT_C BIT(2)
+#define ANT_AB (ANT_A | ANT_B)
+#define ANT_AC (ANT_A | ANT_C)
+#define ANT_BC (ANT_B | ANT_C)
+#define ANT_ABC (ANT_A | ANT_B | ANT_C)
+
+static inline u8 num_of_ant(u8 mask)
+{
+ return !!((mask) & ANT_A) +
+ !!((mask) & ANT_B) +
+ !!((mask) & ANT_C);
+}
+
+/*
+ * @max_ll_items: max number of OTP blocks
+ * @shadow_ram_support: shadow support for OTP memory
+ * @led_compensation: compensate on the led on/off time per HW according
+ * to the deviation to achieve the desired led frequency.
+ * The detail algorithm is described in iwl-led.c
+ * @wd_timeout: TX queues watchdog timeout
+ * @max_event_log_size: size of event log buffer size for ucode event logging
+ * @shadow_reg_enable: HW shadow register support
+ * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command
+ * is in flight. This is due to a HW bug in 7260, 3160 and 7265.
+ * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled.
+ */
+struct iwl_base_params {
+ int eeprom_size;
+ int num_of_queues; /* def: HW dependent */
+ /* for iwl_pcie_apm_init() */
+ u32 pll_cfg_val;
+
+ const u16 max_ll_items;
+ const bool shadow_ram_support;
+ u16 led_compensation;
+ unsigned int wd_timeout;
+ u32 max_event_log_size;
+ const bool shadow_reg_enable;
+ const bool pcie_l1_allowed;
+ const bool apmg_wake_up_wa;
+ const bool scd_chain_ext_wa;
+};
+
+/*
+ * @stbc: support Tx STBC and 1*SS Rx STBC
+ * @ldpc: support Tx/Rx with LDPC
+ * @use_rts_for_aggregation: use rts/cts protection for HT traffic
+ * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40
+ */
+struct iwl_ht_params {
+ enum ieee80211_smps_mode smps_mode;
+ const bool ht_greenfield_support; /* if used set to true */
+ const bool stbc;
+ const bool ldpc;
+ bool use_rts_for_aggregation;
+ u8 ht40_bands;
+};
+
+/*
+ * Tx-backoff threshold
+ * @temperature: The threshold in Celsius
+ * @backoff: The tx-backoff in uSec
+ */
+struct iwl_tt_tx_backoff {
+ s32 temperature;
+ u32 backoff;
+};
+
+#define TT_TX_BACKOFF_SIZE 6
+
+/**
+ * struct iwl_tt_params - thermal throttling parameters
+ * @ct_kill_entry: CT Kill entry threshold
+ * @ct_kill_exit: CT Kill exit threshold
+ * @ct_kill_duration: The time intervals (in uSec) in which the driver needs
+ * to checks whether to exit CT Kill.
+ * @dynamic_smps_entry: Dynamic SMPS entry threshold
+ * @dynamic_smps_exit: Dynamic SMPS exit threshold
+ * @tx_protection_entry: TX protection entry threshold
+ * @tx_protection_exit: TX protection exit threshold
+ * @tx_backoff: Array of thresholds for tx-backoff , in ascending order.
+ * @support_ct_kill: Support CT Kill?
+ * @support_dynamic_smps: Support dynamic SMPS?
+ * @support_tx_protection: Support tx protection?
+ * @support_tx_backoff: Support tx-backoff?
+ */
+struct iwl_tt_params {
+ u32 ct_kill_entry;
+ u32 ct_kill_exit;
+ u32 ct_kill_duration;
+ u32 dynamic_smps_entry;
+ u32 dynamic_smps_exit;
+ u32 tx_protection_entry;
+ u32 tx_protection_exit;
+ struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE];
+ bool support_ct_kill;
+ bool support_dynamic_smps;
+ bool support_tx_protection;
+ bool support_tx_backoff;
+};
+
+/*
+ * information on how to parse the EEPROM
+ */
+#define EEPROM_REG_BAND_1_CHANNELS 0x08
+#define EEPROM_REG_BAND_2_CHANNELS 0x26
+#define EEPROM_REG_BAND_3_CHANNELS 0x42
+#define EEPROM_REG_BAND_4_CHANNELS 0x5C
+#define EEPROM_REG_BAND_5_CHANNELS 0x74
+#define EEPROM_REG_BAND_24_HT40_CHANNELS 0x82
+#define EEPROM_REG_BAND_52_HT40_CHANNELS 0x92
+#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80
+#define EEPROM_REGULATORY_BAND_NO_HT40 0
+
+/* lower blocks contain EEPROM image and calibration data */
+#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_9000 OTP_LOW_IMAGE_SIZE_FAMILY_8000
+
+struct iwl_eeprom_params {
+ const u8 regulatory_bands[7];
+ bool enhanced_txpower;
+};
+
+/* Tx-backoff power threshold
+ * @pwr: The power limit in mw
+ * @backoff: The tx-backoff in uSec
+ */
+struct iwl_pwr_tx_backoff {
+ u32 pwr;
+ u32 backoff;
+};
+
+/**
+ * struct iwl_cfg
+ * @name: Official name of the device
+ * @fw_name_pre: Firmware filename prefix. The api version and extension
+ * (.ucode) will be added to filename before loading from disk. The
+ * filename is constructed as fw_name_pre<api>.ucode.
+ * @ucode_api_max: Highest version of uCode API supported by driver.
+ * @ucode_api_ok: oldest version of the uCode API that is OK to load
+ * without a warning, for use in transitions
+ * @ucode_api_min: Lowest version of uCode API supported by driver.
+ * @max_inst_size: The maximal length of the fw inst section
+ * @max_data_size: The maximal length of the fw data section
+ * @valid_tx_ant: valid transmit antenna
+ * @valid_rx_ant: valid receive antenna
+ * @non_shared_ant: the antenna that is for WiFi only
+ * @nvm_ver: NVM version
+ * @nvm_calib_ver: NVM calibration version
+ * @lib: pointer to the lib ops
+ * @base_params: pointer to basic parameters
+ * @ht_params: point to ht parameters
+ * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
+ * @rx_with_siso_diversity: 1x1 device with rx antenna diversity
+ * @internal_wimax_coex: internal wifi/wimax combo device
+ * @high_temp: Is this NIC is designated to be in high temperature.
+ * @host_interrupt_operation_mode: device needs host interrupt operation
+ * mode set
+ * @nvm_hw_section_num: the ID of the HW NVM section
+ * @features: hw features, any combination of feature_whitelist
+ * @pwr_tx_backoffs: translation table between power limits and backoffs
+ * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
+ * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response
+ * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the
+ * station can receive in HT
+ * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the
+ * station can receive in VHT
+ * @dccm_offset: offset from which DCCM begins
+ * @dccm_len: length of DCCM (including runtime stack CCM)
+ * @dccm2_offset: offset from which the second DCCM begins
+ * @dccm2_len: length of the second DCCM
+ * @smem_offset: offset from which the SMEM begins
+ * @smem_len: the length of SMEM
+ *
+ * We enable the driver to be backward compatible wrt. hardware features.
+ * API differences in uCode shouldn't be handled here but through TLVs
+ * and/or the uCode API version instead.
+ */
+struct iwl_cfg {
+ /* params specific to an individual device within a device family */
+ const char *name;
+ const char *fw_name_pre;
+ const unsigned int ucode_api_max;
+ const unsigned int ucode_api_ok;
+ const unsigned int ucode_api_min;
+ const enum iwl_device_family device_family;
+ const u32 max_data_size;
+ const u32 max_inst_size;
+ u8 valid_tx_ant;
+ u8 valid_rx_ant;
+ u8 non_shared_ant;
+ bool bt_shared_single_ant;
+ u16 nvm_ver;
+ u16 nvm_calib_ver;
+ /* params not likely to change within a device family */
+ const struct iwl_base_params *base_params;
+ /* params likely to change within a device family */
+ const struct iwl_ht_params *ht_params;
+ const struct iwl_eeprom_params *eeprom_params;
+ enum iwl_led_mode led_mode;
+ const bool rx_with_siso_diversity;
+ const bool internal_wimax_coex;
+ const bool host_interrupt_operation_mode;
+ bool high_temp;
+ u8 nvm_hw_section_num;
+ bool lp_xtal_workaround;
+ const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
+ bool no_power_up_nic_in_init;
+ const char *default_nvm_file_B_step;
+ const char *default_nvm_file_C_step;
+ netdev_features_t features;
+ unsigned int max_rx_agg_size;
+ bool disable_dummy_notification;
+ unsigned int max_tx_agg_size;
+ unsigned int max_ht_ampdu_exponent;
+ unsigned int max_vht_ampdu_exponent;
+ const u32 dccm_offset;
+ const u32 dccm_len;
+ const u32 dccm2_offset;
+ const u32 dccm2_len;
+ const u32 smem_offset;
+ const u32 smem_len;
+ const struct iwl_tt_params *thermal_params;
+ bool apmg_not_supported;
+};
+
+/*
+ * 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;
+extern const struct iwl_cfg iwl5100_bgn_cfg;
+extern const struct iwl_cfg iwl5100_abg_cfg;
+extern const struct iwl_cfg iwl5150_agn_cfg;
+extern const struct iwl_cfg iwl5150_abg_cfg;
+extern const struct iwl_cfg iwl6005_2agn_cfg;
+extern const struct iwl_cfg iwl6005_2abg_cfg;
+extern const struct iwl_cfg iwl6005_2bg_cfg;
+extern const struct iwl_cfg iwl6005_2agn_sff_cfg;
+extern const struct iwl_cfg iwl6005_2agn_d_cfg;
+extern const struct iwl_cfg iwl6005_2agn_mow1_cfg;
+extern const struct iwl_cfg iwl6005_2agn_mow2_cfg;
+extern const struct iwl_cfg iwl1030_bgn_cfg;
+extern const struct iwl_cfg iwl1030_bg_cfg;
+extern const struct iwl_cfg iwl6030_2agn_cfg;
+extern const struct iwl_cfg iwl6030_2abg_cfg;
+extern const struct iwl_cfg iwl6030_2bgn_cfg;
+extern const struct iwl_cfg iwl6030_2bg_cfg;
+extern const struct iwl_cfg iwl6000i_2agn_cfg;
+extern const struct iwl_cfg iwl6000i_2abg_cfg;
+extern const struct iwl_cfg iwl6000i_2bg_cfg;
+extern const struct iwl_cfg iwl6000_3agn_cfg;
+extern const struct iwl_cfg iwl6050_2agn_cfg;
+extern const struct iwl_cfg iwl6050_2abg_cfg;
+extern const struct iwl_cfg iwl6150_bgn_cfg;
+extern const struct iwl_cfg iwl6150_bg_cfg;
+extern const struct iwl_cfg iwl1000_bgn_cfg;
+extern const struct iwl_cfg iwl1000_bg_cfg;
+extern const struct iwl_cfg iwl100_bgn_cfg;
+extern const struct iwl_cfg iwl100_bg_cfg;
+extern const struct iwl_cfg iwl130_bgn_cfg;
+extern const struct iwl_cfg iwl130_bg_cfg;
+extern const struct iwl_cfg iwl2000_2bgn_cfg;
+extern const struct iwl_cfg iwl2000_2bgn_d_cfg;
+extern const struct iwl_cfg iwl2030_2bgn_cfg;
+extern const struct iwl_cfg iwl6035_2agn_cfg;
+extern const struct iwl_cfg iwl6035_2agn_sff_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_2ac_cfg_high_temp;
+extern const struct iwl_cfg iwl7260_2n_cfg;
+extern const struct iwl_cfg iwl7260_n_cfg;
+extern const struct iwl_cfg iwl3160_2ac_cfg;
+extern const struct iwl_cfg iwl3160_2n_cfg;
+extern const struct iwl_cfg iwl3160_n_cfg;
+extern const struct iwl_cfg iwl3165_2ac_cfg;
+extern const struct iwl_cfg iwl3168_2ac_cfg;
+extern const struct iwl_cfg iwl7265_2ac_cfg;
+extern const struct iwl_cfg iwl7265_2n_cfg;
+extern const struct iwl_cfg iwl7265_n_cfg;
+extern const struct iwl_cfg iwl7265d_2ac_cfg;
+extern const struct iwl_cfg iwl7265d_2n_cfg;
+extern const struct iwl_cfg iwl7265d_n_cfg;
+extern const struct iwl_cfg iwl8260_2n_cfg;
+extern const struct iwl_cfg iwl8260_2ac_cfg;
+extern const struct iwl_cfg iwl8265_2ac_cfg;
+extern const struct iwl_cfg iwl4165_2ac_cfg;
+extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl4165_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl9260_2ac_cfg;
+extern const struct iwl_cfg iwl5165_2ac_cfg;
+#endif /* CONFIG_IWLMVM */
+
+#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
new file mode 100644
index 000000000000..163b21bc20cb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
@@ -0,0 +1,552 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_csr_h__
+#define __iwl_csr_h__
+/*
+ * CSR (control and status registers)
+ *
+ * CSR registers are mapped directly into PCI bus space, and are accessible
+ * whenever platform supplies power to device, even when device is in
+ * low power states due to driver-invoked device resets
+ * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes.
+ *
+ * Use iwl_write32() and iwl_read32() family to access these registers;
+ * these provide simple PCI bus access, without waking up the MAC.
+ * Do not use iwl_write_direct32() family for these registers;
+ * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ.
+ * The MAC (uCode processor, etc.) does not need to be powered up for accessing
+ * the CSR registers.
+ *
+ * NOTE: Device does need to be awake in order to read this memory
+ * via CSR_EEPROM and CSR_OTP registers
+ */
+#define CSR_BASE (0x000)
+
+#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */
+#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
+#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */
+#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */
+#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/
+#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */
+#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
+#define CSR_GP_CNTRL (CSR_BASE+0x024)
+
+/* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */
+#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005)
+
+/*
+ * Hardware revision info
+ * Bit fields:
+ * 31-16: Reserved
+ * 15-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions
+ * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D
+ * 1-0: "Dash" (-) value, as in A-1, etc.
+ */
+#define CSR_HW_REV (CSR_BASE+0x028)
+
+/*
+ * EEPROM and OTP (one-time-programmable) memory reads
+ *
+ * NOTE: Device must be awake, initialized via apm_ops.init(),
+ * in order to read.
+ */
+#define CSR_EEPROM_REG (CSR_BASE+0x02c)
+#define CSR_EEPROM_GP (CSR_BASE+0x030)
+#define CSR_OTP_GP_REG (CSR_BASE+0x034)
+
+#define CSR_GIO_REG (CSR_BASE+0x03C)
+#define CSR_GP_UCODE_REG (CSR_BASE+0x048)
+#define CSR_GP_DRIVER_REG (CSR_BASE+0x050)
+
+/*
+ * UCODE-DRIVER GP (general purpose) mailbox registers.
+ * SET/CLR registers set/clear bit(s) if "1" is written.
+ */
+#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
+#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058)
+#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
+#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
+
+#define CSR_MBOX_SET_REG (CSR_BASE + 0x88)
+
+#define CSR_LED_REG (CSR_BASE+0x094)
+#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0)
+#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */
+
+
+/* GIO Chicken Bits (PCI Express bus link power management) */
+#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
+
+/* Analog phase-lock-loop configuration */
+#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
+
+/*
+ * CSR HW resources monitor registers
+ */
+#define CSR_MONITOR_CFG_REG (CSR_BASE+0x214)
+#define CSR_MONITOR_STATUS_REG (CSR_BASE+0x228)
+#define CSR_MONITOR_XTAL_RESOURCES (0x00000010)
+
+/*
+ * CSR Hardware Revision Workaround Register. Indicates hardware rev;
+ * "step" determines CCK backoff for txpower calculation. Used for 4965 only.
+ * See also CSR_HW_REV register.
+ * Bit fields:
+ * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step
+ * 1-0: "Dash" (-) value, as in C-1, etc.
+ */
+#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C)
+
+#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240)
+#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250)
+
+/* Bits for CSR_HW_IF_CONFIG_REG */
+#define CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH (0x00000003)
+#define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP (0x0000000C)
+#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0)
+#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100)
+#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200)
+#define CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00)
+#define CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000)
+#define CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000)
+
+#define CSR_HW_IF_CONFIG_REG_POS_MAC_DASH (0)
+#define CSR_HW_IF_CONFIG_REG_POS_MAC_STEP (2)
+#define CSR_HW_IF_CONFIG_REG_POS_BOARD_VER (6)
+#define CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE (10)
+#define CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12)
+#define CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14)
+
+#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000)
+#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
+#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */
+#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */
+#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */
+#define CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000)
+#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */
+
+#define CSR_MBOX_SET_REG_OS_ALIVE BIT(5)
+
+#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/
+#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/
+
+/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
+ * acknowledged (reset) by host writing "1" to flagged bits. */
+#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
+#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
+#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */
+#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
+#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
+#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
+#define CSR_INT_BIT_PAGING (1 << 24) /* SDIO PAGING */
+#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
+#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
+#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */
+#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */
+#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */
+
+#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \
+ CSR_INT_BIT_HW_ERR | \
+ CSR_INT_BIT_FH_TX | \
+ CSR_INT_BIT_SW_ERR | \
+ CSR_INT_BIT_PAGING | \
+ CSR_INT_BIT_RF_KILL | \
+ CSR_INT_BIT_SW_RX | \
+ CSR_INT_BIT_WAKEUP | \
+ CSR_INT_BIT_ALIVE | \
+ CSR_INT_BIT_RX_PERIODIC)
+
+/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
+#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */
+#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */
+#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */
+#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */
+#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */
+#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */
+
+#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \
+ CSR_FH_INT_BIT_RX_CHNL1 | \
+ CSR_FH_INT_BIT_RX_CHNL0)
+
+#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \
+ CSR_FH_INT_BIT_TX_CHNL0)
+
+/* GPIO */
+#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200)
+#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000)
+#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200)
+
+/* RESET */
+#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001)
+#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002)
+#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080)
+#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100)
+#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200)
+#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000)
+
+/*
+ * GP (general purpose) CONTROL REGISTER
+ * Bit fields:
+ * 27: HW_RF_KILL_SW
+ * Indicates state of (platform's) hardware RF-Kill switch
+ * 26-24: POWER_SAVE_TYPE
+ * Indicates current power-saving mode:
+ * 000 -- No power saving
+ * 001 -- MAC power-down
+ * 010 -- PHY (radio) power-down
+ * 011 -- Error
+ * 10: XTAL ON request
+ * 9-6: SYS_CONFIG
+ * Indicates current system configuration, reflecting pins on chip
+ * as forced high/low by device circuit board.
+ * 4: GOING_TO_SLEEP
+ * Indicates MAC is entering a power-saving sleep power-down.
+ * Not a good time to access device-internal resources.
+ * 3: MAC_ACCESS_REQ
+ * Host sets this to request and maintain MAC wakeup, to allow host
+ * access to device-internal resources. Host must wait for
+ * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR
+ * device registers.
+ * 2: INIT_DONE
+ * Host sets this to put device into fully operational D0 power mode.
+ * Host resets this after SW_RESET to put device into low power mode.
+ * 0: MAC_CLOCK_READY
+ * Indicates MAC (ucode processor, etc.) is powered up and can run.
+ * Internal resources are accessible.
+ * NOTE: This does not indicate that the processor is actually running.
+ * NOTE: This does not indicate that device has completed
+ * init or post-power-down restore of internal SRAM memory.
+ * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that
+ * SRAM is restored and uCode is in normal operation mode.
+ * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and
+ * do not need to save/restore it.
+ * NOTE: After device reset, this bit remains "0" until host sets
+ * INIT_DONE
+ */
+#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001)
+#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
+#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
+#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010)
+#define CSR_GP_CNTRL_REG_FLAG_XTAL_ON (0x00000400)
+
+#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001)
+
+#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000)
+#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000)
+#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000)
+
+
+/* HW REV */
+#define CSR_HW_REV_DASH(_val) (((_val) & 0x0000003) >> 0)
+#define CSR_HW_REV_STEP(_val) (((_val) & 0x000000C) >> 2)
+
+
+/**
+ * hw_rev values
+ */
+enum {
+ SILICON_A_STEP = 0,
+ SILICON_B_STEP,
+ SILICON_C_STEP,
+};
+
+
+#define CSR_HW_REV_TYPE_MSK (0x000FFF0)
+#define CSR_HW_REV_TYPE_5300 (0x0000020)
+#define CSR_HW_REV_TYPE_5350 (0x0000030)
+#define CSR_HW_REV_TYPE_5100 (0x0000050)
+#define CSR_HW_REV_TYPE_5150 (0x0000040)
+#define CSR_HW_REV_TYPE_1000 (0x0000060)
+#define CSR_HW_REV_TYPE_6x00 (0x0000070)
+#define CSR_HW_REV_TYPE_6x50 (0x0000080)
+#define CSR_HW_REV_TYPE_6150 (0x0000084)
+#define CSR_HW_REV_TYPE_6x05 (0x00000B0)
+#define CSR_HW_REV_TYPE_6x30 CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_2x30 (0x00000C0)
+#define CSR_HW_REV_TYPE_2x00 (0x0000100)
+#define CSR_HW_REV_TYPE_105 (0x0000110)
+#define CSR_HW_REV_TYPE_135 (0x0000120)
+#define CSR_HW_REV_TYPE_7265D (0x0000210)
+#define CSR_HW_REV_TYPE_NONE (0x00001F0)
+
+/* EEPROM REG */
+#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001)
+#define CSR_EEPROM_REG_BIT_CMD (0x00000002)
+#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC)
+#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000)
+
+/* EEPROM GP */
+#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */
+#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180)
+#define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000)
+#define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001)
+#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002)
+#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004)
+
+/* One-time-programmable memory general purpose reg */
+#define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */
+#define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */
+#define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */
+#define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */
+
+/* GP REG */
+#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */
+#define CSR_GP_REG_NO_POWER_SAVE (0x00000000)
+#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000)
+#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000)
+#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000)
+
+
+/* CSR GIO */
+#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002)
+
+/*
+ * UCODE-DRIVER GP (general purpose) mailbox register 1
+ * Host driver and uCode write and/or read this register to communicate with
+ * each other.
+ * Bit fields:
+ * 4: UCODE_DISABLE
+ * Host sets this to request permanent halt of uCode, same as
+ * sending CARD_STATE command with "halt" bit set.
+ * 3: CT_KILL_EXIT
+ * Host sets this to request exit from CT_KILL state, i.e. host thinks
+ * device temperature is low enough to continue normal operation.
+ * 2: CMD_BLOCKED
+ * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL)
+ * to release uCode to clear all Tx and command queues, enter
+ * unassociated mode, and power down.
+ * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit.
+ * 1: SW_BIT_RFKILL
+ * Host sets this when issuing CARD_STATE command to request
+ * device sleep.
+ * 0: MAC_SLEEP
+ * uCode sets this when preparing a power-saving power-down.
+ * uCode resets this when power-up is complete and SRAM is sane.
+ * NOTE: device saves internal SRAM data to host when powering down,
+ * and must restore this data after powering back up.
+ * MAC_SLEEP is the best indication that restore is complete.
+ * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and
+ * do not need to save/restore it.
+ */
+#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001)
+#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
+#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
+#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
+#define CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020)
+
+/* GP Driver */
+#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003)
+#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000)
+#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001)
+#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002)
+#define CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6 (0x00000004)
+#define CSR_GP_DRIVER_REG_BIT_6050_1x2 (0x00000008)
+
+#define CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER (0x00000080)
+
+/* GIO Chicken Bits (PCI Express bus link power management) */
+#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000)
+#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000)
+
+/* LED */
+#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF)
+#define CSR_LED_REG_TURN_ON (0x60)
+#define CSR_LED_REG_TURN_OFF (0x20)
+
+/* ANA_PLL */
+#define CSR50_ANA_PLL_CFG_VAL (0x00880300)
+
+/* HPET MEM debug */
+#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000)
+
+/* DRAM INT TABLE */
+#define CSR_DRAM_INT_TBL_ENABLE (1 << 31)
+#define CSR_DRAM_INIT_TBL_WRITE_POINTER (1 << 28)
+#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27)
+
+/*
+ * SHR target access (Shared block memory space)
+ *
+ * Shared internal registers can be accessed directly from PCI bus through SHR
+ * arbiter without need for the MAC HW to be powered up. This is possible due to
+ * indirect read/write via HEEP_CTRL_WRD_PCIEX_CTRL (0xEC) and
+ * HEEP_CTRL_WRD_PCIEX_DATA (0xF4) registers.
+ *
+ * Use iwl_write32()/iwl_read32() family to access these registers. The MAC HW
+ * need not be powered up so no "grab inc access" is required.
+ */
+
+/*
+ * Registers for accessing shared registers (e.g. SHR_APMG_GP1,
+ * SHR_APMG_XTAL_CFG). For example, to read from SHR_APMG_GP1 register (0x1DC),
+ * first, write to the control register:
+ * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register)
+ * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 2 (read access)
+ * second, read from the data register HEEP_CTRL_WRD_PCIEX_DATA[31:0].
+ *
+ * To write the register, first, write to the data register
+ * HEEP_CTRL_WRD_PCIEX_DATA[31:0] and then:
+ * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register)
+ * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 3 (write access)
+ */
+#define HEEP_CTRL_WRD_PCIEX_CTRL_REG (CSR_BASE+0x0ec)
+#define HEEP_CTRL_WRD_PCIEX_DATA_REG (CSR_BASE+0x0f4)
+
+/*
+ * HBUS (Host-side Bus)
+ *
+ * HBUS registers are mapped directly into PCI bus space, but are used
+ * to indirectly access device's internal memory or registers that
+ * may be powered-down.
+ *
+ * Use iwl_write_direct32()/iwl_read_direct32() family for these registers;
+ * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ
+ * to make sure the MAC (uCode processor, etc.) is powered up for accessing
+ * internal resources.
+ *
+ * Do not use iwl_write32()/iwl_read32() family to access these registers;
+ * these provide only simple PCI bus access, without waking up the MAC.
+ */
+#define HBUS_BASE (0x400)
+
+/*
+ * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
+ * structures, error log, event log, verifying uCode load).
+ * First write to address register, then read from or write to data register
+ * to complete the job. Once the address register is set up, accesses to
+ * data registers auto-increment the address by one dword.
+ * Bit usage for address registers (read or write):
+ * 0-31: memory address within device
+ */
+#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c)
+#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010)
+#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018)
+#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c)
+
+/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */
+#define HBUS_TARG_MBX_C (HBUS_BASE+0x030)
+#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)
+
+/*
+ * Registers for accessing device's internal peripheral registers
+ * (e.g. SCD, BSM, etc.). First write to address register,
+ * then read from or write to data register to complete the job.
+ * Bit usage for address registers (read or write):
+ * 0-15: register address (offset) within device
+ * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword)
+ */
+#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044)
+#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048)
+#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c)
+#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050)
+
+/* Used to enable DBGM */
+#define HBUS_TARG_TEST_REG (HBUS_BASE+0x05c)
+
+/*
+ * Per-Tx-queue write pointer (index, really!)
+ * Indicates index to next TFD that driver will fill (1 past latest filled).
+ * Bit usage:
+ * 0-7: queue write index
+ * 11-8: queue selector
+ */
+#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
+
+/**********************************************************
+ * CSR values
+ **********************************************************/
+ /*
+ * host interrupt timeout value
+ * used with setting interrupt coalescing timer
+ * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit
+ *
+ * default interrupt coalescing timer is 64 x 32 = 2048 usecs
+ */
+#define IWL_HOST_INT_TIMEOUT_MAX (0xFF)
+#define IWL_HOST_INT_TIMEOUT_DEF (0x40)
+#define IWL_HOST_INT_TIMEOUT_MIN (0x0)
+#define IWL_HOST_INT_OPER_MODE BIT(31)
+
+/*****************************************************************************
+ * 7000/3000 series SHR DTS addresses *
+ *****************************************************************************/
+
+/* Diode Results Register Structure: */
+enum dtd_diode_reg {
+ DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */
+ DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */
+ DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */
+ DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */
+ DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */
+ DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */
+/* Those are the masks INSIDE the flags bit-field: */
+ DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0,
+ DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */
+ DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7,
+ DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */
+};
+
+#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c
new file mode 100644
index 000000000000..b1c3b0d0fcc6
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c
@@ -0,0 +1,136 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/export.h>
+#include "iwl-drv.h"
+#include "iwl-debug.h"
+#include "iwl-devtrace.h"
+
+#define __iwl_fn(fn) \
+void __iwl_ ##fn(struct device *dev, const char *fmt, ...) \
+{ \
+ struct va_format vaf = { \
+ .fmt = fmt, \
+ }; \
+ va_list args; \
+ \
+ va_start(args, fmt); \
+ vaf.va = &args; \
+ dev_ ##fn(dev, "%pV", &vaf); \
+ trace_iwlwifi_ ##fn(&vaf); \
+ va_end(args); \
+}
+
+__iwl_fn(warn)
+IWL_EXPORT_SYMBOL(__iwl_warn);
+__iwl_fn(info)
+IWL_EXPORT_SYMBOL(__iwl_info);
+__iwl_fn(crit)
+IWL_EXPORT_SYMBOL(__iwl_crit);
+
+void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only,
+ const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ if (!trace_only) {
+ if (rfkill_prefix)
+ dev_err(dev, "(RFKILL) %pV", &vaf);
+ else
+ dev_err(dev, "%pV", &vaf);
+ }
+ trace_iwlwifi_err(&vaf);
+ va_end(args);
+}
+IWL_EXPORT_SYMBOL(__iwl_err);
+
+#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING)
+void __iwl_dbg(struct device *dev,
+ u32 level, bool limit, const char *function,
+ const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (iwl_have_debug_level(level) &&
+ (!limit || net_ratelimit()))
+ dev_printk(KERN_DEBUG, dev, "%c %s %pV",
+ in_interrupt() ? 'I' : 'U', function, &vaf);
+#endif
+ trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf);
+ va_end(args);
+}
+IWL_EXPORT_SYMBOL(__iwl_dbg);
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h
new file mode 100644
index 000000000000..110333208450
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h
@@ -0,0 +1,223 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_debug_h__
+#define __iwl_debug_h__
+
+#include "iwl-modparams.h"
+
+
+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,
+ const char *fmt, ...) __printf(4, 5);
+void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3);
+void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3);
+void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3);
+
+/* not all compilers can evaluate strlen() at compile time, so use sizeof() */
+#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n')
+
+/* No matter what is m (priv, bus, trans), this will work */
+#define IWL_ERR_DEV(d, f, a...) \
+ do { \
+ CHECK_FOR_NEWLINE(f); \
+ __iwl_err((d), false, false, f, ## a); \
+ } while (0)
+#define IWL_ERR(m, f, a...) \
+ IWL_ERR_DEV((m)->dev, f, ## a)
+#define IWL_WARN(m, f, a...) \
+ do { \
+ CHECK_FOR_NEWLINE(f); \
+ __iwl_warn((m)->dev, f, ## a); \
+ } while (0)
+#define IWL_INFO(m, f, a...) \
+ do { \
+ CHECK_FOR_NEWLINE(f); \
+ __iwl_info((m)->dev, f, ## a); \
+ } while (0)
+#define IWL_CRIT(m, f, a...) \
+ do { \
+ CHECK_FOR_NEWLINE(f); \
+ __iwl_crit((m)->dev, f, ## a); \
+ } while (0)
+
+#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING)
+void __iwl_dbg(struct device *dev,
+ u32 level, bool limit, const char *function,
+ const char *fmt, ...) __printf(5, 6);
+#else
+__printf(5, 6) static inline void
+__iwl_dbg(struct device *dev,
+ u32 level, bool limit, const char *function,
+ const char *fmt, ...)
+{}
+#endif
+
+#define iwl_print_hex_error(m, p, len) \
+do { \
+ print_hex_dump(KERN_ERR, "iwl data: ", \
+ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \
+} while (0)
+
+#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...) \
+ do { \
+ CHECK_FOR_NEWLINE(fmt); \
+ __iwl_dbg(dev, level, limit, __func__, fmt, ##args); \
+ } while (0)
+#define IWL_DEBUG(m, level, fmt, args...) \
+ __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args)
+#define IWL_DEBUG_DEV(dev, level, fmt, args...) \
+ __IWL_DEBUG_DEV(dev, level, false, fmt, ##args)
+#define IWL_DEBUG_LIMIT(m, level, fmt, args...) \
+ __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args)
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define iwl_print_hex_dump(m, level, p, len) \
+do { \
+ if (iwl_have_debug_level(level)) \
+ print_hex_dump(KERN_DEBUG, "iwl data: ", \
+ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \
+} while (0)
+#else
+#define iwl_print_hex_dump(m, level, p, len)
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+/*
+ * To use the debug system:
+ *
+ * If you are defining a new debug classification, simply add it to the #define
+ * list here in the form of
+ *
+ * #define IWL_DL_xxxx VALUE
+ *
+ * where xxxx should be the name of the classification (for example, WEP).
+ *
+ * You then need to either add a IWL_xxxx_DEBUG() macro definition for your
+ * classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * The active debug levels can be accessed via files
+ *
+ * /sys/module/iwlwifi/parameters/debug
+ * when CONFIG_IWLWIFI_DEBUG=y.
+ *
+ * /sys/kernel/debug/phy0/iwlwifi/debug/debug_level
+ * when CONFIG_IWLWIFI_DEBUGFS=y.
+ *
+ */
+
+/* 0x0000000F - 0x00000001 */
+#define IWL_DL_INFO 0x00000001
+#define IWL_DL_MAC80211 0x00000002
+#define IWL_DL_HCMD 0x00000004
+#define IWL_DL_TDLS 0x00000008
+/* 0x000000F0 - 0x00000010 */
+#define IWL_DL_QUOTA 0x00000010
+#define IWL_DL_TE 0x00000020
+#define IWL_DL_EEPROM 0x00000040
+#define IWL_DL_RADIO 0x00000080
+/* 0x00000F00 - 0x00000100 */
+#define IWL_DL_POWER 0x00000100
+#define IWL_DL_TEMP 0x00000200
+#define IWL_DL_RPM 0x00000400
+#define IWL_DL_SCAN 0x00000800
+/* 0x0000F000 - 0x00001000 */
+#define IWL_DL_ASSOC 0x00001000
+#define IWL_DL_DROP 0x00002000
+#define IWL_DL_LAR 0x00004000
+#define IWL_DL_COEX 0x00008000
+/* 0x000F0000 - 0x00010000 */
+#define IWL_DL_FW 0x00010000
+#define IWL_DL_RF_KILL 0x00020000
+#define IWL_DL_FW_ERRORS 0x00040000
+/* 0x00F00000 - 0x00100000 */
+#define IWL_DL_RATE 0x00100000
+#define IWL_DL_CALIB 0x00200000
+#define IWL_DL_WEP 0x00400000
+#define IWL_DL_TX 0x00800000
+/* 0x0F000000 - 0x01000000 */
+#define IWL_DL_RX 0x01000000
+#define IWL_DL_ISR 0x02000000
+#define IWL_DL_HT 0x04000000
+#define IWL_DL_EXTERNAL 0x08000000
+/* 0xF0000000 - 0x10000000 */
+#define IWL_DL_11H 0x10000000
+#define IWL_DL_STATS 0x20000000
+#define IWL_DL_TX_REPLY 0x40000000
+#define IWL_DL_TX_QUEUES 0x80000000
+
+#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
+#define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a)
+#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
+#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
+#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
+#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a)
+#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a)
+#define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a)
+#define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a)
+#define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a)
+#define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a)
+#define IWL_DEBUG_QUOTA(p, f, a...) IWL_DEBUG(p, IWL_DL_QUOTA, f, ## a)
+#define IWL_DEBUG_TE(p, f, a...) IWL_DEBUG(p, IWL_DL_TE, f, ## a)
+#define IWL_DEBUG_EEPROM(d, f, a...) IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a)
+#define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a)
+#define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a)
+#define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a)
+#define IWL_DEBUG_FW_ERRORS(p, f, a...) IWL_DEBUG(p, IWL_DL_FW_ERRORS, f, ## a)
+#define IWL_DEBUG_DROP(p, f, a...) IWL_DEBUG(p, IWL_DL_DROP, f, ## a)
+#define IWL_DEBUG_DROP_LIMIT(p, f, a...) \
+ IWL_DEBUG_LIMIT(p, IWL_DL_DROP, f, ## a)
+#define IWL_DEBUG_COEX(p, f, a...) IWL_DEBUG(p, IWL_DL_COEX, f, ## a)
+#define IWL_DEBUG_RATE(p, f, a...) IWL_DEBUG(p, IWL_DL_RATE, f, ## a)
+#define IWL_DEBUG_RATE_LIMIT(p, f, a...) \
+ IWL_DEBUG_LIMIT(p, IWL_DL_RATE, f, ## a)
+#define IWL_DEBUG_ASSOC(p, f, a...) \
+ IWL_DEBUG(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
+#define IWL_DEBUG_ASSOC_LIMIT(p, f, a...) \
+ IWL_DEBUG_LIMIT(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
+#define IWL_DEBUG_HT(p, f, a...) IWL_DEBUG(p, IWL_DL_HT, f, ## a)
+#define IWL_DEBUG_STATS(p, f, a...) IWL_DEBUG(p, IWL_DL_STATS, f, ## a)
+#define IWL_DEBUG_STATS_LIMIT(p, f, a...) \
+ IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a)
+#define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a)
+#define IWL_DEBUG_TX_QUEUES(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_QUEUES, f, ## a)
+#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a)
+#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
+#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a)
+#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
+#define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a)
+
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h
new file mode 100644
index 000000000000..d80312b46f16
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_DATA) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_DATA
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_data
+
+TRACE_EVENT(iwlwifi_dev_tx_data,
+ TP_PROTO(const struct device *dev,
+ struct sk_buff *skb,
+ u8 hdr_len, size_t data_len),
+ TP_ARGS(dev, skb, hdr_len, data_len),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ if (iwl_trace_data(skb))
+ skb_copy_bits(skb, hdr_len,
+ __get_dynamic_array(data), data_len);
+ ),
+ TP_printk("[%s] TX frame data", __get_str(dev))
+);
+
+TRACE_EVENT(iwlwifi_dev_tx_tso_chunk,
+ TP_PROTO(const struct device *dev,
+ u8 *data_src, size_t data_len),
+ TP_ARGS(dev, data_src, data_len),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __dynamic_array(u8, data, data_len)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ memcpy(__get_dynamic_array(data), data_src, data_len);
+ ),
+ TP_printk("[%s] TX frame data", __get_str(dev))
+);
+
+TRACE_EVENT(iwlwifi_dev_rx_data,
+ TP_PROTO(const struct device *dev,
+ const struct iwl_trans *trans,
+ void *rxbuf, size_t len),
+ TP_ARGS(dev, trans, rxbuf, len),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __dynamic_array(u8, data,
+ len - iwl_rx_trace_len(trans, rxbuf, len))
+ ),
+ TP_fast_assign(
+ size_t offs = iwl_rx_trace_len(trans, rxbuf, len);
+ DEV_ASSIGN;
+ if (offs < len)
+ memcpy(__get_dynamic_array(data),
+ ((u8 *)rxbuf) + offs, len - offs);
+ ),
+ TP_printk("[%s] RX frame data", __get_str(dev))
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_DATA */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-data
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h
new file mode 100644
index 000000000000..27914eedc146
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h
@@ -0,0 +1,155 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_IO) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_IO
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_io
+
+TRACE_EVENT(iwlwifi_dev_ioread32,
+ TP_PROTO(const struct device *dev, u32 offs, u32 val),
+ TP_ARGS(dev, offs, val),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%s] read io[%#x] = %#x",
+ __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite8,
+ TP_PROTO(const struct device *dev, u32 offs, u8 val),
+ TP_ARGS(dev, offs, val),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u32, offs)
+ __field(u8, val)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%s] write io[%#x] = %#x)",
+ __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite32,
+ TP_PROTO(const struct device *dev, u32 offs, u32 val),
+ TP_ARGS(dev, offs, val),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%s] write io[%#x] = %#x)",
+ __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite_prph32,
+ TP_PROTO(const struct device *dev, u32 offs, u32 val),
+ TP_ARGS(dev, offs, val),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%s] write PRPH[%#x] = %#x)",
+ __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_ioread_prph32,
+ TP_PROTO(const struct device *dev, u32 offs, u32 val),
+ TP_ARGS(dev, offs, val),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%s] read PRPH[%#x] = %#x",
+ __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_irq,
+ TP_PROTO(const struct device *dev),
+ TP_ARGS(dev),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ ),
+ /* TP_printk("") doesn't compile */
+ TP_printk("%d", 0)
+);
+
+TRACE_EVENT(iwlwifi_dev_ict_read,
+ TP_PROTO(const struct device *dev, u32 index, u32 value),
+ TP_ARGS(dev, index, value),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u32, index)
+ __field(u32, value)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->index = index;
+ __entry->value = value;
+ ),
+ TP_printk("[%s] read ict[%d] = %#.8x",
+ __get_str(dev), __entry->index, __entry->value)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_IO */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-io
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
new file mode 100644
index 000000000000..22786d7dc00a
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
@@ -0,0 +1,209 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_IWLWIFI) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_IWLWIFI
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi
+
+TRACE_EVENT(iwlwifi_dev_hcmd,
+ TP_PROTO(const struct device *dev,
+ struct iwl_host_cmd *cmd, u16 total_size,
+ struct iwl_cmd_header_wide *hdr),
+ TP_ARGS(dev, cmd, total_size, hdr),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __dynamic_array(u8, hcmd, total_size)
+ __field(u32, flags)
+ ),
+ TP_fast_assign(
+ int i, offset = sizeof(struct iwl_cmd_header);
+
+ if (hdr->group_id)
+ offset = sizeof(struct iwl_cmd_header_wide);
+
+ DEV_ASSIGN;
+ __entry->flags = cmd->flags;
+ memcpy(__get_dynamic_array(hcmd), hdr, offset);
+
+ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
+ if (!cmd->len[i])
+ continue;
+ memcpy((u8 *)__get_dynamic_array(hcmd) + offset,
+ cmd->data[i], cmd->len[i]);
+ offset += cmd->len[i];
+ }
+ ),
+ TP_printk("[%s] hcmd %#.2x.%#.2x (%ssync)",
+ __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[1],
+ ((u8 *)__get_dynamic_array(hcmd))[0],
+ __entry->flags & CMD_ASYNC ? "a" : "")
+);
+
+TRACE_EVENT(iwlwifi_dev_rx,
+ TP_PROTO(const struct device *dev, const struct iwl_trans *trans,
+ struct iwl_rx_packet *pkt, size_t len),
+ TP_ARGS(dev, trans, pkt, len),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u8, cmd)
+ __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, pkt, len))
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->cmd = pkt->hdr.cmd;
+ memcpy(__get_dynamic_array(rxbuf), pkt,
+ iwl_rx_trace_len(trans, pkt, len));
+ ),
+ TP_printk("[%s] RX cmd %#.2x",
+ __get_str(dev), __entry->cmd)
+);
+
+TRACE_EVENT(iwlwifi_dev_tx,
+ TP_PROTO(const struct device *dev, struct sk_buff *skb,
+ void *tfd, size_t tfdlen,
+ void *buf0, size_t buf0_len,
+ void *buf1, size_t buf1_len),
+ TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __field(size_t, framelen)
+ __dynamic_array(u8, tfd, tfdlen)
+
+ /*
+ * Do not insert between or below these items,
+ * we want to keep the frame together (except
+ * for the possible padding).
+ */
+ __dynamic_array(u8, buf0, buf0_len)
+ __dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->framelen = buf0_len + buf1_len;
+ memcpy(__get_dynamic_array(tfd), tfd, tfdlen);
+ memcpy(__get_dynamic_array(buf0), buf0, buf0_len);
+ if (!iwl_trace_data(skb))
+ memcpy(__get_dynamic_array(buf1), buf1, buf1_len);
+ ),
+ TP_printk("[%s] TX %.2x (%zu bytes)",
+ __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0],
+ __entry->framelen)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_error,
+ TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low,
+ u32 data1, u32 data2, u32 line, u32 blink1,
+ u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time,
+ u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver,
+ u32 brd_ver),
+ TP_ARGS(dev, desc, tsf_low, data1, data2, line,
+ blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2,
+ gp3, major, minor, hw_ver, brd_ver),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+ __field(u32, desc)
+ __field(u32, tsf_low)
+ __field(u32, data1)
+ __field(u32, data2)
+ __field(u32, line)
+ __field(u32, blink1)
+ __field(u32, blink2)
+ __field(u32, ilink1)
+ __field(u32, ilink2)
+ __field(u32, bcon_time)
+ __field(u32, gp1)
+ __field(u32, gp2)
+ __field(u32, gp3)
+ __field(u32, major)
+ __field(u32, minor)
+ __field(u32, hw_ver)
+ __field(u32, brd_ver)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->desc = desc;
+ __entry->tsf_low = tsf_low;
+ __entry->data1 = data1;
+ __entry->data2 = data2;
+ __entry->line = line;
+ __entry->blink1 = blink1;
+ __entry->blink2 = blink2;
+ __entry->ilink1 = ilink1;
+ __entry->ilink2 = ilink2;
+ __entry->bcon_time = bcon_time;
+ __entry->gp1 = gp1;
+ __entry->gp2 = gp2;
+ __entry->gp3 = gp3;
+ __entry->major = major;
+ __entry->minor = minor;
+ __entry->hw_ver = hw_ver;
+ __entry->brd_ver = brd_ver;
+ ),
+ TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, "
+ "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X "
+ "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X "
+ "minor 0x%08X hw 0x%08X brd 0x%08X",
+ __get_str(dev), __entry->desc, __entry->tsf_low,
+ __entry->data1,
+ __entry->data2, __entry->line, __entry->blink1,
+ __entry->blink2, __entry->ilink1, __entry->ilink2,
+ __entry->bcon_time, __entry->gp1, __entry->gp2,
+ __entry->gp3, __entry->major, __entry->minor,
+ __entry->hw_ver, __entry->brd_ver)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_event,
+ TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
+ TP_ARGS(dev, time, data, ev),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __field(u32, time)
+ __field(u32, data)
+ __field(u32, ev)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->time = time;
+ __entry->data = data;
+ __entry->ev = ev;
+ ),
+ TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
+ __get_str(dev), __entry->time, __entry->data, __entry->ev)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_IWLWIFI */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-iwlwifi
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h
new file mode 100644
index 000000000000..5dfc9295a7e0
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_MSG) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_MSG
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_msg
+
+#define MAX_MSG_LEN 110
+
+DECLARE_EVENT_CLASS(iwlwifi_msg_event,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf),
+ TP_STRUCT__entry(
+ __dynamic_array(char, msg, MAX_MSG_LEN)
+ ),
+ TP_fast_assign(
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ MAX_MSG_LEN, vaf->fmt,
+ *vaf->va) >= MAX_MSG_LEN);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+TRACE_EVENT(iwlwifi_dbg,
+ TP_PROTO(u32 level, bool in_interrupt, const char *function,
+ struct va_format *vaf),
+ TP_ARGS(level, in_interrupt, function, vaf),
+ TP_STRUCT__entry(
+ __field(u32, level)
+ __field(u8, in_interrupt)
+ __string(function, function)
+ __dynamic_array(char, msg, MAX_MSG_LEN)
+ ),
+ TP_fast_assign(
+ __entry->level = level;
+ __entry->in_interrupt = in_interrupt;
+ __assign_str(function, function);
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ MAX_MSG_LEN, vaf->fmt,
+ *vaf->va) >= MAX_MSG_LEN);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_MSG */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-msg
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h
new file mode 100644
index 000000000000..e9b8673dd245
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h
@@ -0,0 +1,81 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_UCODE) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_UCODE
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_ucode
+
+TRACE_EVENT(iwlwifi_dev_ucode_cont_event,
+ TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
+ TP_ARGS(dev, time, data, ev),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __field(u32, time)
+ __field(u32, data)
+ __field(u32, ev)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->time = time;
+ __entry->data = data;
+ __entry->ev = ev;
+ ),
+ TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
+ __get_str(dev), __entry->time, __entry->data, __entry->ev)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_wrap_event,
+ TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry),
+ TP_ARGS(dev, wraps, n_entry, p_entry),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __field(u32, wraps)
+ __field(u32, n_entry)
+ __field(u32, p_entry)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ __entry->wraps = wraps;
+ __entry->n_entry = n_entry;
+ __entry->p_entry = p_entry;
+ ),
+ TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X",
+ __get_str(dev), __entry->wraps, __entry->n_entry,
+ __entry->p_entry)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_UCODE */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-ucode
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c
new file mode 100644
index 000000000000..1d9dd153ef1c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c
@@ -0,0 +1,43 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+
+/* sparse doesn't like tracepoint macros */
+#ifndef __CHECKER__
+#include "iwl-trans.h"
+
+#define CREATE_TRACE_POINTS
+#include "iwl-devtrace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event);
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h
new file mode 100644
index 000000000000..f4d3cd010087
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h
@@ -0,0 +1,89 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __IWLWIFI_DEVICE_TRACE
+#include <linux/skbuff.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include "iwl-trans.h"
+#if !defined(__IWLWIFI_DEVICE_TRACE)
+static inline bool iwl_trace_data(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ return false;
+ return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO);
+}
+
+static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans,
+ void *rxbuf, size_t len)
+{
+ struct iwl_cmd_header *cmd = (void *)((u8 *)rxbuf + sizeof(__le32));
+ struct ieee80211_hdr *hdr;
+
+ if (cmd->cmd != trans->rx_mpdu_cmd)
+ return len;
+
+ hdr = (void *)((u8 *)cmd + sizeof(struct iwl_cmd_header) +
+ trans->rx_mpdu_cmd_hdr_size);
+ if (!ieee80211_is_data(hdr->frame_control))
+ return len;
+ /* maybe try to identify EAPOL frames? */
+ return sizeof(__le32) + sizeof(*cmd) + trans->rx_mpdu_cmd_hdr_size +
+ ieee80211_hdrlen(hdr->frame_control);
+}
+#endif
+
+#define __IWLWIFI_DEVICE_TRACE
+
+#include <linux/tracepoint.h>
+#include <linux/device.h>
+#include "iwl-trans.h"
+
+
+#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__)
+#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
+
+#define DEV_ENTRY __string(dev, dev_name(dev))
+#define DEV_ASSIGN __assign_str(dev, dev_name(dev))
+
+#include "iwl-devtrace-io.h"
+#include "iwl-devtrace-ucode.h"
+#include "iwl-devtrace-msg.h"
+#include "iwl-devtrace-data.h"
+#include "iwl-devtrace-iwlwifi.h"
+
+#endif /* __IWLWIFI_DEVICE_TRACE */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
new file mode 100644
index 000000000000..7acb49075683
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -0,0 +1,1718 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "iwl-drv.h"
+#include "iwl-csr.h"
+#include "iwl-debug.h"
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-agn-hw.h"
+#include "iwl-fw.h"
+#include "iwl-config.h"
+#include "iwl-modparams.h"
+
+/******************************************************************************
+ *
+ * module boiler plate
+ *
+ ******************************************************************************/
+
+#define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux"
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static struct dentry *iwl_dbgfs_root;
+#endif
+
+/**
+ * struct iwl_drv - drv common data
+ * @list: list of drv structures using this opmode
+ * @fw: the iwl_fw structure
+ * @op_mode: the running op_mode
+ * @trans: transport layer
+ * @dev: for debug prints only
+ * @cfg: configuration struct
+ * @fw_index: firmware revision to try loading
+ * @firmware_name: composite filename of ucode file to load
+ * @request_firmware_complete: the firmware has been obtained from user space
+ */
+struct iwl_drv {
+ struct list_head list;
+ struct iwl_fw fw;
+
+ struct iwl_op_mode *op_mode;
+ struct iwl_trans *trans;
+ struct device *dev;
+ const struct iwl_cfg *cfg;
+
+ int fw_index; /* firmware we're trying to load */
+ char firmware_name[32]; /* name of firmware file to load */
+
+ struct completion request_firmware_complete;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct dentry *dbgfs_drv;
+ struct dentry *dbgfs_trans;
+ struct dentry *dbgfs_op_mode;
+#endif
+};
+
+enum {
+ DVM_OP_MODE = 0,
+ MVM_OP_MODE = 1,
+};
+
+/* Protects the table contents, i.e. the ops pointer & drv list */
+static struct mutex iwlwifi_opmode_table_mtx;
+static struct iwlwifi_opmode_table {
+ const char *name; /* name: iwldvm, iwlmvm, etc */
+ const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */
+ struct list_head drv; /* list of devices using this op_mode */
+} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */
+ [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL },
+ [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
+};
+
+#define IWL_DEFAULT_SCAN_CHANNELS 40
+
+/*
+ * struct fw_sec: Just for the image parsing process.
+ * For the fw storage we are using struct fw_desc.
+ */
+struct fw_sec {
+ const void *data; /* the sec data */
+ size_t size; /* section size */
+ u32 offset; /* offset of writing in the device */
+};
+
+static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
+{
+ vfree(desc->data);
+ desc->data = NULL;
+ desc->len = 0;
+}
+
+static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
+{
+ int i;
+ for (i = 0; i < IWL_UCODE_SECTION_MAX; i++)
+ iwl_free_fw_desc(drv, &img->sec[i]);
+}
+
+static void iwl_dealloc_ucode(struct iwl_drv *drv)
+{
+ int i;
+
+ kfree(drv->fw.dbg_dest_tlv);
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
+ kfree(drv->fw.dbg_conf_tlv[i]);
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++)
+ kfree(drv->fw.dbg_trigger_tlv[i]);
+
+ for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
+ iwl_free_fw_img(drv, drv->fw.img + i);
+}
+
+static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc,
+ struct fw_sec *sec)
+{
+ void *data;
+
+ desc->data = NULL;
+
+ if (!sec || !sec->size)
+ return -EINVAL;
+
+ data = vmalloc(sec->size);
+ if (!data)
+ return -ENOMEM;
+
+ desc->len = sec->size;
+ desc->offset = sec->offset;
+ memcpy(data, sec->data, desc->len);
+ desc->data = data;
+
+ return 0;
+}
+
+static void iwl_req_fw_callback(const struct firmware *ucode_raw,
+ void *context);
+
+#define UCODE_EXPERIMENTAL_INDEX 100
+#define UCODE_EXPERIMENTAL_TAG "exp"
+
+static int iwl_request_firmware(struct iwl_drv *drv, bool first)
+{
+ const char *name_pre = drv->cfg->fw_name_pre;
+ char tag[8];
+
+ if (first) {
+#ifdef CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
+ drv->fw_index = UCODE_EXPERIMENTAL_INDEX;
+ strcpy(tag, UCODE_EXPERIMENTAL_TAG);
+ } else if (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) {
+#endif
+ drv->fw_index = drv->cfg->ucode_api_max;
+ sprintf(tag, "%d", drv->fw_index);
+ } else {
+ drv->fw_index--;
+ sprintf(tag, "%d", drv->fw_index);
+ }
+
+ if (drv->fw_index < drv->cfg->ucode_api_min) {
+ IWL_ERR(drv, "no suitable firmware found!\n");
+ return -ENOENT;
+ }
+
+ snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode",
+ name_pre, tag);
+
+ /*
+ * Starting 8000B - FW name format has changed. This overwrites the
+ * previous name and uses the new format.
+ */
+ if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+ char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev);
+
+ snprintf(drv->firmware_name, sizeof(drv->firmware_name),
+ "%s%c-%s.ucode", name_pre, rev_step, tag);
+ }
+
+ IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
+ (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
+ ? "EXPERIMENTAL " : "",
+ drv->firmware_name);
+
+ return request_firmware_nowait(THIS_MODULE, 1, drv->firmware_name,
+ drv->trans->dev,
+ GFP_KERNEL, drv, iwl_req_fw_callback);
+}
+
+struct fw_img_parsing {
+ struct fw_sec sec[IWL_UCODE_SECTION_MAX];
+ int sec_counter;
+};
+
+/*
+ * struct fw_sec_parsing: to extract fw section and it's offset from tlv
+ */
+struct fw_sec_parsing {
+ __le32 offset;
+ const u8 data[];
+} __packed;
+
+/**
+ * struct iwl_tlv_calib_data - parse the default calib data from TLV
+ *
+ * @ucode_type: the uCode to which the following default calib relates.
+ * @calib: default calibrations.
+ */
+struct iwl_tlv_calib_data {
+ __le32 ucode_type;
+ struct iwl_tlv_calib_ctrl calib;
+} __packed;
+
+struct iwl_firmware_pieces {
+ struct fw_img_parsing img[IWL_UCODE_TYPE_MAX];
+
+ u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
+ u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+ /* FW debug data parsed for driver usage */
+ struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+ struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
+ size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
+ struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
+ size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
+};
+
+/*
+ * These functions are just to extract uCode section data from the pieces
+ * structure.
+ */
+static struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec)
+{
+ return &pieces->img[type].sec[sec];
+}
+
+static void set_sec_data(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec,
+ const void *data)
+{
+ pieces->img[type].sec[sec].data = data;
+}
+
+static void set_sec_size(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec,
+ size_t size)
+{
+ pieces->img[type].sec[sec].size = size;
+}
+
+static size_t get_sec_size(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec)
+{
+ return pieces->img[type].sec[sec].size;
+}
+
+static void set_sec_offset(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec,
+ u32 offset)
+{
+ pieces->img[type].sec[sec].offset = offset;
+}
+
+static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len)
+{
+ int i, j;
+ struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data;
+ struct iwl_fw_cipher_scheme *fwcs;
+ struct ieee80211_cipher_scheme *cs;
+ u32 cipher;
+
+ if (len < sizeof(*l) ||
+ len < sizeof(l->size) + l->size * sizeof(l->cs[0]))
+ return -EINVAL;
+
+ for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) {
+ fwcs = &l->cs[j];
+ cipher = le32_to_cpu(fwcs->cipher);
+
+ /* we skip schemes with zero cipher suite selector */
+ if (!cipher)
+ continue;
+
+ cs = &fw->cs[j++];
+ cs->cipher = cipher;
+ cs->iftype = BIT(NL80211_IFTYPE_STATION);
+ cs->hdr_len = fwcs->hdr_len;
+ cs->pn_len = fwcs->pn_len;
+ cs->pn_off = fwcs->pn_off;
+ cs->key_idx_off = fwcs->key_idx_off;
+ cs->key_idx_mask = fwcs->key_idx_mask;
+ cs->key_idx_shift = fwcs->key_idx_shift;
+ cs->mic_len = fwcs->mic_len;
+ }
+
+ return 0;
+}
+
+static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data,
+ const u32 len)
+{
+ struct iwl_fw_gscan_capabilities *fw_capa = (void *)data;
+ struct iwl_gscan_capabilities *capa = &fw->gscan_capa;
+
+ if (len < sizeof(*fw_capa))
+ return -EINVAL;
+
+ capa->max_scan_cache_size = le32_to_cpu(fw_capa->max_scan_cache_size);
+ capa->max_scan_buckets = le32_to_cpu(fw_capa->max_scan_buckets);
+ capa->max_ap_cache_per_scan =
+ le32_to_cpu(fw_capa->max_ap_cache_per_scan);
+ capa->max_rssi_sample_size = le32_to_cpu(fw_capa->max_rssi_sample_size);
+ capa->max_scan_reporting_threshold =
+ le32_to_cpu(fw_capa->max_scan_reporting_threshold);
+ capa->max_hotlist_aps = le32_to_cpu(fw_capa->max_hotlist_aps);
+ capa->max_significant_change_aps =
+ le32_to_cpu(fw_capa->max_significant_change_aps);
+ capa->max_bssid_history_entries =
+ le32_to_cpu(fw_capa->max_bssid_history_entries);
+ return 0;
+}
+
+/*
+ * Gets uCode section from tlv.
+ */
+static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces,
+ const void *data, enum iwl_ucode_type type,
+ int size)
+{
+ struct fw_img_parsing *img;
+ struct fw_sec *sec;
+ struct fw_sec_parsing *sec_parse;
+
+ if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX))
+ return -1;
+
+ sec_parse = (struct fw_sec_parsing *)data;
+
+ img = &pieces->img[type];
+ sec = &img->sec[img->sec_counter];
+
+ sec->offset = le32_to_cpu(sec_parse->offset);
+ sec->data = sec_parse->data;
+ sec->size = size - sizeof(sec_parse->offset);
+
+ ++img->sec_counter;
+
+ return 0;
+}
+
+static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data)
+{
+ struct iwl_tlv_calib_data *def_calib =
+ (struct iwl_tlv_calib_data *)data;
+ u32 ucode_type = le32_to_cpu(def_calib->ucode_type);
+ if (ucode_type >= IWL_UCODE_TYPE_MAX) {
+ IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n",
+ ucode_type);
+ return -EINVAL;
+ }
+ drv->fw.default_calib[ucode_type].flow_trigger =
+ def_calib->calib.flow_trigger;
+ drv->fw.default_calib[ucode_type].event_trigger =
+ def_calib->calib.event_trigger;
+
+ return 0;
+}
+
+static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data,
+ struct iwl_ucode_capabilities *capa)
+{
+ const struct iwl_ucode_api *ucode_api = (void *)data;
+ u32 api_index = le32_to_cpu(ucode_api->api_index);
+ u32 api_flags = le32_to_cpu(ucode_api->api_flags);
+ int i;
+
+ if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) {
+ IWL_ERR(drv,
+ "api flags index %d larger than supported by driver\n",
+ api_index);
+ /* don't return an error so we can load FW that has more bits */
+ return 0;
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (api_flags & BIT(i))
+ __set_bit(i + 32 * api_index, capa->_api);
+ }
+
+ return 0;
+}
+
+static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data,
+ struct iwl_ucode_capabilities *capa)
+{
+ const struct iwl_ucode_capa *ucode_capa = (void *)data;
+ u32 api_index = le32_to_cpu(ucode_capa->api_index);
+ u32 api_flags = le32_to_cpu(ucode_capa->api_capa);
+ int i;
+
+ if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) {
+ IWL_ERR(drv,
+ "capa flags index %d larger than supported by driver\n",
+ api_index);
+ /* don't return an error so we can load FW that has more bits */
+ return 0;
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (api_flags & BIT(i))
+ __set_bit(i + 32 * api_index, capa->_capa);
+ }
+
+ return 0;
+}
+
+static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
+ const struct firmware *ucode_raw,
+ struct iwl_firmware_pieces *pieces)
+{
+ struct iwl_ucode_header *ucode = (void *)ucode_raw->data;
+ u32 api_ver, hdr_size, build;
+ char buildstr[25];
+ const u8 *src;
+
+ drv->fw.ucode_ver = le32_to_cpu(ucode->ver);
+ api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
+
+ switch (api_ver) {
+ default:
+ hdr_size = 28;
+ if (ucode_raw->size < hdr_size) {
+ IWL_ERR(drv, "File size too small!\n");
+ return -EINVAL;
+ }
+ build = le32_to_cpu(ucode->u.v2.build);
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v2.inst_size));
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v2.data_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v2.init_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v2.init_data_size));
+ src = ucode->u.v2.data;
+ break;
+ case 0:
+ case 1:
+ case 2:
+ hdr_size = 24;
+ if (ucode_raw->size < hdr_size) {
+ IWL_ERR(drv, "File size too small!\n");
+ return -EINVAL;
+ }
+ build = 0;
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v1.inst_size));
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v1.data_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v1.init_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v1.init_data_size));
+ src = ucode->u.v1.data;
+ break;
+ }
+
+ if (build)
+ sprintf(buildstr, " build %u%s", build,
+ (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
+ ? " (EXP)" : "");
+ else
+ buildstr[0] = '\0';
+
+ snprintf(drv->fw.fw_version,
+ sizeof(drv->fw.fw_version),
+ "%u.%u.%u.%u%s",
+ IWL_UCODE_MAJOR(drv->fw.ucode_ver),
+ IWL_UCODE_MINOR(drv->fw.ucode_ver),
+ IWL_UCODE_API(drv->fw.ucode_ver),
+ IWL_UCODE_SERIAL(drv->fw.ucode_ver),
+ buildstr);
+
+ /* Verify size of file vs. image size info in file's header */
+
+ if (ucode_raw->size != hdr_size +
+ get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) +
+ get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) +
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) +
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)) {
+
+ IWL_ERR(drv,
+ "uCode file size %d does not match expected size\n",
+ (int)ucode_raw->size);
+ return -EINVAL;
+ }
+
+
+ set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, src);
+ src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
+ set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, src);
+ src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
+ set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, src);
+ src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST);
+ set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
+ set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, src);
+ src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA);
+ set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
+ return 0;
+}
+
+static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
+ const struct firmware *ucode_raw,
+ struct iwl_firmware_pieces *pieces,
+ struct iwl_ucode_capabilities *capa,
+ bool *usniffer_images)
+{
+ struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data;
+ struct iwl_ucode_tlv *tlv;
+ size_t len = ucode_raw->size;
+ const u8 *data;
+ u32 tlv_len;
+ u32 usniffer_img;
+ enum iwl_ucode_tlv_type tlv_type;
+ const u8 *tlv_data;
+ char buildstr[25];
+ u32 build, paging_mem_size;
+ int num_of_cpus;
+ bool usniffer_req = false;
+ bool gscan_capa = false;
+
+ if (len < sizeof(*ucode)) {
+ IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
+ return -EINVAL;
+ }
+
+ if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) {
+ IWL_ERR(drv, "invalid uCode magic: 0X%x\n",
+ le32_to_cpu(ucode->magic));
+ return -EINVAL;
+ }
+
+ drv->fw.ucode_ver = le32_to_cpu(ucode->ver);
+ memcpy(drv->fw.human_readable, ucode->human_readable,
+ sizeof(drv->fw.human_readable));
+ build = le32_to_cpu(ucode->build);
+
+ if (build)
+ sprintf(buildstr, " build %u%s", build,
+ (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
+ ? " (EXP)" : "");
+ else
+ buildstr[0] = '\0';
+
+ snprintf(drv->fw.fw_version,
+ sizeof(drv->fw.fw_version),
+ "%u.%u.%u.%u%s",
+ IWL_UCODE_MAJOR(drv->fw.ucode_ver),
+ IWL_UCODE_MINOR(drv->fw.ucode_ver),
+ IWL_UCODE_API(drv->fw.ucode_ver),
+ IWL_UCODE_SERIAL(drv->fw.ucode_ver),
+ buildstr);
+
+ data = ucode->data;
+
+ len -= sizeof(*ucode);
+
+ while (len >= sizeof(*tlv)) {
+ len -= sizeof(*tlv);
+ tlv = (void *)data;
+
+ tlv_len = le32_to_cpu(tlv->length);
+ tlv_type = le32_to_cpu(tlv->type);
+ tlv_data = tlv->data;
+
+ if (len < tlv_len) {
+ IWL_ERR(drv, "invalid TLV len: %zd/%u\n",
+ len, tlv_len);
+ return -EINVAL;
+ }
+ len -= ALIGN(tlv_len, 4);
+ data += sizeof(*tlv) + ALIGN(tlv_len, 4);
+
+ switch (tlv_type) {
+ case IWL_UCODE_TLV_INST:
+ set_sec_data(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
+ break;
+ case IWL_UCODE_TLV_DATA:
+ set_sec_data(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
+ break;
+ case IWL_UCODE_TLV_INIT:
+ set_sec_data(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
+ break;
+ case IWL_UCODE_TLV_INIT_DATA:
+ set_sec_data(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_DATA, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_DATA, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
+ break;
+ case IWL_UCODE_TLV_BOOT:
+ IWL_ERR(drv, "Found unexpected BOOT ucode\n");
+ break;
+ case IWL_UCODE_TLV_PROBE_MAX_LEN:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ capa->max_probe_length =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_PAN:
+ if (tlv_len)
+ goto invalid_tlv_len;
+ capa->flags |= IWL_UCODE_TLV_FLAGS_PAN;
+ break;
+ case IWL_UCODE_TLV_FLAGS:
+ /* must be at least one u32 */
+ if (tlv_len < sizeof(u32))
+ goto invalid_tlv_len;
+ /* and a proper number of u32s */
+ if (tlv_len % sizeof(u32))
+ goto invalid_tlv_len;
+ /*
+ * This driver only reads the first u32 as
+ * right now no more features are defined,
+ * if that changes then either the driver
+ * will not work with the new firmware, or
+ * it'll not take advantage of new features.
+ */
+ capa->flags = le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_API_CHANGES_SET:
+ if (tlv_len != sizeof(struct iwl_ucode_api))
+ goto invalid_tlv_len;
+ if (iwl_set_ucode_api_flags(drv, tlv_data, capa))
+ goto tlv_error;
+ break;
+ case IWL_UCODE_TLV_ENABLED_CAPABILITIES:
+ if (tlv_len != sizeof(struct iwl_ucode_capa))
+ goto invalid_tlv_len;
+ if (iwl_set_ucode_capabilities(drv, tlv_data, capa))
+ goto tlv_error;
+ break;
+ case IWL_UCODE_TLV_INIT_EVTLOG_PTR:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ pieces->init_evtlog_ptr =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_INIT_EVTLOG_SIZE:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ pieces->init_evtlog_size =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_INIT_ERRLOG_PTR:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ pieces->init_errlog_ptr =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_RUNT_EVTLOG_PTR:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ pieces->inst_evtlog_ptr =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ pieces->inst_evtlog_size =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_RUNT_ERRLOG_PTR:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ pieces->inst_errlog_ptr =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_ENHANCE_SENS_TBL:
+ if (tlv_len)
+ goto invalid_tlv_len;
+ drv->fw.enhance_sensitivity_table = true;
+ break;
+ case IWL_UCODE_TLV_WOWLAN_INST:
+ set_sec_data(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_INST, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_INST, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
+ break;
+ case IWL_UCODE_TLV_WOWLAN_DATA:
+ set_sec_data(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_DATA, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_DATA, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
+ break;
+ case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ capa->standard_phy_calibration_size =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_SEC_RT:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_SEC_INIT:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_SEC_WOWLAN:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_DEF_CALIB:
+ if (tlv_len != sizeof(struct iwl_tlv_calib_data))
+ goto invalid_tlv_len;
+ if (iwl_set_default_calib(drv, tlv_data))
+ goto tlv_error;
+ break;
+ case IWL_UCODE_TLV_PHY_SKU:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data);
+ drv->fw.valid_tx_ant = (drv->fw.phy_config &
+ FW_PHY_CFG_TX_CHAIN) >>
+ FW_PHY_CFG_TX_CHAIN_POS;
+ drv->fw.valid_rx_ant = (drv->fw.phy_config &
+ FW_PHY_CFG_RX_CHAIN) >>
+ FW_PHY_CFG_RX_CHAIN_POS;
+ break;
+ case IWL_UCODE_TLV_SECURE_SEC_RT:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_SECURE_SEC_INIT:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_SECURE_SEC_WOWLAN:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_NUM_OF_CPU:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ num_of_cpus =
+ le32_to_cpup((__le32 *)tlv_data);
+
+ if (num_of_cpus == 2) {
+ drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus =
+ true;
+ drv->fw.img[IWL_UCODE_INIT].is_dual_cpus =
+ true;
+ drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus =
+ true;
+ } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) {
+ IWL_ERR(drv, "Driver support upto 2 CPUs\n");
+ return -EINVAL;
+ }
+ break;
+ case IWL_UCODE_TLV_CSCHEME:
+ if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
+ goto invalid_tlv_len;
+ break;
+ case IWL_UCODE_TLV_N_SCAN_CHANNELS:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ capa->n_scan_channels =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_FW_VERSION: {
+ __le32 *ptr = (void *)tlv_data;
+ u32 major, minor;
+ u8 local_comp;
+
+ if (tlv_len != sizeof(u32) * 3)
+ goto invalid_tlv_len;
+
+ major = le32_to_cpup(ptr++);
+ minor = le32_to_cpup(ptr++);
+ local_comp = le32_to_cpup(ptr);
+
+ snprintf(drv->fw.fw_version,
+ sizeof(drv->fw.fw_version), "%u.%u.%u",
+ major, minor, local_comp);
+ break;
+ }
+ case IWL_UCODE_TLV_FW_DBG_DEST: {
+ struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
+
+ if (pieces->dbg_dest_tlv) {
+ IWL_ERR(drv,
+ "dbg destination ignored, already exists\n");
+ break;
+ }
+
+ pieces->dbg_dest_tlv = dest;
+ IWL_INFO(drv, "Found debug destination: %s\n",
+ get_fw_dbg_mode_string(dest->monitor_mode));
+
+ drv->fw.dbg_dest_reg_num =
+ tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
+ reg_ops);
+ drv->fw.dbg_dest_reg_num /=
+ sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
+
+ break;
+ }
+ case IWL_UCODE_TLV_FW_DBG_CONF: {
+ struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
+
+ if (!pieces->dbg_dest_tlv) {
+ IWL_ERR(drv,
+ "Ignore dbg config %d - no destination configured\n",
+ conf->id);
+ break;
+ }
+
+ if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
+ IWL_ERR(drv,
+ "Skip unknown configuration: %d\n",
+ conf->id);
+ break;
+ }
+
+ if (pieces->dbg_conf_tlv[conf->id]) {
+ IWL_ERR(drv,
+ "Ignore duplicate dbg config %d\n",
+ conf->id);
+ break;
+ }
+
+ if (conf->usniffer)
+ usniffer_req = true;
+
+ IWL_INFO(drv, "Found debug configuration: %d\n",
+ conf->id);
+
+ pieces->dbg_conf_tlv[conf->id] = conf;
+ pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
+ break;
+ }
+ case IWL_UCODE_TLV_FW_DBG_TRIGGER: {
+ struct iwl_fw_dbg_trigger_tlv *trigger =
+ (void *)tlv_data;
+ u32 trigger_id = le32_to_cpu(trigger->id);
+
+ if (trigger_id >= ARRAY_SIZE(drv->fw.dbg_trigger_tlv)) {
+ IWL_ERR(drv,
+ "Skip unknown trigger: %u\n",
+ trigger->id);
+ break;
+ }
+
+ if (pieces->dbg_trigger_tlv[trigger_id]) {
+ IWL_ERR(drv,
+ "Ignore duplicate dbg trigger %u\n",
+ trigger->id);
+ break;
+ }
+
+ IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id);
+
+ pieces->dbg_trigger_tlv[trigger_id] = trigger;
+ pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len;
+ break;
+ }
+ case IWL_UCODE_TLV_SEC_RT_USNIFFER:
+ *usniffer_images = true;
+ iwl_store_ucode_sec(pieces, tlv_data,
+ IWL_UCODE_REGULAR_USNIFFER,
+ tlv_len);
+ break;
+ case IWL_UCODE_TLV_PAGING:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ paging_mem_size = le32_to_cpup((__le32 *)tlv_data);
+
+ IWL_DEBUG_FW(drv,
+ "Paging: paging enabled (size = %u bytes)\n",
+ paging_mem_size);
+
+ if (paging_mem_size > MAX_PAGING_IMAGE_SIZE) {
+ IWL_ERR(drv,
+ "Paging: driver supports up to %lu bytes for paging image\n",
+ MAX_PAGING_IMAGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (paging_mem_size & (FW_PAGING_SIZE - 1)) {
+ IWL_ERR(drv,
+ "Paging: image isn't multiple %lu\n",
+ FW_PAGING_SIZE);
+ return -EINVAL;
+ }
+
+ drv->fw.img[IWL_UCODE_REGULAR].paging_mem_size =
+ paging_mem_size;
+ usniffer_img = IWL_UCODE_REGULAR_USNIFFER;
+ drv->fw.img[usniffer_img].paging_mem_size =
+ paging_mem_size;
+ break;
+ case IWL_UCODE_TLV_SDIO_ADMA_ADDR:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ drv->fw.sdio_adma_addr =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
+ case IWL_UCODE_TLV_FW_GSCAN_CAPA:
+ if (iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len))
+ goto invalid_tlv_len;
+ gscan_capa = true;
+ break;
+ default:
+ IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
+ break;
+ }
+ }
+
+ if (usniffer_req && !*usniffer_images) {
+ IWL_ERR(drv,
+ "user selected to work with usniffer but usniffer image isn't available in ucode package\n");
+ return -EINVAL;
+ }
+
+ if (len) {
+ IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len);
+ iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len);
+ return -EINVAL;
+ }
+
+ /*
+ * If ucode advertises that it supports GSCAN but GSCAN
+ * capabilities TLV is not present, warn and continue without GSCAN.
+ */
+ if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) &&
+ WARN(!gscan_capa,
+ "GSCAN is supported but capabilities TLV is unavailable\n"))
+ __clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT,
+ capa->_capa);
+
+ return 0;
+
+ invalid_tlv_len:
+ IWL_ERR(drv, "TLV %d has invalid size: %u\n", tlv_type, tlv_len);
+ tlv_error:
+ iwl_print_hex_dump(drv, IWL_DL_FW, tlv_data, tlv_len);
+
+ return -EINVAL;
+}
+
+static int iwl_alloc_ucode(struct iwl_drv *drv,
+ struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type)
+{
+ int i;
+ for (i = 0;
+ i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i);
+ i++)
+ if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]),
+ get_sec(pieces, type, i)))
+ return -ENOMEM;
+ return 0;
+}
+
+static int validate_sec_sizes(struct iwl_drv *drv,
+ struct iwl_firmware_pieces *pieces,
+ const struct iwl_cfg *cfg)
+{
+ IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST));
+ IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
+ IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST));
+ IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA));
+
+ /* Verify that uCode images will fit in card's SRAM. */
+ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) >
+ cfg->max_inst_size) {
+ IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST));
+ return -1;
+ }
+
+ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) >
+ cfg->max_data_size) {
+ IWL_ERR(drv, "uCode data len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
+ return -1;
+ }
+
+ if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
+ cfg->max_inst_size) {
+ IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST));
+ return -1;
+ }
+
+ if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) >
+ cfg->max_data_size) {
+ IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
+ return -1;
+ }
+ return 0;
+}
+
+static struct iwl_op_mode *
+_iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op)
+{
+ const struct iwl_op_mode_ops *ops = op->ops;
+ struct dentry *dbgfs_dir = NULL;
+ struct iwl_op_mode *op_mode = NULL;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ drv->dbgfs_op_mode = debugfs_create_dir(op->name,
+ drv->dbgfs_drv);
+ if (!drv->dbgfs_op_mode) {
+ IWL_ERR(drv,
+ "failed to create opmode debugfs directory\n");
+ return op_mode;
+ }
+ dbgfs_dir = drv->dbgfs_op_mode;
+#endif
+
+ op_mode = ops->start(drv->trans, drv->cfg, &drv->fw, dbgfs_dir);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (!op_mode) {
+ debugfs_remove_recursive(drv->dbgfs_op_mode);
+ drv->dbgfs_op_mode = NULL;
+ }
+#endif
+
+ return op_mode;
+}
+
+static void _iwl_op_mode_stop(struct iwl_drv *drv)
+{
+ /* op_mode can be NULL if its start failed */
+ if (drv->op_mode) {
+ iwl_op_mode_stop(drv->op_mode);
+ drv->op_mode = NULL;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ debugfs_remove_recursive(drv->dbgfs_op_mode);
+ drv->dbgfs_op_mode = NULL;
+#endif
+ }
+}
+
+/**
+ * iwl_req_fw_callback - callback when firmware was loaded
+ *
+ * If loaded successfully, copies the firmware into buffers
+ * for the card to fetch (via DMA).
+ */
+static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
+{
+ struct iwl_drv *drv = context;
+ struct iwl_fw *fw = &drv->fw;
+ struct iwl_ucode_header *ucode;
+ struct iwlwifi_opmode_table *op;
+ int err;
+ struct iwl_firmware_pieces *pieces;
+ const unsigned int api_max = drv->cfg->ucode_api_max;
+ unsigned int api_ok = drv->cfg->ucode_api_ok;
+ const unsigned int api_min = drv->cfg->ucode_api_min;
+ size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX];
+ u32 api_ver;
+ int i;
+ bool load_module = false;
+ bool usniffer_images = false;
+
+ fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
+ fw->ucode_capa.standard_phy_calibration_size =
+ IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
+ fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;
+
+ if (!api_ok)
+ api_ok = api_max;
+
+ pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
+ if (!pieces)
+ return;
+
+ if (!ucode_raw) {
+ if (drv->fw_index <= api_ok)
+ IWL_ERR(drv,
+ "request for firmware file '%s' failed.\n",
+ drv->firmware_name);
+ goto try_again;
+ }
+
+ IWL_DEBUG_INFO(drv, "Loaded firmware file '%s' (%zd bytes).\n",
+ drv->firmware_name, ucode_raw->size);
+
+ /* Make sure that we got at least the API version number */
+ if (ucode_raw->size < 4) {
+ IWL_ERR(drv, "File size way too small!\n");
+ goto try_again;
+ }
+
+ /* Data from ucode file: header followed by uCode images */
+ ucode = (struct iwl_ucode_header *)ucode_raw->data;
+
+ if (ucode->ver)
+ err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
+ else
+ err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
+ &fw->ucode_capa, &usniffer_images);
+
+ if (err)
+ goto try_again;
+
+ if (fw_has_api(&drv->fw.ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION))
+ api_ver = drv->fw.ucode_ver;
+ else
+ api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
+
+ /*
+ * api_ver should match the api version forming part of the
+ * firmware filename ... but we don't check for that and only rely
+ * on the API version read from firmware header from here on forward
+ */
+ /* no api version check required for experimental uCode */
+ if (drv->fw_index != UCODE_EXPERIMENTAL_INDEX) {
+ if (api_ver < api_min || api_ver > api_max) {
+ IWL_ERR(drv,
+ "Driver unable to support your firmware API. "
+ "Driver supports v%u, firmware is v%u.\n",
+ api_max, api_ver);
+ goto try_again;
+ }
+
+ if (api_ver < api_ok) {
+ if (api_ok != api_max)
+ IWL_ERR(drv, "Firmware has old API version, "
+ "expected v%u through v%u, got v%u.\n",
+ api_ok, api_max, api_ver);
+ else
+ IWL_ERR(drv, "Firmware has old API version, "
+ "expected v%u, got v%u.\n",
+ api_max, api_ver);
+ IWL_ERR(drv, "New firmware can be obtained from "
+ "http://www.intellinuxwireless.org/.\n");
+ }
+ }
+
+ /*
+ * In mvm uCode there is no difference between data and instructions
+ * sections.
+ */
+ if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
+ goto try_again;
+
+ /* Allocate ucode buffers for card's bus-master loading ... */
+
+ /* Runtime instructions and 2 copies of data:
+ * 1) unmodified from disk
+ * 2) backup cache for save/restore during power-downs */
+ for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
+ if (iwl_alloc_ucode(drv, pieces, i))
+ goto out_free_fw;
+
+ if (pieces->dbg_dest_tlv) {
+ drv->fw.dbg_dest_tlv =
+ kmemdup(pieces->dbg_dest_tlv,
+ sizeof(*pieces->dbg_dest_tlv) +
+ sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
+ drv->fw.dbg_dest_reg_num, GFP_KERNEL);
+
+ if (!drv->fw.dbg_dest_tlv)
+ goto out_free_fw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
+ if (pieces->dbg_conf_tlv[i]) {
+ drv->fw.dbg_conf_tlv_len[i] =
+ pieces->dbg_conf_tlv_len[i];
+ drv->fw.dbg_conf_tlv[i] =
+ kmemdup(pieces->dbg_conf_tlv[i],
+ drv->fw.dbg_conf_tlv_len[i],
+ GFP_KERNEL);
+ if (!drv->fw.dbg_conf_tlv[i])
+ goto out_free_fw;
+ }
+ }
+
+ memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz));
+
+ trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] =
+ sizeof(struct iwl_fw_dbg_trigger_missed_bcon);
+ trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0;
+ trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] =
+ sizeof(struct iwl_fw_dbg_trigger_cmd);
+ trigger_tlv_sz[FW_DBG_TRIGGER_MLME] =
+ sizeof(struct iwl_fw_dbg_trigger_mlme);
+ trigger_tlv_sz[FW_DBG_TRIGGER_STATS] =
+ sizeof(struct iwl_fw_dbg_trigger_stats);
+ trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] =
+ sizeof(struct iwl_fw_dbg_trigger_low_rssi);
+ trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] =
+ sizeof(struct iwl_fw_dbg_trigger_txq_timer);
+ trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] =
+ sizeof(struct iwl_fw_dbg_trigger_time_event);
+ trigger_tlv_sz[FW_DBG_TRIGGER_BA] =
+ sizeof(struct iwl_fw_dbg_trigger_ba);
+ trigger_tlv_sz[FW_DBG_TRIGGER_TDLS] =
+ sizeof(struct iwl_fw_dbg_trigger_tdls);
+
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
+ if (pieces->dbg_trigger_tlv[i]) {
+ /*
+ * If the trigger isn't long enough, WARN and exit.
+ * Someone is trying to debug something and he won't
+ * be able to catch the bug he is trying to chase.
+ * We'd better be noisy to be sure he knows what's
+ * going on.
+ */
+ if (WARN_ON(pieces->dbg_trigger_tlv_len[i] <
+ (trigger_tlv_sz[i] +
+ sizeof(struct iwl_fw_dbg_trigger_tlv))))
+ goto out_free_fw;
+ drv->fw.dbg_trigger_tlv_len[i] =
+ pieces->dbg_trigger_tlv_len[i];
+ drv->fw.dbg_trigger_tlv[i] =
+ kmemdup(pieces->dbg_trigger_tlv[i],
+ drv->fw.dbg_trigger_tlv_len[i],
+ GFP_KERNEL);
+ if (!drv->fw.dbg_trigger_tlv[i])
+ goto out_free_fw;
+ }
+ }
+
+ /* Now that we can no longer fail, copy information */
+
+ /*
+ * The (size - 16) / 12 formula is based on the information recorded
+ * for each event, which is of mode 1 (including timestamp) for all
+ * new microcodes that include this information.
+ */
+ fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
+ if (pieces->init_evtlog_size)
+ fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
+ else
+ fw->init_evtlog_size =
+ drv->cfg->base_params->max_event_log_size;
+ fw->init_errlog_ptr = pieces->init_errlog_ptr;
+ fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
+ if (pieces->inst_evtlog_size)
+ fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
+ else
+ fw->inst_evtlog_size =
+ drv->cfg->base_params->max_event_log_size;
+ fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
+
+ /*
+ * figure out the offset of chain noise reset and gain commands
+ * base on the size of standard phy calibration commands table size
+ */
+ if (fw->ucode_capa.standard_phy_calibration_size >
+ IWL_MAX_PHY_CALIBRATE_TBL_SIZE)
+ fw->ucode_capa.standard_phy_calibration_size =
+ IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE;
+
+ /* We have our copies now, allow OS release its copies */
+ release_firmware(ucode_raw);
+
+ mutex_lock(&iwlwifi_opmode_table_mtx);
+ if (fw->mvm_fw)
+ op = &iwlwifi_opmode_table[MVM_OP_MODE];
+ else
+ op = &iwlwifi_opmode_table[DVM_OP_MODE];
+
+ IWL_INFO(drv, "loaded firmware version %s op_mode %s\n",
+ drv->fw.fw_version, op->name);
+
+ /* add this device to the list of devices using this op_mode */
+ list_add_tail(&drv->list, &op->drv);
+
+ if (op->ops) {
+ drv->op_mode = _iwl_op_mode_start(drv, op);
+
+ if (!drv->op_mode) {
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+ goto out_unbind;
+ }
+ } else {
+ load_module = true;
+ }
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+
+ /*
+ * Complete the firmware request last so that
+ * a driver unbind (stop) doesn't run while we
+ * are doing the start() above.
+ */
+ complete(&drv->request_firmware_complete);
+
+ /*
+ * Load the module last so we don't block anything
+ * else from proceeding if the module fails to load
+ * or hangs loading.
+ */
+ if (load_module) {
+ err = request_module("%s", op->name);
+#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR
+ if (err)
+ IWL_ERR(drv,
+ "failed to load module %s (error %d), is dynamic loading enabled?\n",
+ op->name, err);
+#endif
+ }
+ kfree(pieces);
+ return;
+
+ try_again:
+ /* try next, if any */
+ release_firmware(ucode_raw);
+ if (iwl_request_firmware(drv, false))
+ goto out_unbind;
+ kfree(pieces);
+ return;
+
+ out_free_fw:
+ IWL_ERR(drv, "failed to allocate pci memory\n");
+ iwl_dealloc_ucode(drv);
+ release_firmware(ucode_raw);
+ out_unbind:
+ kfree(pieces);
+ complete(&drv->request_firmware_complete);
+ device_release_driver(drv->trans->dev);
+}
+
+struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
+ const struct iwl_cfg *cfg)
+{
+ struct iwl_drv *drv;
+ int ret;
+
+ drv = kzalloc(sizeof(*drv), GFP_KERNEL);
+ if (!drv) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ drv->trans = trans;
+ drv->dev = trans->dev;
+ drv->cfg = cfg;
+
+ init_completion(&drv->request_firmware_complete);
+ INIT_LIST_HEAD(&drv->list);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ /* Create the device debugfs entries. */
+ drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev),
+ iwl_dbgfs_root);
+
+ if (!drv->dbgfs_drv) {
+ IWL_ERR(drv, "failed to create debugfs directory\n");
+ ret = -ENOMEM;
+ goto err_free_drv;
+ }
+
+ /* Create transport layer debugfs dir */
+ drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv);
+
+ if (!drv->trans->dbgfs_dir) {
+ IWL_ERR(drv, "failed to create transport debugfs directory\n");
+ ret = -ENOMEM;
+ goto err_free_dbgfs;
+ }
+#endif
+
+ ret = iwl_request_firmware(drv, true);
+ if (ret) {
+ IWL_ERR(trans, "Couldn't request the fw\n");
+ goto err_fw;
+ }
+
+ return drv;
+
+err_fw:
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+err_free_dbgfs:
+ debugfs_remove_recursive(drv->dbgfs_drv);
+err_free_drv:
+#endif
+ kfree(drv);
+err:
+ return ERR_PTR(ret);
+}
+
+void iwl_drv_stop(struct iwl_drv *drv)
+{
+ wait_for_completion(&drv->request_firmware_complete);
+
+ _iwl_op_mode_stop(drv);
+
+ iwl_dealloc_ucode(drv);
+
+ mutex_lock(&iwlwifi_opmode_table_mtx);
+ /*
+ * List is empty (this item wasn't added)
+ * when firmware loading failed -- in that
+ * case we can't remove it from any list.
+ */
+ if (!list_empty(&drv->list))
+ list_del(&drv->list);
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ debugfs_remove_recursive(drv->dbgfs_drv);
+#endif
+
+ kfree(drv);
+}
+
+
+/* shared module parameters */
+struct iwl_mod_params iwlwifi_mod_params = {
+ .restart_fw = true,
+ .bt_coex_active = true,
+ .power_level = IWL_POWER_INDEX_1,
+ .d0i3_disable = true,
+ .d0i3_entry_delay = 1000,
+#ifndef CONFIG_IWLWIFI_UAPSD
+ .uapsd_disable = true,
+#endif /* CONFIG_IWLWIFI_UAPSD */
+ /* the rest are 0 by default */
+};
+IWL_EXPORT_SYMBOL(iwlwifi_mod_params);
+
+int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
+{
+ int i;
+ struct iwl_drv *drv;
+ struct iwlwifi_opmode_table *op;
+
+ mutex_lock(&iwlwifi_opmode_table_mtx);
+ for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
+ op = &iwlwifi_opmode_table[i];
+ if (strcmp(op->name, name))
+ continue;
+ op->ops = ops;
+ /* TODO: need to handle exceptional case */
+ list_for_each_entry(drv, &op->drv, list)
+ drv->op_mode = _iwl_op_mode_start(drv, op);
+
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+ return 0;
+ }
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+ return -EIO;
+}
+IWL_EXPORT_SYMBOL(iwl_opmode_register);
+
+void iwl_opmode_deregister(const char *name)
+{
+ int i;
+ struct iwl_drv *drv;
+
+ mutex_lock(&iwlwifi_opmode_table_mtx);
+ for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
+ if (strcmp(iwlwifi_opmode_table[i].name, name))
+ continue;
+ iwlwifi_opmode_table[i].ops = NULL;
+
+ /* call the stop routine for all devices */
+ list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list)
+ _iwl_op_mode_stop(drv);
+
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+ return;
+ }
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+}
+IWL_EXPORT_SYMBOL(iwl_opmode_deregister);
+
+static int __init iwl_drv_init(void)
+{
+ int i;
+
+ mutex_init(&iwlwifi_opmode_table_mtx);
+
+ for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++)
+ INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv);
+
+ pr_info(DRV_DESCRIPTION "\n");
+ pr_info(DRV_COPYRIGHT "\n");
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ /* Create the root of iwlwifi debugfs subsystem. */
+ iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL);
+
+ if (!iwl_dbgfs_root)
+ return -EFAULT;
+#endif
+
+ return iwl_pci_register_driver();
+}
+module_init(iwl_drv_init);
+
+static void __exit iwl_drv_exit(void)
+{
+ iwl_pci_unregister_driver();
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ debugfs_remove_recursive(iwl_dbgfs_root);
+#endif
+}
+module_exit(iwl_drv_exit);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+module_param_named(debug, iwlwifi_mod_params.debug_level, uint,
+ S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "debug output mask");
+#endif
+
+module_param_named(swcrypto, iwlwifi_mod_params.sw_crypto, int, S_IRUGO);
+MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
+module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO);
+MODULE_PARM_DESC(11n_disable,
+ "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX");
+module_param_named(amsdu_size, iwlwifi_mod_params.amsdu_size,
+ int, S_IRUGO);
+MODULE_PARM_DESC(amsdu_size, "amsdu size 0:4K 1:8K 2:12K (default 0)");
+module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO);
+MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)");
+
+module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling,
+ int, S_IRUGO);
+MODULE_PARM_DESC(antenna_coupling,
+ "specify antenna coupling in dB (default: 0 dB)");
+
+module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
+MODULE_PARM_DESC(nvm_file, "NVM file name");
+
+module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");
+
+module_param_named(lar_disable, iwlwifi_mod_params.lar_disable,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
+
+module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
+ bool, S_IRUGO | S_IWUSR);
+#ifdef CONFIG_IWLWIFI_UAPSD
+MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)");
+#else
+MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: Y)");
+#endif
+
+/*
+ * set bt_coex_active to true, uCode will do kill/defer
+ * every time the priority line is asserted (BT is sending signals on the
+ * priority line in the PCIx).
+ * set bt_coex_active to false, uCode will ignore the BT activity and
+ * perform the normal operation
+ *
+ * User might experience transmit issue on some platform due to WiFi/BT
+ * co-exist problem. The possible behaviors are:
+ * Able to scan and finding all the available AP
+ * Not able to associate with any AP
+ * On those platforms, WiFi communication can be restored by set
+ * "bt_coex_active" module parameter to "false"
+ *
+ * default: bt_coex_active = true (BT_COEX_ENABLE)
+ */
+module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)");
+
+module_param_named(led_mode, iwlwifi_mod_params.led_mode, int, S_IRUGO);
+MODULE_PARM_DESC(led_mode, "0=system default, "
+ "1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)");
+
+module_param_named(power_save, iwlwifi_mod_params.power_save,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(power_save,
+ "enable WiFi power management (default: disable)");
+
+module_param_named(power_level, iwlwifi_mod_params.power_level,
+ int, S_IRUGO);
+MODULE_PARM_DESC(power_level,
+ "default power save level (range from 1 - 5, default: 1)");
+
+module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
+MODULE_PARM_DESC(fw_monitor,
+ "firmware monitor - to debug FW (default: false - needs lots of memory)");
+
+module_param_named(d0i3_timeout, iwlwifi_mod_params.d0i3_entry_delay,
+ uint, S_IRUGO);
+MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)");
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
new file mode 100644
index 000000000000..f6eacfdbc265
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
@@ -0,0 +1,155 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_drv_h__
+#define __iwl_drv_h__
+#include <linux/export.h>
+
+/* for all modules */
+#define DRV_NAME "iwlwifi"
+#define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation"
+#define DRV_AUTHOR "<linuxwifi@intel.com>"
+
+/* radio config bits (actual values from NVM definition) */
+#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */
+#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */
+#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */
+#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */
+#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */
+#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
+
+#define NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(x) (x & 0xF)
+#define NVM_RF_CFG_DASH_MSK_FAMILY_8000(x) ((x >> 4) & 0xF)
+#define NVM_RF_CFG_STEP_MSK_FAMILY_8000(x) ((x >> 8) & 0xF)
+#define NVM_RF_CFG_TYPE_MSK_FAMILY_8000(x) ((x >> 12) & 0xFFF)
+#define NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(x) ((x >> 24) & 0xF)
+#define NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(x) ((x >> 28) & 0xF)
+
+/**
+ * DOC: Driver system flows - drv component
+ *
+ * This component implements the system flows such as bus enumeration, bus
+ * removal. Bus dependent parts of system flows (such as iwl_pci_probe) are in
+ * bus specific files (transport files). This is the code that is common among
+ * different buses.
+ *
+ * This component is also in charge of managing the several implementations of
+ * the wifi flows: it will allow to have several fw API implementation. These
+ * different implementations will differ in the way they implement mac80211's
+ * handlers too.
+
+ * The init flow wrt to the drv component looks like this:
+ * 1) The bus specific component is called from module_init
+ * 2) The bus specific component registers the bus driver
+ * 3) The bus driver calls the probe function
+ * 4) The bus specific component configures the bus
+ * 5) The bus specific component calls to the drv bus agnostic part
+ * (iwl_drv_start)
+ * 6) iwl_drv_start fetches the fw ASYNC, iwl_req_fw_callback
+ * 7) iwl_req_fw_callback parses the fw file
+ * 8) iwl_req_fw_callback starts the wifi implementation to matches the fw
+ */
+
+struct iwl_drv;
+struct iwl_trans;
+struct iwl_cfg;
+/**
+ * iwl_drv_start - start the drv
+ *
+ * @trans_ops: the ops of the transport
+ * @cfg: device specific constants / virtual functions
+ *
+ * starts the driver: fetches the firmware. This should be called by bus
+ * specific system flows implementations. For example, the bus specific probe
+ * function should do bus related operations only, and then call to this
+ * function. It returns the driver object or %NULL if an error occurred.
+ */
+struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
+ const struct iwl_cfg *cfg);
+
+/**
+ * iwl_drv_stop - stop the drv
+ *
+ * @drv:
+ *
+ * Stop the driver. This should be called by bus specific system flows
+ * implementations. For example, the bus specific remove function should first
+ * call this function and then do the bus related operations only.
+ */
+void iwl_drv_stop(struct iwl_drv *drv);
+
+/*
+ * exported symbol management
+ *
+ * The driver can be split into multiple modules, in which case some symbols
+ * must be exported for the sub-modules. However, if it's not split and
+ * everything is built-in, then we can avoid that.
+ */
+#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR
+#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_GPL(sym)
+#else
+#define IWL_EXPORT_SYMBOL(sym)
+#endif
+
+#endif /* __iwl_drv_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
new file mode 100644
index 000000000000..c15f5be85197
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
@@ -0,0 +1,947 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include "iwl-drv.h"
+#include "iwl-modparams.h"
+#include "iwl-eeprom-parse.h"
+
+/* EEPROM offset definitions */
+
+/* indirect access definitions */
+#define ADDRESS_MSK 0x0000FFFF
+#define INDIRECT_TYPE_MSK 0x000F0000
+#define INDIRECT_HOST 0x00010000
+#define INDIRECT_GENERAL 0x00020000
+#define INDIRECT_REGULATORY 0x00030000
+#define INDIRECT_CALIBRATION 0x00040000
+#define INDIRECT_PROCESS_ADJST 0x00050000
+#define INDIRECT_OTHERS 0x00060000
+#define INDIRECT_TXP_LIMIT 0x00070000
+#define INDIRECT_TXP_LIMIT_SIZE 0x00080000
+#define INDIRECT_ADDRESS 0x00100000
+
+/* corresponding link offsets in EEPROM */
+#define EEPROM_LINK_HOST (2*0x64)
+#define EEPROM_LINK_GENERAL (2*0x65)
+#define EEPROM_LINK_REGULATORY (2*0x66)
+#define EEPROM_LINK_CALIBRATION (2*0x67)
+#define EEPROM_LINK_PROCESS_ADJST (2*0x68)
+#define EEPROM_LINK_OTHERS (2*0x69)
+#define EEPROM_LINK_TXP_LIMIT (2*0x6a)
+#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b)
+
+/* General */
+#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */
+#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */
+#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */
+#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */
+#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */
+#define EEPROM_VERSION (2*0x44) /* 2 bytes */
+#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */
+#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */
+#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */
+#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */
+
+/* calibration */
+struct iwl_eeprom_calib_hdr {
+ u8 version;
+ u8 pa_type;
+ __le16 voltage;
+} __packed;
+
+#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION)
+#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL)
+
+/* temperature */
+#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL)
+#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL)
+
+/* SKU Capabilities (actual values from EEPROM definition) */
+enum eeprom_sku_bits {
+ EEPROM_SKU_CAP_BAND_24GHZ = BIT(4),
+ EEPROM_SKU_CAP_BAND_52GHZ = BIT(5),
+ EEPROM_SKU_CAP_11N_ENABLE = BIT(6),
+ EEPROM_SKU_CAP_AMT_ENABLE = BIT(7),
+ EEPROM_SKU_CAP_IPAN_ENABLE = BIT(8)
+};
+
+/* radio config bits (actual values from EEPROM definition) */
+#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */
+#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */
+#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */
+#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */
+#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */
+#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
+
+
+/*
+ * EEPROM bands
+ * These are the channel numbers from each band in the order
+ * that they are stored in the EEPROM band information. Note
+ * that EEPROM bands aren't the same as mac80211 bands, and
+ * there are even special "ht40 bands" in the EEPROM.
+ */
+static const u8 iwl_eeprom_band_1[14] = { /* 2.4 GHz */
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
+};
+
+static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */
+ 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16
+};
+
+static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */
+ 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
+};
+
+static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */
+ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
+};
+
+static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */
+ 145, 149, 153, 157, 161, 165
+};
+
+static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */
+ 1, 2, 3, 4, 5, 6, 7
+};
+
+static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */
+ 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
+};
+
+#define IWL_NUM_CHANNELS (ARRAY_SIZE(iwl_eeprom_band_1) + \
+ ARRAY_SIZE(iwl_eeprom_band_2) + \
+ ARRAY_SIZE(iwl_eeprom_band_3) + \
+ ARRAY_SIZE(iwl_eeprom_band_4) + \
+ ARRAY_SIZE(iwl_eeprom_band_5))
+
+/* rate data (static) */
+static struct ieee80211_rate iwl_cfg80211_rates[] = {
+ { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
+ { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+ { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+ { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+ { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
+ { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
+ { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
+ { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
+ { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
+ { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
+ { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
+ { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
+};
+#define RATES_24_OFFS 0
+#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates)
+#define RATES_52_OFFS 4
+#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS)
+
+/* EEPROM reading functions */
+
+static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset)
+{
+ if (WARN_ON(offset + sizeof(u16) > eeprom_size))
+ return 0;
+ return le16_to_cpup((__le16 *)(eeprom + offset));
+}
+
+static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size,
+ u32 address)
+{
+ u16 offset = 0;
+
+ if ((address & INDIRECT_ADDRESS) == 0)
+ return address;
+
+ switch (address & INDIRECT_TYPE_MSK) {
+ case INDIRECT_HOST:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_HOST);
+ break;
+ case INDIRECT_GENERAL:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_GENERAL);
+ break;
+ case INDIRECT_REGULATORY:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_REGULATORY);
+ break;
+ case INDIRECT_TXP_LIMIT:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_TXP_LIMIT);
+ break;
+ case INDIRECT_TXP_LIMIT_SIZE:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_TXP_LIMIT_SIZE);
+ break;
+ case INDIRECT_CALIBRATION:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_CALIBRATION);
+ break;
+ case INDIRECT_PROCESS_ADJST:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_PROCESS_ADJST);
+ break;
+ case INDIRECT_OTHERS:
+ offset = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_LINK_OTHERS);
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ /* translate the offset from words to byte */
+ return (address & ADDRESS_MSK) + (offset << 1);
+}
+
+static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size,
+ u32 offset)
+{
+ u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset);
+
+ if (WARN_ON(address >= eeprom_size))
+ return NULL;
+
+ return &eeprom[address];
+}
+
+static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size,
+ struct iwl_nvm_data *data)
+{
+ struct iwl_eeprom_calib_hdr *hdr;
+
+ hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size,
+ EEPROM_CALIB_ALL);
+ if (!hdr)
+ return -ENODATA;
+ data->calib_version = hdr->version;
+ data->calib_voltage = hdr->voltage;
+
+ return 0;
+}
+
+/**
+ * enum iwl_eeprom_channel_flags - channel flags in EEPROM
+ * @EEPROM_CHANNEL_VALID: channel is usable for this SKU/geo
+ * @EEPROM_CHANNEL_IBSS: usable as an IBSS channel
+ * @EEPROM_CHANNEL_ACTIVE: active scanning allowed
+ * @EEPROM_CHANNEL_RADAR: radar detection required
+ * @EEPROM_CHANNEL_WIDE: 20 MHz channel okay (?)
+ * @EEPROM_CHANNEL_DFS: dynamic freq selection candidate
+ */
+enum iwl_eeprom_channel_flags {
+ EEPROM_CHANNEL_VALID = BIT(0),
+ EEPROM_CHANNEL_IBSS = BIT(1),
+ EEPROM_CHANNEL_ACTIVE = BIT(3),
+ EEPROM_CHANNEL_RADAR = BIT(4),
+ EEPROM_CHANNEL_WIDE = BIT(5),
+ EEPROM_CHANNEL_DFS = BIT(7),
+};
+
+/**
+ * struct iwl_eeprom_channel - EEPROM channel data
+ * @flags: %EEPROM_CHANNEL_* flags
+ * @max_power_avg: max power (in dBm) on this channel, at most 31 dBm
+ */
+struct iwl_eeprom_channel {
+ u8 flags;
+ s8 max_power_avg;
+} __packed;
+
+
+enum iwl_eeprom_enhanced_txpwr_flags {
+ IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0),
+ IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1),
+ IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2),
+ IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3),
+ IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4),
+ IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5),
+ IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6),
+ IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7),
+};
+
+/**
+ * iwl_eeprom_enhanced_txpwr structure
+ * @flags: entry flags
+ * @channel: channel number
+ * @chain_a_max_pwr: chain a max power in 1/2 dBm
+ * @chain_b_max_pwr: chain b max power in 1/2 dBm
+ * @chain_c_max_pwr: chain c max power in 1/2 dBm
+ * @delta_20_in_40: 20-in-40 deltas (hi/lo)
+ * @mimo2_max_pwr: mimo2 max power in 1/2 dBm
+ * @mimo3_max_pwr: mimo3 max power in 1/2 dBm
+ *
+ * This structure presents the enhanced regulatory tx power limit layout
+ * in an EEPROM image.
+ */
+struct iwl_eeprom_enhanced_txpwr {
+ u8 flags;
+ u8 channel;
+ s8 chain_a_max;
+ s8 chain_b_max;
+ s8 chain_c_max;
+ u8 delta_20_in_40;
+ s8 mimo2_max;
+ s8 mimo3_max;
+} __packed;
+
+static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data,
+ struct iwl_eeprom_enhanced_txpwr *txp)
+{
+ s8 result = 0; /* (.5 dBm) */
+
+ /* Take the highest tx power from any valid chains */
+ if (data->valid_tx_ant & ANT_A && txp->chain_a_max > result)
+ result = txp->chain_a_max;
+
+ if (data->valid_tx_ant & ANT_B && txp->chain_b_max > result)
+ result = txp->chain_b_max;
+
+ if (data->valid_tx_ant & ANT_C && txp->chain_c_max > result)
+ result = txp->chain_c_max;
+
+ if ((data->valid_tx_ant == ANT_AB ||
+ data->valid_tx_ant == ANT_BC ||
+ data->valid_tx_ant == ANT_AC) && txp->mimo2_max > result)
+ result = txp->mimo2_max;
+
+ if (data->valid_tx_ant == ANT_ABC && txp->mimo3_max > result)
+ result = txp->mimo3_max;
+
+ return result;
+}
+
+#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT)
+#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr)
+#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE)
+
+#define TXP_CHECK_AND_PRINT(x) \
+ ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) ? # x " " : "")
+
+static void
+iwl_eeprom_enh_txp_read_element(struct iwl_nvm_data *data,
+ struct iwl_eeprom_enhanced_txpwr *txp,
+ int n_channels, s8 max_txpower_avg)
+{
+ int ch_idx;
+ enum ieee80211_band band;
+
+ band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ?
+ IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
+
+ for (ch_idx = 0; ch_idx < n_channels; ch_idx++) {
+ struct ieee80211_channel *chan = &data->channels[ch_idx];
+
+ /* update matching channel or from common data only */
+ if (txp->channel != 0 && chan->hw_value != txp->channel)
+ continue;
+
+ /* update matching band only */
+ if (band != chan->band)
+ continue;
+
+ if (chan->max_power < max_txpower_avg &&
+ !(txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ))
+ chan->max_power = max_txpower_avg;
+ }
+}
+
+static void iwl_eeprom_enhanced_txpower(struct device *dev,
+ struct iwl_nvm_data *data,
+ const u8 *eeprom, size_t eeprom_size,
+ int n_channels)
+{
+ struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
+ int idx, entries;
+ __le16 *txp_len;
+ s8 max_txp_avg_halfdbm;
+
+ BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8);
+
+ /* the length is in 16-bit words, but we want entries */
+ txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size,
+ EEPROM_TXP_SZ_OFFS);
+ entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN;
+
+ txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size,
+ EEPROM_TXP_OFFS);
+
+ for (idx = 0; idx < entries; idx++) {
+ txp = &txp_array[idx];
+ /* skip invalid entries */
+ if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID))
+ continue;
+
+ IWL_DEBUG_EEPROM(dev, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n",
+ (txp->channel && (txp->flags &
+ IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ?
+ "Common " : (txp->channel) ?
+ "Channel" : "Common",
+ (txp->channel),
+ TXP_CHECK_AND_PRINT(VALID),
+ TXP_CHECK_AND_PRINT(BAND_52G),
+ TXP_CHECK_AND_PRINT(OFDM),
+ TXP_CHECK_AND_PRINT(40MHZ),
+ TXP_CHECK_AND_PRINT(HT_AP),
+ TXP_CHECK_AND_PRINT(RES1),
+ TXP_CHECK_AND_PRINT(RES2),
+ TXP_CHECK_AND_PRINT(COMMON_TYPE),
+ txp->flags);
+ IWL_DEBUG_EEPROM(dev,
+ "\t\t chain_A: %d chain_B: %d chain_C: %d\n",
+ txp->chain_a_max, txp->chain_b_max,
+ txp->chain_c_max);
+ IWL_DEBUG_EEPROM(dev,
+ "\t\t MIMO2: %d MIMO3: %d High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n",
+ txp->mimo2_max, txp->mimo3_max,
+ ((txp->delta_20_in_40 & 0xf0) >> 4),
+ (txp->delta_20_in_40 & 0x0f));
+
+ max_txp_avg_halfdbm = iwl_get_max_txpwr_half_dbm(data, txp);
+
+ iwl_eeprom_enh_txp_read_element(data, txp, n_channels,
+ DIV_ROUND_UP(max_txp_avg_halfdbm, 2));
+
+ if (max_txp_avg_halfdbm > data->max_tx_pwr_half_dbm)
+ data->max_tx_pwr_half_dbm = max_txp_avg_halfdbm;
+ }
+}
+
+static void iwl_init_band_reference(const struct iwl_cfg *cfg,
+ const u8 *eeprom, size_t eeprom_size,
+ int eeprom_band, int *eeprom_ch_count,
+ const struct iwl_eeprom_channel **ch_info,
+ const u8 **eeprom_ch_array)
+{
+ u32 offset = cfg->eeprom_params->regulatory_bands[eeprom_band - 1];
+
+ offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY;
+
+ *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset);
+
+ switch (eeprom_band) {
+ case 1: /* 2.4GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1);
+ *eeprom_ch_array = iwl_eeprom_band_1;
+ break;
+ case 2: /* 4.9GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2);
+ *eeprom_ch_array = iwl_eeprom_band_2;
+ break;
+ case 3: /* 5.2GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3);
+ *eeprom_ch_array = iwl_eeprom_band_3;
+ break;
+ case 4: /* 5.5GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4);
+ *eeprom_ch_array = iwl_eeprom_band_4;
+ break;
+ case 5: /* 5.7GHz band */
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5);
+ *eeprom_ch_array = iwl_eeprom_band_5;
+ break;
+ case 6: /* 2.4GHz ht40 channels */
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6);
+ *eeprom_ch_array = iwl_eeprom_band_6;
+ break;
+ case 7: /* 5 GHz ht40 channels */
+ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7);
+ *eeprom_ch_array = iwl_eeprom_band_7;
+ break;
+ default:
+ *eeprom_ch_count = 0;
+ *eeprom_ch_array = NULL;
+ WARN_ON(1);
+ }
+}
+
+#define CHECK_AND_PRINT(x) \
+ ((eeprom_ch->flags & EEPROM_CHANNEL_##x) ? # x " " : "")
+
+static void iwl_mod_ht40_chan_info(struct device *dev,
+ struct iwl_nvm_data *data, int n_channels,
+ enum ieee80211_band band, u16 channel,
+ const struct iwl_eeprom_channel *eeprom_ch,
+ u8 clear_ht40_extension_channel)
+{
+ struct ieee80211_channel *chan = NULL;
+ int i;
+
+ for (i = 0; i < n_channels; i++) {
+ if (data->channels[i].band != band)
+ continue;
+ if (data->channels[i].hw_value != channel)
+ continue;
+ chan = &data->channels[i];
+ break;
+ }
+
+ if (!chan)
+ return;
+
+ IWL_DEBUG_EEPROM(dev,
+ "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
+ channel,
+ band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4",
+ CHECK_AND_PRINT(IBSS),
+ CHECK_AND_PRINT(ACTIVE),
+ CHECK_AND_PRINT(RADAR),
+ CHECK_AND_PRINT(WIDE),
+ CHECK_AND_PRINT(DFS),
+ eeprom_ch->flags,
+ eeprom_ch->max_power_avg,
+ ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) &&
+ !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? ""
+ : "not ");
+
+ if (eeprom_ch->flags & EEPROM_CHANNEL_VALID)
+ chan->flags &= ~clear_ht40_extension_channel;
+}
+
+#define CHECK_AND_PRINT_I(x) \
+ ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "")
+
+static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ const u8 *eeprom, size_t eeprom_size)
+{
+ int band, ch_idx;
+ const struct iwl_eeprom_channel *eeprom_ch_info;
+ const u8 *eeprom_ch_array;
+ int eeprom_ch_count;
+ int n_channels = 0;
+
+ /*
+ * Loop through the 5 EEPROM bands and add them to the parse list
+ */
+ for (band = 1; band <= 5; band++) {
+ struct ieee80211_channel *channel;
+
+ iwl_init_band_reference(cfg, eeprom, eeprom_size, band,
+ &eeprom_ch_count, &eeprom_ch_info,
+ &eeprom_ch_array);
+
+ /* Loop through each band adding each of the channels */
+ for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) {
+ const struct iwl_eeprom_channel *eeprom_ch;
+
+ eeprom_ch = &eeprom_ch_info[ch_idx];
+
+ if (!(eeprom_ch->flags & EEPROM_CHANNEL_VALID)) {
+ IWL_DEBUG_EEPROM(dev,
+ "Ch. %d Flags %x [%sGHz] - No traffic\n",
+ eeprom_ch_array[ch_idx],
+ eeprom_ch_info[ch_idx].flags,
+ (band != 1) ? "5.2" : "2.4");
+ continue;
+ }
+
+ channel = &data->channels[n_channels];
+ n_channels++;
+
+ channel->hw_value = eeprom_ch_array[ch_idx];
+ channel->band = (band == 1) ? IEEE80211_BAND_2GHZ
+ : IEEE80211_BAND_5GHZ;
+ channel->center_freq =
+ ieee80211_channel_to_frequency(
+ channel->hw_value, channel->band);
+
+ /* set no-HT40, will enable as appropriate later */
+ channel->flags = IEEE80211_CHAN_NO_HT40;
+
+ if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS))
+ channel->flags |= IEEE80211_CHAN_NO_IR;
+
+ if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE))
+ channel->flags |= IEEE80211_CHAN_NO_IR;
+
+ if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR)
+ channel->flags |= IEEE80211_CHAN_RADAR;
+
+ /* Initialize regulatory-based run-time data */
+ channel->max_power =
+ eeprom_ch_info[ch_idx].max_power_avg;
+ IWL_DEBUG_EEPROM(dev,
+ "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
+ channel->hw_value,
+ (band != 1) ? "5.2" : "2.4",
+ CHECK_AND_PRINT_I(VALID),
+ CHECK_AND_PRINT_I(IBSS),
+ CHECK_AND_PRINT_I(ACTIVE),
+ CHECK_AND_PRINT_I(RADAR),
+ CHECK_AND_PRINT_I(WIDE),
+ CHECK_AND_PRINT_I(DFS),
+ eeprom_ch_info[ch_idx].flags,
+ eeprom_ch_info[ch_idx].max_power_avg,
+ ((eeprom_ch_info[ch_idx].flags &
+ EEPROM_CHANNEL_IBSS) &&
+ !(eeprom_ch_info[ch_idx].flags &
+ EEPROM_CHANNEL_RADAR))
+ ? "" : "not ");
+ }
+ }
+
+ if (cfg->eeprom_params->enhanced_txpower) {
+ /*
+ * for newer device (6000 series and up)
+ * EEPROM contain enhanced tx power information
+ * driver need to process addition information
+ * to determine the max channel tx power limits
+ */
+ iwl_eeprom_enhanced_txpower(dev, data, eeprom, eeprom_size,
+ n_channels);
+ } else {
+ /* All others use data from channel map */
+ int i;
+
+ data->max_tx_pwr_half_dbm = -128;
+
+ for (i = 0; i < n_channels; i++)
+ data->max_tx_pwr_half_dbm =
+ max_t(s8, data->max_tx_pwr_half_dbm,
+ data->channels[i].max_power * 2);
+ }
+
+ /* Check if we do have HT40 channels */
+ if (cfg->eeprom_params->regulatory_bands[5] ==
+ EEPROM_REGULATORY_BAND_NO_HT40 &&
+ cfg->eeprom_params->regulatory_bands[6] ==
+ EEPROM_REGULATORY_BAND_NO_HT40)
+ return n_channels;
+
+ /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */
+ for (band = 6; band <= 7; band++) {
+ enum ieee80211_band ieeeband;
+
+ iwl_init_band_reference(cfg, eeprom, eeprom_size, band,
+ &eeprom_ch_count, &eeprom_ch_info,
+ &eeprom_ch_array);
+
+ /* EEPROM band 6 is 2.4, band 7 is 5 GHz */
+ ieeeband = (band == 6) ? IEEE80211_BAND_2GHZ
+ : IEEE80211_BAND_5GHZ;
+
+ /* Loop through each band adding each of the channels */
+ for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) {
+ /* Set up driver's info for lower half */
+ iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband,
+ eeprom_ch_array[ch_idx],
+ &eeprom_ch_info[ch_idx],
+ IEEE80211_CHAN_NO_HT40PLUS);
+
+ /* Set up driver's info for upper half */
+ iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband,
+ eeprom_ch_array[ch_idx] + 4,
+ &eeprom_ch_info[ch_idx],
+ IEEE80211_CHAN_NO_HT40MINUS);
+ }
+ }
+
+ return n_channels;
+}
+
+int iwl_init_sband_channels(struct iwl_nvm_data *data,
+ struct ieee80211_supported_band *sband,
+ int n_channels, enum ieee80211_band band)
+{
+ struct ieee80211_channel *chan = &data->channels[0];
+ int n = 0, idx = 0;
+
+ while (idx < n_channels && chan->band != band)
+ chan = &data->channels[++idx];
+
+ sband->channels = &data->channels[idx];
+
+ while (idx < n_channels && chan->band == band) {
+ chan = &data->channels[++idx];
+ n++;
+ }
+
+ sband->n_channels = n;
+
+ return n;
+}
+
+#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
+#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
+
+void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ struct ieee80211_sta_ht_cap *ht_info,
+ enum ieee80211_band band,
+ u8 tx_chains, u8 rx_chains)
+{
+ int max_bit_rate = 0;
+
+ tx_chains = hweight8(tx_chains);
+ if (cfg->rx_with_siso_diversity)
+ rx_chains = 1;
+ else
+ rx_chains = hweight8(rx_chains);
+
+ if (!(data->sku_cap_11n_enable) || !cfg->ht_params) {
+ ht_info->ht_supported = false;
+ return;
+ }
+
+ if (data->sku_cap_mimo_disabled)
+ rx_chains = 1;
+
+ ht_info->ht_supported = true;
+ ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
+
+ if (cfg->ht_params->stbc) {
+ ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+
+ if (tx_chains > 1)
+ ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+ }
+
+ if (cfg->ht_params->ldpc)
+ ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+ if (iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K)
+ ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+ ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent;
+ ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
+
+ ht_info->mcs.rx_mask[0] = 0xFF;
+ if (rx_chains >= 2)
+ ht_info->mcs.rx_mask[1] = 0xFF;
+ if (rx_chains >= 3)
+ ht_info->mcs.rx_mask[2] = 0xFF;
+
+ if (cfg->ht_params->ht_greenfield_support)
+ ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+
+ max_bit_rate = MAX_BIT_RATE_20_MHZ;
+
+ if (cfg->ht_params->ht40_bands & BIT(band)) {
+ ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
+ max_bit_rate = MAX_BIT_RATE_40_MHZ;
+ }
+
+ /* Highest supported Rx data rate */
+ max_bit_rate *= rx_chains;
+ WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
+ ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
+
+ /* Tx MCS capabilities */
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ if (tx_chains != rx_chains) {
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
+ ht_info->mcs.tx_params |= ((tx_chains - 1) <<
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
+ }
+}
+
+static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ const u8 *eeprom, size_t eeprom_size)
+{
+ int n_channels = iwl_init_channel_map(dev, cfg, data,
+ eeprom, eeprom_size);
+ int n_used = 0;
+ struct ieee80211_supported_band *sband;
+
+ sband = &data->bands[IEEE80211_BAND_2GHZ];
+ sband->band = IEEE80211_BAND_2GHZ;
+ sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
+ sband->n_bitrates = N_RATES_24;
+ n_used += iwl_init_sband_channels(data, sband, n_channels,
+ IEEE80211_BAND_2GHZ);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
+ data->valid_tx_ant, data->valid_rx_ant);
+
+ sband = &data->bands[IEEE80211_BAND_5GHZ];
+ sband->band = IEEE80211_BAND_5GHZ;
+ sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
+ sband->n_bitrates = N_RATES_52;
+ n_used += iwl_init_sband_channels(data, sband, n_channels,
+ IEEE80211_BAND_5GHZ);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
+ data->valid_tx_ant, data->valid_rx_ant);
+
+ if (n_channels != n_used)
+ IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n",
+ n_used, n_channels);
+}
+
+/* EEPROM data functions */
+
+struct iwl_nvm_data *
+iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
+ const u8 *eeprom, size_t eeprom_size)
+{
+ struct iwl_nvm_data *data;
+ const void *tmp;
+ u16 radio_cfg, sku;
+
+ if (WARN_ON(!cfg || !cfg->eeprom_params))
+ return NULL;
+
+ data = kzalloc(sizeof(*data) +
+ sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS,
+ GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ /* get MAC address(es) */
+ tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_MAC_ADDRESS);
+ if (!tmp)
+ goto err_free;
+ memcpy(data->hw_addr, tmp, ETH_ALEN);
+ data->n_hw_addrs = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_NUM_MAC_ADDRESS);
+
+ if (iwl_eeprom_read_calib(eeprom, eeprom_size, data))
+ goto err_free;
+
+ tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_XTAL);
+ if (!tmp)
+ goto err_free;
+ memcpy(data->xtal_calib, tmp, sizeof(data->xtal_calib));
+
+ tmp = iwl_eeprom_query_addr(eeprom, eeprom_size,
+ EEPROM_RAW_TEMPERATURE);
+ if (!tmp)
+ goto err_free;
+ data->raw_temperature = *(__le16 *)tmp;
+
+ tmp = iwl_eeprom_query_addr(eeprom, eeprom_size,
+ EEPROM_KELVIN_TEMPERATURE);
+ if (!tmp)
+ goto err_free;
+ data->kelvin_temperature = *(__le16 *)tmp;
+ data->kelvin_voltage = *((__le16 *)tmp + 1);
+
+ radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_RADIO_CONFIG);
+ data->radio_cfg_dash = EEPROM_RF_CFG_DASH_MSK(radio_cfg);
+ data->radio_cfg_pnum = EEPROM_RF_CFG_PNUM_MSK(radio_cfg);
+ data->radio_cfg_step = EEPROM_RF_CFG_STEP_MSK(radio_cfg);
+ data->radio_cfg_type = EEPROM_RF_CFG_TYPE_MSK(radio_cfg);
+ data->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg);
+ data->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg);
+
+ sku = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_SKU_CAP);
+ data->sku_cap_11n_enable = sku & EEPROM_SKU_CAP_11N_ENABLE;
+ data->sku_cap_amt_enable = sku & EEPROM_SKU_CAP_AMT_ENABLE;
+ data->sku_cap_band_24GHz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ;
+ data->sku_cap_band_52GHz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ;
+ data->sku_cap_ipan_enable = sku & EEPROM_SKU_CAP_IPAN_ENABLE;
+ if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
+ data->sku_cap_11n_enable = false;
+
+ data->nvm_version = iwl_eeprom_query16(eeprom, eeprom_size,
+ EEPROM_VERSION);
+
+ /* check overrides (some devices have wrong EEPROM) */
+ if (cfg->valid_tx_ant)
+ data->valid_tx_ant = cfg->valid_tx_ant;
+ if (cfg->valid_rx_ant)
+ data->valid_rx_ant = cfg->valid_rx_ant;
+
+ if (!data->valid_tx_ant || !data->valid_rx_ant) {
+ IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n",
+ data->valid_tx_ant, data->valid_rx_ant);
+ goto err_free;
+ }
+
+ iwl_init_sbands(dev, cfg, data, eeprom, eeprom_size);
+
+ return data;
+ err_free:
+ kfree(data);
+ return NULL;
+}
+IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data);
+
+/* helper functions */
+int iwl_nvm_check_version(struct iwl_nvm_data *data,
+ struct iwl_trans *trans)
+{
+ if (data->nvm_version >= trans->cfg->nvm_ver ||
+ data->calib_version >= trans->cfg->nvm_calib_ver) {
+ IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n",
+ data->nvm_version, data->calib_version);
+ return 0;
+ }
+
+ IWL_ERR(trans,
+ "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
+ data->nvm_version, trans->cfg->nvm_ver,
+ data->calib_version, trans->cfg->nvm_calib_ver);
+ return -EINVAL;
+}
+IWL_EXPORT_SYMBOL(iwl_nvm_check_version);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
new file mode 100644
index 000000000000..ad2b834668ff
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
@@ -0,0 +1,144 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#ifndef __iwl_eeprom_parse_h__
+#define __iwl_eeprom_parse_h__
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+#include "iwl-trans.h"
+
+struct iwl_nvm_data {
+ int n_hw_addrs;
+ u8 hw_addr[ETH_ALEN];
+
+ u8 calib_version;
+ __le16 calib_voltage;
+
+ __le16 raw_temperature;
+ __le16 kelvin_temperature;
+ __le16 kelvin_voltage;
+ __le16 xtal_calib[2];
+
+ bool sku_cap_band_24GHz_enable;
+ bool sku_cap_band_52GHz_enable;
+ bool sku_cap_11n_enable;
+ bool sku_cap_11ac_enable;
+ bool sku_cap_amt_enable;
+ bool sku_cap_ipan_enable;
+ bool sku_cap_mimo_disabled;
+
+ u16 radio_cfg_type;
+ u8 radio_cfg_step;
+ u8 radio_cfg_dash;
+ u8 radio_cfg_pnum;
+ u8 valid_tx_ant, valid_rx_ant;
+
+ u32 nvm_version;
+ s8 max_tx_pwr_half_dbm;
+
+ bool lar_enabled;
+ struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
+ struct ieee80211_channel channels[];
+};
+
+/**
+ * iwl_parse_eeprom_data - parse EEPROM data and return values
+ *
+ * @dev: device pointer we're parsing for, for debug only
+ * @cfg: device configuration for parsing and overrides
+ * @eeprom: the EEPROM data
+ * @eeprom_size: length of the EEPROM data
+ *
+ * This function parses all EEPROM values we need and then
+ * returns a (newly allocated) struct containing all the
+ * relevant values for driver use. The struct must be freed
+ * later with iwl_free_nvm_data().
+ */
+struct iwl_nvm_data *
+iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
+ const u8 *eeprom, size_t eeprom_size);
+
+/**
+ * iwl_free_nvm_data - free NVM data
+ * @data: the data to free
+ */
+static inline void iwl_free_nvm_data(struct iwl_nvm_data *data)
+{
+ kfree(data);
+}
+
+int iwl_nvm_check_version(struct iwl_nvm_data *data,
+ struct iwl_trans *trans);
+
+int iwl_init_sband_channels(struct iwl_nvm_data *data,
+ struct ieee80211_supported_band *sband,
+ int n_channels, enum ieee80211_band band);
+
+void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ struct ieee80211_sta_ht_cap *ht_info,
+ enum ieee80211_band band,
+ u8 tx_chains, u8 rx_chains);
+
+#endif /* __iwl_eeprom_parse_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
new file mode 100644
index 000000000000..f2cea1c7befc
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
@@ -0,0 +1,464 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "iwl-drv.h"
+#include "iwl-debug.h"
+#include "iwl-eeprom-read.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "iwl-csr.h"
+
+/*
+ * EEPROM access time values:
+ *
+ * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG.
+ * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1).
+ * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec.
+ * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG.
+ */
+#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */
+
+#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */
+#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
+
+
+/*
+ * The device's EEPROM semaphore prevents conflicts between driver and uCode
+ * when accessing the EEPROM; each access is a series of pulses to/from the
+ * EEPROM chip, not a single event, so even reads could conflict if they
+ * weren't arbitrated by the semaphore.
+ */
+
+#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */
+#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
+
+static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans)
+{
+ u16 count;
+ int ret;
+
+ for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) {
+ /* Request semaphore */
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
+
+ /* See if we got it */
+ ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
+ EEPROM_SEM_TIMEOUT);
+ if (ret >= 0) {
+ IWL_DEBUG_EEPROM(trans->dev,
+ "Acquired semaphore after %d tries.\n",
+ count+1);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static void iwl_eeprom_release_semaphore(struct iwl_trans *trans)
+{
+ iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
+}
+
+static int iwl_eeprom_verify_signature(struct iwl_trans *trans, bool nvm_is_otp)
+{
+ u32 gp = iwl_read32(trans, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
+
+ IWL_DEBUG_EEPROM(trans->dev, "EEPROM signature=0x%08x\n", gp);
+
+ switch (gp) {
+ case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP:
+ if (!nvm_is_otp) {
+ IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n",
+ gp);
+ return -ENOENT;
+ }
+ return 0;
+ case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
+ case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
+ if (nvm_is_otp) {
+ IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp);
+ return -ENOENT;
+ }
+ return 0;
+ case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP:
+ default:
+ IWL_ERR(trans,
+ "bad EEPROM/OTP signature, type=%s, EEPROM_GP=0x%08x\n",
+ nvm_is_otp ? "OTP" : "EEPROM", gp);
+ return -ENOENT;
+ }
+}
+
+/******************************************************************************
+ *
+ * OTP related functions
+ *
+******************************************************************************/
+
+static void iwl_set_otp_access_absolute(struct iwl_trans *trans)
+{
+ iwl_read32(trans, CSR_OTP_GP_REG);
+
+ iwl_clear_bit(trans, CSR_OTP_GP_REG,
+ CSR_OTP_GP_REG_OTP_ACCESS_MODE);
+}
+
+static int iwl_nvm_is_otp(struct iwl_trans *trans)
+{
+ u32 otpgp;
+
+ /* OTP only valid for CP/PP and after */
+ switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) {
+ case CSR_HW_REV_TYPE_NONE:
+ IWL_ERR(trans, "Unknown hardware type\n");
+ return -EIO;
+ case CSR_HW_REV_TYPE_5300:
+ case CSR_HW_REV_TYPE_5350:
+ case CSR_HW_REV_TYPE_5100:
+ case CSR_HW_REV_TYPE_5150:
+ return 0;
+ default:
+ otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
+ if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT)
+ return 1;
+ return 0;
+ }
+}
+
+static int iwl_init_otp_access(struct iwl_trans *trans)
+{
+ int ret;
+
+ /* Enable 40MHz radio clock */
+ iwl_write32(trans, CSR_GP_CNTRL,
+ iwl_read32(trans, CSR_GP_CNTRL) |
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /* wait for clock to be ready */
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ 25000);
+ if (ret < 0) {
+ IWL_ERR(trans, "Time out access OTP\n");
+ } else {
+ iwl_set_bits_prph(trans, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_RESET_REQ);
+ udelay(5);
+ iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_RESET_REQ);
+
+ /*
+ * CSR auto clock gate disable bit -
+ * this is only applicable for HW with OTP shadow RAM
+ */
+ if (trans->cfg->base_params->shadow_ram_support)
+ iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
+ CSR_RESET_LINK_PWR_MGMT_DISABLED);
+ }
+ return ret;
+}
+
+static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr,
+ __le16 *eeprom_data)
+{
+ int ret = 0;
+ u32 r;
+ u32 otpgp;
+
+ iwl_write32(trans, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
+ ret = iwl_poll_bit(trans, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ IWL_EEPROM_ACCESS_TIMEOUT);
+ if (ret < 0) {
+ IWL_ERR(trans, "Time out reading OTP[%d]\n", addr);
+ return ret;
+ }
+ r = iwl_read32(trans, CSR_EEPROM_REG);
+ /* check for ECC errors: */
+ otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
+ if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
+ /* stop in this case */
+ /* set the uncorrectable OTP ECC bit for acknowledgment */
+ iwl_set_bit(trans, CSR_OTP_GP_REG,
+ CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
+ IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n");
+ return -EINVAL;
+ }
+ if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
+ /* continue in this case */
+ /* set the correctable OTP ECC bit for acknowledgment */
+ iwl_set_bit(trans, CSR_OTP_GP_REG,
+ CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
+ IWL_ERR(trans, "Correctable OTP ECC error, continue read\n");
+ }
+ *eeprom_data = cpu_to_le16(r >> 16);
+ return 0;
+}
+
+/*
+ * iwl_is_otp_empty: check for empty OTP
+ */
+static bool iwl_is_otp_empty(struct iwl_trans *trans)
+{
+ u16 next_link_addr = 0;
+ __le16 link_value;
+ bool is_empty = false;
+
+ /* locate the beginning of OTP link list */
+ if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) {
+ if (!link_value) {
+ IWL_ERR(trans, "OTP is empty\n");
+ is_empty = true;
+ }
+ } else {
+ IWL_ERR(trans, "Unable to read first block of OTP list.\n");
+ is_empty = true;
+ }
+
+ return is_empty;
+}
+
+
+/*
+ * iwl_find_otp_image: find EEPROM image in OTP
+ * finding the OTP block that contains the EEPROM image.
+ * the last valid block on the link list (the block _before_ the last block)
+ * is the block we should read and used to configure the device.
+ * If all the available OTP blocks are full, the last block will be the block
+ * we should read and used to configure the device.
+ * only perform this operation if shadow RAM is disabled
+ */
+static int iwl_find_otp_image(struct iwl_trans *trans,
+ u16 *validblockaddr)
+{
+ u16 next_link_addr = 0, valid_addr;
+ __le16 link_value = 0;
+ int usedblocks = 0;
+
+ /* set addressing mode to absolute to traverse the link list */
+ iwl_set_otp_access_absolute(trans);
+
+ /* checking for empty OTP or error */
+ if (iwl_is_otp_empty(trans))
+ return -EINVAL;
+
+ /*
+ * start traverse link list
+ * until reach the max number of OTP blocks
+ * different devices have different number of OTP blocks
+ */
+ do {
+ /* save current valid block address
+ * check for more block on the link list
+ */
+ valid_addr = next_link_addr;
+ next_link_addr = le16_to_cpu(link_value) * sizeof(u16);
+ IWL_DEBUG_EEPROM(trans->dev, "OTP blocks %d addr 0x%x\n",
+ usedblocks, next_link_addr);
+ if (iwl_read_otp_word(trans, next_link_addr, &link_value))
+ return -EINVAL;
+ if (!link_value) {
+ /*
+ * reach the end of link list, return success and
+ * set address point to the starting address
+ * of the image
+ */
+ *validblockaddr = valid_addr;
+ /* skip first 2 bytes (link list pointer) */
+ *validblockaddr += 2;
+ return 0;
+ }
+ /* more in the link list, continue */
+ usedblocks++;
+ } while (usedblocks <= trans->cfg->base_params->max_ll_items);
+
+ /* OTP has no valid blocks */
+ IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n");
+ return -EINVAL;
+}
+
+/**
+ * iwl_read_eeprom - read EEPROM contents
+ *
+ * Load the EEPROM contents from adapter and return it
+ * and its size.
+ *
+ * NOTE: This routine uses the non-debug IO access functions.
+ */
+int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
+{
+ __le16 *e;
+ u32 gp = iwl_read32(trans, CSR_EEPROM_GP);
+ int sz;
+ int ret;
+ u16 addr;
+ u16 validblockaddr = 0;
+ u16 cache_addr = 0;
+ int nvm_is_otp;
+
+ if (!eeprom || !eeprom_size)
+ return -EINVAL;
+
+ nvm_is_otp = iwl_nvm_is_otp(trans);
+ if (nvm_is_otp < 0)
+ return nvm_is_otp;
+
+ sz = trans->cfg->base_params->eeprom_size;
+ IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz);
+
+ e = kmalloc(sz, GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+
+ ret = iwl_eeprom_verify_signature(trans, nvm_is_otp);
+ if (ret < 0) {
+ IWL_ERR(trans, "EEPROM not found, EEPROM_GP=0x%08x\n", gp);
+ goto err_free;
+ }
+
+ /* Make sure driver (instead of uCode) is allowed to read EEPROM */
+ ret = iwl_eeprom_acquire_semaphore(trans);
+ if (ret < 0) {
+ IWL_ERR(trans, "Failed to acquire EEPROM semaphore.\n");
+ goto err_free;
+ }
+
+ if (nvm_is_otp) {
+ ret = iwl_init_otp_access(trans);
+ if (ret) {
+ IWL_ERR(trans, "Failed to initialize OTP access.\n");
+ goto err_unlock;
+ }
+
+ iwl_write32(trans, CSR_EEPROM_GP,
+ iwl_read32(trans, CSR_EEPROM_GP) &
+ ~CSR_EEPROM_GP_IF_OWNER_MSK);
+
+ iwl_set_bit(trans, CSR_OTP_GP_REG,
+ CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
+ CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
+ /* traversing the linked list if no shadow ram supported */
+ if (!trans->cfg->base_params->shadow_ram_support) {
+ ret = iwl_find_otp_image(trans, &validblockaddr);
+ if (ret)
+ goto err_unlock;
+ }
+ for (addr = validblockaddr; addr < validblockaddr + sz;
+ addr += sizeof(u16)) {
+ __le16 eeprom_data;
+
+ ret = iwl_read_otp_word(trans, addr, &eeprom_data);
+ if (ret)
+ goto err_unlock;
+ e[cache_addr / 2] = eeprom_data;
+ cache_addr += sizeof(u16);
+ }
+ } else {
+ /* eeprom is an array of 16bit values */
+ for (addr = 0; addr < sz; addr += sizeof(u16)) {
+ u32 r;
+
+ iwl_write32(trans, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
+
+ ret = iwl_poll_bit(trans, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ IWL_EEPROM_ACCESS_TIMEOUT);
+ if (ret < 0) {
+ IWL_ERR(trans,
+ "Time out reading EEPROM[%d]\n", addr);
+ goto err_unlock;
+ }
+ r = iwl_read32(trans, CSR_EEPROM_REG);
+ e[addr / 2] = cpu_to_le16(r >> 16);
+ }
+ }
+
+ IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n",
+ nvm_is_otp ? "OTP" : "EEPROM");
+
+ iwl_eeprom_release_semaphore(trans);
+
+ *eeprom_size = sz;
+ *eeprom = (u8 *)e;
+ return 0;
+
+ err_unlock:
+ iwl_eeprom_release_semaphore(trans);
+ err_free:
+ kfree(e);
+
+ return ret;
+}
+IWL_EXPORT_SYMBOL(iwl_read_eeprom);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h
new file mode 100644
index 000000000000..1ed78be06c23
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h
@@ -0,0 +1,70 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_eeprom_h__
+#define __iwl_eeprom_h__
+
+#include "iwl-trans.h"
+
+int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size);
+
+#endif /* __iwl_eeprom_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
new file mode 100644
index 000000000000..5cc6be927eab
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
@@ -0,0 +1,535 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_fh_h__
+#define __iwl_fh_h__
+
+#include <linux/types.h>
+
+/****************************/
+/* Flow Handler Definitions */
+/****************************/
+
+/**
+ * This I/O area is directly read/writable by driver (e.g. Linux uses writel())
+ * Addresses are offsets from device's PCI hardware base address.
+ */
+#define FH_MEM_LOWER_BOUND (0x1000)
+#define FH_MEM_UPPER_BOUND (0x2000)
+
+/**
+ * Keep-Warm (KW) buffer base address.
+ *
+ * Driver must allocate a 4KByte buffer that is for keeping the
+ * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency
+ * DRAM access when doing Txing or Rxing. The dummy accesses prevent host
+ * from going into a power-savings mode that would cause higher DRAM latency,
+ * and possible data over/under-runs, before all Tx/Rx is complete.
+ *
+ * Driver loads FH_KW_MEM_ADDR_REG with the physical address (bits 35:4)
+ * of the buffer, which must be 4K aligned. Once this is set up, the device
+ * automatically invokes keep-warm accesses when normal accesses might not
+ * be sufficient to maintain fast DRAM response.
+ *
+ * Bit fields:
+ * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned
+ */
+#define FH_KW_MEM_ADDR_REG (FH_MEM_LOWER_BOUND + 0x97C)
+
+
+/**
+ * TFD Circular Buffers Base (CBBC) addresses
+ *
+ * Device has 16 base pointer registers, one for each of 16 host-DRAM-resident
+ * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs)
+ * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04
+ * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte
+ * aligned (address bits 0-7 must be 0).
+ * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers
+ * for them are in different places.
+ *
+ * Bit fields in each pointer register:
+ * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned
+ */
+#define FH_MEM_CBBC_0_15_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
+#define FH_MEM_CBBC_0_15_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10)
+#define FH_MEM_CBBC_16_19_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBF0)
+#define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
+#define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20)
+#define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80)
+
+/* Find TFD CB base pointer for given queue */
+static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
+{
+ if (chnl < 16)
+ return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl;
+ if (chnl < 20)
+ return FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16);
+ WARN_ON_ONCE(chnl >= 32);
+ return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20);
+}
+
+
+/**
+ * Rx SRAM Control and Status Registers (RSCSR)
+ *
+ * These registers provide handshake between driver and device for the Rx queue
+ * (this queue handles *all* command responses, notifications, Rx data, etc.
+ * sent from uCode to host driver). Unlike Tx, there is only one Rx
+ * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can
+ * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer
+ * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1
+ * mapping between RBDs and RBs.
+ *
+ * Driver must allocate host DRAM memory for the following, and set the
+ * physical address of each into device registers:
+ *
+ * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256
+ * entries (although any power of 2, up to 4096, is selectable by driver).
+ * Each entry (1 dword) points to a receive buffer (RB) of consistent size
+ * (typically 4K, although 8K or 16K are also selectable by driver).
+ * Driver sets up RB size and number of RBDs in the CB via Rx config
+ * register FH_MEM_RCSR_CHNL0_CONFIG_REG.
+ *
+ * Bit fields within one RBD:
+ * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned
+ *
+ * Driver sets physical address [35:8] of base of RBD circular buffer
+ * into FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0].
+ *
+ * 2) Rx status buffer, 8 bytes, in which uCode indicates which Rx Buffers
+ * (RBs) have been filled, via a "write pointer", actually the index of
+ * the RB's corresponding RBD within the circular buffer. Driver sets
+ * physical address [35:4] into FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0].
+ *
+ * Bit fields in lower dword of Rx status buffer (upper dword not used
+ * by driver:
+ * 31-12: Not used by driver
+ * 11- 0: Index of last filled Rx buffer descriptor
+ * (device writes, driver reads this value)
+ *
+ * As the driver prepares Receive Buffers (RBs) for device to fill, driver must
+ * enter pointers to these RBs into contiguous RBD circular buffer entries,
+ * and update the device's "write" index register,
+ * FH_RSCSR_CHNL0_RBDCB_WPTR_REG.
+ *
+ * This "write" index corresponds to the *next* RBD that the driver will make
+ * available, i.e. one RBD past the tail of the ready-to-fill RBDs within
+ * the circular buffer. This value should initially be 0 (before preparing any
+ * RBs), should be 8 after preparing the first 8 RBs (for example), and must
+ * wrap back to 0 at the end of the circular buffer (but don't wrap before
+ * "read" index has advanced past 1! See below).
+ * NOTE: DEVICE EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8.
+ *
+ * As the device fills RBs (referenced from contiguous RBDs within the circular
+ * buffer), it updates the Rx status buffer in host DRAM, 2) described above,
+ * to tell the driver the index of the latest filled RBD. The driver must
+ * read this "read" index from DRAM after receiving an Rx interrupt from device
+ *
+ * The driver must also internally keep track of a third index, which is the
+ * next RBD to process. When receiving an Rx interrupt, driver should process
+ * all filled but unprocessed RBs up to, but not including, the RB
+ * corresponding to the "read" index. For example, if "read" index becomes "1",
+ * driver may process the RB pointed to by RBD 0. Depending on volume of
+ * traffic, there may be many RBs to process.
+ *
+ * If read index == write index, device thinks there is no room to put new data.
+ * Due to this, the maximum number of filled RBs is 255, instead of 256. To
+ * be safe, make sure that there is a gap of at least 2 RBDs between "write"
+ * and "read" indexes; that is, make sure that there are no more than 254
+ * buffers waiting to be filled.
+ */
+#define FH_MEM_RSCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0)
+#define FH_MEM_RSCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
+#define FH_MEM_RSCSR_CHNL0 (FH_MEM_RSCSR_LOWER_BOUND)
+
+/**
+ * Physical base address of 8-byte Rx Status buffer.
+ * Bit fields:
+ * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned.
+ */
+#define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0)
+
+/**
+ * Physical base address of Rx Buffer Descriptor Circular Buffer.
+ * Bit fields:
+ * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned.
+ */
+#define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004)
+
+/**
+ * Rx write pointer (index, really!).
+ * Bit fields:
+ * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1.
+ * NOTE: For 256-entry circular buffer, use only bits [7:0].
+ */
+#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008)
+#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG)
+
+#define FW_RSCSR_CHNL0_RXDCB_RDPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x00c)
+#define FH_RSCSR_CHNL0_RDPTR FW_RSCSR_CHNL0_RXDCB_RDPTR_REG
+
+/**
+ * Rx Config/Status Registers (RCSR)
+ * Rx Config Reg for channel 0 (only channel used)
+ *
+ * Driver must initialize FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for
+ * normal operation (see bit fields).
+ *
+ * Clearing FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA.
+ * Driver should poll FH_MEM_RSSR_RX_STATUS_REG for
+ * FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing.
+ *
+ * Bit fields:
+ * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame,
+ * '10' operate normally
+ * 29-24: reserved
+ * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal),
+ * min "5" for 32 RBDs, max "12" for 4096 RBDs.
+ * 19-18: reserved
+ * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K,
+ * '10' 12K, '11' 16K.
+ * 15-14: reserved
+ * 13-12: IRQ destination; '00' none, '01' host driver (normal operation)
+ * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec)
+ * typical value 0x10 (about 1/2 msec)
+ * 3- 0: reserved
+ */
+#define FH_MEM_RCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
+#define FH_MEM_RCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xCC0)
+#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND)
+
+#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0)
+#define FH_MEM_RCSR_CHNL0_RBDCB_WPTR (FH_MEM_RCSR_CHNL0 + 0x8)
+#define FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ (FH_MEM_RCSR_CHNL0 + 0x10)
+
+#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */
+#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */
+#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */
+#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */
+#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31*/
+
+#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20)
+#define FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4)
+#define RX_RB_TIMEOUT (0x11)
+
+#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000)
+#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000)
+#define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000)
+
+#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000)
+#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000)
+#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000)
+#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000)
+
+#define FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004)
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000)
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000)
+
+/**
+ * Rx Shared Status Registers (RSSR)
+ *
+ * After stopping Rx DMA channel (writing 0 to
+ * FH_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll
+ * FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle.
+ *
+ * Bit fields:
+ * 24: 1 = Channel 0 is idle
+ *
+ * FH_MEM_RSSR_SHARED_CTRL_REG and FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV
+ * contain default values that should not be altered by the driver.
+ */
+#define FH_MEM_RSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC40)
+#define FH_MEM_RSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xD00)
+
+#define FH_MEM_RSSR_SHARED_CTRL_REG (FH_MEM_RSSR_LOWER_BOUND)
+#define FH_MEM_RSSR_RX_STATUS_REG (FH_MEM_RSSR_LOWER_BOUND + 0x004)
+#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\
+ (FH_MEM_RSSR_LOWER_BOUND + 0x008)
+
+#define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000)
+
+#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28
+#define FH_MEM_TB_MAX_LENGTH (0x00020000)
+
+/* TFDB Area - TFDs buffer table */
+#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF)
+#define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900)
+#define FH_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958)
+#define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl))
+#define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4)
+
+/**
+ * Transmit DMA Channel Control/Status Registers (TCSR)
+ *
+ * Device has one configuration register for each of 8 Tx DMA/FIFO channels
+ * supported in hardware (don't confuse these with the 16 Tx queues in DRAM,
+ * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes.
+ *
+ * To use a Tx DMA channel, driver must initialize its
+ * FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with:
+ *
+ * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL
+ *
+ * All other bits should be 0.
+ *
+ * Bit fields:
+ * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame,
+ * '10' operate normally
+ * 29- 4: Reserved, set to "0"
+ * 3: Enable internal DMA requests (1, normal operation), disable (0)
+ * 2- 0: Reserved, set to "0"
+ */
+#define FH_TCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xD00)
+#define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60)
+
+/* Find Control/Status reg for given Tx DMA/FIFO channel */
+#define FH_TCSR_CHNL_NUM (8)
+
+/* TCSR: tx_config register values */
+#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
+ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl))
+#define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \
+ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \
+ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8)
+
+#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001)
+
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008)
+
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000)
+
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000)
+
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000)
+
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003)
+
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12)
+
+/**
+ * Tx Shared Status Registers (TSSR)
+ *
+ * After stopping Tx DMA channel (writing 0 to
+ * FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll
+ * FH_TSSR_TX_STATUS_REG until selected Tx channel is idle
+ * (channel's buffers empty | no pending requests).
+ *
+ * Bit fields:
+ * 31-24: 1 = Channel buffers empty (channel 7:0)
+ * 23-16: 1 = No pending requests (channel 7:0)
+ */
+#define FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0)
+#define FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0)
+
+#define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010)
+
+/**
+ * Bit fields for TSSR(Tx Shared Status & Control) error status register:
+ * 31: Indicates an address error when accessed to internal memory
+ * uCode/driver must write "1" in order to clear this flag
+ * 30: Indicates that Host did not send the expected number of dwords to FH
+ * uCode/driver must write "1" in order to clear this flag
+ * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA
+ * command was received from the scheduler while the TRB was already full
+ * with previous command
+ * uCode/driver must write "1" in order to clear this flag
+ * 7-0: Each status bit indicates a channel's TxCredit error. When an error
+ * bit is set, it indicates that the FH has received a full indication
+ * from the RTC TxFIFO and the current value of the TxCredit counter was
+ * not equal to zero. This mean that the credit mechanism was not
+ * synchronized to the TxFIFO status
+ * uCode/driver must write "1" in order to clear this flag
+ */
+#define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018)
+#define FH_TSSR_TX_MSG_CONFIG_REG (FH_TSSR_LOWER_BOUND + 0x008)
+
+#define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16)
+
+/* Tx service channels */
+#define FH_SRVC_CHNL (9)
+#define FH_SRVC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9C8)
+#define FH_SRVC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
+#define FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \
+ (FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4)
+
+#define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98)
+#define FH_TX_TRB_REG(_chan) (FH_MEM_LOWER_BOUND + 0x958 + (_chan) * 4)
+
+/* Instruct FH to increment the retry count of a packet when
+ * it is brought from the memory to TX-FIFO
+ */
+#define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002)
+
+#define RX_QUEUE_SIZE 256
+#define RX_QUEUE_MASK 255
+#define RX_QUEUE_SIZE_LOG 8
+
+/**
+ * struct iwl_rb_status - reserve buffer status
+ * host memory mapped FH registers
+ * @closed_rb_num [0:11] - Indicates the index of the RB which was closed
+ * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed
+ * @finished_rb_num [0:11] - Indicates the index of the current RB
+ * in which the last frame was written to
+ * @finished_fr_num [0:11] - Indicates the index of the RX Frame
+ * which was transferred
+ */
+struct iwl_rb_status {
+ __le16 closed_rb_num;
+ __le16 closed_fr_num;
+ __le16 finished_rb_num;
+ __le16 finished_fr_nam;
+ __le32 __unused;
+} __packed;
+
+
+#define TFD_QUEUE_SIZE_MAX (256)
+#define TFD_QUEUE_SIZE_BC_DUP (64)
+#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP)
+#define IWL_TX_DMA_MASK DMA_BIT_MASK(36)
+#define IWL_NUM_OF_TBS 20
+
+static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr)
+{
+ return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF;
+}
+/**
+ * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor
+ *
+ * This structure contains dma address and length of transmission address
+ *
+ * @lo: low [31:0] portion of the dma address of TX buffer
+ * every even is unaligned on 16 bit boundary
+ * @hi_n_len 0-3 [35:32] portion of dma
+ * 4-15 length of the tx buffer
+ */
+struct iwl_tfd_tb {
+ __le32 lo;
+ __le16 hi_n_len;
+} __packed;
+
+/**
+ * struct iwl_tfd
+ *
+ * Transmit Frame Descriptor (TFD)
+ *
+ * @ __reserved1[3] reserved
+ * @ num_tbs 0-4 number of active tbs
+ * 5 reserved
+ * 6-7 padding (not used)
+ * @ tbs[20] transmit frame buffer descriptors
+ * @ __pad padding
+ *
+ * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM.
+ * Both driver and device share these circular buffers, each of which must be
+ * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes
+ *
+ * Driver must indicate the physical address of the base of each
+ * circular buffer via the FH_MEM_CBBC_QUEUE registers.
+ *
+ * Each TFD contains pointer/size information for up to 20 data buffers
+ * in host DRAM. These buffers collectively contain the (one) frame described
+ * by the TFD. Each buffer must be a single contiguous block of memory within
+ * itself, but buffers may be scattered in host DRAM. Each buffer has max size
+ * of (4K - 4). The concatenates all of a TFD's buffers into a single
+ * Tx frame, up to 8 KBytes in size.
+ *
+ * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx.
+ */
+struct iwl_tfd {
+ u8 __reserved1[3];
+ u8 num_tbs;
+ struct iwl_tfd_tb tbs[IWL_NUM_OF_TBS];
+ __le32 __pad;
+} __packed;
+
+/* Keep Warm Size */
+#define IWL_KW_SIZE 0x1000 /* 4k */
+
+/* Fixed (non-configurable) rx data from phy */
+
+/**
+ * struct iwlagn_schedq_bc_tbl scheduler byte count table
+ * base physical address provided by SCD_DRAM_BASE_ADDR
+ * @tfd_offset 0-12 - tx command byte count
+ * 12-16 - station index
+ */
+struct iwlagn_scd_bc_tbl {
+ __le16 tfd_offset[TFD_QUEUE_BC_SIZE];
+} __packed;
+
+#endif /* !__iwl_fh_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h
new file mode 100644
index 000000000000..a5aaf6853704
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h
@@ -0,0 +1,327 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_error_dump_h__
+#define __fw_error_dump_h__
+
+#include <linux/types.h>
+
+#define IWL_FW_ERROR_DUMP_BARKER 0x14789632
+
+/**
+ * enum iwl_fw_error_dump_type - types of data in the dump file
+ * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0
+ * @IWL_FW_ERROR_DUMP_RXF:
+ * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
+ * &struct iwl_fw_error_dump_txcmd packets
+ * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info
+ * info on the device / firmware.
+ * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
+ * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
+ * sections like this in a single file.
+ * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
+ * @IWL_FW_ERROR_DUMP_MEM: chunk of memory
+ * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump.
+ * Structured as &struct iwl_fw_error_dump_trigger_desc.
+ * @IWL_FW_ERROR_DUMP_RB: the content of an RB structured as
+ * &struct iwl_fw_error_dump_rb
+ * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were
+ * paged to the DRAM.
+ * @IWL_FW_ERROR_DUMP_RADIO_REG: Dump the radio registers.
+ */
+enum iwl_fw_error_dump_type {
+ /* 0 is deprecated */
+ IWL_FW_ERROR_DUMP_CSR = 1,
+ IWL_FW_ERROR_DUMP_RXF = 2,
+ IWL_FW_ERROR_DUMP_TXCMD = 3,
+ IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
+ IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
+ IWL_FW_ERROR_DUMP_PRPH = 6,
+ IWL_FW_ERROR_DUMP_TXF = 7,
+ IWL_FW_ERROR_DUMP_FH_REGS = 8,
+ IWL_FW_ERROR_DUMP_MEM = 9,
+ IWL_FW_ERROR_DUMP_ERROR_INFO = 10,
+ IWL_FW_ERROR_DUMP_RB = 11,
+ IWL_FW_ERROR_DUMP_PAGING = 12,
+ IWL_FW_ERROR_DUMP_RADIO_REG = 13,
+
+ IWL_FW_ERROR_DUMP_MAX,
+};
+
+/**
+ * struct iwl_fw_error_dump_data - data for one type
+ * @type: %enum iwl_fw_error_dump_type
+ * @len: the length starting from %data
+ * @data: the data itself
+ */
+struct iwl_fw_error_dump_data {
+ __le32 type;
+ __le32 len;
+ __u8 data[];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_file - the layout of the header of the file
+ * @barker: must be %IWL_FW_ERROR_DUMP_BARKER
+ * @file_len: the length of all the file starting from %barker
+ * @data: array of %struct iwl_fw_error_dump_data
+ */
+struct iwl_fw_error_dump_file {
+ __le32 barker;
+ __le32 file_len;
+ u8 data[0];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_txcmd - TX command data
+ * @cmdlen: original length of command
+ * @caplen: captured length of command (may be less)
+ * @data: captured command data, @caplen bytes
+ */
+struct iwl_fw_error_dump_txcmd {
+ __le32 cmdlen;
+ __le32 caplen;
+ u8 data[];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_fifo - RX/TX FIFO data
+ * @fifo_num: number of FIFO (starting from 0)
+ * @available_bytes: num of bytes available in FIFO (may be less than FIFO size)
+ * @wr_ptr: position of write pointer
+ * @rd_ptr: position of read pointer
+ * @fence_ptr: position of fence pointer
+ * @fence_mode: the current mode of the fence (before locking) -
+ * 0=follow RD pointer ; 1 = freeze
+ * @data: all of the FIFO's data
+ */
+struct iwl_fw_error_dump_fifo {
+ __le32 fifo_num;
+ __le32 available_bytes;
+ __le32 wr_ptr;
+ __le32 rd_ptr;
+ __le32 fence_ptr;
+ __le32 fence_mode;
+ u8 data[];
+} __packed;
+
+enum iwl_fw_error_dump_family {
+ IWL_FW_ERROR_DUMP_FAMILY_7 = 7,
+ IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
+};
+
+/**
+ * struct iwl_fw_error_dump_info - info on the device / firmware
+ * @device_family: the family of the device (7 / 8)
+ * @hw_step: the step of the device
+ * @fw_human_readable: human readable FW version
+ * @dev_human_readable: name of the device
+ * @bus_human_readable: name of the bus used
+ */
+struct iwl_fw_error_dump_info {
+ __le32 device_family;
+ __le32 hw_step;
+ u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ];
+ u8 dev_human_readable[64];
+ u8 bus_human_readable[8];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_fw_mon - FW monitor data
+ * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
+ * @fw_mon_base_ptr: base pointer of the data
+ * @fw_mon_cycle_cnt: number of wraparounds
+ * @reserved: for future use
+ * @data: captured data
+ */
+struct iwl_fw_error_dump_fw_mon {
+ __le32 fw_mon_wr_ptr;
+ __le32 fw_mon_base_ptr;
+ __le32 fw_mon_cycle_cnt;
+ __le32 reserved[3];
+ u8 data[];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_prph - periphery registers data
+ * @prph_start: address of the first register in this chunk
+ * @data: the content of the registers
+ */
+struct iwl_fw_error_dump_prph {
+ __le32 prph_start;
+ __le32 data[];
+};
+
+enum iwl_fw_error_dump_mem_type {
+ IWL_FW_ERROR_DUMP_MEM_SRAM,
+ IWL_FW_ERROR_DUMP_MEM_SMEM,
+};
+
+/**
+ * struct iwl_fw_error_dump_mem - chunk of memory
+ * @type: %enum iwl_fw_error_dump_mem_type
+ * @offset: the offset from which the memory was read
+ * @data: the content of the memory
+ */
+struct iwl_fw_error_dump_mem {
+ __le32 type;
+ __le32 offset;
+ u8 data[];
+};
+
+/**
+ * struct iwl_fw_error_dump_rb - content of an Receive Buffer
+ * @index: the index of the Receive Buffer in the Rx queue
+ * @rxq: the RB's Rx queue
+ * @reserved:
+ * @data: the content of the Receive Buffer
+ */
+struct iwl_fw_error_dump_rb {
+ __le32 index;
+ __le32 rxq;
+ __le32 reserved;
+ u8 data[];
+};
+
+/**
+ * struct iwl_fw_error_dump_paging - content of the UMAC's image page
+ * block on DRAM
+ * @index: the index of the page block
+ * @reserved:
+ * @data: the content of the page block
+ */
+struct iwl_fw_error_dump_paging {
+ __le32 index;
+ __le32 reserved;
+ u8 data[];
+};
+
+/**
+ * iwl_fw_error_next_data - advance fw error dump data pointer
+ * @data: previous data block
+ * Returns: next data block
+ */
+static inline struct iwl_fw_error_dump_data *
+iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
+{
+ return (void *)(data->data + le32_to_cpu(data->len));
+}
+
+/**
+ * enum iwl_fw_dbg_trigger - triggers available
+ *
+ * @FW_DBG_TRIGGER_USER: trigger log collection by user
+ * This should not be defined as a trigger to the driver, but a value the
+ * driver should set to indicate that the trigger was initiated by the
+ * user.
+ * @FW_DBG_TRIGGER_FW_ASSERT: trigger log collection when the firmware asserts
+ * @FW_DBG_TRIGGER_MISSED_BEACONS: trigger log collection when beacons are
+ * missed.
+ * @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch.
+ * @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a
+ * command response or a notification.
+ * @FW_DBG_TRIGGER_MLME: trigger log collection upon MLME event.
+ * @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold.
+ * @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon
+ * goes below a threshold.
+ * @FW_DBG_TRIGGER_TXQ_TIMERS: configures the timers for the Tx queue hang
+ * detection.
+ * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related
+ * events.
+ * @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events.
+ * @FW_DBG_TX_LATENCY: trigger log collection when the tx latency goes above a
+ * threshold.
+ * @FW_DBG_TDLS: trigger log collection upon TDLS related events.
+ */
+enum iwl_fw_dbg_trigger {
+ FW_DBG_TRIGGER_INVALID = 0,
+ FW_DBG_TRIGGER_USER,
+ FW_DBG_TRIGGER_FW_ASSERT,
+ FW_DBG_TRIGGER_MISSED_BEACONS,
+ FW_DBG_TRIGGER_CHANNEL_SWITCH,
+ FW_DBG_TRIGGER_FW_NOTIF,
+ FW_DBG_TRIGGER_MLME,
+ FW_DBG_TRIGGER_STATS,
+ FW_DBG_TRIGGER_RSSI,
+ FW_DBG_TRIGGER_TXQ_TIMERS,
+ FW_DBG_TRIGGER_TIME_EVENT,
+ FW_DBG_TRIGGER_BA,
+ FW_DBG_TRIGGER_TX_LATENCY,
+ FW_DBG_TRIGGER_TDLS,
+
+ /* must be last */
+ FW_DBG_TRIGGER_MAX,
+};
+
+/**
+ * struct iwl_fw_error_dump_trigger_desc - describes the trigger condition
+ * @type: %enum iwl_fw_dbg_trigger
+ * @data: raw data about what happened
+ */
+struct iwl_fw_error_dump_trigger_desc {
+ __le32 type;
+ u8 data[];
+};
+
+#endif /* __fw_error_dump_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
new file mode 100644
index 000000000000..84f8aeb926c8
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
@@ -0,0 +1,793 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_fw_file_h__
+#define __iwl_fw_file_h__
+
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+
+/* v1/v2 uCode file layout */
+struct iwl_ucode_header {
+ __le32 ver; /* major/minor/API/serial */
+ union {
+ struct {
+ __le32 inst_size; /* bytes of runtime code */
+ __le32 data_size; /* bytes of runtime data */
+ __le32 init_size; /* bytes of init code */
+ __le32 init_data_size; /* bytes of init data */
+ __le32 boot_size; /* bytes of bootstrap code */
+ u8 data[0]; /* in same order as sizes */
+ } v1;
+ struct {
+ __le32 build; /* build number */
+ __le32 inst_size; /* bytes of runtime code */
+ __le32 data_size; /* bytes of runtime data */
+ __le32 init_size; /* bytes of init code */
+ __le32 init_data_size; /* bytes of init data */
+ __le32 boot_size; /* bytes of bootstrap code */
+ u8 data[0]; /* in same order as sizes */
+ } v2;
+ } u;
+};
+
+/*
+ * new TLV uCode file layout
+ *
+ * The new TLV file format contains TLVs, that each specify
+ * some piece of data.
+ */
+
+enum iwl_ucode_tlv_type {
+ IWL_UCODE_TLV_INVALID = 0, /* unused */
+ IWL_UCODE_TLV_INST = 1,
+ IWL_UCODE_TLV_DATA = 2,
+ IWL_UCODE_TLV_INIT = 3,
+ IWL_UCODE_TLV_INIT_DATA = 4,
+ IWL_UCODE_TLV_BOOT = 5,
+ IWL_UCODE_TLV_PROBE_MAX_LEN = 6, /* a u32 value */
+ IWL_UCODE_TLV_PAN = 7,
+ IWL_UCODE_TLV_RUNT_EVTLOG_PTR = 8,
+ IWL_UCODE_TLV_RUNT_EVTLOG_SIZE = 9,
+ IWL_UCODE_TLV_RUNT_ERRLOG_PTR = 10,
+ IWL_UCODE_TLV_INIT_EVTLOG_PTR = 11,
+ IWL_UCODE_TLV_INIT_EVTLOG_SIZE = 12,
+ IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13,
+ IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14,
+ IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15,
+ IWL_UCODE_TLV_WOWLAN_INST = 16,
+ IWL_UCODE_TLV_WOWLAN_DATA = 17,
+ IWL_UCODE_TLV_FLAGS = 18,
+ IWL_UCODE_TLV_SEC_RT = 19,
+ IWL_UCODE_TLV_SEC_INIT = 20,
+ IWL_UCODE_TLV_SEC_WOWLAN = 21,
+ IWL_UCODE_TLV_DEF_CALIB = 22,
+ IWL_UCODE_TLV_PHY_SKU = 23,
+ IWL_UCODE_TLV_SECURE_SEC_RT = 24,
+ IWL_UCODE_TLV_SECURE_SEC_INIT = 25,
+ IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
+ IWL_UCODE_TLV_NUM_OF_CPU = 27,
+ IWL_UCODE_TLV_CSCHEME = 28,
+ IWL_UCODE_TLV_API_CHANGES_SET = 29,
+ IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
+ IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
+ IWL_UCODE_TLV_PAGING = 32,
+ IWL_UCODE_TLV_SEC_RT_USNIFFER = 34,
+ IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35,
+ IWL_UCODE_TLV_FW_VERSION = 36,
+ IWL_UCODE_TLV_FW_DBG_DEST = 38,
+ IWL_UCODE_TLV_FW_DBG_CONF = 39,
+ IWL_UCODE_TLV_FW_DBG_TRIGGER = 40,
+ IWL_UCODE_TLV_FW_GSCAN_CAPA = 50,
+};
+
+struct iwl_ucode_tlv {
+ __le32 type; /* see above */
+ __le32 length; /* not including type/length fields */
+ u8 data[0];
+};
+
+#define IWL_TLV_UCODE_MAGIC 0x0a4c5749
+#define FW_VER_HUMAN_READABLE_SZ 64
+
+struct iwl_tlv_ucode_header {
+ /*
+ * The TLV style ucode header is distinguished from
+ * the v1/v2 style header by first four bytes being
+ * zero, as such is an invalid combination of
+ * major/minor/API/serial versions.
+ */
+ __le32 zero;
+ __le32 magic;
+ u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
+ /* major/minor/API/serial or major in new format */
+ __le32 ver;
+ __le32 build;
+ __le64 ignore;
+ /*
+ * The data contained herein has a TLV layout,
+ * see above for the TLV header and types.
+ * Note that each TLV is padded to a length
+ * that is a multiple of 4 for alignment.
+ */
+ u8 data[0];
+};
+
+/*
+ * ucode TLVs
+ *
+ * ability to get extension for: flags & capabilities from ucode binaries files
+ */
+struct iwl_ucode_api {
+ __le32 api_index;
+ __le32 api_flags;
+} __packed;
+
+struct iwl_ucode_capa {
+ __le32 api_index;
+ __le32 api_capa;
+} __packed;
+
+/**
+ * enum iwl_ucode_tlv_flag - ucode API flags
+ * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
+ * was a separate TLV but moved here to save space.
+ * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behavior on hidden SSID,
+ * treats good CRC threshold as a boolean
+ * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
+ * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
+ * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
+ * offload profile config command.
+ * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
+ * (rather than two) IPv6 addresses
+ * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
+ * from the probe request template.
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
+ * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
+ * P2P client interfaces simultaneously if they are in different bindings.
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
+ * P2P client interfaces simultaneously if they are in same bindings.
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
+ * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
+ * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
+ * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
+ */
+enum iwl_ucode_tlv_flag {
+ IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
+ IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1),
+ IWL_UCODE_TLV_FLAGS_MFP = BIT(2),
+ IWL_UCODE_TLV_FLAGS_P2P = BIT(3),
+ IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4),
+ IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7),
+ IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10),
+ IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12),
+ IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15),
+ IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16),
+ IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21),
+ IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22),
+ IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23),
+ IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24),
+ IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25),
+ IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26),
+ IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29),
+ IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30),
+};
+
+typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t;
+
+/**
+ * enum iwl_ucode_tlv_api - ucode api
+ * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
+ * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
+ * longer than the passive one, which is essential for fragmented scan.
+ * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source.
+ * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header
+ * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params
+ * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format
+ * @IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority
+ * instead of 3.
+ * @IWL_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size
+ * (command version 3) that supports per-chain limits
+ *
+ * @NUM_IWL_UCODE_TLV_API: number of bits used
+ */
+enum iwl_ucode_tlv_api {
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT = (__force iwl_ucode_tlv_api_t)3,
+ IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8,
+ IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9,
+ IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14,
+ IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18,
+ IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20,
+ IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY = (__force iwl_ucode_tlv_api_t)24,
+ IWL_UCODE_TLV_API_TX_POWER_CHAIN = (__force iwl_ucode_tlv_api_t)27,
+
+ NUM_IWL_UCODE_TLV_API
+#ifdef __CHECKER__
+ /* sparse says it cannot increment the previous enum member */
+ = 128
+#endif
+};
+
+typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t;
+
+/**
+ * enum iwl_ucode_tlv_capa - ucode capabilities
+ * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
+ * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory
+ * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan.
+ * @IWL_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer
+ * @IWL_UCODE_TLV_CAPA_TOF_SUPPORT: supports Time of Flight (802.11mc FTM)
+ * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality
+ * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
+ * tx power value into TPC Report action frame and Link Measurement Report
+ * action frame
+ * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current
+ * channel in DS parameter set element in probe requests.
+ * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
+ * probe requests.
+ * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
+ * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
+ * which also implies support for the scheduler configuration command
+ * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
+ * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
+ * @IWL_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command
+ * @IWL_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload
+ * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics
+ * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running
+ * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different
+ * sources for the MCC. This TLV bit is a future replacement to
+ * IWL_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR
+ * is supported.
+ * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC
+ * @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan
+ * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
+ * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
+ * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT
+ * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what
+ * antenna the beacon should be transmitted
+ * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2
+ *
+ * @NUM_IWL_UCODE_TLV_CAPA: number of bits used
+ */
+enum iwl_ucode_tlv_capa {
+ IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = (__force iwl_ucode_tlv_capa_t)0,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT = (__force iwl_ucode_tlv_capa_t)1,
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN = (__force iwl_ucode_tlv_capa_t)2,
+ IWL_UCODE_TLV_CAPA_BEAMFORMER = (__force iwl_ucode_tlv_capa_t)3,
+ IWL_UCODE_TLV_CAPA_TOF_SUPPORT = (__force iwl_ucode_tlv_capa_t)5,
+ IWL_UCODE_TLV_CAPA_TDLS_SUPPORT = (__force iwl_ucode_tlv_capa_t)6,
+ IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = (__force iwl_ucode_tlv_capa_t)8,
+ IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)9,
+ IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)10,
+ IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = (__force iwl_ucode_tlv_capa_t)11,
+ IWL_UCODE_TLV_CAPA_DQA_SUPPORT = (__force iwl_ucode_tlv_capa_t)12,
+ IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = (__force iwl_ucode_tlv_capa_t)13,
+ IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = (__force iwl_ucode_tlv_capa_t)18,
+ IWL_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = (__force iwl_ucode_tlv_capa_t)19,
+ IWL_UCODE_TLV_CAPA_CSUM_SUPPORT = (__force iwl_ucode_tlv_capa_t)21,
+ IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = (__force iwl_ucode_tlv_capa_t)22,
+ IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = (__force iwl_ucode_tlv_capa_t)28,
+ IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = (__force iwl_ucode_tlv_capa_t)29,
+ IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30,
+ IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31,
+ IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64,
+ IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65,
+ IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67,
+ IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73,
+
+ NUM_IWL_UCODE_TLV_CAPA
+#ifdef __CHECKER__
+ /* sparse says it cannot increment the previous enum member */
+ = 128
+#endif
+};
+
+/* The default calibrate table size if not specified by firmware file */
+#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18
+#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19
+#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253
+
+/* The default max probe length if not specified by the firmware file */
+#define IWL_DEFAULT_MAX_PROBE_LENGTH 200
+
+/*
+ * For 16.0 uCode and above, there is no differentiation between sections,
+ * just an offset to the HW address.
+ */
+#define IWL_UCODE_SECTION_MAX 16
+#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC
+#define PAGING_SEPARATOR_SECTION 0xAAAABBBB
+
+/* uCode version contains 4 values: Major/Minor/API/Serial */
+#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24)
+#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16)
+#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8)
+#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF)
+
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ * flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ * event triggers.
+ */
+struct iwl_tlv_calib_ctrl {
+ __le32 flow_trigger;
+ __le32 event_trigger;
+} __packed;
+
+enum iwl_fw_phy_cfg {
+ FW_PHY_CFG_RADIO_TYPE_POS = 0,
+ FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
+ FW_PHY_CFG_RADIO_STEP_POS = 2,
+ FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
+ FW_PHY_CFG_RADIO_DASH_POS = 4,
+ FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
+ FW_PHY_CFG_TX_CHAIN_POS = 16,
+ FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
+ FW_PHY_CFG_RX_CHAIN_POS = 20,
+ FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
+};
+
+#define IWL_UCODE_MAX_CS 1
+
+/**
+ * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
+ * @cipher: a cipher suite selector
+ * @flags: cipher scheme flags (currently reserved for a future use)
+ * @hdr_len: a size of MPDU security header
+ * @pn_len: a size of PN
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: bit shift needed to get key_idx
+ * @mic_len: mic length in bytes
+ * @hw_cipher: a HW cipher index used in host commands
+ */
+struct iwl_fw_cipher_scheme {
+ __le32 cipher;
+ u8 flags;
+ u8 hdr_len;
+ u8 pn_len;
+ u8 pn_off;
+ u8 key_idx_off;
+ u8 key_idx_mask;
+ u8 key_idx_shift;
+ u8 mic_len;
+ u8 hw_cipher;
+} __packed;
+
+enum iwl_fw_dbg_reg_operator {
+ CSR_ASSIGN,
+ CSR_SETBIT,
+ CSR_CLEARBIT,
+
+ PRPH_ASSIGN,
+ PRPH_SETBIT,
+ PRPH_CLEARBIT,
+
+ INDIRECT_ASSIGN,
+ INDIRECT_SETBIT,
+ INDIRECT_CLEARBIT,
+
+ PRPH_BLOCKBIT,
+};
+
+/**
+ * struct iwl_fw_dbg_reg_op - an operation on a register
+ *
+ * @op: %enum iwl_fw_dbg_reg_operator
+ * @addr: offset of the register
+ * @val: value
+ */
+struct iwl_fw_dbg_reg_op {
+ u8 op;
+ u8 reserved[3];
+ __le32 addr;
+ __le32 val;
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_monitor_mode - available monitor recording modes
+ *
+ * @SMEM_MODE: monitor stores the data in SMEM
+ * @EXTERNAL_MODE: monitor stores the data in allocated DRAM
+ * @MARBH_MODE: monitor stores the data in MARBH buffer
+ * @MIPI_MODE: monitor outputs the data through the MIPI interface
+ */
+enum iwl_fw_dbg_monitor_mode {
+ SMEM_MODE = 0,
+ EXTERNAL_MODE = 1,
+ MARBH_MODE = 2,
+ MIPI_MODE = 3,
+};
+
+/**
+ * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
+ *
+ * @version: version of the TLV - currently 0
+ * @monitor_mode: %enum iwl_fw_dbg_monitor_mode
+ * @size_power: buffer size will be 2^(size_power + 11)
+ * @base_reg: addr of the base addr register (PRPH)
+ * @end_reg: addr of the end addr register (PRPH)
+ * @write_ptr_reg: the addr of the reg of the write pointer
+ * @wrap_count: the addr of the reg of the wrap_count
+ * @base_shift: shift right of the base addr reg
+ * @end_shift: shift right of the end addr reg
+ * @reg_ops: array of registers operations
+ *
+ * This parses IWL_UCODE_TLV_FW_DBG_DEST
+ */
+struct iwl_fw_dbg_dest_tlv {
+ u8 version;
+ u8 monitor_mode;
+ u8 size_power;
+ u8 reserved;
+ __le32 base_reg;
+ __le32 end_reg;
+ __le32 write_ptr_reg;
+ __le32 wrap_count;
+ u8 base_shift;
+ u8 end_shift;
+ struct iwl_fw_dbg_reg_op reg_ops[0];
+} __packed;
+
+struct iwl_fw_dbg_conf_hcmd {
+ u8 id;
+ u8 reserved;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_trigger_mode - triggers functionalities
+ *
+ * @IWL_FW_DBG_TRIGGER_START: when trigger occurs re-conf the dbg mechanism
+ * @IWL_FW_DBG_TRIGGER_STOP: when trigger occurs pull the dbg data
+ * @IWL_FW_DBG_TRIGGER_MONITOR_ONLY: when trigger occurs trigger is set to
+ * collect only monitor data
+ */
+enum iwl_fw_dbg_trigger_mode {
+ IWL_FW_DBG_TRIGGER_START = BIT(0),
+ IWL_FW_DBG_TRIGGER_STOP = BIT(1),
+ IWL_FW_DBG_TRIGGER_MONITOR_ONLY = BIT(2),
+};
+
+/**
+ * enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger
+ * @IWL_FW_DBG_CONF_VIF_ANY: any vif type
+ * @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode
+ * @IWL_FW_DBG_CONF_VIF_STATION: BSS mode
+ * @IWL_FW_DBG_CONF_VIF_AP: AP mode
+ * @IWL_FW_DBG_CONF_VIF_P2P_CLIENT: P2P Client mode
+ * @IWL_FW_DBG_CONF_VIF_P2P_GO: P2P GO mode
+ * @IWL_FW_DBG_CONF_VIF_P2P_DEVICE: P2P device
+ */
+enum iwl_fw_dbg_trigger_vif_type {
+ IWL_FW_DBG_CONF_VIF_ANY = NL80211_IFTYPE_UNSPECIFIED,
+ IWL_FW_DBG_CONF_VIF_IBSS = NL80211_IFTYPE_ADHOC,
+ IWL_FW_DBG_CONF_VIF_STATION = NL80211_IFTYPE_STATION,
+ IWL_FW_DBG_CONF_VIF_AP = NL80211_IFTYPE_AP,
+ IWL_FW_DBG_CONF_VIF_P2P_CLIENT = NL80211_IFTYPE_P2P_CLIENT,
+ IWL_FW_DBG_CONF_VIF_P2P_GO = NL80211_IFTYPE_P2P_GO,
+ IWL_FW_DBG_CONF_VIF_P2P_DEVICE = NL80211_IFTYPE_P2P_DEVICE,
+};
+
+/**
+ * struct iwl_fw_dbg_trigger_tlv - a TLV that describes the trigger
+ * @id: %enum iwl_fw_dbg_trigger
+ * @vif_type: %enum iwl_fw_dbg_trigger_vif_type
+ * @stop_conf_ids: bitmap of configurations this trigger relates to.
+ * if the mode is %IWL_FW_DBG_TRIGGER_STOP, then if the bit corresponding
+ * to the currently running configuration is set, the data should be
+ * collected.
+ * @stop_delay: how many milliseconds to wait before collecting the data
+ * after the STOP trigger fires.
+ * @mode: %enum iwl_fw_dbg_trigger_mode - can be stop / start of both
+ * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what
+ * configuration should be applied when the triggers kicks in.
+ * @occurrences: number of occurrences. 0 means the trigger will never fire.
+ * @trig_dis_ms: the time, in milliseconds, after an occurrence of this
+ * trigger in which another occurrence should be ignored.
+ */
+struct iwl_fw_dbg_trigger_tlv {
+ __le32 id;
+ __le32 vif_type;
+ __le32 stop_conf_ids;
+ __le32 stop_delay;
+ u8 mode;
+ u8 start_conf_id;
+ __le16 occurrences;
+ __le16 trig_dis_ms;
+ __le16 reserved[3];
+
+ u8 data[0];
+} __packed;
+
+#define FW_DBG_START_FROM_ALIVE 0
+#define FW_DBG_CONF_MAX 32
+#define FW_DBG_INVALID 0xff
+
+/**
+ * struct iwl_fw_dbg_trigger_missed_bcon - configures trigger for missed beacons
+ * @stop_consec_missed_bcon: stop recording if threshold is crossed.
+ * @stop_consec_missed_bcon_since_rx: stop recording if threshold is crossed.
+ * @start_consec_missed_bcon: start recording if threshold is crossed.
+ * @start_consec_missed_bcon_since_rx: start recording if threshold is crossed.
+ * @reserved1: reserved
+ * @reserved2: reserved
+ */
+struct iwl_fw_dbg_trigger_missed_bcon {
+ __le32 stop_consec_missed_bcon;
+ __le32 stop_consec_missed_bcon_since_rx;
+ __le32 reserved2[2];
+ __le32 start_consec_missed_bcon;
+ __le32 start_consec_missed_bcon_since_rx;
+ __le32 reserved1[2];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW.
+ * cmds: the list of commands to trigger the collection on
+ */
+struct iwl_fw_dbg_trigger_cmd {
+ struct cmd {
+ u8 cmd_id;
+ u8 group_id;
+ } __packed cmds[16];
+} __packed;
+
+/**
+ * iwl_fw_dbg_trigger_stats - configures trigger for statistics
+ * @stop_offset: the offset of the value to be monitored
+ * @stop_threshold: the threshold above which to collect
+ * @start_offset: the offset of the value to be monitored
+ * @start_threshold: the threshold above which to start recording
+ */
+struct iwl_fw_dbg_trigger_stats {
+ __le32 stop_offset;
+ __le32 stop_threshold;
+ __le32 start_offset;
+ __le32 start_threshold;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_low_rssi - trigger for low beacon RSSI
+ * @rssi: RSSI value to trigger at
+ */
+struct iwl_fw_dbg_trigger_low_rssi {
+ __le32 rssi;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_mlme - configures trigger for mlme events
+ * @stop_auth_denied: number of denied authentication to collect
+ * @stop_auth_timeout: number of authentication timeout to collect
+ * @stop_rx_deauth: number of Rx deauth before to collect
+ * @stop_tx_deauth: number of Tx deauth before to collect
+ * @stop_assoc_denied: number of denied association to collect
+ * @stop_assoc_timeout: number of association timeout to collect
+ * @stop_connection_loss: number of connection loss to collect
+ * @start_auth_denied: number of denied authentication to start recording
+ * @start_auth_timeout: number of authentication timeout to start recording
+ * @start_rx_deauth: number of Rx deauth to start recording
+ * @start_tx_deauth: number of Tx deauth to start recording
+ * @start_assoc_denied: number of denied association to start recording
+ * @start_assoc_timeout: number of association timeout to start recording
+ * @start_connection_loss: number of connection loss to start recording
+ */
+struct iwl_fw_dbg_trigger_mlme {
+ u8 stop_auth_denied;
+ u8 stop_auth_timeout;
+ u8 stop_rx_deauth;
+ u8 stop_tx_deauth;
+
+ u8 stop_assoc_denied;
+ u8 stop_assoc_timeout;
+ u8 stop_connection_loss;
+ u8 reserved;
+
+ u8 start_auth_denied;
+ u8 start_auth_timeout;
+ u8 start_rx_deauth;
+ u8 start_tx_deauth;
+
+ u8 start_assoc_denied;
+ u8 start_assoc_timeout;
+ u8 start_connection_loss;
+ u8 reserved2;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_txq_timer - configures the Tx queue's timer
+ * @command_queue: timeout for the command queue in ms
+ * @bss: timeout for the queues of a BSS (except for TDLS queues) in ms
+ * @softap: timeout for the queues of a softAP in ms
+ * @p2p_go: timeout for the queues of a P2P GO in ms
+ * @p2p_client: timeout for the queues of a P2P client in ms
+ * @p2p_device: timeout for the queues of a P2P device in ms
+ * @ibss: timeout for the queues of an IBSS in ms
+ * @tdls: timeout for the queues of a TDLS station in ms
+ */
+struct iwl_fw_dbg_trigger_txq_timer {
+ __le32 command_queue;
+ __le32 bss;
+ __le32 softap;
+ __le32 p2p_go;
+ __le32 p2p_client;
+ __le32 p2p_device;
+ __le32 ibss;
+ __le32 tdls;
+ __le32 reserved[4];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_time_event - configures a time event trigger
+ * time_Events: a list of tuples <id, action_bitmap>. The driver will issue a
+ * trigger each time a time event notification that relates to time event
+ * id with one of the actions in the bitmap is received and
+ * BIT(notif->status) is set in status_bitmap.
+ *
+ */
+struct iwl_fw_dbg_trigger_time_event {
+ struct {
+ __le32 id;
+ __le32 action_bitmap;
+ __le32 status_bitmap;
+ } __packed time_events[16];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_ba - configures BlockAck related trigger
+ * rx_ba_start: tid bitmap to configure on what tid the trigger should occur
+ * when an Rx BlockAck session is started.
+ * rx_ba_stop: tid bitmap to configure on what tid the trigger should occur
+ * when an Rx BlockAck session is stopped.
+ * tx_ba_start: tid bitmap to configure on what tid the trigger should occur
+ * when a Tx BlockAck session is started.
+ * tx_ba_stop: tid bitmap to configure on what tid the trigger should occur
+ * when a Tx BlockAck session is stopped.
+ * rx_bar: tid bitmap to configure on what tid the trigger should occur
+ * when a BAR is received (for a Tx BlockAck session).
+ * tx_bar: tid bitmap to configure on what tid the trigger should occur
+ * when a BAR is send (for an Rx BlocAck session).
+ * frame_timeout: tid bitmap to configure on what tid the trigger should occur
+ * when a frame times out in the reodering buffer.
+ */
+struct iwl_fw_dbg_trigger_ba {
+ __le16 rx_ba_start;
+ __le16 rx_ba_stop;
+ __le16 tx_ba_start;
+ __le16 tx_ba_stop;
+ __le16 rx_bar;
+ __le16 tx_bar;
+ __le16 frame_timeout;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_tdls - configures trigger for TDLS events.
+ * @action_bitmap: the TDLS action to trigger the collection upon
+ * @peer_mode: trigger on specific peer or all
+ * @peer: the TDLS peer to trigger the collection on
+ */
+struct iwl_fw_dbg_trigger_tdls {
+ u8 action_bitmap;
+ u8 peer_mode;
+ u8 peer[ETH_ALEN];
+ u8 reserved[4];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
+ * @id: conf id
+ * @usniffer: should the uSniffer image be used
+ * @num_of_hcmds: how many HCMDs to send are present here
+ * @hcmd: a variable length host command to be sent to apply the configuration.
+ * If there is more than one HCMD to send, they will appear one after the
+ * other and be sent in the order that they appear in.
+ * This parses IWL_UCODE_TLV_FW_DBG_CONF. The user can add up-to
+ * %FW_DBG_CONF_MAX configuration per run.
+ */
+struct iwl_fw_dbg_conf_tlv {
+ u8 id;
+ u8 usniffer;
+ u8 reserved;
+ u8 num_of_hcmds;
+ struct iwl_fw_dbg_conf_hcmd hcmd;
+} __packed;
+
+/**
+ * struct iwl_fw_gscan_capabilities - gscan capabilities supported by FW
+ * @max_scan_cache_size: total space allocated for scan results (in bytes).
+ * @max_scan_buckets: maximum number of channel buckets.
+ * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan.
+ * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI.
+ * @max_scan_reporting_threshold: max possible report threshold. in percentage.
+ * @max_hotlist_aps: maximum number of entries for hotlist APs.
+ * @max_significant_change_aps: maximum number of entries for significant
+ * change APs.
+ * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can
+ * hold.
+ */
+struct iwl_fw_gscan_capabilities {
+ __le32 max_scan_cache_size;
+ __le32 max_scan_buckets;
+ __le32 max_ap_cache_per_scan;
+ __le32 max_rssi_sample_size;
+ __le32 max_scan_reporting_threshold;
+ __le32 max_hotlist_aps;
+ __le32 max_significant_change_aps;
+ __le32 max_bssid_history_entries;
+} __packed;
+
+#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h
new file mode 100644
index 000000000000..85d6d6d55e2f
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h
@@ -0,0 +1,308 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_fw_h__
+#define __iwl_fw_h__
+#include <linux/types.h>
+#include <net/mac80211.h>
+
+#include "iwl-fw-file.h"
+#include "iwl-fw-error-dump.h"
+
+/**
+ * enum iwl_ucode_type
+ *
+ * The type of ucode.
+ *
+ * @IWL_UCODE_REGULAR: Normal runtime ucode
+ * @IWL_UCODE_INIT: Initial ucode
+ * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
+ * @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image
+ */
+enum iwl_ucode_type {
+ IWL_UCODE_REGULAR,
+ IWL_UCODE_INIT,
+ IWL_UCODE_WOWLAN,
+ IWL_UCODE_REGULAR_USNIFFER,
+ IWL_UCODE_TYPE_MAX,
+};
+
+/*
+ * enumeration of ucode section.
+ * This enumeration is used directly for older firmware (before 16.0).
+ * For new firmware, there can be up to 4 sections (see below) but the
+ * first one packaged into the firmware file is the DATA section and
+ * some debugging code accesses that.
+ */
+enum iwl_ucode_sec {
+ IWL_UCODE_SECTION_DATA,
+ IWL_UCODE_SECTION_INST,
+};
+
+struct iwl_ucode_capabilities {
+ u32 max_probe_length;
+ u32 n_scan_channels;
+ u32 standard_phy_calibration_size;
+ u32 flags;
+ unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];
+ unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)];
+};
+
+static inline bool
+fw_has_api(const struct iwl_ucode_capabilities *capabilities,
+ iwl_ucode_tlv_api_t api)
+{
+ return test_bit((__force long)api, capabilities->_api);
+}
+
+static inline bool
+fw_has_capa(const struct iwl_ucode_capabilities *capabilities,
+ iwl_ucode_tlv_capa_t capa)
+{
+ return test_bit((__force long)capa, capabilities->_capa);
+}
+
+/* one for each uCode image (inst/data, init/runtime/wowlan) */
+struct fw_desc {
+ const void *data; /* vmalloc'ed data */
+ u32 len; /* size in bytes */
+ u32 offset; /* offset in the device */
+};
+
+struct fw_img {
+ struct fw_desc sec[IWL_UCODE_SECTION_MAX];
+ bool is_dual_cpus;
+ u32 paging_mem_size;
+};
+
+struct iwl_sf_region {
+ u32 addr;
+ u32 size;
+};
+
+/*
+ * Block paging calculations
+ */
+#define PAGE_2_EXP_SIZE 12 /* 4K == 2^12 */
+#define FW_PAGING_SIZE BIT(PAGE_2_EXP_SIZE) /* page size is 4KB */
+#define PAGE_PER_GROUP_2_EXP_SIZE 3
+/* 8 pages per group */
+#define NUM_OF_PAGE_PER_GROUP BIT(PAGE_PER_GROUP_2_EXP_SIZE)
+/* don't change, support only 32KB size */
+#define PAGING_BLOCK_SIZE (NUM_OF_PAGE_PER_GROUP * FW_PAGING_SIZE)
+/* 32K == 2^15 */
+#define BLOCK_2_EXP_SIZE (PAGE_2_EXP_SIZE + PAGE_PER_GROUP_2_EXP_SIZE)
+
+/*
+ * Image paging calculations
+ */
+#define BLOCK_PER_IMAGE_2_EXP_SIZE 5
+/* 2^5 == 32 blocks per image */
+#define NUM_OF_BLOCK_PER_IMAGE BIT(BLOCK_PER_IMAGE_2_EXP_SIZE)
+/* maximum image size 1024KB */
+#define MAX_PAGING_IMAGE_SIZE (NUM_OF_BLOCK_PER_IMAGE * PAGING_BLOCK_SIZE)
+
+/* Virtual address signature */
+#define PAGING_ADDR_SIG 0xAA000000
+
+#define PAGING_CMD_IS_SECURED BIT(9)
+#define PAGING_CMD_IS_ENABLED BIT(8)
+#define PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS 0
+#define PAGING_TLV_SECURE_MASK 1
+
+/**
+ * struct iwl_fw_paging
+ * @fw_paging_phys: page phy pointer
+ * @fw_paging_block: pointer to the allocated block
+ * @fw_paging_size: page size
+ */
+struct iwl_fw_paging {
+ dma_addr_t fw_paging_phys;
+ struct page *fw_paging_block;
+ u32 fw_paging_size;
+};
+
+/**
+ * struct iwl_fw_cscheme_list - a cipher scheme list
+ * @size: a number of entries
+ * @cs: cipher scheme entries
+ */
+struct iwl_fw_cscheme_list {
+ u8 size;
+ struct iwl_fw_cipher_scheme cs[];
+} __packed;
+
+/**
+ * struct iwl_gscan_capabilities - gscan capabilities supported by FW
+ * @max_scan_cache_size: total space allocated for scan results (in bytes).
+ * @max_scan_buckets: maximum number of channel buckets.
+ * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan.
+ * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI.
+ * @max_scan_reporting_threshold: max possible report threshold. in percentage.
+ * @max_hotlist_aps: maximum number of entries for hotlist APs.
+ * @max_significant_change_aps: maximum number of entries for significant
+ * change APs.
+ * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can
+ * hold.
+ */
+struct iwl_gscan_capabilities {
+ u32 max_scan_cache_size;
+ u32 max_scan_buckets;
+ u32 max_ap_cache_per_scan;
+ u32 max_rssi_sample_size;
+ u32 max_scan_reporting_threshold;
+ u32 max_hotlist_aps;
+ u32 max_significant_change_aps;
+ u32 max_bssid_history_entries;
+};
+
+/**
+ * struct iwl_fw - variables associated with the firmware
+ *
+ * @ucode_ver: ucode version from the ucode file
+ * @fw_version: firmware version string
+ * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan.
+ * @ucode_capa: capabilities parsed from the ucode file.
+ * @enhance_sensitivity_table: device can do enhanced sensitivity.
+ * @init_evtlog_ptr: event log offset for init ucode.
+ * @init_evtlog_size: event log size for init ucode.
+ * @init_errlog_ptr: error log offfset for init ucode.
+ * @inst_evtlog_ptr: event log offset for runtime ucode.
+ * @inst_evtlog_size: event log size for runtime ucode.
+ * @inst_errlog_ptr: error log offfset for runtime ucode.
+ * @mvm_fw: indicates this is MVM firmware
+ * @cipher_scheme: optional external cipher scheme.
+ * @human_readable: human readable version
+ * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until
+ * we get the ALIVE from the uCode
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
+ * @dbg_trigger_tlv: array of pointers to triggers TLVs
+ * @dbg_trigger_tlv_len: lengths of the @dbg_trigger_tlv entries
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
+ */
+struct iwl_fw {
+ u32 ucode_ver;
+
+ char fw_version[ETHTOOL_FWVERS_LEN];
+
+ /* ucode images */
+ struct fw_img img[IWL_UCODE_TYPE_MAX];
+
+ struct iwl_ucode_capabilities ucode_capa;
+ bool enhance_sensitivity_table;
+
+ u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
+ u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+ struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX];
+ u32 phy_config;
+ u8 valid_tx_ant;
+ u8 valid_rx_ant;
+
+ bool mvm_fw;
+
+ struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
+ u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
+
+ u32 sdio_adma_addr;
+
+ struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+ struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
+ size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
+ struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
+ size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
+ u8 dbg_dest_reg_num;
+ struct iwl_gscan_capabilities gscan_capa;
+};
+
+static inline const char *get_fw_dbg_mode_string(int mode)
+{
+ switch (mode) {
+ case SMEM_MODE:
+ return "SMEM";
+ case EXTERNAL_MODE:
+ return "EXTERNAL_DRAM";
+ case MARBH_MODE:
+ return "MARBH";
+ case MIPI_MODE:
+ return "MIPI";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline bool
+iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
+{
+ const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
+
+ if (!conf_tlv)
+ return false;
+
+ return conf_tlv->usniffer;
+}
+
+#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
new file mode 100644
index 000000000000..32c8f84ae519
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
@@ -0,0 +1,294 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * Portions of this file are derived from the ipw3945 project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/export.h>
+
+#include "iwl-drv.h"
+#include "iwl-io.h"
+#include "iwl-csr.h"
+#include "iwl-debug.h"
+#include "iwl-prph.h"
+#include "iwl-fh.h"
+
+void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
+{
+ trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val);
+ iwl_trans_write8(trans, ofs, val);
+}
+IWL_EXPORT_SYMBOL(iwl_write8);
+
+void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+ trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val);
+ iwl_trans_write32(trans, ofs, val);
+}
+IWL_EXPORT_SYMBOL(iwl_write32);
+
+u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
+{
+ u32 val = iwl_trans_read32(trans, ofs);
+
+ trace_iwlwifi_dev_ioread32(trans->dev, ofs, val);
+ return val;
+}
+IWL_EXPORT_SYMBOL(iwl_read32);
+
+#define IWL_POLL_INTERVAL 10 /* microseconds */
+
+int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
+ u32 bits, u32 mask, int timeout)
+{
+ int t = 0;
+
+ do {
+ if ((iwl_read32(trans, addr) & mask) == (bits & mask))
+ return t;
+ udelay(IWL_POLL_INTERVAL);
+ t += IWL_POLL_INTERVAL;
+ } while (t < timeout);
+
+ return -ETIMEDOUT;
+}
+IWL_EXPORT_SYMBOL(iwl_poll_bit);
+
+u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
+{
+ u32 value = 0x5a5a5a5a;
+ unsigned long flags;
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ value = iwl_read32(trans, reg);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+
+ return value;
+}
+IWL_EXPORT_SYMBOL(iwl_read_direct32);
+
+void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
+{
+ unsigned long flags;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write32(trans, reg, value);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+}
+IWL_EXPORT_SYMBOL(iwl_write_direct32);
+
+int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
+ int timeout)
+{
+ int t = 0;
+
+ do {
+ if ((iwl_read_direct32(trans, addr) & mask) == mask)
+ return t;
+ udelay(IWL_POLL_INTERVAL);
+ t += IWL_POLL_INTERVAL;
+ } while (t < timeout);
+
+ return -ETIMEDOUT;
+}
+IWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
+
+u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs)
+{
+ u32 val = iwl_trans_read_prph(trans, ofs);
+ trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val);
+ return val;
+}
+IWL_EXPORT_SYMBOL(iwl_read_prph_no_grab);
+
+void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+ trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val);
+ iwl_trans_write_prph(trans, ofs, val);
+}
+IWL_EXPORT_SYMBOL(iwl_write_prph_no_grab);
+
+u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
+{
+ unsigned long flags;
+ u32 val = 0x5a5a5a5a;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ val = iwl_read_prph_no_grab(trans, ofs);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+ return val;
+}
+IWL_EXPORT_SYMBOL(iwl_read_prph);
+
+void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+ unsigned long flags;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write_prph_no_grab(trans, ofs, val);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+}
+IWL_EXPORT_SYMBOL(iwl_write_prph);
+
+int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
+ u32 bits, u32 mask, int timeout)
+{
+ int t = 0;
+
+ do {
+ if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
+ return t;
+ udelay(IWL_POLL_INTERVAL);
+ t += IWL_POLL_INTERVAL;
+ } while (t < timeout);
+
+ return -ETIMEDOUT;
+}
+
+void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
+{
+ unsigned long flags;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write_prph_no_grab(trans, ofs,
+ iwl_read_prph_no_grab(trans, ofs) |
+ mask);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+}
+IWL_EXPORT_SYMBOL(iwl_set_bits_prph);
+
+void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
+ u32 bits, u32 mask)
+{
+ unsigned long flags;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write_prph_no_grab(trans, ofs,
+ (iwl_read_prph_no_grab(trans, ofs) &
+ mask) | bits);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+}
+IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph);
+
+void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
+{
+ unsigned long flags;
+ u32 val;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ val = iwl_read_prph_no_grab(trans, ofs);
+ iwl_write_prph_no_grab(trans, ofs, (val & ~mask));
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+}
+IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
+
+void iwl_force_nmi(struct iwl_trans *trans)
+{
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ iwl_write_prph(trans, DEVICE_SET_NMI_REG,
+ DEVICE_SET_NMI_VAL_DRV);
+ iwl_write_prph(trans, DEVICE_SET_NMI_REG,
+ DEVICE_SET_NMI_VAL_HW);
+ } else {
+ iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG,
+ DEVICE_SET_NMI_8000_VAL);
+ iwl_write_prph(trans, DEVICE_SET_NMI_REG,
+ DEVICE_SET_NMI_VAL_DRV);
+ }
+}
+IWL_EXPORT_SYMBOL(iwl_force_nmi);
+
+static const char *get_fh_string(int cmd)
+{
+#define IWL_CMD(x) case x: return #x
+ switch (cmd) {
+ IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
+ IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
+ IWL_CMD(FH_RSCSR_CHNL0_WPTR);
+ IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
+ IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
+ IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
+ IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
+ IWL_CMD(FH_TSSR_TX_STATUS_REG);
+ IWL_CMD(FH_TSSR_TX_ERROR_REG);
+ default:
+ return "UNKNOWN";
+ }
+#undef IWL_CMD
+}
+
+int iwl_dump_fh(struct iwl_trans *trans, char **buf)
+{
+ int i;
+ static const u32 fh_tbl[] = {
+ FH_RSCSR_CHNL0_STTS_WPTR_REG,
+ FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+ FH_RSCSR_CHNL0_WPTR,
+ FH_MEM_RCSR_CHNL0_CONFIG_REG,
+ FH_MEM_RSSR_SHARED_CTRL_REG,
+ FH_MEM_RSSR_RX_STATUS_REG,
+ FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
+ FH_TSSR_TX_STATUS_REG,
+ FH_TSSR_TX_ERROR_REG
+ };
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (buf) {
+ int pos = 0;
+ size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
+ *buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "FH register values:\n");
+
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ " %34s: 0X%08x\n",
+ get_fh_string(fh_tbl[i]),
+ iwl_read_direct32(trans, fh_tbl[i]));
+
+ return pos;
+ }
+#endif
+
+ IWL_ERR(trans, "FH register values:\n");
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
+ IWL_ERR(trans, " %34s: 0X%08x\n",
+ get_fh_string(fh_tbl[i]),
+ iwl_read_direct32(trans, fh_tbl[i]));
+
+ return 0;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
new file mode 100644
index 000000000000..a9bcc788cae1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
@@ -0,0 +1,73 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_io_h__
+#define __iwl_io_h__
+
+#include "iwl-devtrace.h"
+#include "iwl-trans.h"
+
+void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val);
+void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val);
+u32 iwl_read32(struct iwl_trans *trans, u32 ofs);
+
+static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
+{
+ iwl_trans_set_bits_mask(trans, reg, mask, mask);
+}
+
+static inline void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
+{
+ iwl_trans_set_bits_mask(trans, reg, mask, 0);
+}
+
+int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
+ u32 bits, u32 mask, int timeout);
+int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
+ int timeout);
+
+u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg);
+void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value);
+
+
+u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs);
+u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs);
+void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val);
+void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
+int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
+ u32 bits, u32 mask, int timeout);
+void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
+void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
+ u32 bits, u32 mask);
+void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
+void iwl_force_nmi(struct iwl_trans *trans);
+
+/* Error handling */
+int iwl_dump_fh(struct iwl_trans *trans, char **buf);
+
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
new file mode 100644
index 000000000000..fd42f63f5e84
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
@@ -0,0 +1,138 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_modparams_h__
+#define __iwl_modparams_h__
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/gfp.h>
+#include <net/mac80211.h>
+
+extern struct iwl_mod_params iwlwifi_mod_params;
+
+enum iwl_power_level {
+ IWL_POWER_INDEX_1,
+ IWL_POWER_INDEX_2,
+ IWL_POWER_INDEX_3,
+ IWL_POWER_INDEX_4,
+ IWL_POWER_INDEX_5,
+ IWL_POWER_NUM
+};
+
+enum iwl_disable_11n {
+ IWL_DISABLE_HT_ALL = BIT(0),
+ IWL_DISABLE_HT_TXAGG = BIT(1),
+ IWL_DISABLE_HT_RXAGG = BIT(2),
+ IWL_ENABLE_HT_TXAGG = BIT(3),
+};
+
+enum iwl_amsdu_size {
+ IWL_AMSDU_4K = 0,
+ IWL_AMSDU_8K = 1,
+ IWL_AMSDU_12K = 2,
+};
+
+/**
+ * struct iwl_mod_params
+ *
+ * Holds the module parameters
+ *
+ * @sw_crypto: using hardware encryption, default = 0
+ * @disable_11n: disable 11n capabilities, default = 0,
+ * use IWL_[DIS,EN]ABLE_HT_* constants
+ * @amsdu_size: enable 8K amsdu size, default = 4K. enum iwl_amsdu_size.
+ * @restart_fw: restart firmware, default = 1
+ * @bt_coex_active: enable bt coex, default = true
+ * @led_mode: system default, default = 0
+ * @power_save: enable power save, default = false
+ * @power_level: power level, default = 1
+ * @debug_level: levels are IWL_DL_*
+ * @ant_coupling: antenna coupling in dB, default = 0
+ * @d0i3_disable: disable d0i3, default = 1,
+ * @d0i3_entry_delay: time to wait after no refs are taken before
+ * entering D0i3 (in msecs)
+ * @lar_disable: disable LAR (regulatory), default = 0
+ * @fw_monitor: allow to use firmware monitor
+ */
+struct iwl_mod_params {
+ int sw_crypto;
+ unsigned int disable_11n;
+ int amsdu_size;
+ bool restart_fw;
+ bool bt_coex_active;
+ int led_mode;
+ bool power_save;
+ int power_level;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ u32 debug_level;
+#endif
+ int ant_coupling;
+ char *nvm_file;
+ bool uapsd_disable;
+ bool d0i3_disable;
+ unsigned int d0i3_entry_delay;
+ bool lar_disable;
+ bool fw_monitor;
+};
+
+#endif /* #__iwl_modparams_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
new file mode 100644
index 000000000000..8aa1f2b7fdfc
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
@@ -0,0 +1,193 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/sched.h>
+#include <linux/export.h>
+
+#include "iwl-drv.h"
+#include "iwl-notif-wait.h"
+
+
+void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait)
+{
+ spin_lock_init(&notif_wait->notif_wait_lock);
+ INIT_LIST_HEAD(&notif_wait->notif_waits);
+ init_waitqueue_head(&notif_wait->notif_waitq);
+}
+IWL_EXPORT_SYMBOL(iwl_notification_wait_init);
+
+void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt)
+{
+ bool triggered = false;
+
+ if (!list_empty(&notif_wait->notif_waits)) {
+ struct iwl_notification_wait *w;
+
+ spin_lock(&notif_wait->notif_wait_lock);
+ list_for_each_entry(w, &notif_wait->notif_waits, list) {
+ int i;
+ bool found = false;
+
+ /*
+ * If it already finished (triggered) or has been
+ * aborted then don't evaluate it again to avoid races,
+ * Otherwise the function could be called again even
+ * though it returned true before
+ */
+ if (w->triggered || w->aborted)
+ continue;
+
+ for (i = 0; i < w->n_cmds; i++) {
+ if (w->cmds[i] ==
+ WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+
+ if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) {
+ w->triggered = true;
+ triggered = true;
+ }
+ }
+ spin_unlock(&notif_wait->notif_wait_lock);
+
+ }
+
+ if (triggered)
+ wake_up_all(&notif_wait->notif_waitq);
+}
+IWL_EXPORT_SYMBOL(iwl_notification_wait_notify);
+
+void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
+{
+ struct iwl_notification_wait *wait_entry;
+
+ spin_lock(&notif_wait->notif_wait_lock);
+ list_for_each_entry(wait_entry, &notif_wait->notif_waits, list)
+ wait_entry->aborted = true;
+ spin_unlock(&notif_wait->notif_wait_lock);
+
+ wake_up_all(&notif_wait->notif_waitq);
+}
+IWL_EXPORT_SYMBOL(iwl_abort_notification_waits);
+
+void
+iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_notification_wait *wait_entry,
+ const u16 *cmds, int n_cmds,
+ bool (*fn)(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data),
+ void *fn_data)
+{
+ if (WARN_ON(n_cmds > MAX_NOTIF_CMDS))
+ n_cmds = MAX_NOTIF_CMDS;
+
+ wait_entry->fn = fn;
+ wait_entry->fn_data = fn_data;
+ wait_entry->n_cmds = n_cmds;
+ memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(u16));
+ wait_entry->triggered = false;
+ wait_entry->aborted = false;
+
+ spin_lock_bh(&notif_wait->notif_wait_lock);
+ list_add(&wait_entry->list, &notif_wait->notif_waits);
+ spin_unlock_bh(&notif_wait->notif_wait_lock);
+}
+IWL_EXPORT_SYMBOL(iwl_init_notification_wait);
+
+int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_notification_wait *wait_entry,
+ unsigned long timeout)
+{
+ int ret;
+
+ ret = wait_event_timeout(notif_wait->notif_waitq,
+ wait_entry->triggered || wait_entry->aborted,
+ timeout);
+
+ spin_lock_bh(&notif_wait->notif_wait_lock);
+ list_del(&wait_entry->list);
+ spin_unlock_bh(&notif_wait->notif_wait_lock);
+
+ if (wait_entry->aborted)
+ return -EIO;
+
+ /* return value is always >= 0 */
+ if (ret <= 0)
+ return -ETIMEDOUT;
+ return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_wait_notification);
+
+void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_notification_wait *wait_entry)
+{
+ spin_lock_bh(&notif_wait->notif_wait_lock);
+ list_del(&wait_entry->list);
+ spin_unlock_bh(&notif_wait->notif_wait_lock);
+}
+IWL_EXPORT_SYMBOL(iwl_remove_notification);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
new file mode 100644
index 000000000000..0f9995ed71cd
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
@@ -0,0 +1,139 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_notif_wait_h__
+#define __iwl_notif_wait_h__
+
+#include <linux/wait.h>
+
+#include "iwl-trans.h"
+
+struct iwl_notif_wait_data {
+ struct list_head notif_waits;
+ spinlock_t notif_wait_lock;
+ wait_queue_head_t notif_waitq;
+};
+
+#define MAX_NOTIF_CMDS 5
+
+/**
+ * struct iwl_notification_wait - notification wait entry
+ * @list: list head for global list
+ * @fn: Function called with the notification. If the function
+ * returns true, the wait is over, if it returns false then
+ * the waiter stays blocked. If no function is given, any
+ * of the listed commands will unblock the waiter.
+ * @cmds: command IDs
+ * @n_cmds: number of command IDs
+ * @triggered: waiter should be woken up
+ * @aborted: wait was aborted
+ *
+ * This structure is not used directly, to wait for a
+ * notification declare it on the stack, and call
+ * iwlagn_init_notification_wait() with appropriate
+ * parameters. Then do whatever will cause the ucode
+ * to notify the driver, and to wait for that then
+ * call iwlagn_wait_notification().
+ *
+ * Each notification is one-shot. If at some point we
+ * need to support multi-shot notifications (which
+ * can't be allocated on the stack) we need to modify
+ * the code for them.
+ */
+struct iwl_notification_wait {
+ struct list_head list;
+
+ bool (*fn)(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt, void *data);
+ void *fn_data;
+
+ u16 cmds[MAX_NOTIF_CMDS];
+ u8 n_cmds;
+ bool triggered, aborted;
+};
+
+
+/* caller functions */
+void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data);
+void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt);
+void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data);
+
+/* user functions */
+void __acquires(wait_entry)
+iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data,
+ struct iwl_notification_wait *wait_entry,
+ const u16 *cmds, int n_cmds,
+ bool (*fn)(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt, void *data),
+ void *fn_data);
+
+int __must_check __releases(wait_entry)
+iwl_wait_notification(struct iwl_notif_wait_data *notif_data,
+ struct iwl_notification_wait *wait_entry,
+ unsigned long timeout);
+
+void __releases(wait_entry)
+iwl_remove_notification(struct iwl_notif_wait_data *notif_data,
+ struct iwl_notification_wait *wait_entry);
+
+#endif /* __iwl_notif_wait_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
new file mode 100644
index 000000000000..7b89bfc8c8ac
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -0,0 +1,842 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/etherdevice.h>
+#include <linux/pci.h>
+#include "iwl-drv.h"
+#include "iwl-modparams.h"
+#include "iwl-nvm-parse.h"
+
+/* NVM offsets (in words) definitions */
+enum wkp_nvm_offsets {
+ /* NVM HW-Section offset (in words) definitions */
+ HW_ADDR = 0x15,
+
+ /* NVM SW-Section offset (in words) definitions */
+ NVM_SW_SECTION = 0x1C0,
+ NVM_VERSION = 0,
+ RADIO_CFG = 1,
+ SKU = 2,
+ N_HW_ADDRS = 3,
+ NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION,
+
+ /* NVM calibration section offset (in words) definitions */
+ NVM_CALIB_SECTION = 0x2B8,
+ XTAL_CALIB = 0x316 - NVM_CALIB_SECTION
+};
+
+enum family_8000_nvm_offsets {
+ /* NVM HW-Section offset (in words) definitions */
+ HW_ADDR0_WFPM_FAMILY_8000 = 0x12,
+ HW_ADDR1_WFPM_FAMILY_8000 = 0x16,
+ HW_ADDR0_PCIE_FAMILY_8000 = 0x8A,
+ HW_ADDR1_PCIE_FAMILY_8000 = 0x8E,
+ MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
+
+ /* NVM SW-Section offset (in words) definitions */
+ NVM_SW_SECTION_FAMILY_8000 = 0x1C0,
+ NVM_VERSION_FAMILY_8000 = 0,
+ RADIO_CFG_FAMILY_8000 = 0,
+ SKU_FAMILY_8000 = 2,
+ N_HW_ADDRS_FAMILY_8000 = 3,
+
+ /* NVM REGULATORY -Section offset (in words) definitions */
+ NVM_CHANNELS_FAMILY_8000 = 0,
+ NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7,
+ NVM_LAR_OFFSET_FAMILY_8000 = 0x507,
+ NVM_LAR_ENABLED_FAMILY_8000 = 0x7,
+
+ /* NVM calibration section offset (in words) definitions */
+ NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8,
+ XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000
+};
+
+/* SKU Capabilities (actual values from NVM definition) */
+enum nvm_sku_bits {
+ NVM_SKU_CAP_BAND_24GHZ = BIT(0),
+ NVM_SKU_CAP_BAND_52GHZ = BIT(1),
+ NVM_SKU_CAP_11N_ENABLE = BIT(2),
+ NVM_SKU_CAP_11AC_ENABLE = BIT(3),
+ NVM_SKU_CAP_MIMO_DISABLE = BIT(5),
+};
+
+/*
+ * These are the channel numbers in the order that they are stored in the NVM
+ */
+static const u8 iwl_nvm_channels[] = {
+ /* 2.4 GHz */
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ /* 5 GHz */
+ 36, 40, 44 , 48, 52, 56, 60, 64,
+ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
+ 149, 153, 157, 161, 165
+};
+
+static const u8 iwl_nvm_channels_family_8000[] = {
+ /* 2.4 GHz */
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ /* 5 GHz */
+ 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
+ 149, 153, 157, 161, 165, 169, 173, 177, 181
+};
+
+#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
+#define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000)
+#define NUM_2GHZ_CHANNELS 14
+#define NUM_2GHZ_CHANNELS_FAMILY_8000 14
+#define FIRST_2GHZ_HT_MINUS 5
+#define LAST_2GHZ_HT_PLUS 9
+#define LAST_5GHZ_HT 165
+#define LAST_5GHZ_HT_FAMILY_8000 181
+#define N_HW_ADDR_MASK 0xF
+
+/* rate data (static) */
+static struct ieee80211_rate iwl_cfg80211_rates[] = {
+ { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
+ { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+ { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+ { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+ { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
+ { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
+ { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
+ { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
+ { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
+ { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
+ { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
+ { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
+};
+#define RATES_24_OFFS 0
+#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates)
+#define RATES_52_OFFS 4
+#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS)
+
+/**
+ * enum iwl_nvm_channel_flags - channel flags in NVM
+ * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
+ * @NVM_CHANNEL_IBSS: usable as an IBSS channel
+ * @NVM_CHANNEL_ACTIVE: active scanning allowed
+ * @NVM_CHANNEL_RADAR: radar detection required
+ * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed
+ * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
+ * on same channel on 2.4 or same UNII band on 5.2
+ * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
+ * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
+ * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
+ * @NVM_CHANNEL_160MHZ: 160 MHz channel okay (?)
+ */
+enum iwl_nvm_channel_flags {
+ NVM_CHANNEL_VALID = BIT(0),
+ NVM_CHANNEL_IBSS = BIT(1),
+ NVM_CHANNEL_ACTIVE = BIT(3),
+ NVM_CHANNEL_RADAR = BIT(4),
+ NVM_CHANNEL_INDOOR_ONLY = BIT(5),
+ NVM_CHANNEL_GO_CONCURRENT = BIT(6),
+ NVM_CHANNEL_WIDE = BIT(8),
+ NVM_CHANNEL_40MHZ = BIT(9),
+ NVM_CHANNEL_80MHZ = BIT(10),
+ NVM_CHANNEL_160MHZ = BIT(11),
+};
+
+#define CHECK_AND_PRINT_I(x) \
+ ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "")
+
+static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz,
+ u16 nvm_flags, const struct iwl_cfg *cfg)
+{
+ u32 flags = IEEE80211_CHAN_NO_HT40;
+ u32 last_5ghz_ht = LAST_5GHZ_HT;
+
+ if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
+
+ if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if (ch_num <= LAST_2GHZ_HT_PLUS)
+ flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+ if (ch_num >= FIRST_2GHZ_HT_MINUS)
+ flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+ } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+ flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+ else
+ flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+ }
+ if (!(nvm_flags & NVM_CHANNEL_80MHZ))
+ flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (!(nvm_flags & NVM_CHANNEL_160MHZ))
+ flags |= IEEE80211_CHAN_NO_160MHZ;
+
+ if (!(nvm_flags & NVM_CHANNEL_IBSS))
+ flags |= IEEE80211_CHAN_NO_IR;
+
+ if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
+ flags |= IEEE80211_CHAN_NO_IR;
+
+ if (nvm_flags & NVM_CHANNEL_RADAR)
+ flags |= IEEE80211_CHAN_RADAR;
+
+ if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
+ flags |= IEEE80211_CHAN_INDOOR_ONLY;
+
+ /* Set the GO concurrent flag only in case that NO_IR is set.
+ * Otherwise it is meaningless
+ */
+ if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+ (flags & IEEE80211_CHAN_NO_IR))
+ flags |= IEEE80211_CHAN_IR_CONCURRENT;
+
+ return flags;
+}
+
+static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ const __le16 * const nvm_ch_flags,
+ bool lar_supported)
+{
+ int ch_idx;
+ int n_channels = 0;
+ struct ieee80211_channel *channel;
+ u16 ch_flags;
+ bool is_5ghz;
+ int num_of_ch, num_2ghz_channels;
+ const u8 *nvm_chan;
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ num_of_ch = IWL_NUM_CHANNELS;
+ nvm_chan = &iwl_nvm_channels[0];
+ num_2ghz_channels = NUM_2GHZ_CHANNELS;
+ } else {
+ num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000;
+ nvm_chan = &iwl_nvm_channels_family_8000[0];
+ num_2ghz_channels = NUM_2GHZ_CHANNELS_FAMILY_8000;
+ }
+
+ for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
+ ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
+
+ if (ch_idx >= num_2ghz_channels &&
+ !data->sku_cap_band_52GHz_enable)
+ continue;
+
+ if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) {
+ /*
+ * Channels might become valid later if lar is
+ * supported, hence we still want to add them to
+ * the list of supported channels to cfg80211.
+ */
+ IWL_DEBUG_EEPROM(dev,
+ "Ch. %d Flags %x [%sGHz] - No traffic\n",
+ nvm_chan[ch_idx],
+ ch_flags,
+ (ch_idx >= num_2ghz_channels) ?
+ "5.2" : "2.4");
+ continue;
+ }
+
+ channel = &data->channels[n_channels];
+ n_channels++;
+
+ channel->hw_value = nvm_chan[ch_idx];
+ channel->band = (ch_idx < num_2ghz_channels) ?
+ IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+ channel->center_freq =
+ ieee80211_channel_to_frequency(
+ channel->hw_value, channel->band);
+
+ /* Initialize regulatory-based run-time data */
+
+ /*
+ * Default value - highest tx power value. max_power
+ * is not used in mvm, and is used for backwards compatibility
+ */
+ channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
+ is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
+
+ /* don't put limitations in case we're using LAR */
+ if (!lar_supported)
+ channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx],
+ ch_idx, is_5ghz,
+ ch_flags, cfg);
+ else
+ channel->flags = 0;
+
+ IWL_DEBUG_EEPROM(dev,
+ "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
+ channel->hw_value,
+ is_5ghz ? "5.2" : "2.4",
+ CHECK_AND_PRINT_I(VALID),
+ CHECK_AND_PRINT_I(IBSS),
+ CHECK_AND_PRINT_I(ACTIVE),
+ CHECK_AND_PRINT_I(RADAR),
+ CHECK_AND_PRINT_I(WIDE),
+ CHECK_AND_PRINT_I(INDOOR_ONLY),
+ CHECK_AND_PRINT_I(GO_CONCURRENT),
+ ch_flags,
+ channel->max_power,
+ ((ch_flags & NVM_CHANNEL_IBSS) &&
+ !(ch_flags & NVM_CHANNEL_RADAR))
+ ? "" : "not ");
+ }
+
+ return n_channels;
+}
+
+static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ struct ieee80211_sta_vht_cap *vht_cap,
+ u8 tx_chains, u8 rx_chains)
+{
+ int num_rx_ants = num_of_ant(rx_chains);
+ int num_tx_ants = num_of_ant(tx_chains);
+ unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?:
+ IEEE80211_VHT_MAX_AMPDU_1024K);
+
+ vht_cap->vht_supported = true;
+
+ vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_RXSTBC_1 |
+ IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+ 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
+ max_ampdu_exponent <<
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+ if (cfg->ht_params->ldpc)
+ vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
+
+ if (data->sku_cap_mimo_disabled) {
+ num_rx_ants = 1;
+ num_tx_ants = 1;
+ }
+
+ if (num_tx_ants > 1)
+ vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
+ else
+ vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+
+ switch (iwlwifi_mod_params.amsdu_size) {
+ case IWL_AMSDU_4K:
+ vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
+ break;
+ case IWL_AMSDU_8K:
+ vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
+ break;
+ case IWL_AMSDU_12K:
+ vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
+ break;
+ default:
+ break;
+ }
+
+ vht_cap->vht_mcs.rx_mcs_map =
+ cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 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);
+
+ if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) {
+ vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
+ /* this works because NOT_SUPPORTED == 3 */
+ vht_cap->vht_mcs.rx_mcs_map |=
+ cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2);
+ }
+
+ vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
+}
+
+static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ const __le16 *ch_section,
+ u8 tx_chains, u8 rx_chains, bool lar_supported)
+{
+ int n_channels;
+ int n_used = 0;
+ struct ieee80211_supported_band *sband;
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ n_channels = iwl_init_channel_map(
+ dev, cfg, data,
+ &ch_section[NVM_CHANNELS], lar_supported);
+ else
+ n_channels = iwl_init_channel_map(
+ dev, cfg, data,
+ &ch_section[NVM_CHANNELS_FAMILY_8000],
+ lar_supported);
+
+ sband = &data->bands[IEEE80211_BAND_2GHZ];
+ sband->band = IEEE80211_BAND_2GHZ;
+ sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
+ sband->n_bitrates = N_RATES_24;
+ n_used += iwl_init_sband_channels(data, sband, n_channels,
+ IEEE80211_BAND_2GHZ);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
+ tx_chains, rx_chains);
+
+ sband = &data->bands[IEEE80211_BAND_5GHZ];
+ sband->band = IEEE80211_BAND_5GHZ;
+ sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
+ sband->n_bitrates = N_RATES_52;
+ n_used += iwl_init_sband_channels(data, sband, n_channels,
+ IEEE80211_BAND_5GHZ);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
+ tx_chains, rx_chains);
+ if (data->sku_cap_11ac_enable)
+ iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
+ tx_chains, rx_chains);
+
+ if (n_channels != n_used)
+ IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
+ n_used, n_channels);
+}
+
+static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+ const __le16 *phy_sku)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + SKU);
+
+ return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000));
+}
+
+static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + NVM_VERSION);
+ else
+ return le32_to_cpup((__le32 *)(nvm_sw +
+ NVM_VERSION_FAMILY_8000));
+}
+
+static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+ const __le16 *phy_sku)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + RADIO_CFG);
+
+ return le32_to_cpup((__le32 *)(phy_sku + RADIO_CFG_FAMILY_8000));
+
+}
+
+static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
+{
+ int n_hw_addr;
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + N_HW_ADDRS);
+
+ n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000));
+
+ return n_hw_addr & N_HW_ADDR_MASK;
+}
+
+static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ u32 radio_cfg)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
+ data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
+ data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
+ data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
+ return;
+ }
+
+ /* set the radio configuration for family 8000 */
+ data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg);
+ data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg);
+ data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg);
+ data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg);
+ data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg);
+ data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg);
+}
+
+static void iwl_set_hw_address(const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ const __le16 *nvm_sec)
+{
+ const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR);
+
+ /* The byte order is little endian 16 bit, meaning 214365 */
+ data->hw_addr[0] = hw_addr[1];
+ data->hw_addr[1] = hw_addr[0];
+ data->hw_addr[2] = hw_addr[3];
+ data->hw_addr[3] = hw_addr[2];
+ data->hw_addr[4] = hw_addr[5];
+ data->hw_addr[5] = hw_addr[4];
+}
+
+static void iwl_set_hw_address_family_8000(struct device *dev,
+ const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ const __le16 *mac_override,
+ const __le16 *nvm_hw,
+ u32 mac_addr0, u32 mac_addr1)
+{
+ const u8 *hw_addr;
+
+ if (mac_override) {
+ static const u8 reserved_mac[] = {
+ 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
+ };
+
+ hw_addr = (const u8 *)(mac_override +
+ MAC_ADDRESS_OVERRIDE_FAMILY_8000);
+
+ /*
+ * Store the MAC address from MAO section.
+ * No byte swapping is required in MAO section
+ */
+ memcpy(data->hw_addr, hw_addr, ETH_ALEN);
+
+ /*
+ * Force the use of the OTP MAC address in case of reserved MAC
+ * address in the NVM, or if address is given but invalid.
+ */
+ if (is_valid_ether_addr(data->hw_addr) &&
+ memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0)
+ return;
+
+ IWL_ERR_DEV(dev,
+ "mac address from nvm override section is not valid\n");
+ }
+
+ if (nvm_hw) {
+ /* read the MAC address from HW resisters */
+ hw_addr = (const u8 *)&mac_addr0;
+ data->hw_addr[0] = hw_addr[3];
+ data->hw_addr[1] = hw_addr[2];
+ data->hw_addr[2] = hw_addr[1];
+ data->hw_addr[3] = hw_addr[0];
+
+ hw_addr = (const u8 *)&mac_addr1;
+ data->hw_addr[4] = hw_addr[1];
+ data->hw_addr[5] = hw_addr[0];
+
+ if (!is_valid_ether_addr(data->hw_addr))
+ IWL_ERR_DEV(dev,
+ "mac address from hw section is not valid\n");
+
+ return;
+ }
+
+ IWL_ERR_DEV(dev, "mac address is not found\n");
+}
+
+struct iwl_nvm_data *
+iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+ const __le16 *nvm_hw, const __le16 *nvm_sw,
+ const __le16 *nvm_calib, const __le16 *regulatory,
+ const __le16 *mac_override, const __le16 *phy_sku,
+ u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
+ u32 mac_addr0, u32 mac_addr1)
+{
+ struct iwl_nvm_data *data;
+ u32 sku;
+ u32 radio_cfg;
+ u16 lar_config;
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ data = kzalloc(sizeof(*data) +
+ sizeof(struct ieee80211_channel) *
+ IWL_NUM_CHANNELS,
+ GFP_KERNEL);
+ else
+ data = kzalloc(sizeof(*data) +
+ sizeof(struct ieee80211_channel) *
+ IWL_NUM_CHANNELS_FAMILY_8000,
+ GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
+
+ radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku);
+ iwl_set_radio_cfg(cfg, data, radio_cfg);
+ if (data->valid_tx_ant)
+ tx_chains &= data->valid_tx_ant;
+ if (data->valid_rx_ant)
+ rx_chains &= data->valid_rx_ant;
+
+ sku = iwl_get_sku(cfg, nvm_sw, phy_sku);
+ data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
+ data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
+ data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
+ if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
+ data->sku_cap_11n_enable = false;
+ data->sku_cap_11ac_enable = data->sku_cap_11n_enable &&
+ (sku & NVM_SKU_CAP_11AC_ENABLE);
+ data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE;
+
+ data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ /* Checking for required sections */
+ if (!nvm_calib) {
+ IWL_ERR_DEV(dev,
+ "Can't parse empty Calib NVM sections\n");
+ kfree(data);
+ return NULL;
+ }
+ /* in family 8000 Xtal calibration values moved to OTP */
+ data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
+ data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
+ }
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ iwl_set_hw_address(cfg, data, nvm_hw);
+
+ iwl_init_sbands(dev, cfg, data, nvm_sw,
+ tx_chains, rx_chains, lar_fw_supported);
+ } else {
+ u16 lar_offset = data->nvm_version < 0xE39 ?
+ NVM_LAR_OFFSET_FAMILY_8000_OLD :
+ NVM_LAR_OFFSET_FAMILY_8000;
+
+ lar_config = le16_to_cpup(regulatory + lar_offset);
+ data->lar_enabled = !!(lar_config &
+ NVM_LAR_ENABLED_FAMILY_8000);
+
+ /* MAC address in family 8000 */
+ iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
+ nvm_hw, mac_addr0, mac_addr1);
+
+ iwl_init_sbands(dev, cfg, data, regulatory,
+ tx_chains, rx_chains,
+ lar_fw_supported && data->lar_enabled);
+ }
+
+ data->calib_version = 255;
+
+ return data;
+}
+IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
+
+static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,
+ int ch_idx, u16 nvm_flags,
+ const struct iwl_cfg *cfg)
+{
+ u32 flags = NL80211_RRF_NO_HT40;
+ u32 last_5ghz_ht = LAST_5GHZ_HT;
+
+ if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
+
+ if (ch_idx < NUM_2GHZ_CHANNELS &&
+ (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
+ flags &= ~NL80211_RRF_NO_HT40PLUS;
+ if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
+ flags &= ~NL80211_RRF_NO_HT40MINUS;
+ } else if (nvm_chan[ch_idx] <= last_5ghz_ht &&
+ (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+ flags &= ~NL80211_RRF_NO_HT40PLUS;
+ else
+ flags &= ~NL80211_RRF_NO_HT40MINUS;
+ }
+
+ if (!(nvm_flags & NVM_CHANNEL_80MHZ))
+ flags |= NL80211_RRF_NO_80MHZ;
+ if (!(nvm_flags & NVM_CHANNEL_160MHZ))
+ flags |= NL80211_RRF_NO_160MHZ;
+
+ if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
+ flags |= NL80211_RRF_NO_IR;
+
+ if (nvm_flags & NVM_CHANNEL_RADAR)
+ flags |= NL80211_RRF_DFS;
+
+ if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
+ flags |= NL80211_RRF_NO_OUTDOOR;
+
+ /* Set the GO concurrent flag only in case that NO_IR is set.
+ * Otherwise it is meaningless
+ */
+ if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+ (flags & NL80211_RRF_NO_IR))
+ flags |= NL80211_RRF_GO_CONCURRENT;
+
+ return flags;
+}
+
+struct ieee80211_regdomain *
+iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+ int num_of_ch, __le32 *channels, u16 fw_mcc)
+{
+ int ch_idx;
+ u16 ch_flags, prev_ch_flags = 0;
+ const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
+ iwl_nvm_channels_family_8000 : iwl_nvm_channels;
+ struct ieee80211_regdomain *regd;
+ int size_of_regd;
+ struct ieee80211_reg_rule *rule;
+ enum ieee80211_band band;
+ int center_freq, prev_center_freq = 0;
+ int valid_rules = 0;
+ bool new_rule;
+ int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
+ IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS;
+
+ if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES))
+ return ERR_PTR(-EINVAL);
+
+ if (WARN_ON(num_of_ch > max_num_ch))
+ num_of_ch = max_num_ch;
+
+ IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n",
+ num_of_ch);
+
+ /* build a regdomain rule for every valid channel */
+ size_of_regd =
+ sizeof(struct ieee80211_regdomain) +
+ num_of_ch * sizeof(struct ieee80211_reg_rule);
+
+ regd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!regd)
+ return ERR_PTR(-ENOMEM);
+
+ for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
+ ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
+ band = (ch_idx < NUM_2GHZ_CHANNELS) ?
+ IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+ center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx],
+ band);
+ new_rule = false;
+
+ if (!(ch_flags & NVM_CHANNEL_VALID)) {
+ IWL_DEBUG_DEV(dev, IWL_DL_LAR,
+ "Ch. %d Flags %x [%sGHz] - No traffic\n",
+ nvm_chan[ch_idx],
+ ch_flags,
+ (ch_idx >= NUM_2GHZ_CHANNELS) ?
+ "5.2" : "2.4");
+ continue;
+ }
+
+ /* we can't continue the same rule */
+ if (ch_idx == 0 || prev_ch_flags != ch_flags ||
+ center_freq - prev_center_freq > 20) {
+ valid_rules++;
+ new_rule = true;
+ }
+
+ rule = &regd->reg_rules[valid_rules - 1];
+
+ if (new_rule)
+ rule->freq_range.start_freq_khz =
+ MHZ_TO_KHZ(center_freq - 10);
+
+ rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10);
+
+ /* this doesn't matter - not used by FW */
+ rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
+ rule->power_rule.max_eirp =
+ DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER);
+
+ rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx,
+ ch_flags, cfg);
+
+ /* rely on auto-calculation to merge BW of contiguous chans */
+ rule->flags |= NL80211_RRF_AUTO_BW;
+ rule->freq_range.max_bandwidth_khz = 0;
+
+ prev_ch_flags = ch_flags;
+ prev_center_freq = center_freq;
+
+ IWL_DEBUG_DEV(dev, IWL_DL_LAR,
+ "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n",
+ center_freq,
+ band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4",
+ CHECK_AND_PRINT_I(VALID),
+ CHECK_AND_PRINT_I(ACTIVE),
+ CHECK_AND_PRINT_I(RADAR),
+ CHECK_AND_PRINT_I(WIDE),
+ CHECK_AND_PRINT_I(40MHZ),
+ CHECK_AND_PRINT_I(80MHZ),
+ CHECK_AND_PRINT_I(160MHZ),
+ CHECK_AND_PRINT_I(INDOOR_ONLY),
+ CHECK_AND_PRINT_I(GO_CONCURRENT),
+ ch_flags,
+ ((ch_flags & NVM_CHANNEL_ACTIVE) &&
+ !(ch_flags & NVM_CHANNEL_RADAR))
+ ? "" : "not ");
+ }
+
+ regd->n_reg_rules = valid_rules;
+
+ /* set alpha2 from FW. */
+ regd->alpha2[0] = fw_mcc >> 8;
+ regd->alpha2[1] = fw_mcc & 0xff;
+
+ return regd;
+}
+IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
new file mode 100644
index 000000000000..92466ee72806
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#ifndef __iwl_nvm_parse_h__
+#define __iwl_nvm_parse_h__
+
+#include <net/cfg80211.h>
+#include "iwl-eeprom-parse.h"
+
+/**
+ * iwl_parse_nvm_data - parse NVM data and return values
+ *
+ * This function parses all NVM values we need and then
+ * returns a (newly allocated) struct containing all the
+ * relevant values for driver use. The struct must be freed
+ * later with iwl_free_nvm_data().
+ */
+struct iwl_nvm_data *
+iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+ const __le16 *nvm_hw, const __le16 *nvm_sw,
+ const __le16 *nvm_calib, const __le16 *regulatory,
+ const __le16 *mac_override, const __le16 *phy_sku,
+ u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
+ u32 mac_addr0, u32 mac_addr1);
+
+/**
+ * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
+ *
+ * This function parses the regulatory channel data received as a
+ * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain,
+ * to be fed into the regulatory core. An ERR_PTR is returned on error.
+ * If not given to the regulatory core, the user is responsible for freeing
+ * the regdomain returned here with kfree.
+ */
+struct ieee80211_regdomain *
+iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+ int num_of_ch, __le32 *channels, u16 fw_mcc);
+
+#endif /* __iwl_nvm_parse_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
new file mode 100644
index 000000000000..b49eda8150bb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
@@ -0,0 +1,282 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_op_mode_h__
+#define __iwl_op_mode_h__
+
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+
+struct iwl_op_mode;
+struct iwl_trans;
+struct sk_buff;
+struct iwl_device_cmd;
+struct iwl_rx_cmd_buffer;
+struct iwl_fw;
+struct iwl_cfg;
+
+/**
+ * DOC: Operational mode - what is it ?
+ *
+ * The operational mode (a.k.a. op_mode) is the layer that implements
+ * mac80211's handlers. It knows two APIs: mac80211's and the fw's. It uses
+ * the transport API to access the HW. The op_mode doesn't need to know how the
+ * underlying HW works, since the transport layer takes care of that.
+ *
+ * There can be several op_mode: i.e. different fw APIs will require two
+ * different op_modes. This is why the op_mode is virtualized.
+ */
+
+/**
+ * DOC: Life cycle of the Operational mode
+ *
+ * The operational mode has a very simple life cycle.
+ *
+ * 1) The driver layer (iwl-drv.c) chooses the op_mode based on the
+ * capabilities advertised by the fw file (in TLV format).
+ * 2) The driver layer starts the op_mode (ops->start)
+ * 3) The op_mode registers mac80211
+ * 4) The op_mode is governed by mac80211
+ * 5) The driver layer stops the op_mode
+ */
+
+/**
+ * struct iwl_op_mode_ops - op_mode specific operations
+ *
+ * The op_mode exports its ops so that external components can start it and
+ * interact with it. The driver layer typically calls the start and stop
+ * handlers, the transport layer calls the others.
+ *
+ * All the handlers MUST be implemented, except @rx_rss which can be left
+ * out *iff* the opmode will never run on hardware with multi-queue capability.
+ *
+ * @start: start the op_mode. The transport layer is already allocated.
+ * May sleep
+ * @stop: stop the op_mode. Must free all the memory allocated.
+ * May sleep
+ * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
+ * HCMD this Rx responds to. Can't sleep.
+ * @rx_rss: data queue RX notification to the op_mode, for (data) notifications
+ * received on the RSS queue(s). The queue parameter indicates which of the
+ * RSS queues received this frame; it will always be non-zero.
+ * This method must not sleep.
+ * @async_cb: called when an ASYNC command with CMD_WANT_ASYNC_CALLBACK set
+ * completes. Must be atomic.
+ * @queue_full: notifies that a HW queue is full.
+ * Must be atomic and called with BH disabled.
+ * @queue_not_full: notifies that a HW queue is not full any more.
+ * Must be atomic and called with BH disabled.
+ * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that
+ * the radio is killed. Return %true if the device should be stopped by
+ * the transport immediately after the call. May sleep.
+ * @free_skb: allows the transport layer to free skbs that haven't been
+ * reclaimed by the op_mode. This can happen when the driver is freed and
+ * there are Tx packets pending in the transport layer.
+ * Must be atomic
+ * @nic_error: error notification. Must be atomic and must be called with BH
+ * disabled.
+ * @cmd_queue_full: Called when the command queue gets full. Must be atomic and
+ * called with BH disabled.
+ * @nic_config: configure NIC, called before firmware is started.
+ * May sleep
+ * @wimax_active: invoked when WiMax becomes active. May sleep
+ * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3
+ * entrance is aborted (e.g. due to held reference). May sleep.
+ * @exit_d0i3: configure the fw to exit d0i3. May sleep.
+ */
+struct iwl_op_mode_ops {
+ struct iwl_op_mode *(*start)(struct iwl_trans *trans,
+ const struct iwl_cfg *cfg,
+ const struct iwl_fw *fw,
+ struct dentry *dbgfs_dir);
+ void (*stop)(struct iwl_op_mode *op_mode);
+ void (*rx)(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb);
+ void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, unsigned int queue);
+ void (*async_cb)(struct iwl_op_mode *op_mode,
+ const struct iwl_device_cmd *cmd);
+ void (*queue_full)(struct iwl_op_mode *op_mode, int queue);
+ void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue);
+ bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
+ void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
+ void (*nic_error)(struct iwl_op_mode *op_mode);
+ void (*cmd_queue_full)(struct iwl_op_mode *op_mode);
+ void (*nic_config)(struct iwl_op_mode *op_mode);
+ void (*wimax_active)(struct iwl_op_mode *op_mode);
+ int (*enter_d0i3)(struct iwl_op_mode *op_mode);
+ int (*exit_d0i3)(struct iwl_op_mode *op_mode);
+};
+
+int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops);
+void iwl_opmode_deregister(const char *name);
+
+/**
+ * struct iwl_op_mode - operational mode
+ * @ops: pointer to its own ops
+ *
+ * This holds an implementation of the mac80211 / fw API.
+ */
+struct iwl_op_mode {
+ const struct iwl_op_mode_ops *ops;
+
+ char op_mode_specific[0] __aligned(sizeof(void *));
+};
+
+static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode)
+{
+ might_sleep();
+ op_mode->ops->stop(op_mode);
+}
+
+static inline void iwl_op_mode_rx(struct iwl_op_mode *op_mode,
+ struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ return op_mode->ops->rx(op_mode, napi, rxb);
+}
+
+static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode,
+ struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb,
+ unsigned int queue)
+{
+ op_mode->ops->rx_rss(op_mode, napi, rxb, queue);
+}
+
+static inline void iwl_op_mode_async_cb(struct iwl_op_mode *op_mode,
+ const struct iwl_device_cmd *cmd)
+{
+ if (op_mode->ops->async_cb)
+ op_mode->ops->async_cb(op_mode, cmd);
+}
+
+static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode,
+ int queue)
+{
+ op_mode->ops->queue_full(op_mode, queue);
+}
+
+static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode,
+ int queue)
+{
+ op_mode->ops->queue_not_full(op_mode, queue);
+}
+
+static inline bool __must_check
+iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, bool state)
+{
+ might_sleep();
+ return op_mode->ops->hw_rf_kill(op_mode, state);
+}
+
+static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode,
+ struct sk_buff *skb)
+{
+ op_mode->ops->free_skb(op_mode, skb);
+}
+
+static inline void iwl_op_mode_nic_error(struct iwl_op_mode *op_mode)
+{
+ op_mode->ops->nic_error(op_mode);
+}
+
+static inline void iwl_op_mode_cmd_queue_full(struct iwl_op_mode *op_mode)
+{
+ op_mode->ops->cmd_queue_full(op_mode);
+}
+
+static inline void iwl_op_mode_nic_config(struct iwl_op_mode *op_mode)
+{
+ might_sleep();
+ op_mode->ops->nic_config(op_mode);
+}
+
+static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode)
+{
+ might_sleep();
+ op_mode->ops->wimax_active(op_mode);
+}
+
+static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode)
+{
+ might_sleep();
+
+ if (!op_mode->ops->enter_d0i3)
+ return 0;
+ return op_mode->ops->enter_d0i3(op_mode);
+}
+
+static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+ might_sleep();
+
+ if (!op_mode->ops->exit_d0i3)
+ return 0;
+ return op_mode->ops->exit_d0i3(op_mode);
+}
+
+#endif /* __iwl_op_mode_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c
new file mode 100644
index 000000000000..4a4dea08751c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c
@@ -0,0 +1,471 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/export.h>
+
+#include "iwl-drv.h"
+#include "iwl-phy-db.h"
+#include "iwl-debug.h"
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+
+#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */
+#define IWL_NUM_PAPD_CH_GROUPS 9
+#define IWL_NUM_TXP_CH_GROUPS 9
+
+struct iwl_phy_db_entry {
+ u16 size;
+ u8 *data;
+};
+
+/**
+ * struct iwl_phy_db - stores phy configuration and calibration data.
+ *
+ * @cfg: phy configuration.
+ * @calib_nch: non channel specific calibration data.
+ * @calib_ch: channel specific calibration data.
+ * @calib_ch_group_papd: calibration data related to papd channel group.
+ * @calib_ch_group_txp: calibration data related to tx power chanel group.
+ */
+struct iwl_phy_db {
+ struct iwl_phy_db_entry cfg;
+ struct iwl_phy_db_entry calib_nch;
+ 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];
+
+ struct iwl_trans *trans;
+};
+
+enum iwl_phy_db_section_type {
+ IWL_PHY_DB_CFG = 1,
+ IWL_PHY_DB_CALIB_NCH,
+ IWL_PHY_DB_UNUSED,
+ IWL_PHY_DB_CALIB_CHG_PAPD,
+ IWL_PHY_DB_CALIB_CHG_TXP,
+ IWL_PHY_DB_MAX
+};
+
+#define PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */
+
+/*
+ * phy db - configure operational ucode
+ */
+struct iwl_phy_db_cmd {
+ __le16 type;
+ __le16 length;
+ u8 data[];
+} __packed;
+
+/* for parsing of tx power channel group data that comes from the firmware*/
+struct iwl_phy_db_chg_txp {
+ __le32 space;
+ __le16 max_channel_idx;
+} __packed;
+
+/*
+ * phy db - Receive phy db chunk after calibrations
+ */
+struct iwl_calib_res_notif_phy_db {
+ __le16 type;
+ __le16 length;
+ u8 data[];
+} __packed;
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
+{
+ struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
+ GFP_KERNEL);
+
+ if (!phy_db)
+ return phy_db;
+
+ phy_db->trans = trans;
+
+ /* TODO: add default values of the phy db. */
+ return phy_db;
+}
+IWL_EXPORT_SYMBOL(iwl_phy_db_init);
+
+/*
+ * get phy db section: returns a pointer to a phy db section specified by
+ * type and channel group id.
+ */
+static struct iwl_phy_db_entry *
+iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type,
+ u16 chg_id)
+{
+ if (!phy_db || type >= IWL_PHY_DB_MAX)
+ return NULL;
+
+ switch (type) {
+ case IWL_PHY_DB_CFG:
+ return &phy_db->cfg;
+ case IWL_PHY_DB_CALIB_NCH:
+ return &phy_db->calib_nch;
+ case IWL_PHY_DB_CALIB_CHG_PAPD:
+ if (chg_id >= IWL_NUM_PAPD_CH_GROUPS)
+ return NULL;
+ return &phy_db->calib_ch_group_papd[chg_id];
+ case IWL_PHY_DB_CALIB_CHG_TXP:
+ if (chg_id >= IWL_NUM_TXP_CH_GROUPS)
+ return NULL;
+ return &phy_db->calib_ch_group_txp[chg_id];
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type,
+ u16 chg_id)
+{
+ struct iwl_phy_db_entry *entry =
+ iwl_phy_db_get_section(phy_db, type, chg_id);
+ if (!entry)
+ return;
+
+ kfree(entry->data);
+ entry->data = NULL;
+ entry->size = 0;
+}
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db)
+{
+ int i;
+
+ if (!phy_db)
+ return;
+
+ 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);
+ 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++)
+ iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
+
+ kfree(phy_db);
+}
+IWL_EXPORT_SYMBOL(iwl_phy_db_free);
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
+ gfp_t alloc_ctx)
+{
+ struct iwl_calib_res_notif_phy_db *phy_db_notif =
+ (struct iwl_calib_res_notif_phy_db *)pkt->data;
+ enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type);
+ u16 size = le16_to_cpu(phy_db_notif->length);
+ struct iwl_phy_db_entry *entry;
+ u16 chg_id = 0;
+
+ if (!phy_db)
+ return -EINVAL;
+
+ if (type == IWL_PHY_DB_CALIB_CHG_PAPD ||
+ type == IWL_PHY_DB_CALIB_CHG_TXP)
+ chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
+
+ entry = iwl_phy_db_get_section(phy_db, type, chg_id);
+ if (!entry)
+ return -EINVAL;
+
+ kfree(entry->data);
+ entry->data = kmemdup(phy_db_notif->data, size, alloc_ctx);
+ if (!entry->data) {
+ entry->size = 0;
+ return -ENOMEM;
+ }
+
+ entry->size = size;
+
+ IWL_DEBUG_INFO(phy_db->trans,
+ "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
+ __func__, __LINE__, type, size);
+
+ return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
+
+static int is_valid_channel(u16 ch_id)
+{
+ if (ch_id <= 14 ||
+ (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
+ (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
+ (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
+ return 1;
+ return 0;
+}
+
+static u8 ch_id_to_ch_index(u16 ch_id)
+{
+ if (WARN_ON(!is_valid_channel(ch_id)))
+ return 0xff;
+
+ if (ch_id <= 14)
+ return ch_id - 1;
+ if (ch_id <= 64)
+ return (ch_id + 20) / 4;
+ if (ch_id <= 140)
+ return (ch_id - 12) / 4;
+ return (ch_id - 13) / 4;
+}
+
+
+static u16 channel_id_to_papd(u16 ch_id)
+{
+ if (WARN_ON(!is_valid_channel(ch_id)))
+ return 0xff;
+
+ if (1 <= ch_id && ch_id <= 14)
+ return 0;
+ if (36 <= ch_id && ch_id <= 64)
+ return 1;
+ if (100 <= ch_id && ch_id <= 140)
+ return 2;
+ return 3;
+}
+
+static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
+{
+ struct iwl_phy_db_chg_txp *txp_chg;
+ int i;
+ u8 ch_index = ch_id_to_ch_index(ch_id);
+ if (ch_index == 0xff)
+ return 0xff;
+
+ for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) {
+ txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
+ if (!txp_chg)
+ return 0xff;
+ /*
+ * Looking for the first channel group that its max channel is
+ * higher then wanted channel.
+ */
+ if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
+ return i;
+ }
+ return 0xff;
+}
+static
+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;
+ u16 ch_group_id = 0;
+
+ if (!phy_db)
+ return -EINVAL;
+
+ /* find wanted channel group */
+ if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
+ ch_group_id = channel_id_to_papd(ch_id);
+ else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
+ ch_group_id = channel_id_to_txp(phy_db, ch_id);
+
+ entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
+ if (!entry)
+ return -EINVAL;
+
+ *data = entry->data;
+ *size = entry->size;
+
+ IWL_DEBUG_INFO(phy_db->trans,
+ "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
+ __func__, __LINE__, type, *size);
+
+ return 0;
+}
+
+static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
+ u16 length, void *data)
+{
+ struct iwl_phy_db_cmd phy_db_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = PHY_DB_CMD,
+ };
+
+ IWL_DEBUG_INFO(phy_db->trans,
+ "Sending PHY-DB hcmd of type %d, of length %d\n",
+ type, length);
+
+ /* Set phy db cmd variables */
+ phy_db_cmd.type = cpu_to_le16(type);
+ phy_db_cmd.length = cpu_to_le16(length);
+
+ /* Set hcmd variables */
+ cmd.data[0] = &phy_db_cmd;
+ cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
+ cmd.data[1] = data;
+ cmd.len[1] = length;
+ cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
+
+ return iwl_trans_send_cmd(phy_db->trans, &cmd);
+}
+
+static int iwl_phy_db_send_all_channel_groups(
+ struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type,
+ u8 max_ch_groups)
+{
+ u16 i;
+ int err;
+ struct iwl_phy_db_entry *entry;
+
+ /* Send all the channel specific groups to operational fw */
+ for (i = 0; i < max_ch_groups; i++) {
+ entry = iwl_phy_db_get_section(phy_db,
+ type,
+ i);
+ if (!entry)
+ return -EINVAL;
+
+ if (!entry->size)
+ continue;
+
+ /* Send the requested PHY DB section */
+ err = iwl_send_phy_db_cmd(phy_db,
+ type,
+ entry->size,
+ entry->data);
+ if (err) {
+ IWL_ERR(phy_db->trans,
+ "Can't SEND phy_db section %d (%d), err %d\n",
+ type, i, err);
+ return err;
+ }
+
+ IWL_DEBUG_INFO(phy_db->trans,
+ "Sent PHY_DB HCMD, type = %d num = %d\n",
+ type, i);
+ }
+
+ return 0;
+}
+
+int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
+{
+ u8 *data = NULL;
+ u16 size = 0;
+ int err;
+
+ IWL_DEBUG_INFO(phy_db->trans,
+ "Sending phy db data and configuration to runtime image\n");
+
+ /* Send PHY DB CFG section */
+ err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
+ &data, &size, 0);
+ if (err) {
+ IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
+ return err;
+ }
+
+ err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
+ if (err) {
+ IWL_ERR(phy_db->trans,
+ "Cannot send HCMD of Phy DB cfg section\n");
+ return err;
+ }
+
+ err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
+ &data, &size, 0);
+ if (err) {
+ IWL_ERR(phy_db->trans,
+ "Cannot get Phy DB non specific channel section\n");
+ return err;
+ }
+
+ err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
+ if (err) {
+ IWL_ERR(phy_db->trans,
+ "Cannot send HCMD of Phy DB non specific channel section\n");
+ return err;
+ }
+
+ /* Send all the TXP channel specific data */
+ err = iwl_phy_db_send_all_channel_groups(phy_db,
+ IWL_PHY_DB_CALIB_CHG_PAPD,
+ IWL_NUM_PAPD_CH_GROUPS);
+ if (err) {
+ IWL_ERR(phy_db->trans,
+ "Cannot send channel specific PAPD groups\n");
+ return err;
+ }
+
+ /* Send all the TXP channel specific data */
+ err = iwl_phy_db_send_all_channel_groups(phy_db,
+ IWL_PHY_DB_CALIB_CHG_TXP,
+ IWL_NUM_TXP_CH_GROUPS);
+ if (err) {
+ IWL_ERR(phy_db->trans,
+ "Cannot send channel specific TX power groups\n");
+ return err;
+ }
+
+ IWL_DEBUG_INFO(phy_db->trans,
+ "Finished sending phy db non channel data\n");
+ return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h
new file mode 100644
index 000000000000..24103877eab0
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h
@@ -0,0 +1,82 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_PHYDB_H__
+#define __IWL_PHYDB_H__
+
+#include <linux/types.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans);
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db);
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
+ gfp_t alloc_ctx);
+
+
+int iwl_send_phy_db_data(struct iwl_phy_db *phy_db);
+
+#endif /* __IWL_PHYDB_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
new file mode 100644
index 000000000000..5bde23a472b4
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
@@ -0,0 +1,407 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_prph_h__
+#define __iwl_prph_h__
+
+/*
+ * Registers in this file are internal, not PCI bus memory mapped.
+ * Driver accesses these via HBUS_TARG_PRPH_* registers.
+ */
+#define PRPH_BASE (0x00000)
+#define PRPH_END (0xFFFFF)
+
+/* APMG (power management) constants */
+#define APMG_BASE (PRPH_BASE + 0x3000)
+#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000)
+#define APMG_CLK_EN_REG (APMG_BASE + 0x0004)
+#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008)
+#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c)
+#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010)
+#define APMG_RFKILL_REG (APMG_BASE + 0x0014)
+#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c)
+#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020)
+#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058)
+#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C)
+
+#define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001)
+#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200)
+#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800)
+
+#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000)
+#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000)
+#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000)
+#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000)
+#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000)
+#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */
+#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060)
+
+#define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200)
+#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800)
+#define APMG_PCIDEV_STT_VAL_WAKE_ME (0x00004000)
+
+#define APMG_RTC_INT_STT_RFKILL (0x10000000)
+
+/* Device system time */
+#define DEVICE_SYSTEM_TIME_REG 0xA0206C
+
+/* Device NMI register */
+#define DEVICE_SET_NMI_REG 0x00a01c30
+#define DEVICE_SET_NMI_VAL_HW BIT(0)
+#define DEVICE_SET_NMI_VAL_DRV BIT(7)
+#define DEVICE_SET_NMI_8000_REG 0x00a01c24
+#define DEVICE_SET_NMI_8000_VAL 0x1000000
+
+/* Shared registers (0x0..0x3ff, via target indirect or periphery */
+#define SHR_BASE 0x00a10000
+
+/* Shared GP1 register */
+#define SHR_APMG_GP1_REG 0x01dc
+#define SHR_APMG_GP1_REG_PRPH (SHR_BASE + SHR_APMG_GP1_REG)
+#define SHR_APMG_GP1_WF_XTAL_LP_EN 0x00000004
+#define SHR_APMG_GP1_CHICKEN_BIT_SELECT 0x80000000
+
+/* Shared DL_CFG register */
+#define SHR_APMG_DL_CFG_REG 0x01c4
+#define SHR_APMG_DL_CFG_REG_PRPH (SHR_BASE + SHR_APMG_DL_CFG_REG)
+#define SHR_APMG_DL_CFG_RTCS_CLK_SELECTOR_MSK 0x000000c0
+#define SHR_APMG_DL_CFG_RTCS_CLK_INTERNAL_XTAL 0x00000080
+#define SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP 0x00000100
+
+/* Shared APMG_XTAL_CFG register */
+#define SHR_APMG_XTAL_CFG_REG 0x1c0
+#define SHR_APMG_XTAL_CFG_XTAL_ON_REQ 0x80000000
+
+/*
+ * Device reset for family 8000
+ * write to bit 24 in order to reset the CPU
+*/
+#define RELEASE_CPU_RESET (0x300C)
+#define RELEASE_CPU_RESET_BIT BIT(24)
+
+/*****************************************************************************
+ * 7000/3000 series SHR DTS addresses *
+ *****************************************************************************/
+
+#define SHR_MISC_WFM_DTS_EN (0x00a10024)
+#define DTSC_CFG_MODE (0x00a10604)
+#define DTSC_VREF_AVG (0x00a10648)
+#define DTSC_VREF5_AVG (0x00a1064c)
+#define DTSC_CFG_MODE_PERIODIC (0x2)
+#define DTSC_PTAT_AVG (0x00a10650)
+
+
+/**
+ * Tx Scheduler
+ *
+ * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs
+ * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in
+ * host DRAM. It steers each frame's Tx command (which contains the frame
+ * data) into one of up to 7 prioritized Tx DMA FIFO channels within the
+ * device. A queue maps to only one (selectable by driver) Tx DMA channel,
+ * but one DMA channel may take input from several queues.
+ *
+ * Tx DMA FIFOs have dedicated purposes.
+ *
+ * For 5000 series and up, they are used differently
+ * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c):
+ *
+ * 0 -- EDCA BK (background) frames, lowest priority
+ * 1 -- EDCA BE (best effort) frames, normal priority
+ * 2 -- EDCA VI (video) frames, higher priority
+ * 3 -- EDCA VO (voice) and management frames, highest priority
+ * 4 -- unused
+ * 5 -- unused
+ * 6 -- unused
+ * 7 -- Commands
+ *
+ * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6.
+ * In addition, driver can map the remaining queues to Tx DMA/FIFO
+ * channels 0-3 to support 11n aggregation via EDCA DMA channels.
+ *
+ * The driver sets up each queue to work in one of two modes:
+ *
+ * 1) Scheduler-Ack, in which the scheduler automatically supports a
+ * block-ack (BA) window of up to 64 TFDs. In this mode, each queue
+ * contains TFDs for a unique combination of Recipient Address (RA)
+ * and Traffic Identifier (TID), that is, traffic of a given
+ * Quality-Of-Service (QOS) priority, destined for a single station.
+ *
+ * In scheduler-ack mode, the scheduler keeps track of the Tx status of
+ * each frame within the BA window, including whether it's been transmitted,
+ * and whether it's been acknowledged by the receiving station. The device
+ * automatically processes block-acks received from the receiving STA,
+ * and reschedules un-acked frames to be retransmitted (successful
+ * Tx completion may end up being out-of-order).
+ *
+ * The driver must maintain the queue's Byte Count table in host DRAM
+ * for this mode.
+ * This mode does not support fragmentation.
+ *
+ * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order.
+ * The device may automatically retry Tx, but will retry only one frame
+ * at a time, until receiving ACK from receiving station, or reaching
+ * retry limit and giving up.
+ *
+ * The command queue (#4/#9) must use this mode!
+ * This mode does not require use of the Byte Count table in host DRAM.
+ *
+ * Driver controls scheduler operation via 3 means:
+ * 1) Scheduler registers
+ * 2) Shared scheduler data base in internal SRAM
+ * 3) Shared data in host DRAM
+ *
+ * Initialization:
+ *
+ * When loading, driver should allocate memory for:
+ * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs.
+ * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory
+ * (1024 bytes for each queue).
+ *
+ * After receiving "Alive" response from uCode, driver must initialize
+ * the scheduler (especially for queue #4/#9, the command queue, otherwise
+ * the driver can't issue commands!):
+ */
+#define SCD_MEM_LOWER_BOUND (0x0000)
+
+/**
+ * Max Tx window size is the max number of contiguous TFDs that the scheduler
+ * can keep track of at one time when creating block-ack chains of frames.
+ * Note that "64" matches the number of ack bits in a block-ack packet.
+ */
+#define SCD_WIN_SIZE 64
+#define SCD_FRAME_LIMIT 64
+
+#define SCD_TXFIFO_POS_TID (0)
+#define SCD_TXFIFO_POS_RA (4)
+#define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF)
+
+/* agn SCD */
+#define SCD_QUEUE_STTS_REG_POS_TXF (0)
+#define SCD_QUEUE_STTS_REG_POS_ACTIVE (3)
+#define SCD_QUEUE_STTS_REG_POS_WSL (4)
+#define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19)
+#define SCD_QUEUE_STTS_REG_MSK (0x017F0000)
+
+#define SCD_QUEUE_CTX_REG1_CREDIT_POS (8)
+#define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00)
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24)
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000)
+#define SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0)
+#define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F)
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16)
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000)
+#define SCD_GP_CTRL_ENABLE_31_QUEUES BIT(0)
+#define SCD_GP_CTRL_AUTO_ACTIVE_MODE BIT(18)
+
+/* Context Data */
+#define SCD_CONTEXT_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x600)
+#define SCD_CONTEXT_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0)
+
+/* Tx status */
+#define SCD_TX_STTS_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0)
+#define SCD_TX_STTS_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0)
+
+/* Translation Data */
+#define SCD_TRANS_TBL_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0)
+#define SCD_TRANS_TBL_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x808)
+
+#define SCD_CONTEXT_QUEUE_OFFSET(x)\
+ (SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8))
+
+#define SCD_TX_STTS_QUEUE_OFFSET(x)\
+ (SCD_TX_STTS_MEM_LOWER_BOUND + ((x) * 16))
+
+#define SCD_TRANS_TBL_OFFSET_QUEUE(x) \
+ ((SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc)
+
+#define SCD_BASE (PRPH_BASE + 0xa02c00)
+
+#define SCD_SRAM_BASE_ADDR (SCD_BASE + 0x0)
+#define SCD_DRAM_BASE_ADDR (SCD_BASE + 0x8)
+#define SCD_AIT (SCD_BASE + 0x0c)
+#define SCD_TXFACT (SCD_BASE + 0x10)
+#define SCD_ACTIVE (SCD_BASE + 0x14)
+#define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8)
+#define SCD_CHAINEXT_EN (SCD_BASE + 0x244)
+#define SCD_AGGR_SEL (SCD_BASE + 0x248)
+#define SCD_INTERRUPT_MASK (SCD_BASE + 0x108)
+#define SCD_GP_CTRL (SCD_BASE + 0x1a8)
+#define SCD_EN_CTRL (SCD_BASE + 0x254)
+
+/*********************** END TX SCHEDULER *************************************/
+
+/* tcp checksum offload */
+#define RX_EN_CSUM (0x00a00d88)
+
+/* Oscillator clock */
+#define OSC_CLK (0xa04068)
+#define OSC_CLK_FORCE_CONTROL (0x8)
+
+#define FH_UCODE_LOAD_STATUS (0x1AF0)
+#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70)
+enum secure_load_status_reg {
+ LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001,
+ LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003,
+ LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007,
+ LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8,
+ LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00,
+};
+
+#define LMPM_SECURE_INSPECTOR_CODE_ADDR (0x1E38)
+#define LMPM_SECURE_INSPECTOR_DATA_ADDR (0x1E3C)
+#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78)
+#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C)
+
+#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE (0x400000)
+#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE (0x402000)
+#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000)
+#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400)
+
+/* Rx FIFO */
+#define RXF_SIZE_ADDR (0xa00c88)
+#define RXF_RD_D_SPACE (0xa00c40)
+#define RXF_RD_WR_PTR (0xa00c50)
+#define RXF_RD_RD_PTR (0xa00c54)
+#define RXF_RD_FENCE_PTR (0xa00c4c)
+#define RXF_SET_FENCE_MODE (0xa00c14)
+#define RXF_LD_WR2FENCE (0xa00c1c)
+#define RXF_FIFO_RD_FENCE_INC (0xa00c68)
+#define RXF_SIZE_BYTE_CND_POS (7)
+#define RXF_SIZE_BYTE_CNT_MSK (0x3ff << RXF_SIZE_BYTE_CND_POS)
+#define RXF_DIFF_FROM_PREV (0x200)
+
+#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10)
+#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c)
+
+/* Tx FIFO */
+#define TXF_FIFO_ITEM_CNT (0xa00438)
+#define TXF_WR_PTR (0xa00414)
+#define TXF_RD_PTR (0xa00410)
+#define TXF_FENCE_PTR (0xa00418)
+#define TXF_LOCK_FENCE (0xa00424)
+#define TXF_LARC_NUM (0xa0043c)
+#define TXF_READ_MODIFY_DATA (0xa00448)
+#define TXF_READ_MODIFY_ADDR (0xa0044c)
+
+/* Radio registers access */
+#define RSP_RADIO_CMD (0xa02804)
+#define RSP_RADIO_RDDAT (0xa02814)
+#define RADIO_RSP_ADDR_POS (6)
+#define RADIO_RSP_RD_CMD (3)
+
+/* FW monitor */
+#define MON_BUFF_SAMPLE_CTL (0xa03c00)
+#define MON_BUFF_BASE_ADDR (0xa03c3c)
+#define MON_BUFF_END_ADDR (0xa03c40)
+#define MON_BUFF_WRPTR (0xa03c44)
+#define MON_BUFF_CYCLE_CNT (0xa03c48)
+
+#define MON_DMARB_RD_CTL_ADDR (0xa03c60)
+#define MON_DMARB_RD_DATA_ADDR (0xa03c5c)
+
+#define DBGC_IN_SAMPLE (0xa03c00)
+
+/* enable the ID buf for read */
+#define WFPM_PS_CTL_CLR 0xA0300C
+#define WFMP_MAC_ADDR_0 0xA03080
+#define WFMP_MAC_ADDR_1 0xA03084
+#define LMPM_PMG_EN 0xA01CEC
+#define RADIO_REG_SYS_MANUAL_DFT_0 0xAD4078
+#define RFIC_REG_RD 0xAD0470
+#define WFPM_CTRL_REG 0xA03030
+enum {
+ ENABLE_WFPM = BIT(31),
+ WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000,
+};
+
+#define AUX_MISC_REG 0xA200B0
+enum {
+ HW_STEP_LOCATION_BITS = 24,
+};
+
+#define AUX_MISC_MASTER1_EN 0xA20818
+enum aux_misc_master1_en {
+ AUX_MISC_MASTER1_EN_SBE_MSK = 0x1,
+};
+
+#define AUX_MISC_MASTER1_SMPHR_STATUS 0xA20800
+#define RSA_ENABLE 0xA24B08
+#define PREG_AUX_BUS_WPROT_0 0xA04CC0
+#define SB_CPU_1_STATUS 0xA01E30
+#define SB_CPU_2_STATUS 0xA01E34
+
+/* FW chicken bits */
+#define LMPM_CHICK 0xA01FF8
+enum {
+ LMPM_CHICK_EXTENDED_ADDR_SPACE = BIT(0),
+};
+
+/* FW chicken bits */
+#define LMPM_PAGE_PASS_NOTIF 0xA03824
+enum {
+ LMPM_PAGE_PASS_NOTIF_POS = BIT(20),
+};
+
+#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-scd.h b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h
new file mode 100644
index 000000000000..99b43da32adf
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h
@@ -0,0 +1,143 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_scd_h__
+#define __iwl_scd_h__
+
+#include "iwl-trans.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+
+
+static inline void iwl_scd_txq_set_chain(struct iwl_trans *trans,
+ u16 txq_id)
+{
+ iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id));
+}
+
+static inline void iwl_scd_txq_enable_agg(struct iwl_trans *trans,
+ u16 txq_id)
+{
+ iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+}
+
+static inline void iwl_scd_txq_disable_agg(struct iwl_trans *trans,
+ u16 txq_id)
+{
+ iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+}
+
+static inline void iwl_scd_disable_agg(struct iwl_trans *trans)
+{
+ iwl_set_bits_prph(trans, SCD_AGGR_SEL, 0);
+}
+
+static inline void iwl_scd_activate_fifos(struct iwl_trans *trans)
+{
+ iwl_write_prph(trans, SCD_TXFACT, IWL_MASK(0, 7));
+}
+
+static inline void iwl_scd_deactivate_fifos(struct iwl_trans *trans)
+{
+ iwl_write_prph(trans, SCD_TXFACT, 0);
+}
+
+static inline void iwl_scd_enable_set_active(struct iwl_trans *trans,
+ u32 value)
+{
+ iwl_write_prph(trans, SCD_EN_CTRL, value);
+}
+
+static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl)
+{
+ if (chnl < 20)
+ return SCD_BASE + 0x18 + chnl * 4;
+ WARN_ON_ONCE(chnl >= 32);
+ return SCD_BASE + 0x284 + (chnl - 20) * 4;
+}
+
+static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl)
+{
+ if (chnl < 20)
+ return SCD_BASE + 0x68 + chnl * 4;
+ WARN_ON_ONCE(chnl >= 32);
+ return SCD_BASE + 0x2B4 + chnl * 4;
+}
+
+static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
+{
+ if (chnl < 20)
+ return SCD_BASE + 0x10c + chnl * 4;
+ WARN_ON_ONCE(chnl >= 32);
+ return SCD_BASE + 0x334 + chnl * 4;
+}
+
+static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans,
+ u16 txq_id)
+{
+ iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
+ (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
+ (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
+}
+
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
new file mode 100644
index 000000000000..6069a9ff53fa
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/bsearch.h>
+
+#include "iwl-trans.h"
+#include "iwl-drv.h"
+
+struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
+ struct device *dev,
+ const struct iwl_cfg *cfg,
+ const struct iwl_trans_ops *ops,
+ size_t dev_cmd_headroom)
+{
+ struct iwl_trans *trans;
+#ifdef CONFIG_LOCKDEP
+ static struct lock_class_key __key;
+#endif
+
+ trans = kzalloc(sizeof(*trans) + priv_size, GFP_KERNEL);
+ if (!trans)
+ return NULL;
+
+#ifdef CONFIG_LOCKDEP
+ lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
+ &__key, 0);
+#endif
+
+ trans->dev = dev;
+ trans->cfg = cfg;
+ trans->ops = ops;
+ trans->dev_cmd_headroom = dev_cmd_headroom;
+ trans->num_rx_queues = 1;
+
+ snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
+ "iwl_cmd_pool:%s", dev_name(trans->dev));
+ trans->dev_cmd_pool =
+ kmem_cache_create(trans->dev_cmd_pool_name,
+ sizeof(struct iwl_device_cmd)
+ + trans->dev_cmd_headroom,
+ sizeof(void *),
+ SLAB_HWCACHE_ALIGN,
+ NULL);
+ if (!trans->dev_cmd_pool)
+ goto free;
+
+ return trans;
+ free:
+ kfree(trans);
+ return NULL;
+}
+
+void iwl_trans_free(struct iwl_trans *trans)
+{
+ kmem_cache_destroy(trans->dev_cmd_pool);
+ kfree(trans);
+}
+
+int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
+{
+ int ret;
+
+ if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+ test_bit(STATUS_RFKILL, &trans->status)))
+ return -ERFKILL;
+
+ if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+ return -EIO;
+
+ if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return -EIO;
+ }
+
+ if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) &&
+ !(cmd->flags & CMD_ASYNC)))
+ return -EINVAL;
+
+ if (!(cmd->flags & CMD_ASYNC))
+ lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
+
+ ret = trans->ops->send_cmd(trans, cmd);
+
+ if (!(cmd->flags & CMD_ASYNC))
+ lock_map_release(&trans->sync_cmd_lockdep_map);
+
+ return ret;
+}
+IWL_EXPORT_SYMBOL(iwl_trans_send_cmd);
+
+/* Comparator for struct iwl_hcmd_names.
+ * Used in the binary search over a list of host commands.
+ *
+ * @key: command_id that we're looking for.
+ * @elt: struct iwl_hcmd_names candidate for match.
+ *
+ * @return 0 iff equal.
+ */
+static int iwl_hcmd_names_cmp(const void *key, const void *elt)
+{
+ const struct iwl_hcmd_names *name = elt;
+ u8 cmd1 = *(u8 *)key;
+ u8 cmd2 = name->cmd_id;
+
+ return (cmd1 - cmd2);
+}
+
+const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id)
+{
+ u8 grp, cmd;
+ struct iwl_hcmd_names *ret;
+ const struct iwl_hcmd_arr *arr;
+ size_t size = sizeof(struct iwl_hcmd_names);
+
+ grp = iwl_cmd_groupid(id);
+ cmd = iwl_cmd_opcode(id);
+
+ if (!trans->command_groups || grp >= trans->command_groups_size ||
+ !trans->command_groups[grp].arr)
+ return "UNKNOWN";
+
+ arr = &trans->command_groups[grp];
+ ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp);
+ if (!ret)
+ return "UNKNOWN";
+ return ret->cmd_name;
+}
+IWL_EXPORT_SYMBOL(iwl_get_cmd_string);
+
+int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans)
+{
+ int i, j;
+ const struct iwl_hcmd_arr *arr;
+
+ for (i = 0; i < trans->command_groups_size; i++) {
+ arr = &trans->command_groups[i];
+ if (!arr->arr)
+ continue;
+ for (j = 0; j < arr->size - 1; j++)
+ if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id)
+ return -1;
+ }
+ return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
new file mode 100644
index 000000000000..82fb3a97a46d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -0,0 +1,1221 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_trans_h__
+#define __iwl_trans_h__
+
+#include <linux/ieee80211.h>
+#include <linux/mm.h> /* for page_address */
+#include <linux/lockdep.h>
+#include <linux/kernel.h>
+
+#include "iwl-debug.h"
+#include "iwl-config.h"
+#include "iwl-fw.h"
+#include "iwl-op-mode.h"
+
+/**
+ * DOC: Transport layer - what is it ?
+ *
+ * The transport layer is the layer that deals with the HW directly. It provides
+ * an abstraction of the underlying HW to the upper layer. The transport layer
+ * doesn't provide any policy, algorithm or anything of this kind, but only
+ * mechanisms to make the HW do something. It is not completely stateless but
+ * close to it.
+ * We will have an implementation for each different supported bus.
+ */
+
+/**
+ * DOC: Life cycle of the transport layer
+ *
+ * The transport layer has a very precise life cycle.
+ *
+ * 1) A helper function is called during the module initialization and
+ * registers the bus driver's ops with the transport's alloc function.
+ * 2) Bus's probe calls to the transport layer's allocation functions.
+ * Of course this function is bus specific.
+ * 3) This allocation functions will spawn the upper layer which will
+ * register mac80211.
+ *
+ * 4) At some point (i.e. mac80211's start call), the op_mode will call
+ * the following sequence:
+ * start_hw
+ * start_fw
+ *
+ * 5) Then when finished (or reset):
+ * stop_device
+ *
+ * 6) Eventually, the free function will be called.
+ */
+
+/**
+ * DOC: Host command section
+ *
+ * A host command is a command issued by the upper layer to the fw. There are
+ * several versions of fw that have several APIs. The transport layer is
+ * completely agnostic to these differences.
+ * The transport does provide helper functionality (i.e. SYNC / ASYNC mode),
+ */
+#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f)
+#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8)
+#define SEQ_TO_INDEX(s) ((s) & 0xff)
+#define INDEX_TO_SEQ(i) ((i) & 0xff)
+#define SEQ_RX_FRAME cpu_to_le16(0x8000)
+
+/*
+ * those functions retrieve specific information from
+ * the id field in the iwl_host_cmd struct which contains
+ * the command id, the group id and the version of the command
+ * and vice versa
+*/
+static inline u8 iwl_cmd_opcode(u32 cmdid)
+{
+ return cmdid & 0xFF;
+}
+
+static inline u8 iwl_cmd_groupid(u32 cmdid)
+{
+ return ((cmdid & 0xFF00) >> 8);
+}
+
+static inline u8 iwl_cmd_version(u32 cmdid)
+{
+ return ((cmdid & 0xFF0000) >> 16);
+}
+
+static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version)
+{
+ return opcode + (groupid << 8) + (version << 16);
+}
+
+/* make u16 wide id out of u8 group and opcode */
+#define WIDE_ID(grp, opcode) ((grp << 8) | opcode)
+
+/* due to the conversion, this group is special; new groups
+ * should be defined in the appropriate fw-api header files
+ */
+#define IWL_ALWAYS_LONG_GROUP 1
+
+/**
+ * struct iwl_cmd_header
+ *
+ * This header format appears in the beginning of each command sent from the
+ * driver, and each response/notification received from uCode.
+ */
+struct iwl_cmd_header {
+ u8 cmd; /* Command ID: REPLY_RXON, etc. */
+ u8 group_id;
+ /*
+ * The driver sets up the sequence number to values of its choosing.
+ * uCode does not use this value, but passes it back to the driver
+ * when sending the response to each driver-originated command, so
+ * the driver can match the response to the command. Since the values
+ * don't get used by uCode, the driver may set up an arbitrary format.
+ *
+ * There is one exception: uCode sets bit 15 when it originates
+ * the response/notification, i.e. when the response/notification
+ * is not a direct response to a command sent by the driver. For
+ * example, uCode issues REPLY_RX when it sends a received frame
+ * to the driver; it is not a direct response to any driver command.
+ *
+ * The Linux driver uses the following format:
+ *
+ * 0:7 tfd index - position within TX queue
+ * 8:12 TX queue id
+ * 13:14 reserved
+ * 15 unsolicited RX or uCode-originated notification
+ */
+ __le16 sequence;
+} __packed;
+
+/**
+ * struct iwl_cmd_header_wide
+ *
+ * This header format appears in the beginning of each command sent from the
+ * driver, and each response/notification received from uCode.
+ * this is the wide version that contains more information about the command
+ * like length, version and command type
+ */
+struct iwl_cmd_header_wide {
+ u8 cmd;
+ u8 group_id;
+ __le16 sequence;
+ __le16 length;
+ u8 reserved;
+ u8 version;
+} __packed;
+
+#define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */
+#define FH_RSCSR_FRAME_INVALID 0x55550000
+#define FH_RSCSR_FRAME_ALIGN 0x40
+
+struct iwl_rx_packet {
+ /*
+ * The first 4 bytes of the RX frame header contain both the RX frame
+ * size and some flags.
+ * Bit fields:
+ * 31: flag flush RB request
+ * 30: flag ignore TC (terminal counter) request
+ * 29: flag fast IRQ request
+ * 28-14: Reserved
+ * 13-00: RX frame size
+ */
+ __le32 len_n_flags;
+ struct iwl_cmd_header hdr;
+ u8 data[];
+} __packed;
+
+static inline u32 iwl_rx_packet_len(const struct iwl_rx_packet *pkt)
+{
+ return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+}
+
+static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
+{
+ return iwl_rx_packet_len(pkt) - sizeof(pkt->hdr);
+}
+
+/**
+ * enum CMD_MODE - how to send the host commands ?
+ *
+ * @CMD_ASYNC: Return right away and don't wait for the response
+ * @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of
+ * the response. The caller needs to call iwl_free_resp when done.
+ * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
+ * command queue, but after other high priority commands. Valid only
+ * with CMD_ASYNC.
+ * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
+ * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
+ * @CMD_WAKE_UP_TRANS: The command response should wake up the trans
+ * (i.e. mark it as non-idle).
+ * @CMD_WANT_ASYNC_CALLBACK: the op_mode's async callback function must be
+ * called after this command completes. Valid only with CMD_ASYNC.
+ * @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to
+ * check that we leave enough room for the TBs bitmap which needs 20 bits.
+ */
+enum CMD_MODE {
+ CMD_ASYNC = BIT(0),
+ CMD_WANT_SKB = BIT(1),
+ CMD_SEND_IN_RFKILL = BIT(2),
+ CMD_HIGH_PRIO = BIT(3),
+ CMD_SEND_IN_IDLE = BIT(4),
+ CMD_MAKE_TRANS_IDLE = BIT(5),
+ CMD_WAKE_UP_TRANS = BIT(6),
+ CMD_WANT_ASYNC_CALLBACK = BIT(7),
+
+ CMD_TB_BITMAP_POS = 11,
+};
+
+#define DEF_CMD_PAYLOAD_SIZE 320
+
+/**
+ * struct iwl_device_cmd
+ *
+ * For allocation of the command and tx queues, this establishes the overall
+ * size of the largest command we send to uCode, except for commands that
+ * aren't fully copied and use other TFD space.
+ */
+struct iwl_device_cmd {
+ union {
+ struct {
+ struct iwl_cmd_header hdr; /* uCode API */
+ u8 payload[DEF_CMD_PAYLOAD_SIZE];
+ };
+ struct {
+ struct iwl_cmd_header_wide hdr_wide;
+ u8 payload_wide[DEF_CMD_PAYLOAD_SIZE -
+ sizeof(struct iwl_cmd_header_wide) +
+ sizeof(struct iwl_cmd_header)];
+ };
+ };
+} __packed;
+
+#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd))
+
+/*
+ * number of transfer buffers (fragments) per transmit frame descriptor;
+ * this is just the driver's idea, the hardware supports 20
+ */
+#define IWL_MAX_CMD_TBS_PER_TFD 2
+
+/**
+ * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command
+ *
+ * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's
+ * ring. The transport layer doesn't map the command's buffer to DMA, but
+ * rather copies it to a previously allocated DMA buffer. This flag tells
+ * the transport layer not to copy the command, but to map the existing
+ * buffer (that is passed in) instead. This saves the memcpy and allows
+ * commands that are bigger than the fixed buffer to be submitted.
+ * Note that a TFD entry after a NOCOPY one cannot be a normal copied one.
+ * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this
+ * chunk internally and free it again after the command completes. This
+ * can (currently) be used only once per command.
+ * Note that a TFD entry after a DUP one cannot be a normal copied one.
+ */
+enum iwl_hcmd_dataflag {
+ IWL_HCMD_DFL_NOCOPY = BIT(0),
+ IWL_HCMD_DFL_DUP = BIT(1),
+};
+
+/**
+ * struct iwl_host_cmd - Host command to the uCode
+ *
+ * @data: array of chunks that composes the data of the host command
+ * @resp_pkt: response packet, if %CMD_WANT_SKB was set
+ * @_rx_page_order: (internally used to free response packet)
+ * @_rx_page_addr: (internally used to free response packet)
+ * @flags: can be CMD_*
+ * @len: array of the lengths of the chunks in data
+ * @dataflags: IWL_HCMD_DFL_*
+ * @id: command id of the host command, for wide commands encoding the
+ * version and group as well
+ */
+struct iwl_host_cmd {
+ const void *data[IWL_MAX_CMD_TBS_PER_TFD];
+ struct iwl_rx_packet *resp_pkt;
+ unsigned long _rx_page_addr;
+ u32 _rx_page_order;
+
+ u32 flags;
+ u32 id;
+ u16 len[IWL_MAX_CMD_TBS_PER_TFD];
+ u8 dataflags[IWL_MAX_CMD_TBS_PER_TFD];
+};
+
+static inline void iwl_free_resp(struct iwl_host_cmd *cmd)
+{
+ free_pages(cmd->_rx_page_addr, cmd->_rx_page_order);
+}
+
+struct iwl_rx_cmd_buffer {
+ struct page *_page;
+ int _offset;
+ bool _page_stolen;
+ u32 _rx_page_order;
+ unsigned int truesize;
+};
+
+static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r)
+{
+ return (void *)((unsigned long)page_address(r->_page) + r->_offset);
+}
+
+static inline int rxb_offset(struct iwl_rx_cmd_buffer *r)
+{
+ return r->_offset;
+}
+
+static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
+{
+ r->_page_stolen = true;
+ get_page(r->_page);
+ return r->_page;
+}
+
+static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r)
+{
+ __free_pages(r->_page, r->_rx_page_order);
+}
+
+#define MAX_NO_RECLAIM_CMDS 6
+
+/*
+ * The first entry in driver_data array in ieee80211_tx_info
+ * that can be used by the transport.
+ */
+#define IWL_TRANS_FIRST_DRIVER_DATA 2
+#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
+
+/*
+ * Maximum number of HW queues the transport layer
+ * currently supports
+ */
+#define IWL_MAX_HW_QUEUES 32
+#define IWL_MAX_TID_COUNT 8
+#define IWL_FRAME_LIMIT 64
+#define IWL_MAX_RX_HW_QUEUES 16
+
+/**
+ * enum iwl_wowlan_status - WoWLAN image/device status
+ * @IWL_D3_STATUS_ALIVE: firmware is still running after resume
+ * @IWL_D3_STATUS_RESET: device was reset while suspended
+ */
+enum iwl_d3_status {
+ IWL_D3_STATUS_ALIVE,
+ IWL_D3_STATUS_RESET,
+};
+
+/**
+ * enum iwl_trans_status: transport status flags
+ * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed
+ * @STATUS_DEVICE_ENABLED: APM is enabled
+ * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
+ * @STATUS_INT_ENABLED: interrupts are enabled
+ * @STATUS_RFKILL: the HW RFkill switch is in KILL position
+ * @STATUS_FW_ERROR: the fw is in error state
+ * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands
+ * are sent
+ * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
+ * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation
+ */
+enum iwl_trans_status {
+ STATUS_SYNC_HCMD_ACTIVE,
+ STATUS_DEVICE_ENABLED,
+ STATUS_TPOWER_PMI,
+ STATUS_INT_ENABLED,
+ STATUS_RFKILL,
+ STATUS_FW_ERROR,
+ STATUS_TRANS_GOING_IDLE,
+ STATUS_TRANS_IDLE,
+ STATUS_TRANS_DEAD,
+};
+
+static inline int
+iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size)
+{
+ switch (rb_size) {
+ case IWL_AMSDU_4K:
+ return get_order(4 * 1024);
+ case IWL_AMSDU_8K:
+ return get_order(8 * 1024);
+ case IWL_AMSDU_12K:
+ return get_order(12 * 1024);
+ default:
+ WARN_ON(1);
+ return -1;
+ }
+}
+
+struct iwl_hcmd_names {
+ u8 cmd_id;
+ const char *const cmd_name;
+};
+
+#define HCMD_NAME(x) \
+ { .cmd_id = x, .cmd_name = #x }
+
+struct iwl_hcmd_arr {
+ const struct iwl_hcmd_names *arr;
+ int size;
+};
+
+#define HCMD_ARR(x) \
+ { .arr = x, .size = ARRAY_SIZE(x) }
+
+/**
+ * struct iwl_trans_config - transport configuration
+ *
+ * @op_mode: pointer to the upper layer.
+ * @cmd_queue: the index of the command queue.
+ * Must be set before start_fw.
+ * @cmd_fifo: the fifo for host commands
+ * @cmd_q_wdg_timeout: the timeout of the watchdog timer for the command queue.
+ * @no_reclaim_cmds: Some devices erroneously don't set the
+ * SEQ_RX_FRAME bit on some notifications, this is the
+ * list of such notifications to filter. Max length is
+ * %MAX_NO_RECLAIM_CMDS.
+ * @n_no_reclaim_cmds: # of commands in list
+ * @rx_buf_size: RX buffer size needed for A-MSDUs
+ * if unset 4k will be the RX buffer size
+ * @bc_table_dword: set to true if the BC table expects the byte count to be
+ * in DWORD (as opposed to bytes)
+ * @scd_set_active: should the transport configure the SCD for HCMD queue
+ * @wide_cmd_header: firmware supports wide host command header
+ * @sw_csum_tx: transport should compute the TCP checksum
+ * @command_groups: array of command groups, each member is an array of the
+ * commands in the group; for debugging only
+ * @command_groups_size: number of command groups, to avoid illegal access
+ * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until
+ * we get the ALIVE from the uCode
+ */
+struct iwl_trans_config {
+ struct iwl_op_mode *op_mode;
+
+ u8 cmd_queue;
+ u8 cmd_fifo;
+ unsigned int cmd_q_wdg_timeout;
+ const u8 *no_reclaim_cmds;
+ unsigned int n_no_reclaim_cmds;
+
+ enum iwl_amsdu_size rx_buf_size;
+ bool bc_table_dword;
+ bool scd_set_active;
+ bool wide_cmd_header;
+ bool sw_csum_tx;
+ const struct iwl_hcmd_arr *command_groups;
+ int command_groups_size;
+
+ u32 sdio_adma_addr;
+};
+
+struct iwl_trans_dump_data {
+ u32 len;
+ u8 data[];
+};
+
+struct iwl_trans;
+
+struct iwl_trans_txq_scd_cfg {
+ u8 fifo;
+ s8 sta_id;
+ u8 tid;
+ bool aggregate;
+ int frame_limit;
+};
+
+/**
+ * struct iwl_trans_ops - transport specific operations
+ *
+ * All the handlers MUST be implemented
+ *
+ * @start_hw: starts the HW. If low_power is true, the NIC needs to be taken
+ * out of a low power state. From that point on, the HW can send
+ * interrupts. May sleep.
+ * @op_mode_leave: Turn off the HW RF kill indication if on
+ * May sleep
+ * @start_fw: allocates and inits all the resources for the transport
+ * layer. Also kick a fw image.
+ * May sleep
+ * @fw_alive: called when the fw sends alive notification. If the fw provides
+ * the SCD base address in SRAM, then provide it here, or 0 otherwise.
+ * May sleep
+ * @stop_device: stops the whole device (embedded CPU put to reset) and stops
+ * the HW. If low_power is true, the NIC will be put in low power state.
+ * From that point on, the HW will be stopped but will still issue an
+ * interrupt if the HW RF kill switch is triggered.
+ * This callback must do the right thing and not crash even if %start_hw()
+ * was called but not &start_fw(). May sleep.
+ * @d3_suspend: put the device into the correct mode for WoWLAN during
+ * suspend. This is optional, if not implemented WoWLAN will not be
+ * supported. This callback may sleep.
+ * @d3_resume: resume the device after WoWLAN, enabling the opmode to
+ * talk to the WoWLAN image to get its status. This is optional, if not
+ * implemented WoWLAN will not be supported. This callback may sleep.
+ * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted.
+ * If RFkill is asserted in the middle of a SYNC host command, it must
+ * return -ERFKILL straight away.
+ * May sleep only if CMD_ASYNC is not set
+ * @tx: send an skb. The transport relies on the op_mode to zero the
+ * the ieee80211_tx_info->driver_data. If the MPDU is an A-MSDU, all
+ * the CSUM will be taken care of (TCP CSUM and IP header in case of
+ * IPv4). If the MPDU is a single MSDU, the op_mode must compute the IP
+ * header if it is IPv4.
+ * Must be atomic
+ * @reclaim: free packet until ssn. Returns a list of freed packets.
+ * Must be atomic
+ * @txq_enable: setup a queue. To setup an AC queue, use the
+ * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before
+ * this one. The op_mode must not configure the HCMD queue. The scheduler
+ * configuration may be %NULL, in which case the hardware will not be
+ * configured. May sleep.
+ * @txq_disable: de-configure a Tx queue to send AMPDUs
+ * Must be atomic
+ * @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
+ * @freeze_txq_timer: prevents the timer of the queue from firing until the
+ * queue is set to awake. Must be atomic.
+ * @block_txq_ptrs: stop updating the write pointers of the Tx queues. Note
+ * that the transport needs to refcount the calls since this function
+ * will be called several times with block = true, and then the queues
+ * need to be unblocked only after the same number of calls with
+ * block = false.
+ * @write8: write a u8 to a register at offset ofs from the BAR
+ * @write32: write a u32 to a register at offset ofs from the BAR
+ * @read32: read a u32 register at offset ofs from the BAR
+ * @read_prph: read a DWORD from a periphery register
+ * @write_prph: write a DWORD to a periphery register
+ * @read_mem: read device's SRAM in DWORD
+ * @write_mem: write device's SRAM in DWORD. If %buf is %NULL, then the memory
+ * will be zeroed.
+ * @configure: configure parameters required by the transport layer from
+ * the op_mode. May be called several times before start_fw, can't be
+ * called after that.
+ * @set_pmi: set the power pmi state
+ * @grab_nic_access: wake the NIC to be able to access non-HBUS regs.
+ * Sleeping is not allowed between grab_nic_access and
+ * release_nic_access.
+ * @release_nic_access: let the NIC go to sleep. The "flags" parameter
+ * must be the same one that was sent before to the grab_nic_access.
+ * @set_bits_mask - set SRAM register according to value and mask.
+ * @ref: grab a reference to the transport/FW layers, disallowing
+ * certain low power states
+ * @unref: release a reference previously taken with @ref. Note that
+ * initially the reference count is 1, making an initial @unref
+ * necessary to allow low power states.
+ * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last
+ * TX'ed commands and similar. The buffer will be vfree'd by the caller.
+ * Note that the transport must fill in the proper file headers.
+ */
+struct iwl_trans_ops {
+
+ int (*start_hw)(struct iwl_trans *iwl_trans, bool low_power);
+ void (*op_mode_leave)(struct iwl_trans *iwl_trans);
+ int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
+ bool run_in_rfkill);
+ int (*update_sf)(struct iwl_trans *trans,
+ struct iwl_sf_region *st_fwrd_space);
+ void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
+ void (*stop_device)(struct iwl_trans *trans, bool low_power);
+
+ void (*d3_suspend)(struct iwl_trans *trans, bool test);
+ int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status,
+ bool test);
+
+ int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
+
+ int (*tx)(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_device_cmd *dev_cmd, int queue);
+ void (*reclaim)(struct iwl_trans *trans, int queue, int ssn,
+ struct sk_buff_head *skbs);
+
+ void (*txq_enable)(struct iwl_trans *trans, int queue, u16 ssn,
+ const struct iwl_trans_txq_scd_cfg *cfg,
+ unsigned int queue_wdg_timeout);
+ void (*txq_disable)(struct iwl_trans *trans, int queue,
+ bool configure_scd);
+
+ int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
+ void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs,
+ bool freeze);
+ void (*block_txq_ptrs)(struct iwl_trans *trans, bool block);
+
+ void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
+ void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
+ u32 (*read32)(struct iwl_trans *trans, u32 ofs);
+ u32 (*read_prph)(struct iwl_trans *trans, u32 ofs);
+ void (*write_prph)(struct iwl_trans *trans, u32 ofs, u32 val);
+ int (*read_mem)(struct iwl_trans *trans, u32 addr,
+ void *buf, int dwords);
+ int (*write_mem)(struct iwl_trans *trans, u32 addr,
+ const void *buf, int dwords);
+ void (*configure)(struct iwl_trans *trans,
+ const struct iwl_trans_config *trans_cfg);
+ void (*set_pmi)(struct iwl_trans *trans, bool state);
+ bool (*grab_nic_access)(struct iwl_trans *trans, unsigned long *flags);
+ void (*release_nic_access)(struct iwl_trans *trans,
+ unsigned long *flags);
+ void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
+ u32 value);
+ void (*ref)(struct iwl_trans *trans);
+ void (*unref)(struct iwl_trans *trans);
+ int (*suspend)(struct iwl_trans *trans);
+ void (*resume)(struct iwl_trans *trans);
+
+ struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans,
+ const struct iwl_fw_dbg_trigger_tlv
+ *trigger);
+};
+
+/**
+ * enum iwl_trans_state - state of the transport layer
+ *
+ * @IWL_TRANS_NO_FW: no fw has sent an alive response
+ * @IWL_TRANS_FW_ALIVE: a fw has sent an alive response
+ */
+enum iwl_trans_state {
+ IWL_TRANS_NO_FW = 0,
+ IWL_TRANS_FW_ALIVE = 1,
+};
+
+/**
+ * DOC: Platform power management
+ *
+ * There are two types of platform power management: system-wide
+ * (WoWLAN) and runtime.
+ *
+ * In system-wide power management the entire platform goes into a low
+ * power state (e.g. idle or suspend to RAM) at the same time and the
+ * device is configured as a wakeup source for the entire platform.
+ * This is usually triggered by userspace activity (e.g. the user
+ * presses the suspend button or a power management daemon decides to
+ * put the platform in low power mode). The device's behavior in this
+ * mode is dictated by the wake-on-WLAN configuration.
+ *
+ * In runtime power management, only the devices which are themselves
+ * idle enter a low power state. This is done at runtime, which means
+ * that the entire system is still running normally. This mode is
+ * usually triggered automatically by the device driver and requires
+ * the ability to enter and exit the low power modes in a very short
+ * time, so there is not much impact in usability.
+ *
+ * The terms used for the device's behavior are as follows:
+ *
+ * - D0: the device is fully powered and the host is awake;
+ * - D3: the device is in low power mode and only reacts to
+ * specific events (e.g. magic-packet received or scan
+ * results found);
+ * - D0I3: the device is in low power mode and reacts to any
+ * activity (e.g. RX);
+ *
+ * These terms reflect the power modes in the firmware and are not to
+ * be confused with the physical device power state. The NIC can be
+ * in D0I3 mode even if, for instance, the PCI device is in D3 state.
+ */
+
+/**
+ * enum iwl_plat_pm_mode - platform power management mode
+ *
+ * This enumeration describes the device's platform power management
+ * behavior when in idle mode (i.e. runtime power management) or when
+ * in system-wide suspend (i.e WoWLAN).
+ *
+ * @IWL_PLAT_PM_MODE_DISABLED: power management is disabled for this
+ * device. At runtime, this means that nothing happens and the
+ * device always remains in active. In system-wide suspend mode,
+ * it means that the all connections will be closed automatically
+ * by mac80211 before the platform is suspended.
+ * @IWL_PLAT_PM_MODE_D3: the device goes into D3 mode (i.e. WoWLAN).
+ * For runtime power management, this mode is not officially
+ * supported.
+ * @IWL_PLAT_PM_MODE_D0I3: the device goes into D0I3 mode.
+ */
+enum iwl_plat_pm_mode {
+ IWL_PLAT_PM_MODE_DISABLED,
+ IWL_PLAT_PM_MODE_D3,
+ IWL_PLAT_PM_MODE_D0I3,
+};
+
+/**
+ * struct iwl_trans - transport common data
+ *
+ * @ops - pointer to iwl_trans_ops
+ * @op_mode - pointer to the op_mode
+ * @cfg - pointer to the configuration
+ * @status: a bit-mask of transport status flags
+ * @dev - pointer to struct device * that represents the device
+ * @max_skb_frags: maximum number of fragments an SKB can have when transmitted.
+ * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported.
+ * @hw_id: a u32 with the ID of the device / sub-device.
+ * Set during transport allocation.
+ * @hw_id_str: a string with info about HW ID. Set during transport allocation.
+ * @pm_support: set to true in start_hw if link pm is supported
+ * @ltr_enabled: set to true if the LTR is enabled
+ * @num_rx_queues: number of RX queues allocated by the transport;
+ * the transport must set this before calling iwl_drv_start()
+ * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
+ * The user should use iwl_trans_{alloc,free}_tx_cmd.
+ * @dev_cmd_headroom: room needed for the transport's private use before the
+ * device_cmd for Tx - for internal use only
+ * The user should use iwl_trans_{alloc,free}_tx_cmd.
+ * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before
+ * starting the firmware, used for tracing
+ * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
+ * start of the 802.11 header in the @rx_mpdu_cmd
+ * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
+ * @paging_req_addr: The location were the FW will upload / download the pages
+ * from. The address is set by the opmode
+ * @paging_db: Pointer to the opmode paging data base, the pointer is set by
+ * the opmode.
+ * @paging_download_buf: Buffer used for copying all of the pages before
+ * downloading them to the FW. The buffer is allocated in the opmode
+ * @system_pm_mode: the system-wide power management mode in use.
+ * This mode is set dynamically, depending on the WoWLAN values
+ * configured from the userspace at runtime.
+ * @runtime_pm_mode: the runtime power management mode in use. This
+ * mode is set during the initialization phase and is not
+ * supposed to change during runtime.
+ */
+struct iwl_trans {
+ const struct iwl_trans_ops *ops;
+ struct iwl_op_mode *op_mode;
+ const struct iwl_cfg *cfg;
+ enum iwl_trans_state state;
+ unsigned long status;
+
+ struct device *dev;
+ u32 max_skb_frags;
+ u32 hw_rev;
+ u32 hw_id;
+ char hw_id_str[52];
+
+ u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size;
+
+ bool pm_support;
+ bool ltr_enabled;
+
+ const struct iwl_hcmd_arr *command_groups;
+ int command_groups_size;
+
+ u8 num_rx_queues;
+
+ /* The following fields are internal only */
+ struct kmem_cache *dev_cmd_pool;
+ size_t dev_cmd_headroom;
+ char dev_cmd_pool_name[50];
+
+ struct dentry *dbgfs_dir;
+
+#ifdef CONFIG_LOCKDEP
+ struct lockdep_map sync_cmd_lockdep_map;
+#endif
+
+ u64 dflt_pwr_limit;
+
+ const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+ const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
+ struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
+ u8 dbg_dest_reg_num;
+
+ /*
+ * Paging parameters - All of the parameters should be set by the
+ * opmode when paging is enabled
+ */
+ u32 paging_req_addr;
+ struct iwl_fw_paging *paging_db;
+ void *paging_download_buf;
+
+ enum iwl_plat_pm_mode system_pm_mode;
+ enum iwl_plat_pm_mode runtime_pm_mode;
+
+ /* pointer to trans specific struct */
+ /*Ensure that this pointer will always be aligned to sizeof pointer */
+ char trans_specific[0] __aligned(sizeof(void *));
+};
+
+const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id);
+int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans);
+
+static inline void iwl_trans_configure(struct iwl_trans *trans,
+ const struct iwl_trans_config *trans_cfg)
+{
+ trans->op_mode = trans_cfg->op_mode;
+
+ trans->ops->configure(trans, trans_cfg);
+ WARN_ON(iwl_cmd_groups_verify_sorted(trans_cfg));
+}
+
+static inline int _iwl_trans_start_hw(struct iwl_trans *trans, bool low_power)
+{
+ might_sleep();
+
+ return trans->ops->start_hw(trans, low_power);
+}
+
+static inline int iwl_trans_start_hw(struct iwl_trans *trans)
+{
+ return trans->ops->start_hw(trans, true);
+}
+
+static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans)
+{
+ might_sleep();
+
+ if (trans->ops->op_mode_leave)
+ trans->ops->op_mode_leave(trans);
+
+ trans->op_mode = NULL;
+
+ trans->state = IWL_TRANS_NO_FW;
+}
+
+static inline void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr)
+{
+ might_sleep();
+
+ trans->state = IWL_TRANS_FW_ALIVE;
+
+ trans->ops->fw_alive(trans, scd_addr);
+}
+
+static inline int iwl_trans_start_fw(struct iwl_trans *trans,
+ const struct fw_img *fw,
+ bool run_in_rfkill)
+{
+ might_sleep();
+
+ WARN_ON_ONCE(!trans->rx_mpdu_cmd);
+
+ clear_bit(STATUS_FW_ERROR, &trans->status);
+ return trans->ops->start_fw(trans, fw, run_in_rfkill);
+}
+
+static inline int iwl_trans_update_sf(struct iwl_trans *trans,
+ struct iwl_sf_region *st_fwrd_space)
+{
+ might_sleep();
+
+ if (trans->ops->update_sf)
+ return trans->ops->update_sf(trans, st_fwrd_space);
+
+ return 0;
+}
+
+static inline void _iwl_trans_stop_device(struct iwl_trans *trans,
+ bool low_power)
+{
+ might_sleep();
+
+ trans->ops->stop_device(trans, low_power);
+
+ trans->state = IWL_TRANS_NO_FW;
+}
+
+static inline void iwl_trans_stop_device(struct iwl_trans *trans)
+{
+ _iwl_trans_stop_device(trans, true);
+}
+
+static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test)
+{
+ might_sleep();
+ if (trans->ops->d3_suspend)
+ trans->ops->d3_suspend(trans, test);
+}
+
+static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
+ enum iwl_d3_status *status,
+ bool test)
+{
+ might_sleep();
+ if (!trans->ops->d3_resume)
+ return 0;
+
+ return trans->ops->d3_resume(trans, status, test);
+}
+
+static inline void iwl_trans_ref(struct iwl_trans *trans)
+{
+ if (trans->ops->ref)
+ trans->ops->ref(trans);
+}
+
+static inline void iwl_trans_unref(struct iwl_trans *trans)
+{
+ if (trans->ops->unref)
+ trans->ops->unref(trans);
+}
+
+static inline int iwl_trans_suspend(struct iwl_trans *trans)
+{
+ if (!trans->ops->suspend)
+ return 0;
+
+ return trans->ops->suspend(trans);
+}
+
+static inline void iwl_trans_resume(struct iwl_trans *trans)
+{
+ if (trans->ops->resume)
+ trans->ops->resume(trans);
+}
+
+static inline struct iwl_trans_dump_data *
+iwl_trans_dump_data(struct iwl_trans *trans,
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ if (!trans->ops->dump_data)
+ return NULL;
+ return trans->ops->dump_data(trans, trigger);
+}
+
+static inline struct iwl_device_cmd *
+iwl_trans_alloc_tx_cmd(struct iwl_trans *trans)
+{
+ u8 *dev_cmd_ptr = kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC);
+
+ if (unlikely(dev_cmd_ptr == NULL))
+ return NULL;
+
+ return (struct iwl_device_cmd *)
+ (dev_cmd_ptr + trans->dev_cmd_headroom);
+}
+
+int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
+
+static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
+ struct iwl_device_cmd *dev_cmd)
+{
+ u8 *dev_cmd_ptr = (u8 *)dev_cmd - trans->dev_cmd_headroom;
+
+ kmem_cache_free(trans->dev_cmd_pool, dev_cmd_ptr);
+}
+
+static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_device_cmd *dev_cmd, int queue)
+{
+ if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+ return -EIO;
+
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return -EIO;
+ }
+
+ return trans->ops->tx(trans, skb, dev_cmd, queue);
+}
+
+static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
+ int ssn, struct sk_buff_head *skbs)
+{
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
+
+ trans->ops->reclaim(trans, queue, ssn, skbs);
+}
+
+static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue,
+ bool configure_scd)
+{
+ trans->ops->txq_disable(trans, queue, configure_scd);
+}
+
+static inline void
+iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn,
+ const struct iwl_trans_txq_scd_cfg *cfg,
+ unsigned int queue_wdg_timeout)
+{
+ might_sleep();
+
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
+
+ trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout);
+}
+
+static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue,
+ int fifo, int sta_id, int tid,
+ int frame_limit, u16 ssn,
+ unsigned int queue_wdg_timeout)
+{
+ struct iwl_trans_txq_scd_cfg cfg = {
+ .fifo = fifo,
+ .sta_id = sta_id,
+ .tid = tid,
+ .frame_limit = frame_limit,
+ .aggregate = sta_id >= 0,
+ };
+
+ iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg, queue_wdg_timeout);
+}
+
+static inline
+void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo,
+ unsigned int queue_wdg_timeout)
+{
+ struct iwl_trans_txq_scd_cfg cfg = {
+ .fifo = fifo,
+ .sta_id = -1,
+ .tid = IWL_MAX_TID_COUNT,
+ .frame_limit = IWL_FRAME_LIMIT,
+ .aggregate = false,
+ };
+
+ iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout);
+}
+
+static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans,
+ unsigned long txqs,
+ bool freeze)
+{
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
+
+ if (trans->ops->freeze_txq_timer)
+ trans->ops->freeze_txq_timer(trans, txqs, freeze);
+}
+
+static inline void iwl_trans_block_txq_ptrs(struct iwl_trans *trans,
+ bool block)
+{
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
+
+ if (trans->ops->block_txq_ptrs)
+ trans->ops->block_txq_ptrs(trans, block);
+}
+
+static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
+ u32 txqs)
+{
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return -EIO;
+ }
+
+ return trans->ops->wait_tx_queue_empty(trans, txqs);
+}
+
+static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val)
+{
+ trans->ops->write8(trans, ofs, val);
+}
+
+static inline void iwl_trans_write32(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+ trans->ops->write32(trans, ofs, val);
+}
+
+static inline u32 iwl_trans_read32(struct iwl_trans *trans, u32 ofs)
+{
+ return trans->ops->read32(trans, ofs);
+}
+
+static inline u32 iwl_trans_read_prph(struct iwl_trans *trans, u32 ofs)
+{
+ return trans->ops->read_prph(trans, ofs);
+}
+
+static inline void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs,
+ u32 val)
+{
+ return trans->ops->write_prph(trans, ofs, val);
+}
+
+static inline int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
+ void *buf, int dwords)
+{
+ return trans->ops->read_mem(trans, addr, buf, dwords);
+}
+
+#define iwl_trans_read_mem_bytes(trans, addr, buf, bufsize) \
+ do { \
+ if (__builtin_constant_p(bufsize)) \
+ BUILD_BUG_ON((bufsize) % sizeof(u32)); \
+ iwl_trans_read_mem(trans, addr, buf, (bufsize) / sizeof(u32));\
+ } while (0)
+
+static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr)
+{
+ u32 value;
+
+ if (WARN_ON(iwl_trans_read_mem(trans, addr, &value, 1)))
+ return 0xa5a5a5a5;
+
+ return value;
+}
+
+static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
+ const void *buf, int dwords)
+{
+ return trans->ops->write_mem(trans, addr, buf, dwords);
+}
+
+static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr,
+ u32 val)
+{
+ return iwl_trans_write_mem(trans, addr, &val, 1);
+}
+
+static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
+{
+ if (trans->ops->set_pmi)
+ trans->ops->set_pmi(trans, state);
+}
+
+static inline void
+iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value)
+{
+ trans->ops->set_bits_mask(trans, reg, mask, value);
+}
+
+#define iwl_trans_grab_nic_access(trans, flags) \
+ __cond_lock(nic_access, \
+ likely((trans)->ops->grab_nic_access(trans, flags)))
+
+static inline void __releases(nic_access)
+iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
+{
+ trans->ops->release_nic_access(trans, flags);
+ __release(nic_access);
+}
+
+static inline void iwl_trans_fw_error(struct iwl_trans *trans)
+{
+ if (WARN_ON_ONCE(!trans->op_mode))
+ return;
+
+ /* prevent double restarts due to the same erroneous FW */
+ if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status))
+ iwl_op_mode_nic_error(trans->op_mode);
+}
+
+/*****************************************************
+ * transport helper functions
+ *****************************************************/
+struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
+ struct device *dev,
+ const struct iwl_cfg *cfg,
+ const struct iwl_trans_ops *ops,
+ size_t dev_cmd_headroom);
+void iwl_trans_free(struct iwl_trans *trans);
+
+/*****************************************************
+* driver (transport) register/unregister functions
+******************************************************/
+int __must_check iwl_pci_register_driver(void);
+void iwl_pci_unregister_driver(void);
+
+#endif /* __iwl_trans_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile
new file mode 100644
index 000000000000..23e7e2937566
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile
@@ -0,0 +1,12 @@
+obj-$(CONFIG_IWLMVM) += iwlmvm.o
+iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
+iwlmvm-y += utils.o rx.o rxmq.o tx.o binding.o quota.o sta.o sf.o
+iwlmvm-y += scan.o time-event.o rs.o
+iwlmvm-y += power.o coex.o coex_legacy.o
+iwlmvm-y += tt.o offloading.o tdls.o
+iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
+iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
+iwlmvm-y += tof.o fw-dbg.o
+iwlmvm-$(CONFIG_PM) += d3.o
+
+ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c
new file mode 100644
index 000000000000..7cb68f6ed1b0
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c
@@ -0,0 +1,211 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+struct iwl_mvm_iface_iterator_data {
+ struct ieee80211_vif *ignore_vif;
+ int idx;
+
+ struct iwl_mvm_phy_ctxt *phyctxt;
+
+ u16 ids[MAX_MACS_IN_BINDING];
+ u16 colors[MAX_MACS_IN_BINDING];
+};
+
+static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action,
+ struct iwl_mvm_iface_iterator_data *data)
+{
+ struct iwl_binding_cmd cmd;
+ struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt;
+ int i, ret;
+ u32 status;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
+ phyctxt->color));
+ cmd.action = cpu_to_le32(action);
+ cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
+ phyctxt->color));
+
+ for (i = 0; i < MAX_MACS_IN_BINDING; i++)
+ cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
+ for (i = 0; i < data->idx; i++)
+ cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i],
+ data->colors[i]));
+
+ status = 0;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
+ sizeof(cmd), &cmd, &status);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n",
+ action, ret);
+ return ret;
+ }
+
+ if (status) {
+ IWL_ERR(mvm, "Binding command failed: %u\n", status);
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static void iwl_mvm_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_iface_iterator_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (vif == data->ignore_vif)
+ return;
+
+ if (mvmvif->phy_ctxt != data->phyctxt)
+ return;
+
+ if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING))
+ return;
+
+ data->ids[data->idx] = mvmvif->id;
+ data->colors[data->idx] = mvmvif->color;
+ data->idx++;
+}
+
+static int iwl_mvm_binding_update(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mvm_phy_ctxt *phyctxt,
+ bool add)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_iface_iterator_data data = {
+ .ignore_vif = vif,
+ .phyctxt = phyctxt,
+ };
+ u32 action = FW_CTXT_ACTION_MODIFY;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_iface_iterator,
+ &data);
+
+ /*
+ * If there are no other interfaces yet we
+ * need to create a new binding.
+ */
+ if (data.idx == 0) {
+ if (add)
+ action = FW_CTXT_ACTION_ADD;
+ else
+ action = FW_CTXT_ACTION_REMOVE;
+ }
+
+ if (add) {
+ if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING))
+ return -EINVAL;
+
+ data.ids[data.idx] = mvmvif->id;
+ data.colors[data.idx] = mvmvif->color;
+ data.idx++;
+ }
+
+ return iwl_mvm_binding_cmd(mvm, action, &data);
+}
+
+int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+ return -EINVAL;
+
+ /*
+ * Update SF - Disable if needed. if this fails, SF might still be on
+ * while many macs are bound, which is forbidden - so fail the binding.
+ */
+ if (iwl_mvm_sf_update(mvm, vif, false))
+ return -EINVAL;
+
+ return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
+}
+
+int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+ return -EINVAL;
+
+ ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
+
+ if (!ret)
+ if (iwl_mvm_sf_update(mvm, vif, true))
+ IWL_ERR(mvm, "Failed to update SF state\n");
+
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c
new file mode 100644
index 000000000000..2e098f8e0f83
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c
@@ -0,0 +1,1008 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "fw-api-coex.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-debug.h"
+
+/* 20MHz / 40MHz below / 40Mhz above*/
+static const __le64 iwl_ci_mask[][3] = {
+ /* dummy entry for channel 0 */
+ {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
+ {
+ cpu_to_le64(0x0000001FFFULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x00007FFFFFULL),
+ },
+ {
+ cpu_to_le64(0x000000FFFFULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x0003FFFFFFULL),
+ },
+ {
+ cpu_to_le64(0x000003FFFCULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x000FFFFFFCULL),
+ },
+ {
+ cpu_to_le64(0x00001FFFE0ULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x007FFFFFE0ULL),
+ },
+ {
+ cpu_to_le64(0x00007FFF80ULL),
+ cpu_to_le64(0x00007FFFFFULL),
+ cpu_to_le64(0x01FFFFFF80ULL),
+ },
+ {
+ cpu_to_le64(0x0003FFFC00ULL),
+ cpu_to_le64(0x0003FFFFFFULL),
+ cpu_to_le64(0x0FFFFFFC00ULL),
+ },
+ {
+ cpu_to_le64(0x000FFFF000ULL),
+ cpu_to_le64(0x000FFFFFFCULL),
+ cpu_to_le64(0x3FFFFFF000ULL),
+ },
+ {
+ cpu_to_le64(0x007FFF8000ULL),
+ cpu_to_le64(0x007FFFFFE0ULL),
+ cpu_to_le64(0xFFFFFF8000ULL),
+ },
+ {
+ cpu_to_le64(0x01FFFE0000ULL),
+ cpu_to_le64(0x01FFFFFF80ULL),
+ cpu_to_le64(0xFFFFFE0000ULL),
+ },
+ {
+ cpu_to_le64(0x0FFFF00000ULL),
+ cpu_to_le64(0x0FFFFFFC00ULL),
+ cpu_to_le64(0x0ULL),
+ },
+ {
+ cpu_to_le64(0x3FFFC00000ULL),
+ cpu_to_le64(0x3FFFFFF000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFFE000000ULL),
+ cpu_to_le64(0xFFFFFF8000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFF8000000ULL),
+ cpu_to_le64(0xFFFFFE0000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFC0000000ULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x0ULL)
+ },
+};
+
+struct corunning_block_luts {
+ u8 range;
+ __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
+};
+
+/*
+ * Ranges for the antenna coupling calibration / co-running block LUT:
+ * LUT0: [ 0, 12[
+ * LUT1: [12, 20[
+ * LUT2: [20, 21[
+ * LUT3: [21, 23[
+ * LUT4: [23, 27[
+ * LUT5: [27, 30[
+ * LUT6: [30, 32[
+ * LUT7: [32, 33[
+ * LUT8: [33, - [
+ */
+static const struct corunning_block_luts antenna_coupling_ranges[] = {
+ {
+ .range = 0,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 12,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 20,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 21,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 23,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 27,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 30,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 32,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 33,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+};
+
+static enum iwl_bt_coex_lut_type
+iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum iwl_bt_coex_lut_type ret;
+ u16 phy_ctx_id;
+ u32 primary_ch_phy_id, secondary_ch_phy_id;
+
+ /*
+ * Checking that we hold mvm->mutex is a good idea, but the rate
+ * control can't acquire the mutex since it runs in Tx path.
+ * So this is racy in that case, but in the worst case, the AMPDU
+ * size limit will be wrong for a short time which is not a big
+ * issue.
+ */
+
+ rcu_read_lock();
+
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+ if (!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+ rcu_read_unlock();
+ return BT_COEX_INVALID_LUT;
+ }
+
+ ret = BT_COEX_TX_DIS_LUT;
+
+ if (mvm->cfg->bt_shared_single_ant) {
+ rcu_read_unlock();
+ return ret;
+ }
+
+ phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+ primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);
+ secondary_ch_phy_id =
+ le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);
+
+ if (primary_ch_phy_id == phy_ctx_id)
+ ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
+ else if (secondary_ch_phy_id == phy_ctx_id)
+ ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
+ /* else - default = TX TX disallowed */
+
+ rcu_read_unlock();
+
+ return ret;
+}
+
+int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_coex_cmd bt_cmd = {};
+ u32 mode;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_send_bt_init_conf_old(mvm);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+ switch (mvm->bt_force_ant_mode) {
+ case BT_FORCE_ANT_BT:
+ mode = BT_COEX_BT;
+ break;
+ case BT_FORCE_ANT_WIFI:
+ mode = BT_COEX_WIFI;
+ break;
+ default:
+ WARN_ON(1);
+ mode = 0;
+ }
+
+ bt_cmd.mode = cpu_to_le32(mode);
+ goto send_cmd;
+ }
+
+ mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
+ bt_cmd.mode = cpu_to_le32(mode);
+
+ if (IWL_MVM_BT_COEX_SYNC2SCO)
+ bt_cmd.enabled_modules |=
+ cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
+
+ if (iwl_mvm_bt_is_plcr_supported(mvm))
+ bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
+
+ if (iwl_mvm_is_mplut_supported(mvm))
+ bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
+
+ bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
+
+send_cmd:
+ memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
+ memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
+
+ return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd);
+}
+
+static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
+ bool enable)
+{
+ struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};
+ struct iwl_mvm_sta *mvmsta;
+ u32 value;
+ int ret;
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+ if (!mvmsta)
+ return 0;
+
+ /* nothing to do */
+ if (mvmsta->bt_reduced_txpower == enable)
+ return 0;
+
+ value = mvmsta->sta_id;
+
+ if (enable)
+ value |= BT_REDUCED_TX_POWER_BIT;
+
+ IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
+ enable ? "en" : "dis", sta_id);
+
+ cmd.reduced_txp = cpu_to_le32(value);
+ mvmsta->bt_reduced_txpower = enable;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC,
+ sizeof(cmd), &cmd);
+
+ return ret;
+}
+
+struct iwl_bt_iterator_data {
+ struct iwl_bt_coex_profile_notif *notif;
+ struct iwl_mvm *mvm;
+ struct ieee80211_chanctx_conf *primary;
+ struct ieee80211_chanctx_conf *secondary;
+ bool primary_ll;
+};
+
+static inline
+void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable, int rssi)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ mvmvif->bf_data.last_bt_coex_event = rssi;
+ mvmvif->bf_data.bt_coex_max_thold =
+ enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
+ mvmvif->bf_data.bt_coex_min_thold =
+ enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
+}
+
+/* must be called under rcu_read_lock */
+static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_bt_iterator_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ /* default smps_mode is AUTOMATIC - only used for client modes */
+ enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ u32 bt_activity_grading;
+ int ave_rssi;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!mvmvif->ap_ibss_active)
+ return;
+ break;
+ default:
+ return;
+ }
+
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+ /* If channel context is invalid or not on 2.4GHz .. */
+ if ((!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ /* ... relax constraints and disable rssi events */
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+ iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ false);
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+ }
+ return;
+ }
+
+ bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
+ if (bt_activity_grading >= BT_HIGH_TRAFFIC)
+ smps_mode = IEEE80211_SMPS_STATIC;
+ else if (bt_activity_grading >= BT_LOW_TRAFFIC)
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+
+ /* relax SMPS constraints for next association */
+ if (!vif->bss_conf.assoc)
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ if (mvmvif->phy_ctxt &&
+ IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
+ mvmvif->phy_ctxt->id))
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ IWL_DEBUG_COEX(data->mvm,
+ "mac %d: bt_activity_grading %d smps_req %d\n",
+ mvmvif->id, bt_activity_grading, smps_mode);
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+
+ /* low latency is always primary */
+ if (iwl_mvm_vif_low_latency(mvmvif)) {
+ data->primary_ll = true;
+
+ data->secondary = data->primary;
+ data->primary = chanctx_conf;
+ }
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ if (!mvmvif->ap_ibss_active)
+ return;
+
+ if (chanctx_conf == data->primary)
+ return;
+
+ if (!data->primary_ll) {
+ /*
+ * downgrade the current primary no matter what its
+ * type is.
+ */
+ data->secondary = data->primary;
+ data->primary = chanctx_conf;
+ } else {
+ /* there is low latency vif - we will be secondary */
+ data->secondary = chanctx_conf;
+ }
+ return;
+ }
+
+ /*
+ * STA / P2P Client, try to be primary if first vif. If we are in low
+ * latency mode, we are already in primary and just don't do much
+ */
+ if (!data->primary || data->primary == chanctx_conf)
+ data->primary = chanctx_conf;
+ else if (!data->secondary)
+ /* if secondary is not NULL, it might be a GO */
+ data->secondary = chanctx_conf;
+
+ /*
+ * don't reduce the Tx power if one of these is true:
+ * we are in LOOSE
+ * single share antenna product
+ * BT is active
+ * we are associated
+ */
+ if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
+ mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
+ le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
+ iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+ return;
+ }
+
+ /* try to get the avg rssi from fw */
+ ave_rssi = mvmvif->bf_data.ave_beacon_signal;
+
+ /* if the RSSI isn't valid, fake it is very low */
+ if (!ave_rssi)
+ ave_rssi = -100;
+ if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+ } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+ }
+
+ /* Begin to monitor the RSSI: it may influence the reduced Tx power */
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
+}
+
+static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_iterator_data data = {
+ .mvm = mvm,
+ .notif = &mvm->last_bt_notif,
+ };
+ struct iwl_bt_coex_ci_cmd cmd = {};
+ u8 ci_bw_idx;
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ rcu_read_lock();
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bt_notif_iterator, &data);
+
+ if (data.primary) {
+ struct ieee80211_chanctx_conf *chan = data.primary;
+ if (WARN_ON(!chan->def.chan)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+ ci_bw_idx = 0;
+ } else {
+ if (chan->def.center_freq1 >
+ chan->def.chan->center_freq)
+ ci_bw_idx = 2;
+ else
+ ci_bw_idx = 1;
+ }
+
+ cmd.bt_primary_ci =
+ iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+ cmd.primary_ch_phy_id =
+ cpu_to_le32(*((u16 *)data.primary->drv_priv));
+ }
+
+ if (data.secondary) {
+ struct ieee80211_chanctx_conf *chan = data.secondary;
+ if (WARN_ON(!data.secondary->def.chan)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+ ci_bw_idx = 0;
+ } else {
+ if (chan->def.center_freq1 >
+ chan->def.chan->center_freq)
+ ci_bw_idx = 2;
+ else
+ ci_bw_idx = 1;
+ }
+
+ cmd.bt_secondary_ci =
+ iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+ cmd.secondary_ch_phy_id =
+ cpu_to_le32(*((u16 *)data.secondary->drv_priv));
+ }
+
+ rcu_read_unlock();
+
+ /* Don't spam the fw with the same command over and over */
+ if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {
+ if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
+ sizeof(cmd), &cmd))
+ IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
+ memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
+ }
+}
+
+void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ iwl_mvm_rx_bt_coex_notif_old(mvm, rxb);
+ return;
+ }
+
+ IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
+ IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+ IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
+ le32_to_cpu(notif->primary_ch_lut));
+ IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
+ le32_to_cpu(notif->secondary_ch_lut));
+ IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
+ le32_to_cpu(notif->bt_activity_grading));
+
+ /* remember this notification for future use: rssi fluctuations */
+ memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
+
+ iwl_mvm_bt_coex_notif_handle(mvm);
+}
+
+void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event_data rssi_event)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event);
+ return;
+ }
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ /*
+ * Rssi update while not associated - can happen since the statistics
+ * are handled asynchronously
+ */
+ if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ /* No BT - reports should be disabled */
+ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)
+ return;
+
+ IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
+ rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
+
+ /*
+ * Check if rssi is good enough for reduced Tx power, but not in loose
+ * scheme.
+ */
+ if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
+ iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ false);
+ else
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
+
+ if (ret)
+ IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
+}
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
+#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
+
+u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+ struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
+ enum iwl_bt_coex_lut_type lut_type;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
+
+ if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+ BT_HIGH_TRAFFIC)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+
+ if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ /* tight coex, high bt traffic, reduce AGG time limit */
+ return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
+}
+
+bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+ struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
+ enum iwl_bt_coex_lut_type lut_type;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_bt_coex_is_mimo_allowed_old(mvm, sta);
+
+ if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
+ return true;
+
+ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+ BT_HIGH_TRAFFIC)
+ return true;
+
+ /*
+ * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
+ * since BT is already killed.
+ * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
+ * we Tx.
+ * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
+ */
+ lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+ return lut_type != BT_COEX_LOOSE_LUT;
+}
+
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant)
+{
+ /* there is no other antenna, shared antenna is always available */
+ if (mvm->cfg->bt_shared_single_ant)
+ return true;
+
+ if (ant & mvm->cfg->non_shared_ant)
+ return true;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
+ return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+ BT_HIGH_TRAFFIC;
+}
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
+{
+ /* there is no other antenna, shared antenna is always available */
+ if (mvm->cfg->bt_shared_single_ant)
+ return true;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
+ return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC;
+}
+
+bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
+ enum ieee80211_band band)
+{
+ u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band);
+
+ if (band != IEEE80211_BAND_2GHZ)
+ return false;
+
+ return bt_activity >= BT_LOW_TRAFFIC;
+}
+
+u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *info, u8 ac)
+{
+ __le16 fc = hdr->frame_control;
+ bool mplut_enabled = iwl_mvm_is_mplut_supported(mvm);
+
+ if (info->band != IEEE80211_BAND_2GHZ)
+ return 0;
+
+ if (unlikely(mvm->bt_tx_prio))
+ return mvm->bt_tx_prio - 1;
+
+ if (likely(ieee80211_is_data(fc))) {
+ if (likely(ieee80211_is_data_qos(fc))) {
+ switch (ac) {
+ case IEEE80211_AC_BE:
+ return mplut_enabled ? 1 : 0;
+ case IEEE80211_AC_VI:
+ return mplut_enabled ? 2 : 3;
+ case IEEE80211_AC_VO:
+ return 3;
+ default:
+ return 0;
+ }
+ } else if (is_multicast_ether_addr(hdr->addr1)) {
+ return 3;
+ } else
+ return 0;
+ } else if (ieee80211_is_mgmt(fc)) {
+ return ieee80211_is_disassoc(fc) ? 0 : 3;
+ } else if (ieee80211_is_ctl(fc)) {
+ /* ignore cfend and cfendack frames as we never send those */
+ return 3;
+ }
+
+ return 0;
+}
+
+void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
+{
+ if (!fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ iwl_mvm_bt_coex_vif_change_old(mvm);
+ return;
+ }
+
+ iwl_mvm_bt_coex_notif_handle(mvm);
+}
+
+void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+ struct iwl_bt_coex_corun_lut_update_cmd cmd = {};
+ u8 __maybe_unused lower_bound, upper_bound;
+ u8 lut;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb);
+ return;
+ }
+
+ if (!iwl_mvm_bt_is_plcr_supported(mvm))
+ return;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ if (ant_isolation == mvm->last_ant_isol)
+ return;
+
+ for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
+ if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
+ break;
+
+ lower_bound = antenna_coupling_ranges[lut].range;
+
+ if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
+ upper_bound = antenna_coupling_ranges[lut + 1].range;
+ else
+ upper_bound = antenna_coupling_ranges[lut].range;
+
+ IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
+ ant_isolation, lower_bound, upper_bound, lut);
+
+ mvm->last_ant_isol = ant_isolation;
+
+ if (mvm->last_corun_lut == lut)
+ return;
+
+ mvm->last_corun_lut = lut;
+
+ /* For the moment, use the same LUT for 20GHz and 40GHz */
+ memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20,
+ sizeof(cmd.corun_lut20));
+
+ memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20,
+ sizeof(cmd.corun_lut40));
+
+ if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0,
+ sizeof(cmd), &cmd))
+ IWL_ERR(mvm,
+ "failed to send BT_COEX_UPDATE_CORUN_LUT command\n");
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c
new file mode 100644
index 000000000000..015045733444
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c
@@ -0,0 +1,1315 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "fw-api-coex.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-debug.h"
+
+#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
+ [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
+ ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
+
+static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
+ BT_COEX_PRIO_TBL_PRIO_LOW, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
+ BT_COEX_PRIO_TBL_PRIO_LOW, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
+ BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
+ BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
+ BT_COEX_PRIO_TBL_DISABLED, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
+ BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
+ BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
+ BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
+ 0, 0, 0, 0, 0, 0,
+};
+
+#undef EVENT_PRIO_ANT
+
+static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+{
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return 0;
+
+ return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
+ sizeof(struct iwl_bt_coex_prio_tbl_cmd),
+ &iwl_bt_prio_tbl);
+}
+
+static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
+ cpu_to_le32(0xf0f0f0f0), /* 50% */
+ cpu_to_le32(0xc0c0c0c0), /* 25% */
+ cpu_to_le32(0xfcfcfcfc), /* 75% */
+ cpu_to_le32(0xfefefefe), /* 87.5% */
+};
+
+static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+ {
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ },
+};
+
+static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+ {
+ /* Tight */
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0x00004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ /* Loose */
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ /* Tx Tx disabled */
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xeeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+ },
+};
+
+/* 20MHz / 40MHz below / 40Mhz above*/
+static const __le64 iwl_ci_mask[][3] = {
+ /* dummy entry for channel 0 */
+ {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
+ {
+ cpu_to_le64(0x0000001FFFULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x00007FFFFFULL),
+ },
+ {
+ cpu_to_le64(0x000000FFFFULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x0003FFFFFFULL),
+ },
+ {
+ cpu_to_le64(0x000003FFFCULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x000FFFFFFCULL),
+ },
+ {
+ cpu_to_le64(0x00001FFFE0ULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x007FFFFFE0ULL),
+ },
+ {
+ cpu_to_le64(0x00007FFF80ULL),
+ cpu_to_le64(0x00007FFFFFULL),
+ cpu_to_le64(0x01FFFFFF80ULL),
+ },
+ {
+ cpu_to_le64(0x0003FFFC00ULL),
+ cpu_to_le64(0x0003FFFFFFULL),
+ cpu_to_le64(0x0FFFFFFC00ULL),
+ },
+ {
+ cpu_to_le64(0x000FFFF000ULL),
+ cpu_to_le64(0x000FFFFFFCULL),
+ cpu_to_le64(0x3FFFFFF000ULL),
+ },
+ {
+ cpu_to_le64(0x007FFF8000ULL),
+ cpu_to_le64(0x007FFFFFE0ULL),
+ cpu_to_le64(0xFFFFFF8000ULL),
+ },
+ {
+ cpu_to_le64(0x01FFFE0000ULL),
+ cpu_to_le64(0x01FFFFFF80ULL),
+ cpu_to_le64(0xFFFFFE0000ULL),
+ },
+ {
+ cpu_to_le64(0x0FFFF00000ULL),
+ cpu_to_le64(0x0FFFFFFC00ULL),
+ cpu_to_le64(0x0ULL),
+ },
+ {
+ cpu_to_le64(0x3FFFC00000ULL),
+ cpu_to_le64(0x3FFFFFF000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFFE000000ULL),
+ cpu_to_le64(0xFFFFFF8000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFF8000000ULL),
+ cpu_to_le64(0xFFFFFE0000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFC0000000ULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x0ULL)
+ },
+};
+
+enum iwl_bt_kill_msk {
+ BT_KILL_MSK_DEFAULT,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_MAX,
+};
+
+static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
+ [BT_KILL_MSK_DEFAULT] = 0xfffffc00,
+ [BT_KILL_MSK_NEVER] = 0xffffffff,
+ [BT_KILL_MSK_ALWAYS] = 0,
+};
+
+static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ },
+ {
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ },
+ {
+ BT_KILL_MSK_DEFAULT,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_DEFAULT,
+ },
+};
+
+static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_DEFAULT,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_DEFAULT,
+ },
+};
+
+struct corunning_block_luts {
+ u8 range;
+ __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
+};
+
+/*
+ * Ranges for the antenna coupling calibration / co-running block LUT:
+ * LUT0: [ 0, 12[
+ * LUT1: [12, 20[
+ * LUT2: [20, 21[
+ * LUT3: [21, 23[
+ * LUT4: [23, 27[
+ * LUT5: [27, 30[
+ * LUT6: [30, 32[
+ * LUT7: [32, 33[
+ * LUT8: [33, - [
+ */
+static const struct corunning_block_luts antenna_coupling_ranges[] = {
+ {
+ .range = 0,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 12,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 20,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 21,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 23,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 27,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 30,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 32,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 33,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+};
+
+static enum iwl_bt_coex_lut_type
+iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum iwl_bt_coex_lut_type ret;
+ u16 phy_ctx_id;
+
+ /*
+ * Checking that we hold mvm->mutex is a good idea, but the rate
+ * control can't acquire the mutex since it runs in Tx path.
+ * So this is racy in that case, but in the worst case, the AMPDU
+ * size limit will be wrong for a short time which is not a big
+ * issue.
+ */
+
+ rcu_read_lock();
+
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+ if (!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+ rcu_read_unlock();
+ return BT_COEX_INVALID_LUT;
+ }
+
+ ret = BT_COEX_TX_DIS_LUT;
+
+ if (mvm->cfg->bt_shared_single_ant) {
+ rcu_read_unlock();
+ return ret;
+ }
+
+ phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+
+ if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id)
+ ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut);
+ else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id)
+ ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut);
+ /* else - default = TX TX disallowed */
+
+ rcu_read_unlock();
+
+ return ret;
+}
+
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ int ret;
+ u32 flags;
+
+ ret = iwl_send_bt_prio_tbl(mvm);
+ if (ret)
+ return ret;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+ if (!bt_cmd)
+ return -ENOMEM;
+ cmd.data[0] = bt_cmd;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+ switch (mvm->bt_force_ant_mode) {
+ case BT_FORCE_ANT_AUTO:
+ flags = BT_COEX_AUTO_OLD;
+ break;
+ case BT_FORCE_ANT_BT:
+ flags = BT_COEX_BT_OLD;
+ break;
+ case BT_FORCE_ANT_WIFI:
+ flags = BT_COEX_WIFI_OLD;
+ break;
+ default:
+ WARN_ON(1);
+ flags = 0;
+ }
+
+ bt_cmd->flags = cpu_to_le32(flags);
+ bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE);
+ goto send_cmd;
+ }
+
+ bt_cmd->max_kill = 5;
+ bt_cmd->bt4_antenna_isolation_thr =
+ IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS;
+ bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
+ bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
+ bt_cmd->bt4_tx_rx_max_freq0 = 15;
+ bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
+ bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
+
+ flags = iwlwifi_mod_params.bt_coex_active ?
+ BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD;
+ bt_cmd->flags = cpu_to_le32(flags);
+
+ bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
+ BT_VALID_BT_PRIO_BOOST |
+ BT_VALID_MAX_KILL |
+ BT_VALID_3W_TMRS |
+ BT_VALID_KILL_ACK |
+ BT_VALID_KILL_CTS |
+ BT_VALID_REDUCED_TX_POWER |
+ BT_VALID_LUT |
+ BT_VALID_WIFI_RX_SW_PRIO_BOOST |
+ BT_VALID_WIFI_TX_SW_PRIO_BOOST |
+ BT_VALID_ANT_ISOLATION |
+ BT_VALID_ANT_ISOLATION_THRS |
+ BT_VALID_TXTX_DELTA_FREQ_THRS |
+ BT_VALID_TXRX_MAX_FREQ_0 |
+ BT_VALID_SYNC_TO_SCO |
+ BT_VALID_TTC |
+ BT_VALID_RRC);
+
+ if (IWL_MVM_BT_COEX_SYNC2SCO)
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+
+ if (iwl_mvm_bt_is_plcr_supported(mvm)) {
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
+ BT_VALID_CORUN_LUT_40);
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
+ }
+
+ if (IWL_MVM_BT_COEX_MPLUT) {
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+ }
+
+ if (IWL_MVM_BT_COEX_TTC)
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC);
+
+ if (iwl_mvm_bt_is_rrc_supported(mvm))
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC);
+
+ if (mvm->cfg->bt_shared_single_ant)
+ memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
+ sizeof(iwl_single_shared_ant));
+ else
+ memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
+ sizeof(iwl_combined_lookup));
+
+ /* Take first Co-running block LUT to get started */
+ memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
+ sizeof(bt_cmd->bt4_corun_lut20));
+ memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
+ sizeof(bt_cmd->bt4_corun_lut40));
+
+ memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+ sizeof(iwl_bt_prio_boost));
+ bt_cmd->bt4_multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0);
+ bt_cmd->bt4_multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1);
+
+send_cmd:
+ memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
+ memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
+}
+
+static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old;
+ u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
+ u32 ag = le32_to_cpu(notif->bt_activity_grading);
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ u8 ack_kill_msk, cts_kill_msk;
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .data[0] = &bt_cmd,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ int ret = 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ack_kill_msk = iwl_bt_ack_kill_msk[ag][primary_lut];
+ cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut];
+
+ if (mvm->bt_ack_kill_msk[0] == ack_kill_msk &&
+ mvm->bt_cts_kill_msk[0] == cts_kill_msk)
+ return 0;
+
+ mvm->bt_ack_kill_msk[0] = ack_kill_msk;
+ mvm->bt_cts_kill_msk[0] = cts_kill_msk;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+ if (!bt_cmd)
+ return -ENOMEM;
+ cmd.data[0] = bt_cmd;
+ bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+ bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk]);
+ bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk]);
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+ BT_VALID_KILL_ACK |
+ BT_VALID_KILL_CTS);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
+}
+
+static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
+ bool enable)
+{
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ /* Send ASYNC since this can be sent from an atomic context */
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_DUP, },
+ .flags = CMD_ASYNC,
+ };
+ struct iwl_mvm_sta *mvmsta;
+ int ret;
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+ if (!mvmsta)
+ return 0;
+
+ /* nothing to do */
+ if (mvmsta->bt_reduced_txpower == enable)
+ return 0;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
+ if (!bt_cmd)
+ return -ENOMEM;
+ cmd.data[0] = bt_cmd;
+ bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+ bt_cmd->valid_bit_msk =
+ cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
+ bt_cmd->bt_reduced_tx_power = sta_id;
+
+ if (enable)
+ bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+
+ IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
+ enable ? "en" : "dis", sta_id);
+
+ mvmsta->bt_reduced_txpower = enable;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
+}
+
+struct iwl_bt_iterator_data {
+ struct iwl_bt_coex_profile_notif_old *notif;
+ struct iwl_mvm *mvm;
+ struct ieee80211_chanctx_conf *primary;
+ struct ieee80211_chanctx_conf *secondary;
+ bool primary_ll;
+};
+
+static inline
+void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable, int rssi)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ mvmvif->bf_data.last_bt_coex_event = rssi;
+ mvmvif->bf_data.bt_coex_max_thold =
+ enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
+ mvmvif->bf_data.bt_coex_min_thold =
+ enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
+}
+
+/* must be called under rcu_read_lock */
+static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_bt_iterator_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_smps_mode smps_mode;
+ u32 bt_activity_grading;
+ int ave_rssi;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ /* default smps_mode for BSS / P2P client is AUTOMATIC */
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!mvmvif->ap_ibss_active)
+ return;
+ break;
+ default:
+ return;
+ }
+
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+ /* If channel context is invalid or not on 2.4GHz .. */
+ if ((!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ /* ... relax constraints and disable rssi events */
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+ iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ false);
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+ }
+ return;
+ }
+
+ bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
+ if (bt_activity_grading >= BT_HIGH_TRAFFIC)
+ smps_mode = IEEE80211_SMPS_STATIC;
+ else if (bt_activity_grading >= BT_LOW_TRAFFIC)
+ smps_mode = vif->type == NL80211_IFTYPE_AP ?
+ IEEE80211_SMPS_OFF :
+ IEEE80211_SMPS_DYNAMIC;
+
+ /* relax SMPS contraints for next association */
+ if (!vif->bss_conf.assoc)
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ if (mvmvif->phy_ctxt &&
+ data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ IWL_DEBUG_COEX(data->mvm,
+ "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
+ mvmvif->id, data->notif->bt_status, bt_activity_grading,
+ smps_mode);
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+
+ /* low latency is always primary */
+ if (iwl_mvm_vif_low_latency(mvmvif)) {
+ data->primary_ll = true;
+
+ data->secondary = data->primary;
+ data->primary = chanctx_conf;
+ }
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ if (!mvmvif->ap_ibss_active)
+ return;
+
+ if (chanctx_conf == data->primary)
+ return;
+
+ if (!data->primary_ll) {
+ /*
+ * downgrade the current primary no matter what its
+ * type is.
+ */
+ data->secondary = data->primary;
+ data->primary = chanctx_conf;
+ } else {
+ /* there is low latency vif - we will be secondary */
+ data->secondary = chanctx_conf;
+ }
+ return;
+ }
+
+ /*
+ * STA / P2P Client, try to be primary if first vif. If we are in low
+ * latency mode, we are already in primary and just don't do much
+ */
+ if (!data->primary || data->primary == chanctx_conf)
+ data->primary = chanctx_conf;
+ else if (!data->secondary)
+ /* if secondary is not NULL, it might be a GO */
+ data->secondary = chanctx_conf;
+
+ /*
+ * don't reduce the Tx power if one of these is true:
+ * we are in LOOSE
+ * single share antenna product
+ * BT is active
+ * we are associated
+ */
+ if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
+ mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
+ !data->notif->bt_status) {
+ iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+ return;
+ }
+
+ /* try to get the avg rssi from fw */
+ ave_rssi = mvmvif->bf_data.ave_beacon_signal;
+
+ /* if the RSSI isn't valid, fake it is very low */
+ if (!ave_rssi)
+ ave_rssi = -100;
+ if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+ } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+ }
+
+ /* Begin to monitor the RSSI: it may influence the reduced Tx power */
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
+}
+
+static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_iterator_data data = {
+ .mvm = mvm,
+ .notif = &mvm->last_bt_notif_old,
+ };
+ struct iwl_bt_coex_ci_cmd_old cmd = {};
+ u8 ci_bw_idx;
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ rcu_read_lock();
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bt_notif_iterator, &data);
+
+ if (data.primary) {
+ struct ieee80211_chanctx_conf *chan = data.primary;
+
+ if (WARN_ON(!chan->def.chan)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+ ci_bw_idx = 0;
+ cmd.co_run_bw_primary = 0;
+ } else {
+ cmd.co_run_bw_primary = 1;
+ if (chan->def.center_freq1 >
+ chan->def.chan->center_freq)
+ ci_bw_idx = 2;
+ else
+ ci_bw_idx = 1;
+ }
+
+ cmd.bt_primary_ci =
+ iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+ cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+ }
+
+ if (data.secondary) {
+ struct ieee80211_chanctx_conf *chan = data.secondary;
+
+ if (WARN_ON(!data.secondary->def.chan)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+ ci_bw_idx = 0;
+ cmd.co_run_bw_secondary = 0;
+ } else {
+ cmd.co_run_bw_secondary = 1;
+ if (chan->def.center_freq1 >
+ chan->def.chan->center_freq)
+ ci_bw_idx = 2;
+ else
+ ci_bw_idx = 1;
+ }
+
+ cmd.bt_secondary_ci =
+ iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+ cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+ }
+
+ rcu_read_unlock();
+
+ /* Don't spam the fw with the same command over and over */
+ if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) {
+ if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
+ sizeof(cmd), &cmd))
+ IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
+ memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd));
+ }
+
+ if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
+ IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data;
+
+ IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
+ IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
+ notif->bt_status ? "ON" : "OFF");
+ IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
+ IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+ IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
+ le32_to_cpu(notif->primary_ch_lut));
+ IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
+ le32_to_cpu(notif->secondary_ch_lut));
+ IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
+ le32_to_cpu(notif->bt_activity_grading));
+ IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
+ notif->bt_agg_traffic_load);
+
+ /* remember this notification for future use: rssi fluctuations */
+ memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old));
+
+ iwl_mvm_bt_coex_notif_handle(mvm);
+}
+
+static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_bt_iterator_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ /* If channel context is invalid or not on 2.4GHz - don't count it */
+ if (!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+ rcu_read_unlock();
+ return;
+ }
+ rcu_read_unlock();
+
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ /* This can happen if the station has been removed right now */
+ if (IS_ERR_OR_NULL(sta))
+ return;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+}
+
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event_data rssi_event)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_bt_iterator_data data = {
+ .mvm = mvm,
+ };
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ /*
+ * Rssi update while not associated - can happen since the statistics
+ * are handled asynchronously
+ */
+ if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ /* No BT - reports should be disabled */
+ if (!mvm->last_bt_notif_old.bt_status)
+ return;
+
+ IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
+ rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
+
+ /*
+ * Check if rssi is good enough for reduced Tx power, but not in loose
+ * scheme.
+ */
+ if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
+ iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ false);
+ else
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
+
+ if (ret)
+ IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bt_rssi_iterator, &data);
+
+ if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
+ IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
+#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
+
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ enum iwl_bt_coex_lut_type lut_type;
+
+ if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+ BT_HIGH_TRAFFIC)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ if (mvm->last_bt_notif_old.ttc_enabled)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+
+ if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ /* tight coex, high bt traffic, reduce AGG time limit */
+ return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
+}
+
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ enum iwl_bt_coex_lut_type lut_type;
+
+ if (mvm->last_bt_notif_old.ttc_enabled)
+ return true;
+
+ if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+ BT_HIGH_TRAFFIC)
+ return true;
+
+ /*
+ * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
+ * since BT is already killed.
+ * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
+ * we Tx.
+ * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
+ */
+ lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+ return lut_type != BT_COEX_LOOSE_LUT;
+}
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
+{
+ u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+ return ag < BT_HIGH_TRAFFIC;
+}
+
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+ enum ieee80211_band band)
+{
+ u32 bt_activity =
+ le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+
+ if (band != IEEE80211_BAND_2GHZ)
+ return false;
+
+ return bt_activity >= BT_LOW_TRAFFIC;
+}
+
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm)
+{
+ iwl_mvm_bt_coex_notif_handle(mvm);
+}
+
+void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+ u8 __maybe_unused lower_bound, upper_bound;
+ u8 lut;
+
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+
+ if (!iwl_mvm_bt_is_plcr_supported(mvm))
+ return;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ if (ant_isolation == mvm->last_ant_isol)
+ return;
+
+ for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
+ if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
+ break;
+
+ lower_bound = antenna_coupling_ranges[lut].range;
+
+ if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
+ upper_bound = antenna_coupling_ranges[lut + 1].range;
+ else
+ upper_bound = antenna_coupling_ranges[lut].range;
+
+ IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
+ ant_isolation, lower_bound, upper_bound, lut);
+
+ mvm->last_ant_isol = ant_isolation;
+
+ if (mvm->last_corun_lut == lut)
+ return;
+
+ mvm->last_corun_lut = lut;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+ if (!bt_cmd)
+ return;
+ cmd.data[0] = bt_cmd;
+
+ bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+ BT_VALID_CORUN_LUT_20 |
+ BT_VALID_CORUN_LUT_40);
+
+ /* For the moment, use the same LUT for 20GHz and 40GHz */
+ memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
+ sizeof(bt_cmd->bt4_corun_lut20));
+
+ memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
+ sizeof(bt_cmd->bt4_corun_lut40));
+
+ if (iwl_mvm_send_cmd(mvm, &cmd))
+ IWL_ERR(mvm, "failed to send BT_CONFIG command\n");
+
+ kfree(bt_cmd);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h
new file mode 100644
index 000000000000..b00c03fcd447
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __MVM_CONSTANTS_H
+#define __MVM_CONSTANTS_H
+
+#include <linux/ieee80211.h>
+
+#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
+#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
+#define IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */
+#define IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */
+#define IWL_MVM_P2P_LOWLATENCY_PS_ENABLE 0
+#define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC)
+#define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC)
+#define IWL_MVM_UAPSD_QUEUES (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+#define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20
+#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 8
+#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30
+#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20
+#define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50
+#define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50
+#define IWL_MVM_PS_SNOOZE_INTERVAL 25
+#define IWL_MVM_PS_SNOOZE_WINDOW 50
+#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25
+#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64
+#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62
+#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65
+#define IWL_MVM_BT_COEX_SYNC2SCO 1
+#define IWL_MVM_BT_COEX_CORUNNING 0
+#define IWL_MVM_BT_COEX_MPLUT 1
+#define IWL_MVM_BT_COEX_RRC 1
+#define IWL_MVM_BT_COEX_TTC 1
+#define IWL_MVM_BT_COEX_MPLUT_REG0 0x22002200
+#define IWL_MVM_BT_COEX_MPLUT_REG1 0x11118451
+#define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS 30
+#define IWL_MVM_FW_MCAST_FILTER_PASS_ALL 0
+#define IWL_MVM_FW_BCAST_FILTER_PASS_ALL 0
+#define IWL_MVM_QUOTA_THRESHOLD 4
+#define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0
+#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1
+#define IWL_MVM_TOF_IS_RESPONDER 0
+#define IWL_MVM_SW_TX_CSUM_OFFLOAD 0
+#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1
+#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2
+#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1
+#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES 3
+#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES 3
+#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES 2
+#define IWL_MVM_RS_INITIAL_LEGACY_RETRIES 2
+#define IWL_MVM_RS_SECONDARY_LEGACY_RETRIES 1
+#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES 16
+#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES 3
+#define IWL_MVM_RS_SECONDARY_SISO_RETRIES 1
+#define IWL_MVM_RS_RATE_MIN_FAILURE_TH 3
+#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH 8
+#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT 5 /* Seconds */
+#define IWL_MVM_RS_IDLE_TIMEOUT 5 /* Seconds */
+#define IWL_MVM_RS_MISSED_RATE_MAX 15
+#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT 160
+#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT 480
+#define IWL_MVM_RS_LEGACY_TABLE_COUNT 160
+#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT 400
+#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT 4500
+#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT 1500
+#define IWL_MVM_RS_SR_FORCE_DECREASE 15 /* percent */
+#define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */
+#define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */
+#define IWL_MVM_RS_AGG_DISABLE_START 3
+#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */
+#define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */
+#define IWL_MVM_RS_TPC_TX_POWER_STEP 3
+
+#endif /* __MVM_CONSTANTS_H */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
new file mode 100644
index 000000000000..d3e21d95cece
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -0,0 +1,2284 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/fs.h>
+#include <net/cfg80211.h>
+#include <net/ipv6.h>
+#include <net/tcp.h>
+#include <net/addrconf.h>
+#include "iwl-modparams.h"
+#include "fw-api.h"
+#include "mvm.h"
+
+void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (iwlwifi_mod_params.sw_crypto)
+ return;
+
+ mutex_lock(&mvm->mutex);
+
+ memcpy(mvmvif->rekey_data.kek, data->kek, NL80211_KEK_LEN);
+ memcpy(mvmvif->rekey_data.kck, data->kck, NL80211_KCK_LEN);
+ mvmvif->rekey_data.replay_ctr =
+ cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
+ mvmvif->rekey_data.valid = true;
+
+ mutex_unlock(&mvm->mutex);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct inet6_dev *idev)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct inet6_ifaddr *ifa;
+ int idx = 0;
+
+ memset(mvmvif->tentative_addrs, 0, sizeof(mvmvif->tentative_addrs));
+
+ read_lock_bh(&idev->lock);
+ list_for_each_entry(ifa, &idev->addr_list, if_list) {
+ mvmvif->target_ipv6_addrs[idx] = ifa->addr;
+ if (ifa->flags & IFA_F_TENTATIVE)
+ __set_bit(idx, mvmvif->tentative_addrs);
+ idx++;
+ if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
+ break;
+ }
+ read_unlock_bh(&idev->lock);
+
+ mvmvif->num_target_ipv6_addrs = idx;
+}
+#endif
+
+void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int idx)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ mvmvif->tx_key_idx = idx;
+}
+
+static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out)
+{
+ int i;
+
+ for (i = 0; i < IWL_P1K_SIZE; i++)
+ out[i] = cpu_to_le16(p1k[i]);
+}
+
+static const u8 *iwl_mvm_find_max_pn(struct ieee80211_key_conf *key,
+ struct iwl_mvm_key_pn *ptk_pn,
+ struct ieee80211_key_seq *seq,
+ int tid, int queues)
+{
+ const u8 *ret = seq->ccmp.pn;
+ int i;
+
+ /* get the PN from mac80211, used on the default queue */
+ ieee80211_get_key_rx_seq(key, tid, seq);
+
+ /* and use the internal data for the other queues */
+ for (i = 1; i < queues; i++) {
+ const u8 *tmp = ptk_pn->q[i].pn[tid];
+
+ if (memcmp(ret, tmp, IEEE80211_CCMP_PN_LEN) <= 0)
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+struct wowlan_key_data {
+ struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
+ struct iwl_wowlan_tkip_params_cmd *tkip;
+ bool error, use_rsc_tsc, use_tkip, configure_keys;
+ int wep_key_idx;
+};
+
+static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct wowlan_key_data *data = _data;
+ struct aes_sc *aes_sc, *aes_tx_sc = NULL;
+ struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
+ struct iwl_p1k_cache *rx_p1ks;
+ u8 *rx_mic_key;
+ struct ieee80211_key_seq seq;
+ u32 cur_rx_iv32 = 0;
+ u16 p1k[IWL_P1K_SIZE];
+ int ret, i;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */
+ struct {
+ struct iwl_mvm_wep_key_cmd wep_key_cmd;
+ struct iwl_mvm_wep_key wep_key;
+ } __packed wkc = {
+ .wep_key_cmd.mac_id_n_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color)),
+ .wep_key_cmd.num_keys = 1,
+ /* firmware sets STA_KEY_FLG_WEP_13BYTES */
+ .wep_key_cmd.decryption_type = STA_KEY_FLG_WEP,
+ .wep_key.key_index = key->keyidx,
+ .wep_key.key_size = key->keylen,
+ };
+
+ /*
+ * This will fail -- the key functions don't set support
+ * pairwise WEP keys. However, that's better than silently
+ * failing WoWLAN. Or maybe not?
+ */
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ break;
+
+ memcpy(&wkc.wep_key.key[3], key->key, key->keylen);
+ if (key->keyidx == mvmvif->tx_key_idx) {
+ /* TX key must be at offset 0 */
+ wkc.wep_key.key_offset = 0;
+ } else {
+ /* others start at 1 */
+ data->wep_key_idx++;
+ wkc.wep_key.key_offset = data->wep_key_idx;
+ }
+
+ if (data->configure_keys) {
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0,
+ sizeof(wkc), &wkc);
+ data->error = ret != 0;
+
+ mvm->ptk_ivlen = key->iv_len;
+ mvm->ptk_icvlen = key->icv_len;
+ mvm->gtk_ivlen = key->iv_len;
+ mvm->gtk_icvlen = key->icv_len;
+ mutex_unlock(&mvm->mutex);
+ }
+
+ /* don't upload key again */
+ return;
+ }
+ default:
+ data->error = true;
+ return;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ /*
+ * Ignore CMAC keys -- the WoWLAN firmware doesn't support them
+ * but we also shouldn't abort suspend due to that. It does have
+ * support for the IGTK key renewal, but doesn't really use the
+ * IGTK for anything. This means we could spuriously wake up or
+ * be deauthenticated, but that was considered acceptable.
+ */
+ return;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (sta) {
+ tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
+ tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
+
+ rx_p1ks = data->tkip->rx_uni;
+
+ ieee80211_get_key_tx_seq(key, &seq);
+ tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
+ tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
+
+ ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
+ iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k);
+
+ memcpy(data->tkip->mic_keys.tx,
+ &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
+ IWL_MIC_KEY_SIZE);
+
+ rx_mic_key = data->tkip->mic_keys.rx_unicast;
+ } else {
+ tkip_sc =
+ data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
+ rx_p1ks = data->tkip->rx_multi;
+ rx_mic_key = data->tkip->mic_keys.rx_mcast;
+ }
+
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211 use TID 0 (as they need to to avoid replay attacks)
+ * for checking the IV in the frames.
+ */
+ for (i = 0; i < IWL_NUM_RSC; i++) {
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
+ tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
+ /* wrapping isn't allowed, AP must rekey */
+ if (seq.tkip.iv32 > cur_rx_iv32)
+ cur_rx_iv32 = seq.tkip.iv32;
+ }
+
+ ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
+ cur_rx_iv32, p1k);
+ iwl_mvm_convert_p1k(p1k, rx_p1ks[0].p1k);
+ ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
+ cur_rx_iv32 + 1, p1k);
+ iwl_mvm_convert_p1k(p1k, rx_p1ks[1].p1k);
+
+ memcpy(rx_mic_key,
+ &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
+ IWL_MIC_KEY_SIZE);
+
+ data->use_tkip = true;
+ data->use_rsc_tsc = true;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (sta) {
+ u64 pn64;
+
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
+ aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
+
+ pn64 = atomic64_read(&key->tx_pn);
+ aes_tx_sc->pn = cpu_to_le64(pn64);
+ } else {
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
+ }
+
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211/our RX code use TID 0 for checking the PN.
+ */
+ if (sta && iwl_mvm_has_new_rx_api(mvm)) {
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_key_pn *ptk_pn;
+ const u8 *pn;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ ptk_pn = rcu_dereference_protected(
+ mvmsta->ptk_pn[key->keyidx],
+ lockdep_is_held(&mvm->mutex));
+ if (WARN_ON(!ptk_pn))
+ break;
+
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i,
+ mvm->trans->num_rx_queues);
+ aes_sc[i].pn = cpu_to_le64((u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+ } else {
+ for (i = 0; i < IWL_NUM_RSC; i++) {
+ u8 *pn = seq.ccmp.pn;
+
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ aes_sc[i].pn = cpu_to_le64((u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+ }
+ data->use_rsc_tsc = true;
+ break;
+ }
+
+ if (data->configure_keys) {
+ mutex_lock(&mvm->mutex);
+ /*
+ * The D3 firmware hardcodes the key offset 0 as the key it
+ * uses to transmit packets to the AP, i.e. the PTK.
+ */
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ mvm->ptk_ivlen = key->iv_len;
+ mvm->ptk_icvlen = key->icv_len;
+ ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0);
+ } else {
+ /*
+ * firmware only supports TSC/RSC for a single key,
+ * so if there are multiple keep overwriting them
+ * with new ones -- this relies on mac80211 doing
+ * list_add_tail().
+ */
+ mvm->gtk_ivlen = key->iv_len;
+ mvm->gtk_icvlen = key->icv_len;
+ ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1);
+ }
+ mutex_unlock(&mvm->mutex);
+ data->error = ret != 0;
+ }
+}
+
+static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct iwl_wowlan_patterns_cmd *pattern_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = WOWLAN_PATTERNS,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+ int i, err;
+
+ if (!wowlan->n_patterns)
+ return 0;
+
+ cmd.len[0] = sizeof(*pattern_cmd) +
+ wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern);
+
+ pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
+ if (!pattern_cmd)
+ return -ENOMEM;
+
+ pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
+
+ for (i = 0; i < wowlan->n_patterns; i++) {
+ int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
+
+ memcpy(&pattern_cmd->patterns[i].mask,
+ wowlan->patterns[i].mask, mask_len);
+ memcpy(&pattern_cmd->patterns[i].pattern,
+ wowlan->patterns[i].pattern,
+ wowlan->patterns[i].pattern_len);
+ pattern_cmd->patterns[i].mask_size = mask_len;
+ pattern_cmd->patterns[i].pattern_size =
+ wowlan->patterns[i].pattern_len;
+ }
+
+ cmd.data[0] = pattern_cmd;
+ err = iwl_mvm_send_cmd(mvm, &cmd);
+ kfree(pattern_cmd);
+ return err;
+}
+
+enum iwl_mvm_tcp_packet_type {
+ MVM_TCP_TX_SYN,
+ MVM_TCP_RX_SYNACK,
+ MVM_TCP_TX_DATA,
+ MVM_TCP_RX_ACK,
+ MVM_TCP_RX_WAKE,
+ MVM_TCP_TX_FIN,
+};
+
+static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr)
+{
+ __sum16 check = tcp_v4_check(len, saddr, daddr, 0);
+ return cpu_to_le16(be16_to_cpu((__force __be16)check));
+}
+
+static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif,
+ struct cfg80211_wowlan_tcp *tcp,
+ void *_pkt, u8 *mask,
+ __le16 *pseudo_hdr_csum,
+ enum iwl_mvm_tcp_packet_type ptype)
+{
+ struct {
+ struct ethhdr eth;
+ struct iphdr ip;
+ struct tcphdr tcp;
+ u8 data[];
+ } __packed *pkt = _pkt;
+ u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
+ int i;
+
+ pkt->eth.h_proto = cpu_to_be16(ETH_P_IP),
+ pkt->ip.version = 4;
+ pkt->ip.ihl = 5;
+ pkt->ip.protocol = IPPROTO_TCP;
+
+ switch (ptype) {
+ case MVM_TCP_TX_SYN:
+ case MVM_TCP_TX_DATA:
+ case MVM_TCP_TX_FIN:
+ memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN);
+ memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN);
+ pkt->ip.ttl = 128;
+ pkt->ip.saddr = tcp->src;
+ pkt->ip.daddr = tcp->dst;
+ pkt->tcp.source = cpu_to_be16(tcp->src_port);
+ pkt->tcp.dest = cpu_to_be16(tcp->dst_port);
+ /* overwritten for TX SYN later */
+ pkt->tcp.doff = sizeof(struct tcphdr) / 4;
+ pkt->tcp.window = cpu_to_be16(65000);
+ break;
+ case MVM_TCP_RX_SYNACK:
+ case MVM_TCP_RX_ACK:
+ case MVM_TCP_RX_WAKE:
+ memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN);
+ memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN);
+ pkt->ip.saddr = tcp->dst;
+ pkt->ip.daddr = tcp->src;
+ pkt->tcp.source = cpu_to_be16(tcp->dst_port);
+ pkt->tcp.dest = cpu_to_be16(tcp->src_port);
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ switch (ptype) {
+ case MVM_TCP_TX_SYN:
+ /* firmware assumes 8 option bytes - 8 NOPs for now */
+ memset(pkt->data, 0x01, 8);
+ ip_tot_len += 8;
+ pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4;
+ pkt->tcp.syn = 1;
+ break;
+ case MVM_TCP_TX_DATA:
+ ip_tot_len += tcp->payload_len;
+ memcpy(pkt->data, tcp->payload, tcp->payload_len);
+ pkt->tcp.psh = 1;
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_TX_FIN:
+ pkt->tcp.fin = 1;
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_RX_SYNACK:
+ pkt->tcp.syn = 1;
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_RX_ACK:
+ pkt->tcp.ack = 1;
+ break;
+ case MVM_TCP_RX_WAKE:
+ ip_tot_len += tcp->wake_len;
+ pkt->tcp.psh = 1;
+ pkt->tcp.ack = 1;
+ memcpy(pkt->data, tcp->wake_data, tcp->wake_len);
+ break;
+ }
+
+ switch (ptype) {
+ case MVM_TCP_TX_SYN:
+ case MVM_TCP_TX_DATA:
+ case MVM_TCP_TX_FIN:
+ pkt->ip.tot_len = cpu_to_be16(ip_tot_len);
+ pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl);
+ break;
+ case MVM_TCP_RX_WAKE:
+ for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) {
+ u8 tmp = tcp->wake_mask[i];
+ mask[i + 6] |= tmp << 6;
+ if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8))
+ mask[i + 7] = tmp >> 2;
+ }
+ /* fall through for ethernet/IP/TCP headers mask */
+ case MVM_TCP_RX_SYNACK:
+ case MVM_TCP_RX_ACK:
+ mask[0] = 0xff; /* match ethernet */
+ /*
+ * match ethernet, ip.version, ip.ihl
+ * the ip.ihl half byte is really masked out by firmware
+ */
+ mask[1] = 0x7f;
+ mask[2] = 0x80; /* match ip.protocol */
+ mask[3] = 0xfc; /* match ip.saddr, ip.daddr */
+ mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */
+ mask[5] = 0x80; /* match tcp flags */
+ /* leave rest (0 or set for MVM_TCP_RX_WAKE) */
+ break;
+ };
+
+ *pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr),
+ pkt->ip.saddr, pkt->ip.daddr);
+}
+
+static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_wowlan_tcp *tcp)
+{
+ struct iwl_wowlan_remote_wake_config *cfg;
+ struct iwl_host_cmd cmd = {
+ .id = REMOTE_WAKE_CONFIG_CMD,
+ .len = { sizeof(*cfg), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ int ret;
+
+ if (!tcp)
+ return 0;
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+ cmd.data[0] = cfg;
+
+ cfg->max_syn_retries = 10;
+ cfg->max_data_retries = 10;
+ cfg->tcp_syn_ack_timeout = 1; /* seconds */
+ cfg->tcp_ack_timeout = 1; /* seconds */
+
+ /* SYN (TX) */
+ iwl_mvm_build_tcp_packet(
+ 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(
+ 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(
+ 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 =
+ cpu_to_le16(tcp->payload_len);
+ cfg->sequence_number_offset = tcp->payload_seq.offset;
+ /* length must be 0..4, the field is little endian */
+ cfg->sequence_number_length = tcp->payload_seq.len;
+ cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start);
+ cfg->keepalive_interval = cpu_to_le16(tcp->data_interval);
+ if (tcp->payload_tok.len) {
+ cfg->token_offset = tcp->payload_tok.offset;
+ cfg->token_length = tcp->payload_tok.len;
+ cfg->num_tokens =
+ cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len);
+ memcpy(cfg->tokens, tcp->payload_tok.token_stream,
+ tcp->tokens_size);
+ } else {
+ /* set tokens to max value to almost never run out */
+ cfg->num_tokens = cpu_to_le16(65535);
+ }
+
+ /* ACK (RX) */
+ iwl_mvm_build_tcp_packet(
+ 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);
+ cfg->keepalive_ack_rx.info.tcp_payload_length = 0;
+
+ /* WAKEUP (RX) */
+ iwl_mvm_build_tcp_packet(
+ 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 =
+ cpu_to_le16(tcp->wake_len);
+
+ /* FIN */
+ iwl_mvm_build_tcp_packet(
+ 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;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ kfree(cfg);
+
+ return ret;
+}
+
+static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *ap_sta)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_chanctx_conf *ctx;
+ u8 chains_static, chains_dynamic;
+ struct cfg80211_chan_def chandef;
+ int ret, i;
+ struct iwl_binding_cmd binding_cmd = {};
+ struct iwl_time_quota_cmd quota_cmd = {};
+ u32 status;
+
+ /* add back the PHY */
+ if (WARN_ON(!mvmvif->phy_ctxt))
+ return -EINVAL;
+
+ rcu_read_lock();
+ ctx = rcu_dereference(vif->chanctx_conf);
+ if (WARN_ON(!ctx)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ chandef = ctx->def;
+ chains_static = ctx->rx_chains_static;
+ chains_dynamic = ctx->rx_chains_dynamic;
+ rcu_read_unlock();
+
+ ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef,
+ chains_static, chains_dynamic);
+ if (ret)
+ return ret;
+
+ /* add back the MAC */
+ mvmvif->uploaded = false;
+
+ if (WARN_ON(!vif->bss_conf.assoc))
+ return -EINVAL;
+
+ ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+ if (ret)
+ return ret;
+
+ /* add back binding - XXX refactor? */
+ binding_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+ mvmvif->phy_ctxt->color));
+ binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+ binding_cmd.phy =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+ mvmvif->phy_ctxt->color));
+ binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color));
+ for (i = 1; i < MAX_MACS_IN_BINDING; i++)
+ binding_cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
+
+ status = 0;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
+ sizeof(binding_cmd), &binding_cmd,
+ &status);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to add binding: %d\n", ret);
+ return ret;
+ }
+
+ if (status) {
+ IWL_ERR(mvm, "Binding command failed: %u\n", status);
+ return -EIO;
+ }
+
+ ret = iwl_mvm_sta_send_to_fw(mvm, ap_sta, false);
+ if (ret)
+ return ret;
+ rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta);
+
+ ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ if (ret)
+ return ret;
+
+ /* and some quota */
+ quota_cmd.quotas[0].id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+ mvmvif->phy_ctxt->color));
+ quota_cmd.quotas[0].quota = cpu_to_le32(IWL_MVM_MAX_QUOTA);
+ quota_cmd.quotas[0].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA);
+
+ for (i = 1; i < MAX_BINDINGS; i++)
+ quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
+ sizeof(quota_cmd), &quota_cmd);
+ if (ret)
+ IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
+
+ if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm))
+ IWL_ERR(mvm, "Failed to initialize D3 LAR information\n");
+
+ return 0;
+}
+
+static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_nonqos_seq_query_cmd query_cmd = {
+ .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET),
+ .mac_id_n_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color)),
+ };
+ struct iwl_host_cmd cmd = {
+ .id = NON_QOS_TX_COUNTER_CMD,
+ .flags = CMD_WANT_SKB,
+ };
+ int err;
+ u32 size;
+
+ cmd.data[0] = &query_cmd;
+ cmd.len[0] = sizeof(query_cmd);
+
+ err = iwl_mvm_send_cmd(mvm, &cmd);
+ if (err)
+ return err;
+
+ size = iwl_rx_packet_payload_len(cmd.resp_pkt);
+ if (size < sizeof(__le16)) {
+ err = -EINVAL;
+ } else {
+ err = le16_to_cpup((__le16 *)cmd.resp_pkt->data);
+ /* firmware returns next, not last-used seqno */
+ err = (u16) (err - 0x10);
+ }
+
+ iwl_free_resp(&cmd);
+ return err;
+}
+
+void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_nonqos_seq_query_cmd query_cmd = {
+ .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET),
+ .mac_id_n_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color)),
+ .value = cpu_to_le16(mvmvif->seqno),
+ };
+
+ /* return if called during restart, not resume from D3 */
+ if (!mvmvif->seqno_valid)
+ return;
+
+ mvmvif->seqno_valid = false;
+
+ if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0,
+ sizeof(query_cmd), &query_cmd))
+ IWL_ERR(mvm, "failed to set non-QoS seqno\n");
+}
+
+static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm)
+{
+ iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
+
+ iwl_trans_stop_device(mvm->trans);
+
+ /*
+ * Set the HW restart bit -- this is mostly true as we're
+ * going to load new firmware and reprogram that, though
+ * the reprogramming is going to be manual to avoid adding
+ * all the MACs that aren't support.
+ * We don't have to clear up everything though because the
+ * reprogramming is manual. When we resume, we'll actually
+ * go through a proper restart sequence again to switch
+ * back to the runtime firmware image.
+ */
+ set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+
+ /* the fw is reset, so all the keys are cleared */
+ memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
+
+ mvm->ptk_ivlen = 0;
+ mvm->ptk_icvlen = 0;
+ mvm->ptk_ivlen = 0;
+ mvm->ptk_icvlen = 0;
+
+ return iwl_mvm_load_d3_fw(mvm);
+}
+
+static int
+iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan,
+ struct iwl_wowlan_config_cmd *wowlan_config_cmd,
+ struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
+ struct ieee80211_sta *ap_sta)
+{
+ int ret;
+ struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
+
+ /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */
+
+ wowlan_config_cmd->is_11n_connection =
+ ap_sta->ht_cap.ht_supported;
+ wowlan_config_cmd->flags = ENABLE_L3_FILTERING |
+ ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING;
+
+ /* Query the last used seqno and set it */
+ ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
+ if (ret < 0)
+ return ret;
+
+ wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret);
+
+ iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd);
+
+ if (wowlan->disconnect)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+ if (wowlan->magic_pkt)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
+ if (wowlan->gtk_rekey_failure)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
+ if (wowlan->eap_identity_req)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
+ if (wowlan->four_way_handshake)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
+ if (wowlan->n_patterns)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
+
+ if (wowlan->rfkill_release)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+ if (wowlan->tcp) {
+ /*
+ * Set the "link change" (really "link lost") flag as well
+ * since that implies losing the TCP connection.
+ */
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
+ IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
+ IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+ }
+
+ return 0;
+}
+
+static void
+iwl_mvm_iter_d0i3_ap_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *data)
+{
+ struct ieee80211_sta *ap_sta;
+
+ rcu_read_lock();
+
+ ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id]);
+ if (IS_ERR_OR_NULL(ap_sta))
+ goto out;
+
+ ieee80211_iter_keys_rcu(mvm->hw, vif, iter, data);
+out:
+ rcu_read_unlock();
+}
+
+int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool d0i3,
+ u32 cmd_flags)
+{
+ struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
+ struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
+ struct wowlan_key_data key_data = {
+ .configure_keys = !d0i3,
+ .use_rsc_tsc = false,
+ .tkip = &tkip_cmd,
+ .use_tkip = false,
+ };
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+ if (!key_data.rsc_tsc)
+ return -ENOMEM;
+
+ /*
+ * if we have to configure keys, call ieee80211_iter_keys(),
+ * as we need non-atomic context in order to take the
+ * required locks.
+ * for the d0i3 we can't use ieee80211_iter_keys(), as
+ * taking (almost) any mutex might result in deadlock.
+ */
+ if (!d0i3) {
+ /*
+ * Note that currently we don't propagate cmd_flags
+ * to the iterator. In case of key_data.configure_keys,
+ * all the configured commands are SYNC, and
+ * iwl_mvm_wowlan_program_keys() will take care of
+ * locking/unlocking mvm->mutex.
+ */
+ ieee80211_iter_keys(mvm->hw, vif,
+ iwl_mvm_wowlan_program_keys,
+ &key_data);
+ } else {
+ iwl_mvm_iter_d0i3_ap_keys(mvm, vif,
+ iwl_mvm_wowlan_program_keys,
+ &key_data);
+ }
+
+ if (key_data.error) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (key_data.use_rsc_tsc) {
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ WOWLAN_TSC_RSC_PARAM, cmd_flags,
+ sizeof(*key_data.rsc_tsc),
+ key_data.rsc_tsc);
+ if (ret)
+ goto out;
+ }
+
+ if (key_data.use_tkip) {
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ WOWLAN_TKIP_PARAM,
+ cmd_flags, sizeof(tkip_cmd),
+ &tkip_cmd);
+ if (ret)
+ goto out;
+ }
+
+ /* configure rekey data only if offloaded rekey is supported (d3) */
+ if (mvmvif->rekey_data.valid && !d0i3) {
+ memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
+ memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
+ NL80211_KCK_LEN);
+ kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
+ memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
+ NL80211_KEK_LEN);
+ kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
+ kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ WOWLAN_KEK_KCK_MATERIAL, cmd_flags,
+ sizeof(kek_kck_cmd),
+ &kek_kck_cmd);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+out:
+ kfree(key_data.rsc_tsc);
+ return ret;
+}
+
+static int
+iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan,
+ struct iwl_wowlan_config_cmd *wowlan_config_cmd,
+ struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
+ struct ieee80211_sta *ap_sta)
+{
+ int ret;
+
+ ret = iwl_mvm_switch_to_d3(mvm);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta);
+ if (ret)
+ return ret;
+
+ if (!iwlwifi_mod_params.sw_crypto) {
+ /*
+ * This needs to be unlocked due to lock ordering
+ * constraints. Since we're in the suspend path
+ * that isn't really a problem though.
+ */
+ mutex_unlock(&mvm->mutex);
+ ret = iwl_mvm_wowlan_config_key_params(mvm, vif, false,
+ CMD_ASYNC);
+ mutex_lock(&mvm->mutex);
+ if (ret)
+ return ret;
+ }
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+ sizeof(*wowlan_config_cmd),
+ wowlan_config_cmd);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_send_patterns(mvm, wowlan);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_send_proto_offload(mvm, vif, false, true, 0);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
+ return ret;
+}
+
+static int
+iwl_mvm_netdetect_config(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan,
+ struct cfg80211_sched_scan_request *nd_config,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+ int ret;
+
+ ret = iwl_mvm_switch_to_d3(mvm);
+ if (ret)
+ return ret;
+
+ /* rfkill release can be either for wowlan or netdetect */
+ if (wowlan->rfkill_release)
+ wowlan_config_cmd.wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+ sizeof(wowlan_config_cmd),
+ &wowlan_config_cmd);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_sched_scan_start(mvm, vif, nd_config, &mvm->nd_ies,
+ IWL_MVM_SCAN_NETDETECT);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels))
+ return -EBUSY;
+
+ /* save the sched scan matchsets... */
+ if (nd_config->n_match_sets) {
+ mvm->nd_match_sets = kmemdup(nd_config->match_sets,
+ sizeof(*nd_config->match_sets) *
+ nd_config->n_match_sets,
+ GFP_KERNEL);
+ if (mvm->nd_match_sets)
+ mvm->n_nd_match_sets = nd_config->n_match_sets;
+ }
+
+ /* ...and the sched scan channels for later reporting */
+ mvm->nd_channels = kmemdup(nd_config->channels,
+ sizeof(*nd_config->channels) *
+ nd_config->n_channels,
+ GFP_KERNEL);
+ if (mvm->nd_channels)
+ mvm->n_nd_channels = nd_config->n_channels;
+
+ return 0;
+}
+
+static void iwl_mvm_free_nd(struct iwl_mvm *mvm)
+{
+ kfree(mvm->nd_match_sets);
+ mvm->nd_match_sets = NULL;
+ mvm->n_nd_match_sets = 0;
+ kfree(mvm->nd_channels);
+ mvm->nd_channels = NULL;
+ mvm->n_nd_channels = 0;
+}
+
+static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan,
+ bool test)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_vif *vif = NULL;
+ struct iwl_mvm_vif *mvmvif = NULL;
+ struct ieee80211_sta *ap_sta = NULL;
+ struct iwl_d3_manager_config d3_cfg_cmd_data = {
+ /*
+ * Program the minimum sleep time to 10 seconds, as many
+ * platforms have issues processing a wakeup signal while
+ * still being in the process of suspending.
+ */
+ .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
+ };
+ struct iwl_host_cmd d3_cfg_cmd = {
+ .id = D3_CONFIG_CMD,
+ .flags = CMD_WANT_SKB,
+ .data[0] = &d3_cfg_cmd_data,
+ .len[0] = sizeof(d3_cfg_cmd_data),
+ };
+ int ret;
+ int len __maybe_unused;
+
+ if (!wowlan) {
+ /*
+ * mac80211 shouldn't get here, but for D3 test
+ * it doesn't warrant a warning
+ */
+ WARN_ON(!test);
+ return -EINVAL;
+ }
+
+ mutex_lock(&mvm->mutex);
+
+ vif = iwl_mvm_get_bss_vif(mvm);
+ if (IS_ERR_OR_NULL(vif)) {
+ ret = 1;
+ goto out_noreset;
+ }
+
+ mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) {
+ /* if we're not associated, this must be netdetect */
+ if (!wowlan->nd_config) {
+ ret = 1;
+ goto out_noreset;
+ }
+
+ ret = iwl_mvm_netdetect_config(
+ mvm, wowlan, wowlan->nd_config, vif);
+ if (ret)
+ goto out;
+
+ mvm->net_detect = true;
+ } else {
+ struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+
+ ap_sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(ap_sta)) {
+ ret = -EINVAL;
+ goto out_noreset;
+ }
+
+ ret = iwl_mvm_get_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
+ vif, mvmvif, ap_sta);
+ if (ret)
+ goto out_noreset;
+ ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
+ vif, mvmvif, ap_sta);
+ if (ret)
+ goto out;
+
+ mvm->net_detect = false;
+ }
+
+ ret = iwl_mvm_power_update_device(mvm);
+ if (ret)
+ goto out;
+
+ ret = iwl_mvm_power_update_mac(mvm);
+ if (ret)
+ goto out;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvm->d3_wake_sysassert)
+ d3_cfg_cmd_data.wakeup_flags |=
+ cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR);
+#endif
+
+ /* must be last -- this switches firmware state */
+ ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd);
+ if (ret)
+ goto out;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ len = iwl_rx_packet_payload_len(d3_cfg_cmd.resp_pkt);
+ if (len >= sizeof(u32)) {
+ mvm->d3_test_pme_ptr =
+ le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data);
+ }
+#endif
+ iwl_free_resp(&d3_cfg_cmd);
+
+ clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+
+ iwl_trans_d3_suspend(mvm->trans, test);
+ out:
+ if (ret < 0) {
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ ieee80211_restart_hw(mvm->hw);
+ iwl_mvm_free_nd(mvm);
+ }
+ out_noreset:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm)
+{
+ struct iwl_notification_wait wait_d3;
+ static const u16 d3_notif[] = { D3_CONFIG_CMD };
+ int ret;
+
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_d3,
+ d3_notif, ARRAY_SIZE(d3_notif),
+ NULL, NULL);
+
+ ret = iwl_mvm_enter_d0i3(mvm->hw->priv);
+ if (ret)
+ goto remove_notif;
+
+ ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ);
+ WARN_ON_ONCE(ret);
+ return ret;
+
+remove_notif:
+ iwl_remove_notification(&mvm->notif_wait, &wait_d3);
+ return ret;
+}
+
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_trans *trans = mvm->trans;
+ int ret;
+
+ /* make sure the d0i3 exit work is not pending */
+ flush_work(&mvm->d0i3_exit_work);
+
+ ret = iwl_trans_suspend(trans);
+ if (ret)
+ return ret;
+
+ if (wowlan->any) {
+ trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3;
+
+ if (iwl_mvm_enter_d0i3_on_suspend(mvm)) {
+ ret = iwl_mvm_enter_d0i3_sync(mvm);
+
+ if (ret)
+ return ret;
+ }
+
+ mutex_lock(&mvm->d0i3_suspend_mutex);
+ __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+ mutex_unlock(&mvm->d0i3_suspend_mutex);
+
+ iwl_trans_d3_suspend(trans, false);
+
+ return 0;
+ }
+
+ trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
+
+ return __iwl_mvm_suspend(hw, wowlan, false);
+}
+
+/* converted data from the different status responses */
+struct iwl_wowlan_status_data {
+ u16 pattern_number;
+ u16 qos_seq_ctr[8];
+ u32 wakeup_reasons;
+ u32 wake_packet_length;
+ u32 wake_packet_bufsize;
+ const u8 *wake_packet;
+};
+
+static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status_data *status)
+{
+ struct sk_buff *pkt = NULL;
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .pattern_idx = -1,
+ };
+ struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
+ u32 reasons = status->wakeup_reasons;
+
+ if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
+ wakeup_report = NULL;
+ goto report;
+ }
+
+ pm_wakeup_event(mvm->dev, 0);
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET)
+ wakeup.magic_pkt = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN)
+ wakeup.pattern_idx =
+ status->pattern_number;
+
+ if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH))
+ wakeup.disconnect = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)
+ wakeup.gtk_rekey_failure = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
+ wakeup.rfkill_release = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST)
+ wakeup.eap_identity_req = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE)
+ wakeup.four_way_handshake = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS)
+ wakeup.tcp_connlost = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE)
+ wakeup.tcp_nomoretokens = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET)
+ wakeup.tcp_match = true;
+
+ if (status->wake_packet_bufsize) {
+ int pktsize = status->wake_packet_bufsize;
+ int pktlen = status->wake_packet_length;
+ const u8 *pktdata = status->wake_packet;
+ struct ieee80211_hdr *hdr = (void *)pktdata;
+ int truncated = pktlen - pktsize;
+
+ /* this would be a firmware bug */
+ if (WARN_ON_ONCE(truncated < 0))
+ truncated = 0;
+
+ if (ieee80211_is_data(hdr->frame_control)) {
+ int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ int ivlen = 0, icvlen = 4; /* also FCS */
+
+ pkt = alloc_skb(pktsize, GFP_KERNEL);
+ if (!pkt)
+ goto report;
+
+ memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen);
+ pktdata += hdrlen;
+ pktsize -= hdrlen;
+
+ if (ieee80211_has_protected(hdr->frame_control)) {
+ /*
+ * This is unlocked and using gtk_i(c)vlen,
+ * but since everything is under RTNL still
+ * that's not really a problem - changing
+ * it would be difficult.
+ */
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ ivlen = mvm->gtk_ivlen;
+ icvlen += mvm->gtk_icvlen;
+ } else {
+ ivlen = mvm->ptk_ivlen;
+ icvlen += mvm->ptk_icvlen;
+ }
+ }
+
+ /* if truncated, FCS/ICV is (partially) gone */
+ if (truncated >= icvlen) {
+ icvlen = 0;
+ truncated -= icvlen;
+ } else {
+ icvlen -= truncated;
+ truncated = 0;
+ }
+
+ pktsize -= ivlen + icvlen;
+ pktdata += ivlen;
+
+ memcpy(skb_put(pkt, pktsize), pktdata, pktsize);
+
+ if (ieee80211_data_to_8023(pkt, vif->addr, vif->type))
+ goto report;
+ wakeup.packet = pkt->data;
+ wakeup.packet_present_len = pkt->len;
+ wakeup.packet_len = pkt->len - truncated;
+ wakeup.packet_80211 = false;
+ } else {
+ int fcslen = 4;
+
+ if (truncated >= 4) {
+ truncated -= 4;
+ fcslen = 0;
+ } else {
+ fcslen -= truncated;
+ truncated = 0;
+ }
+ pktsize -= fcslen;
+ wakeup.packet = status->wake_packet;
+ wakeup.packet_present_len = pktsize;
+ wakeup.packet_len = pktlen - truncated;
+ wakeup.packet_80211 = true;
+ }
+ }
+
+ report:
+ ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+ kfree_skb(pkt);
+}
+
+static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc,
+ struct ieee80211_key_seq *seq)
+{
+ u64 pn;
+
+ pn = le64_to_cpu(sc->pn);
+ seq->ccmp.pn[0] = pn >> 40;
+ seq->ccmp.pn[1] = pn >> 32;
+ seq->ccmp.pn[2] = pn >> 24;
+ seq->ccmp.pn[3] = pn >> 16;
+ seq->ccmp.pn[4] = pn >> 8;
+ seq->ccmp.pn[5] = pn;
+}
+
+static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc,
+ struct ieee80211_key_seq *seq)
+{
+ seq->tkip.iv32 = le32_to_cpu(sc->iv32);
+ seq->tkip.iv16 = le16_to_cpu(sc->iv16);
+}
+
+static void iwl_mvm_set_aes_rx_seq(struct iwl_mvm *mvm, struct aes_sc *scs,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int tid;
+
+ BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
+
+ if (sta && iwl_mvm_has_new_rx_api(mvm)) {
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_key_pn *ptk_pn;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ ptk_pn = rcu_dereference_protected(mvmsta->ptk_pn[key->keyidx],
+ lockdep_is_held(&mvm->mutex));
+ if (WARN_ON(!ptk_pn))
+ return;
+
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct ieee80211_key_seq seq = {};
+ int i;
+
+ iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ for (i = 1; i < mvm->trans->num_rx_queues; i++)
+ memcpy(ptk_pn->q[i].pn[tid],
+ seq.ccmp.pn, IEEE80211_CCMP_PN_LEN);
+ }
+ } else {
+ for (tid = 0; tid < IWL_NUM_RSC; tid++) {
+ struct ieee80211_key_seq seq = {};
+
+ iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ }
+ }
+}
+
+static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
+ struct ieee80211_key_conf *key)
+{
+ int tid;
+
+ BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
+
+ for (tid = 0; tid < IWL_NUM_RSC; tid++) {
+ struct ieee80211_key_seq seq = {};
+
+ iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ }
+}
+
+static void iwl_mvm_set_key_rx_seq(struct iwl_mvm *mvm,
+ struct ieee80211_key_conf *key,
+ struct iwl_wowlan_status *status)
+{
+ union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ iwl_mvm_set_aes_rx_seq(mvm, rsc->aes.multicast_rsc, NULL, key);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+struct iwl_mvm_d3_gtk_iter_data {
+ struct iwl_mvm *mvm;
+ struct iwl_wowlan_status *status;
+ void *last_gtk;
+ u32 cipher;
+ bool find_phase, unhandled_cipher;
+ int num_keys;
+};
+
+static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mvm_d3_gtk_iter_data *data = _data;
+
+ if (data->unhandled_cipher)
+ return;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* ignore WEP completely, nothing to do */
+ return;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_TKIP:
+ /* we support these */
+ break;
+ default:
+ /* everything else (even CMAC for MFP) - disconnect from AP */
+ data->unhandled_cipher = true;
+ return;
+ }
+
+ data->num_keys++;
+
+ /*
+ * pairwise key - update sequence counters only;
+ * note that this assumes no TDLS sessions are active
+ */
+ if (sta) {
+ struct ieee80211_key_seq seq = {};
+ union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc;
+
+ if (data->find_phase)
+ return;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ iwl_mvm_set_aes_rx_seq(data->mvm, sc->aes.unicast_rsc,
+ sta, key);
+ atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn));
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq);
+ iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key);
+ ieee80211_set_key_tx_seq(key, &seq);
+ break;
+ }
+
+ /* that's it for this key */
+ return;
+ }
+
+ if (data->find_phase) {
+ data->last_gtk = key;
+ data->cipher = key->cipher;
+ return;
+ }
+
+ if (data->status->num_of_gtk_rekeys)
+ ieee80211_remove_key(key);
+ else if (data->last_gtk == key)
+ iwl_mvm_set_key_rx_seq(data->mvm, key, data->status);
+}
+
+static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_d3_gtk_iter_data gtkdata = {
+ .mvm = mvm,
+ .status = status,
+ };
+ u32 disconnection_reasons =
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
+
+ if (!status || !vif->bss_conf.bssid)
+ return false;
+
+ if (le32_to_cpu(status->wakeup_reasons) & disconnection_reasons)
+ return false;
+
+ /* find last GTK that we used initially, if any */
+ gtkdata.find_phase = true;
+ ieee80211_iter_keys(mvm->hw, vif,
+ iwl_mvm_d3_update_keys, &gtkdata);
+ /* not trying to keep connections with MFP/unhandled ciphers */
+ if (gtkdata.unhandled_cipher)
+ return false;
+ if (!gtkdata.num_keys)
+ goto out;
+ if (!gtkdata.last_gtk)
+ return false;
+
+ /*
+ * invalidate all other GTKs that might still exist and update
+ * the one that we used
+ */
+ gtkdata.find_phase = false;
+ ieee80211_iter_keys(mvm->hw, vif,
+ iwl_mvm_d3_update_keys, &gtkdata);
+
+ if (status->num_of_gtk_rekeys) {
+ struct ieee80211_key_conf *key;
+ struct {
+ struct ieee80211_key_conf conf;
+ u8 key[32];
+ } conf = {
+ .conf.cipher = gtkdata.cipher,
+ .conf.keyidx = status->gtk.key_index,
+ };
+
+ switch (gtkdata.cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ conf.conf.keylen = WLAN_KEY_LEN_CCMP;
+ memcpy(conf.conf.key, status->gtk.decrypt_key,
+ WLAN_KEY_LEN_CCMP);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ conf.conf.keylen = WLAN_KEY_LEN_TKIP;
+ memcpy(conf.conf.key, status->gtk.decrypt_key, 16);
+ /* leave TX MIC key zeroed, we don't use it anyway */
+ memcpy(conf.conf.key +
+ NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+ status->gtk.tkip_mic_key, 8);
+ break;
+ }
+
+ key = ieee80211_gtk_rekey_add(vif, &conf.conf);
+ if (IS_ERR(key))
+ return false;
+ iwl_mvm_set_key_rx_seq(mvm, key, status);
+ }
+
+ if (status->num_of_gtk_rekeys) {
+ __be64 replay_ctr =
+ cpu_to_be64(le64_to_cpu(status->replay_ctr));
+ ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
+ (void *)&replay_ctr, GFP_KERNEL);
+ }
+
+out:
+ mvmvif->seqno_valid = true;
+ /* +0x10 because the set API expects next-to-use, not last-used */
+ mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10;
+
+ return true;
+}
+
+static struct iwl_wowlan_status *
+iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ u32 base = mvm->error_event_table;
+ struct error_table_start {
+ /* cf. struct iwl_error_event_table */
+ u32 valid;
+ u32 error_id;
+ } err_info;
+ struct iwl_host_cmd cmd = {
+ .id = WOWLAN_GET_STATUSES,
+ .flags = CMD_WANT_SKB,
+ };
+ struct iwl_wowlan_status *status, *fw_status;
+ int ret, len, status_size;
+
+ iwl_trans_read_mem_bytes(mvm->trans, base,
+ &err_info, sizeof(err_info));
+
+ if (err_info.valid) {
+ IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n",
+ err_info.valid, err_info.error_id);
+ if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .rfkill_release = true,
+ };
+ ieee80211_report_wowlan_wakeup(vif, &wakeup,
+ GFP_KERNEL);
+ }
+ return ERR_PTR(-EIO);
+ }
+
+ /* only for tracing for now */
+ ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL);
+ if (ret)
+ IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret) {
+ IWL_ERR(mvm, "failed to query status (%d)\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ /* RF-kill already asserted again... */
+ if (!cmd.resp_pkt) {
+ fw_status = ERR_PTR(-ERFKILL);
+ goto out_free_resp;
+ }
+
+ status_size = sizeof(*fw_status);
+
+ len = iwl_rx_packet_payload_len(cmd.resp_pkt);
+ if (len < status_size) {
+ IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+ fw_status = ERR_PTR(-EIO);
+ goto out_free_resp;
+ }
+
+ status = (void *)cmd.resp_pkt->data;
+ if (len != (status_size +
+ ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) {
+ IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+ fw_status = ERR_PTR(-EIO);
+ goto out_free_resp;
+ }
+
+ fw_status = kmemdup(status, len, GFP_KERNEL);
+
+out_free_resp:
+ iwl_free_resp(&cmd);
+ return fw_status;
+}
+
+/* releases the MVM mutex */
+static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_wowlan_status_data status;
+ struct iwl_wowlan_status *fw_status;
+ int i;
+ bool keep;
+ struct ieee80211_sta *ap_sta;
+ struct iwl_mvm_sta *mvm_ap_sta;
+
+ fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+ if (IS_ERR_OR_NULL(fw_status))
+ goto out_unlock;
+
+ status.pattern_number = le16_to_cpu(fw_status->pattern_number);
+ for (i = 0; i < 8; i++)
+ status.qos_seq_ctr[i] =
+ le16_to_cpu(fw_status->qos_seq_ctr[i]);
+ status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons);
+ status.wake_packet_length =
+ le32_to_cpu(fw_status->wake_packet_length);
+ status.wake_packet_bufsize =
+ le32_to_cpu(fw_status->wake_packet_bufsize);
+ status.wake_packet = fw_status->wake_packet;
+
+ /* still at hard-coded place 0 for D3 image */
+ ap_sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[0],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(ap_sta))
+ goto out_free;
+
+ mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ u16 seq = status.qos_seq_ctr[i];
+ /* firmware stores last-used value, we store next value */
+ seq += 0x10;
+ mvm_ap_sta->tid_data[i].seq_number = seq;
+ }
+
+ /* now we have all the data we need, unlock to avoid mac80211 issues */
+ mutex_unlock(&mvm->mutex);
+
+ iwl_mvm_report_wakeup_reasons(mvm, vif, &status);
+
+ keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status);
+
+ kfree(fw_status);
+ return keep;
+
+out_free:
+ kfree(fw_status);
+out_unlock:
+ mutex_unlock(&mvm->mutex);
+ return false;
+}
+
+void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status)
+{
+ struct iwl_mvm_d3_gtk_iter_data gtkdata = {
+ .mvm = mvm,
+ .status = status,
+ };
+
+ /*
+ * rekey handling requires taking locks that can't be taken now.
+ * however, d0i3 doesn't offload rekey, so we're fine.
+ */
+ if (WARN_ON_ONCE(status->num_of_gtk_rekeys))
+ return;
+
+ /* find last GTK that we used initially, if any */
+ gtkdata.find_phase = true;
+ iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, &gtkdata);
+
+ gtkdata.find_phase = false;
+ iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, &gtkdata);
+}
+
+struct iwl_mvm_nd_query_results {
+ u32 matched_profiles;
+ struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+};
+
+static int
+iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
+ struct iwl_mvm_nd_query_results *results)
+{
+ struct iwl_scan_offload_profiles_query *query;
+ struct iwl_host_cmd cmd = {
+ .id = SCAN_OFFLOAD_PROFILES_QUERY_CMD,
+ .flags = CMD_WANT_SKB,
+ };
+ int ret, len;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret) {
+ IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret);
+ return ret;
+ }
+
+ /* RF-kill already asserted again... */
+ if (!cmd.resp_pkt) {
+ ret = -ERFKILL;
+ goto out_free_resp;
+ }
+
+ len = iwl_rx_packet_payload_len(cmd.resp_pkt);
+ if (len < sizeof(*query)) {
+ IWL_ERR(mvm, "Invalid scan offload profiles query response!\n");
+ ret = -EIO;
+ goto out_free_resp;
+ }
+
+ query = (void *)cmd.resp_pkt->data;
+
+ results->matched_profiles = le32_to_cpu(query->matched_profiles);
+ memcpy(results->matches, query->matches, sizeof(results->matches));
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done);
+#endif
+
+out_free_resp:
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct cfg80211_wowlan_nd_info *net_detect = NULL;
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .pattern_idx = -1,
+ };
+ struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
+ struct iwl_mvm_nd_query_results query;
+ struct iwl_wowlan_status *fw_status;
+ unsigned long matched_profiles;
+ u32 reasons = 0;
+ int i, j, n_matches, ret;
+
+ fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+ if (!IS_ERR_OR_NULL(fw_status)) {
+ reasons = le32_to_cpu(fw_status->wakeup_reasons);
+ kfree(fw_status);
+ }
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
+ wakeup.rfkill_release = true;
+
+ if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS)
+ goto out;
+
+ ret = iwl_mvm_netdetect_query_results(mvm, &query);
+ if (ret || !query.matched_profiles) {
+ wakeup_report = NULL;
+ goto out;
+ }
+
+ matched_profiles = query.matched_profiles;
+ if (mvm->n_nd_match_sets) {
+ n_matches = hweight_long(matched_profiles);
+ } else {
+ IWL_ERR(mvm, "no net detect match information available\n");
+ n_matches = 0;
+ }
+
+ net_detect = kzalloc(sizeof(*net_detect) +
+ (n_matches * sizeof(net_detect->matches[0])),
+ GFP_KERNEL);
+ if (!net_detect || !n_matches)
+ goto out_report_nd;
+
+ for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) {
+ struct iwl_scan_offload_profile_match *fw_match;
+ struct cfg80211_wowlan_nd_match *match;
+ int idx, n_channels = 0;
+
+ fw_match = &query.matches[i];
+
+ for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++)
+ n_channels += hweight8(fw_match->matching_channels[j]);
+
+ match = kzalloc(sizeof(*match) +
+ (n_channels * sizeof(*match->channels)),
+ GFP_KERNEL);
+ if (!match)
+ goto out_report_nd;
+
+ net_detect->matches[net_detect->n_matches++] = match;
+
+ /* We inverted the order of the SSIDs in the scan
+ * request, so invert the index here.
+ */
+ idx = mvm->n_nd_match_sets - i - 1;
+ match->ssid.ssid_len = mvm->nd_match_sets[idx].ssid.ssid_len;
+ memcpy(match->ssid.ssid, mvm->nd_match_sets[idx].ssid.ssid,
+ match->ssid.ssid_len);
+
+ if (mvm->n_nd_channels < n_channels)
+ continue;
+
+ for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++)
+ if (fw_match->matching_channels[j / 8] & (BIT(j % 8)))
+ match->channels[match->n_channels++] =
+ mvm->nd_channels[j]->center_freq;
+ }
+
+out_report_nd:
+ wakeup.net_detect = net_detect;
+out:
+ iwl_mvm_free_nd(mvm);
+
+ mutex_unlock(&mvm->mutex);
+ ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+
+ if (net_detect) {
+ for (i = 0; i < net_detect->n_matches; i++)
+ kfree(net_detect->matches[i]);
+ kfree(net_detect);
+ }
+}
+
+static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
+{
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+ u32 len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+
+ if (!mvm->store_d3_resume_sram)
+ return;
+
+ if (!mvm->d3_resume_sram) {
+ mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL);
+ if (!mvm->d3_resume_sram)
+ return;
+ }
+
+ iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len);
+#endif
+}
+
+static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ /* skip the one we keep connection on */
+ if (data == vif)
+ return;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ ieee80211_resume_disconnect(vif);
+}
+
+static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
+{
+ struct ieee80211_vif *vif = NULL;
+ int ret;
+ enum iwl_d3_status d3_status;
+ bool keep = false;
+
+ mutex_lock(&mvm->mutex);
+
+ /* get the BSS vif pointer again */
+ vif = iwl_mvm_get_bss_vif(mvm);
+ if (IS_ERR_OR_NULL(vif))
+ goto err;
+
+ ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test);
+ if (ret)
+ goto err;
+
+ if (d3_status != IWL_D3_STATUS_ALIVE) {
+ IWL_INFO(mvm, "Device was reset during suspend\n");
+ goto err;
+ }
+
+ /* query SRAM first in case we want event logging */
+ iwl_mvm_read_d3_sram(mvm);
+
+ /*
+ * Query the current location and source from the D3 firmware so we
+ * can play it back when we re-intiailize the D0 firmware
+ */
+ iwl_mvm_update_changed_regdom(mvm);
+
+ if (mvm->net_detect) {
+ iwl_mvm_query_netdetect_reasons(mvm, vif);
+ /* has unlocked the mutex, so skip that */
+ goto out;
+ } else {
+ keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (keep)
+ mvm->keep_vif = vif;
+#endif
+ /* has unlocked the mutex, so skip that */
+ goto out_iterate;
+ }
+
+err:
+ iwl_mvm_free_nd(mvm);
+ mutex_unlock(&mvm->mutex);
+
+out_iterate:
+ if (!test)
+ ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
+
+out:
+ /* return 1 to reconfigure the device */
+ set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+ set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
+
+ /* We always return 1, which causes mac80211 to do a reconfig
+ * with IEEE80211_RECONFIG_TYPE_RESTART. This type of
+ * reconfig calls iwl_mvm_restart_complete(), where we unref
+ * the IWL_MVM_REF_UCODE_DOWN, so we need to take the
+ * reference here.
+ */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ return 1;
+}
+
+static int iwl_mvm_resume_d3(struct iwl_mvm *mvm)
+{
+ iwl_trans_resume(mvm->trans);
+
+ return __iwl_mvm_resume(mvm, false);
+}
+
+static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
+{
+ bool exit_now;
+ enum iwl_d3_status d3_status;
+ struct iwl_trans *trans = mvm->trans;
+
+ iwl_trans_d3_resume(trans, &d3_status, false);
+
+ /*
+ * make sure to clear D0I3_DEFER_WAKEUP before
+ * calling iwl_trans_resume(), which might wait
+ * for d0i3 exit completion.
+ */
+ mutex_lock(&mvm->d0i3_suspend_mutex);
+ __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+ exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
+ &mvm->d0i3_suspend_flags);
+ mutex_unlock(&mvm->d0i3_suspend_mutex);
+ if (exit_now) {
+ IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
+ _iwl_mvm_exit_d0i3(mvm);
+ }
+
+ iwl_trans_resume(trans);
+
+ if (iwl_mvm_enter_d0i3_on_suspend(mvm)) {
+ int ret = iwl_mvm_exit_d0i3(mvm->hw->priv);
+
+ if (ret)
+ return ret;
+ /*
+ * d0i3 exit will be deferred until reconfig_complete.
+ * make sure there we are out of d0i3.
+ */
+ }
+ return 0;
+}
+
+int iwl_mvm_resume(struct ieee80211_hw *hw)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ if (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3)
+ ret = iwl_mvm_resume_d0i3(mvm);
+ else
+ ret = iwl_mvm_resume_d3(mvm);
+
+ mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
+
+ return ret;
+}
+
+void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ device_set_wakeup_enable(mvm->trans->dev, enabled);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
+{
+ struct iwl_mvm *mvm = inode->i_private;
+ int err;
+
+ if (mvm->d3_test_active)
+ return -EBUSY;
+
+ file->private_data = inode->i_private;
+
+ ieee80211_stop_queues(mvm->hw);
+ synchronize_net();
+
+ mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
+
+ /* start pseudo D3 */
+ rtnl_lock();
+ err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
+ rtnl_unlock();
+ if (err > 0)
+ err = -EINVAL;
+ if (err) {
+ ieee80211_wake_queues(mvm->hw);
+ return err;
+ }
+ mvm->d3_test_active = true;
+ mvm->keep_vif = NULL;
+ return 0;
+}
+
+static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ u32 pme_asserted;
+
+ while (true) {
+ /* read pme_ptr if available */
+ if (mvm->d3_test_pme_ptr) {
+ pme_asserted = iwl_trans_read_mem32(mvm->trans,
+ mvm->d3_test_pme_ptr);
+ if (pme_asserted)
+ break;
+ }
+
+ if (msleep_interruptible(100))
+ break;
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ /* skip the one we keep connection on */
+ if (_data == vif)
+ return;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ ieee80211_connection_loss(vif);
+}
+
+static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
+{
+ struct iwl_mvm *mvm = inode->i_private;
+ int remaining_time = 10;
+
+ mvm->d3_test_active = false;
+
+ rtnl_lock();
+ __iwl_mvm_resume(mvm, true);
+ rtnl_unlock();
+
+ mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
+
+ iwl_abort_notification_waits(&mvm->notif_wait);
+ ieee80211_restart_hw(mvm->hw);
+
+ /* wait for restart and disconnect all interfaces */
+ while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ remaining_time > 0) {
+ remaining_time--;
+ msleep(1000);
+ }
+
+ if (remaining_time == 0)
+ IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n");
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif);
+
+ ieee80211_wake_queues(mvm->hw);
+
+ return 0;
+}
+
+const struct file_operations iwl_dbgfs_d3_test_ops = {
+ .llseek = no_llseek,
+ .open = iwl_mvm_d3_test_open,
+ .read = iwl_mvm_d3_test_read,
+ .release = iwl_mvm_d3_test_release,
+};
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
new file mode 100644
index 000000000000..9e0d46368cdd
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
@@ -0,0 +1,1483 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "mvm.h"
+#include "fw-api-tof.h"
+#include "debugfs.h"
+
+static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ enum iwl_dbgfs_pm_mask param, int val)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
+
+ dbgfs_pm->mask |= param;
+
+ switch (param) {
+ case MVM_DEBUGFS_PM_KEEP_ALIVE: {
+ int dtimper = vif->bss_conf.dtim_period ?: 1;
+ int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+ IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
+ if (val * MSEC_PER_SEC < 3 * dtimper_msec)
+ IWL_WARN(mvm,
+ "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
+ val * MSEC_PER_SEC, 3 * dtimper_msec);
+ dbgfs_pm->keep_alive_seconds = val;
+ break;
+ }
+ case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
+ IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
+ val ? "enabled" : "disabled");
+ dbgfs_pm->skip_over_dtim = val;
+ break;
+ case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
+ IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
+ dbgfs_pm->skip_dtim_periods = val;
+ break;
+ case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
+ IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
+ dbgfs_pm->rx_data_timeout = val;
+ break;
+ case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
+ IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
+ dbgfs_pm->tx_data_timeout = val;
+ break;
+ case MVM_DEBUGFS_PM_LPRX_ENA:
+ IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
+ dbgfs_pm->lprx_ena = val;
+ break;
+ case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
+ IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
+ dbgfs_pm->lprx_rssi_threshold = val;
+ break;
+ case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
+ IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
+ dbgfs_pm->snooze_ena = val;
+ break;
+ case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
+ IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
+ dbgfs_pm->uapsd_misbehaving = val;
+ break;
+ case MVM_DEBUGFS_PM_USE_PS_POLL:
+ IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val);
+ dbgfs_pm->use_ps_poll = val;
+ break;
+ }
+}
+
+static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ enum iwl_dbgfs_pm_mask param;
+ int val, ret;
+
+ if (!strncmp("keep_alive=", buf, 11)) {
+ if (sscanf(buf + 11, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_KEEP_ALIVE;
+ } else if (!strncmp("skip_over_dtim=", buf, 15)) {
+ if (sscanf(buf + 15, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
+ } else if (!strncmp("skip_dtim_periods=", buf, 18)) {
+ if (sscanf(buf + 18, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
+ } else if (!strncmp("rx_data_timeout=", buf, 16)) {
+ if (sscanf(buf + 16, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
+ } else if (!strncmp("tx_data_timeout=", buf, 16)) {
+ if (sscanf(buf + 16, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
+ } else if (!strncmp("lprx=", buf, 5)) {
+ if (sscanf(buf + 5, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_LPRX_ENA;
+ } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
+ if (sscanf(buf + 20, "%d", &val) != 1)
+ return -EINVAL;
+ if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
+ POWER_LPRX_RSSI_THRESHOLD_MIN)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
+ } else if (!strncmp("snooze_enable=", buf, 14)) {
+ if (sscanf(buf + 14, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
+ } else if (!strncmp("uapsd_misbehaving=", buf, 18)) {
+ if (sscanf(buf + 18, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
+ } else if (!strncmp("use_ps_poll=", buf, 12)) {
+ if (sscanf(buf + 12, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_USE_PS_POLL;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&mvm->mutex);
+ iwl_dbgfs_update_pm(mvm, vif, param, val);
+ ret = iwl_mvm_power_update_mac(mvm);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ char buf[64];
+ int bufsz = sizeof(buf);
+ int pos;
+
+ pos = scnprintf(buf, bufsz, "bss limit = %d\n",
+ vif->bss_conf.txpower);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ char buf[512];
+ int bufsz = sizeof(buf);
+ int pos;
+
+ pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u8 ap_sta_id;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ char buf[512];
+ int bufsz = sizeof(buf);
+ int pos = 0;
+ int i;
+
+ mutex_lock(&mvm->mutex);
+
+ ap_sta_id = mvmvif->ap_sta_id;
+
+ switch (ieee80211_vif_type_p2p(vif)) {
+ case NL80211_IFTYPE_ADHOC:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
+ break;
+ case NL80211_IFTYPE_STATION:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
+ break;
+ case NL80211_IFTYPE_AP:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
+ break;
+ default:
+ break;
+ }
+
+ pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
+ mvmvif->id, mvmvif->color);
+ pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
+ vif->bss_conf.bssid);
+ pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
+ for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++)
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n",
+ i, mvmvif->queue_params[i].txop,
+ mvmvif->queue_params[i].cw_min,
+ mvmvif->queue_params[i].cw_max,
+ mvmvif->queue_params[i].aifs,
+ mvmvif->queue_params[i].uapsd);
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ ap_sta_id != IWL_MVM_STATION_COUNT) {
+ struct ieee80211_sta *sta;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (!IS_ERR_OR_NULL(sta)) {
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "ap_sta_id %d - reduced Tx power %d\n",
+ ap_sta_id,
+ mvm_sta->bt_reduced_txpower);
+ }
+ }
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ if (chanctx_conf)
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "idle rx chains %d, active rx chains: %d\n",
+ chanctx_conf->rx_chains_static,
+ chanctx_conf->rx_chains_dynamic);
+ rcu_read_unlock();
+
+ mutex_unlock(&mvm->mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
+ enum iwl_dbgfs_bf_mask param, int value)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+ dbgfs_bf->mask |= param;
+
+ switch (param) {
+ case MVM_DEBUGFS_BF_ENERGY_DELTA:
+ dbgfs_bf->bf_energy_delta = value;
+ break;
+ case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
+ dbgfs_bf->bf_roaming_energy_delta = value;
+ break;
+ case MVM_DEBUGFS_BF_ROAMING_STATE:
+ dbgfs_bf->bf_roaming_state = value;
+ break;
+ case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
+ dbgfs_bf->bf_temp_threshold = value;
+ break;
+ case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
+ dbgfs_bf->bf_temp_fast_filter = value;
+ break;
+ case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
+ dbgfs_bf->bf_temp_slow_filter = value;
+ break;
+ case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
+ dbgfs_bf->bf_enable_beacon_filter = value;
+ break;
+ case MVM_DEBUGFS_BF_DEBUG_FLAG:
+ dbgfs_bf->bf_debug_flag = value;
+ break;
+ case MVM_DEBUGFS_BF_ESCAPE_TIMER:
+ dbgfs_bf->bf_escape_timer = value;
+ break;
+ case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
+ dbgfs_bf->ba_enable_beacon_abort = value;
+ break;
+ case MVM_DEBUGFS_BA_ESCAPE_TIMER:
+ dbgfs_bf->ba_escape_timer = value;
+ break;
+ }
+}
+
+static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ enum iwl_dbgfs_bf_mask param;
+ int value, ret = 0;
+
+ if (!strncmp("bf_energy_delta=", buf, 16)) {
+ if (sscanf(buf+16, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ENERGY_DELTA_MIN ||
+ value > IWL_BF_ENERGY_DELTA_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ENERGY_DELTA;
+ } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
+ if (sscanf(buf+24, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
+ value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
+ } else if (!strncmp("bf_roaming_state=", buf, 17)) {
+ if (sscanf(buf+17, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ROAMING_STATE_MIN ||
+ value > IWL_BF_ROAMING_STATE_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ROAMING_STATE;
+ } else if (!strncmp("bf_temp_threshold=", buf, 18)) {
+ if (sscanf(buf+18, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
+ value > IWL_BF_TEMP_THRESHOLD_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
+ } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
+ if (sscanf(buf+20, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
+ value > IWL_BF_TEMP_FAST_FILTER_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
+ } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
+ if (sscanf(buf+20, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
+ value > IWL_BF_TEMP_SLOW_FILTER_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
+ } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
+ if (sscanf(buf+24, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < 0 || value > 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
+ } else if (!strncmp("bf_debug_flag=", buf, 14)) {
+ if (sscanf(buf+14, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < 0 || value > 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_DEBUG_FLAG;
+ } else if (!strncmp("bf_escape_timer=", buf, 16)) {
+ if (sscanf(buf+16, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ESCAPE_TIMER_MIN ||
+ value > IWL_BF_ESCAPE_TIMER_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
+ } else if (!strncmp("ba_escape_timer=", buf, 16)) {
+ if (sscanf(buf+16, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BA_ESCAPE_TIMER_MIN ||
+ value > IWL_BA_ESCAPE_TIMER_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
+ } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
+ if (sscanf(buf+23, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < 0 || value > 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&mvm->mutex);
+ iwl_dbgfs_update_bf(vif, param, value);
+ if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ else
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[256];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ struct iwl_beacon_filter_cmd cmd = {
+ IWL_BF_CMD_CONFIG_DEFAULTS,
+ .bf_enable_beacon_filter =
+ cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
+ .ba_enable_beacon_abort =
+ cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
+ };
+
+ iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+ if (mvmvif->bf_data.bf_enabled)
+ cmd.bf_enable_beacon_filter = cpu_to_le32(1);
+ else
+ cmd.bf_enable_beacon_filter = 0;
+
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
+ le32_to_cpu(cmd.bf_energy_delta));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
+ le32_to_cpu(cmd.bf_roaming_energy_delta));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
+ le32_to_cpu(cmd.bf_roaming_state));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
+ le32_to_cpu(cmd.bf_temp_threshold));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
+ le32_to_cpu(cmd.bf_temp_fast_filter));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
+ le32_to_cpu(cmd.bf_temp_slow_filter));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
+ le32_to_cpu(cmd.bf_enable_beacon_filter));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
+ le32_to_cpu(cmd.bf_debug_flag));
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
+ le32_to_cpu(cmd.bf_escape_timer));
+ pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
+ le32_to_cpu(cmd.ba_escape_timer));
+ pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
+ le32_to_cpu(cmd.ba_enable_beacon_abort));
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static inline char *iwl_dbgfs_is_match(char *name, char *buf)
+{
+ int len = strlen(name);
+
+ return !strncmp(name, buf, len) ? buf + len : NULL;
+}
+
+static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif,
+ char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u32 value;
+ int ret = -EINVAL;
+ char *data;
+
+ mutex_lock(&mvm->mutex);
+
+ data = iwl_dbgfs_is_match("tof_disabled=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.tof_cfg.tof_disabled = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("one_sided_disabled=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.tof_cfg.one_sided_disabled = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("is_debug_mode=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.tof_cfg.is_debug_mode = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("is_buf=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.tof_cfg.is_buf_required = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("send_tof_cfg=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0 && value) {
+ ret = iwl_mvm_tof_config_cmd(mvm);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_tof_enable_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ char buf[256];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ struct iwl_tof_config_cmd *cmd;
+
+ cmd = &mvm->tof_data.tof_cfg;
+
+ mutex_lock(&mvm->mutex);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "tof_disabled = %d\n",
+ cmd->tof_disabled);
+ pos += scnprintf(buf + pos, bufsz - pos, "one_sided_disabled = %d\n",
+ cmd->one_sided_disabled);
+ pos += scnprintf(buf + pos, bufsz - pos, "is_debug_mode = %d\n",
+ cmd->is_debug_mode);
+ pos += scnprintf(buf + pos, bufsz - pos, "is_buf_required = %d\n",
+ cmd->is_buf_required);
+
+ mutex_unlock(&mvm->mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif,
+ char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u32 value;
+ int ret = 0;
+ char *data;
+
+ mutex_lock(&mvm->mutex);
+
+ data = iwl_dbgfs_is_match("burst_period=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (!ret)
+ mvm->tof_data.responder_cfg.burst_period =
+ cpu_to_le16(value);
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.min_delta_ftm = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("burst_duration=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.burst_duration = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("num_of_burst_exp=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.num_of_burst_exp = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("abort_responder=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.abort_responder = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("get_ch_est=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.get_ch_est = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("recv_sta_req_params=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.recv_sta_req_params = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("channel_num=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.channel_num = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("bandwidth=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.bandwidth = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("rate=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.rate = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("bssid=", buf);
+ if (data) {
+ u8 *mac = mvm->tof_data.responder_cfg.bssid;
+
+ if (!mac_pton(data, mac)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ data = iwl_dbgfs_is_match("tsf_timer_offset_msecs=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.tsf_timer_offset_msecs =
+ cpu_to_le16(value);
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("toa_offset=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.toa_offset =
+ cpu_to_le16(value);
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("center_freq=", buf);
+ if (data) {
+ struct iwl_tof_responder_config_cmd *cmd =
+ &mvm->tof_data.responder_cfg;
+
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0 && value) {
+ enum ieee80211_band band = (cmd->channel_num <= 14) ?
+ IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ;
+ struct ieee80211_channel chn = {
+ .band = band,
+ .center_freq = ieee80211_channel_to_frequency(
+ cmd->channel_num, band),
+ };
+ struct cfg80211_chan_def chandef = {
+ .chan = &chn,
+ .center_freq1 =
+ ieee80211_channel_to_frequency(value,
+ band),
+ };
+
+ cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef);
+ }
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("ftm_per_burst=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.ftm_per_burst = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("ftm_resp_ts_avail=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.ftm_resp_ts_avail = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("asap_mode=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.responder_cfg.asap_mode = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("send_responder_cfg=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0 && value) {
+ ret = iwl_mvm_tof_responder_cmd(mvm, vif);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_tof_responder_params_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ char buf[256];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ struct iwl_tof_responder_config_cmd *cmd;
+
+ cmd = &mvm->tof_data.responder_cfg;
+
+ mutex_lock(&mvm->mutex);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "burst_period = %d\n",
+ le16_to_cpu(cmd->burst_period));
+ pos += scnprintf(buf + pos, bufsz - pos, "burst_duration = %d\n",
+ cmd->burst_duration);
+ pos += scnprintf(buf + pos, bufsz - pos, "bandwidth = %d\n",
+ cmd->bandwidth);
+ pos += scnprintf(buf + pos, bufsz - pos, "channel_num = %d\n",
+ cmd->channel_num);
+ pos += scnprintf(buf + pos, bufsz - pos, "ctrl_ch_position = 0x%x\n",
+ cmd->ctrl_ch_position);
+ pos += scnprintf(buf + pos, bufsz - pos, "bssid = %pM\n",
+ cmd->bssid);
+ pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %d\n",
+ cmd->min_delta_ftm);
+ pos += scnprintf(buf + pos, bufsz - pos, "num_of_burst_exp = %d\n",
+ cmd->num_of_burst_exp);
+ pos += scnprintf(buf + pos, bufsz - pos, "rate = %d\n", cmd->rate);
+ pos += scnprintf(buf + pos, bufsz - pos, "abort_responder = %d\n",
+ cmd->abort_responder);
+ pos += scnprintf(buf + pos, bufsz - pos, "get_ch_est = %d\n",
+ cmd->get_ch_est);
+ pos += scnprintf(buf + pos, bufsz - pos, "recv_sta_req_params = %d\n",
+ cmd->recv_sta_req_params);
+ pos += scnprintf(buf + pos, bufsz - pos, "ftm_per_burst = %d\n",
+ cmd->ftm_per_burst);
+ pos += scnprintf(buf + pos, bufsz - pos, "ftm_resp_ts_avail = %d\n",
+ cmd->ftm_resp_ts_avail);
+ pos += scnprintf(buf + pos, bufsz - pos, "asap_mode = %d\n",
+ cmd->asap_mode);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "tsf_timer_offset_msecs = %d\n",
+ le16_to_cpu(cmd->tsf_timer_offset_msecs));
+ pos += scnprintf(buf + pos, bufsz - pos, "toa_offset = %d\n",
+ le16_to_cpu(cmd->toa_offset));
+
+ mutex_unlock(&mvm->mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u32 value;
+ int ret = 0;
+ char *data;
+
+ mutex_lock(&mvm->mutex);
+
+ data = iwl_dbgfs_is_match("request_id=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req.request_id = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("initiator=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req.initiator = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("one_sided_los_disable=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req.one_sided_los_disable = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("req_timeout=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req.req_timeout = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("report_policy=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req.report_policy = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("macaddr_random=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req.macaddr_random = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("num_of_ap=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req.num_of_ap = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("macaddr_template=", buf);
+ if (data) {
+ u8 mac[ETH_ALEN];
+
+ if (!mac_pton(data, mac)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN);
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("macaddr_mask=", buf);
+ if (data) {
+ u8 mac[ETH_ALEN];
+
+ if (!mac_pton(data, mac)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN);
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("ap=", buf);
+ if (data) {
+ struct iwl_tof_range_req_ap_entry ap = {};
+ int size = sizeof(struct iwl_tof_range_req_ap_entry);
+ u16 burst_period;
+ u8 *mac = ap.bssid;
+ unsigned int i;
+
+ if (sscanf(data, "%u %hhd %hhd %hhd"
+ "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"
+ "%hhd %hhd %hd"
+ "%hhd %hhd %d"
+ "%hhx %hhd %hhd %hhd",
+ &i, &ap.channel_num, &ap.bandwidth,
+ &ap.ctrl_ch_position,
+ mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5,
+ &ap.measure_type, &ap.num_of_bursts,
+ &burst_period,
+ &ap.samples_per_burst, &ap.retries_per_sample,
+ &ap.tsf_delta, &ap.location_req, &ap.asap_mode,
+ &ap.enable_dyn_ack, &ap.rssi) != 20) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (i >= IWL_MVM_TOF_MAX_APS) {
+ IWL_ERR(mvm, "Invalid AP index %d\n", i);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ap.burst_period = cpu_to_le16(burst_period);
+
+ memcpy(&mvm->tof_data.range_req.ap[i], &ap, size);
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("send_range_request=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0 && value)
+ ret = iwl_mvm_tof_range_request_cmd(mvm, vif);
+ goto out;
+ }
+
+ ret = -EINVAL;
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ char buf[512];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ struct iwl_tof_range_req_cmd *cmd;
+ int i;
+
+ cmd = &mvm->tof_data.range_req;
+
+ mutex_lock(&mvm->mutex);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "request_id= %d\n",
+ cmd->request_id);
+ pos += scnprintf(buf + pos, bufsz - pos, "initiator= %d\n",
+ cmd->initiator);
+ pos += scnprintf(buf + pos, bufsz - pos, "one_sided_los_disable = %d\n",
+ cmd->one_sided_los_disable);
+ pos += scnprintf(buf + pos, bufsz - pos, "req_timeout= %d\n",
+ cmd->req_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos, "report_policy= %d\n",
+ cmd->report_policy);
+ pos += scnprintf(buf + pos, bufsz - pos, "macaddr_random= %d\n",
+ cmd->macaddr_random);
+ pos += scnprintf(buf + pos, bufsz - pos, "macaddr_template= %pM\n",
+ cmd->macaddr_template);
+ pos += scnprintf(buf + pos, bufsz - pos, "macaddr_mask= %pM\n",
+ cmd->macaddr_mask);
+ pos += scnprintf(buf + pos, bufsz - pos, "num_of_ap= %d\n",
+ cmd->num_of_ap);
+ for (i = 0; i < cmd->num_of_ap; i++) {
+ struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i];
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ap %.2d: channel_num=%hhd bw=%hhd"
+ " control=%hhd bssid=%pM type=%hhd"
+ " num_of_bursts=%hhd burst_period=%hd ftm=%hhd"
+ " retries=%hhd tsf_delta=%d"
+ " tsf_delta_direction=%hhd location_req=0x%hhx "
+ " asap=%hhd enable=%hhd rssi=%hhd\n",
+ i, ap->channel_num, ap->bandwidth,
+ ap->ctrl_ch_position, ap->bssid,
+ ap->measure_type, ap->num_of_bursts,
+ ap->burst_period, ap->samples_per_burst,
+ ap->retries_per_sample, ap->tsf_delta,
+ ap->tsf_delta_direction,
+ ap->location_req, ap->asap_mode,
+ ap->enable_dyn_ack, ap->rssi);
+ }
+
+ mutex_unlock(&mvm->mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif,
+ char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u32 value;
+ int ret = 0;
+ char *data;
+
+ mutex_lock(&mvm->mutex);
+
+ data = iwl_dbgfs_is_match("tsf_timer_offset_msec=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req_ext.tsf_timer_offset_msec =
+ cpu_to_le16(value);
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req_ext.min_delta_ftm = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("ftm_format_and_bw20M=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req_ext.ftm_format_and_bw20M =
+ value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("ftm_format_and_bw40M=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req_ext.ftm_format_and_bw40M =
+ value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("ftm_format_and_bw80M=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.range_req_ext.ftm_format_and_bw80M =
+ value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("send_range_req_ext=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0 && value)
+ ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif);
+ goto out;
+ }
+
+ ret = -EINVAL;
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ char buf[256];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ struct iwl_tof_range_req_ext_cmd *cmd;
+
+ cmd = &mvm->tof_data.range_req_ext;
+
+ mutex_lock(&mvm->mutex);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "tsf_timer_offset_msec = %hd\n",
+ cmd->tsf_timer_offset_msec);
+ pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n",
+ cmd->min_delta_ftm);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ftm_format_and_bw20M = %hhd\n",
+ cmd->ftm_format_and_bw20M);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ftm_format_and_bw40M = %hhd\n",
+ cmd->ftm_format_and_bw40M);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ftm_format_and_bw80M = %hhd\n",
+ cmd->ftm_format_and_bw80M);
+
+ mutex_unlock(&mvm->mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif,
+ char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u32 value;
+ int abort_id, ret = 0;
+ char *data;
+
+ mutex_lock(&mvm->mutex);
+
+ data = iwl_dbgfs_is_match("abort_id=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0)
+ mvm->tof_data.last_abort_id = value;
+ goto out;
+ }
+
+ data = iwl_dbgfs_is_match("send_range_abort=", buf);
+ if (data) {
+ ret = kstrtou32(data, 10, &value);
+ if (ret == 0 && value) {
+ abort_id = mvm->tof_data.last_abort_id;
+ ret = iwl_mvm_tof_range_abort_cmd(mvm, abort_id);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_tof_range_abort_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ char buf[32];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ int last_abort_id;
+
+ mutex_lock(&mvm->mutex);
+ last_abort_id = mvm->tof_data.last_abort_id;
+ mutex_unlock(&mvm->mutex);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "last_abort_id = %d\n",
+ last_abort_id);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ char *buf;
+ int pos = 0;
+ const size_t bufsz = sizeof(struct iwl_tof_range_rsp_ntfy) + 256;
+ struct iwl_tof_range_rsp_ntfy *cmd;
+ int i, ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+ cmd = &mvm->tof_data.range_resp;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "request_id = %d\n",
+ cmd->request_id);
+ pos += scnprintf(buf + pos, bufsz - pos, "status = %d\n",
+ cmd->request_status);
+ pos += scnprintf(buf + pos, bufsz - pos, "last_in_batch = %d\n",
+ cmd->last_in_batch);
+ pos += scnprintf(buf + pos, bufsz - pos, "num_of_aps = %d\n",
+ cmd->num_of_aps);
+ for (i = 0; i < cmd->num_of_aps; i++) {
+ struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i];
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ap %.2d: bssid=%pM status=%hhd bw=%hhd"
+ " rtt=%d rtt_var=%d rtt_spread=%d"
+ " rssi=%hhd rssi_spread=%hhd"
+ " range=%d range_var=%d"
+ " time_stamp=%d\n",
+ i, ap->bssid, ap->measure_status,
+ ap->measure_bw,
+ ap->rtt, ap->rtt_variance, ap->rtt_spread,
+ ap->rssi, ap->rssi_spread, ap->range,
+ ap->range_variance, ap->timestamp);
+ }
+ mutex_unlock(&mvm->mutex);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u8 value;
+ int ret;
+
+ ret = kstrtou8(buf, 0, &value);
+ if (ret)
+ return ret;
+ if (value > 1)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_update_low_latency(mvm, vif, value);
+ mutex_unlock(&mvm->mutex);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[2];
+
+ buf[0] = mvmvif->low_latency ? '1' : '0';
+ buf[1] = '\n';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
+}
+
+static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[20];
+ int len;
+
+ len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ bool ret;
+
+ mutex_lock(&mvm->mutex);
+ ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ? count : -EINVAL;
+}
+
+static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct iwl_mvm_phy_ctxt *phy_ctxt;
+ u16 value;
+ int ret;
+
+ ret = kstrtou16(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ mutex_lock(&mvm->mutex);
+ rcu_read_lock();
+
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ /* make sure the channel context is assigned */
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ mutex_unlock(&mvm->mutex);
+ return -EINVAL;
+ }
+
+ phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv];
+ rcu_read_unlock();
+
+ mvm->dbgfs_rx_phyinfo = value;
+
+ ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def,
+ chanctx_conf->rx_chains_static,
+ chanctx_conf->rx_chains_dynamic);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[8];
+
+ snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
+}
+
+#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
+ _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
+ _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
+#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \
+ if (!debugfs_create_file(#name, mode, parent, vif, \
+ &iwl_dbgfs_##name##_ops)) \
+ goto err; \
+ } while (0)
+
+MVM_DEBUGFS_READ_FILE_OPS(mac_params);
+MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_enable, 32);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_request, 512);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32);
+MVM_DEBUGFS_READ_FILE_OPS(tof_range_response);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32);
+
+void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct dentry *dbgfs_dir = vif->debugfs_dir;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[100];
+
+ /*
+ * Check if debugfs directory already exist before creating it.
+ * This may happen when, for example, resetting hw or suspend-resume
+ */
+ if (!dbgfs_dir || mvmvif->dbgfs_dir)
+ return;
+
+ mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
+
+ if (!mvmvif->dbgfs_dir) {
+ IWL_ERR(mvm, "Failed to create debugfs directory under %s\n",
+ dbgfs_dir->d_name.name);
+ return;
+ }
+
+ if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+ ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
+ (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
+ mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)))
+ MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
+ S_IRUSR);
+
+ MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+
+ if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+ mvmvif == mvm->bf_allowed_vif)
+ MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) &&
+ !vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) {
+ if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP)
+ MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params,
+ mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+
+ MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, mvmvif->dbgfs_dir,
+ S_IRUSR);
+ }
+
+ /*
+ * Create symlink for convenience pointing to interface specific
+ * debugfs entries for the driver. For example, under
+ * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/
+ * find
+ * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/
+ */
+ snprintf(buf, 100, "../../../%s/%s/%s/%s",
+ dbgfs_dir->d_parent->d_parent->d_name.name,
+ dbgfs_dir->d_parent->d_name.name,
+ dbgfs_dir->d_name.name,
+ mvmvif->dbgfs_dir->d_name.name);
+
+ mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name,
+ mvm->debugfs_dir, buf);
+ if (!mvmvif->dbgfs_slink)
+ IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n",
+ dbgfs_dir->d_name.name);
+ return;
+err:
+ IWL_ERR(mvm, "Can't create debugfs entity\n");
+}
+
+void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ debugfs_remove(mvmvif->dbgfs_slink);
+ mvmvif->dbgfs_slink = NULL;
+
+ debugfs_remove_recursive(mvmvif->dbgfs_dir);
+ mvmvif->dbgfs_dir = NULL;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
new file mode 100644
index 000000000000..90500e2d107b
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
@@ -0,0 +1,1570 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/vmalloc.h>
+
+#include "mvm.h"
+#include "fw-dbg.h"
+#include "sta.h"
+#include "iwl-io.h"
+#include "debugfs.h"
+#include "iwl-fw-error-dump.h"
+
+static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ u32 scd_q_msk;
+
+ if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+ return -EIO;
+
+ if (sscanf(buf, "%x", &scd_q_msk) != 1)
+ return -EINVAL;
+
+ IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, 0) ? : count;
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_sta *mvmsta;
+ int sta_id, drain, ret;
+
+ if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+ return -EIO;
+
+ if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
+ return -EINVAL;
+ if (sta_id < 0 || sta_id >= IWL_MVM_STATION_COUNT)
+ return -EINVAL;
+ if (drain < 0 || drain > 1)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+
+ if (!mvmsta)
+ ret = -ENOENT;
+ else
+ ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count;
+
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ const struct fw_img *img;
+ unsigned int ofs, len;
+ size_t ret;
+ u8 *ptr;
+
+ if (!mvm->ucode_loaded)
+ return -EINVAL;
+
+ /* default is to dump the entire data segment */
+ img = &mvm->fw->img[mvm->cur_ucode];
+ ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+ len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+ if (mvm->dbgfs_sram_len) {
+ ofs = mvm->dbgfs_sram_offset;
+ len = mvm->dbgfs_sram_len;
+ }
+
+ ptr = kzalloc(len, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len);
+
+ kfree(ptr);
+
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ const struct fw_img *img;
+ u32 offset, len;
+ u32 img_offset, img_len;
+
+ if (!mvm->ucode_loaded)
+ return -EINVAL;
+
+ img = &mvm->fw->img[mvm->cur_ucode];
+ img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset;
+ img_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+ if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
+ if ((offset & 0x3) || (len & 0x3))
+ return -EINVAL;
+
+ if (offset + len > img_offset + img_len)
+ return -EINVAL;
+
+ mvm->dbgfs_sram_offset = offset;
+ mvm->dbgfs_sram_len = len;
+ } else {
+ mvm->dbgfs_sram_offset = 0;
+ mvm->dbgfs_sram_len = 0;
+ }
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char buf[16];
+ int pos;
+
+ if (!mvm->temperature_test)
+ pos = scnprintf(buf , sizeof(buf), "disabled\n");
+ else
+ pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+/*
+ * Set NIC Temperature
+ * Cause the driver to ignore the actual NIC temperature reported by the FW
+ * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -
+ * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX
+ * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE
+ */
+static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ int temperature;
+
+ if (!mvm->ucode_loaded && !mvm->temperature_test)
+ return -EIO;
+
+ if (kstrtoint(buf, 10, &temperature))
+ return -EINVAL;
+ /* not a legal temperature */
+ if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX &&
+ temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) ||
+ temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+ if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) {
+ if (!mvm->temperature_test)
+ goto out;
+
+ mvm->temperature_test = false;
+ /* Since we can't read the temp while awake, just set
+ * it to zero until we get the next RX stats from the
+ * firmware.
+ */
+ mvm->temperature = 0;
+ } else {
+ mvm->temperature_test = true;
+ mvm->temperature = temperature;
+ }
+ IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n",
+ mvm->temperature_test ? "En" : "Dis" ,
+ mvm->temperature);
+ /* handle the temperature change */
+ iwl_mvm_tt_handler(mvm);
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_nic_temp_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char buf[16];
+ int pos, temp;
+
+ if (!mvm->ucode_loaded)
+ return -EIO;
+
+ mutex_lock(&mvm->mutex);
+ temp = iwl_mvm_get_temp(mvm);
+ mutex_unlock(&mvm->mutex);
+
+ if (temp < 0)
+ return temp;
+
+ pos = scnprintf(buf , sizeof(buf), "%d\n", temp);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ struct ieee80211_sta *sta;
+ char buf[400];
+ int i, pos = 0, bufsz = sizeof(buf);
+
+ mutex_lock(&mvm->mutex);
+
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (!sta)
+ pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");
+ else if (IS_ERR(sta))
+ pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",
+ PTR_ERR(sta));
+ else
+ pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
+ sta->addr);
+ }
+
+ mutex_unlock(&mvm->mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char buf[64];
+ int bufsz = sizeof(buf);
+ int pos = 0;
+
+ pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n",
+ mvm->disable_power_off);
+ pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n",
+ mvm->disable_power_off_d3);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret, val;
+
+ if (!mvm->ucode_loaded)
+ return -EIO;
+
+ if (!strncmp("disable_power_off_d0=", buf, 21)) {
+ if (sscanf(buf + 21, "%d", &val) != 1)
+ return -EINVAL;
+ mvm->disable_power_off = val;
+ } else if (!strncmp("disable_power_off_d3=", buf, 21)) {
+ if (sscanf(buf + 21, "%d", &val) != 1)
+ return -EINVAL;
+ mvm->disable_power_off_d3 = val;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_power_update_device(mvm);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+#define BT_MBOX_MSG(_notif, _num, _field) \
+ ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
+ >> BT_MBOX##_num##_##_field##_POS)
+
+
+#define BT_MBOX_PRINT(_num, _field, _end) \
+ pos += scnprintf(buf + pos, bufsz - pos, \
+ "\t%s: %d%s", \
+ #_field, \
+ BT_MBOX_MSG(notif, _num, _field), \
+ true ? "\n" : ", ");
+
+static
+int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
+ int pos, int bufsz)
+{
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
+
+ BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
+ BT_MBOX_PRINT(0, LE_PROF1, false);
+ BT_MBOX_PRINT(0, LE_PROF2, false);
+ BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
+ BT_MBOX_PRINT(0, CHL_SEQ_N, false);
+ BT_MBOX_PRINT(0, INBAND_S, false);
+ BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
+ BT_MBOX_PRINT(0, LE_SCAN, false);
+ BT_MBOX_PRINT(0, LE_ADV, false);
+ BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(0, OPEN_CON_1, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
+
+ BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(1, IP_SR, false);
+ BT_MBOX_PRINT(1, LE_MSTR, false);
+ BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
+ BT_MBOX_PRINT(1, MSG_TYPE, false);
+ BT_MBOX_PRINT(1, SSN, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
+
+ BT_MBOX_PRINT(2, SNIFF_ACT, false);
+ BT_MBOX_PRINT(2, PAG, false);
+ BT_MBOX_PRINT(2, INQUIRY, false);
+ BT_MBOX_PRINT(2, CONN, false);
+ BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
+ BT_MBOX_PRINT(2, DISC, false);
+ BT_MBOX_PRINT(2, SCO_TX_ACT, false);
+ BT_MBOX_PRINT(2, SCO_RX_ACT, false);
+ BT_MBOX_PRINT(2, ESCO_RE_TX, false);
+ BT_MBOX_PRINT(2, SCO_DURATION, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
+
+ BT_MBOX_PRINT(3, SCO_STATE, false);
+ BT_MBOX_PRINT(3, SNIFF_STATE, false);
+ BT_MBOX_PRINT(3, A2DP_STATE, false);
+ BT_MBOX_PRINT(3, ACL_STATE, false);
+ BT_MBOX_PRINT(3, MSTR_STATE, false);
+ BT_MBOX_PRINT(3, OBX_STATE, false);
+ BT_MBOX_PRINT(3, OPEN_CON_2, false);
+ BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
+ BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
+ BT_MBOX_PRINT(3, INBAND_P, false);
+ BT_MBOX_PRINT(3, MSG_TYPE_2, false);
+ BT_MBOX_PRINT(3, SSN_2, false);
+ BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
+
+ return pos;
+}
+
+static
+int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif,
+ char *buf, int pos, int bufsz)
+{
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
+
+ BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
+ BT_MBOX_PRINT(0, LE_PROF1, false);
+ BT_MBOX_PRINT(0, LE_PROF2, false);
+ BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
+ BT_MBOX_PRINT(0, CHL_SEQ_N, false);
+ BT_MBOX_PRINT(0, INBAND_S, false);
+ BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
+ BT_MBOX_PRINT(0, LE_SCAN, false);
+ BT_MBOX_PRINT(0, LE_ADV, false);
+ BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(0, OPEN_CON_1, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
+
+ BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(1, IP_SR, false);
+ BT_MBOX_PRINT(1, LE_MSTR, false);
+ BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
+ BT_MBOX_PRINT(1, MSG_TYPE, false);
+ BT_MBOX_PRINT(1, SSN, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
+
+ BT_MBOX_PRINT(2, SNIFF_ACT, false);
+ BT_MBOX_PRINT(2, PAG, false);
+ BT_MBOX_PRINT(2, INQUIRY, false);
+ BT_MBOX_PRINT(2, CONN, false);
+ BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
+ BT_MBOX_PRINT(2, DISC, false);
+ BT_MBOX_PRINT(2, SCO_TX_ACT, false);
+ BT_MBOX_PRINT(2, SCO_RX_ACT, false);
+ BT_MBOX_PRINT(2, ESCO_RE_TX, false);
+ BT_MBOX_PRINT(2, SCO_DURATION, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
+
+ BT_MBOX_PRINT(3, SCO_STATE, false);
+ BT_MBOX_PRINT(3, SNIFF_STATE, false);
+ BT_MBOX_PRINT(3, A2DP_STATE, false);
+ BT_MBOX_PRINT(3, ACL_STATE, false);
+ BT_MBOX_PRINT(3, MSTR_STATE, false);
+ BT_MBOX_PRINT(3, OBX_STATE, false);
+ BT_MBOX_PRINT(3, OPEN_CON_2, false);
+ BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
+ BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
+ BT_MBOX_PRINT(3, INBAND_P, false);
+ BT_MBOX_PRINT(3, MSG_TYPE_2, false);
+ BT_MBOX_PRINT(3, SSN_2, false);
+ BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
+
+ return pos;
+}
+
+static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char *buf;
+ int ret, pos = 0, bufsz = sizeof(char) * 1024;
+
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+
+ if (!fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ struct iwl_bt_coex_profile_notif_old *notif =
+ &mvm->last_bt_notif_old;
+
+ pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+ notif->bt_ci_compliance);
+ pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+ le32_to_cpu(notif->primary_ch_lut));
+ pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+ le32_to_cpu(notif->secondary_ch_lut));
+ pos += scnprintf(buf+pos,
+ bufsz-pos, "bt_activity_grading = %d\n",
+ le32_to_cpu(notif->bt_activity_grading));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "antenna isolation = %d CORUN LUT index = %d\n",
+ mvm->last_ant_isol, mvm->last_corun_lut);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n",
+ notif->rrc_enabled);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n",
+ notif->ttc_enabled);
+ } else {
+ struct iwl_bt_coex_profile_notif *notif =
+ &mvm->last_bt_notif;
+
+ pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+ notif->bt_ci_compliance);
+ pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+ le32_to_cpu(notif->primary_ch_lut));
+ pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+ le32_to_cpu(notif->secondary_ch_lut));
+ pos += scnprintf(buf+pos,
+ bufsz-pos, "bt_activity_grading = %d\n",
+ le32_to_cpu(notif->bt_activity_grading));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "antenna isolation = %d CORUN LUT index = %d\n",
+ mvm->last_ant_isol, mvm->last_corun_lut);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n",
+ (notif->ttc_rrc_status >> 4) & 0xF);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n",
+ notif->ttc_rrc_status & 0xF);
+ }
+
+ pos += scnprintf(buf + pos, bufsz - pos, "sync_sco = %d\n",
+ IWL_MVM_BT_COEX_SYNC2SCO);
+ pos += scnprintf(buf + pos, bufsz - pos, "mplut = %d\n",
+ IWL_MVM_BT_COEX_MPLUT);
+ pos += scnprintf(buf + pos, bufsz - pos, "corunning = %d\n",
+ IWL_MVM_BT_COEX_CORUNNING);
+
+ mutex_unlock(&mvm->mutex);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+
+ return ret;
+}
+#undef BT_MBOX_PRINT
+
+static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char buf[256];
+ int bufsz = sizeof(buf);
+ int pos = 0;
+
+ mutex_lock(&mvm->mutex);
+
+ if (!fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old;
+
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "Channel inhibition CMD\n");
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tPrimary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_primary_ci));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tSecondary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_secondary_ci));
+
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "BT Configuration CMD - 0=default, 1=never, 2=always\n");
+ pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n",
+ mvm->bt_ack_kill_msk[0]);
+ pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n",
+ mvm->bt_cts_kill_msk[0]);
+
+ } else {
+ struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
+
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "Channel inhibition CMD\n");
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tPrimary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_primary_ci));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tSecondary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_secondary_ci));
+ }
+
+ mutex_unlock(&mvm->mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t
+iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ u32 bt_tx_prio;
+
+ if (sscanf(buf, "%u", &bt_tx_prio) != 1)
+ return -EINVAL;
+ if (bt_tx_prio > 4)
+ return -EINVAL;
+
+ mvm->bt_tx_prio = bt_tx_prio;
+
+ return count;
+}
+
+static ssize_t
+iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ static const char * const modes_str[BT_FORCE_ANT_MAX] = {
+ [BT_FORCE_ANT_DIS] = "dis",
+ [BT_FORCE_ANT_AUTO] = "auto",
+ [BT_FORCE_ANT_BT] = "bt",
+ [BT_FORCE_ANT_WIFI] = "wifi",
+ };
+ int ret, bt_force_ant_mode;
+
+ for (bt_force_ant_mode = 0;
+ bt_force_ant_mode < ARRAY_SIZE(modes_str);
+ bt_force_ant_mode++) {
+ if (!strcmp(buf, modes_str[bt_force_ant_mode]))
+ break;
+ }
+
+ if (bt_force_ant_mode >= ARRAY_SIZE(modes_str))
+ return -EINVAL;
+
+ ret = 0;
+ mutex_lock(&mvm->mutex);
+ if (mvm->bt_force_ant_mode == bt_force_ant_mode)
+ goto out;
+
+ mvm->bt_force_ant_mode = bt_force_ant_mode;
+ IWL_DEBUG_COEX(mvm, "Force mode: %s\n",
+ modes_str[mvm->bt_force_ant_mode]);
+ ret = iwl_send_bt_init_conf(mvm);
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret ?: count;
+}
+
+#define PRINT_STATS_LE32(_struct, _memb) \
+ pos += scnprintf(buf + pos, bufsz - pos, \
+ fmt_table, #_memb, \
+ le32_to_cpu(_struct->_memb))
+
+static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
+ char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ static const char *fmt_table = "\t%-30s %10u\n";
+ static const char *fmt_header = "%-32s\n";
+ int pos = 0;
+ char *buf;
+ int ret;
+ /* 43 is the size of each data line, 33 is the size of each header */
+ size_t bufsz =
+ ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) +
+ (4 * 33) + 1;
+
+ struct mvm_statistics_rx_phy *ofdm;
+ struct mvm_statistics_rx_phy *cck;
+ struct mvm_statistics_rx_non_phy *general;
+ struct mvm_statistics_rx_ht_phy *ht;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+
+ ofdm = &mvm->rx_stats.ofdm;
+ cck = &mvm->rx_stats.cck;
+ general = &mvm->rx_stats.general;
+ ht = &mvm->rx_stats.ofdm_ht;
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - OFDM");
+ PRINT_STATS_LE32(ofdm, ina_cnt);
+ PRINT_STATS_LE32(ofdm, fina_cnt);
+ PRINT_STATS_LE32(ofdm, plcp_err);
+ PRINT_STATS_LE32(ofdm, crc32_err);
+ PRINT_STATS_LE32(ofdm, overrun_err);
+ PRINT_STATS_LE32(ofdm, early_overrun_err);
+ PRINT_STATS_LE32(ofdm, crc32_good);
+ PRINT_STATS_LE32(ofdm, false_alarm_cnt);
+ PRINT_STATS_LE32(ofdm, fina_sync_err_cnt);
+ PRINT_STATS_LE32(ofdm, sfd_timeout);
+ PRINT_STATS_LE32(ofdm, fina_timeout);
+ PRINT_STATS_LE32(ofdm, unresponded_rts);
+ PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun);
+ PRINT_STATS_LE32(ofdm, sent_ack_cnt);
+ PRINT_STATS_LE32(ofdm, sent_cts_cnt);
+ PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt);
+ PRINT_STATS_LE32(ofdm, dsp_self_kill);
+ PRINT_STATS_LE32(ofdm, mh_format_err);
+ PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum);
+ PRINT_STATS_LE32(ofdm, reserved);
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - CCK");
+ PRINT_STATS_LE32(cck, ina_cnt);
+ PRINT_STATS_LE32(cck, fina_cnt);
+ PRINT_STATS_LE32(cck, plcp_err);
+ PRINT_STATS_LE32(cck, crc32_err);
+ PRINT_STATS_LE32(cck, overrun_err);
+ PRINT_STATS_LE32(cck, early_overrun_err);
+ PRINT_STATS_LE32(cck, crc32_good);
+ PRINT_STATS_LE32(cck, false_alarm_cnt);
+ PRINT_STATS_LE32(cck, fina_sync_err_cnt);
+ PRINT_STATS_LE32(cck, sfd_timeout);
+ PRINT_STATS_LE32(cck, fina_timeout);
+ PRINT_STATS_LE32(cck, unresponded_rts);
+ PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun);
+ PRINT_STATS_LE32(cck, sent_ack_cnt);
+ PRINT_STATS_LE32(cck, sent_cts_cnt);
+ PRINT_STATS_LE32(cck, sent_ba_rsp_cnt);
+ PRINT_STATS_LE32(cck, dsp_self_kill);
+ PRINT_STATS_LE32(cck, mh_format_err);
+ PRINT_STATS_LE32(cck, re_acq_main_rssi_sum);
+ PRINT_STATS_LE32(cck, reserved);
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - GENERAL");
+ PRINT_STATS_LE32(general, bogus_cts);
+ PRINT_STATS_LE32(general, bogus_ack);
+ PRINT_STATS_LE32(general, non_bssid_frames);
+ PRINT_STATS_LE32(general, filtered_frames);
+ PRINT_STATS_LE32(general, non_channel_beacons);
+ PRINT_STATS_LE32(general, channel_beacons);
+ PRINT_STATS_LE32(general, num_missed_bcon);
+ PRINT_STATS_LE32(general, adc_rx_saturation_time);
+ PRINT_STATS_LE32(general, ina_detection_search_time);
+ PRINT_STATS_LE32(general, beacon_silence_rssi_a);
+ PRINT_STATS_LE32(general, beacon_silence_rssi_b);
+ PRINT_STATS_LE32(general, beacon_silence_rssi_c);
+ PRINT_STATS_LE32(general, interference_data_flag);
+ PRINT_STATS_LE32(general, channel_load);
+ PRINT_STATS_LE32(general, dsp_false_alarms);
+ PRINT_STATS_LE32(general, beacon_rssi_a);
+ PRINT_STATS_LE32(general, beacon_rssi_b);
+ PRINT_STATS_LE32(general, beacon_rssi_c);
+ PRINT_STATS_LE32(general, beacon_energy_a);
+ PRINT_STATS_LE32(general, beacon_energy_b);
+ PRINT_STATS_LE32(general, beacon_energy_c);
+ PRINT_STATS_LE32(general, num_bt_kills);
+ PRINT_STATS_LE32(general, mac_id);
+ PRINT_STATS_LE32(general, directed_data_mpdu);
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - HT");
+ PRINT_STATS_LE32(ht, plcp_err);
+ PRINT_STATS_LE32(ht, overrun_err);
+ PRINT_STATS_LE32(ht, early_overrun_err);
+ PRINT_STATS_LE32(ht, crc32_good);
+ PRINT_STATS_LE32(ht, crc32_err);
+ PRINT_STATS_LE32(ht, mh_format_err);
+ PRINT_STATS_LE32(ht, agg_crc32_good);
+ PRINT_STATS_LE32(ht, agg_mpdu_cnt);
+ PRINT_STATS_LE32(ht, agg_cnt);
+ PRINT_STATS_LE32(ht, unsupport_mcs);
+
+ mutex_unlock(&mvm->mutex);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+
+ return ret;
+}
+#undef PRINT_STAT_LE32
+
+static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm,
+ char __user *user_buf, size_t count,
+ loff_t *ppos,
+ struct iwl_mvm_frame_stats *stats)
+{
+ char *buff, *pos, *endpos;
+ int idx, i;
+ int ret;
+ static const size_t bufsz = 1024;
+
+ buff = kmalloc(bufsz, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ spin_lock_bh(&mvm->drv_stats_lock);
+
+ pos = buff;
+ endpos = pos + bufsz;
+
+ pos += scnprintf(pos, endpos - pos,
+ "Legacy/HT/VHT\t:\t%d/%d/%d\n",
+ stats->legacy_frames,
+ stats->ht_frames,
+ stats->vht_frames);
+ pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n",
+ stats->bw_20_frames,
+ stats->bw_40_frames,
+ stats->bw_80_frames);
+ pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n",
+ stats->ngi_frames,
+ stats->sgi_frames);
+ pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n",
+ stats->siso_frames,
+ stats->mimo2_frames);
+ pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n",
+ stats->fail_frames,
+ stats->success_frames);
+ pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n",
+ stats->agg_frames);
+ pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n",
+ stats->ampdu_count);
+ pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n",
+ stats->ampdu_count > 0 ?
+ (stats->agg_frames / stats->ampdu_count) : 0);
+
+ pos += scnprintf(pos, endpos - pos, "Last Rates\n");
+
+ idx = stats->last_frame_idx - 1;
+ for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) {
+ idx = (idx + 1) % ARRAY_SIZE(stats->last_rates);
+ if (stats->last_rates[idx] == 0)
+ continue;
+ pos += scnprintf(pos, endpos - pos, "Rate[%d]: ",
+ (int)(ARRAY_SIZE(stats->last_rates) - i));
+ pos += rs_pretty_print_rate(pos, stats->last_rates[idx]);
+ }
+ spin_unlock_bh(&mvm->drv_stats_lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
+ kfree(buff);
+
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file,
+ char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+
+ return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos,
+ &mvm->drv_rx_stats);
+}
+
+static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ /* allow one more restart that we're provoking here */
+ if (mvm->restart_fw >= 0)
+ mvm->restart_fw++;
+
+ /* take the return value to make compiler happy - it will fail anyway */
+ ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL);
+
+ mutex_unlock(&mvm->mutex);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_NMI);
+ if (ret)
+ return ret;
+
+ iwl_force_nmi(mvm->trans);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_NMI);
+
+ return count;
+}
+
+static ssize_t
+iwl_dbgfs_scan_ant_rxchain_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ int pos = 0;
+ char buf[32];
+ const size_t bufsz = sizeof(buf);
+
+ /* print which antennas were set for the scan command by the user */
+ pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: ");
+ if (mvm->scan_rx_ant & ANT_A)
+ pos += scnprintf(buf + pos, bufsz - pos, "A");
+ if (mvm->scan_rx_ant & ANT_B)
+ pos += scnprintf(buf + pos, bufsz - pos, "B");
+ if (mvm->scan_rx_ant & ANT_C)
+ pos += scnprintf(buf + pos, bufsz - pos, "C");
+ pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t
+iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ u8 scan_rx_ant;
+
+ if (sscanf(buf, "%hhx", &scan_rx_ant) != 1)
+ return -EINVAL;
+ if (scan_rx_ant > ANT_ABC)
+ return -EINVAL;
+ if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm)))
+ return -EINVAL;
+
+ if (mvm->scan_rx_ant != scan_rx_ant) {
+ mvm->scan_rx_ant = scan_rx_ant;
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ iwl_mvm_config_scan(mvm);
+ }
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ int conf;
+ char buf[8];
+ const size_t bufsz = sizeof(buf);
+ int pos = 0;
+
+ mutex_lock(&mvm->mutex);
+ conf = mvm->fw_dbg_conf;
+ mutex_unlock(&mvm->mutex);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+/*
+ * Enable / Disable continuous recording.
+ * Cause the FW to start continuous recording, by sending the relevant hcmd.
+ * Enable: input of every integer larger than 0, ENABLE_CONT_RECORDING.
+ * Disable: for 0 as input, DISABLE_CONT_RECORDING.
+ */
+static ssize_t iwl_dbgfs_cont_recording_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_trans *trans = mvm->trans;
+ const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+ struct iwl_continuous_record_cmd cont_rec = {};
+ int ret, rec_mode;
+
+ if (!dest)
+ return -EOPNOTSUPP;
+
+ if (dest->monitor_mode != SMEM_MODE ||
+ trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return -EOPNOTSUPP;
+
+ ret = kstrtouint(buf, 0, &rec_mode);
+ if (ret)
+ return ret;
+
+ cont_rec.record_mode.enable_recording = rec_mode ?
+ cpu_to_le16(ENABLE_CONT_RECORDING) :
+ cpu_to_le16(DISABLE_CONT_RECORDING);
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_send_cmd_pdu(mvm, LDBG_CONFIG_CMD, 0,
+ sizeof(cont_rec), &cont_rec);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned int conf_id;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &conf_id);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(conf_id >= FW_DBG_CONF_MAX))
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE);
+
+ if (ret)
+ return ret;
+
+ iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, buf,
+ (count - 1), NULL);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
+
+ return count;
+}
+
+#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ struct iwl_bcast_filter_cmd cmd;
+ const struct iwl_fw_bcast_filter *filter;
+ char *buf;
+ int bufsz = 1024;
+ int i, j, pos = 0;
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+ if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+ ADD_TEXT("None\n");
+ mutex_unlock(&mvm->mutex);
+ goto out;
+ }
+ mutex_unlock(&mvm->mutex);
+
+ for (i = 0; cmd.filters[i].attrs[0].mask; i++) {
+ filter = &cmd.filters[i];
+
+ ADD_TEXT("Filter [%d]:\n", i);
+ ADD_TEXT("\tDiscard=%d\n", filter->discard);
+ ADD_TEXT("\tFrame Type: %s\n",
+ filter->frame_type ? "IPv4" : "Generic");
+
+ for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) {
+ const struct iwl_fw_bcast_filter_attr *attr;
+
+ attr = &filter->attrs[j];
+ if (!attr->mask)
+ break;
+
+ ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n",
+ j, attr->offset,
+ attr->offset_type ? "IP End" :
+ "Payload Start",
+ be32_to_cpu(attr->mask),
+ be32_to_cpu(attr->val),
+ le16_to_cpu(attr->reserved1));
+ }
+ }
+out:
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int pos, next_pos;
+ struct iwl_fw_bcast_filter filter = {};
+ struct iwl_bcast_filter_cmd cmd;
+ u32 filter_id, attr_id, mask, value;
+ int err = 0;
+
+ if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard,
+ &filter.frame_type, &pos) != 3)
+ return -EINVAL;
+
+ if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) ||
+ filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4)
+ return -EINVAL;
+
+ for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs);
+ attr_id++) {
+ struct iwl_fw_bcast_filter_attr *attr =
+ &filter.attrs[attr_id];
+
+ if (pos >= count)
+ break;
+
+ if (sscanf(&buf[pos], "%hhi %hhi %i %i %n",
+ &attr->offset, &attr->offset_type,
+ &mask, &value, &next_pos) != 4)
+ return -EINVAL;
+
+ attr->mask = cpu_to_be32(mask);
+ attr->val = cpu_to_be32(value);
+ if (mask)
+ filter.num_attrs++;
+
+ pos += next_pos;
+ }
+
+ mutex_lock(&mvm->mutex);
+ memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id],
+ &filter, sizeof(filter));
+
+ /* send updated bcast filtering configuration */
+ if (mvm->dbgfs_bcast_filtering.override &&
+ iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+ err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
+ sizeof(cmd), &cmd);
+ mutex_unlock(&mvm->mutex);
+
+ return err ?: count;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ struct iwl_bcast_filter_cmd cmd;
+ char *buf;
+ int bufsz = 1024;
+ int i, pos = 0;
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+ if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+ ADD_TEXT("None\n");
+ mutex_unlock(&mvm->mutex);
+ goto out;
+ }
+ mutex_unlock(&mvm->mutex);
+
+ for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) {
+ const struct iwl_fw_bcast_mac *mac = &cmd.macs[i];
+
+ ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n",
+ i, mac->default_discard, mac->attached_filters);
+ }
+out:
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_bcast_filter_cmd cmd;
+ struct iwl_fw_bcast_mac mac = {};
+ u32 mac_id, attached_filters;
+ int err = 0;
+
+ if (!mvm->bcast_filters)
+ return -ENOENT;
+
+ if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard,
+ &attached_filters) != 3)
+ return -EINVAL;
+
+ if (mac_id >= ARRAY_SIZE(cmd.macs) ||
+ mac.default_discard > 1 ||
+ attached_filters >= BIT(ARRAY_SIZE(cmd.filters)))
+ return -EINVAL;
+
+ mac.attached_filters = cpu_to_le16(attached_filters);
+
+ mutex_lock(&mvm->mutex);
+ memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id],
+ &mac, sizeof(mac));
+
+ /* send updated bcast filtering configuration */
+ if (mvm->dbgfs_bcast_filtering.override &&
+ iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+ err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
+ sizeof(cmd), &cmd);
+ mutex_unlock(&mvm->mutex);
+
+ return err ?: count;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int store;
+
+ if (sscanf(buf, "%d", &store) != 1)
+ return -EINVAL;
+
+ mvm->store_d3_resume_sram = store;
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ const struct fw_img *img;
+ int ofs, len, pos = 0;
+ size_t bufsz, ret;
+ char *buf;
+ u8 *ptr = mvm->d3_resume_sram;
+
+ img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+ len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+ bufsz = len * 4 + 256;
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n",
+ mvm->store_d3_resume_sram ? "en" : "dis");
+
+ if (ptr) {
+ for (ofs = 0; ofs < len; ofs += 16) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "0x%.4x %16ph\n", ofs, ptr + ofs);
+ }
+ } else {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "(no data captured)\n");
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+
+ kfree(buf);
+
+ return ret;
+}
+#endif
+
+#define PRINT_MVM_REF(ref) do { \
+ if (mvm->refs[ref]) \
+ pos += scnprintf(buf + pos, bufsz - pos, \
+ "\t(0x%lx): %d %s\n", \
+ BIT(ref), mvm->refs[ref], #ref); \
+} while (0)
+
+static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ int i, pos = 0;
+ char buf[256];
+ const size_t bufsz = sizeof(buf);
+ u32 refs = 0;
+
+ for (i = 0; i < IWL_MVM_REF_COUNT; i++)
+ if (mvm->refs[i])
+ refs |= BIT(i);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%x\n",
+ refs);
+
+ PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
+ PRINT_MVM_REF(IWL_MVM_REF_SCAN);
+ PRINT_MVM_REF(IWL_MVM_REF_ROC);
+ PRINT_MVM_REF(IWL_MVM_REF_ROC_AUX);
+ PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT);
+ PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS);
+ PRINT_MVM_REF(IWL_MVM_REF_USER);
+ PRINT_MVM_REF(IWL_MVM_REF_TX);
+ PRINT_MVM_REF(IWL_MVM_REF_TX_AGG);
+ PRINT_MVM_REF(IWL_MVM_REF_ADD_IF);
+ PRINT_MVM_REF(IWL_MVM_REF_START_AP);
+ PRINT_MVM_REF(IWL_MVM_REF_BSS_CHANGED);
+ PRINT_MVM_REF(IWL_MVM_REF_PREPARE_TX);
+ PRINT_MVM_REF(IWL_MVM_REF_PROTECT_TDLS);
+ PRINT_MVM_REF(IWL_MVM_REF_CHECK_CTKILL);
+ PRINT_MVM_REF(IWL_MVM_REF_PRPH_READ);
+ PRINT_MVM_REF(IWL_MVM_REF_PRPH_WRITE);
+ PRINT_MVM_REF(IWL_MVM_REF_NMI);
+ PRINT_MVM_REF(IWL_MVM_REF_TM_CMD);
+ PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
+ PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
+ PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT);
+ PRINT_MVM_REF(IWL_MVM_REF_INIT_UCODE);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long value;
+ int ret;
+ bool taken;
+
+ ret = kstrtoul(buf, 10, &value);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&mvm->mutex);
+
+ taken = mvm->refs[IWL_MVM_REF_USER];
+ if (value == 1 && !taken)
+ iwl_mvm_ref(mvm, IWL_MVM_REF_USER);
+ else if (value == 0 && taken)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_USER);
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&mvm->mutex);
+
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
+ _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
+ _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
+#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \
+ if (!debugfs_create_file(alias, mode, parent, mvm, \
+ &iwl_dbgfs_##name##_ops)) \
+ goto err; \
+ } while (0)
+#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
+ MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
+
+static ssize_t
+iwl_dbgfs_prph_reg_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ int pos = 0;
+ char buf[32];
+ const size_t bufsz = sizeof(buf);
+ int ret;
+
+ if (!mvm->dbgfs_prph_reg_addr)
+ return -EINVAL;
+
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_READ);
+ if (ret)
+ return ret;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n",
+ mvm->dbgfs_prph_reg_addr,
+ iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr));
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_READ);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t
+iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ u8 args;
+ u32 value;
+ int ret;
+
+ args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value);
+ /* if we only want to set the reg address - nothing more to do */
+ if (args == 1)
+ goto out;
+
+ /* otherwise, make sure we have both address and value */
+ if (args != 2)
+ return -EINVAL;
+
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE);
+ if (ret)
+ return ret;
+
+ iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
+out:
+ return count;
+}
+
+static ssize_t
+iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
+
+/* Device wide debugfs entries */
+MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
+MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8);
+MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64);
+MVM_DEBUGFS_READ_FILE_OPS(nic_temp);
+MVM_DEBUGFS_READ_FILE_OPS(stations);
+MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
+MVM_DEBUGFS_READ_FILE_OPS(bt_cmd);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64);
+MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
+MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);
+MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
+MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64);
+MVM_DEBUGFS_WRITE_FILE_OPS(cont_recording, 8);
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
+#endif
+
+int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
+{
+ struct dentry *bcast_dir __maybe_unused;
+ char buf[100];
+
+ spin_lock_init(&mvm->drv_stats_lock);
+
+ mvm->debugfs_dir = dbgfs_dir;
+
+ MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir,
+ S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
+ S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
+ S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, S_IWUSR);
+ if (!debugfs_create_bool("enable_scan_iteration_notif",
+ S_IRUSR | S_IWUSR,
+ mvm->debugfs_dir,
+ &mvm->scan_iter_notif_enabled))
+ goto err;
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
+ bcast_dir = debugfs_create_dir("bcast_filtering",
+ mvm->debugfs_dir);
+ if (!bcast_dir)
+ goto err;
+
+ if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR,
+ bcast_dir,
+ &mvm->dbgfs_bcast_filtering.override))
+ goto err;
+
+ MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
+ bcast_dir, S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
+ bcast_dir, S_IWUSR | S_IRUSR);
+ }
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+ MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
+ if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
+ mvm->debugfs_dir, &mvm->d3_wake_sysassert))
+ goto err;
+ if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR,
+ mvm->debugfs_dir, &mvm->last_netdetect_scans))
+ goto err;
+#endif
+
+ if (!debugfs_create_u8("ps_disabled", S_IRUSR,
+ mvm->debugfs_dir, &mvm->ps_disabled))
+ goto err;
+ if (!debugfs_create_blob("nvm_hw", S_IRUSR,
+ mvm->debugfs_dir, &mvm->nvm_hw_blob))
+ goto err;
+ if (!debugfs_create_blob("nvm_sw", S_IRUSR,
+ mvm->debugfs_dir, &mvm->nvm_sw_blob))
+ goto err;
+ if (!debugfs_create_blob("nvm_calib", S_IRUSR,
+ mvm->debugfs_dir, &mvm->nvm_calib_blob))
+ goto err;
+ if (!debugfs_create_blob("nvm_prod", S_IRUSR,
+ mvm->debugfs_dir, &mvm->nvm_prod_blob))
+ goto err;
+ if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR,
+ mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
+ goto err;
+
+ /*
+ * Create a symlink with mac80211. It will be removed when mac80211
+ * exists (before the opmode exists which removes the target.)
+ */
+ snprintf(buf, 100, "../../%s/%s",
+ dbgfs_dir->d_parent->d_parent->d_name.name,
+ dbgfs_dir->d_parent->d_name.name);
+ if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf))
+ goto err;
+
+ return 0;
+err:
+ IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");
+ return -ENOMEM;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h
new file mode 100644
index 000000000000..ede6ef8d390e
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h
@@ -0,0 +1,103 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#define MVM_DEBUGFS_READ_FILE_OPS(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+}
+
+#define MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
+static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ argtype *arg = file->private_data; \
+ char buf[buflen] = {}; \
+ size_t buf_size = min(count, sizeof(buf) - 1); \
+ \
+ if (copy_from_user(buf, user_buf, buf_size)) \
+ return -EFAULT; \
+ \
+ return iwl_dbgfs_##name##_write(arg, buf, buf_size, ppos); \
+} \
+
+#define _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \
+MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = _iwl_dbgfs_##name##_write, \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define _MVM_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \
+MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = _iwl_dbgfs_##name##_write, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h
new file mode 100644
index 000000000000..2a33b694ba10
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h
@@ -0,0 +1,476 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_bt_coex_h__
+#define __fw_api_bt_coex_h__
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#define BITS(nb) (BIT(nb) - 1)
+
+/**
+ * enum iwl_bt_coex_flags - flags for BT_COEX command
+ * @BT_COEX_MODE_POS:
+ * @BT_COEX_MODE_MSK:
+ * @BT_COEX_DISABLE_OLD:
+ * @BT_COEX_2W_OLD:
+ * @BT_COEX_3W_OLD:
+ * @BT_COEX_NW_OLD:
+ * @BT_COEX_AUTO_OLD:
+ * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests)
+ * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests)
+ * @BT_COEX_SYNC2SCO:
+ * @BT_COEX_CORUNNING:
+ * @BT_COEX_MPLUT:
+ * @BT_COEX_TTC:
+ * @BT_COEX_RRC:
+ *
+ * The COEX_MODE must be set for each command. Even if it is not changed.
+ */
+enum iwl_bt_coex_flags {
+ BT_COEX_MODE_POS = 3,
+ BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
+ BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS,
+ BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS,
+ BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS,
+ BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS,
+ BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS,
+ BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS,
+ BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS,
+ BT_COEX_SYNC2SCO = BIT(7),
+ BT_COEX_CORUNNING = BIT(8),
+ BT_COEX_MPLUT = BIT(9),
+ BT_COEX_TTC = BIT(20),
+ BT_COEX_RRC = BIT(21),
+};
+
+/*
+ * indicates what has changed in the BT_COEX command.
+ * BT_VALID_ENABLE must be set for each command. Commands without this bit will
+ * discarded by the firmware
+ */
+enum iwl_bt_coex_valid_bit_msk {
+ BT_VALID_ENABLE = BIT(0),
+ BT_VALID_BT_PRIO_BOOST = BIT(1),
+ BT_VALID_MAX_KILL = BIT(2),
+ BT_VALID_3W_TMRS = BIT(3),
+ BT_VALID_KILL_ACK = BIT(4),
+ BT_VALID_KILL_CTS = BIT(5),
+ BT_VALID_REDUCED_TX_POWER = BIT(6),
+ BT_VALID_LUT = BIT(7),
+ BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8),
+ BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9),
+ BT_VALID_MULTI_PRIO_LUT = BIT(10),
+ BT_VALID_TRM_KICK_FILTER = BIT(11),
+ BT_VALID_CORUN_LUT_20 = BIT(12),
+ BT_VALID_CORUN_LUT_40 = BIT(13),
+ BT_VALID_ANT_ISOLATION = BIT(14),
+ BT_VALID_ANT_ISOLATION_THRS = BIT(15),
+ BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16),
+ BT_VALID_TXRX_MAX_FREQ_0 = BIT(17),
+ BT_VALID_SYNC_TO_SCO = BIT(18),
+ BT_VALID_TTC = BIT(20),
+ BT_VALID_RRC = BIT(21),
+};
+
+/**
+ * enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames.
+ * @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames
+ * @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames
+ *
+ * This mechanism allows to have BT and WiFi run concurrently. Since WiFi
+ * reduces its Tx power, it can work along with BT, hence reducing the amount
+ * of WiFi frames being killed by BT.
+ */
+enum iwl_bt_reduced_tx_power {
+ BT_REDUCED_TX_POWER_CTL = BIT(0),
+ BT_REDUCED_TX_POWER_DATA = BIT(1),
+};
+
+enum iwl_bt_coex_lut_type {
+ BT_COEX_TIGHT_LUT = 0,
+ BT_COEX_LOOSE_LUT,
+ BT_COEX_TX_DIS_LUT,
+
+ BT_COEX_MAX_LUT,
+ BT_COEX_INVALID_LUT = 0xff,
+}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */
+
+#define BT_COEX_LUT_SIZE (12)
+#define BT_COEX_CORUN_LUT_SIZE (32)
+#define BT_COEX_MULTI_PRIO_LUT_SIZE (2)
+#define BT_COEX_BOOST_SIZE (4)
+#define BT_REDUCED_TX_POWER_BIT BIT(7)
+
+/**
+ * struct iwl_bt_coex_cmd_old - bt coex configuration command
+ * @flags:&enum iwl_bt_coex_flags
+ * @max_kill:
+ * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
+ * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ * should be set by default
+ * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ * should be set by default
+ * @bt4_antenna_isolation: antenna isolation
+ * @bt4_antenna_isolation_thr: antenna threshold value
+ * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
+ * @bt4_tx_rx_max_freq0: TxRx max frequency
+ * @bt_prio_boost: BT priority boost registers
+ * @wifi_tx_prio_boost: SW boost of wifi tx priority
+ * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
+ * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
+ * @decision_lut: PTA decision LUT, per Prio-Ch
+ * @bt4_multiprio_lut: multi priority LUT configuration
+ * @bt4_corun_lut20: co-running 20 MHz LUT configuration
+ * @bt4_corun_lut40: co-running 40 MHz LUT configuration
+ * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
+ *
+ * The structure is used for the BT_COEX command.
+ */
+struct iwl_bt_coex_cmd_old {
+ __le32 flags;
+ u8 max_kill;
+ u8 bt_reduced_tx_power;
+ u8 override_primary_lut;
+ u8 override_secondary_lut;
+
+ u8 bt4_antenna_isolation;
+ u8 bt4_antenna_isolation_thr;
+ u8 bt4_tx_tx_delta_freq_thr;
+ u8 bt4_tx_rx_max_freq0;
+
+ __le32 bt_prio_boost[BT_COEX_BOOST_SIZE];
+ __le32 wifi_tx_prio_boost;
+ __le32 wifi_rx_prio_boost;
+ __le32 kill_ack_msk;
+ __le32 kill_cts_msk;
+
+ __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
+ __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
+ __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE];
+ __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE];
+
+ __le32 valid_bit_msk;
+} __packed; /* BT_COEX_CMD_API_S_VER_5 */
+
+enum iwl_bt_coex_mode {
+ BT_COEX_DISABLE = 0x0,
+ BT_COEX_NW = 0x1,
+ BT_COEX_BT = 0x2,
+ BT_COEX_WIFI = 0x3,
+}; /* BT_COEX_MODES_E */
+
+enum iwl_bt_coex_enabled_modules {
+ BT_COEX_MPLUT_ENABLED = BIT(0),
+ BT_COEX_MPLUT_BOOST_ENABLED = BIT(1),
+ BT_COEX_SYNC2SCO_ENABLED = BIT(2),
+ BT_COEX_CORUN_ENABLED = BIT(3),
+ BT_COEX_HIGH_BAND_RET = BIT(4),
+}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */
+
+/**
+ * struct iwl_bt_coex_cmd - bt coex configuration command
+ * @mode: enum %iwl_bt_coex_mode
+ * @enabled_modules: enum %iwl_bt_coex_enabled_modules
+ *
+ * The structure is used for the BT_COEX command.
+ */
+struct iwl_bt_coex_cmd {
+ __le32 mode;
+ __le32 enabled_modules;
+} __packed; /* BT_COEX_CMD_API_S_VER_6 */
+
+/**
+ * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut
+ * @corun_lut20: co-running 20 MHz LUT configuration
+ * @corun_lut40: co-running 40 MHz LUT configuration
+ *
+ * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command.
+ */
+struct iwl_bt_coex_corun_lut_update_cmd {
+ __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE];
+ __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE];
+} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_reduced_txp_update_cmd
+ * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
+ * bits are the sta_id (value)
+ */
+struct iwl_bt_coex_reduced_txp_update_cmd {
+ __le32 reduced_txp;
+} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
+ * @bt_primary_ci:
+ * @primary_ch_phy_id:
+ * @bt_secondary_ci:
+ * @secondary_ch_phy_id:
+ *
+ * Used for BT_COEX_CI command
+ */
+struct iwl_bt_coex_ci_cmd {
+ __le64 bt_primary_ci;
+ __le32 primary_ch_phy_id;
+
+ __le64 bt_secondary_ci;
+ __le32 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_2 */
+
+#define BT_MBOX(n_dw, _msg, _pos, _nbits) \
+ BT_MBOX##n_dw##_##_msg##_POS = (_pos), \
+ BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS
+
+enum iwl_bt_mxbox_dw0 {
+ BT_MBOX(0, LE_SLAVE_LAT, 0, 3),
+ BT_MBOX(0, LE_PROF1, 3, 1),
+ BT_MBOX(0, LE_PROF2, 4, 1),
+ BT_MBOX(0, LE_PROF_OTHER, 5, 1),
+ BT_MBOX(0, CHL_SEQ_N, 8, 4),
+ BT_MBOX(0, INBAND_S, 13, 1),
+ BT_MBOX(0, LE_MIN_RSSI, 16, 4),
+ BT_MBOX(0, LE_SCAN, 20, 1),
+ BT_MBOX(0, LE_ADV, 21, 1),
+ BT_MBOX(0, LE_MAX_TX_POWER, 24, 4),
+ BT_MBOX(0, OPEN_CON_1, 28, 2),
+};
+
+enum iwl_bt_mxbox_dw1 {
+ BT_MBOX(1, BR_MAX_TX_POWER, 0, 4),
+ BT_MBOX(1, IP_SR, 4, 1),
+ BT_MBOX(1, LE_MSTR, 5, 1),
+ BT_MBOX(1, AGGR_TRFC_LD, 8, 6),
+ BT_MBOX(1, MSG_TYPE, 16, 3),
+ BT_MBOX(1, SSN, 19, 2),
+};
+
+enum iwl_bt_mxbox_dw2 {
+ BT_MBOX(2, SNIFF_ACT, 0, 3),
+ BT_MBOX(2, PAG, 3, 1),
+ BT_MBOX(2, INQUIRY, 4, 1),
+ BT_MBOX(2, CONN, 5, 1),
+ BT_MBOX(2, SNIFF_INTERVAL, 8, 5),
+ BT_MBOX(2, DISC, 13, 1),
+ BT_MBOX(2, SCO_TX_ACT, 16, 2),
+ BT_MBOX(2, SCO_RX_ACT, 18, 2),
+ BT_MBOX(2, ESCO_RE_TX, 20, 2),
+ BT_MBOX(2, SCO_DURATION, 24, 6),
+};
+
+enum iwl_bt_mxbox_dw3 {
+ BT_MBOX(3, SCO_STATE, 0, 1),
+ BT_MBOX(3, SNIFF_STATE, 1, 1),
+ BT_MBOX(3, A2DP_STATE, 2, 1),
+ BT_MBOX(3, ACL_STATE, 3, 1),
+ BT_MBOX(3, MSTR_STATE, 4, 1),
+ BT_MBOX(3, OBX_STATE, 5, 1),
+ BT_MBOX(3, OPEN_CON_2, 8, 2),
+ BT_MBOX(3, TRAFFIC_LOAD, 10, 2),
+ BT_MBOX(3, CHL_SEQN_LSB, 12, 1),
+ BT_MBOX(3, INBAND_P, 13, 1),
+ BT_MBOX(3, MSG_TYPE_2, 16, 3),
+ BT_MBOX(3, SSN_2, 19, 2),
+ BT_MBOX(3, UPDATE_REQUEST, 21, 1),
+};
+
+#define BT_MBOX_MSG(_notif, _num, _field) \
+ ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
+ >> BT_MBOX##_num##_##_field##_POS)
+
+enum iwl_bt_activity_grading {
+ BT_OFF = 0,
+ BT_ON_NO_CONNECTION = 1,
+ BT_LOW_TRAFFIC = 2,
+ BT_HIGH_TRAFFIC = 3,
+
+ BT_MAX_AG,
+}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
+
+enum iwl_bt_ci_compliance {
+ BT_CI_COMPLIANCE_NONE = 0,
+ BT_CI_COMPLIANCE_PRIMARY = 1,
+ BT_CI_COMPLIANCE_SECONDARY = 2,
+ BT_CI_COMPLIANCE_BOTH = 3,
+}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */
+
+#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \
+ (_ttc_rrc_status & BIT(_phy_id))
+
+#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \
+ ((_ttc_rrc_status >> 4) & BIT(_phy_id))
+
+/**
+ * struct iwl_bt_coex_profile_notif - notification about BT coex
+ * @mbox_msg: message from BT to WiFi
+ * @msg_idx: the index of the message
+ * @bt_ci_compliance: enum %iwl_bt_ci_compliance
+ * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type
+ * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type
+ * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY
+ */
+struct iwl_bt_coex_profile_notif {
+ __le32 mbox_msg[4];
+ __le32 msg_idx;
+ __le32 bt_ci_compliance;
+
+ __le32 primary_ch_lut;
+ __le32 secondary_ch_lut;
+ __le32 bt_activity_grading;
+ u8 ttc_rrc_status;
+ u8 reserved[3];
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */
+
+enum iwl_bt_coex_prio_table_event {
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
+ BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4,
+ BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5,
+ BT_COEX_PRIO_TBL_EVT_DTIM = 6,
+ BT_COEX_PRIO_TBL_EVT_SCAN52 = 7,
+ BT_COEX_PRIO_TBL_EVT_SCAN24 = 8,
+ BT_COEX_PRIO_TBL_EVT_IDLE = 9,
+ BT_COEX_PRIO_TBL_EVT_MAX = 16,
+}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */
+
+enum iwl_bt_coex_prio_table_prio {
+ BT_COEX_PRIO_TBL_DISABLED = 0,
+ BT_COEX_PRIO_TBL_PRIO_LOW = 1,
+ BT_COEX_PRIO_TBL_PRIO_HIGH = 2,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS = 3,
+ BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4,
+ BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5,
+ BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6,
+ BT_COEX_PRIO_TBL_MAX = 8,
+}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */
+
+#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0)
+#define BT_COEX_PRIO_TBL_PRIO_POS (1)
+#define BT_COEX_PRIO_TBL_RESERVED_POS (4)
+
+/**
+ * struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex
+ * @prio_tbl:
+ */
+struct iwl_bt_coex_prio_tbl_cmd {
+ u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
+} __packed;
+
+/**
+ * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command
+ * @bt_primary_ci:
+ * @bt_secondary_ci:
+ * @co_run_bw_primary:
+ * @co_run_bw_secondary:
+ * @primary_ch_phy_id:
+ * @secondary_ch_phy_id:
+ *
+ * Used for BT_COEX_CI command
+ */
+struct iwl_bt_coex_ci_cmd_old {
+ __le64 bt_primary_ci;
+ __le64 bt_secondary_ci;
+
+ u8 co_run_bw_primary;
+ u8 co_run_bw_secondary;
+ u8 primary_ch_phy_id;
+ u8 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_profile_notif_old - notification about BT coex
+ * @mbox_msg: message from BT to WiFi
+ * @msg_idx: the index of the message
+ * @bt_status: 0 - off, 1 - on
+ * @bt_open_conn: number of BT connections open
+ * @bt_traffic_load: load of BT traffic
+ * @bt_agg_traffic_load: aggregated load of BT traffic
+ * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @primary_ch_lut: LUT used for primary channel
+ * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ */
+struct iwl_bt_coex_profile_notif_old {
+ __le32 mbox_msg[4];
+ __le32 msg_idx;
+ u8 bt_status;
+ u8 bt_open_conn;
+ u8 bt_traffic_load;
+ u8 bt_agg_traffic_load;
+ u8 bt_ci_compliance;
+ u8 ttc_enabled;
+ u8 rrc_enabled;
+ u8 reserved;
+
+ __le32 primary_ch_lut;
+ __le32 secondary_ch_lut;
+ __le32 bt_activity_grading;
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
+
+#endif /* __fw_api_bt_coex_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
new file mode 100644
index 000000000000..62b9a0a96700
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
@@ -0,0 +1,438 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_d3_h__
+#define __fw_api_d3_h__
+
+/**
+ * enum iwl_d3_wakeup_flags - D3 manager wakeup flags
+ * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert
+ */
+enum iwl_d3_wakeup_flags {
+ IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0),
+}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */
+
+/**
+ * struct iwl_d3_manager_config - D3 manager configuration command
+ * @min_sleep_time: minimum sleep time (in usec)
+ * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags
+ * @wakeup_host_timer: force wakeup after this many seconds
+ *
+ * The structure is used for the D3_CONFIG_CMD command.
+ */
+struct iwl_d3_manager_config {
+ __le32 min_sleep_time;
+ __le32 wakeup_flags;
+ __le32 wakeup_host_timer;
+} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */
+
+
+/* TODO: OFFLOADS_QUERY_API_S_VER_1 */
+
+/**
+ * enum iwl_d3_proto_offloads - enabled protocol offloads
+ * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled
+ * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled
+ * @IWL_D3_PROTO_IPV4_VALID: IPv4 data is valid
+ * @IWL_D3_PROTO_IPV6_VALID: IPv6 data is valid
+ */
+enum iwl_proto_offloads {
+ IWL_D3_PROTO_OFFLOAD_ARP = BIT(0),
+ IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
+ IWL_D3_PROTO_IPV4_VALID = BIT(2),
+ IWL_D3_PROTO_IPV6_VALID = BIT(3),
+};
+
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12
+
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2
+
+/**
+ * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
+ * @enabled: enable flags
+ * @remote_ipv4_addr: remote address to answer to (or zero if all)
+ * @host_ipv4_addr: our IPv4 address to respond to queries for
+ * @arp_mac_addr: our MAC address for ARP responses
+ * @reserved: unused
+ */
+struct iwl_proto_offload_cmd_common {
+ __le32 enabled;
+ __be32 remote_ipv4_addr;
+ __be32 host_ipv4_addr;
+ u8 arp_mac_addr[ETH_ALEN];
+ __le16 reserved;
+} __packed;
+
+/**
+ * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ * for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor solicitation response MAC address
+ */
+struct iwl_proto_offload_cmd_v1 {
+ struct iwl_proto_offload_cmd_common common;
+ u8 remote_ipv6_addr[16];
+ u8 solicited_node_ipv6_addr[16];
+ u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16];
+ u8 ndp_mac_addr[ETH_ALEN];
+ __le16 reserved2;
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
+
+/**
+ * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ * for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor solicitation response MAC address
+ */
+struct iwl_proto_offload_cmd_v2 {
+ struct iwl_proto_offload_cmd_common common;
+ u8 remote_ipv6_addr[16];
+ u8 solicited_node_ipv6_addr[16];
+ u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16];
+ u8 ndp_mac_addr[ETH_ALEN];
+ u8 numValidIPv6Addresses;
+ u8 reserved2[3];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
+
+struct iwl_ns_config {
+ struct in6_addr source_ipv6_addr;
+ struct in6_addr dest_ipv6_addr;
+ u8 target_mac_addr[ETH_ALEN];
+ __le16 reserved;
+} __packed; /* NS_OFFLOAD_CONFIG */
+
+struct iwl_targ_addr {
+ struct in6_addr addr;
+ __le32 config_num;
+} __packed; /* TARGET_IPV6_ADDRESS */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_small {
+ struct iwl_proto_offload_cmd_common common;
+ __le32 num_valid_ipv6_addrs;
+ struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S];
+ struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_large {
+ struct iwl_proto_offload_cmd_common common;
+ __le32 num_valid_ipv6_addrs;
+ struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L];
+ struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
+
+/*
+ * WOWLAN_PATTERNS
+ */
+#define IWL_WOWLAN_MIN_PATTERN_LEN 16
+#define IWL_WOWLAN_MAX_PATTERN_LEN 128
+
+struct iwl_wowlan_pattern {
+ u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
+ u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN];
+ u8 mask_size;
+ u8 pattern_size;
+ __le16 reserved;
+} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */
+
+#define IWL_WOWLAN_MAX_PATTERNS 20
+
+struct iwl_wowlan_patterns_cmd {
+ __le32 n_patterns;
+ struct iwl_wowlan_pattern patterns[];
+} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */
+
+enum iwl_wowlan_wakeup_filters {
+ IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0),
+ IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1),
+ IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2),
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3),
+ IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4),
+ IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5),
+ IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6),
+ IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7),
+ IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8),
+ IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9),
+ IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10),
+ IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11),
+ IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12),
+ IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13),
+ IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14),
+ IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15),
+ IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16),
+}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
+
+enum iwl_wowlan_flags {
+ IS_11W_ASSOC = BIT(0),
+ ENABLE_L3_FILTERING = BIT(1),
+ ENABLE_NBNS_FILTERING = BIT(2),
+ ENABLE_DHCP_FILTERING = BIT(3),
+};
+
+struct iwl_wowlan_config_cmd {
+ __le32 wakeup_filter;
+ __le16 non_qos_seq;
+ __le16 qos_seq[8];
+ u8 wowlan_ba_teardown_tids;
+ u8 is_11n_connection;
+ u8 offloading_tid;
+ u8 flags;
+ u8 reserved[2];
+} __packed; /* WOWLAN_CONFIG_API_S_VER_4 */
+
+/*
+ * WOWLAN_TSC_RSC_PARAMS
+ */
+#define IWL_NUM_RSC 16
+
+struct tkip_sc {
+ __le16 iv16;
+ __le16 pad;
+ __le32 iv32;
+} __packed; /* TKIP_SC_API_U_VER_1 */
+
+struct iwl_tkip_rsc_tsc {
+ struct tkip_sc unicast_rsc[IWL_NUM_RSC];
+ struct tkip_sc multicast_rsc[IWL_NUM_RSC];
+ struct tkip_sc tsc;
+} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */
+
+struct aes_sc {
+ __le64 pn;
+} __packed; /* TKIP_AES_SC_API_U_VER_1 */
+
+struct iwl_aes_rsc_tsc {
+ struct aes_sc unicast_rsc[IWL_NUM_RSC];
+ struct aes_sc multicast_rsc[IWL_NUM_RSC];
+ struct aes_sc tsc;
+} __packed; /* AES_TSC_RSC_API_S_VER_1 */
+
+union iwl_all_tsc_rsc {
+ struct iwl_tkip_rsc_tsc tkip;
+ struct iwl_aes_rsc_tsc aes;
+}; /* ALL_TSC_RSC_API_S_VER_2 */
+
+struct iwl_wowlan_rsc_tsc_params_cmd {
+ union iwl_all_tsc_rsc all_tsc_rsc;
+} __packed; /* ALL_TSC_RSC_API_S_VER_2 */
+
+#define IWL_MIC_KEY_SIZE 8
+struct iwl_mic_keys {
+ u8 tx[IWL_MIC_KEY_SIZE];
+ u8 rx_unicast[IWL_MIC_KEY_SIZE];
+ u8 rx_mcast[IWL_MIC_KEY_SIZE];
+} __packed; /* MIC_KEYS_API_S_VER_1 */
+
+#define IWL_P1K_SIZE 5
+struct iwl_p1k_cache {
+ __le16 p1k[IWL_P1K_SIZE];
+} __packed;
+
+#define IWL_NUM_RX_P1K_CACHE 2
+
+struct iwl_wowlan_tkip_params_cmd {
+ struct iwl_mic_keys mic_keys;
+ struct iwl_p1k_cache tx;
+ struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE];
+ struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE];
+} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */
+
+#define IWL_KCK_MAX_SIZE 32
+#define IWL_KEK_MAX_SIZE 32
+
+struct iwl_wowlan_kek_kck_material_cmd {
+ u8 kck[IWL_KCK_MAX_SIZE];
+ u8 kek[IWL_KEK_MAX_SIZE];
+ __le16 kck_len;
+ __le16 kek_len;
+ __le64 replay_ctr;
+} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */
+
+#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87
+
+enum iwl_wowlan_rekey_status {
+ IWL_WOWLAN_REKEY_POST_REKEY = 0,
+ IWL_WOWLAN_REKEY_WHILE_REKEY = 1,
+}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */
+
+enum iwl_wowlan_wakeup_reason {
+ IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0,
+ IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0),
+ IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1),
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2),
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3),
+ IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4),
+ IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5),
+ IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6),
+ IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7),
+ IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8),
+ IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9),
+ IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10),
+ IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11),
+ IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12),
+ IWL_WOWLAN_WAKEUP_BY_IOAC_MAGIC_PACKET = BIT(13),
+ IWL_WOWLAN_WAKEUP_BY_D3_WAKEUP_HOST_TIMER = BIT(14),
+ IWL_WOWLAN_WAKEUP_BY_RXFRAME_FILTERED_IN = BIT(15),
+ IWL_WOWLAN_WAKEUP_BY_BEACON_FILTERED_IN = BIT(16),
+
+}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
+
+struct iwl_wowlan_gtk_status {
+ u8 key_index;
+ u8 reserved[3];
+ u8 decrypt_key[16];
+ u8 tkip_mic_key[8];
+ struct iwl_wowlan_rsc_tsc_params_cmd rsc;
+} __packed;
+
+struct iwl_wowlan_status {
+ struct iwl_wowlan_gtk_status gtk;
+ __le64 replay_ctr;
+ __le16 pattern_number;
+ __le16 non_qos_seq_ctr;
+ __le16 qos_seq_ctr[8];
+ __le32 wakeup_reasons;
+ __le32 num_of_gtk_rekeys;
+ __le32 transmitted_ndps;
+ __le32 received_beacons;
+ __le32 wake_packet_length;
+ __le32 wake_packet_bufsize;
+ u8 wake_packet[]; /* can be truncated from _length to _bufsize */
+} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */
+
+#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64
+#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128
+#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048
+
+struct iwl_tcp_packet_info {
+ __le16 tcp_pseudo_header_checksum;
+ __le16 tcp_payload_length;
+} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */
+
+struct iwl_tcp_packet {
+ struct iwl_tcp_packet_info info;
+ u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
+ u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN];
+} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
+
+struct iwl_remote_wake_packet {
+ struct iwl_tcp_packet_info info;
+ u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
+ u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN];
+} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
+
+struct iwl_wowlan_remote_wake_config {
+ __le32 connection_max_time; /* unused */
+ /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */
+ u8 max_syn_retries;
+ u8 max_data_retries;
+ u8 tcp_syn_ack_timeout;
+ u8 tcp_ack_timeout;
+
+ struct iwl_tcp_packet syn_tx;
+ struct iwl_tcp_packet synack_rx;
+ struct iwl_tcp_packet keepalive_ack_rx;
+ struct iwl_tcp_packet fin_tx;
+
+ struct iwl_remote_wake_packet keepalive_tx;
+ struct iwl_remote_wake_packet wake_rx;
+
+ /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */
+ u8 sequence_number_offset;
+ u8 sequence_number_length;
+ u8 token_offset;
+ u8 token_length;
+ /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */
+ __le32 initial_sequence_number;
+ __le16 keepalive_interval;
+ __le16 num_tokens;
+ u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS];
+} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */
+
+/* TODO: NetDetect API */
+
+#endif /* __fw_api_d3_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h
new file mode 100644
index 000000000000..95ac59d088b1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h
@@ -0,0 +1,387 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_mac_h__
+#define __fw_api_mac_h__
+
+/*
+ * The first MAC indices (starting from 0)
+ * are available to the driver, AUX follows
+ */
+#define MAC_INDEX_AUX 4
+#define MAC_INDEX_MIN_DRIVER 0
+#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX
+#define NUM_MAC_INDEX (MAC_INDEX_AUX + 1)
+
+enum iwl_ac {
+ AC_BK,
+ AC_BE,
+ AC_VI,
+ AC_VO,
+ AC_NUM,
+};
+
+/**
+ * enum iwl_mac_protection_flags - MAC context flags
+ * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames,
+ * this will require CCK RTS/CTS2self.
+ * RTS/CTS will protect full burst time.
+ * @MAC_PROT_FLG_HT_PROT: enable HT protection
+ * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions
+ * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self
+ */
+enum iwl_mac_protection_flags {
+ MAC_PROT_FLG_TGG_PROTECT = BIT(3),
+ MAC_PROT_FLG_HT_PROT = BIT(23),
+ MAC_PROT_FLG_FAT_PROT = BIT(24),
+ MAC_PROT_FLG_SELF_CTS_EN = BIT(30),
+};
+
+#define MAC_FLG_SHORT_SLOT BIT(4)
+#define MAC_FLG_SHORT_PREAMBLE BIT(5)
+
+/**
+ * enum iwl_mac_types - Supported MAC types
+ * @FW_MAC_TYPE_FIRST: lowest supported MAC type
+ * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal)
+ * @FW_MAC_TYPE_LISTENER: monitor MAC type (?)
+ * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS
+ * @FW_MAC_TYPE_IBSS: IBSS
+ * @FW_MAC_TYPE_BSS_STA: BSS (managed) station
+ * @FW_MAC_TYPE_P2P_DEVICE: P2P Device
+ * @FW_MAC_TYPE_P2P_STA: P2P client
+ * @FW_MAC_TYPE_GO: P2P GO
+ * @FW_MAC_TYPE_TEST: ?
+ * @FW_MAC_TYPE_MAX: highest support MAC type
+ */
+enum iwl_mac_types {
+ FW_MAC_TYPE_FIRST = 1,
+ FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST,
+ FW_MAC_TYPE_LISTENER,
+ FW_MAC_TYPE_PIBSS,
+ FW_MAC_TYPE_IBSS,
+ FW_MAC_TYPE_BSS_STA,
+ FW_MAC_TYPE_P2P_DEVICE,
+ FW_MAC_TYPE_P2P_STA,
+ FW_MAC_TYPE_GO,
+ FW_MAC_TYPE_TEST,
+ FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
+}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
+
+/**
+ * enum iwl_tsf_id - TSF hw timer ID
+ * @TSF_ID_A: use TSF A
+ * @TSF_ID_B: use TSF B
+ * @TSF_ID_C: use TSF C
+ * @TSF_ID_D: use TSF D
+ * @NUM_TSF_IDS: number of TSF timers available
+ */
+enum iwl_tsf_id {
+ TSF_ID_A = 0,
+ TSF_ID_B = 1,
+ TSF_ID_C = 2,
+ TSF_ID_D = 3,
+ NUM_TSF_IDS = 4,
+}; /* TSF_ID_API_E_VER_1 */
+
+/**
+ * struct iwl_mac_data_ap - configuration data for AP MAC context
+ * @beacon_time: beacon transmit time in system time
+ * @beacon_tsf: beacon transmit time in TSF
+ * @bi: beacon interval in TU
+ * @bi_reciprocal: 2^32 / bi
+ * @dtim_interval: dtim transmit time in TU
+ * @dtim_reciprocal: 2^32 / dtim_interval
+ * @mcast_qid: queue ID for multicast traffic
+ * @beacon_template: beacon template ID
+ */
+struct iwl_mac_data_ap {
+ __le32 beacon_time;
+ __le64 beacon_tsf;
+ __le32 bi;
+ __le32 bi_reciprocal;
+ __le32 dtim_interval;
+ __le32 dtim_reciprocal;
+ __le32 mcast_qid;
+ __le32 beacon_template;
+} __packed; /* AP_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_ibss - configuration data for IBSS MAC context
+ * @beacon_time: beacon transmit time in system time
+ * @beacon_tsf: beacon transmit time in TSF
+ * @bi: beacon interval in TU
+ * @bi_reciprocal: 2^32 / bi
+ * @beacon_template: beacon template ID
+ */
+struct iwl_mac_data_ibss {
+ __le32 beacon_time;
+ __le64 beacon_tsf;
+ __le32 bi;
+ __le32 bi_reciprocal;
+ __le32 beacon_template;
+} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_sta - configuration data for station MAC context
+ * @is_assoc: 1 for associated state, 0 otherwise
+ * @dtim_time: DTIM arrival time in system time
+ * @dtim_tsf: DTIM arrival time in TSF
+ * @bi: beacon interval in TU, applicable only when associated
+ * @bi_reciprocal: 2^32 / bi , applicable only when associated
+ * @dtim_interval: DTIM interval in TU, applicable only when associated
+ * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated
+ * @listen_interval: in beacon intervals, applicable only when associated
+ * @assoc_id: unique ID assigned by the AP during association
+ */
+struct iwl_mac_data_sta {
+ __le32 is_assoc;
+ __le32 dtim_time;
+ __le64 dtim_tsf;
+ __le32 bi;
+ __le32 bi_reciprocal;
+ __le32 dtim_interval;
+ __le32 dtim_reciprocal;
+ __le32 listen_interval;
+ __le32 assoc_id;
+ __le32 assoc_beacon_arrive_time;
+} __packed; /* STA_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_go - configuration data for P2P GO MAC context
+ * @ap: iwl_mac_data_ap struct with most config data
+ * @ctwin: client traffic window in TU (period after TBTT when GO is present).
+ * 0 indicates that there is no CT window.
+ * @opp_ps_enabled: indicate that opportunistic PS allowed
+ */
+struct iwl_mac_data_go {
+ struct iwl_mac_data_ap ap;
+ __le32 ctwin;
+ __le32 opp_ps_enabled;
+} __packed; /* GO_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context
+ * @sta: iwl_mac_data_sta struct with most config data
+ * @ctwin: client traffic window in TU (period after TBTT when GO is present).
+ * 0 indicates that there is no CT window.
+ */
+struct iwl_mac_data_p2p_sta {
+ struct iwl_mac_data_sta sta;
+ __le32 ctwin;
+} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_pibss - Pseudo IBSS config data
+ * @stats_interval: interval in TU between statistics notifications to host.
+ */
+struct iwl_mac_data_pibss {
+ __le32 stats_interval;
+} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */
+
+/*
+ * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC
+ * context.
+ * @is_disc_extended: if set to true, P2P Device discoverability is enabled on
+ * other channels as well. This should be to true only in case that the
+ * device is discoverable and there is an active GO. Note that setting this
+ * field when not needed, will increase the number of interrupts and have
+ * effect on the platform power, as this setting opens the Rx filters on
+ * all macs.
+ */
+struct iwl_mac_data_p2p_dev {
+ __le32 is_disc_extended;
+} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */
+
+/**
+ * enum iwl_mac_filter_flags - MAC context filter flags
+ * @MAC_FILTER_IN_PROMISC: accept all data frames
+ * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and
+ * control frames to the host
+ * @MAC_FILTER_ACCEPT_GRP: accept multicast frames
+ * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
+ * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames
+ * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host
+ * (in station mode when associated)
+ * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames
+ * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames
+ * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host
+ */
+enum iwl_mac_filter_flags {
+ MAC_FILTER_IN_PROMISC = BIT(0),
+ MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1),
+ MAC_FILTER_ACCEPT_GRP = BIT(2),
+ MAC_FILTER_DIS_DECRYPT = BIT(3),
+ MAC_FILTER_DIS_GRP_DECRYPT = BIT(4),
+ MAC_FILTER_IN_BEACON = BIT(6),
+ MAC_FILTER_OUT_BCAST = BIT(8),
+ MAC_FILTER_IN_CRC32 = BIT(11),
+ MAC_FILTER_IN_PROBE_REQUEST = BIT(12),
+};
+
+/**
+ * enum iwl_mac_qos_flags - QoS flags
+ * @MAC_QOS_FLG_UPDATE_EDCA: ?
+ * @MAC_QOS_FLG_TGN: HT is enabled
+ * @MAC_QOS_FLG_TXOP_TYPE: ?
+ *
+ */
+enum iwl_mac_qos_flags {
+ MAC_QOS_FLG_UPDATE_EDCA = BIT(0),
+ MAC_QOS_FLG_TGN = BIT(1),
+ MAC_QOS_FLG_TXOP_TYPE = BIT(4),
+};
+
+/**
+ * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD
+ * @cw_min: Contention window, start value in numbers of slots.
+ * Should be a power-of-2, minus 1. Device's default is 0x0f.
+ * @cw_max: Contention window, max value in numbers of slots.
+ * Should be a power-of-2, minus 1. Device's default is 0x3f.
+ * @aifsn: Number of slots in Arbitration Interframe Space (before
+ * performing random backoff timing prior to Tx). Device default 1.
+ * @fifos_mask: FIFOs used by this MAC for this AC
+ * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0.
+ *
+ * One instance of this config struct for each of 4 EDCA access categories
+ * in struct iwl_qosparam_cmd.
+ *
+ * Device will automatically increase contention window by (2*CW) + 1 for each
+ * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW
+ * value, to cap the CW value.
+ */
+struct iwl_ac_qos {
+ __le16 cw_min;
+ __le16 cw_max;
+ u8 aifsn;
+ u8 fifos_mask;
+ __le16 edca_txop;
+} __packed; /* AC_QOS_API_S_VER_2 */
+
+/**
+ * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts
+ * ( MAC_CONTEXT_CMD = 0x28 )
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @mac_type: one of FW_MAC_TYPE_*
+ * @tsd_id: TSF HW timer, one of TSF_ID_*
+ * @node_addr: MAC address
+ * @bssid_addr: BSSID
+ * @cck_rates: basic rates available for CCK
+ * @ofdm_rates: basic rates available for OFDM
+ * @protection_flags: combination of MAC_PROT_FLG_FLAG_*
+ * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise
+ * @short_slot: 0x10 for enabling short slots, 0 otherwise
+ * @filter_flags: combination of MAC_FILTER_*
+ * @qos_flags: from MAC_QOS_FLG_*
+ * @ac: one iwl_mac_qos configuration for each AC
+ * @mac_specific: one of struct iwl_mac_data_*, according to mac_type
+ */
+struct iwl_mac_ctx_cmd {
+ /* COMMON_INDEX_HDR_API_S_VER_1 */
+ __le32 id_and_color;
+ __le32 action;
+ /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */
+ __le32 mac_type;
+ __le32 tsf_id;
+ u8 node_addr[6];
+ __le16 reserved_for_node_addr;
+ u8 bssid_addr[6];
+ __le16 reserved_for_bssid_addr;
+ __le32 cck_rates;
+ __le32 ofdm_rates;
+ __le32 protection_flags;
+ __le32 cck_short_preamble;
+ __le32 short_slot;
+ __le32 filter_flags;
+ /* MAC_QOS_PARAM_API_S_VER_1 */
+ __le32 qos_flags;
+ struct iwl_ac_qos ac[AC_NUM+1];
+ /* MAC_CONTEXT_COMMON_DATA_API_S */
+ union {
+ struct iwl_mac_data_ap ap;
+ struct iwl_mac_data_go go;
+ struct iwl_mac_data_sta sta;
+ struct iwl_mac_data_p2p_sta p2p_sta;
+ struct iwl_mac_data_p2p_dev p2p_dev;
+ struct iwl_mac_data_pibss pibss;
+ struct iwl_mac_data_ibss ibss;
+ };
+} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
+
+static inline u32 iwl_mvm_reciprocal(u32 v)
+{
+ if (!v)
+ return 0;
+ return 0xFFFFFFFF / v;
+}
+
+#define IWL_NONQOS_SEQ_GET 0x1
+#define IWL_NONQOS_SEQ_SET 0x2
+struct iwl_nonqos_seq_query_cmd {
+ __le32 get_set_flag;
+ __le32 mac_id_n_color;
+ __le16 value;
+ __le16 reserved;
+} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */
+
+#endif /* __fw_api_mac_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h
new file mode 100644
index 000000000000..65a7c8a4cacf
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h
@@ -0,0 +1,467 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_power_h__
+#define __fw_api_power_h__
+
+/* Power Management Commands, Responses, Notifications */
+
+/**
+ * enum iwl_ltr_config_flags - masks for LTR config command flags
+ * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status
+ * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow
+ * memory access
+ * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR
+ * reg change
+ * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from
+ * D0 to D3
+ * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register
+ * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register
+ * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD
+ */
+enum iwl_ltr_config_flags {
+ LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0),
+ LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1),
+ LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2),
+ LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3),
+ LTR_CFG_FLAG_SW_SET_SHORT = BIT(4),
+ LTR_CFG_FLAG_SW_SET_LONG = BIT(5),
+ LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6),
+};
+
+/**
+ * struct iwl_ltr_config_cmd_v1 - configures the LTR
+ * @flags: See %enum iwl_ltr_config_flags
+ */
+struct iwl_ltr_config_cmd_v1 {
+ __le32 flags;
+ __le32 static_long;
+ __le32 static_short;
+} __packed; /* LTR_CAPABLE_API_S_VER_1 */
+
+#define LTR_VALID_STATES_NUM 4
+
+/**
+ * struct iwl_ltr_config_cmd - configures the LTR
+ * @flags: See %enum iwl_ltr_config_flags
+ * @static_long:
+ * @static_short:
+ * @ltr_cfg_values:
+ * @ltr_short_idle_timeout:
+ */
+struct iwl_ltr_config_cmd {
+ __le32 flags;
+ __le32 static_long;
+ __le32 static_short;
+ __le32 ltr_cfg_values[LTR_VALID_STATES_NUM];
+ __le32 ltr_short_idle_timeout;
+} __packed; /* LTR_CAPABLE_API_S_VER_2 */
+
+/* Radio LP RX Energy Threshold measured in dBm */
+#define POWER_LPRX_RSSI_THRESHOLD 75
+#define POWER_LPRX_RSSI_THRESHOLD_MAX 94
+#define POWER_LPRX_RSSI_THRESHOLD_MIN 30
+
+/**
+ * enum iwl_power_flags - masks for power table command flags
+ * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
+ * receiver and transmitter. '0' - does not allow.
+ * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
+ * '1' Driver enables PM (use rest of parameters)
+ * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
+ * '1' PM could sleep over DTIM till listen Interval.
+ * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
+ * access categories are both delivery and trigger enabled.
+ * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
+ * PBW Snoozing enabled
+ * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
+ * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
+ * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
+ * detection enablement
+*/
+enum iwl_power_flags {
+ POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
+ POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1),
+ POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2),
+ POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5),
+ POWER_FLAGS_BT_SCO_ENA = BIT(8),
+ POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
+ POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
+ POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12),
+};
+
+#define IWL_POWER_VEC_SIZE 5
+
+/**
+ * struct iwl_powertable_cmd - legacy power command. Beside old API support this
+ * is used also with a new power API for device wide power settings.
+ * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
+ *
+ * @flags: Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
+ * Minimum allowed:- 3 * DTIM. Keep alive period must be
+ * set regardless of power scheme or current power state.
+ * FW use this value also when PM is disabled.
+ * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
+ * PSM transition - legacy PM
+ * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
+ * PSM transition - legacy PM
+ * @sleep_interval: not in use
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ * is set. For example, if it is required to skip over
+ * one DTIM, this value need to be set to 2 (DTIM periods).
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ * Default: 80dbm
+ */
+struct iwl_powertable_cmd {
+ /* PM_POWER_TABLE_CMD_API_S_VER_6 */
+ __le16 flags;
+ u8 keep_alive_seconds;
+ u8 debug_flags;
+ __le32 rx_data_timeout;
+ __le32 tx_data_timeout;
+ __le32 sleep_interval[IWL_POWER_VEC_SIZE];
+ __le32 skip_dtim_periods;
+ __le32 lprx_rssi_threshold;
+} __packed;
+
+/**
+ * enum iwl_device_power_flags - masks for device power command flags
+ * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
+ * receiver and transmitter. '0' - does not allow.
+*/
+enum iwl_device_power_flags {
+ DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
+};
+
+/**
+ * struct iwl_device_power_cmd - device wide power command.
+ * DEVICE_POWER_CMD = 0x77 (command, has simple generic response)
+ *
+ * @flags: Power table command flags from DEVICE_POWER_FLAGS_*
+ */
+struct iwl_device_power_cmd {
+ /* PM_POWER_TABLE_CMD_API_S_VER_6 */
+ __le16 flags;
+ __le16 reserved;
+} __packed;
+
+/**
+ * struct iwl_mac_power_cmd - New power command containing uAPSD support
+ * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
+ * @id_and_color: MAC contex identifier
+ * @flags: Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
+ * Minimum allowed:- 3 * DTIM. Keep alive period must be
+ * set regardless of power scheme or current power state.
+ * FW use this value also when PM is disabled.
+ * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
+ * PSM transition - legacy PM
+ * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
+ * PSM transition - legacy PM
+ * @sleep_interval: not in use
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ * is set. For example, if it is required to skip over
+ * one DTIM, this value need to be set to 2 (DTIM periods).
+ * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
+ * PSM transition - uAPSD
+ * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
+ * PSM transition - uAPSD
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ * Default: 80dbm
+ * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set
+ * @snooze_interval: Maximum time between attempts to retrieve buffered data
+ * from the AP [msec]
+ * @snooze_window: A window of time in which PBW snoozing insures that all
+ * packets received. It is also the minimum time from last
+ * received unicast RX packet, before client stops snoozing
+ * for data. [msec]
+ * @snooze_step: TBD
+ * @qndp_tid: TID client shall use for uAPSD QNDP triggers
+ * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for
+ * each corresponding AC.
+ * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
+ * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct
+ * values.
+ * @heavy_tx_thld_packets: TX threshold measured in number of packets
+ * @heavy_rx_thld_packets: RX threshold measured in number of packets
+ * @heavy_tx_thld_percentage: TX threshold measured in load's percentage
+ * @heavy_rx_thld_percentage: RX threshold measured in load's percentage
+ * @limited_ps_threshold:
+*/
+struct iwl_mac_power_cmd {
+ /* CONTEXT_DESC_API_T_VER_1 */
+ __le32 id_and_color;
+
+ /* CLIENT_PM_POWER_TABLE_S_VER_1 */
+ __le16 flags;
+ __le16 keep_alive_seconds;
+ __le32 rx_data_timeout;
+ __le32 tx_data_timeout;
+ __le32 rx_data_timeout_uapsd;
+ __le32 tx_data_timeout_uapsd;
+ u8 lprx_rssi_threshold;
+ u8 skip_dtim_periods;
+ __le16 snooze_interval;
+ __le16 snooze_window;
+ u8 snooze_step;
+ u8 qndp_tid;
+ u8 uapsd_ac_flags;
+ u8 uapsd_max_sp;
+ u8 heavy_tx_thld_packets;
+ u8 heavy_rx_thld_packets;
+ u8 heavy_tx_thld_percentage;
+ u8 heavy_rx_thld_percentage;
+ u8 limited_ps_threshold;
+ u8 reserved;
+} __packed;
+
+/*
+ * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
+ * associated AP is identified as improperly implementing uAPSD protocol.
+ * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78
+ * @sta_id: index of station in uCode's station table - associated AP ID in
+ * this context.
+ */
+struct iwl_uapsd_misbehaving_ap_notif {
+ __le32 sta_id;
+ u8 mac_id;
+ u8 reserved[3];
+} __packed;
+
+/**
+ * struct iwl_reduce_tx_power_cmd - TX power reduction command
+ * REDUCE_TX_POWER_CMD = 0x9f
+ * @flags: (reserved for future implementation)
+ * @mac_context_id: id of the mac ctx for which we are reducing TX power.
+ * @pwr_restriction: TX power restriction in dBms.
+ */
+struct iwl_reduce_tx_power_cmd {
+ u8 flags;
+ u8 mac_context_id;
+ __le16 pwr_restriction;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */
+
+enum iwl_dev_tx_power_cmd_mode {
+ IWL_TX_POWER_MODE_SET_MAC = 0,
+ IWL_TX_POWER_MODE_SET_DEVICE = 1,
+ IWL_TX_POWER_MODE_SET_CHAINS = 2,
+}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_2 */;
+
+/**
+ * struct iwl_dev_tx_power_cmd_v2 - TX power reduction command
+ * @set_mode: see &enum iwl_dev_tx_power_cmd_mode
+ * @mac_context_id: id of the mac ctx for which we are reducing TX power.
+ * @pwr_restriction: TX power restriction in 1/8 dBms.
+ * @dev_24: device TX power restriction in 1/8 dBms
+ * @dev_52_low: device TX power restriction upper band - low
+ * @dev_52_high: device TX power restriction upper band - high
+ */
+struct iwl_dev_tx_power_cmd_v2 {
+ __le32 set_mode;
+ __le32 mac_context_id;
+ __le16 pwr_restriction;
+ __le16 dev_24;
+ __le16 dev_52_low;
+ __le16 dev_52_high;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_2 */
+
+#define IWL_NUM_CHAIN_LIMITS 2
+#define IWL_NUM_SUB_BANDS 5
+
+/**
+ * struct iwl_dev_tx_power_cmd - TX power reduction command
+ * @v2: version 2 of the command, embedded here for easier software handling
+ * @per_chain_restriction: per chain restrictions
+ */
+struct iwl_dev_tx_power_cmd {
+ /* v3 is just an extension of v2 - keep this here */
+ struct iwl_dev_tx_power_cmd_v2 v2;
+ __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS];
+} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */
+
+#define IWL_DEV_MAX_TX_POWER 0x7FFF
+
+/**
+ * struct iwl_beacon_filter_cmd
+ * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
+ * @id_and_color: MAC contex identifier
+ * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon
+ * to driver if delta in Energy values calculated for this and last
+ * passed beacon is greater than this threshold. Zero value means that
+ * the Energy change is ignored for beacon filtering, and beacon will
+ * not be forced to be sent to driver regardless of this delta. Typical
+ * energy delta 5dB.
+ * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state.
+ * Send beacon to driver if delta in Energy values calculated for this
+ * and last passed beacon is greater than this threshold. Zero value
+ * means that the Energy change is ignored for beacon filtering while in
+ * Roaming state, typical energy delta 1dB.
+ * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values
+ * calculated for current beacon is less than the threshold, use
+ * Roaming Energy Delta Threshold, otherwise use normal Energy Delta
+ * Threshold. Typical energy threshold is -72dBm.
+ * @bf_temp_threshold: This threshold determines the type of temperature
+ * filtering (Slow or Fast) that is selected (Units are in Celsuis):
+ * If the current temperature is above this threshold - Fast filter
+ * will be used, If the current temperature is below this threshold -
+ * Slow filter will be used.
+ * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values
+ * calculated for this and the last passed beacon is greater than this
+ * threshold. Zero value means that the temperature change is ignored for
+ * beacon filtering; beacons will not be forced to be sent to driver
+ * regardless of whether its temerature has been changed.
+ * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values
+ * calculated for this and the last passed beacon is greater than this
+ * threshold. Zero value means that the temperature change is ignored for
+ * beacon filtering; beacons will not be forced to be sent to driver
+ * regardless of whether its temerature has been changed.
+ * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
+ * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed
+ * for a specific period of time. Units: Beacons.
+ * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed
+ * for a longer period of time then this escape-timeout. Units: Beacons.
+ * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled.
+ */
+struct iwl_beacon_filter_cmd {
+ __le32 bf_energy_delta;
+ __le32 bf_roaming_energy_delta;
+ __le32 bf_roaming_state;
+ __le32 bf_temp_threshold;
+ __le32 bf_temp_fast_filter;
+ __le32 bf_temp_slow_filter;
+ __le32 bf_enable_beacon_filter;
+ __le32 bf_debug_flag;
+ __le32 bf_escape_timer;
+ __le32 ba_escape_timer;
+ __le32 ba_enable_beacon_abort;
+} __packed;
+
+/* Beacon filtering and beacon abort */
+#define IWL_BF_ENERGY_DELTA_DEFAULT 5
+#define IWL_BF_ENERGY_DELTA_D0I3 20
+#define IWL_BF_ENERGY_DELTA_MAX 255
+#define IWL_BF_ENERGY_DELTA_MIN 0
+
+#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1
+#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20
+#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255
+#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0
+
+#define IWL_BF_ROAMING_STATE_DEFAULT 72
+#define IWL_BF_ROAMING_STATE_D0I3 72
+#define IWL_BF_ROAMING_STATE_MAX 255
+#define IWL_BF_ROAMING_STATE_MIN 0
+
+#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112
+#define IWL_BF_TEMP_THRESHOLD_D0I3 112
+#define IWL_BF_TEMP_THRESHOLD_MAX 255
+#define IWL_BF_TEMP_THRESHOLD_MIN 0
+
+#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1
+#define IWL_BF_TEMP_FAST_FILTER_D0I3 1
+#define IWL_BF_TEMP_FAST_FILTER_MAX 255
+#define IWL_BF_TEMP_FAST_FILTER_MIN 0
+
+#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5
+#define IWL_BF_TEMP_SLOW_FILTER_D0I3 20
+#define IWL_BF_TEMP_SLOW_FILTER_MAX 255
+#define IWL_BF_TEMP_SLOW_FILTER_MIN 0
+
+#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
+
+#define IWL_BF_DEBUG_FLAG_DEFAULT 0
+#define IWL_BF_DEBUG_FLAG_D0I3 0
+
+#define IWL_BF_ESCAPE_TIMER_DEFAULT 0
+#define IWL_BF_ESCAPE_TIMER_D0I3 0
+#define IWL_BF_ESCAPE_TIMER_MAX 1024
+#define IWL_BF_ESCAPE_TIMER_MIN 0
+
+#define IWL_BA_ESCAPE_TIMER_DEFAULT 6
+#define IWL_BA_ESCAPE_TIMER_D0I3 6
+#define IWL_BA_ESCAPE_TIMER_D3 9
+#define IWL_BA_ESCAPE_TIMER_MAX 1024
+#define IWL_BA_ESCAPE_TIMER_MIN 0
+
+#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
+
+#define IWL_BF_CMD_CONFIG(mode) \
+ .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \
+ .bf_roaming_energy_delta = \
+ cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \
+ .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \
+ .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \
+ .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \
+ .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \
+ .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \
+ .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \
+ .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode)
+
+#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT)
+#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3)
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h
new file mode 100644
index 000000000000..ad9cc03e16c4
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h
@@ -0,0 +1,389 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_rs_h__
+#define __fw_api_rs_h__
+
+#include "fw-api-mac.h"
+
+/*
+ * These serve as indexes into
+ * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
+ * TODO: avoid overlap between legacy and HT rates
+ */
+enum {
+ IWL_RATE_1M_INDEX = 0,
+ IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
+ IWL_RATE_2M_INDEX,
+ IWL_RATE_5M_INDEX,
+ IWL_RATE_11M_INDEX,
+ IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
+ IWL_RATE_6M_INDEX,
+ IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+ IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX,
+ IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX,
+ IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX,
+ IWL_RATE_9M_INDEX,
+ IWL_RATE_12M_INDEX,
+ IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX,
+ IWL_RATE_18M_INDEX,
+ IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX,
+ IWL_RATE_24M_INDEX,
+ IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX,
+ IWL_RATE_36M_INDEX,
+ IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX,
+ IWL_RATE_48M_INDEX,
+ IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX,
+ IWL_RATE_54M_INDEX,
+ IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX,
+ IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX,
+ IWL_RATE_60M_INDEX,
+ IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX,
+ IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX,
+ IWL_RATE_MCS_8_INDEX,
+ IWL_RATE_MCS_9_INDEX,
+ IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX,
+ IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1,
+ IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1,
+};
+
+#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX)
+
+/* fw API values for legacy bit rates, both OFDM and CCK */
+enum {
+ IWL_RATE_6M_PLCP = 13,
+ IWL_RATE_9M_PLCP = 15,
+ IWL_RATE_12M_PLCP = 5,
+ IWL_RATE_18M_PLCP = 7,
+ IWL_RATE_24M_PLCP = 9,
+ IWL_RATE_36M_PLCP = 11,
+ IWL_RATE_48M_PLCP = 1,
+ IWL_RATE_54M_PLCP = 3,
+ IWL_RATE_1M_PLCP = 10,
+ IWL_RATE_2M_PLCP = 20,
+ IWL_RATE_5M_PLCP = 55,
+ IWL_RATE_11M_PLCP = 110,
+ IWL_RATE_INVM_PLCP = -1,
+};
+
+/*
+ * rate_n_flags bit fields
+ *
+ * The 32-bit value has different layouts in the low 8 bites depending on the
+ * format. There are three formats, HT, VHT and legacy (11abg, with subformats
+ * for CCK and OFDM).
+ *
+ * High-throughput (HT) rate format
+ * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM)
+ * Very High-throughput (VHT) rate format
+ * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM)
+ * Legacy OFDM rate format for bits 7:0
+ * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM)
+ * Legacy CCK rate format for bits 7:0:
+ * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK)
+ */
+
+/* Bit 8: (1) HT format, (0) legacy or VHT format */
+#define RATE_MCS_HT_POS 8
+#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS)
+
+/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */
+#define RATE_MCS_CCK_POS 9
+#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS)
+
+/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */
+#define RATE_MCS_VHT_POS 26
+#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS)
+
+
+/*
+ * High-throughput (HT) rate format for bits 7:0
+ *
+ * 2-0: MCS rate base
+ * 0) 6 Mbps
+ * 1) 12 Mbps
+ * 2) 18 Mbps
+ * 3) 24 Mbps
+ * 4) 36 Mbps
+ * 5) 48 Mbps
+ * 6) 54 Mbps
+ * 7) 60 Mbps
+ * 4-3: 0) Single stream (SISO)
+ * 1) Dual stream (MIMO)
+ * 2) Triple stream (MIMO)
+ * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data
+ * (bits 7-6 are zero)
+ *
+ * Together the low 5 bits work out to the MCS index because we don't
+ * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two
+ * streams and 16-23 have three streams. We could also support MCS 32
+ * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.)
+ */
+#define RATE_HT_MCS_RATE_CODE_MSK 0x7
+#define RATE_HT_MCS_NSS_POS 3
+#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS)
+
+/* Bit 10: (1) Use Green Field preamble */
+#define RATE_HT_MCS_GF_POS 10
+#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS)
+
+#define RATE_HT_MCS_INDEX_MSK 0x3f
+
+/*
+ * Very High-throughput (VHT) rate format for bits 7:0
+ *
+ * 3-0: VHT MCS (0-9)
+ * 5-4: number of streams - 1:
+ * 0) Single stream (SISO)
+ * 1) Dual stream (MIMO)
+ * 2) Triple stream (MIMO)
+ */
+
+/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */
+#define RATE_VHT_MCS_RATE_CODE_MSK 0xf
+#define RATE_VHT_MCS_NSS_POS 4
+#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS)
+
+/*
+ * Legacy OFDM rate format for bits 7:0
+ *
+ * 3-0: 0xD) 6 Mbps
+ * 0xF) 9 Mbps
+ * 0x5) 12 Mbps
+ * 0x7) 18 Mbps
+ * 0x9) 24 Mbps
+ * 0xB) 36 Mbps
+ * 0x1) 48 Mbps
+ * 0x3) 54 Mbps
+ * (bits 7-4 are 0)
+ *
+ * Legacy CCK rate format for bits 7:0:
+ * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK):
+ *
+ * 6-0: 10) 1 Mbps
+ * 20) 2 Mbps
+ * 55) 5.5 Mbps
+ * 110) 11 Mbps
+ * (bit 7 is 0)
+ */
+#define RATE_LEGACY_RATE_MSK 0xff
+
+
+/*
+ * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz
+ * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT
+ */
+#define RATE_MCS_CHAN_WIDTH_POS 11
+#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS)
+
+/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */
+#define RATE_MCS_SGI_POS 13
+#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS)
+
+/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */
+#define RATE_MCS_ANT_POS 14
+#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \
+ RATE_MCS_ANT_B_MSK)
+#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \
+ RATE_MCS_ANT_C_MSK)
+#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK
+#define RATE_MCS_ANT_NUM 3
+
+/* Bit 17-18: (0) SS, (1) SS*2 */
+#define RATE_MCS_STBC_POS 17
+#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS)
+#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS)
+
+/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */
+#define RATE_MCS_BF_POS 19
+#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS)
+
+/* Bit 20: (0) ZLF is off, (1) ZLF is on */
+#define RATE_MCS_ZLF_POS 20
+#define RATE_MCS_ZLF_MSK (1 << RATE_MCS_ZLF_POS)
+
+/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */
+#define RATE_MCS_DUP_POS 24
+#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS)
+
+/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */
+#define RATE_MCS_LDPC_POS 27
+#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS)
+
+
+/* Link Quality definitions */
+
+/* # entries in rate scale table to support Tx retries */
+#define LQ_MAX_RETRY_NUM 16
+
+/* Link quality command flags bit fields */
+
+/* Bit 0: (0) Don't use RTS (1) Use RTS */
+#define LQ_FLAG_USE_RTS_POS 0
+#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS)
+
+/* Bit 1-3: LQ command color. Used to match responses to LQ commands */
+#define LQ_FLAG_COLOR_POS 1
+#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS)
+
+/* Bit 4-5: Tx RTS BW Signalling
+ * (0) No RTS BW signalling
+ * (1) Static BW signalling
+ * (2) Dynamic BW signalling
+ */
+#define LQ_FLAG_RTS_BW_SIG_POS 4
+#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS)
+#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS)
+#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS)
+
+/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection
+ * Dyanmic BW selection allows Tx with narrower BW then requested in rates
+ */
+#define LQ_FLAG_DYNAMIC_BW_POS 6
+#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS)
+
+/* Single Stream Tx Parameters (lq_cmd->ss_params)
+ * Flags to control a smart FW decision about whether BFER/STBC/SISO will be
+ * used for single stream Tx.
+ */
+
+/* Bit 0-1: Max STBC streams allowed. Can be 0-3.
+ * (0) - No STBC allowed
+ * (1) - 2x1 STBC allowed (HT/VHT)
+ * (2) - 4x2 STBC allowed (HT/VHT)
+ * (3) - 3x2 STBC allowed (HT only)
+ * All our chips are at most 2 antennas so only (1) is valid for now.
+ */
+#define LQ_SS_STBC_ALLOWED_POS 0
+#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK)
+
+/* 2x1 STBC is allowed */
+#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS)
+
+/* Bit 2: Beamformer (VHT only) is allowed */
+#define LQ_SS_BFER_ALLOWED_POS 2
+#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS)
+
+/* Bit 3: Force BFER or STBC for testing
+ * If this is set:
+ * If BFER is allowed then force the ucode to choose BFER else
+ * If STBC is allowed then force the ucode to choose STBC over SISO
+ */
+#define LQ_SS_FORCE_POS 3
+#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS)
+
+/* Bit 31: ss_params field is valid. Used for FW backward compatibility
+ * with other drivers which don't support the ss_params API yet
+ */
+#define LQ_SS_PARAMS_VALID_POS 31
+#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS)
+
+/**
+ * struct iwl_lq_cmd - link quality command
+ * @sta_id: station to update
+ * @control: not used
+ * @flags: combination of LQ_FLAG_*
+ * @mimo_delim: the first SISO index in rs_table, which separates MIMO
+ * and SISO rates
+ * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD).
+ * Should be ANT_[ABC]
+ * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC]
+ * @initial_rate_index: first index from rs_table per AC category
+ * @agg_time_limit: aggregation max time threshold in usec/100, meaning
+ * value of 100 is one usec. Range is 100 to 8000
+ * @agg_disable_start_th: try-count threshold for starting aggregation.
+ * If a frame has higher try-count, it should not be selected for
+ * starting an aggregation sequence.
+ * @agg_frame_cnt_limit: max frame count in an aggregation.
+ * 0: no limit
+ * 1: no aggregation (one frame per aggregation)
+ * 2 - 0x3f: maximal number of frames (up to 3f == 63)
+ * @rs_table: array of rates for each TX try, each is rate_n_flags,
+ * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP
+ * @ss_params: single stream features. declare whether STBC or BFER are allowed.
+ */
+struct iwl_lq_cmd {
+ u8 sta_id;
+ u8 reduced_tpc;
+ u16 control;
+ /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */
+ u8 flags;
+ u8 mimo_delim;
+ u8 single_stream_ant_msk;
+ u8 dual_stream_ant_msk;
+ u8 initial_rate_index[AC_NUM];
+ /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */
+ __le16 agg_time_limit;
+ u8 agg_disable_start_th;
+ u8 agg_frame_cnt_limit;
+ __le32 reserved2;
+ __le32 rs_table[LQ_MAX_RETRY_NUM];
+ __le32 ss_params;
+}; /* LINK_QUALITY_CMD_API_S_VER_1 */
+#endif /* __fw_api_rs_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
new file mode 100644
index 000000000000..fb6d341d6f3d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
@@ -0,0 +1,368 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_rx_h__
+#define __fw_api_rx_h__
+
+/* API for pre-9000 hardware */
+
+#define IWL_RX_INFO_PHY_CNT 8
+#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
+#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
+#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00
+#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000
+#define IWL_RX_INFO_ENERGY_ANT_A_POS 0
+#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
+#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
+
+enum iwl_mac_context_info {
+ MAC_CONTEXT_INFO_NONE,
+ MAC_CONTEXT_INFO_GSCAN,
+};
+
+/**
+ * struct iwl_rx_phy_info - phy info
+ * (REPLY_RX_PHY_CMD = 0xc0)
+ * @non_cfg_phy_cnt: non configurable DSP phy data byte count
+ * @cfg_phy_cnt: configurable DSP phy data byte count
+ * @stat_id: configurable DSP phy data set ID
+ * @reserved1:
+ * @system_timestamp: GP2 at on air rise
+ * @timestamp: TSF at on air rise
+ * @beacon_time_stamp: beacon at on-air rise
+ * @phy_flags: general phy flags: band, modulation, ...
+ * @channel: channel number
+ * @non_cfg_phy_buf: for various implementations of non_cfg_phy
+ * @rate_n_flags: RATE_MCS_*
+ * @byte_count: frame's byte-count
+ * @frame_time: frame's time on the air, based on byte count and frame rate
+ * calculation
+ * @mac_active_msk: what MACs were active when the frame was received
+ * @mac_context_info: additional info on the context in which the frame was
+ * received as defined in &enum iwl_mac_context_info
+ *
+ * Before each Rx, the device sends this data. It contains PHY information
+ * about the reception of the packet.
+ */
+struct iwl_rx_phy_info {
+ u8 non_cfg_phy_cnt;
+ u8 cfg_phy_cnt;
+ u8 stat_id;
+ u8 reserved1;
+ __le32 system_timestamp;
+ __le64 timestamp;
+ __le32 beacon_time_stamp;
+ __le16 phy_flags;
+ __le16 channel;
+ __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
+ __le32 rate_n_flags;
+ __le32 byte_count;
+ u8 mac_active_msk;
+ u8 mac_context_info;
+ __le16 frame_time;
+} __packed;
+
+/*
+ * TCP offload Rx assist info
+ *
+ * bits 0:3 - reserved
+ * bits 4:7 - MIC CRC length
+ * bits 8:12 - MAC header length
+ * bit 13 - Padding indication
+ * bit 14 - A-AMSDU indication
+ * bit 15 - Offload enabled
+ */
+enum iwl_csum_rx_assist_info {
+ CSUM_RXA_RESERVED_MASK = 0x000f,
+ CSUM_RXA_MICSIZE_MASK = 0x00f0,
+ CSUM_RXA_HEADERLEN_MASK = 0x1f00,
+ CSUM_RXA_PADD = BIT(13),
+ CSUM_RXA_AMSDU = BIT(14),
+ CSUM_RXA_ENA = BIT(15)
+};
+
+/**
+ * struct iwl_rx_mpdu_res_start - phy info
+ * @assist: see CSUM_RX_ASSIST_ above
+ */
+struct iwl_rx_mpdu_res_start {
+ __le16 byte_count;
+ __le16 assist;
+} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */
+
+/**
+ * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags
+ * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band
+ * @RX_RES_PHY_FLAGS_MOD_CCK:
+ * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short
+ * @RX_RES_PHY_FLAGS_NARROW_BAND:
+ * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received
+ * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU
+ * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame
+ * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble
+ * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame
+ */
+enum iwl_rx_phy_flags {
+ RX_RES_PHY_FLAGS_BAND_24 = BIT(0),
+ RX_RES_PHY_FLAGS_MOD_CCK = BIT(1),
+ RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2),
+ RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3),
+ RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4),
+ RX_RES_PHY_FLAGS_ANTENNA_POS = 4,
+ RX_RES_PHY_FLAGS_AGG = BIT(7),
+ RX_RES_PHY_FLAGS_OFDM_HT = BIT(8),
+ RX_RES_PHY_FLAGS_OFDM_GF = BIT(9),
+ RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10),
+};
+
+/**
+ * enum iwl_mvm_rx_status - written by fw for each Rx packet
+ * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine
+ * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow
+ * @RX_MPDU_RES_STATUS_SRC_STA_FOUND:
+ * @RX_MPDU_RES_STATUS_KEY_VALID:
+ * @RX_MPDU_RES_STATUS_KEY_PARAM_OK:
+ * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed
+ * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked
+ * in the driver.
+ * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine
+ * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or
+ * alg = CCM only. Checks replay attack for 11w frames. Relevant only if
+ * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set.
+ * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted
+ * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM
+ * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC
+ * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted
+ * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm
+ * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted
+ * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT:
+ * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame
+ * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw
+ * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors
+ * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK:
+ * @RX_MPDU_RES_STATUS_STA_ID_MSK:
+ * @RX_MPDU_RES_STATUS_RRF_KILL:
+ * @RX_MPDU_RES_STATUS_FILTERING_MSK:
+ * @RX_MPDU_RES_STATUS2_FILTERING_MSK:
+ */
+enum iwl_mvm_rx_status {
+ RX_MPDU_RES_STATUS_CRC_OK = BIT(0),
+ RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1),
+ RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2),
+ RX_MPDU_RES_STATUS_KEY_VALID = BIT(3),
+ RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4),
+ RX_MPDU_RES_STATUS_ICV_OK = BIT(5),
+ RX_MPDU_RES_STATUS_MIC_OK = BIT(6),
+ RX_MPDU_RES_STATUS_TTAK_OK = BIT(7),
+ RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7),
+ RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8),
+ RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8),
+ RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8),
+ RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8),
+ RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8),
+ RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8),
+ RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8),
+ RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8),
+ RX_MPDU_RES_STATUS_DEC_DONE = BIT(11),
+ RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12),
+ RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13),
+ RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14),
+ RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15),
+ RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16),
+ RX_MPDU_RES_STATUS_CSUM_OK = BIT(17),
+ RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000),
+ RX_MDPU_RES_STATUS_STA_ID_SHIFT = 24,
+ RX_MPDU_RES_STATUS_STA_ID_MSK = 0x1f << RX_MDPU_RES_STATUS_STA_ID_SHIFT,
+ RX_MPDU_RES_STATUS_RRF_KILL = BIT(29),
+ RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000),
+ RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000),
+};
+
+/* 9000 series API */
+enum iwl_rx_mpdu_mac_flags1 {
+ IWL_RX_MDPU_MFLG1_ADDRTYPE_MASK = 0x03,
+ IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK = 0xf0,
+ /* shift should be 4, but the length is measured in 2-byte
+ * words, so shifting only by 3 gives a byte result
+ */
+ IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_SHIFT = 3,
+};
+
+enum iwl_rx_mpdu_mac_flags2 {
+ /* in 2-byte words */
+ IWL_RX_MPDU_MFLG2_HDR_LEN_MASK = 0x1f,
+ IWL_RX_MPDU_MFLG2_PAD = 0x20,
+ IWL_RX_MPDU_MFLG2_AMSDU = 0x40,
+};
+
+enum iwl_rx_mpdu_amsdu_info {
+ IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x3f,
+ IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x40,
+ /* 0x80 bit reserved for now */
+};
+
+enum iwl_rx_l3l4_flags {
+ IWL_RX_L3L4_IP_HDR_CSUM_OK = BIT(0),
+ IWL_RX_L3L4_TCP_UDP_CSUM_OK = BIT(1),
+ IWL_RX_L3L4_TCP_FIN_SYN_RST_PSH = BIT(2),
+ IWL_RX_L3L4_TCP_ACK = BIT(3),
+ IWL_RX_L3L4_L3_PROTO_MASK = 0xf << 4,
+ IWL_RX_L3L4_L4_PROTO_MASK = 0xf << 8,
+ IWL_RX_L3L4_RSS_HASH_MASK = 0xf << 12,
+};
+
+enum iwl_rx_mpdu_status {
+ IWL_RX_MPDU_STATUS_CRC_OK = BIT(0),
+ IWL_RX_MPDU_STATUS_OVERRUN_OK = BIT(1),
+ IWL_RX_MPDU_STATUS_SRC_STA_FOUND = BIT(2),
+ IWL_RX_MPDU_STATUS_KEY_VALID = BIT(3),
+ IWL_RX_MPDU_STATUS_KEY_ERROR = BIT(4),
+ IWL_RX_MPDU_STATUS_ICV_OK = BIT(5),
+ IWL_RX_MPDU_STATUS_MIC_OK = BIT(6),
+ /* TODO - verify this is the correct value */
+ IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7),
+ IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8,
+ IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8,
+ IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8,
+ IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8,
+ IWL_RX_MPDU_STATUS_SEC_TKIP = 0x3 << 8,
+ /* TODO - define IWL_RX_MPDU_STATUS_SEC_EXT_ENC - this is a stub */
+ IWL_RX_MPDU_STATUS_SEC_EXT_ENC = 0x4 << 8,
+ /* TODO - define IWL_RX_MPDU_STATUS_SEC_GCM - this is a stub */
+ IWL_RX_MPDU_STATUS_SEC_GCM = 0x5 << 8,
+ IWL_RX_MPDU_STATUS_DECRYPTED = BIT(11),
+ IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12),
+ IWL_RX_MPDU_STATUS_EXT_IV_MATCH = BIT(13),
+ IWL_RX_MPDU_STATUS_KEY_ID_MATCH = BIT(14),
+ IWL_RX_MPDU_STATUS_KEY_COLOR = BIT(15),
+};
+
+enum iwl_rx_mpdu_hash_filter {
+ IWL_RX_MPDU_HF_A1_HASH_MASK = 0x3f,
+ IWL_RX_MPDU_HF_FILTER_STATUS_MASK = 0xc0,
+};
+
+enum iwl_rx_mpdu_sta_id_flags {
+ IWL_RX_MPDU_SIF_STA_ID_MASK = 0x1f,
+ IWL_RX_MPDU_SIF_RRF_ABORT = 0x20,
+ IWL_RX_MPDU_SIF_FILTER_STATUS_MASK = 0xc0,
+};
+
+#define IWL_RX_REORDER_DATA_INVALID_BAID 0x7f
+
+enum iwl_rx_mpdu_reorder_data {
+ IWL_RX_MPDU_REORDER_NSSN_MASK = 0x00000fff,
+ IWL_RX_MPDU_REORDER_SN_MASK = 0x00fff000,
+ IWL_RX_MPDU_REORDER_SN_SHIFT = 12,
+ IWL_RX_MPDU_REORDER_BAID_MASK = 0x7f000000,
+ IWL_RX_MPDU_REORDER_BAID_SHIFT = 24,
+ IWL_RX_MPDU_REORDER_BA_OLD_SN = 0x80000000,
+};
+
+struct iwl_rx_mpdu_desc {
+ /* DW2 */
+ __le16 mpdu_len;
+ u8 mac_flags1;
+ u8 mac_flags2;
+ /* DW3 */
+ u8 amsdu_info;
+ __le16 reserved_for_software;
+ u8 mac_phy_idx;
+ /* DW4 */
+ __le16 raw_csum; /* alledgedly unreliable */
+ __le16 l3l4_flags;
+ /* DW5 */
+ __le16 status;
+ u8 hash_filter;
+ u8 sta_id_flags;
+ /* DW6 */
+ __le32 reorder_data;
+ /* DW7 */
+ __le32 rss_hash;
+ /* DW8 */
+ __le32 filter_match;
+ /* DW9 */
+ __le32 gp2_on_air_rise;
+ /* DW10 */
+ __le32 rate_n_flags;
+ /* DW11 */
+ u8 energy_a, energy_b, energy_c, channel;
+ /* DW12 & DW13 */
+ __le64 tsf_on_air_rise;
+} __packed;
+
+struct iwl_frame_release {
+ u8 baid;
+ u8 reserved;
+ __le16 nssn;
+};
+
+#endif /* __fw_api_rx_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h
new file mode 100644
index 000000000000..f01dab0d0dac
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h
@@ -0,0 +1,736 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_scan_h__
+#define __fw_api_scan_h__
+
+#include "fw-api.h"
+
+/* Scan Commands, Responses, Notifications */
+
+/* Max number of IEs for direct SSID scans in a command */
+#define PROBE_OPTION_MAX 20
+
+/**
+ * struct iwl_ssid_ie - directed scan network information element
+ *
+ * Up to 20 of these may appear in REPLY_SCAN_CMD,
+ * selected by "type" bit field in struct iwl_scan_channel;
+ * each channel may select different ssids from among the 20 entries.
+ * SSID IEs get transmitted in reverse order of entry.
+ */
+struct iwl_ssid_ie {
+ u8 id;
+ u8 len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
+
+/* scan offload */
+#define IWL_SCAN_MAX_BLACKLIST_LEN 64
+#define IWL_SCAN_SHORT_BLACKLIST_LEN 16
+#define IWL_SCAN_MAX_PROFILES 11
+#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512
+
+/* Default watchdog (in MS) for scheduled scan iteration */
+#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000)
+
+#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1)
+#define CAN_ABORT_STATUS 1
+
+#define IWL_FULL_SCAN_MULTIPLIER 5
+#define IWL_FAST_SCHED_SCAN_ITERATIONS 3
+#define IWL_MAX_SCHED_SCAN_PLANS 2
+
+enum scan_framework_client {
+ SCAN_CLIENT_SCHED_SCAN = BIT(0),
+ SCAN_CLIENT_NETDETECT = BIT(1),
+ SCAN_CLIENT_ASSET_TRACKING = BIT(2),
+};
+
+/**
+ * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S
+ * @ssid: MAC address to filter out
+ * @reported_rssi: AP rssi reported to the host
+ * @client_bitmap: clients ignore this entry - enum scan_framework_client
+ */
+struct iwl_scan_offload_blacklist {
+ u8 ssid[ETH_ALEN];
+ u8 reported_rssi;
+ u8 client_bitmap;
+} __packed;
+
+enum iwl_scan_offload_network_type {
+ IWL_NETWORK_TYPE_BSS = 1,
+ IWL_NETWORK_TYPE_IBSS = 2,
+ IWL_NETWORK_TYPE_ANY = 3,
+};
+
+enum iwl_scan_offload_band_selection {
+ IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4,
+ IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8,
+ IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc,
+};
+
+/**
+ * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
+ * @ssid_index: index to ssid list in fixed part
+ * @unicast_cipher: encryption algorithm to match - bitmap
+ * @aut_alg: authentication algorithm to match - bitmap
+ * @network_type: enum iwl_scan_offload_network_type
+ * @band_selection: enum iwl_scan_offload_band_selection
+ * @client_bitmap: clients waiting for match - enum scan_framework_client
+ */
+struct iwl_scan_offload_profile {
+ u8 ssid_index;
+ u8 unicast_cipher;
+ u8 auth_alg;
+ u8 network_type;
+ u8 band_selection;
+ u8 client_bitmap;
+ u8 reserved[2];
+} __packed;
+
+/**
+ * iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1
+ * @blaclist: AP list to filter off from scan results
+ * @profiles: profiles to search for match
+ * @blacklist_len: length of blacklist
+ * @num_profiles: num of profiles in the list
+ * @match_notify: clients waiting for match found notification
+ * @pass_match: clients waiting for the results
+ * @active_clients: active clients bitmap - enum scan_framework_client
+ * @any_beacon_notify: clients waiting for match notification without match
+ */
+struct iwl_scan_offload_profile_cfg {
+ struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
+ u8 blacklist_len;
+ u8 num_profiles;
+ u8 match_notify;
+ u8 pass_match;
+ u8 active_clients;
+ u8 any_beacon_notify;
+ u8 reserved[2];
+} __packed;
+
+/**
+ * iwl_scan_schedule_lmac - schedule of scan offload
+ * @delay: delay between iterations, in seconds.
+ * @iterations: num of scan iterations
+ * @full_scan_mul: number of partial scans before each full scan
+ */
+struct iwl_scan_schedule_lmac {
+ __le16 delay;
+ u8 iterations;
+ u8 full_scan_mul;
+} __packed; /* SCAN_SCHEDULE_API_S */
+
+enum iwl_scan_offload_complete_status {
+ IWL_SCAN_OFFLOAD_COMPLETED = 1,
+ IWL_SCAN_OFFLOAD_ABORTED = 2,
+};
+
+enum iwl_scan_ebs_status {
+ IWL_SCAN_EBS_SUCCESS,
+ IWL_SCAN_EBS_FAILED,
+ IWL_SCAN_EBS_CHAN_NOT_FOUND,
+ IWL_SCAN_EBS_INACTIVE,
+};
+
+/**
+ * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S
+ * @tx_flags: combination of TX_CMD_FLG_*
+ * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
+ * cleared. Combination of RATE_MCS_*
+ * @sta_id: index of destination station in FW station table
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_req_tx_cmd {
+ __le32 tx_flags;
+ __le32 rate_n_flags;
+ u8 sta_id;
+ u8 reserved[3];
+} __packed;
+
+enum iwl_scan_channel_flags_lmac {
+ IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27),
+ IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28),
+};
+
+/**
+ * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2
+ * @flags: bits 1-20: directed scan to i'th ssid
+ * other bits &enum iwl_scan_channel_flags_lmac
+ * @channel_number: channel number 1-13 etc
+ * @iter_count: scan iteration on this channel
+ * @iter_interval: interval in seconds between iterations on one channel
+ */
+struct iwl_scan_channel_cfg_lmac {
+ __le32 flags;
+ __le16 channel_num;
+ __le16 iter_count;
+ __le32 iter_interval;
+} __packed;
+
+/*
+ * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1
+ * @offset: offset in the data block
+ * @len: length of the segment
+ */
+struct iwl_scan_probe_segment {
+ __le16 offset;
+ __le16 len;
+} __packed;
+
+/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2
+ * @mac_header: first (and common) part of the probe
+ * @band_data: band specific data
+ * @common_data: last (and common) part of the probe
+ * @buf: raw data block
+ */
+struct iwl_scan_probe_req {
+ struct iwl_scan_probe_segment mac_header;
+ struct iwl_scan_probe_segment band_data[2];
+ struct iwl_scan_probe_segment common_data;
+ u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE];
+} __packed;
+
+enum iwl_scan_channel_flags {
+ IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0),
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1),
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2),
+};
+
+/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S
+ * @flags: enum iwl_scan_channel_flags
+ * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is
+ * involved.
+ * 1 - EBS is disabled.
+ * 2 - every second scan will be full scan(and so on).
+ */
+struct iwl_scan_channel_opt {
+ __le16 flags;
+ __le16 non_ebs_ratio;
+} __packed;
+
+/**
+ * iwl_mvm_lmac_scan_flags
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses
+ * without filtering.
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels
+ * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan
+ * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification
+ * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching
+ * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
+ * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report
+ * and DS parameter set IEs into probe requests.
+ * @IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL: use extended dwell time on channels
+ * 1, 6 and 11.
+ * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches
+ */
+enum iwl_mvm_lmac_scan_flags {
+ IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0),
+ IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1),
+ IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2),
+ IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3),
+ IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4),
+ IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5),
+ IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6),
+ IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL = BIT(7),
+ IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9),
+};
+
+enum iwl_scan_priority {
+ IWL_SCAN_PRIORITY_LOW,
+ IWL_SCAN_PRIORITY_MEDIUM,
+ IWL_SCAN_PRIORITY_HIGH,
+};
+
+enum iwl_scan_priority_ext {
+ IWL_SCAN_PRIORITY_EXT_0_LOWEST,
+ IWL_SCAN_PRIORITY_EXT_1,
+ IWL_SCAN_PRIORITY_EXT_2,
+ IWL_SCAN_PRIORITY_EXT_3,
+ IWL_SCAN_PRIORITY_EXT_4,
+ IWL_SCAN_PRIORITY_EXT_5,
+ IWL_SCAN_PRIORITY_EXT_6,
+ IWL_SCAN_PRIORITY_EXT_7_HIGHEST,
+};
+
+/**
+ * iwl_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1
+ * @reserved1: for alignment and future use
+ * @channel_num: num of channels to scan
+ * @active-dwell: dwell time for active channels
+ * @passive-dwell: dwell time for passive channels
+ * @fragmented-dwell: dwell time for fragmented passive scan
+ * @extended_dwell: dwell time for channels 1, 6 and 11 (in certain cases)
+ * @reserved2: for alignment and future use
+ * @rx_chain_selct: PHY_RX_CHAIN_* flags
+ * @scan_flags: &enum iwl_mvm_lmac_scan_flags
+ * @max_out_time: max time (in TU) to be out of associated channel
+ * @suspend_time: pause scan this long (TUs) when returning to service channel
+ * @flags: RXON flags
+ * @filter_flags: RXON filter
+ * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz
+ * @direct_scan: list of SSIDs for directed active scan
+ * @scan_prio: enum iwl_scan_priority
+ * @iter_num: number of scan iterations
+ * @delay: delay in seconds before first iteration
+ * @schedule: two scheduling plans. The first one is finite, the second one can
+ * be infinite.
+ * @channel_opt: channel optimization options, for full and partial scan
+ * @data: channel configuration and probe request packet.
+ */
+struct iwl_scan_req_lmac {
+ /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */
+ __le32 reserved1;
+ u8 n_channels;
+ u8 active_dwell;
+ u8 passive_dwell;
+ u8 fragmented_dwell;
+ u8 extended_dwell;
+ u8 reserved2;
+ __le16 rx_chain_select;
+ __le32 scan_flags;
+ __le32 max_out_time;
+ __le32 suspend_time;
+ /* RX_ON_FLAGS_API_S_VER_1 */
+ __le32 flags;
+ __le32 filter_flags;
+ struct iwl_scan_req_tx_cmd tx_cmd[2];
+ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+ __le32 scan_prio;
+ /* SCAN_REQ_PERIODIC_PARAMS_API_S */
+ __le32 iter_num;
+ __le32 delay;
+ struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS];
+ struct iwl_scan_channel_opt channel_opt[2];
+ u8 data[];
+} __packed;
+
+/**
+ * struct iwl_scan_results_notif - scan results for one channel -
+ * SCAN_RESULT_NTF_API_S_VER_3
+ * @channel: which channel the results are from
+ * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
+ * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
+ * @num_probe_not_sent: # of request that weren't sent due to not enough time
+ * @duration: duration spent in channel, in usecs
+ */
+struct iwl_scan_results_notif {
+ u8 channel;
+ u8 band;
+ u8 probe_status;
+ u8 num_probe_not_sent;
+ __le32 duration;
+} __packed;
+
+/**
+ * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels)
+ * SCAN_COMPLETE_NTF_API_S_VER_3
+ * @scanned_channels: number of channels scanned (and number of valid results)
+ * @status: one of SCAN_COMP_STATUS_*
+ * @bt_status: BT on/off status
+ * @last_channel: last channel that was scanned
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @results: an array of scan results, only "scanned_channels" of them are valid
+ */
+struct iwl_lmac_scan_complete_notif {
+ u8 scanned_channels;
+ u8 status;
+ u8 bt_status;
+ u8 last_channel;
+ __le32 tsf_low;
+ __le32 tsf_high;
+ struct iwl_scan_results_notif results[];
+} __packed;
+
+/**
+ * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2
+ * @last_schedule_line: last schedule line executed (fast or regular)
+ * @last_schedule_iteration: last scan iteration executed before scan abort
+ * @status: enum iwl_scan_offload_complete_status
+ * @ebs_status: EBS success status &enum iwl_scan_ebs_status
+ * @time_after_last_iter; time in seconds elapsed after last iteration
+ */
+struct iwl_periodic_scan_complete {
+ u8 last_schedule_line;
+ u8 last_schedule_iteration;
+ u8 status;
+ u8 ebs_status;
+ __le32 time_after_last_iter;
+ __le32 reserved;
+} __packed;
+
+/* UMAC Scan API */
+
+/* The maximum of either of these cannot exceed 8, because we use an
+ * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h).
+ */
+#define IWL_MVM_MAX_UMAC_SCANS 8
+#define IWL_MVM_MAX_LMAC_SCANS 1
+
+enum scan_config_flags {
+ SCAN_CONFIG_FLAG_ACTIVATE = BIT(0),
+ SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1),
+ SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2),
+ SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3),
+ SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8),
+ SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9),
+ SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10),
+ SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11),
+ SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12),
+ SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13),
+ SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14),
+ SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15),
+ SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16),
+ SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17),
+ SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18),
+ SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19),
+ SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20),
+ SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21),
+
+ /* Bits 26-31 are for num of channels in channel_array */
+#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26)
+};
+
+enum scan_config_rates {
+ /* OFDM basic rates */
+ SCAN_CONFIG_RATE_6M = BIT(0),
+ SCAN_CONFIG_RATE_9M = BIT(1),
+ SCAN_CONFIG_RATE_12M = BIT(2),
+ SCAN_CONFIG_RATE_18M = BIT(3),
+ SCAN_CONFIG_RATE_24M = BIT(4),
+ SCAN_CONFIG_RATE_36M = BIT(5),
+ SCAN_CONFIG_RATE_48M = BIT(6),
+ SCAN_CONFIG_RATE_54M = BIT(7),
+ /* CCK basic rates */
+ SCAN_CONFIG_RATE_1M = BIT(8),
+ SCAN_CONFIG_RATE_2M = BIT(9),
+ SCAN_CONFIG_RATE_5M = BIT(10),
+ SCAN_CONFIG_RATE_11M = BIT(11),
+
+ /* Bits 16-27 are for supported rates */
+#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16)
+};
+
+enum iwl_channel_flags {
+ IWL_CHANNEL_FLAG_EBS = BIT(0),
+ IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1),
+ IWL_CHANNEL_FLAG_EBS_ADD = BIT(2),
+ IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3),
+};
+
+/**
+ * struct iwl_scan_config
+ * @flags: enum scan_config_flags
+ * @tx_chains: valid_tx antenna - ANT_* definitions
+ * @rx_chains: valid_rx antenna - ANT_* definitions
+ * @legacy_rates: default legacy rates - enum scan_config_rates
+ * @out_of_channel_time: default max out of serving channel time
+ * @suspend_time: default max suspend time
+ * @dwell_active: default dwell time for active scan
+ * @dwell_passive: default dwell time for passive scan
+ * @dwell_fragmented: default dwell time for fragmented scan
+ * @dwell_extended: default dwell time for channels 1, 6 and 11
+ * @mac_addr: default mac address to be used in probes
+ * @bcast_sta_id: the index of the station in the fw
+ * @channel_flags: default channel flags - enum iwl_channel_flags
+ * scan_config_channel_flag
+ * @channel_array: default supported channels
+ */
+struct iwl_scan_config {
+ __le32 flags;
+ __le32 tx_chains;
+ __le32 rx_chains;
+ __le32 legacy_rates;
+ __le32 out_of_channel_time;
+ __le32 suspend_time;
+ u8 dwell_active;
+ u8 dwell_passive;
+ u8 dwell_fragmented;
+ u8 dwell_extended;
+ u8 mac_addr[ETH_ALEN];
+ u8 bcast_sta_id;
+ u8 channel_flags;
+ u8 channel_array[];
+} __packed; /* SCAN_CONFIG_DB_CMD_API_S */
+
+/**
+ * iwl_umac_scan_flags
+ *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request
+ * can be preempted by other scan requests with higher priority.
+ * The low priority scan will be resumed when the higher proirity scan is
+ * completed.
+ *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver
+ * when scan starts.
+ */
+enum iwl_umac_scan_flags {
+ IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0),
+ IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1),
+};
+
+enum iwl_umac_scan_uid_offsets {
+ IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0,
+ IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8,
+};
+
+enum iwl_umac_scan_general_flags {
+ IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0),
+ IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1),
+ IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2),
+ IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3),
+ IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4),
+ IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5),
+ IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6),
+ IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7),
+ IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8),
+ IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9),
+ IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10),
+};
+
+/**
+ * struct iwl_scan_channel_cfg_umac
+ * @flags: bitmap - 0-19: directed scan to i'th ssid.
+ * @channel_num: channel number 1-13 etc.
+ * @iter_count: repetition count for the channel.
+ * @iter_interval: interval between two scan iterations on one channel.
+ */
+struct iwl_scan_channel_cfg_umac {
+ __le32 flags;
+ u8 channel_num;
+ u8 iter_count;
+ __le16 iter_interval;
+} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */
+
+/**
+ * struct iwl_scan_umac_schedule
+ * @interval: interval in seconds between scan iterations
+ * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_umac_schedule {
+ __le16 interval;
+ u8 iter_count;
+ u8 reserved;
+} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */
+
+/**
+ * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command
+ * parameters following channels configuration array.
+ * @schedule: two scheduling plans.
+ * @delay: delay in TUs before starting the first scan iteration
+ * @reserved: for future use and alignment
+ * @preq: probe request with IEs blocks
+ * @direct_scan: list of SSIDs for directed active scan
+ */
+struct iwl_scan_req_umac_tail {
+ /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */
+ struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS];
+ __le16 delay;
+ __le16 reserved;
+ /* SCAN_PROBE_PARAMS_API_S_VER_1 */
+ struct iwl_scan_probe_req preq;
+ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+} __packed;
+
+/**
+ * struct iwl_scan_req_umac
+ * @flags: &enum iwl_umac_scan_flags
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @ooc_priority: out of channel priority - &enum iwl_scan_priority
+ * @general_flags: &enum iwl_umac_scan_general_flags
+ * @extended_dwell: dwell time for channels 1, 6 and 11
+ * @active_dwell: dwell time for active scan
+ * @passive_dwell: dwell time for passive scan
+ * @fragmented_dwell: dwell time for fragmented passive scan
+ * @max_out_time: max out of serving channel time
+ * @suspend_time: max suspend time
+ * @scan_priority: scan internal prioritization &enum iwl_scan_priority
+ * @channel_flags: &enum iwl_scan_channel_flags
+ * @n_channels: num of channels in scan request
+ * @reserved: for future use and alignment
+ * @data: &struct iwl_scan_channel_cfg_umac and
+ * &struct iwl_scan_req_umac_tail
+ */
+struct iwl_scan_req_umac {
+ __le32 flags;
+ __le32 uid;
+ __le32 ooc_priority;
+ /* SCAN_GENERAL_PARAMS_API_S_VER_1 */
+ __le32 general_flags;
+ u8 extended_dwell;
+ u8 active_dwell;
+ u8 passive_dwell;
+ u8 fragmented_dwell;
+ __le32 max_out_time;
+ __le32 suspend_time;
+ __le32 scan_priority;
+ /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
+ u8 channel_flags;
+ u8 n_channels;
+ __le16 reserved;
+ u8 data[];
+} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_abort
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @flags: reserved
+ */
+struct iwl_umac_scan_abort {
+ __le32 uid;
+ __le32 flags;
+} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_complete
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @last_schedule: last scheduling line
+ * @last_iter: last scan iteration number
+ * @scan status: &enum iwl_scan_offload_complete_status
+ * @ebs_status: &enum iwl_scan_ebs_status
+ * @time_from_last_iter: time elapsed from last iteration
+ * @reserved: for future use
+ */
+struct iwl_umac_scan_complete {
+ __le32 uid;
+ u8 last_schedule;
+ u8 last_iter;
+ u8 status;
+ u8 ebs_status;
+ __le32 time_from_last_iter;
+ __le32 reserved;
+} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */
+
+#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5
+/**
+ * struct iwl_scan_offload_profile_match - match information
+ * @bssid: matched bssid
+ * @channel: channel where the match occurred
+ * @energy:
+ * @matching_feature:
+ * @matching_channels: bitmap of channels that matched, referencing
+ * the channels passed in tue scan offload request
+ */
+struct iwl_scan_offload_profile_match {
+ u8 bssid[ETH_ALEN];
+ __le16 reserved;
+ u8 channel;
+ u8 energy;
+ u8 matching_feature;
+ u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN];
+} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */
+
+/**
+ * struct iwl_scan_offload_profiles_query - match results query response
+ * @matched_profiles: bitmap of matched profiles, referencing the
+ * matches passed in the scan offload request
+ * @last_scan_age: age of the last offloaded scan
+ * @n_scans_done: number of offloaded scans done
+ * @gp2_d0u: GP2 when D0U occurred
+ * @gp2_invoked: GP2 when scan offload was invoked
+ * @resume_while_scanning: not used
+ * @self_recovery: obsolete
+ * @reserved: reserved
+ * @matches: array of match information, one for each match
+ */
+struct iwl_scan_offload_profiles_query {
+ __le32 matched_profiles;
+ __le32 last_scan_age;
+ __le32 n_scans_done;
+ __le32 gp2_d0u;
+ __le32 gp2_invoked;
+ u8 resume_while_scanning;
+ u8 self_recovery;
+ __le16 reserved;
+ struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */
+
+/**
+ * struct iwl_umac_scan_iter_complete_notif - notifies end of scanning iteration
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @scanned_channels: number of channels scanned and number of valid elements in
+ * results array
+ * @status: one of SCAN_COMP_STATUS_*
+ * @bt_status: BT on/off status
+ * @last_channel: last channel that was scanned
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @results: array of scan results, only "scanned_channels" of them are valid
+ */
+struct iwl_umac_scan_iter_complete_notif {
+ __le32 uid;
+ u8 scanned_channels;
+ u8 status;
+ u8 bt_status;
+ u8 last_channel;
+ __le32 tsf_low;
+ __le32 tsf_high;
+ struct iwl_scan_results_notif results[];
+} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_1 */
+
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h
new file mode 100644
index 000000000000..6fca4fb1d306
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h
@@ -0,0 +1,414 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_sta_h__
+#define __fw_api_sta_h__
+
+/**
+ * enum iwl_sta_flags - flags for the ADD_STA host command
+ * @STA_FLG_REDUCED_TX_PWR_CTRL:
+ * @STA_FLG_REDUCED_TX_PWR_DATA:
+ * @STA_FLG_DISABLE_TX: set if TX should be disabled
+ * @STA_FLG_PS: set if STA is in Power Save
+ * @STA_FLG_INVALID: set if STA is invalid
+ * @STA_FLG_DLP_EN: Direct Link Protocol is enabled
+ * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs
+ * @STA_FLG_DRAIN_FLOW: drain flow
+ * @STA_FLG_PAN: STA is for PAN interface
+ * @STA_FLG_CLASS_AUTH:
+ * @STA_FLG_CLASS_ASSOC:
+ * @STA_FLG_CLASS_MIMO_PROT:
+ * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU
+ * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation
+ * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is
+ * initialised by driver and can be updated by fw upon reception of
+ * action frames that can change the channel width. When cleared the fw
+ * will send all the frames in 20MHz even when FAT channel is requested.
+ * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the
+ * driver and can be updated by fw upon reception of action frames.
+ * @STA_FLG_MFP_EN: Management Frame Protection
+ */
+enum iwl_sta_flags {
+ STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3),
+ STA_FLG_REDUCED_TX_PWR_DATA = BIT(6),
+
+ STA_FLG_DISABLE_TX = BIT(4),
+
+ STA_FLG_PS = BIT(8),
+ STA_FLG_DRAIN_FLOW = BIT(12),
+ STA_FLG_PAN = BIT(13),
+ STA_FLG_CLASS_AUTH = BIT(14),
+ STA_FLG_CLASS_ASSOC = BIT(15),
+ STA_FLG_RTS_MIMO_PROT = BIT(17),
+
+ STA_FLG_MAX_AGG_SIZE_SHIFT = 19,
+ STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+ STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+
+ STA_FLG_AGG_MPDU_DENS_SHIFT = 23,
+ STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+ STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+ STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+ STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+ STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+
+ STA_FLG_FAT_EN_20MHZ = (0 << 26),
+ STA_FLG_FAT_EN_40MHZ = (1 << 26),
+ STA_FLG_FAT_EN_80MHZ = (2 << 26),
+ STA_FLG_FAT_EN_160MHZ = (3 << 26),
+ STA_FLG_FAT_EN_MSK = (3 << 26),
+
+ STA_FLG_MIMO_EN_SISO = (0 << 28),
+ STA_FLG_MIMO_EN_MIMO2 = (1 << 28),
+ STA_FLG_MIMO_EN_MIMO3 = (2 << 28),
+ STA_FLG_MIMO_EN_MSK = (3 << 28),
+};
+
+/**
+ * enum iwl_sta_key_flag - key flags for the ADD_STA host command
+ * @STA_KEY_FLG_NO_ENC: no encryption
+ * @STA_KEY_FLG_WEP: WEP encryption algorithm
+ * @STA_KEY_FLG_CCM: CCMP encryption algorithm
+ * @STA_KEY_FLG_TKIP: TKIP encryption algorithm
+ * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support)
+ * @STA_KEY_FLG_CMAC: CMAC encryption algorithm
+ * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm
+ * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value
+ * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
+ * station info array (1 - n 1X mode)
+ * @STA_KEY_FLG_KEYID_MSK: the index of the key
+ * @STA_KEY_NOT_VALID: key is invalid
+ * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key
+ * @STA_KEY_MULTICAST: set for multical key
+ * @STA_KEY_MFP: key is used for Management Frame Protection
+ */
+enum iwl_sta_key_flag {
+ STA_KEY_FLG_NO_ENC = (0 << 0),
+ STA_KEY_FLG_WEP = (1 << 0),
+ STA_KEY_FLG_CCM = (2 << 0),
+ STA_KEY_FLG_TKIP = (3 << 0),
+ STA_KEY_FLG_EXT = (4 << 0),
+ STA_KEY_FLG_CMAC = (6 << 0),
+ STA_KEY_FLG_ENC_UNKNOWN = (7 << 0),
+ STA_KEY_FLG_EN_MSK = (7 << 0),
+
+ STA_KEY_FLG_WEP_KEY_MAP = BIT(3),
+ STA_KEY_FLG_KEYID_POS = 8,
+ STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS),
+ STA_KEY_NOT_VALID = BIT(11),
+ STA_KEY_FLG_WEP_13BYTES = BIT(12),
+ STA_KEY_MULTICAST = BIT(14),
+ STA_KEY_MFP = BIT(15),
+};
+
+/**
+ * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed
+ * @STA_MODIFY_KEY: this command modifies %key
+ * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx
+ * @STA_MODIFY_TX_RATE: unused
+ * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid
+ * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid
+ * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count
+ * @STA_MODIFY_PROT_TH:
+ * @STA_MODIFY_QUEUES: modify the queues used by this station
+ */
+enum iwl_sta_modify_flag {
+ STA_MODIFY_KEY = BIT(0),
+ STA_MODIFY_TID_DISABLE_TX = BIT(1),
+ STA_MODIFY_TX_RATE = BIT(2),
+ STA_MODIFY_ADD_BA_TID = BIT(3),
+ STA_MODIFY_REMOVE_BA_TID = BIT(4),
+ STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5),
+ STA_MODIFY_PROT_TH = BIT(6),
+ STA_MODIFY_QUEUES = BIT(7),
+};
+
+#define STA_MODE_MODIFY 1
+
+/**
+ * enum iwl_sta_sleep_flag - type of sleep of the station
+ * @STA_SLEEP_STATE_AWAKE:
+ * @STA_SLEEP_STATE_PS_POLL:
+ * @STA_SLEEP_STATE_UAPSD:
+ * @STA_SLEEP_STATE_MOREDATA: set more-data bit on
+ * (last) released frame
+ */
+enum iwl_sta_sleep_flag {
+ STA_SLEEP_STATE_AWAKE = 0,
+ STA_SLEEP_STATE_PS_POLL = BIT(0),
+ STA_SLEEP_STATE_UAPSD = BIT(1),
+ STA_SLEEP_STATE_MOREDATA = BIT(2),
+};
+
+/* STA ID and color bits definitions */
+#define STA_ID_SEED (0x0f)
+#define STA_ID_POS (0)
+#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS)
+
+#define STA_COLOR_SEED (0x7)
+#define STA_COLOR_POS (4)
+#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS)
+
+#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \
+ (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
+#define STA_ID_N_COLOR_GET_ID(id_n_color) \
+ (((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
+
+#define STA_KEY_MAX_NUM (16)
+#define STA_KEY_IDX_INVALID (0xff)
+#define STA_KEY_MAX_DATA_KEY_NUM (4)
+#define IWL_MAX_GLOBAL_KEYS (4)
+#define STA_KEY_LEN_WEP40 (5)
+#define STA_KEY_LEN_WEP104 (13)
+
+/**
+ * struct iwl_mvm_keyinfo - key information
+ * @key_flags: type %iwl_sta_key_flag
+ * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
+ * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
+ * @key_offset: key offset in the fw's key table
+ * @key: 16-byte unicast decryption key
+ * @tx_secur_seq_cnt: initial RSC / PN needed for replay check
+ * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only
+ * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only
+ */
+struct iwl_mvm_keyinfo {
+ __le16 key_flags;
+ u8 tkip_rx_tsc_byte2;
+ u8 reserved1;
+ __le16 tkip_rx_ttak[5];
+ u8 key_offset;
+ u8 reserved2;
+ u8 key[16];
+ __le64 tx_secur_seq_cnt;
+ __le64 hw_tkip_mic_rx_key;
+ __le64 hw_tkip_mic_tx_key;
+} __packed;
+
+/**
+ * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table.
+ * ( REPLY_ADD_STA = 0x18 )
+ * @add_modify: 1: modify existing, 0: add new station
+ * @awake_acs:
+ * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
+ * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
+ * @mac_id_n_color: the Mac context this station belongs to
+ * @addr[ETH_ALEN]: station's MAC address
+ * @sta_id: index of station in uCode's station table
+ * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave
+ * alone. 1 - modify, 0 - don't change.
+ * @station_flags: look at %iwl_sta_flags
+ * @station_flags_msk: what of %station_flags have changed
+ * @add_immediate_ba_tid: tid for which to add block-ack support (Rx)
+ * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set
+ * add_immediate_ba_ssn.
+ * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx)
+ * Set %STA_MODIFY_REMOVE_BA_TID to use this field
+ * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with
+ * add_immediate_ba_tid.
+ * @sleep_tx_count: number of packets to transmit to station even though it is
+ * asleep. Used to synchronise PS-poll and u-APSD responses while ucode
+ * keeps track of STA sleep state.
+ * @sleep_state_flags: Look at %iwl_sta_sleep_flag.
+ * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP
+ * mac-addr.
+ * @beamform_flags: beam forming controls
+ * @tfd_queue_msk: tfd queues used by this station
+ *
+ * The device contains an internal table of per-station information, with info
+ * on security keys, aggregation parameters, and Tx rates for initial Tx
+ * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD).
+ *
+ * ADD_STA sets up the table entry for one station, either creating a new
+ * entry, or modifying a pre-existing one.
+ */
+struct iwl_mvm_add_sta_cmd {
+ u8 add_modify;
+ u8 awake_acs;
+ __le16 tid_disable_tx;
+ __le32 mac_id_n_color;
+ u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */
+ __le16 reserved2;
+ u8 sta_id;
+ u8 modify_mask;
+ __le16 reserved3;
+ __le32 station_flags;
+ __le32 station_flags_msk;
+ u8 add_immediate_ba_tid;
+ u8 remove_immediate_ba_tid;
+ __le16 add_immediate_ba_ssn;
+ __le16 sleep_tx_count;
+ __le16 sleep_state_flags;
+ __le16 assoc_id;
+ __le16 beamform_flags;
+ __le32 tfd_queue_msk;
+} __packed; /* ADD_STA_CMD_API_S_VER_7 */
+
+/**
+ * struct iwl_mvm_add_sta_key_cmd - add/modify sta key
+ * ( REPLY_ADD_STA_KEY = 0x17 )
+ * @sta_id: index of station in uCode's station table
+ * @key_offset: key offset in key storage
+ * @key_flags: type %iwl_sta_key_flag
+ * @key: key material data
+ * @key2: key material data
+ * @rx_secur_seq_cnt: RX security sequence counter for the key
+ * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
+ * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
+ */
+struct iwl_mvm_add_sta_key_cmd {
+ u8 sta_id;
+ u8 key_offset;
+ __le16 key_flags;
+ u8 key[16];
+ u8 key2[16];
+ u8 rx_secur_seq_cnt[16];
+ u8 tkip_rx_tsc_byte2;
+ u8 reserved;
+ __le16 tkip_rx_ttak[5];
+} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */
+
+/**
+ * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command
+ * @ADD_STA_SUCCESS: operation was executed successfully
+ * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table
+ * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session
+ * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that
+ * doesn't exist.
+ */
+enum iwl_mvm_add_sta_rsp_status {
+ ADD_STA_SUCCESS = 0x1,
+ ADD_STA_STATIONS_OVERLOAD = 0x2,
+ ADD_STA_IMMEDIATE_BA_FAILURE = 0x4,
+ ADD_STA_MODIFY_NON_EXISTING_STA = 0x8,
+};
+
+/**
+ * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table
+ * ( REMOVE_STA = 0x19 )
+ * @sta_id: the station id of the station to be removed
+ */
+struct iwl_mvm_rm_sta_cmd {
+ u8 sta_id;
+ u8 reserved[3];
+} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_mvm_mgmt_mcast_key_cmd
+ * ( MGMT_MCAST_KEY = 0x1f )
+ * @ctrl_flags: %iwl_sta_key_flag
+ * @IGTK:
+ * @K1: unused
+ * @K2: unused
+ * @sta_id: station ID that support IGTK
+ * @key_id:
+ * @receive_seq_cnt: initial RSC/PN needed for replay check
+ */
+struct iwl_mvm_mgmt_mcast_key_cmd {
+ __le32 ctrl_flags;
+ u8 IGTK[16];
+ u8 K1[16];
+ u8 K2[16];
+ __le32 key_id;
+ __le32 sta_id;
+ __le64 receive_seq_cnt;
+} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */
+
+struct iwl_mvm_wep_key {
+ u8 key_index;
+ u8 key_offset;
+ __le16 reserved1;
+ u8 key_size;
+ u8 reserved2[3];
+ u8 key[16];
+} __packed;
+
+struct iwl_mvm_wep_key_cmd {
+ __le32 mac_id_n_color;
+ u8 num_keys;
+ u8 decryption_type;
+ u8 flags;
+ u8 reserved;
+ struct iwl_mvm_wep_key wep_key[0];
+} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_mvm_eosp_notification - EOSP notification from firmware
+ * @remain_frame_count: # of frames remaining, non-zero if SP was cut
+ * short by GO absence
+ * @sta_id: station ID
+ */
+struct iwl_mvm_eosp_notification {
+ __le32 remain_frame_count;
+ __le32 sta_id;
+} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */
+
+#endif /* __fw_api_sta_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h
new file mode 100644
index 000000000000..438665a54923
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h
@@ -0,0 +1,284 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_stats_h__
+#define __fw_api_stats_h__
+#include "fw-api-mac.h"
+
+struct mvm_statistics_dbg {
+ __le32 burst_check;
+ __le32 burst_count;
+ __le32 wait_for_silence_timeout_cnt;
+ __le32 reserved[3];
+} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */
+
+struct mvm_statistics_div {
+ __le32 tx_on_a;
+ __le32 tx_on_b;
+ __le32 exec_time;
+ __le32 probe_time;
+ __le32 rssi_ant;
+ __le32 reserved2;
+} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
+
+struct mvm_statistics_rx_non_phy {
+ __le32 bogus_cts; /* CTS received when not expecting CTS */
+ __le32 bogus_ack; /* ACK received when not expecting ACK */
+ __le32 non_bssid_frames; /* number of frames with BSSID that
+ * doesn't belong to the STA BSSID */
+ __le32 filtered_frames; /* count frames that were dumped in the
+ * filtering process */
+ __le32 non_channel_beacons; /* beacons with our bss id but not on
+ * our serving channel */
+ __le32 channel_beacons; /* beacons with our bss id and in our
+ * serving channel */
+ __le32 num_missed_bcon; /* number of missed beacons */
+ __le32 adc_rx_saturation_time; /* count in 0.8us units the time the
+ * ADC was in saturation */
+ __le32 ina_detection_search_time;/* total time (in 0.8us) searched
+ * for INA */
+ __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */
+ __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */
+ __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */
+ __le32 interference_data_flag; /* flag for interference data
+ * availability. 1 when data is
+ * available. */
+ __le32 channel_load; /* counts RX Enable time in uSec */
+ __le32 dsp_false_alarms; /* DSP false alarm (both OFDM
+ * and CCK) counter */
+ __le32 beacon_rssi_a;
+ __le32 beacon_rssi_b;
+ __le32 beacon_rssi_c;
+ __le32 beacon_energy_a;
+ __le32 beacon_energy_b;
+ __le32 beacon_energy_c;
+ __le32 num_bt_kills;
+ __le32 mac_id;
+ __le32 directed_data_mpdu;
+} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */
+
+struct mvm_statistics_rx_phy {
+ __le32 ina_cnt;
+ __le32 fina_cnt;
+ __le32 plcp_err;
+ __le32 crc32_err;
+ __le32 overrun_err;
+ __le32 early_overrun_err;
+ __le32 crc32_good;
+ __le32 false_alarm_cnt;
+ __le32 fina_sync_err_cnt;
+ __le32 sfd_timeout;
+ __le32 fina_timeout;
+ __le32 unresponded_rts;
+ __le32 rxe_frame_lmt_overrun;
+ __le32 sent_ack_cnt;
+ __le32 sent_cts_cnt;
+ __le32 sent_ba_rsp_cnt;
+ __le32 dsp_self_kill;
+ __le32 mh_format_err;
+ __le32 re_acq_main_rssi_sum;
+ __le32 reserved;
+} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */
+
+struct mvm_statistics_rx_ht_phy {
+ __le32 plcp_err;
+ __le32 overrun_err;
+ __le32 early_overrun_err;
+ __le32 crc32_good;
+ __le32 crc32_err;
+ __le32 mh_format_err;
+ __le32 agg_crc32_good;
+ __le32 agg_mpdu_cnt;
+ __le32 agg_cnt;
+ __le32 unsupport_mcs;
+} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */
+
+struct mvm_statistics_tx_non_phy {
+ __le32 preamble_cnt;
+ __le32 rx_detected_cnt;
+ __le32 bt_prio_defer_cnt;
+ __le32 bt_prio_kill_cnt;
+ __le32 few_bytes_cnt;
+ __le32 cts_timeout;
+ __le32 ack_timeout;
+ __le32 expected_ack_cnt;
+ __le32 actual_ack_cnt;
+ __le32 dump_msdu_cnt;
+ __le32 burst_abort_next_frame_mismatch_cnt;
+ __le32 burst_abort_missing_next_frame_cnt;
+ __le32 cts_timeout_collision;
+ __le32 ack_or_ba_timeout_collision;
+} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */
+
+#define MAX_CHAINS 3
+
+struct mvm_statistics_tx_non_phy_agg {
+ __le32 ba_timeout;
+ __le32 ba_reschedule_frames;
+ __le32 scd_query_agg_frame_cnt;
+ __le32 scd_query_no_agg;
+ __le32 scd_query_agg;
+ __le32 scd_query_mismatch;
+ __le32 frame_not_ready;
+ __le32 underrun;
+ __le32 bt_prio_kill;
+ __le32 rx_ba_rsp_cnt;
+ __s8 txpower[MAX_CHAINS];
+ __s8 reserved;
+ __le32 reserved2;
+} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */
+
+struct mvm_statistics_tx_channel_width {
+ __le32 ext_cca_narrow_ch20[1];
+ __le32 ext_cca_narrow_ch40[2];
+ __le32 ext_cca_narrow_ch80[3];
+ __le32 ext_cca_narrow_ch160[4];
+ __le32 last_tx_ch_width_indx;
+ __le32 rx_detected_per_ch_width[4];
+ __le32 success_per_ch_width[4];
+ __le32 fail_per_ch_width[4];
+}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */
+
+struct mvm_statistics_tx {
+ struct mvm_statistics_tx_non_phy general;
+ struct mvm_statistics_tx_non_phy_agg agg;
+ struct mvm_statistics_tx_channel_width channel_width;
+} __packed; /* STATISTICS_TX_API_S_VER_4 */
+
+
+struct mvm_statistics_bt_activity {
+ __le32 hi_priority_tx_req_cnt;
+ __le32 hi_priority_tx_denied_cnt;
+ __le32 lo_priority_tx_req_cnt;
+ __le32 lo_priority_tx_denied_cnt;
+ __le32 hi_priority_rx_req_cnt;
+ __le32 hi_priority_rx_denied_cnt;
+ __le32 lo_priority_rx_req_cnt;
+ __le32 lo_priority_rx_denied_cnt;
+} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
+
+struct mvm_statistics_general_v8 {
+ __le32 radio_temperature;
+ __le32 radio_voltage;
+ struct mvm_statistics_dbg dbg;
+ __le32 sleep_time;
+ __le32 slots_out;
+ __le32 slots_idle;
+ __le32 ttl_timestamp;
+ struct mvm_statistics_div slow_div;
+ __le32 rx_enable_counter;
+ /*
+ * num_of_sos_states:
+ * count the number of times we have to re-tune
+ * in order to get out of bad PHY status
+ */
+ __le32 num_of_sos_states;
+ __le32 beacon_filtered;
+ __le32 missed_beacons;
+ u8 beacon_filter_average_energy;
+ u8 beacon_filter_reason;
+ u8 beacon_filter_current_energy;
+ u8 beacon_filter_reserved;
+ __le32 beacon_filter_delta_time;
+ struct mvm_statistics_bt_activity bt_activity;
+ __le64 rx_time;
+ __le64 on_time_rf;
+ __le64 on_time_scan;
+ __le64 tx_time;
+ __le32 beacon_counter[NUM_MAC_INDEX];
+ u8 beacon_average_energy[NUM_MAC_INDEX];
+ u8 reserved[4 - (NUM_MAC_INDEX % 4)];
+} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */
+
+struct mvm_statistics_rx {
+ struct mvm_statistics_rx_phy ofdm;
+ struct mvm_statistics_rx_phy cck;
+ struct mvm_statistics_rx_non_phy general;
+ struct mvm_statistics_rx_ht_phy ofdm_ht;
+} __packed; /* STATISTICS_RX_API_S_VER_3 */
+
+/*
+ * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
+ *
+ * By default, uCode issues this notification after receiving a beacon
+ * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
+ * STATISTICS_CMD (0x9c), below.
+ */
+
+struct iwl_notif_statistics_v10 {
+ __le32 flag;
+ struct mvm_statistics_rx rx;
+ struct mvm_statistics_tx tx;
+ struct mvm_statistics_general_v8 general;
+} __packed; /* STATISTICS_NTFY_API_S_VER_10 */
+
+#define IWL_STATISTICS_FLG_CLEAR 0x1
+#define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2
+
+struct iwl_statistics_cmd {
+ __le32 flags;
+} __packed; /* STATISTICS_CMD_API_S_VER_1 */
+
+#endif /* __fw_api_stats_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h
new file mode 100644
index 000000000000..86aa51b2210e
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h
@@ -0,0 +1,386 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __fw_api_tof_h__
+#define __fw_api_tof_h__
+
+#include "fw-api.h"
+
+/* ToF sub-group command IDs */
+enum iwl_mvm_tof_sub_grp_ids {
+ TOF_RANGE_REQ_CMD = 0x1,
+ TOF_CONFIG_CMD = 0x2,
+ TOF_RANGE_ABORT_CMD = 0x3,
+ TOF_RANGE_REQ_EXT_CMD = 0x4,
+ TOF_RESPONDER_CONFIG_CMD = 0x5,
+ TOF_NW_INITIATED_RES_SEND_CMD = 0x6,
+ TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7,
+ TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC,
+ TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD,
+ TOF_RANGE_RESPONSE_NOTIF = 0xFE,
+ TOF_MCSI_DEBUG_NOTIF = 0xFB,
+};
+
+/**
+ * struct iwl_tof_config_cmd - ToF configuration
+ * @tof_disabled: 0 enabled, 1 - disabled
+ * @one_sided_disabled: 0 enabled, 1 - disabled
+ * @is_debug_mode: 1 debug mode, 0 - otherwise
+ * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise
+ */
+struct iwl_tof_config_cmd {
+ __le32 sub_grp_cmd_id;
+ u8 tof_disabled;
+ u8 one_sided_disabled;
+ u8 is_debug_mode;
+ u8 is_buf_required;
+} __packed;
+
+/**
+ * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug)
+ * @burst_period: future use: (currently hard coded in the LMAC)
+ * The interval between two sequential bursts.
+ * @min_delta_ftm: future use: (currently hard coded in the LMAC)
+ * The minimum delay between two sequential FTM Responses
+ * in the same burst.
+ * @burst_duration: future use: (currently hard coded in the LMAC)
+ * The total time for all FTMs handshake in the same burst.
+ * Affect the time events duration in the LMAC.
+ * @num_of_burst_exp: future use: (currently hard coded in the LMAC)
+ * The number of bursts for the current ToF request. Affect
+ * the number of events allocations in the current iteration.
+ * @get_ch_est: for xVT only, NA for driver
+ * @abort_responder: when set to '1' - Responder will terminate its activity
+ * (all other fields in the command are ignored)
+ * @recv_sta_req_params: 1 - Responder will ignore the other Responder's
+ * params and use the recomended Initiator params.
+ * 0 - otherwise
+ * @channel_num: current AP Channel
+ * @bandwidth: current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz
+ * @rate: current AP rate
+ * @ctrl_ch_position: coding of the control channel position relative to
+ * the center frequency.
+ * 40MHz 0 below center, 1 above center
+ * 80MHz bits [0..1]: 0 the near 20MHz to the center,
+ * 1 the far 20MHz to the center
+ * bit[2] as above 40MHz
+ * @ftm_per_burst: FTMs per Burst
+ * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response,
+ * '1' - we measure over the Initial FTM Response
+ * @asap_mode: ASAP / Non ASAP mode for the current WLS station
+ * @sta_id: index of the AP STA when in AP mode
+ * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF
+ * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug
+ * purposes, simulating station movement by adding various values
+ * to this field
+ * @bssid: Current AP BSSID
+ */
+struct iwl_tof_responder_config_cmd {
+ __le32 sub_grp_cmd_id;
+ __le16 burst_period;
+ u8 min_delta_ftm;
+ u8 burst_duration;
+ u8 num_of_burst_exp;
+ u8 get_ch_est;
+ u8 abort_responder;
+ u8 recv_sta_req_params;
+ u8 channel_num;
+ u8 bandwidth;
+ u8 rate;
+ u8 ctrl_ch_position;
+ u8 ftm_per_burst;
+ u8 ftm_resp_ts_avail;
+ u8 asap_mode;
+ u8 sta_id;
+ __le16 tsf_timer_offset_msecs;
+ __le16 toa_offset;
+ u8 bssid[ETH_ALEN];
+} __packed;
+
+/**
+ * struct iwl_tof_range_request_ext_cmd - extended range req for WLS
+ * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF
+ * @min_delta_ftm: Minimal time between two consecutive measurements,
+ * in units of 100us. 0 means no preference by station
+ * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended
+ * value be sent to the AP
+ * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended
+ * value to be sent to the AP
+ * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended
+ * value to be sent to the AP
+ */
+struct iwl_tof_range_req_ext_cmd {
+ __le32 sub_grp_cmd_id;
+ __le16 tsf_timer_offset_msec;
+ __le16 reserved;
+ u8 min_delta_ftm;
+ u8 ftm_format_and_bw20M;
+ u8 ftm_format_and_bw40M;
+ u8 ftm_format_and_bw80M;
+} __packed;
+
+#define IWL_MVM_TOF_MAX_APS 21
+
+/**
+ * struct iwl_tof_range_req_ap_entry - AP configuration parameters
+ * @channel_num: Current AP Channel
+ * @bandwidth: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz
+ * @tsf_delta_direction: TSF relatively to the subject AP
+ * @ctrl_ch_position: Coding of the control channel position relative to the
+ * center frequency.
+ * 40MHz 0 below center, 1 above center
+ * 80MHz bits [0..1]: 0 the near 20MHz to the center,
+ * 1 the far 20MHz to the center
+ * bit[2] as above 40MHz
+ * @bssid: AP's bss id
+ * @measure_type: Measurement type: 0 - two sided, 1 - One sided
+ * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of the
+ * number of measurement iterations (min 2^0 = 1, max 2^14)
+ * @burst_period: Recommended value to be sent to the AP. Measurement
+ * periodicity In units of 100ms. ignored if num_of_bursts = 0
+ * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31)
+ * 1-sided: how many rts/cts pairs should be used per burst.
+ * @retries_per_sample: Max number of retries that the LMAC should send
+ * in case of no replies by the AP.
+ * @tsf_delta: TSF Delta in units of microseconds.
+ * The difference between the AP TSF and the device local clock.
+ * @location_req: Location Request Bit[0] LCI should be sent in the FTMR
+ * Bit[1] Civic should be sent in the FTMR
+ * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided)
+ * @enable_dyn_ack: Enable Dynamic ACK BW.
+ * 0 Initiator interact with regular AP
+ * 1 Initiator interact with Responder machine: need to send the
+ * Initiator Acks with HT 40MHz / 80MHz, since the Responder should
+ * use it for its ch est measurement (this flag will be set when we
+ * configure the opposite machine to be Responder).
+ * @rssi: Last received value
+ * leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value.
+ */
+struct iwl_tof_range_req_ap_entry {
+ u8 channel_num;
+ u8 bandwidth;
+ u8 tsf_delta_direction;
+ u8 ctrl_ch_position;
+ u8 bssid[ETH_ALEN];
+ u8 measure_type;
+ u8 num_of_bursts;
+ __le16 burst_period;
+ u8 samples_per_burst;
+ u8 retries_per_sample;
+ __le32 tsf_delta;
+ u8 location_req;
+ u8 asap_mode;
+ u8 enable_dyn_ack;
+ s8 rssi;
+} __packed;
+
+/**
+ * enum iwl_tof_response_mode
+ * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as
+ * possible (not supported for this release)
+ * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon
+ * timeout expiration
+ * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the
+ * earlier of: measurements completion / timeout
+ * expiration.
+ */
+enum iwl_tof_response_mode {
+ IWL_MVM_TOF_RESPOSE_ASAP = 1,
+ IWL_MVM_TOF_RESPOSE_TIMEOUT,
+ IWL_MVM_TOF_RESPOSE_COMPLETE,
+};
+
+/**
+ * struct iwl_tof_range_req_cmd - start measurement cmd
+ * @request_id: A Token incremented per request. The same Token will be
+ * sent back in the range response
+ * @initiator: 0- NW initiated, 1 - Client Initiated
+ * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided,
+ * '1' - run ML-Algo for ToF only
+ * @req_timeout: Requested timeout of the response in units of 100ms.
+ * This is equivalent to the session time configured to the
+ * LMAC in Initiator Request
+ * @report_policy: Supported partially for this release: For current release -
+ * the range report will be uploaded as a batch when ready or
+ * when the session is done (successfully / partially).
+ * one of iwl_tof_response_mode.
+ * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @macaddr_random: '0' Use default source MAC address (i.e. p2_p),
+ * '1' Use MAC Address randomization according to the below
+ * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
+ * Bits set to 1 shall be randomized by the UMAC
+ */
+struct iwl_tof_range_req_cmd {
+ __le32 sub_grp_cmd_id;
+ u8 request_id;
+ u8 initiator;
+ u8 one_sided_los_disable;
+ u8 req_timeout;
+ u8 report_policy;
+ u8 los_det_disable;
+ u8 num_of_ap;
+ u8 macaddr_random;
+ u8 macaddr_template[ETH_ALEN];
+ u8 macaddr_mask[ETH_ALEN];
+ struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS];
+} __packed;
+
+/**
+ * struct iwl_tof_gen_resp_cmd - generic ToF response
+ */
+struct iwl_tof_gen_resp_cmd {
+ __le32 sub_grp_cmd_id;
+ u8 data[];
+} __packed;
+
+/**
+ * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response)
+ * @measure_status: current APs measurement status
+ * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz
+ * @rtt: The Round Trip Time that took for the last measurement for
+ * current AP [nSec]
+ * @rtt_variance: The Variance of the RTT values measured for current AP
+ * @rtt_spread: The Difference between the maximum and the minimum RTT
+ * values measured for current AP in the current session [nsec]
+ * @rssi: RSSI as uploaded in the Channel Estimation notification
+ * @rssi_spread: The Difference between the maximum and the minimum RSSI values
+ * measured for current AP in the current session
+ * @range: Measured range [cm]
+ * @range_variance: Measured range variance [cm]
+ * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was
+ * uploaded by the LMAC
+ */
+struct iwl_tof_range_rsp_ap_entry_ntfy {
+ u8 bssid[ETH_ALEN];
+ u8 measure_status;
+ u8 measure_bw;
+ __le32 rtt;
+ __le32 rtt_variance;
+ __le32 rtt_spread;
+ s8 rssi;
+ u8 rssi_spread;
+ __le16 reserved;
+ __le32 range;
+ __le32 range_variance;
+ __le32 timestamp;
+} __packed;
+
+/**
+ * struct iwl_tof_range_rsp_ntfy -
+ * @request_id: A Token ID of the corresponding Range request
+ * @request_status: status of current measurement session
+ * @last_in_batch: reprot policy (when not all responses are uploaded at once)
+ * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ */
+struct iwl_tof_range_rsp_ntfy {
+ u8 request_id;
+ u8 request_status;
+ u8 last_in_batch;
+ u8 num_of_aps;
+ struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS];
+} __packed;
+
+#define IWL_MVM_TOF_MCSI_BUF_SIZE (245)
+/**
+ * struct iwl_tof_mcsi_notif - used for debug
+ * @token: token ID for the current session
+ * @role: '0' - initiator, '1' - responder
+ * @initiator_bssid: initiator machine
+ * @responder_bssid: responder machine
+ * @mcsi_buffer: debug data
+ */
+struct iwl_tof_mcsi_notif {
+ u8 token;
+ u8 role;
+ __le16 reserved;
+ u8 initiator_bssid[ETH_ALEN];
+ u8 responder_bssid[ETH_ALEN];
+ u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4];
+} __packed;
+
+/**
+ * struct iwl_tof_neighbor_report_notif
+ * @bssid: BSSID of the AP which sent the report
+ * @request_token: same token as the corresponding request
+ * @status:
+ * @report_ie_len: the length of the response frame starting from the Element ID
+ * @data: the IEs
+ */
+struct iwl_tof_neighbor_report {
+ u8 bssid[ETH_ALEN];
+ u8 request_token;
+ u8 status;
+ __le16 report_ie_len;
+ u8 data[];
+} __packed;
+
+/**
+ * struct iwl_tof_range_abort_cmd
+ * @request_id: corresponds to a range request
+ */
+struct iwl_tof_range_abort_cmd {
+ __le32 sub_grp_cmd_id;
+ u8 request_id;
+ u8 reserved[3];
+} __packed;
+
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h
new file mode 100644
index 000000000000..0036d18334af
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h
@@ -0,0 +1,646 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_tx_h__
+#define __fw_api_tx_h__
+
+/**
+ * enum iwl_tx_flags - bitmasks for tx_flags in TX command
+ * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame
+ * @TX_CMD_FLG_WRITE_TX_POWER: update current tx power value in the mgmt frame
+ * @TX_CMD_FLG_ACK: expect ACK from receiving station
+ * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
+ * Otherwise, use rate_n_flags from the TX command
+ * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
+ * Must set TX_CMD_FLG_ACK with this flag.
+ * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
+ * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
+ * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
+ * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored
+ * on old firmwares).
+ * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame
+ * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
+ * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
+ * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
+ * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
+ * Should be set for beacons and probe responses
+ * @TX_CMD_FLG_CALIB: activate PA TX power calibrations
+ * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
+ * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
+ * Should be set for 26/30 length MAC headers
+ * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
+ * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
+ * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
+ * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
+ * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
+ * @TX_CMD_FLG_EXEC_PAPD: execute PAPD
+ * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power
+ * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk
+ */
+enum iwl_tx_flags {
+ TX_CMD_FLG_PROT_REQUIRE = BIT(0),
+ TX_CMD_FLG_WRITE_TX_POWER = BIT(1),
+ TX_CMD_FLG_ACK = BIT(3),
+ TX_CMD_FLG_STA_RATE = BIT(4),
+ TX_CMD_FLG_BAR = BIT(6),
+ TX_CMD_FLG_TXOP_PROT = BIT(7),
+ TX_CMD_FLG_VHT_NDPA = BIT(8),
+ TX_CMD_FLG_HT_NDPA = BIT(9),
+ TX_CMD_FLG_CSI_FDBK2HOST = BIT(10),
+ TX_CMD_FLG_BT_PRIO_POS = 11,
+ TX_CMD_FLG_BT_DIS = BIT(12),
+ TX_CMD_FLG_SEQ_CTL = BIT(13),
+ TX_CMD_FLG_MORE_FRAG = BIT(14),
+ TX_CMD_FLG_TSF = BIT(16),
+ TX_CMD_FLG_CALIB = BIT(17),
+ TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18),
+ TX_CMD_FLG_MH_PAD = BIT(20),
+ TX_CMD_FLG_RESP_TO_DRV = BIT(21),
+ TX_CMD_FLG_CCMP_AGG = BIT(22),
+ TX_CMD_FLG_TKIP_MIC_DONE = BIT(23),
+ TX_CMD_FLG_DUR = BIT(25),
+ TX_CMD_FLG_FW_DROP = BIT(26),
+ TX_CMD_FLG_EXEC_PAPD = BIT(27),
+ TX_CMD_FLG_PAPD_TYPE = BIT(28),
+ TX_CMD_FLG_HCCA_CHUNK = BIT(31)
+}; /* TX_FLAGS_BITS_API_S_VER_1 */
+
+/**
+ * enum iwl_tx_pm_timeouts - pm timeout values in TX command
+ * @PM_FRAME_NONE: no need to suspend sleep mode
+ * @PM_FRAME_MGMT: fw suspend sleep mode for 100TU
+ * @PM_FRAME_ASSOC: fw suspend sleep mode for 10sec
+ */
+enum iwl_tx_pm_timeouts {
+ PM_FRAME_NONE = 0,
+ PM_FRAME_MGMT = 2,
+ PM_FRAME_ASSOC = 3,
+};
+
+/*
+ * TX command security control
+ */
+#define TX_CMD_SEC_WEP 0x01
+#define TX_CMD_SEC_CCM 0x02
+#define TX_CMD_SEC_TKIP 0x03
+#define TX_CMD_SEC_EXT 0x04
+#define TX_CMD_SEC_MSK 0x07
+#define TX_CMD_SEC_WEP_KEY_IDX_POS 6
+#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0
+#define TX_CMD_SEC_KEY128 0x08
+
+/* TODO: how does these values are OK with only 16 bit variable??? */
+/*
+ * TX command next frame info
+ *
+ * bits 0:2 - security control (TX_CMD_SEC_*)
+ * bit 3 - immediate ACK required
+ * bit 4 - rate is taken from STA table
+ * bit 5 - frame belongs to BA stream
+ * bit 6 - immediate BA response expected
+ * bit 7 - unused
+ * bits 8:15 - Station ID
+ * bits 16:31 - rate
+ */
+#define TX_CMD_NEXT_FRAME_ACK_MSK (0x8)
+#define TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10)
+#define TX_CMD_NEXT_FRAME_BA_MSK (0x20)
+#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40)
+#define TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8)
+#define TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00)
+#define TX_CMD_NEXT_FRAME_STA_ID_POS (8)
+#define TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000)
+#define TX_CMD_NEXT_FRAME_RATE_POS (16)
+
+/*
+ * TX command Frame life time in us - to be written in pm_frame_timeout
+ */
+#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF
+#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/
+#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */
+#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0
+
+/*
+ * TID for non QoS frames - to be written in tid_tspec
+ */
+#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT
+
+/*
+ * Limits on the retransmissions - to be written in {data,rts}_retry_limit
+ */
+#define IWL_DEFAULT_TX_RETRY 15
+#define IWL_MGMT_DFAULT_RETRY_LIMIT 3
+#define IWL_RTS_DFAULT_RETRY_LIMIT 60
+#define IWL_BAR_DFAULT_RETRY_LIMIT 60
+#define IWL_LOW_RETRY_LIMIT 7
+
+/* TODO: complete documentation for try_cnt and btkill_cnt */
+/**
+ * struct iwl_tx_cmd - TX command struct to FW
+ * ( TX_CMD = 0x1c )
+ * @len: in bytes of the payload, see below for details
+ * @tx_flags: combination of TX_CMD_FLG_*
+ * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
+ * cleared. Combination of RATE_MCS_*
+ * @sta_id: index of destination station in FW station table
+ * @sec_ctl: security control, TX_CMD_SEC_*
+ * @initial_rate_index: index into the the rate table for initial TX attempt.
+ * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames.
+ * @key: security key
+ * @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_*
+ * @life_time: frame life time (usecs??)
+ * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt +
+ * btkill_cnd + reserved), first 32 bits. "0" disables usage.
+ * @dram_msb_ptr: upper bits of the scratch physical address
+ * @rts_retry_limit: max attempts for RTS
+ * @data_retry_limit: max attempts to send the data packet
+ * @tid_spec: TID/tspec
+ * @pm_frame_timeout: PM TX frame timeout
+ *
+ * The byte count (both len and next_frame_len) includes MAC header
+ * (24/26/30/32 bytes)
+ * + 2 bytes pad if 26/30 header size
+ * + 8 byte IV for CCM or TKIP (not used for WEP)
+ * + Data payload
+ * + 8-byte MIC (not used for CCM/WEP)
+ * It does not include post-MAC padding, i.e.,
+ * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.
+ * Range of len: 14-2342 bytes.
+ *
+ * After the struct fields the MAC header is placed, plus any padding,
+ * and then the actial payload.
+ */
+struct iwl_tx_cmd {
+ __le16 len;
+ __le16 next_frame_len;
+ __le32 tx_flags;
+ 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;
+ u8 initial_rate_index;
+ u8 reserved2;
+ u8 key[16];
+ __le32 reserved3;
+ __le32 life_time;
+ __le32 dram_lsb_ptr;
+ u8 dram_msb_ptr;
+ u8 rts_retry_limit;
+ u8 data_retry_limit;
+ u8 tid_tspec;
+ __le16 pm_frame_timeout;
+ __le16 reserved4;
+ u8 payload[0];
+ struct ieee80211_hdr hdr[0];
+} __packed; /* TX_CMD_API_S_VER_3 */
+
+/*
+ * TX response related data
+ */
+
+/*
+ * enum iwl_tx_status - status that is returned by the fw after attempts to Tx
+ * @TX_STATUS_SUCCESS:
+ * @TX_STATUS_DIRECT_DONE:
+ * @TX_STATUS_POSTPONE_DELAY:
+ * @TX_STATUS_POSTPONE_FEW_BYTES:
+ * @TX_STATUS_POSTPONE_BT_PRIO:
+ * @TX_STATUS_POSTPONE_QUIET_PERIOD:
+ * @TX_STATUS_POSTPONE_CALC_TTAK:
+ * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
+ * @TX_STATUS_FAIL_SHORT_LIMIT:
+ * @TX_STATUS_FAIL_LONG_LIMIT:
+ * @TX_STATUS_FAIL_UNDERRUN:
+ * @TX_STATUS_FAIL_DRAIN_FLOW:
+ * @TX_STATUS_FAIL_RFKILL_FLUSH:
+ * @TX_STATUS_FAIL_LIFE_EXPIRE:
+ * @TX_STATUS_FAIL_DEST_PS:
+ * @TX_STATUS_FAIL_HOST_ABORTED:
+ * @TX_STATUS_FAIL_BT_RETRY:
+ * @TX_STATUS_FAIL_STA_INVALID:
+ * @TX_TATUS_FAIL_FRAG_DROPPED:
+ * @TX_STATUS_FAIL_TID_DISABLE:
+ * @TX_STATUS_FAIL_FIFO_FLUSHED:
+ * @TX_STATUS_FAIL_SMALL_CF_POLL:
+ * @TX_STATUS_FAIL_FW_DROP:
+ * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and
+ * STA table
+ * @TX_FRAME_STATUS_INTERNAL_ABORT:
+ * @TX_MODE_MSK:
+ * @TX_MODE_NO_BURST:
+ * @TX_MODE_IN_BURST_SEQ:
+ * @TX_MODE_FIRST_IN_BURST:
+ * @TX_QUEUE_NUM_MSK:
+ *
+ * Valid only if frame_count =1
+ * TODO: complete documentation
+ */
+enum iwl_tx_status {
+ TX_STATUS_MSK = 0x000000ff,
+ TX_STATUS_SUCCESS = 0x01,
+ TX_STATUS_DIRECT_DONE = 0x02,
+ /* postpone TX */
+ TX_STATUS_POSTPONE_DELAY = 0x40,
+ TX_STATUS_POSTPONE_FEW_BYTES = 0x41,
+ TX_STATUS_POSTPONE_BT_PRIO = 0x42,
+ TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43,
+ TX_STATUS_POSTPONE_CALC_TTAK = 0x44,
+ /* abort TX */
+ TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81,
+ TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
+ TX_STATUS_FAIL_LONG_LIMIT = 0x83,
+ TX_STATUS_FAIL_UNDERRUN = 0x84,
+ TX_STATUS_FAIL_DRAIN_FLOW = 0x85,
+ TX_STATUS_FAIL_RFKILL_FLUSH = 0x86,
+ TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
+ TX_STATUS_FAIL_DEST_PS = 0x88,
+ TX_STATUS_FAIL_HOST_ABORTED = 0x89,
+ TX_STATUS_FAIL_BT_RETRY = 0x8a,
+ TX_STATUS_FAIL_STA_INVALID = 0x8b,
+ TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
+ TX_STATUS_FAIL_TID_DISABLE = 0x8d,
+ TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
+ TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f,
+ TX_STATUS_FAIL_FW_DROP = 0x90,
+ TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91,
+ TX_STATUS_INTERNAL_ABORT = 0x92,
+ TX_MODE_MSK = 0x00000f00,
+ TX_MODE_NO_BURST = 0x00000000,
+ TX_MODE_IN_BURST_SEQ = 0x00000100,
+ TX_MODE_FIRST_IN_BURST = 0x00000200,
+ TX_QUEUE_NUM_MSK = 0x0001f000,
+ TX_NARROW_BW_MSK = 0x00060000,
+ TX_NARROW_BW_1DIV2 = 0x00020000,
+ TX_NARROW_BW_1DIV4 = 0x00040000,
+ TX_NARROW_BW_1DIV8 = 0x00060000,
+};
+
+/*
+ * enum iwl_tx_agg_status - TX aggregation status
+ * @AGG_TX_STATE_STATUS_MSK:
+ * @AGG_TX_STATE_TRANSMITTED:
+ * @AGG_TX_STATE_UNDERRUN:
+ * @AGG_TX_STATE_BT_PRIO:
+ * @AGG_TX_STATE_FEW_BYTES:
+ * @AGG_TX_STATE_ABORT:
+ * @AGG_TX_STATE_LAST_SENT_TTL:
+ * @AGG_TX_STATE_LAST_SENT_TRY_CNT:
+ * @AGG_TX_STATE_LAST_SENT_BT_KILL:
+ * @AGG_TX_STATE_SCD_QUERY:
+ * @AGG_TX_STATE_TEST_BAD_CRC32:
+ * @AGG_TX_STATE_RESPONSE:
+ * @AGG_TX_STATE_DUMP_TX:
+ * @AGG_TX_STATE_DELAY_TX:
+ * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries
+ * occur if tx failed for this frame when it was a member of a previous
+ * aggregation block). If rate scaling is used, retry count indicates the
+ * rate table entry used for all frames in the new agg.
+ *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for
+ * this frame
+ *
+ * TODO: complete documentation
+ */
+enum iwl_tx_agg_status {
+ AGG_TX_STATE_STATUS_MSK = 0x00fff,
+ AGG_TX_STATE_TRANSMITTED = 0x000,
+ AGG_TX_STATE_UNDERRUN = 0x001,
+ AGG_TX_STATE_BT_PRIO = 0x002,
+ AGG_TX_STATE_FEW_BYTES = 0x004,
+ AGG_TX_STATE_ABORT = 0x008,
+ AGG_TX_STATE_LAST_SENT_TTL = 0x010,
+ AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020,
+ AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040,
+ AGG_TX_STATE_SCD_QUERY = 0x080,
+ AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100,
+ AGG_TX_STATE_RESPONSE = 0x1ff,
+ AGG_TX_STATE_DUMP_TX = 0x200,
+ AGG_TX_STATE_DELAY_TX = 0x400,
+ AGG_TX_STATE_TRY_CNT_POS = 12,
+ AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS,
+};
+
+#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \
+ AGG_TX_STATE_LAST_SENT_TRY_CNT| \
+ AGG_TX_STATE_LAST_SENT_BT_KILL)
+
+/*
+ * The mask below describes a status where we are absolutely sure that the MPDU
+ * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've
+ * written the bytes to the TXE, but we know nothing about what the DSP did.
+ */
+#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \
+ AGG_TX_STATE_ABORT | \
+ AGG_TX_STATE_SCD_QUERY)
+
+/*
+ * REPLY_TX = 0x1c (response)
+ *
+ * This response may be in one of two slightly different formats, indicated
+ * by the frame_count field:
+ *
+ * 1) No aggregation (frame_count == 1). This reports Tx results for a single
+ * frame. Multiple attempts, at various bit rates, may have been made for
+ * this frame.
+ *
+ * 2) Aggregation (frame_count > 1). This reports Tx results for two or more
+ * frames that used block-acknowledge. All frames were transmitted at
+ * same rate. Rate scaling may have been used if first frame in this new
+ * agg block failed in previous agg block(s).
+ *
+ * Note that, for aggregation, ACK (block-ack) status is not delivered
+ * here; block-ack has not been received by the time the device records
+ * this status.
+ * This status relates to reasons the tx might have been blocked or aborted
+ * within the device, rather than whether it was received successfully by
+ * the destination station.
+ */
+
+/**
+ * struct agg_tx_status - per packet TX aggregation status
+ * @status: enum iwl_tx_agg_status
+ * @sequence: Sequence # for this frame's Tx cmd (not SSN!)
+ */
+struct agg_tx_status {
+ __le16 status;
+ __le16 sequence;
+} __packed;
+
+/*
+ * definitions for initial rate index field
+ * bits [3:0] initial rate index
+ * bits [6:4] rate table color, used for the initial rate
+ * bit-7 invalid rate indication
+ */
+#define TX_RES_INIT_RATE_INDEX_MSK 0x0f
+#define TX_RES_RATE_TABLE_COLOR_MSK 0x70
+#define TX_RES_INV_RATE_INDEX_MSK 0x80
+
+#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f)
+#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4)
+
+/**
+ * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet
+ * ( REPLY_TX = 0x1c )
+ * @frame_count: 1 no aggregation, >1 aggregation
+ * @bt_kill_count: num of times blocked by bluetooth (unused for agg)
+ * @failure_rts: num of failures due to unsuccessful RTS
+ * @failure_frame: num failures due to no ACK (unused for agg)
+ * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the
+ * Tx of all the batch. RATE_MCS_*
+ * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK.
+ * for agg: RTS + CTS + aggregation tx time + block-ack time.
+ * in usec.
+ * @pa_status: tx power info
+ * @pa_integ_res_a: tx power info
+ * @pa_integ_res_b: tx power info
+ * @pa_integ_res_c: tx power info
+ * @measurement_req_id: tx power info
+ * @tfd_info: TFD information set by the FH
+ * @seq_ctl: sequence control from the Tx cmd
+ * @byte_cnt: byte count from the Tx cmd
+ * @tlc_info: TLC rate info
+ * @ra_tid: bits [3:0] = ra, bits [7:4] = tid
+ * @frame_ctrl: frame control
+ * @status: for non-agg: frame status TX_STATUS_*
+ * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields
+ * follow this one, up to frame_count.
+ *
+ * After the array of statuses comes the SSN of the SCD. Look at
+ * %iwl_mvm_get_scd_ssn for more details.
+ */
+struct iwl_mvm_tx_resp {
+ u8 frame_count;
+ u8 bt_kill_count;
+ u8 failure_rts;
+ u8 failure_frame;
+ __le32 initial_rate;
+ __le16 wireless_media_time;
+
+ u8 pa_status;
+ u8 pa_integ_res_a[3];
+ u8 pa_integ_res_b[3];
+ u8 pa_integ_res_c[3];
+ __le16 measurement_req_id;
+ u8 reduced_tpc;
+ u8 reserved;
+
+ __le32 tfd_info;
+ __le16 seq_ctl;
+ __le16 byte_cnt;
+ u8 tlc_info;
+ u8 ra_tid;
+ __le16 frame_ctrl;
+
+ struct agg_tx_status status;
+} __packed; /* TX_RSP_API_S_VER_3 */
+
+/**
+ * struct iwl_mvm_ba_notif - notifies about reception of BA
+ * ( BA_NOTIF = 0xc5 )
+ * @sta_addr_lo32: lower 32 bits of the MAC address
+ * @sta_addr_hi16: upper 16 bits of the MAC address
+ * @sta_id: Index of recipient (BA-sending) station in fw's station table
+ * @tid: tid of the session
+ * @seq_ctl:
+ * @bitmap: the bitmap of the BA notification as seen in the air
+ * @scd_flow: the tx queue this BA relates to
+ * @scd_ssn: the index of the last contiguously sent packet
+ * @txed: number of Txed frames in this batch
+ * @txed_2_done: number of Acked frames in this batch
+ */
+struct iwl_mvm_ba_notif {
+ __le32 sta_addr_lo32;
+ __le16 sta_addr_hi16;
+ __le16 reserved;
+
+ u8 sta_id;
+ u8 tid;
+ __le16 seq_ctl;
+ __le64 bitmap;
+ __le16 scd_flow;
+ __le16 scd_ssn;
+ u8 txed;
+ u8 txed_2_done;
+ __le16 reserved1;
+} __packed;
+
+/*
+ * struct iwl_mac_beacon_cmd - beacon template command
+ * @tx: the tx commands associated with the beacon frame
+ * @template_id: currently equal to the mac context id of the coresponding
+ * mac.
+ * @tim_idx: the offset of the tim IE in the beacon
+ * @tim_size: the length of the tim IE
+ * @frame: the template of the beacon frame
+ */
+struct iwl_mac_beacon_cmd {
+ struct iwl_tx_cmd tx;
+ __le32 template_id;
+ __le32 tim_idx;
+ __le32 tim_size;
+ struct ieee80211_hdr frame[0];
+} __packed;
+
+struct iwl_beacon_notif {
+ struct iwl_mvm_tx_resp beacon_notify_hdr;
+ __le64 tsf;
+ __le32 ibss_mgr_status;
+} __packed;
+
+/**
+ * struct iwl_extended_beacon_notif - notifies about beacon transmission
+ * @beacon_notify_hdr: tx response command associated with the beacon
+ * @tsf: last beacon tsf
+ * @ibss_mgr_status: whether IBSS is manager
+ * @gp2: last beacon time in gp2
+ */
+struct iwl_extended_beacon_notif {
+ struct iwl_mvm_tx_resp beacon_notify_hdr;
+ __le64 tsf;
+ __le32 ibss_mgr_status;
+ __le32 gp2;
+} __packed; /* BEACON_NTFY_API_S_VER_5 */
+
+/**
+ * enum iwl_dump_control - dump (flush) control flags
+ * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
+ * and the TFD queues are empty.
+ */
+enum iwl_dump_control {
+ DUMP_TX_FIFO_FLUSH = BIT(1),
+};
+
+/**
+ * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command
+ * @queues_ctl: bitmap of queues to flush
+ * @flush_ctl: control flags
+ * @reserved: reserved
+ */
+struct iwl_tx_path_flush_cmd {
+ __le32 queues_ctl;
+ __le16 flush_ctl;
+ __le16 reserved;
+} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */
+
+/**
+ * iwl_mvm_get_scd_ssn - returns the SSN of the SCD
+ * @tx_resp: the Tx response from the fw (agg or non-agg)
+ *
+ * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since
+ * it can't know that everything will go well until the end of the AMPDU, it
+ * can't know in advance the number of MPDUs that will be sent in the current
+ * batch. This is why it writes the agg Tx response while it fetches the MPDUs.
+ * Hence, it can't know in advance what the SSN of the SCD will be at the end
+ * of the batch. This is why the SSN of the SCD is written at the end of the
+ * whole struct at a variable offset. This function knows how to cope with the
+ * variable offset and returns the SSN of the SCD.
+ */
+static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp)
+{
+ return le32_to_cpup((__le32 *)&tx_resp->status +
+ tx_resp->frame_count) & 0xfff;
+}
+
+/**
+ * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command
+ * @token:
+ * @sta_id: station id
+ * @tid:
+ * @scd_queue: scheduler queue to confiug
+ * @enable: 1 queue enable, 0 queue disable
+ * @aggregate: 1 aggregated queue, 0 otherwise
+ * @tx_fifo: %enum iwl_mvm_tx_fifo
+ * @window: BA window size
+ * @ssn: SSN for the BA agreement
+ */
+struct iwl_scd_txq_cfg_cmd {
+ u8 token;
+ u8 sta_id;
+ u8 tid;
+ u8 scd_queue;
+ u8 enable;
+ u8 aggregate;
+ u8 tx_fifo;
+ u8 window;
+ __le16 ssn;
+ __le16 reserved;
+} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_scd_txq_cfg_rsp
+ * @token: taken from the command
+ * @sta_id: station id from the command
+ * @tid: tid from the command
+ * @scd_queue: scd_queue from the command
+ */
+struct iwl_scd_txq_cfg_rsp {
+ u8 token;
+ u8 sta_id;
+ u8 tid;
+ u8 scd_queue;
+} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */
+
+#endif /* __fw_api_tx_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
new file mode 100644
index 000000000000..82049bb139c2
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
@@ -0,0 +1,1854 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_h__
+#define __fw_api_h__
+
+#include "fw-api-rs.h"
+#include "fw-api-rx.h"
+#include "fw-api-tx.h"
+#include "fw-api-sta.h"
+#include "fw-api-mac.h"
+#include "fw-api-power.h"
+#include "fw-api-d3.h"
+#include "fw-api-coex.h"
+#include "fw-api-scan.h"
+#include "fw-api-stats.h"
+#include "fw-api-tof.h"
+
+/* Tx queue numbers */
+enum {
+ IWL_MVM_OFFCHANNEL_QUEUE = 8,
+ IWL_MVM_CMD_QUEUE = 9,
+};
+
+enum iwl_mvm_tx_fifo {
+ IWL_MVM_TX_FIFO_BK = 0,
+ IWL_MVM_TX_FIFO_BE,
+ IWL_MVM_TX_FIFO_VI,
+ IWL_MVM_TX_FIFO_VO,
+ IWL_MVM_TX_FIFO_MCAST = 5,
+ IWL_MVM_TX_FIFO_CMD = 7,
+};
+
+#define IWL_MVM_STATION_COUNT 16
+
+#define IWL_MVM_TDLS_STA_COUNT 4
+
+/* commands */
+enum {
+ MVM_ALIVE = 0x1,
+ REPLY_ERROR = 0x2,
+ ECHO_CMD = 0x3,
+
+ INIT_COMPLETE_NOTIF = 0x4,
+
+ /* PHY context commands */
+ PHY_CONTEXT_CMD = 0x8,
+ DBG_CFG = 0x9,
+ ANTENNA_COUPLING_NOTIFICATION = 0xa,
+
+ /* UMAC scan commands */
+ SCAN_ITERATION_COMPLETE_UMAC = 0xb5,
+ SCAN_CFG_CMD = 0xc,
+ SCAN_REQ_UMAC = 0xd,
+ SCAN_ABORT_UMAC = 0xe,
+ SCAN_COMPLETE_UMAC = 0xf,
+
+ /* station table */
+ ADD_STA_KEY = 0x17,
+ ADD_STA = 0x18,
+ REMOVE_STA = 0x19,
+
+ /* paging get item */
+ FW_GET_ITEM_CMD = 0x1a,
+
+ /* TX */
+ TX_CMD = 0x1c,
+ TXPATH_FLUSH = 0x1e,
+ MGMT_MCAST_KEY = 0x1f,
+
+ /* scheduler config */
+ SCD_QUEUE_CFG = 0x1d,
+
+ /* global key */
+ WEP_KEY = 0x20,
+
+ /* Memory */
+ SHARED_MEM_CFG = 0x25,
+
+ /* TDLS */
+ TDLS_CHANNEL_SWITCH_CMD = 0x27,
+ TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa,
+ TDLS_CONFIG_CMD = 0xa7,
+
+ /* MAC and Binding commands */
+ MAC_CONTEXT_CMD = 0x28,
+ TIME_EVENT_CMD = 0x29, /* both CMD and response */
+ TIME_EVENT_NOTIFICATION = 0x2a,
+ BINDING_CONTEXT_CMD = 0x2b,
+ TIME_QUOTA_CMD = 0x2c,
+ NON_QOS_TX_COUNTER_CMD = 0x2d,
+
+ LQ_CMD = 0x4e,
+
+ /* paging block to FW cpu2 */
+ FW_PAGING_BLOCK_CMD = 0x4f,
+
+ /* Scan offload */
+ SCAN_OFFLOAD_REQUEST_CMD = 0x51,
+ SCAN_OFFLOAD_ABORT_CMD = 0x52,
+ HOT_SPOT_CMD = 0x53,
+ SCAN_OFFLOAD_COMPLETE = 0x6D,
+ SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
+ SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
+ MATCH_FOUND_NOTIFICATION = 0xd9,
+ SCAN_ITERATION_COMPLETE = 0xe7,
+
+ /* Phy */
+ PHY_CONFIGURATION_CMD = 0x6a,
+ CALIB_RES_NOTIF_PHY_DB = 0x6b,
+ /* PHY_DB_CMD = 0x6c, */
+
+ /* ToF - 802.11mc FTM */
+ TOF_CMD = 0x10,
+ TOF_NOTIFICATION = 0x11,
+
+ /* Power - legacy power table command */
+ POWER_TABLE_CMD = 0x77,
+ PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
+ LTR_CONFIG = 0xee,
+
+ /* Thermal Throttling*/
+ REPLY_THERMAL_MNG_BACKOFF = 0x7e,
+
+ /* Set/Get DC2DC frequency tune */
+ DC2DC_CONFIG_CMD = 0x83,
+
+ /* NVM */
+ NVM_ACCESS_CMD = 0x88,
+
+ SET_CALIB_DEFAULT_CMD = 0x8e,
+
+ BEACON_NOTIFICATION = 0x90,
+ BEACON_TEMPLATE_CMD = 0x91,
+ TX_ANT_CONFIGURATION_CMD = 0x98,
+ STATISTICS_CMD = 0x9c,
+ STATISTICS_NOTIFICATION = 0x9d,
+ EOSP_NOTIFICATION = 0x9e,
+ REDUCE_TX_POWER_CMD = 0x9f,
+
+ /* RF-KILL commands and notifications */
+ CARD_STATE_CMD = 0xa0,
+ CARD_STATE_NOTIFICATION = 0xa1,
+
+ MISSED_BEACONS_NOTIFICATION = 0xa2,
+
+ /* Power - new power table command */
+ MAC_PM_POWER_TABLE = 0xa9,
+
+ MFUART_LOAD_NOTIFICATION = 0xb1,
+
+ REPLY_RX_PHY_CMD = 0xc0,
+ REPLY_RX_MPDU_CMD = 0xc1,
+ FRAME_RELEASE = 0xc3,
+ BA_NOTIF = 0xc5,
+
+ /* Location Aware Regulatory */
+ MCC_UPDATE_CMD = 0xc8,
+ MCC_CHUB_UPDATE_CMD = 0xc9,
+
+ MARKER_CMD = 0xcb,
+
+ /* BT Coex */
+ BT_COEX_PRIO_TABLE = 0xcc,
+ BT_COEX_PROT_ENV = 0xcd,
+ BT_PROFILE_NOTIFICATION = 0xce,
+ BT_CONFIG = 0x9b,
+ BT_COEX_UPDATE_SW_BOOST = 0x5a,
+ BT_COEX_UPDATE_CORUN_LUT = 0x5b,
+ BT_COEX_UPDATE_REDUCED_TXP = 0x5c,
+ BT_COEX_CI = 0x5d,
+
+ REPLY_SF_CFG_CMD = 0xd1,
+ REPLY_BEACON_FILTERING_CMD = 0xd2,
+
+ /* DTS measurements */
+ CMD_DTS_MEASUREMENT_TRIGGER = 0xdc,
+ DTS_MEASUREMENT_NOTIFICATION = 0xdd,
+
+ REPLY_DEBUG_CMD = 0xf0,
+ LDBG_CONFIG_CMD = 0xf6,
+ DEBUG_LOG_MSG = 0xf7,
+
+ BCAST_FILTER_CMD = 0xcf,
+ MCAST_FILTER_CMD = 0xd0,
+
+ /* D3 commands/notifications */
+ D3_CONFIG_CMD = 0xd3,
+ PROT_OFFLOAD_CONFIG_CMD = 0xd4,
+ OFFLOADS_QUERY_CMD = 0xd5,
+ REMOTE_WAKE_CONFIG_CMD = 0xd6,
+ D0I3_END_CMD = 0xed,
+
+ /* for WoWLAN in particular */
+ WOWLAN_PATTERNS = 0xe0,
+ WOWLAN_CONFIGURATION = 0xe1,
+ WOWLAN_TSC_RSC_PARAM = 0xe2,
+ WOWLAN_TKIP_PARAM = 0xe3,
+ WOWLAN_KEK_KCK_MATERIAL = 0xe4,
+ WOWLAN_GET_STATUSES = 0xe5,
+ WOWLAN_TX_POWER_PER_DB = 0xe6,
+
+ /* and for NetDetect */
+ SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56,
+ SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58,
+ SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59,
+
+ REPLY_MAX = 0xff,
+};
+
+/* Please keep this enum *SORTED* by hex value.
+ * Needed for binary search, otherwise a warning will be triggered.
+ */
+enum iwl_phy_ops_subcmd_ids {
+ CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
+ DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
+};
+
+/* command groups */
+enum {
+ LEGACY_GROUP = 0x0,
+ LONG_GROUP = 0x1,
+ PHY_OPS_GROUP = 0x4,
+};
+
+/**
+ * struct iwl_cmd_response - generic response struct for most commands
+ * @status: status of the command asked, changes for each one
+ */
+struct iwl_cmd_response {
+ __le32 status;
+};
+
+/*
+ * struct iwl_tx_ant_cfg_cmd
+ * @valid: valid antenna configuration
+ */
+struct iwl_tx_ant_cfg_cmd {
+ __le32 valid;
+} __packed;
+
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ * flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ * event triggers.
+ */
+struct iwl_calib_ctrl {
+ __le32 flow_trigger;
+ __le32 event_trigger;
+} __packed;
+
+/* This enum defines the bitmap of various calibrations to enable in both
+ * init ucode and runtime ucode through CALIBRATION_CFG_CMD.
+ */
+enum iwl_calib_cfg {
+ IWL_CALIB_CFG_XTAL_IDX = BIT(0),
+ IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1),
+ IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2),
+ IWL_CALIB_CFG_PAPD_IDX = BIT(3),
+ IWL_CALIB_CFG_TX_PWR_IDX = BIT(4),
+ IWL_CALIB_CFG_DC_IDX = BIT(5),
+ IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6),
+ IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7),
+ IWL_CALIB_CFG_TX_IQ_IDX = BIT(8),
+ IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9),
+ IWL_CALIB_CFG_RX_IQ_IDX = BIT(10),
+ IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11),
+ IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12),
+ IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13),
+ IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14),
+ IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15),
+ IWL_CALIB_CFG_DAC_IDX = BIT(16),
+ IWL_CALIB_CFG_ABS_IDX = BIT(17),
+ IWL_CALIB_CFG_AGC_IDX = BIT(18),
+};
+
+/*
+ * Phy configuration command.
+ */
+struct iwl_phy_cfg_cmd {
+ __le32 phy_cfg;
+ struct iwl_calib_ctrl calib_control;
+} __packed;
+
+#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1))
+#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3))
+#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5))
+#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7))
+#define PHY_CFG_TX_CHAIN_A BIT(8)
+#define PHY_CFG_TX_CHAIN_B BIT(9)
+#define PHY_CFG_TX_CHAIN_C BIT(10)
+#define PHY_CFG_RX_CHAIN_A BIT(12)
+#define PHY_CFG_RX_CHAIN_B BIT(13)
+#define PHY_CFG_RX_CHAIN_C BIT(14)
+
+
+/* Target of the NVM_ACCESS_CMD */
+enum {
+ NVM_ACCESS_TARGET_CACHE = 0,
+ NVM_ACCESS_TARGET_OTP = 1,
+ NVM_ACCESS_TARGET_EEPROM = 2,
+};
+
+/* Section types for NVM_ACCESS_CMD */
+enum {
+ NVM_SECTION_TYPE_SW = 1,
+ NVM_SECTION_TYPE_REGULATORY = 3,
+ NVM_SECTION_TYPE_CALIBRATION = 4,
+ NVM_SECTION_TYPE_PRODUCTION = 5,
+ NVM_SECTION_TYPE_MAC_OVERRIDE = 11,
+ NVM_SECTION_TYPE_PHY_SKU = 12,
+ NVM_MAX_NUM_SECTIONS = 13,
+};
+
+/**
+ * struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section
+ * @op_code: 0 - read, 1 - write
+ * @target: NVM_ACCESS_TARGET_*
+ * @type: NVM_SECTION_TYPE_*
+ * @offset: offset in bytes into the section
+ * @length: in bytes, to read/write
+ * @data: if write operation, the data to write. On read its empty
+ */
+struct iwl_nvm_access_cmd {
+ u8 op_code;
+ u8 target;
+ __le16 type;
+ __le16 offset;
+ __le16 length;
+ u8 data[];
+} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */
+
+#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */
+
+/*
+ * struct iwl_fw_paging_cmd - paging layout
+ *
+ * (FW_PAGING_BLOCK_CMD = 0x4f)
+ *
+ * Send to FW the paging layout in the driver.
+ *
+ * @flags: various flags for the command
+ * @block_size: the block size in powers of 2
+ * @block_num: number of blocks specified in the command.
+ * @device_phy_addr: virtual addresses from device side
+*/
+struct iwl_fw_paging_cmd {
+ __le32 flags;
+ __le32 block_size;
+ __le32 block_num;
+ __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS];
+} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */
+
+/*
+ * Fw items ID's
+ *
+ * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload
+ * download
+ */
+enum iwl_fw_item_id {
+ IWL_FW_ITEM_ID_PAGING = 3,
+};
+
+/*
+ * struct iwl_fw_get_item_cmd - get an item from the fw
+ */
+struct iwl_fw_get_item_cmd {
+ __le32 item_id;
+} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */
+
+#define CONT_REC_COMMAND_SIZE 80
+#define ENABLE_CONT_RECORDING 0x15
+#define DISABLE_CONT_RECORDING 0x16
+
+/*
+ * struct iwl_continuous_record_mode - recording mode
+ */
+struct iwl_continuous_record_mode {
+ __le16 enable_recording;
+} __packed;
+
+/*
+ * struct iwl_continuous_record_cmd - enable/disable continuous recording
+ */
+struct iwl_continuous_record_cmd {
+ struct iwl_continuous_record_mode record_mode;
+ u8 pad[CONT_REC_COMMAND_SIZE -
+ sizeof(struct iwl_continuous_record_mode)];
+} __packed;
+
+struct iwl_fw_get_item_resp {
+ __le32 item_id;
+ __le32 item_byte_cnt;
+ __le32 item_val;
+} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */
+
+/**
+ * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD
+ * @offset: offset in bytes into the section
+ * @length: in bytes, either how much was written or read
+ * @type: NVM_SECTION_TYPE_*
+ * @status: 0 for success, fail otherwise
+ * @data: if read operation, the data returned. Empty on write.
+ */
+struct iwl_nvm_access_resp {
+ __le16 offset;
+ __le16 length;
+ __le16 type;
+ __le16 status;
+ u8 data[];
+} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */
+
+/* MVM_ALIVE 0x1 */
+
+/* alive response is_valid values */
+#define ALIVE_RESP_UCODE_OK BIT(0)
+#define ALIVE_RESP_RFKILL BIT(1)
+
+/* alive response ver_type values */
+enum {
+ FW_TYPE_HW = 0,
+ FW_TYPE_PROT = 1,
+ FW_TYPE_AP = 2,
+ FW_TYPE_WOWLAN = 3,
+ FW_TYPE_TIMING = 4,
+ FW_TYPE_WIPAN = 5
+};
+
+/* alive response ver_subtype values */
+enum {
+ FW_SUBTYPE_FULL_FEATURE = 0,
+ FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */
+ FW_SUBTYPE_REDUCED = 2,
+ FW_SUBTYPE_ALIVE_ONLY = 3,
+ FW_SUBTYPE_WOWLAN = 4,
+ FW_SUBTYPE_AP_SUBTYPE = 5,
+ FW_SUBTYPE_WIPAN = 6,
+ FW_SUBTYPE_INITIALIZE = 9
+};
+
+#define IWL_ALIVE_STATUS_ERR 0xDEAD
+#define IWL_ALIVE_STATUS_OK 0xCAFE
+
+#define IWL_ALIVE_FLG_RFKILL BIT(0)
+
+struct mvm_alive_resp_ver1 {
+ __le16 status;
+ __le16 flags;
+ u8 ucode_minor;
+ u8 ucode_major;
+ __le16 id;
+ u8 api_minor;
+ u8 api_major;
+ u8 ver_subtype;
+ u8 ver_type;
+ u8 mac;
+ u8 opt;
+ __le16 reserved2;
+ __le32 timestamp;
+ __le32 error_event_table_ptr; /* SRAM address for error log */
+ __le32 log_event_table_ptr; /* SRAM address for event log */
+ __le32 cpu_register_ptr;
+ __le32 dbgm_config_ptr;
+ __le32 alive_counter_ptr;
+ __le32 scd_base_ptr; /* SRAM address for SCD */
+} __packed; /* ALIVE_RES_API_S_VER_1 */
+
+struct mvm_alive_resp_ver2 {
+ __le16 status;
+ __le16 flags;
+ u8 ucode_minor;
+ u8 ucode_major;
+ __le16 id;
+ u8 api_minor;
+ u8 api_major;
+ u8 ver_subtype;
+ u8 ver_type;
+ u8 mac;
+ u8 opt;
+ __le16 reserved2;
+ __le32 timestamp;
+ __le32 error_event_table_ptr; /* SRAM address for error log */
+ __le32 log_event_table_ptr; /* SRAM address for LMAC event log */
+ __le32 cpu_register_ptr;
+ __le32 dbgm_config_ptr;
+ __le32 alive_counter_ptr;
+ __le32 scd_base_ptr; /* SRAM address for SCD */
+ __le32 st_fwrd_addr; /* pointer to Store and forward */
+ __le32 st_fwrd_size;
+ u8 umac_minor; /* UMAC version: minor */
+ u8 umac_major; /* UMAC version: major */
+ __le16 umac_id; /* UMAC version: id */
+ __le32 error_info_addr; /* SRAM address for UMAC error log */
+ __le32 dbg_print_buff_addr;
+} __packed; /* ALIVE_RES_API_S_VER_2 */
+
+struct mvm_alive_resp {
+ __le16 status;
+ __le16 flags;
+ __le32 ucode_minor;
+ __le32 ucode_major;
+ u8 ver_subtype;
+ u8 ver_type;
+ u8 mac;
+ u8 opt;
+ __le32 timestamp;
+ __le32 error_event_table_ptr; /* SRAM address for error log */
+ __le32 log_event_table_ptr; /* SRAM address for LMAC event log */
+ __le32 cpu_register_ptr;
+ __le32 dbgm_config_ptr;
+ __le32 alive_counter_ptr;
+ __le32 scd_base_ptr; /* SRAM address for SCD */
+ __le32 st_fwrd_addr; /* pointer to Store and forward */
+ __le32 st_fwrd_size;
+ __le32 umac_minor; /* UMAC version: minor */
+ __le32 umac_major; /* UMAC version: major */
+ __le32 error_info_addr; /* SRAM address for UMAC error log */
+ __le32 dbg_print_buff_addr;
+} __packed; /* ALIVE_RES_API_S_VER_3 */
+
+/* Error response/notification */
+enum {
+ FW_ERR_UNKNOWN_CMD = 0x0,
+ FW_ERR_INVALID_CMD_PARAM = 0x1,
+ FW_ERR_SERVICE = 0x2,
+ FW_ERR_ARC_MEMORY = 0x3,
+ FW_ERR_ARC_CODE = 0x4,
+ FW_ERR_WATCH_DOG = 0x5,
+ FW_ERR_WEP_GRP_KEY_INDX = 0x10,
+ FW_ERR_WEP_KEY_SIZE = 0x11,
+ FW_ERR_OBSOLETE_FUNC = 0x12,
+ FW_ERR_UNEXPECTED = 0xFE,
+ FW_ERR_FATAL = 0xFF
+};
+
+/**
+ * struct iwl_error_resp - FW error indication
+ * ( REPLY_ERROR = 0x2 )
+ * @error_type: one of FW_ERR_*
+ * @cmd_id: the command ID for which the error occured
+ * @bad_cmd_seq_num: sequence number of the erroneous command
+ * @error_service: which service created the error, applicable only if
+ * error_type = 2, otherwise 0
+ * @timestamp: TSF in usecs.
+ */
+struct iwl_error_resp {
+ __le32 error_type;
+ u8 cmd_id;
+ u8 reserved1;
+ __le16 bad_cmd_seq_num;
+ __le32 error_service;
+ __le64 timestamp;
+} __packed;
+
+
+/* Common PHY, MAC and Bindings definitions */
+
+#define MAX_MACS_IN_BINDING (3)
+#define MAX_BINDINGS (4)
+#define AUX_BINDING_INDEX (3)
+#define MAX_PHYS (4)
+
+/* Used to extract ID and color from the context dword */
+#define FW_CTXT_ID_POS (0)
+#define FW_CTXT_ID_MSK (0xff << FW_CTXT_ID_POS)
+#define FW_CTXT_COLOR_POS (8)
+#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS)
+#define FW_CTXT_INVALID (0xffffffff)
+
+#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\
+ (_color << FW_CTXT_COLOR_POS))
+
+/* Possible actions on PHYs, MACs and Bindings */
+enum {
+ FW_CTXT_ACTION_STUB = 0,
+ FW_CTXT_ACTION_ADD,
+ FW_CTXT_ACTION_MODIFY,
+ FW_CTXT_ACTION_REMOVE,
+ FW_CTXT_ACTION_NUM
+}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */
+
+/* Time Events */
+
+/* Time Event types, according to MAC type */
+enum iwl_time_event_type {
+ /* BSS Station Events */
+ TE_BSS_STA_AGGRESSIVE_ASSOC,
+ TE_BSS_STA_ASSOC,
+ TE_BSS_EAP_DHCP_PROT,
+ TE_BSS_QUIET_PERIOD,
+
+ /* P2P Device Events */
+ TE_P2P_DEVICE_DISCOVERABLE,
+ TE_P2P_DEVICE_LISTEN,
+ TE_P2P_DEVICE_ACTION_SCAN,
+ TE_P2P_DEVICE_FULL_SCAN,
+
+ /* P2P Client Events */
+ TE_P2P_CLIENT_AGGRESSIVE_ASSOC,
+ TE_P2P_CLIENT_ASSOC,
+ TE_P2P_CLIENT_QUIET_PERIOD,
+
+ /* P2P GO Events */
+ TE_P2P_GO_ASSOC_PROT,
+ TE_P2P_GO_REPETITIVE_NOA,
+ TE_P2P_GO_CT_WINDOW,
+
+ /* WiDi Sync Events */
+ TE_WIDI_TX_SYNC,
+
+ /* Channel Switch NoA */
+ TE_CHANNEL_SWITCH_PERIOD,
+
+ TE_MAX
+}; /* MAC_EVENT_TYPE_API_E_VER_1 */
+
+
+
+/* Time event - defines for command API v1 */
+
+/*
+ * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ * the first fragment is scheduled.
+ * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ * the first 2 fragments are scheduled.
+ * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
+ * number of fragments are valid.
+ *
+ * Other than the constant defined above, specifying a fragmentation value 'x'
+ * means that the event can be fragmented but only the first 'x' will be
+ * scheduled.
+ */
+enum {
+ TE_V1_FRAG_NONE = 0,
+ TE_V1_FRAG_SINGLE = 1,
+ TE_V1_FRAG_DUAL = 2,
+ TE_V1_FRAG_ENDLESS = 0xffffffff
+};
+
+/* If a Time Event can be fragmented, this is the max number of fragments */
+#define TE_V1_FRAG_MAX_MSK 0x0fffffff
+/* Repeat the time event endlessly (until removed) */
+#define TE_V1_REPEAT_ENDLESS 0xffffffff
+/* If a Time Event has bounded repetitions, this is the maximal value */
+#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff
+
+/* Time Event dependencies: none, on another TE, or in a specific time */
+enum {
+ TE_V1_INDEPENDENT = 0,
+ TE_V1_DEP_OTHER = BIT(0),
+ TE_V1_DEP_TSF = BIT(1),
+ TE_V1_EVENT_SOCIOPATHIC = BIT(2),
+}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
+
+/*
+ * @TE_V1_NOTIF_NONE: no notifications
+ * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start
+ * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end
+ * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use
+ * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use.
+ * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start
+ * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end
+ * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use.
+ * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use.
+ *
+ * Supported Time event notifications configuration.
+ * A notification (both event and fragment) includes a status indicating weather
+ * the FW was able to schedule the event or not. For fragment start/end
+ * notification the status is always success. There is no start/end fragment
+ * notification for monolithic events.
+ */
+enum {
+ TE_V1_NOTIF_NONE = 0,
+ TE_V1_NOTIF_HOST_EVENT_START = BIT(0),
+ TE_V1_NOTIF_HOST_EVENT_END = BIT(1),
+ TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2),
+ TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3),
+ TE_V1_NOTIF_HOST_FRAG_START = BIT(4),
+ TE_V1_NOTIF_HOST_FRAG_END = BIT(5),
+ TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6),
+ TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7),
+}; /* MAC_EVENT_ACTION_API_E_VER_2 */
+
+/* Time event - defines for command API */
+
+/*
+ * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ * the first fragment is scheduled.
+ * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ * the first 2 fragments are scheduled.
+ * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
+ * number of fragments are valid.
+ *
+ * Other than the constant defined above, specifying a fragmentation value 'x'
+ * means that the event can be fragmented but only the first 'x' will be
+ * scheduled.
+ */
+enum {
+ TE_V2_FRAG_NONE = 0,
+ TE_V2_FRAG_SINGLE = 1,
+ TE_V2_FRAG_DUAL = 2,
+ TE_V2_FRAG_MAX = 0xfe,
+ TE_V2_FRAG_ENDLESS = 0xff
+};
+
+/* Repeat the time event endlessly (until removed) */
+#define TE_V2_REPEAT_ENDLESS 0xff
+/* If a Time Event has bounded repetitions, this is the maximal value */
+#define TE_V2_REPEAT_MAX 0xfe
+
+#define TE_V2_PLACEMENT_POS 12
+#define TE_V2_ABSENCE_POS 15
+
+/* Time event policy values
+ * A notification (both event and fragment) includes a status indicating weather
+ * the FW was able to schedule the event or not. For fragment start/end
+ * notification the status is always success. There is no start/end fragment
+ * notification for monolithic events.
+ *
+ * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable
+ * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start
+ * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end
+ * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use
+ * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use.
+ * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start
+ * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end
+ * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use.
+ * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use.
+ * @TE_V2_DEP_OTHER: depends on another time event
+ * @TE_V2_DEP_TSF: depends on a specific time
+ * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC
+ * @TE_V2_ABSENCE: are we present or absent during the Time Event.
+ */
+enum {
+ TE_V2_DEFAULT_POLICY = 0x0,
+
+ /* notifications (event start/stop, fragment start/stop) */
+ TE_V2_NOTIF_HOST_EVENT_START = BIT(0),
+ TE_V2_NOTIF_HOST_EVENT_END = BIT(1),
+ TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2),
+ TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3),
+
+ TE_V2_NOTIF_HOST_FRAG_START = BIT(4),
+ TE_V2_NOTIF_HOST_FRAG_END = BIT(5),
+ TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6),
+ TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7),
+ T2_V2_START_IMMEDIATELY = BIT(11),
+
+ TE_V2_NOTIF_MSK = 0xff,
+
+ /* placement characteristics */
+ TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS),
+ TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1),
+ TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2),
+
+ /* are we present or absent during the Time Event. */
+ TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS),
+};
+
+/**
+ * struct iwl_time_event_cmd_api - configuring Time Events
+ * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also
+ * with version 1. determined by IWL_UCODE_TLV_FLAGS)
+ * ( TIME_EVENT_CMD = 0x29 )
+ * @id_and_color: ID and color of the relevant MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @id: this field has two meanings, depending on the action:
+ * If the action is ADD, then it means the type of event to add.
+ * For all other actions it is the unique event ID assigned when the
+ * event was added by the FW.
+ * @apply_time: When to start the Time Event (in GP2)
+ * @max_delay: maximum delay to event's start (apply time), in TU
+ * @depends_on: the unique ID of the event we depend on (if any)
+ * @interval: interval between repetitions, in TU
+ * @duration: duration of event in TU
+ * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
+ * @max_frags: maximal number of fragments the Time Event can be divided to
+ * @policy: defines whether uCode shall notify the host or other uCode modules
+ * on event and/or fragment start and/or end
+ * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
+ * TE_EVENT_SOCIOPATHIC
+ * using TE_ABSENCE and using TE_NOTIF_*
+ */
+struct iwl_time_event_cmd {
+ /* COMMON_INDEX_HDR_API_S_VER_1 */
+ __le32 id_and_color;
+ __le32 action;
+ __le32 id;
+ /* MAC_TIME_EVENT_DATA_API_S_VER_2 */
+ __le32 apply_time;
+ __le32 max_delay;
+ __le32 depends_on;
+ __le32 interval;
+ __le32 duration;
+ u8 repeat;
+ u8 max_frags;
+ __le16 policy;
+} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_time_event_resp - response structure to iwl_time_event_cmd
+ * @status: bit 0 indicates success, all others specify errors
+ * @id: the Time Event type
+ * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE
+ * @id_and_color: ID and color of the relevant MAC
+ */
+struct iwl_time_event_resp {
+ __le32 status;
+ __le32 id;
+ __le32 unique_id;
+ __le32 id_and_color;
+} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */
+
+/**
+ * struct iwl_time_event_notif - notifications of time event start/stop
+ * ( TIME_EVENT_NOTIFICATION = 0x2a )
+ * @timestamp: action timestamp in GP2
+ * @session_id: session's unique id
+ * @unique_id: unique id of the Time Event itself
+ * @id_and_color: ID and color of the relevant MAC
+ * @action: one of TE_NOTIF_START or TE_NOTIF_END
+ * @status: true if scheduled, false otherwise (not executed)
+ */
+struct iwl_time_event_notif {
+ __le32 timestamp;
+ __le32 session_id;
+ __le32 unique_id;
+ __le32 id_and_color;
+ __le32 action;
+ __le32 status;
+} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */
+
+
+/* Bindings and Time Quota */
+
+/**
+ * struct iwl_binding_cmd - configuring bindings
+ * ( BINDING_CONTEXT_CMD = 0x2b )
+ * @id_and_color: ID and color of the relevant Binding
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @macs: array of MAC id and colors which belong to the binding
+ * @phy: PHY id and color which belongs to the binding
+ */
+struct iwl_binding_cmd {
+ /* COMMON_INDEX_HDR_API_S_VER_1 */
+ __le32 id_and_color;
+ __le32 action;
+ /* BINDING_DATA_API_S_VER_1 */
+ __le32 macs[MAX_MACS_IN_BINDING];
+ __le32 phy;
+} __packed; /* BINDING_CMD_API_S_VER_1 */
+
+/* The maximal number of fragments in the FW's schedule session */
+#define IWL_MVM_MAX_QUOTA 128
+
+/**
+ * struct iwl_time_quota_data - configuration of time quota per binding
+ * @id_and_color: ID and color of the relevant Binding
+ * @quota: absolute time quota in TU. The scheduler will try to divide the
+ * remainig quota (after Time Events) according to this quota.
+ * @max_duration: max uninterrupted context duration in TU
+ */
+struct iwl_time_quota_data {
+ __le32 id_and_color;
+ __le32 quota;
+ __le32 max_duration;
+} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_time_quota_cmd - configuration of time quota between bindings
+ * ( TIME_QUOTA_CMD = 0x2c )
+ * @quotas: allocations per binding
+ */
+struct iwl_time_quota_cmd {
+ struct iwl_time_quota_data quotas[MAX_BINDINGS];
+} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */
+
+
+/* PHY context */
+
+/* Supported bands */
+#define PHY_BAND_5 (0)
+#define PHY_BAND_24 (1)
+
+/* Supported channel width, vary if there is VHT support */
+#define PHY_VHT_CHANNEL_MODE20 (0x0)
+#define PHY_VHT_CHANNEL_MODE40 (0x1)
+#define PHY_VHT_CHANNEL_MODE80 (0x2)
+#define PHY_VHT_CHANNEL_MODE160 (0x3)
+
+/*
+ * Control channel position:
+ * For legacy set bit means upper channel, otherwise lower.
+ * For VHT - bit-2 marks if the control is lower/upper relative to center-freq
+ * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0.
+ * center_freq
+ * |
+ * 40Mhz |_______|_______|
+ * 80Mhz |_______|_______|_______|_______|
+ * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______|
+ * code 011 010 001 000 | 100 101 110 111
+ */
+#define PHY_VHT_CTRL_POS_1_BELOW (0x0)
+#define PHY_VHT_CTRL_POS_2_BELOW (0x1)
+#define PHY_VHT_CTRL_POS_3_BELOW (0x2)
+#define PHY_VHT_CTRL_POS_4_BELOW (0x3)
+#define PHY_VHT_CTRL_POS_1_ABOVE (0x4)
+#define PHY_VHT_CTRL_POS_2_ABOVE (0x5)
+#define PHY_VHT_CTRL_POS_3_ABOVE (0x6)
+#define PHY_VHT_CTRL_POS_4_ABOVE (0x7)
+
+/*
+ * @band: PHY_BAND_*
+ * @channel: channel number
+ * @width: PHY_[VHT|LEGACY]_CHANNEL_*
+ * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_*
+ */
+struct iwl_fw_channel_info {
+ u8 band;
+ u8 channel;
+ u8 width;
+ u8 ctrl_pos;
+} __packed;
+
+#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0)
+#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \
+ (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS)
+#define PHY_RX_CHAIN_VALID_POS (1)
+#define PHY_RX_CHAIN_VALID_MSK \
+ (0x7 << PHY_RX_CHAIN_VALID_POS)
+#define PHY_RX_CHAIN_FORCE_SEL_POS (4)
+#define PHY_RX_CHAIN_FORCE_SEL_MSK \
+ (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS)
+#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
+#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \
+ (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS)
+#define PHY_RX_CHAIN_CNT_POS (10)
+#define PHY_RX_CHAIN_CNT_MSK \
+ (0x3 << PHY_RX_CHAIN_CNT_POS)
+#define PHY_RX_CHAIN_MIMO_CNT_POS (12)
+#define PHY_RX_CHAIN_MIMO_CNT_MSK \
+ (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS)
+#define PHY_RX_CHAIN_MIMO_FORCE_POS (14)
+#define PHY_RX_CHAIN_MIMO_FORCE_MSK \
+ (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS)
+
+/* TODO: fix the value, make it depend on firmware at runtime? */
+#define NUM_PHY_CTX 3
+
+/* TODO: complete missing documentation */
+/**
+ * struct iwl_phy_context_cmd - config of the PHY context
+ * ( PHY_CONTEXT_CMD = 0x8 )
+ * @id_and_color: ID and color of the relevant Binding
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @apply_time: 0 means immediate apply and context switch.
+ * other value means apply new params after X usecs
+ * @tx_param_color: ???
+ * @channel_info:
+ * @txchain_info: ???
+ * @rxchain_info: ???
+ * @acquisition_data: ???
+ * @dsp_cfg_flags: set to 0
+ */
+struct iwl_phy_context_cmd {
+ /* COMMON_INDEX_HDR_API_S_VER_1 */
+ __le32 id_and_color;
+ __le32 action;
+ /* PHY_CONTEXT_DATA_API_S_VER_1 */
+ __le32 apply_time;
+ __le32 tx_param_color;
+ struct iwl_fw_channel_info ci;
+ __le32 txchain_info;
+ __le32 rxchain_info;
+ __le32 acquisition_data;
+ __le32 dsp_cfg_flags;
+} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
+
+/*
+ * Aux ROC command
+ *
+ * Command requests the firmware to create a time event for a certain duration
+ * and remain on the given channel. This is done by using the Aux framework in
+ * the FW.
+ * The command was first used for Hot Spot issues - but can be used regardless
+ * to Hot Spot.
+ *
+ * ( HOT_SPOT_CMD 0x53 )
+ *
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the
+ * event_unique_id should be the id of the time event assigned by ucode.
+ * Otherwise ignore the event_unique_id.
+ * @sta_id_and_color: station id and color, resumed during "Remain On Channel"
+ * activity.
+ * @channel_info: channel info
+ * @node_addr: Our MAC Address
+ * @reserved: reserved for alignment
+ * @apply_time: GP2 value to start (should always be the current GP2 value)
+ * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max
+ * time by which start of the event is allowed to be postponed.
+ * @duration: event duration in TU To calculate event duration:
+ * timeEventDuration = min(duration, remainingQuota)
+ */
+struct iwl_hs20_roc_req {
+ /* COMMON_INDEX_HDR_API_S_VER_1 hdr */
+ __le32 id_and_color;
+ __le32 action;
+ __le32 event_unique_id;
+ __le32 sta_id_and_color;
+ struct iwl_fw_channel_info channel_info;
+ u8 node_addr[ETH_ALEN];
+ __le16 reserved;
+ __le32 apply_time;
+ __le32 apply_time_max_delay;
+ __le32 duration;
+} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */
+
+/*
+ * values for AUX ROC result values
+ */
+enum iwl_mvm_hot_spot {
+ HOT_SPOT_RSP_STATUS_OK,
+ HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS,
+ HOT_SPOT_MAX_NUM_OF_SESSIONS,
+};
+
+/*
+ * Aux ROC command response
+ *
+ * In response to iwl_hs20_roc_req the FW sends this command to notify the
+ * driver the uid of the timevent.
+ *
+ * ( HOT_SPOT_CMD 0x53 )
+ *
+ * @event_unique_id: Unique ID of time event assigned by ucode
+ * @status: Return status 0 is success, all the rest used for specific errors
+ */
+struct iwl_hs20_roc_res {
+ __le32 event_unique_id;
+ __le32 status;
+} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */
+
+/**
+ * struct iwl_radio_version_notif - information on the radio version
+ * ( RADIO_VERSION_NOTIFICATION = 0x68 )
+ * @radio_flavor:
+ * @radio_step:
+ * @radio_dash:
+ */
+struct iwl_radio_version_notif {
+ __le32 radio_flavor;
+ __le32 radio_step;
+ __le32 radio_dash;
+} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */
+
+enum iwl_card_state_flags {
+ CARD_ENABLED = 0x00,
+ HW_CARD_DISABLED = 0x01,
+ SW_CARD_DISABLED = 0x02,
+ CT_KILL_CARD_DISABLED = 0x04,
+ HALT_CARD_DISABLED = 0x08,
+ CARD_DISABLED_MSK = 0x0f,
+ CARD_IS_RX_ON = 0x10,
+};
+
+/**
+ * struct iwl_radio_version_notif - information on the radio version
+ * ( CARD_STATE_NOTIFICATION = 0xa1 )
+ * @flags: %iwl_card_state_flags
+ */
+struct iwl_card_state_notif {
+ __le32 flags;
+} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
+
+/**
+ * struct iwl_missed_beacons_notif - information on missed beacons
+ * ( MISSED_BEACONS_NOTIFICATION = 0xa2 )
+ * @mac_id: interface ID
+ * @consec_missed_beacons_since_last_rx: number of consecutive missed
+ * beacons since last RX.
+ * @consec_missed_beacons: number of consecutive missed beacons
+ * @num_expected_beacons:
+ * @num_recvd_beacons:
+ */
+struct iwl_missed_beacons_notif {
+ __le32 mac_id;
+ __le32 consec_missed_beacons_since_last_rx;
+ __le32 consec_missed_beacons;
+ __le32 num_expected_beacons;
+ __le32 num_recvd_beacons;
+} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */
+
+/**
+ * struct iwl_mfuart_load_notif - mfuart image version & status
+ * ( MFUART_LOAD_NOTIFICATION = 0xb1 )
+ * @installed_ver: installed image version
+ * @external_ver: external image version
+ * @status: MFUART loading status
+ * @duration: MFUART loading time
+*/
+struct iwl_mfuart_load_notif {
+ __le32 installed_ver;
+ __le32 external_ver;
+ __le32 status;
+ __le32 duration;
+} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/
+
+/**
+ * struct iwl_set_calib_default_cmd - set default value for calibration.
+ * ( SET_CALIB_DEFAULT_CMD = 0x8e )
+ * @calib_index: the calibration to set value for
+ * @length: of data
+ * @data: the value to set for the calibration result
+ */
+struct iwl_set_calib_default_cmd {
+ __le16 calib_index;
+ __le16 length;
+ u8 data[0];
+} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
+
+#define MAX_PORT_ID_NUM 2
+#define MAX_MCAST_FILTERING_ADDRESSES 256
+
+/**
+ * struct iwl_mcast_filter_cmd - configure multicast filter.
+ * @filter_own: Set 1 to filter out multicast packets sent by station itself
+ * @port_id: Multicast MAC addresses array specifier. This is a strange way
+ * to identify network interface adopted in host-device IF.
+ * It is used by FW as index in array of addresses. This array has
+ * MAX_PORT_ID_NUM members.
+ * @count: Number of MAC addresses in the array
+ * @pass_all: Set 1 to pass all multicast packets.
+ * @bssid: current association BSSID.
+ * @addr_list: Place holder for array of MAC addresses.
+ * IMPORTANT: add padding if necessary to ensure DWORD alignment.
+ */
+struct iwl_mcast_filter_cmd {
+ u8 filter_own;
+ u8 port_id;
+ u8 count;
+ u8 pass_all;
+ u8 bssid[6];
+ u8 reserved[2];
+ u8 addr_list[0];
+} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
+
+#define MAX_BCAST_FILTERS 8
+#define MAX_BCAST_FILTER_ATTRS 2
+
+/**
+ * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet
+ * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start.
+ * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e.
+ * start of ip payload).
+ */
+enum iwl_mvm_bcast_filter_attr_offset {
+ BCAST_FILTER_OFFSET_PAYLOAD_START = 0,
+ BCAST_FILTER_OFFSET_IP_END = 1,
+};
+
+/**
+ * struct iwl_fw_bcast_filter_attr - broadcast filter attribute
+ * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset.
+ * @offset: starting offset of this pattern.
+ * @val: value to match - big endian (MSB is the first
+ * byte to match from offset pos).
+ * @mask: mask to match (big endian).
+ */
+struct iwl_fw_bcast_filter_attr {
+ u8 offset_type;
+ u8 offset;
+ __le16 reserved1;
+ __be32 val;
+ __be32 mask;
+} __packed; /* BCAST_FILTER_ATT_S_VER_1 */
+
+/**
+ * enum iwl_mvm_bcast_filter_frame_type - filter frame type
+ * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames.
+ * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames
+ */
+enum iwl_mvm_bcast_filter_frame_type {
+ BCAST_FILTER_FRAME_TYPE_ALL = 0,
+ BCAST_FILTER_FRAME_TYPE_IPV4 = 1,
+};
+
+/**
+ * struct iwl_fw_bcast_filter - broadcast filter
+ * @discard: discard frame (1) or let it pass (0).
+ * @frame_type: &enum iwl_mvm_bcast_filter_frame_type.
+ * @num_attrs: number of valid attributes in this filter.
+ * @attrs: attributes of this filter. a filter is considered matched
+ * only when all its attributes are matched (i.e. AND relationship)
+ */
+struct iwl_fw_bcast_filter {
+ u8 discard;
+ u8 frame_type;
+ u8 num_attrs;
+ u8 reserved1;
+ struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS];
+} __packed; /* BCAST_FILTER_S_VER_1 */
+
+/**
+ * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration.
+ * @default_discard: default action for this mac (discard (1) / pass (0)).
+ * @attached_filters: bitmap of relevant filters for this mac.
+ */
+struct iwl_fw_bcast_mac {
+ u8 default_discard;
+ u8 reserved1;
+ __le16 attached_filters;
+} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */
+
+/**
+ * struct iwl_bcast_filter_cmd - broadcast filtering configuration
+ * @disable: enable (0) / disable (1)
+ * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS)
+ * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER)
+ * @filters: broadcast filters
+ * @macs: broadcast filtering configuration per-mac
+ */
+struct iwl_bcast_filter_cmd {
+ u8 disable;
+ u8 max_bcast_filters;
+ u8 max_macs;
+ u8 reserved1;
+ struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS];
+ struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER];
+} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */
+
+/*
+ * enum iwl_mvm_marker_id - maker ids
+ *
+ * The ids for different type of markers to insert into the usniffer logs
+ */
+enum iwl_mvm_marker_id {
+ MARKER_ID_TX_FRAME_LATENCY = 1,
+}; /* MARKER_ID_API_E_VER_1 */
+
+/**
+ * struct iwl_mvm_marker - mark info into the usniffer logs
+ *
+ * (MARKER_CMD = 0xcb)
+ *
+ * Mark the UTC time stamp into the usniffer logs together with additional
+ * metadata, so the usniffer output can be parsed.
+ * In the command response the ucode will return the GP2 time.
+ *
+ * @dw_len: The amount of dwords following this byte including this byte.
+ * @marker_id: A unique marker id (iwl_mvm_marker_id).
+ * @reserved: reserved.
+ * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC
+ * @metadata: additional meta data that will be written to the unsiffer log
+ */
+struct iwl_mvm_marker {
+ u8 dwLen;
+ u8 markerId;
+ __le16 reserved;
+ __le64 timestamp;
+ __le32 metadata[0];
+} __packed; /* MARKER_API_S_VER_1 */
+
+/*
+ * enum iwl_dc2dc_config_id - flag ids
+ *
+ * Ids of dc2dc configuration flags
+ */
+enum iwl_dc2dc_config_id {
+ DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */
+ DCDC_FREQ_TUNE_SET = 0x2,
+}; /* MARKER_ID_API_E_VER_1 */
+
+/**
+ * struct iwl_dc2dc_config_cmd - configure dc2dc values
+ *
+ * (DC2DC_CONFIG_CMD = 0x83)
+ *
+ * Set/Get & configure dc2dc values.
+ * The command always returns the current dc2dc values.
+ *
+ * @flags: set/get dc2dc
+ * @enable_low_power_mode: not used.
+ * @dc2dc_freq_tune0: frequency divider - digital domain
+ * @dc2dc_freq_tune1: frequency divider - analog domain
+ */
+struct iwl_dc2dc_config_cmd {
+ __le32 flags;
+ __le32 enable_low_power_mode; /* not used */
+ __le32 dc2dc_freq_tune0;
+ __le32 dc2dc_freq_tune1;
+} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd
+ *
+ * Current dc2dc values returned by the FW.
+ *
+ * @dc2dc_freq_tune0: frequency divider - digital domain
+ * @dc2dc_freq_tune1: frequency divider - analog domain
+ */
+struct iwl_dc2dc_config_resp {
+ __le32 dc2dc_freq_tune0;
+ __le32 dc2dc_freq_tune1;
+} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */
+
+/***********************************
+ * Smart Fifo API
+ ***********************************/
+/* Smart Fifo state */
+enum iwl_sf_state {
+ SF_LONG_DELAY_ON = 0, /* should never be called by driver */
+ SF_FULL_ON,
+ SF_UNINIT,
+ SF_INIT_OFF,
+ SF_HW_NUM_STATES
+};
+
+/* Smart Fifo possible scenario */
+enum iwl_sf_scenario {
+ SF_SCENARIO_SINGLE_UNICAST,
+ SF_SCENARIO_AGG_UNICAST,
+ SF_SCENARIO_MULTICAST,
+ SF_SCENARIO_BA_RESP,
+ SF_SCENARIO_TX_RESP,
+ SF_NUM_SCENARIO
+};
+
+#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */
+#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */
+
+/* smart FIFO default values */
+#define SF_W_MARK_SISO 6144
+#define SF_W_MARK_MIMO2 8192
+#define SF_W_MARK_MIMO3 6144
+#define SF_W_MARK_LEGACY 4096
+#define SF_W_MARK_SCAN 4096
+
+/* SF Scenarios timers for default configuration (aligned to 32 uSec) */
+#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */
+#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */
+
+/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */
+#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */
+#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */
+#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */
+#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */
+#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */
+#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */
+#define SF_BA_IDLE_TIMER 320 /* 300 uSec */
+#define SF_BA_AGING_TIMER 2016 /* 2 mSec */
+#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */
+#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */
+
+#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */
+
+#define SF_CFG_DUMMY_NOTIF_OFF BIT(16)
+
+/**
+ * Smart Fifo configuration command.
+ * @state: smart fifo state, types listed in enum %iwl_sf_sate.
+ * @watermark: Minimum allowed availabe free space in RXF for transient state.
+ * @long_delay_timeouts: aging and idle timer values for each scenario
+ * in long delay state.
+ * @full_on_timeouts: timer values for each scenario in full on state.
+ */
+struct iwl_sf_cfg_cmd {
+ __le32 state;
+ __le32 watermark[SF_TRANSIENT_STATES_NUMBER];
+ __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
+ __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
+} __packed; /* SF_CFG_API_S_VER_2 */
+
+/***********************************
+ * Location Aware Regulatory (LAR) API - MCC updates
+ ***********************************/
+
+/**
+ * struct iwl_mcc_update_cmd_v1 - Request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: the source from where we got the MCC, see iwl_mcc_source
+ * @reserved: reserved for alignment
+ */
+struct iwl_mcc_update_cmd_v1 {
+ __le16 mcc;
+ u8 source_id;
+ u8 reserved;
+} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_mcc_update_cmd - Request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: the source from where we got the MCC, see iwl_mcc_source
+ * @reserved: reserved for alignment
+ * @key: integrity key for MCC API OEM testing
+ * @reserved2: reserved
+ */
+struct iwl_mcc_update_cmd {
+ __le16 mcc;
+ u8 source_id;
+ u8 reserved;
+ __le32 key;
+ __le32 reserved2[5];
+} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */
+
+/**
+ * iwl_mcc_update_resp_v1 - response to MCC_UPDATE_CMD.
+ * Contains the new channel control profile map, if changed, and the new MCC
+ * (mobile country code).
+ * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
+ * @status: see &enum iwl_mcc_update_status
+ * @mcc: the new applied MCC
+ * @cap: capabilities for all channels which matches the MCC
+ * @source_id: the MCC source, see iwl_mcc_source
+ * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
+ * channels, depending on platform)
+ * @channels: channel control data map, DWORD for each channel. Only the first
+ * 16bits are used.
+ */
+struct iwl_mcc_update_resp_v1 {
+ __le32 status;
+ __le16 mcc;
+ u8 cap;
+ u8 source_id;
+ __le32 n_channels;
+ __le32 channels[0];
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */
+
+/**
+ * iwl_mcc_update_resp - response to MCC_UPDATE_CMD.
+ * Contains the new channel control profile map, if changed, and the new MCC
+ * (mobile country code).
+ * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
+ * @status: see &enum iwl_mcc_update_status
+ * @mcc: the new applied MCC
+ * @cap: capabilities for all channels which matches the MCC
+ * @source_id: the MCC source, see iwl_mcc_source
+ * @time: time elapsed from the MCC test start (in 30 seconds TU)
+ * @reserved: reserved.
+ * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
+ * channels, depending on platform)
+ * @channels: channel control data map, DWORD for each channel. Only the first
+ * 16bits are used.
+ */
+struct iwl_mcc_update_resp {
+ __le32 status;
+ __le16 mcc;
+ u8 cap;
+ u8 source_id;
+ __le16 time;
+ __le16 reserved;
+ __le32 n_channels;
+ __le32 channels[0];
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */
+
+/**
+ * struct iwl_mcc_chub_notif - chub notifies of mcc change
+ * (MCC_CHUB_UPDATE_CMD = 0xc9)
+ * The Chub (Communication Hub, CommsHUB) is a HW component that connects to
+ * the cellular and connectivity cores that gets updates of the mcc, and
+ * notifies the ucode directly of any mcc change.
+ * The ucode requests the driver to request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: identity of the change originator, see iwl_mcc_source
+ * @reserved1: reserved for alignment
+ */
+struct iwl_mcc_chub_notif {
+ u16 mcc;
+ u8 source_id;
+ u8 reserved1;
+} __packed; /* LAR_MCC_NOTIFY_S */
+
+enum iwl_mcc_update_status {
+ MCC_RESP_NEW_CHAN_PROFILE,
+ MCC_RESP_SAME_CHAN_PROFILE,
+ MCC_RESP_INVALID,
+ MCC_RESP_NVM_DISABLED,
+ MCC_RESP_ILLEGAL,
+ MCC_RESP_LOW_PRIORITY,
+ MCC_RESP_TEST_MODE_ACTIVE,
+ MCC_RESP_TEST_MODE_NOT_ACTIVE,
+ MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE,
+};
+
+enum iwl_mcc_source {
+ MCC_SOURCE_OLD_FW = 0,
+ MCC_SOURCE_ME = 1,
+ MCC_SOURCE_BIOS = 2,
+ MCC_SOURCE_3G_LTE_HOST = 3,
+ MCC_SOURCE_3G_LTE_DEVICE = 4,
+ MCC_SOURCE_WIFI = 5,
+ MCC_SOURCE_RESERVED = 6,
+ MCC_SOURCE_DEFAULT = 7,
+ MCC_SOURCE_UNINITIALIZED = 8,
+ MCC_SOURCE_MCC_API = 9,
+ MCC_SOURCE_GET_CURRENT = 0x10,
+ MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11,
+};
+
+/* DTS measurements */
+
+enum iwl_dts_measurement_flags {
+ DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0),
+ DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1),
+};
+
+/**
+ * iwl_dts_measurement_cmd - request DTS temperature and/or voltage measurements
+ *
+ * @flags: indicates which measurements we want as specified in &enum
+ * iwl_dts_measurement_flags
+ */
+struct iwl_dts_measurement_cmd {
+ __le32 flags;
+} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */
+
+/**
+* enum iwl_dts_control_measurement_mode - DTS measurement type
+* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read
+* back (latest value. Not waiting for new value). Use automatic
+* SW DTS configuration.
+* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings,
+* trigger DTS reading and provide read back temperature read
+* when available.
+* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read
+* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result,
+* without measurement trigger.
+*/
+enum iwl_dts_control_measurement_mode {
+ DTS_AUTOMATIC = 0,
+ DTS_REQUEST_READ = 1,
+ DTS_OVER_WRITE = 2,
+ DTS_DIRECT_WITHOUT_MEASURE = 3,
+};
+
+/**
+* enum iwl_dts_used - DTS to use or used for measurement in the DTS request
+* @DTS_USE_TOP: Top
+* @DTS_USE_CHAIN_A: chain A
+* @DTS_USE_CHAIN_B: chain B
+* @DTS_USE_CHAIN_C: chain C
+* @XTAL_TEMPERATURE - read temperature from xtal
+*/
+enum iwl_dts_used {
+ DTS_USE_TOP = 0,
+ DTS_USE_CHAIN_A = 1,
+ DTS_USE_CHAIN_B = 2,
+ DTS_USE_CHAIN_C = 3,
+ XTAL_TEMPERATURE = 4,
+};
+
+/**
+* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode
+* @DTS_BIT6_MODE: bit 6 mode
+* @DTS_BIT8_MODE: bit 8 mode
+*/
+enum iwl_dts_bit_mode {
+ DTS_BIT6_MODE = 0,
+ DTS_BIT8_MODE = 1,
+};
+
+/**
+ * iwl_ext_dts_measurement_cmd - request extended DTS temperature measurements
+ * @control_mode: see &enum iwl_dts_control_measurement_mode
+ * @temperature: used when over write DTS mode is selected
+ * @sensor: set temperature sensor to use. See &enum iwl_dts_used
+ * @avg_factor: average factor to DTS in request DTS read mode
+ * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode
+ * @step_duration: step duration for the DTS
+ */
+struct iwl_ext_dts_measurement_cmd {
+ __le32 control_mode;
+ __le32 temperature;
+ __le32 sensor;
+ __le32 avg_factor;
+ __le32 bit_mode;
+ __le32 step_duration;
+} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */
+
+/**
+ * iwl_dts_measurement_notif - notification received with the measurements
+ *
+ * @temp: the measured temperature
+ * @voltage: the measured voltage
+ */
+struct iwl_dts_measurement_notif {
+ __le32 temp;
+ __le32 voltage;
+} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */
+
+/***********************************
+ * TDLS API
+ ***********************************/
+
+/* Type of TDLS request */
+enum iwl_tdls_channel_switch_type {
+ TDLS_SEND_CHAN_SW_REQ = 0,
+ TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH,
+ TDLS_MOVE_CH,
+}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */
+
+/**
+ * Switch timing sub-element in a TDLS channel-switch command
+ * @frame_timestamp: GP2 timestamp of channel-switch request/response packet
+ * received from peer
+ * @max_offchan_duration: What amount of microseconds out of a DTIM is given
+ * to the TDLS off-channel communication. For instance if the DTIM is
+ * 200TU and the TDLS peer is to be given 25% of the time, the value
+ * given will be 50TU, or 50 * 1024 if translated into microseconds.
+ * @switch_time: switch time the peer sent in its channel switch timing IE
+ * @switch_timout: switch timeout the peer sent in its channel switch timing IE
+ */
+struct iwl_tdls_channel_switch_timing {
+ __le32 frame_timestamp; /* GP2 time of peer packet Rx */
+ __le32 max_offchan_duration; /* given in micro-seconds */
+ __le32 switch_time; /* given in micro-seconds */
+ __le32 switch_timeout; /* given in micro-seconds */
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */
+
+#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200
+
+/**
+ * TDLS channel switch frame template
+ *
+ * A template representing a TDLS channel-switch request or response frame
+ *
+ * @switch_time_offset: offset to the channel switch timing IE in the template
+ * @tx_cmd: Tx parameters for the frame
+ * @data: frame data
+ */
+struct iwl_tdls_channel_switch_frame {
+ __le32 switch_time_offset;
+ struct iwl_tx_cmd tx_cmd;
+ u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE];
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */
+
+/**
+ * TDLS channel switch command
+ *
+ * The command is sent to initiate a channel switch and also in response to
+ * incoming TDLS channel-switch request/response packets from remote peers.
+ *
+ * @switch_type: see &enum iwl_tdls_channel_switch_type
+ * @peer_sta_id: station id of TDLS peer
+ * @ci: channel we switch to
+ * @timing: timing related data for command
+ * @frame: channel-switch request/response template, depending to switch_type
+ */
+struct iwl_tdls_channel_switch_cmd {
+ u8 switch_type;
+ __le32 peer_sta_id;
+ struct iwl_fw_channel_info ci;
+ struct iwl_tdls_channel_switch_timing timing;
+ struct iwl_tdls_channel_switch_frame frame;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */
+
+/**
+ * TDLS channel switch start notification
+ *
+ * @status: non-zero on success
+ * @offchannel_duration: duration given in microseconds
+ * @sta_id: peer currently performing the channel-switch with
+ */
+struct iwl_tdls_channel_switch_notif {
+ __le32 status;
+ __le32 offchannel_duration;
+ __le32 sta_id;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */
+
+/**
+ * TDLS station info
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx
+ * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer
+ * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise
+ */
+struct iwl_tdls_sta_info {
+ u8 sta_id;
+ u8 tx_to_peer_tid;
+ __le16 tx_to_peer_ssn;
+ __le32 is_initiator;
+} __packed; /* TDLS_STA_INFO_VER_1 */
+
+/**
+ * TDLS basic config command
+ *
+ * @id_and_color: MAC id and color being configured
+ * @tdls_peer_count: amount of currently connected TDLS peers
+ * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx
+ * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP
+ * @sta_info: per-station info. Only the first tdls_peer_count entries are set
+ * @pti_req_data_offset: offset of network-level data for the PTI template
+ * @pti_req_tx_cmd: Tx parameters for PTI request template
+ * @pti_req_template: PTI request template data
+ */
+struct iwl_tdls_config_cmd {
+ __le32 id_and_color; /* mac id and color */
+ u8 tdls_peer_count;
+ u8 tx_to_ap_tid;
+ __le16 tx_to_ap_ssn;
+ struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT];
+
+ __le32 pti_req_data_offset;
+ struct iwl_tx_cmd pti_req_tx_cmd;
+ u8 pti_req_template[0];
+} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */
+
+/**
+ * TDLS per-station config information from FW
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to
+ * the peer
+ */
+struct iwl_tdls_config_sta_info_res {
+ __le16 sta_id;
+ __le16 tx_to_peer_last_seq;
+} __packed; /* TDLS_STA_INFO_RSP_VER_1 */
+
+/**
+ * TDLS config information from FW
+ *
+ * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP
+ * @sta_info: per-station TDLS config information
+ */
+struct iwl_tdls_config_res {
+ __le32 tx_to_ap_last_seq;
+ struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT];
+} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */
+
+#define TX_FIFO_MAX_NUM 8
+#define RX_FIFO_MAX_NUM 2
+
+/**
+ * Shared memory configuration information from the FW
+ *
+ * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not
+ * accessible)
+ * @shared_mem_size: shared memory size
+ * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to
+ * 0x0 as accessible only via DBGM RDAT)
+ * @sample_buff_size: internal sample buff size
+ * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre
+ * 8000 HW set to 0x0 as not accessible)
+ * @txfifo_size: size of TXF0 ... TXF7
+ * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0
+ * @page_buff_addr: used by UMAC and performance debug (page miss analysis),
+ * when paging is not supported this should be 0
+ * @page_buff_size: size of %page_buff_addr
+ */
+struct iwl_shared_mem_cfg {
+ __le32 shared_mem_addr;
+ __le32 shared_mem_size;
+ __le32 sample_buff_addr;
+ __le32 sample_buff_size;
+ __le32 txfifo_addr;
+ __le32 txfifo_size[TX_FIFO_MAX_NUM];
+ __le32 rxfifo_size[RX_FIFO_MAX_NUM];
+ __le32 page_buff_addr;
+ __le32 page_buff_size;
+} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */
+
+#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
new file mode 100644
index 000000000000..0813f8184e10
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
@@ -0,0 +1,817 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/devcoredump.h>
+
+#include "fw-dbg.h"
+#include "iwl-io.h"
+#include "mvm.h"
+#include "iwl-prph.h"
+#include "iwl-csr.h"
+
+static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen)
+{
+ const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
+ ssize_t bytes_read;
+ ssize_t bytes_read_trans;
+
+ if (offset < dump_ptrs->op_mode_len) {
+ bytes_read = min_t(ssize_t, count,
+ dump_ptrs->op_mode_len - offset);
+ memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
+ bytes_read);
+ offset += bytes_read;
+ count -= bytes_read;
+
+ if (count == 0)
+ return bytes_read;
+ } else {
+ bytes_read = 0;
+ }
+
+ if (!dump_ptrs->trans_ptr)
+ return bytes_read;
+
+ offset -= dump_ptrs->op_mode_len;
+ bytes_read_trans = min_t(ssize_t, count,
+ dump_ptrs->trans_ptr->len - offset);
+ memcpy(buffer + bytes_read,
+ (u8 *)dump_ptrs->trans_ptr->data + offset,
+ bytes_read_trans);
+
+ return bytes_read + bytes_read_trans;
+}
+
+static void iwl_mvm_free_coredump(const void *data)
+{
+ const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
+
+ vfree(fw_error_dump->op_mode_ptr);
+ vfree(fw_error_dump->trans_ptr);
+ kfree(fw_error_dump);
+}
+
+#define RADIO_REG_MAX_READ 0x2ad
+static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm,
+ struct iwl_fw_error_dump_data **dump_data)
+{
+ u8 *pos = (void *)(*dump_data)->data;
+ unsigned long flags;
+ int i;
+
+ if (!iwl_trans_grab_nic_access(mvm->trans, &flags))
+ return;
+
+ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG);
+ (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ);
+
+ for (i = 0; i < RADIO_REG_MAX_READ; i++) {
+ u32 rd_cmd = RADIO_RSP_RD_CMD;
+
+ rd_cmd |= i << RADIO_RSP_ADDR_POS;
+ iwl_write_prph_no_grab(mvm->trans, RSP_RADIO_CMD, rd_cmd);
+ *pos = (u8)iwl_read_prph_no_grab(mvm->trans, RSP_RADIO_RDDAT);
+
+ pos++;
+ }
+
+ *dump_data = iwl_fw_error_next_data(*dump_data);
+
+ iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+
+static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
+ struct iwl_fw_error_dump_data **dump_data)
+{
+ struct iwl_fw_error_dump_fifo *fifo_hdr;
+ u32 *fifo_data;
+ u32 fifo_len;
+ unsigned long flags;
+ int i, j;
+
+ if (!iwl_trans_grab_nic_access(mvm->trans, &flags))
+ return;
+
+ /* Pull RXF data from all RXFs */
+ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
+ /*
+ * Keep aside the additional offset that might be needed for
+ * next RXF
+ */
+ u32 offset_diff = RXF_DIFF_FROM_PREV * i;
+
+ fifo_hdr = (void *)(*dump_data)->data;
+ fifo_data = (void *)fifo_hdr->data;
+ fifo_len = mvm->shared_mem_cfg.rxfifo_size[i];
+
+ /* No need to try to read the data if the length is 0 */
+ if (fifo_len == 0)
+ continue;
+
+ /* Add a TLV for the RXF */
+ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+ (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+ fifo_hdr->fifo_num = cpu_to_le32(i);
+ fifo_hdr->available_bytes =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_D_SPACE +
+ offset_diff));
+ fifo_hdr->wr_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_WR_PTR +
+ offset_diff));
+ fifo_hdr->rd_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_RD_PTR +
+ offset_diff));
+ fifo_hdr->fence_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_FENCE_PTR +
+ offset_diff));
+ fifo_hdr->fence_mode =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_SET_FENCE_MODE +
+ offset_diff));
+
+ /* Lock fence */
+ iwl_trans_write_prph(mvm->trans,
+ RXF_SET_FENCE_MODE + offset_diff, 0x1);
+ /* Set fence pointer to the same place like WR pointer */
+ iwl_trans_write_prph(mvm->trans,
+ RXF_LD_WR2FENCE + offset_diff, 0x1);
+ /* Set fence offset */
+ iwl_trans_write_prph(mvm->trans,
+ RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
+ 0x0);
+
+ /* Read FIFO */
+ fifo_len /= sizeof(u32); /* Size in DWORDS */
+ for (j = 0; j < fifo_len; j++)
+ fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+ RXF_FIFO_RD_FENCE_INC +
+ offset_diff);
+ *dump_data = iwl_fw_error_next_data(*dump_data);
+ }
+
+ /* Pull TXF data from all TXFs */
+ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
+ /* Mark the number of TXF we're pulling now */
+ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
+
+ fifo_hdr = (void *)(*dump_data)->data;
+ fifo_data = (void *)fifo_hdr->data;
+ fifo_len = mvm->shared_mem_cfg.txfifo_size[i];
+
+ /* No need to try to read the data if the length is 0 */
+ if (fifo_len == 0)
+ continue;
+
+ /* Add a TLV for the FIFO */
+ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
+ (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+ fifo_hdr->fifo_num = cpu_to_le32(i);
+ fifo_hdr->available_bytes =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_FIFO_ITEM_CNT));
+ fifo_hdr->wr_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_WR_PTR));
+ fifo_hdr->rd_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_RD_PTR));
+ fifo_hdr->fence_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_FENCE_PTR));
+ fifo_hdr->fence_mode =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_LOCK_FENCE));
+
+ /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
+ iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
+ TXF_WR_PTR);
+
+ /* Dummy-read to advance the read pointer to the head */
+ iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA);
+
+ /* Read FIFO */
+ fifo_len /= sizeof(u32); /* Size in DWORDS */
+ for (j = 0; j < fifo_len; j++)
+ fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+ TXF_READ_MODIFY_DATA);
+ *dump_data = iwl_fw_error_next_data(*dump_data);
+ }
+
+ iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
+{
+ if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert)
+ return;
+
+ kfree(mvm->fw_dump_desc);
+ mvm->fw_dump_desc = NULL;
+}
+
+#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */
+#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */
+
+static const struct {
+ u32 start, end;
+} iwl_prph_dump_addr[] = {
+ { .start = 0x00a00000, .end = 0x00a00000 },
+ { .start = 0x00a0000c, .end = 0x00a00024 },
+ { .start = 0x00a0002c, .end = 0x00a0003c },
+ { .start = 0x00a00410, .end = 0x00a00418 },
+ { .start = 0x00a00420, .end = 0x00a00420 },
+ { .start = 0x00a00428, .end = 0x00a00428 },
+ { .start = 0x00a00430, .end = 0x00a0043c },
+ { .start = 0x00a00444, .end = 0x00a00444 },
+ { .start = 0x00a004c0, .end = 0x00a004cc },
+ { .start = 0x00a004d8, .end = 0x00a004d8 },
+ { .start = 0x00a004e0, .end = 0x00a004f0 },
+ { .start = 0x00a00840, .end = 0x00a00840 },
+ { .start = 0x00a00850, .end = 0x00a00858 },
+ { .start = 0x00a01004, .end = 0x00a01008 },
+ { .start = 0x00a01010, .end = 0x00a01010 },
+ { .start = 0x00a01018, .end = 0x00a01018 },
+ { .start = 0x00a01024, .end = 0x00a01024 },
+ { .start = 0x00a0102c, .end = 0x00a01034 },
+ { .start = 0x00a0103c, .end = 0x00a01040 },
+ { .start = 0x00a01048, .end = 0x00a01094 },
+ { .start = 0x00a01c00, .end = 0x00a01c20 },
+ { .start = 0x00a01c58, .end = 0x00a01c58 },
+ { .start = 0x00a01c7c, .end = 0x00a01c7c },
+ { .start = 0x00a01c28, .end = 0x00a01c54 },
+ { .start = 0x00a01c5c, .end = 0x00a01c5c },
+ { .start = 0x00a01c60, .end = 0x00a01cdc },
+ { .start = 0x00a01ce0, .end = 0x00a01d0c },
+ { .start = 0x00a01d18, .end = 0x00a01d20 },
+ { .start = 0x00a01d2c, .end = 0x00a01d30 },
+ { .start = 0x00a01d40, .end = 0x00a01d5c },
+ { .start = 0x00a01d80, .end = 0x00a01d80 },
+ { .start = 0x00a01d98, .end = 0x00a01d9c },
+ { .start = 0x00a01da8, .end = 0x00a01da8 },
+ { .start = 0x00a01db8, .end = 0x00a01df4 },
+ { .start = 0x00a01dc0, .end = 0x00a01dfc },
+ { .start = 0x00a01e00, .end = 0x00a01e2c },
+ { .start = 0x00a01e40, .end = 0x00a01e60 },
+ { .start = 0x00a01e68, .end = 0x00a01e6c },
+ { .start = 0x00a01e74, .end = 0x00a01e74 },
+ { .start = 0x00a01e84, .end = 0x00a01e90 },
+ { .start = 0x00a01e9c, .end = 0x00a01ec4 },
+ { .start = 0x00a01ed0, .end = 0x00a01ee0 },
+ { .start = 0x00a01f00, .end = 0x00a01f1c },
+ { .start = 0x00a01f44, .end = 0x00a01ffc },
+ { .start = 0x00a02000, .end = 0x00a02048 },
+ { .start = 0x00a02068, .end = 0x00a020f0 },
+ { .start = 0x00a02100, .end = 0x00a02118 },
+ { .start = 0x00a02140, .end = 0x00a0214c },
+ { .start = 0x00a02168, .end = 0x00a0218c },
+ { .start = 0x00a021c0, .end = 0x00a021c0 },
+ { .start = 0x00a02400, .end = 0x00a02410 },
+ { .start = 0x00a02418, .end = 0x00a02420 },
+ { .start = 0x00a02428, .end = 0x00a0242c },
+ { .start = 0x00a02434, .end = 0x00a02434 },
+ { .start = 0x00a02440, .end = 0x00a02460 },
+ { .start = 0x00a02468, .end = 0x00a024b0 },
+ { .start = 0x00a024c8, .end = 0x00a024cc },
+ { .start = 0x00a02500, .end = 0x00a02504 },
+ { .start = 0x00a0250c, .end = 0x00a02510 },
+ { .start = 0x00a02540, .end = 0x00a02554 },
+ { .start = 0x00a02580, .end = 0x00a025f4 },
+ { .start = 0x00a02600, .end = 0x00a0260c },
+ { .start = 0x00a02648, .end = 0x00a02650 },
+ { .start = 0x00a02680, .end = 0x00a02680 },
+ { .start = 0x00a026c0, .end = 0x00a026d0 },
+ { .start = 0x00a02700, .end = 0x00a0270c },
+ { .start = 0x00a02804, .end = 0x00a02804 },
+ { .start = 0x00a02818, .end = 0x00a0281c },
+ { .start = 0x00a02c00, .end = 0x00a02db4 },
+ { .start = 0x00a02df4, .end = 0x00a02fb0 },
+ { .start = 0x00a03000, .end = 0x00a03014 },
+ { .start = 0x00a0301c, .end = 0x00a0302c },
+ { .start = 0x00a03034, .end = 0x00a03038 },
+ { .start = 0x00a03040, .end = 0x00a03048 },
+ { .start = 0x00a03060, .end = 0x00a03068 },
+ { .start = 0x00a03070, .end = 0x00a03074 },
+ { .start = 0x00a0307c, .end = 0x00a0307c },
+ { .start = 0x00a03080, .end = 0x00a03084 },
+ { .start = 0x00a0308c, .end = 0x00a03090 },
+ { .start = 0x00a03098, .end = 0x00a03098 },
+ { .start = 0x00a030a0, .end = 0x00a030a0 },
+ { .start = 0x00a030a8, .end = 0x00a030b4 },
+ { .start = 0x00a030bc, .end = 0x00a030bc },
+ { .start = 0x00a030c0, .end = 0x00a0312c },
+ { .start = 0x00a03c00, .end = 0x00a03c5c },
+ { .start = 0x00a04400, .end = 0x00a04454 },
+ { .start = 0x00a04460, .end = 0x00a04474 },
+ { .start = 0x00a044c0, .end = 0x00a044ec },
+ { .start = 0x00a04500, .end = 0x00a04504 },
+ { .start = 0x00a04510, .end = 0x00a04538 },
+ { .start = 0x00a04540, .end = 0x00a04548 },
+ { .start = 0x00a04560, .end = 0x00a0457c },
+ { .start = 0x00a04590, .end = 0x00a04598 },
+ { .start = 0x00a045c0, .end = 0x00a045f4 },
+ { .start = 0x00a44000, .end = 0x00a7bf80 },
+};
+
+static u32 iwl_dump_prph(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data)
+{
+ struct iwl_fw_error_dump_prph *prph;
+ unsigned long flags;
+ u32 prph_len = 0, i;
+
+ if (!iwl_trans_grab_nic_access(trans, &flags))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
+ /* The range includes both boundaries */
+ int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
+ iwl_prph_dump_addr[i].start + 4;
+ int reg;
+ __le32 *val;
+
+ prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
+ (*data)->len = cpu_to_le32(sizeof(*prph) +
+ num_bytes_in_chunk);
+ prph = (void *)(*data)->data;
+ prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
+ val = (void *)prph->data;
+
+ for (reg = iwl_prph_dump_addr[i].start;
+ reg <= iwl_prph_dump_addr[i].end;
+ reg += 4)
+ *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans,
+ reg));
+
+ *data = iwl_fw_error_next_data(*data);
+ }
+
+ iwl_trans_release_nic_access(trans, &flags);
+
+ return prph_len;
+}
+
+void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
+{
+ struct iwl_fw_error_dump_file *dump_file;
+ struct iwl_fw_error_dump_data *dump_data;
+ struct iwl_fw_error_dump_info *dump_info;
+ struct iwl_fw_error_dump_mem *dump_mem;
+ struct iwl_fw_error_dump_trigger_desc *dump_trig;
+ struct iwl_mvm_dump_ptrs *fw_error_dump;
+ u32 sram_len, sram_ofs;
+ u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0;
+ u32 smem_len = mvm->cfg->smem_len;
+ u32 sram2_len = mvm->cfg->dccm2_len;
+ bool monitor_dump_only = false;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* there's no point in fw dump if the bus is dead */
+ if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) {
+ IWL_ERR(mvm, "Skip fw error dump since bus is dead\n");
+ goto out;
+ }
+
+ if (mvm->fw_dump_trig &&
+ mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
+ monitor_dump_only = true;
+
+ fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
+ if (!fw_error_dump)
+ goto out;
+
+ /* SRAM - include stack CCM if driver knows the values for it */
+ if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
+ const struct fw_img *img;
+
+ img = &mvm->fw->img[mvm->cur_ucode];
+ sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+ sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ } else {
+ sram_ofs = mvm->cfg->dccm_offset;
+ sram_len = mvm->cfg->dccm_len;
+ }
+
+ /* reading RXF/TXF sizes */
+ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
+ struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
+
+ fifo_data_len = 0;
+
+ /* Count RXF size */
+ for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
+ if (!mem_cfg->rxfifo_size[i])
+ continue;
+
+ /* Add header info */
+ fifo_data_len += mem_cfg->rxfifo_size[i] +
+ sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_fifo);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
+ if (!mem_cfg->txfifo_size[i])
+ continue;
+
+ /* Add header info */
+ fifo_data_len += mem_cfg->txfifo_size[i] +
+ sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_fifo);
+ }
+
+ /* Make room for PRPH registers */
+ for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
+ /* The range includes both boundaries */
+ int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
+ iwl_prph_dump_addr[i].start + 4;
+
+ prph_len += sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_prph) +
+ num_bytes_in_chunk;
+ }
+
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+ radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
+ }
+
+ file_len = sizeof(*dump_file) +
+ sizeof(*dump_data) * 2 +
+ sram_len + sizeof(*dump_mem) +
+ fifo_data_len +
+ prph_len +
+ radio_len +
+ sizeof(*dump_info);
+
+ /* Make room for the SMEM, if it exists */
+ if (smem_len)
+ file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
+
+ /* Make room for the secondary SRAM, if it exists */
+ if (sram2_len)
+ file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
+
+ /* Make room for fw's virtual image pages, if it exists */
+ if (mvm->fw->img[mvm->cur_ucode].paging_mem_size)
+ file_len += mvm->num_of_paging_blk *
+ (sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_paging) +
+ PAGING_BLOCK_SIZE);
+
+ /* If we only want a monitor dump, reset the file length */
+ if (monitor_dump_only) {
+ file_len = sizeof(*dump_file) + sizeof(*dump_data) +
+ sizeof(*dump_info);
+ }
+
+ /*
+ * In 8000 HW family B-step include the ICCM (which resides separately)
+ */
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
+ file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
+ IWL8260_ICCM_LEN;
+
+ if (mvm->fw_dump_desc)
+ file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
+ mvm->fw_dump_desc->len;
+
+ dump_file = vzalloc(file_len);
+ if (!dump_file) {
+ kfree(fw_error_dump);
+ goto out;
+ }
+
+ fw_error_dump->op_mode_ptr = dump_file;
+
+ dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
+ dump_data = (void *)dump_file->data;
+
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
+ dump_data->len = cpu_to_le32(sizeof(*dump_info));
+ dump_info = (void *)dump_data->data;
+ dump_info->device_family =
+ mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
+ cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
+ cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+ dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
+ memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
+ sizeof(dump_info->fw_human_readable));
+ strncpy(dump_info->dev_human_readable, mvm->cfg->name,
+ sizeof(dump_info->dev_human_readable));
+ strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
+ sizeof(dump_info->bus_human_readable));
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ /* We only dump the FIFOs if the FW is in error state */
+ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
+ iwl_mvm_dump_fifos(mvm, &dump_data);
+ if (radio_len)
+ iwl_mvm_read_radio_reg(mvm, &dump_data);
+ }
+
+ if (mvm->fw_dump_desc) {
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
+ dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
+ mvm->fw_dump_desc->len);
+ dump_trig = (void *)dump_data->data;
+ memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
+ sizeof(*dump_trig) + mvm->fw_dump_desc->len);
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ }
+
+ /* In case we only want monitor dump, skip to dump trasport data */
+ if (monitor_dump_only)
+ goto dump_trans_data;
+
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+ dump_mem->offset = cpu_to_le32(sram_ofs);
+ iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
+ sram_len);
+
+ if (smem_len) {
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
+ dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
+ iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
+ dump_mem->data, smem_len);
+ }
+
+ if (sram2_len) {
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+ dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
+ iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
+ dump_mem->data, sram2_len);
+ }
+
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
+ sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+ dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
+ iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
+ dump_mem->data, IWL8260_ICCM_LEN);
+ }
+
+ /* Dump fw's virtual image */
+ if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) {
+ u32 i;
+
+ for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
+ struct iwl_fw_error_dump_paging *paging;
+ struct page *pages =
+ mvm->fw_paging_db[i].fw_paging_block;
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
+ dump_data->len = cpu_to_le32(sizeof(*paging) +
+ PAGING_BLOCK_SIZE);
+ paging = (void *)dump_data->data;
+ paging->index = cpu_to_le32(i);
+ memcpy(paging->data, page_address(pages),
+ PAGING_BLOCK_SIZE);
+ }
+ }
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ if (prph_len)
+ iwl_dump_prph(mvm->trans, &dump_data);
+
+dump_trans_data:
+ fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans,
+ mvm->fw_dump_trig);
+ fw_error_dump->op_mode_len = file_len;
+ if (fw_error_dump->trans_ptr)
+ file_len += fw_error_dump->trans_ptr->len;
+ dump_file->file_len = cpu_to_le32(file_len);
+
+ dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
+ GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
+
+out:
+ iwl_mvm_free_fw_dump_desc(mvm);
+ mvm->fw_dump_trig = NULL;
+ clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
+}
+
+const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
+ .trig_desc = {
+ .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
+ },
+};
+
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+ const struct iwl_mvm_dump_desc *desc,
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ unsigned int delay = 0;
+
+ if (trigger)
+ delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
+
+ if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
+ return -EBUSY;
+
+ if (WARN_ON(mvm->fw_dump_desc))
+ iwl_mvm_free_fw_dump_desc(mvm);
+
+ IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
+ le32_to_cpu(desc->trig_desc.type));
+
+ mvm->fw_dump_desc = desc;
+ mvm->fw_dump_trig = trigger;
+
+ queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
+
+ return 0;
+}
+
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+ const char *str, size_t len,
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ struct iwl_mvm_dump_desc *desc;
+
+ desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->len = len;
+ desc->trig_desc.type = cpu_to_le32(trig);
+ memcpy(desc->trig_desc.data, str, len);
+
+ return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger);
+}
+
+int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trigger,
+ const char *fmt, ...)
+{
+ u16 occurrences = le16_to_cpu(trigger->occurrences);
+ int ret, len = 0;
+ char buf[64];
+
+ if (!occurrences)
+ return 0;
+
+ if (fmt) {
+ va_list ap;
+
+ buf[sizeof(buf) - 1] = '\0';
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ /* check for truncation */
+ if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
+ buf[sizeof(buf) - 1] = '\0';
+
+ len = strlen(buf) + 1;
+ }
+
+ ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len,
+ trigger);
+
+ if (ret)
+ return ret;
+
+ trigger->occurrences = cpu_to_le16(occurrences - 1);
+ return 0;
+}
+
+static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
+{
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+ iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+ else
+ iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
+}
+
+int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
+{
+ u8 *ptr;
+ int ret;
+ int i;
+
+ if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
+ "Invalid configuration %d\n", conf_id))
+ return -EINVAL;
+
+ /* EARLY START - firmware's configuration is hard coded */
+ if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
+ !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
+ conf_id == FW_DBG_START_FROM_ALIVE) {
+ iwl_mvm_restart_early_start(mvm);
+ return 0;
+ }
+
+ if (!mvm->fw->dbg_conf_tlv[conf_id])
+ return -EINVAL;
+
+ if (mvm->fw_dbg_conf != FW_DBG_INVALID)
+ IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
+ mvm->fw_dbg_conf);
+
+ /* Send all HCMDs for configuring the FW debug */
+ ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
+ for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
+ struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
+ le16_to_cpu(cmd->len), cmd->data);
+ if (ret)
+ return ret;
+
+ ptr += sizeof(*cmd);
+ ptr += le16_to_cpu(cmd->len);
+ }
+
+ mvm->fw_dbg_conf = conf_id;
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h
new file mode 100644
index 000000000000..f7dff7612c9c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h
@@ -0,0 +1,174 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __mvm_fw_dbg_h__
+#define __mvm_fw_dbg_h__
+#include "iwl-fw-file.h"
+#include "iwl-fw-error-dump.h"
+#include "mvm.h"
+
+void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+ const struct iwl_mvm_dump_desc *desc,
+ const struct iwl_fw_dbg_trigger_tlv *trigger);
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+ const char *str, size_t len,
+ const struct iwl_fw_dbg_trigger_tlv *trigger);
+int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trigger,
+ const char *fmt, ...) __printf(3, 4);
+int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
+
+#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \
+ void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \
+ unlikely(__dbg_trigger); \
+})
+
+static inline struct iwl_fw_dbg_trigger_tlv*
+_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id)
+{
+ return fw->dbg_trigger_tlv[id];
+}
+
+#define iwl_fw_dbg_get_trigger(fw, id) ({ \
+ BUILD_BUG_ON(!__builtin_constant_p(id)); \
+ BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \
+ _iwl_fw_dbg_get_trigger((fw), (id)); \
+})
+
+static inline bool
+iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
+ struct ieee80211_vif *vif)
+{
+ u32 trig_vif = le32_to_cpu(trig->vif_type);
+
+ return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif;
+}
+
+static inline bool
+iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trig)
+{
+ return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
+ (mvm->fw_dbg_conf == FW_DBG_INVALID ||
+ (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids))));
+}
+
+static inline bool
+iwl_fw_dbg_no_trig_window(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trig)
+{
+ unsigned long wind_jiff =
+ msecs_to_jiffies(le16_to_cpu(trig->trig_dis_ms));
+ u32 id = le32_to_cpu(trig->id);
+
+ /* If this is the first event checked, jump to update start ts */
+ if (mvm->fw_dbg_non_collect_ts_start[id] &&
+ (time_after(mvm->fw_dbg_non_collect_ts_start[id] + wind_jiff,
+ jiffies)))
+ return true;
+
+ mvm->fw_dbg_non_collect_ts_start[id] = jiffies;
+ return false;
+}
+
+static inline bool
+iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_fw_dbg_trigger_tlv *trig)
+{
+ if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif))
+ return false;
+
+ if (iwl_fw_dbg_no_trig_window(mvm, trig)) {
+ IWL_WARN(mvm, "Trigger %d occurred while no-collect window.\n",
+ trig->id);
+ return false;
+ }
+
+ return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig);
+}
+
+static inline void
+_iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ if (!trigger)
+ return;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
+}
+
+#define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \
+ _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \
+ iwl_fw_dbg_get_trigger((mvm)->fw,\
+ (trig)))
+
+#endif /* __mvm_fw_dbg_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
new file mode 100644
index 000000000000..4ed5180c547b
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -0,0 +1,1037 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-fw.h"
+#include "iwl-debug.h"
+#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-prph.h"
+#include "iwl-eeprom-parse.h"
+
+#include "mvm.h"
+#include "fw-dbg.h"
+#include "iwl-phy-db.h"
+
+#define MVM_UCODE_ALIVE_TIMEOUT HZ
+#define MVM_UCODE_CALIB_TIMEOUT (2*HZ)
+
+#define UCODE_VALID_OK cpu_to_le32(0x1)
+
+struct iwl_mvm_alive_data {
+ bool valid;
+ u32 scd_base_addr;
+};
+
+static inline const struct fw_img *
+iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type)
+{
+ if (ucode_type >= IWL_UCODE_TYPE_MAX)
+ return NULL;
+
+ return &mvm->fw->img[ucode_type];
+}
+
+static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
+{
+ struct iwl_tx_ant_cfg_cmd tx_ant_cmd = {
+ .valid = cpu_to_le32(valid_tx_ant),
+ };
+
+ IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
+ return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0,
+ sizeof(tx_ant_cmd), &tx_ant_cmd);
+}
+
+static void iwl_free_fw_paging(struct iwl_mvm *mvm)
+{
+ int i;
+
+ if (!mvm->fw_paging_db[0].fw_paging_block)
+ return;
+
+ for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) {
+ if (!mvm->fw_paging_db[i].fw_paging_block) {
+ IWL_DEBUG_FW(mvm,
+ "Paging: block %d already freed, continue to next page\n",
+ i);
+
+ continue;
+ }
+
+ __free_pages(mvm->fw_paging_db[i].fw_paging_block,
+ get_order(mvm->fw_paging_db[i].fw_paging_size));
+ }
+ kfree(mvm->trans->paging_download_buf);
+ memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db));
+}
+
+static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image)
+{
+ int sec_idx, idx;
+ u32 offset = 0;
+
+ /*
+ * find where is the paging image start point:
+ * if CPU2 exist and it's in paging format, then the image looks like:
+ * CPU1 sections (2 or more)
+ * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
+ * CPU2 sections (not paged)
+ * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
+ * non paged to CPU2 paging sec
+ * CPU2 paging CSS
+ * CPU2 paging image (including instruction and data)
+ */
+ for (sec_idx = 0; sec_idx < IWL_UCODE_SECTION_MAX; sec_idx++) {
+ if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) {
+ sec_idx++;
+ break;
+ }
+ }
+
+ if (sec_idx >= IWL_UCODE_SECTION_MAX) {
+ IWL_ERR(mvm, "driver didn't find paging image\n");
+ iwl_free_fw_paging(mvm);
+ return -EINVAL;
+ }
+
+ /* copy the CSS block to the dram */
+ IWL_DEBUG_FW(mvm, "Paging: load paging CSS to FW, sec = %d\n",
+ sec_idx);
+
+ memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block),
+ image->sec[sec_idx].data,
+ mvm->fw_paging_db[0].fw_paging_size);
+
+ IWL_DEBUG_FW(mvm,
+ "Paging: copied %d CSS bytes to first block\n",
+ mvm->fw_paging_db[0].fw_paging_size);
+
+ sec_idx++;
+
+ /*
+ * copy the paging blocks to the dram
+ * loop index start from 1 since that CSS block already copied to dram
+ * and CSS index is 0.
+ * loop stop at num_of_paging_blk since that last block is not full.
+ */
+ for (idx = 1; idx < mvm->num_of_paging_blk; idx++) {
+ memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block),
+ image->sec[sec_idx].data + offset,
+ mvm->fw_paging_db[idx].fw_paging_size);
+
+ IWL_DEBUG_FW(mvm,
+ "Paging: copied %d paging bytes to block %d\n",
+ mvm->fw_paging_db[idx].fw_paging_size,
+ idx);
+
+ offset += mvm->fw_paging_db[idx].fw_paging_size;
+ }
+
+ /* copy the last paging block */
+ if (mvm->num_of_pages_in_last_blk > 0) {
+ memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block),
+ image->sec[sec_idx].data + offset,
+ FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk);
+
+ IWL_DEBUG_FW(mvm,
+ "Paging: copied %d pages in the last block %d\n",
+ mvm->num_of_pages_in_last_blk, idx);
+ }
+
+ return 0;
+}
+
+static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm,
+ const struct fw_img *image)
+{
+ struct page *block;
+ dma_addr_t phys = 0;
+ int blk_idx = 0;
+ int order, num_of_pages;
+ int dma_enabled;
+
+ if (mvm->fw_paging_db[0].fw_paging_block)
+ return 0;
+
+ dma_enabled = is_device_dma_capable(mvm->trans->dev);
+
+ /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */
+ BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE);
+
+ num_of_pages = image->paging_mem_size / FW_PAGING_SIZE;
+ mvm->num_of_paging_blk = ((num_of_pages - 1) /
+ NUM_OF_PAGE_PER_GROUP) + 1;
+
+ mvm->num_of_pages_in_last_blk =
+ num_of_pages -
+ NUM_OF_PAGE_PER_GROUP * (mvm->num_of_paging_blk - 1);
+
+ IWL_DEBUG_FW(mvm,
+ "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n",
+ mvm->num_of_paging_blk,
+ mvm->num_of_pages_in_last_blk);
+
+ /* allocate block of 4Kbytes for paging CSS */
+ order = get_order(FW_PAGING_SIZE);
+ block = alloc_pages(GFP_KERNEL, order);
+ if (!block) {
+ /* free all the previous pages since we failed */
+ iwl_free_fw_paging(mvm);
+ return -ENOMEM;
+ }
+
+ mvm->fw_paging_db[blk_idx].fw_paging_block = block;
+ mvm->fw_paging_db[blk_idx].fw_paging_size = FW_PAGING_SIZE;
+
+ if (dma_enabled) {
+ phys = dma_map_page(mvm->trans->dev, block, 0,
+ PAGE_SIZE << order, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(mvm->trans->dev, phys)) {
+ /*
+ * free the previous pages and the current one since
+ * we failed to map_page.
+ */
+ iwl_free_fw_paging(mvm);
+ return -ENOMEM;
+ }
+ mvm->fw_paging_db[blk_idx].fw_paging_phys = phys;
+ } else {
+ mvm->fw_paging_db[blk_idx].fw_paging_phys = PAGING_ADDR_SIG |
+ blk_idx << BLOCK_2_EXP_SIZE;
+ }
+
+ IWL_DEBUG_FW(mvm,
+ "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n",
+ order);
+
+ /*
+ * allocate blocks in dram.
+ * since that CSS allocated in fw_paging_db[0] loop start from index 1
+ */
+ for (blk_idx = 1; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) {
+ /* allocate block of PAGING_BLOCK_SIZE (32K) */
+ order = get_order(PAGING_BLOCK_SIZE);
+ block = alloc_pages(GFP_KERNEL, order);
+ if (!block) {
+ /* free all the previous pages since we failed */
+ iwl_free_fw_paging(mvm);
+ return -ENOMEM;
+ }
+
+ mvm->fw_paging_db[blk_idx].fw_paging_block = block;
+ mvm->fw_paging_db[blk_idx].fw_paging_size = PAGING_BLOCK_SIZE;
+
+ if (dma_enabled) {
+ phys = dma_map_page(mvm->trans->dev, block, 0,
+ PAGE_SIZE << order,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(mvm->trans->dev, phys)) {
+ /*
+ * free the previous pages and the current one
+ * since we failed to map_page.
+ */
+ iwl_free_fw_paging(mvm);
+ return -ENOMEM;
+ }
+ mvm->fw_paging_db[blk_idx].fw_paging_phys = phys;
+ } else {
+ mvm->fw_paging_db[blk_idx].fw_paging_phys =
+ PAGING_ADDR_SIG |
+ blk_idx << BLOCK_2_EXP_SIZE;
+ }
+
+ IWL_DEBUG_FW(mvm,
+ "Paging: allocated 32K bytes (order %d) for firmware paging.\n",
+ order);
+ }
+
+ return 0;
+}
+
+static int iwl_save_fw_paging(struct iwl_mvm *mvm,
+ const struct fw_img *fw)
+{
+ int ret;
+
+ ret = iwl_alloc_fw_paging_mem(mvm, fw);
+ if (ret)
+ return ret;
+
+ return iwl_fill_paging_mem(mvm, fw);
+}
+
+/* send paging cmd to FW in case CPU2 has paging image */
+static int iwl_send_paging_cmd(struct iwl_mvm *mvm, const struct fw_img *fw)
+{
+ int blk_idx;
+ __le32 dev_phy_addr;
+ struct iwl_fw_paging_cmd fw_paging_cmd = {
+ .flags =
+ cpu_to_le32(PAGING_CMD_IS_SECURED |
+ PAGING_CMD_IS_ENABLED |
+ (mvm->num_of_pages_in_last_blk <<
+ PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
+ .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE),
+ .block_num = cpu_to_le32(mvm->num_of_paging_blk),
+ };
+
+ /* loop for for all paging blocks + CSS block */
+ for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) {
+ dev_phy_addr =
+ cpu_to_le32(mvm->fw_paging_db[blk_idx].fw_paging_phys >>
+ PAGE_2_EXP_SIZE);
+ fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr;
+ }
+
+ return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(FW_PAGING_BLOCK_CMD,
+ IWL_ALWAYS_LONG_GROUP, 0),
+ 0, sizeof(fw_paging_cmd), &fw_paging_cmd);
+}
+
+/*
+ * Send paging item cmd to FW in case CPU2 has paging image
+ */
+static int iwl_trans_get_paging_item(struct iwl_mvm *mvm)
+{
+ int ret;
+ struct iwl_fw_get_item_cmd fw_get_item_cmd = {
+ .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING),
+ };
+
+ struct iwl_fw_get_item_resp *item_resp;
+ struct iwl_host_cmd cmd = {
+ .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0),
+ .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+ .data = { &fw_get_item_cmd, },
+ };
+
+ cmd.len[0] = sizeof(struct iwl_fw_get_item_cmd);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret) {
+ IWL_ERR(mvm,
+ "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data;
+ if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) {
+ IWL_ERR(mvm,
+ "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n",
+ le32_to_cpu(item_resp->item_id));
+ ret = -EIO;
+ goto exit;
+ }
+
+ mvm->trans->paging_download_buf = kzalloc(MAX_PAGING_IMAGE_SIZE,
+ GFP_KERNEL);
+ if (!mvm->trans->paging_download_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ mvm->trans->paging_req_addr = le32_to_cpu(item_resp->item_val);
+ mvm->trans->paging_db = mvm->fw_paging_db;
+ IWL_DEBUG_FW(mvm,
+ "Paging: got paging request address (paging_req_addr 0x%08x)\n",
+ mvm->trans->paging_req_addr);
+
+exit:
+ iwl_free_resp(&cmd);
+
+ return ret;
+}
+
+static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mvm *mvm =
+ container_of(notif_wait, struct iwl_mvm, notif_wait);
+ struct iwl_mvm_alive_data *alive_data = data;
+ struct mvm_alive_resp_ver1 *palive1;
+ struct mvm_alive_resp_ver2 *palive2;
+ struct mvm_alive_resp *palive;
+
+ if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) {
+ palive1 = (void *)pkt->data;
+
+ mvm->support_umac_log = false;
+ mvm->error_event_table =
+ le32_to_cpu(palive1->error_event_table_ptr);
+ mvm->log_event_table =
+ le32_to_cpu(palive1->log_event_table_ptr);
+ alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr);
+
+ alive_data->valid = le16_to_cpu(palive1->status) ==
+ IWL_ALIVE_STATUS_OK;
+ IWL_DEBUG_FW(mvm,
+ "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+ le16_to_cpu(palive1->status), palive1->ver_type,
+ palive1->ver_subtype, palive1->flags);
+ } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) {
+ palive2 = (void *)pkt->data;
+
+ mvm->error_event_table =
+ le32_to_cpu(palive2->error_event_table_ptr);
+ mvm->log_event_table =
+ le32_to_cpu(palive2->log_event_table_ptr);
+ alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
+ mvm->umac_error_event_table =
+ le32_to_cpu(palive2->error_info_addr);
+ mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr);
+ mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size);
+
+ alive_data->valid = le16_to_cpu(palive2->status) ==
+ IWL_ALIVE_STATUS_OK;
+ if (mvm->umac_error_event_table)
+ mvm->support_umac_log = true;
+
+ IWL_DEBUG_FW(mvm,
+ "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+ le16_to_cpu(palive2->status), palive2->ver_type,
+ palive2->ver_subtype, palive2->flags);
+
+ IWL_DEBUG_FW(mvm,
+ "UMAC version: Major - 0x%x, Minor - 0x%x\n",
+ palive2->umac_major, palive2->umac_minor);
+ } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
+ palive = (void *)pkt->data;
+
+ mvm->error_event_table =
+ le32_to_cpu(palive->error_event_table_ptr);
+ mvm->log_event_table =
+ le32_to_cpu(palive->log_event_table_ptr);
+ alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+ mvm->umac_error_event_table =
+ le32_to_cpu(palive->error_info_addr);
+ mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr);
+ mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size);
+
+ alive_data->valid = le16_to_cpu(palive->status) ==
+ IWL_ALIVE_STATUS_OK;
+ if (mvm->umac_error_event_table)
+ mvm->support_umac_log = true;
+
+ IWL_DEBUG_FW(mvm,
+ "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+ le16_to_cpu(palive->status), palive->ver_type,
+ palive->ver_subtype, palive->flags);
+
+ IWL_DEBUG_FW(mvm,
+ "UMAC version: Major - 0x%x, Minor - 0x%x\n",
+ le32_to_cpu(palive->umac_major),
+ le32_to_cpu(palive->umac_minor));
+ }
+
+ return true;
+}
+
+static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_phy_db *phy_db = data;
+
+ if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) {
+ WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF);
+ return true;
+ }
+
+ WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC));
+
+ return false;
+}
+
+static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
+ enum iwl_ucode_type ucode_type)
+{
+ struct iwl_notification_wait alive_wait;
+ struct iwl_mvm_alive_data alive_data;
+ const struct fw_img *fw;
+ int ret, i;
+ enum iwl_ucode_type old_type = mvm->cur_ucode;
+ static const u16 alive_cmd[] = { MVM_ALIVE };
+ struct iwl_sf_region st_fwrd_space;
+
+ if (ucode_type == IWL_UCODE_REGULAR &&
+ iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))
+ fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
+ else
+ fw = iwl_get_ucode_image(mvm, ucode_type);
+ if (WARN_ON(!fw))
+ return -EINVAL;
+ mvm->cur_ucode = ucode_type;
+ mvm->ucode_loaded = false;
+
+ iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
+ alive_cmd, ARRAY_SIZE(alive_cmd),
+ iwl_alive_fn, &alive_data);
+
+ ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT);
+ if (ret) {
+ mvm->cur_ucode = old_type;
+ iwl_remove_notification(&mvm->notif_wait, &alive_wait);
+ return ret;
+ }
+
+ /*
+ * Some things may run in the background now, but we
+ * just wait for the ALIVE notification here.
+ */
+ ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait,
+ MVM_UCODE_ALIVE_TIMEOUT);
+ if (ret) {
+ if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ IWL_ERR(mvm,
+ "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
+ iwl_read_prph(mvm->trans, SB_CPU_1_STATUS),
+ iwl_read_prph(mvm->trans, SB_CPU_2_STATUS));
+ mvm->cur_ucode = old_type;
+ return ret;
+ }
+
+ if (!alive_data.valid) {
+ IWL_ERR(mvm, "Loaded ucode is not valid!\n");
+ mvm->cur_ucode = old_type;
+ return -EIO;
+ }
+
+ /*
+ * update the sdio allocation according to the pointer we get in the
+ * alive notification.
+ */
+ st_fwrd_space.addr = mvm->sf_space.addr;
+ st_fwrd_space.size = mvm->sf_space.size;
+ ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret);
+ return ret;
+ }
+
+ iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
+
+ /*
+ * configure and operate fw paging mechanism.
+ * driver configures the paging flow only once, CPU2 paging image
+ * included in the IWL_UCODE_INIT image.
+ */
+ if (fw->paging_mem_size) {
+ /*
+ * When dma is not enabled, the driver needs to copy / write
+ * the downloaded / uploaded page to / from the smem.
+ * This gets the location of the place were the pages are
+ * stored.
+ */
+ if (!is_device_dma_capable(mvm->trans->dev)) {
+ ret = iwl_trans_get_paging_item(mvm);
+ if (ret) {
+ IWL_ERR(mvm, "failed to get FW paging item\n");
+ return ret;
+ }
+ }
+
+ ret = iwl_save_fw_paging(mvm, fw);
+ if (ret) {
+ IWL_ERR(mvm, "failed to save the FW paging image\n");
+ return ret;
+ }
+
+ ret = iwl_send_paging_cmd(mvm, fw);
+ if (ret) {
+ IWL_ERR(mvm, "failed to send the paging cmd\n");
+ iwl_free_fw_paging(mvm);
+ return ret;
+ }
+ }
+
+ /*
+ * Note: all the queues are enabled as part of the interface
+ * initialization, but in firmware restart scenarios they
+ * could be stopped, so wake them up. In firmware restart,
+ * mac80211 will have the queues stopped as well until the
+ * reconfiguration completes. During normal startup, they
+ * will be empty.
+ */
+
+ memset(&mvm->queue_info, 0, sizeof(mvm->queue_info));
+ mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1;
+
+ for (i = 0; i < IEEE80211_MAX_QUEUES; i++)
+ atomic_set(&mvm->mac80211_queue_stop_count[i], 0);
+
+ mvm->ucode_loaded = true;
+
+ return 0;
+}
+
+static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
+{
+ struct iwl_phy_cfg_cmd phy_cfg_cmd;
+ enum iwl_ucode_type ucode_type = mvm->cur_ucode;
+
+ /* Set parameters */
+ phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm));
+ phy_cfg_cmd.calib_control.event_trigger =
+ mvm->fw->default_calib[ucode_type].event_trigger;
+ phy_cfg_cmd.calib_control.flow_trigger =
+ mvm->fw->default_calib[ucode_type].flow_trigger;
+
+ IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
+ phy_cfg_cmd.phy_cfg);
+
+ return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0,
+ sizeof(phy_cfg_cmd), &phy_cfg_cmd);
+}
+
+int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
+{
+ struct iwl_notification_wait calib_wait;
+ static const u16 init_complete[] = {
+ INIT_COMPLETE_NOTIF,
+ CALIB_RES_NOTIF_PHY_DB
+ };
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (WARN_ON_ONCE(mvm->calibrating))
+ return 0;
+
+ iwl_init_notification_wait(&mvm->notif_wait,
+ &calib_wait,
+ init_complete,
+ ARRAY_SIZE(init_complete),
+ iwl_wait_phy_db_entry,
+ mvm->phy_db);
+
+ /* Will also start the device */
+ ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret);
+ goto error;
+ }
+
+ ret = iwl_send_bt_init_conf(mvm);
+ if (ret)
+ goto error;
+
+ /* Read the NVM only at driver load time, no need to do this twice */
+ if (read_nvm) {
+ /* Read nvm */
+ ret = iwl_nvm_init(mvm, true);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
+ goto error;
+ }
+ }
+
+ /* In case we read the NVM from external file, load it to the NIC */
+ if (mvm->nvm_file_name)
+ iwl_mvm_load_nvm_to_nic(mvm);
+
+ ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
+ WARN_ON(ret);
+
+ /*
+ * abort after reading the nvm in case RF Kill is on, we will complete
+ * the init seq later when RF kill will switch to off
+ */
+ if (iwl_mvm_is_radio_hw_killed(mvm)) {
+ IWL_DEBUG_RF_KILL(mvm,
+ "jump over all phy activities due to RF kill\n");
+ iwl_remove_notification(&mvm->notif_wait, &calib_wait);
+ ret = 1;
+ goto out;
+ }
+
+ mvm->calibrating = true;
+
+ /* Send TX valid antennas before triggering calibrations */
+ ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
+ if (ret)
+ goto error;
+
+ /*
+ * Send phy configurations command to init uCode
+ * to start the 16.0 uCode init image internal calibrations.
+ */
+ ret = iwl_send_phy_cfg_cmd(mvm);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n",
+ ret);
+ goto error;
+ }
+
+ /*
+ * Some things may run in the background now, but we
+ * just wait for the calibration complete notification.
+ */
+ ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
+ MVM_UCODE_CALIB_TIMEOUT);
+
+ if (ret && iwl_mvm_is_radio_hw_killed(mvm)) {
+ IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
+ ret = 1;
+ }
+ goto out;
+
+error:
+ iwl_remove_notification(&mvm->notif_wait, &calib_wait);
+out:
+ mvm->calibrating = false;
+ if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
+ /* we want to debug INIT and we have no NVM - fake */
+ mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
+ sizeof(struct ieee80211_channel) +
+ sizeof(struct ieee80211_rate),
+ GFP_KERNEL);
+ if (!mvm->nvm_data)
+ return -ENOMEM;
+ mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels;
+ mvm->nvm_data->bands[0].n_channels = 1;
+ mvm->nvm_data->bands[0].n_bitrates = 1;
+ mvm->nvm_data->bands[0].bitrates =
+ (void *)mvm->nvm_data->channels + 1;
+ mvm->nvm_data->bands[0].bitrates->hw_value = 10;
+ }
+
+ return ret;
+}
+
+static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm)
+{
+ struct iwl_host_cmd cmd = {
+ .id = SHARED_MEM_CFG,
+ .flags = CMD_WANT_SKB,
+ .data = { NULL, },
+ .len = { 0, },
+ };
+ struct iwl_rx_packet *pkt;
+ struct iwl_shared_mem_cfg *mem_cfg;
+ u32 i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd)))
+ return;
+
+ pkt = cmd.resp_pkt;
+ mem_cfg = (void *)pkt->data;
+
+ mvm->shared_mem_cfg.shared_mem_addr =
+ le32_to_cpu(mem_cfg->shared_mem_addr);
+ mvm->shared_mem_cfg.shared_mem_size =
+ le32_to_cpu(mem_cfg->shared_mem_size);
+ mvm->shared_mem_cfg.sample_buff_addr =
+ le32_to_cpu(mem_cfg->sample_buff_addr);
+ mvm->shared_mem_cfg.sample_buff_size =
+ le32_to_cpu(mem_cfg->sample_buff_size);
+ mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr);
+ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++)
+ mvm->shared_mem_cfg.txfifo_size[i] =
+ le32_to_cpu(mem_cfg->txfifo_size[i]);
+ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++)
+ mvm->shared_mem_cfg.rxfifo_size[i] =
+ le32_to_cpu(mem_cfg->rxfifo_size[i]);
+ mvm->shared_mem_cfg.page_buff_addr =
+ le32_to_cpu(mem_cfg->page_buff_addr);
+ mvm->shared_mem_cfg.page_buff_size =
+ le32_to_cpu(mem_cfg->page_buff_size);
+ IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n");
+
+ iwl_free_resp(&cmd);
+}
+
+static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
+{
+ struct iwl_ltr_config_cmd cmd = {
+ .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
+ };
+
+ if (!mvm->trans->ltr_enabled)
+ return 0;
+
+ return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
+ sizeof(cmd), &cmd);
+}
+
+int iwl_mvm_up(struct iwl_mvm *mvm)
+{
+ int ret, i;
+ struct ieee80211_channel *chan;
+ struct cfg80211_chan_def chandef;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_trans_start_hw(mvm->trans);
+ if (ret)
+ return ret;
+
+ /*
+ * If we haven't completed the run of the init ucode during
+ * module loading, load init ucode now
+ * (for example, if we were in RFKILL)
+ */
+ ret = iwl_run_init_mvm_ucode(mvm, false);
+ if (ret && !iwlmvm_mod_params.init_dbg) {
+ IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
+ /* this can't happen */
+ if (WARN_ON(ret > 0))
+ ret = -ERFKILL;
+ goto error;
+ }
+ if (!iwlmvm_mod_params.init_dbg) {
+ /*
+ * Stop and start the transport without entering low power
+ * mode. This will save the state of other components on the
+ * device that are triggered by the INIT firwmare (MFUART).
+ */
+ _iwl_trans_stop_device(mvm->trans, false);
+ ret = _iwl_trans_start_hw(mvm->trans, false);
+ if (ret)
+ goto error;
+ }
+
+ if (iwlmvm_mod_params.init_dbg)
+ return 0;
+
+ ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
+ goto error;
+ }
+
+ iwl_mvm_get_shared_mem_conf(mvm);
+
+ ret = iwl_mvm_sf_update(mvm, NULL, false);
+ if (ret)
+ IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
+
+ mvm->fw_dbg_conf = FW_DBG_INVALID;
+ /* if we have a destination, assume EARLY START */
+ if (mvm->fw->dbg_dest_tlv)
+ mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE;
+ iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE);
+
+ ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
+ if (ret)
+ goto error;
+
+ ret = iwl_send_bt_init_conf(mvm);
+ if (ret)
+ goto error;
+
+ /* Send phy db control command and then phy db calibration*/
+ ret = iwl_send_phy_db_data(mvm->phy_db);
+ if (ret)
+ goto error;
+
+ ret = iwl_send_phy_cfg_cmd(mvm);
+ if (ret)
+ goto error;
+
+ /* init the fw <-> mac80211 STA mapping */
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
+ RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
+
+ mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+
+ /* reset quota debouncing buffer - 0xff will yield invalid data */
+ memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd));
+
+ /* Add auxiliary station for scanning */
+ ret = iwl_mvm_add_aux_sta(mvm);
+ if (ret)
+ goto error;
+
+ /* Add all the PHY contexts */
+ chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+ for (i = 0; i < NUM_PHY_CTX; i++) {
+ /*
+ * The channel used here isn't relevant as it's
+ * going to be overwritten in the other flows.
+ * For now use the first channel we have.
+ */
+ ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i],
+ &chandef, 1, 1);
+ if (ret)
+ goto error;
+ }
+
+ /* Initialize tx backoffs to the minimal possible */
+ iwl_mvm_tt_tx_backoff(mvm, 0);
+
+ WARN_ON(iwl_mvm_config_ltr(mvm));
+
+ ret = iwl_mvm_power_update_device(mvm);
+ if (ret)
+ goto error;
+
+ /*
+ * RTNL is not taken during Ct-kill, but we don't need to scan/Tx
+ * anyway, so don't init MCC.
+ */
+ if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) {
+ ret = iwl_mvm_init_mcc(mvm);
+ if (ret)
+ goto error;
+ }
+
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ mvm->scan_type = IWL_SCAN_TYPE_NOT_SET;
+ ret = iwl_mvm_config_scan(mvm);
+ if (ret)
+ goto error;
+ }
+
+ if (iwl_mvm_is_csum_supported(mvm) &&
+ mvm->cfg->features & NETIF_F_RXCSUM)
+ iwl_trans_write_prph(mvm->trans, RX_EN_CSUM, 0x3);
+
+ /* allow FW/transport low power modes if not during restart */
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
+ IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
+ return 0;
+ error:
+ iwl_trans_stop_device(mvm->trans);
+ return ret;
+}
+
+int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
+{
+ int ret, i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_trans_start_hw(mvm->trans);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret);
+ goto error;
+ }
+
+ ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
+ if (ret)
+ goto error;
+
+ /* Send phy db control command and then phy db calibration*/
+ ret = iwl_send_phy_db_data(mvm->phy_db);
+ if (ret)
+ goto error;
+
+ ret = iwl_send_phy_cfg_cmd(mvm);
+ if (ret)
+ goto error;
+
+ /* init the fw <-> mac80211 STA mapping */
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
+ RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
+
+ /* Add auxiliary station for scanning */
+ ret = iwl_mvm_add_aux_sta(mvm);
+ if (ret)
+ goto error;
+
+ return 0;
+ error:
+ iwl_trans_stop_device(mvm->trans);
+ return ret;
+}
+
+void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_card_state_notif *card_state_notif = (void *)pkt->data;
+ u32 flags = le32_to_cpu(card_state_notif->flags);
+
+ IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n",
+ (flags & HW_CARD_DISABLED) ? "Kill" : "On",
+ (flags & SW_CARD_DISABLED) ? "Kill" : "On",
+ (flags & CT_KILL_CARD_DISABLED) ?
+ "Reached" : "Not reached");
+}
+
+void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
+
+ IWL_DEBUG_INFO(mvm,
+ "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n",
+ le32_to_cpu(mfuart_notif->installed_ver),
+ le32_to_cpu(mfuart_notif->external_ver),
+ le32_to_cpu(mfuart_notif->status),
+ le32_to_cpu(mfuart_notif->duration));
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
new file mode 100644
index 000000000000..1e51fbe95f7c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
@@ -0,0 +1,136 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/leds.h>
+#include "iwl-io.h"
+#include "iwl-csr.h"
+#include "mvm.h"
+
+/* Set led register on */
+static void iwl_mvm_led_enable(struct iwl_mvm *mvm)
+{
+ iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
+}
+
+/* Set led register off */
+static void iwl_mvm_led_disable(struct iwl_mvm *mvm)
+{
+ iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF);
+}
+
+static void iwl_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led);
+ if (brightness > 0)
+ iwl_mvm_led_enable(mvm);
+ else
+ iwl_mvm_led_disable(mvm);
+}
+
+int iwl_mvm_leds_init(struct iwl_mvm *mvm)
+{
+ int mode = iwlwifi_mod_params.led_mode;
+ int ret;
+
+ switch (mode) {
+ case IWL_LED_BLINK:
+ IWL_ERR(mvm, "Blink led mode not supported, used default\n");
+ case IWL_LED_DEFAULT:
+ case IWL_LED_RF_STATE:
+ mode = IWL_LED_RF_STATE;
+ break;
+ case IWL_LED_DISABLE:
+ IWL_INFO(mvm, "Led disabled\n");
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
+ wiphy_name(mvm->hw->wiphy));
+ mvm->led.brightness_set = iwl_led_brightness_set;
+ mvm->led.max_brightness = 1;
+
+ if (mode == IWL_LED_RF_STATE)
+ mvm->led.default_trigger =
+ ieee80211_get_radio_led_name(mvm->hw);
+
+ ret = led_classdev_register(mvm->trans->dev, &mvm->led);
+ if (ret) {
+ kfree(mvm->led.name);
+ IWL_INFO(mvm, "Failed to enable led\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
+{
+ if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE)
+ return;
+
+ led_classdev_unregister(&mvm->led);
+ kfree(mvm->led.name);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
new file mode 100644
index 000000000000..bf1e5eb5dbdb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -0,0 +1,1464 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "fw-api.h"
+#include "mvm.h"
+#include "time-event.h"
+#include "fw-dbg.h"
+
+const u8 iwl_mvm_ac_to_tx_fifo[] = {
+ IWL_MVM_TX_FIFO_VO,
+ IWL_MVM_TX_FIFO_VI,
+ IWL_MVM_TX_FIFO_BE,
+ IWL_MVM_TX_FIFO_BK,
+};
+
+struct iwl_mvm_mac_iface_iterator_data {
+ struct iwl_mvm *mvm;
+ struct ieee80211_vif *vif;
+ unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
+ unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
+ enum iwl_tsf_id preferred_tsf;
+ bool found_vif;
+};
+
+struct iwl_mvm_hw_queues_iface_iterator_data {
+ struct ieee80211_vif *exclude_vif;
+ unsigned long used_hw_queues;
+};
+
+static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_mac_iface_iterator_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u16 min_bi;
+
+ /* Skip the interface for which we are trying to assign a tsf_id */
+ if (vif == data->vif)
+ return;
+
+ /*
+ * The TSF is a hardware/firmware resource, there are 4 and
+ * the driver should assign and free them as needed. However,
+ * there are cases where 2 MACs should share the same TSF ID
+ * for the purpose of clock sync, an optimization to avoid
+ * clock drift causing overlapping TBTTs/DTIMs for a GO and
+ * client in the system.
+ *
+ * The firmware will decide according to the MAC type which
+ * will be the master and slave. Clients that need to sync
+ * with a remote station will be the master, and an AP or GO
+ * will be the slave.
+ *
+ * Depending on the new interface type it can be slaved to
+ * or become the master of an existing interface.
+ */
+ switch (data->vif->type) {
+ case NL80211_IFTYPE_STATION:
+ /*
+ * The new interface is a client, so if the one we're iterating
+ * is an AP, and the beacon interval of the AP is a multiple or
+ * divisor of the beacon interval of the client, the same TSF
+ * should be used to avoid drift between the new client and
+ * existing AP. The existing AP will get drift updates from the
+ * new client context in this case.
+ */
+ if (vif->type != NL80211_IFTYPE_AP ||
+ data->preferred_tsf != NUM_TSF_IDS ||
+ !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+ break;
+
+ min_bi = min(data->vif->bss_conf.beacon_int,
+ vif->bss_conf.beacon_int);
+
+ if (!min_bi)
+ break;
+
+ if ((data->vif->bss_conf.beacon_int -
+ vif->bss_conf.beacon_int) % min_bi == 0) {
+ data->preferred_tsf = mvmvif->tsf_id;
+ return;
+ }
+ break;
+
+ case NL80211_IFTYPE_AP:
+ /*
+ * The new interface is AP/GO, so if its beacon interval is a
+ * multiple or a divisor of the beacon interval of an existing
+ * interface, it should get drift updates from an existing
+ * client or use the same TSF as an existing GO. There's no
+ * drift between TSFs internally but if they used different
+ * TSFs then a new client MAC could update one of them and
+ * cause drift that way.
+ */
+ if ((vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_STATION) ||
+ data->preferred_tsf != NUM_TSF_IDS ||
+ !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+ break;
+
+ min_bi = min(data->vif->bss_conf.beacon_int,
+ vif->bss_conf.beacon_int);
+
+ if (!min_bi)
+ break;
+
+ if ((data->vif->bss_conf.beacon_int -
+ vif->bss_conf.beacon_int) % min_bi == 0) {
+ data->preferred_tsf = mvmvif->tsf_id;
+ return;
+ }
+ break;
+ default:
+ /*
+ * For all other interface types there's no need to
+ * take drift into account. Either they're exclusive
+ * like IBSS and monitor, or we don't care much about
+ * their TSF (like P2P Device), but we won't be able
+ * to share the TSF resource.
+ */
+ break;
+ }
+
+ /*
+ * Unless we exited above, we can't share the TSF resource
+ * that the virtual interface we're iterating over is using
+ * with the new one, so clear the available bit and if this
+ * was the preferred one, reset that as well.
+ */
+ __clear_bit(mvmvif->tsf_id, data->available_tsf_ids);
+
+ if (data->preferred_tsf == mvmvif->tsf_id)
+ data->preferred_tsf = NUM_TSF_IDS;
+}
+
+/*
+ * Get the mask of the queues used by the vif
+ */
+u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif)
+{
+ u32 qmask = 0, ac;
+
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+ qmask |= BIT(vif->hw_queue[ac]);
+ }
+
+ if (vif->type == NL80211_IFTYPE_AP)
+ qmask |= BIT(vif->cab_queue);
+
+ return qmask;
+}
+
+static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+
+ /* exclude the given vif */
+ if (vif == data->exclude_vif)
+ return;
+
+ data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
+}
+
+static void iwl_mvm_mac_sta_hw_queues_iter(void *_data,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ /* Mark the queues used by the sta */
+ data->used_hw_queues |= mvmsta->tfd_queue_msk;
+}
+
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+ struct ieee80211_vif *exclude_vif)
+{
+ u8 sta_id;
+ struct iwl_mvm_hw_queues_iface_iterator_data data = {
+ .exclude_vif = exclude_vif,
+ .used_hw_queues =
+ BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
+ BIT(mvm->aux_queue) |
+ BIT(IWL_MVM_CMD_QUEUE),
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* mark all VIF used hw queues */
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ iwl_mvm_iface_hw_queues_iter, &data);
+
+ /* don't assign the same hw queues as TDLS stations */
+ ieee80211_iterate_stations_atomic(mvm->hw,
+ iwl_mvm_mac_sta_hw_queues_iter,
+ &data);
+
+ /*
+ * Some TDLS stations may be removed but are in the process of being
+ * drained. Don't touch their queues.
+ */
+ for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT)
+ data.used_hw_queues |= mvm->tfd_drained[sta_id];
+
+ return data.used_hw_queues;
+}
+
+static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_mac_iface_iterator_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ /* Iterator may already find the interface being added -- skip it */
+ if (vif == data->vif) {
+ data->found_vif = true;
+ return;
+ }
+
+ /* Mark MAC IDs as used by clearing the available bit, and
+ * (below) mark TSFs as used if their existing use is not
+ * compatible with the new interface type.
+ * No locking or atomic bit operations are needed since the
+ * data is on the stack of the caller function.
+ */
+ __clear_bit(mvmvif->id, data->available_mac_ids);
+
+ /* find a suitable tsf_id */
+ iwl_mvm_mac_tsf_id_iter(_data, mac, vif);
+}
+
+void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_mac_iface_iterator_data data = {
+ .mvm = mvm,
+ .vif = vif,
+ .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
+ /* no preference yet */
+ .preferred_tsf = NUM_TSF_IDS,
+ };
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ iwl_mvm_mac_tsf_id_iter, &data);
+
+ if (data.preferred_tsf != NUM_TSF_IDS)
+ mvmvif->tsf_id = data.preferred_tsf;
+ else if (!test_bit(mvmvif->tsf_id, data.available_tsf_ids))
+ mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
+ NUM_TSF_IDS);
+}
+
+static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_mac_iface_iterator_data data = {
+ .mvm = mvm,
+ .vif = vif,
+ .available_mac_ids = { (1 << NUM_MAC_INDEX_DRIVER) - 1 },
+ .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
+ /* no preference yet */
+ .preferred_tsf = NUM_TSF_IDS,
+ .found_vif = false,
+ };
+ u32 ac;
+ int ret, i;
+ unsigned long used_hw_queues;
+
+ /*
+ * Allocate a MAC ID and a TSF for this MAC, along with the queues
+ * and other resources.
+ */
+
+ /*
+ * Before the iterator, we start with all MAC IDs and TSFs available.
+ *
+ * During iteration, all MAC IDs are cleared that are in use by other
+ * virtual interfaces, and all TSF IDs are cleared that can't be used
+ * by this new virtual interface because they're used by an interface
+ * that can't share it with the new one.
+ * At the same time, we check if there's a preferred TSF in the case
+ * that we should share it with another interface.
+ */
+
+ /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */
+ switch (vif->type) {
+ case NL80211_IFTYPE_ADHOC:
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!vif->p2p)
+ break;
+ /* fall through */
+ default:
+ __clear_bit(0, data.available_mac_ids);
+ }
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ iwl_mvm_mac_iface_iterator, &data);
+
+ used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif);
+
+ /*
+ * In the case we're getting here during resume, it's similar to
+ * firmware restart, and with RESUME_ALL the iterator will find
+ * the vif being added already.
+ * We don't want to reassign any IDs in either case since doing
+ * so would probably assign different IDs (as interfaces aren't
+ * necessarily added in the same order), but the old IDs were
+ * preserved anyway, so skip ID assignment for both resume and
+ * recovery.
+ */
+ if (data.found_vif)
+ return 0;
+
+ /* Therefore, in recovery, we can't get here */
+ if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
+ return -EBUSY;
+
+ mvmvif->id = find_first_bit(data.available_mac_ids,
+ NUM_MAC_INDEX_DRIVER);
+ if (mvmvif->id == NUM_MAC_INDEX_DRIVER) {
+ IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n");
+ ret = -EIO;
+ goto exit_fail;
+ }
+
+ if (data.preferred_tsf != NUM_TSF_IDS)
+ mvmvif->tsf_id = data.preferred_tsf;
+ else
+ mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
+ NUM_TSF_IDS);
+ if (mvmvif->tsf_id == NUM_TSF_IDS) {
+ IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n");
+ ret = -EIO;
+ goto exit_fail;
+ }
+
+ mvmvif->color = 0;
+
+ INIT_LIST_HEAD(&mvmvif->time_event_data.list);
+ mvmvif->time_event_data.id = TE_MAX;
+
+ /* No need to allocate data queues to P2P Device MAC.*/
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
+
+ return 0;
+ }
+
+ /* Find available queues, and allocate them to the ACs */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ u8 queue = find_first_zero_bit(&used_hw_queues,
+ mvm->first_agg_queue);
+
+ if (queue >= mvm->first_agg_queue) {
+ IWL_ERR(mvm, "Failed to allocate queue\n");
+ ret = -EIO;
+ goto exit_fail;
+ }
+
+ __set_bit(queue, &used_hw_queues);
+ vif->hw_queue[ac] = queue;
+ }
+
+ /* Allocate the CAB queue for softAP and GO interfaces */
+ if (vif->type == NL80211_IFTYPE_AP) {
+ u8 queue = find_first_zero_bit(&used_hw_queues,
+ mvm->first_agg_queue);
+
+ if (queue >= mvm->first_agg_queue) {
+ IWL_ERR(mvm, "Failed to allocate cab queue\n");
+ ret = -EIO;
+ goto exit_fail;
+ }
+
+ vif->cab_queue = queue;
+ } else {
+ vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+ }
+
+ mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT;
+ mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+ for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++)
+ mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC;
+
+ return 0;
+
+exit_fail:
+ memset(mvmvif, 0, sizeof(struct iwl_mvm_vif));
+ memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue));
+ vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+ return ret;
+}
+
+int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ unsigned int wdg_timeout =
+ iwl_mvm_get_wd_timeout(mvm, vif, false, false);
+ u32 ac;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif);
+ if (ret)
+ return ret;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE,
+ IWL_MVM_OFFCHANNEL_QUEUE,
+ IWL_MVM_TX_FIFO_VO, 0, wdg_timeout);
+ break;
+ case NL80211_IFTYPE_AP:
+ iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue,
+ IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout);
+ /* fall through */
+ default:
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac],
+ vif->hw_queue[ac],
+ iwl_mvm_ac_to_tx_fifo[ac], 0,
+ wdg_timeout);
+ break;
+ }
+
+ return 0;
+}
+
+void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ int ac;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE,
+ IWL_MVM_OFFCHANNEL_QUEUE, IWL_MAX_TID_COUNT,
+ 0);
+ break;
+ case NL80211_IFTYPE_AP:
+ iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue,
+ IWL_MAX_TID_COUNT, 0);
+ /* fall through */
+ default:
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ iwl_mvm_disable_txq(mvm, vif->hw_queue[ac],
+ vif->hw_queue[ac],
+ IWL_MAX_TID_COUNT, 0);
+ }
+}
+
+static void iwl_mvm_ack_rates(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ enum ieee80211_band band,
+ u8 *cck_rates, u8 *ofdm_rates)
+{
+ struct ieee80211_supported_band *sband;
+ unsigned long basic = vif->bss_conf.basic_rates;
+ int lowest_present_ofdm = 100;
+ int lowest_present_cck = 100;
+ u8 cck = 0;
+ u8 ofdm = 0;
+ int i;
+
+ sband = mvm->hw->wiphy->bands[band];
+
+ for_each_set_bit(i, &basic, BITS_PER_LONG) {
+ int hw = sband->bitrates[i].hw_value;
+ if (hw >= IWL_FIRST_OFDM_RATE) {
+ ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
+ if (lowest_present_ofdm > hw)
+ lowest_present_ofdm = hw;
+ } else {
+ BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+ cck |= BIT(hw);
+ if (lowest_present_cck > hw)
+ lowest_present_cck = hw;
+ }
+ }
+
+ /*
+ * Now we've got the basic rates as bitmaps in the ofdm and cck
+ * variables. This isn't sufficient though, as there might not
+ * be all the right rates in the bitmap. E.g. if the only basic
+ * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
+ * and 6 Mbps because the 802.11-2007 standard says in 9.6:
+ *
+ * [...] a STA responding to a received frame shall transmit
+ * its Control Response frame [...] at the highest rate in the
+ * BSSBasicRateSet parameter that is less than or equal to the
+ * rate of the immediately previous frame in the frame exchange
+ * sequence ([...]) and that is of the same modulation class
+ * ([...]) as the received frame. If no rate contained in the
+ * BSSBasicRateSet parameter meets these conditions, then the
+ * control frame sent in response to a received frame shall be
+ * transmitted at the highest mandatory rate of the PHY that is
+ * less than or equal to the rate of the received frame, and
+ * that is of the same modulation class as the received frame.
+ *
+ * As a consequence, we need to add all mandatory rates that are
+ * lower than all of the basic rates to these bitmaps.
+ */
+
+ if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
+ ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
+ if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
+ ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
+ /* 6M already there or needed so always add */
+ ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
+
+ /*
+ * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
+ * Note, however:
+ * - if no CCK rates are basic, it must be ERP since there must
+ * be some basic rates at all, so they're OFDM => ERP PHY
+ * (or we're in 5 GHz, and the cck bitmap will never be used)
+ * - if 11M is a basic rate, it must be ERP as well, so add 5.5M
+ * - if 5.5M is basic, 1M and 2M are mandatory
+ * - if 2M is basic, 1M is mandatory
+ * - if 1M is basic, that's the only valid ACK rate.
+ * As a consequence, it's not as complicated as it sounds, just add
+ * any lower rates to the ACK rate bitmap.
+ */
+ if (IWL_RATE_11M_INDEX < lowest_present_cck)
+ cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE;
+ if (IWL_RATE_5M_INDEX < lowest_present_cck)
+ cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE;
+ if (IWL_RATE_2M_INDEX < lowest_present_cck)
+ cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE;
+ /* 1M already there or needed so always add */
+ cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE;
+
+ *cck_rates = cck;
+ *ofdm_rates = ofdm;
+}
+
+static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_ctx_cmd *cmd)
+{
+ /* for both sta and ap, ht_operation_mode hold the protection_mode */
+ u8 protection_mode = vif->bss_conf.ht_operation_mode &
+ IEEE80211_HT_OP_MODE_PROTECTION;
+ /* The fw does not distinguish between ht and fat */
+ u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT;
+
+ IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode);
+ /*
+ * See section 9.23.3.1 of IEEE 80211-2012.
+ * Nongreenfield HT STAs Present is not supported.
+ */
+ switch (protection_mode) {
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+ cmd->protection_flags |= cpu_to_le32(ht_flag);
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+ /* Protect when channel wider than 20MHz */
+ if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20)
+ cmd->protection_flags |= cpu_to_le32(ht_flag);
+ break;
+ default:
+ IWL_ERR(mvm, "Illegal protection mode %d\n",
+ protection_mode);
+ break;
+ }
+}
+
+static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_ctx_cmd *cmd,
+ const u8 *bssid_override,
+ u32 action)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_chanctx_conf *chanctx;
+ bool ht_enabled = !!(vif->bss_conf.ht_operation_mode &
+ IEEE80211_HT_OP_MODE_PROTECTION);
+ u8 cck_ack_rates, ofdm_ack_rates;
+ const u8 *bssid = bssid_override ?: vif->bss_conf.bssid;
+ int i;
+
+ cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color));
+ cmd->action = cpu_to_le32(action);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ if (vif->p2p)
+ cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA);
+ else
+ cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA);
+ break;
+ case NL80211_IFTYPE_AP:
+ cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER);
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id);
+
+ memcpy(cmd->node_addr, vif->addr, ETH_ALEN);
+
+ if (bssid)
+ memcpy(cmd->bssid_addr, bssid, ETH_ALEN);
+ else
+ eth_broadcast_addr(cmd->bssid_addr);
+
+ rcu_read_lock();
+ chanctx = rcu_dereference(vif->chanctx_conf);
+ iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band
+ : IEEE80211_BAND_2GHZ,
+ &cck_ack_rates, &ofdm_ack_rates);
+ rcu_read_unlock();
+
+ cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates);
+ cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates);
+
+ cmd->cck_short_preamble =
+ cpu_to_le32(vif->bss_conf.use_short_preamble ?
+ MAC_FLG_SHORT_PREAMBLE : 0);
+ cmd->short_slot =
+ cpu_to_le32(vif->bss_conf.use_short_slot ?
+ MAC_FLG_SHORT_SLOT : 0);
+
+ cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP);
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ u8 txf = iwl_mvm_ac_to_tx_fifo[i];
+
+ cmd->ac[txf].cw_min =
+ cpu_to_le16(mvmvif->queue_params[i].cw_min);
+ cmd->ac[txf].cw_max =
+ cpu_to_le16(mvmvif->queue_params[i].cw_max);
+ cmd->ac[txf].edca_txop =
+ cpu_to_le16(mvmvif->queue_params[i].txop * 32);
+ cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs;
+ cmd->ac[txf].fifos_mask = BIT(txf);
+ }
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ /* in AP mode, the MCAST FIFO takes the EDCA params from VO */
+ cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |=
+ BIT(IWL_MVM_TX_FIFO_MCAST);
+
+ /*
+ * in AP mode, pass probe requests and beacons from other APs
+ * (needed for ht protection); when there're no any associated
+ * station don't ask FW to pass beacons to prevent unnecessary
+ * wake-ups.
+ */
+ cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+ if (mvmvif->ap_assoc_sta_count) {
+ cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+ IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n");
+ } else {
+ IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
+ }
+ }
+
+ if (vif->bss_conf.qos)
+ cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
+
+ if (vif->bss_conf.use_cts_prot)
+ cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
+
+ IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n",
+ vif->bss_conf.use_cts_prot,
+ vif->bss_conf.ht_operation_mode);
+ if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
+ cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
+ if (ht_enabled)
+ iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd);
+}
+
+static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
+ struct iwl_mac_ctx_cmd *cmd)
+{
+ int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
+ sizeof(*cmd), cmd);
+ if (ret)
+ IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n",
+ le32_to_cpu(cmd->action), ret);
+ return ret;
+}
+
+static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 action, bool force_assoc_off,
+ const u8 *bssid_override)
+{
+ struct iwl_mac_ctx_cmd cmd = {};
+ struct iwl_mac_data_sta *ctxt_sta;
+
+ WARN_ON(vif->type != NL80211_IFTYPE_STATION);
+
+ /* Fill the common data for all mac context types */
+ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, bssid_override, action);
+
+ if (vif->p2p) {
+ struct ieee80211_p2p_noa_attr *noa =
+ &vif->bss_conf.p2p_noa_attr;
+
+ cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow &
+ IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
+ ctxt_sta = &cmd.p2p_sta.sta;
+ } else {
+ ctxt_sta = &cmd.sta;
+ }
+
+ /* We need the dtim_period to set the MAC as associated */
+ if (vif->bss_conf.assoc && vif->bss_conf.dtim_period &&
+ !force_assoc_off) {
+ u32 dtim_offs;
+
+ /*
+ * The DTIM count counts down, so when it is N that means N
+ * more beacon intervals happen until the DTIM TBTT. Therefore
+ * add this to the current time. If that ends up being in the
+ * future, the firmware will handle it.
+ *
+ * Also note that the system_timestamp (which we get here as
+ * "sync_device_ts") and TSF timestamp aren't at exactly the
+ * same offset in the frame -- the TSF is at the first symbol
+ * of the TSF, the system timestamp is at signal acquisition
+ * time. This means there's an offset between them of at most
+ * a few hundred microseconds (24 * 8 bits + PLCP time gives
+ * 384us in the longest case), this is currently not relevant
+ * as the firmware wakes up around 2ms before the TBTT.
+ */
+ dtim_offs = vif->bss_conf.sync_dtim_count *
+ vif->bss_conf.beacon_int;
+ /* convert TU to usecs */
+ dtim_offs *= 1024;
+
+ ctxt_sta->dtim_tsf =
+ cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs);
+ ctxt_sta->dtim_time =
+ cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs);
+
+ IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n",
+ le64_to_cpu(ctxt_sta->dtim_tsf),
+ le32_to_cpu(ctxt_sta->dtim_time),
+ dtim_offs);
+
+ ctxt_sta->is_assoc = cpu_to_le32(1);
+ } else {
+ ctxt_sta->is_assoc = cpu_to_le32(0);
+
+ /* Allow beacons to pass through as long as we are not
+ * associated, or we do not have dtim period information.
+ */
+ cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+ }
+
+ ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int);
+ ctxt_sta->bi_reciprocal =
+ cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+ ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
+ vif->bss_conf.dtim_period);
+ ctxt_sta->dtim_reciprocal =
+ cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
+ vif->bss_conf.dtim_period));
+
+ ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
+ ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
+
+ if (vif->probe_req_reg && vif->bss_conf.assoc && vif->p2p)
+ cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+
+ return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 action)
+{
+ struct iwl_mac_ctx_cmd cmd = {};
+ u32 tfd_queue_msk = 0;
+ int ret, i;
+
+ WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
+
+ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+ tfd_queue_msk |= BIT(vif->hw_queue[i]);
+
+ cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC |
+ MAC_FILTER_IN_CONTROL_AND_MGMT |
+ MAC_FILTER_IN_BEACON |
+ MAC_FILTER_IN_PROBE_REQUEST |
+ MAC_FILTER_IN_CRC32);
+ ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS);
+
+ /* Allocate sniffer station */
+ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->snif_sta, tfd_queue_msk,
+ vif->type);
+ if (ret)
+ return ret;
+
+ return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 action)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mac_ctx_cmd cmd = {};
+
+ WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
+
+ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
+
+ cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON |
+ MAC_FILTER_IN_PROBE_REQUEST);
+
+ /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */
+ cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int);
+ cmd.ibss.bi_reciprocal =
+ cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+
+ /* TODO: Assumes that the beacon id == mac context id */
+ cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id);
+
+ return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+struct iwl_mvm_go_iterator_data {
+ bool go_active;
+};
+
+static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_go_iterator_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (vif->type == NL80211_IFTYPE_AP && vif->p2p &&
+ mvmvif->ap_ibss_active)
+ data->go_active = true;
+}
+
+static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 action)
+{
+ struct iwl_mac_ctx_cmd cmd = {};
+ struct iwl_mvm_go_iterator_data data = {};
+
+ WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
+
+ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
+
+ cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
+
+ /* Override the filter flags to accept only probe requests */
+ cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+
+ /*
+ * This flag should be set to true when the P2P Device is
+ * discoverable and there is at least another active P2P GO. Settings
+ * this flag will allow the P2P Device to be discoverable on other
+ * channels in addition to its listen channel.
+ * Note that this flag should not be set in other cases as it opens the
+ * Rx filters on all MAC and increases the number of interrupts.
+ */
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ iwl_mvm_go_iterator, &data);
+
+ cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0);
+ return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
+ struct iwl_mac_beacon_cmd *beacon_cmd,
+ u8 *beacon, u32 frame_size)
+{
+ u32 tim_idx;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
+
+ /* The index is relative to frame start but we start looking at the
+ * variable-length part of the beacon. */
+ tim_idx = mgmt->u.beacon.variable - beacon;
+
+ /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+ while ((tim_idx < (frame_size - 2)) &&
+ (beacon[tim_idx] != WLAN_EID_TIM))
+ tim_idx += beacon[tim_idx+1] + 2;
+
+ /* If TIM field was found, set variables */
+ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
+ beacon_cmd->tim_idx = cpu_to_le32(tim_idx);
+ beacon_cmd->tim_size = cpu_to_le32((u32)beacon[tim_idx+1]);
+ } else {
+ IWL_WARN(mvm, "Unable to find TIM Element in beacon\n");
+ }
+}
+
+static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct sk_buff *beacon)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_host_cmd cmd = {
+ .id = BEACON_TEMPLATE_CMD,
+ .flags = CMD_ASYNC,
+ };
+ struct iwl_mac_beacon_cmd beacon_cmd = {};
+ struct ieee80211_tx_info *info;
+ u32 beacon_skb_len;
+ u32 rate, tx_flags;
+
+ if (WARN_ON(!beacon))
+ return -EINVAL;
+
+ beacon_skb_len = beacon->len;
+
+ /* TODO: for now the beacon template id is set to be the mac context id.
+ * Might be better to handle it as another resource ... */
+ beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
+ info = IEEE80211_SKB_CB(beacon);
+
+ /* Set up TX command fields */
+ beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len);
+ beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id;
+ beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+ tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;
+ tx_flags |=
+ iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) <<
+ TX_CMD_FLG_BT_PRIO_POS;
+ beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
+ mvm->mgmt_last_antenna_idx =
+ iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
+ mvm->mgmt_last_antenna_idx);
+ }
+
+ beacon_cmd.tx.rate_n_flags =
+ cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
+ RATE_MCS_ANT_POS);
+
+ if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) {
+ rate = IWL_FIRST_OFDM_RATE;
+ } else {
+ rate = IWL_FIRST_CCK_RATE;
+ beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK);
+ }
+ beacon_cmd.tx.rate_n_flags |=
+ cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
+
+ /* Set up TX beacon command fields */
+ if (vif->type == NL80211_IFTYPE_AP)
+ iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
+ beacon->data,
+ beacon_skb_len);
+
+ /* Submit command */
+ cmd.len[0] = sizeof(beacon_cmd);
+ cmd.data[0] = &beacon_cmd;
+ cmd.dataflags[0] = 0;
+ cmd.len[1] = beacon_skb_len;
+ cmd.data[1] = beacon->data;
+ cmd.dataflags[1] = IWL_HCMD_DFL_DUP;
+
+ return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+/* The beacon template for the AP/GO/IBSS has changed and needs update */
+int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct sk_buff *beacon;
+ int ret;
+
+ WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_ADHOC);
+
+ beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL);
+ if (!beacon)
+ return -ENOMEM;
+
+ ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon);
+ dev_kfree_skb(beacon);
+ return ret;
+}
+
+struct iwl_mvm_mac_ap_iterator_data {
+ struct iwl_mvm *mvm;
+ struct ieee80211_vif *vif;
+ u32 beacon_device_ts;
+ u16 beacon_int;
+};
+
+/* Find the beacon_device_ts and beacon_int for a managed interface */
+static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_mac_ap_iterator_data *data = _data;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
+ return;
+
+ /* Station client has higher priority over P2P client*/
+ if (vif->p2p && data->beacon_device_ts)
+ return;
+
+ data->beacon_device_ts = vif->bss_conf.sync_device_ts;
+ data->beacon_int = vif->bss_conf.beacon_int;
+}
+
+/*
+ * Fill the specific data for mac context of type AP of P2P GO
+ */
+static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_data_ap *ctxt_ap,
+ bool add)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_mac_ap_iterator_data data = {
+ .mvm = mvm,
+ .vif = vif,
+ .beacon_device_ts = 0
+ };
+
+ ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
+ ctxt_ap->bi_reciprocal =
+ cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+ ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
+ vif->bss_conf.dtim_period);
+ ctxt_ap->dtim_reciprocal =
+ cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
+ vif->bss_conf.dtim_period));
+
+ ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
+
+ /*
+ * Only set the beacon time when the MAC is being added, when we
+ * just modify the MAC then we should keep the time -- the firmware
+ * can otherwise have a "jumping" TBTT.
+ */
+ if (add) {
+ /*
+ * If there is a station/P2P client interface which is
+ * associated, set the AP's TBTT far enough from the station's
+ * TBTT. Otherwise, set it to the current system time
+ */
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ iwl_mvm_mac_ap_iterator, &data);
+
+ if (data.beacon_device_ts) {
+ u32 rand = (prandom_u32() % (64 - 36)) + 36;
+ mvmvif->ap_beacon_time = data.beacon_device_ts +
+ ieee80211_tu_to_usec(data.beacon_int * rand /
+ 100);
+ } else {
+ mvmvif->ap_beacon_time =
+ iwl_read_prph(mvm->trans,
+ DEVICE_SYSTEM_TIME_REG);
+ }
+ }
+
+ ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time);
+ ctxt_ap->beacon_tsf = 0; /* unused */
+
+ /* TODO: Assume that the beacon id == mac context id */
+ ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 action)
+{
+ struct iwl_mac_ctx_cmd cmd = {};
+
+ WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p);
+
+ /* Fill the common data for all mac context types */
+ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
+
+ /* Fill the data specific for ap mode */
+ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap,
+ action == FW_CTXT_ACTION_ADD);
+
+ return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 action)
+{
+ struct iwl_mac_ctx_cmd cmd = {};
+ struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr;
+
+ WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p);
+
+ /* Fill the common data for all mac context types */
+ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
+
+ /* Fill the data specific for GO mode */
+ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap,
+ action == FW_CTXT_ACTION_ADD);
+
+ cmd.go.ctwin = cpu_to_le32(noa->oppps_ctwindow &
+ IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
+ cmd.go.opp_ps_enabled =
+ cpu_to_le32(!!(noa->oppps_ctwindow &
+ IEEE80211_P2P_OPPPS_ENABLE_BIT));
+
+ return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ u32 action, bool force_assoc_off,
+ const u8 *bssid_override)
+{
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action,
+ force_assoc_off,
+ bssid_override);
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!vif->p2p)
+ return iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, action);
+ else
+ return iwl_mvm_mac_ctxt_cmd_go(mvm, vif, action);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action);
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action);
+ case NL80211_IFTYPE_ADHOC:
+ return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n",
+ vif->addr, ieee80211_vif_type_p2p(vif)))
+ return -EIO;
+
+ ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD,
+ true, NULL);
+ if (ret)
+ return ret;
+
+ /* will only do anything at resume from D3 time */
+ iwl_mvm_set_last_nonqos_seq(mvm, vif);
+
+ mvmvif->uploaded = true;
+ return 0;
+}
+
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool force_assoc_off, const u8 *bssid_override)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n",
+ vif->addr, ieee80211_vif_type_p2p(vif)))
+ return -EIO;
+
+ return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY,
+ force_assoc_off, bssid_override);
+}
+
+int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mac_ctx_cmd cmd;
+ int ret;
+
+ if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n",
+ vif->addr, ieee80211_vif_type_p2p(vif)))
+ return -EIO;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color));
+ cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
+ sizeof(cmd), &cmd);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret);
+ return ret;
+ }
+
+ mvmvif->uploaded = false;
+
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+ __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags);
+ iwl_mvm_dealloc_snif_sta(mvm);
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
+ struct ieee80211_vif *csa_vif, u32 gp2,
+ bool tx_success)
+{
+ struct iwl_mvm_vif *mvmvif =
+ iwl_mvm_vif_from_mac80211(csa_vif);
+
+ /* Don't start to countdown from a failed beacon */
+ if (!tx_success && !mvmvif->csa_countdown)
+ return;
+
+ mvmvif->csa_countdown = true;
+
+ if (!ieee80211_csa_is_complete(csa_vif)) {
+ int c = ieee80211_csa_update_counter(csa_vif);
+
+ iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
+ if (csa_vif->p2p &&
+ !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 &&
+ tx_success) {
+ u32 rel_time = (c + 1) *
+ csa_vif->bss_conf.beacon_int -
+ IWL_MVM_CHANNEL_SWITCH_TIME_GO;
+ u32 apply_time = gp2 + rel_time * 1024;
+
+ iwl_mvm_schedule_csa_period(mvm, csa_vif,
+ IWL_MVM_CHANNEL_SWITCH_TIME_GO -
+ IWL_MVM_CHANNEL_SWITCH_MARGIN,
+ apply_time);
+ }
+ } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) {
+ /* we don't have CSA NoA scheduled yet, switch now */
+ ieee80211_csa_finish(csa_vif);
+ RCU_INIT_POINTER(mvm->csa_vif, NULL);
+ }
+}
+
+void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
+ struct iwl_mvm_tx_resp *beacon_notify_hdr;
+ struct ieee80211_vif *csa_vif;
+ struct ieee80211_vif *tx_blocked_vif;
+ u16 status;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ beacon_notify_hdr = &beacon->beacon_notify_hdr;
+ mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
+
+ status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK;
+ IWL_DEBUG_RX(mvm,
+ "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
+ status, beacon_notify_hdr->failure_frame,
+ le64_to_cpu(beacon->tsf),
+ mvm->ap_last_beacon_gp2,
+ le32_to_cpu(beacon_notify_hdr->initial_rate));
+
+ csa_vif = rcu_dereference_protected(mvm->csa_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (unlikely(csa_vif && csa_vif->csa_active))
+ iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2,
+ (status == TX_STATUS_SUCCESS));
+
+ tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (unlikely(tx_blocked_vif)) {
+ struct iwl_mvm_vif *mvmvif =
+ iwl_mvm_vif_from_mac80211(tx_blocked_vif);
+
+ /*
+ * The channel switch is started and we have blocked the
+ * stations. If this is the first beacon (the timeout wasn't
+ * set), set the unblock timeout, otherwise countdown
+ */
+ if (!mvm->csa_tx_block_bcn_timeout)
+ mvm->csa_tx_block_bcn_timeout =
+ IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
+ else
+ mvm->csa_tx_block_bcn_timeout--;
+
+ /* Check if the timeout is expired, and unblock tx */
+ if (mvm->csa_tx_block_bcn_timeout == 0) {
+ iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
+ RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
+ }
+ }
+}
+
+static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_missed_beacons_notif *missed_beacons = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig;
+ struct iwl_fw_dbg_trigger_tlv *trigger;
+ u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx;
+ u32 rx_missed_bcon, rx_missed_bcon_since_rx;
+
+ if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id))
+ return;
+
+ rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons);
+ rx_missed_bcon_since_rx =
+ le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx);
+ /*
+ * TODO: the threshold should be adjusted based on latency conditions,
+ * and/or in case of a CS flow on one of the other AP vifs.
+ */
+ if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) >
+ IWL_MVM_MISSED_BEACONS_THRESHOLD)
+ ieee80211_beacon_loss(vif);
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw,
+ FW_DBG_TRIGGER_MISSED_BEACONS))
+ return;
+
+ trigger = iwl_fw_dbg_get_trigger(mvm->fw,
+ FW_DBG_TRIGGER_MISSED_BEACONS);
+ bcon_trig = (void *)trigger->data;
+ stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon);
+ stop_trig_missed_bcon_since_rx =
+ le32_to_cpu(bcon_trig->stop_consec_missed_bcon_since_rx);
+
+ /* TODO: implement start trigger */
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
+ return;
+
+ if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx ||
+ rx_missed_bcon >= stop_trig_missed_bcon)
+ iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
+}
+
+void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_missed_beacons_notif *mb = (void *)pkt->data;
+
+ IWL_DEBUG_INFO(mvm,
+ "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n",
+ le32_to_cpu(mb->mac_id),
+ le32_to_cpu(mb->consec_missed_beacons),
+ le32_to_cpu(mb->consec_missed_beacons_since_last_rx),
+ le32_to_cpu(mb->num_recvd_beacons),
+ le32_to_cpu(mb->num_expected_beacons));
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_beacon_loss_iterator,
+ mb);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
new file mode 100644
index 000000000000..d70a1716f3e0
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -0,0 +1,3979 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/devcoredump.h>
+#include <linux/time.h>
+#include <net/mac80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <net/tcp.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-io.h"
+#include "mvm.h"
+#include "sta.h"
+#include "time-event.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-phy-db.h"
+#include "testmode.h"
+#include "iwl-fw-error-dump.h"
+#include "iwl-prph.h"
+#include "iwl-csr.h"
+#include "iwl-nvm-parse.h"
+#include "fw-dbg.h"
+
+static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+ },
+};
+
+static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
+ {
+ .num_different_channels = 2,
+ .max_interfaces = 3,
+ .limits = iwl_mvm_limits,
+ .n_limits = ARRAY_SIZE(iwl_mvm_limits),
+ },
+};
+
+#ifdef CONFIG_PM_SLEEP
+static const struct nl80211_wowlan_tcp_data_token_feature
+iwl_mvm_wowlan_tcp_token_feature = {
+ .min_len = 0,
+ .max_len = 255,
+ .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS,
+};
+
+static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
+ .tok = &iwl_mvm_wowlan_tcp_token_feature,
+ .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN -
+ sizeof(struct ethhdr) -
+ sizeof(struct iphdr) -
+ sizeof(struct tcphdr),
+ .data_interval_max = 65535, /* __le16 in API */
+ .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN -
+ sizeof(struct ethhdr) -
+ sizeof(struct iphdr) -
+ sizeof(struct tcphdr),
+ .seq = true,
+};
+#endif
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+/*
+ * Use the reserved field to indicate magic values.
+ * these values will only be used internally by the driver,
+ * and won't make it to the fw (reserved will be 0).
+ * BC_FILTER_MAGIC_IP - configure the val of this attribute to
+ * be the vif's ip address. in case there is not a single
+ * ip address (0, or more than 1), this attribute will
+ * be skipped.
+ * BC_FILTER_MAGIC_MAC - set the val of this attribute to
+ * the LSB bytes of the vif's mac address
+ */
+enum {
+ BC_FILTER_MAGIC_NONE = 0,
+ BC_FILTER_MAGIC_IP,
+ BC_FILTER_MAGIC_MAC,
+};
+
+static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
+ {
+ /* arp */
+ .discard = 0,
+ .frame_type = BCAST_FILTER_FRAME_TYPE_ALL,
+ .attrs = {
+ {
+ /* frame type - arp, hw type - ethernet */
+ .offset_type =
+ BCAST_FILTER_OFFSET_PAYLOAD_START,
+ .offset = sizeof(rfc1042_header),
+ .val = cpu_to_be32(0x08060001),
+ .mask = cpu_to_be32(0xffffffff),
+ },
+ {
+ /* arp dest ip */
+ .offset_type =
+ BCAST_FILTER_OFFSET_PAYLOAD_START,
+ .offset = sizeof(rfc1042_header) + 2 +
+ sizeof(struct arphdr) +
+ ETH_ALEN + sizeof(__be32) +
+ ETH_ALEN,
+ .mask = cpu_to_be32(0xffffffff),
+ /* mark it as special field */
+ .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP),
+ },
+ },
+ },
+ {
+ /* dhcp offer bcast */
+ .discard = 0,
+ .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4,
+ .attrs = {
+ {
+ /* udp dest port - 68 (bootp client)*/
+ .offset_type = BCAST_FILTER_OFFSET_IP_END,
+ .offset = offsetof(struct udphdr, dest),
+ .val = cpu_to_be32(0x00440000),
+ .mask = cpu_to_be32(0xffff0000),
+ },
+ {
+ /* dhcp - lsb bytes of client hw address */
+ .offset_type = BCAST_FILTER_OFFSET_IP_END,
+ .offset = 38,
+ .mask = cpu_to_be32(0xffffffff),
+ /* mark it as special field */
+ .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC),
+ },
+ },
+ },
+ /* last filter must be empty */
+ {},
+};
+#endif
+
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return;
+
+ IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
+ spin_lock_bh(&mvm->refs_lock);
+ mvm->refs[ref_type]++;
+ spin_unlock_bh(&mvm->refs_lock);
+ iwl_trans_ref(mvm->trans);
+}
+
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return;
+
+ IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
+ spin_lock_bh(&mvm->refs_lock);
+ WARN_ON(!mvm->refs[ref_type]--);
+ spin_unlock_bh(&mvm->refs_lock);
+ iwl_trans_unref(mvm->trans);
+}
+
+static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
+ enum iwl_mvm_ref_type except_ref)
+{
+ int i, j;
+
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return;
+
+ spin_lock_bh(&mvm->refs_lock);
+ for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+ if (except_ref == i || !mvm->refs[i])
+ continue;
+
+ IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n",
+ i, mvm->refs[i]);
+ for (j = 0; j < mvm->refs[i]; j++)
+ iwl_trans_unref(mvm->trans);
+ mvm->refs[i] = 0;
+ }
+ spin_unlock_bh(&mvm->refs_lock);
+}
+
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
+{
+ int i;
+ bool taken = false;
+
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return true;
+
+ spin_lock_bh(&mvm->refs_lock);
+ for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+ if (mvm->refs[i]) {
+ taken = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&mvm->refs_lock);
+
+ return taken;
+}
+
+int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+ iwl_mvm_ref(mvm, ref_type);
+
+ if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+ !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status),
+ HZ)) {
+ WARN_ON_ONCE(1);
+ iwl_mvm_unref(mvm, ref_type);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
+{
+ int i;
+
+ memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts));
+ for (i = 0; i < NUM_PHY_CTX; i++) {
+ mvm->phy_ctxts[i].id = i;
+ mvm->phy_ctxts[i].ref = 0;
+ }
+}
+
+struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
+ const char *alpha2,
+ enum iwl_mcc_source src_id,
+ bool *changed)
+{
+ struct ieee80211_regdomain *regd = NULL;
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mcc_update_resp *resp;
+
+ IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ resp = iwl_mvm_update_mcc(mvm, alpha2, src_id);
+ if (IS_ERR_OR_NULL(resp)) {
+ IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n",
+ PTR_ERR_OR_ZERO(resp));
+ goto out;
+ }
+
+ if (changed)
+ *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE);
+
+ regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
+ __le32_to_cpu(resp->n_channels),
+ resp->channels,
+ __le16_to_cpu(resp->mcc));
+ /* Store the return source id */
+ src_id = resp->source_id;
+ kfree(resp);
+ if (IS_ERR_OR_NULL(regd)) {
+ IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n",
+ PTR_ERR_OR_ZERO(regd));
+ goto out;
+ }
+
+ IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
+ regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id);
+ mvm->lar_regdom_set = true;
+ mvm->mcc_src = src_id;
+
+out:
+ return regd;
+}
+
+void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm)
+{
+ bool changed;
+ struct ieee80211_regdomain *regd;
+
+ if (!iwl_mvm_is_lar_supported(mvm))
+ return;
+
+ regd = iwl_mvm_get_current_regdomain(mvm, &changed);
+ if (!IS_ERR_OR_NULL(regd)) {
+ /* only update the regulatory core if changed */
+ if (changed)
+ regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+
+ kfree(regd);
+ }
+}
+
+struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
+ bool *changed)
+{
+ return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ",
+ iwl_mvm_is_wifi_mcc_supported(mvm) ?
+ MCC_SOURCE_GET_CURRENT :
+ MCC_SOURCE_OLD_FW, changed);
+}
+
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
+{
+ enum iwl_mcc_source used_src;
+ struct ieee80211_regdomain *regd;
+ int ret;
+ bool changed;
+ const struct ieee80211_regdomain *r =
+ rtnl_dereference(mvm->hw->wiphy->regd);
+
+ if (!r)
+ return -ENOENT;
+
+ /* save the last source in case we overwrite it below */
+ used_src = mvm->mcc_src;
+ if (iwl_mvm_is_wifi_mcc_supported(mvm)) {
+ /* Notify the firmware we support wifi location updates */
+ regd = iwl_mvm_get_current_regdomain(mvm, NULL);
+ if (!IS_ERR_OR_NULL(regd))
+ kfree(regd);
+ }
+
+ /* Now set our last stored MCC and source */
+ regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src,
+ &changed);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+
+ /* update cfg80211 if the regdomain was changed */
+ if (changed)
+ ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+ else
+ ret = 0;
+
+ kfree(regd);
+ return ret;
+}
+
+int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
+{
+ struct ieee80211_hw *hw = mvm->hw;
+ int num_mac, ret, i;
+ static const u32 mvm_ciphers[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ };
+
+ /* Tell mac80211 our characteristics */
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, SPECTRUM_MGMT);
+ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+ ieee80211_hw_set(hw, QUEUE_CONTROL);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, TIMING_BEACON_ONLY);
+ ieee80211_hw_set(hw, CONNECTION_MONITOR);
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+ ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
+
+ if (mvm->trans->max_skb_frags)
+ hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
+
+ hw->queues = mvm->first_agg_queue;
+ hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
+ hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
+ IEEE80211_RADIOTAP_MCS_HAVE_STBC;
+ hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
+ hw->rate_control_algorithm = "iwl-mvm-rs";
+ hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
+ hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
+
+ BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 2);
+ memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
+ hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers);
+ hw->wiphy->cipher_suites = mvm->ciphers;
+
+ /*
+ * Enable 11w if advertised by firmware and software crypto
+ * is not enabled (as the firmware will interpret some mgmt
+ * packets, so enabling it with software crypto isn't safe)
+ */
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP &&
+ !iwlwifi_mod_params.sw_crypto) {
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+ mvm->ciphers[hw->wiphy->n_cipher_suites] =
+ WLAN_CIPHER_SUITE_AES_CMAC;
+ hw->wiphy->n_cipher_suites++;
+ }
+
+ /* currently FW API supports only one optional cipher scheme */
+ if (mvm->fw->cs[0].cipher) {
+ mvm->hw->n_cipher_schemes = 1;
+ mvm->hw->cipher_schemes = &mvm->fw->cs[0];
+ mvm->ciphers[hw->wiphy->n_cipher_suites] =
+ mvm->fw->cs[0].cipher;
+ hw->wiphy->n_cipher_suites++;
+ }
+
+ ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
+ hw->wiphy->features |=
+ NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+
+ hw->sta_data_size = sizeof(struct iwl_mvm_sta);
+ hw->vif_data_size = sizeof(struct iwl_mvm_vif);
+ hw->chanctx_data_size = sizeof(u16);
+
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE) |
+ BIT(NL80211_IFTYPE_ADHOC);
+
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
+ if (iwl_mvm_is_lar_supported(mvm))
+ hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+ else
+ hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+ REGULATORY_DISABLE_BEACON_HINTS;
+
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
+ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+
+ hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
+ hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(iwl_mvm_iface_combinations);
+
+ hw->wiphy->max_remain_on_channel_duration = 10000;
+ hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+ /* we can compensate an offset of up to 3 channels = 15 MHz */
+ hw->wiphy->max_adj_channel_rssi_comp = 3 * 5;
+
+ /* Extract MAC address */
+ memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
+ hw->wiphy->addresses = mvm->addresses;
+ hw->wiphy->n_addresses = 1;
+
+ /* Extract additional MAC addresses if available */
+ num_mac = (mvm->nvm_data->n_hw_addrs > 1) ?
+ min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1;
+
+ for (i = 1; i < num_mac; i++) {
+ memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr,
+ ETH_ALEN);
+ mvm->addresses[i].addr[5]++;
+ hw->wiphy->n_addresses++;
+ }
+
+ iwl_mvm_reset_phy_ctxts(mvm);
+
+ hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
+
+ hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+
+ BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK);
+ BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) ||
+ IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK));
+
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS;
+ else
+ mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS;
+
+ if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+ if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) {
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
+ fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_LQ_SS_PARAMS))
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |=
+ IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ }
+
+ hw->wiphy->hw_version = mvm->trans->hw_id;
+
+ if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
+ hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ else
+ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+ hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
+ hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
+ /* we create the 802.11 header and zero length SSID IE. */
+ hw->wiphy->max_sched_scan_ie_len =
+ SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
+ hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS;
+ hw->wiphy->max_sched_scan_plan_interval = U16_MAX;
+
+ /*
+ * the firmware uses u8 for num of iterations, but 0xff is saved for
+ * infinite loop, so the maximum number of iterations is actually 254.
+ */
+ hw->wiphy->max_sched_scan_plan_iterations = 254;
+
+ hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
+ NL80211_FEATURE_LOW_PRIORITY_SCAN |
+ NL80211_FEATURE_P2P_GO_OPPPS |
+ NL80211_FEATURE_DYNAMIC_SMPS |
+ NL80211_FEATURE_STATIC_SMPS |
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT))
+ hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT))
+ hw->wiphy->features |= NL80211_FEATURE_QUIET;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT))
+ hw->wiphy->features |=
+ NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT))
+ hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES;
+
+ mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+
+#ifdef CONFIG_PM_SLEEP
+ if (iwl_mvm_is_d0i3_supported(mvm) &&
+ device_can_wakeup(mvm->trans->dev)) {
+ mvm->wowlan.flags = WIPHY_WOWLAN_ANY;
+ hw->wiphy->wowlan = &mvm->wowlan;
+ }
+
+ if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+ mvm->trans->ops->d3_suspend &&
+ mvm->trans->ops->d3_resume &&
+ device_can_wakeup(mvm->trans->dev)) {
+ mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
+ WIPHY_WOWLAN_DISCONNECT |
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+ WIPHY_WOWLAN_RFKILL_RELEASE |
+ WIPHY_WOWLAN_NET_DETECT;
+ if (!iwlwifi_mod_params.sw_crypto)
+ 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.max_nd_match_sets = IWL_SCAN_MAX_PROFILES;
+ mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
+ hw->wiphy->wowlan = &mvm->wowlan;
+ }
+#endif
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+ /* assign default bcast filtering configuration */
+ mvm->bcast_filters = iwl_mvm_default_bcast_filters;
+#endif
+
+ ret = iwl_mvm_leds_init(mvm);
+ if (ret)
+ return ret;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_TDLS_SUPPORT)) {
+ IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+ ieee80211_hw_set(hw, TDLS_WIDER_BW);
+ }
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) {
+ IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
+ hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+ }
+
+ hw->netdev_features |= mvm->cfg->features;
+ if (!iwl_mvm_is_csum_supported(mvm))
+ hw->netdev_features &= ~NETIF_F_RXCSUM;
+
+ if (IWL_MVM_SW_TX_CSUM_OFFLOAD)
+ hw->netdev_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO | NETIF_F_TSO6;
+
+ ret = ieee80211_register_hw(mvm->hw);
+ if (ret)
+ iwl_mvm_leds_exit(mvm);
+
+ return ret;
+}
+
+static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb)
+{
+ struct iwl_mvm_sta *mvmsta;
+ bool defer = false;
+
+ /*
+ * double check the IN_D0I3 flag both before and after
+ * taking the spinlock, in order to prevent taking
+ * the spinlock when not needed.
+ */
+ if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)))
+ return false;
+
+ spin_lock(&mvm->d0i3_tx_lock);
+ /*
+ * testing the flag again ensures the skb dequeue
+ * loop (on d0i3 exit) hasn't run yet.
+ */
+ if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))
+ goto out;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvmsta->sta_id == IWL_MVM_STATION_COUNT ||
+ mvmsta->sta_id != mvm->d0i3_ap_sta_id)
+ goto out;
+
+ __skb_queue_tail(&mvm->d0i3_tx, skb);
+ ieee80211_stop_queues(mvm->hw);
+
+ /* trigger wakeup */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_TX);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_TX);
+
+ defer = true;
+out:
+ spin_unlock(&mvm->d0i3_tx_lock);
+ return defer;
+}
+
+static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_sta *sta = control->sta;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+
+ if (iwl_mvm_is_radio_killed(mvm)) {
+ IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
+ goto drop;
+ }
+
+ if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
+ !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
+ !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
+ goto drop;
+
+ /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
+ if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER &&
+ ieee80211_is_mgmt(hdr->frame_control) &&
+ !ieee80211_is_deauth(hdr->frame_control) &&
+ !ieee80211_is_disassoc(hdr->frame_control) &&
+ !ieee80211_is_action(hdr->frame_control)))
+ sta = NULL;
+
+ if (sta) {
+ if (iwl_mvm_defer_tx(mvm, sta, skb))
+ return;
+ if (iwl_mvm_tx_skb(mvm, skb, sta))
+ goto drop;
+ return;
+ }
+
+ if (iwl_mvm_tx_skb_non_sta(mvm, skb))
+ goto drop;
+ return;
+ drop:
+ ieee80211_free_txskb(hw, skb);
+}
+
+static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg)
+{
+ if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
+ return false;
+ return true;
+}
+
+static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
+{
+ if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
+ return false;
+ if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
+ return true;
+
+ /* enabled by default */
+ return true;
+}
+
+#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \
+ do { \
+ if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \
+ break; \
+ iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \
+ } while (0)
+
+static void
+iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn,
+ enum ieee80211_ampdu_mlme_action action)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_ba *ba_trig;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
+ ba_trig = (void *)trig->data;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ return;
+
+ switch (action) {
+ case IEEE80211_AMPDU_TX_OPERATIONAL: {
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+
+ CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid,
+ "TX AGG START: MAC %pM tid %d ssn %d\n",
+ sta->addr, tid, tid_data->ssn);
+ break;
+ }
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid,
+ "TX AGG STOP: MAC %pM tid %d\n",
+ sta->addr, tid);
+ break;
+ case IEEE80211_AMPDU_RX_START:
+ CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid,
+ "RX AGG START: MAC %pM tid %d ssn %d\n",
+ sta->addr, tid, rx_ba_ssn);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid,
+ "RX AGG STOP: MAC %pM tid %d\n",
+ sta->addr, tid);
+ break;
+ default:
+ break;
+ }
+}
+
+static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid,
+ u16 *ssn, u8 buf_size, bool amsdu)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+ bool tx_agg_ref = false;
+
+ IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
+ sta->addr, tid, action);
+
+ if (!(mvm->nvm_data->sku_cap_11n_enable))
+ return -EACCES;
+
+ /* return from D0i3 before starting a new Tx aggregation */
+ switch (action) {
+ case IEEE80211_AMPDU_TX_START:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ /*
+ * for tx start, wait synchronously until D0i3 exit to
+ * get the correct sequence number for the tid.
+ * additionally, some other ampdu actions use direct
+ * target access, which is not handled automatically
+ * by the trans layer (unlike commands), so wait for
+ * d0i3 exit in these cases as well.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG);
+ if (ret)
+ return ret;
+
+ tx_agg_ref = true;
+ break;
+ default:
+ break;
+ }
+
+ mutex_lock(&mvm->mutex);
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ if (!iwl_enable_rx_ampdu(mvm->cfg)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ if (!iwl_enable_tx_ampdu(mvm->cfg)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid);
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!ret) {
+ u16 rx_ba_ssn = 0;
+
+ if (action == IEEE80211_AMPDU_RX_START)
+ rx_ba_ssn = *ssn;
+
+ iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid,
+ rx_ba_ssn, action);
+ }
+ mutex_unlock(&mvm->mutex);
+
+ /*
+ * If the tid is marked as started, we won't use it for offloaded
+ * traffic on the next D0i3 entry. It's safe to unref.
+ */
+ if (tx_agg_ref)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
+
+ return ret;
+}
+
+static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ mvmvif->uploaded = false;
+ mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+ spin_lock_bh(&mvm->time_event_lock);
+ iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
+ spin_unlock_bh(&mvm->time_event_lock);
+
+ mvmvif->phy_ctxt = NULL;
+ memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
+}
+
+static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
+{
+ /* clear the D3 reconfig, we only need it to avoid dumping a
+ * firmware coredump on reconfiguration, we shouldn't do that
+ * on D3->D0 transition
+ */
+ if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) {
+ mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert;
+ iwl_mvm_fw_error_dump(mvm);
+ }
+
+ /* cleanup all stale references (scan, roc), but keep the
+ * ucode_down ref until reconfig is complete
+ */
+ iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
+
+ iwl_trans_stop_device(mvm->trans);
+
+ mvm->scan_status = 0;
+ mvm->ps_disabled = false;
+ mvm->calibrating = false;
+
+ /* just in case one was running */
+ iwl_mvm_cleanup_roc_te(mvm);
+ ieee80211_remain_on_channel_expired(mvm->hw);
+
+ /*
+ * cleanup all interfaces, even inactive ones, as some might have
+ * gone down during the HW restart
+ */
+ ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm);
+
+ mvm->p2p_device_vif = NULL;
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+
+ iwl_mvm_reset_phy_ctxts(mvm);
+ memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
+ memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
+ memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained));
+ memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
+ memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
+ memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
+ memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
+ memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk));
+ memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk));
+
+ ieee80211_wake_queues(mvm->hw);
+
+ /* clear any stale d0i3 state */
+ clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+
+ mvm->vif_count = 0;
+ mvm->rx_ba_sessions = 0;
+ mvm->fw_dbg_conf = FW_DBG_INVALID;
+
+ /* keep statistics ticking */
+ iwl_mvm_accu_radio_stats(mvm);
+}
+
+int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
+{
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ /* Clean up some internal and mac80211 state on restart */
+ iwl_mvm_restart_cleanup(mvm);
+ } else {
+ /* Hold the reference to prevent runtime suspend while
+ * the start procedure runs. It's a bit confusing
+ * that the UCODE_DOWN reference is taken, but it just
+ * means "UCODE is not UP yet". ( TODO: rename this
+ * reference).
+ */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ }
+ ret = iwl_mvm_up(mvm);
+
+ if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ /* Something went wrong - we need to finish some cleanup
+ * that normally iwl_mvm_mac_restart_complete() below
+ * would do.
+ */
+ clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+ iwl_mvm_d0i3_enable_tx(mvm, NULL);
+ }
+
+ return ret;
+}
+
+static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ /* Some hw restart cleanups must not hold the mutex */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ /*
+ * Make sure we are out of d0i3. This is needed
+ * to make sure the reference accounting is correct
+ * (and there is no stale d0i3_exit_work).
+ */
+ wait_event_timeout(mvm->d0i3_exit_waitq,
+ !test_bit(IWL_MVM_STATUS_IN_D0I3,
+ &mvm->status),
+ HZ);
+ }
+
+ mutex_lock(&mvm->mutex);
+ ret = __iwl_mvm_mac_start(mvm);
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
+{
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+ iwl_mvm_d0i3_enable_tx(mvm, NULL);
+ ret = iwl_mvm_update_quotas(mvm, true, NULL);
+ if (ret)
+ IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
+ ret);
+
+ /* allow transport/FW low power modes */
+ iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
+ /*
+ * If we have TDLS peers, remove them. We don't know the last seqno/PN
+ * of packets the FW sent out, so we must reconnect.
+ */
+ iwl_mvm_teardown_tdls_peers(mvm);
+
+ mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
+{
+ if (iwl_mvm_is_d0i3_supported(mvm) &&
+ iwl_mvm_enter_d0i3_on_suspend(mvm))
+ WARN_ONCE(!wait_event_timeout(mvm->d0i3_exit_waitq,
+ !test_bit(IWL_MVM_STATUS_IN_D0I3,
+ &mvm->status),
+ HZ),
+ "D0i3 exit on resume timed out\n");
+}
+
+static void
+iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ switch (reconfig_type) {
+ case IEEE80211_RECONFIG_TYPE_RESTART:
+ iwl_mvm_restart_complete(mvm);
+ break;
+ case IEEE80211_RECONFIG_TYPE_SUSPEND:
+ iwl_mvm_resume_complete(mvm);
+ break;
+ }
+}
+
+void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ /* firmware counters are obviously reset now, but we shouldn't
+ * partially track so also clear the fw_reset_accu counters.
+ */
+ memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
+
+ /* async_handlers_wk is now blocked */
+
+ /*
+ * The work item could be running or queued if the
+ * ROC time event stops just as we get here.
+ */
+ flush_work(&mvm->roc_done_wk);
+
+ iwl_trans_stop_device(mvm->trans);
+
+ iwl_mvm_async_handlers_purge(mvm);
+ /* async_handlers_list is empty and will stay empty: HW is stopped */
+
+ /* the fw is stopped, the aux sta is dead: clean up driver state */
+ iwl_mvm_del_aux_sta(mvm);
+
+ /*
+ * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete()
+ * won't be called in this case).
+ * But make sure to cleanup interfaces that have gone down before/during
+ * HW restart was requested.
+ */
+ if (test_and_clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ ieee80211_iterate_interfaces(mvm->hw, 0,
+ iwl_mvm_cleanup_iterator, mvm);
+
+ /* We shouldn't have any UIDs still set. Loop over all the UIDs to
+ * make sure there's nothing left there and warn if any is found.
+ */
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ int i;
+
+ for (i = 0; i < mvm->max_scans; i++) {
+ if (WARN_ONCE(mvm->scan_uid_status[i],
+ "UMAC scan UID %d status was not cleaned\n",
+ i))
+ mvm->scan_uid_status[i] = 0;
+ }
+ }
+
+ mvm->ucode_loaded = false;
+}
+
+static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ flush_work(&mvm->d0i3_exit_work);
+ flush_work(&mvm->async_handlers_wk);
+ cancel_delayed_work_sync(&mvm->fw_dump_wk);
+ iwl_mvm_free_fw_dump_desc(mvm);
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_mac_stop(mvm);
+ mutex_unlock(&mvm->mutex);
+
+ /*
+ * The worker might have been waiting for the mutex, let it run and
+ * discover that its list is now empty.
+ */
+ cancel_work_sync(&mvm->async_handlers_wk);
+}
+
+static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
+{
+ u16 i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ for (i = 0; i < NUM_PHY_CTX; i++)
+ if (!mvm->phy_ctxts[i].ref)
+ return &mvm->phy_ctxts[i];
+
+ IWL_ERR(mvm, "No available PHY context\n");
+ return NULL;
+}
+
+static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ s16 tx_power)
+{
+ struct iwl_dev_tx_power_cmd cmd = {
+ .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC),
+ .v2.mac_context_id =
+ cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id),
+ .v2.pwr_restriction = cpu_to_le16(8 * tx_power),
+ };
+ int len = sizeof(cmd);
+
+ if (tx_power == IWL_DEFAULT_MAX_TX_POWER)
+ cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER);
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_CHAIN))
+ len = sizeof(cmd.v2);
+
+ return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd);
+}
+
+static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ mvmvif->mvm = mvm;
+
+ /*
+ * make sure D0i3 exit is completed, otherwise a target access
+ * during tx queue configuration could be done when still in
+ * D0i3 state.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF);
+ if (ret)
+ return ret;
+
+ /*
+ * Not much to do here. The stack will not allow interface
+ * types or combinations that we didn't advertise, so we
+ * don't really have to check the types.
+ */
+
+ mutex_lock(&mvm->mutex);
+
+ /* make sure that beacon statistics don't go backwards with FW reset */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ mvmvif->beacon_stats.accu_num_beacons +=
+ mvmvif->beacon_stats.num_beacons;
+
+ /* Allocate resources for the MAC context, and add it to the fw */
+ ret = iwl_mvm_mac_ctxt_init(mvm, vif);
+ if (ret)
+ goto out_unlock;
+
+ /* Counting number of interfaces is needed for legacy PM */
+ if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+ mvm->vif_count++;
+
+ /*
+ * The AP binding flow can be done only after the beacon
+ * template is configured (which happens only in the mac80211
+ * start_ap() flow), and adding the broadcast station can happen
+ * only after the binding.
+ * In addition, since modifying the MAC before adding a bcast
+ * station is not allowed by the FW, delay the adding of MAC context to
+ * the point where we can also add the bcast station.
+ * In short: there's not much we can do at this point, other than
+ * allocating resources :)
+ */
+ if (vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_ADHOC) {
+ ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to allocate bcast sta\n");
+ goto out_release;
+ }
+
+ iwl_mvm_vif_dbgfs_register(mvm, vif);
+ goto out_unlock;
+ }
+
+ mvmvif->features |= hw->netdev_features;
+
+ ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+ if (ret)
+ goto out_release;
+
+ ret = iwl_mvm_power_update_mac(mvm);
+ if (ret)
+ goto out_remove_mac;
+
+ /* beacon filtering */
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ goto out_remove_mac;
+
+ if (!mvm->bf_allowed_vif &&
+ vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
+ mvm->bf_allowed_vif = mvmvif;
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+ }
+
+ /*
+ * P2P_DEVICE interface does not have a channel context assigned to it,
+ * so a dedicated PHY context is allocated to it and the corresponding
+ * MAC context is bound to it at this stage.
+ */
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+
+ mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+ if (!mvmvif->phy_ctxt) {
+ ret = -ENOSPC;
+ goto out_free_bf;
+ }
+
+ iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (ret)
+ goto out_unref_phy;
+
+ ret = iwl_mvm_add_bcast_sta(mvm, vif);
+ if (ret)
+ goto out_unbind;
+
+ /* Save a pointer to p2p device vif, so it can later be used to
+ * update the p2p device MAC when a GO is started/stopped */
+ mvm->p2p_device_vif = vif;
+ }
+
+ iwl_mvm_vif_dbgfs_register(mvm, vif);
+ goto out_unlock;
+
+ out_unbind:
+ iwl_mvm_binding_remove_vif(mvm, vif);
+ out_unref_phy:
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+ out_free_bf:
+ if (mvm->bf_allowed_vif == mvmvif) {
+ mvm->bf_allowed_vif = NULL;
+ vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI);
+ }
+ out_remove_mac:
+ mvmvif->phy_ctxt = NULL;
+ iwl_mvm_mac_ctxt_remove(mvm, vif);
+ out_release:
+ if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+ mvm->vif_count--;
+
+ iwl_mvm_mac_ctxt_release(mvm, vif);
+ out_unlock:
+ mutex_unlock(&mvm->mutex);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF);
+
+ return ret;
+}
+
+static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
+
+ if (tfd_msk) {
+ /*
+ * mac80211 first removes all the stations of the vif and
+ * then removes the vif. When it removes a station it also
+ * flushes the AMPDU session. So by now, all the AMPDU sessions
+ * of all the stations of this vif are closed, and the queues
+ * of these AMPDU sessions are properly closed.
+ * We still need to take care of the shared queues of the vif.
+ * Flush them here.
+ */
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_flush_tx_path(mvm, tfd_msk, 0);
+ mutex_unlock(&mvm->mutex);
+
+ /*
+ * There are transports that buffer a few frames in the host.
+ * For these, the flush above isn't enough since while we were
+ * flushing, the transport might have sent more frames to the
+ * device. To solve this, wait here until the transport is
+ * empty. Technically, this could have replaced the flush
+ * above, but flush is much faster than draining. So flush
+ * first, and drain to make sure we have no frames in the
+ * transport anymore.
+ * If a station still had frames on the shared queues, it is
+ * already marked as draining, so to complete the draining, we
+ * just need to wait until the transport is empty.
+ */
+ iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk);
+ }
+
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ /*
+ * Flush the ROC worker which will flush the OFFCHANNEL queue.
+ * We assume here that all the packets sent to the OFFCHANNEL
+ * queue are sent in ROC session.
+ */
+ flush_work(&mvm->roc_done_wk);
+ } else {
+ /*
+ * 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.
+ */
+ flush_work(&mvm->sta_drained_wk);
+ }
+}
+
+static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ iwl_mvm_prepare_mac_removal(mvm, vif);
+
+ mutex_lock(&mvm->mutex);
+
+ if (mvm->bf_allowed_vif == mvmvif) {
+ mvm->bf_allowed_vif = NULL;
+ vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI);
+ }
+
+ iwl_mvm_vif_dbgfs_clean(mvm, vif);
+
+ /*
+ * For AP/GO interface, the tear down of the resources allocated to the
+ * interface is be handled as part of the stop_ap flow.
+ */
+ if (vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_ADHOC) {
+#ifdef CONFIG_NL80211_TESTMODE
+ if (vif == mvm->noa_vif) {
+ mvm->noa_vif = NULL;
+ mvm->noa_duration = 0;
+ }
+#endif
+ iwl_mvm_dealloc_bcast_sta(mvm, vif);
+ goto out_release;
+ }
+
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ mvm->p2p_device_vif = NULL;
+ iwl_mvm_rm_bcast_sta(mvm, vif);
+ iwl_mvm_binding_remove_vif(mvm, vif);
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+ mvmvif->phy_ctxt = NULL;
+ }
+
+ if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
+ mvm->vif_count--;
+
+ iwl_mvm_power_update_mac(mvm);
+ iwl_mvm_mac_ctxt_remove(mvm, vif);
+
+out_release:
+ iwl_mvm_mac_ctxt_release(mvm, vif);
+ mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
+{
+ return 0;
+}
+
+struct iwl_mvm_mc_iter_data {
+ struct iwl_mvm *mvm;
+ int port_id;
+};
+
+static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_mc_iter_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
+ int ret, len;
+
+ /* if we don't have free ports, mcast frames will be dropped */
+ if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM))
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ !vif->bss_conf.assoc)
+ return;
+
+ cmd->port_id = data->port_id++;
+ memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
+ len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd);
+ if (ret)
+ IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
+}
+
+static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_mc_iter_data iter_data = {
+ .mvm = mvm,
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (WARN_ON_ONCE(!mvm->mcast_filter_cmd))
+ return;
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_mc_iface_iterator, &iter_data);
+}
+
+static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mcast_filter_cmd *cmd;
+ struct netdev_hw_addr *addr;
+ int addr_count;
+ bool pass_all;
+ int len;
+
+ addr_count = netdev_hw_addr_list_count(mc_list);
+ pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES ||
+ IWL_MVM_FW_MCAST_FILTER_PASS_ALL;
+ if (pass_all)
+ addr_count = 0;
+
+ len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4);
+ cmd = kzalloc(len, GFP_ATOMIC);
+ if (!cmd)
+ return 0;
+
+ if (pass_all) {
+ cmd->pass_all = 1;
+ return (u64)(unsigned long)cmd;
+ }
+
+ netdev_hw_addr_list_for_each(addr, mc_list) {
+ IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n",
+ cmd->count, addr->addr);
+ memcpy(&cmd->addr_list[cmd->count * ETH_ALEN],
+ addr->addr, ETH_ALEN);
+ cmd->count++;
+ }
+
+ return (u64)(unsigned long)cmd;
+}
+
+static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
+
+ mutex_lock(&mvm->mutex);
+
+ /* replace previous configuration */
+ kfree(mvm->mcast_filter_cmd);
+ mvm->mcast_filter_cmd = cmd;
+
+ if (!cmd)
+ goto out;
+
+ iwl_mvm_recalc_multicast(mvm);
+out:
+ mutex_unlock(&mvm->mutex);
+ *total_flags = 0;
+}
+
+static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int filter_flags,
+ unsigned int changed_flags)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ /* We support only filter for probe requests */
+ if (!(changed_flags & FIF_PROBE_REQ))
+ return;
+
+ /* Supported only for p2p client interfaces */
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc ||
+ !vif->p2p)
+ return;
+
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ mutex_unlock(&mvm->mutex);
+}
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+struct iwl_bcast_iter_data {
+ struct iwl_mvm *mvm;
+ struct iwl_bcast_filter_cmd *cmd;
+ u8 current_filter;
+};
+
+static void
+iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif,
+ const struct iwl_fw_bcast_filter *in_filter,
+ struct iwl_fw_bcast_filter *out_filter)
+{
+ struct iwl_fw_bcast_filter_attr *attr;
+ int i;
+
+ memcpy(out_filter, in_filter, sizeof(*out_filter));
+
+ for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) {
+ attr = &out_filter->attrs[i];
+
+ if (!attr->mask)
+ break;
+
+ switch (attr->reserved1) {
+ case cpu_to_le16(BC_FILTER_MAGIC_IP):
+ if (vif->bss_conf.arp_addr_cnt != 1) {
+ attr->mask = 0;
+ continue;
+ }
+
+ attr->val = vif->bss_conf.arp_addr_list[0];
+ break;
+ case cpu_to_le16(BC_FILTER_MAGIC_MAC):
+ attr->val = *(__be32 *)&vif->addr[2];
+ break;
+ default:
+ break;
+ }
+ attr->reserved1 = 0;
+ out_filter->num_attrs++;
+ }
+}
+
+static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_bcast_iter_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct iwl_bcast_filter_cmd *cmd = data->cmd;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_fw_bcast_mac *bcast_mac;
+ int i;
+
+ if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs)))
+ return;
+
+ bcast_mac = &cmd->macs[mvmvif->id];
+
+ /*
+ * enable filtering only for associated stations, but not for P2P
+ * Clients
+ */
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p ||
+ !vif->bss_conf.assoc)
+ return;
+
+ bcast_mac->default_discard = 1;
+
+ /* copy all configured filters */
+ for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) {
+ /*
+ * Make sure we don't exceed our filters limit.
+ * if there is still a valid filter to be configured,
+ * be on the safe side and just allow bcast for this mac.
+ */
+ if (WARN_ON_ONCE(data->current_filter >=
+ ARRAY_SIZE(cmd->filters))) {
+ bcast_mac->default_discard = 0;
+ bcast_mac->attached_filters = 0;
+ break;
+ }
+
+ iwl_mvm_set_bcast_filter(vif,
+ &mvm->bcast_filters[i],
+ &cmd->filters[data->current_filter]);
+
+ /* skip current filter if it contains no attributes */
+ if (!cmd->filters[data->current_filter].num_attrs)
+ continue;
+
+ /* attach the filter to current mac */
+ bcast_mac->attached_filters |=
+ cpu_to_le16(BIT(data->current_filter));
+
+ data->current_filter++;
+ }
+}
+
+bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
+ struct iwl_bcast_filter_cmd *cmd)
+{
+ struct iwl_bcast_iter_data iter_data = {
+ .mvm = mvm,
+ .cmd = cmd,
+ };
+
+ if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL)
+ return false;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
+ cmd->max_macs = ARRAY_SIZE(cmd->macs);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ /* use debugfs filters/macs if override is configured */
+ if (mvm->dbgfs_bcast_filtering.override) {
+ memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters,
+ sizeof(cmd->filters));
+ memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs,
+ sizeof(cmd->macs));
+ return true;
+ }
+#endif
+
+ /* if no filters are configured, do nothing */
+ if (!mvm->bcast_filters)
+ return false;
+
+ /* configure and attach these filters for each associated sta vif */
+ ieee80211_iterate_active_interfaces(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bcast_filter_iterator, &iter_data);
+
+ return true;
+}
+
+static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm)
+{
+ struct iwl_bcast_filter_cmd cmd;
+
+ if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
+ return 0;
+
+ if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+ return 0;
+
+ return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
+ sizeof(cmd), &cmd);
+}
+#else
+static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm)
+{
+ return 0;
+}
+#endif
+
+static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changes)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ /*
+ * Re-calculate the tsf id, as the master-slave relations depend on the
+ * beacon interval, which was not known when the station interface was
+ * added.
+ */
+ if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc)
+ iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
+
+ /*
+ * If we're not associated yet, take the (new) BSSID before associating
+ * so the firmware knows. If we're already associated, then use the old
+ * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC
+ * branch for disassociation below.
+ */
+ if (changes & BSS_CHANGED_BSSID && !mvmvif->associated)
+ memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
+
+ ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid);
+ if (ret)
+ IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
+
+ /* after sending it once, adopt mac80211 data */
+ memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
+ mvmvif->associated = bss_conf->assoc;
+
+ if (changes & BSS_CHANGED_ASSOC) {
+ if (bss_conf->assoc) {
+ /* clear statistics to get clean beacon counter */
+ iwl_mvm_request_statistics(mvm, true);
+ memset(&mvmvif->beacon_stats, 0,
+ sizeof(mvmvif->beacon_stats));
+
+ /* add quota for this interface */
+ ret = iwl_mvm_update_quotas(mvm, true, NULL);
+ if (ret) {
+ IWL_ERR(mvm, "failed to update quotas\n");
+ return;
+ }
+
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
+ &mvm->status)) {
+ /*
+ * If we're restarting then the firmware will
+ * obviously have lost synchronisation with
+ * the AP. It will attempt to synchronise by
+ * itself, but we can make it more reliable by
+ * scheduling a session protection time event.
+ *
+ * The firmware needs to receive a beacon to
+ * catch up with synchronisation, use 110% of
+ * the beacon interval.
+ *
+ * Set a large maximum delay to allow for more
+ * than a single interface.
+ */
+ u32 dur = (11 * vif->bss_conf.beacon_int) / 10;
+ iwl_mvm_protect_session(mvm, vif, dur, dur,
+ 5 * dur, false);
+ }
+
+ iwl_mvm_sf_update(mvm, vif, false);
+ iwl_mvm_power_vif_assoc(mvm, vif);
+ if (vif->p2p) {
+ iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);
+ iwl_mvm_update_smps(mvm, vif,
+ IWL_MVM_SMPS_REQ_PROT,
+ IEEE80211_SMPS_DYNAMIC);
+ }
+ } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+ /*
+ * If update fails - SF might be running in associated
+ * mode while disassociated - which is forbidden.
+ */
+ WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
+ "Failed to update SF upon disassociation\n");
+
+ /* remove AP station now that the MAC is unassoc */
+ ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
+ if (ret)
+ IWL_ERR(mvm, "failed to remove AP station\n");
+
+ if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+ mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+ /* remove quota for this interface */
+ ret = iwl_mvm_update_quotas(mvm, false, NULL);
+ if (ret)
+ IWL_ERR(mvm, "failed to update quotas\n");
+
+ if (vif->p2p)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);
+
+ /* this will take the cleared BSSID from bss_conf */
+ ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ if (ret)
+ IWL_ERR(mvm,
+ "failed to update MAC %pM (clear after unassoc)\n",
+ vif->addr);
+ }
+
+ iwl_mvm_recalc_multicast(mvm);
+ iwl_mvm_configure_bcast_filter(mvm);
+
+ /* reset rssi values */
+ mvmvif->bf_data.ave_beacon_signal = 0;
+
+ iwl_mvm_bt_coex_vif_change(mvm);
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
+ IEEE80211_SMPS_AUTOMATIC);
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ iwl_mvm_config_scan(mvm);
+ } else if (changes & BSS_CHANGED_BEACON_INFO) {
+ /*
+ * We received a beacon _after_ association so
+ * remove the session protection.
+ */
+ iwl_mvm_remove_time_event(mvm, mvmvif,
+ &mvmvif->time_event_data);
+ }
+
+ if (changes & BSS_CHANGED_BEACON_INFO) {
+ iwl_mvm_sf_update(mvm, vif, false);
+ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
+ }
+
+ if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) {
+ ret = iwl_mvm_power_update_mac(mvm);
+ if (ret)
+ IWL_ERR(mvm, "failed to update power mode\n");
+ }
+
+ if (changes & BSS_CHANGED_TXPOWER) {
+ IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
+ bss_conf->txpower);
+ iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+ }
+
+ if (changes & BSS_CHANGED_CQM) {
+ IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n");
+ /* reset cqm events tracking */
+ mvmvif->bf_data.last_cqm_event = 0;
+ if (mvmvif->bf_data.bf_enabled) {
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ IWL_ERR(mvm,
+ "failed to update CQM thresholds\n");
+ }
+ }
+
+ if (changes & BSS_CHANGED_ARP_FILTER) {
+ IWL_DEBUG_MAC80211(mvm, "arp filter changed\n");
+ iwl_mvm_configure_bcast_filter(mvm);
+ }
+}
+
+static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ /*
+ * iwl_mvm_mac_ctxt_add() might read directly from the device
+ * (the system time), so make sure it is available.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP);
+ if (ret)
+ return ret;
+
+ mutex_lock(&mvm->mutex);
+
+ /* Send the beacon template */
+ ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif);
+ if (ret)
+ goto out_unlock;
+
+ /*
+ * Re-calculate the tsf id, as the master-slave relations depend on the
+ * beacon interval, which was not known when the AP interface was added.
+ */
+ if (vif->type == NL80211_IFTYPE_AP)
+ iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
+
+ mvmvif->ap_assoc_sta_count = 0;
+
+ /* Add the mac context */
+ ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+ if (ret)
+ goto out_unlock;
+
+ /* Perform the binding */
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (ret)
+ goto out_remove;
+
+ /* Send the bcast station. At this stage the TBTT and DTIM time events
+ * are added and applied to the scheduler */
+ ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
+ if (ret)
+ goto out_unbind;
+
+ /* must be set before quota calculations */
+ mvmvif->ap_ibss_active = true;
+
+ /* power updated needs to be done before quotas */
+ iwl_mvm_power_update_mac(mvm);
+
+ ret = iwl_mvm_update_quotas(mvm, false, NULL);
+ if (ret)
+ goto out_quota_failed;
+
+ /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
+ if (vif->p2p && mvm->p2p_device_vif)
+ iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
+
+ iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
+
+ iwl_mvm_bt_coex_vif_change(mvm);
+
+ /* we don't support TDLS during DCM */
+ if (iwl_mvm_phy_ctx_count(mvm) > 1)
+ iwl_mvm_teardown_tdls_peers(mvm);
+
+ goto out_unlock;
+
+out_quota_failed:
+ iwl_mvm_power_update_mac(mvm);
+ mvmvif->ap_ibss_active = false;
+ iwl_mvm_send_rm_bcast_sta(mvm, vif);
+out_unbind:
+ iwl_mvm_binding_remove_vif(mvm, vif);
+out_remove:
+ iwl_mvm_mac_ctxt_remove(mvm, vif);
+out_unlock:
+ mutex_unlock(&mvm->mutex);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP);
+ return ret;
+}
+
+static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ iwl_mvm_prepare_mac_removal(mvm, vif);
+
+ mutex_lock(&mvm->mutex);
+
+ /* Handle AP stop while in CSA */
+ if (rcu_access_pointer(mvm->csa_vif) == vif) {
+ iwl_mvm_remove_time_event(mvm, mvmvif,
+ &mvmvif->time_event_data);
+ RCU_INIT_POINTER(mvm->csa_vif, NULL);
+ mvmvif->csa_countdown = false;
+ }
+
+ if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
+ RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
+ mvm->csa_tx_block_bcn_timeout = 0;
+ }
+
+ mvmvif->ap_ibss_active = false;
+ mvm->ap_last_beacon_gp2 = 0;
+
+ iwl_mvm_bt_coex_vif_change(mvm);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
+
+ /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
+ if (vif->p2p && mvm->p2p_device_vif)
+ iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
+
+ iwl_mvm_update_quotas(mvm, false, NULL);
+ iwl_mvm_send_rm_bcast_sta(mvm, vif);
+ iwl_mvm_binding_remove_vif(mvm, vif);
+
+ iwl_mvm_power_update_mac(mvm);
+
+ iwl_mvm_mac_ctxt_remove(mvm, vif);
+
+ mutex_unlock(&mvm->mutex);
+}
+
+static void
+iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changes)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ /* Changes will be applied when the AP/IBSS is started */
+ if (!mvmvif->ap_ibss_active)
+ return;
+
+ if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
+ BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS) &&
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL))
+ IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
+
+ /* Need to send a new beacon template to the FW */
+ if (changes & BSS_CHANGED_BEACON &&
+ iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
+ IWL_WARN(mvm, "Failed updating beacon data\n");
+
+ if (changes & BSS_CHANGED_TXPOWER) {
+ IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
+ bss_conf->txpower);
+ iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+ }
+
+}
+
+static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changes)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ /*
+ * iwl_mvm_bss_info_changed_station() might call
+ * iwl_mvm_protect_session(), which reads directly from
+ * the device (the system time), so make sure it is available.
+ */
+ if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED))
+ return;
+
+ mutex_lock(&mvm->mutex);
+
+ if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
+ iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes);
+ break;
+ default:
+ /* shouldn't happen */
+ WARN_ON_ONCE(1);
+ }
+
+ mutex_unlock(&mvm->mutex);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED);
+}
+
+static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ if (hw_req->req.n_channels == 0 ||
+ hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies);
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ mutex_lock(&mvm->mutex);
+
+ /* Due to a race condition, it's possible that mac80211 asks
+ * us to stop a hw_scan when it's already stopped. This can
+ * happen, for instance, if we stopped the scan ourselves,
+ * called ieee80211_scan_completed() and the userspace called
+ * cancel scan scan before ieee80211_scan_work() could run.
+ * To handle that, simply return if the scan is not running.
+ */
+ if (mvm->scan_status & IWL_MVM_SCAN_REGULAR)
+ iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
+
+ mutex_unlock(&mvm->mutex);
+}
+
+static void
+iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u16 tids,
+ int num_frames,
+ enum ieee80211_frame_release_type reason,
+ bool more_data)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ /* Called when we need to transmit (a) frame(s) from mac80211 */
+
+ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
+ tids, more_data, false);
+}
+
+static void
+iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u16 tids,
+ int num_frames,
+ enum ieee80211_frame_release_type reason,
+ bool more_data)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ /* Called when we need to transmit (a) frame(s) from agg queue */
+
+ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
+ tids, more_data, true);
+}
+
+static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ unsigned long txqs = 0, tids = 0;
+ int tid;
+
+ spin_lock_bh(&mvmsta->lock);
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+
+ if (tid_data->state != IWL_AGG_ON &&
+ tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+ continue;
+
+ __set_bit(tid_data->txq_id, &txqs);
+
+ if (iwl_mvm_tid_queued(tid_data) == 0)
+ continue;
+
+ __set_bit(tid, &tids);
+ }
+
+ switch (cmd) {
+ case STA_NOTIFY_SLEEP:
+ if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
+ ieee80211_sta_block_awake(hw, sta, true);
+
+ for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT)
+ ieee80211_sta_set_buffered(sta, tid, true);
+
+ if (txqs)
+ iwl_trans_freeze_txq_timer(mvm->trans, txqs, true);
+ /*
+ * The fw updates the STA to be asleep. Tx packets on the Tx
+ * queues to this station will not be transmitted. The fw will
+ * send a Tx response with TX_STATUS_FAIL_DEST_PS.
+ */
+ break;
+ case STA_NOTIFY_AWAKE:
+ if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
+ break;
+
+ if (txqs)
+ iwl_trans_freeze_txq_timer(mvm->trans, txqs, false);
+ iwl_mvm_sta_modify_ps_wake(mvm, sta);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_bh(&mvmsta->lock);
+}
+
+static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+ /*
+ * This is called before mac80211 does RCU synchronisation,
+ * so here we already invalidate our internal RCU-protected
+ * station pointer. The rest of the code will thus no longer
+ * be able to find the station this way, and we don't rely
+ * on further RCU synchronisation after the sta_state()
+ * callback deleted the station.
+ */
+ mutex_lock(&mvm->mutex);
+ if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id]))
+ rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
+ ERR_PTR(-ENOENT));
+
+ mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ const u8 *bssid)
+{
+ if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
+ return;
+
+ if (iwlwifi_mod_params.uapsd_disable) {
+ vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+ return;
+ }
+
+ vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+}
+
+static void
+iwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif, u8 *peer_addr,
+ enum nl80211_tdls_operation action)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_tdls *tdls_trig;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TDLS))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TDLS);
+ tdls_trig = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ return;
+
+ if (!(tdls_trig->action_bitmap & BIT(action)))
+ return;
+
+ if (tdls_trig->peer_mode &&
+ memcmp(tdls_trig->peer, peer_addr, ETH_ALEN) != 0)
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "TDLS event occurred, peer %pM, action %d",
+ peer_addr, action);
+}
+
+static int iwl_mvm_mac_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 iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n",
+ sta->addr, old_state, new_state);
+
+ /* this would be a mac80211 bug ... but don't crash */
+ if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+ return -EINVAL;
+
+ /* if a STA is being removed, reuse its ID */
+ flush_work(&mvm->sta_drained_wk);
+
+ mutex_lock(&mvm->mutex);
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE) {
+ /*
+ * Firmware bug - it'll crash if the beacon interval is less
+ * than 16. We can't avoid connecting at all, so refuse the
+ * station state change, this will cause mac80211 to abandon
+ * attempts to connect to this AP, and eventually wpa_s will
+ * blacklist the AP...
+ */
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ vif->bss_conf.beacon_int < 16) {
+ IWL_ERR(mvm,
+ "AP %pM beacon interval is %d, refusing due to firmware bug!\n",
+ sta->addr, vif->bss_conf.beacon_int);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (sta->tdls &&
+ (vif->p2p ||
+ iwl_mvm_tdls_sta_count(mvm, NULL) ==
+ IWL_MVM_TDLS_STA_COUNT ||
+ iwl_mvm_phy_ctx_count(mvm) > 1)) {
+ IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ ret = iwl_mvm_add_sta(mvm, vif, sta);
+ if (sta->tdls && ret == 0) {
+ iwl_mvm_recalc_tdls_state(mvm, vif, true);
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_SETUP);
+ }
+ } else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_AUTH) {
+ /*
+ * EBS may be disabled due to previous failures reported by FW.
+ * Reset EBS status here assuming environment has been changed.
+ */
+ mvm->last_ebs_successful = true;
+ iwl_mvm_check_uapsd(mvm, vif, sta->addr);
+ ret = 0;
+ } else if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC) {
+ if (vif->type == NL80211_IFTYPE_AP) {
+ mvmvif->ap_assoc_sta_count++;
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ }
+ ret = iwl_mvm_update_sta(mvm, vif, sta);
+ if (ret == 0)
+ iwl_mvm_rs_rate_init(mvm, sta,
+ mvmvif->phy_ctxt->channel->band,
+ true);
+ } else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTHORIZED) {
+
+ /* we don't support TDLS during DCM */
+ if (iwl_mvm_phy_ctx_count(mvm) > 1)
+ iwl_mvm_teardown_tdls_peers(mvm);
+
+ if (sta->tdls)
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_ENABLE_LINK);
+
+ /* enable beacon filtering */
+ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
+ ret = 0;
+ } else if (old_state == IEEE80211_STA_AUTHORIZED &&
+ new_state == IEEE80211_STA_ASSOC) {
+ /* disable beacon filtering */
+ WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0));
+ ret = 0;
+ } else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTH) {
+ if (vif->type == NL80211_IFTYPE_AP) {
+ mvmvif->ap_assoc_sta_count--;
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ }
+ ret = 0;
+ } else if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_NONE) {
+ ret = 0;
+ } else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST) {
+ ret = iwl_mvm_rm_sta(mvm, vif, sta);
+ if (sta->tdls) {
+ iwl_mvm_recalc_tdls_state(mvm, vif, false);
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_DISABLE_LINK);
+ }
+ } else {
+ ret = -EIO;
+ }
+ out_unlock:
+ mutex_unlock(&mvm->mutex);
+
+ if (sta->tdls && ret == 0) {
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE)
+ ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+ else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST)
+ ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+ }
+
+ return ret;
+}
+
+static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ mvm->rts_threshold = value;
+
+ return 0;
+}
+
+static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u32 changed)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ changed & IEEE80211_RC_NSS_CHANGED)
+ iwl_mvm_sf_update(mvm, vif, false);
+}
+
+static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 ac,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ mvmvif->queue_params[ac] = *params;
+
+ /*
+ * No need to update right away, we'll get BSS_CHANGED_QOS
+ * The exception is P2P_DEVICE interface which needs immediate update.
+ */
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ mutex_unlock(&mvm->mutex);
+ return ret;
+ }
+ return 0;
+}
+
+static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS,
+ 200 + vif->bss_conf.beacon_int);
+ u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS,
+ 100 + vif->bss_conf.beacon_int);
+
+ if (WARN_ON_ONCE(vif->bss_conf.assoc))
+ return;
+
+ /*
+ * iwl_mvm_protect_session() reads directly from the device
+ * (the system time), so make sure it is available.
+ */
+ if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX))
+ return;
+
+ mutex_lock(&mvm->mutex);
+ /* Try really hard to protect the session and hear a beacon */
+ iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false);
+ mutex_unlock(&mvm->mutex);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX);
+}
+
+static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ if (!vif->bss_conf.idle) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED);
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ /* Due to a race condition, it's possible that mac80211 asks
+ * us to stop a sched_scan when it's already stopped. This
+ * can happen, for instance, if we stopped the scan ourselves,
+ * called ieee80211_sched_scan_stopped() and the userspace called
+ * stop sched scan scan before ieee80211_sched_scan_stopped_work()
+ * could run. To handle this, simply return if the scan is
+ * not running.
+ */
+ if (!(mvm->scan_status & IWL_MVM_SCAN_SCHED)) {
+ mutex_unlock(&mvm->mutex);
+ return 0;
+ }
+
+ ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, false);
+ mutex_unlock(&mvm->mutex);
+ iwl_mvm_wait_for_async_handlers(mvm);
+
+ return ret;
+}
+
+static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
+ enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_key_pn *ptk_pn;
+ int keyidx = key->keyidx;
+ int ret;
+ u8 key_offset;
+
+ if (iwlwifi_mod_params.sw_crypto) {
+ IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n");
+ return -EOPNOTSUPP;
+ }
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ WARN_ON_ONCE(!ieee80211_hw_check(hw, MFP_CAPABLE));
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* For non-client mode, only use WEP keys for TX as we probably
+ * don't have a station yet anyway and would then have to keep
+ * track of the keys, linking them to each of the clients/peers
+ * as they appear. For now, don't do that, for performance WEP
+ * offload doesn't really matter much, but we need it for some
+ * other offload features in client mode.
+ */
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return 0;
+ break;
+ default:
+ /* currently FW supports only one optional cipher scheme */
+ if (hw->n_cipher_schemes &&
+ hw->cipher_schemes->cipher == key->cipher)
+ key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+ else
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&mvm->mutex);
+
+ switch (cmd) {
+ case SET_KEY:
+ if ((vif->type == NL80211_IFTYPE_ADHOC ||
+ vif->type == NL80211_IFTYPE_AP) && !sta) {
+ /*
+ * GTK on AP interface is a TX-only key, return 0;
+ * on IBSS they're per-station and because we're lazy
+ * we don't support them for RX, so do the same.
+ */
+ ret = 0;
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+ break;
+ }
+
+ /* During FW restart, in order to restore the state as it was,
+ * don't try to reprogram keys we previously failed for.
+ */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ key->hw_key_idx == STA_KEY_IDX_INVALID) {
+ IWL_DEBUG_MAC80211(mvm,
+ "skip invalid idx key programming during restart\n");
+ ret = 0;
+ break;
+ }
+
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ sta && iwl_mvm_has_new_rx_api(mvm) &&
+ key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
+ (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP)) {
+ struct ieee80211_key_seq seq;
+ int tid, q;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx]));
+ ptk_pn = kzalloc(sizeof(*ptk_pn) +
+ mvm->trans->num_rx_queues *
+ sizeof(ptk_pn->q[0]),
+ GFP_KERNEL);
+ if (!ptk_pn) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ ieee80211_get_key_rx_seq(key, tid, &seq);
+ for (q = 0; q < mvm->trans->num_rx_queues; q++)
+ memcpy(ptk_pn->q[q].pn[tid],
+ seq.ccmp.pn,
+ IEEE80211_CCMP_PN_LEN);
+ }
+
+ rcu_assign_pointer(mvmsta->ptk_pn[keyidx], ptk_pn);
+ }
+
+ /* in HW restart reuse the index, otherwise request a new one */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ key_offset = key->hw_key_idx;
+ else
+ key_offset = STA_KEY_IDX_INVALID;
+
+ IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
+ ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, key_offset);
+ if (ret) {
+ IWL_WARN(mvm, "set key failed\n");
+ /*
+ * can't add key for RX, but we don't need it
+ * in the device for TX so still return 0
+ */
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+ ret = 0;
+ }
+
+ break;
+ case DISABLE_KEY:
+ if (key->hw_key_idx == STA_KEY_IDX_INVALID) {
+ ret = 0;
+ break;
+ }
+
+ if (sta && iwl_mvm_has_new_rx_api(mvm) &&
+ key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
+ (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP)) {
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ ptk_pn = rcu_dereference_protected(
+ mvmsta->ptk_pn[keyidx],
+ lockdep_is_held(&mvm->mutex));
+ RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL);
+ if (ptk_pn)
+ kfree_rcu(ptk_pn, rcu_head);
+ }
+
+ IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
+ ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta,
+ u32 iv32, u16 *phase1key)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
+ return;
+
+ iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
+}
+
+
+static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mvm *mvm =
+ container_of(notif_wait, struct iwl_mvm, notif_wait);
+ struct iwl_hs20_roc_res *resp;
+ int resp_len = iwl_rx_packet_payload_len(pkt);
+ struct iwl_mvm_time_event_data *te_data = data;
+
+ if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD))
+ return true;
+
+ if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
+ IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n");
+ return true;
+ }
+
+ resp = (void *)pkt->data;
+
+ IWL_DEBUG_TE(mvm,
+ "Aux ROC: Recieved response from ucode: status=%d uid=%d\n",
+ resp->status, resp->event_unique_id);
+
+ te_data->uid = le32_to_cpu(resp->event_unique_id);
+ IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
+ te_data->uid);
+
+ spin_lock_bh(&mvm->time_event_lock);
+ list_add_tail(&te_data->list, &mvm->aux_roc_te_list);
+ spin_unlock_bh(&mvm->time_event_lock);
+
+ return true;
+}
+
+#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100)
+#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200)
+#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600)
+#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20)
+#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10)
+static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_channel *channel,
+ struct ieee80211_vif *vif,
+ int duration)
+{
+ int res, time_reg = DEVICE_SYSTEM_TIME_REG;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data;
+ static const u16 time_event_response[] = { HOT_SPOT_CMD };
+ struct iwl_notification_wait wait_time_event;
+ u32 dtim_interval = vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int;
+ u32 req_dur, delay;
+ struct iwl_hs20_roc_req aux_roc_req = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+ .id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)),
+ .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id),
+ /* Set the channel info data */
+ .channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ?
+ PHY_BAND_24 : PHY_BAND_5,
+ .channel_info.channel = channel->hw_value,
+ .channel_info.width = PHY_VHT_CHANNEL_MODE20,
+ /* Set the time and duration */
+ .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)),
+ };
+
+ delay = AUX_ROC_MIN_DELAY;
+ req_dur = MSEC_TO_TU(duration);
+
+ /*
+ * If we are associated we want the delay time to be at least one
+ * dtim interval so that the FW can wait until after the DTIM and
+ * then start the time event, this will potentially allow us to
+ * remain off-channel for the max duration.
+ * Since we want to use almost a whole dtim interval we would also
+ * like the delay to be for 2-3 dtim intervals, in case there are
+ * other time events with higher priority.
+ */
+ if (vif->bss_conf.assoc) {
+ delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY);
+ /* We cannot remain off-channel longer than the DTIM interval */
+ if (dtim_interval <= req_dur) {
+ req_dur = dtim_interval - AUX_ROC_SAFETY_BUFFER;
+ if (req_dur <= AUX_ROC_MIN_DURATION)
+ req_dur = dtim_interval -
+ AUX_ROC_MIN_SAFETY_BUFFER;
+ }
+ }
+
+ aux_roc_req.duration = cpu_to_le32(req_dur);
+ aux_roc_req.apply_time_max_delay = cpu_to_le32(delay);
+
+ IWL_DEBUG_TE(mvm,
+ "ROC: Requesting to remain on channel %u for %ums (requested = %ums, max_delay = %ums, dtim_interval = %ums)\n",
+ channel->hw_value, req_dur, duration, delay,
+ dtim_interval);
+ /* Set the node address */
+ memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ spin_lock_bh(&mvm->time_event_lock);
+
+ if (WARN_ON(te_data->id == HOT_SPOT_CMD)) {
+ spin_unlock_bh(&mvm->time_event_lock);
+ return -EIO;
+ }
+
+ te_data->vif = vif;
+ te_data->duration = duration;
+ te_data->id = HOT_SPOT_CMD;
+
+ spin_unlock_bh(&mvm->time_event_lock);
+
+ /*
+ * Use a notification wait, which really just processes the
+ * command response and doesn't wait for anything, in order
+ * to be able to process the response and get the UID inside
+ * the RX path. Using CMD_WANT_SKB doesn't work because it
+ * stores the buffer and then wakes up this thread, by which
+ * time another notification (that the time event started)
+ * might already be processed unsuccessfully.
+ */
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
+ time_event_response,
+ ARRAY_SIZE(time_event_response),
+ iwl_mvm_rx_aux_roc, te_data);
+
+ res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req),
+ &aux_roc_req);
+
+ if (res) {
+ IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res);
+ iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
+ goto out_clear_te;
+ }
+
+ /* No need to wait for anything, so just pass 1 (0 isn't valid) */
+ res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
+ /* should never fail */
+ WARN_ON_ONCE(res);
+
+ if (res) {
+ out_clear_te:
+ spin_lock_bh(&mvm->time_event_lock);
+ iwl_mvm_te_clear_data(mvm, te_data);
+ spin_unlock_bh(&mvm->time_event_lock);
+ }
+
+ return res;
+}
+
+static int iwl_mvm_roc(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *channel,
+ int duration,
+ enum ieee80211_roc_type type)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct cfg80211_chan_def chandef;
+ struct iwl_mvm_phy_ctxt *phy_ctxt;
+ int ret, i;
+
+ IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+ duration, type);
+
+ flush_work(&mvm->roc_done_wk);
+
+ mutex_lock(&mvm->mutex);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) {
+ /* Use aux roc framework (HS20) */
+ ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
+ vif, duration);
+ goto out_unlock;
+ }
+ IWL_ERR(mvm, "hotspot not supported\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* handle below */
+ break;
+ default:
+ IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ for (i = 0; i < NUM_PHY_CTX; i++) {
+ phy_ctxt = &mvm->phy_ctxts[i];
+ if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
+ continue;
+
+ if (phy_ctxt->ref && channel == phy_ctxt->channel) {
+ /*
+ * Unbind the P2P_DEVICE from the current PHY context,
+ * and if the PHY context is not used remove it.
+ */
+ ret = iwl_mvm_binding_remove_vif(mvm, vif);
+ if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+ /* Bind the P2P_DEVICE to the current PHY Context */
+ mvmvif->phy_ctxt = phy_ctxt;
+
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+ goto schedule_time_event;
+ }
+ }
+
+ /* Need to update the PHY context only if the ROC channel changed */
+ if (channel == mvmvif->phy_ctxt->channel)
+ goto schedule_time_event;
+
+ cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
+
+ /*
+ * Change the PHY context configuration as it is currently referenced
+ * only by the P2P Device MAC
+ */
+ if (mvmvif->phy_ctxt->ref == 1) {
+ ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
+ &chandef, 1, 1);
+ if (ret)
+ goto out_unlock;
+ } else {
+ /*
+ * The PHY context is shared with other MACs. Need to remove the
+ * P2P Device from the binding, allocate an new PHY context and
+ * create a new binding
+ */
+ phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+ if (!phy_ctxt) {
+ ret = -ENOSPC;
+ goto out_unlock;
+ }
+
+ ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef,
+ 1, 1);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to change PHY context\n");
+ goto out_unlock;
+ }
+
+ /* Unbind the P2P_DEVICE from the current PHY context */
+ ret = iwl_mvm_binding_remove_vif(mvm, vif);
+ if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+ /* Bind the P2P_DEVICE to the new allocated PHY context */
+ mvmvif->phy_ctxt = phy_ctxt;
+
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+ }
+
+schedule_time_event:
+ /* Schedule the time events */
+ ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
+
+out_unlock:
+ mutex_unlock(&mvm->mutex);
+ IWL_DEBUG_MAC80211(mvm, "leave\n");
+ return ret;
+}
+
+static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ IWL_DEBUG_MAC80211(mvm, "enter\n");
+
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_stop_roc(mvm);
+ mutex_unlock(&mvm->mutex);
+
+ IWL_DEBUG_MAC80211(mvm, "leave\n");
+ return 0;
+}
+
+static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
+
+ phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+ if (!phy_ctxt) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
+ ctx->rx_chains_static,
+ ctx->rx_chains_dynamic);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to add PHY context\n");
+ goto out;
+ }
+
+ iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
+ *phy_ctxt_id = phy_ctxt->id;
+out:
+ return ret;
+}
+
+static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ ret = __iwl_mvm_add_chanctx(mvm, ctx);
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+
+ lockdep_assert_held(&mvm->mutex);
+
+ iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
+}
+
+static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_remove_chanctx(mvm, ctx);
+ mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx,
+ u32 changed)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+
+ if (WARN_ONCE((phy_ctxt->ref > 1) &&
+ (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
+ IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
+ IEEE80211_CHANCTX_CHANGE_RADAR |
+ IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)),
+ "Cannot change PHY. Ref=%d, changed=0x%X\n",
+ phy_ctxt->ref, changed))
+ return;
+
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_bt_coex_vif_change(mvm);
+ iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
+ ctx->rx_chains_static,
+ ctx->rx_chains_dynamic);
+ mutex_unlock(&mvm->mutex);
+}
+
+static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx,
+ bool switching_chanctx)
+{
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ mvmvif->phy_ctxt = phy_ctxt;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ /* only needed if we're switching chanctx (i.e. during CSA) */
+ if (switching_chanctx) {
+ mvmvif->ap_ibss_active = true;
+ break;
+ }
+ case NL80211_IFTYPE_ADHOC:
+ /*
+ * The AP binding flow is handled as part of the start_ap flow
+ * (in bss_info_changed), similarly for IBSS.
+ */
+ ret = 0;
+ goto out;
+ case NL80211_IFTYPE_STATION:
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ /* always disable PS when a monitor interface is active */
+ mvmvif->ps_disabled = true;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (ret)
+ goto out;
+
+ /*
+ * Power state must be updated before quotas,
+ * otherwise fw will complain.
+ */
+ iwl_mvm_power_update_mac(mvm);
+
+ /* Setting the quota at this stage is only required for monitor
+ * interfaces. For the other types, the bss_info changed flow
+ * will handle quota settings.
+ */
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+ mvmvif->monitor_active = true;
+ ret = iwl_mvm_update_quotas(mvm, false, NULL);
+ if (ret)
+ goto out_remove_binding;
+
+ ret = iwl_mvm_add_snif_sta(mvm, vif);
+ if (ret)
+ goto out_remove_binding;
+
+ }
+
+ /* Handle binding during CSA */
+ if (vif->type == NL80211_IFTYPE_AP) {
+ iwl_mvm_update_quotas(mvm, false, NULL);
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ }
+
+ if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
+ u32 duration = 2 * vif->bss_conf.beacon_int;
+
+ /* iwl_mvm_protect_session() reads directly from the
+ * device (the system time), so make sure it is
+ * available.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
+ if (ret)
+ goto out_remove_binding;
+
+ /* Protect the session to make sure we hear the first
+ * beacon on the new channel.
+ */
+ iwl_mvm_protect_session(mvm, vif, duration, duration,
+ vif->bss_conf.beacon_int / 2,
+ true);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+
+ iwl_mvm_update_quotas(mvm, false, NULL);
+ }
+
+ goto out;
+
+out_remove_binding:
+ iwl_mvm_binding_remove_vif(mvm, vif);
+ iwl_mvm_power_update_mac(mvm);
+out:
+ if (ret)
+ mvmvif->phy_ctxt = NULL;
+ return ret;
+}
+static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx,
+ bool switching_chanctx)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_vif *disabled_vif = NULL;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_ADHOC:
+ goto out;
+ case NL80211_IFTYPE_MONITOR:
+ mvmvif->monitor_active = false;
+ mvmvif->ps_disabled = false;
+ iwl_mvm_rm_snif_sta(mvm, vif);
+ break;
+ case NL80211_IFTYPE_AP:
+ /* This part is triggered only during CSA */
+ if (!switching_chanctx || !mvmvif->ap_ibss_active)
+ goto out;
+
+ mvmvif->csa_countdown = false;
+
+ /* Set CS bit on all the stations */
+ iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
+
+ /* Save blocked iface, the timeout is set on the next beacon */
+ rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
+
+ mvmvif->ap_ibss_active = false;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!switching_chanctx)
+ break;
+
+ disabled_vif = vif;
+
+ iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
+ break;
+ default:
+ break;
+ }
+
+ iwl_mvm_update_quotas(mvm, false, disabled_vif);
+ iwl_mvm_binding_remove_vif(mvm, vif);
+
+out:
+ mvmvif->phy_ctxt = NULL;
+ iwl_mvm_power_update_mac(mvm);
+}
+
+static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
+ mutex_unlock(&mvm->mutex);
+}
+
+static int
+iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
+ struct ieee80211_vif_chanctx_switch *vifs)
+{
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+ __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
+
+ ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
+ if (ret) {
+ IWL_ERR(mvm, "failed to add new_ctx during channel switch\n");
+ goto out_reassign;
+ }
+
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+ true);
+ if (ret) {
+ IWL_ERR(mvm,
+ "failed to assign new_ctx during channel switch\n");
+ goto out_remove;
+ }
+
+ /* we don't support TDLS during DCM - can be caused by channel switch */
+ if (iwl_mvm_phy_ctx_count(mvm) > 1)
+ iwl_mvm_teardown_tdls_peers(mvm);
+
+ goto out;
+
+out_remove:
+ __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
+
+out_reassign:
+ if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) {
+ IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
+ goto out_restart;
+ }
+
+ if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+ true)) {
+ IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+ goto out_restart;
+ }
+
+ goto out;
+
+out_restart:
+ /* things keep failing, better restart the hw */
+ iwl_mvm_nic_restart(mvm, false);
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int
+iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
+ struct ieee80211_vif_chanctx_switch *vifs)
+{
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+ true);
+ if (ret) {
+ IWL_ERR(mvm,
+ "failed to assign new_ctx during channel switch\n");
+ goto out_reassign;
+ }
+
+ goto out;
+
+out_reassign:
+ if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+ true)) {
+ IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+ goto out_restart;
+ }
+
+ goto out;
+
+out_restart:
+ /* things keep failing, better restart the hw */
+ iwl_mvm_nic_restart(mvm, false);
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ /* we only support a single-vif right now */
+ if (n_vifs > 1)
+ return -EOPNOTSUPP;
+
+ switch (mode) {
+ case CHANCTX_SWMODE_SWAP_CONTEXTS:
+ ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
+ break;
+ case CHANCTX_SWMODE_REASSIGN_VIF:
+ ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ bool set)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+ if (!mvm_sta || !mvm_sta->vif) {
+ IWL_ERR(mvm, "Station is not associated to a vif\n");
+ return -EINVAL;
+ }
+
+ return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
+}
+
+#ifdef CONFIG_NL80211_TESTMODE
+static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
+ [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
+ [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
+ [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
+};
+
+static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ void *data, int len)
+{
+ struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
+ int err;
+ u32 noa_duration;
+
+ err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy);
+ if (err)
+ return err;
+
+ if (!tb[IWL_MVM_TM_ATTR_CMD])
+ return -EINVAL;
+
+ switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
+ case IWL_MVM_TM_CMD_SET_NOA:
+ if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
+ !vif->bss_conf.enable_beacon ||
+ !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
+ return -EINVAL;
+
+ noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
+ if (noa_duration >= vif->bss_conf.beacon_int)
+ return -EINVAL;
+
+ mvm->noa_duration = noa_duration;
+ mvm->noa_vif = vif;
+
+ return iwl_mvm_update_quotas(mvm, false, NULL);
+ case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
+ /* must be associated client vif - ignore authorized */
+ if (!vif || vif->type != NL80211_IFTYPE_STATION ||
+ !vif->bss_conf.assoc || !vif->bss_conf.dtim_period ||
+ !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
+ return -EINVAL;
+
+ if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
+ return iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+ return iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void *data, int len)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int err;
+
+ mutex_lock(&mvm->mutex);
+ err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
+ mutex_unlock(&mvm->mutex);
+
+ return err;
+}
+#endif
+
+static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ /* By implementing this operation, we prevent mac80211 from
+ * starting its own channel switch timer, so that we can call
+ * ieee80211_chswitch_done() ourselves at the right time
+ * (which is when the absence time event starts).
+ */
+
+ IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw),
+ "dummy channel switch op\n");
+}
+
+static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_vif *csa_vif;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u32 apply_time;
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ mvmvif->csa_failed = false;
+
+ IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
+ chsw->chandef.center_freq1);
+
+ iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ csa_vif =
+ rcu_dereference_protected(mvm->csa_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (WARN_ONCE(csa_vif && csa_vif->csa_active,
+ "Another CSA is already in progress")) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ rcu_assign_pointer(mvm->csa_vif, vif);
+
+ if (WARN_ONCE(mvmvif->csa_countdown,
+ "Previous CSA countdown didn't complete")) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ break;
+ case NL80211_IFTYPE_STATION:
+ /* Schedule the time event to a bit before beacon 1,
+ * to make sure we're in the new channel when the
+ * GO/AP arrives.
+ */
+ apply_time = chsw->device_timestamp +
+ ((vif->bss_conf.beacon_int * (chsw->count - 1) -
+ IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024);
+
+ if (chsw->block_tx)
+ iwl_mvm_csa_client_absent(mvm, vif);
+
+ iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
+ apply_time);
+ if (mvmvif->bf_data.bf_enabled) {
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ goto out_unlock;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ mvmvif->ps_disabled = true;
+
+ ret = iwl_mvm_power_update_ps(mvm);
+ if (ret)
+ goto out_unlock;
+
+ /* we won't be on this channel any longer */
+ iwl_mvm_teardown_tdls_peers(mvm);
+
+out_unlock:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ if (mvmvif->csa_failed) {
+ mvmvif->csa_failed = false;
+ ret = -EIO;
+ goto out_unlock;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ struct iwl_mvm_sta *mvmsta;
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
+ mvmvif->ap_sta_id);
+
+ if (WARN_ON(!mvmsta)) {
+ ret = -EIO;
+ goto out_unlock;
+ }
+
+ iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ goto out_unlock;
+
+ iwl_mvm_stop_session_protection(mvm, vif);
+ }
+
+ mvmvif->ps_disabled = false;
+
+ ret = iwl_mvm_power_update_ps(mvm);
+
+out_unlock:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u32 queues, bool drop)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif;
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_sta *sta;
+ int i;
+ u32 msk = 0;
+
+ if (!vif || vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ mutex_lock(&mvm->mutex);
+ mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ /* flush the AP-station and all TDLS peers */
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta))
+ continue;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvmsta->vif != vif)
+ continue;
+
+ /* make sure only TDLS peers or the AP are flushed */
+ WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
+
+ msk |= mvmsta->tfd_queue_msk;
+ }
+
+ if (drop) {
+ if (iwl_mvm_flush_tx_path(mvm, msk, 0))
+ IWL_ERR(mvm, "flush request fail\n");
+ mutex_unlock(&mvm->mutex);
+ } else {
+ mutex_unlock(&mvm->mutex);
+
+ /* this can take a while, and we may need/want other operations
+ * to succeed while doing this, so do it without the mutex held
+ */
+ iwl_trans_wait_tx_queue_empty(mvm->trans, msk);
+ }
+}
+
+static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ memset(survey, 0, sizeof(*survey));
+
+ /* only support global statistics right now */
+ if (idx != 0)
+ return -ENOENT;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+ return -ENOENT;
+
+ mutex_lock(&mvm->mutex);
+
+ if (mvm->ucode_loaded) {
+ ret = iwl_mvm_request_statistics(mvm, false);
+ if (ret)
+ goto out;
+ }
+
+ survey->filled = SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_RX |
+ SURVEY_INFO_TIME_TX |
+ SURVEY_INFO_TIME_SCAN;
+ survey->time = mvm->accu_radio_stats.on_time_rf +
+ mvm->radio_stats.on_time_rf;
+ do_div(survey->time, USEC_PER_MSEC);
+
+ survey->time_rx = mvm->accu_radio_stats.rx_time +
+ mvm->radio_stats.rx_time;
+ do_div(survey->time_rx, USEC_PER_MSEC);
+
+ survey->time_tx = mvm->accu_radio_stats.tx_time +
+ mvm->radio_stats.tx_time;
+ do_div(survey->time_tx, USEC_PER_MSEC);
+
+ survey->time_scan = mvm->accu_radio_stats.on_time_scan +
+ mvm->radio_stats.on_time_scan;
+ do_div(survey->time_scan, USEC_PER_MSEC);
+
+ ret = 0;
+ out:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct station_info *sinfo)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+ return;
+
+ /* if beacon filtering isn't on mac80211 does it anyway */
+ if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
+ return;
+
+ if (!vif->bss_conf.assoc)
+ return;
+
+ mutex_lock(&mvm->mutex);
+
+ if (mvmvif->ap_sta_id != mvmsta->sta_id)
+ goto unlock;
+
+ if (iwl_mvm_request_statistics(mvm, false))
+ goto unlock;
+
+ sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons +
+ mvmvif->beacon_stats.accu_num_beacons;
+ sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX);
+ if (mvmvif->beacon_stats.avg_signal) {
+ /* firmware only reports a value after RXing a few beacons */
+ sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal;
+ sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
+ }
+ unlock:
+ mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event)
+{
+#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \
+ do { \
+ if ((_cnt) && --(_cnt)) \
+ break; \
+ iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\
+ } while (0)
+
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+ trig_mlme = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ return;
+
+ if (event->u.mlme.data == ASSOC_EVENT) {
+ if (event->u.mlme.status == MLME_DENIED)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_assoc_denied,
+ "DENIED ASSOC: reason %d",
+ event->u.mlme.reason);
+ else if (event->u.mlme.status == MLME_TIMEOUT)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_assoc_timeout,
+ "ASSOC TIMEOUT");
+ } else if (event->u.mlme.data == AUTH_EVENT) {
+ if (event->u.mlme.status == MLME_DENIED)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_auth_denied,
+ "DENIED AUTH: reason %d",
+ event->u.mlme.reason);
+ else if (event->u.mlme.status == MLME_TIMEOUT)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_auth_timeout,
+ "AUTH TIMEOUT");
+ } else if (event->u.mlme.data == DEAUTH_RX_EVENT) {
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_rx_deauth,
+ "DEAUTH RX %d", event->u.mlme.reason);
+ } else if (event->u.mlme.data == DEAUTH_TX_EVENT) {
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_tx_deauth,
+ "DEAUTH TX %d", event->u.mlme.reason);
+ }
+#undef CHECK_MLME_TRIGGER
+}
+
+static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_ba *ba_trig;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
+ ba_trig = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ return;
+
+ if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid)))
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "BAR received from %pM, tid %d, ssn %d",
+ event->u.ba.sta->addr, event->u.ba.tid,
+ event->u.ba.ssn);
+}
+
+static void
+iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_ba *ba_trig;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
+ ba_trig = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ return;
+
+ if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid)))
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "Frame from %pM timed out, tid %d",
+ event->u.ba.sta->addr, event->u.ba.tid);
+}
+
+static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ switch (event->type) {
+ case MLME_EVENT:
+ iwl_mvm_event_mlme_callback(mvm, vif, event);
+ break;
+ case BAR_RX_EVENT:
+ iwl_mvm_event_bar_rx_callback(mvm, vif, event);
+ break;
+ case BA_FRAME_TIMEOUT:
+ iwl_mvm_event_frame_timeout_callback(mvm, vif, event);
+ break;
+ default:
+ break;
+ }
+}
+
+const struct ieee80211_ops iwl_mvm_hw_ops = {
+ .tx = iwl_mvm_mac_tx,
+ .ampdu_action = iwl_mvm_mac_ampdu_action,
+ .start = iwl_mvm_mac_start,
+ .reconfig_complete = iwl_mvm_mac_reconfig_complete,
+ .stop = iwl_mvm_mac_stop,
+ .add_interface = iwl_mvm_mac_add_interface,
+ .remove_interface = iwl_mvm_mac_remove_interface,
+ .config = iwl_mvm_mac_config,
+ .prepare_multicast = iwl_mvm_prepare_multicast,
+ .configure_filter = iwl_mvm_configure_filter,
+ .config_iface_filter = iwl_mvm_config_iface_filter,
+ .bss_info_changed = iwl_mvm_bss_info_changed,
+ .hw_scan = iwl_mvm_mac_hw_scan,
+ .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,
+ .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove,
+ .sta_state = iwl_mvm_mac_sta_state,
+ .sta_notify = iwl_mvm_mac_sta_notify,
+ .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
+ .release_buffered_frames = iwl_mvm_mac_release_buffered_frames,
+ .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
+ .sta_rc_update = iwl_mvm_sta_rc_update,
+ .conf_tx = iwl_mvm_mac_conf_tx,
+ .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+ .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
+ .flush = iwl_mvm_mac_flush,
+ .sched_scan_start = iwl_mvm_mac_sched_scan_start,
+ .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
+ .set_key = iwl_mvm_mac_set_key,
+ .update_tkip_key = iwl_mvm_mac_update_tkip_key,
+ .remain_on_channel = iwl_mvm_roc,
+ .cancel_remain_on_channel = iwl_mvm_cancel_roc,
+ .add_chanctx = iwl_mvm_add_chanctx,
+ .remove_chanctx = iwl_mvm_remove_chanctx,
+ .change_chanctx = iwl_mvm_change_chanctx,
+ .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
+ .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
+ .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx,
+
+ .start_ap = iwl_mvm_start_ap_ibss,
+ .stop_ap = iwl_mvm_stop_ap_ibss,
+ .join_ibss = iwl_mvm_start_ap_ibss,
+ .leave_ibss = iwl_mvm_stop_ap_ibss,
+
+ .set_tim = iwl_mvm_set_tim,
+
+ .channel_switch = iwl_mvm_channel_switch,
+ .pre_channel_switch = iwl_mvm_pre_channel_switch,
+ .post_channel_switch = iwl_mvm_post_channel_switch,
+
+ .tdls_channel_switch = iwl_mvm_tdls_channel_switch,
+ .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
+ .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
+
+ .event_callback = iwl_mvm_mac_event_callback,
+
+ CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
+
+#ifdef CONFIG_PM_SLEEP
+ /* look at d3.c */
+ .suspend = iwl_mvm_suspend,
+ .resume = iwl_mvm_resume,
+ .set_wakeup = iwl_mvm_set_wakeup,
+ .set_rekey_data = iwl_mvm_set_rekey_data,
+#if IS_ENABLED(CONFIG_IPV6)
+ .ipv6_addr_change = iwl_mvm_ipv6_addr_change,
+#endif
+ .set_default_unicast_key = iwl_mvm_set_default_unicast_key,
+#endif
+ .get_survey = iwl_mvm_mac_get_survey,
+ .sta_statistics = iwl_mvm_mac_sta_statistics,
+};
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
new file mode 100644
index 000000000000..5f3ac8cccf49
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -0,0 +1,1543 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_MVM_H__
+#define __IWL_MVM_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include <linux/in6.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+#include "iwl-notif-wait.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-fw-file.h"
+#include "iwl-config.h"
+#include "sta.h"
+#include "fw-api.h"
+#include "constants.h"
+#include "tof.h"
+
+#define IWL_MVM_MAX_ADDRESSES 5
+/* RSSI offset for WkP */
+#define IWL_RSSI_OFFSET 50
+#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
+/* A TimeUnit is 1024 microsecond */
+#define MSEC_TO_TU(_msec) (_msec*1000/1024)
+
+/* For GO, this value represents the number of TUs before CSA "beacon
+ * 0" TBTT when the CSA time-event needs to be scheduled to start. It
+ * must be big enough to ensure that we switch in time.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40
+
+/* For client, this value represents the number of TUs before CSA
+ * "beacon 1" TBTT, instead. This is because we don't know when the
+ * GO/AP will be in the new channel, so we switch early enough.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 10
+
+/*
+ * This value (in TUs) is used to fine tune the CSA NoA end time which should
+ * be just before "beacon 0" TBTT.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
+
+/*
+ * Number of beacons to transmit on a new channel until we unblock tx to
+ * the stations, even if we didn't identify them on a new channel
+ */
+#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
+
+extern const struct ieee80211_ops iwl_mvm_hw_ops;
+
+/**
+ * struct iwl_mvm_mod_params - module parameters for iwlmvm
+ * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
+ * We will register to mac80211 to have testmode working. The NIC must not
+ * be up'ed after the INIT fw asserted. This is useful to be able to use
+ * proprietary tools over testmode to debug the INIT fw.
+ * @tfd_q_hang_detect: enabled the detection of hung transmit queues
+ * @power_scheme: one of enum iwl_power_scheme
+ */
+struct iwl_mvm_mod_params {
+ bool init_dbg;
+ bool tfd_q_hang_detect;
+ int power_scheme;
+};
+extern struct iwl_mvm_mod_params iwlmvm_mod_params;
+
+/**
+ * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump
+ *
+ * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode
+ * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the
+ * transport's data.
+ * @trans_len: length of the valid data in trans_ptr
+ * @op_mode_len: length of the valid data in op_mode_ptr
+ */
+struct iwl_mvm_dump_ptrs {
+ struct iwl_trans_dump_data *trans_ptr;
+ void *op_mode_ptr;
+ u32 op_mode_len;
+};
+
+/**
+ * struct iwl_mvm_dump_desc - describes the dump
+ * @len: length of trig_desc->data
+ * @trig_desc: the description of the dump
+ */
+struct iwl_mvm_dump_desc {
+ size_t len;
+ /* must be last */
+ struct iwl_fw_error_dump_trigger_desc trig_desc;
+};
+
+extern const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert;
+
+struct iwl_mvm_phy_ctxt {
+ u16 id;
+ u16 color;
+ u32 ref;
+
+ /*
+ * TODO: This should probably be removed. Currently here only for rate
+ * scaling algorithm
+ */
+ struct ieee80211_channel *channel;
+};
+
+struct iwl_mvm_time_event_data {
+ struct ieee80211_vif *vif;
+ struct list_head list;
+ unsigned long end_jiffies;
+ u32 duration;
+ bool running;
+ u32 uid;
+
+ /*
+ * The access to the 'id' field must be done when the
+ * mvm->time_event_lock is held, as it value is used to indicate
+ * if the te is in the time event list or not (when id == TE_MAX)
+ */
+ u32 id;
+};
+
+ /* Power management */
+
+/**
+ * enum iwl_power_scheme
+ * @IWL_POWER_LEVEL_CAM - Continuously Active Mode
+ * @IWL_POWER_LEVEL_BPS - Balanced Power Save (default)
+ * @IWL_POWER_LEVEL_LP - Low Power
+ */
+enum iwl_power_scheme {
+ IWL_POWER_SCHEME_CAM = 1,
+ IWL_POWER_SCHEME_BPS,
+ IWL_POWER_SCHEME_LP
+};
+
+#define IWL_CONN_MAX_LISTEN_INTERVAL 10
+#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+enum iwl_dbgfs_pm_mask {
+ MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
+ MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1),
+ MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2),
+ MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3),
+ MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4),
+ MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
+ MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
+ MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
+ MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9),
+ MVM_DEBUGFS_PM_USE_PS_POLL = BIT(10),
+};
+
+struct iwl_dbgfs_pm {
+ u16 keep_alive_seconds;
+ u32 rx_data_timeout;
+ u32 tx_data_timeout;
+ bool skip_over_dtim;
+ u8 skip_dtim_periods;
+ bool lprx_ena;
+ u32 lprx_rssi_threshold;
+ bool snooze_ena;
+ bool uapsd_misbehaving;
+ bool use_ps_poll;
+ int mask;
+};
+
+/* beacon filtering */
+
+enum iwl_dbgfs_bf_mask {
+ MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0),
+ MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1),
+ MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2),
+ MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3),
+ MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4),
+ MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5),
+ MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6),
+ MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7),
+ MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8),
+ MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9),
+ MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10),
+};
+
+struct iwl_dbgfs_bf {
+ u32 bf_energy_delta;
+ u32 bf_roaming_energy_delta;
+ u32 bf_roaming_state;
+ u32 bf_temp_threshold;
+ u32 bf_temp_fast_filter;
+ u32 bf_temp_slow_filter;
+ u32 bf_enable_beacon_filter;
+ u32 bf_debug_flag;
+ u32 bf_escape_timer;
+ u32 ba_escape_timer;
+ u32 ba_enable_beacon_abort;
+ int mask;
+};
+#endif
+
+enum iwl_mvm_smps_type_request {
+ IWL_MVM_SMPS_REQ_BT_COEX,
+ IWL_MVM_SMPS_REQ_TT,
+ IWL_MVM_SMPS_REQ_PROT,
+ NUM_IWL_MVM_SMPS_REQ,
+};
+
+enum iwl_mvm_ref_type {
+ IWL_MVM_REF_UCODE_DOWN,
+ IWL_MVM_REF_SCAN,
+ IWL_MVM_REF_ROC,
+ IWL_MVM_REF_ROC_AUX,
+ IWL_MVM_REF_P2P_CLIENT,
+ IWL_MVM_REF_AP_IBSS,
+ IWL_MVM_REF_USER,
+ IWL_MVM_REF_TX,
+ IWL_MVM_REF_TX_AGG,
+ IWL_MVM_REF_ADD_IF,
+ IWL_MVM_REF_START_AP,
+ IWL_MVM_REF_BSS_CHANGED,
+ IWL_MVM_REF_PREPARE_TX,
+ IWL_MVM_REF_PROTECT_TDLS,
+ IWL_MVM_REF_CHECK_CTKILL,
+ IWL_MVM_REF_PRPH_READ,
+ IWL_MVM_REF_PRPH_WRITE,
+ IWL_MVM_REF_NMI,
+ IWL_MVM_REF_TM_CMD,
+ IWL_MVM_REF_EXIT_WORK,
+ IWL_MVM_REF_PROTECT_CSA,
+ IWL_MVM_REF_FW_DBG_COLLECT,
+ IWL_MVM_REF_INIT_UCODE,
+
+ /* update debugfs.c when changing this */
+
+ IWL_MVM_REF_COUNT,
+};
+
+enum iwl_bt_force_ant_mode {
+ BT_FORCE_ANT_DIS = 0,
+ BT_FORCE_ANT_AUTO,
+ BT_FORCE_ANT_BT,
+ BT_FORCE_ANT_WIFI,
+
+ BT_FORCE_ANT_MAX,
+};
+
+/**
+* struct iwl_mvm_vif_bf_data - beacon filtering related data
+* @bf_enabled: indicates if beacon filtering is enabled
+* @ba_enabled: indicated if beacon abort is enabled
+* @ave_beacon_signal: average beacon signal
+* @last_cqm_event: rssi of the last cqm event
+* @bt_coex_min_thold: minimum threshold for BT coex
+* @bt_coex_max_thold: maximum threshold for BT coex
+* @last_bt_coex_event: rssi of the last BT coex event
+*/
+struct iwl_mvm_vif_bf_data {
+ bool bf_enabled;
+ bool ba_enabled;
+ int ave_beacon_signal;
+ int last_cqm_event;
+ int bt_coex_min_thold;
+ int bt_coex_max_thold;
+ int last_bt_coex_event;
+};
+
+/**
+ * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
+ * @id: between 0 and 3
+ * @color: to solve races upon MAC addition and removal
+ * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA
+ * @bssid: BSSID for this (client) interface
+ * @associated: indicates that we're currently associated, used only for
+ * managing the firmware state in iwl_mvm_bss_info_changed_station()
+ * @ap_assoc_sta_count: count of stations associated to us - valid only
+ * if VIF type is AP
+ * @uploaded: indicates the MAC context has been added to the device
+ * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
+ * should get quota etc.
+ * @pm_enabled - Indicate if MAC power management is allowed
+ * @monitor_active: indicates that monitor context is configured, and that the
+ * interface should get quota etc.
+ * @low_latency: indicates that this interface is in low-latency mode
+ * (VMACLowLatencyMode)
+ * @ps_disabled: indicates that this interface requires PS to be disabled
+ * @queue_params: QoS params for this MAC
+ * @bcast_sta: station used for broadcast packets. Used by the following
+ * vifs: P2P_DEVICE, GO and AP.
+ * @beacon_skb: the skb used to hold the AP/GO beacon template
+ * @smps_requests: the SMPS requests of different parts of the driver,
+ * combined on update to yield the overall request to mac80211.
+ * @beacon_stats: beacon statistics, containing the # of received beacons,
+ * # of received beacons accumulated over FW restart, and the current
+ * average signal of beacons retrieved from the firmware
+ * @csa_failed: CSA failed to schedule time event, report an error later
+ * @features: hw features active for this vif
+ */
+struct iwl_mvm_vif {
+ struct iwl_mvm *mvm;
+ u16 id;
+ u16 color;
+ u8 ap_sta_id;
+
+ u8 bssid[ETH_ALEN];
+ bool associated;
+ u8 ap_assoc_sta_count;
+
+ bool uploaded;
+ bool ap_ibss_active;
+ bool pm_enabled;
+ bool monitor_active;
+ bool low_latency;
+ bool ps_disabled;
+ struct iwl_mvm_vif_bf_data bf_data;
+
+ struct {
+ u32 num_beacons, accu_num_beacons;
+ u8 avg_signal;
+ } beacon_stats;
+
+ u32 ap_beacon_time;
+
+ enum iwl_tsf_id tsf_id;
+
+ /*
+ * QoS data from mac80211, need to store this here
+ * as mac80211 has a separate callback but we need
+ * to have the data for the MAC context
+ */
+ struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ struct iwl_mvm_time_event_data time_event_data;
+ struct iwl_mvm_time_event_data hs_time_event_data;
+
+ struct iwl_mvm_int_sta bcast_sta;
+
+ /*
+ * Assigned while mac80211 has the interface in a channel context,
+ * or, for P2P Device, while it exists.
+ */
+ struct iwl_mvm_phy_ctxt *phy_ctxt;
+
+#ifdef CONFIG_PM
+ /* WoWLAN GTK rekey data */
+ struct {
+ u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
+ __le64 replay_ctr;
+ bool valid;
+ } rekey_data;
+
+ int tx_key_idx;
+
+ bool seqno_valid;
+ u16 seqno;
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+ /* IPv6 addresses for WoWLAN */
+ struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
+ unsigned long tentative_addrs[BITS_TO_LONGS(IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)];
+ int num_target_ipv6_addrs;
+#endif
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct dentry *dbgfs_dir;
+ struct dentry *dbgfs_slink;
+ struct iwl_dbgfs_pm dbgfs_pm;
+ struct iwl_dbgfs_bf dbgfs_bf;
+ struct iwl_mac_power_cmd mac_pwr_cmd;
+#endif
+
+ enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
+
+ /* FW identified misbehaving AP */
+ u8 uapsd_misbehaving_bssid[ETH_ALEN];
+
+ /* Indicates that CSA countdown may be started */
+ bool csa_countdown;
+ bool csa_failed;
+
+ /* TCP Checksum Offload */
+ netdev_features_t features;
+};
+
+static inline struct iwl_mvm_vif *
+iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
+{
+ return (void *)vif->drv_priv;
+}
+
+extern const u8 tid_to_mac80211_ac[];
+
+#define IWL_MVM_SCAN_STOPPING_SHIFT 8
+
+enum iwl_scan_status {
+ IWL_MVM_SCAN_REGULAR = BIT(0),
+ IWL_MVM_SCAN_SCHED = BIT(1),
+ IWL_MVM_SCAN_NETDETECT = BIT(2),
+
+ IWL_MVM_SCAN_STOPPING_REGULAR = BIT(8),
+ IWL_MVM_SCAN_STOPPING_SCHED = BIT(9),
+ IWL_MVM_SCAN_STOPPING_NETDETECT = BIT(10),
+
+ IWL_MVM_SCAN_REGULAR_MASK = IWL_MVM_SCAN_REGULAR |
+ IWL_MVM_SCAN_STOPPING_REGULAR,
+ IWL_MVM_SCAN_SCHED_MASK = IWL_MVM_SCAN_SCHED |
+ IWL_MVM_SCAN_STOPPING_SCHED,
+ IWL_MVM_SCAN_NETDETECT_MASK = IWL_MVM_SCAN_NETDETECT |
+ IWL_MVM_SCAN_STOPPING_NETDETECT,
+
+ IWL_MVM_SCAN_STOPPING_MASK = 0xff << IWL_MVM_SCAN_STOPPING_SHIFT,
+ IWL_MVM_SCAN_MASK = 0xff,
+};
+
+enum iwl_mvm_scan_type {
+ IWL_SCAN_TYPE_NOT_SET,
+ IWL_SCAN_TYPE_UNASSOC,
+ IWL_SCAN_TYPE_WILD,
+ IWL_SCAN_TYPE_MILD,
+ IWL_SCAN_TYPE_FRAGMENTED,
+};
+
+/**
+ * struct iwl_nvm_section - describes an NVM section in memory.
+ *
+ * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
+ * and saved for later use by the driver. Not all NVM sections are saved
+ * this way, only the needed ones.
+ */
+struct iwl_nvm_section {
+ u16 length;
+ const u8 *data;
+};
+
+/**
+ * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure
+ * @ct_kill_exit: worker to exit thermal kill
+ * @dynamic_smps: Is thermal throttling enabled dynamic_smps?
+ * @tx_backoff: The current thremal throttling tx backoff in uSec.
+ * @min_backoff: The minimal tx backoff due to power restrictions
+ * @params: Parameters to configure the thermal throttling algorithm.
+ * @throttle: Is thermal throttling is active?
+ */
+struct iwl_mvm_tt_mgmt {
+ struct delayed_work ct_kill_exit;
+ bool dynamic_smps;
+ u32 tx_backoff;
+ u32 min_backoff;
+ struct iwl_tt_params params;
+ bool throttle;
+};
+
+#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8
+
+struct iwl_mvm_frame_stats {
+ u32 legacy_frames;
+ u32 ht_frames;
+ u32 vht_frames;
+ u32 bw_20_frames;
+ u32 bw_40_frames;
+ u32 bw_80_frames;
+ u32 bw_160_frames;
+ u32 sgi_frames;
+ u32 ngi_frames;
+ u32 siso_frames;
+ u32 mimo2_frames;
+ u32 agg_frames;
+ u32 ampdu_count;
+ u32 success_frames;
+ u32 fail_frames;
+ u32 last_rates[IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES];
+ int last_frame_idx;
+};
+
+enum {
+ D0I3_DEFER_WAKEUP,
+ D0I3_PENDING_WAKEUP,
+};
+
+#define IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE 0xff
+#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100
+#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200
+
+enum iwl_mvm_tdls_cs_state {
+ IWL_MVM_TDLS_SW_IDLE = 0,
+ IWL_MVM_TDLS_SW_REQ_SENT,
+ IWL_MVM_TDLS_SW_RESP_RCVD,
+ IWL_MVM_TDLS_SW_REQ_RCVD,
+ IWL_MVM_TDLS_SW_ACTIVE,
+};
+
+struct iwl_mvm_shared_mem_cfg {
+ u32 shared_mem_addr;
+ u32 shared_mem_size;
+ u32 sample_buff_addr;
+ u32 sample_buff_size;
+ u32 txfifo_addr;
+ u32 txfifo_size[TX_FIFO_MAX_NUM];
+ u32 rxfifo_size[RX_FIFO_MAX_NUM];
+ u32 page_buff_addr;
+ u32 page_buff_size;
+};
+
+struct iwl_mvm {
+ /* for logger access */
+ struct device *dev;
+
+ struct iwl_trans *trans;
+ const struct iwl_fw *fw;
+ const struct iwl_cfg *cfg;
+ struct iwl_phy_db *phy_db;
+ struct ieee80211_hw *hw;
+
+ /* for protecting access to iwl_mvm */
+ struct mutex mutex;
+ struct list_head async_handlers_list;
+ spinlock_t async_handlers_lock;
+ struct work_struct async_handlers_wk;
+
+ struct work_struct roc_done_wk;
+
+ unsigned long status;
+
+ /*
+ * for beacon filtering -
+ * currently only one interface can be supported
+ */
+ struct iwl_mvm_vif *bf_allowed_vif;
+
+ enum iwl_ucode_type cur_ucode;
+ bool ucode_loaded;
+ bool calibrating;
+ u32 error_event_table;
+ u32 log_event_table;
+ u32 umac_error_event_table;
+ bool support_umac_log;
+ struct iwl_sf_region sf_space;
+
+ u32 ampdu_ref;
+
+ struct iwl_notif_wait_data notif_wait;
+
+ struct mvm_statistics_rx rx_stats;
+
+ struct {
+ u64 rx_time;
+ u64 tx_time;
+ u64 on_time_rf;
+ u64 on_time_scan;
+ } radio_stats, accu_radio_stats;
+
+ struct {
+ /* Map to HW queue */
+ u32 hw_queue_to_mac80211;
+ u8 hw_queue_refcount;
+ bool setup_reserved;
+ u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */
+ } queue_info[IWL_MAX_HW_QUEUES];
+ spinlock_t queue_info_lock; /* For syncing queue mgmt operations */
+ atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
+
+ const char *nvm_file_name;
+ struct iwl_nvm_data *nvm_data;
+ /* NVM sections */
+ struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
+
+ /* Paging section */
+ struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS];
+ u16 num_of_paging_blk;
+ u16 num_of_pages_in_last_blk;
+
+ /* EEPROM MAC addresses */
+ struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
+
+ /* data related to data path */
+ struct iwl_rx_phy_info last_phy_info;
+ struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT];
+ struct work_struct sta_drained_wk;
+ unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
+ atomic_t pending_frames[IWL_MVM_STATION_COUNT];
+ u32 tfd_drained[IWL_MVM_STATION_COUNT];
+ u8 rx_ba_sessions;
+
+ /* configured by mac80211 */
+ u32 rts_threshold;
+
+ /* Scan status, cmd (pre-allocated) and auxiliary station */
+ unsigned int scan_status;
+ void *scan_cmd;
+ struct iwl_mcast_filter_cmd *mcast_filter_cmd;
+ enum iwl_mvm_scan_type scan_type;
+
+ /* max number of simultaneous scans the FW supports */
+ unsigned int max_scans;
+
+ /* ts of the beginning of a non-collect fw dbg data period */
+ unsigned long fw_dbg_non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1];
+
+ /* UMAC scan tracking */
+ u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS];
+
+ /* rx chain antennas set through debugfs for the scan command */
+ u8 scan_rx_ant;
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+ /* broadcast filters to configure for each associated station */
+ const struct iwl_fw_bcast_filter *bcast_filters;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct {
+ bool override;
+ struct iwl_bcast_filter_cmd cmd;
+ } dbgfs_bcast_filtering;
+#endif
+#endif
+
+ /* Internal station */
+ struct iwl_mvm_int_sta aux_sta;
+ struct iwl_mvm_int_sta snif_sta;
+
+ bool last_ebs_successful;
+
+ u8 scan_last_antenna_idx; /* to toggle TX between antennas */
+ u8 mgmt_last_antenna_idx;
+
+ /* last smart fifo state that was successfully sent to firmware */
+ enum iwl_sf_state sf_state;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct dentry *debugfs_dir;
+ u32 dbgfs_sram_offset, dbgfs_sram_len;
+ u32 dbgfs_prph_reg_addr;
+ bool disable_power_off;
+ bool disable_power_off_d3;
+
+ bool scan_iter_notif_enabled;
+
+ struct debugfs_blob_wrapper nvm_hw_blob;
+ struct debugfs_blob_wrapper nvm_sw_blob;
+ struct debugfs_blob_wrapper nvm_calib_blob;
+ struct debugfs_blob_wrapper nvm_prod_blob;
+ struct debugfs_blob_wrapper nvm_phy_sku_blob;
+
+ struct iwl_mvm_frame_stats drv_rx_stats;
+ spinlock_t drv_stats_lock;
+ u16 dbgfs_rx_phyinfo;
+#endif
+
+ struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
+
+ struct list_head time_event_list;
+ spinlock_t time_event_lock;
+
+ /*
+ * A bitmap indicating the index of the key in use. The firmware
+ * can hold 16 keys at most. Reflect this fact.
+ */
+ unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
+ u8 fw_key_deleted[STA_KEY_MAX_NUM];
+
+ /* references taken by the driver and spinlock protecting them */
+ spinlock_t refs_lock;
+ u8 refs[IWL_MVM_REF_COUNT];
+
+ u8 vif_count;
+
+ /* -1 for always, 0 for never, >0 for that many times */
+ s8 restart_fw;
+ u8 fw_dbg_conf;
+ struct delayed_work fw_dump_wk;
+ const struct iwl_mvm_dump_desc *fw_dump_desc;
+ const struct iwl_fw_dbg_trigger_tlv *fw_dump_trig;
+
+#ifdef CONFIG_IWLWIFI_LEDS
+ struct led_classdev led;
+#endif
+
+ struct ieee80211_vif *p2p_device_vif;
+
+#ifdef CONFIG_PM
+ struct wiphy_wowlan_support wowlan;
+ int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
+
+ /* sched scan settings for net detect */
+ struct ieee80211_scan_ies nd_ies;
+ struct cfg80211_match_set *nd_match_sets;
+ int n_nd_match_sets;
+ struct ieee80211_channel **nd_channels;
+ int n_nd_channels;
+ bool net_detect;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ bool d3_wake_sysassert;
+ bool d3_test_active;
+ bool store_d3_resume_sram;
+ void *d3_resume_sram;
+ u32 d3_test_pme_ptr;
+ struct ieee80211_vif *keep_vif;
+ u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */
+#endif
+#endif
+
+ /* d0i3 */
+ u8 d0i3_ap_sta_id;
+ bool d0i3_offloading;
+ struct work_struct d0i3_exit_work;
+ struct sk_buff_head d0i3_tx;
+ /* protect d0i3_suspend_flags */
+ struct mutex d0i3_suspend_mutex;
+ unsigned long d0i3_suspend_flags;
+ /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */
+ spinlock_t d0i3_tx_lock;
+ wait_queue_head_t d0i3_exit_waitq;
+
+ /* BT-Coex */
+ u8 bt_ack_kill_msk[NUM_PHY_CTX];
+ u8 bt_cts_kill_msk[NUM_PHY_CTX];
+
+ struct iwl_bt_coex_profile_notif_old last_bt_notif_old;
+ struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old;
+ struct iwl_bt_coex_profile_notif last_bt_notif;
+ struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
+
+ u32 last_ant_isol;
+ u8 last_corun_lut;
+ u8 bt_tx_prio;
+ enum iwl_bt_force_ant_mode bt_force_ant_mode;
+
+ /* Aux ROC */
+ struct list_head aux_roc_te_list;
+
+ /* Thermal Throttling and CTkill */
+ struct iwl_mvm_tt_mgmt thermal_throttle;
+ s32 temperature; /* Celsius */
+ /*
+ * Debug option to set the NIC temperature. This option makes the
+ * driver think this is the actual NIC temperature, and ignore the
+ * real temperature that is received from the fw
+ */
+ bool temperature_test; /* Debug test temperature is enabled */
+
+ struct iwl_time_quota_cmd last_quota_cmd;
+
+#ifdef CONFIG_NL80211_TESTMODE
+ u32 noa_duration;
+ struct ieee80211_vif *noa_vif;
+#endif
+
+ /* Tx queues */
+ u8 aux_queue;
+ u8 first_agg_queue;
+ u8 last_agg_queue;
+
+ /* Indicate if device power save is allowed */
+ u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */
+
+ struct ieee80211_vif __rcu *csa_vif;
+ struct ieee80211_vif __rcu *csa_tx_blocked_vif;
+ u8 csa_tx_block_bcn_timeout;
+
+ /* system time of last beacon (for AP/GO interface) */
+ u32 ap_last_beacon_gp2;
+
+ bool lar_regdom_set;
+ enum iwl_mcc_source mcc_src;
+
+ /* TDLS channel switch data */
+ struct {
+ struct delayed_work dwork;
+ enum iwl_mvm_tdls_cs_state state;
+
+ /*
+ * Current cs sta - might be different from periodic cs peer
+ * station. Value is meaningless when the cs-state is idle.
+ */
+ u8 cur_sta_id;
+
+ /* TDLS periodic channel-switch peer */
+ struct {
+ u8 sta_id;
+ u8 op_class;
+ bool initiator; /* are we the link initiator */
+ struct cfg80211_chan_def chandef;
+ struct sk_buff *skb; /* ch sw template */
+ u32 ch_sw_tm_ie;
+
+ /* timestamp of last ch-sw request sent (GP2 time) */
+ u32 sent_timestamp;
+ } peer;
+ } tdls_cs;
+
+ struct iwl_mvm_shared_mem_cfg shared_mem_cfg;
+
+ u32 ciphers[6];
+ struct iwl_mvm_tof_data tof_data;
+};
+
+/* Extract MVM priv from op_mode and _hw */
+#define IWL_OP_MODE_GET_MVM(_iwl_op_mode) \
+ ((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific)
+
+#define IWL_MAC80211_GET_MVM(_hw) \
+ IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
+
+enum iwl_mvm_status {
+ IWL_MVM_STATUS_HW_RFKILL,
+ IWL_MVM_STATUS_HW_CTKILL,
+ IWL_MVM_STATUS_ROC_RUNNING,
+ IWL_MVM_STATUS_IN_HW_RESTART,
+ IWL_MVM_STATUS_IN_D0I3,
+ IWL_MVM_STATUS_ROC_AUX_RUNNING,
+ IWL_MVM_STATUS_D3_RECONFIG,
+ IWL_MVM_STATUS_DUMPING_FW_LOG,
+};
+
+static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
+{
+ return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) ||
+ test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+}
+
+static inline bool iwl_mvm_is_radio_hw_killed(struct iwl_mvm *mvm)
+{
+ return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
+}
+
+/* Must be called with rcu_read_lock() held and it can only be
+ * released when mvmsta is not needed anymore.
+ */
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id)
+{
+ struct ieee80211_sta *sta;
+
+ if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+ return NULL;
+
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+ /* This can happen if the station has been removed right now */
+ if (IS_ERR_OR_NULL(sta))
+ return NULL;
+
+ return iwl_mvm_sta_from_mac80211(sta);
+}
+
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
+{
+ struct ieee80211_sta *sta;
+
+ if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+ return NULL;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ /* This can happen if the station has been removed right now */
+ if (IS_ERR_OR_NULL(sta))
+ return NULL;
+
+ return iwl_mvm_sta_from_mac80211(sta);
+}
+
+static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
+{
+ return !iwlwifi_mod_params.d0i3_disable &&
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
+}
+
+static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_DQA_SUPPORT);
+}
+
+static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm)
+{
+ /* For now we only use this mode to differentiate between
+ * slave transports, which handle D0i3 entry in suspend by
+ * themselves in conjunction with runtime PM D0i3. So, this
+ * function is used to check whether we need to do anything
+ * when entering suspend or if the transport layer has already
+ * done it.
+ */
+ return (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) &&
+ (mvm->trans->runtime_pm_mode != IWL_PLAT_PM_MODE_D0I3);
+}
+
+static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
+{
+ bool nvm_lar = mvm->nvm_data->lar_enabled;
+ bool tlv_lar = fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
+
+ if (iwlwifi_mod_params.lar_disable)
+ return false;
+
+ /*
+ * Enable LAR only if it is supported by the FW (TLV) &&
+ * enabled in the NVM
+ */
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ return nvm_lar && tlv_lar;
+ else
+ return tlv_lar;
+}
+
+static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_WIFI_MCC_UPDATE) ||
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC);
+}
+
+static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) &&
+ IWL_MVM_BT_COEX_CORUNNING;
+}
+
+static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BT_COEX_RRC) &&
+ IWL_MVM_BT_COEX_RRC;
+}
+
+static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CSUM_SUPPORT);
+}
+
+static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT) &&
+ IWL_MVM_BT_COEX_MPLUT;
+}
+
+static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm)
+{
+ /* firmware flag isn't defined yet */
+ return false;
+}
+
+extern const u8 iwl_mvm_ac_to_tx_fifo[];
+
+struct iwl_rate_info {
+ u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
+ u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
+ u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
+ u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */
+ u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
+};
+
+void __iwl_mvm_mac_stop(struct iwl_mvm *mvm);
+int __iwl_mvm_mac_start(struct iwl_mvm *mvm);
+
+/******************
+ * MVM Methods
+ ******************/
+/* uCode */
+int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm);
+
+/* Utils */
+int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
+ enum ieee80211_band band);
+void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
+ enum ieee80211_band band,
+ struct ieee80211_tx_rate *r);
+u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
+void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
+u8 first_antenna(u8 mask);
+u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
+
+/* Tx / Host Commands */
+int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm,
+ struct iwl_host_cmd *cmd);
+int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id,
+ u32 flags, u16 len, const void *data);
+int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm,
+ struct iwl_host_cmd *cmd,
+ u32 *status);
+int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id,
+ u16 len, const void *data,
+ u32 *status);
+int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct ieee80211_sta *sta);
+int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info, u8 sta_id);
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, __le16 fc);
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_mvm_get_tx_fail_reason(u32 status);
+#else
+static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; }
+#endif
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags);
+void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
+
+static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info,
+ struct iwl_tx_cmd *tx_cmd)
+{
+ struct ieee80211_key_conf *keyconf = info->control.hw_key;
+
+ tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
+ memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
+ if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG);
+}
+
+static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm)
+{
+ flush_work(&mvm->async_handlers_wk);
+}
+
+/* Statistics */
+void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt);
+void iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear);
+void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm);
+
+/* NVM */
+int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
+int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
+
+static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm)
+{
+ return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ?
+ mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant :
+ mvm->fw->valid_tx_ant;
+}
+
+static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm)
+{
+ return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ?
+ mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant :
+ mvm->fw->valid_rx_ant;
+}
+
+static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm)
+{
+ u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
+ FW_PHY_CFG_RX_CHAIN);
+ u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm);
+ u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
+
+ phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS |
+ valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS;
+
+ return mvm->fw->phy_config & phy_config;
+}
+
+int iwl_mvm_up(struct iwl_mvm *mvm);
+int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
+
+int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
+bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
+ struct iwl_bcast_filter_cmd *cmd);
+
+/*
+ * FW notifications / CMD responses handlers
+ * Convention: iwl_mvm_rx_<NAME OF THE CMD>
+ */
+void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, int queue);
+void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb, int queue);
+void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+
+/* MVM PHY */
+int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+ struct cfg80211_chan_def *chandef,
+ u8 chains_static, u8 chains_dynamic);
+int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+ struct cfg80211_chan_def *chandef,
+ u8 chains_static, u8 chains_dynamic);
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
+ struct iwl_mvm_phy_ctxt *ctxt);
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
+ struct iwl_mvm_phy_ctxt *ctxt);
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef);
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef);
+
+/* MAC (virtual interface) programming */
+int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool force_assoc_off, const u8 *bssid_override);
+int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+ struct ieee80211_vif *exclude_vif);
+/* Bindings */
+int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+/* Quota management */
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload,
+ struct ieee80211_vif *disabled_vif);
+
+/* Scanning */
+int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req,
+ struct ieee80211_scan_ies *ies);
+int iwl_mvm_scan_size(struct iwl_mvm *mvm);
+int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify);
+int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm);
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
+
+/* Scheduled scan */
+void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies,
+ int type);
+void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+
+/* UMAC scan */
+int iwl_mvm_config_scan(struct iwl_mvm *mvm);
+void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+
+/* MVM debugfs */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
+void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+#else
+static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm,
+ struct dentry *dbgfs_dir)
+{
+ return 0;
+}
+static inline void
+iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+}
+static inline void
+iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+}
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+
+/* rate scaling */
+int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
+void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg);
+int rs_pretty_print_rate(char *buf, const u32 rate);
+void rs_update_last_rssi(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_rx_status *rx_status);
+
+/* power management */
+int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm);
+int iwl_mvm_power_update_ps(struct iwl_mvm *mvm);
+int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ char *buf, int bufsz);
+
+void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+
+#ifdef CONFIG_IWLWIFI_LEDS
+int iwl_mvm_leds_init(struct iwl_mvm *mvm);
+void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
+#else
+static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm)
+{
+ return 0;
+}
+static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
+{
+}
+#endif
+
+/* D3 (WoWLAN, NetDetect) */
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
+int iwl_mvm_resume(struct ieee80211_hw *hw);
+void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled);
+void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_gtk_rekey_data *data);
+void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct inet6_dev *idev);
+void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int idx);
+extern const struct file_operations iwl_dbgfs_d3_test_ops;
+#ifdef CONFIG_PM
+int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool host_awake,
+ u32 cmd_flags);
+void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status);
+void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+#else
+static inline int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool host_awake,
+ u32 cmd_flags)
+{
+ return 0;
+}
+
+static inline void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status)
+{
+}
+
+static inline void
+iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+}
+#endif
+void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
+ struct iwl_wowlan_config_cmd *cmd);
+int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool disable_offloading,
+ bool offload_ns,
+ u32 cmd_flags);
+
+/* D0i3 */
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
+void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
+int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode);
+int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode);
+int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
+
+/* BT Coex */
+int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
+void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event_data);
+void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
+u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant);
+bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
+bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
+ enum ieee80211_band band);
+u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *info, u8 ac);
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
+void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event_data);
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+ enum ieee80211_band band);
+void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+
+/* beacon filtering */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd);
+#else
+static inline void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd)
+{}
+#endif
+int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable, u32 flags);
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 flags);
+int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 flags);
+/* SMPS */
+void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum iwl_mvm_smps_type_request req_type,
+ enum ieee80211_smps_mode smps_request);
+bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm);
+
+/* Low latency */
+int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool value);
+/* get SystemLowLatencyMode - only needed for beacon threshold? */
+bool iwl_mvm_low_latency(struct iwl_mvm *mvm);
+/* get VMACLowLatencyMode */
+static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
+{
+ /*
+ * should this consider associated/active/... state?
+ *
+ * Normally low-latency should only be active on interfaces
+ * that are active, but at least with debugfs it can also be
+ * enabled on interfaces that aren't active. However, when
+ * interface aren't active then they aren't added into the
+ * binding, so this has no real impact. For now, just return
+ * the current desired low-latency state.
+ */
+
+ return mvmvif->low_latency;
+}
+
+/* hw scheduler queue config */
+void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+ u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg,
+ unsigned int wdg_timeout);
+/*
+ * Disable a TXQ.
+ * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored.
+ */
+void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+ u8 tid, u8 flags);
+int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq);
+
+/* Return a bitmask with all the hw supported queues, except for the
+ * command queue, which can't be flushed.
+ */
+static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm)
+{
+ return ((BIT(mvm->cfg->base_params->num_of_queues) - 1) &
+ ~BIT(IWL_MVM_CMD_QUEUE));
+}
+
+static inline
+void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+ u8 fifo, u16 ssn, unsigned int wdg_timeout)
+{
+ struct iwl_trans_txq_scd_cfg cfg = {
+ .fifo = fifo,
+ .tid = IWL_MAX_TID_COUNT,
+ .aggregate = false,
+ .frame_limit = IWL_FRAME_LIMIT,
+ };
+
+ iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
+}
+
+static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue,
+ int mac80211_queue, int fifo,
+ int sta_id, int tid, int frame_limit,
+ u16 ssn, unsigned int wdg_timeout)
+{
+ struct iwl_trans_txq_scd_cfg cfg = {
+ .fifo = fifo,
+ .sta_id = sta_id,
+ .tid = tid,
+ .frame_limit = frame_limit,
+ .aggregate = true,
+ };
+
+ iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
+}
+
+/* Thermal management and CT-kill */
+void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
+void iwl_mvm_temp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
+void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
+void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
+int iwl_mvm_get_temp(struct iwl_mvm *mvm);
+
+/* Location Aware Regulatory */
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
+ enum iwl_mcc_source src_id);
+int iwl_mvm_init_mcc(struct iwl_mvm *mvm);
+void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
+ const char *alpha2,
+ enum iwl_mcc_source src_id,
+ bool *changed);
+struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
+ bool *changed);
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm);
+void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm);
+
+/* smart fifo */
+int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool added_vif);
+
+/* TDLS */
+
+/*
+ * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present.
+ * This TID is marked as used vs the AP and all connected TDLS peers.
+ */
+#define IWL_MVM_TDLS_FW_TID 4
+
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm);
+void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool sta_added);
+void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
+void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_tdls_ch_sw_params *params);
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work);
+
+struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
+
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool tdls, bool cmd_q);
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ const char *errmsg);
+
+#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
new file mode 100644
index 000000000000..7a3da2da6fd0
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
@@ -0,0 +1,934 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/firmware.h>
+#include <linux/rtnetlink.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include "iwl-trans.h"
+#include "iwl-csr.h"
+#include "mvm.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-eeprom-read.h"
+#include "iwl-nvm-parse.h"
+#include "iwl-prph.h"
+
+/* Default NVM size to read */
+#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
+#define IWL_MAX_NVM_SECTION_SIZE 0x1b58
+#define IWL_MAX_NVM_8000_SECTION_SIZE 0x1ffc
+
+#define NVM_WRITE_OPCODE 1
+#define NVM_READ_OPCODE 0
+
+/* load nvm chunk response */
+enum {
+ READ_NVM_CHUNK_SUCCEED = 0,
+ READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1
+};
+
+/*
+ * prepare the NVM host command w/ the pointers to the nvm buffer
+ * and send it to fw
+ */
+static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
+ u16 offset, u16 length, const u8 *data)
+{
+ struct iwl_nvm_access_cmd nvm_access_cmd = {
+ .offset = cpu_to_le16(offset),
+ .length = cpu_to_le16(length),
+ .type = cpu_to_le16(section),
+ .op_code = NVM_WRITE_OPCODE,
+ };
+ struct iwl_host_cmd cmd = {
+ .id = NVM_ACCESS_CMD,
+ .len = { sizeof(struct iwl_nvm_access_cmd), length },
+ .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+ .data = { &nvm_access_cmd, data },
+ /* data may come from vmalloc, so use _DUP */
+ .dataflags = { 0, IWL_HCMD_DFL_DUP },
+ };
+ struct iwl_rx_packet *pkt;
+ struct iwl_nvm_access_resp *nvm_resp;
+ int ret;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret)
+ return ret;
+
+ pkt = cmd.resp_pkt;
+ if (!pkt) {
+ IWL_ERR(mvm, "Error in NVM_ACCESS response\n");
+ return -EINVAL;
+ }
+ /* Extract & check NVM write response */
+ nvm_resp = (void *)pkt->data;
+ if (le16_to_cpu(nvm_resp->status) != READ_NVM_CHUNK_SUCCEED) {
+ IWL_ERR(mvm,
+ "NVM access write command failed for section %u (status = 0x%x)\n",
+ section, le16_to_cpu(nvm_resp->status));
+ ret = -EIO;
+ }
+
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
+ u16 offset, u16 length, u8 *data)
+{
+ struct iwl_nvm_access_cmd nvm_access_cmd = {
+ .offset = cpu_to_le16(offset),
+ .length = cpu_to_le16(length),
+ .type = cpu_to_le16(section),
+ .op_code = NVM_READ_OPCODE,
+ };
+ struct iwl_nvm_access_resp *nvm_resp;
+ struct iwl_rx_packet *pkt;
+ struct iwl_host_cmd cmd = {
+ .id = NVM_ACCESS_CMD,
+ .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+ .data = { &nvm_access_cmd, },
+ };
+ int ret, bytes_read, offset_read;
+ u8 *resp_data;
+
+ cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret)
+ return ret;
+
+ pkt = cmd.resp_pkt;
+
+ /* Extract NVM response */
+ nvm_resp = (void *)pkt->data;
+ ret = le16_to_cpu(nvm_resp->status);
+ bytes_read = le16_to_cpu(nvm_resp->length);
+ offset_read = le16_to_cpu(nvm_resp->offset);
+ resp_data = nvm_resp->data;
+ if (ret) {
+ if ((offset != 0) &&
+ (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) {
+ /*
+ * meaning of NOT_VALID_ADDRESS:
+ * driver try to read chunk from address that is
+ * multiple of 2K and got an error since addr is empty.
+ * meaning of (offset != 0): driver already
+ * read valid data from another chunk so this case
+ * is not an error.
+ */
+ IWL_DEBUG_EEPROM(mvm->trans->dev,
+ "NVM access command failed on offset 0x%x since that section size is multiple 2K\n",
+ offset);
+ ret = 0;
+ } else {
+ IWL_DEBUG_EEPROM(mvm->trans->dev,
+ "NVM access command failed with status %d (device: %s)\n",
+ ret, mvm->cfg->name);
+ ret = -EIO;
+ }
+ goto exit;
+ }
+
+ if (offset_read != offset) {
+ IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n",
+ offset_read);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Write data to NVM */
+ memcpy(data + offset, resp_data, bytes_read);
+ ret = bytes_read;
+
+exit:
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
+ const u8 *data, u16 length)
+{
+ int offset = 0;
+
+ /* copy data in chunks of 2k (and remainder if any) */
+
+ while (offset < length) {
+ int chunk_size, ret;
+
+ chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE,
+ length - offset);
+
+ ret = iwl_nvm_write_chunk(mvm, section, offset,
+ chunk_size, data + offset);
+ if (ret < 0)
+ return ret;
+
+ offset += chunk_size;
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_nvm_fixups(struct iwl_mvm *mvm, unsigned int section,
+ u8 *data, unsigned int len)
+{
+#define IWL_4165_DEVICE_ID 0x5501
+#define NVM_SKU_CAP_MIMO_DISABLE BIT(5)
+
+ if (section == NVM_SECTION_TYPE_PHY_SKU &&
+ mvm->trans->hw_id == IWL_4165_DEVICE_ID && data && len >= 5 &&
+ (data[4] & NVM_SKU_CAP_MIMO_DISABLE))
+ /* OTP 0x52 bug work around: it's a 1x1 device */
+ data[3] = ANT_B | (ANT_B << 4);
+}
+
+/*
+ * Reads an NVM section completely.
+ * NICs prior to 7000 family doesn't have a real NVM, but just read
+ * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
+ * by uCode, we need to manually check in this case that we don't
+ * overflow and try to read more than the EEPROM size.
+ * For 7000 family NICs, we supply the maximal size we can read, and
+ * the uCode fills the response with as much data as we can,
+ * without overflowing, so no check is needed.
+ */
+static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
+ u8 *data, u32 size_read)
+{
+ u16 length, offset = 0;
+ int ret;
+
+ /* Set nvm section read length */
+ length = IWL_NVM_DEFAULT_CHUNK_SIZE;
+
+ ret = length;
+
+ /* Read the NVM until exhausted (reading less than requested) */
+ while (ret == length) {
+ /* Check no memory assumptions fail and cause an overflow */
+ if ((size_read + offset + length) >
+ mvm->cfg->base_params->eeprom_size) {
+ IWL_ERR(mvm, "EEPROM size is too small for NVM\n");
+ return -ENOBUFS;
+ }
+
+ ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
+ if (ret < 0) {
+ IWL_DEBUG_EEPROM(mvm->trans->dev,
+ "Cannot read NVM from section %d offset %d, length %d\n",
+ section, offset, length);
+ return ret;
+ }
+ offset += ret;
+ }
+
+ iwl_mvm_nvm_fixups(mvm, section, data, offset);
+
+ IWL_DEBUG_EEPROM(mvm->trans->dev,
+ "NVM section %d read completed\n", section);
+ return offset;
+}
+
+static struct iwl_nvm_data *
+iwl_parse_nvm_sections(struct iwl_mvm *mvm)
+{
+ struct iwl_nvm_section *sections = mvm->nvm_sections;
+ const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
+ bool lar_enabled;
+ u32 mac_addr0, mac_addr1;
+
+ /* Checking for required sections */
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
+ !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
+ IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n");
+ return NULL;
+ }
+ } else {
+ /* SW and REGULATORY sections are mandatory */
+ if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
+ !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
+ IWL_ERR(mvm,
+ "Can't parse empty family 8000 OTP/NVM sections\n");
+ return NULL;
+ }
+ /* MAC_OVERRIDE or at least HW section must exist */
+ if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
+ !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
+ IWL_ERR(mvm,
+ "Can't parse mac_address, empty sections\n");
+ return NULL;
+ }
+
+ /* PHY_SKU section is mandatory in B0 */
+ if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
+ IWL_ERR(mvm,
+ "Can't parse phy_sku in B0, empty sections\n");
+ return NULL;
+ }
+ }
+
+ if (WARN_ON(!mvm->cfg))
+ return NULL;
+
+ /* read the mac address from WFMP registers */
+ mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0);
+ mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1);
+
+ hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
+ sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
+ calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
+ regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data;
+ mac_override =
+ (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data;
+ phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data;
+
+ lar_enabled = !iwlwifi_mod_params.lar_disable &&
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
+
+ return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
+ regulatory, mac_override, phy_sku,
+ mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
+ lar_enabled, mac_addr0, mac_addr1);
+}
+
+#define MAX_NVM_FILE_LEN 16384
+
+/*
+ * Reads external NVM from a file into mvm->nvm_sections
+ *
+ * HOW TO CREATE THE NVM FILE FORMAT:
+ * ------------------------------
+ * 1. create hex file, format:
+ * 3800 -> header
+ * 0000 -> header
+ * 5a40 -> data
+ *
+ * rev - 6 bit (word1)
+ * len - 10 bit (word1)
+ * id - 4 bit (word2)
+ * rsv - 12 bit (word2)
+ *
+ * 2. flip 8bits with 8 bits per line to get the right NVM file format
+ *
+ * 3. create binary file from the hex file
+ *
+ * 4. save as "iNVM_xxx.bin" under /lib/firmware
+ */
+static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
+{
+ int ret, section_size;
+ u16 section_id;
+ const struct firmware *fw_entry;
+ const struct {
+ __le16 word1;
+ __le16 word2;
+ u8 data[];
+ } *file_sec;
+ const u8 *eof;
+ u8 *temp;
+ int max_section_size;
+ const __le32 *dword_buff;
+
+#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
+#define NVM_WORD2_ID(x) (x >> 12)
+#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8))
+#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4)
+#define NVM_HEADER_0 (0x2A504C54)
+#define NVM_HEADER_1 (0x4E564D2A)
+#define NVM_HEADER_SIZE (4 * sizeof(u32))
+
+ IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
+
+ /* Maximal size depends on HW family and step */
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ max_section_size = IWL_MAX_NVM_SECTION_SIZE;
+ else
+ max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE;
+
+ /*
+ * Obtain NVM image via request_firmware. Since we already used
+ * request_firmware_nowait() for the firmware binary load and only
+ * get here after that we assume the NVM request can be satisfied
+ * synchronously.
+ */
+ ret = request_firmware(&fw_entry, mvm->nvm_file_name,
+ mvm->trans->dev);
+ if (ret) {
+ IWL_ERR(mvm, "ERROR: %s isn't available %d\n",
+ mvm->nvm_file_name, ret);
+ return ret;
+ }
+
+ IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
+ mvm->nvm_file_name, fw_entry->size);
+
+ if (fw_entry->size > MAX_NVM_FILE_LEN) {
+ IWL_ERR(mvm, "NVM file too large\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ eof = fw_entry->data + fw_entry->size;
+ dword_buff = (__le32 *)fw_entry->data;
+
+ /* some NVM file will contain a header.
+ * The header is identified by 2 dwords header as follow:
+ * dword[0] = 0x2A504C54
+ * dword[1] = 0x4E564D2A
+ *
+ * This header must be skipped when providing the NVM data to the FW.
+ */
+ if (fw_entry->size > NVM_HEADER_SIZE &&
+ dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
+ dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
+ file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE);
+ IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
+ IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
+ le32_to_cpu(dword_buff[3]));
+
+ /* nvm file validation, dword_buff[2] holds the file version */
+ if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP &&
+ le32_to_cpu(dword_buff[2]) < 0xE4A) ||
+ (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP &&
+ le32_to_cpu(dword_buff[2]) >= 0xE4A)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ } else {
+ file_sec = (void *)fw_entry->data;
+ }
+
+ while (true) {
+ if (file_sec->data > eof) {
+ IWL_ERR(mvm,
+ "ERROR - NVM file too short for section header\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* check for EOF marker */
+ if (!file_sec->word1 && !file_sec->word2) {
+ ret = 0;
+ break;
+ }
+
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ section_size =
+ 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
+ section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
+ } else {
+ section_size = 2 * NVM_WORD2_LEN_FAMILY_8000(
+ le16_to_cpu(file_sec->word2));
+ section_id = NVM_WORD1_ID_FAMILY_8000(
+ le16_to_cpu(file_sec->word1));
+ }
+
+ if (section_size > max_section_size) {
+ IWL_ERR(mvm, "ERROR - section too large (%d)\n",
+ section_size);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!section_size) {
+ IWL_ERR(mvm, "ERROR - section empty\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (file_sec->data + section_size > eof) {
+ IWL_ERR(mvm,
+ "ERROR - NVM file too short for section (%d bytes)\n",
+ section_size);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
+ "Invalid NVM section ID %d\n", section_id)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
+ if (!temp) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ iwl_mvm_nvm_fixups(mvm, section_id, temp, section_size);
+
+ kfree(mvm->nvm_sections[section_id].data);
+ mvm->nvm_sections[section_id].data = temp;
+ mvm->nvm_sections[section_id].length = section_size;
+
+ /* advance to the next section */
+ file_sec = (void *)(file_sec->data + section_size);
+ }
+out:
+ release_firmware(fw_entry);
+ return ret;
+}
+
+/* Loads the NVM data stored in mvm->nvm_sections into the NIC */
+int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
+{
+ int i, ret = 0;
+ struct iwl_nvm_section *sections = mvm->nvm_sections;
+
+ IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n");
+
+ for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) {
+ if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length)
+ continue;
+ ret = iwl_nvm_write_section(mvm, i, sections[i].data,
+ sections[i].length);
+ if (ret < 0) {
+ IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
+ break;
+ }
+ }
+ return ret;
+}
+
+int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
+{
+ int ret, section;
+ u32 size_read = 0;
+ u8 *nvm_buffer, *temp;
+ const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step;
+ const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
+
+ if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
+ return -EINVAL;
+
+ /* load NVM values from nic */
+ if (read_nvm_from_nic) {
+ /* Read From FW NVM */
+ IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
+
+ nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+ GFP_KERNEL);
+ if (!nvm_buffer)
+ return -ENOMEM;
+ for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) {
+ /* we override the constness for initial read */
+ ret = iwl_nvm_read_section(mvm, section, nvm_buffer,
+ size_read);
+ if (ret < 0)
+ continue;
+ size_read += ret;
+ temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
+ if (!temp) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ iwl_mvm_nvm_fixups(mvm, section, temp, ret);
+
+ mvm->nvm_sections[section].data = temp;
+ mvm->nvm_sections[section].length = ret;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ switch (section) {
+ case NVM_SECTION_TYPE_SW:
+ mvm->nvm_sw_blob.data = temp;
+ mvm->nvm_sw_blob.size = ret;
+ break;
+ case NVM_SECTION_TYPE_CALIBRATION:
+ mvm->nvm_calib_blob.data = temp;
+ mvm->nvm_calib_blob.size = ret;
+ break;
+ case NVM_SECTION_TYPE_PRODUCTION:
+ mvm->nvm_prod_blob.data = temp;
+ mvm->nvm_prod_blob.size = ret;
+ break;
+ case NVM_SECTION_TYPE_PHY_SKU:
+ mvm->nvm_phy_sku_blob.data = temp;
+ mvm->nvm_phy_sku_blob.size = ret;
+ break;
+ default:
+ if (section == mvm->cfg->nvm_hw_section_num) {
+ mvm->nvm_hw_blob.data = temp;
+ mvm->nvm_hw_blob.size = ret;
+ break;
+ }
+ }
+#endif
+ }
+ if (!size_read)
+ IWL_ERR(mvm, "OTP is blank\n");
+ kfree(nvm_buffer);
+ }
+
+ /* Only if PNVM selected in the mod param - load external NVM */
+ if (mvm->nvm_file_name) {
+ /* read External NVM file from the mod param */
+ ret = iwl_mvm_read_external_nvm(mvm);
+ if (ret) {
+ /* choose the nvm_file name according to the
+ * HW step
+ */
+ if (CSR_HW_REV_STEP(mvm->trans->hw_rev) ==
+ SILICON_B_STEP)
+ mvm->nvm_file_name = nvm_file_B;
+ else
+ mvm->nvm_file_name = nvm_file_C;
+
+ if ((ret == -EFAULT || ret == -ENOENT) &&
+ mvm->nvm_file_name) {
+ /* in case nvm file was failed try again */
+ ret = iwl_mvm_read_external_nvm(mvm);
+ if (ret)
+ return ret;
+ } else {
+ return ret;
+ }
+ }
+ }
+
+ /* parse the relevant nvm sections */
+ mvm->nvm_data = iwl_parse_nvm_sections(mvm);
+ if (!mvm->nvm_data)
+ return -ENODATA;
+ IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n",
+ mvm->nvm_data->nvm_version);
+
+ return 0;
+}
+
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
+ enum iwl_mcc_source src_id)
+{
+ struct iwl_mcc_update_cmd mcc_update_cmd = {
+ .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
+ .source_id = (u8)src_id,
+ };
+ struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL;
+ struct iwl_mcc_update_resp_v1 *mcc_resp_v1 = NULL;
+ struct iwl_rx_packet *pkt;
+ struct iwl_host_cmd cmd = {
+ .id = MCC_UPDATE_CMD,
+ .flags = CMD_WANT_SKB,
+ .data = { &mcc_update_cmd },
+ };
+
+ int ret;
+ u32 status;
+ int resp_len, n_channels;
+ u16 mcc;
+ bool resp_v2 = fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2);
+
+ if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ cmd.len[0] = sizeof(struct iwl_mcc_update_cmd);
+ if (!resp_v2)
+ cmd.len[0] = sizeof(struct iwl_mcc_update_cmd_v1);
+
+ IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n",
+ alpha2[0], alpha2[1], src_id);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret)
+ return ERR_PTR(ret);
+
+ pkt = cmd.resp_pkt;
+
+ /* Extract MCC response */
+ if (resp_v2) {
+ mcc_resp = (void *)pkt->data;
+ n_channels = __le32_to_cpu(mcc_resp->n_channels);
+ } else {
+ mcc_resp_v1 = (void *)pkt->data;
+ n_channels = __le32_to_cpu(mcc_resp_v1->n_channels);
+ }
+
+ resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels *
+ sizeof(__le32);
+
+ resp_cp = kzalloc(resp_len, GFP_KERNEL);
+ if (!resp_cp) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (resp_v2) {
+ memcpy(resp_cp, mcc_resp, resp_len);
+ } else {
+ resp_cp->status = mcc_resp_v1->status;
+ resp_cp->mcc = mcc_resp_v1->mcc;
+ resp_cp->cap = mcc_resp_v1->cap;
+ resp_cp->source_id = mcc_resp_v1->source_id;
+ resp_cp->n_channels = mcc_resp_v1->n_channels;
+ memcpy(resp_cp->channels, mcc_resp_v1->channels,
+ n_channels * sizeof(__le32));
+ }
+
+ status = le32_to_cpu(resp_cp->status);
+
+ mcc = le16_to_cpu(resp_cp->mcc);
+
+ /* W/A for a FW/NVM issue - returns 0x00 for the world domain */
+ if (mcc == 0) {
+ mcc = 0x3030; /* "00" - world */
+ resp_cp->mcc = cpu_to_le16(mcc);
+ }
+
+ IWL_DEBUG_LAR(mvm,
+ "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n",
+ status, mcc, mcc >> 8, mcc & 0xff,
+ !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels);
+
+exit:
+ iwl_free_resp(&cmd);
+ if (ret)
+ return ERR_PTR(ret);
+ return resp_cp;
+}
+
+#ifdef CONFIG_ACPI
+#define WRD_METHOD "WRDD"
+#define WRDD_WIFI (0x07)
+#define WRDD_WIGIG (0x10)
+
+static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd)
+{
+ union acpi_object *mcc_pkg, *domain_type, *mcc_value;
+ u32 i;
+
+ if (wrdd->type != ACPI_TYPE_PACKAGE ||
+ wrdd->package.count < 2 ||
+ wrdd->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ wrdd->package.elements[0].integer.value != 0) {
+ IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n");
+ return 0;
+ }
+
+ for (i = 1 ; i < wrdd->package.count ; ++i) {
+ mcc_pkg = &wrdd->package.elements[i];
+
+ if (mcc_pkg->type != ACPI_TYPE_PACKAGE ||
+ mcc_pkg->package.count < 2 ||
+ mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
+ mcc_pkg = NULL;
+ continue;
+ }
+
+ domain_type = &mcc_pkg->package.elements[0];
+ if (domain_type->integer.value == WRDD_WIFI)
+ break;
+
+ mcc_pkg = NULL;
+ }
+
+ if (mcc_pkg) {
+ mcc_value = &mcc_pkg->package.elements[1];
+ return mcc_value->integer.value;
+ }
+
+ return 0;
+}
+
+static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
+{
+ acpi_handle root_handle;
+ acpi_handle handle;
+ struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_status status;
+ u32 mcc_val;
+ struct pci_dev *pdev = to_pci_dev(mvm->dev);
+
+ root_handle = ACPI_HANDLE(&pdev->dev);
+ if (!root_handle) {
+ IWL_DEBUG_LAR(mvm,
+ "Could not retrieve root port ACPI handle\n");
+ return -ENOENT;
+ }
+
+ /* Get the method's handle */
+ status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle);
+ if (ACPI_FAILURE(status)) {
+ IWL_DEBUG_LAR(mvm, "WRD method not found\n");
+ return -ENOENT;
+ }
+
+ /* Call WRDD with no arguments */
+ status = acpi_evaluate_object(handle, NULL, NULL, &wrdd);
+ if (ACPI_FAILURE(status)) {
+ IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status);
+ return -ENOENT;
+ }
+
+ mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer);
+ kfree(wrdd.pointer);
+ if (!mcc_val)
+ return -ENOENT;
+
+ mcc[0] = (mcc_val >> 8) & 0xff;
+ mcc[1] = mcc_val & 0xff;
+ mcc[2] = '\0';
+ return 0;
+}
+#else /* CONFIG_ACPI */
+static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
+{
+ return -ENOENT;
+}
+#endif
+
+int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
+{
+ bool tlv_lar;
+ bool nvm_lar;
+ int retval;
+ struct ieee80211_regdomain *regd;
+ char mcc[3];
+
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+ tlv_lar = fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
+ nvm_lar = mvm->nvm_data->lar_enabled;
+ if (tlv_lar != nvm_lar)
+ IWL_INFO(mvm,
+ "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n",
+ tlv_lar ? "enabled" : "disabled",
+ nvm_lar ? "enabled" : "disabled");
+ }
+
+ if (!iwl_mvm_is_lar_supported(mvm))
+ return 0;
+
+ /*
+ * try to replay the last set MCC to FW. If it doesn't exist,
+ * queue an update to cfg80211 to retrieve the default alpha2 from FW.
+ */
+ retval = iwl_mvm_init_fw_regd(mvm);
+ if (retval != -ENOENT)
+ return retval;
+
+ /*
+ * Driver regulatory hint for initial update, this also informs the
+ * firmware we support wifi location updates.
+ * Disallow scans that might crash the FW while the LAR regdomain
+ * is not set.
+ */
+ mvm->lar_regdom_set = false;
+
+ regd = iwl_mvm_get_current_regdomain(mvm, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+
+ if (iwl_mvm_is_wifi_mcc_supported(mvm) &&
+ !iwl_mvm_get_bios_mcc(mvm, mcc)) {
+ kfree(regd);
+ regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc,
+ MCC_SOURCE_BIOS, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+ }
+
+ retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+ kfree(regd);
+ return retval;
+}
+
+void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mcc_chub_notif *notif = (void *)pkt->data;
+ enum iwl_mcc_source src;
+ char mcc[3];
+ struct ieee80211_regdomain *regd;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
+ return;
+
+ mcc[0] = notif->mcc >> 8;
+ mcc[1] = notif->mcc & 0xff;
+ mcc[2] = '\0';
+ src = notif->source_id;
+
+ IWL_DEBUG_LAR(mvm,
+ "RX: received chub update mcc cmd (mcc '%s' src %d)\n",
+ mcc, src);
+ regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return;
+
+ regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+ kfree(regd);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
new file mode 100644
index 000000000000..6338d9cf7070
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
@@ -0,0 +1,255 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/ipv6.h>
+#include <net/addrconf.h>
+#include <linux/bitops.h>
+#include "mvm.h"
+
+void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
+ struct iwl_wowlan_config_cmd *cmd)
+{
+ int i;
+
+ /*
+ * For QoS counters, we store the one to use next, so subtract 0x10
+ * since the uCode will add 0x10 *before* using the value while we
+ * increment after using the value (i.e. store the next value to use).
+ */
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ u16 seq = mvm_ap_sta->tid_data[i].seq_number;
+ seq -= 0x10;
+ cmd->qos_seq[i] = cpu_to_le16(seq);
+ }
+}
+
+int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool disable_offloading,
+ bool offload_ns,
+ u32 cmd_flags)
+{
+ union {
+ struct iwl_proto_offload_cmd_v1 v1;
+ struct iwl_proto_offload_cmd_v2 v2;
+ struct iwl_proto_offload_cmd_v3_small v3s;
+ struct iwl_proto_offload_cmd_v3_large v3l;
+ } cmd = {};
+ struct iwl_host_cmd hcmd = {
+ .id = PROT_OFFLOAD_CONFIG_CMD,
+ .flags = cmd_flags,
+ .data[0] = &cmd,
+ .dataflags[0] = IWL_HCMD_DFL_DUP,
+ };
+ struct iwl_proto_offload_cmd_common *common;
+ u32 enabled = 0, size;
+ u32 capa_flags = mvm->fw->ucode_capa.flags;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int i;
+ /*
+ * Skip tentative address when ns offload is enabled to avoid
+ * violating RFC4862.
+ * Keep tentative address when ns offload is disabled so the NS packets
+ * will not be filtered out and will wake up the host.
+ */
+ bool skip_tentative = offload_ns;
+
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
+ capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+ struct iwl_ns_config *nsc;
+ struct iwl_targ_addr *addrs;
+ int n_nsc, n_addrs;
+ int c;
+ int num_skipped = 0;
+
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+ nsc = cmd.v3s.ns_config;
+ n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
+ addrs = cmd.v3s.targ_addrs;
+ n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
+ } else {
+ nsc = cmd.v3l.ns_config;
+ n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
+ addrs = cmd.v3l.targ_addrs;
+ n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
+ }
+
+ /*
+ * For each address we have (and that will fit) fill a target
+ * address struct and combine for NS offload structs with the
+ * solicited node addresses.
+ */
+ for (i = 0, c = 0;
+ i < mvmvif->num_target_ipv6_addrs &&
+ i < n_addrs && c < n_nsc; i++) {
+ struct in6_addr solicited_addr;
+ int j;
+
+ if (skip_tentative &&
+ test_bit(i, mvmvif->tentative_addrs)) {
+ num_skipped++;
+ continue;
+ }
+
+ addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
+ &solicited_addr);
+ for (j = 0; j < c; j++)
+ if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
+ &solicited_addr) == 0)
+ break;
+ if (j == c)
+ c++;
+ addrs[i].addr = mvmvif->target_ipv6_addrs[i];
+ addrs[i].config_num = cpu_to_le32(j);
+ nsc[j].dest_ipv6_addr = solicited_addr;
+ memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
+ }
+
+ if (mvmvif->num_target_ipv6_addrs - num_skipped)
+ enabled |= IWL_D3_PROTO_IPV6_VALID;
+
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
+ cmd.v3s.num_valid_ipv6_addrs =
+ cpu_to_le32(i - num_skipped);
+ else
+ cmd.v3l.num_valid_ipv6_addrs =
+ cpu_to_le32(i - num_skipped);
+ } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+ bool found = false;
+
+ BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
+ sizeof(mvmvif->target_ipv6_addrs[0]));
+
+ for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
+ IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) {
+ if (skip_tentative &&
+ test_bit(i, mvmvif->tentative_addrs))
+ continue;
+
+ memcpy(cmd.v2.target_ipv6_addr[i],
+ &mvmvif->target_ipv6_addrs[i],
+ sizeof(cmd.v2.target_ipv6_addr[i]));
+
+ found = true;
+ }
+ if (found) {
+ enabled |= IWL_D3_PROTO_IPV6_VALID;
+ memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
+ }
+ } else {
+ bool found = false;
+ BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
+ sizeof(mvmvif->target_ipv6_addrs[0]));
+
+ for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
+ IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) {
+ if (skip_tentative &&
+ test_bit(i, mvmvif->tentative_addrs))
+ continue;
+
+ memcpy(cmd.v1.target_ipv6_addr[i],
+ &mvmvif->target_ipv6_addrs[i],
+ sizeof(cmd.v1.target_ipv6_addr[i]));
+
+ found = true;
+ }
+
+ if (found) {
+ enabled |= IWL_D3_PROTO_IPV6_VALID;
+ memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
+ }
+ }
+
+ if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID))
+ enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+#endif
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+ common = &cmd.v3s.common;
+ size = sizeof(cmd.v3s);
+ } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+ common = &cmd.v3l.common;
+ size = sizeof(cmd.v3l);
+ } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+ common = &cmd.v2.common;
+ size = sizeof(cmd.v2);
+ } else {
+ common = &cmd.v1.common;
+ size = sizeof(cmd.v1);
+ }
+
+ if (vif->bss_conf.arp_addr_cnt) {
+ enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID;
+ common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
+ memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
+ }
+
+ if (!disable_offloading)
+ common->enabled = cpu_to_le32(enabled);
+
+ hcmd.len[0] = size;
+ return iwl_mvm_send_cmd(mvm, &hcmd);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
new file mode 100644
index 000000000000..89ea70deeb84
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -0,0 +1,1519 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <net/mac80211.h>
+
+#include "iwl-notif-wait.h"
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-fw.h"
+#include "iwl-debug.h"
+#include "iwl-drv.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-phy-db.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-csr.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "rs.h"
+#include "fw-api-scan.h"
+#include "time-event.h"
+#include "fw-dbg.h"
+#include "fw-api.h"
+#include "fw-api-scan.h"
+
+#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux"
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
+static const struct iwl_op_mode_ops iwl_mvm_ops;
+static const struct iwl_op_mode_ops iwl_mvm_ops_mq;
+
+struct iwl_mvm_mod_params iwlmvm_mod_params = {
+ .power_scheme = IWL_POWER_SCHEME_BPS,
+ .tfd_q_hang_detect = true
+ /* rest of fields are 0 by default */
+};
+
+module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO);
+MODULE_PARM_DESC(init_dbg,
+ "set to true to debug an ASSERT in INIT fw (default: false");
+module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO);
+MODULE_PARM_DESC(power_scheme,
+ "power management scheme: 1-active, 2-balanced, 3-low power, default: 2");
+module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(tfd_q_hang_detect,
+ "TFD queues hang detection (default: true");
+
+/*
+ * module init and exit functions
+ */
+static int __init iwl_mvm_init(void)
+{
+ int ret;
+
+ ret = iwl_mvm_rate_control_register();
+ if (ret) {
+ pr_err("Unable to register rate control algorithm: %d\n", ret);
+ return ret;
+ }
+
+ ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops);
+
+ if (ret) {
+ pr_err("Unable to register MVM op_mode: %d\n", ret);
+ iwl_mvm_rate_control_unregister();
+ }
+
+ return ret;
+}
+module_init(iwl_mvm_init);
+
+static void __exit iwl_mvm_exit(void)
+{
+ iwl_opmode_deregister("iwlmvm");
+ iwl_mvm_rate_control_unregister();
+}
+module_exit(iwl_mvm_exit);
+
+static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
+ u32 reg_val = 0;
+ u32 phy_config = iwl_mvm_get_phy_config(mvm);
+
+ radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >>
+ FW_PHY_CFG_RADIO_TYPE_POS;
+ radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >>
+ FW_PHY_CFG_RADIO_STEP_POS;
+ radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >>
+ FW_PHY_CFG_RADIO_DASH_POS;
+
+ /* SKU control */
+ reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) <<
+ CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
+ reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) <<
+ CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
+
+ /* radio configuration */
+ reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
+ reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
+ reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
+
+ WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) &
+ ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE);
+
+ /*
+ * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and
+ * shouldn't be set to any non-zero value. The same is supposed to be
+ * true of the other HW, but unsetting them (such as the 7260) causes
+ * automatic tests to fail on seemingly unrelated errors. Need to
+ * further investigate this, but for now we'll separate cases.
+ */
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
+
+ iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
+ CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP |
+ CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
+ CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
+ CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH |
+ CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+ CSR_HW_IF_CONFIG_REG_BIT_MAC_SI,
+ reg_val);
+
+ IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
+ radio_cfg_step, radio_cfg_dash);
+
+ /*
+ * W/A : NIC is stuck in a reset state after Early PCIe power off
+ * (PCIe power is lost before PERST# is asserted), causing ME FW
+ * to lose ownership and not being able to obtain it back.
+ */
+ if (!mvm->trans->cfg->apmg_not_supported)
+ iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
+ ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
+}
+
+struct iwl_rx_handlers {
+ u16 cmd_id;
+ bool async;
+ void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+};
+
+#define RX_HANDLER(_cmd_id, _fn, _async) \
+ { .cmd_id = _cmd_id , .fn = _fn , .async = _async }
+#define RX_HANDLER_GRP(_grp, _cmd, _fn, _async) \
+ { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .async = _async }
+
+/*
+ * Handlers for fw notifications
+ * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME
+ * This list should be in order of frequency for performance purposes.
+ *
+ * The handler can be SYNC - this means that it will be called in the Rx path
+ * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and
+ * only in this case!), it should be set as ASYNC. In that case, it will be
+ * called from a worker with mvm->mutex held.
+ */
+static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
+ RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
+ RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
+
+ RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
+ RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true),
+ RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
+ RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
+ iwl_mvm_rx_ant_coupling_notif, true),
+
+ RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
+ RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true),
+
+ RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
+
+ RX_HANDLER(SCAN_ITERATION_COMPLETE,
+ iwl_mvm_rx_lmac_scan_iter_complete_notif, false),
+ RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
+ iwl_mvm_rx_lmac_scan_complete_notif, true),
+ RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_match_found,
+ false),
+ RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
+ true),
+ RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC,
+ iwl_mvm_rx_umac_scan_iter_complete_notif, false),
+
+ RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
+
+ RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif,
+ false),
+
+ RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
+ RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
+ iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
+ RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
+ RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE,
+ iwl_mvm_temp_notif, true),
+
+ RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
+ true),
+ RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false),
+ RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true),
+
+};
+#undef RX_HANDLER
+#undef RX_HANDLER_GRP
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = {
+ HCMD_NAME(MVM_ALIVE),
+ HCMD_NAME(REPLY_ERROR),
+ HCMD_NAME(ECHO_CMD),
+ HCMD_NAME(INIT_COMPLETE_NOTIF),
+ HCMD_NAME(PHY_CONTEXT_CMD),
+ HCMD_NAME(DBG_CFG),
+ HCMD_NAME(ANTENNA_COUPLING_NOTIFICATION),
+ HCMD_NAME(SCAN_CFG_CMD),
+ HCMD_NAME(SCAN_REQ_UMAC),
+ HCMD_NAME(SCAN_ABORT_UMAC),
+ HCMD_NAME(SCAN_COMPLETE_UMAC),
+ HCMD_NAME(TOF_CMD),
+ HCMD_NAME(TOF_NOTIFICATION),
+ HCMD_NAME(ADD_STA_KEY),
+ HCMD_NAME(ADD_STA),
+ HCMD_NAME(REMOVE_STA),
+ HCMD_NAME(FW_GET_ITEM_CMD),
+ HCMD_NAME(TX_CMD),
+ HCMD_NAME(SCD_QUEUE_CFG),
+ HCMD_NAME(TXPATH_FLUSH),
+ HCMD_NAME(MGMT_MCAST_KEY),
+ HCMD_NAME(WEP_KEY),
+ HCMD_NAME(SHARED_MEM_CFG),
+ HCMD_NAME(TDLS_CHANNEL_SWITCH_CMD),
+ HCMD_NAME(MAC_CONTEXT_CMD),
+ HCMD_NAME(TIME_EVENT_CMD),
+ HCMD_NAME(TIME_EVENT_NOTIFICATION),
+ HCMD_NAME(BINDING_CONTEXT_CMD),
+ HCMD_NAME(TIME_QUOTA_CMD),
+ HCMD_NAME(NON_QOS_TX_COUNTER_CMD),
+ HCMD_NAME(LQ_CMD),
+ HCMD_NAME(FW_PAGING_BLOCK_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_REQUEST_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_ABORT_CMD),
+ HCMD_NAME(HOT_SPOT_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_PROFILES_QUERY_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD),
+ HCMD_NAME(BT_COEX_UPDATE_SW_BOOST),
+ HCMD_NAME(BT_COEX_UPDATE_CORUN_LUT),
+ HCMD_NAME(BT_COEX_UPDATE_REDUCED_TXP),
+ HCMD_NAME(BT_COEX_CI),
+ HCMD_NAME(PHY_CONFIGURATION_CMD),
+ HCMD_NAME(CALIB_RES_NOTIF_PHY_DB),
+ HCMD_NAME(SCAN_OFFLOAD_COMPLETE),
+ HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_CONFIG_CMD),
+ HCMD_NAME(POWER_TABLE_CMD),
+ HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
+ HCMD_NAME(REPLY_THERMAL_MNG_BACKOFF),
+ HCMD_NAME(DC2DC_CONFIG_CMD),
+ HCMD_NAME(NVM_ACCESS_CMD),
+ HCMD_NAME(SET_CALIB_DEFAULT_CMD),
+ HCMD_NAME(BEACON_NOTIFICATION),
+ HCMD_NAME(BEACON_TEMPLATE_CMD),
+ HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
+ HCMD_NAME(BT_CONFIG),
+ HCMD_NAME(STATISTICS_CMD),
+ HCMD_NAME(STATISTICS_NOTIFICATION),
+ HCMD_NAME(EOSP_NOTIFICATION),
+ HCMD_NAME(REDUCE_TX_POWER_CMD),
+ HCMD_NAME(CARD_STATE_CMD),
+ HCMD_NAME(CARD_STATE_NOTIFICATION),
+ HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
+ HCMD_NAME(TDLS_CONFIG_CMD),
+ HCMD_NAME(MAC_PM_POWER_TABLE),
+ HCMD_NAME(TDLS_CHANNEL_SWITCH_NOTIFICATION),
+ HCMD_NAME(MFUART_LOAD_NOTIFICATION),
+ HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),
+ HCMD_NAME(REPLY_RX_PHY_CMD),
+ HCMD_NAME(REPLY_RX_MPDU_CMD),
+ HCMD_NAME(BA_NOTIF),
+ HCMD_NAME(MCC_UPDATE_CMD),
+ HCMD_NAME(MCC_CHUB_UPDATE_CMD),
+ HCMD_NAME(MARKER_CMD),
+ HCMD_NAME(BT_COEX_PRIO_TABLE),
+ HCMD_NAME(BT_COEX_PROT_ENV),
+ HCMD_NAME(BT_PROFILE_NOTIFICATION),
+ HCMD_NAME(BCAST_FILTER_CMD),
+ HCMD_NAME(MCAST_FILTER_CMD),
+ HCMD_NAME(REPLY_SF_CFG_CMD),
+ HCMD_NAME(REPLY_BEACON_FILTERING_CMD),
+ HCMD_NAME(D3_CONFIG_CMD),
+ HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD),
+ HCMD_NAME(OFFLOADS_QUERY_CMD),
+ HCMD_NAME(REMOTE_WAKE_CONFIG_CMD),
+ HCMD_NAME(MATCH_FOUND_NOTIFICATION),
+ HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER),
+ HCMD_NAME(DTS_MEASUREMENT_NOTIFICATION),
+ HCMD_NAME(WOWLAN_PATTERNS),
+ HCMD_NAME(WOWLAN_CONFIGURATION),
+ HCMD_NAME(WOWLAN_TSC_RSC_PARAM),
+ HCMD_NAME(WOWLAN_TKIP_PARAM),
+ HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL),
+ HCMD_NAME(WOWLAN_GET_STATUSES),
+ HCMD_NAME(WOWLAN_TX_POWER_PER_DB),
+ HCMD_NAME(SCAN_ITERATION_COMPLETE),
+ HCMD_NAME(D0I3_END_CMD),
+ HCMD_NAME(LTR_CONFIG),
+ HCMD_NAME(REPLY_DEBUG_CMD),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mvm_phy_names[] = {
+ HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
+ HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE),
+};
+
+static const struct iwl_hcmd_arr iwl_mvm_groups[] = {
+ [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
+ [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
+ [PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names),
+};
+
+
+/* this forward declaration can avoid to export the function */
+static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
+static void iwl_mvm_d0i3_exit_work(struct work_struct *wk);
+
+static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg)
+{
+ const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs;
+
+ if (!pwr_tx_backoff)
+ return 0;
+
+ while (pwr_tx_backoff->pwr) {
+ if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr)
+ return pwr_tx_backoff->backoff;
+
+ pwr_tx_backoff++;
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_fw_error_dump_wk(struct work_struct *work);
+
+static struct iwl_op_mode *
+iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+ const struct iwl_fw *fw, struct dentry *dbgfs_dir)
+{
+ struct ieee80211_hw *hw;
+ struct iwl_op_mode *op_mode;
+ struct iwl_mvm *mvm;
+ struct iwl_trans_config trans_cfg = {};
+ static const u8 no_reclaim_cmds[] = {
+ TX_CMD,
+ };
+ int err, scan_size;
+ u32 min_backoff;
+
+ /*
+ * We use IWL_MVM_STATION_COUNT to check the validity of the station
+ * index all over the driver - check that its value corresponds to the
+ * array size.
+ */
+ BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT);
+
+ /********************************
+ * 1. Allocating and configuring HW data
+ ********************************/
+ hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +
+ sizeof(struct iwl_mvm),
+ &iwl_mvm_hw_ops);
+ if (!hw)
+ return NULL;
+
+ if (cfg->max_rx_agg_size)
+ hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size;
+
+ if (cfg->max_tx_agg_size)
+ hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size;
+
+ op_mode = hw->priv;
+
+ mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ mvm->dev = trans->dev;
+ mvm->trans = trans;
+ mvm->cfg = cfg;
+ mvm->fw = fw;
+ mvm->hw = hw;
+
+ if (iwl_mvm_has_new_rx_api(mvm)) {
+ op_mode->ops = &iwl_mvm_ops_mq;
+ } else {
+ op_mode->ops = &iwl_mvm_ops;
+
+ if (WARN_ON(trans->num_rx_queues > 1))
+ goto out_free;
+ }
+
+ mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
+
+ mvm->aux_queue = 15;
+ mvm->first_agg_queue = 16;
+ mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1;
+ if (mvm->cfg->base_params->num_of_queues == 16) {
+ mvm->aux_queue = 11;
+ mvm->first_agg_queue = 12;
+ }
+ mvm->sf_state = SF_UNINIT;
+ mvm->cur_ucode = IWL_UCODE_INIT;
+
+ mutex_init(&mvm->mutex);
+ mutex_init(&mvm->d0i3_suspend_mutex);
+ spin_lock_init(&mvm->async_handlers_lock);
+ INIT_LIST_HEAD(&mvm->time_event_list);
+ INIT_LIST_HEAD(&mvm->aux_roc_te_list);
+ INIT_LIST_HEAD(&mvm->async_handlers_list);
+ spin_lock_init(&mvm->time_event_lock);
+ spin_lock_init(&mvm->queue_info_lock);
+
+ INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
+ INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
+ INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
+ INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
+ INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk);
+ INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
+
+ spin_lock_init(&mvm->d0i3_tx_lock);
+ spin_lock_init(&mvm->refs_lock);
+ skb_queue_head_init(&mvm->d0i3_tx);
+ init_waitqueue_head(&mvm->d0i3_exit_waitq);
+
+ SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
+
+ /*
+ * Populate the state variables that the transport layer needs
+ * to know about.
+ */
+ trans_cfg.op_mode = op_mode;
+ trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
+ trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
+ switch (iwlwifi_mod_params.amsdu_size) {
+ case IWL_AMSDU_4K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ break;
+ case IWL_AMSDU_8K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_8K;
+ break;
+ case IWL_AMSDU_12K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_12K;
+ break;
+ default:
+ pr_err("%s: Unsupported amsdu_size: %d\n", KBUILD_MODNAME,
+ iwlwifi_mod_params.amsdu_size);
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ }
+ trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_WIDE_CMD_HDR);
+
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE)
+ trans_cfg.bc_table_dword = true;
+
+ trans_cfg.command_groups = iwl_mvm_groups;
+ trans_cfg.command_groups_size = ARRAY_SIZE(iwl_mvm_groups);
+
+ trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE;
+ trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD;
+ trans_cfg.scd_set_active = true;
+
+ trans_cfg.sdio_adma_addr = fw->sdio_adma_addr;
+ trans_cfg.sw_csum_tx = IWL_MVM_SW_TX_CSUM_OFFLOAD;
+
+ /* Set a short watchdog for the command queue */
+ trans_cfg.cmd_q_wdg_timeout =
+ iwl_mvm_get_wd_timeout(mvm, NULL, false, true);
+
+ snprintf(mvm->hw->wiphy->fw_version,
+ sizeof(mvm->hw->wiphy->fw_version),
+ "%s", fw->fw_version);
+
+ /* Configure transport layer */
+ iwl_trans_configure(mvm->trans, &trans_cfg);
+
+ trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
+ trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+ trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv;
+ trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
+ memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
+ sizeof(trans->dbg_conf_tlv));
+ trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv;
+
+ /* set up notification wait support */
+ iwl_notification_wait_init(&mvm->notif_wait);
+
+ /* Init phy db */
+ mvm->phy_db = iwl_phy_db_init(trans);
+ if (!mvm->phy_db) {
+ IWL_ERR(mvm, "Cannot init phy_db\n");
+ goto out_free;
+ }
+
+ IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
+ mvm->cfg->name, mvm->trans->hw_rev);
+
+ min_backoff = calc_min_backoff(trans, cfg);
+ iwl_mvm_tt_initialize(mvm, min_backoff);
+
+ if (iwlwifi_mod_params.nvm_file)
+ mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
+ else
+ IWL_DEBUG_EEPROM(mvm->trans->dev,
+ "working without external nvm file\n");
+
+ if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name,
+ "not allowing power-up and not having nvm_file\n"))
+ goto out_free;
+
+ /*
+ * Even if nvm exists in the nvm_file driver should read again the nvm
+ * from the nic because there might be entries that exist in the OTP
+ * and not in the file.
+ * for nics with no_power_up_nic_in_init: rely completley on nvm_file
+ */
+ if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) {
+ err = iwl_nvm_init(mvm, false);
+ if (err)
+ goto out_free;
+ } else {
+ err = iwl_trans_start_hw(mvm->trans);
+ if (err)
+ goto out_free;
+
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE);
+ err = iwl_run_init_mvm_ucode(mvm, true);
+ if (!err || !iwlmvm_mod_params.init_dbg)
+ iwl_trans_stop_device(trans);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE);
+ mutex_unlock(&mvm->mutex);
+ /* returns 0 if successful, 1 if success but in rfkill */
+ if (err < 0 && !iwlmvm_mod_params.init_dbg) {
+ IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
+ goto out_free;
+ }
+ }
+
+ scan_size = iwl_mvm_scan_size(mvm);
+
+ mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
+ if (!mvm->scan_cmd)
+ goto out_free;
+
+ /* Set EBS as successful as long as not stated otherwise by the FW. */
+ mvm->last_ebs_successful = true;
+
+ err = iwl_mvm_mac_setup_register(mvm);
+ if (err)
+ goto out_free;
+
+ err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir);
+ if (err)
+ goto out_unregister;
+
+ memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
+
+ /* rpm starts with a taken reference, we can release it now */
+ iwl_trans_unref(mvm->trans);
+
+ iwl_mvm_tof_init(mvm);
+
+ return op_mode;
+
+ out_unregister:
+ ieee80211_unregister_hw(mvm->hw);
+ iwl_mvm_leds_exit(mvm);
+ out_free:
+ flush_delayed_work(&mvm->fw_dump_wk);
+ iwl_phy_db_free(mvm->phy_db);
+ kfree(mvm->scan_cmd);
+ if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name)
+ iwl_trans_op_mode_leave(trans);
+ ieee80211_free_hw(mvm->hw);
+ return NULL;
+}
+
+static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ int i;
+
+ iwl_mvm_leds_exit(mvm);
+
+ iwl_mvm_tt_exit(mvm);
+
+ ieee80211_unregister_hw(mvm->hw);
+
+ kfree(mvm->scan_cmd);
+ kfree(mvm->mcast_filter_cmd);
+ mvm->mcast_filter_cmd = NULL;
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
+ kfree(mvm->d3_resume_sram);
+#endif
+
+ iwl_trans_op_mode_leave(mvm->trans);
+
+ iwl_phy_db_free(mvm->phy_db);
+ mvm->phy_db = NULL;
+
+ iwl_free_nvm_data(mvm->nvm_data);
+ for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
+ kfree(mvm->nvm_sections[i].data);
+
+ iwl_mvm_tof_clean(mvm);
+
+ ieee80211_free_hw(mvm->hw);
+}
+
+struct iwl_async_handler_entry {
+ struct list_head list;
+ struct iwl_rx_cmd_buffer rxb;
+ void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+};
+
+void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm)
+{
+ struct iwl_async_handler_entry *entry, *tmp;
+
+ spin_lock_bh(&mvm->async_handlers_lock);
+ list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) {
+ iwl_free_rxb(&entry->rxb);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&mvm->async_handlers_lock);
+}
+
+static void iwl_mvm_async_handlers_wk(struct work_struct *wk)
+{
+ struct iwl_mvm *mvm =
+ container_of(wk, struct iwl_mvm, async_handlers_wk);
+ struct iwl_async_handler_entry *entry, *tmp;
+ struct list_head local_list;
+
+ INIT_LIST_HEAD(&local_list);
+
+ /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */
+ mutex_lock(&mvm->mutex);
+
+ /*
+ * Sync with Rx path with a lock. Remove all the entries from this list,
+ * add them to a local one (lock free), and then handle them.
+ */
+ spin_lock_bh(&mvm->async_handlers_lock);
+ list_splice_init(&mvm->async_handlers_list, &local_list);
+ spin_unlock_bh(&mvm->async_handlers_lock);
+
+ list_for_each_entry_safe(entry, tmp, &local_list, list) {
+ entry->fn(mvm, &entry->rxb);
+ iwl_free_rxb(&entry->rxb);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&mvm->mutex);
+}
+
+static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_cmd *cmds_trig;
+ int i;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF);
+ cmds_trig = (void *)trig->data;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) {
+ /* don't collect on CMD 0 */
+ if (!cmds_trig->cmds[i].cmd_id)
+ break;
+
+ if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd ||
+ cmds_trig->cmds[i].group_id != pkt->hdr.group_id)
+ continue;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "CMD 0x%02x.%02x received",
+ pkt->hdr.group_id, pkt->hdr.cmd);
+ break;
+ }
+}
+
+static void iwl_mvm_rx_common(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_rx_packet *pkt)
+{
+ int i;
+
+ iwl_mvm_rx_check_trigger(mvm, pkt);
+
+ /*
+ * Do the notification wait before RX handlers so
+ * even if the RX handler consumes the RXB we have
+ * access to it in the notification wait entry.
+ */
+ iwl_notification_wait_notify(&mvm->notif_wait, pkt);
+
+ for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) {
+ const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i];
+ struct iwl_async_handler_entry *entry;
+
+ if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd))
+ continue;
+
+ if (!rx_h->async) {
+ rx_h->fn(mvm, rxb);
+ return;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ /* we can't do much... */
+ if (!entry)
+ return;
+
+ entry->rxb._page = rxb_steal_page(rxb);
+ entry->rxb._offset = rxb->_offset;
+ entry->rxb._rx_page_order = rxb->_rx_page_order;
+ entry->fn = rx_h->fn;
+ spin_lock(&mvm->async_handlers_lock);
+ list_add_tail(&entry->list, &mvm->async_handlers_list);
+ spin_unlock(&mvm->async_handlers_lock);
+ schedule_work(&mvm->async_handlers_wk);
+ break;
+ }
+}
+
+static void iwl_mvm_rx(struct iwl_op_mode *op_mode,
+ struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
+ iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
+ else if (pkt->hdr.cmd == FRAME_RELEASE)
+ iwl_mvm_rx_frame_release(mvm, rxb, 0);
+ else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
+ iwl_mvm_rx_rx_phy_cmd(mvm, rxb);
+ else
+ iwl_mvm_rx_common(mvm, rxb, pkt);
+}
+
+static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
+ struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
+ iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0);
+ else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
+ iwl_mvm_rx_phy_cmd_mq(mvm, rxb);
+ else
+ iwl_mvm_rx_common(mvm, rxb, pkt);
+}
+
+static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ unsigned long mq;
+ int q;
+
+ spin_lock_bh(&mvm->queue_info_lock);
+ mq = mvm->queue_info[queue].hw_queue_to_mac80211;
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ if (WARN_ON_ONCE(!mq))
+ return;
+
+ for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
+ if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) {
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "queue %d (mac80211 %d) already stopped\n",
+ queue, q);
+ continue;
+ }
+
+ ieee80211_stop_queue(mvm->hw, q);
+ }
+}
+
+static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode,
+ const struct iwl_device_cmd *cmd)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ /*
+ * For now, we only set the CMD_WANT_ASYNC_CALLBACK for ADD_STA
+ * commands that need to block the Tx queues.
+ */
+ iwl_trans_block_txq_ptrs(mvm->trans, false);
+}
+
+static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ unsigned long mq;
+ int q;
+
+ spin_lock_bh(&mvm->queue_info_lock);
+ mq = mvm->queue_info[queue].hw_queue_to_mac80211;
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ if (WARN_ON_ONCE(!mq))
+ return;
+
+ for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
+ if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) {
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "queue %d (mac80211 %d) still stopped\n",
+ queue, q);
+ continue;
+ }
+
+ ieee80211_wake_queue(mvm->hw, q);
+ }
+}
+
+void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
+{
+ if (state)
+ set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+ else
+ clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+
+ wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
+}
+
+static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ bool calibrating = ACCESS_ONCE(mvm->calibrating);
+
+ if (state)
+ set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
+ else
+ clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
+
+ wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
+
+ /* iwl_run_init_mvm_ucode is waiting for results, abort it */
+ if (calibrating)
+ iwl_abort_notification_waits(&mvm->notif_wait);
+
+ /*
+ * Stop the device if we run OPERATIONAL firmware or if we are in the
+ * middle of the calibrations.
+ */
+ return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
+}
+
+static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ struct ieee80211_tx_info *info;
+
+ info = IEEE80211_SKB_CB(skb);
+ iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+ ieee80211_free_txskb(mvm->hw, skb);
+}
+
+struct iwl_mvm_reprobe {
+ struct device *dev;
+ struct work_struct work;
+};
+
+static void iwl_mvm_reprobe_wk(struct work_struct *wk)
+{
+ struct iwl_mvm_reprobe *reprobe;
+
+ reprobe = container_of(wk, struct iwl_mvm_reprobe, work);
+ if (device_reprobe(reprobe->dev))
+ dev_err(reprobe->dev, "reprobe failed!\n");
+ kfree(reprobe);
+ module_put(THIS_MODULE);
+}
+
+static void iwl_mvm_fw_error_dump_wk(struct work_struct *work)
+{
+ struct iwl_mvm *mvm =
+ container_of(work, struct iwl_mvm, fw_dump_wk.work);
+
+ if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT))
+ return;
+
+ mutex_lock(&mvm->mutex);
+
+ /* stop recording */
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+ } else {
+ iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
+ /* wait before we collect the data till the DBGC stop */
+ udelay(100);
+ }
+
+ iwl_mvm_fw_error_dump(mvm);
+
+ /* start recording again if the firmware is not crashed */
+ WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) &&
+ mvm->fw->dbg_dest_tlv &&
+ iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf));
+
+ mutex_unlock(&mvm->mutex);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT);
+}
+
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
+{
+ iwl_abort_notification_waits(&mvm->notif_wait);
+
+ /*
+ * This is a bit racy, but worst case we tell mac80211 about
+ * a stopped/aborted scan when that was already done which
+ * is not a problem. It is necessary to abort any os scan
+ * here because mac80211 requires having the scan cleared
+ * before restarting.
+ * We'll reset the scan_status to NONE in restart cleanup in
+ * the next start() call from mac80211. If restart isn't called
+ * (no fw restart) scan status will stay busy.
+ */
+ iwl_mvm_report_scan_aborted(mvm);
+
+ /*
+ * If we're restarting already, don't cycle restarts.
+ * If INIT fw asserted, it will likely fail again.
+ * If WoWLAN fw asserted, don't restart either, mac80211
+ * can't recover this since we're already half suspended.
+ */
+ if (!mvm->restart_fw && fw_error) {
+ iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert,
+ NULL);
+ } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART,
+ &mvm->status)) {
+ struct iwl_mvm_reprobe *reprobe;
+
+ IWL_ERR(mvm,
+ "Firmware error during reconfiguration - reprobe!\n");
+
+ /*
+ * get a module reference to avoid doing this while unloading
+ * anyway and to avoid scheduling a work with code that's
+ * being removed.
+ */
+ if (!try_module_get(THIS_MODULE)) {
+ IWL_ERR(mvm, "Module is being unloaded - abort\n");
+ return;
+ }
+
+ reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC);
+ if (!reprobe) {
+ module_put(THIS_MODULE);
+ return;
+ }
+ reprobe->dev = mvm->trans->dev;
+ INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
+ schedule_work(&reprobe->work);
+ } else if (mvm->cur_ucode == IWL_UCODE_REGULAR) {
+ /* don't let the transport/FW power down */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
+ if (fw_error && mvm->restart_fw > 0)
+ mvm->restart_fw--;
+ ieee80211_restart_hw(mvm->hw);
+ }
+}
+
+static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ iwl_mvm_dump_nic_error_log(mvm);
+
+ iwl_mvm_nic_restart(mvm, true);
+}
+
+static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ WARN_ON(1);
+ iwl_mvm_nic_restart(mvm, true);
+}
+
+struct iwl_d0i3_iter_data {
+ struct iwl_mvm *mvm;
+ struct ieee80211_vif *connected_vif;
+ u8 ap_sta_id;
+ u8 vif_count;
+ u8 offloading_tid;
+ bool disable_offloading;
+};
+
+static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_d0i3_iter_data *iter_data)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_sta *ap_sta;
+ struct iwl_mvm_sta *mvmsta;
+ u32 available_tids = 0;
+ u8 tid;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_STATION ||
+ mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
+ return false;
+
+ ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]);
+ if (IS_ERR_OR_NULL(ap_sta))
+ return false;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(ap_sta);
+ spin_lock_bh(&mvmsta->lock);
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+
+ /*
+ * in case of pending tx packets, don't use this tid
+ * for offloading in order to prevent reuse of the same
+ * qos seq counters.
+ */
+ if (iwl_mvm_tid_queued(tid_data))
+ continue;
+
+ if (tid_data->state != IWL_AGG_OFF)
+ continue;
+
+ available_tids |= BIT(tid);
+ }
+ spin_unlock_bh(&mvmsta->lock);
+
+ /*
+ * disallow protocol offloading if we have no available tid
+ * (with no pending frames and no active aggregation,
+ * as we don't handle "holes" properly - the scheduler needs the
+ * frame's seq number and TFD index to match)
+ */
+ if (!available_tids)
+ return true;
+
+ /* for simplicity, just use the first available tid */
+ iter_data->offloading_tid = ffs(available_tids) - 1;
+ return false;
+}
+
+static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_d0i3_iter_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
+
+ IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr);
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ !vif->bss_conf.assoc)
+ return;
+
+ /*
+ * in case of pending tx packets or active aggregations,
+ * avoid offloading features in order to prevent reuse of
+ * the same qos seq counters.
+ */
+ if (iwl_mvm_disallow_offloading(mvm, vif, data))
+ data->disable_offloading = true;
+
+ iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags);
+ iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading,
+ false, flags);
+
+ /*
+ * on init/association, mvm already configures POWER_TABLE_CMD
+ * and REPLY_MCAST_FILTER_CMD, so currently don't
+ * reconfigure them (we might want to use different
+ * params later on, though).
+ */
+ data->ap_sta_id = mvmvif->ap_sta_id;
+ data->vif_count++;
+
+ /*
+ * no new commands can be sent at this stage, so it's safe
+ * to save the vif pointer during d0i3 entrance.
+ */
+ data->connected_vif = vif;
+}
+
+static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
+ struct iwl_wowlan_config_cmd *cmd,
+ struct iwl_d0i3_iter_data *iter_data)
+{
+ struct ieee80211_sta *ap_sta;
+ struct iwl_mvm_sta *mvm_ap_sta;
+
+ if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ rcu_read_lock();
+
+ ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]);
+ if (IS_ERR_OR_NULL(ap_sta))
+ goto out;
+
+ mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
+ cmd->is_11n_connection = ap_sta->ht_cap.ht_supported;
+ cmd->offloading_tid = iter_data->offloading_tid;
+ cmd->flags = ENABLE_L3_FILTERING | ENABLE_NBNS_FILTERING |
+ ENABLE_DHCP_FILTERING;
+ /*
+ * The d0i3 uCode takes care of the nonqos counters,
+ * so configure only the qos seq ones.
+ */
+ iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd);
+out:
+ rcu_read_unlock();
+}
+
+int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
+ int ret;
+ struct iwl_d0i3_iter_data d0i3_iter_data = {
+ .mvm = mvm,
+ };
+ struct iwl_wowlan_config_cmd wowlan_config_cmd = {
+ .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
+ IWL_WOWLAN_WAKEUP_BEACON_MISS |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+ IWL_WOWLAN_WAKEUP_BCN_FILTERING),
+ };
+ struct iwl_d3_manager_config d3_cfg_cmd = {
+ .min_sleep_time = cpu_to_le32(1000),
+ .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR),
+ };
+
+ IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
+
+ if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR))
+ return -EINVAL;
+
+ set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+
+ /*
+ * iwl_mvm_ref_sync takes a reference before checking the flag.
+ * so by checking there is no held reference we prevent a state
+ * in which iwl_mvm_ref_sync continues successfully while we
+ * configure the firmware to enter d0i3
+ */
+ if (iwl_mvm_ref_taken(mvm)) {
+ IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n");
+ clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+ wake_up(&mvm->d0i3_exit_waitq);
+ return 1;
+ }
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_enter_d0i3_iterator,
+ &d0i3_iter_data);
+ if (d0i3_iter_data.vif_count == 1) {
+ mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id;
+ mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading;
+ } else {
+ WARN_ON_ONCE(d0i3_iter_data.vif_count > 1);
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+ mvm->d0i3_offloading = false;
+ }
+
+ /* make sure we have no running tx while configuring the seqno */
+ synchronize_net();
+
+ /* Flush the hw queues, in case something got queued during entry */
+ ret = iwl_mvm_flush_tx_path(mvm, iwl_mvm_flushable_queues(mvm), flags);
+ if (ret)
+ return ret;
+
+ /* configure wowlan configuration only if needed */
+ if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) {
+ iwl_mvm_wowlan_config_key_params(mvm,
+ d0i3_iter_data.connected_vif,
+ true, flags);
+
+ iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd,
+ &d0i3_iter_data);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
+ sizeof(wowlan_config_cmd),
+ &wowlan_config_cmd);
+ if (ret)
+ return ret;
+ }
+
+ return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD,
+ flags | CMD_MAKE_TRANS_IDLE,
+ sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+}
+
+static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = _data;
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO;
+
+ IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr);
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ !vif->bss_conf.assoc)
+ return;
+
+ iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags);
+}
+
+struct iwl_mvm_d0i3_exit_work_iter_data {
+ struct iwl_mvm *mvm;
+ struct iwl_wowlan_status *status;
+ u32 wakeup_reasons;
+};
+
+static void iwl_mvm_d0i3_exit_work_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_d0i3_exit_work_iter_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u32 reasons = data->wakeup_reasons;
+
+ /* consider only the relevant station interface */
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc ||
+ data->mvm->d0i3_ap_sta_id != mvmvif->ap_sta_id)
+ return;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)
+ iwl_mvm_connection_loss(data->mvm, vif, "D0i3");
+ else if (reasons & IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON)
+ ieee80211_beacon_loss(vif);
+ else
+ iwl_mvm_d0i3_update_keys(data->mvm, vif, data->status);
+}
+
+void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq)
+{
+ struct ieee80211_sta *sta = NULL;
+ struct iwl_mvm_sta *mvm_ap_sta;
+ int i;
+ bool wake_queues = false;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ spin_lock_bh(&mvm->d0i3_tx_lock);
+
+ if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT)
+ goto out;
+
+ IWL_DEBUG_RPM(mvm, "re-enqueue packets\n");
+
+ /* get the sta in order to update seq numbers and re-enqueue skbs */
+ sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ if (IS_ERR_OR_NULL(sta)) {
+ sta = NULL;
+ goto out;
+ }
+
+ if (mvm->d0i3_offloading && qos_seq) {
+ /* update qos seq numbers if offloading was enabled */
+ mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta);
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ u16 seq = le16_to_cpu(qos_seq[i]);
+ /* firmware stores last-used one, we store next one */
+ seq += 0x10;
+ mvm_ap_sta->tid_data[i].seq_number = seq;
+ }
+ }
+out:
+ /* re-enqueue (or drop) all packets */
+ while (!skb_queue_empty(&mvm->d0i3_tx)) {
+ struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx);
+
+ if (!sta || iwl_mvm_tx_skb(mvm, skb, sta))
+ ieee80211_free_txskb(mvm->hw, skb);
+
+ /* if the skb_queue is not empty, we need to wake queues */
+ wake_queues = true;
+ }
+ clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+ wake_up(&mvm->d0i3_exit_waitq);
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+ if (wake_queues)
+ ieee80211_wake_queues(mvm->hw);
+
+ spin_unlock_bh(&mvm->d0i3_tx_lock);
+}
+
+static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
+{
+ struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
+ struct iwl_host_cmd get_status_cmd = {
+ .id = WOWLAN_GET_STATUSES,
+ .flags = CMD_HIGH_PRIO | CMD_WANT_SKB,
+ };
+ struct iwl_mvm_d0i3_exit_work_iter_data iter_data = {
+ .mvm = mvm,
+ };
+
+ struct iwl_wowlan_status *status;
+ int ret;
+ u32 wakeup_reasons = 0;
+ __le16 *qos_seq = NULL;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_send_cmd(mvm, &get_status_cmd);
+ if (ret)
+ goto out;
+
+ if (!get_status_cmd.resp_pkt)
+ goto out;
+
+ status = (void *)get_status_cmd.resp_pkt->data;
+ wakeup_reasons = le32_to_cpu(status->wakeup_reasons);
+ qos_seq = status->qos_seq_ctr;
+
+ IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons);
+
+ iter_data.wakeup_reasons = wakeup_reasons;
+ iter_data.status = status;
+ ieee80211_iterate_active_interfaces(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_d0i3_exit_work_iter,
+ &iter_data);
+out:
+ iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
+
+ IWL_DEBUG_INFO(mvm, "d0i3 exit completed (wakeup reasons: 0x%x)\n",
+ wakeup_reasons);
+
+ /* qos_seq might point inside resp_pkt, so free it only now */
+ if (get_status_cmd.resp_pkt)
+ iwl_free_resp(&get_status_cmd);
+
+ /* the FW might have updated the regdomain */
+ iwl_mvm_update_changed_regdom(mvm);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK);
+ mutex_unlock(&mvm->mutex);
+}
+
+int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm)
+{
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
+ CMD_WAKE_UP_TRANS;
+ int ret;
+
+ IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
+
+ if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR))
+ return -EINVAL;
+
+ mutex_lock(&mvm->d0i3_suspend_mutex);
+ if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) {
+ IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n");
+ __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags);
+ mutex_unlock(&mvm->d0i3_suspend_mutex);
+ return 0;
+ }
+ mutex_unlock(&mvm->d0i3_suspend_mutex);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
+ if (ret)
+ goto out;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_exit_d0i3_iterator,
+ mvm);
+out:
+ schedule_work(&mvm->d0i3_exit_work);
+ return ret;
+}
+
+int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK);
+ return _iwl_mvm_exit_d0i3(mvm);
+}
+
+#define IWL_MVM_COMMON_OPS \
+ /* these could be differentiated */ \
+ .async_cb = iwl_mvm_async_cb, \
+ .queue_full = iwl_mvm_stop_sw_queue, \
+ .queue_not_full = iwl_mvm_wake_sw_queue, \
+ .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \
+ .free_skb = iwl_mvm_free_skb, \
+ .nic_error = iwl_mvm_nic_error, \
+ .cmd_queue_full = iwl_mvm_cmd_queue_full, \
+ .nic_config = iwl_mvm_nic_config, \
+ .enter_d0i3 = iwl_mvm_enter_d0i3, \
+ .exit_d0i3 = iwl_mvm_exit_d0i3, \
+ /* as we only register one, these MUST be common! */ \
+ .start = iwl_op_mode_mvm_start, \
+ .stop = iwl_op_mode_mvm_stop
+
+static const struct iwl_op_mode_ops iwl_mvm_ops = {
+ IWL_MVM_COMMON_OPS,
+ .rx = iwl_mvm_rx,
+};
+
+static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode,
+ struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb,
+ unsigned int queue)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+ if (unlikely(pkt->hdr.cmd == FRAME_RELEASE))
+ iwl_mvm_rx_frame_release(mvm, rxb, queue);
+ else
+ iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue);
+}
+
+static const struct iwl_op_mode_ops iwl_mvm_ops_mq = {
+ IWL_MVM_COMMON_OPS,
+ .rx = iwl_mvm_rx_mq,
+ .rx_rss = iwl_mvm_rx_mq_rss,
+};
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
new file mode 100644
index 000000000000..6e6a56f2153d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
@@ -0,0 +1,295 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+/* Maps the driver specific channel width definition to the fw values */
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ return PHY_VHT_CHANNEL_MODE20;
+ case NL80211_CHAN_WIDTH_40:
+ return PHY_VHT_CHANNEL_MODE40;
+ case NL80211_CHAN_WIDTH_80:
+ return PHY_VHT_CHANNEL_MODE80;
+ case NL80211_CHAN_WIDTH_160:
+ return PHY_VHT_CHANNEL_MODE160;
+ default:
+ WARN(1, "Invalid channel width=%u", chandef->width);
+ return PHY_VHT_CHANNEL_MODE20;
+ }
+}
+
+/*
+ * Maps the driver specific control channel position (relative to the center
+ * freq) definitions to the the fw values
+ */
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->chan->center_freq - chandef->center_freq1) {
+ case -70:
+ return PHY_VHT_CTRL_POS_4_BELOW;
+ case -50:
+ return PHY_VHT_CTRL_POS_3_BELOW;
+ case -30:
+ return PHY_VHT_CTRL_POS_2_BELOW;
+ case -10:
+ return PHY_VHT_CTRL_POS_1_BELOW;
+ case 10:
+ return PHY_VHT_CTRL_POS_1_ABOVE;
+ case 30:
+ return PHY_VHT_CTRL_POS_2_ABOVE;
+ case 50:
+ return PHY_VHT_CTRL_POS_3_ABOVE;
+ case 70:
+ return PHY_VHT_CTRL_POS_4_ABOVE;
+ default:
+ WARN(1, "Invalid channel definition");
+ case 0:
+ /*
+ * The FW is expected to check the control channel position only
+ * when in HT/VHT and the channel width is not 20MHz. Return
+ * this value as the default one.
+ */
+ return PHY_VHT_CTRL_POS_1_BELOW;
+ }
+}
+
+/*
+ * Construct the generic fields of the PHY context command
+ */
+static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
+ struct iwl_phy_context_cmd *cmd,
+ u32 action, u32 apply_time)
+{
+ memset(cmd, 0, sizeof(struct iwl_phy_context_cmd));
+
+ cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
+ ctxt->color));
+ cmd->action = cpu_to_le32(action);
+ cmd->apply_time = cpu_to_le32(apply_time);
+}
+
+/*
+ * Add the phy configuration to the PHY context command
+ */
+static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
+ struct iwl_phy_context_cmd *cmd,
+ struct cfg80211_chan_def *chandef,
+ u8 chains_static, u8 chains_dynamic)
+{
+ u8 active_cnt, idle_cnt;
+
+ /* Set the channel info data */
+ cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
+ PHY_BAND_24 : PHY_BAND_5);
+
+ cmd->ci.channel = chandef->chan->hw_value;
+ cmd->ci.width = iwl_mvm_get_channel_width(chandef);
+ cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+
+ /* Set rx the chains */
+ idle_cnt = chains_static;
+ active_cnt = chains_dynamic;
+
+ /* In scenarios where we only ever use a single-stream rates,
+ * i.e. legacy 11b/g/a associations, single-stream APs or even
+ * static SMPS, enable both chains to get diversity, improving
+ * the case where we're far enough from the AP that attenuation
+ * between the two antennas is sufficiently different to impact
+ * performance.
+ */
+ if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) {
+ idle_cnt = 2;
+ active_cnt = 2;
+ }
+
+ cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
+ PHY_RX_CHAIN_VALID_POS);
+ cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
+ cmd->rxchain_info |= cpu_to_le32(active_cnt <<
+ PHY_RX_CHAIN_MIMO_CNT_POS);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (unlikely(mvm->dbgfs_rx_phyinfo))
+ cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
+#endif
+
+ cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
+}
+
+/*
+ * Send a command to apply the current phy configuration. The command is send
+ * only if something in the configuration changed: in case that this is the
+ * first time that the phy configuration is applied or in case that the phy
+ * configuration changed from the previous apply.
+ */
+static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
+ struct iwl_mvm_phy_ctxt *ctxt,
+ struct cfg80211_chan_def *chandef,
+ u8 chains_static, u8 chains_dynamic,
+ u32 action, u32 apply_time)
+{
+ struct iwl_phy_context_cmd cmd;
+ int ret;
+
+ /* Set the command header fields */
+ iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
+
+ /* Set the command data */
+ iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
+ chains_static, chains_dynamic);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0,
+ sizeof(struct iwl_phy_context_cmd),
+ &cmd);
+ if (ret)
+ IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
+ return ret;
+}
+
+/*
+ * Send a command to add a PHY context based on the current HW configuration.
+ */
+int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+ struct cfg80211_chan_def *chandef,
+ u8 chains_static, u8 chains_dynamic)
+{
+ WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ ctxt->ref);
+ lockdep_assert_held(&mvm->mutex);
+
+ ctxt->channel = chandef->chan;
+
+ return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+ chains_static, chains_dynamic,
+ FW_CTXT_ACTION_ADD, 0);
+}
+
+/*
+ * Update the number of references to the given PHY context. This is valid only
+ * in case the PHY context was already created, i.e., its reference count > 0.
+ */
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+ lockdep_assert_held(&mvm->mutex);
+ ctxt->ref++;
+}
+
+/*
+ * Send a command to modify the PHY context based on the current HW
+ * configuration. Note that the function does not check that the configuration
+ * changed.
+ */
+int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+ struct cfg80211_chan_def *chandef,
+ u8 chains_static, u8 chains_dynamic)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ ctxt->channel = chandef->chan;
+ return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+ chains_static, chains_dynamic,
+ FW_CTXT_ACTION_MODIFY, 0);
+}
+
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ if (WARN_ON_ONCE(!ctxt))
+ return;
+
+ ctxt->ref--;
+}
+
+static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ unsigned long *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!mvmvif->phy_ctxt)
+ return;
+
+ if (vif->type == NL80211_IFTYPE_STATION ||
+ vif->type == NL80211_IFTYPE_AP)
+ __set_bit(mvmvif->phy_ctxt->id, data);
+}
+
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
+{
+ unsigned long phy_ctxt_counter = 0;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_binding_iterator,
+ &phy_ctxt_counter);
+
+ return hweight8(phy_ctxt_counter);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
new file mode 100644
index 000000000000..9de159f1ef2d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
@@ -0,0 +1,1038 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/etherdevice.h>
+
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "mvm.h"
+#include "iwl-modparams.h"
+#include "fw-api-power.h"
+
+#define POWER_KEEP_ALIVE_PERIOD_SEC 25
+
+static
+int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+ struct iwl_beacon_filter_cmd *cmd,
+ u32 flags)
+{
+ IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
+ le32_to_cpu(cmd->ba_enable_beacon_abort));
+ IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
+ le32_to_cpu(cmd->ba_escape_timer));
+ IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
+ le32_to_cpu(cmd->bf_debug_flag));
+ IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
+ le32_to_cpu(cmd->bf_enable_beacon_filter));
+ IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
+ le32_to_cpu(cmd->bf_energy_delta));
+ IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
+ le32_to_cpu(cmd->bf_escape_timer));
+ IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
+ le32_to_cpu(cmd->bf_roaming_energy_delta));
+ IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
+ le32_to_cpu(cmd->bf_roaming_state));
+ IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
+ le32_to_cpu(cmd->bf_temp_threshold));
+ IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
+ le32_to_cpu(cmd->bf_temp_fast_filter));
+ IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
+ le32_to_cpu(cmd->bf_temp_slow_filter));
+
+ return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags,
+ sizeof(struct iwl_beacon_filter_cmd), cmd);
+}
+
+static
+void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd,
+ bool d0i3)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (vif->bss_conf.cqm_rssi_thold && !d0i3) {
+ cmd->bf_energy_delta =
+ cpu_to_le32(vif->bss_conf.cqm_rssi_hyst);
+ /* fw uses an absolute value for this */
+ cmd->bf_roaming_state =
+ cpu_to_le32(-vif->bss_conf.cqm_rssi_thold);
+ }
+ cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled);
+}
+
+static void iwl_mvm_power_log(struct iwl_mvm *mvm,
+ struct iwl_mac_power_cmd *cmd)
+{
+ IWL_DEBUG_POWER(mvm,
+ "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+ cmd->id_and_color, iwlmvm_mod_params.power_scheme,
+ le16_to_cpu(cmd->flags));
+ IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
+ le16_to_cpu(cmd->keep_alive_seconds));
+
+ if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) {
+ IWL_DEBUG_POWER(mvm, "Disable power management\n");
+ return;
+ }
+
+ IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
+ le32_to_cpu(cmd->rx_data_timeout));
+ IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+ le32_to_cpu(cmd->tx_data_timeout));
+ if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
+ IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
+ cmd->skip_dtim_periods);
+ if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+ IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+ cmd->lprx_rssi_threshold);
+ if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
+ IWL_DEBUG_POWER(mvm, "uAPSD enabled\n");
+ IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
+ le32_to_cpu(cmd->rx_data_timeout_uapsd));
+ IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n",
+ le32_to_cpu(cmd->tx_data_timeout_uapsd));
+ IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid);
+ IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags);
+ IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp);
+ }
+}
+
+static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_power_cmd *cmd)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ enum ieee80211_ac_numbers ac;
+ bool tid_found = false;
+
+ for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
+ if (!mvmvif->queue_params[ac].uapsd)
+ continue;
+
+ if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+
+ cmd->uapsd_ac_flags |= BIT(ac);
+
+ /* QNDP TID - the highest TID with no admission control */
+ if (!tid_found && !mvmvif->queue_params[ac].acm) {
+ tid_found = true;
+ switch (ac) {
+ case IEEE80211_AC_VO:
+ cmd->qndp_tid = 6;
+ break;
+ case IEEE80211_AC_VI:
+ cmd->qndp_tid = 5;
+ break;
+ case IEEE80211_AC_BE:
+ cmd->qndp_tid = 0;
+ break;
+ case IEEE80211_AC_BK:
+ cmd->qndp_tid = 1;
+ break;
+ }
+ }
+ }
+
+ if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ /* set advanced pm flag with no uapsd ACs to enable ps-poll */
+ if (mvmvif->dbgfs_pm.use_ps_poll)
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+#endif
+ return;
+ }
+
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
+
+ if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
+ BIT(IEEE80211_AC_VI) |
+ BIT(IEEE80211_AC_BE) |
+ BIT(IEEE80211_AC_BK))) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+ cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
+ cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
+ cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
+ cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
+ }
+
+ cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
+
+ if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
+ cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+ cmd->rx_data_timeout_uapsd =
+ cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout_uapsd =
+ cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+ } else {
+ cmd->rx_data_timeout_uapsd =
+ cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout_uapsd =
+ cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
+ }
+
+ if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+ cmd->heavy_tx_thld_packets =
+ IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
+ cmd->heavy_rx_thld_packets =
+ IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
+ } else {
+ cmd->heavy_tx_thld_packets =
+ IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
+ cmd->heavy_rx_thld_packets =
+ IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+ }
+ cmd->heavy_tx_thld_percentage =
+ IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
+ cmd->heavy_rx_thld_percentage =
+ IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
+}
+
+static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+ ETH_ALEN))
+ return false;
+
+ if (vif->p2p &&
+ !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
+ return false;
+ /*
+ * Avoid using uAPSD if P2P client is associated to GO that uses
+ * opportunistic power save. This is due to current FW limitation.
+ */
+ if (vif->p2p &&
+ (vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
+ IEEE80211_P2P_OPPPS_ENABLE_BIT))
+ return false;
+
+ /*
+ * Avoid using uAPSD if client is in DCM -
+ * low latency issue in Miracast
+ */
+ if (iwl_mvm_phy_ctx_count(mvm) >= 2)
+ return false;
+
+ return true;
+}
+
+static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
+ bool radar_detect = false;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ WARN_ON(!chanctx_conf);
+ if (chanctx_conf) {
+ chan = chanctx_conf->def.chan;
+ radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
+ }
+ rcu_read_unlock();
+
+ return radar_detect;
+}
+
+static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_power_cmd *cmd,
+ bool host_awake)
+{
+ int dtimper = vif->bss_conf.dtim_period ?: 1;
+ int skip;
+
+ /* disable, in case we're supposed to override */
+ cmd->skip_dtim_periods = 0;
+ cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+
+ if (iwl_mvm_power_is_radar(vif))
+ return;
+
+ if (dtimper >= 10)
+ return;
+
+ /* TODO: check that multicast wake lock is off */
+
+ if (host_awake) {
+ if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP)
+ return;
+ skip = 2;
+ } else {
+ int dtimper_tu = dtimper * vif->bss_conf.beacon_int;
+
+ if (WARN_ON(!dtimper_tu))
+ return;
+ /* configure skip over dtim up to 306TU - 314 msec */
+ skip = max_t(u8, 1, 306 / dtimper_tu);
+ }
+
+ /* the firmware really expects "look at every X DTIMs", so add 1 */
+ cmd->skip_dtim_periods = 1 + skip;
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+}
+
+static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_power_cmd *cmd,
+ bool host_awake)
+{
+ int dtimper, bi;
+ int keep_alive;
+ struct iwl_mvm_vif *mvmvif __maybe_unused =
+ iwl_mvm_vif_from_mac80211(vif);
+
+ cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color));
+ dtimper = vif->bss_conf.dtim_period;
+ bi = vif->bss_conf.beacon_int;
+
+ /*
+ * Regardless of power management state the driver must set
+ * keep alive period. FW will use it for sending keep alive NDPs
+ * immediately after association. Check that keep alive period
+ * is at least 3 * DTIM
+ */
+ keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
+ USEC_PER_SEC);
+ keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
+ cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
+
+ if (mvm->ps_disabled)
+ return;
+
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+ if (!vif->bss_conf.ps || !mvmvif->pm_enabled)
+ return;
+
+ if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
+ (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) ||
+ !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE))
+ return;
+
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+ if (vif->bss_conf.beacon_rate &&
+ (vif->bss_conf.beacon_rate->bitrate == 10 ||
+ vif->bss_conf.beacon_rate->bitrate == 60)) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+ cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
+ }
+
+ iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, host_awake);
+
+ if (!host_awake) {
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+ } else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) {
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT);
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT);
+ } else {
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
+ }
+
+ if (iwl_mvm_power_allow_uapsd(mvm, vif))
+ iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
+ cmd->keep_alive_seconds =
+ cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
+ if (mvmvif->dbgfs_pm.skip_over_dtim)
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ else
+ cmd->flags &=
+ cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ }
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
+ cmd->rx_data_timeout =
+ cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
+ cmd->tx_data_timeout =
+ cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
+ cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
+ if (mvmvif->dbgfs_pm.lprx_ena)
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+ else
+ cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
+ }
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
+ cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {
+ if (mvmvif->dbgfs_pm.snooze_ena)
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+ else
+ cmd->flags &=
+ cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
+ }
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
+ u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
+ if (mvmvif->dbgfs_pm.uapsd_misbehaving)
+ cmd->flags |= cpu_to_le16(flag);
+ else
+ cmd->flags &= cpu_to_le16(flag);
+ }
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+}
+
+static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mac_power_cmd cmd = {};
+
+ iwl_mvm_power_build_cmd(mvm, vif, &cmd,
+ mvm->cur_ucode != IWL_UCODE_WOWLAN);
+ iwl_mvm_power_log(mvm, &cmd);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
+#endif
+
+ return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0,
+ sizeof(cmd), &cmd);
+}
+
+int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
+{
+ struct iwl_device_power_cmd cmd = {
+ .flags = 0,
+ };
+
+ if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+ mvm->ps_disabled = true;
+
+ if (!mvm->ps_disabled)
+ cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 :
+ mvm->disable_power_off)
+ cmd.flags &=
+ cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
+ IWL_DEBUG_POWER(mvm,
+ "Sending device power command with flags = 0x%X\n",
+ cmd.flags);
+
+ return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd),
+ &cmd);
+}
+
+void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid,
+ ETH_ALEN))
+ eth_zero_addr(mvmvif->uapsd_misbehaving_bssid);
+}
+
+static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ u8 *ap_sta_id = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ /* The ap_sta_id is not expected to change during current association
+ * so no explicit protection is needed
+ */
+ if (mvmvif->ap_sta_id == *ap_sta_id)
+ memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+ ETH_ALEN);
+}
+
+void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
+ u8 ap_sta_id = le32_to_cpu(notif->sta_id);
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id);
+}
+
+struct iwl_power_vifs {
+ struct iwl_mvm *mvm;
+ struct ieee80211_vif *bf_vif;
+ struct ieee80211_vif *bss_vif;
+ struct ieee80211_vif *p2p_vif;
+ struct ieee80211_vif *ap_vif;
+ struct ieee80211_vif *monitor_vif;
+ bool p2p_active;
+ bool bss_active;
+ bool ap_active;
+ bool monitor_active;
+};
+
+static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ mvmvif->pm_enabled = false;
+}
+
+static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ bool *disable_ps = _data;
+
+ if (mvmvif->phy_ctxt)
+ if (mvmvif->phy_ctxt->id < MAX_PHYS)
+ *disable_ps |= mvmvif->ps_disabled;
+}
+
+static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_power_vifs *power_iterator = _data;
+
+ switch (ieee80211_vif_type_p2p(vif)) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ /* only a single MAC of the same type */
+ WARN_ON(power_iterator->ap_vif);
+ power_iterator->ap_vif = vif;
+ if (mvmvif->phy_ctxt)
+ if (mvmvif->phy_ctxt->id < MAX_PHYS)
+ power_iterator->ap_active = true;
+ break;
+
+ case NL80211_IFTYPE_MONITOR:
+ /* only a single MAC of the same type */
+ WARN_ON(power_iterator->monitor_vif);
+ power_iterator->monitor_vif = vif;
+ if (mvmvif->phy_ctxt)
+ if (mvmvif->phy_ctxt->id < MAX_PHYS)
+ power_iterator->monitor_active = true;
+ break;
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ /* only a single MAC of the same type */
+ WARN_ON(power_iterator->p2p_vif);
+ power_iterator->p2p_vif = vif;
+ if (mvmvif->phy_ctxt)
+ if (mvmvif->phy_ctxt->id < MAX_PHYS)
+ power_iterator->p2p_active = true;
+ break;
+
+ case NL80211_IFTYPE_STATION:
+ power_iterator->bss_vif = vif;
+ if (mvmvif->phy_ctxt)
+ if (mvmvif->phy_ctxt->id < MAX_PHYS)
+ power_iterator->bss_active = true;
+
+ if (mvmvif->bf_data.bf_enabled &&
+ !WARN_ON(power_iterator->bf_vif))
+ power_iterator->bf_vif = vif;
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
+ struct iwl_power_vifs *vifs)
+{
+ struct iwl_mvm_vif *bss_mvmvif = NULL;
+ struct iwl_mvm_vif *p2p_mvmvif = NULL;
+ struct iwl_mvm_vif *ap_mvmvif = NULL;
+ bool client_same_channel = false;
+ bool ap_same_channel = false;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* set pm_enable to false */
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_power_disable_pm_iterator,
+ NULL);
+
+ if (vifs->bss_vif)
+ bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif);
+
+ if (vifs->p2p_vif)
+ p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif);
+
+ if (vifs->ap_vif)
+ ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
+
+ /* don't allow PM if any TDLS stations exist */
+ if (iwl_mvm_tdls_sta_count(mvm, NULL))
+ return;
+
+ /* enable PM on bss if bss stand alone */
+ if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
+ bss_mvmvif->pm_enabled = true;
+ return;
+ }
+
+ /* enable PM on p2p if p2p stand alone */
+ if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+ p2p_mvmvif->pm_enabled = true;
+ return;
+ }
+
+ if (vifs->bss_active && vifs->p2p_active)
+ client_same_channel = (bss_mvmvif->phy_ctxt->id ==
+ p2p_mvmvif->phy_ctxt->id);
+ if (vifs->bss_active && vifs->ap_active)
+ ap_same_channel = (bss_mvmvif->phy_ctxt->id ==
+ ap_mvmvif->phy_ctxt->id);
+
+ /* clients are not stand alone: enable PM if DCM */
+ if (!(client_same_channel || ap_same_channel) &&
+ (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
+ if (vifs->bss_active)
+ bss_mvmvif->pm_enabled = true;
+ if (vifs->p2p_active &&
+ (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM))
+ p2p_mvmvif->pm_enabled = true;
+ return;
+ }
+
+ /*
+ * There is only one channel in the system and there are only
+ * bss and p2p clients that share it
+ */
+ if (client_same_channel && !vifs->ap_active &&
+ (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) {
+ /* share same channel*/
+ bss_mvmvif->pm_enabled = true;
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+ p2p_mvmvif->pm_enabled = true;
+ }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif, char *buf,
+ int bufsz)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mac_power_cmd cmd = {};
+ int pos = 0;
+
+ mutex_lock(&mvm->mutex);
+ memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
+ mutex_unlock(&mvm->mutex);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
+ iwlmvm_mod_params.power_scheme);
+ pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
+ le16_to_cpu(cmd.flags));
+ pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
+ le16_to_cpu(cmd.keep_alive_seconds));
+
+ if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)))
+ return pos;
+
+ pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+ (cmd.flags &
+ cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0);
+ pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+ cmd.skip_dtim_periods);
+ if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
+ pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+ le32_to_cpu(cmd.rx_data_timeout));
+ pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+ le32_to_cpu(cmd.tx_data_timeout));
+ }
+ if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "lprx_rssi_threshold = %d\n",
+ cmd.lprx_rssi_threshold);
+
+ if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
+ return pos;
+
+ pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n",
+ le32_to_cpu(cmd.rx_data_timeout_uapsd));
+ pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n",
+ le32_to_cpu(cmd.tx_data_timeout_uapsd));
+ pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid);
+ pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n",
+ cmd.uapsd_ac_flags);
+ pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n",
+ cmd.uapsd_max_sp);
+ pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n",
+ cmd.heavy_tx_thld_packets);
+ pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n",
+ cmd.heavy_rx_thld_packets);
+ pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n",
+ cmd.heavy_tx_thld_percentage);
+ pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n",
+ cmd.heavy_rx_thld_percentage);
+ pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n",
+ (cmd.flags &
+ cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ?
+ 1 : 0);
+
+ if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)))
+ return pos;
+
+ pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n",
+ cmd.snooze_interval);
+ pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n",
+ cmd.snooze_window);
+
+ return pos;
+}
+
+void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
+ cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
+ cmd->bf_roaming_energy_delta =
+ cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
+ cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD)
+ cmd->bf_temp_threshold =
+ cpu_to_le32(dbgfs_bf->bf_temp_threshold);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER)
+ cmd->bf_temp_fast_filter =
+ cpu_to_le32(dbgfs_bf->bf_temp_fast_filter);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER)
+ cmd->bf_temp_slow_filter =
+ cpu_to_le32(dbgfs_bf->bf_temp_slow_filter);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
+ cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
+ cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
+ cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
+ cmd->ba_enable_beacon_abort =
+ cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort);
+}
+#endif
+
+static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd,
+ u32 cmd_flags,
+ bool d0i3)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period ||
+ vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ return 0;
+
+ iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd, d0i3);
+ if (!d0i3)
+ iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);
+ ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags);
+
+ /* don't change bf_enabled in case of temporary d0i3 configuration */
+ if (!ret && !d0i3)
+ mvmvif->bf_data.bf_enabled = true;
+
+ return ret;
+}
+
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 flags)
+{
+ struct iwl_beacon_filter_cmd cmd = {
+ IWL_BF_CMD_CONFIG_DEFAULTS,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
+ };
+
+ return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
+}
+
+static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_beacon_filter_cmd cmd = {
+ IWL_BF_CMD_CONFIG_DEFAULTS,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
+ };
+
+ if (!mvmvif->bf_data.bf_enabled)
+ return 0;
+
+ if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
+ cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
+
+ mvmvif->bf_data.ba_enabled = enable;
+ return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false);
+}
+
+int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 flags)
+{
+ struct iwl_beacon_filter_cmd cmd = {};
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ return 0;
+
+ ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
+
+ if (!ret)
+ mvmvif->bf_data.bf_enabled = false;
+
+ return ret;
+}
+
+static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm)
+{
+ bool disable_ps;
+ int ret;
+
+ /* disable PS if CAM */
+ disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
+ /* ...or if any of the vifs require PS to be off */
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_power_ps_disabled_iterator,
+ &disable_ps);
+
+ /* update device power state if it has changed */
+ if (mvm->ps_disabled != disable_ps) {
+ bool old_ps_disabled = mvm->ps_disabled;
+
+ mvm->ps_disabled = disable_ps;
+ ret = iwl_mvm_power_update_device(mvm);
+ if (ret) {
+ mvm->ps_disabled = old_ps_disabled;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm,
+ struct iwl_power_vifs *vifs)
+{
+ struct iwl_mvm_vif *mvmvif;
+ bool ba_enable;
+
+ if (!vifs->bf_vif)
+ return 0;
+
+ mvmvif = iwl_mvm_vif_from_mac80211(vifs->bf_vif);
+
+ ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled ||
+ !vifs->bf_vif->bss_conf.ps ||
+ iwl_mvm_vif_low_latency(mvmvif));
+
+ return iwl_mvm_update_beacon_abort(mvm, vifs->bf_vif, ba_enable);
+}
+
+int iwl_mvm_power_update_ps(struct iwl_mvm *mvm)
+{
+ struct iwl_power_vifs vifs = {
+ .mvm = mvm,
+ };
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* get vifs info */
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_power_get_vifs_iterator, &vifs);
+
+ ret = iwl_mvm_power_set_ps(mvm);
+ if (ret)
+ return ret;
+
+ return iwl_mvm_power_set_ba(mvm, &vifs);
+}
+
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
+{
+ struct iwl_power_vifs vifs = {
+ .mvm = mvm,
+ };
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* get vifs info */
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_power_get_vifs_iterator, &vifs);
+
+ iwl_mvm_power_set_pm(mvm, &vifs);
+
+ ret = iwl_mvm_power_set_ps(mvm);
+ if (ret)
+ return ret;
+
+ if (vifs.bss_vif) {
+ ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif);
+ if (ret)
+ return ret;
+ }
+
+ if (vifs.p2p_vif) {
+ ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif);
+ if (ret)
+ return ret;
+ }
+
+ return iwl_mvm_power_set_ba(mvm, &vifs);
+}
+
+int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable, u32 flags)
+{
+ int ret;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mac_power_cmd cmd = {};
+
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ return 0;
+
+ if (!vif->bss_conf.assoc)
+ return 0;
+
+ iwl_mvm_power_build_cmd(mvm, vif, &cmd, !enable);
+
+ iwl_mvm_power_log(mvm, &cmd);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd));
+#endif
+ ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags,
+ sizeof(cmd), &cmd);
+ if (ret)
+ return ret;
+
+ /* configure beacon filtering */
+ if (mvmvif != mvm->bf_allowed_vif)
+ return 0;
+
+ if (enable) {
+ struct iwl_beacon_filter_cmd cmd_bf = {
+ IWL_BF_CMD_CONFIG_D0I3,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
+ };
+ ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
+ flags, true);
+ } else {
+ if (mvmvif->bf_data.bf_enabled)
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags);
+ else
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags);
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
new file mode 100644
index 000000000000..0b762b4f8fad
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
@@ -0,0 +1,328 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+#define QUOTA_100 IWL_MVM_MAX_QUOTA
+#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
+
+struct iwl_mvm_quota_iterator_data {
+ int n_interfaces[MAX_BINDINGS];
+ int colors[MAX_BINDINGS];
+ int low_latency[MAX_BINDINGS];
+ int n_low_latency_bindings;
+ struct ieee80211_vif *disabled_vif;
+};
+
+static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_quota_iterator_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u16 id;
+
+ /* skip disabled interfaces here immediately */
+ if (vif == data->disabled_vif)
+ return;
+
+ if (!mvmvif->phy_ctxt)
+ return;
+
+ /* currently, PHY ID == binding ID */
+ id = mvmvif->phy_ctxt->id;
+
+ /* need at least one binding per PHY */
+ BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);
+
+ if (WARN_ON_ONCE(id >= MAX_BINDINGS))
+ return;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ if (vif->bss_conf.assoc)
+ break;
+ return;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ if (mvmvif->ap_ibss_active)
+ break;
+ return;
+ case NL80211_IFTYPE_MONITOR:
+ if (mvmvif->monitor_active)
+ break;
+ return;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return;
+ default:
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ if (data->colors[id] < 0)
+ data->colors[id] = mvmvif->phy_ctxt->color;
+ else
+ WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
+
+ data->n_interfaces[id]++;
+
+ if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
+ data->n_low_latency_bindings++;
+ data->low_latency[id] = true;
+ }
+}
+
+static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
+ struct iwl_time_quota_cmd *cmd)
+{
+#ifdef CONFIG_NL80211_TESTMODE
+ struct iwl_mvm_vif *mvmvif;
+ int i, phy_id = -1, beacon_int = 0;
+
+ if (!mvm->noa_duration || !mvm->noa_vif)
+ return;
+
+ mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);
+ if (!mvmvif->ap_ibss_active)
+ return;
+
+ phy_id = mvmvif->phy_ctxt->id;
+ beacon_int = mvm->noa_vif->bss_conf.beacon_int;
+
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color);
+ u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;
+ u32 quota = le32_to_cpu(cmd->quotas[i].quota);
+
+ if (id != phy_id)
+ continue;
+
+ quota *= (beacon_int - mvm->noa_duration);
+ quota /= beacon_int;
+
+ IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n",
+ le32_to_cpu(cmd->quotas[i].quota), quota);
+
+ cmd->quotas[i].quota = cpu_to_le32(quota);
+ }
+#endif
+}
+
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+ bool force_update,
+ struct ieee80211_vif *disabled_vif)
+{
+ struct iwl_time_quota_cmd cmd = {};
+ int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat;
+ struct iwl_mvm_quota_iterator_data data = {
+ .n_interfaces = {},
+ .colors = { -1, -1, -1, -1 },
+ .disabled_vif = disabled_vif,
+ };
+ struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd;
+ bool send = false;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* update all upon completion */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ return 0;
+
+ /* iterator data above must match */
+ BUILD_BUG_ON(MAX_BINDINGS != 4);
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_quota_iterator, &data);
+
+ /*
+ * The FW's scheduling session consists of
+ * IWL_MVM_MAX_QUOTA fragments. Divide these fragments
+ * equally between all the bindings that require quota
+ */
+ num_active_macs = 0;
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
+ num_active_macs += data.n_interfaces[i];
+ }
+
+ n_non_lowlat = num_active_macs;
+
+ if (data.n_low_latency_bindings == 1) {
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ if (data.low_latency[i]) {
+ n_non_lowlat -= data.n_interfaces[i];
+ break;
+ }
+ }
+ }
+
+ if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
+ /*
+ * Reserve quota for the low latency binding in case that
+ * there are several data bindings but only a single
+ * low latency one. Split the rest of the quota equally
+ * between the other data interfaces.
+ */
+ quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
+ quota_rem = QUOTA_100 - n_non_lowlat * quota -
+ QUOTA_LOWLAT_MIN;
+ IWL_DEBUG_QUOTA(mvm,
+ "quota: low-latency binding active, remaining quota per other binding: %d\n",
+ quota);
+ } else if (num_active_macs) {
+ /*
+ * There are 0 or more than 1 low latency bindings, or all the
+ * data interfaces belong to the single low latency binding.
+ * Split the quota equally between the data interfaces.
+ */
+ quota = QUOTA_100 / num_active_macs;
+ quota_rem = QUOTA_100 % num_active_macs;
+ IWL_DEBUG_QUOTA(mvm,
+ "quota: splitting evenly per binding: %d\n",
+ quota);
+ } else {
+ /* values don't really matter - won't be used */
+ quota = 0;
+ quota_rem = 0;
+ }
+
+ for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
+ if (data.colors[i] < 0)
+ continue;
+
+ cmd.quotas[idx].id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
+
+ if (data.n_interfaces[i] <= 0)
+ cmd.quotas[idx].quota = cpu_to_le32(0);
+ else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
+ data.low_latency[i])
+ /*
+ * There is more than one binding, but only one of the
+ * bindings is in low latency. For this case, allocate
+ * the minimal required quota for the low latency
+ * binding.
+ */
+ cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
+ else
+ cmd.quotas[idx].quota =
+ cpu_to_le32(quota * data.n_interfaces[i]);
+
+ WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100,
+ "Binding=%d, quota=%u > max=%u\n",
+ idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100);
+
+ cmd.quotas[idx].max_duration = cpu_to_le32(0);
+
+ idx++;
+ }
+
+ /* Give the remainder of the session to the first data binding */
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ if (le32_to_cpu(cmd.quotas[i].quota) != 0) {
+ le32_add_cpu(&cmd.quotas[i].quota, quota_rem);
+ IWL_DEBUG_QUOTA(mvm,
+ "quota: giving remainder of %d to binding %d\n",
+ quota_rem, i);
+ break;
+ }
+ }
+
+ iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
+
+ /* check that we have non-zero quota for all valid bindings */
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ if (cmd.quotas[i].id_and_color != last->quotas[i].id_and_color)
+ send = true;
+ if (cmd.quotas[i].max_duration != last->quotas[i].max_duration)
+ send = true;
+ if (abs((int)le32_to_cpu(cmd.quotas[i].quota) -
+ (int)le32_to_cpu(last->quotas[i].quota))
+ > IWL_MVM_QUOTA_THRESHOLD)
+ send = true;
+ if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID))
+ continue;
+ WARN_ONCE(cmd.quotas[i].quota == 0,
+ "zero quota on binding %d\n", i);
+ }
+
+ if (!send && !force_update) {
+ /* don't send a practically unchanged command, the firmware has
+ * to re-initialize a lot of state and that can have an adverse
+ * impact on it
+ */
+ return 0;
+ }
+
+ err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd);
+
+ if (err)
+ IWL_ERR(mvm, "Failed to send quota: %d\n", err);
+ else
+ mvm->last_quota_cmd = cmd;
+ return err;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
new file mode 100644
index 000000000000..7bb6fd0e4391
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -0,0 +1,4015 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include <linux/workqueue.h>
+#include "rs.h"
+#include "fw-api.h"
+#include "sta.h"
+#include "iwl-op-mode.h"
+#include "mvm.h"
+#include "debugfs.h"
+
+#define RS_NAME "iwl-mvm-rs"
+
+#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */
+
+/* Calculations of success ratio are done in fixed point where 12800 is 100%.
+ * Use this macro when dealing with thresholds consts set as a percentage
+ */
+#define RS_PERCENT(x) (128 * x)
+
+static u8 rs_ht_to_legacy[] = {
+ [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX,
+ [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX,
+ [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX,
+ [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX,
+ [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX,
+ [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX,
+ [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX,
+ [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX,
+ [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX,
+ [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX,
+};
+
+static const u8 ant_toggle_lookup[] = {
+ [ANT_NONE] = ANT_NONE,
+ [ANT_A] = ANT_B,
+ [ANT_B] = ANT_C,
+ [ANT_AB] = ANT_BC,
+ [ANT_C] = ANT_A,
+ [ANT_AC] = ANT_AB,
+ [ANT_BC] = ANT_AC,
+ [ANT_ABC] = ANT_ABC,
+};
+
+#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \
+ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
+ IWL_RATE_HT_SISO_MCS_##s##_PLCP, \
+ IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \
+ IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \
+ IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\
+ IWL_RATE_##rp##M_INDEX, \
+ IWL_RATE_##rn##M_INDEX }
+
+#define IWL_DECLARE_MCS_RATE(s) \
+ [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \
+ IWL_RATE_HT_SISO_MCS_##s##_PLCP, \
+ IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \
+ IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \
+ IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \
+ IWL_RATE_INVM_INDEX, \
+ IWL_RATE_INVM_INDEX }
+
+/*
+ * Parameter order:
+ * rate, ht rate, prev rate, next rate
+ *
+ * If there isn't a valid next or previous rate then INV is used which
+ * maps to IWL_RATE_INVALID
+ *
+ */
+static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
+ IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */
+ IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */
+ IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */
+ IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */
+ IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */
+ IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */
+ IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */
+ IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */
+ IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */
+ IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */
+ IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */
+ IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */
+ IWL_DECLARE_MCS_RATE(7), /* MCS 7 */
+ IWL_DECLARE_MCS_RATE(8), /* MCS 8 */
+ IWL_DECLARE_MCS_RATE(9), /* MCS 9 */
+};
+
+enum rs_action {
+ RS_ACTION_STAY = 0,
+ RS_ACTION_DOWNSCALE = -1,
+ RS_ACTION_UPSCALE = 1,
+};
+
+enum rs_column_mode {
+ RS_INVALID = 0,
+ RS_LEGACY,
+ RS_SISO,
+ RS_MIMO2,
+};
+
+#define MAX_NEXT_COLUMNS 7
+#define MAX_COLUMN_CHECKS 3
+
+struct rs_tx_column;
+
+typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct rs_rate *rate,
+ const struct rs_tx_column *next_col);
+
+struct rs_tx_column {
+ enum rs_column_mode mode;
+ u8 ant;
+ bool sgi;
+ enum rs_column next_columns[MAX_NEXT_COLUMNS];
+ allow_column_func_t checks[MAX_COLUMN_CHECKS];
+};
+
+static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct rs_rate *rate,
+ const struct rs_tx_column *next_col)
+{
+ return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant);
+}
+
+static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct rs_rate *rate,
+ const struct rs_tx_column *next_col)
+{
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_vif *mvmvif;
+
+ if (!sta->ht_cap.ht_supported)
+ return false;
+
+ if (sta->smps_mode == IEEE80211_SMPS_STATIC)
+ return false;
+
+ if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2)
+ return false;
+
+ if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
+ return false;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+
+ if (mvm->nvm_data->sku_cap_mimo_disabled)
+ return false;
+
+ return true;
+}
+
+static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct rs_rate *rate,
+ const struct rs_tx_column *next_col)
+{
+ if (!sta->ht_cap.ht_supported)
+ return false;
+
+ return true;
+}
+
+static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct rs_rate *rate,
+ const struct rs_tx_column *next_col)
+{
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+
+ if (is_ht20(rate) && (ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_20))
+ return true;
+ if (is_ht40(rate) && (ht_cap->cap &
+ IEEE80211_HT_CAP_SGI_40))
+ return true;
+ if (is_ht80(rate) && (vht_cap->cap &
+ IEEE80211_VHT_CAP_SHORT_GI_80))
+ return true;
+
+ return false;
+}
+
+static const struct rs_tx_column rs_tx_columns[] = {
+ [RS_COLUMN_LEGACY_ANT_A] = {
+ .mode = RS_LEGACY,
+ .ant = ANT_A,
+ .next_columns = {
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_MIMO2,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_ant_allow,
+ },
+ },
+ [RS_COLUMN_LEGACY_ANT_B] = {
+ .mode = RS_LEGACY,
+ .ant = ANT_B,
+ .next_columns = {
+ RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_SISO_ANT_B,
+ RS_COLUMN_MIMO2,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_ant_allow,
+ },
+ },
+ [RS_COLUMN_SISO_ANT_A] = {
+ .mode = RS_SISO,
+ .ant = ANT_A,
+ .next_columns = {
+ RS_COLUMN_SISO_ANT_B,
+ RS_COLUMN_MIMO2,
+ RS_COLUMN_SISO_ANT_A_SGI,
+ RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_siso_allow,
+ rs_ant_allow,
+ },
+ },
+ [RS_COLUMN_SISO_ANT_B] = {
+ .mode = RS_SISO,
+ .ant = ANT_B,
+ .next_columns = {
+ RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_MIMO2,
+ RS_COLUMN_SISO_ANT_B_SGI,
+ RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_siso_allow,
+ rs_ant_allow,
+ },
+ },
+ [RS_COLUMN_SISO_ANT_A_SGI] = {
+ .mode = RS_SISO,
+ .ant = ANT_A,
+ .sgi = true,
+ .next_columns = {
+ RS_COLUMN_SISO_ANT_B_SGI,
+ RS_COLUMN_MIMO2_SGI,
+ RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_siso_allow,
+ rs_ant_allow,
+ rs_sgi_allow,
+ },
+ },
+ [RS_COLUMN_SISO_ANT_B_SGI] = {
+ .mode = RS_SISO,
+ .ant = ANT_B,
+ .sgi = true,
+ .next_columns = {
+ RS_COLUMN_SISO_ANT_A_SGI,
+ RS_COLUMN_MIMO2_SGI,
+ RS_COLUMN_SISO_ANT_B,
+ RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_siso_allow,
+ rs_ant_allow,
+ rs_sgi_allow,
+ },
+ },
+ [RS_COLUMN_MIMO2] = {
+ .mode = RS_MIMO2,
+ .ant = ANT_AB,
+ .next_columns = {
+ RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_MIMO2_SGI,
+ RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_mimo_allow,
+ },
+ },
+ [RS_COLUMN_MIMO2_SGI] = {
+ .mode = RS_MIMO2,
+ .ant = ANT_AB,
+ .sgi = true,
+ .next_columns = {
+ RS_COLUMN_SISO_ANT_A_SGI,
+ RS_COLUMN_MIMO2,
+ RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ RS_COLUMN_INVALID,
+ },
+ .checks = {
+ rs_mimo_allow,
+ rs_sgi_allow,
+ },
+ },
+};
+
+static inline u8 rs_extract_rate(u32 rate_n_flags)
+{
+ /* also works for HT because bits 7:6 are zero there */
+ return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK);
+}
+
+static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
+{
+ int idx = 0;
+
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK;
+ idx += IWL_RATE_MCS_0_INDEX;
+
+ /* skip 9M not supported in HT*/
+ if (idx >= IWL_RATE_9M_INDEX)
+ idx += 1;
+ if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE))
+ return idx;
+ } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+ idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+ idx += IWL_RATE_MCS_0_INDEX;
+
+ /* skip 9M not supported in VHT*/
+ if (idx >= IWL_RATE_9M_INDEX)
+ idx++;
+ if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE))
+ return idx;
+ } else {
+ /* legacy rate format, search for match in table */
+
+ u8 legacy_rate = rs_extract_rate(rate_n_flags);
+ for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
+ if (iwl_rates[idx].plcp == legacy_rate)
+ return idx;
+ }
+
+ return IWL_RATE_INVALID;
+}
+
+static void rs_rate_scale_perform(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ int tid);
+static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ const struct rs_rate *initial_rate);
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
+
+/**
+ * The following tables contain the expected throughput metrics for all rates
+ *
+ * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
+ *
+ * where invalid entries are zeros.
+ *
+ * CCK rates are only valid in legacy table and will only be used in G
+ * (2.4 GHz) band.
+ */
+
+static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
+ 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0
+};
+
+/* Expected TpT tables. 4 indexes:
+ * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI
+ */
+static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0},
+ {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0},
+ {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0},
+ {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0},
+};
+
+static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275},
+ {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280},
+ {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173},
+ {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284},
+};
+
+static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308},
+ {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312},
+ {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466},
+ {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691},
+};
+
+static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0},
+ {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0},
+ {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0},
+ {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0},
+};
+
+static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300},
+ {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303},
+ {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053},
+ {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221},
+};
+
+static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319},
+ {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320},
+ {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219},
+ {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545},
+};
+
+/* mbps, mcs */
+static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
+ { "1", "BPSK DSSS"},
+ { "2", "QPSK DSSS"},
+ {"5.5", "BPSK CCK"},
+ { "11", "QPSK CCK"},
+ { "6", "BPSK 1/2"},
+ { "9", "BPSK 1/2"},
+ { "12", "QPSK 1/2"},
+ { "18", "QPSK 3/4"},
+ { "24", "16QAM 1/2"},
+ { "36", "16QAM 3/4"},
+ { "48", "64QAM 2/3"},
+ { "54", "64QAM 3/4"},
+ { "60", "64QAM 5/6"},
+};
+
+#define MCS_INDEX_PER_STREAM (8)
+
+static const char *rs_pretty_ant(u8 ant)
+{
+ static const char * const ant_name[] = {
+ [ANT_NONE] = "None",
+ [ANT_A] = "A",
+ [ANT_B] = "B",
+ [ANT_AB] = "AB",
+ [ANT_C] = "C",
+ [ANT_AC] = "AC",
+ [ANT_BC] = "BC",
+ [ANT_ABC] = "ABC",
+ };
+
+ if (ant > ANT_ABC)
+ return "UNKNOWN";
+
+ return ant_name[ant];
+}
+
+static const char *rs_pretty_lq_type(enum iwl_table_type type)
+{
+ static const char * const lq_types[] = {
+ [LQ_NONE] = "NONE",
+ [LQ_LEGACY_A] = "LEGACY_A",
+ [LQ_LEGACY_G] = "LEGACY_G",
+ [LQ_HT_SISO] = "HT SISO",
+ [LQ_HT_MIMO2] = "HT MIMO",
+ [LQ_VHT_SISO] = "VHT SISO",
+ [LQ_VHT_MIMO2] = "VHT MIMO",
+ };
+
+ if (type < LQ_NONE || type >= LQ_MAX)
+ return "UNKNOWN";
+
+ return lq_types[type];
+}
+
+static char *rs_pretty_rate(const struct rs_rate *rate)
+{
+ static char buf[40];
+ static const char * const legacy_rates[] = {
+ [IWL_RATE_1M_INDEX] = "1M",
+ [IWL_RATE_2M_INDEX] = "2M",
+ [IWL_RATE_5M_INDEX] = "5.5M",
+ [IWL_RATE_11M_INDEX] = "11M",
+ [IWL_RATE_6M_INDEX] = "6M",
+ [IWL_RATE_9M_INDEX] = "9M",
+ [IWL_RATE_12M_INDEX] = "12M",
+ [IWL_RATE_18M_INDEX] = "18M",
+ [IWL_RATE_24M_INDEX] = "24M",
+ [IWL_RATE_36M_INDEX] = "36M",
+ [IWL_RATE_48M_INDEX] = "48M",
+ [IWL_RATE_54M_INDEX] = "54M",
+ };
+ static const char *const ht_vht_rates[] = {
+ [IWL_RATE_MCS_0_INDEX] = "MCS0",
+ [IWL_RATE_MCS_1_INDEX] = "MCS1",
+ [IWL_RATE_MCS_2_INDEX] = "MCS2",
+ [IWL_RATE_MCS_3_INDEX] = "MCS3",
+ [IWL_RATE_MCS_4_INDEX] = "MCS4",
+ [IWL_RATE_MCS_5_INDEX] = "MCS5",
+ [IWL_RATE_MCS_6_INDEX] = "MCS6",
+ [IWL_RATE_MCS_7_INDEX] = "MCS7",
+ [IWL_RATE_MCS_8_INDEX] = "MCS8",
+ [IWL_RATE_MCS_9_INDEX] = "MCS9",
+ };
+ const char *rate_str;
+
+ if (is_type_legacy(rate->type) && (rate->index <= IWL_RATE_54M_INDEX))
+ rate_str = legacy_rates[rate->index];
+ else if ((is_type_ht(rate->type) || is_type_vht(rate->type)) &&
+ (rate->index <= IWL_RATE_MCS_9_INDEX))
+ rate_str = ht_vht_rates[rate->index];
+ else
+ rate_str = "BAD_RATE";
+
+ sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type),
+ rs_pretty_ant(rate->ant), rate_str);
+ return buf;
+}
+
+static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate,
+ const char *prefix)
+{
+ IWL_DEBUG_RATE(mvm,
+ "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n",
+ prefix, rs_pretty_rate(rate), rate->bw,
+ rate->sgi, rate->ldpc, rate->stbc);
+}
+
+static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
+{
+ window->data = 0;
+ window->success_counter = 0;
+ window->success_ratio = IWL_INVALID_VALUE;
+ window->counter = 0;
+ window->average_tpt = IWL_INVALID_VALUE;
+}
+
+static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm,
+ struct iwl_scale_tbl_info *tbl)
+{
+ int i;
+
+ IWL_DEBUG_RATE(mvm, "Clearing up window stats\n");
+ for (i = 0; i < IWL_RATE_COUNT; i++)
+ rs_rate_scale_clear_window(&tbl->win[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++)
+ rs_rate_scale_clear_window(&tbl->tpc_win[i]);
+}
+
+static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
+{
+ return (ant_type & valid_antenna) == ant_type;
+}
+
+static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_data, u8 tid,
+ struct ieee80211_sta *sta)
+{
+ int ret = -EAGAIN;
+
+ IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
+ sta->addr, tid);
+ ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
+ if (ret == -EAGAIN) {
+ /*
+ * driver and mac80211 is out of sync
+ * this might be cause by reloading firmware
+ * stop the tx ba session here
+ */
+ IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n",
+ tid);
+ ieee80211_stop_tx_ba_session(sta, tid);
+ }
+ return ret;
+}
+
+static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid,
+ struct iwl_lq_sta *lq_data,
+ struct ieee80211_sta *sta)
+{
+ if (tid < IWL_MAX_TID_COUNT)
+ rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta);
+ else
+ IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n",
+ tid, IWL_MAX_TID_COUNT);
+}
+
+static inline int get_num_of_ant_from_rate(u32 rate_n_flags)
+{
+ return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) +
+ !!(rate_n_flags & RATE_MCS_ANT_B_MSK) +
+ !!(rate_n_flags & RATE_MCS_ANT_C_MSK);
+}
+
+/*
+ * Static function to get the expected throughput from an iwl_scale_tbl_info
+ * that wraps a NULL pointer check
+ */
+static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
+{
+ if (tbl->expected_tpt)
+ return tbl->expected_tpt[rs_index];
+ return 0;
+}
+
+/**
+ * rs_collect_tx_data - Update the success/failure sliding window
+ *
+ * We keep a sliding window of the last 62 packets transmitted
+ * at this rate. window->data contains the bitmask of successful
+ * packets.
+ */
+static int _rs_collect_tx_data(struct iwl_mvm *mvm,
+ struct iwl_scale_tbl_info *tbl,
+ int scale_index, int attempts, int successes,
+ struct iwl_rate_scale_data *window)
+{
+ static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1));
+ s32 fail_count, tpt;
+
+ /* Get expected throughput */
+ tpt = get_expected_tpt(tbl, scale_index);
+
+ /*
+ * Keep track of only the latest 62 tx frame attempts in this rate's
+ * history window; anything older isn't really relevant any more.
+ * If we have filled up the sliding window, drop the oldest attempt;
+ * if the oldest attempt (highest bit in bitmap) shows "success",
+ * subtract "1" from the success counter (this is the main reason
+ * we keep these bitmaps!).
+ */
+ while (attempts > 0) {
+ if (window->counter >= IWL_RATE_MAX_WINDOW) {
+ /* remove earliest */
+ window->counter = IWL_RATE_MAX_WINDOW - 1;
+
+ if (window->data & mask) {
+ window->data &= ~mask;
+ window->success_counter--;
+ }
+ }
+
+ /* Increment frames-attempted counter */
+ window->counter++;
+
+ /* Shift bitmap by one frame to throw away oldest history */
+ window->data <<= 1;
+
+ /* Mark the most recent #successes attempts as successful */
+ if (successes > 0) {
+ window->success_counter++;
+ window->data |= 0x1;
+ successes--;
+ }
+
+ attempts--;
+ }
+
+ /* Calculate current success ratio, avoid divide-by-0! */
+ if (window->counter > 0)
+ window->success_ratio = 128 * (100 * window->success_counter)
+ / window->counter;
+ else
+ window->success_ratio = IWL_INVALID_VALUE;
+
+ fail_count = window->counter - window->success_counter;
+
+ /* Calculate average throughput, if we have enough history. */
+ if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) ||
+ (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH))
+ window->average_tpt = (window->success_ratio * tpt + 64) / 128;
+ else
+ window->average_tpt = IWL_INVALID_VALUE;
+
+ return 0;
+}
+
+static int rs_collect_tx_data(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl,
+ int scale_index, int attempts, int successes,
+ u8 reduced_txp)
+{
+ struct iwl_rate_scale_data *window = NULL;
+ int ret;
+
+ if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
+ return -EINVAL;
+
+ if (tbl->column != RS_COLUMN_INVALID) {
+ struct lq_sta_pers *pers = &lq_sta->pers;
+
+ pers->tx_stats[tbl->column][scale_index].total += attempts;
+ pers->tx_stats[tbl->column][scale_index].success += successes;
+ }
+
+ /* Select window for current tx bit rate */
+ window = &(tbl->win[scale_index]);
+
+ ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes,
+ window);
+ if (ret)
+ return ret;
+
+ if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION))
+ return -EINVAL;
+
+ window = &tbl->tpc_win[reduced_txp];
+ return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes,
+ window);
+}
+
+/* Convert rs_rate object into ucode rate bitmask */
+static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm,
+ struct rs_rate *rate)
+{
+ u32 ucode_rate = 0;
+ int index = rate->index;
+
+ ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) &
+ RATE_MCS_ANT_ABC_MSK);
+
+ if (is_legacy(rate)) {
+ ucode_rate |= iwl_rates[index].plcp;
+ if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
+ ucode_rate |= RATE_MCS_CCK_MSK;
+ return ucode_rate;
+ }
+
+ if (is_ht(rate)) {
+ if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) {
+ IWL_ERR(mvm, "Invalid HT rate index %d\n", index);
+ index = IWL_LAST_HT_RATE;
+ }
+ ucode_rate |= RATE_MCS_HT_MSK;
+
+ if (is_ht_siso(rate))
+ ucode_rate |= iwl_rates[index].plcp_ht_siso;
+ else if (is_ht_mimo2(rate))
+ ucode_rate |= iwl_rates[index].plcp_ht_mimo2;
+ else
+ WARN_ON_ONCE(1);
+ } else if (is_vht(rate)) {
+ if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) {
+ IWL_ERR(mvm, "Invalid VHT rate index %d\n", index);
+ index = IWL_LAST_VHT_RATE;
+ }
+ ucode_rate |= RATE_MCS_VHT_MSK;
+ if (is_vht_siso(rate))
+ ucode_rate |= iwl_rates[index].plcp_vht_siso;
+ else if (is_vht_mimo2(rate))
+ ucode_rate |= iwl_rates[index].plcp_vht_mimo2;
+ else
+ WARN_ON_ONCE(1);
+
+ } else {
+ IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type);
+ }
+
+ if (is_siso(rate) && rate->stbc) {
+ /* To enable STBC we need to set both a flag and ANT_AB */
+ ucode_rate |= RATE_MCS_ANT_AB_MSK;
+ ucode_rate |= RATE_MCS_VHT_STBC_MSK;
+ }
+
+ ucode_rate |= rate->bw;
+ if (rate->sgi)
+ ucode_rate |= RATE_MCS_SGI_MSK;
+ if (rate->ldpc)
+ ucode_rate |= RATE_MCS_LDPC_MSK;
+
+ return ucode_rate;
+}
+
+/* Convert a ucode rate into an rs_rate object */
+static int rs_rate_from_ucode_rate(const u32 ucode_rate,
+ enum ieee80211_band band,
+ struct rs_rate *rate)
+{
+ u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK;
+ u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate);
+ u8 nss;
+
+ memset(rate, 0, sizeof(*rate));
+ rate->index = iwl_hwrate_to_plcp_idx(ucode_rate);
+
+ if (rate->index == IWL_RATE_INVALID)
+ return -EINVAL;
+
+ rate->ant = (ant_msk >> RATE_MCS_ANT_POS);
+
+ /* Legacy */
+ if (!(ucode_rate & RATE_MCS_HT_MSK) &&
+ !(ucode_rate & RATE_MCS_VHT_MSK)) {
+ if (num_of_ant == 1) {
+ if (band == IEEE80211_BAND_5GHZ)
+ rate->type = LQ_LEGACY_A;
+ else
+ rate->type = LQ_LEGACY_G;
+ }
+
+ return 0;
+ }
+
+ /* HT or VHT */
+ if (ucode_rate & RATE_MCS_SGI_MSK)
+ rate->sgi = true;
+ if (ucode_rate & RATE_MCS_LDPC_MSK)
+ rate->ldpc = true;
+ if (ucode_rate & RATE_MCS_VHT_STBC_MSK)
+ rate->stbc = true;
+ if (ucode_rate & RATE_MCS_BF_MSK)
+ rate->bfer = true;
+
+ rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK;
+
+ if (ucode_rate & RATE_MCS_HT_MSK) {
+ nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >>
+ RATE_HT_MCS_NSS_POS) + 1;
+
+ if (nss == 1) {
+ rate->type = LQ_HT_SISO;
+ WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1,
+ "stbc %d bfer %d",
+ rate->stbc, rate->bfer);
+ } else if (nss == 2) {
+ rate->type = LQ_HT_MIMO2;
+ WARN_ON_ONCE(num_of_ant != 2);
+ } else {
+ WARN_ON_ONCE(1);
+ }
+ } else if (ucode_rate & RATE_MCS_VHT_MSK) {
+ nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >>
+ RATE_VHT_MCS_NSS_POS) + 1;
+
+ if (nss == 1) {
+ rate->type = LQ_VHT_SISO;
+ WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1,
+ "stbc %d bfer %d",
+ rate->stbc, rate->bfer);
+ } else if (nss == 2) {
+ rate->type = LQ_VHT_MIMO2;
+ WARN_ON_ONCE(num_of_ant != 2);
+ } else {
+ WARN_ON_ONCE(1);
+ }
+ }
+
+ WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160);
+ WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 &&
+ !is_vht(rate));
+
+ return 0;
+}
+
+/* switch to another antenna/antennas and return 1 */
+/* if no other valid antenna found, return 0 */
+static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate)
+{
+ u8 new_ant_type;
+
+ if (!rate->ant || rate->ant > ANT_ABC)
+ return 0;
+
+ if (!rs_is_valid_ant(valid_ant, rate->ant))
+ return 0;
+
+ new_ant_type = ant_toggle_lookup[rate->ant];
+
+ while ((new_ant_type != rate->ant) &&
+ !rs_is_valid_ant(valid_ant, new_ant_type))
+ new_ant_type = ant_toggle_lookup[new_ant_type];
+
+ if (new_ant_type == rate->ant)
+ return 0;
+
+ rate->ant = new_ant_type;
+
+ return 1;
+}
+
+static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
+ struct rs_rate *rate)
+{
+ if (is_legacy(rate))
+ return lq_sta->active_legacy_rate;
+ else if (is_siso(rate))
+ return lq_sta->active_siso_rate;
+ else if (is_mimo2(rate))
+ return lq_sta->active_mimo2_rate;
+
+ WARN_ON_ONCE(1);
+ return 0;
+}
+
+static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
+ int rate_type)
+{
+ u8 high = IWL_RATE_INVALID;
+ u8 low = IWL_RATE_INVALID;
+
+ /* 802.11A or ht walks to the next literal adjacent rate in
+ * the rate table */
+ if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) {
+ int i;
+ u32 mask;
+
+ /* Find the previous rate that is in the rate mask */
+ i = index - 1;
+ for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
+ if (rate_mask & mask) {
+ low = i;
+ break;
+ }
+ }
+
+ /* Find the next rate that is in the rate mask */
+ i = index + 1;
+ for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
+ if (rate_mask & mask) {
+ high = i;
+ break;
+ }
+ }
+
+ return (high << 8) | low;
+ }
+
+ low = index;
+ while (low != IWL_RATE_INVALID) {
+ low = iwl_rates[low].prev_rs;
+ if (low == IWL_RATE_INVALID)
+ break;
+ if (rate_mask & (1 << low))
+ break;
+ }
+
+ high = index;
+ while (high != IWL_RATE_INVALID) {
+ high = iwl_rates[high].next_rs;
+ if (high == IWL_RATE_INVALID)
+ break;
+ if (rate_mask & (1 << high))
+ break;
+ }
+
+ return (high << 8) | low;
+}
+
+static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta,
+ struct rs_rate *rate)
+{
+ return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate);
+}
+
+/* Get the next supported lower rate in the current column.
+ * Return true if bottom rate in the current column was reached
+ */
+static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
+ struct rs_rate *rate)
+{
+ u8 low;
+ u16 high_low;
+ u16 rate_mask;
+ struct iwl_mvm *mvm = lq_sta->pers.drv;
+
+ rate_mask = rs_get_supported_rates(lq_sta, rate);
+ high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
+ rate->type);
+ low = high_low & 0xff;
+
+ /* Bottom rate of column reached */
+ if (low == IWL_RATE_INVALID)
+ return true;
+
+ rate->index = low;
+ return false;
+}
+
+/* Get the next rate to use following a column downgrade */
+static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
+ struct rs_rate *rate)
+{
+ struct iwl_mvm *mvm = lq_sta->pers.drv;
+
+ if (is_legacy(rate)) {
+ /* No column to downgrade from Legacy */
+ return;
+ } else if (is_siso(rate)) {
+ /* Downgrade to Legacy if we were in SISO */
+ if (lq_sta->band == IEEE80211_BAND_5GHZ)
+ rate->type = LQ_LEGACY_A;
+ else
+ rate->type = LQ_LEGACY_G;
+
+ rate->bw = RATE_MCS_CHAN_WIDTH_20;
+
+ WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX ||
+ rate->index > IWL_RATE_MCS_9_INDEX);
+
+ rate->index = rs_ht_to_legacy[rate->index];
+ rate->ldpc = false;
+ } else {
+ /* Downgrade to SISO with same MCS if in MIMO */
+ rate->type = is_vht_mimo2(rate) ?
+ LQ_VHT_SISO : LQ_HT_SISO;
+ }
+
+ if (num_of_ant(rate->ant) > 1)
+ rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm));
+
+ /* Relevant in both switching to SISO or Legacy */
+ rate->sgi = false;
+
+ if (!rs_rate_supported(lq_sta, rate))
+ rs_get_lower_rate_in_column(lq_sta, rate);
+}
+
+/* Check if both rates are identical
+ * allow_ant_mismatch enables matching a SISO rate on ANT_A or ANT_B
+ * with a rate indicating STBC/BFER and ANT_AB.
+ */
+static inline bool rs_rate_equal(struct rs_rate *a,
+ struct rs_rate *b,
+ bool allow_ant_mismatch)
+
+{
+ bool ant_match = (a->ant == b->ant) && (a->stbc == b->stbc) &&
+ (a->bfer == b->bfer);
+
+ if (allow_ant_mismatch) {
+ if (a->stbc || a->bfer) {
+ WARN_ONCE(a->ant != ANT_AB, "stbc %d bfer %d ant %d",
+ a->stbc, a->bfer, a->ant);
+ ant_match |= (b->ant == ANT_A || b->ant == ANT_B);
+ } else if (b->stbc || b->bfer) {
+ WARN_ONCE(b->ant != ANT_AB, "stbc %d bfer %d ant %d",
+ b->stbc, b->bfer, b->ant);
+ ant_match |= (a->ant == ANT_A || a->ant == ANT_B);
+ }
+ }
+
+ return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) &&
+ (a->ldpc == b->ldpc) && (a->index == b->index) && ant_match;
+}
+
+/* Check if both rates share the same column */
+static inline bool rs_rate_column_match(struct rs_rate *a,
+ struct rs_rate *b)
+{
+ bool ant_match;
+
+ if (a->stbc || a->bfer)
+ ant_match = (b->ant == ANT_A || b->ant == ANT_B);
+ else
+ ant_match = (a->ant == b->ant);
+
+ return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi)
+ && ant_match;
+}
+
+static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate)
+{
+ if (is_legacy(rate)) {
+ if (rate->ant == ANT_A)
+ return RS_COLUMN_LEGACY_ANT_A;
+
+ if (rate->ant == ANT_B)
+ return RS_COLUMN_LEGACY_ANT_B;
+
+ goto err;
+ }
+
+ if (is_siso(rate)) {
+ if (rate->ant == ANT_A || rate->stbc || rate->bfer)
+ return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI :
+ RS_COLUMN_SISO_ANT_A;
+
+ if (rate->ant == ANT_B)
+ return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI :
+ RS_COLUMN_SISO_ANT_B;
+
+ goto err;
+ }
+
+ if (is_mimo(rate))
+ return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2;
+
+err:
+ return RS_COLUMN_INVALID;
+}
+
+static u8 rs_get_tid(struct ieee80211_hdr *hdr)
+{
+ u8 tid = IWL_MAX_TID_COUNT;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & 0xf;
+ }
+
+ if (unlikely(tid > IWL_MAX_TID_COUNT))
+ tid = IWL_MAX_TID_COUNT;
+
+ return tid;
+}
+
+void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ int tid, struct ieee80211_tx_info *info)
+{
+ int legacy_success;
+ int retries;
+ int i;
+ struct iwl_lq_cmd *table;
+ u32 lq_hwrate;
+ struct rs_rate lq_rate, tx_resp_rate;
+ struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
+ u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0];
+ u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta;
+ bool allow_ant_mismatch = fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_LQ_SS_PARAMS);
+
+ /* Treat uninitialized rate scaling data same as non-existing. */
+ if (!lq_sta) {
+ IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
+ return;
+ } else if (!lq_sta->pers.drv) {
+ IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
+ return;
+ }
+
+ /* This packet was aggregated but doesn't carry status info */
+ if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+ !(info->flags & IEEE80211_TX_STAT_AMPDU))
+ return;
+
+ rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate);
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /* Disable last tx check if we are debugging with fixed rate but
+ * update tx stats */
+ if (lq_sta->pers.dbg_fixed_rate) {
+ int index = tx_resp_rate.index;
+ enum rs_column column;
+ int attempts, success;
+
+ column = rs_get_column_from_rate(&tx_resp_rate);
+ if (WARN_ONCE(column == RS_COLUMN_INVALID,
+ "Can't map rate 0x%x to column",
+ tx_resp_hwrate))
+ return;
+
+ if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+ attempts = info->status.ampdu_len;
+ success = info->status.ampdu_ack_len;
+ } else {
+ attempts = info->status.rates[0].count;
+ success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ }
+
+ lq_sta->pers.tx_stats[column][index].total += attempts;
+ lq_sta->pers.tx_stats[column][index].success += success;
+
+ IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
+ tx_resp_hwrate, success, attempts);
+ return;
+ }
+#endif
+
+ if (time_after(jiffies,
+ (unsigned long)(lq_sta->last_tx +
+ (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) {
+ int t;
+
+ IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
+ for (t = 0; t < IWL_MAX_TID_COUNT; t++)
+ ieee80211_stop_tx_ba_session(sta, t);
+
+ iwl_mvm_rs_rate_init(mvm, sta, info->band, false);
+ return;
+ }
+ lq_sta->last_tx = jiffies;
+
+ /* Ignore this Tx frame response if its initial rate doesn't match
+ * that of latest Link Quality command. There may be stragglers
+ * from a previous Link Quality command, but we're no longer interested
+ * in those; they're either from the "active" mode while we're trying
+ * to check "search" mode, or a prior "search" mode after we've moved
+ * to a new "search" mode (which might become the new "active" mode).
+ */
+ table = &lq_sta->lq;
+ lq_hwrate = le32_to_cpu(table->rs_table[0]);
+ rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate);
+
+ /* Here we actually compare this rate to the latest LQ command */
+ if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) {
+ IWL_DEBUG_RATE(mvm,
+ "initial tx resp rate 0x%x does not match 0x%x\n",
+ tx_resp_hwrate, lq_hwrate);
+
+ /*
+ * Since rates mis-match, the last LQ command may have failed.
+ * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
+ * ... driver.
+ */
+ lq_sta->missed_rate_counter++;
+ if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) {
+ lq_sta->missed_rate_counter = 0;
+ IWL_DEBUG_RATE(mvm,
+ "Too many rates mismatch. Send sync LQ. rs_state %d\n",
+ lq_sta->rs_state);
+ iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
+ }
+ /* Regardless, ignore this status info for outdated rate */
+ return;
+ } else
+ /* Rate did match, so reset the missed_rate_counter */
+ lq_sta->missed_rate_counter = 0;
+
+ if (!lq_sta->search_better_tbl) {
+ curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ } else {
+ curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ }
+
+ if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) {
+ IWL_DEBUG_RATE(mvm,
+ "Neither active nor search matches tx rate\n");
+ tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE");
+ tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH");
+ rs_dump_rate(mvm, &lq_rate, "ACTUAL");
+
+ /*
+ * no matching table found, let's by-pass the data collection
+ * and continue to perform rate scale to find the rate table
+ */
+ rs_stay_in_table(lq_sta, true);
+ goto done;
+ }
+
+ /*
+ * Updating the frame history depends on whether packets were
+ * aggregated.
+ *
+ * For aggregation, all packets were transmitted at the same rate, the
+ * first index into rate scale table.
+ */
+ if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+ /* ampdu_ack_len = 0 marks no BA was received. In this case
+ * treat it as a single frame loss as we don't want the success
+ * ratio to dip too quickly because a BA wasn't received
+ */
+ if (info->status.ampdu_ack_len == 0)
+ info->status.ampdu_len = 1;
+
+ rs_collect_tx_data(mvm, lq_sta, curr_tbl, lq_rate.index,
+ info->status.ampdu_len,
+ info->status.ampdu_ack_len,
+ reduced_txp);
+
+ /* Update success/fail counts if not searching for new mode */
+ if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
+ lq_sta->total_success += info->status.ampdu_ack_len;
+ lq_sta->total_failed += (info->status.ampdu_len -
+ info->status.ampdu_ack_len);
+ }
+ } else {
+ /* For legacy, update frame history with for each Tx retry. */
+ retries = info->status.rates[0].count - 1;
+ /* HW doesn't send more than 15 retries */
+ retries = min(retries, 15);
+
+ /* The last transmission may have been successful */
+ legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ /* Collect data for each rate used during failed TX attempts */
+ for (i = 0; i <= retries; ++i) {
+ lq_hwrate = le32_to_cpu(table->rs_table[i]);
+ rs_rate_from_ucode_rate(lq_hwrate, info->band,
+ &lq_rate);
+ /*
+ * Only collect stats if retried rate is in the same RS
+ * table as active/search.
+ */
+ if (rs_rate_column_match(&lq_rate, &curr_tbl->rate))
+ tmp_tbl = curr_tbl;
+ else if (rs_rate_column_match(&lq_rate,
+ &other_tbl->rate))
+ tmp_tbl = other_tbl;
+ else
+ continue;
+
+ rs_collect_tx_data(mvm, lq_sta, tmp_tbl, lq_rate.index,
+ 1, i < retries ? 0 : legacy_success,
+ reduced_txp);
+ }
+
+ /* Update success/fail counts if not searching for new mode */
+ if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
+ lq_sta->total_success += legacy_success;
+ lq_sta->total_failed += retries + (1 - legacy_success);
+ }
+ }
+ /* The last TX rate is cached in lq_sta; it's set in if/else above */
+ lq_sta->last_rate_n_flags = lq_hwrate;
+ IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
+done:
+ /* See if there's a better rate or modulation mode to try. */
+ if (sta->supp_rates[info->band])
+ rs_rate_scale_perform(mvm, sta, lq_sta, tid);
+}
+
+/*
+ * mac80211 sends us Tx status
+ */
+static void rs_mac80211_tx_status(void *mvm_r,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r;
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+ return;
+
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ info->flags & IEEE80211_TX_CTL_NO_ACK)
+ return;
+
+ iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info);
+}
+
+/*
+ * Begin a period of staying with a selected modulation mode.
+ * Set "stay_in_tbl" flag to prevent any mode switches.
+ * Set frame tx success limits according to legacy vs. high-throughput,
+ * and reset overall (spanning all rates) tx success history statistics.
+ * These control how long we stay using same modulation mode before
+ * searching for a new mode.
+ */
+static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy,
+ struct iwl_lq_sta *lq_sta)
+{
+ IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n");
+ lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN;
+ if (is_legacy) {
+ lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT;
+ lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT;
+ lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT;
+ } else {
+ lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT;
+ lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT;
+ lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT;
+ }
+ lq_sta->table_count = 0;
+ lq_sta->total_failed = 0;
+ lq_sta->total_success = 0;
+ lq_sta->flush_timer = jiffies;
+ lq_sta->visited_columns = 0;
+}
+
+static inline int rs_get_max_rate_from_mask(unsigned long rate_mask)
+{
+ if (rate_mask)
+ return find_last_bit(&rate_mask, BITS_PER_LONG);
+ return IWL_RATE_INVALID;
+}
+
+static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta,
+ const struct rs_tx_column *column)
+{
+ switch (column->mode) {
+ case RS_LEGACY:
+ return lq_sta->max_legacy_rate_idx;
+ case RS_SISO:
+ return lq_sta->max_siso_rate_idx;
+ case RS_MIMO2:
+ return lq_sta->max_mimo2_rate_idx;
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ return lq_sta->max_legacy_rate_idx;
+}
+
+static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
+ const struct rs_tx_column *column,
+ u32 bw)
+{
+ /* Used to choose among HT tables */
+ const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+
+ if (WARN_ON_ONCE(column->mode != RS_LEGACY &&
+ column->mode != RS_SISO &&
+ column->mode != RS_MIMO2))
+ return expected_tpt_legacy;
+
+ /* Legacy rates have only one table */
+ if (column->mode == RS_LEGACY)
+ return expected_tpt_legacy;
+
+ ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+ /* Choose among many HT tables depending on number of streams
+ * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation
+ * status */
+ if (column->mode == RS_SISO) {
+ switch (bw) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ ht_tbl_pointer = expected_tpt_siso_20MHz;
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ ht_tbl_pointer = expected_tpt_siso_40MHz;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ ht_tbl_pointer = expected_tpt_siso_80MHz;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+ } else if (column->mode == RS_MIMO2) {
+ switch (bw) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ ht_tbl_pointer = expected_tpt_mimo2_80MHz;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+ } else {
+ WARN_ON_ONCE(1);
+ }
+
+ if (!column->sgi && !lq_sta->is_agg) /* Normal */
+ return ht_tbl_pointer[0];
+ else if (column->sgi && !lq_sta->is_agg) /* SGI */
+ return ht_tbl_pointer[1];
+ else if (!column->sgi && lq_sta->is_agg) /* AGG */
+ return ht_tbl_pointer[2];
+ else /* AGG+SGI */
+ return ht_tbl_pointer[3];
+}
+
+static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl)
+{
+ struct rs_rate *rate = &tbl->rate;
+ const struct rs_tx_column *column = &rs_tx_columns[tbl->column];
+
+ tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
+}
+
+static s32 rs_get_best_rate(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl, /* "search" */
+ unsigned long rate_mask, s8 index)
+{
+ struct iwl_scale_tbl_info *active_tbl =
+ &(lq_sta->lq_info[lq_sta->active_tbl]);
+ s32 success_ratio = active_tbl->win[index].success_ratio;
+ u16 expected_current_tpt = active_tbl->expected_tpt[index];
+ const u16 *tpt_tbl = tbl->expected_tpt;
+ u16 high_low;
+ u32 target_tpt;
+ int rate_idx;
+
+ if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) {
+ target_tpt = 100 * expected_current_tpt;
+ IWL_DEBUG_RATE(mvm,
+ "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
+ success_ratio, target_tpt);
+ } else {
+ target_tpt = lq_sta->last_tpt;
+ IWL_DEBUG_RATE(mvm,
+ "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n",
+ success_ratio, target_tpt);
+ }
+
+ rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG);
+
+ while (rate_idx != IWL_RATE_INVALID) {
+ if (target_tpt < (100 * tpt_tbl[rate_idx]))
+ break;
+
+ high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask,
+ tbl->rate.type);
+
+ rate_idx = (high_low >> 8) & 0xff;
+ }
+
+ IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n",
+ rate_idx, target_tpt,
+ rate_idx != IWL_RATE_INVALID ?
+ 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE);
+
+ return rate_idx;
+}
+
+static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta)
+{
+ if (sta->bandwidth >= IEEE80211_STA_RX_BW_80)
+ return RATE_MCS_CHAN_WIDTH_80;
+ else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40)
+ return RATE_MCS_CHAN_WIDTH_40;
+
+ return RATE_MCS_CHAN_WIDTH_20;
+}
+
+/*
+ * Check whether we should continue using same modulation mode, or
+ * begin search for a new mode, based on:
+ * 1) # tx successes or failures while using this mode
+ * 2) # times calling this function
+ * 3) elapsed time in this mode (not used, for now)
+ */
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
+{
+ struct iwl_scale_tbl_info *tbl;
+ int active_tbl;
+ int flush_interval_passed = 0;
+ struct iwl_mvm *mvm;
+
+ mvm = lq_sta->pers.drv;
+ active_tbl = lq_sta->active_tbl;
+
+ tbl = &(lq_sta->lq_info[active_tbl]);
+
+ /* If we've been disallowing search, see if we should now allow it */
+ if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
+ /* Elapsed time using current modulation mode */
+ if (lq_sta->flush_timer)
+ flush_interval_passed =
+ time_after(jiffies,
+ (unsigned long)(lq_sta->flush_timer +
+ (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ)));
+
+ /*
+ * Check if we should allow search for new modulation mode.
+ * If many frames have failed or succeeded, or we've used
+ * this same modulation for a long time, allow search, and
+ * reset history stats that keep track of whether we should
+ * allow a new search. Also (below) reset all bitmaps and
+ * stats in active history.
+ */
+ if (force_search ||
+ (lq_sta->total_failed > lq_sta->max_failure_limit) ||
+ (lq_sta->total_success > lq_sta->max_success_limit) ||
+ ((!lq_sta->search_better_tbl) &&
+ (lq_sta->flush_timer) && (flush_interval_passed))) {
+ IWL_DEBUG_RATE(mvm,
+ "LQ: stay is expired %d %d %d\n",
+ lq_sta->total_failed,
+ lq_sta->total_success,
+ flush_interval_passed);
+
+ /* Allow search for new mode */
+ lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED;
+ IWL_DEBUG_RATE(mvm,
+ "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n");
+ lq_sta->total_failed = 0;
+ lq_sta->total_success = 0;
+ lq_sta->flush_timer = 0;
+ /* mark the current column as visited */
+ lq_sta->visited_columns = BIT(tbl->column);
+ /*
+ * Else if we've used this modulation mode enough repetitions
+ * (regardless of elapsed time or success/failure), reset
+ * history bitmaps and rate-specific stats for all rates in
+ * active table.
+ */
+ } else {
+ lq_sta->table_count++;
+ if (lq_sta->table_count >=
+ lq_sta->table_count_limit) {
+ lq_sta->table_count = 0;
+
+ IWL_DEBUG_RATE(mvm,
+ "LQ: stay in table clear win\n");
+ rs_rate_scale_clear_tbl_windows(mvm, tbl);
+ }
+ }
+
+ /* If transitioning to allow "search", reset all history
+ * bitmaps and stats in active table (this will become the new
+ * "search" table). */
+ if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) {
+ rs_rate_scale_clear_tbl_windows(mvm, tbl);
+ }
+ }
+}
+
+/*
+ * setup rate table in uCode
+ */
+static void rs_update_rate_tbl(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl)
+{
+ rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
+ iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
+}
+
+static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl,
+ enum rs_action scale_action)
+{
+ if (sta->bandwidth != IEEE80211_STA_RX_BW_80)
+ return false;
+
+ if (!is_vht_siso(&tbl->rate))
+ return false;
+
+ if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) &&
+ (tbl->rate.index == IWL_RATE_MCS_0_INDEX) &&
+ (scale_action == RS_ACTION_DOWNSCALE)) {
+ tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20;
+ tbl->rate.index = IWL_RATE_MCS_4_INDEX;
+ IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n");
+ goto tweaked;
+ }
+
+ /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is
+ * sustainable, i.e. we're past the test window. We can't go back
+ * if MCS5 is just tested as this will happen always after switching
+ * to 20Mhz MCS4 because the rate stats are cleared.
+ */
+ if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) &&
+ (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) &&
+ (scale_action == RS_ACTION_STAY)) ||
+ ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) &&
+ (scale_action == RS_ACTION_UPSCALE)))) {
+ tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80;
+ tbl->rate.index = IWL_RATE_MCS_1_INDEX;
+ IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n");
+ goto tweaked;
+ }
+
+ return false;
+
+tweaked:
+ rs_set_expected_tpt_table(lq_sta, tbl);
+ rs_rate_scale_clear_tbl_windows(mvm, tbl);
+ return true;
+}
+
+static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_sta *sta,
+ struct iwl_scale_tbl_info *tbl)
+{
+ int i, j, max_rate;
+ enum rs_column next_col_id;
+ const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column];
+ const struct rs_tx_column *next_col;
+ allow_column_func_t allow_func;
+ u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm);
+ const u16 *expected_tpt_tbl;
+ u16 tpt, max_expected_tpt;
+
+ for (i = 0; i < MAX_NEXT_COLUMNS; i++) {
+ next_col_id = curr_col->next_columns[i];
+
+ if (next_col_id == RS_COLUMN_INVALID)
+ continue;
+
+ if (lq_sta->visited_columns & BIT(next_col_id)) {
+ IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n",
+ next_col_id);
+ continue;
+ }
+
+ next_col = &rs_tx_columns[next_col_id];
+
+ if (!rs_is_valid_ant(valid_ants, next_col->ant)) {
+ IWL_DEBUG_RATE(mvm,
+ "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n",
+ next_col_id, valid_ants, next_col->ant);
+ continue;
+ }
+
+ for (j = 0; j < MAX_COLUMN_CHECKS; j++) {
+ allow_func = next_col->checks[j];
+ if (allow_func && !allow_func(mvm, sta, &tbl->rate,
+ next_col))
+ break;
+ }
+
+ if (j != MAX_COLUMN_CHECKS) {
+ IWL_DEBUG_RATE(mvm,
+ "Skip column %d: not allowed (check %d failed)\n",
+ next_col_id, j);
+
+ continue;
+ }
+
+ tpt = lq_sta->last_tpt / 100;
+ expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col,
+ rs_bw_from_sta_bw(sta));
+ if (WARN_ON_ONCE(!expected_tpt_tbl))
+ continue;
+
+ max_rate = rs_get_max_allowed_rate(lq_sta, next_col);
+ if (max_rate == IWL_RATE_INVALID) {
+ IWL_DEBUG_RATE(mvm,
+ "Skip column %d: no rate is allowed in this column\n",
+ next_col_id);
+ continue;
+ }
+
+ max_expected_tpt = expected_tpt_tbl[max_rate];
+ if (tpt >= max_expected_tpt) {
+ IWL_DEBUG_RATE(mvm,
+ "Skip column %d: can't beat current TPT. Max expected %d current %d\n",
+ next_col_id, max_expected_tpt, tpt);
+ continue;
+ }
+
+ IWL_DEBUG_RATE(mvm,
+ "Found potential column %d. Max expected %d current %d\n",
+ next_col_id, max_expected_tpt, tpt);
+ break;
+ }
+
+ if (i == MAX_NEXT_COLUMNS)
+ return RS_COLUMN_INVALID;
+
+ return next_col_id;
+}
+
+static int rs_switch_to_column(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_sta *sta,
+ enum rs_column col_id)
+{
+ struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ struct iwl_scale_tbl_info *search_tbl =
+ &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+ struct rs_rate *rate = &search_tbl->rate;
+ const struct rs_tx_column *column = &rs_tx_columns[col_id];
+ const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
+ u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+ (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+ unsigned long rate_mask = 0;
+ u32 rate_idx = 0;
+
+ memcpy(search_tbl, tbl, sz);
+
+ rate->sgi = column->sgi;
+ rate->ant = column->ant;
+
+ if (column->mode == RS_LEGACY) {
+ if (lq_sta->band == IEEE80211_BAND_5GHZ)
+ rate->type = LQ_LEGACY_A;
+ else
+ rate->type = LQ_LEGACY_G;
+
+ rate->bw = RATE_MCS_CHAN_WIDTH_20;
+ rate->ldpc = false;
+ rate_mask = lq_sta->active_legacy_rate;
+ } else if (column->mode == RS_SISO) {
+ rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO;
+ rate_mask = lq_sta->active_siso_rate;
+ } else if (column->mode == RS_MIMO2) {
+ rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2;
+ rate_mask = lq_sta->active_mimo2_rate;
+ } else {
+ WARN_ONCE(1, "Bad column mode");
+ }
+
+ if (column->mode != RS_LEGACY) {
+ rate->bw = rs_bw_from_sta_bw(sta);
+ rate->ldpc = lq_sta->ldpc;
+ }
+
+ search_tbl->column = col_id;
+ rs_set_expected_tpt_table(lq_sta, search_tbl);
+
+ lq_sta->visited_columns |= BIT(col_id);
+
+ /* Get the best matching rate if we're changing modes. e.g.
+ * SISO->MIMO, LEGACY->SISO, MIMO->SISO
+ */
+ if (curr_column->mode != column->mode) {
+ rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl,
+ rate_mask, rate->index);
+
+ if ((rate_idx == IWL_RATE_INVALID) ||
+ !(BIT(rate_idx) & rate_mask)) {
+ IWL_DEBUG_RATE(mvm,
+ "can not switch with index %d"
+ " rate mask %lx\n",
+ rate_idx, rate_mask);
+
+ goto err;
+ }
+
+ rate->index = rate_idx;
+ }
+
+ IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n",
+ col_id, rate->index);
+
+ return 0;
+
+err:
+ rate->type = LQ_NONE;
+ return -1;
+}
+
+static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
+ struct iwl_scale_tbl_info *tbl,
+ s32 sr, int low, int high,
+ int current_tpt,
+ int low_tpt, int high_tpt)
+{
+ enum rs_action action = RS_ACTION_STAY;
+
+ if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) ||
+ (current_tpt == 0)) {
+ IWL_DEBUG_RATE(mvm,
+ "Decrease rate because of low SR\n");
+ return RS_ACTION_DOWNSCALE;
+ }
+
+ if ((low_tpt == IWL_INVALID_VALUE) &&
+ (high_tpt == IWL_INVALID_VALUE) &&
+ (high != IWL_RATE_INVALID)) {
+ IWL_DEBUG_RATE(mvm,
+ "No data about high/low rates. Increase rate\n");
+ return RS_ACTION_UPSCALE;
+ }
+
+ if ((high_tpt == IWL_INVALID_VALUE) &&
+ (high != IWL_RATE_INVALID) &&
+ (low_tpt != IWL_INVALID_VALUE) &&
+ (low_tpt < current_tpt)) {
+ IWL_DEBUG_RATE(mvm,
+ "No data about high rate and low rate is worse. Increase rate\n");
+ return RS_ACTION_UPSCALE;
+ }
+
+ if ((high_tpt != IWL_INVALID_VALUE) &&
+ (high_tpt > current_tpt)) {
+ IWL_DEBUG_RATE(mvm,
+ "Higher rate is better. Increate rate\n");
+ return RS_ACTION_UPSCALE;
+ }
+
+ if ((low_tpt != IWL_INVALID_VALUE) &&
+ (high_tpt != IWL_INVALID_VALUE) &&
+ (low_tpt < current_tpt) &&
+ (high_tpt < current_tpt)) {
+ IWL_DEBUG_RATE(mvm,
+ "Both high and low are worse. Maintain rate\n");
+ return RS_ACTION_STAY;
+ }
+
+ if ((low_tpt != IWL_INVALID_VALUE) &&
+ (low_tpt > current_tpt)) {
+ IWL_DEBUG_RATE(mvm,
+ "Lower rate is better\n");
+ action = RS_ACTION_DOWNSCALE;
+ goto out;
+ }
+
+ if ((low_tpt == IWL_INVALID_VALUE) &&
+ (low != IWL_RATE_INVALID)) {
+ IWL_DEBUG_RATE(mvm,
+ "No data about lower rate\n");
+ action = RS_ACTION_DOWNSCALE;
+ goto out;
+ }
+
+ IWL_DEBUG_RATE(mvm, "Maintain rate\n");
+
+out:
+ if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) {
+ if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) {
+ IWL_DEBUG_RATE(mvm,
+ "SR is above NO DECREASE. Avoid downscale\n");
+ action = RS_ACTION_STAY;
+ } else if (current_tpt > (100 * tbl->expected_tpt[low])) {
+ IWL_DEBUG_RATE(mvm,
+ "Current TPT is higher than max expected in low rate. Avoid downscale\n");
+ action = RS_ACTION_STAY;
+ } else {
+ IWL_DEBUG_RATE(mvm, "Decrease rate\n");
+ }
+ }
+
+ return action;
+}
+
+static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta)
+{
+ /* Our chip supports Tx STBC and the peer is an HT/VHT STA which
+ * supports STBC of at least 1*SS
+ */
+ if (!lq_sta->stbc_capable)
+ return false;
+
+ if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
+ return false;
+
+ return true;
+}
+
+static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
+ int *weaker, int *stronger)
+{
+ *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP;
+ if (*weaker > TPC_MAX_REDUCTION)
+ *weaker = TPC_INVALID;
+
+ *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP;
+ if (*stronger < 0)
+ *stronger = TPC_INVALID;
+}
+
+static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct rs_rate *rate, enum ieee80211_band band)
+{
+ int index = rate->index;
+ bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
+ bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
+ !vif->bss_conf.ps);
+
+ IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n",
+ cam, sta_ps_disabled);
+ /*
+ * allow tpc only if power management is enabled, or bt coex
+ * activity grade allows it and we are on 2.4Ghz.
+ */
+ if ((cam || sta_ps_disabled) &&
+ !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
+ return false;
+
+ IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type);
+ if (is_legacy(rate))
+ return index == IWL_RATE_54M_INDEX;
+ if (is_ht(rate))
+ return index == IWL_RATE_MCS_7_INDEX;
+ if (is_vht(rate))
+ return index == IWL_RATE_MCS_7_INDEX ||
+ index == IWL_RATE_MCS_8_INDEX ||
+ index == IWL_RATE_MCS_9_INDEX;
+
+ WARN_ON_ONCE(1);
+ return false;
+}
+
+enum tpc_action {
+ TPC_ACTION_STAY,
+ TPC_ACTION_DECREASE,
+ TPC_ACTION_INCREASE,
+ TPC_ACTION_NO_RESTIRCTION,
+};
+
+static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm,
+ s32 sr, int weak, int strong,
+ int current_tpt,
+ int weak_tpt, int strong_tpt)
+{
+ /* stay until we have valid tpt */
+ if (current_tpt == IWL_INVALID_VALUE) {
+ IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n");
+ return TPC_ACTION_STAY;
+ }
+
+ /* Too many failures, increase txp */
+ if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) ||
+ current_tpt == 0) {
+ IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n");
+ return TPC_ACTION_NO_RESTIRCTION;
+ }
+
+ /* try decreasing first if applicable */
+ if (weak != TPC_INVALID) {
+ if (weak_tpt == IWL_INVALID_VALUE &&
+ (strong_tpt == IWL_INVALID_VALUE ||
+ current_tpt >= strong_tpt)) {
+ IWL_DEBUG_RATE(mvm,
+ "no weak txp measurement. decrease txp\n");
+ return TPC_ACTION_DECREASE;
+ }
+
+ if (weak_tpt > current_tpt) {
+ IWL_DEBUG_RATE(mvm,
+ "lower txp has better tpt. decrease txp\n");
+ return TPC_ACTION_DECREASE;
+ }
+ }
+
+ /* next, increase if needed */
+ if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) &&
+ strong != TPC_INVALID) {
+ if (weak_tpt == IWL_INVALID_VALUE &&
+ strong_tpt != IWL_INVALID_VALUE &&
+ current_tpt < strong_tpt) {
+ IWL_DEBUG_RATE(mvm,
+ "higher txp has better tpt. increase txp\n");
+ return TPC_ACTION_INCREASE;
+ }
+
+ if (weak_tpt < current_tpt &&
+ (strong_tpt == IWL_INVALID_VALUE ||
+ strong_tpt > current_tpt)) {
+ IWL_DEBUG_RATE(mvm,
+ "lower txp has worse tpt. increase txp\n");
+ return TPC_ACTION_INCREASE;
+ }
+ }
+
+ IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n");
+ return TPC_ACTION_STAY;
+}
+
+static bool rs_tpc_perform(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ struct iwl_scale_tbl_info *tbl)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ struct ieee80211_vif *vif = mvm_sta->vif;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_band band;
+ struct iwl_rate_scale_data *window;
+ struct rs_rate *rate = &tbl->rate;
+ enum tpc_action action;
+ s32 sr;
+ u8 cur = lq_sta->lq.reduced_tpc;
+ int current_tpt;
+ int weak, strong;
+ int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
+ IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n",
+ lq_sta->pers.dbg_fixed_txp_reduction);
+ lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction;
+ return cur != lq_sta->pers.dbg_fixed_txp_reduction;
+ }
+#endif
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ if (WARN_ON(!chanctx_conf))
+ band = IEEE80211_NUM_BANDS;
+ else
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
+ if (!rs_tpc_allowed(mvm, vif, rate, band)) {
+ IWL_DEBUG_RATE(mvm,
+ "tpc is not allowed. remove txp restrictions\n");
+ lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
+ return cur != TPC_NO_REDUCTION;
+ }
+
+ rs_get_adjacent_txp(mvm, cur, &weak, &strong);
+
+ /* Collect measured throughputs for current and adjacent rates */
+ window = tbl->tpc_win;
+ sr = window[cur].success_ratio;
+ current_tpt = window[cur].average_tpt;
+ if (weak != TPC_INVALID)
+ weak_tpt = window[weak].average_tpt;
+ if (strong != TPC_INVALID)
+ strong_tpt = window[strong].average_tpt;
+
+ IWL_DEBUG_RATE(mvm,
+ "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n",
+ cur, current_tpt, sr, weak, strong,
+ weak_tpt, strong_tpt);
+
+ action = rs_get_tpc_action(mvm, sr, weak, strong,
+ current_tpt, weak_tpt, strong_tpt);
+
+ /* override actions if we are on the edge */
+ if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) {
+ IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n");
+ action = TPC_ACTION_STAY;
+ } else if (strong == TPC_INVALID &&
+ (action == TPC_ACTION_INCREASE ||
+ action == TPC_ACTION_NO_RESTIRCTION)) {
+ IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n");
+ action = TPC_ACTION_STAY;
+ }
+
+ switch (action) {
+ case TPC_ACTION_DECREASE:
+ lq_sta->lq.reduced_tpc = weak;
+ return true;
+ case TPC_ACTION_INCREASE:
+ lq_sta->lq.reduced_tpc = strong;
+ return true;
+ case TPC_ACTION_NO_RESTIRCTION:
+ lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
+ return true;
+ case TPC_ACTION_STAY:
+ /* do nothing */
+ break;
+ }
+ return false;
+}
+
+/*
+ * Do rate scaling and search for new modulation mode.
+ */
+static void rs_rate_scale_perform(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ int tid)
+{
+ int low = IWL_RATE_INVALID;
+ int high = IWL_RATE_INVALID;
+ int index;
+ struct iwl_rate_scale_data *window = NULL;
+ int current_tpt = IWL_INVALID_VALUE;
+ int low_tpt = IWL_INVALID_VALUE;
+ int high_tpt = IWL_INVALID_VALUE;
+ u32 fail_count;
+ enum rs_action scale_action = RS_ACTION_STAY;
+ u16 rate_mask;
+ u8 update_lq = 0;
+ struct iwl_scale_tbl_info *tbl, *tbl1;
+ u8 active_tbl = 0;
+ u8 done_search = 0;
+ u16 high_low;
+ s32 sr;
+ u8 prev_agg = lq_sta->is_agg;
+ struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_tid_data *tid_data;
+ struct rs_rate *rate;
+
+ lq_sta->is_agg = !!sta_priv->agg_tids;
+
+ /*
+ * Select rate-scale / modulation-mode table to work with in
+ * the rest of this function: "search" if searching for better
+ * modulation mode, or "active" if doing rate scaling within a mode.
+ */
+ if (!lq_sta->search_better_tbl)
+ active_tbl = lq_sta->active_tbl;
+ else
+ active_tbl = 1 - lq_sta->active_tbl;
+
+ tbl = &(lq_sta->lq_info[active_tbl]);
+ rate = &tbl->rate;
+
+ if (prev_agg != lq_sta->is_agg) {
+ IWL_DEBUG_RATE(mvm,
+ "Aggregation changed: prev %d current %d. Update expected TPT table\n",
+ prev_agg, lq_sta->is_agg);
+ rs_set_expected_tpt_table(lq_sta, tbl);
+ rs_rate_scale_clear_tbl_windows(mvm, tbl);
+ }
+
+ /* current tx rate */
+ index = rate->index;
+
+ /* rates available for this association, and for modulation mode */
+ rate_mask = rs_get_supported_rates(lq_sta, rate);
+
+ if (!(BIT(index) & rate_mask)) {
+ IWL_ERR(mvm, "Current Rate is not valid\n");
+ if (lq_sta->search_better_tbl) {
+ /* revert to active table if search table is not valid*/
+ rate->type = LQ_NONE;
+ lq_sta->search_better_tbl = 0;
+ tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
+ }
+ return;
+ }
+
+ /* Get expected throughput table and history window for current rate */
+ if (!tbl->expected_tpt) {
+ IWL_ERR(mvm, "tbl->expected_tpt is NULL\n");
+ return;
+ }
+
+ /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
+ window = &(tbl->win[index]);
+
+ /*
+ * If there is not enough history to calculate actual average
+ * throughput, keep analyzing results of more tx frames, without
+ * changing rate or mode (bypass most of the rest of this function).
+ * Set up new rate table in uCode only if old rate is not supported
+ * in current association (use new rate found above).
+ */
+ fail_count = window->counter - window->success_counter;
+ if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) &&
+ (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) {
+ IWL_DEBUG_RATE(mvm,
+ "%s: Test Window: succ %d total %d\n",
+ rs_pretty_rate(rate),
+ window->success_counter, window->counter);
+
+ /* Can't calculate this yet; not enough history */
+ window->average_tpt = IWL_INVALID_VALUE;
+
+ /* Should we stay with this modulation mode,
+ * or search for a new one? */
+ rs_stay_in_table(lq_sta, false);
+
+ return;
+ }
+
+ /* If we are searching for better modulation mode, check success. */
+ if (lq_sta->search_better_tbl) {
+ /* If good success, continue using the "search" mode;
+ * no need to send new link quality command, since we're
+ * continuing to use the setup that we've been trying. */
+ if (window->average_tpt > lq_sta->last_tpt) {
+ IWL_DEBUG_RATE(mvm,
+ "SWITCHING TO NEW TABLE SR: %d "
+ "cur-tpt %d old-tpt %d\n",
+ window->success_ratio,
+ window->average_tpt,
+ lq_sta->last_tpt);
+
+ /* Swap tables; "search" becomes "active" */
+ lq_sta->active_tbl = active_tbl;
+ current_tpt = window->average_tpt;
+ /* Else poor success; go back to mode in "active" table */
+ } else {
+ IWL_DEBUG_RATE(mvm,
+ "GOING BACK TO THE OLD TABLE: SR %d "
+ "cur-tpt %d old-tpt %d\n",
+ window->success_ratio,
+ window->average_tpt,
+ lq_sta->last_tpt);
+
+ /* Nullify "search" table */
+ rate->type = LQ_NONE;
+
+ /* Revert to "active" table */
+ active_tbl = lq_sta->active_tbl;
+ tbl = &(lq_sta->lq_info[active_tbl]);
+
+ /* Revert to "active" rate and throughput info */
+ index = tbl->rate.index;
+ current_tpt = lq_sta->last_tpt;
+
+ /* Need to set up a new rate table in uCode */
+ update_lq = 1;
+ }
+
+ /* Either way, we've made a decision; modulation mode
+ * search is done, allow rate adjustment next time. */
+ lq_sta->search_better_tbl = 0;
+ done_search = 1; /* Don't switch modes below! */
+ goto lq_update;
+ }
+
+ /* (Else) not in search of better modulation mode, try for better
+ * starting rate, while staying in this mode. */
+ high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type);
+ low = high_low & 0xff;
+ high = (high_low >> 8) & 0xff;
+
+ /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
+
+ sr = window->success_ratio;
+
+ /* Collect measured throughputs for current and adjacent rates */
+ current_tpt = window->average_tpt;
+ if (low != IWL_RATE_INVALID)
+ low_tpt = tbl->win[low].average_tpt;
+ if (high != IWL_RATE_INVALID)
+ high_tpt = tbl->win[high].average_tpt;
+
+ IWL_DEBUG_RATE(mvm,
+ "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n",
+ rs_pretty_rate(rate), current_tpt, sr,
+ low, high, low_tpt, high_tpt);
+
+ scale_action = rs_get_rate_action(mvm, tbl, sr, low, high,
+ current_tpt, low_tpt, high_tpt);
+
+ /* Force a search in case BT doesn't like us being in MIMO */
+ if (is_mimo(rate) &&
+ !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) {
+ IWL_DEBUG_RATE(mvm,
+ "BT Coex forbids MIMO. Search for new config\n");
+ rs_stay_in_table(lq_sta, true);
+ goto lq_update;
+ }
+
+ switch (scale_action) {
+ case RS_ACTION_DOWNSCALE:
+ /* Decrease starting rate, update uCode's rate table */
+ if (low != IWL_RATE_INVALID) {
+ update_lq = 1;
+ index = low;
+ } else {
+ IWL_DEBUG_RATE(mvm,
+ "At the bottom rate. Can't decrease\n");
+ }
+
+ break;
+ case RS_ACTION_UPSCALE:
+ /* Increase starting rate, update uCode's rate table */
+ if (high != IWL_RATE_INVALID) {
+ update_lq = 1;
+ index = high;
+ } else {
+ IWL_DEBUG_RATE(mvm,
+ "At the top rate. Can't increase\n");
+ }
+
+ break;
+ case RS_ACTION_STAY:
+ /* No change */
+ if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN)
+ update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
+ break;
+ default:
+ break;
+ }
+
+lq_update:
+ /* Replace uCode's rate table for the destination station. */
+ if (update_lq) {
+ tbl->rate.index = index;
+ if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK)
+ rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action);
+ rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
+ }
+
+ rs_stay_in_table(lq_sta, false);
+
+ /*
+ * Search for new modulation mode if we're:
+ * 1) Not changing rates right now
+ * 2) Not just finishing up a search
+ * 3) Allowing a new search
+ */
+ if (!update_lq && !done_search &&
+ lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED
+ && window->counter) {
+ enum rs_column next_column;
+
+ /* Save current throughput to compare with "search" throughput*/
+ lq_sta->last_tpt = current_tpt;
+
+ IWL_DEBUG_RATE(mvm,
+ "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n",
+ update_lq, done_search, lq_sta->rs_state,
+ window->counter);
+
+ next_column = rs_get_next_column(mvm, lq_sta, sta, tbl);
+ if (next_column != RS_COLUMN_INVALID) {
+ int ret = rs_switch_to_column(mvm, lq_sta, sta,
+ next_column);
+ if (!ret)
+ lq_sta->search_better_tbl = 1;
+ } else {
+ IWL_DEBUG_RATE(mvm,
+ "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n");
+ lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED;
+ }
+
+ /* If new "search" mode was selected, set up in uCode table */
+ if (lq_sta->search_better_tbl) {
+ /* Access the "search" table, clear its history. */
+ tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+ rs_rate_scale_clear_tbl_windows(mvm, tbl);
+
+ /* Use new "search" start rate */
+ index = tbl->rate.index;
+
+ rs_dump_rate(mvm, &tbl->rate,
+ "Switch to SEARCH TABLE:");
+ rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
+ } else {
+ done_search = 1;
+ }
+ }
+
+ if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) {
+ /* If the "active" (non-search) mode was legacy,
+ * and we've tried switching antennas,
+ * but we haven't been able to try HT modes (not available),
+ * stay with best antenna legacy modulation for a while
+ * before next round of mode comparisons. */
+ tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ if (is_legacy(&tbl1->rate)) {
+ IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n");
+
+ if (tid != IWL_MAX_TID_COUNT) {
+ tid_data = &sta_priv->tid_data[tid];
+ if (tid_data->state != IWL_AGG_OFF) {
+ IWL_DEBUG_RATE(mvm,
+ "Stop aggregation on tid %d\n",
+ tid);
+ ieee80211_stop_tx_ba_session(sta, tid);
+ }
+ }
+ rs_set_stay_in_table(mvm, 1, lq_sta);
+ } else {
+ /* If we're in an HT mode, and all 3 mode switch actions
+ * have been tried and compared, stay in this best modulation
+ * mode for a while before next round of mode comparisons. */
+ if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
+ (lq_sta->tx_agg_tid_en & (1 << tid)) &&
+ (tid != IWL_MAX_TID_COUNT)) {
+ tid_data = &sta_priv->tid_data[tid];
+ if (tid_data->state == IWL_AGG_OFF) {
+ IWL_DEBUG_RATE(mvm,
+ "try to aggregate tid %d\n",
+ tid);
+ rs_tl_turn_on_agg(mvm, tid,
+ lq_sta, sta);
+ }
+ }
+ rs_set_stay_in_table(mvm, 0, lq_sta);
+ }
+ }
+}
+
+struct rs_init_rate_info {
+ s8 rssi;
+ u8 rate_idx;
+};
+
+static const struct rs_init_rate_info rs_optimal_rates_24ghz_legacy[] = {
+ { -60, IWL_RATE_54M_INDEX },
+ { -64, IWL_RATE_48M_INDEX },
+ { -68, IWL_RATE_36M_INDEX },
+ { -80, IWL_RATE_24M_INDEX },
+ { -84, IWL_RATE_18M_INDEX },
+ { -85, IWL_RATE_12M_INDEX },
+ { -86, IWL_RATE_11M_INDEX },
+ { -88, IWL_RATE_5M_INDEX },
+ { -90, IWL_RATE_2M_INDEX },
+ { S8_MIN, IWL_RATE_1M_INDEX },
+};
+
+static const struct rs_init_rate_info rs_optimal_rates_5ghz_legacy[] = {
+ { -60, IWL_RATE_54M_INDEX },
+ { -64, IWL_RATE_48M_INDEX },
+ { -72, IWL_RATE_36M_INDEX },
+ { -80, IWL_RATE_24M_INDEX },
+ { -84, IWL_RATE_18M_INDEX },
+ { -85, IWL_RATE_12M_INDEX },
+ { -87, IWL_RATE_9M_INDEX },
+ { S8_MIN, IWL_RATE_6M_INDEX },
+};
+
+static const struct rs_init_rate_info rs_optimal_rates_ht[] = {
+ { -60, IWL_RATE_MCS_7_INDEX },
+ { -64, IWL_RATE_MCS_6_INDEX },
+ { -68, IWL_RATE_MCS_5_INDEX },
+ { -72, IWL_RATE_MCS_4_INDEX },
+ { -80, IWL_RATE_MCS_3_INDEX },
+ { -84, IWL_RATE_MCS_2_INDEX },
+ { -85, IWL_RATE_MCS_1_INDEX },
+ { S8_MIN, IWL_RATE_MCS_0_INDEX},
+};
+
+static const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = {
+ { -60, IWL_RATE_MCS_8_INDEX },
+ { -64, IWL_RATE_MCS_7_INDEX },
+ { -68, IWL_RATE_MCS_6_INDEX },
+ { -72, IWL_RATE_MCS_5_INDEX },
+ { -80, IWL_RATE_MCS_4_INDEX },
+ { -84, IWL_RATE_MCS_3_INDEX },
+ { -85, IWL_RATE_MCS_2_INDEX },
+ { -87, IWL_RATE_MCS_1_INDEX },
+ { S8_MIN, IWL_RATE_MCS_0_INDEX},
+};
+
+static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = {
+ { -60, IWL_RATE_MCS_9_INDEX },
+ { -64, IWL_RATE_MCS_8_INDEX },
+ { -68, IWL_RATE_MCS_7_INDEX },
+ { -72, IWL_RATE_MCS_6_INDEX },
+ { -80, IWL_RATE_MCS_5_INDEX },
+ { -84, IWL_RATE_MCS_4_INDEX },
+ { -85, IWL_RATE_MCS_3_INDEX },
+ { -87, IWL_RATE_MCS_2_INDEX },
+ { -88, IWL_RATE_MCS_1_INDEX },
+ { S8_MIN, IWL_RATE_MCS_0_INDEX },
+};
+
+#define IWL_RS_LOW_RSSI_THRESHOLD (-76) /* dBm */
+
+/* Init the optimal rate based on STA caps
+ * This combined with rssi is used to report the last tx rate
+ * to userspace when we haven't transmitted enough frames.
+ */
+static void rs_init_optimal_rate(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta)
+{
+ struct rs_rate *rate = &lq_sta->optimal_rate;
+
+ if (lq_sta->max_mimo2_rate_idx != IWL_RATE_INVALID)
+ rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2;
+ else if (lq_sta->max_siso_rate_idx != IWL_RATE_INVALID)
+ rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO;
+ else if (lq_sta->band == IEEE80211_BAND_5GHZ)
+ rate->type = LQ_LEGACY_A;
+ else
+ rate->type = LQ_LEGACY_G;
+
+ rate->bw = rs_bw_from_sta_bw(sta);
+ rate->sgi = rs_sgi_allow(mvm, sta, rate, NULL);
+
+ /* ANT/LDPC/STBC aren't relevant for the rate reported to userspace */
+
+ if (is_mimo(rate)) {
+ lq_sta->optimal_rate_mask = lq_sta->active_mimo2_rate;
+ } else if (is_siso(rate)) {
+ lq_sta->optimal_rate_mask = lq_sta->active_siso_rate;
+ } else {
+ lq_sta->optimal_rate_mask = lq_sta->active_legacy_rate;
+
+ if (lq_sta->band == IEEE80211_BAND_5GHZ) {
+ lq_sta->optimal_rates = rs_optimal_rates_5ghz_legacy;
+ lq_sta->optimal_nentries =
+ ARRAY_SIZE(rs_optimal_rates_5ghz_legacy);
+ } else {
+ lq_sta->optimal_rates = rs_optimal_rates_24ghz_legacy;
+ lq_sta->optimal_nentries =
+ ARRAY_SIZE(rs_optimal_rates_24ghz_legacy);
+ }
+ }
+
+ if (is_vht(rate)) {
+ if (rate->bw == RATE_MCS_CHAN_WIDTH_20) {
+ lq_sta->optimal_rates = rs_optimal_rates_vht_20mhz;
+ lq_sta->optimal_nentries =
+ ARRAY_SIZE(rs_optimal_rates_vht_20mhz);
+ } else {
+ lq_sta->optimal_rates = rs_optimal_rates_vht_40_80mhz;
+ lq_sta->optimal_nentries =
+ ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz);
+ }
+ } else if (is_ht(rate)) {
+ lq_sta->optimal_rates = rs_optimal_rates_ht;
+ lq_sta->optimal_nentries = ARRAY_SIZE(rs_optimal_rates_ht);
+ }
+}
+
+/* Compute the optimal rate index based on RSSI */
+static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta)
+{
+ struct rs_rate *rate = &lq_sta->optimal_rate;
+ int i;
+
+ rate->index = find_first_bit(&lq_sta->optimal_rate_mask,
+ BITS_PER_LONG);
+
+ for (i = 0; i < lq_sta->optimal_nentries; i++) {
+ int rate_idx = lq_sta->optimal_rates[i].rate_idx;
+
+ if ((lq_sta->pers.last_rssi >= lq_sta->optimal_rates[i].rssi) &&
+ (BIT(rate_idx) & lq_sta->optimal_rate_mask)) {
+ rate->index = rate_idx;
+ break;
+ }
+ }
+
+ return rate;
+}
+
+/* Choose an initial legacy rate and antenna to use based on the RSSI
+ * of last Rx
+ */
+static void rs_get_initial_rate(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ enum ieee80211_band band,
+ struct rs_rate *rate)
+{
+ int i, nentries;
+ unsigned long active_rate;
+ s8 best_rssi = S8_MIN;
+ u8 best_ant = ANT_NONE;
+ u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
+ const struct rs_init_rate_info *initial_rates;
+
+ for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) {
+ if (!(lq_sta->pers.chains & BIT(i)))
+ continue;
+
+ if (lq_sta->pers.chain_signal[i] > best_rssi) {
+ best_rssi = lq_sta->pers.chain_signal[i];
+ best_ant = BIT(i);
+ }
+ }
+
+ IWL_DEBUG_RATE(mvm, "Best ANT: %s Best RSSI: %d\n",
+ rs_pretty_ant(best_ant), best_rssi);
+
+ if (best_ant != ANT_A && best_ant != ANT_B)
+ rate->ant = first_antenna(valid_tx_ant);
+ else
+ rate->ant = best_ant;
+
+ rate->sgi = false;
+ rate->ldpc = false;
+ rate->bw = RATE_MCS_CHAN_WIDTH_20;
+
+ rate->index = find_first_bit(&lq_sta->active_legacy_rate,
+ BITS_PER_LONG);
+
+ if (band == IEEE80211_BAND_5GHZ) {
+ rate->type = LQ_LEGACY_A;
+ initial_rates = rs_optimal_rates_5ghz_legacy;
+ nentries = ARRAY_SIZE(rs_optimal_rates_5ghz_legacy);
+ } else {
+ rate->type = LQ_LEGACY_G;
+ initial_rates = rs_optimal_rates_24ghz_legacy;
+ nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy);
+ }
+
+ if (!IWL_MVM_RS_RSSI_BASED_INIT_RATE)
+ goto out;
+
+ /* Start from a higher rate if the corresponding debug capability
+ * is enabled. The rate is chosen according to AP capabilities.
+ * In case of VHT/HT when the rssi is low fallback to the case of
+ * legacy rates.
+ */
+ if (sta->vht_cap.vht_supported &&
+ best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) {
+ if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
+ initial_rates = rs_optimal_rates_vht_40_80mhz;
+ nentries = ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz);
+ if (sta->bandwidth >= IEEE80211_STA_RX_BW_80)
+ rate->bw = RATE_MCS_CHAN_WIDTH_80;
+ else
+ rate->bw = RATE_MCS_CHAN_WIDTH_40;
+ } else if (sta->bandwidth == IEEE80211_STA_RX_BW_20) {
+ initial_rates = rs_optimal_rates_vht_20mhz;
+ nentries = ARRAY_SIZE(rs_optimal_rates_vht_20mhz);
+ rate->bw = RATE_MCS_CHAN_WIDTH_20;
+ } else {
+ IWL_ERR(mvm, "Invalid BW %d\n", sta->bandwidth);
+ goto out;
+ }
+ active_rate = lq_sta->active_siso_rate;
+ rate->type = LQ_VHT_SISO;
+ } else if (sta->ht_cap.ht_supported &&
+ best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) {
+ initial_rates = rs_optimal_rates_ht;
+ nentries = ARRAY_SIZE(rs_optimal_rates_ht);
+ active_rate = lq_sta->active_siso_rate;
+ rate->type = LQ_HT_SISO;
+ } else {
+ active_rate = lq_sta->active_legacy_rate;
+ }
+
+ for (i = 0; i < nentries; i++) {
+ int rate_idx = initial_rates[i].rate_idx;
+
+ if ((best_rssi >= initial_rates[i].rssi) &&
+ (BIT(rate_idx) & active_rate)) {
+ rate->index = rate_idx;
+ break;
+ }
+ }
+
+out:
+ rs_dump_rate(mvm, rate, "INITIAL");
+}
+
+/* Save info about RSSI of last Rx */
+void rs_update_last_rssi(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_rx_status *rx_status)
+{
+ int i;
+
+ lq_sta->pers.chains = rx_status->chains;
+ lq_sta->pers.chain_signal[0] = rx_status->chain_signal[0];
+ lq_sta->pers.chain_signal[1] = rx_status->chain_signal[1];
+ lq_sta->pers.chain_signal[2] = rx_status->chain_signal[2];
+ lq_sta->pers.last_rssi = S8_MIN;
+
+ for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) {
+ if (!(lq_sta->pers.chains & BIT(i)))
+ continue;
+
+ if (lq_sta->pers.chain_signal[i] > lq_sta->pers.last_rssi)
+ lq_sta->pers.last_rssi = lq_sta->pers.chain_signal[i];
+ }
+}
+
+/**
+ * rs_initialize_lq - Initialize a station's hardware rate table
+ *
+ * The uCode's station table contains a table of fallback rates
+ * for automatic fallback during transmission.
+ *
+ * NOTE: This sets up a default set of values. These will be replaced later
+ * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
+ * rc80211_simple.
+ *
+ * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
+ * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
+ * which requires station table entry to exist).
+ */
+static void rs_initialize_lq(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ enum ieee80211_band band,
+ bool init)
+{
+ struct iwl_scale_tbl_info *tbl;
+ struct rs_rate *rate;
+ u8 active_tbl = 0;
+
+ if (!sta || !lq_sta)
+ return;
+
+ if (!lq_sta->search_better_tbl)
+ active_tbl = lq_sta->active_tbl;
+ else
+ active_tbl = 1 - lq_sta->active_tbl;
+
+ tbl = &(lq_sta->lq_info[active_tbl]);
+ rate = &tbl->rate;
+
+ rs_get_initial_rate(mvm, sta, lq_sta, band, rate);
+ rs_init_optimal_rate(mvm, sta, lq_sta);
+
+ WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B);
+ tbl->column = rs_get_column_from_rate(rate);
+
+ rs_set_expected_tpt_table(lq_sta, tbl);
+ rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
+ /* TODO restore station should remember the lq cmd */
+ iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init);
+}
+
+static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
+ struct ieee80211_tx_rate_control *txrc)
+{
+ struct sk_buff *skb = txrc->skb;
+ struct iwl_op_mode *op_mode __maybe_unused =
+ (struct iwl_op_mode *)mvm_r;
+ struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_lq_sta *lq_sta = mvm_sta;
+ struct rs_rate *optimal_rate;
+ u32 last_ucode_rate;
+
+ if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) {
+ /* if vif isn't initialized mvm doesn't know about
+ * this station, so don't do anything with the it
+ */
+ sta = NULL;
+ mvm_sta = NULL;
+ }
+
+ /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
+
+ /* Treat uninitialized rate scaling data same as non-existing. */
+ if (lq_sta && !lq_sta->pers.drv) {
+ IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
+ mvm_sta = NULL;
+ }
+
+ /* Send management frames and NO_ACK data using lowest rate. */
+ if (rate_control_send_low(sta, mvm_sta, txrc))
+ return;
+
+ iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags,
+ info->band, &info->control.rates[0]);
+ info->control.rates[0].count = 1;
+
+ /* Report the optimal rate based on rssi and STA caps if we haven't
+ * converged yet (too little traffic) or exploring other modulations
+ */
+ if (lq_sta->rs_state != RS_STATE_STAY_IN_COLUMN) {
+ optimal_rate = rs_get_optimal_rate(mvm, lq_sta);
+ last_ucode_rate = ucode_rate_from_rs_rate(mvm,
+ optimal_rate);
+ iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band,
+ &txrc->reported_rate);
+ }
+}
+
+static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
+ gfp_t gfp)
+{
+ struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
+
+ IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
+
+ lq_sta->pers.drv = mvm;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ lq_sta->pers.dbg_fixed_rate = 0;
+ lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID;
+ lq_sta->pers.ss_force = RS_SS_FORCE_NONE;
+#endif
+ lq_sta->pers.chains = 0;
+ memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal));
+ lq_sta->pers.last_rssi = S8_MIN;
+
+ return &sta_priv->lq_sta;
+}
+
+static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
+ int nss)
+{
+ u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
+ (0x3 << (2 * (nss - 1)));
+ rx_mcs >>= (2 * (nss - 1));
+
+ if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7)
+ return IWL_RATE_MCS_7_INDEX;
+ else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8)
+ return IWL_RATE_MCS_8_INDEX;
+ else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9)
+ return IWL_RATE_MCS_9_INDEX;
+
+ WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED);
+ return -1;
+}
+
+static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
+ struct ieee80211_sta_vht_cap *vht_cap,
+ struct iwl_lq_sta *lq_sta)
+{
+ int i;
+ int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1);
+
+ if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
+ for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
+ if (i == IWL_RATE_9M_INDEX)
+ continue;
+
+ /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
+ if (i == IWL_RATE_MCS_9_INDEX &&
+ sta->bandwidth == IEEE80211_STA_RX_BW_20)
+ continue;
+
+ lq_sta->active_siso_rate |= BIT(i);
+ }
+ }
+
+ if (sta->rx_nss < 2)
+ return;
+
+ highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2);
+ if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
+ for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
+ if (i == IWL_RATE_9M_INDEX)
+ continue;
+
+ /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
+ if (i == IWL_RATE_MCS_9_INDEX &&
+ sta->bandwidth == IEEE80211_STA_RX_BW_20)
+ continue;
+
+ lq_sta->active_mimo2_rate |= BIT(i);
+ }
+ }
+}
+
+static void rs_ht_init(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_sta_ht_cap *ht_cap)
+{
+ /* active_siso_rate mask includes 9 MBits (bit 5),
+ * and CCK (bits 0-3), supp_rates[] does not;
+ * shift to convert format, force 9 MBits off.
+ */
+ lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
+ lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
+ lq_sta->active_siso_rate &= ~((u16)0x2);
+ lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+
+ lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
+ lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
+ lq_sta->active_mimo2_rate &= ~((u16)0x2);
+ lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+
+ if (mvm->cfg->ht_params->ldpc &&
+ (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING))
+ lq_sta->ldpc = true;
+
+ if (mvm->cfg->ht_params->stbc &&
+ (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
+ (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC))
+ lq_sta->stbc_capable = true;
+
+ lq_sta->is_vht = false;
+}
+
+static void rs_vht_init(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ struct ieee80211_sta_vht_cap *vht_cap)
+{
+ rs_vht_set_enabled_rates(sta, vht_cap, lq_sta);
+
+ if (mvm->cfg->ht_params->ldpc &&
+ (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))
+ lq_sta->ldpc = true;
+
+ if (mvm->cfg->ht_params->stbc &&
+ (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
+ (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))
+ lq_sta->stbc_capable = true;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
+ (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
+ (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ lq_sta->bfer_capable = true;
+
+ lq_sta->is_vht = true;
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm)
+{
+ spin_lock_bh(&mvm->drv_stats_lock);
+ memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats));
+ spin_unlock_bh(&mvm->drv_stats_lock);
+}
+
+void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)
+{
+ u8 nss = 0, mcs = 0;
+
+ spin_lock(&mvm->drv_stats_lock);
+
+ if (agg)
+ mvm->drv_rx_stats.agg_frames++;
+
+ mvm->drv_rx_stats.success_frames++;
+
+ switch (rate & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ mvm->drv_rx_stats.bw_20_frames++;
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ mvm->drv_rx_stats.bw_40_frames++;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ mvm->drv_rx_stats.bw_80_frames++;
+ break;
+ default:
+ WARN_ONCE(1, "bad BW. rate 0x%x", rate);
+ }
+
+ if (rate & RATE_MCS_HT_MSK) {
+ mvm->drv_rx_stats.ht_frames++;
+ mcs = rate & RATE_HT_MCS_RATE_CODE_MSK;
+ nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1;
+ } else if (rate & RATE_MCS_VHT_MSK) {
+ mvm->drv_rx_stats.vht_frames++;
+ mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
+ nss = ((rate & RATE_VHT_MCS_NSS_MSK) >>
+ RATE_VHT_MCS_NSS_POS) + 1;
+ } else {
+ mvm->drv_rx_stats.legacy_frames++;
+ }
+
+ if (nss == 1)
+ mvm->drv_rx_stats.siso_frames++;
+ else if (nss == 2)
+ mvm->drv_rx_stats.mimo2_frames++;
+
+ if (rate & RATE_MCS_SGI_MSK)
+ mvm->drv_rx_stats.sgi_frames++;
+ else
+ mvm->drv_rx_stats.ngi_frames++;
+
+ mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate;
+ mvm->drv_rx_stats.last_frame_idx =
+ (mvm->drv_rx_stats.last_frame_idx + 1) %
+ ARRAY_SIZE(mvm->drv_rx_stats.last_rates);
+
+ spin_unlock(&mvm->drv_stats_lock);
+}
+#endif
+
+/*
+ * Called after adding a new station to initialize rate scaling
+ */
+void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ enum ieee80211_band band, bool init)
+{
+ int i, j;
+ struct ieee80211_hw *hw = mvm->hw;
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+ struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
+ struct ieee80211_supported_band *sband;
+ unsigned long supp; /* must be unsigned long for for_each_set_bit */
+
+ /* clear all non-persistent lq data */
+ memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
+
+ sband = hw->wiphy->bands[band];
+
+ lq_sta->lq.sta_id = sta_priv->sta_id;
+
+ for (j = 0; j < LQ_SIZE; j++)
+ rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]);
+
+ lq_sta->flush_timer = 0;
+ lq_sta->last_tx = jiffies;
+
+ IWL_DEBUG_RATE(mvm,
+ "LQ: *** rate scale station global init for station %d ***\n",
+ sta_priv->sta_id);
+ /* TODO: what is a good starting rate for STA? About middle? Maybe not
+ * the lowest or the highest rate.. Could consider using RSSI from
+ * previous packets? Need to have IEEE 802.1X auth succeed immediately
+ * after assoc.. */
+
+ lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX;
+ lq_sta->band = sband->band;
+ /*
+ * active legacy rates as per supported rates bitmap
+ */
+ supp = sta->supp_rates[sband->band];
+ lq_sta->active_legacy_rate = 0;
+ for_each_set_bit(i, &supp, BITS_PER_LONG)
+ lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
+
+ /* TODO: should probably account for rx_highest for both HT/VHT */
+ if (!vht_cap || !vht_cap->vht_supported)
+ rs_ht_init(mvm, sta, lq_sta, ht_cap);
+ else
+ rs_vht_init(mvm, sta, lq_sta, vht_cap);
+
+ lq_sta->max_legacy_rate_idx =
+ rs_get_max_rate_from_mask(lq_sta->active_legacy_rate);
+ lq_sta->max_siso_rate_idx =
+ rs_get_max_rate_from_mask(lq_sta->active_siso_rate);
+ lq_sta->max_mimo2_rate_idx =
+ rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate);
+
+ IWL_DEBUG_RATE(mvm,
+ "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n",
+ lq_sta->active_legacy_rate,
+ lq_sta->active_siso_rate,
+ lq_sta->active_mimo2_rate,
+ lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable,
+ lq_sta->bfer_capable);
+ IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n",
+ lq_sta->max_legacy_rate_idx,
+ lq_sta->max_siso_rate_idx,
+ lq_sta->max_mimo2_rate_idx);
+
+ /* These values will be overridden later */
+ lq_sta->lq.single_stream_ant_msk =
+ first_antenna(iwl_mvm_get_valid_tx_ant(mvm));
+ lq_sta->lq.dual_stream_ant_msk = ANT_AB;
+
+ /* as default allow aggregation for all tids */
+ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
+ lq_sta->is_agg = 0;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ iwl_mvm_reset_frame_stats(mvm);
+#endif
+ rs_initialize_lq(mvm, sta, lq_sta, band, init);
+}
+
+static void rs_rate_update(void *mvm_r,
+ struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *priv_sta,
+ u32 changed)
+{
+ u8 tid;
+ struct iwl_op_mode *op_mode =
+ (struct iwl_op_mode *)mvm_r;
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+ return;
+
+ /* Stop any ongoing aggregations as rs starts off assuming no agg */
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
+ ieee80211_stop_tx_ba_session(sta, tid);
+
+ iwl_mvm_rs_rate_init(mvm, sta, sband->band, false);
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
+ struct iwl_lq_cmd *lq_cmd,
+ enum ieee80211_band band,
+ u32 ucode_rate)
+{
+ struct rs_rate rate;
+ int i;
+ int num_rates = ARRAY_SIZE(lq_cmd->rs_table);
+ __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate);
+ u8 ant = (ucode_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS;
+
+ for (i = 0; i < num_rates; i++)
+ lq_cmd->rs_table[i] = ucode_rate_le32;
+
+ rs_rate_from_ucode_rate(ucode_rate, band, &rate);
+
+ if (is_mimo(&rate))
+ lq_cmd->mimo_delim = num_rates - 1;
+ else
+ lq_cmd->mimo_delim = 0;
+
+ lq_cmd->reduced_tpc = 0;
+
+ if (num_of_ant(ant) == 1)
+ lq_cmd->single_stream_ant_msk = ant;
+
+ lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+}
+#endif /* CONFIG_MAC80211_DEBUGFS */
+
+static void rs_fill_rates_for_column(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta,
+ struct rs_rate *rate,
+ __le32 *rs_table, int *rs_table_index,
+ int num_rates, int num_retries,
+ u8 valid_tx_ant, bool toggle_ant)
+{
+ int i, j;
+ __le32 ucode_rate;
+ bool bottom_reached = false;
+ int prev_rate_idx = rate->index;
+ int end = LINK_QUAL_MAX_RETRY_NUM;
+ int index = *rs_table_index;
+
+ for (i = 0; i < num_rates && index < end; i++) {
+ for (j = 0; j < num_retries && index < end; j++, index++) {
+ ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm,
+ rate));
+ rs_table[index] = ucode_rate;
+ if (toggle_ant)
+ rs_toggle_antenna(valid_tx_ant, rate);
+ }
+
+ prev_rate_idx = rate->index;
+ bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate);
+ if (bottom_reached && !is_legacy(rate))
+ break;
+ }
+
+ if (!bottom_reached && !is_legacy(rate))
+ rate->index = prev_rate_idx;
+
+ *rs_table_index = index;
+}
+
+/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI
+ * column the rate table should look like this:
+ *
+ * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI
+ * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI
+ * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI
+ * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps
+ * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps
+ * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps
+ * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps
+ * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps
+ * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps
+ * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps
+ */
+static void rs_build_rates_table(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ const struct rs_rate *initial_rate)
+{
+ struct rs_rate rate;
+ int num_rates, num_retries, index = 0;
+ u8 valid_tx_ant = 0;
+ struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+ bool toggle_ant = false;
+
+ memcpy(&rate, initial_rate, sizeof(rate));
+
+ valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
+
+ /* TODO: remove old API when min FW API hits 14 */
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS) &&
+ rs_stbc_allow(mvm, sta, lq_sta))
+ rate.stbc = true;
+
+ if (is_siso(&rate)) {
+ num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES;
+ num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE;
+ } else if (is_mimo(&rate)) {
+ num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES;
+ num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE;
+ } else {
+ num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES;
+ num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES;
+ toggle_ant = true;
+ }
+
+ rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+ num_rates, num_retries, valid_tx_ant,
+ toggle_ant);
+
+ rs_get_lower_rate_down_column(lq_sta, &rate);
+
+ if (is_siso(&rate)) {
+ num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES;
+ num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES;
+ lq_cmd->mimo_delim = index;
+ } else if (is_legacy(&rate)) {
+ num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES;
+ num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES;
+ } else {
+ WARN_ON_ONCE(1);
+ }
+
+ toggle_ant = true;
+
+ rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+ num_rates, num_retries, valid_tx_ant,
+ toggle_ant);
+
+ rs_get_lower_rate_down_column(lq_sta, &rate);
+
+ num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES;
+ num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES;
+
+ rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+ num_rates, num_retries, valid_tx_ant,
+ toggle_ant);
+
+}
+
+struct rs_bfer_active_iter_data {
+ struct ieee80211_sta *exclude_sta;
+ struct iwl_mvm_sta *bfer_mvmsta;
+};
+
+static void rs_bfer_active_iter(void *_data,
+ struct ieee80211_sta *sta)
+{
+ struct rs_bfer_active_iter_data *data = _data;
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq;
+ u32 ss_params = le32_to_cpu(lq_cmd->ss_params);
+
+ if (sta == data->exclude_sta)
+ return;
+
+ /* The current sta has BFER allowed */
+ if (ss_params & LQ_SS_BFER_ALLOWED) {
+ WARN_ON_ONCE(data->bfer_mvmsta != NULL);
+
+ data->bfer_mvmsta = mvmsta;
+ }
+}
+
+static int rs_bfer_priority(struct iwl_mvm_sta *sta)
+{
+ int prio = -1;
+ enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif);
+
+ switch (viftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ prio = 3;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ prio = 2;
+ break;
+ case NL80211_IFTYPE_STATION:
+ prio = 1;
+ break;
+ default:
+ WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id);
+ prio = -1;
+ }
+
+ return prio;
+}
+
+/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */
+static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1,
+ struct iwl_mvm_sta *sta2)
+{
+ int prio1 = rs_bfer_priority(sta1);
+ int prio2 = rs_bfer_priority(sta2);
+
+ if (prio1 > prio2)
+ return 1;
+ if (prio1 < prio2)
+ return -1;
+ return 0;
+}
+
+static void rs_set_lq_ss_params(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ const struct rs_rate *initial_rate)
+{
+ struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct rs_bfer_active_iter_data data = {
+ .exclude_sta = sta,
+ .bfer_mvmsta = NULL,
+ };
+ struct iwl_mvm_sta *bfer_mvmsta = NULL;
+ u32 ss_params = LQ_SS_PARAMS_VALID;
+
+ if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
+ goto out;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /* Check if forcing the decision is configured.
+ * Note that SISO is forced by not allowing STBC or BFER
+ */
+ if (lq_sta->pers.ss_force == RS_SS_FORCE_STBC)
+ ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE);
+ else if (lq_sta->pers.ss_force == RS_SS_FORCE_BFER)
+ ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE);
+
+ if (lq_sta->pers.ss_force != RS_SS_FORCE_NONE) {
+ IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n",
+ lq_sta->pers.ss_force);
+ goto out;
+ }
+#endif
+
+ if (lq_sta->stbc_capable)
+ ss_params |= LQ_SS_STBC_1SS_ALLOWED;
+
+ if (!lq_sta->bfer_capable)
+ goto out;
+
+ ieee80211_iterate_stations_atomic(mvm->hw,
+ rs_bfer_active_iter,
+ &data);
+ bfer_mvmsta = data.bfer_mvmsta;
+
+ /* This code is safe as it doesn't run concurrently for different
+ * stations. This is guaranteed by the fact that calls to
+ * ieee80211_tx_status wouldn't run concurrently for a single HW.
+ */
+ if (!bfer_mvmsta) {
+ IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n");
+
+ ss_params |= LQ_SS_BFER_ALLOWED;
+ goto out;
+ }
+
+ IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n",
+ bfer_mvmsta->sta_id);
+
+ /* Disallow BFER on another STA if active and we're a higher priority */
+ if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) {
+ struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq;
+ u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params);
+
+ bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED;
+ bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params);
+ iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false);
+
+ ss_params |= LQ_SS_BFER_ALLOWED;
+ IWL_DEBUG_RATE(mvm,
+ "Lower priority BFER sta found (%d). Switch BFER\n",
+ bfer_mvmsta->sta_id);
+ }
+out:
+ lq_cmd->ss_params = cpu_to_le32(ss_params);
+}
+
+static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta,
+ const struct rs_rate *initial_rate)
+{
+ struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_vif *mvmvif;
+
+ lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START;
+ lq_cmd->agg_time_limit =
+ cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT);
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ if (lq_sta->pers.dbg_fixed_rate) {
+ rs_build_rates_table_from_fixed(mvm, lq_cmd,
+ lq_sta->band,
+ lq_sta->pers.dbg_fixed_rate);
+ return;
+ }
+#endif
+ if (WARN_ON_ONCE(!sta || !initial_rate))
+ return;
+
+ rs_build_rates_table(mvm, sta, lq_sta, initial_rate);
+
+ if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS))
+ rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate);
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+
+ if (num_of_ant(initial_rate->ant) == 1)
+ lq_cmd->single_stream_ant_msk = initial_rate->ant;
+
+ lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
+
+ /*
+ * In case of low latency, tell the firmware to leave a frame in the
+ * Tx Fifo so that it can start a transaction in the same TxOP. This
+ * basically allows the firmware to send bursts.
+ */
+ if (iwl_mvm_vif_low_latency(mvmvif))
+ lq_cmd->agg_frame_cnt_limit--;
+
+ if (mvmsta->vif->p2p)
+ lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK;
+
+ lq_cmd->agg_time_limit =
+ cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta));
+}
+
+static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+{
+ return hw->priv;
+}
+/* rate scale requires free function to be implemented */
+static void rs_free(void *mvm_rate)
+{
+ return;
+}
+
+static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
+ void *mvm_sta)
+{
+ struct iwl_op_mode *op_mode __maybe_unused = mvm_r;
+ struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+
+ IWL_DEBUG_RATE(mvm, "enter\n");
+ IWL_DEBUG_RATE(mvm, "leave\n");
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+int rs_pretty_print_rate(char *buf, const u32 rate)
+{
+
+ char *type, *bw;
+ u8 mcs = 0, nss = 0;
+ u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS;
+
+ if (!(rate & RATE_MCS_HT_MSK) &&
+ !(rate & RATE_MCS_VHT_MSK)) {
+ int index = iwl_hwrate_to_plcp_idx(rate);
+
+ return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n",
+ rs_pretty_ant(ant),
+ index == IWL_RATE_INVALID ? "BAD" :
+ iwl_rate_mcs[index].mbps);
+ }
+
+ if (rate & RATE_MCS_VHT_MSK) {
+ type = "VHT";
+ mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
+ nss = ((rate & RATE_VHT_MCS_NSS_MSK)
+ >> RATE_VHT_MCS_NSS_POS) + 1;
+ } else if (rate & RATE_MCS_HT_MSK) {
+ type = "HT";
+ mcs = rate & RATE_HT_MCS_INDEX_MSK;
+ } else {
+ type = "Unknown"; /* shouldn't happen */
+ }
+
+ switch (rate & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ bw = "20Mhz";
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ bw = "40Mhz";
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ bw = "80Mhz";
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ bw = "160Mhz";
+ break;
+ default:
+ bw = "BAD BW";
+ }
+
+ return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n",
+ type, rs_pretty_ant(ant), bw, mcs, nss,
+ (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ",
+ (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "",
+ (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
+ (rate & RATE_MCS_BF_MSK) ? "BF " : "",
+ (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : "");
+}
+
+/**
+ * Program the device to use fixed rate for frame transmit
+ * This is for debugging/testing only
+ * once the device start use fixed rate, we need to reload the module
+ * to being back the normal operation.
+ */
+static void rs_program_fix_rate(struct iwl_mvm *mvm,
+ struct iwl_lq_sta *lq_sta)
+{
+ lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */
+ lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
+ lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
+
+ IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
+ lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate);
+
+ if (lq_sta->pers.dbg_fixed_rate) {
+ rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL);
+ iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false);
+ }
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ struct iwl_mvm *mvm;
+ char buf[64];
+ size_t buf_size;
+ u32 parsed_rate;
+
+ mvm = lq_sta->pers.drv;
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (sscanf(buf, "%x", &parsed_rate) == 1)
+ lq_sta->pers.dbg_fixed_rate = parsed_rate;
+ else
+ lq_sta->pers.dbg_fixed_rate = 0;
+
+ rs_program_fix_rate(mvm, lq_sta);
+
+ return count;
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char *buff;
+ int desc = 0;
+ int i = 0;
+ ssize_t ret;
+
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ struct iwl_mvm *mvm;
+ struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ struct rs_rate *rate = &tbl->rate;
+ u32 ss_params;
+ mvm = lq_sta->pers.drv;
+ buff = kmalloc(2048, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
+ desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n",
+ lq_sta->total_failed, lq_sta->total_success,
+ lq_sta->active_legacy_rate);
+ desc += sprintf(buff+desc, "fixed rate 0x%X\n",
+ lq_sta->pers.dbg_fixed_rate);
+ desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
+ (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "",
+ (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "",
+ (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : "");
+ desc += sprintf(buff+desc, "lq type %s\n",
+ (is_legacy(rate)) ? "legacy" :
+ is_vht(rate) ? "VHT" : "HT");
+ if (!is_legacy(rate)) {
+ desc += sprintf(buff + desc, " %s",
+ (is_siso(rate)) ? "SISO" : "MIMO2");
+ desc += sprintf(buff + desc, " %s",
+ (is_ht20(rate)) ? "20MHz" :
+ (is_ht40(rate)) ? "40MHz" :
+ (is_ht80(rate)) ? "80Mhz" : "BAD BW");
+ desc += sprintf(buff + desc, " %s %s %s\n",
+ (rate->sgi) ? "SGI" : "NGI",
+ (rate->ldpc) ? "LDPC" : "BCC",
+ (lq_sta->is_agg) ? "AGG on" : "");
+ }
+ desc += sprintf(buff+desc, "last tx rate=0x%X\n",
+ lq_sta->last_rate_n_flags);
+ desc += sprintf(buff+desc,
+ "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n",
+ lq_sta->lq.flags,
+ lq_sta->lq.mimo_delim,
+ lq_sta->lq.single_stream_ant_msk,
+ lq_sta->lq.dual_stream_ant_msk);
+
+ desc += sprintf(buff+desc,
+ "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n",
+ le16_to_cpu(lq_sta->lq.agg_time_limit),
+ lq_sta->lq.agg_disable_start_th,
+ lq_sta->lq.agg_frame_cnt_limit);
+
+ desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
+ ss_params = le32_to_cpu(lq_sta->lq.ss_params);
+ desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n",
+ (ss_params & LQ_SS_PARAMS_VALID) ?
+ "VALID" : "INVALID",
+ (ss_params & LQ_SS_BFER_ALLOWED) ?
+ ", BFER" : "",
+ (ss_params & LQ_SS_STBC_1SS_ALLOWED) ?
+ ", STBC" : "",
+ (ss_params & LQ_SS_FORCE) ?
+ ", FORCE" : "");
+ desc += sprintf(buff+desc,
+ "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
+ lq_sta->lq.initial_rate_index[0],
+ lq_sta->lq.initial_rate_index[1],
+ lq_sta->lq.initial_rate_index[2],
+ lq_sta->lq.initial_rate_index[3]);
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]);
+
+ desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r);
+ desc += rs_pretty_print_rate(buff+desc, r);
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+ kfree(buff);
+ return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
+ .write = rs_sta_dbgfs_scale_table_write,
+ .read = rs_sta_dbgfs_scale_table_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char *buff;
+ int desc = 0;
+ int i, j;
+ ssize_t ret;
+ struct iwl_scale_tbl_info *tbl;
+ struct rs_rate *rate;
+ struct iwl_lq_sta *lq_sta = file->private_data;
+
+ buff = kmalloc(1024, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ for (i = 0; i < LQ_SIZE; i++) {
+ tbl = &(lq_sta->lq_info[i]);
+ rate = &tbl->rate;
+ desc += sprintf(buff+desc,
+ "%s type=%d SGI=%d BW=%s DUP=0\n"
+ "index=%d\n",
+ lq_sta->active_tbl == i ? "*" : "x",
+ rate->type,
+ rate->sgi,
+ is_ht20(rate) ? "20Mhz" :
+ is_ht40(rate) ? "40Mhz" :
+ is_ht80(rate) ? "80Mhz" : "ERR",
+ rate->index);
+ for (j = 0; j < IWL_RATE_COUNT; j++) {
+ desc += sprintf(buff+desc,
+ "counter=%d success=%d %%=%d\n",
+ tbl->win[j].counter,
+ tbl->win[j].success_counter,
+ tbl->win[j].success_ratio);
+ }
+ }
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+ kfree(buff);
+ return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
+ .read = rs_sta_dbgfs_stats_table_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ static const char * const column_name[] = {
+ [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A",
+ [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B",
+ [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A",
+ [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B",
+ [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI",
+ [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI",
+ [RS_COLUMN_MIMO2] = "MIMO2",
+ [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI",
+ };
+
+ static const char * const rate_name[] = {
+ [IWL_RATE_1M_INDEX] = "1M",
+ [IWL_RATE_2M_INDEX] = "2M",
+ [IWL_RATE_5M_INDEX] = "5.5M",
+ [IWL_RATE_11M_INDEX] = "11M",
+ [IWL_RATE_6M_INDEX] = "6M|MCS0",
+ [IWL_RATE_9M_INDEX] = "9M",
+ [IWL_RATE_12M_INDEX] = "12M|MCS1",
+ [IWL_RATE_18M_INDEX] = "18M|MCS2",
+ [IWL_RATE_24M_INDEX] = "24M|MCS3",
+ [IWL_RATE_36M_INDEX] = "36M|MCS4",
+ [IWL_RATE_48M_INDEX] = "48M|MCS5",
+ [IWL_RATE_54M_INDEX] = "54M|MCS6",
+ [IWL_RATE_MCS_7_INDEX] = "MCS7",
+ [IWL_RATE_MCS_8_INDEX] = "MCS8",
+ [IWL_RATE_MCS_9_INDEX] = "MCS9",
+ };
+
+ char *buff, *pos, *endpos;
+ int col, rate;
+ ssize_t ret;
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ struct rs_rate_stats *stats;
+ static const size_t bufsz = 1024;
+
+ buff = kmalloc(bufsz, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ pos = buff;
+ endpos = pos + bufsz;
+
+ pos += scnprintf(pos, endpos - pos, "COLUMN,");
+ for (rate = 0; rate < IWL_RATE_COUNT; rate++)
+ pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]);
+ pos += scnprintf(pos, endpos - pos, "\n");
+
+ for (col = 0; col < RS_COLUMN_COUNT; col++) {
+ pos += scnprintf(pos, endpos - pos,
+ "%s,", column_name[col]);
+
+ for (rate = 0; rate < IWL_RATE_COUNT; rate++) {
+ stats = &(lq_sta->pers.tx_stats[col][rate]);
+ pos += scnprintf(pos, endpos - pos,
+ "%llu/%llu,",
+ stats->success,
+ stats->total);
+ }
+ pos += scnprintf(pos, endpos - pos, "\n");
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
+ kfree(buff);
+ return ret;
+}
+
+static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ memset(lq_sta->pers.tx_stats, 0, sizeof(lq_sta->pers.tx_stats));
+
+ return count;
+}
+
+static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
+ .read = rs_sta_dbgfs_drv_tx_stats_read,
+ .write = rs_sta_dbgfs_drv_tx_stats_write,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static ssize_t iwl_dbgfs_ss_force_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_lq_sta *lq_sta = file->private_data;
+ char buf[12];
+ int bufsz = sizeof(buf);
+ int pos = 0;
+ static const char * const ss_force_name[] = {
+ [RS_SS_FORCE_NONE] = "none",
+ [RS_SS_FORCE_STBC] = "stbc",
+ [RS_SS_FORCE_BFER] = "bfer",
+ [RS_SS_FORCE_SISO] = "siso",
+ };
+
+ pos += scnprintf(buf+pos, bufsz-pos, "%s\n",
+ ss_force_name[lq_sta->pers.ss_force]);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = lq_sta->pers.drv;
+ int ret = 0;
+
+ if (!strncmp("none", buf, 4)) {
+ lq_sta->pers.ss_force = RS_SS_FORCE_NONE;
+ } else if (!strncmp("siso", buf, 4)) {
+ lq_sta->pers.ss_force = RS_SS_FORCE_SISO;
+ } else if (!strncmp("stbc", buf, 4)) {
+ if (lq_sta->stbc_capable) {
+ lq_sta->pers.ss_force = RS_SS_FORCE_STBC;
+ } else {
+ IWL_ERR(mvm,
+ "can't force STBC. peer doesn't support\n");
+ ret = -EINVAL;
+ }
+ } else if (!strncmp("bfer", buf, 4)) {
+ if (lq_sta->bfer_capable) {
+ lq_sta->pers.ss_force = RS_SS_FORCE_BFER;
+ } else {
+ IWL_ERR(mvm,
+ "can't force BFER. peer doesn't support\n");
+ ret = -EINVAL;
+ }
+ } else {
+ IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n");
+ ret = -EINVAL;
+ }
+ return ret ?: count;
+}
+
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
+ _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta)
+#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \
+ if (!debugfs_create_file(#name, mode, parent, lq_sta, \
+ &iwl_dbgfs_##name##_ops)) \
+ goto err; \
+ } while (0)
+
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32);
+
+static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir)
+{
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ struct iwl_mvm_sta *mvmsta;
+
+ mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
+
+ if (!mvmsta->vif)
+ return;
+
+ debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+ lq_sta, &rs_sta_dbgfs_scale_table_ops);
+ debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+ lq_sta, &rs_sta_dbgfs_stats_table_ops);
+ debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
+ lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
+ debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+ &lq_sta->tx_agg_tid_en);
+ debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
+ &lq_sta->pers.dbg_fixed_txp_reduction);
+
+ MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR);
+ return;
+err:
+ IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n");
+}
+
+static void rs_remove_debugfs(void *mvm, void *mvm_sta)
+{
+}
+#endif
+
+/*
+ * Initialization of rate scaling information is done by driver after
+ * the station is added. Since mac80211 calls this function before a
+ * station is added we ignore it.
+ */
+static void rs_rate_init_stub(void *mvm_r,
+ struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *mvm_sta)
+{
+}
+
+static const struct rate_control_ops rs_mvm_ops = {
+ .name = RS_NAME,
+ .tx_status = rs_mac80211_tx_status,
+ .get_rate = rs_get_rate,
+ .rate_init = rs_rate_init_stub,
+ .alloc = rs_alloc,
+ .free = rs_free,
+ .alloc_sta = rs_alloc_sta,
+ .free_sta = rs_free_sta,
+ .rate_update = rs_rate_update,
+#ifdef CONFIG_MAC80211_DEBUGFS
+ .add_sta_debugfs = rs_add_debugfs,
+ .remove_sta_debugfs = rs_remove_debugfs,
+#endif
+};
+
+int iwl_mvm_rate_control_register(void)
+{
+ return ieee80211_rate_control_register(&rs_mvm_ops);
+}
+
+void iwl_mvm_rate_control_unregister(void)
+{
+ ieee80211_rate_control_unregister(&rs_mvm_ops);
+}
+
+/**
+ * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
+ * Tx protection, according to this request and previous requests,
+ * and send the LQ command.
+ * @mvmsta: The station
+ * @enable: Enable Tx protection?
+ */
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+ bool enable)
+{
+ struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (enable) {
+ if (mvmsta->tx_protection == 0)
+ lq->flags |= LQ_FLAG_USE_RTS_MSK;
+ mvmsta->tx_protection++;
+ } else {
+ mvmsta->tx_protection--;
+ if (mvmsta->tx_protection == 0)
+ lq->flags &= ~LQ_FLAG_USE_RTS_MSK;
+ }
+
+ return iwl_mvm_send_lq_cmd(mvm, lq, false);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
new file mode 100644
index 000000000000..bdb6f2d8d854
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
@@ -0,0 +1,392 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __rs_h__
+#define __rs_h__
+
+#include <net/mac80211.h>
+
+#include "iwl-config.h"
+
+#include "fw-api.h"
+#include "iwl-trans.h"
+
+struct iwl_rs_rate_info {
+ u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
+ u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
+ u8 plcp_ht_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
+ u8 plcp_vht_siso;
+ u8 plcp_vht_mimo2;
+ u8 prev_rs; /* previous rate used in rs algo */
+ u8 next_rs; /* next rate used in rs algo */
+};
+
+#define IWL_RATE_60M_PLCP 3
+
+enum {
+ IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
+ IWL_RATE_INVALID = IWL_RATE_COUNT,
+};
+
+#define LINK_QUAL_MAX_RETRY_NUM 16
+
+enum {
+ IWL_RATE_6M_INDEX_TABLE = 0,
+ IWL_RATE_9M_INDEX_TABLE,
+ IWL_RATE_12M_INDEX_TABLE,
+ IWL_RATE_18M_INDEX_TABLE,
+ IWL_RATE_24M_INDEX_TABLE,
+ IWL_RATE_36M_INDEX_TABLE,
+ IWL_RATE_48M_INDEX_TABLE,
+ IWL_RATE_54M_INDEX_TABLE,
+ IWL_RATE_1M_INDEX_TABLE,
+ IWL_RATE_2M_INDEX_TABLE,
+ IWL_RATE_5M_INDEX_TABLE,
+ IWL_RATE_11M_INDEX_TABLE,
+ IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1,
+};
+
+/* #define vs. enum to keep from defaulting to 'large integer' */
+#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX)
+#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX)
+#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX)
+#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX)
+#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX)
+#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX)
+#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX)
+#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX)
+#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX)
+#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX)
+#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX)
+#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX)
+#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX)
+
+
+/* uCode API values for HT/VHT bit rates */
+enum {
+ IWL_RATE_HT_SISO_MCS_0_PLCP = 0,
+ IWL_RATE_HT_SISO_MCS_1_PLCP = 1,
+ IWL_RATE_HT_SISO_MCS_2_PLCP = 2,
+ IWL_RATE_HT_SISO_MCS_3_PLCP = 3,
+ IWL_RATE_HT_SISO_MCS_4_PLCP = 4,
+ IWL_RATE_HT_SISO_MCS_5_PLCP = 5,
+ IWL_RATE_HT_SISO_MCS_6_PLCP = 6,
+ IWL_RATE_HT_SISO_MCS_7_PLCP = 7,
+ IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8,
+ IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9,
+ IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA,
+ IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB,
+ IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC,
+ IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD,
+ IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE,
+ IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF,
+ IWL_RATE_VHT_SISO_MCS_0_PLCP = 0,
+ IWL_RATE_VHT_SISO_MCS_1_PLCP = 1,
+ IWL_RATE_VHT_SISO_MCS_2_PLCP = 2,
+ IWL_RATE_VHT_SISO_MCS_3_PLCP = 3,
+ IWL_RATE_VHT_SISO_MCS_4_PLCP = 4,
+ IWL_RATE_VHT_SISO_MCS_5_PLCP = 5,
+ IWL_RATE_VHT_SISO_MCS_6_PLCP = 6,
+ IWL_RATE_VHT_SISO_MCS_7_PLCP = 7,
+ IWL_RATE_VHT_SISO_MCS_8_PLCP = 8,
+ IWL_RATE_VHT_SISO_MCS_9_PLCP = 9,
+ IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10,
+ IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11,
+ IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12,
+ IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13,
+ IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14,
+ IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15,
+ IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16,
+ IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17,
+ IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18,
+ IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19,
+ IWL_RATE_HT_SISO_MCS_INV_PLCP,
+ IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+ IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+ IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+ IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+ IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+ IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+ IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+};
+
+#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
+
+#define IWL_INVALID_VALUE -1
+
+#define TPC_MAX_REDUCTION 15
+#define TPC_NO_REDUCTION 0
+#define TPC_INVALID 0xff
+
+#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0)
+
+#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */
+
+/* load per tid defines for A-MPDU activation */
+#define IWL_AGG_TPT_THREHOLD 0
+#define IWL_AGG_ALL_TID 0xff
+
+enum iwl_table_type {
+ LQ_NONE,
+ LQ_LEGACY_G, /* legacy types */
+ LQ_LEGACY_A,
+ LQ_HT_SISO, /* HT types */
+ LQ_HT_MIMO2,
+ LQ_VHT_SISO, /* VHT types */
+ LQ_VHT_MIMO2,
+ LQ_MAX,
+};
+
+struct rs_rate {
+ int index;
+ enum iwl_table_type type;
+ u8 ant;
+ u32 bw;
+ bool sgi;
+ bool ldpc;
+ bool stbc;
+ bool bfer;
+};
+
+
+#define is_type_legacy(type) (((type) == LQ_LEGACY_G) || \
+ ((type) == LQ_LEGACY_A))
+#define is_type_ht_siso(type) ((type) == LQ_HT_SISO)
+#define is_type_ht_mimo2(type) ((type) == LQ_HT_MIMO2)
+#define is_type_vht_siso(type) ((type) == LQ_VHT_SISO)
+#define is_type_vht_mimo2(type) ((type) == LQ_VHT_MIMO2)
+#define is_type_siso(type) (is_type_ht_siso(type) || is_type_vht_siso(type))
+#define is_type_mimo2(type) (is_type_ht_mimo2(type) || is_type_vht_mimo2(type))
+#define is_type_mimo(type) (is_type_mimo2(type))
+#define is_type_ht(type) (is_type_ht_siso(type) || is_type_ht_mimo2(type))
+#define is_type_vht(type) (is_type_vht_siso(type) || is_type_vht_mimo2(type))
+#define is_type_a_band(type) ((type) == LQ_LEGACY_A)
+#define is_type_g_band(type) ((type) == LQ_LEGACY_G)
+
+#define is_legacy(rate) is_type_legacy((rate)->type)
+#define is_ht_siso(rate) is_type_ht_siso((rate)->type)
+#define is_ht_mimo2(rate) is_type_ht_mimo2((rate)->type)
+#define is_vht_siso(rate) is_type_vht_siso((rate)->type)
+#define is_vht_mimo2(rate) is_type_vht_mimo2((rate)->type)
+#define is_siso(rate) is_type_siso((rate)->type)
+#define is_mimo2(rate) is_type_mimo2((rate)->type)
+#define is_mimo(rate) is_type_mimo((rate)->type)
+#define is_ht(rate) is_type_ht((rate)->type)
+#define is_vht(rate) is_type_vht((rate)->type)
+#define is_a_band(rate) is_type_a_band((rate)->type)
+#define is_g_band(rate) is_type_g_band((rate)->type)
+
+#define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20)
+#define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40)
+#define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80)
+
+#define IWL_MAX_MCS_DISPLAY_SIZE 12
+
+struct iwl_rate_mcs_info {
+ char mbps[IWL_MAX_MCS_DISPLAY_SIZE];
+ char mcs[IWL_MAX_MCS_DISPLAY_SIZE];
+};
+
+/**
+ * struct iwl_rate_scale_data -- tx success history for one rate
+ */
+struct iwl_rate_scale_data {
+ u64 data; /* bitmap of successful frames */
+ s32 success_counter; /* number of frames successful */
+ s32 success_ratio; /* per-cent * 128 */
+ s32 counter; /* number of frames attempted */
+ s32 average_tpt; /* success ratio * expected throughput */
+};
+
+/* Possible Tx columns
+ * Tx Column = a combo of legacy/siso/mimo x antenna x SGI
+ */
+enum rs_column {
+ RS_COLUMN_LEGACY_ANT_A = 0,
+ RS_COLUMN_LEGACY_ANT_B,
+ RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_SISO_ANT_B,
+ RS_COLUMN_SISO_ANT_A_SGI,
+ RS_COLUMN_SISO_ANT_B_SGI,
+ RS_COLUMN_MIMO2,
+ RS_COLUMN_MIMO2_SGI,
+
+ RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI,
+ RS_COLUMN_COUNT = RS_COLUMN_LAST + 1,
+ RS_COLUMN_INVALID,
+};
+
+enum rs_ss_force_opt {
+ RS_SS_FORCE_NONE = 0,
+ RS_SS_FORCE_STBC,
+ RS_SS_FORCE_BFER,
+ RS_SS_FORCE_SISO,
+};
+
+/* Packet stats per rate */
+struct rs_rate_stats {
+ u64 success;
+ u64 total;
+};
+
+/**
+ * struct iwl_scale_tbl_info -- tx params and success history for all rates
+ *
+ * There are two of these in struct iwl_lq_sta,
+ * one for "active", and one for "search".
+ */
+struct iwl_scale_tbl_info {
+ struct rs_rate rate;
+ enum rs_column column;
+ const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
+ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
+ /* per txpower-reduction history */
+ struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1];
+};
+
+enum {
+ RS_STATE_SEARCH_CYCLE_STARTED,
+ RS_STATE_SEARCH_CYCLE_ENDED,
+ RS_STATE_STAY_IN_COLUMN,
+};
+
+/**
+ * struct iwl_lq_sta -- driver's rate scaling private structure
+ *
+ * Pointer to this gets passed back and forth between driver and mac80211.
+ */
+struct iwl_lq_sta {
+ u8 active_tbl; /* index of active table, range 0-1 */
+ u8 rs_state; /* RS_STATE_* */
+ u8 search_better_tbl; /* 1: currently trying alternate mode */
+ s32 last_tpt;
+
+ /* The following determine when to search for a new mode */
+ u32 table_count_limit;
+ u32 max_failure_limit; /* # failed frames before new search */
+ u32 max_success_limit; /* # successful frames before new search */
+ u32 table_count;
+ u32 total_failed; /* total failed frames, any/all rates */
+ u32 total_success; /* total successful frames, any/all rates */
+ u64 flush_timer; /* time staying in mode before new search */
+
+ u32 visited_columns; /* Bitmask marking which Tx columns were
+ * explored during a search cycle
+ */
+ u64 last_tx;
+ bool is_vht;
+ bool ldpc; /* LDPC Rx is supported by the STA */
+ bool stbc_capable; /* Tx STBC is supported by chip and Rx by STA */
+ bool bfer_capable; /* Remote supports beamformee and we BFer */
+
+ enum ieee80211_band band;
+
+ /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
+ unsigned long active_legacy_rate;
+ unsigned long active_siso_rate;
+ unsigned long active_mimo2_rate;
+
+ /* Highest rate per Tx mode */
+ u8 max_legacy_rate_idx;
+ u8 max_siso_rate_idx;
+ u8 max_mimo2_rate_idx;
+
+ /* Optimal rate based on RSSI and STA caps.
+ * Used only to reflect link speed to userspace.
+ */
+ struct rs_rate optimal_rate;
+ unsigned long optimal_rate_mask;
+ const struct rs_init_rate_info *optimal_rates;
+ int optimal_nentries;
+
+ u8 missed_rate_counter;
+
+ struct iwl_lq_cmd lq;
+ struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
+ u8 tx_agg_tid_en;
+
+ /* last tx rate_n_flags */
+ u32 last_rate_n_flags;
+ /* packets destined for this STA are aggregated */
+ u8 is_agg;
+
+ /* tx power reduce for this sta */
+ int tpc_reduce;
+
+ /* persistent fields - initialized only once - keep last! */
+ struct lq_sta_pers {
+#ifdef CONFIG_MAC80211_DEBUGFS
+ u32 dbg_fixed_rate;
+ u8 dbg_fixed_txp_reduction;
+
+ /* force STBC/BFER/SISO for testing */
+ enum rs_ss_force_opt ss_force;
+#endif
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
+ s8 last_rssi;
+ struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT];
+ struct iwl_mvm *drv;
+ } pers;
+};
+
+/* Initialize station's rate scaling information after adding station */
+void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ enum ieee80211_band band, bool init);
+
+/* Notify RS about Tx status */
+void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ int tid, struct ieee80211_tx_info *info);
+
+/**
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
+ *
+ * Since the rate control algorithm is hardware specific, there is no need
+ * or reason to place it as a stand alone module. The driver can call
+ * iwl_rate_control_register in order to register the rate control callbacks
+ * with the mac80211 subsystem. This should be performed prior to calling
+ * ieee80211_register_hw
+ *
+ */
+int iwl_mvm_rate_control_register(void);
+
+/**
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
+ *
+ * This should be called after calling ieee80211_unregister_hw, but before
+ * the driver is unloaded.
+ */
+void iwl_mvm_rate_control_unregister(void);
+
+struct iwl_mvm_sta;
+
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+ bool enable);
+
+#endif /* __rs__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
new file mode 100644
index 000000000000..145ec68ce6f9
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -0,0 +1,624 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include "iwl-trans.h"
+#include "mvm.h"
+#include "fw-api.h"
+#include "fw-dbg.h"
+
+/*
+ * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
+ *
+ * Copies the phy information in mvm->last_phy_info, it will be used when the
+ * actual data will come from the fw in the next packet.
+ */
+void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+ memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
+ mvm->ampdu_ref++;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
+ spin_lock(&mvm->drv_stats_lock);
+ mvm->drv_rx_stats.ampdu_count++;
+ spin_unlock(&mvm->drv_stats_lock);
+ }
+#endif
+}
+
+/*
+ * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
+ *
+ * Adds the rxb to a new skb and give it to mac80211
+ */
+static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+ struct napi_struct *napi,
+ struct sk_buff *skb,
+ struct ieee80211_hdr *hdr, u16 len,
+ u32 ampdu_status, u8 crypt_len,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ unsigned int hdrlen, fraglen;
+
+ /* If frame is small enough to fit in skb->head, pull it completely.
+ * If not, only pull ieee80211_hdr (including crypto if present, and
+ * an additional 8 bytes for SNAP/ethertype, see below) so that
+ * splice() or TCP coalesce are more efficient.
+ *
+ * Since, in addition, ieee80211_data_to_8023() always pull in at
+ * least 8 bytes (possibly more for mesh) we can do the same here
+ * to save the cost of doing it later. That still doesn't pull in
+ * the actual IP header since the typical case has a SNAP header.
+ * If the latter changes (there are efforts in the standards group
+ * to do so) we should revisit this and ieee80211_data_to_8023().
+ */
+ hdrlen = (len <= skb_tailroom(skb)) ? len :
+ sizeof(*hdr) + crypt_len + 8;
+
+ memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
+ fraglen = len - hdrlen;
+
+ if (fraglen) {
+ int offset = (void *)hdr + hdrlen -
+ rxb_addr(rxb) + rxb_offset(rxb);
+
+ skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+ fraglen, rxb->truesize);
+ }
+
+ ieee80211_rx_napi(mvm->hw, skb, napi);
+}
+
+/*
+ * iwl_mvm_get_signal_strength - use new rx PHY INFO API
+ * values are reported by the fw as positive values - need to negate
+ * to obtain their dBM. Account for missing antennas by replacing 0
+ * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
+ */
+static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
+ struct iwl_rx_phy_info *phy_info,
+ struct ieee80211_rx_status *rx_status)
+{
+ int energy_a, energy_b, energy_c, max_energy;
+ u32 val;
+
+ val =
+ le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
+ energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
+ IWL_RX_INFO_ENERGY_ANT_A_POS;
+ energy_a = energy_a ? -energy_a : S8_MIN;
+ energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
+ IWL_RX_INFO_ENERGY_ANT_B_POS;
+ energy_b = energy_b ? -energy_b : S8_MIN;
+ energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
+ IWL_RX_INFO_ENERGY_ANT_C_POS;
+ energy_c = energy_c ? -energy_c : S8_MIN;
+ max_energy = max(energy_a, energy_b);
+ max_energy = max(max_energy, energy_c);
+
+ IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
+ energy_a, energy_b, energy_c, max_energy);
+
+ rx_status->signal = max_energy;
+ rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
+ RX_RES_PHY_FLAGS_ANTENNA)
+ >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+ rx_status->chain_signal[0] = energy_a;
+ rx_status->chain_signal[1] = energy_b;
+ rx_status->chain_signal[2] = energy_c;
+}
+
+/*
+ * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
+ * @mvm: the mvm object
+ * @hdr: 80211 header
+ * @stats: status in mac80211's format
+ * @rx_pkt_status: status coming from fw
+ *
+ * returns non 0 value if the packet should be dropped
+ */
+static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_rx_status *stats,
+ u32 rx_pkt_status,
+ u8 *crypt_len)
+{
+ if (!ieee80211_has_protected(hdr->frame_control) ||
+ (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+ RX_MPDU_RES_STATUS_SEC_NO_ENC)
+ return 0;
+
+ /* packet was encrypted with unknown alg */
+ if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+ RX_MPDU_RES_STATUS_SEC_ENC_ERR)
+ return 0;
+
+ switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
+ case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
+ /* alg is CCM: check MIC only */
+ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
+ return -1;
+
+ stats->flag |= RX_FLAG_DECRYPTED;
+ *crypt_len = IEEE80211_CCMP_HDR_LEN;
+ return 0;
+
+ case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
+ /* Don't drop the frame and decrypt it in SW */
+ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
+ return 0;
+ *crypt_len = IEEE80211_TKIP_IV_LEN;
+ /* fall through if TTAK OK */
+
+ case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
+ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
+ return -1;
+
+ stats->flag |= RX_FLAG_DECRYPTED;
+ if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+ RX_MPDU_RES_STATUS_SEC_WEP_ENC)
+ *crypt_len = IEEE80211_WEP_IV_LEN;
+ return 0;
+
+ case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
+ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
+ return -1;
+ stats->flag |= RX_FLAG_DECRYPTED;
+ return 0;
+
+ default:
+ IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
+ struct sk_buff *skb,
+ u32 status)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+
+ if (mvmvif->features & NETIF_F_RXCSUM &&
+ status & RX_MPDU_RES_STATUS_CSUM_DONE &&
+ status & RX_MPDU_RES_STATUS_CSUM_OK)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+/*
+ * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
+ *
+ * Handles the actual data of the Rx packet from the fw
+ */
+void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_rx_status *rx_status;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_rx_phy_info *phy_info;
+ struct iwl_rx_mpdu_res_start *rx_res;
+ struct ieee80211_sta *sta = NULL;
+ struct sk_buff *skb;
+ u32 len;
+ u32 ampdu_status;
+ u32 rate_n_flags;
+ u32 rx_pkt_status;
+ u8 crypt_len = 0;
+
+ phy_info = &mvm->last_phy_info;
+ rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
+ hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
+ len = le16_to_cpu(rx_res->byte_count);
+ rx_pkt_status = le32_to_cpup((__le32 *)
+ (pkt->data + sizeof(*rx_res) + len));
+
+ /* Dont use dev_alloc_skb(), we'll have enough headroom once
+ * ieee80211_hdr pulled.
+ */
+ skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(mvm, "alloc_skb failed\n");
+ return;
+ }
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+
+ /*
+ * drop the packet if it has failed being decrypted by HW
+ */
+ if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
+ &crypt_len)) {
+ IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
+ rx_pkt_status);
+ kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * Keep packets with CRC errors (and with overrun) for monitor mode
+ * (otherwise the firmware discards them) but mark them as bad.
+ */
+ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
+ !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
+ IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ }
+
+ /* This will be used in several places later */
+ rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
+
+ /* rx_status carries information about the packet to mac80211 */
+ rx_status->mactime = le64_to_cpu(phy_info->timestamp);
+ rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
+ rx_status->band =
+ (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
+ IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+ rx_status->freq =
+ ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
+ rx_status->band);
+ /*
+ * TSF as indicated by the fw is at INA time, but mac80211 expects the
+ * TSF at the beginning of the MPDU.
+ */
+ /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/
+
+ iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
+
+ IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal,
+ (unsigned long long)rx_status->mactime);
+
+ rcu_read_lock();
+ if (rx_pkt_status & RX_MPDU_RES_STATUS_SRC_STA_FOUND) {
+ u32 id = rx_pkt_status & RX_MPDU_RES_STATUS_STA_ID_MSK;
+
+ id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT;
+
+ if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) {
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[id]);
+ if (IS_ERR(sta))
+ sta = NULL;
+ }
+ } else if (!is_multicast_ether_addr(hdr->addr2)) {
+ /* This is fine since we prevent two stations with the same
+ * address from being added.
+ */
+ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
+ }
+
+ if (sta) {
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ /* We have tx blocked stations (with CS bit). If we heard
+ * frames from a blocked station on a new channel we can
+ * TX to it again.
+ */
+ if (unlikely(mvm->csa_tx_block_bcn_timeout))
+ iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+
+ rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
+
+ if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
+ ieee80211_is_beacon(hdr->frame_control)) {
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
+ bool trig_check;
+ s32 rssi;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw,
+ FW_DBG_TRIGGER_RSSI);
+ rssi_trig = (void *)trig->data;
+ rssi = le32_to_cpu(rssi_trig->rssi);
+
+ trig_check =
+ iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
+ trig);
+ if (trig_check && rx_status->signal < rssi)
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
+ }
+
+ if (ieee80211_is_data(hdr->frame_control))
+ iwl_mvm_rx_csum(sta, skb, rx_pkt_status);
+ }
+ rcu_read_unlock();
+
+ /* set the preamble flag if appropriate */
+ if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+
+ if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
+ /*
+ * We know which subframes of an A-MPDU belong
+ * together since we get a single PHY response
+ * from the firmware for all of them
+ */
+ rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+ rx_status->ampdu_reference = mvm->ampdu_ref;
+ }
+
+ /* Set up the HT phy flags */
+ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ rx_status->flag |= RX_FLAG_40MHZ;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
+ break;
+ }
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+ if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+ rx_status->flag |= RX_FLAG_HT_GF;
+ if (rate_n_flags & RATE_MCS_LDPC_MSK)
+ rx_status->flag |= RX_FLAG_LDPC;
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
+ RATE_MCS_STBC_POS;
+ rx_status->flag |= RX_FLAG_HT;
+ rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
+ } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+ u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
+ RATE_MCS_STBC_POS;
+ rx_status->vht_nss =
+ ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+ RATE_VHT_MCS_NSS_POS) + 1;
+ rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+ rx_status->flag |= RX_FLAG_VHT;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
+ if (rate_n_flags & RATE_MCS_BF_MSK)
+ rx_status->vht_flag |= RX_VHT_FLAG_BF;
+ } else {
+ rx_status->rate_idx =
+ iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+ rx_status->band);
+ }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ iwl_mvm_update_frame_stats(mvm, rate_n_flags,
+ rx_status->flag & RX_FLAG_AMPDU_DETAILS);
+#endif
+ iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status,
+ crypt_len, rxb);
+}
+
+static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
+ struct mvm_statistics_rx *rx_stats)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ mvm->rx_stats = *rx_stats;
+}
+
+struct iwl_mvm_stat_data {
+ struct iwl_mvm *mvm;
+ __le32 mac_id;
+ u8 beacon_filter_average_energy;
+ struct mvm_statistics_general_v8 *general;
+};
+
+static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_stat_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ int sig = -data->beacon_filter_average_energy;
+ int last_event;
+ int thold = vif->bss_conf.cqm_rssi_thold;
+ int hyst = vif->bss_conf.cqm_rssi_hyst;
+ u16 id = le32_to_cpu(data->mac_id);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ /* This doesn't need the MAC ID check since it's not taking the
+ * data copied into the "data" struct, but rather the data from
+ * the notification directly.
+ */
+ if (data->general) {
+ mvmvif->beacon_stats.num_beacons =
+ le32_to_cpu(data->general->beacon_counter[mvmvif->id]);
+ mvmvif->beacon_stats.avg_signal =
+ -data->general->beacon_average_energy[mvmvif->id];
+ }
+
+ if (mvmvif->id != id)
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (sig == 0) {
+ IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
+ return;
+ }
+
+ mvmvif->bf_data.ave_beacon_signal = sig;
+
+ /* BT Coex */
+ if (mvmvif->bf_data.bt_coex_min_thold !=
+ mvmvif->bf_data.bt_coex_max_thold) {
+ last_event = mvmvif->bf_data.last_bt_coex_event;
+ if (sig > mvmvif->bf_data.bt_coex_max_thold &&
+ (last_event <= mvmvif->bf_data.bt_coex_min_thold ||
+ last_event == 0)) {
+ mvmvif->bf_data.last_bt_coex_event = sig;
+ IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n",
+ sig);
+ iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH);
+ } else if (sig < mvmvif->bf_data.bt_coex_min_thold &&
+ (last_event >= mvmvif->bf_data.bt_coex_max_thold ||
+ last_event == 0)) {
+ mvmvif->bf_data.last_bt_coex_event = sig;
+ IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n",
+ sig);
+ iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW);
+ }
+ }
+
+ if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
+ return;
+
+ /* CQM Notification */
+ last_event = mvmvif->bf_data.last_cqm_event;
+ if (thold && sig < thold && (last_event == 0 ||
+ sig < last_event - hyst)) {
+ mvmvif->bf_data.last_cqm_event = sig;
+ IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n",
+ sig);
+ ieee80211_cqm_rssi_notify(
+ vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ GFP_KERNEL);
+ } else if (sig > thold &&
+ (last_event == 0 || sig > last_event + hyst)) {
+ mvmvif->bf_data.last_cqm_event = sig;
+ IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n",
+ sig);
+ ieee80211_cqm_rssi_notify(
+ vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ GFP_KERNEL);
+ }
+}
+
+static inline void
+iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_stats *trig_stats;
+ u32 trig_offset, trig_thold;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS);
+ trig_stats = (void *)trig->data;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
+ return;
+
+ trig_offset = le32_to_cpu(trig_stats->stop_offset);
+ trig_thold = le32_to_cpu(trig_stats->stop_threshold);
+
+ if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt)))
+ return;
+
+ if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
+}
+
+void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data;
+ struct iwl_mvm_stat_data data = {
+ .mvm = mvm,
+ };
+ u32 temperature;
+
+ if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats))
+ goto invalid;
+
+ temperature = le32_to_cpu(stats->general.radio_temperature);
+ data.mac_id = stats->rx.general.mac_id;
+ data.beacon_filter_average_energy =
+ stats->general.beacon_filter_average_energy;
+
+ iwl_mvm_update_rx_statistics(mvm, &stats->rx);
+
+ mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
+ mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
+ mvm->radio_stats.on_time_rf =
+ le64_to_cpu(stats->general.on_time_rf);
+ mvm->radio_stats.on_time_scan =
+ le64_to_cpu(stats->general.on_time_scan);
+
+ data.general = &stats->general;
+
+ iwl_mvm_rx_stats_check_trigger(mvm, pkt);
+
+ ieee80211_iterate_active_interfaces(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_stat_iterator,
+ &data);
+ return;
+ invalid:
+ IWL_ERR(mvm, "received invalid statistics size (%d)!\n",
+ iwl_rx_packet_payload_len(pkt));
+}
+
+void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
new file mode 100644
index 000000000000..0c073e02fd4c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -0,0 +1,458 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include "iwl-trans.h"
+#include "mvm.h"
+#include "fw-api.h"
+#include "fw-dbg.h"
+
+void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ mvm->ampdu_ref++;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
+ spin_lock(&mvm->drv_stats_lock);
+ mvm->drv_rx_stats.ampdu_count++;
+ spin_unlock(&mvm->drv_stats_lock);
+ }
+#endif
+}
+
+static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,
+ int queue, struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
+ struct iwl_mvm_key_pn *ptk_pn;
+ u8 tid, keyidx;
+ u8 pn[IEEE80211_CCMP_PN_LEN];
+ u8 *extiv;
+
+ /* do PN checking */
+
+ /* multicast and non-data only arrives on default queue */
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return 0;
+
+ /* do not check PN for open AP */
+ if (!(stats->flag & RX_FLAG_DECRYPTED))
+ return 0;
+
+ /*
+ * avoid checking for default queue - we don't want to replicate
+ * all the logic that's necessary for checking the PN on fragmented
+ * frames, leave that to mac80211
+ */
+ if (queue == 0)
+ return 0;
+
+ /* if we are here - this for sure is either CCMP or GCMP */
+ if (IS_ERR_OR_NULL(sta)) {
+ IWL_ERR(mvm,
+ "expected hw-decrypted unicast frame for station\n");
+ return -1;
+ }
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+ keyidx = extiv[3] >> 6;
+
+ ptk_pn = rcu_dereference(mvmsta->ptk_pn[keyidx]);
+ if (!ptk_pn)
+ return -1;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ else
+ tid = 0;
+
+ /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */
+ if (tid >= IWL_MAX_TID_COUNT)
+ return -1;
+
+ /* load pn */
+ pn[0] = extiv[7];
+ pn[1] = extiv[6];
+ pn[2] = extiv[5];
+ pn[3] = extiv[4];
+ pn[4] = extiv[1];
+ pn[5] = extiv[0];
+
+ if (memcmp(pn, ptk_pn->q[queue].pn[tid],
+ IEEE80211_CCMP_PN_LEN) <= 0)
+ return -1;
+
+ memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN);
+ stats->flag |= RX_FLAG_PN_VALIDATED;
+
+ return 0;
+}
+
+/* iwl_mvm_create_skb Adds the rxb to a new skb */
+static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr,
+ u16 len, u8 crypt_len,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ unsigned int hdrlen, fraglen;
+
+ /* If frame is small enough to fit in skb->head, pull it completely.
+ * If not, only pull ieee80211_hdr (including crypto if present, and
+ * an additional 8 bytes for SNAP/ethertype, see below) so that
+ * splice() or TCP coalesce are more efficient.
+ *
+ * Since, in addition, ieee80211_data_to_8023() always pull in at
+ * least 8 bytes (possibly more for mesh) we can do the same here
+ * to save the cost of doing it later. That still doesn't pull in
+ * the actual IP header since the typical case has a SNAP header.
+ * If the latter changes (there are efforts in the standards group
+ * to do so) we should revisit this and ieee80211_data_to_8023().
+ */
+ hdrlen = (len <= skb_tailroom(skb)) ? len :
+ sizeof(*hdr) + crypt_len + 8;
+
+ memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
+ fraglen = len - hdrlen;
+
+ if (fraglen) {
+ int offset = (void *)hdr + hdrlen -
+ rxb_addr(rxb) + rxb_offset(rxb);
+
+ skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+ fraglen, rxb->truesize);
+ }
+}
+
+/* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */
+static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+ struct napi_struct *napi,
+ struct sk_buff *skb, int queue,
+ struct ieee80211_sta *sta)
+{
+ if (iwl_mvm_check_pn(mvm, skb, queue, sta))
+ kfree_skb(skb);
+ else
+ ieee80211_rx_napi(mvm->hw, skb, napi);
+}
+
+static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
+ struct iwl_rx_mpdu_desc *desc,
+ struct ieee80211_rx_status *rx_status)
+{
+ int energy_a, energy_b, energy_c, max_energy;
+
+ energy_a = desc->energy_a;
+ energy_a = energy_a ? -energy_a : S8_MIN;
+ energy_b = desc->energy_b;
+ energy_b = energy_b ? -energy_b : S8_MIN;
+ energy_c = desc->energy_c;
+ energy_c = energy_c ? -energy_c : S8_MIN;
+ max_energy = max(energy_a, energy_b);
+ max_energy = max(max_energy, energy_c);
+
+ IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
+ energy_a, energy_b, energy_c, max_energy);
+
+ rx_status->signal = max_energy;
+ rx_status->chains = 0; /* TODO: phy info */
+ rx_status->chain_signal[0] = energy_a;
+ rx_status->chain_signal[1] = energy_b;
+ rx_status->chain_signal[2] = energy_c;
+}
+
+static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
+ struct ieee80211_rx_status *stats,
+ struct iwl_rx_mpdu_desc *desc, int queue,
+ u8 *crypt_len)
+{
+ u16 status = le16_to_cpu(desc->status);
+
+ if (!ieee80211_has_protected(hdr->frame_control) ||
+ (status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
+ IWL_RX_MPDU_STATUS_SEC_NONE)
+ return 0;
+
+ /* TODO: handle packets encrypted with unknown alg */
+
+ switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) {
+ case IWL_RX_MPDU_STATUS_SEC_CCM:
+ case IWL_RX_MPDU_STATUS_SEC_GCM:
+ BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN);
+ /* alg is CCM: check MIC only */
+ if (!(status & IWL_RX_MPDU_STATUS_MIC_OK))
+ return -1;
+
+ stats->flag |= RX_FLAG_DECRYPTED;
+ *crypt_len = IEEE80211_CCMP_HDR_LEN;
+ return 0;
+ case IWL_RX_MPDU_STATUS_SEC_TKIP:
+ /* Don't drop the frame and decrypt it in SW */
+ if (!(status & IWL_RX_MPDU_RES_STATUS_TTAK_OK))
+ return 0;
+
+ *crypt_len = IEEE80211_TKIP_IV_LEN;
+ /* fall through if TTAK OK */
+ case IWL_RX_MPDU_STATUS_SEC_WEP:
+ if (!(status & IWL_RX_MPDU_STATUS_ICV_OK))
+ return -1;
+
+ stats->flag |= RX_FLAG_DECRYPTED;
+ if ((status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
+ IWL_RX_MPDU_STATUS_SEC_WEP)
+ *crypt_len = IEEE80211_WEP_IV_LEN;
+ return 0;
+ case IWL_RX_MPDU_STATUS_SEC_EXT_ENC:
+ if (!(status & IWL_RX_MPDU_STATUS_MIC_OK))
+ return -1;
+ stats->flag |= RX_FLAG_DECRYPTED;
+ return 0;
+ default:
+ IWL_ERR(mvm, "Unhandled alg: 0x%x\n", status);
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
+ struct sk_buff *skb,
+ struct iwl_rx_mpdu_desc *desc)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+
+ if (mvmvif->features & NETIF_F_RXCSUM &&
+ desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_IP_HDR_CSUM_OK) &&
+ desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_TCP_UDP_CSUM_OK))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, int queue)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_rx_mpdu_desc *desc = (void *)pkt->data;
+ struct ieee80211_hdr *hdr = (void *)(desc + 1);
+ u32 len = le16_to_cpu(desc->mpdu_len);
+ u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags);
+ struct ieee80211_sta *sta = NULL;
+ struct sk_buff *skb;
+ u8 crypt_len = 0;
+
+ /* Dont use dev_alloc_skb(), we'll have enough headroom once
+ * ieee80211_hdr pulled.
+ */
+ skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(mvm, "alloc_skb failed\n");
+ return;
+ }
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+
+ if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, desc, queue, &crypt_len)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * Keep packets with CRC errors (and with overrun) for monitor mode
+ * (otherwise the firmware discards them) but mark them as bad.
+ */
+ if (!(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_CRC_OK)) ||
+ !(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_OVERRUN_OK))) {
+ IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n",
+ le16_to_cpu(desc->status));
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ }
+
+ rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise);
+ rx_status->device_timestamp = le32_to_cpu(desc->gp2_on_air_rise);
+ rx_status->band = desc->channel > 14 ? IEEE80211_BAND_5GHZ :
+ IEEE80211_BAND_2GHZ;
+ rx_status->freq = ieee80211_channel_to_frequency(desc->channel,
+ rx_status->band);
+ iwl_mvm_get_signal_strength(mvm, desc, rx_status);
+
+ rcu_read_lock();
+
+ if (le16_to_cpu(desc->status) & IWL_RX_MPDU_STATUS_SRC_STA_FOUND) {
+ u8 id = desc->sta_id_flags & IWL_RX_MPDU_SIF_STA_ID_MASK;
+
+ if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) {
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[id]);
+ if (IS_ERR(sta))
+ sta = NULL;
+ }
+ } else if (!is_multicast_ether_addr(hdr->addr2)) {
+ /*
+ * This is fine since we prevent two stations with the same
+ * address from being added.
+ */
+ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
+ }
+
+ if (sta) {
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ /*
+ * We have tx blocked stations (with CS bit). If we heard
+ * frames from a blocked station on a new channel we can
+ * TX to it again.
+ */
+ if (unlikely(mvm->csa_tx_block_bcn_timeout))
+ iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+
+ rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
+
+ if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
+ ieee80211_is_beacon(hdr->frame_control)) {
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
+ bool trig_check;
+ s32 rssi;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw,
+ FW_DBG_TRIGGER_RSSI);
+ rssi_trig = (void *)trig->data;
+ rssi = le32_to_cpu(rssi_trig->rssi);
+
+ trig_check =
+ iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
+ trig);
+ if (trig_check && rx_status->signal < rssi)
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
+ }
+
+ /* TODO: multi queue TCM */
+
+ if (ieee80211_is_data(hdr->frame_control))
+ iwl_mvm_rx_csum(sta, skb, desc);
+ }
+
+ /*
+ * TODO: PHY info.
+ * Verify we don't have the information in the MPDU descriptor and
+ * that it is not needed.
+ * Make sure for monitor mode that we are on default queue, update
+ * ampdu_ref and the rest of phy info then
+ */
+
+ /* Set up the HT phy flags */
+ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ rx_status->flag |= RX_FLAG_40MHZ;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
+ break;
+ }
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+ if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+ rx_status->flag |= RX_FLAG_HT_GF;
+ if (rate_n_flags & RATE_MCS_LDPC_MSK)
+ rx_status->flag |= RX_FLAG_LDPC;
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
+ RATE_MCS_STBC_POS;
+ rx_status->flag |= RX_FLAG_HT;
+ rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
+ } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+ u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
+ RATE_MCS_STBC_POS;
+ rx_status->vht_nss =
+ ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+ RATE_VHT_MCS_NSS_POS) + 1;
+ rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+ rx_status->flag |= RX_FLAG_VHT;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
+ if (rate_n_flags & RATE_MCS_BF_MSK)
+ rx_status->vht_flag |= RX_VHT_FLAG_BF;
+ } else {
+ rx_status->rate_idx =
+ iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+ rx_status->band);
+ }
+
+ /* TODO: PHY info - update ampdu queue statistics (for debugfs) */
+ /* TODO: PHY info - gscan */
+
+ iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb);
+ iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
+ rcu_read_unlock();
+}
+
+void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb, int queue)
+{
+ /* TODO */
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
new file mode 100644
index 000000000000..9a15642f80dd
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
@@ -0,0 +1,1585 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "mvm.h"
+#include "fw-api-scan.h"
+
+#define IWL_DENSE_EBS_SCAN_RATIO 5
+#define IWL_SPARSE_EBS_SCAN_RATIO 1
+
+enum iwl_mvm_traffic_load {
+ IWL_MVM_TRAFFIC_LOW,
+ IWL_MVM_TRAFFIC_MEDIUM,
+ IWL_MVM_TRAFFIC_HIGH,
+};
+
+struct iwl_mvm_scan_timing_params {
+ u32 dwell_active;
+ u32 dwell_passive;
+ u32 dwell_fragmented;
+ u32 dwell_extended;
+ u32 suspend_time;
+ u32 max_out_time;
+};
+
+static struct iwl_mvm_scan_timing_params scan_timing[] = {
+ [IWL_SCAN_TYPE_UNASSOC] = {
+ .dwell_active = 10,
+ .dwell_passive = 110,
+ .dwell_fragmented = 44,
+ .dwell_extended = 90,
+ .suspend_time = 0,
+ .max_out_time = 0,
+ },
+ [IWL_SCAN_TYPE_WILD] = {
+ .dwell_active = 10,
+ .dwell_passive = 110,
+ .dwell_fragmented = 44,
+ .dwell_extended = 90,
+ .suspend_time = 30,
+ .max_out_time = 120,
+ },
+ [IWL_SCAN_TYPE_MILD] = {
+ .dwell_active = 10,
+ .dwell_passive = 110,
+ .dwell_fragmented = 44,
+ .dwell_extended = 90,
+ .suspend_time = 120,
+ .max_out_time = 120,
+ },
+ [IWL_SCAN_TYPE_FRAGMENTED] = {
+ .dwell_active = 10,
+ .dwell_passive = 110,
+ .dwell_fragmented = 44,
+ .suspend_time = 95,
+ .max_out_time = 44,
+ },
+};
+
+struct iwl_mvm_scan_params {
+ enum iwl_mvm_scan_type type;
+ u32 n_channels;
+ u16 delay;
+ int n_ssids;
+ struct cfg80211_ssid *ssids;
+ struct ieee80211_channel **channels;
+ u32 flags;
+ u8 *mac_addr;
+ u8 *mac_addr_mask;
+ bool no_cck;
+ bool pass_all;
+ int n_match_sets;
+ struct iwl_scan_probe_req preq;
+ struct cfg80211_match_set *match_sets;
+ int n_scan_plans;
+ struct cfg80211_sched_scan_plan *scan_plans;
+};
+
+static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
+{
+ if (mvm->scan_rx_ant != ANT_NONE)
+ return mvm->scan_rx_ant;
+ return iwl_mvm_get_valid_rx_ant(mvm);
+}
+
+static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
+{
+ u16 rx_chain;
+ u8 rx_ant;
+
+ rx_ant = iwl_mvm_scan_rx_ant(mvm);
+ rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
+ rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
+ rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
+ rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS;
+ return cpu_to_le16(rx_chain);
+}
+
+static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band)
+{
+ if (band == IEEE80211_BAND_2GHZ)
+ return cpu_to_le32(PHY_BAND_24);
+ else
+ return cpu_to_le32(PHY_BAND_5);
+}
+
+static inline __le32
+iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
+ bool no_cck)
+{
+ u32 tx_ant;
+
+ mvm->scan_last_antenna_idx =
+ iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
+ mvm->scan_last_antenna_idx);
+ tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+ if (band == IEEE80211_BAND_2GHZ && !no_cck)
+ return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK |
+ tx_ant);
+ else
+ return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant);
+}
+
+static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int *global_cnt = data;
+
+ if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt &&
+ mvmvif->phy_ctxt->id < MAX_PHYS)
+ *global_cnt += 1;
+}
+
+static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm)
+{
+ return IWL_MVM_TRAFFIC_LOW;
+}
+
+static enum
+iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device)
+{
+ int global_cnt = 0;
+ enum iwl_mvm_traffic_load load;
+ bool low_latency;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_scan_condition_iterator,
+ &global_cnt);
+ if (!global_cnt)
+ return IWL_SCAN_TYPE_UNASSOC;
+
+ load = iwl_mvm_get_traffic_load(mvm);
+ low_latency = iwl_mvm_low_latency(mvm);
+
+ if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && !p2p_device &&
+ fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN))
+ return IWL_SCAN_TYPE_FRAGMENTED;
+
+ if (load >= IWL_MVM_TRAFFIC_MEDIUM || low_latency)
+ return IWL_SCAN_TYPE_MILD;
+
+ return IWL_SCAN_TYPE_WILD;
+}
+
+static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm)
+{
+ /* require rrm scan whenever the fw supports it */
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT);
+}
+
+static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm)
+{
+ int max_probe_len;
+
+ max_probe_len = SCAN_OFFLOAD_PROBE_REQ_SIZE;
+
+ /* we create the 802.11 header and SSID element */
+ max_probe_len -= 24 + 2;
+
+ /* DS parameter set element is added on 2.4GHZ band if required */
+ if (iwl_mvm_rrm_scan_needed(mvm))
+ max_probe_len -= 3;
+
+ return max_probe_len;
+}
+
+int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm)
+{
+ int max_ie_len = iwl_mvm_max_scan_ie_fw_cmd_room(mvm);
+
+ /* TODO: [BUG] This function should return the maximum allowed size of
+ * scan IEs, however the LMAC scan api contains both 2GHZ and 5GHZ IEs
+ * in the same command. So the correct implementation of this function
+ * is just iwl_mvm_max_scan_ie_fw_cmd_room() / 2. Currently the scan
+ * command has only 512 bytes and it would leave us with about 240
+ * bytes for scan IEs, which is clearly not enough. So meanwhile
+ * we will report an incorrect value. This may result in a failure to
+ * issue a scan in unified_scan_lmac and unified_sched_scan_lmac
+ * functions with -ENOBUFS, if a large enough probe will be provided.
+ */
+ return max_ie_len;
+}
+
+static u8 *iwl_mvm_dump_channel_list(struct iwl_scan_results_notif *res,
+ int num_res, u8 *buf, size_t buf_size)
+{
+ int i;
+ u8 *pos = buf, *end = buf + buf_size;
+
+ for (i = 0; pos < end && i < num_res; i++)
+ pos += snprintf(pos, end - pos, " %u", res[i].channel);
+
+ /* terminate the string in case the buffer was too short */
+ *(buf + buf_size - 1) = '\0';
+
+ return buf;
+}
+
+void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_lmac_scan_complete_notif *notif = (void *)pkt->data;
+ u8 buf[256];
+
+ IWL_DEBUG_SCAN(mvm,
+ "Scan offload iteration complete: status=0x%x scanned channels=%d channels list: %s\n",
+ notif->status, notif->scanned_channels,
+ iwl_mvm_dump_channel_list(notif->results,
+ notif->scanned_channels, buf,
+ sizeof(buf)));
+}
+
+void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+ ieee80211_sched_scan_results(mvm->hw);
+}
+
+static const char *iwl_mvm_ebs_status_str(enum iwl_scan_ebs_status status)
+{
+ switch (status) {
+ case IWL_SCAN_EBS_SUCCESS:
+ return "successful";
+ case IWL_SCAN_EBS_INACTIVE:
+ return "inactive";
+ case IWL_SCAN_EBS_FAILED:
+ case IWL_SCAN_EBS_CHAN_NOT_FOUND:
+ default:
+ return "failed";
+ }
+}
+
+void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_periodic_scan_complete *scan_notif = (void *)pkt->data;
+ bool aborted = (scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED);
+
+ /* If this happens, the firmware has mistakenly sent an LMAC
+ * notification during UMAC scans -- warn and ignore it.
+ */
+ if (WARN_ON_ONCE(fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN)))
+ return;
+
+ /* scan status must be locked for proper checking */
+ lockdep_assert_held(&mvm->mutex);
+
+ /* We first check if we were stopping a scan, in which case we
+ * just clear the stopping flag. Then we check if it was a
+ * firmware initiated stop, in which case we need to inform
+ * mac80211.
+ * Note that we can have a stopping and a running scan
+ * simultaneously, but we can't have two different types of
+ * scans stopping or running at the same time (since LMAC
+ * doesn't support it).
+ */
+
+ if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_SCHED) {
+ WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR);
+
+ IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n",
+ aborted ? "aborted" : "completed",
+ iwl_mvm_ebs_status_str(scan_notif->ebs_status));
+ IWL_DEBUG_SCAN(mvm,
+ "Last line %d, Last iteration %d, Time after last iteration %d\n",
+ scan_notif->last_schedule_line,
+ scan_notif->last_schedule_iteration,
+ __le32_to_cpu(scan_notif->time_after_last_iter));
+
+ mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_SCHED;
+ } else if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR) {
+ IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s\n",
+ aborted ? "aborted" : "completed",
+ iwl_mvm_ebs_status_str(scan_notif->ebs_status));
+
+ mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_REGULAR;
+ } else if (mvm->scan_status & IWL_MVM_SCAN_SCHED) {
+ WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_REGULAR);
+
+ IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n",
+ aborted ? "aborted" : "completed",
+ iwl_mvm_ebs_status_str(scan_notif->ebs_status));
+ IWL_DEBUG_SCAN(mvm,
+ "Last line %d, Last iteration %d, Time after last iteration %d (FW)\n",
+ scan_notif->last_schedule_line,
+ scan_notif->last_schedule_iteration,
+ __le32_to_cpu(scan_notif->time_after_last_iter));
+
+ mvm->scan_status &= ~IWL_MVM_SCAN_SCHED;
+ ieee80211_sched_scan_stopped(mvm->hw);
+ } else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) {
+ IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n",
+ aborted ? "aborted" : "completed",
+ iwl_mvm_ebs_status_str(scan_notif->ebs_status));
+
+ mvm->scan_status &= ~IWL_MVM_SCAN_REGULAR;
+ ieee80211_scan_completed(mvm->hw,
+ scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ }
+
+ mvm->last_ebs_successful =
+ scan_notif->ebs_status == IWL_SCAN_EBS_SUCCESS ||
+ scan_notif->ebs_status == IWL_SCAN_EBS_INACTIVE;
+}
+
+static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
+{
+ int i;
+
+ for (i = 0; i < PROBE_OPTION_MAX; i++) {
+ if (!ssid_list[i].len)
+ break;
+ if (ssid_list[i].len == ssid_len &&
+ !memcmp(ssid_list->ssid, ssid, ssid_len))
+ return i;
+ }
+ return -1;
+}
+
+/* We insert the SSIDs in an inverted order, because the FW will
+ * invert it back.
+ */
+static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params,
+ struct iwl_ssid_ie *ssids,
+ u32 *ssid_bitmap)
+{
+ int i, j;
+ int index;
+
+ /*
+ * copy SSIDs from match list.
+ * iwl_config_sched_scan_profiles() uses the order of these ssids to
+ * config match list.
+ */
+ for (i = 0, j = params->n_match_sets - 1;
+ j >= 0 && i < PROBE_OPTION_MAX;
+ i++, j--) {
+ /* skip empty SSID matchsets */
+ if (!params->match_sets[j].ssid.ssid_len)
+ continue;
+ ssids[i].id = WLAN_EID_SSID;
+ ssids[i].len = params->match_sets[j].ssid.ssid_len;
+ memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid,
+ ssids[i].len);
+ }
+
+ /* add SSIDs from scan SSID list */
+ *ssid_bitmap = 0;
+ for (j = params->n_ssids - 1;
+ j >= 0 && i < PROBE_OPTION_MAX;
+ i++, j--) {
+ index = iwl_ssid_exist(params->ssids[j].ssid,
+ params->ssids[j].ssid_len,
+ ssids);
+ if (index < 0) {
+ ssids[i].id = WLAN_EID_SSID;
+ ssids[i].len = params->ssids[j].ssid_len;
+ memcpy(ssids[i].ssid, params->ssids[j].ssid,
+ ssids[i].len);
+ *ssid_bitmap |= BIT(i);
+ } else {
+ *ssid_bitmap |= BIT(index);
+ }
+ }
+}
+
+static int
+iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct iwl_scan_offload_profile *profile;
+ struct iwl_scan_offload_profile_cfg *profile_cfg;
+ struct iwl_scan_offload_blacklist *blacklist;
+ struct iwl_host_cmd cmd = {
+ .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
+ .len[1] = sizeof(*profile_cfg),
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ .dataflags[1] = IWL_HCMD_DFL_NOCOPY,
+ };
+ int blacklist_len;
+ int i;
+ int ret;
+
+ if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES))
+ return -EIO;
+
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL)
+ blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN;
+ else
+ blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN;
+
+ blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL);
+ if (!blacklist)
+ return -ENOMEM;
+
+ profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL);
+ if (!profile_cfg) {
+ ret = -ENOMEM;
+ goto free_blacklist;
+ }
+
+ cmd.data[0] = blacklist;
+ cmd.len[0] = sizeof(*blacklist) * blacklist_len;
+ cmd.data[1] = profile_cfg;
+
+ /* No blacklist configuration */
+
+ profile_cfg->num_profiles = req->n_match_sets;
+ profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN;
+ profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN;
+ profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN;
+ if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len)
+ profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN;
+
+ for (i = 0; i < req->n_match_sets; i++) {
+ profile = &profile_cfg->profiles[i];
+ profile->ssid_index = i;
+ /* Support any cipher and auth algorithm */
+ profile->unicast_cipher = 0xff;
+ profile->auth_alg = 0xff;
+ profile->network_type = IWL_NETWORK_TYPE_ANY;
+ profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY;
+ profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN;
+ }
+
+ IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n");
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ kfree(profile_cfg);
+free_blacklist:
+ kfree(blacklist);
+
+ return ret;
+}
+
+static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm,
+ struct cfg80211_sched_scan_request *req)
+{
+ if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+ IWL_DEBUG_SCAN(mvm,
+ "Sending scheduled scan with filtering, n_match_sets %d\n",
+ req->n_match_sets);
+ return false;
+ }
+
+ IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n");
+ return true;
+}
+
+static int iwl_mvm_lmac_scan_abort(struct iwl_mvm *mvm)
+{
+ int ret;
+ struct iwl_host_cmd cmd = {
+ .id = SCAN_OFFLOAD_ABORT_CMD,
+ };
+ u32 status;
+
+ ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
+ if (ret)
+ return ret;
+
+ if (status != CAN_ABORT_STATUS) {
+ /*
+ * The scan abort will return 1 for success or
+ * 2 for "failure". A failure condition can be
+ * due to simply not being in an active scan which
+ * can occur if we send the scan abort before the
+ * microcode has notified us that a scan is completed.
+ */
+ IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status);
+ ret = -ENOENT;
+ }
+
+ return ret;
+}
+
+static void iwl_mvm_scan_fill_tx_cmd(struct iwl_mvm *mvm,
+ struct iwl_scan_req_tx_cmd *tx_cmd,
+ bool no_cck)
+{
+ tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+ TX_CMD_FLG_BT_DIS);
+ tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+ IEEE80211_BAND_2GHZ,
+ no_cck);
+ tx_cmd[0].sta_id = mvm->aux_sta.sta_id;
+
+ tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+ TX_CMD_FLG_BT_DIS);
+ tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+ IEEE80211_BAND_5GHZ,
+ no_cck);
+ tx_cmd[1].sta_id = mvm->aux_sta.sta_id;
+}
+
+static void
+iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm,
+ struct ieee80211_channel **channels,
+ int n_channels, u32 ssid_bitmap,
+ struct iwl_scan_req_lmac *cmd)
+{
+ struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data;
+ int i;
+
+ for (i = 0; i < n_channels; i++) {
+ channel_cfg[i].channel_num =
+ cpu_to_le16(channels[i]->hw_value);
+ channel_cfg[i].iter_count = cpu_to_le16(1);
+ channel_cfg[i].iter_interval = 0;
+ channel_cfg[i].flags =
+ cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL |
+ ssid_bitmap);
+ }
+}
+
+static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies,
+ size_t len, u8 *const pos)
+{
+ static const u8 before_ds_params[] = {
+ WLAN_EID_SSID,
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_REQUEST,
+ WLAN_EID_EXT_SUPP_RATES,
+ };
+ size_t offs;
+ u8 *newpos = pos;
+
+ if (!iwl_mvm_rrm_scan_needed(mvm)) {
+ memcpy(newpos, ies, len);
+ return newpos + len;
+ }
+
+ offs = ieee80211_ie_split(ies, len,
+ before_ds_params,
+ ARRAY_SIZE(before_ds_params),
+ 0);
+
+ memcpy(newpos, ies, offs);
+ newpos += offs;
+
+ /* Add a placeholder for DS Parameter Set element */
+ *newpos++ = WLAN_EID_DS_PARAMS;
+ *newpos++ = 1;
+ *newpos++ = 0;
+
+ memcpy(newpos, ies + offs, len - offs);
+ newpos += len - offs;
+
+ return newpos;
+}
+
+static void
+iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_scan_ies *ies,
+ struct iwl_mvm_scan_params *params)
+{
+ struct ieee80211_mgmt *frame = (void *)params->preq.buf;
+ u8 *pos, *newpos;
+ const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+ params->mac_addr : NULL;
+
+ /*
+ * Unfortunately, right now the offload scan doesn't support randomising
+ * within the firmware, so until the firmware API is ready we implement
+ * it in the driver. This means that the scan iterations won't really be
+ * random, only when it's restarted, but at least that helps a bit.
+ */
+ if (mac_addr)
+ get_random_mask_addr(frame->sa, mac_addr,
+ params->mac_addr_mask);
+ else
+ memcpy(frame->sa, vif->addr, ETH_ALEN);
+
+ frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+ eth_broadcast_addr(frame->da);
+ eth_broadcast_addr(frame->bssid);
+ frame->seq_ctrl = 0;
+
+ pos = frame->u.probe_req.variable;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = 0;
+
+ params->preq.mac_header.offset = 0;
+ params->preq.mac_header.len = cpu_to_le16(24 + 2);
+
+ /* Insert ds parameter set element on 2.4 GHz band */
+ newpos = iwl_mvm_copy_and_insert_ds_elem(mvm,
+ ies->ies[IEEE80211_BAND_2GHZ],
+ ies->len[IEEE80211_BAND_2GHZ],
+ pos);
+ params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf);
+ params->preq.band_data[0].len = cpu_to_le16(newpos - pos);
+ pos = newpos;
+
+ memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
+ ies->len[IEEE80211_BAND_5GHZ]);
+ params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf);
+ params->preq.band_data[1].len =
+ cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]);
+ pos += ies->len[IEEE80211_BAND_5GHZ];
+
+ memcpy(pos, ies->common_ies, ies->common_ie_len);
+ params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf);
+ params->preq.common_data.len = cpu_to_le16(ies->common_ie_len);
+}
+
+static __le32 iwl_mvm_scan_priority(struct iwl_mvm *mvm,
+ enum iwl_scan_priority_ext prio)
+{
+ if (fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY))
+ return cpu_to_le32(prio);
+
+ if (prio <= IWL_SCAN_PRIORITY_EXT_2)
+ return cpu_to_le32(IWL_SCAN_PRIORITY_LOW);
+
+ if (prio <= IWL_SCAN_PRIORITY_EXT_4)
+ return cpu_to_le32(IWL_SCAN_PRIORITY_MEDIUM);
+
+ return cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+}
+
+static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm,
+ struct iwl_scan_req_lmac *cmd,
+ struct iwl_mvm_scan_params *params)
+{
+ cmd->active_dwell = scan_timing[params->type].dwell_active;
+ cmd->passive_dwell = scan_timing[params->type].dwell_passive;
+ cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented;
+ cmd->extended_dwell = scan_timing[params->type].dwell_extended;
+ cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time);
+ cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time);
+ cmd->scan_prio = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
+}
+
+static inline bool iwl_mvm_scan_fits(struct iwl_mvm *mvm, int n_ssids,
+ struct ieee80211_scan_ies *ies,
+ int n_channels)
+{
+ return ((n_ssids <= PROBE_OPTION_MAX) &&
+ (n_channels <= mvm->fw->ucode_capa.n_scan_channels) &
+ (ies->common_ie_len +
+ ies->len[NL80211_BAND_2GHZ] +
+ ies->len[NL80211_BAND_5GHZ] <=
+ iwl_mvm_max_scan_ie_fw_cmd_room(mvm)));
+}
+
+static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa;
+
+ /* We can only use EBS if:
+ * 1. the feature is supported;
+ * 2. the last EBS was successful;
+ * 3. if only single scan, the single scan EBS API is supported;
+ * 4. it's not a p2p find operation.
+ */
+ return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) &&
+ mvm->last_ebs_successful &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE);
+}
+
+static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params)
+{
+ return params->n_scan_plans == 1 &&
+ params->scan_plans[0].iterations == 1;
+}
+
+static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm,
+ struct iwl_mvm_scan_params *params,
+ struct ieee80211_vif *vif)
+{
+ int flags = 0;
+
+ if (params->n_ssids == 0)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
+
+ if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
+
+ if (params->type == IWL_SCAN_TYPE_FRAGMENTED)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
+
+ if (iwl_mvm_rrm_scan_needed(mvm))
+ flags |= IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED;
+
+ if (params->pass_all)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+ else
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_MATCH;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvm->scan_iter_notif_enabled)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE;
+#endif
+
+ if (iwl_mvm_is_regular_scan(params) &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ params->type != IWL_SCAN_TYPE_FRAGMENTED)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL;
+
+ return flags;
+}
+
+static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct iwl_mvm_scan_params *params)
+{
+ struct iwl_scan_req_lmac *cmd = mvm->scan_cmd;
+ struct iwl_scan_probe_req *preq =
+ (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels);
+ u32 ssid_bitmap = 0;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ memset(cmd, 0, ksize(cmd));
+
+ if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
+ return -EINVAL;
+
+ iwl_mvm_scan_lmac_dwell(mvm, cmd, params);
+
+ cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm);
+ cmd->iter_num = cpu_to_le32(1);
+ cmd->n_channels = (u8)params->n_channels;
+
+ cmd->delay = cpu_to_le32(params->delay);
+
+ cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params,
+ vif));
+
+ cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band);
+ cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+ MAC_FILTER_IN_BEACON);
+ iwl_mvm_scan_fill_tx_cmd(mvm, cmd->tx_cmd, params->no_cck);
+ iwl_scan_build_ssids(params, cmd->direct_scan, &ssid_bitmap);
+
+ /* this API uses bits 1-20 instead of 0-19 */
+ ssid_bitmap <<= 1;
+
+ for (i = 0; i < params->n_scan_plans; i++) {
+ struct cfg80211_sched_scan_plan *scan_plan =
+ &params->scan_plans[i];
+
+ cmd->schedule[i].delay =
+ cpu_to_le16(scan_plan->interval);
+ cmd->schedule[i].iterations = scan_plan->iterations;
+ cmd->schedule[i].full_scan_mul = 1;
+ }
+
+ /*
+ * If the number of iterations of the last scan plan is set to
+ * zero, it should run infinitely. However, this is not always the case.
+ * For example, when regular scan is requested the driver sets one scan
+ * plan with one iteration.
+ */
+ if (!cmd->schedule[i - 1].iterations)
+ cmd->schedule[i - 1].iterations = 0xff;
+
+ if (iwl_mvm_scan_use_ebs(mvm, vif)) {
+ cmd->channel_opt[0].flags =
+ cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+ cmd->channel_opt[0].non_ebs_ratio =
+ cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO);
+ cmd->channel_opt[1].flags =
+ cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+ cmd->channel_opt[1].non_ebs_ratio =
+ cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO);
+ }
+
+ iwl_mvm_lmac_scan_cfg_channels(mvm, params->channels,
+ params->n_channels, ssid_bitmap, cmd);
+
+ *preq = params->preq;
+
+ return 0;
+}
+
+static int rate_to_scan_rate_flag(unsigned int rate)
+{
+ static const int rate_to_scan_rate[IWL_RATE_COUNT] = {
+ [IWL_RATE_1M_INDEX] = SCAN_CONFIG_RATE_1M,
+ [IWL_RATE_2M_INDEX] = SCAN_CONFIG_RATE_2M,
+ [IWL_RATE_5M_INDEX] = SCAN_CONFIG_RATE_5M,
+ [IWL_RATE_11M_INDEX] = SCAN_CONFIG_RATE_11M,
+ [IWL_RATE_6M_INDEX] = SCAN_CONFIG_RATE_6M,
+ [IWL_RATE_9M_INDEX] = SCAN_CONFIG_RATE_9M,
+ [IWL_RATE_12M_INDEX] = SCAN_CONFIG_RATE_12M,
+ [IWL_RATE_18M_INDEX] = SCAN_CONFIG_RATE_18M,
+ [IWL_RATE_24M_INDEX] = SCAN_CONFIG_RATE_24M,
+ [IWL_RATE_36M_INDEX] = SCAN_CONFIG_RATE_36M,
+ [IWL_RATE_48M_INDEX] = SCAN_CONFIG_RATE_48M,
+ [IWL_RATE_54M_INDEX] = SCAN_CONFIG_RATE_54M,
+ };
+
+ return rate_to_scan_rate[rate];
+}
+
+static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm)
+{
+ struct ieee80211_supported_band *band;
+ unsigned int rates = 0;
+ int i;
+
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+ for (i = 0; i < band->n_bitrates; i++)
+ rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+ for (i = 0; i < band->n_bitrates; i++)
+ rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+
+ /* Set both basic rates and supported rates */
+ rates |= SCAN_CONFIG_SUPPORTED_RATE(rates);
+
+ return cpu_to_le32(rates);
+}
+
+int iwl_mvm_config_scan(struct iwl_mvm *mvm)
+{
+ struct iwl_scan_config *scan_config;
+ struct ieee80211_supported_band *band;
+ int num_channels =
+ mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
+ mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+ int ret, i, j = 0, cmd_size;
+ struct iwl_host_cmd cmd = {
+ .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0),
+ };
+ enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false);
+
+ if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels))
+ return -ENOBUFS;
+
+ if (type == mvm->scan_type)
+ return 0;
+
+ cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels;
+
+ scan_config = kzalloc(cmd_size, GFP_KERNEL);
+ if (!scan_config)
+ return -ENOMEM;
+
+ scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE |
+ SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
+ SCAN_CONFIG_FLAG_SET_TX_CHAINS |
+ SCAN_CONFIG_FLAG_SET_RX_CHAINS |
+ SCAN_CONFIG_FLAG_SET_ALL_TIMES |
+ SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
+ SCAN_CONFIG_FLAG_SET_MAC_ADDR |
+ SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
+ SCAN_CONFIG_N_CHANNELS(num_channels) |
+ (type == IWL_SCAN_TYPE_FRAGMENTED ?
+ SCAN_CONFIG_FLAG_SET_FRAGMENTED :
+ SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED));
+ scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
+ scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
+ scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm);
+ scan_config->out_of_channel_time =
+ cpu_to_le32(scan_timing[type].max_out_time);
+ scan_config->suspend_time = cpu_to_le32(scan_timing[type].suspend_time);
+ scan_config->dwell_active = scan_timing[type].dwell_active;
+ scan_config->dwell_passive = scan_timing[type].dwell_passive;
+ scan_config->dwell_fragmented = scan_timing[type].dwell_fragmented;
+ scan_config->dwell_extended = scan_timing[type].dwell_extended;
+
+ memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN);
+
+ scan_config->bcast_sta_id = mvm->aux_sta.sta_id;
+ scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS |
+ IWL_CHANNEL_FLAG_ACCURATE_EBS |
+ IWL_CHANNEL_FLAG_EBS_ADD |
+ IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
+
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+ for (i = 0; i < band->n_channels; i++, j++)
+ scan_config->channel_array[j] = band->channels[i].hw_value;
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+ for (i = 0; i < band->n_channels; i++, j++)
+ scan_config->channel_array[j] = band->channels[i].hw_value;
+
+ cmd.data[0] = scan_config;
+ cmd.len[0] = cmd_size;
+ cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+
+ IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n");
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (!ret)
+ mvm->scan_type = type;
+
+ kfree(scan_config);
+ return ret;
+}
+
+static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status)
+{
+ int i;
+
+ for (i = 0; i < mvm->max_scans; i++)
+ if (mvm->scan_uid_status[i] == status)
+ return i;
+
+ return -ENOENT;
+}
+
+static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
+ struct iwl_scan_req_umac *cmd,
+ struct iwl_mvm_scan_params *params)
+{
+ cmd->extended_dwell = scan_timing[params->type].dwell_extended;
+ cmd->active_dwell = scan_timing[params->type].dwell_active;
+ cmd->passive_dwell = scan_timing[params->type].dwell_passive;
+ cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented;
+ cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time);
+ cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time);
+ cmd->scan_priority =
+ iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
+
+ if (iwl_mvm_is_regular_scan(params))
+ cmd->ooc_priority =
+ iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
+ else
+ cmd->ooc_priority =
+ iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_2);
+}
+
+static void
+iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,
+ struct ieee80211_channel **channels,
+ int n_channels, u32 ssid_bitmap,
+ struct iwl_scan_req_umac *cmd)
+{
+ struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data;
+ int i;
+
+ for (i = 0; i < n_channels; i++) {
+ channel_cfg[i].flags = cpu_to_le32(ssid_bitmap);
+ channel_cfg[i].channel_num = channels[i]->hw_value;
+ channel_cfg[i].iter_count = 1;
+ channel_cfg[i].iter_interval = 0;
+ }
+}
+
+static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
+ struct iwl_mvm_scan_params *params,
+ struct ieee80211_vif *vif)
+{
+ int flags = 0;
+
+ if (params->n_ssids == 0)
+ flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE;
+
+ if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT;
+
+ if (params->type == IWL_SCAN_TYPE_FRAGMENTED)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED;
+
+ if (iwl_mvm_rrm_scan_needed(mvm))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED;
+
+ if (params->pass_all)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
+ else
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH;
+
+ if (!iwl_mvm_is_regular_scan(params))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvm->scan_iter_notif_enabled)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE;
+#endif
+
+ if (iwl_mvm_is_regular_scan(params) &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ params->type != IWL_SCAN_TYPE_FRAGMENTED)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL;
+
+ return flags;
+}
+
+static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct iwl_mvm_scan_params *params,
+ int type)
+{
+ struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+ struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
+ sizeof(struct iwl_scan_channel_cfg_umac) *
+ mvm->fw->ucode_capa.n_scan_channels;
+ int uid, i;
+ u32 ssid_bitmap = 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
+ return -EINVAL;
+
+ uid = iwl_mvm_scan_uid_by_status(mvm, 0);
+ if (uid < 0)
+ return uid;
+
+ memset(cmd, 0, ksize(cmd));
+
+ iwl_mvm_scan_umac_dwell(mvm, cmd, params);
+
+ mvm->scan_uid_status[uid] = type;
+
+ cmd->uid = cpu_to_le32(uid);
+ cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params,
+ vif));
+
+ if (type == IWL_MVM_SCAN_SCHED)
+ cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE);
+
+ if (iwl_mvm_scan_use_ebs(mvm, vif))
+ cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS |
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
+
+ cmd->n_channels = params->n_channels;
+
+ iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap);
+
+ iwl_mvm_umac_scan_cfg_channels(mvm, params->channels,
+ params->n_channels, ssid_bitmap, cmd);
+
+ for (i = 0; i < params->n_scan_plans; i++) {
+ struct cfg80211_sched_scan_plan *scan_plan =
+ &params->scan_plans[i];
+
+ sec_part->schedule[i].iter_count = scan_plan->iterations;
+ sec_part->schedule[i].interval =
+ cpu_to_le16(scan_plan->interval);
+ }
+
+ /*
+ * If the number of iterations of the last scan plan is set to
+ * zero, it should run infinitely. However, this is not always the case.
+ * For example, when regular scan is requested the driver sets one scan
+ * plan with one iteration.
+ */
+ if (!sec_part->schedule[i - 1].iter_count)
+ sec_part->schedule[i - 1].iter_count = 0xff;
+
+ sec_part->delay = cpu_to_le16(params->delay);
+ sec_part->preq = params->preq;
+
+ return 0;
+}
+
+static int iwl_mvm_num_scans(struct iwl_mvm *mvm)
+{
+ return hweight32(mvm->scan_status & IWL_MVM_SCAN_MASK);
+}
+
+static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type)
+{
+ /* This looks a bit arbitrary, but the idea is that if we run
+ * out of possible simultaneous scans and the userspace is
+ * trying to run a scan type that is already running, we
+ * return -EBUSY. But if the userspace wants to start a
+ * different type of scan, we stop the opposite type to make
+ * space for the new request. The reason is backwards
+ * compatibility with old wpa_supplicant that wouldn't stop a
+ * scheduled scan before starting a normal scan.
+ */
+
+ if (iwl_mvm_num_scans(mvm) < mvm->max_scans)
+ return 0;
+
+ /* Use a switch, even though this is a bitmask, so that more
+ * than one bits set will fall in default and we will warn.
+ */
+ switch (type) {
+ case IWL_MVM_SCAN_REGULAR:
+ if (mvm->scan_status & IWL_MVM_SCAN_REGULAR_MASK)
+ return -EBUSY;
+ return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);
+ case IWL_MVM_SCAN_SCHED:
+ if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK)
+ return -EBUSY;
+ return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
+ case IWL_MVM_SCAN_NETDETECT:
+ /* No need to stop anything for net-detect since the
+ * firmware is restarted anyway. This way, any sched
+ * scans that were running will be restarted when we
+ * resume.
+ */
+ return 0;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return -EIO;
+}
+
+int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ struct iwl_host_cmd hcmd = {
+ .len = { iwl_mvm_scan_size(mvm), },
+ .data = { mvm->scan_cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_mvm_scan_params params = {};
+ int ret;
+ struct cfg80211_sched_scan_plan scan_plan = { .iterations = 1 };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+ IWL_ERR(mvm, "scan while LAR regdomain is not set\n");
+ return -EBUSY;
+ }
+
+ ret = iwl_mvm_check_running_scans(mvm, IWL_MVM_SCAN_REGULAR);
+ if (ret)
+ return ret;
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(!mvm->scan_cmd))
+ return -ENOMEM;
+
+ if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels))
+ return -ENOBUFS;
+
+ params.n_ssids = req->n_ssids;
+ params.flags = req->flags;
+ params.n_channels = req->n_channels;
+ params.delay = 0;
+ params.ssids = req->ssids;
+ params.channels = req->channels;
+ params.mac_addr = req->mac_addr;
+ params.mac_addr_mask = req->mac_addr_mask;
+ params.no_cck = req->no_cck;
+ params.pass_all = true;
+ params.n_match_sets = 0;
+ params.match_sets = NULL;
+
+ params.scan_plans = &scan_plan;
+ params.n_scan_plans = 1;
+
+ params.type =
+ iwl_mvm_get_scan_type(mvm,
+ vif->type == NL80211_IFTYPE_P2P_DEVICE);
+
+ iwl_mvm_build_scan_probe(mvm, vif, ies, &params);
+
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0);
+ ret = iwl_mvm_scan_umac(mvm, vif, &params,
+ IWL_MVM_SCAN_REGULAR);
+ } else {
+ hcmd.id = SCAN_OFFLOAD_REQUEST_CMD;
+ ret = iwl_mvm_scan_lmac(mvm, vif, &params);
+ }
+
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
+ if (ret) {
+ /* If the scan failed, it usually means that the FW was unable
+ * to allocate the time events. Warn on it, but maybe we
+ * should try to send the command again with different params.
+ */
+ IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+ return ret;
+ }
+
+ IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
+ mvm->scan_status |= IWL_MVM_SCAN_REGULAR;
+ iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
+
+ return 0;
+}
+
+int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies,
+ int type)
+{
+ struct iwl_host_cmd hcmd = {
+ .len = { iwl_mvm_scan_size(mvm), },
+ .data = { mvm->scan_cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_mvm_scan_params params = {};
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+ IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n");
+ return -EBUSY;
+ }
+
+ ret = iwl_mvm_check_running_scans(mvm, type);
+ if (ret)
+ return ret;
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(!mvm->scan_cmd))
+ return -ENOMEM;
+
+ if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels))
+ return -ENOBUFS;
+
+ params.n_ssids = req->n_ssids;
+ params.flags = req->flags;
+ params.n_channels = req->n_channels;
+ params.ssids = req->ssids;
+ params.channels = req->channels;
+ params.mac_addr = req->mac_addr;
+ params.mac_addr_mask = req->mac_addr_mask;
+ params.no_cck = false;
+ params.pass_all = iwl_mvm_scan_pass_all(mvm, req);
+ params.n_match_sets = req->n_match_sets;
+ params.match_sets = req->match_sets;
+ if (!req->n_scan_plans)
+ return -EINVAL;
+
+ params.n_scan_plans = req->n_scan_plans;
+ params.scan_plans = req->scan_plans;
+
+ params.type =
+ iwl_mvm_get_scan_type(mvm,
+ vif->type == NL80211_IFTYPE_P2P_DEVICE);
+
+ /* In theory, LMAC scans can handle a 32-bit delay, but since
+ * waiting for over 18 hours to start the scan is a bit silly
+ * and to keep it aligned with UMAC scans (which only support
+ * 16-bit delays), trim it down to 16-bits.
+ */
+ if (req->delay > U16_MAX) {
+ IWL_DEBUG_SCAN(mvm,
+ "delay value is > 16-bits, set to max possible\n");
+ params.delay = U16_MAX;
+ } else {
+ params.delay = req->delay;
+ }
+
+ ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+ if (ret)
+ return ret;
+
+ iwl_mvm_build_scan_probe(mvm, vif, ies, &params);
+
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0);
+ ret = iwl_mvm_scan_umac(mvm, vif, &params, IWL_MVM_SCAN_SCHED);
+ } else {
+ hcmd.id = SCAN_OFFLOAD_REQUEST_CMD;
+ ret = iwl_mvm_scan_lmac(mvm, vif, &params);
+ }
+
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
+ if (!ret) {
+ IWL_DEBUG_SCAN(mvm,
+ "Sched scan request was sent successfully\n");
+ mvm->scan_status |= type;
+ } else {
+ /* If the scan failed, it usually means that the FW was unable
+ * to allocate the time events. Warn on it, but maybe we
+ * should try to send the command again with different params.
+ */
+ IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret);
+ }
+
+ return ret;
+}
+
+void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+ u32 uid = __le32_to_cpu(notif->uid);
+ bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED);
+
+ if (WARN_ON(!(mvm->scan_uid_status[uid] & mvm->scan_status)))
+ return;
+
+ /* if the scan is already stopping, we don't need to notify mac80211 */
+ if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_REGULAR) {
+ ieee80211_scan_completed(mvm->hw, aborted);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) {
+ ieee80211_sched_scan_stopped(mvm->hw);
+ }
+
+ mvm->scan_status &= ~mvm->scan_uid_status[uid];
+ IWL_DEBUG_SCAN(mvm,
+ "Scan completed, uid %u type %u, status %s, EBS status %s\n",
+ uid, mvm->scan_uid_status[uid],
+ notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
+ "completed" : "aborted",
+ iwl_mvm_ebs_status_str(notif->ebs_status));
+ IWL_DEBUG_SCAN(mvm,
+ "Last line %d, Last iteration %d, Time from last iteration %d\n",
+ notif->last_schedule, notif->last_iter,
+ __le32_to_cpu(notif->time_from_last_iter));
+
+ if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS &&
+ notif->ebs_status != IWL_SCAN_EBS_INACTIVE)
+ mvm->last_ebs_successful = false;
+
+ mvm->scan_uid_status[uid] = 0;
+}
+
+void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data;
+ u8 buf[256];
+
+ IWL_DEBUG_SCAN(mvm,
+ "UMAC Scan iteration complete: status=0x%x scanned_channels=%d channels list: %s\n",
+ notif->status, notif->scanned_channels,
+ iwl_mvm_dump_channel_list(notif->results,
+ notif->scanned_channels, buf,
+ sizeof(buf)));
+}
+
+static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type)
+{
+ struct iwl_umac_scan_abort cmd = {};
+ int uid, ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* We should always get a valid index here, because we already
+ * checked that this type of scan was running in the generic
+ * code.
+ */
+ uid = iwl_mvm_scan_uid_by_status(mvm, type);
+ if (WARN_ON_ONCE(uid < 0))
+ return uid;
+
+ cmd.uid = cpu_to_le32(uid);
+
+ IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ iwl_cmd_id(SCAN_ABORT_UMAC,
+ IWL_ALWAYS_LONG_GROUP, 0),
+ 0, sizeof(cmd), &cmd);
+ if (!ret)
+ mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT;
+
+ return ret;
+}
+
+static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type)
+{
+ struct iwl_notification_wait wait_scan_done;
+ static const u16 scan_done_notif[] = { SCAN_COMPLETE_UMAC,
+ SCAN_OFFLOAD_COMPLETE, };
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
+ scan_done_notif,
+ ARRAY_SIZE(scan_done_notif),
+ NULL, NULL);
+
+ IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type);
+
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ ret = iwl_mvm_umac_scan_abort(mvm, type);
+ else
+ ret = iwl_mvm_lmac_scan_abort(mvm);
+
+ if (ret) {
+ IWL_DEBUG_SCAN(mvm, "couldn't stop scan type %d\n", type);
+ iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
+ return ret;
+ }
+
+ ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
+
+ return ret;
+}
+
+int iwl_mvm_scan_size(struct iwl_mvm *mvm)
+{
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ return sizeof(struct iwl_scan_req_umac) +
+ sizeof(struct iwl_scan_channel_cfg_umac) *
+ mvm->fw->ucode_capa.n_scan_channels +
+ sizeof(struct iwl_scan_req_umac_tail);
+
+ return sizeof(struct iwl_scan_req_lmac) +
+ sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels +
+ sizeof(struct iwl_scan_probe_req);
+}
+
+/*
+ * This function is used in nic restart flow, to inform mac80211 about scans
+ * that was aborted by restart flow or by an assert.
+ */
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm)
+{
+ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ int uid, i;
+
+ uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_REGULAR);
+ if (uid >= 0) {
+ ieee80211_scan_completed(mvm->hw, true);
+ mvm->scan_uid_status[uid] = 0;
+ }
+ uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED);
+ if (uid >= 0 && !mvm->restart_fw) {
+ ieee80211_sched_scan_stopped(mvm->hw);
+ mvm->scan_uid_status[uid] = 0;
+ }
+
+ /* We shouldn't have any UIDs still set. Loop over all the
+ * UIDs to make sure there's nothing left there and warn if
+ * any is found.
+ */
+ for (i = 0; i < mvm->max_scans; i++) {
+ if (WARN_ONCE(mvm->scan_uid_status[i],
+ "UMAC scan UID %d status was not cleaned\n",
+ i))
+ mvm->scan_uid_status[i] = 0;
+ }
+ } else {
+ if (mvm->scan_status & IWL_MVM_SCAN_REGULAR)
+ ieee80211_scan_completed(mvm->hw, true);
+
+ /* Sched scan will be restarted by mac80211 in
+ * restart_hw, so do not report if FW is about to be
+ * restarted.
+ */
+ if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && !mvm->restart_fw)
+ ieee80211_sched_scan_stopped(mvm->hw);
+ }
+}
+
+int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify)
+{
+ int ret;
+
+ if (!(mvm->scan_status & type))
+ return 0;
+
+ if (iwl_mvm_is_radio_killed(mvm)) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = iwl_mvm_scan_stop_wait(mvm, type);
+ if (!ret)
+ mvm->scan_status |= type << IWL_MVM_SCAN_STOPPING_SHIFT;
+out:
+ /* Clear the scan status so the next scan requests will
+ * succeed and mark the scan as stopping, so that the Rx
+ * handler doesn't do anything, as the scan was stopped from
+ * above.
+ */
+ mvm->scan_status &= ~type;
+
+ if (type == IWL_MVM_SCAN_REGULAR) {
+ /* Since the rx handler won't do anything now, we have
+ * to release the scan reference here.
+ */
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ if (notify)
+ ieee80211_scan_completed(mvm->hw, true);
+ } else if (notify) {
+ ieee80211_sched_scan_stopped(mvm->hw);
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c
new file mode 100644
index 000000000000..c2def1232a8c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c
@@ -0,0 +1,340 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "mvm.h"
+
+/* For counting bound interfaces */
+struct iwl_mvm_active_iface_iterator_data {
+ struct ieee80211_vif *ignore_vif;
+ u8 sta_vif_ap_sta_id;
+ enum iwl_sf_state sta_vif_state;
+ int num_active_macs;
+};
+
+/*
+ * Count bound interfaces which are not p2p, besides data->ignore_vif.
+ * data->station_vif will point to one bound vif of type station, if exists.
+ */
+static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_active_iface_iterator_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
+ vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ return;
+
+ data->num_active_macs++;
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
+ if (vif->bss_conf.assoc)
+ data->sta_vif_state = SF_FULL_ON;
+ else
+ data->sta_vif_state = SF_INIT_OFF;
+ }
+}
+
+/*
+ * Aging and idle timeouts for the different possible scenarios
+ * in default configuration
+ */
+static const
+__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
+ {
+ cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF),
+ cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF),
+ cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_MCAST_AGING_TIMER_DEF),
+ cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_BA_AGING_TIMER_DEF),
+ cpu_to_le32(SF_BA_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF),
+ cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF)
+ },
+};
+
+/*
+ * Aging and idle timeouts for the different possible scenarios
+ * in single BSS MAC configuration.
+ */
+static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
+ {
+ cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
+ cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
+ },
+ {
+ cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
+ cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
+ },
+ {
+ cpu_to_le32(SF_MCAST_AGING_TIMER),
+ cpu_to_le32(SF_MCAST_IDLE_TIMER)
+ },
+ {
+ cpu_to_le32(SF_BA_AGING_TIMER),
+ cpu_to_le32(SF_BA_IDLE_TIMER)
+ },
+ {
+ cpu_to_le32(SF_TX_RE_AGING_TIMER),
+ cpu_to_le32(SF_TX_RE_IDLE_TIMER)
+ },
+};
+
+static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,
+ struct iwl_sf_cfg_cmd *sf_cmd,
+ struct ieee80211_sta *sta)
+{
+ int i, j, watermark;
+
+ sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
+
+ /*
+ * If we are in association flow - check antenna configuration
+ * capabilities of the AP station, and choose the watermark accordingly.
+ */
+ if (sta) {
+ if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
+ switch (sta->rx_nss) {
+ case 1:
+ watermark = SF_W_MARK_SISO;
+ break;
+ case 2:
+ watermark = SF_W_MARK_MIMO2;
+ break;
+ default:
+ watermark = SF_W_MARK_MIMO3;
+ break;
+ }
+ } else {
+ watermark = SF_W_MARK_LEGACY;
+ }
+ /* default watermark value for unassociated mode. */
+ } else {
+ watermark = SF_W_MARK_MIMO2;
+ }
+ sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
+
+ for (i = 0; i < SF_NUM_SCENARIO; i++) {
+ for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
+ sf_cmd->long_delay_timeouts[i][j] =
+ cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
+ }
+ }
+
+ if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) {
+ BUILD_BUG_ON(sizeof(sf_full_timeout) !=
+ sizeof(__le32) * SF_NUM_SCENARIO *
+ SF_NUM_TIMEOUT_TYPES);
+
+ memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
+ sizeof(sf_full_timeout));
+ } else {
+ BUILD_BUG_ON(sizeof(sf_full_timeout_def) !=
+ sizeof(__le32) * SF_NUM_SCENARIO *
+ SF_NUM_TIMEOUT_TYPES);
+
+ memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
+ sizeof(sf_full_timeout_def));
+ }
+
+}
+
+static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
+ enum iwl_sf_state new_state)
+{
+ struct iwl_sf_cfg_cmd sf_cmd = {
+ .state = cpu_to_le32(SF_FULL_ON),
+ };
+ struct ieee80211_sta *sta;
+ int ret = 0;
+
+ if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13)
+ sf_cmd.state = cpu_to_le32(new_state);
+
+ if (mvm->cfg->disable_dummy_notification)
+ sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF);
+
+ /*
+ * If an associated AP sta changed its antenna configuration, the state
+ * will remain FULL_ON but SF parameters need to be reconsidered.
+ */
+ if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
+ return 0;
+
+ switch (new_state) {
+ case SF_UNINIT:
+ if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13)
+ iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
+ break;
+ case SF_FULL_ON:
+ if (sta_id == IWL_MVM_STATION_COUNT) {
+ IWL_ERR(mvm,
+ "No station: Cannot switch SF to FULL_ON\n");
+ return -EINVAL;
+ }
+ rcu_read_lock();
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+ if (IS_ERR_OR_NULL(sta)) {
+ IWL_ERR(mvm, "Invalid station id\n");
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta);
+ rcu_read_unlock();
+ break;
+ case SF_INIT_OFF:
+ iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
+ break;
+ default:
+ WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
+ new_state);
+ return -EINVAL;
+ }
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
+ sizeof(sf_cmd), &sf_cmd);
+ if (!ret)
+ mvm->sf_state = new_state;
+
+ return ret;
+}
+
+/*
+ * Update Smart fifo:
+ * Count bound interfaces that are not to be removed, ignoring p2p devices,
+ * and set new state accordingly.
+ */
+int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
+ bool remove_vif)
+{
+ enum iwl_sf_state new_state;
+ u8 sta_id = IWL_MVM_STATION_COUNT;
+ struct iwl_mvm_vif *mvmvif = NULL;
+ struct iwl_mvm_active_iface_iterator_data data = {
+ .ignore_vif = changed_vif,
+ .sta_vif_state = SF_UNINIT,
+ .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
+ };
+
+ /*
+ * Ignore the call if we are in HW Restart flow, or if the handled
+ * vif is a p2p device.
+ */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
+ (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
+ return 0;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bound_iface_iterator,
+ &data);
+
+ /* If changed_vif exists and is not to be removed, add to the count */
+ if (changed_vif && !remove_vif)
+ data.num_active_macs++;
+
+ switch (data.num_active_macs) {
+ case 0:
+ /* If there are no active macs - change state to SF_INIT_OFF */
+ new_state = SF_INIT_OFF;
+ break;
+ case 1:
+ if (remove_vif) {
+ /* The one active mac left is of type station
+ * and we filled the relevant data during iteration
+ */
+ new_state = data.sta_vif_state;
+ sta_id = data.sta_vif_ap_sta_id;
+ } else {
+ if (WARN_ON(!changed_vif))
+ return -EINVAL;
+ if (changed_vif->type != NL80211_IFTYPE_STATION) {
+ new_state = SF_UNINIT;
+ } else if (changed_vif->bss_conf.assoc &&
+ changed_vif->bss_conf.dtim_period) {
+ mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
+ sta_id = mvmvif->ap_sta_id;
+ new_state = SF_FULL_ON;
+ } else {
+ new_state = SF_INIT_OFF;
+ }
+ }
+ break;
+ default:
+ /* If there are multiple active macs - change to SF_UNINIT */
+ new_state = SF_UNINIT;
+ }
+ return iwl_mvm_sf_config(mvm, sta_id, new_state);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
new file mode 100644
index 000000000000..b556e33658d7
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -0,0 +1,1841 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "mvm.h"
+#include "sta.h"
+#include "rs.h"
+
+static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,
+ enum nl80211_iftype iftype)
+{
+ int sta_id;
+ u32 reserved_ids = 0;
+
+ BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32);
+ WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */
+ if (iftype != NL80211_IFTYPE_STATION)
+ reserved_ids = BIT(0);
+
+ /* Don't take rcu_read_lock() since we are protected by mvm->mutex */
+ for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) {
+ if (BIT(sta_id) & reserved_ids)
+ continue;
+
+ if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex)))
+ return sta_id;
+ }
+ return IWL_MVM_STATION_COUNT;
+}
+
+/* send station add/update command to firmware */
+int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ bool update)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_add_sta_cmd add_sta_cmd = {
+ .sta_id = mvm_sta->sta_id,
+ .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color),
+ .add_modify = update ? 1 : 0,
+ .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK |
+ STA_FLG_MIMO_EN_MSK),
+ .tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg),
+ };
+ int ret;
+ u32 status;
+ u32 agg_size = 0, mpdu_dens = 0;
+
+ if (!update) {
+ add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
+ memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN);
+ }
+
+ switch (sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ);
+ /* fall through */
+ case IEEE80211_STA_RX_BW_80:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ);
+ /* fall through */
+ case IEEE80211_STA_RX_BW_40:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ);
+ /* fall through */
+ case IEEE80211_STA_RX_BW_20:
+ if (sta->ht_cap.ht_supported)
+ add_sta_cmd.station_flags |=
+ cpu_to_le32(STA_FLG_FAT_EN_20MHZ);
+ break;
+ }
+
+ switch (sta->rx_nss) {
+ case 1:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
+ break;
+ case 2:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2);
+ break;
+ case 3 ... 8:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3);
+ break;
+ }
+
+ switch (sta->smps_mode) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_NUM_MODES:
+ WARN_ON(1);
+ break;
+ case IEEE80211_SMPS_STATIC:
+ /* override NSS */
+ add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK);
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT);
+ break;
+ case IEEE80211_SMPS_OFF:
+ /* nothing */
+ break;
+ }
+
+ if (sta->ht_cap.ht_supported) {
+ add_sta_cmd.station_flags_msk |=
+ cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK |
+ STA_FLG_AGG_MPDU_DENS_MSK);
+
+ mpdu_dens = sta->ht_cap.ampdu_density;
+ }
+
+ if (sta->vht_cap.vht_supported) {
+ agg_size = sta->vht_cap.cap &
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ agg_size >>=
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+ } else if (sta->ht_cap.ht_supported) {
+ agg_size = sta->ht_cap.ampdu_factor;
+ }
+
+ add_sta_cmd.station_flags |=
+ cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT);
+ add_sta_cmd.station_flags |=
+ cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT);
+
+ status = ADD_STA_SUCCESS;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
+ &add_sta_cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n");
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "ADD_STA failed\n");
+ break;
+ }
+
+ return ret;
+}
+
+static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ unsigned long used_hw_queues;
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ unsigned int wdg_timeout =
+ iwl_mvm_get_wd_timeout(mvm, NULL, true, false);
+ u32 ac;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL);
+
+ /* Find available queues, and allocate them to the ACs */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ u8 queue = find_first_zero_bit(&used_hw_queues,
+ mvm->first_agg_queue);
+
+ if (queue >= mvm->first_agg_queue) {
+ IWL_ERR(mvm, "Failed to allocate STA queue\n");
+ return -EBUSY;
+ }
+
+ __set_bit(queue, &used_hw_queues);
+ mvmsta->hw_queue[ac] = queue;
+ }
+
+ /* Found a place for all queues - enable them */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac],
+ mvmsta->hw_queue[ac],
+ iwl_mvm_ac_to_tx_fifo[ac], 0,
+ wdg_timeout);
+ mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]);
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ unsigned long sta_msk;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* disable the TDLS STA-specific queues */
+ sta_msk = mvmsta->tfd_queue_msk;
+ for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE)
+ iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0);
+}
+
+int iwl_mvm_add_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ int i, ret, sta_id;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ sta_id = iwl_mvm_find_free_sta_id(mvm,
+ ieee80211_vif_type_p2p(vif));
+ else
+ sta_id = mvm_sta->sta_id;
+
+ if (sta_id == IWL_MVM_STATION_COUNT)
+ return -ENOSPC;
+
+ spin_lock_init(&mvm_sta->lock);
+
+ mvm_sta->sta_id = sta_id;
+ mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color);
+ mvm_sta->vif = vif;
+ mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+ mvm_sta->tx_protection = 0;
+ mvm_sta->tt_tx_protection = false;
+
+ /* HW restart, don't assume the memory has been zeroed */
+ atomic_set(&mvm->pending_frames[sta_id], 0);
+ mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */
+ mvm_sta->tfd_queue_msk = 0;
+
+ /* allocate new queues for a TDLS station */
+ if (sta->tdls) {
+ ret = iwl_mvm_tdls_sta_init(mvm, sta);
+ if (ret)
+ return ret;
+ } else {
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+ mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+ }
+
+ /* for HW restart - reset everything but the sequence number */
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ u16 seq = mvm_sta->tid_data[i].seq_number;
+ memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i]));
+ mvm_sta->tid_data[i].seq_number = seq;
+ }
+ mvm_sta->agg_tids = 0;
+
+ ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
+ if (ret)
+ goto err;
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (!sta->tdls) {
+ WARN_ON(mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT);
+ mvmvif->ap_sta_id = sta_id;
+ } else {
+ WARN_ON(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT);
+ }
+ }
+
+ rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
+
+ return 0;
+
+err:
+ iwl_mvm_tdls_sta_deinit(mvm, sta);
+ return ret;
+}
+
+int iwl_mvm_update_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ return iwl_mvm_sta_send_to_fw(mvm, sta, true);
+}
+
+int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+ bool drain)
+{
+ struct iwl_mvm_add_sta_cmd cmd = {};
+ int ret;
+ u32 status;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color);
+ cmd.sta_id = mvmsta->sta_id;
+ cmd.add_modify = STA_MODE_MODIFY;
+ cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0;
+ cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW);
+
+ status = ADD_STA_SUCCESS;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+ &cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n",
+ mvmsta->sta_id);
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "Couldn't drain frames for staid %d\n",
+ mvmsta->sta_id);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Remove a station from the FW table. Before sending the command to remove
+ * the station validate that the station is indeed known to the driver (sanity
+ * only).
+ */
+static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_rm_sta_cmd rm_sta_cmd = {
+ .sta_id = sta_id,
+ };
+ int ret;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ /* Note: internal stations are marked as error values */
+ if (!sta) {
+ IWL_ERR(mvm, "Invalid station id\n");
+ return -EINVAL;
+ }
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0,
+ sizeof(rm_sta_cmd), &rm_sta_cmd);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
+ return ret;
+ }
+
+ return 0;
+}
+
+void iwl_mvm_sta_drained_wk(struct work_struct *wk)
+{
+ struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk);
+ u8 sta_id;
+
+ /*
+ * The mutex is needed because of the SYNC cmd, but not only: if the
+ * work would run concurrently with iwl_mvm_rm_sta, it would run before
+ * iwl_mvm_rm_sta sets the station as busy, and exit. Then
+ * iwl_mvm_rm_sta would set the station as busy, and nobody will clean
+ * that later.
+ */
+ mutex_lock(&mvm->mutex);
+
+ for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) {
+ int ret;
+ struct ieee80211_sta *sta =
+ rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ /*
+ * This station is in use or RCU-removed; the latter happens in
+ * managed mode, where mac80211 removes the station before we
+ * can remove it from firmware (we can only do that after the
+ * MAC is marked unassociated), and possibly while the deauth
+ * frame to disconnect from the AP is still queued. Then, the
+ * station pointer is -ENOENT when the last skb is reclaimed.
+ */
+ if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT)
+ continue;
+
+ if (PTR_ERR(sta) == -EINVAL) {
+ IWL_ERR(mvm, "Drained sta %d, but it is internal?\n",
+ sta_id);
+ continue;
+ }
+
+ if (!sta) {
+ IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n",
+ sta_id);
+ continue;
+ }
+
+ WARN_ON(PTR_ERR(sta) != -EBUSY);
+ /* This station was removed and we waited until it got drained,
+ * we can now proceed and remove it.
+ */
+ ret = iwl_mvm_rm_sta_common(mvm, sta_id);
+ if (ret) {
+ IWL_ERR(mvm,
+ "Couldn't remove sta %d after it was drained\n",
+ sta_id);
+ continue;
+ }
+ RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
+ clear_bit(sta_id, mvm->sta_drained);
+
+ if (mvm->tfd_drained[sta_id]) {
+ unsigned long i, msk = mvm->tfd_drained[sta_id];
+
+ for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE)
+ iwl_mvm_disable_txq(mvm, i, i,
+ IWL_MAX_TID_COUNT, 0);
+
+ mvm->tfd_drained[sta_id] = 0;
+ IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n",
+ sta_id, msk);
+ }
+ }
+
+ mutex_unlock(&mvm->mutex);
+}
+
+int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ mvmvif->ap_sta_id == mvm_sta->sta_id) {
+ ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+ if (ret)
+ return ret;
+ /* flush its queues here since we are freeing mvm_sta */
+ ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0);
+ if (ret)
+ return ret;
+ ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
+ mvm_sta->tfd_queue_msk);
+ if (ret)
+ return ret;
+ ret = iwl_mvm_drain_sta(mvm, mvm_sta, false);
+
+ /* if we are associated - we can't remove the AP STA now */
+ if (vif->bss_conf.assoc)
+ return ret;
+
+ /* unassoc - go ahead - remove the AP STA now */
+ mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+ /* clear d0i3_ap_sta_id if no longer relevant */
+ if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+ }
+
+ /*
+ * This shouldn't happen - the TDLS channel switch should be canceled
+ * before the STA is removed.
+ */
+ if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) {
+ mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+ cancel_delayed_work(&mvm->tdls_cs.dwork);
+ }
+
+ /*
+ * Make sure that the tx response code sees the station as -EBUSY and
+ * calls the drain worker.
+ */
+ spin_lock_bh(&mvm_sta->lock);
+ /*
+ * There are frames pending on the AC queues for this station.
+ * We need to wait until all the frames are drained...
+ */
+ if (atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) {
+ rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
+ ERR_PTR(-EBUSY));
+ spin_unlock_bh(&mvm_sta->lock);
+
+ /* disable TDLS sta queues on drain complete */
+ if (sta->tdls) {
+ mvm->tfd_drained[mvm_sta->sta_id] =
+ mvm_sta->tfd_queue_msk;
+ IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n",
+ mvm_sta->sta_id);
+ }
+
+ ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+ } else {
+ spin_unlock_bh(&mvm_sta->lock);
+
+ if (sta->tdls)
+ iwl_mvm_tdls_sta_deinit(mvm, sta);
+
+ ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
+ RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
+ }
+
+ return ret;
+}
+
+int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u8 sta_id)
+{
+ int ret = iwl_mvm_rm_sta_common(mvm, sta_id);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
+ return ret;
+}
+
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,
+ struct iwl_mvm_int_sta *sta,
+ u32 qmask, enum nl80211_iftype iftype)
+{
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
+ if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
+ return -ENOSPC;
+ }
+
+ sta->tfd_queue_msk = qmask;
+
+ /* put a non-NULL value so iterating over the stations won't stop */
+ rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL));
+ return 0;
+}
+
+static void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
+ struct iwl_mvm_int_sta *sta)
+{
+ RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
+ memset(sta, 0, sizeof(struct iwl_mvm_int_sta));
+ sta->sta_id = IWL_MVM_STATION_COUNT;
+}
+
+static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
+ struct iwl_mvm_int_sta *sta,
+ const u8 *addr,
+ u16 mac_id, u16 color)
+{
+ struct iwl_mvm_add_sta_cmd cmd;
+ int ret;
+ u32 status;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.sta_id = sta->sta_id;
+ cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
+ color));
+
+ cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk);
+ cmd.tid_disable_tx = cpu_to_le16(0xffff);
+
+ if (addr)
+ memcpy(cmd.addr, addr, ETH_ALEN);
+
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+ &cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ IWL_DEBUG_INFO(mvm, "Internal station added.\n");
+ return 0;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "Add internal station failed, status=0x%x\n",
+ status);
+ break;
+ }
+ return ret;
+}
+
+int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
+{
+ unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
+ mvm->cfg->base_params->wd_timeout :
+ IWL_WATCHDOG_DISABLED;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Map Aux queue to fifo - needs to happen before adding Aux station */
+ iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue,
+ IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout);
+
+ /* Allocate aux station and assign to it the aux queue */
+ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue),
+ NL80211_IFTYPE_UNSPECIFIED);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL,
+ MAC_INDEX_AUX, 0);
+
+ if (ret)
+ iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
+ return ret;
+}
+
+int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ lockdep_assert_held(&mvm->mutex);
+ return iwl_mvm_add_int_sta_common(mvm, &mvm->snif_sta, vif->addr,
+ mvmvif->id, 0);
+}
+
+int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_rm_sta_common(mvm, mvm->snif_sta.sta_id);
+ if (ret)
+ IWL_WARN(mvm, "Failed sending remove station\n");
+
+ return ret;
+}
+
+void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm)
+{
+ iwl_mvm_dealloc_int_sta(mvm, &mvm->snif_sta);
+}
+
+void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
+}
+
+/*
+ * Send the add station command for the vif's broadcast station.
+ * Assumes that the station was already allocated.
+ *
+ * @mvm: the mvm component
+ * @vif: the interface to which the broadcast station is added
+ * @bsta: the broadcast station to add.
+ */
+int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta;
+ static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ const u8 *baddr = _baddr;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (vif->type == NL80211_IFTYPE_ADHOC)
+ baddr = vif->bss_conf.bssid;
+
+ if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT))
+ return -ENOSPC;
+
+ return iwl_mvm_add_int_sta_common(mvm, bsta, baddr,
+ mvmvif->id, mvmvif->color);
+}
+
+/* Send the FW a request to remove the station from it's internal data
+ * structures, but DO NOT remove the entry from the local data structures. */
+int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id);
+ if (ret)
+ IWL_WARN(mvm, "Failed sending remove station\n");
+ return ret;
+}
+
+int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u32 qmask;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ qmask = iwl_mvm_mac_get_queues_mask(vif);
+
+ /*
+ * The firmware defines the TFD queue mask to only be relevant
+ * for *unicast* queues, so the multicast (CAB) queue shouldn't
+ * be included.
+ */
+ if (vif->type == NL80211_IFTYPE_AP)
+ qmask &= ~BIT(vif->cab_queue);
+
+ return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask,
+ ieee80211_vif_type_p2p(vif));
+}
+
+/* Allocate a new station entry for the broadcast station to the given vif,
+ * and send it to the FW.
+ * Note that each P2P mac should have its own broadcast station.
+ *
+ * @mvm: the mvm component
+ * @vif: the interface to which the broadcast station is added
+ * @bsta: the broadcast station to add. */
+int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
+
+ if (ret)
+ iwl_mvm_dealloc_int_sta(mvm, bsta);
+
+ return ret;
+}
+
+void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
+}
+
+/*
+ * Send the FW a request to remove the station from it's internal data
+ * structures, and in addition remove it from the local data structure.
+ */
+int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_send_rm_bcast_sta(mvm, vif);
+
+ iwl_mvm_dealloc_bcast_sta(mvm, vif);
+
+ return ret;
+}
+
+#define IWL_MAX_RX_BA_SESSIONS 16
+
+int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ int tid, u16 ssn, bool start)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_add_sta_cmd cmd = {};
+ int ret;
+ u32 status;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (start && mvm->rx_ba_sessions >= IWL_MAX_RX_BA_SESSIONS) {
+ IWL_WARN(mvm, "Not enough RX BA SESSIONS\n");
+ return -ENOSPC;
+ }
+
+ cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+ cmd.sta_id = mvm_sta->sta_id;
+ cmd.add_modify = STA_MODE_MODIFY;
+ if (start) {
+ cmd.add_immediate_ba_tid = (u8) tid;
+ cmd.add_immediate_ba_ssn = cpu_to_le16(ssn);
+ } else {
+ cmd.remove_immediate_ba_tid = (u8) tid;
+ }
+ cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID :
+ STA_MODIFY_REMOVE_BA_TID;
+
+ status = ADD_STA_SUCCESS;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+ &cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n",
+ start ? "start" : "stopp");
+ break;
+ case ADD_STA_IMMEDIATE_BA_FAILURE:
+ IWL_WARN(mvm, "RX BA Session refused by fw\n");
+ ret = -ENOSPC;
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n",
+ start ? "start" : "stopp", status);
+ break;
+ }
+
+ if (!ret) {
+ if (start)
+ mvm->rx_ba_sessions++;
+ else if (mvm->rx_ba_sessions > 0)
+ /* check that restart flow didn't zero the counter */
+ mvm->rx_ba_sessions--;
+ }
+
+ return ret;
+}
+
+static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ int tid, u8 queue, bool start)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_add_sta_cmd cmd = {};
+ int ret;
+ u32 status;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (start) {
+ mvm_sta->tfd_queue_msk |= BIT(queue);
+ mvm_sta->tid_disable_agg &= ~BIT(tid);
+ } else {
+ mvm_sta->tfd_queue_msk &= ~BIT(queue);
+ mvm_sta->tid_disable_agg |= BIT(tid);
+ }
+
+ cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+ cmd.sta_id = mvm_sta->sta_id;
+ cmd.add_modify = STA_MODE_MODIFY;
+ cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX;
+ cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
+ cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg);
+
+ status = ADD_STA_SUCCESS;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+ &cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n",
+ start ? "start" : "stopp", status);
+ break;
+ }
+
+ return ret;
+}
+
+const u8 tid_to_mac80211_ac[] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO,
+};
+
+static const u8 tid_to_ucode_ac[] = {
+ AC_BE,
+ AC_BK,
+ AC_BK,
+ AC_BE,
+ AC_VI,
+ AC_VI,
+ AC_VO,
+ AC_VO,
+};
+
+int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_tid_data *tid_data;
+ int txq_id;
+ int ret;
+
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ return -EINVAL;
+
+ if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) {
+ IWL_ERR(mvm, "Start AGG when state is not IWL_AGG_OFF %d!\n",
+ mvmsta->tid_data[tid].state);
+ return -ENXIO;
+ }
+
+ lockdep_assert_held(&mvm->mutex);
+
+ spin_lock_bh(&mvmsta->lock);
+
+ /* possible race condition - we entered D0i3 while starting agg */
+ if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) {
+ spin_unlock_bh(&mvmsta->lock);
+ IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n");
+ return -EIO;
+ }
+
+ spin_lock_bh(&mvm->queue_info_lock);
+
+ txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue,
+ mvm->last_agg_queue);
+ if (txq_id < 0) {
+ ret = txq_id;
+ spin_unlock_bh(&mvm->queue_info_lock);
+ IWL_ERR(mvm, "Failed to allocate agg queue\n");
+ goto release_locks;
+ }
+ mvm->queue_info[txq_id].setup_reserved = true;
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ tid_data = &mvmsta->tid_data[tid];
+ tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
+ tid_data->txq_id = txq_id;
+ *ssn = tid_data->ssn;
+
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n",
+ mvmsta->sta_id, tid, txq_id, tid_data->ssn,
+ tid_data->next_reclaimed);
+
+ if (tid_data->ssn == tid_data->next_reclaimed) {
+ tid_data->state = IWL_AGG_STARTING;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ } else {
+ tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+ }
+
+ ret = 0;
+
+release_locks:
+ spin_unlock_bh(&mvmsta->lock);
+
+ return ret;
+}
+
+int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u8 buf_size)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+ unsigned int wdg_timeout =
+ iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false);
+ int queue, fifo, ret;
+ u16 ssn;
+
+ BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE)
+ != IWL_MAX_TID_COUNT);
+
+ buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
+
+ spin_lock_bh(&mvmsta->lock);
+ ssn = tid_data->ssn;
+ queue = tid_data->txq_id;
+ tid_data->state = IWL_AGG_ON;
+ mvmsta->agg_tids |= BIT(tid);
+ tid_data->ssn = 0xffff;
+ spin_unlock_bh(&mvmsta->lock);
+
+ fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
+
+ iwl_mvm_enable_agg_txq(mvm, queue,
+ vif->hw_queue[tid_to_mac80211_ac[tid]], fifo,
+ mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout);
+
+ ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
+ if (ret)
+ return -EIO;
+
+ /* No need to mark as reserved */
+ spin_lock_bh(&mvm->queue_info_lock);
+ mvm->queue_info[queue].setup_reserved = false;
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ /*
+ * Even though in theory the peer could have different
+ * aggregation reorder buffer sizes for different sessions,
+ * our ucode doesn't allow for that and has a global limit
+ * for each station. Therefore, use the minimum of all the
+ * aggregation sessions and our default value.
+ */
+ mvmsta->max_agg_bufsize =
+ min(mvmsta->max_agg_bufsize, buf_size);
+ mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
+
+ IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
+ sta->addr, tid);
+
+ return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false);
+}
+
+int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+ u16 txq_id;
+ int err;
+
+
+ /*
+ * If mac80211 is cleaning its state, then say that we finished since
+ * our state has been cleared anyway.
+ */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ return 0;
+ }
+
+ spin_lock_bh(&mvmsta->lock);
+
+ txq_id = tid_data->txq_id;
+
+ IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n",
+ mvmsta->sta_id, tid, txq_id, tid_data->state);
+
+ mvmsta->agg_tids &= ~BIT(tid);
+
+ /* No need to mark as reserved anymore */
+ spin_lock_bh(&mvm->queue_info_lock);
+ mvm->queue_info[txq_id].setup_reserved = false;
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ switch (tid_data->state) {
+ case IWL_AGG_ON:
+ tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
+
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "ssn = %d, next_recl = %d\n",
+ tid_data->ssn, tid_data->next_reclaimed);
+
+ /* There are still packets for this RA / TID in the HW */
+ if (tid_data->ssn != tid_data->next_reclaimed) {
+ tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA;
+ err = 0;
+ break;
+ }
+
+ tid_data->ssn = 0xffff;
+ tid_data->state = IWL_AGG_OFF;
+ spin_unlock_bh(&mvmsta->lock);
+
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+
+ iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
+
+ iwl_mvm_disable_txq(mvm, txq_id,
+ vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
+ 0);
+ return 0;
+ case IWL_AGG_STARTING:
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ /*
+ * The agg session has been stopped before it was set up. This
+ * can happen when the AddBA timer times out for example.
+ */
+
+ /* No barriers since we are under mutex */
+ lockdep_assert_held(&mvm->mutex);
+
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ tid_data->state = IWL_AGG_OFF;
+ err = 0;
+ break;
+ default:
+ IWL_ERR(mvm,
+ "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
+ mvmsta->sta_id, tid, tid_data->state);
+ IWL_ERR(mvm,
+ "\ttid_data->txq_id = %d\n", tid_data->txq_id);
+ err = -EINVAL;
+ }
+
+ spin_unlock_bh(&mvmsta->lock);
+
+ return err;
+}
+
+int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+ u16 txq_id;
+ enum iwl_mvm_agg_state old_state;
+
+ /*
+ * First set the agg state to OFF to avoid calling
+ * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty.
+ */
+ spin_lock_bh(&mvmsta->lock);
+ txq_id = tid_data->txq_id;
+ IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n",
+ mvmsta->sta_id, tid, txq_id, tid_data->state);
+ old_state = tid_data->state;
+ tid_data->state = IWL_AGG_OFF;
+ mvmsta->agg_tids &= ~BIT(tid);
+ spin_unlock_bh(&mvmsta->lock);
+
+ /* No need to mark as reserved */
+ spin_lock_bh(&mvm->queue_info_lock);
+ mvm->queue_info[txq_id].setup_reserved = false;
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ if (old_state >= IWL_AGG_ON) {
+ iwl_mvm_drain_sta(mvm, mvmsta, true);
+ if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0))
+ IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+ iwl_trans_wait_tx_queue_empty(mvm->trans,
+ mvmsta->tfd_queue_msk);
+ iwl_mvm_drain_sta(mvm, mvmsta, false);
+
+ iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
+
+ iwl_mvm_disable_txq(mvm, tid_data->txq_id,
+ vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
+ 0);
+ }
+
+ return 0;
+}
+
+static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
+{
+ int i, max = -1, max_offs = -1;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Pick the unused key offset with the highest 'deleted'
+ * counter. Every time a key is deleted, all the counters
+ * are incremented and the one that was just deleted is
+ * reset to zero. Thus, the highest counter is the one
+ * that was deleted longest ago. Pick that one.
+ */
+ for (i = 0; i < STA_KEY_MAX_NUM; i++) {
+ if (test_bit(i, mvm->fw_key_table))
+ continue;
+ if (mvm->fw_key_deleted[i] > max) {
+ max = mvm->fw_key_deleted[i];
+ max_offs = i;
+ }
+ }
+
+ if (max_offs < 0)
+ return STA_KEY_IDX_INVALID;
+
+ return max_offs;
+}
+
+static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (sta)
+ return iwl_mvm_sta_from_mac80211(sta);
+
+ /*
+ * The device expects GTKs for station interfaces to be
+ * installed as GTKs for the AP station. If we have no
+ * station ID, then use AP's station ID.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+ u8 sta_id = mvmvif->ap_sta_id;
+
+ sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /*
+ * It is possible that the 'sta' parameter is NULL,
+ * for example when a GTK is removed - the sta_id will then
+ * be the AP ID, and no station was passed by mac80211.
+ */
+ if (IS_ERR_OR_NULL(sta))
+ return NULL;
+
+ return iwl_mvm_sta_from_mac80211(sta);
+ }
+
+ return NULL;
+}
+
+static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
+ struct iwl_mvm_sta *mvm_sta,
+ struct ieee80211_key_conf *keyconf, bool mcast,
+ u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags,
+ u8 key_offset)
+{
+ struct iwl_mvm_add_sta_key_cmd cmd = {};
+ __le16 key_flags;
+ int ret;
+ u32 status;
+ u16 keyidx;
+ int i;
+ u8 sta_id = mvm_sta->sta_id;
+
+ keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
+ STA_KEY_FLG_KEYID_MSK;
+ key_flags = cpu_to_le16(keyidx);
+ key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP);
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP);
+ cmd.tkip_rx_tsc_byte2 = tkip_iv32;
+ for (i = 0; i < 5; i++)
+ cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
+ memcpy(cmd.key, keyconf->key, keyconf->keylen);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
+ memcpy(cmd.key, keyconf->key, keyconf->keylen);
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES);
+ /* fall through */
+ case WLAN_CIPHER_SUITE_WEP40:
+ key_flags |= cpu_to_le16(STA_KEY_FLG_WEP);
+ memcpy(cmd.key + 3, keyconf->key, keyconf->keylen);
+ break;
+ default:
+ key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
+ memcpy(cmd.key, keyconf->key, keyconf->keylen);
+ }
+
+ if (mcast)
+ key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+ cmd.key_offset = key_offset;
+ cmd.key_flags = key_flags;
+ cmd.sta_id = sta_id;
+
+ status = ADD_STA_SUCCESS;
+ if (cmd_flags & CMD_ASYNC)
+ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC,
+ sizeof(cmd), &cmd);
+ else
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+ &cmd, &status);
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n");
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n");
+ break;
+ }
+
+ return ret;
+}
+
+static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
+ struct ieee80211_key_conf *keyconf,
+ u8 sta_id, bool remove_key)
+{
+ struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {};
+
+ /* verify the key details match the required command's expectations */
+ if (WARN_ON((keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC) ||
+ (keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) ||
+ (keyconf->keyidx != 4 && keyconf->keyidx != 5)))
+ return -EINVAL;
+
+ igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx);
+ igtk_cmd.sta_id = cpu_to_le32(sta_id);
+
+ if (remove_key) {
+ igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID);
+ } else {
+ struct ieee80211_key_seq seq;
+ const u8 *pn;
+
+ memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
+ ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+ pn = seq.aes_cmac.pn;
+ igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |
+ ((u64) pn[4] << 8) |
+ ((u64) pn[3] << 16) |
+ ((u64) pn[2] << 24) |
+ ((u64) pn[1] << 32) |
+ ((u64) pn[0] << 40));
+ }
+
+ IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n",
+ remove_key ? "removing" : "installing",
+ igtk_cmd.sta_id);
+
+ return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0,
+ sizeof(igtk_cmd), &igtk_cmd);
+}
+
+
+static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (sta)
+ return sta->addr;
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+ u8 sta_id = mvmvif->ap_sta_id;
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+ return sta->addr;
+ }
+
+
+ return NULL;
+}
+
+static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *keyconf,
+ u8 key_offset,
+ bool mcast)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ int ret;
+ const u8 *addr;
+ struct ieee80211_key_seq seq;
+ u16 p1k[5];
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
+ /* get phase 1 key from mac80211 */
+ ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+ ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+ ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+ seq.tkip.iv32, p1k, 0, key_offset);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+ 0, NULL, 0, key_offset);
+ break;
+ default:
+ ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+ 0, NULL, 0, key_offset);
+ }
+
+ return ret;
+}
+
+static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
+ struct ieee80211_key_conf *keyconf,
+ bool mcast)
+{
+ struct iwl_mvm_add_sta_key_cmd cmd = {};
+ __le16 key_flags;
+ int ret;
+ u32 status;
+
+ key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
+ STA_KEY_FLG_KEYID_MSK);
+ key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
+ key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+
+ if (mcast)
+ key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+ cmd.key_flags = key_flags;
+ cmd.key_offset = keyconf->hw_key_idx;
+ cmd.sta_id = sta_id;
+
+ status = ADD_STA_SUCCESS;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+ &cmd, &status);
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
+ break;
+ }
+
+ return ret;
+}
+
+int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *keyconf,
+ u8 key_offset)
+{
+ bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+ struct iwl_mvm_sta *mvm_sta;
+ u8 sta_id;
+ int ret;
+ static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0};
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Get the station id from the mvm local station table */
+ mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
+ if (!mvm_sta) {
+ IWL_ERR(mvm, "Failed to find station\n");
+ return -EINVAL;
+ }
+ sta_id = mvm_sta->sta_id;
+
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
+ ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false);
+ goto end;
+ }
+
+ /*
+ * It is possible that the 'sta' parameter is NULL, and thus
+ * there is a need to retrieve the sta from the local station table.
+ */
+ if (!sta) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta)) {
+ IWL_ERR(mvm, "Invalid station id\n");
+ return -EINVAL;
+ }
+ }
+
+ if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
+ return -EINVAL;
+
+ /* If the key_offset is not pre-assigned, we need to find a
+ * new offset to use. In normal cases, the offset is not
+ * pre-assigned, but during HW_RESTART we want to reuse the
+ * same indices, so we pass them when this function is called.
+ *
+ * In D3 entry, we need to hardcoded the indices (because the
+ * firmware hardcodes the PTK offset to 0). In this case, we
+ * need to make sure we don't overwrite the hw_key_idx in the
+ * keyconf structure, because otherwise we cannot configure
+ * the original ones back when resuming.
+ */
+ if (key_offset == STA_KEY_IDX_INVALID) {
+ key_offset = iwl_mvm_set_fw_key_idx(mvm);
+ if (key_offset == STA_KEY_IDX_INVALID)
+ return -ENOSPC;
+ keyconf->hw_key_idx = key_offset;
+ }
+
+ ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, mcast);
+ if (ret)
+ goto end;
+
+ /*
+ * For WEP, the same key is used for multicast and unicast. Upload it
+ * again, using the same key offset, and now pointing the other one
+ * to the same key slot (offset).
+ * If this fails, remove the original as well.
+ */
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
+ ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf,
+ key_offset, !mcast);
+ if (ret) {
+ __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+ goto end;
+ }
+ }
+
+ __set_bit(key_offset, mvm->fw_key_table);
+
+end:
+ IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
+ keyconf->cipher, keyconf->keylen, keyconf->keyidx,
+ sta ? sta->addr : zero_addr, ret);
+ return ret;
+}
+
+int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *keyconf)
+{
+ bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+ struct iwl_mvm_sta *mvm_sta;
+ u8 sta_id = IWL_MVM_STATION_COUNT;
+ int ret, i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Get the station from the mvm local station table */
+ mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
+
+ IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n",
+ keyconf->keyidx, sta_id);
+
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true);
+
+ if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) {
+ IWL_ERR(mvm, "offset %d not used in fw key table.\n",
+ keyconf->hw_key_idx);
+ return -ENOENT;
+ }
+
+ /* track which key was deleted last */
+ for (i = 0; i < STA_KEY_MAX_NUM; i++) {
+ if (mvm->fw_key_deleted[i] < U8_MAX)
+ mvm->fw_key_deleted[i]++;
+ }
+ mvm->fw_key_deleted[keyconf->hw_key_idx] = 0;
+
+ if (!mvm_sta) {
+ IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
+ return 0;
+ }
+
+ sta_id = mvm_sta->sta_id;
+
+ ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+ if (ret)
+ return ret;
+
+ /* delete WEP key twice to get rid of (now useless) offset */
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
+ ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast);
+
+ return ret;
+}
+
+void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta, u32 iv32,
+ u16 *phase1key)
+{
+ struct iwl_mvm_sta *mvm_sta;
+ bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+
+ rcu_read_lock();
+
+ mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
+ if (WARN_ON_ONCE(!mvm_sta))
+ goto unlock;
+ iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+ iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx);
+
+ unlock:
+ rcu_read_unlock();
+}
+
+void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_add_sta_cmd cmd = {
+ .add_modify = STA_MODE_MODIFY,
+ .sta_id = mvmsta->sta_id,
+ .station_flags_msk = cpu_to_le32(STA_FLG_PS),
+ .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
+ };
+ int ret;
+
+ 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);
+}
+
+void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ enum ieee80211_frame_release_type reason,
+ u16 cnt, u16 tids, bool more_data,
+ bool agg)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_add_sta_cmd cmd = {
+ .add_modify = STA_MODE_MODIFY,
+ .sta_id = mvmsta->sta_id,
+ .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
+ .sleep_tx_count = cpu_to_le16(cnt),
+ .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
+ };
+ int tid, ret;
+ unsigned long _tids = tids;
+
+ /* convert TIDs to ACs - we don't support TSPEC so that's OK
+ * Note that this field is reserved and unused by firmware not
+ * supporting GO uAPSD, so it's safe to always do this.
+ */
+ for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT)
+ cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]);
+
+ /* If we're releasing frames from aggregation queues then check if the
+ * all queues combined that we're releasing frames from have
+ * - more frames than the service period, in which case more_data
+ * needs to be set
+ * - fewer than 'cnt' frames, in which case we need to adjust the
+ * firmware command (but do that unconditionally)
+ */
+ if (agg) {
+ int remaining = cnt;
+ int sleep_tx_count;
+
+ spin_lock_bh(&mvmsta->lock);
+ for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) {
+ struct iwl_mvm_tid_data *tid_data;
+ u16 n_queued;
+
+ tid_data = &mvmsta->tid_data[tid];
+ if (WARN(tid_data->state != IWL_AGG_ON &&
+ tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA,
+ "TID %d state is %d\n",
+ tid, tid_data->state)) {
+ spin_unlock_bh(&mvmsta->lock);
+ ieee80211_sta_eosp(sta);
+ return;
+ }
+
+ n_queued = iwl_mvm_tid_queued(tid_data);
+ if (n_queued > remaining) {
+ more_data = true;
+ remaining = 0;
+ break;
+ }
+ remaining -= n_queued;
+ }
+ sleep_tx_count = cnt - remaining;
+ if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
+ mvmsta->sleep_tx_count = sleep_tx_count;
+ spin_unlock_bh(&mvmsta->lock);
+
+ cmd.sleep_tx_count = cpu_to_le16(sleep_tx_count);
+ if (WARN_ON(cnt - remaining == 0)) {
+ ieee80211_sta_eosp(sta);
+ return;
+ }
+ }
+
+ /* Note: this is ignored by firmware not supporting GO uAPSD */
+ if (more_data)
+ cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA);
+
+ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) {
+ mvmsta->next_status_eosp = true;
+ cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL);
+ } else {
+ cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
+ }
+
+ /* block the Tx queues until the FW updated the sleep Tx count */
+ iwl_trans_block_txq_ptrs(mvm->trans, true);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA,
+ CMD_ASYNC | CMD_WANT_ASYNC_CALLBACK,
+ sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
+}
+
+void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mvm_eosp_notification *notif = (void *)pkt->data;
+ struct ieee80211_sta *sta;
+ u32 sta_id = le32_to_cpu(notif->sta_id);
+
+ if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT))
+ return;
+
+ rcu_read_lock();
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+ if (!IS_ERR_OR_NULL(sta))
+ ieee80211_sta_eosp(sta);
+ rcu_read_unlock();
+}
+
+void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_sta *mvmsta, bool disable)
+{
+ struct iwl_mvm_add_sta_cmd cmd = {
+ .add_modify = STA_MODE_MODIFY,
+ .sta_id = mvmsta->sta_id,
+ .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0,
+ .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX),
+ .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
+ };
+ int ret;
+
+ 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);
+}
+
+void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ bool disable)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+ spin_lock_bh(&mvm_sta->lock);
+
+ if (mvm_sta->disable_tx == disable) {
+ spin_unlock_bh(&mvm_sta->lock);
+ return;
+ }
+
+ mvm_sta->disable_tx = disable;
+
+ /*
+ * Tell mac80211 to start/stop queuing tx for this station,
+ * but don't stop queuing if there are still pending frames
+ * for this station.
+ */
+ if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
+ ieee80211_sta_block_awake(mvm->hw, sta, disable);
+
+ iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable);
+
+ spin_unlock_bh(&mvm_sta->lock);
+}
+
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ bool disable)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvm_sta;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Block/unblock all the stations of the given mvmvif */
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta))
+ continue;
+
+ mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvm_sta->mac_id_n_color !=
+ FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
+ continue;
+
+ iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
+ }
+}
+
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvmsta;
+
+ rcu_read_lock();
+
+ mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
+
+ if (!WARN_ON(!mvmsta))
+ iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
+
+ rcu_read_unlock();
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
new file mode 100644
index 000000000000..39fdf5224e81
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
@@ -0,0 +1,450 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __sta_h__
+#define __sta_h__
+
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+#include <linux/wait.h>
+
+#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */
+#include "fw-api.h" /* IWL_MVM_STATION_COUNT */
+#include "rs.h"
+
+struct iwl_mvm;
+struct iwl_mvm_vif;
+
+/**
+ * DOC: station table - introduction
+ *
+ * The station table is a list of data structure that reprensent the stations.
+ * In STA/P2P client mode, the driver will hold one station for the AP/ GO.
+ * In GO/AP mode, the driver will have as many stations as associated clients.
+ * All these stations are reflected in the fw's station table. The driver
+ * keeps the fw's station table up to date with the ADD_STA command. Stations
+ * can be removed by the REMOVE_STA command.
+ *
+ * All the data related to a station is held in the structure %iwl_mvm_sta
+ * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area.
+ * This data includes the index of the station in the fw, per tid information
+ * (sequence numbers, Block-ack state machine, etc...). The stations are
+ * created and deleted by the %sta_state callback from %ieee80211_ops.
+ *
+ * The driver holds a map: %fw_id_to_mac_id that allows to fetch a
+ * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw
+ * station index. That way, the driver is able to get the tid related data in
+ * O(1) in time sensitive paths (Tx / Tx response / BA notification). These
+ * paths are triggered by the fw, and the driver needs to get a pointer to the
+ * %ieee80211 structure. This map helps to get that pointer quickly.
+ */
+
+/**
+ * DOC: station table - locking
+ *
+ * As stated before, the station is created / deleted by mac80211's %sta_state
+ * callback from %ieee80211_ops which can sleep. The next paragraph explains
+ * the locking of a single stations, the next ones relates to the station
+ * table.
+ *
+ * The station holds the sequence number per tid. So this data needs to be
+ * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack
+ * information (the state machine / and the logic that checks if the queues
+ * were drained), so it also needs to be accessible from the Tx response flow.
+ * In short, the station needs to be access from sleepable context as well as
+ * from tasklets, so the station itself needs a spinlock.
+ *
+ * The writers of %fw_id_to_mac_id map are serialized by the global mutex of
+ * the mvm op_mode. This is possible since %sta_state can sleep.
+ * The pointers in this map are RCU protected, hence we won't replace the
+ * station while we have Tx / Tx response / BA notification running.
+ *
+ * If a station is deleted while it still has packets in its A-MPDU queues,
+ * then the reclaim flow will notice that there is no station in the map for
+ * sta_id and it will dump the responses.
+ */
+
+/**
+ * DOC: station table - internal stations
+ *
+ * The FW needs a few internal stations that are not reflected in
+ * mac80211, such as broadcast station in AP / GO mode, or AUX sta for
+ * scanning and P2P device (during the GO negotiation).
+ * For these kind of stations we have %iwl_mvm_int_sta struct which holds the
+ * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta.
+ * Usually the data for these stations is static, so no locking is required,
+ * and no TID data as this is also not needed.
+ * One thing to note, is that these stations have an ID in the fw, but not
+ * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id
+ * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of
+ * pointers from this mapping need to check that the value is not error
+ * or NULL.
+ *
+ * Currently there is only one auxiliary station for scanning, initialized
+ * on init.
+ */
+
+/**
+ * DOC: station table - AP Station in STA mode
+ *
+ * %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
+ * %ap_sta_id. To get the point to the corresponding %ieee80211_sta,
+ * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
+ * the AP station from the fw before setting the MAC context as unassociated.
+ * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
+ * removed by mac80211, but the station won't be removed in the fw until the
+ * VIF is set as unassociated. Then, %ap_sta_id will be invalidated.
+ */
+
+/**
+ * DOC: station table - Drain vs. Flush
+ *
+ * Flush means that all the frames in the SCD queue are dumped regardless the
+ * station to which they were sent. We do that when we disassociate and before
+ * we remove the STA of the AP. The flush can be done synchronously against the
+ * fw.
+ * Drain means that the fw will drop all the frames sent to a specific station.
+ * This is useful when a client (if we are IBSS / GO or AP) disassociates. In
+ * that case, we need to drain all the frames for that client from the AC queues
+ * that are shared with the other clients. Only then, we can remove the STA in
+ * the fw. In order to do so, we track the non-AMPDU packets for each station.
+ * If mac80211 removes a STA and if it still has non-AMPDU packets pending in
+ * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all
+ * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped
+ * (we know about it with its Tx response), we remove the station in fw and set
+ * it as %NULL in %fw_id_to_mac_id: this is the purpose of
+ * %iwl_mvm_sta_drained_wk.
+ */
+
+/**
+ * DOC: station table - fw restart
+ *
+ * When the fw asserts, or we have any other issue that requires to reset the
+ * driver, we require mac80211 to reconfigure the driver. Since the private
+ * data of the stations is embed in mac80211's %ieee80211_sta, that data will
+ * not be zeroed and needs to be reinitialized manually.
+ * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us
+ * that we must not allocate a new sta_id but reuse the previous one. This
+ * means that the stations being re-added after the reset will have the same
+ * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id
+ * map, since the stations aren't in the fw any more. Internal stations that
+ * are not added by mac80211 will be re-added in the init flow that is called
+ * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to
+ * %iwl_mvm_up.
+ */
+
+/**
+ * DOC: AP mode - PS
+ *
+ * When a station is asleep, the fw will set it as "asleep". All frames on
+ * shared queues (i.e. non-aggregation queues) to that station will be dropped
+ * by the fw (%TX_STATUS_FAIL_DEST_PS failure code).
+ *
+ * AMPDUs are in a separate queue that is stopped by the fw. We just need to
+ * let mac80211 know when there are frames in these queues so that it can
+ * properly handle trigger frames.
+ *
+ * When a trigger frame is received, mac80211 tells the driver to send frames
+ * from the AMPDU queues or sends frames to non-aggregation queues itself,
+ * depending on which ACs are delivery-enabled and what TID has frames to
+ * transmit. Note that mac80211 has all the knowledge since all the non-agg
+ * frames are buffered / filtered, and the driver tells mac80211 about agg
+ * frames). The driver needs to tell the fw to let frames out even if the
+ * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
+ *
+ * When we receive a frame from that station with PM bit unset, the driver
+ * needs to let the fw know that this station isn't asleep any more. This is
+ * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the
+ * station's wakeup.
+ *
+ * For a GO, the Service Period might be cut short due to an absence period
+ * of the GO. In this (and all other cases) the firmware notifies us with the
+ * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we
+ * already sent to the device will be rejected again.
+ *
+ * See also "AP support for powersaving clients" in mac80211.h.
+ */
+
+/**
+ * enum iwl_mvm_agg_state
+ *
+ * The state machine of the BA agreement establishment / tear down.
+ * These states relate to a specific RA / TID.
+ *
+ * @IWL_AGG_OFF: aggregation is not used
+ * @IWL_AGG_STARTING: aggregation are starting (between start and oper)
+ * @IWL_AGG_ON: aggregation session is up
+ * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
+ * HW queue to be empty from packets for this RA /TID.
+ * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
+ * HW queue to be empty from packets for this RA /TID.
+ */
+enum iwl_mvm_agg_state {
+ IWL_AGG_OFF = 0,
+ IWL_AGG_STARTING,
+ IWL_AGG_ON,
+ IWL_EMPTYING_HW_QUEUE_ADDBA,
+ IWL_EMPTYING_HW_QUEUE_DELBA,
+};
+
+/**
+ * struct iwl_mvm_tid_data - holds the states for each RA / TID
+ * @seq_number: the next WiFi sequence number to use
+ * @next_reclaimed: the WiFi sequence number of the next packet to be acked.
+ * This is basically (last acked packet++).
+ * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
+ * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
+ * @reduced_tpc: Reduced tx power. Holds the data between the
+ * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
+ * @state: state of the BA agreement establishment / tear down.
+ * @txq_id: Tx queue used by the BA session
+ * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
+ * 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.
+ * @tx_time: medium time consumed by this A-MPDU
+ */
+struct iwl_mvm_tid_data {
+ u16 seq_number;
+ u16 next_reclaimed;
+ /* The rest is Tx AGG related */
+ u32 rate_n_flags;
+ u8 reduced_tpc;
+ enum iwl_mvm_agg_state state;
+ u16 txq_id;
+ u16 ssn;
+ u16 tx_time;
+};
+
+static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
+{
+ return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number),
+ tid_data->next_reclaimed);
+}
+
+struct iwl_mvm_key_pn {
+ struct rcu_head rcu_head;
+ struct {
+ u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN];
+ } ____cacheline_aligned_in_smp q[];
+};
+
+/**
+ * struct iwl_mvm_sta - representation of a station in the driver
+ * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
+ * @tfd_queue_msk: the tfd queues used by the station
+ * @hw_queue: per-AC mapping of the TFD queues used by station
+ * @mac_id_n_color: the MAC context this station is linked to
+ * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
+ * tid.
+ * @max_agg_bufsize: the maximal size of the AGG buffer for this station
+ * @bt_reduced_txpower: is reduced tx power enabled for this station
+ * @next_status_eosp: the next reclaimed packet is a PS-Poll response and
+ * we need to signal the EOSP
+ * @lock: lock to protect the whole struct. Since %tid_data is access from Tx
+ * and from Tx response flow, it needs a spinlock.
+ * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
+ * @tx_protection: reference counter for controlling the Tx protection.
+ * @tt_tx_protection: is thermal throttling enable Tx protection?
+ * @disable_tx: is tx to this STA disabled?
+ * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON)
+ * @sleep_tx_count: the number of frames that we told the firmware to let out
+ * even when that station is asleep. This is useful in case the queue
+ * gets empty before all the frames were sent, which can happen when
+ * we are sending frames from an AMPDU queue and there was a hole in
+ * the BA window. To be used for UAPSD only.
+ * @ptk_pn: per-queue PTK PN data structures
+ *
+ * When mac80211 creates a station it reserves some space (hw->sta_data_size)
+ * in the structure for use by driver. This structure is placed in that
+ * space.
+ *
+ */
+struct iwl_mvm_sta {
+ u32 sta_id;
+ u32 tfd_queue_msk;
+ u8 hw_queue[IEEE80211_NUM_ACS];
+ u32 mac_id_n_color;
+ u16 tid_disable_agg;
+ u8 max_agg_bufsize;
+ bool bt_reduced_txpower;
+ bool next_status_eosp;
+ spinlock_t lock;
+ struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
+ struct iwl_lq_sta lq_sta;
+ struct ieee80211_vif *vif;
+
+ struct iwl_mvm_key_pn __rcu *ptk_pn[4];
+
+ /* Temporary, until the new TLC will control the Tx protection */
+ s8 tx_protection;
+ bool tt_tx_protection;
+
+ bool disable_tx;
+ u8 agg_tids;
+ u8 sleep_tx_count;
+};
+
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta)
+{
+ return (void *)sta->drv_priv;
+}
+
+/**
+ * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or
+ * broadcast)
+ * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
+ * @tfd_queue_msk: the tfd queues used by the station
+ */
+struct iwl_mvm_int_sta {
+ u32 sta_id;
+ u32 tfd_queue_msk;
+};
+
+int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ bool update);
+int iwl_mvm_add_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int iwl_mvm_update_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u8 sta_id);
+int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *keyconf,
+ u8 key_offset);
+int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *keyconf);
+
+void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta, u32 iv32,
+ u16 *phase1key);
+
+void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+
+/* AMPDU */
+int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ int tid, u16 ssn, bool start);
+int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid, u8 buf_size);
+int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid);
+int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 tid);
+
+int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
+void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm);
+
+int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,
+ struct iwl_mvm_int_sta *sta,
+ u32 qmask, enum nl80211_iftype iftype);
+void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm);
+
+void iwl_mvm_sta_drained_wk(struct work_struct *wk);
+void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
+void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ enum ieee80211_frame_release_type reason,
+ u16 cnt, u16 tids, bool more_data,
+ bool agg);
+int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+ bool drain);
+void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_sta *mvmsta, bool disable);
+void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ bool disable);
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ bool disable);
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
new file mode 100644
index 000000000000..18711c5de35a
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
@@ -0,0 +1,732 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include "mvm.h"
+#include "time-event.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+
+#define TU_TO_US(x) (x * 1024)
+#define TU_TO_MS(x) (TU_TO_US(x) / 1000)
+
+void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (!sta || IS_ERR(sta) || !sta->tdls)
+ continue;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
+ NL80211_TDLS_TEARDOWN,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
+ GFP_KERNEL);
+ }
+}
+
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ int count = 0;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (!sta || IS_ERR(sta) || !sta->tdls)
+ continue;
+
+ if (vif) {
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvmsta->vif != vif)
+ continue;
+ }
+
+ count++;
+ }
+
+ return count;
+}
+
+static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_rx_packet *pkt;
+ struct iwl_tdls_config_res *resp;
+ struct iwl_tdls_config_cmd tdls_cfg_cmd = {};
+ struct iwl_host_cmd cmd = {
+ .id = TDLS_CONFIG_CMD,
+ .flags = CMD_WANT_SKB,
+ .data = { &tdls_cfg_cmd, },
+ .len = { sizeof(struct iwl_tdls_config_cmd), },
+ };
+ struct ieee80211_sta *sta;
+ int ret, i, cnt;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ tdls_cfg_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+ tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID;
+ tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */
+
+ /* for now the Tx cmd is empty and unused */
+
+ /* populate TDLS peer data */
+ cnt = 0;
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta) || !sta->tdls)
+ continue;
+
+ tdls_cfg_cmd.sta_info[cnt].sta_id = i;
+ tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid =
+ IWL_MVM_TDLS_FW_TID;
+ tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0);
+ tdls_cfg_cmd.sta_info[cnt].is_initiator =
+ cpu_to_le32(sta->tdls_initiator ? 1 : 0);
+
+ cnt++;
+ }
+
+ tdls_cfg_cmd.tdls_peer_count = cnt;
+ IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (WARN_ON_ONCE(ret))
+ return;
+
+ pkt = cmd.resp_pkt;
+
+ WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp));
+
+ /* we don't really care about the response at this point */
+
+ iwl_free_resp(&cmd);
+}
+
+void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool sta_added)
+{
+ int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
+
+ /* when the first peer joins, send a power update first */
+ if (tdls_sta_cnt == 1 && sta_added)
+ iwl_mvm_power_update_mac(mvm);
+
+ /* configure the FW with TDLS peer info */
+ iwl_mvm_tdls_config(mvm, vif);
+
+ /* when the last peer leaves, send a power update last */
+ if (tdls_sta_cnt == 0 && !sta_added)
+ iwl_mvm_power_update_mac(mvm);
+}
+
+void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
+
+ /*
+ * iwl_mvm_protect_session() reads directly from the device
+ * (the system time), so make sure it is available.
+ */
+ if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS))
+ return;
+
+ mutex_lock(&mvm->mutex);
+ /* Protect the session to hear the TDLS setup response on the channel */
+ iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true);
+ mutex_unlock(&mvm->mutex);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
+}
+
+static const char *
+iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state)
+{
+ switch (state) {
+ case IWL_MVM_TDLS_SW_IDLE:
+ return "IDLE";
+ case IWL_MVM_TDLS_SW_REQ_SENT:
+ return "REQ SENT";
+ case IWL_MVM_TDLS_SW_RESP_RCVD:
+ return "RESP RECEIVED";
+ case IWL_MVM_TDLS_SW_REQ_RCVD:
+ return "REQ RECEIVED";
+ case IWL_MVM_TDLS_SW_ACTIVE:
+ return "ACTIVE";
+ }
+
+ return NULL;
+}
+
+static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm,
+ enum iwl_mvm_tdls_cs_state state)
+{
+ if (mvm->tdls_cs.state == state)
+ return;
+
+ IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n",
+ iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state),
+ iwl_mvm_tdls_cs_state_str(state));
+ mvm->tdls_cs.state = state;
+
+ /* we only send requests to our switching peer - update sent time */
+ if (state == IWL_MVM_TDLS_SW_REQ_SENT)
+ mvm->tdls_cs.peer.sent_timestamp =
+ iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
+
+ if (state == IWL_MVM_TDLS_SW_IDLE)
+ mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT;
+}
+
+void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data;
+ struct ieee80211_sta *sta;
+ unsigned int delay;
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_vif *vif;
+ u32 sta_id = le32_to_cpu(notif->sta_id);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* can fail sometimes */
+ if (!le32_to_cpu(notif->status)) {
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+ return;
+ }
+
+ if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT))
+ return;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /* the station may not be here, but if it is, it must be a TDLS peer */
+ if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls))
+ return;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ vif = mvmsta->vif;
+
+ /*
+ * Update state and possibly switch again after this is over (DTIM).
+ * Also convert TU to msec.
+ */
+ delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+ mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
+}
+
+static int
+iwl_mvm_tdls_check_action(struct iwl_mvm *mvm,
+ enum iwl_tdls_channel_switch_type type,
+ const u8 *peer, bool peer_initiator, u32 timestamp)
+{
+ bool same_peer = false;
+ int ret = 0;
+
+ /* get the existing peer if it's there */
+ if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE &&
+ mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+ struct ieee80211_sta *sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (!IS_ERR_OR_NULL(sta))
+ same_peer = ether_addr_equal(peer, sta->addr);
+ }
+
+ switch (mvm->tdls_cs.state) {
+ case IWL_MVM_TDLS_SW_IDLE:
+ /*
+ * might be spurious packet from the peer after the switch is
+ * already done
+ */
+ if (type == TDLS_MOVE_CH)
+ ret = -EINVAL;
+ break;
+ case IWL_MVM_TDLS_SW_REQ_SENT:
+ /* only allow requests from the same peer */
+ if (!same_peer)
+ ret = -EBUSY;
+ else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH &&
+ !peer_initiator)
+ /*
+ * We received a ch-switch request while an outgoing
+ * one is pending. Allow it if the peer is the link
+ * initiator.
+ */
+ ret = -EBUSY;
+ else if (type == TDLS_SEND_CHAN_SW_REQ)
+ /* wait for idle before sending another request */
+ ret = -EBUSY;
+ else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp)
+ /* we got a stale response - ignore it */
+ ret = -EINVAL;
+ break;
+ case IWL_MVM_TDLS_SW_RESP_RCVD:
+ /*
+ * we are waiting for the FW to give an "active" notification,
+ * so ignore requests in the meantime
+ */
+ ret = -EBUSY;
+ break;
+ case IWL_MVM_TDLS_SW_REQ_RCVD:
+ /* as above, allow the link initiator to proceed */
+ if (type == TDLS_SEND_CHAN_SW_REQ) {
+ if (!same_peer)
+ ret = -EBUSY;
+ else if (peer_initiator) /* they are the initiator */
+ ret = -EBUSY;
+ } else if (type == TDLS_MOVE_CH) {
+ ret = -EINVAL;
+ }
+ break;
+ case IWL_MVM_TDLS_SW_ACTIVE:
+ /*
+ * the only valid request when active is a request to return
+ * to the base channel by the current off-channel peer
+ */
+ if (type != TDLS_MOVE_CH || !same_peer)
+ ret = -EBUSY;
+ break;
+ }
+
+ if (ret)
+ IWL_DEBUG_TDLS(mvm,
+ "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n",
+ type, mvm->tdls_cs.state, peer, same_peer,
+ peer_initiator);
+
+ return ret;
+}
+
+static int
+iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ enum iwl_tdls_channel_switch_type type,
+ const u8 *peer, bool peer_initiator,
+ u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ u32 timestamp, u16 switch_time,
+ u16 switch_timeout, struct sk_buff *skb,
+ u32 ch_sw_tm_ie)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_hdr *hdr;
+ struct iwl_tdls_channel_switch_cmd cmd = {0};
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator,
+ timestamp);
+ if (ret)
+ return ret;
+
+ if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cmd.switch_type = type;
+ cmd.timing.frame_timestamp = cpu_to_le32(timestamp);
+ cmd.timing.switch_time = cpu_to_le32(switch_time);
+ cmd.timing.switch_timeout = cpu_to_le32(switch_timeout);
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(vif, peer);
+ if (!sta) {
+ rcu_read_unlock();
+ ret = -ENOENT;
+ goto out;
+ }
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id);
+
+ if (!chandef) {
+ if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+ mvm->tdls_cs.peer.chandef.chan) {
+ /* actually moving to the channel */
+ chandef = &mvm->tdls_cs.peer.chandef;
+ } else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE &&
+ type == TDLS_MOVE_CH) {
+ /* we need to return to base channel */
+ struct ieee80211_chanctx_conf *chanctx =
+ rcu_dereference(vif->chanctx_conf);
+
+ if (WARN_ON_ONCE(!chanctx)) {
+ rcu_read_unlock();
+ goto out;
+ }
+
+ chandef = &chanctx->def;
+ }
+ }
+
+ if (chandef) {
+ cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
+ PHY_BAND_24 : PHY_BAND_5);
+ cmd.ci.channel = chandef->chan->hw_value;
+ cmd.ci.width = iwl_mvm_get_channel_width(chandef);
+ cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+ }
+
+ /* keep quota calculation simple for now - 50% of DTIM for TDLS */
+ cmd.timing.max_offchan_duration =
+ cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int) / 2);
+
+ /* Switch time is the first element in the switch-timing IE. */
+ cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
+
+ info = IEEE80211_SKB_CB(skb);
+ hdr = (void *)skb->data;
+ if (info->control.hw_key) {
+ if (info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP) {
+ rcu_read_unlock();
+ ret = -EINVAL;
+ goto out;
+ }
+ iwl_mvm_set_tx_cmd_ccmp(info, &cmd.frame.tx_cmd);
+ }
+
+ iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info,
+ mvmsta->sta_id);
+
+ iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta,
+ hdr->frame_control);
+ rcu_read_unlock();
+
+ memcpy(cmd.frame.data, skb->data, skb->len);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0,
+ sizeof(cmd), &cmd);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n",
+ ret);
+ goto out;
+ }
+
+ /* channel switch has started, update state */
+ if (type != TDLS_MOVE_CH) {
+ mvm->tdls_cs.cur_sta_id = mvmsta->sta_id;
+ iwl_mvm_tdls_update_cs_state(mvm,
+ type == TDLS_SEND_CHAN_SW_REQ ?
+ IWL_MVM_TDLS_SW_REQ_SENT :
+ IWL_MVM_TDLS_SW_REQ_RCVD);
+ } else {
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD);
+ }
+
+out:
+
+ /* channel switch failed - we are idle */
+ if (ret)
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+ return ret;
+}
+
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work)
+{
+ struct iwl_mvm *mvm;
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_vif *vif;
+ unsigned int delay;
+ int ret;
+
+ mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work);
+ mutex_lock(&mvm->mutex);
+
+ /* called after an active channel switch has finished or timed-out */
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+ /* station might be gone, in that case do nothing */
+ if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT)
+ goto out;
+
+ sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /* the station may not be here, but if it is, it must be a TDLS peer */
+ if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls))
+ goto out;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ vif = mvmsta->vif;
+ ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+ TDLS_SEND_CHAN_SW_REQ,
+ sta->addr,
+ mvm->tdls_cs.peer.initiator,
+ mvm->tdls_cs.peer.op_class,
+ &mvm->tdls_cs.peer.chandef,
+ 0, 0, 0,
+ mvm->tdls_cs.peer.skb,
+ mvm->tdls_cs.peer.ch_sw_tm_ie);
+ if (ret)
+ IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret);
+
+ /* retry after a DTIM if we failed sending now */
+ delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+ queue_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+out:
+ mutex_unlock(&mvm->mutex);
+}
+
+int
+iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_sta *mvmsta;
+ unsigned int delay;
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n",
+ sta->addr, chandef->chan->center_freq, chandef->width);
+
+ /* we only support a single peer for channel switching */
+ if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) {
+ IWL_DEBUG_TDLS(mvm,
+ "Existing peer. Can't start switch with %pM\n",
+ sta->addr);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+ TDLS_SEND_CHAN_SW_REQ,
+ sta->addr, sta->tdls_initiator,
+ oper_class, chandef, 0, 0, 0,
+ tmpl_skb, ch_sw_tm_ie);
+ if (ret)
+ goto out;
+
+ /*
+ * Mark the peer as "in tdls switch" for this vif. We only allow a
+ * single such peer per vif.
+ */
+ mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL);
+ if (!mvm->tdls_cs.peer.skb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ mvm->tdls_cs.peer.sta_id = mvmsta->sta_id;
+ mvm->tdls_cs.peer.chandef = *chandef;
+ mvm->tdls_cs.peer.initiator = sta->tdls_initiator;
+ mvm->tdls_cs.peer.op_class = oper_class;
+ mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie;
+
+ /*
+ * Wait for 2 DTIM periods before attempting the next switch. The next
+ * switch will be made sooner if the current one completes before that.
+ */
+ delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int);
+ mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_sta *cur_sta;
+ bool wait_for_phy = false;
+
+ mutex_lock(&mvm->mutex);
+
+ IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr);
+
+ /* we only support a single peer for channel switching */
+ if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) {
+ IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr);
+ goto out;
+ }
+
+ cur_sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /* make sure it's the same peer */
+ if (cur_sta != sta)
+ goto out;
+
+ /*
+ * If we're currently in a switch because of the now canceled peer,
+ * wait a DTIM here to make sure the phy is back on the base channel.
+ * We can't otherwise force it.
+ */
+ if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id &&
+ mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE)
+ wait_for_phy = true;
+
+ mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+ dev_kfree_skb(mvm->tdls_cs.peer.skb);
+ mvm->tdls_cs.peer.skb = NULL;
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ /* make sure the phy is on the base channel */
+ if (wait_for_phy)
+ msleep(TU_TO_MS(vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int));
+
+ /* flush the channel switch state */
+ flush_delayed_work(&mvm->tdls_cs.dwork);
+
+ IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr);
+}
+
+void
+iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_tdls_ch_sw_params *params)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ enum iwl_tdls_channel_switch_type type;
+ unsigned int delay;
+ const char *action_str =
+ params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ?
+ "REQ" : "RESP";
+
+ mutex_lock(&mvm->mutex);
+
+ IWL_DEBUG_TDLS(mvm,
+ "Received TDLS ch switch action %s from %pM status %d\n",
+ action_str, params->sta->addr, params->status);
+
+ /*
+ * we got a non-zero status from a peer we were switching to - move to
+ * the idle state and retry again later
+ */
+ if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE &&
+ params->status != 0 &&
+ mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+ mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+ struct ieee80211_sta *cur_sta;
+
+ /* make sure it's the same peer */
+ cur_sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (cur_sta == params->sta) {
+ iwl_mvm_tdls_update_cs_state(mvm,
+ IWL_MVM_TDLS_SW_IDLE);
+ goto retry;
+ }
+ }
+
+ type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ?
+ TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH;
+
+ iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr,
+ params->sta->tdls_initiator, 0,
+ params->chandef, params->timestamp,
+ params->switch_time,
+ params->switch_timeout,
+ params->tmpl_skb,
+ params->ch_sw_tm_ie);
+
+retry:
+ /* register a timeout in case we don't succeed in switching */
+ delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
+ 1024 / 1000;
+ mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+ mutex_unlock(&mvm->mutex);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h
new file mode 100644
index 000000000000..cbbc16fd006a
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_MVM_TESTMODE_H__
+#define __IWL_MVM_TESTMODE_H__
+
+/**
+ * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA
+ * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute)
+ * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32)
+ * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32)
+ * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32)
+ */
+enum iwl_mvm_testmode_attrs {
+ IWL_MVM_TM_ATTR_UNSPEC,
+ IWL_MVM_TM_ATTR_CMD,
+ IWL_MVM_TM_ATTR_NOA_DURATION,
+ IWL_MVM_TM_ATTR_BEACON_FILTER_STATE,
+
+ /* keep last */
+ NUM_IWL_MVM_TM_ATTRS,
+ IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1,
+};
+
+/**
+ * enum iwl_mvm_testmode_commands - MVM testmode commands
+ * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing
+ * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on
+ */
+enum iwl_mvm_testmode_commands {
+ IWL_MVM_TM_CMD_SET_NOA,
+ IWL_MVM_TM_CMD_SET_BEACON_FILTER,
+};
+
+#endif /* __IWL_MVM_TESTMODE_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
new file mode 100644
index 000000000000..924dd6a41626
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
@@ -0,0 +1,885 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/jiffies.h>
+#include <net/mac80211.h>
+
+#include "iwl-notif-wait.h"
+#include "iwl-trans.h"
+#include "fw-api.h"
+#include "time-event.h"
+#include "mvm.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "fw-dbg.h"
+
+/*
+ * For the high priority TE use a time event type that has similar priority to
+ * the FW's action scan priority.
+ */
+#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
+#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
+
+void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
+ struct iwl_mvm_time_event_data *te_data)
+{
+ lockdep_assert_held(&mvm->time_event_lock);
+
+ if (!te_data->vif)
+ return;
+
+ list_del(&te_data->list);
+ te_data->running = false;
+ te_data->uid = 0;
+ te_data->id = TE_MAX;
+ te_data->vif = NULL;
+}
+
+void iwl_mvm_roc_done_wk(struct work_struct *wk)
+{
+ struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
+ u32 queues = 0;
+
+ /*
+ * Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit.
+ * This will cause the TX path to drop offchannel transmissions.
+ * That would also be done by mac80211, but it is racy, in particular
+ * in the case that the time event actually completed in the firmware
+ * (which is handled in iwl_mvm_te_handle_notif).
+ */
+ if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) {
+ queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
+ }
+ if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) {
+ queues |= BIT(mvm->aux_queue);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX);
+ }
+
+ synchronize_net();
+
+ /*
+ * Flush the offchannel queue -- this is called when the time
+ * event finishes or is canceled, so that frames queued for it
+ * won't get stuck on the queue and be transmitted in the next
+ * time event.
+ * We have to send the command asynchronously since this cannot
+ * be under the mutex for locking reasons, but that's not an
+ * issue as it will have to complete before the next command is
+ * executed, and a new time event means a new command.
+ */
+ iwl_mvm_flush_tx_path(mvm, queues, CMD_ASYNC);
+}
+
+static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
+{
+ /*
+ * Of course, our status bit is just as racy as mac80211, so in
+ * addition, fire off the work struct which will drop all frames
+ * from the hardware queues that made it through the race. First
+ * it will of course synchronize the TX path to make sure that
+ * any *new* TX will be rejected.
+ */
+ schedule_work(&mvm->roc_done_wk);
+}
+
+static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
+{
+ struct ieee80211_vif *csa_vif;
+
+ rcu_read_lock();
+
+ csa_vif = rcu_dereference(mvm->csa_vif);
+ if (!csa_vif || !csa_vif->csa_active)
+ goto out_unlock;
+
+ IWL_DEBUG_TE(mvm, "CSA NOA started\n");
+
+ /*
+ * CSA NoA is started but we still have beacons to
+ * transmit on the current channel.
+ * So we just do nothing here and the switch
+ * will be performed on the last TBTT.
+ */
+ if (!ieee80211_csa_is_complete(csa_vif)) {
+ IWL_WARN(mvm, "CSA NOA started too early\n");
+ goto out_unlock;
+ }
+
+ ieee80211_csa_finish(csa_vif);
+
+ rcu_read_unlock();
+
+ RCU_INIT_POINTER(mvm->csa_vif, NULL);
+
+ return;
+
+out_unlock:
+ rcu_read_unlock();
+}
+
+static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ const char *errmsg)
+{
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return false;
+ if (vif->bss_conf.assoc && vif->bss_conf.dtim_period)
+ return false;
+ if (errmsg)
+ IWL_ERR(mvm, "%s\n", errmsg);
+
+ iwl_mvm_connection_loss(mvm, vif, errmsg);
+ return true;
+}
+
+static void
+iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
+ struct iwl_mvm_time_event_data *te_data,
+ struct iwl_time_event_notif *notif)
+{
+ struct ieee80211_vif *vif = te_data->vif;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!notif->status)
+ IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
+
+ switch (te_data->vif->type) {
+ case NL80211_IFTYPE_AP:
+ if (!notif->status)
+ mvmvif->csa_failed = true;
+ iwl_mvm_csa_noa_start(mvm);
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!notif->status) {
+ iwl_mvm_connection_loss(mvm, vif,
+ "CSA TE failed to start");
+ break;
+ }
+ iwl_mvm_csa_client_absent(mvm, te_data->vif);
+ ieee80211_chswitch_done(te_data->vif, true);
+ break;
+ default:
+ /* should never happen */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ /* we don't need it anymore */
+ iwl_mvm_te_clear_data(mvm, te_data);
+}
+
+static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
+ struct iwl_time_event_notif *notif,
+ struct iwl_mvm_time_event_data *te_data)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_time_event *te_trig;
+ int i;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT);
+ te_trig = (void *)trig->data;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
+ u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
+ u32 trig_action_bitmap =
+ le32_to_cpu(te_trig->time_events[i].action_bitmap);
+ u32 trig_status_bitmap =
+ le32_to_cpu(te_trig->time_events[i].status_bitmap);
+
+ if (trig_te_id != te_data->id ||
+ !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
+ !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
+ continue;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "Time event %d Action 0x%x received status: %d",
+ te_data->id,
+ le32_to_cpu(notif->action),
+ le32_to_cpu(notif->status));
+ break;
+ }
+}
+
+/*
+ * Handles a FW notification for an event that is known to the driver.
+ *
+ * @mvm: the mvm component
+ * @te_data: the time event data
+ * @notif: the notification data corresponding the time event data.
+ */
+static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
+ struct iwl_mvm_time_event_data *te_data,
+ struct iwl_time_event_notif *notif)
+{
+ lockdep_assert_held(&mvm->time_event_lock);
+
+ IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
+ le32_to_cpu(notif->unique_id),
+ le32_to_cpu(notif->action));
+
+ iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
+ /*
+ * The FW sends the start/end time event notifications even for events
+ * that it fails to schedule. This is indicated in the status field of
+ * the notification. This happens in cases that the scheduler cannot
+ * find a schedule that can handle the event (for example requesting a
+ * P2P Device discoveribility, while there are other higher priority
+ * events in the system).
+ */
+ if (!le32_to_cpu(notif->status)) {
+ const char *msg;
+
+ if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
+ msg = "Time Event start notification failure";
+ else
+ msg = "Time Event end notification failure";
+
+ IWL_DEBUG_TE(mvm, "%s\n", msg);
+
+ if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
+ iwl_mvm_te_clear_data(mvm, te_data);
+ return;
+ }
+ }
+
+ if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
+ IWL_DEBUG_TE(mvm,
+ "TE ended - current time %lu, estimated end %lu\n",
+ jiffies, te_data->end_jiffies);
+
+ switch (te_data->vif->type) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ ieee80211_remain_on_channel_expired(mvm->hw);
+ iwl_mvm_roc_finished(mvm);
+ break;
+ case NL80211_IFTYPE_STATION:
+ /*
+ * By now, we should have finished association
+ * and know the dtim period.
+ */
+ iwl_mvm_te_check_disconnect(mvm, te_data->vif,
+ "No association and the time event is over already...");
+ break;
+ default:
+ break;
+ }
+
+ iwl_mvm_te_clear_data(mvm, te_data);
+ } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
+ te_data->running = true;
+ te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
+
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+ iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
+ ieee80211_ready_on_channel(mvm->hw);
+ } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
+ iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
+ }
+ } else {
+ IWL_WARN(mvm, "Got TE with unknown action\n");
+ }
+}
+
+/*
+ * Handle A Aux ROC time event
+ */
+static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
+ struct iwl_time_event_notif *notif)
+{
+ struct iwl_mvm_time_event_data *te_data, *tmp;
+ bool aux_roc_te = false;
+
+ list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) {
+ if (le32_to_cpu(notif->unique_id) == te_data->uid) {
+ aux_roc_te = true;
+ break;
+ }
+ }
+ if (!aux_roc_te) /* Not a Aux ROC time event */
+ return -EINVAL;
+
+ iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
+ if (!le32_to_cpu(notif->status)) {
+ IWL_DEBUG_TE(mvm,
+ "ERROR: Aux ROC Time Event %s notification failure\n",
+ (le32_to_cpu(notif->action) &
+ TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end");
+ return -EINVAL;
+ }
+
+ IWL_DEBUG_TE(mvm,
+ "Aux ROC time event notification - UID = 0x%x action %d\n",
+ le32_to_cpu(notif->unique_id),
+ le32_to_cpu(notif->action));
+
+ if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
+ /* End TE, notify mac80211 */
+ ieee80211_remain_on_channel_expired(mvm->hw);
+ iwl_mvm_roc_finished(mvm); /* flush aux queue */
+ list_del(&te_data->list); /* remove from list */
+ te_data->running = false;
+ te_data->vif = NULL;
+ te_data->uid = 0;
+ te_data->id = TE_MAX;
+ } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
+ set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
+ te_data->running = true;
+ iwl_mvm_ref(mvm, IWL_MVM_REF_ROC_AUX);
+ ieee80211_ready_on_channel(mvm->hw); /* Start TE */
+ } else {
+ IWL_DEBUG_TE(mvm,
+ "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
+ le32_to_cpu(notif->action));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The Rx handler for time event notifications
+ */
+void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_time_event_notif *notif = (void *)pkt->data;
+ struct iwl_mvm_time_event_data *te_data, *tmp;
+
+ IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
+ le32_to_cpu(notif->unique_id),
+ le32_to_cpu(notif->action));
+
+ spin_lock_bh(&mvm->time_event_lock);
+ /* This time event is triggered for Aux ROC request */
+ if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
+ goto unlock;
+
+ list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
+ if (le32_to_cpu(notif->unique_id) == te_data->uid)
+ iwl_mvm_te_handle_notif(mvm, te_data, notif);
+ }
+unlock:
+ spin_unlock_bh(&mvm->time_event_lock);
+}
+
+static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mvm *mvm =
+ container_of(notif_wait, struct iwl_mvm, notif_wait);
+ struct iwl_mvm_time_event_data *te_data = data;
+ struct iwl_time_event_notif *resp;
+ int resp_len = iwl_rx_packet_payload_len(pkt);
+
+ if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION))
+ return true;
+
+ if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
+ IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n");
+ return true;
+ }
+
+ resp = (void *)pkt->data;
+
+ /* te_data->uid is already set in the TIME_EVENT_CMD response */
+ if (le32_to_cpu(resp->unique_id) != te_data->uid)
+ return false;
+
+ IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
+ te_data->uid);
+ if (!resp->status)
+ IWL_ERR(mvm,
+ "TIME_EVENT_NOTIFICATION received but not executed\n");
+
+ return true;
+}
+
+static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mvm *mvm =
+ container_of(notif_wait, struct iwl_mvm, notif_wait);
+ struct iwl_mvm_time_event_data *te_data = data;
+ struct iwl_time_event_resp *resp;
+ int resp_len = iwl_rx_packet_payload_len(pkt);
+
+ if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
+ return true;
+
+ if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
+ IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
+ return true;
+ }
+
+ resp = (void *)pkt->data;
+
+ /* we should never get a response to another TIME_EVENT_CMD here */
+ if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
+ return false;
+
+ te_data->uid = le32_to_cpu(resp->unique_id);
+ IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
+ te_data->uid);
+ return true;
+}
+
+static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mvm_time_event_data *te_data,
+ struct iwl_time_event_cmd *te_cmd)
+{
+ static const u16 time_event_response[] = { TIME_EVENT_CMD };
+ struct iwl_notification_wait wait_time_event;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
+ le32_to_cpu(te_cmd->duration));
+
+ spin_lock_bh(&mvm->time_event_lock);
+ if (WARN_ON(te_data->id != TE_MAX)) {
+ spin_unlock_bh(&mvm->time_event_lock);
+ return -EIO;
+ }
+ te_data->vif = vif;
+ te_data->duration = le32_to_cpu(te_cmd->duration);
+ te_data->id = le32_to_cpu(te_cmd->id);
+ list_add_tail(&te_data->list, &mvm->time_event_list);
+ spin_unlock_bh(&mvm->time_event_lock);
+
+ /*
+ * Use a notification wait, which really just processes the
+ * command response and doesn't wait for anything, in order
+ * to be able to process the response and get the UID inside
+ * the RX path. Using CMD_WANT_SKB doesn't work because it
+ * stores the buffer and then wakes up this thread, by which
+ * time another notification (that the time event started)
+ * might already be processed unsuccessfully.
+ */
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
+ time_event_response,
+ ARRAY_SIZE(time_event_response),
+ iwl_mvm_time_event_response, te_data);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
+ sizeof(*te_cmd), te_cmd);
+ if (ret) {
+ IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
+ iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
+ goto out_clear_te;
+ }
+
+ /* No need to wait for anything, so just pass 1 (0 isn't valid) */
+ ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
+ /* should never fail */
+ WARN_ON_ONCE(ret);
+
+ if (ret) {
+ out_clear_te:
+ spin_lock_bh(&mvm->time_event_lock);
+ iwl_mvm_te_clear_data(mvm, te_data);
+ spin_unlock_bh(&mvm->time_event_lock);
+ }
+ return ret;
+}
+
+void iwl_mvm_protect_session(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ u32 max_delay, bool wait_for_notif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+ const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION };
+ struct iwl_notification_wait wait_te_notif;
+ struct iwl_time_event_cmd time_cmd = {};
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (te_data->running &&
+ time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
+ IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
+ jiffies_to_msecs(te_data->end_jiffies - jiffies));
+ return;
+ }
+
+ if (te_data->running) {
+ IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
+ te_data->uid,
+ jiffies_to_msecs(te_data->end_jiffies - jiffies));
+ /*
+ * we don't have enough time
+ * cancel the current TE and issue a new one
+ * Of course it would be better to remove the old one only
+ * when the new one is added, but we don't care if we are off
+ * channel for a bit. All we need to do, is not to return
+ * before we actually begin to be on the channel.
+ */
+ iwl_mvm_stop_session_protection(mvm, vif);
+ }
+
+ time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+ time_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+ time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
+
+ time_cmd.apply_time = cpu_to_le32(0);
+
+ time_cmd.max_frags = TE_V2_FRAG_NONE;
+ time_cmd.max_delay = cpu_to_le32(max_delay);
+ /* TODO: why do we need to interval = bi if it is not periodic? */
+ time_cmd.interval = cpu_to_le32(1);
+ time_cmd.duration = cpu_to_le32(duration);
+ time_cmd.repeat = 1;
+ time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+ TE_V2_NOTIF_HOST_EVENT_END |
+ T2_V2_START_IMMEDIATELY);
+
+ if (!wait_for_notif) {
+ iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
+ return;
+ }
+
+ /*
+ * Create notification_wait for the TIME_EVENT_NOTIFICATION to use
+ * right after we send the time event
+ */
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif,
+ te_notif_response,
+ ARRAY_SIZE(te_notif_response),
+ iwl_mvm_te_notif, te_data);
+
+ /* If TE was sent OK - wait for the notification that started */
+ if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) {
+ IWL_ERR(mvm, "Failed to add TE to protect session\n");
+ iwl_remove_notification(&mvm->notif_wait, &wait_te_notif);
+ } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif,
+ TU_TO_JIFFIES(max_delay))) {
+ IWL_ERR(mvm, "Failed to protect session until TE\n");
+ }
+}
+
+static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+ struct iwl_mvm_time_event_data *te_data,
+ u32 *uid)
+{
+ u32 id;
+
+ /*
+ * It is possible that by the time we got to this point the time
+ * event was already removed.
+ */
+ spin_lock_bh(&mvm->time_event_lock);
+
+ /* Save time event uid before clearing its data */
+ *uid = te_data->uid;
+ id = te_data->id;
+
+ /*
+ * The clear_data function handles time events that were already removed
+ */
+ iwl_mvm_te_clear_data(mvm, te_data);
+ spin_unlock_bh(&mvm->time_event_lock);
+
+ /*
+ * It is possible that by the time we try to remove it, the time event
+ * has already ended and removed. In such a case there is no need to
+ * send a removal command.
+ */
+ if (id == TE_MAX) {
+ IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Explicit request to remove a aux roc time event. The removal of a time
+ * event needs to be synchronized with the flow of a time event's end
+ * notification, which also removes the time event from the op mode
+ * data structures.
+ */
+static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ struct iwl_mvm_time_event_data *te_data)
+{
+ struct iwl_hs20_roc_req aux_cmd = {};
+ u32 uid;
+ int ret;
+
+ if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+ return;
+
+ aux_cmd.event_unique_id = cpu_to_le32(uid);
+ aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+ aux_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+ IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
+ le32_to_cpu(aux_cmd.event_unique_id));
+ ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
+ sizeof(aux_cmd), &aux_cmd);
+
+ if (WARN_ON(ret))
+ return;
+}
+
+/*
+ * Explicit request to remove a time event. The removal of a time event needs to
+ * be synchronized with the flow of a time event's end notification, which also
+ * removes the time event from the op mode data structures.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ struct iwl_mvm_time_event_data *te_data)
+{
+ struct iwl_time_event_cmd time_cmd = {};
+ u32 uid;
+ int ret;
+
+ if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+ return;
+
+ /* When we remove a TE, the UID is to be set in the id field */
+ time_cmd.id = cpu_to_le32(uid);
+ time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+ time_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+
+ IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
+ ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
+ sizeof(time_cmd), &time_cmd);
+ if (WARN_ON(ret))
+ return;
+}
+
+void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+
+ lockdep_assert_held(&mvm->mutex);
+ iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+}
+
+int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ int duration, enum ieee80211_roc_type type)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+ struct iwl_time_event_cmd time_cmd = {};
+
+ lockdep_assert_held(&mvm->mutex);
+ if (te_data->running) {
+ IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Flush the done work, just in case it's still pending, so that
+ * the work it does can complete and we can accept new frames.
+ */
+ flush_work(&mvm->roc_done_wk);
+
+ time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+ time_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+
+ switch (type) {
+ case IEEE80211_ROC_TYPE_NORMAL:
+ time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
+ break;
+ case IEEE80211_ROC_TYPE_MGMT_TX:
+ time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
+ break;
+ default:
+ WARN_ONCE(1, "Got an invalid ROC type\n");
+ return -EINVAL;
+ }
+
+ time_cmd.apply_time = cpu_to_le32(0);
+ time_cmd.interval = cpu_to_le32(1);
+
+ /*
+ * The P2P Device TEs can have lower priority than other events
+ * that are being scheduled by the driver/fw, and thus it might not be
+ * scheduled. To improve the chances of it being scheduled, allow them
+ * to be fragmented, and in addition allow them to be delayed.
+ */
+ time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
+ time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
+ time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
+ time_cmd.repeat = 1;
+ time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+ TE_V2_NOTIF_HOST_EVENT_END |
+ T2_V2_START_IMMEDIATELY);
+
+ return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
+}
+
+static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_time_event_data *te_data;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ spin_lock_bh(&mvm->time_event_lock);
+
+ /*
+ * Iterate over the list of time events and find the time event that is
+ * associated with a P2P_DEVICE interface.
+ * This assumes that a P2P_DEVICE interface can have only a single time
+ * event at any given time and this time event coresponds to a ROC
+ * request
+ */
+ list_for_each_entry(te_data, &mvm->time_event_list, list) {
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ goto out;
+ }
+
+ /* There can only be at most one AUX ROC time event, we just use the
+ * list to simplify/unify code. Remove it if it exists.
+ */
+ te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
+ struct iwl_mvm_time_event_data,
+ list);
+out:
+ spin_unlock_bh(&mvm->time_event_lock);
+ return te_data;
+}
+
+void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_time_event_data *te_data;
+ u32 uid;
+
+ te_data = iwl_mvm_get_roc_te(mvm);
+ if (te_data)
+ __iwl_mvm_remove_time_event(mvm, te_data, &uid);
+}
+
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_vif *mvmvif;
+ struct iwl_mvm_time_event_data *te_data;
+
+ te_data = iwl_mvm_get_roc_te(mvm);
+ if (!te_data) {
+ IWL_WARN(mvm, "No remain on channel event\n");
+ return;
+ }
+
+ mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+ else
+ iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
+
+ iwl_mvm_roc_finished(mvm);
+}
+
+int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 apply_time)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+ struct iwl_time_event_cmd time_cmd = {};
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (te_data->running) {
+ IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");
+ return -EBUSY;
+ }
+
+ time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+ time_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+ time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD);
+ time_cmd.apply_time = cpu_to_le32(apply_time);
+ time_cmd.max_frags = TE_V2_FRAG_NONE;
+ time_cmd.duration = cpu_to_le32(duration);
+ time_cmd.repeat = 1;
+ time_cmd.interval = cpu_to_le32(1);
+ time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+ TE_V2_ABSENCE);
+
+ return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h
new file mode 100644
index 000000000000..99d9a35ad5b1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h
@@ -0,0 +1,250 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __time_event_h__
+#define __time_event_h__
+
+#include "fw-api.h"
+
+#include "mvm.h"
+
+/**
+ * DOC: Time Events - what is it?
+ *
+ * Time Events are a fw feature that allows the driver to control the presence
+ * of the device on the channel. Since the fw supports multiple channels
+ * concurrently, the fw may choose to jump to another channel at any time.
+ * In order to make sure that the fw is on a specific channel at a certain time
+ * and for a certain duration, the driver needs to issue a time event.
+ *
+ * The simplest example is for BSS association. The driver issues a time event,
+ * waits for it to start, and only then tells mac80211 that we can start the
+ * association. This way, we make sure that the association will be done
+ * smoothly and won't be interrupted by channel switch decided within the fw.
+ */
+
+ /**
+ * DOC: The flow against the fw
+ *
+ * When the driver needs to make sure we are in a certain channel, at a certain
+ * time and for a certain duration, it sends a Time Event. The flow against the
+ * fw goes like this:
+ * 1) Driver sends a TIME_EVENT_CMD to the fw
+ * 2) Driver gets the response for that command. This response contains the
+ * Unique ID (UID) of the event.
+ * 3) The fw sends notification when the event starts.
+ *
+ * Of course the API provides various options that allow to cover parameters
+ * of the flow.
+ * What is the duration of the event?
+ * What is the start time of the event?
+ * Is there an end-time for the event?
+ * How much can the event be delayed?
+ * Can the event be split?
+ * If yes what is the maximal number of chunks?
+ * etc...
+ */
+
+/**
+ * DOC: Abstraction to the driver
+ *
+ * In order to simplify the use of time events to the rest of the driver,
+ * we abstract the use of time events. This component provides the functions
+ * needed by the driver.
+ */
+
+#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500
+#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400
+
+/**
+ * iwl_mvm_protect_session - start / extend the session protection.
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the session is issued
+ * @duration: the duration of the session in TU.
+ * @min_duration: will start a new session if the current session will end
+ * in less than min_duration.
+ * @max_delay: maximum delay before starting the time event (in TU)
+ * @wait_for_notif: true if it is required that a time event notification be
+ * waited for (that the time event has been scheduled before returning)
+ *
+ * This function can be used to start a session protection which means that the
+ * fw will stay on the channel for %duration_ms milliseconds. This function
+ * can block (sleep) until the session starts. This function can also be used
+ * to extend a currently running session.
+ * This function is meant to be used for BSS association for example, where we
+ * want to make sure that the fw stays on the channel during the association.
+ */
+void iwl_mvm_protect_session(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ u32 max_delay, bool wait_for_notif);
+
+/**
+ * iwl_mvm_stop_session_protection - cancel the session protection.
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the session is issued
+ *
+ * This functions cancels the session protection which is an act of good
+ * citizenship. If it is not needed any more it should be canceled because
+ * the other bindings wait for the medium during that time.
+ * This funtions doesn't sleep.
+ */
+void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+
+/*
+ * iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION.
+ */
+void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+
+/**
+ * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the roc is requested. It is assumed
+ * that the vif type is NL80211_IFTYPE_P2P_DEVICE
+ * @duration: the requested duration in millisecond for the fw to be on the
+ * channel that is bound to the vif.
+ * @type: the remain on channel request type
+ *
+ * This function can be used to issue a remain on channel session,
+ * which means that the fw will stay in the channel for the request %duration
+ * milliseconds. The function is async, meaning that it only issues the ROC
+ * request but does not wait for it to start. Once the FW is ready to serve the
+ * ROC request, it will issue a notification to the driver that it is on the
+ * requested channel. Once the FW completes the ROC request it will issue
+ * another notification to the driver.
+ */
+int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ int duration, enum ieee80211_roc_type type);
+
+/**
+ * iwl_mvm_stop_roc - stop remain on channel functionality
+ * @mvm: the mvm component
+ *
+ * This function can be used to cancel an ongoing ROC session.
+ * The function is async, it will instruct the FW to stop serving the ROC
+ * session, but will not wait for the actual stopping of the session.
+ */
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm);
+
+/**
+ * iwl_mvm_remove_time_event - general function to clean up of time event
+ * @mvm: the mvm component
+ * @vif: the vif to which the time event belongs
+ * @te_data: the time event data that corresponds to that time event
+ *
+ * This function can be used to cancel a time event regardless its type.
+ * It is useful for cleaning up time events running before removing an
+ * interface.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ struct iwl_mvm_time_event_data *te_data);
+
+/**
+ * iwl_mvm_te_clear_data - remove time event from list
+ * @mvm: the mvm component
+ * @te_data: the time event data to remove
+ *
+ * This function is mostly internal, it is made available here only
+ * for firmware restart purposes.
+ */
+void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
+ struct iwl_mvm_time_event_data *te_data);
+
+void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm);
+void iwl_mvm_roc_done_wk(struct work_struct *wk);
+
+/**
+ * iwl_mvm_schedule_csa_period - request channel switch absence period
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the channel switch is issued
+ * @duration: the duration of the NoA in TU.
+ * @apply_time: NoA start time in GP2.
+ *
+ * This function is used to schedule NoA time event and is used to perform
+ * the channel switch flow.
+ */
+int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 apply_time);
+
+/**
+ * iwl_mvm_te_scheduled - check if the fw received the TE cmd
+ * @te_data: the time event data that corresponds to that time event
+ *
+ * This function returns true iff this TE is added to the fw.
+ */
+static inline bool
+iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data)
+{
+ if (!te_data)
+ return false;
+
+ return !!te_data->uid;
+}
+
+#endif /* __time_event_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c
new file mode 100644
index 000000000000..a1947d6f3a2c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c
@@ -0,0 +1,306 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "mvm.h"
+#include "fw-api-tof.h"
+
+#define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256
+
+void iwl_mvm_tof_init(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
+ return;
+
+ memset(tof_data, 0, sizeof(*tof_data));
+
+ tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (IWL_MVM_TOF_IS_RESPONDER) {
+ tof_data->responder_cfg.sub_grp_cmd_id =
+ cpu_to_le32(TOF_RESPONDER_CONFIG_CMD);
+ tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT;
+ }
+#endif
+
+ tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD);
+ tof_data->range_req.req_timeout = 1;
+ tof_data->range_req.initiator = 1;
+ tof_data->range_req.report_policy = 3;
+
+ tof_data->range_req_ext.sub_grp_cmd_id =
+ cpu_to_le32(TOF_RANGE_REQ_EXT_CMD);
+
+ mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
+}
+
+void iwl_mvm_tof_clean(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
+ return;
+
+ memset(tof_data, 0, sizeof(*tof_data));
+ mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
+}
+
+static void iwl_tof_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ bool *enabled = _data;
+
+ /* non bss vif exists */
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
+ *enabled = false;
+}
+
+int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm)
+{
+ struct iwl_tof_config_cmd *cmd = &mvm->tof_data.tof_cfg;
+ bool enabled;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
+ return -EINVAL;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_tof_iterator, &enabled);
+ if (!enabled) {
+ IWL_DEBUG_INFO(mvm, "ToF is not supported (non bss vif)\n");
+ return -EINVAL;
+ }
+
+ mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
+ return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
+ IWL_ALWAYS_LONG_GROUP, 0),
+ 0, sizeof(*cmd), cmd);
+}
+
+int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id)
+{
+ struct iwl_tof_range_abort_cmd cmd = {
+ .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD),
+ .request_id = id,
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
+ return -EINVAL;
+
+ if (id != mvm->tof_data.active_range_request) {
+ IWL_ERR(mvm, "Invalid range request id %d (active %d)\n",
+ id, mvm->tof_data.active_range_request);
+ return -EINVAL;
+ }
+
+ /* after abort is sent there's no active request anymore */
+ mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
+
+ return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
+ IWL_ALWAYS_LONG_GROUP, 0),
+ 0, sizeof(cmd), &cmd);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_tof_responder_config_cmd *cmd = &mvm->tof_data.responder_cfg;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
+ return -EINVAL;
+
+ if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
+ !mvmvif->ap_ibss_active) {
+ IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
+ return -EIO;
+ }
+
+ cmd->sta_id = mvmvif->bcast_sta.sta_id;
+ memcpy(cmd->bssid, vif->addr, ETH_ALEN);
+ return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
+ IWL_ALWAYS_LONG_GROUP, 0),
+ 0, sizeof(*cmd), cmd);
+}
+#endif
+
+int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_host_cmd cmd = {
+ .id = iwl_cmd_id(TOF_CMD, IWL_ALWAYS_LONG_GROUP, 0),
+ .len = { sizeof(mvm->tof_data.range_req), },
+ /* no copy because of the command size */
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
+ return -EINVAL;
+
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
+ IWL_ERR(mvm, "Cannot send range request, not STA mode\n");
+ return -EIO;
+ }
+
+ /* nesting of range requests is not supported in FW */
+ if (mvm->tof_data.active_range_request !=
+ IWL_MVM_TOF_RANGE_REQ_MAX_ID) {
+ IWL_ERR(mvm, "Cannot send range req, already active req %d\n",
+ mvm->tof_data.active_range_request);
+ return -EIO;
+ }
+
+ mvm->tof_data.active_range_request = mvm->tof_data.range_req.request_id;
+
+ cmd.data[0] = &mvm->tof_data.range_req;
+ return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
+ return -EINVAL;
+
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
+ IWL_ERR(mvm, "Cannot send ext range req, not in STA mode\n");
+ return -EIO;
+ }
+
+ return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
+ IWL_ALWAYS_LONG_GROUP, 0),
+ 0, sizeof(mvm->tof_data.range_req_ext),
+ &mvm->tof_data.range_req_ext);
+}
+
+static int iwl_mvm_tof_range_resp(struct iwl_mvm *mvm, void *data)
+{
+ struct iwl_tof_range_rsp_ntfy *resp = (void *)data;
+
+ if (resp->request_id != mvm->tof_data.active_range_request) {
+ IWL_ERR(mvm, "Request id mismatch, got %d, active %d\n",
+ resp->request_id, mvm->tof_data.active_range_request);
+ return -EIO;
+ }
+
+ memcpy(&mvm->tof_data.range_resp, resp,
+ sizeof(struct iwl_tof_range_rsp_ntfy));
+ mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
+
+ return 0;
+}
+
+static int iwl_mvm_tof_mcsi_notif(struct iwl_mvm *mvm, void *data)
+{
+ struct iwl_tof_mcsi_notif *resp = (struct iwl_tof_mcsi_notif *)data;
+
+ IWL_DEBUG_INFO(mvm, "MCSI notification, token %d\n", resp->token);
+ return 0;
+}
+
+static int iwl_mvm_tof_nb_report_notif(struct iwl_mvm *mvm, void *data)
+{
+ struct iwl_tof_neighbor_report *report =
+ (struct iwl_tof_neighbor_report *)data;
+
+ IWL_DEBUG_INFO(mvm, "NB report, bssid %pM, token %d, status 0x%x\n",
+ report->bssid, report->request_token, report->status);
+ return 0;
+}
+
+void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_tof_gen_resp_cmd *resp = (void *)pkt->data;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ switch (le32_to_cpu(resp->sub_grp_cmd_id)) {
+ case TOF_RANGE_RESPONSE_NOTIF:
+ iwl_mvm_tof_range_resp(mvm, resp->data);
+ break;
+ case TOF_MCSI_DEBUG_NOTIF:
+ iwl_mvm_tof_mcsi_notif(mvm, resp->data);
+ break;
+ case TOF_NEIGHBOR_REPORT_RSP_NOTIF:
+ iwl_mvm_tof_nb_report_notif(mvm, resp->data);
+ break;
+ default:
+ IWL_ERR(mvm, "Unknown sub-group command 0x%x\n",
+ resp->sub_grp_cmd_id);
+ break;
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h
new file mode 100644
index 000000000000..8c3421c9991d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h
@@ -0,0 +1,94 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __tof_h__
+#define __tof_h__
+
+#include "fw-api-tof.h"
+
+struct iwl_mvm_tof_data {
+ struct iwl_tof_config_cmd tof_cfg;
+ struct iwl_tof_range_req_cmd range_req;
+ struct iwl_tof_range_req_ext_cmd range_req_ext;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct iwl_tof_responder_config_cmd responder_cfg;
+#endif
+ struct iwl_tof_range_rsp_ntfy range_resp;
+ u8 last_abort_id;
+ u16 active_range_request;
+};
+
+void iwl_mvm_tof_init(struct iwl_mvm *mvm);
+void iwl_mvm_tof_clean(struct iwl_mvm *mvm);
+int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm);
+int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id);
+int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+#endif
+#endif /* __tof_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
new file mode 100644
index 000000000000..fb76004eede4
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -0,0 +1,460 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include "mvm.h"
+
+#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
+
+static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
+ u32 duration = tt->params.ct_kill_duration;
+
+ if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
+ return;
+
+ IWL_ERR(mvm, "Enter CT Kill\n");
+ iwl_mvm_set_hw_ctkill_state(mvm, true);
+
+ tt->throttle = false;
+ tt->dynamic_smps = false;
+
+ /* Don't schedule an exit work if we're in test mode, since
+ * the temperature will not change unless we manually set it
+ * again (or disable testing).
+ */
+ if (!mvm->temperature_test)
+ schedule_delayed_work(&tt->ct_kill_exit,
+ round_jiffies_relative(duration * HZ));
+}
+
+static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
+{
+ if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
+ return;
+
+ IWL_ERR(mvm, "Exit CT Kill\n");
+ iwl_mvm_set_hw_ctkill_state(mvm, false);
+}
+
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
+{
+ /* ignore the notification if we are in test mode */
+ if (mvm->temperature_test)
+ return;
+
+ if (mvm->temperature == temp)
+ return;
+
+ mvm->temperature = temp;
+ iwl_mvm_tt_handler(mvm);
+}
+
+static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_dts_measurement_notif *notif;
+ int len = iwl_rx_packet_payload_len(pkt);
+ int temp;
+
+ if (WARN_ON_ONCE(len < sizeof(*notif))) {
+ IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
+ return -EINVAL;
+ }
+
+ notif = (void *)pkt->data;
+
+ temp = le32_to_cpu(notif->temp);
+
+ /* shouldn't be negative, but since it's s32, make sure it isn't */
+ if (WARN_ON_ONCE(temp < 0))
+ temp = 0;
+
+ IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
+
+ return temp;
+}
+
+static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mvm *mvm =
+ container_of(notif_wait, struct iwl_mvm, notif_wait);
+ int *temp = data;
+ int ret;
+
+ ret = iwl_mvm_temp_notif_parse(mvm, pkt);
+ if (ret < 0)
+ return true;
+
+ *temp = ret;
+
+ return true;
+}
+
+void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ int temp;
+
+ /* the notification is handled synchronously in ctkill, so skip here */
+ if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
+ return;
+
+ temp = iwl_mvm_temp_notif_parse(mvm, pkt);
+ if (temp < 0)
+ return;
+
+ iwl_mvm_tt_temp_changed(mvm, temp);
+}
+
+static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
+{
+ struct iwl_dts_measurement_cmd cmd = {
+ .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
+ };
+ struct iwl_ext_dts_measurement_cmd extcmd = {
+ .control_mode = cpu_to_le32(DTS_AUTOMATIC),
+ };
+ u32 cmdid;
+
+ if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR))
+ cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE,
+ PHY_OPS_GROUP, 0);
+ else
+ cmdid = CMD_DTS_MEASUREMENT_TRIGGER;
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE))
+ return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd);
+
+ return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd);
+}
+
+int iwl_mvm_get_temp(struct iwl_mvm *mvm)
+{
+ struct iwl_notification_wait wait_temp_notif;
+ static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
+ DTS_MEASUREMENT_NOTIF_WIDE) };
+ int ret, temp;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR))
+ temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
+ temp_notif, ARRAY_SIZE(temp_notif),
+ iwl_mvm_temp_notif_wait, &temp);
+
+ ret = iwl_mvm_get_temp_cmd(mvm);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret);
+ iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
+ return ret;
+ }
+
+ ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
+ IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
+ if (ret) {
+ IWL_ERR(mvm, "Getting the temperature timed out\n");
+ return ret;
+ }
+
+ return temp;
+}
+
+static void check_exit_ctkill(struct work_struct *work)
+{
+ struct iwl_mvm_tt_mgmt *tt;
+ struct iwl_mvm *mvm;
+ u32 duration;
+ s32 temp;
+
+ tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
+ mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
+
+ duration = tt->params.ct_kill_duration;
+
+ mutex_lock(&mvm->mutex);
+
+ if (__iwl_mvm_mac_start(mvm))
+ goto reschedule;
+
+ /* make sure the device is available for direct read/writes */
+ if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) {
+ __iwl_mvm_mac_stop(mvm);
+ goto reschedule;
+ }
+
+ temp = iwl_mvm_get_temp(mvm);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL);
+
+ __iwl_mvm_mac_stop(mvm);
+
+ if (temp < 0)
+ goto reschedule;
+
+ IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
+
+ if (temp <= tt->params.ct_kill_exit) {
+ mutex_unlock(&mvm->mutex);
+ iwl_mvm_exit_ctkill(mvm);
+ return;
+ }
+
+reschedule:
+ mutex_unlock(&mvm->mutex);
+ schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
+ round_jiffies(duration * HZ));
+}
+
+static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = _data;
+ enum ieee80211_smps_mode smps_mode;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (mvm->thermal_throttle.dynamic_smps)
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+ else
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
+}
+
+static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ int i, err;
+
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta))
+ continue;
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ if (enable == mvmsta->tt_tx_protection)
+ continue;
+ err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
+ if (err) {
+ IWL_ERR(mvm, "Failed to %s Tx protection\n",
+ enable ? "enable" : "disable");
+ } else {
+ IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
+ enable ? "Enable" : "Disable");
+ mvmsta->tt_tx_protection = enable;
+ }
+ }
+}
+
+void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
+{
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_THERMAL_MNG_BACKOFF,
+ .len = { sizeof(u32), },
+ .data = { &backoff, },
+ };
+
+ backoff = max(backoff, mvm->thermal_throttle.min_backoff);
+
+ if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
+ IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
+ backoff);
+ mvm->thermal_throttle.tx_backoff = backoff;
+ } else {
+ IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
+ }
+}
+
+void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
+{
+ struct iwl_tt_params *params = &mvm->thermal_throttle.params;
+ struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
+ s32 temperature = mvm->temperature;
+ bool throttle_enable = false;
+ int i;
+ u32 tx_backoff;
+
+ IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
+
+ if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
+ iwl_mvm_enter_ctkill(mvm);
+ return;
+ }
+
+ if (params->support_ct_kill &&
+ temperature <= params->ct_kill_exit) {
+ iwl_mvm_exit_ctkill(mvm);
+ return;
+ }
+
+ if (params->support_dynamic_smps) {
+ if (!tt->dynamic_smps &&
+ temperature >= params->dynamic_smps_entry) {
+ IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
+ tt->dynamic_smps = true;
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_tt_smps_iterator, mvm);
+ throttle_enable = true;
+ } else if (tt->dynamic_smps &&
+ temperature <= params->dynamic_smps_exit) {
+ IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
+ tt->dynamic_smps = false;
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_tt_smps_iterator, mvm);
+ }
+ }
+
+ if (params->support_tx_protection) {
+ if (temperature >= params->tx_protection_entry) {
+ iwl_mvm_tt_tx_protection(mvm, true);
+ throttle_enable = true;
+ } else if (temperature <= params->tx_protection_exit) {
+ iwl_mvm_tt_tx_protection(mvm, false);
+ }
+ }
+
+ if (params->support_tx_backoff) {
+ tx_backoff = tt->min_backoff;
+ for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
+ if (temperature < params->tx_backoff[i].temperature)
+ break;
+ tx_backoff = max(tt->min_backoff,
+ params->tx_backoff[i].backoff);
+ }
+ if (tx_backoff != tt->min_backoff)
+ throttle_enable = true;
+ if (tt->tx_backoff != tx_backoff)
+ iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
+ }
+
+ if (!tt->throttle && throttle_enable) {
+ IWL_WARN(mvm,
+ "Due to high temperature thermal throttling initiated\n");
+ tt->throttle = true;
+ } else if (tt->throttle && !tt->dynamic_smps &&
+ tt->tx_backoff == tt->min_backoff &&
+ temperature <= params->tx_protection_exit) {
+ IWL_WARN(mvm,
+ "Temperature is back to normal thermal throttling stopped\n");
+ tt->throttle = false;
+ }
+}
+
+static const struct iwl_tt_params iwl_mvm_default_tt_params = {
+ .ct_kill_entry = 118,
+ .ct_kill_exit = 96,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 114,
+ .dynamic_smps_exit = 110,
+ .tx_protection_entry = 114,
+ .tx_protection_exit = 108,
+ .tx_backoff = {
+ {.temperature = 112, .backoff = 200},
+ {.temperature = 113, .backoff = 600},
+ {.temperature = 114, .backoff = 1200},
+ {.temperature = 115, .backoff = 2000},
+ {.temperature = 116, .backoff = 4000},
+ {.temperature = 117, .backoff = 10000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff)
+{
+ struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
+
+ IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
+
+ if (mvm->cfg->thermal_params)
+ tt->params = *mvm->cfg->thermal_params;
+ else
+ tt->params = iwl_mvm_default_tt_params;
+
+ tt->throttle = false;
+ tt->dynamic_smps = false;
+ tt->min_backoff = min_backoff;
+ INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
+}
+
+void iwl_mvm_tt_exit(struct iwl_mvm *mvm)
+{
+ cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
+ IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
new file mode 100644
index 000000000000..8bf48a7d0f4e
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -0,0 +1,1220 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <linux/tcp.h>
+
+#include "iwl-trans.h"
+#include "iwl-eeprom-parse.h"
+#include "mvm.h"
+#include "sta.h"
+#include "fw-dbg.h"
+
+static void
+iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
+ u16 tid, u16 ssn)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_ba *ba_trig;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
+ ba_trig = (void *)trig->data;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
+ return;
+
+ if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid)))
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "BAR sent to %pM, tid %d, ssn %d",
+ addr, tid, ssn);
+}
+
+/*
+ * Sets most of the Tx cmd's fields
+ */
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info, u8 sta_id)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ __le16 fc = hdr->frame_control;
+ u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
+ u32 len = skb->len + FCS_LEN;
+ u8 ac;
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+ tx_flags |= TX_CMD_FLG_ACK;
+ else
+ tx_flags &= ~TX_CMD_FLG_ACK;
+
+ if (ieee80211_is_probe_resp(fc))
+ tx_flags |= TX_CMD_FLG_TSF;
+
+ if (ieee80211_has_morefrags(fc))
+ tx_flags |= TX_CMD_FLG_MORE_FRAG;
+
+ if (ieee80211_is_data_qos(fc)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+ tx_cmd->tid_tspec = qc[0] & 0xf;
+ tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
+ } else if (ieee80211_is_back_req(fc)) {
+ struct ieee80211_bar *bar = (void *)skb->data;
+ u16 control = le16_to_cpu(bar->control);
+ u16 ssn = le16_to_cpu(bar->start_seq_num);
+
+ tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
+ tx_cmd->tid_tspec = (control &
+ IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
+ IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
+ WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT);
+ iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec,
+ ssn);
+ } else {
+ tx_cmd->tid_tspec = IWL_TID_NON_QOS;
+ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+ tx_flags |= TX_CMD_FLG_SEQ_CTL;
+ else
+ tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
+ }
+
+ /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */
+ if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT)
+ ac = tid_to_mac80211_ac[tx_cmd->tid_tspec];
+ else
+ ac = tid_to_mac80211_ac[0];
+
+ tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) <<
+ TX_CMD_FLG_BT_PRIO_POS;
+
+ if (ieee80211_is_mgmt(fc)) {
+ if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
+ tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_ASSOC);
+ else if (ieee80211_is_action(fc))
+ tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE);
+ else
+ tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT);
+
+ /* The spec allows Action frames in A-MPDU, we don't support
+ * it
+ */
+ WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
+ } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) {
+ tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT);
+ } else {
+ tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE);
+ }
+
+ if (ieee80211_is_data(fc) && len > mvm->rts_threshold &&
+ !is_multicast_ether_addr(ieee80211_get_DA(hdr)))
+ tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT) &&
+ ieee80211_action_contains_tpc(skb))
+ tx_flags |= TX_CMD_FLG_WRITE_TX_POWER;
+
+ tx_cmd->tx_flags = cpu_to_le32(tx_flags);
+ /* Total # bytes to be transmitted */
+ tx_cmd->len = cpu_to_le16((u16)skb->len);
+ tx_cmd->next_frame_len = 0;
+ tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+ tx_cmd->sta_id = sta_id;
+}
+
+/*
+ * Sets the fields in the Tx cmd that are rate related
+ */
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, __le16 fc)
+{
+ u32 rate_flags;
+ int rate_idx;
+ u8 rate_plcp;
+
+ /* Set retry limit on RTS packets */
+ tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT;
+
+ /* Set retry limit on DATA packets and Probe Responses*/
+ if (ieee80211_is_probe_resp(fc)) {
+ tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT;
+ tx_cmd->rts_retry_limit =
+ min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit);
+ } else if (ieee80211_is_back_req(fc)) {
+ tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT;
+ } else {
+ tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY;
+ }
+
+ /*
+ * for data packets, rate info comes from the table inside the fw. This
+ * table is controlled by LINK_QUALITY commands
+ */
+
+ if (ieee80211_is_data(fc) && sta) {
+ tx_cmd->initial_rate_index = 0;
+ tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
+ return;
+ } else if (ieee80211_is_back_req(fc)) {
+ tx_cmd->tx_flags |=
+ cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR);
+ }
+
+ /* HT rate doesn't make sense for a non data frame */
+ WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
+ "Got an HT rate (flags:0x%x/mcs:%d) for a non data frame (fc:0x%x)\n",
+ info->control.rates[0].flags,
+ info->control.rates[0].idx,
+ le16_to_cpu(fc));
+
+ rate_idx = info->control.rates[0].idx;
+ /* if the rate isn't a well known legacy rate, take the lowest one */
+ if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY)
+ rate_idx = rate_lowest_index(
+ &mvm->nvm_data->bands[info->band], sta);
+
+ /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate_idx += IWL_FIRST_OFDM_RATE;
+
+ /* For 2.4 GHZ band, check that there is no need to remap */
+ BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+ /* Get PLCP rate for tx_cmd->rate_n_flags */
+ rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
+
+ mvm->mgmt_last_antenna_idx =
+ iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
+ mvm->mgmt_last_antenna_idx);
+
+ if (info->band == IEEE80211_BAND_2GHZ &&
+ !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
+ rate_flags = mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS;
+ else
+ rate_flags =
+ BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+ /* Set CCK flag as needed */
+ if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
+ rate_flags |= RATE_MCS_CCK_MSK;
+
+ /* Set the rate in the TX cmd */
+ tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags);
+}
+
+/*
+ * Sets the fields in the Tx cmd that are crypto related
+ */
+static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+ struct ieee80211_tx_info *info,
+ struct iwl_tx_cmd *tx_cmd,
+ struct sk_buff *skb_frag,
+ int hdrlen)
+{
+ struct ieee80211_key_conf *keyconf = info->control.hw_key;
+ u8 *crypto_hdr = skb_frag->data + hdrlen;
+ u64 pn;
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ iwl_mvm_set_tx_cmd_ccmp(info, tx_cmd);
+ pn = atomic64_inc_return(&keyconf->tx_pn);
+ crypto_hdr[0] = pn;
+ crypto_hdr[2] = 0;
+ crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6);
+ crypto_hdr[1] = pn >> 8;
+ crypto_hdr[4] = pn >> 16;
+ crypto_hdr[5] = pn >> 24;
+ crypto_hdr[6] = pn >> 32;
+ crypto_hdr[7] = pn >> 40;
+ break;
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
+ ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
+ break;
+
+ case WLAN_CIPHER_SUITE_WEP104:
+ tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
+ /* fall through */
+ case WLAN_CIPHER_SUITE_WEP40:
+ tx_cmd->sec_ctl |= TX_CMD_SEC_WEP |
+ ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) &
+ TX_CMD_SEC_WEP_KEY_IDX_MSK);
+
+ memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
+ break;
+ default:
+ tx_cmd->sec_ctl |= TX_CMD_SEC_EXT;
+ }
+}
+
+/*
+ * Allocates and sets the Tx cmd the driver data pointers in the skb
+ */
+static struct iwl_device_cmd *
+iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
+ int hdrlen, struct ieee80211_sta *sta, u8 sta_id)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_device_cmd *dev_cmd;
+ struct iwl_tx_cmd *tx_cmd;
+
+ dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans);
+
+ if (unlikely(!dev_cmd))
+ return NULL;
+
+ memset(dev_cmd, 0, sizeof(*dev_cmd));
+ dev_cmd->hdr.cmd = TX_CMD;
+ tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+
+ if (info->control.hw_key)
+ iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen);
+
+ iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id);
+
+ iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
+
+ memset(&info->status, 0, sizeof(info->status));
+ memset(info->driver_data, 0, sizeof(info->driver_data));
+
+ info->driver_data[1] = dev_cmd;
+
+ return dev_cmd;
+}
+
+int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_device_cmd *dev_cmd;
+ struct iwl_tx_cmd *tx_cmd;
+ u8 sta_id;
+ int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU))
+ return -1;
+
+ if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM &&
+ (!info->control.vif ||
+ info->hw_queue != info->control.vif->cab_queue)))
+ return -1;
+
+ /*
+ * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used
+ * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel
+ * queue. STATION (HS2.0) uses the auxiliary context of the FW,
+ * and hence needs to be sent on the aux queue
+ */
+ if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
+ info->control.vif->type == NL80211_IFTYPE_STATION)
+ IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue;
+
+ /*
+ * If the interface on which the frame is sent is the P2P_DEVICE
+ * or an AP/GO interface use the broadcast station associated
+ * with it; otherwise if the interface is a managed interface
+ * use the AP station associated with it for multicast traffic
+ * (this is not possible for unicast packets as a TLDS discovery
+ * response are sent without a station entry); otherwise use the
+ * AUX station.
+ */
+ sta_id = mvm->aux_sta.sta_id;
+ if (info->control.vif) {
+ struct iwl_mvm_vif *mvmvif =
+ iwl_mvm_vif_from_mac80211(info->control.vif);
+
+ if (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE ||
+ info->control.vif->type == NL80211_IFTYPE_AP)
+ sta_id = mvmvif->bcast_sta.sta_id;
+ else if (info->control.vif->type == NL80211_IFTYPE_STATION &&
+ is_multicast_ether_addr(hdr->addr1)) {
+ u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id);
+
+ if (ap_sta_id != IWL_MVM_STATION_COUNT)
+ sta_id = ap_sta_id;
+ }
+ }
+
+ IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue);
+
+ dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, NULL, sta_id);
+ if (!dev_cmd)
+ return -1;
+
+ /* From now on, we cannot access info->control */
+ tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+
+ /* Copy MAC header from skb into command buffer */
+ memcpy(tx_cmd->hdr, hdr, hdrlen);
+
+ if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) {
+ iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb_gso,
+ struct ieee80211_sta *sta,
+ struct sk_buff_head *mpdus_skb)
+{
+ struct sk_buff *tmp, *next;
+ char cb[sizeof(skb_gso->cb)];
+
+ memcpy(cb, skb_gso->cb, sizeof(cb));
+ next = skb_gso_segment(skb_gso, 0);
+ if (IS_ERR(next))
+ return -EINVAL;
+ else if (next)
+ consume_skb(skb_gso);
+
+ while (next) {
+ tmp = next;
+ next = tmp->next;
+ memcpy(tmp->cb, cb, sizeof(tmp->cb));
+
+ tmp->prev = NULL;
+ tmp->next = NULL;
+
+ __skb_queue_tail(mpdus_skb, tmp);
+ }
+
+ return 0;
+}
+
+/*
+ * Sets the fields in the Tx cmd that are crypto related
+ */
+static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct ieee80211_sta *sta)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_device_cmd *dev_cmd;
+ struct iwl_tx_cmd *tx_cmd;
+ __le16 fc;
+ u16 seq_number = 0;
+ u8 tid = IWL_MAX_TID_COUNT;
+ u8 txq_id = info->hw_queue;
+ bool is_data_qos = false, is_ampdu = false;
+ int hdrlen;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ fc = hdr->frame_control;
+ hdrlen = ieee80211_hdrlen(fc);
+
+ if (WARN_ON_ONCE(!mvmsta))
+ return -1;
+
+ if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
+ return -1;
+
+ dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, sta, mvmsta->sta_id);
+ if (!dev_cmd)
+ goto drop;
+
+ tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+ /* From now on, we cannot access info->control */
+
+ /*
+ * we handle that entirely ourselves -- for uAPSD the firmware
+ * will always send a notification, and for PS-Poll responses
+ * we'll notify mac80211 when getting frame status
+ */
+ info->flags &= ~IEEE80211_TX_STATUS_EOSP;
+
+ spin_lock(&mvmsta->lock);
+
+ if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
+ u8 *qc = NULL;
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ goto drop_unlock_sta;
+
+ seq_number = mvmsta->tid_data[tid].seq_number;
+ seq_number &= IEEE80211_SCTL_SEQ;
+ hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+ hdr->seq_ctrl |= cpu_to_le16(seq_number);
+ is_data_qos = true;
+ is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU;
+ }
+
+ /* Copy MAC header from skb into command buffer */
+ memcpy(tx_cmd->hdr, hdr, hdrlen);
+
+ WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
+
+ if (sta->tdls) {
+ /* default to TID 0 for non-QoS packets */
+ u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid;
+
+ txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]];
+ }
+
+ if (is_ampdu) {
+ if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
+ goto drop_unlock_sta;
+ txq_id = mvmsta->tid_data[tid].txq_id;
+ }
+
+ IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
+ tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number));
+
+ if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
+ goto drop_unlock_sta;
+
+ if (is_data_qos && !ieee80211_has_morefrags(fc))
+ mvmsta->tid_data[tid].seq_number = seq_number + 0x10;
+
+ spin_unlock(&mvmsta->lock);
+
+ if (txq_id < mvm->first_agg_queue)
+ atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);
+
+ return 0;
+
+drop_unlock_sta:
+ iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
+ spin_unlock(&mvmsta->lock);
+drop:
+ return -1;
+}
+
+int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct sk_buff_head mpdus_skbs;
+ unsigned int payload_len;
+ int ret;
+
+ if (WARN_ON_ONCE(!mvmsta))
+ return -1;
+
+ if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
+ return -1;
+
+ if (!skb_is_gso(skb))
+ return iwl_mvm_tx_mpdu(mvm, skb, sta);
+
+ payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
+ tcp_hdrlen(skb) + skb->data_len;
+
+ if (payload_len <= skb_shinfo(skb)->gso_size)
+ return iwl_mvm_tx_mpdu(mvm, skb, sta);
+
+ __skb_queue_head_init(&mpdus_skbs);
+
+ ret = iwl_mvm_tx_tso(mvm, skb, sta, &mpdus_skbs);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(skb_queue_empty(&mpdus_skbs)))
+ return ret;
+
+ while (!skb_queue_empty(&mpdus_skbs)) {
+ struct sk_buff *skb = __skb_dequeue(&mpdus_skbs);
+
+ ret = iwl_mvm_tx_mpdu(mvm, skb, sta);
+ if (ret) {
+ __skb_queue_purge(&mpdus_skbs);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta, u8 tid)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+ struct ieee80211_vif *vif = mvmsta->vif;
+
+ lockdep_assert_held(&mvmsta->lock);
+
+ if ((tid_data->state == IWL_AGG_ON ||
+ tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
+ iwl_mvm_tid_queued(tid_data) == 0) {
+ /*
+ * Now that this aggregation queue is empty tell mac80211 so it
+ * knows we no longer have frames buffered for the station on
+ * this TID (for the TIM bitmap calculation.)
+ */
+ ieee80211_sta_set_buffered(sta, tid, false);
+ }
+
+ if (tid_data->ssn != tid_data->next_reclaimed)
+ return;
+
+ switch (tid_data->state) {
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "Can continue addBA flow ssn = next_recl = %d\n",
+ tid_data->next_reclaimed);
+ tid_data->state = IWL_AGG_STARTING;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+
+ case IWL_EMPTYING_HW_QUEUE_DELBA:
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "Can continue DELBA flow ssn = next_recl = %d\n",
+ tid_data->next_reclaimed);
+ iwl_mvm_disable_txq(mvm, tid_data->txq_id,
+ vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
+ CMD_ASYNC);
+ tid_data->state = IWL_AGG_OFF;
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+
+ default:
+ break;
+ }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_mvm_get_tx_fail_reason(u32 status)
+{
+#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
+#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+
+ switch (status & TX_STATUS_MSK) {
+ case TX_STATUS_SUCCESS:
+ return "SUCCESS";
+ TX_STATUS_POSTPONE(DELAY);
+ TX_STATUS_POSTPONE(FEW_BYTES);
+ TX_STATUS_POSTPONE(BT_PRIO);
+ TX_STATUS_POSTPONE(QUIET_PERIOD);
+ TX_STATUS_POSTPONE(CALC_TTAK);
+ TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
+ TX_STATUS_FAIL(SHORT_LIMIT);
+ TX_STATUS_FAIL(LONG_LIMIT);
+ TX_STATUS_FAIL(UNDERRUN);
+ TX_STATUS_FAIL(DRAIN_FLOW);
+ TX_STATUS_FAIL(RFKILL_FLUSH);
+ TX_STATUS_FAIL(LIFE_EXPIRE);
+ TX_STATUS_FAIL(DEST_PS);
+ TX_STATUS_FAIL(HOST_ABORTED);
+ TX_STATUS_FAIL(BT_RETRY);
+ TX_STATUS_FAIL(STA_INVALID);
+ TX_STATUS_FAIL(FRAG_DROPPED);
+ TX_STATUS_FAIL(TID_DISABLE);
+ TX_STATUS_FAIL(FIFO_FLUSHED);
+ TX_STATUS_FAIL(SMALL_CF_POLL);
+ TX_STATUS_FAIL(FW_DROP);
+ TX_STATUS_FAIL(STA_COLOR_MISMATCH);
+ }
+
+ return "UNKNOWN";
+
+#undef TX_STATUS_FAIL
+#undef TX_STATUS_POSTPONE
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
+ enum ieee80211_band band,
+ struct ieee80211_tx_rate *r)
+{
+ if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+ r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
+ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
+ break;
+ }
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ r->flags |= IEEE80211_TX_RC_SHORT_GI;
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ r->flags |= IEEE80211_TX_RC_MCS;
+ r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+ } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+ ieee80211_rate_set_vht(
+ r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK,
+ ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+ RATE_VHT_MCS_NSS_POS) + 1);
+ r->flags |= IEEE80211_TX_RC_VHT_MCS;
+ } else {
+ r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+ band);
+ }
+}
+
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags,
+ struct ieee80211_tx_info *info)
+{
+ struct ieee80211_tx_rate *r = &info->status.rates[0];
+
+ info->status.antenna =
+ ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+ iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r);
+}
+
+static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{
+ struct ieee80211_sta *sta;
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ int txq_id = SEQ_TO_QUEUE(sequence);
+ struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+ int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
+ int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
+ u32 status = le16_to_cpu(tx_resp->status.status);
+ u16 ssn = iwl_mvm_get_scd_ssn(tx_resp);
+ struct iwl_mvm_sta *mvmsta;
+ struct sk_buff_head skbs;
+ u8 skb_freed = 0;
+ u16 next_reclaimed, seq_ctl;
+
+ __skb_queue_head_init(&skbs);
+
+ seq_ctl = le16_to_cpu(tx_resp->seq_ctl);
+
+ /* we can free until ssn % q.n_bd not inclusive */
+ iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs);
+
+ while (!skb_queue_empty(&skbs)) {
+ struct sk_buff *skb = __skb_dequeue(&skbs);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ skb_freed++;
+
+ iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+
+ memset(&info->status, 0, sizeof(info->status));
+
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
+ /* inform mac80211 about what happened with the frame */
+ switch (status & TX_STATUS_MSK) {
+ case TX_STATUS_SUCCESS:
+ case TX_STATUS_DIRECT_DONE:
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ break;
+ case TX_STATUS_FAIL_DEST_PS:
+ info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+ break;
+ default:
+ break;
+ }
+
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
+ iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate),
+ info);
+ info->status.status_driver_data[1] =
+ (void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate);
+
+ /* Single frame failure in an AMPDU queue => send BAR */
+ if (txq_id >= mvm->first_agg_queue &&
+ !(info->flags & IEEE80211_TX_STAT_ACK) &&
+ !(info->flags & IEEE80211_TX_STAT_TX_FILTERED))
+ info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+
+ /* 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);
+ }
+
+ /*
+ * TODO: this is not accurate if we are freeing more than one
+ * packet.
+ */
+ info->status.tx_time =
+ le16_to_cpu(tx_resp->wireless_media_time);
+ BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
+ info->status.status_driver_data[0] =
+ (void *)(uintptr_t)tx_resp->reduced_tpc;
+
+ ieee80211_tx_status(mvm->hw, skb);
+ }
+
+ if (txq_id >= mvm->first_agg_queue) {
+ /* If this is an aggregation queue, we use the ssn since:
+ * ssn = wifi seq_num % 256.
+ * The seq_ctl is the sequence control of the packet to which
+ * this Tx response relates. But if there is a hole in the
+ * bitmap of the BA we received, this Tx response may allow to
+ * reclaim the hole and all the subsequent packets that were
+ * already acked. In that case, seq_ctl != ssn, and the next
+ * packet to be reclaimed will be ssn and not seq_ctl. In that
+ * case, several packets will be reclaimed even if
+ * frame_count = 1.
+ *
+ * The ssn is the index (% 256) of the latest packet that has
+ * treated (acked / dropped) + 1.
+ */
+ next_reclaimed = ssn;
+ } else {
+ /* The next packet to be reclaimed is the one after this one */
+ next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10);
+ }
+
+ IWL_DEBUG_TX_REPLY(mvm,
+ "TXQ %d status %s (0x%08x)\n",
+ txq_id, iwl_mvm_get_tx_fail_reason(status), status);
+
+ IWL_DEBUG_TX_REPLY(mvm,
+ "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n",
+ le32_to_cpu(tx_resp->initial_rate),
+ tx_resp->failure_frame, SEQ_TO_INDEX(sequence),
+ ssn, next_reclaimed, seq_ctl);
+
+ rcu_read_lock();
+
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+ /*
+ * sta can't be NULL otherwise it'd mean that the sta has been freed in
+ * the firmware while we still have packets for it in the Tx queues.
+ */
+ if (WARN_ON_ONCE(!sta))
+ goto out;
+
+ if (!IS_ERR(sta)) {
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ if (tid != IWL_TID_NON_QOS) {
+ struct iwl_mvm_tid_data *tid_data =
+ &mvmsta->tid_data[tid];
+ bool send_eosp_ndp = false;
+
+ spin_lock_bh(&mvmsta->lock);
+ tid_data->next_reclaimed = next_reclaimed;
+ IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n",
+ next_reclaimed);
+ iwl_mvm_check_ratid_empty(mvm, sta, tid);
+
+ if (mvmsta->sleep_tx_count) {
+ mvmsta->sleep_tx_count--;
+ if (mvmsta->sleep_tx_count &&
+ !iwl_mvm_tid_queued(tid_data)) {
+ /*
+ * The number of frames in the queue
+ * dropped to 0 even if we sent less
+ * frames than we thought we had on the
+ * Tx queue.
+ * This means we had holes in the BA
+ * window that we just filled, ask
+ * mac80211 to send EOSP since the
+ * firmware won't know how to do that.
+ * Send NDP and the firmware will send
+ * EOSP notification that will trigger
+ * a call to ieee80211_sta_eosp().
+ */
+ send_eosp_ndp = true;
+ }
+ }
+
+ spin_unlock_bh(&mvmsta->lock);
+ if (send_eosp_ndp) {
+ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta,
+ IEEE80211_FRAME_RELEASE_UAPSD,
+ 1, tid, false, false);
+ mvmsta->sleep_tx_count = 0;
+ ieee80211_send_eosp_nullfunc(sta, tid);
+ }
+ }
+
+ if (mvmsta->next_status_eosp) {
+ mvmsta->next_status_eosp = false;
+ ieee80211_sta_eosp(sta);
+ }
+ } else {
+ mvmsta = NULL;
+ }
+
+ /*
+ * If the txq is not an AMPDU queue, there is no chance we freed
+ * several skbs. Check that out...
+ */
+ if (txq_id >= mvm->first_agg_queue)
+ goto out;
+
+ /* We can't free more than one frame at once on a shared queue */
+ WARN_ON(skb_freed > 1);
+
+ /* If we have still frames for this STA nothing to do here */
+ if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
+ goto out;
+
+ if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
+
+ /*
+ * If there are no pending frames for this STA and
+ * the tx to this station is not disabled, notify
+ * mac80211 that this station can now wake up in its
+ * STA table.
+ * If mvmsta is not NULL, sta is valid.
+ */
+
+ spin_lock_bh(&mvmsta->lock);
+
+ if (!mvmsta->disable_tx)
+ ieee80211_sta_block_awake(mvm->hw, sta, false);
+
+ spin_unlock_bh(&mvmsta->lock);
+ }
+
+ if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
+ /*
+ * We are draining and this was the last packet - pre_rcu_remove
+ * has been called already. We might be after the
+ * synchronize_net already.
+ * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues.
+ */
+ set_bit(sta_id, mvm->sta_drained);
+ schedule_work(&mvm->sta_drained_wk);
+ }
+
+out:
+ rcu_read_unlock();
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x
+static const char *iwl_get_agg_tx_status(u16 status)
+{
+ switch (status & AGG_TX_STATE_STATUS_MSK) {
+ AGG_TX_STATE_(TRANSMITTED);
+ AGG_TX_STATE_(UNDERRUN);
+ AGG_TX_STATE_(BT_PRIO);
+ AGG_TX_STATE_(FEW_BYTES);
+ AGG_TX_STATE_(ABORT);
+ AGG_TX_STATE_(LAST_SENT_TTL);
+ AGG_TX_STATE_(LAST_SENT_TRY_CNT);
+ AGG_TX_STATE_(LAST_SENT_BT_KILL);
+ AGG_TX_STATE_(SCD_QUERY);
+ AGG_TX_STATE_(TEST_BAD_CRC32);
+ AGG_TX_STATE_(RESPONSE);
+ AGG_TX_STATE_(DUMP_TX);
+ AGG_TX_STATE_(DELAY_TX);
+ }
+
+ return "UNKNOWN";
+}
+
+static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+ struct agg_tx_status *frame_status = &tx_resp->status;
+ int i;
+
+ for (i = 0; i < tx_resp->frame_count; i++) {
+ u16 fstatus = le16_to_cpu(frame_status[i].status);
+
+ IWL_DEBUG_TX_REPLY(mvm,
+ "status %s (0x%04x), try-count (%d) seq (0x%x)\n",
+ iwl_get_agg_tx_status(fstatus),
+ fstatus & AGG_TX_STATE_STATUS_MSK,
+ (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >>
+ AGG_TX_STATE_TRY_CNT_POS,
+ le16_to_cpu(frame_status[i].sequence));
+ }
+}
+#else
+static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+ int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
+ int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ struct ieee80211_sta *sta;
+
+ if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue))
+ return;
+
+ if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
+ return;
+
+ iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt);
+
+ rcu_read_lock();
+
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+ if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ mvmsta->tid_data[tid].rate_n_flags =
+ le32_to_cpu(tx_resp->initial_rate);
+ mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc;
+ mvmsta->tid_data[tid].tx_time =
+ le16_to_cpu(tx_resp->wireless_media_time);
+ }
+
+ rcu_read_unlock();
+}
+
+void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+
+ if (tx_resp->frame_count == 1)
+ iwl_mvm_rx_tx_cmd_single(mvm, pkt);
+ else
+ iwl_mvm_rx_tx_cmd_agg(mvm, pkt);
+}
+
+static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info,
+ struct iwl_mvm_ba_notif *ba_notif,
+ struct iwl_mvm_tid_data *tid_data)
+{
+ info->flags |= IEEE80211_TX_STAT_AMPDU;
+ info->status.ampdu_ack_len = ba_notif->txed_2_done;
+ info->status.ampdu_len = ba_notif->txed;
+ iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
+ info);
+ /* TODO: not accounted if the whole A-MPDU failed */
+ info->status.tx_time = tid_data->tx_time;
+ info->status.status_driver_data[0] =
+ (void *)(uintptr_t)tid_data->reduced_tpc;
+ info->status.status_driver_data[1] =
+ (void *)(uintptr_t)tid_data->rate_n_flags;
+}
+
+void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data;
+ struct sk_buff_head reclaimed_skbs;
+ struct iwl_mvm_tid_data *tid_data;
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ struct sk_buff *skb;
+ int sta_id, tid, freed;
+ /* "flow" corresponds to Tx queue */
+ u16 scd_flow = le16_to_cpu(ba_notif->scd_flow);
+ /* "ssn" is start of block-ack Tx window, corresponds to index
+ * (in Tx queue's circular buffer) of first TFD/frame in window */
+ u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn);
+
+ sta_id = ba_notif->sta_id;
+ tid = ba_notif->tid;
+
+ if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT ||
+ tid >= IWL_MAX_TID_COUNT,
+ "sta_id %d tid %d", sta_id, tid))
+ return;
+
+ rcu_read_lock();
+
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+ /* Reclaiming frames for a station that has been deleted ? */
+ if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
+ rcu_read_unlock();
+ return;
+ }
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ tid_data = &mvmsta->tid_data[tid];
+
+ if (tid_data->txq_id != scd_flow) {
+ IWL_ERR(mvm,
+ "invalid BA notification: Q %d, tid %d, flow %d\n",
+ tid_data->txq_id, tid, scd_flow);
+ rcu_read_unlock();
+ return;
+ }
+
+ spin_lock_bh(&mvmsta->lock);
+
+ __skb_queue_head_init(&reclaimed_skbs);
+
+ /*
+ * Release all TFDs before the SSN, i.e. all TFDs in front of
+ * block-ack window (we assume that they've been successfully
+ * transmitted ... if not, it's too late anyway).
+ */
+ iwl_trans_reclaim(mvm->trans, scd_flow, ba_resp_scd_ssn,
+ &reclaimed_skbs);
+
+ IWL_DEBUG_TX_REPLY(mvm,
+ "BA_NOTIFICATION Received from %pM, sta_id = %d\n",
+ (u8 *)&ba_notif->sta_addr_lo32,
+ ba_notif->sta_id);
+ IWL_DEBUG_TX_REPLY(mvm,
+ "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
+ ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl),
+ (unsigned long long)le64_to_cpu(ba_notif->bitmap),
+ scd_flow, ba_resp_scd_ssn, ba_notif->txed,
+ ba_notif->txed_2_done);
+
+ tid_data->next_reclaimed = ba_resp_scd_ssn;
+
+ iwl_mvm_check_ratid_empty(mvm, sta, tid);
+
+ freed = 0;
+
+ skb_queue_walk(&reclaimed_skbs, skb) {
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ freed++;
+ else
+ WARN_ON_ONCE(1);
+
+ iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+
+ memset(&info->status, 0, sizeof(info->status));
+ /* Packet was transmitted successfully, failures come as single
+ * frames because before failing a frame the firmware transmits
+ * it without aggregation at least once.
+ */
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ /* this is the first skb we deliver in this batch */
+ /* put the rate scaling data there */
+ if (freed == 1)
+ iwl_mvm_tx_info_from_ba_notif(info, ba_notif, tid_data);
+ }
+
+ spin_unlock_bh(&mvmsta->lock);
+
+ /* We got a BA notif with 0 acked or scd_ssn didn't progress which is
+ * possible (i.e. first MPDU in the aggregation wasn't acked)
+ * Still it's important to update RS about sent vs. acked.
+ */
+ if (skb_queue_empty(&reclaimed_skbs)) {
+ struct ieee80211_tx_info ba_info = {};
+ struct ieee80211_chanctx_conf *chanctx_conf = NULL;
+
+ if (mvmsta->vif)
+ chanctx_conf =
+ rcu_dereference(mvmsta->vif->chanctx_conf);
+
+ if (WARN_ON_ONCE(!chanctx_conf))
+ goto out;
+
+ ba_info.band = chanctx_conf->def.chan->band;
+ iwl_mvm_tx_info_from_ba_notif(&ba_info, ba_notif, tid_data);
+
+ IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n");
+ iwl_mvm_rs_tx_status(mvm, sta, tid, &ba_info);
+ }
+
+out:
+ rcu_read_unlock();
+
+ while (!skb_queue_empty(&reclaimed_skbs)) {
+ skb = __skb_dequeue(&reclaimed_skbs);
+ ieee80211_tx_status(mvm->hw, skb);
+ }
+}
+
+/*
+ * Note that there are transports that buffer frames before they reach
+ * the firmware. This means that after flush_tx_path is called, the
+ * queue might not be empty. The race-free way to handle this is to:
+ * 1) set the station as draining
+ * 2) flush the Tx path
+ * 3) wait for the transport queues to be empty
+ */
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags)
+{
+ int ret;
+ struct iwl_tx_path_flush_cmd flush_cmd = {
+ .queues_ctl = cpu_to_le32(tfd_msk),
+ .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
+ };
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
+ sizeof(flush_cmd), &flush_cmd);
+ if (ret)
+ IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret);
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
new file mode 100644
index 000000000000..3a989f5c20db
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -0,0 +1,1083 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "fw-dbg.h"
+#include "mvm.h"
+#include "fw-api-rs.h"
+
+/*
+ * Will return 0 even if the cmd failed when RFKILL is asserted unless
+ * CMD_WANT_SKB is set in cmd->flags.
+ */
+int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
+{
+ int ret;
+
+#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
+ if (WARN_ON(mvm->d3_test_active))
+ return -EIO;
+#endif
+
+ /*
+ * Synchronous commands from this op-mode must hold
+ * the mutex, this ensures we don't try to send two
+ * (or more) synchronous commands at a time.
+ */
+ if (!(cmd->flags & CMD_ASYNC))
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_trans_send_cmd(mvm->trans, cmd);
+
+ /*
+ * If the caller wants the SKB, then don't hide any problems, the
+ * caller might access the response buffer which will be NULL if
+ * the command failed.
+ */
+ if (cmd->flags & CMD_WANT_SKB)
+ return ret;
+
+ /* Silently ignore failures if RFKILL is asserted */
+ if (!ret || ret == -ERFKILL)
+ return 0;
+ return ret;
+}
+
+int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id,
+ u32 flags, u16 len, const void *data)
+{
+ struct iwl_host_cmd cmd = {
+ .id = id,
+ .len = { len, },
+ .data = { data, },
+ .flags = flags,
+ };
+
+ return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+/*
+ * We assume that the caller set the status to the success value
+ */
+int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
+ u32 *status)
+{
+ struct iwl_rx_packet *pkt;
+ struct iwl_cmd_response *resp;
+ int ret, resp_len;
+
+ lockdep_assert_held(&mvm->mutex);
+
+#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
+ if (WARN_ON(mvm->d3_test_active))
+ return -EIO;
+#endif
+
+ /*
+ * Only synchronous commands can wait for status,
+ * we use WANT_SKB so the caller can't.
+ */
+ if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB),
+ "cmd flags %x", cmd->flags))
+ return -EINVAL;
+
+ cmd->flags |= CMD_WANT_SKB;
+
+ ret = iwl_trans_send_cmd(mvm->trans, cmd);
+ if (ret == -ERFKILL) {
+ /*
+ * The command failed because of RFKILL, don't update
+ * the status, leave it as success and return 0.
+ */
+ return 0;
+ } else if (ret) {
+ return ret;
+ }
+
+ pkt = cmd->resp_pkt;
+ /* Can happen if RFKILL is asserted */
+ if (!pkt) {
+ ret = 0;
+ goto out_free_resp;
+ }
+
+ resp_len = iwl_rx_packet_payload_len(pkt);
+ if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
+ ret = -EIO;
+ goto out_free_resp;
+ }
+
+ resp = (void *)pkt->data;
+ *status = le32_to_cpu(resp->status);
+ out_free_resp:
+ iwl_free_resp(cmd);
+ return ret;
+}
+
+/*
+ * We assume that the caller set the status to the sucess value
+ */
+int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len,
+ const void *data, u32 *status)
+{
+ struct iwl_host_cmd cmd = {
+ .id = id,
+ .len = { len, },
+ .data = { data, },
+ };
+
+ return iwl_mvm_send_cmd_status(mvm, &cmd, status);
+}
+
+#define IWL_DECLARE_RATE_INFO(r) \
+ [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
+
+/*
+ * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
+ */
+static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
+ IWL_DECLARE_RATE_INFO(1),
+ IWL_DECLARE_RATE_INFO(2),
+ IWL_DECLARE_RATE_INFO(5),
+ IWL_DECLARE_RATE_INFO(11),
+ IWL_DECLARE_RATE_INFO(6),
+ IWL_DECLARE_RATE_INFO(9),
+ IWL_DECLARE_RATE_INFO(12),
+ IWL_DECLARE_RATE_INFO(18),
+ IWL_DECLARE_RATE_INFO(24),
+ IWL_DECLARE_RATE_INFO(36),
+ IWL_DECLARE_RATE_INFO(48),
+ IWL_DECLARE_RATE_INFO(54),
+};
+
+int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
+ enum ieee80211_band band)
+{
+ int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
+ int idx;
+ int band_offset = 0;
+
+ /* Legacy rate format, search for match in table */
+ if (band == IEEE80211_BAND_5GHZ)
+ band_offset = IWL_FIRST_OFDM_RATE;
+ for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
+ if (fw_rate_idx_to_plcp[idx] == rate)
+ return idx - band_offset;
+
+ return -1;
+}
+
+u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx)
+{
+ /* Get PLCP rate for tx_cmd->rate_n_flags */
+ return fw_rate_idx_to_plcp[rate_idx];
+}
+
+void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_error_resp *err_resp = (void *)pkt->data;
+
+ IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n",
+ le32_to_cpu(err_resp->error_type), err_resp->cmd_id);
+ IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n",
+ le16_to_cpu(err_resp->bad_cmd_seq_num),
+ le32_to_cpu(err_resp->error_service));
+ IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n",
+ le64_to_cpu(err_resp->timestamp));
+}
+
+/*
+ * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h.
+ * The parameter should also be a combination of ANT_[ABC].
+ */
+u8 first_antenna(u8 mask)
+{
+ BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */
+ if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */
+ return BIT(0);
+ return BIT(ffs(mask) - 1);
+}
+
+/*
+ * Toggles between TX antennas to send the probe request on.
+ * Receives the bitmask of valid TX antennas and the *index* used
+ * for the last TX, and returns the next valid *index* to use.
+ * In order to set it in the tx_cmd, must do BIT(idx).
+ */
+u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
+{
+ u8 ind = last_idx;
+ int i;
+
+ for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
+ ind = (ind + 1) % RATE_MCS_ANT_NUM;
+ if (valid & BIT(ind))
+ return ind;
+ }
+
+ WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid);
+ return last_idx;
+}
+
+static const struct {
+ const char *name;
+ u8 num;
+} advanced_lookup[] = {
+ { "NMI_INTERRUPT_WDG", 0x34 },
+ { "SYSASSERT", 0x35 },
+ { "UCODE_VERSION_MISMATCH", 0x37 },
+ { "BAD_COMMAND", 0x38 },
+ { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
+ { "FATAL_ERROR", 0x3D },
+ { "NMI_TRM_HW_ERR", 0x46 },
+ { "NMI_INTERRUPT_TRM", 0x4C },
+ { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
+ { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
+ { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
+ { "NMI_INTERRUPT_HOST", 0x66 },
+ { "NMI_INTERRUPT_ACTION_PT", 0x7C },
+ { "NMI_INTERRUPT_UNKNOWN", 0x84 },
+ { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
+ { "ADVANCED_SYSASSERT", 0 },
+};
+
+static const char *desc_lookup(u32 num)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
+ if (advanced_lookup[i].num == num)
+ return advanced_lookup[i].name;
+
+ /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
+ return advanced_lookup[i].name;
+}
+
+/*
+ * Note: This structure is read from the device with IO accesses,
+ * and the reading already does the endian conversion. As it is
+ * read with u32-sized accesses, any members with a different size
+ * need to be ordered correctly though!
+ */
+struct iwl_error_event_table_v1 {
+ u32 valid; /* (nonzero) valid, (0) log is empty */
+ u32 error_id; /* type of error */
+ u32 pc; /* program counter */
+ u32 blink1; /* branch link */
+ u32 blink2; /* branch link */
+ u32 ilink1; /* interrupt link */
+ u32 ilink2; /* interrupt link */
+ u32 data1; /* error-specific data */
+ u32 data2; /* error-specific data */
+ u32 data3; /* error-specific data */
+ u32 bcon_time; /* beacon timer */
+ u32 tsf_low; /* network timestamp function timer */
+ u32 tsf_hi; /* network timestamp function timer */
+ u32 gp1; /* GP1 timer register */
+ u32 gp2; /* GP2 timer register */
+ u32 gp3; /* GP3 timer register */
+ u32 ucode_ver; /* uCode version */
+ u32 hw_ver; /* HW Silicon version */
+ u32 brd_ver; /* HW board version */
+ u32 log_pc; /* log program counter */
+ u32 frame_ptr; /* frame pointer */
+ u32 stack_ptr; /* stack pointer */
+ u32 hcmd; /* last host command header */
+ u32 isr0; /* isr status register LMPM_NIC_ISR0:
+ * rxtx_flag */
+ u32 isr1; /* isr status register LMPM_NIC_ISR1:
+ * host_flag */
+ u32 isr2; /* isr status register LMPM_NIC_ISR2:
+ * enc_flag */
+ u32 isr3; /* isr status register LMPM_NIC_ISR3:
+ * time_flag */
+ u32 isr4; /* isr status register LMPM_NIC_ISR4:
+ * wico interrupt */
+ u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
+ u32 wait_event; /* wait event() caller address */
+ u32 l2p_control; /* L2pControlField */
+ u32 l2p_duration; /* L2pDurationField */
+ u32 l2p_mhvalid; /* L2pMhValidBits */
+ u32 l2p_addr_match; /* L2pAddrMatchStat */
+ u32 lmpm_pmg_sel; /* indicate which clocks are turned on
+ * (LMPM_PMG_SEL) */
+ u32 u_timestamp; /* indicate when the date and time of the
+ * compilation */
+ u32 flow_handler; /* FH read/write pointers, RX credit */
+} __packed /* LOG_ERROR_TABLE_API_S_VER_1 */;
+
+struct iwl_error_event_table {
+ u32 valid; /* (nonzero) valid, (0) log is empty */
+ u32 error_id; /* type of error */
+ u32 pc; /* program counter */
+ u32 blink1; /* branch link */
+ u32 blink2; /* branch link */
+ u32 ilink1; /* interrupt link */
+ u32 ilink2; /* interrupt link */
+ u32 data1; /* error-specific data */
+ u32 data2; /* error-specific data */
+ u32 data3; /* error-specific data */
+ u32 bcon_time; /* beacon timer */
+ u32 tsf_low; /* network timestamp function timer */
+ u32 tsf_hi; /* network timestamp function timer */
+ u32 gp1; /* GP1 timer register */
+ u32 gp2; /* GP2 timer register */
+ u32 gp3; /* GP3 timer register */
+ u32 major; /* uCode version major */
+ u32 minor; /* uCode version minor */
+ u32 hw_ver; /* HW Silicon version */
+ u32 brd_ver; /* HW board version */
+ u32 log_pc; /* log program counter */
+ u32 frame_ptr; /* frame pointer */
+ u32 stack_ptr; /* stack pointer */
+ u32 hcmd; /* last host command header */
+ u32 isr0; /* isr status register LMPM_NIC_ISR0:
+ * rxtx_flag */
+ u32 isr1; /* isr status register LMPM_NIC_ISR1:
+ * host_flag */
+ u32 isr2; /* isr status register LMPM_NIC_ISR2:
+ * enc_flag */
+ u32 isr3; /* isr status register LMPM_NIC_ISR3:
+ * time_flag */
+ u32 isr4; /* isr status register LMPM_NIC_ISR4:
+ * wico interrupt */
+ u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
+ u32 wait_event; /* wait event() caller address */
+ u32 l2p_control; /* L2pControlField */
+ u32 l2p_duration; /* L2pDurationField */
+ u32 l2p_mhvalid; /* L2pMhValidBits */
+ u32 l2p_addr_match; /* L2pAddrMatchStat */
+ u32 lmpm_pmg_sel; /* indicate which clocks are turned on
+ * (LMPM_PMG_SEL) */
+ u32 u_timestamp; /* indicate when the date and time of the
+ * compilation */
+ u32 flow_handler; /* FH read/write pointers, RX credit */
+} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */;
+
+/*
+ * UMAC error struct - relevant starting from family 8000 chip.
+ * Note: This structure is read from the device with IO accesses,
+ * and the reading already does the endian conversion. As it is
+ * read with u32-sized accesses, any members with a different size
+ * need to be ordered correctly though!
+ */
+struct iwl_umac_error_event_table {
+ u32 valid; /* (nonzero) valid, (0) log is empty */
+ u32 error_id; /* type of error */
+ u32 blink1; /* branch link */
+ u32 blink2; /* branch link */
+ u32 ilink1; /* interrupt link */
+ u32 ilink2; /* interrupt link */
+ u32 data1; /* error-specific data */
+ u32 data2; /* error-specific data */
+ u32 data3; /* error-specific data */
+ u32 umac_major;
+ u32 umac_minor;
+ u32 frame_pointer; /* core register 27*/
+ u32 stack_pointer; /* core register 28 */
+ u32 cmd_header; /* latest host cmd sent to UMAC */
+ u32 nic_isr_pref; /* ISR status register */
+} __packed;
+
+#define ERROR_START_OFFSET (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+
+static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
+{
+ struct iwl_trans *trans = mvm->trans;
+ struct iwl_umac_error_event_table table;
+ u32 base;
+
+ base = mvm->umac_error_event_table;
+
+ if (base < 0x800000) {
+ IWL_ERR(mvm,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base,
+ (mvm->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return;
+ }
+
+ iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+ if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+ IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+ IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+ mvm->status, table.valid);
+ }
+
+ IWL_ERR(mvm, "0x%08X | %s\n", table.error_id,
+ desc_lookup(table.error_id));
+ IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1);
+ IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2);
+ IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1);
+ IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2);
+ IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
+ IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
+ IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3);
+ IWL_ERR(mvm, "0x%08X | umac major\n", table.umac_major);
+ IWL_ERR(mvm, "0x%08X | umac minor\n", table.umac_minor);
+ IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer);
+ IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer);
+ IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header);
+ IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref);
+}
+
+static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm)
+{
+ struct iwl_trans *trans = mvm->trans;
+ struct iwl_error_event_table_v1 table;
+ u32 base;
+
+ base = mvm->error_event_table;
+ if (mvm->cur_ucode == IWL_UCODE_INIT) {
+ if (!base)
+ base = mvm->fw->init_errlog_ptr;
+ } else {
+ if (!base)
+ base = mvm->fw->inst_errlog_ptr;
+ }
+
+ if (base < 0x800000) {
+ IWL_ERR(mvm,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base,
+ (mvm->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return;
+ }
+
+ iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+ if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+ IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+ IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+ mvm->status, table.valid);
+ }
+
+ /* Do not change this output - scripts rely on it */
+
+ IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
+
+ trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
+ table.data1, table.data2, table.data3,
+ table.blink1, table.blink2, table.ilink1,
+ table.ilink2, table.bcon_time, table.gp1,
+ table.gp2, table.gp3, table.ucode_ver, 0,
+ table.hw_ver, table.brd_ver);
+ IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+ desc_lookup(table.error_id));
+ IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
+ IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
+ IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
+ IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
+ IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
+ IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
+ IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
+ IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
+ IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
+ IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
+ IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
+ IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
+ IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
+ IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
+ IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver);
+ IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
+ IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
+ IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
+ IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
+ IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
+ IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
+ IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
+ IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
+ IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
+ IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
+ IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
+ IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
+ IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
+ IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
+ IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
+ IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
+ IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
+
+ if (mvm->support_umac_log)
+ iwl_mvm_dump_umac_error_log(mvm);
+}
+
+void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
+{
+ struct iwl_trans *trans = mvm->trans;
+ struct iwl_error_event_table table;
+ u32 base;
+
+ if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) {
+ iwl_mvm_dump_nic_error_log_old(mvm);
+ return;
+ }
+
+ base = mvm->error_event_table;
+ if (mvm->cur_ucode == IWL_UCODE_INIT) {
+ if (!base)
+ base = mvm->fw->init_errlog_ptr;
+ } else {
+ if (!base)
+ base = mvm->fw->inst_errlog_ptr;
+ }
+
+ if (base < 0x800000) {
+ IWL_ERR(mvm,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base,
+ (mvm->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return;
+ }
+
+ iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+ if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+ IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+ IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+ mvm->status, table.valid);
+ }
+
+ /* Do not change this output - scripts rely on it */
+
+ IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
+
+ trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
+ table.data1, table.data2, table.data3,
+ table.blink1, table.blink2, table.ilink1,
+ table.ilink2, table.bcon_time, table.gp1,
+ table.gp2, table.gp3, table.major,
+ table.minor, table.hw_ver, table.brd_ver);
+ IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+ desc_lookup(table.error_id));
+ IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
+ IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
+ IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
+ IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
+ IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
+ IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
+ IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
+ IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
+ IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
+ IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
+ IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
+ IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
+ IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
+ IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
+ IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major);
+ IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor);
+ IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
+ IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
+ IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
+ IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
+ IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
+ IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
+ IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
+ IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
+ IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
+ IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
+ IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
+ IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
+ IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
+ IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
+ IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
+ IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
+ IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
+
+ if (mvm->support_umac_log)
+ iwl_mvm_dump_umac_error_log(mvm);
+}
+
+int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq)
+{
+ int i;
+
+ lockdep_assert_held(&mvm->queue_info_lock);
+
+ for (i = minq; i <= maxq; i++)
+ if (mvm->queue_info[i].hw_queue_refcount == 0 &&
+ !mvm->queue_info[i].setup_reserved)
+ return i;
+
+ return -ENOSPC;
+}
+
+void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+ u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg,
+ unsigned int wdg_timeout)
+{
+ bool enable_queue = true;
+
+ spin_lock_bh(&mvm->queue_info_lock);
+
+ /* Make sure this TID isn't already enabled */
+ if (mvm->queue_info[queue].tid_bitmap & BIT(cfg->tid)) {
+ spin_unlock_bh(&mvm->queue_info_lock);
+ IWL_ERR(mvm, "Trying to enable TXQ with existing TID %d\n",
+ cfg->tid);
+ return;
+ }
+
+ /* Update mappings and refcounts */
+ mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue);
+ mvm->queue_info[queue].hw_queue_refcount++;
+ if (mvm->queue_info[queue].hw_queue_refcount > 1)
+ enable_queue = false;
+ mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid);
+
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n",
+ queue, mvm->queue_info[queue].hw_queue_refcount,
+ mvm->queue_info[queue].hw_queue_to_mac80211);
+
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ /* Send the enabling command if we need to */
+ if (enable_queue) {
+ struct iwl_scd_txq_cfg_cmd cmd = {
+ .scd_queue = queue,
+ .enable = 1,
+ .window = cfg->frame_limit,
+ .sta_id = cfg->sta_id,
+ .ssn = cpu_to_le16(ssn),
+ .tx_fifo = cfg->fifo,
+ .aggregate = cfg->aggregate,
+ .tid = cfg->tid,
+ };
+
+ iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL,
+ wdg_timeout);
+ WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd),
+ &cmd),
+ "Failed to configure queue %d on FIFO %d\n", queue,
+ cfg->fifo);
+ }
+}
+
+void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+ u8 tid, u8 flags)
+{
+ struct iwl_scd_txq_cfg_cmd cmd = {
+ .scd_queue = queue,
+ .enable = 0,
+ };
+ bool remove_mac_queue = true;
+ int ret;
+
+ spin_lock_bh(&mvm->queue_info_lock);
+
+ if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) {
+ spin_unlock_bh(&mvm->queue_info_lock);
+ return;
+ }
+
+ mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
+
+ /*
+ * If there is another TID with the same AC - don't remove the MAC queue
+ * from the mapping
+ */
+ if (tid < IWL_MAX_TID_COUNT) {
+ unsigned long tid_bitmap =
+ mvm->queue_info[queue].tid_bitmap;
+ int ac = tid_to_mac80211_ac[tid];
+ int i;
+
+ for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) {
+ if (tid_to_mac80211_ac[i] == ac)
+ remove_mac_queue = false;
+ }
+ }
+
+ if (remove_mac_queue)
+ mvm->queue_info[queue].hw_queue_to_mac80211 &=
+ ~BIT(mac80211_queue);
+ mvm->queue_info[queue].hw_queue_refcount--;
+
+ cmd.enable = mvm->queue_info[queue].hw_queue_refcount ? 1 : 0;
+
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "Disabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n",
+ queue,
+ mvm->queue_info[queue].hw_queue_refcount,
+ mvm->queue_info[queue].hw_queue_to_mac80211);
+
+ /* If the queue is still enabled - nothing left to do in this func */
+ if (cmd.enable) {
+ spin_unlock_bh(&mvm->queue_info_lock);
+ return;
+ }
+
+ /* Make sure queue info is correct even though we overwrite it */
+ WARN(mvm->queue_info[queue].hw_queue_refcount ||
+ mvm->queue_info[queue].tid_bitmap ||
+ mvm->queue_info[queue].hw_queue_to_mac80211,
+ "TXQ #%d info out-of-sync - refcount=%d, mac map=0x%x, tid=0x%x\n",
+ queue, mvm->queue_info[queue].hw_queue_refcount,
+ mvm->queue_info[queue].hw_queue_to_mac80211,
+ mvm->queue_info[queue].tid_bitmap);
+
+ /* If we are here - the queue is freed and we can zero out these vals */
+ mvm->queue_info[queue].hw_queue_refcount = 0;
+ mvm->queue_info[queue].tid_bitmap = 0;
+ mvm->queue_info[queue].hw_queue_to_mac80211 = 0;
+
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ iwl_trans_txq_disable(mvm->trans, queue, false);
+ ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags,
+ sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n",
+ queue, ret);
+}
+
+/**
+ * iwl_mvm_send_lq_cmd() - Send link quality command
+ * @init: This command is sent as part of station initialization right
+ * after station has been added.
+ *
+ * The link quality command is sent as the last step of station creation.
+ * This is the special case in which init is set and we call a callback in
+ * this case to clear the state indicating that station creation is in
+ * progress.
+ */
+int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init)
+{
+ struct iwl_host_cmd cmd = {
+ .id = LQ_CMD,
+ .len = { sizeof(struct iwl_lq_cmd), },
+ .flags = init ? 0 : CMD_ASYNC,
+ .data = { lq, },
+ };
+
+ if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT))
+ return -EINVAL;
+
+ return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+/**
+ * iwl_mvm_update_smps - Get a request to change the SMPS mode
+ * @req_type: The part of the driver who call for a change.
+ * @smps_requests: The request to change the SMPS mode.
+ *
+ * Get a requst to change the SMPS mode,
+ * and change it according to all other requests in the driver.
+ */
+void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum iwl_mvm_smps_type_request req_type,
+ enum ieee80211_smps_mode smps_request)
+{
+ struct iwl_mvm_vif *mvmvif;
+ enum ieee80211_smps_mode smps_mode;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */
+ if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1)
+ return;
+
+ if (vif->type == NL80211_IFTYPE_AP)
+ smps_mode = IEEE80211_SMPS_OFF;
+ else
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ mvmvif->smps_requests[req_type] = smps_request;
+ for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
+ if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) {
+ smps_mode = IEEE80211_SMPS_STATIC;
+ break;
+ }
+ if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+ }
+
+ ieee80211_request_smps(vif, smps_mode);
+}
+
+int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear)
+{
+ struct iwl_statistics_cmd scmd = {
+ .flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0,
+ };
+ struct iwl_host_cmd cmd = {
+ .id = STATISTICS_CMD,
+ .len[0] = sizeof(scmd),
+ .data[0] = &scmd,
+ .flags = CMD_WANT_SKB,
+ };
+ int ret;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret)
+ return ret;
+
+ iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt);
+ iwl_free_resp(&cmd);
+
+ if (clear)
+ iwl_mvm_accu_radio_stats(mvm);
+
+ return 0;
+}
+
+void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm)
+{
+ mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time;
+ mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time;
+ mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf;
+ mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan;
+}
+
+static void iwl_mvm_diversity_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ bool *result = _data;
+ int i;
+
+ for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
+ if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC ||
+ mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
+ *result = false;
+ }
+}
+
+bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm)
+{
+ bool result = true;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1)
+ return false;
+
+ if (mvm->cfg->rx_with_siso_diversity)
+ return false;
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_diversity_iter, &result);
+
+ return result;
+}
+
+int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool value)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int res;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (mvmvif->low_latency == value)
+ return 0;
+
+ mvmvif->low_latency = value;
+
+ res = iwl_mvm_update_quotas(mvm, false, NULL);
+ if (res)
+ return res;
+
+ iwl_mvm_bt_coex_vif_change(mvm);
+
+ return iwl_mvm_power_update_mac(mvm);
+}
+
+static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+ bool *result = _data;
+
+ if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif)))
+ *result = true;
+}
+
+bool iwl_mvm_low_latency(struct iwl_mvm *mvm)
+{
+ bool result = false;
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_ll_iter, &result);
+
+ return result;
+}
+
+struct iwl_bss_iter_data {
+ struct ieee80211_vif *vif;
+ bool error;
+};
+
+static void iwl_mvm_bss_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_bss_iter_data *data = _data;
+
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ return;
+
+ if (data->vif) {
+ data->error = true;
+ return;
+ }
+
+ data->vif = vif;
+}
+
+struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm)
+{
+ struct iwl_bss_iter_data bss_iter_data = {};
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bss_iface_iterator, &bss_iter_data);
+
+ if (bss_iter_data.error) {
+ IWL_ERR(mvm, "More than one managed interface active!\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return bss_iter_data.vif;
+}
+
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool tdls, bool cmd_q)
+{
+ struct iwl_fw_dbg_trigger_tlv *trigger;
+ struct iwl_fw_dbg_trigger_txq_timer *txq_timer;
+ unsigned int default_timeout =
+ cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS))
+ return iwlmvm_mod_params.tfd_q_hang_detect ?
+ default_timeout : IWL_WATCHDOG_DISABLED;
+
+ trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS);
+ txq_timer = (void *)trigger->data;
+
+ if (tdls)
+ return le32_to_cpu(txq_timer->tdls);
+
+ if (cmd_q)
+ return le32_to_cpu(txq_timer->command_queue);
+
+ if (WARN_ON(!vif))
+ return default_timeout;
+
+ switch (ieee80211_vif_type_p2p(vif)) {
+ case NL80211_IFTYPE_ADHOC:
+ return le32_to_cpu(txq_timer->ibss);
+ case NL80211_IFTYPE_STATION:
+ return le32_to_cpu(txq_timer->bss);
+ case NL80211_IFTYPE_AP:
+ return le32_to_cpu(txq_timer->softap);
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return le32_to_cpu(txq_timer->p2p_client);
+ case NL80211_IFTYPE_P2P_GO:
+ return le32_to_cpu(txq_timer->p2p_go);
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return le32_to_cpu(txq_timer->p2p_device);
+ default:
+ WARN_ON(1);
+ return mvm->cfg->base_params->wd_timeout;
+ }
+}
+
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ const char *errmsg)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+ goto out;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+ trig_mlme = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ goto out;
+
+ if (trig_mlme->stop_connection_loss &&
+ --trig_mlme->stop_connection_loss)
+ goto out;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg);
+
+out:
+ ieee80211_connection_loss(vif);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
new file mode 100644
index 000000000000..6261a68cae90
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -0,0 +1,723 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci-aspm.h>
+#include <linux/acpi.h>
+
+#include "iwl-trans.h"
+#include "iwl-drv.h"
+#include "internal.h"
+
+#define IWL_PCI_DEVICE(dev, subdev, cfg) \
+ .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
+ .subvendor = PCI_ANY_ID, .subdevice = (subdev), \
+ .driver_data = (kernel_ulong_t)&(cfg)
+
+/* Hardware specific file defines the PCI IDs table for that hardware module */
+static const struct pci_device_id 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 */
+ {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */
+
+/* 5300 Series WiFi */
+ {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */
+
+/* 5350 Series WiFi/WiMax */
+ {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */
+
+/* 5150 Series Wifi/WiMax */
+ {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_abg_cfg)}, /* Half Mini Card */
+
+ {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */
+
+/* 6x00 Series */
+ {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
+
+/* 6x05 Series */
+ {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */
+ {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */
+
+/* 6x30 Series */
+ {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)},
+ {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)},
+
+/* 6x50 WiFi/WiMax Series */
+ {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
+
+/* 6150 WiFi/WiMax Series */
+ {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)},
+
+/* 1000 Series WiFi */
+ {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)},
+
+/* 100 Series WiFi */
+ {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)},
+ {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)},
+
+/* 130 Series WiFi */
+ {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)},
+
+/* 2x00 Series */
+ {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)},
+
+/* 2x30 Series */
+ {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)},
+
+/* 6x35 Series */
+ {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)},
+
+/* 105 Series */
+ {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)},
+
+/* 135 Series */
+ {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)
+/* 7260 Series */
+ {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
+
+/* 3160 Series */
+ {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)},
+
+/* 3165 Series */
+ {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4212, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)},
+
+/* 3168 Series */
+ {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl3168_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl3168_2ac_cfg)},
+
+/* 7265 Series */
+ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7265_n_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)},
+
+/* 8000 Series */
+ {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1132, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x01F0, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0012, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1012, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1150, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8132, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9132, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8150, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9150, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0044, iwl8260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)},
+
+/* 9000 Series */
+ {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0310, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0410, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9260_2ac_cfg)},
+#endif /* CONFIG_IWLMVM */
+
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
+
+#ifdef CONFIG_ACPI
+#define SPL_METHOD "SPLC"
+#define SPL_DOMAINTYPE_MODULE BIT(0)
+#define SPL_DOMAINTYPE_WIFI BIT(1)
+#define SPL_DOMAINTYPE_WIGIG BIT(2)
+#define SPL_DOMAINTYPE_RFEM BIT(3)
+
+static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
+{
+ union acpi_object *limits, *domain_type, *power_limit;
+
+ if (splx->type != ACPI_TYPE_PACKAGE ||
+ splx->package.count != 2 ||
+ splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ splx->package.elements[0].integer.value != 0) {
+ IWL_ERR(trans, "Unsupported splx structure\n");
+ return 0;
+ }
+
+ limits = &splx->package.elements[1];
+ if (limits->type != ACPI_TYPE_PACKAGE ||
+ limits->package.count < 2 ||
+ limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
+ IWL_ERR(trans, "Invalid limits element\n");
+ return 0;
+ }
+
+ domain_type = &limits->package.elements[0];
+ power_limit = &limits->package.elements[1];
+ if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
+ IWL_DEBUG_INFO(trans, "WiFi power is not limited\n");
+ return 0;
+ }
+
+ return power_limit->integer.value;
+}
+
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
+{
+ acpi_handle pxsx_handle;
+ acpi_handle handle;
+ struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_status status;
+
+ pxsx_handle = ACPI_HANDLE(&pdev->dev);
+ if (!pxsx_handle) {
+ IWL_DEBUG_INFO(trans,
+ "Could not retrieve root port ACPI handle\n");
+ return;
+ }
+
+ /* Get the method's handle */
+ status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
+ if (ACPI_FAILURE(status)) {
+ IWL_DEBUG_INFO(trans, "SPL method not found\n");
+ return;
+ }
+
+ /* Call SPLC with no arguments */
+ status = acpi_evaluate_object(handle, NULL, NULL, &splx);
+ if (ACPI_FAILURE(status)) {
+ IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status);
+ return;
+ }
+
+ trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
+ IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n",
+ trans->dflt_pwr_limit);
+ kfree(splx.pointer);
+}
+
+#else /* CONFIG_ACPI */
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
+#endif
+
+/* PCI registers */
+#define PCI_CFG_RETRY_TIMEOUT 0x041
+
+static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
+ const struct iwl_cfg *cfg_7265d __maybe_unused = NULL;
+ struct iwl_trans *iwl_trans;
+ struct iwl_trans_pcie *trans_pcie;
+ int ret;
+
+ iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg);
+ if (IS_ERR(iwl_trans))
+ return PTR_ERR(iwl_trans);
+
+#if IS_ENABLED(CONFIG_IWLMVM)
+ /*
+ * special-case 7265D, it has the same PCI IDs.
+ *
+ * Note that because we already pass the cfg to the transport above,
+ * all the parameters that the transport uses must, until that is
+ * changed, be identical to the ones in the 7265D configuration.
+ */
+ if (cfg == &iwl7265_2ac_cfg)
+ cfg_7265d = &iwl7265d_2ac_cfg;
+ else if (cfg == &iwl7265_2n_cfg)
+ cfg_7265d = &iwl7265d_2n_cfg;
+ else if (cfg == &iwl7265_n_cfg)
+ cfg_7265d = &iwl7265d_n_cfg;
+ if (cfg_7265d &&
+ (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) {
+ cfg = cfg_7265d;
+ iwl_trans->cfg = cfg_7265d;
+ }
+#endif
+
+ pci_set_drvdata(pdev, iwl_trans);
+
+ trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
+ trans_pcie->drv = iwl_drv_start(iwl_trans, cfg);
+
+ if (IS_ERR(trans_pcie->drv)) {
+ ret = PTR_ERR(trans_pcie->drv);
+ goto out_free_trans;
+ }
+
+ set_dflt_pwr_limit(iwl_trans, pdev);
+
+ /* register transport layer debugfs here */
+ ret = iwl_trans_pcie_dbgfs_register(iwl_trans);
+ if (ret)
+ goto out_free_drv;
+
+ return 0;
+
+out_free_drv:
+ iwl_drv_stop(trans_pcie->drv);
+out_free_trans:
+ iwl_trans_pcie_free(iwl_trans);
+ return ret;
+}
+
+static void iwl_pci_remove(struct pci_dev *pdev)
+{
+ struct iwl_trans *trans = pci_get_drvdata(pdev);
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ iwl_drv_stop(trans_pcie->drv);
+ iwl_trans_pcie_free(trans);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int iwl_pci_suspend(struct device *device)
+{
+ /* Before you put code here, think about WoWLAN. You cannot check here
+ * whether WoWLAN is enabled or not, and your code will run even if
+ * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx.
+ */
+
+ return 0;
+}
+
+static int iwl_pci_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct iwl_trans *trans = pci_get_drvdata(pdev);
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ bool hw_rfkill;
+
+ /* Before you put code here, think about WoWLAN. You cannot check here
+ * whether WoWLAN is enabled or not, and your code will run even if
+ * WoWLAN is enabled - the NIC may be alive.
+ */
+
+ /*
+ * We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state.
+ */
+ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
+ if (!trans->op_mode)
+ return 0;
+
+ /*
+ * Enable rfkill interrupt (in order to keep track of
+ * the rfkill status)
+ */
+ iwl_enable_rfkill_int(trans);
+
+ hw_rfkill = iwl_is_rfkill_set(trans);
+
+ mutex_lock(&trans_pcie->mutex);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+ mutex_unlock(&trans_pcie->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
+
+#define IWL_PM_OPS (&iwl_dev_pm_ops)
+
+#else
+
+#define IWL_PM_OPS NULL
+
+#endif
+
+static struct pci_driver iwl_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = iwl_hw_card_ids,
+ .probe = iwl_pci_probe,
+ .remove = iwl_pci_remove,
+ .driver.pm = IWL_PM_OPS,
+};
+
+int __must_check iwl_pci_register_driver(void)
+{
+ int ret;
+ ret = pci_register_driver(&iwl_pci_driver);
+ if (ret)
+ pr_err("Unable to initialize PCI module\n");
+
+ return ret;
+}
+
+void iwl_pci_unregister_driver(void)
+{
+ pci_unregister_driver(&iwl_pci_driver);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
new file mode 100644
index 000000000000..cc3888e2700d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
@@ -0,0 +1,582 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#ifndef __iwl_trans_int_pcie_h__
+#define __iwl_trans_int_pcie_h__
+
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+
+#include "iwl-fh.h"
+#include "iwl-csr.h"
+#include "iwl-trans.h"
+#include "iwl-debug.h"
+#include "iwl-io.h"
+#include "iwl-op-mode.h"
+
+/* We need 2 entries for the TX command and header, and another one might
+ * be needed for potential data in the SKB's head. The remaining ones can
+ * be used for frags.
+ */
+#define IWL_PCIE_MAX_FRAGS (IWL_NUM_OF_TBS - 3)
+
+/*
+ * RX related structures and functions
+ */
+#define RX_NUM_QUEUES 1
+#define RX_POST_REQ_ALLOC 2
+#define RX_CLAIM_REQ_ALLOC 8
+#define RX_POOL_SIZE ((RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC) * RX_NUM_QUEUES)
+#define RX_LOW_WATERMARK 8
+
+struct iwl_host_cmd;
+
+/*This file includes the declaration that are internal to the
+ * trans_pcie layer */
+
+struct iwl_rx_mem_buffer {
+ dma_addr_t page_dma;
+ struct page *page;
+ struct list_head list;
+};
+
+/**
+ * struct isr_statistics - interrupt statistics
+ *
+ */
+struct isr_statistics {
+ u32 hw;
+ u32 sw;
+ u32 err_code;
+ u32 sch;
+ u32 alive;
+ u32 rfkill;
+ u32 ctkill;
+ u32 wakeup;
+ u32 rx;
+ u32 tx;
+ u32 unhandled;
+};
+
+/**
+ * struct iwl_rxq - Rx queue
+ * @bd: driver's pointer to buffer of receive buffer descriptors (rbd)
+ * @bd_dma: bus address of buffer of receive buffer descriptors (rbd)
+ * @read: Shared index to newest available Rx buffer
+ * @write: Shared index to oldest written Rx packet
+ * @free_count: Number of pre-allocated buffers in rx_free
+ * @used_count: Number of RBDs handled to allocator to use for allocation
+ * @write_actual:
+ * @rx_free: list of RBDs with allocated RB ready for use
+ * @rx_used: list of RBDs with no RB attached
+ * @need_update: flag to indicate we need to update read/write index
+ * @rb_stts: driver's pointer to receive buffer status
+ * @rb_stts_dma: bus address of receive buffer status
+ * @lock:
+ * @pool: initial pool of iwl_rx_mem_buffer for the queue
+ * @queue: actual rx queue
+ *
+ * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers
+ */
+struct iwl_rxq {
+ __le32 *bd;
+ dma_addr_t bd_dma;
+ u32 read;
+ u32 write;
+ u32 free_count;
+ u32 used_count;
+ u32 write_actual;
+ struct list_head rx_free;
+ struct list_head rx_used;
+ bool need_update;
+ struct iwl_rb_status *rb_stts;
+ dma_addr_t rb_stts_dma;
+ spinlock_t lock;
+ struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE];
+ struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE];
+};
+
+/**
+ * struct iwl_rb_allocator - Rx allocator
+ * @pool: initial pool of allocator
+ * @req_pending: number of requests the allcator had not processed yet
+ * @req_ready: number of requests honored and ready for claiming
+ * @rbd_allocated: RBDs with pages allocated and ready to be handled to
+ * the queue. This is a list of &struct iwl_rx_mem_buffer
+ * @rbd_empty: RBDs with no page attached for allocator use. This is a list
+ * of &struct iwl_rx_mem_buffer
+ * @lock: protects the rbd_allocated and rbd_empty lists
+ * @alloc_wq: work queue for background calls
+ * @rx_alloc: work struct for background calls
+ */
+struct iwl_rb_allocator {
+ struct iwl_rx_mem_buffer pool[RX_POOL_SIZE];
+ atomic_t req_pending;
+ atomic_t req_ready;
+ struct list_head rbd_allocated;
+ struct list_head rbd_empty;
+ spinlock_t lock;
+ struct workqueue_struct *alloc_wq;
+ struct work_struct rx_alloc;
+};
+
+struct iwl_dma_ptr {
+ dma_addr_t dma;
+ void *addr;
+ size_t size;
+};
+
+/**
+ * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
+ * @index -- current index
+ */
+static inline int iwl_queue_inc_wrap(int index)
+{
+ return ++index & (TFD_QUEUE_SIZE_MAX - 1);
+}
+
+/**
+ * iwl_queue_dec_wrap - decrement queue index, wrap back to end
+ * @index -- current index
+ */
+static inline int iwl_queue_dec_wrap(int index)
+{
+ return --index & (TFD_QUEUE_SIZE_MAX - 1);
+}
+
+struct iwl_cmd_meta {
+ /* only for SYNC commands, iff the reply skb is wanted */
+ struct iwl_host_cmd *source;
+ u32 flags;
+};
+
+/*
+ * Generic queue structure
+ *
+ * Contains common data for Rx and Tx queues.
+ *
+ * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
+ * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
+ * there might be HW changes in the future). For the normal TX
+ * queues, n_window, which is the size of the software queue data
+ * is also 256; however, for the command queue, n_window is only
+ * 32 since we don't need so many commands pending. Since the HW
+ * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result,
+ * the software buffers (in the variables @meta, @txb in struct
+ * iwl_txq) only have 32 entries, while the HW buffers (@tfds in
+ * the same struct) have 256.
+ * This means that we end up with the following:
+ * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
+ * SW entries: | 0 | ... | 31 |
+ * where N is a number between 0 and 7. This means that the SW
+ * data is a window overlayed over the HW queue.
+ */
+struct iwl_queue {
+ int write_ptr; /* 1-st empty entry (index) host_w*/
+ int read_ptr; /* last used entry (index) host_r*/
+ /* use for monitoring and recovering the stuck queue */
+ dma_addr_t dma_addr; /* physical addr for BD's */
+ int n_window; /* safe queue window */
+ u32 id;
+ int low_mark; /* low watermark, resume queue if free
+ * space more than this */
+ int high_mark; /* high watermark, stop queue if free
+ * space less than this */
+};
+
+#define TFD_TX_CMD_SLOTS 256
+#define TFD_CMD_SLOTS 32
+
+/*
+ * The FH will write back to the first TB only, so we need
+ * to copy some data into the buffer regardless of whether
+ * it should be mapped or not. This indicates how big the
+ * first TB must be to include the scratch buffer. Since
+ * the scratch is 4 bytes at offset 12, it's 16 now. If we
+ * make it bigger then allocations will be bigger and copy
+ * slower, so that's probably not useful.
+ */
+#define IWL_HCMD_SCRATCHBUF_SIZE 16
+
+struct iwl_pcie_txq_entry {
+ struct iwl_device_cmd *cmd;
+ struct sk_buff *skb;
+ /* buffer to free after command completes */
+ const void *free_buf;
+ struct iwl_cmd_meta meta;
+};
+
+struct iwl_pcie_txq_scratch_buf {
+ struct iwl_cmd_header hdr;
+ u8 buf[8];
+ __le32 scratch;
+};
+
+/**
+ * struct iwl_txq - Tx Queue for DMA
+ * @q: generic Rx/Tx queue descriptor
+ * @tfds: transmit frame descriptors (DMA memory)
+ * @scratchbufs: start of command headers, including scratch buffers, for
+ * the writeback -- this is DMA memory and an array holding one buffer
+ * for each command on the queue
+ * @scratchbufs_dma: DMA address for the scratchbufs start
+ * @entries: transmit entries (driver state)
+ * @lock: queue lock
+ * @stuck_timer: timer that fires if queue gets stuck
+ * @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
+ * @wd_timeout: queue watchdog timeout (jiffies) - per queue
+ * @frozen: tx stuck queue timer is frozen
+ * @frozen_expiry_remainder: remember how long until the timer fires
+ *
+ * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
+ * descriptors) and required locking structures.
+ */
+struct iwl_txq {
+ struct iwl_queue q;
+ struct iwl_tfd *tfds;
+ struct iwl_pcie_txq_scratch_buf *scratchbufs;
+ dma_addr_t scratchbufs_dma;
+ struct iwl_pcie_txq_entry *entries;
+ spinlock_t lock;
+ unsigned long frozen_expiry_remainder;
+ struct timer_list stuck_timer;
+ struct iwl_trans_pcie *trans_pcie;
+ bool need_update;
+ bool frozen;
+ u8 active;
+ bool ampdu;
+ bool block;
+ unsigned long wd_timeout;
+};
+
+static inline dma_addr_t
+iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
+{
+ return txq->scratchbufs_dma +
+ sizeof(struct iwl_pcie_txq_scratch_buf) * idx;
+}
+
+struct iwl_tso_hdr_page {
+ struct page *page;
+ u8 *pos;
+};
+
+/**
+ * struct iwl_trans_pcie - PCIe transport specific data
+ * @rxq: all the RX queue data
+ * @rba: allocator for RX replenishing
+ * @drv - pointer to iwl_drv
+ * @trans: pointer to the generic transport area
+ * @scd_base_addr: scheduler sram base address in SRAM
+ * @scd_bc_tbls: pointer to the byte count table of the scheduler
+ * @kw: keep warm address
+ * @pci_dev: basic pci-network driver stuff
+ * @hw_base: pci hardware address support
+ * @ucode_write_complete: indicates that the ucode has been copied.
+ * @ucode_write_waitq: wait queue for uCode load
+ * @cmd_queue - command queue number
+ * @rx_buf_size: Rx buffer size
+ * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
+ * @scd_set_active: should the transport configure the SCD for HCMD queue
+ * @wide_cmd_header: true when ucode supports wide command header format
+ * @sw_csum_tx: if true, then the transport will compute the csum of the TXed
+ * frame.
+ * @rx_page_order: page order for receive buffer size
+ * @reg_lock: protect hw register access
+ * @mutex: to protect stop_device / start_fw / start_hw
+ * @cmd_in_flight: true when we have a host command in flight
+ * @fw_mon_phys: physical address of the buffer for the firmware monitor
+ * @fw_mon_page: points to the first page of the buffer for the firmware monitor
+ * @fw_mon_size: size of the buffer for the firmware monitor
+ */
+struct iwl_trans_pcie {
+ struct iwl_rxq rxq;
+ struct iwl_rb_allocator rba;
+ struct iwl_trans *trans;
+ struct iwl_drv *drv;
+
+ struct net_device napi_dev;
+ struct napi_struct napi;
+
+ struct __percpu iwl_tso_hdr_page *tso_hdr_page;
+
+ /* INT ICT Table */
+ __le32 *ict_tbl;
+ dma_addr_t ict_tbl_dma;
+ int ict_index;
+ bool use_ict;
+ bool is_down;
+ struct isr_statistics isr_stats;
+
+ spinlock_t irq_lock;
+ struct mutex mutex;
+ u32 inta_mask;
+ u32 scd_base_addr;
+ struct iwl_dma_ptr scd_bc_tbls;
+ struct iwl_dma_ptr kw;
+
+ struct iwl_txq *txq;
+ unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
+ unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
+
+ /* PCI bus related data */
+ struct pci_dev *pci_dev;
+ void __iomem *hw_base;
+
+ bool ucode_write_complete;
+ wait_queue_head_t ucode_write_waitq;
+ wait_queue_head_t wait_command_queue;
+
+ u8 cmd_queue;
+ u8 cmd_fifo;
+ unsigned int cmd_q_wdg_timeout;
+ u8 n_no_reclaim_cmds;
+ u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
+
+ enum iwl_amsdu_size rx_buf_size;
+ bool bc_table_dword;
+ bool scd_set_active;
+ bool wide_cmd_header;
+ bool sw_csum_tx;
+ u32 rx_page_order;
+
+ /*protect hw register */
+ spinlock_t reg_lock;
+ bool cmd_hold_nic_awake;
+ bool ref_cmd_in_flight;
+
+ /* protect ref counter */
+ spinlock_t ref_lock;
+ u32 ref_count;
+
+ dma_addr_t fw_mon_phys;
+ struct page *fw_mon_page;
+ u32 fw_mon_size;
+};
+
+static inline struct iwl_trans_pcie *
+IWL_TRANS_GET_PCIE_TRANS(struct iwl_trans *trans)
+{
+ return (void *)trans->trans_specific;
+}
+
+static inline struct iwl_trans *
+iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie)
+{
+ return container_of((void *)trans_pcie, struct iwl_trans,
+ trans_specific);
+}
+
+/*
+ * Convention: trans API functions: iwl_trans_pcie_XXX
+ * Other functions: iwl_pcie_XXX
+ */
+struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
+ const struct pci_device_id *ent,
+ const struct iwl_cfg *cfg);
+void iwl_trans_pcie_free(struct iwl_trans *trans);
+
+/*****************************************************
+* RX
+******************************************************/
+int iwl_pcie_rx_init(struct iwl_trans *trans);
+irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id);
+int iwl_pcie_rx_stop(struct iwl_trans *trans);
+void iwl_pcie_rx_free(struct iwl_trans *trans);
+
+/*****************************************************
+* ICT - interrupt handling
+******************************************************/
+irqreturn_t iwl_pcie_isr(int irq, void *data);
+int iwl_pcie_alloc_ict(struct iwl_trans *trans);
+void iwl_pcie_free_ict(struct iwl_trans *trans);
+void iwl_pcie_reset_ict(struct iwl_trans *trans);
+void iwl_pcie_disable_ict(struct iwl_trans *trans);
+
+/*****************************************************
+* TX / HCMD
+******************************************************/
+int iwl_pcie_tx_init(struct iwl_trans *trans);
+void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr);
+int iwl_pcie_tx_stop(struct iwl_trans *trans);
+void iwl_pcie_tx_free(struct iwl_trans *trans);
+void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn,
+ const struct iwl_trans_txq_scd_cfg *cfg,
+ unsigned int wdg_timeout);
+void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue,
+ bool configure_scd);
+int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_device_cmd *dev_cmd, int txq_id);
+void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans);
+int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
+void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
+ struct sk_buff_head *skbs);
+void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
+
+void iwl_trans_pcie_ref(struct iwl_trans *trans);
+void iwl_trans_pcie_unref(struct iwl_trans *trans);
+
+static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+ return le16_to_cpu(tb->hi_n_len) >> 4;
+}
+
+/*****************************************************
+* Error handling
+******************************************************/
+void iwl_pcie_dump_csr(struct iwl_trans *trans);
+
+/*****************************************************
+* Helpers
+******************************************************/
+static inline void iwl_disable_interrupts(struct iwl_trans *trans)
+{
+ clear_bit(STATUS_INT_ENABLED, &trans->status);
+
+ /* disable interrupts from uCode/NIC to host */
+ iwl_write32(trans, CSR_INT_MASK, 0x00000000);
+
+ /* acknowledge/clear/reset any interrupts still pending
+ * from uCode or flow handler (Rx/Tx DMA) */
+ iwl_write32(trans, CSR_INT, 0xffffffff);
+ iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff);
+ IWL_DEBUG_ISR(trans, "Disabled interrupts\n");
+}
+
+static inline void iwl_enable_interrupts(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ IWL_DEBUG_ISR(trans, "Enabling interrupts\n");
+ set_bit(STATUS_INT_ENABLED, &trans->status);
+ trans_pcie->inta_mask = CSR_INI_SET_MASK;
+ iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+}
+
+static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n");
+ trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL;
+ iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+}
+
+static inline void iwl_wake_queue(struct iwl_trans *trans,
+ struct iwl_txq *txq)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) {
+ IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id);
+ iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id);
+ }
+}
+
+static inline void iwl_stop_queue(struct iwl_trans *trans,
+ struct iwl_txq *txq)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) {
+ iwl_op_mode_queue_full(trans->op_mode, txq->q.id);
+ IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id);
+ } else
+ IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n",
+ txq->q.id);
+}
+
+static inline bool iwl_queue_used(const struct iwl_queue *q, int i)
+{
+ return q->write_ptr >= q->read_ptr ?
+ (i >= q->read_ptr && i < q->write_ptr) :
+ !(i < q->read_ptr && i >= q->write_ptr);
+}
+
+static inline u8 get_cmd_index(struct iwl_queue *q, u32 index)
+{
+ return index & (q->n_window - 1);
+}
+
+static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
+{
+ return !(iwl_read32(trans, CSR_GP_CNTRL) &
+ CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
+}
+
+static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
+ u32 reg, u32 mask, u32 value)
+{
+ u32 v;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ WARN_ON_ONCE(value & ~mask);
+#endif
+
+ v = iwl_read32(trans, reg);
+ v &= ~mask;
+ v |= value;
+ iwl_write32(trans, reg, v);
+}
+
+static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
+ u32 reg, u32 mask)
+{
+ __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
+}
+
+static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
+ u32 reg, u32 mask)
+{
+ __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
+}
+
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans);
+#else
+static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
+{
+ return 0;
+}
+#endif
+
+#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
new file mode 100644
index 000000000000..ccafbd8cf4b3
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
@@ -0,0 +1,1556 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/gfp.h>
+
+#include "iwl-prph.h"
+#include "iwl-io.h"
+#include "internal.h"
+#include "iwl-op-mode.h"
+
+/******************************************************************************
+ *
+ * RX path functions
+ *
+ ******************************************************************************/
+
+/*
+ * Rx theory of operation
+ *
+ * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
+ * each of which point to Receive Buffers to be filled by the NIC. These get
+ * used not only for Rx frames, but for any command response or notification
+ * from the NIC. The driver and NIC manage the Rx buffers by means
+ * of indexes into the circular buffer.
+ *
+ * Rx Queue Indexes
+ * The host/firmware share two index registers for managing the Rx buffers.
+ *
+ * The READ index maps to the first position that the firmware may be writing
+ * to -- the driver can read up to (but not including) this position and get
+ * good data.
+ * The READ index is managed by the firmware once the card is enabled.
+ *
+ * The WRITE index maps to the last position the driver has read from -- the
+ * position preceding WRITE is the last slot the firmware can place a packet.
+ *
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
+ * WRITE = READ.
+ *
+ * During initialization, the host sets up the READ queue position to the first
+ * INDEX position, and WRITE to the last (READ - 1 wrapped)
+ *
+ * When the firmware places a packet in a buffer, it will advance the READ index
+ * and fire the RX interrupt. The driver can then query the READ index and
+ * process as many packets as possible, moving the WRITE index forward as it
+ * resets the Rx queue buffers with new memory.
+ *
+ * The management in the driver is as follows:
+ * + A list of pre-allocated RBDs is stored in iwl->rxq->rx_free.
+ * When the interrupt handler is called, the request is processed.
+ * The page is either stolen - transferred to the upper layer
+ * or reused - added immediately to the iwl->rxq->rx_free list.
+ * + When the page is stolen - the driver updates the matching queue's used
+ * count, detaches the RBD and transfers it to the queue used list.
+ * When there are two used RBDs - they are transferred to the allocator empty
+ * list. Work is then scheduled for the allocator to start allocating
+ * eight buffers.
+ * When there are another 6 used RBDs - they are transferred to the allocator
+ * empty list and the driver tries to claim the pre-allocated buffers and
+ * add them to iwl->rxq->rx_free. If it fails - it continues to claim them
+ * until ready.
+ * When there are 8+ buffers in the free list - either from allocation or from
+ * 8 reused unstolen pages - restock is called to update the FW and indexes.
+ * + In order to make sure the allocator always has RBDs to use for allocation
+ * the allocator has initial pool in the size of num_queues*(8-2) - the
+ * maximum missing RBDs per allocation request (request posted with 2
+ * empty RBDs, there is no guarantee when the other 6 RBDs are supplied).
+ * The queues supplies the recycle of the rest of the RBDs.
+ * + A received packet is processed and handed to the kernel network stack,
+ * detached from the iwl->rxq. The driver 'processed' index is updated.
+ * + If there are no allocated buffers in iwl->rxq->rx_free,
+ * the READ INDEX is not incremented and iwl->status(RX_STALLED) is set.
+ * If there were enough free buffers and RX_STALLED is set it is cleared.
+ *
+ *
+ * Driver sequence:
+ *
+ * iwl_rxq_alloc() Allocates rx_free
+ * iwl_pcie_rx_replenish() Replenishes rx_free list from rx_used, and calls
+ * iwl_pcie_rxq_restock.
+ * Used only during initialization.
+ * iwl_pcie_rxq_restock() Moves available buffers from rx_free into Rx
+ * queue, updates firmware pointers, and updates
+ * the WRITE index.
+ * iwl_pcie_rx_allocator() Background work for allocating pages.
+ *
+ * -- enable interrupts --
+ * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the
+ * READ INDEX, detaching the SKB from the pool.
+ * Moves the packet buffer from queue to rx_used.
+ * Posts and claims requests to the allocator.
+ * Calls iwl_pcie_rxq_restock to refill any empty
+ * slots.
+ *
+ * RBD life-cycle:
+ *
+ * Init:
+ * rxq.pool -> rxq.rx_used -> rxq.rx_free -> rxq.queue
+ *
+ * Regular Receive interrupt:
+ * Page Stolen:
+ * rxq.queue -> rxq.rx_used -> allocator.rbd_empty ->
+ * allocator.rbd_allocated -> rxq.rx_free -> rxq.queue
+ * Page not Stolen:
+ * rxq.queue -> rxq.rx_free -> rxq.queue
+ * ...
+ *
+ */
+
+/*
+ * iwl_rxq_space - Return number of free slots available in queue.
+ */
+static int iwl_rxq_space(const struct iwl_rxq *rxq)
+{
+ /* Make sure RX_QUEUE_SIZE is a power of 2 */
+ BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1));
+
+ /*
+ * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity
+ * between empty and completely full queues.
+ * The following is equivalent to modulo by RX_QUEUE_SIZE and is well
+ * defined for negative dividends.
+ */
+ return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1);
+}
+
+/*
+ * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
+ */
+static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr)
+{
+ return cpu_to_le32((u32)(dma_addr >> 8));
+}
+
+/*
+ * iwl_pcie_rx_stop - stops the Rx DMA
+ */
+int iwl_pcie_rx_stop(struct iwl_trans *trans)
+{
+ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+ return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG,
+ FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
+}
+
+/*
+ * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue
+ */
+static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ u32 reg;
+
+ lockdep_assert_held(&rxq->lock);
+
+ /*
+ * explicitly wake up the NIC if:
+ * 1. shadow registers aren't enabled
+ * 2. there is a chance that the NIC is asleep
+ */
+ if (!trans->cfg->base_params->shadow_reg_enable &&
+ test_bit(STATUS_TPOWER_PMI, &trans->status)) {
+ reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n",
+ reg);
+ iwl_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ rxq->need_update = true;
+ return;
+ }
+ }
+
+ rxq->write_actual = round_down(rxq->write, 8);
+ iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
+}
+
+static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+
+ spin_lock(&rxq->lock);
+
+ if (!rxq->need_update)
+ goto exit_unlock;
+
+ iwl_pcie_rxq_inc_wr_ptr(trans);
+ rxq->need_update = false;
+
+ exit_unlock:
+ spin_unlock(&rxq->lock);
+}
+
+/*
+ * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool
+ *
+ * If there are slots in the RX queue that need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can, pulling from rx_free.
+ *
+ * This moves the 'write' index forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ struct iwl_rx_mem_buffer *rxb;
+
+ /*
+ * If the device isn't enabled - not need to try to add buffers...
+ * This can happen when we stop the device and still have an interrupt
+ * pending. We stop the APM before we sync the interrupts because we
+ * have to (see comment there). On the other hand, since the APM is
+ * stopped, we cannot access the HW (in particular not prph).
+ * So don't try to restock if the APM has been already stopped.
+ */
+ if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
+ return;
+
+ spin_lock(&rxq->lock);
+ while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) {
+ /* The overwritten rxb must be a used one */
+ rxb = rxq->queue[rxq->write];
+ BUG_ON(rxb && rxb->page);
+
+ /* Get next free Rx buffer, remove from free list */
+ rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer,
+ list);
+ list_del(&rxb->list);
+
+ /* Point to Rx buffer via next RBD in circular buffer */
+ rxq->bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma);
+ rxq->queue[rxq->write] = rxb;
+ rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
+ rxq->free_count--;
+ }
+ spin_unlock(&rxq->lock);
+
+ /* If we've added more space for the firmware to place data, tell it.
+ * Increment device's write pointer in multiples of 8. */
+ if (rxq->write_actual != (rxq->write & ~0x7)) {
+ spin_lock(&rxq->lock);
+ iwl_pcie_rxq_inc_wr_ptr(trans);
+ spin_unlock(&rxq->lock);
+ }
+}
+
+/*
+ * iwl_pcie_rx_alloc_page - allocates and returns a page.
+ *
+ */
+static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans,
+ gfp_t priority)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ struct page *page;
+ gfp_t gfp_mask = priority;
+
+ if (rxq->free_count > RX_LOW_WATERMARK)
+ gfp_mask |= __GFP_NOWARN;
+
+ if (trans_pcie->rx_page_order > 0)
+ gfp_mask |= __GFP_COMP;
+
+ /* Alloc a new receive buffer */
+ page = alloc_pages(gfp_mask, trans_pcie->rx_page_order);
+ if (!page) {
+ if (net_ratelimit())
+ IWL_DEBUG_INFO(trans, "alloc_pages failed, order: %d\n",
+ trans_pcie->rx_page_order);
+ /* Issue an error if the hardware has consumed more than half
+ * of its free buffer list and we don't have enough
+ * pre-allocated buffers.
+` */
+ if (rxq->free_count <= RX_LOW_WATERMARK &&
+ iwl_rxq_space(rxq) > (RX_QUEUE_SIZE / 2) &&
+ net_ratelimit())
+ IWL_CRIT(trans,
+ "Failed to alloc_pages with GFP_KERNEL. Only %u free buffers remaining.\n",
+ rxq->free_count);
+ return NULL;
+ }
+ return page;
+}
+
+/*
+ * iwl_pcie_rxq_alloc_rbs - allocate a page for each used RBD
+ *
+ * A used RBD is an Rx buffer that has been given to the stack. To use it again
+ * a page must be allocated and the RBD must point to the page. This function
+ * doesn't change the HW pointer but handles the list of pages that is used by
+ * iwl_pcie_rxq_restock. The latter function will update the HW to use the newly
+ * allocated buffers.
+ */
+static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ struct iwl_rx_mem_buffer *rxb;
+ struct page *page;
+
+ while (1) {
+ spin_lock(&rxq->lock);
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock(&rxq->lock);
+ return;
+ }
+ spin_unlock(&rxq->lock);
+
+ /* Alloc a new receive buffer */
+ page = iwl_pcie_rx_alloc_page(trans, priority);
+ if (!page)
+ return;
+
+ spin_lock(&rxq->lock);
+
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock(&rxq->lock);
+ __free_pages(page, trans_pcie->rx_page_order);
+ return;
+ }
+ rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer,
+ list);
+ list_del(&rxb->list);
+ spin_unlock(&rxq->lock);
+
+ BUG_ON(rxb->page);
+ rxb->page = page;
+ /* Get physical address of the RB */
+ rxb->page_dma =
+ dma_map_page(trans->dev, page, 0,
+ PAGE_SIZE << trans_pcie->rx_page_order,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(trans->dev, rxb->page_dma)) {
+ rxb->page = NULL;
+ spin_lock(&rxq->lock);
+ list_add(&rxb->list, &rxq->rx_used);
+ spin_unlock(&rxq->lock);
+ __free_pages(page, trans_pcie->rx_page_order);
+ return;
+ }
+ /* dma address must be no more than 36 bits */
+ BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
+ /* and also 256 byte aligned! */
+ BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
+
+ spin_lock(&rxq->lock);
+
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+
+ spin_unlock(&rxq->lock);
+ }
+}
+
+static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ int i;
+
+ lockdep_assert_held(&rxq->lock);
+
+ for (i = 0; i < RX_QUEUE_SIZE; i++) {
+ if (!rxq->pool[i].page)
+ continue;
+ dma_unmap_page(trans->dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << trans_pcie->rx_page_order,
+ DMA_FROM_DEVICE);
+ __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order);
+ rxq->pool[i].page = NULL;
+ }
+}
+
+/*
+ * iwl_pcie_rx_replenish - Move all used buffers from rx_used to rx_free
+ *
+ * When moving to rx_free an page is allocated for the slot.
+ *
+ * Also restock the Rx queue via iwl_pcie_rxq_restock.
+ * This is called only during initialization
+ */
+static void iwl_pcie_rx_replenish(struct iwl_trans *trans)
+{
+ iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL);
+
+ iwl_pcie_rxq_restock(trans);
+}
+
+/*
+ * iwl_pcie_rx_allocator - Allocates pages in the background for RX queues
+ *
+ * Allocates for each received request 8 pages
+ * Called as a scheduled work item.
+ */
+static void iwl_pcie_rx_allocator(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+ struct list_head local_empty;
+ int pending = atomic_xchg(&rba->req_pending, 0);
+
+ IWL_DEBUG_RX(trans, "Pending allocation requests = %d\n", pending);
+
+ /* If we were scheduled - there is at least one request */
+ spin_lock(&rba->lock);
+ /* swap out the rba->rbd_empty to a local list */
+ list_replace_init(&rba->rbd_empty, &local_empty);
+ spin_unlock(&rba->lock);
+
+ while (pending) {
+ int i;
+ struct list_head local_allocated;
+
+ INIT_LIST_HEAD(&local_allocated);
+
+ for (i = 0; i < RX_CLAIM_REQ_ALLOC;) {
+ struct iwl_rx_mem_buffer *rxb;
+ struct page *page;
+
+ /* List should never be empty - each reused RBD is
+ * returned to the list, and initial pool covers any
+ * possible gap between the time the page is allocated
+ * to the time the RBD is added.
+ */
+ BUG_ON(list_empty(&local_empty));
+ /* Get the first rxb from the rbd list */
+ rxb = list_first_entry(&local_empty,
+ struct iwl_rx_mem_buffer, list);
+ BUG_ON(rxb->page);
+
+ /* Alloc a new receive buffer */
+ page = iwl_pcie_rx_alloc_page(trans, GFP_KERNEL);
+ if (!page)
+ continue;
+ rxb->page = page;
+
+ /* Get physical address of the RB */
+ rxb->page_dma = dma_map_page(trans->dev, page, 0,
+ PAGE_SIZE << trans_pcie->rx_page_order,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(trans->dev, rxb->page_dma)) {
+ rxb->page = NULL;
+ __free_pages(page, trans_pcie->rx_page_order);
+ continue;
+ }
+ /* dma address must be no more than 36 bits */
+ BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
+ /* and also 256 byte aligned! */
+ BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
+
+ /* move the allocated entry to the out list */
+ list_move(&rxb->list, &local_allocated);
+ i++;
+ }
+
+ pending--;
+ if (!pending) {
+ pending = atomic_xchg(&rba->req_pending, 0);
+ IWL_DEBUG_RX(trans,
+ "Pending allocation requests = %d\n",
+ pending);
+ }
+
+ spin_lock(&rba->lock);
+ /* add the allocated rbds to the allocator allocated list */
+ list_splice_tail(&local_allocated, &rba->rbd_allocated);
+ /* get more empty RBDs for current pending requests */
+ list_splice_tail_init(&rba->rbd_empty, &local_empty);
+ spin_unlock(&rba->lock);
+
+ atomic_inc(&rba->req_ready);
+ }
+
+ spin_lock(&rba->lock);
+ /* return unused rbds to the allocator empty list */
+ list_splice_tail(&local_empty, &rba->rbd_empty);
+ spin_unlock(&rba->lock);
+}
+
+/*
+ * iwl_pcie_rx_allocator_get - Returns the pre-allocated pages
+.*
+.* Called by queue when the queue posted allocation request and
+ * has freed 8 RBDs in order to restock itself.
+ */
+static int iwl_pcie_rx_allocator_get(struct iwl_trans *trans,
+ struct iwl_rx_mem_buffer
+ *out[RX_CLAIM_REQ_ALLOC])
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+ int i;
+
+ /*
+ * atomic_dec_if_positive returns req_ready - 1 for any scenario.
+ * If req_ready is 0 atomic_dec_if_positive will return -1 and this
+ * function will return -ENOMEM, as there are no ready requests.
+ * atomic_dec_if_positive will perofrm the *actual* decrement only if
+ * req_ready > 0, i.e. - there are ready requests and the function
+ * hands one request to the caller.
+ */
+ if (atomic_dec_if_positive(&rba->req_ready) < 0)
+ return -ENOMEM;
+
+ spin_lock(&rba->lock);
+ for (i = 0; i < RX_CLAIM_REQ_ALLOC; i++) {
+ /* Get next free Rx buffer, remove it from free list */
+ out[i] = list_first_entry(&rba->rbd_allocated,
+ struct iwl_rx_mem_buffer, list);
+ list_del(&out[i]->list);
+ }
+ spin_unlock(&rba->lock);
+
+ return 0;
+}
+
+static void iwl_pcie_rx_allocator_work(struct work_struct *data)
+{
+ struct iwl_rb_allocator *rba_p =
+ container_of(data, struct iwl_rb_allocator, rx_alloc);
+ struct iwl_trans_pcie *trans_pcie =
+ container_of(rba_p, struct iwl_trans_pcie, rba);
+
+ iwl_pcie_rx_allocator(trans_pcie->trans);
+}
+
+static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+ struct device *dev = trans->dev;
+
+ memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq));
+
+ spin_lock_init(&rxq->lock);
+ spin_lock_init(&rba->lock);
+
+ if (WARN_ON(rxq->bd || rxq->rb_stts))
+ return -EINVAL;
+
+ /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */
+ rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
+ &rxq->bd_dma, GFP_KERNEL);
+ if (!rxq->bd)
+ goto err_bd;
+
+ /*Allocate the driver's pointer to receive buffer status */
+ rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts),
+ &rxq->rb_stts_dma, GFP_KERNEL);
+ if (!rxq->rb_stts)
+ goto err_rb_stts;
+
+ return 0;
+
+err_rb_stts:
+ dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
+ rxq->bd, rxq->bd_dma);
+ rxq->bd_dma = 0;
+ rxq->bd = NULL;
+err_bd:
+ return -ENOMEM;
+}
+
+static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 rb_size;
+ const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
+
+ switch (trans_pcie->rx_buf_size) {
+ case IWL_AMSDU_4K:
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
+ break;
+ case IWL_AMSDU_8K:
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
+ break;
+ case IWL_AMSDU_12K:
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K;
+ break;
+ default:
+ WARN_ON(1);
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
+ }
+
+ /* Stop Rx DMA */
+ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+ /* reset and flush pointers */
+ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
+ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
+ iwl_write_direct32(trans, FH_RSCSR_CHNL0_RDPTR, 0);
+
+ /* Reset driver's Rx queue write index */
+ iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
+
+ /* Tell device where to find RBD circular buffer in DRAM */
+ iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+ (u32)(rxq->bd_dma >> 8));
+
+ /* Tell device where in DRAM to update its Rx status */
+ iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG,
+ rxq->rb_stts_dma >> 4);
+
+ /* Enable Rx DMA
+ * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in
+ * the credit mechanism in 5000 HW RX FIFO
+ * Direct rx interrupts to hosts
+ * Rx buffer size 4 or 8k or 12k
+ * RB timeout 0x10
+ * 256 RBDs
+ */
+ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG,
+ FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
+ FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
+ FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
+ rb_size|
+ (RX_RB_TIMEOUT << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
+ (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
+
+ /* Set interrupt coalescing timer to default (2048 usecs) */
+ iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
+
+ /* W/A for interrupt coalescing bug in 7260 and 3160 */
+ if (trans->cfg->host_interrupt_operation_mode)
+ iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE);
+}
+
+static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq)
+{
+ int i;
+
+ lockdep_assert_held(&rxq->lock);
+
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+ rxq->free_count = 0;
+ rxq->used_count = 0;
+
+ for (i = 0; i < RX_QUEUE_SIZE; i++)
+ list_add(&rxq->pool[i].list, &rxq->rx_used);
+}
+
+static void iwl_pcie_rx_init_rba(struct iwl_rb_allocator *rba)
+{
+ int i;
+
+ lockdep_assert_held(&rba->lock);
+
+ INIT_LIST_HEAD(&rba->rbd_allocated);
+ INIT_LIST_HEAD(&rba->rbd_empty);
+
+ for (i = 0; i < RX_POOL_SIZE; i++)
+ list_add(&rba->pool[i].list, &rba->rbd_empty);
+}
+
+static void iwl_pcie_rx_free_rba(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+ int i;
+
+ lockdep_assert_held(&rba->lock);
+
+ for (i = 0; i < RX_POOL_SIZE; i++) {
+ if (!rba->pool[i].page)
+ continue;
+ dma_unmap_page(trans->dev, rba->pool[i].page_dma,
+ PAGE_SIZE << trans_pcie->rx_page_order,
+ DMA_FROM_DEVICE);
+ __free_pages(rba->pool[i].page, trans_pcie->rx_page_order);
+ rba->pool[i].page = NULL;
+ }
+}
+
+int iwl_pcie_rx_init(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+ int i, err;
+
+ if (!rxq->bd) {
+ err = iwl_pcie_rx_alloc(trans);
+ if (err)
+ return err;
+ }
+ if (!rba->alloc_wq)
+ rba->alloc_wq = alloc_workqueue("rb_allocator",
+ WQ_HIGHPRI | WQ_UNBOUND, 1);
+ INIT_WORK(&rba->rx_alloc, iwl_pcie_rx_allocator_work);
+
+ spin_lock(&rba->lock);
+ atomic_set(&rba->req_pending, 0);
+ atomic_set(&rba->req_ready, 0);
+ /* free all first - we might be reconfigured for a different size */
+ iwl_pcie_rx_free_rba(trans);
+ iwl_pcie_rx_init_rba(rba);
+ spin_unlock(&rba->lock);
+
+ spin_lock(&rxq->lock);
+
+ /* free all first - we might be reconfigured for a different size */
+ iwl_pcie_rxq_free_rbs(trans);
+ iwl_pcie_rx_init_rxb_lists(rxq);
+
+ for (i = 0; i < RX_QUEUE_SIZE; i++)
+ rxq->queue[i] = NULL;
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->write_actual = 0;
+ memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
+ spin_unlock(&rxq->lock);
+
+ iwl_pcie_rx_replenish(trans);
+
+ iwl_pcie_rx_hw_init(trans, rxq);
+
+ spin_lock(&rxq->lock);
+ iwl_pcie_rxq_inc_wr_ptr(trans);
+ spin_unlock(&rxq->lock);
+
+ return 0;
+}
+
+void iwl_pcie_rx_free(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+
+ /*if rxq->bd is NULL, it means that nothing has been allocated,
+ * exit now */
+ if (!rxq->bd) {
+ IWL_DEBUG_INFO(trans, "Free NULL rx context\n");
+ return;
+ }
+
+ cancel_work_sync(&rba->rx_alloc);
+ if (rba->alloc_wq) {
+ destroy_workqueue(rba->alloc_wq);
+ rba->alloc_wq = NULL;
+ }
+
+ spin_lock(&rba->lock);
+ iwl_pcie_rx_free_rba(trans);
+ spin_unlock(&rba->lock);
+
+ spin_lock(&rxq->lock);
+ iwl_pcie_rxq_free_rbs(trans);
+ spin_unlock(&rxq->lock);
+
+ dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE,
+ rxq->bd, rxq->bd_dma);
+ rxq->bd_dma = 0;
+ rxq->bd = NULL;
+
+ if (rxq->rb_stts)
+ dma_free_coherent(trans->dev,
+ sizeof(struct iwl_rb_status),
+ rxq->rb_stts, rxq->rb_stts_dma);
+ else
+ IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n");
+ rxq->rb_stts_dma = 0;
+ rxq->rb_stts = NULL;
+}
+
+/*
+ * iwl_pcie_rx_reuse_rbd - Recycle used RBDs
+ *
+ * Called when a RBD can be reused. The RBD is transferred to the allocator.
+ * When there are 2 empty RBDs - a request for allocation is posted
+ */
+static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans,
+ struct iwl_rx_mem_buffer *rxb,
+ struct iwl_rxq *rxq, bool emergency)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+
+ /* Move the RBD to the used list, will be moved to allocator in batches
+ * before claiming or posting a request*/
+ list_add_tail(&rxb->list, &rxq->rx_used);
+
+ if (unlikely(emergency))
+ return;
+
+ /* Count the allocator owned RBDs */
+ rxq->used_count++;
+
+ /* If we have RX_POST_REQ_ALLOC new released rx buffers -
+ * issue a request for allocator. Modulo RX_CLAIM_REQ_ALLOC is
+ * used for the case we failed to claim RX_CLAIM_REQ_ALLOC,
+ * after but we still need to post another request.
+ */
+ if ((rxq->used_count % RX_CLAIM_REQ_ALLOC) == RX_POST_REQ_ALLOC) {
+ /* Move the 2 RBDs to the allocator ownership.
+ Allocator has another 6 from pool for the request completion*/
+ spin_lock(&rba->lock);
+ list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty);
+ spin_unlock(&rba->lock);
+
+ atomic_inc(&rba->req_pending);
+ queue_work(rba->alloc_wq, &rba->rx_alloc);
+ }
+}
+
+static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
+ struct iwl_rx_mem_buffer *rxb,
+ bool emergency)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
+ bool page_stolen = false;
+ int max_len = PAGE_SIZE << trans_pcie->rx_page_order;
+ u32 offset = 0;
+
+ if (WARN_ON(!rxb))
+ return;
+
+ dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE);
+
+ while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) {
+ struct iwl_rx_packet *pkt;
+ u16 sequence;
+ bool reclaim;
+ int index, cmd_index, len;
+ struct iwl_rx_cmd_buffer rxcb = {
+ ._offset = offset,
+ ._rx_page_order = trans_pcie->rx_page_order,
+ ._page = rxb->page,
+ ._page_stolen = false,
+ .truesize = max_len,
+ };
+
+ pkt = rxb_addr(&rxcb);
+
+ if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
+ break;
+
+ IWL_DEBUG_RX(trans,
+ "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n",
+ rxcb._offset,
+ iwl_get_cmd_string(trans,
+ iwl_cmd_id(pkt->hdr.cmd,
+ pkt->hdr.group_id,
+ 0)),
+ pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence));
+
+ len = iwl_rx_packet_len(pkt);
+ len += sizeof(u32); /* account for status word */
+ trace_iwlwifi_dev_rx(trans->dev, trans, pkt, len);
+ trace_iwlwifi_dev_rx_data(trans->dev, trans, pkt, len);
+
+ /* Reclaim a command buffer only if this packet is a response
+ * to a (driver-originated) command.
+ * If the packet (e.g. Rx frame) originated from uCode,
+ * there is no command buffer to reclaim.
+ * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
+ * but apparently a few don't get set; catch them here. */
+ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
+ if (reclaim) {
+ int i;
+
+ for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
+ if (trans_pcie->no_reclaim_cmds[i] ==
+ pkt->hdr.cmd) {
+ reclaim = false;
+ break;
+ }
+ }
+ }
+
+ sequence = le16_to_cpu(pkt->hdr.sequence);
+ index = SEQ_TO_INDEX(sequence);
+ cmd_index = get_cmd_index(&txq->q, index);
+
+ iwl_op_mode_rx(trans->op_mode, &trans_pcie->napi, &rxcb);
+
+ if (reclaim) {
+ kzfree(txq->entries[cmd_index].free_buf);
+ txq->entries[cmd_index].free_buf = NULL;
+ }
+
+ /*
+ * After here, we should always check rxcb._page_stolen,
+ * if it is true then one of the handlers took the page.
+ */
+
+ if (reclaim) {
+ /* Invoke any callbacks, transfer the buffer to caller,
+ * and fire off the (possibly) blocking
+ * iwl_trans_send_cmd()
+ * as we reclaim the driver command queue */
+ if (!rxcb._page_stolen)
+ iwl_pcie_hcmd_complete(trans, &rxcb);
+ else
+ IWL_WARN(trans, "Claim null rxb?\n");
+ }
+
+ page_stolen |= rxcb._page_stolen;
+ offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN);
+ }
+
+ /* page was stolen from us -- free our reference */
+ if (page_stolen) {
+ __free_pages(rxb->page, trans_pcie->rx_page_order);
+ rxb->page = NULL;
+ }
+
+ /* Reuse the page if possible. For notification packets and
+ * SKBs that fail to Rx correctly, add them back into the
+ * rx_free list for reuse later. */
+ if (rxb->page != NULL) {
+ rxb->page_dma =
+ dma_map_page(trans->dev, rxb->page, 0,
+ PAGE_SIZE << trans_pcie->rx_page_order,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(trans->dev, rxb->page_dma)) {
+ /*
+ * free the page(s) as well to not break
+ * the invariant that the items on the used
+ * list have no page(s)
+ */
+ __free_pages(rxb->page, trans_pcie->rx_page_order);
+ rxb->page = NULL;
+ iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency);
+ } else {
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ }
+ } else
+ iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency);
+}
+
+/*
+ * iwl_pcie_rx_handle - Main entry function for receiving responses from fw
+ */
+static void iwl_pcie_rx_handle(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ u32 r, i, j, count = 0;
+ bool emergency = false;
+
+restart:
+ spin_lock(&rxq->lock);
+ /* uCode's read index (stored in shared DRAM) indicates the last Rx
+ * buffer that the driver may process (last buffer filled by ucode). */
+ r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
+ i = rxq->read;
+
+ /* Rx interrupt, but nothing sent from uCode */
+ if (i == r)
+ IWL_DEBUG_RX(trans, "HW = SW = %d\n", r);
+
+ while (i != r) {
+ struct iwl_rx_mem_buffer *rxb;
+
+ if (unlikely(rxq->used_count == RX_QUEUE_SIZE / 2))
+ emergency = true;
+
+ rxb = rxq->queue[i];
+ rxq->queue[i] = NULL;
+
+ IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d\n", r, i);
+ iwl_pcie_rx_handle_rb(trans, rxb, emergency);
+
+ i = (i + 1) & RX_QUEUE_MASK;
+
+ /* If we have RX_CLAIM_REQ_ALLOC released rx buffers -
+ * try to claim the pre-allocated buffers from the allocator */
+ if (rxq->used_count >= RX_CLAIM_REQ_ALLOC) {
+ struct iwl_rb_allocator *rba = &trans_pcie->rba;
+ struct iwl_rx_mem_buffer *out[RX_CLAIM_REQ_ALLOC];
+
+ if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 &&
+ !emergency) {
+ /* Add the remaining 6 empty RBDs
+ * for allocator use
+ */
+ spin_lock(&rba->lock);
+ list_splice_tail_init(&rxq->rx_used,
+ &rba->rbd_empty);
+ spin_unlock(&rba->lock);
+ }
+
+ /* If not ready - continue, will try to reclaim later.
+ * No need to reschedule work - allocator exits only on
+ * success */
+ if (!iwl_pcie_rx_allocator_get(trans, out)) {
+ /* If success - then RX_CLAIM_REQ_ALLOC
+ * buffers were retrieved and should be added
+ * to free list */
+ rxq->used_count -= RX_CLAIM_REQ_ALLOC;
+ for (j = 0; j < RX_CLAIM_REQ_ALLOC; j++) {
+ list_add_tail(&out[j]->list,
+ &rxq->rx_free);
+ rxq->free_count++;
+ }
+ }
+ }
+ if (emergency) {
+ count++;
+ if (count == 8) {
+ count = 0;
+ if (rxq->used_count < RX_QUEUE_SIZE / 3)
+ emergency = false;
+ spin_unlock(&rxq->lock);
+ iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC);
+ spin_lock(&rxq->lock);
+ }
+ }
+ /* handle restock for three cases, can be all of them at once:
+ * - we just pulled buffers from the allocator
+ * - we have 8+ unstolen pages accumulated
+ * - we are in emergency and allocated buffers
+ */
+ if (rxq->free_count >= RX_CLAIM_REQ_ALLOC) {
+ rxq->read = i;
+ spin_unlock(&rxq->lock);
+ iwl_pcie_rxq_restock(trans);
+ goto restart;
+ }
+ }
+
+ /* Backtrack one entry */
+ rxq->read = i;
+ spin_unlock(&rxq->lock);
+
+ /*
+ * handle a case where in emergency there are some unallocated RBDs.
+ * those RBDs are in the used list, but are not tracked by the queue's
+ * used_count which counts allocator owned RBDs.
+ * unallocated emergency RBDs must be allocated on exit, otherwise
+ * when called again the function may not be in emergency mode and
+ * they will be handed to the allocator with no tracking in the RBD
+ * allocator counters, which will lead to them never being claimed back
+ * by the queue.
+ * by allocating them here, they are now in the queue free list, and
+ * will be restocked by the next call of iwl_pcie_rxq_restock.
+ */
+ if (unlikely(emergency && count))
+ iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC);
+
+ if (trans_pcie->napi.poll)
+ napi_gro_flush(&trans_pcie->napi, false);
+}
+
+/*
+ * iwl_pcie_irq_handle_error - called for HW or SW error interrupt from card
+ */
+static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int i;
+
+ /* W/A for WiFi/WiMAX coex and WiMAX own the RF */
+ if (trans->cfg->internal_wimax_coex &&
+ !trans->cfg->apmg_not_supported &&
+ (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) &
+ APMS_CLK_VAL_MRB_FUNC_MODE) ||
+ (iwl_read_prph(trans, APMG_PS_CTRL_REG) &
+ APMG_PS_CTRL_VAL_RESET_REQ))) {
+ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+ iwl_op_mode_wimax_active(trans->op_mode);
+ wake_up(&trans_pcie->wait_command_queue);
+ return;
+ }
+
+ iwl_pcie_dump_csr(trans);
+ iwl_dump_fh(trans, NULL);
+
+ local_bh_disable();
+ /* The STATUS_FW_ERROR bit is set in this function. This must happen
+ * before we wake up the command caller, to ensure a proper cleanup. */
+ iwl_trans_fw_error(trans);
+ local_bh_enable();
+
+ for (i = 0; i < trans->cfg->base_params->num_of_queues; i++)
+ del_timer(&trans_pcie->txq[i].stuck_timer);
+
+ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+ wake_up(&trans_pcie->wait_command_queue);
+}
+
+static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans)
+{
+ u32 inta;
+
+ lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock);
+
+ trace_iwlwifi_dev_irq(trans->dev);
+
+ /* Discover which interrupts are active/pending */
+ inta = iwl_read32(trans, CSR_INT);
+
+ /* the thread will service interrupts and re-enable them */
+ return inta;
+}
+
+/* a device (PCI-E) page is 4096 bytes long */
+#define ICT_SHIFT 12
+#define ICT_SIZE (1 << ICT_SHIFT)
+#define ICT_COUNT (ICT_SIZE / sizeof(u32))
+
+/* interrupt handler using ict table, with this interrupt driver will
+ * stop using INTA register to get device's interrupt, reading this register
+ * is expensive, device will write interrupts in ICT dram table, increment
+ * index then will fire interrupt to driver, driver will OR all ICT table
+ * entries from current index up to table entry with 0 value. the result is
+ * the interrupt we need to service, driver will set the entries back to 0 and
+ * set index.
+ */
+static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 inta;
+ u32 val = 0;
+ u32 read;
+
+ trace_iwlwifi_dev_irq(trans->dev);
+
+ /* Ignore interrupt if there's nothing in NIC to service.
+ * This may be due to IRQ shared with another device,
+ * or due to sporadic interrupts thrown from our NIC. */
+ read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
+ trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read);
+ if (!read)
+ return 0;
+
+ /*
+ * Collect all entries up to the first 0, starting from ict_index;
+ * note we already read at ict_index.
+ */
+ do {
+ val |= read;
+ IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n",
+ trans_pcie->ict_index, read);
+ trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
+ trans_pcie->ict_index =
+ ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1));
+
+ read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
+ trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
+ read);
+ } while (read);
+
+ /* We should not get this value, just ignore it. */
+ if (val == 0xffffffff)
+ val = 0;
+
+ /*
+ * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
+ * (bit 15 before shifting it to 31) to clear when using interrupt
+ * coalescing. fortunately, bits 18 and 19 stay set when this happens
+ * so we use them to decide on the real state of the Rx bit.
+ * In order words, bit 15 is set if bit 18 or bit 19 are set.
+ */
+ if (val & 0xC0000)
+ val |= 0x8000;
+
+ inta = (0xff & val) | ((0xff00 & val) << 16);
+ return inta;
+}
+
+irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
+{
+ struct iwl_trans *trans = dev_id;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
+ u32 inta = 0;
+ u32 handled = 0;
+
+ lock_map_acquire(&trans->sync_cmd_lockdep_map);
+
+ spin_lock(&trans_pcie->irq_lock);
+
+ /* dram interrupt table not set yet,
+ * use legacy interrupt.
+ */
+ if (likely(trans_pcie->use_ict))
+ inta = iwl_pcie_int_cause_ict(trans);
+ else
+ inta = iwl_pcie_int_cause_non_ict(trans);
+
+ if (iwl_have_debug_level(IWL_DL_ISR)) {
+ IWL_DEBUG_ISR(trans,
+ "ISR inta 0x%08x, enabled 0x%08x(sw), enabled(hw) 0x%08x, fh 0x%08x\n",
+ inta, trans_pcie->inta_mask,
+ iwl_read32(trans, CSR_INT_MASK),
+ iwl_read32(trans, CSR_FH_INT_STATUS));
+ if (inta & (~trans_pcie->inta_mask))
+ IWL_DEBUG_ISR(trans,
+ "We got a masked interrupt (0x%08x)\n",
+ inta & (~trans_pcie->inta_mask));
+ }
+
+ inta &= trans_pcie->inta_mask;
+
+ /*
+ * Ignore interrupt if there's nothing in NIC to service.
+ * This may be due to IRQ shared with another device,
+ * or due to sporadic interrupts thrown from our NIC.
+ */
+ if (unlikely(!inta)) {
+ IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
+ /*
+ * Re-enable interrupts here since we don't
+ * have anything to service
+ */
+ if (test_bit(STATUS_INT_ENABLED, &trans->status))
+ iwl_enable_interrupts(trans);
+ spin_unlock(&trans_pcie->irq_lock);
+ lock_map_release(&trans->sync_cmd_lockdep_map);
+ return IRQ_NONE;
+ }
+
+ if (unlikely(inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
+ /*
+ * Hardware disappeared. It might have
+ * already raised an interrupt.
+ */
+ IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
+ spin_unlock(&trans_pcie->irq_lock);
+ goto out;
+ }
+
+ /* Ack/clear/reset pending uCode interrupts.
+ * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
+ */
+ /* There is a hardware bug in the interrupt mask function that some
+ * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if
+ * they are disabled in the CSR_INT_MASK register. Furthermore the
+ * ICT interrupt handling mechanism has another bug that might cause
+ * these unmasked interrupts fail to be detected. We workaround the
+ * hardware bugs here by ACKing all the possible interrupts so that
+ * interrupt coalescing can still be achieved.
+ */
+ iwl_write32(trans, CSR_INT, inta | ~trans_pcie->inta_mask);
+
+ if (iwl_have_debug_level(IWL_DL_ISR))
+ IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
+ inta, iwl_read32(trans, CSR_INT_MASK));
+
+ spin_unlock(&trans_pcie->irq_lock);
+
+ /* Now service all interrupt bits discovered above. */
+ if (inta & CSR_INT_BIT_HW_ERR) {
+ IWL_ERR(trans, "Hardware error detected. Restarting.\n");
+
+ /* Tell the device to stop sending interrupts */
+ iwl_disable_interrupts(trans);
+
+ isr_stats->hw++;
+ iwl_pcie_irq_handle_error(trans);
+
+ handled |= CSR_INT_BIT_HW_ERR;
+
+ goto out;
+ }
+
+ 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");
+ isr_stats->sch++;
+ }
+
+ /* Alive notification via Rx interrupt will do the real work */
+ if (inta & CSR_INT_BIT_ALIVE) {
+ IWL_DEBUG_ISR(trans, "Alive interrupt\n");
+ isr_stats->alive++;
+ }
+ }
+
+ /* Safely ignore these bits for debug checks below */
+ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
+
+ /* HW RF KILL switch toggled */
+ if (inta & CSR_INT_BIT_RF_KILL) {
+ bool hw_rfkill;
+
+ hw_rfkill = iwl_is_rfkill_set(trans);
+ IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
+ hw_rfkill ? "disable radio" : "enable radio");
+
+ isr_stats->rfkill++;
+
+ mutex_lock(&trans_pcie->mutex);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+ mutex_unlock(&trans_pcie->mutex);
+ if (hw_rfkill) {
+ set_bit(STATUS_RFKILL, &trans->status);
+ if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
+ &trans->status))
+ IWL_DEBUG_RF_KILL(trans,
+ "Rfkill while SYNC HCMD in flight\n");
+ wake_up(&trans_pcie->wait_command_queue);
+ } else {
+ clear_bit(STATUS_RFKILL, &trans->status);
+ }
+
+ handled |= CSR_INT_BIT_RF_KILL;
+ }
+
+ /* Chip got too hot and stopped itself */
+ if (inta & CSR_INT_BIT_CT_KILL) {
+ IWL_ERR(trans, "Microcode CT kill error detected.\n");
+ isr_stats->ctkill++;
+ handled |= CSR_INT_BIT_CT_KILL;
+ }
+
+ /* Error detected by uCode */
+ if (inta & CSR_INT_BIT_SW_ERR) {
+ IWL_ERR(trans, "Microcode SW error detected. "
+ " Restarting 0x%X.\n", inta);
+ isr_stats->sw++;
+ iwl_pcie_irq_handle_error(trans);
+ handled |= CSR_INT_BIT_SW_ERR;
+ }
+
+ /* uCode wakes up after power-down sleep */
+ if (inta & CSR_INT_BIT_WAKEUP) {
+ IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
+ iwl_pcie_rxq_check_wrptr(trans);
+ iwl_pcie_txq_check_wrptrs(trans);
+
+ isr_stats->wakeup++;
+
+ handled |= CSR_INT_BIT_WAKEUP;
+ }
+
+ /* All uCode command responses, including Tx command responses,
+ * Rx "responses" (frame-received notification), and other
+ * notifications from uCode come through here*/
+ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX |
+ CSR_INT_BIT_RX_PERIODIC)) {
+ IWL_DEBUG_ISR(trans, "Rx interrupt\n");
+ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
+ handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
+ iwl_write32(trans, CSR_FH_INT_STATUS,
+ CSR_FH_INT_RX_MASK);
+ }
+ if (inta & CSR_INT_BIT_RX_PERIODIC) {
+ handled |= CSR_INT_BIT_RX_PERIODIC;
+ iwl_write32(trans,
+ CSR_INT, CSR_INT_BIT_RX_PERIODIC);
+ }
+ /* Sending RX interrupt require many steps to be done in the
+ * the device:
+ * 1- write interrupt to current index in ICT table.
+ * 2- dma RX frame.
+ * 3- update RX shared data to indicate last write index.
+ * 4- send interrupt.
+ * This could lead to RX race, driver could receive RX interrupt
+ * but the shared data changes does not reflect this;
+ * periodic interrupt will detect any dangling Rx activity.
+ */
+
+ /* Disable periodic interrupt; we use it as just a one-shot. */
+ iwl_write8(trans, CSR_INT_PERIODIC_REG,
+ CSR_INT_PERIODIC_DIS);
+
+ /*
+ * Enable periodic interrupt in 8 msec only if we received
+ * real RX interrupt (instead of just periodic int), to catch
+ * any dangling Rx interrupt. If it was just the periodic
+ * interrupt, there was no dangling Rx activity, and no need
+ * to extend the periodic interrupt; one-shot is enough.
+ */
+ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
+ iwl_write8(trans, CSR_INT_PERIODIC_REG,
+ CSR_INT_PERIODIC_ENA);
+
+ isr_stats->rx++;
+
+ local_bh_disable();
+ iwl_pcie_rx_handle(trans);
+ local_bh_enable();
+ }
+
+ /* This "Tx" DMA channel is used only for loading uCode */
+ if (inta & CSR_INT_BIT_FH_TX) {
+ iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK);
+ IWL_DEBUG_ISR(trans, "uCode load interrupt\n");
+ isr_stats->tx++;
+ handled |= CSR_INT_BIT_FH_TX;
+ /* Wake up uCode load routine, now that load is complete */
+ trans_pcie->ucode_write_complete = true;
+ wake_up(&trans_pcie->ucode_write_waitq);
+ }
+
+ if (inta & ~handled) {
+ IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled);
+ isr_stats->unhandled++;
+ }
+
+ if (inta & ~(trans_pcie->inta_mask)) {
+ IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n",
+ inta & ~trans_pcie->inta_mask);
+ }
+
+ /* Re-enable all interrupts */
+ /* only Re-enable if disabled by irq */
+ if (test_bit(STATUS_INT_ENABLED, &trans->status))
+ iwl_enable_interrupts(trans);
+ /* Re-enable RF_KILL if it occurred */
+ else if (handled & CSR_INT_BIT_RF_KILL)
+ iwl_enable_rfkill_int(trans);
+
+out:
+ lock_map_release(&trans->sync_cmd_lockdep_map);
+ return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ *
+ * ICT functions
+ *
+ ******************************************************************************/
+
+/* Free dram table */
+void iwl_pcie_free_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (trans_pcie->ict_tbl) {
+ dma_free_coherent(trans->dev, ICT_SIZE,
+ trans_pcie->ict_tbl,
+ trans_pcie->ict_tbl_dma);
+ trans_pcie->ict_tbl = NULL;
+ trans_pcie->ict_tbl_dma = 0;
+ }
+}
+
+/*
+ * allocate dram shared table, it is an aligned memory
+ * block of ICT_SIZE.
+ * also reset all data related to ICT table interrupt.
+ */
+int iwl_pcie_alloc_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ trans_pcie->ict_tbl =
+ dma_zalloc_coherent(trans->dev, ICT_SIZE,
+ &trans_pcie->ict_tbl_dma,
+ GFP_KERNEL);
+ if (!trans_pcie->ict_tbl)
+ return -ENOMEM;
+
+ /* just an API sanity check ... it is guaranteed to be aligned */
+ if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) {
+ iwl_pcie_free_ict(trans);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Device is going up inform it about using ICT interrupt table,
+ * also we need to tell the driver to start using ICT interrupt.
+ */
+void iwl_pcie_reset_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 val;
+
+ if (!trans_pcie->ict_tbl)
+ return;
+
+ spin_lock(&trans_pcie->irq_lock);
+ iwl_disable_interrupts(trans);
+
+ memset(trans_pcie->ict_tbl, 0, ICT_SIZE);
+
+ val = trans_pcie->ict_tbl_dma >> ICT_SHIFT;
+
+ val |= CSR_DRAM_INT_TBL_ENABLE |
+ CSR_DRAM_INIT_TBL_WRAP_CHECK |
+ CSR_DRAM_INIT_TBL_WRITE_POINTER;
+
+ IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val);
+
+ iwl_write32(trans, CSR_DRAM_INT_TBL_REG, val);
+ trans_pcie->use_ict = true;
+ trans_pcie->ict_index = 0;
+ iwl_write32(trans, CSR_INT, trans_pcie->inta_mask);
+ iwl_enable_interrupts(trans);
+ spin_unlock(&trans_pcie->irq_lock);
+}
+
+/* Device is going down disable ict interrupt usage */
+void iwl_pcie_disable_ict(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ spin_lock(&trans_pcie->irq_lock);
+ trans_pcie->use_ict = false;
+ spin_unlock(&trans_pcie->irq_lock);
+}
+
+irqreturn_t iwl_pcie_isr(int irq, void *data)
+{
+ struct iwl_trans *trans = data;
+
+ if (!trans)
+ return IRQ_NONE;
+
+ /* Disable (but don't clear!) interrupts here to avoid
+ * back-to-back ISRs and sporadic interrupts from our NIC.
+ * If we have something to service, the tasklet will re-enable ints.
+ * If we *don't* have something, we'll re-enable before leaving here.
+ */
+ iwl_write32(trans, CSR_INT_MASK, 0x00000000);
+
+ return IRQ_WAKE_THREAD;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
new file mode 100644
index 000000000000..d60a467a983c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -0,0 +1,2717 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/pci.h>
+#include <linux/pci-aspm.h>
+#include <linux/interrupt.h>
+#include <linux/debugfs.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <linux/gfp.h>
+#include <linux/vmalloc.h>
+
+#include "iwl-drv.h"
+#include "iwl-trans.h"
+#include "iwl-csr.h"
+#include "iwl-prph.h"
+#include "iwl-scd.h"
+#include "iwl-agn-hw.h"
+#include "iwl-fw-error-dump.h"
+#include "internal.h"
+#include "iwl-fh.h"
+
+/* extended range in FW SRAM */
+#define IWL_FW_MEM_EXTENDED_START 0x40000
+#define IWL_FW_MEM_EXTENDED_END 0x57FFF
+
+static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (!trans_pcie->fw_mon_page)
+ return;
+
+ dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys,
+ trans_pcie->fw_mon_size, DMA_FROM_DEVICE);
+ __free_pages(trans_pcie->fw_mon_page,
+ get_order(trans_pcie->fw_mon_size));
+ trans_pcie->fw_mon_page = NULL;
+ trans_pcie->fw_mon_phys = 0;
+ trans_pcie->fw_mon_size = 0;
+}
+
+static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct page *page = NULL;
+ dma_addr_t phys;
+ u32 size = 0;
+ u8 power;
+
+ if (!max_power) {
+ /* default max_power is maximum */
+ max_power = 26;
+ } else {
+ max_power += 11;
+ }
+
+ if (WARN(max_power > 26,
+ "External buffer size for monitor is too big %d, check the FW TLV\n",
+ max_power))
+ return;
+
+ if (trans_pcie->fw_mon_page) {
+ dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys,
+ trans_pcie->fw_mon_size,
+ DMA_FROM_DEVICE);
+ return;
+ }
+
+ phys = 0;
+ for (power = max_power; power >= 11; power--) {
+ int order;
+
+ size = BIT(power);
+ order = get_order(size);
+ page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO,
+ order);
+ if (!page)
+ continue;
+
+ phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(trans->dev, phys)) {
+ __free_pages(page, order);
+ page = NULL;
+ continue;
+ }
+ IWL_INFO(trans,
+ "Allocated 0x%08x bytes (order %d) for firmware monitor.\n",
+ size, order);
+ break;
+ }
+
+ if (WARN_ON_ONCE(!page))
+ return;
+
+ if (power != max_power)
+ IWL_ERR(trans,
+ "Sorry - debug buffer is only %luK while you requested %luK\n",
+ (unsigned long)BIT(power - 10),
+ (unsigned long)BIT(max_power - 10));
+
+ trans_pcie->fw_mon_page = page;
+ trans_pcie->fw_mon_phys = phys;
+ trans_pcie->fw_mon_size = size;
+}
+
+static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
+{
+ iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
+ ((reg & 0x0000ffff) | (2 << 28)));
+ return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG);
+}
+
+static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val)
+{
+ iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val);
+ iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
+ ((reg & 0x0000ffff) | (3 << 28)));
+}
+
+static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
+{
+ if (trans->cfg->apmg_not_supported)
+ return;
+
+ if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
+ iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
+ ~APMG_PS_CTRL_MSK_PWR_SRC);
+ else
+ iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
+ ~APMG_PS_CTRL_MSK_PWR_SRC);
+}
+
+/* PCI registers */
+#define PCI_CFG_RETRY_TIMEOUT 0x041
+
+static void iwl_pcie_apm_config(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u16 lctl;
+ u16 cap;
+
+ /*
+ * HW bug W/A for instability in PCIe bus L0S->L1 transition.
+ * Check if BIOS (or OS) enabled L1-ASPM on this device.
+ * If so (likely), disable L0S, so device moves directly L0->L1;
+ * costs negligible amount of power savings.
+ * If not (unlikely), enable L0S, so there is at least some
+ * power savings, even without L1.
+ */
+ pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl);
+ if (lctl & PCI_EXP_LNKCTL_ASPM_L1)
+ iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
+ else
+ iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
+ trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
+
+ pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap);
+ trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
+ dev_info(trans->dev, "L1 %sabled - LTR %sabled\n",
+ (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
+ trans->ltr_enabled ? "En" : "Dis");
+}
+
+/*
+ * Start up NIC's basic functionality after it has been reset
+ * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop())
+ * NOTE: This does not load uCode nor start the embedded processor
+ */
+static int iwl_pcie_apm_init(struct iwl_trans *trans)
+{
+ int ret = 0;
+ IWL_DEBUG_INFO(trans, "Init card's basic functions\n");
+
+ /*
+ * Use "set_bit" below rather than "write", to preserve any hardware
+ * bits already set by default after reset.
+ */
+
+ /* Disable L0S exit timer (platform NMI Work/Around) */
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+
+ /*
+ * Disable L0s without affecting L1;
+ * don't wait for ICH L0s (ICH bug W/A)
+ */
+ iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
+
+ /* Set FH wait threshold to maximum (HW error during stress W/A) */
+ iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
+
+ /*
+ * Enable HAP INTA (interrupt from management bus) to
+ * wake device's PCI Express link L1a -> L0s
+ */
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
+
+ iwl_pcie_apm_config(trans);
+
+ /* Configure analog phase-lock-loop before activating to D0A */
+ if (trans->cfg->base_params->pll_cfg_val)
+ iwl_set_bit(trans, CSR_ANA_PLL_CFG,
+ trans->cfg->base_params->pll_cfg_val);
+
+ /*
+ * Set "initialization complete" bit to move adapter from
+ * D0U* --> D0A* (powered-up active) state.
+ */
+ iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /*
+ * Wait for clock stabilization; once stabilized, access to
+ * device-internal resources is supported, e.g. iwl_write_prph()
+ * and accesses to uCode SRAM.
+ */
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ if (ret < 0) {
+ IWL_DEBUG_INFO(trans, "Failed to init the card\n");
+ goto out;
+ }
+
+ if (trans->cfg->host_interrupt_operation_mode) {
+ /*
+ * This is a bit of an abuse - This is needed for 7260 / 3160
+ * only check host_interrupt_operation_mode even if this is
+ * not related to host_interrupt_operation_mode.
+ *
+ * Enable the oscillator to count wake up time for L1 exit. This
+ * consumes slightly more power (100uA) - but allows to be sure
+ * that we wake up from L1 on time.
+ *
+ * This looks weird: read twice the same register, discard the
+ * value, set a bit, and yet again, read that same register
+ * just to discard the value. But that's the way the hardware
+ * seems to like it.
+ */
+ iwl_read_prph(trans, OSC_CLK);
+ iwl_read_prph(trans, OSC_CLK);
+ iwl_set_bits_prph(trans, OSC_CLK, OSC_CLK_FORCE_CONTROL);
+ iwl_read_prph(trans, OSC_CLK);
+ iwl_read_prph(trans, OSC_CLK);
+ }
+
+ /*
+ * Enable DMA clock and wait for it to stabilize.
+ *
+ * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0"
+ * bits do not disable clocks. This preserves any hardware
+ * bits already set by default in "CLK_CTRL_REG" after reset.
+ */
+ if (!trans->cfg->apmg_not_supported) {
+ iwl_write_prph(trans, APMG_CLK_EN_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(20);
+
+ /* Disable L1-Active */
+ iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+
+ /* Clear the interrupt in APMG if the NIC is in RFKILL */
+ iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
+ APMG_RTC_INT_STT_RFKILL);
+ }
+
+ set_bit(STATUS_DEVICE_ENABLED, &trans->status);
+
+out:
+ return ret;
+}
+
+/*
+ * Enable LP XTAL to avoid HW bug where device may consume much power if
+ * FW is not loaded after device reset. LP XTAL is disabled by default
+ * after device HW reset. Do it only if XTAL is fed by internal source.
+ * Configure device's "persistence" mode to avoid resetting XTAL again when
+ * SHRD_HW_RST occurs in S3.
+ */
+static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
+{
+ int ret;
+ u32 apmg_gp1_reg;
+ u32 apmg_xtal_cfg_reg;
+ u32 dl_cfg_reg;
+
+ /* Force XTAL ON */
+ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
+
+ /* Reset entire device - do controller reset (results in SHRD_HW_RST) */
+ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ udelay(10);
+
+ /*
+ * Set "initialization complete" bit to move adapter from
+ * D0U* --> D0A* (powered-up active) state.
+ */
+ iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /*
+ * Wait for clock stabilization; once stabilized, access to
+ * device-internal resources is possible.
+ */
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ 25000);
+ if (WARN_ON(ret < 0)) {
+ IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n");
+ /* Release XTAL ON request */
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
+ return;
+ }
+
+ /*
+ * Clear "disable persistence" to avoid LP XTAL resetting when
+ * SHRD_HW_RST is applied in S3.
+ */
+ iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_PERSIST_DIS);
+
+ /*
+ * Force APMG XTAL to be active to prevent its disabling by HW
+ * caused by APMG idle state.
+ */
+ apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans,
+ SHR_APMG_XTAL_CFG_REG);
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG,
+ apmg_xtal_cfg_reg |
+ SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
+
+ /*
+ * Reset entire device again - do controller reset (results in
+ * SHRD_HW_RST). Turn MAC off before proceeding.
+ */
+ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ udelay(10);
+
+ /* Enable LP XTAL by indirect access through CSR */
+ apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG);
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg |
+ SHR_APMG_GP1_WF_XTAL_LP_EN |
+ SHR_APMG_GP1_CHICKEN_BIT_SELECT);
+
+ /* Clear delay line clock power up */
+ dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG);
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg &
+ ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP);
+
+ /*
+ * Enable persistence mode to avoid LP XTAL resetting when
+ * SHRD_HW_RST is applied in S3.
+ */
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
+
+ /*
+ * Clear "initialization complete" bit to move adapter from
+ * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
+ */
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /* Activates XTAL resources monitor */
+ __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG,
+ CSR_MONITOR_XTAL_RESOURCES);
+
+ /* Release XTAL ON request */
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
+ udelay(10);
+
+ /* Release APMG XTAL */
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG,
+ apmg_xtal_cfg_reg &
+ ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
+}
+
+static int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
+{
+ int ret = 0;
+
+ /* stop device's busmaster DMA activity */
+ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
+
+ ret = iwl_poll_bit(trans, CSR_RESET,
+ CSR_RESET_REG_FLAG_MASTER_DISABLED,
+ CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
+ if (ret < 0)
+ IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n");
+
+ IWL_DEBUG_INFO(trans, "stop master\n");
+
+ return ret;
+}
+
+static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
+{
+ IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
+
+ if (op_mode_leave) {
+ if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
+ iwl_pcie_apm_init(trans);
+
+ /* inform ME that we are leaving */
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+ iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_WAKE_ME);
+ else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+ iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
+ CSR_RESET_LINK_PWR_MGMT_DISABLED);
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_PREPARE |
+ CSR_HW_IF_CONFIG_REG_ENABLE_PME);
+ mdelay(1);
+ iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
+ CSR_RESET_LINK_PWR_MGMT_DISABLED);
+ }
+ mdelay(5);
+ }
+
+ clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
+
+ /* Stop device's DMA activity */
+ iwl_pcie_apm_stop_master(trans);
+
+ if (trans->cfg->lp_xtal_workaround) {
+ iwl_pcie_apm_lp_xtal_enable(trans);
+ return;
+ }
+
+ /* Reset the entire device */
+ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ udelay(10);
+
+ /*
+ * Clear "initialization complete" bit to move adapter from
+ * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
+ */
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+}
+
+static int iwl_pcie_nic_init(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ /* nic_init */
+ spin_lock(&trans_pcie->irq_lock);
+ iwl_pcie_apm_init(trans);
+
+ spin_unlock(&trans_pcie->irq_lock);
+
+ iwl_pcie_set_pwr(trans, false);
+
+ iwl_op_mode_nic_config(trans->op_mode);
+
+ /* Allocate the RX queue, or reset if it is already allocated */
+ iwl_pcie_rx_init(trans);
+
+ /* Allocate or reset and init all Tx and Command queues */
+ if (iwl_pcie_tx_init(trans))
+ return -ENOMEM;
+
+ if (trans->cfg->base_params->shadow_reg_enable) {
+ /* enable shadow regs in HW */
+ iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF);
+ IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n");
+ }
+
+ return 0;
+}
+
+#define HW_READY_TIMEOUT (50)
+
+/* Note: returns poll_bit return value, which is >= 0 if success */
+static int iwl_pcie_set_hw_ready(struct iwl_trans *trans)
+{
+ int ret;
+
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
+
+ /* See if we got it */
+ ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+ CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+ HW_READY_TIMEOUT);
+
+ if (ret >= 0)
+ iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE);
+
+ IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : "");
+ return ret;
+}
+
+/* Note: returns standard 0/-ERROR code */
+static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
+{
+ int ret;
+ int t = 0;
+ int iter;
+
+ IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n");
+
+ ret = iwl_pcie_set_hw_ready(trans);
+ /* If the card is ready, exit 0 */
+ if (ret >= 0)
+ return 0;
+
+ iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
+ CSR_RESET_LINK_PWR_MGMT_DISABLED);
+ msleep(1);
+
+ for (iter = 0; iter < 10; iter++) {
+ /* If HW is not ready, prepare the conditions to check again */
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_PREPARE);
+
+ do {
+ ret = iwl_pcie_set_hw_ready(trans);
+ if (ret >= 0)
+ return 0;
+
+ usleep_range(200, 1000);
+ t += 200;
+ } while (t < 150000);
+ msleep(25);
+ }
+
+ IWL_ERR(trans, "Couldn't prepare the card\n");
+
+ return ret;
+}
+
+/*
+ * ucode
+ */
+static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr,
+ dma_addr_t phy_addr, u32 byte_cnt)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int ret;
+
+ trans_pcie->ucode_write_complete = false;
+
+ iwl_write_direct32(trans,
+ FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
+
+ iwl_write_direct32(trans,
+ FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL),
+ dst_addr);
+
+ iwl_write_direct32(trans,
+ FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
+ phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
+
+ iwl_write_direct32(trans,
+ FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
+ (iwl_get_dma_hi_addr(phy_addr)
+ << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
+
+ iwl_write_direct32(trans,
+ FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL),
+ 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
+ 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
+ FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
+
+ iwl_write_direct32(trans,
+ FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
+
+ ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
+ trans_pcie->ucode_write_complete, 5 * HZ);
+ if (!ret) {
+ IWL_ERR(trans, "Failed to load firmware chunk!\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
+ const struct fw_desc *section)
+{
+ u8 *v_addr;
+ dma_addr_t p_addr;
+ u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len);
+ int ret = 0;
+
+ IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
+ section_num);
+
+ v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!v_addr) {
+ IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n");
+ chunk_sz = PAGE_SIZE;
+ v_addr = dma_alloc_coherent(trans->dev, chunk_sz,
+ &p_addr, GFP_KERNEL);
+ if (!v_addr)
+ return -ENOMEM;
+ }
+
+ for (offset = 0; offset < section->len; offset += chunk_sz) {
+ u32 copy_size, dst_addr;
+ bool extended_addr = false;
+
+ copy_size = min_t(u32, chunk_sz, section->len - offset);
+ dst_addr = section->offset + offset;
+
+ if (dst_addr >= IWL_FW_MEM_EXTENDED_START &&
+ dst_addr <= IWL_FW_MEM_EXTENDED_END)
+ extended_addr = true;
+
+ if (extended_addr)
+ iwl_set_bits_prph(trans, LMPM_CHICK,
+ LMPM_CHICK_EXTENDED_ADDR_SPACE);
+
+ memcpy(v_addr, (u8 *)section->data + offset, copy_size);
+ ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr,
+ copy_size);
+
+ if (extended_addr)
+ iwl_clear_bits_prph(trans, LMPM_CHICK,
+ LMPM_CHICK_EXTENDED_ADDR_SPACE);
+
+ if (ret) {
+ IWL_ERR(trans,
+ "Could not load the [%d] uCode section\n",
+ section_num);
+ break;
+ }
+ }
+
+ dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr);
+ return ret;
+}
+
+/*
+ * Driver Takes the ownership on secure machine before FW load
+ * and prevent race with the BT load.
+ * W/A for ROM bug. (should be remove in the next Si step)
+ */
+static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans)
+{
+ u32 val, loop = 1000;
+
+ /*
+ * Check the RSA semaphore is accessible.
+ * If the HW isn't locked and the rsa semaphore isn't accessible,
+ * we are in trouble.
+ */
+ val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0);
+ if (val & (BIT(1) | BIT(17))) {
+ IWL_INFO(trans,
+ "can't access the RSA semaphore it is write protected\n");
+ return 0;
+ }
+
+ /* take ownership on the AUX IF */
+ iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK);
+ iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK);
+
+ do {
+ iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1);
+ val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS);
+ if (val == 0x1) {
+ iwl_write_prph(trans, RSA_ENABLE, 0);
+ return 0;
+ }
+
+ udelay(10);
+ loop--;
+ } while (loop > 0);
+
+ IWL_ERR(trans, "Failed to take ownership on secure machine\n");
+ return -EIO;
+}
+
+static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans,
+ const struct fw_img *image,
+ int cpu,
+ int *first_ucode_section)
+{
+ int shift_param;
+ int i, ret = 0, sec_num = 0x1;
+ u32 val, last_read_idx = 0;
+
+ if (cpu == 1) {
+ shift_param = 0;
+ *first_ucode_section = 0;
+ } else {
+ shift_param = 16;
+ (*first_ucode_section)++;
+ }
+
+ for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
+ last_read_idx = i;
+
+ /*
+ * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
+ * CPU1 to CPU2.
+ * PAGING_SEPARATOR_SECTION delimiter - separate between
+ * CPU2 non paged to CPU2 paging sec.
+ */
+ if (!image->sec[i].data ||
+ image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION ||
+ image->sec[i].offset == PAGING_SEPARATOR_SECTION) {
+ IWL_DEBUG_FW(trans,
+ "Break since Data not valid or Empty section, sec = %d\n",
+ i);
+ break;
+ }
+
+ ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+ if (ret)
+ return ret;
+
+ /* Notify the ucode of the loaded section number and status */
+ val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS);
+ val = val | (sec_num << shift_param);
+ iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val);
+ sec_num = (sec_num << 1) | 0x1;
+ }
+
+ *first_ucode_section = last_read_idx;
+
+ if (cpu == 1)
+ iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFF);
+ else
+ iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
+
+ return 0;
+}
+
+static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
+ const struct fw_img *image,
+ int cpu,
+ int *first_ucode_section)
+{
+ int shift_param;
+ int i, ret = 0;
+ u32 last_read_idx = 0;
+
+ if (cpu == 1) {
+ shift_param = 0;
+ *first_ucode_section = 0;
+ } else {
+ shift_param = 16;
+ (*first_ucode_section)++;
+ }
+
+ for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
+ last_read_idx = i;
+
+ /*
+ * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
+ * CPU1 to CPU2.
+ * PAGING_SEPARATOR_SECTION delimiter - separate between
+ * CPU2 non paged to CPU2 paging sec.
+ */
+ if (!image->sec[i].data ||
+ image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION ||
+ image->sec[i].offset == PAGING_SEPARATOR_SECTION) {
+ IWL_DEBUG_FW(trans,
+ "Break since Data not valid or Empty section, sec = %d\n",
+ i);
+ break;
+ }
+
+ ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+ if (ret)
+ return ret;
+ }
+
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ iwl_set_bits_prph(trans,
+ CSR_UCODE_LOAD_STATUS_ADDR,
+ (LMPM_CPU_UCODE_LOADING_COMPLETED |
+ LMPM_CPU_HDRS_LOADING_COMPLETED |
+ LMPM_CPU_UCODE_LOADING_STARTED) <<
+ shift_param);
+
+ *first_ucode_section = last_read_idx;
+
+ return 0;
+}
+
+static void iwl_pcie_apply_destination(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+ int i;
+
+ if (dest->version)
+ IWL_ERR(trans,
+ "DBG DEST version is %d - expect issues\n",
+ dest->version);
+
+ IWL_INFO(trans, "Applying debug destination %s\n",
+ get_fw_dbg_mode_string(dest->monitor_mode));
+
+ if (dest->monitor_mode == EXTERNAL_MODE)
+ iwl_pcie_alloc_fw_monitor(trans, dest->size_power);
+ else
+ IWL_WARN(trans, "PCI should have external buffer debug\n");
+
+ for (i = 0; i < trans->dbg_dest_reg_num; i++) {
+ u32 addr = le32_to_cpu(dest->reg_ops[i].addr);
+ u32 val = le32_to_cpu(dest->reg_ops[i].val);
+
+ switch (dest->reg_ops[i].op) {
+ case CSR_ASSIGN:
+ iwl_write32(trans, addr, val);
+ break;
+ case CSR_SETBIT:
+ iwl_set_bit(trans, addr, BIT(val));
+ break;
+ case CSR_CLEARBIT:
+ iwl_clear_bit(trans, addr, BIT(val));
+ break;
+ case PRPH_ASSIGN:
+ iwl_write_prph(trans, addr, val);
+ break;
+ case PRPH_SETBIT:
+ iwl_set_bits_prph(trans, addr, BIT(val));
+ break;
+ case PRPH_CLEARBIT:
+ iwl_clear_bits_prph(trans, addr, BIT(val));
+ break;
+ case PRPH_BLOCKBIT:
+ if (iwl_read_prph(trans, addr) & BIT(val)) {
+ IWL_ERR(trans,
+ "BIT(%u) in address 0x%x is 1, stopping FW configuration\n",
+ val, addr);
+ goto monitor;
+ }
+ break;
+ default:
+ IWL_ERR(trans, "FW debug - unknown OP %d\n",
+ dest->reg_ops[i].op);
+ break;
+ }
+ }
+
+monitor:
+ if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
+ iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
+ trans_pcie->fw_mon_phys >> dest->base_shift);
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+ (trans_pcie->fw_mon_phys +
+ trans_pcie->fw_mon_size - 256) >>
+ dest->end_shift);
+ else
+ iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+ (trans_pcie->fw_mon_phys +
+ trans_pcie->fw_mon_size) >>
+ dest->end_shift);
+ }
+}
+
+static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
+ const struct fw_img *image)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int ret = 0;
+ int first_ucode_section;
+
+ IWL_DEBUG_FW(trans, "working with %s CPU\n",
+ image->is_dual_cpus ? "Dual" : "Single");
+
+ /* load to FW the binary non secured sections of CPU1 */
+ ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section);
+ if (ret)
+ return ret;
+
+ if (image->is_dual_cpus) {
+ /* set CPU2 header address */
+ iwl_write_prph(trans,
+ LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR,
+ LMPM_SECURE_CPU2_HDR_MEM_SPACE);
+
+ /* load to FW the binary sections of CPU2 */
+ ret = iwl_pcie_load_cpu_sections(trans, image, 2,
+ &first_ucode_section);
+ if (ret)
+ return ret;
+ }
+
+ /* supported for 7000 only for the moment */
+ if (iwlwifi_mod_params.fw_monitor &&
+ trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ iwl_pcie_alloc_fw_monitor(trans, 0);
+
+ if (trans_pcie->fw_mon_size) {
+ iwl_write_prph(trans, MON_BUFF_BASE_ADDR,
+ trans_pcie->fw_mon_phys >> 4);
+ iwl_write_prph(trans, MON_BUFF_END_ADDR,
+ (trans_pcie->fw_mon_phys +
+ trans_pcie->fw_mon_size) >> 4);
+ }
+ } else if (trans->dbg_dest_tlv) {
+ iwl_pcie_apply_destination(trans);
+ }
+
+ /* release CPU reset */
+ iwl_write32(trans, CSR_RESET, 0);
+
+ return 0;
+}
+
+static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
+ const struct fw_img *image)
+{
+ int ret = 0;
+ int first_ucode_section;
+
+ IWL_DEBUG_FW(trans, "working with %s CPU\n",
+ image->is_dual_cpus ? "Dual" : "Single");
+
+ if (trans->dbg_dest_tlv)
+ iwl_pcie_apply_destination(trans);
+
+ /* TODO: remove in the next Si step */
+ ret = iwl_pcie_rsa_race_bug_wa(trans);
+ if (ret)
+ return ret;
+
+ /* configure the ucode to be ready to get the secured image */
+ /* release CPU reset */
+ iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
+
+ /* load to FW the binary Secured sections of CPU1 */
+ ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1,
+ &first_ucode_section);
+ if (ret)
+ return ret;
+
+ /* load to FW the binary sections of CPU2 */
+ return iwl_pcie_load_cpu_sections_8000(trans, image, 2,
+ &first_ucode_section);
+}
+
+static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
+ const struct fw_img *fw, bool run_in_rfkill)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ bool hw_rfkill;
+ int ret;
+
+ mutex_lock(&trans_pcie->mutex);
+
+ /* Someone called stop_device, don't try to start_fw */
+ if (trans_pcie->is_down) {
+ IWL_WARN(trans,
+ "Can't start_fw since the HW hasn't been started\n");
+ ret = EIO;
+ goto out;
+ }
+
+ /* This may fail if AMT took ownership of the device */
+ if (iwl_pcie_prepare_card_hw(trans)) {
+ IWL_WARN(trans, "Exit HW not ready\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ iwl_enable_rfkill_int(trans);
+
+ /* If platform's RF_KILL switch is NOT set to KILL */
+ hw_rfkill = iwl_is_rfkill_set(trans);
+ if (hw_rfkill)
+ set_bit(STATUS_RFKILL, &trans->status);
+ else
+ clear_bit(STATUS_RFKILL, &trans->status);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+ if (hw_rfkill && !run_in_rfkill) {
+ ret = -ERFKILL;
+ goto out;
+ }
+
+ iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
+
+ ret = iwl_pcie_nic_init(trans);
+ if (ret) {
+ IWL_ERR(trans, "Unable to init nic\n");
+ goto out;
+ }
+
+ /* make sure rfkill handshake bits are cleared */
+ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+ /* clear (again), then enable host interrupts */
+ iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
+ iwl_enable_interrupts(trans);
+
+ /* really make sure rfkill handshake bits are cleared */
+ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+
+ /* Load the given image to the HW */
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ ret = iwl_pcie_load_given_ucode_8000(trans, fw);
+ else
+ ret = iwl_pcie_load_given_ucode(trans, fw);
+
+out:
+ mutex_unlock(&trans_pcie->mutex);
+ return ret;
+}
+
+static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
+{
+ iwl_pcie_reset_ict(trans);
+ iwl_pcie_tx_start(trans, scd_addr);
+}
+
+static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ bool hw_rfkill, was_hw_rfkill;
+
+ lockdep_assert_held(&trans_pcie->mutex);
+
+ if (trans_pcie->is_down)
+ return;
+
+ trans_pcie->is_down = true;
+
+ was_hw_rfkill = iwl_is_rfkill_set(trans);
+
+ /* tell the device to stop sending interrupts */
+ spin_lock(&trans_pcie->irq_lock);
+ iwl_disable_interrupts(trans);
+ spin_unlock(&trans_pcie->irq_lock);
+
+ /* device going down, Stop using ICT table */
+ iwl_pcie_disable_ict(trans);
+
+ /*
+ * If a HW restart happens during firmware loading,
+ * then the firmware loading might call this function
+ * and later it might be called again due to the
+ * restart. So don't process again if the device is
+ * already dead.
+ */
+ if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+ IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
+ iwl_pcie_tx_stop(trans);
+ iwl_pcie_rx_stop(trans);
+
+ /* Power-down device's busmaster DMA clocks */
+ if (!trans->cfg->apmg_not_supported) {
+ iwl_write_prph(trans, APMG_CLK_DIS_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(5);
+ }
+ }
+
+ /* Make sure (redundant) we've released our request to stay awake */
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ /* Stop the device, and put it in low power state */
+ iwl_pcie_apm_stop(trans, false);
+
+ /* stop and reset the on-board processor */
+ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+ udelay(20);
+
+ /*
+ * Upon stop, the APM issues an interrupt if HW RF kill is set.
+ * This is a bug in certain verions of the hardware.
+ * Certain devices also keep sending HW RF kill interrupt all
+ * the time, unless the interrupt is ACKed even if the interrupt
+ * should be masked. Re-ACK all the interrupts here.
+ */
+ spin_lock(&trans_pcie->irq_lock);
+ iwl_disable_interrupts(trans);
+ spin_unlock(&trans_pcie->irq_lock);
+
+
+ /* clear all status bits */
+ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+ clear_bit(STATUS_INT_ENABLED, &trans->status);
+ clear_bit(STATUS_TPOWER_PMI, &trans->status);
+ clear_bit(STATUS_RFKILL, &trans->status);
+
+ /*
+ * Even if we stop the HW, we still want the RF kill
+ * interrupt
+ */
+ iwl_enable_rfkill_int(trans);
+
+ /*
+ * Check again since the RF kill state may have changed while
+ * all the interrupts were disabled, in this case we couldn't
+ * receive the RF kill interrupt and update the state in the
+ * op_mode.
+ * Don't call the op_mode if the rkfill state hasn't changed.
+ * This allows the op_mode to call stop_device from the rfkill
+ * notification without endless recursion. Under very rare
+ * circumstances, we might have a small recursion if the rfkill
+ * state changed exactly now while we were called from stop_device.
+ * This is very unlikely but can happen and is supported.
+ */
+ hw_rfkill = iwl_is_rfkill_set(trans);
+ if (hw_rfkill)
+ set_bit(STATUS_RFKILL, &trans->status);
+ else
+ clear_bit(STATUS_RFKILL, &trans->status);
+ if (hw_rfkill != was_hw_rfkill)
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+
+ /* re-take ownership to prevent other users from stealing the deivce */
+ iwl_pcie_prepare_card_hw(trans);
+}
+
+static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ mutex_lock(&trans_pcie->mutex);
+ _iwl_trans_pcie_stop_device(trans, low_power);
+ mutex_unlock(&trans_pcie->mutex);
+}
+
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
+{
+ struct iwl_trans_pcie __maybe_unused *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ lockdep_assert_held(&trans_pcie->mutex);
+
+ if (iwl_op_mode_hw_rf_kill(trans->op_mode, state))
+ _iwl_trans_pcie_stop_device(trans, true);
+}
+
+static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) {
+ /* Enable persistence mode to avoid reset */
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
+ }
+
+ iwl_disable_interrupts(trans);
+
+ /*
+ * in testing mode, the host stays awake and the
+ * hardware won't be reset (not even partially)
+ */
+ if (test)
+ return;
+
+ iwl_pcie_disable_ict(trans);
+
+ synchronize_irq(trans_pcie->pci_dev->irq);
+
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D3) {
+ /*
+ * reset TX queues -- some of their registers reset during S3
+ * so if we don't reset everything here the D3 image would try
+ * to execute some invalid memory upon resume
+ */
+ iwl_trans_pcie_tx_reset(trans);
+ }
+
+ iwl_pcie_set_pwr(trans, true);
+}
+
+static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
+ enum iwl_d3_status *status,
+ bool test)
+{
+ u32 val;
+ int ret;
+
+ if (test) {
+ iwl_enable_interrupts(trans);
+ *status = IWL_D3_STATUS_ALIVE;
+ return 0;
+ }
+
+ /*
+ * Also enables interrupts - none will happen as the device doesn't
+ * know we're waking it up, only when the opmode actually tells it
+ * after this call.
+ */
+ iwl_pcie_reset_ict(trans);
+
+ iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ udelay(2);
+
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ 25000);
+ if (ret < 0) {
+ IWL_ERR(trans, "Failed to resume the device (mac ready)\n");
+ return ret;
+ }
+
+ iwl_pcie_set_pwr(trans, false);
+
+ if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) {
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ } else {
+ iwl_trans_pcie_tx_reset(trans);
+
+ ret = iwl_pcie_rx_init(trans);
+ if (ret) {
+ IWL_ERR(trans,
+ "Failed to resume the device (RX reset)\n");
+ return ret;
+ }
+ }
+
+ val = iwl_read32(trans, CSR_RESET);
+ if (val & CSR_RESET_REG_FLAG_NEVO_RESET)
+ *status = IWL_D3_STATUS_RESET;
+ else
+ *status = IWL_D3_STATUS_ALIVE;
+
+ return 0;
+}
+
+static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ bool hw_rfkill;
+ int err;
+
+ lockdep_assert_held(&trans_pcie->mutex);
+
+ err = iwl_pcie_prepare_card_hw(trans);
+ if (err) {
+ IWL_ERR(trans, "Error while preparing HW: %d\n", err);
+ return err;
+ }
+
+ /* Reset the entire device */
+ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ usleep_range(10, 15);
+
+ iwl_pcie_apm_init(trans);
+
+ /* From now on, the op_mode will be kept updated about RF kill state */
+ iwl_enable_rfkill_int(trans);
+
+ /* Set is_down to false here so that...*/
+ trans_pcie->is_down = false;
+
+ hw_rfkill = iwl_is_rfkill_set(trans);
+ if (hw_rfkill)
+ set_bit(STATUS_RFKILL, &trans->status);
+ else
+ clear_bit(STATUS_RFKILL, &trans->status);
+ /* ... rfkill can call stop_device and set it false if needed */
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+
+ return 0;
+}
+
+static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int ret;
+
+ mutex_lock(&trans_pcie->mutex);
+ ret = _iwl_trans_pcie_start_hw(trans, low_power);
+ mutex_unlock(&trans_pcie->mutex);
+
+ return ret;
+}
+
+static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ mutex_lock(&trans_pcie->mutex);
+
+ /* disable interrupts - don't enable HW RF kill interrupt */
+ spin_lock(&trans_pcie->irq_lock);
+ iwl_disable_interrupts(trans);
+ spin_unlock(&trans_pcie->irq_lock);
+
+ iwl_pcie_apm_stop(trans, true);
+
+ spin_lock(&trans_pcie->irq_lock);
+ iwl_disable_interrupts(trans);
+ spin_unlock(&trans_pcie->irq_lock);
+
+ iwl_pcie_disable_ict(trans);
+
+ mutex_unlock(&trans_pcie->mutex);
+
+ synchronize_irq(trans_pcie->pci_dev->irq);
+}
+
+static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
+{
+ writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
+}
+
+static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+ writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
+}
+
+static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
+{
+ return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
+}
+
+static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg)
+{
+ iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR,
+ ((reg & 0x000FFFFF) | (3 << 24)));
+ return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT);
+}
+
+static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr,
+ u32 val)
+{
+ iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR,
+ ((addr & 0x000FFFFF) | (3 << 24)));
+ iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
+}
+
+static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget)
+{
+ WARN_ON(1);
+ return 0;
+}
+
+static void iwl_trans_pcie_configure(struct iwl_trans *trans,
+ const struct iwl_trans_config *trans_cfg)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ trans_pcie->cmd_queue = trans_cfg->cmd_queue;
+ trans_pcie->cmd_fifo = trans_cfg->cmd_fifo;
+ trans_pcie->cmd_q_wdg_timeout = trans_cfg->cmd_q_wdg_timeout;
+ if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS))
+ trans_pcie->n_no_reclaim_cmds = 0;
+ else
+ trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds;
+ if (trans_pcie->n_no_reclaim_cmds)
+ memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
+ trans_pcie->n_no_reclaim_cmds * sizeof(u8));
+
+ trans_pcie->rx_buf_size = trans_cfg->rx_buf_size;
+ trans_pcie->rx_page_order =
+ iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size);
+
+ trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header;
+ trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
+ trans_pcie->scd_set_active = trans_cfg->scd_set_active;
+ trans_pcie->sw_csum_tx = trans_cfg->sw_csum_tx;
+
+ trans->command_groups = trans_cfg->command_groups;
+ trans->command_groups_size = trans_cfg->command_groups_size;
+
+ /* init ref_count to 1 (should be cleared when ucode is loaded) */
+ trans_pcie->ref_count = 1;
+
+ /* Initialize NAPI here - it should be before registering to mac80211
+ * in the opmode but after the HW struct is allocated.
+ * As this function may be called again in some corner cases don't
+ * do anything if NAPI was already initialized.
+ */
+ if (!trans_pcie->napi.poll) {
+ init_dummy_netdev(&trans_pcie->napi_dev);
+ netif_napi_add(&trans_pcie->napi_dev, &trans_pcie->napi,
+ iwl_pcie_dummy_napi_poll, 64);
+ }
+}
+
+void iwl_trans_pcie_free(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int i;
+
+ synchronize_irq(trans_pcie->pci_dev->irq);
+
+ iwl_pcie_tx_free(trans);
+ iwl_pcie_rx_free(trans);
+
+ free_irq(trans_pcie->pci_dev->irq, trans);
+ iwl_pcie_free_ict(trans);
+
+ pci_disable_msi(trans_pcie->pci_dev);
+ iounmap(trans_pcie->hw_base);
+ pci_release_regions(trans_pcie->pci_dev);
+ pci_disable_device(trans_pcie->pci_dev);
+
+ if (trans_pcie->napi.poll)
+ netif_napi_del(&trans_pcie->napi);
+
+ iwl_pcie_free_fw_monitor(trans);
+
+ for_each_possible_cpu(i) {
+ struct iwl_tso_hdr_page *p =
+ per_cpu_ptr(trans_pcie->tso_hdr_page, i);
+
+ if (p->page)
+ __free_page(p->page);
+ }
+
+ free_percpu(trans_pcie->tso_hdr_page);
+ iwl_trans_free(trans);
+}
+
+static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
+{
+ if (state)
+ set_bit(STATUS_TPOWER_PMI, &trans->status);
+ else
+ clear_bit(STATUS_TPOWER_PMI, &trans->status);
+}
+
+static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,
+ unsigned long *flags)
+{
+ int ret;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ spin_lock_irqsave(&trans_pcie->reg_lock, *flags);
+
+ if (trans_pcie->cmd_hold_nic_awake)
+ goto out;
+
+ /* this bit wakes up the NIC */
+ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ udelay(2);
+
+ /*
+ * These bits say the device is running, and should keep running for
+ * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
+ * but they do not indicate that embedded SRAM is restored yet;
+ * 3945 and 4965 have volatile SRAM, and must save/restore contents
+ * to/from host DRAM when sleeping/waking for power-saving.
+ * Each direction takes approximately 1/4 millisecond; with this
+ * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
+ * series of register accesses are expected (e.g. reading Event Log),
+ * to keep device from sleeping.
+ *
+ * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
+ * SRAM is okay/restored. We don't check that here because this call
+ * is just for hardware register access; but GP1 MAC_SLEEP check is a
+ * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
+ *
+ * 5000 series and later (including 1000 series) have non-volatile SRAM,
+ * and do not save/restore SRAM when power cycling.
+ */
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+ (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+ CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
+ if (unlikely(ret < 0)) {
+ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
+ WARN_ONCE(1,
+ "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
+ iwl_read32(trans, CSR_GP_CNTRL));
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
+ return false;
+ }
+
+out:
+ /*
+ * Fool sparse by faking we release the lock - sparse will
+ * track nic_access anyway.
+ */
+ __release(&trans_pcie->reg_lock);
+ return true;
+}
+
+static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
+ unsigned long *flags)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ lockdep_assert_held(&trans_pcie->reg_lock);
+
+ /*
+ * Fool sparse by faking we acquiring the lock - sparse will
+ * track nic_access anyway.
+ */
+ __acquire(&trans_pcie->reg_lock);
+
+ if (trans_pcie->cmd_hold_nic_awake)
+ goto out;
+
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ /*
+ * Above we read the CSR_GP_CNTRL register, which will flush
+ * any previous writes, but we need the write that clears the
+ * MAC_ACCESS_REQ bit to be performed before any other writes
+ * scheduled on different CPUs (after we drop reg_lock).
+ */
+ mmiowb();
+out:
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
+}
+
+static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
+ void *buf, int dwords)
+{
+ unsigned long flags;
+ int offs, ret = 0;
+ u32 *vals = buf;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr);
+ for (offs = 0; offs < dwords; offs++)
+ vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+ iwl_trans_release_nic_access(trans, &flags);
+ } else {
+ ret = -EBUSY;
+ }
+ return ret;
+}
+
+static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
+ const void *buf, int dwords)
+{
+ unsigned long flags;
+ int offs, ret = 0;
+ const u32 *vals = buf;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
+ for (offs = 0; offs < dwords; offs++)
+ iwl_write32(trans, HBUS_TARG_MEM_WDAT,
+ vals ? vals[offs] : 0);
+ iwl_trans_release_nic_access(trans, &flags);
+ } else {
+ ret = -EBUSY;
+ }
+ return ret;
+}
+
+static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans,
+ unsigned long txqs,
+ bool freeze)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int queue;
+
+ for_each_set_bit(queue, &txqs, BITS_PER_LONG) {
+ struct iwl_txq *txq = &trans_pcie->txq[queue];
+ unsigned long now;
+
+ spin_lock_bh(&txq->lock);
+
+ now = jiffies;
+
+ if (txq->frozen == freeze)
+ goto next_queue;
+
+ IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n",
+ freeze ? "Freezing" : "Waking", queue);
+
+ txq->frozen = freeze;
+
+ if (txq->q.read_ptr == txq->q.write_ptr)
+ goto next_queue;
+
+ if (freeze) {
+ if (unlikely(time_after(now,
+ txq->stuck_timer.expires))) {
+ /*
+ * The timer should have fired, maybe it is
+ * spinning right now on the lock.
+ */
+ goto next_queue;
+ }
+ /* remember how long until the timer fires */
+ txq->frozen_expiry_remainder =
+ txq->stuck_timer.expires - now;
+ del_timer(&txq->stuck_timer);
+ goto next_queue;
+ }
+
+ /*
+ * Wake a non-empty queue -> arm timer with the
+ * remainder before it froze
+ */
+ mod_timer(&txq->stuck_timer,
+ now + txq->frozen_expiry_remainder);
+
+next_queue:
+ spin_unlock_bh(&txq->lock);
+ }
+}
+
+static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int i;
+
+ for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+ struct iwl_txq *txq = &trans_pcie->txq[i];
+
+ if (i == trans_pcie->cmd_queue)
+ continue;
+
+ spin_lock_bh(&txq->lock);
+
+ if (!block && !(WARN_ON_ONCE(!txq->block))) {
+ txq->block--;
+ if (!txq->block) {
+ iwl_write32(trans, HBUS_TARG_WRPTR,
+ txq->q.write_ptr | (i << 8));
+ }
+ } else if (block) {
+ txq->block++;
+ }
+
+ spin_unlock_bh(&txq->lock);
+ }
+}
+
+#define IWL_FLUSH_WAIT_MS 2000
+
+static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq;
+ struct iwl_queue *q;
+ int cnt;
+ unsigned long now = jiffies;
+ u32 scd_sram_addr;
+ u8 buf[16];
+ int ret = 0;
+
+ /* waiting for all the tx frames complete might take a while */
+ for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
+ u8 wr_ptr;
+
+ if (cnt == trans_pcie->cmd_queue)
+ continue;
+ if (!test_bit(cnt, trans_pcie->queue_used))
+ continue;
+ if (!(BIT(cnt) & txq_bm))
+ continue;
+
+ IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt);
+ txq = &trans_pcie->txq[cnt];
+ q = &txq->q;
+ wr_ptr = ACCESS_ONCE(q->write_ptr);
+
+ while (q->read_ptr != ACCESS_ONCE(q->write_ptr) &&
+ !time_after(jiffies,
+ now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) {
+ u8 write_ptr = ACCESS_ONCE(q->write_ptr);
+
+ if (WARN_ONCE(wr_ptr != write_ptr,
+ "WR pointer moved while flushing %d -> %d\n",
+ wr_ptr, write_ptr))
+ return -ETIMEDOUT;
+ msleep(1);
+ }
+
+ if (q->read_ptr != q->write_ptr) {
+ IWL_ERR(trans,
+ "fail to flush all tx fifo queues Q %d\n", cnt);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt);
+ }
+
+ if (!ret)
+ return 0;
+
+ IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
+ txq->q.read_ptr, txq->q.write_ptr);
+
+ scd_sram_addr = trans_pcie->scd_base_addr +
+ SCD_TX_STTS_QUEUE_OFFSET(txq->q.id);
+ iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
+
+ iwl_print_hex_error(trans, buf, sizeof(buf));
+
+ for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++)
+ IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt,
+ iwl_read_direct32(trans, FH_TX_TRB_REG(cnt)));
+
+ for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
+ u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt));
+ u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
+ bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
+ u32 tbl_dw =
+ iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr +
+ SCD_TRANS_TBL_OFFSET_QUEUE(cnt));
+
+ if (cnt & 0x1)
+ tbl_dw = (tbl_dw & 0xFFFF0000) >> 16;
+ else
+ tbl_dw = tbl_dw & 0x0000FFFF;
+
+ IWL_ERR(trans,
+ "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
+ cnt, active ? "" : "in", fifo, tbl_dw,
+ iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) &
+ (TFD_QUEUE_SIZE_MAX - 1),
+ iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
+ }
+
+ return ret;
+}
+
+static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
+ u32 mask, u32 value)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ unsigned long flags;
+
+ spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+ __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value);
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+}
+
+void iwl_trans_pcie_ref(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ unsigned long flags;
+
+ if (iwlwifi_mod_params.d0i3_disable)
+ return;
+
+ spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+ IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+ trans_pcie->ref_count++;
+ spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+}
+
+void iwl_trans_pcie_unref(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ unsigned long flags;
+
+ if (iwlwifi_mod_params.d0i3_disable)
+ return;
+
+ spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+ IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+ if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) {
+ spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+ return;
+ }
+ trans_pcie->ref_count--;
+ spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+}
+
+static const char *get_csr_string(int cmd)
+{
+#define IWL_CMD(x) case x: return #x
+ switch (cmd) {
+ IWL_CMD(CSR_HW_IF_CONFIG_REG);
+ IWL_CMD(CSR_INT_COALESCING);
+ IWL_CMD(CSR_INT);
+ IWL_CMD(CSR_INT_MASK);
+ IWL_CMD(CSR_FH_INT_STATUS);
+ IWL_CMD(CSR_GPIO_IN);
+ IWL_CMD(CSR_RESET);
+ IWL_CMD(CSR_GP_CNTRL);
+ IWL_CMD(CSR_HW_REV);
+ IWL_CMD(CSR_EEPROM_REG);
+ IWL_CMD(CSR_EEPROM_GP);
+ IWL_CMD(CSR_OTP_GP_REG);
+ IWL_CMD(CSR_GIO_REG);
+ IWL_CMD(CSR_GP_UCODE_REG);
+ IWL_CMD(CSR_GP_DRIVER_REG);
+ IWL_CMD(CSR_UCODE_DRV_GP1);
+ IWL_CMD(CSR_UCODE_DRV_GP2);
+ IWL_CMD(CSR_LED_REG);
+ IWL_CMD(CSR_DRAM_INT_TBL_REG);
+ IWL_CMD(CSR_GIO_CHICKEN_BITS);
+ IWL_CMD(CSR_ANA_PLL_CFG);
+ IWL_CMD(CSR_HW_REV_WA_REG);
+ IWL_CMD(CSR_MONITOR_STATUS_REG);
+ IWL_CMD(CSR_DBG_HPET_MEM_REG);
+ default:
+ return "UNKNOWN";
+ }
+#undef IWL_CMD
+}
+
+void iwl_pcie_dump_csr(struct iwl_trans *trans)
+{
+ int i;
+ static const u32 csr_tbl[] = {
+ CSR_HW_IF_CONFIG_REG,
+ CSR_INT_COALESCING,
+ CSR_INT,
+ CSR_INT_MASK,
+ CSR_FH_INT_STATUS,
+ CSR_GPIO_IN,
+ CSR_RESET,
+ CSR_GP_CNTRL,
+ CSR_HW_REV,
+ CSR_EEPROM_REG,
+ CSR_EEPROM_GP,
+ CSR_OTP_GP_REG,
+ CSR_GIO_REG,
+ CSR_GP_UCODE_REG,
+ CSR_GP_DRIVER_REG,
+ CSR_UCODE_DRV_GP1,
+ CSR_UCODE_DRV_GP2,
+ CSR_LED_REG,
+ CSR_DRAM_INT_TBL_REG,
+ CSR_GIO_CHICKEN_BITS,
+ CSR_ANA_PLL_CFG,
+ CSR_MONITOR_STATUS_REG,
+ CSR_HW_REV_WA_REG,
+ CSR_DBG_HPET_MEM_REG
+ };
+ IWL_ERR(trans, "CSR values:\n");
+ IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is "
+ "CSR_INT_PERIODIC_REG)\n");
+ for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) {
+ IWL_ERR(trans, " %25s: 0X%08x\n",
+ get_csr_string(csr_tbl[i]),
+ iwl_read32(trans, csr_tbl[i]));
+ }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+/* create and remove of files */
+#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
+ if (!debugfs_create_file(#name, mode, parent, trans, \
+ &iwl_dbgfs_##name##_ops)) \
+ goto err; \
+} while (0)
+
+/* file operation */
+#define DEBUGFS_READ_FILE_OPS(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = iwl_dbgfs_##name##_write, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = iwl_dbgfs_##name##_write, \
+ .read = iwl_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq;
+ struct iwl_queue *q;
+ char *buf;
+ int pos = 0;
+ int cnt;
+ int ret;
+ size_t bufsz;
+
+ bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues;
+
+ if (!trans_pcie->txq)
+ return -EAGAIN;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
+ txq = &trans_pcie->txq[cnt];
+ q = &txq->q;
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n",
+ cnt, q->read_ptr, q->write_ptr,
+ !!test_bit(cnt, trans_pcie->queue_used),
+ !!test_bit(cnt, trans_pcie->queue_stopped),
+ txq->need_update, txq->frozen,
+ (cnt == trans_pcie->cmd_queue ? " HCMD" : ""));
+ }
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ char buf[256];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n",
+ rxq->read);
+ pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n",
+ rxq->write);
+ pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n",
+ rxq->write_actual);
+ pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n",
+ rxq->need_update);
+ pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n",
+ rxq->free_count);
+ if (rxq->rb_stts) {
+ pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n",
+ le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF);
+ } else {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "closed_rb_num: Not Allocated\n");
+ }
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
+
+ int pos = 0;
+ char *buf;
+ int bufsz = 24 * 64; /* 24 items * 64 char per item */
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Interrupt Statistics Report:\n");
+
+ pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n",
+ isr_stats->hw);
+ pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n",
+ isr_stats->sw);
+ if (isr_stats->sw || isr_stats->hw) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\tLast Restarting Code: 0x%X\n",
+ isr_stats->err_code);
+ }
+#ifdef CONFIG_IWLWIFI_DEBUG
+ pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n",
+ isr_stats->sch);
+ pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n",
+ isr_stats->alive);
+#endif
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n",
+ isr_stats->ctkill);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n",
+ isr_stats->wakeup);
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Rx command responses:\t\t %u\n", isr_stats->rx);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n",
+ isr_stats->tx);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n",
+ isr_stats->unhandled);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_interrupt_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
+
+ char buf[8];
+ int buf_size;
+ u32 reset_flag;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%x", &reset_flag) != 1)
+ return -EFAULT;
+ if (reset_flag == 0)
+ memset(isr_stats, 0, sizeof(*isr_stats));
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_csr_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ char buf[8];
+ int buf_size;
+ int csr;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &csr) != 1)
+ return -EFAULT;
+
+ iwl_pcie_dump_csr(trans);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_trans *trans = file->private_data;
+ char *buf = NULL;
+ ssize_t ret;
+
+ ret = iwl_dump_fh(trans, &buf);
+ if (ret < 0)
+ return ret;
+ if (!buf)
+ return -EINVAL;
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
+ return ret;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
+DEBUGFS_READ_FILE_OPS(fh_reg);
+DEBUGFS_READ_FILE_OPS(rx_queue);
+DEBUGFS_READ_FILE_OPS(tx_queue);
+DEBUGFS_WRITE_FILE_OPS(csr);
+
+/* Create the debugfs files and directories */
+int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
+{
+ struct dentry *dir = trans->dbgfs_dir;
+
+ DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR);
+ DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR);
+ DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
+ DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
+ return 0;
+
+err:
+ IWL_ERR(trans, "failed to create the trans debugfs entry\n");
+ return -ENOMEM;
+}
+#endif /*CONFIG_IWLWIFI_DEBUGFS */
+
+static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
+{
+ u32 cmdlen = 0;
+ int i;
+
+ for (i = 0; i < IWL_NUM_OF_TBS; i++)
+ cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i);
+
+ return cmdlen;
+}
+
+static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data,
+ int allocated_rb_nums)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int max_len = PAGE_SIZE << trans_pcie->rx_page_order;
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ u32 i, r, j, rb_len = 0;
+
+ spin_lock(&rxq->lock);
+
+ r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
+
+ for (i = rxq->read, j = 0;
+ i != r && j < allocated_rb_nums;
+ i = (i + 1) & RX_QUEUE_MASK, j++) {
+ struct iwl_rx_mem_buffer *rxb = rxq->queue[i];
+ struct iwl_fw_error_dump_rb *rb;
+
+ dma_unmap_page(trans->dev, rxb->page_dma, max_len,
+ DMA_FROM_DEVICE);
+
+ rb_len += sizeof(**data) + sizeof(*rb) + max_len;
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RB);
+ (*data)->len = cpu_to_le32(sizeof(*rb) + max_len);
+ rb = (void *)(*data)->data;
+ rb->index = cpu_to_le32(i);
+ memcpy(rb->data, page_address(rxb->page), max_len);
+ /* remap the page for the free benefit */
+ rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0,
+ max_len,
+ DMA_FROM_DEVICE);
+
+ *data = iwl_fw_error_next_data(*data);
+ }
+
+ spin_unlock(&rxq->lock);
+
+ return rb_len;
+}
+#define IWL_CSR_TO_DUMP (0x250)
+
+static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data)
+{
+ u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP;
+ __le32 *val;
+ int i;
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR);
+ (*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP);
+ val = (void *)(*data)->data;
+
+ for (i = 0; i < IWL_CSR_TO_DUMP; i += 4)
+ *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+
+ *data = iwl_fw_error_next_data(*data);
+
+ return csr_len;
+}
+
+static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data)
+{
+ u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND;
+ unsigned long flags;
+ __le32 *val;
+ int i;
+
+ if (!iwl_trans_grab_nic_access(trans, &flags))
+ return 0;
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS);
+ (*data)->len = cpu_to_le32(fh_regs_len);
+ val = (void *)(*data)->data;
+
+ for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32))
+ *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+
+ iwl_trans_release_nic_access(trans, &flags);
+
+ *data = iwl_fw_error_next_data(*data);
+
+ return sizeof(**data) + fh_regs_len;
+}
+
+static u32
+iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_fw_mon *fw_mon_data,
+ u32 monitor_len)
+{
+ u32 buf_size_in_dwords = (monitor_len >> 2);
+ u32 *buffer = (u32 *)fw_mon_data->data;
+ unsigned long flags;
+ u32 i;
+
+ if (!iwl_trans_grab_nic_access(trans, &flags))
+ return 0;
+
+ iwl_write_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x1);
+ for (i = 0; i < buf_size_in_dwords; i++)
+ buffer[i] = iwl_read_prph_no_grab(trans,
+ MON_DMARB_RD_DATA_ADDR);
+ iwl_write_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x0);
+
+ iwl_trans_release_nic_access(trans, &flags);
+
+ return monitor_len;
+}
+
+static u32
+iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data,
+ u32 monitor_len)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 len = 0;
+
+ if ((trans_pcie->fw_mon_page &&
+ trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
+ trans->dbg_dest_tlv) {
+ struct iwl_fw_error_dump_fw_mon *fw_mon_data;
+ u32 base, write_ptr, wrap_cnt;
+
+ /* If there was a dest TLV - use the values from there */
+ if (trans->dbg_dest_tlv) {
+ write_ptr =
+ le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+ wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+ base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+ } else {
+ base = MON_BUFF_BASE_ADDR;
+ write_ptr = MON_BUFF_WRPTR;
+ wrap_cnt = MON_BUFF_CYCLE_CNT;
+ }
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
+ fw_mon_data = (void *)(*data)->data;
+ fw_mon_data->fw_mon_wr_ptr =
+ cpu_to_le32(iwl_read_prph(trans, write_ptr));
+ fw_mon_data->fw_mon_cycle_cnt =
+ cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
+ fw_mon_data->fw_mon_base_ptr =
+ cpu_to_le32(iwl_read_prph(trans, base));
+
+ len += sizeof(**data) + sizeof(*fw_mon_data);
+ if (trans_pcie->fw_mon_page) {
+ /*
+ * The firmware is now asserted, it won't write anything
+ * to the buffer. CPU can take ownership to fetch the
+ * data. The buffer will be handed back to the device
+ * before the firmware will be restarted.
+ */
+ dma_sync_single_for_cpu(trans->dev,
+ trans_pcie->fw_mon_phys,
+ trans_pcie->fw_mon_size,
+ DMA_FROM_DEVICE);
+ memcpy(fw_mon_data->data,
+ page_address(trans_pcie->fw_mon_page),
+ trans_pcie->fw_mon_size);
+
+ monitor_len = trans_pcie->fw_mon_size;
+ } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) {
+ /*
+ * Update pointers to reflect actual values after
+ * shifting
+ */
+ base = iwl_read_prph(trans, base) <<
+ trans->dbg_dest_tlv->base_shift;
+ iwl_trans_read_mem(trans, base, fw_mon_data->data,
+ monitor_len / sizeof(u32));
+ } else if (trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) {
+ monitor_len =
+ iwl_trans_pci_dump_marbh_monitor(trans,
+ fw_mon_data,
+ monitor_len);
+ } else {
+ /* Didn't match anything - output no monitor data */
+ monitor_len = 0;
+ }
+
+ len += monitor_len;
+ (*data)->len = cpu_to_le32(monitor_len + sizeof(*fw_mon_data));
+ }
+
+ return len;
+}
+
+static struct iwl_trans_dump_data
+*iwl_trans_pcie_dump_data(struct iwl_trans *trans,
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_fw_error_dump_data *data;
+ struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
+ struct iwl_fw_error_dump_txcmd *txcmd;
+ struct iwl_trans_dump_data *dump_data;
+ u32 len, num_rbs;
+ u32 monitor_len;
+ int i, ptr;
+ bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status);
+
+ /* transport dump header */
+ len = sizeof(*dump_data);
+
+ /* host commands */
+ len += sizeof(*data) +
+ cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
+
+ /* FW monitor */
+ if (trans_pcie->fw_mon_page) {
+ len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
+ trans_pcie->fw_mon_size;
+ monitor_len = trans_pcie->fw_mon_size;
+ } else if (trans->dbg_dest_tlv) {
+ u32 base, end;
+
+ base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+ end = le32_to_cpu(trans->dbg_dest_tlv->end_reg);
+
+ base = iwl_read_prph(trans, base) <<
+ trans->dbg_dest_tlv->base_shift;
+ end = iwl_read_prph(trans, end) <<
+ trans->dbg_dest_tlv->end_shift;
+
+ /* Make "end" point to the actual end */
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 ||
+ trans->dbg_dest_tlv->monitor_mode == MARBH_MODE)
+ end += (1 << trans->dbg_dest_tlv->end_shift);
+ monitor_len = end - base;
+ len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
+ monitor_len;
+ } else {
+ monitor_len = 0;
+ }
+
+ if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) {
+ dump_data = vzalloc(len);
+ if (!dump_data)
+ return NULL;
+
+ data = (void *)dump_data->data;
+ len = iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
+ dump_data->len = len;
+
+ return dump_data;
+ }
+
+ /* CSR registers */
+ len += sizeof(*data) + IWL_CSR_TO_DUMP;
+
+ /* FH registers */
+ len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
+
+ if (dump_rbs) {
+ /* RBs */
+ num_rbs = le16_to_cpu(ACCESS_ONCE(
+ trans_pcie->rxq.rb_stts->closed_rb_num))
+ & 0x0FFF;
+ num_rbs = (num_rbs - trans_pcie->rxq.read) & RX_QUEUE_MASK;
+ len += num_rbs * (sizeof(*data) +
+ sizeof(struct iwl_fw_error_dump_rb) +
+ (PAGE_SIZE << trans_pcie->rx_page_order));
+ }
+
+ dump_data = vzalloc(len);
+ if (!dump_data)
+ return NULL;
+
+ len = 0;
+ data = (void *)dump_data->data;
+ data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
+ txcmd = (void *)data->data;
+ spin_lock_bh(&cmdq->lock);
+ ptr = cmdq->q.write_ptr;
+ for (i = 0; i < cmdq->q.n_window; i++) {
+ u8 idx = get_cmd_index(&cmdq->q, ptr);
+ u32 caplen, cmdlen;
+
+ cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]);
+ caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen);
+
+ if (cmdlen) {
+ len += sizeof(*txcmd) + caplen;
+ txcmd->cmdlen = cpu_to_le32(cmdlen);
+ txcmd->caplen = cpu_to_le32(caplen);
+ memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen);
+ txcmd = (void *)((u8 *)txcmd->data + caplen);
+ }
+
+ ptr = iwl_queue_dec_wrap(ptr);
+ }
+ spin_unlock_bh(&cmdq->lock);
+
+ data->len = cpu_to_le32(len);
+ len += sizeof(*data);
+ data = iwl_fw_error_next_data(data);
+
+ len += iwl_trans_pcie_dump_csr(trans, &data);
+ len += iwl_trans_pcie_fh_regs_dump(trans, &data);
+ if (dump_rbs)
+ len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
+
+ len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
+
+ dump_data->len = len;
+
+ return dump_data;
+}
+
+static const struct iwl_trans_ops trans_ops_pcie = {
+ .start_hw = iwl_trans_pcie_start_hw,
+ .op_mode_leave = iwl_trans_pcie_op_mode_leave,
+ .fw_alive = iwl_trans_pcie_fw_alive,
+ .start_fw = iwl_trans_pcie_start_fw,
+ .stop_device = iwl_trans_pcie_stop_device,
+
+ .d3_suspend = iwl_trans_pcie_d3_suspend,
+ .d3_resume = iwl_trans_pcie_d3_resume,
+
+ .send_cmd = iwl_trans_pcie_send_hcmd,
+
+ .tx = iwl_trans_pcie_tx,
+ .reclaim = iwl_trans_pcie_reclaim,
+
+ .txq_disable = iwl_trans_pcie_txq_disable,
+ .txq_enable = iwl_trans_pcie_txq_enable,
+
+ .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
+ .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
+ .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
+
+ .write8 = iwl_trans_pcie_write8,
+ .write32 = iwl_trans_pcie_write32,
+ .read32 = iwl_trans_pcie_read32,
+ .read_prph = iwl_trans_pcie_read_prph,
+ .write_prph = iwl_trans_pcie_write_prph,
+ .read_mem = iwl_trans_pcie_read_mem,
+ .write_mem = iwl_trans_pcie_write_mem,
+ .configure = iwl_trans_pcie_configure,
+ .set_pmi = iwl_trans_pcie_set_pmi,
+ .grab_nic_access = iwl_trans_pcie_grab_nic_access,
+ .release_nic_access = iwl_trans_pcie_release_nic_access,
+ .set_bits_mask = iwl_trans_pcie_set_bits_mask,
+
+ .ref = iwl_trans_pcie_ref,
+ .unref = iwl_trans_pcie_unref,
+
+ .dump_data = iwl_trans_pcie_dump_data,
+};
+
+struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
+ const struct pci_device_id *ent,
+ const struct iwl_cfg *cfg)
+{
+ struct iwl_trans_pcie *trans_pcie;
+ struct iwl_trans *trans;
+ u16 pci_cmd;
+ int ret;
+
+ trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie),
+ &pdev->dev, cfg, &trans_ops_pcie, 0);
+ if (!trans)
+ return ERR_PTR(-ENOMEM);
+
+ trans->max_skb_frags = IWL_PCIE_MAX_FRAGS;
+
+ trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ trans_pcie->trans = trans;
+ spin_lock_init(&trans_pcie->irq_lock);
+ spin_lock_init(&trans_pcie->reg_lock);
+ spin_lock_init(&trans_pcie->ref_lock);
+ mutex_init(&trans_pcie->mutex);
+ init_waitqueue_head(&trans_pcie->ucode_write_waitq);
+ trans_pcie->tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
+ if (!trans_pcie->tso_hdr_page) {
+ ret = -ENOMEM;
+ goto out_no_pci;
+ }
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto out_no_pci;
+
+ if (!cfg->base_params->pcie_l1_allowed) {
+ /*
+ * W/A - seems to solve weird behavior. We need to remove this
+ * if we don't want to stay in L1 all the time. This wastes a
+ * lot of power.
+ */
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 |
+ PCIE_LINK_STATE_CLKPM);
+ }
+
+ pci_set_master(pdev);
+
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
+ if (!ret)
+ ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
+ if (ret) {
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (!ret)
+ ret = pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(32));
+ /* both attempts failed: */
+ if (ret) {
+ dev_err(&pdev->dev, "No suitable DMA available\n");
+ goto out_pci_disable_device;
+ }
+ }
+
+ ret = pci_request_regions(pdev, DRV_NAME);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_request_regions failed\n");
+ goto out_pci_disable_device;
+ }
+
+ trans_pcie->hw_base = pci_ioremap_bar(pdev, 0);
+ if (!trans_pcie->hw_base) {
+ dev_err(&pdev->dev, "pci_ioremap_bar failed\n");
+ ret = -ENODEV;
+ goto out_pci_release_regions;
+ }
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
+ trans->dev = &pdev->dev;
+ trans_pcie->pci_dev = pdev;
+ iwl_disable_interrupts(trans);
+
+ ret = pci_enable_msi(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret);
+ /* enable rfkill interrupt: hw bug w/a */
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+ if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
+ pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
+ }
+ }
+
+ trans->hw_rev = iwl_read32(trans, CSR_HW_REV);
+ /*
+ * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
+ * changed, and now the revision step also includes bit 0-1 (no more
+ * "dash" value). To keep hw_rev backwards compatible - we'll store it
+ * in the old format.
+ */
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+ unsigned long flags;
+
+ trans->hw_rev = (trans->hw_rev & 0xfff0) |
+ (CSR_HW_REV_STEP(trans->hw_rev << 2) << 2);
+
+ ret = iwl_pcie_prepare_card_hw(trans);
+ if (ret) {
+ IWL_WARN(trans, "Exit HW not ready\n");
+ goto out_pci_disable_msi;
+ }
+
+ /*
+ * in-order to recognize C step driver should read chip version
+ * id located at the AUX bus MISC address space.
+ */
+ iwl_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+ udelay(2);
+
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ 25000);
+ if (ret < 0) {
+ IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n");
+ goto out_pci_disable_msi;
+ }
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ u32 hw_step;
+
+ hw_step = iwl_read_prph_no_grab(trans, WFPM_CTRL_REG);
+ hw_step |= ENABLE_WFPM;
+ iwl_write_prph_no_grab(trans, WFPM_CTRL_REG, hw_step);
+ hw_step = iwl_read_prph_no_grab(trans, AUX_MISC_REG);
+ hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF;
+ if (hw_step == 0x3)
+ trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) |
+ (SILICON_C_STEP << 2);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+ }
+
+ trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
+ snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
+ "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
+
+ /* Initialize the wait queue for commands */
+ init_waitqueue_head(&trans_pcie->wait_command_queue);
+
+ ret = iwl_pcie_alloc_ict(trans);
+ if (ret)
+ goto out_pci_disable_msi;
+
+ ret = request_threaded_irq(pdev->irq, iwl_pcie_isr,
+ iwl_pcie_irq_handler,
+ IRQF_SHARED, DRV_NAME, trans);
+ if (ret) {
+ IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
+ goto out_free_ict;
+ }
+
+ trans_pcie->inta_mask = CSR_INI_SET_MASK;
+
+ return trans;
+
+out_free_ict:
+ iwl_pcie_free_ict(trans);
+out_pci_disable_msi:
+ pci_disable_msi(pdev);
+out_pci_release_regions:
+ pci_release_regions(pdev);
+out_pci_disable_device:
+ pci_disable_device(pdev);
+out_no_pci:
+ free_percpu(trans_pcie->tso_hdr_page);
+ iwl_trans_free(trans);
+ return ERR_PTR(ret);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
new file mode 100644
index 000000000000..5262028b5505
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
@@ -0,0 +1,2295 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <net/ip6_checksum.h>
+#include <net/tso.h>
+#include <net/ip6_checksum.h>
+
+#include "iwl-debug.h"
+#include "iwl-csr.h"
+#include "iwl-prph.h"
+#include "iwl-io.h"
+#include "iwl-scd.h"
+#include "iwl-op-mode.h"
+#include "internal.h"
+/* FIXME: need to abstract out TX command (once we know what it looks like) */
+#include "dvm/commands.h"
+
+#define IWL_TX_CRC_SIZE 4
+#define IWL_TX_DELIMITER_SIZE 4
+
+/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
+ * DMA services
+ *
+ * Theory of operation
+ *
+ * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer
+ * of buffer descriptors, each of which points to one or more data buffers for
+ * the device to read from or fill. Driver and device exchange status of each
+ * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty
+ * entries in each circular buffer, to protect against confusing empty and full
+ * queue states.
+ *
+ * The device reads or writes the data in the queues via the device's several
+ * DMA/FIFO channels. Each queue is mapped to a single DMA channel.
+ *
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
+ * Tx queue resumed.
+ *
+ ***************************************************/
+static int iwl_queue_space(const struct iwl_queue *q)
+{
+ unsigned int max;
+ unsigned int used;
+
+ /*
+ * To avoid ambiguity between empty and completely full queues, there
+ * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue.
+ * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need
+ * to reserve any queue entries for this purpose.
+ */
+ if (q->n_window < TFD_QUEUE_SIZE_MAX)
+ max = q->n_window;
+ else
+ max = TFD_QUEUE_SIZE_MAX - 1;
+
+ /*
+ * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to
+ * modulo by TFD_QUEUE_SIZE_MAX and is well defined.
+ */
+ used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1);
+
+ if (WARN_ON(used > max))
+ return 0;
+
+ return max - used;
+}
+
+/*
+ * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
+ */
+static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id)
+{
+ q->n_window = slots_num;
+ q->id = id;
+
+ /* slots_num must be power-of-two size, otherwise
+ * get_cmd_index is broken. */
+ if (WARN_ON(!is_power_of_2(slots_num)))
+ return -EINVAL;
+
+ q->low_mark = q->n_window / 4;
+ if (q->low_mark < 4)
+ q->low_mark = 4;
+
+ q->high_mark = q->n_window / 8;
+ if (q->high_mark < 2)
+ q->high_mark = 2;
+
+ q->write_ptr = 0;
+ q->read_ptr = 0;
+
+ return 0;
+}
+
+static int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans,
+ struct iwl_dma_ptr *ptr, size_t size)
+{
+ if (WARN_ON(ptr->addr))
+ return -EINVAL;
+
+ ptr->addr = dma_alloc_coherent(trans->dev, size,
+ &ptr->dma, GFP_KERNEL);
+ if (!ptr->addr)
+ return -ENOMEM;
+ ptr->size = size;
+ return 0;
+}
+
+static void iwl_pcie_free_dma_ptr(struct iwl_trans *trans,
+ struct iwl_dma_ptr *ptr)
+{
+ if (unlikely(!ptr->addr))
+ return;
+
+ dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma);
+ memset(ptr, 0, sizeof(*ptr));
+}
+
+static void iwl_pcie_txq_stuck_timer(unsigned long data)
+{
+ struct iwl_txq *txq = (void *)data;
+ struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
+ struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie);
+ u32 scd_sram_addr = trans_pcie->scd_base_addr +
+ SCD_TX_STTS_QUEUE_OFFSET(txq->q.id);
+ u8 buf[16];
+ int i;
+
+ spin_lock(&txq->lock);
+ /* check if triggered erroneously */
+ if (txq->q.read_ptr == txq->q.write_ptr) {
+ spin_unlock(&txq->lock);
+ return;
+ }
+ spin_unlock(&txq->lock);
+
+ IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id,
+ jiffies_to_msecs(txq->wd_timeout));
+ IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
+ txq->q.read_ptr, txq->q.write_ptr);
+
+ iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
+
+ iwl_print_hex_error(trans, buf, sizeof(buf));
+
+ for (i = 0; i < FH_TCSR_CHNL_NUM; i++)
+ IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i,
+ iwl_read_direct32(trans, FH_TX_TRB_REG(i)));
+
+ for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+ u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i));
+ u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
+ bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
+ u32 tbl_dw =
+ iwl_trans_read_mem32(trans,
+ trans_pcie->scd_base_addr +
+ SCD_TRANS_TBL_OFFSET_QUEUE(i));
+
+ if (i & 0x1)
+ tbl_dw = (tbl_dw & 0xFFFF0000) >> 16;
+ else
+ tbl_dw = tbl_dw & 0x0000FFFF;
+
+ IWL_ERR(trans,
+ "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
+ i, active ? "" : "in", fifo, tbl_dw,
+ iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) &
+ (TFD_QUEUE_SIZE_MAX - 1),
+ iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
+ }
+
+ iwl_force_nmi(trans);
+}
+
+/*
+ * iwl_pcie_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
+ */
+static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
+ struct iwl_txq *txq, u16 byte_cnt)
+{
+ struct iwlagn_scd_bc_tbl *scd_bc_tbl;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int write_ptr = txq->q.write_ptr;
+ int txq_id = txq->q.id;
+ u8 sec_ctl = 0;
+ u8 sta_id = 0;
+ u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+ __le16 bc_ent;
+ struct iwl_tx_cmd *tx_cmd =
+ (void *) txq->entries[txq->q.write_ptr].cmd->payload;
+
+ scd_bc_tbl = trans_pcie->scd_bc_tbls.addr;
+
+ sta_id = tx_cmd->sta_id;
+ sec_ctl = tx_cmd->sec_ctl;
+
+ switch (sec_ctl & TX_CMD_SEC_MSK) {
+ case TX_CMD_SEC_CCM:
+ len += IEEE80211_CCMP_MIC_LEN;
+ break;
+ case TX_CMD_SEC_TKIP:
+ len += IEEE80211_TKIP_ICV_LEN;
+ break;
+ case TX_CMD_SEC_WEP:
+ len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN;
+ break;
+ }
+
+ if (trans_pcie->bc_table_dword)
+ len = DIV_ROUND_UP(len, 4);
+
+ if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX))
+ return;
+
+ bc_ent = cpu_to_le16(len | (sta_id << 12));
+
+ scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
+
+ if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
+ scd_bc_tbl[txq_id].
+ tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
+}
+
+static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans,
+ struct iwl_txq *txq)
+{
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr;
+ int txq_id = txq->q.id;
+ int read_ptr = txq->q.read_ptr;
+ u8 sta_id = 0;
+ __le16 bc_ent;
+ struct iwl_tx_cmd *tx_cmd =
+ (void *)txq->entries[txq->q.read_ptr].cmd->payload;
+
+ WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
+
+ if (txq_id != trans_pcie->cmd_queue)
+ sta_id = tx_cmd->sta_id;
+
+ bc_ent = cpu_to_le16(1 | (sta_id << 12));
+ scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
+
+ if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
+ scd_bc_tbl[txq_id].
+ tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
+}
+
+/*
+ * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware
+ */
+static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
+ struct iwl_txq *txq)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 reg = 0;
+ int txq_id = txq->q.id;
+
+ lockdep_assert_held(&txq->lock);
+
+ /*
+ * explicitly wake up the NIC if:
+ * 1. shadow registers aren't enabled
+ * 2. NIC is woken up for CMD regardless of shadow outside this function
+ * 3. there is a chance that the NIC is asleep
+ */
+ if (!trans->cfg->base_params->shadow_reg_enable &&
+ txq_id != trans_pcie->cmd_queue &&
+ test_bit(STATUS_TPOWER_PMI, &trans->status)) {
+ /*
+ * wake up nic if it's powered down ...
+ * uCode will wake up, and interrupt us again, so next
+ * time we'll skip this part.
+ */
+ reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n",
+ txq_id, reg);
+ iwl_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ txq->need_update = true;
+ return;
+ }
+ }
+
+ /*
+ * if not in power-save mode, uCode will never sleep when we're
+ * trying to tx (during RFKILL, we're not trying to tx).
+ */
+ IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr);
+ if (!txq->block)
+ iwl_write32(trans, HBUS_TARG_WRPTR,
+ txq->q.write_ptr | (txq_id << 8));
+}
+
+void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int i;
+
+ for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+ struct iwl_txq *txq = &trans_pcie->txq[i];
+
+ spin_lock_bh(&txq->lock);
+ if (trans_pcie->txq[i].need_update) {
+ iwl_pcie_txq_inc_wr_ptr(trans, txq);
+ trans_pcie->txq[i].need_update = false;
+ }
+ spin_unlock_bh(&txq->lock);
+ }
+}
+
+static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+ dma_addr_t addr = get_unaligned_le32(&tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ addr |=
+ ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16;
+
+ return addr;
+}
+
+static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
+ dma_addr_t addr, u16 len)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+ u16 hi_n_len = len << 4;
+
+ put_unaligned_le32(addr, &tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ hi_n_len |= ((addr >> 16) >> 16) & 0xF;
+
+ tb->hi_n_len = cpu_to_le16(hi_n_len);
+
+ tfd->num_tbs = idx + 1;
+}
+
+static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_tfd *tfd)
+{
+ return tfd->num_tbs & 0x1f;
+}
+
+static void iwl_pcie_tfd_unmap(struct iwl_trans *trans,
+ struct iwl_cmd_meta *meta,
+ struct iwl_tfd *tfd)
+{
+ int i;
+ int num_tbs;
+
+ /* Sanity check on number of chunks */
+ num_tbs = iwl_pcie_tfd_get_num_tbs(tfd);
+
+ if (num_tbs >= IWL_NUM_OF_TBS) {
+ IWL_ERR(trans, "Too many chunks: %i\n", num_tbs);
+ /* @todo issue fatal error, it is quite serious situation */
+ return;
+ }
+
+ /* first TB is never freed - it's the scratchbuf data */
+
+ for (i = 1; i < num_tbs; i++) {
+ if (meta->flags & BIT(i + CMD_TB_BITMAP_POS))
+ dma_unmap_page(trans->dev,
+ iwl_pcie_tfd_tb_get_addr(tfd, i),
+ iwl_pcie_tfd_tb_get_len(tfd, i),
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_single(trans->dev,
+ iwl_pcie_tfd_tb_get_addr(tfd, i),
+ iwl_pcie_tfd_tb_get_len(tfd, i),
+ DMA_TO_DEVICE);
+ }
+ tfd->num_tbs = 0;
+}
+
+/*
+ * iwl_pcie_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
+ * @trans - transport private data
+ * @txq - tx queue
+ * @dma_dir - the direction of the DMA mapping
+ *
+ * Does NOT advance any TFD circular buffer read/write indexes
+ * Does NOT free the TFD itself (which is within circular buffer)
+ */
+static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
+{
+ struct iwl_tfd *tfd_tmp = txq->tfds;
+
+ /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
+ * idx is bounded by n_window
+ */
+ int rd_ptr = txq->q.read_ptr;
+ int idx = get_cmd_index(&txq->q, rd_ptr);
+
+ lockdep_assert_held(&txq->lock);
+
+ /* We have only q->n_window txq->entries, but we use
+ * TFD_QUEUE_SIZE_MAX tfds
+ */
+ iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]);
+
+ /* free SKB */
+ if (txq->entries) {
+ struct sk_buff *skb;
+
+ skb = txq->entries[idx].skb;
+
+ /* Can be called from irqs-disabled context
+ * If skb is not NULL, it means that the whole queue is being
+ * freed and that the queue is not empty - free the skb
+ */
+ if (skb) {
+ iwl_op_mode_free_skb(trans->op_mode, skb);
+ txq->entries[idx].skb = NULL;
+ }
+ }
+}
+
+static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
+ dma_addr_t addr, u16 len, bool reset)
+{
+ struct iwl_queue *q;
+ struct iwl_tfd *tfd, *tfd_tmp;
+ u32 num_tbs;
+
+ q = &txq->q;
+ tfd_tmp = txq->tfds;
+ tfd = &tfd_tmp[q->write_ptr];
+
+ if (reset)
+ memset(tfd, 0, sizeof(*tfd));
+
+ num_tbs = iwl_pcie_tfd_get_num_tbs(tfd);
+
+ /* Each TFD can point to a maximum 20 Tx buffers */
+ if (num_tbs >= IWL_NUM_OF_TBS) {
+ IWL_ERR(trans, "Error can not send more than %d chunks\n",
+ IWL_NUM_OF_TBS);
+ return -EINVAL;
+ }
+
+ if (WARN(addr & ~IWL_TX_DMA_MASK,
+ "Unaligned address = %llx\n", (unsigned long long)addr))
+ return -EINVAL;
+
+ iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len);
+
+ return num_tbs;
+}
+
+static int iwl_pcie_txq_alloc(struct iwl_trans *trans,
+ struct iwl_txq *txq, int slots_num,
+ u32 txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX;
+ size_t scratchbuf_sz;
+ int i;
+
+ if (WARN_ON(txq->entries || txq->tfds))
+ return -EINVAL;
+
+ setup_timer(&txq->stuck_timer, iwl_pcie_txq_stuck_timer,
+ (unsigned long)txq);
+ txq->trans_pcie = trans_pcie;
+
+ txq->q.n_window = slots_num;
+
+ txq->entries = kcalloc(slots_num,
+ sizeof(struct iwl_pcie_txq_entry),
+ GFP_KERNEL);
+
+ if (!txq->entries)
+ goto error;
+
+ if (txq_id == trans_pcie->cmd_queue)
+ for (i = 0; i < slots_num; i++) {
+ txq->entries[i].cmd =
+ kmalloc(sizeof(struct iwl_device_cmd),
+ GFP_KERNEL);
+ if (!txq->entries[i].cmd)
+ goto error;
+ }
+
+ /* Circular buffer of transmit frame descriptors (TFDs),
+ * shared with device */
+ txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz,
+ &txq->q.dma_addr, GFP_KERNEL);
+ if (!txq->tfds)
+ goto error;
+
+ BUILD_BUG_ON(IWL_HCMD_SCRATCHBUF_SIZE != sizeof(*txq->scratchbufs));
+ BUILD_BUG_ON(offsetof(struct iwl_pcie_txq_scratch_buf, scratch) !=
+ sizeof(struct iwl_cmd_header) +
+ offsetof(struct iwl_tx_cmd, scratch));
+
+ scratchbuf_sz = sizeof(*txq->scratchbufs) * slots_num;
+
+ txq->scratchbufs = dma_alloc_coherent(trans->dev, scratchbuf_sz,
+ &txq->scratchbufs_dma,
+ GFP_KERNEL);
+ if (!txq->scratchbufs)
+ goto err_free_tfds;
+
+ txq->q.id = txq_id;
+
+ return 0;
+err_free_tfds:
+ dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->q.dma_addr);
+error:
+ if (txq->entries && txq_id == trans_pcie->cmd_queue)
+ for (i = 0; i < slots_num; i++)
+ kfree(txq->entries[i].cmd);
+ kfree(txq->entries);
+ txq->entries = NULL;
+
+ return -ENOMEM;
+
+}
+
+static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
+ int slots_num, u32 txq_id)
+{
+ int ret;
+
+ txq->need_update = false;
+
+ /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
+ * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
+ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
+
+ /* Initialize queue's high/low-water marks, and head/tail indexes */
+ ret = iwl_queue_init(&txq->q, slots_num, txq_id);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&txq->lock);
+
+ /*
+ * Tell nic where to find circular buffer of Tx Frame Descriptors for
+ * given Tx queue, and enable the DMA channel used for that queue.
+ * Circular buffer (TFD queue in DRAM) physical base address */
+ iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id),
+ txq->q.dma_addr >> 8);
+
+ return 0;
+}
+
+static void iwl_pcie_free_tso_page(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA]) {
+ struct page *page =
+ info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA];
+
+ __free_page(page);
+ info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = NULL;
+ }
+}
+
+/*
+ * iwl_pcie_txq_unmap - Unmap any remaining DMA mappings and free skb's
+ */
+static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+ struct iwl_queue *q = &txq->q;
+
+ 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);
+
+ if (txq_id != trans_pcie->cmd_queue) {
+ struct sk_buff *skb = txq->entries[q->read_ptr].skb;
+
+ if (WARN_ON_ONCE(!skb))
+ continue;
+
+ iwl_pcie_free_tso_page(skb);
+ }
+ iwl_pcie_txq_free_tfd(trans, txq);
+ q->read_ptr = iwl_queue_inc_wrap(q->read_ptr);
+ }
+ txq->active = false;
+ spin_unlock_bh(&txq->lock);
+
+ /* just in case - this queue may have been stopped */
+ iwl_wake_queue(trans, txq);
+}
+
+/*
+ * iwl_pcie_txq_free - Deallocate DMA queue.
+ * @txq: Transmit queue to deallocate.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ * 0-fill, but do not free "txq" descriptor structure.
+ */
+static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+ struct device *dev = trans->dev;
+ int i;
+
+ if (WARN_ON(!txq))
+ return;
+
+ iwl_pcie_txq_unmap(trans, txq_id);
+
+ /* De-alloc array of command/tx buffers */
+ if (txq_id == trans_pcie->cmd_queue)
+ for (i = 0; i < txq->q.n_window; i++) {
+ kzfree(txq->entries[i].cmd);
+ kzfree(txq->entries[i].free_buf);
+ }
+
+ /* De-alloc circular buffer of TFDs */
+ if (txq->tfds) {
+ dma_free_coherent(dev,
+ sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX,
+ txq->tfds, txq->q.dma_addr);
+ txq->q.dma_addr = 0;
+ txq->tfds = NULL;
+
+ dma_free_coherent(dev,
+ sizeof(*txq->scratchbufs) * txq->q.n_window,
+ txq->scratchbufs, txq->scratchbufs_dma);
+ }
+
+ kfree(txq->entries);
+ txq->entries = NULL;
+
+ del_timer_sync(&txq->stuck_timer);
+
+ /* 0-fill queue descriptor structure */
+ memset(txq, 0, sizeof(*txq));
+}
+
+void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int nq = trans->cfg->base_params->num_of_queues;
+ int chan;
+ u32 reg_val;
+ int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) -
+ SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(u32);
+
+ /* make sure all queue are not stopped/used */
+ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
+ memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
+
+ trans_pcie->scd_base_addr =
+ iwl_read_prph(trans, SCD_SRAM_BASE_ADDR);
+
+ WARN_ON(scd_base_addr != 0 &&
+ scd_base_addr != trans_pcie->scd_base_addr);
+
+ /* reset context data, TX status and translation data */
+ iwl_trans_write_mem(trans, trans_pcie->scd_base_addr +
+ SCD_CONTEXT_MEM_LOWER_BOUND,
+ NULL, clear_dwords);
+
+ iwl_write_prph(trans, SCD_DRAM_BASE_ADDR,
+ trans_pcie->scd_bc_tbls.dma >> 10);
+
+ /* The chain extension of the SCD doesn't work well. This feature is
+ * enabled by default by the HW, so we need to disable it manually.
+ */
+ if (trans->cfg->base_params->scd_chain_ext_wa)
+ iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
+
+ iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue,
+ trans_pcie->cmd_fifo,
+ trans_pcie->cmd_q_wdg_timeout);
+
+ /* Activate all Tx DMA/FIFO channels */
+ iwl_scd_activate_fifos(trans);
+
+ /* Enable DMA channel */
+ for (chan = 0; chan < FH_TCSR_CHNL_NUM; chan++)
+ iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
+
+ /* Update FH chicken bits */
+ reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG);
+ iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG,
+ reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
+
+ /* Enable L1-Active */
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+}
+
+void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int txq_id;
+
+ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
+ txq_id++) {
+ struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+
+ iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id),
+ txq->q.dma_addr >> 8);
+ iwl_pcie_txq_unmap(trans, txq_id);
+ txq->q.read_ptr = 0;
+ txq->q.write_ptr = 0;
+ }
+
+ /* Tell NIC where to find the "keep warm" buffer */
+ iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
+ trans_pcie->kw.dma >> 4);
+
+ /*
+ * Send 0 as the scd_base_addr since the device may have be reset
+ * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will
+ * contain garbage.
+ */
+ iwl_pcie_tx_start(trans, 0);
+}
+
+static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ unsigned long flags;
+ int ch, ret;
+ u32 mask = 0;
+
+ spin_lock(&trans_pcie->irq_lock);
+
+ if (!iwl_trans_grab_nic_access(trans, &flags))
+ goto out;
+
+ /* Stop each Tx DMA channel */
+ for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) {
+ iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
+ mask |= FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch);
+ }
+
+ /* Wait for DMA channels to be idle */
+ ret = iwl_poll_bit(trans, FH_TSSR_TX_STATUS_REG, mask, mask, 5000);
+ if (ret < 0)
+ IWL_ERR(trans,
+ "Failing on timeout while stopping DMA channel %d [0x%08x]\n",
+ ch, iwl_read32(trans, FH_TSSR_TX_STATUS_REG));
+
+ iwl_trans_release_nic_access(trans, &flags);
+
+out:
+ spin_unlock(&trans_pcie->irq_lock);
+}
+
+/*
+ * iwl_pcie_tx_stop - Stop all Tx DMA channels
+ */
+int iwl_pcie_tx_stop(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int txq_id;
+
+ /* Turn off all Tx DMA fifos */
+ iwl_scd_deactivate_fifos(trans);
+
+ /* Turn off all Tx DMA channels */
+ iwl_pcie_tx_stop_fh(trans);
+
+ /*
+ * This function can be called before the op_mode disabled the
+ * queues. This happens when we have an rfkill interrupt.
+ * Since we stop Tx altogether - mark the queues as stopped.
+ */
+ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
+ memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
+
+ /* This can happen: start_hw, stop_device */
+ if (!trans_pcie->txq)
+ return 0;
+
+ /* Unmap DMA from host system and free skb's */
+ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
+ txq_id++)
+ iwl_pcie_txq_unmap(trans, txq_id);
+
+ return 0;
+}
+
+/*
+ * iwl_trans_tx_free - Free TXQ Context
+ *
+ * Destroy all TX DMA queues and structures
+ */
+void iwl_pcie_tx_free(struct iwl_trans *trans)
+{
+ int txq_id;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ /* Tx queues */
+ if (trans_pcie->txq) {
+ for (txq_id = 0;
+ txq_id < trans->cfg->base_params->num_of_queues; txq_id++)
+ iwl_pcie_txq_free(trans, txq_id);
+ }
+
+ kfree(trans_pcie->txq);
+ trans_pcie->txq = NULL;
+
+ iwl_pcie_free_dma_ptr(trans, &trans_pcie->kw);
+
+ iwl_pcie_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls);
+}
+
+/*
+ * iwl_pcie_tx_alloc - allocate TX context
+ * Allocate all Tx DMA structures and initialize them
+ */
+static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
+{
+ int ret;
+ int txq_id, slots_num;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues *
+ sizeof(struct iwlagn_scd_bc_tbl);
+
+ /*It is not allowed to alloc twice, so warn when this happens.
+ * We cannot rely on the previous allocation, so free and fail */
+ if (WARN_ON(trans_pcie->txq)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls,
+ scd_bc_tbls_size);
+ if (ret) {
+ IWL_ERR(trans, "Scheduler BC Table allocation failed\n");
+ goto error;
+ }
+
+ /* Alloc keep-warm buffer */
+ ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE);
+ if (ret) {
+ IWL_ERR(trans, "Keep Warm allocation failed\n");
+ goto error;
+ }
+
+ trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues,
+ sizeof(struct iwl_txq), GFP_KERNEL);
+ if (!trans_pcie->txq) {
+ IWL_ERR(trans, "Not enough memory for txq\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Alloc and init all Tx queues, including the command queue (#4/#9) */
+ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
+ txq_id++) {
+ slots_num = (txq_id == trans_pcie->cmd_queue) ?
+ TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
+ ret = iwl_pcie_txq_alloc(trans, &trans_pcie->txq[txq_id],
+ slots_num, txq_id);
+ if (ret) {
+ IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ iwl_pcie_tx_free(trans);
+
+ return ret;
+}
+int iwl_pcie_tx_init(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int ret;
+ int txq_id, slots_num;
+ bool alloc = false;
+
+ if (!trans_pcie->txq) {
+ ret = iwl_pcie_tx_alloc(trans);
+ if (ret)
+ goto error;
+ alloc = true;
+ }
+
+ spin_lock(&trans_pcie->irq_lock);
+
+ /* Turn off all Tx DMA fifos */
+ iwl_scd_deactivate_fifos(trans);
+
+ /* Tell NIC where to find the "keep warm" buffer */
+ iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
+ trans_pcie->kw.dma >> 4);
+
+ spin_unlock(&trans_pcie->irq_lock);
+
+ /* Alloc and init all Tx queues, including the command queue (#4/#9) */
+ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
+ txq_id++) {
+ slots_num = (txq_id == trans_pcie->cmd_queue) ?
+ TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
+ ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id],
+ slots_num, txq_id);
+ if (ret) {
+ IWL_ERR(trans, "Tx %d queue init failed\n", txq_id);
+ goto error;
+ }
+ }
+
+ iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE);
+ if (trans->cfg->base_params->num_of_queues > 20)
+ iwl_set_bits_prph(trans, SCD_GP_CTRL,
+ SCD_GP_CTRL_ENABLE_31_QUEUES);
+
+ return 0;
+error:
+ /*Upon error, free only if we allocated something */
+ if (alloc)
+ iwl_pcie_tx_free(trans);
+ return ret;
+}
+
+static inline void iwl_pcie_txq_progress(struct iwl_txq *txq)
+{
+ lockdep_assert_held(&txq->lock);
+
+ if (!txq->wd_timeout)
+ return;
+
+ /*
+ * station is asleep and we send data - that must
+ * be uAPSD or PS-Poll. Don't rearm the timer.
+ */
+ if (txq->frozen)
+ return;
+
+ /*
+ * if empty delete timer, otherwise move timer forward
+ * since we're making progress on this queue
+ */
+ if (txq->q.read_ptr == txq->q.write_ptr)
+ del_timer(&txq->stuck_timer);
+ else
+ mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
+}
+
+/* Frees buffers until index _not_ inclusive */
+void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
+ struct sk_buff_head *skbs)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+ int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1);
+ struct iwl_queue *q = &txq->q;
+ int last_to_free;
+
+ /* This function is not meant to release cmd queue*/
+ if (WARN_ON(txq_id == trans_pcie->cmd_queue))
+ return;
+
+ 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;
+
+ IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n",
+ txq_id, txq->q.read_ptr, tfd_num, ssn);
+
+ /*Since we free until index _not_ inclusive, the one before index is
+ * the last we will free. This one must be used */
+ last_to_free = iwl_queue_dec_wrap(tfd_num);
+
+ if (!iwl_queue_used(q, last_to_free)) {
+ IWL_ERR(trans,
+ "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
+ __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX,
+ q->write_ptr, q->read_ptr);
+ goto out;
+ }
+
+ if (WARN_ON(!skb_queue_empty(skbs)))
+ goto out;
+
+ for (;
+ q->read_ptr != tfd_num;
+ q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
+ struct sk_buff *skb = txq->entries[txq->q.read_ptr].skb;
+
+ if (WARN_ON_ONCE(!skb))
+ continue;
+
+ iwl_pcie_free_tso_page(skb);
+
+ __skb_queue_tail(skbs, skb);
+
+ txq->entries[txq->q.read_ptr].skb = NULL;
+
+ iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq);
+
+ iwl_pcie_txq_free_tfd(trans, txq);
+ }
+
+ iwl_pcie_txq_progress(txq);
+
+ if (iwl_queue_space(&txq->q) > txq->q.low_mark)
+ iwl_wake_queue(trans, txq);
+
+ if (q->read_ptr == q->write_ptr) {
+ IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id);
+ iwl_trans_pcie_unref(trans);
+ }
+
+out:
+ spin_unlock_bh(&txq->lock);
+}
+
+static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
+ const struct iwl_host_cmd *cmd)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int ret;
+
+ lockdep_assert_held(&trans_pcie->reg_lock);
+
+ if (!(cmd->flags & CMD_SEND_IN_IDLE) &&
+ !trans_pcie->ref_cmd_in_flight) {
+ trans_pcie->ref_cmd_in_flight = true;
+ IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n");
+ iwl_trans_pcie_ref(trans);
+ }
+
+ /*
+ * wake up the NIC to make sure that the firmware will see the host
+ * command - we will let the NIC sleep once all the host commands
+ * returned. This needs to be done only on NICs that have
+ * apmg_wake_up_wa set.
+ */
+ if (trans->cfg->base_params->apmg_wake_up_wa &&
+ !trans_pcie->cmd_hold_nic_awake) {
+ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+ (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+ CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
+ 15000);
+ if (ret < 0) {
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
+ return -EIO;
+ }
+ trans_pcie->cmd_hold_nic_awake = true;
+ }
+
+ return 0;
+}
+
+static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ lockdep_assert_held(&trans_pcie->reg_lock);
+
+ if (trans_pcie->ref_cmd_in_flight) {
+ trans_pcie->ref_cmd_in_flight = false;
+ IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n");
+ iwl_trans_pcie_unref(trans);
+ }
+
+ if (trans->cfg->base_params->apmg_wake_up_wa) {
+ if (WARN_ON(!trans_pcie->cmd_hold_nic_awake))
+ return 0;
+
+ trans_pcie->cmd_hold_nic_awake = false;
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ }
+ return 0;
+}
+
+/*
+ * iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd
+ *
+ * When FW advances 'R' index, all entries between old and new 'R' index
+ * need to be reclaimed. As result, some free space forms. If there is
+ * enough free space (> low mark), wake the stack that feeds us.
+ */
+static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+ struct iwl_queue *q = &txq->q;
+ unsigned long flags;
+ int nfreed = 0;
+
+ lockdep_assert_held(&txq->lock);
+
+ if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) {
+ IWL_ERR(trans,
+ "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
+ __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX,
+ q->write_ptr, q->read_ptr);
+ return;
+ }
+
+ for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx;
+ q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
+
+ if (nfreed++ > 0) {
+ IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
+ idx, q->write_ptr, q->read_ptr);
+ iwl_force_nmi(trans);
+ }
+ }
+
+ if (q->read_ptr == q->write_ptr) {
+ spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+ iwl_pcie_clear_cmd_in_flight(trans);
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+ }
+
+ iwl_pcie_txq_progress(txq);
+}
+
+static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid,
+ u16 txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 tbl_dw_addr;
+ u32 tbl_dw;
+ u16 scd_q2ratid;
+
+ scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK;
+
+ tbl_dw_addr = trans_pcie->scd_base_addr +
+ SCD_TRANS_TBL_OFFSET_QUEUE(txq_id);
+
+ tbl_dw = iwl_trans_read_mem32(trans, tbl_dw_addr);
+
+ if (txq_id & 0x1)
+ tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
+ else
+ tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
+
+ iwl_trans_write_mem32(trans, tbl_dw_addr, tbl_dw);
+
+ return 0;
+}
+
+/* Receiver address (actually, Rx station's index into station table),
+ * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
+#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid))
+
+void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn,
+ const struct iwl_trans_txq_scd_cfg *cfg,
+ unsigned int wdg_timeout)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+ int fifo = -1;
+
+ if (test_and_set_bit(txq_id, trans_pcie->queue_used))
+ WARN_ONCE(1, "queue %d already used - expect issues", txq_id);
+
+ txq->wd_timeout = msecs_to_jiffies(wdg_timeout);
+
+ if (cfg) {
+ fifo = cfg->fifo;
+
+ /* Disable the scheduler prior configuring the cmd queue */
+ if (txq_id == trans_pcie->cmd_queue &&
+ trans_pcie->scd_set_active)
+ iwl_scd_enable_set_active(trans, 0);
+
+ /* Stop this Tx queue before configuring it */
+ iwl_scd_txq_set_inactive(trans, txq_id);
+
+ /* Set this queue as a chain-building queue unless it is CMD */
+ if (txq_id != trans_pcie->cmd_queue)
+ iwl_scd_txq_set_chain(trans, txq_id);
+
+ if (cfg->aggregate) {
+ u16 ra_tid = BUILD_RAxTID(cfg->sta_id, cfg->tid);
+
+ /* Map receiver-address / traffic-ID to this queue */
+ iwl_pcie_txq_set_ratid_map(trans, ra_tid, txq_id);
+
+ /* enable aggregations for the queue */
+ iwl_scd_txq_enable_agg(trans, txq_id);
+ txq->ampdu = true;
+ } else {
+ /*
+ * disable aggregations for the queue, this will also
+ * make the ra_tid mapping configuration irrelevant
+ * since it is now a non-AGG queue.
+ */
+ iwl_scd_txq_disable_agg(trans, txq_id);
+
+ ssn = txq->q.read_ptr;
+ }
+ }
+
+ /* Place first TFD at index corresponding to start sequence number.
+ * Assumes that ssn_idx is valid (!= 0xFFF) */
+ txq->q.read_ptr = (ssn & 0xff);
+ txq->q.write_ptr = (ssn & 0xff);
+ iwl_write_direct32(trans, HBUS_TARG_WRPTR,
+ (ssn & 0xff) | (txq_id << 8));
+
+ if (cfg) {
+ u8 frame_limit = cfg->frame_limit;
+
+ iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn);
+
+ /* Set up Tx window size and frame limit for this queue */
+ iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr +
+ SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0);
+ iwl_trans_write_mem32(trans,
+ trans_pcie->scd_base_addr +
+ SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
+ ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
+ SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
+ ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
+
+ /* Set up status area in SRAM, map to Tx DMA/FIFO, activate */
+ iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
+ (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
+ (cfg->fifo << SCD_QUEUE_STTS_REG_POS_TXF) |
+ (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
+ SCD_QUEUE_STTS_REG_MSK);
+
+ /* enable the scheduler for this queue (only) */
+ if (txq_id == trans_pcie->cmd_queue &&
+ trans_pcie->scd_set_active)
+ iwl_scd_enable_set_active(trans, BIT(txq_id));
+
+ IWL_DEBUG_TX_QUEUES(trans,
+ "Activate queue %d on FIFO %d WrPtr: %d\n",
+ txq_id, fifo, ssn & 0xff);
+ } else {
+ IWL_DEBUG_TX_QUEUES(trans,
+ "Activate queue %d WrPtr: %d\n",
+ txq_id, ssn & 0xff);
+ }
+
+ txq->active = true;
+}
+
+void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id,
+ bool configure_scd)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 stts_addr = trans_pcie->scd_base_addr +
+ SCD_TX_STTS_QUEUE_OFFSET(txq_id);
+ static const u32 zero_val[4] = {};
+
+ trans_pcie->txq[txq_id].frozen_expiry_remainder = 0;
+ trans_pcie->txq[txq_id].frozen = false;
+
+ /*
+ * Upon HW Rfkill - we stop the device, and then stop the queues
+ * in the op_mode. Just for the sake of the simplicity of the op_mode,
+ * allow the op_mode to call txq_disable after it already called
+ * stop_device.
+ */
+ if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) {
+ WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status),
+ "queue %d not used", txq_id);
+ return;
+ }
+
+ if (configure_scd) {
+ iwl_scd_txq_set_inactive(trans, txq_id);
+
+ iwl_trans_write_mem(trans, stts_addr, (void *)zero_val,
+ 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);
+}
+
+/*************** HOST COMMAND QUEUE FUNCTIONS *****/
+
+/*
+ * iwl_pcie_enqueue_hcmd - enqueue a uCode command
+ * @priv: device private data point
+ * @cmd: a pointer to the ucode command structure
+ *
+ * The function returns < 0 values to indicate the operation
+ * failed. On success, it returns the index (>= 0) of command in the
+ * command queue.
+ */
+static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
+ struct iwl_host_cmd *cmd)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
+ struct iwl_queue *q = &txq->q;
+ struct iwl_device_cmd *out_cmd;
+ struct iwl_cmd_meta *out_meta;
+ unsigned long flags;
+ void *dup_buf = NULL;
+ dma_addr_t phys_addr;
+ int idx;
+ u16 copy_size, cmd_size, scratch_size;
+ bool had_nocopy = false;
+ u8 group_id = iwl_cmd_groupid(cmd->id);
+ int i, ret;
+ u32 cmd_pos;
+ const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD];
+ u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];
+
+ if (WARN(!trans_pcie->wide_cmd_header &&
+ group_id > IWL_ALWAYS_LONG_GROUP,
+ "unsupported wide command %#x\n", cmd->id))
+ return -EINVAL;
+
+ if (group_id != 0) {
+ copy_size = sizeof(struct iwl_cmd_header_wide);
+ cmd_size = sizeof(struct iwl_cmd_header_wide);
+ } else {
+ copy_size = sizeof(struct iwl_cmd_header);
+ cmd_size = sizeof(struct iwl_cmd_header);
+ }
+
+ /* need one for the header if the first is NOCOPY */
+ BUILD_BUG_ON(IWL_MAX_CMD_TBS_PER_TFD > IWL_NUM_OF_TBS - 1);
+
+ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
+ cmddata[i] = cmd->data[i];
+ cmdlen[i] = cmd->len[i];
+
+ if (!cmd->len[i])
+ continue;
+
+ /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */
+ if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
+ int copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
+
+ if (copy > cmdlen[i])
+ copy = cmdlen[i];
+ cmdlen[i] -= copy;
+ cmddata[i] += copy;
+ copy_size += copy;
+ }
+
+ if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) {
+ had_nocopy = true;
+ if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) {
+ idx = -EINVAL;
+ goto free_dup_buf;
+ }
+ } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) {
+ /*
+ * This is also a chunk that isn't copied
+ * to the static buffer so set had_nocopy.
+ */
+ had_nocopy = true;
+
+ /* only allowed once */
+ if (WARN_ON(dup_buf)) {
+ idx = -EINVAL;
+ goto free_dup_buf;
+ }
+
+ dup_buf = kmemdup(cmddata[i], cmdlen[i],
+ GFP_ATOMIC);
+ if (!dup_buf)
+ return -ENOMEM;
+ } else {
+ /* NOCOPY must not be followed by normal! */
+ if (WARN_ON(had_nocopy)) {
+ idx = -EINVAL;
+ goto free_dup_buf;
+ }
+ copy_size += cmdlen[i];
+ }
+ cmd_size += cmd->len[i];
+ }
+
+ /*
+ * If any of the command structures end up being larger than
+ * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically
+ * allocated into separate TFDs, then we will need to
+ * increase the size of the buffers.
+ */
+ if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE,
+ "Command %s (%#x) is too large (%d bytes)\n",
+ iwl_get_cmd_string(trans, cmd->id),
+ cmd->id, copy_size)) {
+ idx = -EINVAL;
+ goto free_dup_buf;
+ }
+
+ spin_lock_bh(&txq->lock);
+
+ if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
+ spin_unlock_bh(&txq->lock);
+
+ IWL_ERR(trans, "No space in command queue\n");
+ iwl_op_mode_cmd_queue_full(trans->op_mode);
+ idx = -ENOSPC;
+ goto free_dup_buf;
+ }
+
+ idx = get_cmd_index(q, q->write_ptr);
+ out_cmd = txq->entries[idx].cmd;
+ out_meta = &txq->entries[idx].meta;
+
+ memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */
+ if (cmd->flags & CMD_WANT_SKB)
+ out_meta->source = cmd;
+
+ /* set up the header */
+ if (group_id != 0) {
+ out_cmd->hdr_wide.cmd = iwl_cmd_opcode(cmd->id);
+ out_cmd->hdr_wide.group_id = group_id;
+ out_cmd->hdr_wide.version = iwl_cmd_version(cmd->id);
+ out_cmd->hdr_wide.length =
+ cpu_to_le16(cmd_size -
+ sizeof(struct iwl_cmd_header_wide));
+ out_cmd->hdr_wide.reserved = 0;
+ out_cmd->hdr_wide.sequence =
+ cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) |
+ INDEX_TO_SEQ(q->write_ptr));
+
+ cmd_pos = sizeof(struct iwl_cmd_header_wide);
+ copy_size = sizeof(struct iwl_cmd_header_wide);
+ } else {
+ out_cmd->hdr.cmd = iwl_cmd_opcode(cmd->id);
+ out_cmd->hdr.sequence =
+ cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) |
+ INDEX_TO_SEQ(q->write_ptr));
+ out_cmd->hdr.group_id = 0;
+
+ cmd_pos = sizeof(struct iwl_cmd_header);
+ copy_size = sizeof(struct iwl_cmd_header);
+ }
+
+ /* and copy the data that needs to be copied */
+ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
+ int copy;
+
+ if (!cmd->len[i])
+ continue;
+
+ /* copy everything if not nocopy/dup */
+ if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
+ IWL_HCMD_DFL_DUP))) {
+ copy = cmd->len[i];
+
+ memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
+ cmd_pos += copy;
+ copy_size += copy;
+ continue;
+ }
+
+ /*
+ * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied
+ * in total (for the scratchbuf handling), but copy up to what
+ * we can fit into the payload for debug dump purposes.
+ */
+ copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]);
+
+ memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
+ cmd_pos += copy;
+
+ /* However, treat copy_size the proper way, we need it below */
+ if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
+ copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
+
+ if (copy > cmd->len[i])
+ copy = cmd->len[i];
+ copy_size += copy;
+ }
+ }
+
+ IWL_DEBUG_HC(trans,
+ "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n",
+ iwl_get_cmd_string(trans, cmd->id),
+ group_id, out_cmd->hdr.cmd,
+ le16_to_cpu(out_cmd->hdr.sequence),
+ cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue);
+
+ /* start the TFD with the scratchbuf */
+ scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE);
+ memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
+ iwl_pcie_txq_build_tfd(trans, txq,
+ iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
+ scratch_size, true);
+
+ /* map first command fragment, if any remains */
+ if (copy_size > scratch_size) {
+ phys_addr = dma_map_single(trans->dev,
+ ((u8 *)&out_cmd->hdr) + scratch_size,
+ copy_size - scratch_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(trans->dev, phys_addr)) {
+ iwl_pcie_tfd_unmap(trans, out_meta,
+ &txq->tfds[q->write_ptr]);
+ idx = -ENOMEM;
+ goto out;
+ }
+
+ iwl_pcie_txq_build_tfd(trans, txq, phys_addr,
+ copy_size - scratch_size, false);
+ }
+
+ /* map the remaining (adjusted) nocopy/dup fragments */
+ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
+ const void *data = cmddata[i];
+
+ if (!cmdlen[i])
+ continue;
+ if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
+ IWL_HCMD_DFL_DUP)))
+ continue;
+ if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP)
+ data = dup_buf;
+ phys_addr = dma_map_single(trans->dev, (void *)data,
+ cmdlen[i], DMA_TO_DEVICE);
+ if (dma_mapping_error(trans->dev, phys_addr)) {
+ iwl_pcie_tfd_unmap(trans, out_meta,
+ &txq->tfds[q->write_ptr]);
+ idx = -ENOMEM;
+ goto out;
+ }
+
+ iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false);
+ }
+
+ BUILD_BUG_ON(IWL_NUM_OF_TBS + CMD_TB_BITMAP_POS >
+ sizeof(out_meta->flags) * BITS_PER_BYTE);
+ out_meta->flags = cmd->flags;
+ if (WARN_ON_ONCE(txq->entries[idx].free_buf))
+ kzfree(txq->entries[idx].free_buf);
+ txq->entries[idx].free_buf = dup_buf;
+
+ trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide);
+
+ /* start timer if queue currently empty */
+ if (q->read_ptr == q->write_ptr && txq->wd_timeout)
+ mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
+
+ spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+ ret = iwl_pcie_set_cmd_in_flight(trans, cmd);
+ if (ret < 0) {
+ idx = ret;
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+ goto out;
+ }
+
+ /* Increment and update queue's write index */
+ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
+ iwl_pcie_txq_inc_wr_ptr(trans, txq);
+
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+
+ out:
+ spin_unlock_bh(&txq->lock);
+ free_dup_buf:
+ if (idx < 0)
+ kfree(dup_buf);
+ return idx;
+}
+
+/*
+ * iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them
+ * @rxb: Rx buffer to reclaim
+ */
+void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ u8 group_id = iwl_cmd_groupid(pkt->hdr.group_id);
+ u32 cmd_id;
+ int txq_id = SEQ_TO_QUEUE(sequence);
+ int index = SEQ_TO_INDEX(sequence);
+ int cmd_index;
+ struct iwl_device_cmd *cmd;
+ struct iwl_cmd_meta *meta;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
+
+ /* If a Tx command is being handled and it isn't in the actual
+ * command queue then there a command routing bug has been introduced
+ * in the queue management code. */
+ if (WARN(txq_id != trans_pcie->cmd_queue,
+ "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
+ txq_id, trans_pcie->cmd_queue, sequence,
+ trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr,
+ trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) {
+ iwl_print_hex_error(trans, pkt, 32);
+ return;
+ }
+
+ spin_lock_bh(&txq->lock);
+
+ cmd_index = get_cmd_index(&txq->q, index);
+ cmd = txq->entries[cmd_index].cmd;
+ meta = &txq->entries[cmd_index].meta;
+ cmd_id = iwl_cmd_id(cmd->hdr.cmd, group_id, 0);
+
+ iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]);
+
+ /* Input error checking is done when commands are added to queue. */
+ if (meta->flags & CMD_WANT_SKB) {
+ struct page *p = rxb_steal_page(rxb);
+
+ meta->source->resp_pkt = pkt;
+ meta->source->_rx_page_addr = (unsigned long)page_address(p);
+ meta->source->_rx_page_order = trans_pcie->rx_page_order;
+ }
+
+ if (meta->flags & CMD_WANT_ASYNC_CALLBACK)
+ iwl_op_mode_async_cb(trans->op_mode, cmd);
+
+ iwl_pcie_cmdq_reclaim(trans, txq_id, index);
+
+ if (!(meta->flags & CMD_ASYNC)) {
+ if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) {
+ IWL_WARN(trans,
+ "HCMD_ACTIVE already clear for command %s\n",
+ iwl_get_cmd_string(trans, cmd_id));
+ }
+ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+ IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
+ iwl_get_cmd_string(trans, cmd_id));
+ wake_up(&trans_pcie->wait_command_queue);
+ }
+
+ meta->flags = 0;
+
+ spin_unlock_bh(&txq->lock);
+}
+
+#define HOST_COMPLETE_TIMEOUT (2 * HZ)
+
+static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
+ struct iwl_host_cmd *cmd)
+{
+ int ret;
+
+ /* An asynchronous command can not expect an SKB to be set. */
+ if (WARN_ON(cmd->flags & CMD_WANT_SKB))
+ return -EINVAL;
+
+ ret = iwl_pcie_enqueue_hcmd(trans, cmd);
+ if (ret < 0) {
+ IWL_ERR(trans,
+ "Error sending %s: enqueue_hcmd failed: %d\n",
+ iwl_get_cmd_string(trans, cmd->id), ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
+ struct iwl_host_cmd *cmd)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int cmd_idx;
+ int ret;
+
+ IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
+ iwl_get_cmd_string(trans, cmd->id));
+
+ if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE,
+ &trans->status),
+ "Command %s: a command is already active!\n",
+ iwl_get_cmd_string(trans, cmd->id)))
+ return -EIO;
+
+ IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
+ iwl_get_cmd_string(trans, cmd->id));
+
+ cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
+ if (cmd_idx < 0) {
+ ret = cmd_idx;
+ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+ IWL_ERR(trans,
+ "Error sending %s: enqueue_hcmd failed: %d\n",
+ iwl_get_cmd_string(trans, cmd->id), ret);
+ return ret;
+ }
+
+ ret = wait_event_timeout(trans_pcie->wait_command_queue,
+ !test_bit(STATUS_SYNC_HCMD_ACTIVE,
+ &trans->status),
+ HOST_COMPLETE_TIMEOUT);
+ if (!ret) {
+ struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
+ struct iwl_queue *q = &txq->q;
+
+ IWL_ERR(trans, "Error sending %s: time out after %dms.\n",
+ iwl_get_cmd_string(trans, cmd->id),
+ jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
+
+ IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n",
+ q->read_ptr, q->write_ptr);
+
+ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+ IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
+ iwl_get_cmd_string(trans, cmd->id));
+ ret = -ETIMEDOUT;
+
+ iwl_force_nmi(trans);
+ iwl_trans_fw_error(trans);
+
+ goto cancel;
+ }
+
+ if (test_bit(STATUS_FW_ERROR, &trans->status)) {
+ IWL_ERR(trans, "FW error in SYNC CMD %s\n",
+ iwl_get_cmd_string(trans, cmd->id));
+ dump_stack();
+ ret = -EIO;
+ goto cancel;
+ }
+
+ if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+ test_bit(STATUS_RFKILL, &trans->status)) {
+ IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n");
+ ret = -ERFKILL;
+ goto cancel;
+ }
+
+ if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) {
+ IWL_ERR(trans, "Error: Response NULL in '%s'\n",
+ iwl_get_cmd_string(trans, cmd->id));
+ ret = -EIO;
+ goto cancel;
+ }
+
+ return 0;
+
+cancel:
+ if (cmd->flags & CMD_WANT_SKB) {
+ /*
+ * Cancel the CMD_WANT_SKB flag for the cmd in the
+ * TX cmd queue. Otherwise in case the cmd comes
+ * in later, it will possibly set an invalid
+ * address (cmd->meta.source).
+ */
+ trans_pcie->txq[trans_pcie->cmd_queue].
+ entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB;
+ }
+
+ if (cmd->resp_pkt) {
+ iwl_free_resp(cmd);
+ cmd->resp_pkt = NULL;
+ }
+
+ return ret;
+}
+
+int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
+{
+ if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+ test_bit(STATUS_RFKILL, &trans->status)) {
+ IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
+ cmd->id);
+ return -ERFKILL;
+ }
+
+ if (cmd->flags & CMD_ASYNC)
+ return iwl_pcie_send_hcmd_async(trans, cmd);
+
+ /* We still can fail on RFKILL that can be asserted while we wait */
+ return iwl_pcie_send_hcmd_sync(trans, cmd);
+}
+
+static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_txq *txq, u8 hdr_len,
+ struct iwl_cmd_meta *out_meta,
+ struct iwl_device_cmd *dev_cmd, u16 tb1_len)
+{
+ struct iwl_queue *q = &txq->q;
+ u16 tb2_len;
+ int i;
+
+ /*
+ * Set up TFD's third entry to point directly to remainder
+ * of skb's head, if any
+ */
+ tb2_len = skb_headlen(skb) - hdr_len;
+
+ if (tb2_len > 0) {
+ dma_addr_t tb2_phys = dma_map_single(trans->dev,
+ skb->data + hdr_len,
+ tb2_len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) {
+ iwl_pcie_tfd_unmap(trans, out_meta,
+ &txq->tfds[q->write_ptr]);
+ return -EINVAL;
+ }
+ iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
+ }
+
+ /* set up the remaining entries to point to the data */
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ dma_addr_t tb_phys;
+ int tb_idx;
+
+ if (!skb_frag_size(frag))
+ continue;
+
+ tb_phys = skb_frag_dma_map(trans->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+
+ if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
+ iwl_pcie_tfd_unmap(trans, out_meta,
+ &txq->tfds[q->write_ptr]);
+ return -EINVAL;
+ }
+ tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys,
+ skb_frag_size(frag), false);
+
+ out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS);
+ }
+
+ trace_iwlwifi_dev_tx(trans->dev, skb,
+ &txq->tfds[txq->q.write_ptr],
+ sizeof(struct iwl_tfd),
+ &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len,
+ skb->data + hdr_len, tb2_len);
+ trace_iwlwifi_dev_tx_data(trans->dev, skb,
+ hdr_len, skb->len - hdr_len);
+ return 0;
+}
+
+#ifdef CONFIG_INET
+static struct iwl_tso_hdr_page *
+get_page_hdr(struct iwl_trans *trans, size_t len)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tso_hdr_page *p = this_cpu_ptr(trans_pcie->tso_hdr_page);
+
+ if (!p->page)
+ goto alloc;
+
+ /* enough room on this page */
+ if (p->pos + len < (u8 *)page_address(p->page) + PAGE_SIZE)
+ return p;
+
+ /* We don't have enough room on this page, get a new one. */
+ __free_page(p->page);
+
+alloc:
+ p->page = alloc_page(GFP_ATOMIC);
+ if (!p->page)
+ return NULL;
+ p->pos = page_address(p->page);
+ return p;
+}
+
+static void iwl_compute_pseudo_hdr_csum(void *iph, struct tcphdr *tcph,
+ bool ipv6, unsigned int len)
+{
+ if (ipv6) {
+ struct ipv6hdr *iphv6 = iph;
+
+ tcph->check = ~csum_ipv6_magic(&iphv6->saddr, &iphv6->daddr,
+ len + tcph->doff * 4,
+ IPPROTO_TCP, 0);
+ } else {
+ struct iphdr *iphv4 = iph;
+
+ ip_send_check(iphv4);
+ tcph->check = ~csum_tcpudp_magic(iphv4->saddr, iphv4->daddr,
+ len + tcph->doff * 4,
+ IPPROTO_TCP, 0);
+ }
+}
+
+static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_txq *txq, u8 hdr_len,
+ struct iwl_cmd_meta *out_meta,
+ struct iwl_device_cmd *dev_cmd, u16 tb1_len)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
+ unsigned int mss = skb_shinfo(skb)->gso_size;
+ struct iwl_queue *q = &txq->q;
+ u16 length, iv_len, amsdu_pad;
+ u8 *start_hdr;
+ struct iwl_tso_hdr_page *hdr_page;
+ int ret;
+ struct tso_t tso;
+
+ /* if the packet is protected, then it must be CCMP or GCMP */
+ BUILD_BUG_ON(IEEE80211_CCMP_HDR_LEN != IEEE80211_GCMP_HDR_LEN);
+ iv_len = ieee80211_has_protected(hdr->frame_control) ?
+ IEEE80211_CCMP_HDR_LEN : 0;
+
+ trace_iwlwifi_dev_tx(trans->dev, skb,
+ &txq->tfds[txq->q.write_ptr],
+ sizeof(struct iwl_tfd),
+ &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len,
+ NULL, 0);
+
+ ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb);
+ snap_ip_tcp_hdrlen = 8 + ip_hdrlen + tcp_hdrlen(skb);
+ total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len - iv_len;
+ amsdu_pad = 0;
+
+ /* total amount of header we may need for this A-MSDU */
+ hdr_room = DIV_ROUND_UP(total_len, mss) *
+ (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len;
+
+ /* Our device supports 9 segments at most, it will fit in 1 page */
+ hdr_page = get_page_hdr(trans, hdr_room);
+ if (!hdr_page)
+ return -ENOMEM;
+
+ get_page(hdr_page->page);
+ start_hdr = hdr_page->pos;
+ info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = hdr_page->page;
+ memcpy(hdr_page->pos, skb->data + hdr_len, iv_len);
+ hdr_page->pos += iv_len;
+
+ /*
+ * Pull the ieee80211 header + IV to be able to use TSO core,
+ * we will restore it for the tx_status flow.
+ */
+ skb_pull(skb, hdr_len + iv_len);
+
+ tso_start(skb, &tso);
+
+ while (total_len) {
+ /* this is the data left for this subframe */
+ unsigned int data_left =
+ min_t(unsigned int, mss, total_len);
+ struct sk_buff *csum_skb = NULL;
+ unsigned int hdr_tb_len;
+ dma_addr_t hdr_tb_phys;
+ struct tcphdr *tcph;
+ u8 *iph;
+
+ total_len -= data_left;
+
+ memset(hdr_page->pos, 0, amsdu_pad);
+ hdr_page->pos += amsdu_pad;
+ amsdu_pad = (4 - (sizeof(struct ethhdr) + snap_ip_tcp_hdrlen +
+ data_left)) & 0x3;
+ ether_addr_copy(hdr_page->pos, ieee80211_get_DA(hdr));
+ hdr_page->pos += ETH_ALEN;
+ ether_addr_copy(hdr_page->pos, ieee80211_get_SA(hdr));
+ hdr_page->pos += ETH_ALEN;
+
+ length = snap_ip_tcp_hdrlen + data_left;
+ *((__be16 *)hdr_page->pos) = cpu_to_be16(length);
+ hdr_page->pos += sizeof(length);
+
+ /*
+ * This will copy the SNAP as well which will be considered
+ * as MAC header.
+ */
+ tso_build_hdr(skb, hdr_page->pos, &tso, data_left, !total_len);
+ iph = hdr_page->pos + 8;
+ tcph = (void *)(iph + ip_hdrlen);
+
+ /* For testing on current hardware only */
+ if (trans_pcie->sw_csum_tx) {
+ csum_skb = alloc_skb(data_left + tcp_hdrlen(skb),
+ GFP_ATOMIC);
+ if (!csum_skb) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ iwl_compute_pseudo_hdr_csum(iph, tcph,
+ skb->protocol ==
+ htons(ETH_P_IPV6),
+ data_left);
+
+ memcpy(skb_put(csum_skb, tcp_hdrlen(skb)),
+ tcph, tcp_hdrlen(skb));
+ skb_set_transport_header(csum_skb, 0);
+ csum_skb->csum_start =
+ (unsigned char *)tcp_hdr(csum_skb) -
+ csum_skb->head;
+ }
+
+ hdr_page->pos += snap_ip_tcp_hdrlen;
+
+ hdr_tb_len = hdr_page->pos - start_hdr;
+ hdr_tb_phys = dma_map_single(trans->dev, start_hdr,
+ hdr_tb_len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(trans->dev, hdr_tb_phys))) {
+ dev_kfree_skb(csum_skb);
+ ret = -EINVAL;
+ goto out_unmap;
+ }
+ iwl_pcie_txq_build_tfd(trans, txq, hdr_tb_phys,
+ hdr_tb_len, false);
+ trace_iwlwifi_dev_tx_tso_chunk(trans->dev, start_hdr,
+ hdr_tb_len);
+
+ /* prepare the start_hdr for the next subframe */
+ start_hdr = hdr_page->pos;
+
+ /* put the payload */
+ while (data_left) {
+ unsigned int size = min_t(unsigned int, tso.size,
+ data_left);
+ dma_addr_t tb_phys;
+
+ if (trans_pcie->sw_csum_tx)
+ memcpy(skb_put(csum_skb, size), tso.data, size);
+
+ tb_phys = dma_map_single(trans->dev, tso.data,
+ size, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
+ dev_kfree_skb(csum_skb);
+ ret = -EINVAL;
+ goto out_unmap;
+ }
+
+ iwl_pcie_txq_build_tfd(trans, txq, tb_phys,
+ size, false);
+ trace_iwlwifi_dev_tx_tso_chunk(trans->dev, tso.data,
+ size);
+
+ data_left -= size;
+ tso_build_data(skb, &tso, size);
+ }
+
+ /* For testing on early hardware only */
+ if (trans_pcie->sw_csum_tx) {
+ __wsum csum;
+
+ csum = skb_checksum(csum_skb,
+ skb_checksum_start_offset(csum_skb),
+ csum_skb->len -
+ skb_checksum_start_offset(csum_skb),
+ 0);
+ dev_kfree_skb(csum_skb);
+ dma_sync_single_for_cpu(trans->dev, hdr_tb_phys,
+ hdr_tb_len, DMA_TO_DEVICE);
+ tcph->check = csum_fold(csum);
+ dma_sync_single_for_device(trans->dev, hdr_tb_phys,
+ hdr_tb_len, DMA_TO_DEVICE);
+ }
+ }
+
+ /* re -add the WiFi header and IV */
+ skb_push(skb, hdr_len + iv_len);
+
+ return 0;
+
+out_unmap:
+ iwl_pcie_tfd_unmap(trans, out_meta, &txq->tfds[q->write_ptr]);
+ return ret;
+}
+#else /* CONFIG_INET */
+static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_txq *txq, u8 hdr_len,
+ struct iwl_cmd_meta *out_meta,
+ struct iwl_device_cmd *dev_cmd, u16 tb1_len)
+{
+ /* No A-MSDU without CONFIG_INET */
+ WARN_ON(1);
+
+ return -1;
+}
+#endif /* CONFIG_INET */
+
+int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_device_cmd *dev_cmd, int txq_id)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct ieee80211_hdr *hdr;
+ struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+ struct iwl_cmd_meta *out_meta;
+ struct iwl_txq *txq;
+ struct iwl_queue *q;
+ dma_addr_t tb0_phys, tb1_phys, scratch_phys;
+ void *tb1_addr;
+ u16 len, tb1_len;
+ bool wait_write_ptr;
+ __le16 fc;
+ u8 hdr_len;
+ u16 wifi_seq;
+
+ txq = &trans_pcie->txq[txq_id];
+ q = &txq->q;
+
+ if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used),
+ "TX on unused queue %d\n", txq_id))
+ return -EINVAL;
+
+ if (unlikely(trans_pcie->sw_csum_tx &&
+ skb->ip_summed == CHECKSUM_PARTIAL)) {
+ int offs = skb_checksum_start_offset(skb);
+ int csum_offs = offs + skb->csum_offset;
+ __wsum csum;
+
+ if (skb_ensure_writable(skb, csum_offs + sizeof(__sum16)))
+ return -1;
+
+ csum = skb_checksum(skb, offs, skb->len - offs, 0);
+ *(__sum16 *)(skb->data + csum_offs) = csum_fold(csum);
+ }
+
+ if (skb_is_nonlinear(skb) &&
+ skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS &&
+ __skb_linearize(skb))
+ return -ENOMEM;
+
+ /* mac80211 always puts the full header into the SKB's head,
+ * so there's no need to check if it's readable there
+ */
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = hdr->frame_control;
+ hdr_len = ieee80211_hdrlen(fc);
+
+ spin_lock(&txq->lock);
+
+ /* In AGG mode, the index in the ring must correspond to the WiFi
+ * sequence number. This is a HW requirements to help the SCD to parse
+ * the BA.
+ * Check here that the packets are in the right place on the ring.
+ */
+ wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+ WARN_ONCE(txq->ampdu &&
+ (wifi_seq & 0xff) != q->write_ptr,
+ "Q: %d WiFi Seq %d tfdNum %d",
+ txq_id, wifi_seq, q->write_ptr);
+
+ /* Set up driver data for this TFD */
+ txq->entries[q->write_ptr].skb = skb;
+ txq->entries[q->write_ptr].cmd = dev_cmd;
+
+ dev_cmd->hdr.sequence =
+ cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
+ INDEX_TO_SEQ(q->write_ptr)));
+
+ tb0_phys = iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr);
+ scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) +
+ offsetof(struct iwl_tx_cmd, scratch);
+
+ tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
+ tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
+
+ /* Set up first empty entry in queue's array of Tx/cmd buffers */
+ out_meta = &txq->entries[q->write_ptr].meta;
+ out_meta->flags = 0;
+
+ /*
+ * The second TB (tb1) points to the remainder of the TX command
+ * and the 802.11 header - dword aligned size
+ * (This calculation modifies the TX command, so do it before the
+ * setup of the first TB)
+ */
+ len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) +
+ hdr_len - IWL_HCMD_SCRATCHBUF_SIZE;
+ tb1_len = ALIGN(len, 4);
+
+ /* Tell NIC about any 2-byte padding after MAC header */
+ if (tb1_len != len)
+ tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
+
+ /* The first TB points to the scratchbuf data - min_copy bytes */
+ memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr,
+ IWL_HCMD_SCRATCHBUF_SIZE);
+ iwl_pcie_txq_build_tfd(trans, txq, tb0_phys,
+ IWL_HCMD_SCRATCHBUF_SIZE, true);
+
+ /* there must be data left over for TB1 or this code must be changed */
+ BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE);
+
+ /* map the data for TB1 */
+ tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_HCMD_SCRATCHBUF_SIZE;
+ tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(trans->dev, tb1_phys)))
+ goto out_err;
+ iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
+
+ if (ieee80211_is_data_qos(fc) &&
+ (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT)) {
+ if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len,
+ out_meta, dev_cmd,
+ tb1_len)))
+ goto out_err;
+ } else if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len,
+ out_meta, dev_cmd, tb1_len))) {
+ goto out_err;
+ }
+
+ /* Set up entry for this TFD in Tx byte-count array */
+ iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len));
+
+ wait_write_ptr = ieee80211_has_morefrags(fc);
+
+ /* start timer if queue currently empty */
+ if (q->read_ptr == q->write_ptr) {
+ if (txq->wd_timeout) {
+ /*
+ * If the TXQ is active, then set the timer, if not,
+ * set the timer in remainder so that the timer will
+ * be armed with the right value when the station will
+ * wake up.
+ */
+ if (!txq->frozen)
+ mod_timer(&txq->stuck_timer,
+ jiffies + txq->wd_timeout);
+ else
+ txq->frozen_expiry_remainder = txq->wd_timeout;
+ }
+ IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id);
+ iwl_trans_pcie_ref(trans);
+ }
+
+ /* Tell device the write index *just past* this latest filled TFD */
+ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
+ if (!wait_write_ptr)
+ iwl_pcie_txq_inc_wr_ptr(trans, txq);
+
+ /*
+ * At this point the frame is "transmitted" successfully
+ * and we will get a TX status notification eventually.
+ */
+ if (iwl_queue_space(q) < q->high_mark) {
+ if (wait_write_ptr)
+ iwl_pcie_txq_inc_wr_ptr(trans, txq);
+ else
+ iwl_stop_queue(trans, txq);
+ }
+ spin_unlock(&txq->lock);
+ return 0;
+out_err:
+ spin_unlock(&txq->lock);
+ return -1;
+}
diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig
new file mode 100644
index 000000000000..9da136049955
--- /dev/null
+++ b/drivers/net/wireless/intersil/Kconfig
@@ -0,0 +1,38 @@
+config WLAN_VENDOR_INTERSIL
+ bool "Intersil devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_INTERSIL
+
+source "drivers/net/wireless/intersil/hostap/Kconfig"
+source "drivers/net/wireless/intersil/orinoco/Kconfig"
+source "drivers/net/wireless/intersil/p54/Kconfig"
+
+config PRISM54
+ tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)'
+ depends on PCI
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
+ select FW_LOADER
+ ---help---
+ This enables support for FullMAC PCI/Cardbus prism54 devices. This
+ driver is now deprecated in favor for the SoftMAC driver, p54pci.
+ p54pci supports FullMAC PCI/Cardbus devices as well.
+
+ For more information refer to the p54 wiki:
+
+ http://wireless.kernel.org/en/users/Drivers/p54
+
+ Note: You need a motherboard with DMA support to use any of these cards
+
+ When built as module you get the module prism54
+
+endif # WLAN_VENDOR_INTERSIL
diff --git a/drivers/net/wireless/intersil/Makefile b/drivers/net/wireless/intersil/Makefile
new file mode 100644
index 000000000000..9a8cbfee3ea5
--- /dev/null
+++ b/drivers/net/wireless/intersil/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_HOSTAP) += hostap/
+obj-$(CONFIG_HERMES) += orinoco/
+obj-$(CONFIG_P54_COMMON) += p54/
+obj-$(CONFIG_PRISM54) += prism54/
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/intersil/hostap/Kconfig
index 287d82728bc3..287d82728bc3 100644
--- a/drivers/net/wireless/hostap/Kconfig
+++ b/drivers/net/wireless/intersil/hostap/Kconfig
diff --git a/drivers/net/wireless/hostap/Makefile b/drivers/net/wireless/intersil/hostap/Makefile
index b8e41a702c00..b8e41a702c00 100644
--- a/drivers/net/wireless/hostap/Makefile
+++ b/drivers/net/wireless/intersil/hostap/Makefile
diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/intersil/hostap/hostap.h
index ce8721fbc10e..ce8721fbc10e 100644
--- a/drivers/net/wireless/hostap/hostap.h
+++ b/drivers/net/wireless/intersil/hostap/hostap.h
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/intersil/hostap/hostap_80211.h
index ed98ce7c8f65..ed98ce7c8f65 100644
--- a/drivers/net/wireless/hostap/hostap_80211.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211.h
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c
index 599f30f22841..599f30f22841 100644
--- a/drivers/net/wireless/hostap/hostap_80211_rx.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_tx.c
index 055e11d353ca..055e11d353ca 100644
--- a/drivers/net/wireless/hostap/hostap_80211_tx.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211_tx.c
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/intersil/hostap/hostap_ap.c
index c995ace153ee..c995ace153ee 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_ap.c
diff --git a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/intersil/hostap/hostap_ap.h
index 334e2d0b8e11..334e2d0b8e11 100644
--- a/drivers/net/wireless/hostap/hostap_ap.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_ap.h
diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/intersil/hostap/hostap_common.h
index 4230102ac9e4..4230102ac9e4 100644
--- a/drivers/net/wireless/hostap/hostap_common.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_common.h
diff --git a/drivers/net/wireless/hostap/hostap_config.h b/drivers/net/wireless/intersil/hostap/hostap_config.h
index 2c8f71f0ed45..2c8f71f0ed45 100644
--- a/drivers/net/wireless/hostap/hostap_config.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_config.h
diff --git a/drivers/net/wireless/intersil/hostap/hostap_cs.c b/drivers/net/wireless/intersil/hostap/hostap_cs.c
new file mode 100644
index 000000000000..74f63b7bf7b4
--- /dev/null
+++ b/drivers/net/wireless/intersil/hostap/hostap_cs.c
@@ -0,0 +1,710 @@
+#define PRISM2_PCCARD
+
+#include <linux/module.h>
+#include <linux/if.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <asm/io.h>
+
+#include "hostap_wlan.h"
+
+
+static char *dev_info = "hostap_cs";
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
+ "cards (PC Card).");
+MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)");
+MODULE_LICENSE("GPL");
+
+
+static int ignore_cis_vcc;
+module_param(ignore_cis_vcc, int, 0444);
+MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
+
+
+/* struct local_info::hw_priv */
+struct hostap_cs_priv {
+ struct pcmcia_device *link;
+ int sandisk_connectplus;
+};
+
+
+#ifdef PRISM2_IO_DEBUG
+
+static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
+ outb(v, dev->base_addr + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u8 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ v = inb(dev->base_addr + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
+ outw(v, dev->base_addr + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u16 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ v = inw(dev->base_addr + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
+ u8 *buf, int wc)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
+ outsw(dev->base_addr + a, buf, wc);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline void hfa384x_insw_debug(struct net_device *dev, int a,
+ u8 *buf, int wc)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
+ insw(dev->base_addr + a, buf, wc);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
+#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
+#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
+#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
+#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
+#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
+
+#else /* PRISM2_IO_DEBUG */
+
+#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
+#define HFA384X_INB(a) inb(dev->base_addr + (a))
+#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
+#define HFA384X_INW(a) inw(dev->base_addr + (a))
+#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
+#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
+
+#endif /* PRISM2_IO_DEBUG */
+
+
+static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
+ int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ if (len / 2)
+ HFA384X_INSW(d_off, buf, len / 2);
+ pos += len / 2;
+
+ if (len & 1)
+ *((char *) pos) = HFA384X_INB(d_off);
+
+ return 0;
+}
+
+
+static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ if (len / 2)
+ HFA384X_OUTSW(d_off, buf, len / 2);
+ pos += len / 2;
+
+ if (len & 1)
+ HFA384X_OUTB(*((char *) pos), d_off);
+
+ return 0;
+}
+
+
+/* FIX: This might change at some point.. */
+#include "hostap_hw.c"
+
+
+
+static void prism2_detach(struct pcmcia_device *p_dev);
+static void prism2_release(u_long arg);
+static int prism2_config(struct pcmcia_device *link);
+
+
+static int prism2_pccard_card_present(local_info_t *local)
+{
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+ if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link))
+ return 1;
+ return 0;
+}
+
+
+/*
+ * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0
+ * Document No. 20-10-00058, January 2004
+ * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf
+ */
+#define SANDISK_WLAN_ACTIVATION_OFF 0x40
+#define SANDISK_HCR_OFF 0x42
+
+
+static void sandisk_set_iobase(local_info_t *local)
+{
+ int res;
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ res = pcmcia_write_config_byte(hw_priv->link, 0x10,
+ hw_priv->link->resource[0]->start & 0x00ff);
+ if (res != 0) {
+ printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
+ " res=%d\n", res);
+ }
+ udelay(10);
+
+ res = pcmcia_write_config_byte(hw_priv->link, 0x12,
+ (hw_priv->link->resource[0]->start >> 8) & 0x00ff);
+ if (res != 0) {
+ printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
+ " res=%d\n", res);
+ }
+}
+
+
+static void sandisk_write_hcr(local_info_t *local, int hcr)
+{
+ struct net_device *dev = local->dev;
+ int i;
+
+ HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF);
+ udelay(50);
+ for (i = 0; i < 10; i++) {
+ HFA384X_OUTB(hcr, SANDISK_HCR_OFF);
+ }
+ udelay(55);
+ HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF);
+}
+
+
+static int sandisk_enable_wireless(struct net_device *dev)
+{
+ int res, ret = 0;
+ struct hostap_interface *iface = netdev_priv(dev);
+ local_info_t *local = iface->local;
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ if (resource_size(hw_priv->link->resource[0]) < 0x42) {
+ /* Not enough ports to be SanDisk multi-function card */
+ ret = -ENODEV;
+ goto done;
+ }
+
+ if (hw_priv->link->manf_id != 0xd601 || hw_priv->link->card_id != 0x0101) {
+ /* No SanDisk manfid found */
+ ret = -ENODEV;
+ goto done;
+ }
+
+ if (hw_priv->link->socket->functions < 2) {
+ /* No multi-function links found */
+ ret = -ENODEV;
+ goto done;
+ }
+
+ printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected"
+ " - using vendor-specific initialization\n", dev->name);
+ hw_priv->sandisk_connectplus = 1;
+
+ res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
+ COR_SOFT_RESET);
+ if (res != 0) {
+ printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+ dev->name, res);
+ goto done;
+ }
+ mdelay(5);
+
+ /*
+ * Do not enable interrupts here to avoid some bogus events. Interrupts
+ * will be enabled during the first cor_sreset call.
+ */
+ res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
+ (COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE |
+ COR_FUNC_ENA));
+ if (res != 0) {
+ printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+ dev->name, res);
+ goto done;
+ }
+ mdelay(5);
+
+ sandisk_set_iobase(local);
+
+ HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF);
+ udelay(10);
+ HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF);
+ udelay(10);
+
+done:
+ return ret;
+}
+
+
+static void prism2_pccard_cor_sreset(local_info_t *local)
+{
+ int res;
+ u8 val;
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ if (!prism2_pccard_card_present(local))
+ return;
+
+ res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &val);
+ if (res != 0) {
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
+ res);
+ return;
+ }
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n",
+ val);
+
+ val |= COR_SOFT_RESET;
+ res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val);
+ if (res != 0) {
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
+ res);
+ return;
+ }
+
+ mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+ val &= ~COR_SOFT_RESET;
+ if (hw_priv->sandisk_connectplus)
+ val |= COR_IREQ_ENA;
+ res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val);
+ if (res != 0) {
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
+ res);
+ return;
+ }
+
+ mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+ if (hw_priv->sandisk_connectplus)
+ sandisk_set_iobase(local);
+}
+
+
+static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
+{
+ int res;
+ u8 old_cor;
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ if (!prism2_pccard_card_present(local))
+ return;
+
+ if (hw_priv->sandisk_connectplus) {
+ sandisk_write_hcr(local, hcr);
+ return;
+ }
+
+ res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &old_cor);
+ if (res != 0) {
+ printk(KERN_DEBUG "%s failed 1 (%d)\n", __func__, res);
+ return;
+ }
+ printk(KERN_DEBUG "%s: original COR %02x\n", __func__, old_cor);
+
+ res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
+ old_cor | COR_SOFT_RESET);
+ if (res != 0) {
+ printk(KERN_DEBUG "%s failed 2 (%d)\n", __func__, res);
+ return;
+ }
+
+ mdelay(10);
+
+ /* Setup Genesis mode */
+ res = pcmcia_write_config_byte(hw_priv->link, CISREG_CCSR, hcr);
+ if (res != 0) {
+ printk(KERN_DEBUG "%s failed 3 (%d)\n", __func__, res);
+ return;
+ }
+ mdelay(10);
+
+ res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
+ old_cor & ~COR_SOFT_RESET);
+ if (res != 0) {
+ printk(KERN_DEBUG "%s failed 4 (%d)\n", __func__, res);
+ return;
+ }
+
+ mdelay(10);
+}
+
+
+static struct prism2_helper_functions prism2_pccard_funcs =
+{
+ .card_present = prism2_pccard_card_present,
+ .cor_sreset = prism2_pccard_cor_sreset,
+ .genesis_reset = prism2_pccard_genesis_reset,
+ .hw_type = HOSTAP_HW_PCCARD,
+};
+
+
+/* allocate local data and register with CardServices
+ * initialize dev_link structure, but do not configure the card yet */
+static int hostap_cs_probe(struct pcmcia_device *p_dev)
+{
+ int ret;
+
+ PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
+
+ ret = prism2_config(p_dev);
+ if (ret) {
+ PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
+ }
+
+ return ret;
+}
+
+
+static void prism2_detach(struct pcmcia_device *link)
+{
+ PDEBUG(DEBUG_FLOW, "prism2_detach\n");
+
+ prism2_release((u_long)link);
+
+ /* release net devices */
+ if (link->priv) {
+ struct hostap_cs_priv *hw_priv;
+ struct net_device *dev;
+ struct hostap_interface *iface;
+ dev = link->priv;
+ iface = netdev_priv(dev);
+ hw_priv = iface->local->hw_priv;
+ prism2_free_local_data(dev);
+ kfree(hw_priv);
+ }
+}
+
+
+static int prism2_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+ if (p_dev->config_index == 0)
+ return -EINVAL;
+
+ return pcmcia_request_io(p_dev);
+}
+
+static int prism2_config(struct pcmcia_device *link)
+{
+ struct net_device *dev;
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret;
+ struct hostap_cs_priv *hw_priv;
+ unsigned long flags;
+
+ PDEBUG(DEBUG_FLOW, "prism2_config()\n");
+
+ hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
+ if (hw_priv == NULL) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ /* Look for an appropriate configuration table entry in the CIS */
+ link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO |
+ CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
+ if (ignore_cis_vcc)
+ link->config_flags &= ~CONF_AUTO_CHECK_VCC;
+ ret = pcmcia_loop_config(link, prism2_config_check, NULL);
+ if (ret) {
+ if (!ignore_cis_vcc)
+ printk(KERN_ERR "GetNextTuple(): No matching "
+ "CIS configuration. Maybe you need the "
+ "ignore_cis_vcc=1 parameter.\n");
+ goto failed;
+ }
+
+ /* Need to allocate net_device before requesting IRQ handler */
+ dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
+ &link->dev);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+ link->priv = dev;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ local->hw_priv = hw_priv;
+ hw_priv->link = link;
+
+ /*
+ * We enable IRQ here, but IRQ handler will not proceed
+ * until dev->base_addr is set below. This protect us from
+ * receive interrupts when driver is not initialized.
+ */
+ ret = pcmcia_request_irq(link, prism2_interrupt);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto failed;
+
+ spin_lock_irqsave(&local->irq_init_lock, flags);
+ dev->irq = link->irq;
+ dev->base_addr = link->resource[0]->start;
+ spin_unlock_irqrestore(&local->irq_init_lock, flags);
+
+ local->shutdown = 0;
+
+ sandisk_enable_wireless(dev);
+
+ ret = prism2_hw_config(dev, 1);
+ if (!ret)
+ ret = hostap_hw_ready(dev);
+
+ return ret;
+
+ failed:
+ kfree(hw_priv);
+ prism2_release((u_long)link);
+ return ret;
+}
+
+
+static void prism2_release(u_long arg)
+{
+ struct pcmcia_device *link = (struct pcmcia_device *)arg;
+
+ PDEBUG(DEBUG_FLOW, "prism2_release\n");
+
+ if (link->priv) {
+ struct net_device *dev = link->priv;
+ struct hostap_interface *iface;
+
+ iface = netdev_priv(dev);
+ prism2_hw_shutdown(dev, 0);
+ iface->local->shutdown = 1;
+ }
+
+ pcmcia_disable_device(link);
+ PDEBUG(DEBUG_FLOW, "release - done\n");
+}
+
+static int hostap_cs_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = (struct net_device *) link->priv;
+ int dev_open = 0;
+ struct hostap_interface *iface = NULL;
+
+ if (!dev)
+ return -ENODEV;
+
+ iface = netdev_priv(dev);
+
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
+ if (iface && iface->local)
+ dev_open = iface->local->num_dev_open > 0;
+ if (dev_open) {
+ netif_stop_queue(dev);
+ netif_device_detach(dev);
+ }
+ prism2_suspend(dev);
+
+ return 0;
+}
+
+static int hostap_cs_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = (struct net_device *) link->priv;
+ int dev_open = 0;
+ struct hostap_interface *iface = NULL;
+
+ if (!dev)
+ return -ENODEV;
+
+ iface = netdev_priv(dev);
+
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
+
+ if (iface && iface->local)
+ dev_open = iface->local->num_dev_open > 0;
+
+ prism2_hw_shutdown(dev, 1);
+ prism2_hw_config(dev, dev_open ? 0 : 1);
+ if (dev_open) {
+ netif_device_attach(dev);
+ netif_start_queue(dev);
+ }
+
+ return 0;
+}
+
+static const struct pcmcia_device_id hostap_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100),
+ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300),
+ PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777),
+ PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000),
+ PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3301),
+ PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613),
+ PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001),
+ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001),
+ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300),
+/* PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000), conflict with pcnet_cs */
+ PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005),
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010),
+ PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF",
+ 0x2d858104),
+ PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL",
+ 0x74c5e40d),
+ PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil",
+ 0x4b801a17),
+ PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.02",
+ 0x4b74baa0),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus",
+ 0x7a954bd9, 0x74be00c6),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02",
+ 0xe6ec52ce, 0x08649af2, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Canon", "Wireless LAN CF Card K30225", "Version 01.00",
+ 0x96ef6fe2, 0x263fcbab, 0xa57adb8c),
+ PCMCIA_DEVICE_PROD_ID123(
+ "D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02",
+ 0x71b18589, 0xb6f1b0ab, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Instant Wireless ", " Network PC CARD", "Version 01.02",
+ 0x11d901af, 0x6e9bd926, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID123(
+ "SMC", "SMC2632W", "Version 01.02",
+ 0xc4f8b18b, 0x474a1f2a, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G",
+ 0x2decece3, 0x82067c18),
+ PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card",
+ 0x54f7c49c, 0x15a75e5b),
+ PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE",
+ 0x74c5e40d, 0xdb472a18),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card",
+ 0x0733cc81, 0x0c52f395),
+ PCMCIA_DEVICE_PROD_ID12(
+ "ZoomAir 11Mbps High", "Rate wireless Networking",
+ 0x273fe3db, 0x32a1eaee),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card",
+ 0xa37434e9, 0x9762e8f1),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Pretec", "CompactWLAN Card 802.11b", "2.5",
+ 0x1cadd3e5, 0xe697636c, 0x7a5bfcf1),
+ PCMCIA_DEVICE_PROD_ID123(
+ "U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02",
+ 0xc7b8df9d, 0x1700d087, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio",
+ "Ver. 1.00",
+ 0x5cd01705, 0x4271660f, 0x9d08ee12),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Wireless LAN" , "11Mbps PC Card", "Version 01.02",
+ 0x4b8870ff, 0x70e946d1, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092),
+ PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2),
+ PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b),
+ PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
+
+
+static struct pcmcia_driver hostap_driver = {
+ .name = "hostap_cs",
+ .probe = hostap_cs_probe,
+ .remove = prism2_detach,
+ .owner = THIS_MODULE,
+ .id_table = hostap_cs_ids,
+ .suspend = hostap_cs_suspend,
+ .resume = hostap_cs_resume,
+};
+module_pcmcia_driver(hostap_driver);
diff --git a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/intersil/hostap/hostap_download.c
index 705fe668b969..705fe668b969 100644
--- a/drivers/net/wireless/hostap/hostap_download.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_download.c
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/intersil/hostap/hostap_hw.c
index 6df3ee561d52..6df3ee561d52 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_hw.c
diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/intersil/hostap/hostap_info.c
index 7635ac4f6679..7635ac4f6679 100644
--- a/drivers/net/wireless/hostap/hostap_info.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_info.c
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c
index 3e5fa7872b64..3e5fa7872b64 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c
index 80d4228ba754..80d4228ba754 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_main.c
diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/intersil/hostap/hostap_pci.c
index c864ef4b0015..c864ef4b0015 100644
--- a/drivers/net/wireless/hostap/hostap_pci.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_pci.c
diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/intersil/hostap/hostap_plx.c
index 4901a99c6c59..4901a99c6c59 100644
--- a/drivers/net/wireless/hostap/hostap_plx.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_plx.c
diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/intersil/hostap/hostap_proc.c
index dd84557cf957..dd84557cf957 100644
--- a/drivers/net/wireless/hostap/hostap_proc.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_proc.c
diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/intersil/hostap/hostap_wlan.h
index ca25283e1c92..ca25283e1c92 100644
--- a/drivers/net/wireless/hostap/hostap_wlan.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_wlan.h
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/intersil/orinoco/Kconfig
index f6fa3f4e294f..f6fa3f4e294f 100644
--- a/drivers/net/wireless/orinoco/Kconfig
+++ b/drivers/net/wireless/intersil/orinoco/Kconfig
diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/intersil/orinoco/Makefile
index bfdefb85abcd..bfdefb85abcd 100644
--- a/drivers/net/wireless/orinoco/Makefile
+++ b/drivers/net/wireless/intersil/orinoco/Makefile
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/intersil/orinoco/airport.c
index 77e6c53040a3..77e6c53040a3 100644
--- a/drivers/net/wireless/orinoco/airport.c
+++ b/drivers/net/wireless/intersil/orinoco/airport.c
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/intersil/orinoco/cfg.c
index 0f6ea316e38e..0f6ea316e38e 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/intersil/orinoco/cfg.c
diff --git a/drivers/net/wireless/orinoco/cfg.h b/drivers/net/wireless/intersil/orinoco/cfg.h
index 3ddc96a06cd7..3ddc96a06cd7 100644
--- a/drivers/net/wireless/orinoco/cfg.h
+++ b/drivers/net/wireless/intersil/orinoco/cfg.h
diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/intersil/orinoco/fw.c
index 400a35217644..400a35217644 100644
--- a/drivers/net/wireless/orinoco/fw.c
+++ b/drivers/net/wireless/intersil/orinoco/fw.c
diff --git a/drivers/net/wireless/orinoco/fw.h b/drivers/net/wireless/intersil/orinoco/fw.h
index aca63e3c4b5b..aca63e3c4b5b 100644
--- a/drivers/net/wireless/orinoco/fw.h
+++ b/drivers/net/wireless/intersil/orinoco/fw.h
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/intersil/orinoco/hermes.c
index 43790fbea0e0..43790fbea0e0 100644
--- a/drivers/net/wireless/orinoco/hermes.c
+++ b/drivers/net/wireless/intersil/orinoco/hermes.c
diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/intersil/orinoco/hermes.h
index 28a42448d329..28a42448d329 100644
--- a/drivers/net/wireless/orinoco/hermes.h
+++ b/drivers/net/wireless/intersil/orinoco/hermes.h
diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/intersil/orinoco/hermes_dld.c
index 4a10b7aca043..4a10b7aca043 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.c
+++ b/drivers/net/wireless/intersil/orinoco/hermes_dld.c
diff --git a/drivers/net/wireless/orinoco/hermes_dld.h b/drivers/net/wireless/intersil/orinoco/hermes_dld.h
index b5377e232c63..b5377e232c63 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.h
+++ b/drivers/net/wireless/intersil/orinoco/hermes_dld.h
diff --git a/drivers/net/wireless/orinoco/hermes_rid.h b/drivers/net/wireless/intersil/orinoco/hermes_rid.h
index 42eb67dea1df..42eb67dea1df 100644
--- a/drivers/net/wireless/orinoco/hermes_rid.h
+++ b/drivers/net/wireless/intersil/orinoco/hermes_rid.h
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/intersil/orinoco/hw.c
index e27e32851f1e..e27e32851f1e 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/intersil/orinoco/hw.c
diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/intersil/orinoco/hw.h
index 466d1ede76f1..466d1ede76f1 100644
--- a/drivers/net/wireless/orinoco/hw.h
+++ b/drivers/net/wireless/intersil/orinoco/hw.h
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/intersil/orinoco/main.c
index 7b5c554323c7..7b5c554323c7 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/intersil/orinoco/main.c
diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/intersil/orinoco/main.h
index 5a8fec26136e..5a8fec26136e 100644
--- a/drivers/net/wireless/orinoco/main.h
+++ b/drivers/net/wireless/intersil/orinoco/main.h
diff --git a/drivers/net/wireless/orinoco/mic.c b/drivers/net/wireless/intersil/orinoco/mic.c
index fce4a843e656..fce4a843e656 100644
--- a/drivers/net/wireless/orinoco/mic.c
+++ b/drivers/net/wireless/intersil/orinoco/mic.c
diff --git a/drivers/net/wireless/orinoco/mic.h b/drivers/net/wireless/intersil/orinoco/mic.h
index 04d05bc566d6..04d05bc566d6 100644
--- a/drivers/net/wireless/orinoco/mic.h
+++ b/drivers/net/wireless/intersil/orinoco/mic.h
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/intersil/orinoco/orinoco.h
index eebd2be21ee9..eebd2be21ee9 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/intersil/orinoco/orinoco.h
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c
index a956f965a1e5..a956f965a1e5 100644
--- a/drivers/net/wireless/orinoco/orinoco_cs.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c
diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c
index 048693b6c6c2..048693b6c6c2 100644
--- a/drivers/net/wireless/orinoco/orinoco_nortel.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/intersil/orinoco/orinoco_pci.c
index 4938a2208a37..4938a2208a37 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_pci.c
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.h b/drivers/net/wireless/intersil/orinoco/orinoco_pci.h
index 43f5b9f5a0b0..43f5b9f5a0b0 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.h
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_pci.h
diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/intersil/orinoco/orinoco_plx.c
index 221352027779..221352027779 100644
--- a/drivers/net/wireless/orinoco/orinoco_plx.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_plx.c
diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/intersil/orinoco/orinoco_tmd.c
index 20ce569b8a43..20ce569b8a43 100644
--- a/drivers/net/wireless/orinoco/orinoco_tmd.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_tmd.c
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c
index f2cd513d54b2..f2cd513d54b2 100644
--- a/drivers/net/wireless/orinoco/orinoco_usb.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c
diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/intersil/orinoco/scan.c
index 2c66166add70..2c66166add70 100644
--- a/drivers/net/wireless/orinoco/scan.c
+++ b/drivers/net/wireless/intersil/orinoco/scan.c
diff --git a/drivers/net/wireless/orinoco/scan.h b/drivers/net/wireless/intersil/orinoco/scan.h
index 27281fb0a6dc..27281fb0a6dc 100644
--- a/drivers/net/wireless/orinoco/scan.h
+++ b/drivers/net/wireless/intersil/orinoco/scan.h
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c
index b60048c95e0a..b60048c95e0a 100644
--- a/drivers/net/wireless/orinoco/spectrum_cs.c
+++ b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/intersil/orinoco/wext.c
index 1d4dae422106..1d4dae422106 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/intersil/orinoco/wext.c
diff --git a/drivers/net/wireless/orinoco/wext.h b/drivers/net/wireless/intersil/orinoco/wext.h
index 1479f4e26dde..1479f4e26dde 100644
--- a/drivers/net/wireless/orinoco/wext.h
+++ b/drivers/net/wireless/intersil/orinoco/wext.h
diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/intersil/p54/Kconfig
index cdafb8c73e82..cdafb8c73e82 100644
--- a/drivers/net/wireless/p54/Kconfig
+++ b/drivers/net/wireless/intersil/p54/Kconfig
diff --git a/drivers/net/wireless/p54/Makefile b/drivers/net/wireless/intersil/p54/Makefile
index b542e68f1781..b542e68f1781 100644
--- a/drivers/net/wireless/p54/Makefile
+++ b/drivers/net/wireless/intersil/p54/Makefile
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/intersil/p54/eeprom.c
index 2fe713eda7ad..2fe713eda7ad 100644
--- a/drivers/net/wireless/p54/eeprom.c
+++ b/drivers/net/wireless/intersil/p54/eeprom.c
diff --git a/drivers/net/wireless/p54/eeprom.h b/drivers/net/wireless/intersil/p54/eeprom.h
index 20ebe39a3f4e..20ebe39a3f4e 100644
--- a/drivers/net/wireless/p54/eeprom.h
+++ b/drivers/net/wireless/intersil/p54/eeprom.h
diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c
index 257a9eadd595..257a9eadd595 100644
--- a/drivers/net/wireless/p54/fwio.c
+++ b/drivers/net/wireless/intersil/p54/fwio.c
diff --git a/drivers/net/wireless/p54/led.c b/drivers/net/wireless/intersil/p54/led.c
index 9a8fedd3c0f5..9a8fedd3c0f5 100644
--- a/drivers/net/wireless/p54/led.c
+++ b/drivers/net/wireless/intersil/p54/led.c
diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/intersil/p54/lmac.h
index de1d46bf97df..de1d46bf97df 100644
--- a/drivers/net/wireless/p54/lmac.h
+++ b/drivers/net/wireless/intersil/p54/lmac.h
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/intersil/p54/main.c
index 7805864e76f9..7805864e76f9 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/intersil/p54/main.c
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/intersil/p54/p54.h
index 40b401ed6845..40b401ed6845 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/intersil/p54/p54.h
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/intersil/p54/p54pci.c
index 27a49068d32d..27a49068d32d 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/intersil/p54/p54pci.c
diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/intersil/p54/p54pci.h
index 68405c142f97..68405c142f97 100644
--- a/drivers/net/wireless/p54/p54pci.h
+++ b/drivers/net/wireless/intersil/p54/p54pci.h
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/intersil/p54/p54spi.c
index 7ab2f43ab425..7ab2f43ab425 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/intersil/p54/p54spi.c
diff --git a/drivers/net/wireless/p54/p54spi.h b/drivers/net/wireless/intersil/p54/p54spi.h
index dfaa62aaeb07..dfaa62aaeb07 100644
--- a/drivers/net/wireless/p54/p54spi.h
+++ b/drivers/net/wireless/intersil/p54/p54spi.h
diff --git a/drivers/net/wireless/p54/p54spi_eeprom.h b/drivers/net/wireless/intersil/p54/p54spi_eeprom.h
index 0b7bfb0adcf2..0b7bfb0adcf2 100644
--- a/drivers/net/wireless/p54/p54spi_eeprom.h
+++ b/drivers/net/wireless/intersil/p54/p54spi_eeprom.h
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/intersil/p54/p54usb.c
index 043bd1c23c19..043bd1c23c19 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/intersil/p54/p54usb.c
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/intersil/p54/p54usb.h
index a5f5f0fea3bd..a5f5f0fea3bd 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/intersil/p54/p54usb.h
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c
index 24e5ff9a9272..24e5ff9a9272 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/intersil/p54/txrx.c
diff --git a/drivers/net/wireless/prism54/Makefile b/drivers/net/wireless/intersil/prism54/Makefile
index fad305c76737..fad305c76737 100644
--- a/drivers/net/wireless/prism54/Makefile
+++ b/drivers/net/wireless/intersil/prism54/Makefile
diff --git a/drivers/net/wireless/prism54/isl_38xx.c b/drivers/net/wireless/intersil/prism54/isl_38xx.c
index 333c1a2f882e..333c1a2f882e 100644
--- a/drivers/net/wireless/prism54/isl_38xx.c
+++ b/drivers/net/wireless/intersil/prism54/isl_38xx.c
diff --git a/drivers/net/wireless/prism54/isl_38xx.h b/drivers/net/wireless/intersil/prism54/isl_38xx.h
index 547ab885610b..547ab885610b 100644
--- a/drivers/net/wireless/prism54/isl_38xx.h
+++ b/drivers/net/wireless/intersil/prism54/isl_38xx.h
diff --git a/drivers/net/wireless/intersil/prism54/isl_ioctl.c b/drivers/net/wireless/intersil/prism54/isl_ioctl.c
new file mode 100644
index 000000000000..48e8a978a832
--- /dev/null
+++ b/drivers/net/wireless/intersil/prism54/isl_ioctl.c
@@ -0,0 +1,2910 @@
+/*
+ * Copyright (C) 2002 Intersil Americas Inc.
+ * (C) 2003,2004 Aurelien Alleaume <slts@free.fr>
+ * (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
+ * (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/if_arp.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+
+#include <asm/uaccess.h>
+
+#include "prismcompat.h"
+#include "isl_ioctl.h"
+#include "islpci_mgt.h"
+#include "isl_oid.h" /* additional types and defs for isl38xx fw */
+#include "oid_mgt.h"
+
+#include <net/iw_handler.h> /* New driver API */
+
+#define KEY_SIZE_WEP104 13 /* 104/128-bit WEP keys */
+#define KEY_SIZE_WEP40 5 /* 40/64-bit WEP keys */
+/* KEY_SIZE_TKIP should match isl_oid.h, struct obj_key.key[] size */
+#define KEY_SIZE_TKIP 32 /* TKIP keys */
+
+static void prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid,
+ u8 *wpa_ie, size_t wpa_ie_len);
+static size_t prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie);
+static int prism54_set_wpa(struct net_device *, struct iw_request_info *,
+ __u32 *, char *);
+
+/* In 500 kbps */
+static const unsigned char scan_rate_list[] = { 2, 4, 11, 22,
+ 12, 18, 24, 36,
+ 48, 72, 96, 108 };
+
+/**
+ * prism54_mib_mode_helper - MIB change mode helper function
+ * @mib: the &struct islpci_mib object to modify
+ * @iw_mode: new mode (%IW_MODE_*)
+ *
+ * This is a helper function, hence it does not lock. Make sure
+ * caller deals with locking *if* necessary. This function sets the
+ * mode-dependent mib values and does the mapping of the Linux
+ * Wireless API modes to Device firmware modes. It also checks for
+ * correct valid Linux wireless modes.
+ */
+static int
+prism54_mib_mode_helper(islpci_private *priv, u32 iw_mode)
+{
+ u32 config = INL_CONFIG_MANUALRUN;
+ u32 mode, bsstype;
+
+ /* For now, just catch early the Repeater and Secondary modes here */
+ if (iw_mode == IW_MODE_REPEAT || iw_mode == IW_MODE_SECOND) {
+ printk(KERN_DEBUG
+ "%s(): Sorry, Repeater mode and Secondary mode "
+ "are not yet supported by this driver.\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->iw_mode = iw_mode;
+
+ switch (iw_mode) {
+ case IW_MODE_AUTO:
+ mode = INL_MODE_CLIENT;
+ bsstype = DOT11_BSSTYPE_ANY;
+ break;
+ case IW_MODE_ADHOC:
+ mode = INL_MODE_CLIENT;
+ bsstype = DOT11_BSSTYPE_IBSS;
+ break;
+ case IW_MODE_INFRA:
+ mode = INL_MODE_CLIENT;
+ bsstype = DOT11_BSSTYPE_INFRA;
+ break;
+ case IW_MODE_MASTER:
+ mode = INL_MODE_AP;
+ bsstype = DOT11_BSSTYPE_INFRA;
+ break;
+ case IW_MODE_MONITOR:
+ mode = INL_MODE_PROMISCUOUS;
+ bsstype = DOT11_BSSTYPE_ANY;
+ config |= INL_CONFIG_RXANNEX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (init_wds)
+ config |= INL_CONFIG_WDS;
+ mgt_set(priv, DOT11_OID_BSSTYPE, &bsstype);
+ mgt_set(priv, OID_INL_CONFIG, &config);
+ mgt_set(priv, OID_INL_MODE, &mode);
+
+ return 0;
+}
+
+/**
+ * prism54_mib_init - fill MIB cache with defaults
+ *
+ * this function initializes the struct given as @mib with defaults,
+ * of which many are retrieved from the global module parameter
+ * variables.
+ */
+
+void
+prism54_mib_init(islpci_private *priv)
+{
+ u32 channel, authen, wep, filter, dot1x, mlme, conformance, power, mode;
+ struct obj_buffer psm_buffer = {
+ .size = PSM_BUFFER_SIZE,
+ .addr = priv->device_psm_buffer
+ };
+
+ channel = CARD_DEFAULT_CHANNEL;
+ authen = CARD_DEFAULT_AUTHEN;
+ wep = CARD_DEFAULT_WEP;
+ filter = CARD_DEFAULT_FILTER; /* (0) Do not filter un-encrypted data */
+ dot1x = CARD_DEFAULT_DOT1X;
+ mlme = CARD_DEFAULT_MLME_MODE;
+ conformance = CARD_DEFAULT_CONFORMANCE;
+ power = 127;
+ mode = CARD_DEFAULT_IW_MODE;
+
+ mgt_set(priv, DOT11_OID_CHANNEL, &channel);
+ mgt_set(priv, DOT11_OID_AUTHENABLE, &authen);
+ mgt_set(priv, DOT11_OID_PRIVACYINVOKED, &wep);
+ mgt_set(priv, DOT11_OID_PSMBUFFER, &psm_buffer);
+ mgt_set(priv, DOT11_OID_EXUNENCRYPTED, &filter);
+ mgt_set(priv, DOT11_OID_DOT1XENABLE, &dot1x);
+ mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlme);
+ mgt_set(priv, OID_INL_DOT11D_CONFORMANCE, &conformance);
+ mgt_set(priv, OID_INL_OUTPUTPOWER, &power);
+
+ /* This sets all of the mode-dependent values */
+ prism54_mib_mode_helper(priv, mode);
+}
+
+/* this will be executed outside of atomic context thanks to
+ * schedule_work(), thus we can as well use sleeping semaphore
+ * locking */
+void
+prism54_update_stats(struct work_struct *work)
+{
+ islpci_private *priv = container_of(work, islpci_private, stats_work);
+ char *data;
+ int j;
+ struct obj_bss bss, *bss2;
+ union oid_res_t r;
+
+ mutex_lock(&priv->stats_lock);
+
+/* Noise floor.
+ * I'm not sure if the unit is dBm.
+ * Note : If we are not connected, this value seems to be irrelevant. */
+
+ mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
+ priv->local_iwstatistics.qual.noise = r.u;
+
+/* Get the rssi of the link. To do this we need to retrieve a bss. */
+
+ /* First get the MAC address of the AP we are associated with. */
+ mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
+ data = r.ptr;
+
+ /* copy this MAC to the bss */
+ memcpy(bss.address, data, ETH_ALEN);
+ kfree(data);
+
+ /* now ask for the corresponding bss */
+ j = mgt_get_request(priv, DOT11_OID_BSSFIND, 0, (void *) &bss, &r);
+ bss2 = r.ptr;
+ /* report the rssi and use it to calculate
+ * link quality through a signal-noise
+ * ratio */
+ priv->local_iwstatistics.qual.level = bss2->rssi;
+ priv->local_iwstatistics.qual.qual =
+ bss2->rssi - priv->iwstatistics.qual.noise;
+
+ kfree(bss2);
+
+ /* report that the stats are new */
+ priv->local_iwstatistics.qual.updated = 0x7;
+
+/* Rx : unable to decrypt the MPDU */
+ mgt_get_request(priv, DOT11_OID_PRIVRXFAILED, 0, NULL, &r);
+ priv->local_iwstatistics.discard.code = r.u;
+
+/* Tx : Max MAC retries num reached */
+ mgt_get_request(priv, DOT11_OID_MPDUTXFAILED, 0, NULL, &r);
+ priv->local_iwstatistics.discard.retries = r.u;
+
+ mutex_unlock(&priv->stats_lock);
+}
+
+struct iw_statistics *
+prism54_get_wireless_stats(struct net_device *ndev)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ /* If the stats are being updated return old data */
+ if (mutex_trylock(&priv->stats_lock)) {
+ memcpy(&priv->iwstatistics, &priv->local_iwstatistics,
+ sizeof (struct iw_statistics));
+ /* They won't be marked updated for the next time */
+ priv->local_iwstatistics.qual.updated = 0;
+ mutex_unlock(&priv->stats_lock);
+ } else
+ priv->iwstatistics.qual.updated = 0;
+
+ /* Update our wireless stats, but do not schedule to often
+ * (max 1 HZ) */
+ if ((priv->stats_timestamp == 0) ||
+ time_after(jiffies, priv->stats_timestamp + 1 * HZ)) {
+ schedule_work(&priv->stats_work);
+ priv->stats_timestamp = jiffies;
+ }
+
+ return &priv->iwstatistics;
+}
+
+static int
+prism54_commit(struct net_device *ndev, struct iw_request_info *info,
+ char *cwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ /* simply re-set the last set SSID, this should commit most stuff */
+
+ /* Commit in Monitor mode is not necessary, also setting essid
+ * in Monitor mode does not make sense and isn't allowed for this
+ * device's firmware */
+ if (priv->iw_mode != IW_MODE_MONITOR)
+ return mgt_set_request(priv, DOT11_OID_SSID, 0, NULL);
+ return 0;
+}
+
+static int
+prism54_get_name(struct net_device *ndev, struct iw_request_info *info,
+ char *cwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ char *capabilities;
+ union oid_res_t r;
+ int rvalue;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT) {
+ strncpy(cwrq, "NOT READY!", IFNAMSIZ);
+ return 0;
+ }
+ rvalue = mgt_get_request(priv, OID_INL_PHYCAPABILITIES, 0, NULL, &r);
+
+ switch (r.u) {
+ case INL_PHYCAP_5000MHZ:
+ capabilities = "IEEE 802.11a/b/g";
+ break;
+ case INL_PHYCAP_FAA:
+ capabilities = "IEEE 802.11b/g - FAA Support";
+ break;
+ case INL_PHYCAP_2400MHZ:
+ default:
+ capabilities = "IEEE 802.11b/g"; /* Default */
+ break;
+ }
+ strncpy(cwrq, capabilities, IFNAMSIZ);
+ return rvalue;
+}
+
+static int
+prism54_set_freq(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_freq *fwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int rvalue;
+ u32 c;
+
+ if (fwrq->m < 1000)
+ /* we have a channel number */
+ c = fwrq->m;
+ else
+ c = (fwrq->e == 1) ? channel_of_freq(fwrq->m / 100000) : 0;
+
+ rvalue = c ? mgt_set_request(priv, DOT11_OID_CHANNEL, 0, &c) : -EINVAL;
+
+ /* Call commit handler */
+ return (rvalue ? rvalue : -EINPROGRESS);
+}
+
+static int
+prism54_get_freq(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_freq *fwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ union oid_res_t r;
+ int rvalue;
+
+ rvalue = mgt_get_request(priv, DOT11_OID_CHANNEL, 0, NULL, &r);
+ fwrq->i = r.u;
+ rvalue |= mgt_get_request(priv, DOT11_OID_FREQUENCY, 0, NULL, &r);
+ fwrq->m = r.u;
+ fwrq->e = 3;
+
+ return rvalue;
+}
+
+static int
+prism54_set_mode(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ u32 mlmeautolevel = CARD_DEFAULT_MLME_MODE;
+
+ /* Let's see if the user passed a valid Linux Wireless mode */
+ if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) {
+ printk(KERN_DEBUG
+ "%s: %s() You passed a non-valid init_mode.\n",
+ priv->ndev->name, __func__);
+ return -EINVAL;
+ }
+
+ down_write(&priv->mib_sem);
+
+ if (prism54_mib_mode_helper(priv, *uwrq)) {
+ up_write(&priv->mib_sem);
+ return -EOPNOTSUPP;
+ }
+
+ /* the ACL code needs an intermediate mlmeautolevel. The wpa stuff an
+ * extended one.
+ */
+ if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN))
+ mlmeautolevel = DOT11_MLME_INTERMEDIATE;
+ if (priv->wpa)
+ mlmeautolevel = DOT11_MLME_EXTENDED;
+
+ mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
+
+ if (mgt_commit(priv)) {
+ up_write(&priv->mib_sem);
+ return -EIO;
+ }
+ priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR)
+ ? priv->monitor_type : ARPHRD_ETHER;
+ up_write(&priv->mib_sem);
+
+ return 0;
+}
+
+/* Use mib cache */
+static int
+prism54_get_mode(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ BUG_ON((priv->iw_mode < IW_MODE_AUTO) || (priv->iw_mode >
+ IW_MODE_MONITOR));
+ *uwrq = priv->iw_mode;
+
+ return 0;
+}
+
+/* we use DOT11_OID_EDTHRESHOLD. From what I guess the card will not try to
+ * emit data if (sensitivity > rssi - noise) (in dBm).
+ * prism54_set_sens does not seem to work.
+ */
+
+static int
+prism54_set_sens(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ u32 sens;
+
+ /* by default the card sets this to 20. */
+ sens = vwrq->disabled ? 20 : vwrq->value;
+
+ return mgt_set_request(priv, DOT11_OID_EDTHRESHOLD, 0, &sens);
+}
+
+static int
+prism54_get_sens(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ union oid_res_t r;
+ int rvalue;
+
+ rvalue = mgt_get_request(priv, DOT11_OID_EDTHRESHOLD, 0, NULL, &r);
+
+ vwrq->value = r.u;
+ vwrq->disabled = (vwrq->value == 0);
+ vwrq->fixed = 1;
+
+ return rvalue;
+}
+
+static int
+prism54_get_range(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ struct iw_range *range = (struct iw_range *) extra;
+ islpci_private *priv = netdev_priv(ndev);
+ u8 *data;
+ int i, m, rvalue;
+ struct obj_frequencies *freq;
+ union oid_res_t r;
+
+ memset(range, 0, sizeof (struct iw_range));
+ dwrq->length = sizeof (struct iw_range);
+
+ /* set the wireless extension version number */
+ range->we_version_source = SUPPORTED_WIRELESS_EXT;
+ range->we_version_compiled = WIRELESS_EXT;
+
+ /* Now the encoding capabilities */
+ range->num_encoding_sizes = 3;
+ /* 64(40) bits WEP */
+ range->encoding_size[0] = 5;
+ /* 128(104) bits WEP */
+ range->encoding_size[1] = 13;
+ /* 256 bits for WPA-PSK */
+ range->encoding_size[2] = 32;
+ /* 4 keys are allowed */
+ range->max_encoding_tokens = 4;
+
+ /* we don't know the quality range... */
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+ range->max_qual.qual = 0;
+ /* these value describe an average quality. Needs more tweaking... */
+ range->avg_qual.level = -80; /* -80 dBm */
+ range->avg_qual.noise = 0; /* don't know what to put here */
+ range->avg_qual.qual = 0;
+
+ range->sensitivity = 200;
+
+ /* retry limit capabilities */
+ range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->r_time_flags = IW_RETRY_LIFETIME;
+
+ /* I don't know the range. Put stupid things here */
+ range->min_retry = 1;
+ range->max_retry = 65535;
+ range->min_r_time = 1024;
+ range->max_r_time = 65535 * 1024;
+
+ /* txpower is supported in dBm's */
+ range->txpower_capa = IW_TXPOW_DBM;
+
+ /* Event capability (kernel + driver) */
+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+ IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+ IW_EVENT_CAPA_MASK(SIOCGIWAP));
+ range->event_capa[1] = IW_EVENT_CAPA_K_1;
+ range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVCUSTOM);
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return 0;
+
+ /* Request the device for the supported frequencies
+ * not really relevant since some devices will report the 5 GHz band
+ * frequencies even if they don't support them.
+ */
+ rvalue =
+ mgt_get_request(priv, DOT11_OID_SUPPORTEDFREQUENCIES, 0, NULL, &r);
+ freq = r.ptr;
+
+ range->num_channels = freq->nr;
+ range->num_frequency = freq->nr;
+
+ m = min(IW_MAX_FREQUENCIES, (int) freq->nr);
+ for (i = 0; i < m; i++) {
+ range->freq[i].m = freq->mhz[i];
+ range->freq[i].e = 6;
+ range->freq[i].i = channel_of_freq(freq->mhz[i]);
+ }
+ kfree(freq);
+
+ rvalue |= mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r);
+ data = r.ptr;
+
+ /* We got an array of char. It is NULL terminated. */
+ i = 0;
+ while ((i < IW_MAX_BITRATES) && (*data != 0)) {
+ /* the result must be in bps. The card gives us 500Kbps */
+ range->bitrate[i] = *data * 500000;
+ i++;
+ data++;
+ }
+ range->num_bitrates = i;
+ kfree(r.ptr);
+
+ return rvalue;
+}
+
+/* Set AP address*/
+
+static int
+prism54_set_wap(struct net_device *ndev, struct iw_request_info *info,
+ struct sockaddr *awrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ char bssid[6];
+ int rvalue;
+
+ if (awrq->sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ /* prepare the structure for the set object */
+ memcpy(&bssid[0], awrq->sa_data, ETH_ALEN);
+
+ /* set the bssid -- does this make sense when in AP mode? */
+ rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid);
+
+ return (rvalue ? rvalue : -EINPROGRESS); /* Call commit handler */
+}
+
+/* get AP address*/
+
+static int
+prism54_get_wap(struct net_device *ndev, struct iw_request_info *info,
+ struct sockaddr *awrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ union oid_res_t r;
+ int rvalue;
+
+ rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
+ memcpy(awrq->sa_data, r.ptr, ETH_ALEN);
+ awrq->sa_family = ARPHRD_ETHER;
+ kfree(r.ptr);
+
+ return rvalue;
+}
+
+static int
+prism54_set_scan(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ /* hehe the device does this automagicaly */
+ return 0;
+}
+
+/* a little helper that will translate our data into a card independent
+ * format that the Wireless Tools will understand. This was inspired by
+ * the "Aironet driver for 4500 and 4800 series cards" (GPL)
+ */
+
+static char *
+prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info,
+ char *current_ev, char *end_buf, struct obj_bss *bss,
+ char noise)
+{
+ struct iw_event iwe; /* Temporary buffer */
+ short cap;
+ islpci_private *priv = netdev_priv(ndev);
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ size_t wpa_ie_len;
+
+ /* The first entry must be the MAC address */
+ memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN);
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ iwe.cmd = SIOCGIWAP;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ /* The following entries will be displayed in the same order we give them */
+
+ /* The ESSID. */
+ iwe.u.data.length = bss->ssid.length;
+ iwe.u.data.flags = 1;
+ iwe.cmd = SIOCGIWESSID;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, bss->ssid.octets);
+
+ /* Capabilities */
+#define CAP_ESS 0x01
+#define CAP_IBSS 0x02
+#define CAP_CRYPT 0x10
+
+ /* Mode */
+ cap = bss->capinfo;
+ iwe.u.mode = 0;
+ if (cap & CAP_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else if (cap & CAP_IBSS)
+ iwe.u.mode = IW_MODE_ADHOC;
+ iwe.cmd = SIOCGIWMODE;
+ if (iwe.u.mode)
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+
+ /* Encryption capability */
+ if (cap & CAP_CRYPT)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ iwe.cmd = SIOCGIWENCODE;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, NULL);
+
+ /* Add frequency. (short) bss->channel is the frequency in MHz */
+ iwe.u.freq.m = bss->channel;
+ iwe.u.freq.e = 6;
+ iwe.cmd = SIOCGIWFREQ;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+
+ /* Add quality statistics */
+ iwe.u.qual.level = bss->rssi;
+ iwe.u.qual.noise = noise;
+ /* do a simple SNR for quality */
+ iwe.u.qual.qual = bss->rssi - noise;
+ iwe.cmd = IWEVQUAL;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+
+ /* Add WPA/RSN Information Element, if any */
+ wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie);
+ if (wpa_ie_len > 0) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = min_t(size_t, wpa_ie_len, MAX_WPA_IE_LEN);
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, wpa_ie);
+ }
+ /* Do the bitrates */
+ {
+ char *current_val = current_ev + iwe_stream_lcp_len(info);
+ int i;
+ int mask;
+
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ /* Parse the bitmask */
+ mask = 0x1;
+ for(i = 0; i < sizeof(scan_rate_list); i++) {
+ if(bss->rates & mask) {
+ iwe.u.bitrate.value = (scan_rate_list[i] * 500000);
+ current_val = iwe_stream_add_value(
+ info, current_ev, current_val,
+ end_buf, &iwe, IW_EV_PARAM_LEN);
+ }
+ mask <<= 1;
+ }
+ /* Check if we added any event */
+ if ((current_val - current_ev) > iwe_stream_lcp_len(info))
+ current_ev = current_val;
+ }
+
+ return current_ev;
+}
+
+static int
+prism54_get_scan(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int i, rvalue;
+ struct obj_bsslist *bsslist;
+ u32 noise = 0;
+ char *current_ev = extra;
+ union oid_res_t r;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT) {
+ /* device is not ready, fail gently */
+ dwrq->length = 0;
+ return 0;
+ }
+
+ /* first get the noise value. We will use it to report the link quality */
+ rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
+ noise = r.u;
+
+ /* Ask the device for a list of known bss.
+ * The old API, using SIOCGIWAPLIST, had a hard limit of IW_MAX_AP=64.
+ * The new API, using SIOCGIWSCAN, is only limited by the buffer size.
+ * WE-14->WE-16, the buffer is limited to IW_SCAN_MAX_DATA bytes.
+ * Starting with WE-17, the buffer can be as big as needed.
+ * But the device won't repport anything if you change the value
+ * of IWMAX_BSS=24. */
+
+ rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r);
+ bsslist = r.ptr;
+
+ /* ok now, scan the list and translate its info */
+ for (i = 0; i < (int) bsslist->nr; i++) {
+ current_ev = prism54_translate_bss(ndev, info, current_ev,
+ extra + dwrq->length,
+ &(bsslist->bsslist[i]),
+ noise);
+
+ /* Check if there is space for one more entry */
+ if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a bigger buffer */
+ rvalue = -E2BIG;
+ break;
+ }
+ }
+
+ kfree(bsslist);
+ dwrq->length = (current_ev - extra);
+ dwrq->flags = 0; /* todo */
+
+ return rvalue;
+}
+
+static int
+prism54_set_essid(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct obj_ssid essid;
+
+ memset(essid.octets, 0, 33);
+
+ /* Check if we were asked for `any' */
+ if (dwrq->flags && dwrq->length) {
+ if (dwrq->length > 32)
+ return -E2BIG;
+ essid.length = dwrq->length;
+ memcpy(essid.octets, extra, dwrq->length);
+ } else
+ essid.length = 0;
+
+ if (priv->iw_mode != IW_MODE_MONITOR)
+ return mgt_set_request(priv, DOT11_OID_SSID, 0, &essid);
+
+ /* If in monitor mode, just save to mib */
+ mgt_set(priv, DOT11_OID_SSID, &essid);
+ return 0;
+
+}
+
+static int
+prism54_get_essid(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct obj_ssid *essid;
+ union oid_res_t r;
+ int rvalue;
+
+ rvalue = mgt_get_request(priv, DOT11_OID_SSID, 0, NULL, &r);
+ essid = r.ptr;
+
+ if (essid->length) {
+ dwrq->flags = 1; /* set ESSID to ON for Wireless Extensions */
+ /* if it is too big, trunk it */
+ dwrq->length = min((u8)IW_ESSID_MAX_SIZE, essid->length);
+ } else {
+ dwrq->flags = 0;
+ dwrq->length = 0;
+ }
+ essid->octets[dwrq->length] = '\0';
+ memcpy(extra, essid->octets, dwrq->length);
+ kfree(essid);
+
+ return rvalue;
+}
+
+/* Provides no functionality, just completes the ioctl. In essence this is a
+ * just a cosmetic ioctl.
+ */
+static int
+prism54_set_nick(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ if (dwrq->length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+
+ down_write(&priv->mib_sem);
+ memset(priv->nickname, 0, sizeof (priv->nickname));
+ memcpy(priv->nickname, extra, dwrq->length);
+ up_write(&priv->mib_sem);
+
+ return 0;
+}
+
+static int
+prism54_get_nick(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ dwrq->length = 0;
+
+ down_read(&priv->mib_sem);
+ dwrq->length = strlen(priv->nickname);
+ memcpy(extra, priv->nickname, dwrq->length);
+ up_read(&priv->mib_sem);
+
+ return 0;
+}
+
+/* Set the allowed Bitrates */
+
+static int
+prism54_set_rate(struct net_device *ndev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+
+ islpci_private *priv = netdev_priv(ndev);
+ u32 rate, profile;
+ char *data;
+ int ret, i;
+ union oid_res_t r;
+
+ if (vwrq->value == -1) {
+ /* auto mode. No limit. */
+ profile = 1;
+ return mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
+ }
+
+ ret = mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r);
+ if (ret) {
+ kfree(r.ptr);
+ return ret;
+ }
+
+ rate = (u32) (vwrq->value / 500000);
+ data = r.ptr;
+ i = 0;
+
+ while (data[i]) {
+ if (rate && (data[i] == rate)) {
+ break;
+ }
+ if (vwrq->value == i) {
+ break;
+ }
+ data[i] |= 0x80;
+ i++;
+ }
+
+ if (!data[i]) {
+ kfree(r.ptr);
+ return -EINVAL;
+ }
+
+ data[i] |= 0x80;
+ data[i + 1] = 0;
+
+ /* Now, check if we want a fixed or auto value */
+ if (vwrq->fixed) {
+ data[0] = data[i];
+ data[1] = 0;
+ }
+
+/*
+ i = 0;
+ printk("prism54 rate: ");
+ while(data[i]) {
+ printk("%u ", data[i]);
+ i++;
+ }
+ printk("0\n");
+*/
+ profile = -1;
+ ret = mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
+ ret |= mgt_set_request(priv, DOT11_OID_EXTENDEDRATES, 0, data);
+ ret |= mgt_set_request(priv, DOT11_OID_RATES, 0, data);
+
+ kfree(r.ptr);
+
+ return ret;
+}
+
+/* Get the current bit rate */
+static int
+prism54_get_rate(struct net_device *ndev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int rvalue;
+ char *data;
+ union oid_res_t r;
+
+ /* Get the current bit rate */
+ if ((rvalue = mgt_get_request(priv, GEN_OID_LINKSTATE, 0, NULL, &r)))
+ return rvalue;
+ vwrq->value = r.u * 500000;
+
+ /* request the device for the enabled rates */
+ rvalue = mgt_get_request(priv, DOT11_OID_RATES, 0, NULL, &r);
+ if (rvalue) {
+ kfree(r.ptr);
+ return rvalue;
+ }
+ data = r.ptr;
+ vwrq->fixed = (data[0] != 0) && (data[1] == 0);
+ kfree(r.ptr);
+
+ return 0;
+}
+
+static int
+prism54_set_rts(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ return mgt_set_request(priv, DOT11_OID_RTSTHRESH, 0, &vwrq->value);
+}
+
+static int
+prism54_get_rts(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ union oid_res_t r;
+ int rvalue;
+
+ /* get the rts threshold */
+ rvalue = mgt_get_request(priv, DOT11_OID_RTSTHRESH, 0, NULL, &r);
+ vwrq->value = r.u;
+
+ return rvalue;
+}
+
+static int
+prism54_set_frag(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ return mgt_set_request(priv, DOT11_OID_FRAGTHRESH, 0, &vwrq->value);
+}
+
+static int
+prism54_get_frag(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ union oid_res_t r;
+ int rvalue;
+
+ rvalue = mgt_get_request(priv, DOT11_OID_FRAGTHRESH, 0, NULL, &r);
+ vwrq->value = r.u;
+
+ return rvalue;
+}
+
+/* Here we have (min,max) = max retries for (small frames, big frames). Where
+ * big frame <=> bigger than the rts threshold
+ * small frame <=> smaller than the rts threshold
+ * This is not really the behavior expected by the wireless tool but it seems
+ * to be a common behavior in other drivers.
+ */
+
+static int
+prism54_set_retry(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ u32 slimit = 0, llimit = 0; /* short and long limit */
+ u32 lifetime = 0;
+ int rvalue = 0;
+
+ if (vwrq->disabled)
+ /* we cannot disable this feature */
+ return -EINVAL;
+
+ if (vwrq->flags & IW_RETRY_LIMIT) {
+ if (vwrq->flags & IW_RETRY_SHORT)
+ slimit = vwrq->value;
+ else if (vwrq->flags & IW_RETRY_LONG)
+ llimit = vwrq->value;
+ else {
+ /* we are asked to set both */
+ slimit = vwrq->value;
+ llimit = vwrq->value;
+ }
+ }
+ if (vwrq->flags & IW_RETRY_LIFETIME)
+ /* Wireless tools use us unit while the device uses 1024 us unit */
+ lifetime = vwrq->value / 1024;
+
+ /* now set what is requested */
+ if (slimit)
+ rvalue =
+ mgt_set_request(priv, DOT11_OID_SHORTRETRIES, 0, &slimit);
+ if (llimit)
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_LONGRETRIES, 0, &llimit);
+ if (lifetime)
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_MAXTXLIFETIME, 0,
+ &lifetime);
+ return rvalue;
+}
+
+static int
+prism54_get_retry(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ union oid_res_t r;
+ int rvalue = 0;
+ vwrq->disabled = 0; /* It cannot be disabled */
+
+ if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+ /* we are asked for the life time */
+ rvalue =
+ mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r);
+ vwrq->value = r.u * 1024;
+ vwrq->flags = IW_RETRY_LIFETIME;
+ } else if ((vwrq->flags & IW_RETRY_LONG)) {
+ /* we are asked for the long retry limit */
+ rvalue |=
+ mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r);
+ vwrq->value = r.u;
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ } else {
+ /* default. get the short retry limit */
+ rvalue |=
+ mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r);
+ vwrq->value = r.u;
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
+ }
+
+ return rvalue;
+}
+
+static int
+prism54_set_encode(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int rvalue = 0, force = 0;
+ int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
+ union oid_res_t r;
+
+ /* with the new API, it's impossible to get a NULL pointer.
+ * New version of iwconfig set the IW_ENCODE_NOKEY flag
+ * when no key is given, but older versions don't. */
+
+ if (dwrq->length > 0) {
+ /* we have a key to set */
+ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ int current_index;
+ struct obj_key key = { DOT11_PRIV_WEP, 0, "" };
+
+ /* get the current key index */
+ rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
+ current_index = r.u;
+ /* Verify that the key is not marked as invalid */
+ if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
+ if (dwrq->length > KEY_SIZE_TKIP) {
+ /* User-provided key data too big */
+ return -EINVAL;
+ }
+ if (dwrq->length > KEY_SIZE_WEP104) {
+ /* WPA-PSK TKIP */
+ key.type = DOT11_PRIV_TKIP;
+ key.length = KEY_SIZE_TKIP;
+ } else if (dwrq->length > KEY_SIZE_WEP40) {
+ /* WEP 104/128 */
+ key.length = KEY_SIZE_WEP104;
+ } else {
+ /* WEP 40/64 */
+ key.length = KEY_SIZE_WEP40;
+ }
+ memset(key.key, 0, sizeof (key.key));
+ memcpy(key.key, extra, dwrq->length);
+
+ if ((index < 0) || (index > 3))
+ /* no index provided use the current one */
+ index = current_index;
+
+ /* now send the key to the card */
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_DEFKEYX, index,
+ &key);
+ }
+ /*
+ * If a valid key is set, encryption should be enabled
+ * (user may turn it off later).
+ * This is also how "iwconfig ethX key on" works
+ */
+ if ((index == current_index) && (key.length > 0))
+ force = 1;
+ } else {
+ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ if ((index >= 0) && (index <= 3)) {
+ /* we want to set the key index */
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_DEFKEYID, 0,
+ &index);
+ } else {
+ if (!(dwrq->flags & IW_ENCODE_MODE)) {
+ /* we cannot do anything. Complain. */
+ return -EINVAL;
+ }
+ }
+ }
+ /* now read the flags */
+ if (dwrq->flags & IW_ENCODE_DISABLED) {
+ /* Encoding disabled,
+ * authen = DOT11_AUTH_OS;
+ * invoke = 0;
+ * exunencrypt = 0; */
+ }
+ if (dwrq->flags & IW_ENCODE_OPEN)
+ /* Encode but accept non-encoded packets. No auth */
+ invoke = 1;
+ if ((dwrq->flags & IW_ENCODE_RESTRICTED) || force) {
+ /* Refuse non-encoded packets. Auth */
+ authen = DOT11_AUTH_BOTH;
+ invoke = 1;
+ exunencrypt = 1;
+ }
+ /* do the change if requested */
+ if ((dwrq->flags & IW_ENCODE_MODE) || force) {
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke);
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
+ &exunencrypt);
+ }
+ return rvalue;
+}
+
+static int
+prism54_get_encode(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct obj_key *key;
+ u32 devindex, index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ u32 authen = 0, invoke = 0, exunencrypt = 0;
+ int rvalue;
+ union oid_res_t r;
+
+ /* first get the flags */
+ rvalue = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
+ authen = r.u;
+ rvalue |= mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
+ invoke = r.u;
+ rvalue |= mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
+ exunencrypt = r.u;
+
+ if (invoke && (authen == DOT11_AUTH_BOTH) && exunencrypt)
+ dwrq->flags = IW_ENCODE_RESTRICTED;
+ else if ((authen == DOT11_AUTH_OS) && !exunencrypt) {
+ if (invoke)
+ dwrq->flags = IW_ENCODE_OPEN;
+ else
+ dwrq->flags = IW_ENCODE_DISABLED;
+ } else
+ /* The card should not work in this state */
+ dwrq->flags = 0;
+
+ /* get the current device key index */
+ rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
+ devindex = r.u;
+ /* Now get the key, return it */
+ if (index == -1 || index > 3)
+ /* no index provided, use the current one */
+ index = devindex;
+ rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r);
+ key = r.ptr;
+ dwrq->length = key->length;
+ memcpy(extra, key->key, dwrq->length);
+ kfree(key);
+ /* return the used key index */
+ dwrq->flags |= devindex + 1;
+
+ return rvalue;
+}
+
+static int
+prism54_get_txpower(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ union oid_res_t r;
+ int rvalue;
+
+ rvalue = mgt_get_request(priv, OID_INL_OUTPUTPOWER, 0, NULL, &r);
+ /* intersil firmware operates in 0.25 dBm (1/4 dBm) */
+ vwrq->value = (s32) r.u / 4;
+ vwrq->fixed = 1;
+ /* radio is not turned of
+ * btw: how is possible to turn off only the radio
+ */
+ vwrq->disabled = 0;
+
+ return rvalue;
+}
+
+static int
+prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_param *vwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ s32 u = vwrq->value;
+
+ /* intersil firmware operates in 0.25 dBm (1/4) */
+ u *= 4;
+ if (vwrq->disabled) {
+ /* don't know how to disable radio */
+ printk(KERN_DEBUG
+ "%s: %s() disabling radio is not yet supported.\n",
+ priv->ndev->name, __func__);
+ return -ENOTSUPP;
+ } else if (vwrq->fixed)
+ /* currently only fixed value is supported */
+ return mgt_set_request(priv, OID_INL_OUTPUTPOWER, 0, &u);
+ else {
+ printk(KERN_DEBUG
+ "%s: %s() auto power will be implemented later.\n",
+ priv->ndev->name, __func__);
+ return -ENOTSUPP;
+ }
+}
+
+static int prism54_set_genie(struct net_device *ndev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int alen, ret = 0;
+ struct obj_attachment *attach;
+
+ if (data->length > MAX_WPA_IE_LEN ||
+ (data->length && extra == NULL))
+ return -EINVAL;
+
+ memcpy(priv->wpa_ie, extra, data->length);
+ priv->wpa_ie_len = data->length;
+
+ alen = sizeof(*attach) + priv->wpa_ie_len;
+ attach = kzalloc(alen, GFP_KERNEL);
+ if (attach == NULL)
+ return -ENOMEM;
+
+#define WLAN_FC_TYPE_MGMT 0
+#define WLAN_FC_STYPE_ASSOC_REQ 0
+#define WLAN_FC_STYPE_REASSOC_REQ 2
+
+ /* Note: endianness is covered by mgt_set_varlen */
+ attach->type = (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_ASSOC_REQ << 4);
+ attach->id = -1;
+ attach->size = priv->wpa_ie_len;
+ memcpy(attach->data, extra, priv->wpa_ie_len);
+
+ ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach,
+ priv->wpa_ie_len);
+ if (ret == 0) {
+ attach->type = (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_REASSOC_REQ << 4);
+
+ ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach,
+ priv->wpa_ie_len);
+ if (ret == 0)
+ printk(KERN_DEBUG "%s: WPA IE Attachment was set\n",
+ ndev->name);
+ }
+
+ kfree(attach);
+ return ret;
+}
+
+
+static int prism54_get_genie(struct net_device *ndev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int len = priv->wpa_ie_len;
+
+ if (len <= 0) {
+ data->length = 0;
+ return 0;
+ }
+
+ if (data->length < len)
+ return -E2BIG;
+
+ data->length = len;
+ memcpy(extra, priv->wpa_ie, len);
+
+ return 0;
+}
+
+static int prism54_set_auth(struct net_device *ndev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct iw_param *param = &wrqu->param;
+ u32 mlmelevel = 0, authen = 0, dot1x = 0;
+ u32 exunencrypt = 0, privinvoked = 0, wpa = 0;
+ u32 old_wpa;
+ int ret = 0;
+ union oid_res_t r;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return 0;
+
+ /* first get the flags */
+ down_write(&priv->mib_sem);
+ wpa = old_wpa = priv->wpa;
+ up_write(&priv->mib_sem);
+ ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
+ authen = r.u;
+ ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
+ privinvoked = r.u;
+ ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
+ exunencrypt = r.u;
+ ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r);
+ dot1x = r.u;
+ ret = mgt_get_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, NULL, &r);
+ mlmelevel = r.u;
+
+ if (ret < 0)
+ goto out;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ /* Do the same thing as IW_AUTH_WPA_VERSION */
+ if (param->value) {
+ wpa = 1;
+ privinvoked = 1; /* For privacy invoked */
+ exunencrypt = 1; /* Filter out all unencrypted frames */
+ dot1x = 0x01; /* To enable eap filter */
+ mlmelevel = DOT11_MLME_EXTENDED;
+ authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */
+ } else {
+ wpa = 0;
+ privinvoked = 0;
+ exunencrypt = 0; /* Do not filter un-encrypted data */
+ dot1x = 0;
+ mlmelevel = DOT11_MLME_AUTO;
+ }
+ break;
+
+ case IW_AUTH_WPA_VERSION:
+ if (param->value & IW_AUTH_WPA_VERSION_DISABLED) {
+ wpa = 0;
+ privinvoked = 0;
+ exunencrypt = 0; /* Do not filter un-encrypted data */
+ dot1x = 0;
+ mlmelevel = DOT11_MLME_AUTO;
+ } else {
+ if (param->value & IW_AUTH_WPA_VERSION_WPA)
+ wpa = 1;
+ else if (param->value & IW_AUTH_WPA_VERSION_WPA2)
+ wpa = 2;
+ privinvoked = 1; /* For privacy invoked */
+ exunencrypt = 1; /* Filter out all unencrypted frames */
+ dot1x = 0x01; /* To enable eap filter */
+ mlmelevel = DOT11_MLME_EXTENDED;
+ authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */
+ }
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ /* dot1x should be the opposite of RX_UNENCRYPTED_EAPOL;
+ * turn off dot1x when allowing receipt of unencrypted EAPOL
+ * frames, turn on dot1x when receipt should be disallowed
+ */
+ dot1x = param->value ? 0 : 0x01;
+ break;
+
+ case IW_AUTH_PRIVACY_INVOKED:
+ privinvoked = param->value ? 1 : 0;
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ exunencrypt = param->value ? 1 : 0;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (param->value & IW_AUTH_ALG_SHARED_KEY) {
+ /* Only WEP uses _SK and _BOTH */
+ if (wpa > 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ authen = DOT11_AUTH_SK;
+ } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ authen = DOT11_AUTH_OS;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Set all the values */
+ down_write(&priv->mib_sem);
+ priv->wpa = wpa;
+ up_write(&priv->mib_sem);
+ mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
+ mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &privinvoked);
+ mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &exunencrypt);
+ mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x);
+ mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlmelevel);
+
+out:
+ return ret;
+}
+
+static int prism54_get_auth(struct net_device *ndev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct iw_param *param = &wrqu->param;
+ u32 wpa = 0;
+ int ret = 0;
+ union oid_res_t r;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return 0;
+
+ /* first get the flags */
+ down_write(&priv->mib_sem);
+ wpa = priv->wpa;
+ up_write(&priv->mib_sem);
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * wpa_supplicant will control these internally
+ */
+ ret = -EOPNOTSUPP;
+ break;
+
+ case IW_AUTH_WPA_VERSION:
+ switch (wpa) {
+ case 1:
+ param->value = IW_AUTH_WPA_VERSION_WPA;
+ break;
+ case 2:
+ param->value = IW_AUTH_WPA_VERSION_WPA2;
+ break;
+ case 0:
+ default:
+ param->value = IW_AUTH_WPA_VERSION_DISABLED;
+ break;
+ }
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
+ if (ret >= 0)
+ param->value = r.u > 0 ? 1 : 0;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
+ if (ret >= 0) {
+ switch (r.u) {
+ case DOT11_AUTH_OS:
+ param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+ break;
+ case DOT11_AUTH_BOTH:
+ case DOT11_AUTH_SK:
+ param->value = IW_AUTH_ALG_SHARED_KEY;
+ break;
+ case DOT11_AUTH_NONE:
+ default:
+ param->value = 0;
+ break;
+ }
+ }
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = wpa > 0 ? 1 : 0;
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r);
+ if (ret >= 0)
+ param->value = r.u > 0 ? 1 : 0;
+ break;
+
+ case IW_AUTH_PRIVACY_INVOKED:
+ ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
+ if (ret >= 0)
+ param->value = r.u > 0 ? 1 : 0;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int prism54_set_encodeext(struct net_device *ndev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, alg = ext->alg, set_key = 1;
+ union oid_res_t r;
+ int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
+ int ret = 0;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return 0;
+
+ /* Determine and validate the key index */
+ idx = (encoding->flags & IW_ENCODE_INDEX) - 1;
+ if (idx) {
+ if (idx < 0 || idx > 3)
+ return -EINVAL;
+ } else {
+ ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
+ if (ret < 0)
+ goto out;
+ idx = r.u;
+ }
+
+ if (encoding->flags & IW_ENCODE_DISABLED)
+ alg = IW_ENCODE_ALG_NONE;
+
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ /* Only set transmit key index here, actual
+ * key is set below if needed.
+ */
+ ret = mgt_set_request(priv, DOT11_OID_DEFKEYID, 0, &idx);
+ set_key = ext->key_len > 0 ? 1 : 0;
+ }
+
+ if (set_key) {
+ struct obj_key key = { DOT11_PRIV_WEP, 0, "" };
+ switch (alg) {
+ case IW_ENCODE_ALG_NONE:
+ break;
+ case IW_ENCODE_ALG_WEP:
+ if (ext->key_len > KEY_SIZE_WEP104) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (ext->key_len > KEY_SIZE_WEP40)
+ key.length = KEY_SIZE_WEP104;
+ else
+ key.length = KEY_SIZE_WEP40;
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ if (ext->key_len > KEY_SIZE_TKIP) {
+ ret = -EINVAL;
+ goto out;
+ }
+ key.type = DOT11_PRIV_TKIP;
+ key.length = KEY_SIZE_TKIP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (key.length) {
+ memset(key.key, 0, sizeof(key.key));
+ memcpy(key.key, ext->key, ext->key_len);
+ ret = mgt_set_request(priv, DOT11_OID_DEFKEYX, idx,
+ &key);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+ /* Read the flags */
+ if (encoding->flags & IW_ENCODE_DISABLED) {
+ /* Encoding disabled,
+ * authen = DOT11_AUTH_OS;
+ * invoke = 0;
+ * exunencrypt = 0; */
+ }
+ if (encoding->flags & IW_ENCODE_OPEN) {
+ /* Encode but accept non-encoded packets. No auth */
+ invoke = 1;
+ }
+ if (encoding->flags & IW_ENCODE_RESTRICTED) {
+ /* Refuse non-encoded packets. Auth */
+ authen = DOT11_AUTH_BOTH;
+ invoke = 1;
+ exunencrypt = 1;
+ }
+
+ /* do the change if requested */
+ if (encoding->flags & IW_ENCODE_MODE) {
+ ret = mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0,
+ &authen);
+ ret = mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0,
+ &invoke);
+ ret = mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
+ &exunencrypt);
+ }
+
+out:
+ return ret;
+}
+
+
+static int prism54_get_encodeext(struct net_device *ndev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, max_key_len;
+ union oid_res_t r;
+ int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0, wpa = 0;
+ int ret = 0;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return 0;
+
+ /* first get the flags */
+ ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
+ authen = r.u;
+ ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
+ invoke = r.u;
+ ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
+ exunencrypt = r.u;
+ if (ret < 0)
+ goto out;
+
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ return -EINVAL;
+
+ idx = (encoding->flags & IW_ENCODE_INDEX) - 1;
+ if (idx) {
+ if (idx < 0 || idx > 3)
+ return -EINVAL;
+ } else {
+ ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
+ if (ret < 0)
+ goto out;
+ idx = r.u;
+ }
+
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ switch (authen) {
+ case DOT11_AUTH_BOTH:
+ case DOT11_AUTH_SK:
+ wrqu->encoding.flags |= IW_ENCODE_RESTRICTED;
+ case DOT11_AUTH_OS:
+ default:
+ wrqu->encoding.flags |= IW_ENCODE_OPEN;
+ break;
+ }
+
+ down_write(&priv->mib_sem);
+ wpa = priv->wpa;
+ up_write(&priv->mib_sem);
+
+ if (authen == DOT11_AUTH_OS && !exunencrypt && !invoke && !wpa) {
+ /* No encryption */
+ ext->alg = IW_ENCODE_ALG_NONE;
+ ext->key_len = 0;
+ wrqu->encoding.flags |= IW_ENCODE_DISABLED;
+ } else {
+ struct obj_key *key;
+
+ ret = mgt_get_request(priv, DOT11_OID_DEFKEYX, idx, NULL, &r);
+ if (ret < 0)
+ goto out;
+ key = r.ptr;
+ if (max_key_len < key->length) {
+ ret = -E2BIG;
+ goto out;
+ }
+ memcpy(ext->key, key->key, key->length);
+ ext->key_len = key->length;
+
+ switch (key->type) {
+ case DOT11_PRIV_TKIP:
+ ext->alg = IW_ENCODE_ALG_TKIP;
+ break;
+ default:
+ case DOT11_PRIV_WEP:
+ ext->alg = IW_ENCODE_ALG_WEP;
+ break;
+ }
+ wrqu->encoding.flags |= IW_ENCODE_ENABLED;
+ }
+
+out:
+ return ret;
+}
+
+
+static int
+prism54_reset(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_reset(netdev_priv(ndev), 0);
+
+ return 0;
+}
+
+static int
+prism54_get_oid(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ union oid_res_t r;
+ int rvalue;
+ enum oid_num_t n = dwrq->flags;
+
+ rvalue = mgt_get_request(netdev_priv(ndev), n, 0, NULL, &r);
+ dwrq->length = mgt_response_to_str(n, &r, extra);
+ if ((isl_oid[n].flags & OID_FLAG_TYPE) != OID_TYPE_U32)
+ kfree(r.ptr);
+ return rvalue;
+}
+
+static int
+prism54_set_u32(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ u32 oid = uwrq[0], u = uwrq[1];
+
+ return mgt_set_request(netdev_priv(ndev), oid, 0, &u);
+}
+
+static int
+prism54_set_raw(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ u32 oid = dwrq->flags;
+
+ return mgt_set_request(netdev_priv(ndev), oid, 0, extra);
+}
+
+void
+prism54_acl_init(struct islpci_acl *acl)
+{
+ mutex_init(&acl->lock);
+ INIT_LIST_HEAD(&acl->mac_list);
+ acl->size = 0;
+ acl->policy = MAC_POLICY_OPEN;
+}
+
+static void
+prism54_clear_mac(struct islpci_acl *acl)
+{
+ struct list_head *ptr, *next;
+ struct mac_entry *entry;
+
+ mutex_lock(&acl->lock);
+
+ if (acl->size == 0) {
+ mutex_unlock(&acl->lock);
+ return;
+ }
+
+ for (ptr = acl->mac_list.next, next = ptr->next;
+ ptr != &acl->mac_list; ptr = next, next = ptr->next) {
+ entry = list_entry(ptr, struct mac_entry, _list);
+ list_del(ptr);
+ kfree(entry);
+ }
+ acl->size = 0;
+ mutex_unlock(&acl->lock);
+}
+
+void
+prism54_acl_clean(struct islpci_acl *acl)
+{
+ prism54_clear_mac(acl);
+}
+
+static int
+prism54_add_mac(struct net_device *ndev, struct iw_request_info *info,
+ struct sockaddr *awrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct islpci_acl *acl = &priv->acl;
+ struct mac_entry *entry;
+ struct sockaddr *addr = (struct sockaddr *) extra;
+
+ if (addr->sa_family != ARPHRD_ETHER)
+ return -EOPNOTSUPP;
+
+ entry = kmalloc(sizeof (struct mac_entry), GFP_KERNEL);
+ if (entry == NULL)
+ return -ENOMEM;
+
+ memcpy(entry->addr, addr->sa_data, ETH_ALEN);
+
+ if (mutex_lock_interruptible(&acl->lock)) {
+ kfree(entry);
+ return -ERESTARTSYS;
+ }
+ list_add_tail(&entry->_list, &acl->mac_list);
+ acl->size++;
+ mutex_unlock(&acl->lock);
+
+ return 0;
+}
+
+static int
+prism54_del_mac(struct net_device *ndev, struct iw_request_info *info,
+ struct sockaddr *awrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct islpci_acl *acl = &priv->acl;
+ struct mac_entry *entry;
+ struct sockaddr *addr = (struct sockaddr *) extra;
+
+ if (addr->sa_family != ARPHRD_ETHER)
+ return -EOPNOTSUPP;
+
+ if (mutex_lock_interruptible(&acl->lock))
+ return -ERESTARTSYS;
+ list_for_each_entry(entry, &acl->mac_list, _list) {
+ if (ether_addr_equal(entry->addr, addr->sa_data)) {
+ list_del(&entry->_list);
+ acl->size--;
+ kfree(entry);
+ mutex_unlock(&acl->lock);
+ return 0;
+ }
+ }
+ mutex_unlock(&acl->lock);
+ return -EINVAL;
+}
+
+static int
+prism54_get_mac(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct islpci_acl *acl = &priv->acl;
+ struct mac_entry *entry;
+ struct sockaddr *dst = (struct sockaddr *) extra;
+
+ dwrq->length = 0;
+
+ if (mutex_lock_interruptible(&acl->lock))
+ return -ERESTARTSYS;
+
+ list_for_each_entry(entry, &acl->mac_list, _list) {
+ memcpy(dst->sa_data, entry->addr, ETH_ALEN);
+ dst->sa_family = ARPHRD_ETHER;
+ dwrq->length++;
+ dst++;
+ }
+ mutex_unlock(&acl->lock);
+ return 0;
+}
+
+/* Setting policy also clears the MAC acl, even if we don't change the default
+ * policy
+ */
+
+static int
+prism54_set_policy(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct islpci_acl *acl = &priv->acl;
+ u32 mlmeautolevel;
+
+ prism54_clear_mac(acl);
+
+ if ((*uwrq < MAC_POLICY_OPEN) || (*uwrq > MAC_POLICY_REJECT))
+ return -EINVAL;
+
+ down_write(&priv->mib_sem);
+
+ acl->policy = *uwrq;
+
+ /* the ACL code needs an intermediate mlmeautolevel */
+ if ((priv->iw_mode == IW_MODE_MASTER) &&
+ (acl->policy != MAC_POLICY_OPEN))
+ mlmeautolevel = DOT11_MLME_INTERMEDIATE;
+ else
+ mlmeautolevel = CARD_DEFAULT_MLME_MODE;
+ if (priv->wpa)
+ mlmeautolevel = DOT11_MLME_EXTENDED;
+ mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
+ /* restart the card with our new policy */
+ if (mgt_commit(priv)) {
+ up_write(&priv->mib_sem);
+ return -EIO;
+ }
+ up_write(&priv->mib_sem);
+
+ return 0;
+}
+
+static int
+prism54_get_policy(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct islpci_acl *acl = &priv->acl;
+
+ *uwrq = acl->policy;
+
+ return 0;
+}
+
+/* Return 1 only if client should be accepted. */
+
+static int
+prism54_mac_accept(struct islpci_acl *acl, char *mac)
+{
+ struct mac_entry *entry;
+ int res = 0;
+
+ if (mutex_lock_interruptible(&acl->lock))
+ return -ERESTARTSYS;
+
+ if (acl->policy == MAC_POLICY_OPEN) {
+ mutex_unlock(&acl->lock);
+ return 1;
+ }
+
+ list_for_each_entry(entry, &acl->mac_list, _list) {
+ if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
+ res = 1;
+ break;
+ }
+ }
+ res = (acl->policy == MAC_POLICY_ACCEPT) ? !res : res;
+ mutex_unlock(&acl->lock);
+
+ return res;
+}
+
+static int
+prism54_kick_all(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra)
+{
+ struct obj_mlme *mlme;
+ int rvalue;
+
+ mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
+ if (mlme == NULL)
+ return -ENOMEM;
+
+ /* Tell the card to kick every client */
+ mlme->id = 0;
+ rvalue =
+ mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
+ kfree(mlme);
+
+ return rvalue;
+}
+
+static int
+prism54_kick_mac(struct net_device *ndev, struct iw_request_info *info,
+ struct sockaddr *awrq, char *extra)
+{
+ struct obj_mlme *mlme;
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ int rvalue;
+
+ if (addr->sa_family != ARPHRD_ETHER)
+ return -EOPNOTSUPP;
+
+ mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
+ if (mlme == NULL)
+ return -ENOMEM;
+
+ /* Tell the card to only kick the corresponding bastard */
+ memcpy(mlme->address, addr->sa_data, ETH_ALEN);
+ mlme->id = -1;
+ rvalue =
+ mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
+
+ kfree(mlme);
+
+ return rvalue;
+}
+
+/* Translate a TRAP oid into a wireless event. Called in islpci_mgt_receive. */
+
+static void
+format_event(islpci_private *priv, char *dest, const char *str,
+ const struct obj_mlme *mlme, u16 *length, int error)
+{
+ int n = snprintf(dest, IW_CUSTOM_MAX,
+ "%s %s %pM %s (%2.2X)",
+ str,
+ ((priv->iw_mode == IW_MODE_MASTER) ? "from" : "to"),
+ mlme->address,
+ (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")
+ : ""), mlme->code);
+ WARN_ON(n >= IW_CUSTOM_MAX);
+ *length = n;
+}
+
+static void
+send_formatted_event(islpci_private *priv, const char *str,
+ const struct obj_mlme *mlme, int error)
+{
+ union iwreq_data wrqu;
+ char *memptr;
+
+ memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
+ if (!memptr)
+ return;
+ wrqu.data.pointer = memptr;
+ wrqu.data.length = 0;
+ format_event(priv, memptr, str, mlme, &wrqu.data.length,
+ error);
+ wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr);
+ kfree(memptr);
+}
+
+static void
+send_simple_event(islpci_private *priv, const char *str)
+{
+ union iwreq_data wrqu;
+ char *memptr;
+ int n = strlen(str);
+
+ memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
+ if (!memptr)
+ return;
+ BUG_ON(n >= IW_CUSTOM_MAX);
+ wrqu.data.pointer = memptr;
+ wrqu.data.length = n;
+ strcpy(memptr, str);
+ wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr);
+ kfree(memptr);
+}
+
+static void
+link_changed(struct net_device *ndev, u32 bitrate)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ if (bitrate) {
+ netif_carrier_on(ndev);
+ if (priv->iw_mode == IW_MODE_INFRA) {
+ union iwreq_data uwrq;
+ prism54_get_wap(ndev, NULL, (struct sockaddr *) &uwrq,
+ NULL);
+ wireless_send_event(ndev, SIOCGIWAP, &uwrq, NULL);
+ } else
+ send_simple_event(netdev_priv(ndev),
+ "Link established");
+ } else {
+ netif_carrier_off(ndev);
+ send_simple_event(netdev_priv(ndev), "Link lost");
+ }
+}
+
+/* Beacon/ProbeResp payload header */
+struct ieee80211_beacon_phdr {
+ u8 timestamp[8];
+ u16 beacon_int;
+ u16 capab_info;
+} __packed;
+
+#define WLAN_EID_GENERIC 0xdd
+static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 };
+
+static void
+prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid,
+ u8 *wpa_ie, size_t wpa_ie_len)
+{
+ struct list_head *ptr;
+ struct islpci_bss_wpa_ie *bss = NULL;
+
+ if (wpa_ie_len > MAX_WPA_IE_LEN)
+ wpa_ie_len = MAX_WPA_IE_LEN;
+
+ mutex_lock(&priv->wpa_lock);
+
+ /* try to use existing entry */
+ list_for_each(ptr, &priv->bss_wpa_list) {
+ bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
+ if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {
+ list_move(&bss->list, &priv->bss_wpa_list);
+ break;
+ }
+ bss = NULL;
+ }
+
+ if (bss == NULL) {
+ /* add a new BSS entry; if max number of entries is already
+ * reached, replace the least recently updated */
+ if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) {
+ bss = list_entry(priv->bss_wpa_list.prev,
+ struct islpci_bss_wpa_ie, list);
+ list_del(&bss->list);
+ } else {
+ bss = kzalloc(sizeof (*bss), GFP_ATOMIC);
+ if (bss != NULL)
+ priv->num_bss_wpa++;
+ }
+ if (bss != NULL) {
+ memcpy(bss->bssid, bssid, ETH_ALEN);
+ list_add(&bss->list, &priv->bss_wpa_list);
+ }
+ }
+
+ if (bss != NULL) {
+ memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);
+ bss->wpa_ie_len = wpa_ie_len;
+ bss->last_update = jiffies;
+ } else {
+ printk(KERN_DEBUG "Failed to add BSS WPA entry for "
+ "%pM\n", bssid);
+ }
+
+ /* expire old entries from WPA list */
+ while (priv->num_bss_wpa > 0) {
+ bss = list_entry(priv->bss_wpa_list.prev,
+ struct islpci_bss_wpa_ie, list);
+ if (!time_after(jiffies, bss->last_update + 60 * HZ))
+ break;
+
+ list_del(&bss->list);
+ priv->num_bss_wpa--;
+ kfree(bss);
+ }
+
+ mutex_unlock(&priv->wpa_lock);
+}
+
+static size_t
+prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie)
+{
+ struct list_head *ptr;
+ struct islpci_bss_wpa_ie *bss = NULL;
+ size_t len = 0;
+
+ mutex_lock(&priv->wpa_lock);
+
+ list_for_each(ptr, &priv->bss_wpa_list) {
+ bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
+ if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ break;
+ bss = NULL;
+ }
+ if (bss) {
+ len = bss->wpa_ie_len;
+ memcpy(wpa_ie, bss->wpa_ie, len);
+ }
+ mutex_unlock(&priv->wpa_lock);
+
+ return len;
+}
+
+void
+prism54_wpa_bss_ie_init(islpci_private *priv)
+{
+ INIT_LIST_HEAD(&priv->bss_wpa_list);
+ mutex_init(&priv->wpa_lock);
+}
+
+void
+prism54_wpa_bss_ie_clean(islpci_private *priv)
+{
+ struct islpci_bss_wpa_ie *bss, *n;
+
+ list_for_each_entry_safe(bss, n, &priv->bss_wpa_list, list) {
+ kfree(bss);
+ }
+}
+
+static void
+prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
+ u8 *payload, size_t len)
+{
+ struct ieee80211_beacon_phdr *hdr;
+ u8 *pos, *end;
+
+ if (!priv->wpa)
+ return;
+
+ hdr = (struct ieee80211_beacon_phdr *) payload;
+ pos = (u8 *) (hdr + 1);
+ end = payload + len;
+ while (pos < end) {
+ if (pos + 2 + pos[1] > end) {
+ printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed "
+ "for %pM\n", addr);
+ return;
+ }
+ if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 &&
+ memcmp(pos + 2, wpa_oid, 4) == 0) {
+ prism54_wpa_bss_ie_add(priv, addr, pos, pos[1] + 2);
+ return;
+ }
+ pos += 2 + pos[1];
+ }
+}
+
+static void
+handle_request(islpci_private *priv, struct obj_mlme *mlme, enum oid_num_t oid)
+{
+ if (((mlme->state == DOT11_STATE_AUTHING) ||
+ (mlme->state == DOT11_STATE_ASSOCING))
+ && mgt_mlme_answer(priv)) {
+ /* Someone is requesting auth and we must respond. Just send back
+ * the trap with error code set accordingly.
+ */
+ mlme->code = prism54_mac_accept(&priv->acl,
+ mlme->address) ? 0 : 1;
+ mgt_set_request(priv, oid, 0, mlme);
+ }
+}
+
+static int
+prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
+ char *data)
+{
+ struct obj_mlme *mlme = (struct obj_mlme *) data;
+ struct obj_mlmeex *mlmeex = (struct obj_mlmeex *) data;
+ struct obj_mlmeex *confirm;
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ int wpa_ie_len;
+ size_t len = 0; /* u16, better? */
+ u8 *payload = NULL, *pos = NULL;
+ int ret;
+
+ /* I think all trapable objects are listed here.
+ * Some oids have a EX version. The difference is that they are emitted
+ * in DOT11_MLME_EXTENDED mode (set with DOT11_OID_MLMEAUTOLEVEL)
+ * with more info.
+ * The few events already defined by the wireless tools are not really
+ * suited. We use the more flexible custom event facility.
+ */
+
+ if (oid >= DOT11_OID_BEACON) {
+ len = mlmeex->size;
+ payload = pos = mlmeex->data;
+ }
+
+ /* I fear prism54_process_bss_data won't work with big endian data */
+ if ((oid == DOT11_OID_BEACON) || (oid == DOT11_OID_PROBE))
+ prism54_process_bss_data(priv, oid, mlmeex->address,
+ payload, len);
+
+ mgt_le_to_cpu(isl_oid[oid].flags & OID_FLAG_TYPE, (void *) mlme);
+
+ switch (oid) {
+
+ case GEN_OID_LINKSTATE:
+ link_changed(priv->ndev, (u32) *data);
+ break;
+
+ case DOT11_OID_MICFAILURE:
+ send_simple_event(priv, "Mic failure");
+ break;
+
+ case DOT11_OID_DEAUTHENTICATE:
+ send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
+ break;
+
+ case DOT11_OID_AUTHENTICATE:
+ handle_request(priv, mlme, oid);
+ send_formatted_event(priv, "Authenticate request", mlme, 1);
+ break;
+
+ case DOT11_OID_DISASSOCIATE:
+ send_formatted_event(priv, "Disassociate request", mlme, 0);
+ break;
+
+ case DOT11_OID_ASSOCIATE:
+ handle_request(priv, mlme, oid);
+ send_formatted_event(priv, "Associate request", mlme, 1);
+ break;
+
+ case DOT11_OID_REASSOCIATE:
+ handle_request(priv, mlme, oid);
+ send_formatted_event(priv, "ReAssociate request", mlme, 1);
+ break;
+
+ case DOT11_OID_BEACON:
+ send_formatted_event(priv,
+ "Received a beacon from an unknown AP",
+ mlme, 0);
+ break;
+
+ case DOT11_OID_PROBE:
+ /* we received a probe from a client. */
+ send_formatted_event(priv, "Received a probe from client", mlme,
+ 0);
+ break;
+
+ /* Note : "mlme" is actually a "struct obj_mlmeex *" here, but this
+ * is backward compatible layout-wise with "struct obj_mlme".
+ */
+
+ case DOT11_OID_DEAUTHENTICATEEX:
+ send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
+ break;
+
+ case DOT11_OID_AUTHENTICATEEX:
+ handle_request(priv, mlme, oid);
+ send_formatted_event(priv, "Authenticate request (ex)", mlme, 1);
+
+ if (priv->iw_mode != IW_MODE_MASTER
+ && mlmeex->state != DOT11_STATE_AUTHING)
+ break;
+
+ confirm = kmalloc(sizeof(struct obj_mlmeex) + 6, GFP_ATOMIC);
+
+ if (!confirm)
+ break;
+
+ memcpy(&confirm->address, mlmeex->address, ETH_ALEN);
+ printk(KERN_DEBUG "Authenticate from: address:\t%pM\n",
+ mlmeex->address);
+ confirm->id = -1; /* or mlmeex->id ? */
+ confirm->state = 0; /* not used */
+ confirm->code = 0;
+ confirm->size = 6;
+ confirm->data[0] = 0x00;
+ confirm->data[1] = 0x00;
+ confirm->data[2] = 0x02;
+ confirm->data[3] = 0x00;
+ confirm->data[4] = 0x00;
+ confirm->data[5] = 0x00;
+
+ ret = mgt_set_varlen(priv, DOT11_OID_ASSOCIATEEX, confirm, 6);
+
+ kfree(confirm);
+ if (ret)
+ return ret;
+ break;
+
+ case DOT11_OID_DISASSOCIATEEX:
+ send_formatted_event(priv, "Disassociate request (ex)", mlme, 0);
+ break;
+
+ case DOT11_OID_ASSOCIATEEX:
+ handle_request(priv, mlme, oid);
+ send_formatted_event(priv, "Associate request (ex)", mlme, 1);
+
+ if (priv->iw_mode != IW_MODE_MASTER
+ && mlmeex->state != DOT11_STATE_ASSOCING)
+ break;
+
+ confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC);
+
+ if (!confirm)
+ break;
+
+ memcpy(&confirm->address, mlmeex->address, ETH_ALEN);
+
+ confirm->id = ((struct obj_mlmeex *)mlme)->id;
+ confirm->state = 0; /* not used */
+ confirm->code = 0;
+
+ wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie);
+
+ if (!wpa_ie_len) {
+ printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n",
+ mlmeex->address);
+ kfree(confirm);
+ break;
+ }
+
+ confirm->size = wpa_ie_len;
+ memcpy(&confirm->data, wpa_ie, wpa_ie_len);
+
+ mgt_set_varlen(priv, oid, confirm, wpa_ie_len);
+
+ kfree(confirm);
+
+ break;
+
+ case DOT11_OID_REASSOCIATEEX:
+ handle_request(priv, mlme, oid);
+ send_formatted_event(priv, "Reassociate request (ex)", mlme, 1);
+
+ if (priv->iw_mode != IW_MODE_MASTER
+ && mlmeex->state != DOT11_STATE_ASSOCING)
+ break;
+
+ confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC);
+
+ if (!confirm)
+ break;
+
+ memcpy(&confirm->address, mlmeex->address, ETH_ALEN);
+
+ confirm->id = mlmeex->id;
+ confirm->state = 0; /* not used */
+ confirm->code = 0;
+
+ wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie);
+
+ if (!wpa_ie_len) {
+ printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n",
+ mlmeex->address);
+ kfree(confirm);
+ break;
+ }
+
+ confirm->size = wpa_ie_len;
+ memcpy(&confirm->data, wpa_ie, wpa_ie_len);
+
+ mgt_set_varlen(priv, oid, confirm, wpa_ie_len);
+
+ kfree(confirm);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Process a device trap. This is called via schedule_work(), outside of
+ * interrupt context, no locks held.
+ */
+void
+prism54_process_trap(struct work_struct *work)
+{
+ struct islpci_mgmtframe *frame =
+ container_of(work, struct islpci_mgmtframe, ws);
+ struct net_device *ndev = frame->ndev;
+ enum oid_num_t n = mgt_oidtonum(frame->header->oid);
+
+ if (n != OID_NUM_LAST)
+ prism54_process_trap_helper(netdev_priv(ndev), n, frame->data);
+ islpci_mgt_release(frame);
+}
+
+int
+prism54_set_mac_address(struct net_device *ndev, void *addr)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int ret;
+
+ if (ndev->addr_len != 6)
+ return -EINVAL;
+ ret = mgt_set_request(priv, GEN_OID_MACADDRESS, 0,
+ &((struct sockaddr *) addr)->sa_data);
+ if (!ret)
+ memcpy(priv->ndev->dev_addr,
+ &((struct sockaddr *) addr)->sa_data, ETH_ALEN);
+
+ return ret;
+}
+
+#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12
+
+static int
+prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ u32 mlme, authen, dot1x, filter, wep;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return 0;
+
+ wep = 1; /* For privacy invoked */
+ filter = 1; /* Filter out all unencrypted frames */
+ dot1x = 0x01; /* To enable eap filter */
+ mlme = DOT11_MLME_EXTENDED;
+ authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */
+
+ down_write(&priv->mib_sem);
+ priv->wpa = *uwrq;
+
+ switch (priv->wpa) {
+ default:
+ case 0: /* Clears/disables WPA and friends */
+ wep = 0;
+ filter = 0; /* Do not filter un-encrypted data */
+ dot1x = 0;
+ mlme = DOT11_MLME_AUTO;
+ printk("%s: Disabling WPA\n", ndev->name);
+ break;
+ case 2:
+ case 1: /* WPA */
+ printk("%s: Enabling WPA\n", ndev->name);
+ break;
+ }
+ up_write(&priv->mib_sem);
+
+ mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
+ mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &wep);
+ mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &filter);
+ mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x);
+ mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlme);
+
+ return 0;
+}
+
+static int
+prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ *uwrq = priv->wpa;
+ return 0;
+}
+
+static int
+prism54_set_prismhdr(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ priv->monitor_type =
+ (*uwrq ? ARPHRD_IEEE80211_PRISM : ARPHRD_IEEE80211);
+ if (priv->iw_mode == IW_MODE_MONITOR)
+ priv->ndev->type = priv->monitor_type;
+
+ return 0;
+}
+
+static int
+prism54_get_prismhdr(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ *uwrq = (priv->monitor_type == ARPHRD_IEEE80211_PRISM);
+ return 0;
+}
+
+static int
+prism54_debug_oid(struct net_device *ndev, struct iw_request_info *info,
+ __u32 * uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ priv->priv_oid = *uwrq;
+ printk("%s: oid 0x%08X\n", ndev->name, *uwrq);
+
+ return 0;
+}
+
+static int
+prism54_debug_get_oid(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct islpci_mgmtframe *response;
+ int ret = -EIO;
+
+ printk("%s: get_oid 0x%08X\n", ndev->name, priv->priv_oid);
+ data->length = 0;
+
+ if (islpci_get_state(priv) >= PRV_STATE_INIT) {
+ ret =
+ islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
+ priv->priv_oid, extra, 256,
+ &response);
+ printk("%s: ret: %i\n", ndev->name, ret);
+ if (ret || !response
+ || response->header->operation == PIMFOR_OP_ERROR) {
+ if (response) {
+ islpci_mgt_release(response);
+ }
+ printk("%s: EIO\n", ndev->name);
+ ret = -EIO;
+ }
+ if (!ret) {
+ data->length = response->header->length;
+ memcpy(extra, response->data, data->length);
+ islpci_mgt_release(response);
+ printk("%s: len: %i\n", ndev->name, data->length);
+ }
+ }
+
+ return ret;
+}
+
+static int
+prism54_debug_set_oid(struct net_device *ndev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ struct islpci_mgmtframe *response;
+ int ret = 0, response_op = PIMFOR_OP_ERROR;
+
+ printk("%s: set_oid 0x%08X\tlen: %d\n", ndev->name, priv->priv_oid,
+ data->length);
+
+ if (islpci_get_state(priv) >= PRV_STATE_INIT) {
+ ret =
+ islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET,
+ priv->priv_oid, extra, data->length,
+ &response);
+ printk("%s: ret: %i\n", ndev->name, ret);
+ if (ret || !response
+ || response->header->operation == PIMFOR_OP_ERROR) {
+ if (response) {
+ islpci_mgt_release(response);
+ }
+ printk("%s: EIO\n", ndev->name);
+ ret = -EIO;
+ }
+ if (!ret) {
+ response_op = response->header->operation;
+ printk("%s: response_op: %i\n", ndev->name,
+ response_op);
+ islpci_mgt_release(response);
+ }
+ }
+
+ return (ret ? ret : -EINPROGRESS);
+}
+
+static int
+prism54_set_spy(struct net_device *ndev,
+ struct iw_request_info *info,
+ union iwreq_data *uwrq, char *extra)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ u32 u;
+ enum oid_num_t oid = OID_INL_CONFIG;
+
+ down_write(&priv->mib_sem);
+ mgt_get(priv, OID_INL_CONFIG, &u);
+
+ if ((uwrq->data.length == 0) && (priv->spy_data.spy_number > 0))
+ /* disable spy */
+ u &= ~INL_CONFIG_RXANNEX;
+ else if ((uwrq->data.length > 0) && (priv->spy_data.spy_number == 0))
+ /* enable spy */
+ u |= INL_CONFIG_RXANNEX;
+
+ mgt_set(priv, OID_INL_CONFIG, &u);
+ mgt_commit_list(priv, &oid, 1);
+ up_write(&priv->mib_sem);
+
+ return iw_handler_set_spy(ndev, info, uwrq, extra);
+}
+
+static const iw_handler prism54_handler[] = {
+ (iw_handler) prism54_commit, /* SIOCSIWCOMMIT */
+ (iw_handler) prism54_get_name, /* SIOCGIWNAME */
+ (iw_handler) NULL, /* SIOCSIWNWID */
+ (iw_handler) NULL, /* SIOCGIWNWID */
+ (iw_handler) prism54_set_freq, /* SIOCSIWFREQ */
+ (iw_handler) prism54_get_freq, /* SIOCGIWFREQ */
+ (iw_handler) prism54_set_mode, /* SIOCSIWMODE */
+ (iw_handler) prism54_get_mode, /* SIOCGIWMODE */
+ (iw_handler) prism54_set_sens, /* SIOCSIWSENS */
+ (iw_handler) prism54_get_sens, /* SIOCGIWSENS */
+ (iw_handler) NULL, /* SIOCSIWRANGE */
+ (iw_handler) prism54_get_range, /* SIOCGIWRANGE */
+ (iw_handler) NULL, /* SIOCSIWPRIV */
+ (iw_handler) NULL, /* SIOCGIWPRIV */
+ (iw_handler) NULL, /* SIOCSIWSTATS */
+ (iw_handler) NULL, /* SIOCGIWSTATS */
+ prism54_set_spy, /* SIOCSIWSPY */
+ iw_handler_get_spy, /* SIOCGIWSPY */
+ iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
+ iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
+ (iw_handler) prism54_set_wap, /* SIOCSIWAP */
+ (iw_handler) prism54_get_wap, /* SIOCGIWAP */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* SIOCGIWAPLIST deprecated */
+ (iw_handler) prism54_set_scan, /* SIOCSIWSCAN */
+ (iw_handler) prism54_get_scan, /* SIOCGIWSCAN */
+ (iw_handler) prism54_set_essid, /* SIOCSIWESSID */
+ (iw_handler) prism54_get_essid, /* SIOCGIWESSID */
+ (iw_handler) prism54_set_nick, /* SIOCSIWNICKN */
+ (iw_handler) prism54_get_nick, /* SIOCGIWNICKN */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) prism54_set_rate, /* SIOCSIWRATE */
+ (iw_handler) prism54_get_rate, /* SIOCGIWRATE */
+ (iw_handler) prism54_set_rts, /* SIOCSIWRTS */
+ (iw_handler) prism54_get_rts, /* SIOCGIWRTS */
+ (iw_handler) prism54_set_frag, /* SIOCSIWFRAG */
+ (iw_handler) prism54_get_frag, /* SIOCGIWFRAG */
+ (iw_handler) prism54_set_txpower, /* SIOCSIWTXPOW */
+ (iw_handler) prism54_get_txpower, /* SIOCGIWTXPOW */
+ (iw_handler) prism54_set_retry, /* SIOCSIWRETRY */
+ (iw_handler) prism54_get_retry, /* SIOCGIWRETRY */
+ (iw_handler) prism54_set_encode, /* SIOCSIWENCODE */
+ (iw_handler) prism54_get_encode, /* SIOCGIWENCODE */
+ (iw_handler) NULL, /* SIOCSIWPOWER */
+ (iw_handler) NULL, /* SIOCGIWPOWER */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ (iw_handler) prism54_set_genie, /* SIOCSIWGENIE */
+ (iw_handler) prism54_get_genie, /* SIOCGIWGENIE */
+ (iw_handler) prism54_set_auth, /* SIOCSIWAUTH */
+ (iw_handler) prism54_get_auth, /* SIOCGIWAUTH */
+ (iw_handler) prism54_set_encodeext, /* SIOCSIWENCODEEXT */
+ (iw_handler) prism54_get_encodeext, /* SIOCGIWENCODEEXT */
+ NULL, /* SIOCSIWPMKSA */
+};
+
+/* The low order bit identify a SET (0) or a GET (1) ioctl. */
+
+#define PRISM54_RESET SIOCIWFIRSTPRIV
+#define PRISM54_GET_POLICY SIOCIWFIRSTPRIV+1
+#define PRISM54_SET_POLICY SIOCIWFIRSTPRIV+2
+#define PRISM54_GET_MAC SIOCIWFIRSTPRIV+3
+#define PRISM54_ADD_MAC SIOCIWFIRSTPRIV+4
+
+#define PRISM54_DEL_MAC SIOCIWFIRSTPRIV+6
+
+#define PRISM54_KICK_MAC SIOCIWFIRSTPRIV+8
+
+#define PRISM54_KICK_ALL SIOCIWFIRSTPRIV+10
+
+#define PRISM54_GET_WPA SIOCIWFIRSTPRIV+11
+#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12
+
+#define PRISM54_DBG_OID SIOCIWFIRSTPRIV+14
+#define PRISM54_DBG_GET_OID SIOCIWFIRSTPRIV+15
+#define PRISM54_DBG_SET_OID SIOCIWFIRSTPRIV+16
+
+#define PRISM54_GET_OID SIOCIWFIRSTPRIV+17
+#define PRISM54_SET_OID_U32 SIOCIWFIRSTPRIV+18
+#define PRISM54_SET_OID_STR SIOCIWFIRSTPRIV+20
+#define PRISM54_SET_OID_ADDR SIOCIWFIRSTPRIV+22
+
+#define PRISM54_GET_PRISMHDR SIOCIWFIRSTPRIV+23
+#define PRISM54_SET_PRISMHDR SIOCIWFIRSTPRIV+24
+
+#define IWPRIV_SET_U32(n,x) { n, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x }
+#define IWPRIV_SET_SSID(n,x) { n, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x }
+#define IWPRIV_SET_ADDR(n,x) { n, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x }
+#define IWPRIV_GET(n,x) { n, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, "g_"x }
+
+#define IWPRIV_U32(n,x) IWPRIV_SET_U32(n,x), IWPRIV_GET(n,x)
+#define IWPRIV_SSID(n,x) IWPRIV_SET_SSID(n,x), IWPRIV_GET(n,x)
+#define IWPRIV_ADDR(n,x) IWPRIV_SET_ADDR(n,x), IWPRIV_GET(n,x)
+
+/* Note : limited to 128 private ioctls (wireless tools 26) */
+
+static const struct iw_priv_args prism54_private_args[] = {
+/*{ cmd, set_args, get_args, name } */
+ {PRISM54_RESET, 0, 0, "reset"},
+ {PRISM54_GET_PRISMHDR, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ "get_prismhdr"},
+ {PRISM54_SET_PRISMHDR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
+ "set_prismhdr"},
+ {PRISM54_GET_POLICY, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ "getPolicy"},
+ {PRISM54_SET_POLICY, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
+ "setPolicy"},
+ {PRISM54_GET_MAC, 0, IW_PRIV_TYPE_ADDR | 64, "getMac"},
+ {PRISM54_ADD_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
+ "addMac"},
+ {PRISM54_DEL_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
+ "delMac"},
+ {PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
+ "kickMac"},
+ {PRISM54_KICK_ALL, 0, 0, "kickAll"},
+ {PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ "get_wpa"},
+ {PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
+ "set_wpa"},
+ {PRISM54_DBG_OID, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
+ "dbg_oid"},
+ {PRISM54_DBG_GET_OID, 0, IW_PRIV_TYPE_BYTE | 256, "dbg_get_oid"},
+ {PRISM54_DBG_SET_OID, IW_PRIV_TYPE_BYTE | 256, 0, "dbg_set_oid"},
+ /* --- sub-ioctls handlers --- */
+ {PRISM54_GET_OID,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, ""},
+ {PRISM54_SET_OID_U32,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ""},
+ {PRISM54_SET_OID_STR,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, ""},
+ {PRISM54_SET_OID_ADDR,
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, ""},
+ /* --- sub-ioctls definitions --- */
+ IWPRIV_ADDR(GEN_OID_MACADDRESS, "addr"),
+ IWPRIV_GET(GEN_OID_LINKSTATE, "linkstate"),
+ IWPRIV_U32(DOT11_OID_BSSTYPE, "bsstype"),
+ IWPRIV_ADDR(DOT11_OID_BSSID, "bssid"),
+ IWPRIV_U32(DOT11_OID_STATE, "state"),
+ IWPRIV_U32(DOT11_OID_AID, "aid"),
+
+ IWPRIV_SSID(DOT11_OID_SSIDOVERRIDE, "ssidoverride"),
+
+ IWPRIV_U32(DOT11_OID_MEDIUMLIMIT, "medlimit"),
+ IWPRIV_U32(DOT11_OID_BEACONPERIOD, "beacon"),
+ IWPRIV_U32(DOT11_OID_DTIMPERIOD, "dtimperiod"),
+
+ IWPRIV_U32(DOT11_OID_AUTHENABLE, "authenable"),
+ IWPRIV_U32(DOT11_OID_PRIVACYINVOKED, "privinvok"),
+ IWPRIV_U32(DOT11_OID_EXUNENCRYPTED, "exunencrypt"),
+
+ IWPRIV_U32(DOT11_OID_REKEYTHRESHOLD, "rekeythresh"),
+
+ IWPRIV_U32(DOT11_OID_MAXTXLIFETIME, "maxtxlife"),
+ IWPRIV_U32(DOT11_OID_MAXRXLIFETIME, "maxrxlife"),
+ IWPRIV_U32(DOT11_OID_ALOFT_FIXEDRATE, "fixedrate"),
+ IWPRIV_U32(DOT11_OID_MAXFRAMEBURST, "frameburst"),
+ IWPRIV_U32(DOT11_OID_PSM, "psm"),
+
+ IWPRIV_U32(DOT11_OID_BRIDGELOCAL, "bridge"),
+ IWPRIV_U32(DOT11_OID_CLIENTS, "clients"),
+ IWPRIV_U32(DOT11_OID_CLIENTSASSOCIATED, "clientassoc"),
+ IWPRIV_U32(DOT11_OID_DOT1XENABLE, "dot1xenable"),
+ IWPRIV_U32(DOT11_OID_ANTENNARX, "rxant"),
+ IWPRIV_U32(DOT11_OID_ANTENNATX, "txant"),
+ IWPRIV_U32(DOT11_OID_ANTENNADIVERSITY, "antdivers"),
+ IWPRIV_U32(DOT11_OID_EDTHRESHOLD, "edthresh"),
+ IWPRIV_U32(DOT11_OID_PREAMBLESETTINGS, "preamble"),
+ IWPRIV_GET(DOT11_OID_RATES, "rates"),
+ IWPRIV_U32(DOT11_OID_OUTPUTPOWER, ".11outpower"),
+ IWPRIV_GET(DOT11_OID_SUPPORTEDRATES, "supprates"),
+ IWPRIV_GET(DOT11_OID_SUPPORTEDFREQUENCIES, "suppfreq"),
+
+ IWPRIV_U32(DOT11_OID_NOISEFLOOR, "noisefloor"),
+ IWPRIV_GET(DOT11_OID_FREQUENCYACTIVITY, "freqactivity"),
+ IWPRIV_U32(DOT11_OID_NONERPPROTECTION, "nonerpprotec"),
+ IWPRIV_U32(DOT11_OID_PROFILES, "profile"),
+ IWPRIV_GET(DOT11_OID_EXTENDEDRATES, "extrates"),
+ IWPRIV_U32(DOT11_OID_MLMEAUTOLEVEL, "mlmelevel"),
+
+ IWPRIV_GET(DOT11_OID_BSSS, "bsss"),
+ IWPRIV_GET(DOT11_OID_BSSLIST, "bsslist"),
+ IWPRIV_U32(OID_INL_MODE, "mode"),
+ IWPRIV_U32(OID_INL_CONFIG, "config"),
+ IWPRIV_U32(OID_INL_DOT11D_CONFORMANCE, ".11dconform"),
+ IWPRIV_GET(OID_INL_PHYCAPABILITIES, "phycapa"),
+ IWPRIV_U32(OID_INL_OUTPUTPOWER, "outpower"),
+};
+
+static const iw_handler prism54_private_handler[] = {
+ (iw_handler) prism54_reset,
+ (iw_handler) prism54_get_policy,
+ (iw_handler) prism54_set_policy,
+ (iw_handler) prism54_get_mac,
+ (iw_handler) prism54_add_mac,
+ (iw_handler) NULL,
+ (iw_handler) prism54_del_mac,
+ (iw_handler) NULL,
+ (iw_handler) prism54_kick_mac,
+ (iw_handler) NULL,
+ (iw_handler) prism54_kick_all,
+ (iw_handler) prism54_get_wpa,
+ (iw_handler) prism54_set_wpa,
+ (iw_handler) NULL,
+ (iw_handler) prism54_debug_oid,
+ (iw_handler) prism54_debug_get_oid,
+ (iw_handler) prism54_debug_set_oid,
+ (iw_handler) prism54_get_oid,
+ (iw_handler) prism54_set_u32,
+ (iw_handler) NULL,
+ (iw_handler) prism54_set_raw,
+ (iw_handler) NULL,
+ (iw_handler) prism54_set_raw,
+ (iw_handler) prism54_get_prismhdr,
+ (iw_handler) prism54_set_prismhdr,
+};
+
+const struct iw_handler_def prism54_handler_def = {
+ .num_standard = ARRAY_SIZE(prism54_handler),
+ .num_private = ARRAY_SIZE(prism54_private_handler),
+ .num_private_args = ARRAY_SIZE(prism54_private_args),
+ .standard = (iw_handler *) prism54_handler,
+ .private = (iw_handler *) prism54_private_handler,
+ .private_args = (struct iw_priv_args *) prism54_private_args,
+ .get_wireless_stats = prism54_get_wireless_stats,
+};
diff --git a/drivers/net/wireless/prism54/isl_ioctl.h b/drivers/net/wireless/intersil/prism54/isl_ioctl.h
index 842a2549facc..842a2549facc 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.h
+++ b/drivers/net/wireless/intersil/prism54/isl_ioctl.h
diff --git a/drivers/net/wireless/prism54/isl_oid.h b/drivers/net/wireless/intersil/prism54/isl_oid.h
index 83fec557997e..83fec557997e 100644
--- a/drivers/net/wireless/prism54/isl_oid.h
+++ b/drivers/net/wireless/intersil/prism54/isl_oid.h
diff --git a/drivers/net/wireless/intersil/prism54/islpci_dev.c b/drivers/net/wireless/intersil/prism54/islpci_dev.c
new file mode 100644
index 000000000000..84a42012aeae
--- /dev/null
+++ b/drivers/net/wireless/intersil/prism54/islpci_dev.c
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2002 Intersil Americas Inc.
+ * Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
+ * Copyright (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/hardirq.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/if_arp.h>
+
+#include <asm/io.h>
+
+#include "prismcompat.h"
+#include "isl_38xx.h"
+#include "isl_ioctl.h"
+#include "islpci_dev.h"
+#include "islpci_mgt.h"
+#include "islpci_eth.h"
+#include "oid_mgt.h"
+
+#define ISL3877_IMAGE_FILE "isl3877"
+#define ISL3886_IMAGE_FILE "isl3886"
+#define ISL3890_IMAGE_FILE "isl3890"
+MODULE_FIRMWARE(ISL3877_IMAGE_FILE);
+MODULE_FIRMWARE(ISL3886_IMAGE_FILE);
+MODULE_FIRMWARE(ISL3890_IMAGE_FILE);
+
+static int prism54_bring_down(islpci_private *);
+static int islpci_alloc_memory(islpci_private *);
+
+/* Temporary dummy MAC address to use until firmware is loaded.
+ * The idea there is that some tools (such as nameif) may query
+ * the MAC address before the netdev is 'open'. By using a valid
+ * OUI prefix, they can process the netdev properly.
+ * Of course, this is not the final/real MAC address. It doesn't
+ * matter, as you are suppose to be able to change it anytime via
+ * ndev->set_mac_address. Jean II */
+static const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 };
+
+static int
+isl_upload_firmware(islpci_private *priv)
+{
+ u32 reg, rc;
+ void __iomem *device_base = priv->device_base;
+
+ /* clear the RAMBoot and the Reset bit */
+ reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ reg &= ~ISL38XX_CTRL_STAT_RAMBOOT;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* set the Reset bit without reading the register ! */
+ reg |= ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* clear the Reset bit */
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+
+ /* wait a while for the device to reboot */
+ mdelay(50);
+
+ {
+ const struct firmware *fw_entry = NULL;
+ long fw_len;
+ const u32 *fw_ptr;
+
+ rc = request_firmware(&fw_entry, priv->firmware, PRISM_FW_PDEV);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: request_firmware() failed for '%s'\n",
+ "prism54", priv->firmware);
+ return rc;
+ }
+ /* prepare the Direct Memory Base register */
+ reg = ISL38XX_DEV_FIRMWARE_ADDRES;
+
+ fw_ptr = (u32 *) fw_entry->data;
+ fw_len = fw_entry->size;
+
+ if (fw_len % 4) {
+ printk(KERN_ERR
+ "%s: firmware '%s' size is not multiple of 32bit, aborting!\n",
+ "prism54", priv->firmware);
+ release_firmware(fw_entry);
+ return -EILSEQ; /* Illegal byte sequence */;
+ }
+
+ while (fw_len > 0) {
+ long _fw_len =
+ (fw_len >
+ ISL38XX_MEMORY_WINDOW_SIZE) ?
+ ISL38XX_MEMORY_WINDOW_SIZE : fw_len;
+ u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN;
+
+ /* set the card's base address for writing the data */
+ isl38xx_w32_flush(device_base, reg,
+ ISL38XX_DIR_MEM_BASE_REG);
+ wmb(); /* be paranoid */
+
+ /* increment the write address for next iteration */
+ reg += _fw_len;
+ fw_len -= _fw_len;
+
+ /* write the data to the Direct Memory Window 32bit-wise */
+ /* memcpy_toio() doesn't guarantee 32bit writes :-| */
+ while (_fw_len > 0) {
+ /* use non-swapping writel() */
+ __raw_writel(*fw_ptr, dev_fw_ptr);
+ fw_ptr++, dev_fw_ptr++;
+ _fw_len -= 4;
+ }
+
+ /* flush PCI posting */
+ (void) readl(device_base + ISL38XX_PCI_POSTING_FLUSH);
+ wmb(); /* be paranoid again */
+
+ BUG_ON(_fw_len != 0);
+ }
+
+ BUG_ON(fw_len != 0);
+
+ /* Firmware version is at offset 40 (also for "newmac") */
+ printk(KERN_DEBUG "%s: firmware version: %.8s\n",
+ priv->ndev->name, fw_entry->data + 40);
+
+ release_firmware(fw_entry);
+ }
+
+ /* now reset the device
+ * clear the Reset & ClkRun bit, set the RAMBoot bit */
+ reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
+ reg &= ~ISL38XX_CTRL_STAT_CLKRUN;
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ reg |= ISL38XX_CTRL_STAT_RAMBOOT;
+ isl38xx_w32_flush(device_base, reg, ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* set the reset bit latches the host override and RAMBoot bits
+ * into the device for operation when the reset bit is reset */
+ reg |= ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ /* don't do flush PCI posting here! */
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* clear the reset bit should start the whole circus */
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ /* don't do flush PCI posting here! */
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ return 0;
+}
+
+/******************************************************************************
+ Device Interrupt Handler
+******************************************************************************/
+
+irqreturn_t
+islpci_interrupt(int irq, void *config)
+{
+ u32 reg;
+ islpci_private *priv = config;
+ struct net_device *ndev = priv->ndev;
+ void __iomem *device = priv->device_base;
+ int powerstate = ISL38XX_PSM_POWERSAVE_STATE;
+
+ /* lock the interrupt handler */
+ spin_lock(&priv->slock);
+
+ /* received an interrupt request on a shared IRQ line
+ * first check whether the device is in sleep mode */
+ reg = readl(device + ISL38XX_CTRL_STAT_REG);
+ if (reg & ISL38XX_CTRL_STAT_SLEEPMODE)
+ /* device is in sleep mode, IRQ was generated by someone else */
+ {
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
+#endif
+ spin_unlock(&priv->slock);
+ return IRQ_NONE;
+ }
+
+
+ /* check whether there is any source of interrupt on the device */
+ reg = readl(device + ISL38XX_INT_IDENT_REG);
+
+ /* also check the contents of the Interrupt Enable Register, because this
+ * will filter out interrupt sources from other devices on the same irq ! */
+ reg &= readl(device + ISL38XX_INT_EN_REG);
+ reg &= ISL38XX_INT_SOURCES;
+
+ if (reg != 0) {
+ if (islpci_get_state(priv) != PRV_STATE_SLEEP)
+ powerstate = ISL38XX_PSM_ACTIVE_STATE;
+
+ /* reset the request bits in the Identification register */
+ isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG);
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_FUNCTION_CALLS,
+ "IRQ: Identification register 0x%p 0x%x\n", device, reg);
+#endif
+
+ /* check for each bit in the register separately */
+ if (reg & ISL38XX_INT_IDENT_UPDATE) {
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ /* Queue has been updated */
+ DEBUG(SHOW_TRACING, "IRQ: Update flag\n");
+
+ DEBUG(SHOW_QUEUE_INDEXES,
+ "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n",
+ le32_to_cpu(priv->control_block->
+ driver_curr_frag[0]),
+ le32_to_cpu(priv->control_block->
+ driver_curr_frag[1]),
+ le32_to_cpu(priv->control_block->
+ driver_curr_frag[2]),
+ le32_to_cpu(priv->control_block->
+ driver_curr_frag[3]),
+ le32_to_cpu(priv->control_block->
+ driver_curr_frag[4]),
+ le32_to_cpu(priv->control_block->
+ driver_curr_frag[5])
+ );
+
+ DEBUG(SHOW_QUEUE_INDEXES,
+ "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n",
+ le32_to_cpu(priv->control_block->
+ device_curr_frag[0]),
+ le32_to_cpu(priv->control_block->
+ device_curr_frag[1]),
+ le32_to_cpu(priv->control_block->
+ device_curr_frag[2]),
+ le32_to_cpu(priv->control_block->
+ device_curr_frag[3]),
+ le32_to_cpu(priv->control_block->
+ device_curr_frag[4]),
+ le32_to_cpu(priv->control_block->
+ device_curr_frag[5])
+ );
+#endif
+
+ /* cleanup the data low transmit queue */
+ islpci_eth_cleanup_transmit(priv, priv->control_block);
+
+ /* device is in active state, update the
+ * powerstate flag if necessary */
+ powerstate = ISL38XX_PSM_ACTIVE_STATE;
+
+ /* check all three queues in priority order
+ * call the PIMFOR receive function until the
+ * queue is empty */
+ if (isl38xx_in_queue(priv->control_block,
+ ISL38XX_CB_RX_MGMTQ) != 0) {
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING,
+ "Received frame in Management Queue\n");
+#endif
+ islpci_mgt_receive(ndev);
+
+ islpci_mgt_cleanup_transmit(ndev);
+
+ /* Refill slots in receive queue */
+ islpci_mgmt_rx_fill(ndev);
+
+ /* no need to trigger the device, next
+ islpci_mgt_transaction does it */
+ }
+
+ while (isl38xx_in_queue(priv->control_block,
+ ISL38XX_CB_RX_DATA_LQ) != 0) {
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING,
+ "Received frame in Data Low Queue\n");
+#endif
+ islpci_eth_receive(priv);
+ }
+
+ /* check whether the data transmit queues were full */
+ if (priv->data_low_tx_full) {
+ /* check whether the transmit is not full anymore */
+ if (ISL38XX_CB_TX_QSIZE -
+ isl38xx_in_queue(priv->control_block,
+ ISL38XX_CB_TX_DATA_LQ) >=
+ ISL38XX_MIN_QTHRESHOLD) {
+ /* nope, the driver is ready for more network frames */
+ netif_wake_queue(priv->ndev);
+
+ /* reset the full flag */
+ priv->data_low_tx_full = 0;
+ }
+ }
+ }
+
+ if (reg & ISL38XX_INT_IDENT_INIT) {
+ /* Device has been initialized */
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING,
+ "IRQ: Init flag, device initialized\n");
+#endif
+ wake_up(&priv->reset_done);
+ }
+
+ if (reg & ISL38XX_INT_IDENT_SLEEP) {
+ /* Device intends to move to powersave state */
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "IRQ: Sleep flag\n");
+#endif
+ isl38xx_handle_sleep_request(priv->control_block,
+ &powerstate,
+ priv->device_base);
+ }
+
+ if (reg & ISL38XX_INT_IDENT_WAKEUP) {
+ /* Device has been woken up to active state */
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "IRQ: Wakeup flag\n");
+#endif
+
+ isl38xx_handle_wakeup(priv->control_block,
+ &powerstate, priv->device_base);
+ }
+ } else {
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
+#endif
+ spin_unlock(&priv->slock);
+ return IRQ_NONE;
+ }
+
+ /* sleep -> ready */
+ if (islpci_get_state(priv) == PRV_STATE_SLEEP
+ && powerstate == ISL38XX_PSM_ACTIVE_STATE)
+ islpci_set_state(priv, PRV_STATE_READY);
+
+ /* !sleep -> sleep */
+ if (islpci_get_state(priv) != PRV_STATE_SLEEP
+ && powerstate == ISL38XX_PSM_POWERSAVE_STATE)
+ islpci_set_state(priv, PRV_STATE_SLEEP);
+
+ /* unlock the interrupt handler */
+ spin_unlock(&priv->slock);
+
+ return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ Network Interface Control & Statistical functions
+******************************************************************************/
+static int
+islpci_open(struct net_device *ndev)
+{
+ u32 rc;
+ islpci_private *priv = netdev_priv(ndev);
+
+ /* reset data structures, upload firmware and reset device */
+ rc = islpci_reset(priv,1);
+ if (rc) {
+ prism54_bring_down(priv);
+ return rc; /* Returns informative message */
+ }
+
+ netif_start_queue(ndev);
+
+ /* Turn off carrier if in STA or Ad-hoc mode. It will be turned on
+ * once the firmware receives a trap of being associated
+ * (GEN_OID_LINKSTATE). In other modes (AP or WDS or monitor) we
+ * should just leave the carrier on as its expected the firmware
+ * won't send us a trigger. */
+ if (priv->iw_mode == IW_MODE_INFRA || priv->iw_mode == IW_MODE_ADHOC)
+ netif_carrier_off(ndev);
+ else
+ netif_carrier_on(ndev);
+
+ return 0;
+}
+
+static int
+islpci_close(struct net_device *ndev)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ printk(KERN_DEBUG "%s: islpci_close ()\n", ndev->name);
+
+ netif_stop_queue(ndev);
+
+ return prism54_bring_down(priv);
+}
+
+static int
+prism54_bring_down(islpci_private *priv)
+{
+ void __iomem *device_base = priv->device_base;
+ u32 reg;
+ /* we are going to shutdown the device */
+ islpci_set_state(priv, PRV_STATE_PREBOOT);
+
+ /* disable all device interrupts in case they weren't */
+ isl38xx_disable_interrupts(priv->device_base);
+
+ /* For safety reasons, we may want to ensure that no DMA transfer is
+ * currently in progress by emptying the TX and RX queues. */
+
+ /* wait until interrupts have finished executing on other CPUs */
+ synchronize_irq(priv->pdev->irq);
+
+ reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
+ reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT);
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ reg |= ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* clear the Reset bit */
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+
+ /* wait a while for the device to reset */
+ schedule_timeout_uninterruptible(msecs_to_jiffies(50));
+
+ return 0;
+}
+
+static int
+islpci_upload_fw(islpci_private *priv)
+{
+ islpci_state_t old_state;
+ u32 rc;
+
+ old_state = islpci_set_state(priv, PRV_STATE_BOOT);
+
+ printk(KERN_DEBUG "%s: uploading firmware...\n", priv->ndev->name);
+
+ rc = isl_upload_firmware(priv);
+ if (rc) {
+ /* error uploading the firmware */
+ printk(KERN_ERR "%s: could not upload firmware ('%s')\n",
+ priv->ndev->name, priv->firmware);
+
+ islpci_set_state(priv, old_state);
+ return rc;
+ }
+
+ printk(KERN_DEBUG "%s: firmware upload complete\n",
+ priv->ndev->name);
+
+ islpci_set_state(priv, PRV_STATE_POSTBOOT);
+
+ return 0;
+}
+
+static int
+islpci_reset_if(islpci_private *priv)
+{
+ long remaining;
+ int result = -ETIME;
+ int count;
+
+ DEFINE_WAIT(wait);
+ prepare_to_wait(&priv->reset_done, &wait, TASK_UNINTERRUPTIBLE);
+
+ /* now the last step is to reset the interface */
+ isl38xx_interface_reset(priv->device_base, priv->device_host_address);
+ islpci_set_state(priv, PRV_STATE_PREINIT);
+
+ for(count = 0; count < 2 && result; count++) {
+ /* The software reset acknowledge needs about 220 msec here.
+ * Be conservative and wait for up to one second. */
+
+ remaining = schedule_timeout_uninterruptible(HZ);
+
+ if(remaining > 0) {
+ result = 0;
+ break;
+ }
+
+ /* If we're here it's because our IRQ hasn't yet gone through.
+ * Retry a bit more...
+ */
+ printk(KERN_ERR "%s: no 'reset complete' IRQ seen - retrying\n",
+ priv->ndev->name);
+ }
+
+ finish_wait(&priv->reset_done, &wait);
+
+ if (result) {
+ printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name);
+ return result;
+ }
+
+ islpci_set_state(priv, PRV_STATE_INIT);
+
+ /* Now that the device is 100% up, let's allow
+ * for the other interrupts --
+ * NOTE: this is not *yet* true since we've only allowed the
+ * INIT interrupt on the IRQ line. We can perhaps poll
+ * the IRQ line until we know for sure the reset went through */
+ isl38xx_enable_common_interrupts(priv->device_base);
+
+ down_write(&priv->mib_sem);
+ result = mgt_commit(priv);
+ if (result) {
+ printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name);
+ up_write(&priv->mib_sem);
+ return result;
+ }
+ up_write(&priv->mib_sem);
+
+ islpci_set_state(priv, PRV_STATE_READY);
+
+ printk(KERN_DEBUG "%s: interface reset complete\n", priv->ndev->name);
+ return 0;
+}
+
+int
+islpci_reset(islpci_private *priv, int reload_firmware)
+{
+ isl38xx_control_block *cb = /* volatile not needed */
+ (isl38xx_control_block *) priv->control_block;
+ unsigned counter;
+ int rc;
+
+ if (reload_firmware)
+ islpci_set_state(priv, PRV_STATE_PREBOOT);
+ else
+ islpci_set_state(priv, PRV_STATE_POSTBOOT);
+
+ printk(KERN_DEBUG "%s: resetting device...\n", priv->ndev->name);
+
+ /* disable all device interrupts in case they weren't */
+ isl38xx_disable_interrupts(priv->device_base);
+
+ /* flush all management queues */
+ priv->index_mgmt_tx = 0;
+ priv->index_mgmt_rx = 0;
+
+ /* clear the indexes in the frame pointer */
+ for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) {
+ cb->driver_curr_frag[counter] = cpu_to_le32(0);
+ cb->device_curr_frag[counter] = cpu_to_le32(0);
+ }
+
+ /* reset the mgmt receive queue */
+ for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
+ isl38xx_fragment *frag = &cb->rx_data_mgmt[counter];
+ frag->size = cpu_to_le16(MGMT_FRAME_SIZE);
+ frag->flags = 0;
+ frag->address = cpu_to_le32(priv->mgmt_rx[counter].pci_addr);
+ }
+
+ for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
+ cb->rx_data_low[counter].address =
+ cpu_to_le32((u32) priv->pci_map_rx_address[counter]);
+ }
+
+ /* since the receive queues are filled with empty fragments, now we can
+ * set the corresponding indexes in the Control Block */
+ priv->control_block->driver_curr_frag[ISL38XX_CB_RX_DATA_LQ] =
+ cpu_to_le32(ISL38XX_CB_RX_QSIZE);
+ priv->control_block->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] =
+ cpu_to_le32(ISL38XX_CB_MGMT_QSIZE);
+
+ /* reset the remaining real index registers and full flags */
+ priv->free_data_rx = 0;
+ priv->free_data_tx = 0;
+ priv->data_low_tx_full = 0;
+
+ if (reload_firmware) { /* Should we load the firmware ? */
+ /* now that the data structures are cleaned up, upload
+ * firmware and reset interface */
+ rc = islpci_upload_fw(priv);
+ if (rc) {
+ printk(KERN_ERR "%s: islpci_reset: failure\n",
+ priv->ndev->name);
+ return rc;
+ }
+ }
+
+ /* finally reset interface */
+ rc = islpci_reset_if(priv);
+ if (rc)
+ printk(KERN_ERR "prism54: Your card/socket may be faulty, or IRQ line too busy :(\n");
+ return rc;
+}
+
+/******************************************************************************
+ Network device configuration functions
+******************************************************************************/
+static int
+islpci_alloc_memory(islpci_private *priv)
+{
+ int counter;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ printk(KERN_DEBUG "islpci_alloc_memory\n");
+#endif
+
+ /* remap the PCI device base address to accessible */
+ if (!(priv->device_base =
+ ioremap(pci_resource_start(priv->pdev, 0),
+ ISL38XX_PCI_MEM_SIZE))) {
+ /* error in remapping the PCI device memory address range */
+ printk(KERN_ERR "PCI memory remapping failed\n");
+ return -1;
+ }
+
+ /* memory layout for consistent DMA region:
+ *
+ * Area 1: Control Block for the device interface
+ * Area 2: Power Save Mode Buffer for temporary frame storage. Be aware that
+ * the number of supported stations in the AP determines the minimal
+ * size of the buffer !
+ */
+
+ /* perform the allocation */
+ priv->driver_mem_address = pci_alloc_consistent(priv->pdev,
+ HOST_MEM_BLOCK,
+ &priv->
+ device_host_address);
+
+ if (!priv->driver_mem_address) {
+ /* error allocating the block of PCI memory */
+ printk(KERN_ERR "%s: could not allocate DMA memory, aborting!",
+ "prism54");
+ return -1;
+ }
+
+ /* assign the Control Block to the first address of the allocated area */
+ priv->control_block =
+ (isl38xx_control_block *) priv->driver_mem_address;
+
+ /* set the Power Save Buffer pointer directly behind the CB */
+ priv->device_psm_buffer =
+ priv->device_host_address + CONTROL_BLOCK_SIZE;
+
+ /* make sure all buffer pointers are initialized */
+ for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) {
+ priv->control_block->driver_curr_frag[counter] = cpu_to_le32(0);
+ priv->control_block->device_curr_frag[counter] = cpu_to_le32(0);
+ }
+
+ priv->index_mgmt_rx = 0;
+ memset(priv->mgmt_rx, 0, sizeof(priv->mgmt_rx));
+ memset(priv->mgmt_tx, 0, sizeof(priv->mgmt_tx));
+
+ /* allocate rx queue for management frames */
+ if (islpci_mgmt_rx_fill(priv->ndev) < 0)
+ goto out_free;
+
+ /* now get the data rx skb's */
+ memset(priv->data_low_rx, 0, sizeof (priv->data_low_rx));
+ memset(priv->pci_map_rx_address, 0, sizeof (priv->pci_map_rx_address));
+
+ for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
+ struct sk_buff *skb;
+
+ /* allocate an sk_buff for received data frames storage
+ * each frame on receive size consists of 1 fragment
+ * include any required allignment operations */
+ if (!(skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2))) {
+ /* error allocating an sk_buff structure elements */
+ printk(KERN_ERR "Error allocating skb.\n");
+ skb = NULL;
+ goto out_free;
+ }
+ skb_reserve(skb, (4 - (long) skb->data) & 0x03);
+ /* add the new allocated sk_buff to the buffer array */
+ priv->data_low_rx[counter] = skb;
+
+ /* map the allocated skb data area to pci */
+ priv->pci_map_rx_address[counter] =
+ pci_map_single(priv->pdev, (void *) skb->data,
+ MAX_FRAGMENT_SIZE_RX + 2,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pdev,
+ priv->pci_map_rx_address[counter])) {
+ priv->pci_map_rx_address[counter] = 0;
+ /* error mapping the buffer to device
+ accessible memory address */
+ printk(KERN_ERR "failed to map skb DMA'able\n");
+ goto out_free;
+ }
+ }
+
+ prism54_acl_init(&priv->acl);
+ prism54_wpa_bss_ie_init(priv);
+ if (mgt_init(priv))
+ goto out_free;
+
+ return 0;
+ out_free:
+ islpci_free_memory(priv);
+ return -1;
+}
+
+int
+islpci_free_memory(islpci_private *priv)
+{
+ int counter;
+
+ if (priv->device_base)
+ iounmap(priv->device_base);
+ priv->device_base = NULL;
+
+ /* free consistent DMA area... */
+ if (priv->driver_mem_address)
+ pci_free_consistent(priv->pdev, HOST_MEM_BLOCK,
+ priv->driver_mem_address,
+ priv->device_host_address);
+
+ /* clear some dangling pointers */
+ priv->driver_mem_address = NULL;
+ priv->device_host_address = 0;
+ priv->device_psm_buffer = 0;
+ priv->control_block = NULL;
+
+ /* clean up mgmt rx buffers */
+ for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
+ struct islpci_membuf *buf = &priv->mgmt_rx[counter];
+ if (buf->pci_addr)
+ pci_unmap_single(priv->pdev, buf->pci_addr,
+ buf->size, PCI_DMA_FROMDEVICE);
+ buf->pci_addr = 0;
+ kfree(buf->mem);
+ buf->size = 0;
+ buf->mem = NULL;
+ }
+
+ /* clean up data rx buffers */
+ for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
+ if (priv->pci_map_rx_address[counter])
+ pci_unmap_single(priv->pdev,
+ priv->pci_map_rx_address[counter],
+ MAX_FRAGMENT_SIZE_RX + 2,
+ PCI_DMA_FROMDEVICE);
+ priv->pci_map_rx_address[counter] = 0;
+
+ if (priv->data_low_rx[counter])
+ dev_kfree_skb(priv->data_low_rx[counter]);
+ priv->data_low_rx[counter] = NULL;
+ }
+
+ /* Free the access control list and the WPA list */
+ prism54_acl_clean(&priv->acl);
+ prism54_wpa_bss_ie_clean(priv);
+ mgt_clean(priv);
+
+ return 0;
+}
+
+#if 0
+static void
+islpci_set_multicast_list(struct net_device *dev)
+{
+ /* put device into promisc mode and let network layer handle it */
+}
+#endif
+
+static void islpci_ethtool_get_drvinfo(struct net_device *dev,
+ 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 islpci_ethtool_ops = {
+ .get_drvinfo = islpci_ethtool_get_drvinfo,
+};
+
+static const struct net_device_ops islpci_netdev_ops = {
+ .ndo_open = islpci_open,
+ .ndo_stop = islpci_close,
+ .ndo_start_xmit = islpci_eth_transmit,
+ .ndo_tx_timeout = islpci_eth_tx_timeout,
+ .ndo_set_mac_address = prism54_set_mac_address,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static struct device_type wlan_type = {
+ .name = "wlan",
+};
+
+struct net_device *
+islpci_setup(struct pci_dev *pdev)
+{
+ islpci_private *priv;
+ struct net_device *ndev = alloc_etherdev(sizeof (islpci_private));
+
+ if (!ndev)
+ return ndev;
+
+ pci_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ SET_NETDEV_DEVTYPE(ndev, &wlan_type);
+
+ /* setup the structure members */
+ ndev->base_addr = pci_resource_start(pdev, 0);
+ ndev->irq = pdev->irq;
+
+ /* initialize the function pointers */
+ ndev->netdev_ops = &islpci_netdev_ops;
+ ndev->wireless_handlers = &prism54_handler_def;
+ ndev->ethtool_ops = &islpci_ethtool_ops;
+
+ /* ndev->set_multicast_list = &islpci_set_multicast_list; */
+ ndev->addr_len = ETH_ALEN;
+ /* Get a non-zero dummy MAC address for nameif. Jean II */
+ memcpy(ndev->dev_addr, dummy_mac, ETH_ALEN);
+
+ ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT;
+
+ /* allocate a private device structure to the network device */
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ priv->pdev = pdev;
+ priv->monitor_type = ARPHRD_IEEE80211;
+ priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) ?
+ priv->monitor_type : ARPHRD_ETHER;
+
+ /* Add pointers to enable iwspy support. */
+ priv->wireless_data.spy_data = &priv->spy_data;
+ ndev->wireless_data = &priv->wireless_data;
+
+ /* save the start and end address of the PCI memory area */
+ ndev->mem_start = (unsigned long) priv->device_base;
+ ndev->mem_end = ndev->mem_start + ISL38XX_PCI_MEM_SIZE;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "PCI Memory remapped to 0x%p\n", priv->device_base);
+#endif
+
+ init_waitqueue_head(&priv->reset_done);
+
+ /* init the queue read locks, process wait counter */
+ mutex_init(&priv->mgmt_lock);
+ priv->mgmt_received = NULL;
+ init_waitqueue_head(&priv->mgmt_wqueue);
+ mutex_init(&priv->stats_lock);
+ spin_lock_init(&priv->slock);
+
+ /* init state machine with off#1 state */
+ priv->state = PRV_STATE_OFF;
+ priv->state_off = 1;
+
+ /* initialize workqueue's */
+ INIT_WORK(&priv->stats_work, prism54_update_stats);
+ priv->stats_timestamp = 0;
+
+ INIT_WORK(&priv->reset_task, islpci_do_reset_and_wake);
+ priv->reset_task_pending = 0;
+
+ /* allocate various memory areas */
+ if (islpci_alloc_memory(priv))
+ goto do_free_netdev;
+
+ /* select the firmware file depending on the device id */
+ switch (pdev->device) {
+ case 0x3877:
+ strcpy(priv->firmware, ISL3877_IMAGE_FILE);
+ break;
+
+ case 0x3886:
+ strcpy(priv->firmware, ISL3886_IMAGE_FILE);
+ break;
+
+ default:
+ strcpy(priv->firmware, ISL3890_IMAGE_FILE);
+ break;
+ }
+
+ if (register_netdev(ndev)) {
+ DEBUG(SHOW_ERROR_MESSAGES,
+ "ERROR: register_netdev() failed\n");
+ goto do_islpci_free_memory;
+ }
+
+ return ndev;
+
+ do_islpci_free_memory:
+ islpci_free_memory(priv);
+ do_free_netdev:
+ free_netdev(ndev);
+ priv = NULL;
+ return NULL;
+}
+
+islpci_state_t
+islpci_set_state(islpci_private *priv, islpci_state_t new_state)
+{
+ islpci_state_t old_state;
+
+ /* lock */
+ old_state = priv->state;
+
+ /* this means either a race condition or some serious error in
+ * the driver code */
+ switch (new_state) {
+ case PRV_STATE_OFF:
+ priv->state_off++;
+ default:
+ priv->state = new_state;
+ break;
+
+ case PRV_STATE_PREBOOT:
+ /* there are actually many off-states, enumerated by
+ * state_off */
+ if (old_state == PRV_STATE_OFF)
+ priv->state_off--;
+
+ /* only if hw_unavailable is zero now it means we either
+ * were in off#1 state, or came here from
+ * somewhere else */
+ if (!priv->state_off)
+ priv->state = new_state;
+ break;
+ }
+#if 0
+ printk(KERN_DEBUG "%s: state transition %d -> %d (off#%d)\n",
+ priv->ndev->name, old_state, new_state, priv->state_off);
+#endif
+
+ /* invariants */
+ BUG_ON(priv->state_off < 0);
+ BUG_ON(priv->state_off && (priv->state != PRV_STATE_OFF));
+ BUG_ON(!priv->state_off && (priv->state == PRV_STATE_OFF));
+
+ /* unlock */
+ return old_state;
+}
diff --git a/drivers/net/wireless/prism54/islpci_dev.h b/drivers/net/wireless/intersil/prism54/islpci_dev.h
index f6f088e05fe4..f6f088e05fe4 100644
--- a/drivers/net/wireless/prism54/islpci_dev.h
+++ b/drivers/net/wireless/intersil/prism54/islpci_dev.h
diff --git a/drivers/net/wireless/intersil/prism54/islpci_eth.c b/drivers/net/wireless/intersil/prism54/islpci_eth.c
new file mode 100644
index 000000000000..d83f6332019e
--- /dev/null
+++ b/drivers/net/wireless/intersil/prism54/islpci_eth.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2002 Intersil Americas Inc.
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/gfp.h>
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <asm/byteorder.h>
+
+#include "prismcompat.h"
+#include "isl_38xx.h"
+#include "islpci_eth.h"
+#include "islpci_mgt.h"
+#include "oid_mgt.h"
+
+/******************************************************************************
+ Network Interface functions
+******************************************************************************/
+void
+islpci_eth_cleanup_transmit(islpci_private *priv,
+ isl38xx_control_block *control_block)
+{
+ struct sk_buff *skb;
+ u32 index;
+
+ /* compare the control block read pointer with the free pointer */
+ while (priv->free_data_tx !=
+ le32_to_cpu(control_block->
+ device_curr_frag[ISL38XX_CB_TX_DATA_LQ])) {
+ /* read the index of the first fragment to be freed */
+ index = priv->free_data_tx % ISL38XX_CB_TX_QSIZE;
+
+ /* check for holes in the arrays caused by multi fragment frames
+ * searching for the last fragment of a frame */
+ if (priv->pci_map_tx_address[index]) {
+ /* entry is the last fragment of a frame
+ * free the skb structure and unmap pci memory */
+ skb = priv->data_low_tx[index];
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING,
+ "cleanup skb %p skb->data %p skb->len %u truesize %u\n ",
+ skb, skb->data, skb->len, skb->truesize);
+#endif
+
+ pci_unmap_single(priv->pdev,
+ priv->pci_map_tx_address[index],
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(skb);
+ skb = NULL;
+ }
+ /* increment the free data low queue pointer */
+ priv->free_data_tx++;
+ }
+}
+
+netdev_tx_t
+islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ isl38xx_control_block *cb = priv->control_block;
+ u32 index;
+ dma_addr_t pci_map_address;
+ int frame_size;
+ isl38xx_fragment *fragment;
+ int offset;
+ struct sk_buff *newskb;
+ int newskb_offset;
+ unsigned long flags;
+ unsigned char wds_mac[6];
+ u32 curr_frag;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit\n");
+#endif
+
+ /* lock the driver code */
+ spin_lock_irqsave(&priv->slock, flags);
+
+ /* check whether the destination queue has enough fragments for the frame */
+ curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]);
+ if (unlikely(curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE)) {
+ printk(KERN_ERR "%s: transmit device queue full when awake\n",
+ ndev->name);
+ netif_stop_queue(ndev);
+
+ /* trigger the device */
+ isl38xx_w32_flush(priv->device_base, ISL38XX_DEV_INT_UPDATE,
+ ISL38XX_DEV_INT_REG);
+ udelay(ISL38XX_WRITEIO_DELAY);
+ goto drop_free;
+ }
+ /* Check alignment and WDS frame formatting. The start of the packet should
+ * be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes
+ * and add WDS address information */
+ if (likely(((long) skb->data & 0x03) | init_wds)) {
+ /* get the number of bytes to add and re-align */
+ offset = (4 - (long) skb->data) & 0x03;
+ offset += init_wds ? 6 : 0;
+
+ /* check whether the current skb can be used */
+ if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) {
+ unsigned char *src = skb->data;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "skb offset %i wds %i\n", offset,
+ init_wds);
+#endif
+
+ /* align the buffer on 4-byte boundary */
+ skb_reserve(skb, (4 - (long) skb->data) & 0x03);
+ if (init_wds) {
+ /* wds requires an additional address field of 6 bytes */
+ skb_put(skb, 6);
+#ifdef ISLPCI_ETH_DEBUG
+ printk("islpci_eth_transmit:wds_mac\n");
+#endif
+ memmove(skb->data + 6, src, skb->len);
+ skb_copy_to_linear_data(skb, wds_mac, 6);
+ } else {
+ memmove(skb->data, src, skb->len);
+ }
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "memmove %p %p %i\n", skb->data,
+ src, skb->len);
+#endif
+ } else {
+ newskb =
+ dev_alloc_skb(init_wds ? skb->len + 6 : skb->len);
+ if (unlikely(newskb == NULL)) {
+ printk(KERN_ERR "%s: Cannot allocate skb\n",
+ ndev->name);
+ goto drop_free;
+ }
+ newskb_offset = (4 - (long) newskb->data) & 0x03;
+
+ /* Check if newskb->data is aligned */
+ if (newskb_offset)
+ skb_reserve(newskb, newskb_offset);
+
+ skb_put(newskb, init_wds ? skb->len + 6 : skb->len);
+ if (init_wds) {
+ skb_copy_from_linear_data(skb,
+ newskb->data + 6,
+ skb->len);
+ skb_copy_to_linear_data(newskb, wds_mac, 6);
+#ifdef ISLPCI_ETH_DEBUG
+ printk("islpci_eth_transmit:wds_mac\n");
+#endif
+ } else
+ skb_copy_from_linear_data(skb, newskb->data,
+ skb->len);
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n",
+ newskb->data, skb->data, skb->len, init_wds);
+#endif
+
+ newskb->dev = skb->dev;
+ dev_kfree_skb_irq(skb);
+ skb = newskb;
+ }
+ }
+ /* display the buffer contents for debugging */
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_BUFFER_CONTENTS, "\ntx %p ", skb->data);
+ display_buffer((char *) skb->data, skb->len);
+#endif
+
+ /* map the skb buffer to pci memory for DMA operation */
+ pci_map_address = pci_map_single(priv->pdev,
+ (void *) skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pdev, pci_map_address)) {
+ printk(KERN_WARNING "%s: cannot map buffer to PCI\n",
+ ndev->name);
+ goto drop_free;
+ }
+ /* Place the fragment in the control block structure. */
+ index = curr_frag % ISL38XX_CB_TX_QSIZE;
+ fragment = &cb->tx_data_low[index];
+
+ priv->pci_map_tx_address[index] = pci_map_address;
+ /* store the skb address for future freeing */
+ priv->data_low_tx[index] = skb;
+ /* set the proper fragment start address and size information */
+ frame_size = skb->len;
+ fragment->size = cpu_to_le16(frame_size);
+ fragment->flags = cpu_to_le16(0); /* set to 1 if more fragments */
+ fragment->address = cpu_to_le32(pci_map_address);
+ curr_frag++;
+
+ /* The fragment address in the control block must have been
+ * written before announcing the frame buffer to device. */
+ wmb();
+ cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ] = cpu_to_le32(curr_frag);
+
+ if (curr_frag - priv->free_data_tx + ISL38XX_MIN_QTHRESHOLD
+ > ISL38XX_CB_TX_QSIZE) {
+ /* stop sends from upper layers */
+ netif_stop_queue(ndev);
+
+ /* set the full flag for the transmission queue */
+ priv->data_low_tx_full = 1;
+ }
+
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+
+ /* trigger the device */
+ islpci_trigger(priv);
+
+ /* unlock the driver code */
+ spin_unlock_irqrestore(&priv->slock, flags);
+
+ return NETDEV_TX_OK;
+
+ drop_free:
+ ndev->stats.tx_dropped++;
+ spin_unlock_irqrestore(&priv->slock, flags);
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static inline int
+islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb)
+{
+ /* The card reports full 802.11 packets but with a 20 bytes
+ * header and without the FCS. But there a is a bit that
+ * indicates if the packet is corrupted :-) */
+ struct rfmon_header *hdr = (struct rfmon_header *) (*skb)->data;
+
+ if (hdr->flags & 0x01)
+ /* This one is bad. Drop it ! */
+ return -1;
+ if (priv->ndev->type == ARPHRD_IEEE80211_PRISM) {
+ struct avs_80211_1_header *avs;
+ /* extract the relevant data from the header */
+ u32 clock = le32_to_cpu(hdr->clock);
+ u8 rate = hdr->rate;
+ u16 freq = le16_to_cpu(hdr->freq);
+ u8 rssi = hdr->rssi;
+
+ skb_pull(*skb, sizeof (struct rfmon_header));
+
+ if (skb_headroom(*skb) < sizeof (struct avs_80211_1_header)) {
+ struct sk_buff *newskb = skb_copy_expand(*skb,
+ sizeof (struct
+ avs_80211_1_header),
+ 0, GFP_ATOMIC);
+ if (newskb) {
+ dev_kfree_skb_irq(*skb);
+ *skb = newskb;
+ } else
+ return -1;
+ /* This behavior is not very subtile... */
+ }
+
+ /* make room for the new header and fill it. */
+ avs =
+ (struct avs_80211_1_header *) skb_push(*skb,
+ sizeof (struct
+ avs_80211_1_header));
+
+ avs->version = cpu_to_be32(P80211CAPTURE_VERSION);
+ avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header));
+ avs->mactime = cpu_to_be64(clock);
+ avs->hosttime = cpu_to_be64(jiffies);
+ avs->phytype = cpu_to_be32(6); /*OFDM: 6 for (g), 8 for (a) */
+ avs->channel = cpu_to_be32(channel_of_freq(freq));
+ avs->datarate = cpu_to_be32(rate * 5);
+ avs->antenna = cpu_to_be32(0); /*unknown */
+ avs->priority = cpu_to_be32(0); /*unknown */
+ avs->ssi_type = cpu_to_be32(3); /*2: dBm, 3: raw RSSI */
+ avs->ssi_signal = cpu_to_be32(rssi & 0x7f);
+ avs->ssi_noise = cpu_to_be32(priv->local_iwstatistics.qual.noise); /*better than 'undefined', I assume */
+ avs->preamble = cpu_to_be32(0); /*unknown */
+ avs->encoding = cpu_to_be32(0); /*unknown */
+ } else
+ skb_pull(*skb, sizeof (struct rfmon_header));
+
+ (*skb)->protocol = htons(ETH_P_802_2);
+ skb_reset_mac_header(*skb);
+ (*skb)->pkt_type = PACKET_OTHERHOST;
+
+ return 0;
+}
+
+int
+islpci_eth_receive(islpci_private *priv)
+{
+ struct net_device *ndev = priv->ndev;
+ isl38xx_control_block *control_block = priv->control_block;
+ struct sk_buff *skb;
+ u16 size;
+ u32 index, offset;
+ unsigned char *src;
+ int discard = 0;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_receive\n");
+#endif
+
+ /* the device has written an Ethernet frame in the data area
+ * of the sk_buff without updating the structure, do it now */
+ index = priv->free_data_rx % ISL38XX_CB_RX_QSIZE;
+ size = le16_to_cpu(control_block->rx_data_low[index].size);
+ skb = priv->data_low_rx[index];
+ offset = ((unsigned long)
+ le32_to_cpu(control_block->rx_data_low[index].address) -
+ (unsigned long) skb->data) & 3;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING,
+ "frq->addr %x skb->data %p skb->len %u offset %u truesize %u\n ",
+ control_block->rx_data_low[priv->free_data_rx].address, skb->data,
+ skb->len, offset, skb->truesize);
+#endif
+
+ /* delete the streaming DMA mapping before processing the skb */
+ pci_unmap_single(priv->pdev,
+ priv->pci_map_rx_address[index],
+ MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE);
+
+ /* update the skb structure and align the buffer */
+ skb_put(skb, size);
+ if (offset) {
+ /* shift the buffer allocation offset bytes to get the right frame */
+ skb_pull(skb, 2);
+ skb_put(skb, 2);
+ }
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ /* display the buffer contents for debugging */
+ DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
+ display_buffer((char *) skb->data, skb->len);
+#endif
+
+ /* check whether WDS is enabled and whether the data frame is a WDS frame */
+
+ if (init_wds) {
+ /* WDS enabled, check for the wds address on the first 6 bytes of the buffer */
+ src = skb->data + 6;
+ memmove(skb->data, src, skb->len - 6);
+ skb_trim(skb, skb->len - 6);
+ }
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "Fragment size %i in skb at %p\n", size, skb);
+ DEBUG(SHOW_TRACING, "Skb data at %p, length %i\n", skb->data, skb->len);
+
+ /* display the buffer contents for debugging */
+ DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
+ display_buffer((char *) skb->data, skb->len);
+#endif
+ /* take care of monitor mode and spy monitoring. */
+ if (unlikely(priv->iw_mode == IW_MODE_MONITOR)) {
+ skb->dev = ndev;
+ discard = islpci_monitor_rx(priv, &skb);
+ } else {
+ if (unlikely(skb->data[2 * ETH_ALEN] == 0)) {
+ /* The packet has a rx_annex. Read it for spy monitoring, Then
+ * remove it, while keeping the 2 leading MAC addr.
+ */
+ struct iw_quality wstats;
+ struct rx_annex_header *annex =
+ (struct rx_annex_header *) skb->data;
+ wstats.level = annex->rfmon.rssi;
+ /* The noise value can be a bit outdated if nobody's
+ * reading wireless stats... */
+ wstats.noise = priv->local_iwstatistics.qual.noise;
+ wstats.qual = wstats.level - wstats.noise;
+ wstats.updated = 0x07;
+ /* Update spy records */
+ wireless_spy_update(ndev, annex->addr2, &wstats);
+
+ skb_copy_from_linear_data(skb,
+ (skb->data +
+ sizeof(struct rfmon_header)),
+ 2 * ETH_ALEN);
+ skb_pull(skb, sizeof (struct rfmon_header));
+ }
+ skb->protocol = eth_type_trans(skb, ndev);
+ }
+ skb->ip_summed = CHECKSUM_NONE;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += size;
+
+ /* deliver the skb to the network layer */
+#ifdef ISLPCI_ETH_DEBUG
+ printk
+ ("islpci_eth_receive:netif_rx %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ skb->data[0], skb->data[1], skb->data[2], skb->data[3],
+ skb->data[4], skb->data[5]);
+#endif
+ if (unlikely(discard)) {
+ dev_kfree_skb_irq(skb);
+ skb = NULL;
+ } else
+ netif_rx(skb);
+
+ /* increment the read index for the rx data low queue */
+ priv->free_data_rx++;
+
+ /* add one or more sk_buff structures */
+ while (index =
+ le32_to_cpu(control_block->
+ driver_curr_frag[ISL38XX_CB_RX_DATA_LQ]),
+ index - priv->free_data_rx < ISL38XX_CB_RX_QSIZE) {
+ /* allocate an sk_buff for received data frames storage
+ * include any required allignment operations */
+ skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2);
+ if (unlikely(skb == NULL)) {
+ /* error allocating an sk_buff structure elements */
+ DEBUG(SHOW_ERROR_MESSAGES, "Error allocating skb\n");
+ break;
+ }
+ skb_reserve(skb, (4 - (long) skb->data) & 0x03);
+ /* store the new skb structure pointer */
+ index = index % ISL38XX_CB_RX_QSIZE;
+ priv->data_low_rx[index] = skb;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING,
+ "new alloc skb %p skb->data %p skb->len %u index %u truesize %u\n ",
+ skb, skb->data, skb->len, index, skb->truesize);
+#endif
+
+ /* set the streaming DMA mapping for proper PCI bus operation */
+ priv->pci_map_rx_address[index] =
+ pci_map_single(priv->pdev, (void *) skb->data,
+ MAX_FRAGMENT_SIZE_RX + 2,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pdev,
+ priv->pci_map_rx_address[index])) {
+ /* error mapping the buffer to device accessible memory address */
+ DEBUG(SHOW_ERROR_MESSAGES,
+ "Error mapping DMA address\n");
+
+ /* free the skbuf structure before aborting */
+ dev_kfree_skb_irq(skb);
+ skb = NULL;
+ break;
+ }
+ /* update the fragment address */
+ control_block->rx_data_low[index].address =
+ cpu_to_le32((u32)priv->pci_map_rx_address[index]);
+ wmb();
+
+ /* increment the driver read pointer */
+ le32_add_cpu(&control_block->
+ driver_curr_frag[ISL38XX_CB_RX_DATA_LQ], 1);
+ }
+
+ /* trigger the device */
+ islpci_trigger(priv);
+
+ return 0;
+}
+
+void
+islpci_do_reset_and_wake(struct work_struct *work)
+{
+ islpci_private *priv = container_of(work, islpci_private, reset_task);
+
+ islpci_reset(priv, 1);
+ priv->reset_task_pending = 0;
+ smp_wmb();
+ netif_wake_queue(priv->ndev);
+}
+
+void
+islpci_eth_tx_timeout(struct net_device *ndev)
+{
+ islpci_private *priv = netdev_priv(ndev);
+
+ /* increment the transmit error counter */
+ ndev->stats.tx_errors++;
+
+ if (!priv->reset_task_pending) {
+ printk(KERN_WARNING
+ "%s: tx_timeout, scheduling reset", ndev->name);
+ netif_stop_queue(ndev);
+ priv->reset_task_pending = 1;
+ schedule_work(&priv->reset_task);
+ } else {
+ printk(KERN_WARNING
+ "%s: tx_timeout, waiting for reset", ndev->name);
+ }
+}
diff --git a/drivers/net/wireless/prism54/islpci_eth.h b/drivers/net/wireless/intersil/prism54/islpci_eth.h
index 80f50f1bc6f2..80f50f1bc6f2 100644
--- a/drivers/net/wireless/prism54/islpci_eth.h
+++ b/drivers/net/wireless/intersil/prism54/islpci_eth.h
diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/intersil/prism54/islpci_hotplug.c
index 300c846ea087..300c846ea087 100644
--- a/drivers/net/wireless/prism54/islpci_hotplug.c
+++ b/drivers/net/wireless/intersil/prism54/islpci_hotplug.c
diff --git a/drivers/net/wireless/intersil/prism54/islpci_mgt.c b/drivers/net/wireless/intersil/prism54/islpci_mgt.c
new file mode 100644
index 000000000000..53d7a1705e8e
--- /dev/null
+++ b/drivers/net/wireless/intersil/prism54/islpci_mgt.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2002 Intersil Americas Inc.
+ * Copyright 2004 Jens Maurer <Jens.Maurer@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <linux/if_arp.h>
+
+#include "prismcompat.h"
+#include "isl_38xx.h"
+#include "islpci_mgt.h"
+#include "isl_oid.h" /* additional types and defs for isl38xx fw */
+#include "isl_ioctl.h"
+
+#include <net/iw_handler.h>
+
+/******************************************************************************
+ Global variable definition section
+******************************************************************************/
+int pc_debug = VERBOSE;
+module_param(pc_debug, int, 0);
+
+/******************************************************************************
+ Driver general functions
+******************************************************************************/
+#if VERBOSE > SHOW_ERROR_MESSAGES
+void
+display_buffer(char *buffer, int length)
+{
+ if ((pc_debug & SHOW_BUFFER_CONTENTS) == 0)
+ return;
+
+ while (length > 0) {
+ printk("[%02x]", *buffer & 255);
+ length--;
+ buffer++;
+ }
+
+ printk("\n");
+}
+#endif
+
+/*****************************************************************************
+ Queue handling for management frames
+******************************************************************************/
+
+/*
+ * Helper function to create a PIMFOR management frame header.
+ */
+static void
+pimfor_encode_header(int operation, u32 oid, u32 length, pimfor_header_t *h)
+{
+ h->version = PIMFOR_VERSION;
+ h->operation = operation;
+ h->device_id = PIMFOR_DEV_ID_MHLI_MIB;
+ h->flags = 0;
+ h->oid = cpu_to_be32(oid);
+ h->length = cpu_to_be32(length);
+}
+
+/*
+ * Helper function to analyze a PIMFOR management frame header.
+ */
+static pimfor_header_t *
+pimfor_decode_header(void *data, int len)
+{
+ pimfor_header_t *h = data;
+
+ while ((void *) h < data + len) {
+ if (h->flags & PIMFOR_FLAG_LITTLE_ENDIAN) {
+ le32_to_cpus(&h->oid);
+ le32_to_cpus(&h->length);
+ } else {
+ be32_to_cpus(&h->oid);
+ be32_to_cpus(&h->length);
+ }
+ if (h->oid != OID_INL_TUNNEL)
+ return h;
+ h++;
+ }
+ return NULL;
+}
+
+/*
+ * Fill the receive queue for management frames with fresh buffers.
+ */
+int
+islpci_mgmt_rx_fill(struct net_device *ndev)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ isl38xx_control_block *cb = /* volatile not needed */
+ (isl38xx_control_block *) priv->control_block;
+ u32 curr = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ]);
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgmt_rx_fill\n");
+#endif
+
+ while (curr - priv->index_mgmt_rx < ISL38XX_CB_MGMT_QSIZE) {
+ u32 index = curr % ISL38XX_CB_MGMT_QSIZE;
+ struct islpci_membuf *buf = &priv->mgmt_rx[index];
+ isl38xx_fragment *frag = &cb->rx_data_mgmt[index];
+
+ if (buf->mem == NULL) {
+ buf->mem = kmalloc(MGMT_FRAME_SIZE, GFP_ATOMIC);
+ if (!buf->mem)
+ return -ENOMEM;
+ buf->size = MGMT_FRAME_SIZE;
+ }
+ if (buf->pci_addr == 0) {
+ buf->pci_addr = pci_map_single(priv->pdev, buf->mem,
+ MGMT_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pdev, buf->pci_addr)) {
+ printk(KERN_WARNING
+ "Failed to make memory DMA'able.\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* be safe: always reset control block information */
+ frag->size = cpu_to_le16(MGMT_FRAME_SIZE);
+ frag->flags = 0;
+ frag->address = cpu_to_le32(buf->pci_addr);
+ curr++;
+
+ /* The fragment address in the control block must have
+ * been written before announcing the frame buffer to
+ * device */
+ wmb();
+ cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] = cpu_to_le32(curr);
+ }
+ return 0;
+}
+
+/*
+ * Create and transmit a management frame using "operation" and "oid",
+ * with arguments data/length.
+ * We either return an error and free the frame, or we return 0 and
+ * islpci_mgt_cleanup_transmit() frees the frame in the tx-done
+ * interrupt.
+ */
+static int
+islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid,
+ void *data, int length)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ isl38xx_control_block *cb =
+ (isl38xx_control_block *) priv->control_block;
+ void *p;
+ int err = -EINVAL;
+ unsigned long flags;
+ isl38xx_fragment *frag;
+ struct islpci_membuf buf;
+ u32 curr_frag;
+ int index;
+ int frag_len = length + PIMFOR_HEADER_SIZE;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_transmit\n");
+#endif
+
+ if (frag_len > MGMT_FRAME_SIZE) {
+ printk(KERN_DEBUG "%s: mgmt frame too large %d\n",
+ ndev->name, frag_len);
+ goto error;
+ }
+
+ err = -ENOMEM;
+ p = buf.mem = kmalloc(frag_len, GFP_KERNEL);
+ if (!buf.mem)
+ goto error;
+
+ buf.size = frag_len;
+
+ /* create the header directly in the fragment data area */
+ pimfor_encode_header(operation, oid, length, (pimfor_header_t *) p);
+ p += PIMFOR_HEADER_SIZE;
+
+ if (data)
+ memcpy(p, data, length);
+ else
+ memset(p, 0, length);
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ {
+ pimfor_header_t *h = buf.mem;
+ DEBUG(SHOW_PIMFOR_FRAMES,
+ "PIMFOR: op %i, oid 0x%08lx, device %i, flags 0x%x length 0x%x\n",
+ h->operation, oid, h->device_id, h->flags, length);
+
+ /* display the buffer contents for debugging */
+ display_buffer((char *) h, sizeof (pimfor_header_t));
+ display_buffer(p, length);
+ }
+#endif
+
+ err = -ENOMEM;
+ buf.pci_addr = pci_map_single(priv->pdev, buf.mem, frag_len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pdev, buf.pci_addr)) {
+ printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n",
+ ndev->name);
+ goto error_free;
+ }
+
+ /* Protect the control block modifications against interrupts. */
+ spin_lock_irqsave(&priv->slock, flags);
+ curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ]);
+ if (curr_frag - priv->index_mgmt_tx >= ISL38XX_CB_MGMT_QSIZE) {
+ printk(KERN_WARNING "%s: mgmt tx queue is still full\n",
+ ndev->name);
+ goto error_unlock;
+ }
+
+ /* commit the frame to the tx device queue */
+ index = curr_frag % ISL38XX_CB_MGMT_QSIZE;
+ priv->mgmt_tx[index] = buf;
+ frag = &cb->tx_data_mgmt[index];
+ frag->size = cpu_to_le16(frag_len);
+ frag->flags = 0; /* for any other than the last fragment, set to 1 */
+ frag->address = cpu_to_le32(buf.pci_addr);
+
+ /* The fragment address in the control block must have
+ * been written before announcing the frame buffer to
+ * device */
+ wmb();
+ cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ] = cpu_to_le32(curr_frag + 1);
+ spin_unlock_irqrestore(&priv->slock, flags);
+
+ /* trigger the device */
+ islpci_trigger(priv);
+ return 0;
+
+ error_unlock:
+ spin_unlock_irqrestore(&priv->slock, flags);
+ error_free:
+ kfree(buf.mem);
+ error:
+ return err;
+}
+
+/*
+ * Receive a management frame from the device.
+ * This can be an arbitrary number of traps, and at most one response
+ * frame for a previous request sent via islpci_mgt_transmit().
+ */
+int
+islpci_mgt_receive(struct net_device *ndev)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ isl38xx_control_block *cb =
+ (isl38xx_control_block *) priv->control_block;
+ u32 curr_frag;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_receive\n");
+#endif
+
+ /* Only once per interrupt, determine fragment range to
+ * process. This avoids an endless loop (i.e. lockup) if
+ * frames come in faster than we can process them. */
+ curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_RX_MGMTQ]);
+ barrier();
+
+ for (; priv->index_mgmt_rx < curr_frag; priv->index_mgmt_rx++) {
+ pimfor_header_t *header;
+ u32 index = priv->index_mgmt_rx % ISL38XX_CB_MGMT_QSIZE;
+ struct islpci_membuf *buf = &priv->mgmt_rx[index];
+ u16 frag_len;
+ int size;
+ struct islpci_mgmtframe *frame;
+
+ /* I have no idea (and no documentation) if flags != 0
+ * is possible. Drop the frame, reuse the buffer. */
+ if (le16_to_cpu(cb->rx_data_mgmt[index].flags) != 0) {
+ printk(KERN_WARNING "%s: unknown flags 0x%04x\n",
+ ndev->name,
+ le16_to_cpu(cb->rx_data_mgmt[index].flags));
+ continue;
+ }
+
+ /* The device only returns the size of the header(s) here. */
+ frag_len = le16_to_cpu(cb->rx_data_mgmt[index].size);
+
+ /*
+ * We appear to have no way to tell the device the
+ * size of a receive buffer. Thus, if this check
+ * triggers, we likely have kernel heap corruption. */
+ if (frag_len > MGMT_FRAME_SIZE) {
+ printk(KERN_WARNING
+ "%s: Bogus packet size of %d (%#x).\n",
+ ndev->name, frag_len, frag_len);
+ frag_len = MGMT_FRAME_SIZE;
+ }
+
+ /* Ensure the results of device DMA are visible to the CPU. */
+ pci_dma_sync_single_for_cpu(priv->pdev, buf->pci_addr,
+ buf->size, PCI_DMA_FROMDEVICE);
+
+ /* Perform endianess conversion for PIMFOR header in-place. */
+ header = pimfor_decode_header(buf->mem, frag_len);
+ if (!header) {
+ printk(KERN_WARNING "%s: no PIMFOR header found\n",
+ ndev->name);
+ continue;
+ }
+
+ /* The device ID from the PIMFOR packet received from
+ * the MVC is always 0. We forward a sensible device_id.
+ * Not that anyone upstream would care... */
+ header->device_id = priv->ndev->ifindex;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_PIMFOR_FRAMES,
+ "PIMFOR: op %i, oid 0x%08x, device %i, flags 0x%x length 0x%x\n",
+ header->operation, header->oid, header->device_id,
+ header->flags, header->length);
+
+ /* display the buffer contents for debugging */
+ display_buffer((char *) header, PIMFOR_HEADER_SIZE);
+ display_buffer((char *) header + PIMFOR_HEADER_SIZE,
+ header->length);
+#endif
+
+ /* nobody sends these */
+ if (header->flags & PIMFOR_FLAG_APPLIC_ORIGIN) {
+ printk(KERN_DEBUG
+ "%s: errant PIMFOR application frame\n",
+ ndev->name);
+ continue;
+ }
+
+ /* Determine frame size, skipping OID_INL_TUNNEL headers. */
+ size = PIMFOR_HEADER_SIZE + header->length;
+ frame = kmalloc(sizeof(struct islpci_mgmtframe) + size,
+ GFP_ATOMIC);
+ if (!frame)
+ continue;
+
+ frame->ndev = ndev;
+ memcpy(&frame->buf, header, size);
+ frame->header = (pimfor_header_t *) frame->buf;
+ frame->data = frame->buf + PIMFOR_HEADER_SIZE;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_PIMFOR_FRAMES,
+ "frame: header: %p, data: %p, size: %d\n",
+ frame->header, frame->data, size);
+#endif
+
+ if (header->operation == PIMFOR_OP_TRAP) {
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ printk(KERN_DEBUG
+ "TRAP: oid 0x%x, device %i, flags 0x%x length %i\n",
+ header->oid, header->device_id, header->flags,
+ header->length);
+#endif
+
+ /* Create work to handle trap out of interrupt
+ * context. */
+ INIT_WORK(&frame->ws, prism54_process_trap);
+ schedule_work(&frame->ws);
+
+ } else {
+ /* Signal the one waiting process that a response
+ * has been received. */
+ if ((frame = xchg(&priv->mgmt_received, frame)) != NULL) {
+ printk(KERN_WARNING
+ "%s: mgmt response not collected\n",
+ ndev->name);
+ kfree(frame);
+ }
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "Wake up Mgmt Queue\n");
+#endif
+ wake_up(&priv->mgmt_wqueue);
+ }
+
+ }
+
+ return 0;
+}
+
+/*
+ * Cleanup the transmit queue by freeing all frames handled by the device.
+ */
+void
+islpci_mgt_cleanup_transmit(struct net_device *ndev)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ isl38xx_control_block *cb = /* volatile not needed */
+ (isl38xx_control_block *) priv->control_block;
+ u32 curr_frag;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_cleanup_transmit\n");
+#endif
+
+ /* Only once per cleanup, determine fragment range to
+ * process. This avoids an endless loop (i.e. lockup) if
+ * the device became confused, incrementing device_curr_frag
+ * rapidly. */
+ curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_TX_MGMTQ]);
+ barrier();
+
+ for (; priv->index_mgmt_tx < curr_frag; priv->index_mgmt_tx++) {
+ int index = priv->index_mgmt_tx % ISL38XX_CB_MGMT_QSIZE;
+ struct islpci_membuf *buf = &priv->mgmt_tx[index];
+ pci_unmap_single(priv->pdev, buf->pci_addr, buf->size,
+ PCI_DMA_TODEVICE);
+ buf->pci_addr = 0;
+ kfree(buf->mem);
+ buf->mem = NULL;
+ buf->size = 0;
+ }
+}
+
+/*
+ * Perform one request-response transaction to the device.
+ */
+int
+islpci_mgt_transaction(struct net_device *ndev,
+ int operation, unsigned long oid,
+ void *senddata, int sendlen,
+ struct islpci_mgmtframe **recvframe)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ const long wait_cycle_jiffies = msecs_to_jiffies(ISL38XX_WAIT_CYCLE * 10);
+ long timeout_left = ISL38XX_MAX_WAIT_CYCLES * wait_cycle_jiffies;
+ int err;
+ DEFINE_WAIT(wait);
+
+ *recvframe = NULL;
+
+ if (mutex_lock_interruptible(&priv->mgmt_lock))
+ return -ERESTARTSYS;
+
+ prepare_to_wait(&priv->mgmt_wqueue, &wait, TASK_UNINTERRUPTIBLE);
+ err = islpci_mgt_transmit(ndev, operation, oid, senddata, sendlen);
+ if (err)
+ goto out;
+
+ err = -ETIMEDOUT;
+ while (timeout_left > 0) {
+ int timeleft;
+ struct islpci_mgmtframe *frame;
+
+ timeleft = schedule_timeout_uninterruptible(wait_cycle_jiffies);
+ frame = xchg(&priv->mgmt_received, NULL);
+ if (frame) {
+ if (frame->header->oid == oid) {
+ *recvframe = frame;
+ err = 0;
+ goto out;
+ } else {
+ printk(KERN_DEBUG
+ "%s: expecting oid 0x%x, received 0x%x.\n",
+ ndev->name, (unsigned int) oid,
+ frame->header->oid);
+ kfree(frame);
+ frame = NULL;
+ }
+ }
+ if (timeleft == 0) {
+ printk(KERN_DEBUG
+ "%s: timeout waiting for mgmt response %lu, "
+ "triggering device\n",
+ ndev->name, timeout_left);
+ islpci_trigger(priv);
+ }
+ timeout_left += timeleft - wait_cycle_jiffies;
+ }
+ printk(KERN_WARNING "%s: timeout waiting for mgmt response\n",
+ ndev->name);
+
+ /* TODO: we should reset the device here */
+ out:
+ finish_wait(&priv->mgmt_wqueue, &wait);
+ mutex_unlock(&priv->mgmt_lock);
+ return err;
+}
+
diff --git a/drivers/net/wireless/prism54/islpci_mgt.h b/drivers/net/wireless/intersil/prism54/islpci_mgt.h
index 700c434c8803..700c434c8803 100644
--- a/drivers/net/wireless/prism54/islpci_mgt.h
+++ b/drivers/net/wireless/intersil/prism54/islpci_mgt.h
diff --git a/drivers/net/wireless/intersil/prism54/oid_mgt.c b/drivers/net/wireless/intersil/prism54/oid_mgt.c
new file mode 100644
index 000000000000..6528ed5b9b1d
--- /dev/null
+++ b/drivers/net/wireless/intersil/prism54/oid_mgt.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 2003,2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "prismcompat.h"
+#include "islpci_dev.h"
+#include "islpci_mgt.h"
+#include "isl_oid.h"
+#include "oid_mgt.h"
+#include "isl_ioctl.h"
+
+/* to convert between channel and freq */
+static const int frequency_list_bg[] = { 2412, 2417, 2422, 2427, 2432,
+ 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484
+};
+
+int
+channel_of_freq(int f)
+{
+ int c = 0;
+
+ if ((f >= 2412) && (f <= 2484)) {
+ while ((c < 14) && (f != frequency_list_bg[c]))
+ c++;
+ return (c >= 14) ? 0 : ++c;
+ } else if ((f >= (int) 5000) && (f <= (int) 6000)) {
+ return ( (f - 5000) / 5 );
+ } else
+ return 0;
+}
+
+#define OID_STRUCT(name,oid,s,t) [name] = {oid, 0, sizeof(s), t}
+#define OID_STRUCT_C(name,oid,s,t) OID_STRUCT(name,oid,s,t | OID_FLAG_CACHED)
+#define OID_U32(name,oid) OID_STRUCT(name,oid,u32,OID_TYPE_U32)
+#define OID_U32_C(name,oid) OID_STRUCT_C(name,oid,u32,OID_TYPE_U32)
+#define OID_STRUCT_MLME(name,oid) OID_STRUCT(name,oid,struct obj_mlme,OID_TYPE_MLME)
+#define OID_STRUCT_MLMEEX(name,oid) OID_STRUCT(name,oid,struct obj_mlmeex,OID_TYPE_MLMEEX)
+
+#define OID_UNKNOWN(name,oid) OID_STRUCT(name,oid,0,0)
+
+struct oid_t isl_oid[] = {
+ OID_STRUCT(GEN_OID_MACADDRESS, 0x00000000, u8[6], OID_TYPE_ADDR),
+ OID_U32(GEN_OID_LINKSTATE, 0x00000001),
+ OID_UNKNOWN(GEN_OID_WATCHDOG, 0x00000002),
+ OID_UNKNOWN(GEN_OID_MIBOP, 0x00000003),
+ OID_UNKNOWN(GEN_OID_OPTIONS, 0x00000004),
+ OID_UNKNOWN(GEN_OID_LEDCONFIG, 0x00000005),
+
+ /* 802.11 */
+ OID_U32_C(DOT11_OID_BSSTYPE, 0x10000000),
+ OID_STRUCT_C(DOT11_OID_BSSID, 0x10000001, u8[6], OID_TYPE_RAW),
+ OID_STRUCT_C(DOT11_OID_SSID, 0x10000002, struct obj_ssid,
+ OID_TYPE_SSID),
+ OID_U32(DOT11_OID_STATE, 0x10000003),
+ OID_U32(DOT11_OID_AID, 0x10000004),
+ OID_STRUCT(DOT11_OID_COUNTRYSTRING, 0x10000005, u8[4], OID_TYPE_RAW),
+ OID_STRUCT_C(DOT11_OID_SSIDOVERRIDE, 0x10000006, struct obj_ssid,
+ OID_TYPE_SSID),
+
+ OID_U32(DOT11_OID_MEDIUMLIMIT, 0x11000000),
+ OID_U32_C(DOT11_OID_BEACONPERIOD, 0x11000001),
+ OID_U32(DOT11_OID_DTIMPERIOD, 0x11000002),
+ OID_U32(DOT11_OID_ATIMWINDOW, 0x11000003),
+ OID_U32(DOT11_OID_LISTENINTERVAL, 0x11000004),
+ OID_U32(DOT11_OID_CFPPERIOD, 0x11000005),
+ OID_U32(DOT11_OID_CFPDURATION, 0x11000006),
+
+ OID_U32_C(DOT11_OID_AUTHENABLE, 0x12000000),
+ OID_U32_C(DOT11_OID_PRIVACYINVOKED, 0x12000001),
+ OID_U32_C(DOT11_OID_EXUNENCRYPTED, 0x12000002),
+ OID_U32_C(DOT11_OID_DEFKEYID, 0x12000003),
+ [DOT11_OID_DEFKEYX] = {0x12000004, 3, sizeof (struct obj_key),
+ OID_FLAG_CACHED | OID_TYPE_KEY}, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */
+ OID_UNKNOWN(DOT11_OID_STAKEY, 0x12000008),
+ OID_U32(DOT11_OID_REKEYTHRESHOLD, 0x12000009),
+ OID_UNKNOWN(DOT11_OID_STASC, 0x1200000a),
+
+ OID_U32(DOT11_OID_PRIVTXREJECTED, 0x1a000000),
+ OID_U32(DOT11_OID_PRIVRXPLAIN, 0x1a000001),
+ OID_U32(DOT11_OID_PRIVRXFAILED, 0x1a000002),
+ OID_U32(DOT11_OID_PRIVRXNOKEY, 0x1a000003),
+
+ OID_U32_C(DOT11_OID_RTSTHRESH, 0x13000000),
+ OID_U32_C(DOT11_OID_FRAGTHRESH, 0x13000001),
+ OID_U32_C(DOT11_OID_SHORTRETRIES, 0x13000002),
+ OID_U32_C(DOT11_OID_LONGRETRIES, 0x13000003),
+ OID_U32_C(DOT11_OID_MAXTXLIFETIME, 0x13000004),
+ OID_U32(DOT11_OID_MAXRXLIFETIME, 0x13000005),
+ OID_U32(DOT11_OID_AUTHRESPTIMEOUT, 0x13000006),
+ OID_U32(DOT11_OID_ASSOCRESPTIMEOUT, 0x13000007),
+
+ OID_UNKNOWN(DOT11_OID_ALOFT_TABLE, 0x1d000000),
+ OID_UNKNOWN(DOT11_OID_ALOFT_CTRL_TABLE, 0x1d000001),
+ OID_UNKNOWN(DOT11_OID_ALOFT_RETREAT, 0x1d000002),
+ OID_UNKNOWN(DOT11_OID_ALOFT_PROGRESS, 0x1d000003),
+ OID_U32(DOT11_OID_ALOFT_FIXEDRATE, 0x1d000004),
+ OID_UNKNOWN(DOT11_OID_ALOFT_RSSIGRAPH, 0x1d000005),
+ OID_UNKNOWN(DOT11_OID_ALOFT_CONFIG, 0x1d000006),
+
+ [DOT11_OID_VDCFX] = {0x1b000000, 7, 0, 0},
+ OID_U32(DOT11_OID_MAXFRAMEBURST, 0x1b000008),
+
+ OID_U32(DOT11_OID_PSM, 0x14000000),
+ OID_U32(DOT11_OID_CAMTIMEOUT, 0x14000001),
+ OID_U32(DOT11_OID_RECEIVEDTIMS, 0x14000002),
+ OID_U32(DOT11_OID_ROAMPREFERENCE, 0x14000003),
+
+ OID_U32(DOT11_OID_BRIDGELOCAL, 0x15000000),
+ OID_U32(DOT11_OID_CLIENTS, 0x15000001),
+ OID_U32(DOT11_OID_CLIENTSASSOCIATED, 0x15000002),
+ [DOT11_OID_CLIENTX] = {0x15000003, 2006, 0, 0}, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */
+
+ OID_STRUCT(DOT11_OID_CLIENTFIND, 0x150007DB, u8[6], OID_TYPE_ADDR),
+ OID_STRUCT(DOT11_OID_WDSLINKADD, 0x150007DC, u8[6], OID_TYPE_ADDR),
+ OID_STRUCT(DOT11_OID_WDSLINKREMOVE, 0x150007DD, u8[6], OID_TYPE_ADDR),
+ OID_STRUCT(DOT11_OID_EAPAUTHSTA, 0x150007DE, u8[6], OID_TYPE_ADDR),
+ OID_STRUCT(DOT11_OID_EAPUNAUTHSTA, 0x150007DF, u8[6], OID_TYPE_ADDR),
+ OID_U32_C(DOT11_OID_DOT1XENABLE, 0x150007E0),
+ OID_UNKNOWN(DOT11_OID_MICFAILURE, 0x150007E1),
+ OID_UNKNOWN(DOT11_OID_REKEYINDICATE, 0x150007E2),
+
+ OID_U32(DOT11_OID_MPDUTXSUCCESSFUL, 0x16000000),
+ OID_U32(DOT11_OID_MPDUTXONERETRY, 0x16000001),
+ OID_U32(DOT11_OID_MPDUTXMULTIPLERETRIES, 0x16000002),
+ OID_U32(DOT11_OID_MPDUTXFAILED, 0x16000003),
+ OID_U32(DOT11_OID_MPDURXSUCCESSFUL, 0x16000004),
+ OID_U32(DOT11_OID_MPDURXDUPS, 0x16000005),
+ OID_U32(DOT11_OID_RTSSUCCESSFUL, 0x16000006),
+ OID_U32(DOT11_OID_RTSFAILED, 0x16000007),
+ OID_U32(DOT11_OID_ACKFAILED, 0x16000008),
+ OID_U32(DOT11_OID_FRAMERECEIVES, 0x16000009),
+ OID_U32(DOT11_OID_FRAMEERRORS, 0x1600000A),
+ OID_U32(DOT11_OID_FRAMEABORTS, 0x1600000B),
+ OID_U32(DOT11_OID_FRAMEABORTSPHY, 0x1600000C),
+
+ OID_U32(DOT11_OID_SLOTTIME, 0x17000000),
+ OID_U32(DOT11_OID_CWMIN, 0x17000001),
+ OID_U32(DOT11_OID_CWMAX, 0x17000002),
+ OID_U32(DOT11_OID_ACKWINDOW, 0x17000003),
+ OID_U32(DOT11_OID_ANTENNARX, 0x17000004),
+ OID_U32(DOT11_OID_ANTENNATX, 0x17000005),
+ OID_U32(DOT11_OID_ANTENNADIVERSITY, 0x17000006),
+ OID_U32_C(DOT11_OID_CHANNEL, 0x17000007),
+ OID_U32_C(DOT11_OID_EDTHRESHOLD, 0x17000008),
+ OID_U32(DOT11_OID_PREAMBLESETTINGS, 0x17000009),
+ OID_STRUCT(DOT11_OID_RATES, 0x1700000A, u8[IWMAX_BITRATES + 1],
+ OID_TYPE_RAW),
+ OID_U32(DOT11_OID_CCAMODESUPPORTED, 0x1700000B),
+ OID_U32(DOT11_OID_CCAMODE, 0x1700000C),
+ OID_UNKNOWN(DOT11_OID_RSSIVECTOR, 0x1700000D),
+ OID_UNKNOWN(DOT11_OID_OUTPUTPOWERTABLE, 0x1700000E),
+ OID_U32(DOT11_OID_OUTPUTPOWER, 0x1700000F),
+ OID_STRUCT(DOT11_OID_SUPPORTEDRATES, 0x17000010,
+ u8[IWMAX_BITRATES + 1], OID_TYPE_RAW),
+ OID_U32_C(DOT11_OID_FREQUENCY, 0x17000011),
+ [DOT11_OID_SUPPORTEDFREQUENCIES] =
+ {0x17000012, 0, sizeof (struct obj_frequencies)
+ + sizeof (u16) * IWMAX_FREQ, OID_TYPE_FREQUENCIES},
+
+ OID_U32(DOT11_OID_NOISEFLOOR, 0x17000013),
+ OID_STRUCT(DOT11_OID_FREQUENCYACTIVITY, 0x17000014, u8[IWMAX_FREQ + 1],
+ OID_TYPE_RAW),
+ OID_UNKNOWN(DOT11_OID_IQCALIBRATIONTABLE, 0x17000015),
+ OID_U32(DOT11_OID_NONERPPROTECTION, 0x17000016),
+ OID_U32(DOT11_OID_SLOTSETTINGS, 0x17000017),
+ OID_U32(DOT11_OID_NONERPTIMEOUT, 0x17000018),
+ OID_U32(DOT11_OID_PROFILES, 0x17000019),
+ OID_STRUCT(DOT11_OID_EXTENDEDRATES, 0x17000020,
+ u8[IWMAX_BITRATES + 1], OID_TYPE_RAW),
+
+ OID_STRUCT_MLME(DOT11_OID_DEAUTHENTICATE, 0x18000000),
+ OID_STRUCT_MLME(DOT11_OID_AUTHENTICATE, 0x18000001),
+ OID_STRUCT_MLME(DOT11_OID_DISASSOCIATE, 0x18000002),
+ OID_STRUCT_MLME(DOT11_OID_ASSOCIATE, 0x18000003),
+ OID_UNKNOWN(DOT11_OID_SCAN, 0x18000004),
+ OID_STRUCT_MLMEEX(DOT11_OID_BEACON, 0x18000005),
+ OID_STRUCT_MLMEEX(DOT11_OID_PROBE, 0x18000006),
+ OID_STRUCT_MLMEEX(DOT11_OID_DEAUTHENTICATEEX, 0x18000007),
+ OID_STRUCT_MLMEEX(DOT11_OID_AUTHENTICATEEX, 0x18000008),
+ OID_STRUCT_MLMEEX(DOT11_OID_DISASSOCIATEEX, 0x18000009),
+ OID_STRUCT_MLMEEX(DOT11_OID_ASSOCIATEEX, 0x1800000A),
+ OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATE, 0x1800000B),
+ OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATEEX, 0x1800000C),
+
+ OID_U32(DOT11_OID_NONERPSTATUS, 0x1E000000),
+
+ OID_U32(DOT11_OID_STATIMEOUT, 0x19000000),
+ OID_U32_C(DOT11_OID_MLMEAUTOLEVEL, 0x19000001),
+ OID_U32(DOT11_OID_BSSTIMEOUT, 0x19000002),
+ [DOT11_OID_ATTACHMENT] = {0x19000003, 0,
+ sizeof(struct obj_attachment), OID_TYPE_ATTACH},
+ OID_STRUCT_C(DOT11_OID_PSMBUFFER, 0x19000004, struct obj_buffer,
+ OID_TYPE_BUFFER),
+
+ OID_U32(DOT11_OID_BSSS, 0x1C000000),
+ [DOT11_OID_BSSX] = {0x1C000001, 63, sizeof (struct obj_bss),
+ OID_TYPE_BSS}, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */
+ OID_STRUCT(DOT11_OID_BSSFIND, 0x1C000042, struct obj_bss, OID_TYPE_BSS),
+ [DOT11_OID_BSSLIST] = {0x1C000043, 0, sizeof (struct
+ obj_bsslist) +
+ sizeof (struct obj_bss[IWMAX_BSS]),
+ OID_TYPE_BSSLIST},
+
+ OID_UNKNOWN(OID_INL_TUNNEL, 0xFF020000),
+ OID_UNKNOWN(OID_INL_MEMADDR, 0xFF020001),
+ OID_UNKNOWN(OID_INL_MEMORY, 0xFF020002),
+ OID_U32_C(OID_INL_MODE, 0xFF020003),
+ OID_UNKNOWN(OID_INL_COMPONENT_NR, 0xFF020004),
+ OID_STRUCT(OID_INL_VERSION, 0xFF020005, u8[8], OID_TYPE_RAW),
+ OID_UNKNOWN(OID_INL_INTERFACE_ID, 0xFF020006),
+ OID_UNKNOWN(OID_INL_COMPONENT_ID, 0xFF020007),
+ OID_U32_C(OID_INL_CONFIG, 0xFF020008),
+ OID_U32_C(OID_INL_DOT11D_CONFORMANCE, 0xFF02000C),
+ OID_U32(OID_INL_PHYCAPABILITIES, 0xFF02000D),
+ OID_U32_C(OID_INL_OUTPUTPOWER, 0xFF02000F),
+
+};
+
+int
+mgt_init(islpci_private *priv)
+{
+ int i;
+
+ priv->mib = kcalloc(OID_NUM_LAST, sizeof (void *), GFP_KERNEL);
+ if (!priv->mib)
+ return -ENOMEM;
+
+ /* Alloc the cache */
+ for (i = 0; i < OID_NUM_LAST; i++) {
+ if (isl_oid[i].flags & OID_FLAG_CACHED) {
+ priv->mib[i] = kzalloc(isl_oid[i].size *
+ (isl_oid[i].range + 1),
+ GFP_KERNEL);
+ if (!priv->mib[i])
+ return -ENOMEM;
+ } else
+ priv->mib[i] = NULL;
+ }
+
+ init_rwsem(&priv->mib_sem);
+ prism54_mib_init(priv);
+
+ return 0;
+}
+
+void
+mgt_clean(islpci_private *priv)
+{
+ int i;
+
+ if (!priv->mib)
+ return;
+ for (i = 0; i < OID_NUM_LAST; i++) {
+ kfree(priv->mib[i]);
+ priv->mib[i] = NULL;
+ }
+ kfree(priv->mib);
+ priv->mib = NULL;
+}
+
+void
+mgt_le_to_cpu(int type, void *data)
+{
+ switch (type) {
+ case OID_TYPE_U32:
+ *(u32 *) data = le32_to_cpu(*(u32 *) data);
+ break;
+ case OID_TYPE_BUFFER:{
+ struct obj_buffer *buff = data;
+ buff->size = le32_to_cpu(buff->size);
+ buff->addr = le32_to_cpu(buff->addr);
+ break;
+ }
+ case OID_TYPE_BSS:{
+ struct obj_bss *bss = data;
+ bss->age = le16_to_cpu(bss->age);
+ bss->channel = le16_to_cpu(bss->channel);
+ bss->capinfo = le16_to_cpu(bss->capinfo);
+ bss->rates = le16_to_cpu(bss->rates);
+ bss->basic_rates = le16_to_cpu(bss->basic_rates);
+ break;
+ }
+ case OID_TYPE_BSSLIST:{
+ struct obj_bsslist *list = data;
+ int i;
+ list->nr = le32_to_cpu(list->nr);
+ for (i = 0; i < list->nr; i++)
+ mgt_le_to_cpu(OID_TYPE_BSS, &list->bsslist[i]);
+ break;
+ }
+ case OID_TYPE_FREQUENCIES:{
+ struct obj_frequencies *freq = data;
+ int i;
+ freq->nr = le16_to_cpu(freq->nr);
+ for (i = 0; i < freq->nr; i++)
+ freq->mhz[i] = le16_to_cpu(freq->mhz[i]);
+ break;
+ }
+ case OID_TYPE_MLME:{
+ struct obj_mlme *mlme = data;
+ mlme->id = le16_to_cpu(mlme->id);
+ mlme->state = le16_to_cpu(mlme->state);
+ mlme->code = le16_to_cpu(mlme->code);
+ break;
+ }
+ case OID_TYPE_MLMEEX:{
+ struct obj_mlmeex *mlme = data;
+ mlme->id = le16_to_cpu(mlme->id);
+ mlme->state = le16_to_cpu(mlme->state);
+ mlme->code = le16_to_cpu(mlme->code);
+ mlme->size = le16_to_cpu(mlme->size);
+ break;
+ }
+ case OID_TYPE_ATTACH:{
+ struct obj_attachment *attach = data;
+ attach->id = le16_to_cpu(attach->id);
+ attach->size = le16_to_cpu(attach->size);
+ break;
+ }
+ case OID_TYPE_SSID:
+ case OID_TYPE_KEY:
+ case OID_TYPE_ADDR:
+ case OID_TYPE_RAW:
+ break;
+ default:
+ BUG();
+ }
+}
+
+static void
+mgt_cpu_to_le(int type, void *data)
+{
+ switch (type) {
+ case OID_TYPE_U32:
+ *(u32 *) data = cpu_to_le32(*(u32 *) data);
+ break;
+ case OID_TYPE_BUFFER:{
+ struct obj_buffer *buff = data;
+ buff->size = cpu_to_le32(buff->size);
+ buff->addr = cpu_to_le32(buff->addr);
+ break;
+ }
+ case OID_TYPE_BSS:{
+ struct obj_bss *bss = data;
+ bss->age = cpu_to_le16(bss->age);
+ bss->channel = cpu_to_le16(bss->channel);
+ bss->capinfo = cpu_to_le16(bss->capinfo);
+ bss->rates = cpu_to_le16(bss->rates);
+ bss->basic_rates = cpu_to_le16(bss->basic_rates);
+ break;
+ }
+ case OID_TYPE_BSSLIST:{
+ struct obj_bsslist *list = data;
+ int i;
+ list->nr = cpu_to_le32(list->nr);
+ for (i = 0; i < list->nr; i++)
+ mgt_cpu_to_le(OID_TYPE_BSS, &list->bsslist[i]);
+ break;
+ }
+ case OID_TYPE_FREQUENCIES:{
+ struct obj_frequencies *freq = data;
+ int i;
+ freq->nr = cpu_to_le16(freq->nr);
+ for (i = 0; i < freq->nr; i++)
+ freq->mhz[i] = cpu_to_le16(freq->mhz[i]);
+ break;
+ }
+ case OID_TYPE_MLME:{
+ struct obj_mlme *mlme = data;
+ mlme->id = cpu_to_le16(mlme->id);
+ mlme->state = cpu_to_le16(mlme->state);
+ mlme->code = cpu_to_le16(mlme->code);
+ break;
+ }
+ case OID_TYPE_MLMEEX:{
+ struct obj_mlmeex *mlme = data;
+ mlme->id = cpu_to_le16(mlme->id);
+ mlme->state = cpu_to_le16(mlme->state);
+ mlme->code = cpu_to_le16(mlme->code);
+ mlme->size = cpu_to_le16(mlme->size);
+ break;
+ }
+ case OID_TYPE_ATTACH:{
+ struct obj_attachment *attach = data;
+ attach->id = cpu_to_le16(attach->id);
+ attach->size = cpu_to_le16(attach->size);
+ break;
+ }
+ case OID_TYPE_SSID:
+ case OID_TYPE_KEY:
+ case OID_TYPE_ADDR:
+ case OID_TYPE_RAW:
+ break;
+ default:
+ BUG();
+ }
+}
+
+/* Note : data is modified during this function */
+
+int
+mgt_set_request(islpci_private *priv, enum oid_num_t n, int extra, void *data)
+{
+ int ret = 0;
+ struct islpci_mgmtframe *response = NULL;
+ int response_op = PIMFOR_OP_ERROR;
+ int dlen;
+ void *cache, *_data = data;
+ u32 oid;
+
+ BUG_ON(n >= OID_NUM_LAST);
+ BUG_ON(extra > isl_oid[n].range);
+
+ if (!priv->mib)
+ /* memory has been freed */
+ return -1;
+
+ dlen = isl_oid[n].size;
+ cache = priv->mib[n];
+ cache += (cache ? extra * dlen : 0);
+ oid = isl_oid[n].oid + extra;
+
+ if (_data == NULL)
+ /* we are requested to re-set a cached value */
+ _data = cache;
+ else
+ mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, _data);
+ /* If we are going to write to the cache, we don't want anyone to read
+ * it -> acquire write lock.
+ * Else we could acquire a read lock to be sure we don't bother the
+ * commit process (which takes a write lock). But I'm not sure if it's
+ * needed.
+ */
+ if (cache)
+ down_write(&priv->mib_sem);
+
+ if (islpci_get_state(priv) >= PRV_STATE_READY) {
+ ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid,
+ _data, dlen, &response);
+ if (!ret) {
+ response_op = response->header->operation;
+ islpci_mgt_release(response);
+ }
+ if (ret || response_op == PIMFOR_OP_ERROR)
+ ret = -EIO;
+ } else if (!cache)
+ ret = -EIO;
+
+ if (cache) {
+ if (!ret && data)
+ memcpy(cache, _data, dlen);
+ up_write(&priv->mib_sem);
+ }
+
+ /* re-set given data to what it was */
+ if (data)
+ mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data);
+
+ return ret;
+}
+
+/* None of these are cached */
+int
+mgt_set_varlen(islpci_private *priv, enum oid_num_t n, void *data, int extra_len)
+{
+ int ret = 0;
+ struct islpci_mgmtframe *response;
+ int response_op = PIMFOR_OP_ERROR;
+ int dlen;
+ u32 oid;
+
+ BUG_ON(n >= OID_NUM_LAST);
+
+ dlen = isl_oid[n].size;
+ oid = isl_oid[n].oid;
+
+ mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, data);
+
+ if (islpci_get_state(priv) >= PRV_STATE_READY) {
+ ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid,
+ data, dlen + extra_len, &response);
+ if (!ret) {
+ response_op = response->header->operation;
+ islpci_mgt_release(response);
+ }
+ if (ret || response_op == PIMFOR_OP_ERROR)
+ ret = -EIO;
+ } else
+ ret = -EIO;
+
+ /* re-set given data to what it was */
+ if (data)
+ mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data);
+
+ return ret;
+}
+
+int
+mgt_get_request(islpci_private *priv, enum oid_num_t n, int extra, void *data,
+ union oid_res_t *res)
+{
+
+ int ret = -EIO;
+ int reslen = 0;
+ struct islpci_mgmtframe *response = NULL;
+
+ int dlen;
+ void *cache, *_res = NULL;
+ u32 oid;
+
+ BUG_ON(n >= OID_NUM_LAST);
+ BUG_ON(extra > isl_oid[n].range);
+
+ res->ptr = NULL;
+
+ if (!priv->mib)
+ /* memory has been freed */
+ return -1;
+
+ dlen = isl_oid[n].size;
+ cache = priv->mib[n];
+ cache += cache ? extra * dlen : 0;
+ oid = isl_oid[n].oid + extra;
+ reslen = dlen;
+
+ if (cache)
+ down_read(&priv->mib_sem);
+
+ if (islpci_get_state(priv) >= PRV_STATE_READY) {
+ ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
+ oid, data, dlen, &response);
+ if (ret || !response ||
+ response->header->operation == PIMFOR_OP_ERROR) {
+ if (response)
+ islpci_mgt_release(response);
+ ret = -EIO;
+ }
+ if (!ret) {
+ _res = response->data;
+ reslen = response->header->length;
+ }
+ } else if (cache) {
+ _res = cache;
+ ret = 0;
+ }
+ if ((isl_oid[n].flags & OID_FLAG_TYPE) == OID_TYPE_U32)
+ res->u = ret ? 0 : le32_to_cpu(*(u32 *) _res);
+ else {
+ res->ptr = kmalloc(reslen, GFP_KERNEL);
+ BUG_ON(res->ptr == NULL);
+ if (ret)
+ memset(res->ptr, 0, reslen);
+ else {
+ memcpy(res->ptr, _res, reslen);
+ mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE,
+ res->ptr);
+ }
+ }
+ if (cache)
+ up_read(&priv->mib_sem);
+
+ if (response && !ret)
+ islpci_mgt_release(response);
+
+ if (reslen > isl_oid[n].size)
+ printk(KERN_DEBUG
+ "mgt_get_request(0x%x): received data length was bigger "
+ "than expected (%d > %d). Memory is probably corrupted...",
+ oid, reslen, isl_oid[n].size);
+
+ return ret;
+}
+
+/* lock outside */
+int
+mgt_commit_list(islpci_private *priv, enum oid_num_t *l, int n)
+{
+ int i, ret = 0;
+ struct islpci_mgmtframe *response;
+
+ for (i = 0; i < n; i++) {
+ struct oid_t *t = &(isl_oid[l[i]]);
+ void *data = priv->mib[l[i]];
+ int j = 0;
+ u32 oid = t->oid;
+ BUG_ON(data == NULL);
+ while (j <= t->range) {
+ int r = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET,
+ oid, data, t->size,
+ &response);
+ if (response) {
+ r |= (response->header->operation == PIMFOR_OP_ERROR);
+ islpci_mgt_release(response);
+ }
+ if (r)
+ printk(KERN_ERR "%s: mgt_commit_list: failure. "
+ "oid=%08x err=%d\n",
+ priv->ndev->name, oid, r);
+ ret |= r;
+ j++;
+ oid++;
+ data += t->size;
+ }
+ }
+ return ret;
+}
+
+/* Lock outside */
+
+void
+mgt_set(islpci_private *priv, enum oid_num_t n, void *data)
+{
+ BUG_ON(n >= OID_NUM_LAST);
+ BUG_ON(priv->mib[n] == NULL);
+
+ memcpy(priv->mib[n], data, isl_oid[n].size);
+ mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, priv->mib[n]);
+}
+
+void
+mgt_get(islpci_private *priv, enum oid_num_t n, void *res)
+{
+ BUG_ON(n >= OID_NUM_LAST);
+ BUG_ON(priv->mib[n] == NULL);
+ BUG_ON(res == NULL);
+
+ memcpy(res, priv->mib[n], isl_oid[n].size);
+ mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, res);
+}
+
+/* Commits the cache. Lock outside. */
+
+static enum oid_num_t commit_part1[] = {
+ OID_INL_CONFIG,
+ OID_INL_MODE,
+ DOT11_OID_BSSTYPE,
+ DOT11_OID_CHANNEL,
+ DOT11_OID_MLMEAUTOLEVEL
+};
+
+static enum oid_num_t commit_part2[] = {
+ DOT11_OID_SSID,
+ DOT11_OID_PSMBUFFER,
+ DOT11_OID_AUTHENABLE,
+ DOT11_OID_PRIVACYINVOKED,
+ DOT11_OID_EXUNENCRYPTED,
+ DOT11_OID_DEFKEYX, /* MULTIPLE */
+ DOT11_OID_DEFKEYID,
+ DOT11_OID_DOT1XENABLE,
+ OID_INL_DOT11D_CONFORMANCE,
+ /* Do not initialize this - fw < 1.0.4.3 rejects it
+ OID_INL_OUTPUTPOWER,
+ */
+};
+
+/* update the MAC addr. */
+static int
+mgt_update_addr(islpci_private *priv)
+{
+ struct islpci_mgmtframe *res;
+ int ret;
+
+ ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
+ isl_oid[GEN_OID_MACADDRESS].oid, NULL,
+ isl_oid[GEN_OID_MACADDRESS].size, &res);
+
+ if ((ret == 0) && res && (res->header->operation != PIMFOR_OP_ERROR))
+ memcpy(priv->ndev->dev_addr, res->data, ETH_ALEN);
+ else
+ ret = -EIO;
+ if (res)
+ islpci_mgt_release(res);
+
+ if (ret)
+ printk(KERN_ERR "%s: mgt_update_addr: failure\n", priv->ndev->name);
+ return ret;
+}
+
+int
+mgt_commit(islpci_private *priv)
+{
+ int rvalue;
+ enum oid_num_t u;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return 0;
+
+ rvalue = mgt_commit_list(priv, commit_part1, ARRAY_SIZE(commit_part1));
+
+ if (priv->iw_mode != IW_MODE_MONITOR)
+ rvalue |= mgt_commit_list(priv, commit_part2, ARRAY_SIZE(commit_part2));
+
+ u = OID_INL_MODE;
+ rvalue |= mgt_commit_list(priv, &u, 1);
+ rvalue |= mgt_update_addr(priv);
+
+ if (rvalue) {
+ /* some request have failed. The device might be in an
+ incoherent state. We should reset it ! */
+ printk(KERN_DEBUG "%s: mgt_commit: failure\n", priv->ndev->name);
+ }
+ return rvalue;
+}
+
+/* The following OIDs need to be "unlatched":
+ *
+ * MEDIUMLIMIT,BEACONPERIOD,DTIMPERIOD,ATIMWINDOW,LISTENINTERVAL
+ * FREQUENCY,EXTENDEDRATES.
+ *
+ * The way to do this is to set ESSID. Note though that they may get
+ * unlatch before though by setting another OID. */
+#if 0
+void
+mgt_unlatch_all(islpci_private *priv)
+{
+ u32 u;
+ int rvalue = 0;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT)
+ return;
+
+ u = DOT11_OID_SSID;
+ rvalue = mgt_commit_list(priv, &u, 1);
+ /* Necessary if in MANUAL RUN mode? */
+#if 0
+ u = OID_INL_MODE;
+ rvalue |= mgt_commit_list(priv, &u, 1);
+
+ u = DOT11_OID_MLMEAUTOLEVEL;
+ rvalue |= mgt_commit_list(priv, &u, 1);
+
+ u = OID_INL_MODE;
+ rvalue |= mgt_commit_list(priv, &u, 1);
+#endif
+
+ if (rvalue)
+ printk(KERN_DEBUG "%s: Unlatching OIDs failed\n", priv->ndev->name);
+}
+#endif
+
+/* This will tell you if you are allowed to answer a mlme(ex) request .*/
+
+int
+mgt_mlme_answer(islpci_private *priv)
+{
+ u32 mlmeautolevel;
+ /* Acquire a read lock because if we are in a mode change, it's
+ * possible to answer true, while the card is leaving master to managed
+ * mode. Answering to a mlme in this situation could hang the card.
+ */
+ down_read(&priv->mib_sem);
+ mlmeautolevel =
+ le32_to_cpu(*(u32 *) priv->mib[DOT11_OID_MLMEAUTOLEVEL]);
+ up_read(&priv->mib_sem);
+
+ return ((priv->iw_mode == IW_MODE_MASTER) &&
+ (mlmeautolevel >= DOT11_MLME_INTERMEDIATE));
+}
+
+enum oid_num_t
+mgt_oidtonum(u32 oid)
+{
+ int i;
+
+ for (i = 0; i < OID_NUM_LAST; i++)
+ if (isl_oid[i].oid == oid)
+ return i;
+
+ printk(KERN_DEBUG "looking for an unknown oid 0x%x", oid);
+
+ return OID_NUM_LAST;
+}
+
+int
+mgt_response_to_str(enum oid_num_t n, union oid_res_t *r, char *str)
+{
+ switch (isl_oid[n].flags & OID_FLAG_TYPE) {
+ case OID_TYPE_U32:
+ return snprintf(str, PRIV_STR_SIZE, "%u\n", r->u);
+ case OID_TYPE_BUFFER:{
+ struct obj_buffer *buff = r->ptr;
+ return snprintf(str, PRIV_STR_SIZE,
+ "size=%u\naddr=0x%X\n", buff->size,
+ buff->addr);
+ }
+ break;
+ case OID_TYPE_BSS:{
+ struct obj_bss *bss = r->ptr;
+ return snprintf(str, PRIV_STR_SIZE,
+ "age=%u\nchannel=%u\n"
+ "capinfo=0x%X\nrates=0x%X\n"
+ "basic_rates=0x%X\n", bss->age,
+ bss->channel, bss->capinfo,
+ bss->rates, bss->basic_rates);
+ }
+ break;
+ case OID_TYPE_BSSLIST:{
+ struct obj_bsslist *list = r->ptr;
+ int i, k;
+ k = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", list->nr);
+ for (i = 0; i < list->nr; i++)
+ k += snprintf(str + k, PRIV_STR_SIZE - k,
+ "bss[%u] :\nage=%u\nchannel=%u\n"
+ "capinfo=0x%X\nrates=0x%X\n"
+ "basic_rates=0x%X\n",
+ i, list->bsslist[i].age,
+ list->bsslist[i].channel,
+ list->bsslist[i].capinfo,
+ list->bsslist[i].rates,
+ list->bsslist[i].basic_rates);
+ return k;
+ }
+ break;
+ case OID_TYPE_FREQUENCIES:{
+ struct obj_frequencies *freq = r->ptr;
+ int i, t;
+ printk("nr : %u\n", freq->nr);
+ t = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", freq->nr);
+ for (i = 0; i < freq->nr; i++)
+ t += snprintf(str + t, PRIV_STR_SIZE - t,
+ "mhz[%u]=%u\n", i, freq->mhz[i]);
+ return t;
+ }
+ break;
+ case OID_TYPE_MLME:{
+ struct obj_mlme *mlme = r->ptr;
+ return snprintf(str, PRIV_STR_SIZE,
+ "id=0x%X\nstate=0x%X\ncode=0x%X\n",
+ mlme->id, mlme->state, mlme->code);
+ }
+ break;
+ case OID_TYPE_MLMEEX:{
+ struct obj_mlmeex *mlme = r->ptr;
+ return snprintf(str, PRIV_STR_SIZE,
+ "id=0x%X\nstate=0x%X\n"
+ "code=0x%X\nsize=0x%X\n", mlme->id,
+ mlme->state, mlme->code, mlme->size);
+ }
+ break;
+ case OID_TYPE_ATTACH:{
+ struct obj_attachment *attach = r->ptr;
+ return snprintf(str, PRIV_STR_SIZE,
+ "id=%d\nsize=%d\n",
+ attach->id,
+ attach->size);
+ }
+ break;
+ case OID_TYPE_SSID:{
+ struct obj_ssid *ssid = r->ptr;
+ return snprintf(str, PRIV_STR_SIZE,
+ "length=%u\noctets=%.*s\n",
+ ssid->length, ssid->length,
+ ssid->octets);
+ }
+ break;
+ case OID_TYPE_KEY:{
+ struct obj_key *key = r->ptr;
+ int t, i;
+ t = snprintf(str, PRIV_STR_SIZE,
+ "type=0x%X\nlength=0x%X\nkey=0x",
+ key->type, key->length);
+ for (i = 0; i < key->length; i++)
+ t += snprintf(str + t, PRIV_STR_SIZE - t,
+ "%02X:", key->key[i]);
+ t += snprintf(str + t, PRIV_STR_SIZE - t, "\n");
+ return t;
+ }
+ break;
+ case OID_TYPE_RAW:
+ case OID_TYPE_ADDR:{
+ unsigned char *buff = r->ptr;
+ int t, i;
+ t = snprintf(str, PRIV_STR_SIZE, "hex data=");
+ for (i = 0; i < isl_oid[n].size; i++)
+ t += snprintf(str + t, PRIV_STR_SIZE - t,
+ "%02X:", buff[i]);
+ t += snprintf(str + t, PRIV_STR_SIZE - t, "\n");
+ return t;
+ }
+ break;
+ default:
+ BUG();
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/prism54/oid_mgt.h b/drivers/net/wireless/intersil/prism54/oid_mgt.h
index cf5141df8474..cf5141df8474 100644
--- a/drivers/net/wireless/prism54/oid_mgt.h
+++ b/drivers/net/wireless/intersil/prism54/oid_mgt.h
diff --git a/drivers/net/wireless/prism54/prismcompat.h b/drivers/net/wireless/intersil/prism54/prismcompat.h
index bc1401eb4b9d..bc1401eb4b9d 100644
--- a/drivers/net/wireless/prism54/prismcompat.h
+++ b/drivers/net/wireless/intersil/prism54/prismcompat.h
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
deleted file mode 100644
index 36818c7f30b9..000000000000
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ /dev/null
@@ -1,8639 +0,0 @@
-/******************************************************************************
-
- Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc., 59
- Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
- Contact Information:
- Intel Linux Wireless <ilw@linux.intel.com>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
- Portions of this file are based on the sample_* files provided by Wireless
- Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes
- <jt@hpl.hp.com>
-
- Portions of this file are based on the Host AP project,
- Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- <j@w1.fi>
- Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
-
- Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
- ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
- available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox
-
-******************************************************************************/
-/*
-
- Initial driver on which this is based was developed by Janusz Gorycki,
- Maciej Urbaniak, and Maciej Sosnowski.
-
- Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak.
-
-Theory of Operation
-
-Tx - Commands and Data
-
-Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs)
-Each TBD contains a pointer to the physical (dma_addr_t) address of data being
-sent to the firmware as well as the length of the data.
-
-The host writes to the TBD queue at the WRITE index. The WRITE index points
-to the _next_ packet to be written and is advanced when after the TBD has been
-filled.
-
-The firmware pulls from the TBD queue at the READ index. The READ index points
-to the currently being read entry, and is advanced once the firmware is
-done with a packet.
-
-When data is sent to the firmware, the first TBD is used to indicate to the
-firmware if a Command or Data is being sent. If it is Command, all of the
-command information is contained within the physical address referred to by the
-TBD. If it is Data, the first TBD indicates the type of data packet, number
-of fragments, etc. The next TBD then refers to the actual packet location.
-
-The Tx flow cycle is as follows:
-
-1) ipw2100_tx() is called by kernel with SKB to transmit
-2) Packet is move from the tx_free_list and appended to the transmit pending
- list (tx_pend_list)
-3) work is scheduled to move pending packets into the shared circular queue.
-4) when placing packet in the circular queue, the incoming SKB is DMA mapped
- to a physical address. That address is entered into a TBD. Two TBDs are
- filled out. The first indicating a data packet, the second referring to the
- actual payload data.
-5) the packet is removed from tx_pend_list and placed on the end of the
- firmware pending list (fw_pend_list)
-6) firmware is notified that the WRITE index has
-7) Once the firmware has processed the TBD, INTA is triggered.
-8) For each Tx interrupt received from the firmware, the READ index is checked
- to see which TBDs are done being processed.
-9) For each TBD that has been processed, the ISR pulls the oldest packet
- from the fw_pend_list.
-10)The packet structure contained in the fw_pend_list is then used
- to unmap the DMA address and to free the SKB originally passed to the driver
- from the kernel.
-11)The packet structure is placed onto the tx_free_list
-
-The above steps are the same for commands, only the msg_free_list/msg_pend_list
-are used instead of tx_free_list/tx_pend_list
-
-...
-
-Critical Sections / Locking :
-
-There are two locks utilized. The first is the low level lock (priv->low_lock)
-that protects the following:
-
-- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows:
-
- tx_free_list : Holds pre-allocated Tx buffers.
- TAIL modified in __ipw2100_tx_process()
- HEAD modified in ipw2100_tx()
-
- tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring
- TAIL modified ipw2100_tx()
- HEAD modified by ipw2100_tx_send_data()
-
- msg_free_list : Holds pre-allocated Msg (Command) buffers
- TAIL modified in __ipw2100_tx_process()
- HEAD modified in ipw2100_hw_send_command()
-
- msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring
- TAIL modified in ipw2100_hw_send_command()
- HEAD modified in ipw2100_tx_send_commands()
-
- The flow of data on the TX side is as follows:
-
- MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST
- TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST
-
- The methods that work on the TBD ring are protected via priv->low_lock.
-
-- The internal data state of the device itself
-- Access to the firmware read/write indexes for the BD queues
- and associated logic
-
-All external entry functions are locked with the priv->action_lock to ensure
-that only one external action is invoked at a time.
-
-
-*/
-
-#include <linux/compiler.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/kernel.h>
-#include <linux/kmod.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/unistd.h>
-#include <linux/stringify.h>
-#include <linux/tcp.h>
-#include <linux/types.h>
-#include <linux/time.h>
-#include <linux/firmware.h>
-#include <linux/acpi.h>
-#include <linux/ctype.h>
-#include <linux/pm_qos.h>
-
-#include <net/lib80211.h>
-
-#include "ipw2100.h"
-#include "ipw.h"
-
-#define IPW2100_VERSION "git-1.2.2"
-
-#define DRV_NAME "ipw2100"
-#define DRV_VERSION IPW2100_VERSION
-#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
-#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
-
-static struct pm_qos_request ipw2100_pm_qos_req;
-
-/* Debugging stuff */
-#ifdef CONFIG_IPW2100_DEBUG
-#define IPW2100_RX_DEBUG /* Reception debugging */
-#endif
-
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT);
-MODULE_LICENSE("GPL");
-
-static int debug = 0;
-static int network_mode = 0;
-static int channel = 0;
-static int associate = 0;
-static int disable = 0;
-#ifdef CONFIG_PM
-static struct ipw2100_fw ipw2100_firmware;
-#endif
-
-#include <linux/moduleparam.h>
-module_param(debug, int, 0444);
-module_param_named(mode, network_mode, int, 0444);
-module_param(channel, int, 0444);
-module_param(associate, int, 0444);
-module_param(disable, int, 0444);
-
-MODULE_PARM_DESC(debug, "debug level");
-MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
-MODULE_PARM_DESC(channel, "channel");
-MODULE_PARM_DESC(associate, "auto associate when scanning (default off)");
-MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
-
-static u32 ipw2100_debug_level = IPW_DL_NONE;
-
-#ifdef CONFIG_IPW2100_DEBUG
-#define IPW_DEBUG(level, message...) \
-do { \
- if (ipw2100_debug_level & (level)) { \
- printk(KERN_DEBUG "ipw2100: %c %s ", \
- in_interrupt() ? 'I' : 'U', __func__); \
- printk(message); \
- } \
-} while (0)
-#else
-#define IPW_DEBUG(level, message...) do {} while (0)
-#endif /* CONFIG_IPW2100_DEBUG */
-
-#ifdef CONFIG_IPW2100_DEBUG
-static const char *command_types[] = {
- "undefined",
- "unused", /* HOST_ATTENTION */
- "HOST_COMPLETE",
- "unused", /* SLEEP */
- "unused", /* HOST_POWER_DOWN */
- "unused",
- "SYSTEM_CONFIG",
- "unused", /* SET_IMR */
- "SSID",
- "MANDATORY_BSSID",
- "AUTHENTICATION_TYPE",
- "ADAPTER_ADDRESS",
- "PORT_TYPE",
- "INTERNATIONAL_MODE",
- "CHANNEL",
- "RTS_THRESHOLD",
- "FRAG_THRESHOLD",
- "POWER_MODE",
- "TX_RATES",
- "BASIC_TX_RATES",
- "WEP_KEY_INFO",
- "unused",
- "unused",
- "unused",
- "unused",
- "WEP_KEY_INDEX",
- "WEP_FLAGS",
- "ADD_MULTICAST",
- "CLEAR_ALL_MULTICAST",
- "BEACON_INTERVAL",
- "ATIM_WINDOW",
- "CLEAR_STATISTICS",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "TX_POWER_INDEX",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "BROADCAST_SCAN",
- "CARD_DISABLE",
- "PREFERRED_BSSID",
- "SET_SCAN_OPTIONS",
- "SCAN_DWELL_TIME",
- "SWEEP_TABLE",
- "AP_OR_STATION_TABLE",
- "GROUP_ORDINALS",
- "SHORT_RETRY_LIMIT",
- "LONG_RETRY_LIMIT",
- "unused", /* SAVE_CALIBRATION */
- "unused", /* RESTORE_CALIBRATION */
- "undefined",
- "undefined",
- "undefined",
- "HOST_PRE_POWER_DOWN",
- "unused", /* HOST_INTERRUPT_COALESCING */
- "undefined",
- "CARD_DISABLE_PHY_OFF",
- "MSDU_TX_RATES",
- "undefined",
- "SET_STATION_STAT_BITS",
- "CLEAR_STATIONS_STAT_BITS",
- "LEAP_ROGUE_MODE",
- "SET_SECURITY_INFORMATION",
- "DISASSOCIATION_BSSID",
- "SET_WPA_ASS_IE"
-};
-#endif
-
-static const long ipw2100_frequencies[] = {
- 2412, 2417, 2422, 2427,
- 2432, 2437, 2442, 2447,
- 2452, 2457, 2462, 2467,
- 2472, 2484
-};
-
-#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies)
-
-static struct ieee80211_rate ipw2100_bg_rates[] = {
- { .bitrate = 10 },
- { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
-};
-
-#define RATE_COUNT ARRAY_SIZE(ipw2100_bg_rates)
-
-/* Pre-decl until we get the code solid and then we can clean it up */
-static void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
-static void ipw2100_tx_send_data(struct ipw2100_priv *priv);
-static int ipw2100_adapter_setup(struct ipw2100_priv *priv);
-
-static void ipw2100_queues_initialize(struct ipw2100_priv *priv);
-static void ipw2100_queues_free(struct ipw2100_priv *priv);
-static int ipw2100_queues_allocate(struct ipw2100_priv *priv);
-
-static int ipw2100_fw_download(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static int ipw2100_get_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
- size_t max);
-static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
- size_t max);
-static void ipw2100_release_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static int ipw2100_ucode_download(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static void ipw2100_wx_event_work(struct work_struct *work);
-static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev);
-static struct iw_handler_def ipw2100_wx_handler_def;
-
-static inline void read_register(struct net_device *dev, u32 reg, u32 * val)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- *val = ioread32(priv->ioaddr + reg);
- IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val);
-}
-
-static inline void write_register(struct net_device *dev, u32 reg, u32 val)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- iowrite32(val, priv->ioaddr + reg);
- IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val);
-}
-
-static inline void read_register_word(struct net_device *dev, u32 reg,
- u16 * val)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- *val = ioread16(priv->ioaddr + reg);
- IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val);
-}
-
-static inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- *val = ioread8(priv->ioaddr + reg);
- IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val);
-}
-
-static inline void write_register_word(struct net_device *dev, u32 reg, u16 val)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- iowrite16(val, priv->ioaddr + reg);
- IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val);
-}
-
-static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- iowrite8(val, priv->ioaddr + reg);
- IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val);
-}
-
-static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr)
-{
- write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
-}
-
-static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val)
-{
- write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val);
-}
-
-static void write_nic_memory(struct net_device *dev, u32 addr, u32 len,
- const u8 * buf)
-{
- u32 aligned_addr;
- u32 aligned_len;
- u32 dif_len;
- u32 i;
-
- /* read first nibble byte by byte */
- aligned_addr = addr & (~0x3);
- dif_len = addr - aligned_addr;
- if (dif_len) {
- /* Start reading at aligned_addr + dif_len */
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- aligned_addr);
- for (i = dif_len; i < 4; i++, buf++)
- write_register_byte(dev,
- IPW_REG_INDIRECT_ACCESS_DATA + i,
- *buf);
-
- len -= dif_len;
- aligned_addr += 4;
- }
-
- /* read DWs through autoincrement registers */
- write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
- aligned_len = len & (~0x3);
- for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
- write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf);
-
- /* copy the last nibble */
- dif_len = len - aligned_len;
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
- for (i = 0; i < dif_len; i++, buf++)
- write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i,
- *buf);
-}
-
-static void read_nic_memory(struct net_device *dev, u32 addr, u32 len,
- u8 * buf)
-{
- u32 aligned_addr;
- u32 aligned_len;
- u32 dif_len;
- u32 i;
-
- /* read first nibble byte by byte */
- aligned_addr = addr & (~0x3);
- dif_len = addr - aligned_addr;
- if (dif_len) {
- /* Start reading at aligned_addr + dif_len */
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- aligned_addr);
- for (i = dif_len; i < 4; i++, buf++)
- read_register_byte(dev,
- IPW_REG_INDIRECT_ACCESS_DATA + i,
- buf);
-
- len -= dif_len;
- aligned_addr += 4;
- }
-
- /* read DWs through autoincrement registers */
- write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
- aligned_len = len & (~0x3);
- for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
- read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf);
-
- /* copy the last nibble */
- dif_len = len - aligned_len;
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
- for (i = 0; i < dif_len; i++, buf++)
- read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf);
-}
-
-static bool ipw2100_hw_is_adapter_in_system(struct net_device *dev)
-{
- u32 dbg;
-
- read_register(dev, IPW_REG_DOA_DEBUG_AREA_START, &dbg);
-
- return dbg == IPW_DATA_DOA_DEBUG_VALUE;
-}
-
-static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
- void *val, u32 * len)
-{
- struct ipw2100_ordinals *ordinals = &priv->ordinals;
- u32 addr;
- u32 field_info;
- u16 field_len;
- u16 field_count;
- u32 total_length;
-
- if (ordinals->table1_addr == 0) {
- printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals "
- "before they have been loaded.\n");
- return -EINVAL;
- }
-
- if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
- if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) {
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
-
- printk(KERN_WARNING DRV_NAME
- ": ordinal buffer length too small, need %zd\n",
- IPW_ORD_TAB_1_ENTRY_SIZE);
-
- return -EINVAL;
- }
-
- read_nic_dword(priv->net_dev,
- ordinals->table1_addr + (ord << 2), &addr);
- read_nic_dword(priv->net_dev, addr, val);
-
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
-
- return 0;
- }
-
- if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) {
-
- ord -= IPW_START_ORD_TAB_2;
-
- /* get the address of statistic */
- read_nic_dword(priv->net_dev,
- ordinals->table2_addr + (ord << 3), &addr);
-
- /* get the second DW of statistics ;
- * two 16-bit words - first is length, second is count */
- read_nic_dword(priv->net_dev,
- ordinals->table2_addr + (ord << 3) + sizeof(u32),
- &field_info);
-
- /* get each entry length */
- field_len = *((u16 *) & field_info);
-
- /* get number of entries */
- field_count = *(((u16 *) & field_info) + 1);
-
- /* abort if no enough memory */
- total_length = field_len * field_count;
- if (total_length > *len) {
- *len = total_length;
- return -EINVAL;
- }
-
- *len = total_length;
- if (!total_length)
- return 0;
-
- /* read the ordinal data from the SRAM */
- read_nic_memory(priv->net_dev, addr, total_length, val);
-
- return 0;
- }
-
- printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor "
- "in table 2\n", ord);
-
- return -EINVAL;
-}
-
-static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val,
- u32 * len)
-{
- struct ipw2100_ordinals *ordinals = &priv->ordinals;
- u32 addr;
-
- if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
- if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) {
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
- IPW_DEBUG_INFO("wrong size\n");
- return -EINVAL;
- }
-
- read_nic_dword(priv->net_dev,
- ordinals->table1_addr + (ord << 2), &addr);
-
- write_nic_dword(priv->net_dev, addr, *val);
-
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
-
- return 0;
- }
-
- IPW_DEBUG_INFO("wrong table\n");
- if (IS_ORDINAL_TABLE_TWO(ordinals, ord))
- return -EINVAL;
-
- return -EINVAL;
-}
-
-static char *snprint_line(char *buf, size_t count,
- const u8 * data, u32 len, u32 ofs)
-{
- int out, i, j, l;
- char c;
-
- out = snprintf(buf, count, "%08X", ofs);
-
- for (l = 0, i = 0; i < 2; i++) {
- out += snprintf(buf + out, count - out, " ");
- for (j = 0; j < 8 && l < len; j++, l++)
- out += snprintf(buf + out, count - out, "%02X ",
- data[(i * 8 + j)]);
- for (; j < 8; j++)
- out += snprintf(buf + out, count - out, " ");
- }
-
- out += snprintf(buf + out, count - out, " ");
- for (l = 0, i = 0; i < 2; i++) {
- out += snprintf(buf + out, count - out, " ");
- for (j = 0; j < 8 && l < len; j++, l++) {
- c = data[(i * 8 + j)];
- if (!isascii(c) || !isprint(c))
- c = '.';
-
- out += snprintf(buf + out, count - out, "%c", c);
- }
-
- for (; j < 8; j++)
- out += snprintf(buf + out, count - out, " ");
- }
-
- return buf;
-}
-
-static void printk_buf(int level, const u8 * data, u32 len)
-{
- char line[81];
- u32 ofs = 0;
- if (!(ipw2100_debug_level & level))
- return;
-
- while (len) {
- printk(KERN_DEBUG "%s\n",
- snprint_line(line, sizeof(line), &data[ofs],
- min(len, 16U), ofs));
- ofs += 16;
- len -= min(len, 16U);
- }
-}
-
-#define MAX_RESET_BACKOFF 10
-
-static void schedule_reset(struct ipw2100_priv *priv)
-{
- unsigned long now = get_seconds();
-
- /* If we haven't received a reset request within the backoff period,
- * then we can reset the backoff interval so this reset occurs
- * immediately */
- if (priv->reset_backoff &&
- (now - priv->last_reset > priv->reset_backoff))
- priv->reset_backoff = 0;
-
- priv->last_reset = get_seconds();
-
- if (!(priv->status & STATUS_RESET_PENDING)) {
- IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n",
- priv->net_dev->name, priv->reset_backoff);
- netif_carrier_off(priv->net_dev);
- netif_stop_queue(priv->net_dev);
- priv->status |= STATUS_RESET_PENDING;
- if (priv->reset_backoff)
- schedule_delayed_work(&priv->reset_work,
- priv->reset_backoff * HZ);
- else
- schedule_delayed_work(&priv->reset_work, 0);
-
- if (priv->reset_backoff < MAX_RESET_BACKOFF)
- priv->reset_backoff++;
-
- wake_up_interruptible(&priv->wait_command_queue);
- } else
- IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n",
- priv->net_dev->name);
-
-}
-
-#define HOST_COMPLETE_TIMEOUT (2 * HZ)
-static int ipw2100_hw_send_command(struct ipw2100_priv *priv,
- struct host_command *cmd)
-{
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- unsigned long flags;
- int err = 0;
-
- IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
- command_types[cmd->host_command], cmd->host_command,
- cmd->host_command_length);
- printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters,
- cmd->host_command_length);
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (priv->fatal_error) {
- IPW_DEBUG_INFO
- ("Attempt to send command while hardware in fatal error condition.\n");
- err = -EIO;
- goto fail_unlock;
- }
-
- if (!(priv->status & STATUS_RUNNING)) {
- IPW_DEBUG_INFO
- ("Attempt to send command while hardware is not running.\n");
- err = -EIO;
- goto fail_unlock;
- }
-
- if (priv->status & STATUS_CMD_ACTIVE) {
- IPW_DEBUG_INFO
- ("Attempt to send command while another command is pending.\n");
- err = -EBUSY;
- goto fail_unlock;
- }
-
- if (list_empty(&priv->msg_free_list)) {
- IPW_DEBUG_INFO("no available msg buffers\n");
- goto fail_unlock;
- }
-
- priv->status |= STATUS_CMD_ACTIVE;
- priv->messages_sent++;
-
- element = priv->msg_free_list.next;
-
- packet = list_entry(element, struct ipw2100_tx_packet, list);
- packet->jiffy_start = jiffies;
-
- /* initialize the firmware command packet */
- packet->info.c_struct.cmd->host_command_reg = cmd->host_command;
- packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1;
- packet->info.c_struct.cmd->host_command_len_reg =
- cmd->host_command_length;
- packet->info.c_struct.cmd->sequence = cmd->host_command_sequence;
-
- memcpy(packet->info.c_struct.cmd->host_command_params_reg,
- cmd->host_command_parameters,
- sizeof(packet->info.c_struct.cmd->host_command_params_reg));
-
- list_del(element);
- DEC_STAT(&priv->msg_free_stat);
-
- list_add_tail(element, &priv->msg_pend_list);
- INC_STAT(&priv->msg_pend_stat);
-
- ipw2100_tx_send_commands(priv);
- ipw2100_tx_send_data(priv);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- /*
- * We must wait for this command to complete before another
- * command can be sent... but if we wait more than 3 seconds
- * then there is a problem.
- */
-
- err =
- wait_event_interruptible_timeout(priv->wait_command_queue,
- !(priv->
- status & STATUS_CMD_ACTIVE),
- HOST_COMPLETE_TIMEOUT);
-
- if (err == 0) {
- IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
- 1000 * (HOST_COMPLETE_TIMEOUT / HZ));
- priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT;
- priv->status &= ~STATUS_CMD_ACTIVE;
- schedule_reset(priv);
- return -EIO;
- }
-
- if (priv->fatal_error) {
- printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n",
- priv->net_dev->name);
- return -EIO;
- }
-
- /* !!!!! HACK TEST !!!!!
- * When lots of debug trace statements are enabled, the driver
- * doesn't seem to have as many firmware restart cycles...
- *
- * As a test, we're sticking in a 1/100s delay here */
- schedule_timeout_uninterruptible(msecs_to_jiffies(10));
-
- return 0;
-
- fail_unlock:
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- return err;
-}
-
-/*
- * Verify the values and data access of the hardware
- * No locks needed or used. No functions called.
- */
-static int ipw2100_verify(struct ipw2100_priv *priv)
-{
- u32 data1, data2;
- u32 address;
-
- u32 val1 = 0x76543210;
- u32 val2 = 0xFEDCBA98;
-
- /* Domain 0 check - all values should be DOA_DEBUG */
- for (address = IPW_REG_DOA_DEBUG_AREA_START;
- address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) {
- read_register(priv->net_dev, address, &data1);
- if (data1 != IPW_DATA_DOA_DEBUG_VALUE)
- return -EIO;
- }
-
- /* Domain 1 check - use arbitrary read/write compare */
- for (address = 0; address < 5; address++) {
- /* The memory area is not used now */
- write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
- val1);
- write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
- val2);
- read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
- &data1);
- read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
- &data2);
- if (val1 == data1 && val2 == data2)
- return 0;
- }
-
- return -EIO;
-}
-
-/*
- *
- * Loop until the CARD_DISABLED bit is the same value as the
- * supplied parameter
- *
- * TODO: See if it would be more efficient to do a wait/wake
- * cycle and have the completion event trigger the wakeup
- *
- */
-#define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli
-static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state)
-{
- int i;
- u32 card_state;
- u32 len = sizeof(card_state);
- int err;
-
- for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) {
- err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED,
- &card_state, &len);
- if (err) {
- IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal "
- "failed.\n");
- return 0;
- }
-
- /* We'll break out if either the HW state says it is
- * in the state we want, or if HOST_COMPLETE command
- * finishes */
- if ((card_state == state) ||
- ((priv->status & STATUS_ENABLED) ?
- IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) {
- if (state == IPW_HW_STATE_ENABLED)
- priv->status |= STATUS_ENABLED;
- else
- priv->status &= ~STATUS_ENABLED;
-
- return 0;
- }
-
- udelay(50);
- }
-
- IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n",
- state ? "DISABLED" : "ENABLED");
- return -EIO;
-}
-
-/*********************************************************************
- Procedure : sw_reset_and_clock
- Purpose : Asserts s/w reset, asserts clock initialization
- and waits for clock stabilization
- ********************************************************************/
-static int sw_reset_and_clock(struct ipw2100_priv *priv)
-{
- int i;
- u32 r;
-
- // assert s/w reset
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_SW_RESET);
-
- // wait for clock stabilization
- for (i = 0; i < 1000; i++) {
- udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY);
-
- // check clock ready bit
- read_register(priv->net_dev, IPW_REG_RESET_REG, &r);
- if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET)
- break;
- }
-
- if (i == 1000)
- return -EIO; // TODO: better error value
-
- /* set "initialization complete" bit to move adapter to
- * D0 state */
- write_register(priv->net_dev, IPW_REG_GP_CNTRL,
- IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE);
-
- /* wait for clock stabilization */
- for (i = 0; i < 10000; i++) {
- udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4);
-
- /* check clock ready bit */
- read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
- if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY)
- break;
- }
-
- if (i == 10000)
- return -EIO; /* TODO: better error value */
-
- /* set D0 standby bit */
- read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
- write_register(priv->net_dev, IPW_REG_GP_CNTRL,
- r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
-
- return 0;
-}
-
-/*********************************************************************
- Procedure : ipw2100_download_firmware
- Purpose : Initiaze adapter after power on.
- The sequence is:
- 1. assert s/w reset first!
- 2. awake clocks & wait for clock stabilization
- 3. hold ARC (don't ask me why...)
- 4. load Dino ucode and reset/clock init again
- 5. zero-out shared mem
- 6. download f/w
- *******************************************************************/
-static int ipw2100_download_firmware(struct ipw2100_priv *priv)
-{
- u32 address;
- int err;
-
-#ifndef CONFIG_PM
- /* Fetch the firmware and microcode */
- struct ipw2100_fw ipw2100_firmware;
-#endif
-
- if (priv->fatal_error) {
- IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after "
- "fatal error %d. Interface must be brought down.\n",
- priv->net_dev->name, priv->fatal_error);
- return -EINVAL;
- }
-#ifdef CONFIG_PM
- if (!ipw2100_firmware.version) {
- err = ipw2100_get_firmware(priv, &ipw2100_firmware);
- if (err) {
- IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
- priv->net_dev->name, err);
- priv->fatal_error = IPW2100_ERR_FW_LOAD;
- goto fail;
- }
- }
-#else
- err = ipw2100_get_firmware(priv, &ipw2100_firmware);
- if (err) {
- IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
- priv->net_dev->name, err);
- priv->fatal_error = IPW2100_ERR_FW_LOAD;
- goto fail;
- }
-#endif
- priv->firmware_version = ipw2100_firmware.version;
-
- /* s/w reset and clock stabilization */
- err = sw_reset_and_clock(priv);
- if (err) {
- IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- err = ipw2100_verify(priv);
- if (err) {
- IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- /* Hold ARC */
- write_nic_dword(priv->net_dev,
- IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000);
-
- /* allow ARC to run */
- write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
-
- /* load microcode */
- err = ipw2100_ucode_download(priv, &ipw2100_firmware);
- if (err) {
- printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- /* release ARC */
- write_nic_dword(priv->net_dev,
- IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000);
-
- /* s/w reset and clock stabilization (again!!!) */
- err = sw_reset_and_clock(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: sw_reset_and_clock failed: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- /* load f/w */
- err = ipw2100_fw_download(priv, &ipw2100_firmware);
- if (err) {
- IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-#ifndef CONFIG_PM
- /*
- * When the .resume method of the driver is called, the other
- * part of the system, i.e. the ide driver could still stay in
- * the suspend stage. This prevents us from loading the firmware
- * from the disk. --YZ
- */
-
- /* free any storage allocated for firmware image */
- ipw2100_release_firmware(priv, &ipw2100_firmware);
-#endif
-
- /* zero out Domain 1 area indirectly (Si requirement) */
- for (address = IPW_HOST_FW_SHARED_AREA0;
- address < IPW_HOST_FW_SHARED_AREA0_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_SHARED_AREA1;
- address < IPW_HOST_FW_SHARED_AREA1_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_SHARED_AREA2;
- address < IPW_HOST_FW_SHARED_AREA2_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_SHARED_AREA3;
- address < IPW_HOST_FW_SHARED_AREA3_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_INTERRUPT_AREA;
- address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
-
- return 0;
-
- fail:
- ipw2100_release_firmware(priv, &ipw2100_firmware);
- return err;
-}
-
-static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv)
-{
- if (priv->status & STATUS_INT_ENABLED)
- return;
- priv->status |= STATUS_INT_ENABLED;
- write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK);
-}
-
-static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv)
-{
- if (!(priv->status & STATUS_INT_ENABLED))
- return;
- priv->status &= ~STATUS_INT_ENABLED;
- write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0);
-}
-
-static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv)
-{
- struct ipw2100_ordinals *ord = &priv->ordinals;
-
- IPW_DEBUG_INFO("enter\n");
-
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1,
- &ord->table1_addr);
-
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2,
- &ord->table2_addr);
-
- read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size);
- read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size);
-
- ord->table2_size &= 0x0000FFFF;
-
- IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size);
- IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size);
- IPW_DEBUG_INFO("exit\n");
-}
-
-static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv)
-{
- u32 reg = 0;
- /*
- * Set GPIO 3 writable by FW; GPIO 1 writable
- * by driver and enable clock
- */
- reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE |
- IPW_BIT_GPIO_LED_OFF);
- write_register(priv->net_dev, IPW_REG_GPIO, reg);
-}
-
-static int rf_kill_active(struct ipw2100_priv *priv)
-{
-#define MAX_RF_KILL_CHECKS 5
-#define RF_KILL_CHECK_DELAY 40
-
- unsigned short value = 0;
- u32 reg = 0;
- int i;
-
- if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
- wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
- priv->status &= ~STATUS_RF_KILL_HW;
- return 0;
- }
-
- for (i = 0; i < MAX_RF_KILL_CHECKS; i++) {
- udelay(RF_KILL_CHECK_DELAY);
- read_register(priv->net_dev, IPW_REG_GPIO, &reg);
- value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
- }
-
- if (value == 0) {
- wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
- priv->status |= STATUS_RF_KILL_HW;
- } else {
- wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
- priv->status &= ~STATUS_RF_KILL_HW;
- }
-
- return (value == 0);
-}
-
-static int ipw2100_get_hw_features(struct ipw2100_priv *priv)
-{
- u32 addr, len;
- u32 val;
-
- /*
- * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1
- */
- len = sizeof(addr);
- if (ipw2100_get_ordinal
- (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return -EIO;
- }
-
- IPW_DEBUG_INFO("EEPROM address: %08X\n", addr);
-
- /*
- * EEPROM version is the byte at offset 0xfd in firmware
- * We read 4 bytes, then shift out the byte we actually want */
- read_nic_dword(priv->net_dev, addr + 0xFC, &val);
- priv->eeprom_version = (val >> 24) & 0xFF;
- IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version);
-
- /*
- * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware
- *
- * notice that the EEPROM bit is reverse polarity, i.e.
- * bit = 0 signifies HW RF kill switch is supported
- * bit = 1 signifies HW RF kill switch is NOT supported
- */
- read_nic_dword(priv->net_dev, addr + 0x20, &val);
- if (!((val >> 24) & 0x01))
- priv->hw_features |= HW_FEATURE_RFKILL;
-
- IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n",
- (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not ");
-
- return 0;
-}
-
-/*
- * Start firmware execution after power on and intialization
- * The sequence is:
- * 1. Release ARC
- * 2. Wait for f/w initialization completes;
- */
-static int ipw2100_start_adapter(struct ipw2100_priv *priv)
-{
- int i;
- u32 inta, inta_mask, gpio;
-
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->status & STATUS_RUNNING)
- return 0;
-
- /*
- * Initialize the hw - drive adapter to DO state by setting
- * init_done bit. Wait for clk_ready bit and Download
- * fw & dino ucode
- */
- if (ipw2100_download_firmware(priv)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to power on the adapter.\n",
- priv->net_dev->name);
- return -EIO;
- }
-
- /* Clear the Tx, Rx and Msg queues and the r/w indexes
- * in the firmware RBD and TBD ring queue */
- ipw2100_queues_initialize(priv);
-
- ipw2100_hw_set_gpio(priv);
-
- /* TODO -- Look at disabling interrupts here to make sure none
- * get fired during FW initialization */
-
- /* Release ARC - clear reset bit */
- write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
-
- /* wait for f/w intialization complete */
- IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n");
- i = 5000;
- do {
- schedule_timeout_uninterruptible(msecs_to_jiffies(40));
- /* Todo... wait for sync command ... */
-
- read_register(priv->net_dev, IPW_REG_INTA, &inta);
-
- /* check "init done" bit */
- if (inta & IPW2100_INTA_FW_INIT_DONE) {
- /* reset "init done" bit */
- write_register(priv->net_dev, IPW_REG_INTA,
- IPW2100_INTA_FW_INIT_DONE);
- break;
- }
-
- /* check error conditions : we check these after the firmware
- * check so that if there is an error, the interrupt handler
- * will see it and the adapter will be reset */
- if (inta &
- (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) {
- /* clear error conditions */
- write_register(priv->net_dev, IPW_REG_INTA,
- IPW2100_INTA_FATAL_ERROR |
- IPW2100_INTA_PARITY_ERROR);
- }
- } while (--i);
-
- /* Clear out any pending INTAs since we aren't supposed to have
- * interrupts enabled at this point... */
- read_register(priv->net_dev, IPW_REG_INTA, &inta);
- read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
- inta &= IPW_INTERRUPT_MASK;
- /* Clear out any pending interrupts */
- if (inta & inta_mask)
- write_register(priv->net_dev, IPW_REG_INTA, inta);
-
- IPW_DEBUG_FW("f/w initialization complete: %s\n",
- i ? "SUCCESS" : "FAILED");
-
- if (!i) {
- printk(KERN_WARNING DRV_NAME
- ": %s: Firmware did not initialize.\n",
- priv->net_dev->name);
- return -EIO;
- }
-
- /* allow firmware to write to GPIO1 & GPIO3 */
- read_register(priv->net_dev, IPW_REG_GPIO, &gpio);
-
- gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK);
-
- write_register(priv->net_dev, IPW_REG_GPIO, gpio);
-
- /* Ready to receive commands */
- priv->status |= STATUS_RUNNING;
-
- /* The adapter has been reset; we are not associated */
- priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv)
-{
- if (!priv->fatal_error)
- return;
-
- priv->fatal_errors[priv->fatal_index++] = priv->fatal_error;
- priv->fatal_index %= IPW2100_ERROR_QUEUE;
- priv->fatal_error = 0;
-}
-
-/* NOTE: Our interrupt is disabled when this method is called */
-static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv)
-{
- u32 reg;
- int i;
-
- IPW_DEBUG_INFO("Power cycling the hardware.\n");
-
- ipw2100_hw_set_gpio(priv);
-
- /* Step 1. Stop Master Assert */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_STOP_MASTER);
-
- /* Step 2. Wait for stop Master Assert
- * (not more than 50us, otherwise ret error */
- i = 5;
- do {
- udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
- read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
-
- if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
- break;
- } while (--i);
-
- priv->status &= ~STATUS_RESET_PENDING;
-
- if (!i) {
- IPW_DEBUG_INFO
- ("exit - waited too long for master assert stop\n");
- return -EIO;
- }
-
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_SW_RESET);
-
- /* Reset any fatal_error conditions */
- ipw2100_reset_fatalerror(priv);
-
- /* At this point, the adapter is now stopped and disabled */
- priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING |
- STATUS_ASSOCIATED | STATUS_ENABLED);
-
- return 0;
-}
-
-/*
- * Send the CARD_DISABLE_PHY_OFF command to the card to disable it
- *
- * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent.
- *
- * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of
- * if STATUS_ASSN_LOST is sent.
- */
-static int ipw2100_hw_phy_off(struct ipw2100_priv *priv)
-{
-
-#define HW_PHY_OFF_LOOP_DELAY (msecs_to_jiffies(50))
-
- struct host_command cmd = {
- .host_command = CARD_DISABLE_PHY_OFF,
- .host_command_sequence = 0,
- .host_command_length = 0,
- };
- int err, i;
- u32 val1, val2;
-
- IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n");
-
- /* Turn off the radio */
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- for (i = 0; i < 2500; i++) {
- read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1);
- read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2);
-
- if ((val1 & IPW2100_CONTROL_PHY_OFF) &&
- (val2 & IPW2100_COMMAND_PHY_OFF))
- return 0;
-
- schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY);
- }
-
- return -EIO;
-}
-
-static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = HOST_COMPLETE,
- .host_command_sequence = 0,
- .host_command_length = 0
- };
- int err = 0;
-
- IPW_DEBUG_HC("HOST_COMPLETE\n");
-
- if (priv->status & STATUS_ENABLED)
- return 0;
-
- mutex_lock(&priv->adapter_mutex);
-
- if (rf_kill_active(priv)) {
- IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
- goto fail_up;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err) {
- IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n");
- goto fail_up;
- }
-
- err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED);
- if (err) {
- IPW_DEBUG_INFO("%s: card not responding to init command.\n",
- priv->net_dev->name);
- goto fail_up;
- }
-
- if (priv->stop_hang_check) {
- priv->stop_hang_check = 0;
- schedule_delayed_work(&priv->hang_check, HZ / 2);
- }
-
- fail_up:
- mutex_unlock(&priv->adapter_mutex);
- return err;
-}
-
-static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
-{
-#define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100))
-
- struct host_command cmd = {
- .host_command = HOST_PRE_POWER_DOWN,
- .host_command_sequence = 0,
- .host_command_length = 0,
- };
- int err, i;
- u32 reg;
-
- if (!(priv->status & STATUS_RUNNING))
- return 0;
-
- priv->status |= STATUS_STOPPING;
-
- /* We can only shut down the card if the firmware is operational. So,
- * if we haven't reset since a fatal_error, then we can not send the
- * shutdown commands. */
- if (!priv->fatal_error) {
- /* First, make sure the adapter is enabled so that the PHY_OFF
- * command can shut it down */
- ipw2100_enable_adapter(priv);
-
- err = ipw2100_hw_phy_off(priv);
- if (err)
- printk(KERN_WARNING DRV_NAME
- ": Error disabling radio %d\n", err);
-
- /*
- * If in D0-standby mode going directly to D3 may cause a
- * PCI bus violation. Therefore we must change out of the D0
- * state.
- *
- * Sending the PREPARE_FOR_POWER_DOWN will restrict the
- * hardware from going into standby mode and will transition
- * out of D0-standby if it is already in that state.
- *
- * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
- * driver upon completion. Once received, the driver can
- * proceed to the D3 state.
- *
- * Prepare for power down command to fw. This command would
- * take HW out of D0-standby and prepare it for D3 state.
- *
- * Currently FW does not support event notification for this
- * event. Therefore, skip waiting for it. Just wait a fixed
- * 100ms
- */
- IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n");
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- printk(KERN_WARNING DRV_NAME ": "
- "%s: Power down command failed: Error %d\n",
- priv->net_dev->name, err);
- else
- schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY);
- }
-
- priv->status &= ~STATUS_ENABLED;
-
- /*
- * Set GPIO 3 writable by FW; GPIO 1 writable
- * by driver and enable clock
- */
- ipw2100_hw_set_gpio(priv);
-
- /*
- * Power down adapter. Sequence:
- * 1. Stop master assert (RESET_REG[9]=1)
- * 2. Wait for stop master (RESET_REG[8]==1)
- * 3. S/w reset assert (RESET_REG[7] = 1)
- */
-
- /* Stop master assert */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_STOP_MASTER);
-
- /* wait stop master not more than 50 usec.
- * Otherwise return error. */
- for (i = 5; i > 0; i--) {
- udelay(10);
-
- /* Check master stop bit */
- read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
-
- if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
- break;
- }
-
- if (i == 0)
- printk(KERN_WARNING DRV_NAME
- ": %s: Could now power down adapter.\n",
- priv->net_dev->name);
-
- /* assert s/w reset */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_SW_RESET);
-
- priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING);
-
- return 0;
-}
-
-static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = CARD_DISABLE,
- .host_command_sequence = 0,
- .host_command_length = 0
- };
- int err = 0;
-
- IPW_DEBUG_HC("CARD_DISABLE\n");
-
- if (!(priv->status & STATUS_ENABLED))
- return 0;
-
- /* Make sure we clear the associated state */
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
-
- if (!priv->stop_hang_check) {
- priv->stop_hang_check = 1;
- cancel_delayed_work(&priv->hang_check);
- }
-
- mutex_lock(&priv->adapter_mutex);
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- ": exit - failed to send CARD_DISABLE command\n");
- goto fail_up;
- }
-
- err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- ": exit - card failed to change to DISABLED\n");
- goto fail_up;
- }
-
- IPW_DEBUG_INFO("TODO: implement scan state machine\n");
-
- fail_up:
- mutex_unlock(&priv->adapter_mutex);
- return err;
-}
-
-static int ipw2100_set_scan_options(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = SET_SCAN_OPTIONS,
- .host_command_sequence = 0,
- .host_command_length = 8
- };
- int err;
-
- IPW_DEBUG_INFO("enter\n");
-
- IPW_DEBUG_SCAN("setting scan options\n");
-
- cmd.host_command_parameters[0] = 0;
-
- if (!(priv->config & CFG_ASSOCIATE))
- cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE;
- if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled)
- cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL;
- if (priv->config & CFG_PASSIVE_SCAN)
- cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE;
-
- cmd.host_command_parameters[1] = priv->channel_mask;
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n",
- cmd.host_command_parameters[0]);
-
- return err;
-}
-
-static int ipw2100_start_scan(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = BROADCAST_SCAN,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- IPW_DEBUG_HC("START_SCAN\n");
-
- cmd.host_command_parameters[0] = 0;
-
- /* No scanning if in monitor mode */
- if (priv->ieee->iw_mode == IW_MODE_MONITOR)
- return 1;
-
- if (priv->status & STATUS_SCANNING) {
- IPW_DEBUG_SCAN("Scan requested while already in scan...\n");
- return 0;
- }
-
- IPW_DEBUG_INFO("enter\n");
-
- /* Not clearing here; doing so makes iwlist always return nothing...
- *
- * We should modify the table logic to use aging tables vs. clearing
- * the table on each scan start.
- */
- IPW_DEBUG_SCAN("starting scan\n");
-
- priv->status |= STATUS_SCANNING;
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- priv->status &= ~STATUS_SCANNING;
-
- IPW_DEBUG_INFO("exit\n");
-
- return err;
-}
-
-static const struct libipw_geo ipw_geos[] = {
- { /* Restricted */
- "---",
- .bg_channels = 14,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}, {2467, 12},
- {2472, 13}, {2484, 14}},
- },
-};
-
-static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
-{
- unsigned long flags;
- int rc = 0;
- u32 lock;
- u32 ord_len = sizeof(lock);
-
- /* Age scan list entries found before suspend */
- if (priv->suspend_time) {
- libipw_networks_age(priv->ieee, priv->suspend_time);
- priv->suspend_time = 0;
- }
-
- /* Quiet if manually disabled. */
- if (priv->status & STATUS_RF_KILL_SW) {
- IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable "
- "switch\n", priv->net_dev->name);
- return 0;
- }
-
- /* the ipw2100 hardware really doesn't want power management delays
- * longer than 175usec
- */
- pm_qos_update_request(&ipw2100_pm_qos_req, 175);
-
- /* If the interrupt is enabled, turn it off... */
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_disable_interrupts(priv);
-
- /* Reset any fatal_error conditions */
- ipw2100_reset_fatalerror(priv);
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- if (priv->status & STATUS_POWERED ||
- (priv->status & STATUS_RESET_PENDING)) {
- /* Power cycle the card ... */
- if (ipw2100_power_cycle_adapter(priv)) {
- printk(KERN_WARNING DRV_NAME
- ": %s: Could not cycle adapter.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
- } else
- priv->status |= STATUS_POWERED;
-
- /* Load the firmware, start the clocks, etc. */
- if (ipw2100_start_adapter(priv)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to start the firmware.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- ipw2100_initialize_ordinals(priv);
-
- /* Determine capabilities of this particular HW configuration */
- if (ipw2100_get_hw_features(priv)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to determine HW features.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- /* Initialize the geo */
- libipw_set_geo(priv->ieee, &ipw_geos[0]);
- priv->ieee->freq_band = LIBIPW_24GHZ_BAND;
-
- lock = LOCK_NONE;
- if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to clear ordinal lock.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- priv->status &= ~STATUS_SCANNING;
-
- if (rf_kill_active(priv)) {
- printk(KERN_INFO "%s: Radio is disabled by RF switch.\n",
- priv->net_dev->name);
-
- if (priv->stop_rf_kill) {
- priv->stop_rf_kill = 0;
- schedule_delayed_work(&priv->rf_kill,
- round_jiffies_relative(HZ));
- }
-
- deferred = 1;
- }
-
- /* Turn on the interrupt so that commands can be processed */
- ipw2100_enable_interrupts(priv);
-
- /* Send all of the commands that must be sent prior to
- * HOST_COMPLETE */
- if (ipw2100_adapter_setup(priv)) {
- printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- if (!deferred) {
- /* Enable the adapter - sends HOST_COMPLETE */
- if (ipw2100_enable_adapter(priv)) {
- printk(KERN_ERR DRV_NAME ": "
- "%s: failed in call to enable adapter.\n",
- priv->net_dev->name);
- ipw2100_hw_stop_adapter(priv);
- rc = 1;
- goto exit;
- }
-
- /* Start a scan . . . */
- ipw2100_set_scan_options(priv);
- ipw2100_start_scan(priv);
- }
-
- exit:
- return rc;
-}
-
-static void ipw2100_down(struct ipw2100_priv *priv)
-{
- unsigned long flags;
- union iwreq_data wrqu = {
- .ap_addr = {
- .sa_family = ARPHRD_ETHER}
- };
- int associated = priv->status & STATUS_ASSOCIATED;
-
- /* Kill the RF switch timer */
- if (!priv->stop_rf_kill) {
- priv->stop_rf_kill = 1;
- cancel_delayed_work(&priv->rf_kill);
- }
-
- /* Kill the firmware hang check timer */
- if (!priv->stop_hang_check) {
- priv->stop_hang_check = 1;
- cancel_delayed_work(&priv->hang_check);
- }
-
- /* Kill any pending resets */
- if (priv->status & STATUS_RESET_PENDING)
- cancel_delayed_work(&priv->reset_work);
-
- /* Make sure the interrupt is on so that FW commands will be
- * processed correctly */
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_enable_interrupts(priv);
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- if (ipw2100_hw_stop_adapter(priv))
- printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n",
- priv->net_dev->name);
-
- /* Do not disable the interrupt until _after_ we disable
- * the adaptor. Otherwise the CARD_DISABLE command will never
- * be ack'd by the firmware */
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_disable_interrupts(priv);
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- pm_qos_update_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE);
-
- /* We have to signal any supplicant if we are disassociating */
- if (associated)
- wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
-
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
- netif_carrier_off(priv->net_dev);
- netif_stop_queue(priv->net_dev);
-}
-
-static int ipw2100_wdev_init(struct net_device *dev)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
- struct wireless_dev *wdev = &priv->ieee->wdev;
- int i;
-
- memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
-
- /* fill-out priv->ieee->bg_band */
- if (geo->bg_channels) {
- struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band;
-
- bg_band->band = IEEE80211_BAND_2GHZ;
- bg_band->n_channels = geo->bg_channels;
- bg_band->channels = kcalloc(geo->bg_channels,
- sizeof(struct ieee80211_channel),
- GFP_KERNEL);
- if (!bg_band->channels) {
- ipw2100_down(priv);
- return -ENOMEM;
- }
- /* translate geo->bg to bg_band.channels */
- for (i = 0; i < geo->bg_channels; i++) {
- bg_band->channels[i].band = IEEE80211_BAND_2GHZ;
- bg_band->channels[i].center_freq = geo->bg[i].freq;
- bg_band->channels[i].hw_value = geo->bg[i].channel;
- bg_band->channels[i].max_power = geo->bg[i].max_power;
- if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY)
- bg_band->channels[i].flags |=
- IEEE80211_CHAN_NO_IR;
- if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS)
- bg_band->channels[i].flags |=
- IEEE80211_CHAN_NO_IR;
- if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)
- bg_band->channels[i].flags |=
- IEEE80211_CHAN_RADAR;
- /* No equivalent for LIBIPW_CH_80211H_RULES,
- LIBIPW_CH_UNIFORM_SPREADING, or
- LIBIPW_CH_B_ONLY... */
- }
- /* point at bitrate info */
- bg_band->bitrates = ipw2100_bg_rates;
- bg_band->n_bitrates = RATE_COUNT;
-
- wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band;
- }
-
- wdev->wiphy->cipher_suites = ipw_cipher_suites;
- wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites);
-
- set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
- if (wiphy_register(wdev->wiphy))
- return -EIO;
- return 0;
-}
-
-static void ipw2100_reset_adapter(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, reset_work.work);
- unsigned long flags;
- union iwreq_data wrqu = {
- .ap_addr = {
- .sa_family = ARPHRD_ETHER}
- };
- int associated = priv->status & STATUS_ASSOCIATED;
-
- spin_lock_irqsave(&priv->low_lock, flags);
- IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name);
- priv->resets++;
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
- priv->status |= STATUS_SECURITY_UPDATED;
-
- /* Force a power cycle even if interface hasn't been opened
- * yet */
- cancel_delayed_work(&priv->reset_work);
- priv->status |= STATUS_RESET_PENDING;
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- mutex_lock(&priv->action_mutex);
- /* stop timed checks so that they don't interfere with reset */
- priv->stop_hang_check = 1;
- cancel_delayed_work(&priv->hang_check);
-
- /* We have to signal any supplicant if we are disassociating */
- if (associated)
- wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
-
- ipw2100_up(priv, 0);
- mutex_unlock(&priv->action_mutex);
-
-}
-
-static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
-{
-
-#define MAC_ASSOCIATION_READ_DELAY (HZ)
- int ret;
- unsigned int len, essid_len;
- char essid[IW_ESSID_MAX_SIZE];
- u32 txrate;
- u32 chan;
- char *txratename;
- u8 bssid[ETH_ALEN];
-
- /*
- * TBD: BSSID is usually 00:00:00:00:00:00 here and not
- * an actual MAC of the AP. Seems like FW sets this
- * address too late. Read it later and expose through
- * /proc or schedule a later task to query and update
- */
-
- essid_len = IW_ESSID_MAX_SIZE;
- ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID,
- essid, &essid_len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
-
- len = sizeof(u32);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
-
- len = sizeof(u32);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
- len = ETH_ALEN;
- ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid,
- &len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
- memcpy(priv->ieee->bssid, bssid, ETH_ALEN);
-
- switch (txrate) {
- case TX_RATE_1_MBIT:
- txratename = "1Mbps";
- break;
- case TX_RATE_2_MBIT:
- txratename = "2Mbsp";
- break;
- case TX_RATE_5_5_MBIT:
- txratename = "5.5Mbps";
- break;
- case TX_RATE_11_MBIT:
- txratename = "11Mbps";
- break;
- default:
- IPW_DEBUG_INFO("Unknown rate: %d\n", txrate);
- txratename = "unknown rate";
- break;
- }
-
- IPW_DEBUG_INFO("%s: Associated with '%*pE' at %s, channel %d (BSSID=%pM)\n",
- priv->net_dev->name, essid_len, essid,
- txratename, chan, bssid);
-
- /* now we copy read ssid into dev */
- if (!(priv->config & CFG_STATIC_ESSID)) {
- priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE);
- memcpy(priv->essid, essid, priv->essid_len);
- }
- priv->channel = chan;
- memcpy(priv->bssid, bssid, ETH_ALEN);
-
- priv->status |= STATUS_ASSOCIATING;
- priv->connect_start = get_seconds();
-
- schedule_delayed_work(&priv->wx_event_work, HZ / 10);
-}
-
-static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid,
- int length, int batch_mode)
-{
- int ssid_len = min(length, IW_ESSID_MAX_SIZE);
- struct host_command cmd = {
- .host_command = SSID,
- .host_command_sequence = 0,
- .host_command_length = ssid_len
- };
- int err;
-
- IPW_DEBUG_HC("SSID: '%*pE'\n", ssid_len, essid);
-
- if (ssid_len)
- memcpy(cmd.host_command_parameters, essid, ssid_len);
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to
- * disable auto association -- so we cheat by setting a bogus SSID */
- if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) {
- int i;
- u8 *bogus = (u8 *) cmd.host_command_parameters;
- for (i = 0; i < IW_ESSID_MAX_SIZE; i++)
- bogus[i] = 0x18 + i;
- cmd.host_command_length = IW_ESSID_MAX_SIZE;
- }
-
- /* NOTE: We always send the SSID command even if the provided ESSID is
- * the same as what we currently think is set. */
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (!err) {
- memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len);
- memcpy(priv->essid, essid, ssid_len);
- priv->essid_len = ssid_len;
- }
-
- if (!batch_mode) {
- if (ipw2100_enable_adapter(priv))
- err = -EIO;
- }
-
- return err;
-}
-
-static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
-{
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
- "disassociated: '%*pE' %pM\n", priv->essid_len, priv->essid,
- priv->bssid);
-
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
-
- if (priv->status & STATUS_STOPPING) {
- IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n");
- return;
- }
-
- eth_zero_addr(priv->bssid);
- eth_zero_addr(priv->ieee->bssid);
-
- netif_carrier_off(priv->net_dev);
- netif_stop_queue(priv->net_dev);
-
- if (!(priv->status & STATUS_RUNNING))
- return;
-
- if (priv->status & STATUS_SECURITY_UPDATED)
- schedule_delayed_work(&priv->security_work, 0);
-
- schedule_delayed_work(&priv->wx_event_work, 0);
-}
-
-static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
-{
- IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n",
- priv->net_dev->name);
-
- /* RF_KILL is now enabled (else we wouldn't be here) */
- wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
- priv->status |= STATUS_RF_KILL_HW;
-
- /* Make sure the RF Kill check timer is running */
- priv->stop_rf_kill = 0;
- mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ));
-}
-
-static void ipw2100_scan_event(struct work_struct *work)
-{
- struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv,
- scan_event.work);
- union iwreq_data wrqu;
-
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
-}
-
-static void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
-{
- IPW_DEBUG_SCAN("scan complete\n");
- /* Age the scan results... */
- priv->ieee->scans++;
- priv->status &= ~STATUS_SCANNING;
-
- /* Only userspace-requested scan completion events go out immediately */
- if (!priv->user_requested_scan) {
- schedule_delayed_work(&priv->scan_event,
- round_jiffies_relative(msecs_to_jiffies(4000)));
- } else {
- priv->user_requested_scan = 0;
- mod_delayed_work(system_wq, &priv->scan_event, 0);
- }
-}
-
-#ifdef CONFIG_IPW2100_DEBUG
-#define IPW2100_HANDLER(v, f) { v, f, # v }
-struct ipw2100_status_indicator {
- int status;
- void (*cb) (struct ipw2100_priv * priv, u32 status);
- char *name;
-};
-#else
-#define IPW2100_HANDLER(v, f) { v, f }
-struct ipw2100_status_indicator {
- int status;
- void (*cb) (struct ipw2100_priv * priv, u32 status);
-};
-#endif /* CONFIG_IPW2100_DEBUG */
-
-static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status)
-{
- IPW_DEBUG_SCAN("Scanning...\n");
- priv->status |= STATUS_SCANNING;
-}
-
-static const struct ipw2100_status_indicator status_handlers[] = {
- IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL),
- IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL),
- IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated),
- IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost),
- IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL),
- IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete),
- IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL),
- IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL),
- IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill),
- IPW2100_HANDLER(IPW_STATE_DISABLED, NULL),
- IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL),
- IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning),
- IPW2100_HANDLER(-1, NULL)
-};
-
-static void isr_status_change(struct ipw2100_priv *priv, int status)
-{
- int i;
-
- if (status == IPW_STATE_SCANNING &&
- priv->status & STATUS_ASSOCIATED &&
- !(priv->status & STATUS_SCANNING)) {
- IPW_DEBUG_INFO("Scan detected while associated, with "
- "no scan request. Restarting firmware.\n");
-
- /* Wake up any sleeping jobs */
- schedule_reset(priv);
- }
-
- for (i = 0; status_handlers[i].status != -1; i++) {
- if (status == status_handlers[i].status) {
- IPW_DEBUG_NOTIF("Status change: %s\n",
- status_handlers[i].name);
- if (status_handlers[i].cb)
- status_handlers[i].cb(priv, status);
- priv->wstats.status = status;
- return;
- }
- }
-
- IPW_DEBUG_NOTIF("unknown status received: %04x\n", status);
-}
-
-static void isr_rx_complete_command(struct ipw2100_priv *priv,
- struct ipw2100_cmd_header *cmd)
-{
-#ifdef CONFIG_IPW2100_DEBUG
- if (cmd->host_command_reg < ARRAY_SIZE(command_types)) {
- IPW_DEBUG_HC("Command completed '%s (%d)'\n",
- command_types[cmd->host_command_reg],
- cmd->host_command_reg);
- }
-#endif
- if (cmd->host_command_reg == HOST_COMPLETE)
- priv->status |= STATUS_ENABLED;
-
- if (cmd->host_command_reg == CARD_DISABLE)
- priv->status &= ~STATUS_ENABLED;
-
- priv->status &= ~STATUS_CMD_ACTIVE;
-
- wake_up_interruptible(&priv->wait_command_queue);
-}
-
-#ifdef CONFIG_IPW2100_DEBUG
-static const char *frame_types[] = {
- "COMMAND_STATUS_VAL",
- "STATUS_CHANGE_VAL",
- "P80211_DATA_VAL",
- "P8023_DATA_VAL",
- "HOST_NOTIFICATION_VAL"
-};
-#endif
-
-static int ipw2100_alloc_skb(struct ipw2100_priv *priv,
- struct ipw2100_rx_packet *packet)
-{
- packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx));
- if (!packet->skb)
- return -ENOMEM;
-
- packet->rxp = (struct ipw2100_rx *)packet->skb->data;
- packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data,
- sizeof(struct ipw2100_rx),
- PCI_DMA_FROMDEVICE);
- /* NOTE: pci_map_single does not return an error code, and 0 is a valid
- * dma_addr */
-
- return 0;
-}
-
-#define SEARCH_ERROR 0xffffffff
-#define SEARCH_FAIL 0xfffffffe
-#define SEARCH_SUCCESS 0xfffffff0
-#define SEARCH_DISCARD 0
-#define SEARCH_SNAPSHOT 1
-
-#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff))
-static void ipw2100_snapshot_free(struct ipw2100_priv *priv)
-{
- int i;
- if (!priv->snapshot[0])
- return;
- for (i = 0; i < 0x30; i++)
- kfree(priv->snapshot[i]);
- priv->snapshot[0] = NULL;
-}
-
-#ifdef IPW2100_DEBUG_C3
-static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
-{
- int i;
- if (priv->snapshot[0])
- return 1;
- for (i = 0; i < 0x30; i++) {
- priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC);
- if (!priv->snapshot[i]) {
- IPW_DEBUG_INFO("%s: Error allocating snapshot "
- "buffer %d\n", priv->net_dev->name, i);
- while (i > 0)
- kfree(priv->snapshot[--i]);
- priv->snapshot[0] = NULL;
- return 0;
- }
- }
-
- return 1;
-}
-
-static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf,
- size_t len, int mode)
-{
- u32 i, j;
- u32 tmp;
- u8 *s, *d;
- u32 ret;
-
- s = in_buf;
- if (mode == SEARCH_SNAPSHOT) {
- if (!ipw2100_snapshot_alloc(priv))
- mode = SEARCH_DISCARD;
- }
-
- for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) {
- read_nic_dword(priv->net_dev, i, &tmp);
- if (mode == SEARCH_SNAPSHOT)
- *(u32 *) SNAPSHOT_ADDR(i) = tmp;
- if (ret == SEARCH_FAIL) {
- d = (u8 *) & tmp;
- for (j = 0; j < 4; j++) {
- if (*s != *d) {
- s = in_buf;
- continue;
- }
-
- s++;
- d++;
-
- if ((s - in_buf) == len)
- ret = (i + j) - len + 1;
- }
- } else if (mode == SEARCH_DISCARD)
- return ret;
- }
-
- return ret;
-}
-#endif
-
-/*
- *
- * 0) Disconnect the SKB from the firmware (just unmap)
- * 1) Pack the ETH header into the SKB
- * 2) Pass the SKB to the network stack
- *
- * When packet is provided by the firmware, it contains the following:
- *
- * . libipw_hdr
- * . libipw_snap_hdr
- *
- * The size of the constructed ethernet
- *
- */
-#ifdef IPW2100_RX_DEBUG
-static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH];
-#endif
-
-static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i)
-{
-#ifdef IPW2100_DEBUG_C3
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- u32 match, reg;
- int j;
-#endif
-
- IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n",
- i * sizeof(struct ipw2100_status));
-
-#ifdef IPW2100_DEBUG_C3
- /* Halt the firmware so we can get a good image */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_STOP_MASTER);
- j = 5;
- do {
- udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
- read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
-
- if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
- break;
- } while (j--);
-
- match = ipw2100_match_buf(priv, (u8 *) status,
- sizeof(struct ipw2100_status),
- SEARCH_SNAPSHOT);
- if (match < SEARCH_SUCCESS)
- IPW_DEBUG_INFO("%s: DMA status match in Firmware at "
- "offset 0x%06X, length %d:\n",
- priv->net_dev->name, match,
- sizeof(struct ipw2100_status));
- else
- IPW_DEBUG_INFO("%s: No DMA status match in "
- "Firmware.\n", priv->net_dev->name);
-
- printk_buf((u8 *) priv->status_queue.drv,
- sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH);
-#endif
-
- priv->fatal_error = IPW2100_ERR_C3_CORRUPTION;
- priv->net_dev->stats.rx_errors++;
- schedule_reset(priv);
-}
-
-static void isr_rx(struct ipw2100_priv *priv, int i,
- struct libipw_rx_stats *stats)
-{
- struct net_device *dev = priv->net_dev;
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
-
- IPW_DEBUG_RX("Handler...\n");
-
- if (unlikely(status->frame_size > skb_tailroom(packet->skb))) {
- IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
- " Dropping.\n",
- dev->name,
- status->frame_size, skb_tailroom(packet->skb));
- dev->stats.rx_errors++;
- return;
- }
-
- if (unlikely(!netif_running(dev))) {
- dev->stats.rx_errors++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
- return;
- }
-
- if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
- !(priv->status & STATUS_ASSOCIATED))) {
- IPW_DEBUG_DROP("Dropping packet while not associated.\n");
- priv->wstats.discard.misc++;
- return;
- }
-
- pci_unmap_single(priv->pci_dev,
- packet->dma_addr,
- sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
-
- skb_put(packet->skb, status->frame_size);
-
-#ifdef IPW2100_RX_DEBUG
- /* Make a copy of the frame so we can dump it to the logs if
- * libipw_rx fails */
- skb_copy_from_linear_data(packet->skb, packet_data,
- min_t(u32, status->frame_size,
- IPW_RX_NIC_BUFFER_LENGTH));
-#endif
-
- if (!libipw_rx(priv->ieee, packet->skb, stats)) {
-#ifdef IPW2100_RX_DEBUG
- IPW_DEBUG_DROP("%s: Non consumed packet:\n",
- dev->name);
- printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
-#endif
- dev->stats.rx_errors++;
-
- /* libipw_rx failed, so it didn't free the SKB */
- dev_kfree_skb_any(packet->skb);
- packet->skb = NULL;
- }
-
- /* We need to allocate a new SKB and attach it to the RDB. */
- if (unlikely(ipw2100_alloc_skb(priv, packet))) {
- printk(KERN_WARNING DRV_NAME ": "
- "%s: Unable to allocate SKB onto RBD ring - disabling "
- "adapter.\n", dev->name);
- /* TODO: schedule adapter shutdown */
- IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
- }
-
- /* Update the RDB entry */
- priv->rx_queue.drv[i].host_addr = packet->dma_addr;
-}
-
-#ifdef CONFIG_IPW2100_MONITOR
-
-static void isr_rx_monitor(struct ipw2100_priv *priv, int i,
- struct libipw_rx_stats *stats)
-{
- struct net_device *dev = priv->net_dev;
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
-
- /* Magic struct that slots into the radiotap header -- no reason
- * to build this manually element by element, we can write it much
- * more efficiently than we can parse it. ORDER MATTERS HERE */
- struct ipw_rt_hdr {
- struct ieee80211_radiotap_header rt_hdr;
- s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
- } *ipw_rt;
-
- IPW_DEBUG_RX("Handler...\n");
-
- if (unlikely(status->frame_size > skb_tailroom(packet->skb) -
- sizeof(struct ipw_rt_hdr))) {
- IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
- " Dropping.\n",
- dev->name,
- status->frame_size,
- skb_tailroom(packet->skb));
- dev->stats.rx_errors++;
- return;
- }
-
- if (unlikely(!netif_running(dev))) {
- dev->stats.rx_errors++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
- return;
- }
-
- if (unlikely(priv->config & CFG_CRC_CHECK &&
- status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
- IPW_DEBUG_RX("CRC error in packet. Dropping.\n");
- dev->stats.rx_errors++;
- return;
- }
-
- pci_unmap_single(priv->pci_dev, packet->dma_addr,
- sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
- memmove(packet->skb->data + sizeof(struct ipw_rt_hdr),
- packet->skb->data, status->frame_size);
-
- ipw_rt = (struct ipw_rt_hdr *) packet->skb->data;
-
- ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
- ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
- ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */
-
- ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
-
- ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM;
-
- skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr));
-
- if (!libipw_rx(priv->ieee, packet->skb, stats)) {
- dev->stats.rx_errors++;
-
- /* libipw_rx failed, so it didn't free the SKB */
- dev_kfree_skb_any(packet->skb);
- packet->skb = NULL;
- }
-
- /* We need to allocate a new SKB and attach it to the RDB. */
- if (unlikely(ipw2100_alloc_skb(priv, packet))) {
- IPW_DEBUG_WARNING(
- "%s: Unable to allocate SKB onto RBD ring - disabling "
- "adapter.\n", dev->name);
- /* TODO: schedule adapter shutdown */
- IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
- }
-
- /* Update the RDB entry */
- priv->rx_queue.drv[i].host_addr = packet->dma_addr;
-}
-
-#endif
-
-static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
-{
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- struct ipw2100_rx *u = priv->rx_buffers[i].rxp;
- u16 frame_type = status->status_fields & STATUS_TYPE_MASK;
-
- switch (frame_type) {
- case COMMAND_STATUS_VAL:
- return (status->frame_size != sizeof(u->rx_data.command));
- case STATUS_CHANGE_VAL:
- return (status->frame_size != sizeof(u->rx_data.status));
- case HOST_NOTIFICATION_VAL:
- return (status->frame_size < sizeof(u->rx_data.notification));
- case P80211_DATA_VAL:
- case P8023_DATA_VAL:
-#ifdef CONFIG_IPW2100_MONITOR
- return 0;
-#else
- switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
- case IEEE80211_FTYPE_MGMT:
- case IEEE80211_FTYPE_CTL:
- return 0;
- case IEEE80211_FTYPE_DATA:
- return (status->frame_size >
- IPW_MAX_802_11_PAYLOAD_LENGTH);
- }
-#endif
- }
-
- return 1;
-}
-
-/*
- * ipw2100 interrupts are disabled at this point, and the ISR
- * is the only code that calls this method. So, we do not need
- * to play with any locks.
- *
- * RX Queue works as follows:
- *
- * Read index - firmware places packet in entry identified by the
- * Read index and advances Read index. In this manner,
- * Read index will always point to the next packet to
- * be filled--but not yet valid.
- *
- * Write index - driver fills this entry with an unused RBD entry.
- * This entry has not filled by the firmware yet.
- *
- * In between the W and R indexes are the RBDs that have been received
- * but not yet processed.
- *
- * The process of handling packets will start at WRITE + 1 and advance
- * until it reaches the READ index.
- *
- * The WRITE index is cached in the variable 'priv->rx_queue.next'.
- *
- */
-static void __ipw2100_rx_process(struct ipw2100_priv *priv)
-{
- struct ipw2100_bd_queue *rxq = &priv->rx_queue;
- struct ipw2100_status_queue *sq = &priv->status_queue;
- struct ipw2100_rx_packet *packet;
- u16 frame_type;
- u32 r, w, i, s;
- struct ipw2100_rx *u;
- struct libipw_rx_stats stats = {
- .mac_time = jiffies,
- };
-
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r);
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w);
-
- if (r >= rxq->entries) {
- IPW_DEBUG_RX("exit - bad read index\n");
- return;
- }
-
- i = (rxq->next + 1) % rxq->entries;
- s = i;
- while (i != r) {
- /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n",
- r, rxq->next, i); */
-
- packet = &priv->rx_buffers[i];
-
- /* Sync the DMA for the RX buffer so CPU is sure to get
- * the correct values */
- pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr,
- sizeof(struct ipw2100_rx),
- PCI_DMA_FROMDEVICE);
-
- if (unlikely(ipw2100_corruption_check(priv, i))) {
- ipw2100_corruption_detected(priv, i);
- goto increment;
- }
-
- u = packet->rxp;
- frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK;
- stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM;
- stats.len = sq->drv[i].frame_size;
-
- stats.mask = 0;
- if (stats.rssi != 0)
- stats.mask |= LIBIPW_STATMASK_RSSI;
- stats.freq = LIBIPW_24GHZ_BAND;
-
- IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n",
- priv->net_dev->name, frame_types[frame_type],
- stats.len);
-
- switch (frame_type) {
- case COMMAND_STATUS_VAL:
- /* Reset Rx watchdog */
- isr_rx_complete_command(priv, &u->rx_data.command);
- break;
-
- case STATUS_CHANGE_VAL:
- isr_status_change(priv, u->rx_data.status);
- break;
-
- case P80211_DATA_VAL:
- case P8023_DATA_VAL:
-#ifdef CONFIG_IPW2100_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- isr_rx_monitor(priv, i, &stats);
- break;
- }
-#endif
- if (stats.len < sizeof(struct libipw_hdr_3addr))
- break;
- switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
- case IEEE80211_FTYPE_MGMT:
- libipw_rx_mgt(priv->ieee,
- &u->rx_data.header, &stats);
- break;
-
- case IEEE80211_FTYPE_CTL:
- break;
-
- case IEEE80211_FTYPE_DATA:
- isr_rx(priv, i, &stats);
- break;
-
- }
- break;
- }
-
- increment:
- /* clear status field associated with this RBD */
- rxq->drv[i].status.info.field = 0;
-
- i = (i + 1) % rxq->entries;
- }
-
- if (i != s) {
- /* backtrack one entry, wrapping to end if at 0 */
- rxq->next = (i ? i : rxq->entries) - 1;
-
- write_register(priv->net_dev,
- IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next);
- }
-}
-
-/*
- * __ipw2100_tx_process
- *
- * This routine will determine whether the next packet on
- * the fw_pend_list has been processed by the firmware yet.
- *
- * If not, then it does nothing and returns.
- *
- * If so, then it removes the item from the fw_pend_list, frees
- * any associated storage, and places the item back on the
- * free list of its source (either msg_free_list or tx_free_list)
- *
- * TX Queue works as follows:
- *
- * Read index - points to the next TBD that the firmware will
- * process. The firmware will read the data, and once
- * done processing, it will advance the Read index.
- *
- * Write index - driver fills this entry with an constructed TBD
- * entry. The Write index is not advanced until the
- * packet has been configured.
- *
- * In between the W and R indexes are the TBDs that have NOT been
- * processed. Lagging behind the R index are packets that have
- * been processed but have not been freed by the driver.
- *
- * In order to free old storage, an internal index will be maintained
- * that points to the next packet to be freed. When all used
- * packets have been freed, the oldest index will be the same as the
- * firmware's read index.
- *
- * The OLDEST index is cached in the variable 'priv->tx_queue.oldest'
- *
- * Because the TBD structure can not contain arbitrary data, the
- * driver must keep an internal queue of cached allocations such that
- * it can put that data back into the tx_free_list and msg_free_list
- * for use by future command and data packets.
- *
- */
-static int __ipw2100_tx_process(struct ipw2100_priv *priv)
-{
- struct ipw2100_bd_queue *txq = &priv->tx_queue;
- struct ipw2100_bd *tbd;
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- int descriptors_used;
- int e, i;
- u32 r, w, frag_num = 0;
-
- if (list_empty(&priv->fw_pend_list))
- return 0;
-
- element = priv->fw_pend_list.next;
-
- packet = list_entry(element, struct ipw2100_tx_packet, list);
- tbd = &txq->drv[packet->index];
-
- /* Determine how many TBD entries must be finished... */
- switch (packet->type) {
- case COMMAND:
- /* COMMAND uses only one slot; don't advance */
- descriptors_used = 1;
- e = txq->oldest;
- break;
-
- case DATA:
- /* DATA uses two slots; advance and loop position. */
- descriptors_used = tbd->num_fragments;
- frag_num = tbd->num_fragments - 1;
- e = txq->oldest + frag_num;
- e %= txq->entries;
- break;
-
- default:
- printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n",
- priv->net_dev->name);
- return 0;
- }
-
- /* if the last TBD is not done by NIC yet, then packet is
- * not ready to be released.
- *
- */
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
- &r);
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
- &w);
- if (w != txq->next)
- printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n",
- priv->net_dev->name);
-
- /*
- * txq->next is the index of the last packet written txq->oldest is
- * the index of the r is the index of the next packet to be read by
- * firmware
- */
-
- /*
- * Quick graphic to help you visualize the following
- * if / else statement
- *
- * ===>| s---->|===============
- * e>|
- * | a | b | c | d | e | f | g | h | i | j | k | l
- * r---->|
- * w
- *
- * w - updated by driver
- * r - updated by firmware
- * s - start of oldest BD entry (txq->oldest)
- * e - end of oldest BD entry
- *
- */
- if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) {
- IPW_DEBUG_TX("exit - no processed packets ready to release.\n");
- return 0;
- }
-
- list_del(element);
- DEC_STAT(&priv->fw_pend_stat);
-
-#ifdef CONFIG_IPW2100_DEBUG
- {
- i = txq->oldest;
- IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
- &txq->drv[i],
- (u32) (txq->nic + i * sizeof(struct ipw2100_bd)),
- txq->drv[i].host_addr, txq->drv[i].buf_length);
-
- if (packet->type == DATA) {
- i = (i + 1) % txq->entries;
-
- IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
- &txq->drv[i],
- (u32) (txq->nic + i *
- sizeof(struct ipw2100_bd)),
- (u32) txq->drv[i].host_addr,
- txq->drv[i].buf_length);
- }
- }
-#endif
-
- switch (packet->type) {
- case DATA:
- if (txq->drv[txq->oldest].status.info.fields.txType != 0)
- printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
- "Expecting DATA TBD but pulled "
- "something else: ids %d=%d.\n",
- priv->net_dev->name, txq->oldest, packet->index);
-
- /* DATA packet; we have to unmap and free the SKB */
- for (i = 0; i < frag_num; i++) {
- tbd = &txq->drv[(packet->index + 1 + i) % txq->entries];
-
- IPW_DEBUG_TX("TX%d P=%08x L=%d\n",
- (packet->index + 1 + i) % txq->entries,
- tbd->host_addr, tbd->buf_length);
-
- pci_unmap_single(priv->pci_dev,
- tbd->host_addr,
- tbd->buf_length, PCI_DMA_TODEVICE);
- }
-
- libipw_txb_free(packet->info.d_struct.txb);
- packet->info.d_struct.txb = NULL;
-
- list_add_tail(element, &priv->tx_free_list);
- INC_STAT(&priv->tx_free_stat);
-
- /* We have a free slot in the Tx queue, so wake up the
- * transmit layer if it is stopped. */
- if (priv->status & STATUS_ASSOCIATED)
- netif_wake_queue(priv->net_dev);
-
- /* A packet was processed by the hardware, so update the
- * watchdog */
- priv->net_dev->trans_start = jiffies;
-
- break;
-
- case COMMAND:
- if (txq->drv[txq->oldest].status.info.fields.txType != 1)
- printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
- "Expecting COMMAND TBD but pulled "
- "something else: ids %d=%d.\n",
- priv->net_dev->name, txq->oldest, packet->index);
-
-#ifdef CONFIG_IPW2100_DEBUG
- if (packet->info.c_struct.cmd->host_command_reg <
- ARRAY_SIZE(command_types))
- IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n",
- command_types[packet->info.c_struct.cmd->
- host_command_reg],
- packet->info.c_struct.cmd->
- host_command_reg,
- packet->info.c_struct.cmd->cmd_status_reg);
-#endif
-
- list_add_tail(element, &priv->msg_free_list);
- INC_STAT(&priv->msg_free_stat);
- break;
- }
-
- /* advance oldest used TBD pointer to start of next entry */
- txq->oldest = (e + 1) % txq->entries;
- /* increase available TBDs number */
- txq->available += descriptors_used;
- SET_STAT(&priv->txq_stat, txq->available);
-
- IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n",
- jiffies - packet->jiffy_start);
-
- return (!list_empty(&priv->fw_pend_list));
-}
-
-static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv)
-{
- int i = 0;
-
- while (__ipw2100_tx_process(priv) && i < 200)
- i++;
-
- if (i == 200) {
- printk(KERN_WARNING DRV_NAME ": "
- "%s: Driver is running slow (%d iters).\n",
- priv->net_dev->name, i);
- }
-}
-
-static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
-{
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- struct ipw2100_bd_queue *txq = &priv->tx_queue;
- struct ipw2100_bd *tbd;
- int next = txq->next;
-
- while (!list_empty(&priv->msg_pend_list)) {
- /* if there isn't enough space in TBD queue, then
- * don't stuff a new one in.
- * NOTE: 3 are needed as a command will take one,
- * and there is a minimum of 2 that must be
- * maintained between the r and w indexes
- */
- if (txq->available <= 3) {
- IPW_DEBUG_TX("no room in tx_queue\n");
- break;
- }
-
- element = priv->msg_pend_list.next;
- list_del(element);
- DEC_STAT(&priv->msg_pend_stat);
-
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- IPW_DEBUG_TX("using TBD at virt=%p, phys=%04X\n",
- &txq->drv[txq->next],
- (u32) (txq->nic + txq->next *
- sizeof(struct ipw2100_bd)));
-
- packet->index = txq->next;
-
- tbd = &txq->drv[txq->next];
-
- /* initialize TBD */
- tbd->host_addr = packet->info.c_struct.cmd_phys;
- tbd->buf_length = sizeof(struct ipw2100_cmd_header);
- /* not marking number of fragments causes problems
- * with f/w debug version */
- tbd->num_fragments = 1;
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_COMMAND |
- IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
-
- /* update TBD queue counters */
- txq->next++;
- txq->next %= txq->entries;
- txq->available--;
- DEC_STAT(&priv->txq_stat);
-
- list_add_tail(element, &priv->fw_pend_list);
- INC_STAT(&priv->fw_pend_stat);
- }
-
- if (txq->next != next) {
- /* kick off the DMA by notifying firmware the
- * write index has moved; make sure TBD stores are sync'd */
- wmb();
- write_register(priv->net_dev,
- IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
- txq->next);
- }
-}
-
-/*
- * ipw2100_tx_send_data
- *
- */
-static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
-{
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- struct ipw2100_bd_queue *txq = &priv->tx_queue;
- struct ipw2100_bd *tbd;
- int next = txq->next;
- int i = 0;
- struct ipw2100_data_header *ipw_hdr;
- struct libipw_hdr_3addr *hdr;
-
- while (!list_empty(&priv->tx_pend_list)) {
- /* if there isn't enough space in TBD queue, then
- * don't stuff a new one in.
- * NOTE: 4 are needed as a data will take two,
- * and there is a minimum of 2 that must be
- * maintained between the r and w indexes
- */
- element = priv->tx_pend_list.next;
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- if (unlikely(1 + packet->info.d_struct.txb->nr_frags >
- IPW_MAX_BDS)) {
- /* TODO: Support merging buffers if more than
- * IPW_MAX_BDS are used */
- IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded. "
- "Increase fragmentation level.\n",
- priv->net_dev->name);
- }
-
- if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) {
- IPW_DEBUG_TX("no room in tx_queue\n");
- break;
- }
-
- list_del(element);
- DEC_STAT(&priv->tx_pend_stat);
-
- tbd = &txq->drv[txq->next];
-
- packet->index = txq->next;
-
- ipw_hdr = packet->info.d_struct.data;
- hdr = (struct libipw_hdr_3addr *)packet->info.d_struct.txb->
- fragments[0]->data;
-
- if (priv->ieee->iw_mode == IW_MODE_INFRA) {
- /* To DS: Addr1 = BSSID, Addr2 = SA,
- Addr3 = DA */
- memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
- memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN);
- } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- /* not From/To DS: Addr1 = DA, Addr2 = SA,
- Addr3 = BSSID */
- memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
- memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN);
- }
-
- ipw_hdr->host_command_reg = SEND;
- ipw_hdr->host_command_reg1 = 0;
-
- /* For now we only support host based encryption */
- ipw_hdr->needs_encryption = 0;
- ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted;
- if (packet->info.d_struct.txb->nr_frags > 1)
- ipw_hdr->fragment_size =
- packet->info.d_struct.txb->frag_size -
- LIBIPW_3ADDR_LEN;
- else
- ipw_hdr->fragment_size = 0;
-
- tbd->host_addr = packet->info.d_struct.data_phys;
- tbd->buf_length = sizeof(struct ipw2100_data_header);
- tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags;
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_802_3 |
- IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
- txq->next++;
- txq->next %= txq->entries;
-
- IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n",
- packet->index, tbd->host_addr, tbd->buf_length);
-#ifdef CONFIG_IPW2100_DEBUG
- if (packet->info.d_struct.txb->nr_frags > 1)
- IPW_DEBUG_FRAG("fragment Tx: %d frames\n",
- packet->info.d_struct.txb->nr_frags);
-#endif
-
- for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) {
- tbd = &txq->drv[txq->next];
- if (i == packet->info.d_struct.txb->nr_frags - 1)
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_802_3 |
- IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
- else
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_802_3 |
- IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
-
- tbd->buf_length = packet->info.d_struct.txb->
- fragments[i]->len - LIBIPW_3ADDR_LEN;
-
- tbd->host_addr = pci_map_single(priv->pci_dev,
- packet->info.d_struct.
- txb->fragments[i]->
- data +
- LIBIPW_3ADDR_LEN,
- tbd->buf_length,
- PCI_DMA_TODEVICE);
-
- IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n",
- txq->next, tbd->host_addr,
- tbd->buf_length);
-
- pci_dma_sync_single_for_device(priv->pci_dev,
- tbd->host_addr,
- tbd->buf_length,
- PCI_DMA_TODEVICE);
-
- txq->next++;
- txq->next %= txq->entries;
- }
-
- txq->available -= 1 + packet->info.d_struct.txb->nr_frags;
- SET_STAT(&priv->txq_stat, txq->available);
-
- list_add_tail(element, &priv->fw_pend_list);
- INC_STAT(&priv->fw_pend_stat);
- }
-
- if (txq->next != next) {
- /* kick off the DMA by notifying firmware the
- * write index has moved; make sure TBD stores are sync'd */
- write_register(priv->net_dev,
- IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
- txq->next);
- }
-}
-
-static void ipw2100_irq_tasklet(struct ipw2100_priv *priv)
-{
- struct net_device *dev = priv->net_dev;
- unsigned long flags;
- u32 inta, tmp;
-
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_disable_interrupts(priv);
-
- read_register(dev, IPW_REG_INTA, &inta);
-
- IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n",
- (unsigned long)inta & IPW_INTERRUPT_MASK);
-
- priv->in_isr++;
- priv->interrupts++;
-
- /* We do not loop and keep polling for more interrupts as this
- * is frowned upon and doesn't play nicely with other potentially
- * chained IRQs */
- IPW_DEBUG_ISR("INTA: 0x%08lX\n",
- (unsigned long)inta & IPW_INTERRUPT_MASK);
-
- if (inta & IPW2100_INTA_FATAL_ERROR) {
- printk(KERN_WARNING DRV_NAME
- ": Fatal interrupt. Scheduling firmware restart.\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR);
-
- read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error);
- IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n",
- priv->net_dev->name, priv->fatal_error);
-
- read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp);
- IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n",
- priv->net_dev->name, tmp);
-
- /* Wake up any sleeping jobs */
- schedule_reset(priv);
- }
-
- if (inta & IPW2100_INTA_PARITY_ERROR) {
- printk(KERN_ERR DRV_NAME
- ": ***** PARITY ERROR INTERRUPT !!!!\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR);
- }
-
- if (inta & IPW2100_INTA_RX_TRANSFER) {
- IPW_DEBUG_ISR("RX interrupt\n");
-
- priv->rx_interrupts++;
-
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER);
-
- __ipw2100_rx_process(priv);
- __ipw2100_tx_complete(priv);
- }
-
- if (inta & IPW2100_INTA_TX_TRANSFER) {
- IPW_DEBUG_ISR("TX interrupt\n");
-
- priv->tx_interrupts++;
-
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER);
-
- __ipw2100_tx_complete(priv);
- ipw2100_tx_send_commands(priv);
- ipw2100_tx_send_data(priv);
- }
-
- if (inta & IPW2100_INTA_TX_COMPLETE) {
- IPW_DEBUG_ISR("TX complete\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE);
-
- __ipw2100_tx_complete(priv);
- }
-
- if (inta & IPW2100_INTA_EVENT_INTERRUPT) {
- /* ipw2100_handle_event(dev); */
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT);
- }
-
- if (inta & IPW2100_INTA_FW_INIT_DONE) {
- IPW_DEBUG_ISR("FW init done interrupt\n");
- priv->inta_other++;
-
- read_register(dev, IPW_REG_INTA, &tmp);
- if (tmp & (IPW2100_INTA_FATAL_ERROR |
- IPW2100_INTA_PARITY_ERROR)) {
- write_register(dev, IPW_REG_INTA,
- IPW2100_INTA_FATAL_ERROR |
- IPW2100_INTA_PARITY_ERROR);
- }
-
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE);
- }
-
- if (inta & IPW2100_INTA_STATUS_CHANGE) {
- IPW_DEBUG_ISR("Status change interrupt\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE);
- }
-
- if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) {
- IPW_DEBUG_ISR("slave host mode interrupt\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA,
- IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE);
- }
-
- priv->in_isr--;
- ipw2100_enable_interrupts(priv);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- IPW_DEBUG_ISR("exit\n");
-}
-
-static irqreturn_t ipw2100_interrupt(int irq, void *data)
-{
- struct ipw2100_priv *priv = data;
- u32 inta, inta_mask;
-
- if (!data)
- return IRQ_NONE;
-
- spin_lock(&priv->low_lock);
-
- /* We check to see if we should be ignoring interrupts before
- * we touch the hardware. During ucode load if we try and handle
- * an interrupt we can cause keyboard problems as well as cause
- * the ucode to fail to initialize */
- if (!(priv->status & STATUS_INT_ENABLED)) {
- /* Shared IRQ */
- goto none;
- }
-
- read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
- read_register(priv->net_dev, IPW_REG_INTA, &inta);
-
- if (inta == 0xFFFFFFFF) {
- /* Hardware disappeared */
- printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n");
- goto none;
- }
-
- inta &= IPW_INTERRUPT_MASK;
-
- if (!(inta & inta_mask)) {
- /* Shared interrupt */
- goto none;
- }
-
- /* We disable the hardware interrupt here just to prevent unneeded
- * calls to be made. We disable this again within the actual
- * work tasklet, so if another part of the code re-enables the
- * interrupt, that is fine */
- ipw2100_disable_interrupts(priv);
-
- tasklet_schedule(&priv->irq_tasklet);
- spin_unlock(&priv->low_lock);
-
- return IRQ_HANDLED;
- none:
- spin_unlock(&priv->low_lock);
- return IRQ_NONE;
-}
-
-static netdev_tx_t ipw2100_tx(struct libipw_txb *txb,
- struct net_device *dev, int pri)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (!(priv->status & STATUS_ASSOCIATED)) {
- IPW_DEBUG_INFO("Can not transmit when not connected.\n");
- priv->net_dev->stats.tx_carrier_errors++;
- netif_stop_queue(dev);
- goto fail_unlock;
- }
-
- if (list_empty(&priv->tx_free_list))
- goto fail_unlock;
-
- element = priv->tx_free_list.next;
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- packet->info.d_struct.txb = txb;
-
- IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len);
- printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len);
-
- packet->jiffy_start = jiffies;
-
- list_del(element);
- DEC_STAT(&priv->tx_free_stat);
-
- list_add_tail(element, &priv->tx_pend_list);
- INC_STAT(&priv->tx_pend_stat);
-
- ipw2100_tx_send_data(priv);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
- return NETDEV_TX_OK;
-
-fail_unlock:
- netif_stop_queue(dev);
- spin_unlock_irqrestore(&priv->low_lock, flags);
- return NETDEV_TX_BUSY;
-}
-
-static int ipw2100_msg_allocate(struct ipw2100_priv *priv)
-{
- int i, j, err = -EINVAL;
- void *v;
- dma_addr_t p;
-
- priv->msg_buffers =
- kmalloc(IPW_COMMAND_POOL_SIZE * sizeof(struct ipw2100_tx_packet),
- GFP_KERNEL);
- if (!priv->msg_buffers)
- return -ENOMEM;
-
- for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
- v = pci_zalloc_consistent(priv->pci_dev,
- sizeof(struct ipw2100_cmd_header),
- &p);
- if (!v) {
- printk(KERN_ERR DRV_NAME ": "
- "%s: PCI alloc failed for msg "
- "buffers.\n", priv->net_dev->name);
- err = -ENOMEM;
- break;
- }
-
- priv->msg_buffers[i].type = COMMAND;
- priv->msg_buffers[i].info.c_struct.cmd =
- (struct ipw2100_cmd_header *)v;
- priv->msg_buffers[i].info.c_struct.cmd_phys = p;
- }
-
- if (i == IPW_COMMAND_POOL_SIZE)
- return 0;
-
- for (j = 0; j < i; j++) {
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_cmd_header),
- priv->msg_buffers[j].info.c_struct.cmd,
- priv->msg_buffers[j].info.c_struct.
- cmd_phys);
- }
-
- kfree(priv->msg_buffers);
- priv->msg_buffers = NULL;
-
- return err;
-}
-
-static int ipw2100_msg_initialize(struct ipw2100_priv *priv)
-{
- int i;
-
- INIT_LIST_HEAD(&priv->msg_free_list);
- INIT_LIST_HEAD(&priv->msg_pend_list);
-
- for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++)
- list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list);
- SET_STAT(&priv->msg_free_stat, i);
-
- return 0;
-}
-
-static void ipw2100_msg_free(struct ipw2100_priv *priv)
-{
- int i;
-
- if (!priv->msg_buffers)
- return;
-
- for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_cmd_header),
- priv->msg_buffers[i].info.c_struct.cmd,
- priv->msg_buffers[i].info.c_struct.
- cmd_phys);
- }
-
- kfree(priv->msg_buffers);
- priv->msg_buffers = NULL;
-}
-
-static ssize_t show_pci(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev);
- char *out = buf;
- int i, j;
- u32 val;
-
- for (i = 0; i < 16; i++) {
- out += sprintf(out, "[%08X] ", i * 16);
- for (j = 0; j < 16; j += 4) {
- pci_read_config_dword(pci_dev, i * 16 + j, &val);
- out += sprintf(out, "%08X ", val);
- }
- out += sprintf(out, "\n");
- }
-
- return out - buf;
-}
-
-static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL);
-
-static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *p = dev_get_drvdata(d);
- return sprintf(buf, "0x%08x\n", (int)p->config);
-}
-
-static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
-
-static ssize_t show_status(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *p = dev_get_drvdata(d);
- return sprintf(buf, "0x%08x\n", (int)p->status);
-}
-
-static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
-
-static ssize_t show_capability(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *p = dev_get_drvdata(d);
- return sprintf(buf, "0x%08x\n", (int)p->capability);
-}
-
-static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL);
-
-#define IPW2100_REG(x) { IPW_ ##x, #x }
-static const struct {
- u32 addr;
- const char *name;
-} hw_data[] = {
-IPW2100_REG(REG_GP_CNTRL),
- IPW2100_REG(REG_GPIO),
- IPW2100_REG(REG_INTA),
- IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),};
-#define IPW2100_NIC(x, s) { x, #x, s }
-static const struct {
- u32 addr;
- const char *name;
- size_t size;
-} nic_data[] = {
-IPW2100_NIC(IPW2100_CONTROL_REG, 2),
- IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),};
-#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d }
-static const struct {
- u8 index;
- const char *name;
- const char *desc;
-} ord_data[] = {
-IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
- IPW2100_ORD(STAT_TX_HOST_COMPLETE,
- "successful Host Tx's (MSDU)"),
- IPW2100_ORD(STAT_TX_DIR_DATA,
- "successful Directed Tx's (MSDU)"),
- IPW2100_ORD(STAT_TX_DIR_DATA1,
- "successful Directed Tx's (MSDU) @ 1MB"),
- IPW2100_ORD(STAT_TX_DIR_DATA2,
- "successful Directed Tx's (MSDU) @ 2MB"),
- IPW2100_ORD(STAT_TX_DIR_DATA5_5,
- "successful Directed Tx's (MSDU) @ 5_5MB"),
- IPW2100_ORD(STAT_TX_DIR_DATA11,
- "successful Directed Tx's (MSDU) @ 11MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA1,
- "successful Non_Directed Tx's (MSDU) @ 1MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA2,
- "successful Non_Directed Tx's (MSDU) @ 2MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA5_5,
- "successful Non_Directed Tx's (MSDU) @ 5.5MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA11,
- "successful Non_Directed Tx's (MSDU) @ 11MB"),
- IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"),
- IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"),
- IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"),
- IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"),
- IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"),
- IPW2100_ORD(STAT_TX_ASSN_RESP,
- "successful Association response Tx's"),
- IPW2100_ORD(STAT_TX_REASSN,
- "successful Reassociation Tx's"),
- IPW2100_ORD(STAT_TX_REASSN_RESP,
- "successful Reassociation response Tx's"),
- IPW2100_ORD(STAT_TX_PROBE,
- "probes successfully transmitted"),
- IPW2100_ORD(STAT_TX_PROBE_RESP,
- "probe responses successfully transmitted"),
- IPW2100_ORD(STAT_TX_BEACON, "tx beacon"),
- IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"),
- IPW2100_ORD(STAT_TX_DISASSN,
- "successful Disassociation TX"),
- IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"),
- IPW2100_ORD(STAT_TX_DEAUTH,
- "successful Deauthentication TX"),
- IPW2100_ORD(STAT_TX_TOTAL_BYTES,
- "Total successful Tx data bytes"),
- IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"),
- IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"),
- IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"),
- IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"),
- IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"),
- IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"),
- IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP,
- "times max tries in a hop failed"),
- IPW2100_ORD(STAT_TX_DISASSN_FAIL,
- "times disassociation failed"),
- IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"),
- IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"),
- IPW2100_ORD(STAT_RX_HOST, "packets passed to host"),
- IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"),
- IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"),
- IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"),
- IPW2100_ORD(STAT_RX_DIR_DATA5_5,
- "directed packets at 5.5MB"),
- IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"),
- IPW2100_ORD(STAT_RX_NODIR_DATA1,
- "nondirected packets at 1MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA2,
- "nondirected packets at 2MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA5_5,
- "nondirected packets at 5.5MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA11,
- "nondirected packets at 11MB"),
- IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"),
- IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS,
- "Rx CTS"),
- IPW2100_ORD(STAT_RX_ACK, "Rx ACK"),
- IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"),
- IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"),
- IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"),
- IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"),
- IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"),
- IPW2100_ORD(STAT_RX_REASSN_RESP,
- "Reassociation response Rx's"),
- IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"),
- IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"),
- IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"),
- IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"),
- IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"),
- IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"),
- IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"),
- IPW2100_ORD(STAT_RX_TOTAL_BYTES,
- "Total rx data bytes received"),
- IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"),
- IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"),
- IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"),
- IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"),
- IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE1,
- "duplicate rx packets at 1MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE2,
- "duplicate rx packets at 2MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE5_5,
- "duplicate rx packets at 5.5MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE11,
- "duplicate rx packets at 11MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"),
- IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"),
- IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"),
- IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"),
- IPW2100_ORD(STAT_RX_INVALID_PROTOCOL,
- "rx frames with invalid protocol"),
- IPW2100_ORD(SYS_BOOT_TIME, "Boot time"),
- IPW2100_ORD(STAT_RX_NO_BUFFER,
- "rx frames rejected due to no buffer"),
- IPW2100_ORD(STAT_RX_MISSING_FRAG,
- "rx frames dropped due to missing fragment"),
- IPW2100_ORD(STAT_RX_ORPHAN_FRAG,
- "rx frames dropped due to non-sequential fragment"),
- IPW2100_ORD(STAT_RX_ORPHAN_FRAME,
- "rx frames dropped due to unmatched 1st frame"),
- IPW2100_ORD(STAT_RX_FRAG_AGEOUT,
- "rx frames dropped due to uncompleted frame"),
- IPW2100_ORD(STAT_RX_ICV_ERRORS,
- "ICV errors during decryption"),
- IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"),
- IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"),
- IPW2100_ORD(STAT_PSP_POLL_TIMEOUT,
- "poll response timeouts"),
- IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT,
- "timeouts waiting for last {broad,multi}cast pkt"),
- IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"),
- IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"),
- IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"),
- IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"),
- IPW2100_ORD(STAT_PERCENT_MISSED_BCNS,
- "current calculation of % missed beacons"),
- IPW2100_ORD(STAT_PERCENT_RETRIES,
- "current calculation of % missed tx retries"),
- IPW2100_ORD(ASSOCIATED_AP_PTR,
- "0 if not associated, else pointer to AP table entry"),
- IPW2100_ORD(AVAILABLE_AP_CNT,
- "AP's decsribed in the AP table"),
- IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"),
- IPW2100_ORD(STAT_AP_ASSNS, "associations"),
- IPW2100_ORD(STAT_ASSN_FAIL, "association failures"),
- IPW2100_ORD(STAT_ASSN_RESP_FAIL,
- "failures due to response fail"),
- IPW2100_ORD(STAT_FULL_SCANS, "full scans"),
- IPW2100_ORD(CARD_DISABLED, "Card Disabled"),
- IPW2100_ORD(STAT_ROAM_INHIBIT,
- "times roaming was inhibited due to activity"),
- IPW2100_ORD(RSSI_AT_ASSN,
- "RSSI of associated AP at time of association"),
- IPW2100_ORD(STAT_ASSN_CAUSE1,
- "reassociation: no probe response or TX on hop"),
- IPW2100_ORD(STAT_ASSN_CAUSE2,
- "reassociation: poor tx/rx quality"),
- IPW2100_ORD(STAT_ASSN_CAUSE3,
- "reassociation: tx/rx quality (excessive AP load"),
- IPW2100_ORD(STAT_ASSN_CAUSE4,
- "reassociation: AP RSSI level"),
- IPW2100_ORD(STAT_ASSN_CAUSE5,
- "reassociations due to load leveling"),
- IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"),
- IPW2100_ORD(STAT_AUTH_RESP_FAIL,
- "times authentication response failed"),
- IPW2100_ORD(STATION_TABLE_CNT,
- "entries in association table"),
- IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"),
- IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"),
- IPW2100_ORD(COUNTRY_CODE,
- "IEEE country code as recv'd from beacon"),
- IPW2100_ORD(COUNTRY_CHANNELS,
- "channels supported by country"),
- IPW2100_ORD(RESET_CNT, "adapter resets (warm)"),
- IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"),
- IPW2100_ORD(ANTENNA_DIVERSITY,
- "TRUE if antenna diversity is disabled"),
- IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"),
- IPW2100_ORD(OUR_FREQ,
- "current radio freq lower digits - channel ID"),
- IPW2100_ORD(RTC_TIME, "current RTC time"),
- IPW2100_ORD(PORT_TYPE, "operating mode"),
- IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"),
- IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"),
- IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"),
- IPW2100_ORD(BASIC_RATES, "basic tx rates"),
- IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"),
- IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"),
- IPW2100_ORD(CAPABILITIES,
- "Management frame capability field"),
- IPW2100_ORD(AUTH_TYPE, "Type of authentication"),
- IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"),
- IPW2100_ORD(RTS_THRESHOLD,
- "Min packet length for RTS handshaking"),
- IPW2100_ORD(INT_MODE, "International mode"),
- IPW2100_ORD(FRAGMENTATION_THRESHOLD,
- "protocol frag threshold"),
- IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS,
- "EEPROM offset in SRAM"),
- IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE,
- "EEPROM size in SRAM"),
- IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"),
- IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS,
- "EEPROM IBSS 11b channel set"),
- IPW2100_ORD(MAC_VERSION, "MAC Version"),
- IPW2100_ORD(MAC_REVISION, "MAC Revision"),
- IPW2100_ORD(RADIO_VERSION, "Radio Version"),
- IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"),
- IPW2100_ORD(UCODE_VERSION, "Ucode Version"),};
-
-static ssize_t show_registers(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- int i;
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- char *out = buf;
- u32 val = 0;
-
- out += sprintf(out, "%30s [Address ] : Hex\n", "Register");
-
- for (i = 0; i < ARRAY_SIZE(hw_data); i++) {
- read_register(dev, hw_data[i].addr, &val);
- out += sprintf(out, "%30s [%08X] : %08X\n",
- hw_data[i].name, hw_data[i].addr, val);
- }
-
- return out - buf;
-}
-
-static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
-
-static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- char *out = buf;
- int i;
-
- out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry");
-
- for (i = 0; i < ARRAY_SIZE(nic_data); i++) {
- u8 tmp8;
- u16 tmp16;
- u32 tmp32;
-
- switch (nic_data[i].size) {
- case 1:
- read_nic_byte(dev, nic_data[i].addr, &tmp8);
- out += sprintf(out, "%30s [%08X] : %02X\n",
- nic_data[i].name, nic_data[i].addr,
- tmp8);
- break;
- case 2:
- read_nic_word(dev, nic_data[i].addr, &tmp16);
- out += sprintf(out, "%30s [%08X] : %04X\n",
- nic_data[i].name, nic_data[i].addr,
- tmp16);
- break;
- case 4:
- read_nic_dword(dev, nic_data[i].addr, &tmp32);
- out += sprintf(out, "%30s [%08X] : %08X\n",
- nic_data[i].name, nic_data[i].addr,
- tmp32);
- break;
- }
- }
- return out - buf;
-}
-
-static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
-
-static ssize_t show_memory(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- static unsigned long loop = 0;
- int len = 0;
- u32 buffer[4];
- int i;
- char line[81];
-
- if (loop >= 0x30000)
- loop = 0;
-
- /* sysfs provides us PAGE_SIZE buffer */
- while (len < PAGE_SIZE - 128 && loop < 0x30000) {
-
- if (priv->snapshot[0])
- for (i = 0; i < 4; i++)
- buffer[i] =
- *(u32 *) SNAPSHOT_ADDR(loop + i * 4);
- else
- for (i = 0; i < 4; i++)
- read_nic_dword(dev, loop + i * 4, &buffer[i]);
-
- if (priv->dump_raw)
- len += sprintf(buf + len,
- "%c%c%c%c"
- "%c%c%c%c"
- "%c%c%c%c"
- "%c%c%c%c",
- ((u8 *) buffer)[0x0],
- ((u8 *) buffer)[0x1],
- ((u8 *) buffer)[0x2],
- ((u8 *) buffer)[0x3],
- ((u8 *) buffer)[0x4],
- ((u8 *) buffer)[0x5],
- ((u8 *) buffer)[0x6],
- ((u8 *) buffer)[0x7],
- ((u8 *) buffer)[0x8],
- ((u8 *) buffer)[0x9],
- ((u8 *) buffer)[0xa],
- ((u8 *) buffer)[0xb],
- ((u8 *) buffer)[0xc],
- ((u8 *) buffer)[0xd],
- ((u8 *) buffer)[0xe],
- ((u8 *) buffer)[0xf]);
- else
- len += sprintf(buf + len, "%s\n",
- snprint_line(line, sizeof(line),
- (u8 *) buffer, 16, loop));
- loop += 16;
- }
-
- return len;
-}
-
-static ssize_t store_memory(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- const char *p = buf;
-
- (void)dev; /* kill unused-var warning for debug-only code */
-
- if (count < 1)
- return count;
-
- if (p[0] == '1' ||
- (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) {
- IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n",
- dev->name);
- priv->dump_raw = 1;
-
- } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' &&
- tolower(p[1]) == 'f')) {
- IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n",
- dev->name);
- priv->dump_raw = 0;
-
- } else if (tolower(p[0]) == 'r') {
- IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name);
- ipw2100_snapshot_free(priv);
-
- } else
- IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, "
- "reset = clear memory snapshot\n", dev->name);
-
- return count;
-}
-
-static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory);
-
-static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- u32 val = 0;
- int len = 0;
- u32 val_len;
- static int loop = 0;
-
- if (priv->status & STATUS_RF_KILL_MASK)
- return 0;
-
- if (loop >= ARRAY_SIZE(ord_data))
- loop = 0;
-
- /* sysfs provides us PAGE_SIZE buffer */
- while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) {
- val_len = sizeof(u32);
-
- if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val,
- &val_len))
- len += sprintf(buf + len, "[0x%02X] = ERROR %s\n",
- ord_data[loop].index,
- ord_data[loop].desc);
- else
- len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n",
- ord_data[loop].index, val,
- ord_data[loop].desc);
- loop++;
- }
-
- return len;
-}
-
-static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL);
-
-static ssize_t show_stats(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- char *out = buf;
-
- out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n",
- priv->interrupts, priv->tx_interrupts,
- priv->rx_interrupts, priv->inta_other);
- out += sprintf(out, "firmware resets: %d\n", priv->resets);
- out += sprintf(out, "firmware hangs: %d\n", priv->hangs);
-#ifdef CONFIG_IPW2100_DEBUG
- out += sprintf(out, "packet mismatch image: %s\n",
- priv->snapshot[0] ? "YES" : "NO");
-#endif
-
- return out - buf;
-}
-
-static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
-
-static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
-{
- int err;
-
- if (mode == priv->ieee->iw_mode)
- return 0;
-
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
-
- switch (mode) {
- case IW_MODE_INFRA:
- priv->net_dev->type = ARPHRD_ETHER;
- break;
- case IW_MODE_ADHOC:
- priv->net_dev->type = ARPHRD_ETHER;
- break;
-#ifdef CONFIG_IPW2100_MONITOR
- case IW_MODE_MONITOR:
- priv->last_mode = priv->ieee->iw_mode;
- priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
- break;
-#endif /* CONFIG_IPW2100_MONITOR */
- }
-
- priv->ieee->iw_mode = mode;
-
-#ifdef CONFIG_PM
- /* Indicate ipw2100_download_firmware download firmware
- * from disk instead of memory. */
- ipw2100_firmware.version = 0;
-#endif
-
- printk(KERN_INFO "%s: Resetting on mode change.\n", priv->net_dev->name);
- priv->reset_backoff = 0;
- schedule_reset(priv);
-
- return 0;
-}
-
-static ssize_t show_internals(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- int len = 0;
-
-#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x)
-
- if (priv->status & STATUS_ASSOCIATED)
- len += sprintf(buf + len, "connected: %lu\n",
- get_seconds() - priv->connect_start);
- else
- len += sprintf(buf + len, "not connected\n");
-
- DUMP_VAR(ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx], "p");
- DUMP_VAR(status, "08lx");
- DUMP_VAR(config, "08lx");
- DUMP_VAR(capability, "08lx");
-
- len +=
- sprintf(buf + len, "last_rtc: %lu\n",
- (unsigned long)priv->last_rtc);
-
- DUMP_VAR(fatal_error, "d");
- DUMP_VAR(stop_hang_check, "d");
- DUMP_VAR(stop_rf_kill, "d");
- DUMP_VAR(messages_sent, "d");
-
- DUMP_VAR(tx_pend_stat.value, "d");
- DUMP_VAR(tx_pend_stat.hi, "d");
-
- DUMP_VAR(tx_free_stat.value, "d");
- DUMP_VAR(tx_free_stat.lo, "d");
-
- DUMP_VAR(msg_free_stat.value, "d");
- DUMP_VAR(msg_free_stat.lo, "d");
-
- DUMP_VAR(msg_pend_stat.value, "d");
- DUMP_VAR(msg_pend_stat.hi, "d");
-
- DUMP_VAR(fw_pend_stat.value, "d");
- DUMP_VAR(fw_pend_stat.hi, "d");
-
- DUMP_VAR(txq_stat.value, "d");
- DUMP_VAR(txq_stat.lo, "d");
-
- DUMP_VAR(ieee->scans, "d");
- DUMP_VAR(reset_backoff, "d");
-
- return len;
-}
-
-static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL);
-
-static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- char essid[IW_ESSID_MAX_SIZE + 1];
- u8 bssid[ETH_ALEN];
- u32 chan = 0;
- char *out = buf;
- unsigned int length;
- int ret;
-
- if (priv->status & STATUS_RF_KILL_MASK)
- return 0;
-
- memset(essid, 0, sizeof(essid));
- memset(bssid, 0, sizeof(bssid));
-
- length = IW_ESSID_MAX_SIZE;
- ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length);
- if (ret)
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
-
- length = sizeof(bssid);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
- bssid, &length);
- if (ret)
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
-
- length = sizeof(u32);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length);
- if (ret)
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
-
- out += sprintf(out, "ESSID: %s\n", essid);
- out += sprintf(out, "BSSID: %pM\n", bssid);
- out += sprintf(out, "Channel: %d\n", chan);
-
- return out - buf;
-}
-
-static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL);
-
-#ifdef CONFIG_IPW2100_DEBUG
-static ssize_t show_debug_level(struct device_driver *d, char *buf)
-{
- return sprintf(buf, "0x%08X\n", ipw2100_debug_level);
-}
-
-static ssize_t store_debug_level(struct device_driver *d,
- const char *buf, size_t count)
-{
- u32 val;
- int ret;
-
- ret = kstrtou32(buf, 0, &val);
- if (ret)
- IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf);
- else
- ipw2100_debug_level = val;
-
- return strnlen(buf, count);
-}
-
-static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level,
- store_debug_level);
-#endif /* CONFIG_IPW2100_DEBUG */
-
-static ssize_t show_fatal_error(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- char *out = buf;
- int i;
-
- if (priv->fatal_error)
- out += sprintf(out, "0x%08X\n", priv->fatal_error);
- else
- out += sprintf(out, "0\n");
-
- for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) {
- if (!priv->fatal_errors[(priv->fatal_index - i) %
- IPW2100_ERROR_QUEUE])
- continue;
-
- out += sprintf(out, "%d. 0x%08X\n", i,
- priv->fatal_errors[(priv->fatal_index - i) %
- IPW2100_ERROR_QUEUE]);
- }
-
- return out - buf;
-}
-
-static ssize_t store_fatal_error(struct device *d,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- schedule_reset(priv);
- return count;
-}
-
-static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error,
- store_fatal_error);
-
-static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- return sprintf(buf, "%d\n", priv->ieee->scan_age);
-}
-
-static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- unsigned long val;
- int ret;
-
- (void)dev; /* kill unused-var warning for debug-only code */
-
- IPW_DEBUG_INFO("enter\n");
-
- ret = kstrtoul(buf, 0, &val);
- if (ret) {
- IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
- } else {
- priv->ieee->scan_age = val;
- IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
- }
-
- IPW_DEBUG_INFO("exit\n");
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
-
-static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- /* 0 - RF kill not enabled
- 1 - SW based RF kill active (sysfs)
- 2 - HW based RF kill active
- 3 - Both HW and SW baed RF kill active */
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
- (rf_kill_active(priv) ? 0x2 : 0x0);
- return sprintf(buf, "%i\n", val);
-}
-
-static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
-{
- if ((disable_radio ? 1 : 0) ==
- (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
- return 0;
-
- IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
- disable_radio ? "OFF" : "ON");
-
- mutex_lock(&priv->action_mutex);
-
- if (disable_radio) {
- priv->status |= STATUS_RF_KILL_SW;
- ipw2100_down(priv);
- } else {
- priv->status &= ~STATUS_RF_KILL_SW;
- if (rf_kill_active(priv)) {
- IPW_DEBUG_RF_KILL("Can not turn radio back on - "
- "disabled by HW switch\n");
- /* Make sure the RF_KILL check timer is running */
- priv->stop_rf_kill = 0;
- mod_delayed_work(system_wq, &priv->rf_kill,
- round_jiffies_relative(HZ));
- } else
- schedule_reset(priv);
- }
-
- mutex_unlock(&priv->action_mutex);
- return 1;
-}
-
-static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- ipw_radio_kill_sw(priv, buf[0] == '1');
- return count;
-}
-
-static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
-
-static struct attribute *ipw2100_sysfs_entries[] = {
- &dev_attr_hardware.attr,
- &dev_attr_registers.attr,
- &dev_attr_ordinals.attr,
- &dev_attr_pci.attr,
- &dev_attr_stats.attr,
- &dev_attr_internals.attr,
- &dev_attr_bssinfo.attr,
- &dev_attr_memory.attr,
- &dev_attr_scan_age.attr,
- &dev_attr_fatal_error.attr,
- &dev_attr_rf_kill.attr,
- &dev_attr_cfg.attr,
- &dev_attr_status.attr,
- &dev_attr_capability.attr,
- NULL,
-};
-
-static struct attribute_group ipw2100_attribute_group = {
- .attrs = ipw2100_sysfs_entries,
-};
-
-static int status_queue_allocate(struct ipw2100_priv *priv, int entries)
-{
- struct ipw2100_status_queue *q = &priv->status_queue;
-
- IPW_DEBUG_INFO("enter\n");
-
- q->size = entries * sizeof(struct ipw2100_status);
- q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic);
- if (!q->drv) {
- IPW_DEBUG_WARNING("Can not allocate status queue.\n");
- return -ENOMEM;
- }
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-static void status_queue_free(struct ipw2100_priv *priv)
-{
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->status_queue.drv) {
- pci_free_consistent(priv->pci_dev, priv->status_queue.size,
- priv->status_queue.drv,
- priv->status_queue.nic);
- priv->status_queue.drv = NULL;
- }
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static int bd_queue_allocate(struct ipw2100_priv *priv,
- struct ipw2100_bd_queue *q, int entries)
-{
- IPW_DEBUG_INFO("enter\n");
-
- memset(q, 0, sizeof(struct ipw2100_bd_queue));
-
- q->entries = entries;
- q->size = entries * sizeof(struct ipw2100_bd);
- q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic);
- if (!q->drv) {
- IPW_DEBUG_INFO
- ("can't allocate shared memory for buffer descriptors\n");
- return -ENOMEM;
- }
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-static void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q)
-{
- IPW_DEBUG_INFO("enter\n");
-
- if (!q)
- return;
-
- if (q->drv) {
- pci_free_consistent(priv->pci_dev, q->size, q->drv, q->nic);
- q->drv = NULL;
- }
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static void bd_queue_initialize(struct ipw2100_priv *priv,
- struct ipw2100_bd_queue *q, u32 base, u32 size,
- u32 r, u32 w)
-{
- IPW_DEBUG_INFO("enter\n");
-
- IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv,
- (u32) q->nic);
-
- write_register(priv->net_dev, base, q->nic);
- write_register(priv->net_dev, size, q->entries);
- write_register(priv->net_dev, r, q->oldest);
- write_register(priv->net_dev, w, q->next);
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static void ipw2100_kill_works(struct ipw2100_priv *priv)
-{
- priv->stop_rf_kill = 1;
- priv->stop_hang_check = 1;
- cancel_delayed_work_sync(&priv->reset_work);
- cancel_delayed_work_sync(&priv->security_work);
- cancel_delayed_work_sync(&priv->wx_event_work);
- cancel_delayed_work_sync(&priv->hang_check);
- cancel_delayed_work_sync(&priv->rf_kill);
- cancel_delayed_work_sync(&priv->scan_event);
-}
-
-static int ipw2100_tx_allocate(struct ipw2100_priv *priv)
-{
- int i, j, err = -EINVAL;
- void *v;
- dma_addr_t p;
-
- IPW_DEBUG_INFO("enter\n");
-
- err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH);
- if (err) {
- IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n",
- priv->net_dev->name);
- return err;
- }
-
- priv->tx_buffers = kmalloc_array(TX_PENDED_QUEUE_LENGTH,
- sizeof(struct ipw2100_tx_packet),
- GFP_ATOMIC);
- if (!priv->tx_buffers) {
- bd_queue_free(priv, &priv->tx_queue);
- return -ENOMEM;
- }
-
- for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
- v = pci_alloc_consistent(priv->pci_dev,
- sizeof(struct ipw2100_data_header),
- &p);
- if (!v) {
- printk(KERN_ERR DRV_NAME
- ": %s: PCI alloc failed for tx " "buffers.\n",
- priv->net_dev->name);
- err = -ENOMEM;
- break;
- }
-
- priv->tx_buffers[i].type = DATA;
- priv->tx_buffers[i].info.d_struct.data =
- (struct ipw2100_data_header *)v;
- priv->tx_buffers[i].info.d_struct.data_phys = p;
- priv->tx_buffers[i].info.d_struct.txb = NULL;
- }
-
- if (i == TX_PENDED_QUEUE_LENGTH)
- return 0;
-
- for (j = 0; j < i; j++) {
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_data_header),
- priv->tx_buffers[j].info.d_struct.data,
- priv->tx_buffers[j].info.d_struct.
- data_phys);
- }
-
- kfree(priv->tx_buffers);
- priv->tx_buffers = NULL;
-
- return err;
-}
-
-static void ipw2100_tx_initialize(struct ipw2100_priv *priv)
-{
- int i;
-
- IPW_DEBUG_INFO("enter\n");
-
- /*
- * reinitialize packet info lists
- */
- INIT_LIST_HEAD(&priv->fw_pend_list);
- INIT_STAT(&priv->fw_pend_stat);
-
- /*
- * reinitialize lists
- */
- INIT_LIST_HEAD(&priv->tx_pend_list);
- INIT_LIST_HEAD(&priv->tx_free_list);
- INIT_STAT(&priv->tx_pend_stat);
- INIT_STAT(&priv->tx_free_stat);
-
- for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
- /* We simply drop any SKBs that have been queued for
- * transmit */
- if (priv->tx_buffers[i].info.d_struct.txb) {
- libipw_txb_free(priv->tx_buffers[i].info.d_struct.
- txb);
- priv->tx_buffers[i].info.d_struct.txb = NULL;
- }
-
- list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list);
- }
-
- SET_STAT(&priv->tx_free_stat, i);
-
- priv->tx_queue.oldest = 0;
- priv->tx_queue.available = priv->tx_queue.entries;
- priv->tx_queue.next = 0;
- INIT_STAT(&priv->txq_stat);
- SET_STAT(&priv->txq_stat, priv->tx_queue.available);
-
- bd_queue_initialize(priv, &priv->tx_queue,
- IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE,
- IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE,
- IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
- IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX);
-
- IPW_DEBUG_INFO("exit\n");
-
-}
-
-static void ipw2100_tx_free(struct ipw2100_priv *priv)
-{
- int i;
-
- IPW_DEBUG_INFO("enter\n");
-
- bd_queue_free(priv, &priv->tx_queue);
-
- if (!priv->tx_buffers)
- return;
-
- for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
- if (priv->tx_buffers[i].info.d_struct.txb) {
- libipw_txb_free(priv->tx_buffers[i].info.d_struct.
- txb);
- priv->tx_buffers[i].info.d_struct.txb = NULL;
- }
- if (priv->tx_buffers[i].info.d_struct.data)
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_data_header),
- priv->tx_buffers[i].info.d_struct.
- data,
- priv->tx_buffers[i].info.d_struct.
- data_phys);
- }
-
- kfree(priv->tx_buffers);
- priv->tx_buffers = NULL;
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static int ipw2100_rx_allocate(struct ipw2100_priv *priv)
-{
- int i, j, err = -EINVAL;
-
- IPW_DEBUG_INFO("enter\n");
-
- err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH);
- if (err) {
- IPW_DEBUG_INFO("failed bd_queue_allocate\n");
- return err;
- }
-
- err = status_queue_allocate(priv, RX_QUEUE_LENGTH);
- if (err) {
- IPW_DEBUG_INFO("failed status_queue_allocate\n");
- bd_queue_free(priv, &priv->rx_queue);
- return err;
- }
-
- /*
- * allocate packets
- */
- priv->rx_buffers = kmalloc(RX_QUEUE_LENGTH *
- sizeof(struct ipw2100_rx_packet),
- GFP_KERNEL);
- if (!priv->rx_buffers) {
- IPW_DEBUG_INFO("can't allocate rx packet buffer table\n");
-
- bd_queue_free(priv, &priv->rx_queue);
-
- status_queue_free(priv);
-
- return -ENOMEM;
- }
-
- for (i = 0; i < RX_QUEUE_LENGTH; i++) {
- struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
-
- err = ipw2100_alloc_skb(priv, packet);
- if (unlikely(err)) {
- err = -ENOMEM;
- break;
- }
-
- /* The BD holds the cache aligned address */
- priv->rx_queue.drv[i].host_addr = packet->dma_addr;
- priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH;
- priv->status_queue.drv[i].status_fields = 0;
- }
-
- if (i == RX_QUEUE_LENGTH)
- return 0;
-
- for (j = 0; j < i; j++) {
- pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr,
- sizeof(struct ipw2100_rx_packet),
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb(priv->rx_buffers[j].skb);
- }
-
- kfree(priv->rx_buffers);
- priv->rx_buffers = NULL;
-
- bd_queue_free(priv, &priv->rx_queue);
-
- status_queue_free(priv);
-
- return err;
-}
-
-static void ipw2100_rx_initialize(struct ipw2100_priv *priv)
-{
- IPW_DEBUG_INFO("enter\n");
-
- priv->rx_queue.oldest = 0;
- priv->rx_queue.available = priv->rx_queue.entries - 1;
- priv->rx_queue.next = priv->rx_queue.entries - 1;
-
- INIT_STAT(&priv->rxq_stat);
- SET_STAT(&priv->rxq_stat, priv->rx_queue.available);
-
- bd_queue_initialize(priv, &priv->rx_queue,
- IPW_MEM_HOST_SHARED_RX_BD_BASE,
- IPW_MEM_HOST_SHARED_RX_BD_SIZE,
- IPW_MEM_HOST_SHARED_RX_READ_INDEX,
- IPW_MEM_HOST_SHARED_RX_WRITE_INDEX);
-
- /* set up the status queue */
- write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE,
- priv->status_queue.nic);
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static void ipw2100_rx_free(struct ipw2100_priv *priv)
-{
- int i;
-
- IPW_DEBUG_INFO("enter\n");
-
- bd_queue_free(priv, &priv->rx_queue);
- status_queue_free(priv);
-
- if (!priv->rx_buffers)
- return;
-
- for (i = 0; i < RX_QUEUE_LENGTH; i++) {
- if (priv->rx_buffers[i].rxp) {
- pci_unmap_single(priv->pci_dev,
- priv->rx_buffers[i].dma_addr,
- sizeof(struct ipw2100_rx),
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb(priv->rx_buffers[i].skb);
- }
- }
-
- kfree(priv->rx_buffers);
- priv->rx_buffers = NULL;
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static int ipw2100_read_mac_address(struct ipw2100_priv *priv)
-{
- u32 length = ETH_ALEN;
- u8 addr[ETH_ALEN];
-
- int err;
-
- err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length);
- if (err) {
- IPW_DEBUG_INFO("MAC address read failed\n");
- return -EIO;
- }
-
- memcpy(priv->net_dev->dev_addr, addr, ETH_ALEN);
- IPW_DEBUG_INFO("card MAC is %pM\n", priv->net_dev->dev_addr);
-
- return 0;
-}
-
-/********************************************************************
- *
- * Firmware Commands
- *
- ********************************************************************/
-
-static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = ADAPTER_ADDRESS,
- .host_command_sequence = 0,
- .host_command_length = ETH_ALEN
- };
- int err;
-
- IPW_DEBUG_HC("SET_MAC_ADDRESS\n");
-
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->config & CFG_CUSTOM_MAC) {
- memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN);
- memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
- } else
- memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr,
- ETH_ALEN);
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- IPW_DEBUG_INFO("exit\n");
- return err;
-}
-
-static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = PORT_TYPE,
- .host_command_sequence = 0,
- .host_command_length = sizeof(u32)
- };
- int err;
-
- switch (port_type) {
- case IW_MODE_INFRA:
- cmd.host_command_parameters[0] = IPW_BSS;
- break;
- case IW_MODE_ADHOC:
- cmd.host_command_parameters[0] = IPW_IBSS;
- break;
- }
-
- IPW_DEBUG_HC("PORT_TYPE: %s\n",
- port_type == IPW_IBSS ? "Ad-Hoc" : "Managed");
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = CHANNEL,
- .host_command_sequence = 0,
- .host_command_length = sizeof(u32)
- };
- int err;
-
- cmd.host_command_parameters[0] = channel;
-
- IPW_DEBUG_HC("CHANNEL: %d\n", channel);
-
- /* If BSS then we don't support channel selection */
- if (priv->ieee->iw_mode == IW_MODE_INFRA)
- return 0;
-
- if ((channel != 0) &&
- ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL)))
- return -EINVAL;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err) {
- IPW_DEBUG_INFO("Failed to set channel to %d", channel);
- return err;
- }
-
- if (channel)
- priv->config |= CFG_STATIC_CHANNEL;
- else
- priv->config &= ~CFG_STATIC_CHANNEL;
-
- priv->channel = channel;
-
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = SYSTEM_CONFIG,
- .host_command_sequence = 0,
- .host_command_length = 12,
- };
- u32 ibss_mask, len = sizeof(u32);
- int err;
-
- /* Set system configuration */
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC)
- cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START;
-
- cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK |
- IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE;
-
- if (!(priv->config & CFG_LONG_PREAMBLE))
- cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO;
-
- err = ipw2100_get_ordinal(priv,
- IPW_ORD_EEPROM_IBSS_11B_CHANNELS,
- &ibss_mask, &len);
- if (err)
- ibss_mask = IPW_IBSS_11B_DEFAULT_MASK;
-
- cmd.host_command_parameters[1] = REG_CHANNEL_MASK;
- cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask;
-
- /* 11b only */
- /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
-/* If IPv6 is configured in the kernel then we don't want to filter out all
- * of the multicast packets as IPv6 needs some. */
-#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
- cmd.host_command = ADD_MULTICAST;
- cmd.host_command_sequence = 0;
- cmd.host_command_length = 0;
-
- ipw2100_hw_send_command(priv, &cmd);
-#endif
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = BASIC_TX_RATES,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = rate & TX_RATE_MASK;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- /* Set BASIC TX Rate first */
- ipw2100_hw_send_command(priv, &cmd);
-
- /* Set TX Rate */
- cmd.host_command = TX_RATES;
- ipw2100_hw_send_command(priv, &cmd);
-
- /* Set MSDU TX Rate */
- cmd.host_command = MSDU_TX_RATES;
- ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
-
- priv->tx_rates = rate;
-
- return 0;
-}
-
-static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level)
-{
- struct host_command cmd = {
- .host_command = POWER_MODE,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = power_level;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- if (power_level == IPW_POWER_MODE_CAM)
- priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
- else
- priv->power_mode = IPW_POWER_ENABLED | power_level;
-
-#ifdef IPW2100_TX_POWER
- if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) {
- /* Set beacon interval */
- cmd.host_command = TX_POWER_INDEX;
- cmd.host_command_parameters[0] = (u32) priv->adhoc_power;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
- }
-#endif
-
- return 0;
-}
-
-static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold)
-{
- struct host_command cmd = {
- .host_command = RTS_THRESHOLD,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- if (threshold & RTS_DISABLED)
- cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD;
- else
- cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- priv->rts_threshold = threshold;
-
- return 0;
-}
-
-#if 0
-int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv,
- u32 threshold, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = FRAG_THRESHOLD,
- .host_command_sequence = 0,
- .host_command_length = 4,
- .host_command_parameters[0] = 0,
- };
- int err;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- if (threshold == 0)
- threshold = DEFAULT_FRAG_THRESHOLD;
- else {
- threshold = max(threshold, MIN_FRAG_THRESHOLD);
- threshold = min(threshold, MAX_FRAG_THRESHOLD);
- }
-
- cmd.host_command_parameters[0] = threshold;
-
- IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold);
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- if (!err)
- priv->frag_threshold = threshold;
-
- return err;
-}
-#endif
-
-static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry)
-{
- struct host_command cmd = {
- .host_command = SHORT_RETRY_LIMIT,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = retry;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- priv->short_retry_limit = retry;
-
- return 0;
-}
-
-static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry)
-{
- struct host_command cmd = {
- .host_command = LONG_RETRY_LIMIT,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = retry;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- priv->long_retry_limit = retry;
-
- return 0;
-}
-
-static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = MANDATORY_BSSID,
- .host_command_sequence = 0,
- .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN
- };
- int err;
-
-#ifdef CONFIG_IPW2100_DEBUG
- if (bssid != NULL)
- IPW_DEBUG_HC("MANDATORY_BSSID: %pM\n", bssid);
- else
- IPW_DEBUG_HC("MANDATORY_BSSID: <clear>\n");
-#endif
- /* if BSSID is empty then we disable mandatory bssid mode */
- if (bssid != NULL)
- memcpy(cmd.host_command_parameters, bssid, ETH_ALEN);
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = DISASSOCIATION_BSSID,
- .host_command_sequence = 0,
- .host_command_length = ETH_ALEN
- };
- int err;
- int len;
-
- IPW_DEBUG_HC("DISASSOCIATION_BSSID\n");
-
- len = ETH_ALEN;
- /* The Firmware currently ignores the BSSID and just disassociates from
- * the currently associated AP -- but in the off chance that a future
- * firmware does use the BSSID provided here, we go ahead and try and
- * set it to the currently associated AP's BSSID */
- memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN);
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- return err;
-}
-
-static int ipw2100_set_wpa_ie(struct ipw2100_priv *,
- struct ipw2100_wpa_assoc_frame *, int)
- __attribute__ ((unused));
-
-static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv,
- struct ipw2100_wpa_assoc_frame *wpa_frame,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = SET_WPA_IE,
- .host_command_sequence = 0,
- .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame),
- };
- int err;
-
- IPW_DEBUG_HC("SET_WPA_IE\n");
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- memcpy(cmd.host_command_parameters, wpa_frame,
- sizeof(struct ipw2100_wpa_assoc_frame));
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- if (ipw2100_enable_adapter(priv))
- err = -EIO;
- }
-
- return err;
-}
-
-struct security_info_params {
- u32 allowed_ciphers;
- u16 version;
- u8 auth_mode;
- u8 replay_counters_number;
- u8 unicast_using_group;
-} __packed;
-
-static int ipw2100_set_security_information(struct ipw2100_priv *priv,
- int auth_mode,
- int security_level,
- int unicast_using_group,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = SET_SECURITY_INFORMATION,
- .host_command_sequence = 0,
- .host_command_length = sizeof(struct security_info_params)
- };
- struct security_info_params *security =
- (struct security_info_params *)&cmd.host_command_parameters;
- int err;
- memset(security, 0, sizeof(*security));
-
- /* If shared key AP authentication is turned on, then we need to
- * configure the firmware to try and use it.
- *
- * Actual data encryption/decryption is handled by the host. */
- security->auth_mode = auth_mode;
- security->unicast_using_group = unicast_using_group;
-
- switch (security_level) {
- default:
- case SEC_LEVEL_0:
- security->allowed_ciphers = IPW_NONE_CIPHER;
- break;
- case SEC_LEVEL_1:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER;
- break;
- case SEC_LEVEL_2:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER | IPW_TKIP_CIPHER;
- break;
- case SEC_LEVEL_2_CKIP:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER | IPW_CKIP_CIPHER;
- break;
- case SEC_LEVEL_3:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER;
- break;
- }
-
- IPW_DEBUG_HC
- ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n",
- security->auth_mode, security->allowed_ciphers, security_level);
-
- security->replay_counters_number = 0;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power)
-{
- struct host_command cmd = {
- .host_command = TX_POWER_INDEX,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err = 0;
- u32 tmp = tx_power;
-
- if (tx_power != IPW_TX_POWER_DEFAULT)
- tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 /
- (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
-
- cmd.host_command_parameters[0] = tmp;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC)
- err = ipw2100_hw_send_command(priv, &cmd);
- if (!err)
- priv->tx_power = tx_power;
-
- return 0;
-}
-
-static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv,
- u32 interval, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = BEACON_INTERVAL,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = interval;
-
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
- }
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-static void ipw2100_queues_initialize(struct ipw2100_priv *priv)
-{
- ipw2100_tx_initialize(priv);
- ipw2100_rx_initialize(priv);
- ipw2100_msg_initialize(priv);
-}
-
-static void ipw2100_queues_free(struct ipw2100_priv *priv)
-{
- ipw2100_tx_free(priv);
- ipw2100_rx_free(priv);
- ipw2100_msg_free(priv);
-}
-
-static int ipw2100_queues_allocate(struct ipw2100_priv *priv)
-{
- if (ipw2100_tx_allocate(priv) ||
- ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv))
- goto fail;
-
- return 0;
-
- fail:
- ipw2100_tx_free(priv);
- ipw2100_rx_free(priv);
- ipw2100_msg_free(priv);
- return -ENOMEM;
-}
-
-#define IPW_PRIVACY_CAPABLE 0x0008
-
-static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = WEP_FLAGS,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = flags;
-
- IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags);
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-struct ipw2100_wep_key {
- u8 idx;
- u8 len;
- u8 key[13];
-};
-
-/* Macros to ease up priting WEP keys */
-#define WEP_FMT_64 "%02X%02X%02X%02X-%02X"
-#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X"
-#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4]
-#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10]
-
-/**
- * Set a the wep key
- *
- * @priv: struct to work on
- * @idx: index of the key we want to set
- * @key: ptr to the key data to set
- * @len: length of the buffer at @key
- * @batch_mode: FIXME perform the operation in batch mode, not
- * disabling the device.
- *
- * @returns 0 if OK, < 0 errno code on error.
- *
- * Fill out a command structure with the new wep key, length an
- * index and send it down the wire.
- */
-static int ipw2100_set_key(struct ipw2100_priv *priv,
- int idx, char *key, int len, int batch_mode)
-{
- int keylen = len ? (len <= 5 ? 5 : 13) : 0;
- struct host_command cmd = {
- .host_command = WEP_KEY_INFO,
- .host_command_sequence = 0,
- .host_command_length = sizeof(struct ipw2100_wep_key),
- };
- struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters;
- int err;
-
- IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n",
- idx, keylen, len);
-
- /* NOTE: We don't check cached values in case the firmware was reset
- * or some other problem is occurring. If the user is setting the key,
- * then we push the change */
-
- wep_key->idx = idx;
- wep_key->len = keylen;
-
- if (keylen) {
- memcpy(wep_key->key, key, len);
- memset(wep_key->key + len, 0, keylen - len);
- }
-
- /* Will be optimized out on debug not being configured in */
- if (keylen == 0)
- IPW_DEBUG_WEP("%s: Clearing key %d\n",
- priv->net_dev->name, wep_key->idx);
- else if (keylen == 5)
- IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n",
- priv->net_dev->name, wep_key->idx, wep_key->len,
- WEP_STR_64(wep_key->key));
- else
- IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128
- "\n",
- priv->net_dev->name, wep_key->idx, wep_key->len,
- WEP_STR_128(wep_key->key));
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- int err2 = ipw2100_enable_adapter(priv);
- if (err == 0)
- err = err2;
- }
- return err;
-}
-
-static int ipw2100_set_key_index(struct ipw2100_priv *priv,
- int idx, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = WEP_KEY_INDEX,
- .host_command_sequence = 0,
- .host_command_length = 4,
- .host_command_parameters = {idx},
- };
- int err;
-
- IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx);
-
- if (idx < 0 || idx > 3)
- return -EINVAL;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode)
-{
- int i, err, auth_mode, sec_level, use_group;
-
- if (!(priv->status & STATUS_RUNNING))
- return 0;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- if (!priv->ieee->sec.enabled) {
- err =
- ipw2100_set_security_information(priv, IPW_AUTH_OPEN,
- SEC_LEVEL_0, 0, 1);
- } else {
- auth_mode = IPW_AUTH_OPEN;
- if (priv->ieee->sec.flags & SEC_AUTH_MODE) {
- if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)
- auth_mode = IPW_AUTH_SHARED;
- else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)
- auth_mode = IPW_AUTH_LEAP_CISCO_ID;
- }
-
- sec_level = SEC_LEVEL_0;
- if (priv->ieee->sec.flags & SEC_LEVEL)
- sec_level = priv->ieee->sec.level;
-
- use_group = 0;
- if (priv->ieee->sec.flags & SEC_UNICAST_GROUP)
- use_group = priv->ieee->sec.unicast_uses_group;
-
- err =
- ipw2100_set_security_information(priv, auth_mode, sec_level,
- use_group, 1);
- }
-
- if (err)
- goto exit;
-
- if (priv->ieee->sec.enabled) {
- for (i = 0; i < 4; i++) {
- if (!(priv->ieee->sec.flags & (1 << i))) {
- memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN);
- priv->ieee->sec.key_sizes[i] = 0;
- } else {
- err = ipw2100_set_key(priv, i,
- priv->ieee->sec.keys[i],
- priv->ieee->sec.
- key_sizes[i], 1);
- if (err)
- goto exit;
- }
- }
-
- ipw2100_set_key_index(priv, priv->ieee->crypt_info.tx_keyidx, 1);
- }
-
- /* Always enable privacy so the Host can filter WEP packets if
- * encrypted data is sent up */
- err =
- ipw2100_set_wep_flags(priv,
- priv->ieee->sec.
- enabled ? IPW_PRIVACY_CAPABLE : 0, 1);
- if (err)
- goto exit;
-
- priv->status &= ~STATUS_SECURITY_UPDATED;
-
- exit:
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static void ipw2100_security_work(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, security_work.work);
-
- /* If we happen to have reconnected before we get a chance to
- * process this, then update the security settings--which causes
- * a disassociation to occur */
- if (!(priv->status & STATUS_ASSOCIATED) &&
- priv->status & STATUS_SECURITY_UPDATED)
- ipw2100_configure_security(priv, 0);
-}
-
-static void shim__set_security(struct net_device *dev,
- struct libipw_security *sec)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int i, force_update = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED))
- goto done;
-
- for (i = 0; i < 4; i++) {
- if (sec->flags & (1 << i)) {
- priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
- if (sec->key_sizes[i] == 0)
- priv->ieee->sec.flags &= ~(1 << i);
- else
- memcpy(priv->ieee->sec.keys[i], sec->keys[i],
- sec->key_sizes[i]);
- if (sec->level == SEC_LEVEL_1) {
- priv->ieee->sec.flags |= (1 << i);
- priv->status |= STATUS_SECURITY_UPDATED;
- } else
- priv->ieee->sec.flags &= ~(1 << i);
- }
- }
-
- if ((sec->flags & SEC_ACTIVE_KEY) &&
- priv->ieee->sec.active_key != sec->active_key) {
- if (sec->active_key <= 3) {
- priv->ieee->sec.active_key = sec->active_key;
- priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
- } else
- priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
-
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- if ((sec->flags & SEC_AUTH_MODE) &&
- (priv->ieee->sec.auth_mode != sec->auth_mode)) {
- priv->ieee->sec.auth_mode = sec->auth_mode;
- priv->ieee->sec.flags |= SEC_AUTH_MODE;
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
- priv->ieee->sec.flags |= SEC_ENABLED;
- priv->ieee->sec.enabled = sec->enabled;
- priv->status |= STATUS_SECURITY_UPDATED;
- force_update = 1;
- }
-
- if (sec->flags & SEC_ENCRYPT)
- priv->ieee->sec.encrypt = sec->encrypt;
-
- if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
- priv->ieee->sec.level = sec->level;
- priv->ieee->sec.flags |= SEC_LEVEL;
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n",
- priv->ieee->sec.flags & (1 << 8) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 7) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 6) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 5) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 4) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 3) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 2) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 1) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 0) ? '1' : '0');
-
-/* As a temporary work around to enable WPA until we figure out why
- * wpa_supplicant toggles the security capability of the driver, which
- * forces a disassocation with force_update...
- *
- * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/
- if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
- ipw2100_configure_security(priv, 0);
- done:
- mutex_unlock(&priv->action_mutex);
-}
-
-static int ipw2100_adapter_setup(struct ipw2100_priv *priv)
-{
- int err;
- int batch_mode = 1;
- u8 *bssid;
-
- IPW_DEBUG_INFO("enter\n");
-
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
-#ifdef CONFIG_IPW2100_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- err = ipw2100_set_channel(priv, priv->channel, batch_mode);
- if (err)
- return err;
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
- }
-#endif /* CONFIG_IPW2100_MONITOR */
-
- err = ipw2100_read_mac_address(priv);
- if (err)
- return -EIO;
-
- err = ipw2100_set_mac_address(priv, batch_mode);
- if (err)
- return err;
-
- err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode);
- if (err)
- return err;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- err = ipw2100_set_channel(priv, priv->channel, batch_mode);
- if (err)
- return err;
- }
-
- err = ipw2100_system_config(priv, batch_mode);
- if (err)
- return err;
-
- err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode);
- if (err)
- return err;
-
- /* Default to power mode OFF */
- err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
- if (err)
- return err;
-
- err = ipw2100_set_rts_threshold(priv, priv->rts_threshold);
- if (err)
- return err;
-
- if (priv->config & CFG_STATIC_BSSID)
- bssid = priv->bssid;
- else
- bssid = NULL;
- err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode);
- if (err)
- return err;
-
- if (priv->config & CFG_STATIC_ESSID)
- err = ipw2100_set_essid(priv, priv->essid, priv->essid_len,
- batch_mode);
- else
- err = ipw2100_set_essid(priv, NULL, 0, batch_mode);
- if (err)
- return err;
-
- err = ipw2100_configure_security(priv, batch_mode);
- if (err)
- return err;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- err =
- ipw2100_set_ibss_beacon_interval(priv,
- priv->beacon_interval,
- batch_mode);
- if (err)
- return err;
-
- err = ipw2100_set_tx_power(priv, priv->tx_power);
- if (err)
- return err;
- }
-
- /*
- err = ipw2100_set_fragmentation_threshold(
- priv, priv->frag_threshold, batch_mode);
- if (err)
- return err;
- */
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-/*************************************************************************
- *
- * EXTERNALLY CALLED METHODS
- *
- *************************************************************************/
-
-/* This method is called by the network layer -- not to be confused with
- * ipw2100_set_mac_address() declared above called by this driver (and this
- * method as well) to talk to the firmware */
-static int ipw2100_set_address(struct net_device *dev, void *p)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct sockaddr *addr = p;
- int err = 0;
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
-
- mutex_lock(&priv->action_mutex);
-
- priv->config |= CFG_CUSTOM_MAC;
- memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
-
- err = ipw2100_set_mac_address(priv, 0);
- if (err)
- goto done;
-
- priv->reset_backoff = 0;
- mutex_unlock(&priv->action_mutex);
- ipw2100_reset_adapter(&priv->reset_work.work);
- return 0;
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_open(struct net_device *dev)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- unsigned long flags;
- IPW_DEBUG_INFO("dev->open\n");
-
- spin_lock_irqsave(&priv->low_lock, flags);
- if (priv->status & STATUS_ASSOCIATED) {
- netif_carrier_on(dev);
- netif_start_queue(dev);
- }
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- return 0;
-}
-
-static int ipw2100_close(struct net_device *dev)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- unsigned long flags;
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
-
- IPW_DEBUG_INFO("enter\n");
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (priv->status & STATUS_ASSOCIATED)
- netif_carrier_off(dev);
- netif_stop_queue(dev);
-
- /* Flush the TX queue ... */
- while (!list_empty(&priv->tx_pend_list)) {
- element = priv->tx_pend_list.next;
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- list_del(element);
- DEC_STAT(&priv->tx_pend_stat);
-
- libipw_txb_free(packet->info.d_struct.txb);
- packet->info.d_struct.txb = NULL;
-
- list_add_tail(element, &priv->tx_free_list);
- INC_STAT(&priv->tx_free_stat);
- }
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-/*
- * TODO: Fix this function... its just wrong
- */
-static void ipw2100_tx_timeout(struct net_device *dev)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- dev->stats.tx_errors++;
-
-#ifdef CONFIG_IPW2100_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR)
- return;
-#endif
-
- IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n",
- dev->name);
- schedule_reset(priv);
-}
-
-static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value)
-{
- /* This is called when wpa_supplicant loads and closes the driver
- * interface. */
- priv->ieee->wpa_enabled = value;
- return 0;
-}
-
-static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value)
-{
-
- struct libipw_device *ieee = priv->ieee;
- struct libipw_security sec = {
- .flags = SEC_AUTH_MODE,
- };
- int ret = 0;
-
- if (value & IW_AUTH_ALG_SHARED_KEY) {
- sec.auth_mode = WLAN_AUTH_SHARED_KEY;
- ieee->open_wep = 0;
- } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
- sec.auth_mode = WLAN_AUTH_OPEN;
- ieee->open_wep = 1;
- } else if (value & IW_AUTH_ALG_LEAP) {
- sec.auth_mode = WLAN_AUTH_LEAP;
- ieee->open_wep = 1;
- } else
- return -EINVAL;
-
- if (ieee->set_security)
- ieee->set_security(ieee->dev, &sec);
- else
- ret = -EOPNOTSUPP;
-
- return ret;
-}
-
-static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
- char *wpa_ie, int wpa_ie_len)
-{
-
- struct ipw2100_wpa_assoc_frame frame;
-
- frame.fixed_ie_mask = 0;
-
- /* copy WPA IE */
- memcpy(frame.var_ie, wpa_ie, wpa_ie_len);
- frame.var_ie_len = wpa_ie_len;
-
- /* make sure WPA is enabled */
- ipw2100_wpa_enable(priv, 1);
- ipw2100_set_wpa_ie(priv, &frame, 0);
-}
-
-static void ipw_ethtool_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- char fw_ver[64], ucode_ver[64];
-
- strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
- strlcpy(info->version, DRV_VERSION, sizeof(info->version));
-
- ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver));
- ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver));
-
- snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s",
- fw_ver, priv->eeprom_version, ucode_ver);
-
- strlcpy(info->bus_info, pci_name(priv->pci_dev),
- sizeof(info->bus_info));
-}
-
-static u32 ipw2100_ethtool_get_link(struct net_device *dev)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
-}
-
-static const struct ethtool_ops ipw2100_ethtool_ops = {
- .get_link = ipw2100_ethtool_get_link,
- .get_drvinfo = ipw_ethtool_get_drvinfo,
-};
-
-static void ipw2100_hang_check(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, hang_check.work);
- unsigned long flags;
- u32 rtc = 0xa5a5a5a5;
- u32 len = sizeof(rtc);
- int restart = 0;
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (priv->fatal_error != 0) {
- /* If fatal_error is set then we need to restart */
- IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n",
- priv->net_dev->name);
-
- restart = 1;
- } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) ||
- (rtc == priv->last_rtc)) {
- /* Check if firmware is hung */
- IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n",
- priv->net_dev->name);
-
- restart = 1;
- }
-
- if (restart) {
- /* Kill timer */
- priv->stop_hang_check = 1;
- priv->hangs++;
-
- /* Restart the NIC */
- schedule_reset(priv);
- }
-
- priv->last_rtc = rtc;
-
- if (!priv->stop_hang_check)
- schedule_delayed_work(&priv->hang_check, HZ / 2);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
-}
-
-static void ipw2100_rf_kill(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, rf_kill.work);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (rf_kill_active(priv)) {
- IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
- if (!priv->stop_rf_kill)
- schedule_delayed_work(&priv->rf_kill,
- round_jiffies_relative(HZ));
- goto exit_unlock;
- }
-
- /* RF Kill is now disabled, so bring the device back up */
-
- if (!(priv->status & STATUS_RF_KILL_MASK)) {
- IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
- "device\n");
- schedule_reset(priv);
- } else
- IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
- "enabled\n");
-
- exit_unlock:
- spin_unlock_irqrestore(&priv->low_lock, flags);
-}
-
-static void ipw2100_irq_tasklet(struct ipw2100_priv *priv);
-
-static const struct net_device_ops ipw2100_netdev_ops = {
- .ndo_open = ipw2100_open,
- .ndo_stop = ipw2100_close,
- .ndo_start_xmit = libipw_xmit,
- .ndo_change_mtu = libipw_change_mtu,
- .ndo_tx_timeout = ipw2100_tx_timeout,
- .ndo_set_mac_address = ipw2100_set_address,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-/* Look into using netdev destructor to shutdown libipw? */
-
-static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
- void __iomem * ioaddr)
-{
- struct ipw2100_priv *priv;
- struct net_device *dev;
-
- dev = alloc_libipw(sizeof(struct ipw2100_priv), 0);
- if (!dev)
- return NULL;
- priv = libipw_priv(dev);
- priv->ieee = netdev_priv(dev);
- priv->pci_dev = pci_dev;
- priv->net_dev = dev;
- priv->ioaddr = ioaddr;
-
- priv->ieee->hard_start_xmit = ipw2100_tx;
- priv->ieee->set_security = shim__set_security;
-
- priv->ieee->perfect_rssi = -20;
- priv->ieee->worst_rssi = -85;
-
- dev->netdev_ops = &ipw2100_netdev_ops;
- dev->ethtool_ops = &ipw2100_ethtool_ops;
- dev->wireless_handlers = &ipw2100_wx_handler_def;
- priv->wireless_data.libipw = priv->ieee;
- dev->wireless_data = &priv->wireless_data;
- dev->watchdog_timeo = 3 * HZ;
- dev->irq = 0;
-
- /* NOTE: We don't use the wireless_handlers hook
- * in dev as the system will start throwing WX requests
- * to us before we're actually initialized and it just
- * ends up causing problems. So, we just handle
- * the WX extensions through the ipw2100_ioctl interface */
-
- /* memset() puts everything to 0, so we only have explicitly set
- * those values that need to be something else */
-
- /* If power management is turned on, default to AUTO mode */
- priv->power_mode = IPW_POWER_AUTO;
-
-#ifdef CONFIG_IPW2100_MONITOR
- priv->config |= CFG_CRC_CHECK;
-#endif
- priv->ieee->wpa_enabled = 0;
- priv->ieee->drop_unencrypted = 0;
- priv->ieee->privacy_invoked = 0;
- priv->ieee->ieee802_1x = 1;
-
- /* Set module parameters */
- switch (network_mode) {
- case 1:
- priv->ieee->iw_mode = IW_MODE_ADHOC;
- break;
-#ifdef CONFIG_IPW2100_MONITOR
- case 2:
- priv->ieee->iw_mode = IW_MODE_MONITOR;
- break;
-#endif
- default:
- case 0:
- priv->ieee->iw_mode = IW_MODE_INFRA;
- break;
- }
-
- if (disable == 1)
- priv->status |= STATUS_RF_KILL_SW;
-
- if (channel != 0 &&
- ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) {
- priv->config |= CFG_STATIC_CHANNEL;
- priv->channel = channel;
- }
-
- if (associate)
- priv->config |= CFG_ASSOCIATE;
-
- priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
- priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
- priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
- priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED;
- priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED;
- priv->tx_power = IPW_TX_POWER_DEFAULT;
- priv->tx_rates = DEFAULT_TX_RATES;
-
- strcpy(priv->nick, "ipw2100");
-
- spin_lock_init(&priv->low_lock);
- mutex_init(&priv->action_mutex);
- mutex_init(&priv->adapter_mutex);
-
- init_waitqueue_head(&priv->wait_command_queue);
-
- netif_carrier_off(dev);
-
- INIT_LIST_HEAD(&priv->msg_free_list);
- INIT_LIST_HEAD(&priv->msg_pend_list);
- INIT_STAT(&priv->msg_free_stat);
- INIT_STAT(&priv->msg_pend_stat);
-
- INIT_LIST_HEAD(&priv->tx_free_list);
- INIT_LIST_HEAD(&priv->tx_pend_list);
- INIT_STAT(&priv->tx_free_stat);
- INIT_STAT(&priv->tx_pend_stat);
-
- INIT_LIST_HEAD(&priv->fw_pend_list);
- INIT_STAT(&priv->fw_pend_stat);
-
- INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter);
- INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work);
- INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work);
- INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check);
- INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill);
- INIT_DELAYED_WORK(&priv->scan_event, ipw2100_scan_event);
-
- tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
- ipw2100_irq_tasklet, (unsigned long)priv);
-
- /* NOTE: We do not start the deferred work for status checks yet */
- priv->stop_rf_kill = 1;
- priv->stop_hang_check = 1;
-
- return dev;
-}
-
-static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
- const struct pci_device_id *ent)
-{
- void __iomem *ioaddr;
- struct net_device *dev = NULL;
- struct ipw2100_priv *priv = NULL;
- int err = 0;
- int registered = 0;
- u32 val;
-
- IPW_DEBUG_INFO("enter\n");
-
- if (!(pci_resource_flags(pci_dev, 0) & IORESOURCE_MEM)) {
- IPW_DEBUG_INFO("weird - resource type is not memory\n");
- err = -ENODEV;
- goto out;
- }
-
- ioaddr = pci_iomap(pci_dev, 0, 0);
- if (!ioaddr) {
- printk(KERN_WARNING DRV_NAME
- "Error calling ioremap_nocache.\n");
- err = -EIO;
- goto fail;
- }
-
- /* allocate and initialize our net_device */
- dev = ipw2100_alloc_device(pci_dev, ioaddr);
- if (!dev) {
- printk(KERN_WARNING DRV_NAME
- "Error calling ipw2100_alloc_device.\n");
- err = -ENOMEM;
- goto fail;
- }
-
- /* set up PCI mappings for device */
- err = pci_enable_device(pci_dev);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling pci_enable_device.\n");
- return err;
- }
-
- priv = libipw_priv(dev);
-
- pci_set_master(pci_dev);
- pci_set_drvdata(pci_dev, priv);
-
- err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling pci_set_dma_mask.\n");
- pci_disable_device(pci_dev);
- return err;
- }
-
- err = pci_request_regions(pci_dev, DRV_NAME);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling pci_request_regions.\n");
- pci_disable_device(pci_dev);
- return err;
- }
-
- /* We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state */
- pci_read_config_dword(pci_dev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
-
- if (!ipw2100_hw_is_adapter_in_system(dev)) {
- printk(KERN_WARNING DRV_NAME
- "Device not found via register read.\n");
- err = -ENODEV;
- goto fail;
- }
-
- SET_NETDEV_DEV(dev, &pci_dev->dev);
-
- /* Force interrupts to be shut off on the device */
- priv->status |= STATUS_INT_ENABLED;
- ipw2100_disable_interrupts(priv);
-
- /* Allocate and initialize the Tx/Rx queues and lists */
- if (ipw2100_queues_allocate(priv)) {
- printk(KERN_WARNING DRV_NAME
- "Error calling ipw2100_queues_allocate.\n");
- err = -ENOMEM;
- goto fail;
- }
- ipw2100_queues_initialize(priv);
-
- err = request_irq(pci_dev->irq,
- ipw2100_interrupt, IRQF_SHARED, dev->name, priv);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling request_irq: %d.\n", pci_dev->irq);
- goto fail;
- }
- dev->irq = pci_dev->irq;
-
- IPW_DEBUG_INFO("Attempting to register device...\n");
-
- printk(KERN_INFO DRV_NAME
- ": Detected Intel PRO/Wireless 2100 Network Connection\n");
-
- err = ipw2100_up(priv, 1);
- if (err)
- goto fail;
-
- err = ipw2100_wdev_init(dev);
- if (err)
- goto fail;
- registered = 1;
-
- /* Bring up the interface. Pre 0.46, after we registered the
- * network device we would call ipw2100_up. This introduced a race
- * condition with newer hotplug configurations (network was coming
- * up and making calls before the device was initialized).
- */
- err = register_netdev(dev);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling register_netdev.\n");
- goto fail;
- }
- registered = 2;
-
- mutex_lock(&priv->action_mutex);
-
- IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
-
- /* perform this after register_netdev so that dev->name is set */
- err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
- if (err)
- goto fail_unlock;
-
- /* If the RF Kill switch is disabled, go ahead and complete the
- * startup sequence */
- if (!(priv->status & STATUS_RF_KILL_MASK)) {
- /* Enable the adapter - sends HOST_COMPLETE */
- if (ipw2100_enable_adapter(priv)) {
- printk(KERN_WARNING DRV_NAME
- ": %s: failed in call to enable adapter.\n",
- priv->net_dev->name);
- ipw2100_hw_stop_adapter(priv);
- err = -EIO;
- goto fail_unlock;
- }
-
- /* Start a scan . . . */
- ipw2100_set_scan_options(priv);
- ipw2100_start_scan(priv);
- }
-
- IPW_DEBUG_INFO("exit\n");
-
- priv->status |= STATUS_INITIALIZED;
-
- mutex_unlock(&priv->action_mutex);
-out:
- return err;
-
- fail_unlock:
- mutex_unlock(&priv->action_mutex);
- fail:
- if (dev) {
- if (registered >= 2)
- unregister_netdev(dev);
-
- if (registered) {
- wiphy_unregister(priv->ieee->wdev.wiphy);
- kfree(priv->ieee->bg_band.channels);
- }
-
- ipw2100_hw_stop_adapter(priv);
-
- ipw2100_disable_interrupts(priv);
-
- if (dev->irq)
- free_irq(dev->irq, priv);
-
- ipw2100_kill_works(priv);
-
- /* These are safe to call even if they weren't allocated */
- ipw2100_queues_free(priv);
- sysfs_remove_group(&pci_dev->dev.kobj,
- &ipw2100_attribute_group);
-
- free_libipw(dev, 0);
- }
-
- pci_iounmap(pci_dev, ioaddr);
-
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
- goto out;
-}
-
-static void ipw2100_pci_remove_one(struct pci_dev *pci_dev)
-{
- struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
- struct net_device *dev = priv->net_dev;
-
- mutex_lock(&priv->action_mutex);
-
- priv->status &= ~STATUS_INITIALIZED;
-
- sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
-
-#ifdef CONFIG_PM
- if (ipw2100_firmware.version)
- ipw2100_release_firmware(priv, &ipw2100_firmware);
-#endif
- /* Take down the hardware */
- ipw2100_down(priv);
-
- /* Release the mutex so that the network subsystem can
- * complete any needed calls into the driver... */
- mutex_unlock(&priv->action_mutex);
-
- /* Unregister the device first - this results in close()
- * being called if the device is open. If we free storage
- * first, then close() will crash.
- * FIXME: remove the comment above. */
- unregister_netdev(dev);
-
- ipw2100_kill_works(priv);
-
- ipw2100_queues_free(priv);
-
- /* Free potential debugging firmware snapshot */
- ipw2100_snapshot_free(priv);
-
- free_irq(dev->irq, priv);
-
- pci_iounmap(pci_dev, priv->ioaddr);
-
- /* wiphy_unregister needs to be here, before free_libipw */
- wiphy_unregister(priv->ieee->wdev.wiphy);
- kfree(priv->ieee->bg_band.channels);
- free_libipw(dev, 0);
-
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-#ifdef CONFIG_PM
-static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
-{
- struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
- struct net_device *dev = priv->net_dev;
-
- IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name);
-
- mutex_lock(&priv->action_mutex);
- if (priv->status & STATUS_INITIALIZED) {
- /* Take down the device; powers it off, etc. */
- ipw2100_down(priv);
- }
-
- /* Remove the PRESENT state of the device */
- netif_device_detach(dev);
-
- pci_save_state(pci_dev);
- pci_disable_device(pci_dev);
- pci_set_power_state(pci_dev, PCI_D3hot);
-
- priv->suspend_at = get_seconds();
-
- mutex_unlock(&priv->action_mutex);
-
- return 0;
-}
-
-static int ipw2100_resume(struct pci_dev *pci_dev)
-{
- struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
- struct net_device *dev = priv->net_dev;
- int err;
- u32 val;
-
- if (IPW2100_PM_DISABLED)
- return 0;
-
- mutex_lock(&priv->action_mutex);
-
- IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name);
-
- pci_set_power_state(pci_dev, PCI_D0);
- err = pci_enable_device(pci_dev);
- if (err) {
- printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
- dev->name);
- mutex_unlock(&priv->action_mutex);
- return err;
- }
- pci_restore_state(pci_dev);
-
- /*
- * 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_restore_state won't help
- * here since it only restores the first 64 bytes pci config header.
- */
- pci_read_config_dword(pci_dev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
-
- /* Set the device back into the PRESENT state; this will also wake
- * the queue of needed */
- netif_device_attach(dev);
-
- priv->suspend_time = get_seconds() - priv->suspend_at;
-
- /* Bring the device back up */
- if (!(priv->status & STATUS_RF_KILL_SW))
- ipw2100_up(priv, 0);
-
- mutex_unlock(&priv->action_mutex);
-
- return 0;
-}
-#endif
-
-static void ipw2100_shutdown(struct pci_dev *pci_dev)
-{
- struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
-
- /* Take down the device; powers it off, etc. */
- ipw2100_down(priv);
-
- pci_disable_device(pci_dev);
-}
-
-#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
-
-static const struct pci_device_id ipw2100_pci_id_table[] = {
- IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */
- IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */
- IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */
- IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */
-
- IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */
-
- IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */
- {0,},
-};
-
-MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table);
-
-static struct pci_driver ipw2100_pci_driver = {
- .name = DRV_NAME,
- .id_table = ipw2100_pci_id_table,
- .probe = ipw2100_pci_init_one,
- .remove = ipw2100_pci_remove_one,
-#ifdef CONFIG_PM
- .suspend = ipw2100_suspend,
- .resume = ipw2100_resume,
-#endif
- .shutdown = ipw2100_shutdown,
-};
-
-/**
- * Initialize the ipw2100 driver/module
- *
- * @returns 0 if ok, < 0 errno node con error.
- *
- * Note: we cannot init the /proc stuff until the PCI driver is there,
- * or we risk an unlikely race condition on someone accessing
- * uninitialized data in the PCI dev struct through /proc.
- */
-static int __init ipw2100_init(void)
-{
- int ret;
-
- printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
- printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
-
- pm_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
-
- ret = pci_register_driver(&ipw2100_pci_driver);
- if (ret)
- goto out;
-
-#ifdef CONFIG_IPW2100_DEBUG
- ipw2100_debug_level = debug;
- ret = driver_create_file(&ipw2100_pci_driver.driver,
- &driver_attr_debug_level);
-#endif
-
-out:
- return ret;
-}
-
-/**
- * Cleanup ipw2100 driver registration
- */
-static void __exit ipw2100_exit(void)
-{
- /* FIXME: IPG: check that we have no instances of the devices open */
-#ifdef CONFIG_IPW2100_DEBUG
- driver_remove_file(&ipw2100_pci_driver.driver,
- &driver_attr_debug_level);
-#endif
- pci_unregister_driver(&ipw2100_pci_driver);
- pm_qos_remove_request(&ipw2100_pm_qos_req);
-}
-
-module_init(ipw2100_init);
-module_exit(ipw2100_exit);
-
-static int ipw2100_wx_get_name(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- if (!(priv->status & STATUS_ASSOCIATED))
- strcpy(wrqu->name, "unassociated");
- else
- snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
-
- IPW_DEBUG_WX("Name: %s\n", wrqu->name);
- return 0;
-}
-
-static int ipw2100_wx_set_freq(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct iw_freq *fwrq = &wrqu->freq;
- int err = 0;
-
- if (priv->ieee->iw_mode == IW_MODE_INFRA)
- return -EOPNOTSUPP;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- /* if setting by freq convert to channel */
- if (fwrq->e == 1) {
- if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) {
- int f = fwrq->m / 100000;
- int c = 0;
-
- while ((c < REG_MAX_CHANNEL) &&
- (f != ipw2100_frequencies[c]))
- c++;
-
- /* hack to fall through */
- fwrq->e = 0;
- fwrq->m = c + 1;
- }
- }
-
- if (fwrq->e > 0 || fwrq->m > 1000) {
- err = -EOPNOTSUPP;
- goto done;
- } else { /* Set the channel */
- IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m);
- err = ipw2100_set_channel(priv, fwrq->m, 0);
- }
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_freq(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- wrqu->freq.e = 0;
-
- /* If we are associated, trying to associate, or have a statically
- * configured CHANNEL then return that; otherwise return ANY */
- if (priv->config & CFG_STATIC_CHANNEL ||
- priv->status & STATUS_ASSOCIATED)
- wrqu->freq.m = priv->channel;
- else
- wrqu->freq.m = 0;
-
- IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel);
- return 0;
-
-}
-
-static int ipw2100_wx_set_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err = 0;
-
- IPW_DEBUG_WX("SET Mode -> %d\n", wrqu->mode);
-
- if (wrqu->mode == priv->ieee->iw_mode)
- return 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- switch (wrqu->mode) {
-#ifdef CONFIG_IPW2100_MONITOR
- case IW_MODE_MONITOR:
- err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
- break;
-#endif /* CONFIG_IPW2100_MONITOR */
- case IW_MODE_ADHOC:
- err = ipw2100_switch_mode(priv, IW_MODE_ADHOC);
- break;
- case IW_MODE_INFRA:
- case IW_MODE_AUTO:
- default:
- err = ipw2100_switch_mode(priv, IW_MODE_INFRA);
- break;
- }
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- wrqu->mode = priv->ieee->iw_mode;
- IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode);
-
- return 0;
-}
-
-#define POWER_MODES 5
-
-/* Values are in microsecond */
-static const s32 timeout_duration[POWER_MODES] = {
- 350000,
- 250000,
- 75000,
- 37000,
- 25000,
-};
-
-static const s32 period_duration[POWER_MODES] = {
- 400000,
- 700000,
- 1000000,
- 1000000,
- 1000000
-};
-
-static int ipw2100_wx_get_range(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct iw_range *range = (struct iw_range *)extra;
- u16 val;
- int i, level;
-
- wrqu->data.length = sizeof(*range);
- memset(range, 0, sizeof(*range));
-
- /* Let's try to keep this struct in the same order as in
- * linux/include/wireless.h
- */
-
- /* TODO: See what values we can set, and remove the ones we can't
- * set, or fill them with some default data.
- */
-
- /* ~5 Mb/s real (802.11b) */
- range->throughput = 5 * 1000 * 1000;
-
-// range->sensitivity; /* signal level threshold range */
-
- range->max_qual.qual = 100;
- /* TODO: Find real max RSSI and stick here */
- range->max_qual.level = 0;
- range->max_qual.noise = 0;
- range->max_qual.updated = 7; /* Updated all three */
-
- range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
- /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
- range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
- range->avg_qual.noise = 0;
- range->avg_qual.updated = 7; /* Updated all three */
-
- range->num_bitrates = RATE_COUNT;
-
- for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) {
- range->bitrate[i] = ipw2100_bg_rates[i].bitrate * 100 * 1000;
- }
-
- range->min_rts = MIN_RTS_THRESHOLD;
- range->max_rts = MAX_RTS_THRESHOLD;
- range->min_frag = MIN_FRAG_THRESHOLD;
- range->max_frag = MAX_FRAG_THRESHOLD;
-
- range->min_pmp = period_duration[0]; /* Minimal PM period */
- range->max_pmp = period_duration[POWER_MODES - 1]; /* Maximal PM period */
- range->min_pmt = timeout_duration[POWER_MODES - 1]; /* Minimal PM timeout */
- range->max_pmt = timeout_duration[0]; /* Maximal PM timeout */
-
- /* How to decode max/min PM period */
- range->pmp_flags = IW_POWER_PERIOD;
- /* How to decode max/min PM period */
- range->pmt_flags = IW_POWER_TIMEOUT;
- /* What PM options are supported */
- range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD;
-
- range->encoding_size[0] = 5;
- range->encoding_size[1] = 13; /* Different token sizes */
- range->num_encoding_sizes = 2; /* Number of entry in the list */
- range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */
-// range->encoding_login_index; /* token index for login token */
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- range->txpower_capa = IW_TXPOW_DBM;
- range->num_txpower = IW_MAX_TXPOWER;
- for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16);
- i < IW_MAX_TXPOWER;
- i++, level -=
- ((IPW_TX_POWER_MAX_DBM -
- IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1))
- range->txpower[i] = level / 16;
- } else {
- range->txpower_capa = 0;
- range->num_txpower = 0;
- }
-
- /* Set the Wireless Extension versions */
- range->we_version_compiled = WIRELESS_EXT;
- range->we_version_source = 18;
-
-// range->retry_capa; /* What retry options are supported */
-// range->retry_flags; /* How to decode max/min retry limit */
-// range->r_time_flags; /* How to decode max/min retry life */
-// range->min_retry; /* Minimal number of retries */
-// range->max_retry; /* Maximal number of retries */
-// range->min_r_time; /* Minimal retry lifetime */
-// range->max_r_time; /* Maximal retry lifetime */
-
- range->num_channels = FREQ_COUNT;
-
- val = 0;
- for (i = 0; i < FREQ_COUNT; i++) {
- // TODO: Include only legal frequencies for some countries
-// if (local->channel_mask & (1 << i)) {
- range->freq[val].i = i + 1;
- range->freq[val].m = ipw2100_frequencies[i] * 100000;
- range->freq[val].e = 1;
- val++;
-// }
- if (val == IW_MAX_FREQUENCIES)
- break;
- }
- range->num_frequency = val;
-
- /* Event capability (kernel + driver) */
- range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
- IW_EVENT_CAPA_MASK(SIOCGIWAP));
- range->event_capa[1] = IW_EVENT_CAPA_K_1;
-
- range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
- IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
- IPW_DEBUG_WX("GET Range\n");
-
- return 0;
-}
-
-static int ipw2100_wx_set_wap(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err = 0;
-
- // sanity checks
- if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
- return -EINVAL;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) ||
- is_zero_ether_addr(wrqu->ap_addr.sa_data)) {
- /* we disable mandatory BSSID association */
- IPW_DEBUG_WX("exit - disable mandatory BSSID\n");
- priv->config &= ~CFG_STATIC_BSSID;
- err = ipw2100_set_mandatory_bssid(priv, NULL, 0);
- goto done;
- }
-
- priv->config |= CFG_STATIC_BSSID;
- memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN);
-
- err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0);
-
- IPW_DEBUG_WX("SET BSSID -> %pM\n", wrqu->ap_addr.sa_data);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_wap(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- /* If we are associated, trying to associate, or have a statically
- * configured BSSID then return that; otherwise return ANY */
- if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) {
- wrqu->ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
- } else
- eth_zero_addr(wrqu->ap_addr.sa_data);
-
- IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data);
- return 0;
-}
-
-static int ipw2100_wx_set_essid(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- char *essid = ""; /* ANY */
- int length = 0;
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->essid.flags && wrqu->essid.length) {
- length = wrqu->essid.length;
- essid = extra;
- }
-
- if (length == 0) {
- IPW_DEBUG_WX("Setting ESSID to ANY\n");
- priv->config &= ~CFG_STATIC_ESSID;
- err = ipw2100_set_essid(priv, NULL, 0, 0);
- goto done;
- }
-
- length = min(length, IW_ESSID_MAX_SIZE);
-
- priv->config |= CFG_STATIC_ESSID;
-
- if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
- IPW_DEBUG_WX("ESSID set to current ESSID.\n");
- err = 0;
- goto done;
- }
-
- IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, essid, length);
-
- priv->essid_len = length;
- memcpy(priv->essid, essid, priv->essid_len);
-
- err = ipw2100_set_essid(priv, essid, length, 0);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_essid(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- /* If we are associated, trying to associate, or have a statically
- * configured ESSID then return that; otherwise return ANY */
- if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) {
- IPW_DEBUG_WX("Getting essid: '%*pE'\n",
- priv->essid_len, priv->essid);
- memcpy(extra, priv->essid, priv->essid_len);
- wrqu->essid.length = priv->essid_len;
- wrqu->essid.flags = 1; /* active */
- } else {
- IPW_DEBUG_WX("Getting essid: ANY\n");
- wrqu->essid.length = 0;
- wrqu->essid.flags = 0; /* active */
- }
-
- return 0;
-}
-
-static int ipw2100_wx_set_nick(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- if (wrqu->data.length > IW_ESSID_MAX_SIZE)
- return -E2BIG;
-
- wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
- memset(priv->nick, 0, sizeof(priv->nick));
- memcpy(priv->nick, extra, wrqu->data.length);
-
- IPW_DEBUG_WX("SET Nickname -> %s\n", priv->nick);
-
- return 0;
-}
-
-static int ipw2100_wx_get_nick(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- wrqu->data.length = strlen(priv->nick);
- memcpy(extra, priv->nick, wrqu->data.length);
- wrqu->data.flags = 1; /* active */
-
- IPW_DEBUG_WX("GET Nickname -> %s\n", extra);
-
- return 0;
-}
-
-static int ipw2100_wx_set_rate(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- u32 target_rate = wrqu->bitrate.value;
- u32 rate;
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- rate = 0;
-
- if (target_rate == 1000000 ||
- (!wrqu->bitrate.fixed && target_rate > 1000000))
- rate |= TX_RATE_1_MBIT;
- if (target_rate == 2000000 ||
- (!wrqu->bitrate.fixed && target_rate > 2000000))
- rate |= TX_RATE_2_MBIT;
- if (target_rate == 5500000 ||
- (!wrqu->bitrate.fixed && target_rate > 5500000))
- rate |= TX_RATE_5_5_MBIT;
- if (target_rate == 11000000 ||
- (!wrqu->bitrate.fixed && target_rate > 11000000))
- rate |= TX_RATE_11_MBIT;
- if (rate == 0)
- rate = DEFAULT_TX_RATES;
-
- err = ipw2100_set_tx_rates(priv, rate, 0);
-
- IPW_DEBUG_WX("SET Rate -> %04X\n", rate);
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_rate(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int val;
- unsigned int len = sizeof(val);
- int err = 0;
-
- if (!(priv->status & STATUS_ENABLED) ||
- priv->status & STATUS_RF_KILL_MASK ||
- !(priv->status & STATUS_ASSOCIATED)) {
- wrqu->bitrate.value = 0;
- return 0;
- }
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len);
- if (err) {
- IPW_DEBUG_WX("failed querying ordinals.\n");
- goto done;
- }
-
- switch (val & TX_RATE_MASK) {
- case TX_RATE_1_MBIT:
- wrqu->bitrate.value = 1000000;
- break;
- case TX_RATE_2_MBIT:
- wrqu->bitrate.value = 2000000;
- break;
- case TX_RATE_5_5_MBIT:
- wrqu->bitrate.value = 5500000;
- break;
- case TX_RATE_11_MBIT:
- wrqu->bitrate.value = 11000000;
- break;
- default:
- wrqu->bitrate.value = 0;
- }
-
- IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_set_rts(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int value, err;
-
- /* Auto RTS not yet supported */
- if (wrqu->rts.fixed == 0)
- return -EINVAL;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->rts.disabled)
- value = priv->rts_threshold | RTS_DISABLED;
- else {
- if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) {
- err = -EINVAL;
- goto done;
- }
- value = wrqu->rts.value;
- }
-
- err = ipw2100_set_rts_threshold(priv, value);
-
- IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X\n", value);
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_rts(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED;
- wrqu->rts.fixed = 1; /* no auto select */
-
- /* If RTS is set to the default value, then it is disabled */
- wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0;
-
- IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X\n", wrqu->rts.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_txpow(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err = 0, value;
-
- if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled))
- return -EINPROGRESS;
-
- if (priv->ieee->iw_mode != IW_MODE_ADHOC)
- return 0;
-
- if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
- return -EINVAL;
-
- if (wrqu->txpower.fixed == 0)
- value = IPW_TX_POWER_DEFAULT;
- else {
- if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM ||
- wrqu->txpower.value > IPW_TX_POWER_MAX_DBM)
- return -EINVAL;
-
- value = wrqu->txpower.value;
- }
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- err = ipw2100_set_tx_power(priv, value);
-
- IPW_DEBUG_WX("SET TX Power -> %d\n", value);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_txpow(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
-
- if (priv->tx_power == IPW_TX_POWER_DEFAULT) {
- wrqu->txpower.fixed = 0;
- wrqu->txpower.value = IPW_TX_POWER_MAX_DBM;
- } else {
- wrqu->txpower.fixed = 1;
- wrqu->txpower.value = priv->tx_power;
- }
-
- wrqu->txpower.flags = IW_TXPOW_DBM;
-
- IPW_DEBUG_WX("GET TX Power -> %d\n", wrqu->txpower.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_frag(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- if (!wrqu->frag.fixed)
- return -EINVAL;
-
- if (wrqu->frag.disabled) {
- priv->frag_threshold |= FRAG_DISABLED;
- priv->ieee->fts = DEFAULT_FTS;
- } else {
- if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
- wrqu->frag.value > MAX_FRAG_THRESHOLD)
- return -EINVAL;
-
- priv->ieee->fts = wrqu->frag.value & ~0x1;
- priv->frag_threshold = priv->ieee->fts;
- }
-
- IPW_DEBUG_WX("SET Frag Threshold -> %d\n", priv->ieee->fts);
-
- return 0;
-}
-
-static int ipw2100_wx_get_frag(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED;
- wrqu->frag.fixed = 0; /* no auto select */
- wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0;
-
- IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_retry(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err = 0;
-
- if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
- return -EINVAL;
-
- if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
- return 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->retry.flags & IW_RETRY_SHORT) {
- err = ipw2100_set_short_retry(priv, wrqu->retry.value);
- IPW_DEBUG_WX("SET Short Retry Limit -> %d\n",
- wrqu->retry.value);
- goto done;
- }
-
- if (wrqu->retry.flags & IW_RETRY_LONG) {
- err = ipw2100_set_long_retry(priv, wrqu->retry.value);
- IPW_DEBUG_WX("SET Long Retry Limit -> %d\n",
- wrqu->retry.value);
- goto done;
- }
-
- err = ipw2100_set_short_retry(priv, wrqu->retry.value);
- if (!err)
- err = ipw2100_set_long_retry(priv, wrqu->retry.value);
-
- IPW_DEBUG_WX("SET Both Retry Limits -> %d\n", wrqu->retry.value);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_retry(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- wrqu->retry.disabled = 0; /* can't be disabled */
-
- if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
- return -EINVAL;
-
- if (wrqu->retry.flags & IW_RETRY_LONG) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- wrqu->retry.value = priv->long_retry_limit;
- } else {
- wrqu->retry.flags =
- (priv->short_retry_limit !=
- priv->long_retry_limit) ?
- IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
-
- wrqu->retry.value = priv->short_retry_limit;
- }
-
- IPW_DEBUG_WX("GET Retry -> %d\n", wrqu->retry.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_scan(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- IPW_DEBUG_WX("Initiating scan...\n");
-
- priv->user_requested_scan = 1;
- if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) {
- IPW_DEBUG_WX("Start scan failed.\n");
-
- /* TODO: Mark a scan as pending so when hardware initialized
- * a scan starts */
- }
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_scan(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- return libipw_wx_get_scan(priv->ieee, info, wrqu, extra);
-}
-
-/*
- * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c
- */
-static int ipw2100_wx_set_encode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *key)
-{
- /*
- * No check of STATUS_INITIALIZED required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- return libipw_wx_set_encode(priv->ieee, info, wrqu, key);
-}
-
-static int ipw2100_wx_get_encode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *key)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- return libipw_wx_get_encode(priv->ieee, info, wrqu, key);
-}
-
-static int ipw2100_wx_set_power(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->power.disabled) {
- priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
- err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
- IPW_DEBUG_WX("SET Power Management Mode -> off\n");
- goto done;
- }
-
- switch (wrqu->power.flags & IW_POWER_MODE) {
- case IW_POWER_ON: /* If not specified */
- case IW_POWER_MODE: /* If set all mask */
- case IW_POWER_ALL_R: /* If explicitly state all */
- break;
- default: /* Otherwise we don't support it */
- IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
- wrqu->power.flags);
- err = -EOPNOTSUPP;
- goto done;
- }
-
- /* If the user hasn't specified a power management mode yet, default
- * to BATTERY */
- priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
- err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
-
- IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-
-}
-
-static int ipw2100_wx_get_power(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- if (!(priv->power_mode & IPW_POWER_ENABLED))
- wrqu->power.disabled = 1;
- else {
- wrqu->power.disabled = 0;
- wrqu->power.flags = 0;
- }
-
- IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
-
- return 0;
-}
-
-/*
- * WE-18 WPA support
- */
-
-/* SIOCSIWGENIE */
-static int ipw2100_wx_set_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct libipw_device *ieee = priv->ieee;
- u8 *buf;
-
- if (!ieee->wpa_enabled)
- return -EOPNOTSUPP;
-
- if (wrqu->data.length > MAX_WPA_IE_LEN ||
- (wrqu->data.length && extra == NULL))
- return -EINVAL;
-
- if (wrqu->data.length) {
- buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = buf;
- ieee->wpa_ie_len = wrqu->data.length;
- } else {
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = NULL;
- ieee->wpa_ie_len = 0;
- }
-
- ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
-
- return 0;
-}
-
-/* SIOCGIWGENIE */
-static int ipw2100_wx_get_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct libipw_device *ieee = priv->ieee;
-
- if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
- wrqu->data.length = 0;
- return 0;
- }
-
- if (wrqu->data.length < ieee->wpa_ie_len)
- return -E2BIG;
-
- wrqu->data.length = ieee->wpa_ie_len;
- memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
-
- return 0;
-}
-
-/* SIOCSIWAUTH */
-static int ipw2100_wx_set_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct libipw_device *ieee = priv->ieee;
- struct iw_param *param = &wrqu->param;
- struct lib80211_crypt_data *crypt;
- unsigned long flags;
- int ret = 0;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- /*
- * ipw2200 does not use these parameters
- */
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
- if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
- break;
-
- flags = crypt->ops->get_flags(crypt->priv);
-
- if (param->value)
- flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
- else
- flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
-
- crypt->ops->set_flags(flags, crypt->priv);
-
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:{
- /* HACK:
- *
- * wpa_supplicant calls set_wpa_enabled when the driver
- * is loaded and unloaded, regardless of if WPA is being
- * used. No other calls are made which can be used to
- * determine if encryption will be used or not prior to
- * association being expected. If encryption is not being
- * used, drop_unencrypted is set to false, else true -- we
- * can use this to determine if the CAP_PRIVACY_ON bit should
- * be set.
- */
- struct libipw_security sec = {
- .flags = SEC_ENABLED,
- .enabled = param->value,
- };
- priv->ieee->drop_unencrypted = param->value;
- /* We only change SEC_LEVEL for open mode. Others
- * are set by ipw_wpa_set_encryption.
- */
- if (!param->value) {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_0;
- } else {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_1;
- }
- if (priv->ieee->set_security)
- priv->ieee->set_security(priv->ieee->dev, &sec);
- break;
- }
-
- case IW_AUTH_80211_AUTH_ALG:
- ret = ipw2100_wpa_set_auth_algs(priv, param->value);
- break;
-
- case IW_AUTH_WPA_ENABLED:
- ret = ipw2100_wpa_enable(priv, param->value);
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- ieee->ieee802_1x = param->value;
- break;
-
- //case IW_AUTH_ROAMING_CONTROL:
- case IW_AUTH_PRIVACY_INVOKED:
- ieee->privacy_invoked = param->value;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return ret;
-}
-
-/* SIOCGIWAUTH */
-static int ipw2100_wx_get_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct libipw_device *ieee = priv->ieee;
- struct lib80211_crypt_data *crypt;
- struct iw_param *param = &wrqu->param;
- int ret = 0;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- /*
- * wpa_supplicant will control these internally
- */
- ret = -EOPNOTSUPP;
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
- if (!crypt || !crypt->ops->get_flags) {
- IPW_DEBUG_WARNING("Can't get TKIP countermeasures: "
- "crypt not set!\n");
- break;
- }
-
- param->value = (crypt->ops->get_flags(crypt->priv) &
- IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
-
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:
- param->value = ieee->drop_unencrypted;
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- param->value = priv->ieee->sec.auth_mode;
- break;
-
- case IW_AUTH_WPA_ENABLED:
- param->value = ieee->wpa_enabled;
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- param->value = ieee->ieee802_1x;
- break;
-
- case IW_AUTH_ROAMING_CONTROL:
- case IW_AUTH_PRIVACY_INVOKED:
- param->value = ieee->privacy_invoked;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-/* SIOCSIWENCODEEXT */
-static int ipw2100_wx_set_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra);
-}
-
-/* SIOCGIWENCODEEXT */
-static int ipw2100_wx_get_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra);
-}
-
-/* SIOCSIWMLME */
-static int ipw2100_wx_set_mlme(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct iw_mlme *mlme = (struct iw_mlme *)extra;
- __le16 reason;
-
- reason = cpu_to_le16(mlme->reason_code);
-
- switch (mlme->cmd) {
- case IW_MLME_DEAUTH:
- // silently ignore
- break;
-
- case IW_MLME_DISASSOC:
- ipw2100_disassociate_bssid(priv);
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-/*
- *
- * IWPRIV handlers
- *
- */
-#ifdef CONFIG_IPW2100_MONITOR
-static int ipw2100_wx_set_promisc(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int *parms = (int *)extra;
- int enable = (parms[0] > 0);
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (enable) {
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- err = ipw2100_set_channel(priv, parms[1], 0);
- goto done;
- }
- priv->channel = parms[1];
- err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
- } else {
- if (priv->ieee->iw_mode == IW_MODE_MONITOR)
- err = ipw2100_switch_mode(priv, priv->last_mode);
- }
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_reset(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- if (priv->status & STATUS_INITIALIZED)
- schedule_reset(priv);
- return 0;
-}
-
-#endif
-
-static int ipw2100_wx_set_powermode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err = 0, mode = *(int *)extra;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if ((mode < 0) || (mode > POWER_MODES))
- mode = IPW_POWER_AUTO;
-
- if (IPW_POWER_LEVEL(priv->power_mode) != mode)
- err = ipw2100_set_power_mode(priv, mode);
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-#define MAX_POWER_STRING 80
-static int ipw2100_wx_get_powermode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- int level = IPW_POWER_LEVEL(priv->power_mode);
- s32 timeout, period;
-
- if (!(priv->power_mode & IPW_POWER_ENABLED)) {
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d (Off)", level);
- } else {
- switch (level) {
- case IPW_POWER_MODE_CAM:
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d (None)", level);
- break;
- case IPW_POWER_AUTO:
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d (Auto)", level);
- break;
- default:
- timeout = timeout_duration[level - 1] / 1000;
- period = period_duration[level - 1] / 1000;
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d "
- "(Timeout %dms, Period %dms)",
- level, timeout, period);
- }
- }
-
- wrqu->data.length = strlen(extra) + 1;
-
- return 0;
-}
-
-static int ipw2100_wx_set_preamble(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err, mode = *(int *)extra;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (mode == 1)
- priv->config |= CFG_LONG_PREAMBLE;
- else if (mode == 0)
- priv->config &= ~CFG_LONG_PREAMBLE;
- else {
- err = -EINVAL;
- goto done;
- }
-
- err = ipw2100_system_config(priv, 0);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_preamble(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- if (priv->config & CFG_LONG_PREAMBLE)
- snprintf(wrqu->name, IFNAMSIZ, "long (1)");
- else
- snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
-
- return 0;
-}
-
-#ifdef CONFIG_IPW2100_MONITOR
-static int ipw2100_wx_set_crc_check(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- int err, mode = *(int *)extra;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (mode == 1)
- priv->config |= CFG_CRC_CHECK;
- else if (mode == 0)
- priv->config &= ~CFG_CRC_CHECK;
- else {
- err = -EINVAL;
- goto done;
- }
- err = 0;
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_crc_check(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = libipw_priv(dev);
-
- if (priv->config & CFG_CRC_CHECK)
- snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)");
- else
- snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)");
-
- return 0;
-}
-#endif /* CONFIG_IPW2100_MONITOR */
-
-static iw_handler ipw2100_wx_handlers[] = {
- IW_HANDLER(SIOCGIWNAME, ipw2100_wx_get_name),
- IW_HANDLER(SIOCSIWFREQ, ipw2100_wx_set_freq),
- IW_HANDLER(SIOCGIWFREQ, ipw2100_wx_get_freq),
- IW_HANDLER(SIOCSIWMODE, ipw2100_wx_set_mode),
- IW_HANDLER(SIOCGIWMODE, ipw2100_wx_get_mode),
- IW_HANDLER(SIOCGIWRANGE, ipw2100_wx_get_range),
- IW_HANDLER(SIOCSIWAP, ipw2100_wx_set_wap),
- IW_HANDLER(SIOCGIWAP, ipw2100_wx_get_wap),
- IW_HANDLER(SIOCSIWMLME, ipw2100_wx_set_mlme),
- IW_HANDLER(SIOCSIWSCAN, ipw2100_wx_set_scan),
- IW_HANDLER(SIOCGIWSCAN, ipw2100_wx_get_scan),
- IW_HANDLER(SIOCSIWESSID, ipw2100_wx_set_essid),
- IW_HANDLER(SIOCGIWESSID, ipw2100_wx_get_essid),
- IW_HANDLER(SIOCSIWNICKN, ipw2100_wx_set_nick),
- IW_HANDLER(SIOCGIWNICKN, ipw2100_wx_get_nick),
- IW_HANDLER(SIOCSIWRATE, ipw2100_wx_set_rate),
- IW_HANDLER(SIOCGIWRATE, ipw2100_wx_get_rate),
- IW_HANDLER(SIOCSIWRTS, ipw2100_wx_set_rts),
- IW_HANDLER(SIOCGIWRTS, ipw2100_wx_get_rts),
- IW_HANDLER(SIOCSIWFRAG, ipw2100_wx_set_frag),
- IW_HANDLER(SIOCGIWFRAG, ipw2100_wx_get_frag),
- IW_HANDLER(SIOCSIWTXPOW, ipw2100_wx_set_txpow),
- IW_HANDLER(SIOCGIWTXPOW, ipw2100_wx_get_txpow),
- IW_HANDLER(SIOCSIWRETRY, ipw2100_wx_set_retry),
- IW_HANDLER(SIOCGIWRETRY, ipw2100_wx_get_retry),
- IW_HANDLER(SIOCSIWENCODE, ipw2100_wx_set_encode),
- IW_HANDLER(SIOCGIWENCODE, ipw2100_wx_get_encode),
- IW_HANDLER(SIOCSIWPOWER, ipw2100_wx_set_power),
- IW_HANDLER(SIOCGIWPOWER, ipw2100_wx_get_power),
- IW_HANDLER(SIOCSIWGENIE, ipw2100_wx_set_genie),
- IW_HANDLER(SIOCGIWGENIE, ipw2100_wx_get_genie),
- IW_HANDLER(SIOCSIWAUTH, ipw2100_wx_set_auth),
- IW_HANDLER(SIOCGIWAUTH, ipw2100_wx_get_auth),
- IW_HANDLER(SIOCSIWENCODEEXT, ipw2100_wx_set_encodeext),
- IW_HANDLER(SIOCGIWENCODEEXT, ipw2100_wx_get_encodeext),
-};
-
-#define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV
-#define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1
-#define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2
-#define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3
-#define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4
-#define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5
-#define IPW2100_PRIV_SET_CRC_CHECK SIOCIWFIRSTPRIV+6
-#define IPW2100_PRIV_GET_CRC_CHECK SIOCIWFIRSTPRIV+7
-
-static const struct iw_priv_args ipw2100_private_args[] = {
-
-#ifdef CONFIG_IPW2100_MONITOR
- {
- IPW2100_PRIV_SET_MONITOR,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
- {
- IPW2100_PRIV_RESET,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
-#endif /* CONFIG_IPW2100_MONITOR */
-
- {
- IPW2100_PRIV_SET_POWER,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"},
- {
- IPW2100_PRIV_GET_POWER,
- 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING,
- "get_power"},
- {
- IPW2100_PRIV_SET_LONGPREAMBLE,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
- {
- IPW2100_PRIV_GET_LONGPREAMBLE,
- 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"},
-#ifdef CONFIG_IPW2100_MONITOR
- {
- IPW2100_PRIV_SET_CRC_CHECK,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"},
- {
- IPW2100_PRIV_GET_CRC_CHECK,
- 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"},
-#endif /* CONFIG_IPW2100_MONITOR */
-};
-
-static iw_handler ipw2100_private_handler[] = {
-#ifdef CONFIG_IPW2100_MONITOR
- ipw2100_wx_set_promisc,
- ipw2100_wx_reset,
-#else /* CONFIG_IPW2100_MONITOR */
- NULL,
- NULL,
-#endif /* CONFIG_IPW2100_MONITOR */
- ipw2100_wx_set_powermode,
- ipw2100_wx_get_powermode,
- ipw2100_wx_set_preamble,
- ipw2100_wx_get_preamble,
-#ifdef CONFIG_IPW2100_MONITOR
- ipw2100_wx_set_crc_check,
- ipw2100_wx_get_crc_check,
-#else /* CONFIG_IPW2100_MONITOR */
- NULL,
- NULL,
-#endif /* CONFIG_IPW2100_MONITOR */
-};
-
-/*
- * Get wireless statistics.
- * Called by /proc/net/wireless
- * Also called by SIOCGIWSTATS
- */
-static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev)
-{
- enum {
- POOR = 30,
- FAIR = 60,
- GOOD = 80,
- VERY_GOOD = 90,
- EXCELLENT = 95,
- PERFECT = 100
- };
- int rssi_qual;
- int tx_qual;
- int beacon_qual;
- int quality;
-
- struct ipw2100_priv *priv = libipw_priv(dev);
- struct iw_statistics *wstats;
- u32 rssi, tx_retries, missed_beacons, tx_failures;
- u32 ord_len = sizeof(u32);
-
- if (!priv)
- return (struct iw_statistics *)NULL;
-
- wstats = &priv->wstats;
-
- /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
- * ipw2100_wx_wireless_stats seems to be called before fw is
- * initialized. STATUS_ASSOCIATED will only be set if the hw is up
- * and associated; if not associcated, the values are all meaningless
- * anyway, so set them all to NULL and INVALID */
- if (!(priv->status & STATUS_ASSOCIATED)) {
- wstats->miss.beacon = 0;
- wstats->discard.retries = 0;
- wstats->qual.qual = 0;
- wstats->qual.level = 0;
- wstats->qual.noise = 0;
- wstats->qual.updated = 7;
- wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
- IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
- return wstats;
- }
-
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS,
- &missed_beacons, &ord_len))
- goto fail_get_ordinal;
-
- /* If we don't have a connection the quality and level is 0 */
- if (!(priv->status & STATUS_ASSOCIATED)) {
- wstats->qual.qual = 0;
- wstats->qual.level = 0;
- } else {
- if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR,
- &rssi, &ord_len))
- goto fail_get_ordinal;
- wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
- if (rssi < 10)
- rssi_qual = rssi * POOR / 10;
- else if (rssi < 15)
- rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR;
- else if (rssi < 20)
- rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR;
- else if (rssi < 30)
- rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) /
- 10 + GOOD;
- else
- rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) /
- 10 + VERY_GOOD;
-
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES,
- &tx_retries, &ord_len))
- goto fail_get_ordinal;
-
- if (tx_retries > 75)
- tx_qual = (90 - tx_retries) * POOR / 15;
- else if (tx_retries > 70)
- tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
- else if (tx_retries > 65)
- tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
- else if (tx_retries > 50)
- tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
- 15 + GOOD;
- else
- tx_qual = (50 - tx_retries) *
- (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
-
- if (missed_beacons > 50)
- beacon_qual = (60 - missed_beacons) * POOR / 10;
- else if (missed_beacons > 40)
- beacon_qual = (50 - missed_beacons) * (FAIR - POOR) /
- 10 + POOR;
- else if (missed_beacons > 32)
- beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) /
- 18 + FAIR;
- else if (missed_beacons > 20)
- beacon_qual = (32 - missed_beacons) *
- (VERY_GOOD - GOOD) / 20 + GOOD;
- else
- beacon_qual = (20 - missed_beacons) *
- (PERFECT - VERY_GOOD) / 20 + VERY_GOOD;
-
- quality = min(tx_qual, rssi_qual);
- quality = min(beacon_qual, quality);
-
-#ifdef CONFIG_IPW2100_DEBUG
- if (beacon_qual == quality)
- IPW_DEBUG_WX("Quality clamped by Missed Beacons\n");
- else if (tx_qual == quality)
- IPW_DEBUG_WX("Quality clamped by Tx Retries\n");
- else if (quality != 100)
- IPW_DEBUG_WX("Quality clamped by Signal Strength\n");
- else
- IPW_DEBUG_WX("Quality not clamped.\n");
-#endif
-
- wstats->qual.qual = quality;
- wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
- }
-
- wstats->qual.noise = 0;
- wstats->qual.updated = 7;
- wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
-
- /* FIXME: this is percent and not a # */
- wstats->miss.beacon = missed_beacons;
-
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES,
- &tx_failures, &ord_len))
- goto fail_get_ordinal;
- wstats->discard.retries = tx_failures;
-
- return wstats;
-
- fail_get_ordinal:
- IPW_DEBUG_WX("failed querying ordinals.\n");
-
- return (struct iw_statistics *)NULL;
-}
-
-static struct iw_handler_def ipw2100_wx_handler_def = {
- .standard = ipw2100_wx_handlers,
- .num_standard = ARRAY_SIZE(ipw2100_wx_handlers),
- .num_private = ARRAY_SIZE(ipw2100_private_handler),
- .num_private_args = ARRAY_SIZE(ipw2100_private_args),
- .private = (iw_handler *) ipw2100_private_handler,
- .private_args = (struct iw_priv_args *)ipw2100_private_args,
- .get_wireless_stats = ipw2100_wx_wireless_stats,
-};
-
-static void ipw2100_wx_event_work(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, wx_event_work.work);
- union iwreq_data wrqu;
- unsigned int len = ETH_ALEN;
-
- if (priv->status & STATUS_STOPPING)
- return;
-
- mutex_lock(&priv->action_mutex);
-
- IPW_DEBUG_WX("enter\n");
-
- mutex_unlock(&priv->action_mutex);
-
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-
- /* Fetch BSSID from the hardware */
- if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) ||
- priv->status & STATUS_RF_KILL_MASK ||
- ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
- &priv->bssid, &len)) {
- eth_zero_addr(wrqu.ap_addr.sa_data);
- } else {
- /* We now have the BSSID, so can finish setting to the full
- * associated state */
- memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
- memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN);
- priv->status &= ~STATUS_ASSOCIATING;
- priv->status |= STATUS_ASSOCIATED;
- netif_carrier_on(priv->net_dev);
- netif_wake_queue(priv->net_dev);
- }
-
- if (!(priv->status & STATUS_ASSOCIATED)) {
- IPW_DEBUG_WX("Configuring ESSID\n");
- mutex_lock(&priv->action_mutex);
- /* This is a disassociation event, so kick the firmware to
- * look for another AP */
- if (priv->config & CFG_STATIC_ESSID)
- ipw2100_set_essid(priv, priv->essid, priv->essid_len,
- 0);
- else
- ipw2100_set_essid(priv, NULL, 0, 0);
- mutex_unlock(&priv->action_mutex);
- }
-
- wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
-}
-
-#define IPW2100_FW_MAJOR_VERSION 1
-#define IPW2100_FW_MINOR_VERSION 3
-
-#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8)
-#define IPW2100_FW_MAJOR(x) (x & 0xff)
-
-#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \
- IPW2100_FW_MAJOR_VERSION)
-
-#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \
-"." __stringify(IPW2100_FW_MINOR_VERSION)
-
-#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw"
-
-/*
-
-BINARY FIRMWARE HEADER FORMAT
-
-offset length desc
-0 2 version
-2 2 mode == 0:BSS,1:IBSS,2:MONITOR
-4 4 fw_len
-8 4 uc_len
-C fw_len firmware data
-12 + fw_len uc_len microcode data
-
-*/
-
-struct ipw2100_fw_header {
- short version;
- short mode;
- unsigned int fw_size;
- unsigned int uc_size;
-} __packed;
-
-static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
-{
- struct ipw2100_fw_header *h =
- (struct ipw2100_fw_header *)fw->fw_entry->data;
-
- if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
- printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
- "(detected version id of %u). "
- "See Documentation/networking/README.ipw2100\n",
- h->version);
- return 1;
- }
-
- fw->version = h->version;
- fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header);
- fw->fw.size = h->fw_size;
- fw->uc.data = fw->fw.data + h->fw_size;
- fw->uc.size = h->uc_size;
-
- return 0;
-}
-
-static int ipw2100_get_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw)
-{
- char *fw_name;
- int rc;
-
- IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n",
- priv->net_dev->name);
-
- switch (priv->ieee->iw_mode) {
- case IW_MODE_ADHOC:
- fw_name = IPW2100_FW_NAME("-i");
- break;
-#ifdef CONFIG_IPW2100_MONITOR
- case IW_MODE_MONITOR:
- fw_name = IPW2100_FW_NAME("-p");
- break;
-#endif
- case IW_MODE_INFRA:
- default:
- fw_name = IPW2100_FW_NAME("");
- break;
- }
-
- rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev);
-
- if (rc < 0) {
- printk(KERN_ERR DRV_NAME ": "
- "%s: Firmware '%s' not available or load failed.\n",
- priv->net_dev->name, fw_name);
- return rc;
- }
- IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data,
- fw->fw_entry->size);
-
- ipw2100_mod_firmware_load(fw);
-
- return 0;
-}
-
-MODULE_FIRMWARE(IPW2100_FW_NAME("-i"));
-#ifdef CONFIG_IPW2100_MONITOR
-MODULE_FIRMWARE(IPW2100_FW_NAME("-p"));
-#endif
-MODULE_FIRMWARE(IPW2100_FW_NAME(""));
-
-static void ipw2100_release_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw)
-{
- fw->version = 0;
- release_firmware(fw->fw_entry);
- fw->fw_entry = NULL;
-}
-
-static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
- size_t max)
-{
- char ver[MAX_FW_VERSION_LEN];
- u32 len = MAX_FW_VERSION_LEN;
- u32 tmp;
- int i;
- /* firmware version is an ascii string (max len of 14) */
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len))
- return -EIO;
- tmp = max;
- if (len >= max)
- len = max - 1;
- for (i = 0; i < len; i++)
- buf[i] = ver[i];
- buf[i] = '\0';
- return tmp;
-}
-
-static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
- size_t max)
-{
- u32 ver;
- u32 len = sizeof(ver);
- /* microcode version is a 32 bit integer */
- if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len))
- return -EIO;
- return snprintf(buf, max, "%08X", ver);
-}
-
-/*
- * On exit, the firmware will have been freed from the fw list
- */
-static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw)
-{
- /* firmware is constructed of N contiguous entries, each entry is
- * structured as:
- *
- * offset sie desc
- * 0 4 address to write to
- * 4 2 length of data run
- * 6 length data
- */
- unsigned int addr;
- unsigned short len;
-
- const unsigned char *firmware_data = fw->fw.data;
- unsigned int firmware_data_left = fw->fw.size;
-
- while (firmware_data_left > 0) {
- addr = *(u32 *) (firmware_data);
- firmware_data += 4;
- firmware_data_left -= 4;
-
- len = *(u16 *) (firmware_data);
- firmware_data += 2;
- firmware_data_left -= 2;
-
- if (len > 32) {
- printk(KERN_ERR DRV_NAME ": "
- "Invalid firmware run-length of %d bytes\n",
- len);
- return -EINVAL;
- }
-
- write_nic_memory(priv->net_dev, addr, len, firmware_data);
- firmware_data += len;
- firmware_data_left -= len;
- }
-
- return 0;
-}
-
-struct symbol_alive_response {
- u8 cmd_id;
- u8 seq_num;
- u8 ucode_rev;
- u8 eeprom_valid;
- u16 valid_flags;
- u8 IEEE_addr[6];
- u16 flags;
- u16 pcb_rev;
- u16 clock_settle_time; // 1us LSB
- u16 powerup_settle_time; // 1us LSB
- u16 hop_settle_time; // 1us LSB
- u8 date[3]; // month, day, year
- u8 time[2]; // hours, minutes
- u8 ucode_valid;
-};
-
-static int ipw2100_ucode_download(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw)
-{
- struct net_device *dev = priv->net_dev;
- const unsigned char *microcode_data = fw->uc.data;
- unsigned int microcode_data_left = fw->uc.size;
- void __iomem *reg = priv->ioaddr;
-
- struct symbol_alive_response response;
- int i, j;
- u8 data;
-
- /* Symbol control */
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
- readl(reg);
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
- readl(reg);
-
- /* HW config */
- write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
- readl(reg);
- write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
- readl(reg);
-
- /* EN_CS_ACCESS bit to reset control store pointer */
- write_nic_byte(dev, 0x210000, 0x40);
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x0);
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x40);
- readl(reg);
-
- /* copy microcode from buffer into Symbol */
-
- while (microcode_data_left > 0) {
- write_nic_byte(dev, 0x210010, *microcode_data++);
- write_nic_byte(dev, 0x210010, *microcode_data++);
- microcode_data_left -= 2;
- }
-
- /* EN_CS_ACCESS bit to reset the control store pointer */
- write_nic_byte(dev, 0x210000, 0x0);
- readl(reg);
-
- /* Enable System (Reg 0)
- * first enable causes garbage in RX FIFO */
- write_nic_byte(dev, 0x210000, 0x0);
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x80);
- readl(reg);
-
- /* Reset External Baseband Reg */
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
- readl(reg);
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
- readl(reg);
-
- /* HW Config (Reg 5) */
- write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
- readl(reg);
- write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
- readl(reg);
-
- /* Enable System (Reg 0)
- * second enable should be OK */
- write_nic_byte(dev, 0x210000, 0x00); // clear enable system
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x80); // set enable system
-
- /* check Symbol is enabled - upped this from 5 as it wasn't always
- * catching the update */
- for (i = 0; i < 10; i++) {
- udelay(10);
-
- /* check Dino is enabled bit */
- read_nic_byte(dev, 0x210000, &data);
- if (data & 0x1)
- break;
- }
-
- if (i == 10) {
- printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n",
- dev->name);
- return -EIO;
- }
-
- /* Get Symbol alive response */
- for (i = 0; i < 30; i++) {
- /* Read alive response structure */
- for (j = 0;
- j < (sizeof(struct symbol_alive_response) >> 1); j++)
- read_nic_word(dev, 0x210004, ((u16 *) & response) + j);
-
- if ((response.cmd_id == 1) && (response.ucode_valid == 0x1))
- break;
- udelay(10);
- }
-
- if (i == 30) {
- printk(KERN_ERR DRV_NAME
- ": %s: No response from Symbol - hw not alive\n",
- dev->name);
- printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response));
- return -EIO;
- }
-
- return 0;
-}
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
deleted file mode 100644
index 6656215a13a9..000000000000
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ /dev/null
@@ -1,6868 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/pci-aspm.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-
-#include <net/mac80211.h>
-
-#include <asm/div64.h>
-
-#define DRV_NAME "iwl4965"
-
-#include "common.h"
-#include "4965.h"
-
-/******************************************************************************
- *
- * module boiler plate
- *
- ******************************************************************************/
-
-/*
- * module name, copyright, version, etc.
- */
-#define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux"
-
-#ifdef CONFIG_IWLEGACY_DEBUG
-#define VD "d"
-#else
-#define VD
-#endif
-
-#define DRV_VERSION IWLWIFI_VERSION VD
-
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("iwl4965");
-
-void
-il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status)
-{
- if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
- IL_ERR("Tx flush command to flush out all frames\n");
- if (!test_bit(S_EXIT_PENDING, &il->status))
- queue_work(il->workqueue, &il->tx_flush);
- }
-}
-
-/*
- * EEPROM
- */
-struct il_mod_params il4965_mod_params = {
- .restart_fw = 1,
- /* the rest are 0 by default */
-};
-
-void
-il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq)
-{
- unsigned long flags;
- int i;
- spin_lock_irqsave(&rxq->lock, flags);
- INIT_LIST_HEAD(&rxq->rx_free);
- INIT_LIST_HEAD(&rxq->rx_used);
- /* Fill the rx_used queue with _all_ of the Rx buffers */
- for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
- /* In the reset function, these buffers may have been allocated
- * to an SKB, so we need to unmap and free potential storage */
- if (rxq->pool[i].page != NULL) {
- pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma,
- PAGE_SIZE << il->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- __il_free_pages(il, rxq->pool[i].page);
- rxq->pool[i].page = NULL;
- }
- list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
- }
-
- for (i = 0; i < RX_QUEUE_SIZE; i++)
- rxq->queue[i] = NULL;
-
- /* Set us so that we have processed and used all buffers, but have
- * not restocked the Rx queue with fresh buffers */
- rxq->read = rxq->write = 0;
- rxq->write_actual = 0;
- rxq->free_count = 0;
- spin_unlock_irqrestore(&rxq->lock, flags);
-}
-
-int
-il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq)
-{
- u32 rb_size;
- const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
- u32 rb_timeout = 0;
-
- if (il->cfg->mod_params->amsdu_size_8K)
- rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
- else
- rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
-
- /* Stop Rx DMA */
- il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);
-
- /* Reset driver's Rx queue write idx */
- il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
-
- /* Tell device where to find RBD circular buffer in DRAM */
- il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8));
-
- /* Tell device where in DRAM to update its Rx status */
- il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4);
-
- /* Enable Rx DMA
- * Direct rx interrupts to hosts
- * Rx buffer size 4 or 8k
- * RB timeout 0x10
- * 256 RBDs
- */
- il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG,
- FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
- FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
- FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
- rb_size |
- (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
- (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
-
- /* Set interrupt coalescing timer to default (2048 usecs) */
- il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF);
-
- return 0;
-}
-
-static void
-il4965_set_pwr_vmain(struct il_priv *il)
-{
-/*
- * (for documentation purposes)
- * to set power to V_AUX, do:
-
- if (pci_pme_capable(il->pci_dev, PCI_D3cold))
- il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
- ~APMG_PS_CTRL_MSK_PWR_SRC);
- */
-
- il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
- ~APMG_PS_CTRL_MSK_PWR_SRC);
-}
-
-int
-il4965_hw_nic_init(struct il_priv *il)
-{
- unsigned long flags;
- struct il_rx_queue *rxq = &il->rxq;
- int ret;
-
- spin_lock_irqsave(&il->lock, flags);
- il_apm_init(il);
- /* Set interrupt coalescing calibration timer to default (512 usecs) */
- il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF);
- spin_unlock_irqrestore(&il->lock, flags);
-
- il4965_set_pwr_vmain(il);
- il4965_nic_config(il);
-
- /* Allocate the RX queue, or reset if it is already allocated */
- if (!rxq->bd) {
- ret = il_rx_queue_alloc(il);
- if (ret) {
- IL_ERR("Unable to initialize Rx queue\n");
- return -ENOMEM;
- }
- } else
- il4965_rx_queue_reset(il, rxq);
-
- il4965_rx_replenish(il);
-
- il4965_rx_init(il, rxq);
-
- spin_lock_irqsave(&il->lock, flags);
-
- rxq->need_update = 1;
- il_rx_queue_update_write_ptr(il, rxq);
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- /* Allocate or reset and init all Tx and Command queues */
- if (!il->txq) {
- ret = il4965_txq_ctx_alloc(il);
- if (ret)
- return ret;
- } else
- il4965_txq_ctx_reset(il);
-
- set_bit(S_INIT, &il->status);
-
- return 0;
-}
-
-/**
- * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
- */
-static inline __le32
-il4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr)
-{
- return cpu_to_le32((u32) (dma_addr >> 8));
-}
-
-/**
- * il4965_rx_queue_restock - refill RX queue from pre-allocated pool
- *
- * If there are slots in the RX queue that need to be restocked,
- * and we have free pre-allocated buffers, fill the ranks as much
- * as we can, pulling from rx_free.
- *
- * This moves the 'write' idx forward to catch up with 'processed', and
- * also updates the memory address in the firmware to reference the new
- * target buffer.
- */
-void
-il4965_rx_queue_restock(struct il_priv *il)
-{
- struct il_rx_queue *rxq = &il->rxq;
- struct list_head *element;
- struct il_rx_buf *rxb;
- unsigned long flags;
-
- spin_lock_irqsave(&rxq->lock, flags);
- while (il_rx_queue_space(rxq) > 0 && rxq->free_count) {
- /* The overwritten rxb must be a used one */
- rxb = rxq->queue[rxq->write];
- BUG_ON(rxb && rxb->page);
-
- /* Get next free Rx buffer, remove from free list */
- element = rxq->rx_free.next;
- rxb = list_entry(element, struct il_rx_buf, list);
- list_del(element);
-
- /* Point to Rx buffer via next RBD in circular buffer */
- rxq->bd[rxq->write] =
- il4965_dma_addr2rbd_ptr(il, rxb->page_dma);
- rxq->queue[rxq->write] = rxb;
- rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
- rxq->free_count--;
- }
- spin_unlock_irqrestore(&rxq->lock, flags);
- /* If the pre-allocated buffer pool is dropping low, schedule to
- * refill it */
- if (rxq->free_count <= RX_LOW_WATERMARK)
- queue_work(il->workqueue, &il->rx_replenish);
-
- /* If we've added more space for the firmware to place data, tell it.
- * Increment device's write pointer in multiples of 8. */
- if (rxq->write_actual != (rxq->write & ~0x7)) {
- spin_lock_irqsave(&rxq->lock, flags);
- rxq->need_update = 1;
- spin_unlock_irqrestore(&rxq->lock, flags);
- il_rx_queue_update_write_ptr(il, rxq);
- }
-}
-
-/**
- * il4965_rx_replenish - Move all used packet from rx_used to rx_free
- *
- * When moving to rx_free an SKB is allocated for the slot.
- *
- * Also restock the Rx queue via il_rx_queue_restock.
- * This is called as a scheduled work item (except for during initialization)
- */
-static void
-il4965_rx_allocate(struct il_priv *il, gfp_t priority)
-{
- struct il_rx_queue *rxq = &il->rxq;
- struct list_head *element;
- struct il_rx_buf *rxb;
- struct page *page;
- dma_addr_t page_dma;
- unsigned long flags;
- gfp_t gfp_mask = priority;
-
- while (1) {
- spin_lock_irqsave(&rxq->lock, flags);
- if (list_empty(&rxq->rx_used)) {
- spin_unlock_irqrestore(&rxq->lock, flags);
- return;
- }
- spin_unlock_irqrestore(&rxq->lock, flags);
-
- if (rxq->free_count > RX_LOW_WATERMARK)
- gfp_mask |= __GFP_NOWARN;
-
- if (il->hw_params.rx_page_order > 0)
- gfp_mask |= __GFP_COMP;
-
- /* Alloc a new receive buffer */
- page = alloc_pages(gfp_mask, il->hw_params.rx_page_order);
- if (!page) {
- if (net_ratelimit())
- D_INFO("alloc_pages failed, " "order: %d\n",
- il->hw_params.rx_page_order);
-
- if (rxq->free_count <= RX_LOW_WATERMARK &&
- net_ratelimit())
- IL_ERR("Failed to alloc_pages with %s. "
- "Only %u free buffers remaining.\n",
- priority ==
- GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL",
- rxq->free_count);
- /* We don't reschedule replenish work here -- we will
- * call the restock method and if it still needs
- * more buffers it will schedule replenish */
- return;
- }
-
- /* Get physical address of the RB */
- page_dma =
- pci_map_page(il->pci_dev, page, 0,
- PAGE_SIZE << il->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) {
- __free_pages(page, il->hw_params.rx_page_order);
- break;
- }
-
- spin_lock_irqsave(&rxq->lock, flags);
-
- if (list_empty(&rxq->rx_used)) {
- spin_unlock_irqrestore(&rxq->lock, flags);
- pci_unmap_page(il->pci_dev, page_dma,
- PAGE_SIZE << il->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- __free_pages(page, il->hw_params.rx_page_order);
- return;
- }
-
- element = rxq->rx_used.next;
- rxb = list_entry(element, struct il_rx_buf, list);
- list_del(element);
-
- BUG_ON(rxb->page);
-
- rxb->page = page;
- rxb->page_dma = page_dma;
- list_add_tail(&rxb->list, &rxq->rx_free);
- rxq->free_count++;
- il->alloc_rxb_page++;
-
- spin_unlock_irqrestore(&rxq->lock, flags);
- }
-}
-
-void
-il4965_rx_replenish(struct il_priv *il)
-{
- unsigned long flags;
-
- il4965_rx_allocate(il, GFP_KERNEL);
-
- spin_lock_irqsave(&il->lock, flags);
- il4965_rx_queue_restock(il);
- spin_unlock_irqrestore(&il->lock, flags);
-}
-
-void
-il4965_rx_replenish_now(struct il_priv *il)
-{
- il4965_rx_allocate(il, GFP_ATOMIC);
-
- il4965_rx_queue_restock(il);
-}
-
-/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
- * If an SKB has been detached, the POOL needs to have its SKB set to NULL
- * This free routine walks the list of POOL entries and if SKB is set to
- * non NULL it is unmapped and freed
- */
-void
-il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq)
-{
- int i;
- for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
- if (rxq->pool[i].page != NULL) {
- pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma,
- PAGE_SIZE << il->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- __il_free_pages(il, rxq->pool[i].page);
- rxq->pool[i].page = NULL;
- }
- }
-
- dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
- rxq->bd_dma);
- dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status),
- rxq->rb_stts, rxq->rb_stts_dma);
- rxq->bd = NULL;
- rxq->rb_stts = NULL;
-}
-
-int
-il4965_rxq_stop(struct il_priv *il)
-{
- int ret;
-
- _il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);
- ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG,
- FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
- FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
- 1000);
- if (ret < 0)
- IL_ERR("Can't stop Rx DMA.\n");
-
- return 0;
-}
-
-int
-il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band)
-{
- int idx = 0;
- int band_offset = 0;
-
- /* HT rate format: mac80211 wants an MCS number, which is just LSB */
- if (rate_n_flags & RATE_MCS_HT_MSK) {
- idx = (rate_n_flags & 0xff);
- return idx;
- /* Legacy rate format, search for match in table */
- } else {
- if (band == IEEE80211_BAND_5GHZ)
- band_offset = IL_FIRST_OFDM_RATE;
- for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++)
- if (il_rates[idx].plcp == (rate_n_flags & 0xFF))
- return idx - band_offset;
- }
-
- return -1;
-}
-
-static int
-il4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp)
-{
- /* data from PHY/DSP regarding signal strength, etc.,
- * contents are always there, not configurable by host. */
- struct il4965_rx_non_cfg_phy *ncphy =
- (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
- u32 agc =
- (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >>
- IL49_AGC_DB_POS;
-
- u32 valid_antennae =
- (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK)
- >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET;
- u8 max_rssi = 0;
- u32 i;
-
- /* Find max rssi among 3 possible receivers.
- * These values are measured by the digital signal processor (DSP).
- * They should stay fairly constant even as the signal strength varies,
- * if the radio's automatic gain control (AGC) is working right.
- * AGC value (see below) will provide the "interesting" info. */
- for (i = 0; i < 3; i++)
- if (valid_antennae & (1 << i))
- max_rssi = max(ncphy->rssi_info[i << 1], max_rssi);
-
- D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n",
- ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4],
- max_rssi, agc);
-
- /* dBm = max_rssi dB - agc dB - constant.
- * Higher AGC (higher radio gain) means lower signal. */
- return max_rssi - agc - IL4965_RSSI_OFFSET;
-}
-
-static u32
-il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
-{
- u32 decrypt_out = 0;
-
- if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) ==
- RX_RES_STATUS_STATION_FOUND)
- decrypt_out |=
- (RX_RES_STATUS_STATION_FOUND |
- RX_RES_STATUS_NO_STATION_INFO_MISMATCH);
-
- decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK);
-
- /* packet was not encrypted */
- if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
- RX_RES_STATUS_SEC_TYPE_NONE)
- return decrypt_out;
-
- /* packet was encrypted with unknown alg */
- if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
- RX_RES_STATUS_SEC_TYPE_ERR)
- return decrypt_out;
-
- /* decryption was not done in HW */
- if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) !=
- RX_MPDU_RES_STATUS_DEC_DONE_MSK)
- return decrypt_out;
-
- switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) {
-
- case RX_RES_STATUS_SEC_TYPE_CCMP:
- /* alg is CCM: check MIC only */
- if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK))
- /* Bad MIC */
- decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
- else
- decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
-
- break;
-
- case RX_RES_STATUS_SEC_TYPE_TKIP:
- if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) {
- /* Bad TTAK */
- decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
- break;
- }
- /* fall through if TTAK OK */
- default:
- if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
- decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
- else
- decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
- break;
- }
-
- D_RX("decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out);
-
- return decrypt_out;
-}
-
-#define SMALL_PACKET_SIZE 256
-
-static void
-il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
- u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
- struct ieee80211_rx_status *stats)
-{
- struct sk_buff *skb;
- __le16 fc = hdr->frame_control;
-
- /* We only process data packets if the interface is open */
- if (unlikely(!il->is_open)) {
- D_DROP("Dropping packet while interface is not open.\n");
- 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))
- return;
-
- skb = dev_alloc_skb(SMALL_PACKET_SIZE);
- if (!skb) {
- IL_ERR("dev_alloc_skb failed\n");
- return;
- }
-
- if (len <= SMALL_PACKET_SIZE) {
- memcpy(skb_put(skb, len), hdr, len);
- } else {
- skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
- len, PAGE_SIZE << il->hw_params.rx_page_order);
- il->alloc_rxb_page--;
- rxb->page = NULL;
- }
-
- il_update_stats(il, false, fc, len);
- memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
-
- ieee80211_rx(il->hw, skb);
-}
-
-/* Called for N_RX (legacy ABG frames), or
- * N_RX_MPDU (HT high-throughput N frames). */
-static void
-il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct ieee80211_hdr *header;
- struct ieee80211_rx_status rx_status = {};
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_rx_phy_res *phy_res;
- __le32 rx_pkt_status;
- struct il_rx_mpdu_res_start *amsdu;
- u32 len;
- u32 ampdu_status;
- u32 rate_n_flags;
-
- /**
- * N_RX and N_RX_MPDU are handled differently.
- * N_RX: physical layer info is in this buffer
- * N_RX_MPDU: physical layer info was sent in separate
- * command and cached in il->last_phy_res
- *
- * Here we set up local variables depending on which command is
- * received.
- */
- if (pkt->hdr.cmd == N_RX) {
- phy_res = (struct il_rx_phy_res *)pkt->u.raw;
- header =
- (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) +
- phy_res->cfg_phy_cnt);
-
- len = le16_to_cpu(phy_res->byte_count);
- rx_pkt_status =
- *(__le32 *) (pkt->u.raw + sizeof(*phy_res) +
- phy_res->cfg_phy_cnt + len);
- ampdu_status = le32_to_cpu(rx_pkt_status);
- } else {
- if (!il->_4965.last_phy_res_valid) {
- IL_ERR("MPDU frame without cached PHY data\n");
- return;
- }
- phy_res = &il->_4965.last_phy_res;
- amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw;
- header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu));
- len = le16_to_cpu(amsdu->byte_count);
- rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len);
- ampdu_status =
- il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status));
- }
-
- if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
- D_DROP("dsp size out of range [0,20]: %d\n",
- phy_res->cfg_phy_cnt);
- return;
- }
-
- if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) ||
- !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
- D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status));
- return;
- }
-
- /* This will be used in several places later */
- rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);
-
- /* rx_status carries information about the packet to mac80211 */
- rx_status.mactime = le64_to_cpu(phy_res->timestamp);
- rx_status.band =
- (phy_res->
- phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ :
- IEEE80211_BAND_5GHZ;
- rx_status.freq =
- ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel),
- rx_status.band);
- rx_status.rate_idx =
- il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band);
- rx_status.flag = 0;
-
- /* TSF isn't reliable. In order to allow smooth user experience,
- * this W/A doesn't propagate it to the mac80211 */
- /*rx_status.flag |= RX_FLAG_MACTIME_START; */
-
- il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp);
-
- /* Find max signal strength (dBm) among 3 antenna/receiver chains */
- rx_status.signal = il4965_calc_rssi(il, phy_res);
-
- D_STATS("Rssi %d, TSF %llu\n", rx_status.signal,
- (unsigned long long)rx_status.mactime);
-
- /*
- * "antenna number"
- *
- * It seems that the antenna field in the phy flags value
- * is actually a bit field. This is undefined by radiotap,
- * it wants an actual antenna number but I always get "7"
- * for most legacy frames I receive indicating that the
- * same frame was received on all three RX chains.
- *
- * I think this field should be removed in favor of a
- * new 802.11n radiotap field "RX chains" that is defined
- * as a bitmask.
- */
- rx_status.antenna =
- (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >>
- RX_RES_PHY_FLAGS_ANTENNA_POS;
-
- /* set the preamble flag if appropriate */
- if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
- rx_status.flag |= RX_FLAG_SHORTPRE;
-
- /* Set up the HT phy flags */
- if (rate_n_flags & RATE_MCS_HT_MSK)
- rx_status.flag |= RX_FLAG_HT;
- if (rate_n_flags & RATE_MCS_HT40_MSK)
- rx_status.flag |= RX_FLAG_40MHZ;
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- rx_status.flag |= RX_FLAG_SHORT_GI;
-
- if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
- /* We know which subframes of an A-MPDU belong
- * together since we get a single PHY response
- * from the firmware for all of them.
- */
-
- rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
- rx_status.ampdu_reference = il->_4965.ampdu_ref;
- }
-
- il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb,
- &rx_status);
-}
-
-/* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY).
- * This will be used later in il_hdl_rx() for N_RX_MPDU. */
-static void
-il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- il->_4965.last_phy_res_valid = true;
- il->_4965.ampdu_ref++;
- memcpy(&il->_4965.last_phy_res, pkt->u.raw,
- sizeof(struct il_rx_phy_res));
-}
-
-static int
-il4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif,
- enum ieee80211_band band, u8 is_active,
- u8 n_probes, struct il_scan_channel *scan_ch)
-{
- struct ieee80211_channel *chan;
- const struct ieee80211_supported_band *sband;
- const struct il_channel_info *ch_info;
- u16 passive_dwell = 0;
- u16 active_dwell = 0;
- int added, i;
- u16 channel;
-
- sband = il_get_hw_mode(il, band);
- if (!sband)
- return 0;
-
- active_dwell = il_get_active_dwell_time(il, band, n_probes);
- passive_dwell = il_get_passive_dwell_time(il, band, vif);
-
- if (passive_dwell <= active_dwell)
- passive_dwell = active_dwell + 1;
-
- for (i = 0, added = 0; i < il->scan_request->n_channels; i++) {
- chan = il->scan_request->channels[i];
-
- if (chan->band != band)
- continue;
-
- channel = chan->hw_value;
- scan_ch->channel = cpu_to_le16(channel);
-
- ch_info = il_get_channel_info(il, band, channel);
- if (!il_is_channel_valid(ch_info)) {
- D_SCAN("Channel %d is INVALID for this band.\n",
- channel);
- continue;
- }
-
- if (!is_active || il_is_channel_passive(ch_info) ||
- (chan->flags & IEEE80211_CHAN_NO_IR))
- scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
- else
- scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
-
- if (n_probes)
- scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes);
-
- scan_ch->active_dwell = cpu_to_le16(active_dwell);
- scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
-
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
-
- /* NOTE: if we were doing 6Mb OFDM for scans we'd use
- * power level:
- * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
- */
- if (band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
-
- D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel,
- le32_to_cpu(scan_ch->type),
- (scan_ch->
- type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE",
- (scan_ch->
- type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell :
- passive_dwell);
-
- scan_ch++;
- added++;
- }
-
- D_SCAN("total channels to scan %d\n", added);
- return added;
-}
-
-static void
-il4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid)
-{
- int i;
- u8 ind = *ant;
-
- for (i = 0; i < RATE_ANT_NUM - 1; i++) {
- ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0;
- if (valid & BIT(ind)) {
- *ant = ind;
- return;
- }
- }
-}
-
-int
-il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif)
-{
- struct il_host_cmd cmd = {
- .id = C_SCAN,
- .len = sizeof(struct il_scan_cmd),
- .flags = CMD_SIZE_HUGE,
- };
- struct il_scan_cmd *scan;
- u32 rate_flags = 0;
- u16 cmd_len;
- u16 rx_chain = 0;
- enum ieee80211_band band;
- u8 n_probes = 0;
- u8 rx_ant = il->hw_params.valid_rx_ant;
- u8 rate;
- bool is_active = false;
- int chan_mod;
- u8 active_chains;
- u8 scan_tx_antennas = il->hw_params.valid_tx_ant;
- int ret;
-
- lockdep_assert_held(&il->mutex);
-
- if (!il->scan_cmd) {
- il->scan_cmd =
- kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE,
- GFP_KERNEL);
- if (!il->scan_cmd) {
- D_SCAN("fail to allocate memory for scan\n");
- return -ENOMEM;
- }
- }
- scan = il->scan_cmd;
- memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE);
-
- scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH;
- scan->quiet_time = IL_ACTIVE_QUIET_TIME;
-
- if (il_is_any_associated(il)) {
- u16 interval;
- u32 extra;
- u32 suspend_time = 100;
- u32 scan_suspend_time = 100;
-
- D_INFO("Scanning while associated...\n");
- interval = vif->bss_conf.beacon_int;
-
- scan->suspend_time = 0;
- scan->max_out_time = cpu_to_le32(200 * 1024);
- if (!interval)
- interval = suspend_time;
-
- extra = (suspend_time / interval) << 22;
- scan_suspend_time =
- (extra | ((suspend_time % interval) * 1024));
- scan->suspend_time = cpu_to_le32(scan_suspend_time);
- D_SCAN("suspend_time 0x%X beacon interval %d\n",
- scan_suspend_time, interval);
- }
-
- if (il->scan_request->n_ssids) {
- int i, p = 0;
- D_SCAN("Kicking off active scan\n");
- for (i = 0; i < il->scan_request->n_ssids; i++) {
- /* always does wildcard anyway */
- if (!il->scan_request->ssids[i].ssid_len)
- continue;
- scan->direct_scan[p].id = WLAN_EID_SSID;
- scan->direct_scan[p].len =
- il->scan_request->ssids[i].ssid_len;
- memcpy(scan->direct_scan[p].ssid,
- il->scan_request->ssids[i].ssid,
- il->scan_request->ssids[i].ssid_len);
- n_probes++;
- p++;
- }
- is_active = true;
- } else
- D_SCAN("Start passive scan.\n");
-
- scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
- scan->tx_cmd.sta_id = il->hw_params.bcast_id;
- scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
-
- switch (il->scan_band) {
- case IEEE80211_BAND_2GHZ:
- scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
- chan_mod =
- le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >>
- RXON_FLG_CHANNEL_MODE_POS;
- if (chan_mod == CHANNEL_MODE_PURE_40) {
- rate = RATE_6M_PLCP;
- } else {
- rate = RATE_1M_PLCP;
- rate_flags = RATE_MCS_CCK_MSK;
- }
- break;
- case IEEE80211_BAND_5GHZ:
- rate = RATE_6M_PLCP;
- break;
- default:
- IL_WARN("Invalid scan band\n");
- return -EIO;
- }
-
- /*
- * If active scanning is requested but a certain channel is
- * marked passive, we can do active scanning if we detect
- * transmissions.
- *
- * There is an issue with some firmware versions that triggers
- * a sysassert on a "good CRC threshold" of zero (== disabled),
- * on a radar channel even though this means that we should NOT
- * send probes.
- *
- * The "good CRC threshold" is the number of frames that we
- * need to receive during our dwell time on a channel before
- * sending out probes -- setting this to a huge value will
- * mean we never reach it, but at the same time work around
- * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER
- * here instead of IL_GOOD_CRC_TH_DISABLED.
- */
- scan->good_CRC_th =
- is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER;
-
- band = il->scan_band;
-
- if (il->cfg->scan_rx_antennas[band])
- rx_ant = il->cfg->scan_rx_antennas[band];
-
- il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas);
- rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS;
- scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags);
-
- /* In power save mode use one chain, otherwise use all chains */
- if (test_bit(S_POWER_PMI, &il->status)) {
- /* rx_ant has been set to all valid chains previously */
- active_chains =
- rx_ant & ((u8) (il->chain_noise_data.active_chains));
- if (!active_chains)
- active_chains = rx_ant;
-
- D_SCAN("chain_noise_data.active_chains: %u\n",
- il->chain_noise_data.active_chains);
-
- rx_ant = il4965_first_antenna(active_chains);
- }
-
- /* MIMO is not used here, but value is required */
- rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
- rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
- rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
- rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
- scan->rx_chain = cpu_to_le16(rx_chain);
-
- cmd_len =
- il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data,
- vif->addr, il->scan_request->ie,
- il->scan_request->ie_len,
- IL_MAX_SCAN_SIZE - sizeof(*scan));
- scan->tx_cmd.len = cpu_to_le16(cmd_len);
-
- scan->filter_flags |=
- (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK);
-
- scan->channel_count =
- il4965_get_channels_for_scan(il, vif, band, is_active, n_probes,
- (void *)&scan->data[cmd_len]);
- if (scan->channel_count == 0) {
- D_SCAN("channel count %d\n", scan->channel_count);
- return -EIO;
- }
-
- cmd.len +=
- le16_to_cpu(scan->tx_cmd.len) +
- scan->channel_count * sizeof(struct il_scan_channel);
- cmd.data = scan;
- scan->len = cpu_to_le16(cmd.len);
-
- set_bit(S_SCAN_HW, &il->status);
-
- ret = il_send_cmd_sync(il, &cmd);
- if (ret)
- clear_bit(S_SCAN_HW, &il->status);
-
- return ret;
-}
-
-int
-il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif,
- bool add)
-{
- struct il_vif_priv *vif_priv = (void *)vif->drv_priv;
-
- if (add)
- return il4965_add_bssid_station(il, vif->bss_conf.bssid,
- &vif_priv->ibss_bssid_sta_id);
- return il_remove_station(il, vif_priv->ibss_bssid_sta_id,
- vif->bss_conf.bssid);
-}
-
-void
-il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed)
-{
- lockdep_assert_held(&il->sta_lock);
-
- if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed)
- il->stations[sta_id].tid[tid].tfds_in_queue -= freed;
- else {
- D_TX("free more than tfds_in_queue (%u:%d)\n",
- il->stations[sta_id].tid[tid].tfds_in_queue, freed);
- il->stations[sta_id].tid[tid].tfds_in_queue = 0;
- }
-}
-
-#define IL_TX_QUEUE_MSK 0xfffff
-
-static bool
-il4965_is_single_rx_stream(struct il_priv *il)
-{
- return il->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
- il->current_ht_config.single_chain_sufficient;
-}
-
-#define IL_NUM_RX_CHAINS_MULTIPLE 3
-#define IL_NUM_RX_CHAINS_SINGLE 2
-#define IL_NUM_IDLE_CHAINS_DUAL 2
-#define IL_NUM_IDLE_CHAINS_SINGLE 1
-
-/*
- * Determine how many receiver/antenna chains to use.
- *
- * More provides better reception via diversity. Fewer saves power
- * at the expense of throughput, but only when not in powersave to
- * start with.
- *
- * MIMO (dual stream) requires at least 2, but works better with 3.
- * This does not determine *which* chains to use, just how many.
- */
-static int
-il4965_get_active_rx_chain_count(struct il_priv *il)
-{
- /* # of Rx chains to use when expecting MIMO. */
- if (il4965_is_single_rx_stream(il))
- return IL_NUM_RX_CHAINS_SINGLE;
- else
- return IL_NUM_RX_CHAINS_MULTIPLE;
-}
-
-/*
- * When we are in power saving mode, unless device support spatial
- * multiplexing power save, use the active count for rx chain count.
- */
-static int
-il4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt)
-{
- /* # Rx chains when idling, depending on SMPS mode */
- switch (il->current_ht_config.smps) {
- case IEEE80211_SMPS_STATIC:
- case IEEE80211_SMPS_DYNAMIC:
- return IL_NUM_IDLE_CHAINS_SINGLE;
- case IEEE80211_SMPS_OFF:
- return active_cnt;
- default:
- WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps);
- return active_cnt;
- }
-}
-
-/* up to 4 chains */
-static u8
-il4965_count_chain_bitmap(u32 chain_bitmap)
-{
- u8 res;
- res = (chain_bitmap & BIT(0)) >> 0;
- res += (chain_bitmap & BIT(1)) >> 1;
- res += (chain_bitmap & BIT(2)) >> 2;
- res += (chain_bitmap & BIT(3)) >> 3;
- return res;
-}
-
-/**
- * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
- *
- * Selects how many and which Rx receivers/antennas/chains to use.
- * This should not be used for scan command ... it puts data in wrong place.
- */
-void
-il4965_set_rxon_chain(struct il_priv *il)
-{
- bool is_single = il4965_is_single_rx_stream(il);
- bool is_cam = !test_bit(S_POWER_PMI, &il->status);
- u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
- u32 active_chains;
- u16 rx_chain;
-
- /* Tell uCode which antennas are actually connected.
- * Before first association, we assume all antennas are connected.
- * Just after first association, il4965_chain_noise_calibration()
- * checks which antennas actually *are* connected. */
- if (il->chain_noise_data.active_chains)
- active_chains = il->chain_noise_data.active_chains;
- else
- active_chains = il->hw_params.valid_rx_ant;
-
- rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
-
- /* How many receivers should we use? */
- active_rx_cnt = il4965_get_active_rx_chain_count(il);
- idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt);
-
- /* correct rx chain count according hw settings
- * and chain noise calibration
- */
- valid_rx_cnt = il4965_count_chain_bitmap(active_chains);
- if (valid_rx_cnt < active_rx_cnt)
- active_rx_cnt = valid_rx_cnt;
-
- if (valid_rx_cnt < idle_rx_cnt)
- idle_rx_cnt = valid_rx_cnt;
-
- rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
- rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;
-
- il->staging.rx_chain = cpu_to_le16(rx_chain);
-
- if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam)
- il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
- else
- il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
-
- D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain,
- active_rx_cnt, idle_rx_cnt);
-
- WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 ||
- active_rx_cnt < idle_rx_cnt);
-}
-
-static const char *
-il4965_get_fh_string(int cmd)
-{
- switch (cmd) {
- IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG);
- IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG);
- IL_CMD(FH49_RSCSR_CHNL0_WPTR);
- IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG);
- IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG);
- IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG);
- IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
- IL_CMD(FH49_TSSR_TX_STATUS_REG);
- IL_CMD(FH49_TSSR_TX_ERROR_REG);
- default:
- return "UNKNOWN";
- }
-}
-
-int
-il4965_dump_fh(struct il_priv *il, char **buf, bool display)
-{
- int i;
-#ifdef CONFIG_IWLEGACY_DEBUG
- int pos = 0;
- size_t bufsz = 0;
-#endif
- static const u32 fh_tbl[] = {
- FH49_RSCSR_CHNL0_STTS_WPTR_REG,
- FH49_RSCSR_CHNL0_RBDCB_BASE_REG,
- FH49_RSCSR_CHNL0_WPTR,
- FH49_MEM_RCSR_CHNL0_CONFIG_REG,
- FH49_MEM_RSSR_SHARED_CTRL_REG,
- FH49_MEM_RSSR_RX_STATUS_REG,
- FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
- FH49_TSSR_TX_STATUS_REG,
- FH49_TSSR_TX_ERROR_REG
- };
-#ifdef CONFIG_IWLEGACY_DEBUG
- if (display) {
- bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
- pos +=
- scnprintf(*buf + pos, bufsz - pos, "FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
- pos +=
- scnprintf(*buf + pos, bufsz - pos,
- " %34s: 0X%08x\n",
- il4965_get_fh_string(fh_tbl[i]),
- il_rd(il, fh_tbl[i]));
- }
- return pos;
- }
-#endif
- IL_ERR("FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
- IL_ERR(" %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]),
- il_rd(il, fh_tbl[i]));
- }
- return 0;
-}
-
-static void
-il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_missed_beacon_notif *missed_beacon;
-
- missed_beacon = &pkt->u.missed_beacon;
- if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
- il->missed_beacon_threshold) {
- D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n",
- le32_to_cpu(missed_beacon->consecutive_missed_beacons),
- le32_to_cpu(missed_beacon->total_missed_becons),
- le32_to_cpu(missed_beacon->num_recvd_beacons),
- le32_to_cpu(missed_beacon->num_expected_beacons));
- if (!test_bit(S_SCANNING, &il->status))
- il4965_init_sensitivity(il);
- }
-}
-
-/* Calculate noise level, based on measurements during network silence just
- * before arriving beacon. This measurement can be done only if we know
- * exactly when to expect beacons, therefore only when we're associated. */
-static void
-il4965_rx_calc_noise(struct il_priv *il)
-{
- struct stats_rx_non_phy *rx_info;
- int num_active_rx = 0;
- int total_silence = 0;
- int bcn_silence_a, bcn_silence_b, bcn_silence_c;
- int last_rx_noise;
-
- rx_info = &(il->_4965.stats.rx.general);
- bcn_silence_a =
- le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
- bcn_silence_b =
- le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
- bcn_silence_c =
- le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
-
- if (bcn_silence_a) {
- total_silence += bcn_silence_a;
- num_active_rx++;
- }
- if (bcn_silence_b) {
- total_silence += bcn_silence_b;
- num_active_rx++;
- }
- if (bcn_silence_c) {
- total_silence += bcn_silence_c;
- num_active_rx++;
- }
-
- /* Average among active antennas */
- if (num_active_rx)
- last_rx_noise = (total_silence / num_active_rx) - 107;
- else
- last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE;
-
- D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a,
- bcn_silence_b, bcn_silence_c, last_rx_noise);
-}
-
-#ifdef CONFIG_IWLEGACY_DEBUGFS
-/*
- * based on the assumption of all stats counter are in DWORD
- * FIXME: This function is for debugging, do not deal with
- * the case of counters roll-over.
- */
-static void
-il4965_accumulative_stats(struct il_priv *il, __le32 * stats)
-{
- int i, size;
- __le32 *prev_stats;
- u32 *accum_stats;
- u32 *delta, *max_delta;
- struct stats_general_common *general, *accum_general;
- struct stats_tx *tx, *accum_tx;
-
- prev_stats = (__le32 *) &il->_4965.stats;
- accum_stats = (u32 *) &il->_4965.accum_stats;
- size = sizeof(struct il_notif_stats);
- general = &il->_4965.stats.general.common;
- accum_general = &il->_4965.accum_stats.general.common;
- tx = &il->_4965.stats.tx;
- accum_tx = &il->_4965.accum_stats.tx;
- delta = (u32 *) &il->_4965.delta_stats;
- max_delta = (u32 *) &il->_4965.max_delta;
-
- for (i = sizeof(__le32); i < size;
- i +=
- sizeof(__le32), stats++, prev_stats++, delta++, max_delta++,
- accum_stats++) {
- if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) {
- *delta =
- (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats));
- *accum_stats += *delta;
- if (*delta > *max_delta)
- *max_delta = *delta;
- }
- }
-
- /* reset accumulative stats for "no-counter" type stats */
- accum_general->temperature = general->temperature;
- accum_general->ttl_timestamp = general->ttl_timestamp;
-}
-#endif
-
-static void
-il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb)
-{
- const int recalib_seconds = 60;
- bool change;
- struct il_rx_pkt *pkt = rxb_addr(rxb);
-
- D_RX("Statistics notification received (%d vs %d).\n",
- (int)sizeof(struct il_notif_stats),
- le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK);
-
- change =
- ((il->_4965.stats.general.common.temperature !=
- pkt->u.stats.general.common.temperature) ||
- ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) !=
- (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)));
-#ifdef CONFIG_IWLEGACY_DEBUGFS
- il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats);
-#endif
-
- /* TODO: reading some of stats is unneeded */
- memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats));
-
- set_bit(S_STATS, &il->status);
-
- /*
- * Reschedule the stats timer to occur in recalib_seconds to ensure
- * we get a thermal update even if the uCode doesn't give us one
- */
- mod_timer(&il->stats_periodic,
- jiffies + msecs_to_jiffies(recalib_seconds * 1000));
-
- if (unlikely(!test_bit(S_SCANNING, &il->status)) &&
- (pkt->hdr.cmd == N_STATS)) {
- il4965_rx_calc_noise(il);
- queue_work(il->workqueue, &il->run_time_calib_work);
- }
-
- if (change)
- il4965_temperature_calib(il);
-}
-
-static void
-il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
-
- if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) {
-#ifdef CONFIG_IWLEGACY_DEBUGFS
- memset(&il->_4965.accum_stats, 0,
- sizeof(struct il_notif_stats));
- memset(&il->_4965.delta_stats, 0,
- sizeof(struct il_notif_stats));
- memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats));
-#endif
- D_RX("Statistics have been cleared\n");
- }
- il4965_hdl_stats(il, rxb);
-}
-
-
-/*
- * mac80211 queues, ACs, hardware queues, FIFOs.
- *
- * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues
- *
- * Mac80211 uses the following numbers, which we get as from it
- * by way of skb_get_queue_mapping(skb):
- *
- * VO 0
- * VI 1
- * BE 2
- * BK 3
- *
- *
- * Regular (not A-MPDU) frames are put into hardware queues corresponding
- * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their
- * own queue per aggregation session (RA/TID combination), such queues are
- * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In
- * order to map frames to the right queue, we also need an AC->hw queue
- * mapping. This is implemented here.
- *
- * Due to the way hw queues are set up (by the hw specific modules like
- * 4965.c), the AC->hw queue mapping is the identity
- * mapping.
- */
-
-static const u8 tid_to_ac[] = {
- IEEE80211_AC_BE,
- IEEE80211_AC_BK,
- IEEE80211_AC_BK,
- IEEE80211_AC_BE,
- IEEE80211_AC_VI,
- IEEE80211_AC_VI,
- IEEE80211_AC_VO,
- IEEE80211_AC_VO
-};
-
-static inline int
-il4965_get_ac_from_tid(u16 tid)
-{
- if (likely(tid < ARRAY_SIZE(tid_to_ac)))
- return tid_to_ac[tid];
-
- /* no support for TIDs 8-15 yet */
- return -EINVAL;
-}
-
-static inline int
-il4965_get_fifo_from_tid(u16 tid)
-{
- const u8 ac_to_fifo[] = {
- IL_TX_FIFO_VO,
- IL_TX_FIFO_VI,
- IL_TX_FIFO_BE,
- IL_TX_FIFO_BK,
- };
-
- if (likely(tid < ARRAY_SIZE(tid_to_ac)))
- return ac_to_fifo[tid_to_ac[tid]];
-
- /* no support for TIDs 8-15 yet */
- return -EINVAL;
-}
-
-/*
- * handle build C_TX command notification.
- */
-static void
-il4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb,
- struct il_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_hdr *hdr, u8 std_id)
-{
- __le16 fc = hdr->frame_control;
- __le32 tx_flags = tx_cmd->tx_flags;
-
- tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
- tx_flags |= TX_CMD_FLG_ACK_MSK;
- if (ieee80211_is_mgmt(fc))
- tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
- if (ieee80211_is_probe_resp(fc) &&
- !(le16_to_cpu(hdr->seq_ctrl) & 0xf))
- tx_flags |= TX_CMD_FLG_TSF_MSK;
- } else {
- tx_flags &= (~TX_CMD_FLG_ACK_MSK);
- tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
- }
-
- if (ieee80211_is_back_req(fc))
- tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
-
- tx_cmd->sta_id = std_id;
- if (ieee80211_has_morefrags(fc))
- tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
-
- if (ieee80211_is_data_qos(fc)) {
- u8 *qc = ieee80211_get_qos_ctl(hdr);
- tx_cmd->tid_tspec = qc[0] & 0xf;
- tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
- } else {
- tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
- }
-
- il_tx_cmd_protection(il, info, fc, &tx_flags);
-
- tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
- if (ieee80211_is_mgmt(fc)) {
- if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
- tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3);
- else
- tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2);
- } else {
- tx_cmd->timeout.pm_frame_timeout = 0;
- }
-
- tx_cmd->driver_txop = 0;
- tx_cmd->tx_flags = tx_flags;
- tx_cmd->next_frame_len = 0;
-}
-
-static void
-il4965_tx_cmd_build_rate(struct il_priv *il,
- struct il_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- __le16 fc)
-{
- const u8 rts_retry_limit = 60;
- u32 rate_flags;
- int rate_idx;
- u8 data_retry_limit;
- u8 rate_plcp;
-
- /* Set retry limit on DATA packets and Probe Responses */
- if (ieee80211_is_probe_resp(fc))
- data_retry_limit = 3;
- else
- data_retry_limit = IL4965_DEFAULT_TX_RETRY;
- tx_cmd->data_retry_limit = data_retry_limit;
- /* Set retry limit on RTS packets */
- tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit);
-
- /* DATA packets will use the uCode station table for rate/antenna
- * selection */
- if (ieee80211_is_data(fc)) {
- tx_cmd->initial_rate_idx = 0;
- tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
- return;
- }
-
- /**
- * If the current TX rate stored in mac80211 has the MCS bit set, it's
- * not really a TX rate. Thus, we use the lowest supported rate for
- * this band. Also use the lowest supported rate if the stored rate
- * idx is invalid.
- */
- rate_idx = info->control.rates[0].idx;
- if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0
- || rate_idx > RATE_COUNT_LEGACY)
- rate_idx = rate_lowest_index(&il->bands[info->band], sta);
- /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
- if (info->band == IEEE80211_BAND_5GHZ)
- rate_idx += IL_FIRST_OFDM_RATE;
- /* Get PLCP rate for tx_cmd->rate_n_flags */
- rate_plcp = il_rates[rate_idx].plcp;
- /* Zero out flags for this packet */
- rate_flags = 0;
-
- /* Set CCK flag as needed */
- if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE)
- rate_flags |= RATE_MCS_CCK_MSK;
-
- /* Set up antennas */
- il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant);
- rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS;
-
- /* Set the rate in the TX cmd */
- tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags);
-}
-
-static void
-il4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info,
- struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag,
- int sta_id)
-{
- struct ieee80211_key_conf *keyconf = info->control.hw_key;
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
- memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
- D_TX("tx_cmd with AES hwcrypto\n");
- break;
-
- case WLAN_CIPHER_SUITE_TKIP:
- tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
- ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
- D_TX("tx_cmd with tkip hwcrypto\n");
- break;
-
- case WLAN_CIPHER_SUITE_WEP104:
- tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
- /* fall through */
- case WLAN_CIPHER_SUITE_WEP40:
- tx_cmd->sec_ctl |=
- (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) <<
- TX_CMD_SEC_SHIFT);
-
- memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
-
- D_TX("Configuring packet for WEP encryption " "with key %d\n",
- keyconf->keyidx);
- break;
-
- default:
- IL_ERR("Unknown encode cipher %x\n", keyconf->cipher);
- break;
- }
-}
-
-/*
- * start C_TX command process
- */
-int
-il4965_tx_skb(struct il_priv *il,
- struct ieee80211_sta *sta,
- struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct il_station_priv *sta_priv = NULL;
- struct il_tx_queue *txq;
- struct il_queue *q;
- struct il_device_cmd *out_cmd;
- struct il_cmd_meta *out_meta;
- struct il_tx_cmd *tx_cmd;
- int txq_id;
- dma_addr_t phys_addr;
- dma_addr_t txcmd_phys;
- dma_addr_t scratch_phys;
- u16 len, firstlen, secondlen;
- u16 seq_number = 0;
- __le16 fc;
- u8 hdr_len;
- u8 sta_id;
- u8 wait_write_ptr = 0;
- u8 tid = 0;
- u8 *qc = NULL;
- unsigned long flags;
- bool is_agg = false;
-
- spin_lock_irqsave(&il->lock, flags);
- if (il_is_rfkill(il)) {
- D_DROP("Dropping - RF KILL\n");
- goto drop_unlock;
- }
-
- fc = hdr->frame_control;
-
-#ifdef CONFIG_IWLEGACY_DEBUG
- if (ieee80211_is_auth(fc))
- D_TX("Sending AUTH frame\n");
- else if (ieee80211_is_assoc_req(fc))
- D_TX("Sending ASSOC frame\n");
- else if (ieee80211_is_reassoc_req(fc))
- D_TX("Sending REASSOC frame\n");
-#endif
-
- hdr_len = ieee80211_hdrlen(fc);
-
- /* For management frames use broadcast id to do not break aggregation */
- if (!ieee80211_is_data(fc))
- sta_id = il->hw_params.bcast_id;
- else {
- /* Find idx into station table for destination station */
- sta_id = il_sta_id_or_broadcast(il, sta);
-
- if (sta_id == IL_INVALID_STATION) {
- D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1);
- goto drop_unlock;
- }
- }
-
- D_TX("station Id %d\n", sta_id);
-
- if (sta)
- sta_priv = (void *)sta->drv_priv;
-
- if (sta_priv && sta_priv->asleep &&
- (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) {
- /*
- * This sends an asynchronous command to the device,
- * but we can rely on it being processed before the
- * next frame is processed -- and the next frame to
- * this station is the one that will consume this
- * counter.
- * For now set the counter to just 1 since we do not
- * support uAPSD yet.
- */
- il4965_sta_modify_sleep_tx_count(il, sta_id, 1);
- }
-
- /* FIXME: remove me ? */
- WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
-
- /* Access category (AC) is also the queue number */
- txq_id = skb_get_queue_mapping(skb);
-
- /* irqs already disabled/saved above when locking il->lock */
- spin_lock(&il->sta_lock);
-
- if (ieee80211_is_data_qos(fc)) {
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
- if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) {
- spin_unlock(&il->sta_lock);
- goto drop_unlock;
- }
- seq_number = il->stations[sta_id].tid[tid].seq_number;
- seq_number &= IEEE80211_SCTL_SEQ;
- hdr->seq_ctrl =
- hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG);
- hdr->seq_ctrl |= cpu_to_le16(seq_number);
- seq_number += 0x10;
- /* aggregation is on for this <sta,tid> */
- if (info->flags & IEEE80211_TX_CTL_AMPDU &&
- il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) {
- txq_id = il->stations[sta_id].tid[tid].agg.txq_id;
- is_agg = true;
- }
- }
-
- txq = &il->txq[txq_id];
- q = &txq->q;
-
- if (unlikely(il_queue_space(q) < q->high_mark)) {
- spin_unlock(&il->sta_lock);
- goto drop_unlock;
- }
-
- if (ieee80211_is_data_qos(fc)) {
- il->stations[sta_id].tid[tid].tfds_in_queue++;
- if (!ieee80211_has_morefrags(fc))
- il->stations[sta_id].tid[tid].seq_number = seq_number;
- }
-
- spin_unlock(&il->sta_lock);
-
- txq->skbs[q->write_ptr] = skb;
-
- /* Set up first empty entry in queue's array of Tx/cmd buffers */
- out_cmd = txq->cmd[q->write_ptr];
- out_meta = &txq->meta[q->write_ptr];
- tx_cmd = &out_cmd->cmd.tx;
- memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
- memset(tx_cmd, 0, sizeof(struct il_tx_cmd));
-
- /*
- * Set up the Tx-command (not MAC!) header.
- * Store the chosen Tx queue and TFD idx within the sequence field;
- * after Tx, uCode's Tx response will return this value so driver can
- * locate the frame within the tx queue and do post-tx processing.
- */
- out_cmd->hdr.cmd = C_TX;
- out_cmd->hdr.sequence =
- cpu_to_le16((u16)
- (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr)));
-
- /* Copy MAC header from skb into command buffer */
- memcpy(tx_cmd->hdr, hdr, hdr_len);
-
- /* Total # bytes to be transmitted */
- tx_cmd->len = cpu_to_le16((u16) skb->len);
-
- if (info->control.hw_key)
- il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id);
-
- /* TODO need this for burst mode later on */
- il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id);
-
- il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc);
-
- /*
- * Use the first empty entry in this queue's command buffer array
- * to contain the Tx command and MAC header concatenated together
- * (payload data will be in another buffer).
- * Size of this varies, due to varying MAC header length.
- * If end is not dword aligned, we'll have 2 extra bytes at the end
- * of the MAC header (device reads on dword boundaries).
- * We'll tell device about this padding later.
- */
- len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len;
- firstlen = (len + 3) & ~3;
-
- /* Tell NIC about any 2-byte padding after MAC header */
- if (firstlen != len)
- tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
-
- /* Physical address of this Tx command's header (not MAC header!),
- * within command buffer array. */
- txcmd_phys =
- pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen,
- PCI_DMA_BIDIRECTIONAL);
- if (unlikely(pci_dma_mapping_error(il->pci_dev, txcmd_phys)))
- goto drop_unlock;
-
- /* Set up TFD's 2nd entry to point directly to remainder of skb,
- * if any (802.11 null frames have no payload). */
- secondlen = skb->len - hdr_len;
- if (secondlen > 0) {
- phys_addr =
- pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen,
- PCI_DMA_TODEVICE);
- if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr)))
- goto drop_unlock;
- }
-
- /* Add buffer containing Tx command and MAC(!) header to TFD's
- * first entry */
- il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0);
- dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
- dma_unmap_len_set(out_meta, len, firstlen);
- if (secondlen)
- il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen,
- 0, 0);
-
- if (!ieee80211_has_morefrags(hdr->frame_control)) {
- txq->need_update = 1;
- } else {
- wait_write_ptr = 1;
- txq->need_update = 0;
- }
-
- scratch_phys =
- txcmd_phys + sizeof(struct il_cmd_header) +
- offsetof(struct il_tx_cmd, scratch);
-
- /* take back ownership of DMA buffer to enable update */
- pci_dma_sync_single_for_cpu(il->pci_dev, txcmd_phys, firstlen,
- PCI_DMA_BIDIRECTIONAL);
- tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
- tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys);
-
- il_update_stats(il, true, fc, skb->len);
-
- D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence));
- D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
- il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd));
- il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len);
-
- /* Set up entry for this TFD in Tx byte-count array */
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len));
-
- pci_dma_sync_single_for_device(il->pci_dev, txcmd_phys, firstlen,
- PCI_DMA_BIDIRECTIONAL);
-
- /* Tell device the write idx *just past* this latest filled TFD */
- q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd);
- il_txq_update_write_ptr(il, txq);
- spin_unlock_irqrestore(&il->lock, flags);
-
- /*
- * At this point the frame is "transmitted" successfully
- * and we will get a TX status notification eventually,
- * regardless of the value of ret. "ret" only indicates
- * whether or not we should update the write pointer.
- */
-
- /*
- * Avoid atomic ops if it isn't an associated client.
- * Also, if this is a packet for aggregation, don't
- * increase the counter because the ucode will stop
- * aggregation queues when their respective station
- * goes to sleep.
- */
- if (sta_priv && sta_priv->client && !is_agg)
- atomic_inc(&sta_priv->pending_frames);
-
- if (il_queue_space(q) < q->high_mark && il->mac80211_registered) {
- if (wait_write_ptr) {
- spin_lock_irqsave(&il->lock, flags);
- txq->need_update = 1;
- il_txq_update_write_ptr(il, txq);
- spin_unlock_irqrestore(&il->lock, flags);
- } else {
- il_stop_queue(il, txq);
- }
- }
-
- return 0;
-
-drop_unlock:
- spin_unlock_irqrestore(&il->lock, flags);
- return -1;
-}
-
-static inline int
-il4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size)
-{
- ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma,
- GFP_KERNEL);
- if (!ptr->addr)
- return -ENOMEM;
- ptr->size = size;
- return 0;
-}
-
-static inline void
-il4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr)
-{
- if (unlikely(!ptr->addr))
- return;
-
- dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma);
- memset(ptr, 0, sizeof(*ptr));
-}
-
-/**
- * il4965_hw_txq_ctx_free - Free TXQ Context
- *
- * Destroy all TX DMA queues and structures
- */
-void
-il4965_hw_txq_ctx_free(struct il_priv *il)
-{
- int txq_id;
-
- /* Tx queues */
- if (il->txq) {
- for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
- if (txq_id == il->cmd_queue)
- il_cmd_queue_free(il);
- else
- il_tx_queue_free(il, txq_id);
- }
- il4965_free_dma_ptr(il, &il->kw);
-
- il4965_free_dma_ptr(il, &il->scd_bc_tbls);
-
- /* free tx queue structure */
- il_free_txq_mem(il);
-}
-
-/**
- * il4965_txq_ctx_alloc - allocate TX queue context
- * Allocate all Tx DMA structures and initialize them
- *
- * @param il
- * @return error code
- */
-int
-il4965_txq_ctx_alloc(struct il_priv *il)
-{
- int ret, txq_id;
- unsigned long flags;
-
- /* Free all tx/cmd queues and keep-warm buffer */
- il4965_hw_txq_ctx_free(il);
-
- ret =
- il4965_alloc_dma_ptr(il, &il->scd_bc_tbls,
- il->hw_params.scd_bc_tbls_size);
- if (ret) {
- IL_ERR("Scheduler BC Table allocation failed\n");
- goto error_bc_tbls;
- }
- /* Alloc keep-warm buffer */
- ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE);
- if (ret) {
- IL_ERR("Keep Warm allocation failed\n");
- goto error_kw;
- }
-
- /* allocate tx queue structure */
- ret = il_alloc_txq_mem(il);
- if (ret)
- goto error;
-
- spin_lock_irqsave(&il->lock, flags);
-
- /* Turn off all Tx DMA fifos */
- il4965_txq_set_sched(il, 0);
-
- /* Tell NIC where to find the "keep warm" buffer */
- il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- /* Alloc and init all Tx queues, including the command queue (#4/#9) */
- for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) {
- ret = il_tx_queue_init(il, txq_id);
- if (ret) {
- IL_ERR("Tx %d queue init failed\n", txq_id);
- goto error;
- }
- }
-
- return ret;
-
-error:
- il4965_hw_txq_ctx_free(il);
- il4965_free_dma_ptr(il, &il->kw);
-error_kw:
- il4965_free_dma_ptr(il, &il->scd_bc_tbls);
-error_bc_tbls:
- return ret;
-}
-
-void
-il4965_txq_ctx_reset(struct il_priv *il)
-{
- int txq_id;
- unsigned long flags;
-
- spin_lock_irqsave(&il->lock, flags);
-
- /* Turn off all Tx DMA fifos */
- il4965_txq_set_sched(il, 0);
- /* Tell NIC where to find the "keep warm" buffer */
- il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- /* Alloc and init all Tx queues, including the command queue (#4) */
- for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
- il_tx_queue_reset(il, txq_id);
-}
-
-static void
-il4965_txq_ctx_unmap(struct il_priv *il)
-{
- int txq_id;
-
- if (!il->txq)
- return;
-
- /* Unmap DMA from host system and free skb's */
- for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
- if (txq_id == il->cmd_queue)
- il_cmd_queue_unmap(il);
- else
- il_tx_queue_unmap(il, txq_id);
-}
-
-/**
- * il4965_txq_ctx_stop - Stop all Tx DMA channels
- */
-void
-il4965_txq_ctx_stop(struct il_priv *il)
-{
- int ch, ret;
-
- _il_wr_prph(il, IL49_SCD_TXFACT, 0);
-
- /* Stop each Tx DMA channel, and wait for it to be idle */
- for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) {
- _il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
- ret =
- _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG,
- FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
- FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
- 1000);
- if (ret < 0)
- IL_ERR("Timeout stopping DMA channel %d [0x%08x]",
- ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG));
- }
-}
-
-/*
- * Find first available (lowest unused) Tx Queue, mark it "active".
- * Called only when finding queue for aggregation.
- * Should never return anything < 7, because they should already
- * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
- */
-static int
-il4965_txq_ctx_activate_free(struct il_priv *il)
-{
- int txq_id;
-
- for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
- if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk))
- return txq_id;
- return -1;
-}
-
-/**
- * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration
- */
-static void
-il4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id)
-{
- /* Simply stop the queue, but don't change any configuration;
- * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
- il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id),
- (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
- (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
-}
-
-/**
- * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue
- */
-static int
-il4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id)
-{
- u32 tbl_dw_addr;
- u32 tbl_dw;
- u16 scd_q2ratid;
-
- scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;
-
- tbl_dw_addr =
- il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
-
- tbl_dw = il_read_targ_mem(il, tbl_dw_addr);
-
- if (txq_id & 0x1)
- tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
- else
- tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
-
- il_write_targ_mem(il, tbl_dw_addr, tbl_dw);
-
- return 0;
-}
-
-/**
- * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue
- *
- * NOTE: txq_id must be greater than IL49_FIRST_AMPDU_QUEUE,
- * i.e. it must be one of the higher queues used for aggregation
- */
-static int
-il4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id,
- int tid, u16 ssn_idx)
-{
- unsigned long flags;
- u16 ra_tid;
- int ret;
-
- if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
- (IL49_FIRST_AMPDU_QUEUE +
- il->cfg->num_of_ampdu_queues <= txq_id)) {
- IL_WARN("queue number out of range: %d, must be %d to %d\n",
- txq_id, IL49_FIRST_AMPDU_QUEUE,
- IL49_FIRST_AMPDU_QUEUE +
- il->cfg->num_of_ampdu_queues - 1);
- return -EINVAL;
- }
-
- ra_tid = BUILD_RAxTID(sta_id, tid);
-
- /* Modify device's station table to Tx this TID */
- ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid);
- if (ret)
- return ret;
-
- spin_lock_irqsave(&il->lock, flags);
-
- /* Stop this Tx queue before configuring it */
- il4965_tx_queue_stop_scheduler(il, txq_id);
-
- /* Map receiver-address / traffic-ID to this queue */
- il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id);
-
- /* Set this queue as a chain-building queue */
- il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
-
- /* Place first TFD at idx corresponding to start sequence number.
- * Assumes that ssn_idx is valid (!= 0xFFF) */
- il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
- il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
- il4965_set_wr_ptrs(il, txq_id, ssn_idx);
-
- /* Set up Tx win size and frame limit for this queue */
- il_write_targ_mem(il,
- il->scd_base_addr +
- IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id),
- (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS)
- & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
-
- il_write_targ_mem(il,
- il->scd_base_addr +
- IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
- (SCD_FRAME_LIMIT <<
- IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
- IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
-
- il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));
-
- /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
- il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1);
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- return 0;
-}
-
-int
-il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u16 * ssn)
-{
- int sta_id;
- int tx_fifo;
- int txq_id;
- int ret;
- unsigned long flags;
- struct il_tid_data *tid_data;
-
- /* FIXME: warning if tx fifo not found ? */
- tx_fifo = il4965_get_fifo_from_tid(tid);
- if (unlikely(tx_fifo < 0))
- return tx_fifo;
-
- D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid);
-
- sta_id = il_sta_id(sta);
- if (sta_id == IL_INVALID_STATION) {
- IL_ERR("Start AGG on invalid station\n");
- return -ENXIO;
- }
- if (unlikely(tid >= MAX_TID_COUNT))
- return -EINVAL;
-
- if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) {
- IL_ERR("Start AGG when state is not IL_AGG_OFF !\n");
- return -ENXIO;
- }
-
- txq_id = il4965_txq_ctx_activate_free(il);
- if (txq_id == -1) {
- IL_ERR("No free aggregation queue available\n");
- return -ENXIO;
- }
-
- spin_lock_irqsave(&il->sta_lock, flags);
- tid_data = &il->stations[sta_id].tid[tid];
- *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
- tid_data->agg.txq_id = txq_id;
- il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id);
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn);
- if (ret)
- return ret;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- tid_data = &il->stations[sta_id].tid[tid];
- if (tid_data->tfds_in_queue == 0) {
- D_HT("HW queue is empty\n");
- tid_data->agg.state = IL_AGG_ON;
- ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- } else {
- D_HT("HW queue is NOT empty: %d packets in HW queue\n",
- tid_data->tfds_in_queue);
- tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA;
- }
- spin_unlock_irqrestore(&il->sta_lock, flags);
- return ret;
-}
-
-/**
- * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE
- * il->lock must be held by the caller
- */
-static int
-il4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo)
-{
- if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
- (IL49_FIRST_AMPDU_QUEUE +
- il->cfg->num_of_ampdu_queues <= txq_id)) {
- IL_WARN("queue number out of range: %d, must be %d to %d\n",
- txq_id, IL49_FIRST_AMPDU_QUEUE,
- IL49_FIRST_AMPDU_QUEUE +
- il->cfg->num_of_ampdu_queues - 1);
- return -EINVAL;
- }
-
- il4965_tx_queue_stop_scheduler(il, txq_id);
-
- il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
-
- il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
- il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
- /* supposes that ssn_idx is valid (!= 0xFFF) */
- il4965_set_wr_ptrs(il, txq_id, ssn_idx);
-
- il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));
- il_txq_ctx_deactivate(il, txq_id);
- il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0);
-
- return 0;
-}
-
-int
-il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid)
-{
- int tx_fifo_id, txq_id, sta_id, ssn;
- struct il_tid_data *tid_data;
- int write_ptr, read_ptr;
- unsigned long flags;
-
- /* FIXME: warning if tx_fifo_id not found ? */
- tx_fifo_id = il4965_get_fifo_from_tid(tid);
- if (unlikely(tx_fifo_id < 0))
- return tx_fifo_id;
-
- sta_id = il_sta_id(sta);
-
- if (sta_id == IL_INVALID_STATION) {
- IL_ERR("Invalid station for AGG tid %d\n", tid);
- return -ENXIO;
- }
-
- spin_lock_irqsave(&il->sta_lock, flags);
-
- tid_data = &il->stations[sta_id].tid[tid];
- ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
- txq_id = tid_data->agg.txq_id;
-
- switch (il->stations[sta_id].tid[tid].agg.state) {
- case IL_EMPTYING_HW_QUEUE_ADDBA:
- /*
- * This can happen if the peer stops aggregation
- * again before we've had a chance to drain the
- * queue we selected previously, i.e. before the
- * session was really started completely.
- */
- D_HT("AGG stop before setup done\n");
- goto turn_off;
- case IL_AGG_ON:
- break;
- default:
- IL_WARN("Stopping AGG while state not ON or starting\n");
- }
-
- write_ptr = il->txq[txq_id].q.write_ptr;
- read_ptr = il->txq[txq_id].q.read_ptr;
-
- /* The queue is not empty */
- if (write_ptr != read_ptr) {
- D_HT("Stopping a non empty AGG HW QUEUE\n");
- il->stations[sta_id].tid[tid].agg.state =
- IL_EMPTYING_HW_QUEUE_DELBA;
- spin_unlock_irqrestore(&il->sta_lock, flags);
- return 0;
- }
-
- D_HT("HW queue is empty\n");
-turn_off:
- il->stations[sta_id].tid[tid].agg.state = IL_AGG_OFF;
-
- /* do not restore/save irqs */
- spin_unlock(&il->sta_lock);
- spin_lock(&il->lock);
-
- /*
- * the only reason this call can fail is queue number out of range,
- * which can happen if uCode is reloaded and all the station
- * information are lost. if it is outside the range, there is no need
- * to deactivate the uCode queue, just return "success" to allow
- * mac80211 to clean up it own data.
- */
- il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo_id);
- spin_unlock_irqrestore(&il->lock, flags);
-
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-
- return 0;
-}
-
-int
-il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id)
-{
- struct il_queue *q = &il->txq[txq_id].q;
- u8 *addr = il->stations[sta_id].sta.sta.addr;
- struct il_tid_data *tid_data = &il->stations[sta_id].tid[tid];
-
- lockdep_assert_held(&il->sta_lock);
-
- switch (il->stations[sta_id].tid[tid].agg.state) {
- case IL_EMPTYING_HW_QUEUE_DELBA:
- /* We are reclaiming the last packet of the */
- /* aggregated HW queue */
- if (txq_id == tid_data->agg.txq_id &&
- q->read_ptr == q->write_ptr) {
- u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
- int tx_fifo = il4965_get_fifo_from_tid(tid);
- D_HT("HW queue empty: continue DELBA flow\n");
- il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo);
- tid_data->agg.state = IL_AGG_OFF;
- ieee80211_stop_tx_ba_cb_irqsafe(il->vif, addr, tid);
- }
- break;
- case IL_EMPTYING_HW_QUEUE_ADDBA:
- /* We are reclaiming the last packet of the queue */
- if (tid_data->tfds_in_queue == 0) {
- D_HT("HW queue empty: continue ADDBA flow\n");
- tid_data->agg.state = IL_AGG_ON;
- ieee80211_start_tx_ba_cb_irqsafe(il->vif, addr, tid);
- }
- break;
- }
-
- return 0;
-}
-
-static void
-il4965_non_agg_tx_status(struct il_priv *il, const u8 *addr1)
-{
- struct ieee80211_sta *sta;
- struct il_station_priv *sta_priv;
-
- rcu_read_lock();
- sta = ieee80211_find_sta(il->vif, addr1);
- if (sta) {
- sta_priv = (void *)sta->drv_priv;
- /* avoid atomic ops if this isn't a client */
- if (sta_priv->client &&
- atomic_dec_return(&sta_priv->pending_frames) == 0)
- ieee80211_sta_block_awake(il->hw, sta, false);
- }
- rcu_read_unlock();
-}
-
-static void
-il4965_tx_status(struct il_priv *il, struct sk_buff *skb, bool is_agg)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
- if (!is_agg)
- il4965_non_agg_tx_status(il, hdr->addr1);
-
- ieee80211_tx_status_irqsafe(il->hw, skb);
-}
-
-int
-il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx)
-{
- struct il_tx_queue *txq = &il->txq[txq_id];
- struct il_queue *q = &txq->q;
- int nfreed = 0;
- struct ieee80211_hdr *hdr;
- struct sk_buff *skb;
-
- if (idx >= q->n_bd || il_queue_used(q, idx) == 0) {
- IL_ERR("Read idx for DMA queue txq id (%d), idx %d, "
- "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd,
- q->write_ptr, q->read_ptr);
- return 0;
- }
-
- for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
- q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) {
-
- skb = txq->skbs[txq->q.read_ptr];
-
- if (WARN_ON_ONCE(skb == NULL))
- continue;
-
- hdr = (struct ieee80211_hdr *) skb->data;
- if (ieee80211_is_data_qos(hdr->frame_control))
- nfreed++;
-
- il4965_tx_status(il, skb, txq_id >= IL4965_FIRST_AMPDU_QUEUE);
-
- txq->skbs[txq->q.read_ptr] = NULL;
- il->ops->txq_free_tfd(il, txq);
- }
- return nfreed;
-}
-
-/**
- * il4965_tx_status_reply_compressed_ba - Update tx status from block-ack
- *
- * Go through block-ack's bitmap of ACK'd frames, update driver's record of
- * ACK vs. not. This gets sent to mac80211, then to rate scaling algo.
- */
-static int
-il4965_tx_status_reply_compressed_ba(struct il_priv *il, struct il_ht_agg *agg,
- struct il_compressed_ba_resp *ba_resp)
-{
- int i, sh, ack;
- u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl);
- u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
- int successes = 0;
- struct ieee80211_tx_info *info;
- u64 bitmap, sent_bitmap;
-
- if (unlikely(!agg->wait_for_ba)) {
- if (unlikely(ba_resp->bitmap))
- IL_ERR("Received BA when not expected\n");
- return -EINVAL;
- }
-
- /* Mark that the expected block-ack response arrived */
- agg->wait_for_ba = 0;
- D_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl);
-
- /* Calculate shift to align block-ack bits with our Tx win bits */
- sh = agg->start_idx - SEQ_TO_IDX(seq_ctl >> 4);
- if (sh < 0) /* tbw something is wrong with indices */
- sh += 0x100;
-
- if (agg->frame_count > (64 - sh)) {
- D_TX_REPLY("more frames than bitmap size");
- return -1;
- }
-
- /* don't use 64-bit values for now */
- bitmap = le64_to_cpu(ba_resp->bitmap) >> sh;
-
- /* check for success or failure according to the
- * transmitted bitmap and block-ack bitmap */
- sent_bitmap = bitmap & agg->bitmap;
-
- /* For each frame attempted in aggregation,
- * update driver's record of tx frame's status. */
- i = 0;
- while (sent_bitmap) {
- ack = sent_bitmap & 1ULL;
- successes += ack;
- D_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack ? "ACK" : "NACK",
- i, (agg->start_idx + i) & 0xff, agg->start_idx + i);
- sent_bitmap >>= 1;
- ++i;
- }
-
- D_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap);
-
- info = IEEE80211_SKB_CB(il->txq[scd_flow].skbs[agg->start_idx]);
- memset(&info->status, 0, sizeof(info->status));
- info->flags |= IEEE80211_TX_STAT_ACK;
- info->flags |= IEEE80211_TX_STAT_AMPDU;
- info->status.ampdu_ack_len = successes;
- info->status.ampdu_len = agg->frame_count;
- il4965_hwrate_to_tx_control(il, agg->rate_n_flags, info);
-
- return 0;
-}
-
-static inline bool
-il4965_is_tx_success(u32 status)
-{
- status &= TX_STATUS_MSK;
- return (status == TX_STATUS_SUCCESS || status == TX_STATUS_DIRECT_DONE);
-}
-
-static u8
-il4965_find_station(struct il_priv *il, const u8 *addr)
-{
- int i;
- int start = 0;
- int ret = IL_INVALID_STATION;
- unsigned long flags;
-
- if (il->iw_mode == NL80211_IFTYPE_ADHOC)
- start = IL_STA_ID;
-
- if (is_broadcast_ether_addr(addr))
- return il->hw_params.bcast_id;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- for (i = start; i < il->hw_params.max_stations; i++)
- if (il->stations[i].used &&
- ether_addr_equal(il->stations[i].sta.sta.addr, addr)) {
- ret = i;
- goto out;
- }
-
- D_ASSOC("can not find STA %pM total %d\n", addr, il->num_stations);
-
-out:
- /*
- * It may be possible that more commands interacting with stations
- * arrive before we completed processing the adding of
- * station
- */
- if (ret != IL_INVALID_STATION &&
- (!(il->stations[ret].used & IL_STA_UCODE_ACTIVE) ||
- ((il->stations[ret].used & IL_STA_UCODE_ACTIVE) &&
- (il->stations[ret].used & IL_STA_UCODE_INPROGRESS)))) {
- IL_ERR("Requested station info for sta %d before ready.\n",
- ret);
- ret = IL_INVALID_STATION;
- }
- spin_unlock_irqrestore(&il->sta_lock, flags);
- return ret;
-}
-
-static int
-il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr)
-{
- if (il->iw_mode == NL80211_IFTYPE_STATION)
- return IL_AP_ID;
- else {
- u8 *da = ieee80211_get_DA(hdr);
-
- return il4965_find_station(il, da);
- }
-}
-
-static inline u32
-il4965_get_scd_ssn(struct il4965_tx_resp *tx_resp)
-{
- return le32_to_cpup(&tx_resp->u.status +
- tx_resp->frame_count) & IEEE80211_MAX_SN;
-}
-
-static inline u32
-il4965_tx_status_to_mac80211(u32 status)
-{
- status &= TX_STATUS_MSK;
-
- switch (status) {
- case TX_STATUS_SUCCESS:
- case TX_STATUS_DIRECT_DONE:
- return IEEE80211_TX_STAT_ACK;
- case TX_STATUS_FAIL_DEST_PS:
- return IEEE80211_TX_STAT_TX_FILTERED;
- default:
- return 0;
- }
-}
-
-/**
- * il4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue
- */
-static int
-il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg,
- struct il4965_tx_resp *tx_resp, int txq_id,
- u16 start_idx)
-{
- u16 status;
- struct agg_tx_status *frame_status = tx_resp->u.agg_status;
- struct ieee80211_tx_info *info = NULL;
- struct ieee80211_hdr *hdr = NULL;
- u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
- int i, sh, idx;
- u16 seq;
- if (agg->wait_for_ba)
- D_TX_REPLY("got tx response w/o block-ack\n");
-
- agg->frame_count = tx_resp->frame_count;
- agg->start_idx = start_idx;
- agg->rate_n_flags = rate_n_flags;
- agg->bitmap = 0;
-
- /* num frames attempted by Tx command */
- if (agg->frame_count == 1) {
- /* Only one frame was attempted; no block-ack will arrive */
- status = le16_to_cpu(frame_status[0].status);
- idx = start_idx;
-
- D_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n",
- agg->frame_count, agg->start_idx, idx);
-
- info = IEEE80211_SKB_CB(il->txq[txq_id].skbs[idx]);
- info->status.rates[0].count = tx_resp->failure_frame + 1;
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- info->flags |= il4965_tx_status_to_mac80211(status);
- il4965_hwrate_to_tx_control(il, rate_n_flags, info);
-
- D_TX_REPLY("1 Frame 0x%x failure :%d\n", status & 0xff,
- tx_resp->failure_frame);
- D_TX_REPLY("Rate Info rate_n_flags=%x\n", rate_n_flags);
-
- agg->wait_for_ba = 0;
- } else {
- /* Two or more frames were attempted; expect block-ack */
- u64 bitmap = 0;
- int start = agg->start_idx;
- struct sk_buff *skb;
-
- /* Construct bit-map of pending frames within Tx win */
- for (i = 0; i < agg->frame_count; i++) {
- u16 sc;
- status = le16_to_cpu(frame_status[i].status);
- seq = le16_to_cpu(frame_status[i].sequence);
- idx = SEQ_TO_IDX(seq);
- txq_id = SEQ_TO_QUEUE(seq);
-
- if (status &
- (AGG_TX_STATE_FEW_BYTES_MSK |
- AGG_TX_STATE_ABORT_MSK))
- continue;
-
- D_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n",
- agg->frame_count, txq_id, idx);
-
- skb = il->txq[txq_id].skbs[idx];
- if (WARN_ON_ONCE(skb == NULL))
- return -1;
- hdr = (struct ieee80211_hdr *) skb->data;
-
- sc = le16_to_cpu(hdr->seq_ctrl);
- if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) {
- IL_ERR("BUG_ON idx doesn't match seq control"
- " idx=%d, seq_idx=%d, seq=%d\n", idx,
- IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl);
- return -1;
- }
-
- D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx,
- IEEE80211_SEQ_TO_SN(sc));
-
- sh = idx - start;
- if (sh > 64) {
- sh = (start - idx) + 0xff;
- bitmap = bitmap << sh;
- sh = 0;
- start = idx;
- } else if (sh < -64)
- sh = 0xff - (start - idx);
- else if (sh < 0) {
- sh = start - idx;
- start = idx;
- bitmap = bitmap << sh;
- sh = 0;
- }
- bitmap |= 1ULL << sh;
- D_TX_REPLY("start=%d bitmap=0x%llx\n", start,
- (unsigned long long)bitmap);
- }
-
- agg->bitmap = bitmap;
- agg->start_idx = start;
- D_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n",
- agg->frame_count, agg->start_idx,
- (unsigned long long)agg->bitmap);
-
- if (bitmap)
- agg->wait_for_ba = 1;
- }
- return 0;
-}
-
-/**
- * il4965_hdl_tx - Handle standard (non-aggregation) Tx response
- */
-static void
-il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- int txq_id = SEQ_TO_QUEUE(sequence);
- int idx = SEQ_TO_IDX(sequence);
- struct il_tx_queue *txq = &il->txq[txq_id];
- struct sk_buff *skb;
- struct ieee80211_hdr *hdr;
- struct ieee80211_tx_info *info;
- struct il4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
- u32 status = le32_to_cpu(tx_resp->u.status);
- int uninitialized_var(tid);
- int sta_id;
- int freed;
- u8 *qc = NULL;
- unsigned long flags;
-
- if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) {
- IL_ERR("Read idx for DMA queue txq_id (%d) idx %d "
- "is out of range [0-%d] %d %d\n", txq_id, idx,
- txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr);
- return;
- }
-
- txq->time_stamp = jiffies;
-
- skb = txq->skbs[txq->q.read_ptr];
- info = IEEE80211_SKB_CB(skb);
- memset(&info->status, 0, sizeof(info->status));
-
- hdr = (struct ieee80211_hdr *) skb->data;
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
- }
-
- sta_id = il4965_get_ra_sta_id(il, hdr);
- if (txq->sched_retry && unlikely(sta_id == IL_INVALID_STATION)) {
- IL_ERR("Station not known\n");
- 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);
- struct il_ht_agg *agg = NULL;
- WARN_ON(!qc);
-
- agg = &il->stations[sta_id].tid[tid].agg;
-
- il4965_tx_status_reply_tx(il, agg, tx_resp, txq_id, idx);
-
- /* check if BAR is needed */
- if (tx_resp->frame_count == 1 &&
- !il4965_is_tx_success(status))
- info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
-
- if (txq->q.read_ptr != (scd_ssn & 0xff)) {
- idx = il_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
- D_TX_REPLY("Retry scheduler reclaim scd_ssn "
- "%d idx %d\n", scd_ssn, idx);
- freed = il4965_tx_queue_reclaim(il, txq_id, idx);
- if (qc)
- il4965_free_tfds_in_queue(il, sta_id, tid,
- freed);
-
- if (il->mac80211_registered &&
- il_queue_space(&txq->q) > txq->q.low_mark &&
- agg->state != IL_EMPTYING_HW_QUEUE_DELBA)
- il_wake_queue(il, txq);
- }
- } else {
- info->status.rates[0].count = tx_resp->failure_frame + 1;
- info->flags |= il4965_tx_status_to_mac80211(status);
- il4965_hwrate_to_tx_control(il,
- le32_to_cpu(tx_resp->rate_n_flags),
- info);
-
- D_TX_REPLY("TXQ %d status %s (0x%08x) "
- "rate_n_flags 0x%x retries %d\n", txq_id,
- il4965_get_tx_fail_reason(status), status,
- le32_to_cpu(tx_resp->rate_n_flags),
- tx_resp->failure_frame);
-
- freed = il4965_tx_queue_reclaim(il, txq_id, idx);
- if (qc && likely(sta_id != IL_INVALID_STATION))
- il4965_free_tfds_in_queue(il, sta_id, tid, freed);
- else if (sta_id == IL_INVALID_STATION)
- D_TX_REPLY("Station not known\n");
-
- if (il->mac80211_registered &&
- il_queue_space(&txq->q) > txq->q.low_mark)
- il_wake_queue(il, txq);
- }
- if (qc && likely(sta_id != IL_INVALID_STATION))
- il4965_txq_check_empty(il, sta_id, tid, txq_id);
-
- il4965_check_abort_status(il, tx_resp->frame_count, status);
-
- spin_unlock_irqrestore(&il->sta_lock, flags);
-}
-
-/**
- * translate ucode response to mac80211 tx status control values
- */
-void
-il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags,
- struct ieee80211_tx_info *info)
-{
- struct ieee80211_tx_rate *r = &info->status.rates[0];
-
- info->status.antenna =
- ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
- if (rate_n_flags & RATE_MCS_HT_MSK)
- r->flags |= IEEE80211_TX_RC_MCS;
- if (rate_n_flags & RATE_MCS_GF_MSK)
- r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
- if (rate_n_flags & RATE_MCS_HT40_MSK)
- r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
- if (rate_n_flags & RATE_MCS_DUP_MSK)
- r->flags |= IEEE80211_TX_RC_DUP_DATA;
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- r->flags |= IEEE80211_TX_RC_SHORT_GI;
- r->idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, info->band);
-}
-
-/**
- * il4965_hdl_compressed_ba - Handler for N_COMPRESSED_BA
- *
- * Handles block-acknowledge notification from device, which reports success
- * of frames sent via aggregation.
- */
-static void
-il4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
- struct il_tx_queue *txq = NULL;
- struct il_ht_agg *agg;
- int idx;
- int sta_id;
- int tid;
- unsigned long flags;
-
- /* "flow" corresponds to Tx queue */
- u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
-
- /* "ssn" is start of block-ack Tx win, corresponds to idx
- * (in Tx queue's circular buffer) of first TFD/frame in win */
- u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
-
- if (scd_flow >= il->hw_params.max_txq_num) {
- IL_ERR("BUG_ON scd_flow is bigger than number of queues\n");
- return;
- }
-
- txq = &il->txq[scd_flow];
- sta_id = ba_resp->sta_id;
- tid = ba_resp->tid;
- agg = &il->stations[sta_id].tid[tid].agg;
- if (unlikely(agg->txq_id != scd_flow)) {
- /*
- * FIXME: this is a uCode bug which need to be addressed,
- * log the information and return for now!
- * since it is possible happen very often and in order
- * not to fill the syslog, don't enable the logging by default
- */
- D_TX_REPLY("BA scd_flow %d does not match txq_id %d\n",
- scd_flow, agg->txq_id);
- return;
- }
-
- /* Find idx just before block-ack win */
- idx = il_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
-
- spin_lock_irqsave(&il->sta_lock, flags);
-
- D_TX_REPLY("N_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n",
- agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32,
- ba_resp->sta_id);
- D_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx," "scd_flow = "
- "%d, scd_ssn = %d\n", ba_resp->tid, ba_resp->seq_ctl,
- (unsigned long long)le64_to_cpu(ba_resp->bitmap),
- ba_resp->scd_flow, ba_resp->scd_ssn);
- D_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx\n", agg->start_idx,
- (unsigned long long)agg->bitmap);
-
- /* Update driver's record of ACK vs. not for each frame in win */
- il4965_tx_status_reply_compressed_ba(il, agg, ba_resp);
-
- /* Release all TFDs before the SSN, i.e. all TFDs in front of
- * block-ack win (we assume that they've been successfully
- * transmitted ... if not, it's too late anyway). */
- if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
- /* calculate mac80211 ampdu sw queue to wake */
- int freed = il4965_tx_queue_reclaim(il, scd_flow, idx);
- il4965_free_tfds_in_queue(il, sta_id, tid, freed);
-
- if (il_queue_space(&txq->q) > txq->q.low_mark &&
- il->mac80211_registered &&
- agg->state != IL_EMPTYING_HW_QUEUE_DELBA)
- il_wake_queue(il, txq);
-
- il4965_txq_check_empty(il, sta_id, tid, scd_flow);
- }
-
- spin_unlock_irqrestore(&il->sta_lock, flags);
-}
-
-#ifdef CONFIG_IWLEGACY_DEBUG
-const char *
-il4965_get_tx_fail_reason(u32 status)
-{
-#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
-#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
-
- switch (status & TX_STATUS_MSK) {
- case TX_STATUS_SUCCESS:
- return "SUCCESS";
- TX_STATUS_POSTPONE(DELAY);
- TX_STATUS_POSTPONE(FEW_BYTES);
- TX_STATUS_POSTPONE(QUIET_PERIOD);
- TX_STATUS_POSTPONE(CALC_TTAK);
- TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
- TX_STATUS_FAIL(SHORT_LIMIT);
- TX_STATUS_FAIL(LONG_LIMIT);
- TX_STATUS_FAIL(FIFO_UNDERRUN);
- TX_STATUS_FAIL(DRAIN_FLOW);
- TX_STATUS_FAIL(RFKILL_FLUSH);
- TX_STATUS_FAIL(LIFE_EXPIRE);
- TX_STATUS_FAIL(DEST_PS);
- TX_STATUS_FAIL(HOST_ABORTED);
- TX_STATUS_FAIL(BT_RETRY);
- TX_STATUS_FAIL(STA_INVALID);
- TX_STATUS_FAIL(FRAG_DROPPED);
- TX_STATUS_FAIL(TID_DISABLE);
- TX_STATUS_FAIL(FIFO_FLUSHED);
- TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
- TX_STATUS_FAIL(PASSIVE_NO_RX);
- TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
- }
-
- return "UNKNOWN";
-
-#undef TX_STATUS_FAIL
-#undef TX_STATUS_POSTPONE
-}
-#endif /* CONFIG_IWLEGACY_DEBUG */
-
-static struct il_link_quality_cmd *
-il4965_sta_alloc_lq(struct il_priv *il, u8 sta_id)
-{
- int i, r;
- struct il_link_quality_cmd *link_cmd;
- u32 rate_flags = 0;
- __le32 rate_n_flags;
-
- link_cmd = kzalloc(sizeof(struct il_link_quality_cmd), GFP_KERNEL);
- if (!link_cmd) {
- IL_ERR("Unable to allocate memory for LQ cmd.\n");
- return NULL;
- }
- /* Set up the rate scaling to start at selected rate, fall back
- * all the way down to 1M in IEEE order, and then spin on 1M */
- if (il->band == IEEE80211_BAND_5GHZ)
- r = RATE_6M_IDX;
- else
- r = RATE_1M_IDX;
-
- if (r >= IL_FIRST_CCK_RATE && r <= IL_LAST_CCK_RATE)
- rate_flags |= RATE_MCS_CCK_MSK;
-
- rate_flags |=
- il4965_first_antenna(il->hw_params.
- valid_tx_ant) << RATE_MCS_ANT_POS;
- rate_n_flags = cpu_to_le32(il_rates[r].plcp | rate_flags);
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
- link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
-
- link_cmd->general_params.single_stream_ant_msk =
- il4965_first_antenna(il->hw_params.valid_tx_ant);
-
- link_cmd->general_params.dual_stream_ant_msk =
- il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params.
- valid_tx_ant);
- if (!link_cmd->general_params.dual_stream_ant_msk) {
- link_cmd->general_params.dual_stream_ant_msk = ANT_AB;
- } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) {
- link_cmd->general_params.dual_stream_ant_msk =
- il->hw_params.valid_tx_ant;
- }
-
- link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
- link_cmd->agg_params.agg_time_limit =
- cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
-
- link_cmd->sta_id = sta_id;
-
- return link_cmd;
-}
-
-/*
- * il4965_add_bssid_station - Add the special IBSS BSSID station
- *
- * Function sleeps.
- */
-int
-il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r)
-{
- int ret;
- u8 sta_id;
- struct il_link_quality_cmd *link_cmd;
- unsigned long flags;
-
- if (sta_id_r)
- *sta_id_r = IL_INVALID_STATION;
-
- ret = il_add_station_common(il, addr, 0, NULL, &sta_id);
- if (ret) {
- IL_ERR("Unable to add station %pM\n", addr);
- return ret;
- }
-
- if (sta_id_r)
- *sta_id_r = sta_id;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].used |= IL_STA_LOCAL;
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- /* Set up default rate scaling table in device's station table */
- link_cmd = il4965_sta_alloc_lq(il, sta_id);
- if (!link_cmd) {
- IL_ERR("Unable to initialize rate scaling for station %pM.\n",
- addr);
- return -ENOMEM;
- }
-
- ret = il_send_lq_cmd(il, link_cmd, CMD_SYNC, true);
- if (ret)
- IL_ERR("Link quality command failed (%d)\n", ret);
-
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].lq = link_cmd;
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return 0;
-}
-
-static int
-il4965_static_wepkey_cmd(struct il_priv *il, bool send_if_empty)
-{
- int i;
- u8 buff[sizeof(struct il_wep_cmd) +
- sizeof(struct il_wep_key) * WEP_KEYS_MAX];
- struct il_wep_cmd *wep_cmd = (struct il_wep_cmd *)buff;
- size_t cmd_size = sizeof(struct il_wep_cmd);
- struct il_host_cmd cmd = {
- .id = C_WEPKEY,
- .data = wep_cmd,
- .flags = CMD_SYNC,
- };
- bool not_empty = false;
-
- might_sleep();
-
- memset(wep_cmd, 0,
- cmd_size + (sizeof(struct il_wep_key) * WEP_KEYS_MAX));
-
- for (i = 0; i < WEP_KEYS_MAX; i++) {
- u8 key_size = il->_4965.wep_keys[i].key_size;
-
- wep_cmd->key[i].key_idx = i;
- if (key_size) {
- wep_cmd->key[i].key_offset = i;
- not_empty = true;
- } else
- wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET;
-
- wep_cmd->key[i].key_size = key_size;
- memcpy(&wep_cmd->key[i].key[3], il->_4965.wep_keys[i].key, key_size);
- }
-
- wep_cmd->global_key_type = WEP_KEY_WEP_TYPE;
- wep_cmd->num_keys = WEP_KEYS_MAX;
-
- cmd_size += sizeof(struct il_wep_key) * WEP_KEYS_MAX;
- cmd.len = cmd_size;
-
- if (not_empty || send_if_empty)
- return il_send_cmd(il, &cmd);
- else
- return 0;
-}
-
-int
-il4965_restore_default_wep_keys(struct il_priv *il)
-{
- lockdep_assert_held(&il->mutex);
-
- return il4965_static_wepkey_cmd(il, false);
-}
-
-int
-il4965_remove_default_wep_key(struct il_priv *il,
- struct ieee80211_key_conf *keyconf)
-{
- int ret;
- int idx = keyconf->keyidx;
-
- lockdep_assert_held(&il->mutex);
-
- D_WEP("Removing default WEP key: idx=%d\n", idx);
-
- memset(&il->_4965.wep_keys[idx], 0, sizeof(struct il_wep_key));
- if (il_is_rfkill(il)) {
- D_WEP("Not sending C_WEPKEY command due to RFKILL.\n");
- /* but keys in device are clear anyway so return success */
- return 0;
- }
- ret = il4965_static_wepkey_cmd(il, 1);
- D_WEP("Remove default WEP key: idx=%d ret=%d\n", idx, ret);
-
- return ret;
-}
-
-int
-il4965_set_default_wep_key(struct il_priv *il,
- struct ieee80211_key_conf *keyconf)
-{
- int ret;
- int len = keyconf->keylen;
- int idx = keyconf->keyidx;
-
- lockdep_assert_held(&il->mutex);
-
- if (len != WEP_KEY_LEN_128 && len != WEP_KEY_LEN_64) {
- D_WEP("Bad WEP key length %d\n", keyconf->keylen);
- return -EINVAL;
- }
-
- keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
- keyconf->hw_key_idx = HW_KEY_DEFAULT;
- il->stations[IL_AP_ID].keyinfo.cipher = keyconf->cipher;
-
- il->_4965.wep_keys[idx].key_size = len;
- memcpy(&il->_4965.wep_keys[idx].key, &keyconf->key, len);
-
- ret = il4965_static_wepkey_cmd(il, false);
-
- D_WEP("Set default WEP key: len=%d idx=%d ret=%d\n", len, idx, ret);
- return ret;
-}
-
-static int
-il4965_set_wep_dynamic_key_info(struct il_priv *il,
- struct ieee80211_key_conf *keyconf, u8 sta_id)
-{
- unsigned long flags;
- __le16 key_flags = 0;
- struct il_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&il->mutex);
-
- keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
-
- key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
-
- if (keyconf->keylen == WEP_KEY_LEN_128)
- key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
-
- if (sta_id == il->hw_params.bcast_id)
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- spin_lock_irqsave(&il->sta_lock, flags);
-
- il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
- il->stations[sta_id].keyinfo.keylen = keyconf->keylen;
- il->stations[sta_id].keyinfo.keyidx = keyconf->keyidx;
-
- memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen);
-
- memcpy(&il->stations[sta_id].sta.key.key[3], keyconf->key,
- keyconf->keylen);
-
- if ((il->stations[sta_id].sta.key.
- key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
- il->stations[sta_id].sta.key.key_offset =
- il_get_free_ucode_key_idx(il);
- /* else, we are overriding an existing key => no need to allocated room
- * in uCode. */
-
- WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
- "no space for a new key");
-
- il->stations[sta_id].sta.key.key_flags = key_flags;
- il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
-
- memcpy(&sta_cmd, &il->stations[sta_id].sta,
- sizeof(struct il_addsta_cmd));
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
-}
-
-static int
-il4965_set_ccmp_dynamic_key_info(struct il_priv *il,
- struct ieee80211_key_conf *keyconf, u8 sta_id)
-{
- unsigned long flags;
- __le16 key_flags = 0;
- struct il_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&il->mutex);
-
- key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
-
- if (sta_id == il->hw_params.bcast_id)
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
- il->stations[sta_id].keyinfo.keylen = keyconf->keylen;
-
- memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen);
-
- memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen);
-
- if ((il->stations[sta_id].sta.key.
- key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
- il->stations[sta_id].sta.key.key_offset =
- il_get_free_ucode_key_idx(il);
- /* else, we are overriding an existing key => no need to allocated room
- * in uCode. */
-
- WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
- "no space for a new key");
-
- il->stations[sta_id].sta.key.key_flags = key_flags;
- il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
-
- memcpy(&sta_cmd, &il->stations[sta_id].sta,
- sizeof(struct il_addsta_cmd));
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
-}
-
-static int
-il4965_set_tkip_dynamic_key_info(struct il_priv *il,
- struct ieee80211_key_conf *keyconf, u8 sta_id)
-{
- unsigned long flags;
- int ret = 0;
- __le16 key_flags = 0;
-
- key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
-
- if (sta_id == il->hw_params.bcast_id)
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
-
- spin_lock_irqsave(&il->sta_lock, flags);
-
- il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
- il->stations[sta_id].keyinfo.keylen = 16;
-
- if ((il->stations[sta_id].sta.key.
- key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
- il->stations[sta_id].sta.key.key_offset =
- il_get_free_ucode_key_idx(il);
- /* else, we are overriding an existing key => no need to allocated room
- * in uCode. */
-
- WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
- "no space for a new key");
-
- il->stations[sta_id].sta.key.key_flags = key_flags;
-
- /* This copy is acutally not needed: we get the key with each TX */
- memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, 16);
-
- memcpy(il->stations[sta_id].sta.key.key, keyconf->key, 16);
-
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return ret;
-}
-
-void
-il4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta, u32 iv32, u16 *phase1key)
-{
- u8 sta_id;
- unsigned long flags;
- int i;
-
- if (il_scan_cancel(il)) {
- /* cancel scan failed, just live w/ bad key and rely
- briefly on SW decryption */
- return;
- }
-
- sta_id = il_sta_id_or_broadcast(il, sta);
- if (sta_id == IL_INVALID_STATION)
- return;
-
- spin_lock_irqsave(&il->sta_lock, flags);
-
- il->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32;
-
- for (i = 0; i < 5; i++)
- il->stations[sta_id].sta.key.tkip_rx_ttak[i] =
- cpu_to_le16(phase1key[i]);
-
- il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
-
- il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC);
-
- spin_unlock_irqrestore(&il->sta_lock, flags);
-}
-
-int
-il4965_remove_dynamic_key(struct il_priv *il,
- struct ieee80211_key_conf *keyconf, u8 sta_id)
-{
- unsigned long flags;
- u16 key_flags;
- u8 keyidx;
- struct il_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&il->mutex);
-
- il->_4965.key_mapping_keys--;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- key_flags = le16_to_cpu(il->stations[sta_id].sta.key.key_flags);
- keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3;
-
- D_WEP("Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id);
-
- if (keyconf->keyidx != keyidx) {
- /* We need to remove a key with idx different that the one
- * in the uCode. This means that the key we need to remove has
- * been replaced by another one with different idx.
- * Don't do anything and return ok
- */
- spin_unlock_irqrestore(&il->sta_lock, flags);
- return 0;
- }
-
- if (il->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_INVALID) {
- IL_WARN("Removing wrong key %d 0x%x\n", keyconf->keyidx,
- key_flags);
- spin_unlock_irqrestore(&il->sta_lock, flags);
- return 0;
- }
-
- if (!test_and_clear_bit
- (il->stations[sta_id].sta.key.key_offset, &il->ucode_key_table))
- IL_ERR("idx %d not used in uCode key table.\n",
- il->stations[sta_id].sta.key.key_offset);
- memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key));
- memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo));
- il->stations[sta_id].sta.key.key_flags =
- STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID;
- il->stations[sta_id].sta.key.key_offset = keyconf->hw_key_idx;
- il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
-
- if (il_is_rfkill(il)) {
- D_WEP
- ("Not sending C_ADD_STA command because RFKILL enabled.\n");
- spin_unlock_irqrestore(&il->sta_lock, flags);
- return 0;
- }
- memcpy(&sta_cmd, &il->stations[sta_id].sta,
- sizeof(struct il_addsta_cmd));
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
-}
-
-int
-il4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf,
- u8 sta_id)
-{
- int ret;
-
- lockdep_assert_held(&il->mutex);
-
- il->_4965.key_mapping_keys++;
- keyconf->hw_key_idx = HW_KEY_DYNAMIC;
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- ret =
- il4965_set_ccmp_dynamic_key_info(il, keyconf, sta_id);
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- ret =
- il4965_set_tkip_dynamic_key_info(il, keyconf, sta_id);
- break;
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- ret = il4965_set_wep_dynamic_key_info(il, keyconf, sta_id);
- break;
- default:
- IL_ERR("Unknown alg: %s cipher = %x\n", __func__,
- keyconf->cipher);
- ret = -EINVAL;
- }
-
- D_WEP("Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n",
- keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret);
-
- return ret;
-}
-
-/**
- * il4965_alloc_bcast_station - add broadcast station into driver's station table.
- *
- * This adds the broadcast station into the driver's station table
- * and marks it driver active, so that it will be restored to the
- * device at the next best time.
- */
-int
-il4965_alloc_bcast_station(struct il_priv *il)
-{
- struct il_link_quality_cmd *link_cmd;
- unsigned long flags;
- u8 sta_id;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- sta_id = il_prep_station(il, il_bcast_addr, false, NULL);
- if (sta_id == IL_INVALID_STATION) {
- IL_ERR("Unable to prepare broadcast station\n");
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return -EINVAL;
- }
-
- il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE;
- il->stations[sta_id].used |= IL_STA_BCAST;
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- link_cmd = il4965_sta_alloc_lq(il, sta_id);
- if (!link_cmd) {
- IL_ERR
- ("Unable to initialize rate scaling for bcast station.\n");
- return -ENOMEM;
- }
-
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].lq = link_cmd;
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return 0;
-}
-
-/**
- * il4965_update_bcast_station - update broadcast station's LQ command
- *
- * Only used by iwl4965. Placed here to have all bcast station management
- * code together.
- */
-static int
-il4965_update_bcast_station(struct il_priv *il)
-{
- unsigned long flags;
- struct il_link_quality_cmd *link_cmd;
- u8 sta_id = il->hw_params.bcast_id;
-
- link_cmd = il4965_sta_alloc_lq(il, sta_id);
- if (!link_cmd) {
- IL_ERR("Unable to initialize rate scaling for bcast sta.\n");
- return -ENOMEM;
- }
-
- spin_lock_irqsave(&il->sta_lock, flags);
- if (il->stations[sta_id].lq)
- kfree(il->stations[sta_id].lq);
- else
- D_INFO("Bcast sta rate scaling has not been initialized.\n");
- il->stations[sta_id].lq = link_cmd;
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return 0;
-}
-
-int
-il4965_update_bcast_stations(struct il_priv *il)
-{
- return il4965_update_bcast_station(il);
-}
-
-/**
- * il4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
- */
-int
-il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid)
-{
- unsigned long flags;
- struct il_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&il->mutex);
-
- /* Remove "disable" flag, to enable Tx for this TID */
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
- il->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- memcpy(&sta_cmd, &il->stations[sta_id].sta,
- sizeof(struct il_addsta_cmd));
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
-}
-
-int
-il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid,
- u16 ssn)
-{
- unsigned long flags;
- int sta_id;
- struct il_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&il->mutex);
-
- sta_id = il_sta_id(sta);
- if (sta_id == IL_INVALID_STATION)
- return -ENXIO;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].sta.station_flags_msk = 0;
- il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
- il->stations[sta_id].sta.add_immediate_ba_tid = (u8) tid;
- il->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- memcpy(&sta_cmd, &il->stations[sta_id].sta,
- sizeof(struct il_addsta_cmd));
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
-}
-
-int
-il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid)
-{
- unsigned long flags;
- int sta_id;
- struct il_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&il->mutex);
-
- sta_id = il_sta_id(sta);
- if (sta_id == IL_INVALID_STATION) {
- IL_ERR("Invalid station for AGG tid %d\n", tid);
- return -ENXIO;
- }
-
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].sta.station_flags_msk = 0;
- il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
- il->stations[sta_id].sta.remove_immediate_ba_tid = (u8) tid;
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- memcpy(&sta_cmd, &il->stations[sta_id].sta,
- sizeof(struct il_addsta_cmd));
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
-}
-
-void
-il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- il->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK;
- il->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
- il->stations[sta_id].sta.sta.modify_mask =
- STA_MODIFY_SLEEP_TX_COUNT_MSK;
- il->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt);
- il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC);
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
-}
-
-void
-il4965_update_chain_flags(struct il_priv *il)
-{
- if (il->ops->set_rxon_chain) {
- il->ops->set_rxon_chain(il);
- if (il->active.rx_chain != il->staging.rx_chain)
- il_commit_rxon(il);
- }
-}
-
-static void
-il4965_clear_free_frames(struct il_priv *il)
-{
- struct list_head *element;
-
- D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count);
-
- while (!list_empty(&il->free_frames)) {
- element = il->free_frames.next;
- list_del(element);
- kfree(list_entry(element, struct il_frame, list));
- il->frames_count--;
- }
-
- if (il->frames_count) {
- IL_WARN("%d frames still in use. Did we lose one?\n",
- il->frames_count);
- il->frames_count = 0;
- }
-}
-
-static struct il_frame *
-il4965_get_free_frame(struct il_priv *il)
-{
- struct il_frame *frame;
- struct list_head *element;
- if (list_empty(&il->free_frames)) {
- frame = kzalloc(sizeof(*frame), GFP_KERNEL);
- if (!frame) {
- IL_ERR("Could not allocate frame!\n");
- return NULL;
- }
-
- il->frames_count++;
- return frame;
- }
-
- element = il->free_frames.next;
- list_del(element);
- return list_entry(element, struct il_frame, list);
-}
-
-static void
-il4965_free_frame(struct il_priv *il, struct il_frame *frame)
-{
- memset(frame, 0, sizeof(*frame));
- list_add(&frame->list, &il->free_frames);
-}
-
-static u32
-il4965_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr,
- int left)
-{
- lockdep_assert_held(&il->mutex);
-
- if (!il->beacon_skb)
- return 0;
-
- if (il->beacon_skb->len > left)
- return 0;
-
- memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len);
-
- return il->beacon_skb->len;
-}
-
-/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
-static void
-il4965_set_beacon_tim(struct il_priv *il,
- struct il_tx_beacon_cmd *tx_beacon_cmd, u8 * beacon,
- u32 frame_size)
-{
- u16 tim_idx;
- struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
-
- /*
- * The idx is relative to frame start but we start looking at the
- * variable-length part of the beacon.
- */
- tim_idx = mgmt->u.beacon.variable - beacon;
-
- /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
- while ((tim_idx < (frame_size - 2)) &&
- (beacon[tim_idx] != WLAN_EID_TIM))
- tim_idx += beacon[tim_idx + 1] + 2;
-
- /* If TIM field was found, set variables */
- if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
- tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
- tx_beacon_cmd->tim_size = beacon[tim_idx + 1];
- } else
- IL_WARN("Unable to find TIM Element in beacon\n");
-}
-
-static unsigned int
-il4965_hw_get_beacon_cmd(struct il_priv *il, struct il_frame *frame)
-{
- struct il_tx_beacon_cmd *tx_beacon_cmd;
- u32 frame_size;
- u32 rate_flags;
- u32 rate;
- /*
- * We have to set up the TX command, the TX Beacon command, and the
- * beacon contents.
- */
-
- lockdep_assert_held(&il->mutex);
-
- if (!il->beacon_enabled) {
- IL_ERR("Trying to build beacon without beaconing enabled\n");
- return 0;
- }
-
- /* Initialize memory */
- tx_beacon_cmd = &frame->u.beacon;
- memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
-
- /* Set up TX beacon contents */
- frame_size =
- il4965_fill_beacon_frame(il, tx_beacon_cmd->frame,
- sizeof(frame->u) - sizeof(*tx_beacon_cmd));
- if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE))
- return 0;
- if (!frame_size)
- return 0;
-
- /* Set up TX command fields */
- tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size);
- tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id;
- tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
- tx_beacon_cmd->tx.tx_flags =
- TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK |
- TX_CMD_FLG_STA_RATE_MSK;
-
- /* Set up TX beacon command fields */
- il4965_set_beacon_tim(il, tx_beacon_cmd, (u8 *) tx_beacon_cmd->frame,
- frame_size);
-
- /* Set up packet rate and flags */
- rate = il_get_lowest_plcp(il);
- il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant);
- rate_flags = BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS;
- if ((rate >= IL_FIRST_CCK_RATE) && (rate <= IL_LAST_CCK_RATE))
- rate_flags |= RATE_MCS_CCK_MSK;
- tx_beacon_cmd->tx.rate_n_flags = cpu_to_le32(rate | rate_flags);
-
- return sizeof(*tx_beacon_cmd) + frame_size;
-}
-
-int
-il4965_send_beacon_cmd(struct il_priv *il)
-{
- struct il_frame *frame;
- unsigned int frame_size;
- int rc;
-
- frame = il4965_get_free_frame(il);
- if (!frame) {
- IL_ERR("Could not obtain free frame buffer for beacon "
- "command.\n");
- return -ENOMEM;
- }
-
- frame_size = il4965_hw_get_beacon_cmd(il, frame);
- if (!frame_size) {
- IL_ERR("Error configuring the beacon command\n");
- il4965_free_frame(il, frame);
- return -EINVAL;
- }
-
- rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]);
-
- il4965_free_frame(il, frame);
-
- return rc;
-}
-
-static inline dma_addr_t
-il4965_tfd_tb_get_addr(struct il_tfd *tfd, u8 idx)
-{
- struct il_tfd_tb *tb = &tfd->tbs[idx];
-
- dma_addr_t addr = get_unaligned_le32(&tb->lo);
- if (sizeof(dma_addr_t) > sizeof(u32))
- addr |=
- ((dma_addr_t) (le16_to_cpu(tb->hi_n_len) & 0xF) << 16) <<
- 16;
-
- return addr;
-}
-
-static inline u16
-il4965_tfd_tb_get_len(struct il_tfd *tfd, u8 idx)
-{
- struct il_tfd_tb *tb = &tfd->tbs[idx];
-
- return le16_to_cpu(tb->hi_n_len) >> 4;
-}
-
-static inline void
-il4965_tfd_set_tb(struct il_tfd *tfd, u8 idx, dma_addr_t addr, u16 len)
-{
- struct il_tfd_tb *tb = &tfd->tbs[idx];
- u16 hi_n_len = len << 4;
-
- put_unaligned_le32(addr, &tb->lo);
- if (sizeof(dma_addr_t) > sizeof(u32))
- hi_n_len |= ((addr >> 16) >> 16) & 0xF;
-
- tb->hi_n_len = cpu_to_le16(hi_n_len);
-
- tfd->num_tbs = idx + 1;
-}
-
-static inline u8
-il4965_tfd_get_num_tbs(struct il_tfd *tfd)
-{
- return tfd->num_tbs & 0x1f;
-}
-
-/**
- * il4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
- * @il - driver ilate data
- * @txq - tx queue
- *
- * Does NOT advance any TFD circular buffer read/write idxes
- * Does NOT free the TFD itself (which is within circular buffer)
- */
-void
-il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq)
-{
- struct il_tfd *tfd_tmp = (struct il_tfd *)txq->tfds;
- struct il_tfd *tfd;
- struct pci_dev *dev = il->pci_dev;
- int idx = txq->q.read_ptr;
- int i;
- int num_tbs;
-
- tfd = &tfd_tmp[idx];
-
- /* Sanity check on number of chunks */
- num_tbs = il4965_tfd_get_num_tbs(tfd);
-
- if (num_tbs >= IL_NUM_OF_TBS) {
- IL_ERR("Too many chunks: %i\n", num_tbs);
- /* @todo issue fatal error, it is quite serious situation */
- return;
- }
-
- /* Unmap tx_cmd */
- if (num_tbs)
- pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping),
- dma_unmap_len(&txq->meta[idx], len),
- PCI_DMA_BIDIRECTIONAL);
-
- /* Unmap chunks, if any. */
- for (i = 1; i < num_tbs; i++)
- pci_unmap_single(dev, il4965_tfd_tb_get_addr(tfd, i),
- il4965_tfd_tb_get_len(tfd, i),
- PCI_DMA_TODEVICE);
-
- /* free SKB */
- if (txq->skbs) {
- struct sk_buff *skb = txq->skbs[txq->q.read_ptr];
-
- /* can be called from irqs-disabled context */
- if (skb) {
- dev_kfree_skb_any(skb);
- txq->skbs[txq->q.read_ptr] = NULL;
- }
- }
-}
-
-int
-il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq,
- dma_addr_t addr, u16 len, u8 reset, u8 pad)
-{
- struct il_queue *q;
- struct il_tfd *tfd, *tfd_tmp;
- u32 num_tbs;
-
- q = &txq->q;
- tfd_tmp = (struct il_tfd *)txq->tfds;
- tfd = &tfd_tmp[q->write_ptr];
-
- if (reset)
- memset(tfd, 0, sizeof(*tfd));
-
- num_tbs = il4965_tfd_get_num_tbs(tfd);
-
- /* Each TFD can point to a maximum 20 Tx buffers */
- if (num_tbs >= IL_NUM_OF_TBS) {
- IL_ERR("Error can not send more than %d chunks\n",
- IL_NUM_OF_TBS);
- return -EINVAL;
- }
-
- BUG_ON(addr & ~DMA_BIT_MASK(36));
- if (unlikely(addr & ~IL_TX_DMA_MASK))
- IL_ERR("Unaligned address = %llx\n", (unsigned long long)addr);
-
- il4965_tfd_set_tb(tfd, num_tbs, addr, len);
-
- return 0;
-}
-
-/*
- * Tell nic where to find circular buffer of Tx Frame Descriptors for
- * given Tx queue, and enable the DMA channel used for that queue.
- *
- * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA
- * channels supported in hardware.
- */
-int
-il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq)
-{
- int txq_id = txq->q.id;
-
- /* Circular buffer (TFD queue in DRAM) physical base address */
- il_wr(il, FH49_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8);
-
- return 0;
-}
-
-/******************************************************************************
- *
- * Generic RX handler implementations
- *
- ******************************************************************************/
-static void
-il4965_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_alive_resp *palive;
- struct delayed_work *pwork;
-
- palive = &pkt->u.alive_frame;
-
- D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n",
- palive->is_valid, palive->ver_type, palive->ver_subtype);
-
- if (palive->ver_subtype == INITIALIZE_SUBTYPE) {
- D_INFO("Initialization Alive received.\n");
- memcpy(&il->card_alive_init, &pkt->u.alive_frame,
- sizeof(struct il_init_alive_resp));
- pwork = &il->init_alive_start;
- } else {
- D_INFO("Runtime Alive received.\n");
- memcpy(&il->card_alive, &pkt->u.alive_frame,
- sizeof(struct il_alive_resp));
- pwork = &il->alive_start;
- }
-
- /* We delay the ALIVE response by 5ms to
- * give the HW RF Kill time to activate... */
- if (palive->is_valid == UCODE_VALID_OK)
- queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5));
- else
- IL_WARN("uCode did not respond OK.\n");
-}
-
-/**
- * il4965_bg_stats_periodic - Timer callback to queue stats
- *
- * This callback is provided in order to send a stats request.
- *
- * This timer function is continually reset to execute within
- * 60 seconds since the last N_STATS was received. We need to
- * ensure we receive the stats in order to update the temperature
- * used for calibrating the TXPOWER.
- */
-static void
-il4965_bg_stats_periodic(unsigned long data)
-{
- struct il_priv *il = (struct il_priv *)data;
-
- if (test_bit(S_EXIT_PENDING, &il->status))
- return;
-
- /* dont send host command if rf-kill is on */
- if (!il_is_ready_rf(il))
- return;
-
- il_send_stats_request(il, CMD_ASYNC, false);
-}
-
-static void
-il4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il4965_beacon_notif *beacon =
- (struct il4965_beacon_notif *)pkt->u.raw;
-#ifdef CONFIG_IWLEGACY_DEBUG
- u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
-
- D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n",
- le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK,
- beacon->beacon_notify_hdr.failure_frame,
- le32_to_cpu(beacon->ibss_mgr_status),
- le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate);
-#endif
- il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
-}
-
-static void
-il4965_perform_ct_kill_task(struct il_priv *il)
-{
- unsigned long flags;
-
- D_POWER("Stop all queues\n");
-
- if (il->mac80211_registered)
- ieee80211_stop_queues(il->hw);
-
- _il_wr(il, CSR_UCODE_DRV_GP1_SET,
- CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
- _il_rd(il, CSR_UCODE_DRV_GP1);
-
- spin_lock_irqsave(&il->reg_lock, flags);
- if (likely(_il_grab_nic_access(il)))
- _il_release_nic_access(il);
- spin_unlock_irqrestore(&il->reg_lock, flags);
-}
-
-/* Handle notification from uCode that card's power state is changing
- * due to software, hardware, or critical temperature RFKILL */
-static void
-il4965_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
- unsigned long status = il->status;
-
- D_RF_KILL("Card state received: HW:%s SW:%s CT:%s\n",
- (flags & HW_CARD_DISABLED) ? "Kill" : "On",
- (flags & SW_CARD_DISABLED) ? "Kill" : "On",
- (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached");
-
- if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) {
-
- _il_wr(il, CSR_UCODE_DRV_GP1_SET,
- CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-
- il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
-
- if (!(flags & RXON_CARD_DISABLED)) {
- _il_wr(il, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
- il_wr(il, HBUS_TARG_MBX_C,
- HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
- }
- }
-
- if (flags & CT_CARD_DISABLED)
- il4965_perform_ct_kill_task(il);
-
- if (flags & HW_CARD_DISABLED)
- set_bit(S_RFKILL, &il->status);
- else
- clear_bit(S_RFKILL, &il->status);
-
- if (!(flags & RXON_CARD_DISABLED))
- il_scan_cancel(il);
-
- if ((test_bit(S_RFKILL, &status) !=
- test_bit(S_RFKILL, &il->status)))
- wiphy_rfkill_set_hw_state(il->hw->wiphy,
- test_bit(S_RFKILL, &il->status));
- else
- wake_up(&il->wait_command_queue);
-}
-
-/**
- * il4965_setup_handlers - Initialize Rx handler callbacks
- *
- * Setup the RX handlers for each of the reply types sent from the uCode
- * to the host.
- *
- * This function chains into the hardware specific files for them to setup
- * any hardware specific handlers as well.
- */
-static void
-il4965_setup_handlers(struct il_priv *il)
-{
- il->handlers[N_ALIVE] = il4965_hdl_alive;
- il->handlers[N_ERROR] = il_hdl_error;
- il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa;
- il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement;
- il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep;
- il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats;
- il->handlers[N_BEACON] = il4965_hdl_beacon;
-
- /*
- * The same handler is used for both the REPLY to a discrete
- * stats request from the host as well as for the periodic
- * stats notifications (after received beacons) from the uCode.
- */
- il->handlers[C_STATS] = il4965_hdl_c_stats;
- il->handlers[N_STATS] = il4965_hdl_stats;
-
- il_setup_rx_scan_handlers(il);
-
- /* status change handler */
- il->handlers[N_CARD_STATE] = il4965_hdl_card_state;
-
- il->handlers[N_MISSED_BEACONS] = il4965_hdl_missed_beacon;
- /* Rx handlers */
- il->handlers[N_RX_PHY] = il4965_hdl_rx_phy;
- il->handlers[N_RX_MPDU] = il4965_hdl_rx;
- il->handlers[N_RX] = il4965_hdl_rx;
- /* block ack */
- il->handlers[N_COMPRESSED_BA] = il4965_hdl_compressed_ba;
- /* Tx response */
- il->handlers[C_TX] = il4965_hdl_tx;
-}
-
-/**
- * il4965_rx_handle - Main entry function for receiving responses from uCode
- *
- * Uses the il->handlers callback function array to invoke
- * the appropriate handlers, including command responses,
- * frame-received notifications, and other notifications.
- */
-void
-il4965_rx_handle(struct il_priv *il)
-{
- struct il_rx_buf *rxb;
- struct il_rx_pkt *pkt;
- struct il_rx_queue *rxq = &il->rxq;
- u32 r, i;
- int reclaim;
- unsigned long flags;
- u8 fill_rx = 0;
- u32 count = 8;
- int total_empty;
-
- /* uCode's read idx (stored in shared DRAM) indicates the last Rx
- * buffer that the driver may process (last buffer filled by ucode). */
- r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF;
- i = rxq->read;
-
- /* Rx interrupt, but nothing sent from uCode */
- if (i == r)
- D_RX("r = %d, i = %d\n", r, i);
-
- /* calculate total frames need to be restock after handling RX */
- total_empty = r - rxq->write_actual;
- if (total_empty < 0)
- total_empty += RX_QUEUE_SIZE;
-
- if (total_empty > (RX_QUEUE_SIZE / 2))
- fill_rx = 1;
-
- while (i != r) {
- int len;
-
- rxb = rxq->queue[i];
-
- /* If an RXB doesn't have a Rx queue slot associated with it,
- * then a bug has been introduced in the queue refilling
- * routines -- catch it here */
- BUG_ON(rxb == NULL);
-
- rxq->queue[i] = NULL;
-
- pci_unmap_page(il->pci_dev, rxb->page_dma,
- PAGE_SIZE << il->hw_params.rx_page_order,
- PCI_DMA_FROMDEVICE);
- pkt = rxb_addr(rxb);
-
- len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
- len += sizeof(u32); /* account for status word */
-
- reclaim = il_need_reclaim(il, pkt);
-
- /* Based on type of command response or notification,
- * handle those that need handling via function in
- * handlers table. See il4965_setup_handlers() */
- if (il->handlers[pkt->hdr.cmd]) {
- D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i,
- il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- il->isr_stats.handlers[pkt->hdr.cmd]++;
- il->handlers[pkt->hdr.cmd] (il, rxb);
- } else {
- /* No handling needed */
- D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r,
- i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- }
-
- /*
- * XXX: After here, we should always check rxb->page
- * against NULL before touching it or its virtual
- * memory (pkt). Because some handler might have
- * already taken or freed the pages.
- */
-
- if (reclaim) {
- /* Invoke any callbacks, transfer the buffer to caller,
- * and fire off the (possibly) blocking il_send_cmd()
- * as we reclaim the driver command queue */
- if (rxb->page)
- il_tx_cmd_complete(il, rxb);
- else
- IL_WARN("Claim null rxb?\n");
- }
-
- /* Reuse the page if possible. For notification packets and
- * SKBs that fail to Rx correctly, add them back into the
- * rx_free list for reuse later. */
- spin_lock_irqsave(&rxq->lock, flags);
- if (rxb->page != NULL) {
- rxb->page_dma =
- pci_map_page(il->pci_dev, rxb->page, 0,
- PAGE_SIZE << il->hw_params.
- rx_page_order, PCI_DMA_FROMDEVICE);
-
- if (unlikely(pci_dma_mapping_error(il->pci_dev,
- rxb->page_dma))) {
- __il_free_pages(il, rxb->page);
- rxb->page = NULL;
- list_add_tail(&rxb->list, &rxq->rx_used);
- } else {
- list_add_tail(&rxb->list, &rxq->rx_free);
- rxq->free_count++;
- }
- } else
- list_add_tail(&rxb->list, &rxq->rx_used);
-
- spin_unlock_irqrestore(&rxq->lock, flags);
-
- i = (i + 1) & RX_QUEUE_MASK;
- /* If there are a lot of unused frames,
- * restock the Rx queue so ucode wont assert. */
- if (fill_rx) {
- count++;
- if (count >= 8) {
- rxq->read = i;
- il4965_rx_replenish_now(il);
- count = 0;
- }
- }
- }
-
- /* Backtrack one entry */
- rxq->read = i;
- if (fill_rx)
- il4965_rx_replenish_now(il);
- else
- il4965_rx_queue_restock(il);
-}
-
-/* call this function to flush any scheduled tasklet */
-static inline void
-il4965_synchronize_irq(struct il_priv *il)
-{
- /* wait to make sure we flush pending tasklet */
- synchronize_irq(il->pci_dev->irq);
- tasklet_kill(&il->irq_tasklet);
-}
-
-static void
-il4965_irq_tasklet(struct il_priv *il)
-{
- u32 inta, handled = 0;
- u32 inta_fh;
- unsigned long flags;
- u32 i;
-#ifdef CONFIG_IWLEGACY_DEBUG
- u32 inta_mask;
-#endif
-
- spin_lock_irqsave(&il->lock, flags);
-
- /* Ack/clear/reset pending uCode interrupts.
- * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
- * and will clear only when CSR_FH_INT_STATUS gets cleared. */
- inta = _il_rd(il, CSR_INT);
- _il_wr(il, CSR_INT, inta);
-
- /* Ack/clear/reset pending flow-handler (DMA) interrupts.
- * Any new interrupts that happen after this, either while we're
- * in this tasklet, or later, will show up in next ISR/tasklet. */
- inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
- _il_wr(il, CSR_FH_INT_STATUS, inta_fh);
-
-#ifdef CONFIG_IWLEGACY_DEBUG
- if (il_get_debug_level(il) & IL_DL_ISR) {
- /* just for debug */
- inta_mask = _il_rd(il, CSR_INT_MASK);
- D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta,
- inta_mask, inta_fh);
- }
-#endif
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
- * atomic, make sure that inta covers all the interrupts that
- * we've discovered, even if FH interrupt came in just after
- * reading CSR_INT. */
- if (inta_fh & CSR49_FH_INT_RX_MASK)
- inta |= CSR_INT_BIT_FH_RX;
- if (inta_fh & CSR49_FH_INT_TX_MASK)
- inta |= CSR_INT_BIT_FH_TX;
-
- /* Now service all interrupt bits discovered above. */
- if (inta & CSR_INT_BIT_HW_ERR) {
- IL_ERR("Hardware error detected. Restarting.\n");
-
- /* Tell the device to stop sending interrupts */
- il_disable_interrupts(il);
-
- il->isr_stats.hw++;
- il_irq_handle_error(il);
-
- handled |= CSR_INT_BIT_HW_ERR;
-
- return;
- }
-#ifdef CONFIG_IWLEGACY_DEBUG
- if (il_get_debug_level(il) & (IL_DL_ISR)) {
- /* NIC fires this, but we don't use it, redundant with WAKEUP */
- if (inta & CSR_INT_BIT_SCD) {
- D_ISR("Scheduler finished to transmit "
- "the frame/frames.\n");
- il->isr_stats.sch++;
- }
-
- /* Alive notification via Rx interrupt will do the real work */
- if (inta & CSR_INT_BIT_ALIVE) {
- D_ISR("Alive interrupt\n");
- il->isr_stats.alive++;
- }
- }
-#endif
- /* Safely ignore these bits for debug checks below */
- inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
-
- /* HW RF KILL switch toggled */
- if (inta & CSR_INT_BIT_RF_KILL) {
- int hw_rf_kill = 0;
-
- if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
- hw_rf_kill = 1;
-
- IL_WARN("RF_KILL bit toggled to %s.\n",
- hw_rf_kill ? "disable radio" : "enable radio");
-
- il->isr_stats.rfkill++;
-
- /* driver only loads ucode once setting the interface up.
- * the driver allows loading the ucode even if the radio
- * is killed. Hence update the killswitch state here. The
- * rfkill handler will care about restarting if needed.
- */
- if (hw_rf_kill) {
- set_bit(S_RFKILL, &il->status);
- } else {
- clear_bit(S_RFKILL, &il->status);
- il_force_reset(il, true);
- }
- wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill);
-
- handled |= CSR_INT_BIT_RF_KILL;
- }
-
- /* Chip got too hot and stopped itself */
- if (inta & CSR_INT_BIT_CT_KILL) {
- IL_ERR("Microcode CT kill error detected.\n");
- il->isr_stats.ctkill++;
- handled |= CSR_INT_BIT_CT_KILL;
- }
-
- /* Error detected by uCode */
- if (inta & CSR_INT_BIT_SW_ERR) {
- IL_ERR("Microcode SW error detected. " " Restarting 0x%X.\n",
- inta);
- il->isr_stats.sw++;
- il_irq_handle_error(il);
- handled |= CSR_INT_BIT_SW_ERR;
- }
-
- /*
- * uCode wakes up after power-down sleep.
- * Tell device about any new tx or host commands enqueued,
- * and about any Rx buffers made available while asleep.
- */
- if (inta & CSR_INT_BIT_WAKEUP) {
- D_ISR("Wakeup interrupt\n");
- il_rx_queue_update_write_ptr(il, &il->rxq);
- for (i = 0; i < il->hw_params.max_txq_num; i++)
- il_txq_update_write_ptr(il, &il->txq[i]);
- il->isr_stats.wakeup++;
- handled |= CSR_INT_BIT_WAKEUP;
- }
-
- /* All uCode command responses, including Tx command responses,
- * Rx "responses" (frame-received notification), and other
- * notifications from uCode come through here*/
- if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
- il4965_rx_handle(il);
- il->isr_stats.rx++;
- handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
- }
-
- /* This "Tx" DMA channel is used only for loading uCode */
- if (inta & CSR_INT_BIT_FH_TX) {
- D_ISR("uCode load interrupt\n");
- il->isr_stats.tx++;
- handled |= CSR_INT_BIT_FH_TX;
- /* Wake up uCode load routine, now that load is complete */
- il->ucode_write_complete = 1;
- wake_up(&il->wait_command_queue);
- }
-
- if (inta & ~handled) {
- IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
- il->isr_stats.unhandled++;
- }
-
- if (inta & ~(il->inta_mask)) {
- IL_WARN("Disabled INTA bits 0x%08x were pending\n",
- inta & ~il->inta_mask);
- IL_WARN(" with FH49_INT = 0x%08x\n", inta_fh);
- }
-
- /* Re-enable all interrupts */
- /* only Re-enable if disabled by irq */
- if (test_bit(S_INT_ENABLED, &il->status))
- il_enable_interrupts(il);
- /* Re-enable RF_KILL if it occurred */
- else if (handled & CSR_INT_BIT_RF_KILL)
- il_enable_rfkill_int(il);
-
-#ifdef CONFIG_IWLEGACY_DEBUG
- if (il_get_debug_level(il) & (IL_DL_ISR)) {
- inta = _il_rd(il, CSR_INT);
- inta_mask = _il_rd(il, CSR_INT_MASK);
- inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
- D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, "
- "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
- }
-#endif
-}
-
-/*****************************************************************************
- *
- * sysfs attributes
- *
- *****************************************************************************/
-
-#ifdef CONFIG_IWLEGACY_DEBUG
-
-/*
- * The following adds a new attribute to the sysfs representation
- * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/)
- * used for controlling the debug level.
- *
- * See the level definitions in iwl for details.
- *
- * The debug_level being managed using sysfs below is a per device debug
- * level that is used instead of the global debug level if it (the per
- * device debug level) is set.
- */
-static ssize_t
-il4965_show_debug_level(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct il_priv *il = dev_get_drvdata(d);
- return sprintf(buf, "0x%08X\n", il_get_debug_level(il));
-}
-
-static ssize_t
-il4965_store_debug_level(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct il_priv *il = dev_get_drvdata(d);
- unsigned long val;
- int ret;
-
- ret = kstrtoul(buf, 0, &val);
- if (ret)
- IL_ERR("%s is not in hex or decimal form.\n", buf);
- else
- il->debug_level = val;
-
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il4965_show_debug_level,
- il4965_store_debug_level);
-
-#endif /* CONFIG_IWLEGACY_DEBUG */
-
-static ssize_t
-il4965_show_temperature(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct il_priv *il = dev_get_drvdata(d);
-
- if (!il_is_alive(il))
- return -EAGAIN;
-
- return sprintf(buf, "%d\n", il->temperature);
-}
-
-static DEVICE_ATTR(temperature, S_IRUGO, il4965_show_temperature, NULL);
-
-static ssize_t
-il4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf)
-{
- struct il_priv *il = dev_get_drvdata(d);
-
- if (!il_is_ready_rf(il))
- return sprintf(buf, "off\n");
- else
- return sprintf(buf, "%d\n", il->tx_power_user_lmt);
-}
-
-static ssize_t
-il4965_store_tx_power(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct il_priv *il = dev_get_drvdata(d);
- unsigned long val;
- int ret;
-
- ret = kstrtoul(buf, 10, &val);
- if (ret)
- IL_INFO("%s is not in decimal form.\n", buf);
- else {
- ret = il_set_tx_power(il, val, false);
- if (ret)
- IL_ERR("failed setting tx power (0x%08x).\n", ret);
- else
- ret = count;
- }
- return ret;
-}
-
-static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il4965_show_tx_power,
- il4965_store_tx_power);
-
-static struct attribute *il_sysfs_entries[] = {
- &dev_attr_temperature.attr,
- &dev_attr_tx_power.attr,
-#ifdef CONFIG_IWLEGACY_DEBUG
- &dev_attr_debug_level.attr,
-#endif
- NULL
-};
-
-static struct attribute_group il_attribute_group = {
- .name = NULL, /* put in device directory */
- .attrs = il_sysfs_entries,
-};
-
-/******************************************************************************
- *
- * uCode download functions
- *
- ******************************************************************************/
-
-static void
-il4965_dealloc_ucode_pci(struct il_priv *il)
-{
- il_free_fw_desc(il->pci_dev, &il->ucode_code);
- il_free_fw_desc(il->pci_dev, &il->ucode_data);
- il_free_fw_desc(il->pci_dev, &il->ucode_data_backup);
- il_free_fw_desc(il->pci_dev, &il->ucode_init);
- il_free_fw_desc(il->pci_dev, &il->ucode_init_data);
- il_free_fw_desc(il->pci_dev, &il->ucode_boot);
-}
-
-static void
-il4965_nic_start(struct il_priv *il)
-{
- /* Remove all resets to allow NIC to operate */
- _il_wr(il, CSR_RESET, 0);
-}
-
-static void il4965_ucode_callback(const struct firmware *ucode_raw,
- void *context);
-static int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length);
-
-static int __must_check
-il4965_request_firmware(struct il_priv *il, bool first)
-{
- const char *name_pre = il->cfg->fw_name_pre;
- char tag[8];
-
- if (first) {
- il->fw_idx = il->cfg->ucode_api_max;
- sprintf(tag, "%d", il->fw_idx);
- } else {
- il->fw_idx--;
- sprintf(tag, "%d", il->fw_idx);
- }
-
- if (il->fw_idx < il->cfg->ucode_api_min) {
- IL_ERR("no suitable firmware found!\n");
- return -ENOENT;
- }
-
- sprintf(il->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
-
- D_INFO("attempting to load firmware '%s'\n", il->firmware_name);
-
- return request_firmware_nowait(THIS_MODULE, 1, il->firmware_name,
- &il->pci_dev->dev, GFP_KERNEL, il,
- il4965_ucode_callback);
-}
-
-struct il4965_firmware_pieces {
- const void *inst, *data, *init, *init_data, *boot;
- size_t inst_size, data_size, init_size, init_data_size, boot_size;
-};
-
-static int
-il4965_load_firmware(struct il_priv *il, const struct firmware *ucode_raw,
- struct il4965_firmware_pieces *pieces)
-{
- struct il_ucode_header *ucode = (void *)ucode_raw->data;
- u32 api_ver, hdr_size;
- const u8 *src;
-
- il->ucode_ver = le32_to_cpu(ucode->ver);
- api_ver = IL_UCODE_API(il->ucode_ver);
-
- switch (api_ver) {
- default:
- case 0:
- case 1:
- case 2:
- hdr_size = 24;
- if (ucode_raw->size < hdr_size) {
- IL_ERR("File size too small!\n");
- return -EINVAL;
- }
- pieces->inst_size = le32_to_cpu(ucode->v1.inst_size);
- pieces->data_size = le32_to_cpu(ucode->v1.data_size);
- pieces->init_size = le32_to_cpu(ucode->v1.init_size);
- pieces->init_data_size = le32_to_cpu(ucode->v1.init_data_size);
- pieces->boot_size = le32_to_cpu(ucode->v1.boot_size);
- src = ucode->v1.data;
- break;
- }
-
- /* Verify size of file vs. image size info in file's header */
- if (ucode_raw->size !=
- hdr_size + pieces->inst_size + pieces->data_size +
- pieces->init_size + pieces->init_data_size + pieces->boot_size) {
-
- IL_ERR("uCode file size %d does not match expected size\n",
- (int)ucode_raw->size);
- return -EINVAL;
- }
-
- pieces->inst = src;
- src += pieces->inst_size;
- pieces->data = src;
- src += pieces->data_size;
- pieces->init = src;
- src += pieces->init_size;
- pieces->init_data = src;
- src += pieces->init_data_size;
- pieces->boot = src;
- src += pieces->boot_size;
-
- return 0;
-}
-
-/**
- * il4965_ucode_callback - callback when firmware was loaded
- *
- * If loaded successfully, copies the firmware into buffers
- * for the card to fetch (via DMA).
- */
-static void
-il4965_ucode_callback(const struct firmware *ucode_raw, void *context)
-{
- struct il_priv *il = context;
- struct il_ucode_header *ucode;
- int err;
- struct il4965_firmware_pieces pieces;
- const unsigned int api_max = il->cfg->ucode_api_max;
- const unsigned int api_min = il->cfg->ucode_api_min;
- u32 api_ver;
-
- u32 max_probe_length = 200;
- u32 standard_phy_calibration_size =
- IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
-
- memset(&pieces, 0, sizeof(pieces));
-
- if (!ucode_raw) {
- if (il->fw_idx <= il->cfg->ucode_api_max)
- IL_ERR("request for firmware file '%s' failed.\n",
- il->firmware_name);
- goto try_again;
- }
-
- D_INFO("Loaded firmware file '%s' (%zd bytes).\n", il->firmware_name,
- ucode_raw->size);
-
- /* Make sure that we got at least the API version number */
- if (ucode_raw->size < 4) {
- IL_ERR("File size way too small!\n");
- goto try_again;
- }
-
- /* Data from ucode file: header followed by uCode images */
- ucode = (struct il_ucode_header *)ucode_raw->data;
-
- err = il4965_load_firmware(il, ucode_raw, &pieces);
-
- if (err)
- goto try_again;
-
- api_ver = IL_UCODE_API(il->ucode_ver);
-
- /*
- * api_ver should match the api version forming part of the
- * firmware filename ... but we don't check for that and only rely
- * on the API version read from firmware header from here on forward
- */
- if (api_ver < api_min || api_ver > api_max) {
- IL_ERR("Driver unable to support your firmware API. "
- "Driver supports v%u, firmware is v%u.\n", api_max,
- api_ver);
- goto try_again;
- }
-
- if (api_ver != api_max)
- IL_ERR("Firmware has old API version. Expected v%u, "
- "got v%u. New firmware can be obtained "
- "from http://www.intellinuxwireless.org.\n", api_max,
- api_ver);
-
- IL_INFO("loaded firmware version %u.%u.%u.%u\n",
- IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver),
- IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver));
-
- snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version),
- "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver),
- IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver),
- IL_UCODE_SERIAL(il->ucode_ver));
-
- /*
- * For any of the failures below (before allocating pci memory)
- * we will try to load a version with a smaller API -- maybe the
- * user just got a corrupted version of the latest API.
- */
-
- D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver);
- D_INFO("f/w package hdr runtime inst size = %Zd\n", pieces.inst_size);
- D_INFO("f/w package hdr runtime data size = %Zd\n", pieces.data_size);
- D_INFO("f/w package hdr init inst size = %Zd\n", pieces.init_size);
- D_INFO("f/w package hdr init data size = %Zd\n", pieces.init_data_size);
- D_INFO("f/w package hdr boot inst size = %Zd\n", pieces.boot_size);
-
- /* Verify that uCode images will fit in card's SRAM */
- if (pieces.inst_size > il->hw_params.max_inst_size) {
- IL_ERR("uCode instr len %Zd too large to fit in\n",
- pieces.inst_size);
- goto try_again;
- }
-
- if (pieces.data_size > il->hw_params.max_data_size) {
- IL_ERR("uCode data len %Zd too large to fit in\n",
- pieces.data_size);
- goto try_again;
- }
-
- if (pieces.init_size > il->hw_params.max_inst_size) {
- IL_ERR("uCode init instr len %Zd too large to fit in\n",
- pieces.init_size);
- goto try_again;
- }
-
- if (pieces.init_data_size > il->hw_params.max_data_size) {
- IL_ERR("uCode init data len %Zd too large to fit in\n",
- pieces.init_data_size);
- goto try_again;
- }
-
- if (pieces.boot_size > il->hw_params.max_bsm_size) {
- IL_ERR("uCode boot instr len %Zd too large to fit in\n",
- pieces.boot_size);
- goto try_again;
- }
-
- /* Allocate ucode buffers for card's bus-master loading ... */
-
- /* Runtime instructions and 2 copies of data:
- * 1) unmodified from disk
- * 2) backup cache for save/restore during power-downs */
- il->ucode_code.len = pieces.inst_size;
- il_alloc_fw_desc(il->pci_dev, &il->ucode_code);
-
- il->ucode_data.len = pieces.data_size;
- il_alloc_fw_desc(il->pci_dev, &il->ucode_data);
-
- il->ucode_data_backup.len = pieces.data_size;
- il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup);
-
- if (!il->ucode_code.v_addr || !il->ucode_data.v_addr ||
- !il->ucode_data_backup.v_addr)
- goto err_pci_alloc;
-
- /* Initialization instructions and data */
- if (pieces.init_size && pieces.init_data_size) {
- il->ucode_init.len = pieces.init_size;
- il_alloc_fw_desc(il->pci_dev, &il->ucode_init);
-
- il->ucode_init_data.len = pieces.init_data_size;
- il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data);
-
- if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr)
- goto err_pci_alloc;
- }
-
- /* Bootstrap (instructions only, no data) */
- if (pieces.boot_size) {
- il->ucode_boot.len = pieces.boot_size;
- il_alloc_fw_desc(il->pci_dev, &il->ucode_boot);
-
- if (!il->ucode_boot.v_addr)
- goto err_pci_alloc;
- }
-
- /* Now that we can no longer fail, copy information */
-
- il->sta_key_max_num = STA_KEY_MAX_NUM;
-
- /* Copy images into buffers for card's bus-master reads ... */
-
- /* Runtime instructions (first block of data in file) */
- D_INFO("Copying (but not loading) uCode instr len %Zd\n",
- pieces.inst_size);
- memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size);
-
- D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n",
- il->ucode_code.v_addr, (u32) il->ucode_code.p_addr);
-
- /*
- * Runtime data
- * NOTE: Copy into backup buffer will be done in il_up()
- */
- D_INFO("Copying (but not loading) uCode data len %Zd\n",
- pieces.data_size);
- memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size);
- memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size);
-
- /* Initialization instructions */
- if (pieces.init_size) {
- D_INFO("Copying (but not loading) init instr len %Zd\n",
- pieces.init_size);
- memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size);
- }
-
- /* Initialization data */
- if (pieces.init_data_size) {
- D_INFO("Copying (but not loading) init data len %Zd\n",
- pieces.init_data_size);
- memcpy(il->ucode_init_data.v_addr, pieces.init_data,
- pieces.init_data_size);
- }
-
- /* Bootstrap instructions */
- D_INFO("Copying (but not loading) boot instr len %Zd\n",
- pieces.boot_size);
- memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size);
-
- /*
- * figure out the offset of chain noise reset and gain commands
- * base on the size of standard phy calibration commands table size
- */
- il->_4965.phy_calib_chain_noise_reset_cmd =
- standard_phy_calibration_size;
- il->_4965.phy_calib_chain_noise_gain_cmd =
- standard_phy_calibration_size + 1;
-
- /**************************************************
- * This is still part of probe() in a sense...
- *
- * 9. Setup and register with mac80211 and debugfs
- **************************************************/
- err = il4965_mac_setup_register(il, max_probe_length);
- if (err)
- goto out_unbind;
-
- err = il_dbgfs_register(il, DRV_NAME);
- if (err)
- IL_ERR("failed to create debugfs files. Ignoring error: %d\n",
- err);
-
- err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group);
- if (err) {
- IL_ERR("failed to create sysfs device attributes\n");
- goto out_unbind;
- }
-
- /* We have our copies now, allow OS release its copies */
- release_firmware(ucode_raw);
- complete(&il->_4965.firmware_loading_complete);
- return;
-
-try_again:
- /* try next, if any */
- if (il4965_request_firmware(il, false))
- goto out_unbind;
- release_firmware(ucode_raw);
- return;
-
-err_pci_alloc:
- IL_ERR("failed to allocate pci memory\n");
- il4965_dealloc_ucode_pci(il);
-out_unbind:
- complete(&il->_4965.firmware_loading_complete);
- device_release_driver(&il->pci_dev->dev);
- release_firmware(ucode_raw);
-}
-
-static const char *const desc_lookup_text[] = {
- "OK",
- "FAIL",
- "BAD_PARAM",
- "BAD_CHECKSUM",
- "NMI_INTERRUPT_WDG",
- "SYSASSERT",
- "FATAL_ERROR",
- "BAD_COMMAND",
- "HW_ERROR_TUNE_LOCK",
- "HW_ERROR_TEMPERATURE",
- "ILLEGAL_CHAN_FREQ",
- "VCC_NOT_STBL",
- "FH49_ERROR",
- "NMI_INTERRUPT_HOST",
- "NMI_INTERRUPT_ACTION_PT",
- "NMI_INTERRUPT_UNKNOWN",
- "UCODE_VERSION_MISMATCH",
- "HW_ERROR_ABS_LOCK",
- "HW_ERROR_CAL_LOCK_FAIL",
- "NMI_INTERRUPT_INST_ACTION_PT",
- "NMI_INTERRUPT_DATA_ACTION_PT",
- "NMI_TRM_HW_ER",
- "NMI_INTERRUPT_TRM",
- "NMI_INTERRUPT_BREAK_POINT",
- "DEBUG_0",
- "DEBUG_1",
- "DEBUG_2",
- "DEBUG_3",
-};
-
-static struct {
- char *name;
- u8 num;
-} advanced_lookup[] = {
- {
- "NMI_INTERRUPT_WDG", 0x34}, {
- "SYSASSERT", 0x35}, {
- "UCODE_VERSION_MISMATCH", 0x37}, {
- "BAD_COMMAND", 0x38}, {
- "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C}, {
- "FATAL_ERROR", 0x3D}, {
- "NMI_TRM_HW_ERR", 0x46}, {
- "NMI_INTERRUPT_TRM", 0x4C}, {
- "NMI_INTERRUPT_BREAK_POINT", 0x54}, {
- "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C}, {
- "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64}, {
- "NMI_INTERRUPT_HOST", 0x66}, {
- "NMI_INTERRUPT_ACTION_PT", 0x7C}, {
- "NMI_INTERRUPT_UNKNOWN", 0x84}, {
- "NMI_INTERRUPT_INST_ACTION_PT", 0x86}, {
-"ADVANCED_SYSASSERT", 0},};
-
-static const char *
-il4965_desc_lookup(u32 num)
-{
- int i;
- int max = ARRAY_SIZE(desc_lookup_text);
-
- if (num < max)
- return desc_lookup_text[num];
-
- max = ARRAY_SIZE(advanced_lookup) - 1;
- for (i = 0; i < max; i++) {
- if (advanced_lookup[i].num == num)
- break;
- }
- return advanced_lookup[i].name;
-}
-
-#define ERROR_START_OFFSET (1 * sizeof(u32))
-#define ERROR_ELEM_SIZE (7 * sizeof(u32))
-
-void
-il4965_dump_nic_error_log(struct il_priv *il)
-{
- u32 data2, line;
- u32 desc, time, count, base, data1;
- u32 blink1, blink2, ilink1, ilink2;
- u32 pc, hcmd;
-
- if (il->ucode_type == UCODE_INIT)
- base = le32_to_cpu(il->card_alive_init.error_event_table_ptr);
- else
- base = le32_to_cpu(il->card_alive.error_event_table_ptr);
-
- if (!il->ops->is_valid_rtc_data_addr(base)) {
- IL_ERR("Not valid error log pointer 0x%08X for %s uCode\n",
- base, (il->ucode_type == UCODE_INIT) ? "Init" : "RT");
- return;
- }
-
- count = il_read_targ_mem(il, base);
-
- if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
- IL_ERR("Start IWL Error Log Dump:\n");
- IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count);
- }
-
- desc = il_read_targ_mem(il, base + 1 * sizeof(u32));
- il->isr_stats.err_code = desc;
- pc = il_read_targ_mem(il, base + 2 * sizeof(u32));
- blink1 = il_read_targ_mem(il, base + 3 * sizeof(u32));
- blink2 = il_read_targ_mem(il, base + 4 * sizeof(u32));
- ilink1 = il_read_targ_mem(il, base + 5 * sizeof(u32));
- ilink2 = il_read_targ_mem(il, base + 6 * sizeof(u32));
- data1 = il_read_targ_mem(il, base + 7 * sizeof(u32));
- data2 = il_read_targ_mem(il, base + 8 * sizeof(u32));
- line = il_read_targ_mem(il, base + 9 * sizeof(u32));
- time = il_read_targ_mem(il, base + 11 * sizeof(u32));
- hcmd = il_read_targ_mem(il, base + 22 * sizeof(u32));
-
- IL_ERR("Desc Time "
- "data1 data2 line\n");
- IL_ERR("%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n",
- il4965_desc_lookup(desc), desc, time, data1, data2, line);
- IL_ERR("pc blink1 blink2 ilink1 ilink2 hcmd\n");
- IL_ERR("0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", pc, blink1,
- blink2, ilink1, ilink2, hcmd);
-}
-
-static void
-il4965_rf_kill_ct_config(struct il_priv *il)
-{
- struct il_ct_kill_config cmd;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&il->lock, flags);
- _il_wr(il, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
- spin_unlock_irqrestore(&il->lock, flags);
-
- cmd.critical_temperature_R =
- cpu_to_le32(il->hw_params.ct_kill_threshold);
-
- ret = il_send_cmd_pdu(il, C_CT_KILL_CONFIG, sizeof(cmd), &cmd);
- if (ret)
- IL_ERR("C_CT_KILL_CONFIG failed\n");
- else
- D_INFO("C_CT_KILL_CONFIG " "succeeded, "
- "critical temperature is %d\n",
- il->hw_params.ct_kill_threshold);
-}
-
-static const s8 default_queue_to_tx_fifo[] = {
- IL_TX_FIFO_VO,
- IL_TX_FIFO_VI,
- IL_TX_FIFO_BE,
- IL_TX_FIFO_BK,
- IL49_CMD_FIFO_NUM,
- IL_TX_FIFO_UNUSED,
- IL_TX_FIFO_UNUSED,
-};
-
-#define IL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
-
-static int
-il4965_alive_notify(struct il_priv *il)
-{
- u32 a;
- unsigned long flags;
- int i, chan;
- u32 reg_val;
-
- spin_lock_irqsave(&il->lock, flags);
-
- /* Clear 4965's internal Tx Scheduler data base */
- il->scd_base_addr = il_rd_prph(il, IL49_SCD_SRAM_BASE_ADDR);
- a = il->scd_base_addr + IL49_SCD_CONTEXT_DATA_OFFSET;
- for (; a < il->scd_base_addr + IL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4)
- il_write_targ_mem(il, a, 0);
- for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET; a += 4)
- il_write_targ_mem(il, a, 0);
- for (;
- a <
- il->scd_base_addr +
- IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(il->hw_params.max_txq_num);
- a += 4)
- il_write_targ_mem(il, a, 0);
-
- /* Tel 4965 where to find Tx byte count tables */
- il_wr_prph(il, IL49_SCD_DRAM_BASE_ADDR, il->scd_bc_tbls.dma >> 10);
-
- /* Enable DMA channel */
- for (chan = 0; chan < FH49_TCSR_CHNL_NUM; chan++)
- il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(chan),
- FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
-
- /* Update FH chicken bits */
- reg_val = il_rd(il, FH49_TX_CHICKEN_BITS_REG);
- il_wr(il, FH49_TX_CHICKEN_BITS_REG,
- reg_val | FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
-
- /* Disable chain mode for all queues */
- il_wr_prph(il, IL49_SCD_QUEUECHAIN_SEL, 0);
-
- /* Initialize each Tx queue (including the command queue) */
- for (i = 0; i < il->hw_params.max_txq_num; i++) {
-
- /* TFD circular buffer read/write idxes */
- il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(i), 0);
- il_wr(il, HBUS_TARG_WRPTR, 0 | (i << 8));
-
- /* Max Tx Window size for Scheduler-ACK mode */
- il_write_targ_mem(il,
- il->scd_base_addr +
- IL49_SCD_CONTEXT_QUEUE_OFFSET(i),
- (SCD_WIN_SIZE <<
- IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) &
- IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
-
- /* Frame limit */
- il_write_targ_mem(il,
- il->scd_base_addr +
- IL49_SCD_CONTEXT_QUEUE_OFFSET(i) +
- sizeof(u32),
- (SCD_FRAME_LIMIT <<
- IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
- IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
-
- }
- il_wr_prph(il, IL49_SCD_INTERRUPT_MASK,
- (1 << il->hw_params.max_txq_num) - 1);
-
- /* Activate all Tx DMA/FIFO channels */
- il4965_txq_set_sched(il, IL_MASK(0, 6));
-
- il4965_set_wr_ptrs(il, IL_DEFAULT_CMD_QUEUE_NUM, 0);
-
- /* make sure all queue are not stopped */
- memset(&il->queue_stopped[0], 0, sizeof(il->queue_stopped));
- for (i = 0; i < 4; i++)
- atomic_set(&il->queue_stop_count[i], 0);
-
- /* reset to 0 to enable all the queue first */
- il->txq_ctx_active_msk = 0;
- /* Map each Tx/cmd queue to its corresponding fifo */
- BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7);
-
- for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) {
- int ac = default_queue_to_tx_fifo[i];
-
- il_txq_ctx_activate(il, i);
-
- if (ac == IL_TX_FIFO_UNUSED)
- continue;
-
- il4965_tx_queue_set_status(il, &il->txq[i], ac, 0);
- }
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- return 0;
-}
-
-/**
- * il4965_alive_start - called after N_ALIVE notification received
- * from protocol/runtime uCode (initialization uCode's
- * Alive gets handled by il_init_alive_start()).
- */
-static void
-il4965_alive_start(struct il_priv *il)
-{
- int ret = 0;
-
- D_INFO("Runtime Alive received.\n");
-
- if (il->card_alive.is_valid != UCODE_VALID_OK) {
- /* We had an error bringing up the hardware, so take it
- * all the way back down so we can try again */
- D_INFO("Alive failed.\n");
- goto restart;
- }
-
- /* Initialize uCode has loaded Runtime uCode ... verify inst image.
- * This is a paranoid check, because we would not have gotten the
- * "runtime" alive if code weren't properly loaded. */
- if (il4965_verify_ucode(il)) {
- /* Runtime instruction load was bad;
- * take it all the way back down so we can try again */
- D_INFO("Bad runtime uCode load.\n");
- goto restart;
- }
-
- ret = il4965_alive_notify(il);
- if (ret) {
- IL_WARN("Could not complete ALIVE transition [ntf]: %d\n", ret);
- goto restart;
- }
-
- /* After the ALIVE response, we can send host commands to the uCode */
- set_bit(S_ALIVE, &il->status);
-
- /* Enable watchdog to monitor the driver tx queues */
- il_setup_watchdog(il);
-
- if (il_is_rfkill(il))
- return;
-
- ieee80211_wake_queues(il->hw);
-
- il->active_rate = RATES_MASK;
-
- il_power_update_mode(il, true);
- D_INFO("Updated power mode\n");
-
- if (il_is_associated(il)) {
- struct il_rxon_cmd *active_rxon =
- (struct il_rxon_cmd *)&il->active;
- /* apply any changes in staging */
- il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
- active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- } else {
- /* Initialize our rx_config data */
- il_connection_init_rx_config(il);
-
- if (il->ops->set_rxon_chain)
- il->ops->set_rxon_chain(il);
- }
-
- /* Configure bluetooth coexistence if enabled */
- il_send_bt_config(il);
-
- il4965_reset_run_time_calib(il);
-
- set_bit(S_READY, &il->status);
-
- /* Configure the adapter for unassociated operation */
- il_commit_rxon(il);
-
- /* At this point, the NIC is initialized and operational */
- il4965_rf_kill_ct_config(il);
-
- D_INFO("ALIVE processing complete.\n");
- wake_up(&il->wait_command_queue);
-
- return;
-
-restart:
- queue_work(il->workqueue, &il->restart);
-}
-
-static void il4965_cancel_deferred_work(struct il_priv *il);
-
-static void
-__il4965_down(struct il_priv *il)
-{
- unsigned long flags;
- int exit_pending;
-
- D_INFO(DRV_NAME " is going down\n");
-
- il_scan_cancel_timeout(il, 200);
-
- exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status);
-
- /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set
- * to prevent rearm timer */
- del_timer_sync(&il->watchdog);
-
- il_clear_ucode_stations(il);
-
- /* FIXME: race conditions ? */
- spin_lock_irq(&il->sta_lock);
- /*
- * Remove all key information that is not stored as part
- * of station information since mac80211 may not have had
- * a chance to remove all the keys. When device is
- * reconfigured by mac80211 after an error all keys will
- * be reconfigured.
- */
- memset(il->_4965.wep_keys, 0, sizeof(il->_4965.wep_keys));
- il->_4965.key_mapping_keys = 0;
- spin_unlock_irq(&il->sta_lock);
-
- il_dealloc_bcast_stations(il);
- il_clear_driver_stations(il);
-
- /* Unblock any waiting calls */
- wake_up_all(&il->wait_command_queue);
-
- /* Wipe out the EXIT_PENDING status bit if we are not actually
- * exiting the module */
- if (!exit_pending)
- clear_bit(S_EXIT_PENDING, &il->status);
-
- /* stop and reset the on-board processor */
- _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
-
- /* tell the device to stop sending interrupts */
- spin_lock_irqsave(&il->lock, flags);
- il_disable_interrupts(il);
- spin_unlock_irqrestore(&il->lock, flags);
- il4965_synchronize_irq(il);
-
- if (il->mac80211_registered)
- ieee80211_stop_queues(il->hw);
-
- /* If we have not previously called il_init() then
- * clear all bits but the RF Kill bit and return */
- if (!il_is_init(il)) {
- il->status =
- test_bit(S_RFKILL, &il->status) << S_RFKILL |
- test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED |
- test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING;
- goto exit;
- }
-
- /* ...otherwise clear out all the status bits but the RF Kill
- * bit and continue taking the NIC down. */
- il->status &=
- test_bit(S_RFKILL, &il->status) << S_RFKILL |
- test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED |
- test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR |
- test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING;
-
- /*
- * We disabled and synchronized interrupt, and priv->mutex is taken, so
- * here is the only thread which will program device registers, but
- * still have lockdep assertions, so we are taking reg_lock.
- */
- spin_lock_irq(&il->reg_lock);
- /* FIXME: il_grab_nic_access if rfkill is off ? */
-
- il4965_txq_ctx_stop(il);
- il4965_rxq_stop(il);
- /* Power-down device's busmaster DMA clocks */
- _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
- udelay(5);
- /* Make sure (redundant) we've released our request to stay awake */
- _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- /* Stop the device, and put it in low power state */
- _il_apm_stop(il);
-
- spin_unlock_irq(&il->reg_lock);
-
- il4965_txq_ctx_unmap(il);
-exit:
- memset(&il->card_alive, 0, sizeof(struct il_alive_resp));
-
- dev_kfree_skb(il->beacon_skb);
- il->beacon_skb = NULL;
-
- /* clear out any free frames */
- il4965_clear_free_frames(il);
-}
-
-static void
-il4965_down(struct il_priv *il)
-{
- mutex_lock(&il->mutex);
- __il4965_down(il);
- mutex_unlock(&il->mutex);
-
- il4965_cancel_deferred_work(il);
-}
-
-
-static void
-il4965_set_hw_ready(struct il_priv *il)
-{
- int ret;
-
- il_set_bit(il, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
-
- /* See if we got it */
- ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
- 100);
- if (ret >= 0)
- il->hw_ready = true;
-
- D_INFO("hardware %s ready\n", (il->hw_ready) ? "" : "not");
-}
-
-static void
-il4965_prepare_card_hw(struct il_priv *il)
-{
- int ret;
-
- il->hw_ready = false;
-
- il4965_set_hw_ready(il);
- if (il->hw_ready)
- return;
-
- /* If HW is not ready, prepare the conditions to check again */
- il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE);
-
- ret =
- _il_poll_bit(il, CSR_HW_IF_CONFIG_REG,
- ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000);
-
- /* HW should be ready by now, check again. */
- if (ret != -ETIMEDOUT)
- il4965_set_hw_ready(il);
-}
-
-#define MAX_HW_RESTARTS 5
-
-static int
-__il4965_up(struct il_priv *il)
-{
- int i;
- int ret;
-
- if (test_bit(S_EXIT_PENDING, &il->status)) {
- IL_WARN("Exit pending; will not bring the NIC up\n");
- return -EIO;
- }
-
- if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) {
- IL_ERR("ucode not available for device bringup\n");
- return -EIO;
- }
-
- ret = il4965_alloc_bcast_station(il);
- if (ret) {
- il_dealloc_bcast_stations(il);
- return ret;
- }
-
- il4965_prepare_card_hw(il);
- if (!il->hw_ready) {
- IL_ERR("HW not ready\n");
- return -EIO;
- }
-
- /* If platform's RF_KILL switch is NOT set to KILL */
- if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
- clear_bit(S_RFKILL, &il->status);
- else {
- set_bit(S_RFKILL, &il->status);
- wiphy_rfkill_set_hw_state(il->hw->wiphy, true);
-
- il_enable_rfkill_int(il);
- IL_WARN("Radio disabled by HW RF Kill switch\n");
- return 0;
- }
-
- _il_wr(il, CSR_INT, 0xFFFFFFFF);
-
- /* must be initialised before il_hw_nic_init */
- il->cmd_queue = IL_DEFAULT_CMD_QUEUE_NUM;
-
- ret = il4965_hw_nic_init(il);
- if (ret) {
- IL_ERR("Unable to init nic\n");
- return ret;
- }
-
- /* make sure rfkill handshake bits are cleared */
- _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
- _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-
- /* clear (again), then enable host interrupts */
- _il_wr(il, CSR_INT, 0xFFFFFFFF);
- il_enable_interrupts(il);
-
- /* really make sure rfkill handshake bits are cleared */
- _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
- _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
-
- /* Copy original ucode data image from disk into backup cache.
- * This will be used to initialize the on-board processor's
- * data SRAM for a clean start when the runtime program first loads. */
- memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr,
- il->ucode_data.len);
-
- for (i = 0; i < MAX_HW_RESTARTS; i++) {
-
- /* load bootstrap state machine,
- * load bootstrap program into processor's memory,
- * prepare to load the "initialize" uCode */
- ret = il->ops->load_ucode(il);
-
- if (ret) {
- IL_ERR("Unable to set up bootstrap uCode: %d\n", ret);
- continue;
- }
-
- /* start card; "initialize" will load runtime ucode */
- il4965_nic_start(il);
-
- D_INFO(DRV_NAME " is coming up\n");
-
- return 0;
- }
-
- set_bit(S_EXIT_PENDING, &il->status);
- __il4965_down(il);
- clear_bit(S_EXIT_PENDING, &il->status);
-
- /* tried to restart and config the device for as long as our
- * patience could withstand */
- IL_ERR("Unable to initialize device after %d attempts.\n", i);
- return -EIO;
-}
-
-/*****************************************************************************
- *
- * Workqueue callbacks
- *
- *****************************************************************************/
-
-static void
-il4965_bg_init_alive_start(struct work_struct *data)
-{
- struct il_priv *il =
- container_of(data, struct il_priv, init_alive_start.work);
-
- mutex_lock(&il->mutex);
- if (test_bit(S_EXIT_PENDING, &il->status))
- goto out;
-
- il->ops->init_alive_start(il);
-out:
- mutex_unlock(&il->mutex);
-}
-
-static void
-il4965_bg_alive_start(struct work_struct *data)
-{
- struct il_priv *il =
- container_of(data, struct il_priv, alive_start.work);
-
- mutex_lock(&il->mutex);
- if (test_bit(S_EXIT_PENDING, &il->status))
- goto out;
-
- il4965_alive_start(il);
-out:
- mutex_unlock(&il->mutex);
-}
-
-static void
-il4965_bg_run_time_calib_work(struct work_struct *work)
-{
- struct il_priv *il = container_of(work, struct il_priv,
- run_time_calib_work);
-
- mutex_lock(&il->mutex);
-
- if (test_bit(S_EXIT_PENDING, &il->status) ||
- test_bit(S_SCANNING, &il->status)) {
- mutex_unlock(&il->mutex);
- return;
- }
-
- if (il->start_calib) {
- il4965_chain_noise_calibration(il, (void *)&il->_4965.stats);
- il4965_sensitivity_calibration(il, (void *)&il->_4965.stats);
- }
-
- mutex_unlock(&il->mutex);
-}
-
-static void
-il4965_bg_restart(struct work_struct *data)
-{
- struct il_priv *il = container_of(data, struct il_priv, restart);
-
- if (test_bit(S_EXIT_PENDING, &il->status))
- return;
-
- if (test_and_clear_bit(S_FW_ERROR, &il->status)) {
- mutex_lock(&il->mutex);
- il->is_open = 0;
-
- __il4965_down(il);
-
- mutex_unlock(&il->mutex);
- il4965_cancel_deferred_work(il);
- ieee80211_restart_hw(il->hw);
- } else {
- il4965_down(il);
-
- mutex_lock(&il->mutex);
- if (test_bit(S_EXIT_PENDING, &il->status)) {
- mutex_unlock(&il->mutex);
- return;
- }
-
- __il4965_up(il);
- mutex_unlock(&il->mutex);
- }
-}
-
-static void
-il4965_bg_rx_replenish(struct work_struct *data)
-{
- struct il_priv *il = container_of(data, struct il_priv, rx_replenish);
-
- if (test_bit(S_EXIT_PENDING, &il->status))
- return;
-
- mutex_lock(&il->mutex);
- il4965_rx_replenish(il);
- mutex_unlock(&il->mutex);
-}
-
-/*****************************************************************************
- *
- * mac80211 entry point functions
- *
- *****************************************************************************/
-
-#define UCODE_READY_TIMEOUT (4 * HZ)
-
-/*
- * Not a mac80211 entry point function, but it fits in with all the
- * other mac80211 functions grouped here.
- */
-static int
-il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
-{
- int ret;
- struct ieee80211_hw *hw = il->hw;
-
- hw->rate_control_algorithm = "iwl-4965-rs";
-
- /* Tell mac80211 our characteristics */
- ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
- ieee80211_hw_set(hw, SUPPORTS_PS);
- ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
- ieee80211_hw_set(hw, SPECTRUM_MGMT);
- ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
- ieee80211_hw_set(hw, SIGNAL_DBM);
- ieee80211_hw_set(hw, AMPDU_AGGREGATION);
- if (il->cfg->sku & IL_SKU_N)
- hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS |
- NL80211_FEATURE_STATIC_SMPS;
-
- hw->sta_data_size = sizeof(struct il_station_priv);
- hw->vif_data_size = sizeof(struct il_vif_priv);
-
- hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
-
- hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
- hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
- REGULATORY_DISABLE_BEACON_HINTS;
-
- /*
- * For now, disable PS by default because it affects
- * RX performance significantly.
- */
- hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
-
- hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
- /* we create the 802.11 header and a zero-length SSID element */
- hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2;
-
- /* Default value; 4 EDCA QOS priorities */
- hw->queues = 4;
-
- hw->max_listen_interval = IL_CONN_MAX_LISTEN_INTERVAL;
-
- if (il->bands[IEEE80211_BAND_2GHZ].n_channels)
- il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &il->bands[IEEE80211_BAND_2GHZ];
- if (il->bands[IEEE80211_BAND_5GHZ].n_channels)
- il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &il->bands[IEEE80211_BAND_5GHZ];
-
- il_leds_init(il);
-
- ret = ieee80211_register_hw(il->hw);
- if (ret) {
- IL_ERR("Failed to register hw (error %d)\n", ret);
- return ret;
- }
- il->mac80211_registered = 1;
-
- return 0;
-}
-
-int
-il4965_mac_start(struct ieee80211_hw *hw)
-{
- struct il_priv *il = hw->priv;
- int ret;
-
- D_MAC80211("enter\n");
-
- /* we should be verifying the device is ready to be opened */
- mutex_lock(&il->mutex);
- ret = __il4965_up(il);
- mutex_unlock(&il->mutex);
-
- if (ret)
- return ret;
-
- if (il_is_rfkill(il))
- goto out;
-
- D_INFO("Start UP work done.\n");
-
- /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from
- * mac80211 will not be run successfully. */
- ret = wait_event_timeout(il->wait_command_queue,
- test_bit(S_READY, &il->status),
- UCODE_READY_TIMEOUT);
- if (!ret) {
- if (!test_bit(S_READY, &il->status)) {
- IL_ERR("START_ALIVE timeout after %dms.\n",
- jiffies_to_msecs(UCODE_READY_TIMEOUT));
- return -ETIMEDOUT;
- }
- }
-
- il4965_led_enable(il);
-
-out:
- il->is_open = 1;
- D_MAC80211("leave\n");
- return 0;
-}
-
-void
-il4965_mac_stop(struct ieee80211_hw *hw)
-{
- struct il_priv *il = hw->priv;
-
- D_MAC80211("enter\n");
-
- if (!il->is_open)
- return;
-
- il->is_open = 0;
-
- il4965_down(il);
-
- flush_workqueue(il->workqueue);
-
- /* User space software may expect getting rfkill changes
- * even if interface is down */
- _il_wr(il, CSR_INT, 0xFFFFFFFF);
- il_enable_rfkill_int(il);
-
- D_MAC80211("leave\n");
-}
-
-void
-il4965_mac_tx(struct ieee80211_hw *hw,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
-{
- struct il_priv *il = hw->priv;
-
- D_MACDUMP("enter\n");
-
- D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
- ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate);
-
- if (il4965_tx_skb(il, control->sta, skb))
- dev_kfree_skb_any(skb);
-
- D_MACDUMP("leave\n");
-}
-
-void
-il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta, u32 iv32, u16 * phase1key)
-{
- struct il_priv *il = hw->priv;
-
- D_MAC80211("enter\n");
-
- il4965_update_tkip_key(il, keyconf, sta, iv32, phase1key);
-
- D_MAC80211("leave\n");
-}
-
-int
-il4965_mac_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 il_priv *il = hw->priv;
- int ret;
- u8 sta_id;
- bool is_default_wep_key = false;
-
- D_MAC80211("enter\n");
-
- if (il->cfg->mod_params->sw_crypto) {
- D_MAC80211("leave - hwcrypto disabled\n");
- return -EOPNOTSUPP;
- }
-
- /*
- * To support IBSS RSN, don't program group keys in IBSS, the
- * hardware will then not attempt to decrypt the frames.
- */
- if (vif->type == NL80211_IFTYPE_ADHOC &&
- !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
- D_MAC80211("leave - ad-hoc group key\n");
- return -EOPNOTSUPP;
- }
-
- sta_id = il_sta_id_or_broadcast(il, sta);
- if (sta_id == IL_INVALID_STATION)
- return -EINVAL;
-
- mutex_lock(&il->mutex);
- il_scan_cancel_timeout(il, 100);
-
- /*
- * If we are getting WEP group key and we didn't receive any key mapping
- * so far, we are in legacy wep mode (group key only), otherwise we are
- * in 1X mode.
- * In legacy wep mode, we use another host command to the uCode.
- */
- if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) {
- if (cmd == SET_KEY)
- is_default_wep_key = !il->_4965.key_mapping_keys;
- else
- is_default_wep_key =
- (key->hw_key_idx == HW_KEY_DEFAULT);
- }
-
- switch (cmd) {
- case SET_KEY:
- if (is_default_wep_key)
- ret = il4965_set_default_wep_key(il, key);
- else
- ret = il4965_set_dynamic_key(il, key, sta_id);
-
- D_MAC80211("enable hwcrypto key\n");
- break;
- case DISABLE_KEY:
- if (is_default_wep_key)
- ret = il4965_remove_default_wep_key(il, key);
- else
- ret = il4965_remove_dynamic_key(il, key, sta_id);
-
- D_MAC80211("disable hwcrypto key\n");
- break;
- default:
- ret = -EINVAL;
- }
-
- mutex_unlock(&il->mutex);
- D_MAC80211("leave\n");
-
- return ret;
-}
-
-int
-il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 * ssn,
- u8 buf_size, bool amsdu)
-{
- struct il_priv *il = hw->priv;
- int ret = -EINVAL;
-
- D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid);
-
- if (!(il->cfg->sku & IL_SKU_N))
- return -EACCES;
-
- mutex_lock(&il->mutex);
-
- switch (action) {
- case IEEE80211_AMPDU_RX_START:
- D_HT("start Rx\n");
- ret = il4965_sta_rx_agg_start(il, sta, tid, *ssn);
- break;
- case IEEE80211_AMPDU_RX_STOP:
- D_HT("stop Rx\n");
- ret = il4965_sta_rx_agg_stop(il, sta, tid);
- if (test_bit(S_EXIT_PENDING, &il->status))
- ret = 0;
- break;
- case IEEE80211_AMPDU_TX_START:
- D_HT("start Tx\n");
- ret = il4965_tx_agg_start(il, vif, sta, tid, ssn);
- break;
- case IEEE80211_AMPDU_TX_STOP_CONT:
- case IEEE80211_AMPDU_TX_STOP_FLUSH:
- case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- D_HT("stop Tx\n");
- ret = il4965_tx_agg_stop(il, vif, sta, tid);
- if (test_bit(S_EXIT_PENDING, &il->status))
- ret = 0;
- break;
- case IEEE80211_AMPDU_TX_OPERATIONAL:
- ret = 0;
- break;
- }
- mutex_unlock(&il->mutex);
-
- return ret;
-}
-
-int
-il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct il_priv *il = hw->priv;
- struct il_station_priv *sta_priv = (void *)sta->drv_priv;
- bool is_ap = vif->type == NL80211_IFTYPE_STATION;
- int ret;
- u8 sta_id;
-
- D_INFO("received request to add station %pM\n", sta->addr);
- mutex_lock(&il->mutex);
- D_INFO("proceeding to add station %pM\n", sta->addr);
- sta_priv->common.sta_id = IL_INVALID_STATION;
-
- atomic_set(&sta_priv->pending_frames, 0);
-
- ret =
- il_add_station_common(il, sta->addr, is_ap, sta, &sta_id);
- if (ret) {
- IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret);
- /* Should we return success if return code is EEXIST ? */
- mutex_unlock(&il->mutex);
- return ret;
- }
-
- sta_priv->common.sta_id = sta_id;
-
- /* Initialize rate scaling */
- D_INFO("Initializing rate scaling for station %pM\n", sta->addr);
- il4965_rs_rate_init(il, sta, sta_id);
- mutex_unlock(&il->mutex);
-
- return 0;
-}
-
-void
-il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_channel_switch *ch_switch)
-{
- struct il_priv *il = hw->priv;
- const struct il_channel_info *ch_info;
- struct ieee80211_conf *conf = &hw->conf;
- struct ieee80211_channel *channel = ch_switch->chandef.chan;
- struct il_ht_config *ht_conf = &il->current_ht_config;
- u16 ch;
-
- D_MAC80211("enter\n");
-
- mutex_lock(&il->mutex);
-
- if (il_is_rfkill(il))
- goto out;
-
- if (test_bit(S_EXIT_PENDING, &il->status) ||
- test_bit(S_SCANNING, &il->status) ||
- test_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
- goto out;
-
- if (!il_is_associated(il))
- goto out;
-
- if (!il->ops->set_channel_switch)
- goto out;
-
- ch = channel->hw_value;
- if (le16_to_cpu(il->active.channel) == ch)
- goto out;
-
- ch_info = il_get_channel_info(il, channel->band, ch);
- if (!il_is_channel_valid(ch_info)) {
- D_MAC80211("invalid channel\n");
- goto out;
- }
-
- spin_lock_irq(&il->lock);
-
- il->current_ht_config.smps = conf->smps_mode;
-
- /* Configure HT40 channels */
- switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
- il->ht.is_40mhz = false;
- il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
- break;
- case NL80211_CHAN_HT40MINUS:
- il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- il->ht.is_40mhz = true;
- break;
- case NL80211_CHAN_HT40PLUS:
- il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- il->ht.is_40mhz = true;
- break;
- }
-
- if ((le16_to_cpu(il->staging.channel) != ch))
- il->staging.flags = 0;
-
- il_set_rxon_channel(il, channel);
- il_set_rxon_ht(il, ht_conf);
- il_set_flags_for_band(il, channel->band, il->vif);
-
- spin_unlock_irq(&il->lock);
-
- il_set_rate(il);
- /*
- * at this point, staging_rxon has the
- * configuration for channel switch
- */
- set_bit(S_CHANNEL_SWITCH_PENDING, &il->status);
- il->switch_channel = cpu_to_le16(ch);
- if (il->ops->set_channel_switch(il, ch_switch)) {
- clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status);
- il->switch_channel = 0;
- ieee80211_chswitch_done(il->vif, false);
- }
-
-out:
- mutex_unlock(&il->mutex);
- D_MAC80211("leave\n");
-}
-
-void
-il4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
- unsigned int *total_flags, u64 multicast)
-{
- struct il_priv *il = hw->priv;
- __le32 filter_or = 0, filter_nand = 0;
-
-#define CHK(test, flag) do { \
- if (*total_flags & (test)) \
- filter_or |= (flag); \
- else \
- filter_nand |= (flag); \
- } while (0)
-
- D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags,
- *total_flags);
-
- CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK);
- /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */
- CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK);
- CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK);
-
-#undef CHK
-
- mutex_lock(&il->mutex);
-
- il->staging.filter_flags &= ~filter_nand;
- il->staging.filter_flags |= filter_or;
-
- /*
- * Not committing directly because hardware can perform a scan,
- * but we'll eventually commit the filter flags change anyway.
- */
-
- mutex_unlock(&il->mutex);
-
- /*
- * Receiving all multicast frames is always enabled by the
- * default flags setup in il_connection_init_rx_config()
- * since we currently do not support programming multicast
- * filters into the device.
- */
- *total_flags &=
- FIF_OTHER_BSS | FIF_ALLMULTI |
- FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
-}
-
-/*****************************************************************************
- *
- * driver setup and teardown
- *
- *****************************************************************************/
-
-static void
-il4965_bg_txpower_work(struct work_struct *work)
-{
- struct il_priv *il = container_of(work, struct il_priv,
- txpower_work);
-
- mutex_lock(&il->mutex);
-
- /* If a scan happened to start before we got here
- * then just return; the stats notification will
- * kick off another scheduled work to compensate for
- * any temperature delta we missed here. */
- if (test_bit(S_EXIT_PENDING, &il->status) ||
- test_bit(S_SCANNING, &il->status))
- goto out;
-
- /* Regardless of if we are associated, we must reconfigure the
- * TX power since frames can be sent on non-radar channels while
- * not associated */
- il->ops->send_tx_power(il);
-
- /* Update last_temperature to keep is_calib_needed from running
- * when it isn't needed... */
- il->last_temperature = il->temperature;
-out:
- mutex_unlock(&il->mutex);
-}
-
-static void
-il4965_setup_deferred_work(struct il_priv *il)
-{
- il->workqueue = create_singlethread_workqueue(DRV_NAME);
-
- init_waitqueue_head(&il->wait_command_queue);
-
- INIT_WORK(&il->restart, il4965_bg_restart);
- INIT_WORK(&il->rx_replenish, il4965_bg_rx_replenish);
- INIT_WORK(&il->run_time_calib_work, il4965_bg_run_time_calib_work);
- INIT_DELAYED_WORK(&il->init_alive_start, il4965_bg_init_alive_start);
- INIT_DELAYED_WORK(&il->alive_start, il4965_bg_alive_start);
-
- il_setup_scan_deferred_work(il);
-
- INIT_WORK(&il->txpower_work, il4965_bg_txpower_work);
-
- setup_timer(&il->stats_periodic, il4965_bg_stats_periodic,
- (unsigned long)il);
-
- setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il);
-
- tasklet_init(&il->irq_tasklet,
- (void (*)(unsigned long))il4965_irq_tasklet,
- (unsigned long)il);
-}
-
-static void
-il4965_cancel_deferred_work(struct il_priv *il)
-{
- cancel_work_sync(&il->txpower_work);
- cancel_delayed_work_sync(&il->init_alive_start);
- cancel_delayed_work(&il->alive_start);
- cancel_work_sync(&il->run_time_calib_work);
-
- il_cancel_scan_deferred_work(il);
-
- del_timer_sync(&il->stats_periodic);
-}
-
-static void
-il4965_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates)
-{
- int i;
-
- for (i = 0; i < RATE_COUNT_LEGACY; i++) {
- rates[i].bitrate = il_rates[i].ieee * 5;
- rates[i].hw_value = i; /* Rate scaling will work on idxes */
- rates[i].hw_value_short = i;
- rates[i].flags = 0;
- if ((i >= IL_FIRST_CCK_RATE) && (i <= IL_LAST_CCK_RATE)) {
- /*
- * If CCK != 1M then set short preamble rate flag.
- */
- rates[i].flags |=
- (il_rates[i].plcp ==
- RATE_1M_PLCP) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE;
- }
- }
-}
-
-/*
- * Acquire il->lock before calling this function !
- */
-void
-il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx)
-{
- il_wr(il, HBUS_TARG_WRPTR, (idx & 0xff) | (txq_id << 8));
- il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(txq_id), idx);
-}
-
-void
-il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq,
- int tx_fifo_id, int scd_retry)
-{
- int txq_id = txq->q.id;
-
- /* Find out whether to activate Tx queue */
- int active = test_bit(txq_id, &il->txq_ctx_active_msk) ? 1 : 0;
-
- /* Set up and activate */
- il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id),
- (active << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
- (tx_fifo_id << IL49_SCD_QUEUE_STTS_REG_POS_TXF) |
- (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_WSL) |
- (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) |
- IL49_SCD_QUEUE_STTS_REG_MSK);
-
- txq->sched_retry = scd_retry;
-
- D_INFO("%s %s Queue %d on AC %d\n", active ? "Activate" : "Deactivate",
- scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
-}
-
-static const struct ieee80211_ops il4965_mac_ops = {
- .tx = il4965_mac_tx,
- .start = il4965_mac_start,
- .stop = il4965_mac_stop,
- .add_interface = il_mac_add_interface,
- .remove_interface = il_mac_remove_interface,
- .change_interface = il_mac_change_interface,
- .config = il_mac_config,
- .configure_filter = il4965_configure_filter,
- .set_key = il4965_mac_set_key,
- .update_tkip_key = il4965_mac_update_tkip_key,
- .conf_tx = il_mac_conf_tx,
- .reset_tsf = il_mac_reset_tsf,
- .bss_info_changed = il_mac_bss_info_changed,
- .ampdu_action = il4965_mac_ampdu_action,
- .hw_scan = il_mac_hw_scan,
- .sta_add = il4965_mac_sta_add,
- .sta_remove = il_mac_sta_remove,
- .channel_switch = il4965_mac_channel_switch,
- .tx_last_beacon = il_mac_tx_last_beacon,
- .flush = il_mac_flush,
-};
-
-static int
-il4965_init_drv(struct il_priv *il)
-{
- int ret;
-
- spin_lock_init(&il->sta_lock);
- spin_lock_init(&il->hcmd_lock);
-
- INIT_LIST_HEAD(&il->free_frames);
-
- mutex_init(&il->mutex);
-
- il->ieee_channels = NULL;
- il->ieee_rates = NULL;
- il->band = IEEE80211_BAND_2GHZ;
-
- il->iw_mode = NL80211_IFTYPE_STATION;
- il->current_ht_config.smps = IEEE80211_SMPS_STATIC;
- il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF;
-
- /* initialize force reset */
- il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD;
-
- /* Choose which receivers/antennas to use */
- if (il->ops->set_rxon_chain)
- il->ops->set_rxon_chain(il);
-
- il_init_scan_params(il);
-
- ret = il_init_channel_map(il);
- if (ret) {
- IL_ERR("initializing regulatory failed: %d\n", ret);
- goto err;
- }
-
- ret = il_init_geos(il);
- if (ret) {
- IL_ERR("initializing geos failed: %d\n", ret);
- goto err_free_channel_map;
- }
- il4965_init_hw_rates(il, il->ieee_rates);
-
- return 0;
-
-err_free_channel_map:
- il_free_channel_map(il);
-err:
- return ret;
-}
-
-static void
-il4965_uninit_drv(struct il_priv *il)
-{
- il_free_geos(il);
- il_free_channel_map(il);
- kfree(il->scan_cmd);
-}
-
-static void
-il4965_hw_detect(struct il_priv *il)
-{
- il->hw_rev = _il_rd(il, CSR_HW_REV);
- il->hw_wa_rev = _il_rd(il, CSR_HW_REV_WA_REG);
- il->rev_id = il->pci_dev->revision;
- D_INFO("HW Revision ID = 0x%X\n", il->rev_id);
-}
-
-static struct il_sensitivity_ranges il4965_sensitivity = {
- .min_nrg_cck = 97,
- .max_nrg_cck = 0, /* not used, set to 0 */
-
- .auto_corr_min_ofdm = 85,
- .auto_corr_min_ofdm_mrc = 170,
- .auto_corr_min_ofdm_x1 = 105,
- .auto_corr_min_ofdm_mrc_x1 = 220,
-
- .auto_corr_max_ofdm = 120,
- .auto_corr_max_ofdm_mrc = 210,
- .auto_corr_max_ofdm_x1 = 140,
- .auto_corr_max_ofdm_mrc_x1 = 270,
-
- .auto_corr_min_cck = 125,
- .auto_corr_max_cck = 200,
- .auto_corr_min_cck_mrc = 200,
- .auto_corr_max_cck_mrc = 400,
-
- .nrg_th_cck = 100,
- .nrg_th_ofdm = 100,
-
- .barker_corr_th_min = 190,
- .barker_corr_th_min_mrc = 390,
- .nrg_th_cca = 62,
-};
-
-static void
-il4965_set_hw_params(struct il_priv *il)
-{
- il->hw_params.bcast_id = IL4965_BROADCAST_ID;
- il->hw_params.max_rxq_size = RX_QUEUE_SIZE;
- il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
- if (il->cfg->mod_params->amsdu_size_8K)
- il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_8K);
- else
- il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_4K);
-
- il->hw_params.max_beacon_itrvl = IL_MAX_UCODE_BEACON_INTERVAL;
-
- if (il->cfg->mod_params->disable_11n)
- il->cfg->sku &= ~IL_SKU_N;
-
- if (il->cfg->mod_params->num_of_queues >= IL_MIN_NUM_QUEUES &&
- il->cfg->mod_params->num_of_queues <= IL49_NUM_QUEUES)
- il->cfg->num_of_queues =
- il->cfg->mod_params->num_of_queues;
-
- il->hw_params.max_txq_num = il->cfg->num_of_queues;
- il->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM;
- il->hw_params.scd_bc_tbls_size =
- il->cfg->num_of_queues *
- sizeof(struct il4965_scd_bc_tbl);
-
- il->hw_params.tfd_size = sizeof(struct il_tfd);
- il->hw_params.max_stations = IL4965_STATION_COUNT;
- il->hw_params.max_data_size = IL49_RTC_DATA_SIZE;
- il->hw_params.max_inst_size = IL49_RTC_INST_SIZE;
- il->hw_params.max_bsm_size = BSM_SRAM_SIZE;
- il->hw_params.ht40_channel = BIT(IEEE80211_BAND_5GHZ);
-
- il->hw_params.rx_wrt_ptr_reg = FH49_RSCSR_CHNL0_WPTR;
-
- il->hw_params.tx_chains_num = il4965_num_of_ant(il->cfg->valid_tx_ant);
- il->hw_params.rx_chains_num = il4965_num_of_ant(il->cfg->valid_rx_ant);
- il->hw_params.valid_tx_ant = il->cfg->valid_tx_ant;
- il->hw_params.valid_rx_ant = il->cfg->valid_rx_ant;
-
- il->hw_params.ct_kill_threshold =
- CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY);
-
- il->hw_params.sens = &il4965_sensitivity;
- il->hw_params.beacon_time_tsf_bits = IL4965_EXT_BEACON_TIME_POS;
-}
-
-static int
-il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- int err = 0;
- struct il_priv *il;
- struct ieee80211_hw *hw;
- struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data);
- unsigned long flags;
- u16 pci_cmd;
-
- /************************
- * 1. Allocating HW data
- ************************/
-
- hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il4965_mac_ops);
- if (!hw) {
- err = -ENOMEM;
- goto out;
- }
- il = hw->priv;
- il->hw = hw;
- SET_IEEE80211_DEV(hw, &pdev->dev);
-
- D_INFO("*** LOAD DRIVER ***\n");
- il->cfg = cfg;
- il->ops = &il4965_ops;
-#ifdef CONFIG_IWLEGACY_DEBUGFS
- il->debugfs_ops = &il4965_debugfs_ops;
-#endif
- il->pci_dev = pdev;
- il->inta_mask = CSR_INI_SET_MASK;
-
- /**************************
- * 2. Initializing PCI bus
- **************************/
- pci_disable_link_state(pdev,
- PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
- PCIE_LINK_STATE_CLKPM);
-
- if (pci_enable_device(pdev)) {
- err = -ENODEV;
- goto out_ieee80211_free_hw;
- }
-
- pci_set_master(pdev);
-
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
- if (!err)
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
- if (err) {
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (!err)
- err =
- pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- /* both attempts failed: */
- if (err) {
- IL_WARN("No suitable DMA available.\n");
- goto out_pci_disable_device;
- }
- }
-
- err = pci_request_regions(pdev, DRV_NAME);
- if (err)
- goto out_pci_disable_device;
-
- pci_set_drvdata(pdev, il);
-
- /***********************
- * 3. Read REV register
- ***********************/
- il->hw_base = pci_ioremap_bar(pdev, 0);
- if (!il->hw_base) {
- err = -ENODEV;
- goto out_pci_release_regions;
- }
-
- D_INFO("pci_resource_len = 0x%08llx\n",
- (unsigned long long)pci_resource_len(pdev, 0));
- D_INFO("pci_resource_base = %p\n", il->hw_base);
-
- /* these spin locks will be used in apm_ops.init and EEPROM access
- * we should init now
- */
- spin_lock_init(&il->reg_lock);
- spin_lock_init(&il->lock);
-
- /*
- * stop and reset the on-board processor just in case it is in a
- * strange state ... like being left stranded by a primary kernel
- * and this is now the kdump kernel trying to start up
- */
- _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
-
- il4965_hw_detect(il);
- IL_INFO("Detected %s, REV=0x%X\n", il->cfg->name, il->hw_rev);
-
- /* We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state */
- pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
-
- il4965_prepare_card_hw(il);
- if (!il->hw_ready) {
- IL_WARN("Failed, HW not ready\n");
- err = -EIO;
- goto out_iounmap;
- }
-
- /*****************
- * 4. Read EEPROM
- *****************/
- /* Read the EEPROM */
- err = il_eeprom_init(il);
- if (err) {
- IL_ERR("Unable to init EEPROM\n");
- goto out_iounmap;
- }
- err = il4965_eeprom_check_version(il);
- if (err)
- goto out_free_eeprom;
-
- /* extract MAC Address */
- il4965_eeprom_get_mac(il, il->addresses[0].addr);
- D_INFO("MAC address: %pM\n", il->addresses[0].addr);
- il->hw->wiphy->addresses = il->addresses;
- il->hw->wiphy->n_addresses = 1;
-
- /************************
- * 5. Setup HW constants
- ************************/
- il4965_set_hw_params(il);
-
- /*******************
- * 6. Setup il
- *******************/
-
- err = il4965_init_drv(il);
- if (err)
- goto out_free_eeprom;
- /* At this point both hw and il are initialized. */
-
- /********************
- * 7. Setup services
- ********************/
- spin_lock_irqsave(&il->lock, flags);
- il_disable_interrupts(il);
- spin_unlock_irqrestore(&il->lock, flags);
-
- pci_enable_msi(il->pci_dev);
-
- err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il);
- if (err) {
- IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq);
- goto out_disable_msi;
- }
-
- il4965_setup_deferred_work(il);
- il4965_setup_handlers(il);
-
- /*********************************************
- * 8. Enable interrupts and read RFKILL state
- *********************************************/
-
- /* enable rfkill interrupt: hw bug w/a */
- pci_read_config_word(il->pci_dev, PCI_COMMAND, &pci_cmd);
- if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
- pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
- pci_write_config_word(il->pci_dev, PCI_COMMAND, pci_cmd);
- }
-
- il_enable_rfkill_int(il);
-
- /* If platform's RF_KILL switch is NOT set to KILL */
- if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
- clear_bit(S_RFKILL, &il->status);
- else
- set_bit(S_RFKILL, &il->status);
-
- wiphy_rfkill_set_hw_state(il->hw->wiphy,
- test_bit(S_RFKILL, &il->status));
-
- il_power_initialize(il);
-
- init_completion(&il->_4965.firmware_loading_complete);
-
- err = il4965_request_firmware(il, true);
- if (err)
- goto out_destroy_workqueue;
-
- return 0;
-
-out_destroy_workqueue:
- destroy_workqueue(il->workqueue);
- il->workqueue = NULL;
- free_irq(il->pci_dev->irq, il);
-out_disable_msi:
- pci_disable_msi(il->pci_dev);
- il4965_uninit_drv(il);
-out_free_eeprom:
- il_eeprom_free(il);
-out_iounmap:
- iounmap(il->hw_base);
-out_pci_release_regions:
- pci_release_regions(pdev);
-out_pci_disable_device:
- pci_disable_device(pdev);
-out_ieee80211_free_hw:
- ieee80211_free_hw(il->hw);
-out:
- return err;
-}
-
-static void
-il4965_pci_remove(struct pci_dev *pdev)
-{
- struct il_priv *il = pci_get_drvdata(pdev);
- unsigned long flags;
-
- if (!il)
- return;
-
- wait_for_completion(&il->_4965.firmware_loading_complete);
-
- D_INFO("*** UNLOAD DRIVER ***\n");
-
- il_dbgfs_unregister(il);
- sysfs_remove_group(&pdev->dev.kobj, &il_attribute_group);
-
- /* ieee80211_unregister_hw call wil cause il_mac_stop to
- * to be called and il4965_down since we are removing the device
- * we need to set S_EXIT_PENDING bit.
- */
- set_bit(S_EXIT_PENDING, &il->status);
-
- il_leds_exit(il);
-
- if (il->mac80211_registered) {
- ieee80211_unregister_hw(il->hw);
- il->mac80211_registered = 0;
- } else {
- il4965_down(il);
- }
-
- /*
- * Make sure device is reset to low power before unloading driver.
- * This may be redundant with il4965_down(), but there are paths to
- * run il4965_down() without calling apm_ops.stop(), and there are
- * paths to avoid running il4965_down() at all before leaving driver.
- * This (inexpensive) call *makes sure* device is reset.
- */
- il_apm_stop(il);
-
- /* make sure we flush any pending irq or
- * tasklet for the driver
- */
- spin_lock_irqsave(&il->lock, flags);
- il_disable_interrupts(il);
- spin_unlock_irqrestore(&il->lock, flags);
-
- il4965_synchronize_irq(il);
-
- il4965_dealloc_ucode_pci(il);
-
- if (il->rxq.bd)
- il4965_rx_queue_free(il, &il->rxq);
- il4965_hw_txq_ctx_free(il);
-
- il_eeprom_free(il);
-
- /*netif_stop_queue(dev); */
- flush_workqueue(il->workqueue);
-
- /* ieee80211_unregister_hw calls il_mac_stop, which flushes
- * il->workqueue... so we can't take down the workqueue
- * until now... */
- destroy_workqueue(il->workqueue);
- il->workqueue = NULL;
-
- free_irq(il->pci_dev->irq, il);
- pci_disable_msi(il->pci_dev);
- iounmap(il->hw_base);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
-
- il4965_uninit_drv(il);
-
- dev_kfree_skb(il->beacon_skb);
-
- ieee80211_free_hw(il->hw);
-}
-
-/*
- * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
- * must be called under il->lock and mac access
- */
-void
-il4965_txq_set_sched(struct il_priv *il, u32 mask)
-{
- il_wr_prph(il, IL49_SCD_TXFACT, mask);
-}
-
-/*****************************************************************************
- *
- * driver and module entry point
- *
- *****************************************************************************/
-
-/* Hardware specific file defines the PCI IDs table for that hardware module */
-static const struct pci_device_id il4965_hw_card_ids[] = {
- {IL_PCI_DEVICE(0x4229, PCI_ANY_ID, il4965_cfg)},
- {IL_PCI_DEVICE(0x4230, PCI_ANY_ID, il4965_cfg)},
- {0}
-};
-MODULE_DEVICE_TABLE(pci, il4965_hw_card_ids);
-
-static struct pci_driver il4965_driver = {
- .name = DRV_NAME,
- .id_table = il4965_hw_card_ids,
- .probe = il4965_pci_probe,
- .remove = il4965_pci_remove,
- .driver.pm = IL_LEGACY_PM_OPS,
-};
-
-static int __init
-il4965_init(void)
-{
-
- int ret;
- pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
- pr_info(DRV_COPYRIGHT "\n");
-
- ret = il4965_rate_control_register();
- if (ret) {
- pr_err("Unable to register rate control algorithm: %d\n", ret);
- return ret;
- }
-
- ret = pci_register_driver(&il4965_driver);
- if (ret) {
- pr_err("Unable to initialize PCI module\n");
- goto error_register;
- }
-
- return ret;
-
-error_register:
- il4965_rate_control_unregister();
- return ret;
-}
-
-static void __exit
-il4965_exit(void)
-{
- pci_unregister_driver(&il4965_driver);
- il4965_rate_control_unregister();
-}
-
-module_exit(il4965_exit);
-module_init(il4965_init);
-
-#ifdef CONFIG_IWLEGACY_DEBUG
-module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "debug output mask");
-#endif
-
-module_param_named(swcrypto, il4965_mod_params.sw_crypto, int, S_IRUGO);
-MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
-module_param_named(queues_num, il4965_mod_params.num_of_queues, int, S_IRUGO);
-MODULE_PARM_DESC(queues_num, "number of hw queues.");
-module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO);
-MODULE_PARM_DESC(11n_disable, "disable 11n functionality");
-module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int,
- S_IRUGO);
-MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])");
-module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO);
-MODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c
deleted file mode 100644
index 887114582583..000000000000
--- a/drivers/net/wireless/iwlegacy/common.c
+++ /dev/null
@@ -1,5586 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/etherdevice.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/lockdep.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <net/mac80211.h>
-
-#include "common.h"
-
-int
-_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout)
-{
- const int interval = 10; /* microseconds */
- int t = 0;
-
- do {
- if ((_il_rd(il, addr) & mask) == (bits & mask))
- return t;
- udelay(interval);
- t += interval;
- } while (t < timeout);
-
- return -ETIMEDOUT;
-}
-EXPORT_SYMBOL(_il_poll_bit);
-
-void
-il_set_bit(struct il_priv *p, u32 r, u32 m)
-{
- unsigned long reg_flags;
-
- spin_lock_irqsave(&p->reg_lock, reg_flags);
- _il_set_bit(p, r, m);
- spin_unlock_irqrestore(&p->reg_lock, reg_flags);
-}
-EXPORT_SYMBOL(il_set_bit);
-
-void
-il_clear_bit(struct il_priv *p, u32 r, u32 m)
-{
- unsigned long reg_flags;
-
- spin_lock_irqsave(&p->reg_lock, reg_flags);
- _il_clear_bit(p, r, m);
- spin_unlock_irqrestore(&p->reg_lock, reg_flags);
-}
-EXPORT_SYMBOL(il_clear_bit);
-
-bool
-_il_grab_nic_access(struct il_priv *il)
-{
- int ret;
- u32 val;
-
- /* this bit wakes up the NIC */
- _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-
- /*
- * These bits say the device is running, and should keep running for
- * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
- * but they do not indicate that embedded SRAM is restored yet;
- * 3945 and 4965 have volatile SRAM, and must save/restore contents
- * to/from host DRAM when sleeping/waking for power-saving.
- * Each direction takes approximately 1/4 millisecond; with this
- * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
- * series of register accesses are expected (e.g. reading Event Log),
- * to keep device from sleeping.
- *
- * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
- * SRAM is okay/restored. We don't check that here because this call
- * is just for hardware register access; but GP1 MAC_SLEEP check is a
- * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
- *
- */
- ret =
- _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
- (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
- CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
- if (unlikely(ret < 0)) {
- val = _il_rd(il, CSR_GP_CNTRL);
- WARN_ONCE(1, "Timeout waiting for ucode processor access "
- "(CSR_GP_CNTRL 0x%08x)\n", val);
- _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
- return false;
- }
-
- return true;
-}
-EXPORT_SYMBOL_GPL(_il_grab_nic_access);
-
-int
-il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout)
-{
- const int interval = 10; /* microseconds */
- int t = 0;
-
- do {
- if ((il_rd(il, addr) & mask) == mask)
- return t;
- udelay(interval);
- t += interval;
- } while (t < timeout);
-
- return -ETIMEDOUT;
-}
-EXPORT_SYMBOL(il_poll_bit);
-
-u32
-il_rd_prph(struct il_priv *il, u32 reg)
-{
- unsigned long reg_flags;
- u32 val;
-
- spin_lock_irqsave(&il->reg_lock, reg_flags);
- _il_grab_nic_access(il);
- val = _il_rd_prph(il, reg);
- _il_release_nic_access(il);
- spin_unlock_irqrestore(&il->reg_lock, reg_flags);
- return val;
-}
-EXPORT_SYMBOL(il_rd_prph);
-
-void
-il_wr_prph(struct il_priv *il, u32 addr, u32 val)
-{
- unsigned long reg_flags;
-
- spin_lock_irqsave(&il->reg_lock, reg_flags);
- if (likely(_il_grab_nic_access(il))) {
- _il_wr_prph(il, addr, val);
- _il_release_nic_access(il);
- }
- spin_unlock_irqrestore(&il->reg_lock, reg_flags);
-}
-EXPORT_SYMBOL(il_wr_prph);
-
-u32
-il_read_targ_mem(struct il_priv *il, u32 addr)
-{
- unsigned long reg_flags;
- u32 value;
-
- spin_lock_irqsave(&il->reg_lock, reg_flags);
- _il_grab_nic_access(il);
-
- _il_wr(il, HBUS_TARG_MEM_RADDR, addr);
- value = _il_rd(il, HBUS_TARG_MEM_RDAT);
-
- _il_release_nic_access(il);
- spin_unlock_irqrestore(&il->reg_lock, reg_flags);
- return value;
-}
-EXPORT_SYMBOL(il_read_targ_mem);
-
-void
-il_write_targ_mem(struct il_priv *il, u32 addr, u32 val)
-{
- unsigned long reg_flags;
-
- spin_lock_irqsave(&il->reg_lock, reg_flags);
- if (likely(_il_grab_nic_access(il))) {
- _il_wr(il, HBUS_TARG_MEM_WADDR, addr);
- _il_wr(il, HBUS_TARG_MEM_WDAT, val);
- _il_release_nic_access(il);
- }
- spin_unlock_irqrestore(&il->reg_lock, reg_flags);
-}
-EXPORT_SYMBOL(il_write_targ_mem);
-
-const char *
-il_get_cmd_string(u8 cmd)
-{
- switch (cmd) {
- IL_CMD(N_ALIVE);
- IL_CMD(N_ERROR);
- IL_CMD(C_RXON);
- IL_CMD(C_RXON_ASSOC);
- IL_CMD(C_QOS_PARAM);
- IL_CMD(C_RXON_TIMING);
- IL_CMD(C_ADD_STA);
- IL_CMD(C_REM_STA);
- IL_CMD(C_WEPKEY);
- IL_CMD(N_3945_RX);
- IL_CMD(C_TX);
- IL_CMD(C_RATE_SCALE);
- IL_CMD(C_LEDS);
- IL_CMD(C_TX_LINK_QUALITY_CMD);
- IL_CMD(C_CHANNEL_SWITCH);
- IL_CMD(N_CHANNEL_SWITCH);
- IL_CMD(C_SPECTRUM_MEASUREMENT);
- IL_CMD(N_SPECTRUM_MEASUREMENT);
- IL_CMD(C_POWER_TBL);
- IL_CMD(N_PM_SLEEP);
- IL_CMD(N_PM_DEBUG_STATS);
- IL_CMD(C_SCAN);
- IL_CMD(C_SCAN_ABORT);
- IL_CMD(N_SCAN_START);
- IL_CMD(N_SCAN_RESULTS);
- IL_CMD(N_SCAN_COMPLETE);
- IL_CMD(N_BEACON);
- IL_CMD(C_TX_BEACON);
- IL_CMD(C_TX_PWR_TBL);
- IL_CMD(C_BT_CONFIG);
- IL_CMD(C_STATS);
- IL_CMD(N_STATS);
- IL_CMD(N_CARD_STATE);
- IL_CMD(N_MISSED_BEACONS);
- IL_CMD(C_CT_KILL_CONFIG);
- IL_CMD(C_SENSITIVITY);
- IL_CMD(C_PHY_CALIBRATION);
- IL_CMD(N_RX_PHY);
- IL_CMD(N_RX_MPDU);
- IL_CMD(N_RX);
- IL_CMD(N_COMPRESSED_BA);
- default:
- return "UNKNOWN";
-
- }
-}
-EXPORT_SYMBOL(il_get_cmd_string);
-
-#define HOST_COMPLETE_TIMEOUT (HZ / 2)
-
-static void
-il_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd,
- struct il_rx_pkt *pkt)
-{
- if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
- IL_ERR("Bad return from %s (0x%08X)\n",
- il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
- return;
- }
-#ifdef CONFIG_IWLEGACY_DEBUG
- switch (cmd->hdr.cmd) {
- case C_TX_LINK_QUALITY_CMD:
- case C_SENSITIVITY:
- D_HC_DUMP("back from %s (0x%08X)\n",
- il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
- break;
- default:
- D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd),
- pkt->hdr.flags);
- }
-#endif
-}
-
-static int
-il_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd)
-{
- int ret;
-
- BUG_ON(!(cmd->flags & CMD_ASYNC));
-
- /* An asynchronous command can not expect an SKB to be set. */
- BUG_ON(cmd->flags & CMD_WANT_SKB);
-
- /* Assign a generic callback if one is not provided */
- if (!cmd->callback)
- cmd->callback = il_generic_cmd_callback;
-
- if (test_bit(S_EXIT_PENDING, &il->status))
- return -EBUSY;
-
- ret = il_enqueue_hcmd(il, cmd);
- if (ret < 0) {
- IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
- il_get_cmd_string(cmd->id), ret);
- return ret;
- }
- return 0;
-}
-
-int
-il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd)
-{
- int cmd_idx;
- int ret;
-
- lockdep_assert_held(&il->mutex);
-
- BUG_ON(cmd->flags & CMD_ASYNC);
-
- /* A synchronous command can not have a callback set. */
- BUG_ON(cmd->callback);
-
- D_INFO("Attempting to send sync command %s\n",
- il_get_cmd_string(cmd->id));
-
- set_bit(S_HCMD_ACTIVE, &il->status);
- D_INFO("Setting HCMD_ACTIVE for command %s\n",
- il_get_cmd_string(cmd->id));
-
- cmd_idx = il_enqueue_hcmd(il, cmd);
- if (cmd_idx < 0) {
- ret = cmd_idx;
- IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
- il_get_cmd_string(cmd->id), ret);
- goto out;
- }
-
- ret = wait_event_timeout(il->wait_command_queue,
- !test_bit(S_HCMD_ACTIVE, &il->status),
- HOST_COMPLETE_TIMEOUT);
- if (!ret) {
- if (test_bit(S_HCMD_ACTIVE, &il->status)) {
- IL_ERR("Error sending %s: time out after %dms.\n",
- il_get_cmd_string(cmd->id),
- jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
-
- clear_bit(S_HCMD_ACTIVE, &il->status);
- D_INFO("Clearing HCMD_ACTIVE for command %s\n",
- il_get_cmd_string(cmd->id));
- ret = -ETIMEDOUT;
- goto cancel;
- }
- }
-
- if (test_bit(S_RFKILL, &il->status)) {
- IL_ERR("Command %s aborted: RF KILL Switch\n",
- il_get_cmd_string(cmd->id));
- ret = -ECANCELED;
- goto fail;
- }
- if (test_bit(S_FW_ERROR, &il->status)) {
- IL_ERR("Command %s failed: FW Error\n",
- il_get_cmd_string(cmd->id));
- ret = -EIO;
- goto fail;
- }
- if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
- IL_ERR("Error: Response NULL in '%s'\n",
- il_get_cmd_string(cmd->id));
- ret = -EIO;
- goto cancel;
- }
-
- ret = 0;
- goto out;
-
-cancel:
- if (cmd->flags & CMD_WANT_SKB) {
- /*
- * Cancel the CMD_WANT_SKB flag for the cmd in the
- * TX cmd queue. Otherwise in case the cmd comes
- * in later, it will possibly set an invalid
- * address (cmd->meta.source).
- */
- il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB;
- }
-fail:
- if (cmd->reply_page) {
- il_free_pages(il, cmd->reply_page);
- cmd->reply_page = 0;
- }
-out:
- return ret;
-}
-EXPORT_SYMBOL(il_send_cmd_sync);
-
-int
-il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd)
-{
- if (cmd->flags & CMD_ASYNC)
- return il_send_cmd_async(il, cmd);
-
- return il_send_cmd_sync(il, cmd);
-}
-EXPORT_SYMBOL(il_send_cmd);
-
-int
-il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data)
-{
- struct il_host_cmd cmd = {
- .id = id,
- .len = len,
- .data = data,
- };
-
- return il_send_cmd_sync(il, &cmd);
-}
-EXPORT_SYMBOL(il_send_cmd_pdu);
-
-int
-il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data,
- void (*callback) (struct il_priv *il,
- struct il_device_cmd *cmd,
- struct il_rx_pkt *pkt))
-{
- struct il_host_cmd cmd = {
- .id = id,
- .len = len,
- .data = data,
- };
-
- cmd.flags |= CMD_ASYNC;
- cmd.callback = callback;
-
- return il_send_cmd_async(il, &cmd);
-}
-EXPORT_SYMBOL(il_send_cmd_pdu_async);
-
-/* default: IL_LED_BLINK(0) using blinking idx table */
-static int led_mode;
-module_param(led_mode, int, S_IRUGO);
-MODULE_PARM_DESC(led_mode,
- "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking");
-
-/* Throughput OFF time(ms) ON time (ms)
- * >300 25 25
- * >200 to 300 40 40
- * >100 to 200 55 55
- * >70 to 100 65 65
- * >50 to 70 75 75
- * >20 to 50 85 85
- * >10 to 20 95 95
- * >5 to 10 110 110
- * >1 to 5 130 130
- * >0 to 1 167 167
- * <=0 SOLID ON
- */
-static const struct ieee80211_tpt_blink il_blink[] = {
- {.throughput = 0, .blink_time = 334},
- {.throughput = 1 * 1024 - 1, .blink_time = 260},
- {.throughput = 5 * 1024 - 1, .blink_time = 220},
- {.throughput = 10 * 1024 - 1, .blink_time = 190},
- {.throughput = 20 * 1024 - 1, .blink_time = 170},
- {.throughput = 50 * 1024 - 1, .blink_time = 150},
- {.throughput = 70 * 1024 - 1, .blink_time = 130},
- {.throughput = 100 * 1024 - 1, .blink_time = 110},
- {.throughput = 200 * 1024 - 1, .blink_time = 80},
- {.throughput = 300 * 1024 - 1, .blink_time = 50},
-};
-
-/*
- * Adjust led blink rate to compensate on a MAC Clock difference on every HW
- * Led blink rate analysis showed an average deviation of 0% on 3945,
- * 5% on 4965 HW.
- * Need to compensate on the led on/off time per HW according to the deviation
- * to achieve the desired led frequency
- * The calculation is: (100-averageDeviation)/100 * blinkTime
- * For code efficiency the calculation will be:
- * compensation = (100 - averageDeviation) * 64 / 100
- * NewBlinkTime = (compensation * BlinkTime) / 64
- */
-static inline u8
-il_blink_compensation(struct il_priv *il, u8 time, u16 compensation)
-{
- if (!compensation) {
- IL_ERR("undefined blink compensation: "
- "use pre-defined blinking time\n");
- return time;
- }
-
- return (u8) ((time * compensation) >> 6);
-}
-
-/* Set led pattern command */
-static int
-il_led_cmd(struct il_priv *il, unsigned long on, unsigned long off)
-{
- struct il_led_cmd led_cmd = {
- .id = IL_LED_LINK,
- .interval = IL_DEF_LED_INTRVL
- };
- int ret;
-
- if (!test_bit(S_READY, &il->status))
- return -EBUSY;
-
- if (il->blink_on == on && il->blink_off == off)
- return 0;
-
- if (off == 0) {
- /* led is SOLID_ON */
- on = IL_LED_SOLID;
- }
-
- D_LED("Led blink time compensation=%u\n",
- il->cfg->led_compensation);
- led_cmd.on =
- il_blink_compensation(il, on,
- il->cfg->led_compensation);
- led_cmd.off =
- il_blink_compensation(il, off,
- il->cfg->led_compensation);
-
- ret = il->ops->send_led_cmd(il, &led_cmd);
- if (!ret) {
- il->blink_on = on;
- il->blink_off = off;
- }
- return ret;
-}
-
-static void
-il_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct il_priv *il = container_of(led_cdev, struct il_priv, led);
- unsigned long on = 0;
-
- if (brightness > 0)
- on = IL_LED_SOLID;
-
- il_led_cmd(il, on, 0);
-}
-
-static int
-il_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
- unsigned long *delay_off)
-{
- struct il_priv *il = container_of(led_cdev, struct il_priv, led);
-
- return il_led_cmd(il, *delay_on, *delay_off);
-}
-
-void
-il_leds_init(struct il_priv *il)
-{
- int mode = led_mode;
- int ret;
-
- if (mode == IL_LED_DEFAULT)
- mode = il->cfg->led_mode;
-
- il->led.name =
- kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy));
- il->led.brightness_set = il_led_brightness_set;
- il->led.blink_set = il_led_blink_set;
- il->led.max_brightness = 1;
-
- switch (mode) {
- case IL_LED_DEFAULT:
- WARN_ON(1);
- break;
- case IL_LED_BLINK:
- il->led.default_trigger =
- ieee80211_create_tpt_led_trigger(il->hw,
- IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
- il_blink,
- ARRAY_SIZE(il_blink));
- break;
- case IL_LED_RF_STATE:
- il->led.default_trigger = ieee80211_get_radio_led_name(il->hw);
- break;
- }
-
- ret = led_classdev_register(&il->pci_dev->dev, &il->led);
- if (ret) {
- kfree(il->led.name);
- return;
- }
-
- il->led_registered = true;
-}
-EXPORT_SYMBOL(il_leds_init);
-
-void
-il_leds_exit(struct il_priv *il)
-{
- if (!il->led_registered)
- return;
-
- led_classdev_unregister(&il->led);
- kfree(il->led.name);
-}
-EXPORT_SYMBOL(il_leds_exit);
-
-/************************** EEPROM BANDS ****************************
- *
- * The il_eeprom_band definitions below provide the mapping from the
- * EEPROM contents to the specific channel number supported for each
- * band.
- *
- * For example, il_priv->eeprom.band_3_channels[4] from the band_3
- * definition below maps to physical channel 42 in the 5.2GHz spectrum.
- * The specific geography and calibration information for that channel
- * is contained in the eeprom map itself.
- *
- * During init, we copy the eeprom information and channel map
- * information into il->channel_info_24/52 and il->channel_map_24/52
- *
- * channel_map_24/52 provides the idx in the channel_info array for a
- * given channel. We have to have two separate maps as there is channel
- * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and
- * band_2
- *
- * A value of 0xff stored in the channel_map indicates that the channel
- * is not supported by the hardware at all.
- *
- * A value of 0xfe in the channel_map indicates that the channel is not
- * valid for Tx with the current hardware. This means that
- * while the system can tune and receive on a given channel, it may not
- * be able to associate or transmit any frames on that
- * channel. There is no corresponding channel information for that
- * entry.
- *
- *********************************************************************/
-
-/* 2.4 GHz */
-const u8 il_eeprom_band_1[14] = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
-};
-
-/* 5.2 GHz bands */
-static const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */
- 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16
-};
-
-static const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */
- 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
-};
-
-static const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */
- 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
-};
-
-static const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */
- 145, 149, 153, 157, 161, 165
-};
-
-static const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */
- 1, 2, 3, 4, 5, 6, 7
-};
-
-static const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */
- 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
-};
-
-/******************************************************************************
- *
- * EEPROM related functions
- *
-******************************************************************************/
-
-static int
-il_eeprom_verify_signature(struct il_priv *il)
-{
- u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
- int ret = 0;
-
- D_EEPROM("EEPROM signature=0x%08x\n", gp);
- switch (gp) {
- case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
- case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
- break;
- default:
- IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp);
- ret = -ENOENT;
- break;
- }
- return ret;
-}
-
-const u8 *
-il_eeprom_query_addr(const struct il_priv *il, size_t offset)
-{
- BUG_ON(offset >= il->cfg->eeprom_size);
- return &il->eeprom[offset];
-}
-EXPORT_SYMBOL(il_eeprom_query_addr);
-
-u16
-il_eeprom_query16(const struct il_priv *il, size_t offset)
-{
- if (!il->eeprom)
- return 0;
- return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8);
-}
-EXPORT_SYMBOL(il_eeprom_query16);
-
-/**
- * il_eeprom_init - read EEPROM contents
- *
- * Load the EEPROM contents from adapter into il->eeprom
- *
- * NOTE: This routine uses the non-debug IO access functions.
- */
-int
-il_eeprom_init(struct il_priv *il)
-{
- __le16 *e;
- u32 gp = _il_rd(il, CSR_EEPROM_GP);
- int sz;
- int ret;
- u16 addr;
-
- /* allocate eeprom */
- sz = il->cfg->eeprom_size;
- D_EEPROM("NVM size = %d\n", sz);
- il->eeprom = kzalloc(sz, GFP_KERNEL);
- if (!il->eeprom) {
- ret = -ENOMEM;
- goto alloc_err;
- }
- e = (__le16 *) il->eeprom;
-
- il->ops->apm_init(il);
-
- ret = il_eeprom_verify_signature(il);
- if (ret < 0) {
- IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp);
- ret = -ENOENT;
- goto err;
- }
-
- /* Make sure driver (instead of uCode) is allowed to read EEPROM */
- ret = il->ops->eeprom_acquire_semaphore(il);
- if (ret < 0) {
- IL_ERR("Failed to acquire EEPROM semaphore.\n");
- ret = -ENOENT;
- goto err;
- }
-
- /* eeprom is an array of 16bit values */
- for (addr = 0; addr < sz; addr += sizeof(u16)) {
- u32 r;
-
- _il_wr(il, CSR_EEPROM_REG,
- CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
-
- ret =
- _il_poll_bit(il, CSR_EEPROM_REG,
- CSR_EEPROM_REG_READ_VALID_MSK,
- CSR_EEPROM_REG_READ_VALID_MSK,
- IL_EEPROM_ACCESS_TIMEOUT);
- if (ret < 0) {
- IL_ERR("Time out reading EEPROM[%d]\n", addr);
- goto done;
- }
- r = _il_rd(il, CSR_EEPROM_REG);
- e[addr / 2] = cpu_to_le16(r >> 16);
- }
-
- D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM",
- il_eeprom_query16(il, EEPROM_VERSION));
-
- ret = 0;
-done:
- il->ops->eeprom_release_semaphore(il);
-
-err:
- if (ret)
- il_eeprom_free(il);
- /* Reset chip to save power until we load uCode during "up". */
- il_apm_stop(il);
-alloc_err:
- return ret;
-}
-EXPORT_SYMBOL(il_eeprom_init);
-
-void
-il_eeprom_free(struct il_priv *il)
-{
- kfree(il->eeprom);
- il->eeprom = NULL;
-}
-EXPORT_SYMBOL(il_eeprom_free);
-
-static void
-il_init_band_reference(const struct il_priv *il, int eep_band,
- int *eeprom_ch_count,
- const struct il_eeprom_channel **eeprom_ch_info,
- const u8 **eeprom_ch_idx)
-{
- u32 offset = il->cfg->regulatory_bands[eep_band - 1];
-
- switch (eep_band) {
- case 1: /* 2.4GHz band */
- *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1);
- *eeprom_ch_info =
- (struct il_eeprom_channel *)il_eeprom_query_addr(il,
- offset);
- *eeprom_ch_idx = il_eeprom_band_1;
- break;
- case 2: /* 4.9GHz band */
- *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2);
- *eeprom_ch_info =
- (struct il_eeprom_channel *)il_eeprom_query_addr(il,
- offset);
- *eeprom_ch_idx = il_eeprom_band_2;
- break;
- case 3: /* 5.2GHz band */
- *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3);
- *eeprom_ch_info =
- (struct il_eeprom_channel *)il_eeprom_query_addr(il,
- offset);
- *eeprom_ch_idx = il_eeprom_band_3;
- break;
- case 4: /* 5.5GHz band */
- *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4);
- *eeprom_ch_info =
- (struct il_eeprom_channel *)il_eeprom_query_addr(il,
- offset);
- *eeprom_ch_idx = il_eeprom_band_4;
- break;
- case 5: /* 5.7GHz band */
- *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5);
- *eeprom_ch_info =
- (struct il_eeprom_channel *)il_eeprom_query_addr(il,
- offset);
- *eeprom_ch_idx = il_eeprom_band_5;
- break;
- case 6: /* 2.4GHz ht40 channels */
- *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6);
- *eeprom_ch_info =
- (struct il_eeprom_channel *)il_eeprom_query_addr(il,
- offset);
- *eeprom_ch_idx = il_eeprom_band_6;
- break;
- case 7: /* 5 GHz ht40 channels */
- *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7);
- *eeprom_ch_info =
- (struct il_eeprom_channel *)il_eeprom_query_addr(il,
- offset);
- *eeprom_ch_idx = il_eeprom_band_7;
- break;
- default:
- BUG();
- }
-}
-
-#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \
- ? # x " " : "")
-/**
- * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il.
- *
- * Does not set up a command, or touch hardware.
- */
-static int
-il_mod_ht40_chan_info(struct il_priv *il, enum ieee80211_band band, u16 channel,
- const struct il_eeprom_channel *eeprom_ch,
- u8 clear_ht40_extension_channel)
-{
- struct il_channel_info *ch_info;
-
- ch_info =
- (struct il_channel_info *)il_get_channel_info(il, band, channel);
-
- if (!il_is_channel_valid(ch_info))
- return -1;
-
- D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):"
- " Ad-Hoc %ssupported\n", ch_info->channel,
- il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
- CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE),
- CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE),
- CHECK_AND_PRINT(DFS), eeprom_ch->flags,
- eeprom_ch->max_power_avg,
- ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) &&
- !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not ");
-
- ch_info->ht40_eeprom = *eeprom_ch;
- ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg;
- ch_info->ht40_flags = eeprom_ch->flags;
- if (eeprom_ch->flags & EEPROM_CHANNEL_VALID)
- ch_info->ht40_extension_channel &=
- ~clear_ht40_extension_channel;
-
- return 0;
-}
-
-#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \
- ? # x " " : "")
-
-/**
- * il_init_channel_map - Set up driver's info for all possible channels
- */
-int
-il_init_channel_map(struct il_priv *il)
-{
- int eeprom_ch_count = 0;
- const u8 *eeprom_ch_idx = NULL;
- const struct il_eeprom_channel *eeprom_ch_info = NULL;
- int band, ch;
- struct il_channel_info *ch_info;
-
- if (il->channel_count) {
- D_EEPROM("Channel map already initialized.\n");
- return 0;
- }
-
- D_EEPROM("Initializing regulatory info from EEPROM\n");
-
- il->channel_count =
- ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) +
- ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) +
- ARRAY_SIZE(il_eeprom_band_5);
-
- D_EEPROM("Parsing data for %d channels.\n", il->channel_count);
-
- il->channel_info =
- kzalloc(sizeof(struct il_channel_info) * il->channel_count,
- GFP_KERNEL);
- if (!il->channel_info) {
- IL_ERR("Could not allocate channel_info\n");
- il->channel_count = 0;
- return -ENOMEM;
- }
-
- ch_info = il->channel_info;
-
- /* Loop through the 5 EEPROM bands adding them in order to the
- * channel map we maintain (that contains additional information than
- * what just in the EEPROM) */
- for (band = 1; band <= 5; band++) {
-
- il_init_band_reference(il, band, &eeprom_ch_count,
- &eeprom_ch_info, &eeprom_ch_idx);
-
- /* Loop through each band adding each of the channels */
- for (ch = 0; ch < eeprom_ch_count; ch++) {
- ch_info->channel = eeprom_ch_idx[ch];
- ch_info->band =
- (band ==
- 1) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
-
- /* permanently store EEPROM's channel regulatory flags
- * and max power in channel info database. */
- ch_info->eeprom = eeprom_ch_info[ch];
-
- /* Copy the run-time flags so they are there even on
- * invalid channels */
- ch_info->flags = eeprom_ch_info[ch].flags;
- /* First write that ht40 is not enabled, and then enable
- * one by one */
- ch_info->ht40_extension_channel =
- IEEE80211_CHAN_NO_HT40;
-
- if (!(il_is_channel_valid(ch_info))) {
- D_EEPROM("Ch. %d Flags %x [%sGHz] - "
- "No traffic\n", ch_info->channel,
- ch_info->flags,
- il_is_channel_a_band(ch_info) ? "5.2" :
- "2.4");
- ch_info++;
- continue;
- }
-
- /* Initialize regulatory-based run-time data */
- ch_info->max_power_avg = ch_info->curr_txpow =
- eeprom_ch_info[ch].max_power_avg;
- ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
- ch_info->min_power = 0;
-
- D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):"
- " Ad-Hoc %ssupported\n", ch_info->channel,
- il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
- CHECK_AND_PRINT_I(VALID),
- CHECK_AND_PRINT_I(IBSS),
- CHECK_AND_PRINT_I(ACTIVE),
- CHECK_AND_PRINT_I(RADAR),
- CHECK_AND_PRINT_I(WIDE),
- CHECK_AND_PRINT_I(DFS),
- eeprom_ch_info[ch].flags,
- eeprom_ch_info[ch].max_power_avg,
- ((eeprom_ch_info[ch].
- flags & EEPROM_CHANNEL_IBSS) &&
- !(eeprom_ch_info[ch].
- flags & EEPROM_CHANNEL_RADAR)) ? "" :
- "not ");
-
- ch_info++;
- }
- }
-
- /* Check if we do have HT40 channels */
- if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 &&
- il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40)
- return 0;
-
- /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */
- for (band = 6; band <= 7; band++) {
- enum ieee80211_band ieeeband;
-
- il_init_band_reference(il, band, &eeprom_ch_count,
- &eeprom_ch_info, &eeprom_ch_idx);
-
- /* EEPROM band 6 is 2.4, band 7 is 5 GHz */
- ieeeband =
- (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
-
- /* Loop through each band adding each of the channels */
- for (ch = 0; ch < eeprom_ch_count; ch++) {
- /* Set up driver's info for lower half */
- il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch],
- &eeprom_ch_info[ch],
- IEEE80211_CHAN_NO_HT40PLUS);
-
- /* Set up driver's info for upper half */
- il_mod_ht40_chan_info(il, ieeeband,
- eeprom_ch_idx[ch] + 4,
- &eeprom_ch_info[ch],
- IEEE80211_CHAN_NO_HT40MINUS);
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL(il_init_channel_map);
-
-/*
- * il_free_channel_map - undo allocations in il_init_channel_map
- */
-void
-il_free_channel_map(struct il_priv *il)
-{
- kfree(il->channel_info);
- il->channel_count = 0;
-}
-EXPORT_SYMBOL(il_free_channel_map);
-
-/**
- * il_get_channel_info - Find driver's ilate channel info
- *
- * Based on band and channel number.
- */
-const struct il_channel_info *
-il_get_channel_info(const struct il_priv *il, enum ieee80211_band band,
- u16 channel)
-{
- int i;
-
- switch (band) {
- case IEEE80211_BAND_5GHZ:
- for (i = 14; i < il->channel_count; i++) {
- if (il->channel_info[i].channel == channel)
- return &il->channel_info[i];
- }
- break;
- case IEEE80211_BAND_2GHZ:
- if (channel >= 1 && channel <= 14)
- return &il->channel_info[channel - 1];
- break;
- default:
- BUG();
- }
-
- return NULL;
-}
-EXPORT_SYMBOL(il_get_channel_info);
-
-/*
- * Setting power level allows the card to go to sleep when not busy.
- *
- * We calculate a sleep command based on the required latency, which
- * we get from mac80211.
- */
-
-#define SLP_VEC(X0, X1, X2, X3, X4) { \
- cpu_to_le32(X0), \
- cpu_to_le32(X1), \
- cpu_to_le32(X2), \
- cpu_to_le32(X3), \
- cpu_to_le32(X4) \
-}
-
-static void
-il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd)
-{
- const __le32 interval[3][IL_POWER_VEC_SIZE] = {
- SLP_VEC(2, 2, 4, 6, 0xFF),
- SLP_VEC(2, 4, 7, 10, 10),
- SLP_VEC(4, 7, 10, 10, 0xFF)
- };
- int i, dtim_period, no_dtim;
- u32 max_sleep;
- bool skip;
-
- memset(cmd, 0, sizeof(*cmd));
-
- if (il->power_data.pci_pm)
- cmd->flags |= IL_POWER_PCI_PM_MSK;
-
- /* if no Power Save, we are done */
- if (il->power_data.ps_disabled)
- return;
-
- cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK;
- cmd->keep_alive_seconds = 0;
- cmd->debug_flags = 0;
- cmd->rx_data_timeout = cpu_to_le32(25 * 1024);
- cmd->tx_data_timeout = cpu_to_le32(25 * 1024);
- cmd->keep_alive_beacons = 0;
-
- dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0;
-
- if (dtim_period <= 2) {
- memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0]));
- no_dtim = 2;
- } else if (dtim_period <= 10) {
- memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1]));
- no_dtim = 2;
- } else {
- memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2]));
- no_dtim = 0;
- }
-
- if (dtim_period == 0) {
- dtim_period = 1;
- skip = false;
- } else {
- skip = !!no_dtim;
- }
-
- if (skip) {
- __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1];
-
- max_sleep = le32_to_cpu(tmp);
- if (max_sleep == 0xFF)
- max_sleep = dtim_period * (skip + 1);
- else if (max_sleep > dtim_period)
- max_sleep = (max_sleep / dtim_period) * dtim_period;
- cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK;
- } else {
- max_sleep = dtim_period;
- cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK;
- }
-
- for (i = 0; i < IL_POWER_VEC_SIZE; i++)
- if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
- cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
-}
-
-static int
-il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd)
-{
- D_POWER("Sending power/sleep command\n");
- D_POWER("Flags value = 0x%08X\n", cmd->flags);
- D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
- D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
- D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n",
- le32_to_cpu(cmd->sleep_interval[0]),
- le32_to_cpu(cmd->sleep_interval[1]),
- le32_to_cpu(cmd->sleep_interval[2]),
- le32_to_cpu(cmd->sleep_interval[3]),
- le32_to_cpu(cmd->sleep_interval[4]));
-
- return il_send_cmd_pdu(il, C_POWER_TBL,
- sizeof(struct il_powertable_cmd), cmd);
-}
-
-static int
-il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force)
-{
- int ret;
- bool update_chains;
-
- lockdep_assert_held(&il->mutex);
-
- /* Don't update the RX chain when chain noise calibration is running */
- update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE ||
- il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE;
-
- if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
- return 0;
-
- if (!il_is_ready_rf(il))
- return -EIO;
-
- /* scan complete use sleep_power_next, need to be updated */
- memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
- if (test_bit(S_SCANNING, &il->status) && !force) {
- D_INFO("Defer power set mode while scanning\n");
- return 0;
- }
-
- if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)
- set_bit(S_POWER_PMI, &il->status);
-
- ret = il_set_power(il, cmd);
- if (!ret) {
- if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK))
- clear_bit(S_POWER_PMI, &il->status);
-
- if (il->ops->update_chain_flags && update_chains)
- il->ops->update_chain_flags(il);
- else if (il->ops->update_chain_flags)
- D_POWER("Cannot update the power, chain noise "
- "calibration running: %d\n",
- il->chain_noise_data.state);
-
- memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd));
- } else
- IL_ERR("set power fail, ret = %d", ret);
-
- return ret;
-}
-
-int
-il_power_update_mode(struct il_priv *il, bool force)
-{
- struct il_powertable_cmd cmd;
-
- il_build_powertable_cmd(il, &cmd);
-
- return il_power_set_mode(il, &cmd, force);
-}
-EXPORT_SYMBOL(il_power_update_mode);
-
-/* initialize to default */
-void
-il_power_initialize(struct il_priv *il)
-{
- u16 lctl;
-
- pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
- il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
-
- il->power_data.debug_sleep_level_override = -1;
-
- memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd));
-}
-EXPORT_SYMBOL(il_power_initialize);
-
-/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
- * sending probe req. This should be set long enough to hear probe responses
- * from more than one AP. */
-#define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */
-#define IL_ACTIVE_DWELL_TIME_52 (20)
-
-#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3)
-#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2)
-
-/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
- * Must be set longer than active dwell time.
- * For the most reliable scan, set > AP beacon interval (typically 100msec). */
-#define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */
-#define IL_PASSIVE_DWELL_TIME_52 (10)
-#define IL_PASSIVE_DWELL_BASE (100)
-#define IL_CHANNEL_TUNE_TIME 5
-
-static int
-il_send_scan_abort(struct il_priv *il)
-{
- int ret;
- struct il_rx_pkt *pkt;
- struct il_host_cmd cmd = {
- .id = C_SCAN_ABORT,
- .flags = CMD_WANT_SKB,
- };
-
- /* Exit instantly with error when device is not ready
- * to receive scan abort command or it does not perform
- * hardware scan currently */
- if (!test_bit(S_READY, &il->status) ||
- !test_bit(S_GEO_CONFIGURED, &il->status) ||
- !test_bit(S_SCAN_HW, &il->status) ||
- test_bit(S_FW_ERROR, &il->status) ||
- test_bit(S_EXIT_PENDING, &il->status))
- return -EIO;
-
- ret = il_send_cmd_sync(il, &cmd);
- if (ret)
- return ret;
-
- pkt = (struct il_rx_pkt *)cmd.reply_page;
- if (pkt->u.status != CAN_ABORT_STATUS) {
- /* The scan abort will return 1 for success or
- * 2 for "failure". A failure condition can be
- * due to simply not being in an active scan which
- * can occur if we send the scan abort before we
- * the microcode has notified us that a scan is
- * completed. */
- D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status);
- ret = -EIO;
- }
-
- il_free_pages(il, cmd.reply_page);
- return ret;
-}
-
-static void
-il_complete_scan(struct il_priv *il, bool aborted)
-{
- /* check if scan was requested from mac80211 */
- if (il->scan_request) {
- D_SCAN("Complete scan in mac80211\n");
- ieee80211_scan_completed(il->hw, aborted);
- }
-
- il->scan_vif = NULL;
- il->scan_request = NULL;
-}
-
-void
-il_force_scan_end(struct il_priv *il)
-{
- lockdep_assert_held(&il->mutex);
-
- if (!test_bit(S_SCANNING, &il->status)) {
- D_SCAN("Forcing scan end while not scanning\n");
- return;
- }
-
- D_SCAN("Forcing scan end\n");
- clear_bit(S_SCANNING, &il->status);
- clear_bit(S_SCAN_HW, &il->status);
- clear_bit(S_SCAN_ABORTING, &il->status);
- il_complete_scan(il, true);
-}
-
-static void
-il_do_scan_abort(struct il_priv *il)
-{
- int ret;
-
- lockdep_assert_held(&il->mutex);
-
- if (!test_bit(S_SCANNING, &il->status)) {
- D_SCAN("Not performing scan to abort\n");
- return;
- }
-
- if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) {
- D_SCAN("Scan abort in progress\n");
- return;
- }
-
- ret = il_send_scan_abort(il);
- if (ret) {
- D_SCAN("Send scan abort failed %d\n", ret);
- il_force_scan_end(il);
- } else
- D_SCAN("Successfully send scan abort\n");
-}
-
-/**
- * il_scan_cancel - Cancel any currently executing HW scan
- */
-int
-il_scan_cancel(struct il_priv *il)
-{
- D_SCAN("Queuing abort scan\n");
- queue_work(il->workqueue, &il->abort_scan);
- return 0;
-}
-EXPORT_SYMBOL(il_scan_cancel);
-
-/**
- * il_scan_cancel_timeout - Cancel any currently executing HW scan
- * @ms: amount of time to wait (in milliseconds) for scan to abort
- *
- */
-int
-il_scan_cancel_timeout(struct il_priv *il, unsigned long ms)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(ms);
-
- lockdep_assert_held(&il->mutex);
-
- D_SCAN("Scan cancel timeout\n");
-
- il_do_scan_abort(il);
-
- while (time_before_eq(jiffies, timeout)) {
- if (!test_bit(S_SCAN_HW, &il->status))
- break;
- msleep(20);
- }
-
- return test_bit(S_SCAN_HW, &il->status);
-}
-EXPORT_SYMBOL(il_scan_cancel_timeout);
-
-/* Service response to C_SCAN (0x80) */
-static void
-il_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb)
-{
-#ifdef CONFIG_IWLEGACY_DEBUG
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_scanreq_notification *notif =
- (struct il_scanreq_notification *)pkt->u.raw;
-
- D_SCAN("Scan request status = 0x%x\n", notif->status);
-#endif
-}
-
-/* Service N_SCAN_START (0x82) */
-static void
-il_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_scanstart_notification *notif =
- (struct il_scanstart_notification *)pkt->u.raw;
- il->scan_start_tsf = le32_to_cpu(notif->tsf_low);
- D_SCAN("Scan start: " "%d [802.11%s] "
- "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel,
- notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high),
- le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer);
-}
-
-/* Service N_SCAN_RESULTS (0x83) */
-static void
-il_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb)
-{
-#ifdef CONFIG_IWLEGACY_DEBUG
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_scanresults_notification *notif =
- (struct il_scanresults_notification *)pkt->u.raw;
-
- D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d "
- "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a",
- le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low),
- le32_to_cpu(notif->stats[0]),
- le32_to_cpu(notif->tsf_low) - il->scan_start_tsf);
-#endif
-}
-
-/* Service N_SCAN_COMPLETE (0x84) */
-static void
-il_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb)
-{
-
-#ifdef CONFIG_IWLEGACY_DEBUG
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
-#endif
-
- D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
- scan_notif->scanned_channels, scan_notif->tsf_low,
- scan_notif->tsf_high, scan_notif->status);
-
- /* The HW is no longer scanning */
- clear_bit(S_SCAN_HW, &il->status);
-
- D_SCAN("Scan on %sGHz took %dms\n",
- (il->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
- jiffies_to_msecs(jiffies - il->scan_start));
-
- queue_work(il->workqueue, &il->scan_completed);
-}
-
-void
-il_setup_rx_scan_handlers(struct il_priv *il)
-{
- /* scan handlers */
- il->handlers[C_SCAN] = il_hdl_scan;
- il->handlers[N_SCAN_START] = il_hdl_scan_start;
- il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results;
- il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete;
-}
-EXPORT_SYMBOL(il_setup_rx_scan_handlers);
-
-u16
-il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band,
- u8 n_probes)
-{
- if (band == IEEE80211_BAND_5GHZ)
- return IL_ACTIVE_DWELL_TIME_52 +
- IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1);
- else
- return IL_ACTIVE_DWELL_TIME_24 +
- IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1);
-}
-EXPORT_SYMBOL(il_get_active_dwell_time);
-
-u16
-il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band,
- struct ieee80211_vif *vif)
-{
- u16 value;
-
- u16 passive =
- (band ==
- IEEE80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE +
- IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE +
- IL_PASSIVE_DWELL_TIME_52;
-
- if (il_is_any_associated(il)) {
- /*
- * If we're associated, we clamp the maximum passive
- * dwell time to be 98% of the smallest beacon interval
- * (minus 2 * channel tune time)
- */
- value = il->vif ? il->vif->bss_conf.beacon_int : 0;
- if (value > IL_PASSIVE_DWELL_BASE || !value)
- value = IL_PASSIVE_DWELL_BASE;
- value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2;
- passive = min(value, passive);
- }
-
- return passive;
-}
-EXPORT_SYMBOL(il_get_passive_dwell_time);
-
-void
-il_init_scan_params(struct il_priv *il)
-{
- u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1;
- if (!il->scan_tx_ant[IEEE80211_BAND_5GHZ])
- il->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx;
- if (!il->scan_tx_ant[IEEE80211_BAND_2GHZ])
- il->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
-}
-EXPORT_SYMBOL(il_init_scan_params);
-
-static int
-il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
-{
- int ret;
-
- lockdep_assert_held(&il->mutex);
-
- cancel_delayed_work(&il->scan_check);
-
- if (!il_is_ready_rf(il)) {
- IL_WARN("Request scan called when driver not ready.\n");
- return -EIO;
- }
-
- if (test_bit(S_SCAN_HW, &il->status)) {
- D_SCAN("Multiple concurrent scan requests in parallel.\n");
- return -EBUSY;
- }
-
- if (test_bit(S_SCAN_ABORTING, &il->status)) {
- D_SCAN("Scan request while abort pending.\n");
- return -EBUSY;
- }
-
- D_SCAN("Starting scan...\n");
-
- set_bit(S_SCANNING, &il->status);
- il->scan_start = jiffies;
-
- ret = il->ops->request_scan(il, vif);
- if (ret) {
- clear_bit(S_SCANNING, &il->status);
- return ret;
- }
-
- queue_delayed_work(il->workqueue, &il->scan_check,
- IL_SCAN_CHECK_WATCHDOG);
-
- return 0;
-}
-
-int
-il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_scan_request *hw_req)
-{
- struct cfg80211_scan_request *req = &hw_req->req;
- struct il_priv *il = hw->priv;
- int ret;
-
- if (req->n_channels == 0) {
- IL_ERR("Can not scan on no channels.\n");
- return -EINVAL;
- }
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter\n");
-
- if (test_bit(S_SCANNING, &il->status)) {
- D_SCAN("Scan already in progress.\n");
- ret = -EAGAIN;
- goto out_unlock;
- }
-
- /* mac80211 will only ask for one band at a time */
- il->scan_request = req;
- il->scan_vif = vif;
- il->scan_band = req->channels[0]->band;
-
- ret = il_scan_initiate(il, vif);
-
-out_unlock:
- D_MAC80211("leave ret %d\n", ret);
- mutex_unlock(&il->mutex);
-
- return ret;
-}
-EXPORT_SYMBOL(il_mac_hw_scan);
-
-static void
-il_bg_scan_check(struct work_struct *data)
-{
- struct il_priv *il =
- container_of(data, struct il_priv, scan_check.work);
-
- D_SCAN("Scan check work\n");
-
- /* Since we are here firmware does not finish scan and
- * most likely is in bad shape, so we don't bother to
- * send abort command, just force scan complete to mac80211 */
- mutex_lock(&il->mutex);
- il_force_scan_end(il);
- mutex_unlock(&il->mutex);
-}
-
-/**
- * il_fill_probe_req - fill in all required fields and IE for probe request
- */
-
-u16
-il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
- const u8 *ta, const u8 *ies, int ie_len, int left)
-{
- int len = 0;
- u8 *pos = NULL;
-
- /* Make sure there is enough space for the probe request,
- * two mandatory IEs and the data */
- left -= 24;
- if (left < 0)
- return 0;
-
- frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
- eth_broadcast_addr(frame->da);
- memcpy(frame->sa, ta, ETH_ALEN);
- eth_broadcast_addr(frame->bssid);
- frame->seq_ctrl = 0;
-
- len += 24;
-
- /* ...next IE... */
- pos = &frame->u.probe_req.variable[0];
-
- /* fill in our indirect SSID IE */
- left -= 2;
- if (left < 0)
- return 0;
- *pos++ = WLAN_EID_SSID;
- *pos++ = 0;
-
- len += 2;
-
- if (WARN_ON(left < ie_len))
- return len;
-
- if (ies && ie_len) {
- memcpy(pos, ies, ie_len);
- len += ie_len;
- }
-
- return (u16) len;
-}
-EXPORT_SYMBOL(il_fill_probe_req);
-
-static void
-il_bg_abort_scan(struct work_struct *work)
-{
- struct il_priv *il = container_of(work, struct il_priv, abort_scan);
-
- D_SCAN("Abort scan work\n");
-
- /* We keep scan_check work queued in case when firmware will not
- * report back scan completed notification */
- mutex_lock(&il->mutex);
- il_scan_cancel_timeout(il, 200);
- mutex_unlock(&il->mutex);
-}
-
-static void
-il_bg_scan_completed(struct work_struct *work)
-{
- struct il_priv *il = container_of(work, struct il_priv, scan_completed);
- bool aborted;
-
- D_SCAN("Completed scan.\n");
-
- cancel_delayed_work(&il->scan_check);
-
- mutex_lock(&il->mutex);
-
- aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status);
- if (aborted)
- D_SCAN("Aborted scan completed.\n");
-
- if (!test_and_clear_bit(S_SCANNING, &il->status)) {
- D_SCAN("Scan already completed.\n");
- goto out_settings;
- }
-
- il_complete_scan(il, aborted);
-
-out_settings:
- /* Can we still talk to firmware ? */
- if (!il_is_ready_rf(il))
- goto out;
-
- /*
- * We do not commit power settings while scan is pending,
- * do it now if the settings changed.
- */
- il_power_set_mode(il, &il->power_data.sleep_cmd_next, false);
- il_set_tx_power(il, il->tx_power_next, false);
-
- il->ops->post_scan(il);
-
-out:
- mutex_unlock(&il->mutex);
-}
-
-void
-il_setup_scan_deferred_work(struct il_priv *il)
-{
- INIT_WORK(&il->scan_completed, il_bg_scan_completed);
- INIT_WORK(&il->abort_scan, il_bg_abort_scan);
- INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check);
-}
-EXPORT_SYMBOL(il_setup_scan_deferred_work);
-
-void
-il_cancel_scan_deferred_work(struct il_priv *il)
-{
- cancel_work_sync(&il->abort_scan);
- cancel_work_sync(&il->scan_completed);
-
- if (cancel_delayed_work_sync(&il->scan_check)) {
- mutex_lock(&il->mutex);
- il_force_scan_end(il);
- mutex_unlock(&il->mutex);
- }
-}
-EXPORT_SYMBOL(il_cancel_scan_deferred_work);
-
-/* il->sta_lock must be held */
-static void
-il_sta_ucode_activate(struct il_priv *il, u8 sta_id)
-{
-
- if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE))
- IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n",
- sta_id, il->stations[sta_id].sta.sta.addr);
-
- if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) {
- D_ASSOC("STA id %u addr %pM already present"
- " in uCode (according to driver)\n", sta_id,
- il->stations[sta_id].sta.sta.addr);
- } else {
- il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE;
- D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id,
- il->stations[sta_id].sta.sta.addr);
- }
-}
-
-static int
-il_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta,
- struct il_rx_pkt *pkt, bool sync)
-{
- u8 sta_id = addsta->sta.sta_id;
- unsigned long flags;
- int ret = -EIO;
-
- if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
- IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags);
- return ret;
- }
-
- D_INFO("Processing response for adding station %u\n", sta_id);
-
- spin_lock_irqsave(&il->sta_lock, flags);
-
- switch (pkt->u.add_sta.status) {
- case ADD_STA_SUCCESS_MSK:
- D_INFO("C_ADD_STA PASSED\n");
- il_sta_ucode_activate(il, sta_id);
- ret = 0;
- break;
- case ADD_STA_NO_ROOM_IN_TBL:
- IL_ERR("Adding station %d failed, no room in table.\n", sta_id);
- break;
- case ADD_STA_NO_BLOCK_ACK_RESOURCE:
- IL_ERR("Adding station %d failed, no block ack resource.\n",
- sta_id);
- break;
- case ADD_STA_MODIFY_NON_EXIST_STA:
- IL_ERR("Attempting to modify non-existing station %d\n",
- sta_id);
- break;
- default:
- D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status);
- break;
- }
-
- D_INFO("%s station id %u addr %pM\n",
- il->stations[sta_id].sta.mode ==
- STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id,
- il->stations[sta_id].sta.sta.addr);
-
- /*
- * XXX: The MAC address in the command buffer is often changed from
- * the original sent to the device. That is, the MAC address
- * written to the command buffer often is not the same MAC address
- * read from the command buffer when the command returns. This
- * issue has not yet been resolved and this debugging is left to
- * observe the problem.
- */
- D_INFO("%s station according to cmd buffer %pM\n",
- il->stations[sta_id].sta.mode ==
- STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr);
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return ret;
-}
-
-static void
-il_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd,
- struct il_rx_pkt *pkt)
-{
- struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload;
-
- il_process_add_sta_resp(il, addsta, pkt, false);
-
-}
-
-int
-il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags)
-{
- struct il_rx_pkt *pkt = NULL;
- int ret = 0;
- u8 data[sizeof(*sta)];
- struct il_host_cmd cmd = {
- .id = C_ADD_STA,
- .flags = flags,
- .data = data,
- };
- u8 sta_id __maybe_unused = sta->sta.sta_id;
-
- D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr,
- flags & CMD_ASYNC ? "a" : "");
-
- if (flags & CMD_ASYNC)
- cmd.callback = il_add_sta_callback;
- else {
- cmd.flags |= CMD_WANT_SKB;
- might_sleep();
- }
-
- cmd.len = il->ops->build_addsta_hcmd(sta, data);
- ret = il_send_cmd(il, &cmd);
-
- if (ret || (flags & CMD_ASYNC))
- return ret;
-
- if (ret == 0) {
- pkt = (struct il_rx_pkt *)cmd.reply_page;
- ret = il_process_add_sta_resp(il, sta, pkt, true);
- }
- il_free_pages(il, cmd.reply_page);
-
- return ret;
-}
-EXPORT_SYMBOL(il_send_add_sta);
-
-static void
-il_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta)
-{
- struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap;
- __le32 sta_flags;
-
- if (!sta || !sta_ht_inf->ht_supported)
- goto done;
-
- D_ASSOC("spatial multiplexing power save mode: %s\n",
- (sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" :
- (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" :
- "disabled");
-
- sta_flags = il->stations[idx].sta.station_flags;
-
- sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
-
- switch (sta->smps_mode) {
- case IEEE80211_SMPS_STATIC:
- sta_flags |= STA_FLG_MIMO_DIS_MSK;
- break;
- case IEEE80211_SMPS_DYNAMIC:
- sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
- break;
- case IEEE80211_SMPS_OFF:
- break;
- default:
- IL_WARN("Invalid MIMO PS mode %d\n", sta->smps_mode);
- break;
- }
-
- sta_flags |=
- cpu_to_le32((u32) sta_ht_inf->
- ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
-
- sta_flags |=
- cpu_to_le32((u32) sta_ht_inf->
- ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
-
- if (il_is_ht40_tx_allowed(il, &sta->ht_cap))
- sta_flags |= STA_FLG_HT40_EN_MSK;
- else
- sta_flags &= ~STA_FLG_HT40_EN_MSK;
-
- il->stations[idx].sta.station_flags = sta_flags;
-done:
- return;
-}
-
-/**
- * il_prep_station - Prepare station information for addition
- *
- * should be called with sta_lock held
- */
-u8
-il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap,
- struct ieee80211_sta *sta)
-{
- struct il_station_entry *station;
- int i;
- u8 sta_id = IL_INVALID_STATION;
- u16 rate;
-
- if (is_ap)
- sta_id = IL_AP_ID;
- else if (is_broadcast_ether_addr(addr))
- sta_id = il->hw_params.bcast_id;
- else
- for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) {
- if (ether_addr_equal(il->stations[i].sta.sta.addr,
- addr)) {
- sta_id = i;
- break;
- }
-
- if (!il->stations[i].used &&
- sta_id == IL_INVALID_STATION)
- sta_id = i;
- }
-
- /*
- * These two conditions have the same outcome, but keep them
- * separate
- */
- if (unlikely(sta_id == IL_INVALID_STATION))
- return sta_id;
-
- /*
- * uCode is not able to deal with multiple requests to add a
- * station. Keep track if one is in progress so that we do not send
- * another.
- */
- if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
- D_INFO("STA %d already in process of being added.\n", sta_id);
- return sta_id;
- }
-
- if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
- (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) &&
- ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) {
- D_ASSOC("STA %d (%pM) already added, not adding again.\n",
- sta_id, addr);
- return sta_id;
- }
-
- station = &il->stations[sta_id];
- station->used = IL_STA_DRIVER_ACTIVE;
- D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr);
- il->num_stations++;
-
- /* Set up the C_ADD_STA command to send to device */
- memset(&station->sta, 0, sizeof(struct il_addsta_cmd));
- memcpy(station->sta.sta.addr, addr, ETH_ALEN);
- station->sta.mode = 0;
- station->sta.sta.sta_id = sta_id;
- station->sta.station_flags = 0;
-
- /*
- * OK to call unconditionally, since local stations (IBSS BSSID
- * STA and broadcast STA) pass in a NULL sta, and mac80211
- * doesn't allow HT IBSS.
- */
- il_set_ht_add_station(il, sta_id, sta);
-
- /* 3945 only */
- rate = (il->band == IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP;
- /* Turn on both antennas for the station... */
- station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
-
- return sta_id;
-
-}
-EXPORT_SYMBOL_GPL(il_prep_station);
-
-#define STA_WAIT_TIMEOUT (HZ/2)
-
-/**
- * il_add_station_common -
- */
-int
-il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap,
- struct ieee80211_sta *sta, u8 *sta_id_r)
-{
- unsigned long flags_spin;
- int ret = 0;
- u8 sta_id;
- struct il_addsta_cmd sta_cmd;
-
- *sta_id_r = 0;
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- sta_id = il_prep_station(il, addr, is_ap, sta);
- if (sta_id == IL_INVALID_STATION) {
- IL_ERR("Unable to prepare station %pM for addition\n", addr);
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- return -EINVAL;
- }
-
- /*
- * uCode is not able to deal with multiple requests to add a
- * station. Keep track if one is in progress so that we do not send
- * another.
- */
- if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
- D_INFO("STA %d already in process of being added.\n", sta_id);
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- return -EEXIST;
- }
-
- if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
- (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
- D_ASSOC("STA %d (%pM) already added, not adding again.\n",
- sta_id, addr);
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- return -EEXIST;
- }
-
- il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS;
- memcpy(&sta_cmd, &il->stations[sta_id].sta,
- sizeof(struct il_addsta_cmd));
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
-
- /* Add station to device's station table */
- ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
- if (ret) {
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- IL_ERR("Adding station %pM failed.\n",
- il->stations[sta_id].sta.sta.addr);
- il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
- il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- }
- *sta_id_r = sta_id;
- return ret;
-}
-EXPORT_SYMBOL(il_add_station_common);
-
-/**
- * il_sta_ucode_deactivate - deactivate ucode status for a station
- *
- * il->sta_lock must be held
- */
-static void
-il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id)
-{
- /* Ucode must be active and driver must be non active */
- if ((il->stations[sta_id].
- used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) !=
- IL_STA_UCODE_ACTIVE)
- IL_ERR("removed non active STA %u\n", sta_id);
-
- il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE;
-
- memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry));
- D_ASSOC("Removed STA %u\n", sta_id);
-}
-
-static int
-il_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id,
- bool temporary)
-{
- struct il_rx_pkt *pkt;
- int ret;
-
- unsigned long flags_spin;
- struct il_rem_sta_cmd rm_sta_cmd;
-
- struct il_host_cmd cmd = {
- .id = C_REM_STA,
- .len = sizeof(struct il_rem_sta_cmd),
- .flags = CMD_SYNC,
- .data = &rm_sta_cmd,
- };
-
- memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
- rm_sta_cmd.num_sta = 1;
- memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
-
- cmd.flags |= CMD_WANT_SKB;
-
- ret = il_send_cmd(il, &cmd);
-
- if (ret)
- return ret;
-
- pkt = (struct il_rx_pkt *)cmd.reply_page;
- if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
- IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags);
- ret = -EIO;
- }
-
- if (!ret) {
- switch (pkt->u.rem_sta.status) {
- case REM_STA_SUCCESS_MSK:
- if (!temporary) {
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- il_sta_ucode_deactivate(il, sta_id);
- spin_unlock_irqrestore(&il->sta_lock,
- flags_spin);
- }
- D_ASSOC("C_REM_STA PASSED\n");
- break;
- default:
- ret = -EIO;
- IL_ERR("C_REM_STA failed\n");
- break;
- }
- }
- il_free_pages(il, cmd.reply_page);
-
- return ret;
-}
-
-/**
- * il_remove_station - Remove driver's knowledge of station.
- */
-int
-il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr)
-{
- unsigned long flags;
-
- if (!il_is_ready(il)) {
- D_INFO("Unable to remove station %pM, device not ready.\n",
- addr);
- /*
- * It is typical for stations to be removed when we are
- * going down. Return success since device will be down
- * soon anyway
- */
- return 0;
- }
-
- D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr);
-
- if (WARN_ON(sta_id == IL_INVALID_STATION))
- return -EINVAL;
-
- spin_lock_irqsave(&il->sta_lock, flags);
-
- if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) {
- D_INFO("Removing %pM but non DRIVER active\n", addr);
- goto out_err;
- }
-
- if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
- D_INFO("Removing %pM but non UCODE active\n", addr);
- goto out_err;
- }
-
- if (il->stations[sta_id].used & IL_STA_LOCAL) {
- kfree(il->stations[sta_id].lq);
- il->stations[sta_id].lq = NULL;
- }
-
- il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
-
- il->num_stations--;
-
- BUG_ON(il->num_stations < 0);
-
- spin_unlock_irqrestore(&il->sta_lock, flags);
-
- return il_send_remove_station(il, addr, sta_id, false);
-out_err:
- spin_unlock_irqrestore(&il->sta_lock, flags);
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(il_remove_station);
-
-/**
- * il_clear_ucode_stations - clear ucode station table bits
- *
- * This function clears all the bits in the driver indicating
- * which stations are active in the ucode. Call when something
- * other than explicit station management would cause this in
- * the ucode, e.g. unassociated RXON.
- */
-void
-il_clear_ucode_stations(struct il_priv *il)
-{
- int i;
- unsigned long flags_spin;
- bool cleared = false;
-
- D_INFO("Clearing ucode stations in driver\n");
-
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- for (i = 0; i < il->hw_params.max_stations; i++) {
- if (il->stations[i].used & IL_STA_UCODE_ACTIVE) {
- D_INFO("Clearing ucode active for station %d\n", i);
- il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
- cleared = true;
- }
- }
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
-
- if (!cleared)
- D_INFO("No active stations found to be cleared\n");
-}
-EXPORT_SYMBOL(il_clear_ucode_stations);
-
-/**
- * il_restore_stations() - Restore driver known stations to device
- *
- * All stations considered active by driver, but not present in ucode, is
- * restored.
- *
- * Function sleeps.
- */
-void
-il_restore_stations(struct il_priv *il)
-{
- struct il_addsta_cmd sta_cmd;
- struct il_link_quality_cmd lq;
- unsigned long flags_spin;
- int i;
- bool found = false;
- int ret;
- bool send_lq;
-
- if (!il_is_ready(il)) {
- D_INFO("Not ready yet, not restoring any stations.\n");
- return;
- }
-
- D_ASSOC("Restoring all known stations ... start.\n");
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- for (i = 0; i < il->hw_params.max_stations; i++) {
- if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) &&
- !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) {
- D_ASSOC("Restoring sta %pM\n",
- il->stations[i].sta.sta.addr);
- il->stations[i].sta.mode = 0;
- il->stations[i].used |= IL_STA_UCODE_INPROGRESS;
- found = true;
- }
- }
-
- for (i = 0; i < il->hw_params.max_stations; i++) {
- if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) {
- memcpy(&sta_cmd, &il->stations[i].sta,
- sizeof(struct il_addsta_cmd));
- send_lq = false;
- if (il->stations[i].lq) {
- memcpy(&lq, il->stations[i].lq,
- sizeof(struct il_link_quality_cmd));
- send_lq = true;
- }
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
- if (ret) {
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- IL_ERR("Adding station %pM failed.\n",
- il->stations[i].sta.sta.addr);
- il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE;
- il->stations[i].used &=
- ~IL_STA_UCODE_INPROGRESS;
- spin_unlock_irqrestore(&il->sta_lock,
- flags_spin);
- }
- /*
- * Rate scaling has already been initialized, send
- * current LQ command
- */
- if (send_lq)
- il_send_lq_cmd(il, &lq, CMD_SYNC, true);
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS;
- }
- }
-
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- if (!found)
- D_INFO("Restoring all known stations"
- " .... no stations to be restored.\n");
- else
- D_INFO("Restoring all known stations" " .... complete.\n");
-}
-EXPORT_SYMBOL(il_restore_stations);
-
-int
-il_get_free_ucode_key_idx(struct il_priv *il)
-{
- int i;
-
- for (i = 0; i < il->sta_key_max_num; i++)
- if (!test_and_set_bit(i, &il->ucode_key_table))
- return i;
-
- return WEP_INVALID_OFFSET;
-}
-EXPORT_SYMBOL(il_get_free_ucode_key_idx);
-
-void
-il_dealloc_bcast_stations(struct il_priv *il)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&il->sta_lock, flags);
- for (i = 0; i < il->hw_params.max_stations; i++) {
- if (!(il->stations[i].used & IL_STA_BCAST))
- continue;
-
- il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
- il->num_stations--;
- BUG_ON(il->num_stations < 0);
- kfree(il->stations[i].lq);
- il->stations[i].lq = NULL;
- }
- spin_unlock_irqrestore(&il->sta_lock, flags);
-}
-EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations);
-
-#ifdef CONFIG_IWLEGACY_DEBUG
-static void
-il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
-{
- int i;
- D_RATE("lq station id 0x%x\n", lq->sta_id);
- D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk,
- lq->general_params.dual_stream_ant_msk);
-
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
- D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags);
-}
-#else
-static inline void
-il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
-{
-}
-#endif
-
-/**
- * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity
- *
- * It sometimes happens when a HT rate has been in use and we
- * loose connectivity with AP then mac80211 will first tell us that the
- * current channel is not HT anymore before removing the station. In such a
- * scenario the RXON flags will be updated to indicate we are not
- * communicating HT anymore, but the LQ command may still contain HT rates.
- * Test for this to prevent driver from sending LQ command between the time
- * RXON flags are updated and when LQ command is updated.
- */
-static bool
-il_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq)
-{
- int i;
-
- if (il->ht.enabled)
- return true;
-
- D_INFO("Channel %u is not an HT channel\n", il->active.channel);
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
- if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) {
- D_INFO("idx %d of LQ expects HT channel\n", i);
- return false;
- }
- }
- return true;
-}
-
-/**
- * il_send_lq_cmd() - Send link quality command
- * @init: This command is sent as part of station initialization right
- * after station has been added.
- *
- * The link quality command is sent as the last step of station creation.
- * This is the special case in which init is set and we call a callback in
- * this case to clear the state indicating that station creation is in
- * progress.
- */
-int
-il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq,
- u8 flags, bool init)
-{
- int ret = 0;
- unsigned long flags_spin;
-
- struct il_host_cmd cmd = {
- .id = C_TX_LINK_QUALITY_CMD,
- .len = sizeof(struct il_link_quality_cmd),
- .flags = flags,
- .data = lq,
- };
-
- if (WARN_ON(lq->sta_id == IL_INVALID_STATION))
- return -EINVAL;
-
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) {
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
-
- il_dump_lq_cmd(il, lq);
- BUG_ON(init && (cmd.flags & CMD_ASYNC));
-
- if (il_is_lq_table_valid(il, lq))
- ret = il_send_cmd(il, &cmd);
- else
- ret = -EINVAL;
-
- if (cmd.flags & CMD_ASYNC)
- return ret;
-
- if (init) {
- D_INFO("init LQ command complete,"
- " clearing sta addition status for sta %d\n",
- lq->sta_id);
- spin_lock_irqsave(&il->sta_lock, flags_spin);
- il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
- spin_unlock_irqrestore(&il->sta_lock, flags_spin);
- }
- return ret;
-}
-EXPORT_SYMBOL(il_send_lq_cmd);
-
-int
-il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct il_priv *il = hw->priv;
- struct il_station_priv_common *sta_common = (void *)sta->drv_priv;
- int ret;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter station %pM\n", sta->addr);
-
- ret = il_remove_station(il, sta_common->sta_id, sta->addr);
- if (ret)
- IL_ERR("Error removing station %pM\n", sta->addr);
-
- D_MAC80211("leave ret %d\n", ret);
- mutex_unlock(&il->mutex);
-
- return ret;
-}
-EXPORT_SYMBOL(il_mac_sta_remove);
-
-/************************** RX-FUNCTIONS ****************************/
-/*
- * Rx theory of operation
- *
- * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
- * each of which point to Receive Buffers to be filled by the NIC. These get
- * used not only for Rx frames, but for any command response or notification
- * from the NIC. The driver and NIC manage the Rx buffers by means
- * of idxes into the circular buffer.
- *
- * Rx Queue Indexes
- * The host/firmware share two idx registers for managing the Rx buffers.
- *
- * The READ idx maps to the first position that the firmware may be writing
- * to -- the driver can read up to (but not including) this position and get
- * good data.
- * The READ idx is managed by the firmware once the card is enabled.
- *
- * The WRITE idx maps to the last position the driver has read from -- the
- * position preceding WRITE is the last slot the firmware can place a packet.
- *
- * The queue is empty (no good data) if WRITE = READ - 1, and is full if
- * WRITE = READ.
- *
- * During initialization, the host sets up the READ queue position to the first
- * IDX position, and WRITE to the last (READ - 1 wrapped)
- *
- * When the firmware places a packet in a buffer, it will advance the READ idx
- * and fire the RX interrupt. The driver can then query the READ idx and
- * process as many packets as possible, moving the WRITE idx forward as it
- * resets the Rx queue buffers with new memory.
- *
- * The management in the driver is as follows:
- * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When
- * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
- * to replenish the iwl->rxq->rx_free.
- * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the
- * iwl->rxq is replenished and the READ IDX is updated (updating the
- * 'processed' and 'read' driver idxes as well)
- * + A received packet is processed and handed to the kernel network stack,
- * detached from the iwl->rxq. The driver 'processed' idx is updated.
- * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free
- * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ
- * IDX is not incremented and iwl->status(RX_STALLED) is set. If there
- * were enough free buffers and RX_STALLED is set it is cleared.
- *
- *
- * Driver sequence:
- *
- * il_rx_queue_alloc() Allocates rx_free
- * il_rx_replenish() Replenishes rx_free list from rx_used, and calls
- * il_rx_queue_restock
- * il_rx_queue_restock() Moves available buffers from rx_free into Rx
- * queue, updates firmware pointers, and updates
- * the WRITE idx. If insufficient rx_free buffers
- * are available, schedules il_rx_replenish
- *
- * -- enable interrupts --
- * ISR - il_rx() Detach il_rx_bufs from pool up to the
- * READ IDX, detaching the SKB from the pool.
- * Moves the packet buffer from queue to rx_used.
- * Calls il_rx_queue_restock to refill any empty
- * slots.
- * ...
- *
- */
-
-/**
- * il_rx_queue_space - Return number of free slots available in queue.
- */
-int
-il_rx_queue_space(const struct il_rx_queue *q)
-{
- int s = q->read - q->write;
- if (s <= 0)
- s += RX_QUEUE_SIZE;
- /* keep some buffer to not confuse full and empty queue */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
-}
-EXPORT_SYMBOL(il_rx_queue_space);
-
-/**
- * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue
- */
-void
-il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q)
-{
- unsigned long flags;
- u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg;
- u32 reg;
-
- spin_lock_irqsave(&q->lock, flags);
-
- if (q->need_update == 0)
- goto exit_unlock;
-
- /* If power-saving is in use, make sure device is awake */
- if (test_bit(S_POWER_PMI, &il->status)) {
- reg = _il_rd(il, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n",
- reg);
- il_set_bit(il, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- goto exit_unlock;
- }
-
- q->write_actual = (q->write & ~0x7);
- il_wr(il, rx_wrt_ptr_reg, q->write_actual);
-
- /* Else device is assumed to be awake */
- } else {
- /* Device expects a multiple of 8 */
- q->write_actual = (q->write & ~0x7);
- il_wr(il, rx_wrt_ptr_reg, q->write_actual);
- }
-
- q->need_update = 0;
-
-exit_unlock:
- spin_unlock_irqrestore(&q->lock, flags);
-}
-EXPORT_SYMBOL(il_rx_queue_update_write_ptr);
-
-int
-il_rx_queue_alloc(struct il_priv *il)
-{
- struct il_rx_queue *rxq = &il->rxq;
- struct device *dev = &il->pci_dev->dev;
- int i;
-
- spin_lock_init(&rxq->lock);
- INIT_LIST_HEAD(&rxq->rx_free);
- INIT_LIST_HEAD(&rxq->rx_used);
-
- /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */
- rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma,
- GFP_KERNEL);
- if (!rxq->bd)
- goto err_bd;
-
- rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status),
- &rxq->rb_stts_dma, GFP_KERNEL);
- if (!rxq->rb_stts)
- goto err_rb;
-
- /* Fill the rx_used queue with _all_ of the Rx buffers */
- for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
- list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
-
- /* Set us so that we have processed and used all buffers, but have
- * not restocked the Rx queue with fresh buffers */
- rxq->read = rxq->write = 0;
- rxq->write_actual = 0;
- rxq->free_count = 0;
- rxq->need_update = 0;
- return 0;
-
-err_rb:
- dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
- rxq->bd_dma);
-err_bd:
- return -ENOMEM;
-}
-EXPORT_SYMBOL(il_rx_queue_alloc);
-
-void
-il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_spectrum_notification *report = &(pkt->u.spectrum_notif);
-
- if (!report->state) {
- D_11H("Spectrum Measure Notification: Start\n");
- return;
- }
-
- memcpy(&il->measure_report, report, sizeof(*report));
- il->measurement_status |= MEASUREMENT_READY;
-}
-EXPORT_SYMBOL(il_hdl_spectrum_measurement);
-
-/*
- * returns non-zero if packet should be dropped
- */
-int
-il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
- u32 decrypt_res, struct ieee80211_rx_status *stats)
-{
- u16 fc = le16_to_cpu(hdr->frame_control);
-
- /*
- * All contexts have the same setting here due to it being
- * a module parameter, so OK to check any context.
- */
- if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK)
- return 0;
-
- if (!(fc & IEEE80211_FCTL_PROTECTED))
- return 0;
-
- D_RX("decrypt_res:0x%x\n", decrypt_res);
- switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
- case RX_RES_STATUS_SEC_TYPE_TKIP:
- /* The uCode has got a bad phase 1 Key, pushes the packet.
- * Decryption will be done in SW. */
- if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
- RX_RES_STATUS_BAD_KEY_TTAK)
- break;
-
- case RX_RES_STATUS_SEC_TYPE_WEP:
- if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
- RX_RES_STATUS_BAD_ICV_MIC) {
- /* bad ICV, the packet is destroyed since the
- * decryption is inplace, drop it */
- D_RX("Packet destroyed\n");
- return -1;
- }
- case RX_RES_STATUS_SEC_TYPE_CCMP:
- if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
- RX_RES_STATUS_DECRYPT_OK) {
- D_RX("hw decrypt successfully!!!\n");
- stats->flag |= RX_FLAG_DECRYPTED;
- }
- break;
-
- default:
- break;
- }
- return 0;
-}
-EXPORT_SYMBOL(il_set_decrypted_flag);
-
-/**
- * il_txq_update_write_ptr - Send new write idx to hardware
- */
-void
-il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq)
-{
- u32 reg = 0;
- int txq_id = txq->q.id;
-
- if (txq->need_update == 0)
- return;
-
- /* if we're trying to save power */
- if (test_bit(S_POWER_PMI, &il->status)) {
- /* wake up nic if it's powered down ...
- * uCode will wake up, and interrupt us again, so next
- * time we'll skip this part. */
- reg = _il_rd(il, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n",
- txq_id, reg);
- il_set_bit(il, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- return;
- }
-
- il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
-
- /*
- * else not in power-save mode,
- * uCode will never sleep when we're
- * trying to tx (during RFKILL, we're not trying to tx).
- */
- } else
- _il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
- txq->need_update = 0;
-}
-EXPORT_SYMBOL(il_txq_update_write_ptr);
-
-/**
- * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's
- */
-void
-il_tx_queue_unmap(struct il_priv *il, int txq_id)
-{
- struct il_tx_queue *txq = &il->txq[txq_id];
- struct il_queue *q = &txq->q;
-
- if (q->n_bd == 0)
- return;
-
- while (q->write_ptr != q->read_ptr) {
- il->ops->txq_free_tfd(il, txq);
- q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd);
- }
-}
-EXPORT_SYMBOL(il_tx_queue_unmap);
-
-/**
- * il_tx_queue_free - Deallocate DMA queue.
- * @txq: Transmit queue to deallocate.
- *
- * Empty queue by removing and destroying all BD's.
- * Free all buffers.
- * 0-fill, but do not free "txq" descriptor structure.
- */
-void
-il_tx_queue_free(struct il_priv *il, int txq_id)
-{
- struct il_tx_queue *txq = &il->txq[txq_id];
- struct device *dev = &il->pci_dev->dev;
- int i;
-
- il_tx_queue_unmap(il, txq_id);
-
- /* De-alloc array of command/tx buffers */
- for (i = 0; i < TFD_TX_CMD_SLOTS; i++)
- kfree(txq->cmd[i]);
-
- /* De-alloc circular buffer of TFDs */
- if (txq->q.n_bd)
- dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd,
- txq->tfds, txq->q.dma_addr);
-
- /* De-alloc array of per-TFD driver data */
- kfree(txq->skbs);
- txq->skbs = NULL;
-
- /* deallocate arrays */
- kfree(txq->cmd);
- kfree(txq->meta);
- txq->cmd = NULL;
- txq->meta = NULL;
-
- /* 0-fill queue descriptor structure */
- memset(txq, 0, sizeof(*txq));
-}
-EXPORT_SYMBOL(il_tx_queue_free);
-
-/**
- * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue
- */
-void
-il_cmd_queue_unmap(struct il_priv *il)
-{
- struct il_tx_queue *txq = &il->txq[il->cmd_queue];
- struct il_queue *q = &txq->q;
- int i;
-
- if (q->n_bd == 0)
- return;
-
- while (q->read_ptr != q->write_ptr) {
- i = il_get_cmd_idx(q, q->read_ptr, 0);
-
- if (txq->meta[i].flags & CMD_MAPPED) {
- pci_unmap_single(il->pci_dev,
- dma_unmap_addr(&txq->meta[i], mapping),
- dma_unmap_len(&txq->meta[i], len),
- PCI_DMA_BIDIRECTIONAL);
- txq->meta[i].flags = 0;
- }
-
- q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd);
- }
-
- i = q->n_win;
- if (txq->meta[i].flags & CMD_MAPPED) {
- pci_unmap_single(il->pci_dev,
- dma_unmap_addr(&txq->meta[i], mapping),
- dma_unmap_len(&txq->meta[i], len),
- PCI_DMA_BIDIRECTIONAL);
- txq->meta[i].flags = 0;
- }
-}
-EXPORT_SYMBOL(il_cmd_queue_unmap);
-
-/**
- * il_cmd_queue_free - Deallocate DMA queue.
- * @txq: Transmit queue to deallocate.
- *
- * Empty queue by removing and destroying all BD's.
- * Free all buffers.
- * 0-fill, but do not free "txq" descriptor structure.
- */
-void
-il_cmd_queue_free(struct il_priv *il)
-{
- struct il_tx_queue *txq = &il->txq[il->cmd_queue];
- struct device *dev = &il->pci_dev->dev;
- int i;
-
- il_cmd_queue_unmap(il);
-
- /* De-alloc array of command/tx buffers */
- for (i = 0; i <= TFD_CMD_SLOTS; i++)
- kfree(txq->cmd[i]);
-
- /* De-alloc circular buffer of TFDs */
- if (txq->q.n_bd)
- dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd,
- txq->tfds, txq->q.dma_addr);
-
- /* deallocate arrays */
- kfree(txq->cmd);
- kfree(txq->meta);
- txq->cmd = NULL;
- txq->meta = NULL;
-
- /* 0-fill queue descriptor structure */
- memset(txq, 0, sizeof(*txq));
-}
-EXPORT_SYMBOL(il_cmd_queue_free);
-
-/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
- * DMA services
- *
- * Theory of operation
- *
- * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer
- * of buffer descriptors, each of which points to one or more data buffers for
- * the device to read from or fill. Driver and device exchange status of each
- * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty
- * entries in each circular buffer, to protect against confusing empty and full
- * queue states.
- *
- * The device reads or writes the data in the queues via the device's several
- * DMA/FIFO channels. Each queue is mapped to a single DMA channel.
- *
- * For Tx queue, there are low mark and high mark limits. If, after queuing
- * the packet for Tx, free space become < low mark, Tx queue stopped. When
- * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
- * Tx queue resumed.
- *
- * See more detailed info in 4965.h.
- ***************************************************/
-
-int
-il_queue_space(const struct il_queue *q)
-{
- int s = q->read_ptr - q->write_ptr;
-
- if (q->read_ptr > q->write_ptr)
- s -= q->n_bd;
-
- if (s <= 0)
- s += q->n_win;
- /* keep some reserve to not confuse empty and full situations */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
-}
-EXPORT_SYMBOL(il_queue_space);
-
-
-/**
- * il_queue_init - Initialize queue's high/low-water and read/write idxes
- */
-static int
-il_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id)
-{
- /*
- * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
- * il_queue_inc_wrap and il_queue_dec_wrap are broken.
- */
- BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
- /* FIXME: remove q->n_bd */
- q->n_bd = TFD_QUEUE_SIZE_MAX;
-
- q->n_win = slots;
- q->id = id;
-
- /* slots_must be power-of-two size, otherwise
- * il_get_cmd_idx is broken. */
- BUG_ON(!is_power_of_2(slots));
-
- q->low_mark = q->n_win / 4;
- if (q->low_mark < 4)
- q->low_mark = 4;
-
- q->high_mark = q->n_win / 8;
- if (q->high_mark < 2)
- q->high_mark = 2;
-
- q->write_ptr = q->read_ptr = 0;
-
- return 0;
-}
-
-/**
- * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue
- */
-static int
-il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id)
-{
- struct device *dev = &il->pci_dev->dev;
- size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX;
-
- /* Driver ilate data, only for Tx (not command) queues,
- * not shared with device. */
- if (id != il->cmd_queue) {
- txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX,
- sizeof(struct sk_buff *),
- GFP_KERNEL);
- if (!txq->skbs) {
- IL_ERR("Fail to alloc skbs\n");
- goto error;
- }
- } else
- txq->skbs = NULL;
-
- /* Circular buffer of transmit frame descriptors (TFDs),
- * shared with device */
- txq->tfds =
- dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL);
- if (!txq->tfds)
- goto error;
-
- txq->q.id = id;
-
- return 0;
-
-error:
- kfree(txq->skbs);
- txq->skbs = NULL;
-
- return -ENOMEM;
-}
-
-/**
- * il_tx_queue_init - Allocate and initialize one tx/cmd queue
- */
-int
-il_tx_queue_init(struct il_priv *il, u32 txq_id)
-{
- int i, len, ret;
- int slots, actual_slots;
- struct il_tx_queue *txq = &il->txq[txq_id];
-
- /*
- * Alloc buffer array for commands (Tx or other types of commands).
- * For the command queue (#4/#9), allocate command space + one big
- * command for scan, since scan command is very huge; the system will
- * not have two scans at the same time, so only one is needed.
- * For normal Tx queues (all other queues), no super-size command
- * space is needed.
- */
- if (txq_id == il->cmd_queue) {
- slots = TFD_CMD_SLOTS;
- actual_slots = slots + 1;
- } else {
- slots = TFD_TX_CMD_SLOTS;
- actual_slots = slots;
- }
-
- txq->meta =
- kzalloc(sizeof(struct il_cmd_meta) * actual_slots, GFP_KERNEL);
- txq->cmd =
- kzalloc(sizeof(struct il_device_cmd *) * actual_slots, GFP_KERNEL);
-
- if (!txq->meta || !txq->cmd)
- goto out_free_arrays;
-
- len = sizeof(struct il_device_cmd);
- for (i = 0; i < actual_slots; i++) {
- /* only happens for cmd queue */
- if (i == slots)
- len = IL_MAX_CMD_SIZE;
-
- txq->cmd[i] = kmalloc(len, GFP_KERNEL);
- if (!txq->cmd[i])
- goto err;
- }
-
- /* Alloc driver data array and TFD circular buffer */
- ret = il_tx_queue_alloc(il, txq, txq_id);
- if (ret)
- goto err;
-
- txq->need_update = 0;
-
- /*
- * For the default queues 0-3, set up the swq_id
- * already -- all others need to get one later
- * (if they need one at all).
- */
- if (txq_id < 4)
- il_set_swq_id(txq, txq_id, txq_id);
-
- /* Initialize queue's high/low-water marks, and head/tail idxes */
- il_queue_init(il, &txq->q, slots, txq_id);
-
- /* Tell device where to find queue */
- il->ops->txq_init(il, txq);
-
- return 0;
-err:
- for (i = 0; i < actual_slots; i++)
- kfree(txq->cmd[i]);
-out_free_arrays:
- kfree(txq->meta);
- kfree(txq->cmd);
-
- return -ENOMEM;
-}
-EXPORT_SYMBOL(il_tx_queue_init);
-
-void
-il_tx_queue_reset(struct il_priv *il, u32 txq_id)
-{
- int slots, actual_slots;
- struct il_tx_queue *txq = &il->txq[txq_id];
-
- if (txq_id == il->cmd_queue) {
- slots = TFD_CMD_SLOTS;
- actual_slots = TFD_CMD_SLOTS + 1;
- } else {
- slots = TFD_TX_CMD_SLOTS;
- actual_slots = TFD_TX_CMD_SLOTS;
- }
-
- memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots);
- txq->need_update = 0;
-
- /* Initialize queue's high/low-water marks, and head/tail idxes */
- il_queue_init(il, &txq->q, slots, txq_id);
-
- /* Tell device where to find queue */
- il->ops->txq_init(il, txq);
-}
-EXPORT_SYMBOL(il_tx_queue_reset);
-
-/*************** HOST COMMAND QUEUE FUNCTIONS *****/
-
-/**
- * il_enqueue_hcmd - enqueue a uCode command
- * @il: device ilate data point
- * @cmd: a point to the ucode command structure
- *
- * The function returns < 0 values to indicate the operation is
- * failed. On success, it turns the idx (> 0) of command in the
- * command queue.
- */
-int
-il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd)
-{
- struct il_tx_queue *txq = &il->txq[il->cmd_queue];
- struct il_queue *q = &txq->q;
- struct il_device_cmd *out_cmd;
- struct il_cmd_meta *out_meta;
- dma_addr_t phys_addr;
- unsigned long flags;
- int len;
- u32 idx;
- u16 fix_size;
-
- cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len);
- fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr));
-
- /* If any of the command structures end up being larger than
- * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then
- * we will need to increase the size of the TFD entries
- * Also, check to see if command buffer should not exceed the size
- * of device_cmd and max_cmd_size. */
- BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) &&
- !(cmd->flags & CMD_SIZE_HUGE));
- BUG_ON(fix_size > IL_MAX_CMD_SIZE);
-
- if (il_is_rfkill(il) || il_is_ctkill(il)) {
- IL_WARN("Not sending command - %s KILL\n",
- il_is_rfkill(il) ? "RF" : "CT");
- return -EIO;
- }
-
- spin_lock_irqsave(&il->hcmd_lock, flags);
-
- if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
- spin_unlock_irqrestore(&il->hcmd_lock, flags);
-
- IL_ERR("Restarting adapter due to command queue full\n");
- queue_work(il->workqueue, &il->restart);
- return -ENOSPC;
- }
-
- idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE);
- out_cmd = txq->cmd[idx];
- out_meta = &txq->meta[idx];
-
- if (WARN_ON(out_meta->flags & CMD_MAPPED)) {
- spin_unlock_irqrestore(&il->hcmd_lock, flags);
- return -ENOSPC;
- }
-
- memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */
- out_meta->flags = cmd->flags | CMD_MAPPED;
- if (cmd->flags & CMD_WANT_SKB)
- out_meta->source = cmd;
- if (cmd->flags & CMD_ASYNC)
- out_meta->callback = cmd->callback;
-
- out_cmd->hdr.cmd = cmd->id;
- memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len);
-
- /* At this point, the out_cmd now has all of the incoming cmd
- * information */
-
- out_cmd->hdr.flags = 0;
- out_cmd->hdr.sequence =
- cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr));
- if (cmd->flags & CMD_SIZE_HUGE)
- out_cmd->hdr.sequence |= SEQ_HUGE_FRAME;
- len = sizeof(struct il_device_cmd);
- if (idx == TFD_CMD_SLOTS)
- len = IL_MAX_CMD_SIZE;
-
-#ifdef CONFIG_IWLEGACY_DEBUG
- switch (out_cmd->hdr.cmd) {
- case C_TX_LINK_QUALITY_CMD:
- case C_SENSITIVITY:
- D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, "
- "%d bytes at %d[%d]:%d\n",
- il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd,
- le16_to_cpu(out_cmd->hdr.sequence), fix_size,
- q->write_ptr, idx, il->cmd_queue);
- break;
- default:
- D_HC("Sending command %s (#%x), seq: 0x%04X, "
- "%d bytes at %d[%d]:%d\n",
- il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd,
- le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr,
- idx, il->cmd_queue);
- }
-#endif
-
- phys_addr =
- pci_map_single(il->pci_dev, &out_cmd->hdr, fix_size,
- PCI_DMA_BIDIRECTIONAL);
- if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) {
- idx = -ENOMEM;
- goto out;
- }
- dma_unmap_addr_set(out_meta, mapping, phys_addr);
- dma_unmap_len_set(out_meta, len, fix_size);
-
- txq->need_update = 1;
-
- if (il->ops->txq_update_byte_cnt_tbl)
- /* Set up entry in queue's byte count circular buffer */
- il->ops->txq_update_byte_cnt_tbl(il, txq, 0);
-
- il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1,
- U32_PAD(cmd->len));
-
- /* Increment and update queue's write idx */
- q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd);
- il_txq_update_write_ptr(il, txq);
-
-out:
- spin_unlock_irqrestore(&il->hcmd_lock, flags);
- return idx;
-}
-
-/**
- * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd
- *
- * When FW advances 'R' idx, all entries between old and new 'R' idx
- * need to be reclaimed. As result, some free space forms. If there is
- * enough free space (> low mark), wake the stack that feeds us.
- */
-static void
-il_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx)
-{
- struct il_tx_queue *txq = &il->txq[txq_id];
- struct il_queue *q = &txq->q;
- int nfreed = 0;
-
- if (idx >= q->n_bd || il_queue_used(q, idx) == 0) {
- IL_ERR("Read idx for DMA queue txq id (%d), idx %d, "
- "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd,
- q->write_ptr, q->read_ptr);
- return;
- }
-
- for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
- q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) {
-
- if (nfreed++ > 0) {
- IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx,
- q->write_ptr, q->read_ptr);
- queue_work(il->workqueue, &il->restart);
- }
-
- }
-}
-
-/**
- * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
- * @rxb: Rx buffer to reclaim
- *
- * If an Rx buffer has an async callback associated with it the callback
- * will be executed. The attached skb (if present) will only be freed
- * if the callback returns 1
- */
-void
-il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- int txq_id = SEQ_TO_QUEUE(sequence);
- int idx = SEQ_TO_IDX(sequence);
- int cmd_idx;
- bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME);
- struct il_device_cmd *cmd;
- struct il_cmd_meta *meta;
- struct il_tx_queue *txq = &il->txq[il->cmd_queue];
- unsigned long flags;
-
- /* If a Tx command is being handled and it isn't in the actual
- * command queue then there a command routing bug has been introduced
- * in the queue management code. */
- if (WARN
- (txq_id != il->cmd_queue,
- "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
- txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr,
- il->txq[il->cmd_queue].q.write_ptr)) {
- il_print_hex_error(il, pkt, 32);
- return;
- }
-
- cmd_idx = il_get_cmd_idx(&txq->q, idx, huge);
- cmd = txq->cmd[cmd_idx];
- meta = &txq->meta[cmd_idx];
-
- txq->time_stamp = jiffies;
-
- pci_unmap_single(il->pci_dev, dma_unmap_addr(meta, mapping),
- dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL);
-
- /* Input error checking is done when commands are added to queue. */
- if (meta->flags & CMD_WANT_SKB) {
- meta->source->reply_page = (unsigned long)rxb_addr(rxb);
- rxb->page = NULL;
- } else if (meta->callback)
- meta->callback(il, cmd, pkt);
-
- spin_lock_irqsave(&il->hcmd_lock, flags);
-
- il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx);
-
- if (!(meta->flags & CMD_ASYNC)) {
- clear_bit(S_HCMD_ACTIVE, &il->status);
- D_INFO("Clearing HCMD_ACTIVE for command %s\n",
- il_get_cmd_string(cmd->hdr.cmd));
- wake_up(&il->wait_command_queue);
- }
-
- /* Mark as unmapped */
- meta->flags = 0;
-
- spin_unlock_irqrestore(&il->hcmd_lock, flags);
-}
-EXPORT_SYMBOL(il_tx_cmd_complete);
-
-MODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965");
-MODULE_VERSION(IWLWIFI_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
-MODULE_LICENSE("GPL");
-
-/*
- * set bt_coex_active to true, uCode will do kill/defer
- * every time the priority line is asserted (BT is sending signals on the
- * priority line in the PCIx).
- * set bt_coex_active to false, uCode will ignore the BT activity and
- * perform the normal operation
- *
- * User might experience transmit issue on some platform due to WiFi/BT
- * co-exist problem. The possible behaviors are:
- * Able to scan and finding all the available AP
- * Not able to associate with any AP
- * On those platforms, WiFi communication can be restored by set
- * "bt_coex_active" module parameter to "false"
- *
- * default: bt_coex_active = true (BT_COEX_ENABLE)
- */
-static bool bt_coex_active = true;
-module_param(bt_coex_active, bool, S_IRUGO);
-MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist");
-
-u32 il_debug_level;
-EXPORT_SYMBOL(il_debug_level);
-
-const u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-EXPORT_SYMBOL(il_bcast_addr);
-
-#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
-#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
-static void
-il_init_ht_hw_capab(const struct il_priv *il,
- struct ieee80211_sta_ht_cap *ht_info,
- enum ieee80211_band band)
-{
- u16 max_bit_rate = 0;
- u8 rx_chains_num = il->hw_params.rx_chains_num;
- u8 tx_chains_num = il->hw_params.tx_chains_num;
-
- ht_info->cap = 0;
- memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
-
- ht_info->ht_supported = true;
-
- ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
- max_bit_rate = MAX_BIT_RATE_20_MHZ;
- if (il->hw_params.ht40_channel & BIT(band)) {
- ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
- ht_info->mcs.rx_mask[4] = 0x01;
- max_bit_rate = MAX_BIT_RATE_40_MHZ;
- }
-
- if (il->cfg->mod_params->amsdu_size_8K)
- ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
-
- ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
- ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF;
-
- ht_info->mcs.rx_mask[0] = 0xFF;
- if (rx_chains_num >= 2)
- ht_info->mcs.rx_mask[1] = 0xFF;
- if (rx_chains_num >= 3)
- ht_info->mcs.rx_mask[2] = 0xFF;
-
- /* Highest supported Rx data rate */
- max_bit_rate *= rx_chains_num;
- WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
- ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
-
- /* Tx MCS capabilities */
- ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
- if (tx_chains_num != rx_chains_num) {
- ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
- ht_info->mcs.tx_params |=
- ((tx_chains_num -
- 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
- }
-}
-
-/**
- * il_init_geos - Initialize mac80211's geo/channel info based from eeprom
- */
-int
-il_init_geos(struct il_priv *il)
-{
- struct il_channel_info *ch;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *channels;
- struct ieee80211_channel *geo_ch;
- struct ieee80211_rate *rates;
- int i = 0;
- s8 max_tx_power = 0;
-
- if (il->bands[IEEE80211_BAND_2GHZ].n_bitrates ||
- il->bands[IEEE80211_BAND_5GHZ].n_bitrates) {
- D_INFO("Geography modes already initialized.\n");
- set_bit(S_GEO_CONFIGURED, &il->status);
- return 0;
- }
-
- channels =
- kzalloc(sizeof(struct ieee80211_channel) * il->channel_count,
- GFP_KERNEL);
- if (!channels)
- return -ENOMEM;
-
- rates =
- kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY),
- GFP_KERNEL);
- if (!rates) {
- kfree(channels);
- return -ENOMEM;
- }
-
- /* 5.2GHz channels start after the 2.4GHz channels */
- sband = &il->bands[IEEE80211_BAND_5GHZ];
- sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)];
- /* just OFDM */
- sband->bitrates = &rates[IL_FIRST_OFDM_RATE];
- sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE;
-
- if (il->cfg->sku & IL_SKU_N)
- il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_5GHZ);
-
- sband = &il->bands[IEEE80211_BAND_2GHZ];
- sband->channels = channels;
- /* OFDM & CCK */
- sband->bitrates = rates;
- sband->n_bitrates = RATE_COUNT_LEGACY;
-
- if (il->cfg->sku & IL_SKU_N)
- il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_2GHZ);
-
- il->ieee_channels = channels;
- il->ieee_rates = rates;
-
- for (i = 0; i < il->channel_count; i++) {
- ch = &il->channel_info[i];
-
- if (!il_is_channel_valid(ch))
- continue;
-
- sband = &il->bands[ch->band];
-
- geo_ch = &sband->channels[sband->n_channels++];
-
- geo_ch->center_freq =
- ieee80211_channel_to_frequency(ch->channel, ch->band);
- geo_ch->max_power = ch->max_power_avg;
- geo_ch->max_antenna_gain = 0xff;
- geo_ch->hw_value = ch->channel;
-
- if (il_is_channel_valid(ch)) {
- if (!(ch->flags & EEPROM_CHANNEL_IBSS))
- geo_ch->flags |= IEEE80211_CHAN_NO_IR;
-
- if (!(ch->flags & EEPROM_CHANNEL_ACTIVE))
- geo_ch->flags |= IEEE80211_CHAN_NO_IR;
-
- if (ch->flags & EEPROM_CHANNEL_RADAR)
- geo_ch->flags |= IEEE80211_CHAN_RADAR;
-
- geo_ch->flags |= ch->ht40_extension_channel;
-
- if (ch->max_power_avg > max_tx_power)
- max_tx_power = ch->max_power_avg;
- } else {
- geo_ch->flags |= IEEE80211_CHAN_DISABLED;
- }
-
- D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel,
- geo_ch->center_freq,
- il_is_channel_a_band(ch) ? "5.2" : "2.4",
- geo_ch->
- flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid",
- geo_ch->flags);
- }
-
- il->tx_power_device_lmt = max_tx_power;
- il->tx_power_user_lmt = max_tx_power;
- il->tx_power_next = max_tx_power;
-
- if (il->bands[IEEE80211_BAND_5GHZ].n_channels == 0 &&
- (il->cfg->sku & IL_SKU_A)) {
- IL_INFO("Incorrectly detected BG card as ABG. "
- "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n",
- il->pci_dev->device, il->pci_dev->subsystem_device);
- il->cfg->sku &= ~IL_SKU_A;
- }
-
- IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n",
- il->bands[IEEE80211_BAND_2GHZ].n_channels,
- il->bands[IEEE80211_BAND_5GHZ].n_channels);
-
- set_bit(S_GEO_CONFIGURED, &il->status);
-
- return 0;
-}
-EXPORT_SYMBOL(il_init_geos);
-
-/*
- * il_free_geos - undo allocations in il_init_geos
- */
-void
-il_free_geos(struct il_priv *il)
-{
- kfree(il->ieee_channels);
- kfree(il->ieee_rates);
- clear_bit(S_GEO_CONFIGURED, &il->status);
-}
-EXPORT_SYMBOL(il_free_geos);
-
-static bool
-il_is_channel_extension(struct il_priv *il, enum ieee80211_band band,
- u16 channel, u8 extension_chan_offset)
-{
- const struct il_channel_info *ch_info;
-
- ch_info = il_get_channel_info(il, band, channel);
- if (!il_is_channel_valid(ch_info))
- return false;
-
- if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
- return !(ch_info->
- ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS);
- else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)
- return !(ch_info->
- ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS);
-
- return false;
-}
-
-bool
-il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap)
-{
- if (!il->ht.enabled || !il->ht.is_40mhz)
- return false;
-
- /*
- * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
- * the bit will not set if it is pure 40MHz case
- */
- if (ht_cap && !ht_cap->ht_supported)
- return false;
-
-#ifdef CONFIG_IWLEGACY_DEBUGFS
- if (il->disable_ht40)
- return false;
-#endif
-
- return il_is_channel_extension(il, il->band,
- le16_to_cpu(il->staging.channel),
- il->ht.extension_chan_offset);
-}
-EXPORT_SYMBOL(il_is_ht40_tx_allowed);
-
-static u16
-il_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
-{
- u16 new_val;
- u16 beacon_factor;
-
- /*
- * If mac80211 hasn't given us a beacon interval, program
- * the default into the device.
- */
- if (!beacon_val)
- return DEFAULT_BEACON_INTERVAL;
-
- /*
- * If the beacon interval we obtained from the peer
- * is too large, we'll have to wake up more often
- * (and in IBSS case, we'll beacon too much)
- *
- * For example, if max_beacon_val is 4096, and the
- * requested beacon interval is 7000, we'll have to
- * use 3500 to be able to wake up on the beacons.
- *
- * This could badly influence beacon detection stats.
- */
-
- beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
- new_val = beacon_val / beacon_factor;
-
- if (!new_val)
- new_val = max_beacon_val;
-
- return new_val;
-}
-
-int
-il_send_rxon_timing(struct il_priv *il)
-{
- u64 tsf;
- s32 interval_tm, rem;
- struct ieee80211_conf *conf = NULL;
- u16 beacon_int;
- struct ieee80211_vif *vif = il->vif;
-
- conf = &il->hw->conf;
-
- lockdep_assert_held(&il->mutex);
-
- memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd));
-
- il->timing.timestamp = cpu_to_le64(il->timestamp);
- il->timing.listen_interval = cpu_to_le16(conf->listen_interval);
-
- beacon_int = vif ? vif->bss_conf.beacon_int : 0;
-
- /*
- * TODO: For IBSS we need to get atim_win from mac80211,
- * for now just always use 0
- */
- il->timing.atim_win = 0;
-
- beacon_int =
- il_adjust_beacon_interval(beacon_int,
- il->hw_params.max_beacon_itrvl *
- TIME_UNIT);
- il->timing.beacon_interval = cpu_to_le16(beacon_int);
-
- tsf = il->timestamp; /* tsf is modifed by do_div: copy it */
- interval_tm = beacon_int * TIME_UNIT;
- rem = do_div(tsf, interval_tm);
- il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
-
- il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1;
-
- D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n",
- le16_to_cpu(il->timing.beacon_interval),
- le32_to_cpu(il->timing.beacon_init_val),
- le16_to_cpu(il->timing.atim_win));
-
- return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing),
- &il->timing);
-}
-EXPORT_SYMBOL(il_send_rxon_timing);
-
-void
-il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt)
-{
- struct il_rxon_cmd *rxon = &il->staging;
-
- if (hw_decrypt)
- rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
- else
- rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
-
-}
-EXPORT_SYMBOL(il_set_rxon_hwcrypto);
-
-/* validate RXON structure is valid */
-int
-il_check_rxon_cmd(struct il_priv *il)
-{
- struct il_rxon_cmd *rxon = &il->staging;
- bool error = false;
-
- if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
- if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
- IL_WARN("check 2.4G: wrong narrow\n");
- error = true;
- }
- if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
- IL_WARN("check 2.4G: wrong radar\n");
- error = true;
- }
- } else {
- if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
- IL_WARN("check 5.2G: not short slot!\n");
- error = true;
- }
- if (rxon->flags & RXON_FLG_CCK_MSK) {
- IL_WARN("check 5.2G: CCK!\n");
- error = true;
- }
- }
- if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
- IL_WARN("mac/bssid mcast!\n");
- error = true;
- }
-
- /* make sure basic rates 6Mbps and 1Mbps are supported */
- if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 &&
- (rxon->cck_basic_rates & RATE_1M_MASK) == 0) {
- IL_WARN("neither 1 nor 6 are basic\n");
- error = true;
- }
-
- if (le16_to_cpu(rxon->assoc_id) > 2007) {
- IL_WARN("aid > 2007\n");
- error = true;
- }
-
- if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) ==
- (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
- IL_WARN("CCK and short slot\n");
- error = true;
- }
-
- if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) ==
- (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
- IL_WARN("CCK and auto detect");
- error = true;
- }
-
- if ((rxon->
- flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) ==
- RXON_FLG_TGG_PROTECT_MSK) {
- IL_WARN("TGg but no auto-detect\n");
- error = true;
- }
-
- if (error)
- IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel));
-
- if (error) {
- IL_ERR("Invalid RXON\n");
- return -EINVAL;
- }
- return 0;
-}
-EXPORT_SYMBOL(il_check_rxon_cmd);
-
-/**
- * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
- * @il: staging_rxon is compared to active_rxon
- *
- * If the RXON structure is changing enough to require a new tune,
- * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
- * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
- */
-int
-il_full_rxon_required(struct il_priv *il)
-{
- const struct il_rxon_cmd *staging = &il->staging;
- const struct il_rxon_cmd *active = &il->active;
-
-#define CHK(cond) \
- if ((cond)) { \
- D_INFO("need full RXON - " #cond "\n"); \
- return 1; \
- }
-
-#define CHK_NEQ(c1, c2) \
- if ((c1) != (c2)) { \
- D_INFO("need full RXON - " \
- #c1 " != " #c2 " - %d != %d\n", \
- (c1), (c2)); \
- return 1; \
- }
-
- /* These items are only settable from the full RXON command */
- CHK(!il_is_associated(il));
- CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr));
- CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr));
- CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr,
- active->wlap_bssid_addr));
- CHK_NEQ(staging->dev_type, active->dev_type);
- CHK_NEQ(staging->channel, active->channel);
- CHK_NEQ(staging->air_propagation, active->air_propagation);
- CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
- active->ofdm_ht_single_stream_basic_rates);
- CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
- active->ofdm_ht_dual_stream_basic_rates);
- CHK_NEQ(staging->assoc_id, active->assoc_id);
-
- /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
- * be updated with the RXON_ASSOC command -- however only some
- * flag transitions are allowed using RXON_ASSOC */
-
- /* Check if we are not switching bands */
- CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
- active->flags & RXON_FLG_BAND_24G_MSK);
-
- /* Check if we are switching association toggle */
- CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
- active->filter_flags & RXON_FILTER_ASSOC_MSK);
-
-#undef CHK
-#undef CHK_NEQ
-
- return 0;
-}
-EXPORT_SYMBOL(il_full_rxon_required);
-
-u8
-il_get_lowest_plcp(struct il_priv *il)
-{
- /*
- * Assign the lowest rate -- should really get this from
- * the beacon skb from mac80211.
- */
- if (il->staging.flags & RXON_FLG_BAND_24G_MSK)
- return RATE_1M_PLCP;
- else
- return RATE_6M_PLCP;
-}
-EXPORT_SYMBOL(il_get_lowest_plcp);
-
-static void
-_il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf)
-{
- struct il_rxon_cmd *rxon = &il->staging;
-
- if (!il->ht.enabled) {
- rxon->flags &=
- ~(RXON_FLG_CHANNEL_MODE_MSK |
- RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK
- | RXON_FLG_HT_PROT_MSK);
- return;
- }
-
- rxon->flags |=
- cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS);
-
- /* Set up channel bandwidth:
- * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
- /* clear the HT channel mode before set the mode */
- rxon->flags &=
- ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
- if (il_is_ht40_tx_allowed(il, NULL)) {
- /* pure ht40 */
- if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
- rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
- /* Note: control channel is opposite of extension channel */
- switch (il->ht.extension_chan_offset) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- rxon->flags &=
- ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
- break;
- }
- } else {
- /* Note: control channel is opposite of extension channel */
- switch (il->ht.extension_chan_offset) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- rxon->flags &=
- ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
- rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
- rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_NONE:
- default:
- /* channel location only valid if in Mixed mode */
- IL_ERR("invalid extension channel offset\n");
- break;
- }
- }
- } else {
- rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
- }
-
- if (il->ops->set_rxon_chain)
- il->ops->set_rxon_chain(il);
-
- D_ASSOC("rxon flags 0x%X operation mode :0x%X "
- "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags),
- il->ht.protection, il->ht.extension_chan_offset);
-}
-
-void
-il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf)
-{
- _il_set_rxon_ht(il, ht_conf);
-}
-EXPORT_SYMBOL(il_set_rxon_ht);
-
-/* Return valid, unused, channel for a passive scan to reset the RF */
-u8
-il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band)
-{
- const struct il_channel_info *ch_info;
- int i;
- u8 channel = 0;
- u8 min, max;
-
- if (band == IEEE80211_BAND_5GHZ) {
- min = 14;
- max = il->channel_count;
- } else {
- min = 0;
- max = 14;
- }
-
- for (i = min; i < max; i++) {
- channel = il->channel_info[i].channel;
- if (channel == le16_to_cpu(il->staging.channel))
- continue;
-
- ch_info = il_get_channel_info(il, band, channel);
- if (il_is_channel_valid(ch_info))
- break;
- }
-
- return channel;
-}
-EXPORT_SYMBOL(il_get_single_channel_number);
-
-/**
- * il_set_rxon_channel - Set the band and channel values in staging RXON
- * @ch: requested channel as a pointer to struct ieee80211_channel
-
- * NOTE: Does not commit to the hardware; it sets appropriate bit fields
- * in the staging RXON flag structure based on the ch->band
- */
-int
-il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch)
-{
- enum ieee80211_band band = ch->band;
- u16 channel = ch->hw_value;
-
- if (le16_to_cpu(il->staging.channel) == channel && il->band == band)
- return 0;
-
- il->staging.channel = cpu_to_le16(channel);
- if (band == IEEE80211_BAND_5GHZ)
- il->staging.flags &= ~RXON_FLG_BAND_24G_MSK;
- else
- il->staging.flags |= RXON_FLG_BAND_24G_MSK;
-
- il->band = band;
-
- D_INFO("Staging channel set to %d [%d]\n", channel, band);
-
- return 0;
-}
-EXPORT_SYMBOL(il_set_rxon_channel);
-
-void
-il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band,
- struct ieee80211_vif *vif)
-{
- if (band == IEEE80211_BAND_5GHZ) {
- il->staging.flags &=
- ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK |
- RXON_FLG_CCK_MSK);
- il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
- } else {
- /* Copied from il_post_associate() */
- if (vif && vif->bss_conf.use_short_slot)
- il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
- else
- il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
-
- il->staging.flags |= RXON_FLG_BAND_24G_MSK;
- il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK;
- il->staging.flags &= ~RXON_FLG_CCK_MSK;
- }
-}
-EXPORT_SYMBOL(il_set_flags_for_band);
-
-/*
- * initialize rxon structure with default values from eeprom
- */
-void
-il_connection_init_rx_config(struct il_priv *il)
-{
- const struct il_channel_info *ch_info;
-
- memset(&il->staging, 0, sizeof(il->staging));
-
- switch (il->iw_mode) {
- case NL80211_IFTYPE_UNSPECIFIED:
- il->staging.dev_type = RXON_DEV_TYPE_ESS;
- break;
- case NL80211_IFTYPE_STATION:
- il->staging.dev_type = RXON_DEV_TYPE_ESS;
- il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
- break;
- case NL80211_IFTYPE_ADHOC:
- il->staging.dev_type = RXON_DEV_TYPE_IBSS;
- il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
- il->staging.filter_flags =
- RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
- break;
- default:
- IL_ERR("Unsupported interface type %d\n", il->vif->type);
- return;
- }
-
-#if 0
- /* TODO: Figure out when short_preamble would be set and cache from
- * that */
- if (!hw_to_local(il->hw)->short_preamble)
- il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
- else
- il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
-#endif
-
- ch_info =
- il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel));
-
- if (!ch_info)
- ch_info = &il->channel_info[0];
-
- il->staging.channel = cpu_to_le16(ch_info->channel);
- il->band = ch_info->band;
-
- il_set_flags_for_band(il, il->band, il->vif);
-
- il->staging.ofdm_basic_rates =
- (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF;
- il->staging.cck_basic_rates =
- (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF;
-
- /* clear both MIX and PURE40 mode flag */
- il->staging.flags &=
- ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40);
- if (il->vif)
- memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN);
-
- il->staging.ofdm_ht_single_stream_basic_rates = 0xff;
- il->staging.ofdm_ht_dual_stream_basic_rates = 0xff;
-}
-EXPORT_SYMBOL(il_connection_init_rx_config);
-
-void
-il_set_rate(struct il_priv *il)
-{
- const struct ieee80211_supported_band *hw = NULL;
- struct ieee80211_rate *rate;
- int i;
-
- hw = il_get_hw_mode(il, il->band);
- if (!hw) {
- IL_ERR("Failed to set rate: unable to get hw mode\n");
- return;
- }
-
- il->active_rate = 0;
-
- for (i = 0; i < hw->n_bitrates; i++) {
- rate = &(hw->bitrates[i]);
- if (rate->hw_value < RATE_COUNT_LEGACY)
- il->active_rate |= (1 << rate->hw_value);
- }
-
- D_RATE("Set active_rate = %0x\n", il->active_rate);
-
- il->staging.cck_basic_rates =
- (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF;
-
- il->staging.ofdm_basic_rates =
- (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF;
-}
-EXPORT_SYMBOL(il_set_rate);
-
-void
-il_chswitch_done(struct il_priv *il, bool is_success)
-{
- if (test_bit(S_EXIT_PENDING, &il->status))
- return;
-
- if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
- ieee80211_chswitch_done(il->vif, is_success);
-}
-EXPORT_SYMBOL(il_chswitch_done);
-
-void
-il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_csa_notification *csa = &(pkt->u.csa_notif);
- struct il_rxon_cmd *rxon = (void *)&il->active;
-
- if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
- return;
-
- if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) {
- rxon->channel = csa->channel;
- il->staging.channel = csa->channel;
- D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel));
- il_chswitch_done(il, true);
- } else {
- IL_ERR("CSA notif (fail) : channel %d\n",
- le16_to_cpu(csa->channel));
- il_chswitch_done(il, false);
- }
-}
-EXPORT_SYMBOL(il_hdl_csa);
-
-#ifdef CONFIG_IWLEGACY_DEBUG
-void
-il_print_rx_config_cmd(struct il_priv *il)
-{
- struct il_rxon_cmd *rxon = &il->staging;
-
- D_RADIO("RX CONFIG:\n");
- il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
- D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel));
- D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags));
- D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags));
- D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type);
- D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates);
- D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates);
- D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr);
- D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
- D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
-}
-EXPORT_SYMBOL(il_print_rx_config_cmd);
-#endif
-/**
- * il_irq_handle_error - called for HW or SW error interrupt from card
- */
-void
-il_irq_handle_error(struct il_priv *il)
-{
- /* Set the FW error flag -- cleared on il_down */
- set_bit(S_FW_ERROR, &il->status);
-
- /* Cancel currently queued command. */
- clear_bit(S_HCMD_ACTIVE, &il->status);
-
- IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version);
-
- il->ops->dump_nic_error_log(il);
- if (il->ops->dump_fh)
- il->ops->dump_fh(il, NULL, false);
-#ifdef CONFIG_IWLEGACY_DEBUG
- if (il_get_debug_level(il) & IL_DL_FW_ERRORS)
- il_print_rx_config_cmd(il);
-#endif
-
- wake_up(&il->wait_command_queue);
-
- /* Keep the restart process from trying to send host
- * commands by clearing the INIT status bit */
- clear_bit(S_READY, &il->status);
-
- if (!test_bit(S_EXIT_PENDING, &il->status)) {
- IL_DBG(IL_DL_FW_ERRORS,
- "Restarting adapter due to uCode error.\n");
-
- if (il->cfg->mod_params->restart_fw)
- queue_work(il->workqueue, &il->restart);
- }
-}
-EXPORT_SYMBOL(il_irq_handle_error);
-
-static int
-_il_apm_stop_master(struct il_priv *il)
-{
- int ret = 0;
-
- /* stop device's busmaster DMA activity */
- _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
-
- ret =
- _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED,
- CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
- if (ret < 0)
- IL_WARN("Master Disable Timed Out, 100 usec\n");
-
- D_INFO("stop master\n");
-
- return ret;
-}
-
-void
-_il_apm_stop(struct il_priv *il)
-{
- lockdep_assert_held(&il->reg_lock);
-
- D_INFO("Stop card, put in low power state\n");
-
- /* Stop device's DMA activity */
- _il_apm_stop_master(il);
-
- /* Reset the entire device */
- _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
-
- /*
- * Clear "initialization complete" bit to move adapter from
- * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
- */
- _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-}
-EXPORT_SYMBOL(_il_apm_stop);
-
-void
-il_apm_stop(struct il_priv *il)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&il->reg_lock, flags);
- _il_apm_stop(il);
- spin_unlock_irqrestore(&il->reg_lock, flags);
-}
-EXPORT_SYMBOL(il_apm_stop);
-
-/*
- * Start up NIC's basic functionality after it has been reset
- * (e.g. after platform boot, or shutdown via il_apm_stop())
- * NOTE: This does not load uCode nor start the embedded processor
- */
-int
-il_apm_init(struct il_priv *il)
-{
- int ret = 0;
- u16 lctl;
-
- D_INFO("Init card's basic functions\n");
-
- /*
- * Use "set_bit" below rather than "write", to preserve any hardware
- * bits already set by default after reset.
- */
-
- /* Disable L0S exit timer (platform NMI Work/Around) */
- il_set_bit(il, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
-
- /*
- * Disable L0s without affecting L1;
- * don't wait for ICH L0s (ICH bug W/A)
- */
- il_set_bit(il, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
-
- /* Set FH wait threshold to maximum (HW error during stress W/A) */
- il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
-
- /*
- * Enable HAP INTA (interrupt from management bus) to
- * wake device's PCI Express link L1a -> L0s
- * NOTE: This is no-op for 3945 (non-existent bit)
- */
- il_set_bit(il, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
-
- /*
- * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition.
- * Check if BIOS (or OS) enabled L1-ASPM on this device.
- * If so (likely), disable L0S, so device moves directly L0->L1;
- * costs negligible amount of power savings.
- * If not (unlikely), enable L0S, so there is at least some
- * power savings, even without L1.
- */
- if (il->cfg->set_l0s) {
- pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
- if (lctl & PCI_EXP_LNKCTL_ASPM_L1) {
- /* L1-ASPM enabled; disable(!) L0S */
- il_set_bit(il, CSR_GIO_REG,
- CSR_GIO_REG_VAL_L0S_ENABLED);
- D_POWER("L1 Enabled; Disabling L0S\n");
- } else {
- /* L1-ASPM disabled; enable(!) L0S */
- il_clear_bit(il, CSR_GIO_REG,
- CSR_GIO_REG_VAL_L0S_ENABLED);
- D_POWER("L1 Disabled; Enabling L0S\n");
- }
- }
-
- /* Configure analog phase-lock-loop before activating to D0A */
- if (il->cfg->pll_cfg_val)
- il_set_bit(il, CSR_ANA_PLL_CFG,
- il->cfg->pll_cfg_val);
-
- /*
- * Set "initialization complete" bit to move adapter from
- * D0U* --> D0A* (powered-up active) state.
- */
- il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /*
- * Wait for clock stabilization; once stabilized, access to
- * device-internal resources is supported, e.g. il_wr_prph()
- * and accesses to uCode SRAM.
- */
- ret =
- _il_poll_bit(il, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (ret < 0) {
- D_INFO("Failed to init the card\n");
- goto out;
- }
-
- /*
- * Enable DMA and BSM (if used) clocks, wait for them to stabilize.
- * BSM (Boostrap State Machine) is only in 3945 and 4965.
- *
- * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
- * do not disable clocks. This preserves any hardware bits already
- * set by default in "CLK_CTRL_REG" after reset.
- */
- if (il->cfg->use_bsm)
- il_wr_prph(il, APMG_CLK_EN_REG,
- APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT);
- else
- il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
- udelay(20);
-
- /* Disable L1-Active */
- il_set_bits_prph(il, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-
-out:
- return ret;
-}
-EXPORT_SYMBOL(il_apm_init);
-
-int
-il_set_tx_power(struct il_priv *il, s8 tx_power, bool force)
-{
- int ret;
- s8 prev_tx_power;
- bool defer;
-
- lockdep_assert_held(&il->mutex);
-
- if (il->tx_power_user_lmt == tx_power && !force)
- return 0;
-
- if (!il->ops->send_tx_power)
- return -EOPNOTSUPP;
-
- /* 0 dBm mean 1 milliwatt */
- if (tx_power < 0) {
- IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power);
- return -EINVAL;
- }
-
- if (tx_power > il->tx_power_device_lmt) {
- IL_WARN("Requested user TXPOWER %d above upper limit %d.\n",
- tx_power, il->tx_power_device_lmt);
- return -EINVAL;
- }
-
- if (!il_is_ready_rf(il))
- return -EIO;
-
- /* scan complete and commit_rxon use tx_power_next value,
- * it always need to be updated for newest request */
- il->tx_power_next = tx_power;
-
- /* do not set tx power when scanning or channel changing */
- defer = test_bit(S_SCANNING, &il->status) ||
- memcmp(&il->active, &il->staging, sizeof(il->staging));
- if (defer && !force) {
- D_INFO("Deferring tx power set\n");
- return 0;
- }
-
- prev_tx_power = il->tx_power_user_lmt;
- il->tx_power_user_lmt = tx_power;
-
- ret = il->ops->send_tx_power(il);
-
- /* if fail to set tx_power, restore the orig. tx power */
- if (ret) {
- il->tx_power_user_lmt = prev_tx_power;
- il->tx_power_next = prev_tx_power;
- }
- return ret;
-}
-EXPORT_SYMBOL(il_set_tx_power);
-
-void
-il_send_bt_config(struct il_priv *il)
-{
- struct il_bt_cmd bt_cmd = {
- .lead_time = BT_LEAD_TIME_DEF,
- .max_kill = BT_MAX_KILL_DEF,
- .kill_ack_mask = 0,
- .kill_cts_mask = 0,
- };
-
- if (!bt_coex_active)
- bt_cmd.flags = BT_COEX_DISABLE;
- else
- bt_cmd.flags = BT_COEX_ENABLE;
-
- D_INFO("BT coex %s\n",
- (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
-
- if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd))
- IL_ERR("failed to send BT Coex Config\n");
-}
-EXPORT_SYMBOL(il_send_bt_config);
-
-int
-il_send_stats_request(struct il_priv *il, u8 flags, bool clear)
-{
- struct il_stats_cmd stats_cmd = {
- .configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0,
- };
-
- if (flags & CMD_ASYNC)
- return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd),
- &stats_cmd, NULL);
- else
- return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd),
- &stats_cmd);
-}
-EXPORT_SYMBOL(il_send_stats_request);
-
-void
-il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb)
-{
-#ifdef CONFIG_IWLEGACY_DEBUG
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- struct il_sleep_notification *sleep = &(pkt->u.sleep_notif);
- D_RX("sleep mode: %d, src: %d\n",
- sleep->pm_sleep_mode, sleep->pm_wakeup_src);
-#endif
-}
-EXPORT_SYMBOL(il_hdl_pm_sleep);
-
-void
-il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
- u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
- D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len,
- il_get_cmd_string(pkt->hdr.cmd));
- il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len);
-}
-EXPORT_SYMBOL(il_hdl_pm_debug_stats);
-
-void
-il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb)
-{
- struct il_rx_pkt *pkt = rxb_addr(rxb);
-
- IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) "
- "seq 0x%04X ser 0x%08X\n",
- le32_to_cpu(pkt->u.err_resp.error_type),
- il_get_cmd_string(pkt->u.err_resp.cmd_id),
- pkt->u.err_resp.cmd_id,
- le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num),
- le32_to_cpu(pkt->u.err_resp.error_info));
-}
-EXPORT_SYMBOL(il_hdl_error);
-
-void
-il_clear_isr_stats(struct il_priv *il)
-{
- memset(&il->isr_stats, 0, sizeof(il->isr_stats));
-}
-
-int
-il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
- const struct ieee80211_tx_queue_params *params)
-{
- struct il_priv *il = hw->priv;
- unsigned long flags;
- int q;
-
- D_MAC80211("enter\n");
-
- if (!il_is_ready_rf(il)) {
- D_MAC80211("leave - RF not ready\n");
- return -EIO;
- }
-
- if (queue >= AC_NUM) {
- D_MAC80211("leave - queue >= AC_NUM %d\n", queue);
- return 0;
- }
-
- q = AC_NUM - 1 - queue;
-
- spin_lock_irqsave(&il->lock, flags);
-
- il->qos_data.def_qos_parm.ac[q].cw_min =
- cpu_to_le16(params->cw_min);
- il->qos_data.def_qos_parm.ac[q].cw_max =
- cpu_to_le16(params->cw_max);
- il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs;
- il->qos_data.def_qos_parm.ac[q].edca_txop =
- cpu_to_le16((params->txop * 32));
-
- il->qos_data.def_qos_parm.ac[q].reserved1 = 0;
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- D_MAC80211("leave\n");
- return 0;
-}
-EXPORT_SYMBOL(il_mac_conf_tx);
-
-int
-il_mac_tx_last_beacon(struct ieee80211_hw *hw)
-{
- struct il_priv *il = hw->priv;
- int ret;
-
- D_MAC80211("enter\n");
-
- ret = (il->ibss_manager == IL_IBSS_MANAGER);
-
- D_MAC80211("leave ret %d\n", ret);
- return ret;
-}
-EXPORT_SYMBOL_GPL(il_mac_tx_last_beacon);
-
-static int
-il_set_mode(struct il_priv *il)
-{
- il_connection_init_rx_config(il);
-
- if (il->ops->set_rxon_chain)
- il->ops->set_rxon_chain(il);
-
- return il_commit_rxon(il);
-}
-
-int
-il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
- struct il_priv *il = hw->priv;
- int err;
- bool reset;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
-
- if (!il_is_ready_rf(il)) {
- IL_WARN("Try to add interface when device not ready\n");
- err = -EINVAL;
- goto out;
- }
-
- /*
- * We do not support multiple virtual interfaces, but on hardware reset
- * we have to add the same interface again.
- */
- reset = (il->vif == vif);
- if (il->vif && !reset) {
- err = -EOPNOTSUPP;
- goto out;
- }
-
- il->vif = vif;
- il->iw_mode = vif->type;
-
- err = il_set_mode(il);
- if (err) {
- IL_WARN("Fail to set mode %d\n", vif->type);
- if (!reset) {
- il->vif = NULL;
- il->iw_mode = NL80211_IFTYPE_STATION;
- }
- }
-
-out:
- D_MAC80211("leave err %d\n", err);
- mutex_unlock(&il->mutex);
-
- return err;
-}
-EXPORT_SYMBOL(il_mac_add_interface);
-
-static void
-il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif)
-{
- lockdep_assert_held(&il->mutex);
-
- if (il->scan_vif == vif) {
- il_scan_cancel_timeout(il, 200);
- il_force_scan_end(il);
- }
-
- il_set_mode(il);
-}
-
-void
-il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
- struct il_priv *il = hw->priv;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
-
- WARN_ON(il->vif != vif);
- il->vif = NULL;
- il->iw_mode = NL80211_IFTYPE_UNSPECIFIED;
- il_teardown_interface(il, vif);
- eth_zero_addr(il->bssid);
-
- D_MAC80211("leave\n");
- mutex_unlock(&il->mutex);
-}
-EXPORT_SYMBOL(il_mac_remove_interface);
-
-int
-il_alloc_txq_mem(struct il_priv *il)
-{
- if (!il->txq)
- il->txq =
- kzalloc(sizeof(struct il_tx_queue) *
- il->cfg->num_of_queues, GFP_KERNEL);
- if (!il->txq) {
- IL_ERR("Not enough memory for txq\n");
- return -ENOMEM;
- }
- return 0;
-}
-EXPORT_SYMBOL(il_alloc_txq_mem);
-
-void
-il_free_txq_mem(struct il_priv *il)
-{
- kfree(il->txq);
- il->txq = NULL;
-}
-EXPORT_SYMBOL(il_free_txq_mem);
-
-int
-il_force_reset(struct il_priv *il, bool external)
-{
- struct il_force_reset *force_reset;
-
- if (test_bit(S_EXIT_PENDING, &il->status))
- return -EINVAL;
-
- force_reset = &il->force_reset;
- force_reset->reset_request_count++;
- if (!external) {
- if (force_reset->last_force_reset_jiffies &&
- time_after(force_reset->last_force_reset_jiffies +
- force_reset->reset_duration, jiffies)) {
- D_INFO("force reset rejected\n");
- force_reset->reset_reject_count++;
- return -EAGAIN;
- }
- }
- force_reset->reset_success_count++;
- force_reset->last_force_reset_jiffies = jiffies;
-
- /*
- * if the request is from external(ex: debugfs),
- * then always perform the request in regardless the module
- * parameter setting
- * if the request is from internal (uCode error or driver
- * detect failure), then fw_restart module parameter
- * need to be check before performing firmware reload
- */
-
- if (!external && !il->cfg->mod_params->restart_fw) {
- D_INFO("Cancel firmware reload based on "
- "module parameter setting\n");
- return 0;
- }
-
- IL_ERR("On demand firmware reload\n");
-
- /* Set the FW error flag -- cleared on il_down */
- set_bit(S_FW_ERROR, &il->status);
- wake_up(&il->wait_command_queue);
- /*
- * Keep the restart process from trying to send host
- * commands by clearing the INIT status bit
- */
- clear_bit(S_READY, &il->status);
- queue_work(il->workqueue, &il->restart);
-
- return 0;
-}
-EXPORT_SYMBOL(il_force_reset);
-
-int
-il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- enum nl80211_iftype newtype, bool newp2p)
-{
- struct il_priv *il = hw->priv;
- int err;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n",
- vif->type, vif->addr, newtype, newp2p);
-
- if (newp2p) {
- err = -EOPNOTSUPP;
- goto out;
- }
-
- if (!il->vif || !il_is_ready_rf(il)) {
- /*
- * Huh? But wait ... this can maybe happen when
- * we're in the middle of a firmware restart!
- */
- err = -EBUSY;
- goto out;
- }
-
- /* success */
- vif->type = newtype;
- vif->p2p = false;
- il->iw_mode = newtype;
- il_teardown_interface(il, vif);
- err = 0;
-
-out:
- D_MAC80211("leave err %d\n", err);
- mutex_unlock(&il->mutex);
-
- return err;
-}
-EXPORT_SYMBOL(il_mac_change_interface);
-
-void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- u32 queues, bool drop)
-{
- struct il_priv *il = hw->priv;
- unsigned long timeout = jiffies + msecs_to_jiffies(500);
- int i;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter\n");
-
- if (il->txq == NULL)
- goto out;
-
- for (i = 0; i < il->hw_params.max_txq_num; i++) {
- struct il_queue *q;
-
- if (i == il->cmd_queue)
- continue;
-
- q = &il->txq[i].q;
- if (q->read_ptr == q->write_ptr)
- continue;
-
- if (time_after(jiffies, timeout)) {
- IL_ERR("Failed to flush queue %d\n", q->id);
- break;
- }
-
- msleep(20);
- }
-out:
- D_MAC80211("leave\n");
- mutex_unlock(&il->mutex);
-}
-EXPORT_SYMBOL(il_mac_flush);
-
-/*
- * On every watchdog tick we check (latest) time stamp. If it does not
- * change during timeout period and queue is not empty we reset firmware.
- */
-static int
-il_check_stuck_queue(struct il_priv *il, int cnt)
-{
- struct il_tx_queue *txq = &il->txq[cnt];
- struct il_queue *q = &txq->q;
- unsigned long timeout;
- unsigned long now = jiffies;
- int ret;
-
- if (q->read_ptr == q->write_ptr) {
- txq->time_stamp = now;
- return 0;
- }
-
- timeout =
- txq->time_stamp +
- msecs_to_jiffies(il->cfg->wd_timeout);
-
- if (time_after(now, timeout)) {
- IL_ERR("Queue %d stuck for %u ms.\n", q->id,
- jiffies_to_msecs(now - txq->time_stamp));
- ret = il_force_reset(il, false);
- return (ret == -EAGAIN) ? 0 : 1;
- }
-
- return 0;
-}
-
-/*
- * Making watchdog tick be a quarter of timeout assure we will
- * discover the queue hung between timeout and 1.25*timeout
- */
-#define IL_WD_TICK(timeout) ((timeout) / 4)
-
-/*
- * Watchdog timer callback, we check each tx queue for stuck, if if hung
- * we reset the firmware. If everything is fine just rearm the timer.
- */
-void
-il_bg_watchdog(unsigned long data)
-{
- struct il_priv *il = (struct il_priv *)data;
- int cnt;
- unsigned long timeout;
-
- if (test_bit(S_EXIT_PENDING, &il->status))
- return;
-
- timeout = il->cfg->wd_timeout;
- if (timeout == 0)
- return;
-
- /* monitor and check for stuck cmd queue */
- if (il_check_stuck_queue(il, il->cmd_queue))
- return;
-
- /* monitor and check for other stuck queues */
- for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) {
- /* skip as we already checked the command queue */
- if (cnt == il->cmd_queue)
- continue;
- if (il_check_stuck_queue(il, cnt))
- return;
- }
-
- mod_timer(&il->watchdog,
- jiffies + msecs_to_jiffies(IL_WD_TICK(timeout)));
-}
-EXPORT_SYMBOL(il_bg_watchdog);
-
-void
-il_setup_watchdog(struct il_priv *il)
-{
- unsigned int timeout = il->cfg->wd_timeout;
-
- if (timeout)
- mod_timer(&il->watchdog,
- jiffies + msecs_to_jiffies(IL_WD_TICK(timeout)));
- else
- del_timer(&il->watchdog);
-}
-EXPORT_SYMBOL(il_setup_watchdog);
-
-/*
- * extended beacon time format
- * time in usec will be changed into a 32-bit value in extended:internal format
- * the extended part is the beacon counts
- * the internal part is the time in usec within one beacon interval
- */
-u32
-il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval)
-{
- u32 quot;
- u32 rem;
- u32 interval = beacon_interval * TIME_UNIT;
-
- if (!interval || !usec)
- return 0;
-
- quot =
- (usec /
- interval) & (il_beacon_time_mask_high(il,
- il->hw_params.
- beacon_time_tsf_bits) >> il->
- hw_params.beacon_time_tsf_bits);
- rem =
- (usec % interval) & il_beacon_time_mask_low(il,
- il->hw_params.
- beacon_time_tsf_bits);
-
- return (quot << il->hw_params.beacon_time_tsf_bits) + rem;
-}
-EXPORT_SYMBOL(il_usecs_to_beacons);
-
-/* base is usually what we get from ucode with each received frame,
- * the same as HW timer counter counting down
- */
-__le32
-il_add_beacon_time(struct il_priv *il, u32 base, u32 addon,
- u32 beacon_interval)
-{
- u32 base_low = base & il_beacon_time_mask_low(il,
- il->hw_params.
- beacon_time_tsf_bits);
- u32 addon_low = addon & il_beacon_time_mask_low(il,
- il->hw_params.
- beacon_time_tsf_bits);
- u32 interval = beacon_interval * TIME_UNIT;
- u32 res = (base & il_beacon_time_mask_high(il,
- il->hw_params.
- beacon_time_tsf_bits)) +
- (addon & il_beacon_time_mask_high(il,
- il->hw_params.
- beacon_time_tsf_bits));
-
- if (base_low > addon_low)
- res += base_low - addon_low;
- else if (base_low < addon_low) {
- res += interval + base_low - addon_low;
- res += (1 << il->hw_params.beacon_time_tsf_bits);
- } else
- res += (1 << il->hw_params.beacon_time_tsf_bits);
-
- return cpu_to_le32(res);
-}
-EXPORT_SYMBOL(il_add_beacon_time);
-
-#ifdef CONFIG_PM_SLEEP
-
-static int
-il_pci_suspend(struct device *device)
-{
- struct pci_dev *pdev = to_pci_dev(device);
- struct il_priv *il = pci_get_drvdata(pdev);
-
- /*
- * This function is called when system goes into suspend state
- * mac80211 will call il_mac_stop() from the mac80211 suspend function
- * first but since il_mac_stop() has no knowledge of who the caller is,
- * it will not call apm_ops.stop() to stop the DMA operation.
- * Calling apm_ops.stop here to make sure we stop the DMA.
- */
- il_apm_stop(il);
-
- return 0;
-}
-
-static int
-il_pci_resume(struct device *device)
-{
- struct pci_dev *pdev = to_pci_dev(device);
- struct il_priv *il = pci_get_drvdata(pdev);
- bool hw_rfkill = false;
-
- /*
- * We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state.
- */
- pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
-
- il_enable_interrupts(il);
-
- if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
- hw_rfkill = true;
-
- if (hw_rfkill)
- set_bit(S_RFKILL, &il->status);
- else
- clear_bit(S_RFKILL, &il->status);
-
- wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill);
-
- return 0;
-}
-
-SIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume);
-EXPORT_SYMBOL(il_pm_ops);
-
-#endif /* CONFIG_PM_SLEEP */
-
-static void
-il_update_qos(struct il_priv *il)
-{
- if (test_bit(S_EXIT_PENDING, &il->status))
- return;
-
- il->qos_data.def_qos_parm.qos_flags = 0;
-
- if (il->qos_data.qos_active)
- il->qos_data.def_qos_parm.qos_flags |=
- QOS_PARAM_FLG_UPDATE_EDCA_MSK;
-
- if (il->ht.enabled)
- il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
-
- D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n",
- il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags);
-
- il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd),
- &il->qos_data.def_qos_parm, NULL);
-}
-
-/**
- * il_mac_config - mac80211 config callback
- */
-int
-il_mac_config(struct ieee80211_hw *hw, u32 changed)
-{
- struct il_priv *il = hw->priv;
- const struct il_channel_info *ch_info;
- struct ieee80211_conf *conf = &hw->conf;
- struct ieee80211_channel *channel = conf->chandef.chan;
- struct il_ht_config *ht_conf = &il->current_ht_config;
- unsigned long flags = 0;
- int ret = 0;
- u16 ch;
- int scan_active = 0;
- bool ht_changed = false;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value,
- changed);
-
- if (unlikely(test_bit(S_SCANNING, &il->status))) {
- scan_active = 1;
- D_MAC80211("scan active\n");
- }
-
- if (changed &
- (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) {
- /* mac80211 uses static for non-HT which is what we want */
- il->current_ht_config.smps = conf->smps_mode;
-
- /*
- * Recalculate chain counts.
- *
- * If monitor mode is enabled then mac80211 will
- * set up the SM PS mode to OFF if an HT channel is
- * configured.
- */
- if (il->ops->set_rxon_chain)
- il->ops->set_rxon_chain(il);
- }
-
- /* during scanning mac80211 will delay channel setting until
- * scan finish with changed = 0
- */
- if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
-
- if (scan_active)
- goto set_ch_out;
-
- ch = channel->hw_value;
- ch_info = il_get_channel_info(il, channel->band, ch);
- if (!il_is_channel_valid(ch_info)) {
- D_MAC80211("leave - invalid channel\n");
- ret = -EINVAL;
- goto set_ch_out;
- }
-
- if (il->iw_mode == NL80211_IFTYPE_ADHOC &&
- !il_is_channel_ibss(ch_info)) {
- D_MAC80211("leave - not IBSS channel\n");
- ret = -EINVAL;
- goto set_ch_out;
- }
-
- spin_lock_irqsave(&il->lock, flags);
-
- /* Configure HT40 channels */
- if (il->ht.enabled != conf_is_ht(conf)) {
- il->ht.enabled = conf_is_ht(conf);
- ht_changed = true;
- }
- if (il->ht.enabled) {
- if (conf_is_ht40_minus(conf)) {
- il->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- il->ht.is_40mhz = true;
- } else if (conf_is_ht40_plus(conf)) {
- il->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- il->ht.is_40mhz = true;
- } else {
- il->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_NONE;
- il->ht.is_40mhz = false;
- }
- } else
- il->ht.is_40mhz = false;
-
- /*
- * Default to no protection. Protection mode will
- * later be set from BSS config in il_ht_conf
- */
- il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
-
- /* if we are switching from ht to 2.4 clear flags
- * from any ht related info since 2.4 does not
- * support ht */
- if ((le16_to_cpu(il->staging.channel) != ch))
- il->staging.flags = 0;
-
- il_set_rxon_channel(il, channel);
- il_set_rxon_ht(il, ht_conf);
-
- il_set_flags_for_band(il, channel->band, il->vif);
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- if (il->ops->update_bcast_stations)
- ret = il->ops->update_bcast_stations(il);
-
-set_ch_out:
- /* The list of supported rates and rate mask can be different
- * for each band; since the band may have changed, reset
- * the rate mask to what mac80211 lists */
- il_set_rate(il);
- }
-
- if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) {
- il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS);
- ret = il_power_update_mode(il, false);
- if (ret)
- D_MAC80211("Error setting sleep level\n");
- }
-
- if (changed & IEEE80211_CONF_CHANGE_POWER) {
- D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt,
- conf->power_level);
-
- il_set_tx_power(il, conf->power_level, false);
- }
-
- if (!il_is_ready(il)) {
- D_MAC80211("leave - not ready\n");
- goto out;
- }
-
- if (scan_active)
- goto out;
-
- if (memcmp(&il->active, &il->staging, sizeof(il->staging)))
- il_commit_rxon(il);
- else
- D_INFO("Not re-sending same RXON configuration.\n");
- if (ht_changed)
- il_update_qos(il);
-
-out:
- D_MAC80211("leave ret %d\n", ret);
- mutex_unlock(&il->mutex);
-
- return ret;
-}
-EXPORT_SYMBOL(il_mac_config);
-
-void
-il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
- struct il_priv *il = hw->priv;
- unsigned long flags;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
-
- spin_lock_irqsave(&il->lock, flags);
-
- memset(&il->current_ht_config, 0, sizeof(struct il_ht_config));
-
- /* new association get rid of ibss beacon skb */
- if (il->beacon_skb)
- dev_kfree_skb(il->beacon_skb);
- il->beacon_skb = NULL;
- il->timestamp = 0;
-
- spin_unlock_irqrestore(&il->lock, flags);
-
- il_scan_cancel_timeout(il, 100);
- if (!il_is_ready_rf(il)) {
- D_MAC80211("leave - not ready\n");
- mutex_unlock(&il->mutex);
- return;
- }
-
- /* we are restarting association process */
- il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- il_commit_rxon(il);
-
- il_set_rate(il);
-
- D_MAC80211("leave\n");
- mutex_unlock(&il->mutex);
-}
-EXPORT_SYMBOL(il_mac_reset_tsf);
-
-static void
-il_ht_conf(struct il_priv *il, struct ieee80211_vif *vif)
-{
- struct il_ht_config *ht_conf = &il->current_ht_config;
- struct ieee80211_sta *sta;
- struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-
- D_ASSOC("enter:\n");
-
- if (!il->ht.enabled)
- return;
-
- il->ht.protection =
- bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
- il->ht.non_gf_sta_present =
- !!(bss_conf->
- ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
-
- ht_conf->single_chain_sufficient = false;
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- rcu_read_lock();
- sta = ieee80211_find_sta(vif, bss_conf->bssid);
- if (sta) {
- struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- int maxstreams;
-
- maxstreams =
- (ht_cap->mcs.
- tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
- >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
- maxstreams += 1;
-
- if (ht_cap->mcs.rx_mask[1] == 0 &&
- ht_cap->mcs.rx_mask[2] == 0)
- ht_conf->single_chain_sufficient = true;
- if (maxstreams <= 1)
- ht_conf->single_chain_sufficient = true;
- } else {
- /*
- * If at all, this can only happen through a race
- * when the AP disconnects us while we're still
- * setting up the connection, in that case mac80211
- * will soon tell us about that.
- */
- ht_conf->single_chain_sufficient = true;
- }
- rcu_read_unlock();
- break;
- case NL80211_IFTYPE_ADHOC:
- ht_conf->single_chain_sufficient = true;
- break;
- default:
- break;
- }
-
- D_ASSOC("leave\n");
-}
-
-static inline void
-il_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif)
-{
- /*
- * inform the ucode that there is no longer an
- * association and that no more packets should be
- * sent
- */
- il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- il->staging.assoc_id = 0;
- il_commit_rxon(il);
-}
-
-static void
-il_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
- struct il_priv *il = hw->priv;
- unsigned long flags;
- __le64 timestamp;
- struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
-
- if (!skb)
- return;
-
- D_MAC80211("enter\n");
-
- lockdep_assert_held(&il->mutex);
-
- if (!il->beacon_enabled) {
- IL_ERR("update beacon with no beaconing enabled\n");
- dev_kfree_skb(skb);
- return;
- }
-
- spin_lock_irqsave(&il->lock, flags);
-
- if (il->beacon_skb)
- dev_kfree_skb(il->beacon_skb);
-
- il->beacon_skb = skb;
-
- timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
- il->timestamp = le64_to_cpu(timestamp);
-
- D_MAC80211("leave\n");
- spin_unlock_irqrestore(&il->lock, flags);
-
- if (!il_is_ready_rf(il)) {
- D_MAC80211("leave - RF not ready\n");
- return;
- }
-
- il->ops->post_associate(il);
-}
-
-void
-il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *bss_conf, u32 changes)
-{
- struct il_priv *il = hw->priv;
- int ret;
-
- mutex_lock(&il->mutex);
- D_MAC80211("enter: changes 0x%x\n", changes);
-
- if (!il_is_alive(il)) {
- D_MAC80211("leave - not alive\n");
- mutex_unlock(&il->mutex);
- return;
- }
-
- if (changes & BSS_CHANGED_QOS) {
- unsigned long flags;
-
- spin_lock_irqsave(&il->lock, flags);
- il->qos_data.qos_active = bss_conf->qos;
- il_update_qos(il);
- spin_unlock_irqrestore(&il->lock, flags);
- }
-
- if (changes & BSS_CHANGED_BEACON_ENABLED) {
- /* FIXME: can we remove beacon_enabled ? */
- if (vif->bss_conf.enable_beacon)
- il->beacon_enabled = true;
- else
- il->beacon_enabled = false;
- }
-
- 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
- * able to authenticate (FIXME: why ?)
- */
- if (il_scan_cancel_timeout(il, 100)) {
- D_MAC80211("leave - scan abort failed\n");
- mutex_unlock(&il->mutex);
- return;
- }
-
- /* mac80211 only sets assoc when in STATION mode */
- memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
-
- /* FIXME: currently needed in a few places */
- memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
- }
-
- /*
- * This needs to be after setting the BSSID in case
- * mac80211 decides to do both changes at once because
- * it will invoke post_associate.
- */
- if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON))
- il_beacon_update(hw, vif);
-
- if (changes & BSS_CHANGED_ERP_PREAMBLE) {
- D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble);
- if (bss_conf->use_short_preamble)
- il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
- else
- il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
- }
-
- if (changes & BSS_CHANGED_ERP_CTS_PROT) {
- D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot);
- if (bss_conf->use_cts_prot && il->band != IEEE80211_BAND_5GHZ)
- il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
- else
- il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
- if (bss_conf->use_cts_prot)
- il->staging.flags |= RXON_FLG_SELF_CTS_EN;
- else
- il->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
- }
-
- if (changes & BSS_CHANGED_BASIC_RATES) {
- /* XXX use this information
- *
- * To do that, remove code from il_set_rate() and put something
- * like this here:
- *
- if (A-band)
- il->staging.ofdm_basic_rates =
- bss_conf->basic_rates;
- else
- il->staging.ofdm_basic_rates =
- bss_conf->basic_rates >> 4;
- il->staging.cck_basic_rates =
- bss_conf->basic_rates & 0xF;
- */
- }
-
- if (changes & BSS_CHANGED_HT) {
- il_ht_conf(il, vif);
-
- if (il->ops->set_rxon_chain)
- il->ops->set_rxon_chain(il);
- }
-
- if (changes & BSS_CHANGED_ASSOC) {
- D_MAC80211("ASSOC %d\n", bss_conf->assoc);
- if (bss_conf->assoc) {
- il->timestamp = bss_conf->sync_tsf;
-
- if (!il_is_rfkill(il))
- il->ops->post_associate(il);
- } else
- il_set_no_assoc(il, vif);
- }
-
- if (changes && il_is_associated(il) && bss_conf->aid) {
- D_MAC80211("Changes (%#x) while associated\n", changes);
- ret = il_send_rxon_assoc(il);
- if (!ret) {
- /* Sync active_rxon with latest change. */
- memcpy((void *)&il->active, &il->staging,
- sizeof(struct il_rxon_cmd));
- }
- }
-
- if (changes & BSS_CHANGED_BEACON_ENABLED) {
- if (vif->bss_conf.enable_beacon) {
- memcpy(il->staging.bssid_addr, bss_conf->bssid,
- ETH_ALEN);
- memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
- il->ops->config_ap(il);
- } else
- il_set_no_assoc(il, vif);
- }
-
- if (changes & BSS_CHANGED_IBSS) {
- ret = il->ops->manage_ibss_station(il, vif,
- bss_conf->ibss_joined);
- if (ret)
- IL_ERR("failed to %s IBSS station %pM\n",
- bss_conf->ibss_joined ? "add" : "remove",
- bss_conf->bssid);
- }
-
- D_MAC80211("leave\n");
- mutex_unlock(&il->mutex);
-}
-EXPORT_SYMBOL(il_mac_bss_info_changed);
-
-irqreturn_t
-il_isr(int irq, void *data)
-{
- struct il_priv *il = data;
- u32 inta, inta_mask;
- u32 inta_fh;
- unsigned long flags;
- if (!il)
- return IRQ_NONE;
-
- spin_lock_irqsave(&il->lock, flags);
-
- /* Disable (but don't clear!) interrupts here to avoid
- * back-to-back ISRs and sporadic interrupts from our NIC.
- * If we have something to service, the tasklet will re-enable ints.
- * If we *don't* have something, we'll re-enable before leaving here. */
- inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */
- _il_wr(il, CSR_INT_MASK, 0x00000000);
-
- /* Discover which interrupts are active/pending */
- inta = _il_rd(il, CSR_INT);
- inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
-
- /* Ignore interrupt if there's nothing in NIC to service.
- * This may be due to IRQ shared with another device,
- * or due to sporadic interrupts thrown from our NIC. */
- if (!inta && !inta_fh) {
- D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n");
- goto none;
- }
-
- if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) {
- /* Hardware disappeared. It might have already raised
- * an interrupt */
- IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta);
- goto unplugged;
- }
-
- D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask,
- inta_fh);
-
- inta &= ~CSR_INT_BIT_SCD;
-
- /* il_irq_tasklet() will service interrupts and re-enable them */
- if (likely(inta || inta_fh))
- tasklet_schedule(&il->irq_tasklet);
-
-unplugged:
- spin_unlock_irqrestore(&il->lock, flags);
- return IRQ_HANDLED;
-
-none:
- /* re-enable interrupts here since we don't have anything to service. */
- /* only Re-enable if disabled by irq */
- if (test_bit(S_INT_ENABLED, &il->status))
- il_enable_interrupts(il);
- spin_unlock_irqrestore(&il->lock, flags);
- return IRQ_NONE;
-}
-EXPORT_SYMBOL(il_isr);
-
-/*
- * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this
- * function.
- */
-void
-il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info,
- __le16 fc, __le32 *tx_flags)
-{
- if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
- *tx_flags |= TX_CMD_FLG_RTS_MSK;
- *tx_flags &= ~TX_CMD_FLG_CTS_MSK;
- *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
-
- if (!ieee80211_is_mgmt(fc))
- return;
-
- switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
- case cpu_to_le16(IEEE80211_STYPE_AUTH):
- case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
- case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
- case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
- *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
- *tx_flags |= TX_CMD_FLG_CTS_MSK;
- break;
- }
- } else if (info->control.rates[0].
- flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
- *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
- *tx_flags |= TX_CMD_FLG_CTS_MSK;
- *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
- }
-}
-EXPORT_SYMBOL(il_tx_cmd_protection);
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
deleted file mode 100644
index 6e949df399d6..000000000000
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ /dev/null
@@ -1,161 +0,0 @@
-config IWLWIFI
- tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
- depends on PCI && MAC80211 && HAS_IOMEM
- select FW_LOADER
- ---help---
- Select to build the driver supporting the:
-
- Intel Wireless WiFi Link Next-Gen AGN
-
- This option enables support for use with the following hardware:
- Intel Wireless WiFi Link 6250AGN Adapter
- Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN)
- Intel WiFi Link 1000BGN
- Intel Wireless WiFi 5150AGN
- Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN
- Intel 6005 Series Wi-Fi Adapters
- Intel 6030 Series Wi-Fi Adapters
- Intel Wireless WiFi Link 6150BGN 2 Adapter
- Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN)
- Intel 2000 Series Wi-Fi Adapters
- Intel 7260 Wi-Fi Adapter
- Intel 3160 Wi-Fi Adapter
- Intel 7265 Wi-Fi Adapter
- Intel 8260 Wi-Fi Adapter
- Intel 3165 Wi-Fi Adapter
-
-
- This driver uses the kernel's mac80211 subsystem.
-
- In order to use this driver, you will need a firmware
- image for it. You can obtain the microcode from:
-
- <http://wireless.kernel.org/en/users/Drivers/iwlwifi>.
-
- The firmware is typically installed in /lib/firmware. You can
- look in the hotplug script /etc/hotplug/firmware.agent to
- determine which directory FIRMWARE_DIR is set to when the script
- runs.
-
- If you want to compile the driver as a module ( = code which can be
- inserted in and removed from the running kernel whenever you want),
- say M here and read <file:Documentation/kbuild/modules.txt>. The
- module will be called iwlwifi.
-
-if IWLWIFI
-
-config IWLWIFI_LEDS
- bool
- depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
- select LEDS_TRIGGERS
- select MAC80211_LEDS
- default y
-
-config IWLDVM
- tristate "Intel Wireless WiFi DVM Firmware support"
- default IWLWIFI
- help
- This is the driver that supports the DVM firmware. The list
- of the devices that use this firmware is available here:
- https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
-
-config IWLMVM
- tristate "Intel Wireless WiFi MVM Firmware support"
- select WANT_DEV_COREDUMP
- help
- This is the driver that supports the MVM firmware. The list
- of the devices that use this firmware is available here:
- https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
-
-# don't call it _MODULE -- will confuse Kconfig/fixdep/...
-config IWLWIFI_OPMODE_MODULAR
- bool
- default y if IWLDVM=m
- default y if IWLMVM=m
-
-comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
- depends on IWLDVM=n && IWLMVM=n
-
-config IWLWIFI_BCAST_FILTERING
- bool "Enable broadcast filtering"
- depends on IWLMVM
- help
- Say Y here to enable default bcast filtering configuration.
-
- Enabling broadcast filtering will drop any incoming wireless
- broadcast frames, except some very specific predefined
- patterns (e.g. incoming arp requests).
-
- If unsure, don't enable this option, as some programs might
- expect incoming broadcasts for their normal operations.
-
-config IWLWIFI_UAPSD
- bool "enable U-APSD by default"
- depends on IWLMVM
- help
- Say Y here to enable U-APSD by default. This may cause
- interoperability problems with some APs, manifesting in lower than
- expected throughput due to those APs not enabling aggregation
-
- If unsure, say N.
-
-menu "Debugging Options"
-
-config IWLWIFI_DEBUG
- bool "Enable full debugging output in the iwlwifi driver"
- ---help---
- This option will enable debug tracing output for the iwlwifi drivers
-
- This will result in the kernel module being ~100k larger. You can
- control which debug output is sent to the kernel log by setting the
- value in
-
- /sys/module/iwlwifi/parameters/debug
-
- This entry will only exist if this option is enabled.
-
- To set a value, simply echo an 8-byte hex value to the same file:
-
- % echo 0x43fff > /sys/module/iwlwifi/parameters/debug
-
- You can find the list of debug mask values in:
- drivers/net/wireless/iwlwifi/iwl-debug.h
-
- If this is your first time using this driver, you should say Y here
- as the debug information can assist others in helping you resolve
- any problems you may encounter.
-
-config IWLWIFI_DEBUGFS
- bool "iwlwifi debugfs support"
- depends on MAC80211_DEBUGFS
- ---help---
- Enable creation of debugfs files for the iwlwifi drivers. This
- is a low-impact option that allows getting insight into the
- driver's state at runtime.
-
-config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
- bool "Experimental uCode support"
- depends on IWLWIFI_DEBUG
- ---help---
- Enable use of experimental ucode for testing and debugging.
-
-config IWLWIFI_DEVICE_TRACING
- bool "iwlwifi device access tracing"
- depends on EVENT_TRACING
- default y
- help
- Say Y here to trace all commands, including TX frames and IO
- accesses, sent to the device. If you say yes, iwlwifi will
- register with the ftrace framework for event tracing and dump
- all this information to the ringbuffer, you may need to
- increase the ringbuffer size. See the ftrace documentation
- for more information.
-
- When tracing is not enabled, this option still has some
- (though rather small) overhead.
-
- If unsure, say Y so we can help you better when problems
- occur.
-endmenu
-
-endif
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
deleted file mode 100644
index dbfc5b18bcb7..000000000000
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-# common
-obj-$(CONFIG_IWLWIFI) += iwlwifi.o
-iwlwifi-objs += iwl-io.o
-iwlwifi-objs += iwl-drv.o
-iwlwifi-objs += iwl-debug.o
-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-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
-iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o
-iwlwifi-objs += iwl-trans.o
-
-iwlwifi-objs += $(iwlwifi-m)
-
-iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
-
-ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
-
-obj-$(CONFIG_IWLDVM) += dvm/
-obj-$(CONFIG_IWLMVM) += mvm/
-
-CFLAGS_iwl-devtrace.o := -I$(src)
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h
deleted file mode 100644
index 991def878881..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/agn.h
+++ /dev/null
@@ -1,485 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __iwl_agn_h__
-#define __iwl_agn_h__
-
-#include "iwl-config.h"
-
-#include "dev.h"
-
-/* The first 11 queues (0-10) are used otherwise */
-#define IWLAGN_FIRST_AMPDU_QUEUE 11
-
-/* AUX (TX during scan dwell) queue */
-#define IWL_AUX_QUEUE 10
-
-#define IWL_INVALID_STATION 255
-
-/* device operations */
-extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_105_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg;
-extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
-
-
-#define TIME_UNIT 1024
-
-/*****************************************************
-* DRIVER STATUS FUNCTIONS
-******************************************************/
-#define STATUS_RF_KILL_HW 0
-#define STATUS_CT_KILL 1
-#define STATUS_ALIVE 2
-#define STATUS_READY 3
-#define STATUS_EXIT_PENDING 5
-#define STATUS_STATISTICS 6
-#define STATUS_SCANNING 7
-#define STATUS_SCAN_ABORTING 8
-#define STATUS_SCAN_HW 9
-#define STATUS_FW_ERROR 10
-#define STATUS_CHANNEL_SWITCH_PENDING 11
-#define STATUS_SCAN_COMPLETE 12
-#define STATUS_POWER_PMI 13
-
-struct iwl_ucode_capabilities;
-
-extern const struct ieee80211_ops iwlagn_hw_ops;
-
-static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd)
-{
- hdr->op_code = cmd;
- hdr->first_group = 0;
- hdr->groups_num = 1;
- hdr->data_valid = 1;
-}
-
-void iwl_down(struct iwl_priv *priv);
-void iwl_cancel_deferred_work(struct iwl_priv *priv);
-void iwlagn_prepare_restart(struct iwl_priv *priv);
-void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb);
-
-bool iwl_check_for_ct_kill(struct iwl_priv *priv);
-
-void iwlagn_lift_passive_no_rx(struct iwl_priv *priv);
-
-/* MAC80211 */
-struct ieee80211_hw *iwl_alloc_all(void);
-int iwlagn_mac_setup_register(struct iwl_priv *priv,
- const struct iwl_ucode_capabilities *capa);
-void iwlagn_mac_unregister(struct iwl_priv *priv);
-
-/* commands */
-int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
-int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id,
- u32 flags, u16 len, const void *data);
-
-/* RXON */
-void iwl_connection_init_rx_config(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-int iwlagn_set_pan_params(struct iwl_priv *priv);
-int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed);
-void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *bss_conf,
- u32 changes);
-void iwlagn_config_ht40(struct ieee80211_conf *conf,
- struct iwl_rxon_context *ctx);
-void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf);
-void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch,
- struct iwl_rxon_context *ctx);
-void iwl_set_flags_for_band(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- enum ieee80211_band band,
- struct ieee80211_vif *vif);
-
-/* uCode */
-int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type);
-void iwl_send_prio_tbl(struct iwl_priv *priv);
-int iwl_init_alive_start(struct iwl_priv *priv);
-int iwl_run_init_ucode(struct iwl_priv *priv);
-int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
- enum iwl_ucode_type ucode_type);
-int iwl_send_calib_results(struct iwl_priv *priv);
-int iwl_calib_set(struct iwl_priv *priv,
- const struct iwl_calib_hdr *cmd, int len);
-void iwl_calib_free_results(struct iwl_priv *priv);
-int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
- char **buf);
-int iwlagn_hw_valid_rtc_data_addr(u32 addr);
-
-/* lib */
-int iwlagn_send_tx_power(struct iwl_priv *priv);
-void iwlagn_temperature(struct iwl_priv *priv);
-int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk);
-void iwlagn_dev_txfifo_flush(struct iwl_priv *priv);
-int iwlagn_send_beacon_cmd(struct iwl_priv *priv);
-int iwl_send_statistics_request(struct iwl_priv *priv,
- u8 flags, bool clear);
-
-static inline const struct ieee80211_supported_band *iwl_get_hw_mode(
- struct iwl_priv *priv, enum ieee80211_band band)
-{
- return priv->hw->wiphy->bands[band];
-}
-
-#ifdef CONFIG_PM_SLEEP
-int iwlagn_send_patterns(struct iwl_priv *priv,
- struct cfg80211_wowlan *wowlan);
-int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan);
-#endif
-
-/* rx */
-int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band);
-void iwl_setup_rx_handlers(struct iwl_priv *priv);
-void iwl_chswitch_done(struct iwl_priv *priv, bool is_success);
-
-
-/* tx */
-int iwlagn_tx_skb(struct iwl_priv *priv,
- struct ieee80211_sta *sta,
- struct sk_buff *skb);
-int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn);
-int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u8 buf_size);
-int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid);
-int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid);
-void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb);
-void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb);
-
-static inline u32 iwl_tx_status_to_mac80211(u32 status)
-{
- status &= TX_STATUS_MSK;
-
- switch (status) {
- case TX_STATUS_SUCCESS:
- case TX_STATUS_DIRECT_DONE:
- return IEEE80211_TX_STAT_ACK;
- case TX_STATUS_FAIL_DEST_PS:
- case TX_STATUS_FAIL_PASSIVE_NO_RX:
- return IEEE80211_TX_STAT_TX_FILTERED;
- default:
- return 0;
- }
-}
-
-static inline bool iwl_is_tx_success(u32 status)
-{
- status &= TX_STATUS_MSK;
- return (status == TX_STATUS_SUCCESS) ||
- (status == TX_STATUS_DIRECT_DONE);
-}
-
-u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid);
-
-/* scan */
-void iwlagn_post_scan(struct iwl_priv *priv);
-int iwl_force_rf_reset(struct iwl_priv *priv, bool external);
-void iwl_init_scan_params(struct iwl_priv *priv);
-int iwl_scan_cancel(struct iwl_priv *priv);
-void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
-void iwl_force_scan_end(struct iwl_priv *priv);
-void iwl_internal_short_hw_scan(struct iwl_priv *priv);
-void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
-void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
-void iwl_cancel_scan_deferred_work(struct iwl_priv *priv);
-int __must_check iwl_scan_initiate(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum iwl_scan_type scan_type,
- enum ieee80211_band band);
-
-/* For faster active scanning, scan will move to the next channel if fewer than
- * PLCP_QUIET_THRESH packets are heard on this channel within
- * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell
- * time if it's a quiet channel (nothing responded to our probe, and there's
- * no other traffic).
- * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */
-#define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */
-#define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */
-
-#define IWL_SCAN_CHECK_WATCHDOG (HZ * 15)
-
-
-/* bt coex */
-void iwlagn_send_advance_bt_config(struct iwl_priv *priv);
-void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv);
-void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv);
-void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv);
-void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv);
-void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena);
-
-static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv)
-{
- return priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist;
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-const char *iwl_get_tx_fail_reason(u32 status);
-const char *iwl_get_agg_tx_fail_reason(u16 status);
-#else
-static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; }
-static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; }
-#endif
-
-
-/* station management */
-int iwlagn_manage_ibss_station(struct iwl_priv *priv,
- struct ieee80211_vif *vif, bool add);
-#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
-#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
-#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of
- being activated */
-#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211;
- (this is for the IBSS BSSID stations) */
-#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */
-
-
-void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-void iwl_clear_ucode_stations(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-void iwl_dealloc_bcast_stations(struct iwl_priv *priv);
-int iwl_get_free_ucode_key_offset(struct iwl_priv *priv);
-int iwl_send_add_sta(struct iwl_priv *priv,
- struct iwl_addsta_cmd *sta, u8 flags);
-int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap,
- struct ieee80211_sta *sta, u8 *sta_id_r);
-int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
- const u8 *addr);
-void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id,
- const u8 *addr);
-u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap, struct ieee80211_sta *sta);
-
-int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct iwl_link_quality_cmd *lq, u8 flags, bool init);
-void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb);
-int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct ieee80211_sta *sta);
-
-bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_sta *sta);
-
-static inline int iwl_sta_id(struct ieee80211_sta *sta)
-{
- if (WARN_ON(!sta))
- return IWL_INVALID_STATION;
-
- return ((struct iwl_station_priv *)sta->drv_priv)->sta_id;
-}
-
-int iwlagn_alloc_bcast_station(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, u8 *sta_id_r);
-int iwl_remove_default_wep_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *key);
-int iwl_set_default_wep_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *key);
-int iwl_restore_default_wep_keys(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *key,
- struct ieee80211_sta *sta);
-int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *key,
- struct ieee80211_sta *sta);
-void iwl_update_tkip_key(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta, u32 iv32, u16 *phase1key);
-int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
-int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
- int tid, u16 ssn);
-int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
- int tid);
-void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt);
-int iwl_update_bcast_station(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx);
-int iwl_update_bcast_stations(struct iwl_priv *priv);
-
-/* rate */
-static inline u32 iwl_ant_idx_to_flags(u8 ant_idx)
-{
- return BIT(ant_idx) << RATE_MCS_ANT_POS;
-}
-
-static inline u8 iwl_hw_get_rate(__le32 rate_n_flags)
-{
- return le32_to_cpu(rate_n_flags) & RATE_MCS_RATE_MSK;
-}
-
-static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
-{
- return cpu_to_le32(flags|(u32)rate);
-}
-
-int iwl_alive_start(struct iwl_priv *priv);
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-void iwl_print_rx_config_cmd(struct iwl_priv *priv,
- enum iwl_rxon_context_id ctxid);
-#else
-static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv,
- enum iwl_rxon_context_id ctxid)
-{
-}
-#endif
-
-/* status checks */
-
-static inline int iwl_is_ready(struct iwl_priv *priv)
-{
- /* The adapter is 'ready' if READY EXIT_PENDING is not set */
- return test_bit(STATUS_READY, &priv->status) &&
- !test_bit(STATUS_EXIT_PENDING, &priv->status);
-}
-
-static inline int iwl_is_alive(struct iwl_priv *priv)
-{
- return test_bit(STATUS_ALIVE, &priv->status);
-}
-
-static inline int iwl_is_rfkill(struct iwl_priv *priv)
-{
- return test_bit(STATUS_RF_KILL_HW, &priv->status);
-}
-
-static inline int iwl_is_ctkill(struct iwl_priv *priv)
-{
- return test_bit(STATUS_CT_KILL, &priv->status);
-}
-
-static inline int iwl_is_ready_rf(struct iwl_priv *priv)
-{
- if (iwl_is_rfkill(priv))
- return 0;
-
- return iwl_is_ready(priv);
-}
-
-static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state)
-{
- if (state)
- set_bit(STATUS_POWER_PMI, &priv->status);
- else
- clear_bit(STATUS_POWER_PMI, &priv->status);
- iwl_trans_set_pmi(priv->trans, state);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir);
-#else
-static inline int iwl_dbgfs_register(struct iwl_priv *priv,
- struct dentry *dbgfs_dir)
-{
- return 0;
-}
-#endif /* CONFIG_IWLWIFI_DEBUGFS */
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \
-do { \
- if (!iwl_is_rfkill((m))) \
- IWL_ERR(m, fmt, ##args); \
- else \
- __iwl_err((m)->dev, true, \
- !iwl_have_debug_level(IWL_DL_RADIO), \
- fmt, ##args); \
-} while (0)
-#else
-#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \
-do { \
- if (!iwl_is_rfkill((m))) \
- IWL_ERR(m, fmt, ##args); \
- else \
- __iwl_err((m)->dev, true, true, fmt, ##args); \
-} while (0)
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-extern const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1];
-
-static inline const char *iwl_dvm_get_cmd_string(u8 cmd)
-{
- const char *s = iwl_dvm_cmd_strings[cmd];
- if (s)
- return s;
- return "UNKNOWN";
-}
-#endif /* __iwl_agn_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c
deleted file mode 100644
index 20e6aa910700..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/calib.c
+++ /dev/null
@@ -1,1113 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#include <linux/slab.h>
-#include <net/mac80211.h>
-
-#include "iwl-trans.h"
-
-#include "dev.h"
-#include "calib.h"
-#include "agn.h"
-
-/*****************************************************************************
- * INIT calibrations framework
- *****************************************************************************/
-
-/* Opaque calibration results */
-struct iwl_calib_result {
- struct list_head list;
- size_t cmd_len;
- struct iwl_calib_hdr hdr;
- /* data follows */
-};
-
-struct statistics_general_data {
- u32 beacon_silence_rssi_a;
- u32 beacon_silence_rssi_b;
- u32 beacon_silence_rssi_c;
- u32 beacon_energy_a;
- u32 beacon_energy_b;
- u32 beacon_energy_c;
-};
-
-int iwl_send_calib_results(struct iwl_priv *priv)
-{
- struct iwl_host_cmd hcmd = {
- .id = REPLY_PHY_CALIBRATION_CMD,
- };
- struct iwl_calib_result *res;
-
- list_for_each_entry(res, &priv->calib_results, list) {
- int ret;
-
- hcmd.len[0] = res->cmd_len;
- hcmd.data[0] = &res->hdr;
- hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
- ret = iwl_dvm_send_cmd(priv, &hcmd);
- if (ret) {
- IWL_ERR(priv, "Error %d on calib cmd %d\n",
- ret, res->hdr.op_code);
- return ret;
- }
- }
-
- return 0;
-}
-
-int iwl_calib_set(struct iwl_priv *priv,
- const struct iwl_calib_hdr *cmd, int len)
-{
- struct iwl_calib_result *res, *tmp;
-
- res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr),
- GFP_ATOMIC);
- if (!res)
- return -ENOMEM;
- memcpy(&res->hdr, cmd, len);
- res->cmd_len = len;
-
- list_for_each_entry(tmp, &priv->calib_results, list) {
- if (tmp->hdr.op_code == res->hdr.op_code) {
- list_replace(&tmp->list, &res->list);
- kfree(tmp);
- return 0;
- }
- }
-
- /* wasn't in list already */
- list_add_tail(&res->list, &priv->calib_results);
-
- return 0;
-}
-
-void iwl_calib_free_results(struct iwl_priv *priv)
-{
- struct iwl_calib_result *res, *tmp;
-
- list_for_each_entry_safe(res, tmp, &priv->calib_results, list) {
- list_del(&res->list);
- kfree(res);
- }
-}
-
-/*****************************************************************************
- * RUNTIME calibrations framework
- *****************************************************************************/
-
-/* "false alarms" are signals that our DSP tries to lock onto,
- * but then determines that they are either noise, or transmissions
- * from a distant wireless network (also "noise", really) that get
- * "stepped on" by stronger transmissions within our own network.
- * This algorithm attempts to set a sensitivity level that is high
- * enough to receive all of our own network traffic, but not so
- * high that our DSP gets too busy trying to lock onto non-network
- * activity/noise. */
-static int iwl_sens_energy_cck(struct iwl_priv *priv,
- u32 norm_fa,
- u32 rx_enable_time,
- struct statistics_general_data *rx_info)
-{
- u32 max_nrg_cck = 0;
- int i = 0;
- u8 max_silence_rssi = 0;
- u32 silence_ref = 0;
- u8 silence_rssi_a = 0;
- u8 silence_rssi_b = 0;
- u8 silence_rssi_c = 0;
- u32 val;
-
- /* "false_alarms" values below are cross-multiplications to assess the
- * numbers of false alarms within the measured period of actual Rx
- * (Rx is off when we're txing), vs the min/max expected false alarms
- * (some should be expected if rx is sensitive enough) in a
- * hypothetical listening period of 200 time units (TU), 204.8 msec:
- *
- * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
- *
- * */
- u32 false_alarms = norm_fa * 200 * 1024;
- u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
- u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
- struct iwl_sensitivity_data *data = NULL;
- const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
-
- data = &(priv->sensitivity_data);
-
- data->nrg_auto_corr_silence_diff = 0;
-
- /* Find max silence rssi among all 3 receivers.
- * This is background noise, which may include transmissions from other
- * networks, measured during silence before our network's beacon */
- silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
- ALL_BAND_FILTER) >> 8);
- silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
- ALL_BAND_FILTER) >> 8);
- silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
- ALL_BAND_FILTER) >> 8);
-
- val = max(silence_rssi_b, silence_rssi_c);
- max_silence_rssi = max(silence_rssi_a, (u8) val);
-
- /* Store silence rssi in 20-beacon history table */
- data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
- data->nrg_silence_idx++;
- if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
- data->nrg_silence_idx = 0;
-
- /* Find max silence rssi across 20 beacon history */
- for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
- val = data->nrg_silence_rssi[i];
- silence_ref = max(silence_ref, val);
- }
- IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n",
- silence_rssi_a, silence_rssi_b, silence_rssi_c,
- silence_ref);
-
- /* Find max rx energy (min value!) among all 3 receivers,
- * measured during beacon frame.
- * Save it in 10-beacon history table. */
- i = data->nrg_energy_idx;
- val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
- data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
-
- data->nrg_energy_idx++;
- if (data->nrg_energy_idx >= 10)
- data->nrg_energy_idx = 0;
-
- /* Find min rx energy (max value) across 10 beacon history.
- * This is the minimum signal level that we want to receive well.
- * Add backoff (margin so we don't miss slightly lower energy frames).
- * This establishes an upper bound (min value) for energy threshold. */
- max_nrg_cck = data->nrg_value[0];
- for (i = 1; i < 10; i++)
- max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
- max_nrg_cck += 6;
-
- IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
- rx_info->beacon_energy_a, rx_info->beacon_energy_b,
- rx_info->beacon_energy_c, max_nrg_cck - 6);
-
- /* Count number of consecutive beacons with fewer-than-desired
- * false alarms. */
- if (false_alarms < min_false_alarms)
- data->num_in_cck_no_fa++;
- else
- data->num_in_cck_no_fa = 0;
- IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n",
- data->num_in_cck_no_fa);
-
- /* If we got too many false alarms this time, reduce sensitivity */
- if ((false_alarms > max_false_alarms) &&
- (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) {
- IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n",
- false_alarms, max_false_alarms);
- IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n");
- data->nrg_curr_state = IWL_FA_TOO_MANY;
- /* Store for "fewer than desired" on later beacon */
- data->nrg_silence_ref = silence_ref;
-
- /* increase energy threshold (reduce nrg value)
- * to decrease sensitivity */
- data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK;
- /* Else if we got fewer than desired, increase sensitivity */
- } else if (false_alarms < min_false_alarms) {
- data->nrg_curr_state = IWL_FA_TOO_FEW;
-
- /* Compare silence level with silence level for most recent
- * healthy number or too many false alarms */
- data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
- (s32)silence_ref;
-
- IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n",
- false_alarms, min_false_alarms,
- data->nrg_auto_corr_silence_diff);
-
- /* Increase value to increase sensitivity, but only if:
- * 1a) previous beacon did *not* have *too many* false alarms
- * 1b) AND there's a significant difference in Rx levels
- * from a previous beacon with too many, or healthy # FAs
- * OR 2) We've seen a lot of beacons (100) with too few
- * false alarms */
- if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
- ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
- (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
-
- IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n");
- /* Increase nrg value to increase sensitivity */
- val = data->nrg_th_cck + NRG_STEP_CCK;
- data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val);
- } else {
- IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n");
- }
-
- /* Else we got a healthy number of false alarms, keep status quo */
- } else {
- IWL_DEBUG_CALIB(priv, " FA in safe zone\n");
- data->nrg_curr_state = IWL_FA_GOOD_RANGE;
-
- /* Store for use in "fewer than desired" with later beacon */
- data->nrg_silence_ref = silence_ref;
-
- /* If previous beacon had too many false alarms,
- * give it some extra margin by reducing sensitivity again
- * (but don't go below measured energy of desired Rx) */
- if (IWL_FA_TOO_MANY == data->nrg_prev_state) {
- IWL_DEBUG_CALIB(priv, "... increasing margin\n");
- if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
- data->nrg_th_cck -= NRG_MARGIN;
- else
- data->nrg_th_cck = max_nrg_cck;
- }
- }
-
- /* Make sure the energy threshold does not go above the measured
- * energy of the desired Rx signals (reduced by backoff margin),
- * or else we might start missing Rx frames.
- * Lower value is higher energy, so we use max()!
- */
- data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
- IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck);
-
- data->nrg_prev_state = data->nrg_curr_state;
-
- /* Auto-correlation CCK algorithm */
- if (false_alarms > min_false_alarms) {
-
- /* increase auto_corr values to decrease sensitivity
- * so the DSP won't be disturbed by the noise
- */
- if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
- data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
- else {
- val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
- data->auto_corr_cck =
- min((u32)ranges->auto_corr_max_cck, val);
- }
- val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
- data->auto_corr_cck_mrc =
- min((u32)ranges->auto_corr_max_cck_mrc, val);
- } else if ((false_alarms < min_false_alarms) &&
- ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
- (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
-
- /* Decrease auto_corr values to increase sensitivity */
- val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
- data->auto_corr_cck =
- max((u32)ranges->auto_corr_min_cck, val);
- val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
- data->auto_corr_cck_mrc =
- max((u32)ranges->auto_corr_min_cck_mrc, val);
- }
-
- return 0;
-}
-
-
-static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
- u32 norm_fa,
- u32 rx_enable_time)
-{
- u32 val;
- u32 false_alarms = norm_fa * 200 * 1024;
- u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
- u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
- struct iwl_sensitivity_data *data = NULL;
- const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
-
- data = &(priv->sensitivity_data);
-
- /* If we got too many false alarms this time, reduce sensitivity */
- if (false_alarms > max_false_alarms) {
-
- IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n",
- false_alarms, max_false_alarms);
-
- val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm =
- min((u32)ranges->auto_corr_max_ofdm, val);
-
- val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm_mrc =
- min((u32)ranges->auto_corr_max_ofdm_mrc, val);
-
- val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm_x1 =
- min((u32)ranges->auto_corr_max_ofdm_x1, val);
-
- val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm_mrc_x1 =
- min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val);
- }
-
- /* Else if we got fewer than desired, increase sensitivity */
- else if (false_alarms < min_false_alarms) {
-
- IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n",
- false_alarms, min_false_alarms);
-
- val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm =
- max((u32)ranges->auto_corr_min_ofdm, val);
-
- val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm_mrc =
- max((u32)ranges->auto_corr_min_ofdm_mrc, val);
-
- val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm_x1 =
- max((u32)ranges->auto_corr_min_ofdm_x1, val);
-
- val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
- data->auto_corr_ofdm_mrc_x1 =
- max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val);
- } else {
- IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n",
- min_false_alarms, false_alarms, max_false_alarms);
- }
- return 0;
-}
-
-static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv,
- struct iwl_sensitivity_data *data,
- __le16 *tbl)
-{
- tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
- cpu_to_le16((u16)data->auto_corr_ofdm);
- tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
- cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
- tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
- cpu_to_le16((u16)data->auto_corr_ofdm_x1);
- tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
- cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
-
- tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
- cpu_to_le16((u16)data->auto_corr_cck);
- tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
- cpu_to_le16((u16)data->auto_corr_cck_mrc);
-
- tbl[HD_MIN_ENERGY_CCK_DET_INDEX] =
- cpu_to_le16((u16)data->nrg_th_cck);
- tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] =
- cpu_to_le16((u16)data->nrg_th_ofdm);
-
- tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
- cpu_to_le16(data->barker_corr_th_min);
- tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
- cpu_to_le16(data->barker_corr_th_min_mrc);
- tbl[HD_OFDM_ENERGY_TH_IN_INDEX] =
- cpu_to_le16(data->nrg_th_cca);
-
- IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
- data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
- data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
- data->nrg_th_ofdm);
-
- IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n",
- data->auto_corr_cck, data->auto_corr_cck_mrc,
- data->nrg_th_cck);
-}
-
-/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
-static int iwl_sensitivity_write(struct iwl_priv *priv)
-{
- struct iwl_sensitivity_cmd cmd;
- struct iwl_sensitivity_data *data = NULL;
- struct iwl_host_cmd cmd_out = {
- .id = SENSITIVITY_CMD,
- .len = { sizeof(struct iwl_sensitivity_cmd), },
- .flags = CMD_ASYNC,
- .data = { &cmd, },
- };
-
- data = &(priv->sensitivity_data);
-
- memset(&cmd, 0, sizeof(cmd));
-
- iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]);
-
- /* Update uCode's "work" table, and copy it to DSP */
- cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
-
- /* Don't send command to uCode if nothing has changed */
- if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
- sizeof(u16)*HD_TABLE_SIZE)) {
- IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
- return 0;
- }
-
- /* Copy table for comparison next time */
- memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
- sizeof(u16)*HD_TABLE_SIZE);
-
- return iwl_dvm_send_cmd(priv, &cmd_out);
-}
-
-/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
-static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
-{
- struct iwl_enhance_sensitivity_cmd cmd;
- struct iwl_sensitivity_data *data = NULL;
- struct iwl_host_cmd cmd_out = {
- .id = SENSITIVITY_CMD,
- .len = { sizeof(struct iwl_enhance_sensitivity_cmd), },
- .flags = CMD_ASYNC,
- .data = { &cmd, },
- };
-
- data = &(priv->sensitivity_data);
-
- memset(&cmd, 0, sizeof(cmd));
-
- iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
-
- if (priv->lib->hd_v2) {
- cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
- HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
- cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
- HD_INA_NON_SQUARE_DET_CCK_DATA_V2;
- cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
- HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
- HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
- HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
- HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
- HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
- HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
- HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
- HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
- HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2;
- } else {
- cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
- HD_INA_NON_SQUARE_DET_OFDM_DATA_V1;
- cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
- HD_INA_NON_SQUARE_DET_CCK_DATA_V1;
- cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
- HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
- HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
- HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
- HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1;
- cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
- HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
- HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
- HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
- HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1;
- cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
- HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1;
- }
-
- /* Update uCode's "work" table, and copy it to DSP */
- cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
-
- /* Don't send command to uCode if nothing has changed */
- if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]),
- sizeof(u16)*HD_TABLE_SIZE) &&
- !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX],
- &(priv->enhance_sensitivity_tbl[0]),
- sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) {
- IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
- return 0;
- }
-
- /* Copy table for comparison next time */
- memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]),
- sizeof(u16)*HD_TABLE_SIZE);
- memcpy(&(priv->enhance_sensitivity_tbl[0]),
- &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]),
- sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES);
-
- return iwl_dvm_send_cmd(priv, &cmd_out);
-}
-
-void iwl_init_sensitivity(struct iwl_priv *priv)
-{
- int ret = 0;
- int i;
- struct iwl_sensitivity_data *data = NULL;
- const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
-
- if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
- return;
-
- IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n");
-
- /* Clear driver's sensitivity algo data */
- data = &(priv->sensitivity_data);
-
- if (ranges == NULL)
- return;
-
- memset(data, 0, sizeof(struct iwl_sensitivity_data));
-
- data->num_in_cck_no_fa = 0;
- data->nrg_curr_state = IWL_FA_TOO_MANY;
- data->nrg_prev_state = IWL_FA_TOO_MANY;
- data->nrg_silence_ref = 0;
- data->nrg_silence_idx = 0;
- data->nrg_energy_idx = 0;
-
- for (i = 0; i < 10; i++)
- data->nrg_value[i] = 0;
-
- for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
- data->nrg_silence_rssi[i] = 0;
-
- data->auto_corr_ofdm = ranges->auto_corr_min_ofdm;
- data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
- data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1;
- data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
- data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
- data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
- data->nrg_th_cck = ranges->nrg_th_cck;
- data->nrg_th_ofdm = ranges->nrg_th_ofdm;
- data->barker_corr_th_min = ranges->barker_corr_th_min;
- data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
- data->nrg_th_cca = ranges->nrg_th_cca;
-
- data->last_bad_plcp_cnt_ofdm = 0;
- data->last_fa_cnt_ofdm = 0;
- data->last_bad_plcp_cnt_cck = 0;
- data->last_fa_cnt_cck = 0;
-
- if (priv->fw->enhance_sensitivity_table)
- ret |= iwl_enhance_sensitivity_write(priv);
- else
- ret |= iwl_sensitivity_write(priv);
- IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret);
-}
-
-void iwl_sensitivity_calibration(struct iwl_priv *priv)
-{
- u32 rx_enable_time;
- u32 fa_cck;
- u32 fa_ofdm;
- u32 bad_plcp_cck;
- u32 bad_plcp_ofdm;
- u32 norm_fa_ofdm;
- u32 norm_fa_cck;
- struct iwl_sensitivity_data *data = NULL;
- struct statistics_rx_non_phy *rx_info;
- struct statistics_rx_phy *ofdm, *cck;
- struct statistics_general_data statis;
-
- if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
- return;
-
- data = &(priv->sensitivity_data);
-
- if (!iwl_is_any_associated(priv)) {
- IWL_DEBUG_CALIB(priv, "<< - not associated\n");
- return;
- }
-
- spin_lock_bh(&priv->statistics.lock);
- rx_info = &priv->statistics.rx_non_phy;
- ofdm = &priv->statistics.rx_ofdm;
- cck = &priv->statistics.rx_cck;
- if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
- IWL_DEBUG_CALIB(priv, "<< invalid data.\n");
- spin_unlock_bh(&priv->statistics.lock);
- return;
- }
-
- /* Extract Statistics: */
- rx_enable_time = le32_to_cpu(rx_info->channel_load);
- fa_cck = le32_to_cpu(cck->false_alarm_cnt);
- fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
- bad_plcp_cck = le32_to_cpu(cck->plcp_err);
- bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);
-
- statis.beacon_silence_rssi_a =
- le32_to_cpu(rx_info->beacon_silence_rssi_a);
- statis.beacon_silence_rssi_b =
- le32_to_cpu(rx_info->beacon_silence_rssi_b);
- statis.beacon_silence_rssi_c =
- le32_to_cpu(rx_info->beacon_silence_rssi_c);
- statis.beacon_energy_a =
- le32_to_cpu(rx_info->beacon_energy_a);
- statis.beacon_energy_b =
- le32_to_cpu(rx_info->beacon_energy_b);
- statis.beacon_energy_c =
- le32_to_cpu(rx_info->beacon_energy_c);
-
- spin_unlock_bh(&priv->statistics.lock);
-
- IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time);
-
- if (!rx_enable_time) {
- IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n");
- return;
- }
-
- /* These statistics increase monotonically, and do not reset
- * at each beacon. Calculate difference from last value, or just
- * use the new statistics value if it has reset or wrapped around. */
- if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
- data->last_bad_plcp_cnt_cck = bad_plcp_cck;
- else {
- bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
- data->last_bad_plcp_cnt_cck += bad_plcp_cck;
- }
-
- if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
- data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
- else {
- bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
- data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
- }
-
- if (data->last_fa_cnt_ofdm > fa_ofdm)
- data->last_fa_cnt_ofdm = fa_ofdm;
- else {
- fa_ofdm -= data->last_fa_cnt_ofdm;
- data->last_fa_cnt_ofdm += fa_ofdm;
- }
-
- if (data->last_fa_cnt_cck > fa_cck)
- data->last_fa_cnt_cck = fa_cck;
- else {
- fa_cck -= data->last_fa_cnt_cck;
- data->last_fa_cnt_cck += fa_cck;
- }
-
- /* Total aborted signal locks */
- norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
- norm_fa_cck = fa_cck + bad_plcp_cck;
-
- IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck,
- bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
-
- iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
- iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
- if (priv->fw->enhance_sensitivity_table)
- iwl_enhance_sensitivity_write(priv);
- else
- iwl_sensitivity_write(priv);
-}
-
-static inline u8 find_first_chain(u8 mask)
-{
- if (mask & ANT_A)
- return CHAIN_A;
- if (mask & ANT_B)
- return CHAIN_B;
- return CHAIN_C;
-}
-
-/**
- * Run disconnected antenna algorithm to find out which antennas are
- * disconnected.
- */
-static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
- struct iwl_chain_noise_data *data)
-{
- u32 active_chains = 0;
- u32 max_average_sig;
- u16 max_average_sig_antenna_i;
- u8 num_tx_chains;
- u8 first_chain;
- u16 i = 0;
-
- average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS;
- average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS;
- average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS;
-
- if (average_sig[0] >= average_sig[1]) {
- max_average_sig = average_sig[0];
- max_average_sig_antenna_i = 0;
- active_chains = (1 << max_average_sig_antenna_i);
- } else {
- max_average_sig = average_sig[1];
- max_average_sig_antenna_i = 1;
- active_chains = (1 << max_average_sig_antenna_i);
- }
-
- if (average_sig[2] >= max_average_sig) {
- max_average_sig = average_sig[2];
- max_average_sig_antenna_i = 2;
- active_chains = (1 << max_average_sig_antenna_i);
- }
-
- IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n",
- average_sig[0], average_sig[1], average_sig[2]);
- IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n",
- max_average_sig, max_average_sig_antenna_i);
-
- /* Compare signal strengths for all 3 receivers. */
- for (i = 0; i < NUM_RX_CHAINS; i++) {
- if (i != max_average_sig_antenna_i) {
- s32 rssi_delta = (max_average_sig - average_sig[i]);
-
- /* If signal is very weak, compared with
- * strongest, mark it as disconnected. */
- if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
- data->disconn_array[i] = 1;
- else
- active_chains |= (1 << i);
- IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d "
- "disconn_array[i] = %d\n",
- i, rssi_delta, data->disconn_array[i]);
- }
- }
-
- /*
- * The above algorithm sometimes fails when the ucode
- * reports 0 for all chains. It's not clear why that
- * happens to start with, but it is then causing trouble
- * because this can make us enable more chains than the
- * hardware really has.
- *
- * To be safe, simply mask out any chains that we know
- * are not on the device.
- */
- active_chains &= priv->nvm_data->valid_rx_ant;
-
- num_tx_chains = 0;
- for (i = 0; i < NUM_RX_CHAINS; i++) {
- /* loops on all the bits of
- * priv->hw_setting.valid_tx_ant */
- u8 ant_msk = (1 << i);
- if (!(priv->nvm_data->valid_tx_ant & ant_msk))
- continue;
-
- num_tx_chains++;
- if (data->disconn_array[i] == 0)
- /* there is a Tx antenna connected */
- break;
- if (num_tx_chains == priv->hw_params.tx_chains_num &&
- data->disconn_array[i]) {
- /*
- * If all chains are disconnected
- * connect the first valid tx chain
- */
- first_chain =
- find_first_chain(priv->nvm_data->valid_tx_ant);
- data->disconn_array[first_chain] = 0;
- active_chains |= BIT(first_chain);
- IWL_DEBUG_CALIB(priv,
- "All Tx chains are disconnected W/A - declare %d as connected\n",
- first_chain);
- break;
- }
- }
-
- if (active_chains != priv->nvm_data->valid_rx_ant &&
- active_chains != priv->chain_noise_data.active_chains)
- IWL_DEBUG_CALIB(priv,
- "Detected that not all antennas are connected! "
- "Connected: %#x, valid: %#x.\n",
- active_chains,
- priv->nvm_data->valid_rx_ant);
-
- /* Save for use within RXON, TX, SCAN commands, etc. */
- data->active_chains = active_chains;
- IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n",
- active_chains);
-}
-
-static void iwlagn_gain_computation(struct iwl_priv *priv,
- u32 average_noise[NUM_RX_CHAINS],
- u8 default_chain)
-{
- int i;
- s32 delta_g;
- struct iwl_chain_noise_data *data = &priv->chain_noise_data;
-
- /*
- * Find Gain Code for the chains based on "default chain"
- */
- for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
- if ((data->disconn_array[i])) {
- data->delta_gain_code[i] = 0;
- continue;
- }
-
- delta_g = (priv->lib->chain_noise_scale *
- ((s32)average_noise[default_chain] -
- (s32)average_noise[i])) / 1500;
-
- /* bound gain by 2 bits value max, 3rd bit is sign */
- data->delta_gain_code[i] =
- min(abs(delta_g),
- (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
-
- if (delta_g < 0)
- /*
- * set negative sign ...
- * note to Intel developers: This is uCode API format,
- * not the format of any internal device registers.
- * Do not change this format for e.g. 6050 or similar
- * devices. Change format only if more resolution
- * (i.e. more than 2 bits magnitude) is needed.
- */
- data->delta_gain_code[i] |= (1 << 2);
- }
-
- IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n",
- data->delta_gain_code[1], data->delta_gain_code[2]);
-
- if (!data->radio_write) {
- struct iwl_calib_chain_noise_gain_cmd cmd;
-
- memset(&cmd, 0, sizeof(cmd));
-
- iwl_set_calib_hdr(&cmd.hdr,
- priv->phy_calib_chain_noise_gain_cmd);
- cmd.delta_gain_1 = data->delta_gain_code[1];
- cmd.delta_gain_2 = data->delta_gain_code[2];
- iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
- CMD_ASYNC, sizeof(cmd), &cmd);
-
- data->radio_write = 1;
- data->state = IWL_CHAIN_NOISE_CALIBRATED;
- }
-}
-
-/*
- * Accumulate 16 beacons of signal and noise statistics for each of
- * 3 receivers/antennas/rx-chains, then figure out:
- * 1) Which antennas are connected.
- * 2) Differential rx gain settings to balance the 3 receivers.
- */
-void iwl_chain_noise_calibration(struct iwl_priv *priv)
-{
- struct iwl_chain_noise_data *data = NULL;
-
- u32 chain_noise_a;
- u32 chain_noise_b;
- u32 chain_noise_c;
- u32 chain_sig_a;
- u32 chain_sig_b;
- u32 chain_sig_c;
- u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
- u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
- u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
- u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
- u16 i = 0;
- u16 rxon_chnum = INITIALIZATION_VALUE;
- u16 stat_chnum = INITIALIZATION_VALUE;
- u8 rxon_band24;
- u8 stat_band24;
- struct statistics_rx_non_phy *rx_info;
-
- /*
- * MULTI-FIXME:
- * When we support multiple interfaces on different channels,
- * this must be modified/fixed.
- */
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
-
- if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
- return;
-
- data = &(priv->chain_noise_data);
-
- /*
- * Accumulate just the first "chain_noise_num_beacons" after
- * the first association, then we're done forever.
- */
- if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
- if (data->state == IWL_CHAIN_NOISE_ALIVE)
- IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n");
- return;
- }
-
- spin_lock_bh(&priv->statistics.lock);
-
- rx_info = &priv->statistics.rx_non_phy;
-
- if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
- IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n");
- spin_unlock_bh(&priv->statistics.lock);
- return;
- }
-
- rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK);
- rxon_chnum = le16_to_cpu(ctx->staging.channel);
- stat_band24 =
- !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK);
- stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16;
-
- /* Make sure we accumulate data for just the associated channel
- * (even if scanning). */
- if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) {
- IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n",
- rxon_chnum, rxon_band24);
- spin_unlock_bh(&priv->statistics.lock);
- return;
- }
-
- /*
- * Accumulate beacon statistics values across
- * "chain_noise_num_beacons"
- */
- chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
- IN_BAND_FILTER;
- chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
- IN_BAND_FILTER;
- chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
- IN_BAND_FILTER;
-
- chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
- chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
- chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
-
- spin_unlock_bh(&priv->statistics.lock);
-
- data->beacon_count++;
-
- data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
- data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
- data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
-
- data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
- data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
- data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
-
- IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n",
- rxon_chnum, rxon_band24, data->beacon_count);
- IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n",
- chain_sig_a, chain_sig_b, chain_sig_c);
- IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n",
- chain_noise_a, chain_noise_b, chain_noise_c);
-
- /* If this is the "chain_noise_num_beacons", determine:
- * 1) Disconnected antennas (using signal strengths)
- * 2) Differential gain (using silence noise) to balance receivers */
- if (data->beacon_count != IWL_CAL_NUM_BEACONS)
- return;
-
- /* Analyze signal for disconnected antenna */
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist) {
- /* Disable disconnected antenna algorithm for advanced
- bt coex, assuming valid antennas are connected */
- data->active_chains = priv->nvm_data->valid_rx_ant;
- for (i = 0; i < NUM_RX_CHAINS; i++)
- if (!(data->active_chains & (1<<i)))
- data->disconn_array[i] = 1;
- } else
- iwl_find_disconn_antenna(priv, average_sig, data);
-
- /* Analyze noise for rx balance */
- average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS;
- average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS;
- average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS;
-
- for (i = 0; i < NUM_RX_CHAINS; i++) {
- if (!(data->disconn_array[i]) &&
- (average_noise[i] <= min_average_noise)) {
- /* This means that chain i is active and has
- * lower noise values so far: */
- min_average_noise = average_noise[i];
- min_average_noise_antenna_i = i;
- }
- }
-
- IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n",
- average_noise[0], average_noise[1],
- average_noise[2]);
-
- IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n",
- min_average_noise, min_average_noise_antenna_i);
-
- iwlagn_gain_computation(
- priv, average_noise,
- find_first_chain(priv->nvm_data->valid_rx_ant));
-
- /* Some power changes may have been made during the calibration.
- * Update and commit the RXON
- */
- iwl_update_chain_flags(priv);
-
- data->state = IWL_CHAIN_NOISE_DONE;
- iwl_power_update_mode(priv, false);
-}
-
-void iwl_reset_run_time_calib(struct iwl_priv *priv)
-{
- int i;
- memset(&(priv->sensitivity_data), 0,
- sizeof(struct iwl_sensitivity_data));
- memset(&(priv->chain_noise_data), 0,
- sizeof(struct iwl_chain_noise_data));
- for (i = 0; i < NUM_RX_CHAINS; i++)
- priv->chain_noise_data.delta_gain_code[i] =
- CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
-
- /* Ask for statistics now, the uCode will send notification
- * periodically after association */
- iwl_send_statistics_request(priv, CMD_ASYNC, true);
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.h b/drivers/net/wireless/iwlwifi/dvm/calib.h
deleted file mode 100644
index aeae4e80ea40..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/calib.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-#ifndef __iwl_calib_h__
-#define __iwl_calib_h__
-
-#include "dev.h"
-#include "commands.h"
-
-void iwl_chain_noise_calibration(struct iwl_priv *priv);
-void iwl_sensitivity_calibration(struct iwl_priv *priv);
-
-void iwl_init_sensitivity(struct iwl_priv *priv);
-void iwl_reset_run_time_calib(struct iwl_priv *priv);
-
-#endif /* __iwl_calib_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h
deleted file mode 100644
index 7a34e4d158d1..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ /dev/null
@@ -1,4008 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-/*
- * Please use this file (commands.h) only for uCode API definitions.
- * Please use iwl-xxxx-hw.h for hardware-related definitions.
- * Please use dev.h for driver implementation definitions.
- */
-
-#ifndef __iwl_commands_h__
-#define __iwl_commands_h__
-
-#include <linux/ieee80211.h>
-#include <linux/types.h>
-
-
-enum {
- REPLY_ALIVE = 0x1,
- REPLY_ERROR = 0x2,
- REPLY_ECHO = 0x3, /* test command */
-
- /* RXON and QOS commands */
- REPLY_RXON = 0x10,
- REPLY_RXON_ASSOC = 0x11,
- REPLY_QOS_PARAM = 0x13,
- REPLY_RXON_TIMING = 0x14,
-
- /* Multi-Station support */
- REPLY_ADD_STA = 0x18,
- REPLY_REMOVE_STA = 0x19,
- REPLY_REMOVE_ALL_STA = 0x1a, /* not used */
- REPLY_TXFIFO_FLUSH = 0x1e,
-
- /* Security */
- REPLY_WEPKEY = 0x20,
-
- /* RX, TX, LEDs */
- REPLY_TX = 0x1c,
- REPLY_LEDS_CMD = 0x48,
- REPLY_TX_LINK_QUALITY_CMD = 0x4e,
-
- /* WiMAX coexistence */
- COEX_PRIORITY_TABLE_CMD = 0x5a,
- COEX_MEDIUM_NOTIFICATION = 0x5b,
- COEX_EVENT_CMD = 0x5c,
-
- /* Calibration */
- TEMPERATURE_NOTIFICATION = 0x62,
- CALIBRATION_CFG_CMD = 0x65,
- CALIBRATION_RES_NOTIFICATION = 0x66,
- CALIBRATION_COMPLETE_NOTIFICATION = 0x67,
-
- /* 802.11h related */
- REPLY_QUIET_CMD = 0x71, /* not used */
- REPLY_CHANNEL_SWITCH = 0x72,
- CHANNEL_SWITCH_NOTIFICATION = 0x73,
- REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74,
- SPECTRUM_MEASURE_NOTIFICATION = 0x75,
-
- /* Power Management */
- POWER_TABLE_CMD = 0x77,
- PM_SLEEP_NOTIFICATION = 0x7A,
- PM_DEBUG_STATISTIC_NOTIFIC = 0x7B,
-
- /* Scan commands and notifications */
- REPLY_SCAN_CMD = 0x80,
- REPLY_SCAN_ABORT_CMD = 0x81,
- SCAN_START_NOTIFICATION = 0x82,
- SCAN_RESULTS_NOTIFICATION = 0x83,
- SCAN_COMPLETE_NOTIFICATION = 0x84,
-
- /* IBSS/AP commands */
- BEACON_NOTIFICATION = 0x90,
- REPLY_TX_BEACON = 0x91,
- WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */
-
- /* Miscellaneous commands */
- REPLY_TX_POWER_DBM_CMD = 0x95,
- QUIET_NOTIFICATION = 0x96, /* not used */
- REPLY_TX_PWR_TABLE_CMD = 0x97,
- REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */
- TX_ANT_CONFIGURATION_CMD = 0x98,
- MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */
-
- /* Bluetooth device coexistence config command */
- REPLY_BT_CONFIG = 0x9b,
-
- /* Statistics */
- REPLY_STATISTICS_CMD = 0x9c,
- STATISTICS_NOTIFICATION = 0x9d,
-
- /* RF-KILL commands and notifications */
- REPLY_CARD_STATE_CMD = 0xa0,
- CARD_STATE_NOTIFICATION = 0xa1,
-
- /* Missed beacons notification */
- MISSED_BEACONS_NOTIFICATION = 0xa2,
-
- REPLY_CT_KILL_CONFIG_CMD = 0xa4,
- SENSITIVITY_CMD = 0xa8,
- REPLY_PHY_CALIBRATION_CMD = 0xb0,
- REPLY_RX_PHY_CMD = 0xc0,
- REPLY_RX_MPDU_CMD = 0xc1,
- REPLY_RX = 0xc3,
- REPLY_COMPRESSED_BA = 0xc5,
-
- /* BT Coex */
- REPLY_BT_COEX_PRIO_TABLE = 0xcc,
- REPLY_BT_COEX_PROT_ENV = 0xcd,
- REPLY_BT_COEX_PROFILE_NOTIF = 0xce,
-
- /* PAN commands */
- REPLY_WIPAN_PARAMS = 0xb2,
- REPLY_WIPAN_RXON = 0xb3, /* use REPLY_RXON structure */
- REPLY_WIPAN_RXON_TIMING = 0xb4, /* use REPLY_RXON_TIMING structure */
- REPLY_WIPAN_RXON_ASSOC = 0xb6, /* use REPLY_RXON_ASSOC structure */
- REPLY_WIPAN_QOS_PARAM = 0xb7, /* use REPLY_QOS_PARAM structure */
- REPLY_WIPAN_WEPKEY = 0xb8, /* use REPLY_WEPKEY structure */
- REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9,
- REPLY_WIPAN_NOA_NOTIFICATION = 0xbc,
- REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd,
-
- REPLY_WOWLAN_PATTERNS = 0xe0,
- REPLY_WOWLAN_WAKEUP_FILTER = 0xe1,
- REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2,
- REPLY_WOWLAN_TKIP_PARAMS = 0xe3,
- REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4,
- REPLY_WOWLAN_GET_STATUS = 0xe5,
- REPLY_D3_CONFIG = 0xd3,
-
- REPLY_MAX = 0xff
-};
-
-/*
- * Minimum number of queues. MAX_NUM is defined in hw specific files.
- * Set the minimum to accommodate
- * - 4 standard TX queues
- * - the command queue
- * - 4 PAN TX queues
- * - the PAN multicast queue, and
- * - the AUX (TX during scan dwell) queue.
- */
-#define IWL_MIN_NUM_QUEUES 11
-
-/*
- * Command queue depends on iPAN support.
- */
-#define IWL_DEFAULT_CMD_QUEUE_NUM 4
-#define IWL_IPAN_CMD_QUEUE_NUM 9
-
-#define IWL_TX_FIFO_BK 0 /* shared */
-#define IWL_TX_FIFO_BE 1
-#define IWL_TX_FIFO_VI 2 /* shared */
-#define IWL_TX_FIFO_VO 3
-#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK
-#define IWL_TX_FIFO_BE_IPAN 4
-#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI
-#define IWL_TX_FIFO_VO_IPAN 5
-/* re-uses the VO FIFO, uCode will properly flush/schedule */
-#define IWL_TX_FIFO_AUX 5
-#define IWL_TX_FIFO_UNUSED 255
-
-#define IWLAGN_CMD_FIFO_NUM 7
-
-/*
- * This queue number is required for proper operation
- * because the ucode will stop/start the scheduler as
- * required.
- */
-#define IWL_IPAN_MCAST_QUEUE 8
-
-/******************************************************************************
- * (0)
- * Commonly used structures and definitions:
- * Command header, rate_n_flags, txpower
- *
- *****************************************************************************/
-
-/**
- * iwlagn rate_n_flags bit fields
- *
- * rate_n_flags format is used in following iwlagn commands:
- * REPLY_RX (response only)
- * REPLY_RX_MPDU (response only)
- * REPLY_TX (both command and response)
- * REPLY_TX_LINK_QUALITY_CMD
- *
- * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"):
- * 2-0: 0) 6 Mbps
- * 1) 12 Mbps
- * 2) 18 Mbps
- * 3) 24 Mbps
- * 4) 36 Mbps
- * 5) 48 Mbps
- * 6) 54 Mbps
- * 7) 60 Mbps
- *
- * 4-3: 0) Single stream (SISO)
- * 1) Dual stream (MIMO)
- * 2) Triple stream (MIMO)
- *
- * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data
- *
- * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"):
- * 3-0: 0xD) 6 Mbps
- * 0xF) 9 Mbps
- * 0x5) 12 Mbps
- * 0x7) 18 Mbps
- * 0x9) 24 Mbps
- * 0xB) 36 Mbps
- * 0x1) 48 Mbps
- * 0x3) 54 Mbps
- *
- * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"):
- * 6-0: 10) 1 Mbps
- * 20) 2 Mbps
- * 55) 5.5 Mbps
- * 110) 11 Mbps
- */
-#define RATE_MCS_CODE_MSK 0x7
-#define RATE_MCS_SPATIAL_POS 3
-#define RATE_MCS_SPATIAL_MSK 0x18
-#define RATE_MCS_HT_DUP_POS 5
-#define RATE_MCS_HT_DUP_MSK 0x20
-/* Both legacy and HT use bits 7:0 as the CCK/OFDM rate or HT MCS */
-#define RATE_MCS_RATE_MSK 0xff
-
-/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */
-#define RATE_MCS_FLAGS_POS 8
-#define RATE_MCS_HT_POS 8
-#define RATE_MCS_HT_MSK 0x100
-
-/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */
-#define RATE_MCS_CCK_POS 9
-#define RATE_MCS_CCK_MSK 0x200
-
-/* Bit 10: (1) Use Green Field preamble */
-#define RATE_MCS_GF_POS 10
-#define RATE_MCS_GF_MSK 0x400
-
-/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */
-#define RATE_MCS_HT40_POS 11
-#define RATE_MCS_HT40_MSK 0x800
-
-/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */
-#define RATE_MCS_DUP_POS 12
-#define RATE_MCS_DUP_MSK 0x1000
-
-/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */
-#define RATE_MCS_SGI_POS 13
-#define RATE_MCS_SGI_MSK 0x2000
-
-/**
- * rate_n_flags Tx antenna masks
- * 4965 has 2 transmitters
- * 5100 has 1 transmitter B
- * 5150 has 1 transmitter A
- * 5300 has 3 transmitters
- * 5350 has 3 transmitters
- * bit14:16
- */
-#define RATE_MCS_ANT_POS 14
-#define RATE_MCS_ANT_A_MSK 0x04000
-#define RATE_MCS_ANT_B_MSK 0x08000
-#define RATE_MCS_ANT_C_MSK 0x10000
-#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK)
-#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK)
-#define RATE_ANT_NUM 3
-
-#define POWER_TABLE_NUM_ENTRIES 33
-#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32
-#define POWER_TABLE_CCK_ENTRY 32
-
-#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24
-#define IWL_PWR_CCK_ENTRIES 2
-
-/**
- * struct tx_power_dual_stream
- *
- * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH
- *
- * Same format as iwl_tx_power_dual_stream, but __le32
- */
-struct tx_power_dual_stream {
- __le32 dw;
-} __packed;
-
-/**
- * Command REPLY_TX_POWER_DBM_CMD = 0x98
- * struct iwlagn_tx_power_dbm_cmd
- */
-#define IWLAGN_TX_POWER_AUTO 0x7f
-#define IWLAGN_TX_POWER_NO_CLOSED (0x1 << 6)
-
-struct iwlagn_tx_power_dbm_cmd {
- s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */
- u8 flags;
- s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */
- u8 reserved;
-} __packed;
-
-/**
- * Command TX_ANT_CONFIGURATION_CMD = 0x98
- * This command is used to configure valid Tx antenna.
- * By default uCode concludes the valid antenna according to the radio flavor.
- * This command enables the driver to override/modify this conclusion.
- */
-struct iwl_tx_ant_config_cmd {
- __le32 valid;
-} __packed;
-
-/******************************************************************************
- * (0a)
- * Alive and Error Commands & Responses:
- *
- *****************************************************************************/
-
-#define UCODE_VALID_OK cpu_to_le32(0x1)
-
-/**
- * REPLY_ALIVE = 0x1 (response only, not a command)
- *
- * uCode issues this "alive" notification once the runtime image is ready
- * to receive commands from the driver. This is the *second* "alive"
- * notification that the driver will receive after rebooting uCode;
- * this "alive" is indicated by subtype field != 9.
- *
- * See comments documenting "BSM" (bootstrap state machine).
- *
- * This response includes two pointers to structures within the device's
- * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging:
- *
- * 1) log_event_table_ptr indicates base of the event log. This traces
- * a 256-entry history of uCode execution within a circular buffer.
- * Its header format is:
- *
- * __le32 log_size; log capacity (in number of entries)
- * __le32 type; (1) timestamp with each entry, (0) no timestamp
- * __le32 wraps; # times uCode has wrapped to top of circular buffer
- * __le32 write_index; next circular buffer entry that uCode would fill
- *
- * The header is followed by the circular buffer of log entries. Entries
- * with timestamps have the following format:
- *
- * __le32 event_id; range 0 - 1500
- * __le32 timestamp; low 32 bits of TSF (of network, if associated)
- * __le32 data; event_id-specific data value
- *
- * Entries without timestamps contain only event_id and data.
- *
- *
- * 2) error_event_table_ptr indicates base of the error log. This contains
- * information about any uCode error that occurs. For agn, the format
- * of the error log is defined by struct iwl_error_event_table.
- *
- * The Linux driver can print both logs to the system log when a uCode error
- * occurs.
- */
-
-/*
- * Note: This structure is read from the device with IO accesses,
- * and the reading already does the endian conversion. As it is
- * read with u32-sized accesses, any members with a different size
- * need to be ordered correctly though!
- */
-struct iwl_error_event_table {
- u32 valid; /* (nonzero) valid, (0) log is empty */
- u32 error_id; /* type of error */
- u32 pc; /* program counter */
- u32 blink1; /* branch link */
- u32 blink2; /* branch link */
- u32 ilink1; /* interrupt link */
- u32 ilink2; /* interrupt link */
- u32 data1; /* error-specific data */
- u32 data2; /* error-specific data */
- u32 line; /* source code line of error */
- u32 bcon_time; /* beacon timer */
- u32 tsf_low; /* network timestamp function timer */
- u32 tsf_hi; /* network timestamp function timer */
- u32 gp1; /* GP1 timer register */
- u32 gp2; /* GP2 timer register */
- u32 gp3; /* GP3 timer register */
- u32 ucode_ver; /* uCode version */
- u32 hw_ver; /* HW Silicon version */
- u32 brd_ver; /* HW board version */
- u32 log_pc; /* log program counter */
- u32 frame_ptr; /* frame pointer */
- u32 stack_ptr; /* stack pointer */
- u32 hcmd; /* last host command header */
- u32 isr0; /* isr status register LMPM_NIC_ISR0:
- * rxtx_flag */
- u32 isr1; /* isr status register LMPM_NIC_ISR1:
- * host_flag */
- u32 isr2; /* isr status register LMPM_NIC_ISR2:
- * enc_flag */
- u32 isr3; /* isr status register LMPM_NIC_ISR3:
- * time_flag */
- u32 isr4; /* isr status register LMPM_NIC_ISR4:
- * wico interrupt */
- u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
- u32 wait_event; /* wait event() caller address */
- u32 l2p_control; /* L2pControlField */
- u32 l2p_duration; /* L2pDurationField */
- u32 l2p_mhvalid; /* L2pMhValidBits */
- u32 l2p_addr_match; /* L2pAddrMatchStat */
- u32 lmpm_pmg_sel; /* indicate which clocks are turned on
- * (LMPM_PMG_SEL) */
- u32 u_timestamp; /* indicate when the date and time of the
- * compilation */
- u32 flow_handler; /* FH read/write pointers, RX credit */
-} __packed;
-
-struct iwl_alive_resp {
- u8 ucode_minor;
- u8 ucode_major;
- __le16 reserved1;
- u8 sw_rev[8];
- u8 ver_type;
- u8 ver_subtype; /* not "9" for runtime alive */
- __le16 reserved2;
- __le32 log_event_table_ptr; /* SRAM address for event log */
- __le32 error_event_table_ptr; /* SRAM address for error log */
- __le32 timestamp;
- __le32 is_valid;
-} __packed;
-
-/*
- * REPLY_ERROR = 0x2 (response only, not a command)
- */
-struct iwl_error_resp {
- __le32 error_type;
- u8 cmd_id;
- u8 reserved1;
- __le16 bad_cmd_seq_num;
- __le32 error_info;
- __le64 timestamp;
-} __packed;
-
-/******************************************************************************
- * (1)
- * RXON Commands & Responses:
- *
- *****************************************************************************/
-
-/*
- * Rx config defines & structure
- */
-/* rx_config device types */
-enum {
- RXON_DEV_TYPE_AP = 1,
- RXON_DEV_TYPE_ESS = 3,
- RXON_DEV_TYPE_IBSS = 4,
- RXON_DEV_TYPE_SNIFFER = 6,
- RXON_DEV_TYPE_CP = 7,
- RXON_DEV_TYPE_2STA = 8,
- RXON_DEV_TYPE_P2P = 9,
-};
-
-
-#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0)
-#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0)
-#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1)
-#define RXON_RX_CHAIN_VALID_POS (1)
-#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4)
-#define RXON_RX_CHAIN_FORCE_SEL_POS (4)
-#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7)
-#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
-#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10)
-#define RXON_RX_CHAIN_CNT_POS (10)
-#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12)
-#define RXON_RX_CHAIN_MIMO_CNT_POS (12)
-#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14)
-#define RXON_RX_CHAIN_MIMO_FORCE_POS (14)
-
-/* rx_config flags */
-/* band & modulation selection */
-#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0)
-#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1)
-/* auto detection enable */
-#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2)
-/* TGg protection when tx */
-#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3)
-/* cck short slot & preamble */
-#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4)
-#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5)
-/* antenna selection */
-#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7)
-#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00)
-#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8)
-#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9)
-/* radar detection enable */
-#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12)
-#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13)
-/* rx response to host with 8-byte TSF
-* (according to ON_AIR deassertion) */
-#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15)
-
-
-/* HT flags */
-#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22)
-#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22)
-
-#define RXON_FLG_HT_OPERATING_MODE_POS (23)
-
-#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23)
-#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23)
-
-#define RXON_FLG_CHANNEL_MODE_POS (25)
-#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25)
-
-/* channel mode */
-enum {
- CHANNEL_MODE_LEGACY = 0,
- CHANNEL_MODE_PURE_40 = 1,
- CHANNEL_MODE_MIXED = 2,
- CHANNEL_MODE_RESERVED = 3,
-};
-#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS)
-#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS)
-#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS)
-
-/* CTS to self (if spec allows) flag */
-#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30)
-
-/* rx_config filter flags */
-/* accept all data frames */
-#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0)
-/* pass control & management to host */
-#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1)
-/* accept multi-cast */
-#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2)
-/* don't decrypt uni-cast frames */
-#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3)
-/* don't decrypt multi-cast frames */
-#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4)
-/* STA is associated */
-#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5)
-/* transfer to host non bssid beacons in associated state */
-#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6)
-
-/**
- * REPLY_RXON = 0x10 (command, has simple generic response)
- *
- * RXON tunes the radio tuner to a service channel, and sets up a number
- * of parameters that are used primarily for Rx, but also for Tx operations.
- *
- * NOTE: When tuning to a new channel, driver must set the
- * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent
- * info within the device, including the station tables, tx retry
- * rate tables, and txpower tables. Driver must build a new station
- * table and txpower table before transmitting anything on the RXON
- * channel.
- *
- * NOTE: All RXONs wipe clean the internal txpower table. Driver must
- * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10),
- * regardless of whether RXON_FILTER_ASSOC_MSK is set.
- */
-
-struct iwl_rxon_cmd {
- u8 node_addr[6];
- __le16 reserved1;
- u8 bssid_addr[6];
- __le16 reserved2;
- u8 wlap_bssid_addr[6];
- __le16 reserved3;
- u8 dev_type;
- u8 air_propagation;
- __le16 rx_chain;
- u8 ofdm_basic_rates;
- u8 cck_basic_rates;
- __le16 assoc_id;
- __le32 flags;
- __le32 filter_flags;
- __le16 channel;
- u8 ofdm_ht_single_stream_basic_rates;
- u8 ofdm_ht_dual_stream_basic_rates;
- u8 ofdm_ht_triple_stream_basic_rates;
- u8 reserved5;
- __le16 acquisition_data;
- __le16 reserved6;
-} __packed;
-
-/*
- * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response)
- */
-struct iwl_rxon_assoc_cmd {
- __le32 flags;
- __le32 filter_flags;
- u8 ofdm_basic_rates;
- u8 cck_basic_rates;
- __le16 reserved1;
- u8 ofdm_ht_single_stream_basic_rates;
- u8 ofdm_ht_dual_stream_basic_rates;
- u8 ofdm_ht_triple_stream_basic_rates;
- u8 reserved2;
- __le16 rx_chain_select_flags;
- __le16 acquisition_data;
- __le32 reserved3;
-} __packed;
-
-#define IWL_CONN_MAX_LISTEN_INTERVAL 10
-#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */
-
-/*
- * REPLY_RXON_TIMING = 0x14 (command, has simple generic response)
- */
-struct iwl_rxon_time_cmd {
- __le64 timestamp;
- __le16 beacon_interval;
- __le16 atim_window;
- __le32 beacon_init_val;
- __le16 listen_interval;
- u8 dtim_period;
- u8 delta_cp_bss_tbtts;
-} __packed;
-
-/*
- * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response)
- */
-/**
- * struct iwl5000_channel_switch_cmd
- * @band: 0- 5.2GHz, 1- 2.4GHz
- * @expect_beacon: 0- resume transmits after channel switch
- * 1- wait for beacon to resume transmits
- * @channel: new channel number
- * @rxon_flags: Rx on flags
- * @rxon_filter_flags: filtering parameters
- * @switch_time: switch time in extended beacon format
- * @reserved: reserved bytes
- */
-struct iwl5000_channel_switch_cmd {
- u8 band;
- u8 expect_beacon;
- __le16 channel;
- __le32 rxon_flags;
- __le32 rxon_filter_flags;
- __le32 switch_time;
- __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES];
-} __packed;
-
-/**
- * struct iwl6000_channel_switch_cmd
- * @band: 0- 5.2GHz, 1- 2.4GHz
- * @expect_beacon: 0- resume transmits after channel switch
- * 1- wait for beacon to resume transmits
- * @channel: new channel number
- * @rxon_flags: Rx on flags
- * @rxon_filter_flags: filtering parameters
- * @switch_time: switch time in extended beacon format
- * @reserved: reserved bytes
- */
-struct iwl6000_channel_switch_cmd {
- u8 band;
- u8 expect_beacon;
- __le16 channel;
- __le32 rxon_flags;
- __le32 rxon_filter_flags;
- __le32 switch_time;
- __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES];
-} __packed;
-
-/*
- * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command)
- */
-struct iwl_csa_notification {
- __le16 band;
- __le16 channel;
- __le32 status; /* 0 - OK, 1 - fail */
-} __packed;
-
-/******************************************************************************
- * (2)
- * Quality-of-Service (QOS) Commands & Responses:
- *
- *****************************************************************************/
-
-/**
- * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM
- * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd
- *
- * @cw_min: Contention window, start value in numbers of slots.
- * Should be a power-of-2, minus 1. Device's default is 0x0f.
- * @cw_max: Contention window, max value in numbers of slots.
- * Should be a power-of-2, minus 1. Device's default is 0x3f.
- * @aifsn: Number of slots in Arbitration Interframe Space (before
- * performing random backoff timing prior to Tx). Device default 1.
- * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0.
- *
- * Device will automatically increase contention window by (2*CW) + 1 for each
- * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW
- * value, to cap the CW value.
- */
-struct iwl_ac_qos {
- __le16 cw_min;
- __le16 cw_max;
- u8 aifsn;
- u8 reserved1;
- __le16 edca_txop;
-} __packed;
-
-/* QoS flags defines */
-#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01)
-#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02)
-#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10)
-
-/* Number of Access Categories (AC) (EDCA), queues 0..3 */
-#define AC_NUM 4
-
-/*
- * REPLY_QOS_PARAM = 0x13 (command, has simple generic response)
- *
- * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs
- * 0: Background, 1: Best Effort, 2: Video, 3: Voice.
- */
-struct iwl_qosparam_cmd {
- __le32 qos_flags;
- struct iwl_ac_qos ac[AC_NUM];
-} __packed;
-
-/******************************************************************************
- * (3)
- * Add/Modify Stations Commands & Responses:
- *
- *****************************************************************************/
-/*
- * Multi station support
- */
-
-/* Special, dedicated locations within device's station table */
-#define IWL_AP_ID 0
-#define IWL_AP_ID_PAN 1
-#define IWL_STA_ID 2
-#define IWLAGN_PAN_BCAST_ID 14
-#define IWLAGN_BROADCAST_ID 15
-#define IWLAGN_STATION_COUNT 16
-
-#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT
-
-#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2)
-#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8)
-#define STA_FLG_PAN_STATION cpu_to_le32(1 << 13)
-#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17)
-#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18)
-#define STA_FLG_MAX_AGG_SIZE_POS (19)
-#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19)
-#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21)
-#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22)
-#define STA_FLG_AGG_MPDU_DENSITY_POS (23)
-#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23)
-
-/* Use in mode field. 1: modify existing entry, 0: add new station entry */
-#define STA_CONTROL_MODIFY_MSK 0x01
-
-/* key flags __le16*/
-#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007)
-#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000)
-#define STA_KEY_FLG_WEP cpu_to_le16(0x0001)
-#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002)
-#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003)
-
-#define STA_KEY_FLG_KEYID_POS 8
-#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800)
-/* wep key is either from global key (0) or from station info array (1) */
-#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008)
-
-/* wep key in STA: 5-bytes (0) or 13-bytes (1) */
-#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000)
-#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000)
-#define STA_KEY_MAX_NUM 8
-#define STA_KEY_MAX_NUM_PAN 16
-/* must not match WEP_INVALID_OFFSET */
-#define IWLAGN_HW_KEY_DEFAULT 0xfe
-
-/* Flags indicate whether to modify vs. don't change various station params */
-#define STA_MODIFY_KEY_MASK 0x01
-#define STA_MODIFY_TID_DISABLE_TX 0x02
-#define STA_MODIFY_TX_RATE_MSK 0x04
-#define STA_MODIFY_ADDBA_TID_MSK 0x08
-#define STA_MODIFY_DELBA_TID_MSK 0x10
-#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20
-
-/* agn */
-struct iwl_keyinfo {
- __le16 key_flags;
- u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */
- u8 reserved1;
- __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */
- u8 key_offset;
- u8 reserved2;
- u8 key[16]; /* 16-byte unicast decryption key */
- __le64 tx_secur_seq_cnt;
- __le64 hw_tkip_mic_rx_key;
- __le64 hw_tkip_mic_tx_key;
-} __packed;
-
-/**
- * struct sta_id_modify
- * @addr[ETH_ALEN]: station's MAC address
- * @sta_id: index of station in uCode's station table
- * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change
- *
- * Driver selects unused table index when adding new station,
- * or the index to a pre-existing station entry when modifying that station.
- * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP).
- *
- * modify_mask flags select which parameters to modify vs. leave alone.
- */
-struct sta_id_modify {
- u8 addr[ETH_ALEN];
- __le16 reserved1;
- u8 sta_id;
- u8 modify_mask;
- __le16 reserved2;
-} __packed;
-
-/*
- * REPLY_ADD_STA = 0x18 (command)
- *
- * The device contains an internal table of per-station information,
- * with info on security keys, aggregation parameters, and Tx rates for
- * initial Tx attempt and any retries (agn devices uses
- * REPLY_TX_LINK_QUALITY_CMD,
- *
- * REPLY_ADD_STA sets up the table entry for one station, either creating
- * a new entry, or modifying a pre-existing one.
- *
- * NOTE: RXON command (without "associated" bit set) wipes the station table
- * clean. Moving into RF_KILL state does this also. Driver must set up
- * new station table before transmitting anything on the RXON channel
- * (except active scans or active measurements; those commands carry
- * their own txpower/rate setup data).
- *
- * When getting started on a new channel, driver must set up the
- * IWL_BROADCAST_ID entry (last entry in the table). For a client
- * station in a BSS, once an AP is selected, driver sets up the AP STA
- * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP
- * are all that are needed for a BSS client station. If the device is
- * used as AP, or in an IBSS network, driver must set up station table
- * entries for all STAs in network, starting with index IWL_STA_ID.
- */
-
-struct iwl_addsta_cmd {
- u8 mode; /* 1: modify existing, 0: add new station */
- u8 reserved[3];
- struct sta_id_modify sta;
- struct iwl_keyinfo key;
- __le32 station_flags; /* STA_FLG_* */
- __le32 station_flags_msk; /* STA_FLG_* */
-
- /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID)
- * corresponding to bit (e.g. bit 5 controls TID 5).
- * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */
- __le16 tid_disable_tx;
- __le16 legacy_reserved;
-
- /* TID for which to add block-ack support.
- * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */
- u8 add_immediate_ba_tid;
-
- /* TID for which to remove block-ack support.
- * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */
- u8 remove_immediate_ba_tid;
-
- /* Starting Sequence Number for added block-ack support.
- * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */
- __le16 add_immediate_ba_ssn;
-
- /*
- * Number of packets OK to transmit to station even though
- * it is asleep -- used to synchronise PS-poll and u-APSD
- * responses while ucode keeps track of STA sleep state.
- */
- __le16 sleep_tx_count;
-
- __le16 reserved2;
-} __packed;
-
-
-#define ADD_STA_SUCCESS_MSK 0x1
-#define ADD_STA_NO_ROOM_IN_TABLE 0x2
-#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4
-#define ADD_STA_MODIFY_NON_EXIST_STA 0x8
-/*
- * REPLY_ADD_STA = 0x18 (response)
- */
-struct iwl_add_sta_resp {
- u8 status; /* ADD_STA_* */
-} __packed;
-
-#define REM_STA_SUCCESS_MSK 0x1
-/*
- * REPLY_REM_STA = 0x19 (response)
- */
-struct iwl_rem_sta_resp {
- u8 status;
-} __packed;
-
-/*
- * REPLY_REM_STA = 0x19 (command)
- */
-struct iwl_rem_sta_cmd {
- u8 num_sta; /* number of removed stations */
- u8 reserved[3];
- u8 addr[ETH_ALEN]; /* MAC addr of the first station */
- u8 reserved2[2];
-} __packed;
-
-
-/* WiFi queues mask */
-#define IWL_SCD_BK_MSK BIT(0)
-#define IWL_SCD_BE_MSK BIT(1)
-#define IWL_SCD_VI_MSK BIT(2)
-#define IWL_SCD_VO_MSK BIT(3)
-#define IWL_SCD_MGMT_MSK BIT(3)
-
-/* PAN queues mask */
-#define IWL_PAN_SCD_BK_MSK BIT(4)
-#define IWL_PAN_SCD_BE_MSK BIT(5)
-#define IWL_PAN_SCD_VI_MSK BIT(6)
-#define IWL_PAN_SCD_VO_MSK BIT(7)
-#define IWL_PAN_SCD_MGMT_MSK BIT(7)
-#define IWL_PAN_SCD_MULTICAST_MSK BIT(8)
-
-#define IWL_AGG_TX_QUEUE_MSK 0xffc00
-
-#define IWL_DROP_ALL BIT(1)
-
-/*
- * REPLY_TXFIFO_FLUSH = 0x1e(command and response)
- *
- * When using full FIFO flush this command checks the scheduler HW block WR/RD
- * pointers to check if all the frames were transferred by DMA into the
- * relevant TX FIFO queue. Only when the DMA is finished and the queue is
- * empty the command can finish.
- * This command is used to flush the TXFIFO from transmit commands, it may
- * operate on single or multiple queues, the command queue can't be flushed by
- * this command. The command response is returned when all the queue flush
- * operations are done. Each TX command flushed return response with the FLUSH
- * status set in the TX response status. When FIFO flush operation is used,
- * the flush operation ends when both the scheduler DMA done and TXFIFO empty
- * are set.
- *
- * @queue_control: bit mask for which queues to flush
- * @flush_control: flush controls
- * 0: Dump single MSDU
- * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable.
- * 2: Dump all FIFO
- */
-struct iwl_txfifo_flush_cmd_v3 {
- __le32 queue_control;
- __le16 flush_control;
- __le16 reserved;
-} __packed;
-
-struct iwl_txfifo_flush_cmd_v2 {
- __le16 queue_control;
- __le16 flush_control;
-} __packed;
-
-/*
- * REPLY_WEP_KEY = 0x20
- */
-struct iwl_wep_key {
- u8 key_index;
- u8 key_offset;
- u8 reserved1[2];
- u8 key_size;
- u8 reserved2[3];
- u8 key[16];
-} __packed;
-
-struct iwl_wep_cmd {
- u8 num_keys;
- u8 global_key_type;
- u8 flags;
- u8 reserved;
- struct iwl_wep_key key[0];
-} __packed;
-
-#define WEP_KEY_WEP_TYPE 1
-#define WEP_KEYS_MAX 4
-#define WEP_INVALID_OFFSET 0xff
-#define WEP_KEY_LEN_64 5
-#define WEP_KEY_LEN_128 13
-
-/******************************************************************************
- * (4)
- * Rx Responses:
- *
- *****************************************************************************/
-
-#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0)
-#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1)
-
-#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0)
-#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1)
-#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2)
-#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3)
-#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70
-#define RX_RES_PHY_FLAGS_ANTENNA_POS 4
-#define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7)
-
-#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
-#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
-#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8)
-#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8)
-#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8)
-#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8)
-
-#define RX_RES_STATUS_STATION_FOUND (1<<6)
-#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7)
-
-#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11)
-#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11)
-#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11)
-#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11)
-#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11)
-
-#define RX_MPDU_RES_STATUS_ICV_OK (0x20)
-#define RX_MPDU_RES_STATUS_MIC_OK (0x40)
-#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7)
-#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800)
-
-
-#define IWLAGN_RX_RES_PHY_CNT 8
-#define IWLAGN_RX_RES_AGC_IDX 1
-#define IWLAGN_RX_RES_RSSI_AB_IDX 2
-#define IWLAGN_RX_RES_RSSI_C_IDX 3
-#define IWLAGN_OFDM_AGC_MSK 0xfe00
-#define IWLAGN_OFDM_AGC_BIT_POS 9
-#define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff
-#define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00
-#define IWLAGN_OFDM_RSSI_A_BIT_POS 0
-#define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000
-#define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000
-#define IWLAGN_OFDM_RSSI_B_BIT_POS 16
-#define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff
-#define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00
-#define IWLAGN_OFDM_RSSI_C_BIT_POS 0
-
-struct iwlagn_non_cfg_phy {
- __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */
-} __packed;
-
-
-/*
- * REPLY_RX = 0xc3 (response only, not a command)
- * Used only for legacy (non 11n) frames.
- */
-struct iwl_rx_phy_res {
- u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */
- u8 cfg_phy_cnt; /* configurable DSP phy data byte count */
- u8 stat_id; /* configurable DSP phy data set ID */
- u8 reserved1;
- __le64 timestamp; /* TSF at on air rise */
- __le32 beacon_time_stamp; /* beacon at on-air rise */
- __le16 phy_flags; /* general phy flags: band, modulation, ... */
- __le16 channel; /* channel number */
- u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */
- __le32 rate_n_flags; /* RATE_MCS_* */
- __le16 byte_count; /* frame's byte-count */
- __le16 frame_time; /* frame's time on the air */
-} __packed;
-
-struct iwl_rx_mpdu_res_start {
- __le16 byte_count;
- __le16 reserved;
-} __packed;
-
-
-/******************************************************************************
- * (5)
- * Tx Commands & Responses:
- *
- * Driver must place each REPLY_TX command into one of the prioritized Tx
- * queues in host DRAM, shared between driver and device (see comments for
- * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode
- * are preparing to transmit, the device pulls the Tx command over the PCI
- * bus via one of the device's Tx DMA channels, to fill an internal FIFO
- * from which data will be transmitted.
- *
- * uCode handles all timing and protocol related to control frames
- * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler
- * handle reception of block-acks; uCode updates the host driver via
- * REPLY_COMPRESSED_BA.
- *
- * uCode handles retrying Tx when an ACK is expected but not received.
- * This includes trying lower data rates than the one requested in the Tx
- * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn).
- *
- * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD.
- * This command must be executed after every RXON command, before Tx can occur.
- *****************************************************************************/
-
-/* REPLY_TX Tx flags field */
-
-/*
- * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it
- * before this frame. if CTS-to-self required check
- * RXON_FLG_SELF_CTS_EN status.
- */
-#define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0)
-
-/* 1: Expect ACK from receiving station
- * 0: Don't expect ACK (MAC header's duration field s/b 0)
- * Set this for unicast frames, but not broadcast/multicast. */
-#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3)
-
-/* For agn devices:
- * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD).
- * Tx command's initial_rate_index indicates first rate to try;
- * uCode walks through table for additional Tx attempts.
- * 0: Use Tx rate/MCS from Tx command's rate_n_flags field.
- * This rate will be used for all Tx attempts; it will not be scaled. */
-#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4)
-
-/* 1: Expect immediate block-ack.
- * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */
-#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6)
-
-/* Tx antenna selection field; reserved (0) for agn devices. */
-#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00)
-
-/* 1: Ignore Bluetooth priority for this frame.
- * 0: Delay Tx until Bluetooth device is done (normal usage). */
-#define TX_CMD_FLG_IGNORE_BT cpu_to_le32(1 << 12)
-
-/* 1: uCode overrides sequence control field in MAC header.
- * 0: Driver provides sequence control field in MAC header.
- * Set this for management frames, non-QOS data frames, non-unicast frames,
- * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */
-#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13)
-
-/* 1: This frame is non-last MPDU; more fragments are coming.
- * 0: Last fragment, or not using fragmentation. */
-#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14)
-
-/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame.
- * 0: No TSF required in outgoing frame.
- * Set this for transmitting beacons and probe responses. */
-#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16)
-
-/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword
- * alignment of frame's payload data field.
- * 0: No pad
- * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4
- * field (but not both). Driver must align frame data (i.e. data following
- * MAC header) to DWORD boundary. */
-#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20)
-
-/* accelerate aggregation support
- * 0 - no CCMP encryption; 1 - CCMP encryption */
-#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22)
-
-/* HCCA-AP - disable duration overwriting. */
-#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25)
-
-
-/*
- * TX command security control
- */
-#define TX_CMD_SEC_WEP 0x01
-#define TX_CMD_SEC_CCM 0x02
-#define TX_CMD_SEC_TKIP 0x03
-#define TX_CMD_SEC_MSK 0x03
-#define TX_CMD_SEC_SHIFT 6
-#define TX_CMD_SEC_KEY128 0x08
-
-/*
- * REPLY_TX = 0x1c (command)
- */
-
-/*
- * 4965 uCode updates these Tx attempt count values in host DRAM.
- * Used for managing Tx retries when expecting block-acks.
- * Driver should set these fields to 0.
- */
-struct iwl_dram_scratch {
- u8 try_cnt; /* Tx attempts */
- u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */
- __le16 reserved;
-} __packed;
-
-struct iwl_tx_cmd {
- /*
- * MPDU byte count:
- * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size,
- * + 8 byte IV for CCM or TKIP (not used for WEP)
- * + Data payload
- * + 8-byte MIC (not used for CCM/WEP)
- * NOTE: Does not include Tx command bytes, post-MAC pad bytes,
- * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i
- * Range: 14-2342 bytes.
- */
- __le16 len;
-
- /*
- * MPDU or MSDU byte count for next frame.
- * Used for fragmentation and bursting, but not 11n aggregation.
- * Same as "len", but for next frame. Set to 0 if not applicable.
- */
- __le16 next_frame_len;
-
- __le32 tx_flags; /* TX_CMD_FLG_* */
-
- /* uCode may modify this field of the Tx command (in host DRAM!).
- * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */
- struct iwl_dram_scratch scratch;
-
- /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */
- __le32 rate_n_flags; /* RATE_MCS_* */
-
- /* Index of destination station in uCode's station table */
- u8 sta_id;
-
- /* Type of security encryption: CCM or TKIP */
- u8 sec_ctl; /* TX_CMD_SEC_* */
-
- /*
- * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial
- * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for
- * data frames, this field may be used to selectively reduce initial
- * rate (via non-0 value) for special frames (e.g. management), while
- * still supporting rate scaling for all frames.
- */
- u8 initial_rate_index;
- u8 reserved;
- u8 key[16];
- __le16 next_frame_flags;
- __le16 reserved2;
- union {
- __le32 life_time;
- __le32 attempt;
- } stop_time;
-
- /* Host DRAM physical address pointer to "scratch" in this command.
- * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */
- __le32 dram_lsb_ptr;
- u8 dram_msb_ptr;
-
- u8 rts_retry_limit; /*byte 50 */
- u8 data_retry_limit; /*byte 51 */
- u8 tid_tspec;
- union {
- __le16 pm_frame_timeout;
- __le16 attempt_duration;
- } timeout;
-
- /*
- * Duration of EDCA burst Tx Opportunity, in 32-usec units.
- * Set this if txop time is not specified by HCCA protocol (e.g. by AP).
- */
- __le16 driver_txop;
-
- /*
- * MAC header goes here, followed by 2 bytes padding if MAC header
- * length is 26 or 30 bytes, followed by payload data
- */
- u8 payload[0];
- struct ieee80211_hdr hdr[0];
-} __packed;
-
-/*
- * TX command response is sent after *agn* transmission attempts.
- *
- * both postpone and abort status are expected behavior from uCode. there is
- * no special operation required from driver; except for RFKILL_FLUSH,
- * which required tx flush host command to flush all the tx frames in queues
- */
-enum {
- TX_STATUS_SUCCESS = 0x01,
- TX_STATUS_DIRECT_DONE = 0x02,
- /* postpone TX */
- TX_STATUS_POSTPONE_DELAY = 0x40,
- TX_STATUS_POSTPONE_FEW_BYTES = 0x41,
- TX_STATUS_POSTPONE_BT_PRIO = 0x42,
- TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43,
- TX_STATUS_POSTPONE_CALC_TTAK = 0x44,
- /* abort TX */
- TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81,
- TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
- TX_STATUS_FAIL_LONG_LIMIT = 0x83,
- TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84,
- TX_STATUS_FAIL_DRAIN_FLOW = 0x85,
- TX_STATUS_FAIL_RFKILL_FLUSH = 0x86,
- TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
- TX_STATUS_FAIL_DEST_PS = 0x88,
- TX_STATUS_FAIL_HOST_ABORTED = 0x89,
- TX_STATUS_FAIL_BT_RETRY = 0x8a,
- TX_STATUS_FAIL_STA_INVALID = 0x8b,
- TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
- TX_STATUS_FAIL_TID_DISABLE = 0x8d,
- TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
- TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f,
- TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90,
- TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91,
-};
-
-#define TX_PACKET_MODE_REGULAR 0x0000
-#define TX_PACKET_MODE_BURST_SEQ 0x0100
-#define TX_PACKET_MODE_BURST_FIRST 0x0200
-
-enum {
- TX_POWER_PA_NOT_ACTIVE = 0x0,
-};
-
-enum {
- TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */
- TX_STATUS_DELAY_MSK = 0x00000040,
- TX_STATUS_ABORT_MSK = 0x00000080,
- TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */
- TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */
- TX_RESERVED = 0x00780000, /* bits 19:22 */
- TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */
- TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */
-};
-
-/* *******************************
- * TX aggregation status
- ******************************* */
-
-enum {
- AGG_TX_STATE_TRANSMITTED = 0x00,
- AGG_TX_STATE_UNDERRUN_MSK = 0x01,
- AGG_TX_STATE_BT_PRIO_MSK = 0x02,
- AGG_TX_STATE_FEW_BYTES_MSK = 0x04,
- AGG_TX_STATE_ABORT_MSK = 0x08,
- AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10,
- AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20,
- AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40,
- AGG_TX_STATE_SCD_QUERY_MSK = 0x80,
- AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100,
- AGG_TX_STATE_RESPONSE_MSK = 0x1ff,
- AGG_TX_STATE_DUMP_TX_MSK = 0x200,
- AGG_TX_STATE_DELAY_TX_MSK = 0x400
-};
-
-#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */
-#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */
-#define AGG_TX_TRY_POS 12
-
-#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \
- AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \
- AGG_TX_STATE_LAST_SENT_BT_KILL_MSK)
-
-/* # tx attempts for first frame in aggregation */
-#define AGG_TX_STATE_TRY_CNT_POS 12
-#define AGG_TX_STATE_TRY_CNT_MSK 0xf000
-
-/* Command ID and sequence number of Tx command for this frame */
-#define AGG_TX_STATE_SEQ_NUM_POS 16
-#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000
-
-/*
- * REPLY_TX = 0x1c (response)
- *
- * This response may be in one of two slightly different formats, indicated
- * by the frame_count field:
- *
- * 1) No aggregation (frame_count == 1). This reports Tx results for
- * a single frame. Multiple attempts, at various bit rates, may have
- * been made for this frame.
- *
- * 2) Aggregation (frame_count > 1). This reports Tx results for
- * 2 or more frames that used block-acknowledge. All frames were
- * transmitted at same rate. Rate scaling may have been used if first
- * frame in this new agg block failed in previous agg block(s).
- *
- * Note that, for aggregation, ACK (block-ack) status is not delivered here;
- * block-ack has not been received by the time the agn device records
- * this status.
- * This status relates to reasons the tx might have been blocked or aborted
- * within the sending station (this agn device), rather than whether it was
- * received successfully by the destination station.
- */
-struct agg_tx_status {
- __le16 status;
- __le16 sequence;
-} __packed;
-
-/*
- * definitions for initial rate index field
- * bits [3:0] initial rate index
- * bits [6:4] rate table color, used for the initial rate
- * bit-7 invalid rate indication
- * i.e. rate was not chosen from rate table
- * or rate table color was changed during frame retries
- * refer tlc rate info
- */
-
-#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0
-#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f
-#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4
-#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70
-#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80
-
-/* refer to ra_tid */
-#define IWLAGN_TX_RES_TID_POS 0
-#define IWLAGN_TX_RES_TID_MSK 0x0f
-#define IWLAGN_TX_RES_RA_POS 4
-#define IWLAGN_TX_RES_RA_MSK 0xf0
-
-struct iwlagn_tx_resp {
- u8 frame_count; /* 1 no aggregation, >1 aggregation */
- u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */
- u8 failure_rts; /* # failures due to unsuccessful RTS */
- u8 failure_frame; /* # failures due to no ACK (unused for agg) */
-
- /* For non-agg: Rate at which frame was successful.
- * For agg: Rate at which all frames were transmitted. */
- __le32 rate_n_flags; /* RATE_MCS_* */
-
- /* For non-agg: RTS + CTS + frame tx attempts time + ACK.
- * For agg: RTS + CTS + aggregation tx time + block-ack time. */
- __le16 wireless_media_time; /* uSecs */
-
- u8 pa_status; /* RF power amplifier measurement (not used) */
- u8 pa_integ_res_a[3];
- u8 pa_integ_res_b[3];
- u8 pa_integ_res_C[3];
-
- __le32 tfd_info;
- __le16 seq_ctl;
- __le16 byte_cnt;
- u8 tlc_info;
- u8 ra_tid; /* tid (0:3), sta_id (4:7) */
- __le16 frame_ctrl;
- /*
- * For non-agg: frame status TX_STATUS_*
- * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status
- * fields follow this one, up to frame_count.
- * Bit fields:
- * 11- 0: AGG_TX_STATE_* status code
- * 15-12: Retry count for 1st frame in aggregation (retries
- * occur if tx failed for this frame when it was a
- * member of a previous aggregation block). If rate
- * scaling is used, retry count indicates the rate
- * table entry used for all frames in the new agg.
- * 31-16: Sequence # for this frame's Tx cmd (not SSN!)
- */
- struct agg_tx_status status; /* TX status (in aggregation -
- * status of 1st frame) */
-} __packed;
-/*
- * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command)
- *
- * Reports Block-Acknowledge from recipient station
- */
-struct iwl_compressed_ba_resp {
- __le32 sta_addr_lo32;
- __le16 sta_addr_hi16;
- __le16 reserved;
-
- /* Index of recipient (BA-sending) station in uCode's station table */
- u8 sta_id;
- u8 tid;
- __le16 seq_ctl;
- __le64 bitmap;
- __le16 scd_flow;
- __le16 scd_ssn;
- u8 txed; /* number of frames sent */
- u8 txed_2_done; /* number of frames acked */
- __le16 reserved1;
-} __packed;
-
-/*
- * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response)
- *
- */
-
-/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */
-#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0)
-
-/* # of EDCA prioritized tx fifos */
-#define LINK_QUAL_AC_NUM AC_NUM
-
-/* # entries in rate scale table to support Tx retries */
-#define LINK_QUAL_MAX_RETRY_NUM 16
-
-/* Tx antenna selection values */
-#define LINK_QUAL_ANT_A_MSK (1 << 0)
-#define LINK_QUAL_ANT_B_MSK (1 << 1)
-#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK)
-
-
-/**
- * struct iwl_link_qual_general_params
- *
- * Used in REPLY_TX_LINK_QUALITY_CMD
- */
-struct iwl_link_qual_general_params {
- u8 flags;
-
- /* No entries at or above this (driver chosen) index contain MIMO */
- u8 mimo_delimiter;
-
- /* Best single antenna to use for single stream (legacy, SISO). */
- u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */
-
- /* Best antennas to use for MIMO (unused for 4965, assumes both). */
- u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */
-
- /*
- * If driver needs to use different initial rates for different
- * EDCA QOS access categories (as implemented by tx fifos 0-3),
- * this table will set that up, by indicating the indexes in the
- * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start.
- * Otherwise, driver should set all entries to 0.
- *
- * Entry usage:
- * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice
- * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3.
- */
- u8 start_rate_index[LINK_QUAL_AC_NUM];
-} __packed;
-
-#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */
-#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000)
-#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100)
-
-#define LINK_QUAL_AGG_DISABLE_START_DEF (3)
-#define LINK_QUAL_AGG_DISABLE_START_MAX (255)
-#define LINK_QUAL_AGG_DISABLE_START_MIN (0)
-
-#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63)
-#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63)
-#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0)
-
-/**
- * struct iwl_link_qual_agg_params
- *
- * Used in REPLY_TX_LINK_QUALITY_CMD
- */
-struct iwl_link_qual_agg_params {
-
- /*
- *Maximum number of uSec in aggregation.
- * default set to 4000 (4 milliseconds) if not configured in .cfg
- */
- __le16 agg_time_limit;
-
- /*
- * Number of Tx retries allowed for a frame, before that frame will
- * no longer be considered for the start of an aggregation sequence
- * (scheduler will then try to tx it as single frame).
- * Driver should set this to 3.
- */
- u8 agg_dis_start_th;
-
- /*
- * Maximum number of frames in aggregation.
- * 0 = no limit (default). 1 = no aggregation.
- * Other values = max # frames in aggregation.
- */
- u8 agg_frame_cnt_limit;
-
- __le32 reserved;
-} __packed;
-
-/*
- * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response)
- *
- * For agn devices
- *
- * Each station in the agn device's internal station table has its own table
- * of 16
- * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when
- * an ACK is not received. This command replaces the entire table for
- * one station.
- *
- * NOTE: Station must already be in agn device's station table.
- * Use REPLY_ADD_STA.
- *
- * The rate scaling procedures described below work well. Of course, other
- * procedures are possible, and may work better for particular environments.
- *
- *
- * FILLING THE RATE TABLE
- *
- * Given a particular initial rate and mode, as determined by the rate
- * scaling algorithm described below, the Linux driver uses the following
- * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the
- * Link Quality command:
- *
- *
- * 1) If using High-throughput (HT) (SISO or MIMO) initial rate:
- * a) Use this same initial rate for first 3 entries.
- * b) Find next lower available rate using same mode (SISO or MIMO),
- * use for next 3 entries. If no lower rate available, switch to
- * legacy mode (no HT40 channel, no MIMO, no short guard interval).
- * c) If using MIMO, set command's mimo_delimiter to number of entries
- * using MIMO (3 or 6).
- * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel,
- * no MIMO, no short guard interval), at the next lower bit rate
- * (e.g. if second HT bit rate was 54, try 48 legacy), and follow
- * legacy procedure for remaining table entries.
- *
- * 2) If using legacy initial rate:
- * a) Use the initial rate for only one entry.
- * b) For each following entry, reduce the rate to next lower available
- * rate, until reaching the lowest available rate.
- * c) When reducing rate, also switch antenna selection.
- * d) Once lowest available rate is reached, repeat this rate until
- * rate table is filled (16 entries), switching antenna each entry.
- *
- *
- * ACCUMULATING HISTORY
- *
- * The rate scaling algorithm for agn devices, as implemented in Linux driver,
- * uses two sets of frame Tx success history: One for the current/active
- * modulation mode, and one for a speculative/search mode that is being
- * attempted. If the speculative mode turns out to be more effective (i.e.
- * actual transfer rate is better), then the driver continues to use the
- * speculative mode as the new current active mode.
- *
- * Each history set contains, separately for each possible rate, data for a
- * sliding window of the 62 most recent tx attempts at that rate. The data
- * includes a shifting bitmap of success(1)/failure(0), and sums of successful
- * and attempted frames, from which the driver can additionally calculate a
- * success ratio (success / attempted) and number of failures
- * (attempted - success), and control the size of the window (attempted).
- * The driver uses the bit map to remove successes from the success sum, as
- * the oldest tx attempts fall out of the window.
- *
- * When the agn device makes multiple tx attempts for a given frame, each
- * attempt might be at a different rate, and have different modulation
- * characteristics (e.g. antenna, fat channel, short guard interval), as set
- * up in the rate scaling table in the Link Quality command. The driver must
- * determine which rate table entry was used for each tx attempt, to determine
- * which rate-specific history to update, and record only those attempts that
- * match the modulation characteristics of the history set.
- *
- * When using block-ack (aggregation), all frames are transmitted at the same
- * rate, since there is no per-attempt acknowledgment from the destination
- * station. The Tx response struct iwl_tx_resp indicates the Tx rate in
- * rate_n_flags field. After receiving a block-ack, the driver can update
- * history for the entire block all at once.
- *
- *
- * FINDING BEST STARTING RATE:
- *
- * When working with a selected initial modulation mode (see below), the
- * driver attempts to find a best initial rate. The initial rate is the
- * first entry in the Link Quality command's rate table.
- *
- * 1) Calculate actual throughput (success ratio * expected throughput, see
- * table below) for current initial rate. Do this only if enough frames
- * have been attempted to make the value meaningful: at least 6 failed
- * tx attempts, or at least 8 successes. If not enough, don't try rate
- * scaling yet.
- *
- * 2) Find available rates adjacent to current initial rate. Available means:
- * a) supported by hardware &&
- * b) supported by association &&
- * c) within any constraints selected by user
- *
- * 3) Gather measured throughputs for adjacent rates. These might not have
- * enough history to calculate a throughput. That's okay, we might try
- * using one of them anyway!
- *
- * 4) Try decreasing rate if, for current rate:
- * a) success ratio is < 15% ||
- * b) lower adjacent rate has better measured throughput ||
- * c) higher adjacent rate has worse throughput, and lower is unmeasured
- *
- * As a sanity check, if decrease was determined above, leave rate
- * unchanged if:
- * a) lower rate unavailable
- * b) success ratio at current rate > 85% (very good)
- * c) current measured throughput is better than expected throughput
- * of lower rate (under perfect 100% tx conditions, see table below)
- *
- * 5) Try increasing rate if, for current rate:
- * a) success ratio is < 15% ||
- * b) both adjacent rates' throughputs are unmeasured (try it!) ||
- * b) higher adjacent rate has better measured throughput ||
- * c) lower adjacent rate has worse throughput, and higher is unmeasured
- *
- * As a sanity check, if increase was determined above, leave rate
- * unchanged if:
- * a) success ratio at current rate < 70%. This is not particularly
- * good performance; higher rate is sure to have poorer success.
- *
- * 6) Re-evaluate the rate after each tx frame. If working with block-
- * acknowledge, history and statistics may be calculated for the entire
- * block (including prior history that fits within the history windows),
- * before re-evaluation.
- *
- * FINDING BEST STARTING MODULATION MODE:
- *
- * After working with a modulation mode for a "while" (and doing rate scaling),
- * the driver searches for a new initial mode in an attempt to improve
- * throughput. The "while" is measured by numbers of attempted frames:
- *
- * For legacy mode, search for new mode after:
- * 480 successful frames, or 160 failed frames
- * For high-throughput modes (SISO or MIMO), search for new mode after:
- * 4500 successful frames, or 400 failed frames
- *
- * Mode switch possibilities are (3 for each mode):
- *
- * For legacy:
- * Change antenna, try SISO (if HT association), try MIMO (if HT association)
- * For SISO:
- * Change antenna, try MIMO, try shortened guard interval (SGI)
- * For MIMO:
- * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI)
- *
- * When trying a new mode, use the same bit rate as the old/current mode when
- * trying antenna switches and shortened guard interval. When switching to
- * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate
- * for which the expected throughput (under perfect conditions) is about the
- * same or slightly better than the actual measured throughput delivered by
- * the old/current mode.
- *
- * Actual throughput can be estimated by multiplying the expected throughput
- * by the success ratio (successful / attempted tx frames). Frame size is
- * not considered in this calculation; it assumes that frame size will average
- * out to be fairly consistent over several samples. The following are
- * metric values for expected throughput assuming 100% success ratio.
- * Only G band has support for CCK rates:
- *
- * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60
- *
- * G: 7 13 35 58 40 57 72 98 121 154 177 186 186
- * A: 0 0 0 0 40 57 72 98 121 154 177 186 186
- * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202
- * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211
- * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251
- * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257
- * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257
- * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264
- * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289
- * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293
- *
- * After the new mode has been tried for a short while (minimum of 6 failed
- * frames or 8 successful frames), compare success ratio and actual throughput
- * estimate of the new mode with the old. If either is better with the new
- * mode, continue to use the new mode.
- *
- * Continue comparing modes until all 3 possibilities have been tried.
- * If moving from legacy to HT, try all 3 possibilities from the new HT
- * mode. After trying all 3, a best mode is found. Continue to use this mode
- * for the longer "while" described above (e.g. 480 successful frames for
- * legacy), and then repeat the search process.
- *
- */
-struct iwl_link_quality_cmd {
-
- /* Index of destination/recipient station in uCode's station table */
- u8 sta_id;
- u8 reserved1;
- __le16 control; /* not used */
- struct iwl_link_qual_general_params general_params;
- struct iwl_link_qual_agg_params agg_params;
-
- /*
- * Rate info; when using rate-scaling, Tx command's initial_rate_index
- * specifies 1st Tx rate attempted, via index into this table.
- * agn devices works its way through table when retrying Tx.
- */
- struct {
- __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */
- } rs_table[LINK_QUAL_MAX_RETRY_NUM];
- __le32 reserved2;
-} __packed;
-
-/*
- * BT configuration enable flags:
- * bit 0 - 1: BT channel announcement enabled
- * 0: disable
- * bit 1 - 1: priority of BT device enabled
- * 0: disable
- * bit 2 - 1: BT 2 wire support enabled
- * 0: disable
- */
-#define BT_COEX_DISABLE (0x0)
-#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0)
-#define BT_ENABLE_PRIORITY BIT(1)
-#define BT_ENABLE_2_WIRE BIT(2)
-
-#define BT_COEX_DISABLE (0x0)
-#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY)
-
-#define BT_LEAD_TIME_MIN (0x0)
-#define BT_LEAD_TIME_DEF (0x1E)
-#define BT_LEAD_TIME_MAX (0xFF)
-
-#define BT_MAX_KILL_MIN (0x1)
-#define BT_MAX_KILL_DEF (0x5)
-#define BT_MAX_KILL_MAX (0xFF)
-
-#define BT_DURATION_LIMIT_DEF 625
-#define BT_DURATION_LIMIT_MAX 1250
-#define BT_DURATION_LIMIT_MIN 625
-
-#define BT_ON_THRESHOLD_DEF 4
-#define BT_ON_THRESHOLD_MAX 1000
-#define BT_ON_THRESHOLD_MIN 1
-
-#define BT_FRAG_THRESHOLD_DEF 0
-#define BT_FRAG_THRESHOLD_MAX 0
-#define BT_FRAG_THRESHOLD_MIN 0
-
-#define BT_AGG_THRESHOLD_DEF 1200
-#define BT_AGG_THRESHOLD_MAX 8000
-#define BT_AGG_THRESHOLD_MIN 400
-
-/*
- * REPLY_BT_CONFIG = 0x9b (command, has simple generic response)
- *
- * agn devices support hardware handshake with Bluetooth device on
- * same platform. Bluetooth device alerts wireless device when it will Tx;
- * wireless device can delay or kill its own Tx to accommodate.
- */
-struct iwl_bt_cmd {
- u8 flags;
- u8 lead_time;
- u8 max_kill;
- u8 reserved;
- __le32 kill_ack_mask;
- __le32 kill_cts_mask;
-} __packed;
-
-#define IWLAGN_BT_FLAG_CHANNEL_INHIBITION BIT(0)
-
-#define IWLAGN_BT_FLAG_COEX_MODE_MASK (BIT(3)|BIT(4)|BIT(5))
-#define IWLAGN_BT_FLAG_COEX_MODE_SHIFT 3
-#define IWLAGN_BT_FLAG_COEX_MODE_DISABLED 0
-#define IWLAGN_BT_FLAG_COEX_MODE_LEGACY_2W 1
-#define IWLAGN_BT_FLAG_COEX_MODE_3W 2
-#define IWLAGN_BT_FLAG_COEX_MODE_4W 3
-
-#define IWLAGN_BT_FLAG_UCODE_DEFAULT BIT(6)
-/* Disable Sync PSPoll on SCO/eSCO */
-#define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7)
-
-#define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */
-#define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */
-
-#define IWLAGN_BT_PRIO_BOOST_MAX 0xFF
-#define IWLAGN_BT_PRIO_BOOST_MIN 0x00
-#define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0
-#define IWLAGN_BT_PRIO_BOOST_DEFAULT32 0xF0F0F0F0
-
-#define IWLAGN_BT_MAX_KILL_DEFAULT 5
-
-#define IWLAGN_BT3_T7_DEFAULT 1
-
-enum iwl_bt_kill_idx {
- IWL_BT_KILL_DEFAULT = 0,
- IWL_BT_KILL_OVERRIDE = 1,
- IWL_BT_KILL_REDUCE = 2,
-};
-
-#define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000)
-#define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000)
-#define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff)
-#define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0)
-
-#define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2
-
-#define IWLAGN_BT3_T2_DEFAULT 0xc
-
-#define IWLAGN_BT_VALID_ENABLE_FLAGS cpu_to_le16(BIT(0))
-#define IWLAGN_BT_VALID_BOOST cpu_to_le16(BIT(1))
-#define IWLAGN_BT_VALID_MAX_KILL cpu_to_le16(BIT(2))
-#define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3))
-#define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4))
-#define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5))
-#define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6))
-#define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7))
-
-#define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \
- IWLAGN_BT_VALID_BOOST | \
- IWLAGN_BT_VALID_MAX_KILL | \
- IWLAGN_BT_VALID_3W_TIMERS | \
- IWLAGN_BT_VALID_KILL_ACK_MASK | \
- IWLAGN_BT_VALID_KILL_CTS_MASK | \
- IWLAGN_BT_VALID_REDUCED_TX_PWR | \
- IWLAGN_BT_VALID_3W_LUT)
-
-#define IWLAGN_BT_REDUCED_TX_PWR BIT(0)
-
-#define IWLAGN_BT_DECISION_LUT_SIZE 12
-
-struct iwl_basic_bt_cmd {
- u8 flags;
- u8 ledtime; /* unused */
- u8 max_kill;
- u8 bt3_timer_t7_value;
- __le32 kill_ack_mask;
- __le32 kill_cts_mask;
- u8 bt3_prio_sample_time;
- u8 bt3_timer_t2_value;
- __le16 bt4_reaction_time; /* unused */
- __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE];
- /*
- * bit 0: use reduced tx power for control frame
- * bit 1 - 7: reserved
- */
- u8 reduce_txpower;
- u8 reserved;
- __le16 valid;
-};
-
-struct iwl_bt_cmd_v1 {
- struct iwl_basic_bt_cmd basic;
- u8 prio_boost;
- /*
- * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask
- * if configure the following patterns
- */
- u8 tx_prio_boost; /* SW boost of WiFi tx priority */
- __le16 rx_prio_boost; /* SW boost of WiFi rx priority */
-};
-
-struct iwl_bt_cmd_v2 {
- struct iwl_basic_bt_cmd basic;
- __le32 prio_boost;
- /*
- * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask
- * if configure the following patterns
- */
- u8 reserved;
- u8 tx_prio_boost; /* SW boost of WiFi tx priority */
- __le16 rx_prio_boost; /* SW boost of WiFi rx priority */
-};
-
-#define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0))
-
-struct iwlagn_bt_sco_cmd {
- __le32 flags;
-};
-
-/******************************************************************************
- * (6)
- * Spectrum Management (802.11h) Commands, Responses, Notifications:
- *
- *****************************************************************************/
-
-/*
- * Spectrum Management
- */
-#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \
- RXON_FILTER_CTL2HOST_MSK | \
- RXON_FILTER_ACCEPT_GRP_MSK | \
- RXON_FILTER_DIS_DECRYPT_MSK | \
- RXON_FILTER_DIS_GRP_DECRYPT_MSK | \
- RXON_FILTER_ASSOC_MSK | \
- RXON_FILTER_BCON_AWARE_MSK)
-
-struct iwl_measure_channel {
- __le32 duration; /* measurement duration in extended beacon
- * format */
- u8 channel; /* channel to measure */
- u8 type; /* see enum iwl_measure_type */
- __le16 reserved;
-} __packed;
-
-/*
- * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command)
- */
-struct iwl_spectrum_cmd {
- __le16 len; /* number of bytes starting from token */
- u8 token; /* token id */
- u8 id; /* measurement id -- 0 or 1 */
- u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */
- u8 periodic; /* 1 = periodic */
- __le16 path_loss_timeout;
- __le32 start_time; /* start time in extended beacon format */
- __le32 reserved2;
- __le32 flags; /* rxon flags */
- __le32 filter_flags; /* rxon filter flags */
- __le16 channel_count; /* minimum 1, maximum 10 */
- __le16 reserved3;
- struct iwl_measure_channel channels[10];
-} __packed;
-
-/*
- * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response)
- */
-struct iwl_spectrum_resp {
- u8 token;
- u8 id; /* id of the prior command replaced, or 0xff */
- __le16 status; /* 0 - command will be handled
- * 1 - cannot handle (conflicts with another
- * measurement) */
-} __packed;
-
-enum iwl_measurement_state {
- IWL_MEASUREMENT_START = 0,
- IWL_MEASUREMENT_STOP = 1,
-};
-
-enum iwl_measurement_status {
- IWL_MEASUREMENT_OK = 0,
- IWL_MEASUREMENT_CONCURRENT = 1,
- IWL_MEASUREMENT_CSA_CONFLICT = 2,
- IWL_MEASUREMENT_TGH_CONFLICT = 3,
- /* 4-5 reserved */
- IWL_MEASUREMENT_STOPPED = 6,
- IWL_MEASUREMENT_TIMEOUT = 7,
- IWL_MEASUREMENT_PERIODIC_FAILED = 8,
-};
-
-#define NUM_ELEMENTS_IN_HISTOGRAM 8
-
-struct iwl_measurement_histogram {
- __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */
- __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */
-} __packed;
-
-/* clear channel availability counters */
-struct iwl_measurement_cca_counters {
- __le32 ofdm;
- __le32 cck;
-} __packed;
-
-enum iwl_measure_type {
- IWL_MEASURE_BASIC = (1 << 0),
- IWL_MEASURE_CHANNEL_LOAD = (1 << 1),
- IWL_MEASURE_HISTOGRAM_RPI = (1 << 2),
- IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3),
- IWL_MEASURE_FRAME = (1 << 4),
- /* bits 5:6 are reserved */
- IWL_MEASURE_IDLE = (1 << 7),
-};
-
-/*
- * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command)
- */
-struct iwl_spectrum_notification {
- u8 id; /* measurement id -- 0 or 1 */
- u8 token;
- u8 channel_index; /* index in measurement channel list */
- u8 state; /* 0 - start, 1 - stop */
- __le32 start_time; /* lower 32-bits of TSF */
- u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */
- u8 channel;
- u8 type; /* see enum iwl_measurement_type */
- u8 reserved1;
- /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only
- * valid if applicable for measurement type requested. */
- __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */
- __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */
- __le32 cca_time; /* channel load time in usecs */
- u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 -
- * unidentified */
- u8 reserved2[3];
- struct iwl_measurement_histogram histogram;
- __le32 stop_time; /* lower 32-bits of TSF */
- __le32 status; /* see iwl_measurement_status */
-} __packed;
-
-/******************************************************************************
- * (7)
- * Power Management Commands, Responses, Notifications:
- *
- *****************************************************************************/
-
-/**
- * struct iwl_powertable_cmd - Power Table Command
- * @flags: See below:
- *
- * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
- *
- * PM allow:
- * bit 0 - '0' Driver not allow power management
- * '1' Driver allow PM (use rest of parameters)
- *
- * uCode send sleep notifications:
- * bit 1 - '0' Don't send sleep notification
- * '1' send sleep notification (SEND_PM_NOTIFICATION)
- *
- * Sleep over DTIM
- * bit 2 - '0' PM have to walk up every DTIM
- * '1' PM could sleep over DTIM till listen Interval.
- *
- * PCI power managed
- * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1)
- * '1' !(PCI_CFG_LINK_CTRL & 0x1)
- *
- * Fast PD
- * bit 4 - '1' Put radio to sleep when receiving frame for others
- *
- * Force sleep Modes
- * bit 31/30- '00' use both mac/xtal sleeps
- * '01' force Mac sleep
- * '10' force xtal sleep
- * '11' Illegal set
- *
- * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then
- * ucode assume sleep over DTIM is allowed and we don't need to wake up
- * for every DTIM.
- */
-#define IWL_POWER_VEC_SIZE 5
-
-#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0))
-#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0))
-#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1))
-#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2))
-#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3))
-#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4))
-#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5))
-#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6))
-#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7))
-#define IWL_POWER_BT_SCO_ENA cpu_to_le16(BIT(8))
-#define IWL_POWER_ADVANCE_PM_ENA_MSK cpu_to_le16(BIT(9))
-
-struct iwl_powertable_cmd {
- __le16 flags;
- u8 keep_alive_seconds;
- u8 debug_flags;
- __le32 rx_data_timeout;
- __le32 tx_data_timeout;
- __le32 sleep_interval[IWL_POWER_VEC_SIZE];
- __le32 keep_alive_beacons;
-} __packed;
-
-/*
- * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command)
- * all devices identical.
- */
-struct iwl_sleep_notification {
- u8 pm_sleep_mode;
- u8 pm_wakeup_src;
- __le16 reserved;
- __le32 sleep_time;
- __le32 tsf_low;
- __le32 bcon_timer;
-} __packed;
-
-/* Sleep states. all devices identical. */
-enum {
- IWL_PM_NO_SLEEP = 0,
- IWL_PM_SLP_MAC = 1,
- IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2,
- IWL_PM_SLP_FULL_MAC_CARD_STATE = 3,
- IWL_PM_SLP_PHY = 4,
- IWL_PM_SLP_REPENT = 5,
- IWL_PM_WAKEUP_BY_TIMER = 6,
- IWL_PM_WAKEUP_BY_DRIVER = 7,
- IWL_PM_WAKEUP_BY_RFKILL = 8,
- /* 3 reserved */
- IWL_PM_NUM_OF_MODES = 12,
-};
-
-/*
- * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response)
- */
-#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */
-#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */
-#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */
-struct iwl_card_state_cmd {
- __le32 status; /* CARD_STATE_CMD_* request new power state */
-} __packed;
-
-/*
- * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command)
- */
-struct iwl_card_state_notif {
- __le32 flags;
-} __packed;
-
-#define HW_CARD_DISABLED 0x01
-#define SW_CARD_DISABLED 0x02
-#define CT_CARD_DISABLED 0x04
-#define RXON_CARD_DISABLED 0x10
-
-struct iwl_ct_kill_config {
- __le32 reserved;
- __le32 critical_temperature_M;
- __le32 critical_temperature_R;
-} __packed;
-
-/* 1000, and 6x00 */
-struct iwl_ct_kill_throttling_config {
- __le32 critical_temperature_exit;
- __le32 reserved;
- __le32 critical_temperature_enter;
-} __packed;
-
-/******************************************************************************
- * (8)
- * Scan Commands, Responses, Notifications:
- *
- *****************************************************************************/
-
-#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0)
-#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1)
-
-/**
- * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
- *
- * One for each channel in the scan list.
- * Each channel can independently select:
- * 1) SSID for directed active scans
- * 2) Txpower setting (for rate specified within Tx command)
- * 3) How long to stay on-channel (behavior may be modified by quiet_time,
- * quiet_plcp_th, good_CRC_th)
- *
- * To avoid uCode errors, make sure the following are true (see comments
- * under struct iwl_scan_cmd about max_out_time and quiet_time):
- * 1) If using passive_dwell (i.e. passive_dwell != 0):
- * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0)
- * 2) quiet_time <= active_dwell
- * 3) If restricting off-channel time (i.e. max_out_time !=0):
- * passive_dwell < max_out_time
- * active_dwell < max_out_time
- */
-
-struct iwl_scan_channel {
- /*
- * type is defined as:
- * 0:0 1 = active, 0 = passive
- * 1:20 SSID direct bit map; if a bit is set, then corresponding
- * SSID IE is transmitted in probe request.
- * 21:31 reserved
- */
- __le32 type;
- __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */
- u8 tx_gain; /* gain for analog radio */
- u8 dsp_atten; /* gain for DSP */
- __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */
- __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */
-} __packed;
-
-/* set number of direct probes __le32 type */
-#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1))))
-
-/**
- * struct iwl_ssid_ie - directed scan network information element
- *
- * Up to 20 of these may appear in REPLY_SCAN_CMD,
- * selected by "type" bit field in struct iwl_scan_channel;
- * each channel may select different ssids from among the 20 entries.
- * SSID IEs get transmitted in reverse order of entry.
- */
-struct iwl_ssid_ie {
- u8 id;
- u8 len;
- u8 ssid[32];
-} __packed;
-
-#define PROBE_OPTION_MAX 20
-#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF)
-#define IWL_GOOD_CRC_TH_DISABLED 0
-#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1)
-#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff)
-#define IWL_MAX_CMD_SIZE 4096
-
-/*
- * REPLY_SCAN_CMD = 0x80 (command)
- *
- * The hardware scan command is very powerful; the driver can set it up to
- * maintain (relatively) normal network traffic while doing a scan in the
- * background. The max_out_time and suspend_time control the ratio of how
- * long the device stays on an associated network channel ("service channel")
- * vs. how long it's away from the service channel, i.e. tuned to other channels
- * for scanning.
- *
- * max_out_time is the max time off-channel (in usec), and suspend_time
- * is how long (in "extended beacon" format) that the scan is "suspended"
- * after returning to the service channel. That is, suspend_time is the
- * time that we stay on the service channel, doing normal work, between
- * scan segments. The driver may set these parameters differently to support
- * scanning when associated vs. not associated, and light vs. heavy traffic
- * loads when associated.
- *
- * After receiving this command, the device's scan engine does the following;
- *
- * 1) Sends SCAN_START notification to driver
- * 2) Checks to see if it has time to do scan for one channel
- * 3) Sends NULL packet, with power-save (PS) bit set to 1,
- * to tell AP that we're going off-channel
- * 4) Tunes to first channel in scan list, does active or passive scan
- * 5) Sends SCAN_RESULT notification to driver
- * 6) Checks to see if it has time to do scan on *next* channel in list
- * 7) Repeats 4-6 until it no longer has time to scan the next channel
- * before max_out_time expires
- * 8) Returns to service channel
- * 9) Sends NULL packet with PS=0 to tell AP that we're back
- * 10) Stays on service channel until suspend_time expires
- * 11) Repeats entire process 2-10 until list is complete
- * 12) Sends SCAN_COMPLETE notification
- *
- * For fast, efficient scans, the scan command also has support for staying on
- * a channel for just a short time, if doing active scanning and getting no
- * responses to the transmitted probe request. This time is controlled by
- * quiet_time, and the number of received packets below which a channel is
- * considered "quiet" is controlled by quiet_plcp_threshold.
- *
- * For active scanning on channels that have regulatory restrictions against
- * blindly transmitting, the scan can listen before transmitting, to make sure
- * that there is already legitimate activity on the channel. If enough
- * packets are cleanly received on the channel (controlled by good_CRC_th,
- * typical value 1), the scan engine starts transmitting probe requests.
- *
- * Driver must use separate scan commands for 2.4 vs. 5 GHz bands.
- *
- * To avoid uCode errors, see timing restrictions described under
- * struct iwl_scan_channel.
- */
-
-enum iwl_scan_flags {
- /* BIT(0) currently unused */
- IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1),
- /* bits 2-7 reserved */
-};
-
-struct iwl_scan_cmd {
- __le16 len;
- u8 scan_flags; /* scan flags: see enum iwl_scan_flags */
- u8 channel_count; /* # channels in channel list */
- __le16 quiet_time; /* dwell only this # millisecs on quiet channel
- * (only for active scan) */
- __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */
- __le16 good_CRC_th; /* passive -> active promotion threshold */
- __le16 rx_chain; /* RXON_RX_CHAIN_* */
- __le32 max_out_time; /* max usec to be away from associated (service)
- * channel */
- __le32 suspend_time; /* pause scan this long (in "extended beacon
- * format") when returning to service chnl:
- */
- __le32 flags; /* RXON_FLG_* */
- __le32 filter_flags; /* RXON_FILTER_* */
-
- /* For active scans (set to all-0s for passive scans).
- * Does not include payload. Must specify Tx rate; no rate scaling. */
- struct iwl_tx_cmd tx_cmd;
-
- /* For directed active scans (set to all-0s otherwise) */
- struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
-
- /*
- * Probe request frame, followed by channel list.
- *
- * Size of probe request frame is specified by byte count in tx_cmd.
- * Channel list follows immediately after probe request frame.
- * Number of channels in list is specified by channel_count.
- * Each channel in list is of type:
- *
- * struct iwl_scan_channel channels[0];
- *
- * NOTE: Only one band of channels can be scanned per pass. You
- * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
- * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
- * before requesting another scan.
- */
- u8 data[0];
-} __packed;
-
-/* Can abort will notify by complete notification with abort status. */
-#define CAN_ABORT_STATUS cpu_to_le32(0x1)
-/* complete notification statuses */
-#define ABORT_STATUS 0x2
-
-/*
- * REPLY_SCAN_CMD = 0x80 (response)
- */
-struct iwl_scanreq_notification {
- __le32 status; /* 1: okay, 2: cannot fulfill request */
-} __packed;
-
-/*
- * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command)
- */
-struct iwl_scanstart_notification {
- __le32 tsf_low;
- __le32 tsf_high;
- __le32 beacon_timer;
- u8 channel;
- u8 band;
- u8 reserved[2];
- __le32 status;
-} __packed;
-
-#define SCAN_OWNER_STATUS 0x1
-#define MEASURE_OWNER_STATUS 0x2
-
-#define IWL_PROBE_STATUS_OK 0
-#define IWL_PROBE_STATUS_TX_FAILED BIT(0)
-/* error statuses combined with TX_FAILED */
-#define IWL_PROBE_STATUS_FAIL_TTL BIT(1)
-#define IWL_PROBE_STATUS_FAIL_BT BIT(2)
-
-#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */
-/*
- * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command)
- */
-struct iwl_scanresults_notification {
- u8 channel;
- u8 band;
- u8 probe_status;
- u8 num_probe_not_sent; /* not enough time to send */
- __le32 tsf_low;
- __le32 tsf_high;
- __le32 statistics[NUMBER_OF_STATISTICS];
-} __packed;
-
-/*
- * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command)
- */
-struct iwl_scancomplete_notification {
- u8 scanned_channels;
- u8 status;
- u8 bt_status; /* BT On/Off status */
- u8 last_channel;
- __le32 tsf_low;
- __le32 tsf_high;
-} __packed;
-
-
-/******************************************************************************
- * (9)
- * IBSS/AP Commands and Notifications:
- *
- *****************************************************************************/
-
-enum iwl_ibss_manager {
- IWL_NOT_IBSS_MANAGER = 0,
- IWL_IBSS_MANAGER = 1,
-};
-
-/*
- * BEACON_NOTIFICATION = 0x90 (notification only, not a command)
- */
-
-struct iwlagn_beacon_notif {
- struct iwlagn_tx_resp beacon_notify_hdr;
- __le32 low_tsf;
- __le32 high_tsf;
- __le32 ibss_mgr_status;
-} __packed;
-
-/*
- * REPLY_TX_BEACON = 0x91 (command, has simple generic response)
- */
-
-struct iwl_tx_beacon_cmd {
- struct iwl_tx_cmd tx;
- __le16 tim_idx;
- u8 tim_size;
- u8 reserved1;
- struct ieee80211_hdr frame[0]; /* beacon frame */
-} __packed;
-
-/******************************************************************************
- * (10)
- * Statistics Commands and Notifications:
- *
- *****************************************************************************/
-
-#define IWL_TEMP_CONVERT 260
-
-#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
-#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
-#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
-
-/* Used for passing to driver number of successes and failures per rate */
-struct rate_histogram {
- union {
- __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
- __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
- __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
- } success;
- union {
- __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
- __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
- __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
- } failed;
-} __packed;
-
-/* statistics command response */
-
-struct statistics_dbg {
- __le32 burst_check;
- __le32 burst_count;
- __le32 wait_for_silence_timeout_cnt;
- __le32 reserved[3];
-} __packed;
-
-struct statistics_rx_phy {
- __le32 ina_cnt;
- __le32 fina_cnt;
- __le32 plcp_err;
- __le32 crc32_err;
- __le32 overrun_err;
- __le32 early_overrun_err;
- __le32 crc32_good;
- __le32 false_alarm_cnt;
- __le32 fina_sync_err_cnt;
- __le32 sfd_timeout;
- __le32 fina_timeout;
- __le32 unresponded_rts;
- __le32 rxe_frame_limit_overrun;
- __le32 sent_ack_cnt;
- __le32 sent_cts_cnt;
- __le32 sent_ba_rsp_cnt;
- __le32 dsp_self_kill;
- __le32 mh_format_err;
- __le32 re_acq_main_rssi_sum;
- __le32 reserved3;
-} __packed;
-
-struct statistics_rx_ht_phy {
- __le32 plcp_err;
- __le32 overrun_err;
- __le32 early_overrun_err;
- __le32 crc32_good;
- __le32 crc32_err;
- __le32 mh_format_err;
- __le32 agg_crc32_good;
- __le32 agg_mpdu_cnt;
- __le32 agg_cnt;
- __le32 unsupport_mcs;
-} __packed;
-
-#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1)
-
-struct statistics_rx_non_phy {
- __le32 bogus_cts; /* CTS received when not expecting CTS */
- __le32 bogus_ack; /* ACK received when not expecting ACK */
- __le32 non_bssid_frames; /* number of frames with BSSID that
- * doesn't belong to the STA BSSID */
- __le32 filtered_frames; /* count frames that were dumped in the
- * filtering process */
- __le32 non_channel_beacons; /* beacons with our bss id but not on
- * our serving channel */
- __le32 channel_beacons; /* beacons with our bss id and in our
- * serving channel */
- __le32 num_missed_bcon; /* number of missed beacons */
- __le32 adc_rx_saturation_time; /* count in 0.8us units the time the
- * ADC was in saturation */
- __le32 ina_detection_search_time;/* total time (in 0.8us) searched
- * for INA */
- __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */
- __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */
- __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */
- __le32 interference_data_flag; /* flag for interference data
- * availability. 1 when data is
- * available. */
- __le32 channel_load; /* counts RX Enable time in uSec */
- __le32 dsp_false_alarms; /* DSP false alarm (both OFDM
- * and CCK) counter */
- __le32 beacon_rssi_a;
- __le32 beacon_rssi_b;
- __le32 beacon_rssi_c;
- __le32 beacon_energy_a;
- __le32 beacon_energy_b;
- __le32 beacon_energy_c;
-} __packed;
-
-struct statistics_rx_non_phy_bt {
- struct statistics_rx_non_phy common;
- /* additional stats for bt */
- __le32 num_bt_kills;
- __le32 reserved[2];
-} __packed;
-
-struct statistics_rx {
- struct statistics_rx_phy ofdm;
- struct statistics_rx_phy cck;
- struct statistics_rx_non_phy general;
- struct statistics_rx_ht_phy ofdm_ht;
-} __packed;
-
-struct statistics_rx_bt {
- struct statistics_rx_phy ofdm;
- struct statistics_rx_phy cck;
- struct statistics_rx_non_phy_bt general;
- struct statistics_rx_ht_phy ofdm_ht;
-} __packed;
-
-/**
- * struct statistics_tx_power - current tx power
- *
- * @ant_a: current tx power on chain a in 1/2 dB step
- * @ant_b: current tx power on chain b in 1/2 dB step
- * @ant_c: current tx power on chain c in 1/2 dB step
- */
-struct statistics_tx_power {
- u8 ant_a;
- u8 ant_b;
- u8 ant_c;
- u8 reserved;
-} __packed;
-
-struct statistics_tx_non_phy_agg {
- __le32 ba_timeout;
- __le32 ba_reschedule_frames;
- __le32 scd_query_agg_frame_cnt;
- __le32 scd_query_no_agg;
- __le32 scd_query_agg;
- __le32 scd_query_mismatch;
- __le32 frame_not_ready;
- __le32 underrun;
- __le32 bt_prio_kill;
- __le32 rx_ba_rsp_cnt;
-} __packed;
-
-struct statistics_tx {
- __le32 preamble_cnt;
- __le32 rx_detected_cnt;
- __le32 bt_prio_defer_cnt;
- __le32 bt_prio_kill_cnt;
- __le32 few_bytes_cnt;
- __le32 cts_timeout;
- __le32 ack_timeout;
- __le32 expected_ack_cnt;
- __le32 actual_ack_cnt;
- __le32 dump_msdu_cnt;
- __le32 burst_abort_next_frame_mismatch_cnt;
- __le32 burst_abort_missing_next_frame_cnt;
- __le32 cts_timeout_collision;
- __le32 ack_or_ba_timeout_collision;
- struct statistics_tx_non_phy_agg agg;
- /*
- * "tx_power" are optional parameters provided by uCode,
- * 6000 series is the only device provide the information,
- * Those are reserved fields for all the other devices
- */
- struct statistics_tx_power tx_power;
- __le32 reserved1;
-} __packed;
-
-
-struct statistics_div {
- __le32 tx_on_a;
- __le32 tx_on_b;
- __le32 exec_time;
- __le32 probe_time;
- __le32 reserved1;
- __le32 reserved2;
-} __packed;
-
-struct statistics_general_common {
- __le32 temperature; /* radio temperature */
- __le32 temperature_m; /* radio voltage */
- struct statistics_dbg dbg;
- __le32 sleep_time;
- __le32 slots_out;
- __le32 slots_idle;
- __le32 ttl_timestamp;
- struct statistics_div div;
- __le32 rx_enable_counter;
- /*
- * num_of_sos_states:
- * count the number of times we have to re-tune
- * in order to get out of bad PHY status
- */
- __le32 num_of_sos_states;
-} __packed;
-
-struct statistics_bt_activity {
- /* Tx statistics */
- __le32 hi_priority_tx_req_cnt;
- __le32 hi_priority_tx_denied_cnt;
- __le32 lo_priority_tx_req_cnt;
- __le32 lo_priority_tx_denied_cnt;
- /* Rx statistics */
- __le32 hi_priority_rx_req_cnt;
- __le32 hi_priority_rx_denied_cnt;
- __le32 lo_priority_rx_req_cnt;
- __le32 lo_priority_rx_denied_cnt;
-} __packed;
-
-struct statistics_general {
- struct statistics_general_common common;
- __le32 reserved2;
- __le32 reserved3;
-} __packed;
-
-struct statistics_general_bt {
- struct statistics_general_common common;
- struct statistics_bt_activity activity;
- __le32 reserved2;
- __le32 reserved3;
-} __packed;
-
-#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0)
-#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1)
-#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2)
-
-/*
- * REPLY_STATISTICS_CMD = 0x9c,
- * all devices identical.
- *
- * This command triggers an immediate response containing uCode statistics.
- * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below.
- *
- * If the CLEAR_STATS configuration flag is set, uCode will clear its
- * internal copy of the statistics (counters) after issuing the response.
- * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below).
- *
- * If the DISABLE_NOTIF configuration flag is set, uCode will not issue
- * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag
- * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself.
- */
-#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */
-#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */
-struct iwl_statistics_cmd {
- __le32 configuration_flags; /* IWL_STATS_CONF_* */
-} __packed;
-
-/*
- * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
- *
- * By default, uCode issues this notification after receiving a beacon
- * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
- * REPLY_STATISTICS_CMD 0x9c, above.
- *
- * Statistics counters continue to increment beacon after beacon, but are
- * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
- * 0x9c with CLEAR_STATS bit set (see above).
- *
- * uCode also issues this notification during scans. uCode clears statistics
- * appropriately so that each notification contains statistics for only the
- * one channel that has just been scanned.
- */
-#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2)
-#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8)
-
-struct iwl_notif_statistics {
- __le32 flag;
- struct statistics_rx rx;
- struct statistics_tx tx;
- struct statistics_general general;
-} __packed;
-
-struct iwl_bt_notif_statistics {
- __le32 flag;
- struct statistics_rx_bt rx;
- struct statistics_tx tx;
- struct statistics_general_bt general;
-} __packed;
-
-/*
- * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command)
- *
- * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed
- * in regardless of how many missed beacons, which mean when driver receive the
- * notification, inside the command, it can find all the beacons information
- * which include number of total missed beacons, number of consecutive missed
- * beacons, number of beacons received and number of beacons expected to
- * receive.
- *
- * If uCode detected consecutive_missed_beacons > 5, it will reset the radio
- * in order to bring the radio/PHY back to working state; which has no relation
- * to when driver will perform sensitivity calibration.
- *
- * Driver should set it own missed_beacon_threshold to decide when to perform
- * sensitivity calibration based on number of consecutive missed beacons in
- * order to improve overall performance, especially in noisy environment.
- *
- */
-
-#define IWL_MISSED_BEACON_THRESHOLD_MIN (1)
-#define IWL_MISSED_BEACON_THRESHOLD_DEF (5)
-#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF
-
-struct iwl_missed_beacon_notif {
- __le32 consecutive_missed_beacons;
- __le32 total_missed_becons;
- __le32 num_expected_beacons;
- __le32 num_recvd_beacons;
-} __packed;
-
-
-/******************************************************************************
- * (11)
- * Rx Calibration Commands:
- *
- * With the uCode used for open source drivers, most Tx calibration (except
- * for Tx Power) and most Rx calibration is done by uCode during the
- * "initialize" phase of uCode boot. Driver must calibrate only:
- *
- * 1) Tx power (depends on temperature), described elsewhere
- * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas)
- * 3) Receiver sensitivity (to optimize signal detection)
- *
- *****************************************************************************/
-
-/**
- * SENSITIVITY_CMD = 0xa8 (command, has simple generic response)
- *
- * This command sets up the Rx signal detector for a sensitivity level that
- * is high enough to lock onto all signals within the associated network,
- * but low enough to ignore signals that are below a certain threshold, so as
- * not to have too many "false alarms". False alarms are signals that the
- * Rx DSP tries to lock onto, but then discards after determining that they
- * are noise.
- *
- * The optimum number of false alarms is between 5 and 50 per 200 TUs
- * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e.
- * time listening, not transmitting). Driver must adjust sensitivity so that
- * the ratio of actual false alarms to actual Rx time falls within this range.
- *
- * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each
- * received beacon. These provide information to the driver to analyze the
- * sensitivity. Don't analyze statistics that come in from scanning, or any
- * other non-associated-network source. Pertinent statistics include:
- *
- * From "general" statistics (struct statistics_rx_non_phy):
- *
- * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level)
- * Measure of energy of desired signal. Used for establishing a level
- * below which the device does not detect signals.
- *
- * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB)
- * Measure of background noise in silent period after beacon.
- *
- * channel_load
- * uSecs of actual Rx time during beacon period (varies according to
- * how much time was spent transmitting).
- *
- * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately:
- *
- * false_alarm_cnt
- * Signal locks abandoned early (before phy-level header).
- *
- * plcp_err
- * Signal locks abandoned late (during phy-level header).
- *
- * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from
- * beacon to beacon, i.e. each value is an accumulation of all errors
- * before and including the latest beacon. Values will wrap around to 0
- * after counting up to 2^32 - 1. Driver must differentiate vs.
- * previous beacon's values to determine # false alarms in the current
- * beacon period.
- *
- * Total number of false alarms = false_alarms + plcp_errs
- *
- * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd
- * (notice that the start points for OFDM are at or close to settings for
- * maximum sensitivity):
- *
- * START / MIN / MAX
- * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120
- * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210
- * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140
- * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270
- *
- * If actual rate of OFDM false alarms (+ plcp_errors) is too high
- * (greater than 50 for each 204.8 msecs listening), reduce sensitivity
- * by *adding* 1 to all 4 of the table entries above, up to the max for
- * each entry. Conversely, if false alarm rate is too low (less than 5
- * for each 204.8 msecs listening), *subtract* 1 from each entry to
- * increase sensitivity.
- *
- * For CCK sensitivity, keep track of the following:
- *
- * 1). 20-beacon history of maximum background noise, indicated by
- * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the
- * 3 receivers. For any given beacon, the "silence reference" is
- * the maximum of last 60 samples (20 beacons * 3 receivers).
- *
- * 2). 10-beacon history of strongest signal level, as indicated
- * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers,
- * i.e. the strength of the signal through the best receiver at the
- * moment. These measurements are "upside down", with lower values
- * for stronger signals, so max energy will be *minimum* value.
- *
- * Then for any given beacon, the driver must determine the *weakest*
- * of the strongest signals; this is the minimum level that needs to be
- * successfully detected, when using the best receiver at the moment.
- * "Max cck energy" is the maximum (higher value means lower energy!)
- * of the last 10 minima. Once this is determined, driver must add
- * a little margin by adding "6" to it.
- *
- * 3). Number of consecutive beacon periods with too few false alarms.
- * Reset this to 0 at the first beacon period that falls within the
- * "good" range (5 to 50 false alarms per 204.8 milliseconds rx).
- *
- * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd
- * (notice that the start points for CCK are at maximum sensitivity):
- *
- * START / MIN / MAX
- * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200
- * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400
- * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100
- *
- * If actual rate of CCK false alarms (+ plcp_errors) is too high
- * (greater than 50 for each 204.8 msecs listening), method for reducing
- * sensitivity is:
- *
- * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX,
- * up to max 400.
- *
- * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160,
- * sensitivity has been reduced a significant amount; bring it up to
- * a moderate 161. Otherwise, *add* 3, up to max 200.
- *
- * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160,
- * sensitivity has been reduced only a moderate or small amount;
- * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX,
- * down to min 0. Otherwise (if gain has been significantly reduced),
- * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value.
- *
- * b) Save a snapshot of the "silence reference".
- *
- * If actual rate of CCK false alarms (+ plcp_errors) is too low
- * (less than 5 for each 204.8 msecs listening), method for increasing
- * sensitivity is used only if:
- *
- * 1a) Previous beacon did not have too many false alarms
- * 1b) AND difference between previous "silence reference" and current
- * "silence reference" (prev - current) is 2 or more,
- * OR 2) 100 or more consecutive beacon periods have had rate of
- * less than 5 false alarms per 204.8 milliseconds rx time.
- *
- * Method for increasing sensitivity:
- *
- * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX,
- * down to min 125.
- *
- * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX,
- * down to min 200.
- *
- * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100.
- *
- * If actual rate of CCK false alarms (+ plcp_errors) is within good range
- * (between 5 and 50 for each 204.8 msecs listening):
- *
- * 1) Save a snapshot of the silence reference.
- *
- * 2) If previous beacon had too many CCK false alarms (+ plcp_errors),
- * give some extra margin to energy threshold by *subtracting* 8
- * from value in HD_MIN_ENERGY_CCK_DET_INDEX.
- *
- * For all cases (too few, too many, good range), make sure that the CCK
- * detection threshold (energy) is below the energy level for robust
- * detection over the past 10 beacon periods, the "Max cck energy".
- * Lower values mean higher energy; this means making sure that the value
- * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy".
- *
- */
-
-/*
- * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd)
- */
-#define HD_TABLE_SIZE (11) /* number of entries */
-#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */
-#define HD_MIN_ENERGY_OFDM_DET_INDEX (1)
-#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2)
-#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3)
-#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4)
-#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5)
-#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6)
-#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7)
-#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8)
-#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9)
-#define HD_OFDM_ENERGY_TH_IN_INDEX (10)
-
-/*
- * Additional table entries in enhance SENSITIVITY_CMD
- */
-#define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11)
-#define HD_INA_NON_SQUARE_DET_CCK_INDEX (12)
-#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21)
-#define HD_RESERVED (22)
-
-/* number of entries for enhanced tbl */
-#define ENHANCE_HD_TABLE_SIZE (23)
-
-/* number of additional entries for enhanced tbl */
-#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE)
-
-#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0)
-#define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0)
-#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99)
-
-#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1)
-#define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1)
-#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40)
-#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486)
-#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60)
-#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476)
-#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99)
-
-
-/* Control field in struct iwl_sensitivity_cmd */
-#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0)
-#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1)
-
-/**
- * struct iwl_sensitivity_cmd
- * @control: (1) updates working table, (0) updates default table
- * @table: energy threshold values, use HD_* as index into table
- *
- * Always use "1" in "control" to update uCode's working table and DSP.
- */
-struct iwl_sensitivity_cmd {
- __le16 control; /* always use "1" */
- __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */
-} __packed;
-
-/*
- *
- */
-struct iwl_enhance_sensitivity_cmd {
- __le16 control; /* always use "1" */
- __le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */
-} __packed;
-
-
-/**
- * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response)
- *
- * This command sets the relative gains of agn device's 3 radio receiver chains.
- *
- * After the first association, driver should accumulate signal and noise
- * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20
- * beacons from the associated network (don't collect statistics that come
- * in from scanning, or any other non-network source).
- *
- * DISCONNECTED ANTENNA:
- *
- * Driver should determine which antennas are actually connected, by comparing
- * average beacon signal levels for the 3 Rx chains. Accumulate (add) the
- * following values over 20 beacons, one accumulator for each of the chains
- * a/b/c, from struct statistics_rx_non_phy:
- *
- * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB)
- *
- * Find the strongest signal from among a/b/c. Compare the other two to the
- * strongest. If any signal is more than 15 dB (times 20, unless you
- * divide the accumulated values by 20) below the strongest, the driver
- * considers that antenna to be disconnected, and should not try to use that
- * antenna/chain for Rx or Tx. If both A and B seem to be disconnected,
- * driver should declare the stronger one as connected, and attempt to use it
- * (A and B are the only 2 Tx chains!).
- *
- *
- * RX BALANCE:
- *
- * Driver should balance the 3 receivers (but just the ones that are connected
- * to antennas, see above) for gain, by comparing the average signal levels
- * detected during the silence after each beacon (background noise).
- * Accumulate (add) the following values over 20 beacons, one accumulator for
- * each of the chains a/b/c, from struct statistics_rx_non_phy:
- *
- * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB)
- *
- * Find the weakest background noise level from among a/b/c. This Rx chain
- * will be the reference, with 0 gain adjustment. Attenuate other channels by
- * finding noise difference:
- *
- * (accum_noise[i] - accum_noise[reference]) / 30
- *
- * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB.
- * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the
- * driver should limit the difference results to a range of 0-3 (0-4.5 dB),
- * and set bit 2 to indicate "reduce gain". The value for the reference
- * (weakest) chain should be "0".
- *
- * diff_gain_[abc] bit fields:
- * 2: (1) reduce gain, (0) increase gain
- * 1-0: amount of gain, units of 1.5 dB
- */
-
-/* Phy calibration command for series */
-enum {
- IWL_PHY_CALIBRATE_DC_CMD = 8,
- IWL_PHY_CALIBRATE_LO_CMD = 9,
- IWL_PHY_CALIBRATE_TX_IQ_CMD = 11,
- IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15,
- IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16,
- IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17,
- IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD = 18,
-};
-
-/* This enum defines the bitmap of various calibrations to enable in both
- * init ucode and runtime ucode through CALIBRATION_CFG_CMD.
- */
-enum iwl_ucode_calib_cfg {
- IWL_CALIB_CFG_RX_BB_IDX = BIT(0),
- IWL_CALIB_CFG_DC_IDX = BIT(1),
- IWL_CALIB_CFG_LO_IDX = BIT(2),
- IWL_CALIB_CFG_TX_IQ_IDX = BIT(3),
- IWL_CALIB_CFG_RX_IQ_IDX = BIT(4),
- IWL_CALIB_CFG_NOISE_IDX = BIT(5),
- IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6),
- IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7),
- IWL_CALIB_CFG_PAPD_IDX = BIT(8),
- IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9),
- IWL_CALIB_CFG_TX_PWR_IDX = BIT(10),
-};
-
-#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \
- IWL_CALIB_CFG_DC_IDX | \
- IWL_CALIB_CFG_LO_IDX | \
- IWL_CALIB_CFG_TX_IQ_IDX | \
- IWL_CALIB_CFG_RX_IQ_IDX | \
- IWL_CALIB_CFG_CRYSTAL_IDX)
-
-#define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \
- IWL_CALIB_CFG_DC_IDX | \
- IWL_CALIB_CFG_LO_IDX | \
- IWL_CALIB_CFG_TX_IQ_IDX | \
- IWL_CALIB_CFG_RX_IQ_IDX | \
- IWL_CALIB_CFG_TEMPERATURE_IDX | \
- IWL_CALIB_CFG_PAPD_IDX | \
- IWL_CALIB_CFG_TX_PWR_IDX | \
- IWL_CALIB_CFG_CRYSTAL_IDX)
-
-#define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0))
-
-struct iwl_calib_cfg_elmnt_s {
- __le32 is_enable;
- __le32 start;
- __le32 send_res;
- __le32 apply_res;
- __le32 reserved;
-} __packed;
-
-struct iwl_calib_cfg_status_s {
- struct iwl_calib_cfg_elmnt_s once;
- struct iwl_calib_cfg_elmnt_s perd;
- __le32 flags;
-} __packed;
-
-struct iwl_calib_cfg_cmd {
- struct iwl_calib_cfg_status_s ucd_calib_cfg;
- struct iwl_calib_cfg_status_s drv_calib_cfg;
- __le32 reserved1;
-} __packed;
-
-struct iwl_calib_hdr {
- u8 op_code;
- u8 first_group;
- u8 groups_num;
- u8 data_valid;
-} __packed;
-
-struct iwl_calib_cmd {
- struct iwl_calib_hdr hdr;
- u8 data[0];
-} __packed;
-
-struct iwl_calib_xtal_freq_cmd {
- struct iwl_calib_hdr hdr;
- u8 cap_pin1;
- u8 cap_pin2;
- u8 pad[2];
-} __packed;
-
-#define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700)
-struct iwl_calib_temperature_offset_cmd {
- struct iwl_calib_hdr hdr;
- __le16 radio_sensor_offset;
- __le16 reserved;
-} __packed;
-
-struct iwl_calib_temperature_offset_v2_cmd {
- struct iwl_calib_hdr hdr;
- __le16 radio_sensor_offset_high;
- __le16 radio_sensor_offset_low;
- __le16 burntVoltageRef;
- __le16 reserved;
-} __packed;
-
-/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
-struct iwl_calib_chain_noise_reset_cmd {
- struct iwl_calib_hdr hdr;
- u8 data[0];
-};
-
-/* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */
-struct iwl_calib_chain_noise_gain_cmd {
- struct iwl_calib_hdr hdr;
- u8 delta_gain_1;
- u8 delta_gain_2;
- u8 pad[2];
-} __packed;
-
-/******************************************************************************
- * (12)
- * Miscellaneous Commands:
- *
- *****************************************************************************/
-
-/*
- * LEDs Command & Response
- * REPLY_LEDS_CMD = 0x48 (command, has simple generic response)
- *
- * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field),
- * this command turns it on or off, or sets up a periodic blinking cycle.
- */
-struct iwl_led_cmd {
- __le32 interval; /* "interval" in uSec */
- u8 id; /* 1: Activity, 2: Link, 3: Tech */
- u8 off; /* # intervals off while blinking;
- * "0", with >0 "on" value, turns LED on */
- u8 on; /* # intervals on while blinking;
- * "0", regardless of "off", turns LED off */
- u8 reserved;
-} __packed;
-
-/*
- * station priority table entries
- * also used as potential "events" value for both
- * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD
- */
-
-/*
- * COEX events entry flag masks
- * RP - Requested Priority
- * WP - Win Medium Priority: priority assigned when the contention has been won
- */
-#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1)
-#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2)
-#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4)
-
-#define COEX_CU_UNASSOC_IDLE_RP 4
-#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4
-#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4
-#define COEX_CU_CALIBRATION_RP 4
-#define COEX_CU_PERIODIC_CALIBRATION_RP 4
-#define COEX_CU_CONNECTION_ESTAB_RP 4
-#define COEX_CU_ASSOCIATED_IDLE_RP 4
-#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4
-#define COEX_CU_ASSOC_AUTO_SCAN_RP 4
-#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4
-#define COEX_CU_RF_ON_RP 6
-#define COEX_CU_RF_OFF_RP 4
-#define COEX_CU_STAND_ALONE_DEBUG_RP 6
-#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4
-#define COEX_CU_RSRVD1_RP 4
-#define COEX_CU_RSRVD2_RP 4
-
-#define COEX_CU_UNASSOC_IDLE_WP 3
-#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3
-#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3
-#define COEX_CU_CALIBRATION_WP 3
-#define COEX_CU_PERIODIC_CALIBRATION_WP 3
-#define COEX_CU_CONNECTION_ESTAB_WP 3
-#define COEX_CU_ASSOCIATED_IDLE_WP 3
-#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3
-#define COEX_CU_ASSOC_AUTO_SCAN_WP 3
-#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3
-#define COEX_CU_RF_ON_WP 3
-#define COEX_CU_RF_OFF_WP 3
-#define COEX_CU_STAND_ALONE_DEBUG_WP 6
-#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3
-#define COEX_CU_RSRVD1_WP 3
-#define COEX_CU_RSRVD2_WP 3
-
-#define COEX_UNASSOC_IDLE_FLAGS 0
-#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
-#define COEX_UNASSOC_AUTO_SCAN_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
-#define COEX_CALIBRATION_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
-#define COEX_PERIODIC_CALIBRATION_FLAGS 0
-/*
- * COEX_CONNECTION_ESTAB:
- * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network.
- */
-#define COEX_CONNECTION_ESTAB_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
- COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
-#define COEX_ASSOCIATED_IDLE_FLAGS 0
-#define COEX_ASSOC_MANUAL_SCAN_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
-#define COEX_ASSOC_AUTO_SCAN_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
-#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0
-#define COEX_RF_ON_FLAGS 0
-#define COEX_RF_OFF_FLAGS 0
-#define COEX_STAND_ALONE_DEBUG_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
-#define COEX_IPAN_ASSOC_LEVEL_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
- COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
-#define COEX_RSRVD1_FLAGS 0
-#define COEX_RSRVD2_FLAGS 0
-/*
- * COEX_CU_RF_ON is the event wrapping all radio ownership.
- * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network.
- */
-#define COEX_CU_RF_ON_FLAGS \
- (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
- COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
- COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
-
-
-enum {
- /* un-association part */
- COEX_UNASSOC_IDLE = 0,
- COEX_UNASSOC_MANUAL_SCAN = 1,
- COEX_UNASSOC_AUTO_SCAN = 2,
- /* calibration */
- COEX_CALIBRATION = 3,
- COEX_PERIODIC_CALIBRATION = 4,
- /* connection */
- COEX_CONNECTION_ESTAB = 5,
- /* association part */
- COEX_ASSOCIATED_IDLE = 6,
- COEX_ASSOC_MANUAL_SCAN = 7,
- COEX_ASSOC_AUTO_SCAN = 8,
- COEX_ASSOC_ACTIVE_LEVEL = 9,
- /* RF ON/OFF */
- COEX_RF_ON = 10,
- COEX_RF_OFF = 11,
- COEX_STAND_ALONE_DEBUG = 12,
- /* IPAN */
- COEX_IPAN_ASSOC_LEVEL = 13,
- /* reserved */
- COEX_RSRVD1 = 14,
- COEX_RSRVD2 = 15,
- COEX_NUM_OF_EVENTS = 16
-};
-
-/*
- * Coexistence WIFI/WIMAX Command
- * COEX_PRIORITY_TABLE_CMD = 0x5a
- *
- */
-struct iwl_wimax_coex_event_entry {
- u8 request_prio;
- u8 win_medium_prio;
- u8 reserved;
- u8 flags;
-} __packed;
-
-/* COEX flag masks */
-
-/* Station table is valid */
-#define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1)
-/* UnMask wake up src at unassociated sleep */
-#define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4)
-/* UnMask wake up src at associated sleep */
-#define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8)
-/* Enable CoEx feature. */
-#define COEX_FLAGS_COEX_ENABLE_MSK (0x80)
-
-struct iwl_wimax_coex_cmd {
- u8 flags;
- u8 reserved[3];
- struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS];
-} __packed;
-
-/*
- * Coexistence MEDIUM NOTIFICATION
- * COEX_MEDIUM_NOTIFICATION = 0x5b
- *
- * notification from uCode to host to indicate medium changes
- *
- */
-/*
- * status field
- * bit 0 - 2: medium status
- * bit 3: medium change indication
- * bit 4 - 31: reserved
- */
-/* status option values, (0 - 2 bits) */
-#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */
-#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */
-#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */
-#define COEX_MEDIUM_MSK (0x7)
-
-/* send notification status (1 bit) */
-#define COEX_MEDIUM_CHANGED (0x8)
-#define COEX_MEDIUM_CHANGED_MSK (0x8)
-#define COEX_MEDIUM_SHIFT (3)
-
-struct iwl_coex_medium_notification {
- __le32 status;
- __le32 events;
-} __packed;
-
-/*
- * Coexistence EVENT Command
- * COEX_EVENT_CMD = 0x5c
- *
- * send from host to uCode for coex event request.
- */
-/* flags options */
-#define COEX_EVENT_REQUEST_MSK (0x1)
-
-struct iwl_coex_event_cmd {
- u8 flags;
- u8 event;
- __le16 reserved;
-} __packed;
-
-struct iwl_coex_event_resp {
- __le32 status;
-} __packed;
-
-
-/******************************************************************************
- * Bluetooth Coexistence commands
- *
- *****************************************************************************/
-
-/*
- * BT Status notification
- * REPLY_BT_COEX_PROFILE_NOTIF = 0xce
- */
-enum iwl_bt_coex_profile_traffic_load {
- IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0,
- IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1,
- IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2,
- IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3,
-/*
- * There are no more even though below is a u8, the
- * indication from the BT device only has two bits.
- */
-};
-
-#define BT_SESSION_ACTIVITY_1_UART_MSG 0x1
-#define BT_SESSION_ACTIVITY_2_UART_MSG 0x2
-
-/* BT UART message - Share Part (BT -> WiFi) */
-#define BT_UART_MSG_FRAME1MSGTYPE_POS (0)
-#define BT_UART_MSG_FRAME1MSGTYPE_MSK \
- (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS)
-#define BT_UART_MSG_FRAME1SSN_POS (3)
-#define BT_UART_MSG_FRAME1SSN_MSK \
- (0x3 << BT_UART_MSG_FRAME1SSN_POS)
-#define BT_UART_MSG_FRAME1UPDATEREQ_POS (5)
-#define BT_UART_MSG_FRAME1UPDATEREQ_MSK \
- (0x1 << BT_UART_MSG_FRAME1UPDATEREQ_POS)
-#define BT_UART_MSG_FRAME1RESERVED_POS (6)
-#define BT_UART_MSG_FRAME1RESERVED_MSK \
- (0x3 << BT_UART_MSG_FRAME1RESERVED_POS)
-
-#define BT_UART_MSG_FRAME2OPENCONNECTIONS_POS (0)
-#define BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK \
- (0x3 << BT_UART_MSG_FRAME2OPENCONNECTIONS_POS)
-#define BT_UART_MSG_FRAME2TRAFFICLOAD_POS (2)
-#define BT_UART_MSG_FRAME2TRAFFICLOAD_MSK \
- (0x3 << BT_UART_MSG_FRAME2TRAFFICLOAD_POS)
-#define BT_UART_MSG_FRAME2CHLSEQN_POS (4)
-#define BT_UART_MSG_FRAME2CHLSEQN_MSK \
- (0x1 << BT_UART_MSG_FRAME2CHLSEQN_POS)
-#define BT_UART_MSG_FRAME2INBAND_POS (5)
-#define BT_UART_MSG_FRAME2INBAND_MSK \
- (0x1 << BT_UART_MSG_FRAME2INBAND_POS)
-#define BT_UART_MSG_FRAME2RESERVED_POS (6)
-#define BT_UART_MSG_FRAME2RESERVED_MSK \
- (0x3 << BT_UART_MSG_FRAME2RESERVED_POS)
-
-#define BT_UART_MSG_FRAME3SCOESCO_POS (0)
-#define BT_UART_MSG_FRAME3SCOESCO_MSK \
- (0x1 << BT_UART_MSG_FRAME3SCOESCO_POS)
-#define BT_UART_MSG_FRAME3SNIFF_POS (1)
-#define BT_UART_MSG_FRAME3SNIFF_MSK \
- (0x1 << BT_UART_MSG_FRAME3SNIFF_POS)
-#define BT_UART_MSG_FRAME3A2DP_POS (2)
-#define BT_UART_MSG_FRAME3A2DP_MSK \
- (0x1 << BT_UART_MSG_FRAME3A2DP_POS)
-#define BT_UART_MSG_FRAME3ACL_POS (3)
-#define BT_UART_MSG_FRAME3ACL_MSK \
- (0x1 << BT_UART_MSG_FRAME3ACL_POS)
-#define BT_UART_MSG_FRAME3MASTER_POS (4)
-#define BT_UART_MSG_FRAME3MASTER_MSK \
- (0x1 << BT_UART_MSG_FRAME3MASTER_POS)
-#define BT_UART_MSG_FRAME3OBEX_POS (5)
-#define BT_UART_MSG_FRAME3OBEX_MSK \
- (0x1 << BT_UART_MSG_FRAME3OBEX_POS)
-#define BT_UART_MSG_FRAME3RESERVED_POS (6)
-#define BT_UART_MSG_FRAME3RESERVED_MSK \
- (0x3 << BT_UART_MSG_FRAME3RESERVED_POS)
-
-#define BT_UART_MSG_FRAME4IDLEDURATION_POS (0)
-#define BT_UART_MSG_FRAME4IDLEDURATION_MSK \
- (0x3F << BT_UART_MSG_FRAME4IDLEDURATION_POS)
-#define BT_UART_MSG_FRAME4RESERVED_POS (6)
-#define BT_UART_MSG_FRAME4RESERVED_MSK \
- (0x3 << BT_UART_MSG_FRAME4RESERVED_POS)
-
-#define BT_UART_MSG_FRAME5TXACTIVITY_POS (0)
-#define BT_UART_MSG_FRAME5TXACTIVITY_MSK \
- (0x3 << BT_UART_MSG_FRAME5TXACTIVITY_POS)
-#define BT_UART_MSG_FRAME5RXACTIVITY_POS (2)
-#define BT_UART_MSG_FRAME5RXACTIVITY_MSK \
- (0x3 << BT_UART_MSG_FRAME5RXACTIVITY_POS)
-#define BT_UART_MSG_FRAME5ESCORETRANSMIT_POS (4)
-#define BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK \
- (0x3 << BT_UART_MSG_FRAME5ESCORETRANSMIT_POS)
-#define BT_UART_MSG_FRAME5RESERVED_POS (6)
-#define BT_UART_MSG_FRAME5RESERVED_MSK \
- (0x3 << BT_UART_MSG_FRAME5RESERVED_POS)
-
-#define BT_UART_MSG_FRAME6SNIFFINTERVAL_POS (0)
-#define BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK \
- (0x1F << BT_UART_MSG_FRAME6SNIFFINTERVAL_POS)
-#define BT_UART_MSG_FRAME6DISCOVERABLE_POS (5)
-#define BT_UART_MSG_FRAME6DISCOVERABLE_MSK \
- (0x1 << BT_UART_MSG_FRAME6DISCOVERABLE_POS)
-#define BT_UART_MSG_FRAME6RESERVED_POS (6)
-#define BT_UART_MSG_FRAME6RESERVED_MSK \
- (0x3 << BT_UART_MSG_FRAME6RESERVED_POS)
-
-#define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0)
-#define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \
- (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS)
-#define BT_UART_MSG_FRAME7PAGE_POS (3)
-#define BT_UART_MSG_FRAME7PAGE_MSK \
- (0x1 << BT_UART_MSG_FRAME7PAGE_POS)
-#define BT_UART_MSG_FRAME7INQUIRY_POS (4)
-#define BT_UART_MSG_FRAME7INQUIRY_MSK \
- (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS)
-#define BT_UART_MSG_FRAME7CONNECTABLE_POS (5)
-#define BT_UART_MSG_FRAME7CONNECTABLE_MSK \
- (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS)
-#define BT_UART_MSG_FRAME7RESERVED_POS (6)
-#define BT_UART_MSG_FRAME7RESERVED_MSK \
- (0x3 << BT_UART_MSG_FRAME7RESERVED_POS)
-
-/* BT Session Activity 2 UART message (BT -> WiFi) */
-#define BT_UART_MSG_2_FRAME1RESERVED1_POS (5)
-#define BT_UART_MSG_2_FRAME1RESERVED1_MSK \
- (0x1<<BT_UART_MSG_2_FRAME1RESERVED1_POS)
-#define BT_UART_MSG_2_FRAME1RESERVED2_POS (6)
-#define BT_UART_MSG_2_FRAME1RESERVED2_MSK \
- (0x3<<BT_UART_MSG_2_FRAME1RESERVED2_POS)
-
-#define BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_POS (0)
-#define BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_MSK \
- (0x3F<<BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_POS)
-#define BT_UART_MSG_2_FRAME2RESERVED_POS (6)
-#define BT_UART_MSG_2_FRAME2RESERVED_MSK \
- (0x3<<BT_UART_MSG_2_FRAME2RESERVED_POS)
-
-#define BT_UART_MSG_2_FRAME3BRLASTTXPOWER_POS (0)
-#define BT_UART_MSG_2_FRAME3BRLASTTXPOWER_MSK \
- (0xF<<BT_UART_MSG_2_FRAME3BRLASTTXPOWER_POS)
-#define BT_UART_MSG_2_FRAME3INQPAGESRMODE_POS (4)
-#define BT_UART_MSG_2_FRAME3INQPAGESRMODE_MSK \
- (0x1<<BT_UART_MSG_2_FRAME3INQPAGESRMODE_POS)
-#define BT_UART_MSG_2_FRAME3LEMASTER_POS (5)
-#define BT_UART_MSG_2_FRAME3LEMASTER_MSK \
- (0x1<<BT_UART_MSG_2_FRAME3LEMASTER_POS)
-#define BT_UART_MSG_2_FRAME3RESERVED_POS (6)
-#define BT_UART_MSG_2_FRAME3RESERVED_MSK \
- (0x3<<BT_UART_MSG_2_FRAME3RESERVED_POS)
-
-#define BT_UART_MSG_2_FRAME4LELASTTXPOWER_POS (0)
-#define BT_UART_MSG_2_FRAME4LELASTTXPOWER_MSK \
- (0xF<<BT_UART_MSG_2_FRAME4LELASTTXPOWER_POS)
-#define BT_UART_MSG_2_FRAME4NUMLECONN_POS (4)
-#define BT_UART_MSG_2_FRAME4NUMLECONN_MSK \
- (0x3<<BT_UART_MSG_2_FRAME4NUMLECONN_POS)
-#define BT_UART_MSG_2_FRAME4RESERVED_POS (6)
-#define BT_UART_MSG_2_FRAME4RESERVED_MSK \
- (0x3<<BT_UART_MSG_2_FRAME4RESERVED_POS)
-
-#define BT_UART_MSG_2_FRAME5BTMINRSSI_POS (0)
-#define BT_UART_MSG_2_FRAME5BTMINRSSI_MSK \
- (0xF<<BT_UART_MSG_2_FRAME5BTMINRSSI_POS)
-#define BT_UART_MSG_2_FRAME5LESCANINITMODE_POS (4)
-#define BT_UART_MSG_2_FRAME5LESCANINITMODE_MSK \
- (0x1<<BT_UART_MSG_2_FRAME5LESCANINITMODE_POS)
-#define BT_UART_MSG_2_FRAME5LEADVERMODE_POS (5)
-#define BT_UART_MSG_2_FRAME5LEADVERMODE_MSK \
- (0x1<<BT_UART_MSG_2_FRAME5LEADVERMODE_POS)
-#define BT_UART_MSG_2_FRAME5RESERVED_POS (6)
-#define BT_UART_MSG_2_FRAME5RESERVED_MSK \
- (0x3<<BT_UART_MSG_2_FRAME5RESERVED_POS)
-
-#define BT_UART_MSG_2_FRAME6LECONNINTERVAL_POS (0)
-#define BT_UART_MSG_2_FRAME6LECONNINTERVAL_MSK \
- (0x1F<<BT_UART_MSG_2_FRAME6LECONNINTERVAL_POS)
-#define BT_UART_MSG_2_FRAME6RFU_POS (5)
-#define BT_UART_MSG_2_FRAME6RFU_MSK \
- (0x1<<BT_UART_MSG_2_FRAME6RFU_POS)
-#define BT_UART_MSG_2_FRAME6RESERVED_POS (6)
-#define BT_UART_MSG_2_FRAME6RESERVED_MSK \
- (0x3<<BT_UART_MSG_2_FRAME6RESERVED_POS)
-
-#define BT_UART_MSG_2_FRAME7LECONNSLAVELAT_POS (0)
-#define BT_UART_MSG_2_FRAME7LECONNSLAVELAT_MSK \
- (0x7<<BT_UART_MSG_2_FRAME7LECONNSLAVELAT_POS)
-#define BT_UART_MSG_2_FRAME7LEPROFILE1_POS (3)
-#define BT_UART_MSG_2_FRAME7LEPROFILE1_MSK \
- (0x1<<BT_UART_MSG_2_FRAME7LEPROFILE1_POS)
-#define BT_UART_MSG_2_FRAME7LEPROFILE2_POS (4)
-#define BT_UART_MSG_2_FRAME7LEPROFILE2_MSK \
- (0x1<<BT_UART_MSG_2_FRAME7LEPROFILE2_POS)
-#define BT_UART_MSG_2_FRAME7LEPROFILEOTHER_POS (5)
-#define BT_UART_MSG_2_FRAME7LEPROFILEOTHER_MSK \
- (0x1<<BT_UART_MSG_2_FRAME7LEPROFILEOTHER_POS)
-#define BT_UART_MSG_2_FRAME7RESERVED_POS (6)
-#define BT_UART_MSG_2_FRAME7RESERVED_MSK \
- (0x3<<BT_UART_MSG_2_FRAME7RESERVED_POS)
-
-
-#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
-#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
-
-struct iwl_bt_uart_msg {
- u8 header;
- u8 frame1;
- u8 frame2;
- u8 frame3;
- u8 frame4;
- u8 frame5;
- u8 frame6;
- u8 frame7;
-} __packed;
-
-struct iwl_bt_coex_profile_notif {
- struct iwl_bt_uart_msg last_bt_uart_msg;
- u8 bt_status; /* 0 - off, 1 - on */
- u8 bt_traffic_load; /* 0 .. 3? */
- u8 bt_ci_compliance; /* 0 - not complied, 1 - complied */
- u8 reserved;
-} __packed;
-
-#define IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS 0
-#define IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_MSK 0x1
-#define IWL_BT_COEX_PRIO_TBL_PRIO_POS 1
-#define IWL_BT_COEX_PRIO_TBL_PRIO_MASK 0x0e
-#define IWL_BT_COEX_PRIO_TBL_RESERVED_POS 4
-#define IWL_BT_COEX_PRIO_TBL_RESERVED_MASK 0xf0
-#define IWL_BT_COEX_PRIO_TBL_PRIO_SHIFT 1
-
-/*
- * BT Coexistence Priority table
- * REPLY_BT_COEX_PRIO_TABLE = 0xcc
- */
-enum bt_coex_prio_table_events {
- BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
- BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1,
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2,
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3, /* DC calib */
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4,
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5,
- BT_COEX_PRIO_TBL_EVT_DTIM = 6,
- BT_COEX_PRIO_TBL_EVT_SCAN52 = 7,
- BT_COEX_PRIO_TBL_EVT_SCAN24 = 8,
- BT_COEX_PRIO_TBL_EVT_RESERVED0 = 9,
- BT_COEX_PRIO_TBL_EVT_RESERVED1 = 10,
- BT_COEX_PRIO_TBL_EVT_RESERVED2 = 11,
- BT_COEX_PRIO_TBL_EVT_RESERVED3 = 12,
- BT_COEX_PRIO_TBL_EVT_RESERVED4 = 13,
- BT_COEX_PRIO_TBL_EVT_RESERVED5 = 14,
- BT_COEX_PRIO_TBL_EVT_RESERVED6 = 15,
- /* BT_COEX_PRIO_TBL_EVT_MAX should always be last */
- BT_COEX_PRIO_TBL_EVT_MAX,
-};
-
-enum bt_coex_prio_table_priorities {
- BT_COEX_PRIO_TBL_DISABLED = 0,
- BT_COEX_PRIO_TBL_PRIO_LOW = 1,
- BT_COEX_PRIO_TBL_PRIO_HIGH = 2,
- BT_COEX_PRIO_TBL_PRIO_BYPASS = 3,
- BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4,
- BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5,
- BT_COEX_PRIO_TBL_PRIO_RSRVD1 = 6,
- BT_COEX_PRIO_TBL_PRIO_RSRVD2 = 7,
- BT_COEX_PRIO_TBL_MAX,
-};
-
-struct iwl_bt_coex_prio_table_cmd {
- u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
-} __packed;
-
-#define IWL_BT_COEX_ENV_CLOSE 0
-#define IWL_BT_COEX_ENV_OPEN 1
-/*
- * BT Protection Envelope
- * REPLY_BT_COEX_PROT_ENV = 0xcd
- */
-struct iwl_bt_coex_prot_env_cmd {
- u8 action; /* 0 = closed, 1 = open */
- u8 type; /* 0 .. 15 */
- u8 reserved[2];
-} __packed;
-
-/*
- * REPLY_D3_CONFIG
- */
-enum iwlagn_d3_wakeup_filters {
- IWLAGN_D3_WAKEUP_RFKILL = BIT(0),
- IWLAGN_D3_WAKEUP_SYSASSERT = BIT(1),
-};
-
-struct iwlagn_d3_config_cmd {
- __le32 min_sleep_time;
- __le32 wakeup_flags;
-} __packed;
-
-/*
- * REPLY_WOWLAN_PATTERNS
- */
-#define IWLAGN_WOWLAN_MIN_PATTERN_LEN 16
-#define IWLAGN_WOWLAN_MAX_PATTERN_LEN 128
-
-struct iwlagn_wowlan_pattern {
- u8 mask[IWLAGN_WOWLAN_MAX_PATTERN_LEN / 8];
- u8 pattern[IWLAGN_WOWLAN_MAX_PATTERN_LEN];
- u8 mask_size;
- u8 pattern_size;
- __le16 reserved;
-} __packed;
-
-#define IWLAGN_WOWLAN_MAX_PATTERNS 20
-
-struct iwlagn_wowlan_patterns_cmd {
- __le32 n_patterns;
- struct iwlagn_wowlan_pattern patterns[];
-} __packed;
-
-/*
- * REPLY_WOWLAN_WAKEUP_FILTER
- */
-enum iwlagn_wowlan_wakeup_filters {
- IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0),
- IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1),
- IWLAGN_WOWLAN_WAKEUP_BEACON_MISS = BIT(2),
- IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3),
- IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4),
- IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5),
- IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6),
- IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(7),
- IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(8),
-};
-
-struct iwlagn_wowlan_wakeup_filter_cmd {
- __le32 enabled;
- __le16 non_qos_seq;
- __le16 reserved;
- __le16 qos_seq[8];
-};
-
-/*
- * REPLY_WOWLAN_TSC_RSC_PARAMS
- */
-#define IWLAGN_NUM_RSC 16
-
-struct tkip_sc {
- __le16 iv16;
- __le16 pad;
- __le32 iv32;
-} __packed;
-
-struct iwlagn_tkip_rsc_tsc {
- struct tkip_sc unicast_rsc[IWLAGN_NUM_RSC];
- struct tkip_sc multicast_rsc[IWLAGN_NUM_RSC];
- struct tkip_sc tsc;
-} __packed;
-
-struct aes_sc {
- __le64 pn;
-} __packed;
-
-struct iwlagn_aes_rsc_tsc {
- struct aes_sc unicast_rsc[IWLAGN_NUM_RSC];
- struct aes_sc multicast_rsc[IWLAGN_NUM_RSC];
- struct aes_sc tsc;
-} __packed;
-
-union iwlagn_all_tsc_rsc {
- struct iwlagn_tkip_rsc_tsc tkip;
- struct iwlagn_aes_rsc_tsc aes;
-};
-
-struct iwlagn_wowlan_rsc_tsc_params_cmd {
- union iwlagn_all_tsc_rsc all_tsc_rsc;
-} __packed;
-
-/*
- * REPLY_WOWLAN_TKIP_PARAMS
- */
-#define IWLAGN_MIC_KEY_SIZE 8
-#define IWLAGN_P1K_SIZE 5
-struct iwlagn_mic_keys {
- u8 tx[IWLAGN_MIC_KEY_SIZE];
- u8 rx_unicast[IWLAGN_MIC_KEY_SIZE];
- u8 rx_mcast[IWLAGN_MIC_KEY_SIZE];
-} __packed;
-
-struct iwlagn_p1k_cache {
- __le16 p1k[IWLAGN_P1K_SIZE];
-} __packed;
-
-#define IWLAGN_NUM_RX_P1K_CACHE 2
-
-struct iwlagn_wowlan_tkip_params_cmd {
- struct iwlagn_mic_keys mic_keys;
- struct iwlagn_p1k_cache tx;
- struct iwlagn_p1k_cache rx_uni[IWLAGN_NUM_RX_P1K_CACHE];
- struct iwlagn_p1k_cache rx_multi[IWLAGN_NUM_RX_P1K_CACHE];
-} __packed;
-
-/*
- * REPLY_WOWLAN_KEK_KCK_MATERIAL
- */
-
-#define IWLAGN_KCK_MAX_SIZE 32
-#define IWLAGN_KEK_MAX_SIZE 32
-
-struct iwlagn_wowlan_kek_kck_material_cmd {
- u8 kck[IWLAGN_KCK_MAX_SIZE];
- u8 kek[IWLAGN_KEK_MAX_SIZE];
- __le16 kck_len;
- __le16 kek_len;
- __le64 replay_ctr;
-} __packed;
-
-#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87
-
-/*
- * REPLY_WOWLAN_GET_STATUS = 0xe5
- */
-struct iwlagn_wowlan_status {
- __le64 replay_ctr;
- __le32 rekey_status;
- __le32 wakeup_reason;
- u8 pattern_number;
- u8 reserved1;
- __le16 qos_seq_ctr[8];
- __le16 non_qos_seq_ctr;
- __le16 reserved2;
- union iwlagn_all_tsc_rsc tsc_rsc;
- __le16 reserved3;
-} __packed;
-
-/*
- * REPLY_WIPAN_PARAMS = 0xb2 (Commands and Notification)
- */
-
-/*
- * Minimum slot time in TU
- */
-#define IWL_MIN_SLOT_TIME 20
-
-/**
- * struct iwl_wipan_slot
- * @width: Time in TU
- * @type:
- * 0 - BSS
- * 1 - PAN
- */
-struct iwl_wipan_slot {
- __le16 width;
- u8 type;
- u8 reserved;
-} __packed;
-
-#define IWL_WIPAN_PARAMS_FLG_LEAVE_CHANNEL_CTS BIT(1) /* reserved */
-#define IWL_WIPAN_PARAMS_FLG_LEAVE_CHANNEL_QUIET BIT(2) /* reserved */
-#define IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE BIT(3) /* reserved */
-#define IWL_WIPAN_PARAMS_FLG_FILTER_BEACON_NOTIF BIT(4)
-#define IWL_WIPAN_PARAMS_FLG_FULL_SLOTTED_MODE BIT(5)
-
-/**
- * struct iwl_wipan_params_cmd
- * @flags:
- * bit0: reserved
- * bit1: CP leave channel with CTS
- * bit2: CP leave channel qith Quiet
- * bit3: slotted mode
- * 1 - work in slotted mode
- * 0 - work in non slotted mode
- * bit4: filter beacon notification
- * bit5: full tx slotted mode. if this flag is set,
- * uCode will perform leaving channel methods in context switch
- * also when working in same channel mode
- * @num_slots: 1 - 10
- */
-struct iwl_wipan_params_cmd {
- __le16 flags;
- u8 reserved;
- u8 num_slots;
- struct iwl_wipan_slot slots[10];
-} __packed;
-
-/*
- * REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9
- *
- * TODO: Figure out what this is used for,
- * it can only switch between 2.4 GHz
- * channels!!
- */
-
-struct iwl_wipan_p2p_channel_switch_cmd {
- __le16 channel;
- __le16 reserved;
-};
-
-/*
- * REPLY_WIPAN_NOA_NOTIFICATION = 0xbc
- *
- * This is used by the device to notify us of the
- * NoA schedule it determined so we can forward it
- * to userspace for inclusion in probe responses.
- *
- * In beacons, the NoA schedule is simply appended
- * to the frame we give the device.
- */
-
-struct iwl_wipan_noa_descriptor {
- u8 count;
- __le32 duration;
- __le32 interval;
- __le32 starttime;
-} __packed;
-
-struct iwl_wipan_noa_attribute {
- u8 id;
- __le16 length;
- u8 index;
- u8 ct_window;
- struct iwl_wipan_noa_descriptor descr0, descr1;
- u8 reserved;
-} __packed;
-
-struct iwl_wipan_noa_notification {
- u32 noa_active;
- struct iwl_wipan_noa_attribute noa_attribute;
-} __packed;
-
-#endif /* __iwl_commands_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c
deleted file mode 100644
index b15e44f8d1bd..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c
+++ /dev/null
@@ -1,2441 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/ieee80211.h>
-#include <net/mac80211.h>
-#include "iwl-debug.h"
-#include "iwl-io.h"
-#include "dev.h"
-#include "agn.h"
-
-/* create and remove of files */
-#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
- if (!debugfs_create_file(#name, mode, parent, priv, \
- &iwl_dbgfs_##name##_ops)) \
- goto err; \
-} while (0)
-
-#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
- struct dentry *__tmp; \
- __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \
- parent, ptr); \
- if (IS_ERR(__tmp) || !__tmp) \
- goto err; \
-} while (0)
-
-#define DEBUGFS_ADD_X32(name, parent, ptr) do { \
- struct dentry *__tmp; \
- __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \
- parent, ptr); \
- if (IS_ERR(__tmp) || !__tmp) \
- goto err; \
-} while (0)
-
-#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \
- struct dentry *__tmp; \
- __tmp = debugfs_create_u32(#name, mode, \
- parent, ptr); \
- if (IS_ERR(__tmp) || !__tmp) \
- goto err; \
-} while (0)
-
-/* file operation */
-#define DEBUGFS_READ_FILE_OPS(name) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .read = iwl_dbgfs_##name##_read, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
-
-#define DEBUGFS_WRITE_FILE_OPS(name) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .write = iwl_dbgfs_##name##_write, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
-
-
-#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .write = iwl_dbgfs_##name##_write, \
- .read = iwl_dbgfs_##name##_read, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
-
-static ssize_t iwl_dbgfs_sram_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- u32 val = 0;
- char *buf;
- ssize_t ret;
- int i = 0;
- bool device_format = false;
- int offset = 0;
- int len = 0;
- int pos = 0;
- int sram;
- struct iwl_priv *priv = file->private_data;
- const struct fw_img *img;
- size_t bufsz;
-
- if (!iwl_is_ready_rf(priv))
- return -EAGAIN;
-
- /* default is to dump the entire data segment */
- if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) {
- priv->dbgfs_sram_offset = 0x800000;
- if (!priv->ucode_loaded)
- return -EINVAL;
- img = &priv->fw->img[priv->cur_ucode];
- priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
- }
- len = priv->dbgfs_sram_len;
-
- if (len == -4) {
- device_format = true;
- len = 4;
- }
-
- bufsz = 50 + len * 4;
- buf = kmalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n",
- len);
- pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
- priv->dbgfs_sram_offset);
-
- /* adjust sram address since reads are only on even u32 boundaries */
- offset = priv->dbgfs_sram_offset & 0x3;
- sram = priv->dbgfs_sram_offset & ~0x3;
-
- /* read the first u32 from sram */
- val = iwl_trans_read_mem32(priv->trans, sram);
-
- for (; len; len--) {
- /* put the address at the start of every line */
- if (i == 0)
- pos += scnprintf(buf + pos, bufsz - pos,
- "%08X: ", sram + offset);
-
- if (device_format)
- pos += scnprintf(buf + pos, bufsz - pos,
- "%02x", (val >> (8 * (3 - offset))) & 0xff);
- else
- pos += scnprintf(buf + pos, bufsz - pos,
- "%02x ", (val >> (8 * offset)) & 0xff);
-
- /* if all bytes processed, read the next u32 from sram */
- if (++offset == 4) {
- sram += 4;
- offset = 0;
- val = iwl_trans_read_mem32(priv->trans, sram);
- }
-
- /* put in extra spaces and split lines for human readability */
- if (++i == 16) {
- i = 0;
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- } else if (!(i & 7)) {
- pos += scnprintf(buf + pos, bufsz - pos, " ");
- } else if (!(i & 3)) {
- pos += scnprintf(buf + pos, bufsz - pos, " ");
- }
- }
- if (i)
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_sram_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[64];
- int buf_size;
- u32 offset, len;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
- priv->dbgfs_sram_offset = offset;
- priv->dbgfs_sram_len = len;
- } else if (sscanf(buf, "%x", &offset) == 1) {
- priv->dbgfs_sram_offset = offset;
- priv->dbgfs_sram_len = -4;
- } else {
- priv->dbgfs_sram_offset = 0;
- priv->dbgfs_sram_len = 0;
- }
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN];
-
- if (!priv->wowlan_sram)
- return -ENODATA;
-
- return simple_read_from_buffer(user_buf, count, ppos,
- priv->wowlan_sram,
- img->sec[IWL_UCODE_SECTION_DATA].len);
-}
-static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- struct iwl_station_entry *station;
- struct iwl_tid_data *tid_data;
- char *buf;
- int i, j, pos = 0;
- ssize_t ret;
- /* Add 30 for initial string */
- const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations);
-
- buf = kmalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n",
- priv->num_stations);
-
- for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
- station = &priv->stations[i];
- if (!station->used)
- continue;
- pos += scnprintf(buf + pos, bufsz - pos,
- "station %d - addr: %pM, flags: %#x\n",
- i, station->sta.sta.addr,
- station->sta.station_flags_msk);
- pos += scnprintf(buf + pos, bufsz - pos,
- "TID seqno next_rclmd "
- "rate_n_flags state txq\n");
-
- for (j = 0; j < IWL_MAX_TID_COUNT; j++) {
- tid_data = &priv->tid_data[i][j];
- pos += scnprintf(buf + pos, bufsz - pos,
- "%d: 0x%.4x 0x%.4x 0x%.8x "
- "%d %.2d",
- j, tid_data->seq_number,
- tid_data->next_reclaimed,
- tid_data->agg.rate_n_flags,
- tid_data->agg.state,
- tid_data->agg.txq_id);
-
- if (tid_data->agg.wait_for_ba)
- pos += scnprintf(buf + pos, bufsz - pos,
- " - waitforba");
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- }
-
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- }
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_nvm_read(struct file *file,
- char __user *user_buf,
- size_t count,
- loff_t *ppos)
-{
- ssize_t ret;
- struct iwl_priv *priv = file->private_data;
- int pos = 0, ofs = 0, buf_size = 0;
- const u8 *ptr;
- char *buf;
- u16 nvm_ver;
- size_t eeprom_len = priv->eeprom_blob_size;
- buf_size = 4 * eeprom_len + 256;
-
- if (eeprom_len % 16)
- return -ENODATA;
-
- ptr = priv->eeprom_blob;
- if (!ptr)
- return -ENOMEM;
-
- /* 4 characters for byte 0xYY */
- buf = kzalloc(buf_size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- nvm_ver = priv->nvm_data->nvm_version;
- pos += scnprintf(buf + pos, buf_size - pos,
- "NVM version: 0x%x\n", nvm_ver);
- for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) {
- pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x %16ph\n",
- ofs, ptr + ofs);
- }
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- struct ieee80211_channel *channels = NULL;
- const struct ieee80211_supported_band *supp_band = NULL;
- int pos = 0, i, bufsz = PAGE_SIZE;
- char *buf;
- ssize_t ret;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ);
- if (supp_band) {
- channels = supp_band->channels;
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Displaying %d channels in 2.4GHz band 802.11bg):\n",
- supp_band->n_channels);
-
- for (i = 0; i < supp_band->n_channels; i++)
- pos += scnprintf(buf + pos, bufsz - pos,
- "%d: %ddBm: BSS%s%s, %s.\n",
- channels[i].hw_value,
- channels[i].max_power,
- channels[i].flags & IEEE80211_CHAN_RADAR ?
- " (IEEE 802.11h required)" : "",
- ((channels[i].flags & IEEE80211_CHAN_NO_IR)
- || (channels[i].flags &
- IEEE80211_CHAN_RADAR)) ? "" :
- ", IBSS",
- channels[i].flags &
- IEEE80211_CHAN_NO_IR ?
- "passive only" : "active/passive");
- }
- supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ);
- if (supp_band) {
- channels = supp_band->channels;
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Displaying %d channels in 5.2GHz band (802.11a)\n",
- supp_band->n_channels);
-
- for (i = 0; i < supp_band->n_channels; i++)
- pos += scnprintf(buf + pos, bufsz - pos,
- "%d: %ddBm: BSS%s%s, %s.\n",
- channels[i].hw_value,
- channels[i].max_power,
- channels[i].flags & IEEE80211_CHAN_RADAR ?
- " (IEEE 802.11h required)" : "",
- ((channels[i].flags & IEEE80211_CHAN_NO_IR)
- || (channels[i].flags &
- IEEE80211_CHAN_RADAR)) ? "" :
- ", IBSS",
- channels[i].flags &
- IEEE80211_CHAN_NO_IR ?
- "passive only" : "active/passive");
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_status_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- char buf[512];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
- test_bit(STATUS_RF_KILL_HW, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n",
- test_bit(STATUS_CT_KILL, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n",
- test_bit(STATUS_ALIVE, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n",
- test_bit(STATUS_READY, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n",
- test_bit(STATUS_EXIT_PENDING, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n",
- test_bit(STATUS_STATISTICS, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n",
- test_bit(STATUS_SCANNING, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n",
- test_bit(STATUS_SCAN_ABORTING, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n",
- test_bit(STATUS_SCAN_HW, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n",
- test_bit(STATUS_POWER_PMI, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n",
- test_bit(STATUS_FW_ERROR, &priv->status));
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
-
- int pos = 0;
- int cnt = 0;
- char *buf;
- int bufsz = 24 * 64; /* 24 items * 64 char per item */
- ssize_t ret;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- for (cnt = 0; cnt < REPLY_MAX; cnt++) {
- if (priv->rx_handlers_stats[cnt] > 0)
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tRx handler[%36s]:\t\t %u\n",
- iwl_dvm_get_cmd_string(cnt),
- priv->rx_handlers_stats[cnt]);
- }
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
-
- char buf[8];
- int buf_size;
- u32 reset_flag;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%x", &reset_flag) != 1)
- return -EFAULT;
- if (reset_flag == 0)
- memset(&priv->rx_handlers_stats[0], 0,
- sizeof(priv->rx_handlers_stats));
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- struct iwl_rxon_context *ctx;
- int pos = 0, i;
- char buf[256 * NUM_IWL_RXON_CTX];
- const size_t bufsz = sizeof(buf);
-
- for_each_context(priv, ctx) {
- pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n",
- ctx->ctxid);
- for (i = 0; i < AC_NUM; i++) {
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tcw_min\tcw_max\taifsn\ttxop\n");
- pos += scnprintf(buf + pos, bufsz - pos,
- "AC[%d]\t%u\t%u\t%u\t%u\n", i,
- ctx->qos_data.def_qos_parm.ac[i].cw_min,
- ctx->qos_data.def_qos_parm.ac[i].cw_max,
- ctx->qos_data.def_qos_parm.ac[i].aifsn,
- ctx->qos_data.def_qos_parm.ac[i].edca_txop);
- }
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- }
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- struct iwl_tt_restriction *restriction;
- char buf[100];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Thermal Throttling Mode: %s\n",
- tt->advanced_tt ? "Advance" : "Legacy");
- pos += scnprintf(buf + pos, bufsz - pos,
- "Thermal Throttling State: %d\n",
- tt->state);
- if (tt->advanced_tt) {
- restriction = tt->restriction + tt->state;
- pos += scnprintf(buf + pos, bufsz - pos,
- "Tx mode: %d\n",
- restriction->tx_stream);
- pos += scnprintf(buf + pos, bufsz - pos,
- "Rx mode: %d\n",
- restriction->rx_stream);
- pos += scnprintf(buf + pos, bufsz - pos,
- "HT mode: %d\n",
- restriction->is_ht);
- }
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int ht40;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &ht40) != 1)
- return -EFAULT;
- if (!iwl_is_any_associated(priv))
- priv->disable_ht40 = ht40 ? true : false;
- else
- return -EINVAL;
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[100];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "11n 40MHz Mode: %s\n",
- priv->disable_ht40 ? "Disabled" : "Enabled");
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_temperature_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature);
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-
-static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int value;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- if (sscanf(buf, "%d", &value) != 1)
- return -EINVAL;
-
- /*
- * Our users expect 0 to be "CAM", but 0 isn't actually
- * valid here. However, let's not confuse them and present
- * IWL_POWER_INDEX_1 as "1", not "0".
- */
- if (value == 0)
- return -EINVAL;
- else if (value > 0)
- value -= 1;
-
- if (value != -1 && (value < 0 || value >= IWL_POWER_NUM))
- return -EINVAL;
-
- if (!iwl_is_ready_rf(priv))
- return -EAGAIN;
-
- priv->power_data.debug_sleep_level_override = value;
-
- mutex_lock(&priv->mutex);
- iwl_power_update_mode(priv, true);
- mutex_unlock(&priv->mutex);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[10];
- int pos, value;
- const size_t bufsz = sizeof(buf);
-
- /* see the write function */
- value = priv->power_data.debug_sleep_level_override;
- if (value >= 0)
- value += 1;
-
- pos = scnprintf(buf, bufsz, "%d\n", value);
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[200];
- int pos = 0, i;
- const size_t bufsz = sizeof(buf);
- struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd;
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "flags: %#.2x\n", le16_to_cpu(cmd->flags));
- pos += scnprintf(buf + pos, bufsz - pos,
- "RX/TX timeout: %d/%d usec\n",
- le32_to_cpu(cmd->rx_data_timeout),
- le32_to_cpu(cmd->tx_data_timeout));
- for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
- pos += scnprintf(buf + pos, bufsz - pos,
- "sleep_interval[%d]: %d\n", i,
- le32_to_cpu(cmd->sleep_interval[i]));
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-DEBUGFS_READ_WRITE_FILE_OPS(sram);
-DEBUGFS_READ_FILE_OPS(wowlan_sram);
-DEBUGFS_READ_FILE_OPS(nvm);
-DEBUGFS_READ_FILE_OPS(stations);
-DEBUGFS_READ_FILE_OPS(channels);
-DEBUGFS_READ_FILE_OPS(status);
-DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers);
-DEBUGFS_READ_FILE_OPS(qos);
-DEBUGFS_READ_FILE_OPS(thermal_throttling);
-DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40);
-DEBUGFS_READ_FILE_OPS(temperature);
-DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override);
-DEBUGFS_READ_FILE_OPS(current_sleep_command);
-
-static const char *fmt_value = " %-30s %10u\n";
-static const char *fmt_hex = " %-30s 0x%02X\n";
-static const char *fmt_table = " %-30s %10u %10u %10u %10u\n";
-static const char *fmt_header =
- "%-32s current cumulative delta max\n";
-
-static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz)
-{
- int p = 0;
- u32 flag;
-
- lockdep_assert_held(&priv->statistics.lock);
-
- flag = le32_to_cpu(priv->statistics.flag);
-
- p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag);
- if (flag & UCODE_STATISTICS_CLEAR_MSK)
- p += scnprintf(buf + p, bufsz - p,
- "\tStatistics have been cleared\n");
- p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n",
- (flag & UCODE_STATISTICS_FREQUENCY_MSK)
- ? "2.4 GHz" : "5.2 GHz");
- p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n",
- (flag & UCODE_STATISTICS_NARROW_BAND_MSK)
- ? "enabled" : "disabled");
-
- return p;
-}
-
-static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char *buf;
- int bufsz = sizeof(struct statistics_rx_phy) * 40 +
- sizeof(struct statistics_rx_non_phy) * 40 +
- sizeof(struct statistics_rx_ht_phy) * 40 + 400;
- ssize_t ret;
- struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm;
- struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck;
- struct statistics_rx_non_phy *general, *accum_general;
- struct statistics_rx_non_phy *delta_general, *max_general;
- struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht;
-
- if (!iwl_is_alive(priv))
- return -EAGAIN;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /*
- * the statistic information display here is based on
- * the last statistics notification from uCode
- * might not reflect the current uCode activity
- */
- spin_lock_bh(&priv->statistics.lock);
- ofdm = &priv->statistics.rx_ofdm;
- cck = &priv->statistics.rx_cck;
- general = &priv->statistics.rx_non_phy;
- ht = &priv->statistics.rx_ofdm_ht;
- accum_ofdm = &priv->accum_stats.rx_ofdm;
- accum_cck = &priv->accum_stats.rx_cck;
- accum_general = &priv->accum_stats.rx_non_phy;
- accum_ht = &priv->accum_stats.rx_ofdm_ht;
- delta_ofdm = &priv->delta_stats.rx_ofdm;
- delta_cck = &priv->delta_stats.rx_cck;
- delta_general = &priv->delta_stats.rx_non_phy;
- delta_ht = &priv->delta_stats.rx_ofdm_ht;
- max_ofdm = &priv->max_delta_stats.rx_ofdm;
- max_cck = &priv->max_delta_stats.rx_cck;
- max_general = &priv->max_delta_stats.rx_non_phy;
- max_ht = &priv->max_delta_stats.rx_ofdm_ht;
-
- pos += iwl_statistics_flag(priv, buf, bufsz);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_header, "Statistics_Rx - OFDM:");
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "ina_cnt:",
- le32_to_cpu(ofdm->ina_cnt),
- accum_ofdm->ina_cnt,
- delta_ofdm->ina_cnt, max_ofdm->ina_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "fina_cnt:",
- le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt,
- delta_ofdm->fina_cnt, max_ofdm->fina_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "plcp_err:",
- le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err,
- delta_ofdm->plcp_err, max_ofdm->plcp_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "crc32_err:",
- le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err,
- delta_ofdm->crc32_err, max_ofdm->crc32_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "overrun_err:",
- le32_to_cpu(ofdm->overrun_err),
- accum_ofdm->overrun_err, delta_ofdm->overrun_err,
- max_ofdm->overrun_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "early_overrun_err:",
- le32_to_cpu(ofdm->early_overrun_err),
- accum_ofdm->early_overrun_err,
- delta_ofdm->early_overrun_err,
- max_ofdm->early_overrun_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "crc32_good:",
- le32_to_cpu(ofdm->crc32_good),
- accum_ofdm->crc32_good, delta_ofdm->crc32_good,
- max_ofdm->crc32_good);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "false_alarm_cnt:",
- le32_to_cpu(ofdm->false_alarm_cnt),
- accum_ofdm->false_alarm_cnt,
- delta_ofdm->false_alarm_cnt,
- max_ofdm->false_alarm_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "fina_sync_err_cnt:",
- le32_to_cpu(ofdm->fina_sync_err_cnt),
- accum_ofdm->fina_sync_err_cnt,
- delta_ofdm->fina_sync_err_cnt,
- max_ofdm->fina_sync_err_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sfd_timeout:",
- le32_to_cpu(ofdm->sfd_timeout),
- accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout,
- max_ofdm->sfd_timeout);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "fina_timeout:",
- le32_to_cpu(ofdm->fina_timeout),
- accum_ofdm->fina_timeout, delta_ofdm->fina_timeout,
- max_ofdm->fina_timeout);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "unresponded_rts:",
- le32_to_cpu(ofdm->unresponded_rts),
- accum_ofdm->unresponded_rts,
- delta_ofdm->unresponded_rts,
- max_ofdm->unresponded_rts);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "rxe_frame_lmt_ovrun:",
- le32_to_cpu(ofdm->rxe_frame_limit_overrun),
- accum_ofdm->rxe_frame_limit_overrun,
- delta_ofdm->rxe_frame_limit_overrun,
- max_ofdm->rxe_frame_limit_overrun);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sent_ack_cnt:",
- le32_to_cpu(ofdm->sent_ack_cnt),
- accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt,
- max_ofdm->sent_ack_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sent_cts_cnt:",
- le32_to_cpu(ofdm->sent_cts_cnt),
- accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt,
- max_ofdm->sent_cts_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sent_ba_rsp_cnt:",
- le32_to_cpu(ofdm->sent_ba_rsp_cnt),
- accum_ofdm->sent_ba_rsp_cnt,
- delta_ofdm->sent_ba_rsp_cnt,
- max_ofdm->sent_ba_rsp_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "dsp_self_kill:",
- le32_to_cpu(ofdm->dsp_self_kill),
- accum_ofdm->dsp_self_kill,
- delta_ofdm->dsp_self_kill,
- max_ofdm->dsp_self_kill);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "mh_format_err:",
- le32_to_cpu(ofdm->mh_format_err),
- accum_ofdm->mh_format_err,
- delta_ofdm->mh_format_err,
- max_ofdm->mh_format_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "re_acq_main_rssi_sum:",
- le32_to_cpu(ofdm->re_acq_main_rssi_sum),
- accum_ofdm->re_acq_main_rssi_sum,
- delta_ofdm->re_acq_main_rssi_sum,
- max_ofdm->re_acq_main_rssi_sum);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_header, "Statistics_Rx - CCK:");
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "ina_cnt:",
- le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt,
- delta_cck->ina_cnt, max_cck->ina_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "fina_cnt:",
- le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt,
- delta_cck->fina_cnt, max_cck->fina_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "plcp_err:",
- le32_to_cpu(cck->plcp_err), accum_cck->plcp_err,
- delta_cck->plcp_err, max_cck->plcp_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "crc32_err:",
- le32_to_cpu(cck->crc32_err), accum_cck->crc32_err,
- delta_cck->crc32_err, max_cck->crc32_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "overrun_err:",
- le32_to_cpu(cck->overrun_err),
- accum_cck->overrun_err, delta_cck->overrun_err,
- max_cck->overrun_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "early_overrun_err:",
- le32_to_cpu(cck->early_overrun_err),
- accum_cck->early_overrun_err,
- delta_cck->early_overrun_err,
- max_cck->early_overrun_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "crc32_good:",
- le32_to_cpu(cck->crc32_good), accum_cck->crc32_good,
- delta_cck->crc32_good, max_cck->crc32_good);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "false_alarm_cnt:",
- le32_to_cpu(cck->false_alarm_cnt),
- accum_cck->false_alarm_cnt,
- delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "fina_sync_err_cnt:",
- le32_to_cpu(cck->fina_sync_err_cnt),
- accum_cck->fina_sync_err_cnt,
- delta_cck->fina_sync_err_cnt,
- max_cck->fina_sync_err_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sfd_timeout:",
- le32_to_cpu(cck->sfd_timeout),
- accum_cck->sfd_timeout, delta_cck->sfd_timeout,
- max_cck->sfd_timeout);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "fina_timeout:",
- le32_to_cpu(cck->fina_timeout),
- accum_cck->fina_timeout, delta_cck->fina_timeout,
- max_cck->fina_timeout);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "unresponded_rts:",
- le32_to_cpu(cck->unresponded_rts),
- accum_cck->unresponded_rts, delta_cck->unresponded_rts,
- max_cck->unresponded_rts);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "rxe_frame_lmt_ovrun:",
- le32_to_cpu(cck->rxe_frame_limit_overrun),
- accum_cck->rxe_frame_limit_overrun,
- delta_cck->rxe_frame_limit_overrun,
- max_cck->rxe_frame_limit_overrun);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sent_ack_cnt:",
- le32_to_cpu(cck->sent_ack_cnt),
- accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt,
- max_cck->sent_ack_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sent_cts_cnt:",
- le32_to_cpu(cck->sent_cts_cnt),
- accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt,
- max_cck->sent_cts_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sent_ba_rsp_cnt:",
- le32_to_cpu(cck->sent_ba_rsp_cnt),
- accum_cck->sent_ba_rsp_cnt,
- delta_cck->sent_ba_rsp_cnt,
- max_cck->sent_ba_rsp_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "dsp_self_kill:",
- le32_to_cpu(cck->dsp_self_kill),
- accum_cck->dsp_self_kill, delta_cck->dsp_self_kill,
- max_cck->dsp_self_kill);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "mh_format_err:",
- le32_to_cpu(cck->mh_format_err),
- accum_cck->mh_format_err, delta_cck->mh_format_err,
- max_cck->mh_format_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "re_acq_main_rssi_sum:",
- le32_to_cpu(cck->re_acq_main_rssi_sum),
- accum_cck->re_acq_main_rssi_sum,
- delta_cck->re_acq_main_rssi_sum,
- max_cck->re_acq_main_rssi_sum);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_header, "Statistics_Rx - GENERAL:");
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "bogus_cts:",
- le32_to_cpu(general->bogus_cts),
- accum_general->bogus_cts, delta_general->bogus_cts,
- max_general->bogus_cts);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "bogus_ack:",
- le32_to_cpu(general->bogus_ack),
- accum_general->bogus_ack, delta_general->bogus_ack,
- max_general->bogus_ack);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "non_bssid_frames:",
- le32_to_cpu(general->non_bssid_frames),
- accum_general->non_bssid_frames,
- delta_general->non_bssid_frames,
- max_general->non_bssid_frames);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "filtered_frames:",
- le32_to_cpu(general->filtered_frames),
- accum_general->filtered_frames,
- delta_general->filtered_frames,
- max_general->filtered_frames);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "non_channel_beacons:",
- le32_to_cpu(general->non_channel_beacons),
- accum_general->non_channel_beacons,
- delta_general->non_channel_beacons,
- max_general->non_channel_beacons);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "channel_beacons:",
- le32_to_cpu(general->channel_beacons),
- accum_general->channel_beacons,
- delta_general->channel_beacons,
- max_general->channel_beacons);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "num_missed_bcon:",
- le32_to_cpu(general->num_missed_bcon),
- accum_general->num_missed_bcon,
- delta_general->num_missed_bcon,
- max_general->num_missed_bcon);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "adc_rx_saturation_time:",
- le32_to_cpu(general->adc_rx_saturation_time),
- accum_general->adc_rx_saturation_time,
- delta_general->adc_rx_saturation_time,
- max_general->adc_rx_saturation_time);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "ina_detect_search_tm:",
- le32_to_cpu(general->ina_detection_search_time),
- accum_general->ina_detection_search_time,
- delta_general->ina_detection_search_time,
- max_general->ina_detection_search_time);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_silence_rssi_a:",
- le32_to_cpu(general->beacon_silence_rssi_a),
- accum_general->beacon_silence_rssi_a,
- delta_general->beacon_silence_rssi_a,
- max_general->beacon_silence_rssi_a);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_silence_rssi_b:",
- le32_to_cpu(general->beacon_silence_rssi_b),
- accum_general->beacon_silence_rssi_b,
- delta_general->beacon_silence_rssi_b,
- max_general->beacon_silence_rssi_b);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_silence_rssi_c:",
- le32_to_cpu(general->beacon_silence_rssi_c),
- accum_general->beacon_silence_rssi_c,
- delta_general->beacon_silence_rssi_c,
- max_general->beacon_silence_rssi_c);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "interference_data_flag:",
- le32_to_cpu(general->interference_data_flag),
- accum_general->interference_data_flag,
- delta_general->interference_data_flag,
- max_general->interference_data_flag);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "channel_load:",
- le32_to_cpu(general->channel_load),
- accum_general->channel_load,
- delta_general->channel_load,
- max_general->channel_load);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "dsp_false_alarms:",
- le32_to_cpu(general->dsp_false_alarms),
- accum_general->dsp_false_alarms,
- delta_general->dsp_false_alarms,
- max_general->dsp_false_alarms);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_rssi_a:",
- le32_to_cpu(general->beacon_rssi_a),
- accum_general->beacon_rssi_a,
- delta_general->beacon_rssi_a,
- max_general->beacon_rssi_a);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_rssi_b:",
- le32_to_cpu(general->beacon_rssi_b),
- accum_general->beacon_rssi_b,
- delta_general->beacon_rssi_b,
- max_general->beacon_rssi_b);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_rssi_c:",
- le32_to_cpu(general->beacon_rssi_c),
- accum_general->beacon_rssi_c,
- delta_general->beacon_rssi_c,
- max_general->beacon_rssi_c);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_energy_a:",
- le32_to_cpu(general->beacon_energy_a),
- accum_general->beacon_energy_a,
- delta_general->beacon_energy_a,
- max_general->beacon_energy_a);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_energy_b:",
- le32_to_cpu(general->beacon_energy_b),
- accum_general->beacon_energy_b,
- delta_general->beacon_energy_b,
- max_general->beacon_energy_b);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "beacon_energy_c:",
- le32_to_cpu(general->beacon_energy_c),
- accum_general->beacon_energy_c,
- delta_general->beacon_energy_c,
- max_general->beacon_energy_c);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_header, "Statistics_Rx - OFDM_HT:");
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "plcp_err:",
- le32_to_cpu(ht->plcp_err), accum_ht->plcp_err,
- delta_ht->plcp_err, max_ht->plcp_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "overrun_err:",
- le32_to_cpu(ht->overrun_err), accum_ht->overrun_err,
- delta_ht->overrun_err, max_ht->overrun_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "early_overrun_err:",
- le32_to_cpu(ht->early_overrun_err),
- accum_ht->early_overrun_err,
- delta_ht->early_overrun_err,
- max_ht->early_overrun_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "crc32_good:",
- le32_to_cpu(ht->crc32_good), accum_ht->crc32_good,
- delta_ht->crc32_good, max_ht->crc32_good);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "crc32_err:",
- le32_to_cpu(ht->crc32_err), accum_ht->crc32_err,
- delta_ht->crc32_err, max_ht->crc32_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "mh_format_err:",
- le32_to_cpu(ht->mh_format_err),
- accum_ht->mh_format_err,
- delta_ht->mh_format_err, max_ht->mh_format_err);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg_crc32_good:",
- le32_to_cpu(ht->agg_crc32_good),
- accum_ht->agg_crc32_good,
- delta_ht->agg_crc32_good, max_ht->agg_crc32_good);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg_mpdu_cnt:",
- le32_to_cpu(ht->agg_mpdu_cnt),
- accum_ht->agg_mpdu_cnt,
- delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg_cnt:",
- le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt,
- delta_ht->agg_cnt, max_ht->agg_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "unsupport_mcs:",
- le32_to_cpu(ht->unsupport_mcs),
- accum_ht->unsupport_mcs,
- delta_ht->unsupport_mcs, max_ht->unsupport_mcs);
-
- spin_unlock_bh(&priv->statistics.lock);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char *buf;
- int bufsz = (sizeof(struct statistics_tx) * 48) + 250;
- ssize_t ret;
- struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx;
-
- if (!iwl_is_alive(priv))
- return -EAGAIN;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /* the statistic information display here is based on
- * the last statistics notification from uCode
- * might not reflect the current uCode activity
- */
- spin_lock_bh(&priv->statistics.lock);
-
- tx = &priv->statistics.tx;
- accum_tx = &priv->accum_stats.tx;
- delta_tx = &priv->delta_stats.tx;
- max_tx = &priv->max_delta_stats.tx;
-
- pos += iwl_statistics_flag(priv, buf, bufsz);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_header, "Statistics_Tx:");
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "preamble:",
- le32_to_cpu(tx->preamble_cnt),
- accum_tx->preamble_cnt,
- delta_tx->preamble_cnt, max_tx->preamble_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "rx_detected_cnt:",
- le32_to_cpu(tx->rx_detected_cnt),
- accum_tx->rx_detected_cnt,
- delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "bt_prio_defer_cnt:",
- le32_to_cpu(tx->bt_prio_defer_cnt),
- accum_tx->bt_prio_defer_cnt,
- delta_tx->bt_prio_defer_cnt,
- max_tx->bt_prio_defer_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "bt_prio_kill_cnt:",
- le32_to_cpu(tx->bt_prio_kill_cnt),
- accum_tx->bt_prio_kill_cnt,
- delta_tx->bt_prio_kill_cnt,
- max_tx->bt_prio_kill_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "few_bytes_cnt:",
- le32_to_cpu(tx->few_bytes_cnt),
- accum_tx->few_bytes_cnt,
- delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "cts_timeout:",
- le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout,
- delta_tx->cts_timeout, max_tx->cts_timeout);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "ack_timeout:",
- le32_to_cpu(tx->ack_timeout),
- accum_tx->ack_timeout,
- delta_tx->ack_timeout, max_tx->ack_timeout);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "expected_ack_cnt:",
- le32_to_cpu(tx->expected_ack_cnt),
- accum_tx->expected_ack_cnt,
- delta_tx->expected_ack_cnt,
- max_tx->expected_ack_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "actual_ack_cnt:",
- le32_to_cpu(tx->actual_ack_cnt),
- accum_tx->actual_ack_cnt,
- delta_tx->actual_ack_cnt,
- max_tx->actual_ack_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "dump_msdu_cnt:",
- le32_to_cpu(tx->dump_msdu_cnt),
- accum_tx->dump_msdu_cnt,
- delta_tx->dump_msdu_cnt,
- max_tx->dump_msdu_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "abort_nxt_frame_mismatch:",
- le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt),
- accum_tx->burst_abort_next_frame_mismatch_cnt,
- delta_tx->burst_abort_next_frame_mismatch_cnt,
- max_tx->burst_abort_next_frame_mismatch_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "abort_missing_nxt_frame:",
- le32_to_cpu(tx->burst_abort_missing_next_frame_cnt),
- accum_tx->burst_abort_missing_next_frame_cnt,
- delta_tx->burst_abort_missing_next_frame_cnt,
- max_tx->burst_abort_missing_next_frame_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "cts_timeout_collision:",
- le32_to_cpu(tx->cts_timeout_collision),
- accum_tx->cts_timeout_collision,
- delta_tx->cts_timeout_collision,
- max_tx->cts_timeout_collision);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "ack_ba_timeout_collision:",
- le32_to_cpu(tx->ack_or_ba_timeout_collision),
- accum_tx->ack_or_ba_timeout_collision,
- delta_tx->ack_or_ba_timeout_collision,
- max_tx->ack_or_ba_timeout_collision);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg ba_timeout:",
- le32_to_cpu(tx->agg.ba_timeout),
- accum_tx->agg.ba_timeout,
- delta_tx->agg.ba_timeout,
- max_tx->agg.ba_timeout);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg ba_resched_frames:",
- le32_to_cpu(tx->agg.ba_reschedule_frames),
- accum_tx->agg.ba_reschedule_frames,
- delta_tx->agg.ba_reschedule_frames,
- max_tx->agg.ba_reschedule_frames);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg scd_query_agg_frame:",
- le32_to_cpu(tx->agg.scd_query_agg_frame_cnt),
- accum_tx->agg.scd_query_agg_frame_cnt,
- delta_tx->agg.scd_query_agg_frame_cnt,
- max_tx->agg.scd_query_agg_frame_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg scd_query_no_agg:",
- le32_to_cpu(tx->agg.scd_query_no_agg),
- accum_tx->agg.scd_query_no_agg,
- delta_tx->agg.scd_query_no_agg,
- max_tx->agg.scd_query_no_agg);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg scd_query_agg:",
- le32_to_cpu(tx->agg.scd_query_agg),
- accum_tx->agg.scd_query_agg,
- delta_tx->agg.scd_query_agg,
- max_tx->agg.scd_query_agg);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg scd_query_mismatch:",
- le32_to_cpu(tx->agg.scd_query_mismatch),
- accum_tx->agg.scd_query_mismatch,
- delta_tx->agg.scd_query_mismatch,
- max_tx->agg.scd_query_mismatch);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg frame_not_ready:",
- le32_to_cpu(tx->agg.frame_not_ready),
- accum_tx->agg.frame_not_ready,
- delta_tx->agg.frame_not_ready,
- max_tx->agg.frame_not_ready);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg underrun:",
- le32_to_cpu(tx->agg.underrun),
- accum_tx->agg.underrun,
- delta_tx->agg.underrun, max_tx->agg.underrun);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg bt_prio_kill:",
- le32_to_cpu(tx->agg.bt_prio_kill),
- accum_tx->agg.bt_prio_kill,
- delta_tx->agg.bt_prio_kill,
- max_tx->agg.bt_prio_kill);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "agg rx_ba_rsp_cnt:",
- le32_to_cpu(tx->agg.rx_ba_rsp_cnt),
- accum_tx->agg.rx_ba_rsp_cnt,
- delta_tx->agg.rx_ba_rsp_cnt,
- max_tx->agg.rx_ba_rsp_cnt);
-
- if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) {
- pos += scnprintf(buf + pos, bufsz - pos,
- "tx power: (1/2 dB step)\n");
- if ((priv->nvm_data->valid_tx_ant & ANT_A) &&
- tx->tx_power.ant_a)
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_hex, "antenna A:",
- tx->tx_power.ant_a);
- if ((priv->nvm_data->valid_tx_ant & ANT_B) &&
- tx->tx_power.ant_b)
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_hex, "antenna B:",
- tx->tx_power.ant_b);
- if ((priv->nvm_data->valid_tx_ant & ANT_C) &&
- tx->tx_power.ant_c)
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_hex, "antenna C:",
- tx->tx_power.ant_c);
- }
-
- spin_unlock_bh(&priv->statistics.lock);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char *buf;
- int bufsz = sizeof(struct statistics_general) * 10 + 300;
- ssize_t ret;
- struct statistics_general_common *general, *accum_general;
- struct statistics_general_common *delta_general, *max_general;
- struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg;
- struct statistics_div *div, *accum_div, *delta_div, *max_div;
-
- if (!iwl_is_alive(priv))
- return -EAGAIN;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /* the statistic information display here is based on
- * the last statistics notification from uCode
- * might not reflect the current uCode activity
- */
-
- spin_lock_bh(&priv->statistics.lock);
-
- general = &priv->statistics.common;
- dbg = &priv->statistics.common.dbg;
- div = &priv->statistics.common.div;
- accum_general = &priv->accum_stats.common;
- accum_dbg = &priv->accum_stats.common.dbg;
- accum_div = &priv->accum_stats.common.div;
- delta_general = &priv->delta_stats.common;
- max_general = &priv->max_delta_stats.common;
- delta_dbg = &priv->delta_stats.common.dbg;
- max_dbg = &priv->max_delta_stats.common.dbg;
- delta_div = &priv->delta_stats.common.div;
- max_div = &priv->max_delta_stats.common.div;
-
- pos += iwl_statistics_flag(priv, buf, bufsz);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_header, "Statistics_General:");
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_value, "temperature:",
- le32_to_cpu(general->temperature));
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_value, "temperature_m:",
- le32_to_cpu(general->temperature_m));
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_value, "ttl_timestamp:",
- le32_to_cpu(general->ttl_timestamp));
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "burst_check:",
- le32_to_cpu(dbg->burst_check),
- accum_dbg->burst_check,
- delta_dbg->burst_check, max_dbg->burst_check);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "burst_count:",
- le32_to_cpu(dbg->burst_count),
- accum_dbg->burst_count,
- delta_dbg->burst_count, max_dbg->burst_count);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "wait_for_silence_timeout_count:",
- le32_to_cpu(dbg->wait_for_silence_timeout_cnt),
- accum_dbg->wait_for_silence_timeout_cnt,
- delta_dbg->wait_for_silence_timeout_cnt,
- max_dbg->wait_for_silence_timeout_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "sleep_time:",
- le32_to_cpu(general->sleep_time),
- accum_general->sleep_time,
- delta_general->sleep_time, max_general->sleep_time);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "slots_out:",
- le32_to_cpu(general->slots_out),
- accum_general->slots_out,
- delta_general->slots_out, max_general->slots_out);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "slots_idle:",
- le32_to_cpu(general->slots_idle),
- accum_general->slots_idle,
- delta_general->slots_idle, max_general->slots_idle);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "tx_on_a:",
- le32_to_cpu(div->tx_on_a), accum_div->tx_on_a,
- delta_div->tx_on_a, max_div->tx_on_a);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "tx_on_b:",
- le32_to_cpu(div->tx_on_b), accum_div->tx_on_b,
- delta_div->tx_on_b, max_div->tx_on_b);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "exec_time:",
- le32_to_cpu(div->exec_time), accum_div->exec_time,
- delta_div->exec_time, max_div->exec_time);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "probe_time:",
- le32_to_cpu(div->probe_time), accum_div->probe_time,
- delta_div->probe_time, max_div->probe_time);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "rx_enable_counter:",
- le32_to_cpu(general->rx_enable_counter),
- accum_general->rx_enable_counter,
- delta_general->rx_enable_counter,
- max_general->rx_enable_counter);
- pos += scnprintf(buf + pos, bufsz - pos,
- fmt_table, "num_of_sos_states:",
- le32_to_cpu(general->num_of_sos_states),
- accum_general->num_of_sos_states,
- delta_general->num_of_sos_states,
- max_general->num_of_sos_states);
-
- spin_unlock_bh(&priv->statistics.lock);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
- int pos = 0;
- char *buf;
- int bufsz = (sizeof(struct statistics_bt_activity) * 24) + 200;
- ssize_t ret;
- struct statistics_bt_activity *bt, *accum_bt;
-
- if (!iwl_is_alive(priv))
- return -EAGAIN;
-
- if (!priv->bt_enable_flag)
- return -EINVAL;
-
- /* make request to uCode to retrieve statistics information */
- mutex_lock(&priv->mutex);
- ret = iwl_send_statistics_request(priv, 0, false);
- mutex_unlock(&priv->mutex);
-
- if (ret)
- return -EAGAIN;
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /*
- * the statistic information display here is based on
- * the last statistics notification from uCode
- * might not reflect the current uCode activity
- */
-
- spin_lock_bh(&priv->statistics.lock);
-
- bt = &priv->statistics.bt_activity;
- accum_bt = &priv->accum_stats.bt_activity;
-
- pos += iwl_statistics_flag(priv, buf, bufsz);
- pos += scnprintf(buf + pos, bufsz - pos, "Statistics_BT:\n");
- pos += scnprintf(buf + pos, bufsz - pos,
- "\t\t\tcurrent\t\t\taccumulative\n");
- pos += scnprintf(buf + pos, bufsz - pos,
- "hi_priority_tx_req_cnt:\t\t%u\t\t\t%u\n",
- le32_to_cpu(bt->hi_priority_tx_req_cnt),
- accum_bt->hi_priority_tx_req_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- "hi_priority_tx_denied_cnt:\t%u\t\t\t%u\n",
- le32_to_cpu(bt->hi_priority_tx_denied_cnt),
- accum_bt->hi_priority_tx_denied_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- "lo_priority_tx_req_cnt:\t\t%u\t\t\t%u\n",
- le32_to_cpu(bt->lo_priority_tx_req_cnt),
- accum_bt->lo_priority_tx_req_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n",
- le32_to_cpu(bt->lo_priority_tx_denied_cnt),
- accum_bt->lo_priority_tx_denied_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- "hi_priority_rx_req_cnt:\t\t%u\t\t\t%u\n",
- le32_to_cpu(bt->hi_priority_rx_req_cnt),
- accum_bt->hi_priority_rx_req_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- "hi_priority_rx_denied_cnt:\t%u\t\t\t%u\n",
- le32_to_cpu(bt->hi_priority_rx_denied_cnt),
- accum_bt->hi_priority_rx_denied_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- "lo_priority_rx_req_cnt:\t\t%u\t\t\t%u\n",
- le32_to_cpu(bt->lo_priority_rx_req_cnt),
- accum_bt->lo_priority_rx_req_cnt);
- pos += scnprintf(buf + pos, bufsz - pos,
- "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n",
- le32_to_cpu(bt->lo_priority_rx_denied_cnt),
- accum_bt->lo_priority_rx_denied_cnt);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "(rx)num_bt_kills:\t\t%u\t\t\t%u\n",
- le32_to_cpu(priv->statistics.num_bt_kills),
- priv->statistics.accum_num_bt_kills);
-
- spin_unlock_bh(&priv->statistics.lock);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
- int pos = 0;
- char *buf;
- int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) +
- (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200;
- ssize_t ret;
-
- if (!iwl_is_alive(priv))
- return -EAGAIN;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n");
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY),
- priv->reply_tx_stats.pp_delay);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES),
- priv->reply_tx_stats.pp_few_bytes);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO),
- priv->reply_tx_stats.pp_bt_prio);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD),
- priv->reply_tx_stats.pp_quiet_period);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK),
- priv->reply_tx_stats.pp_calc_ttak);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
- iwl_get_tx_fail_reason(
- TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY),
- priv->reply_tx_stats.int_crossed_retry);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT),
- priv->reply_tx_stats.short_limit);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT),
- priv->reply_tx_stats.long_limit);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN),
- priv->reply_tx_stats.fifo_underrun);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW),
- priv->reply_tx_stats.drain_flow);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH),
- priv->reply_tx_stats.rfkill_flush);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE),
- priv->reply_tx_stats.life_expire);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS),
- priv->reply_tx_stats.dest_ps);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED),
- priv->reply_tx_stats.host_abort);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY),
- priv->reply_tx_stats.pp_delay);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID),
- priv->reply_tx_stats.sta_invalid);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED),
- priv->reply_tx_stats.frag_drop);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE),
- priv->reply_tx_stats.tid_disable);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED),
- priv->reply_tx_stats.fifo_flush);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
- iwl_get_tx_fail_reason(
- TX_STATUS_FAIL_INSUFFICIENT_CF_POLL),
- priv->reply_tx_stats.insuff_cf_poll);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX),
- priv->reply_tx_stats.fail_hw_drop);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
- iwl_get_tx_fail_reason(
- TX_STATUS_FAIL_NO_BEACON_ON_RADAR),
- priv->reply_tx_stats.sta_color_mismatch);
- pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
- priv->reply_tx_stats.unknown);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "\nStatistics_Agg_TX_Error:\n");
-
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK),
- priv->reply_agg_tx_stats.underrun);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK),
- priv->reply_agg_tx_stats.bt_prio);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK),
- priv->reply_agg_tx_stats.few_bytes);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK),
- priv->reply_agg_tx_stats.abort);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
- iwl_get_agg_tx_fail_reason(
- AGG_TX_STATE_LAST_SENT_TTL_MSK),
- priv->reply_agg_tx_stats.last_sent_ttl);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
- iwl_get_agg_tx_fail_reason(
- AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK),
- priv->reply_agg_tx_stats.last_sent_try);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
- iwl_get_agg_tx_fail_reason(
- AGG_TX_STATE_LAST_SENT_BT_KILL_MSK),
- priv->reply_agg_tx_stats.last_sent_bt_kill);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK),
- priv->reply_agg_tx_stats.scd_query);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
- iwl_get_agg_tx_fail_reason(
- AGG_TX_STATE_TEST_BAD_CRC32_MSK),
- priv->reply_agg_tx_stats.bad_crc32);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK),
- priv->reply_agg_tx_stats.response);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK),
- priv->reply_agg_tx_stats.dump_tx);
- pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
- iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK),
- priv->reply_agg_tx_stats.delay_tx);
- pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
- priv->reply_agg_tx_stats.unknown);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_sensitivity_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- int cnt = 0;
- char *buf;
- int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100;
- ssize_t ret;
- struct iwl_sensitivity_data *data;
-
- data = &priv->sensitivity_data;
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n",
- data->auto_corr_ofdm);
- pos += scnprintf(buf + pos, bufsz - pos,
- "auto_corr_ofdm_mrc:\t\t %u\n",
- data->auto_corr_ofdm_mrc);
- pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n",
- data->auto_corr_ofdm_x1);
- pos += scnprintf(buf + pos, bufsz - pos,
- "auto_corr_ofdm_mrc_x1:\t\t %u\n",
- data->auto_corr_ofdm_mrc_x1);
- pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n",
- data->auto_corr_cck);
- pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n",
- data->auto_corr_cck_mrc);
- pos += scnprintf(buf + pos, bufsz - pos,
- "last_bad_plcp_cnt_ofdm:\t\t %u\n",
- data->last_bad_plcp_cnt_ofdm);
- pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n",
- data->last_fa_cnt_ofdm);
- pos += scnprintf(buf + pos, bufsz - pos,
- "last_bad_plcp_cnt_cck:\t\t %u\n",
- data->last_bad_plcp_cnt_cck);
- pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n",
- data->last_fa_cnt_cck);
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n",
- data->nrg_curr_state);
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n",
- data->nrg_prev_state);
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t");
- for (cnt = 0; cnt < 10; cnt++) {
- pos += scnprintf(buf + pos, bufsz - pos, " %u",
- data->nrg_value[cnt]);
- }
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t");
- for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) {
- pos += scnprintf(buf + pos, bufsz - pos, " %u",
- data->nrg_silence_rssi[cnt]);
- }
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n",
- data->nrg_silence_ref);
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n",
- data->nrg_energy_idx);
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n",
- data->nrg_silence_idx);
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n",
- data->nrg_th_cck);
- pos += scnprintf(buf + pos, bufsz - pos,
- "nrg_auto_corr_silence_diff:\t %u\n",
- data->nrg_auto_corr_silence_diff);
- pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n",
- data->num_in_cck_no_fa);
- pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n",
- data->nrg_th_ofdm);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-
-static ssize_t iwl_dbgfs_chain_noise_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- int cnt = 0;
- char *buf;
- int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100;
- ssize_t ret;
- struct iwl_chain_noise_data *data;
-
- data = &priv->chain_noise_data;
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n",
- data->active_chains);
- pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n",
- data->chain_noise_a);
- pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n",
- data->chain_noise_b);
- pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n",
- data->chain_noise_c);
- pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n",
- data->chain_signal_a);
- pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n",
- data->chain_signal_b);
- pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n",
- data->chain_signal_c);
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n",
- data->beacon_count);
-
- pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t");
- for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) {
- pos += scnprintf(buf + pos, bufsz - pos, " %u",
- data->disconn_array[cnt]);
- }
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t");
- for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) {
- pos += scnprintf(buf + pos, bufsz - pos, " %u",
- data->delta_gain_code[cnt]);
- }
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n",
- data->radio_write);
- pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n",
- data->state);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_power_save_status_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[60];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
- u32 pwrsave_status;
-
- pwrsave_status = iwl_read32(priv->trans, CSR_GP_CNTRL) &
- CSR_GP_REG_POWER_SAVE_STATUS_MSK;
-
- pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: ");
- pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
- (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" :
- (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" :
- (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" :
- "error");
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int clear;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &clear) != 1)
- return -EFAULT;
-
- /* make request to uCode to retrieve statistics information */
- mutex_lock(&priv->mutex);
- iwl_send_statistics_request(priv, 0, true);
- mutex_unlock(&priv->mutex);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char buf[128];
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n",
- priv->event_log.ucode_trace ? "On" : "Off");
- pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n",
- priv->event_log.non_wraps_count);
- pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n",
- priv->event_log.wraps_once_count);
- pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n",
- priv->event_log.wraps_more_count);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int trace;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &trace) != 1)
- return -EFAULT;
-
- if (trace) {
- priv->event_log.ucode_trace = true;
- if (iwl_is_alive(priv)) {
- /* start collecting data now */
- mod_timer(&priv->ucode_trace, jiffies);
- }
- } else {
- priv->event_log.ucode_trace = false;
- del_timer_sync(&priv->ucode_trace);
- }
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_rxon_flags_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int len = 0;
- char buf[20];
-
- len = sprintf(buf, "0x%04X\n",
- le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags));
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int len = 0;
- char buf[20];
-
- len = sprintf(buf, "0x%04X\n",
- le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags));
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char buf[12];
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "%d\n",
- priv->missed_beacon_threshold);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int missed;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &missed) != 1)
- return -EINVAL;
-
- if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN ||
- missed > IWL_MISSED_BEACON_THRESHOLD_MAX)
- priv->missed_beacon_threshold =
- IWL_MISSED_BEACON_THRESHOLD_DEF;
- else
- priv->missed_beacon_threshold = missed;
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char buf[12];
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "%u\n",
- priv->plcp_delta_threshold);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int plcp;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &plcp) != 1)
- return -EINVAL;
- if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) ||
- (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX))
- priv->plcp_delta_threshold =
- IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE;
- else
- priv->plcp_delta_threshold = plcp;
- return count;
-}
-
-static ssize_t iwl_dbgfs_rf_reset_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- int pos = 0;
- char buf[300];
- const size_t bufsz = sizeof(buf);
- struct iwl_rf_reset *rf_reset = &priv->rf_reset;
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "RF reset statistics\n");
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tnumber of reset request: %d\n",
- rf_reset->reset_request_count);
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tnumber of reset request success: %d\n",
- rf_reset->reset_success_count);
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tnumber of reset request reject: %d\n",
- rf_reset->reset_reject_count);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_rf_reset_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- int ret;
-
- ret = iwl_force_rf_reset(priv, true);
- return ret ? ret : count;
-}
-
-static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int flush;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &flush) != 1)
- return -EINVAL;
-
- if (iwl_is_rfkill(priv))
- return -EFAULT;
-
- iwlagn_dev_txfifo_flush(priv);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
- int pos = 0;
- char buf[200];
- const size_t bufsz = sizeof(buf);
-
- if (!priv->bt_enable_flag) {
- pos += scnprintf(buf + pos, bufsz - pos, "BT coex disabled\n");
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- }
- pos += scnprintf(buf + pos, bufsz - pos, "BT enable flag: 0x%x\n",
- priv->bt_enable_flag);
- pos += scnprintf(buf + pos, bufsz - pos, "BT in %s mode\n",
- priv->bt_full_concurrent ? "full concurrency" : "3-wire");
- pos += scnprintf(buf + pos, bufsz - pos, "BT status: %s, "
- "last traffic notif: %d\n",
- priv->bt_status ? "On" : "Off", priv->last_bt_traffic_load);
- pos += scnprintf(buf + pos, bufsz - pos, "ch_announcement: %d, "
- "kill_ack_mask: %x, kill_cts_mask: %x\n",
- priv->bt_ch_announce, priv->kill_ack_mask,
- priv->kill_cts_mask);
-
- pos += scnprintf(buf + pos, bufsz - pos, "bluetooth traffic load: ");
- switch (priv->bt_traffic_load) {
- case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
- pos += scnprintf(buf + pos, bufsz - pos, "Continuous\n");
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
- pos += scnprintf(buf + pos, bufsz - pos, "High\n");
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
- pos += scnprintf(buf + pos, bufsz - pos, "Low\n");
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
- default:
- pos += scnprintf(buf + pos, bufsz - pos, "None\n");
- break;
- }
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_protection_mode_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
-
- int pos = 0;
- char buf[40];
- const size_t bufsz = sizeof(buf);
-
- if (priv->cfg->ht_params)
- pos += scnprintf(buf + pos, bufsz - pos,
- "use %s for aggregation\n",
- (priv->hw_params.use_rts_for_aggregation) ?
- "rts/cts" : "cts-to-self");
- else
- pos += scnprintf(buf + pos, bufsz - pos, "N/A");
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_protection_mode_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos) {
-
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
- int rts;
-
- if (!priv->cfg->ht_params)
- return -EINVAL;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &rts) != 1)
- return -EINVAL;
- if (rts)
- priv->hw_params.use_rts_for_aggregation = true;
- else
- priv->hw_params.use_rts_for_aggregation = false;
- return count;
-}
-
-static int iwl_cmd_echo_test(struct iwl_priv *priv)
-{
- int ret;
- struct iwl_host_cmd cmd = {
- .id = REPLY_ECHO,
- .len = { 0 },
- };
-
- ret = iwl_dvm_send_cmd(priv, &cmd);
- if (ret)
- IWL_ERR(priv, "echo testing fail: 0X%x\n", ret);
- else
- IWL_DEBUG_INFO(priv, "echo testing pass\n");
- return ret;
-}
-
-static ssize_t iwl_dbgfs_echo_test_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- int buf_size;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- iwl_cmd_echo_test(priv);
- return count;
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-static ssize_t iwl_dbgfs_log_event_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char *buf = NULL;
- ssize_t ret;
-
- ret = iwl_dump_nic_event_log(priv, true, &buf);
- if (ret > 0)
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_log_event_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- u32 event_log_flag;
- char buf[8];
- int buf_size;
-
- /* check that the interface is up */
- if (!iwl_is_ready(priv))
- return -EAGAIN;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &event_log_flag) != 1)
- return -EFAULT;
- if (event_log_flag == 1)
- iwl_dump_nic_event_log(priv, true, NULL);
-
- return count;
-}
-#endif
-
-static ssize_t iwl_dbgfs_calib_disabled_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[120];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Sensitivity calibrations %s\n",
- (priv->calib_disabled &
- IWL_SENSITIVITY_CALIB_DISABLED) ?
- "DISABLED" : "ENABLED");
- pos += scnprintf(buf + pos, bufsz - pos,
- "Chain noise calibrations %s\n",
- (priv->calib_disabled &
- IWL_CHAIN_NOISE_CALIB_DISABLED) ?
- "DISABLED" : "ENABLED");
- pos += scnprintf(buf + pos, bufsz - pos,
- "Tx power calibrations %s\n",
- (priv->calib_disabled &
- IWL_TX_POWER_CALIB_DISABLED) ?
- "DISABLED" : "ENABLED");
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- char buf[8];
- u32 calib_disabled;
- int buf_size;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%x", &calib_disabled) != 1)
- return -EFAULT;
-
- priv->calib_disabled = calib_disabled;
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- bool restart_fw = iwlwifi_mod_params.restart_fw;
- int ret;
-
- iwlwifi_mod_params.restart_fw = true;
-
- mutex_lock(&priv->mutex);
-
- /* take the return value to make compiler happy - it will fail anyway */
- ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL);
-
- mutex_unlock(&priv->mutex);
-
- iwlwifi_mod_params.restart_fw = restart_fw;
-
- return count;
-}
-
-DEBUGFS_READ_FILE_OPS(ucode_rx_stats);
-DEBUGFS_READ_FILE_OPS(ucode_tx_stats);
-DEBUGFS_READ_FILE_OPS(ucode_general_stats);
-DEBUGFS_READ_FILE_OPS(sensitivity);
-DEBUGFS_READ_FILE_OPS(chain_noise);
-DEBUGFS_READ_FILE_OPS(power_save_status);
-DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics);
-DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing);
-DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon);
-DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta);
-DEBUGFS_READ_WRITE_FILE_OPS(rf_reset);
-DEBUGFS_READ_FILE_OPS(rxon_flags);
-DEBUGFS_READ_FILE_OPS(rxon_filter_flags);
-DEBUGFS_WRITE_FILE_OPS(txfifo_flush);
-DEBUGFS_READ_FILE_OPS(ucode_bt_stats);
-DEBUGFS_READ_FILE_OPS(bt_traffic);
-DEBUGFS_READ_WRITE_FILE_OPS(protection_mode);
-DEBUGFS_READ_FILE_OPS(reply_tx_error);
-DEBUGFS_WRITE_FILE_OPS(echo_test);
-DEBUGFS_WRITE_FILE_OPS(fw_restart);
-#ifdef CONFIG_IWLWIFI_DEBUG
-DEBUGFS_READ_WRITE_FILE_OPS(log_event);
-#endif
-DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled);
-
-/*
- * Create the debugfs files and directories
- *
- */
-int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir)
-{
- struct dentry *dir_data, *dir_rf, *dir_debug;
-
- priv->debugfs_dir = dbgfs_dir;
-
- dir_data = debugfs_create_dir("data", dbgfs_dir);
- if (!dir_data)
- goto err;
- dir_rf = debugfs_create_dir("rf", dbgfs_dir);
- if (!dir_rf)
- goto err;
- dir_debug = debugfs_create_dir("debug", dbgfs_dir);
- if (!dir_debug)
- goto err;
-
- DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR);
-
- DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(fw_restart, dir_debug, S_IWUSR);
-#ifdef CONFIG_IWLWIFI_DEBUG
- DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR);
-#endif
-
- if (iwl_advanced_bt_coexist(priv))
- DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR);
-
- /* Calibrations disabled/enabled status*/
- DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR);
-
- /*
- * Create a symlink with mac80211. This is not very robust, as it does
- * not remove the symlink created. The implicit assumption is that
- * when the opmode exits, mac80211 will also exit, and will remove
- * this symlink as part of its cleanup.
- */
- if (priv->mac80211_registered) {
- char buf[100];
- struct dentry *mac80211_dir, *dev_dir, *root_dir;
-
- dev_dir = dbgfs_dir->d_parent;
- root_dir = dev_dir->d_parent;
- mac80211_dir = priv->hw->wiphy->debugfsdir;
-
- snprintf(buf, 100, "../../%s/%s", root_dir->d_name.name,
- dev_dir->d_name.name);
-
- if (!debugfs_create_symlink("iwlwifi", mac80211_dir, buf))
- goto err;
- }
-
- return 0;
-
-err:
- IWL_ERR(priv, "failed to create the dvm debugfs entries\n");
- return -ENOMEM;
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h
deleted file mode 100644
index 0ba3e56d6015..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/dev.h
+++ /dev/null
@@ -1,949 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-/*
- * Please use this file (dev.h) for driver implementation definitions.
- * Please use commands.h for uCode API definitions.
- */
-
-#ifndef __iwl_dev_h__
-#define __iwl_dev_h__
-
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/wait.h>
-#include <linux/leds.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-
-#include "iwl-fw.h"
-#include "iwl-eeprom-parse.h"
-#include "iwl-csr.h"
-#include "iwl-debug.h"
-#include "iwl-agn-hw.h"
-#include "iwl-op-mode.h"
-#include "iwl-notif-wait.h"
-#include "iwl-trans.h"
-
-#include "led.h"
-#include "power.h"
-#include "rs.h"
-#include "tt.h"
-
-/* CT-KILL constants */
-#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */
-#define CT_KILL_THRESHOLD 114 /* in Celsius */
-#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */
-
-/* Default noise level to report when noise measurement is not available.
- * This may be because we're:
- * 1) Not associated no beacon statistics being sent to driver)
- * 2) Scanning (noise measurement does not apply to associated channel)
- * Use default noise value of -127 ... this is below the range of measurable
- * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user.
- * Also, -127 works better than 0 when averaging frames with/without
- * noise info (e.g. averaging might be done in app); measured dBm values are
- * always negative ... using a negative value as the default keeps all
- * averages within an s8's (used in some apps) range of negative values. */
-#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127)
-
-/*
- * RTS threshold here is total size [2347] minus 4 FCS bytes
- * Per spec:
- * a value of 0 means RTS on all data/management packets
- * a value > max MSDU size means no RTS
- * else RTS for data/management frames where MPDU is larger
- * than RTS value.
- */
-#define DEFAULT_RTS_THRESHOLD 2347U
-#define MIN_RTS_THRESHOLD 0U
-#define MAX_RTS_THRESHOLD 2347U
-#define MAX_MSDU_SIZE 2304U
-#define MAX_MPDU_SIZE 2346U
-#define DEFAULT_BEACON_INTERVAL 200U
-#define DEFAULT_SHORT_RETRY_LIMIT 7U
-#define DEFAULT_LONG_RETRY_LIMIT 4U
-
-#define IWL_NUM_SCAN_RATES (2)
-
-
-#define IEEE80211_DATA_LEN 2304
-#define IEEE80211_4ADDR_LEN 30
-#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN)
-#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
-
-#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
-#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
-#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
-
-#define IWL_SUPPORTED_RATES_IE_LEN 8
-
-#define IWL_INVALID_RATE 0xFF
-#define IWL_INVALID_VALUE -1
-
-union iwl_ht_rate_supp {
- u16 rates;
- struct {
- u8 siso_rate;
- u8 mimo_rate;
- };
-};
-
-struct iwl_ht_config {
- bool single_chain_sufficient;
- enum ieee80211_smps_mode smps; /* current smps mode */
-};
-
-/* QoS structures */
-struct iwl_qos_info {
- int qos_active;
- struct iwl_qosparam_cmd def_qos_parm;
-};
-
-/**
- * enum iwl_agg_state
- *
- * The state machine of the BA agreement establishment / tear down.
- * These states relate to a specific RA / TID.
- *
- * @IWL_AGG_OFF: aggregation is not used
- * @IWL_AGG_STARTING: aggregation are starting (between start and oper)
- * @IWL_AGG_ON: aggregation session is up
- * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
- * HW queue to be empty from packets for this RA /TID.
- * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
- * HW queue to be empty from packets for this RA /TID.
- */
-enum iwl_agg_state {
- IWL_AGG_OFF = 0,
- IWL_AGG_STARTING,
- IWL_AGG_ON,
- IWL_EMPTYING_HW_QUEUE_ADDBA,
- IWL_EMPTYING_HW_QUEUE_DELBA,
-};
-
-/**
- * struct iwl_ht_agg - aggregation state machine
-
- * This structs holds the states for the BA agreement establishment and tear
- * down. It also holds the state during the BA session itself. This struct is
- * duplicated for each RA / TID.
-
- * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
- * Tx response (REPLY_TX), and the block ack notification
- * (REPLY_COMPRESSED_BA).
- * @state: state of the BA agreement establishment / tear down.
- * @txq_id: Tx queue used by the BA session
- * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
- * 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_ht_agg {
- u32 rate_n_flags;
- enum iwl_agg_state state;
- u16 txq_id;
- u16 ssn;
- bool wait_for_ba;
-};
-
-/**
- * struct iwl_tid_data - one for each RA / TID
-
- * This structs holds the states for each RA / TID.
-
- * @seq_number: the next WiFi sequence number to use
- * @next_reclaimed: the WiFi sequence number of the next packet to be acked.
- * This is basically (last acked packet++).
- * @agg: aggregation state machine
- */
-struct iwl_tid_data {
- u16 seq_number;
- u16 next_reclaimed;
- struct iwl_ht_agg agg;
-};
-
-/*
- * Structure should be accessed with sta_lock held. When station addition
- * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only
- * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock
- * held.
- */
-struct iwl_station_entry {
- struct iwl_addsta_cmd sta;
- u8 used, ctxid;
- struct iwl_link_quality_cmd *lq;
-};
-
-/*
- * iwl_station_priv: Driver's private station information
- *
- * When mac80211 creates a station it reserves some space (hw->sta_data_size)
- * in the structure for use by driver. This structure is places in that
- * space.
- */
-struct iwl_station_priv {
- struct iwl_rxon_context *ctx;
- struct iwl_lq_sta lq_sta;
- atomic_t pending_frames;
- bool client;
- bool asleep;
- u8 max_agg_bufsize;
- u8 sta_id;
-};
-
-/**
- * struct iwl_vif_priv - driver's private per-interface information
- *
- * When mac80211 allocates a virtual interface, it can allocate
- * space for us to put data into.
- */
-struct iwl_vif_priv {
- struct iwl_rxon_context *ctx;
- u8 ibss_bssid_sta_id;
-};
-
-struct iwl_sensitivity_ranges {
- u16 min_nrg_cck;
-
- u16 nrg_th_cck;
- u16 nrg_th_ofdm;
-
- u16 auto_corr_min_ofdm;
- u16 auto_corr_min_ofdm_mrc;
- u16 auto_corr_min_ofdm_x1;
- u16 auto_corr_min_ofdm_mrc_x1;
-
- u16 auto_corr_max_ofdm;
- u16 auto_corr_max_ofdm_mrc;
- u16 auto_corr_max_ofdm_x1;
- u16 auto_corr_max_ofdm_mrc_x1;
-
- u16 auto_corr_max_cck;
- u16 auto_corr_max_cck_mrc;
- u16 auto_corr_min_cck;
- u16 auto_corr_min_cck_mrc;
-
- u16 barker_corr_th_min;
- u16 barker_corr_th_min_mrc;
- u16 nrg_th_cca;
-};
-
-
-#define KELVIN_TO_CELSIUS(x) ((x)-273)
-#define CELSIUS_TO_KELVIN(x) ((x)+273)
-
-
-/******************************************************************************
- *
- * Functions implemented in core module which are forward declared here
- * for use by iwl-[4-5].c
- *
- * NOTE: The implementation of these functions are not hardware specific
- * which is why they are in the core module files.
- *
- * Naming convention --
- * iwl_ <-- Is part of iwlwifi
- * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX)
- *
- ****************************************************************************/
-void iwl_update_chain_flags(struct iwl_priv *priv);
-extern const u8 iwl_bcast_addr[ETH_ALEN];
-
-#define IWL_OPERATION_MODE_AUTO 0
-#define IWL_OPERATION_MODE_HT_ONLY 1
-#define IWL_OPERATION_MODE_MIXED 2
-#define IWL_OPERATION_MODE_20MHZ 3
-
-#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000
-
-/* Sensitivity and chain noise calibration */
-#define INITIALIZATION_VALUE 0xFFFF
-#define IWL_CAL_NUM_BEACONS 16
-#define MAXIMUM_ALLOWED_PATHLOSS 15
-
-#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3
-
-#define MAX_FA_OFDM 50
-#define MIN_FA_OFDM 5
-#define MAX_FA_CCK 50
-#define MIN_FA_CCK 5
-
-#define AUTO_CORR_STEP_OFDM 1
-
-#define AUTO_CORR_STEP_CCK 3
-#define AUTO_CORR_MAX_TH_CCK 160
-
-#define NRG_DIFF 2
-#define NRG_STEP_CCK 2
-#define NRG_MARGIN 8
-#define MAX_NUMBER_CCK_NO_FA 100
-
-#define AUTO_CORR_CCK_MIN_VAL_DEF (125)
-
-#define CHAIN_A 0
-#define CHAIN_B 1
-#define CHAIN_C 2
-#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4
-#define ALL_BAND_FILTER 0xFF00
-#define IN_BAND_FILTER 0xFF
-#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF
-
-#define NRG_NUM_PREV_STAT_L 20
-#define NUM_RX_CHAINS 3
-
-enum iwlagn_false_alarm_state {
- IWL_FA_TOO_MANY = 0,
- IWL_FA_TOO_FEW = 1,
- IWL_FA_GOOD_RANGE = 2,
-};
-
-enum iwlagn_chain_noise_state {
- IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */
- IWL_CHAIN_NOISE_ACCUMULATE,
- IWL_CHAIN_NOISE_CALIBRATED,
- IWL_CHAIN_NOISE_DONE,
-};
-
-/* Sensitivity calib data */
-struct iwl_sensitivity_data {
- u32 auto_corr_ofdm;
- u32 auto_corr_ofdm_mrc;
- u32 auto_corr_ofdm_x1;
- u32 auto_corr_ofdm_mrc_x1;
- u32 auto_corr_cck;
- u32 auto_corr_cck_mrc;
-
- u32 last_bad_plcp_cnt_ofdm;
- u32 last_fa_cnt_ofdm;
- u32 last_bad_plcp_cnt_cck;
- u32 last_fa_cnt_cck;
-
- u32 nrg_curr_state;
- u32 nrg_prev_state;
- u32 nrg_value[10];
- u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L];
- u32 nrg_silence_ref;
- u32 nrg_energy_idx;
- u32 nrg_silence_idx;
- u32 nrg_th_cck;
- s32 nrg_auto_corr_silence_diff;
- u32 num_in_cck_no_fa;
- u32 nrg_th_ofdm;
-
- u16 barker_corr_th_min;
- u16 barker_corr_th_min_mrc;
- u16 nrg_th_cca;
-};
-
-/* Chain noise (differential Rx gain) calib data */
-struct iwl_chain_noise_data {
- u32 active_chains;
- u32 chain_noise_a;
- u32 chain_noise_b;
- u32 chain_noise_c;
- u32 chain_signal_a;
- u32 chain_signal_b;
- u32 chain_signal_c;
- u16 beacon_count;
- u8 disconn_array[NUM_RX_CHAINS];
- u8 delta_gain_code[NUM_RX_CHAINS];
- u8 radio_write;
- u8 state;
-};
-
-enum {
- MEASUREMENT_READY = (1 << 0),
- MEASUREMENT_ACTIVE = (1 << 1),
-};
-
-/* reply_tx_statistics (for _agn devices) */
-struct reply_tx_error_statistics {
- u32 pp_delay;
- u32 pp_few_bytes;
- u32 pp_bt_prio;
- u32 pp_quiet_period;
- u32 pp_calc_ttak;
- u32 int_crossed_retry;
- u32 short_limit;
- u32 long_limit;
- u32 fifo_underrun;
- u32 drain_flow;
- u32 rfkill_flush;
- u32 life_expire;
- u32 dest_ps;
- u32 host_abort;
- u32 bt_retry;
- u32 sta_invalid;
- u32 frag_drop;
- u32 tid_disable;
- u32 fifo_flush;
- u32 insuff_cf_poll;
- u32 fail_hw_drop;
- u32 sta_color_mismatch;
- u32 unknown;
-};
-
-/* reply_agg_tx_statistics (for _agn devices) */
-struct reply_agg_tx_error_statistics {
- u32 underrun;
- u32 bt_prio;
- u32 few_bytes;
- u32 abort;
- u32 last_sent_ttl;
- u32 last_sent_try;
- u32 last_sent_bt_kill;
- u32 scd_query;
- u32 bad_crc32;
- u32 response;
- u32 dump_tx;
- u32 delay_tx;
- u32 unknown;
-};
-
-/*
- * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds
- * to perform continuous uCode event logging operation if enabled
- */
-#define UCODE_TRACE_PERIOD (10)
-
-/*
- * iwl_event_log: current uCode event log position
- *
- * @ucode_trace: enable/disable ucode continuous trace timer
- * @num_wraps: how many times the event buffer wraps
- * @next_entry: the entry just before the next one that uCode would fill
- * @non_wraps_count: counter for no wrap detected when dump ucode events
- * @wraps_once_count: counter for wrap once detected when dump ucode events
- * @wraps_more_count: counter for wrap more than once detected
- * when dump ucode events
- */
-struct iwl_event_log {
- bool ucode_trace;
- u32 num_wraps;
- u32 next_entry;
- int non_wraps_count;
- int wraps_once_count;
- int wraps_more_count;
-};
-
-#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
-
-/* BT Antenna Coupling Threshold (dB) */
-#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35)
-
-/* Firmware reload counter and Timestamp */
-#define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */
-#define IWL_MAX_CONTINUE_RELOAD_CNT 4
-
-
-struct iwl_rf_reset {
- int reset_request_count;
- int reset_success_count;
- int reset_reject_count;
- unsigned long last_reset_jiffies;
-};
-
-enum iwl_rxon_context_id {
- IWL_RXON_CTX_BSS,
- IWL_RXON_CTX_PAN,
-
- NUM_IWL_RXON_CTX
-};
-
-/* extend beacon time format bit shifting */
-/*
- * for _agn devices
- * bits 31:22 - extended
- * bits 21:0 - interval
- */
-#define IWLAGN_EXT_BEACON_TIME_POS 22
-
-struct iwl_rxon_context {
- struct ieee80211_vif *vif;
-
- u8 mcast_queue;
- u8 ac_to_queue[IEEE80211_NUM_ACS];
- u8 ac_to_fifo[IEEE80211_NUM_ACS];
-
- /*
- * We could use the vif to indicate active, but we
- * also need it to be active during disabling when
- * we already removed the vif for type setting.
- */
- bool always_active, is_active;
-
- bool ht_need_multiple_chains;
-
- enum iwl_rxon_context_id ctxid;
-
- u32 interface_modes, exclusive_interface_modes;
- u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype;
-
- /*
- * We declare this const so it can only be
- * changed via explicit cast within the
- * routines that actually update the physical
- * hardware.
- */
- const struct iwl_rxon_cmd active;
- struct iwl_rxon_cmd staging;
-
- struct iwl_rxon_time_cmd timing;
-
- struct iwl_qos_info qos_data;
-
- u8 bcast_sta_id, ap_sta_id;
-
- u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd;
- u8 qos_cmd;
- u8 wep_key_cmd;
-
- struct iwl_wep_key wep_keys[WEP_KEYS_MAX];
- u8 key_mapping_keys;
-
- __le32 station_flags;
-
- int beacon_int;
-
- struct {
- bool non_gf_sta_present;
- u8 protection;
- bool enabled, is_40mhz;
- u8 extension_chan_offset;
- } ht;
-};
-
-enum iwl_scan_type {
- IWL_SCAN_NORMAL,
- IWL_SCAN_RADIO_RESET,
-};
-
-/**
- * struct iwl_hw_params
- *
- * Holds the module parameters
- *
- * @tx_chains_num: Number of TX chains
- * @rx_chains_num: Number of RX chains
- * @ct_kill_threshold: temperature threshold - in hw dependent unit
- * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit
- * relevant for 1000, 6000 and up
- * @struct iwl_sensitivity_ranges: range of sensitivity values
- * @use_rts_for_aggregation: use rts/cts protection for HT traffic
- */
-struct iwl_hw_params {
- u8 tx_chains_num;
- u8 rx_chains_num;
- bool use_rts_for_aggregation;
- u32 ct_kill_threshold;
- u32 ct_kill_exit_threshold;
-
- const struct iwl_sensitivity_ranges *sens;
-};
-
-/**
- * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters
- * @advanced_bt_coexist: support advanced bt coexist
- * @bt_init_traffic_load: specify initial bt traffic load
- * @bt_prio_boost: default bt priority boost value
- * @agg_time_limit: maximum number of uSec in aggregation
- * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
- */
-struct iwl_dvm_bt_params {
- bool advanced_bt_coexist;
- u8 bt_init_traffic_load;
- u32 bt_prio_boost;
- u16 agg_time_limit;
- bool bt_sco_disable;
- bool bt_session_2;
-};
-
-/**
- * struct iwl_dvm_cfg - DVM firmware specific device configuration
- * @set_hw_params: set hardware parameters
- * @set_channel_switch: send channel switch command
- * @nic_config: apply device specific configuration
- * @temperature: read temperature
- * @adv_thermal_throttle: support advance thermal throttle
- * @support_ct_kill_exit: support ct kill exit condition
- * @plcp_delta_threshold: plcp error rate threshold used to trigger
- * radio tuning when there is a high receiving plcp error rate
- * @chain_noise_scale: default chain noise scale used for gain computation
- * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
- * @no_idle_support: do not support idle mode
- * @bt_params: pointer to BT parameters
- * @need_temp_offset_calib: need to perform temperature offset calibration
- * @no_xtal_calib: some devices do not need crystal calibration data,
- * don't send it to those
- * @temp_offset_v2: support v2 of temperature offset calibration
- * @adv_pm: advanced power management
- */
-struct iwl_dvm_cfg {
- void (*set_hw_params)(struct iwl_priv *priv);
- int (*set_channel_switch)(struct iwl_priv *priv,
- struct ieee80211_channel_switch *ch_switch);
- void (*nic_config)(struct iwl_priv *priv);
- void (*temperature)(struct iwl_priv *priv);
-
- const struct iwl_dvm_bt_params *bt_params;
- s32 chain_noise_scale;
- u8 plcp_delta_threshold;
- bool adv_thermal_throttle;
- bool support_ct_kill_exit;
- bool hd_v2;
- bool no_idle_support;
- bool need_temp_offset_calib;
- bool no_xtal_calib;
- bool temp_offset_v2;
- bool adv_pm;
-};
-
-struct iwl_wipan_noa_data {
- struct rcu_head rcu_head;
- u32 length;
- u8 data[];
-};
-
-/* Calibration disabling bit mask */
-enum {
- IWL_CALIB_ENABLE_ALL = 0,
-
- IWL_SENSITIVITY_CALIB_DISABLED = BIT(0),
- IWL_CHAIN_NOISE_CALIB_DISABLED = BIT(1),
- IWL_TX_POWER_CALIB_DISABLED = BIT(2),
-
- IWL_CALIB_DISABLE_ALL = 0xFFFFFFFF,
-};
-
-#define IWL_OP_MODE_GET_DVM(_iwl_op_mode) \
- ((struct iwl_priv *) ((_iwl_op_mode)->op_mode_specific))
-
-#define IWL_MAC80211_GET_DVM(_hw) \
- ((struct iwl_priv *) ((struct iwl_op_mode *) \
- (_hw)->priv)->op_mode_specific)
-
-struct iwl_priv {
-
- struct iwl_trans *trans;
- struct device *dev; /* for debug prints only */
- const struct iwl_cfg *cfg;
- const struct iwl_fw *fw;
- const struct iwl_dvm_cfg *lib;
- unsigned long status;
-
- spinlock_t sta_lock;
- struct mutex mutex;
-
- unsigned long transport_queue_stop;
- bool passive_no_rx;
-#define IWL_INVALID_MAC80211_QUEUE 0xff
- u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
- atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
-
- unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
-
- /* ieee device used by generic ieee processing code */
- struct ieee80211_hw *hw;
-
- struct napi_struct *napi;
-
- struct list_head calib_results;
-
- struct workqueue_struct *workqueue;
-
- struct iwl_hw_params hw_params;
-
- enum ieee80211_band band;
- u8 valid_contexts;
-
- void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb);
-
- struct iwl_notif_wait_data notif_wait;
-
- /* spectrum measurement report caching */
- struct iwl_spectrum_notification measure_report;
- u8 measurement_status;
-
- /* ucode beacon time */
- u32 ucode_beacon_time;
- int missed_beacon_threshold;
-
- /* track IBSS manager (last beacon) status */
- u32 ibss_manager;
-
- /* jiffies when last recovery from statistics was performed */
- unsigned long rx_statistics_jiffies;
-
- /*counters */
- u32 rx_handlers_stats[REPLY_MAX];
-
- /* rf reset */
- struct iwl_rf_reset rf_reset;
-
- /* firmware reload counter and timestamp */
- unsigned long reload_jiffies;
- int reload_count;
- bool ucode_loaded;
-
- u8 plcp_delta_threshold;
-
- /* thermal calibration */
- s32 temperature; /* Celsius */
- s32 last_temperature;
-
- struct iwl_wipan_noa_data __rcu *noa_data;
-
- /* Scan related variables */
- unsigned long scan_start;
- unsigned long scan_start_tsf;
- void *scan_cmd;
- enum ieee80211_band scan_band;
- struct cfg80211_scan_request *scan_request;
- struct ieee80211_vif *scan_vif;
- enum iwl_scan_type scan_type;
- u8 scan_tx_ant[IEEE80211_NUM_BANDS];
- u8 mgmt_tx_ant;
-
- /* max number of station keys */
- u8 sta_key_max_num;
-
- bool new_scan_threshold_behaviour;
-
- bool wowlan;
-
- /* EEPROM MAC addresses */
- struct mac_address addresses[2];
-
- struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX];
-
- __le16 switch_channel;
-
- u8 start_calib;
- struct iwl_sensitivity_data sensitivity_data;
- struct iwl_chain_noise_data chain_noise_data;
- __le16 sensitivity_tbl[HD_TABLE_SIZE];
- __le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES];
-
- struct iwl_ht_config current_ht_config;
-
- /* Rate scaling data */
- u8 retry_rate;
-
- int activity_timer_active;
-
- struct iwl_power_mgr power_data;
- struct iwl_tt_mgmt thermal_throttle;
-
- /* station table variables */
- int num_stations;
- struct iwl_station_entry stations[IWLAGN_STATION_COUNT];
- unsigned long ucode_key_table;
- struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT];
- atomic_t num_aux_in_flight;
-
- u8 mac80211_registered;
-
- /* Indication if ieee80211_ops->open has been called */
- u8 is_open;
-
- enum nl80211_iftype iw_mode;
-
- /* Last Rx'd beacon timestamp */
- u64 timestamp;
-
- struct {
- __le32 flag;
- struct statistics_general_common common;
- struct statistics_rx_non_phy rx_non_phy;
- struct statistics_rx_phy rx_ofdm;
- struct statistics_rx_ht_phy rx_ofdm_ht;
- struct statistics_rx_phy rx_cck;
- struct statistics_tx tx;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- struct statistics_bt_activity bt_activity;
- __le32 num_bt_kills, accum_num_bt_kills;
-#endif
- spinlock_t lock;
- } statistics;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- struct {
- struct statistics_general_common common;
- struct statistics_rx_non_phy rx_non_phy;
- struct statistics_rx_phy rx_ofdm;
- struct statistics_rx_ht_phy rx_ofdm_ht;
- struct statistics_rx_phy rx_cck;
- struct statistics_tx tx;
- struct statistics_bt_activity bt_activity;
- } accum_stats, delta_stats, max_delta_stats;
-#endif
-
- /*
- * reporting the number of tids has AGG on. 0 means
- * no AGGREGATION
- */
- u8 agg_tids_count;
-
- struct iwl_rx_phy_res last_phy_res;
- u32 ampdu_ref;
- bool last_phy_res_valid;
-
- /*
- * chain noise reset and gain commands are the
- * two extra calibration commands follows the standard
- * phy calibration commands
- */
- u8 phy_calib_chain_noise_reset_cmd;
- u8 phy_calib_chain_noise_gain_cmd;
-
- /* counts reply_tx error */
- struct reply_tx_error_statistics reply_tx_stats;
- struct reply_agg_tx_error_statistics reply_agg_tx_stats;
-
- /* bt coex */
- u8 bt_enable_flag;
- u8 bt_status;
- u8 bt_traffic_load, last_bt_traffic_load;
- bool bt_ch_announce;
- bool bt_full_concurrent;
- bool bt_ant_couple_ok;
- __le32 kill_ack_mask;
- __le32 kill_cts_mask;
- __le16 bt_valid;
- bool reduced_txpower;
- u16 bt_on_thresh;
- u16 bt_duration;
- u16 dynamic_frag_thresh;
- u8 bt_ci_compliance;
- struct work_struct bt_traffic_change_work;
- bool bt_enable_pspoll;
- struct iwl_rxon_context *cur_rssi_ctx;
- bool bt_is_sco;
-
- struct work_struct restart;
- struct work_struct scan_completed;
- struct work_struct abort_scan;
-
- struct work_struct beacon_update;
- struct iwl_rxon_context *beacon_ctx;
- struct sk_buff *beacon_skb;
- void *beacon_cmd;
-
- struct work_struct tt_work;
- struct work_struct ct_enter;
- struct work_struct ct_exit;
- struct work_struct start_internal_scan;
- struct work_struct tx_flush;
- struct work_struct bt_full_concurrency;
- struct work_struct bt_runtime_config;
-
- struct delayed_work scan_check;
-
- /* TX Power settings */
- s8 tx_power_user_lmt;
- s8 tx_power_next;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- /* debugfs */
- struct dentry *debugfs_dir;
- u32 dbgfs_sram_offset, dbgfs_sram_len;
- bool disable_ht40;
- void *wowlan_sram;
-#endif /* CONFIG_IWLWIFI_DEBUGFS */
-
- struct iwl_nvm_data *nvm_data;
- /* eeprom blob for debugfs */
- u8 *eeprom_blob;
- size_t eeprom_blob_size;
-
- struct work_struct txpower_work;
- u32 calib_disabled;
- struct work_struct run_time_calib_work;
- struct timer_list statistics_periodic;
- struct timer_list ucode_trace;
-
- struct iwl_event_log event_log;
-
-#ifdef CONFIG_IWLWIFI_LEDS
- struct led_classdev led;
- unsigned long blink_on, blink_off;
- bool led_registered;
-#endif
-
- /* WoWLAN GTK rekey data */
- u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
- __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 {
- u32 error_event_table;
- u32 log_event_table;
- } device_pointers;
-
- /* indicator of loaded ucode image */
- enum iwl_ucode_type cur_ucode;
-}; /*iwl_priv */
-
-static inline struct iwl_rxon_context *
-iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif)
-{
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
-
- return vif_priv->ctx;
-}
-
-#define for_each_context(priv, ctx) \
- for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \
- ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \
- if (priv->valid_contexts & BIT(ctx->ctxid))
-
-static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx)
-{
- return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0;
-}
-
-static inline int iwl_is_associated(struct iwl_priv *priv,
- enum iwl_rxon_context_id ctxid)
-{
- return iwl_is_associated_ctx(&priv->contexts[ctxid]);
-}
-
-static inline int iwl_is_any_associated(struct iwl_priv *priv)
-{
- struct iwl_rxon_context *ctx;
- for_each_context(priv, ctx)
- if (iwl_is_associated_ctx(ctx))
- return true;
- return false;
-}
-
-#endif /* __iwl_dev_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c
deleted file mode 100644
index 34b41e5f7cfc..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/devices.c
+++ /dev/null
@@ -1,690 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-/*
- * DVM device-specific data & functions
- */
-#include "iwl-io.h"
-#include "iwl-prph.h"
-#include "iwl-eeprom-parse.h"
-
-#include "agn.h"
-#include "dev.h"
-#include "commands.h"
-
-
-/*
- * 1000 series
- * ===========
- */
-
-/*
- * For 1000, use advance thermal throttling critical temperature threshold,
- * but legacy thermal management implementation for now.
- * This is for the reason of 1000 uCode using advance thermal throttling API
- * but not implement ct_kill_exit based on ct_kill exit temperature
- * so the thermal throttling will still based on legacy thermal throttling
- * management.
- * The code here need to be modified once 1000 uCode has the advanced thermal
- * throttling algorithm in place
- */
-static void iwl1000_set_ct_threshold(struct iwl_priv *priv)
-{
- /* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
- priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
-}
-
-/* NIC configuration for 1000 series */
-static void iwl1000_nic_config(struct iwl_priv *priv)
-{
- /* Setting digital SVR for 1000 card to 1.32V */
- /* locking is acquired in iwl_set_bits_mask_prph() function */
- iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG,
- APMG_SVR_DIGITAL_VOLTAGE_1_32,
- ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK);
-}
-
-/**
- * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time
- * @priv -- pointer to iwl_priv data structure
- * @tsf_bits -- number of bits need to shift for masking)
- */
-static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv,
- u16 tsf_bits)
-{
- return (1 << tsf_bits) - 1;
-}
-
-/**
- * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time
- * @priv -- pointer to iwl_priv data structure
- * @tsf_bits -- number of bits need to shift for masking)
- */
-static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv,
- u16 tsf_bits)
-{
- return ((1 << (32 - tsf_bits)) - 1) << tsf_bits;
-}
-
-/*
- * extended beacon time format
- * time in usec will be changed into a 32-bit value in extended:internal format
- * the extended part is the beacon counts
- * the internal part is the time in usec within one beacon interval
- */
-static u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec,
- u32 beacon_interval)
-{
- u32 quot;
- u32 rem;
- u32 interval = beacon_interval * TIME_UNIT;
-
- if (!interval || !usec)
- return 0;
-
- quot = (usec / interval) &
- (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >>
- IWLAGN_EXT_BEACON_TIME_POS);
- rem = (usec % interval) & iwl_beacon_time_mask_low(priv,
- IWLAGN_EXT_BEACON_TIME_POS);
-
- return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem;
-}
-
-/* base is usually what we get from ucode with each received frame,
- * the same as HW timer counter counting down
- */
-static __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
- u32 addon, u32 beacon_interval)
-{
- u32 base_low = base & iwl_beacon_time_mask_low(priv,
- IWLAGN_EXT_BEACON_TIME_POS);
- u32 addon_low = addon & iwl_beacon_time_mask_low(priv,
- IWLAGN_EXT_BEACON_TIME_POS);
- u32 interval = beacon_interval * TIME_UNIT;
- u32 res = (base & iwl_beacon_time_mask_high(priv,
- IWLAGN_EXT_BEACON_TIME_POS)) +
- (addon & iwl_beacon_time_mask_high(priv,
- IWLAGN_EXT_BEACON_TIME_POS));
-
- if (base_low > addon_low)
- res += base_low - addon_low;
- else if (base_low < addon_low) {
- res += interval + base_low - addon_low;
- res += (1 << IWLAGN_EXT_BEACON_TIME_POS);
- } else
- res += (1 << IWLAGN_EXT_BEACON_TIME_POS);
-
- return cpu_to_le32(res);
-}
-
-static const struct iwl_sensitivity_ranges iwl1000_sensitivity = {
- .min_nrg_cck = 95,
- .auto_corr_min_ofdm = 90,
- .auto_corr_min_ofdm_mrc = 170,
- .auto_corr_min_ofdm_x1 = 120,
- .auto_corr_min_ofdm_mrc_x1 = 240,
-
- .auto_corr_max_ofdm = 120,
- .auto_corr_max_ofdm_mrc = 210,
- .auto_corr_max_ofdm_x1 = 155,
- .auto_corr_max_ofdm_mrc_x1 = 290,
-
- .auto_corr_min_cck = 125,
- .auto_corr_max_cck = 200,
- .auto_corr_min_cck_mrc = 170,
- .auto_corr_max_cck_mrc = 400,
- .nrg_th_cck = 95,
- .nrg_th_ofdm = 95,
-
- .barker_corr_th_min = 190,
- .barker_corr_th_min_mrc = 390,
- .nrg_th_cca = 62,
-};
-
-static void iwl1000_hw_set_hw_params(struct iwl_priv *priv)
-{
- iwl1000_set_ct_threshold(priv);
-
- /* Set initial sensitivity parameters */
- priv->hw_params.sens = &iwl1000_sensitivity;
-}
-
-const struct iwl_dvm_cfg iwl_dvm_1000_cfg = {
- .set_hw_params = iwl1000_hw_set_hw_params,
- .nic_config = iwl1000_nic_config,
- .temperature = iwlagn_temperature,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
-};
-
-
-/*
- * 2000 series
- * ===========
- */
-
-static void iwl2000_set_ct_threshold(struct iwl_priv *priv)
-{
- /* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
- priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
-}
-
-/* NIC configuration for 2000 series */
-static void iwl2000_nic_config(struct iwl_priv *priv)
-{
- iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
- CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER);
-}
-
-static const struct iwl_sensitivity_ranges iwl2000_sensitivity = {
- .min_nrg_cck = 97,
- .auto_corr_min_ofdm = 80,
- .auto_corr_min_ofdm_mrc = 128,
- .auto_corr_min_ofdm_x1 = 105,
- .auto_corr_min_ofdm_mrc_x1 = 192,
-
- .auto_corr_max_ofdm = 145,
- .auto_corr_max_ofdm_mrc = 232,
- .auto_corr_max_ofdm_x1 = 110,
- .auto_corr_max_ofdm_mrc_x1 = 232,
-
- .auto_corr_min_cck = 125,
- .auto_corr_max_cck = 175,
- .auto_corr_min_cck_mrc = 160,
- .auto_corr_max_cck_mrc = 310,
- .nrg_th_cck = 97,
- .nrg_th_ofdm = 100,
-
- .barker_corr_th_min = 190,
- .barker_corr_th_min_mrc = 390,
- .nrg_th_cca = 62,
-};
-
-static void iwl2000_hw_set_hw_params(struct iwl_priv *priv)
-{
- iwl2000_set_ct_threshold(priv);
-
- /* Set initial sensitivity parameters */
- priv->hw_params.sens = &iwl2000_sensitivity;
-}
-
-const struct iwl_dvm_cfg iwl_dvm_2000_cfg = {
- .set_hw_params = iwl2000_hw_set_hw_params,
- .nic_config = iwl2000_nic_config,
- .temperature = iwlagn_temperature,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
- .hd_v2 = true,
- .need_temp_offset_calib = true,
- .temp_offset_v2 = true,
-};
-
-const struct iwl_dvm_cfg iwl_dvm_105_cfg = {
- .set_hw_params = iwl2000_hw_set_hw_params,
- .nic_config = iwl2000_nic_config,
- .temperature = iwlagn_temperature,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
- .hd_v2 = true,
- .need_temp_offset_calib = true,
- .temp_offset_v2 = true,
- .adv_pm = true,
-};
-
-static const struct iwl_dvm_bt_params iwl2030_bt_params = {
- /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
- .advanced_bt_coexist = true,
- .agg_time_limit = BT_AGG_THRESHOLD_DEF,
- .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
- .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
- .bt_sco_disable = true,
- .bt_session_2 = true,
-};
-
-const struct iwl_dvm_cfg iwl_dvm_2030_cfg = {
- .set_hw_params = iwl2000_hw_set_hw_params,
- .nic_config = iwl2000_nic_config,
- .temperature = iwlagn_temperature,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
- .hd_v2 = true,
- .bt_params = &iwl2030_bt_params,
- .need_temp_offset_calib = true,
- .temp_offset_v2 = true,
- .adv_pm = true,
-};
-
-/*
- * 5000 series
- * ===========
- */
-
-/* NIC configuration for 5000 series */
-static const struct iwl_sensitivity_ranges iwl5000_sensitivity = {
- .min_nrg_cck = 100,
- .auto_corr_min_ofdm = 90,
- .auto_corr_min_ofdm_mrc = 170,
- .auto_corr_min_ofdm_x1 = 105,
- .auto_corr_min_ofdm_mrc_x1 = 220,
-
- .auto_corr_max_ofdm = 120,
- .auto_corr_max_ofdm_mrc = 210,
- .auto_corr_max_ofdm_x1 = 120,
- .auto_corr_max_ofdm_mrc_x1 = 240,
-
- .auto_corr_min_cck = 125,
- .auto_corr_max_cck = 200,
- .auto_corr_min_cck_mrc = 200,
- .auto_corr_max_cck_mrc = 400,
- .nrg_th_cck = 100,
- .nrg_th_ofdm = 100,
-
- .barker_corr_th_min = 190,
- .barker_corr_th_min_mrc = 390,
- .nrg_th_cca = 62,
-};
-
-static const struct iwl_sensitivity_ranges iwl5150_sensitivity = {
- .min_nrg_cck = 95,
- .auto_corr_min_ofdm = 90,
- .auto_corr_min_ofdm_mrc = 170,
- .auto_corr_min_ofdm_x1 = 105,
- .auto_corr_min_ofdm_mrc_x1 = 220,
-
- .auto_corr_max_ofdm = 120,
- .auto_corr_max_ofdm_mrc = 210,
- /* max = min for performance bug in 5150 DSP */
- .auto_corr_max_ofdm_x1 = 105,
- .auto_corr_max_ofdm_mrc_x1 = 220,
-
- .auto_corr_min_cck = 125,
- .auto_corr_max_cck = 200,
- .auto_corr_min_cck_mrc = 170,
- .auto_corr_max_cck_mrc = 400,
- .nrg_th_cck = 95,
- .nrg_th_ofdm = 95,
-
- .barker_corr_th_min = 190,
- .barker_corr_th_min_mrc = 390,
- .nrg_th_cca = 62,
-};
-
-#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5)
-
-static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv)
-{
- u16 temperature, voltage;
-
- temperature = le16_to_cpu(priv->nvm_data->kelvin_temperature);
- voltage = le16_to_cpu(priv->nvm_data->kelvin_voltage);
-
- /* offset = temp - volt / coeff */
- return (s32)(temperature -
- voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF);
-}
-
-static void iwl5150_set_ct_threshold(struct iwl_priv *priv)
-{
- const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF;
- s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) -
- iwl_temp_calib_to_offset(priv);
-
- priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef;
-}
-
-static void iwl5000_set_ct_threshold(struct iwl_priv *priv)
-{
- /* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY;
-}
-
-static void iwl5000_hw_set_hw_params(struct iwl_priv *priv)
-{
- iwl5000_set_ct_threshold(priv);
-
- /* Set initial sensitivity parameters */
- priv->hw_params.sens = &iwl5000_sensitivity;
-}
-
-static void iwl5150_hw_set_hw_params(struct iwl_priv *priv)
-{
- iwl5150_set_ct_threshold(priv);
-
- /* Set initial sensitivity parameters */
- priv->hw_params.sens = &iwl5150_sensitivity;
-}
-
-static void iwl5150_temperature(struct iwl_priv *priv)
-{
- u32 vt = 0;
- s32 offset = iwl_temp_calib_to_offset(priv);
-
- vt = le32_to_cpu(priv->statistics.common.temperature);
- vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset;
- /* now vt hold the temperature in Kelvin */
- priv->temperature = KELVIN_TO_CELSIUS(vt);
- iwl_tt_handler(priv);
-}
-
-static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
- struct ieee80211_channel_switch *ch_switch)
-{
- /*
- * MULTI-FIXME
- * See iwlagn_mac_channel_switch.
- */
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- struct iwl5000_channel_switch_cmd cmd;
- u32 switch_time_in_usec, ucode_switch_time;
- u16 ch;
- u32 tsf_low;
- u8 switch_count;
- u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval);
- struct ieee80211_vif *vif = ctx->vif;
- struct iwl_host_cmd hcmd = {
- .id = REPLY_CHANNEL_SWITCH,
- .len = { sizeof(cmd), },
- .data = { &cmd, },
- };
-
- cmd.band = priv->band == IEEE80211_BAND_2GHZ;
- ch = ch_switch->chandef.chan->hw_value;
- IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
- ctx->active.channel, ch);
- cmd.channel = cpu_to_le16(ch);
- cmd.rxon_flags = ctx->staging.flags;
- cmd.rxon_filter_flags = ctx->staging.filter_flags;
- switch_count = ch_switch->count;
- tsf_low = ch_switch->timestamp & 0x0ffffffff;
- /*
- * calculate the ucode channel switch time
- * adding TSF as one of the factor for when to switch
- */
- if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
- if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
- beacon_interval)) {
- switch_count -= (priv->ucode_beacon_time -
- tsf_low) / beacon_interval;
- } else
- switch_count = 0;
- }
- if (switch_count <= 1)
- cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
- else {
- switch_time_in_usec =
- vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
- ucode_switch_time = iwl_usecs_to_beacons(priv,
- switch_time_in_usec,
- beacon_interval);
- cmd.switch_time = iwl_add_beacon_time(priv,
- priv->ucode_beacon_time,
- ucode_switch_time,
- beacon_interval);
- }
- IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
- cmd.switch_time);
- cmd.expect_beacon =
- ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR;
-
- return iwl_dvm_send_cmd(priv, &hcmd);
-}
-
-const struct iwl_dvm_cfg iwl_dvm_5000_cfg = {
- .set_hw_params = iwl5000_hw_set_hw_params,
- .set_channel_switch = iwl5000_hw_channel_switch,
- .temperature = iwlagn_temperature,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
- .no_idle_support = true,
-};
-
-const struct iwl_dvm_cfg iwl_dvm_5150_cfg = {
- .set_hw_params = iwl5150_hw_set_hw_params,
- .set_channel_switch = iwl5000_hw_channel_switch,
- .temperature = iwl5150_temperature,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
- .no_idle_support = true,
- .no_xtal_calib = true,
-};
-
-
-
-/*
- * 6000 series
- * ===========
- */
-
-static void iwl6000_set_ct_threshold(struct iwl_priv *priv)
-{
- /* want Celsius */
- priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
- priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD;
-}
-
-/* NIC configuration for 6000 series */
-static void iwl6000_nic_config(struct iwl_priv *priv)
-{
- switch (priv->cfg->device_family) {
- case IWL_DEVICE_FAMILY_6005:
- case IWL_DEVICE_FAMILY_6030:
- case IWL_DEVICE_FAMILY_6000:
- break;
- case IWL_DEVICE_FAMILY_6000i:
- /* 2x2 IPA phy type */
- iwl_write32(priv->trans, CSR_GP_DRIVER_REG,
- CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA);
- break;
- case IWL_DEVICE_FAMILY_6050:
- /* Indicate calibration version to uCode. */
- if (priv->nvm_data->calib_version >= 6)
- iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
- CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6);
- break;
- case IWL_DEVICE_FAMILY_6150:
- /* Indicate calibration version to uCode. */
- if (priv->nvm_data->calib_version >= 6)
- iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
- CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6);
- iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG,
- CSR_GP_DRIVER_REG_BIT_6050_1x2);
- break;
- default:
- WARN_ON(1);
- }
-}
-
-static const struct iwl_sensitivity_ranges iwl6000_sensitivity = {
- .min_nrg_cck = 110,
- .auto_corr_min_ofdm = 80,
- .auto_corr_min_ofdm_mrc = 128,
- .auto_corr_min_ofdm_x1 = 105,
- .auto_corr_min_ofdm_mrc_x1 = 192,
-
- .auto_corr_max_ofdm = 145,
- .auto_corr_max_ofdm_mrc = 232,
- .auto_corr_max_ofdm_x1 = 110,
- .auto_corr_max_ofdm_mrc_x1 = 232,
-
- .auto_corr_min_cck = 125,
- .auto_corr_max_cck = 175,
- .auto_corr_min_cck_mrc = 160,
- .auto_corr_max_cck_mrc = 310,
- .nrg_th_cck = 110,
- .nrg_th_ofdm = 110,
-
- .barker_corr_th_min = 190,
- .barker_corr_th_min_mrc = 336,
- .nrg_th_cca = 62,
-};
-
-static void iwl6000_hw_set_hw_params(struct iwl_priv *priv)
-{
- iwl6000_set_ct_threshold(priv);
-
- /* Set initial sensitivity parameters */
- priv->hw_params.sens = &iwl6000_sensitivity;
-
-}
-
-static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
- struct ieee80211_channel_switch *ch_switch)
-{
- /*
- * MULTI-FIXME
- * See iwlagn_mac_channel_switch.
- */
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- struct iwl6000_channel_switch_cmd *cmd;
- u32 switch_time_in_usec, ucode_switch_time;
- u16 ch;
- u32 tsf_low;
- u8 switch_count;
- u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval);
- struct ieee80211_vif *vif = ctx->vif;
- struct iwl_host_cmd hcmd = {
- .id = REPLY_CHANNEL_SWITCH,
- .len = { sizeof(*cmd), },
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- };
- int err;
-
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
-
- hcmd.data[0] = cmd;
-
- cmd->band = priv->band == IEEE80211_BAND_2GHZ;
- ch = ch_switch->chandef.chan->hw_value;
- IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
- ctx->active.channel, ch);
- cmd->channel = cpu_to_le16(ch);
- cmd->rxon_flags = ctx->staging.flags;
- cmd->rxon_filter_flags = ctx->staging.filter_flags;
- switch_count = ch_switch->count;
- tsf_low = ch_switch->timestamp & 0x0ffffffff;
- /*
- * calculate the ucode channel switch time
- * adding TSF as one of the factor for when to switch
- */
- if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
- if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
- beacon_interval)) {
- switch_count -= (priv->ucode_beacon_time -
- tsf_low) / beacon_interval;
- } else
- switch_count = 0;
- }
- if (switch_count <= 1)
- cmd->switch_time = cpu_to_le32(priv->ucode_beacon_time);
- else {
- switch_time_in_usec =
- vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
- ucode_switch_time = iwl_usecs_to_beacons(priv,
- switch_time_in_usec,
- beacon_interval);
- cmd->switch_time = iwl_add_beacon_time(priv,
- priv->ucode_beacon_time,
- ucode_switch_time,
- beacon_interval);
- }
- IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
- cmd->switch_time);
- cmd->expect_beacon =
- ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR;
-
- err = iwl_dvm_send_cmd(priv, &hcmd);
- kfree(cmd);
- return err;
-}
-
-const struct iwl_dvm_cfg iwl_dvm_6000_cfg = {
- .set_hw_params = iwl6000_hw_set_hw_params,
- .set_channel_switch = iwl6000_hw_channel_switch,
- .nic_config = iwl6000_nic_config,
- .temperature = iwlagn_temperature,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
-};
-
-const struct iwl_dvm_cfg iwl_dvm_6005_cfg = {
- .set_hw_params = iwl6000_hw_set_hw_params,
- .set_channel_switch = iwl6000_hw_channel_switch,
- .nic_config = iwl6000_nic_config,
- .temperature = iwlagn_temperature,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
- .need_temp_offset_calib = true,
-};
-
-const struct iwl_dvm_cfg iwl_dvm_6050_cfg = {
- .set_hw_params = iwl6000_hw_set_hw_params,
- .set_channel_switch = iwl6000_hw_channel_switch,
- .nic_config = iwl6000_nic_config,
- .temperature = iwlagn_temperature,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1500,
-};
-
-static const struct iwl_dvm_bt_params iwl6000_bt_params = {
- /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
- .advanced_bt_coexist = true,
- .agg_time_limit = BT_AGG_THRESHOLD_DEF,
- .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
- .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
- .bt_sco_disable = true,
-};
-
-const struct iwl_dvm_cfg iwl_dvm_6030_cfg = {
- .set_hw_params = iwl6000_hw_set_hw_params,
- .set_channel_switch = iwl6000_hw_channel_switch,
- .nic_config = iwl6000_nic_config,
- .temperature = iwlagn_temperature,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
- .bt_params = &iwl6000_bt_params,
- .need_temp_offset_calib = true,
- .adv_pm = true,
-};
diff --git a/drivers/net/wireless/iwlwifi/dvm/led.c b/drivers/net/wireless/iwlwifi/dvm/led.c
deleted file mode 100644
index ca4d6692cc4e..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/led.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <net/mac80211.h>
-#include <linux/etherdevice.h>
-#include <asm/unaligned.h>
-#include "iwl-io.h"
-#include "iwl-trans.h"
-#include "iwl-modparams.h"
-#include "dev.h"
-#include "agn.h"
-
-/* Throughput OFF time(ms) ON time (ms)
- * >300 25 25
- * >200 to 300 40 40
- * >100 to 200 55 55
- * >70 to 100 65 65
- * >50 to 70 75 75
- * >20 to 50 85 85
- * >10 to 20 95 95
- * >5 to 10 110 110
- * >1 to 5 130 130
- * >0 to 1 167 167
- * <=0 SOLID ON
- */
-static const struct ieee80211_tpt_blink iwl_blink[] = {
- { .throughput = 0, .blink_time = 334 },
- { .throughput = 1 * 1024 - 1, .blink_time = 260 },
- { .throughput = 5 * 1024 - 1, .blink_time = 220 },
- { .throughput = 10 * 1024 - 1, .blink_time = 190 },
- { .throughput = 20 * 1024 - 1, .blink_time = 170 },
- { .throughput = 50 * 1024 - 1, .blink_time = 150 },
- { .throughput = 70 * 1024 - 1, .blink_time = 130 },
- { .throughput = 100 * 1024 - 1, .blink_time = 110 },
- { .throughput = 200 * 1024 - 1, .blink_time = 80 },
- { .throughput = 300 * 1024 - 1, .blink_time = 50 },
-};
-
-/* Set led register off */
-void iwlagn_led_enable(struct iwl_priv *priv)
-{
- iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
-}
-
-/*
- * Adjust led blink rate to compensate on a MAC Clock difference on every HW
- * Led blink rate analysis showed an average deviation of 20% on 5000 series
- * and up.
- * Need to compensate on the led on/off time per HW according to the deviation
- * to achieve the desired led frequency
- * The calculation is: (100-averageDeviation)/100 * blinkTime
- * For code efficiency the calculation will be:
- * compensation = (100 - averageDeviation) * 64 / 100
- * NewBlinkTime = (compensation * BlinkTime) / 64
- */
-static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
- u8 time, u16 compensation)
-{
- if (!compensation) {
- IWL_ERR(priv, "undefined blink compensation: "
- "use pre-defined blinking time\n");
- return time;
- }
-
- return (u8)((time * compensation) >> 6);
-}
-
-static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd)
-{
- struct iwl_host_cmd cmd = {
- .id = REPLY_LEDS_CMD,
- .len = { sizeof(struct iwl_led_cmd), },
- .data = { led_cmd, },
- .flags = CMD_ASYNC,
- };
- u32 reg;
-
- reg = iwl_read32(priv->trans, CSR_LED_REG);
- if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
- iwl_write32(priv->trans, CSR_LED_REG,
- reg & CSR_LED_BSM_CTRL_MSK);
-
- return iwl_dvm_send_cmd(priv, &cmd);
-}
-
-/* Set led pattern command */
-static int iwl_led_cmd(struct iwl_priv *priv,
- unsigned long on,
- unsigned long off)
-{
- struct iwl_led_cmd led_cmd = {
- .id = IWL_LED_LINK,
- .interval = IWL_DEF_LED_INTRVL
- };
- int ret;
-
- if (!test_bit(STATUS_READY, &priv->status))
- return -EBUSY;
-
- if (priv->blink_on == on && priv->blink_off == off)
- return 0;
-
- if (off == 0) {
- /* led is SOLID_ON */
- on = IWL_LED_SOLID;
- }
-
- IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n",
- priv->cfg->base_params->led_compensation);
- led_cmd.on = iwl_blink_compensation(priv, on,
- priv->cfg->base_params->led_compensation);
- led_cmd.off = iwl_blink_compensation(priv, off,
- priv->cfg->base_params->led_compensation);
-
- ret = iwl_send_led_cmd(priv, &led_cmd);
- if (!ret) {
- priv->blink_on = on;
- priv->blink_off = off;
- }
- return ret;
-}
-
-static void iwl_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
- unsigned long on = 0;
-
- if (brightness > 0)
- on = IWL_LED_SOLID;
-
- iwl_led_cmd(priv, on, 0);
-}
-
-static int iwl_led_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on,
- unsigned long *delay_off)
-{
- struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
-
- return iwl_led_cmd(priv, *delay_on, *delay_off);
-}
-
-void iwl_leds_init(struct iwl_priv *priv)
-{
- int mode = iwlwifi_mod_params.led_mode;
- int ret;
-
- if (mode == IWL_LED_DISABLE) {
- IWL_INFO(priv, "Led disabled\n");
- return;
- }
- if (mode == IWL_LED_DEFAULT)
- mode = priv->cfg->led_mode;
-
- priv->led.name = kasprintf(GFP_KERNEL, "%s-led",
- wiphy_name(priv->hw->wiphy));
- priv->led.brightness_set = iwl_led_brightness_set;
- priv->led.blink_set = iwl_led_blink_set;
- priv->led.max_brightness = 1;
-
- switch (mode) {
- case IWL_LED_DEFAULT:
- WARN_ON(1);
- break;
- case IWL_LED_BLINK:
- priv->led.default_trigger =
- ieee80211_create_tpt_led_trigger(priv->hw,
- IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
- iwl_blink, ARRAY_SIZE(iwl_blink));
- break;
- case IWL_LED_RF_STATE:
- priv->led.default_trigger =
- ieee80211_get_radio_led_name(priv->hw);
- break;
- }
-
- ret = led_classdev_register(priv->trans->dev, &priv->led);
- if (ret) {
- kfree(priv->led.name);
- return;
- }
-
- priv->led_registered = true;
-}
-
-void iwl_leds_exit(struct iwl_priv *priv)
-{
- if (!priv->led_registered)
- return;
-
- led_classdev_unregister(&priv->led);
- kfree(priv->led.name);
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/led.h b/drivers/net/wireless/iwlwifi/dvm/led.h
deleted file mode 100644
index 1c6b2252d0f2..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/led.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#ifndef __iwl_leds_h__
-#define __iwl_leds_h__
-
-
-struct iwl_priv;
-
-#define IWL_LED_SOLID 11
-#define IWL_DEF_LED_INTRVL cpu_to_le32(1000)
-
-#define IWL_LED_ACTIVITY (0<<1)
-#define IWL_LED_LINK (1<<1)
-
-#ifdef CONFIG_IWLWIFI_LEDS
-void iwlagn_led_enable(struct iwl_priv *priv);
-void iwl_leds_init(struct iwl_priv *priv);
-void iwl_leds_exit(struct iwl_priv *priv);
-#else
-static inline void iwlagn_led_enable(struct iwl_priv *priv)
-{
-}
-static inline void iwl_leds_init(struct iwl_priv *priv)
-{
-}
-static inline void iwl_leds_exit(struct iwl_priv *priv)
-{
-}
-#endif
-
-#endif /* __iwl_leds_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c
deleted file mode 100644
index e18629a16fb0..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/lib.c
+++ /dev/null
@@ -1,1300 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/etherdevice.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <net/mac80211.h>
-
-#include "iwl-io.h"
-#include "iwl-agn-hw.h"
-#include "iwl-trans.h"
-#include "iwl-modparams.h"
-
-#include "dev.h"
-#include "agn.h"
-
-int iwlagn_hw_valid_rtc_data_addr(u32 addr)
-{
- return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) &&
- (addr < IWLAGN_RTC_DATA_UPPER_BOUND);
-}
-
-int iwlagn_send_tx_power(struct iwl_priv *priv)
-{
- struct iwlagn_tx_power_dbm_cmd tx_power_cmd;
- u8 tx_ant_cfg_cmd;
-
- if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status),
- "TX Power requested while scanning!\n"))
- return -EAGAIN;
-
- /* half dBm need to multiply */
- tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt);
-
- if (tx_power_cmd.global_lmt > priv->nvm_data->max_tx_pwr_half_dbm) {
- /*
- * For the newer devices which using enhanced/extend tx power
- * table in EEPROM, the format is in half dBm. driver need to
- * convert to dBm format before report to mac80211.
- * By doing so, there is a possibility of 1/2 dBm resolution
- * lost. driver will perform "round-up" operation before
- * reporting, but it will cause 1/2 dBm tx power over the
- * regulatory limit. Perform the checking here, if the
- * "tx_power_user_lmt" is higher than EEPROM value (in
- * half-dBm format), lower the tx power based on EEPROM
- */
- tx_power_cmd.global_lmt =
- priv->nvm_data->max_tx_pwr_half_dbm;
- }
- tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED;
- tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO;
-
- if (IWL_UCODE_API(priv->fw->ucode_ver) == 1)
- tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1;
- else
- tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD;
-
- return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0,
- sizeof(tx_power_cmd), &tx_power_cmd);
-}
-
-void iwlagn_temperature(struct iwl_priv *priv)
-{
- lockdep_assert_held(&priv->statistics.lock);
-
- /* store temperature from correct statistics (in Celsius) */
- priv->temperature = le32_to_cpu(priv->statistics.common.temperature);
- iwl_tt_handler(priv);
-}
-
-int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band)
-{
- int idx = 0;
- int band_offset = 0;
-
- /* HT rate format: mac80211 wants an MCS number, which is just LSB */
- if (rate_n_flags & RATE_MCS_HT_MSK) {
- idx = (rate_n_flags & 0xff);
- return idx;
- /* Legacy rate format, search for match in table */
- } else {
- if (band == IEEE80211_BAND_5GHZ)
- band_offset = IWL_FIRST_OFDM_RATE;
- for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
- if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF))
- return idx - band_offset;
- }
-
- return -1;
-}
-
-int iwlagn_manage_ibss_station(struct iwl_priv *priv,
- struct ieee80211_vif *vif, bool add)
-{
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
-
- if (add)
- return iwlagn_add_bssid_station(priv, vif_priv->ctx,
- vif->bss_conf.bssid,
- &vif_priv->ibss_bssid_sta_id);
- return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id,
- vif->bss_conf.bssid);
-}
-
-/**
- * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode
- *
- * pre-requirements:
- * 1. acquire mutex before calling
- * 2. make sure rf is on and not in exit state
- */
-int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
-{
- struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = {
- .flush_control = cpu_to_le16(IWL_DROP_ALL),
- };
- struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = {
- .flush_control = cpu_to_le16(IWL_DROP_ALL),
- };
-
- u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
- IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK;
-
- if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
- queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK |
- IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK |
- IWL_PAN_SCD_MGMT_MSK |
- IWL_PAN_SCD_MULTICAST_MSK;
-
- if (priv->nvm_data->sku_cap_11n_enable)
- queue_control |= IWL_AGG_TX_QUEUE_MSK;
-
- if (scd_q_msk)
- queue_control = scd_q_msk;
-
- IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control);
- flush_cmd_v3.queue_control = cpu_to_le32(queue_control);
- flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control);
-
- if (IWL_UCODE_API(priv->fw->ucode_ver) > 2)
- return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
- sizeof(flush_cmd_v3),
- &flush_cmd_v3);
- return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
- sizeof(flush_cmd_v2), &flush_cmd_v2);
-}
-
-void iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
-{
- mutex_lock(&priv->mutex);
- ieee80211_stop_queues(priv->hw);
- if (iwlagn_txfifo_flush(priv, 0)) {
- IWL_ERR(priv, "flush request fail\n");
- goto done;
- }
- IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
- iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
-done:
- ieee80211_wake_queues(priv->hw);
- mutex_unlock(&priv->mutex);
-}
-
-/*
- * BT coex
- */
-/* Notmal TDM */
-static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaeaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0x00004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
-};
-
-
-/* Loose Coex */
-static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaeaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
-};
-
-/* Full concurrency */
-static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000),
-};
-
-void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
-{
- struct iwl_basic_bt_cmd basic = {
- .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT,
- .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT,
- .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT,
- .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT,
- };
- struct iwl_bt_cmd_v1 bt_cmd_v1;
- struct iwl_bt_cmd_v2 bt_cmd_v2;
- int ret;
-
- BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
- sizeof(basic.bt3_lookup_table));
-
- if (priv->lib->bt_params) {
- /*
- * newer generation of devices (2000 series and newer)
- * use the version 2 of the bt command
- * we need to make sure sending the host command
- * with correct data structure to avoid uCode assert
- */
- if (priv->lib->bt_params->bt_session_2) {
- bt_cmd_v2.prio_boost = cpu_to_le32(
- priv->lib->bt_params->bt_prio_boost);
- bt_cmd_v2.tx_prio_boost = 0;
- bt_cmd_v2.rx_prio_boost = 0;
- } else {
- /* older version only has 8 bits */
- WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF);
- bt_cmd_v1.prio_boost =
- priv->lib->bt_params->bt_prio_boost;
- bt_cmd_v1.tx_prio_boost = 0;
- bt_cmd_v1.rx_prio_boost = 0;
- }
- } else {
- IWL_ERR(priv, "failed to construct BT Coex Config\n");
- return;
- }
-
- /*
- * Possible situations when BT needs to take over for receive,
- * at the same time where STA needs to response to AP's frame(s),
- * reduce the tx power of the required response frames, by that,
- * allow the concurrent BT receive & WiFi transmit
- * (BT - ANT A, WiFi -ANT B), without interference to one another
- *
- * Reduced tx power apply to control frames only (ACK/Back/CTS)
- * when indicated by the BT config command
- */
- basic.kill_ack_mask = priv->kill_ack_mask;
- basic.kill_cts_mask = priv->kill_cts_mask;
- if (priv->reduced_txpower)
- basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR;
- basic.valid = priv->bt_valid;
-
- /*
- * Configure BT coex mode to "no coexistence" when the
- * user disabled BT coexistence, we have no interface
- * (might be in monitor mode), or the interface is in
- * IBSS mode (no proper uCode support for coex then).
- */
- if (!iwlwifi_mod_params.bt_coex_active ||
- priv->iw_mode == NL80211_IFTYPE_ADHOC) {
- basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED;
- } else {
- basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W <<
- IWLAGN_BT_FLAG_COEX_MODE_SHIFT;
-
- if (!priv->bt_enable_pspoll)
- basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
- else
- basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
-
- if (priv->bt_ch_announce)
- basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION;
- IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags);
- }
- priv->bt_enable_flag = basic.flags;
- if (priv->bt_full_concurrent)
- memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup,
- sizeof(iwlagn_concurrent_lookup));
- else
- memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup,
- sizeof(iwlagn_def_3w_lookup));
-
- IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n",
- basic.flags ? "active" : "disabled",
- priv->bt_full_concurrent ?
- "full concurrency" : "3-wire");
-
- if (priv->lib->bt_params->bt_session_2) {
- memcpy(&bt_cmd_v2.basic, &basic,
- sizeof(basic));
- ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
- 0, sizeof(bt_cmd_v2), &bt_cmd_v2);
- } else {
- memcpy(&bt_cmd_v1.basic, &basic,
- sizeof(basic));
- ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
- 0, sizeof(bt_cmd_v1), &bt_cmd_v1);
- }
- if (ret)
- IWL_ERR(priv, "failed to send BT Coex Config\n");
-
-}
-
-void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena)
-{
- struct iwl_rxon_context *ctx, *found_ctx = NULL;
- bool found_ap = false;
-
- lockdep_assert_held(&priv->mutex);
-
- /* Check whether AP or GO mode is active. */
- if (rssi_ena) {
- for_each_context(priv, ctx) {
- if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP &&
- iwl_is_associated_ctx(ctx)) {
- found_ap = true;
- break;
- }
- }
- }
-
- /*
- * If disable was received or If GO/AP mode, disable RSSI
- * measurements.
- */
- if (!rssi_ena || found_ap) {
- if (priv->cur_rssi_ctx) {
- ctx = priv->cur_rssi_ctx;
- ieee80211_disable_rssi_reports(ctx->vif);
- priv->cur_rssi_ctx = NULL;
- }
- return;
- }
-
- /*
- * If rssi measurements need to be enabled, consider all cases now.
- * Figure out how many contexts are active.
- */
- for_each_context(priv, ctx) {
- if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION &&
- iwl_is_associated_ctx(ctx)) {
- found_ctx = ctx;
- break;
- }
- }
-
- /*
- * rssi monitor already enabled for the correct interface...nothing
- * to do.
- */
- if (found_ctx == priv->cur_rssi_ctx)
- return;
-
- /*
- * Figure out if rssi monitor is currently enabled, and needs
- * to be changed. If rssi monitor is already enabled, disable
- * it first else just enable rssi measurements on the
- * interface found above.
- */
- if (priv->cur_rssi_ctx) {
- ctx = priv->cur_rssi_ctx;
- if (ctx->vif)
- ieee80211_disable_rssi_reports(ctx->vif);
- }
-
- priv->cur_rssi_ctx = found_ctx;
-
- if (!found_ctx)
- return;
-
- ieee80211_enable_rssi_reports(found_ctx->vif,
- IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD,
- IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD);
-}
-
-static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg)
-{
- return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
- BT_UART_MSG_FRAME3SCOESCO_POS;
-}
-
-static void iwlagn_bt_traffic_change_work(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, bt_traffic_change_work);
- struct iwl_rxon_context *ctx;
- int smps_request = -1;
-
- if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) {
- /* bt coex disabled */
- return;
- }
-
- /*
- * Note: bt_traffic_load can be overridden by scan complete and
- * coex profile notifications. Ignore that since only bad consequence
- * can be not matching debug print with actual state.
- */
- IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n",
- priv->bt_traffic_load);
-
- switch (priv->bt_traffic_load) {
- case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
- if (priv->bt_status)
- smps_request = IEEE80211_SMPS_DYNAMIC;
- else
- smps_request = IEEE80211_SMPS_AUTOMATIC;
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
- smps_request = IEEE80211_SMPS_DYNAMIC;
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
- case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
- smps_request = IEEE80211_SMPS_STATIC;
- break;
- default:
- IWL_ERR(priv, "Invalid BT traffic load: %d\n",
- priv->bt_traffic_load);
- break;
- }
-
- mutex_lock(&priv->mutex);
-
- /*
- * We can not send command to firmware while scanning. When the scan
- * complete we will schedule this work again. We do check with mutex
- * locked to prevent new scan request to arrive. We do not check
- * STATUS_SCANNING to avoid race when queue_work two times from
- * different notifications, but quit and not perform any work at all.
- */
- if (test_bit(STATUS_SCAN_HW, &priv->status))
- goto out;
-
- iwl_update_chain_flags(priv);
-
- if (smps_request != -1) {
- priv->current_ht_config.smps = smps_request;
- for_each_context(priv, ctx) {
- if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION)
- ieee80211_request_smps(ctx->vif, smps_request);
- }
- }
-
- /*
- * Dynamic PS poll related functionality. Adjust RSSI measurements if
- * necessary.
- */
- iwlagn_bt_coex_rssi_monitor(priv);
-out:
- mutex_unlock(&priv->mutex);
-}
-
-/*
- * If BT sco traffic, and RSSI monitor is enabled, move measurements to the
- * correct interface or disable it if this is the last interface to be
- * removed.
- */
-void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv)
-{
- if (priv->bt_is_sco &&
- priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS)
- iwlagn_bt_adjust_rssi_monitor(priv, true);
- else
- iwlagn_bt_adjust_rssi_monitor(priv, false);
-}
-
-static void iwlagn_print_uartmsg(struct iwl_priv *priv,
- struct iwl_bt_uart_msg *uart_msg)
-{
- IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, "
- "Update Req = 0x%X\n",
- (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >>
- BT_UART_MSG_FRAME1MSGTYPE_POS,
- (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >>
- BT_UART_MSG_FRAME1SSN_POS,
- (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >>
- BT_UART_MSG_FRAME1UPDATEREQ_POS);
-
- IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, "
- "Chl_SeqN = 0x%X, In band = 0x%X\n",
- (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >>
- BT_UART_MSG_FRAME2OPENCONNECTIONS_POS,
- (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >>
- BT_UART_MSG_FRAME2TRAFFICLOAD_POS,
- (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >>
- BT_UART_MSG_FRAME2CHLSEQN_POS,
- (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >>
- BT_UART_MSG_FRAME2INBAND_POS);
-
- IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, "
- "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n",
- (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
- BT_UART_MSG_FRAME3SCOESCO_POS,
- (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >>
- BT_UART_MSG_FRAME3SNIFF_POS,
- (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >>
- BT_UART_MSG_FRAME3A2DP_POS,
- (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >>
- BT_UART_MSG_FRAME3ACL_POS,
- (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >>
- BT_UART_MSG_FRAME3MASTER_POS,
- (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >>
- BT_UART_MSG_FRAME3OBEX_POS);
-
- IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n",
- (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >>
- BT_UART_MSG_FRAME4IDLEDURATION_POS);
-
- IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, "
- "eSCO Retransmissions = 0x%X\n",
- (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >>
- BT_UART_MSG_FRAME5TXACTIVITY_POS,
- (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >>
- BT_UART_MSG_FRAME5RXACTIVITY_POS,
- (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >>
- BT_UART_MSG_FRAME5ESCORETRANSMIT_POS);
-
- IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n",
- (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >>
- BT_UART_MSG_FRAME6SNIFFINTERVAL_POS,
- (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >>
- BT_UART_MSG_FRAME6DISCOVERABLE_POS);
-
- IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = "
- "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n",
- (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >>
- BT_UART_MSG_FRAME7SNIFFACTIVITY_POS,
- (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >>
- BT_UART_MSG_FRAME7PAGE_POS,
- (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >>
- BT_UART_MSG_FRAME7INQUIRY_POS,
- (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >>
- BT_UART_MSG_FRAME7CONNECTABLE_POS);
-}
-
-static bool iwlagn_set_kill_msk(struct iwl_priv *priv,
- struct iwl_bt_uart_msg *uart_msg)
-{
- bool need_update = false;
- u8 kill_msk = IWL_BT_KILL_REDUCE;
- static const __le32 bt_kill_ack_msg[3] = {
- IWLAGN_BT_KILL_ACK_MASK_DEFAULT,
- IWLAGN_BT_KILL_ACK_CTS_MASK_SCO,
- IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE};
- static const __le32 bt_kill_cts_msg[3] = {
- IWLAGN_BT_KILL_CTS_MASK_DEFAULT,
- IWLAGN_BT_KILL_ACK_CTS_MASK_SCO,
- IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE};
-
- if (!priv->reduced_txpower)
- kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3)
- ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT;
- if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] ||
- priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) {
- priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK;
- priv->kill_ack_mask = bt_kill_ack_msg[kill_msk];
- priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK;
- priv->kill_cts_mask = bt_kill_cts_msg[kill_msk];
- need_update = true;
- }
- return need_update;
-}
-
-/*
- * Upon RSSI changes, sends a bt config command with following changes
- * 1. enable/disable "reduced control frames tx power
- * 2. update the "kill)ack_mask" and "kill_cts_mask"
- *
- * If "reduced tx power" is enabled, uCode shall
- * 1. ACK/Back/CTS rate shall reduced to 6Mbps
- * 2. not use duplciate 20/40MHz mode
- */
-static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv,
- struct iwl_bt_uart_msg *uart_msg)
-{
- bool need_update = false;
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- int ave_rssi;
-
- if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) {
- IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n");
- return false;
- }
-
- ave_rssi = ieee80211_ave_rssi(ctx->vif);
- if (!ave_rssi) {
- /* no rssi data, no changes to reduce tx power */
- IWL_DEBUG_COEX(priv, "no rssi data available\n");
- return need_update;
- }
- if (!priv->reduced_txpower &&
- !iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
- (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) &&
- (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK |
- BT_UART_MSG_FRAME3OBEX_MSK)) &&
- !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK |
- BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) {
- /* enabling reduced tx power */
- priv->reduced_txpower = true;
- priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR;
- need_update = true;
- } else if (priv->reduced_txpower &&
- (iwl_is_associated(priv, IWL_RXON_CTX_PAN) ||
- (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) ||
- (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK |
- BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) ||
- !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK |
- BT_UART_MSG_FRAME3OBEX_MSK)))) {
- /* disable reduced tx power */
- priv->reduced_txpower = false;
- priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR;
- need_update = true;
- }
-
- return need_update;
-}
-
-static void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data;
- struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg;
-
- if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) {
- /* bt coex disabled */
- return;
- }
-
- IWL_DEBUG_COEX(priv, "BT Coex notification:\n");
- IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status);
- IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load);
- IWL_DEBUG_COEX(priv, " CI compliance: %d\n",
- coex->bt_ci_compliance);
- iwlagn_print_uartmsg(priv, uart_msg);
-
- priv->last_bt_traffic_load = priv->bt_traffic_load;
- priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg);
-
- if (priv->iw_mode != NL80211_IFTYPE_ADHOC) {
- if (priv->bt_status != coex->bt_status ||
- priv->last_bt_traffic_load != coex->bt_traffic_load) {
- if (coex->bt_status) {
- /* BT on */
- if (!priv->bt_ch_announce)
- priv->bt_traffic_load =
- IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
- else
- priv->bt_traffic_load =
- coex->bt_traffic_load;
- } else {
- /* BT off */
- priv->bt_traffic_load =
- IWL_BT_COEX_TRAFFIC_LOAD_NONE;
- }
- priv->bt_status = coex->bt_status;
- queue_work(priv->workqueue,
- &priv->bt_traffic_change_work);
- }
- }
-
- /* schedule to send runtime bt_config */
- /* check reduce power before change ack/cts kill mask */
- if (iwlagn_fill_txpower_mode(priv, uart_msg) ||
- iwlagn_set_kill_msk(priv, uart_msg))
- queue_work(priv->workqueue, &priv->bt_runtime_config);
-
-
- /* FIXME: based on notification, adjust the prio_boost */
-
- priv->bt_ci_compliance = coex->bt_ci_compliance;
-}
-
-void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv)
-{
- priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] =
- iwlagn_bt_coex_profile_notif;
-}
-
-void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv)
-{
- INIT_WORK(&priv->bt_traffic_change_work,
- iwlagn_bt_traffic_change_work);
-}
-
-void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv)
-{
- cancel_work_sync(&priv->bt_traffic_change_work);
-}
-
-static bool is_single_rx_stream(struct iwl_priv *priv)
-{
- return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
- priv->current_ht_config.single_chain_sufficient;
-}
-
-#define IWL_NUM_RX_CHAINS_MULTIPLE 3
-#define IWL_NUM_RX_CHAINS_SINGLE 2
-#define IWL_NUM_IDLE_CHAINS_DUAL 2
-#define IWL_NUM_IDLE_CHAINS_SINGLE 1
-
-/*
- * Determine how many receiver/antenna chains to use.
- *
- * More provides better reception via diversity. Fewer saves power
- * at the expense of throughput, but only when not in powersave to
- * start with.
- *
- * MIMO (dual stream) requires at least 2, but works better with 3.
- * This does not determine *which* chains to use, just how many.
- */
-static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
-{
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist &&
- (priv->bt_full_concurrent ||
- priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
- /*
- * only use chain 'A' in bt high traffic load or
- * full concurrency mode
- */
- return IWL_NUM_RX_CHAINS_SINGLE;
- }
- /* # of Rx chains to use when expecting MIMO. */
- if (is_single_rx_stream(priv))
- return IWL_NUM_RX_CHAINS_SINGLE;
- else
- return IWL_NUM_RX_CHAINS_MULTIPLE;
-}
-
-/*
- * When we are in power saving mode, unless device support spatial
- * multiplexing power save, use the active count for rx chain count.
- */
-static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
-{
- /* # Rx chains when idling, depending on SMPS mode */
- switch (priv->current_ht_config.smps) {
- case IEEE80211_SMPS_STATIC:
- case IEEE80211_SMPS_DYNAMIC:
- return IWL_NUM_IDLE_CHAINS_SINGLE;
- case IEEE80211_SMPS_AUTOMATIC:
- case IEEE80211_SMPS_OFF:
- return active_cnt;
- default:
- WARN(1, "invalid SMPS mode %d",
- priv->current_ht_config.smps);
- return active_cnt;
- }
-}
-
-/* up to 4 chains */
-static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
-{
- u8 res;
- res = (chain_bitmap & BIT(0)) >> 0;
- res += (chain_bitmap & BIT(1)) >> 1;
- res += (chain_bitmap & BIT(2)) >> 2;
- res += (chain_bitmap & BIT(3)) >> 3;
- return res;
-}
-
-/**
- * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
- *
- * Selects how many and which Rx receivers/antennas/chains to use.
- * This should not be used for scan command ... it puts data in wrong place.
- */
-void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- bool is_single = is_single_rx_stream(priv);
- bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
- u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
- u32 active_chains;
- u16 rx_chain;
-
- /* Tell uCode which antennas are actually connected.
- * Before first association, we assume all antennas are connected.
- * Just after first association, iwl_chain_noise_calibration()
- * checks which antennas actually *are* connected. */
- if (priv->chain_noise_data.active_chains)
- active_chains = priv->chain_noise_data.active_chains;
- else
- active_chains = priv->nvm_data->valid_rx_ant;
-
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist &&
- (priv->bt_full_concurrent ||
- priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
- /*
- * only use chain 'A' in bt high traffic load or
- * full concurrency mode
- */
- active_chains = first_antenna(active_chains);
- }
-
- rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
-
- /* How many receivers should we use? */
- active_rx_cnt = iwl_get_active_rx_chain_count(priv);
- idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt);
-
-
- /* correct rx chain count according hw settings
- * and chain noise calibration
- */
- valid_rx_cnt = iwl_count_chain_bitmap(active_chains);
- if (valid_rx_cnt < active_rx_cnt)
- active_rx_cnt = valid_rx_cnt;
-
- if (valid_rx_cnt < idle_rx_cnt)
- idle_rx_cnt = valid_rx_cnt;
-
- rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
- rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;
-
- ctx->staging.rx_chain = cpu_to_le16(rx_chain);
-
- if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam)
- ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
- else
- ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
-
- IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n",
- ctx->staging.rx_chain,
- active_rx_cnt, idle_rx_cnt);
-
- WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 ||
- active_rx_cnt < idle_rx_cnt);
-}
-
-u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid)
-{
- int i;
- u8 ind = ant;
-
- if (priv->band == IEEE80211_BAND_2GHZ &&
- priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
- return 0;
-
- for (i = 0; i < RATE_ANT_NUM - 1; i++) {
- ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0;
- if (valid & BIT(ind))
- return ind;
- }
- return ant;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static void iwlagn_convert_p1k(u16 *p1k, __le16 *out)
-{
- int i;
-
- for (i = 0; i < IWLAGN_P1K_SIZE; i++)
- out[i] = cpu_to_le16(p1k[i]);
-}
-
-struct wowlan_key_data {
- struct iwl_rxon_context *ctx;
- struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc;
- struct iwlagn_wowlan_tkip_params_cmd *tkip;
- const u8 *bssid;
- bool error, use_rsc_tsc, use_tkip;
-};
-
-
-static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key,
- void *_data)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct wowlan_key_data *data = _data;
- struct iwl_rxon_context *ctx = data->ctx;
- struct aes_sc *aes_sc, *aes_tx_sc = NULL;
- struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
- struct iwlagn_p1k_cache *rx_p1ks;
- u8 *rx_mic_key;
- struct ieee80211_key_seq seq;
- u32 cur_rx_iv32 = 0;
- u16 p1k[IWLAGN_P1K_SIZE];
- int ret, i;
-
- mutex_lock(&priv->mutex);
-
- if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
- !sta && !ctx->key_mapping_keys)
- ret = iwl_set_default_wep_key(priv, ctx, key);
- else
- ret = iwl_set_dynamic_key(priv, ctx, key, sta);
-
- if (ret) {
- IWL_ERR(priv, "Error setting key during suspend!\n");
- data->error = true;
- }
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- if (sta) {
- tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
- tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
-
- rx_p1ks = data->tkip->rx_uni;
-
- ieee80211_get_key_tx_seq(key, &seq);
- tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
- tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
-
- ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
- iwlagn_convert_p1k(p1k, data->tkip->tx.p1k);
-
- memcpy(data->tkip->mic_keys.tx,
- &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
- IWLAGN_MIC_KEY_SIZE);
-
- rx_mic_key = data->tkip->mic_keys.rx_unicast;
- } else {
- tkip_sc =
- data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
- rx_p1ks = data->tkip->rx_multi;
- rx_mic_key = data->tkip->mic_keys.rx_mcast;
- }
-
- /*
- * For non-QoS this relies on the fact that both the uCode and
- * mac80211 use TID 0 (as they need to to avoid replay attacks)
- * for checking the IV in the frames.
- */
- for (i = 0; i < IWLAGN_NUM_RSC; i++) {
- ieee80211_get_key_rx_seq(key, i, &seq);
- tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
- tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
- /* wrapping isn't allowed, AP must rekey */
- if (seq.tkip.iv32 > cur_rx_iv32)
- cur_rx_iv32 = seq.tkip.iv32;
- }
-
- ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k);
- iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k);
- ieee80211_get_tkip_rx_p1k(key, data->bssid,
- cur_rx_iv32 + 1, p1k);
- iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k);
-
- memcpy(rx_mic_key,
- &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
- IWLAGN_MIC_KEY_SIZE);
-
- data->use_tkip = true;
- data->use_rsc_tsc = true;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- if (sta) {
- u8 *pn = seq.ccmp.pn;
-
- aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
- aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
-
- ieee80211_get_key_tx_seq(key, &seq);
- aes_tx_sc->pn = cpu_to_le64(
- (u64)pn[5] |
- ((u64)pn[4] << 8) |
- ((u64)pn[3] << 16) |
- ((u64)pn[2] << 24) |
- ((u64)pn[1] << 32) |
- ((u64)pn[0] << 40));
- } else
- aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
-
- /*
- * For non-QoS this relies on the fact that both the uCode and
- * mac80211 use TID 0 for checking the IV in the frames.
- */
- for (i = 0; i < IWLAGN_NUM_RSC; i++) {
- u8 *pn = seq.ccmp.pn;
-
- ieee80211_get_key_rx_seq(key, i, &seq);
- aes_sc[i].pn = cpu_to_le64(
- (u64)pn[5] |
- ((u64)pn[4] << 8) |
- ((u64)pn[3] << 16) |
- ((u64)pn[2] << 24) |
- ((u64)pn[1] << 32) |
- ((u64)pn[0] << 40));
- }
- data->use_rsc_tsc = true;
- break;
- }
-
- mutex_unlock(&priv->mutex);
-}
-
-int iwlagn_send_patterns(struct iwl_priv *priv,
- struct cfg80211_wowlan *wowlan)
-{
- struct iwlagn_wowlan_patterns_cmd *pattern_cmd;
- struct iwl_host_cmd cmd = {
- .id = REPLY_WOWLAN_PATTERNS,
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- };
- int i, err;
-
- if (!wowlan->n_patterns)
- return 0;
-
- cmd.len[0] = sizeof(*pattern_cmd) +
- wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern);
-
- pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
- if (!pattern_cmd)
- return -ENOMEM;
-
- pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
-
- for (i = 0; i < wowlan->n_patterns; i++) {
- int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
-
- memcpy(&pattern_cmd->patterns[i].mask,
- wowlan->patterns[i].mask, mask_len);
- memcpy(&pattern_cmd->patterns[i].pattern,
- wowlan->patterns[i].pattern,
- wowlan->patterns[i].pattern_len);
- pattern_cmd->patterns[i].mask_size = mask_len;
- pattern_cmd->patterns[i].pattern_size =
- wowlan->patterns[i].pattern_len;
- }
-
- cmd.data[0] = pattern_cmd;
- err = iwl_dvm_send_cmd(priv, &cmd);
- kfree(pattern_cmd);
- return err;
-}
-
-int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
-{
- struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd;
- struct iwl_rxon_cmd rxon;
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd;
- struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {};
- struct iwlagn_d3_config_cmd d3_cfg_cmd = {
- /*
- * Program the minimum sleep time to 10 seconds, as many
- * platforms have issues processing a wakeup signal while
- * still being in the process of suspending.
- */
- .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
- };
- struct wowlan_key_data key_data = {
- .ctx = ctx,
- .bssid = ctx->active.bssid_addr,
- .use_rsc_tsc = false,
- .tkip = &tkip_cmd,
- .use_tkip = false,
- };
- int ret, i;
- u16 seq;
-
- key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
- if (!key_data.rsc_tsc)
- return -ENOMEM;
-
- memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd));
-
- /*
- * We know the last used seqno, and the uCode expects to know that
- * one, it will increment before TX.
- */
- seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ;
- wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq);
-
- /*
- * For QoS counters, we store the one to use next, so subtract 0x10
- * since the uCode will add 0x10 before using the value.
- */
- for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
- seq = priv->tid_data[IWL_AP_ID][i].seq_number;
- seq -= 0x10;
- wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq);
- }
-
- if (wowlan->disconnect)
- wakeup_filter_cmd.enabled |=
- cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
- IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE);
- if (wowlan->magic_pkt)
- wakeup_filter_cmd.enabled |=
- cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET);
- if (wowlan->gtk_rekey_failure)
- wakeup_filter_cmd.enabled |=
- cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
- if (wowlan->eap_identity_req)
- wakeup_filter_cmd.enabled |=
- cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ);
- if (wowlan->four_way_handshake)
- wakeup_filter_cmd.enabled |=
- cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
- if (wowlan->n_patterns)
- wakeup_filter_cmd.enabled |=
- cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH);
-
- if (wowlan->rfkill_release)
- d3_cfg_cmd.wakeup_flags |=
- cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL);
-
- iwl_scan_cancel_timeout(priv, 200);
-
- memcpy(&rxon, &ctx->active, sizeof(rxon));
-
- priv->ucode_loaded = false;
- iwl_trans_stop_device(priv->trans);
-
- priv->wowlan = true;
-
- ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
- if (ret)
- goto out;
-
- /* now configure WoWLAN ucode */
- ret = iwl_alive_start(priv);
- if (ret)
- goto out;
-
- memcpy(&ctx->staging, &rxon, sizeof(rxon));
- ret = iwlagn_commit_rxon(priv, ctx);
- if (ret)
- goto out;
-
- ret = iwl_power_update_mode(priv, true);
- if (ret)
- goto out;
-
- if (!iwlwifi_mod_params.sw_crypto) {
- /* mark all keys clear */
- priv->ucode_key_table = 0;
- ctx->key_mapping_keys = 0;
-
- /*
- * This needs to be unlocked due to lock ordering
- * constraints. Since we're in the suspend path
- * that isn't really a problem though.
- */
- mutex_unlock(&priv->mutex);
- ieee80211_iter_keys(priv->hw, ctx->vif,
- iwlagn_wowlan_program_keys,
- &key_data);
- mutex_lock(&priv->mutex);
- if (key_data.error) {
- ret = -EIO;
- goto out;
- }
-
- if (key_data.use_rsc_tsc) {
- struct iwl_host_cmd rsc_tsc_cmd = {
- .id = REPLY_WOWLAN_TSC_RSC_PARAMS,
- .data[0] = key_data.rsc_tsc,
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- .len[0] = sizeof(*key_data.rsc_tsc),
- };
-
- ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd);
- if (ret)
- goto out;
- }
-
- if (key_data.use_tkip) {
- ret = iwl_dvm_send_cmd_pdu(priv,
- REPLY_WOWLAN_TKIP_PARAMS,
- 0, sizeof(tkip_cmd),
- &tkip_cmd);
- if (ret)
- goto out;
- }
-
- if (priv->have_rekey_data) {
- memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
- memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN);
- kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
- memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN);
- kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
- kek_kck_cmd.replay_ctr = priv->replay_ctr;
-
- ret = iwl_dvm_send_cmd_pdu(priv,
- REPLY_WOWLAN_KEK_KCK_MATERIAL,
- 0, sizeof(kek_kck_cmd),
- &kek_kck_cmd);
- if (ret)
- goto out;
- }
- }
-
- ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0,
- sizeof(d3_cfg_cmd), &d3_cfg_cmd);
- if (ret)
- goto out;
-
- ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER,
- 0, sizeof(wakeup_filter_cmd),
- &wakeup_filter_cmd);
- if (ret)
- goto out;
-
- ret = iwlagn_send_patterns(priv, wowlan);
- out:
- kfree(key_data.rsc_tsc);
- return ret;
-}
-#endif
-
-int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
-{
- if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) {
- IWL_WARN(priv, "Not sending command - %s KILL\n",
- iwl_is_rfkill(priv) ? "RF" : "CT");
- return -EIO;
- }
-
- if (test_bit(STATUS_FW_ERROR, &priv->status)) {
- IWL_ERR(priv, "Command %s failed: FW Error\n",
- iwl_dvm_get_cmd_string(cmd->id));
- return -EIO;
- }
-
- /*
- * This can happen upon FW ASSERT: we clear the STATUS_FW_ERROR flag
- * in iwl_down but cancel the workers only later.
- */
- if (!priv->ucode_loaded) {
- IWL_ERR(priv, "Fw not loaded - dropping CMD: %x\n", cmd->id);
- return -EIO;
- }
-
- /*
- * Synchronous commands from this op-mode must hold
- * the mutex, this ensures we don't try to send two
- * (or more) synchronous commands at a time.
- */
- if (!(cmd->flags & CMD_ASYNC))
- lockdep_assert_held(&priv->mutex);
-
- return iwl_trans_send_cmd(priv->trans, cmd);
-}
-
-int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id,
- u32 flags, u16 len, const void *data)
-{
- struct iwl_host_cmd cmd = {
- .id = id,
- .len = { len, },
- .data = { data, },
- .flags = flags,
- };
-
- return iwl_dvm_send_cmd(priv, &cmd);
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
deleted file mode 100644
index b3ad34e8bf5a..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ /dev/null
@@ -1,1655 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-
-#include <net/ieee80211_radiotap.h>
-#include <net/mac80211.h>
-
-#include <asm/div64.h>
-
-#include "iwl-io.h"
-#include "iwl-trans.h"
-#include "iwl-op-mode.h"
-#include "iwl-modparams.h"
-
-#include "dev.h"
-#include "calib.h"
-#include "agn.h"
-
-/*****************************************************************************
- *
- * mac80211 entry point functions
- *
- *****************************************************************************/
-
-static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = {
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_STATION),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_AP),
- },
-};
-
-static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = {
- {
- .max = 2,
- .types = BIT(NL80211_IFTYPE_STATION),
- },
-};
-
-static const struct ieee80211_iface_combination
-iwlagn_iface_combinations_dualmode[] = {
- { .num_different_channels = 1,
- .max_interfaces = 2,
- .beacon_int_infra_match = true,
- .limits = iwlagn_sta_ap_limits,
- .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits),
- },
- { .num_different_channels = 1,
- .max_interfaces = 2,
- .limits = iwlagn_2sta_limits,
- .n_limits = ARRAY_SIZE(iwlagn_2sta_limits),
- },
-};
-
-/*
- * Not a mac80211 entry point function, but it fits in with all the
- * other mac80211 functions grouped here.
- */
-int iwlagn_mac_setup_register(struct iwl_priv *priv,
- const struct iwl_ucode_capabilities *capa)
-{
- int ret;
- struct ieee80211_hw *hw = priv->hw;
- struct iwl_rxon_context *ctx;
-
- hw->rate_control_algorithm = "iwl-agn-rs";
-
- /* Tell mac80211 our characteristics */
- ieee80211_hw_set(hw, SIGNAL_DBM);
- ieee80211_hw_set(hw, AMPDU_AGGREGATION);
- ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
- ieee80211_hw_set(hw, SPECTRUM_MGMT);
- ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
- ieee80211_hw_set(hw, QUEUE_CONTROL);
- ieee80211_hw_set(hw, SUPPORTS_PS);
- ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
- ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
- ieee80211_hw_set(hw, WANT_MONITOR_VIF);
-
- hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE;
- hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT;
-
- /*
- * Including the following line will crash some AP's. This
- * workaround removes the stimulus which causes the crash until
- * the AP software can be fixed.
- hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
- */
-
- if (priv->nvm_data->sku_cap_11n_enable)
- hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS |
- NL80211_FEATURE_STATIC_SMPS;
-
- /*
- * Enable 11w if advertised by firmware and software crypto
- * is not enabled (as the firmware will interpret some mgmt
- * packets, so enabling it with software crypto isn't safe)
- */
- if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP &&
- !iwlwifi_mod_params.sw_crypto)
- ieee80211_hw_set(hw, MFP_CAPABLE);
-
- hw->sta_data_size = sizeof(struct iwl_station_priv);
- hw->vif_data_size = sizeof(struct iwl_vif_priv);
-
- for_each_context(priv, ctx) {
- hw->wiphy->interface_modes |= ctx->interface_modes;
- hw->wiphy->interface_modes |= ctx->exclusive_interface_modes;
- }
-
- BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
-
- if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
- hw->wiphy->iface_combinations =
- iwlagn_iface_combinations_dualmode;
- hw->wiphy->n_iface_combinations =
- ARRAY_SIZE(iwlagn_iface_combinations_dualmode);
- }
-
- hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
- hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
- REGULATORY_DISABLE_BEACON_HINTS;
-
-#ifdef CONFIG_PM_SLEEP
- if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
- priv->trans->ops->d3_suspend &&
- priv->trans->ops->d3_resume &&
- device_can_wakeup(priv->trans->dev)) {
- 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)
- priv->wowlan_support.flags |=
- WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
- WIPHY_WOWLAN_GTK_REKEY_FAILURE;
-
- priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
- priv->wowlan_support.pattern_min_len =
- IWLAGN_WOWLAN_MIN_PATTERN_LEN;
- priv->wowlan_support.pattern_max_len =
- IWLAGN_WOWLAN_MAX_PATTERN_LEN;
- hw->wiphy->wowlan = &priv->wowlan_support;
- }
-#endif
-
- if (iwlwifi_mod_params.power_save)
- hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
- else
- hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
-
- hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
- /* we create the 802.11 header and a max-length SSID element */
- hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34;
-
- /*
- * We don't use all queues: 4 and 9 are unused and any
- * aggregation queue gets mapped down to the AC queue.
- */
- hw->queues = IWLAGN_FIRST_AMPDU_QUEUE;
-
- hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
-
- if (priv->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
- priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &priv->nvm_data->bands[IEEE80211_BAND_2GHZ];
- if (priv->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels)
- priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &priv->nvm_data->bands[IEEE80211_BAND_5GHZ];
-
- hw->wiphy->hw_version = priv->trans->hw_id;
-
- iwl_leds_init(priv);
-
- ret = ieee80211_register_hw(priv->hw);
- if (ret) {
- IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
- iwl_leds_exit(priv);
- return ret;
- }
- priv->mac80211_registered = 1;
-
- return 0;
-}
-
-void iwlagn_mac_unregister(struct iwl_priv *priv)
-{
- if (!priv->mac80211_registered)
- return;
- iwl_leds_exit(priv);
- ieee80211_unregister_hw(priv->hw);
- priv->mac80211_registered = 0;
-}
-
-static int __iwl_up(struct iwl_priv *priv)
-{
- struct iwl_rxon_context *ctx;
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
- IWL_WARN(priv, "Exit pending; will not bring the NIC up\n");
- return -EIO;
- }
-
- for_each_context(priv, ctx) {
- ret = iwlagn_alloc_bcast_station(priv, ctx);
- if (ret) {
- iwl_dealloc_bcast_stations(priv);
- return ret;
- }
- }
-
- ret = iwl_trans_start_hw(priv->trans);
- if (ret) {
- IWL_ERR(priv, "Failed to start HW: %d\n", ret);
- goto error;
- }
-
- ret = iwl_run_init_ucode(priv);
- if (ret) {
- IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret);
- goto error;
- }
-
- ret = iwl_trans_start_hw(priv->trans);
- if (ret) {
- IWL_ERR(priv, "Failed to start HW: %d\n", ret);
- goto error;
- }
-
- ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR);
- if (ret) {
- IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret);
- goto error;
- }
-
- ret = iwl_alive_start(priv);
- if (ret)
- goto error;
- return 0;
-
- error:
- set_bit(STATUS_EXIT_PENDING, &priv->status);
- iwl_down(priv);
- clear_bit(STATUS_EXIT_PENDING, &priv->status);
-
- IWL_ERR(priv, "Unable to initialize device.\n");
- return ret;
-}
-
-static int iwlagn_mac_start(struct ieee80211_hw *hw)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- int ret;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- /* we should be verifying the device is ready to be opened */
- mutex_lock(&priv->mutex);
- ret = __iwl_up(priv);
- mutex_unlock(&priv->mutex);
- if (ret)
- return ret;
-
- IWL_DEBUG_INFO(priv, "Start UP work done.\n");
-
- /* Now we should be done, and the READY bit should be set. */
- if (WARN_ON(!test_bit(STATUS_READY, &priv->status)))
- ret = -EIO;
-
- iwlagn_led_enable(priv);
-
- priv->is_open = 1;
- IWL_DEBUG_MAC80211(priv, "leave\n");
- return 0;
-}
-
-static void iwlagn_mac_stop(struct ieee80211_hw *hw)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- if (!priv->is_open)
- return;
-
- priv->is_open = 0;
-
- mutex_lock(&priv->mutex);
- iwl_down(priv);
- mutex_unlock(&priv->mutex);
-
- iwl_cancel_deferred_work(priv);
-
- flush_workqueue(priv->workqueue);
-
- IWL_DEBUG_MAC80211(priv, "leave\n");
-}
-
-static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_gtk_rekey_data *data)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- if (iwlwifi_mod_params.sw_crypto)
- return;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
-
- if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif)
- goto out;
-
- memcpy(priv->kek, data->kek, NL80211_KEK_LEN);
- memcpy(priv->kck, data->kck, NL80211_KCK_LEN);
- priv->replay_ctr =
- cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
- priv->have_rekey_data = true;
-
- out:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
- struct cfg80211_wowlan *wowlan)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- int ret;
-
- if (WARN_ON(!wowlan))
- return -EINVAL;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
-
- /* Don't attempt WoWLAN when not associated, tear down instead. */
- if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION ||
- !iwl_is_associated_ctx(ctx)) {
- ret = 1;
- goto out;
- }
-
- ret = iwlagn_suspend(priv, wowlan);
- if (ret)
- goto error;
-
- /* let the ucode operate on its own */
- iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
- CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
-
- iwl_trans_d3_suspend(priv->trans, false);
-
- goto out;
-
- error:
- priv->wowlan = false;
- iwlagn_prepare_restart(priv);
- ieee80211_restart_hw(priv->hw);
- out:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- return ret;
-}
-
-struct iwl_resume_data {
- struct iwl_priv *priv;
- struct iwlagn_wowlan_status *cmd;
- bool valid;
-};
-
-static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_resume_data *resume_data = data;
- struct iwl_priv *priv = resume_data->priv;
-
- if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) {
- IWL_ERR(priv, "rx wrong size data\n");
- return true;
- }
- memcpy(resume_data->cmd, pkt->data, sizeof(*resume_data->cmd));
- resume_data->valid = true;
-
- return true;
-}
-
-static int iwlagn_mac_resume(struct ieee80211_hw *hw)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- struct ieee80211_vif *vif;
- u32 base;
- int ret;
- enum iwl_d3_status d3_status;
- struct error_table_start {
- /* cf. struct iwl_error_event_table */
- u32 valid;
- u32 error_id;
- } err_info;
- struct iwl_notification_wait status_wait;
- static const u16 status_cmd[] = {
- REPLY_WOWLAN_GET_STATUS,
- };
- struct iwlagn_wowlan_status status_data = {};
- struct iwl_resume_data resume_data = {
- .priv = priv,
- .cmd = &status_data,
- .valid = false,
- };
- struct cfg80211_wowlan_wakeup wakeup = {
- .pattern_idx = -1,
- };
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- const struct fw_img *img;
-#endif
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
-
- /* we'll clear ctx->vif during iwlagn_prepare_restart() */
- vif = ctx->vif;
-
- ret = iwl_trans_d3_resume(priv->trans, &d3_status, false);
- if (ret)
- goto out_unlock;
-
- if (d3_status != IWL_D3_STATUS_ALIVE) {
- IWL_INFO(priv, "Device was reset during suspend\n");
- goto out_unlock;
- }
-
- /* uCode is no longer operating by itself */
- iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
-
- base = priv->device_pointers.error_event_table;
- if (!iwlagn_hw_valid_rtc_data_addr(base)) {
- IWL_WARN(priv, "Invalid error table during resume!\n");
- goto out_unlock;
- }
-
- iwl_trans_read_mem_bytes(priv->trans, base,
- &err_info, sizeof(err_info));
-
- if (err_info.valid) {
- IWL_INFO(priv, "error table is valid (%d, 0x%x)\n",
- err_info.valid, err_info.error_id);
- if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
- wakeup.rfkill_release = true;
- ieee80211_report_wowlan_wakeup(vif, &wakeup,
- GFP_KERNEL);
- }
- goto out_unlock;
- }
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- img = &priv->fw->img[IWL_UCODE_WOWLAN];
- if (!priv->wowlan_sram)
- priv->wowlan_sram =
- kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len,
- GFP_KERNEL);
-
- if (priv->wowlan_sram)
- iwl_trans_read_mem(priv->trans, 0x800000,
- priv->wowlan_sram,
- img->sec[IWL_UCODE_SECTION_DATA].len / 4);
-#endif
-
- /*
- * This is very strange. The GET_STATUS command is sent but the device
- * doesn't reply properly, it seems it doesn't close the RBD so one is
- * always left open ... As a result, we need to send another command
- * and have to reset the driver afterwards. As we need to switch to
- * runtime firmware again that'll happen.
- */
-
- iwl_init_notification_wait(&priv->notif_wait, &status_wait, status_cmd,
- ARRAY_SIZE(status_cmd), iwl_resume_status_fn,
- &resume_data);
-
- iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_GET_STATUS, CMD_ASYNC, 0, NULL);
- iwl_dvm_send_cmd_pdu(priv, REPLY_ECHO, CMD_ASYNC, 0, NULL);
- /* an RBD is left open in the firmware now! */
-
- ret = iwl_wait_notification(&priv->notif_wait, &status_wait, HZ/5);
- if (ret)
- goto out_unlock;
-
- if (resume_data.valid && priv->contexts[IWL_RXON_CTX_BSS].vif) {
- u32 reasons = le32_to_cpu(status_data.wakeup_reason);
- struct cfg80211_wowlan_wakeup *wakeup_report;
-
- IWL_INFO(priv, "WoWLAN wakeup reason(s): 0x%.8x\n", reasons);
-
- if (reasons) {
- if (reasons & IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET)
- wakeup.magic_pkt = true;
- if (reasons & IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH)
- wakeup.pattern_idx = status_data.pattern_number;
- if (reasons & (IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
- IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE))
- wakeup.disconnect = true;
- if (reasons & IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL)
- wakeup.gtk_rekey_failure = true;
- if (reasons & IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ)
- wakeup.eap_identity_req = true;
- if (reasons & IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE)
- wakeup.four_way_handshake = true;
- wakeup_report = &wakeup;
- } else {
- wakeup_report = NULL;
- }
-
- ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
- }
-
- priv->wowlan = false;
-
- iwlagn_prepare_restart(priv);
-
- memset((void *)&ctx->active, 0, sizeof(ctx->active));
- iwl_connection_init_rx_config(priv, ctx);
- iwlagn_set_rxon_chain(priv, ctx);
-
- out_unlock:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- ieee80211_resume_disconnect(vif);
-
- return 1;
-}
-
-static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- device_set_wakeup_enable(priv->trans->dev, enabled);
-}
-#endif
-
-static void iwlagn_mac_tx(struct ieee80211_hw *hw,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- if (iwlagn_tx_skb(priv, control->sta, skb))
- ieee80211_free_txskb(hw, skb);
-}
-
-static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta,
- u32 iv32, u16 *phase1key)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key);
-}
-
-static int iwlagn_mac_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 iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
- struct iwl_rxon_context *ctx = vif_priv->ctx;
- int ret;
- bool is_default_wep_key = false;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- if (iwlwifi_mod_params.sw_crypto) {
- IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n");
- return -EOPNOTSUPP;
- }
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
- /* fall through */
- case WLAN_CIPHER_SUITE_CCMP:
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- break;
- default:
- break;
- }
-
- /*
- * We could program these keys into the hardware as well, but we
- * don't expect much multicast traffic in IBSS and having keys
- * for more stations is probably more useful.
- *
- * Mark key TX-only and return 0.
- */
- if (vif->type == NL80211_IFTYPE_ADHOC &&
- !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
- key->hw_key_idx = WEP_INVALID_OFFSET;
- return 0;
- }
-
- /* If they key was TX-only, accept deletion */
- if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET)
- return 0;
-
- mutex_lock(&priv->mutex);
- iwl_scan_cancel_timeout(priv, 100);
-
- BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT);
-
- /*
- * If we are getting WEP group key and we didn't receive any key mapping
- * so far, we are in legacy wep mode (group key only), otherwise we are
- * in 1X mode.
- * In legacy wep mode, we use another host command to the uCode.
- */
- if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) {
- if (cmd == SET_KEY)
- is_default_wep_key = !ctx->key_mapping_keys;
- else
- is_default_wep_key =
- key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT;
- }
-
-
- switch (cmd) {
- case SET_KEY:
- if (is_default_wep_key) {
- ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key);
- break;
- }
- ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta);
- if (ret) {
- /*
- * can't add key for RX, but we don't need it
- * in the device for TX so still return 0
- */
- ret = 0;
- key->hw_key_idx = WEP_INVALID_OFFSET;
- }
-
- IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n");
- break;
- case DISABLE_KEY:
- if (is_default_wep_key)
- ret = iwl_remove_default_wep_key(priv, ctx, key);
- else
- ret = iwl_remove_dynamic_key(priv, ctx, key, sta);
-
- IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n");
- break;
- default:
- ret = -EINVAL;
- }
-
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- return ret;
-}
-
-static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg)
-{
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
- return false;
- return true;
-}
-
-static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
-{
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
- return false;
- if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
- return true;
-
- /* disabled by default */
- return false;
-}
-
-static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn,
- u8 buf_size, bool amsdu)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- int ret = -EINVAL;
- struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
-
- IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
- sta->addr, tid);
-
- if (!(priv->nvm_data->sku_cap_11n_enable))
- return -EACCES;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
-
- switch (action) {
- case IEEE80211_AMPDU_RX_START:
- if (!iwl_enable_rx_ampdu(priv->cfg))
- break;
- IWL_DEBUG_HT(priv, "start Rx\n");
- ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn);
- break;
- case IEEE80211_AMPDU_RX_STOP:
- IWL_DEBUG_HT(priv, "stop Rx\n");
- ret = iwl_sta_rx_agg_stop(priv, sta, tid);
- break;
- case IEEE80211_AMPDU_TX_START:
- if (!priv->trans->ops->txq_enable)
- break;
- if (!iwl_enable_tx_ampdu(priv->cfg))
- break;
- IWL_DEBUG_HT(priv, "start Tx\n");
- ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
- break;
- case IEEE80211_AMPDU_TX_STOP_FLUSH:
- case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- IWL_DEBUG_HT(priv, "Flush Tx\n");
- ret = iwlagn_tx_agg_flush(priv, vif, sta, tid);
- break;
- case IEEE80211_AMPDU_TX_STOP_CONT:
- IWL_DEBUG_HT(priv, "stop Tx\n");
- ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
- if ((ret == 0) && (priv->agg_tids_count > 0)) {
- priv->agg_tids_count--;
- IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n",
- priv->agg_tids_count);
- }
- if (!priv->agg_tids_count &&
- priv->hw_params.use_rts_for_aggregation) {
- /*
- * switch off RTS/CTS if it was previously enabled
- */
- sta_priv->lq_sta.lq.general_params.flags &=
- ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
- iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif),
- &sta_priv->lq_sta.lq, CMD_ASYNC, false);
- }
- break;
- case IEEE80211_AMPDU_TX_OPERATIONAL:
- ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size);
- break;
- }
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
- return ret;
-}
-
-static int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
- bool is_ap = vif->type == NL80211_IFTYPE_STATION;
- int ret;
- u8 sta_id;
-
- IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n",
- sta->addr);
- sta_priv->sta_id = IWL_INVALID_STATION;
-
- atomic_set(&sta_priv->pending_frames, 0);
- if (vif->type == NL80211_IFTYPE_AP)
- sta_priv->client = true;
-
- ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr,
- is_ap, sta, &sta_id);
- if (ret) {
- IWL_ERR(priv, "Unable to add station %pM (%d)\n",
- sta->addr, ret);
- /* Should we return success if return code is EEXIST ? */
- return ret;
- }
-
- sta_priv->sta_id = sta_id;
-
- return 0;
-}
-
-static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- int ret;
-
- IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr);
-
- if (vif->type == NL80211_IFTYPE_STATION) {
- /*
- * Station will be removed from device when the RXON
- * is set to unassociated -- just deactivate it here
- * to avoid re-programming it.
- */
- ret = 0;
- iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr);
- } else {
- ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr);
- if (ret)
- IWL_DEBUG_QUIET_RFKILL(priv,
- "Error removing station %pM\n", sta->addr);
- }
- return ret;
-}
-
-static int iwlagn_mac_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 iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
- enum {
- NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT,
- } op = NONE;
- int ret;
-
- IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n",
- sta->addr, old_state, new_state);
-
- mutex_lock(&priv->mutex);
- if (vif->type == NL80211_IFTYPE_STATION) {
- if (old_state == IEEE80211_STA_NOTEXIST &&
- new_state == IEEE80211_STA_NONE)
- op = ADD;
- else if (old_state == IEEE80211_STA_NONE &&
- new_state == IEEE80211_STA_NOTEXIST)
- op = REMOVE;
- else if (old_state == IEEE80211_STA_AUTH &&
- new_state == IEEE80211_STA_ASSOC)
- op = HT_RATE_INIT;
- } else {
- if (old_state == IEEE80211_STA_AUTH &&
- new_state == IEEE80211_STA_ASSOC)
- op = ADD_RATE_INIT;
- else if (old_state == IEEE80211_STA_ASSOC &&
- new_state == IEEE80211_STA_AUTH)
- op = REMOVE;
- }
-
- switch (op) {
- case ADD:
- ret = iwlagn_mac_sta_add(hw, vif, sta);
- if (ret)
- break;
- /*
- * Clear the in-progress flag, the AP station entry was added
- * but we'll initialize LQ only when we've associated (which
- * would also clear the in-progress flag). This is necessary
- * in case we never initialize LQ because association fails.
- */
- spin_lock_bh(&priv->sta_lock);
- priv->stations[iwl_sta_id(sta)].used &=
- ~IWL_STA_UCODE_INPROGRESS;
- spin_unlock_bh(&priv->sta_lock);
- break;
- case REMOVE:
- ret = iwlagn_mac_sta_remove(hw, vif, sta);
- break;
- case ADD_RATE_INIT:
- ret = iwlagn_mac_sta_add(hw, vif, sta);
- if (ret)
- break;
- /* Initialize rate scaling */
- IWL_DEBUG_INFO(priv,
- "Initializing rate scaling for station %pM\n",
- sta->addr);
- iwl_rs_rate_init(priv, sta, iwl_sta_id(sta));
- ret = 0;
- break;
- case HT_RATE_INIT:
- /* Initialize rate scaling */
- ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta);
- if (ret)
- break;
- IWL_DEBUG_INFO(priv,
- "Initializing rate scaling for station %pM\n",
- sta->addr);
- iwl_rs_rate_init(priv, sta, iwl_sta_id(sta));
- ret = 0;
- break;
- default:
- ret = 0;
- break;
- }
-
- /*
- * mac80211 might WARN if we fail, but due the way we
- * (badly) handle hard rfkill, we might fail here
- */
- if (iwl_is_rfkill(priv))
- ret = 0;
-
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- return ret;
-}
-
-static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_channel_switch *ch_switch)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct ieee80211_conf *conf = &hw->conf;
- struct ieee80211_channel *channel = ch_switch->chandef.chan;
- struct iwl_ht_config *ht_conf = &priv->current_ht_config;
- /*
- * MULTI-FIXME
- * When we add support for multiple interfaces, we need to
- * revisit this. The channel switch command in the device
- * only affects the BSS context, but what does that really
- * mean? And what if we get a CSA on the second interface?
- * This needs a lot of work.
- */
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- u16 ch;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- mutex_lock(&priv->mutex);
-
- if (iwl_is_rfkill(priv))
- goto out;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
- test_bit(STATUS_SCANNING, &priv->status) ||
- test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
- goto out;
-
- if (!iwl_is_associated_ctx(ctx))
- goto out;
-
- if (!priv->lib->set_channel_switch)
- goto out;
-
- ch = channel->hw_value;
- if (le16_to_cpu(ctx->active.channel) == ch)
- goto out;
-
- priv->current_ht_config.smps = conf->smps_mode;
-
- /* Configure HT40 channels */
- switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
- ctx->ht.is_40mhz = false;
- ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
- break;
- case NL80211_CHAN_HT40MINUS:
- ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- ctx->ht.is_40mhz = true;
- break;
- case NL80211_CHAN_HT40PLUS:
- ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- ctx->ht.is_40mhz = true;
- break;
- }
-
- if ((le16_to_cpu(ctx->staging.channel) != ch))
- ctx->staging.flags = 0;
-
- iwl_set_rxon_channel(priv, channel, ctx);
- iwl_set_rxon_ht(priv, ht_conf);
- iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif);
-
- /*
- * at this point, staging_rxon has the
- * configuration for channel switch
- */
- set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
- priv->switch_channel = cpu_to_le16(ch);
- if (priv->lib->set_channel_switch(priv, ch_switch)) {
- clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
- priv->switch_channel = 0;
- ieee80211_chswitch_done(ctx->vif, false);
- }
-
-out:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-}
-
-void iwl_chswitch_done(struct iwl_priv *priv, bool is_success)
-{
- /*
- * MULTI-FIXME
- * See iwlagn_mac_channel_switch.
- */
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
- return;
-
- if (ctx->vif)
- ieee80211_chswitch_done(ctx->vif, is_success);
-}
-
-static void iwlagn_configure_filter(struct ieee80211_hw *hw,
- unsigned int changed_flags,
- unsigned int *total_flags,
- u64 multicast)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- __le32 filter_or = 0, filter_nand = 0;
- struct iwl_rxon_context *ctx;
-
-#define CHK(test, flag) do { \
- if (*total_flags & (test)) \
- filter_or |= (flag); \
- else \
- filter_nand |= (flag); \
- } while (0)
-
- IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n",
- changed_flags, *total_flags);
-
- CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK);
- /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */
- CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK);
- CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK);
-
-#undef CHK
-
- mutex_lock(&priv->mutex);
-
- for_each_context(priv, ctx) {
- ctx->staging.filter_flags &= ~filter_nand;
- ctx->staging.filter_flags |= filter_or;
-
- /*
- * Not committing directly because hardware can perform a scan,
- * but we'll eventually commit the filter flags change anyway.
- */
- }
-
- mutex_unlock(&priv->mutex);
-
- /*
- * Receiving all multicast frames is always enabled by the
- * default flags setup in iwl_connection_init_rx_config()
- * since we currently do not support programming multicast
- * filters into the device.
- */
- *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI |
- FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
-}
-
-static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- u32 queues, bool drop)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- u32 scd_queues;
-
- mutex_lock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
- IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n");
- goto done;
- }
- if (iwl_is_rfkill(priv)) {
- IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n");
- goto done;
- }
-
- scd_queues = BIT(priv->cfg->base_params->num_of_queues) - 1;
- scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) |
- BIT(IWL_DEFAULT_CMD_QUEUE_NUM));
-
- if (drop) {
- IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n",
- scd_queues);
- if (iwlagn_txfifo_flush(priv, scd_queues)) {
- IWL_ERR(priv, "flush request fail\n");
- goto done;
- }
- }
-
- IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n");
- iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues);
-done:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-}
-
-static void iwlagn_mac_event_callback(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- const struct ieee80211_event *event)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- if (event->type != RSSI_EVENT)
- return;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist) {
- if (event->u.rssi.data == RSSI_EVENT_LOW)
- priv->bt_enable_pspoll = true;
- else if (event->u.rssi.data == RSSI_EVENT_HIGH)
- priv->bt_enable_pspoll = false;
-
- queue_work(priv->workqueue, &priv->bt_runtime_config);
- } else {
- IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled,"
- "ignoring RSSI callback\n");
- }
-
- IWL_DEBUG_MAC80211(priv, "leave\n");
-}
-
-static int iwlagn_mac_set_tim(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta, bool set)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- queue_work(priv->workqueue, &priv->beacon_update);
-
- return 0;
-}
-
-static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, u16 queue,
- const struct ieee80211_tx_queue_params *params)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
- struct iwl_rxon_context *ctx = vif_priv->ctx;
- int q;
-
- if (WARN_ON(!ctx))
- return -EINVAL;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- if (!iwl_is_ready_rf(priv)) {
- IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
- return -EIO;
- }
-
- if (queue >= AC_NUM) {
- IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue);
- return 0;
- }
-
- q = AC_NUM - 1 - queue;
-
- mutex_lock(&priv->mutex);
-
- ctx->qos_data.def_qos_parm.ac[q].cw_min =
- cpu_to_le16(params->cw_min);
- ctx->qos_data.def_qos_parm.ac[q].cw_max =
- cpu_to_le16(params->cw_max);
- ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs;
- ctx->qos_data.def_qos_parm.ac[q].edca_txop =
- cpu_to_le16((params->txop * 32));
-
- ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0;
-
- mutex_unlock(&priv->mutex);
-
- IWL_DEBUG_MAC80211(priv, "leave\n");
- return 0;
-}
-
-static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- return priv->ibss_manager == IWL_IBSS_MANAGER;
-}
-
-static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- iwl_connection_init_rx_config(priv, ctx);
-
- iwlagn_set_rxon_chain(priv, ctx);
-
- return iwlagn_commit_rxon(priv, ctx);
-}
-
-static int iwl_setup_interface(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- struct ieee80211_vif *vif = ctx->vif;
- int err, ac;
-
- lockdep_assert_held(&priv->mutex);
-
- /*
- * This variable will be correct only when there's just
- * a single context, but all code using it is for hardware
- * that supports only one context.
- */
- priv->iw_mode = vif->type;
-
- ctx->is_active = true;
-
- err = iwl_set_mode(priv, ctx);
- if (err) {
- if (!ctx->always_active)
- ctx->is_active = false;
- return err;
- }
-
- if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist &&
- vif->type == NL80211_IFTYPE_ADHOC) {
- /*
- * pretend to have high BT traffic as long as we
- * are operating in IBSS mode, as this will cause
- * the rate scaling etc. to behave as intended.
- */
- priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
- }
-
- /* set up queue mappings */
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
- vif->hw_queue[ac] = ctx->ac_to_queue[ac];
-
- if (vif->type == NL80211_IFTYPE_AP)
- vif->cab_queue = ctx->mcast_queue;
- else
- vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
-
- return 0;
-}
-
-static int iwlagn_mac_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
- struct iwl_rxon_context *tmp, *ctx = NULL;
- int err;
- enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif);
- bool reset = false;
-
- IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
- viftype, vif->addr);
-
- mutex_lock(&priv->mutex);
-
- if (!iwl_is_ready_rf(priv)) {
- IWL_WARN(priv, "Try to add interface when device not ready\n");
- err = -EINVAL;
- goto out;
- }
-
- for_each_context(priv, tmp) {
- u32 possible_modes =
- tmp->interface_modes | tmp->exclusive_interface_modes;
-
- if (tmp->vif) {
- /* On reset we need to add the same interface again */
- if (tmp->vif == vif) {
- reset = true;
- ctx = tmp;
- break;
- }
-
- /* check if this busy context is exclusive */
- if (tmp->exclusive_interface_modes &
- BIT(tmp->vif->type)) {
- err = -EINVAL;
- goto out;
- }
- continue;
- }
-
- if (!(possible_modes & BIT(viftype)))
- continue;
-
- /* have maybe usable context w/o interface */
- ctx = tmp;
- break;
- }
-
- if (!ctx) {
- err = -EOPNOTSUPP;
- goto out;
- }
-
- vif_priv->ctx = ctx;
- ctx->vif = vif;
-
- /*
- * In SNIFFER device type, the firmware reports the FCS to
- * the host, rather than snipping it off. Unfortunately,
- * mac80211 doesn't (yet) provide a per-packet flag for
- * this, so that we have to set the hardware flag based
- * on the interfaces added. As the monitor interface can
- * only be present by itself, and will be removed before
- * other interfaces are added, this is safe.
- */
- if (vif->type == NL80211_IFTYPE_MONITOR)
- ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS);
- else
- __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, priv->hw->flags);
-
- err = iwl_setup_interface(priv, ctx);
- if (!err || reset)
- goto out;
-
- ctx->vif = NULL;
- priv->iw_mode = NL80211_IFTYPE_STATION;
- out:
- mutex_unlock(&priv->mutex);
-
- IWL_DEBUG_MAC80211(priv, "leave\n");
- return err;
-}
-
-static void iwl_teardown_interface(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- bool mode_change)
-{
- struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
-
- lockdep_assert_held(&priv->mutex);
-
- if (priv->scan_vif == vif) {
- iwl_scan_cancel_timeout(priv, 200);
- iwl_force_scan_end(priv);
- }
-
- if (!mode_change) {
- iwl_set_mode(priv, ctx);
- if (!ctx->always_active)
- ctx->is_active = false;
- }
-
- /*
- * When removing the IBSS interface, overwrite the
- * BT traffic load with the stored one from the last
- * notification, if any. If this is a device that
- * doesn't implement this, this has no effect since
- * both values are the same and zero.
- */
- if (vif->type == NL80211_IFTYPE_ADHOC)
- priv->bt_traffic_load = priv->last_bt_traffic_load;
-}
-
-static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- mutex_lock(&priv->mutex);
-
- if (WARN_ON(ctx->vif != vif)) {
- struct iwl_rxon_context *tmp;
- IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif);
- for_each_context(priv, tmp)
- IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n",
- tmp->ctxid, tmp, tmp->vif);
- }
- ctx->vif = NULL;
-
- iwl_teardown_interface(priv, vif, false);
-
- mutex_unlock(&priv->mutex);
-
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
-}
-
-static int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum nl80211_iftype newtype, bool newp2p)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_rxon_context *ctx, *tmp;
- enum nl80211_iftype newviftype = newtype;
- u32 interface_modes;
- int err;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- newtype = ieee80211_iftype_p2p(newtype, newp2p);
-
- mutex_lock(&priv->mutex);
-
- ctx = iwl_rxon_ctx_from_vif(vif);
-
- /*
- * To simplify this code, only support changes on the
- * BSS context. The PAN context is usually reassigned
- * by creating/removing P2P interfaces anyway.
- */
- if (ctx->ctxid != IWL_RXON_CTX_BSS) {
- err = -EBUSY;
- goto out;
- }
-
- if (!ctx->vif || !iwl_is_ready_rf(priv)) {
- /*
- * Huh? But wait ... this can maybe happen when
- * we're in the middle of a firmware restart!
- */
- err = -EBUSY;
- goto out;
- }
-
- /* Check if the switch is supported in the same context */
- interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes;
- if (!(interface_modes & BIT(newtype))) {
- err = -EBUSY;
- goto out;
- }
-
- if (ctx->exclusive_interface_modes & BIT(newtype)) {
- for_each_context(priv, tmp) {
- if (ctx == tmp)
- continue;
-
- if (!tmp->is_active)
- continue;
-
- /*
- * The current mode switch would be exclusive, but
- * another context is active ... refuse the switch.
- */
- err = -EBUSY;
- goto out;
- }
- }
-
- /* success */
- iwl_teardown_interface(priv, vif, true);
- vif->type = newviftype;
- vif->p2p = newp2p;
- err = iwl_setup_interface(priv, ctx);
- WARN_ON(err);
- /*
- * We've switched internally, but submitting to the
- * device may have failed for some reason. Mask this
- * error, because otherwise mac80211 will not switch
- * (and set the interface type back) and we'll be
- * out of sync with it.
- */
- err = 0;
-
- out:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- return err;
-}
-
-static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_scan_request *hw_req)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct cfg80211_scan_request *req = &hw_req->req;
- int ret;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- if (req->n_channels == 0)
- return -EINVAL;
-
- mutex_lock(&priv->mutex);
-
- /*
- * If an internal scan is in progress, just set
- * up the scan_request as per above.
- */
- if (priv->scan_type != IWL_SCAN_NORMAL) {
- IWL_DEBUG_SCAN(priv,
- "SCAN request during internal scan - defer\n");
- priv->scan_request = req;
- priv->scan_vif = vif;
- ret = 0;
- } else {
- priv->scan_request = req;
- priv->scan_vif = vif;
- /*
- * mac80211 will only ask for one band at a time
- * so using channels[0] here is ok
- */
- ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL,
- req->channels[0]->band);
- if (ret) {
- priv->scan_request = NULL;
- priv->scan_vif = NULL;
- }
- }
-
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- mutex_unlock(&priv->mutex);
-
- return ret;
-}
-
-static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
-{
- struct iwl_addsta_cmd cmd = {
- .mode = STA_CONTROL_MODIFY_MSK,
- .station_flags_msk = STA_FLG_PWR_SAVE_MSK,
- .sta.sta_id = sta_id,
- };
-
- iwl_send_add_sta(priv, &cmd, CMD_ASYNC);
-}
-
-static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum sta_notify_cmd cmd,
- struct ieee80211_sta *sta)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- int sta_id;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
-
- switch (cmd) {
- case STA_NOTIFY_SLEEP:
- WARN_ON(!sta_priv->client);
- sta_priv->asleep = true;
- if (atomic_read(&sta_priv->pending_frames) > 0)
- ieee80211_sta_block_awake(hw, sta, true);
- break;
- case STA_NOTIFY_AWAKE:
- WARN_ON(!sta_priv->client);
- if (!sta_priv->asleep)
- break;
- sta_priv->asleep = false;
- sta_id = iwl_sta_id(sta);
- if (sta_id != IWL_INVALID_STATION)
- iwl_sta_modify_ps_wake(priv, sta_id);
- break;
- default:
- break;
- }
- IWL_DEBUG_MAC80211(priv, "leave\n");
-}
-
-const struct ieee80211_ops iwlagn_hw_ops = {
- .tx = iwlagn_mac_tx,
- .start = iwlagn_mac_start,
- .stop = iwlagn_mac_stop,
-#ifdef CONFIG_PM_SLEEP
- .suspend = iwlagn_mac_suspend,
- .resume = iwlagn_mac_resume,
- .set_wakeup = iwlagn_mac_set_wakeup,
-#endif
- .add_interface = iwlagn_mac_add_interface,
- .remove_interface = iwlagn_mac_remove_interface,
- .change_interface = iwlagn_mac_change_interface,
- .config = iwlagn_mac_config,
- .configure_filter = iwlagn_configure_filter,
- .set_key = iwlagn_mac_set_key,
- .update_tkip_key = iwlagn_mac_update_tkip_key,
- .set_rekey_data = iwlagn_mac_set_rekey_data,
- .conf_tx = iwlagn_mac_conf_tx,
- .bss_info_changed = iwlagn_bss_info_changed,
- .ampdu_action = iwlagn_mac_ampdu_action,
- .hw_scan = iwlagn_mac_hw_scan,
- .sta_notify = iwlagn_mac_sta_notify,
- .sta_state = iwlagn_mac_sta_state,
- .channel_switch = iwlagn_mac_channel_switch,
- .flush = iwlagn_mac_flush,
- .tx_last_beacon = iwlagn_mac_tx_last_beacon,
- .event_callback = iwlagn_mac_event_callback,
- .set_tim = iwlagn_mac_set_tim,
-};
-
-/* This function both allocates and initializes hw and priv. */
-struct ieee80211_hw *iwl_alloc_all(void)
-{
- struct iwl_priv *priv;
- struct iwl_op_mode *op_mode;
- /* mac80211 allocates memory for this device instance, including
- * space for this driver's private structure */
- struct ieee80211_hw *hw;
-
- hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) +
- sizeof(struct iwl_op_mode), &iwlagn_hw_ops);
- if (!hw)
- goto out;
-
- op_mode = hw->priv;
- priv = IWL_OP_MODE_GET_DVM(op_mode);
- priv->hw = hw;
-
-out:
- return hw;
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c
deleted file mode 100644
index e7616f0ee6e8..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ /dev/null
@@ -1,2077 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-
-#include <net/mac80211.h>
-
-#include <asm/div64.h>
-
-#include "iwl-eeprom-read.h"
-#include "iwl-eeprom-parse.h"
-#include "iwl-io.h"
-#include "iwl-trans.h"
-#include "iwl-op-mode.h"
-#include "iwl-drv.h"
-#include "iwl-modparams.h"
-#include "iwl-prph.h"
-
-#include "dev.h"
-#include "calib.h"
-#include "agn.h"
-
-
-/******************************************************************************
- *
- * module boiler plate
- *
- ******************************************************************************/
-
-#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux"
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
-MODULE_LICENSE("GPL");
-
-static const struct iwl_op_mode_ops iwl_dvm_ops;
-
-void iwl_update_chain_flags(struct iwl_priv *priv)
-{
- struct iwl_rxon_context *ctx;
-
- for_each_context(priv, ctx) {
- iwlagn_set_rxon_chain(priv, ctx);
- if (ctx->active.rx_chain != ctx->staging.rx_chain)
- iwlagn_commit_rxon(priv, ctx);
- }
-}
-
-/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
-static void iwl_set_beacon_tim(struct iwl_priv *priv,
- struct iwl_tx_beacon_cmd *tx_beacon_cmd,
- u8 *beacon, u32 frame_size)
-{
- u16 tim_idx;
- struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
-
- /*
- * The index is relative to frame start but we start looking at the
- * variable-length part of the beacon.
- */
- tim_idx = mgmt->u.beacon.variable - beacon;
-
- /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
- while ((tim_idx < (frame_size - 2)) &&
- (beacon[tim_idx] != WLAN_EID_TIM))
- tim_idx += beacon[tim_idx+1] + 2;
-
- /* If TIM field was found, set variables */
- if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
- tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
- tx_beacon_cmd->tim_size = beacon[tim_idx+1];
- } else
- IWL_WARN(priv, "Unable to find TIM Element in beacon\n");
-}
-
-int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
-{
- struct iwl_tx_beacon_cmd *tx_beacon_cmd;
- struct iwl_host_cmd cmd = {
- .id = REPLY_TX_BEACON,
- };
- struct ieee80211_tx_info *info;
- u32 frame_size;
- u32 rate_flags;
- u32 rate;
-
- /*
- * We have to set up the TX command, the TX Beacon command, and the
- * beacon contents.
- */
-
- lockdep_assert_held(&priv->mutex);
-
- if (!priv->beacon_ctx) {
- IWL_ERR(priv, "trying to build beacon w/o beacon context!\n");
- return 0;
- }
-
- if (WARN_ON(!priv->beacon_skb))
- return -EINVAL;
-
- /* Allocate beacon command */
- if (!priv->beacon_cmd)
- priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL);
- tx_beacon_cmd = priv->beacon_cmd;
- if (!tx_beacon_cmd)
- return -ENOMEM;
-
- frame_size = priv->beacon_skb->len;
-
- /* Set up TX command fields */
- tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
- tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id;
- tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
- tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
- TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK;
-
- /* Set up TX beacon command fields */
- iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data,
- frame_size);
-
- /* Set up packet rate and flags */
- info = IEEE80211_SKB_CB(priv->beacon_skb);
-
- /*
- * Let's set up the rate at least somewhat correctly;
- * it will currently not actually be used by the uCode,
- * it uses the broadcast station's rate instead.
- */
- if (info->control.rates[0].idx < 0 ||
- info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
- rate = 0;
- else
- rate = info->control.rates[0].idx;
-
- priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
- priv->nvm_data->valid_tx_ant);
- rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
-
- /* In mac80211, rates for 5 GHz start at 0 */
- if (info->band == IEEE80211_BAND_5GHZ)
- rate += IWL_FIRST_OFDM_RATE;
- else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE)
- rate_flags |= RATE_MCS_CCK_MSK;
-
- tx_beacon_cmd->tx.rate_n_flags =
- iwl_hw_set_rate_n_flags(rate, rate_flags);
-
- /* Submit command */
- cmd.len[0] = sizeof(*tx_beacon_cmd);
- cmd.data[0] = tx_beacon_cmd;
- cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
- cmd.len[1] = frame_size;
- cmd.data[1] = priv->beacon_skb->data;
- cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
-
- return iwl_dvm_send_cmd(priv, &cmd);
-}
-
-static void iwl_bg_beacon_update(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, beacon_update);
- struct sk_buff *beacon;
-
- mutex_lock(&priv->mutex);
- if (!priv->beacon_ctx) {
- IWL_ERR(priv, "updating beacon w/o beacon context!\n");
- goto out;
- }
-
- if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) {
- /*
- * The ucode will send beacon notifications even in
- * IBSS mode, but we don't want to process them. But
- * we need to defer the type check to here due to
- * requiring locking around the beacon_ctx access.
- */
- goto out;
- }
-
- /* Pull updated AP beacon from mac80211. will fail if not in AP mode */
- beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif);
- if (!beacon) {
- IWL_ERR(priv, "update beacon failed -- keeping old\n");
- goto out;
- }
-
- /* new beacon skb is allocated every time; dispose previous.*/
- dev_kfree_skb(priv->beacon_skb);
-
- priv->beacon_skb = beacon;
-
- iwlagn_send_beacon_cmd(priv);
- out:
- mutex_unlock(&priv->mutex);
-}
-
-static void iwl_bg_bt_runtime_config(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, bt_runtime_config);
-
- mutex_lock(&priv->mutex);
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- goto out;
-
- /* dont send host command if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
- goto out;
-
- iwlagn_send_advance_bt_config(priv);
-out:
- mutex_unlock(&priv->mutex);
-}
-
-static void iwl_bg_bt_full_concurrency(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, bt_full_concurrency);
- struct iwl_rxon_context *ctx;
-
- mutex_lock(&priv->mutex);
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- goto out;
-
- /* dont send host command if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
- goto out;
-
- IWL_DEBUG_INFO(priv, "BT coex in %s mode\n",
- priv->bt_full_concurrent ?
- "full concurrency" : "3-wire");
-
- /*
- * LQ & RXON updated cmds must be sent before BT Config cmd
- * to avoid 3-wire collisions
- */
- for_each_context(priv, ctx) {
- iwlagn_set_rxon_chain(priv, ctx);
- iwlagn_commit_rxon(priv, ctx);
- }
-
- iwlagn_send_advance_bt_config(priv);
-out:
- mutex_unlock(&priv->mutex);
-}
-
-int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
-{
- struct iwl_statistics_cmd statistics_cmd = {
- .configuration_flags =
- clear ? IWL_STATS_CONF_CLEAR_STATS : 0,
- };
-
- if (flags & CMD_ASYNC)
- return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
- CMD_ASYNC,
- sizeof(struct iwl_statistics_cmd),
- &statistics_cmd);
- else
- return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0,
- sizeof(struct iwl_statistics_cmd),
- &statistics_cmd);
-}
-
-/**
- * iwl_bg_statistics_periodic - Timer callback to queue statistics
- *
- * This callback is provided in order to send a statistics request.
- *
- * This timer function is continually reset to execute within
- * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION
- * was received. We need to ensure we receive the statistics in order
- * to update the temperature used for calibrating the TXPOWER.
- */
-static void iwl_bg_statistics_periodic(unsigned long data)
-{
- struct iwl_priv *priv = (struct iwl_priv *)data;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- /* dont send host command if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
- return;
-
- iwl_send_statistics_request(priv, CMD_ASYNC, false);
-}
-
-
-static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
- u32 start_idx, u32 num_events,
- u32 capacity, u32 mode)
-{
- u32 i;
- u32 ptr; /* SRAM byte address of log data */
- u32 ev, time, data; /* event log data */
- unsigned long reg_flags;
-
- if (mode == 0)
- ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32));
- else
- ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
-
- /* Make sure device is powered up for SRAM reads */
- if (!iwl_trans_grab_nic_access(priv->trans, false, &reg_flags))
- return;
-
- /* Set starting address; reads will auto-increment */
- iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr);
-
- /*
- * Refuse to read more than would have fit into the log from
- * the current start_idx. This used to happen due to the race
- * described below, but now WARN because the code below should
- * prevent it from happening here.
- */
- if (WARN_ON(num_events > capacity - start_idx))
- num_events = capacity - start_idx;
-
- /*
- * "time" is actually "data" for mode 0 (no timestamp).
- * place event id # at far right for easier visual parsing.
- */
- for (i = 0; i < num_events; i++) {
- ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
- time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
- if (mode == 0) {
- trace_iwlwifi_dev_ucode_cont_event(
- priv->trans->dev, 0, time, ev);
- } else {
- data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
- trace_iwlwifi_dev_ucode_cont_event(
- priv->trans->dev, time, data, ev);
- }
- }
- /* Allow device to power down */
- iwl_trans_release_nic_access(priv->trans, &reg_flags);
-}
-
-static void iwl_continuous_event_trace(struct iwl_priv *priv)
-{
- u32 capacity; /* event log capacity in # entries */
- struct {
- u32 capacity;
- u32 mode;
- u32 wrap_counter;
- u32 write_counter;
- } __packed read;
- u32 base; /* SRAM byte address of event log header */
- u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
- u32 num_wraps; /* # times uCode wrapped to top of log */
- u32 next_entry; /* index of next entry to be written by uCode */
-
- base = priv->device_pointers.log_event_table;
- if (iwlagn_hw_valid_rtc_data_addr(base)) {
- iwl_trans_read_mem_bytes(priv->trans, base,
- &read, sizeof(read));
- capacity = read.capacity;
- mode = read.mode;
- num_wraps = read.wrap_counter;
- next_entry = read.write_counter;
- } else
- return;
-
- /*
- * Unfortunately, the uCode doesn't use temporary variables.
- * Therefore, it can happen that we read next_entry == capacity,
- * which really means next_entry == 0.
- */
- if (unlikely(next_entry == capacity))
- next_entry = 0;
- /*
- * Additionally, the uCode increases the write pointer before
- * the wraps counter, so if the write pointer is smaller than
- * the old write pointer (wrap occurred) but we read that no
- * wrap occurred, we actually read between the next_entry and
- * num_wraps update (this does happen in practice!!) -- take
- * that into account by increasing num_wraps.
- */
- if (unlikely(next_entry < priv->event_log.next_entry &&
- num_wraps == priv->event_log.num_wraps))
- num_wraps++;
-
- if (num_wraps == priv->event_log.num_wraps) {
- iwl_print_cont_event_trace(
- priv, base, priv->event_log.next_entry,
- next_entry - priv->event_log.next_entry,
- capacity, mode);
-
- priv->event_log.non_wraps_count++;
- } else {
- if (num_wraps - priv->event_log.num_wraps > 1)
- priv->event_log.wraps_more_count++;
- else
- priv->event_log.wraps_once_count++;
-
- trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev,
- num_wraps - priv->event_log.num_wraps,
- next_entry, priv->event_log.next_entry);
-
- if (next_entry < priv->event_log.next_entry) {
- iwl_print_cont_event_trace(
- priv, base, priv->event_log.next_entry,
- capacity - priv->event_log.next_entry,
- capacity, mode);
-
- iwl_print_cont_event_trace(
- priv, base, 0, next_entry, capacity, mode);
- } else {
- iwl_print_cont_event_trace(
- priv, base, next_entry,
- capacity - next_entry,
- capacity, mode);
-
- iwl_print_cont_event_trace(
- priv, base, 0, next_entry, capacity, mode);
- }
- }
-
- priv->event_log.num_wraps = num_wraps;
- priv->event_log.next_entry = next_entry;
-}
-
-/**
- * iwl_bg_ucode_trace - Timer callback to log ucode event
- *
- * The timer is continually set to execute every
- * UCODE_TRACE_PERIOD milliseconds after the last timer expired
- * this function is to perform continuous uCode event logging operation
- * if enabled
- */
-static void iwl_bg_ucode_trace(unsigned long data)
-{
- struct iwl_priv *priv = (struct iwl_priv *)data;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (priv->event_log.ucode_trace) {
- iwl_continuous_event_trace(priv);
- /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */
- mod_timer(&priv->ucode_trace,
- jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
- }
-}
-
-static void iwl_bg_tx_flush(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, tx_flush);
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- /* do nothing if rf-kill is on */
- if (!iwl_is_ready_rf(priv))
- return;
-
- IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n");
- iwlagn_dev_txfifo_flush(priv);
-}
-
-/*
- * queue/FIFO/AC mapping definitions
- */
-
-static const u8 iwlagn_bss_ac_to_fifo[] = {
- IWL_TX_FIFO_VO,
- IWL_TX_FIFO_VI,
- IWL_TX_FIFO_BE,
- IWL_TX_FIFO_BK,
-};
-
-static const u8 iwlagn_bss_ac_to_queue[] = {
- 0, 1, 2, 3,
-};
-
-static const u8 iwlagn_pan_ac_to_fifo[] = {
- IWL_TX_FIFO_VO_IPAN,
- IWL_TX_FIFO_VI_IPAN,
- IWL_TX_FIFO_BE_IPAN,
- IWL_TX_FIFO_BK_IPAN,
-};
-
-static const u8 iwlagn_pan_ac_to_queue[] = {
- 7, 6, 5, 4,
-};
-
-static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
-{
- int i;
-
- /*
- * The default context is always valid,
- * the PAN context depends on uCode.
- */
- priv->valid_contexts = BIT(IWL_RXON_CTX_BSS);
- if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN)
- priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
-
- for (i = 0; i < NUM_IWL_RXON_CTX; i++)
- priv->contexts[i].ctxid = i;
-
- priv->contexts[IWL_RXON_CTX_BSS].always_active = true;
- priv->contexts[IWL_RXON_CTX_BSS].is_active = true;
- priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON;
- priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING;
- priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC;
- priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM;
- priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID;
- priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY;
- priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
- priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes =
- BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR);
- priv->contexts[IWL_RXON_CTX_BSS].interface_modes =
- BIT(NL80211_IFTYPE_STATION);
- priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP;
- priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS;
- priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS;
- priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS;
- memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue,
- iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue));
- memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo,
- iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo));
-
- priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON;
- priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd =
- REPLY_WIPAN_RXON_TIMING;
- priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd =
- REPLY_WIPAN_RXON_ASSOC;
- priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM;
- priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN;
- priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY;
- priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID;
- priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION;
- priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
- BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
-
- priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
- priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
- priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
- memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue,
- iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue));
- memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo,
- iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo));
- priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE;
-
- BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
-}
-
-static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
-{
- struct iwl_ct_kill_config cmd;
- struct iwl_ct_kill_throttling_config adv_cmd;
- int ret = 0;
-
- iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
-
- priv->thermal_throttle.ct_kill_toggle = false;
-
- if (priv->lib->support_ct_kill_exit) {
- adv_cmd.critical_temperature_enter =
- cpu_to_le32(priv->hw_params.ct_kill_threshold);
- adv_cmd.critical_temperature_exit =
- cpu_to_le32(priv->hw_params.ct_kill_exit_threshold);
-
- ret = iwl_dvm_send_cmd_pdu(priv,
- REPLY_CT_KILL_CONFIG_CMD,
- 0, sizeof(adv_cmd), &adv_cmd);
- if (ret)
- IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
- else
- IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
- "succeeded, critical temperature enter is %d,"
- "exit is %d\n",
- priv->hw_params.ct_kill_threshold,
- priv->hw_params.ct_kill_exit_threshold);
- } else {
- cmd.critical_temperature_R =
- cpu_to_le32(priv->hw_params.ct_kill_threshold);
-
- ret = iwl_dvm_send_cmd_pdu(priv,
- REPLY_CT_KILL_CONFIG_CMD,
- 0, sizeof(cmd), &cmd);
- if (ret)
- IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
- else
- IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
- "succeeded, "
- "critical temperature is %d\n",
- priv->hw_params.ct_kill_threshold);
- }
-}
-
-static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg)
-{
- struct iwl_calib_cfg_cmd calib_cfg_cmd;
- struct iwl_host_cmd cmd = {
- .id = CALIBRATION_CFG_CMD,
- .len = { sizeof(struct iwl_calib_cfg_cmd), },
- .data = { &calib_cfg_cmd, },
- };
-
- memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
- calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL;
- calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg);
-
- return iwl_dvm_send_cmd(priv, &cmd);
-}
-
-
-static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
-{
- struct iwl_tx_ant_config_cmd tx_ant_cmd = {
- .valid = cpu_to_le32(valid_tx_ant),
- };
-
- if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) {
- IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
- return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0,
- sizeof(struct iwl_tx_ant_config_cmd),
- &tx_ant_cmd);
- } else {
- IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n");
- return -EOPNOTSUPP;
- }
-}
-
-static void iwl_send_bt_config(struct iwl_priv *priv)
-{
- struct iwl_bt_cmd bt_cmd = {
- .lead_time = BT_LEAD_TIME_DEF,
- .max_kill = BT_MAX_KILL_DEF,
- .kill_ack_mask = 0,
- .kill_cts_mask = 0,
- };
-
- if (!iwlwifi_mod_params.bt_coex_active)
- bt_cmd.flags = BT_COEX_DISABLE;
- else
- bt_cmd.flags = BT_COEX_ENABLE;
-
- priv->bt_enable_flag = bt_cmd.flags;
- IWL_DEBUG_INFO(priv, "BT coex %s\n",
- (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
-
- if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
- 0, sizeof(struct iwl_bt_cmd), &bt_cmd))
- IWL_ERR(priv, "failed to send BT Coex Config\n");
-}
-
-/**
- * iwl_alive_start - called after REPLY_ALIVE notification received
- * from protocol/runtime uCode (initialization uCode's
- * Alive gets handled by iwl_init_alive_start()).
- */
-int iwl_alive_start(struct iwl_priv *priv)
-{
- int ret = 0;
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
-
- IWL_DEBUG_INFO(priv, "Runtime Alive received.\n");
-
- /* After the ALIVE response, we can send host commands to the uCode */
- set_bit(STATUS_ALIVE, &priv->status);
-
- if (iwl_is_rfkill(priv))
- return -ERFKILL;
-
- if (priv->event_log.ucode_trace) {
- /* start collecting data now */
- mod_timer(&priv->ucode_trace, jiffies);
- }
-
- /* download priority table before any calibration request */
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist) {
- /* Configure Bluetooth device coexistence support */
- if (priv->lib->bt_params->bt_sco_disable)
- priv->bt_enable_pspoll = false;
- else
- priv->bt_enable_pspoll = true;
-
- priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
- priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
- priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
- iwlagn_send_advance_bt_config(priv);
- priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
- priv->cur_rssi_ctx = NULL;
-
- iwl_send_prio_tbl(priv);
-
- /* FIXME: w/a to force change uCode BT state machine */
- ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
- BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
- if (ret)
- return ret;
- ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
- BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
- if (ret)
- return ret;
- } else if (priv->lib->bt_params) {
- /*
- * default is 2-wire BT coexexistence support
- */
- iwl_send_bt_config(priv);
- }
-
- /*
- * Perform runtime calibrations, including DC calibration.
- */
- iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX);
-
- ieee80211_wake_queues(priv->hw);
-
- /* Configure Tx antenna selection based on H/W config */
- iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant);
-
- if (iwl_is_associated_ctx(ctx) && !priv->wowlan) {
- struct iwl_rxon_cmd *active_rxon =
- (struct iwl_rxon_cmd *)&ctx->active;
- /* apply any changes in staging */
- ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
- active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- } else {
- struct iwl_rxon_context *tmp;
- /* Initialize our rx_config data */
- for_each_context(priv, tmp)
- iwl_connection_init_rx_config(priv, tmp);
-
- iwlagn_set_rxon_chain(priv, ctx);
- }
-
- if (!priv->wowlan) {
- /* WoWLAN ucode will not reply in the same way, skip it */
- iwl_reset_run_time_calib(priv);
- }
-
- set_bit(STATUS_READY, &priv->status);
-
- /* Configure the adapter for unassociated operation */
- ret = iwlagn_commit_rxon(priv, ctx);
- if (ret)
- return ret;
-
- /* At this point, the NIC is initialized and operational */
- iwl_rf_kill_ct_config(priv);
-
- IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
-
- return iwl_power_update_mode(priv, true);
-}
-
-/**
- * iwl_clear_driver_stations - clear knowledge of all stations from driver
- * @priv: iwl priv struct
- *
- * This is called during iwl_down() to make sure that in the case
- * we're coming there from a hardware restart mac80211 will be
- * able to reconfigure stations -- if we're getting there in the
- * normal down flow then the stations will already be cleared.
- */
-static void iwl_clear_driver_stations(struct iwl_priv *priv)
-{
- struct iwl_rxon_context *ctx;
-
- spin_lock_bh(&priv->sta_lock);
- memset(priv->stations, 0, sizeof(priv->stations));
- priv->num_stations = 0;
-
- priv->ucode_key_table = 0;
-
- for_each_context(priv, ctx) {
- /*
- * Remove all key information that is not stored as part
- * of station information since mac80211 may not have had
- * a chance to remove all the keys. When device is
- * reconfigured by mac80211 after an error all keys will
- * be reconfigured.
- */
- memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys));
- ctx->key_mapping_keys = 0;
- }
-
- spin_unlock_bh(&priv->sta_lock);
-}
-
-void iwl_down(struct iwl_priv *priv)
-{
- int exit_pending;
-
- IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
-
- lockdep_assert_held(&priv->mutex);
-
- iwl_scan_cancel_timeout(priv, 200);
-
- exit_pending =
- test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
-
- iwl_clear_ucode_stations(priv, NULL);
- iwl_dealloc_bcast_stations(priv);
- iwl_clear_driver_stations(priv);
-
- /* reset BT coex data */
- priv->bt_status = 0;
- priv->cur_rssi_ctx = NULL;
- priv->bt_is_sco = 0;
- if (priv->lib->bt_params)
- priv->bt_traffic_load =
- priv->lib->bt_params->bt_init_traffic_load;
- else
- priv->bt_traffic_load = 0;
- priv->bt_full_concurrent = false;
- priv->bt_ci_compliance = 0;
-
- /* Wipe out the EXIT_PENDING status bit if we are not actually
- * exiting the module */
- if (!exit_pending)
- clear_bit(STATUS_EXIT_PENDING, &priv->status);
-
- if (priv->mac80211_registered)
- ieee80211_stop_queues(priv->hw);
-
- priv->ucode_loaded = false;
- iwl_trans_stop_device(priv->trans);
-
- /* Set num_aux_in_flight must be done after the transport is stopped */
- atomic_set(&priv->num_aux_in_flight, 0);
-
- /* Clear out all status bits but a few that are stable across reset */
- priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) <<
- STATUS_RF_KILL_HW |
- test_bit(STATUS_FW_ERROR, &priv->status) <<
- STATUS_FW_ERROR |
- test_bit(STATUS_EXIT_PENDING, &priv->status) <<
- STATUS_EXIT_PENDING;
-
- dev_kfree_skb(priv->beacon_skb);
- priv->beacon_skb = NULL;
-}
-
-/*****************************************************************************
- *
- * Workqueue callbacks
- *
- *****************************************************************************/
-
-static void iwl_bg_run_time_calib_work(struct work_struct *work)
-{
- struct iwl_priv *priv = container_of(work, struct iwl_priv,
- run_time_calib_work);
-
- mutex_lock(&priv->mutex);
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
- test_bit(STATUS_SCANNING, &priv->status)) {
- mutex_unlock(&priv->mutex);
- return;
- }
-
- if (priv->start_calib) {
- iwl_chain_noise_calibration(priv);
- iwl_sensitivity_calibration(priv);
- }
-
- mutex_unlock(&priv->mutex);
-}
-
-void iwlagn_prepare_restart(struct iwl_priv *priv)
-{
- bool bt_full_concurrent;
- u8 bt_ci_compliance;
- u8 bt_load;
- u8 bt_status;
- bool bt_is_sco;
- int i;
-
- lockdep_assert_held(&priv->mutex);
-
- priv->is_open = 0;
-
- /*
- * __iwl_down() will clear the BT status variables,
- * which is correct, but when we restart we really
- * want to keep them so restore them afterwards.
- *
- * The restart process will later pick them up and
- * re-configure the hw when we reconfigure the BT
- * command.
- */
- bt_full_concurrent = priv->bt_full_concurrent;
- bt_ci_compliance = priv->bt_ci_compliance;
- bt_load = priv->bt_traffic_load;
- bt_status = priv->bt_status;
- bt_is_sco = priv->bt_is_sco;
-
- iwl_down(priv);
-
- priv->bt_full_concurrent = bt_full_concurrent;
- priv->bt_ci_compliance = bt_ci_compliance;
- priv->bt_traffic_load = bt_load;
- priv->bt_status = bt_status;
- priv->bt_is_sco = bt_is_sco;
-
- /* reset aggregation queues */
- for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++)
- priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
- /* and stop counts */
- for (i = 0; i < IWL_MAX_HW_QUEUES; i++)
- atomic_set(&priv->queue_stop_count[i], 0);
-
- memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc));
-}
-
-static void iwl_bg_restart(struct work_struct *data)
-{
- struct iwl_priv *priv = container_of(data, struct iwl_priv, restart);
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
- mutex_lock(&priv->mutex);
- iwlagn_prepare_restart(priv);
- mutex_unlock(&priv->mutex);
- iwl_cancel_deferred_work(priv);
- if (priv->mac80211_registered)
- ieee80211_restart_hw(priv->hw);
- else
- IWL_ERR(priv,
- "Cannot request restart before registrating with mac80211\n");
- } else {
- WARN_ON(1);
- }
-}
-
-/*****************************************************************************
- *
- * driver setup and teardown
- *
- *****************************************************************************/
-
-static void iwl_setup_deferred_work(struct iwl_priv *priv)
-{
- priv->workqueue = create_singlethread_workqueue(DRV_NAME);
-
- INIT_WORK(&priv->restart, iwl_bg_restart);
- INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
- INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
- INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
- INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
- INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
-
- iwl_setup_scan_deferred_work(priv);
-
- if (priv->lib->bt_params)
- iwlagn_bt_setup_deferred_work(priv);
-
- setup_timer(&priv->statistics_periodic, iwl_bg_statistics_periodic,
- (unsigned long)priv);
-
- setup_timer(&priv->ucode_trace, iwl_bg_ucode_trace,
- (unsigned long)priv);
-}
-
-void iwl_cancel_deferred_work(struct iwl_priv *priv)
-{
- if (priv->lib->bt_params)
- iwlagn_bt_cancel_deferred_work(priv);
-
- cancel_work_sync(&priv->run_time_calib_work);
- cancel_work_sync(&priv->beacon_update);
-
- iwl_cancel_scan_deferred_work(priv);
-
- cancel_work_sync(&priv->bt_full_concurrency);
- cancel_work_sync(&priv->bt_runtime_config);
-
- del_timer_sync(&priv->statistics_periodic);
- del_timer_sync(&priv->ucode_trace);
-}
-
-static int iwl_init_drv(struct iwl_priv *priv)
-{
- spin_lock_init(&priv->sta_lock);
-
- mutex_init(&priv->mutex);
-
- INIT_LIST_HEAD(&priv->calib_results);
-
- priv->band = IEEE80211_BAND_2GHZ;
-
- priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold;
-
- priv->iw_mode = NL80211_IFTYPE_STATION;
- priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
- priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
- priv->agg_tids_count = 0;
-
- priv->rx_statistics_jiffies = jiffies;
-
- /* Choose which receivers/antennas to use */
- iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]);
-
- iwl_init_scan_params(priv);
-
- /* init bt coex */
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist) {
- priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
- priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
- priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
- priv->bt_on_thresh = BT_ON_THRESHOLD_DEF;
- priv->bt_duration = BT_DURATION_LIMIT_DEF;
- priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF;
- }
-
- return 0;
-}
-
-static void iwl_uninit_drv(struct iwl_priv *priv)
-{
- kfree(priv->scan_cmd);
- kfree(priv->beacon_cmd);
- kfree(rcu_dereference_raw(priv->noa_data));
- iwl_calib_free_results(priv);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- kfree(priv->wowlan_sram);
-#endif
-}
-
-static void iwl_set_hw_params(struct iwl_priv *priv)
-{
- if (priv->cfg->ht_params)
- priv->hw_params.use_rts_for_aggregation =
- priv->cfg->ht_params->use_rts_for_aggregation;
-
- /* Device-specific setup */
- priv->lib->set_hw_params(priv);
-}
-
-
-
-/* show what optional capabilities we have */
-static void iwl_option_config(struct iwl_priv *priv)
-{
-#ifdef CONFIG_IWLWIFI_DEBUG
- IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n");
-#else
- IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n");
-#endif
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n");
-#else
- IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n");
-#endif
-
-#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
- IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n");
-#else
- IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n");
-#endif
-}
-
-static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
-{
- struct iwl_nvm_data *data = priv->nvm_data;
-
- if (data->sku_cap_11n_enable &&
- !priv->cfg->ht_params) {
- IWL_ERR(priv, "Invalid 11n configuration\n");
- return -EINVAL;
- }
-
- if (!data->sku_cap_11n_enable && !data->sku_cap_band_24GHz_enable &&
- !data->sku_cap_band_52GHz_enable) {
- IWL_ERR(priv, "Invalid device sku\n");
- return -EINVAL;
- }
-
- IWL_DEBUG_INFO(priv,
- "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n",
- data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled",
- data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled",
- data->sku_cap_11n_enable ? "" : "NOT", "enabled");
-
- priv->hw_params.tx_chains_num =
- num_of_ant(data->valid_tx_ant);
- if (priv->cfg->rx_with_siso_diversity)
- priv->hw_params.rx_chains_num = 1;
- else
- priv->hw_params.rx_chains_num =
- num_of_ant(data->valid_rx_ant);
-
- IWL_DEBUG_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n",
- data->valid_tx_ant,
- data->valid_rx_ant);
-
- return 0;
-}
-
-static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
- const struct iwl_cfg *cfg,
- const struct iwl_fw *fw,
- struct dentry *dbgfs_dir)
-{
- struct iwl_priv *priv;
- struct ieee80211_hw *hw;
- struct iwl_op_mode *op_mode;
- u16 num_mac;
- u32 ucode_flags;
- struct iwl_trans_config trans_cfg = {};
- static const u8 no_reclaim_cmds[] = {
- REPLY_RX_PHY_CMD,
- REPLY_RX_MPDU_CMD,
- REPLY_COMPRESSED_BA,
- STATISTICS_NOTIFICATION,
- REPLY_TX,
- };
- int i;
-
- /************************
- * 1. Allocating HW data
- ************************/
- hw = iwl_alloc_all();
- if (!hw) {
- pr_err("%s: Cannot allocate network device\n", cfg->name);
- goto out;
- }
-
- op_mode = hw->priv;
- op_mode->ops = &iwl_dvm_ops;
- priv = IWL_OP_MODE_GET_DVM(op_mode);
- priv->trans = trans;
- priv->dev = trans->dev;
- priv->cfg = cfg;
- priv->fw = fw;
-
- switch (priv->cfg->device_family) {
- case IWL_DEVICE_FAMILY_1000:
- case IWL_DEVICE_FAMILY_100:
- priv->lib = &iwl_dvm_1000_cfg;
- break;
- case IWL_DEVICE_FAMILY_2000:
- priv->lib = &iwl_dvm_2000_cfg;
- break;
- case IWL_DEVICE_FAMILY_105:
- priv->lib = &iwl_dvm_105_cfg;
- break;
- case IWL_DEVICE_FAMILY_2030:
- case IWL_DEVICE_FAMILY_135:
- priv->lib = &iwl_dvm_2030_cfg;
- break;
- case IWL_DEVICE_FAMILY_5000:
- priv->lib = &iwl_dvm_5000_cfg;
- break;
- case IWL_DEVICE_FAMILY_5150:
- priv->lib = &iwl_dvm_5150_cfg;
- break;
- case IWL_DEVICE_FAMILY_6000:
- case IWL_DEVICE_FAMILY_6000i:
- priv->lib = &iwl_dvm_6000_cfg;
- break;
- case IWL_DEVICE_FAMILY_6005:
- priv->lib = &iwl_dvm_6005_cfg;
- break;
- case IWL_DEVICE_FAMILY_6050:
- case IWL_DEVICE_FAMILY_6150:
- priv->lib = &iwl_dvm_6050_cfg;
- break;
- case IWL_DEVICE_FAMILY_6030:
- priv->lib = &iwl_dvm_6030_cfg;
- break;
- default:
- break;
- }
-
- if (WARN_ON(!priv->lib))
- goto out_free_hw;
-
- /*
- * Populate the state variables that the transport layer needs
- * to know about.
- */
- trans_cfg.op_mode = op_mode;
- trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
- trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
- trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
- trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED;
-
- trans_cfg.command_names = iwl_dvm_cmd_strings;
- trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM;
-
- WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE <
- priv->cfg->base_params->num_of_queues);
-
- ucode_flags = fw->ucode_capa.flags;
-
- if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
- priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
- trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
- } else {
- priv->sta_key_max_num = STA_KEY_MAX_NUM;
- trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
- }
-
- /* Configure transport layer */
- iwl_trans_configure(priv->trans, &trans_cfg);
-
- trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
- trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
-
- /* At this point both hw and priv are allocated. */
-
- SET_IEEE80211_DEV(priv->hw, priv->trans->dev);
-
- iwl_option_config(priv);
-
- IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
-
- /* is antenna coupling more than 35dB ? */
- priv->bt_ant_couple_ok =
- (iwlwifi_mod_params.ant_coupling >
- IWL_BT_ANTENNA_COUPLING_THRESHOLD) ?
- true : false;
-
- /* bt channel inhibition enabled*/
- priv->bt_ch_announce = true;
- IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n",
- (priv->bt_ch_announce) ? "On" : "Off");
-
- /* these spin locks will be used in apm_ops.init and EEPROM access
- * we should init now
- */
- spin_lock_init(&priv->statistics.lock);
-
- /***********************
- * 2. Read REV register
- ***********************/
- IWL_INFO(priv, "Detected %s, REV=0x%X\n",
- priv->cfg->name, priv->trans->hw_rev);
-
- if (iwl_trans_start_hw(priv->trans))
- goto out_free_hw;
-
- /* Read the EEPROM */
- if (iwl_read_eeprom(priv->trans, &priv->eeprom_blob,
- &priv->eeprom_blob_size)) {
- IWL_ERR(priv, "Unable to init EEPROM\n");
- goto out_free_hw;
- }
-
- /* Reset chip to save power until we load uCode during "up". */
- iwl_trans_stop_device(priv->trans);
-
- priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg,
- priv->eeprom_blob,
- priv->eeprom_blob_size);
- if (!priv->nvm_data)
- goto out_free_eeprom_blob;
-
- if (iwl_nvm_check_version(priv->nvm_data, priv->trans))
- goto out_free_eeprom;
-
- if (iwl_eeprom_init_hw_params(priv))
- goto out_free_eeprom;
-
- /* extract MAC Address */
- memcpy(priv->addresses[0].addr, priv->nvm_data->hw_addr, ETH_ALEN);
- IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr);
- priv->hw->wiphy->addresses = priv->addresses;
- priv->hw->wiphy->n_addresses = 1;
- num_mac = priv->nvm_data->n_hw_addrs;
- if (num_mac > 1) {
- memcpy(priv->addresses[1].addr, priv->addresses[0].addr,
- ETH_ALEN);
- priv->addresses[1].addr[5]++;
- priv->hw->wiphy->n_addresses++;
- }
-
- /************************
- * 4. Setup HW constants
- ************************/
- iwl_set_hw_params(priv);
-
- if (!(priv->nvm_data->sku_cap_ipan_enable)) {
- IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n");
- ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN;
- /*
- * if not PAN, then don't support P2P -- might be a uCode
- * packaging bug or due to the eeprom check above
- */
- priv->sta_key_max_num = STA_KEY_MAX_NUM;
- trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
-
- /* Configure transport layer again*/
- iwl_trans_configure(priv->trans, &trans_cfg);
- }
-
- /*******************
- * 5. Setup priv
- *******************/
- for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
- priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
- if (i < IWLAGN_FIRST_AMPDU_QUEUE &&
- i != IWL_DEFAULT_CMD_QUEUE_NUM &&
- i != IWL_IPAN_CMD_QUEUE_NUM)
- priv->queue_to_mac80211[i] = i;
- atomic_set(&priv->queue_stop_count[i], 0);
- }
-
- if (iwl_init_drv(priv))
- goto out_free_eeprom;
-
- /* At this point both hw and priv are initialized. */
-
- /********************
- * 6. Setup services
- ********************/
- iwl_setup_deferred_work(priv);
- iwl_setup_rx_handlers(priv);
-
- iwl_power_initialize(priv);
- iwl_tt_initialize(priv);
-
- snprintf(priv->hw->wiphy->fw_version,
- sizeof(priv->hw->wiphy->fw_version),
- "%s", fw->fw_version);
-
- priv->new_scan_threshold_behaviour =
- !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN);
-
- priv->phy_calib_chain_noise_reset_cmd =
- fw->ucode_capa.standard_phy_calibration_size;
- priv->phy_calib_chain_noise_gain_cmd =
- fw->ucode_capa.standard_phy_calibration_size + 1;
-
- /* initialize all valid contexts */
- iwl_init_context(priv, ucode_flags);
-
- /**************************************************
- * This is still part of probe() in a sense...
- *
- * 7. Setup and register with mac80211 and debugfs
- **************************************************/
- if (iwlagn_mac_setup_register(priv, &fw->ucode_capa))
- goto out_destroy_workqueue;
-
- if (iwl_dbgfs_register(priv, dbgfs_dir))
- goto out_mac80211_unregister;
-
- return op_mode;
-
-out_mac80211_unregister:
- iwlagn_mac_unregister(priv);
-out_destroy_workqueue:
- iwl_tt_exit(priv);
- iwl_cancel_deferred_work(priv);
- destroy_workqueue(priv->workqueue);
- priv->workqueue = NULL;
- iwl_uninit_drv(priv);
-out_free_eeprom_blob:
- kfree(priv->eeprom_blob);
-out_free_eeprom:
- iwl_free_nvm_data(priv->nvm_data);
-out_free_hw:
- ieee80211_free_hw(priv->hw);
-out:
- op_mode = NULL;
- return op_mode;
-}
-
-static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n");
-
- iwlagn_mac_unregister(priv);
-
- iwl_tt_exit(priv);
-
- kfree(priv->eeprom_blob);
- iwl_free_nvm_data(priv->nvm_data);
-
- /*netif_stop_queue(dev); */
- flush_workqueue(priv->workqueue);
-
- /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes
- * priv->workqueue... so we can't take down the workqueue
- * until now... */
- destroy_workqueue(priv->workqueue);
- priv->workqueue = NULL;
-
- iwl_uninit_drv(priv);
-
- dev_kfree_skb(priv->beacon_skb);
-
- iwl_trans_op_mode_leave(priv->trans);
- ieee80211_free_hw(priv->hw);
-}
-
-static const char * const desc_lookup_text[] = {
- "OK",
- "FAIL",
- "BAD_PARAM",
- "BAD_CHECKSUM",
- "NMI_INTERRUPT_WDG",
- "SYSASSERT",
- "FATAL_ERROR",
- "BAD_COMMAND",
- "HW_ERROR_TUNE_LOCK",
- "HW_ERROR_TEMPERATURE",
- "ILLEGAL_CHAN_FREQ",
- "VCC_NOT_STABLE",
- "FH_ERROR",
- "NMI_INTERRUPT_HOST",
- "NMI_INTERRUPT_ACTION_PT",
- "NMI_INTERRUPT_UNKNOWN",
- "UCODE_VERSION_MISMATCH",
- "HW_ERROR_ABS_LOCK",
- "HW_ERROR_CAL_LOCK_FAIL",
- "NMI_INTERRUPT_INST_ACTION_PT",
- "NMI_INTERRUPT_DATA_ACTION_PT",
- "NMI_TRM_HW_ER",
- "NMI_INTERRUPT_TRM",
- "NMI_INTERRUPT_BREAK_POINT",
- "DEBUG_0",
- "DEBUG_1",
- "DEBUG_2",
- "DEBUG_3",
-};
-
-static struct { char *name; u8 num; } advanced_lookup[] = {
- { "NMI_INTERRUPT_WDG", 0x34 },
- { "SYSASSERT", 0x35 },
- { "UCODE_VERSION_MISMATCH", 0x37 },
- { "BAD_COMMAND", 0x38 },
- { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
- { "FATAL_ERROR", 0x3D },
- { "NMI_TRM_HW_ERR", 0x46 },
- { "NMI_INTERRUPT_TRM", 0x4C },
- { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
- { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
- { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
- { "NMI_INTERRUPT_HOST", 0x66 },
- { "NMI_INTERRUPT_ACTION_PT", 0x7C },
- { "NMI_INTERRUPT_UNKNOWN", 0x84 },
- { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
- { "ADVANCED_SYSASSERT", 0 },
-};
-
-static const char *desc_lookup(u32 num)
-{
- int i;
- int max = ARRAY_SIZE(desc_lookup_text);
-
- if (num < max)
- return desc_lookup_text[num];
-
- max = ARRAY_SIZE(advanced_lookup) - 1;
- for (i = 0; i < max; i++) {
- if (advanced_lookup[i].num == num)
- break;
- }
- return advanced_lookup[i].name;
-}
-
-#define ERROR_START_OFFSET (1 * sizeof(u32))
-#define ERROR_ELEM_SIZE (7 * sizeof(u32))
-
-static void iwl_dump_nic_error_log(struct iwl_priv *priv)
-{
- struct iwl_trans *trans = priv->trans;
- u32 base;
- struct iwl_error_event_table table;
-
- base = priv->device_pointers.error_event_table;
- if (priv->cur_ucode == IWL_UCODE_INIT) {
- if (!base)
- base = priv->fw->init_errlog_ptr;
- } else {
- if (!base)
- base = priv->fw->inst_errlog_ptr;
- }
-
- if (!iwlagn_hw_valid_rtc_data_addr(base)) {
- IWL_ERR(priv,
- "Not valid error log pointer 0x%08X for %s uCode\n",
- base,
- (priv->cur_ucode == IWL_UCODE_INIT)
- ? "Init" : "RT");
- return;
- }
-
- /*TODO: Update dbgfs with ISR error stats obtained below */
- iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
-
- if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
- IWL_ERR(trans, "Start IWL Error Log Dump:\n");
- IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
- priv->status, table.valid);
- }
-
- trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
- table.data1, table.data2, table.line,
- table.blink1, table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.ucode_ver,
- table.hw_ver, 0, table.brd_ver);
- IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
- desc_lookup(table.error_id));
- IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
- IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1);
- IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2);
- IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1);
- IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2);
- IWL_ERR(priv, "0x%08X | data1\n", table.data1);
- IWL_ERR(priv, "0x%08X | data2\n", table.data2);
- IWL_ERR(priv, "0x%08X | line\n", table.line);
- IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time);
- IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low);
- IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi);
- IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1);
- IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3);
- IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver);
- IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver);
- IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver);
- IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd);
- IWL_ERR(priv, "0x%08X | isr0\n", table.isr0);
- IWL_ERR(priv, "0x%08X | isr1\n", table.isr1);
- IWL_ERR(priv, "0x%08X | isr2\n", table.isr2);
- IWL_ERR(priv, "0x%08X | isr3\n", table.isr3);
- IWL_ERR(priv, "0x%08X | isr4\n", table.isr4);
- IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref);
- IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event);
- IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control);
- IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration);
- IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
- IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
- IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
- IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp);
- IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler);
-}
-
-#define EVENT_START_OFFSET (4 * sizeof(u32))
-
-/**
- * iwl_print_event_log - Dump error event log to syslog
- *
- */
-static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
- u32 num_events, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- u32 i;
- u32 base; /* SRAM byte address of event log header */
- u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
- u32 ptr; /* SRAM byte address of log data */
- u32 ev, time, data; /* event log data */
- unsigned long reg_flags;
-
- struct iwl_trans *trans = priv->trans;
-
- if (num_events == 0)
- return pos;
-
- base = priv->device_pointers.log_event_table;
- if (priv->cur_ucode == IWL_UCODE_INIT) {
- if (!base)
- base = priv->fw->init_evtlog_ptr;
- } else {
- if (!base)
- base = priv->fw->inst_evtlog_ptr;
- }
-
- if (mode == 0)
- event_size = 2 * sizeof(u32);
- else
- event_size = 3 * sizeof(u32);
-
- ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
-
- /* Make sure device is powered up for SRAM reads */
- if (!iwl_trans_grab_nic_access(trans, false, &reg_flags))
- return pos;
-
- /* Set starting address; reads will auto-increment */
- iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
-
- /* "time" is actually "data" for mode 0 (no timestamp).
- * place event id # at far right for easier visual parsing. */
- for (i = 0; i < num_events; i++) {
- ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
- time = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
- if (mode == 0) {
- /* data, ev */
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- } else {
- trace_iwlwifi_dev_ucode_event(trans->dev, 0,
- time, ev);
- IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- }
- } else {
- data = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- } else {
- IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- trace_iwlwifi_dev_ucode_event(trans->dev, time,
- data, ev);
- }
- }
- }
-
- /* Allow device to power down */
- iwl_trans_release_nic_access(trans, &reg_flags);
- return pos;
-}
-
-/**
- * iwl_print_last_event_logs - Dump the newest # of event log to syslog
- */
-static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
- u32 num_wraps, u32 next_entry,
- u32 size, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- /*
- * display the newest DEFAULT_LOG_ENTRIES entries
- * i.e the entries just before the next ont that uCode would fill.
- */
- if (num_wraps) {
- if (next_entry < size) {
- pos = iwl_print_event_log(priv,
- capacity - (size - next_entry),
- size - next_entry, mode,
- pos, buf, bufsz);
- pos = iwl_print_event_log(priv, 0,
- next_entry, mode,
- pos, buf, bufsz);
- } else
- pos = iwl_print_event_log(priv, next_entry - size,
- size, mode, pos, buf, bufsz);
- } else {
- if (next_entry < size) {
- pos = iwl_print_event_log(priv, 0, next_entry,
- mode, pos, buf, bufsz);
- } else {
- pos = iwl_print_event_log(priv, next_entry - size,
- size, mode, pos, buf, bufsz);
- }
- }
- return pos;
-}
-
-#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
-
-int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
- char **buf)
-{
- u32 base; /* SRAM byte address of event log header */
- u32 capacity; /* event log capacity in # entries */
- u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
- u32 num_wraps; /* # times uCode wrapped to top of log */
- u32 next_entry; /* index of next entry to be written by uCode */
- u32 size; /* # entries that we'll print */
- u32 logsize;
- int pos = 0;
- size_t bufsz = 0;
- struct iwl_trans *trans = priv->trans;
-
- base = priv->device_pointers.log_event_table;
- if (priv->cur_ucode == IWL_UCODE_INIT) {
- logsize = priv->fw->init_evtlog_size;
- if (!base)
- base = priv->fw->init_evtlog_ptr;
- } else {
- logsize = priv->fw->inst_evtlog_size;
- if (!base)
- base = priv->fw->inst_evtlog_ptr;
- }
-
- if (!iwlagn_hw_valid_rtc_data_addr(base)) {
- IWL_ERR(priv,
- "Invalid event log pointer 0x%08X for %s uCode\n",
- base,
- (priv->cur_ucode == IWL_UCODE_INIT)
- ? "Init" : "RT");
- return -EINVAL;
- }
-
- /* event log header */
- capacity = iwl_trans_read_mem32(trans, base);
- mode = iwl_trans_read_mem32(trans, base + (1 * sizeof(u32)));
- num_wraps = iwl_trans_read_mem32(trans, base + (2 * sizeof(u32)));
- next_entry = iwl_trans_read_mem32(trans, base + (3 * sizeof(u32)));
-
- if (capacity > logsize) {
- IWL_ERR(priv, "Log capacity %d is bogus, limit to %d "
- "entries\n", capacity, logsize);
- capacity = logsize;
- }
-
- if (next_entry > logsize) {
- IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
- next_entry, logsize);
- next_entry = logsize;
- }
-
- size = num_wraps ? capacity : next_entry;
-
- /* bail out if nothing in log */
- if (size == 0) {
- IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n");
- return pos;
- }
-
- if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log)
- size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
- IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
- size);
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (buf) {
- if (full_log)
- bufsz = capacity * 48;
- else
- bufsz = size * 48;
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
- }
- if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) {
- /*
- * if uCode has wrapped back to top of log,
- * start at the oldest entry,
- * i.e the next one that uCode would fill.
- */
- if (num_wraps)
- pos = iwl_print_event_log(priv, next_entry,
- capacity - next_entry, mode,
- pos, buf, bufsz);
- /* (then/else) start at top of log */
- pos = iwl_print_event_log(priv, 0,
- next_entry, mode, pos, buf, bufsz);
- } else
- pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#else
- pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#endif
- return pos;
-}
-
-static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
-{
- unsigned int reload_msec;
- unsigned long reload_jiffies;
-
- if (iwl_have_debug_level(IWL_DL_FW_ERRORS))
- iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS);
-
- /* uCode is no longer loaded. */
- priv->ucode_loaded = false;
-
- /* Set the FW error flag -- cleared on iwl_down */
- set_bit(STATUS_FW_ERROR, &priv->status);
-
- iwl_abort_notification_waits(&priv->notif_wait);
-
- /* Keep the restart process from trying to send host
- * commands by clearing the ready bit */
- clear_bit(STATUS_READY, &priv->status);
-
- if (!ondemand) {
- /*
- * If firmware keep reloading, then it indicate something
- * serious wrong and firmware having problem to recover
- * from it. Instead of keep trying which will fill the syslog
- * and hang the system, let's just stop it
- */
- reload_jiffies = jiffies;
- reload_msec = jiffies_to_msecs((long) reload_jiffies -
- (long) priv->reload_jiffies);
- priv->reload_jiffies = reload_jiffies;
- if (reload_msec <= IWL_MIN_RELOAD_DURATION) {
- priv->reload_count++;
- if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) {
- IWL_ERR(priv, "BUG_ON, Stop restarting\n");
- return;
- }
- } else
- priv->reload_count = 0;
- }
-
- if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) {
- if (iwlwifi_mod_params.restart_fw) {
- IWL_DEBUG_FW_ERRORS(priv,
- "Restarting adapter due to uCode error.\n");
- queue_work(priv->workqueue, &priv->restart);
- } else
- IWL_DEBUG_FW_ERRORS(priv,
- "Detected FW error, but not restarting\n");
- }
-}
-
-static void iwl_nic_error(struct iwl_op_mode *op_mode)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- IWL_ERR(priv, "Loaded firmware version: %s\n",
- priv->fw->fw_version);
-
- iwl_dump_nic_error_log(priv);
- iwl_dump_nic_event_log(priv, false, NULL);
-
- iwlagn_fw_error(priv, false);
-}
-
-static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- if (!iwl_check_for_ct_kill(priv)) {
- IWL_ERR(priv, "Restarting adapter queue is full\n");
- iwlagn_fw_error(priv, false);
- }
-}
-
-#define EEPROM_RF_CONFIG_TYPE_MAX 0x3
-
-static void iwl_nic_config(struct iwl_op_mode *op_mode)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- /* SKU Control */
- iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
- CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP,
- (CSR_HW_REV_STEP(priv->trans->hw_rev) <<
- CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) |
- (CSR_HW_REV_DASH(priv->trans->hw_rev) <<
- CSR_HW_IF_CONFIG_REG_POS_MAC_DASH));
-
- /* write radio config values to register */
- if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) {
- u32 reg_val =
- priv->nvm_data->radio_cfg_type <<
- CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE |
- priv->nvm_data->radio_cfg_step <<
- CSR_HW_IF_CONFIG_REG_POS_PHY_STEP |
- priv->nvm_data->radio_cfg_dash <<
- CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
-
- iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
- CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
- CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH,
- reg_val);
-
- IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n",
- priv->nvm_data->radio_cfg_type,
- priv->nvm_data->radio_cfg_step,
- priv->nvm_data->radio_cfg_dash);
- } else {
- WARN_ON(1);
- }
-
- /* set CSR_HW_CONFIG_REG for uCode use */
- iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
- CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
-
- /* W/A : NIC is stuck in a reset state after Early PCIe power off
- * (PCIe power is lost before PERST# is asserted),
- * causing ME FW to lose ownership and not being able to obtain it back.
- */
- iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
- ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
-
- if (priv->lib->nic_config)
- priv->lib->nic_config(priv);
-}
-
-static void iwl_wimax_active(struct iwl_op_mode *op_mode)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- clear_bit(STATUS_READY, &priv->status);
- IWL_ERR(priv, "RF is used by WiMAX\n");
-}
-
-static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
- int mq = priv->queue_to_mac80211[queue];
-
- if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
- return;
-
- if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) {
- IWL_DEBUG_TX_QUEUES(priv,
- "queue %d (mac80211 %d) already stopped\n",
- queue, mq);
- return;
- }
-
- set_bit(mq, &priv->transport_queue_stop);
- ieee80211_stop_queue(priv->hw, mq);
-}
-
-static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
- int mq = priv->queue_to_mac80211[queue];
-
- if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
- return;
-
- if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) {
- IWL_DEBUG_TX_QUEUES(priv,
- "queue %d (mac80211 %d) already awake\n",
- queue, mq);
- return;
- }
-
- clear_bit(mq, &priv->transport_queue_stop);
-
- if (!priv->passive_no_rx)
- ieee80211_wake_queue(priv->hw, mq);
-}
-
-void iwlagn_lift_passive_no_rx(struct iwl_priv *priv)
-{
- int mq;
-
- if (!priv->passive_no_rx)
- return;
-
- for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) {
- if (!test_bit(mq, &priv->transport_queue_stop)) {
- IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq);
- ieee80211_wake_queue(priv->hw, mq);
- } else {
- IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq);
- }
- }
-
- priv->passive_no_rx = false;
-}
-
-static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
- struct ieee80211_tx_info *info;
-
- info = IEEE80211_SKB_CB(skb);
- iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]);
- ieee80211_free_txskb(priv->hw, skb);
-}
-
-static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- if (state)
- set_bit(STATUS_RF_KILL_HW, &priv->status);
- else
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
-
- wiphy_rfkill_set_hw_state(priv->hw->wiphy, state);
-
- return false;
-}
-
-static const struct iwl_op_mode_ops iwl_dvm_ops = {
- .start = iwl_op_mode_dvm_start,
- .stop = iwl_op_mode_dvm_stop,
- .rx = iwl_rx_dispatch,
- .queue_full = iwl_stop_sw_queue,
- .queue_not_full = iwl_wake_sw_queue,
- .hw_rf_kill = iwl_set_hw_rfkill_state,
- .free_skb = iwl_free_skb,
- .nic_error = iwl_nic_error,
- .cmd_queue_full = iwl_cmd_queue_full,
- .nic_config = iwl_nic_config,
- .wimax_active = iwl_wimax_active,
-};
-
-/*****************************************************************************
- *
- * driver and module entry point
- *
- *****************************************************************************/
-static int __init iwl_init(void)
-{
-
- int ret;
-
- ret = iwlagn_rate_control_register();
- if (ret) {
- pr_err("Unable to register rate control algorithm: %d\n", ret);
- return ret;
- }
-
- ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops);
- if (ret) {
- pr_err("Unable to register op_mode: %d\n", ret);
- iwlagn_rate_control_unregister();
- }
-
- return ret;
-}
-module_init(iwl_init);
-
-static void __exit iwl_exit(void)
-{
- iwl_opmode_deregister("iwldvm");
- iwlagn_rate_control_unregister();
-}
-module_exit(iwl_exit);
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c
deleted file mode 100644
index 1513dbc79c14..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/power.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-#include "iwl-io.h"
-#include "iwl-debug.h"
-#include "iwl-trans.h"
-#include "iwl-modparams.h"
-#include "dev.h"
-#include "agn.h"
-#include "commands.h"
-#include "power.h"
-
-static bool force_cam = true;
-module_param(force_cam, bool, 0644);
-MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");
-
-/*
- * Setting power level allows the card to go to sleep when not busy.
- *
- * We calculate a sleep command based on the required latency, which
- * we get from mac80211. In order to handle thermal throttling, we can
- * also use pre-defined power levels.
- */
-
-/*
- * This defines the old power levels. They are still used by default
- * (level 1) and for thermal throttle (levels 3 through 5)
- */
-
-struct iwl_power_vec_entry {
- struct iwl_powertable_cmd cmd;
- u8 no_dtim; /* number of skip dtim */
-};
-
-#define IWL_DTIM_RANGE_0_MAX 2
-#define IWL_DTIM_RANGE_1_MAX 10
-
-#define NOSLP cpu_to_le16(0), 0, 0
-#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0
-#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \
- IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \
- IWL_POWER_ADVANCE_PM_ENA_MSK)
-#define ASLP_TOUT(T) cpu_to_le32(T)
-#define TU_TO_USEC 1024
-#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC)
-#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \
- cpu_to_le32(X1), \
- cpu_to_le32(X2), \
- cpu_to_le32(X3), \
- cpu_to_le32(X4)}
-/* default power management (not Tx power) table values */
-/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */
-/* DTIM 0 - 2 */
-static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = {
- {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0},
- {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1},
- {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2}
-};
-
-
-/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
-/* DTIM 3 - 10 */
-static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = {
- {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
- {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1},
- {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2}
-};
-
-/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
-/* DTIM 11 - */
-static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = {
- {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
- {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
- {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}
-};
-
-/* advance power management */
-/* DTIM 0 - 2 */
-static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = {
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
-};
-
-
-/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
-/* DTIM 3 - 10 */
-static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = {
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2}
-};
-
-/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
-/* DTIM 11 - */
-static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = {
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
- {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
- SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
-};
-
-static void iwl_static_sleep_cmd(struct iwl_priv *priv,
- struct iwl_powertable_cmd *cmd,
- enum iwl_power_level lvl, int period)
-{
- const struct iwl_power_vec_entry *table;
- int max_sleep[IWL_POWER_VEC_SIZE] = { 0 };
- int i;
- u8 skip;
- u32 slp_itrvl;
-
- if (priv->lib->adv_pm) {
- table = apm_range_2;
- if (period <= IWL_DTIM_RANGE_1_MAX)
- table = apm_range_1;
- if (period <= IWL_DTIM_RANGE_0_MAX)
- table = apm_range_0;
- } else {
- table = range_2;
- if (period <= IWL_DTIM_RANGE_1_MAX)
- table = range_1;
- if (period <= IWL_DTIM_RANGE_0_MAX)
- table = range_0;
- }
-
- if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM))
- memset(cmd, 0, sizeof(*cmd));
- else
- *cmd = table[lvl].cmd;
-
- if (period == 0) {
- skip = 0;
- period = 1;
- for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
- max_sleep[i] = 1;
-
- } else {
- skip = table[lvl].no_dtim;
- for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
- max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]);
- max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1;
- }
-
- slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
- /* figure out the listen interval based on dtim period and skip */
- if (slp_itrvl == 0xFF)
- cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
- cpu_to_le32(period * (skip + 1));
-
- slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
- if (slp_itrvl > period)
- cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
- cpu_to_le32((slp_itrvl / period) * period);
-
- if (skip)
- cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK;
- else
- cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
-
- if (priv->cfg->base_params->shadow_reg_enable)
- cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
- else
- cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
-
- if (iwl_advanced_bt_coexist(priv)) {
- if (!priv->lib->bt_params->bt_sco_disable)
- cmd->flags |= IWL_POWER_BT_SCO_ENA;
- else
- cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
- }
-
-
- slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
- if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL)
- cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
- cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL);
-
- /* enforce max sleep interval */
- for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) {
- if (le32_to_cpu(cmd->sleep_interval[i]) >
- (max_sleep[i] * period))
- cmd->sleep_interval[i] =
- cpu_to_le32(max_sleep[i] * period);
- if (i != (IWL_POWER_VEC_SIZE - 1)) {
- if (le32_to_cpu(cmd->sleep_interval[i]) >
- le32_to_cpu(cmd->sleep_interval[i+1]))
- cmd->sleep_interval[i] =
- cmd->sleep_interval[i+1];
- }
- }
-
- if (priv->power_data.bus_pm)
- cmd->flags |= IWL_POWER_PCI_PM_MSK;
- else
- cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
-
- IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n",
- skip, period);
- /* The power level here is 0-4 (used as array index), but user expects
- to see 1-5 (according to spec). */
- IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1);
-}
-
-static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv,
- struct iwl_powertable_cmd *cmd)
-{
- memset(cmd, 0, sizeof(*cmd));
-
- if (priv->power_data.bus_pm)
- cmd->flags |= IWL_POWER_PCI_PM_MSK;
-
- IWL_DEBUG_POWER(priv, "Sleep command for CAM\n");
-}
-
-static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
-{
- IWL_DEBUG_POWER(priv, "Sending power/sleep command\n");
- IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags);
- IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
- IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
- IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n",
- le32_to_cpu(cmd->sleep_interval[0]),
- le32_to_cpu(cmd->sleep_interval[1]),
- le32_to_cpu(cmd->sleep_interval[2]),
- le32_to_cpu(cmd->sleep_interval[3]),
- le32_to_cpu(cmd->sleep_interval[4]));
-
- return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0,
- sizeof(struct iwl_powertable_cmd), cmd);
-}
-
-static void iwl_power_build_cmd(struct iwl_priv *priv,
- struct iwl_powertable_cmd *cmd)
-{
- bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
- int dtimper;
-
- if (force_cam) {
- iwl_power_sleep_cam_cmd(priv, cmd);
- return;
- }
-
- dtimper = priv->hw->conf.ps_dtim_period ?: 1;
-
- if (priv->wowlan)
- iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
- else if (!priv->lib->no_idle_support &&
- priv->hw->conf.flags & IEEE80211_CONF_IDLE)
- iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
- else if (iwl_tt_is_low_power_state(priv)) {
- /* in thermal throttling low power state */
- iwl_static_sleep_cmd(priv, cmd,
- iwl_tt_current_power_mode(priv), dtimper);
- } else if (!enabled)
- iwl_power_sleep_cam_cmd(priv, cmd);
- else if (priv->power_data.debug_sleep_level_override >= 0)
- iwl_static_sleep_cmd(priv, cmd,
- priv->power_data.debug_sleep_level_override,
- dtimper);
- else {
- /* Note that the user parameter is 1-5 (according to spec),
- but we pass 0-4 because it acts as an array index. */
- if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 &&
- iwlwifi_mod_params.power_level <= IWL_POWER_NUM)
- iwl_static_sleep_cmd(priv, cmd,
- iwlwifi_mod_params.power_level - 1, dtimper);
- else
- iwl_static_sleep_cmd(priv, cmd,
- IWL_POWER_INDEX_1, dtimper);
- }
-}
-
-int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
- bool force)
-{
- int ret;
- bool update_chains;
-
- lockdep_assert_held(&priv->mutex);
-
- /* Don't update the RX chain when chain noise calibration is running */
- update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
- priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
-
- if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
- return 0;
-
- if (!iwl_is_ready_rf(priv))
- return -EIO;
-
- /* scan complete use sleep_power_next, need to be updated */
- memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
- if (test_bit(STATUS_SCANNING, &priv->status) && !force) {
- IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n");
- return 0;
- }
-
- if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
- iwl_dvm_set_pmi(priv, true);
-
- ret = iwl_set_power(priv, cmd);
- if (!ret) {
- if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
- iwl_dvm_set_pmi(priv, false);
-
- if (update_chains)
- iwl_update_chain_flags(priv);
- else
- IWL_DEBUG_POWER(priv,
- "Cannot update the power, chain noise "
- "calibration running: %d\n",
- priv->chain_noise_data.state);
-
- memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));
- } else
- IWL_ERR(priv, "set power fail, ret = %d\n", ret);
-
- return ret;
-}
-
-int iwl_power_update_mode(struct iwl_priv *priv, bool force)
-{
- struct iwl_powertable_cmd cmd;
-
- iwl_power_build_cmd(priv, &cmd);
- return iwl_power_set_mode(priv, &cmd, force);
-}
-
-/* initialize to default */
-void iwl_power_initialize(struct iwl_priv *priv)
-{
- priv->power_data.bus_pm = priv->trans->pm_support;
-
- priv->power_data.debug_sleep_level_override = -1;
-
- memset(&priv->power_data.sleep_cmd, 0,
- sizeof(priv->power_data.sleep_cmd));
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.h b/drivers/net/wireless/iwlwifi/dvm/power.h
deleted file mode 100644
index 570d3a5e4670..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/power.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-#ifndef __iwl_power_setting_h__
-#define __iwl_power_setting_h__
-
-#include "commands.h"
-
-struct iwl_power_mgr {
- struct iwl_powertable_cmd sleep_cmd;
- struct iwl_powertable_cmd sleep_cmd_next;
- int debug_sleep_level_override;
- bool bus_pm;
-};
-
-int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
- bool force);
-int iwl_power_update_mode(struct iwl_priv *priv, bool force);
-void iwl_power_initialize(struct iwl_priv *priv);
-
-extern bool no_sleep_autoadjust;
-
-#endif /* __iwl_power_setting_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
deleted file mode 100644
index cef921c1a623..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ /dev/null
@@ -1,3338 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-
-#include <linux/workqueue.h>
-
-#include "dev.h"
-#include "agn.h"
-
-#define RS_NAME "iwl-agn-rs"
-
-#define NUM_TRY_BEFORE_ANT_TOGGLE 1
-#define IWL_NUMBER_TRY 1
-#define IWL_HT_NUMBER_TRY 3
-
-#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */
-#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */
-#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */
-
-/* max allowed rate miss before sync LQ cmd */
-#define IWL_MISSED_RATE_MAX 15
-/* max time to accum history 2 seconds */
-#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ)
-
-static u8 rs_ht_to_legacy[] = {
- IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
- IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
- IWL_RATE_6M_INDEX,
- IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
- IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
- IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
- IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
-};
-
-static const u8 ant_toggle_lookup[] = {
- /*ANT_NONE -> */ ANT_NONE,
- /*ANT_A -> */ ANT_B,
- /*ANT_B -> */ ANT_C,
- /*ANT_AB -> */ ANT_BC,
- /*ANT_C -> */ ANT_A,
- /*ANT_AC -> */ ANT_AB,
- /*ANT_BC -> */ ANT_AC,
- /*ANT_ABC -> */ ANT_ABC,
-};
-
-#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \
- [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
- IWL_RATE_SISO_##s##M_PLCP, \
- IWL_RATE_MIMO2_##s##M_PLCP,\
- IWL_RATE_MIMO3_##s##M_PLCP,\
- IWL_RATE_##r##M_IEEE, \
- IWL_RATE_##ip##M_INDEX, \
- IWL_RATE_##in##M_INDEX, \
- IWL_RATE_##rp##M_INDEX, \
- IWL_RATE_##rn##M_INDEX, \
- IWL_RATE_##pp##M_INDEX, \
- IWL_RATE_##np##M_INDEX }
-
-/*
- * Parameter order:
- * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
- *
- * If there isn't a valid next or previous rate then INV is used which
- * maps to IWL_RATE_INVALID
- *
- */
-const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = {
- IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */
- IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */
- IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */
- IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */
- IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */
- IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */
- IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */
- IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */
- IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */
- IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */
- IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */
- IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
- IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
- /* FIXME:RS: ^^ should be INV (legacy) */
-};
-
-static inline u8 rs_extract_rate(u32 rate_n_flags)
-{
- return (u8)(rate_n_flags & RATE_MCS_RATE_MSK);
-}
-
-static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
-{
- int idx = 0;
-
- /* HT rate format */
- if (rate_n_flags & RATE_MCS_HT_MSK) {
- idx = rs_extract_rate(rate_n_flags);
-
- if (idx >= IWL_RATE_MIMO3_6M_PLCP)
- idx = idx - IWL_RATE_MIMO3_6M_PLCP;
- else if (idx >= IWL_RATE_MIMO2_6M_PLCP)
- idx = idx - IWL_RATE_MIMO2_6M_PLCP;
-
- idx += IWL_FIRST_OFDM_RATE;
- /* skip 9M not supported in ht*/
- if (idx >= IWL_RATE_9M_INDEX)
- idx += 1;
- if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
- return idx;
-
- /* legacy rate format, search for match in table */
- } else {
- for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
- if (iwl_rates[idx].plcp ==
- rs_extract_rate(rate_n_flags))
- return idx;
- }
-
- return -1;
-}
-
-static void rs_rate_scale_perform(struct iwl_priv *priv,
- struct sk_buff *skb,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta);
-static void rs_fill_link_cmd(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
-static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
-
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
- u32 *rate_n_flags, int index);
-#else
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
- u32 *rate_n_flags, int index)
-{}
-#endif
-
-/**
- * The following tables contain the expected throughput metrics for all rates
- *
- * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
- *
- * where invalid entries are zeros.
- *
- * CCK rates are only valid in legacy table and will only be used in G
- * (2.4 GHz) band.
- */
-
-static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
- 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
-};
-
-static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */
- {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */
- {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */
- {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
-};
-
-static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
- {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
- {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
- {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
-};
-
-static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
- {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
- {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
- {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
-};
-
-static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
- {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
- {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
- {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
-};
-
-static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
- {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
- {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
- {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
-};
-
-static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */
- {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */
- {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */
- {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */
-};
-
-/* mbps, mcs */
-static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
- { "1", "BPSK DSSS"},
- { "2", "QPSK DSSS"},
- {"5.5", "BPSK CCK"},
- { "11", "QPSK CCK"},
- { "6", "BPSK 1/2"},
- { "9", "BPSK 1/2"},
- { "12", "QPSK 1/2"},
- { "18", "QPSK 3/4"},
- { "24", "16QAM 1/2"},
- { "36", "16QAM 3/4"},
- { "48", "64QAM 2/3"},
- { "54", "64QAM 3/4"},
- { "60", "64QAM 5/6"},
-};
-
-#define MCS_INDEX_PER_STREAM (8)
-
-static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
-{
- window->data = 0;
- window->success_counter = 0;
- window->success_ratio = IWL_INVALID_VALUE;
- window->counter = 0;
- window->average_tpt = IWL_INVALID_VALUE;
- window->stamp = 0;
-}
-
-static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
-{
- return (ant_type & valid_antenna) == ant_type;
-}
-
-/*
- * removes the old data from the statistics. All data that is older than
- * TID_MAX_TIME_DIFF, will be deleted.
- */
-static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time)
-{
- /* The oldest age we want to keep */
- u32 oldest_time = curr_time - TID_MAX_TIME_DIFF;
-
- while (tl->queue_count &&
- (tl->time_stamp < oldest_time)) {
- tl->total -= tl->packet_count[tl->head];
- tl->packet_count[tl->head] = 0;
- tl->time_stamp += TID_QUEUE_CELL_SPACING;
- tl->queue_count--;
- tl->head++;
- if (tl->head >= TID_QUEUE_MAX_SIZE)
- tl->head = 0;
- }
-}
-
-/*
- * increment traffic load value for tid and also remove
- * any old values if passed the certain time period
- */
-static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
- struct ieee80211_hdr *hdr)
-{
- u32 curr_time = jiffies_to_msecs(jiffies);
- u32 time_diff;
- s32 index;
- struct iwl_traffic_load *tl = NULL;
- u8 tid;
-
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- u8 *qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
- } else
- return IWL_MAX_TID_COUNT;
-
- if (unlikely(tid >= IWL_MAX_TID_COUNT))
- return IWL_MAX_TID_COUNT;
-
- tl = &lq_data->load[tid];
-
- curr_time -= curr_time % TID_ROUND_VALUE;
-
- /* Happens only for the first packet. Initialize the data */
- if (!(tl->queue_count)) {
- tl->total = 1;
- tl->time_stamp = curr_time;
- tl->queue_count = 1;
- tl->head = 0;
- tl->packet_count[0] = 1;
- return IWL_MAX_TID_COUNT;
- }
-
- time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
- index = time_diff / TID_QUEUE_CELL_SPACING;
-
- /* The history is too long: remove data that is older than */
- /* TID_MAX_TIME_DIFF */
- if (index >= TID_QUEUE_MAX_SIZE)
- rs_tl_rm_old_stats(tl, curr_time);
-
- index = (tl->head + index) % TID_QUEUE_MAX_SIZE;
- tl->packet_count[index] = tl->packet_count[index] + 1;
- tl->total = tl->total + 1;
-
- if ((index + 1) > tl->queue_count)
- tl->queue_count = index + 1;
-
- return tid;
-}
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-/**
- * Program the device to use fixed rate for frame transmit
- * This is for debugging/testing only
- * once the device start use fixed rate, we need to reload the module
- * to being back the normal operation.
- */
-static void rs_program_fix_rate(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta)
-{
- struct iwl_station_priv *sta_priv =
- container_of(lq_sta, struct iwl_station_priv, lq_sta);
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */
- lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
- lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
- lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
-
- IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n",
- lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
-
- if (lq_sta->dbg_fixed_rate) {
- rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
- iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC,
- false);
- }
-}
-#endif
-
-/*
- get the traffic load value for tid
-*/
-static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid)
-{
- u32 curr_time = jiffies_to_msecs(jiffies);
- u32 time_diff;
- s32 index;
- struct iwl_traffic_load *tl = NULL;
-
- if (tid >= IWL_MAX_TID_COUNT)
- return 0;
-
- tl = &(lq_data->load[tid]);
-
- curr_time -= curr_time % TID_ROUND_VALUE;
-
- if (!(tl->queue_count))
- return 0;
-
- time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
- index = time_diff / TID_QUEUE_CELL_SPACING;
-
- /* The history is too long: remove data that is older than */
- /* TID_MAX_TIME_DIFF */
- if (index >= TID_QUEUE_MAX_SIZE)
- rs_tl_rm_old_stats(tl, curr_time);
-
- return tl->total;
-}
-
-static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_data, u8 tid,
- struct ieee80211_sta *sta)
-{
- int ret = -EAGAIN;
- u32 load;
-
- /*
- * Don't create TX aggregation sessions when in high
- * BT traffic, as they would just be disrupted by BT.
- */
- if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) {
- IWL_DEBUG_COEX(priv,
- "BT traffic (%d), no aggregation allowed\n",
- priv->bt_traffic_load);
- return ret;
- }
-
- load = rs_tl_get_load(lq_data, tid);
-
- IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n",
- sta->addr, tid);
- ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
- if (ret == -EAGAIN) {
- /*
- * driver and mac80211 is out of sync
- * this might be cause by reloading firmware
- * stop the tx ba session here
- */
- IWL_ERR(priv, "Fail start Tx agg on tid: %d\n",
- tid);
- ieee80211_stop_tx_ba_session(sta, tid);
- }
- return ret;
-}
-
-static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid,
- struct iwl_lq_sta *lq_data,
- struct ieee80211_sta *sta)
-{
- if (tid < IWL_MAX_TID_COUNT)
- rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta);
- else
- IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n",
- tid, IWL_MAX_TID_COUNT);
-}
-
-static inline int get_num_of_ant_from_rate(u32 rate_n_flags)
-{
- return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) +
- !!(rate_n_flags & RATE_MCS_ANT_B_MSK) +
- !!(rate_n_flags & RATE_MCS_ANT_C_MSK);
-}
-
-/*
- * Static function to get the expected throughput from an iwl_scale_tbl_info
- * that wraps a NULL pointer check
- */
-static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
-{
- if (tbl->expected_tpt)
- return tbl->expected_tpt[rs_index];
- return 0;
-}
-
-/**
- * rs_collect_tx_data - Update the success/failure sliding window
- *
- * We keep a sliding window of the last 62 packets transmitted
- * at this rate. window->data contains the bitmask of successful
- * packets.
- */
-static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
- int scale_index, int attempts, int successes)
-{
- struct iwl_rate_scale_data *window = NULL;
- static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1));
- s32 fail_count, tpt;
-
- if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
- return -EINVAL;
-
- /* Select window for current tx bit rate */
- window = &(tbl->win[scale_index]);
-
- /* Get expected throughput */
- tpt = get_expected_tpt(tbl, scale_index);
-
- /*
- * Keep track of only the latest 62 tx frame attempts in this rate's
- * history window; anything older isn't really relevant any more.
- * If we have filled up the sliding window, drop the oldest attempt;
- * if the oldest attempt (highest bit in bitmap) shows "success",
- * subtract "1" from the success counter (this is the main reason
- * we keep these bitmaps!).
- */
- while (attempts > 0) {
- if (window->counter >= IWL_RATE_MAX_WINDOW) {
-
- /* remove earliest */
- window->counter = IWL_RATE_MAX_WINDOW - 1;
-
- if (window->data & mask) {
- window->data &= ~mask;
- window->success_counter--;
- }
- }
-
- /* Increment frames-attempted counter */
- window->counter++;
-
- /* Shift bitmap by one frame to throw away oldest history */
- window->data <<= 1;
-
- /* Mark the most recent #successes attempts as successful */
- if (successes > 0) {
- window->success_counter++;
- window->data |= 0x1;
- successes--;
- }
-
- attempts--;
- }
-
- /* Calculate current success ratio, avoid divide-by-0! */
- if (window->counter > 0)
- window->success_ratio = 128 * (100 * window->success_counter)
- / window->counter;
- else
- window->success_ratio = IWL_INVALID_VALUE;
-
- fail_count = window->counter - window->success_counter;
-
- /* Calculate average throughput, if we have enough history. */
- if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
- (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
- window->average_tpt = (window->success_ratio * tpt + 64) / 128;
- else
- window->average_tpt = IWL_INVALID_VALUE;
-
- /* Tag this window as having been updated */
- window->stamp = jiffies;
-
- return 0;
-}
-
-/*
- * Fill uCode API rate_n_flags field, based on "search" or "active" table.
- */
-/* FIXME:RS:remove this function and put the flags statically in the table */
-static u32 rate_n_flags_from_tbl(struct iwl_priv *priv,
- struct iwl_scale_tbl_info *tbl,
- int index, u8 use_green)
-{
- u32 rate_n_flags = 0;
-
- if (is_legacy(tbl->lq_type)) {
- rate_n_flags = iwl_rates[index].plcp;
- if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
- rate_n_flags |= RATE_MCS_CCK_MSK;
-
- } else if (is_Ht(tbl->lq_type)) {
- if (index > IWL_LAST_OFDM_RATE) {
- IWL_ERR(priv, "Invalid HT rate index %d\n", index);
- index = IWL_LAST_OFDM_RATE;
- }
- rate_n_flags = RATE_MCS_HT_MSK;
-
- if (is_siso(tbl->lq_type))
- rate_n_flags |= iwl_rates[index].plcp_siso;
- else if (is_mimo2(tbl->lq_type))
- rate_n_flags |= iwl_rates[index].plcp_mimo2;
- else
- rate_n_flags |= iwl_rates[index].plcp_mimo3;
- } else {
- IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type);
- }
-
- rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
- RATE_MCS_ANT_ABC_MSK);
-
- if (is_Ht(tbl->lq_type)) {
- if (tbl->is_ht40) {
- if (tbl->is_dup)
- rate_n_flags |= RATE_MCS_DUP_MSK;
- else
- rate_n_flags |= RATE_MCS_HT40_MSK;
- }
- if (tbl->is_SGI)
- rate_n_flags |= RATE_MCS_SGI_MSK;
-
- if (use_green) {
- rate_n_flags |= RATE_MCS_GF_MSK;
- if (is_siso(tbl->lq_type) && tbl->is_SGI) {
- rate_n_flags &= ~RATE_MCS_SGI_MSK;
- IWL_ERR(priv, "GF was set with SGI:SISO\n");
- }
- }
- }
- return rate_n_flags;
-}
-
-/*
- * Interpret uCode API's rate_n_flags format,
- * fill "search" or "active" tx mode table.
- */
-static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
- enum ieee80211_band band,
- struct iwl_scale_tbl_info *tbl,
- int *rate_idx)
-{
- u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK);
- u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
- u8 mcs;
-
- memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
- *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
-
- if (*rate_idx == IWL_RATE_INVALID) {
- *rate_idx = -1;
- return -EINVAL;
- }
- tbl->is_SGI = 0; /* default legacy setup */
- tbl->is_ht40 = 0;
- tbl->is_dup = 0;
- tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS);
- tbl->lq_type = LQ_NONE;
- tbl->max_search = IWL_MAX_SEARCH;
-
- /* legacy rate format */
- if (!(rate_n_flags & RATE_MCS_HT_MSK)) {
- if (num_of_ant == 1) {
- if (band == IEEE80211_BAND_5GHZ)
- tbl->lq_type = LQ_A;
- else
- tbl->lq_type = LQ_G;
- }
- /* HT rate format */
- } else {
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- tbl->is_SGI = 1;
-
- if ((rate_n_flags & RATE_MCS_HT40_MSK) ||
- (rate_n_flags & RATE_MCS_DUP_MSK))
- tbl->is_ht40 = 1;
-
- if (rate_n_flags & RATE_MCS_DUP_MSK)
- tbl->is_dup = 1;
-
- mcs = rs_extract_rate(rate_n_flags);
-
- /* SISO */
- if (mcs <= IWL_RATE_SISO_60M_PLCP) {
- if (num_of_ant == 1)
- tbl->lq_type = LQ_SISO; /*else NONE*/
- /* MIMO2 */
- } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
- if (num_of_ant == 2)
- tbl->lq_type = LQ_MIMO2;
- /* MIMO3 */
- } else {
- if (num_of_ant == 3) {
- tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
- tbl->lq_type = LQ_MIMO3;
- }
- }
- }
- return 0;
-}
-
-/* switch to another antenna/antennas and return 1 */
-/* if no other valid antenna found, return 0 */
-static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
- struct iwl_scale_tbl_info *tbl)
-{
- u8 new_ant_type;
-
- if (!tbl->ant_type || tbl->ant_type > ANT_ABC)
- return 0;
-
- if (!rs_is_valid_ant(valid_ant, tbl->ant_type))
- return 0;
-
- new_ant_type = ant_toggle_lookup[tbl->ant_type];
-
- while ((new_ant_type != tbl->ant_type) &&
- !rs_is_valid_ant(valid_ant, new_ant_type))
- new_ant_type = ant_toggle_lookup[new_ant_type];
-
- if (new_ant_type == tbl->ant_type)
- return 0;
-
- tbl->ant_type = new_ant_type;
- *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK;
- *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS;
- return 1;
-}
-
-/**
- * Green-field mode is valid if the station supports it and
- * there are no non-GF stations present in the BSS.
- */
-static bool rs_use_green(struct ieee80211_sta *sta)
-{
- /*
- * There's a bug somewhere in this code that causes the
- * scaling to get stuck because GF+SGI can't be combined
- * in SISO rates. Until we find that bug, disable GF, it
- * has only limited benefit and we still interoperate with
- * GF APs since we can always receive GF transmissions.
- */
- return false;
-}
-
-/**
- * rs_get_supported_rates - get the available rates
- *
- * if management frame or broadcast frame only return
- * basic available rates.
- *
- */
-static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
- struct ieee80211_hdr *hdr,
- enum iwl_table_type rate_type)
-{
- if (is_legacy(rate_type)) {
- return lq_sta->active_legacy_rate;
- } else {
- if (is_siso(rate_type))
- return lq_sta->active_siso_rate;
- else if (is_mimo2(rate_type))
- return lq_sta->active_mimo2_rate;
- else
- return lq_sta->active_mimo3_rate;
- }
-}
-
-static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask,
- int rate_type)
-{
- u8 high = IWL_RATE_INVALID;
- u8 low = IWL_RATE_INVALID;
-
- /* 802.11A or ht walks to the next literal adjacent rate in
- * the rate table */
- if (is_a_band(rate_type) || !is_legacy(rate_type)) {
- int i;
- u32 mask;
-
- /* Find the previous rate that is in the rate mask */
- i = index - 1;
- for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
- if (rate_mask & mask) {
- low = i;
- break;
- }
- }
-
- /* Find the next rate that is in the rate mask */
- i = index + 1;
- for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
- if (rate_mask & mask) {
- high = i;
- break;
- }
- }
-
- return (high << 8) | low;
- }
-
- low = index;
- while (low != IWL_RATE_INVALID) {
- low = iwl_rates[low].prev_rs;
- if (low == IWL_RATE_INVALID)
- break;
- if (rate_mask & (1 << low))
- break;
- IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low);
- }
-
- high = index;
- while (high != IWL_RATE_INVALID) {
- high = iwl_rates[high].next_rs;
- if (high == IWL_RATE_INVALID)
- break;
- if (rate_mask & (1 << high))
- break;
- IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high);
- }
-
- return (high << 8) | low;
-}
-
-static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl,
- u8 scale_index, u8 ht_possible)
-{
- s32 low;
- u16 rate_mask;
- u16 high_low;
- u8 switch_to_legacy = 0;
- u8 is_green = lq_sta->is_green;
- struct iwl_priv *priv = lq_sta->drv;
-
- /* check if we need to switch from HT to legacy rates.
- * assumption is that mandatory rates (1Mbps or 6Mbps)
- * are always supported (spec demand) */
- if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) {
- switch_to_legacy = 1;
- scale_index = rs_ht_to_legacy[scale_index];
- if (lq_sta->band == IEEE80211_BAND_5GHZ)
- tbl->lq_type = LQ_A;
- else
- tbl->lq_type = LQ_G;
-
- if (num_of_ant(tbl->ant_type) > 1)
- tbl->ant_type =
- first_antenna(priv->nvm_data->valid_tx_ant);
-
- tbl->is_ht40 = 0;
- tbl->is_SGI = 0;
- tbl->max_search = IWL_MAX_SEARCH;
- }
-
- rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type);
-
- /* Mask with station rate restriction */
- if (is_legacy(tbl->lq_type)) {
- /* supp_rates has no CCK bits in A mode */
- if (lq_sta->band == IEEE80211_BAND_5GHZ)
- rate_mask = (u16)(rate_mask &
- (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
- else
- rate_mask = (u16)(rate_mask & lq_sta->supp_rates);
- }
-
- /* If we switched from HT to legacy, check current rate */
- if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
- low = scale_index;
- goto out;
- }
-
- high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
- tbl->lq_type);
- low = high_low & 0xff;
-
- if (low == IWL_RATE_INVALID)
- low = scale_index;
-
-out:
- return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green);
-}
-
-/*
- * Simple function to compare two rate scale table types
- */
-static bool table_type_matches(struct iwl_scale_tbl_info *a,
- struct iwl_scale_tbl_info *b)
-{
- return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) &&
- (a->is_SGI == b->is_SGI);
-}
-
-static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct iwl_lq_sta *lq_sta)
-{
- struct iwl_scale_tbl_info *tbl;
- bool full_concurrent = priv->bt_full_concurrent;
-
- if (priv->bt_ant_couple_ok) {
- /*
- * Is there a need to switch between
- * full concurrency and 3-wire?
- */
- if (priv->bt_ci_compliance && priv->bt_ant_couple_ok)
- full_concurrent = true;
- else
- full_concurrent = false;
- }
- if ((priv->bt_traffic_load != priv->last_bt_traffic_load) ||
- (priv->bt_full_concurrent != full_concurrent)) {
- priv->bt_full_concurrent = full_concurrent;
- priv->last_bt_traffic_load = priv->bt_traffic_load;
-
- /* Update uCode's rate table. */
- tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
- iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
-
- queue_work(priv->workqueue, &priv->bt_full_concurrency);
- }
-}
-
-/*
- * mac80211 sends us Tx status
- */
-static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb)
-{
- int legacy_success;
- int retries;
- int rs_index, mac_index, i;
- struct iwl_lq_sta *lq_sta = priv_sta;
- struct iwl_link_quality_cmd *table;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r;
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- enum mac80211_rate_control_flags mac_flags;
- u32 tx_rate;
- struct iwl_scale_tbl_info tbl_type;
- struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
-
- /* Treat uninitialized rate scaling data same as non-existing. */
- if (!lq_sta) {
- IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n");
- return;
- } else if (!lq_sta->drv) {
- IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
- return;
- }
-
- if (!ieee80211_is_data(hdr->frame_control) ||
- info->flags & IEEE80211_TX_CTL_NO_ACK)
- return;
-
- /* This packet was aggregated but doesn't carry status info */
- if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
- !(info->flags & IEEE80211_TX_STAT_AMPDU))
- return;
-
- /*
- * Ignore this Tx frame response if its initial rate doesn't match
- * that of latest Link Quality command. There may be stragglers
- * from a previous Link Quality command, but we're no longer interested
- * in those; they're either from the "active" mode while we're trying
- * to check "search" mode, or a prior "search" mode after we've moved
- * to a new "search" mode (which might become the new "active" mode).
- */
- table = &lq_sta->lq;
- tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
- rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index);
- if (priv->band == IEEE80211_BAND_5GHZ)
- rs_index -= IWL_FIRST_OFDM_RATE;
- mac_flags = info->status.rates[0].flags;
- mac_index = info->status.rates[0].idx;
- /* For HT packets, map MCS to PLCP */
- if (mac_flags & IEEE80211_TX_RC_MCS) {
- mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */
- if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
- mac_index++;
- /*
- * mac80211 HT index is always zero-indexed; we need to move
- * HT OFDM rates after CCK rates in 2.4 GHz band
- */
- if (priv->band == IEEE80211_BAND_2GHZ)
- mac_index += IWL_FIRST_OFDM_RATE;
- }
- /* Here we actually compare this rate to the latest LQ command */
- if ((mac_index < 0) ||
- (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
- (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
- (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) ||
- (tbl_type.ant_type != info->status.antenna) ||
- (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
- (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
- (rs_index != mac_index)) {
- IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate);
- /*
- * Since rates mis-match, the last LQ command may have failed.
- * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
- * ... driver.
- */
- lq_sta->missed_rate_counter++;
- if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
- lq_sta->missed_rate_counter = 0;
- iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
- }
- /* Regardless, ignore this status info for outdated rate */
- return;
- } else
- /* Rate did match, so reset the missed_rate_counter */
- lq_sta->missed_rate_counter = 0;
-
- /* Figure out if rate scale algorithm is in active or search table */
- if (table_type_matches(&tbl_type,
- &(lq_sta->lq_info[lq_sta->active_tbl]))) {
- curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
- } else if (table_type_matches(&tbl_type,
- &lq_sta->lq_info[1 - lq_sta->active_tbl])) {
- curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
- other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- } else {
- IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n");
- tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n",
- tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI);
- tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
- IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n",
- tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI);
- IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n",
- tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI);
- /*
- * no matching table found, let's by-pass the data collection
- * and continue to perform rate scale to find the rate table
- */
- rs_stay_in_table(lq_sta, true);
- goto done;
- }
-
- /*
- * Updating the frame history depends on whether packets were
- * aggregated.
- *
- * For aggregation, all packets were transmitted at the same rate, the
- * first index into rate scale table.
- */
- if (info->flags & IEEE80211_TX_STAT_AMPDU) {
- tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
- rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type,
- &rs_index);
- rs_collect_tx_data(curr_tbl, rs_index,
- info->status.ampdu_len,
- info->status.ampdu_ack_len);
-
- /* Update success/fail counts if not searching for new mode */
- if (lq_sta->stay_in_tbl) {
- lq_sta->total_success += info->status.ampdu_ack_len;
- lq_sta->total_failed += (info->status.ampdu_len -
- info->status.ampdu_ack_len);
- }
- } else {
- /*
- * For legacy, update frame history with for each Tx retry.
- */
- retries = info->status.rates[0].count - 1;
- /* HW doesn't send more than 15 retries */
- retries = min(retries, 15);
-
- /* The last transmission may have been successful */
- legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
- /* Collect data for each rate used during failed TX attempts */
- for (i = 0; i <= retries; ++i) {
- tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags);
- rs_get_tbl_info_from_mcs(tx_rate, priv->band,
- &tbl_type, &rs_index);
- /*
- * Only collect stats if retried rate is in the same RS
- * table as active/search.
- */
- if (table_type_matches(&tbl_type, curr_tbl))
- tmp_tbl = curr_tbl;
- else if (table_type_matches(&tbl_type, other_tbl))
- tmp_tbl = other_tbl;
- else
- continue;
- rs_collect_tx_data(tmp_tbl, rs_index, 1,
- i < retries ? 0 : legacy_success);
- }
-
- /* Update success/fail counts if not searching for new mode */
- if (lq_sta->stay_in_tbl) {
- lq_sta->total_success += legacy_success;
- lq_sta->total_failed += retries + (1 - legacy_success);
- }
- }
- /* The last TX rate is cached in lq_sta; it's set in if/else above */
- lq_sta->last_rate_n_flags = tx_rate;
-done:
- /* See if there's a better rate or modulation mode to try. */
- if (sta && sta->supp_rates[sband->band])
- rs_rate_scale_perform(priv, skb, sta, lq_sta);
-
- if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist)
- rs_bt_update_lq(priv, ctx, lq_sta);
-}
-
-/*
- * Begin a period of staying with a selected modulation mode.
- * Set "stay_in_tbl" flag to prevent any mode switches.
- * Set frame tx success limits according to legacy vs. high-throughput,
- * and reset overall (spanning all rates) tx success history statistics.
- * These control how long we stay using same modulation mode before
- * searching for a new mode.
- */
-static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy,
- struct iwl_lq_sta *lq_sta)
-{
- IWL_DEBUG_RATE(priv, "we are staying in the same table\n");
- lq_sta->stay_in_tbl = 1; /* only place this gets set */
- if (is_legacy) {
- lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT;
- lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT;
- lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT;
- } else {
- lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT;
- lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT;
- lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT;
- }
- lq_sta->table_count = 0;
- lq_sta->total_failed = 0;
- lq_sta->total_success = 0;
- lq_sta->flush_timer = jiffies;
- lq_sta->action_counter = 0;
-}
-
-/*
- * Find correct throughput table for given mode of modulation
- */
-static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl)
-{
- /* Used to choose among HT tables */
- const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
-
- /* Check for invalid LQ type */
- if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
- tbl->expected_tpt = expected_tpt_legacy;
- return;
- }
-
- /* Legacy rates have only one table */
- if (is_legacy(tbl->lq_type)) {
- tbl->expected_tpt = expected_tpt_legacy;
- return;
- }
-
- /* Choose among many HT tables depending on number of streams
- * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation
- * status */
- if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
- ht_tbl_pointer = expected_tpt_siso20MHz;
- else if (is_siso(tbl->lq_type))
- ht_tbl_pointer = expected_tpt_siso40MHz;
- else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
- ht_tbl_pointer = expected_tpt_mimo2_20MHz;
- else if (is_mimo2(tbl->lq_type))
- ht_tbl_pointer = expected_tpt_mimo2_40MHz;
- else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
- ht_tbl_pointer = expected_tpt_mimo3_20MHz;
- else /* if (is_mimo3(tbl->lq_type)) <-- must be true */
- ht_tbl_pointer = expected_tpt_mimo3_40MHz;
-
- if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */
- tbl->expected_tpt = ht_tbl_pointer[0];
- else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */
- tbl->expected_tpt = ht_tbl_pointer[1];
- else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */
- tbl->expected_tpt = ht_tbl_pointer[2];
- else /* AGG+SGI */
- tbl->expected_tpt = ht_tbl_pointer[3];
-}
-
-/*
- * Find starting rate for new "search" high-throughput mode of modulation.
- * Goal is to find lowest expected rate (under perfect conditions) that is
- * above the current measured throughput of "active" mode, to give new mode
- * a fair chance to prove itself without too many challenges.
- *
- * This gets called when transitioning to more aggressive modulation
- * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
- * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need
- * to decrease to match "active" throughput. When moving from MIMO to SISO,
- * bit rate will typically need to increase, but not if performance was bad.
- */
-static s32 rs_get_best_rate(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl, /* "search" */
- u16 rate_mask, s8 index)
-{
- /* "active" values */
- struct iwl_scale_tbl_info *active_tbl =
- &(lq_sta->lq_info[lq_sta->active_tbl]);
- s32 active_sr = active_tbl->win[index].success_ratio;
- s32 active_tpt = active_tbl->expected_tpt[index];
- /* expected "search" throughput */
- const u16 *tpt_tbl = tbl->expected_tpt;
-
- s32 new_rate, high, low, start_hi;
- u16 high_low;
- s8 rate = index;
-
- new_rate = high = low = start_hi = IWL_RATE_INVALID;
-
- for (; ;) {
- high_low = rs_get_adjacent_rate(priv, rate, rate_mask,
- tbl->lq_type);
-
- low = high_low & 0xff;
- high = (high_low >> 8) & 0xff;
-
- /*
- * Lower the "search" bit rate, to give new "search" mode
- * approximately the same throughput as "active" if:
- *
- * 1) "Active" mode has been working modestly well (but not
- * great), and expected "search" throughput (under perfect
- * conditions) at candidate rate is above the actual
- * measured "active" throughput (but less than expected
- * "active" throughput under perfect conditions).
- * OR
- * 2) "Active" mode has been working perfectly or very well
- * and expected "search" throughput (under perfect
- * conditions) at candidate rate is above expected
- * "active" throughput (under perfect conditions).
- */
- if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
- ((active_sr > IWL_RATE_DECREASE_TH) &&
- (active_sr <= IWL_RATE_HIGH_TH) &&
- (tpt_tbl[rate] <= active_tpt))) ||
- ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
- (tpt_tbl[rate] > active_tpt))) {
-
- /* (2nd or later pass)
- * If we've already tried to raise the rate, and are
- * now trying to lower it, use the higher rate. */
- if (start_hi != IWL_RATE_INVALID) {
- new_rate = start_hi;
- break;
- }
-
- new_rate = rate;
-
- /* Loop again with lower rate */
- if (low != IWL_RATE_INVALID)
- rate = low;
-
- /* Lower rate not available, use the original */
- else
- break;
-
- /* Else try to raise the "search" rate to match "active" */
- } else {
- /* (2nd or later pass)
- * If we've already tried to lower the rate, and are
- * now trying to raise it, use the lower rate. */
- if (new_rate != IWL_RATE_INVALID)
- break;
-
- /* Loop again with higher rate */
- else if (high != IWL_RATE_INVALID) {
- start_hi = high;
- rate = high;
-
- /* Higher rate not available, use the original */
- } else {
- new_rate = rate;
- break;
- }
- }
- }
-
- return new_rate;
-}
-
-/*
- * Set up search table for MIMO2
- */
-static int rs_switch_to_mimo2(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_conf *conf,
- struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl, int index)
-{
- u16 rate_mask;
- s32 rate;
- s8 is_green = lq_sta->is_green;
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
- return -1;
-
- if (sta->smps_mode == IEEE80211_SMPS_STATIC)
- return -1;
-
- /* Need both Tx chains/antennas to support MIMO */
- if (priv->hw_params.tx_chains_num < 2)
- return -1;
-
- IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n");
-
- tbl->lq_type = LQ_MIMO2;
- tbl->is_dup = lq_sta->is_dup;
- tbl->action = 0;
- tbl->max_search = IWL_MAX_SEARCH;
- rate_mask = lq_sta->active_mimo2_rate;
-
- if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
- tbl->is_ht40 = 1;
- else
- tbl->is_ht40 = 0;
-
- rs_set_expected_tpt_table(lq_sta, tbl);
-
- rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index);
-
- IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask);
- if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
- IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n",
- rate, rate_mask);
- return -1;
- }
- tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green);
-
- IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n",
- tbl->current_rate, is_green);
- return 0;
-}
-
-/*
- * Set up search table for MIMO3
- */
-static int rs_switch_to_mimo3(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_conf *conf,
- struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl, int index)
-{
- u16 rate_mask;
- s32 rate;
- s8 is_green = lq_sta->is_green;
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
- return -1;
-
- if (sta->smps_mode == IEEE80211_SMPS_STATIC)
- return -1;
-
- /* Need both Tx chains/antennas to support MIMO */
- if (priv->hw_params.tx_chains_num < 3)
- return -1;
-
- IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n");
-
- tbl->lq_type = LQ_MIMO3;
- tbl->is_dup = lq_sta->is_dup;
- tbl->action = 0;
- tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
- rate_mask = lq_sta->active_mimo3_rate;
-
- if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
- tbl->is_ht40 = 1;
- else
- tbl->is_ht40 = 0;
-
- rs_set_expected_tpt_table(lq_sta, tbl);
-
- rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index);
-
- IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n",
- rate, rate_mask);
- if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
- IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n",
- rate, rate_mask);
- return -1;
- }
- tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green);
-
- IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n",
- tbl->current_rate, is_green);
- return 0;
-}
-
-/*
- * Set up search table for SISO
- */
-static int rs_switch_to_siso(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_conf *conf,
- struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl, int index)
-{
- u16 rate_mask;
- u8 is_green = lq_sta->is_green;
- s32 rate;
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported)
- return -1;
-
- IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n");
-
- tbl->is_dup = lq_sta->is_dup;
- tbl->lq_type = LQ_SISO;
- tbl->action = 0;
- tbl->max_search = IWL_MAX_SEARCH;
- rate_mask = lq_sta->active_siso_rate;
-
- if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
- tbl->is_ht40 = 1;
- else
- tbl->is_ht40 = 0;
-
- if (is_green)
- tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/
-
- rs_set_expected_tpt_table(lq_sta, tbl);
- rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index);
-
- IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask);
- if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
- IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n",
- rate, rate_mask);
- return -1;
- }
- tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green);
- IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n",
- tbl->current_rate, is_green);
- return 0;
-}
-
-/*
- * Try to switch to new modulation mode from legacy
- */
-static void rs_move_legacy_other(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_conf *conf,
- struct ieee80211_sta *sta,
- int index)
-{
- struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- struct iwl_scale_tbl_info *search_tbl =
- &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- struct iwl_rate_scale_data *window = &(tbl->win[index]);
- u32 sz = (sizeof(struct iwl_scale_tbl_info) -
- (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
- u8 start_action;
- u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
- int ret = 0;
- u8 update_search_tbl_counter = 0;
-
- switch (priv->bt_traffic_load) {
- case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
- /* nothing */
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
- /* avoid antenna B unless MIMO */
- if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2)
- tbl->action = IWL_LEGACY_SWITCH_SISO;
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
- case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
- /* avoid antenna B and MIMO */
- valid_tx_ant =
- first_antenna(priv->nvm_data->valid_tx_ant);
- if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 &&
- tbl->action != IWL_LEGACY_SWITCH_SISO)
- tbl->action = IWL_LEGACY_SWITCH_SISO;
- break;
- default:
- IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
- break;
- }
-
- if (!iwl_ht_enabled(priv))
- /* stay in Legacy */
- tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
- else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE &&
- tbl->action > IWL_LEGACY_SWITCH_SISO)
- tbl->action = IWL_LEGACY_SWITCH_SISO;
-
- /* configure as 1x1 if bt full concurrency */
- if (priv->bt_full_concurrent) {
- if (!iwl_ht_enabled(priv))
- tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
- else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
- tbl->action = IWL_LEGACY_SWITCH_SISO;
- valid_tx_ant =
- first_antenna(priv->nvm_data->valid_tx_ant);
- }
-
- start_action = tbl->action;
- for (; ;) {
- lq_sta->action_counter++;
- switch (tbl->action) {
- case IWL_LEGACY_SWITCH_ANTENNA1:
- case IWL_LEGACY_SWITCH_ANTENNA2:
- IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n");
-
- if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
- tx_chains_num <= 1) ||
- (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
- tx_chains_num <= 2))
- break;
-
- /* Don't change antenna if success has been great */
- if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
- !priv->bt_full_concurrent &&
- priv->bt_traffic_load ==
- IWL_BT_COEX_TRAFFIC_LOAD_NONE)
- break;
-
- /* Set up search table to try other antenna */
- memcpy(search_tbl, tbl, sz);
-
- if (rs_toggle_antenna(valid_tx_ant,
- &search_tbl->current_rate, search_tbl)) {
- update_search_tbl_counter = 1;
- rs_set_expected_tpt_table(lq_sta, search_tbl);
- goto out;
- }
- break;
- case IWL_LEGACY_SWITCH_SISO:
- IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n");
-
- /* Set up search table to try SISO */
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
- ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret) {
- lq_sta->action_counter = 0;
- goto out;
- }
-
- break;
- case IWL_LEGACY_SWITCH_MIMO2_AB:
- case IWL_LEGACY_SWITCH_MIMO2_AC:
- case IWL_LEGACY_SWITCH_MIMO2_BC:
- IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n");
-
- /* Set up search table to try MIMO */
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
-
- if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
- search_tbl->ant_type = ANT_AB;
- else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
- search_tbl->ant_type = ANT_AC;
- else
- search_tbl->ant_type = ANT_BC;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret) {
- lq_sta->action_counter = 0;
- goto out;
- }
- break;
-
- case IWL_LEGACY_SWITCH_MIMO3_ABC:
- IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n");
-
- /* Set up search table to try MIMO3 */
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
-
- search_tbl->ant_type = ANT_ABC;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret) {
- lq_sta->action_counter = 0;
- goto out;
- }
- break;
- }
- tbl->action++;
- if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
- tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
-
- if (tbl->action == start_action)
- break;
-
- }
- search_tbl->lq_type = LQ_NONE;
- return;
-
-out:
- lq_sta->search_better_tbl = 1;
- tbl->action++;
- if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
- tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
- if (update_search_tbl_counter)
- search_tbl->action = tbl->action;
-}
-
-/*
- * Try to switch to new modulation mode from SISO
- */
-static void rs_move_siso_to_other(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_conf *conf,
- struct ieee80211_sta *sta, int index)
-{
- u8 is_green = lq_sta->is_green;
- struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- struct iwl_scale_tbl_info *search_tbl =
- &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- struct iwl_rate_scale_data *window = &(tbl->win[index]);
- struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- u32 sz = (sizeof(struct iwl_scale_tbl_info) -
- (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
- u8 start_action;
- u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
- u8 update_search_tbl_counter = 0;
- int ret;
-
- switch (priv->bt_traffic_load) {
- case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
- /* nothing */
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
- /* avoid antenna B unless MIMO */
- if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
- tbl->action = IWL_SISO_SWITCH_MIMO2_AB;
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
- case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
- /* avoid antenna B and MIMO */
- valid_tx_ant =
- first_antenna(priv->nvm_data->valid_tx_ant);
- if (tbl->action != IWL_SISO_SWITCH_ANTENNA1)
- tbl->action = IWL_SISO_SWITCH_ANTENNA1;
- break;
- default:
- IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
- break;
- }
-
- if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE &&
- tbl->action > IWL_SISO_SWITCH_ANTENNA2) {
- /* stay in SISO */
- tbl->action = IWL_SISO_SWITCH_ANTENNA1;
- }
-
- /* configure as 1x1 if bt full concurrency */
- if (priv->bt_full_concurrent) {
- valid_tx_ant =
- first_antenna(priv->nvm_data->valid_tx_ant);
- if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
- tbl->action = IWL_SISO_SWITCH_ANTENNA1;
- }
-
- start_action = tbl->action;
- for (;;) {
- lq_sta->action_counter++;
- switch (tbl->action) {
- case IWL_SISO_SWITCH_ANTENNA1:
- case IWL_SISO_SWITCH_ANTENNA2:
- IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n");
- if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
- tx_chains_num <= 1) ||
- (tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
- tx_chains_num <= 2))
- break;
-
- if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
- !priv->bt_full_concurrent &&
- priv->bt_traffic_load ==
- IWL_BT_COEX_TRAFFIC_LOAD_NONE)
- break;
-
- memcpy(search_tbl, tbl, sz);
- if (rs_toggle_antenna(valid_tx_ant,
- &search_tbl->current_rate, search_tbl)) {
- update_search_tbl_counter = 1;
- goto out;
- }
- break;
- case IWL_SISO_SWITCH_MIMO2_AB:
- case IWL_SISO_SWITCH_MIMO2_AC:
- case IWL_SISO_SWITCH_MIMO2_BC:
- IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n");
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
-
- if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
- search_tbl->ant_type = ANT_AB;
- else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
- search_tbl->ant_type = ANT_AC;
- else
- search_tbl->ant_type = ANT_BC;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret)
- goto out;
- break;
- case IWL_SISO_SWITCH_GI:
- if (!tbl->is_ht40 && !(ht_cap->cap &
- IEEE80211_HT_CAP_SGI_20))
- break;
- if (tbl->is_ht40 && !(ht_cap->cap &
- IEEE80211_HT_CAP_SGI_40))
- break;
-
- IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n");
-
- memcpy(search_tbl, tbl, sz);
- if (is_green) {
- if (!tbl->is_SGI)
- break;
- else
- IWL_ERR(priv,
- "SGI was set in GF+SISO\n");
- }
- search_tbl->is_SGI = !tbl->is_SGI;
- rs_set_expected_tpt_table(lq_sta, search_tbl);
- if (tbl->is_SGI) {
- s32 tpt = lq_sta->last_tpt / 100;
- if (tpt >= search_tbl->expected_tpt[index])
- break;
- }
- search_tbl->current_rate =
- rate_n_flags_from_tbl(priv, search_tbl,
- index, is_green);
- update_search_tbl_counter = 1;
- goto out;
- case IWL_SISO_SWITCH_MIMO3_ABC:
- IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n");
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
- search_tbl->ant_type = ANT_ABC;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret)
- goto out;
- break;
- }
- tbl->action++;
- if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
- tbl->action = IWL_SISO_SWITCH_ANTENNA1;
-
- if (tbl->action == start_action)
- break;
- }
- search_tbl->lq_type = LQ_NONE;
- return;
-
- out:
- lq_sta->search_better_tbl = 1;
- tbl->action++;
- if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC)
- tbl->action = IWL_SISO_SWITCH_ANTENNA1;
- if (update_search_tbl_counter)
- search_tbl->action = tbl->action;
-}
-
-/*
- * Try to switch to new modulation mode from MIMO2
- */
-static void rs_move_mimo2_to_other(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_conf *conf,
- struct ieee80211_sta *sta, int index)
-{
- s8 is_green = lq_sta->is_green;
- struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- struct iwl_scale_tbl_info *search_tbl =
- &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- struct iwl_rate_scale_data *window = &(tbl->win[index]);
- struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- u32 sz = (sizeof(struct iwl_scale_tbl_info) -
- (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
- u8 start_action;
- u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
- u8 update_search_tbl_counter = 0;
- int ret;
-
- switch (priv->bt_traffic_load) {
- case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
- /* nothing */
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
- case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
- /* avoid antenna B and MIMO */
- if (tbl->action != IWL_MIMO2_SWITCH_SISO_A)
- tbl->action = IWL_MIMO2_SWITCH_SISO_A;
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
- /* avoid antenna B unless MIMO */
- if (tbl->action == IWL_MIMO2_SWITCH_SISO_B ||
- tbl->action == IWL_MIMO2_SWITCH_SISO_C)
- tbl->action = IWL_MIMO2_SWITCH_SISO_A;
- break;
- default:
- IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
- break;
- }
-
- if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) &&
- (tbl->action < IWL_MIMO2_SWITCH_SISO_A ||
- tbl->action > IWL_MIMO2_SWITCH_SISO_C)) {
- /* switch in SISO */
- tbl->action = IWL_MIMO2_SWITCH_SISO_A;
- }
-
- /* configure as 1x1 if bt full concurrency */
- if (priv->bt_full_concurrent &&
- (tbl->action < IWL_MIMO2_SWITCH_SISO_A ||
- tbl->action > IWL_MIMO2_SWITCH_SISO_C))
- tbl->action = IWL_MIMO2_SWITCH_SISO_A;
-
- start_action = tbl->action;
- for (;;) {
- lq_sta->action_counter++;
- switch (tbl->action) {
- case IWL_MIMO2_SWITCH_ANTENNA1:
- case IWL_MIMO2_SWITCH_ANTENNA2:
- IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n");
-
- if (tx_chains_num <= 2)
- break;
-
- if (window->success_ratio >= IWL_RS_GOOD_RATIO)
- break;
-
- memcpy(search_tbl, tbl, sz);
- if (rs_toggle_antenna(valid_tx_ant,
- &search_tbl->current_rate, search_tbl)) {
- update_search_tbl_counter = 1;
- goto out;
- }
- break;
- case IWL_MIMO2_SWITCH_SISO_A:
- case IWL_MIMO2_SWITCH_SISO_B:
- case IWL_MIMO2_SWITCH_SISO_C:
- IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n");
-
- /* Set up new search table for SISO */
- memcpy(search_tbl, tbl, sz);
-
- if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
- search_tbl->ant_type = ANT_A;
- else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
- search_tbl->ant_type = ANT_B;
- else
- search_tbl->ant_type = ANT_C;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret)
- goto out;
-
- break;
-
- case IWL_MIMO2_SWITCH_GI:
- if (!tbl->is_ht40 && !(ht_cap->cap &
- IEEE80211_HT_CAP_SGI_20))
- break;
- if (tbl->is_ht40 && !(ht_cap->cap &
- IEEE80211_HT_CAP_SGI_40))
- break;
-
- IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n");
-
- /* Set up new search table for MIMO2 */
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = !tbl->is_SGI;
- rs_set_expected_tpt_table(lq_sta, search_tbl);
- /*
- * If active table already uses the fastest possible
- * modulation (dual stream with short guard interval),
- * and it's working well, there's no need to look
- * for a better type of modulation!
- */
- if (tbl->is_SGI) {
- s32 tpt = lq_sta->last_tpt / 100;
- if (tpt >= search_tbl->expected_tpt[index])
- break;
- }
- search_tbl->current_rate =
- rate_n_flags_from_tbl(priv, search_tbl,
- index, is_green);
- update_search_tbl_counter = 1;
- goto out;
-
- case IWL_MIMO2_SWITCH_MIMO3_ABC:
- IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n");
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
- search_tbl->ant_type = ANT_ABC;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret)
- goto out;
-
- break;
- }
- tbl->action++;
- if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
- tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
-
- if (tbl->action == start_action)
- break;
- }
- search_tbl->lq_type = LQ_NONE;
- return;
- out:
- lq_sta->search_better_tbl = 1;
- tbl->action++;
- if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
- tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
- if (update_search_tbl_counter)
- search_tbl->action = tbl->action;
-
-}
-
-/*
- * Try to switch to new modulation mode from MIMO3
- */
-static void rs_move_mimo3_to_other(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_conf *conf,
- struct ieee80211_sta *sta, int index)
-{
- s8 is_green = lq_sta->is_green;
- struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- struct iwl_scale_tbl_info *search_tbl =
- &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- struct iwl_rate_scale_data *window = &(tbl->win[index]);
- struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- u32 sz = (sizeof(struct iwl_scale_tbl_info) -
- (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
- u8 start_action;
- u8 valid_tx_ant = priv->nvm_data->valid_tx_ant;
- u8 tx_chains_num = priv->hw_params.tx_chains_num;
- int ret;
- u8 update_search_tbl_counter = 0;
-
- switch (priv->bt_traffic_load) {
- case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
- /* nothing */
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
- case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
- /* avoid antenna B and MIMO */
- if (tbl->action != IWL_MIMO3_SWITCH_SISO_A)
- tbl->action = IWL_MIMO3_SWITCH_SISO_A;
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
- /* avoid antenna B unless MIMO */
- if (tbl->action == IWL_MIMO3_SWITCH_SISO_B ||
- tbl->action == IWL_MIMO3_SWITCH_SISO_C)
- tbl->action = IWL_MIMO3_SWITCH_SISO_A;
- break;
- default:
- IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
- break;
- }
-
- if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) &&
- (tbl->action < IWL_MIMO3_SWITCH_SISO_A ||
- tbl->action > IWL_MIMO3_SWITCH_SISO_C)) {
- /* switch in SISO */
- tbl->action = IWL_MIMO3_SWITCH_SISO_A;
- }
-
- /* configure as 1x1 if bt full concurrency */
- if (priv->bt_full_concurrent &&
- (tbl->action < IWL_MIMO3_SWITCH_SISO_A ||
- tbl->action > IWL_MIMO3_SWITCH_SISO_C))
- tbl->action = IWL_MIMO3_SWITCH_SISO_A;
-
- start_action = tbl->action;
- for (;;) {
- lq_sta->action_counter++;
- switch (tbl->action) {
- case IWL_MIMO3_SWITCH_ANTENNA1:
- case IWL_MIMO3_SWITCH_ANTENNA2:
- IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n");
-
- if (tx_chains_num <= 3)
- break;
-
- if (window->success_ratio >= IWL_RS_GOOD_RATIO)
- break;
-
- memcpy(search_tbl, tbl, sz);
- if (rs_toggle_antenna(valid_tx_ant,
- &search_tbl->current_rate, search_tbl))
- goto out;
- break;
- case IWL_MIMO3_SWITCH_SISO_A:
- case IWL_MIMO3_SWITCH_SISO_B:
- case IWL_MIMO3_SWITCH_SISO_C:
- IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n");
-
- /* Set up new search table for SISO */
- memcpy(search_tbl, tbl, sz);
-
- if (tbl->action == IWL_MIMO3_SWITCH_SISO_A)
- search_tbl->ant_type = ANT_A;
- else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B)
- search_tbl->ant_type = ANT_B;
- else
- search_tbl->ant_type = ANT_C;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret)
- goto out;
-
- break;
-
- case IWL_MIMO3_SWITCH_MIMO2_AB:
- case IWL_MIMO3_SWITCH_MIMO2_AC:
- case IWL_MIMO3_SWITCH_MIMO2_BC:
- IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n");
-
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
- if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB)
- search_tbl->ant_type = ANT_AB;
- else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC)
- search_tbl->ant_type = ANT_AC;
- else
- search_tbl->ant_type = ANT_BC;
-
- if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
- search_tbl, index);
- if (!ret)
- goto out;
-
- break;
-
- case IWL_MIMO3_SWITCH_GI:
- if (!tbl->is_ht40 && !(ht_cap->cap &
- IEEE80211_HT_CAP_SGI_20))
- break;
- if (tbl->is_ht40 && !(ht_cap->cap &
- IEEE80211_HT_CAP_SGI_40))
- break;
-
- IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n");
-
- /* Set up new search table for MIMO */
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = !tbl->is_SGI;
- rs_set_expected_tpt_table(lq_sta, search_tbl);
- /*
- * If active table already uses the fastest possible
- * modulation (dual stream with short guard interval),
- * and it's working well, there's no need to look
- * for a better type of modulation!
- */
- if (tbl->is_SGI) {
- s32 tpt = lq_sta->last_tpt / 100;
- if (tpt >= search_tbl->expected_tpt[index])
- break;
- }
- search_tbl->current_rate =
- rate_n_flags_from_tbl(priv, search_tbl,
- index, is_green);
- update_search_tbl_counter = 1;
- goto out;
- }
- tbl->action++;
- if (tbl->action > IWL_MIMO3_SWITCH_GI)
- tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
-
- if (tbl->action == start_action)
- break;
- }
- search_tbl->lq_type = LQ_NONE;
- return;
- out:
- lq_sta->search_better_tbl = 1;
- tbl->action++;
- if (tbl->action > IWL_MIMO3_SWITCH_GI)
- tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
- if (update_search_tbl_counter)
- search_tbl->action = tbl->action;
-}
-
-/*
- * Check whether we should continue using same modulation mode, or
- * begin search for a new mode, based on:
- * 1) # tx successes or failures while using this mode
- * 2) # times calling this function
- * 3) elapsed time in this mode (not used, for now)
- */
-static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
-{
- struct iwl_scale_tbl_info *tbl;
- int i;
- int active_tbl;
- int flush_interval_passed = 0;
- struct iwl_priv *priv;
-
- priv = lq_sta->drv;
- active_tbl = lq_sta->active_tbl;
-
- tbl = &(lq_sta->lq_info[active_tbl]);
-
- /* If we've been disallowing search, see if we should now allow it */
- if (lq_sta->stay_in_tbl) {
-
- /* Elapsed time using current modulation mode */
- if (lq_sta->flush_timer)
- flush_interval_passed =
- time_after(jiffies,
- (unsigned long)(lq_sta->flush_timer +
- IWL_RATE_SCALE_FLUSH_INTVL));
-
- /*
- * Check if we should allow search for new modulation mode.
- * If many frames have failed or succeeded, or we've used
- * this same modulation for a long time, allow search, and
- * reset history stats that keep track of whether we should
- * allow a new search. Also (below) reset all bitmaps and
- * stats in active history.
- */
- if (force_search ||
- (lq_sta->total_failed > lq_sta->max_failure_limit) ||
- (lq_sta->total_success > lq_sta->max_success_limit) ||
- ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer)
- && (flush_interval_passed))) {
- IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n",
- lq_sta->total_failed,
- lq_sta->total_success,
- flush_interval_passed);
-
- /* Allow search for new mode */
- lq_sta->stay_in_tbl = 0; /* only place reset */
- lq_sta->total_failed = 0;
- lq_sta->total_success = 0;
- lq_sta->flush_timer = 0;
-
- /*
- * Else if we've used this modulation mode enough repetitions
- * (regardless of elapsed time or success/failure), reset
- * history bitmaps and rate-specific stats for all rates in
- * active table.
- */
- } else {
- lq_sta->table_count++;
- if (lq_sta->table_count >=
- lq_sta->table_count_limit) {
- lq_sta->table_count = 0;
-
- IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n");
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(
- &(tbl->win[i]));
- }
- }
-
- /* If transitioning to allow "search", reset all history
- * bitmaps and stats in active table (this will become the new
- * "search" table). */
- if (!lq_sta->stay_in_tbl) {
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&(tbl->win[i]));
- }
- }
-}
-
-/*
- * setup rate table in uCode
- */
-static void rs_update_rate_tbl(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl,
- int index, u8 is_green)
-{
- u32 rate;
-
- /* Update uCode's rate table. */
- rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
- rs_fill_link_cmd(priv, lq_sta, rate);
- iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
-}
-
-/*
- * Do rate scaling and search for new modulation mode.
- */
-static void rs_rate_scale_perform(struct iwl_priv *priv,
- struct sk_buff *skb,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta)
-{
- struct ieee80211_hw *hw = priv->hw;
- struct ieee80211_conf *conf = &hw->conf;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- int low = IWL_RATE_INVALID;
- int high = IWL_RATE_INVALID;
- int index;
- int i;
- struct iwl_rate_scale_data *window = NULL;
- int current_tpt = IWL_INVALID_VALUE;
- int low_tpt = IWL_INVALID_VALUE;
- int high_tpt = IWL_INVALID_VALUE;
- u32 fail_count;
- s8 scale_action = 0;
- u16 rate_mask;
- u8 update_lq = 0;
- struct iwl_scale_tbl_info *tbl, *tbl1;
- u16 rate_scale_index_msk = 0;
- u8 is_green = 0;
- u8 active_tbl = 0;
- u8 done_search = 0;
- u16 high_low;
- s32 sr;
- u8 tid = IWL_MAX_TID_COUNT;
- struct iwl_tid_data *tid_data;
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n");
-
- /* Send management frames and NO_ACK data using lowest rate. */
- /* TODO: this could probably be improved.. */
- if (!ieee80211_is_data(hdr->frame_control) ||
- info->flags & IEEE80211_TX_CTL_NO_ACK)
- return;
-
- lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
-
- tid = rs_tl_add_packet(lq_sta, hdr);
- if ((tid != IWL_MAX_TID_COUNT) &&
- (lq_sta->tx_agg_tid_en & (1 << tid))) {
- tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid];
- if (tid_data->agg.state == IWL_AGG_OFF)
- lq_sta->is_agg = 0;
- else
- lq_sta->is_agg = 1;
- } else
- lq_sta->is_agg = 0;
-
- /*
- * Select rate-scale / modulation-mode table to work with in
- * the rest of this function: "search" if searching for better
- * modulation mode, or "active" if doing rate scaling within a mode.
- */
- if (!lq_sta->search_better_tbl)
- active_tbl = lq_sta->active_tbl;
- else
- active_tbl = 1 - lq_sta->active_tbl;
-
- tbl = &(lq_sta->lq_info[active_tbl]);
- if (is_legacy(tbl->lq_type))
- lq_sta->is_green = 0;
- else
- lq_sta->is_green = rs_use_green(sta);
- is_green = lq_sta->is_green;
-
- /* current tx rate */
- index = lq_sta->last_txrate_idx;
-
- IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index,
- tbl->lq_type);
-
- /* rates available for this association, and for modulation mode */
- rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type);
-
- IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask);
-
- /* mask with station rate restriction */
- if (is_legacy(tbl->lq_type)) {
- if (lq_sta->band == IEEE80211_BAND_5GHZ)
- /* supp_rates has no CCK bits in A mode */
- rate_scale_index_msk = (u16) (rate_mask &
- (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
- else
- rate_scale_index_msk = (u16) (rate_mask &
- lq_sta->supp_rates);
-
- } else
- rate_scale_index_msk = rate_mask;
-
- if (!rate_scale_index_msk)
- rate_scale_index_msk = rate_mask;
-
- if (!((1 << index) & rate_scale_index_msk)) {
- IWL_ERR(priv, "Current Rate is not valid\n");
- if (lq_sta->search_better_tbl) {
- /* revert to active table if search table is not valid*/
- tbl->lq_type = LQ_NONE;
- lq_sta->search_better_tbl = 0;
- tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- /* get "active" rate info */
- index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
- rs_update_rate_tbl(priv, ctx, lq_sta, tbl,
- index, is_green);
- }
- return;
- }
-
- /* Get expected throughput table and history window for current rate */
- if (!tbl->expected_tpt) {
- IWL_ERR(priv, "tbl->expected_tpt is NULL\n");
- return;
- }
-
- /* force user max rate if set by user */
- if ((lq_sta->max_rate_idx != -1) &&
- (lq_sta->max_rate_idx < index)) {
- index = lq_sta->max_rate_idx;
- update_lq = 1;
- window = &(tbl->win[index]);
- goto lq_update;
- }
-
- window = &(tbl->win[index]);
-
- /*
- * If there is not enough history to calculate actual average
- * throughput, keep analyzing results of more tx frames, without
- * changing rate or mode (bypass most of the rest of this function).
- * Set up new rate table in uCode only if old rate is not supported
- * in current association (use new rate found above).
- */
- fail_count = window->counter - window->success_counter;
- if ((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
- (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) {
- IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d "
- "for index %d\n",
- window->success_counter, window->counter, index);
-
- /* Can't calculate this yet; not enough history */
- window->average_tpt = IWL_INVALID_VALUE;
-
- /* Should we stay with this modulation mode,
- * or search for a new one? */
- rs_stay_in_table(lq_sta, false);
-
- goto out;
- }
- /* Else we have enough samples; calculate estimate of
- * actual average throughput */
- if (window->average_tpt != ((window->success_ratio *
- tbl->expected_tpt[index] + 64) / 128)) {
- IWL_ERR(priv, "expected_tpt should have been calculated by now\n");
- window->average_tpt = ((window->success_ratio *
- tbl->expected_tpt[index] + 64) / 128);
- }
-
- /* If we are searching for better modulation mode, check success. */
- if (lq_sta->search_better_tbl &&
- (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) {
- /* If good success, continue using the "search" mode;
- * no need to send new link quality command, since we're
- * continuing to use the setup that we've been trying. */
- if (window->average_tpt > lq_sta->last_tpt) {
-
- IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE "
- "suc=%d cur-tpt=%d old-tpt=%d\n",
- window->success_ratio,
- window->average_tpt,
- lq_sta->last_tpt);
-
- if (!is_legacy(tbl->lq_type))
- lq_sta->enable_counter = 1;
-
- /* Swap tables; "search" becomes "active" */
- lq_sta->active_tbl = active_tbl;
- current_tpt = window->average_tpt;
-
- /* Else poor success; go back to mode in "active" table */
- } else {
-
- IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE "
- "suc=%d cur-tpt=%d old-tpt=%d\n",
- window->success_ratio,
- window->average_tpt,
- lq_sta->last_tpt);
-
- /* Nullify "search" table */
- tbl->lq_type = LQ_NONE;
-
- /* Revert to "active" table */
- active_tbl = lq_sta->active_tbl;
- tbl = &(lq_sta->lq_info[active_tbl]);
-
- /* Revert to "active" rate and throughput info */
- index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
- current_tpt = lq_sta->last_tpt;
-
- /* Need to set up a new rate table in uCode */
- update_lq = 1;
- }
-
- /* Either way, we've made a decision; modulation mode
- * search is done, allow rate adjustment next time. */
- lq_sta->search_better_tbl = 0;
- done_search = 1; /* Don't switch modes below! */
- goto lq_update;
- }
-
- /* (Else) not in search of better modulation mode, try for better
- * starting rate, while staying in this mode. */
- high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk,
- tbl->lq_type);
- low = high_low & 0xff;
- high = (high_low >> 8) & 0xff;
-
- /* If user set max rate, dont allow higher than user constrain */
- if ((lq_sta->max_rate_idx != -1) &&
- (lq_sta->max_rate_idx < high))
- high = IWL_RATE_INVALID;
-
- sr = window->success_ratio;
-
- /* Collect measured throughputs for current and adjacent rates */
- current_tpt = window->average_tpt;
- if (low != IWL_RATE_INVALID)
- low_tpt = tbl->win[low].average_tpt;
- if (high != IWL_RATE_INVALID)
- high_tpt = tbl->win[high].average_tpt;
-
- scale_action = 0;
-
- /* Too many failures, decrease rate */
- if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) {
- IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n");
- scale_action = -1;
-
- /* No throughput measured yet for adjacent rates; try increase. */
- } else if ((low_tpt == IWL_INVALID_VALUE) &&
- (high_tpt == IWL_INVALID_VALUE)) {
-
- if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH)
- scale_action = 1;
- else if (low != IWL_RATE_INVALID)
- scale_action = 0;
- }
-
- /* Both adjacent throughputs are measured, but neither one has better
- * throughput; we're using the best rate, don't change it! */
- else if ((low_tpt != IWL_INVALID_VALUE) &&
- (high_tpt != IWL_INVALID_VALUE) &&
- (low_tpt < current_tpt) &&
- (high_tpt < current_tpt))
- scale_action = 0;
-
- /* At least one adjacent rate's throughput is measured,
- * and may have better performance. */
- else {
- /* Higher adjacent rate's throughput is measured */
- if (high_tpt != IWL_INVALID_VALUE) {
- /* Higher rate has better throughput */
- if (high_tpt > current_tpt &&
- sr >= IWL_RATE_INCREASE_TH) {
- scale_action = 1;
- } else {
- scale_action = 0;
- }
-
- /* Lower adjacent rate's throughput is measured */
- } else if (low_tpt != IWL_INVALID_VALUE) {
- /* Lower rate has better throughput */
- if (low_tpt > current_tpt) {
- IWL_DEBUG_RATE(priv,
- "decrease rate because of low tpt\n");
- scale_action = -1;
- } else if (sr >= IWL_RATE_INCREASE_TH) {
- scale_action = 1;
- }
- }
- }
-
- /* Sanity check; asked for decrease, but success rate or throughput
- * has been good at old rate. Don't change it. */
- if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
- ((sr > IWL_RATE_HIGH_TH) ||
- (current_tpt > (100 * tbl->expected_tpt[low]))))
- scale_action = 0;
- if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type))
- scale_action = -1;
- if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI &&
- (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type)))
- scale_action = -1;
-
- if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
- (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
- if (lq_sta->last_bt_traffic > priv->bt_traffic_load) {
- /*
- * don't set scale_action, don't want to scale up if
- * the rate scale doesn't otherwise think that is a
- * good idea.
- */
- } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) {
- scale_action = -1;
- }
- }
- lq_sta->last_bt_traffic = priv->bt_traffic_load;
-
- if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
- (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
- /* search for a new modulation */
- rs_stay_in_table(lq_sta, true);
- goto lq_update;
- }
-
- switch (scale_action) {
- case -1:
- /* Decrease starting rate, update uCode's rate table */
- if (low != IWL_RATE_INVALID) {
- update_lq = 1;
- index = low;
- }
-
- break;
- case 1:
- /* Increase starting rate, update uCode's rate table */
- if (high != IWL_RATE_INVALID) {
- update_lq = 1;
- index = high;
- }
-
- break;
- case 0:
- /* No change */
- default:
- break;
- }
-
- IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d "
- "high %d type %d\n",
- index, scale_action, low, high, tbl->lq_type);
-
-lq_update:
- /* Replace uCode's rate table for the destination station. */
- if (update_lq)
- rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green);
-
- if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) {
- /* Should we stay with this modulation mode,
- * or search for a new one? */
- rs_stay_in_table(lq_sta, false);
- }
- /*
- * Search for new modulation mode if we're:
- * 1) Not changing rates right now
- * 2) Not just finishing up a search
- * 3) Allowing a new search
- */
- if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) {
- /* Save current throughput to compare with "search" throughput*/
- lq_sta->last_tpt = current_tpt;
-
- /* Select a new "search" modulation mode to try.
- * If one is found, set up the new "search" table. */
- if (is_legacy(tbl->lq_type))
- rs_move_legacy_other(priv, lq_sta, conf, sta, index);
- else if (is_siso(tbl->lq_type))
- rs_move_siso_to_other(priv, lq_sta, conf, sta, index);
- else if (is_mimo2(tbl->lq_type))
- rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index);
- else
- rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index);
-
- /* If new "search" mode was selected, set up in uCode table */
- if (lq_sta->search_better_tbl) {
- /* Access the "search" table, clear its history. */
- tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&(tbl->win[i]));
-
- /* Use new "search" start rate */
- index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
-
- IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n",
- tbl->current_rate, index);
- rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
- iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false);
- } else
- done_search = 1;
- }
-
- if (done_search && !lq_sta->stay_in_tbl) {
- /* If the "active" (non-search) mode was legacy,
- * and we've tried switching antennas,
- * but we haven't been able to try HT modes (not available),
- * stay with best antenna legacy modulation for a while
- * before next round of mode comparisons. */
- tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
- if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) &&
- lq_sta->action_counter > tbl1->max_search) {
- IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n");
- rs_set_stay_in_table(priv, 1, lq_sta);
- }
-
- /* If we're in an HT mode, and all 3 mode switch actions
- * have been tried and compared, stay in this best modulation
- * mode for a while before next round of mode comparisons. */
- if (lq_sta->enable_counter &&
- (lq_sta->action_counter >= tbl1->max_search) &&
- iwl_ht_enabled(priv)) {
- if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
- (lq_sta->tx_agg_tid_en & (1 << tid)) &&
- (tid != IWL_MAX_TID_COUNT)) {
- u8 sta_id = lq_sta->lq.sta_id;
- tid_data = &priv->tid_data[sta_id][tid];
- if (tid_data->agg.state == IWL_AGG_OFF) {
- IWL_DEBUG_RATE(priv,
- "try to aggregate tid %d\n",
- tid);
- rs_tl_turn_on_agg(priv, tid,
- lq_sta, sta);
- }
- }
- rs_set_stay_in_table(priv, 0, lq_sta);
- }
- }
-
-out:
- tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
- lq_sta->last_txrate_idx = index;
-}
-
-/**
- * rs_initialize_lq - Initialize a station's hardware rate table
- *
- * The uCode's station table contains a table of fallback rates
- * for automatic fallback during transmission.
- *
- * NOTE: This sets up a default set of values. These will be replaced later
- * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
- * rc80211_simple.
- *
- * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
- * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
- * which requires station table entry to exist).
- */
-static void rs_initialize_lq(struct iwl_priv *priv,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta)
-{
- struct iwl_scale_tbl_info *tbl;
- int rate_idx;
- int i;
- u32 rate;
- u8 use_green = rs_use_green(sta);
- u8 active_tbl = 0;
- u8 valid_tx_ant;
- struct iwl_station_priv *sta_priv;
- struct iwl_rxon_context *ctx;
-
- if (!sta || !lq_sta)
- return;
-
- sta_priv = (void *)sta->drv_priv;
- ctx = sta_priv->ctx;
-
- i = lq_sta->last_txrate_idx;
-
- valid_tx_ant = priv->nvm_data->valid_tx_ant;
-
- if (!lq_sta->search_better_tbl)
- active_tbl = lq_sta->active_tbl;
- else
- active_tbl = 1 - lq_sta->active_tbl;
-
- tbl = &(lq_sta->lq_info[active_tbl]);
-
- if ((i < 0) || (i >= IWL_RATE_COUNT))
- i = 0;
-
- rate = iwl_rates[i].plcp;
- tbl->ant_type = first_antenna(valid_tx_ant);
- rate |= tbl->ant_type << RATE_MCS_ANT_POS;
-
- if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE)
- rate |= RATE_MCS_CCK_MSK;
-
- rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx);
- if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type))
- rs_toggle_antenna(valid_tx_ant, &rate, tbl);
-
- rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green);
- tbl->current_rate = rate;
- rs_set_expected_tpt_table(lq_sta, tbl);
- rs_fill_link_cmd(NULL, lq_sta, rate);
- priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq;
- iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true);
-}
-
-static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
- struct ieee80211_tx_rate_control *txrc)
-{
-
- struct sk_buff *skb = txrc->skb;
- struct ieee80211_supported_band *sband = txrc->sband;
- struct iwl_op_mode *op_mode __maybe_unused =
- (struct iwl_op_mode *)priv_r;
- struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl_lq_sta *lq_sta = priv_sta;
- int rate_idx;
-
- IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n");
-
- /* Get max rate if user set max rate */
- if (lq_sta) {
- lq_sta->max_rate_idx = txrc->max_rate_idx;
- if ((sband->band == IEEE80211_BAND_5GHZ) &&
- (lq_sta->max_rate_idx != -1))
- lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE;
- if ((lq_sta->max_rate_idx < 0) ||
- (lq_sta->max_rate_idx >= IWL_RATE_COUNT))
- lq_sta->max_rate_idx = -1;
- }
-
- /* Treat uninitialized rate scaling data same as non-existing. */
- if (lq_sta && !lq_sta->drv) {
- IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
- priv_sta = NULL;
- }
-
- /* Send management frames and NO_ACK data using lowest rate. */
- if (rate_control_send_low(sta, priv_sta, txrc))
- return;
-
- rate_idx = lq_sta->last_txrate_idx;
-
- if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
- rate_idx -= IWL_FIRST_OFDM_RATE;
- /* 6M and 9M shared same MCS index */
- rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
- if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
- IWL_RATE_MIMO3_6M_PLCP)
- rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
- else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
- IWL_RATE_MIMO2_6M_PLCP)
- rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
- info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
- if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
- info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
- if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK)
- info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA;
- if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK)
- info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
- if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK)
- info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
- } else {
- /* Check for invalid rates */
- if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) ||
- ((sband->band == IEEE80211_BAND_5GHZ) &&
- (rate_idx < IWL_FIRST_OFDM_RATE)))
- rate_idx = rate_lowest_index(sband, sta);
- /* On valid 5 GHz rate, adjust index */
- else if (sband->band == IEEE80211_BAND_5GHZ)
- rate_idx -= IWL_FIRST_OFDM_RATE;
- info->control.rates[0].flags = 0;
- }
- info->control.rates[0].idx = rate_idx;
- info->control.rates[0].count = 1;
-}
-
-static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
- gfp_t gfp)
-{
- struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv;
- struct iwl_op_mode *op_mode __maybe_unused =
- (struct iwl_op_mode *)priv_rate;
- struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode);
-
- IWL_DEBUG_RATE(priv, "create station rate scale window\n");
-
- return &sta_priv->lq_sta;
-}
-
-/*
- * Called after adding a new station to initialize rate scaling
- */
-void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id)
-{
- int i, j;
- struct ieee80211_hw *hw = priv->hw;
- struct ieee80211_conf *conf = &priv->hw->conf;
- struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- struct iwl_station_priv *sta_priv;
- struct iwl_lq_sta *lq_sta;
- struct ieee80211_supported_band *sband;
- unsigned long supp; /* must be unsigned long for for_each_set_bit */
-
- sta_priv = (struct iwl_station_priv *) sta->drv_priv;
- lq_sta = &sta_priv->lq_sta;
- sband = hw->wiphy->bands[conf->chandef.chan->band];
-
-
- lq_sta->lq.sta_id = sta_id;
-
- for (j = 0; j < LQ_SIZE; j++)
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
-
- lq_sta->flush_timer = 0;
- lq_sta->supp_rates = sta->supp_rates[sband->band];
-
- IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n",
- sta_id);
- /* TODO: what is a good starting rate for STA? About middle? Maybe not
- * the lowest or the highest rate.. Could consider using RSSI from
- * previous packets? Need to have IEEE 802.1X auth succeed immediately
- * after assoc.. */
-
- lq_sta->is_dup = 0;
- lq_sta->max_rate_idx = -1;
- lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
- lq_sta->is_green = rs_use_green(sta);
- lq_sta->band = sband->band;
- /*
- * active legacy rates as per supported rates bitmap
- */
- supp = sta->supp_rates[sband->band];
- lq_sta->active_legacy_rate = 0;
- for_each_set_bit(i, &supp, BITS_PER_LONG)
- lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
-
- /*
- * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3),
- * supp_rates[] does not; shift to convert format, force 9 MBits off.
- */
- lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
- lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
- lq_sta->active_siso_rate &= ~((u16)0x2);
- lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
-
- /* Same here */
- lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
- lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
- lq_sta->active_mimo2_rate &= ~((u16)0x2);
- lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
-
- lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1;
- lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1;
- lq_sta->active_mimo3_rate &= ~((u16)0x2);
- lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE;
-
- IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n",
- lq_sta->active_siso_rate,
- lq_sta->active_mimo2_rate,
- lq_sta->active_mimo3_rate);
-
- /* These values will be overridden later */
- lq_sta->lq.general_params.single_stream_ant_msk =
- first_antenna(priv->nvm_data->valid_tx_ant);
- lq_sta->lq.general_params.dual_stream_ant_msk =
- priv->nvm_data->valid_tx_ant &
- ~first_antenna(priv->nvm_data->valid_tx_ant);
- if (!lq_sta->lq.general_params.dual_stream_ant_msk) {
- lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB;
- } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) {
- lq_sta->lq.general_params.dual_stream_ant_msk =
- priv->nvm_data->valid_tx_ant;
- }
-
- /* as default allow aggregation for all tids */
- lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
- lq_sta->drv = priv;
-
- /* Set last_txrate_idx to lowest rate */
- lq_sta->last_txrate_idx = rate_lowest_index(sband, sta);
- if (sband->band == IEEE80211_BAND_5GHZ)
- lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
- lq_sta->is_agg = 0;
-#ifdef CONFIG_MAC80211_DEBUGFS
- lq_sta->dbg_fixed_rate = 0;
-#endif
-
- rs_initialize_lq(priv, sta, lq_sta);
-}
-
-static void rs_fill_link_cmd(struct iwl_priv *priv,
- struct iwl_lq_sta *lq_sta, u32 new_rate)
-{
- struct iwl_scale_tbl_info tbl_type;
- int index = 0;
- int rate_idx;
- int repeat_rate = 0;
- u8 ant_toggle_cnt = 0;
- u8 use_ht_possible = 1;
- u8 valid_tx_ant = 0;
- struct iwl_station_priv *sta_priv =
- container_of(lq_sta, struct iwl_station_priv, lq_sta);
- struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq;
-
- /* Override starting rate (index 0) if needed for debug purposes */
- rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
-
- /* Interpret new_rate (rate_n_flags) */
- rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
- &tbl_type, &rate_idx);
-
- if (priv && priv->bt_full_concurrent) {
- /* 1x1 only */
- tbl_type.ant_type =
- first_antenna(priv->nvm_data->valid_tx_ant);
- }
-
- /* How many times should we repeat the initial rate? */
- if (is_legacy(tbl_type.lq_type)) {
- ant_toggle_cnt = 1;
- repeat_rate = IWL_NUMBER_TRY;
- } else {
- repeat_rate = min(IWL_HT_NUMBER_TRY,
- LINK_QUAL_AGG_DISABLE_START_DEF - 1);
- }
-
- lq_cmd->general_params.mimo_delimiter =
- is_mimo(tbl_type.lq_type) ? 1 : 0;
-
- /* Fill 1st table entry (index 0) */
- lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate);
-
- if (num_of_ant(tbl_type.ant_type) == 1) {
- lq_cmd->general_params.single_stream_ant_msk =
- tbl_type.ant_type;
- } else if (num_of_ant(tbl_type.ant_type) == 2) {
- lq_cmd->general_params.dual_stream_ant_msk =
- tbl_type.ant_type;
- } /* otherwise we don't modify the existing value */
-
- index++;
- repeat_rate--;
- if (priv) {
- if (priv->bt_full_concurrent)
- valid_tx_ant = ANT_A;
- else
- valid_tx_ant = priv->nvm_data->valid_tx_ant;
- }
-
- /* Fill rest of rate table */
- while (index < LINK_QUAL_MAX_RETRY_NUM) {
- /* Repeat initial/next rate.
- * For legacy IWL_NUMBER_TRY == 1, this loop will not execute.
- * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */
- while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) {
- if (is_legacy(tbl_type.lq_type)) {
- if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
- ant_toggle_cnt++;
- else if (priv &&
- rs_toggle_antenna(valid_tx_ant,
- &new_rate, &tbl_type))
- ant_toggle_cnt = 1;
- }
-
- /* Override next rate if needed for debug purposes */
- rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
-
- /* Fill next table entry */
- lq_cmd->rs_table[index].rate_n_flags =
- cpu_to_le32(new_rate);
- repeat_rate--;
- index++;
- }
-
- rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type,
- &rate_idx);
-
- if (priv && priv->bt_full_concurrent) {
- /* 1x1 only */
- tbl_type.ant_type =
- first_antenna(priv->nvm_data->valid_tx_ant);
- }
-
- /* Indicate to uCode which entries might be MIMO.
- * If initial rate was MIMO, this will finally end up
- * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
- if (is_mimo(tbl_type.lq_type))
- lq_cmd->general_params.mimo_delimiter = index;
-
- /* Get next rate */
- new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx,
- use_ht_possible);
-
- /* How many times should we repeat the next rate? */
- if (is_legacy(tbl_type.lq_type)) {
- if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
- ant_toggle_cnt++;
- else if (priv &&
- rs_toggle_antenna(valid_tx_ant,
- &new_rate, &tbl_type))
- ant_toggle_cnt = 1;
-
- repeat_rate = IWL_NUMBER_TRY;
- } else {
- repeat_rate = IWL_HT_NUMBER_TRY;
- }
-
- /* Don't allow HT rates after next pass.
- * rs_get_lower_rate() will change type to LQ_A or LQ_G. */
- use_ht_possible = 0;
-
- /* Override next rate if needed for debug purposes */
- rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
-
- /* Fill next table entry */
- lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate);
-
- index++;
- repeat_rate--;
- }
-
- lq_cmd->agg_params.agg_frame_cnt_limit =
- sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF;
- lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
-
- lq_cmd->agg_params.agg_time_limit =
- cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
- /*
- * overwrite if needed, pass aggregation time limit
- * to uCode in uSec
- */
- if (priv && priv->lib->bt_params &&
- priv->lib->bt_params->agg_time_limit &&
- priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
- lq_cmd->agg_params.agg_time_limit =
- cpu_to_le16(priv->lib->bt_params->agg_time_limit);
-}
-
-static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
-{
- return hw->priv;
-}
-/* rate scale requires free function to be implemented */
-static void rs_free(void *priv_rate)
-{
- return;
-}
-
-static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta,
- void *priv_sta)
-{
- struct iwl_op_mode *op_mode __maybe_unused = priv_r;
- struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode);
-
- IWL_DEBUG_RATE(priv, "enter\n");
- IWL_DEBUG_RATE(priv, "leave\n");
-}
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
- u32 *rate_n_flags, int index)
-{
- struct iwl_priv *priv;
- u8 valid_tx_ant;
- u8 ant_sel_tx;
-
- priv = lq_sta->drv;
- valid_tx_ant = priv->nvm_data->valid_tx_ant;
- if (lq_sta->dbg_fixed_rate) {
- ant_sel_tx =
- ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
- >> RATE_MCS_ANT_POS);
- if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) {
- *rate_n_flags = lq_sta->dbg_fixed_rate;
- IWL_DEBUG_RATE(priv, "Fixed rate ON\n");
- } else {
- lq_sta->dbg_fixed_rate = 0;
- IWL_ERR(priv,
- "Invalid antenna selection 0x%X, Valid is 0x%X\n",
- ant_sel_tx, valid_tx_ant);
- IWL_DEBUG_RATE(priv, "Fixed rate OFF\n");
- }
- } else {
- IWL_DEBUG_RATE(priv, "Fixed rate OFF\n");
- }
-}
-
-static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct iwl_lq_sta *lq_sta = file->private_data;
- struct iwl_priv *priv;
- char buf[64];
- size_t buf_size;
- u32 parsed_rate;
-
-
- priv = lq_sta->drv;
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- if (sscanf(buf, "%x", &parsed_rate) == 1)
- lq_sta->dbg_fixed_rate = parsed_rate;
- else
- lq_sta->dbg_fixed_rate = 0;
-
- rs_program_fix_rate(priv, lq_sta);
-
- return count;
-}
-
-static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
- char __user *user_buf, size_t count, loff_t *ppos)
-{
- char *buff;
- int desc = 0;
- int i = 0;
- int index = 0;
- ssize_t ret;
-
- struct iwl_lq_sta *lq_sta = file->private_data;
- struct iwl_priv *priv;
- struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-
- priv = lq_sta->drv;
- buff = kmalloc(1024, GFP_KERNEL);
- if (!buff)
- return -ENOMEM;
-
- desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
- desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n",
- lq_sta->total_failed, lq_sta->total_success,
- lq_sta->active_legacy_rate);
- desc += sprintf(buff+desc, "fixed rate 0x%X\n",
- lq_sta->dbg_fixed_rate);
- desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
- (priv->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "",
- (priv->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "",
- (priv->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : "");
- desc += sprintf(buff+desc, "lq type %s\n",
- (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
- if (is_Ht(tbl->lq_type)) {
- desc += sprintf(buff + desc, " %s",
- (is_siso(tbl->lq_type)) ? "SISO" :
- ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
- desc += sprintf(buff + desc, " %s",
- (tbl->is_ht40) ? "40MHz" : "20MHz");
- desc += sprintf(buff + desc, " %s %s %s\n",
- (tbl->is_SGI) ? "SGI" : "",
- (lq_sta->is_green) ? "GF enabled" : "",
- (lq_sta->is_agg) ? "AGG on" : "");
- }
- desc += sprintf(buff+desc, "last tx rate=0x%X\n",
- lq_sta->last_rate_n_flags);
- desc += sprintf(buff+desc, "general:"
- "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n",
- lq_sta->lq.general_params.flags,
- lq_sta->lq.general_params.mimo_delimiter,
- lq_sta->lq.general_params.single_stream_ant_msk,
- lq_sta->lq.general_params.dual_stream_ant_msk);
-
- desc += sprintf(buff+desc, "agg:"
- "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n",
- le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit),
- lq_sta->lq.agg_params.agg_dis_start_th,
- lq_sta->lq.agg_params.agg_frame_cnt_limit);
-
- desc += sprintf(buff+desc,
- "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
- lq_sta->lq.general_params.start_rate_index[0],
- lq_sta->lq.general_params.start_rate_index[1],
- lq_sta->lq.general_params.start_rate_index[2],
- lq_sta->lq.general_params.start_rate_index[3]);
-
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
- index = iwl_hwrate_to_plcp_idx(
- le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags));
- if (is_legacy(tbl->lq_type)) {
- desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n",
- i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags),
- iwl_rate_mcs[index].mbps);
- } else {
- desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n",
- i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags),
- iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs);
- }
- }
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
- kfree(buff);
- return ret;
-}
-
-static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
- .write = rs_sta_dbgfs_scale_table_write,
- .read = rs_sta_dbgfs_scale_table_read,
- .open = simple_open,
- .llseek = default_llseek,
-};
-static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
- char __user *user_buf, size_t count, loff_t *ppos)
-{
- char *buff;
- int desc = 0;
- int i, j;
- ssize_t ret;
-
- struct iwl_lq_sta *lq_sta = file->private_data;
-
- buff = kmalloc(1024, GFP_KERNEL);
- if (!buff)
- return -ENOMEM;
-
- for (i = 0; i < LQ_SIZE; i++) {
- desc += sprintf(buff+desc,
- "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n"
- "rate=0x%X\n",
- lq_sta->active_tbl == i ? "*" : "x",
- lq_sta->lq_info[i].lq_type,
- lq_sta->lq_info[i].is_SGI,
- lq_sta->lq_info[i].is_ht40,
- lq_sta->lq_info[i].is_dup,
- lq_sta->is_green,
- lq_sta->lq_info[i].current_rate);
- for (j = 0; j < IWL_RATE_COUNT; j++) {
- desc += sprintf(buff+desc,
- "counter=%d success=%d %%=%d\n",
- lq_sta->lq_info[i].win[j].counter,
- lq_sta->lq_info[i].win[j].success_counter,
- lq_sta->lq_info[i].win[j].success_ratio);
- }
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
- kfree(buff);
- return ret;
-}
-
-static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
- .read = rs_sta_dbgfs_stats_table_read,
- .open = simple_open,
- .llseek = default_llseek,
-};
-
-static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file,
- char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct iwl_lq_sta *lq_sta = file->private_data;
- struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
- char buff[120];
- int desc = 0;
-
- if (is_Ht(tbl->lq_type))
- desc += sprintf(buff+desc,
- "Bit Rate= %d Mb/s\n",
- tbl->expected_tpt[lq_sta->last_txrate_idx]);
- else
- desc += sprintf(buff+desc,
- "Bit Rate= %d Mb/s\n",
- iwl_rates[lq_sta->last_txrate_idx].ieee >> 1);
-
- return simple_read_from_buffer(user_buf, count, ppos, buff, desc);
-}
-
-static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
- .read = rs_sta_dbgfs_rate_scale_data_read,
- .open = simple_open,
- .llseek = default_llseek,
-};
-
-static void rs_add_debugfs(void *priv, void *priv_sta,
- struct dentry *dir)
-{
- struct iwl_lq_sta *lq_sta = priv_sta;
- lq_sta->rs_sta_dbgfs_scale_table_file =
- debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
- lq_sta, &rs_sta_dbgfs_scale_table_ops);
- lq_sta->rs_sta_dbgfs_stats_table_file =
- debugfs_create_file("rate_stats_table", S_IRUSR, dir,
- lq_sta, &rs_sta_dbgfs_stats_table_ops);
- lq_sta->rs_sta_dbgfs_rate_scale_data_file =
- debugfs_create_file("rate_scale_data", S_IRUSR, dir,
- lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
- lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
- debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
- &lq_sta->tx_agg_tid_en);
-
-}
-
-static void rs_remove_debugfs(void *priv, void *priv_sta)
-{
- struct iwl_lq_sta *lq_sta = priv_sta;
- debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
- debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
- debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file);
- debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
-}
-#endif
-
-/*
- * Initialization of rate scaling information is done by driver after
- * the station is added. Since mac80211 calls this function before a
- * station is added we ignore it.
- */
-static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
- struct cfg80211_chan_def *chandef,
- struct ieee80211_sta *sta, void *priv_sta)
-{
-}
-
-static const struct rate_control_ops rs_ops = {
- .name = RS_NAME,
- .tx_status = rs_tx_status,
- .get_rate = rs_get_rate,
- .rate_init = rs_rate_init_stub,
- .alloc = rs_alloc,
- .free = rs_free,
- .alloc_sta = rs_alloc_sta,
- .free_sta = rs_free_sta,
-#ifdef CONFIG_MAC80211_DEBUGFS
- .add_sta_debugfs = rs_add_debugfs,
- .remove_sta_debugfs = rs_remove_debugfs,
-#endif
-};
-
-int iwlagn_rate_control_register(void)
-{
- return ieee80211_rate_control_register(&rs_ops);
-}
-
-void iwlagn_rate_control_unregister(void)
-{
- ieee80211_rate_control_unregister(&rs_ops);
-}
-
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h
deleted file mode 100644
index f6bd25cad203..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/rs.h
+++ /dev/null
@@ -1,426 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#ifndef __iwl_agn_rs_h__
-#define __iwl_agn_rs_h__
-
-#include <net/mac80211.h>
-
-#include "iwl-config.h"
-
-#include "commands.h"
-
-struct iwl_rate_info {
- u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
- u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
- u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
- u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */
- u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
- u8 prev_ieee; /* previous rate in IEEE speeds */
- u8 next_ieee; /* next rate in IEEE speeds */
- u8 prev_rs; /* previous rate used in rs algo */
- u8 next_rs; /* next rate used in rs algo */
- u8 prev_rs_tgg; /* previous rate used in TGG rs algo */
- u8 next_rs_tgg; /* next rate used in TGG rs algo */
-};
-
-/*
- * These serve as indexes into
- * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
- */
-enum {
- IWL_RATE_1M_INDEX = 0,
- IWL_RATE_2M_INDEX,
- IWL_RATE_5M_INDEX,
- IWL_RATE_11M_INDEX,
- IWL_RATE_6M_INDEX,
- IWL_RATE_9M_INDEX,
- IWL_RATE_12M_INDEX,
- IWL_RATE_18M_INDEX,
- IWL_RATE_24M_INDEX,
- IWL_RATE_36M_INDEX,
- IWL_RATE_48M_INDEX,
- IWL_RATE_54M_INDEX,
- IWL_RATE_60M_INDEX,
- IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/
- IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */
- IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
- IWL_RATE_INVALID = IWL_RATE_COUNT,
-};
-
-enum {
- IWL_RATE_6M_INDEX_TABLE = 0,
- IWL_RATE_9M_INDEX_TABLE,
- IWL_RATE_12M_INDEX_TABLE,
- IWL_RATE_18M_INDEX_TABLE,
- IWL_RATE_24M_INDEX_TABLE,
- IWL_RATE_36M_INDEX_TABLE,
- IWL_RATE_48M_INDEX_TABLE,
- IWL_RATE_54M_INDEX_TABLE,
- IWL_RATE_1M_INDEX_TABLE,
- IWL_RATE_2M_INDEX_TABLE,
- IWL_RATE_5M_INDEX_TABLE,
- IWL_RATE_11M_INDEX_TABLE,
- IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1,
-};
-
-enum {
- IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
- IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
- IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
- IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
-};
-
-/* #define vs. enum to keep from defaulting to 'large integer' */
-#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX)
-#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX)
-#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX)
-#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX)
-#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX)
-#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX)
-#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX)
-#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX)
-#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX)
-#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX)
-#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX)
-#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX)
-#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX)
-
-/* uCode API values for legacy bit rates, both OFDM and CCK */
-enum {
- IWL_RATE_6M_PLCP = 13,
- IWL_RATE_9M_PLCP = 15,
- IWL_RATE_12M_PLCP = 5,
- IWL_RATE_18M_PLCP = 7,
- IWL_RATE_24M_PLCP = 9,
- IWL_RATE_36M_PLCP = 11,
- IWL_RATE_48M_PLCP = 1,
- IWL_RATE_54M_PLCP = 3,
- IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/
- IWL_RATE_1M_PLCP = 10,
- IWL_RATE_2M_PLCP = 20,
- IWL_RATE_5M_PLCP = 55,
- IWL_RATE_11M_PLCP = 110,
- /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */
- /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/
-};
-
-/* uCode API values for OFDM high-throughput (HT) bit rates */
-enum {
- IWL_RATE_SISO_6M_PLCP = 0,
- IWL_RATE_SISO_12M_PLCP = 1,
- IWL_RATE_SISO_18M_PLCP = 2,
- IWL_RATE_SISO_24M_PLCP = 3,
- IWL_RATE_SISO_36M_PLCP = 4,
- IWL_RATE_SISO_48M_PLCP = 5,
- IWL_RATE_SISO_54M_PLCP = 6,
- IWL_RATE_SISO_60M_PLCP = 7,
- IWL_RATE_MIMO2_6M_PLCP = 0x8,
- IWL_RATE_MIMO2_12M_PLCP = 0x9,
- IWL_RATE_MIMO2_18M_PLCP = 0xa,
- IWL_RATE_MIMO2_24M_PLCP = 0xb,
- IWL_RATE_MIMO2_36M_PLCP = 0xc,
- IWL_RATE_MIMO2_48M_PLCP = 0xd,
- IWL_RATE_MIMO2_54M_PLCP = 0xe,
- IWL_RATE_MIMO2_60M_PLCP = 0xf,
- IWL_RATE_MIMO3_6M_PLCP = 0x10,
- IWL_RATE_MIMO3_12M_PLCP = 0x11,
- IWL_RATE_MIMO3_18M_PLCP = 0x12,
- IWL_RATE_MIMO3_24M_PLCP = 0x13,
- IWL_RATE_MIMO3_36M_PLCP = 0x14,
- IWL_RATE_MIMO3_48M_PLCP = 0x15,
- IWL_RATE_MIMO3_54M_PLCP = 0x16,
- IWL_RATE_MIMO3_60M_PLCP = 0x17,
- IWL_RATE_SISO_INVM_PLCP,
- IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
- IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
-};
-
-/* MAC header values for bit rates */
-enum {
- IWL_RATE_6M_IEEE = 12,
- IWL_RATE_9M_IEEE = 18,
- IWL_RATE_12M_IEEE = 24,
- IWL_RATE_18M_IEEE = 36,
- IWL_RATE_24M_IEEE = 48,
- IWL_RATE_36M_IEEE = 72,
- IWL_RATE_48M_IEEE = 96,
- IWL_RATE_54M_IEEE = 108,
- IWL_RATE_60M_IEEE = 120,
- IWL_RATE_1M_IEEE = 2,
- IWL_RATE_2M_IEEE = 4,
- IWL_RATE_5M_IEEE = 11,
- IWL_RATE_11M_IEEE = 22,
-};
-
-#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
-
-#define IWL_INVALID_VALUE -1
-
-#define IWL_MIN_RSSI_VAL -100
-#define IWL_MAX_RSSI_VAL 0
-
-/* These values specify how many Tx frame attempts before
- * searching for a new modulation mode */
-#define IWL_LEGACY_FAILURE_LIMIT 160
-#define IWL_LEGACY_SUCCESS_LIMIT 480
-#define IWL_LEGACY_TABLE_COUNT 160
-
-#define IWL_NONE_LEGACY_FAILURE_LIMIT 400
-#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500
-#define IWL_NONE_LEGACY_TABLE_COUNT 1500
-
-/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */
-#define IWL_RS_GOOD_RATIO 12800 /* 100% */
-#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */
-#define IWL_RATE_HIGH_TH 10880 /* 85% */
-#define IWL_RATE_INCREASE_TH 6400 /* 50% */
-#define IWL_RATE_DECREASE_TH 1920 /* 15% */
-
-/* possible actions when in legacy mode */
-#define IWL_LEGACY_SWITCH_ANTENNA1 0
-#define IWL_LEGACY_SWITCH_ANTENNA2 1
-#define IWL_LEGACY_SWITCH_SISO 2
-#define IWL_LEGACY_SWITCH_MIMO2_AB 3
-#define IWL_LEGACY_SWITCH_MIMO2_AC 4
-#define IWL_LEGACY_SWITCH_MIMO2_BC 5
-#define IWL_LEGACY_SWITCH_MIMO3_ABC 6
-
-/* possible actions when in siso mode */
-#define IWL_SISO_SWITCH_ANTENNA1 0
-#define IWL_SISO_SWITCH_ANTENNA2 1
-#define IWL_SISO_SWITCH_MIMO2_AB 2
-#define IWL_SISO_SWITCH_MIMO2_AC 3
-#define IWL_SISO_SWITCH_MIMO2_BC 4
-#define IWL_SISO_SWITCH_GI 5
-#define IWL_SISO_SWITCH_MIMO3_ABC 6
-
-
-/* possible actions when in mimo mode */
-#define IWL_MIMO2_SWITCH_ANTENNA1 0
-#define IWL_MIMO2_SWITCH_ANTENNA2 1
-#define IWL_MIMO2_SWITCH_SISO_A 2
-#define IWL_MIMO2_SWITCH_SISO_B 3
-#define IWL_MIMO2_SWITCH_SISO_C 4
-#define IWL_MIMO2_SWITCH_GI 5
-#define IWL_MIMO2_SWITCH_MIMO3_ABC 6
-
-
-/* possible actions when in mimo3 mode */
-#define IWL_MIMO3_SWITCH_ANTENNA1 0
-#define IWL_MIMO3_SWITCH_ANTENNA2 1
-#define IWL_MIMO3_SWITCH_SISO_A 2
-#define IWL_MIMO3_SWITCH_SISO_B 3
-#define IWL_MIMO3_SWITCH_SISO_C 4
-#define IWL_MIMO3_SWITCH_MIMO2_AB 5
-#define IWL_MIMO3_SWITCH_MIMO2_AC 6
-#define IWL_MIMO3_SWITCH_MIMO2_BC 7
-#define IWL_MIMO3_SWITCH_GI 8
-
-
-#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI
-#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC
-
-/*FIXME:RS:add possible actions for MIMO3*/
-
-#define IWL_ACTION_LIMIT 3 /* # possible actions */
-
-#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */
-
-/* load per tid defines for A-MPDU activation */
-#define IWL_AGG_TPT_THREHOLD 0
-#define IWL_AGG_LOAD_THRESHOLD 10
-#define IWL_AGG_ALL_TID 0xff
-#define TID_QUEUE_CELL_SPACING 50 /*mS */
-#define TID_QUEUE_MAX_SIZE 20
-#define TID_ROUND_VALUE 5 /* mS */
-
-#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
-#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
-
-extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
-
-enum iwl_table_type {
- LQ_NONE,
- LQ_G, /* legacy types */
- LQ_A,
- LQ_SISO, /* high-throughput types */
- LQ_MIMO2,
- LQ_MIMO3,
- LQ_MAX,
-};
-
-#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
-#define is_siso(tbl) ((tbl) == LQ_SISO)
-#define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
-#define is_mimo3(tbl) ((tbl) == LQ_MIMO3)
-#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl))
-#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
-#define is_a_band(tbl) ((tbl) == LQ_A)
-#define is_g_and(tbl) ((tbl) == LQ_G)
-
-#define IWL_MAX_MCS_DISPLAY_SIZE 12
-
-struct iwl_rate_mcs_info {
- char mbps[IWL_MAX_MCS_DISPLAY_SIZE];
- char mcs[IWL_MAX_MCS_DISPLAY_SIZE];
-};
-
-/**
- * struct iwl_rate_scale_data -- tx success history for one rate
- */
-struct iwl_rate_scale_data {
- u64 data; /* bitmap of successful frames */
- s32 success_counter; /* number of frames successful */
- s32 success_ratio; /* per-cent * 128 */
- s32 counter; /* number of frames attempted */
- s32 average_tpt; /* success ratio * expected throughput */
- unsigned long stamp;
-};
-
-/**
- * struct iwl_scale_tbl_info -- tx params and success history for all rates
- *
- * There are two of these in struct iwl_lq_sta,
- * one for "active", and one for "search".
- */
-struct iwl_scale_tbl_info {
- enum iwl_table_type lq_type;
- u8 ant_type;
- u8 is_SGI; /* 1 = short guard interval */
- u8 is_ht40; /* 1 = 40 MHz channel width */
- u8 is_dup; /* 1 = duplicated data streams */
- u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
- u8 max_search; /* maximun number of tables we can search */
- const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
- u32 current_rate; /* rate_n_flags, uCode API format */
- struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
-};
-
-struct iwl_traffic_load {
- unsigned long time_stamp; /* age of the oldest statistics */
- u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time
- * slice */
- u32 total; /* total num of packets during the
- * last TID_MAX_TIME_DIFF */
- u8 queue_count; /* number of queues that has
- * been used since the last cleanup */
- u8 head; /* start of the circular buffer */
-};
-
-/**
- * struct iwl_lq_sta -- driver's rate scaling private structure
- *
- * Pointer to this gets passed back and forth between driver and mac80211.
- */
-struct iwl_lq_sta {
- u8 active_tbl; /* index of active table, range 0-1 */
- u8 enable_counter; /* indicates HT mode */
- u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */
- u8 search_better_tbl; /* 1: currently trying alternate mode */
- s32 last_tpt;
-
- /* The following determine when to search for a new mode */
- u32 table_count_limit;
- u32 max_failure_limit; /* # failed frames before new search */
- u32 max_success_limit; /* # successful frames before new search */
- u32 table_count;
- u32 total_failed; /* total failed frames, any/all rates */
- u32 total_success; /* total successful frames, any/all rates */
- u64 flush_timer; /* time staying in mode before new search */
-
- u8 action_counter; /* # mode-switch actions tried */
- u8 is_green;
- u8 is_dup;
- enum ieee80211_band band;
-
- /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
- u32 supp_rates;
- u16 active_legacy_rate;
- u16 active_siso_rate;
- u16 active_mimo2_rate;
- u16 active_mimo3_rate;
- s8 max_rate_idx; /* Max rate set by user */
- u8 missed_rate_counter;
-
- struct iwl_link_quality_cmd lq;
- struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
- struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
- u8 tx_agg_tid_en;
-#ifdef CONFIG_MAC80211_DEBUGFS
- struct dentry *rs_sta_dbgfs_scale_table_file;
- struct dentry *rs_sta_dbgfs_stats_table_file;
- struct dentry *rs_sta_dbgfs_rate_scale_data_file;
- struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
- u32 dbg_fixed_rate;
-#endif
- struct iwl_priv *drv;
-
- /* used to be in sta_info */
- int last_txrate_idx;
- /* last tx rate_n_flags */
- u32 last_rate_n_flags;
- /* packets destined for this STA are aggregated */
- u8 is_agg;
- /* BT traffic this sta was last updated in */
- u8 last_bt_traffic;
-};
-
-static inline u8 first_antenna(u8 mask)
-{
- if (mask & ANT_A)
- return ANT_A;
- if (mask & ANT_B)
- return ANT_B;
- return ANT_C;
-}
-
-
-/* Initialize station's rate scaling information after adding station */
-void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta,
- u8 sta_id);
-
-/**
- * iwl_rate_control_register - Register the rate control algorithm callbacks
- *
- * Since the rate control algorithm is hardware specific, there is no need
- * or reason to place it as a stand alone module. The driver can call
- * iwl_rate_control_register in order to register the rate control callbacks
- * with the mac80211 subsystem. This should be performed prior to calling
- * ieee80211_register_hw
- *
- */
-int iwlagn_rate_control_register(void);
-
-/**
- * iwl_rate_control_unregister - Unregister the rate control callbacks
- *
- * This should be called after calling ieee80211_unregister_hw, but before
- * the driver is unloaded.
- */
-void iwlagn_rate_control_unregister(void);
-
-#endif /* __iwl_agn__rs__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c
deleted file mode 100644
index 4a45b0b594c7..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/rx.c
+++ /dev/null
@@ -1,1101 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portionhelp of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/etherdevice.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <net/mac80211.h>
-#include <asm/unaligned.h>
-#include "iwl-io.h"
-#include "dev.h"
-#include "calib.h"
-#include "agn.h"
-
-#define IWL_CMD_ENTRY(x) [x] = #x
-
-const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1] = {
- IWL_CMD_ENTRY(REPLY_ALIVE),
- IWL_CMD_ENTRY(REPLY_ERROR),
- IWL_CMD_ENTRY(REPLY_ECHO),
- IWL_CMD_ENTRY(REPLY_RXON),
- IWL_CMD_ENTRY(REPLY_RXON_ASSOC),
- IWL_CMD_ENTRY(REPLY_QOS_PARAM),
- IWL_CMD_ENTRY(REPLY_RXON_TIMING),
- IWL_CMD_ENTRY(REPLY_ADD_STA),
- IWL_CMD_ENTRY(REPLY_REMOVE_STA),
- IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA),
- IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH),
- IWL_CMD_ENTRY(REPLY_WEPKEY),
- IWL_CMD_ENTRY(REPLY_TX),
- IWL_CMD_ENTRY(REPLY_LEDS_CMD),
- IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD),
- IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD),
- IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION),
- IWL_CMD_ENTRY(COEX_EVENT_CMD),
- IWL_CMD_ENTRY(REPLY_QUIET_CMD),
- IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH),
- IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD),
- IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION),
- IWL_CMD_ENTRY(POWER_TABLE_CMD),
- IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION),
- IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC),
- IWL_CMD_ENTRY(REPLY_SCAN_CMD),
- IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD),
- IWL_CMD_ENTRY(SCAN_START_NOTIFICATION),
- IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION),
- IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION),
- IWL_CMD_ENTRY(BEACON_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_TX_BEACON),
- IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION),
- IWL_CMD_ENTRY(QUIET_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD),
- IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_BT_CONFIG),
- IWL_CMD_ENTRY(REPLY_STATISTICS_CMD),
- IWL_CMD_ENTRY(STATISTICS_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD),
- IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION),
- IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD),
- IWL_CMD_ENTRY(SENSITIVITY_CMD),
- IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD),
- IWL_CMD_ENTRY(REPLY_RX_PHY_CMD),
- IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD),
- IWL_CMD_ENTRY(REPLY_COMPRESSED_BA),
- IWL_CMD_ENTRY(CALIBRATION_CFG_CMD),
- IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION),
- IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD),
- IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION),
- IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD),
- IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF),
- IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE),
- IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV),
- IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS),
- IWL_CMD_ENTRY(REPLY_WIPAN_RXON),
- IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING),
- IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC),
- IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM),
- IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY),
- IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH),
- IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE),
- IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS),
- IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER),
- IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS),
- IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS),
- IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL),
- IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS),
- IWL_CMD_ENTRY(REPLY_D3_CONFIG),
-};
-#undef IWL_CMD_ENTRY
-
-/******************************************************************************
- *
- * Generic RX handler implementations
- *
- ******************************************************************************/
-
-static void iwlagn_rx_reply_error(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_error_resp *err_resp = (void *)pkt->data;
-
- IWL_ERR(priv, "Error Reply type 0x%08X cmd REPLY_ERROR (0x%02X) "
- "seq 0x%04X ser 0x%08X\n",
- le32_to_cpu(err_resp->error_type),
- err_resp->cmd_id,
- le16_to_cpu(err_resp->bad_cmd_seq_num),
- le32_to_cpu(err_resp->error_info));
-}
-
-static void iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_csa_notification *csa = (void *)pkt->data;
- /*
- * MULTI-FIXME
- * See iwlagn_mac_channel_switch.
- */
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- struct iwl_rxon_cmd *rxon = (void *)&ctx->active;
-
- if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
- return;
-
- if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) {
- rxon->channel = csa->channel;
- ctx->staging.channel = csa->channel;
- IWL_DEBUG_11H(priv, "CSA notif: channel %d\n",
- le16_to_cpu(csa->channel));
- iwl_chswitch_done(priv, true);
- } else {
- IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
- le16_to_cpu(csa->channel));
- iwl_chswitch_done(priv, false);
- }
-}
-
-
-static void iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_spectrum_notification *report = (void *)pkt->data;
-
- if (!report->state) {
- IWL_DEBUG_11H(priv,
- "Spectrum Measure Notification: Start\n");
- return;
- }
-
- memcpy(&priv->measure_report, report, sizeof(*report));
- priv->measurement_status |= MEASUREMENT_READY;
-}
-
-static void iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
-#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_sleep_notification *sleep = (void *)pkt->data;
- IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n",
- sleep->pm_sleep_mode, sleep->pm_wakeup_src);
-#endif
-}
-
-static void iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u32 __maybe_unused len = iwl_rx_packet_len(pkt);
- IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled "
- "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len);
- iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len);
-}
-
-static void iwlagn_rx_beacon_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwlagn_beacon_notif *beacon = (void *)pkt->data;
-#ifdef CONFIG_IWLWIFI_DEBUG
- u16 status = le16_to_cpu(beacon->beacon_notify_hdr.status.status);
- u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
-
- IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d "
- "tsf:0x%.8x%.8x rate:%d\n",
- status & TX_STATUS_MSK,
- beacon->beacon_notify_hdr.failure_frame,
- le32_to_cpu(beacon->ibss_mgr_status),
- le32_to_cpu(beacon->high_tsf),
- le32_to_cpu(beacon->low_tsf), rate);
-#endif
-
- priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
-}
-
-/**
- * iwl_good_plcp_health - checks for plcp error.
- *
- * When the plcp error is exceeding the thresholds, reset the radio
- * to improve the throughput.
- */
-static bool iwlagn_good_plcp_health(struct iwl_priv *priv,
- struct statistics_rx_phy *cur_ofdm,
- struct statistics_rx_ht_phy *cur_ofdm_ht,
- unsigned int msecs)
-{
- int delta;
- int threshold = priv->plcp_delta_threshold;
-
- if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) {
- IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n");
- return true;
- }
-
- delta = le32_to_cpu(cur_ofdm->plcp_err) -
- le32_to_cpu(priv->statistics.rx_ofdm.plcp_err) +
- le32_to_cpu(cur_ofdm_ht->plcp_err) -
- le32_to_cpu(priv->statistics.rx_ofdm_ht.plcp_err);
-
- /* Can be negative if firmware reset statistics */
- if (delta <= 0)
- return true;
-
- if ((delta * 100 / msecs) > threshold) {
- IWL_DEBUG_RADIO(priv,
- "plcp health threshold %u delta %d msecs %u\n",
- threshold, delta, msecs);
- return false;
- }
-
- return true;
-}
-
-int iwl_force_rf_reset(struct iwl_priv *priv, bool external)
-{
- struct iwl_rf_reset *rf_reset;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return -EAGAIN;
-
- if (!iwl_is_any_associated(priv)) {
- IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n");
- return -ENOLINK;
- }
-
- rf_reset = &priv->rf_reset;
- rf_reset->reset_request_count++;
- if (!external && rf_reset->last_reset_jiffies &&
- time_after(rf_reset->last_reset_jiffies +
- IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) {
- IWL_DEBUG_INFO(priv, "RF reset rejected\n");
- rf_reset->reset_reject_count++;
- return -EAGAIN;
- }
- rf_reset->reset_success_count++;
- rf_reset->last_reset_jiffies = jiffies;
-
- /*
- * There is no easy and better way to force reset the radio,
- * the only known method is switching channel which will force to
- * reset and tune the radio.
- * Use internal short scan (single channel) operation to should
- * achieve this objective.
- * Driver should reset the radio when number of consecutive missed
- * beacon, or any other uCode error condition detected.
- */
- IWL_DEBUG_INFO(priv, "perform radio reset.\n");
- iwl_internal_short_hw_scan(priv);
- return 0;
-}
-
-
-static void iwlagn_recover_from_statistics(struct iwl_priv *priv,
- struct statistics_rx_phy *cur_ofdm,
- struct statistics_rx_ht_phy *cur_ofdm_ht,
- struct statistics_tx *tx,
- unsigned long stamp)
-{
- unsigned int msecs;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies);
-
- /* Only gather statistics and update time stamp when not associated */
- if (!iwl_is_any_associated(priv))
- return;
-
- /* Do not check/recover when do not have enough statistics data */
- if (msecs < 99)
- return;
-
- if (!iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs))
- iwl_force_rf_reset(priv, false);
-}
-
-/* Calculate noise level, based on measurements during network silence just
- * before arriving beacon. This measurement can be done only if we know
- * exactly when to expect beacons, therefore only when we're associated. */
-static void iwlagn_rx_calc_noise(struct iwl_priv *priv)
-{
- struct statistics_rx_non_phy *rx_info;
- int num_active_rx = 0;
- int total_silence = 0;
- int bcn_silence_a, bcn_silence_b, bcn_silence_c;
- int last_rx_noise;
-
- rx_info = &priv->statistics.rx_non_phy;
-
- bcn_silence_a =
- le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
- bcn_silence_b =
- le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
- bcn_silence_c =
- le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
-
- if (bcn_silence_a) {
- total_silence += bcn_silence_a;
- num_active_rx++;
- }
- if (bcn_silence_b) {
- total_silence += bcn_silence_b;
- num_active_rx++;
- }
- if (bcn_silence_c) {
- total_silence += bcn_silence_c;
- num_active_rx++;
- }
-
- /* Average among active antennas */
- if (num_active_rx)
- last_rx_noise = (total_silence / num_active_rx) - 107;
- else
- last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
-
- IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n",
- bcn_silence_a, bcn_silence_b, bcn_silence_c,
- last_rx_noise);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-/*
- * based on the assumption of all statistics counter are in DWORD
- * FIXME: This function is for debugging, do not deal with
- * the case of counters roll-over.
- */
-static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta,
- __le32 *max_delta, __le32 *accum, int size)
-{
- int i;
-
- for (i = 0;
- i < size / sizeof(__le32);
- i++, prev++, cur++, delta++, max_delta++, accum++) {
- if (le32_to_cpu(*cur) > le32_to_cpu(*prev)) {
- *delta = cpu_to_le32(
- le32_to_cpu(*cur) - le32_to_cpu(*prev));
- le32_add_cpu(accum, le32_to_cpu(*delta));
- if (le32_to_cpu(*delta) > le32_to_cpu(*max_delta))
- *max_delta = *delta;
- }
- }
-}
-
-static void
-iwlagn_accumulative_statistics(struct iwl_priv *priv,
- struct statistics_general_common *common,
- struct statistics_rx_non_phy *rx_non_phy,
- struct statistics_rx_phy *rx_ofdm,
- struct statistics_rx_ht_phy *rx_ofdm_ht,
- struct statistics_rx_phy *rx_cck,
- struct statistics_tx *tx,
- struct statistics_bt_activity *bt_activity)
-{
-#define ACCUM(_name) \
- accum_stats((__le32 *)&priv->statistics._name, \
- (__le32 *)_name, \
- (__le32 *)&priv->delta_stats._name, \
- (__le32 *)&priv->max_delta_stats._name, \
- (__le32 *)&priv->accum_stats._name, \
- sizeof(*_name));
-
- ACCUM(common);
- ACCUM(rx_non_phy);
- ACCUM(rx_ofdm);
- ACCUM(rx_ofdm_ht);
- ACCUM(rx_cck);
- ACCUM(tx);
- if (bt_activity)
- ACCUM(bt_activity);
-#undef ACCUM
-}
-#else
-static inline void
-iwlagn_accumulative_statistics(struct iwl_priv *priv,
- struct statistics_general_common *common,
- struct statistics_rx_non_phy *rx_non_phy,
- struct statistics_rx_phy *rx_ofdm,
- struct statistics_rx_ht_phy *rx_ofdm_ht,
- struct statistics_rx_phy *rx_cck,
- struct statistics_tx *tx,
- struct statistics_bt_activity *bt_activity)
-{
-}
-#endif
-
-static void iwlagn_rx_statistics(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- unsigned long stamp = jiffies;
- const int reg_recalib_period = 60;
- int change;
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u32 len = iwl_rx_packet_payload_len(pkt);
- __le32 *flag;
- struct statistics_general_common *common;
- struct statistics_rx_non_phy *rx_non_phy;
- struct statistics_rx_phy *rx_ofdm;
- struct statistics_rx_ht_phy *rx_ofdm_ht;
- struct statistics_rx_phy *rx_cck;
- struct statistics_tx *tx;
- struct statistics_bt_activity *bt_activity;
-
- IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n",
- len);
-
- spin_lock(&priv->statistics.lock);
-
- if (len == sizeof(struct iwl_bt_notif_statistics)) {
- struct iwl_bt_notif_statistics *stats;
- stats = (void *)&pkt->data;
- flag = &stats->flag;
- common = &stats->general.common;
- rx_non_phy = &stats->rx.general.common;
- rx_ofdm = &stats->rx.ofdm;
- rx_ofdm_ht = &stats->rx.ofdm_ht;
- rx_cck = &stats->rx.cck;
- tx = &stats->tx;
- bt_activity = &stats->general.activity;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- /* handle this exception directly */
- priv->statistics.num_bt_kills = stats->rx.general.num_bt_kills;
- le32_add_cpu(&priv->statistics.accum_num_bt_kills,
- le32_to_cpu(stats->rx.general.num_bt_kills));
-#endif
- } else if (len == sizeof(struct iwl_notif_statistics)) {
- struct iwl_notif_statistics *stats;
- stats = (void *)&pkt->data;
- flag = &stats->flag;
- common = &stats->general.common;
- rx_non_phy = &stats->rx.general;
- rx_ofdm = &stats->rx.ofdm;
- rx_ofdm_ht = &stats->rx.ofdm_ht;
- rx_cck = &stats->rx.cck;
- tx = &stats->tx;
- bt_activity = NULL;
- } else {
- WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n",
- len, sizeof(struct iwl_bt_notif_statistics),
- sizeof(struct iwl_notif_statistics));
- spin_unlock(&priv->statistics.lock);
- return;
- }
-
- change = common->temperature != priv->statistics.common.temperature ||
- (*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
- (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK);
-
- iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm,
- rx_ofdm_ht, rx_cck, tx, bt_activity);
-
- iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp);
-
- priv->statistics.flag = *flag;
- memcpy(&priv->statistics.common, common, sizeof(*common));
- memcpy(&priv->statistics.rx_non_phy, rx_non_phy, sizeof(*rx_non_phy));
- memcpy(&priv->statistics.rx_ofdm, rx_ofdm, sizeof(*rx_ofdm));
- memcpy(&priv->statistics.rx_ofdm_ht, rx_ofdm_ht, sizeof(*rx_ofdm_ht));
- memcpy(&priv->statistics.rx_cck, rx_cck, sizeof(*rx_cck));
- memcpy(&priv->statistics.tx, tx, sizeof(*tx));
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (bt_activity)
- memcpy(&priv->statistics.bt_activity, bt_activity,
- sizeof(*bt_activity));
-#endif
-
- priv->rx_statistics_jiffies = stamp;
-
- set_bit(STATUS_STATISTICS, &priv->status);
-
- /* Reschedule the statistics timer to occur in
- * reg_recalib_period seconds to ensure we get a
- * thermal update even if the uCode doesn't give
- * us one */
- mod_timer(&priv->statistics_periodic, jiffies +
- msecs_to_jiffies(reg_recalib_period * 1000));
-
- if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
- (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {
- iwlagn_rx_calc_noise(priv);
- queue_work(priv->workqueue, &priv->run_time_calib_work);
- }
- if (priv->lib->temperature && change)
- priv->lib->temperature(priv);
-
- spin_unlock(&priv->statistics.lock);
-}
-
-static void iwlagn_rx_reply_statistics(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_notif_statistics *stats = (void *)pkt->data;
-
- if (le32_to_cpu(stats->flag) & UCODE_STATISTICS_CLEAR_MSK) {
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- memset(&priv->accum_stats, 0,
- sizeof(priv->accum_stats));
- memset(&priv->delta_stats, 0,
- sizeof(priv->delta_stats));
- memset(&priv->max_delta_stats, 0,
- sizeof(priv->max_delta_stats));
-#endif
- IWL_DEBUG_RX(priv, "Statistics have been cleared\n");
- }
-
- iwlagn_rx_statistics(priv, rxb);
-}
-
-/* Handle notification from uCode that card's power state is changing
- * due to software, hardware, or critical temperature RFKILL */
-static void iwlagn_rx_card_state_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_card_state_notif *card_state_notif = (void *)pkt->data;
- u32 flags = le32_to_cpu(card_state_notif->flags);
- unsigned long status = priv->status;
-
- IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n",
- (flags & HW_CARD_DISABLED) ? "Kill" : "On",
- (flags & SW_CARD_DISABLED) ? "Kill" : "On",
- (flags & CT_CARD_DISABLED) ?
- "Reached" : "Not reached");
-
- if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED |
- CT_CARD_DISABLED)) {
-
- iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
- CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-
- iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C,
- HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
-
- if (!(flags & RXON_CARD_DISABLED)) {
- iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
- iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C,
- HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
- }
- if (flags & CT_CARD_DISABLED)
- iwl_tt_enter_ct_kill(priv);
- }
- if (!(flags & CT_CARD_DISABLED))
- iwl_tt_exit_ct_kill(priv);
-
- if (flags & HW_CARD_DISABLED)
- set_bit(STATUS_RF_KILL_HW, &priv->status);
- else
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
-
-
- if (!(flags & RXON_CARD_DISABLED))
- iwl_scan_cancel(priv);
-
- if ((test_bit(STATUS_RF_KILL_HW, &status) !=
- test_bit(STATUS_RF_KILL_HW, &priv->status)))
- wiphy_rfkill_set_hw_state(priv->hw->wiphy,
- test_bit(STATUS_RF_KILL_HW, &priv->status));
-}
-
-static void iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_missed_beacon_notif *missed_beacon = (void *)pkt->data;
-
- if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
- priv->missed_beacon_threshold) {
- IWL_DEBUG_CALIB(priv,
- "missed bcn cnsq %d totl %d rcd %d expctd %d\n",
- le32_to_cpu(missed_beacon->consecutive_missed_beacons),
- le32_to_cpu(missed_beacon->total_missed_becons),
- le32_to_cpu(missed_beacon->num_recvd_beacons),
- le32_to_cpu(missed_beacon->num_expected_beacons));
- if (!test_bit(STATUS_SCANNING, &priv->status))
- iwl_init_sensitivity(priv);
- }
-}
-
-/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD).
- * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */
-static void iwlagn_rx_reply_rx_phy(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
-
- priv->last_phy_res_valid = true;
- priv->ampdu_ref++;
- memcpy(&priv->last_phy_res, pkt->data,
- sizeof(struct iwl_rx_phy_res));
-}
-
-/*
- * returns non-zero if packet should be dropped
- */
-static int iwlagn_set_decrypted_flag(struct iwl_priv *priv,
- struct ieee80211_hdr *hdr,
- u32 decrypt_res,
- struct ieee80211_rx_status *stats)
-{
- u16 fc = le16_to_cpu(hdr->frame_control);
-
- /*
- * All contexts have the same setting here due to it being
- * a module parameter, so OK to check any context.
- */
- if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags &
- RXON_FILTER_DIS_DECRYPT_MSK)
- return 0;
-
- if (!(fc & IEEE80211_FCTL_PROTECTED))
- return 0;
-
- IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res);
- switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
- case RX_RES_STATUS_SEC_TYPE_TKIP:
- /* The uCode has got a bad phase 1 Key, pushes the packet.
- * Decryption will be done in SW. */
- if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
- RX_RES_STATUS_BAD_KEY_TTAK)
- break;
-
- case RX_RES_STATUS_SEC_TYPE_WEP:
- if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
- RX_RES_STATUS_BAD_ICV_MIC) {
- /* bad ICV, the packet is destroyed since the
- * decryption is inplace, drop it */
- IWL_DEBUG_RX(priv, "Packet destroyed\n");
- return -1;
- }
- case RX_RES_STATUS_SEC_TYPE_CCMP:
- if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
- RX_RES_STATUS_DECRYPT_OK) {
- IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n");
- stats->flag |= RX_FLAG_DECRYPTED;
- }
- break;
-
- default:
- break;
- }
- return 0;
-}
-
-static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
- struct ieee80211_hdr *hdr,
- u16 len,
- u32 ampdu_status,
- struct iwl_rx_cmd_buffer *rxb,
- struct ieee80211_rx_status *stats)
-{
- struct sk_buff *skb;
- __le16 fc = hdr->frame_control;
- struct iwl_rxon_context *ctx;
- unsigned int hdrlen, fraglen;
-
- /* We only process data packets if the interface is open */
- if (unlikely(!priv->is_open)) {
- IWL_DEBUG_DROP_LIMIT(priv,
- "Dropping packet while interface is not open.\n");
- return;
- }
-
- /* In case of HW accelerated crypto and bad decryption, drop */
- if (!iwlwifi_mod_params.sw_crypto &&
- iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats))
- return;
-
- /* Dont use dev_alloc_skb(), we'll have enough headroom once
- * ieee80211_hdr pulled.
- */
- skb = alloc_skb(128, GFP_ATOMIC);
- if (!skb) {
- IWL_ERR(priv, "alloc_skb failed\n");
- return;
- }
- /* If frame is small enough to fit in skb->head, pull it completely.
- * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
- * are more efficient.
- */
- hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
-
- memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
- fraglen = len - hdrlen;
-
- if (fraglen) {
- int offset = (void *)hdr + hdrlen -
- rxb_addr(rxb) + rxb_offset(rxb);
-
- skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
- fraglen, rxb->truesize);
- }
-
- /*
- * Wake any queues that were stopped due to a passive channel tx
- * failure. This can happen because the regulatory enforcement in
- * the device waits for a beacon before allowing transmission,
- * sometimes even after already having transmitted frames for the
- * association because the new RXON may reset the information.
- */
- if (unlikely(ieee80211_is_beacon(fc) && priv->passive_no_rx)) {
- for_each_context(priv, ctx) {
- if (!ether_addr_equal(hdr->addr3,
- ctx->active.bssid_addr))
- continue;
- iwlagn_lift_passive_no_rx(priv);
- }
- }
-
- memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
-
- ieee80211_rx_napi(priv->hw, skb, priv->napi);
-}
-
-static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
-{
- u32 decrypt_out = 0;
-
- if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) ==
- RX_RES_STATUS_STATION_FOUND)
- decrypt_out |= (RX_RES_STATUS_STATION_FOUND |
- RX_RES_STATUS_NO_STATION_INFO_MISMATCH);
-
- decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK);
-
- /* packet was not encrypted */
- if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
- RX_RES_STATUS_SEC_TYPE_NONE)
- return decrypt_out;
-
- /* packet was encrypted with unknown alg */
- if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
- RX_RES_STATUS_SEC_TYPE_ERR)
- return decrypt_out;
-
- /* decryption was not done in HW */
- if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) !=
- RX_MPDU_RES_STATUS_DEC_DONE_MSK)
- return decrypt_out;
-
- switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) {
-
- case RX_RES_STATUS_SEC_TYPE_CCMP:
- /* alg is CCM: check MIC only */
- if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK))
- /* Bad MIC */
- decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
- else
- decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
-
- break;
-
- case RX_RES_STATUS_SEC_TYPE_TKIP:
- if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) {
- /* Bad TTAK */
- decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
- break;
- }
- /* fall through if TTAK OK */
- default:
- if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
- decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
- else
- decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
- break;
- }
-
- IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n",
- decrypt_in, decrypt_out);
-
- return decrypt_out;
-}
-
-/* Calc max signal level (dBm) among 3 possible receivers */
-static int iwlagn_calc_rssi(struct iwl_priv *priv,
- struct iwl_rx_phy_res *rx_resp)
-{
- /* data from PHY/DSP regarding signal strength, etc.,
- * contents are always there, not configurable by host
- */
- struct iwlagn_non_cfg_phy *ncphy =
- (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
- u32 val, rssi_a, rssi_b, rssi_c, max_rssi;
- u8 agc;
-
- val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]);
- agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS;
-
- /* Find max rssi among 3 possible receivers.
- * These values are measured by the digital signal processor (DSP).
- * They should stay fairly constant even as the signal strength varies,
- * if the radio's automatic gain control (AGC) is working right.
- * AGC value (see below) will provide the "interesting" info.
- */
- val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]);
- rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >>
- IWLAGN_OFDM_RSSI_A_BIT_POS;
- rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >>
- IWLAGN_OFDM_RSSI_B_BIT_POS;
- val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]);
- rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >>
- IWLAGN_OFDM_RSSI_C_BIT_POS;
-
- max_rssi = max_t(u32, rssi_a, rssi_b);
- max_rssi = max_t(u32, max_rssi, rssi_c);
-
- IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
- rssi_a, rssi_b, rssi_c, max_rssi, agc);
-
- /* dBm = max_rssi dB - agc dB - constant.
- * Higher AGC (higher radio gain) means lower signal. */
- return max_rssi - agc - IWLAGN_RSSI_OFFSET;
-}
-
-/* Called for REPLY_RX_MPDU_CMD */
-static void iwlagn_rx_reply_rx(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct ieee80211_hdr *header;
- struct ieee80211_rx_status rx_status = {};
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_rx_phy_res *phy_res;
- __le32 rx_pkt_status;
- struct iwl_rx_mpdu_res_start *amsdu;
- u32 len;
- u32 ampdu_status;
- u32 rate_n_flags;
-
- if (!priv->last_phy_res_valid) {
- IWL_ERR(priv, "MPDU frame without cached PHY data\n");
- return;
- }
- phy_res = &priv->last_phy_res;
- amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data;
- header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu));
- len = le16_to_cpu(amsdu->byte_count);
- rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len);
- ampdu_status = iwlagn_translate_rx_status(priv,
- le32_to_cpu(rx_pkt_status));
-
- if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
- IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n",
- phy_res->cfg_phy_cnt);
- return;
- }
-
- if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) ||
- !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
- IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n",
- le32_to_cpu(rx_pkt_status));
- return;
- }
-
- /* This will be used in several places later */
- rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);
-
- /* rx_status carries information about the packet to mac80211 */
- rx_status.mactime = le64_to_cpu(phy_res->timestamp);
- rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
- IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
- rx_status.freq =
- ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel),
- rx_status.band);
- rx_status.rate_idx =
- iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band);
- rx_status.flag = 0;
-
- /* TSF isn't reliable. In order to allow smooth user experience,
- * this W/A doesn't propagate it to the mac80211 */
- /*rx_status.flag |= RX_FLAG_MACTIME_START;*/
-
- priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp);
-
- /* Find max signal strength (dBm) among 3 antenna/receiver chains */
- rx_status.signal = iwlagn_calc_rssi(priv, phy_res);
-
- IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n",
- rx_status.signal, (unsigned long long)rx_status.mactime);
-
- /*
- * "antenna number"
- *
- * It seems that the antenna field in the phy flags value
- * is actually a bit field. This is undefined by radiotap,
- * it wants an actual antenna number but I always get "7"
- * for most legacy frames I receive indicating that the
- * same frame was received on all three RX chains.
- *
- * I think this field should be removed in favor of a
- * new 802.11n radiotap field "RX chains" that is defined
- * as a bitmask.
- */
- rx_status.antenna =
- (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK)
- >> RX_RES_PHY_FLAGS_ANTENNA_POS;
-
- /* set the preamble flag if appropriate */
- if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
- rx_status.flag |= RX_FLAG_SHORTPRE;
-
- if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
- /*
- * We know which subframes of an A-MPDU belong
- * together since we get a single PHY response
- * from the firmware for all of them
- */
- rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
- rx_status.ampdu_reference = priv->ampdu_ref;
- }
-
- /* Set up the HT phy flags */
- if (rate_n_flags & RATE_MCS_HT_MSK)
- rx_status.flag |= RX_FLAG_HT;
- if (rate_n_flags & RATE_MCS_HT40_MSK)
- rx_status.flag |= RX_FLAG_40MHZ;
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- rx_status.flag |= RX_FLAG_SHORT_GI;
- if (rate_n_flags & RATE_MCS_GF_MSK)
- rx_status.flag |= RX_FLAG_HT_GF;
-
- iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status,
- rxb, &rx_status);
-}
-
-static void iwlagn_rx_noa_notification(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_wipan_noa_data *new_data, *old_data;
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->data;
-
- /* no condition -- we're in softirq */
- old_data = rcu_dereference_protected(priv->noa_data, true);
-
- if (noa_notif->noa_active) {
- u32 len = le16_to_cpu(noa_notif->noa_attribute.length);
- u32 copylen = len;
-
- /* EID, len, OUI, subtype */
- len += 1 + 1 + 3 + 1;
- /* P2P id, P2P length */
- len += 1 + 2;
- copylen += 1 + 2;
-
- new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC);
- if (new_data) {
- new_data->length = len;
- new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC;
- new_data->data[1] = len - 2; /* not counting EID, len */
- new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
- new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
- new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
- new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P;
- memcpy(&new_data->data[6], &noa_notif->noa_attribute,
- copylen);
- }
- } else
- new_data = NULL;
-
- rcu_assign_pointer(priv->noa_data, new_data);
-
- if (old_data)
- kfree_rcu(old_data, rcu_head);
-}
-
-/**
- * iwl_setup_rx_handlers - Initialize Rx handler callbacks
- *
- * Setup the RX handlers for each of the reply types sent from the uCode
- * to the host.
- */
-void iwl_setup_rx_handlers(struct iwl_priv *priv)
-{
- void (**handlers)(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb);
-
- handlers = priv->rx_handlers;
-
- handlers[REPLY_ERROR] = iwlagn_rx_reply_error;
- handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa;
- handlers[SPECTRUM_MEASURE_NOTIFICATION] =
- iwlagn_rx_spectrum_measure_notif;
- handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif;
- handlers[PM_DEBUG_STATISTIC_NOTIFIC] =
- iwlagn_rx_pm_debug_statistics_notif;
- handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif;
- handlers[REPLY_ADD_STA] = iwl_add_sta_callback;
-
- handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification;
-
- /*
- * The same handler is used for both the REPLY to a discrete
- * statistics request from the host as well as for the periodic
- * statistics notifications (after received beacons) from the uCode.
- */
- handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics;
- handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics;
-
- iwl_setup_rx_scan_handlers(priv);
-
- handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif;
- handlers[MISSED_BEACONS_NOTIFICATION] =
- iwlagn_rx_missed_beacon_notif;
-
- /* Rx handlers */
- handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy;
- handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx;
-
- /* block ack */
- handlers[REPLY_COMPRESSED_BA] =
- iwlagn_rx_reply_compressed_ba;
-
- priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
-
- /* set up notification wait support */
- iwl_notification_wait_init(&priv->notif_wait);
-
- /* Set up BT Rx handlers */
- if (priv->lib->bt_params)
- iwlagn_bt_rx_handler_setup(priv);
-}
-
-void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- /*
- * Do the notification wait before RX handlers so
- * even if the RX handler consumes the RXB we have
- * access to it in the notification wait entry.
- */
- iwl_notification_wait_notify(&priv->notif_wait, pkt);
-
- /* Based on type of command response or notification,
- * handle those that need handling via function in
- * rx_handlers table. See iwl_setup_rx_handlers() */
- if (priv->rx_handlers[pkt->hdr.cmd]) {
- priv->rx_handlers_stats[pkt->hdr.cmd]++;
- priv->rx_handlers[pkt->hdr.cmd](priv, rxb);
- } else {
- /* No handling needed */
- IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n",
- iwl_dvm_get_cmd_string(pkt->hdr.cmd),
- pkt->hdr.cmd);
- }
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c
deleted file mode 100644
index 85ceceb34fcc..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/rxon.c
+++ /dev/null
@@ -1,1572 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/etherdevice.h>
-#include "iwl-trans.h"
-#include "iwl-modparams.h"
-#include "dev.h"
-#include "agn.h"
-#include "calib.h"
-
-/*
- * initialize rxon structure with default values from eeprom
- */
-void iwl_connection_init_rx_config(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- memset(&ctx->staging, 0, sizeof(ctx->staging));
-
- if (!ctx->vif) {
- ctx->staging.dev_type = ctx->unused_devtype;
- } else
- switch (ctx->vif->type) {
- case NL80211_IFTYPE_AP:
- ctx->staging.dev_type = ctx->ap_devtype;
- break;
-
- case NL80211_IFTYPE_STATION:
- ctx->staging.dev_type = ctx->station_devtype;
- ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
- break;
-
- case NL80211_IFTYPE_ADHOC:
- ctx->staging.dev_type = ctx->ibss_devtype;
- ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
- ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK |
- RXON_FILTER_ACCEPT_GRP_MSK;
- break;
-
- case NL80211_IFTYPE_MONITOR:
- ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER;
- break;
-
- default:
- IWL_ERR(priv, "Unsupported interface type %d\n",
- ctx->vif->type);
- break;
- }
-
-#if 0
- /* TODO: Figure out when short_preamble would be set and cache from
- * that */
- if (!hw_to_local(priv->hw)->short_preamble)
- ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
- else
- ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
-#endif
-
- ctx->staging.channel =
- cpu_to_le16(priv->hw->conf.chandef.chan->hw_value);
- priv->band = priv->hw->conf.chandef.chan->band;
-
- iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif);
-
- /* clear both MIX and PURE40 mode flag */
- ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED |
- RXON_FLG_CHANNEL_MODE_PURE_40);
- if (ctx->vif)
- memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN);
-
- ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff;
- ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff;
- ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff;
-}
-
-static int iwlagn_disable_bss(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct iwl_rxon_cmd *send)
-{
- __le32 old_filter = send->filter_flags;
- int ret;
-
- send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
- 0, sizeof(*send), send);
-
- send->filter_flags = old_filter;
-
- if (ret)
- IWL_DEBUG_QUIET_RFKILL(priv,
- "Error clearing ASSOC_MSK on BSS (%d)\n", ret);
-
- return ret;
-}
-
-static int iwlagn_disable_pan(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct iwl_rxon_cmd *send)
-{
- struct iwl_notification_wait disable_wait;
- __le32 old_filter = send->filter_flags;
- u8 old_dev_type = send->dev_type;
- int ret;
- static const u16 deactivate_cmd[] = {
- REPLY_WIPAN_DEACTIVATION_COMPLETE
- };
-
- iwl_init_notification_wait(&priv->notif_wait, &disable_wait,
- deactivate_cmd, ARRAY_SIZE(deactivate_cmd),
- NULL, NULL);
-
- send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- send->dev_type = RXON_DEV_TYPE_P2P;
- ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
- 0, sizeof(*send), send);
-
- send->filter_flags = old_filter;
- send->dev_type = old_dev_type;
-
- if (ret) {
- IWL_ERR(priv, "Error disabling PAN (%d)\n", ret);
- iwl_remove_notification(&priv->notif_wait, &disable_wait);
- } else {
- ret = iwl_wait_notification(&priv->notif_wait,
- &disable_wait, HZ);
- if (ret)
- IWL_ERR(priv, "Timed out waiting for PAN disable\n");
- }
-
- return ret;
-}
-
-static int iwlagn_disconn_pan(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct iwl_rxon_cmd *send)
-{
- __le32 old_filter = send->filter_flags;
- int ret;
-
- send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
- sizeof(*send), send);
-
- send->filter_flags = old_filter;
-
- return ret;
-}
-
-static void iwlagn_update_qos(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- int ret;
-
- if (!ctx->is_active)
- return;
-
- ctx->qos_data.def_qos_parm.qos_flags = 0;
-
- if (ctx->qos_data.qos_active)
- ctx->qos_data.def_qos_parm.qos_flags |=
- QOS_PARAM_FLG_UPDATE_EDCA_MSK;
-
- if (ctx->ht.enabled)
- ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
-
- IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
- ctx->qos_data.qos_active,
- ctx->qos_data.def_qos_parm.qos_flags);
-
- ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0,
- sizeof(struct iwl_qosparam_cmd),
- &ctx->qos_data.def_qos_parm);
- if (ret)
- IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n");
-}
-
-static int iwlagn_update_beacon(struct iwl_priv *priv,
- struct ieee80211_vif *vif)
-{
- lockdep_assert_held(&priv->mutex);
-
- dev_kfree_skb(priv->beacon_skb);
- priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif);
- if (!priv->beacon_skb)
- return -ENOMEM;
- return iwlagn_send_beacon_cmd(priv);
-}
-
-static int iwlagn_send_rxon_assoc(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- int ret = 0;
- struct iwl_rxon_assoc_cmd rxon_assoc;
- const struct iwl_rxon_cmd *rxon1 = &ctx->staging;
- const struct iwl_rxon_cmd *rxon2 = &ctx->active;
-
- if ((rxon1->flags == rxon2->flags) &&
- (rxon1->filter_flags == rxon2->filter_flags) &&
- (rxon1->cck_basic_rates == rxon2->cck_basic_rates) &&
- (rxon1->ofdm_ht_single_stream_basic_rates ==
- rxon2->ofdm_ht_single_stream_basic_rates) &&
- (rxon1->ofdm_ht_dual_stream_basic_rates ==
- rxon2->ofdm_ht_dual_stream_basic_rates) &&
- (rxon1->ofdm_ht_triple_stream_basic_rates ==
- rxon2->ofdm_ht_triple_stream_basic_rates) &&
- (rxon1->acquisition_data == rxon2->acquisition_data) &&
- (rxon1->rx_chain == rxon2->rx_chain) &&
- (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) {
- IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC. Not resending.\n");
- return 0;
- }
-
- rxon_assoc.flags = ctx->staging.flags;
- rxon_assoc.filter_flags = ctx->staging.filter_flags;
- rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates;
- rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates;
- rxon_assoc.reserved1 = 0;
- rxon_assoc.reserved2 = 0;
- rxon_assoc.reserved3 = 0;
- rxon_assoc.ofdm_ht_single_stream_basic_rates =
- ctx->staging.ofdm_ht_single_stream_basic_rates;
- rxon_assoc.ofdm_ht_dual_stream_basic_rates =
- ctx->staging.ofdm_ht_dual_stream_basic_rates;
- rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain;
- rxon_assoc.ofdm_ht_triple_stream_basic_rates =
- ctx->staging.ofdm_ht_triple_stream_basic_rates;
- rxon_assoc.acquisition_data = ctx->staging.acquisition_data;
-
- ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd,
- CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc);
- return ret;
-}
-
-static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
-{
- u16 new_val;
- u16 beacon_factor;
-
- /*
- * If mac80211 hasn't given us a beacon interval, program
- * the default into the device (not checking this here
- * would cause the adjustment below to return the maximum
- * value, which may break PAN.)
- */
- if (!beacon_val)
- return DEFAULT_BEACON_INTERVAL;
-
- /*
- * If the beacon interval we obtained from the peer
- * is too large, we'll have to wake up more often
- * (and in IBSS case, we'll beacon too much)
- *
- * For example, if max_beacon_val is 4096, and the
- * requested beacon interval is 7000, we'll have to
- * use 3500 to be able to wake up on the beacons.
- *
- * This could badly influence beacon detection stats.
- */
-
- beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
- new_val = beacon_val / beacon_factor;
-
- if (!new_val)
- new_val = max_beacon_val;
-
- return new_val;
-}
-
-static int iwl_send_rxon_timing(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- u64 tsf;
- s32 interval_tm, rem;
- struct ieee80211_conf *conf = NULL;
- u16 beacon_int;
- struct ieee80211_vif *vif = ctx->vif;
-
- conf = &priv->hw->conf;
-
- lockdep_assert_held(&priv->mutex);
-
- memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd));
-
- ctx->timing.timestamp = cpu_to_le64(priv->timestamp);
- ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval);
-
- beacon_int = vif ? vif->bss_conf.beacon_int : 0;
-
- /*
- * TODO: For IBSS we need to get atim_window from mac80211,
- * for now just always use 0
- */
- ctx->timing.atim_window = 0;
-
- if (ctx->ctxid == IWL_RXON_CTX_PAN &&
- (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) &&
- iwl_is_associated(priv, IWL_RXON_CTX_BSS) &&
- priv->contexts[IWL_RXON_CTX_BSS].vif &&
- priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) {
- ctx->timing.beacon_interval =
- priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval;
- beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
- } else if (ctx->ctxid == IWL_RXON_CTX_BSS &&
- iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
- priv->contexts[IWL_RXON_CTX_PAN].vif &&
- priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int &&
- (!iwl_is_associated_ctx(ctx) || !ctx->vif ||
- !ctx->vif->bss_conf.beacon_int)) {
- ctx->timing.beacon_interval =
- priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval;
- beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
- } else {
- beacon_int = iwl_adjust_beacon_interval(beacon_int,
- IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT);
- ctx->timing.beacon_interval = cpu_to_le16(beacon_int);
- }
-
- ctx->beacon_int = beacon_int;
-
- tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */
- interval_tm = beacon_int * TIME_UNIT;
- rem = do_div(tsf, interval_tm);
- ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
-
- ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1;
-
- IWL_DEBUG_ASSOC(priv,
- "beacon interval %d beacon timer %d beacon tim %d\n",
- le16_to_cpu(ctx->timing.beacon_interval),
- le32_to_cpu(ctx->timing.beacon_init_val),
- le16_to_cpu(ctx->timing.atim_window));
-
- return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
- 0, sizeof(ctx->timing), &ctx->timing);
-}
-
-static int iwlagn_rxon_disconn(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- int ret;
- struct iwl_rxon_cmd *active = (void *)&ctx->active;
-
- if (ctx->ctxid == IWL_RXON_CTX_BSS) {
- ret = iwlagn_disable_bss(priv, ctx, &ctx->staging);
- } else {
- ret = iwlagn_disable_pan(priv, ctx, &ctx->staging);
- if (ret)
- return ret;
- if (ctx->vif) {
- ret = iwl_send_rxon_timing(priv, ctx);
- if (ret) {
- IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
- return ret;
- }
- ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging);
- }
- }
- if (ret)
- return ret;
-
- /*
- * Un-assoc RXON clears the station table and WEP
- * keys, so we have to restore those afterwards.
- */
- iwl_clear_ucode_stations(priv, ctx);
- /* update -- might need P2P now */
- iwl_update_bcast_station(priv, ctx);
- iwl_restore_stations(priv, ctx);
- ret = iwl_restore_default_wep_keys(priv, ctx);
- if (ret) {
- IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret);
- return ret;
- }
-
- memcpy(active, &ctx->staging, sizeof(*active));
- return 0;
-}
-
-static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
-{
- int ret;
- s8 prev_tx_power;
- bool defer;
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
-
- if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED)
- return 0;
-
- lockdep_assert_held(&priv->mutex);
-
- if (priv->tx_power_user_lmt == tx_power && !force)
- return 0;
-
- if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) {
- IWL_WARN(priv,
- "Requested user TXPOWER %d below lower limit %d.\n",
- tx_power,
- IWLAGN_TX_POWER_TARGET_POWER_MIN);
- return -EINVAL;
- }
-
- if (tx_power > DIV_ROUND_UP(priv->nvm_data->max_tx_pwr_half_dbm, 2)) {
- IWL_WARN(priv,
- "Requested user TXPOWER %d above upper limit %d.\n",
- tx_power, priv->nvm_data->max_tx_pwr_half_dbm);
- return -EINVAL;
- }
-
- if (!iwl_is_ready_rf(priv))
- return -EIO;
-
- /* scan complete and commit_rxon use tx_power_next value,
- * it always need to be updated for newest request */
- priv->tx_power_next = tx_power;
-
- /* do not set tx power when scanning or channel changing */
- defer = test_bit(STATUS_SCANNING, &priv->status) ||
- memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging));
- if (defer && !force) {
- IWL_DEBUG_INFO(priv, "Deferring tx power set\n");
- return 0;
- }
-
- prev_tx_power = priv->tx_power_user_lmt;
- priv->tx_power_user_lmt = tx_power;
-
- ret = iwlagn_send_tx_power(priv);
-
- /* if fail to set tx_power, restore the orig. tx power */
- if (ret) {
- priv->tx_power_user_lmt = prev_tx_power;
- priv->tx_power_next = prev_tx_power;
- }
- return ret;
-}
-
-static int iwlagn_rxon_connect(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- int ret;
- struct iwl_rxon_cmd *active = (void *)&ctx->active;
-
- /* RXON timing must be before associated RXON */
- if (ctx->ctxid == IWL_RXON_CTX_BSS) {
- ret = iwl_send_rxon_timing(priv, ctx);
- if (ret) {
- IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
- return ret;
- }
- }
- /* QoS info may be cleared by previous un-assoc RXON */
- iwlagn_update_qos(priv, ctx);
-
- /*
- * We'll run into this code path when beaconing is
- * enabled, but then we also need to send the beacon
- * to the device.
- */
- if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) {
- ret = iwlagn_update_beacon(priv, ctx->vif);
- if (ret) {
- IWL_ERR(priv,
- "Error sending required beacon (%d)!\n",
- ret);
- return ret;
- }
- }
-
- priv->start_calib = 0;
- /*
- * Apply the new configuration.
- *
- * Associated RXON doesn't clear the station table in uCode,
- * so we don't need to restore stations etc. after this.
- */
- ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
- sizeof(struct iwl_rxon_cmd), &ctx->staging);
- if (ret) {
- IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
- return ret;
- }
- memcpy(active, &ctx->staging, sizeof(*active));
-
- /* IBSS beacon needs to be sent after setting assoc */
- if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC))
- if (iwlagn_update_beacon(priv, ctx->vif))
- IWL_ERR(priv, "Error sending IBSS beacon\n");
- iwl_init_sensitivity(priv);
-
- /*
- * If we issue a new RXON command which required a tune then
- * we must send a new TXPOWER command or we won't be able to
- * Tx any frames.
- *
- * It's expected we set power here if channel is changing.
- */
- ret = iwl_set_tx_power(priv, priv->tx_power_next, true);
- if (ret) {
- IWL_ERR(priv, "Error sending TX power (%d)\n", ret);
- return ret;
- }
-
- if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION &&
- priv->cfg->ht_params && priv->cfg->ht_params->smps_mode)
- ieee80211_request_smps(ctx->vif,
- priv->cfg->ht_params->smps_mode);
-
- return 0;
-}
-
-int iwlagn_set_pan_params(struct iwl_priv *priv)
-{
- struct iwl_wipan_params_cmd cmd;
- struct iwl_rxon_context *ctx_bss, *ctx_pan;
- int slot0 = 300, slot1 = 0;
- int ret;
-
- if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS))
- return 0;
-
- BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
-
- lockdep_assert_held(&priv->mutex);
-
- ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS];
- ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN];
-
- /*
- * If the PAN context is inactive, then we don't need
- * to update the PAN parameters, the last thing we'll
- * have done before it goes inactive is making the PAN
- * parameters be WLAN-only.
- */
- if (!ctx_pan->is_active)
- return 0;
-
- memset(&cmd, 0, sizeof(cmd));
-
- /* only 2 slots are currently allowed */
- cmd.num_slots = 2;
-
- cmd.slots[0].type = 0; /* BSS */
- cmd.slots[1].type = 1; /* PAN */
-
- if (ctx_bss->vif && ctx_pan->vif) {
- int bcnint = ctx_pan->beacon_int;
- int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
-
- /* should be set, but seems unused?? */
- cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE);
-
- if (ctx_pan->vif->type == NL80211_IFTYPE_AP &&
- bcnint &&
- bcnint != ctx_bss->beacon_int) {
- IWL_ERR(priv,
- "beacon intervals don't match (%d, %d)\n",
- ctx_bss->beacon_int, ctx_pan->beacon_int);
- } else
- bcnint = max_t(int, bcnint,
- ctx_bss->beacon_int);
- if (!bcnint)
- bcnint = DEFAULT_BEACON_INTERVAL;
- slot0 = bcnint / 2;
- slot1 = bcnint - slot0;
-
- if (test_bit(STATUS_SCAN_HW, &priv->status) ||
- (!ctx_bss->vif->bss_conf.idle &&
- !ctx_bss->vif->bss_conf.assoc)) {
- slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
- slot1 = IWL_MIN_SLOT_TIME;
- } else if (!ctx_pan->vif->bss_conf.idle &&
- !ctx_pan->vif->bss_conf.assoc) {
- slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
- slot0 = IWL_MIN_SLOT_TIME;
- }
- } else if (ctx_pan->vif) {
- slot0 = 0;
- slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) *
- ctx_pan->beacon_int;
- slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1);
-
- if (test_bit(STATUS_SCAN_HW, &priv->status)) {
- slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME;
- slot1 = IWL_MIN_SLOT_TIME;
- }
- }
-
- cmd.slots[0].width = cpu_to_le16(slot0);
- cmd.slots[1].width = cpu_to_le16(slot1);
-
- ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0,
- sizeof(cmd), &cmd);
- if (ret)
- IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret);
-
- return ret;
-}
-
-static void _iwl_set_rxon_ht(struct iwl_priv *priv,
- struct iwl_ht_config *ht_conf,
- struct iwl_rxon_context *ctx)
-{
- struct iwl_rxon_cmd *rxon = &ctx->staging;
-
- if (!ctx->ht.enabled) {
- rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
- RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
- RXON_FLG_HT40_PROT_MSK |
- RXON_FLG_HT_PROT_MSK);
- return;
- }
-
- /* FIXME: if the definition of ht.protection changed, the "translation"
- * will be needed for rxon->flags
- */
- rxon->flags |= cpu_to_le32(ctx->ht.protection <<
- RXON_FLG_HT_OPERATING_MODE_POS);
-
- /* Set up channel bandwidth:
- * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
- /* clear the HT channel mode before set the mode */
- rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
- RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
- if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) {
- /* pure ht40 */
- if (ctx->ht.protection ==
- IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
- rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
- /*
- * Note: control channel is opposite of extension
- * channel
- */
- switch (ctx->ht.extension_chan_offset) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- rxon->flags &=
- ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- rxon->flags |=
- RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
- break;
- }
- } else {
- /*
- * Note: control channel is opposite of extension
- * channel
- */
- switch (ctx->ht.extension_chan_offset) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- rxon->flags &=
- ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
- rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
- rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_NONE:
- default:
- /*
- * channel location only valid if in Mixed
- * mode
- */
- IWL_ERR(priv,
- "invalid extension channel offset\n");
- break;
- }
- }
- } else {
- rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
- }
-
- iwlagn_set_rxon_chain(priv, ctx);
-
- IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X "
- "extension channel offset 0x%x\n",
- le32_to_cpu(rxon->flags), ctx->ht.protection,
- ctx->ht.extension_chan_offset);
-}
-
-void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf)
-{
- struct iwl_rxon_context *ctx;
-
- for_each_context(priv, ctx)
- _iwl_set_rxon_ht(priv, ht_conf, ctx);
-}
-
-/**
- * iwl_set_rxon_channel - Set the band and channel values in staging RXON
- * @ch: requested channel as a pointer to struct ieee80211_channel
-
- * NOTE: Does not commit to the hardware; it sets appropriate bit fields
- * in the staging RXON flag structure based on the ch->band
- */
-void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch,
- struct iwl_rxon_context *ctx)
-{
- enum ieee80211_band band = ch->band;
- u16 channel = ch->hw_value;
-
- if ((le16_to_cpu(ctx->staging.channel) == channel) &&
- (priv->band == band))
- return;
-
- ctx->staging.channel = cpu_to_le16(channel);
- if (band == IEEE80211_BAND_5GHZ)
- ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK;
- else
- ctx->staging.flags |= RXON_FLG_BAND_24G_MSK;
-
- priv->band = band;
-
- IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band);
-
-}
-
-void iwl_set_flags_for_band(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- enum ieee80211_band band,
- struct ieee80211_vif *vif)
-{
- if (band == IEEE80211_BAND_5GHZ) {
- ctx->staging.flags &=
- ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK
- | RXON_FLG_CCK_MSK);
- ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
- } else {
- /* Copied from iwl_post_associate() */
- if (vif && vif->bss_conf.use_short_slot)
- ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
- else
- ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
-
- ctx->staging.flags |= RXON_FLG_BAND_24G_MSK;
- ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK;
- ctx->staging.flags &= ~RXON_FLG_CCK_MSK;
- }
-}
-
-static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx, int hw_decrypt)
-{
- struct iwl_rxon_cmd *rxon = &ctx->staging;
-
- if (hw_decrypt)
- rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
- else
- rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
-
-}
-
-/* validate RXON structure is valid */
-static int iwl_check_rxon_cmd(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- struct iwl_rxon_cmd *rxon = &ctx->staging;
- u32 errors = 0;
-
- if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
- if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
- IWL_WARN(priv, "check 2.4G: wrong narrow\n");
- errors |= BIT(0);
- }
- if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
- IWL_WARN(priv, "check 2.4G: wrong radar\n");
- errors |= BIT(1);
- }
- } else {
- if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
- IWL_WARN(priv, "check 5.2G: not short slot!\n");
- errors |= BIT(2);
- }
- if (rxon->flags & RXON_FLG_CCK_MSK) {
- IWL_WARN(priv, "check 5.2G: CCK!\n");
- errors |= BIT(3);
- }
- }
- if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
- IWL_WARN(priv, "mac/bssid mcast!\n");
- errors |= BIT(4);
- }
-
- /* make sure basic rates 6Mbps and 1Mbps are supported */
- if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 &&
- (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) {
- IWL_WARN(priv, "neither 1 nor 6 are basic\n");
- errors |= BIT(5);
- }
-
- if (le16_to_cpu(rxon->assoc_id) > 2007) {
- IWL_WARN(priv, "aid > 2007\n");
- errors |= BIT(6);
- }
-
- if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
- == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
- IWL_WARN(priv, "CCK and short slot\n");
- errors |= BIT(7);
- }
-
- if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
- == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
- IWL_WARN(priv, "CCK and auto detect\n");
- errors |= BIT(8);
- }
-
- if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
- RXON_FLG_TGG_PROTECT_MSK)) ==
- RXON_FLG_TGG_PROTECT_MSK) {
- IWL_WARN(priv, "TGg but no auto-detect\n");
- errors |= BIT(9);
- }
-
- if (rxon->channel == 0) {
- IWL_WARN(priv, "zero channel is invalid\n");
- errors |= BIT(10);
- }
-
- WARN(errors, "Invalid RXON (%#x), channel %d",
- errors, le16_to_cpu(rxon->channel));
-
- return errors ? -EINVAL : 0;
-}
-
-/**
- * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
- * @priv: staging_rxon is compared to active_rxon
- *
- * If the RXON structure is changing enough to require a new tune,
- * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
- * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
- */
-static int iwl_full_rxon_required(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- const struct iwl_rxon_cmd *staging = &ctx->staging;
- const struct iwl_rxon_cmd *active = &ctx->active;
-
-#define CHK(cond) \
- if ((cond)) { \
- IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \
- return 1; \
- }
-
-#define CHK_NEQ(c1, c2) \
- if ((c1) != (c2)) { \
- IWL_DEBUG_INFO(priv, "need full RXON - " \
- #c1 " != " #c2 " - %d != %d\n", \
- (c1), (c2)); \
- return 1; \
- }
-
- /* These items are only settable from the full RXON command */
- CHK(!iwl_is_associated_ctx(ctx));
- CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr));
- CHK(!ether_addr_equal(staging->node_addr, active->node_addr));
- CHK(!ether_addr_equal(staging->wlap_bssid_addr,
- active->wlap_bssid_addr));
- CHK_NEQ(staging->dev_type, active->dev_type);
- CHK_NEQ(staging->channel, active->channel);
- CHK_NEQ(staging->air_propagation, active->air_propagation);
- CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
- active->ofdm_ht_single_stream_basic_rates);
- CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
- active->ofdm_ht_dual_stream_basic_rates);
- CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates,
- active->ofdm_ht_triple_stream_basic_rates);
- CHK_NEQ(staging->assoc_id, active->assoc_id);
-
- /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
- * be updated with the RXON_ASSOC command -- however only some
- * flag transitions are allowed using RXON_ASSOC */
-
- /* Check if we are not switching bands */
- CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
- active->flags & RXON_FLG_BAND_24G_MSK);
-
- /* Check if we are switching association toggle */
- CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
- active->filter_flags & RXON_FILTER_ASSOC_MSK);
-
-#undef CHK
-#undef CHK_NEQ
-
- return 0;
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-void iwl_print_rx_config_cmd(struct iwl_priv *priv,
- enum iwl_rxon_context_id ctxid)
-{
- struct iwl_rxon_context *ctx = &priv->contexts[ctxid];
- struct iwl_rxon_cmd *rxon = &ctx->staging;
-
- IWL_DEBUG_RADIO(priv, "RX CONFIG:\n");
- iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
- IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n",
- le16_to_cpu(rxon->channel));
- IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n",
- le32_to_cpu(rxon->flags));
- IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n",
- le32_to_cpu(rxon->filter_flags));
- IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type);
- IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n",
- rxon->ofdm_basic_rates);
- IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n",
- rxon->cck_basic_rates);
- IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr);
- IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
- IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n",
- le16_to_cpu(rxon->assoc_id));
-}
-#endif
-
-static void iwl_calc_basic_rates(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- int lowest_present_ofdm = 100;
- int lowest_present_cck = 100;
- u8 cck = 0;
- u8 ofdm = 0;
-
- if (ctx->vif) {
- struct ieee80211_supported_band *sband;
- unsigned long basic = ctx->vif->bss_conf.basic_rates;
- int i;
-
- sband = priv->hw->wiphy->bands[priv->hw->conf.chandef.chan->band];
-
- for_each_set_bit(i, &basic, BITS_PER_LONG) {
- int hw = sband->bitrates[i].hw_value;
- if (hw >= IWL_FIRST_OFDM_RATE) {
- ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
- if (lowest_present_ofdm > hw)
- lowest_present_ofdm = hw;
- } else {
- BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
-
- cck |= BIT(hw);
- if (lowest_present_cck > hw)
- lowest_present_cck = hw;
- }
- }
- }
-
- /*
- * Now we've got the basic rates as bitmaps in the ofdm and cck
- * variables. This isn't sufficient though, as there might not
- * be all the right rates in the bitmap. E.g. if the only basic
- * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
- * and 6 Mbps because the 802.11-2007 standard says in 9.6:
- *
- * [...] a STA responding to a received frame shall transmit
- * its Control Response frame [...] at the highest rate in the
- * BSSBasicRateSet parameter that is less than or equal to the
- * rate of the immediately previous frame in the frame exchange
- * sequence ([...]) and that is of the same modulation class
- * ([...]) as the received frame. If no rate contained in the
- * BSSBasicRateSet parameter meets these conditions, then the
- * control frame sent in response to a received frame shall be
- * transmitted at the highest mandatory rate of the PHY that is
- * less than or equal to the rate of the received frame, and
- * that is of the same modulation class as the received frame.
- *
- * As a consequence, we need to add all mandatory rates that are
- * lower than all of the basic rates to these bitmaps.
- */
-
- if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
- ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE;
- if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
- ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE;
- /* 6M already there or needed so always add */
- ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE;
-
- /*
- * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
- * Note, however:
- * - if no CCK rates are basic, it must be ERP since there must
- * be some basic rates at all, so they're OFDM => ERP PHY
- * (or we're in 5 GHz, and the cck bitmap will never be used)
- * - if 11M is a basic rate, it must be ERP as well, so add 5.5M
- * - if 5.5M is basic, 1M and 2M are mandatory
- * - if 2M is basic, 1M is mandatory
- * - if 1M is basic, that's the only valid ACK rate.
- * As a consequence, it's not as complicated as it sounds, just add
- * any lower rates to the ACK rate bitmap.
- */
- if (IWL_RATE_11M_INDEX < lowest_present_cck)
- cck |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE;
- if (IWL_RATE_5M_INDEX < lowest_present_cck)
- cck |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE;
- if (IWL_RATE_2M_INDEX < lowest_present_cck)
- cck |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE;
- /* 1M already there or needed so always add */
- cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE;
-
- IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n",
- cck, ofdm);
-
- /* "basic_rates" is a misnomer here -- should be called ACK rates */
- ctx->staging.cck_basic_rates = cck;
- ctx->staging.ofdm_basic_rates = ofdm;
-}
-
-/**
- * iwlagn_commit_rxon - commit staging_rxon to hardware
- *
- * The RXON command in staging_rxon is committed to the hardware and
- * the active_rxon structure is updated with the new data. This
- * function correctly transitions out of the RXON_ASSOC_MSK state if
- * a HW tune is required based on the RXON structure changes.
- *
- * The connect/disconnect flow should be as the following:
- *
- * 1. make sure send RXON command with association bit unset if not connect
- * this should include the channel and the band for the candidate
- * to be connected to
- * 2. Add Station before RXON association with the AP
- * 3. RXON_timing has to send before RXON for connection
- * 4. full RXON command - associated bit set
- * 5. use RXON_ASSOC command to update any flags changes
- */
-int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- /* cast away the const for active_rxon in this function */
- struct iwl_rxon_cmd *active = (void *)&ctx->active;
- bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK);
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- if (!iwl_is_alive(priv))
- return -EBUSY;
-
- /* This function hardcodes a bunch of dual-mode assumptions */
- BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
-
- if (!ctx->is_active)
- return 0;
-
- /* always get timestamp with Rx frame */
- ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
-
- /* recalculate basic rates */
- iwl_calc_basic_rates(priv, ctx);
-
- /*
- * force CTS-to-self frames protection if RTS-CTS is not preferred
- * one aggregation protection method
- */
- if (!priv->hw_params.use_rts_for_aggregation)
- ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
-
- if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
- !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
- ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
- else
- ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
-
- iwl_print_rx_config_cmd(priv, ctx->ctxid);
- ret = iwl_check_rxon_cmd(priv, ctx);
- if (ret) {
- IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n");
- return -EINVAL;
- }
-
- /*
- * receive commit_rxon request
- * abort any previous channel switch if still in process
- */
- if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) &&
- (priv->switch_channel != ctx->staging.channel)) {
- IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
- le16_to_cpu(priv->switch_channel));
- iwl_chswitch_done(priv, false);
- }
-
- /*
- * If we don't need to send a full RXON, we can use
- * iwl_rxon_assoc_cmd which is used to reconfigure filter
- * and other flags for the current radio configuration.
- */
- if (!iwl_full_rxon_required(priv, ctx)) {
- ret = iwlagn_send_rxon_assoc(priv, ctx);
- if (ret) {
- IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret);
- return ret;
- }
-
- memcpy(active, &ctx->staging, sizeof(*active));
- /*
- * We do not commit tx power settings while channel changing,
- * do it now if after settings changed.
- */
- iwl_set_tx_power(priv, priv->tx_power_next, false);
-
- /* make sure we are in the right PS state */
- iwl_power_update_mode(priv, true);
-
- return 0;
- }
-
- iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.sw_crypto);
-
- IWL_DEBUG_INFO(priv,
- "Going to commit RXON\n"
- " * with%s RXON_FILTER_ASSOC_MSK\n"
- " * channel = %d\n"
- " * bssid = %pM\n",
- (new_assoc ? "" : "out"),
- le16_to_cpu(ctx->staging.channel),
- ctx->staging.bssid_addr);
-
- /*
- * Always clear associated first, but with the correct config.
- * This is required as for example station addition for the
- * AP station must be done after the BSSID is set to correctly
- * set up filters in the device.
- */
- ret = iwlagn_rxon_disconn(priv, ctx);
- if (ret)
- return ret;
-
- ret = iwlagn_set_pan_params(priv);
- if (ret)
- return ret;
-
- if (new_assoc)
- return iwlagn_rxon_connect(priv, ctx);
-
- return 0;
-}
-
-void iwlagn_config_ht40(struct ieee80211_conf *conf,
- struct iwl_rxon_context *ctx)
-{
- if (conf_is_ht40_minus(conf)) {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- ctx->ht.is_40mhz = true;
- } else if (conf_is_ht40_plus(conf)) {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- ctx->ht.is_40mhz = true;
- } else {
- ctx->ht.extension_chan_offset =
- IEEE80211_HT_PARAM_CHA_SEC_NONE;
- ctx->ht.is_40mhz = false;
- }
-}
-
-int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_rxon_context *ctx;
- struct ieee80211_conf *conf = &hw->conf;
- struct ieee80211_channel *channel = conf->chandef.chan;
- int ret = 0;
-
- IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed);
-
- mutex_lock(&priv->mutex);
-
- if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
- IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
- goto out;
- }
-
- if (!iwl_is_ready(priv)) {
- IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
- goto out;
- }
-
- if (changed & (IEEE80211_CONF_CHANGE_SMPS |
- IEEE80211_CONF_CHANGE_CHANNEL)) {
- /* mac80211 uses static for non-HT which is what we want */
- priv->current_ht_config.smps = conf->smps_mode;
-
- /*
- * Recalculate chain counts.
- *
- * If monitor mode is enabled then mac80211 will
- * set up the SM PS mode to OFF if an HT channel is
- * configured.
- */
- for_each_context(priv, ctx)
- iwlagn_set_rxon_chain(priv, ctx);
- }
-
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- for_each_context(priv, ctx) {
- /* Configure HT40 channels */
- if (ctx->ht.enabled != conf_is_ht(conf))
- ctx->ht.enabled = conf_is_ht(conf);
-
- if (ctx->ht.enabled) {
- /* if HT40 is used, it should not change
- * after associated except channel switch */
- if (!ctx->ht.is_40mhz ||
- !iwl_is_associated_ctx(ctx))
- iwlagn_config_ht40(conf, ctx);
- } else
- ctx->ht.is_40mhz = false;
-
- /*
- * Default to no protection. Protection mode will
- * later be set from BSS config in iwl_ht_conf
- */
- ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
-
- /* if we are switching from ht to 2.4 clear flags
- * from any ht related info since 2.4 does not
- * support ht */
- if (le16_to_cpu(ctx->staging.channel) !=
- channel->hw_value)
- ctx->staging.flags = 0;
-
- iwl_set_rxon_channel(priv, channel, ctx);
- iwl_set_rxon_ht(priv, &priv->current_ht_config);
-
- iwl_set_flags_for_band(priv, ctx, channel->band,
- ctx->vif);
- }
-
- iwl_update_bcast_stations(priv);
- }
-
- if (changed & (IEEE80211_CONF_CHANGE_PS |
- IEEE80211_CONF_CHANGE_IDLE)) {
- ret = iwl_power_update_mode(priv, false);
- if (ret)
- IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
- }
-
- if (changed & IEEE80211_CONF_CHANGE_POWER) {
- IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
- priv->tx_power_user_lmt, conf->power_level);
-
- iwl_set_tx_power(priv, conf->power_level, false);
- }
-
- for_each_context(priv, ctx) {
- if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
- continue;
- iwlagn_commit_rxon(priv, ctx);
- }
- out:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- return ret;
-}
-
-static void iwlagn_check_needed_chains(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_bss_conf *bss_conf)
-{
- struct ieee80211_vif *vif = ctx->vif;
- struct iwl_rxon_context *tmp;
- struct ieee80211_sta *sta;
- struct iwl_ht_config *ht_conf = &priv->current_ht_config;
- struct ieee80211_sta_ht_cap *ht_cap;
- bool need_multiple;
-
- lockdep_assert_held(&priv->mutex);
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- rcu_read_lock();
- sta = ieee80211_find_sta(vif, bss_conf->bssid);
- if (!sta) {
- /*
- * If at all, this can only happen through a race
- * when the AP disconnects us while we're still
- * setting up the connection, in that case mac80211
- * will soon tell us about that.
- */
- need_multiple = false;
- rcu_read_unlock();
- break;
- }
-
- ht_cap = &sta->ht_cap;
-
- need_multiple = true;
-
- /*
- * If the peer advertises no support for receiving 2 and 3
- * stream MCS rates, it can't be transmitting them either.
- */
- if (ht_cap->mcs.rx_mask[1] == 0 &&
- ht_cap->mcs.rx_mask[2] == 0) {
- need_multiple = false;
- } else if (!(ht_cap->mcs.tx_params &
- IEEE80211_HT_MCS_TX_DEFINED)) {
- /* If it can't TX MCS at all ... */
- need_multiple = false;
- } else if (ht_cap->mcs.tx_params &
- IEEE80211_HT_MCS_TX_RX_DIFF) {
- int maxstreams;
-
- /*
- * But if it can receive them, it might still not
- * be able to transmit them, which is what we need
- * to check here -- so check the number of streams
- * it advertises for TX (if different from RX).
- */
-
- maxstreams = (ht_cap->mcs.tx_params &
- IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK);
- maxstreams >>=
- IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
- maxstreams += 1;
-
- if (maxstreams <= 1)
- need_multiple = false;
- }
-
- rcu_read_unlock();
- break;
- case NL80211_IFTYPE_ADHOC:
- /* currently */
- need_multiple = false;
- break;
- default:
- /* only AP really */
- need_multiple = true;
- break;
- }
-
- ctx->ht_need_multiple_chains = need_multiple;
-
- if (!need_multiple) {
- /* check all contexts */
- for_each_context(priv, tmp) {
- if (!tmp->vif)
- continue;
- if (tmp->ht_need_multiple_chains) {
- need_multiple = true;
- break;
- }
- }
- }
-
- ht_conf->single_chain_sufficient = !need_multiple;
-}
-
-static void iwlagn_chain_noise_reset(struct iwl_priv *priv)
-{
- struct iwl_chain_noise_data *data = &priv->chain_noise_data;
- int ret;
-
- if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
- return;
-
- if ((data->state == IWL_CHAIN_NOISE_ALIVE) &&
- iwl_is_any_associated(priv)) {
- struct iwl_calib_chain_noise_reset_cmd cmd;
-
- /* clear data for chain noise calibration algorithm */
- data->chain_noise_a = 0;
- data->chain_noise_b = 0;
- data->chain_noise_c = 0;
- data->chain_signal_a = 0;
- data->chain_signal_b = 0;
- data->chain_signal_c = 0;
- data->beacon_count = 0;
-
- memset(&cmd, 0, sizeof(cmd));
- iwl_set_calib_hdr(&cmd.hdr,
- priv->phy_calib_chain_noise_reset_cmd);
- ret = iwl_dvm_send_cmd_pdu(priv,
- REPLY_PHY_CALIBRATION_CMD,
- 0, sizeof(cmd), &cmd);
- if (ret)
- IWL_ERR(priv,
- "Could not send REPLY_PHY_CALIBRATION_CMD\n");
- data->state = IWL_CHAIN_NOISE_ACCUMULATE;
- IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n");
- }
-}
-
-void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *bss_conf,
- u32 changes)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
- int ret;
- bool force = false;
-
- mutex_lock(&priv->mutex);
-
- if (changes & BSS_CHANGED_IDLE && bss_conf->idle) {
- /*
- * If we go idle, then clearly no "passive-no-rx"
- * workaround is needed any more, this is a reset.
- */
- iwlagn_lift_passive_no_rx(priv);
- }
-
- if (unlikely(!iwl_is_ready(priv))) {
- IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
- mutex_unlock(&priv->mutex);
- return;
- }
-
- if (unlikely(!ctx->vif)) {
- IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n");
- mutex_unlock(&priv->mutex);
- return;
- }
-
- if (changes & BSS_CHANGED_BEACON_INT)
- force = true;
-
- if (changes & BSS_CHANGED_QOS) {
- ctx->qos_data.qos_active = bss_conf->qos;
- iwlagn_update_qos(priv, ctx);
- }
-
- ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid);
- if (vif->bss_conf.use_short_preamble)
- ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
- else
- ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
-
- if (changes & BSS_CHANGED_ASSOC) {
- if (bss_conf->assoc) {
- priv->timestamp = bss_conf->sync_tsf;
- ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
- } else {
- ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-
- if (ctx->ctxid == IWL_RXON_CTX_BSS)
- priv->have_rekey_data = false;
- }
-
- iwlagn_bt_coex_rssi_monitor(priv);
- }
-
- if (ctx->ht.enabled) {
- ctx->ht.protection = bss_conf->ht_operation_mode &
- IEEE80211_HT_OP_MODE_PROTECTION;
- ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode &
- IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
- iwlagn_check_needed_chains(priv, ctx, bss_conf);
- iwl_set_rxon_ht(priv, &priv->current_ht_config);
- }
-
- iwlagn_set_rxon_chain(priv, ctx);
-
- if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ))
- ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
- else
- ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
-
- if (bss_conf->use_cts_prot)
- ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
- else
- ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
-
- memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
-
- if (vif->type == NL80211_IFTYPE_AP ||
- vif->type == NL80211_IFTYPE_ADHOC) {
- if (vif->bss_conf.enable_beacon) {
- ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
- priv->beacon_ctx = ctx;
- } else {
- ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- priv->beacon_ctx = NULL;
- }
- }
-
- /*
- * If the ucode decides to do beacon filtering before
- * association, it will lose beacons that are needed
- * before sending frames out on passive channels. This
- * causes association failures on those channels. Enable
- * receiving beacons in such cases.
- */
-
- if (vif->type == NL80211_IFTYPE_STATION) {
- if (!bss_conf->assoc)
- ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK;
- else
- ctx->staging.filter_flags &=
- ~RXON_FILTER_BCON_AWARE_MSK;
- }
-
- if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
- iwlagn_commit_rxon(priv, ctx);
-
- if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) {
- /*
- * The chain noise calibration will enable PM upon
- * completion. If calibration has already been run
- * then we need to enable power management here.
- */
- if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE)
- iwl_power_update_mode(priv, false);
-
- /* Enable RX differential gain and sensitivity calibrations */
- iwlagn_chain_noise_reset(priv);
- priv->start_calib = 1;
- }
-
- if (changes & BSS_CHANGED_IBSS) {
- ret = iwlagn_manage_ibss_station(priv, vif,
- bss_conf->ibss_joined);
- if (ret)
- IWL_ERR(priv, "failed to %s IBSS station %pM\n",
- bss_conf->ibss_joined ? "add" : "remove",
- bss_conf->bssid);
- }
-
- if (changes & BSS_CHANGED_BEACON && priv->beacon_ctx == ctx) {
- if (iwlagn_update_beacon(priv, vif))
- IWL_ERR(priv, "Error updating beacon\n");
- }
-
- mutex_unlock(&priv->mutex);
-}
-
-void iwlagn_post_scan(struct iwl_priv *priv)
-{
- struct iwl_rxon_context *ctx;
-
- /*
- * We do not commit power settings while scan is pending,
- * do it now if the settings changed.
- */
- iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false);
- iwl_set_tx_power(priv, priv->tx_power_next, false);
-
- /*
- * Since setting the RXON may have been deferred while
- * performing the scan, fire one off if needed
- */
- for_each_context(priv, ctx)
- if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
- iwlagn_commit_rxon(priv, ctx);
-
- iwlagn_set_pan_params(priv);
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c
deleted file mode 100644
index 648159495bbc..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/scan.c
+++ /dev/null
@@ -1,1075 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/etherdevice.h>
-#include <net/mac80211.h>
-
-#include "dev.h"
-#include "agn.h"
-
-/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
- * sending probe req. This should be set long enough to hear probe responses
- * from more than one AP. */
-#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */
-#define IWL_ACTIVE_DWELL_TIME_52 (20)
-
-#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3)
-#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2)
-
-/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
- * Must be set longer than active dwell time.
- * For the most reliable scan, set > AP beacon interval (typically 100msec). */
-#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */
-#define IWL_PASSIVE_DWELL_TIME_52 (10)
-#define IWL_PASSIVE_DWELL_BASE (100)
-#define IWL_CHANNEL_TUNE_TIME 5
-#define MAX_SCAN_CHANNEL 50
-
-/* For reset radio, need minimal dwell time only */
-#define IWL_RADIO_RESET_DWELL_TIME 5
-
-static int iwl_send_scan_abort(struct iwl_priv *priv)
-{
- int ret;
- struct iwl_host_cmd cmd = {
- .id = REPLY_SCAN_ABORT_CMD,
- .flags = CMD_WANT_SKB,
- };
- __le32 *status;
-
- /* Exit instantly with error when device is not ready
- * to receive scan abort command or it does not perform
- * hardware scan currently */
- if (!test_bit(STATUS_READY, &priv->status) ||
- !test_bit(STATUS_SCAN_HW, &priv->status) ||
- test_bit(STATUS_FW_ERROR, &priv->status))
- return -EIO;
-
- ret = iwl_dvm_send_cmd(priv, &cmd);
- if (ret)
- return ret;
-
- status = (void *)cmd.resp_pkt->data;
- if (*status != CAN_ABORT_STATUS) {
- /* The scan abort will return 1 for success or
- * 2 for "failure". A failure condition can be
- * due to simply not being in an active scan which
- * can occur if we send the scan abort before we
- * the microcode has notified us that a scan is
- * completed. */
- IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n",
- le32_to_cpu(*status));
- ret = -EIO;
- }
-
- iwl_free_resp(&cmd);
- return ret;
-}
-
-static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
-{
- /* check if scan was requested from mac80211 */
- if (priv->scan_request) {
- IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n");
- ieee80211_scan_completed(priv->hw, aborted);
- }
-
- priv->scan_type = IWL_SCAN_NORMAL;
- priv->scan_vif = NULL;
- priv->scan_request = NULL;
-}
-
-static void iwl_process_scan_complete(struct iwl_priv *priv)
-{
- bool aborted;
-
- lockdep_assert_held(&priv->mutex);
-
- if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status))
- return;
-
- IWL_DEBUG_SCAN(priv, "Completed scan.\n");
-
- cancel_delayed_work(&priv->scan_check);
-
- aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status);
- if (aborted)
- IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n");
-
- if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Scan already completed.\n");
- goto out_settings;
- }
-
- if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
- int err;
-
- /* Check if mac80211 requested scan during our internal scan */
- if (priv->scan_request == NULL)
- goto out_complete;
-
- /* If so request a new scan */
- err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL,
- priv->scan_request->channels[0]->band);
- if (err) {
- IWL_DEBUG_SCAN(priv,
- "failed to initiate pending scan: %d\n", err);
- aborted = true;
- goto out_complete;
- }
-
- return;
- }
-
-out_complete:
- iwl_complete_scan(priv, aborted);
-
-out_settings:
- /* Can we still talk to firmware ? */
- if (!iwl_is_ready_rf(priv))
- return;
-
- iwlagn_post_scan(priv);
-}
-
-void iwl_force_scan_end(struct iwl_priv *priv)
-{
- lockdep_assert_held(&priv->mutex);
-
- if (!test_bit(STATUS_SCANNING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n");
- return;
- }
-
- IWL_DEBUG_SCAN(priv, "Forcing scan end\n");
- clear_bit(STATUS_SCANNING, &priv->status);
- clear_bit(STATUS_SCAN_HW, &priv->status);
- clear_bit(STATUS_SCAN_ABORTING, &priv->status);
- clear_bit(STATUS_SCAN_COMPLETE, &priv->status);
- iwl_complete_scan(priv, true);
-}
-
-static void iwl_do_scan_abort(struct iwl_priv *priv)
-{
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- if (!test_bit(STATUS_SCANNING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n");
- return;
- }
-
- if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Scan abort in progress\n");
- return;
- }
-
- ret = iwl_send_scan_abort(priv);
- if (ret) {
- IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret);
- iwl_force_scan_end(priv);
- } else
- IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n");
-}
-
-/**
- * iwl_scan_cancel - Cancel any currently executing HW scan
- */
-int iwl_scan_cancel(struct iwl_priv *priv)
-{
- IWL_DEBUG_SCAN(priv, "Queuing abort scan\n");
- queue_work(priv->workqueue, &priv->abort_scan);
- return 0;
-}
-
-/**
- * iwl_scan_cancel_timeout - Cancel any currently executing HW scan
- * @ms: amount of time to wait (in milliseconds) for scan to abort
- *
- */
-void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(ms);
-
- lockdep_assert_held(&priv->mutex);
-
- IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n");
-
- iwl_do_scan_abort(priv);
-
- while (time_before_eq(jiffies, timeout)) {
- if (!test_bit(STATUS_SCAN_HW, &priv->status))
- goto finished;
- msleep(20);
- }
-
- return;
-
- finished:
- /*
- * Now STATUS_SCAN_HW is clear. This means that the
- * device finished, but the background work is going
- * to execute at best as soon as we release the mutex.
- * Since we need to be able to issue a new scan right
- * after this function returns, run the complete here.
- * The STATUS_SCAN_COMPLETE bit will then be cleared
- * and prevent the background work from "completing"
- * a possible new scan.
- */
- iwl_process_scan_complete(priv);
-}
-
-/* Service response to REPLY_SCAN_CMD (0x80) */
-static void iwl_rx_reply_scan(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
-#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_scanreq_notification *notif = (void *)pkt->data;
-
- IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status);
-#endif
-}
-
-/* Service SCAN_START_NOTIFICATION (0x82) */
-static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_scanstart_notification *notif = (void *)pkt->data;
-
- priv->scan_start_tsf = le32_to_cpu(notif->tsf_low);
- IWL_DEBUG_SCAN(priv, "Scan start: "
- "%d [802.11%s] "
- "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n",
- notif->channel,
- notif->band ? "bg" : "a",
- le32_to_cpu(notif->tsf_high),
- le32_to_cpu(notif->tsf_low),
- notif->status, notif->beacon_timer);
-}
-
-/* Service SCAN_RESULTS_NOTIFICATION (0x83) */
-static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
-#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_scanresults_notification *notif = (void *)pkt->data;
-
- IWL_DEBUG_SCAN(priv, "Scan ch.res: "
- "%d [802.11%s] "
- "probe status: %u:%u "
- "(TSF: 0x%08X:%08X) - %d "
- "elapsed=%lu usec\n",
- notif->channel,
- notif->band ? "bg" : "a",
- notif->probe_status, notif->num_probe_not_sent,
- le32_to_cpu(notif->tsf_high),
- le32_to_cpu(notif->tsf_low),
- le32_to_cpu(notif->statistics[0]),
- le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf);
-#endif
-}
-
-/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
-static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data;
-
- IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
- scan_notif->scanned_channels,
- scan_notif->tsf_low,
- scan_notif->tsf_high, scan_notif->status);
-
- IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n",
- (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
- jiffies_to_msecs(jiffies - priv->scan_start));
-
- /*
- * When aborting, we run the scan completed background work inline
- * and the background work must then do nothing. The SCAN_COMPLETE
- * bit helps implement that logic and thus needs to be set before
- * queueing the work. Also, since the scan abort waits for SCAN_HW
- * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW
- * to avoid a race there.
- */
- set_bit(STATUS_SCAN_COMPLETE, &priv->status);
- clear_bit(STATUS_SCAN_HW, &priv->status);
- queue_work(priv->workqueue, &priv->scan_completed);
-
- if (priv->iw_mode != NL80211_IFTYPE_ADHOC &&
- iwl_advanced_bt_coexist(priv) &&
- priv->bt_status != scan_notif->bt_status) {
- if (scan_notif->bt_status) {
- /* BT on */
- if (!priv->bt_ch_announce)
- priv->bt_traffic_load =
- IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
- /*
- * otherwise, no traffic load information provided
- * no changes made
- */
- } else {
- /* BT off */
- priv->bt_traffic_load =
- IWL_BT_COEX_TRAFFIC_LOAD_NONE;
- }
- priv->bt_status = scan_notif->bt_status;
- queue_work(priv->workqueue,
- &priv->bt_traffic_change_work);
- }
-}
-
-void iwl_setup_rx_scan_handlers(struct iwl_priv *priv)
-{
- /* scan handlers */
- priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan;
- priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif;
- priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] =
- iwl_rx_scan_results_notif;
- priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
- iwl_rx_scan_complete_notif;
-}
-
-static u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
- enum ieee80211_band band, u8 n_probes)
-{
- if (band == IEEE80211_BAND_5GHZ)
- return IWL_ACTIVE_DWELL_TIME_52 +
- IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1);
- else
- return IWL_ACTIVE_DWELL_TIME_24 +
- IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1);
-}
-
-static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time)
-{
- struct iwl_rxon_context *ctx;
- int limits[NUM_IWL_RXON_CTX] = {};
- int n_active = 0;
- u16 limit;
-
- BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
-
- /*
- * If we're associated, we clamp the dwell time 98%
- * of the beacon interval (minus 2 * channel tune time)
- * If both contexts are active, we have to restrict to
- * 1/2 of the minimum of them, because they might be in
- * lock-step with the time inbetween only half of what
- * time we'd have in each of them.
- */
- for_each_context(priv, ctx) {
- switch (ctx->staging.dev_type) {
- case RXON_DEV_TYPE_P2P:
- /* no timing constraints */
- continue;
- case RXON_DEV_TYPE_ESS:
- default:
- /* timing constraints if associated */
- if (!iwl_is_associated_ctx(ctx))
- continue;
- break;
- case RXON_DEV_TYPE_CP:
- case RXON_DEV_TYPE_2STA:
- /*
- * These seem to always have timers for TBTT
- * active in uCode even when not associated yet.
- */
- break;
- }
-
- limits[n_active++] = ctx->beacon_int ?: IWL_PASSIVE_DWELL_BASE;
- }
-
- switch (n_active) {
- case 0:
- return dwell_time;
- case 2:
- limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
- limit /= 2;
- dwell_time = min(limit, dwell_time);
- /* fall through to limit further */
- case 1:
- limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
- limit /= n_active;
- return min(limit, dwell_time);
- default:
- WARN_ON_ONCE(1);
- return dwell_time;
- }
-}
-
-static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
- enum ieee80211_band band)
-{
- u16 passive = (band == IEEE80211_BAND_2GHZ) ?
- IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 :
- IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52;
-
- return iwl_limit_dwell(priv, passive);
-}
-
-/* Return valid, unused, channel for a passive scan to reset the RF */
-static u8 iwl_get_single_channel_number(struct iwl_priv *priv,
- enum ieee80211_band band)
-{
- struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band];
- struct iwl_rxon_context *ctx;
- int i;
-
- for (i = 0; i < sband->n_channels; i++) {
- bool busy = false;
-
- for_each_context(priv, ctx) {
- busy = sband->channels[i].hw_value ==
- le16_to_cpu(ctx->staging.channel);
- if (busy)
- break;
- }
-
- if (busy)
- continue;
-
- if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED))
- return sband->channels[i].hw_value;
- }
-
- return 0;
-}
-
-static int iwl_get_channel_for_reset_scan(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum ieee80211_band band,
- struct iwl_scan_channel *scan_ch)
-{
- const struct ieee80211_supported_band *sband;
- u16 channel;
-
- sband = iwl_get_hw_mode(priv, band);
- if (!sband) {
- IWL_ERR(priv, "invalid band\n");
- return 0;
- }
-
- channel = iwl_get_single_channel_number(priv, band);
- if (channel) {
- scan_ch->channel = cpu_to_le16(channel);
- scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
- scan_ch->active_dwell =
- cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME);
- scan_ch->passive_dwell =
- cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME);
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
- if (band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
- return 1;
- }
-
- IWL_ERR(priv, "no valid channel found\n");
- return 0;
-}
-
-static int iwl_get_channels_for_scan(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum ieee80211_band band,
- u8 is_active, u8 n_probes,
- struct iwl_scan_channel *scan_ch)
-{
- struct ieee80211_channel *chan;
- const struct ieee80211_supported_band *sband;
- u16 passive_dwell = 0;
- u16 active_dwell = 0;
- int added, i;
- u16 channel;
-
- sband = iwl_get_hw_mode(priv, band);
- if (!sband)
- return 0;
-
- active_dwell = iwl_get_active_dwell_time(priv, band, n_probes);
- passive_dwell = iwl_get_passive_dwell_time(priv, band);
-
- if (passive_dwell <= active_dwell)
- passive_dwell = active_dwell + 1;
-
- for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) {
- chan = priv->scan_request->channels[i];
-
- if (chan->band != band)
- continue;
-
- channel = chan->hw_value;
- scan_ch->channel = cpu_to_le16(channel);
-
- if (!is_active || (chan->flags & IEEE80211_CHAN_NO_IR))
- scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
- else
- scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
-
- if (n_probes)
- scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes);
-
- scan_ch->active_dwell = cpu_to_le16(active_dwell);
- scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
-
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
-
- /* NOTE: if we were doing 6Mb OFDM for scans we'd use
- * power level:
- * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
- */
- if (band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
-
- IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n",
- channel, le32_to_cpu(scan_ch->type),
- (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
- "ACTIVE" : "PASSIVE",
- (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ?
- active_dwell : passive_dwell);
-
- scan_ch++;
- added++;
- }
-
- IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added);
- return added;
-}
-
-/**
- * iwl_fill_probe_req - fill in all required fields and IE for probe request
- */
-
-static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
- const u8 *ies, int ie_len, const u8 *ssid,
- u8 ssid_len, int left)
-{
- int len = 0;
- u8 *pos = NULL;
-
- /* Make sure there is enough space for the probe request,
- * two mandatory IEs and the data */
- left -= 24;
- if (left < 0)
- return 0;
-
- frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
- eth_broadcast_addr(frame->da);
- memcpy(frame->sa, ta, ETH_ALEN);
- eth_broadcast_addr(frame->bssid);
- frame->seq_ctrl = 0;
-
- len += 24;
-
- /* ...next IE... */
- pos = &frame->u.probe_req.variable[0];
-
- /* fill in our SSID IE */
- left -= ssid_len + 2;
- if (left < 0)
- return 0;
- *pos++ = WLAN_EID_SSID;
- *pos++ = ssid_len;
- if (ssid && ssid_len) {
- memcpy(pos, ssid, ssid_len);
- pos += ssid_len;
- }
-
- len += ssid_len + 2;
-
- if (WARN_ON(left < ie_len))
- return len;
-
- if (ies && ie_len) {
- memcpy(pos, ies, ie_len);
- len += ie_len;
- }
-
- return (u16)len;
-}
-
-static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
-{
- struct iwl_host_cmd cmd = {
- .id = REPLY_SCAN_CMD,
- .len = { sizeof(struct iwl_scan_cmd), },
- };
- struct iwl_scan_cmd *scan;
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- u32 rate_flags = 0;
- u16 cmd_len = 0;
- u16 rx_chain = 0;
- enum ieee80211_band band;
- u8 n_probes = 0;
- u8 rx_ant = priv->nvm_data->valid_rx_ant;
- u8 rate;
- bool is_active = false;
- int chan_mod;
- u8 active_chains;
- u8 scan_tx_antennas = priv->nvm_data->valid_tx_ant;
- int ret;
- int scan_cmd_size = sizeof(struct iwl_scan_cmd) +
- MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) +
- priv->fw->ucode_capa.max_probe_length;
- const u8 *ssid = NULL;
- u8 ssid_len = 0;
-
- if (WARN_ON(priv->scan_type == IWL_SCAN_NORMAL &&
- (!priv->scan_request ||
- priv->scan_request->n_channels > MAX_SCAN_CHANNEL)))
- return -EINVAL;
-
- lockdep_assert_held(&priv->mutex);
-
- if (vif)
- ctx = iwl_rxon_ctx_from_vif(vif);
-
- if (!priv->scan_cmd) {
- priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL);
- if (!priv->scan_cmd) {
- IWL_DEBUG_SCAN(priv,
- "fail to allocate memory for scan\n");
- return -ENOMEM;
- }
- }
- scan = priv->scan_cmd;
- memset(scan, 0, scan_cmd_size);
-
- scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
- scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
-
- if (iwl_is_any_associated(priv)) {
- u16 interval = 0;
- u32 extra;
- u32 suspend_time = 100;
- u32 scan_suspend_time = 100;
-
- IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
- switch (priv->scan_type) {
- case IWL_SCAN_RADIO_RESET:
- interval = 0;
- break;
- case IWL_SCAN_NORMAL:
- interval = vif->bss_conf.beacon_int;
- break;
- }
-
- scan->suspend_time = 0;
- scan->max_out_time = cpu_to_le32(200 * 1024);
- if (!interval)
- interval = suspend_time;
-
- extra = (suspend_time / interval) << 22;
- scan_suspend_time = (extra |
- ((suspend_time % interval) * 1024));
- scan->suspend_time = cpu_to_le32(scan_suspend_time);
- IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
- scan_suspend_time, interval);
- }
-
- switch (priv->scan_type) {
- case IWL_SCAN_RADIO_RESET:
- IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
- /*
- * Override quiet time as firmware checks that active
- * dwell is >= quiet; since we use passive scan it'll
- * not actually be used.
- */
- scan->quiet_time = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME);
- break;
- case IWL_SCAN_NORMAL:
- if (priv->scan_request->n_ssids) {
- int i, p = 0;
- IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
- /*
- * The highest priority SSID is inserted to the
- * probe request template.
- */
- ssid_len = priv->scan_request->ssids[0].ssid_len;
- ssid = priv->scan_request->ssids[0].ssid;
-
- /*
- * Invert the order of ssids, the firmware will invert
- * it back.
- */
- for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) {
- scan->direct_scan[p].id = WLAN_EID_SSID;
- scan->direct_scan[p].len =
- priv->scan_request->ssids[i].ssid_len;
- memcpy(scan->direct_scan[p].ssid,
- priv->scan_request->ssids[i].ssid,
- priv->scan_request->ssids[i].ssid_len);
- n_probes++;
- p++;
- }
- is_active = true;
- } else
- IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
- break;
- }
-
- scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
- scan->tx_cmd.sta_id = ctx->bcast_sta_id;
- scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
-
- switch (priv->scan_band) {
- case IEEE80211_BAND_2GHZ:
- scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
- chan_mod = le32_to_cpu(
- priv->contexts[IWL_RXON_CTX_BSS].active.flags &
- RXON_FLG_CHANNEL_MODE_MSK)
- >> RXON_FLG_CHANNEL_MODE_POS;
- if ((priv->scan_request && priv->scan_request->no_cck) ||
- chan_mod == CHANNEL_MODE_PURE_40) {
- rate = IWL_RATE_6M_PLCP;
- } else {
- rate = IWL_RATE_1M_PLCP;
- rate_flags = RATE_MCS_CCK_MSK;
- }
- /*
- * Internal scans are passive, so we can indiscriminately set
- * the BT ignore flag on 2.4 GHz since it applies to TX only.
- */
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist)
- scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
- break;
- case IEEE80211_BAND_5GHZ:
- rate = IWL_RATE_6M_PLCP;
- break;
- default:
- IWL_WARN(priv, "Invalid scan band\n");
- return -EIO;
- }
-
- /*
- * If active scanning is requested but a certain channel is
- * marked passive, we can do active scanning if we detect
- * transmissions.
- *
- * There is an issue with some firmware versions that triggers
- * a sysassert on a "good CRC threshold" of zero (== disabled),
- * on a radar channel even though this means that we should NOT
- * send probes.
- *
- * The "good CRC threshold" is the number of frames that we
- * need to receive during our dwell time on a channel before
- * sending out probes -- setting this to a huge value will
- * mean we never reach it, but at the same time work around
- * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER
- * here instead of IWL_GOOD_CRC_TH_DISABLED.
- *
- * This was fixed in later versions along with some other
- * scan changes, and the threshold behaves as a flag in those
- * versions.
- */
- if (priv->new_scan_threshold_behaviour)
- scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
- IWL_GOOD_CRC_TH_DISABLED;
- else
- scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
- IWL_GOOD_CRC_TH_NEVER;
-
- band = priv->scan_band;
-
- if (band == IEEE80211_BAND_2GHZ &&
- priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist) {
- /* transmit 2.4 GHz probes only on first antenna */
- scan_tx_antennas = first_antenna(scan_tx_antennas);
- }
-
- priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv,
- priv->scan_tx_ant[band],
- scan_tx_antennas);
- rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
- scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
-
- /*
- * In power save mode while associated use one chain,
- * otherwise use all chains
- */
- if (test_bit(STATUS_POWER_PMI, &priv->status) &&
- !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) {
- /* rx_ant has been set to all valid chains previously */
- active_chains = rx_ant &
- ((u8)(priv->chain_noise_data.active_chains));
- if (!active_chains)
- active_chains = rx_ant;
-
- IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n",
- priv->chain_noise_data.active_chains);
-
- rx_ant = first_antenna(active_chains);
- }
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist &&
- priv->bt_full_concurrent) {
- /* operated as 1x1 in full concurrency mode */
- rx_ant = first_antenna(rx_ant);
- }
-
- /* MIMO is not used here, but value is required */
- rx_chain |=
- priv->nvm_data->valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
- rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
- rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
- rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
- scan->rx_chain = cpu_to_le16(rx_chain);
- switch (priv->scan_type) {
- case IWL_SCAN_NORMAL:
- cmd_len = iwl_fill_probe_req(
- (struct ieee80211_mgmt *)scan->data,
- vif->addr,
- priv->scan_request->ie,
- priv->scan_request->ie_len,
- ssid, ssid_len,
- scan_cmd_size - sizeof(*scan));
- break;
- case IWL_SCAN_RADIO_RESET:
- /* use bcast addr, will not be transmitted but must be valid */
- cmd_len = iwl_fill_probe_req(
- (struct ieee80211_mgmt *)scan->data,
- iwl_bcast_addr, NULL, 0,
- NULL, 0,
- scan_cmd_size - sizeof(*scan));
- break;
- default:
- BUG();
- }
- scan->tx_cmd.len = cpu_to_le16(cmd_len);
-
- scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
- RXON_FILTER_BCON_AWARE_MSK);
-
- switch (priv->scan_type) {
- case IWL_SCAN_RADIO_RESET:
- scan->channel_count =
- iwl_get_channel_for_reset_scan(priv, vif, band,
- (void *)&scan->data[cmd_len]);
- break;
- case IWL_SCAN_NORMAL:
- scan->channel_count =
- iwl_get_channels_for_scan(priv, vif, band,
- is_active, n_probes,
- (void *)&scan->data[cmd_len]);
- break;
- }
-
- if (scan->channel_count == 0) {
- IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
- return -EIO;
- }
-
- cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) +
- scan->channel_count * sizeof(struct iwl_scan_channel);
- cmd.data[0] = scan;
- cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
- scan->len = cpu_to_le16(cmd.len[0]);
-
- /* set scan bit here for PAN params */
- set_bit(STATUS_SCAN_HW, &priv->status);
-
- ret = iwlagn_set_pan_params(priv);
- if (ret) {
- clear_bit(STATUS_SCAN_HW, &priv->status);
- return ret;
- }
-
- ret = iwl_dvm_send_cmd(priv, &cmd);
- if (ret) {
- clear_bit(STATUS_SCAN_HW, &priv->status);
- iwlagn_set_pan_params(priv);
- }
-
- return ret;
-}
-
-void iwl_init_scan_params(struct iwl_priv *priv)
-{
- u8 ant_idx = fls(priv->nvm_data->valid_tx_ant) - 1;
- if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ])
- priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx;
- if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ])
- priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
-}
-
-int __must_check iwl_scan_initiate(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- enum iwl_scan_type scan_type,
- enum ieee80211_band band)
-{
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- cancel_delayed_work(&priv->scan_check);
-
- if (!iwl_is_ready_rf(priv)) {
- IWL_WARN(priv, "Request scan called when driver not ready.\n");
- return -EIO;
- }
-
- if (test_bit(STATUS_SCAN_HW, &priv->status)) {
- IWL_DEBUG_SCAN(priv,
- "Multiple concurrent scan requests in parallel.\n");
- return -EBUSY;
- }
-
- if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n");
- return -EBUSY;
- }
-
- IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
- scan_type == IWL_SCAN_NORMAL ? "" :
- "internal short ");
-
- set_bit(STATUS_SCANNING, &priv->status);
- priv->scan_type = scan_type;
- priv->scan_start = jiffies;
- priv->scan_band = band;
-
- ret = iwlagn_request_scan(priv, vif);
- if (ret) {
- clear_bit(STATUS_SCANNING, &priv->status);
- priv->scan_type = IWL_SCAN_NORMAL;
- return ret;
- }
-
- queue_delayed_work(priv->workqueue, &priv->scan_check,
- IWL_SCAN_CHECK_WATCHDOG);
-
- return 0;
-}
-
-
-/*
- * internal short scan, this function should only been called while associated.
- * It will reset and tune the radio to prevent possible RF related problem
- */
-void iwl_internal_short_hw_scan(struct iwl_priv *priv)
-{
- queue_work(priv->workqueue, &priv->start_internal_scan);
-}
-
-static void iwl_bg_start_internal_scan(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, start_internal_scan);
-
- IWL_DEBUG_SCAN(priv, "Start internal scan\n");
-
- mutex_lock(&priv->mutex);
-
- if (priv->scan_type == IWL_SCAN_RADIO_RESET) {
- IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
- goto unlock;
- }
-
- if (test_bit(STATUS_SCANNING, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
- goto unlock;
- }
-
- if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band))
- IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n");
- unlock:
- mutex_unlock(&priv->mutex);
-}
-
-static void iwl_bg_scan_check(struct work_struct *data)
-{
- struct iwl_priv *priv =
- container_of(data, struct iwl_priv, scan_check.work);
-
- IWL_DEBUG_SCAN(priv, "Scan check work\n");
-
- /* Since we are here firmware does not finish scan and
- * most likely is in bad shape, so we don't bother to
- * send abort command, just force scan complete to mac80211 */
- mutex_lock(&priv->mutex);
- iwl_force_scan_end(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void iwl_bg_abort_scan(struct work_struct *work)
-{
- struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);
-
- IWL_DEBUG_SCAN(priv, "Abort scan work\n");
-
- /* We keep scan_check work queued in case when firmware will not
- * report back scan completed notification */
- mutex_lock(&priv->mutex);
- iwl_scan_cancel_timeout(priv, 200);
- mutex_unlock(&priv->mutex);
-}
-
-static void iwl_bg_scan_completed(struct work_struct *work)
-{
- struct iwl_priv *priv =
- container_of(work, struct iwl_priv, scan_completed);
-
- mutex_lock(&priv->mutex);
- iwl_process_scan_complete(priv);
- mutex_unlock(&priv->mutex);
-}
-
-void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
-{
- INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed);
- INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
- INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan);
- INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
-}
-
-void iwl_cancel_scan_deferred_work(struct iwl_priv *priv)
-{
- cancel_work_sync(&priv->start_internal_scan);
- cancel_work_sync(&priv->abort_scan);
- cancel_work_sync(&priv->scan_completed);
-
- if (cancel_delayed_work_sync(&priv->scan_check)) {
- mutex_lock(&priv->mutex);
- iwl_force_scan_end(priv);
- mutex_unlock(&priv->mutex);
- }
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c
deleted file mode 100644
index 0fa67d3b7235..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/sta.c
+++ /dev/null
@@ -1,1442 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/etherdevice.h>
-#include <net/mac80211.h>
-#include "iwl-trans.h"
-#include "dev.h"
-#include "agn.h"
-
-const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-
-static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
-{
- lockdep_assert_held(&priv->sta_lock);
-
- if (sta_id >= IWLAGN_STATION_COUNT) {
- IWL_ERR(priv, "invalid sta_id %u\n", sta_id);
- return -EINVAL;
- }
- if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
- IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u "
- "addr %pM\n",
- sta_id, priv->stations[sta_id].sta.sta.addr);
-
- if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) {
- IWL_DEBUG_ASSOC(priv,
- "STA id %u addr %pM already present in uCode "
- "(according to driver)\n",
- sta_id, priv->stations[sta_id].sta.sta.addr);
- } else {
- priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
- IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n",
- sta_id, priv->stations[sta_id].sta.sta.addr);
- }
- return 0;
-}
-
-static void iwl_process_add_sta_resp(struct iwl_priv *priv,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data;
-
- IWL_DEBUG_INFO(priv, "Processing response for adding station\n");
-
- spin_lock_bh(&priv->sta_lock);
-
- switch (add_sta_resp->status) {
- case ADD_STA_SUCCESS_MSK:
- IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n");
- break;
- case ADD_STA_NO_ROOM_IN_TABLE:
- IWL_ERR(priv, "Adding station failed, no room in table.\n");
- break;
- case ADD_STA_NO_BLOCK_ACK_RESOURCE:
- IWL_ERR(priv,
- "Adding station failed, no block ack resource.\n");
- break;
- case ADD_STA_MODIFY_NON_EXIST_STA:
- IWL_ERR(priv, "Attempting to modify non-existing station\n");
- break;
- default:
- IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n",
- add_sta_resp->status);
- break;
- }
-
- spin_unlock_bh(&priv->sta_lock);
-}
-
-void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
-
- iwl_process_add_sta_resp(priv, pkt);
-}
-
-int iwl_send_add_sta(struct iwl_priv *priv,
- struct iwl_addsta_cmd *sta, u8 flags)
-{
- int ret = 0;
- struct iwl_host_cmd cmd = {
- .id = REPLY_ADD_STA,
- .flags = flags,
- .data = { sta, },
- .len = { sizeof(*sta), },
- };
- u8 sta_id __maybe_unused = sta->sta.sta_id;
- struct iwl_rx_packet *pkt;
- struct iwl_add_sta_resp *add_sta_resp;
-
- IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n",
- sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : "");
-
- if (!(flags & CMD_ASYNC)) {
- cmd.flags |= CMD_WANT_SKB;
- might_sleep();
- }
-
- ret = iwl_dvm_send_cmd(priv, &cmd);
-
- if (ret || (flags & CMD_ASYNC))
- return ret;
-
- pkt = cmd.resp_pkt;
- add_sta_resp = (void *)pkt->data;
-
- /* debug messages are printed in the handler */
- if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) {
- spin_lock_bh(&priv->sta_lock);
- ret = iwl_sta_ucode_activate(priv, sta_id);
- spin_unlock_bh(&priv->sta_lock);
- } else {
- ret = -EIO;
- }
-
- iwl_free_resp(&cmd);
-
- return ret;
-}
-
-bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_sta *sta)
-{
- if (!ctx->ht.enabled || !ctx->ht.is_40mhz)
- return false;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (priv->disable_ht40)
- return false;
-#endif
-
- /* special case for RXON */
- if (!sta)
- return true;
-
- return sta->bandwidth >= IEEE80211_STA_RX_BW_40;
-}
-
-static void iwl_sta_calc_ht_flags(struct iwl_priv *priv,
- struct ieee80211_sta *sta,
- struct iwl_rxon_context *ctx,
- __le32 *flags, __le32 *mask)
-{
- struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap;
-
- *mask = STA_FLG_RTS_MIMO_PROT_MSK |
- STA_FLG_MIMO_DIS_MSK |
- STA_FLG_HT40_EN_MSK |
- STA_FLG_MAX_AGG_SIZE_MSK |
- STA_FLG_AGG_MPDU_DENSITY_MSK;
- *flags = 0;
-
- if (!sta || !sta_ht_inf->ht_supported)
- return;
-
- IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n",
- sta->addr,
- (sta->smps_mode == IEEE80211_SMPS_STATIC) ?
- "static" :
- (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ?
- "dynamic" : "disabled");
-
- switch (sta->smps_mode) {
- case IEEE80211_SMPS_STATIC:
- *flags |= STA_FLG_MIMO_DIS_MSK;
- break;
- case IEEE80211_SMPS_DYNAMIC:
- *flags |= STA_FLG_RTS_MIMO_PROT_MSK;
- break;
- case IEEE80211_SMPS_OFF:
- break;
- default:
- IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->smps_mode);
- break;
- }
-
- *flags |= cpu_to_le32(
- (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
-
- *flags |= cpu_to_le32(
- (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
-
- if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
- *flags |= STA_FLG_HT40_EN_MSK;
-}
-
-int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct ieee80211_sta *sta)
-{
- u8 sta_id = iwl_sta_id(sta);
- __le32 flags, mask;
- struct iwl_addsta_cmd cmd;
-
- if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
- return -EINVAL;
-
- iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask);
-
- spin_lock_bh(&priv->sta_lock);
- priv->stations[sta_id].sta.station_flags &= ~mask;
- priv->stations[sta_id].sta.station_flags |= flags;
- spin_unlock_bh(&priv->sta_lock);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.mode = STA_CONTROL_MODIFY_MSK;
- cmd.station_flags_msk = mask;
- cmd.station_flags = flags;
- cmd.sta.sta_id = sta_id;
-
- return iwl_send_add_sta(priv, &cmd, 0);
-}
-
-static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
- struct ieee80211_sta *sta,
- struct iwl_rxon_context *ctx)
-{
- __le32 flags, mask;
-
- iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask);
-
- lockdep_assert_held(&priv->sta_lock);
- priv->stations[index].sta.station_flags &= ~mask;
- priv->stations[index].sta.station_flags |= flags;
-}
-
-/**
- * iwl_prep_station - Prepare station information for addition
- *
- * should be called with sta_lock held
- */
-u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
-{
- struct iwl_station_entry *station;
- int i;
- u8 sta_id = IWL_INVALID_STATION;
-
- if (is_ap)
- sta_id = ctx->ap_sta_id;
- else if (is_broadcast_ether_addr(addr))
- sta_id = ctx->bcast_sta_id;
- else
- for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) {
- if (ether_addr_equal(priv->stations[i].sta.sta.addr,
- addr)) {
- sta_id = i;
- break;
- }
-
- if (!priv->stations[i].used &&
- sta_id == IWL_INVALID_STATION)
- sta_id = i;
- }
-
- /*
- * These two conditions have the same outcome, but keep them
- * separate
- */
- if (unlikely(sta_id == IWL_INVALID_STATION))
- return sta_id;
-
- /*
- * uCode is not able to deal with multiple requests to add a
- * station. Keep track if one is in progress so that we do not send
- * another.
- */
- if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
- IWL_DEBUG_INFO(priv, "STA %d already in process of being "
- "added.\n", sta_id);
- return sta_id;
- }
-
- if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
- (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) &&
- ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) {
- IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
- "adding again.\n", sta_id, addr);
- return sta_id;
- }
-
- station = &priv->stations[sta_id];
- station->used = IWL_STA_DRIVER_ACTIVE;
- IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
- sta_id, addr);
- priv->num_stations++;
-
- /* Set up the REPLY_ADD_STA command to send to device */
- memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
- memcpy(station->sta.sta.addr, addr, ETH_ALEN);
- station->sta.mode = 0;
- station->sta.sta.sta_id = sta_id;
- station->sta.station_flags = ctx->station_flags;
- station->ctxid = ctx->ctxid;
-
- if (sta) {
- struct iwl_station_priv *sta_priv;
-
- sta_priv = (void *)sta->drv_priv;
- sta_priv->ctx = ctx;
- }
-
- /*
- * OK to call unconditionally, since local stations (IBSS BSSID
- * STA and broadcast STA) pass in a NULL sta, and mac80211
- * doesn't allow HT IBSS.
- */
- iwl_set_ht_add_station(priv, sta_id, sta, ctx);
-
- return sta_id;
-
-}
-
-#define STA_WAIT_TIMEOUT (HZ/2)
-
-/**
- * iwl_add_station_common -
- */
-int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- const u8 *addr, bool is_ap,
- struct ieee80211_sta *sta, u8 *sta_id_r)
-{
- int ret = 0;
- u8 sta_id;
- struct iwl_addsta_cmd sta_cmd;
-
- *sta_id_r = 0;
- spin_lock_bh(&priv->sta_lock);
- sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_ERR(priv, "Unable to prepare station %pM for addition\n",
- addr);
- spin_unlock_bh(&priv->sta_lock);
- return -EINVAL;
- }
-
- /*
- * uCode is not able to deal with multiple requests to add a
- * station. Keep track if one is in progress so that we do not send
- * another.
- */
- if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
- IWL_DEBUG_INFO(priv, "STA %d already in process of being "
- "added.\n", sta_id);
- spin_unlock_bh(&priv->sta_lock);
- return -EEXIST;
- }
-
- if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
- (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
- IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
- "adding again.\n", sta_id, addr);
- spin_unlock_bh(&priv->sta_lock);
- return -EEXIST;
- }
-
- priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS;
- memcpy(&sta_cmd, &priv->stations[sta_id].sta,
- sizeof(struct iwl_addsta_cmd));
- spin_unlock_bh(&priv->sta_lock);
-
- /* Add station to device's station table */
- ret = iwl_send_add_sta(priv, &sta_cmd, 0);
- if (ret) {
- spin_lock_bh(&priv->sta_lock);
- IWL_ERR(priv, "Adding station %pM failed.\n",
- priv->stations[sta_id].sta.sta.addr);
- priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
- priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
- spin_unlock_bh(&priv->sta_lock);
- }
- *sta_id_r = sta_id;
- return ret;
-}
-
-/**
- * iwl_sta_ucode_deactivate - deactivate ucode status for a station
- */
-static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id)
-{
- lockdep_assert_held(&priv->sta_lock);
-
- /* Ucode must be active and driver must be non active */
- if ((priv->stations[sta_id].used &
- (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) !=
- IWL_STA_UCODE_ACTIVE)
- IWL_ERR(priv, "removed non active STA %u\n", sta_id);
-
- priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
-
- memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
- IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id);
-}
-
-static int iwl_send_remove_station(struct iwl_priv *priv,
- const u8 *addr, int sta_id,
- bool temporary)
-{
- struct iwl_rx_packet *pkt;
- int ret;
- struct iwl_rem_sta_cmd rm_sta_cmd;
- struct iwl_rem_sta_resp *rem_sta_resp;
-
- struct iwl_host_cmd cmd = {
- .id = REPLY_REMOVE_STA,
- .len = { sizeof(struct iwl_rem_sta_cmd), },
- .data = { &rm_sta_cmd, },
- };
-
- memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
- rm_sta_cmd.num_sta = 1;
- memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
-
- cmd.flags |= CMD_WANT_SKB;
-
- ret = iwl_dvm_send_cmd(priv, &cmd);
-
- if (ret)
- return ret;
-
- pkt = cmd.resp_pkt;
- rem_sta_resp = (void *)pkt->data;
-
- switch (rem_sta_resp->status) {
- case REM_STA_SUCCESS_MSK:
- if (!temporary) {
- spin_lock_bh(&priv->sta_lock);
- iwl_sta_ucode_deactivate(priv, sta_id);
- spin_unlock_bh(&priv->sta_lock);
- }
- IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
- break;
- default:
- ret = -EIO;
- IWL_ERR(priv, "REPLY_REMOVE_STA failed\n");
- break;
- }
-
- iwl_free_resp(&cmd);
-
- return ret;
-}
-
-/**
- * iwl_remove_station - Remove driver's knowledge of station.
- */
-int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
- const u8 *addr)
-{
- u8 tid;
-
- if (!iwl_is_ready(priv)) {
- IWL_DEBUG_INFO(priv,
- "Unable to remove station %pM, device not ready.\n",
- addr);
- /*
- * It is typical for stations to be removed when we are
- * going down. Return success since device will be down
- * soon anyway
- */
- return 0;
- }
-
- IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n",
- sta_id, addr);
-
- if (WARN_ON(sta_id == IWL_INVALID_STATION))
- return -EINVAL;
-
- spin_lock_bh(&priv->sta_lock);
-
- if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
- IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
- addr);
- goto out_err;
- }
-
- if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
- IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
- addr);
- goto out_err;
- }
-
- if (priv->stations[sta_id].used & IWL_STA_LOCAL) {
- kfree(priv->stations[sta_id].lq);
- priv->stations[sta_id].lq = NULL;
- }
-
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
- memset(&priv->tid_data[sta_id][tid], 0,
- sizeof(priv->tid_data[sta_id][tid]));
-
- priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
-
- priv->num_stations--;
-
- if (WARN_ON(priv->num_stations < 0))
- priv->num_stations = 0;
-
- spin_unlock_bh(&priv->sta_lock);
-
- return iwl_send_remove_station(priv, addr, sta_id, false);
-out_err:
- spin_unlock_bh(&priv->sta_lock);
- return -EINVAL;
-}
-
-void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id,
- const u8 *addr)
-{
- u8 tid;
-
- if (!iwl_is_ready(priv)) {
- IWL_DEBUG_INFO(priv,
- "Unable to remove station %pM, device not ready.\n",
- addr);
- return;
- }
-
- IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id);
-
- if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
- return;
-
- spin_lock_bh(&priv->sta_lock);
-
- WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE));
-
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
- memset(&priv->tid_data[sta_id][tid], 0,
- sizeof(priv->tid_data[sta_id][tid]));
-
- priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
- priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
-
- priv->num_stations--;
-
- if (WARN_ON_ONCE(priv->num_stations < 0))
- priv->num_stations = 0;
-
- spin_unlock_bh(&priv->sta_lock);
-}
-
-static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- u8 sta_id, struct iwl_link_quality_cmd *link_cmd)
-{
- int i, r;
- u32 rate_flags = 0;
- __le32 rate_n_flags;
-
- lockdep_assert_held(&priv->mutex);
-
- memset(link_cmd, 0, sizeof(*link_cmd));
-
- /* Set up the rate scaling to start at selected rate, fall back
- * all the way down to 1M in IEEE order, and then spin on 1M */
- if (priv->band == IEEE80211_BAND_5GHZ)
- r = IWL_RATE_6M_INDEX;
- else if (ctx && ctx->vif && ctx->vif->p2p)
- r = IWL_RATE_6M_INDEX;
- else
- r = IWL_RATE_1M_INDEX;
-
- if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
- rate_flags |= RATE_MCS_CCK_MSK;
-
- rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) <<
- RATE_MCS_ANT_POS;
- rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
- link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
-
- link_cmd->general_params.single_stream_ant_msk =
- first_antenna(priv->nvm_data->valid_tx_ant);
-
- link_cmd->general_params.dual_stream_ant_msk =
- priv->nvm_data->valid_tx_ant &
- ~first_antenna(priv->nvm_data->valid_tx_ant);
- if (!link_cmd->general_params.dual_stream_ant_msk) {
- link_cmd->general_params.dual_stream_ant_msk = ANT_AB;
- } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) {
- link_cmd->general_params.dual_stream_ant_msk =
- priv->nvm_data->valid_tx_ant;
- }
-
- link_cmd->agg_params.agg_dis_start_th =
- LINK_QUAL_AGG_DISABLE_START_DEF;
- link_cmd->agg_params.agg_time_limit =
- cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
-
- link_cmd->sta_id = sta_id;
-}
-
-/**
- * iwl_clear_ucode_stations - clear ucode station table bits
- *
- * This function clears all the bits in the driver indicating
- * which stations are active in the ucode. Call when something
- * other than explicit station management would cause this in
- * the ucode, e.g. unassociated RXON.
- */
-void iwl_clear_ucode_stations(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- int i;
- bool cleared = false;
-
- IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n");
-
- spin_lock_bh(&priv->sta_lock);
- for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
- if (ctx && ctx->ctxid != priv->stations[i].ctxid)
- continue;
-
- if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) {
- IWL_DEBUG_INFO(priv,
- "Clearing ucode active for station %d\n", i);
- priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
- cleared = true;
- }
- }
- spin_unlock_bh(&priv->sta_lock);
-
- if (!cleared)
- IWL_DEBUG_INFO(priv,
- "No active stations found to be cleared\n");
-}
-
-/**
- * iwl_restore_stations() - Restore driver known stations to device
- *
- * All stations considered active by driver, but not present in ucode, is
- * restored.
- *
- * Function sleeps.
- */
-void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- struct iwl_addsta_cmd sta_cmd;
- static const struct iwl_link_quality_cmd zero_lq = {};
- struct iwl_link_quality_cmd lq;
- int i;
- bool found = false;
- int ret;
- bool send_lq;
-
- if (!iwl_is_ready(priv)) {
- IWL_DEBUG_INFO(priv,
- "Not ready yet, not restoring any stations.\n");
- return;
- }
-
- IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
- spin_lock_bh(&priv->sta_lock);
- for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
- if (ctx->ctxid != priv->stations[i].ctxid)
- continue;
- if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
- !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
- IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
- priv->stations[i].sta.sta.addr);
- priv->stations[i].sta.mode = 0;
- priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
- found = true;
- }
- }
-
- for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
- if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
- memcpy(&sta_cmd, &priv->stations[i].sta,
- sizeof(struct iwl_addsta_cmd));
- send_lq = false;
- if (priv->stations[i].lq) {
- if (priv->wowlan)
- iwl_sta_fill_lq(priv, ctx, i, &lq);
- else
- memcpy(&lq, priv->stations[i].lq,
- sizeof(struct iwl_link_quality_cmd));
-
- if (memcmp(&lq, &zero_lq, sizeof(lq)))
- send_lq = true;
- }
- spin_unlock_bh(&priv->sta_lock);
- ret = iwl_send_add_sta(priv, &sta_cmd, 0);
- if (ret) {
- spin_lock_bh(&priv->sta_lock);
- IWL_ERR(priv, "Adding station %pM failed.\n",
- priv->stations[i].sta.sta.addr);
- priv->stations[i].used &=
- ~IWL_STA_DRIVER_ACTIVE;
- priv->stations[i].used &=
- ~IWL_STA_UCODE_INPROGRESS;
- continue;
- }
- /*
- * Rate scaling has already been initialized, send
- * current LQ command
- */
- if (send_lq)
- iwl_send_lq_cmd(priv, ctx, &lq, 0, true);
- spin_lock_bh(&priv->sta_lock);
- priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
- }
- }
-
- spin_unlock_bh(&priv->sta_lock);
- if (!found)
- IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
- "no stations to be restored.\n");
- else
- IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
- "complete.\n");
-}
-
-int iwl_get_free_ucode_key_offset(struct iwl_priv *priv)
-{
- int i;
-
- for (i = 0; i < priv->sta_key_max_num; i++)
- if (!test_and_set_bit(i, &priv->ucode_key_table))
- return i;
-
- return WEP_INVALID_OFFSET;
-}
-
-void iwl_dealloc_bcast_stations(struct iwl_priv *priv)
-{
- int i;
-
- spin_lock_bh(&priv->sta_lock);
- for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
- if (!(priv->stations[i].used & IWL_STA_BCAST))
- continue;
-
- priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
- priv->num_stations--;
- if (WARN_ON(priv->num_stations < 0))
- priv->num_stations = 0;
- kfree(priv->stations[i].lq);
- priv->stations[i].lq = NULL;
- }
- spin_unlock_bh(&priv->sta_lock);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-static void iwl_dump_lq_cmd(struct iwl_priv *priv,
- struct iwl_link_quality_cmd *lq)
-{
- int i;
- IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id);
- IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n",
- lq->general_params.single_stream_ant_msk,
- lq->general_params.dual_stream_ant_msk);
-
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
- IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n",
- i, lq->rs_table[i].rate_n_flags);
-}
-#else
-static inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
- struct iwl_link_quality_cmd *lq)
-{
-}
-#endif
-
-/**
- * is_lq_table_valid() - Test one aspect of LQ cmd for validity
- *
- * It sometimes happens when a HT rate has been in use and we
- * loose connectivity with AP then mac80211 will first tell us that the
- * current channel is not HT anymore before removing the station. In such a
- * scenario the RXON flags will be updated to indicate we are not
- * communicating HT anymore, but the LQ command may still contain HT rates.
- * Test for this to prevent driver from sending LQ command between the time
- * RXON flags are updated and when LQ command is updated.
- */
-static bool is_lq_table_valid(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct iwl_link_quality_cmd *lq)
-{
- int i;
-
- if (ctx->ht.enabled)
- return true;
-
- IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n",
- ctx->active.channel);
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
- if (le32_to_cpu(lq->rs_table[i].rate_n_flags) &
- RATE_MCS_HT_MSK) {
- IWL_DEBUG_INFO(priv,
- "index %d of LQ expects HT channel\n",
- i);
- return false;
- }
- }
- return true;
-}
-
-/**
- * iwl_send_lq_cmd() - Send link quality command
- * @init: This command is sent as part of station initialization right
- * after station has been added.
- *
- * The link quality command is sent as the last step of station creation.
- * This is the special case in which init is set and we call a callback in
- * this case to clear the state indicating that station creation is in
- * progress.
- */
-int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- struct iwl_link_quality_cmd *lq, u8 flags, bool init)
-{
- int ret = 0;
- struct iwl_host_cmd cmd = {
- .id = REPLY_TX_LINK_QUALITY_CMD,
- .len = { sizeof(struct iwl_link_quality_cmd), },
- .flags = flags,
- .data = { lq, },
- };
-
- if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
- return -EINVAL;
-
-
- spin_lock_bh(&priv->sta_lock);
- if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
- spin_unlock_bh(&priv->sta_lock);
- return -EINVAL;
- }
- spin_unlock_bh(&priv->sta_lock);
-
- iwl_dump_lq_cmd(priv, lq);
- if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
- return -EINVAL;
-
- if (is_lq_table_valid(priv, ctx, lq))
- ret = iwl_dvm_send_cmd(priv, &cmd);
- else
- ret = -EINVAL;
-
- if (cmd.flags & CMD_ASYNC)
- return ret;
-
- if (init) {
- IWL_DEBUG_INFO(priv, "init LQ command complete, "
- "clearing sta addition status for sta %d\n",
- lq->sta_id);
- spin_lock_bh(&priv->sta_lock);
- priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
- spin_unlock_bh(&priv->sta_lock);
- }
- return ret;
-}
-
-
-static struct iwl_link_quality_cmd *
-iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- u8 sta_id)
-{
- struct iwl_link_quality_cmd *link_cmd;
-
- link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL);
- if (!link_cmd) {
- IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n");
- return NULL;
- }
-
- iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd);
-
- return link_cmd;
-}
-
-/*
- * iwlagn_add_bssid_station - Add the special IBSS BSSID station
- *
- * Function sleeps.
- */
-int iwlagn_add_bssid_station(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- const u8 *addr, u8 *sta_id_r)
-{
- int ret;
- u8 sta_id;
- struct iwl_link_quality_cmd *link_cmd;
-
- if (sta_id_r)
- *sta_id_r = IWL_INVALID_STATION;
-
- ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id);
- if (ret) {
- IWL_ERR(priv, "Unable to add station %pM\n", addr);
- return ret;
- }
-
- if (sta_id_r)
- *sta_id_r = sta_id;
-
- spin_lock_bh(&priv->sta_lock);
- priv->stations[sta_id].used |= IWL_STA_LOCAL;
- spin_unlock_bh(&priv->sta_lock);
-
- /* Set up default rate scaling table in device's station table */
- link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
- if (!link_cmd) {
- IWL_ERR(priv,
- "Unable to initialize rate scaling for station %pM.\n",
- addr);
- return -ENOMEM;
- }
-
- ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true);
- if (ret)
- IWL_ERR(priv, "Link quality command failed (%d)\n", ret);
-
- spin_lock_bh(&priv->sta_lock);
- priv->stations[sta_id].lq = link_cmd;
- spin_unlock_bh(&priv->sta_lock);
-
- return 0;
-}
-
-/*
- * static WEP keys
- *
- * For each context, the device has a table of 4 static WEP keys
- * (one for each key index) that is updated with the following
- * commands.
- */
-
-static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- bool send_if_empty)
-{
- int i, not_empty = 0;
- u8 buff[sizeof(struct iwl_wep_cmd) +
- sizeof(struct iwl_wep_key) * WEP_KEYS_MAX];
- struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff;
- size_t cmd_size = sizeof(struct iwl_wep_cmd);
- struct iwl_host_cmd cmd = {
- .id = ctx->wep_key_cmd,
- .data = { wep_cmd, },
- };
-
- might_sleep();
-
- memset(wep_cmd, 0, cmd_size +
- (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX));
-
- for (i = 0; i < WEP_KEYS_MAX ; i++) {
- wep_cmd->key[i].key_index = i;
- if (ctx->wep_keys[i].key_size) {
- wep_cmd->key[i].key_offset = i;
- not_empty = 1;
- } else {
- wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET;
- }
-
- wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size;
- memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key,
- ctx->wep_keys[i].key_size);
- }
-
- wep_cmd->global_key_type = WEP_KEY_WEP_TYPE;
- wep_cmd->num_keys = WEP_KEYS_MAX;
-
- cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX;
-
- cmd.len[0] = cmd_size;
-
- if (not_empty || send_if_empty)
- return iwl_dvm_send_cmd(priv, &cmd);
- else
- return 0;
-}
-
-int iwl_restore_default_wep_keys(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- lockdep_assert_held(&priv->mutex);
-
- return iwl_send_static_wepkey_cmd(priv, ctx, false);
-}
-
-int iwl_remove_default_wep_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf)
-{
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n",
- keyconf->keyidx);
-
- memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0]));
- if (iwl_is_rfkill(priv)) {
- IWL_DEBUG_WEP(priv,
- "Not sending REPLY_WEPKEY command due to RFKILL.\n");
- /* but keys in device are clear anyway so return success */
- return 0;
- }
- ret = iwl_send_static_wepkey_cmd(priv, ctx, 1);
- IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n",
- keyconf->keyidx, ret);
-
- return ret;
-}
-
-int iwl_set_default_wep_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf)
-{
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- if (keyconf->keylen != WEP_KEY_LEN_128 &&
- keyconf->keylen != WEP_KEY_LEN_64) {
- IWL_DEBUG_WEP(priv,
- "Bad WEP key length %d\n", keyconf->keylen);
- return -EINVAL;
- }
-
- keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT;
-
- ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen;
- memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key,
- keyconf->keylen);
-
- ret = iwl_send_static_wepkey_cmd(priv, ctx, false);
- IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n",
- keyconf->keylen, keyconf->keyidx, ret);
-
- return ret;
-}
-
-/*
- * dynamic (per-station) keys
- *
- * The dynamic keys are a little more complicated. The device has
- * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys.
- * These are linked to stations by a table that contains an index
- * into the key table for each station/key index/{mcast,unicast},
- * i.e. it's basically an array of pointers like this:
- * key_offset_t key_mapping[NUM_STATIONS][4][2];
- * (it really works differently, but you can think of it as such)
- *
- * The key uploading and linking happens in the same command, the
- * add station command with STA_MODIFY_KEY_MASK.
- */
-
-static u8 iwlagn_key_sta_id(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
-
- if (sta)
- return iwl_sta_id(sta);
-
- /*
- * The device expects GTKs for station interfaces to be
- * installed as GTKs for the AP station. If we have no
- * station ID, then use the ap_sta_id in that case.
- */
- if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx)
- return vif_priv->ctx->ap_sta_id;
-
- return IWL_INVALID_STATION;
-}
-
-static int iwlagn_send_sta_key(struct iwl_priv *priv,
- struct ieee80211_key_conf *keyconf,
- u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
- u32 cmd_flags)
-{
- __le16 key_flags;
- struct iwl_addsta_cmd sta_cmd;
- int i;
-
- spin_lock_bh(&priv->sta_lock);
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
- spin_unlock_bh(&priv->sta_lock);
-
- key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags |= STA_KEY_FLG_MAP_KEY_MSK;
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- key_flags |= STA_KEY_FLG_CCMP;
- memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen);
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- key_flags |= STA_KEY_FLG_TKIP;
- sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
- for (i = 0; i < 5; i++)
- sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
- memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen);
- break;
- case WLAN_CIPHER_SUITE_WEP104:
- key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
- /* fall through */
- case WLAN_CIPHER_SUITE_WEP40:
- key_flags |= STA_KEY_FLG_WEP;
- memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen);
- break;
- default:
- WARN_ON(1);
- return -EINVAL;
- }
-
- if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- /* key pointer (offset) */
- sta_cmd.key.key_offset = keyconf->hw_key_idx;
-
- sta_cmd.key.key_flags = key_flags;
- sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
- sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
-
- return iwl_send_add_sta(priv, &sta_cmd, cmd_flags);
-}
-
-void iwl_update_tkip_key(struct iwl_priv *priv,
- struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta, u32 iv32, u16 *phase1key)
-{
- u8 sta_id = iwlagn_key_sta_id(priv, vif, sta);
-
- if (sta_id == IWL_INVALID_STATION)
- return;
-
- if (iwl_scan_cancel(priv)) {
- /* cancel scan failed, just live w/ bad key and rely
- briefly on SW decryption */
- return;
- }
-
- iwlagn_send_sta_key(priv, keyconf, sta_id,
- iv32, phase1key, CMD_ASYNC);
-}
-
-int iwl_remove_dynamic_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta)
-{
- struct iwl_addsta_cmd sta_cmd;
- u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
- __le16 key_flags;
-
- /* if station isn't there, neither is the key */
- if (sta_id == IWL_INVALID_STATION)
- return -ENOENT;
-
- spin_lock_bh(&priv->sta_lock);
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
- if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE))
- sta_id = IWL_INVALID_STATION;
- spin_unlock_bh(&priv->sta_lock);
-
- if (sta_id == IWL_INVALID_STATION)
- return 0;
-
- lockdep_assert_held(&priv->mutex);
-
- ctx->key_mapping_keys--;
-
- IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n",
- keyconf->keyidx, sta_id);
-
- if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table))
- IWL_ERR(priv, "offset %d not used in uCode key table.\n",
- keyconf->hw_key_idx);
-
- key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC |
- STA_KEY_FLG_INVALID;
-
- if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- sta_cmd.key.key_flags = key_flags;
- sta_cmd.key.key_offset = keyconf->hw_key_idx;
- sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
- sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
-
- return iwl_send_add_sta(priv, &sta_cmd, 0);
-}
-
-int iwl_set_dynamic_key(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta)
-{
- struct ieee80211_key_seq seq;
- u16 p1k[5];
- int ret;
- u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
- const u8 *addr;
-
- if (sta_id == IWL_INVALID_STATION)
- return -EINVAL;
-
- lockdep_assert_held(&priv->mutex);
-
- keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv);
- if (keyconf->hw_key_idx == WEP_INVALID_OFFSET)
- return -ENOSPC;
-
- ctx->key_mapping_keys++;
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- if (sta)
- addr = sta->addr;
- else /* station mode case only */
- addr = ctx->active.bssid_addr;
-
- /* pre-fill phase 1 key into device cache */
- ieee80211_get_key_rx_seq(keyconf, 0, &seq);
- ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
- ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
- seq.tkip.iv32, p1k, 0);
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
- 0, NULL, 0);
- break;
- default:
- IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher);
- ret = -EINVAL;
- }
-
- if (ret) {
- ctx->key_mapping_keys--;
- clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table);
- }
-
- IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
- keyconf->cipher, keyconf->keylen, keyconf->keyidx,
- sta ? sta->addr : NULL, ret);
-
- return ret;
-}
-
-/**
- * iwlagn_alloc_bcast_station - add broadcast station into driver's station table.
- *
- * This adds the broadcast station into the driver's station table
- * and marks it driver active, so that it will be restored to the
- * device at the next best time.
- */
-int iwlagn_alloc_bcast_station(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- struct iwl_link_quality_cmd *link_cmd;
- u8 sta_id;
-
- spin_lock_bh(&priv->sta_lock);
- sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_ERR(priv, "Unable to prepare broadcast station\n");
- spin_unlock_bh(&priv->sta_lock);
-
- return -EINVAL;
- }
-
- priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE;
- priv->stations[sta_id].used |= IWL_STA_BCAST;
- spin_unlock_bh(&priv->sta_lock);
-
- link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
- if (!link_cmd) {
- IWL_ERR(priv,
- "Unable to initialize rate scaling for bcast station.\n");
- return -ENOMEM;
- }
-
- spin_lock_bh(&priv->sta_lock);
- priv->stations[sta_id].lq = link_cmd;
- spin_unlock_bh(&priv->sta_lock);
-
- return 0;
-}
-
-/**
- * iwl_update_bcast_station - update broadcast station's LQ command
- *
- * Only used by iwlagn. Placed here to have all bcast station management
- * code together.
- */
-int iwl_update_bcast_station(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- struct iwl_link_quality_cmd *link_cmd;
- u8 sta_id = ctx->bcast_sta_id;
-
- link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
- if (!link_cmd) {
- IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n");
- return -ENOMEM;
- }
-
- spin_lock_bh(&priv->sta_lock);
- if (priv->stations[sta_id].lq)
- kfree(priv->stations[sta_id].lq);
- else
- IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n");
- priv->stations[sta_id].lq = link_cmd;
- spin_unlock_bh(&priv->sta_lock);
-
- return 0;
-}
-
-int iwl_update_bcast_stations(struct iwl_priv *priv)
-{
- struct iwl_rxon_context *ctx;
- int ret = 0;
-
- for_each_context(priv, ctx) {
- ret = iwl_update_bcast_station(priv, ctx);
- if (ret)
- break;
- }
-
- return ret;
-}
-
-/**
- * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
- */
-int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
-{
- struct iwl_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&priv->mutex);
-
- /* Remove "disable" flag, to enable Tx for this TID */
- spin_lock_bh(&priv->sta_lock);
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
- priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_bh(&priv->sta_lock);
-
- return iwl_send_add_sta(priv, &sta_cmd, 0);
-}
-
-int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
- int tid, u16 ssn)
-{
- int sta_id;
- struct iwl_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&priv->mutex);
-
- sta_id = iwl_sta_id(sta);
- if (sta_id == IWL_INVALID_STATION)
- return -ENXIO;
-
- spin_lock_bh(&priv->sta_lock);
- priv->stations[sta_id].sta.station_flags_msk = 0;
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
- priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
- priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_bh(&priv->sta_lock);
-
- return iwl_send_add_sta(priv, &sta_cmd, 0);
-}
-
-int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
- int tid)
-{
- int sta_id;
- struct iwl_addsta_cmd sta_cmd;
-
- lockdep_assert_held(&priv->mutex);
-
- sta_id = iwl_sta_id(sta);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
- return -ENXIO;
- }
-
- spin_lock_bh(&priv->sta_lock);
- priv->stations[sta_id].sta.station_flags_msk = 0;
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
- priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
- spin_unlock_bh(&priv->sta_lock);
-
- return iwl_send_add_sta(priv, &sta_cmd, 0);
-}
-
-
-
-void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
-{
- struct iwl_addsta_cmd cmd = {
- .mode = STA_CONTROL_MODIFY_MSK,
- .station_flags = STA_FLG_PWR_SAVE_MSK,
- .station_flags_msk = STA_FLG_PWR_SAVE_MSK,
- .sta.sta_id = sta_id,
- .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK,
- .sleep_tx_count = cpu_to_le16(cnt),
- };
-
- iwl_send_add_sta(priv, &cmd, CMD_ASYNC);
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c
deleted file mode 100644
index c4736c8834c5..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/tt.c
+++ /dev/null
@@ -1,685 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-#include "iwl-io.h"
-#include "iwl-modparams.h"
-#include "iwl-debug.h"
-#include "agn.h"
-#include "dev.h"
-#include "commands.h"
-#include "tt.h"
-
-/* default Thermal Throttling transaction table
- * Current state | Throttling Down | Throttling Up
- *=============================================================================
- * Condition Nxt State Condition Nxt State Condition Nxt State
- *-----------------------------------------------------------------------------
- * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
- * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
- * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
- * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
- *=============================================================================
- */
-static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = {
- {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104},
- {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
-};
-static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = {
- {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95},
- {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
-};
-static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = {
- {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
-};
-static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = {
- {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD},
- {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX},
- {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}
-};
-
-/* Advance Thermal Throttling default restriction table */
-static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = {
- {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true },
- {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true },
- {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false },
- {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false }
-};
-
-bool iwl_tt_is_low_power_state(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
-
- if (tt->state >= IWL_TI_1)
- return true;
- return false;
-}
-
-u8 iwl_tt_current_power_mode(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
-
- return tt->tt_power_mode;
-}
-
-bool iwl_ht_enabled(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- struct iwl_tt_restriction *restriction;
-
- if (!priv->thermal_throttle.advanced_tt)
- return true;
- restriction = tt->restriction + tt->state;
- return restriction->is_ht;
-}
-
-static bool iwl_within_ct_kill_margin(struct iwl_priv *priv)
-{
- s32 temp = priv->temperature; /* degrees CELSIUS except specified */
- bool within_margin = false;
-
- if (!priv->thermal_throttle.advanced_tt)
- within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
- CT_KILL_THRESHOLD_LEGACY) ? true : false;
- else
- within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
- CT_KILL_THRESHOLD) ? true : false;
- return within_margin;
-}
-
-bool iwl_check_for_ct_kill(struct iwl_priv *priv)
-{
- bool is_ct_kill = false;
-
- if (iwl_within_ct_kill_margin(priv)) {
- iwl_tt_enter_ct_kill(priv);
- is_ct_kill = true;
- }
- return is_ct_kill;
-}
-
-enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- struct iwl_tt_restriction *restriction;
-
- if (!priv->thermal_throttle.advanced_tt)
- return IWL_ANT_OK_MULTI;
- restriction = tt->restriction + tt->state;
- return restriction->tx_stream;
-}
-
-enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- struct iwl_tt_restriction *restriction;
-
- if (!priv->thermal_throttle.advanced_tt)
- return IWL_ANT_OK_MULTI;
- restriction = tt->restriction + tt->state;
- return restriction->rx_stream;
-}
-
-#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
-#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */
-
-/*
- * toggle the bit to wake up uCode and check the temperature
- * if the temperature is below CT, uCode will stay awake and send card
- * state notification with CT_KILL bit clear to inform Thermal Throttling
- * Management to change state. Otherwise, uCode will go back to sleep
- * without doing anything, driver should continue the 5 seconds timer
- * to wake up uCode for temperature check until temperature drop below CT
- */
-static void iwl_tt_check_exit_ct_kill(unsigned long data)
-{
- struct iwl_priv *priv = (struct iwl_priv *)data;
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- unsigned long flags;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (tt->state == IWL_TI_CT_KILL) {
- if (priv->thermal_throttle.ct_kill_toggle) {
- iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
- priv->thermal_throttle.ct_kill_toggle = false;
- } else {
- iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
- CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
- priv->thermal_throttle.ct_kill_toggle = true;
- }
- iwl_read32(priv->trans, CSR_UCODE_DRV_GP1);
- if (iwl_trans_grab_nic_access(priv->trans, false, &flags))
- iwl_trans_release_nic_access(priv->trans, &flags);
-
- /* Reschedule the ct_kill timer to occur in
- * CT_KILL_EXIT_DURATION seconds to ensure we get a
- * thermal update */
- IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n");
- mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
- jiffies + CT_KILL_EXIT_DURATION * HZ);
- }
-}
-
-static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
- bool stop)
-{
- if (stop) {
- IWL_DEBUG_TEMP(priv, "Stop all queues\n");
- if (priv->mac80211_registered)
- ieee80211_stop_queues(priv->hw);
- IWL_DEBUG_TEMP(priv,
- "Schedule 5 seconds CT_KILL Timer\n");
- mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
- jiffies + CT_KILL_EXIT_DURATION * HZ);
- } else {
- IWL_DEBUG_TEMP(priv, "Wake all queues\n");
- if (priv->mac80211_registered)
- ieee80211_wake_queues(priv->hw);
- }
-}
-
-static void iwl_tt_ready_for_ct_kill(unsigned long data)
-{
- struct iwl_priv *priv = (struct iwl_priv *)data;
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- /* temperature timer expired, ready to go into CT_KILL state */
- if (tt->state != IWL_TI_CT_KILL) {
- IWL_DEBUG_TEMP(priv, "entering CT_KILL state when "
- "temperature timer expired\n");
- tt->state = IWL_TI_CT_KILL;
- set_bit(STATUS_CT_KILL, &priv->status);
- iwl_perform_ct_kill_task(priv, true);
- }
-}
-
-static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
-{
- IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n");
- /* make request to retrieve statistics information */
- iwl_send_statistics_request(priv, 0, false);
- /* Reschedule the ct_kill wait timer */
- mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
- jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION));
-}
-
-#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY)
-#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100)
-#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90)
-
-/*
- * Legacy thermal throttling
- * 1) Avoid NIC destruction due to high temperatures
- * Chip will identify dangerously high temperatures that can
- * harm the device and will power down
- * 2) Avoid the NIC power down due to high temperature
- * Throttle early enough to lower the power consumption before
- * drastic steps are needed
- */
-static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- enum iwl_tt_state old_state;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if ((tt->tt_previous_temp) &&
- (temp > tt->tt_previous_temp) &&
- ((temp - tt->tt_previous_temp) >
- IWL_TT_INCREASE_MARGIN)) {
- IWL_DEBUG_TEMP(priv,
- "Temperature increase %d degree Celsius\n",
- (temp - tt->tt_previous_temp));
- }
-#endif
- old_state = tt->state;
- /* in Celsius */
- if (temp >= IWL_MINIMAL_POWER_THRESHOLD)
- tt->state = IWL_TI_CT_KILL;
- else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2)
- tt->state = IWL_TI_2;
- else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1)
- tt->state = IWL_TI_1;
- else
- tt->state = IWL_TI_0;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- tt->tt_previous_temp = temp;
-#endif
- /* stop ct_kill_waiting_tm timer */
- del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
- if (tt->state != old_state) {
- switch (tt->state) {
- case IWL_TI_0:
- /*
- * When the system is ready to go back to IWL_TI_0
- * we only have to call iwl_power_update_mode() to
- * do so.
- */
- break;
- case IWL_TI_1:
- tt->tt_power_mode = IWL_POWER_INDEX_3;
- break;
- case IWL_TI_2:
- tt->tt_power_mode = IWL_POWER_INDEX_4;
- break;
- default:
- tt->tt_power_mode = IWL_POWER_INDEX_5;
- break;
- }
- mutex_lock(&priv->mutex);
- if (old_state == IWL_TI_CT_KILL)
- clear_bit(STATUS_CT_KILL, &priv->status);
- if (tt->state != IWL_TI_CT_KILL &&
- iwl_power_update_mode(priv, true)) {
- /* TT state not updated
- * try again during next temperature read
- */
- if (old_state == IWL_TI_CT_KILL)
- set_bit(STATUS_CT_KILL, &priv->status);
- tt->state = old_state;
- IWL_ERR(priv, "Cannot update power mode, "
- "TT state not updated\n");
- } else {
- if (tt->state == IWL_TI_CT_KILL) {
- if (force) {
- set_bit(STATUS_CT_KILL, &priv->status);
- iwl_perform_ct_kill_task(priv, true);
- } else {
- iwl_prepare_ct_kill_task(priv);
- tt->state = old_state;
- }
- } else if (old_state == IWL_TI_CT_KILL &&
- tt->state != IWL_TI_CT_KILL)
- iwl_perform_ct_kill_task(priv, false);
- IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n",
- tt->state);
- IWL_DEBUG_TEMP(priv, "Power Index change to %u\n",
- tt->tt_power_mode);
- }
- mutex_unlock(&priv->mutex);
- }
-}
-
-/*
- * Advance thermal throttling
- * 1) Avoid NIC destruction due to high temperatures
- * Chip will identify dangerously high temperatures that can
- * harm the device and will power down
- * 2) Avoid the NIC power down due to high temperature
- * Throttle early enough to lower the power consumption before
- * drastic steps are needed
- * Actions include relaxing the power down sleep thresholds and
- * decreasing the number of TX streams
- * 3) Avoid throughput performance impact as much as possible
- *
- *=============================================================================
- * Condition Nxt State Condition Nxt State Condition Nxt State
- *-----------------------------------------------------------------------------
- * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
- * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
- * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
- * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
- *=============================================================================
- */
-static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- int i;
- bool changed = false;
- enum iwl_tt_state old_state;
- struct iwl_tt_trans *transaction;
-
- old_state = tt->state;
- for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) {
- /* based on the current TT state,
- * find the curresponding transaction table
- * each table has (IWL_TI_STATE_MAX - 1) entries
- * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1))
- * will advance to the correct table.
- * then based on the current temperature
- * find the next state need to transaction to
- * go through all the possible (IWL_TI_STATE_MAX - 1) entries
- * in the current table to see if transaction is needed
- */
- transaction = tt->transaction +
- ((old_state * (IWL_TI_STATE_MAX - 1)) + i);
- if (temp >= transaction->tt_low &&
- temp <= transaction->tt_high) {
-#ifdef CONFIG_IWLWIFI_DEBUG
- if ((tt->tt_previous_temp) &&
- (temp > tt->tt_previous_temp) &&
- ((temp - tt->tt_previous_temp) >
- IWL_TT_INCREASE_MARGIN)) {
- IWL_DEBUG_TEMP(priv,
- "Temperature increase %d "
- "degree Celsius\n",
- (temp - tt->tt_previous_temp));
- }
- tt->tt_previous_temp = temp;
-#endif
- if (old_state !=
- transaction->next_state) {
- changed = true;
- tt->state =
- transaction->next_state;
- }
- break;
- }
- }
- /* stop ct_kill_waiting_tm timer */
- del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
- if (changed) {
- if (tt->state >= IWL_TI_1) {
- /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */
- tt->tt_power_mode = IWL_POWER_INDEX_5;
-
- if (!iwl_ht_enabled(priv)) {
- struct iwl_rxon_context *ctx;
-
- for_each_context(priv, ctx) {
- struct iwl_rxon_cmd *rxon;
-
- rxon = &ctx->staging;
-
- /* disable HT */
- rxon->flags &= ~(
- RXON_FLG_CHANNEL_MODE_MSK |
- RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
- RXON_FLG_HT40_PROT_MSK |
- RXON_FLG_HT_PROT_MSK);
- }
- } else {
- /* check HT capability and set
- * according to the system HT capability
- * in case get disabled before */
- iwl_set_rxon_ht(priv, &priv->current_ht_config);
- }
-
- } else {
- /*
- * restore system power setting -- it will be
- * recalculated automatically.
- */
-
- /* check HT capability and set
- * according to the system HT capability
- * in case get disabled before */
- iwl_set_rxon_ht(priv, &priv->current_ht_config);
- }
- mutex_lock(&priv->mutex);
- if (old_state == IWL_TI_CT_KILL)
- clear_bit(STATUS_CT_KILL, &priv->status);
- if (tt->state != IWL_TI_CT_KILL &&
- iwl_power_update_mode(priv, true)) {
- /* TT state not updated
- * try again during next temperature read
- */
- IWL_ERR(priv, "Cannot update power mode, "
- "TT state not updated\n");
- if (old_state == IWL_TI_CT_KILL)
- set_bit(STATUS_CT_KILL, &priv->status);
- tt->state = old_state;
- } else {
- IWL_DEBUG_TEMP(priv,
- "Thermal Throttling to new state: %u\n",
- tt->state);
- if (old_state != IWL_TI_CT_KILL &&
- tt->state == IWL_TI_CT_KILL) {
- if (force) {
- IWL_DEBUG_TEMP(priv,
- "Enter IWL_TI_CT_KILL\n");
- set_bit(STATUS_CT_KILL, &priv->status);
- iwl_perform_ct_kill_task(priv, true);
- } else {
- tt->state = old_state;
- iwl_prepare_ct_kill_task(priv);
- }
- } else if (old_state == IWL_TI_CT_KILL &&
- tt->state != IWL_TI_CT_KILL) {
- IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n");
- iwl_perform_ct_kill_task(priv, false);
- }
- }
- mutex_unlock(&priv->mutex);
- }
-}
-
-/* Card State Notification indicated reach critical temperature
- * if PSP not enable, no Thermal Throttling function will be performed
- * just set the GP1 bit to acknowledge the event
- * otherwise, go into IWL_TI_CT_KILL state
- * since Card State Notification will not provide any temperature reading
- * for Legacy mode
- * so just pass the CT_KILL temperature to iwl_legacy_tt_handler()
- * for advance mode
- * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state
- */
-static void iwl_bg_ct_enter(struct work_struct *work)
-{
- struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter);
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (!iwl_is_ready(priv))
- return;
-
- if (tt->state != IWL_TI_CT_KILL) {
- IWL_ERR(priv, "Device reached critical temperature "
- "- ucode going to sleep!\n");
- if (!priv->thermal_throttle.advanced_tt)
- iwl_legacy_tt_handler(priv,
- IWL_MINIMAL_POWER_THRESHOLD,
- true);
- else
- iwl_advance_tt_handler(priv,
- CT_KILL_THRESHOLD + 1, true);
- }
-}
-
-/* Card State Notification indicated out of critical temperature
- * since Card State Notification will not provide any temperature reading
- * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
- * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
- */
-static void iwl_bg_ct_exit(struct work_struct *work)
-{
- struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit);
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (!iwl_is_ready(priv))
- return;
-
- /* stop ct_kill_exit_tm timer */
- del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
-
- if (tt->state == IWL_TI_CT_KILL) {
- IWL_ERR(priv,
- "Device temperature below critical"
- "- ucode awake!\n");
- /*
- * exit from CT_KILL state
- * reset the current temperature reading
- */
- priv->temperature = 0;
- if (!priv->thermal_throttle.advanced_tt)
- iwl_legacy_tt_handler(priv,
- IWL_REDUCED_PERFORMANCE_THRESHOLD_2,
- true);
- else
- iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD,
- true);
- }
-}
-
-void iwl_tt_enter_ct_kill(struct iwl_priv *priv)
-{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n");
- queue_work(priv->workqueue, &priv->ct_enter);
-}
-
-void iwl_tt_exit_ct_kill(struct iwl_priv *priv)
-{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n");
- queue_work(priv->workqueue, &priv->ct_exit);
-}
-
-static void iwl_bg_tt_work(struct work_struct *work)
-{
- struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work);
- s32 temp = priv->temperature; /* degrees CELSIUS except specified */
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- if (!priv->thermal_throttle.advanced_tt)
- iwl_legacy_tt_handler(priv, temp, false);
- else
- iwl_advance_tt_handler(priv, temp, false);
-}
-
-void iwl_tt_handler(struct iwl_priv *priv)
-{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n");
- queue_work(priv->workqueue, &priv->tt_work);
-}
-
-/* Thermal throttling initialization
- * For advance thermal throttling:
- * Initialize Thermal Index and temperature threshold table
- * Initialize thermal throttling restriction table
- */
-void iwl_tt_initialize(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1);
- struct iwl_tt_trans *transaction;
-
- IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n");
-
- memset(tt, 0, sizeof(struct iwl_tt_mgmt));
-
- tt->state = IWL_TI_0;
- setup_timer(&priv->thermal_throttle.ct_kill_exit_tm,
- iwl_tt_check_exit_ct_kill, (unsigned long)priv);
- setup_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
- iwl_tt_ready_for_ct_kill, (unsigned long)priv);
- /* setup deferred ct kill work */
- INIT_WORK(&priv->tt_work, iwl_bg_tt_work);
- INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
- INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
-
- if (priv->lib->adv_thermal_throttle) {
- IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n");
- tt->restriction = kcalloc(IWL_TI_STATE_MAX,
- sizeof(struct iwl_tt_restriction),
- GFP_KERNEL);
- tt->transaction = kcalloc(IWL_TI_STATE_MAX *
- (IWL_TI_STATE_MAX - 1),
- sizeof(struct iwl_tt_trans),
- GFP_KERNEL);
- if (!tt->restriction || !tt->transaction) {
- IWL_ERR(priv, "Fallback to Legacy Throttling\n");
- priv->thermal_throttle.advanced_tt = false;
- kfree(tt->restriction);
- tt->restriction = NULL;
- kfree(tt->transaction);
- tt->transaction = NULL;
- } else {
- transaction = tt->transaction +
- (IWL_TI_0 * (IWL_TI_STATE_MAX - 1));
- memcpy(transaction, &tt_range_0[0], size);
- transaction = tt->transaction +
- (IWL_TI_1 * (IWL_TI_STATE_MAX - 1));
- memcpy(transaction, &tt_range_1[0], size);
- transaction = tt->transaction +
- (IWL_TI_2 * (IWL_TI_STATE_MAX - 1));
- memcpy(transaction, &tt_range_2[0], size);
- transaction = tt->transaction +
- (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1));
- memcpy(transaction, &tt_range_3[0], size);
- size = sizeof(struct iwl_tt_restriction) *
- IWL_TI_STATE_MAX;
- memcpy(tt->restriction,
- &restriction_range[0], size);
- priv->thermal_throttle.advanced_tt = true;
- }
- } else {
- IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n");
- priv->thermal_throttle.advanced_tt = false;
- }
-}
-
-/* cleanup thermal throttling management related memory and timer */
-void iwl_tt_exit(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
-
- /* stop ct_kill_exit_tm timer if activated */
- del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
- /* stop ct_kill_waiting_tm timer if activated */
- del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
- cancel_work_sync(&priv->tt_work);
- cancel_work_sync(&priv->ct_enter);
- cancel_work_sync(&priv->ct_exit);
-
- if (priv->thermal_throttle.advanced_tt) {
- /* free advance thermal throttling memory */
- kfree(tt->restriction);
- tt->restriction = NULL;
- kfree(tt->transaction);
- tt->transaction = NULL;
- }
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.h b/drivers/net/wireless/iwlwifi/dvm/tt.h
deleted file mode 100644
index 507726534b84..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/tt.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *****************************************************************************/
-#ifndef __iwl_tt_setting_h__
-#define __iwl_tt_setting_h__
-
-#include "commands.h"
-
-#define IWL_ABSOLUTE_ZERO 0
-#define IWL_ABSOLUTE_MAX 0xFFFFFFFF
-#define IWL_TT_INCREASE_MARGIN 5
-#define IWL_TT_CT_KILL_MARGIN 3
-
-enum iwl_antenna_ok {
- IWL_ANT_OK_NONE,
- IWL_ANT_OK_SINGLE,
- IWL_ANT_OK_MULTI,
-};
-
-/* Thermal Throttling State Machine states */
-enum iwl_tt_state {
- IWL_TI_0, /* normal temperature, system power state */
- IWL_TI_1, /* high temperature detect, low power state */
- IWL_TI_2, /* higher temperature detected, lower power state */
- IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */
- IWL_TI_STATE_MAX
-};
-
-/**
- * struct iwl_tt_restriction - Thermal Throttling restriction table
- * @tx_stream: number of tx stream allowed
- * @is_ht: ht enable/disable
- * @rx_stream: number of rx stream allowed
- *
- * This table is used by advance thermal throttling management
- * based on the current thermal throttling state, and determines
- * the number of tx/rx streams and the status of HT operation.
- */
-struct iwl_tt_restriction {
- enum iwl_antenna_ok tx_stream;
- enum iwl_antenna_ok rx_stream;
- bool is_ht;
-};
-
-/**
- * struct iwl_tt_trans - Thermal Throttling transaction table
- * @next_state: next thermal throttling mode
- * @tt_low: low temperature threshold to change state
- * @tt_high: high temperature threshold to change state
- *
- * This is used by the advanced thermal throttling algorithm
- * to determine the next thermal state to go based on the
- * current temperature.
- */
-struct iwl_tt_trans {
- enum iwl_tt_state next_state;
- u32 tt_low;
- u32 tt_high;
-};
-
-/**
- * struct iwl_tt_mgnt - Thermal Throttling Management structure
- * @advanced_tt: advanced thermal throttle required
- * @state: current Thermal Throttling state
- * @tt_power_mode: Thermal Throttling power mode index
- * being used to set power level when
- * when thermal throttling state != IWL_TI_0
- * the tt_power_mode should set to different
- * power mode based on the current tt state
- * @tt_previous_temperature: last measured temperature
- * @iwl_tt_restriction: ptr to restriction tbl, used by advance
- * thermal throttling to determine how many tx/rx streams
- * should be used in tt state; and can HT be enabled or not
- * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling
- * state transaction
- * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature
- * @ct_kill_exit_tm: timer to exit thermal kill
- */
-struct iwl_tt_mgmt {
- enum iwl_tt_state state;
- bool advanced_tt;
- u8 tt_power_mode;
- bool ct_kill_toggle;
-#ifdef CONFIG_IWLWIFI_DEBUG
- s32 tt_previous_temp;
-#endif
- struct iwl_tt_restriction *restriction;
- struct iwl_tt_trans *transaction;
- struct timer_list ct_kill_exit_tm;
- struct timer_list ct_kill_waiting_tm;
-};
-
-u8 iwl_tt_current_power_mode(struct iwl_priv *priv);
-bool iwl_tt_is_low_power_state(struct iwl_priv *priv);
-bool iwl_ht_enabled(struct iwl_priv *priv);
-enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
-enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
-void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
-void iwl_tt_exit_ct_kill(struct iwl_priv *priv);
-void iwl_tt_handler(struct iwl_priv *priv);
-void iwl_tt_initialize(struct iwl_priv *priv);
-void iwl_tt_exit(struct iwl_priv *priv);
-
-#endif /* __iwl_tt_setting_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
deleted file mode 100644
index bddd19769035..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ /dev/null
@@ -1,1412 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/ieee80211.h>
-#include "iwl-io.h"
-#include "iwl-trans.h"
-#include "iwl-agn-hw.h"
-#include "dev.h"
-#include "agn.h"
-
-static const u8 tid_to_ac[] = {
- IEEE80211_AC_BE,
- IEEE80211_AC_BK,
- IEEE80211_AC_BK,
- IEEE80211_AC_BE,
- IEEE80211_AC_VI,
- IEEE80211_AC_VI,
- IEEE80211_AC_VO,
- IEEE80211_AC_VO,
-};
-
-static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
- struct ieee80211_tx_info *info,
- __le16 fc, __le32 *tx_flags)
-{
- if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS ||
- info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT ||
- info->flags & IEEE80211_TX_CTL_AMPDU)
- *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK;
-}
-
-/*
- * handle build REPLY_TX command notification.
- */
-static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
- struct sk_buff *skb,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_hdr *hdr, u8 sta_id)
-{
- __le16 fc = hdr->frame_control;
- __le32 tx_flags = tx_cmd->tx_flags;
-
- tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
-
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
- tx_flags |= TX_CMD_FLG_ACK_MSK;
- else
- tx_flags &= ~TX_CMD_FLG_ACK_MSK;
-
- if (ieee80211_is_probe_resp(fc))
- tx_flags |= TX_CMD_FLG_TSF_MSK;
- else if (ieee80211_is_back_req(fc))
- tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
- else if (info->band == IEEE80211_BAND_2GHZ &&
- priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist &&
- (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
- ieee80211_is_reassoc_req(fc) ||
- info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
- tx_flags |= TX_CMD_FLG_IGNORE_BT;
-
-
- tx_cmd->sta_id = sta_id;
- if (ieee80211_has_morefrags(fc))
- tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
-
- if (ieee80211_is_data_qos(fc)) {
- u8 *qc = ieee80211_get_qos_ctl(hdr);
- tx_cmd->tid_tspec = qc[0] & 0xf;
- tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
- } else {
- tx_cmd->tid_tspec = IWL_TID_NON_QOS;
- if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
- tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
- else
- tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
- }
-
- iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags);
-
- tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
- if (ieee80211_is_mgmt(fc)) {
- if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
- tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3);
- else
- tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2);
- } else {
- tx_cmd->timeout.pm_frame_timeout = 0;
- }
-
- tx_cmd->driver_txop = 0;
- tx_cmd->tx_flags = tx_flags;
- tx_cmd->next_frame_len = 0;
-}
-
-static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- __le16 fc)
-{
- u32 rate_flags;
- int rate_idx;
- u8 rts_retry_limit;
- u8 data_retry_limit;
- u8 rate_plcp;
-
- if (priv->wowlan) {
- rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
- data_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
- } else {
- /* Set retry limit on RTS packets */
- rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT;
-
- /* Set retry limit on DATA packets and Probe Responses*/
- if (ieee80211_is_probe_resp(fc)) {
- data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT;
- rts_retry_limit =
- min(data_retry_limit, rts_retry_limit);
- } else if (ieee80211_is_back_req(fc))
- data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT;
- else
- data_retry_limit = IWLAGN_DEFAULT_TX_RETRY;
- }
-
- tx_cmd->data_retry_limit = data_retry_limit;
- tx_cmd->rts_retry_limit = rts_retry_limit;
-
- /* DATA packets will use the uCode station table for rate/antenna
- * selection */
- if (ieee80211_is_data(fc)) {
- tx_cmd->initial_rate_index = 0;
- tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
- return;
- } else if (ieee80211_is_back_req(fc))
- tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
-
- /**
- * If the current TX rate stored in mac80211 has the MCS bit set, it's
- * not really a TX rate. Thus, we use the lowest supported rate for
- * this band. Also use the lowest supported rate if the stored rate
- * index is invalid.
- */
- rate_idx = info->control.rates[0].idx;
- if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS ||
- (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY))
- rate_idx = rate_lowest_index(
- &priv->nvm_data->bands[info->band], sta);
- /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
- if (info->band == IEEE80211_BAND_5GHZ)
- rate_idx += IWL_FIRST_OFDM_RATE;
- /* Get PLCP rate for tx_cmd->rate_n_flags */
- rate_plcp = iwl_rates[rate_idx].plcp;
- /* Zero out flags for this packet */
- rate_flags = 0;
-
- /* Set CCK flag as needed */
- if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
- rate_flags |= RATE_MCS_CCK_MSK;
-
- /* Set up antennas */
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist &&
- priv->bt_full_concurrent) {
- /* operated as 1x1 in full concurrency mode */
- priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
- first_antenna(priv->nvm_data->valid_tx_ant));
- } else
- priv->mgmt_tx_ant = iwl_toggle_tx_ant(
- priv, priv->mgmt_tx_ant,
- priv->nvm_data->valid_tx_ant);
- rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
-
- /* Set the rate in the TX cmd */
- tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags);
-}
-
-static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
- struct ieee80211_tx_info *info,
- struct iwl_tx_cmd *tx_cmd,
- struct sk_buff *skb_frag)
-{
- struct ieee80211_key_conf *keyconf = info->control.hw_key;
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
- memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
- break;
-
- case WLAN_CIPHER_SUITE_TKIP:
- tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
- ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
- break;
-
- case WLAN_CIPHER_SUITE_WEP104:
- tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
- /* fall through */
- case WLAN_CIPHER_SUITE_WEP40:
- tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP |
- (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT);
-
- memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
-
- IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption "
- "with key %d\n", keyconf->keyidx);
- break;
-
- default:
- IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher);
- break;
- }
-}
-
-/**
- * iwl_sta_id_or_broadcast - return sta_id or broadcast sta
- * @context: the current context
- * @sta: mac80211 station
- *
- * In certain circumstances mac80211 passes a station pointer
- * that may be %NULL, for example during TX or key setup. In
- * that case, we need to use the broadcast station, so this
- * inline wraps that pattern.
- */
-static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context,
- struct ieee80211_sta *sta)
-{
- int sta_id;
-
- if (!sta)
- return context->bcast_sta_id;
-
- sta_id = iwl_sta_id(sta);
-
- /*
- * mac80211 should not be passing a partially
- * initialised station!
- */
- WARN_ON(sta_id == IWL_INVALID_STATION);
-
- return sta_id;
-}
-
-/*
- * start REPLY_TX command process
- */
-int iwlagn_tx_skb(struct iwl_priv *priv,
- struct ieee80211_sta *sta,
- struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl_station_priv *sta_priv = NULL;
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- struct iwl_device_cmd *dev_cmd;
- struct iwl_tx_cmd *tx_cmd;
- __le16 fc;
- u8 hdr_len;
- u16 len, seq_number = 0;
- u8 sta_id, tid = IWL_MAX_TID_COUNT;
- bool is_agg = false, is_data_qos = false;
- int txq_id;
-
- if (info->control.vif)
- ctx = iwl_rxon_ctx_from_vif(info->control.vif);
-
- if (iwl_is_rfkill(priv)) {
- IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n");
- goto drop_unlock_priv;
- }
-
- fc = hdr->frame_control;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (ieee80211_is_auth(fc))
- IWL_DEBUG_TX(priv, "Sending AUTH frame\n");
- else if (ieee80211_is_assoc_req(fc))
- IWL_DEBUG_TX(priv, "Sending ASSOC frame\n");
- else if (ieee80211_is_reassoc_req(fc))
- IWL_DEBUG_TX(priv, "Sending REASSOC frame\n");
-#endif
-
- if (unlikely(ieee80211_is_probe_resp(fc))) {
- struct iwl_wipan_noa_data *noa_data =
- rcu_dereference(priv->noa_data);
-
- if (noa_data &&
- pskb_expand_head(skb, 0, noa_data->length,
- GFP_ATOMIC) == 0) {
- memcpy(skb_put(skb, noa_data->length),
- noa_data->data, noa_data->length);
- hdr = (struct ieee80211_hdr *)skb->data;
- }
- }
-
- hdr_len = ieee80211_hdrlen(fc);
-
- /* For management frames use broadcast id to do not break aggregation */
- if (!ieee80211_is_data(fc))
- sta_id = ctx->bcast_sta_id;
- else {
- /* Find index into station table for destination station */
- sta_id = iwl_sta_id_or_broadcast(ctx, sta);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
- hdr->addr1);
- goto drop_unlock_priv;
- }
- }
-
- if (sta)
- sta_priv = (void *)sta->drv_priv;
-
- if (sta_priv && sta_priv->asleep &&
- (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) {
- /*
- * This sends an asynchronous command to the device,
- * but we can rely on it being processed before the
- * next frame is processed -- and the next frame to
- * this station is the one that will consume this
- * counter.
- * For now set the counter to just 1 since we do not
- * support uAPSD yet.
- *
- * FIXME: If we get two non-bufferable frames one
- * after the other, we might only send out one of
- * them because this is racy.
- */
- iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
- }
-
- dev_cmd = iwl_trans_alloc_tx_cmd(priv->trans);
-
- if (unlikely(!dev_cmd))
- goto drop_unlock_priv;
-
- memset(dev_cmd, 0, sizeof(*dev_cmd));
- dev_cmd->hdr.cmd = REPLY_TX;
- tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
-
- /* Total # bytes to be transmitted */
- len = (u16)skb->len;
- tx_cmd->len = cpu_to_le16(len);
-
- if (info->control.hw_key)
- iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb);
-
- /* TODO need this for burst mode later on */
- iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id);
-
- iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc);
-
- memset(&info->status, 0, sizeof(info->status));
-
- info->driver_data[0] = ctx;
- info->driver_data[1] = dev_cmd;
- /* From now on, we cannot access info->control */
-
- spin_lock(&priv->sta_lock);
-
- if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
- u8 *qc = NULL;
- struct iwl_tid_data *tid_data;
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
- if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
- goto drop_unlock_sta;
- tid_data = &priv->tid_data[sta_id][tid];
-
- /* aggregation is on for this <sta,tid> */
- if (info->flags & IEEE80211_TX_CTL_AMPDU &&
- tid_data->agg.state != IWL_AGG_ON) {
- IWL_ERR(priv,
- "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n",
- info->flags, tid_data->agg.state);
- IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n",
- sta_id, tid,
- IEEE80211_SEQ_TO_SN(tid_data->seq_number));
- goto drop_unlock_sta;
- }
-
- /* We can receive packets from the stack in IWL_AGG_{ON,OFF}
- * only. Check this here.
- */
- if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON &&
- tid_data->agg.state != IWL_AGG_OFF,
- "Tx while agg.state = %d\n", tid_data->agg.state))
- goto drop_unlock_sta;
-
- seq_number = tid_data->seq_number;
- seq_number &= IEEE80211_SCTL_SEQ;
- hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
- hdr->seq_ctrl |= cpu_to_le16(seq_number);
- seq_number += 0x10;
-
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- is_agg = true;
- is_data_qos = true;
- }
-
- /* Copy MAC header from skb into command buffer */
- memcpy(tx_cmd->hdr, hdr, hdr_len);
-
- txq_id = info->hw_queue;
-
- if (is_agg)
- txq_id = priv->tid_data[sta_id][tid].agg.txq_id;
- else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
- /*
- * The microcode will clear the more data
- * bit in the last frame it transmits.
- */
- hdr->frame_control |=
- cpu_to_le16(IEEE80211_FCTL_MOREDATA);
- }
-
- WARN_ON_ONCE(is_agg &&
- priv->queue_to_mac80211[txq_id] != info->hw_queue);
-
- IWL_DEBUG_TX(priv, "TX to [%d|%d] Q:%d - seq: 0x%x\n", sta_id, tid,
- txq_id, seq_number);
-
- if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id))
- goto drop_unlock_sta;
-
- if (is_data_qos && !ieee80211_has_morefrags(fc))
- priv->tid_data[sta_id][tid].seq_number = seq_number;
-
- spin_unlock(&priv->sta_lock);
-
- /*
- * Avoid atomic ops if it isn't an associated client.
- * Also, if this is a packet for aggregation, don't
- * increase the counter because the ucode will stop
- * aggregation queues when their respective station
- * goes to sleep.
- */
- if (sta_priv && sta_priv->client && !is_agg)
- atomic_inc(&sta_priv->pending_frames);
-
- return 0;
-
-drop_unlock_sta:
- if (dev_cmd)
- iwl_trans_free_tx_cmd(priv->trans, dev_cmd);
- spin_unlock(&priv->sta_lock);
-drop_unlock_priv:
- return -1;
-}
-
-static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq)
-{
- int q;
-
- for (q = IWLAGN_FIRST_AMPDU_QUEUE;
- q < priv->cfg->base_params->num_of_queues; q++) {
- if (!test_and_set_bit(q, priv->agg_q_alloc)) {
- priv->queue_to_mac80211[q] = mq;
- return q;
- }
- }
-
- return -ENOSPC;
-}
-
-static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q)
-{
- clear_bit(q, priv->agg_q_alloc);
- priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE;
-}
-
-int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid)
-{
- struct iwl_tid_data *tid_data;
- int sta_id, txq_id;
- enum iwl_agg_state agg_state;
-
- sta_id = iwl_sta_id(sta);
-
- if (sta_id == IWL_INVALID_STATION) {
- IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
- return -ENXIO;
- }
-
- spin_lock_bh(&priv->sta_lock);
-
- tid_data = &priv->tid_data[sta_id][tid];
- txq_id = tid_data->agg.txq_id;
-
- switch (tid_data->agg.state) {
- case IWL_EMPTYING_HW_QUEUE_ADDBA:
- /*
- * This can happen if the peer stops aggregation
- * again before we've had a chance to drain the
- * queue we selected previously, i.e. before the
- * session was really started completely.
- */
- IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
- goto turn_off;
- case IWL_AGG_STARTING:
- /*
- * This can happen when the session is stopped before
- * we receive ADDBA response
- */
- IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n");
- goto turn_off;
- case IWL_AGG_ON:
- break;
- default:
- IWL_WARN(priv,
- "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
- sta_id, tid, tid_data->agg.state);
- spin_unlock_bh(&priv->sta_lock);
- return 0;
- }
-
- tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
-
- /* There are still packets for this RA / TID in the HW */
- if (!test_bit(txq_id, priv->agg_q_alloc)) {
- IWL_DEBUG_TX_QUEUES(priv,
- "stopping AGG on STA/TID %d/%d but hwq %d not used\n",
- sta_id, tid, txq_id);
- } else if (tid_data->agg.ssn != tid_data->next_reclaimed) {
- IWL_DEBUG_TX_QUEUES(priv,
- "Can't proceed: ssn %d, next_recl = %d\n",
- tid_data->agg.ssn,
- tid_data->next_reclaimed);
- tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_DELBA;
- spin_unlock_bh(&priv->sta_lock);
- return 0;
- }
-
- IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
- tid_data->agg.ssn);
-turn_off:
- agg_state = tid_data->agg.state;
- tid_data->agg.state = IWL_AGG_OFF;
-
- spin_unlock_bh(&priv->sta_lock);
-
- if (test_bit(txq_id, priv->agg_q_alloc)) {
- /*
- * If the transport didn't know that we wanted to start
- * agreggation, don't tell it that we want to stop them.
- * This can happen when we don't get the addBA response on
- * time, or we hadn't time to drain the AC queues.
- */
- if (agg_state == IWL_AGG_ON)
- iwl_trans_txq_disable(priv->trans, txq_id, true);
- else
- IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n",
- agg_state);
- iwlagn_dealloc_agg_txq(priv, txq_id);
- }
-
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-
- return 0;
-}
-
-int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn)
-{
- struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
- struct iwl_tid_data *tid_data;
- int sta_id, txq_id, ret;
-
- IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n",
- sta->addr, tid);
-
- sta_id = iwl_sta_id(sta);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_ERR(priv, "Start AGG on invalid station\n");
- return -ENXIO;
- }
- if (unlikely(tid >= IWL_MAX_TID_COUNT))
- return -EINVAL;
-
- if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) {
- IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n");
- return -ENXIO;
- }
-
- txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]);
- if (txq_id < 0) {
- IWL_DEBUG_TX_QUEUES(priv,
- "No free aggregation queue for %pM/%d\n",
- sta->addr, tid);
- return txq_id;
- }
-
- ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
- if (ret)
- return ret;
-
- spin_lock_bh(&priv->sta_lock);
- tid_data = &priv->tid_data[sta_id][tid];
- tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
- tid_data->agg.txq_id = txq_id;
-
- *ssn = tid_data->agg.ssn;
-
- if (*ssn == tid_data->next_reclaimed) {
- IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
- tid_data->agg.ssn);
- tid_data->agg.state = IWL_AGG_STARTING;
- ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- } else {
- IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
- "next_reclaimed = %d\n",
- tid_data->agg.ssn,
- tid_data->next_reclaimed);
- tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
- }
- spin_unlock_bh(&priv->sta_lock);
-
- return ret;
-}
-
-int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid)
-{
- struct iwl_tid_data *tid_data;
- enum iwl_agg_state agg_state;
- int sta_id, txq_id;
- sta_id = iwl_sta_id(sta);
-
- /*
- * First set the agg state to OFF to avoid calling
- * ieee80211_stop_tx_ba_cb in iwlagn_check_ratid_empty.
- */
- spin_lock_bh(&priv->sta_lock);
-
- tid_data = &priv->tid_data[sta_id][tid];
- txq_id = tid_data->agg.txq_id;
- agg_state = tid_data->agg.state;
- IWL_DEBUG_TX_QUEUES(priv, "Flush AGG: sta %d tid %d q %d state %d\n",
- sta_id, tid, txq_id, tid_data->agg.state);
-
- tid_data->agg.state = IWL_AGG_OFF;
-
- spin_unlock_bh(&priv->sta_lock);
-
- if (iwlagn_txfifo_flush(priv, BIT(txq_id)))
- IWL_ERR(priv, "Couldn't flush the AGG queue\n");
-
- if (test_bit(txq_id, priv->agg_q_alloc)) {
- /*
- * If the transport didn't know that we wanted to start
- * agreggation, don't tell it that we want to stop them.
- * This can happen when we don't get the addBA response on
- * time, or we hadn't time to drain the AC queues.
- */
- if (agg_state == IWL_AGG_ON)
- iwl_trans_txq_disable(priv->trans, txq_id, true);
- else
- IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n",
- agg_state);
- iwlagn_dealloc_agg_txq(priv, txq_id);
- }
-
- return 0;
-}
-
-int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u8 buf_size)
-{
- struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
- struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
- int q, fifo;
- u16 ssn;
-
- buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
-
- spin_lock_bh(&priv->sta_lock);
- ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn;
- q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id;
- priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON;
- spin_unlock_bh(&priv->sta_lock);
-
- fifo = ctx->ac_to_fifo[tid_to_ac[tid]];
-
- iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid,
- buf_size, ssn, 0);
-
- /*
- * If the limit is 0, then it wasn't initialised yet,
- * use the default. We can do that since we take the
- * minimum below, and we don't want to go above our
- * default due to hardware restrictions.
- */
- if (sta_priv->max_agg_bufsize == 0)
- sta_priv->max_agg_bufsize =
- LINK_QUAL_AGG_FRAME_LIMIT_DEF;
-
- /*
- * Even though in theory the peer could have different
- * aggregation reorder buffer sizes for different sessions,
- * our ucode doesn't allow for that and has a global limit
- * for each station. Therefore, use the minimum of all the
- * aggregation sessions and our default value.
- */
- sta_priv->max_agg_bufsize =
- min(sta_priv->max_agg_bufsize, buf_size);
-
- if (priv->hw_params.use_rts_for_aggregation) {
- /*
- * switch to RTS/CTS if it is the prefer protection
- * method for HT traffic
- */
-
- sta_priv->lq_sta.lq.general_params.flags |=
- LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
- }
- priv->agg_tids_count++;
- IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n",
- priv->agg_tids_count);
-
- sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit =
- sta_priv->max_agg_bufsize;
-
- IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n",
- sta->addr, tid);
-
- return iwl_send_lq_cmd(priv, ctx,
- &sta_priv->lq_sta.lq, CMD_ASYNC, false);
-}
-
-static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
-{
- struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid];
- enum iwl_rxon_context_id ctx;
- struct ieee80211_vif *vif;
- u8 *addr;
-
- lockdep_assert_held(&priv->sta_lock);
-
- addr = priv->stations[sta_id].sta.sta.addr;
- ctx = priv->stations[sta_id].ctxid;
- vif = priv->contexts[ctx].vif;
-
- switch (priv->tid_data[sta_id][tid].agg.state) {
- case IWL_EMPTYING_HW_QUEUE_DELBA:
- /* There are no packets for this RA / TID in the HW any more */
- if (tid_data->agg.ssn == tid_data->next_reclaimed) {
- IWL_DEBUG_TX_QUEUES(priv,
- "Can continue DELBA flow ssn = next_recl = %d\n",
- tid_data->next_reclaimed);
- iwl_trans_txq_disable(priv->trans,
- tid_data->agg.txq_id, true);
- iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id);
- tid_data->agg.state = IWL_AGG_OFF;
- ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
- }
- break;
- case IWL_EMPTYING_HW_QUEUE_ADDBA:
- /* There are no packets for this RA / TID in the HW any more */
- if (tid_data->agg.ssn == tid_data->next_reclaimed) {
- IWL_DEBUG_TX_QUEUES(priv,
- "Can continue ADDBA flow ssn = next_recl = %d\n",
- tid_data->next_reclaimed);
- tid_data->agg.state = IWL_AGG_STARTING;
- ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
- }
- break;
- default:
- break;
- }
-}
-
-static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx,
- const u8 *addr1)
-{
- struct ieee80211_sta *sta;
- struct iwl_station_priv *sta_priv;
-
- rcu_read_lock();
- sta = ieee80211_find_sta(ctx->vif, addr1);
- if (sta) {
- sta_priv = (void *)sta->drv_priv;
- /* avoid atomic ops if this isn't a client */
- if (sta_priv->client &&
- atomic_dec_return(&sta_priv->pending_frames) == 0)
- ieee80211_sta_block_awake(priv->hw, sta, false);
- }
- rcu_read_unlock();
-}
-
-/**
- * translate ucode response to mac80211 tx status control values
- */
-static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
- struct ieee80211_tx_info *info)
-{
- struct ieee80211_tx_rate *r = &info->status.rates[0];
-
- info->status.antenna =
- ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
- if (rate_n_flags & RATE_MCS_HT_MSK)
- r->flags |= IEEE80211_TX_RC_MCS;
- if (rate_n_flags & RATE_MCS_GF_MSK)
- r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
- if (rate_n_flags & RATE_MCS_HT40_MSK)
- r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
- if (rate_n_flags & RATE_MCS_DUP_MSK)
- r->flags |= IEEE80211_TX_RC_DUP_DATA;
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- r->flags |= IEEE80211_TX_RC_SHORT_GI;
- r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-const char *iwl_get_tx_fail_reason(u32 status)
-{
-#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
-#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
-
- switch (status & TX_STATUS_MSK) {
- case TX_STATUS_SUCCESS:
- return "SUCCESS";
- TX_STATUS_POSTPONE(DELAY);
- TX_STATUS_POSTPONE(FEW_BYTES);
- TX_STATUS_POSTPONE(BT_PRIO);
- TX_STATUS_POSTPONE(QUIET_PERIOD);
- TX_STATUS_POSTPONE(CALC_TTAK);
- TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
- TX_STATUS_FAIL(SHORT_LIMIT);
- TX_STATUS_FAIL(LONG_LIMIT);
- TX_STATUS_FAIL(FIFO_UNDERRUN);
- TX_STATUS_FAIL(DRAIN_FLOW);
- TX_STATUS_FAIL(RFKILL_FLUSH);
- TX_STATUS_FAIL(LIFE_EXPIRE);
- TX_STATUS_FAIL(DEST_PS);
- TX_STATUS_FAIL(HOST_ABORTED);
- TX_STATUS_FAIL(BT_RETRY);
- TX_STATUS_FAIL(STA_INVALID);
- TX_STATUS_FAIL(FRAG_DROPPED);
- TX_STATUS_FAIL(TID_DISABLE);
- TX_STATUS_FAIL(FIFO_FLUSHED);
- TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
- TX_STATUS_FAIL(PASSIVE_NO_RX);
- TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
- }
-
- return "UNKNOWN";
-
-#undef TX_STATUS_FAIL
-#undef TX_STATUS_POSTPONE
-}
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
-{
- status &= AGG_TX_STATUS_MSK;
-
- switch (status) {
- case AGG_TX_STATE_UNDERRUN_MSK:
- priv->reply_agg_tx_stats.underrun++;
- break;
- case AGG_TX_STATE_BT_PRIO_MSK:
- priv->reply_agg_tx_stats.bt_prio++;
- break;
- case AGG_TX_STATE_FEW_BYTES_MSK:
- priv->reply_agg_tx_stats.few_bytes++;
- break;
- case AGG_TX_STATE_ABORT_MSK:
- priv->reply_agg_tx_stats.abort++;
- break;
- case AGG_TX_STATE_LAST_SENT_TTL_MSK:
- priv->reply_agg_tx_stats.last_sent_ttl++;
- break;
- case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK:
- priv->reply_agg_tx_stats.last_sent_try++;
- break;
- case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK:
- priv->reply_agg_tx_stats.last_sent_bt_kill++;
- break;
- case AGG_TX_STATE_SCD_QUERY_MSK:
- priv->reply_agg_tx_stats.scd_query++;
- break;
- case AGG_TX_STATE_TEST_BAD_CRC32_MSK:
- priv->reply_agg_tx_stats.bad_crc32++;
- break;
- case AGG_TX_STATE_RESPONSE_MSK:
- priv->reply_agg_tx_stats.response++;
- break;
- case AGG_TX_STATE_DUMP_TX_MSK:
- priv->reply_agg_tx_stats.dump_tx++;
- break;
- case AGG_TX_STATE_DELAY_TX_MSK:
- priv->reply_agg_tx_stats.delay_tx++;
- break;
- default:
- priv->reply_agg_tx_stats.unknown++;
- break;
- }
-}
-
-static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp)
-{
- return le32_to_cpup((__le32 *)&tx_resp->status +
- tx_resp->frame_count) & IEEE80211_MAX_SN;
-}
-
-static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
- struct iwlagn_tx_resp *tx_resp)
-{
- struct agg_tx_status *frame_status = &tx_resp->status;
- int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
- IWLAGN_TX_RES_TID_POS;
- int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
- IWLAGN_TX_RES_RA_POS;
- struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg;
- u32 status = le16_to_cpu(tx_resp->status.status);
- int i;
-
- WARN_ON(tid == IWL_TID_NON_QOS);
-
- if (agg->wait_for_ba)
- IWL_DEBUG_TX_REPLY(priv,
- "got tx response w/o block-ack\n");
-
- agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
- agg->wait_for_ba = (tx_resp->frame_count > 1);
-
- /*
- * If the BT kill count is non-zero, we'll get this
- * notification again.
- */
- if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
- priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist) {
- IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
- }
-
- if (tx_resp->frame_count == 1)
- return;
-
- IWL_DEBUG_TX_REPLY(priv, "TXQ %d initial_rate 0x%x ssn %d frm_cnt %d\n",
- agg->txq_id,
- le32_to_cpu(tx_resp->rate_n_flags),
- iwlagn_get_scd_ssn(tx_resp), tx_resp->frame_count);
-
- /* Construct bit-map of pending frames within Tx window */
- for (i = 0; i < tx_resp->frame_count; i++) {
- u16 fstatus = le16_to_cpu(frame_status[i].status);
- u8 retry_cnt = (fstatus & AGG_TX_TRY_MSK) >> AGG_TX_TRY_POS;
-
- if (status & AGG_TX_STATUS_MSK)
- iwlagn_count_agg_tx_err_status(priv, fstatus);
-
- if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
- AGG_TX_STATE_ABORT_MSK))
- continue;
-
- if (status & AGG_TX_STATUS_MSK || retry_cnt > 1)
- IWL_DEBUG_TX_REPLY(priv,
- "%d: status %s (0x%04x), try-count (0x%01x)\n",
- i,
- iwl_get_agg_tx_fail_reason(fstatus),
- fstatus & AGG_TX_STATUS_MSK,
- retry_cnt);
- }
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x
-
-const char *iwl_get_agg_tx_fail_reason(u16 status)
-{
- status &= AGG_TX_STATUS_MSK;
- switch (status) {
- case AGG_TX_STATE_TRANSMITTED:
- return "SUCCESS";
- AGG_TX_STATE_FAIL(UNDERRUN_MSK);
- AGG_TX_STATE_FAIL(BT_PRIO_MSK);
- AGG_TX_STATE_FAIL(FEW_BYTES_MSK);
- AGG_TX_STATE_FAIL(ABORT_MSK);
- AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK);
- AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK);
- AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK);
- AGG_TX_STATE_FAIL(SCD_QUERY_MSK);
- AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK);
- AGG_TX_STATE_FAIL(RESPONSE_MSK);
- AGG_TX_STATE_FAIL(DUMP_TX_MSK);
- AGG_TX_STATE_FAIL(DELAY_TX_MSK);
- }
-
- return "UNKNOWN";
-}
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status)
-{
- status &= TX_STATUS_MSK;
-
- switch (status) {
- case TX_STATUS_POSTPONE_DELAY:
- priv->reply_tx_stats.pp_delay++;
- break;
- case TX_STATUS_POSTPONE_FEW_BYTES:
- priv->reply_tx_stats.pp_few_bytes++;
- break;
- case TX_STATUS_POSTPONE_BT_PRIO:
- priv->reply_tx_stats.pp_bt_prio++;
- break;
- case TX_STATUS_POSTPONE_QUIET_PERIOD:
- priv->reply_tx_stats.pp_quiet_period++;
- break;
- case TX_STATUS_POSTPONE_CALC_TTAK:
- priv->reply_tx_stats.pp_calc_ttak++;
- break;
- case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
- priv->reply_tx_stats.int_crossed_retry++;
- break;
- case TX_STATUS_FAIL_SHORT_LIMIT:
- priv->reply_tx_stats.short_limit++;
- break;
- case TX_STATUS_FAIL_LONG_LIMIT:
- priv->reply_tx_stats.long_limit++;
- break;
- case TX_STATUS_FAIL_FIFO_UNDERRUN:
- priv->reply_tx_stats.fifo_underrun++;
- break;
- case TX_STATUS_FAIL_DRAIN_FLOW:
- priv->reply_tx_stats.drain_flow++;
- break;
- case TX_STATUS_FAIL_RFKILL_FLUSH:
- priv->reply_tx_stats.rfkill_flush++;
- break;
- case TX_STATUS_FAIL_LIFE_EXPIRE:
- priv->reply_tx_stats.life_expire++;
- break;
- case TX_STATUS_FAIL_DEST_PS:
- priv->reply_tx_stats.dest_ps++;
- break;
- case TX_STATUS_FAIL_HOST_ABORTED:
- priv->reply_tx_stats.host_abort++;
- break;
- case TX_STATUS_FAIL_BT_RETRY:
- priv->reply_tx_stats.bt_retry++;
- break;
- case TX_STATUS_FAIL_STA_INVALID:
- priv->reply_tx_stats.sta_invalid++;
- break;
- case TX_STATUS_FAIL_FRAG_DROPPED:
- priv->reply_tx_stats.frag_drop++;
- break;
- case TX_STATUS_FAIL_TID_DISABLE:
- priv->reply_tx_stats.tid_disable++;
- break;
- case TX_STATUS_FAIL_FIFO_FLUSHED:
- priv->reply_tx_stats.fifo_flush++;
- break;
- case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL:
- priv->reply_tx_stats.insuff_cf_poll++;
- break;
- case TX_STATUS_FAIL_PASSIVE_NO_RX:
- priv->reply_tx_stats.fail_hw_drop++;
- break;
- case TX_STATUS_FAIL_NO_BEACON_ON_RADAR:
- priv->reply_tx_stats.sta_color_mismatch++;
- break;
- default:
- priv->reply_tx_stats.unknown++;
- break;
- }
-}
-
-static void iwlagn_set_tx_status(struct iwl_priv *priv,
- struct ieee80211_tx_info *info,
- struct iwlagn_tx_resp *tx_resp)
-{
- u16 status = le16_to_cpu(tx_resp->status.status);
-
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-
- info->status.rates[0].count = tx_resp->failure_frame + 1;
- info->flags |= iwl_tx_status_to_mac80211(status);
- iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
- info);
- if (!iwl_is_tx_success(status))
- iwlagn_count_tx_err_status(priv, status);
-}
-
-static void iwl_check_abort_status(struct iwl_priv *priv,
- u8 frame_count, u32 status)
-{
- if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
- IWL_ERR(priv, "Tx flush command to flush out all frames\n");
- if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
- queue_work(priv->workqueue, &priv->tx_flush);
- }
-}
-
-void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- int txq_id = SEQ_TO_QUEUE(sequence);
- int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence);
- struct iwlagn_tx_resp *tx_resp = (void *)pkt->data;
- struct ieee80211_hdr *hdr;
- u32 status = le16_to_cpu(tx_resp->status.status);
- u16 ssn = iwlagn_get_scd_ssn(tx_resp);
- int tid;
- int sta_id;
- int freed;
- struct ieee80211_tx_info *info;
- struct sk_buff_head skbs;
- struct sk_buff *skb;
- struct iwl_rxon_context *ctx;
- bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
-
- tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
- IWLAGN_TX_RES_TID_POS;
- sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
- IWLAGN_TX_RES_RA_POS;
-
- spin_lock_bh(&priv->sta_lock);
-
- if (is_agg) {
- WARN_ON_ONCE(sta_id >= IWLAGN_STATION_COUNT ||
- tid >= IWL_MAX_TID_COUNT);
- if (txq_id != priv->tid_data[sta_id][tid].agg.txq_id)
- IWL_ERR(priv, "txq_id mismatch: %d %d\n", txq_id,
- priv->tid_data[sta_id][tid].agg.txq_id);
- iwl_rx_reply_tx_agg(priv, tx_resp);
- }
-
- __skb_queue_head_init(&skbs);
-
- if (tx_resp->frame_count == 1) {
- u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
- next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10);
-
- if (is_agg) {
- /* If this is an aggregation queue, we can rely on the
- * ssn since the wifi sequence number corresponds to
- * the index in the TFD ring (%256).
- * The seq_ctl is the sequence control of the packet
- * to which this Tx response relates. But if there is a
- * hole in the bitmap of the BA we received, this Tx
- * response may allow to reclaim the hole and all the
- * subsequent packets that were already acked.
- * In that case, seq_ctl != ssn, and the next packet
- * to be reclaimed will be ssn and not seq_ctl.
- */
- next_reclaimed = ssn;
- }
-
- if (tid != IWL_TID_NON_QOS) {
- priv->tid_data[sta_id][tid].next_reclaimed =
- next_reclaimed;
- IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
- next_reclaimed);
- }
-
- iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs);
-
- iwlagn_check_ratid_empty(priv, sta_id, tid);
- freed = 0;
-
- /* process frames */
- skb_queue_walk(&skbs, skb) {
- hdr = (struct ieee80211_hdr *)skb->data;
-
- if (!ieee80211_is_data_qos(hdr->frame_control))
- priv->last_seq_ctl = tx_resp->seq_ctl;
-
- info = IEEE80211_SKB_CB(skb);
- ctx = info->driver_data[0];
- iwl_trans_free_tx_cmd(priv->trans,
- info->driver_data[1]);
-
- memset(&info->status, 0, sizeof(info->status));
-
- if (status == TX_STATUS_FAIL_PASSIVE_NO_RX &&
- ctx->vif &&
- ctx->vif->type == NL80211_IFTYPE_STATION) {
- /* block and stop all queues */
- priv->passive_no_rx = true;
- IWL_DEBUG_TX_QUEUES(priv,
- "stop all queues: passive channel\n");
- ieee80211_stop_queues(priv->hw);
-
- IWL_DEBUG_TX_REPLY(priv,
- "TXQ %d status %s (0x%08x) "
- "rate_n_flags 0x%x retries %d\n",
- txq_id,
- iwl_get_tx_fail_reason(status),
- status,
- le32_to_cpu(tx_resp->rate_n_flags),
- tx_resp->failure_frame);
-
- IWL_DEBUG_TX_REPLY(priv,
- "FrameCnt = %d, idx=%d\n",
- tx_resp->frame_count, cmd_index);
- }
-
- /* check if BAR is needed */
- if (is_agg && !iwl_is_tx_success(status))
- info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
- iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb),
- tx_resp);
- if (!is_agg)
- iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
-
- freed++;
- }
-
- if (tid != IWL_TID_NON_QOS) {
- priv->tid_data[sta_id][tid].next_reclaimed =
- next_reclaimed;
- IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
- next_reclaimed);
- }
-
- if (!is_agg && freed != 1)
- IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed);
-
- IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id,
- iwl_get_tx_fail_reason(status), status);
-
- IWL_DEBUG_TX_REPLY(priv,
- "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n",
- le32_to_cpu(tx_resp->rate_n_flags),
- tx_resp->failure_frame,
- SEQ_TO_INDEX(sequence), ssn,
- le16_to_cpu(tx_resp->seq_ctl));
- }
-
- iwl_check_abort_status(priv, tx_resp->frame_count, status);
- spin_unlock_bh(&priv->sta_lock);
-
- while (!skb_queue_empty(&skbs)) {
- skb = __skb_dequeue(&skbs);
- ieee80211_tx_status(priv->hw, skb);
- }
-}
-
-/**
- * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA
- *
- * Handles block-acknowledge notification from device, which reports success
- * of frames sent via aggregation.
- */
-void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data;
- struct iwl_ht_agg *agg;
- struct sk_buff_head reclaimed_skbs;
- struct sk_buff *skb;
- int sta_id;
- int tid;
- int freed;
-
- /* "flow" corresponds to Tx queue */
- u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
-
- /* "ssn" is start of block-ack Tx window, corresponds to index
- * (in Tx queue's circular buffer) of first TFD/frame in window */
- u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
-
- if (scd_flow >= priv->cfg->base_params->num_of_queues) {
- IWL_ERR(priv,
- "BUG_ON scd_flow is bigger than number of queues\n");
- return;
- }
-
- sta_id = ba_resp->sta_id;
- tid = ba_resp->tid;
- agg = &priv->tid_data[sta_id][tid].agg;
-
- spin_lock_bh(&priv->sta_lock);
-
- if (unlikely(!agg->wait_for_ba)) {
- if (unlikely(ba_resp->bitmap))
- IWL_ERR(priv, "Received BA when not expected\n");
- spin_unlock_bh(&priv->sta_lock);
- return;
- }
-
- if (unlikely(scd_flow != agg->txq_id)) {
- /*
- * FIXME: this is a uCode bug which need to be addressed,
- * log the information and return for now.
- * Since it is can possibly happen very often and in order
- * not to fill the syslog, don't use IWL_ERR or IWL_WARN
- */
- IWL_DEBUG_TX_QUEUES(priv,
- "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n",
- scd_flow, sta_id, tid, agg->txq_id);
- spin_unlock_bh(&priv->sta_lock);
- return;
- }
-
- __skb_queue_head_init(&reclaimed_skbs);
-
- /* Release all TFDs before the SSN, i.e. all TFDs in front of
- * block-ack window (we assume that they've been successfully
- * transmitted ... if not, it's too late anyway). */
- iwl_trans_reclaim(priv->trans, scd_flow, ba_resp_scd_ssn,
- &reclaimed_skbs);
-
- IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, "
- "sta_id = %d\n",
- agg->wait_for_ba,
- (u8 *) &ba_resp->sta_addr_lo32,
- ba_resp->sta_id);
- IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, "
- "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
- ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl),
- (unsigned long long)le64_to_cpu(ba_resp->bitmap),
- scd_flow, ba_resp_scd_ssn, ba_resp->txed,
- ba_resp->txed_2_done);
-
- /* Mark that the expected block-ack response arrived */
- agg->wait_for_ba = false;
-
- /* Sanity check values reported by uCode */
- if (ba_resp->txed_2_done > ba_resp->txed) {
- IWL_DEBUG_TX_REPLY(priv,
- "bogus sent(%d) and ack(%d) count\n",
- ba_resp->txed, ba_resp->txed_2_done);
- /*
- * set txed_2_done = txed,
- * so it won't impact rate scale
- */
- ba_resp->txed = ba_resp->txed_2_done;
- }
-
- priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn;
-
- iwlagn_check_ratid_empty(priv, sta_id, tid);
- freed = 0;
-
- skb_queue_walk(&reclaimed_skbs, skb) {
- struct ieee80211_hdr *hdr = (void *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- if (ieee80211_is_data_qos(hdr->frame_control))
- freed++;
- else
- WARN_ON_ONCE(1);
-
- iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]);
-
- memset(&info->status, 0, sizeof(info->status));
- /* Packet was transmitted successfully, failures come as single
- * frames because before failing a frame the firmware transmits
- * it without aggregation at least once.
- */
- info->flags |= IEEE80211_TX_STAT_ACK;
-
- if (freed == 1) {
- /* this is the first skb we deliver in this batch */
- /* put the rate scaling data there */
- info = IEEE80211_SKB_CB(skb);
- memset(&info->status, 0, sizeof(info->status));
- info->flags |= IEEE80211_TX_STAT_AMPDU;
- info->status.ampdu_ack_len = ba_resp->txed_2_done;
- info->status.ampdu_len = ba_resp->txed;
- iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags,
- info);
- }
- }
-
- spin_unlock_bh(&priv->sta_lock);
-
- while (!skb_queue_empty(&reclaimed_skbs)) {
- skb = __skb_dequeue(&reclaimed_skbs);
- ieee80211_tx_status(priv->hw, skb);
- }
-}
diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c
deleted file mode 100644
index 931a8e4269ef..000000000000
--- a/drivers/net/wireless/iwlwifi/dvm/ucode.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/******************************************************************************
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/kernel.h>
-
-#include "iwl-io.h"
-#include "iwl-agn-hw.h"
-#include "iwl-trans.h"
-#include "iwl-fh.h"
-#include "iwl-op-mode.h"
-
-#include "dev.h"
-#include "agn.h"
-#include "calib.h"
-
-/******************************************************************************
- *
- * uCode download functions
- *
- ******************************************************************************/
-
-static inline const struct fw_img *
-iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type)
-{
- if (ucode_type >= IWL_UCODE_TYPE_MAX)
- return NULL;
-
- return &priv->fw->img[ucode_type];
-}
-
-/*
- * Calibration
- */
-static int iwl_set_Xtal_calib(struct iwl_priv *priv)
-{
- struct iwl_calib_xtal_freq_cmd cmd;
- __le16 *xtal_calib = priv->nvm_data->xtal_calib;
-
- iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD);
- cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]);
- cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]);
- return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
-}
-
-static int iwl_set_temperature_offset_calib(struct iwl_priv *priv)
-{
- struct iwl_calib_temperature_offset_cmd cmd;
-
- memset(&cmd, 0, sizeof(cmd));
- iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
- cmd.radio_sensor_offset = priv->nvm_data->raw_temperature;
- if (!(cmd.radio_sensor_offset))
- cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET;
-
- IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n",
- le16_to_cpu(cmd.radio_sensor_offset));
- return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
-}
-
-static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv)
-{
- struct iwl_calib_temperature_offset_v2_cmd cmd;
-
- memset(&cmd, 0, sizeof(cmd));
- iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
- cmd.radio_sensor_offset_high = priv->nvm_data->kelvin_temperature;
- cmd.radio_sensor_offset_low = priv->nvm_data->raw_temperature;
- if (!cmd.radio_sensor_offset_low) {
- IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n");
- cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET;
- cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET;
- }
- cmd.burntVoltageRef = priv->nvm_data->calib_voltage;
-
- IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n",
- le16_to_cpu(cmd.radio_sensor_offset_high));
- IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n",
- le16_to_cpu(cmd.radio_sensor_offset_low));
- IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n",
- le16_to_cpu(cmd.burntVoltageRef));
-
- return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
-}
-
-static int iwl_send_calib_cfg(struct iwl_priv *priv)
-{
- struct iwl_calib_cfg_cmd calib_cfg_cmd;
- struct iwl_host_cmd cmd = {
- .id = CALIBRATION_CFG_CMD,
- .len = { sizeof(struct iwl_calib_cfg_cmd), },
- .data = { &calib_cfg_cmd, },
- };
-
- memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
- calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL;
- calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL;
- calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL;
- calib_cfg_cmd.ucd_calib_cfg.flags =
- IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK;
-
- return iwl_dvm_send_cmd(priv, &cmd);
-}
-
-int iwl_init_alive_start(struct iwl_priv *priv)
-{
- int ret;
-
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist) {
- /*
- * Tell uCode we are ready to perform calibration
- * need to perform this before any calibration
- * no need to close the envlope since we are going
- * to load the runtime uCode later.
- */
- ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
- BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
- if (ret)
- return ret;
-
- }
-
- ret = iwl_send_calib_cfg(priv);
- if (ret)
- return ret;
-
- /**
- * temperature offset calibration is only needed for runtime ucode,
- * so prepare the value now.
- */
- if (priv->lib->need_temp_offset_calib) {
- if (priv->lib->temp_offset_v2)
- return iwl_set_temperature_offset_calib_v2(priv);
- else
- return iwl_set_temperature_offset_calib(priv);
- }
-
- return 0;
-}
-
-static int iwl_send_wimax_coex(struct iwl_priv *priv)
-{
- struct iwl_wimax_coex_cmd coex_cmd;
-
- /* coexistence is disabled */
- memset(&coex_cmd, 0, sizeof(coex_cmd));
-
- return iwl_dvm_send_cmd_pdu(priv,
- COEX_PRIORITY_TABLE_CMD, 0,
- sizeof(coex_cmd), &coex_cmd);
-}
-
-static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
- ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- ((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
- (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
- 0, 0, 0, 0, 0, 0, 0
-};
-
-void iwl_send_prio_tbl(struct iwl_priv *priv)
-{
- struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd;
-
- memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl,
- sizeof(iwl_bt_prio_tbl));
- if (iwl_dvm_send_cmd_pdu(priv,
- REPLY_BT_COEX_PRIO_TABLE, 0,
- sizeof(prio_tbl_cmd), &prio_tbl_cmd))
- IWL_ERR(priv, "failed to send BT prio tbl command\n");
-}
-
-int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
-{
- struct iwl_bt_coex_prot_env_cmd env_cmd;
- int ret;
-
- env_cmd.action = action;
- env_cmd.type = type;
- ret = iwl_dvm_send_cmd_pdu(priv,
- REPLY_BT_COEX_PROT_ENV, 0,
- sizeof(env_cmd), &env_cmd);
- if (ret)
- IWL_ERR(priv, "failed to send BT env command\n");
- return ret;
-}
-
-static const u8 iwlagn_default_queue_to_tx_fifo[] = {
- IWL_TX_FIFO_VO,
- IWL_TX_FIFO_VI,
- IWL_TX_FIFO_BE,
- IWL_TX_FIFO_BK,
-};
-
-static const u8 iwlagn_ipan_queue_to_tx_fifo[] = {
- IWL_TX_FIFO_VO,
- IWL_TX_FIFO_VI,
- IWL_TX_FIFO_BE,
- IWL_TX_FIFO_BK,
- IWL_TX_FIFO_BK_IPAN,
- IWL_TX_FIFO_BE_IPAN,
- IWL_TX_FIFO_VI_IPAN,
- IWL_TX_FIFO_VO_IPAN,
- IWL_TX_FIFO_BE_IPAN,
- IWL_TX_FIFO_UNUSED,
- IWL_TX_FIFO_AUX,
-};
-
-static int iwl_alive_notify(struct iwl_priv *priv)
-{
- const u8 *queue_to_txf;
- u8 n_queues;
- int ret;
- int i;
-
- iwl_trans_fw_alive(priv->trans, 0);
-
- if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN &&
- priv->nvm_data->sku_cap_ipan_enable) {
- n_queues = ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo);
- queue_to_txf = iwlagn_ipan_queue_to_tx_fifo;
- } else {
- n_queues = ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo);
- queue_to_txf = iwlagn_default_queue_to_tx_fifo;
- }
-
- for (i = 0; i < n_queues; i++)
- if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED)
- iwl_trans_ac_txq_enable(priv->trans, i,
- queue_to_txf[i], 0);
-
- priv->passive_no_rx = false;
- priv->transport_queue_stop = 0;
-
- ret = iwl_send_wimax_coex(priv);
- if (ret)
- return ret;
-
- if (!priv->lib->no_xtal_calib) {
- ret = iwl_set_Xtal_calib(priv);
- if (ret)
- return ret;
- }
-
- return iwl_send_calib_results(priv);
-}
-
-struct iwl_alive_data {
- bool valid;
- u8 subtype;
-};
-
-static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_priv *priv =
- container_of(notif_wait, struct iwl_priv, notif_wait);
- struct iwl_alive_data *alive_data = data;
- struct iwl_alive_resp *palive;
-
- palive = (void *)pkt->data;
-
- IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision "
- "0x%01X 0x%01X\n",
- palive->is_valid, palive->ver_type,
- palive->ver_subtype);
-
- priv->device_pointers.error_event_table =
- le32_to_cpu(palive->error_event_table_ptr);
- priv->device_pointers.log_event_table =
- le32_to_cpu(palive->log_event_table_ptr);
-
- alive_data->subtype = palive->ver_subtype;
- alive_data->valid = palive->is_valid == UCODE_VALID_OK;
-
- return true;
-}
-
-#define UCODE_ALIVE_TIMEOUT HZ
-#define UCODE_CALIB_TIMEOUT (2*HZ)
-
-int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
- enum iwl_ucode_type ucode_type)
-{
- struct iwl_notification_wait alive_wait;
- struct iwl_alive_data alive_data;
- const struct fw_img *fw;
- int ret;
- enum iwl_ucode_type old_type;
- static const u16 alive_cmd[] = { REPLY_ALIVE };
-
- fw = iwl_get_ucode_image(priv, ucode_type);
- if (WARN_ON(!fw))
- return -EINVAL;
-
- old_type = priv->cur_ucode;
- priv->cur_ucode = ucode_type;
- priv->ucode_loaded = false;
-
- iwl_init_notification_wait(&priv->notif_wait, &alive_wait,
- alive_cmd, ARRAY_SIZE(alive_cmd),
- iwl_alive_fn, &alive_data);
-
- ret = iwl_trans_start_fw(priv->trans, fw, false);
- if (ret) {
- priv->cur_ucode = old_type;
- iwl_remove_notification(&priv->notif_wait, &alive_wait);
- return ret;
- }
-
- /*
- * Some things may run in the background now, but we
- * just wait for the ALIVE notification here.
- */
- ret = iwl_wait_notification(&priv->notif_wait, &alive_wait,
- UCODE_ALIVE_TIMEOUT);
- if (ret) {
- priv->cur_ucode = old_type;
- return ret;
- }
-
- if (!alive_data.valid) {
- IWL_ERR(priv, "Loaded ucode is not valid!\n");
- priv->cur_ucode = old_type;
- return -EIO;
- }
-
- priv->ucode_loaded = true;
-
- if (ucode_type != IWL_UCODE_WOWLAN) {
- /* delay a bit to give rfkill time to run */
- msleep(5);
- }
-
- ret = iwl_alive_notify(priv);
- if (ret) {
- IWL_WARN(priv,
- "Could not complete ALIVE transition: %d\n", ret);
- priv->cur_ucode = old_type;
- return ret;
- }
-
- return 0;
-}
-
-static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_priv *priv = data;
- struct iwl_calib_hdr *hdr;
-
- if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) {
- WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION);
- return true;
- }
-
- hdr = (struct iwl_calib_hdr *)pkt->data;
-
- if (iwl_calib_set(priv, hdr, iwl_rx_packet_payload_len(pkt)))
- IWL_ERR(priv, "Failed to record calibration data %d\n",
- hdr->op_code);
-
- return false;
-}
-
-int iwl_run_init_ucode(struct iwl_priv *priv)
-{
- struct iwl_notification_wait calib_wait;
- static const u16 calib_complete[] = {
- CALIBRATION_RES_NOTIFICATION,
- CALIBRATION_COMPLETE_NOTIFICATION
- };
- int ret;
-
- lockdep_assert_held(&priv->mutex);
-
- /* No init ucode required? Curious, but maybe ok */
- if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len)
- return 0;
-
- iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
- calib_complete, ARRAY_SIZE(calib_complete),
- iwlagn_wait_calib, priv);
-
- /* Will also start the device */
- ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
- if (ret)
- goto error;
-
- ret = iwl_init_alive_start(priv);
- if (ret)
- goto error;
-
- /*
- * Some things may run in the background now, but we
- * just wait for the calibration complete notification.
- */
- ret = iwl_wait_notification(&priv->notif_wait, &calib_wait,
- UCODE_CALIB_TIMEOUT);
-
- goto out;
-
- error:
- iwl_remove_notification(&priv->notif_wait, &calib_wait);
- out:
- /* Whatever happened, stop the device */
- iwl_trans_stop_device(priv->trans);
- priv->ucode_loaded = false;
-
- return ret;
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
deleted file mode 100644
index 06f6cc08f451..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/stringify.h>
-#include "iwl-config.h"
-#include "iwl-csr.h"
-#include "iwl-agn-hw.h"
-
-/* Highest firmware API version supported */
-#define IWL1000_UCODE_API_MAX 5
-#define IWL100_UCODE_API_MAX 5
-
-/* Oldest version we won't warn about */
-#define IWL1000_UCODE_API_OK 5
-#define IWL100_UCODE_API_OK 5
-
-/* Lowest firmware API version supported */
-#define IWL1000_UCODE_API_MIN 1
-#define IWL100_UCODE_API_MIN 5
-
-/* EEPROM version */
-#define EEPROM_1000_TX_POWER_VERSION (4)
-#define EEPROM_1000_EEPROM_VERSION (0x15C)
-
-#define IWL1000_FW_PRE "iwlwifi-1000-"
-#define IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE __stringify(api) ".ucode"
-
-#define IWL100_FW_PRE "iwlwifi-100-"
-#define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE __stringify(api) ".ucode"
-
-
-static const struct iwl_base_params iwl1000_base_params = {
- .num_of_queues = IWLAGN_NUM_QUEUES,
- .eeprom_size = OTP_LOW_IMAGE_SIZE,
- .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
- .max_ll_items = OTP_MAX_LL_ITEMS_1000,
- .shadow_ram_support = false,
- .led_compensation = 51,
- .wd_timeout = IWL_WATCHDOG_DISABLED,
- .max_event_log_size = 128,
- .scd_chain_ext_wa = true,
-};
-
-static const struct iwl_ht_params iwl1000_ht_params = {
- .ht_greenfield_support = true,
- .use_rts_for_aggregation = true, /* use rts/cts protection */
- .ht40_bands = BIT(IEEE80211_BAND_2GHZ),
-};
-
-static const struct iwl_eeprom_params iwl1000_eeprom_params = {
- .regulatory_bands = {
- EEPROM_REG_BAND_1_CHANNELS,
- EEPROM_REG_BAND_2_CHANNELS,
- EEPROM_REG_BAND_3_CHANNELS,
- EEPROM_REG_BAND_4_CHANNELS,
- EEPROM_REG_BAND_5_CHANNELS,
- EEPROM_REG_BAND_24_HT40_CHANNELS,
- EEPROM_REGULATORY_BAND_NO_HT40,
- }
-};
-
-#define IWL_DEVICE_1000 \
- .fw_name_pre = IWL1000_FW_PRE, \
- .ucode_api_max = IWL1000_UCODE_API_MAX, \
- .ucode_api_ok = IWL1000_UCODE_API_OK, \
- .ucode_api_min = IWL1000_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_1000, \
- .max_inst_size = IWLAGN_RTC_INST_SIZE, \
- .max_data_size = IWLAGN_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_1000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
- .base_params = &iwl1000_base_params, \
- .eeprom_params = &iwl1000_eeprom_params, \
- .led_mode = IWL_LED_BLINK, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl1000_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
- IWL_DEVICE_1000,
- .ht_params = &iwl1000_ht_params,
-};
-
-const struct iwl_cfg iwl1000_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1000 BG",
- IWL_DEVICE_1000,
-};
-
-#define IWL_DEVICE_100 \
- .fw_name_pre = IWL100_FW_PRE, \
- .ucode_api_max = IWL100_UCODE_API_MAX, \
- .ucode_api_ok = IWL100_UCODE_API_OK, \
- .ucode_api_min = IWL100_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_100, \
- .max_inst_size = IWLAGN_RTC_INST_SIZE, \
- .max_data_size = IWLAGN_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_1000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
- .base_params = &iwl1000_base_params, \
- .eeprom_params = &iwl1000_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .rx_with_siso_diversity = true, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl100_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 100 BGN",
- IWL_DEVICE_100,
- .ht_params = &iwl1000_ht_params,
-};
-
-const struct iwl_cfg iwl100_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 100 BG",
- IWL_DEVICE_100,
-};
-
-MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK));
-MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c
deleted file mode 100644
index 890b95f497d6..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-2000.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/stringify.h>
-#include "iwl-config.h"
-#include "iwl-agn-hw.h"
-#include "dvm/commands.h" /* needed for BT for now */
-
-/* Highest firmware API version supported */
-#define IWL2030_UCODE_API_MAX 6
-#define IWL2000_UCODE_API_MAX 6
-#define IWL105_UCODE_API_MAX 6
-#define IWL135_UCODE_API_MAX 6
-
-/* Oldest version we won't warn about */
-#define IWL2030_UCODE_API_OK 6
-#define IWL2000_UCODE_API_OK 6
-#define IWL105_UCODE_API_OK 6
-#define IWL135_UCODE_API_OK 6
-
-/* Lowest firmware API version supported */
-#define IWL2030_UCODE_API_MIN 5
-#define IWL2000_UCODE_API_MIN 5
-#define IWL105_UCODE_API_MIN 5
-#define IWL135_UCODE_API_MIN 5
-
-/* EEPROM version */
-#define EEPROM_2000_TX_POWER_VERSION (6)
-#define EEPROM_2000_EEPROM_VERSION (0x805)
-
-
-#define IWL2030_FW_PRE "iwlwifi-2030-"
-#define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode"
-
-#define IWL2000_FW_PRE "iwlwifi-2000-"
-#define IWL2000_MODULE_FIRMWARE(api) IWL2000_FW_PRE __stringify(api) ".ucode"
-
-#define IWL105_FW_PRE "iwlwifi-105-"
-#define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode"
-
-#define IWL135_FW_PRE "iwlwifi-135-"
-#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode"
-
-static const struct iwl_base_params iwl2000_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE,
- .num_of_queues = IWLAGN_NUM_QUEUES,
- .pll_cfg_val = 0,
- .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
- .shadow_ram_support = true,
- .led_compensation = 51,
- .wd_timeout = IWL_DEF_WD_TIMEOUT,
- .max_event_log_size = 512,
- .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
- .scd_chain_ext_wa = true,
-};
-
-
-static const struct iwl_base_params iwl2030_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE,
- .num_of_queues = IWLAGN_NUM_QUEUES,
- .pll_cfg_val = 0,
- .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
- .shadow_ram_support = true,
- .led_compensation = 57,
- .wd_timeout = IWL_LONG_WD_TIMEOUT,
- .max_event_log_size = 512,
- .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
- .scd_chain_ext_wa = true,
-};
-
-static const struct iwl_ht_params iwl2000_ht_params = {
- .ht_greenfield_support = true,
- .use_rts_for_aggregation = true, /* use rts/cts protection */
- .ht40_bands = BIT(IEEE80211_BAND_2GHZ),
-};
-
-static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
- .regulatory_bands = {
- EEPROM_REG_BAND_1_CHANNELS,
- EEPROM_REG_BAND_2_CHANNELS,
- EEPROM_REG_BAND_3_CHANNELS,
- EEPROM_REG_BAND_4_CHANNELS,
- EEPROM_REG_BAND_5_CHANNELS,
- EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
- EEPROM_REGULATORY_BAND_NO_HT40,
- },
- .enhanced_txpower = true,
-};
-
-#define IWL_DEVICE_2000 \
- .fw_name_pre = IWL2000_FW_PRE, \
- .ucode_api_max = IWL2000_UCODE_API_MAX, \
- .ucode_api_ok = IWL2000_UCODE_API_OK, \
- .ucode_api_min = IWL2000_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_2000, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .base_params = &iwl2000_base_params, \
- .eeprom_params = &iwl20x0_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-
-const struct iwl_cfg iwl2000_2bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN",
- IWL_DEVICE_2000,
- .ht_params = &iwl2000_ht_params,
-};
-
-const struct iwl_cfg iwl2000_2bgn_d_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN",
- IWL_DEVICE_2000,
- .ht_params = &iwl2000_ht_params,
-};
-
-#define IWL_DEVICE_2030 \
- .fw_name_pre = IWL2030_FW_PRE, \
- .ucode_api_max = IWL2030_UCODE_API_MAX, \
- .ucode_api_ok = IWL2030_UCODE_API_OK, \
- .ucode_api_min = IWL2030_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_2030, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .base_params = &iwl2030_base_params, \
- .eeprom_params = &iwl20x0_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl2030_2bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
- IWL_DEVICE_2030,
- .ht_params = &iwl2000_ht_params,
-};
-
-#define IWL_DEVICE_105 \
- .fw_name_pre = IWL105_FW_PRE, \
- .ucode_api_max = IWL105_UCODE_API_MAX, \
- .ucode_api_ok = IWL105_UCODE_API_OK, \
- .ucode_api_min = IWL105_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_105, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .base_params = &iwl2000_base_params, \
- .eeprom_params = &iwl20x0_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .rx_with_siso_diversity = true, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl105_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 105 BGN",
- IWL_DEVICE_105,
- .ht_params = &iwl2000_ht_params,
-};
-
-const struct iwl_cfg iwl105_bgn_d_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 105D BGN",
- IWL_DEVICE_105,
- .ht_params = &iwl2000_ht_params,
-};
-
-#define IWL_DEVICE_135 \
- .fw_name_pre = IWL135_FW_PRE, \
- .ucode_api_max = IWL135_UCODE_API_MAX, \
- .ucode_api_ok = IWL135_UCODE_API_OK, \
- .ucode_api_min = IWL135_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_135, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .base_params = &iwl2030_base_params, \
- .eeprom_params = &iwl20x0_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .rx_with_siso_diversity = true, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl135_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 135 BGN",
- IWL_DEVICE_135,
- .ht_params = &iwl2000_ht_params,
-};
-
-MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK));
-MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK));
-MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK));
-MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
deleted file mode 100644
index 724194e23414..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/stringify.h>
-#include "iwl-config.h"
-#include "iwl-agn-hw.h"
-#include "iwl-csr.h"
-
-/* Highest firmware API version supported */
-#define IWL5000_UCODE_API_MAX 5
-#define IWL5150_UCODE_API_MAX 2
-
-/* Oldest version we won't warn about */
-#define IWL5000_UCODE_API_OK 5
-#define IWL5150_UCODE_API_OK 2
-
-/* Lowest firmware API version supported */
-#define IWL5000_UCODE_API_MIN 1
-#define IWL5150_UCODE_API_MIN 1
-
-/* EEPROM versions */
-#define EEPROM_5000_TX_POWER_VERSION (4)
-#define EEPROM_5000_EEPROM_VERSION (0x11A)
-#define EEPROM_5050_TX_POWER_VERSION (4)
-#define EEPROM_5050_EEPROM_VERSION (0x21E)
-
-#define IWL5000_FW_PRE "iwlwifi-5000-"
-#define IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE __stringify(api) ".ucode"
-
-#define IWL5150_FW_PRE "iwlwifi-5150-"
-#define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE __stringify(api) ".ucode"
-
-static const struct iwl_base_params iwl5000_base_params = {
- .eeprom_size = IWLAGN_EEPROM_IMG_SIZE,
- .num_of_queues = IWLAGN_NUM_QUEUES,
- .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
- .led_compensation = 51,
- .wd_timeout = IWL_WATCHDOG_DISABLED,
- .max_event_log_size = 512,
- .scd_chain_ext_wa = true,
-};
-
-static const struct iwl_ht_params iwl5000_ht_params = {
- .ht_greenfield_support = true,
- .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
-};
-
-static const struct iwl_eeprom_params iwl5000_eeprom_params = {
- .regulatory_bands = {
- EEPROM_REG_BAND_1_CHANNELS,
- EEPROM_REG_BAND_2_CHANNELS,
- EEPROM_REG_BAND_3_CHANNELS,
- EEPROM_REG_BAND_4_CHANNELS,
- EEPROM_REG_BAND_5_CHANNELS,
- EEPROM_REG_BAND_24_HT40_CHANNELS,
- EEPROM_REG_BAND_52_HT40_CHANNELS
- },
-};
-
-#define IWL_DEVICE_5000 \
- .fw_name_pre = IWL5000_FW_PRE, \
- .ucode_api_max = IWL5000_UCODE_API_MAX, \
- .ucode_api_ok = IWL5000_UCODE_API_OK, \
- .ucode_api_min = IWL5000_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_5000, \
- .max_inst_size = IWLAGN_RTC_INST_SIZE, \
- .max_data_size = IWLAGN_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_5000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \
- .base_params = &iwl5000_base_params, \
- .eeprom_params = &iwl5000_eeprom_params, \
- .led_mode = IWL_LED_BLINK, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl5300_agn_cfg = {
- .name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
- IWL_DEVICE_5000,
- /* at least EEPROM 0x11A has wrong info */
- .valid_tx_ant = ANT_ABC, /* .cfg overwrite */
- .valid_rx_ant = ANT_ABC, /* .cfg overwrite */
- .ht_params = &iwl5000_ht_params,
-};
-
-const struct iwl_cfg iwl5100_bgn_cfg = {
- .name = "Intel(R) WiFi Link 5100 BGN",
- IWL_DEVICE_5000,
- .valid_tx_ant = ANT_B, /* .cfg overwrite */
- .valid_rx_ant = ANT_AB, /* .cfg overwrite */
- .ht_params = &iwl5000_ht_params,
-};
-
-const struct iwl_cfg iwl5100_abg_cfg = {
- .name = "Intel(R) WiFi Link 5100 ABG",
- IWL_DEVICE_5000,
- .valid_tx_ant = ANT_B, /* .cfg overwrite */
- .valid_rx_ant = ANT_AB, /* .cfg overwrite */
-};
-
-const struct iwl_cfg iwl5100_agn_cfg = {
- .name = "Intel(R) WiFi Link 5100 AGN",
- IWL_DEVICE_5000,
- .valid_tx_ant = ANT_B, /* .cfg overwrite */
- .valid_rx_ant = ANT_AB, /* .cfg overwrite */
- .ht_params = &iwl5000_ht_params,
-};
-
-const struct iwl_cfg iwl5350_agn_cfg = {
- .name = "Intel(R) WiMAX/WiFi Link 5350 AGN",
- .fw_name_pre = IWL5000_FW_PRE,
- .ucode_api_max = IWL5000_UCODE_API_MAX,
- .ucode_api_ok = IWL5000_UCODE_API_OK,
- .ucode_api_min = IWL5000_UCODE_API_MIN,
- .device_family = IWL_DEVICE_FAMILY_5000,
- .max_inst_size = IWLAGN_RTC_INST_SIZE,
- .max_data_size = IWLAGN_RTC_DATA_SIZE,
- .nvm_ver = EEPROM_5050_EEPROM_VERSION,
- .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION,
- .base_params = &iwl5000_base_params,
- .eeprom_params = &iwl5000_eeprom_params,
- .ht_params = &iwl5000_ht_params,
- .led_mode = IWL_LED_BLINK,
- .internal_wimax_coex = true,
-};
-
-#define IWL_DEVICE_5150 \
- .fw_name_pre = IWL5150_FW_PRE, \
- .ucode_api_max = IWL5150_UCODE_API_MAX, \
- .ucode_api_ok = IWL5150_UCODE_API_OK, \
- .ucode_api_min = IWL5150_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_5150, \
- .max_inst_size = IWLAGN_RTC_INST_SIZE, \
- .max_data_size = IWLAGN_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_5050_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \
- .base_params = &iwl5000_base_params, \
- .eeprom_params = &iwl5000_eeprom_params, \
- .led_mode = IWL_LED_BLINK, \
- .internal_wimax_coex = true, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl5150_agn_cfg = {
- .name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
- IWL_DEVICE_5150,
- .ht_params = &iwl5000_ht_params,
-
-};
-
-const struct iwl_cfg iwl5150_abg_cfg = {
- .name = "Intel(R) WiMAX/WiFi Link 5150 ABG",
- IWL_DEVICE_5150,
-};
-
-MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK));
-MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
deleted file mode 100644
index 21b2630763dc..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ /dev/null
@@ -1,389 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/stringify.h>
-#include "iwl-config.h"
-#include "iwl-agn-hw.h"
-#include "dvm/commands.h" /* needed for BT for now */
-
-/* Highest firmware API version supported */
-#define IWL6000_UCODE_API_MAX 6
-#define IWL6050_UCODE_API_MAX 5
-#define IWL6000G2_UCODE_API_MAX 6
-#define IWL6035_UCODE_API_MAX 6
-
-/* Oldest version we won't warn about */
-#define IWL6000_UCODE_API_OK 4
-#define IWL6000G2_UCODE_API_OK 5
-#define IWL6050_UCODE_API_OK 5
-#define IWL6000G2B_UCODE_API_OK 6
-#define IWL6035_UCODE_API_OK 6
-
-/* Lowest firmware API version supported */
-#define IWL6000_UCODE_API_MIN 4
-#define IWL6050_UCODE_API_MIN 4
-#define IWL6000G2_UCODE_API_MIN 5
-#define IWL6035_UCODE_API_MIN 6
-
-/* EEPROM versions */
-#define EEPROM_6000_TX_POWER_VERSION (4)
-#define EEPROM_6000_EEPROM_VERSION (0x423)
-#define EEPROM_6050_TX_POWER_VERSION (4)
-#define EEPROM_6050_EEPROM_VERSION (0x532)
-#define EEPROM_6150_TX_POWER_VERSION (6)
-#define EEPROM_6150_EEPROM_VERSION (0x553)
-#define EEPROM_6005_TX_POWER_VERSION (6)
-#define EEPROM_6005_EEPROM_VERSION (0x709)
-#define EEPROM_6030_TX_POWER_VERSION (6)
-#define EEPROM_6030_EEPROM_VERSION (0x709)
-#define EEPROM_6035_TX_POWER_VERSION (6)
-#define EEPROM_6035_EEPROM_VERSION (0x753)
-
-#define IWL6000_FW_PRE "iwlwifi-6000-"
-#define IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE __stringify(api) ".ucode"
-
-#define IWL6050_FW_PRE "iwlwifi-6050-"
-#define IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE __stringify(api) ".ucode"
-
-#define IWL6005_FW_PRE "iwlwifi-6000g2a-"
-#define IWL6005_MODULE_FIRMWARE(api) IWL6005_FW_PRE __stringify(api) ".ucode"
-
-#define IWL6030_FW_PRE "iwlwifi-6000g2b-"
-#define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode"
-
-static const struct iwl_base_params iwl6000_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE,
- .num_of_queues = IWLAGN_NUM_QUEUES,
- .pll_cfg_val = 0,
- .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
- .shadow_ram_support = true,
- .led_compensation = 51,
- .wd_timeout = IWL_DEF_WD_TIMEOUT,
- .max_event_log_size = 512,
- .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
- .scd_chain_ext_wa = true,
-};
-
-static const struct iwl_base_params iwl6050_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE,
- .num_of_queues = IWLAGN_NUM_QUEUES,
- .pll_cfg_val = 0,
- .max_ll_items = OTP_MAX_LL_ITEMS_6x50,
- .shadow_ram_support = true,
- .led_compensation = 51,
- .wd_timeout = IWL_DEF_WD_TIMEOUT,
- .max_event_log_size = 1024,
- .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
- .scd_chain_ext_wa = true,
-};
-
-static const struct iwl_base_params iwl6000_g2_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE,
- .num_of_queues = IWLAGN_NUM_QUEUES,
- .pll_cfg_val = 0,
- .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
- .shadow_ram_support = true,
- .led_compensation = 57,
- .wd_timeout = IWL_LONG_WD_TIMEOUT,
- .max_event_log_size = 512,
- .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
- .scd_chain_ext_wa = true,
-};
-
-static const struct iwl_ht_params iwl6000_ht_params = {
- .ht_greenfield_support = true,
- .use_rts_for_aggregation = true, /* use rts/cts protection */
- .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
-};
-
-static const struct iwl_eeprom_params iwl6000_eeprom_params = {
- .regulatory_bands = {
- EEPROM_REG_BAND_1_CHANNELS,
- EEPROM_REG_BAND_2_CHANNELS,
- EEPROM_REG_BAND_3_CHANNELS,
- EEPROM_REG_BAND_4_CHANNELS,
- EEPROM_REG_BAND_5_CHANNELS,
- EEPROM_6000_REG_BAND_24_HT40_CHANNELS,
- EEPROM_REG_BAND_52_HT40_CHANNELS
- },
- .enhanced_txpower = true,
-};
-
-#define IWL_DEVICE_6005 \
- .fw_name_pre = IWL6005_FW_PRE, \
- .ucode_api_max = IWL6000G2_UCODE_API_MAX, \
- .ucode_api_ok = IWL6000G2_UCODE_API_OK, \
- .ucode_api_min = IWL6000G2_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_6005, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_6005_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
- .base_params = &iwl6000_g2_base_params, \
- .eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl6005_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6005_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG",
- IWL_DEVICE_6005,
-};
-
-const struct iwl_cfg iwl6005_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 BG",
- IWL_DEVICE_6005,
-};
-
-const struct iwl_cfg iwl6005_2agn_sff_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6005_2agn_d_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6005_2agn_mow1_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
-
-#define IWL_DEVICE_6030 \
- .fw_name_pre = IWL6030_FW_PRE, \
- .ucode_api_max = IWL6000G2_UCODE_API_MAX, \
- .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \
- .ucode_api_min = IWL6000G2_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_6030, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_6030_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
- .base_params = &iwl6000_g2_base_params, \
- .eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl6030_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
- IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6030_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG",
- IWL_DEVICE_6030,
-};
-
-const struct iwl_cfg iwl6030_2bgn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN",
- IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6030_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 BG",
- IWL_DEVICE_6030,
-};
-
-#define IWL_DEVICE_6035 \
- .fw_name_pre = IWL6030_FW_PRE, \
- .ucode_api_max = IWL6035_UCODE_API_MAX, \
- .ucode_api_ok = IWL6035_UCODE_API_OK, \
- .ucode_api_min = IWL6035_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_6030, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_6030_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
- .base_params = &iwl6000_g2_base_params, \
- .eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl6035_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
- IWL_DEVICE_6035,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6035_2agn_sff_cfg = {
- .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN",
- IWL_DEVICE_6035,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl1030_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN",
- IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl1030_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1030 BG",
- IWL_DEVICE_6030,
-};
-
-const struct iwl_cfg iwl130_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 130 BGN",
- IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
- .rx_with_siso_diversity = true,
-};
-
-const struct iwl_cfg iwl130_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 130 BG",
- IWL_DEVICE_6030,
- .rx_with_siso_diversity = true,
-};
-
-/*
- * "i": Internal configuration, use internal Power Amplifier
- */
-#define IWL_DEVICE_6000i \
- .fw_name_pre = IWL6000_FW_PRE, \
- .ucode_api_max = IWL6000_UCODE_API_MAX, \
- .ucode_api_ok = IWL6000_UCODE_API_OK, \
- .ucode_api_min = IWL6000_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_6000i, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \
- .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \
- .nvm_ver = EEPROM_6000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \
- .base_params = &iwl6000_base_params, \
- .eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_BLINK, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl6000i_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
- IWL_DEVICE_6000i,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6000i_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG",
- IWL_DEVICE_6000i,
-};
-
-const struct iwl_cfg iwl6000i_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 BG",
- IWL_DEVICE_6000i,
-};
-
-#define IWL_DEVICE_6050 \
- .fw_name_pre = IWL6050_FW_PRE, \
- .ucode_api_max = IWL6050_UCODE_API_MAX, \
- .ucode_api_min = IWL6050_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_6050, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \
- .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \
- .nvm_ver = EEPROM_6050_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_6050_TX_POWER_VERSION, \
- .base_params = &iwl6050_base_params, \
- .eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_BLINK, \
- .internal_wimax_coex = true, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl6050_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
- IWL_DEVICE_6050,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6050_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG",
- IWL_DEVICE_6050,
-};
-
-#define IWL_DEVICE_6150 \
- .fw_name_pre = IWL6050_FW_PRE, \
- .ucode_api_max = IWL6050_UCODE_API_MAX, \
- .ucode_api_min = IWL6050_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_6150, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_6150_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_6150_TX_POWER_VERSION, \
- .base_params = &iwl6050_base_params, \
- .eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_BLINK, \
- .internal_wimax_coex = true, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
-
-const struct iwl_cfg iwl6150_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN",
- IWL_DEVICE_6150,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6150_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG",
- IWL_DEVICE_6150,
-};
-
-const struct iwl_cfg iwl6000_3agn_cfg = {
- .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN",
- .fw_name_pre = IWL6000_FW_PRE,
- .ucode_api_max = IWL6000_UCODE_API_MAX,
- .ucode_api_ok = IWL6000_UCODE_API_OK,
- .ucode_api_min = IWL6000_UCODE_API_MIN,
- .device_family = IWL_DEVICE_FAMILY_6000,
- .max_inst_size = IWL60_RTC_INST_SIZE,
- .max_data_size = IWL60_RTC_DATA_SIZE,
- .nvm_ver = EEPROM_6000_EEPROM_VERSION,
- .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION,
- .base_params = &iwl6000_base_params,
- .eeprom_params = &iwl6000_eeprom_params,
- .ht_params = &iwl6000_ht_params,
- .led_mode = IWL_LED_BLINK,
-};
-
-MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK));
-MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK));
-MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK));
-MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
deleted file mode 100644
index d9a4aee246a6..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ /dev/null
@@ -1,367 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/stringify.h>
-#include "iwl-config.h"
-#include "iwl-agn-hw.h"
-
-/* Highest firmware API version supported */
-#define IWL7260_UCODE_API_MAX 17
-#define IWL7265_UCODE_API_MAX 19
-#define IWL7265D_UCODE_API_MAX 19
-
-/* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK 13
-#define IWL7265_UCODE_API_OK 13
-#define IWL7265D_UCODE_API_OK 13
-
-/* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN 13
-#define IWL7265_UCODE_API_MIN 13
-#define IWL7265D_UCODE_API_MIN 13
-
-/* NVM versions */
-#define IWL7260_NVM_VERSION 0x0a1d
-#define IWL7260_TX_POWER_VERSION 0xffff /* meaningless */
-#define IWL3160_NVM_VERSION 0x709
-#define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */
-#define IWL3165_NVM_VERSION 0x709
-#define IWL3165_TX_POWER_VERSION 0xffff /* meaningless */
-#define IWL7265_NVM_VERSION 0x0a1d
-#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */
-#define IWL7265D_NVM_VERSION 0x0c11
-#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */
-
-/* DCCM offsets and lengths */
-#define IWL7000_DCCM_OFFSET 0x800000
-#define IWL7260_DCCM_LEN 0x14000
-#define IWL3160_DCCM_LEN 0x10000
-#define IWL7265_DCCM_LEN 0x17A00
-
-#define IWL7260_FW_PRE "iwlwifi-7260-"
-#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
-
-#define IWL3160_FW_PRE "iwlwifi-3160-"
-#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode"
-
-#define IWL7265_FW_PRE "iwlwifi-7265-"
-#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
-
-#define IWL7265D_FW_PRE "iwlwifi-7265D-"
-#define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE __stringify(api) ".ucode"
-
-#define NVM_HW_SECTION_NUM_FAMILY_7000 0
-
-static const struct iwl_base_params iwl7000_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
- .num_of_queues = 31,
- .pll_cfg_val = 0,
- .shadow_ram_support = true,
- .led_compensation = 57,
- .wd_timeout = IWL_LONG_WD_TIMEOUT,
- .max_event_log_size = 512,
- .shadow_reg_enable = true,
- .pcie_l1_allowed = true,
- .apmg_wake_up_wa = true,
-};
-
-static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
- .ct_kill_entry = 118,
- .ct_kill_exit = 96,
- .ct_kill_duration = 5,
- .dynamic_smps_entry = 114,
- .dynamic_smps_exit = 110,
- .tx_protection_entry = 114,
- .tx_protection_exit = 108,
- .tx_backoff = {
- {.temperature = 112, .backoff = 300},
- {.temperature = 113, .backoff = 800},
- {.temperature = 114, .backoff = 1500},
- {.temperature = 115, .backoff = 3000},
- {.temperature = 116, .backoff = 5000},
- {.temperature = 117, .backoff = 10000},
- },
- .support_ct_kill = true,
- .support_dynamic_smps = true,
- .support_tx_protection = true,
- .support_tx_backoff = true,
-};
-
-static const struct iwl_ht_params iwl7000_ht_params = {
- .stbc = true,
- .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
-};
-
-#define IWL_DEVICE_7000_COMMON \
- .device_family = IWL_DEVICE_FAMILY_7000, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .base_params = &iwl7000_base_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \
- .non_shared_ant = ANT_A, \
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \
- .dccm_offset = IWL7000_DCCM_OFFSET
-
-#define IWL_DEVICE_7000 \
- IWL_DEVICE_7000_COMMON, \
- .ucode_api_max = IWL7260_UCODE_API_MAX, \
- .ucode_api_ok = IWL7260_UCODE_API_OK, \
- .ucode_api_min = IWL7260_UCODE_API_MIN
-
-#define IWL_DEVICE_7005 \
- IWL_DEVICE_7000_COMMON, \
- .ucode_api_max = IWL7265_UCODE_API_MAX, \
- .ucode_api_ok = IWL7265_UCODE_API_OK, \
- .ucode_api_min = IWL7265_UCODE_API_MIN
-
-#define IWL_DEVICE_7005D \
- IWL_DEVICE_7000_COMMON, \
- .ucode_api_max = IWL7265D_UCODE_API_MAX, \
- .ucode_api_ok = IWL7265D_UCODE_API_OK, \
- .ucode_api_min = IWL7265D_UCODE_API_MIN
-
-const struct iwl_cfg iwl7260_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 7260",
- .fw_name_pre = IWL7260_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL7260_NVM_VERSION,
- .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
- .host_interrupt_operation_mode = true,
- .lp_xtal_workaround = true,
- .dccm_len = IWL7260_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
- .name = "Intel(R) Dual Band Wireless AC 7260",
- .fw_name_pre = IWL7260_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL7260_NVM_VERSION,
- .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
- .high_temp = true,
- .host_interrupt_operation_mode = true,
- .lp_xtal_workaround = true,
- .dccm_len = IWL7260_DCCM_LEN,
- .thermal_params = &iwl7000_high_temp_tt_params,
-};
-
-const struct iwl_cfg iwl7260_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 7260",
- .fw_name_pre = IWL7260_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL7260_NVM_VERSION,
- .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
- .host_interrupt_operation_mode = true,
- .lp_xtal_workaround = true,
- .dccm_len = IWL7260_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7260_n_cfg = {
- .name = "Intel(R) Wireless N 7260",
- .fw_name_pre = IWL7260_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL7260_NVM_VERSION,
- .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
- .host_interrupt_operation_mode = true,
- .lp_xtal_workaround = true,
- .dccm_len = IWL7260_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl3160_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 3160",
- .fw_name_pre = IWL3160_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL3160_NVM_VERSION,
- .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
- .host_interrupt_operation_mode = true,
- .dccm_len = IWL3160_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl3160_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 3160",
- .fw_name_pre = IWL3160_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL3160_NVM_VERSION,
- .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
- .host_interrupt_operation_mode = true,
- .dccm_len = IWL3160_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl3160_n_cfg = {
- .name = "Intel(R) Wireless N 3160",
- .fw_name_pre = IWL3160_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL3160_NVM_VERSION,
- .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
- .host_interrupt_operation_mode = true,
- .dccm_len = IWL3160_DCCM_LEN,
-};
-
-static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = {
- {.pwr = 1600, .backoff = 0},
- {.pwr = 1300, .backoff = 467},
- {.pwr = 900, .backoff = 1900},
- {.pwr = 800, .backoff = 2630},
- {.pwr = 700, .backoff = 3720},
- {.pwr = 600, .backoff = 5550},
- {.pwr = 500, .backoff = 9350},
- {0},
-};
-
-static const struct iwl_ht_params iwl7265_ht_params = {
- .stbc = true,
- .ldpc = true,
- .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
-};
-
-const struct iwl_cfg iwl3165_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 3165",
- .fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7005D,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL3165_NVM_VERSION,
- .nvm_calib_ver = IWL3165_TX_POWER_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 7265",
- .fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7005,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265_NVM_VERSION,
- .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 7265",
- .fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7005,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265_NVM_VERSION,
- .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265_n_cfg = {
- .name = "Intel(R) Wireless N 7265",
- .fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7005,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265_NVM_VERSION,
- .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265d_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 7265",
- .fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7005D,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265D_NVM_VERSION,
- .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265d_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 7265",
- .fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7005D,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265D_NVM_VERSION,
- .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265d_n_cfg = {
- .name = "Intel(R) Wireless N 7265",
- .fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7005D,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265D_NVM_VERSION,
- .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
-MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
-MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7265_UCODE_API_OK));
-MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7265D_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c
deleted file mode 100644
index 9bcc0bf937d8..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-8000.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/stringify.h>
-#include "iwl-config.h"
-#include "iwl-agn-hw.h"
-
-/* Highest firmware API version supported */
-#define IWL8000_UCODE_API_MAX 19
-
-/* Oldest version we won't warn about */
-#define IWL8000_UCODE_API_OK 13
-
-/* Lowest firmware API version supported */
-#define IWL8000_UCODE_API_MIN 13
-
-/* NVM versions */
-#define IWL8000_NVM_VERSION 0x0a1d
-#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */
-
-/* Memory offsets and lengths */
-#define IWL8260_DCCM_OFFSET 0x800000
-#define IWL8260_DCCM_LEN 0x18000
-#define IWL8260_DCCM2_OFFSET 0x880000
-#define IWL8260_DCCM2_LEN 0x8000
-#define IWL8260_SMEM_OFFSET 0x400000
-#define IWL8260_SMEM_LEN 0x68000
-
-#define IWL8000_FW_PRE "iwlwifi-8000"
-#define IWL8000_MODULE_FIRMWARE(api) \
- IWL8000_FW_PRE "-" __stringify(api) ".ucode"
-
-#define NVM_HW_SECTION_NUM_FAMILY_8000 10
-#define DEFAULT_NVM_FILE_FAMILY_8000B "nvmData-8000B"
-#define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C"
-
-/* Max SDIO RX/TX aggregation sizes of the ADDBA request/response */
-#define MAX_RX_AGG_SIZE_8260_SDIO 21
-#define MAX_TX_AGG_SIZE_8260_SDIO 40
-
-/* Max A-MPDU exponent for HT and VHT */
-#define MAX_HT_AMPDU_EXPONENT_8260_SDIO IEEE80211_HT_MAX_AMPDU_32K
-#define MAX_VHT_AMPDU_EXPONENT_8260_SDIO IEEE80211_VHT_MAX_AMPDU_32K
-
-static const struct iwl_base_params iwl8000_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
- .num_of_queues = 31,
- .pll_cfg_val = 0,
- .shadow_ram_support = true,
- .led_compensation = 57,
- .wd_timeout = IWL_LONG_WD_TIMEOUT,
- .max_event_log_size = 512,
- .shadow_reg_enable = true,
- .pcie_l1_allowed = true,
-};
-
-static const struct iwl_ht_params iwl8000_ht_params = {
- .stbc = true,
- .ldpc = true,
- .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
-};
-
-static const struct iwl_tt_params iwl8000_tt_params = {
- .ct_kill_entry = 115,
- .ct_kill_exit = 93,
- .ct_kill_duration = 5,
- .dynamic_smps_entry = 111,
- .dynamic_smps_exit = 107,
- .tx_protection_entry = 112,
- .tx_protection_exit = 105,
- .tx_backoff = {
- {.temperature = 110, .backoff = 200},
- {.temperature = 111, .backoff = 600},
- {.temperature = 112, .backoff = 1200},
- {.temperature = 113, .backoff = 2000},
- {.temperature = 114, .backoff = 4000},
- },
- .support_ct_kill = true,
- .support_dynamic_smps = true,
- .support_tx_protection = true,
- .support_tx_backoff = true,
-};
-
-#define IWL_DEVICE_8000 \
- .ucode_api_max = IWL8000_UCODE_API_MAX, \
- .ucode_api_ok = IWL8000_UCODE_API_OK, \
- .ucode_api_min = IWL8000_UCODE_API_MIN, \
- .device_family = IWL_DEVICE_FAMILY_8000, \
- .max_inst_size = IWL60_RTC_INST_SIZE, \
- .max_data_size = IWL60_RTC_DATA_SIZE, \
- .base_params = &iwl8000_base_params, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \
- .d0i3 = true, \
- .features = NETIF_F_RXCSUM, \
- .non_shared_ant = ANT_A, \
- .dccm_offset = IWL8260_DCCM_OFFSET, \
- .dccm_len = IWL8260_DCCM_LEN, \
- .dccm2_offset = IWL8260_DCCM2_OFFSET, \
- .dccm2_len = IWL8260_DCCM2_LEN, \
- .smem_offset = IWL8260_SMEM_OFFSET, \
- .smem_len = IWL8260_SMEM_LEN, \
- .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B, \
- .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C, \
- .thermal_params = &iwl8000_tt_params, \
- .apmg_not_supported = true
-
-const struct iwl_cfg iwl8260_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 8260",
- .fw_name_pre = IWL8000_FW_PRE,
- IWL_DEVICE_8000,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
- .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
-};
-
-const struct iwl_cfg iwl8260_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 8260",
- .fw_name_pre = IWL8000_FW_PRE,
- IWL_DEVICE_8000,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
- .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
-};
-
-const struct iwl_cfg iwl4165_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 4165",
- .fw_name_pre = IWL8000_FW_PRE,
- IWL_DEVICE_8000,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
- .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
- .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
-};
-
-const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
- .name = "Intel(R) Dual Band Wireless-AC 8260",
- .fw_name_pre = IWL8000_FW_PRE,
- IWL_DEVICE_8000,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
- .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
- .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
- .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO,
- .disable_dummy_notification = true,
- .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
- .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
-};
-
-const struct iwl_cfg iwl4165_2ac_sdio_cfg = {
- .name = "Intel(R) Dual Band Wireless-AC 4165",
- .fw_name_pre = IWL8000_FW_PRE,
- IWL_DEVICE_8000,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
- .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
- .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
- .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO,
- .bt_shared_single_ant = true,
- .disable_dummy_notification = true,
- .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
- .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
-};
-
-MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
deleted file mode 100644
index 04a483d38659..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-/*
- * Please use this file (iwl-agn-hw.h) only for hardware-related definitions.
- */
-
-#ifndef __iwl_agn_hw_h__
-#define __iwl_agn_hw_h__
-
-#define IWLAGN_RTC_INST_LOWER_BOUND (0x000000)
-#define IWLAGN_RTC_INST_UPPER_BOUND (0x020000)
-
-#define IWLAGN_RTC_DATA_LOWER_BOUND (0x800000)
-#define IWLAGN_RTC_DATA_UPPER_BOUND (0x80C000)
-
-#define IWLAGN_RTC_INST_SIZE (IWLAGN_RTC_INST_UPPER_BOUND - \
- IWLAGN_RTC_INST_LOWER_BOUND)
-#define IWLAGN_RTC_DATA_SIZE (IWLAGN_RTC_DATA_UPPER_BOUND - \
- IWLAGN_RTC_DATA_LOWER_BOUND)
-
-#define IWL60_RTC_INST_LOWER_BOUND (0x000000)
-#define IWL60_RTC_INST_UPPER_BOUND (0x040000)
-#define IWL60_RTC_DATA_LOWER_BOUND (0x800000)
-#define IWL60_RTC_DATA_UPPER_BOUND (0x814000)
-#define IWL60_RTC_INST_SIZE \
- (IWL60_RTC_INST_UPPER_BOUND - IWL60_RTC_INST_LOWER_BOUND)
-#define IWL60_RTC_DATA_SIZE \
- (IWL60_RTC_DATA_UPPER_BOUND - IWL60_RTC_DATA_LOWER_BOUND)
-
-/* RSSI to dBm */
-#define IWLAGN_RSSI_OFFSET 44
-
-#define IWLAGN_DEFAULT_TX_RETRY 15
-#define IWLAGN_MGMT_DFAULT_RETRY_LIMIT 3
-#define IWLAGN_RTS_DFAULT_RETRY_LIMIT 60
-#define IWLAGN_BAR_DFAULT_RETRY_LIMIT 60
-#define IWLAGN_LOW_RETRY_LIMIT 7
-
-/* Limit range of txpower output target to be between these values */
-#define IWLAGN_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */
-#define IWLAGN_TX_POWER_TARGET_POWER_MAX (16) /* 16 dBm */
-
-/* EEPROM */
-#define IWLAGN_EEPROM_IMG_SIZE 2048
-
-/* high blocks contain PAPD data */
-#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */
-#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */
-#define OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */
-#define OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */
-#define OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */
-#define OTP_MAX_LL_ITEMS_2x00 (4) /* OTP blocks for 2x00 */
-
-
-#define IWLAGN_NUM_QUEUES 20
-
-#endif /* __iwl_agn_hw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
deleted file mode 100644
index 910970858f98..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ /dev/null
@@ -1,437 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __IWL_CONFIG_H__
-#define __IWL_CONFIG_H__
-
-#include <linux/types.h>
-#include <net/mac80211.h>
-
-
-enum iwl_device_family {
- IWL_DEVICE_FAMILY_UNDEFINED,
- IWL_DEVICE_FAMILY_1000,
- IWL_DEVICE_FAMILY_100,
- IWL_DEVICE_FAMILY_2000,
- IWL_DEVICE_FAMILY_2030,
- IWL_DEVICE_FAMILY_105,
- IWL_DEVICE_FAMILY_135,
- IWL_DEVICE_FAMILY_5000,
- IWL_DEVICE_FAMILY_5150,
- IWL_DEVICE_FAMILY_6000,
- IWL_DEVICE_FAMILY_6000i,
- IWL_DEVICE_FAMILY_6005,
- IWL_DEVICE_FAMILY_6030,
- IWL_DEVICE_FAMILY_6050,
- IWL_DEVICE_FAMILY_6150,
- IWL_DEVICE_FAMILY_7000,
- IWL_DEVICE_FAMILY_8000,
-};
-
-static inline bool iwl_has_secure_boot(u32 hw_rev,
- enum iwl_device_family family)
-{
- /* return 1 only for family 8000 B0 */
- if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC))
- return true;
-
- return false;
-}
-
-/*
- * LED mode
- * IWL_LED_DEFAULT: use device default
- * IWL_LED_RF_STATE: turn LED on/off based on RF state
- * LED ON = RF ON
- * LED OFF = RF OFF
- * IWL_LED_BLINK: adjust led blink rate based on blink table
- * IWL_LED_DISABLE: led disabled
- */
-enum iwl_led_mode {
- IWL_LED_DEFAULT,
- IWL_LED_RF_STATE,
- IWL_LED_BLINK,
- IWL_LED_DISABLE,
-};
-
-/*
- * This is the threshold value of plcp error rate per 100mSecs. It is
- * used to set and check for the validity of plcp_delta.
- */
-#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN 1
-#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF 50
-#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF 100
-#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF 200
-#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX 255
-#define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE 0
-
-/* TX queue watchdog timeouts in mSecs */
-#define IWL_WATCHDOG_DISABLED 0
-#define IWL_DEF_WD_TIMEOUT 2500
-#define IWL_LONG_WD_TIMEOUT 10000
-#define IWL_MAX_WD_TIMEOUT 120000
-
-#define IWL_DEFAULT_MAX_TX_POWER 22
-
-/* Antenna presence definitions */
-#define ANT_NONE 0x0
-#define ANT_A BIT(0)
-#define ANT_B BIT(1)
-#define ANT_C BIT(2)
-#define ANT_AB (ANT_A | ANT_B)
-#define ANT_AC (ANT_A | ANT_C)
-#define ANT_BC (ANT_B | ANT_C)
-#define ANT_ABC (ANT_A | ANT_B | ANT_C)
-
-static inline u8 num_of_ant(u8 mask)
-{
- return !!((mask) & ANT_A) +
- !!((mask) & ANT_B) +
- !!((mask) & ANT_C);
-}
-
-/*
- * @max_ll_items: max number of OTP blocks
- * @shadow_ram_support: shadow support for OTP memory
- * @led_compensation: compensate on the led on/off time per HW according
- * to the deviation to achieve the desired led frequency.
- * The detail algorithm is described in iwl-led.c
- * @wd_timeout: TX queues watchdog timeout
- * @max_event_log_size: size of event log buffer size for ucode event logging
- * @shadow_reg_enable: HW shadow register support
- * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command
- * is in flight. This is due to a HW bug in 7260, 3160 and 7265.
- * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled.
- */
-struct iwl_base_params {
- int eeprom_size;
- int num_of_queues; /* def: HW dependent */
- /* for iwl_pcie_apm_init() */
- u32 pll_cfg_val;
-
- const u16 max_ll_items;
- const bool shadow_ram_support;
- u16 led_compensation;
- unsigned int wd_timeout;
- u32 max_event_log_size;
- const bool shadow_reg_enable;
- const bool pcie_l1_allowed;
- const bool apmg_wake_up_wa;
- const bool scd_chain_ext_wa;
-};
-
-/*
- * @stbc: support Tx STBC and 1*SS Rx STBC
- * @ldpc: support Tx/Rx with LDPC
- * @use_rts_for_aggregation: use rts/cts protection for HT traffic
- * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40
- */
-struct iwl_ht_params {
- enum ieee80211_smps_mode smps_mode;
- const bool ht_greenfield_support; /* if used set to true */
- const bool stbc;
- const bool ldpc;
- bool use_rts_for_aggregation;
- u8 ht40_bands;
-};
-
-/*
- * Tx-backoff threshold
- * @temperature: The threshold in Celsius
- * @backoff: The tx-backoff in uSec
- */
-struct iwl_tt_tx_backoff {
- s32 temperature;
- u32 backoff;
-};
-
-#define TT_TX_BACKOFF_SIZE 6
-
-/**
- * struct iwl_tt_params - thermal throttling parameters
- * @ct_kill_entry: CT Kill entry threshold
- * @ct_kill_exit: CT Kill exit threshold
- * @ct_kill_duration: The time intervals (in uSec) in which the driver needs
- * to checks whether to exit CT Kill.
- * @dynamic_smps_entry: Dynamic SMPS entry threshold
- * @dynamic_smps_exit: Dynamic SMPS exit threshold
- * @tx_protection_entry: TX protection entry threshold
- * @tx_protection_exit: TX protection exit threshold
- * @tx_backoff: Array of thresholds for tx-backoff , in ascending order.
- * @support_ct_kill: Support CT Kill?
- * @support_dynamic_smps: Support dynamic SMPS?
- * @support_tx_protection: Support tx protection?
- * @support_tx_backoff: Support tx-backoff?
- */
-struct iwl_tt_params {
- u32 ct_kill_entry;
- u32 ct_kill_exit;
- u32 ct_kill_duration;
- u32 dynamic_smps_entry;
- u32 dynamic_smps_exit;
- u32 tx_protection_entry;
- u32 tx_protection_exit;
- struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE];
- bool support_ct_kill;
- bool support_dynamic_smps;
- bool support_tx_protection;
- bool support_tx_backoff;
-};
-
-/*
- * information on how to parse the EEPROM
- */
-#define EEPROM_REG_BAND_1_CHANNELS 0x08
-#define EEPROM_REG_BAND_2_CHANNELS 0x26
-#define EEPROM_REG_BAND_3_CHANNELS 0x42
-#define EEPROM_REG_BAND_4_CHANNELS 0x5C
-#define EEPROM_REG_BAND_5_CHANNELS 0x74
-#define EEPROM_REG_BAND_24_HT40_CHANNELS 0x82
-#define EEPROM_REG_BAND_52_HT40_CHANNELS 0x92
-#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80
-#define EEPROM_REGULATORY_BAND_NO_HT40 0
-
-/* lower blocks contain EEPROM image and calibration data */
-#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
-#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */
-#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
-
-struct iwl_eeprom_params {
- const u8 regulatory_bands[7];
- bool enhanced_txpower;
-};
-
-/* Tx-backoff power threshold
- * @pwr: The power limit in mw
- * @backoff: The tx-backoff in uSec
- */
-struct iwl_pwr_tx_backoff {
- u32 pwr;
- u32 backoff;
-};
-
-/**
- * struct iwl_cfg
- * @name: Official name of the device
- * @fw_name_pre: Firmware filename prefix. The api version and extension
- * (.ucode) will be added to filename before loading from disk. The
- * filename is constructed as fw_name_pre<api>.ucode.
- * @ucode_api_max: Highest version of uCode API supported by driver.
- * @ucode_api_ok: oldest version of the uCode API that is OK to load
- * without a warning, for use in transitions
- * @ucode_api_min: Lowest version of uCode API supported by driver.
- * @max_inst_size: The maximal length of the fw inst section
- * @max_data_size: The maximal length of the fw data section
- * @valid_tx_ant: valid transmit antenna
- * @valid_rx_ant: valid receive antenna
- * @non_shared_ant: the antenna that is for WiFi only
- * @nvm_ver: NVM version
- * @nvm_calib_ver: NVM calibration version
- * @lib: pointer to the lib ops
- * @base_params: pointer to basic parameters
- * @ht_params: point to ht parameters
- * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
- * @rx_with_siso_diversity: 1x1 device with rx antenna diversity
- * @internal_wimax_coex: internal wifi/wimax combo device
- * @high_temp: Is this NIC is designated to be in high temperature.
- * @host_interrupt_operation_mode: device needs host interrupt operation
- * mode set
- * @d0i3: device uses d0i3 instead of d3
- * @nvm_hw_section_num: the ID of the HW NVM section
- * @features: hw features, any combination of feature_whitelist
- * @pwr_tx_backoffs: translation table between power limits and backoffs
- * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
- * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response
- * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the
- * station can receive in HT
- * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the
- * station can receive in VHT
- * @dccm_offset: offset from which DCCM begins
- * @dccm_len: length of DCCM (including runtime stack CCM)
- * @dccm2_offset: offset from which the second DCCM begins
- * @dccm2_len: length of the second DCCM
- * @smem_offset: offset from which the SMEM begins
- * @smem_len: the length of SMEM
- *
- * We enable the driver to be backward compatible wrt. hardware features.
- * API differences in uCode shouldn't be handled here but through TLVs
- * and/or the uCode API version instead.
- */
-struct iwl_cfg {
- /* params specific to an individual device within a device family */
- const char *name;
- const char *fw_name_pre;
- const unsigned int ucode_api_max;
- const unsigned int ucode_api_ok;
- const unsigned int ucode_api_min;
- const enum iwl_device_family device_family;
- const u32 max_data_size;
- const u32 max_inst_size;
- u8 valid_tx_ant;
- u8 valid_rx_ant;
- u8 non_shared_ant;
- bool bt_shared_single_ant;
- u16 nvm_ver;
- u16 nvm_calib_ver;
- /* params not likely to change within a device family */
- const struct iwl_base_params *base_params;
- /* params likely to change within a device family */
- const struct iwl_ht_params *ht_params;
- const struct iwl_eeprom_params *eeprom_params;
- enum iwl_led_mode led_mode;
- const bool rx_with_siso_diversity;
- const bool internal_wimax_coex;
- const bool host_interrupt_operation_mode;
- bool high_temp;
- bool d0i3;
- u8 nvm_hw_section_num;
- bool lp_xtal_workaround;
- const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
- bool no_power_up_nic_in_init;
- const char *default_nvm_file_B_step;
- const char *default_nvm_file_C_step;
- netdev_features_t features;
- unsigned int max_rx_agg_size;
- bool disable_dummy_notification;
- unsigned int max_tx_agg_size;
- unsigned int max_ht_ampdu_exponent;
- unsigned int max_vht_ampdu_exponent;
- const u32 dccm_offset;
- const u32 dccm_len;
- const u32 dccm2_offset;
- const u32 dccm2_len;
- const u32 smem_offset;
- const u32 smem_len;
- const struct iwl_tt_params *thermal_params;
- bool apmg_not_supported;
-};
-
-/*
- * 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;
-extern const struct iwl_cfg iwl5100_bgn_cfg;
-extern const struct iwl_cfg iwl5100_abg_cfg;
-extern const struct iwl_cfg iwl5150_agn_cfg;
-extern const struct iwl_cfg iwl5150_abg_cfg;
-extern const struct iwl_cfg iwl6005_2agn_cfg;
-extern const struct iwl_cfg iwl6005_2abg_cfg;
-extern const struct iwl_cfg iwl6005_2bg_cfg;
-extern const struct iwl_cfg iwl6005_2agn_sff_cfg;
-extern const struct iwl_cfg iwl6005_2agn_d_cfg;
-extern const struct iwl_cfg iwl6005_2agn_mow1_cfg;
-extern const struct iwl_cfg iwl6005_2agn_mow2_cfg;
-extern const struct iwl_cfg iwl1030_bgn_cfg;
-extern const struct iwl_cfg iwl1030_bg_cfg;
-extern const struct iwl_cfg iwl6030_2agn_cfg;
-extern const struct iwl_cfg iwl6030_2abg_cfg;
-extern const struct iwl_cfg iwl6030_2bgn_cfg;
-extern const struct iwl_cfg iwl6030_2bg_cfg;
-extern const struct iwl_cfg iwl6000i_2agn_cfg;
-extern const struct iwl_cfg iwl6000i_2abg_cfg;
-extern const struct iwl_cfg iwl6000i_2bg_cfg;
-extern const struct iwl_cfg iwl6000_3agn_cfg;
-extern const struct iwl_cfg iwl6050_2agn_cfg;
-extern const struct iwl_cfg iwl6050_2abg_cfg;
-extern const struct iwl_cfg iwl6150_bgn_cfg;
-extern const struct iwl_cfg iwl6150_bg_cfg;
-extern const struct iwl_cfg iwl1000_bgn_cfg;
-extern const struct iwl_cfg iwl1000_bg_cfg;
-extern const struct iwl_cfg iwl100_bgn_cfg;
-extern const struct iwl_cfg iwl100_bg_cfg;
-extern const struct iwl_cfg iwl130_bgn_cfg;
-extern const struct iwl_cfg iwl130_bg_cfg;
-extern const struct iwl_cfg iwl2000_2bgn_cfg;
-extern const struct iwl_cfg iwl2000_2bgn_d_cfg;
-extern const struct iwl_cfg iwl2030_2bgn_cfg;
-extern const struct iwl_cfg iwl6035_2agn_cfg;
-extern const struct iwl_cfg iwl6035_2agn_sff_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_2ac_cfg_high_temp;
-extern const struct iwl_cfg iwl7260_2n_cfg;
-extern const struct iwl_cfg iwl7260_n_cfg;
-extern const struct iwl_cfg iwl3160_2ac_cfg;
-extern const struct iwl_cfg iwl3160_2n_cfg;
-extern const struct iwl_cfg iwl3160_n_cfg;
-extern const struct iwl_cfg iwl3165_2ac_cfg;
-extern const struct iwl_cfg iwl7265_2ac_cfg;
-extern const struct iwl_cfg iwl7265_2n_cfg;
-extern const struct iwl_cfg iwl7265_n_cfg;
-extern const struct iwl_cfg iwl7265d_2ac_cfg;
-extern const struct iwl_cfg iwl7265d_2n_cfg;
-extern const struct iwl_cfg iwl7265d_n_cfg;
-extern const struct iwl_cfg iwl8260_2n_cfg;
-extern const struct iwl_cfg iwl8260_2ac_cfg;
-extern const struct iwl_cfg iwl4165_2ac_cfg;
-extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
-extern const struct iwl_cfg iwl4165_2ac_sdio_cfg;
-#endif /* CONFIG_IWLMVM */
-
-#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
deleted file mode 100644
index 543abeaffcf0..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ /dev/null
@@ -1,552 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_csr_h__
-#define __iwl_csr_h__
-/*
- * CSR (control and status registers)
- *
- * CSR registers are mapped directly into PCI bus space, and are accessible
- * whenever platform supplies power to device, even when device is in
- * low power states due to driver-invoked device resets
- * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes.
- *
- * Use iwl_write32() and iwl_read32() family to access these registers;
- * these provide simple PCI bus access, without waking up the MAC.
- * Do not use iwl_write_direct32() family for these registers;
- * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ.
- * The MAC (uCode processor, etc.) does not need to be powered up for accessing
- * the CSR registers.
- *
- * NOTE: Device does need to be awake in order to read this memory
- * via CSR_EEPROM and CSR_OTP registers
- */
-#define CSR_BASE (0x000)
-
-#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */
-#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
-#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */
-#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */
-#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/
-#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */
-#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
-#define CSR_GP_CNTRL (CSR_BASE+0x024)
-
-/* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */
-#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005)
-
-/*
- * Hardware revision info
- * Bit fields:
- * 31-16: Reserved
- * 15-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions
- * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D
- * 1-0: "Dash" (-) value, as in A-1, etc.
- */
-#define CSR_HW_REV (CSR_BASE+0x028)
-
-/*
- * EEPROM and OTP (one-time-programmable) memory reads
- *
- * NOTE: Device must be awake, initialized via apm_ops.init(),
- * in order to read.
- */
-#define CSR_EEPROM_REG (CSR_BASE+0x02c)
-#define CSR_EEPROM_GP (CSR_BASE+0x030)
-#define CSR_OTP_GP_REG (CSR_BASE+0x034)
-
-#define CSR_GIO_REG (CSR_BASE+0x03C)
-#define CSR_GP_UCODE_REG (CSR_BASE+0x048)
-#define CSR_GP_DRIVER_REG (CSR_BASE+0x050)
-
-/*
- * UCODE-DRIVER GP (general purpose) mailbox registers.
- * SET/CLR registers set/clear bit(s) if "1" is written.
- */
-#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
-#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058)
-#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
-#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
-
-#define CSR_MBOX_SET_REG (CSR_BASE + 0x88)
-
-#define CSR_LED_REG (CSR_BASE+0x094)
-#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0)
-#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */
-
-
-/* GIO Chicken Bits (PCI Express bus link power management) */
-#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
-
-/* Analog phase-lock-loop configuration */
-#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
-
-/*
- * CSR HW resources monitor registers
- */
-#define CSR_MONITOR_CFG_REG (CSR_BASE+0x214)
-#define CSR_MONITOR_STATUS_REG (CSR_BASE+0x228)
-#define CSR_MONITOR_XTAL_RESOURCES (0x00000010)
-
-/*
- * CSR Hardware Revision Workaround Register. Indicates hardware rev;
- * "step" determines CCK backoff for txpower calculation. Used for 4965 only.
- * See also CSR_HW_REV register.
- * Bit fields:
- * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step
- * 1-0: "Dash" (-) value, as in C-1, etc.
- */
-#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C)
-
-#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240)
-#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250)
-
-/* Bits for CSR_HW_IF_CONFIG_REG */
-#define CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH (0x00000003)
-#define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP (0x0000000C)
-#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0)
-#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100)
-#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200)
-#define CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00)
-#define CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000)
-#define CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000)
-
-#define CSR_HW_IF_CONFIG_REG_POS_MAC_DASH (0)
-#define CSR_HW_IF_CONFIG_REG_POS_MAC_STEP (2)
-#define CSR_HW_IF_CONFIG_REG_POS_BOARD_VER (6)
-#define CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE (10)
-#define CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12)
-#define CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14)
-
-#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000)
-#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
-#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */
-#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */
-#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */
-#define CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000)
-#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */
-
-#define CSR_MBOX_SET_REG_OS_ALIVE BIT(5)
-
-#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/
-#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/
-
-/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
- * acknowledged (reset) by host writing "1" to flagged bits. */
-#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
-#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
-#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */
-#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
-#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
-#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
-#define CSR_INT_BIT_PAGING (1 << 24) /* SDIO PAGING */
-#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
-#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
-#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */
-#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */
-#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */
-
-#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \
- CSR_INT_BIT_HW_ERR | \
- CSR_INT_BIT_FH_TX | \
- CSR_INT_BIT_SW_ERR | \
- CSR_INT_BIT_PAGING | \
- CSR_INT_BIT_RF_KILL | \
- CSR_INT_BIT_SW_RX | \
- CSR_INT_BIT_WAKEUP | \
- CSR_INT_BIT_ALIVE | \
- CSR_INT_BIT_RX_PERIODIC)
-
-/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
-#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */
-#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */
-#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */
-#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */
-#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */
-#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */
-
-#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \
- CSR_FH_INT_BIT_RX_CHNL1 | \
- CSR_FH_INT_BIT_RX_CHNL0)
-
-#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \
- CSR_FH_INT_BIT_TX_CHNL0)
-
-/* GPIO */
-#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200)
-#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000)
-#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200)
-
-/* RESET */
-#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001)
-#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002)
-#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080)
-#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100)
-#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200)
-#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000)
-
-/*
- * GP (general purpose) CONTROL REGISTER
- * Bit fields:
- * 27: HW_RF_KILL_SW
- * Indicates state of (platform's) hardware RF-Kill switch
- * 26-24: POWER_SAVE_TYPE
- * Indicates current power-saving mode:
- * 000 -- No power saving
- * 001 -- MAC power-down
- * 010 -- PHY (radio) power-down
- * 011 -- Error
- * 10: XTAL ON request
- * 9-6: SYS_CONFIG
- * Indicates current system configuration, reflecting pins on chip
- * as forced high/low by device circuit board.
- * 4: GOING_TO_SLEEP
- * Indicates MAC is entering a power-saving sleep power-down.
- * Not a good time to access device-internal resources.
- * 3: MAC_ACCESS_REQ
- * Host sets this to request and maintain MAC wakeup, to allow host
- * access to device-internal resources. Host must wait for
- * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR
- * device registers.
- * 2: INIT_DONE
- * Host sets this to put device into fully operational D0 power mode.
- * Host resets this after SW_RESET to put device into low power mode.
- * 0: MAC_CLOCK_READY
- * Indicates MAC (ucode processor, etc.) is powered up and can run.
- * Internal resources are accessible.
- * NOTE: This does not indicate that the processor is actually running.
- * NOTE: This does not indicate that device has completed
- * init or post-power-down restore of internal SRAM memory.
- * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that
- * SRAM is restored and uCode is in normal operation mode.
- * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and
- * do not need to save/restore it.
- * NOTE: After device reset, this bit remains "0" until host sets
- * INIT_DONE
- */
-#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001)
-#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
-#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
-#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010)
-#define CSR_GP_CNTRL_REG_FLAG_XTAL_ON (0x00000400)
-
-#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001)
-
-#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000)
-#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000)
-#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000)
-
-
-/* HW REV */
-#define CSR_HW_REV_DASH(_val) (((_val) & 0x0000003) >> 0)
-#define CSR_HW_REV_STEP(_val) (((_val) & 0x000000C) >> 2)
-
-
-/**
- * hw_rev values
- */
-enum {
- SILICON_A_STEP = 0,
- SILICON_B_STEP,
- SILICON_C_STEP,
-};
-
-
-#define CSR_HW_REV_TYPE_MSK (0x000FFF0)
-#define CSR_HW_REV_TYPE_5300 (0x0000020)
-#define CSR_HW_REV_TYPE_5350 (0x0000030)
-#define CSR_HW_REV_TYPE_5100 (0x0000050)
-#define CSR_HW_REV_TYPE_5150 (0x0000040)
-#define CSR_HW_REV_TYPE_1000 (0x0000060)
-#define CSR_HW_REV_TYPE_6x00 (0x0000070)
-#define CSR_HW_REV_TYPE_6x50 (0x0000080)
-#define CSR_HW_REV_TYPE_6150 (0x0000084)
-#define CSR_HW_REV_TYPE_6x05 (0x00000B0)
-#define CSR_HW_REV_TYPE_6x30 CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_2x30 (0x00000C0)
-#define CSR_HW_REV_TYPE_2x00 (0x0000100)
-#define CSR_HW_REV_TYPE_105 (0x0000110)
-#define CSR_HW_REV_TYPE_135 (0x0000120)
-#define CSR_HW_REV_TYPE_7265D (0x0000210)
-#define CSR_HW_REV_TYPE_NONE (0x00001F0)
-
-/* EEPROM REG */
-#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001)
-#define CSR_EEPROM_REG_BIT_CMD (0x00000002)
-#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC)
-#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000)
-
-/* EEPROM GP */
-#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */
-#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180)
-#define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000)
-#define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001)
-#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002)
-#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004)
-
-/* One-time-programmable memory general purpose reg */
-#define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */
-#define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */
-#define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */
-#define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */
-
-/* GP REG */
-#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */
-#define CSR_GP_REG_NO_POWER_SAVE (0x00000000)
-#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000)
-#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000)
-#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000)
-
-
-/* CSR GIO */
-#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002)
-
-/*
- * UCODE-DRIVER GP (general purpose) mailbox register 1
- * Host driver and uCode write and/or read this register to communicate with
- * each other.
- * Bit fields:
- * 4: UCODE_DISABLE
- * Host sets this to request permanent halt of uCode, same as
- * sending CARD_STATE command with "halt" bit set.
- * 3: CT_KILL_EXIT
- * Host sets this to request exit from CT_KILL state, i.e. host thinks
- * device temperature is low enough to continue normal operation.
- * 2: CMD_BLOCKED
- * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL)
- * to release uCode to clear all Tx and command queues, enter
- * unassociated mode, and power down.
- * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit.
- * 1: SW_BIT_RFKILL
- * Host sets this when issuing CARD_STATE command to request
- * device sleep.
- * 0: MAC_SLEEP
- * uCode sets this when preparing a power-saving power-down.
- * uCode resets this when power-up is complete and SRAM is sane.
- * NOTE: device saves internal SRAM data to host when powering down,
- * and must restore this data after powering back up.
- * MAC_SLEEP is the best indication that restore is complete.
- * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and
- * do not need to save/restore it.
- */
-#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001)
-#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
-#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
-#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
-#define CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020)
-
-/* GP Driver */
-#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003)
-#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000)
-#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001)
-#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002)
-#define CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6 (0x00000004)
-#define CSR_GP_DRIVER_REG_BIT_6050_1x2 (0x00000008)
-
-#define CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER (0x00000080)
-
-/* GIO Chicken Bits (PCI Express bus link power management) */
-#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000)
-#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000)
-
-/* LED */
-#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF)
-#define CSR_LED_REG_TURN_ON (0x60)
-#define CSR_LED_REG_TURN_OFF (0x20)
-
-/* ANA_PLL */
-#define CSR50_ANA_PLL_CFG_VAL (0x00880300)
-
-/* HPET MEM debug */
-#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000)
-
-/* DRAM INT TABLE */
-#define CSR_DRAM_INT_TBL_ENABLE (1 << 31)
-#define CSR_DRAM_INIT_TBL_WRITE_POINTER (1 << 28)
-#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27)
-
-/*
- * SHR target access (Shared block memory space)
- *
- * Shared internal registers can be accessed directly from PCI bus through SHR
- * arbiter without need for the MAC HW to be powered up. This is possible due to
- * indirect read/write via HEEP_CTRL_WRD_PCIEX_CTRL (0xEC) and
- * HEEP_CTRL_WRD_PCIEX_DATA (0xF4) registers.
- *
- * Use iwl_write32()/iwl_read32() family to access these registers. The MAC HW
- * need not be powered up so no "grab inc access" is required.
- */
-
-/*
- * Registers for accessing shared registers (e.g. SHR_APMG_GP1,
- * SHR_APMG_XTAL_CFG). For example, to read from SHR_APMG_GP1 register (0x1DC),
- * first, write to the control register:
- * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register)
- * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 2 (read access)
- * second, read from the data register HEEP_CTRL_WRD_PCIEX_DATA[31:0].
- *
- * To write the register, first, write to the data register
- * HEEP_CTRL_WRD_PCIEX_DATA[31:0] and then:
- * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register)
- * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 3 (write access)
- */
-#define HEEP_CTRL_WRD_PCIEX_CTRL_REG (CSR_BASE+0x0ec)
-#define HEEP_CTRL_WRD_PCIEX_DATA_REG (CSR_BASE+0x0f4)
-
-/*
- * HBUS (Host-side Bus)
- *
- * HBUS registers are mapped directly into PCI bus space, but are used
- * to indirectly access device's internal memory or registers that
- * may be powered-down.
- *
- * Use iwl_write_direct32()/iwl_read_direct32() family for these registers;
- * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ
- * to make sure the MAC (uCode processor, etc.) is powered up for accessing
- * internal resources.
- *
- * Do not use iwl_write32()/iwl_read32() family to access these registers;
- * these provide only simple PCI bus access, without waking up the MAC.
- */
-#define HBUS_BASE (0x400)
-
-/*
- * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
- * structures, error log, event log, verifying uCode load).
- * First write to address register, then read from or write to data register
- * to complete the job. Once the address register is set up, accesses to
- * data registers auto-increment the address by one dword.
- * Bit usage for address registers (read or write):
- * 0-31: memory address within device
- */
-#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c)
-#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010)
-#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018)
-#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c)
-
-/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */
-#define HBUS_TARG_MBX_C (HBUS_BASE+0x030)
-#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)
-
-/*
- * Registers for accessing device's internal peripheral registers
- * (e.g. SCD, BSM, etc.). First write to address register,
- * then read from or write to data register to complete the job.
- * Bit usage for address registers (read or write):
- * 0-15: register address (offset) within device
- * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword)
- */
-#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044)
-#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048)
-#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c)
-#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050)
-
-/* Used to enable DBGM */
-#define HBUS_TARG_TEST_REG (HBUS_BASE+0x05c)
-
-/*
- * Per-Tx-queue write pointer (index, really!)
- * Indicates index to next TFD that driver will fill (1 past latest filled).
- * Bit usage:
- * 0-7: queue write index
- * 11-8: queue selector
- */
-#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
-
-/**********************************************************
- * CSR values
- **********************************************************/
- /*
- * host interrupt timeout value
- * used with setting interrupt coalescing timer
- * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit
- *
- * default interrupt coalescing timer is 64 x 32 = 2048 usecs
- */
-#define IWL_HOST_INT_TIMEOUT_MAX (0xFF)
-#define IWL_HOST_INT_TIMEOUT_DEF (0x40)
-#define IWL_HOST_INT_TIMEOUT_MIN (0x0)
-#define IWL_HOST_INT_OPER_MODE BIT(31)
-
-/*****************************************************************************
- * 7000/3000 series SHR DTS addresses *
- *****************************************************************************/
-
-/* Diode Results Register Structure: */
-enum dtd_diode_reg {
- DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */
- DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */
- DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */
- DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */
- DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */
- DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */
-/* Those are the masks INSIDE the flags bit-field: */
- DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0,
- DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */
- DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7,
- DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */
-};
-
-#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/iwlwifi/iwl-debug.c
deleted file mode 100644
index 09feff4fa226..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-debug.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/export.h>
-#include "iwl-drv.h"
-#include "iwl-debug.h"
-#include "iwl-devtrace.h"
-
-#define __iwl_fn(fn) \
-void __iwl_ ##fn(struct device *dev, const char *fmt, ...) \
-{ \
- struct va_format vaf = { \
- .fmt = fmt, \
- }; \
- va_list args; \
- \
- va_start(args, fmt); \
- vaf.va = &args; \
- dev_ ##fn(dev, "%pV", &vaf); \
- trace_iwlwifi_ ##fn(&vaf); \
- va_end(args); \
-}
-
-__iwl_fn(warn)
-IWL_EXPORT_SYMBOL(__iwl_warn);
-__iwl_fn(info)
-IWL_EXPORT_SYMBOL(__iwl_info);
-__iwl_fn(crit)
-IWL_EXPORT_SYMBOL(__iwl_crit);
-
-void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only,
- const char *fmt, ...)
-{
- struct va_format vaf = {
- .fmt = fmt,
- };
- va_list args;
-
- va_start(args, fmt);
- vaf.va = &args;
- if (!trace_only) {
- if (rfkill_prefix)
- dev_err(dev, "(RFKILL) %pV", &vaf);
- else
- dev_err(dev, "%pV", &vaf);
- }
- trace_iwlwifi_err(&vaf);
- va_end(args);
-}
-IWL_EXPORT_SYMBOL(__iwl_err);
-
-#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING)
-void __iwl_dbg(struct device *dev,
- u32 level, bool limit, const char *function,
- const char *fmt, ...)
-{
- struct va_format vaf = {
- .fmt = fmt,
- };
- va_list args;
-
- va_start(args, fmt);
- vaf.va = &args;
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (iwl_have_debug_level(level) &&
- (!limit || net_ratelimit()))
- dev_printk(KERN_DEBUG, dev, "%c %s %pV",
- in_interrupt() ? 'I' : 'U', function, &vaf);
-#endif
- trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf);
- va_end(args);
-}
-IWL_EXPORT_SYMBOL(__iwl_dbg);
-#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
deleted file mode 100644
index 9bb36d79c2bd..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ /dev/null
@@ -1,225 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#ifndef __iwl_debug_h__
-#define __iwl_debug_h__
-
-#include "iwl-modparams.h"
-
-
-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,
- const char *fmt, ...) __printf(4, 5);
-void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3);
-void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3);
-void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3);
-
-/* not all compilers can evaluate strlen() at compile time, so use sizeof() */
-#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n')
-
-/* No matter what is m (priv, bus, trans), this will work */
-#define IWL_ERR_DEV(d, f, a...) \
- do { \
- CHECK_FOR_NEWLINE(f); \
- __iwl_err((d), false, false, f, ## a); \
- } while (0)
-#define IWL_ERR(m, f, a...) \
- IWL_ERR_DEV((m)->dev, f, ## a)
-#define IWL_WARN(m, f, a...) \
- do { \
- CHECK_FOR_NEWLINE(f); \
- __iwl_warn((m)->dev, f, ## a); \
- } while (0)
-#define IWL_INFO(m, f, a...) \
- do { \
- CHECK_FOR_NEWLINE(f); \
- __iwl_info((m)->dev, f, ## a); \
- } while (0)
-#define IWL_CRIT(m, f, a...) \
- do { \
- CHECK_FOR_NEWLINE(f); \
- __iwl_crit((m)->dev, f, ## a); \
- } while (0)
-
-#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING)
-void __iwl_dbg(struct device *dev,
- u32 level, bool limit, const char *function,
- const char *fmt, ...) __printf(5, 6);
-#else
-__printf(5, 6) static inline void
-__iwl_dbg(struct device *dev,
- u32 level, bool limit, const char *function,
- const char *fmt, ...)
-{}
-#endif
-
-#define iwl_print_hex_error(m, p, len) \
-do { \
- print_hex_dump(KERN_ERR, "iwl data: ", \
- DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \
-} while (0)
-
-#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...) \
- do { \
- CHECK_FOR_NEWLINE(fmt); \
- __iwl_dbg(dev, level, limit, __func__, fmt, ##args); \
- } while (0)
-#define IWL_DEBUG(m, level, fmt, args...) \
- __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args)
-#define IWL_DEBUG_DEV(dev, level, fmt, args...) \
- __IWL_DEBUG_DEV(dev, level, false, fmt, ##args)
-#define IWL_DEBUG_LIMIT(m, level, fmt, args...) \
- __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args)
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-#define iwl_print_hex_dump(m, level, p, len) \
-do { \
- if (iwl_have_debug_level(level)) \
- print_hex_dump(KERN_DEBUG, "iwl data: ", \
- DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \
-} while (0)
-#else
-#define iwl_print_hex_dump(m, level, p, len)
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-/*
- * To use the debug system:
- *
- * If you are defining a new debug classification, simply add it to the #define
- * list here in the form of
- *
- * #define IWL_DL_xxxx VALUE
- *
- * where xxxx should be the name of the classification (for example, WEP).
- *
- * You then need to either add a IWL_xxxx_DEBUG() macro definition for your
- * classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want
- * to send output to that classification.
- *
- * The active debug levels can be accessed via files
- *
- * /sys/module/iwlwifi/parameters/debug
- * when CONFIG_IWLWIFI_DEBUG=y.
- *
- * /sys/kernel/debug/phy0/iwlwifi/debug/debug_level
- * when CONFIG_IWLWIFI_DEBUGFS=y.
- *
- */
-
-/* 0x0000000F - 0x00000001 */
-#define IWL_DL_INFO 0x00000001
-#define IWL_DL_MAC80211 0x00000002
-#define IWL_DL_HCMD 0x00000004
-#define IWL_DL_TDLS 0x00000008
-/* 0x000000F0 - 0x00000010 */
-#define IWL_DL_QUOTA 0x00000010
-#define IWL_DL_TE 0x00000020
-#define IWL_DL_EEPROM 0x00000040
-#define IWL_DL_RADIO 0x00000080
-/* 0x00000F00 - 0x00000100 */
-#define IWL_DL_POWER 0x00000100
-#define IWL_DL_TEMP 0x00000200
-#define IWL_DL_RPM 0x00000400
-#define IWL_DL_SCAN 0x00000800
-/* 0x0000F000 - 0x00001000 */
-#define IWL_DL_ASSOC 0x00001000
-#define IWL_DL_DROP 0x00002000
-#define IWL_DL_LAR 0x00004000
-#define IWL_DL_COEX 0x00008000
-/* 0x000F0000 - 0x00010000 */
-#define IWL_DL_FW 0x00010000
-#define IWL_DL_RF_KILL 0x00020000
-#define IWL_DL_FW_ERRORS 0x00040000
-#define IWL_DL_LED 0x00080000
-/* 0x00F00000 - 0x00100000 */
-#define IWL_DL_RATE 0x00100000
-#define IWL_DL_CALIB 0x00200000
-#define IWL_DL_WEP 0x00400000
-#define IWL_DL_TX 0x00800000
-/* 0x0F000000 - 0x01000000 */
-#define IWL_DL_RX 0x01000000
-#define IWL_DL_ISR 0x02000000
-#define IWL_DL_HT 0x04000000
-#define IWL_DL_EXTERNAL 0x08000000
-/* 0xF0000000 - 0x10000000 */
-#define IWL_DL_11H 0x10000000
-#define IWL_DL_STATS 0x20000000
-#define IWL_DL_TX_REPLY 0x40000000
-#define IWL_DL_TX_QUEUES 0x80000000
-
-#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
-#define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a)
-#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
-#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
-#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
-#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a)
-#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a)
-#define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a)
-#define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a)
-#define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a)
-#define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a)
-#define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a)
-#define IWL_DEBUG_QUOTA(p, f, a...) IWL_DEBUG(p, IWL_DL_QUOTA, f, ## a)
-#define IWL_DEBUG_TE(p, f, a...) IWL_DEBUG(p, IWL_DL_TE, f, ## a)
-#define IWL_DEBUG_EEPROM(d, f, a...) IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a)
-#define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a)
-#define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a)
-#define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a)
-#define IWL_DEBUG_FW_ERRORS(p, f, a...) IWL_DEBUG(p, IWL_DL_FW_ERRORS, f, ## a)
-#define IWL_DEBUG_DROP(p, f, a...) IWL_DEBUG(p, IWL_DL_DROP, f, ## a)
-#define IWL_DEBUG_DROP_LIMIT(p, f, a...) \
- IWL_DEBUG_LIMIT(p, IWL_DL_DROP, f, ## a)
-#define IWL_DEBUG_COEX(p, f, a...) IWL_DEBUG(p, IWL_DL_COEX, f, ## a)
-#define IWL_DEBUG_RATE(p, f, a...) IWL_DEBUG(p, IWL_DL_RATE, f, ## a)
-#define IWL_DEBUG_RATE_LIMIT(p, f, a...) \
- IWL_DEBUG_LIMIT(p, IWL_DL_RATE, f, ## a)
-#define IWL_DEBUG_ASSOC(p, f, a...) \
- IWL_DEBUG(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
-#define IWL_DEBUG_ASSOC_LIMIT(p, f, a...) \
- IWL_DEBUG_LIMIT(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
-#define IWL_DEBUG_HT(p, f, a...) IWL_DEBUG(p, IWL_DL_HT, f, ## a)
-#define IWL_DEBUG_STATS(p, f, a...) IWL_DEBUG(p, IWL_DL_STATS, f, ## a)
-#define IWL_DEBUG_STATS_LIMIT(p, f, a...) \
- IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a)
-#define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a)
-#define IWL_DEBUG_TX_QUEUES(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_QUEUES, f, ## a)
-#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a)
-#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
-#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a)
-#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
-#define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a)
-
-#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h
deleted file mode 100644
index 71a78cede9b0..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#if !defined(__IWLWIFI_DEVICE_TRACE_DATA) || defined(TRACE_HEADER_MULTI_READ)
-#define __IWLWIFI_DEVICE_TRACE_DATA
-
-#include <linux/tracepoint.h>
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_data
-
-TRACE_EVENT(iwlwifi_dev_tx_data,
- TP_PROTO(const struct device *dev,
- struct sk_buff *skb,
- u8 hdr_len, size_t data_len),
- TP_ARGS(dev, skb, hdr_len, data_len),
- TP_STRUCT__entry(
- DEV_ENTRY
-
- __dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- if (iwl_trace_data(skb))
- skb_copy_bits(skb, hdr_len,
- __get_dynamic_array(data), data_len);
- ),
- TP_printk("[%s] TX frame data", __get_str(dev))
-);
-
-TRACE_EVENT(iwlwifi_dev_rx_data,
- TP_PROTO(const struct device *dev,
- const struct iwl_trans *trans,
- void *rxbuf, size_t len),
- TP_ARGS(dev, trans, rxbuf, len),
- TP_STRUCT__entry(
- DEV_ENTRY
-
- __dynamic_array(u8, data,
- len - iwl_rx_trace_len(trans, rxbuf, len))
- ),
- TP_fast_assign(
- size_t offs = iwl_rx_trace_len(trans, rxbuf, len);
- DEV_ASSIGN;
- if (offs < len)
- memcpy(__get_dynamic_array(data),
- ((u8 *)rxbuf) + offs, len - offs);
- ),
- TP_printk("[%s] RX frame data", __get_str(dev))
-);
-#endif /* __IWLWIFI_DEVICE_TRACE_DATA */
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE iwl-devtrace-data
-#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h
deleted file mode 100644
index f62c54485852..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#if !defined(__IWLWIFI_DEVICE_TRACE_IO) || defined(TRACE_HEADER_MULTI_READ)
-#define __IWLWIFI_DEVICE_TRACE_IO
-
-#include <linux/tracepoint.h>
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_io
-
-TRACE_EVENT(iwlwifi_dev_ioread32,
- TP_PROTO(const struct device *dev, u32 offs, u32 val),
- TP_ARGS(dev, offs, val),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u32, offs)
- __field(u32, val)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->offs = offs;
- __entry->val = val;
- ),
- TP_printk("[%s] read io[%#x] = %#x",
- __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_iowrite8,
- TP_PROTO(const struct device *dev, u32 offs, u8 val),
- TP_ARGS(dev, offs, val),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u32, offs)
- __field(u8, val)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->offs = offs;
- __entry->val = val;
- ),
- TP_printk("[%s] write io[%#x] = %#x)",
- __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_iowrite32,
- TP_PROTO(const struct device *dev, u32 offs, u32 val),
- TP_ARGS(dev, offs, val),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u32, offs)
- __field(u32, val)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->offs = offs;
- __entry->val = val;
- ),
- TP_printk("[%s] write io[%#x] = %#x)",
- __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_iowrite_prph32,
- TP_PROTO(const struct device *dev, u32 offs, u32 val),
- TP_ARGS(dev, offs, val),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u32, offs)
- __field(u32, val)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->offs = offs;
- __entry->val = val;
- ),
- TP_printk("[%s] write PRPH[%#x] = %#x)",
- __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_ioread_prph32,
- TP_PROTO(const struct device *dev, u32 offs, u32 val),
- TP_ARGS(dev, offs, val),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u32, offs)
- __field(u32, val)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->offs = offs;
- __entry->val = val;
- ),
- TP_printk("[%s] read PRPH[%#x] = %#x",
- __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_irq,
- TP_PROTO(const struct device *dev),
- TP_ARGS(dev),
- TP_STRUCT__entry(
- DEV_ENTRY
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- ),
- /* TP_printk("") doesn't compile */
- TP_printk("%d", 0)
-);
-
-TRACE_EVENT(iwlwifi_dev_ict_read,
- TP_PROTO(const struct device *dev, u32 index, u32 value),
- TP_ARGS(dev, index, value),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u32, index)
- __field(u32, value)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->index = index;
- __entry->value = value;
- ),
- TP_printk("[%s] read ict[%d] = %#.8x",
- __get_str(dev), __entry->index, __entry->value)
-);
-#endif /* __IWLWIFI_DEVICE_TRACE_IO */
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE iwl-devtrace-io
-#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h
deleted file mode 100644
index eb4b99a1c8cd..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#if !defined(__IWLWIFI_DEVICE_TRACE_IWLWIFI) || defined(TRACE_HEADER_MULTI_READ)
-#define __IWLWIFI_DEVICE_TRACE_IWLWIFI
-
-#include <linux/tracepoint.h>
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi
-
-TRACE_EVENT(iwlwifi_dev_hcmd,
- TP_PROTO(const struct device *dev,
- struct iwl_host_cmd *cmd, u16 total_size,
- struct iwl_cmd_header_wide *hdr),
- TP_ARGS(dev, cmd, total_size, hdr),
- TP_STRUCT__entry(
- DEV_ENTRY
- __dynamic_array(u8, hcmd, total_size)
- __field(u32, flags)
- ),
- TP_fast_assign(
- int i, offset = sizeof(struct iwl_cmd_header);
-
- if (hdr->group_id)
- offset = sizeof(struct iwl_cmd_header_wide);
-
- DEV_ASSIGN;
- __entry->flags = cmd->flags;
- memcpy(__get_dynamic_array(hcmd), hdr, offset);
-
- for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
- if (!cmd->len[i])
- continue;
- memcpy((u8 *)__get_dynamic_array(hcmd) + offset,
- cmd->data[i], cmd->len[i]);
- offset += cmd->len[i];
- }
- ),
- TP_printk("[%s] hcmd %#.2x.%#.2x (%ssync)",
- __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[1],
- ((u8 *)__get_dynamic_array(hcmd))[0],
- __entry->flags & CMD_ASYNC ? "a" : "")
-);
-
-TRACE_EVENT(iwlwifi_dev_rx,
- TP_PROTO(const struct device *dev, const struct iwl_trans *trans,
- struct iwl_rx_packet *pkt, size_t len),
- TP_ARGS(dev, trans, pkt, len),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u8, cmd)
- __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, pkt, len))
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->cmd = pkt->hdr.cmd;
- memcpy(__get_dynamic_array(rxbuf), pkt,
- iwl_rx_trace_len(trans, pkt, len));
- ),
- TP_printk("[%s] RX cmd %#.2x",
- __get_str(dev), __entry->cmd)
-);
-
-TRACE_EVENT(iwlwifi_dev_tx,
- TP_PROTO(const struct device *dev, struct sk_buff *skb,
- void *tfd, size_t tfdlen,
- void *buf0, size_t buf0_len,
- void *buf1, size_t buf1_len),
- TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len),
- TP_STRUCT__entry(
- DEV_ENTRY
-
- __field(size_t, framelen)
- __dynamic_array(u8, tfd, tfdlen)
-
- /*
- * Do not insert between or below these items,
- * we want to keep the frame together (except
- * for the possible padding).
- */
- __dynamic_array(u8, buf0, buf0_len)
- __dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->framelen = buf0_len + buf1_len;
- memcpy(__get_dynamic_array(tfd), tfd, tfdlen);
- memcpy(__get_dynamic_array(buf0), buf0, buf0_len);
- if (!iwl_trace_data(skb))
- memcpy(__get_dynamic_array(buf1), buf1, buf1_len);
- ),
- TP_printk("[%s] TX %.2x (%zu bytes)",
- __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0],
- __entry->framelen)
-);
-
-TRACE_EVENT(iwlwifi_dev_ucode_error,
- TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low,
- u32 data1, u32 data2, u32 line, u32 blink1,
- u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time,
- u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver,
- u32 brd_ver),
- TP_ARGS(dev, desc, tsf_low, data1, data2, line,
- blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2,
- gp3, major, minor, hw_ver, brd_ver),
- TP_STRUCT__entry(
- DEV_ENTRY
- __field(u32, desc)
- __field(u32, tsf_low)
- __field(u32, data1)
- __field(u32, data2)
- __field(u32, line)
- __field(u32, blink1)
- __field(u32, blink2)
- __field(u32, ilink1)
- __field(u32, ilink2)
- __field(u32, bcon_time)
- __field(u32, gp1)
- __field(u32, gp2)
- __field(u32, gp3)
- __field(u32, major)
- __field(u32, minor)
- __field(u32, hw_ver)
- __field(u32, brd_ver)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->desc = desc;
- __entry->tsf_low = tsf_low;
- __entry->data1 = data1;
- __entry->data2 = data2;
- __entry->line = line;
- __entry->blink1 = blink1;
- __entry->blink2 = blink2;
- __entry->ilink1 = ilink1;
- __entry->ilink2 = ilink2;
- __entry->bcon_time = bcon_time;
- __entry->gp1 = gp1;
- __entry->gp2 = gp2;
- __entry->gp3 = gp3;
- __entry->major = major;
- __entry->minor = minor;
- __entry->hw_ver = hw_ver;
- __entry->brd_ver = brd_ver;
- ),
- TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, "
- "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X "
- "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X "
- "minor 0x%08X hw 0x%08X brd 0x%08X",
- __get_str(dev), __entry->desc, __entry->tsf_low,
- __entry->data1,
- __entry->data2, __entry->line, __entry->blink1,
- __entry->blink2, __entry->ilink1, __entry->ilink2,
- __entry->bcon_time, __entry->gp1, __entry->gp2,
- __entry->gp3, __entry->major, __entry->minor,
- __entry->hw_ver, __entry->brd_ver)
-);
-
-TRACE_EVENT(iwlwifi_dev_ucode_event,
- TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
- TP_ARGS(dev, time, data, ev),
- TP_STRUCT__entry(
- DEV_ENTRY
-
- __field(u32, time)
- __field(u32, data)
- __field(u32, ev)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->time = time;
- __entry->data = data;
- __entry->ev = ev;
- ),
- TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
- __get_str(dev), __entry->time, __entry->data, __entry->ev)
-);
-#endif /* __IWLWIFI_DEVICE_TRACE_IWLWIFI */
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE iwl-devtrace-iwlwifi
-#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h
deleted file mode 100644
index a3b3c2465f89..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#if !defined(__IWLWIFI_DEVICE_TRACE_MSG) || defined(TRACE_HEADER_MULTI_READ)
-#define __IWLWIFI_DEVICE_TRACE_MSG
-
-#include <linux/tracepoint.h>
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_msg
-
-#define MAX_MSG_LEN 110
-
-DECLARE_EVENT_CLASS(iwlwifi_msg_event,
- TP_PROTO(struct va_format *vaf),
- TP_ARGS(vaf),
- TP_STRUCT__entry(
- __dynamic_array(char, msg, MAX_MSG_LEN)
- ),
- TP_fast_assign(
- WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
- MAX_MSG_LEN, vaf->fmt,
- *vaf->va) >= MAX_MSG_LEN);
- ),
- TP_printk("%s", __get_str(msg))
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err,
- TP_PROTO(struct va_format *vaf),
- TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn,
- TP_PROTO(struct va_format *vaf),
- TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info,
- TP_PROTO(struct va_format *vaf),
- TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit,
- TP_PROTO(struct va_format *vaf),
- TP_ARGS(vaf)
-);
-
-TRACE_EVENT(iwlwifi_dbg,
- TP_PROTO(u32 level, bool in_interrupt, const char *function,
- struct va_format *vaf),
- TP_ARGS(level, in_interrupt, function, vaf),
- TP_STRUCT__entry(
- __field(u32, level)
- __field(u8, in_interrupt)
- __string(function, function)
- __dynamic_array(char, msg, MAX_MSG_LEN)
- ),
- TP_fast_assign(
- __entry->level = level;
- __entry->in_interrupt = in_interrupt;
- __assign_str(function, function);
- WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
- MAX_MSG_LEN, vaf->fmt,
- *vaf->va) >= MAX_MSG_LEN);
- ),
- TP_printk("%s", __get_str(msg))
-);
-#endif /* __IWLWIFI_DEVICE_TRACE_MSG */
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE iwl-devtrace-msg
-#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h
deleted file mode 100644
index 10839fae9cd9..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#if !defined(__IWLWIFI_DEVICE_TRACE_UCODE) || defined(TRACE_HEADER_MULTI_READ)
-#define __IWLWIFI_DEVICE_TRACE_UCODE
-
-#include <linux/tracepoint.h>
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_ucode
-
-TRACE_EVENT(iwlwifi_dev_ucode_cont_event,
- TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
- TP_ARGS(dev, time, data, ev),
- TP_STRUCT__entry(
- DEV_ENTRY
-
- __field(u32, time)
- __field(u32, data)
- __field(u32, ev)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->time = time;
- __entry->data = data;
- __entry->ev = ev;
- ),
- TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
- __get_str(dev), __entry->time, __entry->data, __entry->ev)
-);
-
-TRACE_EVENT(iwlwifi_dev_ucode_wrap_event,
- TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry),
- TP_ARGS(dev, wraps, n_entry, p_entry),
- TP_STRUCT__entry(
- DEV_ENTRY
-
- __field(u32, wraps)
- __field(u32, n_entry)
- __field(u32, p_entry)
- ),
- TP_fast_assign(
- DEV_ASSIGN;
- __entry->wraps = wraps;
- __entry->n_entry = n_entry;
- __entry->p_entry = p_entry;
- ),
- TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X",
- __get_str(dev), __entry->wraps, __entry->n_entry,
- __entry->p_entry)
-);
-#endif /* __IWLWIFI_DEVICE_TRACE_UCODE */
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE iwl-devtrace-ucode
-#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c
deleted file mode 100644
index 90987d6f348e..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#include <linux/module.h>
-
-/* sparse doesn't like tracepoint macros */
-#ifndef __CHECKER__
-#include "iwl-trans.h"
-
-#define CREATE_TRACE_POINTS
-#include "iwl-devtrace.h"
-
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event);
-EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event);
-#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
deleted file mode 100644
index b87acd6a229b..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#ifndef __IWLWIFI_DEVICE_TRACE
-#include <linux/skbuff.h>
-#include <linux/ieee80211.h>
-#include <net/cfg80211.h>
-#include "iwl-trans.h"
-#if !defined(__IWLWIFI_DEVICE_TRACE)
-static inline bool iwl_trace_data(struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (void *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- if (!ieee80211_is_data(hdr->frame_control))
- return false;
- return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO);
-}
-
-static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans,
- void *rxbuf, size_t len)
-{
- struct iwl_cmd_header *cmd = (void *)((u8 *)rxbuf + sizeof(__le32));
- struct ieee80211_hdr *hdr;
-
- if (cmd->cmd != trans->rx_mpdu_cmd)
- return len;
-
- hdr = (void *)((u8 *)cmd + sizeof(struct iwl_cmd_header) +
- trans->rx_mpdu_cmd_hdr_size);
- if (!ieee80211_is_data(hdr->frame_control))
- return len;
- /* maybe try to identify EAPOL frames? */
- return sizeof(__le32) + sizeof(*cmd) + trans->rx_mpdu_cmd_hdr_size +
- ieee80211_hdrlen(hdr->frame_control);
-}
-#endif
-
-#define __IWLWIFI_DEVICE_TRACE
-
-#include <linux/tracepoint.h>
-#include <linux/device.h>
-#include "iwl-trans.h"
-
-
-#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__)
-#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
-
-#define DEV_ENTRY __string(dev, dev_name(dev))
-#define DEV_ASSIGN __assign_str(dev, dev_name(dev))
-
-#include "iwl-devtrace-io.h"
-#include "iwl-devtrace-ucode.h"
-#include "iwl-devtrace-msg.h"
-#include "iwl-devtrace-data.h"
-#include "iwl-devtrace-iwlwifi.h"
-
-#endif /* __IWLWIFI_DEVICE_TRACE */
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
deleted file mode 100644
index 463cadfbfccb..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ /dev/null
@@ -1,1706 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/completion.h>
-#include <linux/dma-mapping.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-
-#include "iwl-drv.h"
-#include "iwl-csr.h"
-#include "iwl-debug.h"
-#include "iwl-trans.h"
-#include "iwl-op-mode.h"
-#include "iwl-agn-hw.h"
-#include "iwl-fw.h"
-#include "iwl-config.h"
-#include "iwl-modparams.h"
-
-/******************************************************************************
- *
- * module boiler plate
- *
- ******************************************************************************/
-
-#define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux"
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
-MODULE_LICENSE("GPL");
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-static struct dentry *iwl_dbgfs_root;
-#endif
-
-/**
- * struct iwl_drv - drv common data
- * @list: list of drv structures using this opmode
- * @fw: the iwl_fw structure
- * @op_mode: the running op_mode
- * @trans: transport layer
- * @dev: for debug prints only
- * @cfg: configuration struct
- * @fw_index: firmware revision to try loading
- * @firmware_name: composite filename of ucode file to load
- * @request_firmware_complete: the firmware has been obtained from user space
- */
-struct iwl_drv {
- struct list_head list;
- struct iwl_fw fw;
-
- struct iwl_op_mode *op_mode;
- struct iwl_trans *trans;
- struct device *dev;
- const struct iwl_cfg *cfg;
-
- int fw_index; /* firmware we're trying to load */
- char firmware_name[32]; /* name of firmware file to load */
-
- struct completion request_firmware_complete;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- struct dentry *dbgfs_drv;
- struct dentry *dbgfs_trans;
- struct dentry *dbgfs_op_mode;
-#endif
-};
-
-enum {
- DVM_OP_MODE = 0,
- MVM_OP_MODE = 1,
-};
-
-/* Protects the table contents, i.e. the ops pointer & drv list */
-static struct mutex iwlwifi_opmode_table_mtx;
-static struct iwlwifi_opmode_table {
- const char *name; /* name: iwldvm, iwlmvm, etc */
- const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */
- struct list_head drv; /* list of devices using this op_mode */
-} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */
- [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL },
- [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
-};
-
-#define IWL_DEFAULT_SCAN_CHANNELS 40
-
-/*
- * struct fw_sec: Just for the image parsing process.
- * For the fw storage we are using struct fw_desc.
- */
-struct fw_sec {
- const void *data; /* the sec data */
- size_t size; /* section size */
- u32 offset; /* offset of writing in the device */
-};
-
-static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
-{
- vfree(desc->data);
- desc->data = NULL;
- desc->len = 0;
-}
-
-static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
-{
- int i;
- for (i = 0; i < IWL_UCODE_SECTION_MAX; i++)
- iwl_free_fw_desc(drv, &img->sec[i]);
-}
-
-static void iwl_dealloc_ucode(struct iwl_drv *drv)
-{
- int i;
-
- kfree(drv->fw.dbg_dest_tlv);
- for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
- kfree(drv->fw.dbg_conf_tlv[i]);
- for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++)
- kfree(drv->fw.dbg_trigger_tlv[i]);
-
- for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
- iwl_free_fw_img(drv, drv->fw.img + i);
-}
-
-static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc,
- struct fw_sec *sec)
-{
- void *data;
-
- desc->data = NULL;
-
- if (!sec || !sec->size)
- return -EINVAL;
-
- data = vmalloc(sec->size);
- if (!data)
- return -ENOMEM;
-
- desc->len = sec->size;
- desc->offset = sec->offset;
- memcpy(data, sec->data, desc->len);
- desc->data = data;
-
- return 0;
-}
-
-static void iwl_req_fw_callback(const struct firmware *ucode_raw,
- void *context);
-
-#define UCODE_EXPERIMENTAL_INDEX 100
-#define UCODE_EXPERIMENTAL_TAG "exp"
-
-static int iwl_request_firmware(struct iwl_drv *drv, bool first)
-{
- const char *name_pre = drv->cfg->fw_name_pre;
- char tag[8];
-
- if (first) {
-#ifdef CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
- drv->fw_index = UCODE_EXPERIMENTAL_INDEX;
- strcpy(tag, UCODE_EXPERIMENTAL_TAG);
- } else if (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) {
-#endif
- drv->fw_index = drv->cfg->ucode_api_max;
- sprintf(tag, "%d", drv->fw_index);
- } else {
- drv->fw_index--;
- sprintf(tag, "%d", drv->fw_index);
- }
-
- if (drv->fw_index < drv->cfg->ucode_api_min) {
- IWL_ERR(drv, "no suitable firmware found!\n");
- return -ENOENT;
- }
-
- snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode",
- name_pre, tag);
-
- /*
- * Starting 8000B - FW name format has changed. This overwrites the
- * previous name and uses the new format.
- */
- if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
- char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev);
-
- snprintf(drv->firmware_name, sizeof(drv->firmware_name),
- "%s%c-%s.ucode", name_pre, rev_step, tag);
- }
-
- IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
- (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
- ? "EXPERIMENTAL " : "",
- drv->firmware_name);
-
- return request_firmware_nowait(THIS_MODULE, 1, drv->firmware_name,
- drv->trans->dev,
- GFP_KERNEL, drv, iwl_req_fw_callback);
-}
-
-struct fw_img_parsing {
- struct fw_sec sec[IWL_UCODE_SECTION_MAX];
- int sec_counter;
-};
-
-/*
- * struct fw_sec_parsing: to extract fw section and it's offset from tlv
- */
-struct fw_sec_parsing {
- __le32 offset;
- const u8 data[];
-} __packed;
-
-/**
- * struct iwl_tlv_calib_data - parse the default calib data from TLV
- *
- * @ucode_type: the uCode to which the following default calib relates.
- * @calib: default calibrations.
- */
-struct iwl_tlv_calib_data {
- __le32 ucode_type;
- struct iwl_tlv_calib_ctrl calib;
-} __packed;
-
-struct iwl_firmware_pieces {
- struct fw_img_parsing img[IWL_UCODE_TYPE_MAX];
-
- u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
- u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
-
- /* FW debug data parsed for driver usage */
- struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
- struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
- size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
- struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
- size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
-};
-
-/*
- * These functions are just to extract uCode section data from the pieces
- * structure.
- */
-static struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces,
- enum iwl_ucode_type type,
- int sec)
-{
- return &pieces->img[type].sec[sec];
-}
-
-static void set_sec_data(struct iwl_firmware_pieces *pieces,
- enum iwl_ucode_type type,
- int sec,
- const void *data)
-{
- pieces->img[type].sec[sec].data = data;
-}
-
-static void set_sec_size(struct iwl_firmware_pieces *pieces,
- enum iwl_ucode_type type,
- int sec,
- size_t size)
-{
- pieces->img[type].sec[sec].size = size;
-}
-
-static size_t get_sec_size(struct iwl_firmware_pieces *pieces,
- enum iwl_ucode_type type,
- int sec)
-{
- return pieces->img[type].sec[sec].size;
-}
-
-static void set_sec_offset(struct iwl_firmware_pieces *pieces,
- enum iwl_ucode_type type,
- int sec,
- u32 offset)
-{
- pieces->img[type].sec[sec].offset = offset;
-}
-
-static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len)
-{
- int i, j;
- struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data;
- struct iwl_fw_cipher_scheme *fwcs;
- struct ieee80211_cipher_scheme *cs;
- u32 cipher;
-
- if (len < sizeof(*l) ||
- len < sizeof(l->size) + l->size * sizeof(l->cs[0]))
- return -EINVAL;
-
- for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) {
- fwcs = &l->cs[j];
- cipher = le32_to_cpu(fwcs->cipher);
-
- /* we skip schemes with zero cipher suite selector */
- if (!cipher)
- continue;
-
- cs = &fw->cs[j++];
- cs->cipher = cipher;
- cs->iftype = BIT(NL80211_IFTYPE_STATION);
- cs->hdr_len = fwcs->hdr_len;
- cs->pn_len = fwcs->pn_len;
- cs->pn_off = fwcs->pn_off;
- cs->key_idx_off = fwcs->key_idx_off;
- cs->key_idx_mask = fwcs->key_idx_mask;
- cs->key_idx_shift = fwcs->key_idx_shift;
- cs->mic_len = fwcs->mic_len;
- }
-
- return 0;
-}
-
-static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data,
- const u32 len)
-{
- struct iwl_fw_gscan_capabilities *fw_capa = (void *)data;
- struct iwl_gscan_capabilities *capa = &fw->gscan_capa;
-
- if (len < sizeof(*fw_capa))
- return -EINVAL;
-
- capa->max_scan_cache_size = le32_to_cpu(fw_capa->max_scan_cache_size);
- capa->max_scan_buckets = le32_to_cpu(fw_capa->max_scan_buckets);
- capa->max_ap_cache_per_scan =
- le32_to_cpu(fw_capa->max_ap_cache_per_scan);
- capa->max_rssi_sample_size = le32_to_cpu(fw_capa->max_rssi_sample_size);
- capa->max_scan_reporting_threshold =
- le32_to_cpu(fw_capa->max_scan_reporting_threshold);
- capa->max_hotlist_aps = le32_to_cpu(fw_capa->max_hotlist_aps);
- capa->max_significant_change_aps =
- le32_to_cpu(fw_capa->max_significant_change_aps);
- capa->max_bssid_history_entries =
- le32_to_cpu(fw_capa->max_bssid_history_entries);
- return 0;
-}
-
-/*
- * Gets uCode section from tlv.
- */
-static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces,
- const void *data, enum iwl_ucode_type type,
- int size)
-{
- struct fw_img_parsing *img;
- struct fw_sec *sec;
- struct fw_sec_parsing *sec_parse;
-
- if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX))
- return -1;
-
- sec_parse = (struct fw_sec_parsing *)data;
-
- img = &pieces->img[type];
- sec = &img->sec[img->sec_counter];
-
- sec->offset = le32_to_cpu(sec_parse->offset);
- sec->data = sec_parse->data;
- sec->size = size - sizeof(sec_parse->offset);
-
- ++img->sec_counter;
-
- return 0;
-}
-
-static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data)
-{
- struct iwl_tlv_calib_data *def_calib =
- (struct iwl_tlv_calib_data *)data;
- u32 ucode_type = le32_to_cpu(def_calib->ucode_type);
- if (ucode_type >= IWL_UCODE_TYPE_MAX) {
- IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n",
- ucode_type);
- return -EINVAL;
- }
- drv->fw.default_calib[ucode_type].flow_trigger =
- def_calib->calib.flow_trigger;
- drv->fw.default_calib[ucode_type].event_trigger =
- def_calib->calib.event_trigger;
-
- return 0;
-}
-
-static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data,
- struct iwl_ucode_capabilities *capa)
-{
- const struct iwl_ucode_api *ucode_api = (void *)data;
- u32 api_index = le32_to_cpu(ucode_api->api_index);
- u32 api_flags = le32_to_cpu(ucode_api->api_flags);
- int i;
-
- if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) {
- IWL_ERR(drv, "api_index larger than supported by driver\n");
- /* don't return an error so we can load FW that has more bits */
- return 0;
- }
-
- for (i = 0; i < 32; i++) {
- if (api_flags & BIT(i))
- __set_bit(i + 32 * api_index, capa->_api);
- }
-
- return 0;
-}
-
-static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data,
- struct iwl_ucode_capabilities *capa)
-{
- const struct iwl_ucode_capa *ucode_capa = (void *)data;
- u32 api_index = le32_to_cpu(ucode_capa->api_index);
- u32 api_flags = le32_to_cpu(ucode_capa->api_capa);
- int i;
-
- if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) {
- IWL_ERR(drv, "api_index larger than supported by driver\n");
- /* don't return an error so we can load FW that has more bits */
- return 0;
- }
-
- for (i = 0; i < 32; i++) {
- if (api_flags & BIT(i))
- __set_bit(i + 32 * api_index, capa->_capa);
- }
-
- return 0;
-}
-
-static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
- const struct firmware *ucode_raw,
- struct iwl_firmware_pieces *pieces)
-{
- struct iwl_ucode_header *ucode = (void *)ucode_raw->data;
- u32 api_ver, hdr_size, build;
- char buildstr[25];
- const u8 *src;
-
- drv->fw.ucode_ver = le32_to_cpu(ucode->ver);
- api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
-
- switch (api_ver) {
- default:
- hdr_size = 28;
- if (ucode_raw->size < hdr_size) {
- IWL_ERR(drv, "File size too small!\n");
- return -EINVAL;
- }
- build = le32_to_cpu(ucode->u.v2.build);
- set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
- le32_to_cpu(ucode->u.v2.inst_size));
- set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
- le32_to_cpu(ucode->u.v2.data_size));
- set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
- le32_to_cpu(ucode->u.v2.init_size));
- set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
- le32_to_cpu(ucode->u.v2.init_data_size));
- src = ucode->u.v2.data;
- break;
- case 0:
- case 1:
- case 2:
- hdr_size = 24;
- if (ucode_raw->size < hdr_size) {
- IWL_ERR(drv, "File size too small!\n");
- return -EINVAL;
- }
- build = 0;
- set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
- le32_to_cpu(ucode->u.v1.inst_size));
- set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
- le32_to_cpu(ucode->u.v1.data_size));
- set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
- le32_to_cpu(ucode->u.v1.init_size));
- set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
- le32_to_cpu(ucode->u.v1.init_data_size));
- src = ucode->u.v1.data;
- break;
- }
-
- if (build)
- sprintf(buildstr, " build %u%s", build,
- (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
- ? " (EXP)" : "");
- else
- buildstr[0] = '\0';
-
- snprintf(drv->fw.fw_version,
- sizeof(drv->fw.fw_version),
- "%u.%u.%u.%u%s",
- IWL_UCODE_MAJOR(drv->fw.ucode_ver),
- IWL_UCODE_MINOR(drv->fw.ucode_ver),
- IWL_UCODE_API(drv->fw.ucode_ver),
- IWL_UCODE_SERIAL(drv->fw.ucode_ver),
- buildstr);
-
- /* Verify size of file vs. image size info in file's header */
-
- if (ucode_raw->size != hdr_size +
- get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) +
- get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) +
- get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) +
- get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)) {
-
- IWL_ERR(drv,
- "uCode file size %d does not match expected size\n",
- (int)ucode_raw->size);
- return -EINVAL;
- }
-
-
- set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, src);
- src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST);
- set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
- IWLAGN_RTC_INST_LOWER_BOUND);
- set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, src);
- src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA);
- set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
- IWLAGN_RTC_DATA_LOWER_BOUND);
- set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, src);
- src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST);
- set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
- IWLAGN_RTC_INST_LOWER_BOUND);
- set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, src);
- src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA);
- set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
- IWLAGN_RTC_DATA_LOWER_BOUND);
- return 0;
-}
-
-static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
- const struct firmware *ucode_raw,
- struct iwl_firmware_pieces *pieces,
- struct iwl_ucode_capabilities *capa)
-{
- struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data;
- struct iwl_ucode_tlv *tlv;
- size_t len = ucode_raw->size;
- const u8 *data;
- u32 tlv_len;
- u32 usniffer_img;
- enum iwl_ucode_tlv_type tlv_type;
- const u8 *tlv_data;
- char buildstr[25];
- u32 build, paging_mem_size;
- int num_of_cpus;
- bool usniffer_images = false;
- bool usniffer_req = false;
- bool gscan_capa = false;
-
- if (len < sizeof(*ucode)) {
- IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
- return -EINVAL;
- }
-
- if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) {
- IWL_ERR(drv, "invalid uCode magic: 0X%x\n",
- le32_to_cpu(ucode->magic));
- return -EINVAL;
- }
-
- drv->fw.ucode_ver = le32_to_cpu(ucode->ver);
- memcpy(drv->fw.human_readable, ucode->human_readable,
- sizeof(drv->fw.human_readable));
- build = le32_to_cpu(ucode->build);
-
- if (build)
- sprintf(buildstr, " build %u%s", build,
- (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
- ? " (EXP)" : "");
- else
- buildstr[0] = '\0';
-
- snprintf(drv->fw.fw_version,
- sizeof(drv->fw.fw_version),
- "%u.%u.%u.%u%s",
- IWL_UCODE_MAJOR(drv->fw.ucode_ver),
- IWL_UCODE_MINOR(drv->fw.ucode_ver),
- IWL_UCODE_API(drv->fw.ucode_ver),
- IWL_UCODE_SERIAL(drv->fw.ucode_ver),
- buildstr);
-
- data = ucode->data;
-
- len -= sizeof(*ucode);
-
- while (len >= sizeof(*tlv)) {
- len -= sizeof(*tlv);
- tlv = (void *)data;
-
- tlv_len = le32_to_cpu(tlv->length);
- tlv_type = le32_to_cpu(tlv->type);
- tlv_data = tlv->data;
-
- if (len < tlv_len) {
- IWL_ERR(drv, "invalid TLV len: %zd/%u\n",
- len, tlv_len);
- return -EINVAL;
- }
- len -= ALIGN(tlv_len, 4);
- data += sizeof(*tlv) + ALIGN(tlv_len, 4);
-
- switch (tlv_type) {
- case IWL_UCODE_TLV_INST:
- set_sec_data(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_INST, tlv_data);
- set_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_INST, tlv_len);
- set_sec_offset(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_INST,
- IWLAGN_RTC_INST_LOWER_BOUND);
- break;
- case IWL_UCODE_TLV_DATA:
- set_sec_data(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA, tlv_data);
- set_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA, tlv_len);
- set_sec_offset(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA,
- IWLAGN_RTC_DATA_LOWER_BOUND);
- break;
- case IWL_UCODE_TLV_INIT:
- set_sec_data(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_INST, tlv_data);
- set_sec_size(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_INST, tlv_len);
- set_sec_offset(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_INST,
- IWLAGN_RTC_INST_LOWER_BOUND);
- break;
- case IWL_UCODE_TLV_INIT_DATA:
- set_sec_data(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_DATA, tlv_data);
- set_sec_size(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_DATA, tlv_len);
- set_sec_offset(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_DATA,
- IWLAGN_RTC_DATA_LOWER_BOUND);
- break;
- case IWL_UCODE_TLV_BOOT:
- IWL_ERR(drv, "Found unexpected BOOT ucode\n");
- break;
- case IWL_UCODE_TLV_PROBE_MAX_LEN:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- capa->max_probe_length =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_PAN:
- if (tlv_len)
- goto invalid_tlv_len;
- capa->flags |= IWL_UCODE_TLV_FLAGS_PAN;
- break;
- case IWL_UCODE_TLV_FLAGS:
- /* must be at least one u32 */
- if (tlv_len < sizeof(u32))
- goto invalid_tlv_len;
- /* and a proper number of u32s */
- if (tlv_len % sizeof(u32))
- goto invalid_tlv_len;
- /*
- * This driver only reads the first u32 as
- * right now no more features are defined,
- * if that changes then either the driver
- * will not work with the new firmware, or
- * it'll not take advantage of new features.
- */
- capa->flags = le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_API_CHANGES_SET:
- if (tlv_len != sizeof(struct iwl_ucode_api))
- goto invalid_tlv_len;
- if (iwl_set_ucode_api_flags(drv, tlv_data, capa))
- goto tlv_error;
- break;
- case IWL_UCODE_TLV_ENABLED_CAPABILITIES:
- if (tlv_len != sizeof(struct iwl_ucode_capa))
- goto invalid_tlv_len;
- if (iwl_set_ucode_capabilities(drv, tlv_data, capa))
- goto tlv_error;
- break;
- case IWL_UCODE_TLV_INIT_EVTLOG_PTR:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- pieces->init_evtlog_ptr =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_INIT_EVTLOG_SIZE:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- pieces->init_evtlog_size =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_INIT_ERRLOG_PTR:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- pieces->init_errlog_ptr =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_RUNT_EVTLOG_PTR:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- pieces->inst_evtlog_ptr =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- pieces->inst_evtlog_size =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_RUNT_ERRLOG_PTR:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- pieces->inst_errlog_ptr =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_ENHANCE_SENS_TBL:
- if (tlv_len)
- goto invalid_tlv_len;
- drv->fw.enhance_sensitivity_table = true;
- break;
- case IWL_UCODE_TLV_WOWLAN_INST:
- set_sec_data(pieces, IWL_UCODE_WOWLAN,
- IWL_UCODE_SECTION_INST, tlv_data);
- set_sec_size(pieces, IWL_UCODE_WOWLAN,
- IWL_UCODE_SECTION_INST, tlv_len);
- set_sec_offset(pieces, IWL_UCODE_WOWLAN,
- IWL_UCODE_SECTION_INST,
- IWLAGN_RTC_INST_LOWER_BOUND);
- break;
- case IWL_UCODE_TLV_WOWLAN_DATA:
- set_sec_data(pieces, IWL_UCODE_WOWLAN,
- IWL_UCODE_SECTION_DATA, tlv_data);
- set_sec_size(pieces, IWL_UCODE_WOWLAN,
- IWL_UCODE_SECTION_DATA, tlv_len);
- set_sec_offset(pieces, IWL_UCODE_WOWLAN,
- IWL_UCODE_SECTION_DATA,
- IWLAGN_RTC_DATA_LOWER_BOUND);
- break;
- case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- capa->standard_phy_calibration_size =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_SEC_RT:
- iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
- tlv_len);
- drv->fw.mvm_fw = true;
- break;
- case IWL_UCODE_TLV_SEC_INIT:
- iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
- tlv_len);
- drv->fw.mvm_fw = true;
- break;
- case IWL_UCODE_TLV_SEC_WOWLAN:
- iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
- tlv_len);
- drv->fw.mvm_fw = true;
- break;
- case IWL_UCODE_TLV_DEF_CALIB:
- if (tlv_len != sizeof(struct iwl_tlv_calib_data))
- goto invalid_tlv_len;
- if (iwl_set_default_calib(drv, tlv_data))
- goto tlv_error;
- break;
- case IWL_UCODE_TLV_PHY_SKU:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data);
- drv->fw.valid_tx_ant = (drv->fw.phy_config &
- FW_PHY_CFG_TX_CHAIN) >>
- FW_PHY_CFG_TX_CHAIN_POS;
- drv->fw.valid_rx_ant = (drv->fw.phy_config &
- FW_PHY_CFG_RX_CHAIN) >>
- FW_PHY_CFG_RX_CHAIN_POS;
- break;
- case IWL_UCODE_TLV_SECURE_SEC_RT:
- iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
- tlv_len);
- drv->fw.mvm_fw = true;
- break;
- case IWL_UCODE_TLV_SECURE_SEC_INIT:
- iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
- tlv_len);
- drv->fw.mvm_fw = true;
- break;
- case IWL_UCODE_TLV_SECURE_SEC_WOWLAN:
- iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
- tlv_len);
- drv->fw.mvm_fw = true;
- break;
- case IWL_UCODE_TLV_NUM_OF_CPU:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- num_of_cpus =
- le32_to_cpup((__le32 *)tlv_data);
-
- if (num_of_cpus == 2) {
- drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus =
- true;
- drv->fw.img[IWL_UCODE_INIT].is_dual_cpus =
- true;
- drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus =
- true;
- } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) {
- IWL_ERR(drv, "Driver support upto 2 CPUs\n");
- return -EINVAL;
- }
- break;
- case IWL_UCODE_TLV_CSCHEME:
- if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
- goto invalid_tlv_len;
- break;
- case IWL_UCODE_TLV_N_SCAN_CHANNELS:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- capa->n_scan_channels =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_FW_VERSION: {
- __le32 *ptr = (void *)tlv_data;
- u32 major, minor;
- u8 local_comp;
-
- if (tlv_len != sizeof(u32) * 3)
- goto invalid_tlv_len;
-
- major = le32_to_cpup(ptr++);
- minor = le32_to_cpup(ptr++);
- local_comp = le32_to_cpup(ptr);
-
- snprintf(drv->fw.fw_version,
- sizeof(drv->fw.fw_version), "%u.%u.%u",
- major, minor, local_comp);
- break;
- }
- case IWL_UCODE_TLV_FW_DBG_DEST: {
- struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
-
- if (pieces->dbg_dest_tlv) {
- IWL_ERR(drv,
- "dbg destination ignored, already exists\n");
- break;
- }
-
- pieces->dbg_dest_tlv = dest;
- IWL_INFO(drv, "Found debug destination: %s\n",
- get_fw_dbg_mode_string(dest->monitor_mode));
-
- drv->fw.dbg_dest_reg_num =
- tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
- reg_ops);
- drv->fw.dbg_dest_reg_num /=
- sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
-
- break;
- }
- case IWL_UCODE_TLV_FW_DBG_CONF: {
- struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
-
- if (!pieces->dbg_dest_tlv) {
- IWL_ERR(drv,
- "Ignore dbg config %d - no destination configured\n",
- conf->id);
- break;
- }
-
- if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
- IWL_ERR(drv,
- "Skip unknown configuration: %d\n",
- conf->id);
- break;
- }
-
- if (pieces->dbg_conf_tlv[conf->id]) {
- IWL_ERR(drv,
- "Ignore duplicate dbg config %d\n",
- conf->id);
- break;
- }
-
- if (conf->usniffer)
- usniffer_req = true;
-
- IWL_INFO(drv, "Found debug configuration: %d\n",
- conf->id);
-
- pieces->dbg_conf_tlv[conf->id] = conf;
- pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
- break;
- }
- case IWL_UCODE_TLV_FW_DBG_TRIGGER: {
- struct iwl_fw_dbg_trigger_tlv *trigger =
- (void *)tlv_data;
- u32 trigger_id = le32_to_cpu(trigger->id);
-
- if (trigger_id >= ARRAY_SIZE(drv->fw.dbg_trigger_tlv)) {
- IWL_ERR(drv,
- "Skip unknown trigger: %u\n",
- trigger->id);
- break;
- }
-
- if (pieces->dbg_trigger_tlv[trigger_id]) {
- IWL_ERR(drv,
- "Ignore duplicate dbg trigger %u\n",
- trigger->id);
- break;
- }
-
- IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id);
-
- pieces->dbg_trigger_tlv[trigger_id] = trigger;
- pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len;
- break;
- }
- case IWL_UCODE_TLV_SEC_RT_USNIFFER:
- usniffer_images = true;
- iwl_store_ucode_sec(pieces, tlv_data,
- IWL_UCODE_REGULAR_USNIFFER,
- tlv_len);
- break;
- case IWL_UCODE_TLV_PAGING:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- paging_mem_size = le32_to_cpup((__le32 *)tlv_data);
-
- IWL_DEBUG_FW(drv,
- "Paging: paging enabled (size = %u bytes)\n",
- paging_mem_size);
-
- if (paging_mem_size > MAX_PAGING_IMAGE_SIZE) {
- IWL_ERR(drv,
- "Paging: driver supports up to %lu bytes for paging image\n",
- MAX_PAGING_IMAGE_SIZE);
- return -EINVAL;
- }
-
- if (paging_mem_size & (FW_PAGING_SIZE - 1)) {
- IWL_ERR(drv,
- "Paging: image isn't multiple %lu\n",
- FW_PAGING_SIZE);
- return -EINVAL;
- }
-
- drv->fw.img[IWL_UCODE_REGULAR].paging_mem_size =
- paging_mem_size;
- usniffer_img = IWL_UCODE_REGULAR_USNIFFER;
- drv->fw.img[usniffer_img].paging_mem_size =
- paging_mem_size;
- break;
- case IWL_UCODE_TLV_SDIO_ADMA_ADDR:
- if (tlv_len != sizeof(u32))
- goto invalid_tlv_len;
- drv->fw.sdio_adma_addr =
- le32_to_cpup((__le32 *)tlv_data);
- break;
- case IWL_UCODE_TLV_FW_GSCAN_CAPA:
- if (iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len))
- goto invalid_tlv_len;
- gscan_capa = true;
- break;
- default:
- IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
- break;
- }
- }
-
- if (usniffer_req && !usniffer_images) {
- IWL_ERR(drv,
- "user selected to work with usniffer but usniffer image isn't available in ucode package\n");
- return -EINVAL;
- }
-
- if (len) {
- IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len);
- iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len);
- return -EINVAL;
- }
-
- /*
- * If ucode advertises that it supports GSCAN but GSCAN
- * capabilities TLV is not present, warn and continue without GSCAN.
- */
- if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) &&
- WARN(!gscan_capa,
- "GSCAN is supported but capabilities TLV is unavailable\n"))
- __clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT,
- capa->_capa);
-
- return 0;
-
- invalid_tlv_len:
- IWL_ERR(drv, "TLV %d has invalid size: %u\n", tlv_type, tlv_len);
- tlv_error:
- iwl_print_hex_dump(drv, IWL_DL_FW, tlv_data, tlv_len);
-
- return -EINVAL;
-}
-
-static int iwl_alloc_ucode(struct iwl_drv *drv,
- struct iwl_firmware_pieces *pieces,
- enum iwl_ucode_type type)
-{
- int i;
- for (i = 0;
- i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i);
- i++)
- if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]),
- get_sec(pieces, type, i)))
- return -ENOMEM;
- return 0;
-}
-
-static int validate_sec_sizes(struct iwl_drv *drv,
- struct iwl_firmware_pieces *pieces,
- const struct iwl_cfg *cfg)
-{
- IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n",
- get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_INST));
- IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n",
- get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA));
- IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n",
- get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST));
- IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n",
- get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA));
-
- /* Verify that uCode images will fit in card's SRAM. */
- if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) >
- cfg->max_inst_size) {
- IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n",
- get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_INST));
- return -1;
- }
-
- if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) >
- cfg->max_data_size) {
- IWL_ERR(drv, "uCode data len %Zd too large to fit in\n",
- get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA));
- return -1;
- }
-
- if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
- cfg->max_inst_size) {
- IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n",
- get_sec_size(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_INST));
- return -1;
- }
-
- if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) >
- cfg->max_data_size) {
- IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n",
- get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA));
- return -1;
- }
- return 0;
-}
-
-static struct iwl_op_mode *
-_iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op)
-{
- const struct iwl_op_mode_ops *ops = op->ops;
- struct dentry *dbgfs_dir = NULL;
- struct iwl_op_mode *op_mode = NULL;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- drv->dbgfs_op_mode = debugfs_create_dir(op->name,
- drv->dbgfs_drv);
- if (!drv->dbgfs_op_mode) {
- IWL_ERR(drv,
- "failed to create opmode debugfs directory\n");
- return op_mode;
- }
- dbgfs_dir = drv->dbgfs_op_mode;
-#endif
-
- op_mode = ops->start(drv->trans, drv->cfg, &drv->fw, dbgfs_dir);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (!op_mode) {
- debugfs_remove_recursive(drv->dbgfs_op_mode);
- drv->dbgfs_op_mode = NULL;
- }
-#endif
-
- return op_mode;
-}
-
-static void _iwl_op_mode_stop(struct iwl_drv *drv)
-{
- /* op_mode can be NULL if its start failed */
- if (drv->op_mode) {
- iwl_op_mode_stop(drv->op_mode);
- drv->op_mode = NULL;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- debugfs_remove_recursive(drv->dbgfs_op_mode);
- drv->dbgfs_op_mode = NULL;
-#endif
- }
-}
-
-/**
- * iwl_req_fw_callback - callback when firmware was loaded
- *
- * If loaded successfully, copies the firmware into buffers
- * for the card to fetch (via DMA).
- */
-static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
-{
- struct iwl_drv *drv = context;
- struct iwl_fw *fw = &drv->fw;
- struct iwl_ucode_header *ucode;
- struct iwlwifi_opmode_table *op;
- int err;
- struct iwl_firmware_pieces *pieces;
- const unsigned int api_max = drv->cfg->ucode_api_max;
- unsigned int api_ok = drv->cfg->ucode_api_ok;
- const unsigned int api_min = drv->cfg->ucode_api_min;
- size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX];
- u32 api_ver;
- int i;
- bool load_module = false;
-
- fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
- fw->ucode_capa.standard_phy_calibration_size =
- IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
- fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;
-
- if (!api_ok)
- api_ok = api_max;
-
- pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
- if (!pieces)
- return;
-
- if (!ucode_raw) {
- if (drv->fw_index <= api_ok)
- IWL_ERR(drv,
- "request for firmware file '%s' failed.\n",
- drv->firmware_name);
- goto try_again;
- }
-
- IWL_DEBUG_INFO(drv, "Loaded firmware file '%s' (%zd bytes).\n",
- drv->firmware_name, ucode_raw->size);
-
- /* Make sure that we got at least the API version number */
- if (ucode_raw->size < 4) {
- IWL_ERR(drv, "File size way too small!\n");
- goto try_again;
- }
-
- /* Data from ucode file: header followed by uCode images */
- ucode = (struct iwl_ucode_header *)ucode_raw->data;
-
- if (ucode->ver)
- err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
- else
- err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
- &fw->ucode_capa);
-
- if (err)
- goto try_again;
-
- if (fw_has_api(&drv->fw.ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION))
- api_ver = drv->fw.ucode_ver;
- else
- api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
-
- /*
- * api_ver should match the api version forming part of the
- * firmware filename ... but we don't check for that and only rely
- * on the API version read from firmware header from here on forward
- */
- /* no api version check required for experimental uCode */
- if (drv->fw_index != UCODE_EXPERIMENTAL_INDEX) {
- if (api_ver < api_min || api_ver > api_max) {
- IWL_ERR(drv,
- "Driver unable to support your firmware API. "
- "Driver supports v%u, firmware is v%u.\n",
- api_max, api_ver);
- goto try_again;
- }
-
- if (api_ver < api_ok) {
- if (api_ok != api_max)
- IWL_ERR(drv, "Firmware has old API version, "
- "expected v%u through v%u, got v%u.\n",
- api_ok, api_max, api_ver);
- else
- IWL_ERR(drv, "Firmware has old API version, "
- "expected v%u, got v%u.\n",
- api_max, api_ver);
- IWL_ERR(drv, "New firmware can be obtained from "
- "http://www.intellinuxwireless.org/.\n");
- }
- }
-
- /*
- * In mvm uCode there is no difference between data and instructions
- * sections.
- */
- if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
- goto try_again;
-
- /* Allocate ucode buffers for card's bus-master loading ... */
-
- /* Runtime instructions and 2 copies of data:
- * 1) unmodified from disk
- * 2) backup cache for save/restore during power-downs */
- for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
- if (iwl_alloc_ucode(drv, pieces, i))
- goto out_free_fw;
-
- if (pieces->dbg_dest_tlv) {
- drv->fw.dbg_dest_tlv =
- kmemdup(pieces->dbg_dest_tlv,
- sizeof(*pieces->dbg_dest_tlv) +
- sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
- drv->fw.dbg_dest_reg_num, GFP_KERNEL);
-
- if (!drv->fw.dbg_dest_tlv)
- goto out_free_fw;
- }
-
- for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
- if (pieces->dbg_conf_tlv[i]) {
- drv->fw.dbg_conf_tlv_len[i] =
- pieces->dbg_conf_tlv_len[i];
- drv->fw.dbg_conf_tlv[i] =
- kmemdup(pieces->dbg_conf_tlv[i],
- drv->fw.dbg_conf_tlv_len[i],
- GFP_KERNEL);
- if (!drv->fw.dbg_conf_tlv[i])
- goto out_free_fw;
- }
- }
-
- memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz));
-
- trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] =
- sizeof(struct iwl_fw_dbg_trigger_missed_bcon);
- trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0;
- trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] =
- sizeof(struct iwl_fw_dbg_trigger_cmd);
- trigger_tlv_sz[FW_DBG_TRIGGER_MLME] =
- sizeof(struct iwl_fw_dbg_trigger_mlme);
- trigger_tlv_sz[FW_DBG_TRIGGER_STATS] =
- sizeof(struct iwl_fw_dbg_trigger_stats);
- trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] =
- sizeof(struct iwl_fw_dbg_trigger_low_rssi);
- trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] =
- sizeof(struct iwl_fw_dbg_trigger_txq_timer);
- trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] =
- sizeof(struct iwl_fw_dbg_trigger_time_event);
- trigger_tlv_sz[FW_DBG_TRIGGER_BA] =
- sizeof(struct iwl_fw_dbg_trigger_ba);
-
- for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
- if (pieces->dbg_trigger_tlv[i]) {
- /*
- * If the trigger isn't long enough, WARN and exit.
- * Someone is trying to debug something and he won't
- * be able to catch the bug he is trying to chase.
- * We'd better be noisy to be sure he knows what's
- * going on.
- */
- if (WARN_ON(pieces->dbg_trigger_tlv_len[i] <
- (trigger_tlv_sz[i] +
- sizeof(struct iwl_fw_dbg_trigger_tlv))))
- goto out_free_fw;
- drv->fw.dbg_trigger_tlv_len[i] =
- pieces->dbg_trigger_tlv_len[i];
- drv->fw.dbg_trigger_tlv[i] =
- kmemdup(pieces->dbg_trigger_tlv[i],
- drv->fw.dbg_trigger_tlv_len[i],
- GFP_KERNEL);
- if (!drv->fw.dbg_trigger_tlv[i])
- goto out_free_fw;
- }
- }
-
- /* Now that we can no longer fail, copy information */
-
- /*
- * The (size - 16) / 12 formula is based on the information recorded
- * for each event, which is of mode 1 (including timestamp) for all
- * new microcodes that include this information.
- */
- fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
- if (pieces->init_evtlog_size)
- fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
- else
- fw->init_evtlog_size =
- drv->cfg->base_params->max_event_log_size;
- fw->init_errlog_ptr = pieces->init_errlog_ptr;
- fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
- if (pieces->inst_evtlog_size)
- fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
- else
- fw->inst_evtlog_size =
- drv->cfg->base_params->max_event_log_size;
- fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
-
- /*
- * figure out the offset of chain noise reset and gain commands
- * base on the size of standard phy calibration commands table size
- */
- if (fw->ucode_capa.standard_phy_calibration_size >
- IWL_MAX_PHY_CALIBRATE_TBL_SIZE)
- fw->ucode_capa.standard_phy_calibration_size =
- IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE;
-
- /* We have our copies now, allow OS release its copies */
- release_firmware(ucode_raw);
-
- mutex_lock(&iwlwifi_opmode_table_mtx);
- if (fw->mvm_fw)
- op = &iwlwifi_opmode_table[MVM_OP_MODE];
- else
- op = &iwlwifi_opmode_table[DVM_OP_MODE];
-
- IWL_INFO(drv, "loaded firmware version %s op_mode %s\n",
- drv->fw.fw_version, op->name);
-
- /* add this device to the list of devices using this op_mode */
- list_add_tail(&drv->list, &op->drv);
-
- if (op->ops) {
- drv->op_mode = _iwl_op_mode_start(drv, op);
-
- if (!drv->op_mode) {
- mutex_unlock(&iwlwifi_opmode_table_mtx);
- goto out_unbind;
- }
- } else {
- load_module = true;
- }
- mutex_unlock(&iwlwifi_opmode_table_mtx);
-
- /*
- * Complete the firmware request last so that
- * a driver unbind (stop) doesn't run while we
- * are doing the start() above.
- */
- complete(&drv->request_firmware_complete);
-
- /*
- * Load the module last so we don't block anything
- * else from proceeding if the module fails to load
- * or hangs loading.
- */
- if (load_module) {
- err = request_module("%s", op->name);
-#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR
- if (err)
- IWL_ERR(drv,
- "failed to load module %s (error %d), is dynamic loading enabled?\n",
- op->name, err);
-#endif
- }
- kfree(pieces);
- return;
-
- try_again:
- /* try next, if any */
- release_firmware(ucode_raw);
- if (iwl_request_firmware(drv, false))
- goto out_unbind;
- kfree(pieces);
- return;
-
- out_free_fw:
- IWL_ERR(drv, "failed to allocate pci memory\n");
- iwl_dealloc_ucode(drv);
- release_firmware(ucode_raw);
- out_unbind:
- kfree(pieces);
- complete(&drv->request_firmware_complete);
- device_release_driver(drv->trans->dev);
-}
-
-struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
- const struct iwl_cfg *cfg)
-{
- struct iwl_drv *drv;
- int ret;
-
- drv = kzalloc(sizeof(*drv), GFP_KERNEL);
- if (!drv) {
- ret = -ENOMEM;
- goto err;
- }
-
- drv->trans = trans;
- drv->dev = trans->dev;
- drv->cfg = cfg;
-
- init_completion(&drv->request_firmware_complete);
- INIT_LIST_HEAD(&drv->list);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- /* Create the device debugfs entries. */
- drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev),
- iwl_dbgfs_root);
-
- if (!drv->dbgfs_drv) {
- IWL_ERR(drv, "failed to create debugfs directory\n");
- ret = -ENOMEM;
- goto err_free_drv;
- }
-
- /* Create transport layer debugfs dir */
- drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv);
-
- if (!drv->trans->dbgfs_dir) {
- IWL_ERR(drv, "failed to create transport debugfs directory\n");
- ret = -ENOMEM;
- goto err_free_dbgfs;
- }
-#endif
-
- ret = iwl_request_firmware(drv, true);
- if (ret) {
- IWL_ERR(trans, "Couldn't request the fw\n");
- goto err_fw;
- }
-
- return drv;
-
-err_fw:
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-err_free_dbgfs:
- debugfs_remove_recursive(drv->dbgfs_drv);
-err_free_drv:
-#endif
- kfree(drv);
-err:
- return ERR_PTR(ret);
-}
-
-void iwl_drv_stop(struct iwl_drv *drv)
-{
- wait_for_completion(&drv->request_firmware_complete);
-
- _iwl_op_mode_stop(drv);
-
- iwl_dealloc_ucode(drv);
-
- mutex_lock(&iwlwifi_opmode_table_mtx);
- /*
- * List is empty (this item wasn't added)
- * when firmware loading failed -- in that
- * case we can't remove it from any list.
- */
- if (!list_empty(&drv->list))
- list_del(&drv->list);
- mutex_unlock(&iwlwifi_opmode_table_mtx);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- debugfs_remove_recursive(drv->dbgfs_drv);
-#endif
-
- kfree(drv);
-}
-
-
-/* shared module parameters */
-struct iwl_mod_params iwlwifi_mod_params = {
- .restart_fw = true,
- .bt_coex_active = true,
- .power_level = IWL_POWER_INDEX_1,
- .d0i3_disable = true,
-#ifndef CONFIG_IWLWIFI_UAPSD
- .uapsd_disable = true,
-#endif /* CONFIG_IWLWIFI_UAPSD */
- /* the rest are 0 by default */
-};
-IWL_EXPORT_SYMBOL(iwlwifi_mod_params);
-
-int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
-{
- int i;
- struct iwl_drv *drv;
- struct iwlwifi_opmode_table *op;
-
- mutex_lock(&iwlwifi_opmode_table_mtx);
- for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
- op = &iwlwifi_opmode_table[i];
- if (strcmp(op->name, name))
- continue;
- op->ops = ops;
- /* TODO: need to handle exceptional case */
- list_for_each_entry(drv, &op->drv, list)
- drv->op_mode = _iwl_op_mode_start(drv, op);
-
- mutex_unlock(&iwlwifi_opmode_table_mtx);
- return 0;
- }
- mutex_unlock(&iwlwifi_opmode_table_mtx);
- return -EIO;
-}
-IWL_EXPORT_SYMBOL(iwl_opmode_register);
-
-void iwl_opmode_deregister(const char *name)
-{
- int i;
- struct iwl_drv *drv;
-
- mutex_lock(&iwlwifi_opmode_table_mtx);
- for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
- if (strcmp(iwlwifi_opmode_table[i].name, name))
- continue;
- iwlwifi_opmode_table[i].ops = NULL;
-
- /* call the stop routine for all devices */
- list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list)
- _iwl_op_mode_stop(drv);
-
- mutex_unlock(&iwlwifi_opmode_table_mtx);
- return;
- }
- mutex_unlock(&iwlwifi_opmode_table_mtx);
-}
-IWL_EXPORT_SYMBOL(iwl_opmode_deregister);
-
-static int __init iwl_drv_init(void)
-{
- int i;
-
- mutex_init(&iwlwifi_opmode_table_mtx);
-
- for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++)
- INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv);
-
- pr_info(DRV_DESCRIPTION "\n");
- pr_info(DRV_COPYRIGHT "\n");
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- /* Create the root of iwlwifi debugfs subsystem. */
- iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL);
-
- if (!iwl_dbgfs_root)
- return -EFAULT;
-#endif
-
- return iwl_pci_register_driver();
-}
-module_init(iwl_drv_init);
-
-static void __exit iwl_drv_exit(void)
-{
- iwl_pci_unregister_driver();
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- debugfs_remove_recursive(iwl_dbgfs_root);
-#endif
-}
-module_exit(iwl_drv_exit);
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-module_param_named(debug, iwlwifi_mod_params.debug_level, uint,
- S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "debug output mask");
-#endif
-
-module_param_named(swcrypto, iwlwifi_mod_params.sw_crypto, int, S_IRUGO);
-MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
-module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO);
-MODULE_PARM_DESC(11n_disable,
- "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX");
-module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K,
- int, S_IRUGO);
-MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)");
-module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO);
-MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)");
-
-module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling,
- int, S_IRUGO);
-MODULE_PARM_DESC(antenna_coupling,
- "specify antenna coupling in dB (default: 0 dB)");
-
-module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
-MODULE_PARM_DESC(nvm_file, "NVM file name");
-
-module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable,
- bool, S_IRUGO);
-MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");
-
-module_param_named(lar_disable, iwlwifi_mod_params.lar_disable,
- bool, S_IRUGO);
-MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
-
-module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
- bool, S_IRUGO | S_IWUSR);
-#ifdef CONFIG_IWLWIFI_UAPSD
-MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)");
-#else
-MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: Y)");
-#endif
-
-/*
- * set bt_coex_active to true, uCode will do kill/defer
- * every time the priority line is asserted (BT is sending signals on the
- * priority line in the PCIx).
- * set bt_coex_active to false, uCode will ignore the BT activity and
- * perform the normal operation
- *
- * User might experience transmit issue on some platform due to WiFi/BT
- * co-exist problem. The possible behaviors are:
- * Able to scan and finding all the available AP
- * Not able to associate with any AP
- * On those platforms, WiFi communication can be restored by set
- * "bt_coex_active" module parameter to "false"
- *
- * default: bt_coex_active = true (BT_COEX_ENABLE)
- */
-module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active,
- bool, S_IRUGO);
-MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)");
-
-module_param_named(led_mode, iwlwifi_mod_params.led_mode, int, S_IRUGO);
-MODULE_PARM_DESC(led_mode, "0=system default, "
- "1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)");
-
-module_param_named(power_save, iwlwifi_mod_params.power_save,
- bool, S_IRUGO);
-MODULE_PARM_DESC(power_save,
- "enable WiFi power management (default: disable)");
-
-module_param_named(power_level, iwlwifi_mod_params.power_level,
- int, S_IRUGO);
-MODULE_PARM_DESC(power_level,
- "default power save level (range from 1 - 5, default: 1)");
-
-module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
-MODULE_PARM_DESC(fw_monitor,
- "firmware monitor - to debug FW (default: false - needs lots of memory)");
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h
deleted file mode 100644
index cda746b33db1..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-drv.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __iwl_drv_h__
-#define __iwl_drv_h__
-#include <linux/export.h>
-
-/* for all modules */
-#define DRV_NAME "iwlwifi"
-#define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation"
-#define DRV_AUTHOR "<ilw@linux.intel.com>"
-
-/* radio config bits (actual values from NVM definition) */
-#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */
-#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */
-#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */
-#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */
-#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */
-#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
-
-#define NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(x) (x & 0xF)
-#define NVM_RF_CFG_DASH_MSK_FAMILY_8000(x) ((x >> 4) & 0xF)
-#define NVM_RF_CFG_STEP_MSK_FAMILY_8000(x) ((x >> 8) & 0xF)
-#define NVM_RF_CFG_TYPE_MSK_FAMILY_8000(x) ((x >> 12) & 0xFFF)
-#define NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(x) ((x >> 24) & 0xF)
-#define NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(x) ((x >> 28) & 0xF)
-
-/**
- * DOC: Driver system flows - drv component
- *
- * This component implements the system flows such as bus enumeration, bus
- * removal. Bus dependent parts of system flows (such as iwl_pci_probe) are in
- * bus specific files (transport files). This is the code that is common among
- * different buses.
- *
- * This component is also in charge of managing the several implementations of
- * the wifi flows: it will allow to have several fw API implementation. These
- * different implementations will differ in the way they implement mac80211's
- * handlers too.
-
- * The init flow wrt to the drv component looks like this:
- * 1) The bus specific component is called from module_init
- * 2) The bus specific component registers the bus driver
- * 3) The bus driver calls the probe function
- * 4) The bus specific component configures the bus
- * 5) The bus specific component calls to the drv bus agnostic part
- * (iwl_drv_start)
- * 6) iwl_drv_start fetches the fw ASYNC, iwl_req_fw_callback
- * 7) iwl_req_fw_callback parses the fw file
- * 8) iwl_req_fw_callback starts the wifi implementation to matches the fw
- */
-
-struct iwl_drv;
-struct iwl_trans;
-struct iwl_cfg;
-/**
- * iwl_drv_start - start the drv
- *
- * @trans_ops: the ops of the transport
- * @cfg: device specific constants / virtual functions
- *
- * starts the driver: fetches the firmware. This should be called by bus
- * specific system flows implementations. For example, the bus specific probe
- * function should do bus related operations only, and then call to this
- * function. It returns the driver object or %NULL if an error occurred.
- */
-struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
- const struct iwl_cfg *cfg);
-
-/**
- * iwl_drv_stop - stop the drv
- *
- * @drv:
- *
- * Stop the driver. This should be called by bus specific system flows
- * implementations. For example, the bus specific remove function should first
- * call this function and then do the bus related operations only.
- */
-void iwl_drv_stop(struct iwl_drv *drv);
-
-/*
- * exported symbol management
- *
- * The driver can be split into multiple modules, in which case some symbols
- * must be exported for the sub-modules. However, if it's not split and
- * everything is built-in, then we can avoid that.
- */
-#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR
-#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_GPL(sym)
-#else
-#define IWL_EXPORT_SYMBOL(sym)
-#endif
-
-#endif /* __iwl_drv_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
deleted file mode 100644
index acc3d186c5c1..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ /dev/null
@@ -1,947 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include "iwl-drv.h"
-#include "iwl-modparams.h"
-#include "iwl-eeprom-parse.h"
-
-/* EEPROM offset definitions */
-
-/* indirect access definitions */
-#define ADDRESS_MSK 0x0000FFFF
-#define INDIRECT_TYPE_MSK 0x000F0000
-#define INDIRECT_HOST 0x00010000
-#define INDIRECT_GENERAL 0x00020000
-#define INDIRECT_REGULATORY 0x00030000
-#define INDIRECT_CALIBRATION 0x00040000
-#define INDIRECT_PROCESS_ADJST 0x00050000
-#define INDIRECT_OTHERS 0x00060000
-#define INDIRECT_TXP_LIMIT 0x00070000
-#define INDIRECT_TXP_LIMIT_SIZE 0x00080000
-#define INDIRECT_ADDRESS 0x00100000
-
-/* corresponding link offsets in EEPROM */
-#define EEPROM_LINK_HOST (2*0x64)
-#define EEPROM_LINK_GENERAL (2*0x65)
-#define EEPROM_LINK_REGULATORY (2*0x66)
-#define EEPROM_LINK_CALIBRATION (2*0x67)
-#define EEPROM_LINK_PROCESS_ADJST (2*0x68)
-#define EEPROM_LINK_OTHERS (2*0x69)
-#define EEPROM_LINK_TXP_LIMIT (2*0x6a)
-#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b)
-
-/* General */
-#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */
-#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */
-#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */
-#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */
-#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */
-#define EEPROM_VERSION (2*0x44) /* 2 bytes */
-#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */
-#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */
-#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */
-#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */
-
-/* calibration */
-struct iwl_eeprom_calib_hdr {
- u8 version;
- u8 pa_type;
- __le16 voltage;
-} __packed;
-
-#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION)
-#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL)
-
-/* temperature */
-#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL)
-#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL)
-
-/* SKU Capabilities (actual values from EEPROM definition) */
-enum eeprom_sku_bits {
- EEPROM_SKU_CAP_BAND_24GHZ = BIT(4),
- EEPROM_SKU_CAP_BAND_52GHZ = BIT(5),
- EEPROM_SKU_CAP_11N_ENABLE = BIT(6),
- EEPROM_SKU_CAP_AMT_ENABLE = BIT(7),
- EEPROM_SKU_CAP_IPAN_ENABLE = BIT(8)
-};
-
-/* radio config bits (actual values from EEPROM definition) */
-#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */
-#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */
-#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */
-#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */
-#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */
-#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
-
-
-/*
- * EEPROM bands
- * These are the channel numbers from each band in the order
- * that they are stored in the EEPROM band information. Note
- * that EEPROM bands aren't the same as mac80211 bands, and
- * there are even special "ht40 bands" in the EEPROM.
- */
-static const u8 iwl_eeprom_band_1[14] = { /* 2.4 GHz */
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
-};
-
-static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */
- 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16
-};
-
-static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */
- 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
-};
-
-static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */
- 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
-};
-
-static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */
- 145, 149, 153, 157, 161, 165
-};
-
-static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */
- 1, 2, 3, 4, 5, 6, 7
-};
-
-static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */
- 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
-};
-
-#define IWL_NUM_CHANNELS (ARRAY_SIZE(iwl_eeprom_band_1) + \
- ARRAY_SIZE(iwl_eeprom_band_2) + \
- ARRAY_SIZE(iwl_eeprom_band_3) + \
- ARRAY_SIZE(iwl_eeprom_band_4) + \
- ARRAY_SIZE(iwl_eeprom_band_5))
-
-/* rate data (static) */
-static struct ieee80211_rate iwl_cfg80211_rates[] = {
- { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
- { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
- { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
- { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
- { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
- { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
- { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
- { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
- { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
- { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
- { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
- { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
-};
-#define RATES_24_OFFS 0
-#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates)
-#define RATES_52_OFFS 4
-#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS)
-
-/* EEPROM reading functions */
-
-static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset)
-{
- if (WARN_ON(offset + sizeof(u16) > eeprom_size))
- return 0;
- return le16_to_cpup((__le16 *)(eeprom + offset));
-}
-
-static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size,
- u32 address)
-{
- u16 offset = 0;
-
- if ((address & INDIRECT_ADDRESS) == 0)
- return address;
-
- switch (address & INDIRECT_TYPE_MSK) {
- case INDIRECT_HOST:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_HOST);
- break;
- case INDIRECT_GENERAL:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_GENERAL);
- break;
- case INDIRECT_REGULATORY:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_REGULATORY);
- break;
- case INDIRECT_TXP_LIMIT:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_TXP_LIMIT);
- break;
- case INDIRECT_TXP_LIMIT_SIZE:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_TXP_LIMIT_SIZE);
- break;
- case INDIRECT_CALIBRATION:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_CALIBRATION);
- break;
- case INDIRECT_PROCESS_ADJST:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_PROCESS_ADJST);
- break;
- case INDIRECT_OTHERS:
- offset = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_LINK_OTHERS);
- break;
- default:
- WARN_ON(1);
- break;
- }
-
- /* translate the offset from words to byte */
- return (address & ADDRESS_MSK) + (offset << 1);
-}
-
-static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size,
- u32 offset)
-{
- u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset);
-
- if (WARN_ON(address >= eeprom_size))
- return NULL;
-
- return &eeprom[address];
-}
-
-static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size,
- struct iwl_nvm_data *data)
-{
- struct iwl_eeprom_calib_hdr *hdr;
-
- hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_CALIB_ALL);
- if (!hdr)
- return -ENODATA;
- data->calib_version = hdr->version;
- data->calib_voltage = hdr->voltage;
-
- return 0;
-}
-
-/**
- * enum iwl_eeprom_channel_flags - channel flags in EEPROM
- * @EEPROM_CHANNEL_VALID: channel is usable for this SKU/geo
- * @EEPROM_CHANNEL_IBSS: usable as an IBSS channel
- * @EEPROM_CHANNEL_ACTIVE: active scanning allowed
- * @EEPROM_CHANNEL_RADAR: radar detection required
- * @EEPROM_CHANNEL_WIDE: 20 MHz channel okay (?)
- * @EEPROM_CHANNEL_DFS: dynamic freq selection candidate
- */
-enum iwl_eeprom_channel_flags {
- EEPROM_CHANNEL_VALID = BIT(0),
- EEPROM_CHANNEL_IBSS = BIT(1),
- EEPROM_CHANNEL_ACTIVE = BIT(3),
- EEPROM_CHANNEL_RADAR = BIT(4),
- EEPROM_CHANNEL_WIDE = BIT(5),
- EEPROM_CHANNEL_DFS = BIT(7),
-};
-
-/**
- * struct iwl_eeprom_channel - EEPROM channel data
- * @flags: %EEPROM_CHANNEL_* flags
- * @max_power_avg: max power (in dBm) on this channel, at most 31 dBm
- */
-struct iwl_eeprom_channel {
- u8 flags;
- s8 max_power_avg;
-} __packed;
-
-
-enum iwl_eeprom_enhanced_txpwr_flags {
- IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0),
- IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1),
- IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2),
- IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3),
- IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4),
- IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5),
- IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6),
- IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7),
-};
-
-/**
- * iwl_eeprom_enhanced_txpwr structure
- * @flags: entry flags
- * @channel: channel number
- * @chain_a_max_pwr: chain a max power in 1/2 dBm
- * @chain_b_max_pwr: chain b max power in 1/2 dBm
- * @chain_c_max_pwr: chain c max power in 1/2 dBm
- * @delta_20_in_40: 20-in-40 deltas (hi/lo)
- * @mimo2_max_pwr: mimo2 max power in 1/2 dBm
- * @mimo3_max_pwr: mimo3 max power in 1/2 dBm
- *
- * This structure presents the enhanced regulatory tx power limit layout
- * in an EEPROM image.
- */
-struct iwl_eeprom_enhanced_txpwr {
- u8 flags;
- u8 channel;
- s8 chain_a_max;
- s8 chain_b_max;
- s8 chain_c_max;
- u8 delta_20_in_40;
- s8 mimo2_max;
- s8 mimo3_max;
-} __packed;
-
-static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data,
- struct iwl_eeprom_enhanced_txpwr *txp)
-{
- s8 result = 0; /* (.5 dBm) */
-
- /* Take the highest tx power from any valid chains */
- if (data->valid_tx_ant & ANT_A && txp->chain_a_max > result)
- result = txp->chain_a_max;
-
- if (data->valid_tx_ant & ANT_B && txp->chain_b_max > result)
- result = txp->chain_b_max;
-
- if (data->valid_tx_ant & ANT_C && txp->chain_c_max > result)
- result = txp->chain_c_max;
-
- if ((data->valid_tx_ant == ANT_AB ||
- data->valid_tx_ant == ANT_BC ||
- data->valid_tx_ant == ANT_AC) && txp->mimo2_max > result)
- result = txp->mimo2_max;
-
- if (data->valid_tx_ant == ANT_ABC && txp->mimo3_max > result)
- result = txp->mimo3_max;
-
- return result;
-}
-
-#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT)
-#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr)
-#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE)
-
-#define TXP_CHECK_AND_PRINT(x) \
- ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) ? # x " " : "")
-
-static void
-iwl_eeprom_enh_txp_read_element(struct iwl_nvm_data *data,
- struct iwl_eeprom_enhanced_txpwr *txp,
- int n_channels, s8 max_txpower_avg)
-{
- int ch_idx;
- enum ieee80211_band band;
-
- band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ?
- IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
-
- for (ch_idx = 0; ch_idx < n_channels; ch_idx++) {
- struct ieee80211_channel *chan = &data->channels[ch_idx];
-
- /* update matching channel or from common data only */
- if (txp->channel != 0 && chan->hw_value != txp->channel)
- continue;
-
- /* update matching band only */
- if (band != chan->band)
- continue;
-
- if (chan->max_power < max_txpower_avg &&
- !(txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ))
- chan->max_power = max_txpower_avg;
- }
-}
-
-static void iwl_eeprom_enhanced_txpower(struct device *dev,
- struct iwl_nvm_data *data,
- const u8 *eeprom, size_t eeprom_size,
- int n_channels)
-{
- struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
- int idx, entries;
- __le16 *txp_len;
- s8 max_txp_avg_halfdbm;
-
- BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8);
-
- /* the length is in 16-bit words, but we want entries */
- txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_TXP_SZ_OFFS);
- entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN;
-
- txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_TXP_OFFS);
-
- for (idx = 0; idx < entries; idx++) {
- txp = &txp_array[idx];
- /* skip invalid entries */
- if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID))
- continue;
-
- IWL_DEBUG_EEPROM(dev, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n",
- (txp->channel && (txp->flags &
- IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ?
- "Common " : (txp->channel) ?
- "Channel" : "Common",
- (txp->channel),
- TXP_CHECK_AND_PRINT(VALID),
- TXP_CHECK_AND_PRINT(BAND_52G),
- TXP_CHECK_AND_PRINT(OFDM),
- TXP_CHECK_AND_PRINT(40MHZ),
- TXP_CHECK_AND_PRINT(HT_AP),
- TXP_CHECK_AND_PRINT(RES1),
- TXP_CHECK_AND_PRINT(RES2),
- TXP_CHECK_AND_PRINT(COMMON_TYPE),
- txp->flags);
- IWL_DEBUG_EEPROM(dev,
- "\t\t chain_A: 0x%02x chain_B: 0X%02x chain_C: 0X%02x\n",
- txp->chain_a_max, txp->chain_b_max,
- txp->chain_c_max);
- IWL_DEBUG_EEPROM(dev,
- "\t\t MIMO2: 0x%02x MIMO3: 0x%02x High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n",
- txp->mimo2_max, txp->mimo3_max,
- ((txp->delta_20_in_40 & 0xf0) >> 4),
- (txp->delta_20_in_40 & 0x0f));
-
- max_txp_avg_halfdbm = iwl_get_max_txpwr_half_dbm(data, txp);
-
- iwl_eeprom_enh_txp_read_element(data, txp, n_channels,
- DIV_ROUND_UP(max_txp_avg_halfdbm, 2));
-
- if (max_txp_avg_halfdbm > data->max_tx_pwr_half_dbm)
- data->max_tx_pwr_half_dbm = max_txp_avg_halfdbm;
- }
-}
-
-static void iwl_init_band_reference(const struct iwl_cfg *cfg,
- const u8 *eeprom, size_t eeprom_size,
- int eeprom_band, int *eeprom_ch_count,
- const struct iwl_eeprom_channel **ch_info,
- const u8 **eeprom_ch_array)
-{
- u32 offset = cfg->eeprom_params->regulatory_bands[eeprom_band - 1];
-
- offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY;
-
- *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset);
-
- switch (eeprom_band) {
- case 1: /* 2.4GHz band */
- *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1);
- *eeprom_ch_array = iwl_eeprom_band_1;
- break;
- case 2: /* 4.9GHz band */
- *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2);
- *eeprom_ch_array = iwl_eeprom_band_2;
- break;
- case 3: /* 5.2GHz band */
- *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3);
- *eeprom_ch_array = iwl_eeprom_band_3;
- break;
- case 4: /* 5.5GHz band */
- *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4);
- *eeprom_ch_array = iwl_eeprom_band_4;
- break;
- case 5: /* 5.7GHz band */
- *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5);
- *eeprom_ch_array = iwl_eeprom_band_5;
- break;
- case 6: /* 2.4GHz ht40 channels */
- *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6);
- *eeprom_ch_array = iwl_eeprom_band_6;
- break;
- case 7: /* 5 GHz ht40 channels */
- *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7);
- *eeprom_ch_array = iwl_eeprom_band_7;
- break;
- default:
- *eeprom_ch_count = 0;
- *eeprom_ch_array = NULL;
- WARN_ON(1);
- }
-}
-
-#define CHECK_AND_PRINT(x) \
- ((eeprom_ch->flags & EEPROM_CHANNEL_##x) ? # x " " : "")
-
-static void iwl_mod_ht40_chan_info(struct device *dev,
- struct iwl_nvm_data *data, int n_channels,
- enum ieee80211_band band, u16 channel,
- const struct iwl_eeprom_channel *eeprom_ch,
- u8 clear_ht40_extension_channel)
-{
- struct ieee80211_channel *chan = NULL;
- int i;
-
- for (i = 0; i < n_channels; i++) {
- if (data->channels[i].band != band)
- continue;
- if (data->channels[i].hw_value != channel)
- continue;
- chan = &data->channels[i];
- break;
- }
-
- if (!chan)
- return;
-
- IWL_DEBUG_EEPROM(dev,
- "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
- channel,
- band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4",
- CHECK_AND_PRINT(IBSS),
- CHECK_AND_PRINT(ACTIVE),
- CHECK_AND_PRINT(RADAR),
- CHECK_AND_PRINT(WIDE),
- CHECK_AND_PRINT(DFS),
- eeprom_ch->flags,
- eeprom_ch->max_power_avg,
- ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) &&
- !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? ""
- : "not ");
-
- if (eeprom_ch->flags & EEPROM_CHANNEL_VALID)
- chan->flags &= ~clear_ht40_extension_channel;
-}
-
-#define CHECK_AND_PRINT_I(x) \
- ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "")
-
-static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- const u8 *eeprom, size_t eeprom_size)
-{
- int band, ch_idx;
- const struct iwl_eeprom_channel *eeprom_ch_info;
- const u8 *eeprom_ch_array;
- int eeprom_ch_count;
- int n_channels = 0;
-
- /*
- * Loop through the 5 EEPROM bands and add them to the parse list
- */
- for (band = 1; band <= 5; band++) {
- struct ieee80211_channel *channel;
-
- iwl_init_band_reference(cfg, eeprom, eeprom_size, band,
- &eeprom_ch_count, &eeprom_ch_info,
- &eeprom_ch_array);
-
- /* Loop through each band adding each of the channels */
- for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) {
- const struct iwl_eeprom_channel *eeprom_ch;
-
- eeprom_ch = &eeprom_ch_info[ch_idx];
-
- if (!(eeprom_ch->flags & EEPROM_CHANNEL_VALID)) {
- IWL_DEBUG_EEPROM(dev,
- "Ch. %d Flags %x [%sGHz] - No traffic\n",
- eeprom_ch_array[ch_idx],
- eeprom_ch_info[ch_idx].flags,
- (band != 1) ? "5.2" : "2.4");
- continue;
- }
-
- channel = &data->channels[n_channels];
- n_channels++;
-
- channel->hw_value = eeprom_ch_array[ch_idx];
- channel->band = (band == 1) ? IEEE80211_BAND_2GHZ
- : IEEE80211_BAND_5GHZ;
- channel->center_freq =
- ieee80211_channel_to_frequency(
- channel->hw_value, channel->band);
-
- /* set no-HT40, will enable as appropriate later */
- channel->flags = IEEE80211_CHAN_NO_HT40;
-
- if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS))
- channel->flags |= IEEE80211_CHAN_NO_IR;
-
- if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE))
- channel->flags |= IEEE80211_CHAN_NO_IR;
-
- if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR)
- channel->flags |= IEEE80211_CHAN_RADAR;
-
- /* Initialize regulatory-based run-time data */
- channel->max_power =
- eeprom_ch_info[ch_idx].max_power_avg;
- IWL_DEBUG_EEPROM(dev,
- "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
- channel->hw_value,
- (band != 1) ? "5.2" : "2.4",
- CHECK_AND_PRINT_I(VALID),
- CHECK_AND_PRINT_I(IBSS),
- CHECK_AND_PRINT_I(ACTIVE),
- CHECK_AND_PRINT_I(RADAR),
- CHECK_AND_PRINT_I(WIDE),
- CHECK_AND_PRINT_I(DFS),
- eeprom_ch_info[ch_idx].flags,
- eeprom_ch_info[ch_idx].max_power_avg,
- ((eeprom_ch_info[ch_idx].flags &
- EEPROM_CHANNEL_IBSS) &&
- !(eeprom_ch_info[ch_idx].flags &
- EEPROM_CHANNEL_RADAR))
- ? "" : "not ");
- }
- }
-
- if (cfg->eeprom_params->enhanced_txpower) {
- /*
- * for newer device (6000 series and up)
- * EEPROM contain enhanced tx power information
- * driver need to process addition information
- * to determine the max channel tx power limits
- */
- iwl_eeprom_enhanced_txpower(dev, data, eeprom, eeprom_size,
- n_channels);
- } else {
- /* All others use data from channel map */
- int i;
-
- data->max_tx_pwr_half_dbm = -128;
-
- for (i = 0; i < n_channels; i++)
- data->max_tx_pwr_half_dbm =
- max_t(s8, data->max_tx_pwr_half_dbm,
- data->channels[i].max_power * 2);
- }
-
- /* Check if we do have HT40 channels */
- if (cfg->eeprom_params->regulatory_bands[5] ==
- EEPROM_REGULATORY_BAND_NO_HT40 &&
- cfg->eeprom_params->regulatory_bands[6] ==
- EEPROM_REGULATORY_BAND_NO_HT40)
- return n_channels;
-
- /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */
- for (band = 6; band <= 7; band++) {
- enum ieee80211_band ieeeband;
-
- iwl_init_band_reference(cfg, eeprom, eeprom_size, band,
- &eeprom_ch_count, &eeprom_ch_info,
- &eeprom_ch_array);
-
- /* EEPROM band 6 is 2.4, band 7 is 5 GHz */
- ieeeband = (band == 6) ? IEEE80211_BAND_2GHZ
- : IEEE80211_BAND_5GHZ;
-
- /* Loop through each band adding each of the channels */
- for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) {
- /* Set up driver's info for lower half */
- iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband,
- eeprom_ch_array[ch_idx],
- &eeprom_ch_info[ch_idx],
- IEEE80211_CHAN_NO_HT40PLUS);
-
- /* Set up driver's info for upper half */
- iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband,
- eeprom_ch_array[ch_idx] + 4,
- &eeprom_ch_info[ch_idx],
- IEEE80211_CHAN_NO_HT40MINUS);
- }
- }
-
- return n_channels;
-}
-
-int iwl_init_sband_channels(struct iwl_nvm_data *data,
- struct ieee80211_supported_band *sband,
- int n_channels, enum ieee80211_band band)
-{
- struct ieee80211_channel *chan = &data->channels[0];
- int n = 0, idx = 0;
-
- while (idx < n_channels && chan->band != band)
- chan = &data->channels[++idx];
-
- sband->channels = &data->channels[idx];
-
- while (idx < n_channels && chan->band == band) {
- chan = &data->channels[++idx];
- n++;
- }
-
- sband->n_channels = n;
-
- return n;
-}
-
-#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
-#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
-
-void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- struct ieee80211_sta_ht_cap *ht_info,
- enum ieee80211_band band,
- u8 tx_chains, u8 rx_chains)
-{
- int max_bit_rate = 0;
-
- tx_chains = hweight8(tx_chains);
- if (cfg->rx_with_siso_diversity)
- rx_chains = 1;
- else
- rx_chains = hweight8(rx_chains);
-
- if (!(data->sku_cap_11n_enable) || !cfg->ht_params) {
- ht_info->ht_supported = false;
- return;
- }
-
- if (data->sku_cap_mimo_disabled)
- rx_chains = 1;
-
- ht_info->ht_supported = true;
- ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
-
- if (cfg->ht_params->stbc) {
- ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
-
- if (tx_chains > 1)
- ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
- }
-
- if (cfg->ht_params->ldpc)
- ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
-
- if (iwlwifi_mod_params.amsdu_size_8K)
- ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
-
- ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent;
- ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
-
- ht_info->mcs.rx_mask[0] = 0xFF;
- if (rx_chains >= 2)
- ht_info->mcs.rx_mask[1] = 0xFF;
- if (rx_chains >= 3)
- ht_info->mcs.rx_mask[2] = 0xFF;
-
- if (cfg->ht_params->ht_greenfield_support)
- ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
- ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
-
- max_bit_rate = MAX_BIT_RATE_20_MHZ;
-
- if (cfg->ht_params->ht40_bands & BIT(band)) {
- ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
- max_bit_rate = MAX_BIT_RATE_40_MHZ;
- }
-
- /* Highest supported Rx data rate */
- max_bit_rate *= rx_chains;
- WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
- ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
-
- /* Tx MCS capabilities */
- ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
- if (tx_chains != rx_chains) {
- ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
- ht_info->mcs.tx_params |= ((tx_chains - 1) <<
- IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
- }
-}
-
-static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- const u8 *eeprom, size_t eeprom_size)
-{
- int n_channels = iwl_init_channel_map(dev, cfg, data,
- eeprom, eeprom_size);
- int n_used = 0;
- struct ieee80211_supported_band *sband;
-
- sband = &data->bands[IEEE80211_BAND_2GHZ];
- sband->band = IEEE80211_BAND_2GHZ;
- sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
- sband->n_bitrates = N_RATES_24;
- n_used += iwl_init_sband_channels(data, sband, n_channels,
- IEEE80211_BAND_2GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
- data->valid_tx_ant, data->valid_rx_ant);
-
- sband = &data->bands[IEEE80211_BAND_5GHZ];
- sband->band = IEEE80211_BAND_5GHZ;
- sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
- sband->n_bitrates = N_RATES_52;
- n_used += iwl_init_sband_channels(data, sband, n_channels,
- IEEE80211_BAND_5GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
- data->valid_tx_ant, data->valid_rx_ant);
-
- if (n_channels != n_used)
- IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n",
- n_used, n_channels);
-}
-
-/* EEPROM data functions */
-
-struct iwl_nvm_data *
-iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
- const u8 *eeprom, size_t eeprom_size)
-{
- struct iwl_nvm_data *data;
- const void *tmp;
- u16 radio_cfg, sku;
-
- if (WARN_ON(!cfg || !cfg->eeprom_params))
- return NULL;
-
- data = kzalloc(sizeof(*data) +
- sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS,
- GFP_KERNEL);
- if (!data)
- return NULL;
-
- /* get MAC address(es) */
- tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_MAC_ADDRESS);
- if (!tmp)
- goto err_free;
- memcpy(data->hw_addr, tmp, ETH_ALEN);
- data->n_hw_addrs = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_NUM_MAC_ADDRESS);
-
- if (iwl_eeprom_read_calib(eeprom, eeprom_size, data))
- goto err_free;
-
- tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_XTAL);
- if (!tmp)
- goto err_free;
- memcpy(data->xtal_calib, tmp, sizeof(data->xtal_calib));
-
- tmp = iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_RAW_TEMPERATURE);
- if (!tmp)
- goto err_free;
- data->raw_temperature = *(__le16 *)tmp;
-
- tmp = iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_KELVIN_TEMPERATURE);
- if (!tmp)
- goto err_free;
- data->kelvin_temperature = *(__le16 *)tmp;
- data->kelvin_voltage = *((__le16 *)tmp + 1);
-
- radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_RADIO_CONFIG);
- data->radio_cfg_dash = EEPROM_RF_CFG_DASH_MSK(radio_cfg);
- data->radio_cfg_pnum = EEPROM_RF_CFG_PNUM_MSK(radio_cfg);
- data->radio_cfg_step = EEPROM_RF_CFG_STEP_MSK(radio_cfg);
- data->radio_cfg_type = EEPROM_RF_CFG_TYPE_MSK(radio_cfg);
- data->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg);
- data->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg);
-
- sku = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_SKU_CAP);
- data->sku_cap_11n_enable = sku & EEPROM_SKU_CAP_11N_ENABLE;
- data->sku_cap_amt_enable = sku & EEPROM_SKU_CAP_AMT_ENABLE;
- data->sku_cap_band_24GHz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ;
- data->sku_cap_band_52GHz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ;
- data->sku_cap_ipan_enable = sku & EEPROM_SKU_CAP_IPAN_ENABLE;
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
- data->sku_cap_11n_enable = false;
-
- data->nvm_version = iwl_eeprom_query16(eeprom, eeprom_size,
- EEPROM_VERSION);
-
- /* check overrides (some devices have wrong EEPROM) */
- if (cfg->valid_tx_ant)
- data->valid_tx_ant = cfg->valid_tx_ant;
- if (cfg->valid_rx_ant)
- data->valid_rx_ant = cfg->valid_rx_ant;
-
- if (!data->valid_tx_ant || !data->valid_rx_ant) {
- IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n",
- data->valid_tx_ant, data->valid_rx_ant);
- goto err_free;
- }
-
- iwl_init_sbands(dev, cfg, data, eeprom, eeprom_size);
-
- return data;
- err_free:
- kfree(data);
- return NULL;
-}
-IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data);
-
-/* helper functions */
-int iwl_nvm_check_version(struct iwl_nvm_data *data,
- struct iwl_trans *trans)
-{
- if (data->nvm_version >= trans->cfg->nvm_ver ||
- data->calib_version >= trans->cfg->nvm_calib_ver) {
- IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n",
- data->nvm_version, data->calib_version);
- return 0;
- }
-
- IWL_ERR(trans,
- "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
- data->nvm_version, trans->cfg->nvm_ver,
- data->calib_version, trans->cfg->nvm_calib_ver);
- return -EINVAL;
-}
-IWL_EXPORT_SYMBOL(iwl_nvm_check_version);
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
deleted file mode 100644
index 750c8c9ee70d..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-#ifndef __iwl_eeprom_parse_h__
-#define __iwl_eeprom_parse_h__
-
-#include <linux/types.h>
-#include <linux/if_ether.h>
-#include "iwl-trans.h"
-
-struct iwl_nvm_data {
- int n_hw_addrs;
- u8 hw_addr[ETH_ALEN];
-
- u8 calib_version;
- __le16 calib_voltage;
-
- __le16 raw_temperature;
- __le16 kelvin_temperature;
- __le16 kelvin_voltage;
- __le16 xtal_calib[2];
-
- bool sku_cap_band_24GHz_enable;
- bool sku_cap_band_52GHz_enable;
- bool sku_cap_11n_enable;
- bool sku_cap_11ac_enable;
- bool sku_cap_amt_enable;
- bool sku_cap_ipan_enable;
- bool sku_cap_mimo_disabled;
-
- u16 radio_cfg_type;
- u8 radio_cfg_step;
- u8 radio_cfg_dash;
- u8 radio_cfg_pnum;
- u8 valid_tx_ant, valid_rx_ant;
-
- u32 nvm_version;
- s8 max_tx_pwr_half_dbm;
-
- bool lar_enabled;
- struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
- struct ieee80211_channel channels[];
-};
-
-/**
- * iwl_parse_eeprom_data - parse EEPROM data and return values
- *
- * @dev: device pointer we're parsing for, for debug only
- * @cfg: device configuration for parsing and overrides
- * @eeprom: the EEPROM data
- * @eeprom_size: length of the EEPROM data
- *
- * This function parses all EEPROM values we need and then
- * returns a (newly allocated) struct containing all the
- * relevant values for driver use. The struct must be freed
- * later with iwl_free_nvm_data().
- */
-struct iwl_nvm_data *
-iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
- const u8 *eeprom, size_t eeprom_size);
-
-/**
- * iwl_free_nvm_data - free NVM data
- * @data: the data to free
- */
-static inline void iwl_free_nvm_data(struct iwl_nvm_data *data)
-{
- kfree(data);
-}
-
-int iwl_nvm_check_version(struct iwl_nvm_data *data,
- struct iwl_trans *trans);
-
-int iwl_init_sband_channels(struct iwl_nvm_data *data,
- struct ieee80211_supported_band *sband,
- int n_channels, enum ieee80211_band band);
-
-void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- struct ieee80211_sta_ht_cap *ht_info,
- enum ieee80211_band band,
- u8 tx_chains, u8 rx_chains);
-
-#endif /* __iwl_eeprom_parse_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
deleted file mode 100644
index 219ca8acca62..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
+++ /dev/null
@@ -1,464 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include "iwl-drv.h"
-#include "iwl-debug.h"
-#include "iwl-eeprom-read.h"
-#include "iwl-io.h"
-#include "iwl-prph.h"
-#include "iwl-csr.h"
-
-/*
- * EEPROM access time values:
- *
- * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG.
- * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1).
- * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec.
- * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG.
- */
-#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */
-
-#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */
-#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
-
-
-/*
- * The device's EEPROM semaphore prevents conflicts between driver and uCode
- * when accessing the EEPROM; each access is a series of pulses to/from the
- * EEPROM chip, not a single event, so even reads could conflict if they
- * weren't arbitrated by the semaphore.
- */
-
-#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */
-#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
-
-static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans)
-{
- u16 count;
- int ret;
-
- for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) {
- /* Request semaphore */
- iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
-
- /* See if we got it */
- ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
- CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
- EEPROM_SEM_TIMEOUT);
- if (ret >= 0) {
- IWL_DEBUG_EEPROM(trans->dev,
- "Acquired semaphore after %d tries.\n",
- count+1);
- return ret;
- }
- }
-
- return ret;
-}
-
-static void iwl_eeprom_release_semaphore(struct iwl_trans *trans)
-{
- iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
-}
-
-static int iwl_eeprom_verify_signature(struct iwl_trans *trans, bool nvm_is_otp)
-{
- u32 gp = iwl_read32(trans, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
-
- IWL_DEBUG_EEPROM(trans->dev, "EEPROM signature=0x%08x\n", gp);
-
- switch (gp) {
- case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP:
- if (!nvm_is_otp) {
- IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n",
- gp);
- return -ENOENT;
- }
- return 0;
- case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
- case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
- if (nvm_is_otp) {
- IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp);
- return -ENOENT;
- }
- return 0;
- case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP:
- default:
- IWL_ERR(trans,
- "bad EEPROM/OTP signature, type=%s, EEPROM_GP=0x%08x\n",
- nvm_is_otp ? "OTP" : "EEPROM", gp);
- return -ENOENT;
- }
-}
-
-/******************************************************************************
- *
- * OTP related functions
- *
-******************************************************************************/
-
-static void iwl_set_otp_access_absolute(struct iwl_trans *trans)
-{
- iwl_read32(trans, CSR_OTP_GP_REG);
-
- iwl_clear_bit(trans, CSR_OTP_GP_REG,
- CSR_OTP_GP_REG_OTP_ACCESS_MODE);
-}
-
-static int iwl_nvm_is_otp(struct iwl_trans *trans)
-{
- u32 otpgp;
-
- /* OTP only valid for CP/PP and after */
- switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) {
- case CSR_HW_REV_TYPE_NONE:
- IWL_ERR(trans, "Unknown hardware type\n");
- return -EIO;
- case CSR_HW_REV_TYPE_5300:
- case CSR_HW_REV_TYPE_5350:
- case CSR_HW_REV_TYPE_5100:
- case CSR_HW_REV_TYPE_5150:
- return 0;
- default:
- otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
- if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT)
- return 1;
- return 0;
- }
-}
-
-static int iwl_init_otp_access(struct iwl_trans *trans)
-{
- int ret;
-
- /* Enable 40MHz radio clock */
- iwl_write32(trans, CSR_GP_CNTRL,
- iwl_read32(trans, CSR_GP_CNTRL) |
- CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /* wait for clock to be ready */
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- 25000);
- if (ret < 0) {
- IWL_ERR(trans, "Time out access OTP\n");
- } else {
- iwl_set_bits_prph(trans, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_RESET_REQ);
- udelay(5);
- iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_RESET_REQ);
-
- /*
- * CSR auto clock gate disable bit -
- * this is only applicable for HW with OTP shadow RAM
- */
- if (trans->cfg->base_params->shadow_ram_support)
- iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
- CSR_RESET_LINK_PWR_MGMT_DISABLED);
- }
- return ret;
-}
-
-static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr,
- __le16 *eeprom_data)
-{
- int ret = 0;
- u32 r;
- u32 otpgp;
-
- iwl_write32(trans, CSR_EEPROM_REG,
- CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
- ret = iwl_poll_bit(trans, CSR_EEPROM_REG,
- CSR_EEPROM_REG_READ_VALID_MSK,
- CSR_EEPROM_REG_READ_VALID_MSK,
- IWL_EEPROM_ACCESS_TIMEOUT);
- if (ret < 0) {
- IWL_ERR(trans, "Time out reading OTP[%d]\n", addr);
- return ret;
- }
- r = iwl_read32(trans, CSR_EEPROM_REG);
- /* check for ECC errors: */
- otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
- if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
- /* stop in this case */
- /* set the uncorrectable OTP ECC bit for acknowledgment */
- iwl_set_bit(trans, CSR_OTP_GP_REG,
- CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
- IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n");
- return -EINVAL;
- }
- if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
- /* continue in this case */
- /* set the correctable OTP ECC bit for acknowledgment */
- iwl_set_bit(trans, CSR_OTP_GP_REG,
- CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
- IWL_ERR(trans, "Correctable OTP ECC error, continue read\n");
- }
- *eeprom_data = cpu_to_le16(r >> 16);
- return 0;
-}
-
-/*
- * iwl_is_otp_empty: check for empty OTP
- */
-static bool iwl_is_otp_empty(struct iwl_trans *trans)
-{
- u16 next_link_addr = 0;
- __le16 link_value;
- bool is_empty = false;
-
- /* locate the beginning of OTP link list */
- if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) {
- if (!link_value) {
- IWL_ERR(trans, "OTP is empty\n");
- is_empty = true;
- }
- } else {
- IWL_ERR(trans, "Unable to read first block of OTP list.\n");
- is_empty = true;
- }
-
- return is_empty;
-}
-
-
-/*
- * iwl_find_otp_image: find EEPROM image in OTP
- * finding the OTP block that contains the EEPROM image.
- * the last valid block on the link list (the block _before_ the last block)
- * is the block we should read and used to configure the device.
- * If all the available OTP blocks are full, the last block will be the block
- * we should read and used to configure the device.
- * only perform this operation if shadow RAM is disabled
- */
-static int iwl_find_otp_image(struct iwl_trans *trans,
- u16 *validblockaddr)
-{
- u16 next_link_addr = 0, valid_addr;
- __le16 link_value = 0;
- int usedblocks = 0;
-
- /* set addressing mode to absolute to traverse the link list */
- iwl_set_otp_access_absolute(trans);
-
- /* checking for empty OTP or error */
- if (iwl_is_otp_empty(trans))
- return -EINVAL;
-
- /*
- * start traverse link list
- * until reach the max number of OTP blocks
- * different devices have different number of OTP blocks
- */
- do {
- /* save current valid block address
- * check for more block on the link list
- */
- valid_addr = next_link_addr;
- next_link_addr = le16_to_cpu(link_value) * sizeof(u16);
- IWL_DEBUG_EEPROM(trans->dev, "OTP blocks %d addr 0x%x\n",
- usedblocks, next_link_addr);
- if (iwl_read_otp_word(trans, next_link_addr, &link_value))
- return -EINVAL;
- if (!link_value) {
- /*
- * reach the end of link list, return success and
- * set address point to the starting address
- * of the image
- */
- *validblockaddr = valid_addr;
- /* skip first 2 bytes (link list pointer) */
- *validblockaddr += 2;
- return 0;
- }
- /* more in the link list, continue */
- usedblocks++;
- } while (usedblocks <= trans->cfg->base_params->max_ll_items);
-
- /* OTP has no valid blocks */
- IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n");
- return -EINVAL;
-}
-
-/**
- * iwl_read_eeprom - read EEPROM contents
- *
- * Load the EEPROM contents from adapter and return it
- * and its size.
- *
- * NOTE: This routine uses the non-debug IO access functions.
- */
-int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
-{
- __le16 *e;
- u32 gp = iwl_read32(trans, CSR_EEPROM_GP);
- int sz;
- int ret;
- u16 addr;
- u16 validblockaddr = 0;
- u16 cache_addr = 0;
- int nvm_is_otp;
-
- if (!eeprom || !eeprom_size)
- return -EINVAL;
-
- nvm_is_otp = iwl_nvm_is_otp(trans);
- if (nvm_is_otp < 0)
- return nvm_is_otp;
-
- sz = trans->cfg->base_params->eeprom_size;
- IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz);
-
- e = kmalloc(sz, GFP_KERNEL);
- if (!e)
- return -ENOMEM;
-
- ret = iwl_eeprom_verify_signature(trans, nvm_is_otp);
- if (ret < 0) {
- IWL_ERR(trans, "EEPROM not found, EEPROM_GP=0x%08x\n", gp);
- goto err_free;
- }
-
- /* Make sure driver (instead of uCode) is allowed to read EEPROM */
- ret = iwl_eeprom_acquire_semaphore(trans);
- if (ret < 0) {
- IWL_ERR(trans, "Failed to acquire EEPROM semaphore.\n");
- goto err_free;
- }
-
- if (nvm_is_otp) {
- ret = iwl_init_otp_access(trans);
- if (ret) {
- IWL_ERR(trans, "Failed to initialize OTP access.\n");
- goto err_unlock;
- }
-
- iwl_write32(trans, CSR_EEPROM_GP,
- iwl_read32(trans, CSR_EEPROM_GP) &
- ~CSR_EEPROM_GP_IF_OWNER_MSK);
-
- iwl_set_bit(trans, CSR_OTP_GP_REG,
- CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
- CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
- /* traversing the linked list if no shadow ram supported */
- if (!trans->cfg->base_params->shadow_ram_support) {
- ret = iwl_find_otp_image(trans, &validblockaddr);
- if (ret)
- goto err_unlock;
- }
- for (addr = validblockaddr; addr < validblockaddr + sz;
- addr += sizeof(u16)) {
- __le16 eeprom_data;
-
- ret = iwl_read_otp_word(trans, addr, &eeprom_data);
- if (ret)
- goto err_unlock;
- e[cache_addr / 2] = eeprom_data;
- cache_addr += sizeof(u16);
- }
- } else {
- /* eeprom is an array of 16bit values */
- for (addr = 0; addr < sz; addr += sizeof(u16)) {
- u32 r;
-
- iwl_write32(trans, CSR_EEPROM_REG,
- CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
-
- ret = iwl_poll_bit(trans, CSR_EEPROM_REG,
- CSR_EEPROM_REG_READ_VALID_MSK,
- CSR_EEPROM_REG_READ_VALID_MSK,
- IWL_EEPROM_ACCESS_TIMEOUT);
- if (ret < 0) {
- IWL_ERR(trans,
- "Time out reading EEPROM[%d]\n", addr);
- goto err_unlock;
- }
- r = iwl_read32(trans, CSR_EEPROM_REG);
- e[addr / 2] = cpu_to_le16(r >> 16);
- }
- }
-
- IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n",
- nvm_is_otp ? "OTP" : "EEPROM");
-
- iwl_eeprom_release_semaphore(trans);
-
- *eeprom_size = sz;
- *eeprom = (u8 *)e;
- return 0;
-
- err_unlock:
- iwl_eeprom_release_semaphore(trans);
- err_free:
- kfree(e);
-
- return ret;
-}
-IWL_EXPORT_SYMBOL(iwl_read_eeprom);
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
deleted file mode 100644
index a6d3bdf82cdd..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __iwl_eeprom_h__
-#define __iwl_eeprom_h__
-
-#include "iwl-trans.h"
-
-int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size);
-
-#endif /* __iwl_eeprom_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
deleted file mode 100644
index d56064861a9c..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ /dev/null
@@ -1,535 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_fh_h__
-#define __iwl_fh_h__
-
-#include <linux/types.h>
-
-/****************************/
-/* Flow Handler Definitions */
-/****************************/
-
-/**
- * This I/O area is directly read/writable by driver (e.g. Linux uses writel())
- * Addresses are offsets from device's PCI hardware base address.
- */
-#define FH_MEM_LOWER_BOUND (0x1000)
-#define FH_MEM_UPPER_BOUND (0x2000)
-
-/**
- * Keep-Warm (KW) buffer base address.
- *
- * Driver must allocate a 4KByte buffer that is for keeping the
- * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency
- * DRAM access when doing Txing or Rxing. The dummy accesses prevent host
- * from going into a power-savings mode that would cause higher DRAM latency,
- * and possible data over/under-runs, before all Tx/Rx is complete.
- *
- * Driver loads FH_KW_MEM_ADDR_REG with the physical address (bits 35:4)
- * of the buffer, which must be 4K aligned. Once this is set up, the device
- * automatically invokes keep-warm accesses when normal accesses might not
- * be sufficient to maintain fast DRAM response.
- *
- * Bit fields:
- * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned
- */
-#define FH_KW_MEM_ADDR_REG (FH_MEM_LOWER_BOUND + 0x97C)
-
-
-/**
- * TFD Circular Buffers Base (CBBC) addresses
- *
- * Device has 16 base pointer registers, one for each of 16 host-DRAM-resident
- * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs)
- * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04
- * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte
- * aligned (address bits 0-7 must be 0).
- * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers
- * for them are in different places.
- *
- * Bit fields in each pointer register:
- * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned
- */
-#define FH_MEM_CBBC_0_15_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
-#define FH_MEM_CBBC_0_15_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10)
-#define FH_MEM_CBBC_16_19_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBF0)
-#define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
-#define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20)
-#define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80)
-
-/* Find TFD CB base pointer for given queue */
-static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
-{
- if (chnl < 16)
- return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl;
- if (chnl < 20)
- return FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16);
- WARN_ON_ONCE(chnl >= 32);
- return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20);
-}
-
-
-/**
- * Rx SRAM Control and Status Registers (RSCSR)
- *
- * These registers provide handshake between driver and device for the Rx queue
- * (this queue handles *all* command responses, notifications, Rx data, etc.
- * sent from uCode to host driver). Unlike Tx, there is only one Rx
- * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can
- * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer
- * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1
- * mapping between RBDs and RBs.
- *
- * Driver must allocate host DRAM memory for the following, and set the
- * physical address of each into device registers:
- *
- * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256
- * entries (although any power of 2, up to 4096, is selectable by driver).
- * Each entry (1 dword) points to a receive buffer (RB) of consistent size
- * (typically 4K, although 8K or 16K are also selectable by driver).
- * Driver sets up RB size and number of RBDs in the CB via Rx config
- * register FH_MEM_RCSR_CHNL0_CONFIG_REG.
- *
- * Bit fields within one RBD:
- * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned
- *
- * Driver sets physical address [35:8] of base of RBD circular buffer
- * into FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0].
- *
- * 2) Rx status buffer, 8 bytes, in which uCode indicates which Rx Buffers
- * (RBs) have been filled, via a "write pointer", actually the index of
- * the RB's corresponding RBD within the circular buffer. Driver sets
- * physical address [35:4] into FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0].
- *
- * Bit fields in lower dword of Rx status buffer (upper dword not used
- * by driver:
- * 31-12: Not used by driver
- * 11- 0: Index of last filled Rx buffer descriptor
- * (device writes, driver reads this value)
- *
- * As the driver prepares Receive Buffers (RBs) for device to fill, driver must
- * enter pointers to these RBs into contiguous RBD circular buffer entries,
- * and update the device's "write" index register,
- * FH_RSCSR_CHNL0_RBDCB_WPTR_REG.
- *
- * This "write" index corresponds to the *next* RBD that the driver will make
- * available, i.e. one RBD past the tail of the ready-to-fill RBDs within
- * the circular buffer. This value should initially be 0 (before preparing any
- * RBs), should be 8 after preparing the first 8 RBs (for example), and must
- * wrap back to 0 at the end of the circular buffer (but don't wrap before
- * "read" index has advanced past 1! See below).
- * NOTE: DEVICE EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8.
- *
- * As the device fills RBs (referenced from contiguous RBDs within the circular
- * buffer), it updates the Rx status buffer in host DRAM, 2) described above,
- * to tell the driver the index of the latest filled RBD. The driver must
- * read this "read" index from DRAM after receiving an Rx interrupt from device
- *
- * The driver must also internally keep track of a third index, which is the
- * next RBD to process. When receiving an Rx interrupt, driver should process
- * all filled but unprocessed RBs up to, but not including, the RB
- * corresponding to the "read" index. For example, if "read" index becomes "1",
- * driver may process the RB pointed to by RBD 0. Depending on volume of
- * traffic, there may be many RBs to process.
- *
- * If read index == write index, device thinks there is no room to put new data.
- * Due to this, the maximum number of filled RBs is 255, instead of 256. To
- * be safe, make sure that there is a gap of at least 2 RBDs between "write"
- * and "read" indexes; that is, make sure that there are no more than 254
- * buffers waiting to be filled.
- */
-#define FH_MEM_RSCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0)
-#define FH_MEM_RSCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
-#define FH_MEM_RSCSR_CHNL0 (FH_MEM_RSCSR_LOWER_BOUND)
-
-/**
- * Physical base address of 8-byte Rx Status buffer.
- * Bit fields:
- * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned.
- */
-#define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0)
-
-/**
- * Physical base address of Rx Buffer Descriptor Circular Buffer.
- * Bit fields:
- * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned.
- */
-#define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004)
-
-/**
- * Rx write pointer (index, really!).
- * Bit fields:
- * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1.
- * NOTE: For 256-entry circular buffer, use only bits [7:0].
- */
-#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008)
-#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG)
-
-#define FW_RSCSR_CHNL0_RXDCB_RDPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x00c)
-#define FH_RSCSR_CHNL0_RDPTR FW_RSCSR_CHNL0_RXDCB_RDPTR_REG
-
-/**
- * Rx Config/Status Registers (RCSR)
- * Rx Config Reg for channel 0 (only channel used)
- *
- * Driver must initialize FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for
- * normal operation (see bit fields).
- *
- * Clearing FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA.
- * Driver should poll FH_MEM_RSSR_RX_STATUS_REG for
- * FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing.
- *
- * Bit fields:
- * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame,
- * '10' operate normally
- * 29-24: reserved
- * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal),
- * min "5" for 32 RBDs, max "12" for 4096 RBDs.
- * 19-18: reserved
- * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K,
- * '10' 12K, '11' 16K.
- * 15-14: reserved
- * 13-12: IRQ destination; '00' none, '01' host driver (normal operation)
- * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec)
- * typical value 0x10 (about 1/2 msec)
- * 3- 0: reserved
- */
-#define FH_MEM_RCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
-#define FH_MEM_RCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xCC0)
-#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND)
-
-#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0)
-#define FH_MEM_RCSR_CHNL0_RBDCB_WPTR (FH_MEM_RCSR_CHNL0 + 0x8)
-#define FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ (FH_MEM_RCSR_CHNL0 + 0x10)
-
-#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */
-#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */
-#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */
-#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */
-#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */
-#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31*/
-
-#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20)
-#define FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4)
-#define RX_RB_TIMEOUT (0x11)
-
-#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000)
-#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000)
-#define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000)
-
-#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000)
-#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000)
-#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000)
-#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000)
-
-#define FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004)
-#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000)
-#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000)
-
-/**
- * Rx Shared Status Registers (RSSR)
- *
- * After stopping Rx DMA channel (writing 0 to
- * FH_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll
- * FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle.
- *
- * Bit fields:
- * 24: 1 = Channel 0 is idle
- *
- * FH_MEM_RSSR_SHARED_CTRL_REG and FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV
- * contain default values that should not be altered by the driver.
- */
-#define FH_MEM_RSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC40)
-#define FH_MEM_RSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xD00)
-
-#define FH_MEM_RSSR_SHARED_CTRL_REG (FH_MEM_RSSR_LOWER_BOUND)
-#define FH_MEM_RSSR_RX_STATUS_REG (FH_MEM_RSSR_LOWER_BOUND + 0x004)
-#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\
- (FH_MEM_RSSR_LOWER_BOUND + 0x008)
-
-#define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000)
-
-#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28
-#define FH_MEM_TB_MAX_LENGTH (0x00020000)
-
-/* TFDB Area - TFDs buffer table */
-#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF)
-#define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900)
-#define FH_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958)
-#define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl))
-#define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4)
-
-/**
- * Transmit DMA Channel Control/Status Registers (TCSR)
- *
- * Device has one configuration register for each of 8 Tx DMA/FIFO channels
- * supported in hardware (don't confuse these with the 16 Tx queues in DRAM,
- * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes.
- *
- * To use a Tx DMA channel, driver must initialize its
- * FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with:
- *
- * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL
- *
- * All other bits should be 0.
- *
- * Bit fields:
- * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame,
- * '10' operate normally
- * 29- 4: Reserved, set to "0"
- * 3: Enable internal DMA requests (1, normal operation), disable (0)
- * 2- 0: Reserved, set to "0"
- */
-#define FH_TCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xD00)
-#define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60)
-
-/* Find Control/Status reg for given Tx DMA/FIFO channel */
-#define FH_TCSR_CHNL_NUM (8)
-
-/* TCSR: tx_config register values */
-#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
- (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl))
-#define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \
- (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \
- (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8)
-
-#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001)
-
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008)
-
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000)
-
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000)
-
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000)
-
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003)
-
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12)
-
-/**
- * Tx Shared Status Registers (TSSR)
- *
- * After stopping Tx DMA channel (writing 0 to
- * FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll
- * FH_TSSR_TX_STATUS_REG until selected Tx channel is idle
- * (channel's buffers empty | no pending requests).
- *
- * Bit fields:
- * 31-24: 1 = Channel buffers empty (channel 7:0)
- * 23-16: 1 = No pending requests (channel 7:0)
- */
-#define FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0)
-#define FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0)
-
-#define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010)
-
-/**
- * Bit fields for TSSR(Tx Shared Status & Control) error status register:
- * 31: Indicates an address error when accessed to internal memory
- * uCode/driver must write "1" in order to clear this flag
- * 30: Indicates that Host did not send the expected number of dwords to FH
- * uCode/driver must write "1" in order to clear this flag
- * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA
- * command was received from the scheduler while the TRB was already full
- * with previous command
- * uCode/driver must write "1" in order to clear this flag
- * 7-0: Each status bit indicates a channel's TxCredit error. When an error
- * bit is set, it indicates that the FH has received a full indication
- * from the RTC TxFIFO and the current value of the TxCredit counter was
- * not equal to zero. This mean that the credit mechanism was not
- * synchronized to the TxFIFO status
- * uCode/driver must write "1" in order to clear this flag
- */
-#define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018)
-#define FH_TSSR_TX_MSG_CONFIG_REG (FH_TSSR_LOWER_BOUND + 0x008)
-
-#define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16)
-
-/* Tx service channels */
-#define FH_SRVC_CHNL (9)
-#define FH_SRVC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9C8)
-#define FH_SRVC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
-#define FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \
- (FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4)
-
-#define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98)
-#define FH_TX_TRB_REG(_chan) (FH_MEM_LOWER_BOUND + 0x958 + (_chan) * 4)
-
-/* Instruct FH to increment the retry count of a packet when
- * it is brought from the memory to TX-FIFO
- */
-#define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002)
-
-#define RX_QUEUE_SIZE 256
-#define RX_QUEUE_MASK 255
-#define RX_QUEUE_SIZE_LOG 8
-
-/**
- * struct iwl_rb_status - reserve buffer status
- * host memory mapped FH registers
- * @closed_rb_num [0:11] - Indicates the index of the RB which was closed
- * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed
- * @finished_rb_num [0:11] - Indicates the index of the current RB
- * in which the last frame was written to
- * @finished_fr_num [0:11] - Indicates the index of the RX Frame
- * which was transferred
- */
-struct iwl_rb_status {
- __le16 closed_rb_num;
- __le16 closed_fr_num;
- __le16 finished_rb_num;
- __le16 finished_fr_nam;
- __le32 __unused;
-} __packed;
-
-
-#define TFD_QUEUE_SIZE_MAX (256)
-#define TFD_QUEUE_SIZE_BC_DUP (64)
-#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP)
-#define IWL_TX_DMA_MASK DMA_BIT_MASK(36)
-#define IWL_NUM_OF_TBS 20
-
-static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr)
-{
- return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF;
-}
-/**
- * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor
- *
- * This structure contains dma address and length of transmission address
- *
- * @lo: low [31:0] portion of the dma address of TX buffer
- * every even is unaligned on 16 bit boundary
- * @hi_n_len 0-3 [35:32] portion of dma
- * 4-15 length of the tx buffer
- */
-struct iwl_tfd_tb {
- __le32 lo;
- __le16 hi_n_len;
-} __packed;
-
-/**
- * struct iwl_tfd
- *
- * Transmit Frame Descriptor (TFD)
- *
- * @ __reserved1[3] reserved
- * @ num_tbs 0-4 number of active tbs
- * 5 reserved
- * 6-7 padding (not used)
- * @ tbs[20] transmit frame buffer descriptors
- * @ __pad padding
- *
- * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM.
- * Both driver and device share these circular buffers, each of which must be
- * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes
- *
- * Driver must indicate the physical address of the base of each
- * circular buffer via the FH_MEM_CBBC_QUEUE registers.
- *
- * Each TFD contains pointer/size information for up to 20 data buffers
- * in host DRAM. These buffers collectively contain the (one) frame described
- * by the TFD. Each buffer must be a single contiguous block of memory within
- * itself, but buffers may be scattered in host DRAM. Each buffer has max size
- * of (4K - 4). The concatenates all of a TFD's buffers into a single
- * Tx frame, up to 8 KBytes in size.
- *
- * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx.
- */
-struct iwl_tfd {
- u8 __reserved1[3];
- u8 num_tbs;
- struct iwl_tfd_tb tbs[IWL_NUM_OF_TBS];
- __le32 __pad;
-} __packed;
-
-/* Keep Warm Size */
-#define IWL_KW_SIZE 0x1000 /* 4k */
-
-/* Fixed (non-configurable) rx data from phy */
-
-/**
- * struct iwlagn_schedq_bc_tbl scheduler byte count table
- * base physical address provided by SCD_DRAM_BASE_ADDR
- * @tfd_offset 0-12 - tx command byte count
- * 12-16 - station index
- */
-struct iwlagn_scd_bc_tbl {
- __le16 tfd_offset[TFD_QUEUE_BC_SIZE];
-} __packed;
-
-#endif /* !__iwl_fh_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
deleted file mode 100644
index 9dbe19cbb4dd..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ /dev/null
@@ -1,320 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __fw_error_dump_h__
-#define __fw_error_dump_h__
-
-#include <linux/types.h>
-
-#define IWL_FW_ERROR_DUMP_BARKER 0x14789632
-
-/**
- * enum iwl_fw_error_dump_type - types of data in the dump file
- * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0
- * @IWL_FW_ERROR_DUMP_RXF:
- * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
- * &struct iwl_fw_error_dump_txcmd packets
- * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info
- * info on the device / firmware.
- * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
- * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
- * sections like this in a single file.
- * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
- * @IWL_FW_ERROR_DUMP_MEM: chunk of memory
- * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump.
- * Structured as &struct iwl_fw_error_dump_trigger_desc.
- * @IWL_FW_ERROR_DUMP_RB: the content of an RB structured as
- * &struct iwl_fw_error_dump_rb
- * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were
- * paged to the DRAM.
- */
-enum iwl_fw_error_dump_type {
- /* 0 is deprecated */
- IWL_FW_ERROR_DUMP_CSR = 1,
- IWL_FW_ERROR_DUMP_RXF = 2,
- IWL_FW_ERROR_DUMP_TXCMD = 3,
- IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
- IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
- IWL_FW_ERROR_DUMP_PRPH = 6,
- IWL_FW_ERROR_DUMP_TXF = 7,
- IWL_FW_ERROR_DUMP_FH_REGS = 8,
- IWL_FW_ERROR_DUMP_MEM = 9,
- IWL_FW_ERROR_DUMP_ERROR_INFO = 10,
- IWL_FW_ERROR_DUMP_RB = 11,
- IWL_FW_ERROR_DUMP_PAGING = 12,
-
- IWL_FW_ERROR_DUMP_MAX,
-};
-
-/**
- * struct iwl_fw_error_dump_data - data for one type
- * @type: %enum iwl_fw_error_dump_type
- * @len: the length starting from %data
- * @data: the data itself
- */
-struct iwl_fw_error_dump_data {
- __le32 type;
- __le32 len;
- __u8 data[];
-} __packed;
-
-/**
- * struct iwl_fw_error_dump_file - the layout of the header of the file
- * @barker: must be %IWL_FW_ERROR_DUMP_BARKER
- * @file_len: the length of all the file starting from %barker
- * @data: array of %struct iwl_fw_error_dump_data
- */
-struct iwl_fw_error_dump_file {
- __le32 barker;
- __le32 file_len;
- u8 data[0];
-} __packed;
-
-/**
- * struct iwl_fw_error_dump_txcmd - TX command data
- * @cmdlen: original length of command
- * @caplen: captured length of command (may be less)
- * @data: captured command data, @caplen bytes
- */
-struct iwl_fw_error_dump_txcmd {
- __le32 cmdlen;
- __le32 caplen;
- u8 data[];
-} __packed;
-
-/**
- * struct iwl_fw_error_dump_fifo - RX/TX FIFO data
- * @fifo_num: number of FIFO (starting from 0)
- * @available_bytes: num of bytes available in FIFO (may be less than FIFO size)
- * @wr_ptr: position of write pointer
- * @rd_ptr: position of read pointer
- * @fence_ptr: position of fence pointer
- * @fence_mode: the current mode of the fence (before locking) -
- * 0=follow RD pointer ; 1 = freeze
- * @data: all of the FIFO's data
- */
-struct iwl_fw_error_dump_fifo {
- __le32 fifo_num;
- __le32 available_bytes;
- __le32 wr_ptr;
- __le32 rd_ptr;
- __le32 fence_ptr;
- __le32 fence_mode;
- u8 data[];
-} __packed;
-
-enum iwl_fw_error_dump_family {
- IWL_FW_ERROR_DUMP_FAMILY_7 = 7,
- IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
-};
-
-/**
- * struct iwl_fw_error_dump_info - info on the device / firmware
- * @device_family: the family of the device (7 / 8)
- * @hw_step: the step of the device
- * @fw_human_readable: human readable FW version
- * @dev_human_readable: name of the device
- * @bus_human_readable: name of the bus used
- */
-struct iwl_fw_error_dump_info {
- __le32 device_family;
- __le32 hw_step;
- u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ];
- u8 dev_human_readable[64];
- u8 bus_human_readable[8];
-} __packed;
-
-/**
- * struct iwl_fw_error_dump_fw_mon - FW monitor data
- * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
- * @fw_mon_base_ptr: base pointer of the data
- * @fw_mon_cycle_cnt: number of wraparounds
- * @reserved: for future use
- * @data: captured data
- */
-struct iwl_fw_error_dump_fw_mon {
- __le32 fw_mon_wr_ptr;
- __le32 fw_mon_base_ptr;
- __le32 fw_mon_cycle_cnt;
- __le32 reserved[3];
- u8 data[];
-} __packed;
-
-/**
- * struct iwl_fw_error_dump_prph - periphery registers data
- * @prph_start: address of the first register in this chunk
- * @data: the content of the registers
- */
-struct iwl_fw_error_dump_prph {
- __le32 prph_start;
- __le32 data[];
-};
-
-enum iwl_fw_error_dump_mem_type {
- IWL_FW_ERROR_DUMP_MEM_SRAM,
- IWL_FW_ERROR_DUMP_MEM_SMEM,
-};
-
-/**
- * struct iwl_fw_error_dump_mem - chunk of memory
- * @type: %enum iwl_fw_error_dump_mem_type
- * @offset: the offset from which the memory was read
- * @data: the content of the memory
- */
-struct iwl_fw_error_dump_mem {
- __le32 type;
- __le32 offset;
- u8 data[];
-};
-
-/**
- * struct iwl_fw_error_dump_rb - content of an Receive Buffer
- * @index: the index of the Receive Buffer in the Rx queue
- * @rxq: the RB's Rx queue
- * @reserved:
- * @data: the content of the Receive Buffer
- */
-struct iwl_fw_error_dump_rb {
- __le32 index;
- __le32 rxq;
- __le32 reserved;
- u8 data[];
-};
-
-/**
- * struct iwl_fw_error_dump_paging - content of the UMAC's image page
- * block on DRAM
- * @index: the index of the page block
- * @reserved:
- * @data: the content of the page block
- */
-struct iwl_fw_error_dump_paging {
- __le32 index;
- __le32 reserved;
- u8 data[];
-};
-
-/**
- * iwl_fw_error_next_data - advance fw error dump data pointer
- * @data: previous data block
- * Returns: next data block
- */
-static inline struct iwl_fw_error_dump_data *
-iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
-{
- return (void *)(data->data + le32_to_cpu(data->len));
-}
-
-/**
- * enum iwl_fw_dbg_trigger - triggers available
- *
- * @FW_DBG_TRIGGER_USER: trigger log collection by user
- * This should not be defined as a trigger to the driver, but a value the
- * driver should set to indicate that the trigger was initiated by the
- * user.
- * @FW_DBG_TRIGGER_FW_ASSERT: trigger log collection when the firmware asserts
- * @FW_DBG_TRIGGER_MISSED_BEACONS: trigger log collection when beacons are
- * missed.
- * @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch.
- * @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a
- * command response or a notification.
- * @FW_DBG_TRIGGER_MLME: trigger log collection upon MLME event.
- * @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold.
- * @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon
- * goes below a threshold.
- * @FW_DBG_TRIGGER_TXQ_TIMERS: configures the timers for the Tx queue hang
- * detection.
- * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related
- * events.
- * @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events.
- */
-enum iwl_fw_dbg_trigger {
- FW_DBG_TRIGGER_INVALID = 0,
- FW_DBG_TRIGGER_USER,
- FW_DBG_TRIGGER_FW_ASSERT,
- FW_DBG_TRIGGER_MISSED_BEACONS,
- FW_DBG_TRIGGER_CHANNEL_SWITCH,
- FW_DBG_TRIGGER_FW_NOTIF,
- FW_DBG_TRIGGER_MLME,
- FW_DBG_TRIGGER_STATS,
- FW_DBG_TRIGGER_RSSI,
- FW_DBG_TRIGGER_TXQ_TIMERS,
- FW_DBG_TRIGGER_TIME_EVENT,
- FW_DBG_TRIGGER_BA,
-
- /* must be last */
- FW_DBG_TRIGGER_MAX,
-};
-
-/**
- * struct iwl_fw_error_dump_trigger_desc - describes the trigger condition
- * @type: %enum iwl_fw_dbg_trigger
- * @data: raw data about what happened
- */
-struct iwl_fw_error_dump_trigger_desc {
- __le32 type;
- u8 data[];
-};
-
-#endif /* __fw_error_dump_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
deleted file mode 100644
index 08303db0000f..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ /dev/null
@@ -1,768 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __iwl_fw_file_h__
-#define __iwl_fw_file_h__
-
-#include <linux/netdevice.h>
-#include <linux/nl80211.h>
-
-/* v1/v2 uCode file layout */
-struct iwl_ucode_header {
- __le32 ver; /* major/minor/API/serial */
- union {
- struct {
- __le32 inst_size; /* bytes of runtime code */
- __le32 data_size; /* bytes of runtime data */
- __le32 init_size; /* bytes of init code */
- __le32 init_data_size; /* bytes of init data */
- __le32 boot_size; /* bytes of bootstrap code */
- u8 data[0]; /* in same order as sizes */
- } v1;
- struct {
- __le32 build; /* build number */
- __le32 inst_size; /* bytes of runtime code */
- __le32 data_size; /* bytes of runtime data */
- __le32 init_size; /* bytes of init code */
- __le32 init_data_size; /* bytes of init data */
- __le32 boot_size; /* bytes of bootstrap code */
- u8 data[0]; /* in same order as sizes */
- } v2;
- } u;
-};
-
-/*
- * new TLV uCode file layout
- *
- * The new TLV file format contains TLVs, that each specify
- * some piece of data.
- */
-
-enum iwl_ucode_tlv_type {
- IWL_UCODE_TLV_INVALID = 0, /* unused */
- IWL_UCODE_TLV_INST = 1,
- IWL_UCODE_TLV_DATA = 2,
- IWL_UCODE_TLV_INIT = 3,
- IWL_UCODE_TLV_INIT_DATA = 4,
- IWL_UCODE_TLV_BOOT = 5,
- IWL_UCODE_TLV_PROBE_MAX_LEN = 6, /* a u32 value */
- IWL_UCODE_TLV_PAN = 7,
- IWL_UCODE_TLV_RUNT_EVTLOG_PTR = 8,
- IWL_UCODE_TLV_RUNT_EVTLOG_SIZE = 9,
- IWL_UCODE_TLV_RUNT_ERRLOG_PTR = 10,
- IWL_UCODE_TLV_INIT_EVTLOG_PTR = 11,
- IWL_UCODE_TLV_INIT_EVTLOG_SIZE = 12,
- IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13,
- IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14,
- IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15,
- IWL_UCODE_TLV_WOWLAN_INST = 16,
- IWL_UCODE_TLV_WOWLAN_DATA = 17,
- IWL_UCODE_TLV_FLAGS = 18,
- IWL_UCODE_TLV_SEC_RT = 19,
- IWL_UCODE_TLV_SEC_INIT = 20,
- IWL_UCODE_TLV_SEC_WOWLAN = 21,
- IWL_UCODE_TLV_DEF_CALIB = 22,
- IWL_UCODE_TLV_PHY_SKU = 23,
- IWL_UCODE_TLV_SECURE_SEC_RT = 24,
- IWL_UCODE_TLV_SECURE_SEC_INIT = 25,
- IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
- IWL_UCODE_TLV_NUM_OF_CPU = 27,
- IWL_UCODE_TLV_CSCHEME = 28,
- IWL_UCODE_TLV_API_CHANGES_SET = 29,
- IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
- IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
- IWL_UCODE_TLV_PAGING = 32,
- IWL_UCODE_TLV_SEC_RT_USNIFFER = 34,
- IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35,
- IWL_UCODE_TLV_FW_VERSION = 36,
- IWL_UCODE_TLV_FW_DBG_DEST = 38,
- IWL_UCODE_TLV_FW_DBG_CONF = 39,
- IWL_UCODE_TLV_FW_DBG_TRIGGER = 40,
- IWL_UCODE_TLV_FW_GSCAN_CAPA = 50,
-};
-
-struct iwl_ucode_tlv {
- __le32 type; /* see above */
- __le32 length; /* not including type/length fields */
- u8 data[0];
-};
-
-#define IWL_TLV_UCODE_MAGIC 0x0a4c5749
-#define FW_VER_HUMAN_READABLE_SZ 64
-
-struct iwl_tlv_ucode_header {
- /*
- * The TLV style ucode header is distinguished from
- * the v1/v2 style header by first four bytes being
- * zero, as such is an invalid combination of
- * major/minor/API/serial versions.
- */
- __le32 zero;
- __le32 magic;
- u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
- /* major/minor/API/serial or major in new format */
- __le32 ver;
- __le32 build;
- __le64 ignore;
- /*
- * The data contained herein has a TLV layout,
- * see above for the TLV header and types.
- * Note that each TLV is padded to a length
- * that is a multiple of 4 for alignment.
- */
- u8 data[0];
-};
-
-/*
- * ucode TLVs
- *
- * ability to get extension for: flags & capabilities from ucode binaries files
- */
-struct iwl_ucode_api {
- __le32 api_index;
- __le32 api_flags;
-} __packed;
-
-struct iwl_ucode_capa {
- __le32 api_index;
- __le32 api_capa;
-} __packed;
-
-/**
- * enum iwl_ucode_tlv_flag - ucode API flags
- * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
- * was a separate TLV but moved here to save space.
- * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behavior on hidden SSID,
- * treats good CRC threshold as a boolean
- * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
- * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
- * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
- * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
- * offload profile config command.
- * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
- * (rather than two) IPv6 addresses
- * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
- * from the probe request template.
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
- * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
- * P2P client interfaces simultaneously if they are in different bindings.
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
- * P2P client interfaces simultaneously if they are in same bindings.
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
- * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
- * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
- * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
- * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
- */
-enum iwl_ucode_tlv_flag {
- IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
- IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1),
- IWL_UCODE_TLV_FLAGS_MFP = BIT(2),
- IWL_UCODE_TLV_FLAGS_P2P = BIT(3),
- IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4),
- IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7),
- IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10),
- IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12),
- IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15),
- IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16),
- IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21),
- IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22),
- IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23),
- IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24),
- IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25),
- IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26),
- IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29),
- IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30),
-};
-
-typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t;
-
-/**
- * enum iwl_ucode_tlv_api - ucode api
- * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
- * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
- * longer than the passive one, which is essential for fragmented scan.
- * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source.
- * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header
- * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params
- * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format
- * @IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority
- * instead of 3.
- * @IWL_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size
- * (command version 3) that supports per-chain limits
- *
- * @NUM_IWL_UCODE_TLV_API: number of bits used
- */
-enum iwl_ucode_tlv_api {
- IWL_UCODE_TLV_API_BT_COEX_SPLIT = (__force iwl_ucode_tlv_api_t)3,
- IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8,
- IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9,
- IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14,
- IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18,
- IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20,
- IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY = (__force iwl_ucode_tlv_api_t)24,
- IWL_UCODE_TLV_API_TX_POWER_CHAIN = (__force iwl_ucode_tlv_api_t)27,
-
- NUM_IWL_UCODE_TLV_API
-#ifdef __CHECKER__
- /* sparse says it cannot increment the previous enum member */
- = 128
-#endif
-};
-
-typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t;
-
-/**
- * enum iwl_ucode_tlv_capa - ucode capabilities
- * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
- * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory
- * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan.
- * @IWL_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer
- * @IWL_UCODE_TLV_CAPA_TOF_SUPPORT: supports Time of Flight (802.11mc FTM)
- * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality
- * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
- * tx power value into TPC Report action frame and Link Measurement Report
- * action frame
- * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current
- * channel in DS parameter set element in probe requests.
- * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
- * probe requests.
- * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
- * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
- * which also implies support for the scheduler configuration command
- * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
- * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
- * @IWL_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command
- * @IWL_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload
- * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics
- * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running
- * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different
- * sources for the MCC. This TLV bit is a future replacement to
- * IWL_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR
- * is supported.
- * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC
- * @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan
- * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
- * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
- *
- * @NUM_IWL_UCODE_TLV_CAPA: number of bits used
- */
-enum iwl_ucode_tlv_capa {
- IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = (__force iwl_ucode_tlv_capa_t)0,
- IWL_UCODE_TLV_CAPA_LAR_SUPPORT = (__force iwl_ucode_tlv_capa_t)1,
- IWL_UCODE_TLV_CAPA_UMAC_SCAN = (__force iwl_ucode_tlv_capa_t)2,
- IWL_UCODE_TLV_CAPA_BEAMFORMER = (__force iwl_ucode_tlv_capa_t)3,
- IWL_UCODE_TLV_CAPA_TOF_SUPPORT = (__force iwl_ucode_tlv_capa_t)5,
- IWL_UCODE_TLV_CAPA_TDLS_SUPPORT = (__force iwl_ucode_tlv_capa_t)6,
- IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = (__force iwl_ucode_tlv_capa_t)8,
- IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)9,
- IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)10,
- IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = (__force iwl_ucode_tlv_capa_t)11,
- IWL_UCODE_TLV_CAPA_DQA_SUPPORT = (__force iwl_ucode_tlv_capa_t)12,
- IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = (__force iwl_ucode_tlv_capa_t)13,
- IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = (__force iwl_ucode_tlv_capa_t)18,
- IWL_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = (__force iwl_ucode_tlv_capa_t)19,
- IWL_UCODE_TLV_CAPA_CSUM_SUPPORT = (__force iwl_ucode_tlv_capa_t)21,
- IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = (__force iwl_ucode_tlv_capa_t)22,
- IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = (__force iwl_ucode_tlv_capa_t)28,
- IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = (__force iwl_ucode_tlv_capa_t)29,
- IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30,
- IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31,
- IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64,
- IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65,
-
- NUM_IWL_UCODE_TLV_CAPA
-#ifdef __CHECKER__
- /* sparse says it cannot increment the previous enum member */
- = 128
-#endif
-};
-
-/* The default calibrate table size if not specified by firmware file */
-#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18
-#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19
-#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253
-
-/* The default max probe length if not specified by the firmware file */
-#define IWL_DEFAULT_MAX_PROBE_LENGTH 200
-
-/*
- * For 16.0 uCode and above, there is no differentiation between sections,
- * just an offset to the HW address.
- */
-#define IWL_UCODE_SECTION_MAX 16
-#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC
-#define PAGING_SEPARATOR_SECTION 0xAAAABBBB
-
-/* uCode version contains 4 values: Major/Minor/API/Serial */
-#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24)
-#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16)
-#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8)
-#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF)
-
-/*
- * Calibration control struct.
- * Sent as part of the phy configuration command.
- * @flow_trigger: bitmap for which calibrations to perform according to
- * flow triggers.
- * @event_trigger: bitmap for which calibrations to perform according to
- * event triggers.
- */
-struct iwl_tlv_calib_ctrl {
- __le32 flow_trigger;
- __le32 event_trigger;
-} __packed;
-
-enum iwl_fw_phy_cfg {
- FW_PHY_CFG_RADIO_TYPE_POS = 0,
- FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
- FW_PHY_CFG_RADIO_STEP_POS = 2,
- FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
- FW_PHY_CFG_RADIO_DASH_POS = 4,
- FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
- FW_PHY_CFG_TX_CHAIN_POS = 16,
- FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
- FW_PHY_CFG_RX_CHAIN_POS = 20,
- FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
-};
-
-#define IWL_UCODE_MAX_CS 1
-
-/**
- * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
- * @cipher: a cipher suite selector
- * @flags: cipher scheme flags (currently reserved for a future use)
- * @hdr_len: a size of MPDU security header
- * @pn_len: a size of PN
- * @pn_off: an offset of pn from the beginning of the security header
- * @key_idx_off: an offset of key index byte in the security header
- * @key_idx_mask: a bit mask of key_idx bits
- * @key_idx_shift: bit shift needed to get key_idx
- * @mic_len: mic length in bytes
- * @hw_cipher: a HW cipher index used in host commands
- */
-struct iwl_fw_cipher_scheme {
- __le32 cipher;
- u8 flags;
- u8 hdr_len;
- u8 pn_len;
- u8 pn_off;
- u8 key_idx_off;
- u8 key_idx_mask;
- u8 key_idx_shift;
- u8 mic_len;
- u8 hw_cipher;
-} __packed;
-
-enum iwl_fw_dbg_reg_operator {
- CSR_ASSIGN,
- CSR_SETBIT,
- CSR_CLEARBIT,
-
- PRPH_ASSIGN,
- PRPH_SETBIT,
- PRPH_CLEARBIT,
-
- INDIRECT_ASSIGN,
- INDIRECT_SETBIT,
- INDIRECT_CLEARBIT,
-
- PRPH_BLOCKBIT,
-};
-
-/**
- * struct iwl_fw_dbg_reg_op - an operation on a register
- *
- * @op: %enum iwl_fw_dbg_reg_operator
- * @addr: offset of the register
- * @val: value
- */
-struct iwl_fw_dbg_reg_op {
- u8 op;
- u8 reserved[3];
- __le32 addr;
- __le32 val;
-} __packed;
-
-/**
- * enum iwl_fw_dbg_monitor_mode - available monitor recording modes
- *
- * @SMEM_MODE: monitor stores the data in SMEM
- * @EXTERNAL_MODE: monitor stores the data in allocated DRAM
- * @MARBH_MODE: monitor stores the data in MARBH buffer
- * @MIPI_MODE: monitor outputs the data through the MIPI interface
- */
-enum iwl_fw_dbg_monitor_mode {
- SMEM_MODE = 0,
- EXTERNAL_MODE = 1,
- MARBH_MODE = 2,
- MIPI_MODE = 3,
-};
-
-/**
- * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
- *
- * @version: version of the TLV - currently 0
- * @monitor_mode: %enum iwl_fw_dbg_monitor_mode
- * @size_power: buffer size will be 2^(size_power + 11)
- * @base_reg: addr of the base addr register (PRPH)
- * @end_reg: addr of the end addr register (PRPH)
- * @write_ptr_reg: the addr of the reg of the write pointer
- * @wrap_count: the addr of the reg of the wrap_count
- * @base_shift: shift right of the base addr reg
- * @end_shift: shift right of the end addr reg
- * @reg_ops: array of registers operations
- *
- * This parses IWL_UCODE_TLV_FW_DBG_DEST
- */
-struct iwl_fw_dbg_dest_tlv {
- u8 version;
- u8 monitor_mode;
- u8 size_power;
- u8 reserved;
- __le32 base_reg;
- __le32 end_reg;
- __le32 write_ptr_reg;
- __le32 wrap_count;
- u8 base_shift;
- u8 end_shift;
- struct iwl_fw_dbg_reg_op reg_ops[0];
-} __packed;
-
-struct iwl_fw_dbg_conf_hcmd {
- u8 id;
- u8 reserved;
- __le16 len;
- u8 data[0];
-} __packed;
-
-/**
- * enum iwl_fw_dbg_trigger_mode - triggers functionalities
- *
- * @IWL_FW_DBG_TRIGGER_START: when trigger occurs re-conf the dbg mechanism
- * @IWL_FW_DBG_TRIGGER_STOP: when trigger occurs pull the dbg data
- * @IWL_FW_DBG_TRIGGER_MONITOR_ONLY: when trigger occurs trigger is set to
- * collect only monitor data
- */
-enum iwl_fw_dbg_trigger_mode {
- IWL_FW_DBG_TRIGGER_START = BIT(0),
- IWL_FW_DBG_TRIGGER_STOP = BIT(1),
- IWL_FW_DBG_TRIGGER_MONITOR_ONLY = BIT(2),
-};
-
-/**
- * enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger
- * @IWL_FW_DBG_CONF_VIF_ANY: any vif type
- * @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode
- * @IWL_FW_DBG_CONF_VIF_STATION: BSS mode
- * @IWL_FW_DBG_CONF_VIF_AP: AP mode
- * @IWL_FW_DBG_CONF_VIF_P2P_CLIENT: P2P Client mode
- * @IWL_FW_DBG_CONF_VIF_P2P_GO: P2P GO mode
- * @IWL_FW_DBG_CONF_VIF_P2P_DEVICE: P2P device
- */
-enum iwl_fw_dbg_trigger_vif_type {
- IWL_FW_DBG_CONF_VIF_ANY = NL80211_IFTYPE_UNSPECIFIED,
- IWL_FW_DBG_CONF_VIF_IBSS = NL80211_IFTYPE_ADHOC,
- IWL_FW_DBG_CONF_VIF_STATION = NL80211_IFTYPE_STATION,
- IWL_FW_DBG_CONF_VIF_AP = NL80211_IFTYPE_AP,
- IWL_FW_DBG_CONF_VIF_P2P_CLIENT = NL80211_IFTYPE_P2P_CLIENT,
- IWL_FW_DBG_CONF_VIF_P2P_GO = NL80211_IFTYPE_P2P_GO,
- IWL_FW_DBG_CONF_VIF_P2P_DEVICE = NL80211_IFTYPE_P2P_DEVICE,
-};
-
-/**
- * struct iwl_fw_dbg_trigger_tlv - a TLV that describes the trigger
- * @id: %enum iwl_fw_dbg_trigger
- * @vif_type: %enum iwl_fw_dbg_trigger_vif_type
- * @stop_conf_ids: bitmap of configurations this trigger relates to.
- * if the mode is %IWL_FW_DBG_TRIGGER_STOP, then if the bit corresponding
- * to the currently running configuration is set, the data should be
- * collected.
- * @stop_delay: how many milliseconds to wait before collecting the data
- * after the STOP trigger fires.
- * @mode: %enum iwl_fw_dbg_trigger_mode - can be stop / start of both
- * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what
- * configuration should be applied when the triggers kicks in.
- * @occurrences: number of occurrences. 0 means the trigger will never fire.
- */
-struct iwl_fw_dbg_trigger_tlv {
- __le32 id;
- __le32 vif_type;
- __le32 stop_conf_ids;
- __le32 stop_delay;
- u8 mode;
- u8 start_conf_id;
- __le16 occurrences;
- __le32 reserved[2];
-
- u8 data[0];
-} __packed;
-
-#define FW_DBG_START_FROM_ALIVE 0
-#define FW_DBG_CONF_MAX 32
-#define FW_DBG_INVALID 0xff
-
-/**
- * struct iwl_fw_dbg_trigger_missed_bcon - configures trigger for missed beacons
- * @stop_consec_missed_bcon: stop recording if threshold is crossed.
- * @stop_consec_missed_bcon_since_rx: stop recording if threshold is crossed.
- * @start_consec_missed_bcon: start recording if threshold is crossed.
- * @start_consec_missed_bcon_since_rx: start recording if threshold is crossed.
- * @reserved1: reserved
- * @reserved2: reserved
- */
-struct iwl_fw_dbg_trigger_missed_bcon {
- __le32 stop_consec_missed_bcon;
- __le32 stop_consec_missed_bcon_since_rx;
- __le32 reserved2[2];
- __le32 start_consec_missed_bcon;
- __le32 start_consec_missed_bcon_since_rx;
- __le32 reserved1[2];
-} __packed;
-
-/**
- * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW.
- * cmds: the list of commands to trigger the collection on
- */
-struct iwl_fw_dbg_trigger_cmd {
- struct cmd {
- u8 cmd_id;
- u8 group_id;
- } __packed cmds[16];
-} __packed;
-
-/**
- * iwl_fw_dbg_trigger_stats - configures trigger for statistics
- * @stop_offset: the offset of the value to be monitored
- * @stop_threshold: the threshold above which to collect
- * @start_offset: the offset of the value to be monitored
- * @start_threshold: the threshold above which to start recording
- */
-struct iwl_fw_dbg_trigger_stats {
- __le32 stop_offset;
- __le32 stop_threshold;
- __le32 start_offset;
- __le32 start_threshold;
-} __packed;
-
-/**
- * struct iwl_fw_dbg_trigger_low_rssi - trigger for low beacon RSSI
- * @rssi: RSSI value to trigger at
- */
-struct iwl_fw_dbg_trigger_low_rssi {
- __le32 rssi;
-} __packed;
-
-/**
- * struct iwl_fw_dbg_trigger_mlme - configures trigger for mlme events
- * @stop_auth_denied: number of denied authentication to collect
- * @stop_auth_timeout: number of authentication timeout to collect
- * @stop_rx_deauth: number of Rx deauth before to collect
- * @stop_tx_deauth: number of Tx deauth before to collect
- * @stop_assoc_denied: number of denied association to collect
- * @stop_assoc_timeout: number of association timeout to collect
- * @stop_connection_loss: number of connection loss to collect
- * @start_auth_denied: number of denied authentication to start recording
- * @start_auth_timeout: number of authentication timeout to start recording
- * @start_rx_deauth: number of Rx deauth to start recording
- * @start_tx_deauth: number of Tx deauth to start recording
- * @start_assoc_denied: number of denied association to start recording
- * @start_assoc_timeout: number of association timeout to start recording
- * @start_connection_loss: number of connection loss to start recording
- */
-struct iwl_fw_dbg_trigger_mlme {
- u8 stop_auth_denied;
- u8 stop_auth_timeout;
- u8 stop_rx_deauth;
- u8 stop_tx_deauth;
-
- u8 stop_assoc_denied;
- u8 stop_assoc_timeout;
- u8 stop_connection_loss;
- u8 reserved;
-
- u8 start_auth_denied;
- u8 start_auth_timeout;
- u8 start_rx_deauth;
- u8 start_tx_deauth;
-
- u8 start_assoc_denied;
- u8 start_assoc_timeout;
- u8 start_connection_loss;
- u8 reserved2;
-} __packed;
-
-/**
- * struct iwl_fw_dbg_trigger_txq_timer - configures the Tx queue's timer
- * @command_queue: timeout for the command queue in ms
- * @bss: timeout for the queues of a BSS (except for TDLS queues) in ms
- * @softap: timeout for the queues of a softAP in ms
- * @p2p_go: timeout for the queues of a P2P GO in ms
- * @p2p_client: timeout for the queues of a P2P client in ms
- * @p2p_device: timeout for the queues of a P2P device in ms
- * @ibss: timeout for the queues of an IBSS in ms
- * @tdls: timeout for the queues of a TDLS station in ms
- */
-struct iwl_fw_dbg_trigger_txq_timer {
- __le32 command_queue;
- __le32 bss;
- __le32 softap;
- __le32 p2p_go;
- __le32 p2p_client;
- __le32 p2p_device;
- __le32 ibss;
- __le32 tdls;
- __le32 reserved[4];
-} __packed;
-
-/**
- * struct iwl_fw_dbg_trigger_time_event - configures a time event trigger
- * time_Events: a list of tuples <id, action_bitmap>. The driver will issue a
- * trigger each time a time event notification that relates to time event
- * id with one of the actions in the bitmap is received and
- * BIT(notif->status) is set in status_bitmap.
- *
- */
-struct iwl_fw_dbg_trigger_time_event {
- struct {
- __le32 id;
- __le32 action_bitmap;
- __le32 status_bitmap;
- } __packed time_events[16];
-} __packed;
-
-/**
- * struct iwl_fw_dbg_trigger_ba - configures BlockAck related trigger
- * rx_ba_start: tid bitmap to configure on what tid the trigger should occur
- * when an Rx BlockAck session is started.
- * rx_ba_stop: tid bitmap to configure on what tid the trigger should occur
- * when an Rx BlockAck session is stopped.
- * tx_ba_start: tid bitmap to configure on what tid the trigger should occur
- * when a Tx BlockAck session is started.
- * tx_ba_stop: tid bitmap to configure on what tid the trigger should occur
- * when a Tx BlockAck session is stopped.
- * rx_bar: tid bitmap to configure on what tid the trigger should occur
- * when a BAR is received (for a Tx BlockAck session).
- * tx_bar: tid bitmap to configure on what tid the trigger should occur
- * when a BAR is send (for an Rx BlocAck session).
- * frame_timeout: tid bitmap to configure on what tid the trigger should occur
- * when a frame times out in the reodering buffer.
- */
-struct iwl_fw_dbg_trigger_ba {
- __le16 rx_ba_start;
- __le16 rx_ba_stop;
- __le16 tx_ba_start;
- __le16 tx_ba_stop;
- __le16 rx_bar;
- __le16 tx_bar;
- __le16 frame_timeout;
-} __packed;
-
-/**
- * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
- * @id: conf id
- * @usniffer: should the uSniffer image be used
- * @num_of_hcmds: how many HCMDs to send are present here
- * @hcmd: a variable length host command to be sent to apply the configuration.
- * If there is more than one HCMD to send, they will appear one after the
- * other and be sent in the order that they appear in.
- * This parses IWL_UCODE_TLV_FW_DBG_CONF. The user can add up-to
- * %FW_DBG_CONF_MAX configuration per run.
- */
-struct iwl_fw_dbg_conf_tlv {
- u8 id;
- u8 usniffer;
- u8 reserved;
- u8 num_of_hcmds;
- struct iwl_fw_dbg_conf_hcmd hcmd;
-} __packed;
-
-/**
- * struct iwl_fw_gscan_capabilities - gscan capabilities supported by FW
- * @max_scan_cache_size: total space allocated for scan results (in bytes).
- * @max_scan_buckets: maximum number of channel buckets.
- * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan.
- * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI.
- * @max_scan_reporting_threshold: max possible report threshold. in percentage.
- * @max_hotlist_aps: maximum number of entries for hotlist APs.
- * @max_significant_change_aps: maximum number of entries for significant
- * change APs.
- * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can
- * hold.
- */
-struct iwl_fw_gscan_capabilities {
- __le32 max_scan_cache_size;
- __le32 max_scan_buckets;
- __le32 max_ap_cache_per_scan;
- __le32 max_rssi_sample_size;
- __le32 max_scan_reporting_threshold;
- __le32 max_hotlist_aps;
- __le32 max_significant_change_aps;
- __le32 max_bssid_history_entries;
-} __packed;
-
-#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
deleted file mode 100644
index 84ec0cefb62a..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ /dev/null
@@ -1,322 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __iwl_fw_h__
-#define __iwl_fw_h__
-#include <linux/types.h>
-#include <net/mac80211.h>
-
-#include "iwl-fw-file.h"
-#include "iwl-fw-error-dump.h"
-
-/**
- * enum iwl_ucode_type
- *
- * The type of ucode.
- *
- * @IWL_UCODE_REGULAR: Normal runtime ucode
- * @IWL_UCODE_INIT: Initial ucode
- * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
- * @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image
- */
-enum iwl_ucode_type {
- IWL_UCODE_REGULAR,
- IWL_UCODE_INIT,
- IWL_UCODE_WOWLAN,
- IWL_UCODE_REGULAR_USNIFFER,
- IWL_UCODE_TYPE_MAX,
-};
-
-/*
- * enumeration of ucode section.
- * This enumeration is used directly for older firmware (before 16.0).
- * For new firmware, there can be up to 4 sections (see below) but the
- * first one packaged into the firmware file is the DATA section and
- * some debugging code accesses that.
- */
-enum iwl_ucode_sec {
- IWL_UCODE_SECTION_DATA,
- IWL_UCODE_SECTION_INST,
-};
-
-struct iwl_ucode_capabilities {
- u32 max_probe_length;
- u32 n_scan_channels;
- u32 standard_phy_calibration_size;
- u32 flags;
- unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];
- unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)];
-};
-
-static inline bool
-fw_has_api(const struct iwl_ucode_capabilities *capabilities,
- iwl_ucode_tlv_api_t api)
-{
- return test_bit((__force long)api, capabilities->_api);
-}
-
-static inline bool
-fw_has_capa(const struct iwl_ucode_capabilities *capabilities,
- iwl_ucode_tlv_capa_t capa)
-{
- return test_bit((__force long)capa, capabilities->_capa);
-}
-
-/* one for each uCode image (inst/data, init/runtime/wowlan) */
-struct fw_desc {
- const void *data; /* vmalloc'ed data */
- u32 len; /* size in bytes */
- u32 offset; /* offset in the device */
-};
-
-struct fw_img {
- struct fw_desc sec[IWL_UCODE_SECTION_MAX];
- bool is_dual_cpus;
- u32 paging_mem_size;
-};
-
-struct iwl_sf_region {
- u32 addr;
- u32 size;
-};
-
-/*
- * Block paging calculations
- */
-#define PAGE_2_EXP_SIZE 12 /* 4K == 2^12 */
-#define FW_PAGING_SIZE BIT(PAGE_2_EXP_SIZE) /* page size is 4KB */
-#define PAGE_PER_GROUP_2_EXP_SIZE 3
-/* 8 pages per group */
-#define NUM_OF_PAGE_PER_GROUP BIT(PAGE_PER_GROUP_2_EXP_SIZE)
-/* don't change, support only 32KB size */
-#define PAGING_BLOCK_SIZE (NUM_OF_PAGE_PER_GROUP * FW_PAGING_SIZE)
-/* 32K == 2^15 */
-#define BLOCK_2_EXP_SIZE (PAGE_2_EXP_SIZE + PAGE_PER_GROUP_2_EXP_SIZE)
-
-/*
- * Image paging calculations
- */
-#define BLOCK_PER_IMAGE_2_EXP_SIZE 5
-/* 2^5 == 32 blocks per image */
-#define NUM_OF_BLOCK_PER_IMAGE BIT(BLOCK_PER_IMAGE_2_EXP_SIZE)
-/* maximum image size 1024KB */
-#define MAX_PAGING_IMAGE_SIZE (NUM_OF_BLOCK_PER_IMAGE * PAGING_BLOCK_SIZE)
-
-/* Virtual address signature */
-#define PAGING_ADDR_SIG 0xAA000000
-
-#define PAGING_CMD_IS_SECURED BIT(9)
-#define PAGING_CMD_IS_ENABLED BIT(8)
-#define PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS 0
-#define PAGING_TLV_SECURE_MASK 1
-
-/**
- * struct iwl_fw_paging
- * @fw_paging_phys: page phy pointer
- * @fw_paging_block: pointer to the allocated block
- * @fw_paging_size: page size
- */
-struct iwl_fw_paging {
- dma_addr_t fw_paging_phys;
- struct page *fw_paging_block;
- u32 fw_paging_size;
-};
-
-/**
- * struct iwl_fw_cscheme_list - a cipher scheme list
- * @size: a number of entries
- * @cs: cipher scheme entries
- */
-struct iwl_fw_cscheme_list {
- u8 size;
- struct iwl_fw_cipher_scheme cs[];
-} __packed;
-
-/**
- * struct iwl_gscan_capabilities - gscan capabilities supported by FW
- * @max_scan_cache_size: total space allocated for scan results (in bytes).
- * @max_scan_buckets: maximum number of channel buckets.
- * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan.
- * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI.
- * @max_scan_reporting_threshold: max possible report threshold. in percentage.
- * @max_hotlist_aps: maximum number of entries for hotlist APs.
- * @max_significant_change_aps: maximum number of entries for significant
- * change APs.
- * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can
- * hold.
- */
-struct iwl_gscan_capabilities {
- u32 max_scan_cache_size;
- u32 max_scan_buckets;
- u32 max_ap_cache_per_scan;
- u32 max_rssi_sample_size;
- u32 max_scan_reporting_threshold;
- u32 max_hotlist_aps;
- u32 max_significant_change_aps;
- u32 max_bssid_history_entries;
-};
-
-/**
- * struct iwl_fw - variables associated with the firmware
- *
- * @ucode_ver: ucode version from the ucode file
- * @fw_version: firmware version string
- * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan.
- * @ucode_capa: capabilities parsed from the ucode file.
- * @enhance_sensitivity_table: device can do enhanced sensitivity.
- * @init_evtlog_ptr: event log offset for init ucode.
- * @init_evtlog_size: event log size for init ucode.
- * @init_errlog_ptr: error log offfset for init ucode.
- * @inst_evtlog_ptr: event log offset for runtime ucode.
- * @inst_evtlog_size: event log size for runtime ucode.
- * @inst_errlog_ptr: error log offfset for runtime ucode.
- * @mvm_fw: indicates this is MVM firmware
- * @cipher_scheme: optional external cipher scheme.
- * @human_readable: human readable version
- * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until
- * we get the ALIVE from the uCode
- * @dbg_dest_tlv: points to the destination TLV for debug
- * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
- * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
- * @dbg_trigger_tlv: array of pointers to triggers TLVs
- * @dbg_trigger_tlv_len: lengths of the @dbg_trigger_tlv entries
- * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
- */
-struct iwl_fw {
- u32 ucode_ver;
-
- char fw_version[ETHTOOL_FWVERS_LEN];
-
- /* ucode images */
- struct fw_img img[IWL_UCODE_TYPE_MAX];
-
- struct iwl_ucode_capabilities ucode_capa;
- bool enhance_sensitivity_table;
-
- u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
- u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
-
- struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX];
- u32 phy_config;
- u8 valid_tx_ant;
- u8 valid_rx_ant;
-
- bool mvm_fw;
-
- struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
- u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
-
- u32 sdio_adma_addr;
-
- struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
- struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
- size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
- struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
- size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
- u8 dbg_dest_reg_num;
- struct iwl_gscan_capabilities gscan_capa;
-};
-
-static inline const char *get_fw_dbg_mode_string(int mode)
-{
- switch (mode) {
- case SMEM_MODE:
- return "SMEM";
- case EXTERNAL_MODE:
- return "EXTERNAL_DRAM";
- case MARBH_MODE:
- return "MARBH";
- case MIPI_MODE:
- return "MIPI";
- default:
- return "UNKNOWN";
- }
-}
-
-static inline bool
-iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
-{
- const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
-
- if (!conf_tlv)
- return false;
-
- return conf_tlv->usniffer;
-}
-
-#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \
- void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \
- unlikely(__dbg_trigger); \
-})
-
-static inline struct iwl_fw_dbg_trigger_tlv*
-iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id)
-{
- if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv)))
- return NULL;
-
- return fw->dbg_trigger_tlv[id];
-}
-
-#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c
deleted file mode 100644
index 0bd9d4aad0c0..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-io.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/export.h>
-
-#include "iwl-drv.h"
-#include "iwl-io.h"
-#include "iwl-csr.h"
-#include "iwl-debug.h"
-#include "iwl-prph.h"
-#include "iwl-fh.h"
-
-void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
-{
- trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val);
- iwl_trans_write8(trans, ofs, val);
-}
-IWL_EXPORT_SYMBOL(iwl_write8);
-
-void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val)
-{
- trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val);
- iwl_trans_write32(trans, ofs, val);
-}
-IWL_EXPORT_SYMBOL(iwl_write32);
-
-u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
-{
- u32 val = iwl_trans_read32(trans, ofs);
-
- trace_iwlwifi_dev_ioread32(trans->dev, ofs, val);
- return val;
-}
-IWL_EXPORT_SYMBOL(iwl_read32);
-
-#define IWL_POLL_INTERVAL 10 /* microseconds */
-
-int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
- u32 bits, u32 mask, int timeout)
-{
- int t = 0;
-
- do {
- if ((iwl_read32(trans, addr) & mask) == (bits & mask))
- return t;
- udelay(IWL_POLL_INTERVAL);
- t += IWL_POLL_INTERVAL;
- } while (t < timeout);
-
- return -ETIMEDOUT;
-}
-IWL_EXPORT_SYMBOL(iwl_poll_bit);
-
-u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
-{
- u32 value = 0x5a5a5a5a;
- unsigned long flags;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- value = iwl_read32(trans, reg);
- iwl_trans_release_nic_access(trans, &flags);
- }
-
- return value;
-}
-IWL_EXPORT_SYMBOL(iwl_read_direct32);
-
-void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
-{
- unsigned long flags;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- iwl_write32(trans, reg, value);
- iwl_trans_release_nic_access(trans, &flags);
- }
-}
-IWL_EXPORT_SYMBOL(iwl_write_direct32);
-
-int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
- int timeout)
-{
- int t = 0;
-
- do {
- if ((iwl_read_direct32(trans, addr) & mask) == mask)
- return t;
- udelay(IWL_POLL_INTERVAL);
- t += IWL_POLL_INTERVAL;
- } while (t < timeout);
-
- return -ETIMEDOUT;
-}
-IWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
-
-u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs)
-{
- u32 val = iwl_trans_read_prph(trans, ofs);
- trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val);
- return val;
-}
-
-void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
-{
- trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val);
- iwl_trans_write_prph(trans, ofs, val);
-}
-
-u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
-{
- unsigned long flags;
- u32 val = 0x5a5a5a5a;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- val = __iwl_read_prph(trans, ofs);
- iwl_trans_release_nic_access(trans, &flags);
- }
- return val;
-}
-IWL_EXPORT_SYMBOL(iwl_read_prph);
-
-void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
-{
- unsigned long flags;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- __iwl_write_prph(trans, ofs, val);
- iwl_trans_release_nic_access(trans, &flags);
- }
-}
-IWL_EXPORT_SYMBOL(iwl_write_prph);
-
-int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
- u32 bits, u32 mask, int timeout)
-{
- int t = 0;
-
- do {
- if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
- return t;
- udelay(IWL_POLL_INTERVAL);
- t += IWL_POLL_INTERVAL;
- } while (t < timeout);
-
- return -ETIMEDOUT;
-}
-
-void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
-{
- unsigned long flags;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- __iwl_write_prph(trans, ofs,
- __iwl_read_prph(trans, ofs) | mask);
- iwl_trans_release_nic_access(trans, &flags);
- }
-}
-IWL_EXPORT_SYMBOL(iwl_set_bits_prph);
-
-void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
- u32 bits, u32 mask)
-{
- unsigned long flags;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- __iwl_write_prph(trans, ofs,
- (__iwl_read_prph(trans, ofs) & mask) | bits);
- iwl_trans_release_nic_access(trans, &flags);
- }
-}
-IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph);
-
-void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
-{
- unsigned long flags;
- u32 val;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- val = __iwl_read_prph(trans, ofs);
- __iwl_write_prph(trans, ofs, (val & ~mask));
- iwl_trans_release_nic_access(trans, &flags);
- }
-}
-IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
-
-void iwl_force_nmi(struct iwl_trans *trans)
-{
- if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- iwl_write_prph(trans, DEVICE_SET_NMI_REG,
- DEVICE_SET_NMI_VAL_DRV);
- iwl_write_prph(trans, DEVICE_SET_NMI_REG,
- DEVICE_SET_NMI_VAL_HW);
- } else {
- iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG,
- DEVICE_SET_NMI_8000_VAL);
- iwl_write_prph(trans, DEVICE_SET_NMI_REG,
- DEVICE_SET_NMI_VAL_DRV);
- }
-}
-IWL_EXPORT_SYMBOL(iwl_force_nmi);
-
-static const char *get_fh_string(int cmd)
-{
-#define IWL_CMD(x) case x: return #x
- switch (cmd) {
- IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
- IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
- IWL_CMD(FH_RSCSR_CHNL0_WPTR);
- IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
- IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
- IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
- IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
- IWL_CMD(FH_TSSR_TX_STATUS_REG);
- IWL_CMD(FH_TSSR_TX_ERROR_REG);
- default:
- return "UNKNOWN";
- }
-#undef IWL_CMD
-}
-
-int iwl_dump_fh(struct iwl_trans *trans, char **buf)
-{
- int i;
- static const u32 fh_tbl[] = {
- FH_RSCSR_CHNL0_STTS_WPTR_REG,
- FH_RSCSR_CHNL0_RBDCB_BASE_REG,
- FH_RSCSR_CHNL0_WPTR,
- FH_MEM_RCSR_CHNL0_CONFIG_REG,
- FH_MEM_RSSR_SHARED_CTRL_REG,
- FH_MEM_RSSR_RX_STATUS_REG,
- FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
- FH_TSSR_TX_STATUS_REG,
- FH_TSSR_TX_ERROR_REG
- };
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (buf) {
- int pos = 0;
- size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
-
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
-
- pos += scnprintf(*buf + pos, bufsz - pos,
- "FH register values:\n");
-
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
- pos += scnprintf(*buf + pos, bufsz - pos,
- " %34s: 0X%08x\n",
- get_fh_string(fh_tbl[i]),
- iwl_read_direct32(trans, fh_tbl[i]));
-
- return pos;
- }
-#endif
-
- IWL_ERR(trans, "FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
- IWL_ERR(trans, " %34s: 0X%08x\n",
- get_fh_string(fh_tbl[i]),
- iwl_read_direct32(trans, fh_tbl[i]));
-
- return 0;
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
deleted file mode 100644
index 501d0560c061..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- *
- * Portions of this file are derived from the ipw3945 project.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#ifndef __iwl_io_h__
-#define __iwl_io_h__
-
-#include "iwl-devtrace.h"
-#include "iwl-trans.h"
-
-void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val);
-void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val);
-u32 iwl_read32(struct iwl_trans *trans, u32 ofs);
-
-static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
- iwl_trans_set_bits_mask(trans, reg, mask, mask);
-}
-
-static inline void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
- iwl_trans_set_bits_mask(trans, reg, mask, 0);
-}
-
-int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
- u32 bits, u32 mask, int timeout);
-int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
- int timeout);
-
-u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg);
-void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value);
-
-
-u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs);
-u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs);
-void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
-void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
-int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
- u32 bits, u32 mask, int timeout);
-void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
-void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
- u32 bits, u32 mask);
-void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
-void iwl_force_nmi(struct iwl_trans *trans);
-
-/* Error handling */
-int iwl_dump_fh(struct iwl_trans *trans, char **buf);
-
-#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h
deleted file mode 100644
index ac2b90df8413..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_modparams_h__
-#define __iwl_modparams_h__
-
-#include <linux/types.h>
-#include <linux/spinlock.h>
-#include <linux/gfp.h>
-#include <net/mac80211.h>
-
-extern struct iwl_mod_params iwlwifi_mod_params;
-
-enum iwl_power_level {
- IWL_POWER_INDEX_1,
- IWL_POWER_INDEX_2,
- IWL_POWER_INDEX_3,
- IWL_POWER_INDEX_4,
- IWL_POWER_INDEX_5,
- IWL_POWER_NUM
-};
-
-enum iwl_disable_11n {
- IWL_DISABLE_HT_ALL = BIT(0),
- IWL_DISABLE_HT_TXAGG = BIT(1),
- IWL_DISABLE_HT_RXAGG = BIT(2),
- IWL_ENABLE_HT_TXAGG = BIT(3),
-};
-
-/**
- * struct iwl_mod_params
- *
- * Holds the module parameters
- *
- * @sw_crypto: using hardware encryption, default = 0
- * @disable_11n: disable 11n capabilities, default = 0,
- * use IWL_[DIS,EN]ABLE_HT_* constants
- * @amsdu_size_8K: enable 8K amsdu size, default = 0
- * @restart_fw: restart firmware, default = 1
- * @bt_coex_active: enable bt coex, default = true
- * @led_mode: system default, default = 0
- * @power_save: enable power save, default = false
- * @power_level: power level, default = 1
- * @debug_level: levels are IWL_DL_*
- * @ant_coupling: antenna coupling in dB, default = 0
- * @d0i3_disable: disable d0i3, default = 1,
- * @lar_disable: disable LAR (regulatory), default = 0
- * @fw_monitor: allow to use firmware monitor
- */
-struct iwl_mod_params {
- int sw_crypto;
- unsigned int disable_11n;
- int amsdu_size_8K;
- bool restart_fw;
- bool bt_coex_active;
- int led_mode;
- bool power_save;
- int power_level;
-#ifdef CONFIG_IWLWIFI_DEBUG
- u32 debug_level;
-#endif
- int ant_coupling;
- char *nvm_file;
- bool uapsd_disable;
- bool d0i3_disable;
- bool lar_disable;
- bool fw_monitor;
-};
-
-#endif /* #__iwl_modparams_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
deleted file mode 100644
index 6caf2affbbb5..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/sched.h>
-#include <linux/export.h>
-
-#include "iwl-drv.h"
-#include "iwl-notif-wait.h"
-
-
-void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait)
-{
- spin_lock_init(&notif_wait->notif_wait_lock);
- INIT_LIST_HEAD(&notif_wait->notif_waits);
- init_waitqueue_head(&notif_wait->notif_waitq);
-}
-IWL_EXPORT_SYMBOL(iwl_notification_wait_init);
-
-void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt)
-{
- bool triggered = false;
-
- if (!list_empty(&notif_wait->notif_waits)) {
- struct iwl_notification_wait *w;
-
- spin_lock(&notif_wait->notif_wait_lock);
- list_for_each_entry(w, &notif_wait->notif_waits, list) {
- int i;
- bool found = false;
-
- /*
- * If it already finished (triggered) or has been
- * aborted then don't evaluate it again to avoid races,
- * Otherwise the function could be called again even
- * though it returned true before
- */
- if (w->triggered || w->aborted)
- continue;
-
- for (i = 0; i < w->n_cmds; i++) {
- if (w->cmds[i] ==
- WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) {
- found = true;
- break;
- }
- }
- if (!found)
- continue;
-
- if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) {
- w->triggered = true;
- triggered = true;
- }
- }
- spin_unlock(&notif_wait->notif_wait_lock);
-
- }
-
- if (triggered)
- wake_up_all(&notif_wait->notif_waitq);
-}
-IWL_EXPORT_SYMBOL(iwl_notification_wait_notify);
-
-void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
-{
- struct iwl_notification_wait *wait_entry;
-
- spin_lock(&notif_wait->notif_wait_lock);
- list_for_each_entry(wait_entry, &notif_wait->notif_waits, list)
- wait_entry->aborted = true;
- spin_unlock(&notif_wait->notif_wait_lock);
-
- wake_up_all(&notif_wait->notif_waitq);
-}
-IWL_EXPORT_SYMBOL(iwl_abort_notification_waits);
-
-void
-iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait,
- struct iwl_notification_wait *wait_entry,
- const u16 *cmds, int n_cmds,
- bool (*fn)(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data),
- void *fn_data)
-{
- if (WARN_ON(n_cmds > MAX_NOTIF_CMDS))
- n_cmds = MAX_NOTIF_CMDS;
-
- wait_entry->fn = fn;
- wait_entry->fn_data = fn_data;
- wait_entry->n_cmds = n_cmds;
- memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(u16));
- wait_entry->triggered = false;
- wait_entry->aborted = false;
-
- spin_lock_bh(&notif_wait->notif_wait_lock);
- list_add(&wait_entry->list, &notif_wait->notif_waits);
- spin_unlock_bh(&notif_wait->notif_wait_lock);
-}
-IWL_EXPORT_SYMBOL(iwl_init_notification_wait);
-
-int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait,
- struct iwl_notification_wait *wait_entry,
- unsigned long timeout)
-{
- int ret;
-
- ret = wait_event_timeout(notif_wait->notif_waitq,
- wait_entry->triggered || wait_entry->aborted,
- timeout);
-
- spin_lock_bh(&notif_wait->notif_wait_lock);
- list_del(&wait_entry->list);
- spin_unlock_bh(&notif_wait->notif_wait_lock);
-
- if (wait_entry->aborted)
- return -EIO;
-
- /* return value is always >= 0 */
- if (ret <= 0)
- return -ETIMEDOUT;
- return 0;
-}
-IWL_EXPORT_SYMBOL(iwl_wait_notification);
-
-void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait,
- struct iwl_notification_wait *wait_entry)
-{
- spin_lock_bh(&notif_wait->notif_wait_lock);
- list_del(&wait_entry->list);
- spin_unlock_bh(&notif_wait->notif_wait_lock);
-}
-IWL_EXPORT_SYMBOL(iwl_remove_notification);
diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
deleted file mode 100644
index dbe8234521de..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_notif_wait_h__
-#define __iwl_notif_wait_h__
-
-#include <linux/wait.h>
-
-#include "iwl-trans.h"
-
-struct iwl_notif_wait_data {
- struct list_head notif_waits;
- spinlock_t notif_wait_lock;
- wait_queue_head_t notif_waitq;
-};
-
-#define MAX_NOTIF_CMDS 5
-
-/**
- * struct iwl_notification_wait - notification wait entry
- * @list: list head for global list
- * @fn: Function called with the notification. If the function
- * returns true, the wait is over, if it returns false then
- * the waiter stays blocked. If no function is given, any
- * of the listed commands will unblock the waiter.
- * @cmds: command IDs
- * @n_cmds: number of command IDs
- * @triggered: waiter should be woken up
- * @aborted: wait was aborted
- *
- * This structure is not used directly, to wait for a
- * notification declare it on the stack, and call
- * iwlagn_init_notification_wait() with appropriate
- * parameters. Then do whatever will cause the ucode
- * to notify the driver, and to wait for that then
- * call iwlagn_wait_notification().
- *
- * Each notification is one-shot. If at some point we
- * need to support multi-shot notifications (which
- * can't be allocated on the stack) we need to modify
- * the code for them.
- */
-struct iwl_notification_wait {
- struct list_head list;
-
- bool (*fn)(struct iwl_notif_wait_data *notif_data,
- struct iwl_rx_packet *pkt, void *data);
- void *fn_data;
-
- u16 cmds[MAX_NOTIF_CMDS];
- u8 n_cmds;
- bool triggered, aborted;
-};
-
-
-/* caller functions */
-void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data);
-void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data,
- struct iwl_rx_packet *pkt);
-void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data);
-
-/* user functions */
-void __acquires(wait_entry)
-iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data,
- struct iwl_notification_wait *wait_entry,
- const u16 *cmds, int n_cmds,
- bool (*fn)(struct iwl_notif_wait_data *notif_data,
- struct iwl_rx_packet *pkt, void *data),
- void *fn_data);
-
-int __must_check __releases(wait_entry)
-iwl_wait_notification(struct iwl_notif_wait_data *notif_data,
- struct iwl_notification_wait *wait_entry,
- unsigned long timeout);
-
-void __releases(wait_entry)
-iwl_remove_notification(struct iwl_notif_wait_data *notif_data,
- struct iwl_notification_wait *wait_entry);
-
-#endif /* __iwl_notif_wait_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
deleted file mode 100644
index d82984912e04..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ /dev/null
@@ -1,844 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/etherdevice.h>
-#include <linux/pci.h>
-#include "iwl-drv.h"
-#include "iwl-modparams.h"
-#include "iwl-nvm-parse.h"
-
-/* NVM offsets (in words) definitions */
-enum wkp_nvm_offsets {
- /* NVM HW-Section offset (in words) definitions */
- HW_ADDR = 0x15,
-
- /* NVM SW-Section offset (in words) definitions */
- NVM_SW_SECTION = 0x1C0,
- NVM_VERSION = 0,
- RADIO_CFG = 1,
- SKU = 2,
- N_HW_ADDRS = 3,
- NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION,
-
- /* NVM calibration section offset (in words) definitions */
- NVM_CALIB_SECTION = 0x2B8,
- XTAL_CALIB = 0x316 - NVM_CALIB_SECTION
-};
-
-enum family_8000_nvm_offsets {
- /* NVM HW-Section offset (in words) definitions */
- HW_ADDR0_WFPM_FAMILY_8000 = 0x12,
- HW_ADDR1_WFPM_FAMILY_8000 = 0x16,
- HW_ADDR0_PCIE_FAMILY_8000 = 0x8A,
- HW_ADDR1_PCIE_FAMILY_8000 = 0x8E,
- MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
-
- /* NVM SW-Section offset (in words) definitions */
- NVM_SW_SECTION_FAMILY_8000 = 0x1C0,
- NVM_VERSION_FAMILY_8000 = 0,
- RADIO_CFG_FAMILY_8000 = 0,
- SKU_FAMILY_8000 = 2,
- N_HW_ADDRS_FAMILY_8000 = 3,
-
- /* NVM REGULATORY -Section offset (in words) definitions */
- NVM_CHANNELS_FAMILY_8000 = 0,
- NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7,
- NVM_LAR_OFFSET_FAMILY_8000 = 0x507,
- NVM_LAR_ENABLED_FAMILY_8000 = 0x7,
-
- /* NVM calibration section offset (in words) definitions */
- NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8,
- XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000
-};
-
-/* SKU Capabilities (actual values from NVM definition) */
-enum nvm_sku_bits {
- NVM_SKU_CAP_BAND_24GHZ = BIT(0),
- NVM_SKU_CAP_BAND_52GHZ = BIT(1),
- NVM_SKU_CAP_11N_ENABLE = BIT(2),
- NVM_SKU_CAP_11AC_ENABLE = BIT(3),
- NVM_SKU_CAP_MIMO_DISABLE = BIT(5),
-};
-
-/*
- * These are the channel numbers in the order that they are stored in the NVM
- */
-static const u8 iwl_nvm_channels[] = {
- /* 2.4 GHz */
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- /* 5 GHz */
- 36, 40, 44 , 48, 52, 56, 60, 64,
- 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
- 149, 153, 157, 161, 165
-};
-
-static const u8 iwl_nvm_channels_family_8000[] = {
- /* 2.4 GHz */
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- /* 5 GHz */
- 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
- 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
- 149, 153, 157, 161, 165, 169, 173, 177, 181
-};
-
-#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
-#define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000)
-#define NUM_2GHZ_CHANNELS 14
-#define NUM_2GHZ_CHANNELS_FAMILY_8000 14
-#define FIRST_2GHZ_HT_MINUS 5
-#define LAST_2GHZ_HT_PLUS 9
-#define LAST_5GHZ_HT 165
-#define LAST_5GHZ_HT_FAMILY_8000 181
-#define N_HW_ADDR_MASK 0xF
-
-/* rate data (static) */
-static struct ieee80211_rate iwl_cfg80211_rates[] = {
- { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
- { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
- { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
- { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
- { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
- { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
- { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
- { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
- { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
- { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
- { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
- { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
-};
-#define RATES_24_OFFS 0
-#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates)
-#define RATES_52_OFFS 4
-#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS)
-
-/**
- * enum iwl_nvm_channel_flags - channel flags in NVM
- * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
- * @NVM_CHANNEL_IBSS: usable as an IBSS channel
- * @NVM_CHANNEL_ACTIVE: active scanning allowed
- * @NVM_CHANNEL_RADAR: radar detection required
- * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed
- * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
- * on same channel on 2.4 or same UNII band on 5.2
- * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
- * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
- * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
- * @NVM_CHANNEL_160MHZ: 160 MHz channel okay (?)
- */
-enum iwl_nvm_channel_flags {
- NVM_CHANNEL_VALID = BIT(0),
- NVM_CHANNEL_IBSS = BIT(1),
- NVM_CHANNEL_ACTIVE = BIT(3),
- NVM_CHANNEL_RADAR = BIT(4),
- NVM_CHANNEL_INDOOR_ONLY = BIT(5),
- NVM_CHANNEL_GO_CONCURRENT = BIT(6),
- NVM_CHANNEL_WIDE = BIT(8),
- NVM_CHANNEL_40MHZ = BIT(9),
- NVM_CHANNEL_80MHZ = BIT(10),
- NVM_CHANNEL_160MHZ = BIT(11),
-};
-
-#define CHECK_AND_PRINT_I(x) \
- ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "")
-
-static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz,
- u16 nvm_flags, const struct iwl_cfg *cfg)
-{
- u32 flags = IEEE80211_CHAN_NO_HT40;
- u32 last_5ghz_ht = LAST_5GHZ_HT;
-
- if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
- last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
-
- if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) {
- if (ch_num <= LAST_2GHZ_HT_PLUS)
- flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
- if (ch_num >= FIRST_2GHZ_HT_MINUS)
- flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
- } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) {
- if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
- flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
- else
- flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
- }
- if (!(nvm_flags & NVM_CHANNEL_80MHZ))
- flags |= IEEE80211_CHAN_NO_80MHZ;
- if (!(nvm_flags & NVM_CHANNEL_160MHZ))
- flags |= IEEE80211_CHAN_NO_160MHZ;
-
- if (!(nvm_flags & NVM_CHANNEL_IBSS))
- flags |= IEEE80211_CHAN_NO_IR;
-
- if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
- flags |= IEEE80211_CHAN_NO_IR;
-
- if (nvm_flags & NVM_CHANNEL_RADAR)
- flags |= IEEE80211_CHAN_RADAR;
-
- if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
- flags |= IEEE80211_CHAN_INDOOR_ONLY;
-
- /* Set the GO concurrent flag only in case that NO_IR is set.
- * Otherwise it is meaningless
- */
- if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
- (flags & IEEE80211_CHAN_NO_IR))
- flags |= IEEE80211_CHAN_IR_CONCURRENT;
-
- return flags;
-}
-
-static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- const __le16 * const nvm_ch_flags,
- bool lar_supported)
-{
- int ch_idx;
- int n_channels = 0;
- struct ieee80211_channel *channel;
- u16 ch_flags;
- bool is_5ghz;
- int num_of_ch, num_2ghz_channels;
- const u8 *nvm_chan;
-
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- num_of_ch = IWL_NUM_CHANNELS;
- nvm_chan = &iwl_nvm_channels[0];
- num_2ghz_channels = NUM_2GHZ_CHANNELS;
- } else {
- num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000;
- nvm_chan = &iwl_nvm_channels_family_8000[0];
- num_2ghz_channels = NUM_2GHZ_CHANNELS_FAMILY_8000;
- }
-
- for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
- ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
-
- if (ch_idx >= num_2ghz_channels &&
- !data->sku_cap_band_52GHz_enable)
- continue;
-
- if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) {
- /*
- * Channels might become valid later if lar is
- * supported, hence we still want to add them to
- * the list of supported channels to cfg80211.
- */
- IWL_DEBUG_EEPROM(dev,
- "Ch. %d Flags %x [%sGHz] - No traffic\n",
- nvm_chan[ch_idx],
- ch_flags,
- (ch_idx >= num_2ghz_channels) ?
- "5.2" : "2.4");
- continue;
- }
-
- channel = &data->channels[n_channels];
- n_channels++;
-
- channel->hw_value = nvm_chan[ch_idx];
- channel->band = (ch_idx < num_2ghz_channels) ?
- IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
- channel->center_freq =
- ieee80211_channel_to_frequency(
- channel->hw_value, channel->band);
-
- /* Initialize regulatory-based run-time data */
-
- /*
- * Default value - highest tx power value. max_power
- * is not used in mvm, and is used for backwards compatibility
- */
- channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
- is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
-
- /* don't put limitations in case we're using LAR */
- if (!lar_supported)
- channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx],
- ch_idx, is_5ghz,
- ch_flags, cfg);
- else
- channel->flags = 0;
-
- IWL_DEBUG_EEPROM(dev,
- "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
- channel->hw_value,
- is_5ghz ? "5.2" : "2.4",
- CHECK_AND_PRINT_I(VALID),
- CHECK_AND_PRINT_I(IBSS),
- CHECK_AND_PRINT_I(ACTIVE),
- CHECK_AND_PRINT_I(RADAR),
- CHECK_AND_PRINT_I(WIDE),
- CHECK_AND_PRINT_I(INDOOR_ONLY),
- CHECK_AND_PRINT_I(GO_CONCURRENT),
- ch_flags,
- channel->max_power,
- ((ch_flags & NVM_CHANNEL_IBSS) &&
- !(ch_flags & NVM_CHANNEL_RADAR))
- ? "" : "not ");
- }
-
- return n_channels;
-}
-
-static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- struct ieee80211_sta_vht_cap *vht_cap,
- u8 tx_chains, u8 rx_chains)
-{
- int num_rx_ants = num_of_ant(rx_chains);
- int num_tx_ants = num_of_ant(tx_chains);
- unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?:
- IEEE80211_VHT_MAX_AMPDU_1024K);
-
- vht_cap->vht_supported = true;
-
- vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
- IEEE80211_VHT_CAP_RXSTBC_1 |
- IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
- 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
- max_ampdu_exponent <<
- IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
-
- if (cfg->ht_params->ldpc)
- vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
-
- if (data->sku_cap_mimo_disabled) {
- num_rx_ants = 1;
- num_tx_ants = 1;
- }
-
- if (num_tx_ants > 1)
- vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
- else
- vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
-
- if (iwlwifi_mod_params.amsdu_size_8K)
- vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
-
- vht_cap->vht_mcs.rx_mcs_map =
- cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 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);
-
- if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) {
- vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
- /* this works because NOT_SUPPORTED == 3 */
- vht_cap->vht_mcs.rx_mcs_map |=
- cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2);
- }
-
- vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
-}
-
-static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- const __le16 *ch_section,
- u8 tx_chains, u8 rx_chains, bool lar_supported)
-{
- int n_channels;
- int n_used = 0;
- struct ieee80211_supported_band *sband;
-
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
- n_channels = iwl_init_channel_map(
- dev, cfg, data,
- &ch_section[NVM_CHANNELS], lar_supported);
- else
- n_channels = iwl_init_channel_map(
- dev, cfg, data,
- &ch_section[NVM_CHANNELS_FAMILY_8000],
- lar_supported);
-
- sband = &data->bands[IEEE80211_BAND_2GHZ];
- sband->band = IEEE80211_BAND_2GHZ;
- sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
- sband->n_bitrates = N_RATES_24;
- n_used += iwl_init_sband_channels(data, sband, n_channels,
- IEEE80211_BAND_2GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
- tx_chains, rx_chains);
-
- sband = &data->bands[IEEE80211_BAND_5GHZ];
- sband->band = IEEE80211_BAND_5GHZ;
- sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
- sband->n_bitrates = N_RATES_52;
- n_used += iwl_init_sband_channels(data, sband, n_channels,
- IEEE80211_BAND_5GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
- tx_chains, rx_chains);
- if (data->sku_cap_11ac_enable)
- iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
- tx_chains, rx_chains);
-
- if (n_channels != n_used)
- IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
- n_used, n_channels);
-}
-
-static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
- const __le16 *phy_sku)
-{
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
- return le16_to_cpup(nvm_sw + SKU);
-
- return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000));
-}
-
-static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
-{
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
- return le16_to_cpup(nvm_sw + NVM_VERSION);
- else
- return le32_to_cpup((__le32 *)(nvm_sw +
- NVM_VERSION_FAMILY_8000));
-}
-
-static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
- const __le16 *phy_sku)
-{
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
- return le16_to_cpup(nvm_sw + RADIO_CFG);
-
- return le32_to_cpup((__le32 *)(phy_sku + RADIO_CFG_FAMILY_8000));
-
-}
-
-static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
-{
- int n_hw_addr;
-
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
- return le16_to_cpup(nvm_sw + N_HW_ADDRS);
-
- n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000));
-
- return n_hw_addr & N_HW_ADDR_MASK;
-}
-
-static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- u32 radio_cfg)
-{
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
- data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
- data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
- data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
- return;
- }
-
- /* set the radio configuration for family 8000 */
- data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg);
- data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg);
- data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg);
- data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg);
- data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg);
- data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg);
-}
-
-static void iwl_set_hw_address(const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- const __le16 *nvm_sec)
-{
- const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR);
-
- /* The byte order is little endian 16 bit, meaning 214365 */
- data->hw_addr[0] = hw_addr[1];
- data->hw_addr[1] = hw_addr[0];
- data->hw_addr[2] = hw_addr[3];
- data->hw_addr[3] = hw_addr[2];
- data->hw_addr[4] = hw_addr[5];
- data->hw_addr[5] = hw_addr[4];
-}
-
-static void iwl_set_hw_address_family_8000(struct device *dev,
- const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- const __le16 *mac_override,
- const __le16 *nvm_hw,
- u32 mac_addr0, u32 mac_addr1)
-{
- const u8 *hw_addr;
-
- if (mac_override) {
- static const u8 reserved_mac[] = {
- 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
- };
-
- hw_addr = (const u8 *)(mac_override +
- MAC_ADDRESS_OVERRIDE_FAMILY_8000);
-
- /*
- * Store the MAC address from MAO section.
- * No byte swapping is required in MAO section
- */
- memcpy(data->hw_addr, hw_addr, ETH_ALEN);
-
- /*
- * Force the use of the OTP MAC address in case of reserved MAC
- * address in the NVM, or if address is given but invalid.
- */
- if (is_valid_ether_addr(data->hw_addr) &&
- memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0)
- return;
-
- IWL_ERR_DEV(dev,
- "mac address from nvm override section is not valid\n");
- }
-
- if (nvm_hw) {
- /* read the MAC address from HW resisters */
- hw_addr = (const u8 *)&mac_addr0;
- data->hw_addr[0] = hw_addr[3];
- data->hw_addr[1] = hw_addr[2];
- data->hw_addr[2] = hw_addr[1];
- data->hw_addr[3] = hw_addr[0];
-
- hw_addr = (const u8 *)&mac_addr1;
- data->hw_addr[4] = hw_addr[1];
- data->hw_addr[5] = hw_addr[0];
-
- if (!is_valid_ether_addr(data->hw_addr))
- IWL_ERR_DEV(dev,
- "mac address from hw section is not valid\n");
-
- return;
- }
-
- IWL_ERR_DEV(dev, "mac address is not found\n");
-}
-
-#define IWL_4165_DEVICE_ID 0x5501
-
-struct iwl_nvm_data *
-iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
- const __le16 *nvm_hw, const __le16 *nvm_sw,
- const __le16 *nvm_calib, const __le16 *regulatory,
- const __le16 *mac_override, const __le16 *phy_sku,
- u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
- u32 mac_addr0, u32 mac_addr1, u32 hw_id)
-{
- struct iwl_nvm_data *data;
- u32 sku;
- u32 radio_cfg;
- u16 lar_config;
-
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
- data = kzalloc(sizeof(*data) +
- sizeof(struct ieee80211_channel) *
- IWL_NUM_CHANNELS,
- GFP_KERNEL);
- else
- data = kzalloc(sizeof(*data) +
- sizeof(struct ieee80211_channel) *
- IWL_NUM_CHANNELS_FAMILY_8000,
- GFP_KERNEL);
- if (!data)
- return NULL;
-
- data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
-
- radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku);
- iwl_set_radio_cfg(cfg, data, radio_cfg);
- if (data->valid_tx_ant)
- tx_chains &= data->valid_tx_ant;
- if (data->valid_rx_ant)
- rx_chains &= data->valid_rx_ant;
-
- sku = iwl_get_sku(cfg, nvm_sw, phy_sku);
- data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
- data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
- data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
- data->sku_cap_11n_enable = false;
- data->sku_cap_11ac_enable = data->sku_cap_11n_enable &&
- (sku & NVM_SKU_CAP_11AC_ENABLE);
- data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE;
-
- /*
- * OTP 0x52 bug work around
- * define antenna 1x1 according to MIMO disabled
- */
- if (hw_id == IWL_4165_DEVICE_ID && data->sku_cap_mimo_disabled) {
- data->valid_tx_ant = ANT_B;
- data->valid_rx_ant = ANT_B;
- tx_chains = ANT_B;
- rx_chains = ANT_B;
- }
-
- data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
-
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- /* Checking for required sections */
- if (!nvm_calib) {
- IWL_ERR_DEV(dev,
- "Can't parse empty Calib NVM sections\n");
- kfree(data);
- return NULL;
- }
- /* in family 8000 Xtal calibration values moved to OTP */
- data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
- data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
- }
-
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- iwl_set_hw_address(cfg, data, nvm_hw);
-
- iwl_init_sbands(dev, cfg, data, nvm_sw,
- tx_chains, rx_chains, lar_fw_supported);
- } else {
- u16 lar_offset = data->nvm_version < 0xE39 ?
- NVM_LAR_OFFSET_FAMILY_8000_OLD :
- NVM_LAR_OFFSET_FAMILY_8000;
-
- lar_config = le16_to_cpup(regulatory + lar_offset);
- data->lar_enabled = !!(lar_config &
- NVM_LAR_ENABLED_FAMILY_8000);
-
- /* MAC address in family 8000 */
- iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
- nvm_hw, mac_addr0, mac_addr1);
-
- iwl_init_sbands(dev, cfg, data, regulatory,
- tx_chains, rx_chains,
- lar_fw_supported && data->lar_enabled);
- }
-
- data->calib_version = 255;
-
- return data;
-}
-IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
-
-static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,
- int ch_idx, u16 nvm_flags,
- const struct iwl_cfg *cfg)
-{
- u32 flags = NL80211_RRF_NO_HT40;
- u32 last_5ghz_ht = LAST_5GHZ_HT;
-
- if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
- last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
-
- if (ch_idx < NUM_2GHZ_CHANNELS &&
- (nvm_flags & NVM_CHANNEL_40MHZ)) {
- if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
- flags &= ~NL80211_RRF_NO_HT40PLUS;
- if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
- flags &= ~NL80211_RRF_NO_HT40MINUS;
- } else if (nvm_chan[ch_idx] <= last_5ghz_ht &&
- (nvm_flags & NVM_CHANNEL_40MHZ)) {
- if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
- flags &= ~NL80211_RRF_NO_HT40PLUS;
- else
- flags &= ~NL80211_RRF_NO_HT40MINUS;
- }
-
- if (!(nvm_flags & NVM_CHANNEL_80MHZ))
- flags |= NL80211_RRF_NO_80MHZ;
- if (!(nvm_flags & NVM_CHANNEL_160MHZ))
- flags |= NL80211_RRF_NO_160MHZ;
-
- if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
- flags |= NL80211_RRF_NO_IR;
-
- if (nvm_flags & NVM_CHANNEL_RADAR)
- flags |= NL80211_RRF_DFS;
-
- if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
- flags |= NL80211_RRF_NO_OUTDOOR;
-
- /* Set the GO concurrent flag only in case that NO_IR is set.
- * Otherwise it is meaningless
- */
- if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
- (flags & NL80211_RRF_NO_IR))
- flags |= NL80211_RRF_GO_CONCURRENT;
-
- return flags;
-}
-
-struct ieee80211_regdomain *
-iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
- int num_of_ch, __le32 *channels, u16 fw_mcc)
-{
- int ch_idx;
- u16 ch_flags, prev_ch_flags = 0;
- const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
- iwl_nvm_channels_family_8000 : iwl_nvm_channels;
- struct ieee80211_regdomain *regd;
- int size_of_regd;
- struct ieee80211_reg_rule *rule;
- enum ieee80211_band band;
- int center_freq, prev_center_freq = 0;
- int valid_rules = 0;
- bool new_rule;
- int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
- IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS;
-
- if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES))
- return ERR_PTR(-EINVAL);
-
- if (WARN_ON(num_of_ch > max_num_ch))
- num_of_ch = max_num_ch;
-
- IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n",
- num_of_ch);
-
- /* build a regdomain rule for every valid channel */
- size_of_regd =
- sizeof(struct ieee80211_regdomain) +
- num_of_ch * sizeof(struct ieee80211_reg_rule);
-
- regd = kzalloc(size_of_regd, GFP_KERNEL);
- if (!regd)
- return ERR_PTR(-ENOMEM);
-
- for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
- ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
- band = (ch_idx < NUM_2GHZ_CHANNELS) ?
- IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
- center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx],
- band);
- new_rule = false;
-
- if (!(ch_flags & NVM_CHANNEL_VALID)) {
- IWL_DEBUG_DEV(dev, IWL_DL_LAR,
- "Ch. %d Flags %x [%sGHz] - No traffic\n",
- nvm_chan[ch_idx],
- ch_flags,
- (ch_idx >= NUM_2GHZ_CHANNELS) ?
- "5.2" : "2.4");
- continue;
- }
-
- /* we can't continue the same rule */
- if (ch_idx == 0 || prev_ch_flags != ch_flags ||
- center_freq - prev_center_freq > 20) {
- valid_rules++;
- new_rule = true;
- }
-
- rule = &regd->reg_rules[valid_rules - 1];
-
- if (new_rule)
- rule->freq_range.start_freq_khz =
- MHZ_TO_KHZ(center_freq - 10);
-
- rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10);
-
- /* this doesn't matter - not used by FW */
- rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
- rule->power_rule.max_eirp =
- DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER);
-
- rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx,
- ch_flags, cfg);
-
- /* rely on auto-calculation to merge BW of contiguous chans */
- rule->flags |= NL80211_RRF_AUTO_BW;
- rule->freq_range.max_bandwidth_khz = 0;
-
- prev_ch_flags = ch_flags;
- prev_center_freq = center_freq;
-
- IWL_DEBUG_DEV(dev, IWL_DL_LAR,
- "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n",
- center_freq,
- band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4",
- CHECK_AND_PRINT_I(VALID),
- CHECK_AND_PRINT_I(ACTIVE),
- CHECK_AND_PRINT_I(RADAR),
- CHECK_AND_PRINT_I(WIDE),
- CHECK_AND_PRINT_I(40MHZ),
- CHECK_AND_PRINT_I(80MHZ),
- CHECK_AND_PRINT_I(160MHZ),
- CHECK_AND_PRINT_I(INDOOR_ONLY),
- CHECK_AND_PRINT_I(GO_CONCURRENT),
- ch_flags,
- ((ch_flags & NVM_CHANNEL_ACTIVE) &&
- !(ch_flags & NVM_CHANNEL_RADAR))
- ? "" : "not ");
- }
-
- regd->n_reg_rules = valid_rules;
-
- /* set alpha2 from FW. */
- regd->alpha2[0] = fw_mcc >> 8;
- regd->alpha2[1] = fw_mcc & 0xff;
-
- return regd;
-}
-IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
deleted file mode 100644
index 9f44d8188c5c..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-#ifndef __iwl_nvm_parse_h__
-#define __iwl_nvm_parse_h__
-
-#include <net/cfg80211.h>
-#include "iwl-eeprom-parse.h"
-
-/**
- * iwl_parse_nvm_data - parse NVM data and return values
- *
- * This function parses all NVM values we need and then
- * returns a (newly allocated) struct containing all the
- * relevant values for driver use. The struct must be freed
- * later with iwl_free_nvm_data().
- */
-struct iwl_nvm_data *
-iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
- const __le16 *nvm_hw, const __le16 *nvm_sw,
- const __le16 *nvm_calib, const __le16 *regulatory,
- const __le16 *mac_override, const __le16 *phy_sku,
- u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
- u32 mac_addr0, u32 mac_addr1, u32 hw_id);
-
-/**
- * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
- *
- * This function parses the regulatory channel data received as a
- * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain,
- * to be fed into the regulatory core. An ERR_PTR is returned on error.
- * If not given to the regulatory core, the user is responsible for freeing
- * the regdomain returned here with kfree.
- */
-struct ieee80211_regdomain *
-iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
- int num_of_ch, __le32 *channels, u16 fw_mcc);
-
-#endif /* __iwl_nvm_parse_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
deleted file mode 100644
index 2a58d6833224..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ /dev/null
@@ -1,271 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_op_mode_h__
-#define __iwl_op_mode_h__
-
-#include <linux/netdevice.h>
-#include <linux/debugfs.h>
-
-struct iwl_op_mode;
-struct iwl_trans;
-struct sk_buff;
-struct iwl_device_cmd;
-struct iwl_rx_cmd_buffer;
-struct iwl_fw;
-struct iwl_cfg;
-
-/**
- * DOC: Operational mode - what is it ?
- *
- * The operational mode (a.k.a. op_mode) is the layer that implements
- * mac80211's handlers. It knows two APIs: mac80211's and the fw's. It uses
- * the transport API to access the HW. The op_mode doesn't need to know how the
- * underlying HW works, since the transport layer takes care of that.
- *
- * There can be several op_mode: i.e. different fw APIs will require two
- * different op_modes. This is why the op_mode is virtualized.
- */
-
-/**
- * DOC: Life cycle of the Operational mode
- *
- * The operational mode has a very simple life cycle.
- *
- * 1) The driver layer (iwl-drv.c) chooses the op_mode based on the
- * capabilities advertised by the fw file (in TLV format).
- * 2) The driver layer starts the op_mode (ops->start)
- * 3) The op_mode registers mac80211
- * 4) The op_mode is governed by mac80211
- * 5) The driver layer stops the op_mode
- */
-
-/**
- * struct iwl_op_mode_ops - op_mode specific operations
- *
- * The op_mode exports its ops so that external components can start it and
- * interact with it. The driver layer typically calls the start and stop
- * handlers, the transport layer calls the others.
- *
- * All the handlers MUST be implemented, except @rx_rss which can be left
- * out *iff* the opmode will never run on hardware with multi-queue capability.
- *
- * @start: start the op_mode. The transport layer is already allocated.
- * May sleep
- * @stop: stop the op_mode. Must free all the memory allocated.
- * May sleep
- * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
- * HCMD this Rx responds to. Can't sleep.
- * @rx_rss: data queue RX notification to the op_mode, for (data) notifications
- * received on the RSS queue(s). The queue parameter indicates which of the
- * RSS queues received this frame; it will always be non-zero.
- * This method must not sleep.
- * @queue_full: notifies that a HW queue is full.
- * Must be atomic and called with BH disabled.
- * @queue_not_full: notifies that a HW queue is not full any more.
- * Must be atomic and called with BH disabled.
- * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that
- * the radio is killed. Return %true if the device should be stopped by
- * the transport immediately after the call. May sleep.
- * @free_skb: allows the transport layer to free skbs that haven't been
- * reclaimed by the op_mode. This can happen when the driver is freed and
- * there are Tx packets pending in the transport layer.
- * Must be atomic
- * @nic_error: error notification. Must be atomic and must be called with BH
- * disabled.
- * @cmd_queue_full: Called when the command queue gets full. Must be atomic and
- * called with BH disabled.
- * @nic_config: configure NIC, called before firmware is started.
- * May sleep
- * @wimax_active: invoked when WiMax becomes active. May sleep
- * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3
- * entrance is aborted (e.g. due to held reference). May sleep.
- * @exit_d0i3: configure the fw to exit d0i3. May sleep.
- */
-struct iwl_op_mode_ops {
- struct iwl_op_mode *(*start)(struct iwl_trans *trans,
- const struct iwl_cfg *cfg,
- const struct iwl_fw *fw,
- struct dentry *dbgfs_dir);
- void (*stop)(struct iwl_op_mode *op_mode);
- void (*rx)(struct iwl_op_mode *op_mode, struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb);
- void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb, unsigned int queue);
- void (*queue_full)(struct iwl_op_mode *op_mode, int queue);
- void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue);
- bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
- void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
- void (*nic_error)(struct iwl_op_mode *op_mode);
- void (*cmd_queue_full)(struct iwl_op_mode *op_mode);
- void (*nic_config)(struct iwl_op_mode *op_mode);
- void (*wimax_active)(struct iwl_op_mode *op_mode);
- int (*enter_d0i3)(struct iwl_op_mode *op_mode);
- int (*exit_d0i3)(struct iwl_op_mode *op_mode);
-};
-
-int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops);
-void iwl_opmode_deregister(const char *name);
-
-/**
- * struct iwl_op_mode - operational mode
- * @ops: pointer to its own ops
- *
- * This holds an implementation of the mac80211 / fw API.
- */
-struct iwl_op_mode {
- const struct iwl_op_mode_ops *ops;
-
- char op_mode_specific[0] __aligned(sizeof(void *));
-};
-
-static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode)
-{
- might_sleep();
- op_mode->ops->stop(op_mode);
-}
-
-static inline void iwl_op_mode_rx(struct iwl_op_mode *op_mode,
- struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb)
-{
- return op_mode->ops->rx(op_mode, napi, rxb);
-}
-
-static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode,
- struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb,
- unsigned int queue)
-{
- op_mode->ops->rx_rss(op_mode, napi, rxb, queue);
-}
-
-static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode,
- int queue)
-{
- op_mode->ops->queue_full(op_mode, queue);
-}
-
-static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode,
- int queue)
-{
- op_mode->ops->queue_not_full(op_mode, queue);
-}
-
-static inline bool __must_check
-iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, bool state)
-{
- might_sleep();
- return op_mode->ops->hw_rf_kill(op_mode, state);
-}
-
-static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode,
- struct sk_buff *skb)
-{
- op_mode->ops->free_skb(op_mode, skb);
-}
-
-static inline void iwl_op_mode_nic_error(struct iwl_op_mode *op_mode)
-{
- op_mode->ops->nic_error(op_mode);
-}
-
-static inline void iwl_op_mode_cmd_queue_full(struct iwl_op_mode *op_mode)
-{
- op_mode->ops->cmd_queue_full(op_mode);
-}
-
-static inline void iwl_op_mode_nic_config(struct iwl_op_mode *op_mode)
-{
- might_sleep();
- op_mode->ops->nic_config(op_mode);
-}
-
-static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode)
-{
- might_sleep();
- op_mode->ops->wimax_active(op_mode);
-}
-
-static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode)
-{
- might_sleep();
-
- if (!op_mode->ops->enter_d0i3)
- return 0;
- return op_mode->ops->enter_d0i3(op_mode);
-}
-
-static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode)
-{
- might_sleep();
-
- if (!op_mode->ops->exit_d0i3)
- return 0;
- return op_mode->ops->exit_d0i3(op_mode);
-}
-
-#endif /* __iwl_op_mode_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
deleted file mode 100644
index a105455b6a24..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c
+++ /dev/null
@@ -1,471 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/export.h>
-
-#include "iwl-drv.h"
-#include "iwl-phy-db.h"
-#include "iwl-debug.h"
-#include "iwl-op-mode.h"
-#include "iwl-trans.h"
-
-#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */
-#define IWL_NUM_PAPD_CH_GROUPS 9
-#define IWL_NUM_TXP_CH_GROUPS 9
-
-struct iwl_phy_db_entry {
- u16 size;
- u8 *data;
-};
-
-/**
- * struct iwl_phy_db - stores phy configuration and calibration data.
- *
- * @cfg: phy configuration.
- * @calib_nch: non channel specific calibration data.
- * @calib_ch: channel specific calibration data.
- * @calib_ch_group_papd: calibration data related to papd channel group.
- * @calib_ch_group_txp: calibration data related to tx power chanel group.
- */
-struct iwl_phy_db {
- struct iwl_phy_db_entry cfg;
- struct iwl_phy_db_entry calib_nch;
- 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];
-
- struct iwl_trans *trans;
-};
-
-enum iwl_phy_db_section_type {
- IWL_PHY_DB_CFG = 1,
- IWL_PHY_DB_CALIB_NCH,
- IWL_PHY_DB_UNUSED,
- IWL_PHY_DB_CALIB_CHG_PAPD,
- IWL_PHY_DB_CALIB_CHG_TXP,
- IWL_PHY_DB_MAX
-};
-
-#define PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */
-
-/*
- * phy db - configure operational ucode
- */
-struct iwl_phy_db_cmd {
- __le16 type;
- __le16 length;
- u8 data[];
-} __packed;
-
-/* for parsing of tx power channel group data that comes from the firmware*/
-struct iwl_phy_db_chg_txp {
- __le32 space;
- __le16 max_channel_idx;
-} __packed;
-
-/*
- * phy db - Receive phy db chunk after calibrations
- */
-struct iwl_calib_res_notif_phy_db {
- __le16 type;
- __le16 length;
- u8 data[];
-} __packed;
-
-struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
-{
- struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
- GFP_KERNEL);
-
- if (!phy_db)
- return phy_db;
-
- phy_db->trans = trans;
-
- /* TODO: add default values of the phy db. */
- return phy_db;
-}
-IWL_EXPORT_SYMBOL(iwl_phy_db_init);
-
-/*
- * get phy db section: returns a pointer to a phy db section specified by
- * type and channel group id.
- */
-static struct iwl_phy_db_entry *
-iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
- enum iwl_phy_db_section_type type,
- u16 chg_id)
-{
- if (!phy_db || type >= IWL_PHY_DB_MAX)
- return NULL;
-
- switch (type) {
- case IWL_PHY_DB_CFG:
- return &phy_db->cfg;
- case IWL_PHY_DB_CALIB_NCH:
- return &phy_db->calib_nch;
- case IWL_PHY_DB_CALIB_CHG_PAPD:
- if (chg_id >= IWL_NUM_PAPD_CH_GROUPS)
- return NULL;
- return &phy_db->calib_ch_group_papd[chg_id];
- case IWL_PHY_DB_CALIB_CHG_TXP:
- if (chg_id >= IWL_NUM_TXP_CH_GROUPS)
- return NULL;
- return &phy_db->calib_ch_group_txp[chg_id];
- default:
- return NULL;
- }
- return NULL;
-}
-
-static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
- enum iwl_phy_db_section_type type,
- u16 chg_id)
-{
- struct iwl_phy_db_entry *entry =
- iwl_phy_db_get_section(phy_db, type, chg_id);
- if (!entry)
- return;
-
- kfree(entry->data);
- entry->data = NULL;
- entry->size = 0;
-}
-
-void iwl_phy_db_free(struct iwl_phy_db *phy_db)
-{
- int i;
-
- if (!phy_db)
- return;
-
- 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);
- 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++)
- iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
-
- kfree(phy_db);
-}
-IWL_EXPORT_SYMBOL(iwl_phy_db_free);
-
-int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
- gfp_t alloc_ctx)
-{
- struct iwl_calib_res_notif_phy_db *phy_db_notif =
- (struct iwl_calib_res_notif_phy_db *)pkt->data;
- enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type);
- u16 size = le16_to_cpu(phy_db_notif->length);
- struct iwl_phy_db_entry *entry;
- u16 chg_id = 0;
-
- if (!phy_db)
- return -EINVAL;
-
- if (type == IWL_PHY_DB_CALIB_CHG_PAPD ||
- type == IWL_PHY_DB_CALIB_CHG_TXP)
- chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
-
- entry = iwl_phy_db_get_section(phy_db, type, chg_id);
- if (!entry)
- return -EINVAL;
-
- kfree(entry->data);
- entry->data = kmemdup(phy_db_notif->data, size, alloc_ctx);
- if (!entry->data) {
- entry->size = 0;
- return -ENOMEM;
- }
-
- entry->size = size;
-
- IWL_DEBUG_INFO(phy_db->trans,
- "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
- __func__, __LINE__, type, size);
-
- return 0;
-}
-IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
-
-static int is_valid_channel(u16 ch_id)
-{
- if (ch_id <= 14 ||
- (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
- (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
- (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
- return 1;
- return 0;
-}
-
-static u8 ch_id_to_ch_index(u16 ch_id)
-{
- if (WARN_ON(!is_valid_channel(ch_id)))
- return 0xff;
-
- if (ch_id <= 14)
- return ch_id - 1;
- if (ch_id <= 64)
- return (ch_id + 20) / 4;
- if (ch_id <= 140)
- return (ch_id - 12) / 4;
- return (ch_id - 13) / 4;
-}
-
-
-static u16 channel_id_to_papd(u16 ch_id)
-{
- if (WARN_ON(!is_valid_channel(ch_id)))
- return 0xff;
-
- if (1 <= ch_id && ch_id <= 14)
- return 0;
- if (36 <= ch_id && ch_id <= 64)
- return 1;
- if (100 <= ch_id && ch_id <= 140)
- return 2;
- return 3;
-}
-
-static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
-{
- struct iwl_phy_db_chg_txp *txp_chg;
- int i;
- u8 ch_index = ch_id_to_ch_index(ch_id);
- if (ch_index == 0xff)
- return 0xff;
-
- for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) {
- txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
- if (!txp_chg)
- return 0xff;
- /*
- * Looking for the first channel group that its max channel is
- * higher then wanted channel.
- */
- if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
- return i;
- }
- return 0xff;
-}
-static
-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;
- u16 ch_group_id = 0;
-
- if (!phy_db)
- return -EINVAL;
-
- /* find wanted channel group */
- if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
- ch_group_id = channel_id_to_papd(ch_id);
- else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
- ch_group_id = channel_id_to_txp(phy_db, ch_id);
-
- entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
- if (!entry)
- return -EINVAL;
-
- *data = entry->data;
- *size = entry->size;
-
- IWL_DEBUG_INFO(phy_db->trans,
- "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
- __func__, __LINE__, type, *size);
-
- return 0;
-}
-
-static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
- u16 length, void *data)
-{
- struct iwl_phy_db_cmd phy_db_cmd;
- struct iwl_host_cmd cmd = {
- .id = PHY_DB_CMD,
- };
-
- IWL_DEBUG_INFO(phy_db->trans,
- "Sending PHY-DB hcmd of type %d, of length %d\n",
- type, length);
-
- /* Set phy db cmd variables */
- phy_db_cmd.type = cpu_to_le16(type);
- phy_db_cmd.length = cpu_to_le16(length);
-
- /* Set hcmd variables */
- cmd.data[0] = &phy_db_cmd;
- cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
- cmd.data[1] = data;
- cmd.len[1] = length;
- cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
-
- return iwl_trans_send_cmd(phy_db->trans, &cmd);
-}
-
-static int iwl_phy_db_send_all_channel_groups(
- struct iwl_phy_db *phy_db,
- enum iwl_phy_db_section_type type,
- u8 max_ch_groups)
-{
- u16 i;
- int err;
- struct iwl_phy_db_entry *entry;
-
- /* Send all the channel specific groups to operational fw */
- for (i = 0; i < max_ch_groups; i++) {
- entry = iwl_phy_db_get_section(phy_db,
- type,
- i);
- if (!entry)
- return -EINVAL;
-
- if (!entry->size)
- continue;
-
- /* Send the requested PHY DB section */
- err = iwl_send_phy_db_cmd(phy_db,
- type,
- entry->size,
- entry->data);
- if (err) {
- IWL_ERR(phy_db->trans,
- "Can't SEND phy_db section %d (%d), err %d\n",
- type, i, err);
- return err;
- }
-
- IWL_DEBUG_INFO(phy_db->trans,
- "Sent PHY_DB HCMD, type = %d num = %d\n",
- type, i);
- }
-
- return 0;
-}
-
-int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
-{
- u8 *data = NULL;
- u16 size = 0;
- int err;
-
- IWL_DEBUG_INFO(phy_db->trans,
- "Sending phy db data and configuration to runtime image\n");
-
- /* Send PHY DB CFG section */
- err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
- &data, &size, 0);
- if (err) {
- IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
- return err;
- }
-
- err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
- if (err) {
- IWL_ERR(phy_db->trans,
- "Cannot send HCMD of Phy DB cfg section\n");
- return err;
- }
-
- err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
- &data, &size, 0);
- if (err) {
- IWL_ERR(phy_db->trans,
- "Cannot get Phy DB non specific channel section\n");
- return err;
- }
-
- err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
- if (err) {
- IWL_ERR(phy_db->trans,
- "Cannot send HCMD of Phy DB non specific channel section\n");
- return err;
- }
-
- /* Send all the TXP channel specific data */
- err = iwl_phy_db_send_all_channel_groups(phy_db,
- IWL_PHY_DB_CALIB_CHG_PAPD,
- IWL_NUM_PAPD_CH_GROUPS);
- if (err) {
- IWL_ERR(phy_db->trans,
- "Cannot send channel specific PAPD groups\n");
- return err;
- }
-
- /* Send all the TXP channel specific data */
- err = iwl_phy_db_send_all_channel_groups(phy_db,
- IWL_PHY_DB_CALIB_CHG_TXP,
- IWL_NUM_TXP_CH_GROUPS);
- if (err) {
- IWL_ERR(phy_db->trans,
- "Cannot send channel specific TX power groups\n");
- return err;
- }
-
- IWL_DEBUG_INFO(phy_db->trans,
- "Finished sending phy db non channel data\n");
- return 0;
-}
-IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
deleted file mode 100644
index 9ee18d0d2d01..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-phy-db.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __IWL_PHYDB_H__
-#define __IWL_PHYDB_H__
-
-#include <linux/types.h>
-
-#include "iwl-op-mode.h"
-#include "iwl-trans.h"
-
-struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans);
-
-void iwl_phy_db_free(struct iwl_phy_db *phy_db);
-
-int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
- gfp_t alloc_ctx);
-
-
-int iwl_send_phy_db_data(struct iwl_phy_db *phy_db);
-
-#endif /* __IWL_PHYDB_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
deleted file mode 100644
index 3ab777f79e4f..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ /dev/null
@@ -1,401 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __iwl_prph_h__
-#define __iwl_prph_h__
-
-/*
- * Registers in this file are internal, not PCI bus memory mapped.
- * Driver accesses these via HBUS_TARG_PRPH_* registers.
- */
-#define PRPH_BASE (0x00000)
-#define PRPH_END (0xFFFFF)
-
-/* APMG (power management) constants */
-#define APMG_BASE (PRPH_BASE + 0x3000)
-#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000)
-#define APMG_CLK_EN_REG (APMG_BASE + 0x0004)
-#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008)
-#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c)
-#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010)
-#define APMG_RFKILL_REG (APMG_BASE + 0x0014)
-#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c)
-#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020)
-#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058)
-#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C)
-
-#define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001)
-#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200)
-#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800)
-
-#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000)
-#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000)
-#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000)
-#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000)
-#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000)
-#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */
-#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060)
-
-#define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200)
-#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800)
-#define APMG_PCIDEV_STT_VAL_WAKE_ME (0x00004000)
-
-#define APMG_RTC_INT_STT_RFKILL (0x10000000)
-
-/* Device system time */
-#define DEVICE_SYSTEM_TIME_REG 0xA0206C
-
-/* Device NMI register */
-#define DEVICE_SET_NMI_REG 0x00a01c30
-#define DEVICE_SET_NMI_VAL_HW BIT(0)
-#define DEVICE_SET_NMI_VAL_DRV BIT(7)
-#define DEVICE_SET_NMI_8000_REG 0x00a01c24
-#define DEVICE_SET_NMI_8000_VAL 0x1000000
-
-/* Shared registers (0x0..0x3ff, via target indirect or periphery */
-#define SHR_BASE 0x00a10000
-
-/* Shared GP1 register */
-#define SHR_APMG_GP1_REG 0x01dc
-#define SHR_APMG_GP1_REG_PRPH (SHR_BASE + SHR_APMG_GP1_REG)
-#define SHR_APMG_GP1_WF_XTAL_LP_EN 0x00000004
-#define SHR_APMG_GP1_CHICKEN_BIT_SELECT 0x80000000
-
-/* Shared DL_CFG register */
-#define SHR_APMG_DL_CFG_REG 0x01c4
-#define SHR_APMG_DL_CFG_REG_PRPH (SHR_BASE + SHR_APMG_DL_CFG_REG)
-#define SHR_APMG_DL_CFG_RTCS_CLK_SELECTOR_MSK 0x000000c0
-#define SHR_APMG_DL_CFG_RTCS_CLK_INTERNAL_XTAL 0x00000080
-#define SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP 0x00000100
-
-/* Shared APMG_XTAL_CFG register */
-#define SHR_APMG_XTAL_CFG_REG 0x1c0
-#define SHR_APMG_XTAL_CFG_XTAL_ON_REQ 0x80000000
-
-/*
- * Device reset for family 8000
- * write to bit 24 in order to reset the CPU
-*/
-#define RELEASE_CPU_RESET (0x300C)
-#define RELEASE_CPU_RESET_BIT BIT(24)
-
-/*****************************************************************************
- * 7000/3000 series SHR DTS addresses *
- *****************************************************************************/
-
-#define SHR_MISC_WFM_DTS_EN (0x00a10024)
-#define DTSC_CFG_MODE (0x00a10604)
-#define DTSC_VREF_AVG (0x00a10648)
-#define DTSC_VREF5_AVG (0x00a1064c)
-#define DTSC_CFG_MODE_PERIODIC (0x2)
-#define DTSC_PTAT_AVG (0x00a10650)
-
-
-/**
- * Tx Scheduler
- *
- * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs
- * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in
- * host DRAM. It steers each frame's Tx command (which contains the frame
- * data) into one of up to 7 prioritized Tx DMA FIFO channels within the
- * device. A queue maps to only one (selectable by driver) Tx DMA channel,
- * but one DMA channel may take input from several queues.
- *
- * Tx DMA FIFOs have dedicated purposes.
- *
- * For 5000 series and up, they are used differently
- * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c):
- *
- * 0 -- EDCA BK (background) frames, lowest priority
- * 1 -- EDCA BE (best effort) frames, normal priority
- * 2 -- EDCA VI (video) frames, higher priority
- * 3 -- EDCA VO (voice) and management frames, highest priority
- * 4 -- unused
- * 5 -- unused
- * 6 -- unused
- * 7 -- Commands
- *
- * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6.
- * In addition, driver can map the remaining queues to Tx DMA/FIFO
- * channels 0-3 to support 11n aggregation via EDCA DMA channels.
- *
- * The driver sets up each queue to work in one of two modes:
- *
- * 1) Scheduler-Ack, in which the scheduler automatically supports a
- * block-ack (BA) window of up to 64 TFDs. In this mode, each queue
- * contains TFDs for a unique combination of Recipient Address (RA)
- * and Traffic Identifier (TID), that is, traffic of a given
- * Quality-Of-Service (QOS) priority, destined for a single station.
- *
- * In scheduler-ack mode, the scheduler keeps track of the Tx status of
- * each frame within the BA window, including whether it's been transmitted,
- * and whether it's been acknowledged by the receiving station. The device
- * automatically processes block-acks received from the receiving STA,
- * and reschedules un-acked frames to be retransmitted (successful
- * Tx completion may end up being out-of-order).
- *
- * The driver must maintain the queue's Byte Count table in host DRAM
- * for this mode.
- * This mode does not support fragmentation.
- *
- * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order.
- * The device may automatically retry Tx, but will retry only one frame
- * at a time, until receiving ACK from receiving station, or reaching
- * retry limit and giving up.
- *
- * The command queue (#4/#9) must use this mode!
- * This mode does not require use of the Byte Count table in host DRAM.
- *
- * Driver controls scheduler operation via 3 means:
- * 1) Scheduler registers
- * 2) Shared scheduler data base in internal SRAM
- * 3) Shared data in host DRAM
- *
- * Initialization:
- *
- * When loading, driver should allocate memory for:
- * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs.
- * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory
- * (1024 bytes for each queue).
- *
- * After receiving "Alive" response from uCode, driver must initialize
- * the scheduler (especially for queue #4/#9, the command queue, otherwise
- * the driver can't issue commands!):
- */
-#define SCD_MEM_LOWER_BOUND (0x0000)
-
-/**
- * Max Tx window size is the max number of contiguous TFDs that the scheduler
- * can keep track of at one time when creating block-ack chains of frames.
- * Note that "64" matches the number of ack bits in a block-ack packet.
- */
-#define SCD_WIN_SIZE 64
-#define SCD_FRAME_LIMIT 64
-
-#define SCD_TXFIFO_POS_TID (0)
-#define SCD_TXFIFO_POS_RA (4)
-#define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF)
-
-/* agn SCD */
-#define SCD_QUEUE_STTS_REG_POS_TXF (0)
-#define SCD_QUEUE_STTS_REG_POS_ACTIVE (3)
-#define SCD_QUEUE_STTS_REG_POS_WSL (4)
-#define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19)
-#define SCD_QUEUE_STTS_REG_MSK (0x017F0000)
-
-#define SCD_QUEUE_CTX_REG1_CREDIT_POS (8)
-#define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00)
-#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24)
-#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000)
-#define SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0)
-#define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F)
-#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16)
-#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000)
-#define SCD_GP_CTRL_ENABLE_31_QUEUES BIT(0)
-#define SCD_GP_CTRL_AUTO_ACTIVE_MODE BIT(18)
-
-/* Context Data */
-#define SCD_CONTEXT_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x600)
-#define SCD_CONTEXT_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0)
-
-/* Tx status */
-#define SCD_TX_STTS_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0)
-#define SCD_TX_STTS_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0)
-
-/* Translation Data */
-#define SCD_TRANS_TBL_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0)
-#define SCD_TRANS_TBL_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x808)
-
-#define SCD_CONTEXT_QUEUE_OFFSET(x)\
- (SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8))
-
-#define SCD_TX_STTS_QUEUE_OFFSET(x)\
- (SCD_TX_STTS_MEM_LOWER_BOUND + ((x) * 16))
-
-#define SCD_TRANS_TBL_OFFSET_QUEUE(x) \
- ((SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc)
-
-#define SCD_BASE (PRPH_BASE + 0xa02c00)
-
-#define SCD_SRAM_BASE_ADDR (SCD_BASE + 0x0)
-#define SCD_DRAM_BASE_ADDR (SCD_BASE + 0x8)
-#define SCD_AIT (SCD_BASE + 0x0c)
-#define SCD_TXFACT (SCD_BASE + 0x10)
-#define SCD_ACTIVE (SCD_BASE + 0x14)
-#define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8)
-#define SCD_CHAINEXT_EN (SCD_BASE + 0x244)
-#define SCD_AGGR_SEL (SCD_BASE + 0x248)
-#define SCD_INTERRUPT_MASK (SCD_BASE + 0x108)
-#define SCD_GP_CTRL (SCD_BASE + 0x1a8)
-#define SCD_EN_CTRL (SCD_BASE + 0x254)
-
-/*********************** END TX SCHEDULER *************************************/
-
-/* tcp checksum offload */
-#define RX_EN_CSUM (0x00a00d88)
-
-/* Oscillator clock */
-#define OSC_CLK (0xa04068)
-#define OSC_CLK_FORCE_CONTROL (0x8)
-
-#define FH_UCODE_LOAD_STATUS (0x1AF0)
-#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70)
-enum secure_load_status_reg {
- LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001,
- LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003,
- LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007,
- LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8,
- LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00,
-};
-
-#define LMPM_SECURE_INSPECTOR_CODE_ADDR (0x1E38)
-#define LMPM_SECURE_INSPECTOR_DATA_ADDR (0x1E3C)
-#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78)
-#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C)
-
-#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE (0x400000)
-#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE (0x402000)
-#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000)
-#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400)
-
-/* Rx FIFO */
-#define RXF_SIZE_ADDR (0xa00c88)
-#define RXF_RD_D_SPACE (0xa00c40)
-#define RXF_RD_WR_PTR (0xa00c50)
-#define RXF_RD_RD_PTR (0xa00c54)
-#define RXF_RD_FENCE_PTR (0xa00c4c)
-#define RXF_SET_FENCE_MODE (0xa00c14)
-#define RXF_LD_WR2FENCE (0xa00c1c)
-#define RXF_FIFO_RD_FENCE_INC (0xa00c68)
-#define RXF_SIZE_BYTE_CND_POS (7)
-#define RXF_SIZE_BYTE_CNT_MSK (0x3ff << RXF_SIZE_BYTE_CND_POS)
-#define RXF_DIFF_FROM_PREV (0x200)
-
-#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10)
-#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c)
-
-/* Tx FIFO */
-#define TXF_FIFO_ITEM_CNT (0xa00438)
-#define TXF_WR_PTR (0xa00414)
-#define TXF_RD_PTR (0xa00410)
-#define TXF_FENCE_PTR (0xa00418)
-#define TXF_LOCK_FENCE (0xa00424)
-#define TXF_LARC_NUM (0xa0043c)
-#define TXF_READ_MODIFY_DATA (0xa00448)
-#define TXF_READ_MODIFY_ADDR (0xa0044c)
-
-/* FW monitor */
-#define MON_BUFF_SAMPLE_CTL (0xa03c00)
-#define MON_BUFF_BASE_ADDR (0xa03c3c)
-#define MON_BUFF_END_ADDR (0xa03c40)
-#define MON_BUFF_WRPTR (0xa03c44)
-#define MON_BUFF_CYCLE_CNT (0xa03c48)
-
-#define MON_DMARB_RD_CTL_ADDR (0xa03c60)
-#define MON_DMARB_RD_DATA_ADDR (0xa03c5c)
-
-#define DBGC_IN_SAMPLE (0xa03c00)
-
-/* enable the ID buf for read */
-#define WFPM_PS_CTL_CLR 0xA0300C
-#define WFMP_MAC_ADDR_0 0xA03080
-#define WFMP_MAC_ADDR_1 0xA03084
-#define LMPM_PMG_EN 0xA01CEC
-#define RADIO_REG_SYS_MANUAL_DFT_0 0xAD4078
-#define RFIC_REG_RD 0xAD0470
-#define WFPM_CTRL_REG 0xA03030
-enum {
- ENABLE_WFPM = BIT(31),
- WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000,
-};
-
-#define AUX_MISC_REG 0xA200B0
-enum {
- HW_STEP_LOCATION_BITS = 24,
-};
-
-#define AUX_MISC_MASTER1_EN 0xA20818
-enum aux_misc_master1_en {
- AUX_MISC_MASTER1_EN_SBE_MSK = 0x1,
-};
-
-#define AUX_MISC_MASTER1_SMPHR_STATUS 0xA20800
-#define RSA_ENABLE 0xA24B08
-#define PREG_AUX_BUS_WPROT_0 0xA04CC0
-#define SB_CPU_1_STATUS 0xA01E30
-#define SB_CPU_2_STATUS 0xA01E34
-
-/* FW chicken bits */
-#define LMPM_CHICK 0xA01FF8
-enum {
- LMPM_CHICK_EXTENDED_ADDR_SPACE = BIT(0),
-};
-
-/* FW chicken bits */
-#define LMPM_PAGE_PASS_NOTIF 0xA03824
-enum {
- LMPM_PAGE_PASS_NOTIF_POS = BIT(20),
-};
-
-#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-scd.h b/drivers/net/wireless/iwlwifi/iwl-scd.h
deleted file mode 100644
index f2353ebf2666..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-scd.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __iwl_scd_h__
-#define __iwl_scd_h__
-
-#include "iwl-trans.h"
-#include "iwl-io.h"
-#include "iwl-prph.h"
-
-
-static inline void iwl_scd_txq_set_chain(struct iwl_trans *trans,
- u16 txq_id)
-{
- iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id));
-}
-
-static inline void iwl_scd_txq_enable_agg(struct iwl_trans *trans,
- u16 txq_id)
-{
- iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
-}
-
-static inline void iwl_scd_txq_disable_agg(struct iwl_trans *trans,
- u16 txq_id)
-{
- iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
-}
-
-static inline void iwl_scd_disable_agg(struct iwl_trans *trans)
-{
- iwl_set_bits_prph(trans, SCD_AGGR_SEL, 0);
-}
-
-static inline void iwl_scd_activate_fifos(struct iwl_trans *trans)
-{
- iwl_write_prph(trans, SCD_TXFACT, IWL_MASK(0, 7));
-}
-
-static inline void iwl_scd_deactivate_fifos(struct iwl_trans *trans)
-{
- iwl_write_prph(trans, SCD_TXFACT, 0);
-}
-
-static inline void iwl_scd_enable_set_active(struct iwl_trans *trans,
- u32 value)
-{
- iwl_write_prph(trans, SCD_EN_CTRL, value);
-}
-
-static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl)
-{
- if (chnl < 20)
- return SCD_BASE + 0x18 + chnl * 4;
- WARN_ON_ONCE(chnl >= 32);
- return SCD_BASE + 0x284 + (chnl - 20) * 4;
-}
-
-static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl)
-{
- if (chnl < 20)
- return SCD_BASE + 0x68 + chnl * 4;
- WARN_ON_ONCE(chnl >= 32);
- return SCD_BASE + 0x2B4 + chnl * 4;
-}
-
-static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
-{
- if (chnl < 20)
- return SCD_BASE + 0x10c + chnl * 4;
- WARN_ON_ONCE(chnl >= 32);
- return SCD_BASE + 0x334 + chnl * 4;
-}
-
-static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans,
- u16 txq_id)
-{
- iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
- (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
- (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
-}
-
-#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c
deleted file mode 100644
index 71610968c365..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-trans.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/kernel.h>
-#include "iwl-trans.h"
-
-struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
- struct device *dev,
- const struct iwl_cfg *cfg,
- const struct iwl_trans_ops *ops,
- size_t dev_cmd_headroom)
-{
- struct iwl_trans *trans;
-#ifdef CONFIG_LOCKDEP
- static struct lock_class_key __key;
-#endif
-
- trans = kzalloc(sizeof(*trans) + priv_size, GFP_KERNEL);
- if (!trans)
- return NULL;
-
-#ifdef CONFIG_LOCKDEP
- lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
- &__key, 0);
-#endif
-
- trans->dev = dev;
- trans->cfg = cfg;
- trans->ops = ops;
- trans->dev_cmd_headroom = dev_cmd_headroom;
- trans->num_rx_queues = 1;
-
- snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
- "iwl_cmd_pool:%s", dev_name(trans->dev));
- trans->dev_cmd_pool =
- kmem_cache_create(trans->dev_cmd_pool_name,
- sizeof(struct iwl_device_cmd)
- + trans->dev_cmd_headroom,
- sizeof(void *),
- SLAB_HWCACHE_ALIGN,
- NULL);
- if (!trans->dev_cmd_pool)
- goto free;
-
- return trans;
- free:
- kfree(trans);
- return NULL;
-}
-
-void iwl_trans_free(struct iwl_trans *trans)
-{
- kmem_cache_destroy(trans->dev_cmd_pool);
- kfree(trans);
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
deleted file mode 100644
index 6f76525088f0..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ /dev/null
@@ -1,1125 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_trans_h__
-#define __iwl_trans_h__
-
-#include <linux/ieee80211.h>
-#include <linux/mm.h> /* for page_address */
-#include <linux/lockdep.h>
-
-#include "iwl-debug.h"
-#include "iwl-config.h"
-#include "iwl-fw.h"
-#include "iwl-op-mode.h"
-
-/**
- * DOC: Transport layer - what is it ?
- *
- * The transport layer is the layer that deals with the HW directly. It provides
- * an abstraction of the underlying HW to the upper layer. The transport layer
- * doesn't provide any policy, algorithm or anything of this kind, but only
- * mechanisms to make the HW do something. It is not completely stateless but
- * close to it.
- * We will have an implementation for each different supported bus.
- */
-
-/**
- * DOC: Life cycle of the transport layer
- *
- * The transport layer has a very precise life cycle.
- *
- * 1) A helper function is called during the module initialization and
- * registers the bus driver's ops with the transport's alloc function.
- * 2) Bus's probe calls to the transport layer's allocation functions.
- * Of course this function is bus specific.
- * 3) This allocation functions will spawn the upper layer which will
- * register mac80211.
- *
- * 4) At some point (i.e. mac80211's start call), the op_mode will call
- * the following sequence:
- * start_hw
- * start_fw
- *
- * 5) Then when finished (or reset):
- * stop_device
- *
- * 6) Eventually, the free function will be called.
- */
-
-/**
- * DOC: Host command section
- *
- * A host command is a command issued by the upper layer to the fw. There are
- * several versions of fw that have several APIs. The transport layer is
- * completely agnostic to these differences.
- * The transport does provide helper functionality (i.e. SYNC / ASYNC mode),
- */
-#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f)
-#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8)
-#define SEQ_TO_INDEX(s) ((s) & 0xff)
-#define INDEX_TO_SEQ(i) ((i) & 0xff)
-#define SEQ_RX_FRAME cpu_to_le16(0x8000)
-
-/*
- * those functions retrieve specific information from
- * the id field in the iwl_host_cmd struct which contains
- * the command id, the group id and the version of the command
- * and vice versa
-*/
-static inline u8 iwl_cmd_opcode(u32 cmdid)
-{
- return cmdid & 0xFF;
-}
-
-static inline u8 iwl_cmd_groupid(u32 cmdid)
-{
- return ((cmdid & 0xFF00) >> 8);
-}
-
-static inline u8 iwl_cmd_version(u32 cmdid)
-{
- return ((cmdid & 0xFF0000) >> 16);
-}
-
-static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version)
-{
- return opcode + (groupid << 8) + (version << 16);
-}
-
-/* make u16 wide id out of u8 group and opcode */
-#define WIDE_ID(grp, opcode) ((grp << 8) | opcode)
-
-/* due to the conversion, this group is special; new groups
- * should be defined in the appropriate fw-api header files
- */
-#define IWL_ALWAYS_LONG_GROUP 1
-
-/**
- * struct iwl_cmd_header
- *
- * This header format appears in the beginning of each command sent from the
- * driver, and each response/notification received from uCode.
- */
-struct iwl_cmd_header {
- u8 cmd; /* Command ID: REPLY_RXON, etc. */
- u8 group_id;
- /*
- * The driver sets up the sequence number to values of its choosing.
- * uCode does not use this value, but passes it back to the driver
- * when sending the response to each driver-originated command, so
- * the driver can match the response to the command. Since the values
- * don't get used by uCode, the driver may set up an arbitrary format.
- *
- * There is one exception: uCode sets bit 15 when it originates
- * the response/notification, i.e. when the response/notification
- * is not a direct response to a command sent by the driver. For
- * example, uCode issues REPLY_RX when it sends a received frame
- * to the driver; it is not a direct response to any driver command.
- *
- * The Linux driver uses the following format:
- *
- * 0:7 tfd index - position within TX queue
- * 8:12 TX queue id
- * 13:14 reserved
- * 15 unsolicited RX or uCode-originated notification
- */
- __le16 sequence;
-} __packed;
-
-/**
- * struct iwl_cmd_header_wide
- *
- * This header format appears in the beginning of each command sent from the
- * driver, and each response/notification received from uCode.
- * this is the wide version that contains more information about the command
- * like length, version and command type
- */
-struct iwl_cmd_header_wide {
- u8 cmd;
- u8 group_id;
- __le16 sequence;
- __le16 length;
- u8 reserved;
- u8 version;
-} __packed;
-
-#define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */
-#define FH_RSCSR_FRAME_INVALID 0x55550000
-#define FH_RSCSR_FRAME_ALIGN 0x40
-
-struct iwl_rx_packet {
- /*
- * The first 4 bytes of the RX frame header contain both the RX frame
- * size and some flags.
- * Bit fields:
- * 31: flag flush RB request
- * 30: flag ignore TC (terminal counter) request
- * 29: flag fast IRQ request
- * 28-14: Reserved
- * 13-00: RX frame size
- */
- __le32 len_n_flags;
- struct iwl_cmd_header hdr;
- u8 data[];
-} __packed;
-
-static inline u32 iwl_rx_packet_len(const struct iwl_rx_packet *pkt)
-{
- return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
-}
-
-static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
-{
- return iwl_rx_packet_len(pkt) - sizeof(pkt->hdr);
-}
-
-/**
- * enum CMD_MODE - how to send the host commands ?
- *
- * @CMD_ASYNC: Return right away and don't wait for the response
- * @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of
- * the response. The caller needs to call iwl_free_resp when done.
- * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
- * command queue, but after other high priority commands. Valid only
- * with CMD_ASYNC.
- * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
- * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
- * @CMD_WAKE_UP_TRANS: The command response should wake up the trans
- * (i.e. mark it as non-idle).
- * @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to
- * check that we leave enough room for the TBs bitmap which needs 20 bits.
- */
-enum CMD_MODE {
- CMD_ASYNC = BIT(0),
- CMD_WANT_SKB = BIT(1),
- CMD_SEND_IN_RFKILL = BIT(2),
- CMD_HIGH_PRIO = BIT(3),
- CMD_SEND_IN_IDLE = BIT(4),
- CMD_MAKE_TRANS_IDLE = BIT(5),
- CMD_WAKE_UP_TRANS = BIT(6),
-
- CMD_TB_BITMAP_POS = 11,
-};
-
-#define DEF_CMD_PAYLOAD_SIZE 320
-
-/**
- * struct iwl_device_cmd
- *
- * For allocation of the command and tx queues, this establishes the overall
- * size of the largest command we send to uCode, except for commands that
- * aren't fully copied and use other TFD space.
- */
-struct iwl_device_cmd {
- union {
- struct {
- struct iwl_cmd_header hdr; /* uCode API */
- u8 payload[DEF_CMD_PAYLOAD_SIZE];
- };
- struct {
- struct iwl_cmd_header_wide hdr_wide;
- u8 payload_wide[DEF_CMD_PAYLOAD_SIZE -
- sizeof(struct iwl_cmd_header_wide) +
- sizeof(struct iwl_cmd_header)];
- };
- };
-} __packed;
-
-#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd))
-
-/*
- * number of transfer buffers (fragments) per transmit frame descriptor;
- * this is just the driver's idea, the hardware supports 20
- */
-#define IWL_MAX_CMD_TBS_PER_TFD 2
-
-/**
- * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command
- *
- * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's
- * ring. The transport layer doesn't map the command's buffer to DMA, but
- * rather copies it to a previously allocated DMA buffer. This flag tells
- * the transport layer not to copy the command, but to map the existing
- * buffer (that is passed in) instead. This saves the memcpy and allows
- * commands that are bigger than the fixed buffer to be submitted.
- * Note that a TFD entry after a NOCOPY one cannot be a normal copied one.
- * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this
- * chunk internally and free it again after the command completes. This
- * can (currently) be used only once per command.
- * Note that a TFD entry after a DUP one cannot be a normal copied one.
- */
-enum iwl_hcmd_dataflag {
- IWL_HCMD_DFL_NOCOPY = BIT(0),
- IWL_HCMD_DFL_DUP = BIT(1),
-};
-
-/**
- * struct iwl_host_cmd - Host command to the uCode
- *
- * @data: array of chunks that composes the data of the host command
- * @resp_pkt: response packet, if %CMD_WANT_SKB was set
- * @_rx_page_order: (internally used to free response packet)
- * @_rx_page_addr: (internally used to free response packet)
- * @flags: can be CMD_*
- * @len: array of the lengths of the chunks in data
- * @dataflags: IWL_HCMD_DFL_*
- * @id: command id of the host command, for wide commands encoding the
- * version and group as well
- */
-struct iwl_host_cmd {
- const void *data[IWL_MAX_CMD_TBS_PER_TFD];
- struct iwl_rx_packet *resp_pkt;
- unsigned long _rx_page_addr;
- u32 _rx_page_order;
-
- u32 flags;
- u32 id;
- u16 len[IWL_MAX_CMD_TBS_PER_TFD];
- u8 dataflags[IWL_MAX_CMD_TBS_PER_TFD];
-};
-
-static inline void iwl_free_resp(struct iwl_host_cmd *cmd)
-{
- free_pages(cmd->_rx_page_addr, cmd->_rx_page_order);
-}
-
-struct iwl_rx_cmd_buffer {
- struct page *_page;
- int _offset;
- bool _page_stolen;
- u32 _rx_page_order;
- unsigned int truesize;
-};
-
-static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r)
-{
- return (void *)((unsigned long)page_address(r->_page) + r->_offset);
-}
-
-static inline int rxb_offset(struct iwl_rx_cmd_buffer *r)
-{
- return r->_offset;
-}
-
-static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
-{
- r->_page_stolen = true;
- get_page(r->_page);
- return r->_page;
-}
-
-static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r)
-{
- __free_pages(r->_page, r->_rx_page_order);
-}
-
-#define MAX_NO_RECLAIM_CMDS 6
-
-#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
-
-/*
- * Maximum number of HW queues the transport layer
- * currently supports
- */
-#define IWL_MAX_HW_QUEUES 32
-#define IWL_MAX_TID_COUNT 8
-#define IWL_FRAME_LIMIT 64
-#define IWL_MAX_RX_HW_QUEUES 16
-
-/**
- * enum iwl_wowlan_status - WoWLAN image/device status
- * @IWL_D3_STATUS_ALIVE: firmware is still running after resume
- * @IWL_D3_STATUS_RESET: device was reset while suspended
- */
-enum iwl_d3_status {
- IWL_D3_STATUS_ALIVE,
- IWL_D3_STATUS_RESET,
-};
-
-/**
- * enum iwl_trans_status: transport status flags
- * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed
- * @STATUS_DEVICE_ENABLED: APM is enabled
- * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
- * @STATUS_INT_ENABLED: interrupts are enabled
- * @STATUS_RFKILL: the HW RFkill switch is in KILL position
- * @STATUS_FW_ERROR: the fw is in error state
- * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands
- * are sent
- * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
- * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation
- */
-enum iwl_trans_status {
- STATUS_SYNC_HCMD_ACTIVE,
- STATUS_DEVICE_ENABLED,
- STATUS_TPOWER_PMI,
- STATUS_INT_ENABLED,
- STATUS_RFKILL,
- STATUS_FW_ERROR,
- STATUS_TRANS_GOING_IDLE,
- STATUS_TRANS_IDLE,
- STATUS_TRANS_DEAD,
-};
-
-/**
- * struct iwl_trans_config - transport configuration
- *
- * @op_mode: pointer to the upper layer.
- * @cmd_queue: the index of the command queue.
- * Must be set before start_fw.
- * @cmd_fifo: the fifo for host commands
- * @cmd_q_wdg_timeout: the timeout of the watchdog timer for the command queue.
- * @no_reclaim_cmds: Some devices erroneously don't set the
- * SEQ_RX_FRAME bit on some notifications, this is the
- * list of such notifications to filter. Max length is
- * %MAX_NO_RECLAIM_CMDS.
- * @n_no_reclaim_cmds: # of commands in list
- * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs,
- * if unset 4k will be the RX buffer size
- * @bc_table_dword: set to true if the BC table expects the byte count to be
- * in DWORD (as opposed to bytes)
- * @scd_set_active: should the transport configure the SCD for HCMD queue
- * @wide_cmd_header: firmware supports wide host command header
- * @command_names: array of command names, must be 256 entries
- * (one for each command); for debugging only
- * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until
- * we get the ALIVE from the uCode
- */
-struct iwl_trans_config {
- struct iwl_op_mode *op_mode;
-
- u8 cmd_queue;
- u8 cmd_fifo;
- unsigned int cmd_q_wdg_timeout;
- const u8 *no_reclaim_cmds;
- unsigned int n_no_reclaim_cmds;
-
- bool rx_buf_size_8k;
- bool bc_table_dword;
- bool scd_set_active;
- bool wide_cmd_header;
- const char *const *command_names;
-
- u32 sdio_adma_addr;
-};
-
-struct iwl_trans_dump_data {
- u32 len;
- u8 data[];
-};
-
-struct iwl_trans;
-
-struct iwl_trans_txq_scd_cfg {
- u8 fifo;
- s8 sta_id;
- u8 tid;
- bool aggregate;
- int frame_limit;
-};
-
-/**
- * struct iwl_trans_ops - transport specific operations
- *
- * All the handlers MUST be implemented
- *
- * @start_hw: starts the HW. If low_power is true, the NIC needs to be taken
- * out of a low power state. From that point on, the HW can send
- * interrupts. May sleep.
- * @op_mode_leave: Turn off the HW RF kill indication if on
- * May sleep
- * @start_fw: allocates and inits all the resources for the transport
- * layer. Also kick a fw image.
- * May sleep
- * @fw_alive: called when the fw sends alive notification. If the fw provides
- * the SCD base address in SRAM, then provide it here, or 0 otherwise.
- * May sleep
- * @stop_device: stops the whole device (embedded CPU put to reset) and stops
- * the HW. If low_power is true, the NIC will be put in low power state.
- * From that point on, the HW will be stopped but will still issue an
- * interrupt if the HW RF kill switch is triggered.
- * This callback must do the right thing and not crash even if %start_hw()
- * was called but not &start_fw(). May sleep.
- * @d3_suspend: put the device into the correct mode for WoWLAN during
- * suspend. This is optional, if not implemented WoWLAN will not be
- * supported. This callback may sleep.
- * @d3_resume: resume the device after WoWLAN, enabling the opmode to
- * talk to the WoWLAN image to get its status. This is optional, if not
- * implemented WoWLAN will not be supported. This callback may sleep.
- * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted.
- * If RFkill is asserted in the middle of a SYNC host command, it must
- * return -ERFKILL straight away.
- * May sleep only if CMD_ASYNC is not set
- * @tx: send an skb
- * Must be atomic
- * @reclaim: free packet until ssn. Returns a list of freed packets.
- * Must be atomic
- * @txq_enable: setup a queue. To setup an AC queue, use the
- * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before
- * this one. The op_mode must not configure the HCMD queue. The scheduler
- * configuration may be %NULL, in which case the hardware will not be
- * configured. May sleep.
- * @txq_disable: de-configure a Tx queue to send AMPDUs
- * Must be atomic
- * @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
- * @freeze_txq_timer: prevents the timer of the queue from firing until the
- * queue is set to awake. Must be atomic.
- * @dbgfs_register: add the dbgfs files under this directory. Files will be
- * automatically deleted.
- * @write8: write a u8 to a register at offset ofs from the BAR
- * @write32: write a u32 to a register at offset ofs from the BAR
- * @read32: read a u32 register at offset ofs from the BAR
- * @read_prph: read a DWORD from a periphery register
- * @write_prph: write a DWORD to a periphery register
- * @read_mem: read device's SRAM in DWORD
- * @write_mem: write device's SRAM in DWORD. If %buf is %NULL, then the memory
- * will be zeroed.
- * @configure: configure parameters required by the transport layer from
- * the op_mode. May be called several times before start_fw, can't be
- * called after that.
- * @set_pmi: set the power pmi state
- * @grab_nic_access: wake the NIC to be able to access non-HBUS regs.
- * Sleeping is not allowed between grab_nic_access and
- * release_nic_access.
- * @release_nic_access: let the NIC go to sleep. The "flags" parameter
- * must be the same one that was sent before to the grab_nic_access.
- * @set_bits_mask - set SRAM register according to value and mask.
- * @ref: grab a reference to the transport/FW layers, disallowing
- * certain low power states
- * @unref: release a reference previously taken with @ref. Note that
- * initially the reference count is 1, making an initial @unref
- * necessary to allow low power states.
- * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last
- * TX'ed commands and similar. The buffer will be vfree'd by the caller.
- * Note that the transport must fill in the proper file headers.
- */
-struct iwl_trans_ops {
-
- int (*start_hw)(struct iwl_trans *iwl_trans, bool low_power);
- void (*op_mode_leave)(struct iwl_trans *iwl_trans);
- int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
- bool run_in_rfkill);
- int (*update_sf)(struct iwl_trans *trans,
- struct iwl_sf_region *st_fwrd_space);
- void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
- void (*stop_device)(struct iwl_trans *trans, bool low_power);
-
- void (*d3_suspend)(struct iwl_trans *trans, bool test);
- int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status,
- bool test);
-
- int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
-
- int (*tx)(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, int queue);
- void (*reclaim)(struct iwl_trans *trans, int queue, int ssn,
- struct sk_buff_head *skbs);
-
- void (*txq_enable)(struct iwl_trans *trans, int queue, u16 ssn,
- const struct iwl_trans_txq_scd_cfg *cfg,
- unsigned int queue_wdg_timeout);
- void (*txq_disable)(struct iwl_trans *trans, int queue,
- bool configure_scd);
-
- int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
- int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
- void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs,
- bool freeze);
-
- void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
- void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
- u32 (*read32)(struct iwl_trans *trans, u32 ofs);
- u32 (*read_prph)(struct iwl_trans *trans, u32 ofs);
- void (*write_prph)(struct iwl_trans *trans, u32 ofs, u32 val);
- int (*read_mem)(struct iwl_trans *trans, u32 addr,
- void *buf, int dwords);
- int (*write_mem)(struct iwl_trans *trans, u32 addr,
- const void *buf, int dwords);
- void (*configure)(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg);
- void (*set_pmi)(struct iwl_trans *trans, bool state);
- bool (*grab_nic_access)(struct iwl_trans *trans, bool silent,
- unsigned long *flags);
- void (*release_nic_access)(struct iwl_trans *trans,
- unsigned long *flags);
- void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
- u32 value);
- void (*ref)(struct iwl_trans *trans);
- void (*unref)(struct iwl_trans *trans);
- int (*suspend)(struct iwl_trans *trans);
- void (*resume)(struct iwl_trans *trans);
-
- struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans,
- struct iwl_fw_dbg_trigger_tlv
- *trigger);
-};
-
-/**
- * enum iwl_trans_state - state of the transport layer
- *
- * @IWL_TRANS_NO_FW: no fw has sent an alive response
- * @IWL_TRANS_FW_ALIVE: a fw has sent an alive response
- */
-enum iwl_trans_state {
- IWL_TRANS_NO_FW = 0,
- IWL_TRANS_FW_ALIVE = 1,
-};
-
-/**
- * enum iwl_d0i3_mode - d0i3 mode
- *
- * @IWL_D0I3_MODE_OFF - d0i3 is disabled
- * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle
- * (e.g. no active references)
- * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend
- * (in case of 'any' trigger)
- */
-enum iwl_d0i3_mode {
- IWL_D0I3_MODE_OFF = 0,
- IWL_D0I3_MODE_ON_IDLE,
- IWL_D0I3_MODE_ON_SUSPEND,
-};
-
-/**
- * struct iwl_trans - transport common data
- *
- * @ops - pointer to iwl_trans_ops
- * @op_mode - pointer to the op_mode
- * @cfg - pointer to the configuration
- * @status: a bit-mask of transport status flags
- * @dev - pointer to struct device * that represents the device
- * @max_skb_frags: maximum number of fragments an SKB can have when transmitted.
- * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported.
- * @hw_id: a u32 with the ID of the device / sub-device.
- * Set during transport allocation.
- * @hw_id_str: a string with info about HW ID. Set during transport allocation.
- * @pm_support: set to true in start_hw if link pm is supported
- * @ltr_enabled: set to true if the LTR is enabled
- * @num_rx_queues: number of RX queues allocated by the transport;
- * the transport must set this before calling iwl_drv_start()
- * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
- * The user should use iwl_trans_{alloc,free}_tx_cmd.
- * @dev_cmd_headroom: room needed for the transport's private use before the
- * device_cmd for Tx - for internal use only
- * The user should use iwl_trans_{alloc,free}_tx_cmd.
- * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before
- * starting the firmware, used for tracing
- * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
- * start of the 802.11 header in the @rx_mpdu_cmd
- * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
- * @dbg_dest_tlv: points to the destination TLV for debug
- * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
- * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug
- * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
- * @paging_req_addr: The location were the FW will upload / download the pages
- * from. The address is set by the opmode
- * @paging_db: Pointer to the opmode paging data base, the pointer is set by
- * the opmode.
- * @paging_download_buf: Buffer used for copying all of the pages before
- * downloading them to the FW. The buffer is allocated in the opmode
- */
-struct iwl_trans {
- const struct iwl_trans_ops *ops;
- struct iwl_op_mode *op_mode;
- const struct iwl_cfg *cfg;
- enum iwl_trans_state state;
- unsigned long status;
-
- struct device *dev;
- u32 max_skb_frags;
- u32 hw_rev;
- u32 hw_id;
- char hw_id_str[52];
-
- u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size;
-
- bool pm_support;
- bool ltr_enabled;
-
- u8 num_rx_queues;
-
- /* The following fields are internal only */
- struct kmem_cache *dev_cmd_pool;
- size_t dev_cmd_headroom;
- char dev_cmd_pool_name[50];
-
- struct dentry *dbgfs_dir;
-
-#ifdef CONFIG_LOCKDEP
- struct lockdep_map sync_cmd_lockdep_map;
-#endif
-
- u64 dflt_pwr_limit;
-
- const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
- const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
- struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
- u8 dbg_dest_reg_num;
-
- /*
- * Paging parameters - All of the parameters should be set by the
- * opmode when paging is enabled
- */
- u32 paging_req_addr;
- struct iwl_fw_paging *paging_db;
- void *paging_download_buf;
-
- enum iwl_d0i3_mode d0i3_mode;
-
- bool wowlan_d0i3;
-
- /* pointer to trans specific struct */
- /*Ensure that this pointer will always be aligned to sizeof pointer */
- char trans_specific[0] __aligned(sizeof(void *));
-};
-
-static inline void iwl_trans_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg)
-{
- trans->op_mode = trans_cfg->op_mode;
-
- trans->ops->configure(trans, trans_cfg);
-}
-
-static inline int _iwl_trans_start_hw(struct iwl_trans *trans, bool low_power)
-{
- might_sleep();
-
- return trans->ops->start_hw(trans, low_power);
-}
-
-static inline int iwl_trans_start_hw(struct iwl_trans *trans)
-{
- return trans->ops->start_hw(trans, true);
-}
-
-static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans)
-{
- might_sleep();
-
- if (trans->ops->op_mode_leave)
- trans->ops->op_mode_leave(trans);
-
- trans->op_mode = NULL;
-
- trans->state = IWL_TRANS_NO_FW;
-}
-
-static inline void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr)
-{
- might_sleep();
-
- trans->state = IWL_TRANS_FW_ALIVE;
-
- trans->ops->fw_alive(trans, scd_addr);
-}
-
-static inline int iwl_trans_start_fw(struct iwl_trans *trans,
- const struct fw_img *fw,
- bool run_in_rfkill)
-{
- might_sleep();
-
- WARN_ON_ONCE(!trans->rx_mpdu_cmd);
-
- clear_bit(STATUS_FW_ERROR, &trans->status);
- return trans->ops->start_fw(trans, fw, run_in_rfkill);
-}
-
-static inline int iwl_trans_update_sf(struct iwl_trans *trans,
- struct iwl_sf_region *st_fwrd_space)
-{
- might_sleep();
-
- if (trans->ops->update_sf)
- return trans->ops->update_sf(trans, st_fwrd_space);
-
- return 0;
-}
-
-static inline void _iwl_trans_stop_device(struct iwl_trans *trans,
- bool low_power)
-{
- might_sleep();
-
- trans->ops->stop_device(trans, low_power);
-
- trans->state = IWL_TRANS_NO_FW;
-}
-
-static inline void iwl_trans_stop_device(struct iwl_trans *trans)
-{
- _iwl_trans_stop_device(trans, true);
-}
-
-static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test)
-{
- might_sleep();
- if (trans->ops->d3_suspend)
- trans->ops->d3_suspend(trans, test);
-}
-
-static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
- enum iwl_d3_status *status,
- bool test)
-{
- might_sleep();
- if (!trans->ops->d3_resume)
- return 0;
-
- return trans->ops->d3_resume(trans, status, test);
-}
-
-static inline void iwl_trans_ref(struct iwl_trans *trans)
-{
- if (trans->ops->ref)
- trans->ops->ref(trans);
-}
-
-static inline void iwl_trans_unref(struct iwl_trans *trans)
-{
- if (trans->ops->unref)
- trans->ops->unref(trans);
-}
-
-static inline int iwl_trans_suspend(struct iwl_trans *trans)
-{
- if (!trans->ops->suspend)
- return 0;
-
- return trans->ops->suspend(trans);
-}
-
-static inline void iwl_trans_resume(struct iwl_trans *trans)
-{
- if (trans->ops->resume)
- trans->ops->resume(trans);
-}
-
-static inline struct iwl_trans_dump_data *
-iwl_trans_dump_data(struct iwl_trans *trans,
- struct iwl_fw_dbg_trigger_tlv *trigger)
-{
- if (!trans->ops->dump_data)
- return NULL;
- return trans->ops->dump_data(trans, trigger);
-}
-
-static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
- struct iwl_host_cmd *cmd)
-{
- int ret;
-
- if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) &&
- test_bit(STATUS_RFKILL, &trans->status)))
- return -ERFKILL;
-
- if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
- return -EIO;
-
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
- IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
- return -EIO;
- }
-
- if (!(cmd->flags & CMD_ASYNC))
- lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
-
- ret = trans->ops->send_cmd(trans, cmd);
-
- if (!(cmd->flags & CMD_ASYNC))
- lock_map_release(&trans->sync_cmd_lockdep_map);
-
- return ret;
-}
-
-static inline struct iwl_device_cmd *
-iwl_trans_alloc_tx_cmd(struct iwl_trans *trans)
-{
- u8 *dev_cmd_ptr = kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC);
-
- if (unlikely(dev_cmd_ptr == NULL))
- return NULL;
-
- return (struct iwl_device_cmd *)
- (dev_cmd_ptr + trans->dev_cmd_headroom);
-}
-
-static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
- struct iwl_device_cmd *dev_cmd)
-{
- u8 *dev_cmd_ptr = (u8 *)dev_cmd - trans->dev_cmd_headroom;
-
- kmem_cache_free(trans->dev_cmd_pool, dev_cmd_ptr);
-}
-
-static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, int queue)
-{
- if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
- return -EIO;
-
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
- IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
-
- return trans->ops->tx(trans, skb, dev_cmd, queue);
-}
-
-static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
- int ssn, struct sk_buff_head *skbs)
-{
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
- IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
-
- trans->ops->reclaim(trans, queue, ssn, skbs);
-}
-
-static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue,
- bool configure_scd)
-{
- trans->ops->txq_disable(trans, queue, configure_scd);
-}
-
-static inline void
-iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn,
- const struct iwl_trans_txq_scd_cfg *cfg,
- unsigned int queue_wdg_timeout)
-{
- might_sleep();
-
- if (unlikely((trans->state != IWL_TRANS_FW_ALIVE)))
- IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
-
- trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout);
-}
-
-static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue,
- int fifo, int sta_id, int tid,
- int frame_limit, u16 ssn,
- unsigned int queue_wdg_timeout)
-{
- struct iwl_trans_txq_scd_cfg cfg = {
- .fifo = fifo,
- .sta_id = sta_id,
- .tid = tid,
- .frame_limit = frame_limit,
- .aggregate = sta_id >= 0,
- };
-
- iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg, queue_wdg_timeout);
-}
-
-static inline
-void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo,
- unsigned int queue_wdg_timeout)
-{
- struct iwl_trans_txq_scd_cfg cfg = {
- .fifo = fifo,
- .sta_id = -1,
- .tid = IWL_MAX_TID_COUNT,
- .frame_limit = IWL_FRAME_LIMIT,
- .aggregate = false,
- };
-
- iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout);
-}
-
-static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans,
- unsigned long txqs,
- bool freeze)
-{
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
- IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
-
- if (trans->ops->freeze_txq_timer)
- trans->ops->freeze_txq_timer(trans, txqs, freeze);
-}
-
-static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
- u32 txqs)
-{
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
- IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
-
- return trans->ops->wait_tx_queue_empty(trans, txqs);
-}
-
-static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
- struct dentry *dir)
-{
- return trans->ops->dbgfs_register(trans, dir);
-}
-
-static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val)
-{
- trans->ops->write8(trans, ofs, val);
-}
-
-static inline void iwl_trans_write32(struct iwl_trans *trans, u32 ofs, u32 val)
-{
- trans->ops->write32(trans, ofs, val);
-}
-
-static inline u32 iwl_trans_read32(struct iwl_trans *trans, u32 ofs)
-{
- return trans->ops->read32(trans, ofs);
-}
-
-static inline u32 iwl_trans_read_prph(struct iwl_trans *trans, u32 ofs)
-{
- return trans->ops->read_prph(trans, ofs);
-}
-
-static inline void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs,
- u32 val)
-{
- return trans->ops->write_prph(trans, ofs, val);
-}
-
-static inline int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
- void *buf, int dwords)
-{
- return trans->ops->read_mem(trans, addr, buf, dwords);
-}
-
-#define iwl_trans_read_mem_bytes(trans, addr, buf, bufsize) \
- do { \
- if (__builtin_constant_p(bufsize)) \
- BUILD_BUG_ON((bufsize) % sizeof(u32)); \
- iwl_trans_read_mem(trans, addr, buf, (bufsize) / sizeof(u32));\
- } while (0)
-
-static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr)
-{
- u32 value;
-
- if (WARN_ON(iwl_trans_read_mem(trans, addr, &value, 1)))
- return 0xa5a5a5a5;
-
- return value;
-}
-
-static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
- const void *buf, int dwords)
-{
- return trans->ops->write_mem(trans, addr, buf, dwords);
-}
-
-static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr,
- u32 val)
-{
- return iwl_trans_write_mem(trans, addr, &val, 1);
-}
-
-static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
-{
- if (trans->ops->set_pmi)
- trans->ops->set_pmi(trans, state);
-}
-
-static inline void
-iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value)
-{
- trans->ops->set_bits_mask(trans, reg, mask, value);
-}
-
-#define iwl_trans_grab_nic_access(trans, silent, flags) \
- __cond_lock(nic_access, \
- likely((trans)->ops->grab_nic_access(trans, silent, flags)))
-
-static inline void __releases(nic_access)
-iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
-{
- trans->ops->release_nic_access(trans, flags);
- __release(nic_access);
-}
-
-static inline void iwl_trans_fw_error(struct iwl_trans *trans)
-{
- if (WARN_ON_ONCE(!trans->op_mode))
- return;
-
- /* prevent double restarts due to the same erroneous FW */
- if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status))
- iwl_op_mode_nic_error(trans->op_mode);
-}
-
-/*****************************************************
- * transport helper functions
- *****************************************************/
-struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
- struct device *dev,
- const struct iwl_cfg *cfg,
- const struct iwl_trans_ops *ops,
- size_t dev_cmd_headroom);
-void iwl_trans_free(struct iwl_trans *trans);
-
-/*****************************************************
-* driver (transport) register/unregister functions
-******************************************************/
-int __must_check iwl_pci_register_driver(void);
-void iwl_pci_unregister_driver(void);
-
-#endif /* __iwl_trans_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
deleted file mode 100644
index 8c2c3d13b092..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-obj-$(CONFIG_IWLMVM) += iwlmvm.o
-iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
-iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
-iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o coex.o coex_legacy.o
-iwlmvm-y += tt.o offloading.o tdls.o
-iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
-iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
-iwlmvm-y += tof.o
-iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
-
-ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c
deleted file mode 100644
index a1376539d2dc..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/binding.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <net/mac80211.h>
-#include "fw-api.h"
-#include "mvm.h"
-
-struct iwl_mvm_iface_iterator_data {
- struct ieee80211_vif *ignore_vif;
- int idx;
-
- struct iwl_mvm_phy_ctxt *phyctxt;
-
- u16 ids[MAX_MACS_IN_BINDING];
- u16 colors[MAX_MACS_IN_BINDING];
-};
-
-static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action,
- struct iwl_mvm_iface_iterator_data *data)
-{
- struct iwl_binding_cmd cmd;
- struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt;
- int i, ret;
- u32 status;
-
- memset(&cmd, 0, sizeof(cmd));
-
- cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
- phyctxt->color));
- cmd.action = cpu_to_le32(action);
- cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
- phyctxt->color));
-
- for (i = 0; i < MAX_MACS_IN_BINDING; i++)
- cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
- for (i = 0; i < data->idx; i++)
- cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i],
- data->colors[i]));
-
- status = 0;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
- sizeof(cmd), &cmd, &status);
- if (ret) {
- IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n",
- action, ret);
- return ret;
- }
-
- if (status) {
- IWL_ERR(mvm, "Binding command failed: %u\n", status);
- ret = -EIO;
- }
-
- return ret;
-}
-
-static void iwl_mvm_iface_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_iface_iterator_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (vif == data->ignore_vif)
- return;
-
- if (mvmvif->phy_ctxt != data->phyctxt)
- return;
-
- if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING))
- return;
-
- data->ids[data->idx] = mvmvif->id;
- data->colors[data->idx] = mvmvif->color;
- data->idx++;
-}
-
-static int iwl_mvm_binding_update(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mvm_phy_ctxt *phyctxt,
- bool add)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_iface_iterator_data data = {
- .ignore_vif = vif,
- .phyctxt = phyctxt,
- };
- u32 action = FW_CTXT_ACTION_MODIFY;
-
- lockdep_assert_held(&mvm->mutex);
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_iface_iterator,
- &data);
-
- /*
- * If there are no other interfaces yet we
- * need to create a new binding.
- */
- if (data.idx == 0) {
- if (add)
- action = FW_CTXT_ACTION_ADD;
- else
- action = FW_CTXT_ACTION_REMOVE;
- }
-
- if (add) {
- if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING))
- return -EINVAL;
-
- data.ids[data.idx] = mvmvif->id;
- data.colors[data.idx] = mvmvif->color;
- data.idx++;
- }
-
- return iwl_mvm_binding_cmd(mvm, action, &data);
-}
-
-int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
- return -EINVAL;
-
- /*
- * Update SF - Disable if needed. if this fails, SF might still be on
- * while many macs are bound, which is forbidden - so fail the binding.
- */
- if (iwl_mvm_sf_update(mvm, vif, false))
- return -EINVAL;
-
- return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
-}
-
-int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
- return -EINVAL;
-
- ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
-
- if (!ret)
- if (iwl_mvm_sf_update(mvm, vif, true))
- IWL_ERR(mvm, "Failed to update SF state\n");
-
- return ret;
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c
deleted file mode 100644
index e290ac67d975..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/coex.c
+++ /dev/null
@@ -1,1005 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/ieee80211.h>
-#include <linux/etherdevice.h>
-#include <net/mac80211.h>
-
-#include "fw-api-coex.h"
-#include "iwl-modparams.h"
-#include "mvm.h"
-#include "iwl-debug.h"
-
-/* 20MHz / 40MHz below / 40Mhz above*/
-static const __le64 iwl_ci_mask[][3] = {
- /* dummy entry for channel 0 */
- {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
- {
- cpu_to_le64(0x0000001FFFULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x00007FFFFFULL),
- },
- {
- cpu_to_le64(0x000000FFFFULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x0003FFFFFFULL),
- },
- {
- cpu_to_le64(0x000003FFFCULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x000FFFFFFCULL),
- },
- {
- cpu_to_le64(0x00001FFFE0ULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x007FFFFFE0ULL),
- },
- {
- cpu_to_le64(0x00007FFF80ULL),
- cpu_to_le64(0x00007FFFFFULL),
- cpu_to_le64(0x01FFFFFF80ULL),
- },
- {
- cpu_to_le64(0x0003FFFC00ULL),
- cpu_to_le64(0x0003FFFFFFULL),
- cpu_to_le64(0x0FFFFFFC00ULL),
- },
- {
- cpu_to_le64(0x000FFFF000ULL),
- cpu_to_le64(0x000FFFFFFCULL),
- cpu_to_le64(0x3FFFFFF000ULL),
- },
- {
- cpu_to_le64(0x007FFF8000ULL),
- cpu_to_le64(0x007FFFFFE0ULL),
- cpu_to_le64(0xFFFFFF8000ULL),
- },
- {
- cpu_to_le64(0x01FFFE0000ULL),
- cpu_to_le64(0x01FFFFFF80ULL),
- cpu_to_le64(0xFFFFFE0000ULL),
- },
- {
- cpu_to_le64(0x0FFFF00000ULL),
- cpu_to_le64(0x0FFFFFFC00ULL),
- cpu_to_le64(0x0ULL),
- },
- {
- cpu_to_le64(0x3FFFC00000ULL),
- cpu_to_le64(0x3FFFFFF000ULL),
- cpu_to_le64(0x0)
- },
- {
- cpu_to_le64(0xFFFE000000ULL),
- cpu_to_le64(0xFFFFFF8000ULL),
- cpu_to_le64(0x0)
- },
- {
- cpu_to_le64(0xFFF8000000ULL),
- cpu_to_le64(0xFFFFFE0000ULL),
- cpu_to_le64(0x0)
- },
- {
- cpu_to_le64(0xFFC0000000ULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x0ULL)
- },
-};
-
-struct corunning_block_luts {
- u8 range;
- __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
-};
-
-/*
- * Ranges for the antenna coupling calibration / co-running block LUT:
- * LUT0: [ 0, 12[
- * LUT1: [12, 20[
- * LUT2: [20, 21[
- * LUT3: [21, 23[
- * LUT4: [23, 27[
- * LUT5: [27, 30[
- * LUT6: [30, 32[
- * LUT7: [32, 33[
- * LUT8: [33, - [
- */
-static const struct corunning_block_luts antenna_coupling_ranges[] = {
- {
- .range = 0,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 12,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 20,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 21,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 23,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 27,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 30,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 32,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 33,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
-};
-
-static enum iwl_bt_coex_lut_type
-iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
-{
- struct ieee80211_chanctx_conf *chanctx_conf;
- enum iwl_bt_coex_lut_type ret;
- u16 phy_ctx_id;
- u32 primary_ch_phy_id, secondary_ch_phy_id;
-
- /*
- * Checking that we hold mvm->mutex is a good idea, but the rate
- * control can't acquire the mutex since it runs in Tx path.
- * So this is racy in that case, but in the worst case, the AMPDU
- * size limit will be wrong for a short time which is not a big
- * issue.
- */
-
- rcu_read_lock();
-
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
-
- if (!chanctx_conf ||
- chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
- rcu_read_unlock();
- return BT_COEX_INVALID_LUT;
- }
-
- ret = BT_COEX_TX_DIS_LUT;
-
- if (mvm->cfg->bt_shared_single_ant) {
- rcu_read_unlock();
- return ret;
- }
-
- phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
- primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);
- secondary_ch_phy_id =
- le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);
-
- if (primary_ch_phy_id == phy_ctx_id)
- ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
- else if (secondary_ch_phy_id == phy_ctx_id)
- ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
- /* else - default = TX TX disallowed */
-
- rcu_read_unlock();
-
- return ret;
-}
-
-int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
-{
- struct iwl_bt_coex_cmd bt_cmd = {};
- u32 mode;
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
- return iwl_send_bt_init_conf_old(mvm);
-
- lockdep_assert_held(&mvm->mutex);
-
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
- switch (mvm->bt_force_ant_mode) {
- case BT_FORCE_ANT_BT:
- mode = BT_COEX_BT;
- break;
- case BT_FORCE_ANT_WIFI:
- mode = BT_COEX_WIFI;
- break;
- default:
- WARN_ON(1);
- mode = 0;
- }
-
- bt_cmd.mode = cpu_to_le32(mode);
- goto send_cmd;
- }
-
- mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
- bt_cmd.mode = cpu_to_le32(mode);
-
- if (IWL_MVM_BT_COEX_SYNC2SCO)
- bt_cmd.enabled_modules |=
- cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
-
- if (iwl_mvm_bt_is_plcr_supported(mvm))
- bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
-
- if (IWL_MVM_BT_COEX_MPLUT) {
- bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
- bt_cmd.enabled_modules |=
- cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED);
- }
-
- bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
-
-send_cmd:
- memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
- memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
-
- return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd);
-}
-
-static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
- bool enable)
-{
- struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};
- struct iwl_mvm_sta *mvmsta;
- u32 value;
- int ret;
-
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
- if (!mvmsta)
- return 0;
-
- /* nothing to do */
- if (mvmsta->bt_reduced_txpower == enable)
- return 0;
-
- value = mvmsta->sta_id;
-
- if (enable)
- value |= BT_REDUCED_TX_POWER_BIT;
-
- IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
- enable ? "en" : "dis", sta_id);
-
- cmd.reduced_txp = cpu_to_le32(value);
- mvmsta->bt_reduced_txpower = enable;
-
- ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC,
- sizeof(cmd), &cmd);
-
- return ret;
-}
-
-struct iwl_bt_iterator_data {
- struct iwl_bt_coex_profile_notif *notif;
- struct iwl_mvm *mvm;
- struct ieee80211_chanctx_conf *primary;
- struct ieee80211_chanctx_conf *secondary;
- bool primary_ll;
-};
-
-static inline
-void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool enable, int rssi)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- mvmvif->bf_data.last_bt_coex_event = rssi;
- mvmvif->bf_data.bt_coex_max_thold =
- enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
- mvmvif->bf_data.bt_coex_min_thold =
- enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
-}
-
-/* must be called under rcu_read_lock */
-static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_bt_iterator_data *data = _data;
- struct iwl_mvm *mvm = data->mvm;
- struct ieee80211_chanctx_conf *chanctx_conf;
- /* default smps_mode is AUTOMATIC - only used for client modes */
- enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
- u32 bt_activity_grading;
- int ave_rssi;
-
- lockdep_assert_held(&mvm->mutex);
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- break;
- case NL80211_IFTYPE_AP:
- if (!mvmvif->ap_ibss_active)
- return;
- break;
- default:
- return;
- }
-
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
-
- /* If channel context is invalid or not on 2.4GHz .. */
- if ((!chanctx_conf ||
- chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
- if (vif->type == NL80211_IFTYPE_STATION) {
- /* ... relax constraints and disable rssi events */
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
- smps_mode);
- iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
- false);
- iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
- }
- return;
- }
-
- bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
- if (bt_activity_grading >= BT_HIGH_TRAFFIC)
- smps_mode = IEEE80211_SMPS_STATIC;
- else if (bt_activity_grading >= BT_LOW_TRAFFIC)
- smps_mode = IEEE80211_SMPS_DYNAMIC;
-
- /* relax SMPS constraints for next association */
- if (!vif->bss_conf.assoc)
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
-
- if (mvmvif->phy_ctxt &&
- IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
- mvmvif->phy_ctxt->id))
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
-
- IWL_DEBUG_COEX(data->mvm,
- "mac %d: bt_activity_grading %d smps_req %d\n",
- mvmvif->id, bt_activity_grading, smps_mode);
-
- if (vif->type == NL80211_IFTYPE_STATION)
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
- smps_mode);
-
- /* low latency is always primary */
- if (iwl_mvm_vif_low_latency(mvmvif)) {
- data->primary_ll = true;
-
- data->secondary = data->primary;
- data->primary = chanctx_conf;
- }
-
- if (vif->type == NL80211_IFTYPE_AP) {
- if (!mvmvif->ap_ibss_active)
- return;
-
- if (chanctx_conf == data->primary)
- return;
-
- if (!data->primary_ll) {
- /*
- * downgrade the current primary no matter what its
- * type is.
- */
- data->secondary = data->primary;
- data->primary = chanctx_conf;
- } else {
- /* there is low latency vif - we will be secondary */
- data->secondary = chanctx_conf;
- }
- return;
- }
-
- /*
- * STA / P2P Client, try to be primary if first vif. If we are in low
- * latency mode, we are already in primary and just don't do much
- */
- if (!data->primary || data->primary == chanctx_conf)
- data->primary = chanctx_conf;
- else if (!data->secondary)
- /* if secondary is not NULL, it might be a GO */
- data->secondary = chanctx_conf;
-
- /*
- * don't reduce the Tx power if one of these is true:
- * we are in LOOSE
- * single share antenna product
- * BT is active
- * we are associated
- */
- if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
- mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
- le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
- iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
- iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
- return;
- }
-
- /* try to get the avg rssi from fw */
- ave_rssi = mvmvif->bf_data.ave_beacon_signal;
-
- /* if the RSSI isn't valid, fake it is very low */
- if (!ave_rssi)
- ave_rssi = -100;
- if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
- if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
- IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
- } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
- if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
- IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
- }
-
- /* Begin to monitor the RSSI: it may influence the reduced Tx power */
- iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
-}
-
-static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
-{
- struct iwl_bt_iterator_data data = {
- .mvm = mvm,
- .notif = &mvm->last_bt_notif,
- };
- struct iwl_bt_coex_ci_cmd cmd = {};
- u8 ci_bw_idx;
-
- /* Ignore updates if we are in force mode */
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
- return;
-
- rcu_read_lock();
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_bt_notif_iterator, &data);
-
- if (data.primary) {
- struct ieee80211_chanctx_conf *chan = data.primary;
- if (WARN_ON(!chan->def.chan)) {
- rcu_read_unlock();
- return;
- }
-
- if (chan->def.width < NL80211_CHAN_WIDTH_40) {
- ci_bw_idx = 0;
- } else {
- if (chan->def.center_freq1 >
- chan->def.chan->center_freq)
- ci_bw_idx = 2;
- else
- ci_bw_idx = 1;
- }
-
- cmd.bt_primary_ci =
- iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
- cmd.primary_ch_phy_id =
- cpu_to_le32(*((u16 *)data.primary->drv_priv));
- }
-
- if (data.secondary) {
- struct ieee80211_chanctx_conf *chan = data.secondary;
- if (WARN_ON(!data.secondary->def.chan)) {
- rcu_read_unlock();
- return;
- }
-
- if (chan->def.width < NL80211_CHAN_WIDTH_40) {
- ci_bw_idx = 0;
- } else {
- if (chan->def.center_freq1 >
- chan->def.chan->center_freq)
- ci_bw_idx = 2;
- else
- ci_bw_idx = 1;
- }
-
- cmd.bt_secondary_ci =
- iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
- cmd.secondary_ch_phy_id =
- cpu_to_le32(*((u16 *)data.secondary->drv_priv));
- }
-
- rcu_read_unlock();
-
- /* Don't spam the fw with the same command over and over */
- if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {
- if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
- sizeof(cmd), &cmd))
- IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
- memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
- }
-}
-
-void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
-
- if (!fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
- iwl_mvm_rx_bt_coex_notif_old(mvm, rxb);
- return;
- }
-
- IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
- IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
- IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
- le32_to_cpu(notif->primary_ch_lut));
- IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
- le32_to_cpu(notif->secondary_ch_lut));
- IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
- le32_to_cpu(notif->bt_activity_grading));
-
- /* remember this notification for future use: rssi fluctuations */
- memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
-
- iwl_mvm_bt_coex_notif_handle(mvm);
-}
-
-void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- enum ieee80211_rssi_event_data rssi_event)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- if (!fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
- iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event);
- return;
- }
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Ignore updates if we are in force mode */
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
- return;
-
- /*
- * Rssi update while not associated - can happen since the statistics
- * are handled asynchronously
- */
- if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
- return;
-
- /* No BT - reports should be disabled */
- if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)
- return;
-
- IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
- rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
-
- /*
- * Check if rssi is good enough for reduced Tx power, but not in loose
- * scheme.
- */
- if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
- iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
- ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
- false);
- else
- ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
-
- if (ret)
- IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
-}
-
-#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
-#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
-
-u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
- struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
- enum iwl_bt_coex_lut_type lut_type;
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
- return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
-
- if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
- return LINK_QUAL_AGG_TIME_LIMIT_DEF;
-
- if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
- BT_HIGH_TRAFFIC)
- return LINK_QUAL_AGG_TIME_LIMIT_DEF;
-
- lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
-
- if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
- return LINK_QUAL_AGG_TIME_LIMIT_DEF;
-
- /* tight coex, high bt traffic, reduce AGG time limit */
- return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
-}
-
-bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
- struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
- enum iwl_bt_coex_lut_type lut_type;
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
- return iwl_mvm_bt_coex_is_mimo_allowed_old(mvm, sta);
-
- if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
- return true;
-
- if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
- BT_HIGH_TRAFFIC)
- return true;
-
- /*
- * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
- * since BT is already killed.
- * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
- * we Tx.
- * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
- */
- lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
- return lut_type != BT_COEX_LOOSE_LUT;
-}
-
-bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant)
-{
- /* there is no other antenna, shared antenna is always available */
- if (mvm->cfg->bt_shared_single_ant)
- return true;
-
- if (ant & mvm->cfg->non_shared_ant)
- return true;
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
- return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
-
- return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
- BT_HIGH_TRAFFIC;
-}
-
-bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
-{
- /* there is no other antenna, shared antenna is always available */
- if (mvm->cfg->bt_shared_single_ant)
- return true;
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
- return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
-
- return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC;
-}
-
-bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
- enum ieee80211_band band)
-{
- u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT))
- return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band);
-
- if (band != IEEE80211_BAND_2GHZ)
- return false;
-
- return bt_activity >= BT_LOW_TRAFFIC;
-}
-
-u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
- struct ieee80211_tx_info *info, u8 ac)
-{
- __le16 fc = hdr->frame_control;
-
- if (info->band != IEEE80211_BAND_2GHZ)
- return 0;
-
- if (unlikely(mvm->bt_tx_prio))
- return mvm->bt_tx_prio - 1;
-
- /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
- if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO ||
- is_multicast_ether_addr(hdr->addr1) ||
- ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) ||
- ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc))
- return 3;
-
- switch (ac) {
- case IEEE80211_AC_BE:
- return 1;
- case IEEE80211_AC_VO:
- return 3;
- case IEEE80211_AC_VI:
- return 2;
- default:
- break;
- }
-
- return 0;
-}
-
-void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
-{
- if (!fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
- iwl_mvm_bt_coex_vif_change_old(mvm);
- return;
- }
-
- iwl_mvm_bt_coex_notif_handle(mvm);
-}
-
-void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u32 ant_isolation = le32_to_cpup((void *)pkt->data);
- struct iwl_bt_coex_corun_lut_update_cmd cmd = {};
- u8 __maybe_unused lower_bound, upper_bound;
- u8 lut;
-
- if (!fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
- iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb);
- return;
- }
-
- if (!iwl_mvm_bt_is_plcr_supported(mvm))
- return;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Ignore updates if we are in force mode */
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
- return;
-
- if (ant_isolation == mvm->last_ant_isol)
- return;
-
- for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
- if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
- break;
-
- lower_bound = antenna_coupling_ranges[lut].range;
-
- if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
- upper_bound = antenna_coupling_ranges[lut + 1].range;
- else
- upper_bound = antenna_coupling_ranges[lut].range;
-
- IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
- ant_isolation, lower_bound, upper_bound, lut);
-
- mvm->last_ant_isol = ant_isolation;
-
- if (mvm->last_corun_lut == lut)
- return;
-
- mvm->last_corun_lut = lut;
-
- /* For the moment, use the same LUT for 20GHz and 40GHz */
- memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20,
- sizeof(cmd.corun_lut20));
-
- memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20,
- sizeof(cmd.corun_lut40));
-
- if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0,
- sizeof(cmd), &cmd))
- IWL_ERR(mvm,
- "failed to send BT_COEX_UPDATE_CORUN_LUT command\n");
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
deleted file mode 100644
index 61c07b05fcaa..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
+++ /dev/null
@@ -1,1315 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/ieee80211.h>
-#include <linux/etherdevice.h>
-#include <net/mac80211.h>
-
-#include "fw-api-coex.h"
-#include "iwl-modparams.h"
-#include "mvm.h"
-#include "iwl-debug.h"
-
-#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
- [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
- ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
-
-static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
- BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
- BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
- BT_COEX_PRIO_TBL_PRIO_LOW, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
- BT_COEX_PRIO_TBL_PRIO_LOW, 1),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
- BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
- BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
- BT_COEX_PRIO_TBL_DISABLED, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
- BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
- BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
- BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
- 0, 0, 0, 0, 0, 0,
-};
-
-#undef EVENT_PRIO_ANT
-
-static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
-{
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
- return 0;
-
- return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
- sizeof(struct iwl_bt_coex_prio_tbl_cmd),
- &iwl_bt_prio_tbl);
-}
-
-static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
- cpu_to_le32(0xf0f0f0f0), /* 50% */
- cpu_to_le32(0xc0c0c0c0), /* 25% */
- cpu_to_le32(0xfcfcfcfc), /* 75% */
- cpu_to_le32(0xfefefefe), /* 87.5% */
-};
-
-static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
- {
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- },
- {
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- },
- {
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- },
-};
-
-static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
- {
- /* Tight */
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaeaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0x00004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
- },
- {
- /* Loose */
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
- },
- {
- /* Tx Tx disabled */
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xeeaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
- },
-};
-
-/* 20MHz / 40MHz below / 40Mhz above*/
-static const __le64 iwl_ci_mask[][3] = {
- /* dummy entry for channel 0 */
- {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
- {
- cpu_to_le64(0x0000001FFFULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x00007FFFFFULL),
- },
- {
- cpu_to_le64(0x000000FFFFULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x0003FFFFFFULL),
- },
- {
- cpu_to_le64(0x000003FFFCULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x000FFFFFFCULL),
- },
- {
- cpu_to_le64(0x00001FFFE0ULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x007FFFFFE0ULL),
- },
- {
- cpu_to_le64(0x00007FFF80ULL),
- cpu_to_le64(0x00007FFFFFULL),
- cpu_to_le64(0x01FFFFFF80ULL),
- },
- {
- cpu_to_le64(0x0003FFFC00ULL),
- cpu_to_le64(0x0003FFFFFFULL),
- cpu_to_le64(0x0FFFFFFC00ULL),
- },
- {
- cpu_to_le64(0x000FFFF000ULL),
- cpu_to_le64(0x000FFFFFFCULL),
- cpu_to_le64(0x3FFFFFF000ULL),
- },
- {
- cpu_to_le64(0x007FFF8000ULL),
- cpu_to_le64(0x007FFFFFE0ULL),
- cpu_to_le64(0xFFFFFF8000ULL),
- },
- {
- cpu_to_le64(0x01FFFE0000ULL),
- cpu_to_le64(0x01FFFFFF80ULL),
- cpu_to_le64(0xFFFFFE0000ULL),
- },
- {
- cpu_to_le64(0x0FFFF00000ULL),
- cpu_to_le64(0x0FFFFFFC00ULL),
- cpu_to_le64(0x0ULL),
- },
- {
- cpu_to_le64(0x3FFFC00000ULL),
- cpu_to_le64(0x3FFFFFF000ULL),
- cpu_to_le64(0x0)
- },
- {
- cpu_to_le64(0xFFFE000000ULL),
- cpu_to_le64(0xFFFFFF8000ULL),
- cpu_to_le64(0x0)
- },
- {
- cpu_to_le64(0xFFF8000000ULL),
- cpu_to_le64(0xFFFFFE0000ULL),
- cpu_to_le64(0x0)
- },
- {
- cpu_to_le64(0xFFC0000000ULL),
- cpu_to_le64(0x0ULL),
- cpu_to_le64(0x0ULL)
- },
-};
-
-enum iwl_bt_kill_msk {
- BT_KILL_MSK_DEFAULT,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_MAX,
-};
-
-static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
- [BT_KILL_MSK_DEFAULT] = 0xfffffc00,
- [BT_KILL_MSK_NEVER] = 0xffffffff,
- [BT_KILL_MSK_ALWAYS] = 0,
-};
-
-static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- },
- {
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- },
- {
- BT_KILL_MSK_DEFAULT,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_DEFAULT,
- },
-};
-
-static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_DEFAULT,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_DEFAULT,
- },
-};
-
-struct corunning_block_luts {
- u8 range;
- __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
-};
-
-/*
- * Ranges for the antenna coupling calibration / co-running block LUT:
- * LUT0: [ 0, 12[
- * LUT1: [12, 20[
- * LUT2: [20, 21[
- * LUT3: [21, 23[
- * LUT4: [23, 27[
- * LUT5: [27, 30[
- * LUT6: [30, 32[
- * LUT7: [32, 33[
- * LUT8: [33, - [
- */
-static const struct corunning_block_luts antenna_coupling_ranges[] = {
- {
- .range = 0,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 12,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 20,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 21,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 23,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 27,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 30,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 32,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
- {
- .range = 33,
- .lut20 = {
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
- },
- },
-};
-
-static enum iwl_bt_coex_lut_type
-iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
-{
- struct ieee80211_chanctx_conf *chanctx_conf;
- enum iwl_bt_coex_lut_type ret;
- u16 phy_ctx_id;
-
- /*
- * Checking that we hold mvm->mutex is a good idea, but the rate
- * control can't acquire the mutex since it runs in Tx path.
- * So this is racy in that case, but in the worst case, the AMPDU
- * size limit will be wrong for a short time which is not a big
- * issue.
- */
-
- rcu_read_lock();
-
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
-
- if (!chanctx_conf ||
- chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
- rcu_read_unlock();
- return BT_COEX_INVALID_LUT;
- }
-
- ret = BT_COEX_TX_DIS_LUT;
-
- if (mvm->cfg->bt_shared_single_ant) {
- rcu_read_unlock();
- return ret;
- }
-
- phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
-
- if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id)
- ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut);
- else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id)
- ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut);
- /* else - default = TX TX disallowed */
-
- rcu_read_unlock();
-
- return ret;
-}
-
-int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
-{
- struct iwl_bt_coex_cmd_old *bt_cmd;
- struct iwl_host_cmd cmd = {
- .id = BT_CONFIG,
- .len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
- int ret;
- u32 flags;
-
- ret = iwl_send_bt_prio_tbl(mvm);
- if (ret)
- return ret;
-
- bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
- if (!bt_cmd)
- return -ENOMEM;
- cmd.data[0] = bt_cmd;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
- switch (mvm->bt_force_ant_mode) {
- case BT_FORCE_ANT_AUTO:
- flags = BT_COEX_AUTO_OLD;
- break;
- case BT_FORCE_ANT_BT:
- flags = BT_COEX_BT_OLD;
- break;
- case BT_FORCE_ANT_WIFI:
- flags = BT_COEX_WIFI_OLD;
- break;
- default:
- WARN_ON(1);
- flags = 0;
- }
-
- bt_cmd->flags = cpu_to_le32(flags);
- bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE);
- goto send_cmd;
- }
-
- bt_cmd->max_kill = 5;
- bt_cmd->bt4_antenna_isolation_thr =
- IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS;
- bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
- bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
- bt_cmd->bt4_tx_rx_max_freq0 = 15;
- bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
- bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
-
- flags = iwlwifi_mod_params.bt_coex_active ?
- BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD;
- bt_cmd->flags = cpu_to_le32(flags);
-
- bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
- BT_VALID_BT_PRIO_BOOST |
- BT_VALID_MAX_KILL |
- BT_VALID_3W_TMRS |
- BT_VALID_KILL_ACK |
- BT_VALID_KILL_CTS |
- BT_VALID_REDUCED_TX_POWER |
- BT_VALID_LUT |
- BT_VALID_WIFI_RX_SW_PRIO_BOOST |
- BT_VALID_WIFI_TX_SW_PRIO_BOOST |
- BT_VALID_ANT_ISOLATION |
- BT_VALID_ANT_ISOLATION_THRS |
- BT_VALID_TXTX_DELTA_FREQ_THRS |
- BT_VALID_TXRX_MAX_FREQ_0 |
- BT_VALID_SYNC_TO_SCO |
- BT_VALID_TTC |
- BT_VALID_RRC);
-
- if (IWL_MVM_BT_COEX_SYNC2SCO)
- bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
-
- if (iwl_mvm_bt_is_plcr_supported(mvm)) {
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
- BT_VALID_CORUN_LUT_40);
- bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
- }
-
- if (IWL_MVM_BT_COEX_MPLUT) {
- bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
- }
-
- if (IWL_MVM_BT_COEX_TTC)
- bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC);
-
- if (iwl_mvm_bt_is_rrc_supported(mvm))
- bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC);
-
- if (mvm->cfg->bt_shared_single_ant)
- memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
- sizeof(iwl_single_shared_ant));
- else
- memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
- sizeof(iwl_combined_lookup));
-
- /* Take first Co-running block LUT to get started */
- memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
- sizeof(bt_cmd->bt4_corun_lut20));
- memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
- sizeof(bt_cmd->bt4_corun_lut40));
-
- memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
- sizeof(iwl_bt_prio_boost));
- bt_cmd->bt4_multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0);
- bt_cmd->bt4_multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1);
-
-send_cmd:
- memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
- memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
-
- kfree(bt_cmd);
- return ret;
-}
-
-static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm)
-{
- struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old;
- u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
- u32 ag = le32_to_cpu(notif->bt_activity_grading);
- struct iwl_bt_coex_cmd_old *bt_cmd;
- u8 ack_kill_msk, cts_kill_msk;
- struct iwl_host_cmd cmd = {
- .id = BT_CONFIG,
- .data[0] = &bt_cmd,
- .len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
- int ret = 0;
-
- lockdep_assert_held(&mvm->mutex);
-
- ack_kill_msk = iwl_bt_ack_kill_msk[ag][primary_lut];
- cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut];
-
- if (mvm->bt_ack_kill_msk[0] == ack_kill_msk &&
- mvm->bt_cts_kill_msk[0] == cts_kill_msk)
- return 0;
-
- mvm->bt_ack_kill_msk[0] = ack_kill_msk;
- mvm->bt_cts_kill_msk[0] = cts_kill_msk;
-
- bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
- if (!bt_cmd)
- return -ENOMEM;
- cmd.data[0] = bt_cmd;
- bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
-
- bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk]);
- bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk]);
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
- BT_VALID_KILL_ACK |
- BT_VALID_KILL_CTS);
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
-
- kfree(bt_cmd);
- return ret;
-}
-
-static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
- bool enable)
-{
- struct iwl_bt_coex_cmd_old *bt_cmd;
- /* Send ASYNC since this can be sent from an atomic context */
- struct iwl_host_cmd cmd = {
- .id = BT_CONFIG,
- .len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_DUP, },
- .flags = CMD_ASYNC,
- };
- struct iwl_mvm_sta *mvmsta;
- int ret;
-
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
- if (!mvmsta)
- return 0;
-
- /* nothing to do */
- if (mvmsta->bt_reduced_txpower == enable)
- return 0;
-
- bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
- if (!bt_cmd)
- return -ENOMEM;
- cmd.data[0] = bt_cmd;
- bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
-
- bt_cmd->valid_bit_msk =
- cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
- bt_cmd->bt_reduced_tx_power = sta_id;
-
- if (enable)
- bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
-
- IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
- enable ? "en" : "dis", sta_id);
-
- mvmsta->bt_reduced_txpower = enable;
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
-
- kfree(bt_cmd);
- return ret;
-}
-
-struct iwl_bt_iterator_data {
- struct iwl_bt_coex_profile_notif_old *notif;
- struct iwl_mvm *mvm;
- struct ieee80211_chanctx_conf *primary;
- struct ieee80211_chanctx_conf *secondary;
- bool primary_ll;
-};
-
-static inline
-void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool enable, int rssi)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- mvmvif->bf_data.last_bt_coex_event = rssi;
- mvmvif->bf_data.bt_coex_max_thold =
- enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
- mvmvif->bf_data.bt_coex_min_thold =
- enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
-}
-
-/* must be called under rcu_read_lock */
-static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_bt_iterator_data *data = _data;
- struct iwl_mvm *mvm = data->mvm;
- struct ieee80211_chanctx_conf *chanctx_conf;
- enum ieee80211_smps_mode smps_mode;
- u32 bt_activity_grading;
- int ave_rssi;
-
- lockdep_assert_held(&mvm->mutex);
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- /* default smps_mode for BSS / P2P client is AUTOMATIC */
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
- break;
- case NL80211_IFTYPE_AP:
- if (!mvmvif->ap_ibss_active)
- return;
- break;
- default:
- return;
- }
-
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
-
- /* If channel context is invalid or not on 2.4GHz .. */
- if ((!chanctx_conf ||
- chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
- if (vif->type == NL80211_IFTYPE_STATION) {
- /* ... relax constraints and disable rssi events */
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
- smps_mode);
- iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
- false);
- iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
- }
- return;
- }
-
- bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
- if (bt_activity_grading >= BT_HIGH_TRAFFIC)
- smps_mode = IEEE80211_SMPS_STATIC;
- else if (bt_activity_grading >= BT_LOW_TRAFFIC)
- smps_mode = vif->type == NL80211_IFTYPE_AP ?
- IEEE80211_SMPS_OFF :
- IEEE80211_SMPS_DYNAMIC;
-
- /* relax SMPS contraints for next association */
- if (!vif->bss_conf.assoc)
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
-
- if (mvmvif->phy_ctxt &&
- data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
-
- IWL_DEBUG_COEX(data->mvm,
- "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
- mvmvif->id, data->notif->bt_status, bt_activity_grading,
- smps_mode);
-
- if (vif->type == NL80211_IFTYPE_STATION)
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
- smps_mode);
-
- /* low latency is always primary */
- if (iwl_mvm_vif_low_latency(mvmvif)) {
- data->primary_ll = true;
-
- data->secondary = data->primary;
- data->primary = chanctx_conf;
- }
-
- if (vif->type == NL80211_IFTYPE_AP) {
- if (!mvmvif->ap_ibss_active)
- return;
-
- if (chanctx_conf == data->primary)
- return;
-
- if (!data->primary_ll) {
- /*
- * downgrade the current primary no matter what its
- * type is.
- */
- data->secondary = data->primary;
- data->primary = chanctx_conf;
- } else {
- /* there is low latency vif - we will be secondary */
- data->secondary = chanctx_conf;
- }
- return;
- }
-
- /*
- * STA / P2P Client, try to be primary if first vif. If we are in low
- * latency mode, we are already in primary and just don't do much
- */
- if (!data->primary || data->primary == chanctx_conf)
- data->primary = chanctx_conf;
- else if (!data->secondary)
- /* if secondary is not NULL, it might be a GO */
- data->secondary = chanctx_conf;
-
- /*
- * don't reduce the Tx power if one of these is true:
- * we are in LOOSE
- * single share antenna product
- * BT is active
- * we are associated
- */
- if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
- mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
- !data->notif->bt_status) {
- iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
- iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
- return;
- }
-
- /* try to get the avg rssi from fw */
- ave_rssi = mvmvif->bf_data.ave_beacon_signal;
-
- /* if the RSSI isn't valid, fake it is very low */
- if (!ave_rssi)
- ave_rssi = -100;
- if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
- if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
- IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
- } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
- if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
- IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
- }
-
- /* Begin to monitor the RSSI: it may influence the reduced Tx power */
- iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
-}
-
-static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
-{
- struct iwl_bt_iterator_data data = {
- .mvm = mvm,
- .notif = &mvm->last_bt_notif_old,
- };
- struct iwl_bt_coex_ci_cmd_old cmd = {};
- u8 ci_bw_idx;
-
- /* Ignore updates if we are in force mode */
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
- return;
-
- rcu_read_lock();
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_bt_notif_iterator, &data);
-
- if (data.primary) {
- struct ieee80211_chanctx_conf *chan = data.primary;
-
- if (WARN_ON(!chan->def.chan)) {
- rcu_read_unlock();
- return;
- }
-
- if (chan->def.width < NL80211_CHAN_WIDTH_40) {
- ci_bw_idx = 0;
- cmd.co_run_bw_primary = 0;
- } else {
- cmd.co_run_bw_primary = 1;
- if (chan->def.center_freq1 >
- chan->def.chan->center_freq)
- ci_bw_idx = 2;
- else
- ci_bw_idx = 1;
- }
-
- cmd.bt_primary_ci =
- iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
- cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
- }
-
- if (data.secondary) {
- struct ieee80211_chanctx_conf *chan = data.secondary;
-
- if (WARN_ON(!data.secondary->def.chan)) {
- rcu_read_unlock();
- return;
- }
-
- if (chan->def.width < NL80211_CHAN_WIDTH_40) {
- ci_bw_idx = 0;
- cmd.co_run_bw_secondary = 0;
- } else {
- cmd.co_run_bw_secondary = 1;
- if (chan->def.center_freq1 >
- chan->def.chan->center_freq)
- ci_bw_idx = 2;
- else
- ci_bw_idx = 1;
- }
-
- cmd.bt_secondary_ci =
- iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
- cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
- }
-
- rcu_read_unlock();
-
- /* Don't spam the fw with the same command over and over */
- if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) {
- if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
- sizeof(cmd), &cmd))
- IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
- memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd));
- }
-
- if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
- IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
-}
-
-void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data;
-
- IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
- IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
- notif->bt_status ? "ON" : "OFF");
- IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
- IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
- IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
- le32_to_cpu(notif->primary_ch_lut));
- IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
- le32_to_cpu(notif->secondary_ch_lut));
- IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
- le32_to_cpu(notif->bt_activity_grading));
- IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
- notif->bt_agg_traffic_load);
-
- /* remember this notification for future use: rssi fluctuations */
- memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old));
-
- iwl_mvm_bt_coex_notif_handle(mvm);
-}
-
-static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_bt_iterator_data *data = _data;
- struct iwl_mvm *mvm = data->mvm;
-
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvmsta;
-
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
- /* If channel context is invalid or not on 2.4GHz - don't count it */
- if (!chanctx_conf ||
- chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
- rcu_read_unlock();
- return;
- }
- rcu_read_unlock();
-
- if (vif->type != NL80211_IFTYPE_STATION ||
- mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
- return;
-
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
- lockdep_is_held(&mvm->mutex));
-
- /* This can happen if the station has been removed right now */
- if (IS_ERR_OR_NULL(sta))
- return;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
-}
-
-void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- enum ieee80211_rssi_event_data rssi_event)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_bt_iterator_data data = {
- .mvm = mvm,
- };
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Ignore updates if we are in force mode */
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
- return;
-
- /*
- * Rssi update while not associated - can happen since the statistics
- * are handled asynchronously
- */
- if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
- return;
-
- /* No BT - reports should be disabled */
- if (!mvm->last_bt_notif_old.bt_status)
- return;
-
- IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
- rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
-
- /*
- * Check if rssi is good enough for reduced Tx power, but not in loose
- * scheme.
- */
- if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
- iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
- ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
- false);
- else
- ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
-
- if (ret)
- IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_bt_rssi_iterator, &data);
-
- if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
- IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
-}
-
-#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
-#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
-
-u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- enum iwl_bt_coex_lut_type lut_type;
-
- if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
- BT_HIGH_TRAFFIC)
- return LINK_QUAL_AGG_TIME_LIMIT_DEF;
-
- if (mvm->last_bt_notif_old.ttc_enabled)
- return LINK_QUAL_AGG_TIME_LIMIT_DEF;
-
- lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
-
- if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
- return LINK_QUAL_AGG_TIME_LIMIT_DEF;
-
- /* tight coex, high bt traffic, reduce AGG time limit */
- return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
-}
-
-bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- enum iwl_bt_coex_lut_type lut_type;
-
- if (mvm->last_bt_notif_old.ttc_enabled)
- return true;
-
- if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
- BT_HIGH_TRAFFIC)
- return true;
-
- /*
- * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
- * since BT is already killed.
- * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
- * we Tx.
- * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
- */
- lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
- return lut_type != BT_COEX_LOOSE_LUT;
-}
-
-bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
-{
- u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
- return ag < BT_HIGH_TRAFFIC;
-}
-
-bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
- enum ieee80211_band band)
-{
- u32 bt_activity =
- le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
-
- if (band != IEEE80211_BAND_2GHZ)
- return false;
-
- return bt_activity >= BT_LOW_TRAFFIC;
-}
-
-void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm)
-{
- iwl_mvm_bt_coex_notif_handle(mvm);
-}
-
-void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u32 ant_isolation = le32_to_cpup((void *)pkt->data);
- u8 __maybe_unused lower_bound, upper_bound;
- u8 lut;
-
- struct iwl_bt_coex_cmd_old *bt_cmd;
- struct iwl_host_cmd cmd = {
- .id = BT_CONFIG,
- .len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
-
- if (!iwl_mvm_bt_is_plcr_supported(mvm))
- return;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Ignore updates if we are in force mode */
- if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
- return;
-
- if (ant_isolation == mvm->last_ant_isol)
- return;
-
- for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
- if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
- break;
-
- lower_bound = antenna_coupling_ranges[lut].range;
-
- if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
- upper_bound = antenna_coupling_ranges[lut + 1].range;
- else
- upper_bound = antenna_coupling_ranges[lut].range;
-
- IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
- ant_isolation, lower_bound, upper_bound, lut);
-
- mvm->last_ant_isol = ant_isolation;
-
- if (mvm->last_corun_lut == lut)
- return;
-
- mvm->last_corun_lut = lut;
-
- bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
- if (!bt_cmd)
- return;
- cmd.data[0] = bt_cmd;
-
- bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
- BT_VALID_CORUN_LUT_20 |
- BT_VALID_CORUN_LUT_40);
-
- /* For the moment, use the same LUT for 20GHz and 40GHz */
- memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
- sizeof(bt_cmd->bt4_corun_lut20));
-
- memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
- sizeof(bt_cmd->bt4_corun_lut40));
-
- if (iwl_mvm_send_cmd(mvm, &cmd))
- IWL_ERR(mvm, "failed to send BT_CONFIG command\n");
-
- kfree(bt_cmd);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
deleted file mode 100644
index 5c21231e195d..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/constants.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __MVM_CONSTANTS_H
-#define __MVM_CONSTANTS_H
-
-#include <linux/ieee80211.h>
-
-#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
-#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
-#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
-#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
-#define IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */
-#define IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */
-#define IWL_MVM_P2P_LOWLATENCY_PS_ENABLE 0
-#define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC)
-#define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC)
-#define IWL_MVM_UAPSD_QUEUES (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\
- IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\
- IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\
- IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
-#define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20
-#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 8
-#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30
-#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20
-#define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50
-#define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50
-#define IWL_MVM_PS_SNOOZE_INTERVAL 25
-#define IWL_MVM_PS_SNOOZE_WINDOW 50
-#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25
-#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64
-#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62
-#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65
-#define IWL_MVM_BT_COEX_SYNC2SCO 1
-#define IWL_MVM_BT_COEX_CORUNNING 0
-#define IWL_MVM_BT_COEX_MPLUT 1
-#define IWL_MVM_BT_COEX_RRC 1
-#define IWL_MVM_BT_COEX_TTC 1
-#define IWL_MVM_BT_COEX_MPLUT_REG0 0x22002200
-#define IWL_MVM_BT_COEX_MPLUT_REG1 0x11118451
-#define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS 30
-#define IWL_MVM_FW_MCAST_FILTER_PASS_ALL 0
-#define IWL_MVM_FW_BCAST_FILTER_PASS_ALL 0
-#define IWL_MVM_QUOTA_THRESHOLD 4
-#define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0
-#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1
-#define IWL_MVM_TOF_IS_RESPONDER 0
-#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1
-#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2
-#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1
-#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES 3
-#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES 3
-#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES 2
-#define IWL_MVM_RS_INITIAL_LEGACY_RETRIES 2
-#define IWL_MVM_RS_SECONDARY_LEGACY_RETRIES 1
-#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES 16
-#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES 3
-#define IWL_MVM_RS_SECONDARY_SISO_RETRIES 1
-#define IWL_MVM_RS_RATE_MIN_FAILURE_TH 3
-#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH 8
-#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT 5 /* Seconds */
-#define IWL_MVM_RS_IDLE_TIMEOUT 5 /* Seconds */
-#define IWL_MVM_RS_MISSED_RATE_MAX 15
-#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT 160
-#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT 480
-#define IWL_MVM_RS_LEGACY_TABLE_COUNT 160
-#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT 400
-#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT 4500
-#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT 1500
-#define IWL_MVM_RS_SR_FORCE_DECREASE 15 /* percent */
-#define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */
-#define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */
-#define IWL_MVM_RS_AGG_DISABLE_START 3
-#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */
-#define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */
-#define IWL_MVM_RS_TPC_TX_POWER_STEP 3
-
-#endif /* __MVM_CONSTANTS_H */
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
deleted file mode 100644
index 29ae58ebf223..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ /dev/null
@@ -1,2100 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/etherdevice.h>
-#include <linux/ip.h>
-#include <linux/fs.h>
-#include <net/cfg80211.h>
-#include <net/ipv6.h>
-#include <net/tcp.h>
-#include <net/addrconf.h>
-#include "iwl-modparams.h"
-#include "fw-api.h"
-#include "mvm.h"
-
-void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_gtk_rekey_data *data)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (iwlwifi_mod_params.sw_crypto)
- return;
-
- mutex_lock(&mvm->mutex);
-
- memcpy(mvmvif->rekey_data.kek, data->kek, NL80211_KEK_LEN);
- memcpy(mvmvif->rekey_data.kck, data->kck, NL80211_KCK_LEN);
- mvmvif->rekey_data.replay_ctr =
- cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
- mvmvif->rekey_data.valid = true;
-
- mutex_unlock(&mvm->mutex);
-}
-
-#if IS_ENABLED(CONFIG_IPV6)
-void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct inet6_dev *idev)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct inet6_ifaddr *ifa;
- int idx = 0;
-
- read_lock_bh(&idev->lock);
- list_for_each_entry(ifa, &idev->addr_list, if_list) {
- mvmvif->target_ipv6_addrs[idx] = ifa->addr;
- idx++;
- if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
- break;
- }
- read_unlock_bh(&idev->lock);
-
- mvmvif->num_target_ipv6_addrs = idx;
-}
-#endif
-
-void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, int idx)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- mvmvif->tx_key_idx = idx;
-}
-
-static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out)
-{
- int i;
-
- for (i = 0; i < IWL_P1K_SIZE; i++)
- out[i] = cpu_to_le16(p1k[i]);
-}
-
-struct wowlan_key_data {
- struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
- struct iwl_wowlan_tkip_params_cmd *tkip;
- bool error, use_rsc_tsc, use_tkip;
- int wep_key_idx;
-};
-
-static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key,
- void *_data)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct wowlan_key_data *data = _data;
- struct aes_sc *aes_sc, *aes_tx_sc = NULL;
- struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
- struct iwl_p1k_cache *rx_p1ks;
- u8 *rx_mic_key;
- struct ieee80211_key_seq seq;
- u32 cur_rx_iv32 = 0;
- u16 p1k[IWL_P1K_SIZE];
- int ret, i;
-
- mutex_lock(&mvm->mutex);
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */
- struct {
- struct iwl_mvm_wep_key_cmd wep_key_cmd;
- struct iwl_mvm_wep_key wep_key;
- } __packed wkc = {
- .wep_key_cmd.mac_id_n_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color)),
- .wep_key_cmd.num_keys = 1,
- /* firmware sets STA_KEY_FLG_WEP_13BYTES */
- .wep_key_cmd.decryption_type = STA_KEY_FLG_WEP,
- .wep_key.key_index = key->keyidx,
- .wep_key.key_size = key->keylen,
- };
-
- /*
- * This will fail -- the key functions don't set support
- * pairwise WEP keys. However, that's better than silently
- * failing WoWLAN. Or maybe not?
- */
- if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
- break;
-
- memcpy(&wkc.wep_key.key[3], key->key, key->keylen);
- if (key->keyidx == mvmvif->tx_key_idx) {
- /* TX key must be at offset 0 */
- wkc.wep_key.key_offset = 0;
- } else {
- /* others start at 1 */
- data->wep_key_idx++;
- wkc.wep_key.key_offset = data->wep_key_idx;
- }
-
- ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc);
- data->error = ret != 0;
-
- mvm->ptk_ivlen = key->iv_len;
- mvm->ptk_icvlen = key->icv_len;
- mvm->gtk_ivlen = key->iv_len;
- mvm->gtk_icvlen = key->icv_len;
-
- /* don't upload key again */
- goto out_unlock;
- }
- default:
- data->error = true;
- goto out_unlock;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- /*
- * Ignore CMAC keys -- the WoWLAN firmware doesn't support them
- * but we also shouldn't abort suspend due to that. It does have
- * support for the IGTK key renewal, but doesn't really use the
- * IGTK for anything. This means we could spuriously wake up or
- * be deauthenticated, but that was considered acceptable.
- */
- goto out_unlock;
- case WLAN_CIPHER_SUITE_TKIP:
- if (sta) {
- tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
- tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
-
- rx_p1ks = data->tkip->rx_uni;
-
- ieee80211_get_key_tx_seq(key, &seq);
- tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
- tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
-
- ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
- iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k);
-
- memcpy(data->tkip->mic_keys.tx,
- &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
- IWL_MIC_KEY_SIZE);
-
- rx_mic_key = data->tkip->mic_keys.rx_unicast;
- } else {
- tkip_sc =
- data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
- rx_p1ks = data->tkip->rx_multi;
- rx_mic_key = data->tkip->mic_keys.rx_mcast;
- }
-
- /*
- * For non-QoS this relies on the fact that both the uCode and
- * mac80211 use TID 0 (as they need to to avoid replay attacks)
- * for checking the IV in the frames.
- */
- for (i = 0; i < IWL_NUM_RSC; i++) {
- ieee80211_get_key_rx_seq(key, i, &seq);
- tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
- tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
- /* wrapping isn't allowed, AP must rekey */
- if (seq.tkip.iv32 > cur_rx_iv32)
- cur_rx_iv32 = seq.tkip.iv32;
- }
-
- ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
- cur_rx_iv32, p1k);
- iwl_mvm_convert_p1k(p1k, rx_p1ks[0].p1k);
- ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
- cur_rx_iv32 + 1, p1k);
- iwl_mvm_convert_p1k(p1k, rx_p1ks[1].p1k);
-
- memcpy(rx_mic_key,
- &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
- IWL_MIC_KEY_SIZE);
-
- data->use_tkip = true;
- data->use_rsc_tsc = true;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- if (sta) {
- u64 pn64;
-
- aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
- aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
-
- pn64 = atomic64_read(&key->tx_pn);
- aes_tx_sc->pn = cpu_to_le64(pn64);
- } else {
- aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
- }
-
- /*
- * For non-QoS this relies on the fact that both the uCode and
- * mac80211 use TID 0 for checking the IV in the frames.
- */
- for (i = 0; i < IWL_NUM_RSC; i++) {
- u8 *pn = seq.ccmp.pn;
-
- ieee80211_get_key_rx_seq(key, i, &seq);
- aes_sc[i].pn = cpu_to_le64((u64)pn[5] |
- ((u64)pn[4] << 8) |
- ((u64)pn[3] << 16) |
- ((u64)pn[2] << 24) |
- ((u64)pn[1] << 32) |
- ((u64)pn[0] << 40));
- }
- data->use_rsc_tsc = true;
- break;
- }
-
- /*
- * The D3 firmware hardcodes the key offset 0 as the key it uses
- * to transmit packets to the AP, i.e. the PTK.
- */
- if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
- mvm->ptk_ivlen = key->iv_len;
- mvm->ptk_icvlen = key->icv_len;
- ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0);
- } else {
- /*
- * firmware only supports TSC/RSC for a single key,
- * so if there are multiple keep overwriting them
- * with new ones -- this relies on mac80211 doing
- * list_add_tail().
- */
- mvm->gtk_ivlen = key->iv_len;
- mvm->gtk_icvlen = key->icv_len;
- ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1);
- }
-
- data->error = ret != 0;
-out_unlock:
- mutex_unlock(&mvm->mutex);
-}
-
-static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
- struct cfg80211_wowlan *wowlan)
-{
- struct iwl_wowlan_patterns_cmd *pattern_cmd;
- struct iwl_host_cmd cmd = {
- .id = WOWLAN_PATTERNS,
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- };
- int i, err;
-
- if (!wowlan->n_patterns)
- return 0;
-
- cmd.len[0] = sizeof(*pattern_cmd) +
- wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern);
-
- pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
- if (!pattern_cmd)
- return -ENOMEM;
-
- pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
-
- for (i = 0; i < wowlan->n_patterns; i++) {
- int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
-
- memcpy(&pattern_cmd->patterns[i].mask,
- wowlan->patterns[i].mask, mask_len);
- memcpy(&pattern_cmd->patterns[i].pattern,
- wowlan->patterns[i].pattern,
- wowlan->patterns[i].pattern_len);
- pattern_cmd->patterns[i].mask_size = mask_len;
- pattern_cmd->patterns[i].pattern_size =
- wowlan->patterns[i].pattern_len;
- }
-
- cmd.data[0] = pattern_cmd;
- err = iwl_mvm_send_cmd(mvm, &cmd);
- kfree(pattern_cmd);
- return err;
-}
-
-enum iwl_mvm_tcp_packet_type {
- MVM_TCP_TX_SYN,
- MVM_TCP_RX_SYNACK,
- MVM_TCP_TX_DATA,
- MVM_TCP_RX_ACK,
- MVM_TCP_RX_WAKE,
- MVM_TCP_TX_FIN,
-};
-
-static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr)
-{
- __sum16 check = tcp_v4_check(len, saddr, daddr, 0);
- return cpu_to_le16(be16_to_cpu((__force __be16)check));
-}
-
-static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif,
- struct cfg80211_wowlan_tcp *tcp,
- void *_pkt, u8 *mask,
- __le16 *pseudo_hdr_csum,
- enum iwl_mvm_tcp_packet_type ptype)
-{
- struct {
- struct ethhdr eth;
- struct iphdr ip;
- struct tcphdr tcp;
- u8 data[];
- } __packed *pkt = _pkt;
- u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
- int i;
-
- pkt->eth.h_proto = cpu_to_be16(ETH_P_IP),
- pkt->ip.version = 4;
- pkt->ip.ihl = 5;
- pkt->ip.protocol = IPPROTO_TCP;
-
- switch (ptype) {
- case MVM_TCP_TX_SYN:
- case MVM_TCP_TX_DATA:
- case MVM_TCP_TX_FIN:
- memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN);
- memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN);
- pkt->ip.ttl = 128;
- pkt->ip.saddr = tcp->src;
- pkt->ip.daddr = tcp->dst;
- pkt->tcp.source = cpu_to_be16(tcp->src_port);
- pkt->tcp.dest = cpu_to_be16(tcp->dst_port);
- /* overwritten for TX SYN later */
- pkt->tcp.doff = sizeof(struct tcphdr) / 4;
- pkt->tcp.window = cpu_to_be16(65000);
- break;
- case MVM_TCP_RX_SYNACK:
- case MVM_TCP_RX_ACK:
- case MVM_TCP_RX_WAKE:
- memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN);
- memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN);
- pkt->ip.saddr = tcp->dst;
- pkt->ip.daddr = tcp->src;
- pkt->tcp.source = cpu_to_be16(tcp->dst_port);
- pkt->tcp.dest = cpu_to_be16(tcp->src_port);
- break;
- default:
- WARN_ON(1);
- return;
- }
-
- switch (ptype) {
- case MVM_TCP_TX_SYN:
- /* firmware assumes 8 option bytes - 8 NOPs for now */
- memset(pkt->data, 0x01, 8);
- ip_tot_len += 8;
- pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4;
- pkt->tcp.syn = 1;
- break;
- case MVM_TCP_TX_DATA:
- ip_tot_len += tcp->payload_len;
- memcpy(pkt->data, tcp->payload, tcp->payload_len);
- pkt->tcp.psh = 1;
- pkt->tcp.ack = 1;
- break;
- case MVM_TCP_TX_FIN:
- pkt->tcp.fin = 1;
- pkt->tcp.ack = 1;
- break;
- case MVM_TCP_RX_SYNACK:
- pkt->tcp.syn = 1;
- pkt->tcp.ack = 1;
- break;
- case MVM_TCP_RX_ACK:
- pkt->tcp.ack = 1;
- break;
- case MVM_TCP_RX_WAKE:
- ip_tot_len += tcp->wake_len;
- pkt->tcp.psh = 1;
- pkt->tcp.ack = 1;
- memcpy(pkt->data, tcp->wake_data, tcp->wake_len);
- break;
- }
-
- switch (ptype) {
- case MVM_TCP_TX_SYN:
- case MVM_TCP_TX_DATA:
- case MVM_TCP_TX_FIN:
- pkt->ip.tot_len = cpu_to_be16(ip_tot_len);
- pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl);
- break;
- case MVM_TCP_RX_WAKE:
- for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) {
- u8 tmp = tcp->wake_mask[i];
- mask[i + 6] |= tmp << 6;
- if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8))
- mask[i + 7] = tmp >> 2;
- }
- /* fall through for ethernet/IP/TCP headers mask */
- case MVM_TCP_RX_SYNACK:
- case MVM_TCP_RX_ACK:
- mask[0] = 0xff; /* match ethernet */
- /*
- * match ethernet, ip.version, ip.ihl
- * the ip.ihl half byte is really masked out by firmware
- */
- mask[1] = 0x7f;
- mask[2] = 0x80; /* match ip.protocol */
- mask[3] = 0xfc; /* match ip.saddr, ip.daddr */
- mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */
- mask[5] = 0x80; /* match tcp flags */
- /* leave rest (0 or set for MVM_TCP_RX_WAKE) */
- break;
- };
-
- *pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr),
- pkt->ip.saddr, pkt->ip.daddr);
-}
-
-static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct cfg80211_wowlan_tcp *tcp)
-{
- struct iwl_wowlan_remote_wake_config *cfg;
- struct iwl_host_cmd cmd = {
- .id = REMOTE_WAKE_CONFIG_CMD,
- .len = { sizeof(*cfg), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
- int ret;
-
- if (!tcp)
- return 0;
-
- cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
- if (!cfg)
- return -ENOMEM;
- cmd.data[0] = cfg;
-
- cfg->max_syn_retries = 10;
- cfg->max_data_retries = 10;
- cfg->tcp_syn_ack_timeout = 1; /* seconds */
- cfg->tcp_ack_timeout = 1; /* seconds */
-
- /* SYN (TX) */
- iwl_mvm_build_tcp_packet(
- 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(
- 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(
- 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 =
- cpu_to_le16(tcp->payload_len);
- cfg->sequence_number_offset = tcp->payload_seq.offset;
- /* length must be 0..4, the field is little endian */
- cfg->sequence_number_length = tcp->payload_seq.len;
- cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start);
- cfg->keepalive_interval = cpu_to_le16(tcp->data_interval);
- if (tcp->payload_tok.len) {
- cfg->token_offset = tcp->payload_tok.offset;
- cfg->token_length = tcp->payload_tok.len;
- cfg->num_tokens =
- cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len);
- memcpy(cfg->tokens, tcp->payload_tok.token_stream,
- tcp->tokens_size);
- } else {
- /* set tokens to max value to almost never run out */
- cfg->num_tokens = cpu_to_le16(65535);
- }
-
- /* ACK (RX) */
- iwl_mvm_build_tcp_packet(
- 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);
- cfg->keepalive_ack_rx.info.tcp_payload_length = 0;
-
- /* WAKEUP (RX) */
- iwl_mvm_build_tcp_packet(
- 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 =
- cpu_to_le16(tcp->wake_len);
-
- /* FIN */
- iwl_mvm_build_tcp_packet(
- 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;
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- kfree(cfg);
-
- return ret;
-}
-
-static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *ap_sta)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct ieee80211_chanctx_conf *ctx;
- u8 chains_static, chains_dynamic;
- struct cfg80211_chan_def chandef;
- int ret, i;
- struct iwl_binding_cmd binding_cmd = {};
- struct iwl_time_quota_cmd quota_cmd = {};
- u32 status;
-
- /* add back the PHY */
- if (WARN_ON(!mvmvif->phy_ctxt))
- return -EINVAL;
-
- rcu_read_lock();
- ctx = rcu_dereference(vif->chanctx_conf);
- if (WARN_ON(!ctx)) {
- rcu_read_unlock();
- return -EINVAL;
- }
- chandef = ctx->def;
- chains_static = ctx->rx_chains_static;
- chains_dynamic = ctx->rx_chains_dynamic;
- rcu_read_unlock();
-
- ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef,
- chains_static, chains_dynamic);
- if (ret)
- return ret;
-
- /* add back the MAC */
- mvmvif->uploaded = false;
-
- if (WARN_ON(!vif->bss_conf.assoc))
- return -EINVAL;
-
- ret = iwl_mvm_mac_ctxt_add(mvm, vif);
- if (ret)
- return ret;
-
- /* add back binding - XXX refactor? */
- binding_cmd.id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
- mvmvif->phy_ctxt->color));
- binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
- binding_cmd.phy =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
- mvmvif->phy_ctxt->color));
- binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color));
- for (i = 1; i < MAX_MACS_IN_BINDING; i++)
- binding_cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
-
- status = 0;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
- sizeof(binding_cmd), &binding_cmd,
- &status);
- if (ret) {
- IWL_ERR(mvm, "Failed to add binding: %d\n", ret);
- return ret;
- }
-
- if (status) {
- IWL_ERR(mvm, "Binding command failed: %u\n", status);
- return -EIO;
- }
-
- ret = iwl_mvm_sta_send_to_fw(mvm, ap_sta, false);
- if (ret)
- return ret;
- rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta);
-
- ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- if (ret)
- return ret;
-
- /* and some quota */
- quota_cmd.quotas[0].id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
- mvmvif->phy_ctxt->color));
- quota_cmd.quotas[0].quota = cpu_to_le32(IWL_MVM_MAX_QUOTA);
- quota_cmd.quotas[0].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA);
-
- for (i = 1; i < MAX_BINDINGS; i++)
- quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
- sizeof(quota_cmd), &quota_cmd);
- if (ret)
- IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
-
- if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm))
- IWL_ERR(mvm, "Failed to initialize D3 LAR information\n");
-
- return 0;
-}
-
-static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_nonqos_seq_query_cmd query_cmd = {
- .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET),
- .mac_id_n_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color)),
- };
- struct iwl_host_cmd cmd = {
- .id = NON_QOS_TX_COUNTER_CMD,
- .flags = CMD_WANT_SKB,
- };
- int err;
- u32 size;
-
- cmd.data[0] = &query_cmd;
- cmd.len[0] = sizeof(query_cmd);
-
- err = iwl_mvm_send_cmd(mvm, &cmd);
- if (err)
- return err;
-
- size = iwl_rx_packet_payload_len(cmd.resp_pkt);
- if (size < sizeof(__le16)) {
- err = -EINVAL;
- } else {
- err = le16_to_cpup((__le16 *)cmd.resp_pkt->data);
- /* firmware returns next, not last-used seqno */
- err = (u16) (err - 0x10);
- }
-
- iwl_free_resp(&cmd);
- return err;
-}
-
-void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_nonqos_seq_query_cmd query_cmd = {
- .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET),
- .mac_id_n_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color)),
- .value = cpu_to_le16(mvmvif->seqno),
- };
-
- /* return if called during restart, not resume from D3 */
- if (!mvmvif->seqno_valid)
- return;
-
- mvmvif->seqno_valid = false;
-
- if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0,
- sizeof(query_cmd), &query_cmd))
- IWL_ERR(mvm, "failed to set non-QoS seqno\n");
-}
-
-static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm)
-{
- iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
-
- iwl_trans_stop_device(mvm->trans);
-
- /*
- * Set the HW restart bit -- this is mostly true as we're
- * going to load new firmware and reprogram that, though
- * the reprogramming is going to be manual to avoid adding
- * all the MACs that aren't support.
- * We don't have to clear up everything though because the
- * reprogramming is manual. When we resume, we'll actually
- * go through a proper restart sequence again to switch
- * back to the runtime firmware image.
- */
- set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
-
- mvm->ptk_ivlen = 0;
- mvm->ptk_icvlen = 0;
- mvm->ptk_ivlen = 0;
- mvm->ptk_icvlen = 0;
-
- return iwl_mvm_load_d3_fw(mvm);
-}
-
-static int
-iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
- struct cfg80211_wowlan *wowlan,
- struct iwl_wowlan_config_cmd *wowlan_config_cmd,
- struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
- struct ieee80211_sta *ap_sta)
-{
- int ret;
- struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
-
- /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */
-
- wowlan_config_cmd->is_11n_connection =
- ap_sta->ht_cap.ht_supported;
-
- /* Query the last used seqno and set it */
- ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
- if (ret < 0)
- return ret;
-
- wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret);
-
- iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd);
-
- if (wowlan->disconnect)
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
- IWL_WOWLAN_WAKEUP_LINK_CHANGE);
- if (wowlan->magic_pkt)
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
- if (wowlan->gtk_rekey_failure)
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
- if (wowlan->eap_identity_req)
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
- if (wowlan->four_way_handshake)
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
- if (wowlan->n_patterns)
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
-
- if (wowlan->rfkill_release)
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
-
- if (wowlan->tcp) {
- /*
- * Set the "link change" (really "link lost") flag as well
- * since that implies losing the TCP connection.
- */
- wowlan_config_cmd->wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
- IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
- IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
- IWL_WOWLAN_WAKEUP_LINK_CHANGE);
- }
-
- return 0;
-}
-
-static int
-iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
- struct cfg80211_wowlan *wowlan,
- struct iwl_wowlan_config_cmd *wowlan_config_cmd,
- struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
- struct ieee80211_sta *ap_sta)
-{
- struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
- struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
- struct wowlan_key_data key_data = {
- .use_rsc_tsc = false,
- .tkip = &tkip_cmd,
- .use_tkip = false,
- };
- int ret;
-
- ret = iwl_mvm_switch_to_d3(mvm);
- if (ret)
- return ret;
-
- ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta);
- if (ret)
- return ret;
-
- key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
- if (!key_data.rsc_tsc)
- return -ENOMEM;
-
- if (!iwlwifi_mod_params.sw_crypto) {
- /*
- * This needs to be unlocked due to lock ordering
- * constraints. Since we're in the suspend path
- * that isn't really a problem though.
- */
- mutex_unlock(&mvm->mutex);
- ieee80211_iter_keys(mvm->hw, vif,
- iwl_mvm_wowlan_program_keys,
- &key_data);
- mutex_lock(&mvm->mutex);
- if (key_data.error) {
- ret = -EIO;
- goto out;
- }
-
- if (key_data.use_rsc_tsc) {
- struct iwl_host_cmd rsc_tsc_cmd = {
- .id = WOWLAN_TSC_RSC_PARAM,
- .data[0] = key_data.rsc_tsc,
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- .len[0] = sizeof(*key_data.rsc_tsc),
- };
-
- ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd);
- if (ret)
- goto out;
- }
-
- if (key_data.use_tkip) {
- ret = iwl_mvm_send_cmd_pdu(mvm,
- WOWLAN_TKIP_PARAM,
- 0, sizeof(tkip_cmd),
- &tkip_cmd);
- if (ret)
- goto out;
- }
-
- if (mvmvif->rekey_data.valid) {
- memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
- memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
- NL80211_KCK_LEN);
- kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
- memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
- NL80211_KEK_LEN);
- kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
- kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
-
- ret = iwl_mvm_send_cmd_pdu(mvm,
- WOWLAN_KEK_KCK_MATERIAL, 0,
- sizeof(kek_kck_cmd),
- &kek_kck_cmd);
- if (ret)
- goto out;
- }
- }
-
- ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
- sizeof(*wowlan_config_cmd),
- wowlan_config_cmd);
- if (ret)
- goto out;
-
- ret = iwl_mvm_send_patterns(mvm, wowlan);
- if (ret)
- goto out;
-
- ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0);
- if (ret)
- goto out;
-
- ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
-
-out:
- kfree(key_data.rsc_tsc);
- return ret;
-}
-
-static int
-iwl_mvm_netdetect_config(struct iwl_mvm *mvm,
- struct cfg80211_wowlan *wowlan,
- struct cfg80211_sched_scan_request *nd_config,
- struct ieee80211_vif *vif)
-{
- struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
- int ret;
-
- ret = iwl_mvm_switch_to_d3(mvm);
- if (ret)
- return ret;
-
- /* rfkill release can be either for wowlan or netdetect */
- if (wowlan->rfkill_release)
- wowlan_config_cmd.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
- sizeof(wowlan_config_cmd),
- &wowlan_config_cmd);
- if (ret)
- return ret;
-
- ret = iwl_mvm_sched_scan_start(mvm, vif, nd_config, &mvm->nd_ies,
- IWL_MVM_SCAN_NETDETECT);
- if (ret)
- return ret;
-
- if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels))
- return -EBUSY;
-
- /* save the sched scan matchsets... */
- if (nd_config->n_match_sets) {
- mvm->nd_match_sets = kmemdup(nd_config->match_sets,
- sizeof(*nd_config->match_sets) *
- nd_config->n_match_sets,
- GFP_KERNEL);
- if (mvm->nd_match_sets)
- mvm->n_nd_match_sets = nd_config->n_match_sets;
- }
-
- /* ...and the sched scan channels for later reporting */
- mvm->nd_channels = kmemdup(nd_config->channels,
- sizeof(*nd_config->channels) *
- nd_config->n_channels,
- GFP_KERNEL);
- if (mvm->nd_channels)
- mvm->n_nd_channels = nd_config->n_channels;
-
- return 0;
-}
-
-static void iwl_mvm_free_nd(struct iwl_mvm *mvm)
-{
- kfree(mvm->nd_match_sets);
- mvm->nd_match_sets = NULL;
- mvm->n_nd_match_sets = 0;
- kfree(mvm->nd_channels);
- mvm->nd_channels = NULL;
- mvm->n_nd_channels = 0;
-}
-
-static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
- struct cfg80211_wowlan *wowlan,
- bool test)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct ieee80211_vif *vif = NULL;
- struct iwl_mvm_vif *mvmvif = NULL;
- struct ieee80211_sta *ap_sta = NULL;
- struct iwl_d3_manager_config d3_cfg_cmd_data = {
- /*
- * Program the minimum sleep time to 10 seconds, as many
- * platforms have issues processing a wakeup signal while
- * still being in the process of suspending.
- */
- .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
- };
- struct iwl_host_cmd d3_cfg_cmd = {
- .id = D3_CONFIG_CMD,
- .flags = CMD_WANT_SKB,
- .data[0] = &d3_cfg_cmd_data,
- .len[0] = sizeof(d3_cfg_cmd_data),
- };
- int ret;
- int len __maybe_unused;
-
- if (!wowlan) {
- /*
- * mac80211 shouldn't get here, but for D3 test
- * it doesn't warrant a warning
- */
- WARN_ON(!test);
- return -EINVAL;
- }
-
- mutex_lock(&mvm->mutex);
-
- vif = iwl_mvm_get_bss_vif(mvm);
- if (IS_ERR_OR_NULL(vif)) {
- ret = 1;
- goto out_noreset;
- }
-
- mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) {
- /* if we're not associated, this must be netdetect */
- if (!wowlan->nd_config && !mvm->nd_config) {
- ret = 1;
- goto out_noreset;
- }
-
- ret = iwl_mvm_netdetect_config(
- mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif);
- if (ret)
- goto out;
-
- mvm->net_detect = true;
- } else {
- struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
-
- ap_sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(ap_sta)) {
- ret = -EINVAL;
- goto out_noreset;
- }
-
- ret = iwl_mvm_get_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
- vif, mvmvif, ap_sta);
- if (ret)
- goto out_noreset;
- ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
- vif, mvmvif, ap_sta);
- if (ret)
- goto out;
-
- mvm->net_detect = false;
- }
-
- ret = iwl_mvm_power_update_device(mvm);
- if (ret)
- goto out;
-
- ret = iwl_mvm_power_update_mac(mvm);
- if (ret)
- goto out;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvm->d3_wake_sysassert)
- d3_cfg_cmd_data.wakeup_flags |=
- cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR);
-#endif
-
- /* must be last -- this switches firmware state */
- ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd);
- if (ret)
- goto out;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- len = iwl_rx_packet_payload_len(d3_cfg_cmd.resp_pkt);
- if (len >= sizeof(u32)) {
- mvm->d3_test_pme_ptr =
- le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data);
- }
-#endif
- iwl_free_resp(&d3_cfg_cmd);
-
- clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
-
- iwl_trans_d3_suspend(mvm->trans, test);
- out:
- if (ret < 0) {
- iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
- ieee80211_restart_hw(mvm->hw);
- iwl_mvm_free_nd(mvm);
- }
- out_noreset:
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm)
-{
- struct iwl_notification_wait wait_d3;
- static const u16 d3_notif[] = { D3_CONFIG_CMD };
- int ret;
-
- iwl_init_notification_wait(&mvm->notif_wait, &wait_d3,
- d3_notif, ARRAY_SIZE(d3_notif),
- NULL, NULL);
-
- ret = iwl_mvm_enter_d0i3(mvm->hw->priv);
- if (ret)
- goto remove_notif;
-
- ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ);
- WARN_ON_ONCE(ret);
- return ret;
-
-remove_notif:
- iwl_remove_notification(&mvm->notif_wait, &wait_d3);
- return ret;
-}
-
-int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- /* make sure the d0i3 exit work is not pending */
- flush_work(&mvm->d0i3_exit_work);
-
- ret = iwl_trans_suspend(mvm->trans);
- if (ret)
- return ret;
-
- mvm->trans->wowlan_d0i3 = wowlan->any;
- if (mvm->trans->wowlan_d0i3) {
- /* 'any' trigger means d0i3 usage */
- if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
- ret = iwl_mvm_enter_d0i3_sync(mvm);
-
- if (ret)
- return ret;
- }
-
- mutex_lock(&mvm->d0i3_suspend_mutex);
- __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
- mutex_unlock(&mvm->d0i3_suspend_mutex);
-
- iwl_trans_d3_suspend(mvm->trans, false);
-
- return 0;
- }
-
- return __iwl_mvm_suspend(hw, wowlan, false);
-}
-
-/* converted data from the different status responses */
-struct iwl_wowlan_status_data {
- u16 pattern_number;
- u16 qos_seq_ctr[8];
- u32 wakeup_reasons;
- u32 wake_packet_length;
- u32 wake_packet_bufsize;
- const u8 *wake_packet;
-};
-
-static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_wowlan_status_data *status)
-{
- struct sk_buff *pkt = NULL;
- struct cfg80211_wowlan_wakeup wakeup = {
- .pattern_idx = -1,
- };
- struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
- u32 reasons = status->wakeup_reasons;
-
- if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
- wakeup_report = NULL;
- goto report;
- }
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET)
- wakeup.magic_pkt = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN)
- wakeup.pattern_idx =
- status->pattern_number;
-
- if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH))
- wakeup.disconnect = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)
- wakeup.gtk_rekey_failure = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
- wakeup.rfkill_release = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST)
- wakeup.eap_identity_req = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE)
- wakeup.four_way_handshake = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS)
- wakeup.tcp_connlost = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE)
- wakeup.tcp_nomoretokens = true;
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET)
- wakeup.tcp_match = true;
-
- if (status->wake_packet_bufsize) {
- int pktsize = status->wake_packet_bufsize;
- int pktlen = status->wake_packet_length;
- const u8 *pktdata = status->wake_packet;
- struct ieee80211_hdr *hdr = (void *)pktdata;
- int truncated = pktlen - pktsize;
-
- /* this would be a firmware bug */
- if (WARN_ON_ONCE(truncated < 0))
- truncated = 0;
-
- if (ieee80211_is_data(hdr->frame_control)) {
- int hdrlen = ieee80211_hdrlen(hdr->frame_control);
- int ivlen = 0, icvlen = 4; /* also FCS */
-
- pkt = alloc_skb(pktsize, GFP_KERNEL);
- if (!pkt)
- goto report;
-
- memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen);
- pktdata += hdrlen;
- pktsize -= hdrlen;
-
- if (ieee80211_has_protected(hdr->frame_control)) {
- /*
- * This is unlocked and using gtk_i(c)vlen,
- * but since everything is under RTNL still
- * that's not really a problem - changing
- * it would be difficult.
- */
- if (is_multicast_ether_addr(hdr->addr1)) {
- ivlen = mvm->gtk_ivlen;
- icvlen += mvm->gtk_icvlen;
- } else {
- ivlen = mvm->ptk_ivlen;
- icvlen += mvm->ptk_icvlen;
- }
- }
-
- /* if truncated, FCS/ICV is (partially) gone */
- if (truncated >= icvlen) {
- icvlen = 0;
- truncated -= icvlen;
- } else {
- icvlen -= truncated;
- truncated = 0;
- }
-
- pktsize -= ivlen + icvlen;
- pktdata += ivlen;
-
- memcpy(skb_put(pkt, pktsize), pktdata, pktsize);
-
- if (ieee80211_data_to_8023(pkt, vif->addr, vif->type))
- goto report;
- wakeup.packet = pkt->data;
- wakeup.packet_present_len = pkt->len;
- wakeup.packet_len = pkt->len - truncated;
- wakeup.packet_80211 = false;
- } else {
- int fcslen = 4;
-
- if (truncated >= 4) {
- truncated -= 4;
- fcslen = 0;
- } else {
- fcslen -= truncated;
- truncated = 0;
- }
- pktsize -= fcslen;
- wakeup.packet = status->wake_packet;
- wakeup.packet_present_len = pktsize;
- wakeup.packet_len = pktlen - truncated;
- wakeup.packet_80211 = true;
- }
- }
-
- report:
- ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
- kfree_skb(pkt);
-}
-
-static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc,
- struct ieee80211_key_seq *seq)
-{
- u64 pn;
-
- pn = le64_to_cpu(sc->pn);
- seq->ccmp.pn[0] = pn >> 40;
- seq->ccmp.pn[1] = pn >> 32;
- seq->ccmp.pn[2] = pn >> 24;
- seq->ccmp.pn[3] = pn >> 16;
- seq->ccmp.pn[4] = pn >> 8;
- seq->ccmp.pn[5] = pn;
-}
-
-static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc,
- struct ieee80211_key_seq *seq)
-{
- seq->tkip.iv32 = le32_to_cpu(sc->iv32);
- seq->tkip.iv16 = le16_to_cpu(sc->iv16);
-}
-
-static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs,
- struct ieee80211_key_conf *key)
-{
- int tid;
-
- BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
-
- for (tid = 0; tid < IWL_NUM_RSC; tid++) {
- struct ieee80211_key_seq seq = {};
-
- iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
- ieee80211_set_key_rx_seq(key, tid, &seq);
- }
-}
-
-static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
- struct ieee80211_key_conf *key)
-{
- int tid;
-
- BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
-
- for (tid = 0; tid < IWL_NUM_RSC; tid++) {
- struct ieee80211_key_seq seq = {};
-
- iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq);
- ieee80211_set_key_rx_seq(key, tid, &seq);
- }
-}
-
-static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
- struct iwl_wowlan_status *status)
-{
- union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key);
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key);
- break;
- default:
- WARN_ON(1);
- }
-}
-
-struct iwl_mvm_d3_gtk_iter_data {
- struct iwl_wowlan_status *status;
- void *last_gtk;
- u32 cipher;
- bool find_phase, unhandled_cipher;
- int num_keys;
-};
-
-static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key,
- void *_data)
-{
- struct iwl_mvm_d3_gtk_iter_data *data = _data;
-
- if (data->unhandled_cipher)
- return;
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- /* ignore WEP completely, nothing to do */
- return;
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_TKIP:
- /* we support these */
- break;
- default:
- /* everything else (even CMAC for MFP) - disconnect from AP */
- data->unhandled_cipher = true;
- return;
- }
-
- data->num_keys++;
-
- /*
- * pairwise key - update sequence counters only;
- * note that this assumes no TDLS sessions are active
- */
- if (sta) {
- struct ieee80211_key_seq seq = {};
- union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc;
-
- if (data->find_phase)
- return;
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key);
- atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn));
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq);
- iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key);
- ieee80211_set_key_tx_seq(key, &seq);
- break;
- }
-
- /* that's it for this key */
- return;
- }
-
- if (data->find_phase) {
- data->last_gtk = key;
- data->cipher = key->cipher;
- return;
- }
-
- if (data->status->num_of_gtk_rekeys)
- ieee80211_remove_key(key);
- else if (data->last_gtk == key)
- iwl_mvm_set_key_rx_seq(key, data->status);
-}
-
-static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_wowlan_status *status)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_d3_gtk_iter_data gtkdata = {
- .status = status,
- };
- u32 disconnection_reasons =
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
-
- if (!status || !vif->bss_conf.bssid)
- return false;
-
- if (le32_to_cpu(status->wakeup_reasons) & disconnection_reasons)
- return false;
-
- /* find last GTK that we used initially, if any */
- gtkdata.find_phase = true;
- ieee80211_iter_keys(mvm->hw, vif,
- iwl_mvm_d3_update_gtks, &gtkdata);
- /* not trying to keep connections with MFP/unhandled ciphers */
- if (gtkdata.unhandled_cipher)
- return false;
- if (!gtkdata.num_keys)
- goto out;
- if (!gtkdata.last_gtk)
- return false;
-
- /*
- * invalidate all other GTKs that might still exist and update
- * the one that we used
- */
- gtkdata.find_phase = false;
- ieee80211_iter_keys(mvm->hw, vif,
- iwl_mvm_d3_update_gtks, &gtkdata);
-
- if (status->num_of_gtk_rekeys) {
- struct ieee80211_key_conf *key;
- struct {
- struct ieee80211_key_conf conf;
- u8 key[32];
- } conf = {
- .conf.cipher = gtkdata.cipher,
- .conf.keyidx = status->gtk.key_index,
- };
-
- switch (gtkdata.cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- conf.conf.keylen = WLAN_KEY_LEN_CCMP;
- memcpy(conf.conf.key, status->gtk.decrypt_key,
- WLAN_KEY_LEN_CCMP);
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- conf.conf.keylen = WLAN_KEY_LEN_TKIP;
- memcpy(conf.conf.key, status->gtk.decrypt_key, 16);
- /* leave TX MIC key zeroed, we don't use it anyway */
- memcpy(conf.conf.key +
- NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
- status->gtk.tkip_mic_key, 8);
- break;
- }
-
- key = ieee80211_gtk_rekey_add(vif, &conf.conf);
- if (IS_ERR(key))
- return false;
- iwl_mvm_set_key_rx_seq(key, status);
- }
-
- if (status->num_of_gtk_rekeys) {
- __be64 replay_ctr =
- cpu_to_be64(le64_to_cpu(status->replay_ctr));
- ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
- (void *)&replay_ctr, GFP_KERNEL);
- }
-
-out:
- mvmvif->seqno_valid = true;
- /* +0x10 because the set API expects next-to-use, not last-used */
- mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10;
-
- return true;
-}
-
-static struct iwl_wowlan_status *
-iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- u32 base = mvm->error_event_table;
- struct error_table_start {
- /* cf. struct iwl_error_event_table */
- u32 valid;
- u32 error_id;
- } err_info;
- struct iwl_host_cmd cmd = {
- .id = WOWLAN_GET_STATUSES,
- .flags = CMD_WANT_SKB,
- };
- struct iwl_wowlan_status *status, *fw_status;
- int ret, len, status_size;
-
- iwl_trans_read_mem_bytes(mvm->trans, base,
- &err_info, sizeof(err_info));
-
- if (err_info.valid) {
- IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n",
- err_info.valid, err_info.error_id);
- if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
- struct cfg80211_wowlan_wakeup wakeup = {
- .rfkill_release = true,
- };
- ieee80211_report_wowlan_wakeup(vif, &wakeup,
- GFP_KERNEL);
- }
- return ERR_PTR(-EIO);
- }
-
- /* only for tracing for now */
- ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL);
- if (ret)
- IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (ret) {
- IWL_ERR(mvm, "failed to query status (%d)\n", ret);
- return ERR_PTR(ret);
- }
-
- /* RF-kill already asserted again... */
- if (!cmd.resp_pkt) {
- fw_status = ERR_PTR(-ERFKILL);
- goto out_free_resp;
- }
-
- status_size = sizeof(*fw_status);
-
- len = iwl_rx_packet_payload_len(cmd.resp_pkt);
- if (len < status_size) {
- IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
- fw_status = ERR_PTR(-EIO);
- goto out_free_resp;
- }
-
- status = (void *)cmd.resp_pkt->data;
- if (len != (status_size +
- ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) {
- IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
- fw_status = ERR_PTR(-EIO);
- goto out_free_resp;
- }
-
- fw_status = kmemdup(status, len, GFP_KERNEL);
-
-out_free_resp:
- iwl_free_resp(&cmd);
- return fw_status;
-}
-
-/* releases the MVM mutex */
-static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_wowlan_status_data status;
- struct iwl_wowlan_status *fw_status;
- int i;
- bool keep;
- struct ieee80211_sta *ap_sta;
- struct iwl_mvm_sta *mvm_ap_sta;
-
- fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
- if (IS_ERR_OR_NULL(fw_status))
- goto out_unlock;
-
- status.pattern_number = le16_to_cpu(fw_status->pattern_number);
- for (i = 0; i < 8; i++)
- status.qos_seq_ctr[i] =
- le16_to_cpu(fw_status->qos_seq_ctr[i]);
- status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons);
- status.wake_packet_length =
- le32_to_cpu(fw_status->wake_packet_length);
- status.wake_packet_bufsize =
- le32_to_cpu(fw_status->wake_packet_bufsize);
- status.wake_packet = fw_status->wake_packet;
-
- /* still at hard-coded place 0 for D3 image */
- ap_sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[0],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(ap_sta))
- goto out_free;
-
- mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
- for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
- u16 seq = status.qos_seq_ctr[i];
- /* firmware stores last-used value, we store next value */
- seq += 0x10;
- mvm_ap_sta->tid_data[i].seq_number = seq;
- }
-
- /* now we have all the data we need, unlock to avoid mac80211 issues */
- mutex_unlock(&mvm->mutex);
-
- iwl_mvm_report_wakeup_reasons(mvm, vif, &status);
-
- keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status);
-
- kfree(fw_status);
- return keep;
-
-out_free:
- kfree(fw_status);
-out_unlock:
- mutex_unlock(&mvm->mutex);
- return false;
-}
-
-struct iwl_mvm_nd_query_results {
- u32 matched_profiles;
- struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
-};
-
-static int
-iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
- struct iwl_mvm_nd_query_results *results)
-{
- struct iwl_scan_offload_profiles_query *query;
- struct iwl_host_cmd cmd = {
- .id = SCAN_OFFLOAD_PROFILES_QUERY_CMD,
- .flags = CMD_WANT_SKB,
- };
- int ret, len;
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (ret) {
- IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret);
- return ret;
- }
-
- /* RF-kill already asserted again... */
- if (!cmd.resp_pkt) {
- ret = -ERFKILL;
- goto out_free_resp;
- }
-
- len = iwl_rx_packet_payload_len(cmd.resp_pkt);
- if (len < sizeof(*query)) {
- IWL_ERR(mvm, "Invalid scan offload profiles query response!\n");
- ret = -EIO;
- goto out_free_resp;
- }
-
- query = (void *)cmd.resp_pkt->data;
-
- results->matched_profiles = le32_to_cpu(query->matched_profiles);
- memcpy(results->matches, query->matches, sizeof(results->matches));
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done);
-#endif
-
-out_free_resp:
- iwl_free_resp(&cmd);
- return ret;
-}
-
-static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct cfg80211_wowlan_nd_info *net_detect = NULL;
- struct cfg80211_wowlan_wakeup wakeup = {
- .pattern_idx = -1,
- };
- struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
- struct iwl_mvm_nd_query_results query;
- struct iwl_wowlan_status *fw_status;
- unsigned long matched_profiles;
- u32 reasons = 0;
- int i, j, n_matches, ret;
-
- fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
- if (!IS_ERR_OR_NULL(fw_status)) {
- reasons = le32_to_cpu(fw_status->wakeup_reasons);
- kfree(fw_status);
- }
-
- if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
- wakeup.rfkill_release = true;
-
- if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS)
- goto out;
-
- ret = iwl_mvm_netdetect_query_results(mvm, &query);
- if (ret || !query.matched_profiles) {
- wakeup_report = NULL;
- goto out;
- }
-
- matched_profiles = query.matched_profiles;
- if (mvm->n_nd_match_sets) {
- n_matches = hweight_long(matched_profiles);
- } else {
- IWL_ERR(mvm, "no net detect match information available\n");
- n_matches = 0;
- }
-
- net_detect = kzalloc(sizeof(*net_detect) +
- (n_matches * sizeof(net_detect->matches[0])),
- GFP_KERNEL);
- if (!net_detect || !n_matches)
- goto out_report_nd;
-
- for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) {
- struct iwl_scan_offload_profile_match *fw_match;
- struct cfg80211_wowlan_nd_match *match;
- int idx, n_channels = 0;
-
- fw_match = &query.matches[i];
-
- for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++)
- n_channels += hweight8(fw_match->matching_channels[j]);
-
- match = kzalloc(sizeof(*match) +
- (n_channels * sizeof(*match->channels)),
- GFP_KERNEL);
- if (!match)
- goto out_report_nd;
-
- net_detect->matches[net_detect->n_matches++] = match;
-
- /* We inverted the order of the SSIDs in the scan
- * request, so invert the index here.
- */
- idx = mvm->n_nd_match_sets - i - 1;
- match->ssid.ssid_len = mvm->nd_match_sets[idx].ssid.ssid_len;
- memcpy(match->ssid.ssid, mvm->nd_match_sets[idx].ssid.ssid,
- match->ssid.ssid_len);
-
- if (mvm->n_nd_channels < n_channels)
- continue;
-
- for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++)
- if (fw_match->matching_channels[j / 8] & (BIT(j % 8)))
- match->channels[match->n_channels++] =
- mvm->nd_channels[j]->center_freq;
- }
-
-out_report_nd:
- wakeup.net_detect = net_detect;
-out:
- iwl_mvm_free_nd(mvm);
-
- mutex_unlock(&mvm->mutex);
- ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
-
- if (net_detect) {
- for (i = 0; i < net_detect->n_matches; i++)
- kfree(net_detect->matches[i]);
- kfree(net_detect);
- }
-}
-
-static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
-{
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN];
- u32 len = img->sec[IWL_UCODE_SECTION_DATA].len;
- u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset;
-
- if (!mvm->store_d3_resume_sram)
- return;
-
- if (!mvm->d3_resume_sram) {
- mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL);
- if (!mvm->d3_resume_sram)
- return;
- }
-
- iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len);
-#endif
-}
-
-static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- /* skip the one we keep connection on */
- if (data == vif)
- return;
-
- if (vif->type == NL80211_IFTYPE_STATION)
- ieee80211_resume_disconnect(vif);
-}
-
-static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
-{
- struct ieee80211_vif *vif = NULL;
- int ret;
- enum iwl_d3_status d3_status;
- bool keep = false;
-
- mutex_lock(&mvm->mutex);
-
- /* get the BSS vif pointer again */
- vif = iwl_mvm_get_bss_vif(mvm);
- if (IS_ERR_OR_NULL(vif))
- goto err;
-
- ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test);
- if (ret)
- goto err;
-
- if (d3_status != IWL_D3_STATUS_ALIVE) {
- IWL_INFO(mvm, "Device was reset during suspend\n");
- goto err;
- }
-
- /* query SRAM first in case we want event logging */
- iwl_mvm_read_d3_sram(mvm);
-
- /*
- * Query the current location and source from the D3 firmware so we
- * can play it back when we re-intiailize the D0 firmware
- */
- iwl_mvm_update_changed_regdom(mvm);
-
- if (mvm->net_detect) {
- iwl_mvm_query_netdetect_reasons(mvm, vif);
- /* has unlocked the mutex, so skip that */
- goto out;
- } else {
- keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (keep)
- mvm->keep_vif = vif;
-#endif
- /* has unlocked the mutex, so skip that */
- goto out_iterate;
- }
-
-err:
- iwl_mvm_free_nd(mvm);
- mutex_unlock(&mvm->mutex);
-
-out_iterate:
- if (!test)
- ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
-
-out:
- /* return 1 to reconfigure the device */
- set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
- set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
-
- /* We always return 1, which causes mac80211 to do a reconfig
- * with IEEE80211_RECONFIG_TYPE_RESTART. This type of
- * reconfig calls iwl_mvm_restart_complete(), where we unref
- * the IWL_MVM_REF_UCODE_DOWN, so we need to take the
- * reference here.
- */
- iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
- return 1;
-}
-
-static int iwl_mvm_resume_d3(struct iwl_mvm *mvm)
-{
- iwl_trans_resume(mvm->trans);
-
- return __iwl_mvm_resume(mvm, false);
-}
-
-static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
-{
- bool exit_now;
- enum iwl_d3_status d3_status;
-
- iwl_trans_d3_resume(mvm->trans, &d3_status, false);
-
- /*
- * make sure to clear D0I3_DEFER_WAKEUP before
- * calling iwl_trans_resume(), which might wait
- * for d0i3 exit completion.
- */
- mutex_lock(&mvm->d0i3_suspend_mutex);
- __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
- exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
- &mvm->d0i3_suspend_flags);
- mutex_unlock(&mvm->d0i3_suspend_mutex);
- if (exit_now) {
- IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
- _iwl_mvm_exit_d0i3(mvm);
- }
-
- iwl_trans_resume(mvm->trans);
-
- if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
- int ret = iwl_mvm_exit_d0i3(mvm->hw->priv);
-
- if (ret)
- return ret;
- /*
- * d0i3 exit will be deferred until reconfig_complete.
- * make sure there we are out of d0i3.
- */
- }
- return 0;
-}
-
-int iwl_mvm_resume(struct ieee80211_hw *hw)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- /* 'any' trigger means d0i3 was used */
- if (hw->wiphy->wowlan_config->any)
- return iwl_mvm_resume_d0i3(mvm);
- else
- return iwl_mvm_resume_d3(mvm);
-}
-
-void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- device_set_wakeup_enable(mvm->trans->dev, enabled);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
-{
- struct iwl_mvm *mvm = inode->i_private;
- int err;
-
- if (mvm->d3_test_active)
- return -EBUSY;
-
- file->private_data = inode->i_private;
-
- ieee80211_stop_queues(mvm->hw);
- synchronize_net();
-
- /* start pseudo D3 */
- rtnl_lock();
- err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
- rtnl_unlock();
- if (err > 0)
- err = -EINVAL;
- if (err) {
- ieee80211_wake_queues(mvm->hw);
- return err;
- }
- mvm->d3_test_active = true;
- mvm->keep_vif = NULL;
- return 0;
-}
-
-static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- u32 pme_asserted;
-
- while (true) {
- /* read pme_ptr if available */
- if (mvm->d3_test_pme_ptr) {
- pme_asserted = iwl_trans_read_mem32(mvm->trans,
- mvm->d3_test_pme_ptr);
- if (pme_asserted)
- break;
- }
-
- if (msleep_interruptible(100))
- break;
- }
-
- return 0;
-}
-
-static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- /* skip the one we keep connection on */
- if (_data == vif)
- return;
-
- if (vif->type == NL80211_IFTYPE_STATION)
- ieee80211_connection_loss(vif);
-}
-
-static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
-{
- struct iwl_mvm *mvm = inode->i_private;
- int remaining_time = 10;
-
- mvm->d3_test_active = false;
- rtnl_lock();
- __iwl_mvm_resume(mvm, true);
- rtnl_unlock();
- iwl_abort_notification_waits(&mvm->notif_wait);
- ieee80211_restart_hw(mvm->hw);
-
- /* wait for restart and disconnect all interfaces */
- while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
- remaining_time > 0) {
- remaining_time--;
- msleep(1000);
- }
-
- if (remaining_time == 0)
- IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n");
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif);
-
- ieee80211_wake_queues(mvm->hw);
-
- return 0;
-}
-
-const struct file_operations iwl_dbgfs_d3_test_ops = {
- .llseek = no_llseek,
- .open = iwl_mvm_d3_test_open,
- .read = iwl_mvm_d3_test_read,
- .release = iwl_mvm_d3_test_release,
-};
-#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
deleted file mode 100644
index 7904b41a04c6..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
+++ /dev/null
@@ -1,1483 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include "mvm.h"
-#include "fw-api-tof.h"
-#include "debugfs.h"
-
-static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- enum iwl_dbgfs_pm_mask param, int val)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
-
- dbgfs_pm->mask |= param;
-
- switch (param) {
- case MVM_DEBUGFS_PM_KEEP_ALIVE: {
- int dtimper = vif->bss_conf.dtim_period ?: 1;
- int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
-
- IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
- if (val * MSEC_PER_SEC < 3 * dtimper_msec)
- IWL_WARN(mvm,
- "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
- val * MSEC_PER_SEC, 3 * dtimper_msec);
- dbgfs_pm->keep_alive_seconds = val;
- break;
- }
- case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
- IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
- val ? "enabled" : "disabled");
- dbgfs_pm->skip_over_dtim = val;
- break;
- case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
- IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
- dbgfs_pm->skip_dtim_periods = val;
- break;
- case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
- IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
- dbgfs_pm->rx_data_timeout = val;
- break;
- case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
- IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
- dbgfs_pm->tx_data_timeout = val;
- break;
- case MVM_DEBUGFS_PM_LPRX_ENA:
- IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
- dbgfs_pm->lprx_ena = val;
- break;
- case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
- IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
- dbgfs_pm->lprx_rssi_threshold = val;
- break;
- case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
- IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
- dbgfs_pm->snooze_ena = val;
- break;
- case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
- IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
- dbgfs_pm->uapsd_misbehaving = val;
- break;
- case MVM_DEBUGFS_PM_USE_PS_POLL:
- IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val);
- dbgfs_pm->use_ps_poll = val;
- break;
- }
-}
-
-static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- enum iwl_dbgfs_pm_mask param;
- int val, ret;
-
- if (!strncmp("keep_alive=", buf, 11)) {
- if (sscanf(buf + 11, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_KEEP_ALIVE;
- } else if (!strncmp("skip_over_dtim=", buf, 15)) {
- if (sscanf(buf + 15, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
- } else if (!strncmp("skip_dtim_periods=", buf, 18)) {
- if (sscanf(buf + 18, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
- } else if (!strncmp("rx_data_timeout=", buf, 16)) {
- if (sscanf(buf + 16, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
- } else if (!strncmp("tx_data_timeout=", buf, 16)) {
- if (sscanf(buf + 16, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
- } else if (!strncmp("lprx=", buf, 5)) {
- if (sscanf(buf + 5, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_LPRX_ENA;
- } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
- if (sscanf(buf + 20, "%d", &val) != 1)
- return -EINVAL;
- if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
- POWER_LPRX_RSSI_THRESHOLD_MIN)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
- } else if (!strncmp("snooze_enable=", buf, 14)) {
- if (sscanf(buf + 14, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
- } else if (!strncmp("uapsd_misbehaving=", buf, 18)) {
- if (sscanf(buf + 18, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
- } else if (!strncmp("use_ps_poll=", buf, 12)) {
- if (sscanf(buf + 12, "%d", &val) != 1)
- return -EINVAL;
- param = MVM_DEBUGFS_PM_USE_PS_POLL;
- } else {
- return -EINVAL;
- }
-
- mutex_lock(&mvm->mutex);
- iwl_dbgfs_update_pm(mvm, vif, param, val);
- ret = iwl_mvm_power_update_mac(mvm);
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- char buf[64];
- int bufsz = sizeof(buf);
- int pos;
-
- pos = scnprintf(buf, bufsz, "bss limit = %d\n",
- vif->bss_conf.txpower);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- char buf[512];
- int bufsz = sizeof(buf);
- int pos;
-
- pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- u8 ap_sta_id;
- struct ieee80211_chanctx_conf *chanctx_conf;
- char buf[512];
- int bufsz = sizeof(buf);
- int pos = 0;
- int i;
-
- mutex_lock(&mvm->mutex);
-
- ap_sta_id = mvmvif->ap_sta_id;
-
- switch (ieee80211_vif_type_p2p(vif)) {
- case NL80211_IFTYPE_ADHOC:
- pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
- break;
- case NL80211_IFTYPE_STATION:
- pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
- break;
- case NL80211_IFTYPE_AP:
- pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
- break;
- case NL80211_IFTYPE_P2P_GO:
- pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
- break;
- default:
- break;
- }
-
- pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
- mvmvif->id, mvmvif->color);
- pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
- vif->bss_conf.bssid);
- pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
- for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++)
- pos += scnprintf(buf+pos, bufsz-pos,
- "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n",
- i, mvmvif->queue_params[i].txop,
- mvmvif->queue_params[i].cw_min,
- mvmvif->queue_params[i].cw_max,
- mvmvif->queue_params[i].aifs,
- mvmvif->queue_params[i].uapsd);
-
- if (vif->type == NL80211_IFTYPE_STATION &&
- ap_sta_id != IWL_MVM_STATION_COUNT) {
- struct ieee80211_sta *sta;
-
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
- lockdep_is_held(&mvm->mutex));
- if (!IS_ERR_OR_NULL(sta)) {
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
-
- pos += scnprintf(buf+pos, bufsz-pos,
- "ap_sta_id %d - reduced Tx power %d\n",
- ap_sta_id,
- mvm_sta->bt_reduced_txpower);
- }
- }
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
- if (chanctx_conf)
- pos += scnprintf(buf+pos, bufsz-pos,
- "idle rx chains %d, active rx chains: %d\n",
- chanctx_conf->rx_chains_static,
- chanctx_conf->rx_chains_dynamic);
- rcu_read_unlock();
-
- mutex_unlock(&mvm->mutex);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
- enum iwl_dbgfs_bf_mask param, int value)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
-
- dbgfs_bf->mask |= param;
-
- switch (param) {
- case MVM_DEBUGFS_BF_ENERGY_DELTA:
- dbgfs_bf->bf_energy_delta = value;
- break;
- case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
- dbgfs_bf->bf_roaming_energy_delta = value;
- break;
- case MVM_DEBUGFS_BF_ROAMING_STATE:
- dbgfs_bf->bf_roaming_state = value;
- break;
- case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
- dbgfs_bf->bf_temp_threshold = value;
- break;
- case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
- dbgfs_bf->bf_temp_fast_filter = value;
- break;
- case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
- dbgfs_bf->bf_temp_slow_filter = value;
- break;
- case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
- dbgfs_bf->bf_enable_beacon_filter = value;
- break;
- case MVM_DEBUGFS_BF_DEBUG_FLAG:
- dbgfs_bf->bf_debug_flag = value;
- break;
- case MVM_DEBUGFS_BF_ESCAPE_TIMER:
- dbgfs_bf->bf_escape_timer = value;
- break;
- case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
- dbgfs_bf->ba_enable_beacon_abort = value;
- break;
- case MVM_DEBUGFS_BA_ESCAPE_TIMER:
- dbgfs_bf->ba_escape_timer = value;
- break;
- }
-}
-
-static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- enum iwl_dbgfs_bf_mask param;
- int value, ret = 0;
-
- if (!strncmp("bf_energy_delta=", buf, 16)) {
- if (sscanf(buf+16, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BF_ENERGY_DELTA_MIN ||
- value > IWL_BF_ENERGY_DELTA_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_ENERGY_DELTA;
- } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
- if (sscanf(buf+24, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
- value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
- } else if (!strncmp("bf_roaming_state=", buf, 17)) {
- if (sscanf(buf+17, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BF_ROAMING_STATE_MIN ||
- value > IWL_BF_ROAMING_STATE_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_ROAMING_STATE;
- } else if (!strncmp("bf_temp_threshold=", buf, 18)) {
- if (sscanf(buf+18, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
- value > IWL_BF_TEMP_THRESHOLD_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
- } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
- if (sscanf(buf+20, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
- value > IWL_BF_TEMP_FAST_FILTER_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
- } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
- if (sscanf(buf+20, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
- value > IWL_BF_TEMP_SLOW_FILTER_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
- } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
- if (sscanf(buf+24, "%d", &value) != 1)
- return -EINVAL;
- if (value < 0 || value > 1)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
- } else if (!strncmp("bf_debug_flag=", buf, 14)) {
- if (sscanf(buf+14, "%d", &value) != 1)
- return -EINVAL;
- if (value < 0 || value > 1)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_DEBUG_FLAG;
- } else if (!strncmp("bf_escape_timer=", buf, 16)) {
- if (sscanf(buf+16, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BF_ESCAPE_TIMER_MIN ||
- value > IWL_BF_ESCAPE_TIMER_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
- } else if (!strncmp("ba_escape_timer=", buf, 16)) {
- if (sscanf(buf+16, "%d", &value) != 1)
- return -EINVAL;
- if (value < IWL_BA_ESCAPE_TIMER_MIN ||
- value > IWL_BA_ESCAPE_TIMER_MAX)
- return -EINVAL;
- param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
- } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
- if (sscanf(buf+23, "%d", &value) != 1)
- return -EINVAL;
- if (value < 0 || value > 1)
- return -EINVAL;
- param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
- } else {
- return -EINVAL;
- }
-
- mutex_lock(&mvm->mutex);
- iwl_dbgfs_update_bf(vif, param, value);
- if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
- ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
- else
- ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- char buf[256];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
- struct iwl_beacon_filter_cmd cmd = {
- IWL_BF_CMD_CONFIG_DEFAULTS,
- .bf_enable_beacon_filter =
- cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
- .ba_enable_beacon_abort =
- cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
- };
-
- iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
- if (mvmvif->bf_data.bf_enabled)
- cmd.bf_enable_beacon_filter = cpu_to_le32(1);
- else
- cmd.bf_enable_beacon_filter = 0;
-
- pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
- le32_to_cpu(cmd.bf_energy_delta));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
- le32_to_cpu(cmd.bf_roaming_energy_delta));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
- le32_to_cpu(cmd.bf_roaming_state));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
- le32_to_cpu(cmd.bf_temp_threshold));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
- le32_to_cpu(cmd.bf_temp_fast_filter));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
- le32_to_cpu(cmd.bf_temp_slow_filter));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
- le32_to_cpu(cmd.bf_enable_beacon_filter));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
- le32_to_cpu(cmd.bf_debug_flag));
- pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
- le32_to_cpu(cmd.bf_escape_timer));
- pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
- le32_to_cpu(cmd.ba_escape_timer));
- pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
- le32_to_cpu(cmd.ba_enable_beacon_abort));
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static inline char *iwl_dbgfs_is_match(char *name, char *buf)
-{
- int len = strlen(name);
-
- return !strncmp(name, buf, len) ? buf + len : NULL;
-}
-
-static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif,
- char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- u32 value;
- int ret = -EINVAL;
- char *data;
-
- mutex_lock(&mvm->mutex);
-
- data = iwl_dbgfs_is_match("tof_disabled=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.tof_cfg.tof_disabled = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("one_sided_disabled=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.tof_cfg.one_sided_disabled = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("is_debug_mode=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.tof_cfg.is_debug_mode = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("is_buf=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.tof_cfg.is_buf_required = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("send_tof_cfg=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0 && value) {
- ret = iwl_mvm_tof_config_cmd(mvm);
- goto out;
- }
- }
-
-out:
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_enable_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- char buf[256];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
- struct iwl_tof_config_cmd *cmd;
-
- cmd = &mvm->tof_data.tof_cfg;
-
- mutex_lock(&mvm->mutex);
-
- pos += scnprintf(buf + pos, bufsz - pos, "tof_disabled = %d\n",
- cmd->tof_disabled);
- pos += scnprintf(buf + pos, bufsz - pos, "one_sided_disabled = %d\n",
- cmd->one_sided_disabled);
- pos += scnprintf(buf + pos, bufsz - pos, "is_debug_mode = %d\n",
- cmd->is_debug_mode);
- pos += scnprintf(buf + pos, bufsz - pos, "is_buf_required = %d\n",
- cmd->is_buf_required);
-
- mutex_unlock(&mvm->mutex);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif,
- char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- u32 value;
- int ret = 0;
- char *data;
-
- mutex_lock(&mvm->mutex);
-
- data = iwl_dbgfs_is_match("burst_period=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (!ret)
- mvm->tof_data.responder_cfg.burst_period =
- cpu_to_le16(value);
- goto out;
- }
-
- data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.min_delta_ftm = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("burst_duration=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.burst_duration = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("num_of_burst_exp=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.num_of_burst_exp = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("abort_responder=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.abort_responder = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("get_ch_est=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.get_ch_est = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("recv_sta_req_params=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.recv_sta_req_params = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("channel_num=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.channel_num = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("bandwidth=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.bandwidth = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("rate=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.rate = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("bssid=", buf);
- if (data) {
- u8 *mac = mvm->tof_data.responder_cfg.bssid;
-
- if (!mac_pton(data, mac)) {
- ret = -EINVAL;
- goto out;
- }
- }
-
- data = iwl_dbgfs_is_match("tsf_timer_offset_msecs=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.tsf_timer_offset_msecs =
- cpu_to_le16(value);
- goto out;
- }
-
- data = iwl_dbgfs_is_match("toa_offset=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.toa_offset =
- cpu_to_le16(value);
- goto out;
- }
-
- data = iwl_dbgfs_is_match("center_freq=", buf);
- if (data) {
- struct iwl_tof_responder_config_cmd *cmd =
- &mvm->tof_data.responder_cfg;
-
- ret = kstrtou32(data, 10, &value);
- if (ret == 0 && value) {
- enum ieee80211_band band = (cmd->channel_num <= 14) ?
- IEEE80211_BAND_2GHZ :
- IEEE80211_BAND_5GHZ;
- struct ieee80211_channel chn = {
- .band = band,
- .center_freq = ieee80211_channel_to_frequency(
- cmd->channel_num, band),
- };
- struct cfg80211_chan_def chandef = {
- .chan = &chn,
- .center_freq1 =
- ieee80211_channel_to_frequency(value,
- band),
- };
-
- cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef);
- }
- goto out;
- }
-
- data = iwl_dbgfs_is_match("ftm_per_burst=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.ftm_per_burst = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("ftm_resp_ts_avail=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.ftm_resp_ts_avail = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("asap_mode=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.responder_cfg.asap_mode = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("send_responder_cfg=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0 && value) {
- ret = iwl_mvm_tof_responder_cmd(mvm, vif);
- goto out;
- }
- }
-
-out:
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_responder_params_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- char buf[256];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
- struct iwl_tof_responder_config_cmd *cmd;
-
- cmd = &mvm->tof_data.responder_cfg;
-
- mutex_lock(&mvm->mutex);
-
- pos += scnprintf(buf + pos, bufsz - pos, "burst_period = %d\n",
- le16_to_cpu(cmd->burst_period));
- pos += scnprintf(buf + pos, bufsz - pos, "burst_duration = %d\n",
- cmd->burst_duration);
- pos += scnprintf(buf + pos, bufsz - pos, "bandwidth = %d\n",
- cmd->bandwidth);
- pos += scnprintf(buf + pos, bufsz - pos, "channel_num = %d\n",
- cmd->channel_num);
- pos += scnprintf(buf + pos, bufsz - pos, "ctrl_ch_position = 0x%x\n",
- cmd->ctrl_ch_position);
- pos += scnprintf(buf + pos, bufsz - pos, "bssid = %pM\n",
- cmd->bssid);
- pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %d\n",
- cmd->min_delta_ftm);
- pos += scnprintf(buf + pos, bufsz - pos, "num_of_burst_exp = %d\n",
- cmd->num_of_burst_exp);
- pos += scnprintf(buf + pos, bufsz - pos, "rate = %d\n", cmd->rate);
- pos += scnprintf(buf + pos, bufsz - pos, "abort_responder = %d\n",
- cmd->abort_responder);
- pos += scnprintf(buf + pos, bufsz - pos, "get_ch_est = %d\n",
- cmd->get_ch_est);
- pos += scnprintf(buf + pos, bufsz - pos, "recv_sta_req_params = %d\n",
- cmd->recv_sta_req_params);
- pos += scnprintf(buf + pos, bufsz - pos, "ftm_per_burst = %d\n",
- cmd->ftm_per_burst);
- pos += scnprintf(buf + pos, bufsz - pos, "ftm_resp_ts_avail = %d\n",
- cmd->ftm_resp_ts_avail);
- pos += scnprintf(buf + pos, bufsz - pos, "asap_mode = %d\n",
- cmd->asap_mode);
- pos += scnprintf(buf + pos, bufsz - pos,
- "tsf_timer_offset_msecs = %d\n",
- le16_to_cpu(cmd->tsf_timer_offset_msecs));
- pos += scnprintf(buf + pos, bufsz - pos, "toa_offset = %d\n",
- le16_to_cpu(cmd->toa_offset));
-
- mutex_unlock(&mvm->mutex);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
- char *buf, size_t count,
- loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- u32 value;
- int ret = 0;
- char *data;
-
- mutex_lock(&mvm->mutex);
-
- data = iwl_dbgfs_is_match("request_id=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req.request_id = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("initiator=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req.initiator = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("one_sided_los_disable=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req.one_sided_los_disable = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("req_timeout=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req.req_timeout = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("report_policy=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req.report_policy = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("macaddr_random=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req.macaddr_random = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("num_of_ap=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req.num_of_ap = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("macaddr_template=", buf);
- if (data) {
- u8 mac[ETH_ALEN];
-
- if (!mac_pton(data, mac)) {
- ret = -EINVAL;
- goto out;
- }
- memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN);
- goto out;
- }
-
- data = iwl_dbgfs_is_match("macaddr_mask=", buf);
- if (data) {
- u8 mac[ETH_ALEN];
-
- if (!mac_pton(data, mac)) {
- ret = -EINVAL;
- goto out;
- }
- memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN);
- goto out;
- }
-
- data = iwl_dbgfs_is_match("ap=", buf);
- if (data) {
- struct iwl_tof_range_req_ap_entry ap = {};
- int size = sizeof(struct iwl_tof_range_req_ap_entry);
- u16 burst_period;
- u8 *mac = ap.bssid;
- unsigned int i;
-
- if (sscanf(data, "%u %hhd %hhd %hhd"
- "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"
- "%hhd %hhd %hd"
- "%hhd %hhd %d"
- "%hhx %hhd %hhd %hhd",
- &i, &ap.channel_num, &ap.bandwidth,
- &ap.ctrl_ch_position,
- mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5,
- &ap.measure_type, &ap.num_of_bursts,
- &burst_period,
- &ap.samples_per_burst, &ap.retries_per_sample,
- &ap.tsf_delta, &ap.location_req, &ap.asap_mode,
- &ap.enable_dyn_ack, &ap.rssi) != 20) {
- ret = -EINVAL;
- goto out;
- }
- if (i >= IWL_MVM_TOF_MAX_APS) {
- IWL_ERR(mvm, "Invalid AP index %d\n", i);
- ret = -EINVAL;
- goto out;
- }
-
- ap.burst_period = cpu_to_le16(burst_period);
-
- memcpy(&mvm->tof_data.range_req.ap[i], &ap, size);
- goto out;
- }
-
- data = iwl_dbgfs_is_match("send_range_request=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0 && value)
- ret = iwl_mvm_tof_range_request_cmd(mvm, vif);
- goto out;
- }
-
- ret = -EINVAL;
-out:
- mutex_unlock(&mvm->mutex);
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- char buf[512];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
- struct iwl_tof_range_req_cmd *cmd;
- int i;
-
- cmd = &mvm->tof_data.range_req;
-
- mutex_lock(&mvm->mutex);
-
- pos += scnprintf(buf + pos, bufsz - pos, "request_id= %d\n",
- cmd->request_id);
- pos += scnprintf(buf + pos, bufsz - pos, "initiator= %d\n",
- cmd->initiator);
- pos += scnprintf(buf + pos, bufsz - pos, "one_sided_los_disable = %d\n",
- cmd->one_sided_los_disable);
- pos += scnprintf(buf + pos, bufsz - pos, "req_timeout= %d\n",
- cmd->req_timeout);
- pos += scnprintf(buf + pos, bufsz - pos, "report_policy= %d\n",
- cmd->report_policy);
- pos += scnprintf(buf + pos, bufsz - pos, "macaddr_random= %d\n",
- cmd->macaddr_random);
- pos += scnprintf(buf + pos, bufsz - pos, "macaddr_template= %pM\n",
- cmd->macaddr_template);
- pos += scnprintf(buf + pos, bufsz - pos, "macaddr_mask= %pM\n",
- cmd->macaddr_mask);
- pos += scnprintf(buf + pos, bufsz - pos, "num_of_ap= %d\n",
- cmd->num_of_ap);
- for (i = 0; i < cmd->num_of_ap; i++) {
- struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i];
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "ap %.2d: channel_num=%hhd bw=%hhd"
- " control=%hhd bssid=%pM type=%hhd"
- " num_of_bursts=%hhd burst_period=%hd ftm=%hhd"
- " retries=%hhd tsf_delta=%d"
- " tsf_delta_direction=%hhd location_req=0x%hhx "
- " asap=%hhd enable=%hhd rssi=%hhd\n",
- i, ap->channel_num, ap->bandwidth,
- ap->ctrl_ch_position, ap->bssid,
- ap->measure_type, ap->num_of_bursts,
- ap->burst_period, ap->samples_per_burst,
- ap->retries_per_sample, ap->tsf_delta,
- ap->tsf_delta_direction,
- ap->location_req, ap->asap_mode,
- ap->enable_dyn_ack, ap->rssi);
- }
-
- mutex_unlock(&mvm->mutex);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif,
- char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- u32 value;
- int ret = 0;
- char *data;
-
- mutex_lock(&mvm->mutex);
-
- data = iwl_dbgfs_is_match("tsf_timer_offset_msec=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req_ext.tsf_timer_offset_msec =
- cpu_to_le16(value);
- goto out;
- }
-
- data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req_ext.min_delta_ftm = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("ftm_format_and_bw20M=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req_ext.ftm_format_and_bw20M =
- value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("ftm_format_and_bw40M=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req_ext.ftm_format_and_bw40M =
- value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("ftm_format_and_bw80M=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.range_req_ext.ftm_format_and_bw80M =
- value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("send_range_req_ext=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0 && value)
- ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif);
- goto out;
- }
-
- ret = -EINVAL;
-out:
- mutex_unlock(&mvm->mutex);
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- char buf[256];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
- struct iwl_tof_range_req_ext_cmd *cmd;
-
- cmd = &mvm->tof_data.range_req_ext;
-
- mutex_lock(&mvm->mutex);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "tsf_timer_offset_msec = %hd\n",
- cmd->tsf_timer_offset_msec);
- pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n",
- cmd->min_delta_ftm);
- pos += scnprintf(buf + pos, bufsz - pos,
- "ftm_format_and_bw20M = %hhd\n",
- cmd->ftm_format_and_bw20M);
- pos += scnprintf(buf + pos, bufsz - pos,
- "ftm_format_and_bw40M = %hhd\n",
- cmd->ftm_format_and_bw40M);
- pos += scnprintf(buf + pos, bufsz - pos,
- "ftm_format_and_bw80M = %hhd\n",
- cmd->ftm_format_and_bw80M);
-
- mutex_unlock(&mvm->mutex);
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif,
- char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- u32 value;
- int abort_id, ret = 0;
- char *data;
-
- mutex_lock(&mvm->mutex);
-
- data = iwl_dbgfs_is_match("abort_id=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0)
- mvm->tof_data.last_abort_id = value;
- goto out;
- }
-
- data = iwl_dbgfs_is_match("send_range_abort=", buf);
- if (data) {
- ret = kstrtou32(data, 10, &value);
- if (ret == 0 && value) {
- abort_id = mvm->tof_data.last_abort_id;
- ret = iwl_mvm_tof_range_abort_cmd(mvm, abort_id);
- goto out;
- }
- }
-
-out:
- mutex_unlock(&mvm->mutex);
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_range_abort_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- char buf[32];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
- int last_abort_id;
-
- mutex_lock(&mvm->mutex);
- last_abort_id = mvm->tof_data.last_abort_id;
- mutex_unlock(&mvm->mutex);
-
- pos += scnprintf(buf + pos, bufsz - pos, "last_abort_id = %d\n",
- last_abort_id);
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- char *buf;
- int pos = 0;
- const size_t bufsz = sizeof(struct iwl_tof_range_rsp_ntfy) + 256;
- struct iwl_tof_range_rsp_ntfy *cmd;
- int i, ret;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- mutex_lock(&mvm->mutex);
- cmd = &mvm->tof_data.range_resp;
-
- pos += scnprintf(buf + pos, bufsz - pos, "request_id = %d\n",
- cmd->request_id);
- pos += scnprintf(buf + pos, bufsz - pos, "status = %d\n",
- cmd->request_status);
- pos += scnprintf(buf + pos, bufsz - pos, "last_in_batch = %d\n",
- cmd->last_in_batch);
- pos += scnprintf(buf + pos, bufsz - pos, "num_of_aps = %d\n",
- cmd->num_of_aps);
- for (i = 0; i < cmd->num_of_aps; i++) {
- struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i];
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "ap %.2d: bssid=%pM status=%hhd bw=%hhd"
- " rtt=%d rtt_var=%d rtt_spread=%d"
- " rssi=%hhd rssi_spread=%hhd"
- " range=%d range_var=%d"
- " time_stamp=%d\n",
- i, ap->bssid, ap->measure_status,
- ap->measure_bw,
- ap->rtt, ap->rtt_variance, ap->rtt_spread,
- ap->rssi, ap->rssi_spread, ap->range,
- ap->range_variance, ap->timestamp);
- }
- mutex_unlock(&mvm->mutex);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- u8 value;
- int ret;
-
- ret = kstrtou8(buf, 0, &value);
- if (ret)
- return ret;
- if (value > 1)
- return -EINVAL;
-
- mutex_lock(&mvm->mutex);
- iwl_mvm_update_low_latency(mvm, vif, value);
- mutex_unlock(&mvm->mutex);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- char buf[2];
-
- buf[0] = mvmvif->low_latency ? '1' : '0';
- buf[1] = '\n';
- return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
-}
-
-static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- char buf[20];
- int len;
-
- len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid);
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif,
- char *buf, size_t count,
- loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- bool ret;
-
- mutex_lock(&mvm->mutex);
- ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid);
- mutex_unlock(&mvm->mutex);
-
- return ret ? count : -EINVAL;
-}
-
-static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- struct ieee80211_chanctx_conf *chanctx_conf;
- struct iwl_mvm_phy_ctxt *phy_ctxt;
- u16 value;
- int ret;
-
- ret = kstrtou16(buf, 0, &value);
- if (ret)
- return ret;
-
- mutex_lock(&mvm->mutex);
- rcu_read_lock();
-
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
- /* make sure the channel context is assigned */
- if (!chanctx_conf) {
- rcu_read_unlock();
- mutex_unlock(&mvm->mutex);
- return -EINVAL;
- }
-
- phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv];
- rcu_read_unlock();
-
- mvm->dbgfs_rx_phyinfo = value;
-
- ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def,
- chanctx_conf->rx_chains_static,
- chanctx_conf->rx_chains_dynamic);
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_vif *vif = file->private_data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- char buf[8];
-
- snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
-}
-
-#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
- _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
-#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
- _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
-#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \
- if (!debugfs_create_file(#name, mode, parent, vif, \
- &iwl_dbgfs_##name##_ops)) \
- goto err; \
- } while (0)
-
-MVM_DEBUGFS_READ_FILE_OPS(mac_params);
-MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_enable, 32);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_request, 512);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32);
-MVM_DEBUGFS_READ_FILE_OPS(tof_range_response);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32);
-
-void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct dentry *dbgfs_dir = vif->debugfs_dir;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- char buf[100];
-
- /*
- * Check if debugfs directory already exist before creating it.
- * This may happen when, for example, resetting hw or suspend-resume
- */
- if (!dbgfs_dir || mvmvif->dbgfs_dir)
- return;
-
- mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
-
- if (!mvmvif->dbgfs_dir) {
- IWL_ERR(mvm, "Failed to create debugfs directory under %s\n",
- dbgfs_dir->d_name.name);
- return;
- }
-
- if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
- ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
- (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
- mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)))
- MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
- S_IRUSR);
-
- MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
-
- if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
- mvmvif == mvm->bf_allowed_vif)
- MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
-
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) &&
- !vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) {
- if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP)
- MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params,
- mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
-
- MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, mvmvif->dbgfs_dir,
- S_IRUSR);
- }
-
- /*
- * Create symlink for convenience pointing to interface specific
- * debugfs entries for the driver. For example, under
- * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/
- * find
- * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/
- */
- snprintf(buf, 100, "../../../%s/%s/%s/%s",
- dbgfs_dir->d_parent->d_parent->d_name.name,
- dbgfs_dir->d_parent->d_name.name,
- dbgfs_dir->d_name.name,
- mvmvif->dbgfs_dir->d_name.name);
-
- mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name,
- mvm->debugfs_dir, buf);
- if (!mvmvif->dbgfs_slink)
- IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n",
- dbgfs_dir->d_name.name);
- return;
-err:
- IWL_ERR(mvm, "Can't create debugfs entity\n");
-}
-
-void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- debugfs_remove(mvmvif->dbgfs_slink);
- mvmvif->dbgfs_slink = NULL;
-
- debugfs_remove_recursive(mvmvif->dbgfs_dir);
- mvmvif->dbgfs_dir = NULL;
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
deleted file mode 100644
index 05928fb4021d..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ /dev/null
@@ -1,1516 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/vmalloc.h>
-
-#include "mvm.h"
-#include "sta.h"
-#include "iwl-io.h"
-#include "debugfs.h"
-#include "iwl-fw-error-dump.h"
-
-static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- int ret;
- u32 scd_q_msk;
-
- if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
- return -EIO;
-
- if (sscanf(buf, "%x", &scd_q_msk) != 1)
- return -EINVAL;
-
- IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, 0) ? : count;
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_sta *mvmsta;
- int sta_id, drain, ret;
-
- if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
- return -EIO;
-
- if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
- return -EINVAL;
- if (sta_id < 0 || sta_id >= IWL_MVM_STATION_COUNT)
- return -EINVAL;
- if (drain < 0 || drain > 1)
- return -EINVAL;
-
- mutex_lock(&mvm->mutex);
-
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
-
- if (!mvmsta)
- ret = -ENOENT;
- else
- ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count;
-
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- const struct fw_img *img;
- unsigned int ofs, len;
- size_t ret;
- u8 *ptr;
-
- if (!mvm->ucode_loaded)
- return -EINVAL;
-
- /* default is to dump the entire data segment */
- img = &mvm->fw->img[mvm->cur_ucode];
- ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
- len = img->sec[IWL_UCODE_SECTION_DATA].len;
-
- if (mvm->dbgfs_sram_len) {
- ofs = mvm->dbgfs_sram_offset;
- len = mvm->dbgfs_sram_len;
- }
-
- ptr = kzalloc(len, GFP_KERNEL);
- if (!ptr)
- return -ENOMEM;
-
- iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len);
-
- kfree(ptr);
-
- return ret;
-}
-
-static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- const struct fw_img *img;
- u32 offset, len;
- u32 img_offset, img_len;
-
- if (!mvm->ucode_loaded)
- return -EINVAL;
-
- img = &mvm->fw->img[mvm->cur_ucode];
- img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset;
- img_len = img->sec[IWL_UCODE_SECTION_DATA].len;
-
- if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
- if ((offset & 0x3) || (len & 0x3))
- return -EINVAL;
-
- if (offset + len > img_offset + img_len)
- return -EINVAL;
-
- mvm->dbgfs_sram_offset = offset;
- mvm->dbgfs_sram_len = len;
- } else {
- mvm->dbgfs_sram_offset = 0;
- mvm->dbgfs_sram_len = 0;
- }
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- char buf[16];
- int pos;
-
- if (!mvm->temperature_test)
- pos = scnprintf(buf , sizeof(buf), "disabled\n");
- else
- pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-/*
- * Set NIC Temperature
- * Cause the driver to ignore the actual NIC temperature reported by the FW
- * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -
- * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX
- * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE
- */
-static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm,
- char *buf, size_t count,
- loff_t *ppos)
-{
- int temperature;
-
- if (!mvm->ucode_loaded && !mvm->temperature_test)
- return -EIO;
-
- if (kstrtoint(buf, 10, &temperature))
- return -EINVAL;
- /* not a legal temperature */
- if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX &&
- temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) ||
- temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN)
- return -EINVAL;
-
- mutex_lock(&mvm->mutex);
- if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) {
- if (!mvm->temperature_test)
- goto out;
-
- mvm->temperature_test = false;
- /* Since we can't read the temp while awake, just set
- * it to zero until we get the next RX stats from the
- * firmware.
- */
- mvm->temperature = 0;
- } else {
- mvm->temperature_test = true;
- mvm->temperature = temperature;
- }
- IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n",
- mvm->temperature_test ? "En" : "Dis" ,
- mvm->temperature);
- /* handle the temperature change */
- iwl_mvm_tt_handler(mvm);
-
-out:
- mutex_unlock(&mvm->mutex);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_nic_temp_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- char buf[16];
- int pos, temp;
-
- if (!mvm->ucode_loaded)
- return -EIO;
-
- mutex_lock(&mvm->mutex);
- temp = iwl_mvm_get_temp(mvm);
- mutex_unlock(&mvm->mutex);
-
- if (temp < 0)
- return temp;
-
- pos = scnprintf(buf , sizeof(buf), "%d\n", temp);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- struct ieee80211_sta *sta;
- char buf[400];
- int i, pos = 0, bufsz = sizeof(buf);
-
- mutex_lock(&mvm->mutex);
-
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
- pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
- lockdep_is_held(&mvm->mutex));
- if (!sta)
- pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");
- else if (IS_ERR(sta))
- pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",
- PTR_ERR(sta));
- else
- pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
- sta->addr);
- }
-
- mutex_unlock(&mvm->mutex);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- char buf[64];
- int bufsz = sizeof(buf);
- int pos = 0;
-
- pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n",
- mvm->disable_power_off);
- pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n",
- mvm->disable_power_off_d3);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- int ret, val;
-
- if (!mvm->ucode_loaded)
- return -EIO;
-
- if (!strncmp("disable_power_off_d0=", buf, 21)) {
- if (sscanf(buf + 21, "%d", &val) != 1)
- return -EINVAL;
- mvm->disable_power_off = val;
- } else if (!strncmp("disable_power_off_d3=", buf, 21)) {
- if (sscanf(buf + 21, "%d", &val) != 1)
- return -EINVAL;
- mvm->disable_power_off_d3 = val;
- } else {
- return -EINVAL;
- }
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_power_update_device(mvm);
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-#define BT_MBOX_MSG(_notif, _num, _field) \
- ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
- >> BT_MBOX##_num##_##_field##_POS)
-
-
-#define BT_MBOX_PRINT(_num, _field, _end) \
- pos += scnprintf(buf + pos, bufsz - pos, \
- "\t%s: %d%s", \
- #_field, \
- BT_MBOX_MSG(notif, _num, _field), \
- true ? "\n" : ", ");
-
-static
-int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
- int pos, int bufsz)
-{
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
-
- BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
- BT_MBOX_PRINT(0, LE_PROF1, false);
- BT_MBOX_PRINT(0, LE_PROF2, false);
- BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
- BT_MBOX_PRINT(0, CHL_SEQ_N, false);
- BT_MBOX_PRINT(0, INBAND_S, false);
- BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
- BT_MBOX_PRINT(0, LE_SCAN, false);
- BT_MBOX_PRINT(0, LE_ADV, false);
- BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
- BT_MBOX_PRINT(0, OPEN_CON_1, true);
-
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
-
- BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
- BT_MBOX_PRINT(1, IP_SR, false);
- BT_MBOX_PRINT(1, LE_MSTR, false);
- BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
- BT_MBOX_PRINT(1, MSG_TYPE, false);
- BT_MBOX_PRINT(1, SSN, true);
-
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
-
- BT_MBOX_PRINT(2, SNIFF_ACT, false);
- BT_MBOX_PRINT(2, PAG, false);
- BT_MBOX_PRINT(2, INQUIRY, false);
- BT_MBOX_PRINT(2, CONN, false);
- BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
- BT_MBOX_PRINT(2, DISC, false);
- BT_MBOX_PRINT(2, SCO_TX_ACT, false);
- BT_MBOX_PRINT(2, SCO_RX_ACT, false);
- BT_MBOX_PRINT(2, ESCO_RE_TX, false);
- BT_MBOX_PRINT(2, SCO_DURATION, true);
-
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
-
- BT_MBOX_PRINT(3, SCO_STATE, false);
- BT_MBOX_PRINT(3, SNIFF_STATE, false);
- BT_MBOX_PRINT(3, A2DP_STATE, false);
- BT_MBOX_PRINT(3, ACL_STATE, false);
- BT_MBOX_PRINT(3, MSTR_STATE, false);
- BT_MBOX_PRINT(3, OBX_STATE, false);
- BT_MBOX_PRINT(3, OPEN_CON_2, false);
- BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
- BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
- BT_MBOX_PRINT(3, INBAND_P, false);
- BT_MBOX_PRINT(3, MSG_TYPE_2, false);
- BT_MBOX_PRINT(3, SSN_2, false);
- BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
-
- return pos;
-}
-
-static
-int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif,
- char *buf, int pos, int bufsz)
-{
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
-
- BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
- BT_MBOX_PRINT(0, LE_PROF1, false);
- BT_MBOX_PRINT(0, LE_PROF2, false);
- BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
- BT_MBOX_PRINT(0, CHL_SEQ_N, false);
- BT_MBOX_PRINT(0, INBAND_S, false);
- BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
- BT_MBOX_PRINT(0, LE_SCAN, false);
- BT_MBOX_PRINT(0, LE_ADV, false);
- BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
- BT_MBOX_PRINT(0, OPEN_CON_1, true);
-
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
-
- BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
- BT_MBOX_PRINT(1, IP_SR, false);
- BT_MBOX_PRINT(1, LE_MSTR, false);
- BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
- BT_MBOX_PRINT(1, MSG_TYPE, false);
- BT_MBOX_PRINT(1, SSN, true);
-
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
-
- BT_MBOX_PRINT(2, SNIFF_ACT, false);
- BT_MBOX_PRINT(2, PAG, false);
- BT_MBOX_PRINT(2, INQUIRY, false);
- BT_MBOX_PRINT(2, CONN, false);
- BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
- BT_MBOX_PRINT(2, DISC, false);
- BT_MBOX_PRINT(2, SCO_TX_ACT, false);
- BT_MBOX_PRINT(2, SCO_RX_ACT, false);
- BT_MBOX_PRINT(2, ESCO_RE_TX, false);
- BT_MBOX_PRINT(2, SCO_DURATION, true);
-
- pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
-
- BT_MBOX_PRINT(3, SCO_STATE, false);
- BT_MBOX_PRINT(3, SNIFF_STATE, false);
- BT_MBOX_PRINT(3, A2DP_STATE, false);
- BT_MBOX_PRINT(3, ACL_STATE, false);
- BT_MBOX_PRINT(3, MSTR_STATE, false);
- BT_MBOX_PRINT(3, OBX_STATE, false);
- BT_MBOX_PRINT(3, OPEN_CON_2, false);
- BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
- BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
- BT_MBOX_PRINT(3, INBAND_P, false);
- BT_MBOX_PRINT(3, MSG_TYPE_2, false);
- BT_MBOX_PRINT(3, SSN_2, false);
- BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
-
- return pos;
-}
-
-static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- char *buf;
- int ret, pos = 0, bufsz = sizeof(char) * 1024;
-
- buf = kmalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- mutex_lock(&mvm->mutex);
-
- if (!fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
- struct iwl_bt_coex_profile_notif_old *notif =
- &mvm->last_bt_notif_old;
-
- pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz);
-
- pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
- notif->bt_ci_compliance);
- pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
- le32_to_cpu(notif->primary_ch_lut));
- pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
- le32_to_cpu(notif->secondary_ch_lut));
- pos += scnprintf(buf+pos,
- bufsz-pos, "bt_activity_grading = %d\n",
- le32_to_cpu(notif->bt_activity_grading));
- pos += scnprintf(buf+pos, bufsz-pos,
- "antenna isolation = %d CORUN LUT index = %d\n",
- mvm->last_ant_isol, mvm->last_corun_lut);
- } else {
- struct iwl_bt_coex_profile_notif *notif =
- &mvm->last_bt_notif;
-
- pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
-
- pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
- notif->bt_ci_compliance);
- pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
- le32_to_cpu(notif->primary_ch_lut));
- pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
- le32_to_cpu(notif->secondary_ch_lut));
- pos += scnprintf(buf+pos,
- bufsz-pos, "bt_activity_grading = %d\n",
- le32_to_cpu(notif->bt_activity_grading));
- pos += scnprintf(buf+pos, bufsz-pos,
- "antenna isolation = %d CORUN LUT index = %d\n",
- mvm->last_ant_isol, mvm->last_corun_lut);
- }
-
- mutex_unlock(&mvm->mutex);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
-
- return ret;
-}
-#undef BT_MBOX_PRINT
-
-static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- char buf[256];
- int bufsz = sizeof(buf);
- int pos = 0;
-
- mutex_lock(&mvm->mutex);
-
- if (!fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
- struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old;
-
- pos += scnprintf(buf+pos, bufsz-pos,
- "Channel inhibition CMD\n");
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tPrimary Channel Bitmap 0x%016llx\n",
- le64_to_cpu(cmd->bt_primary_ci));
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tSecondary Channel Bitmap 0x%016llx\n",
- le64_to_cpu(cmd->bt_secondary_ci));
-
- pos += scnprintf(buf+pos, bufsz-pos,
- "BT Configuration CMD - 0=default, 1=never, 2=always\n");
- pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n",
- mvm->bt_ack_kill_msk[0]);
- pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n",
- mvm->bt_cts_kill_msk[0]);
-
- } else {
- struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
-
- pos += scnprintf(buf+pos, bufsz-pos,
- "Channel inhibition CMD\n");
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tPrimary Channel Bitmap 0x%016llx\n",
- le64_to_cpu(cmd->bt_primary_ci));
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tSecondary Channel Bitmap 0x%016llx\n",
- le64_to_cpu(cmd->bt_secondary_ci));
- }
-
- mutex_unlock(&mvm->mutex);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t
-iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- u32 bt_tx_prio;
-
- if (sscanf(buf, "%u", &bt_tx_prio) != 1)
- return -EINVAL;
- if (bt_tx_prio > 4)
- return -EINVAL;
-
- mvm->bt_tx_prio = bt_tx_prio;
-
- return count;
-}
-
-static ssize_t
-iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- static const char * const modes_str[BT_FORCE_ANT_MAX] = {
- [BT_FORCE_ANT_DIS] = "dis",
- [BT_FORCE_ANT_AUTO] = "auto",
- [BT_FORCE_ANT_BT] = "bt",
- [BT_FORCE_ANT_WIFI] = "wifi",
- };
- int ret, bt_force_ant_mode;
-
- for (bt_force_ant_mode = 0;
- bt_force_ant_mode < ARRAY_SIZE(modes_str);
- bt_force_ant_mode++) {
- if (!strcmp(buf, modes_str[bt_force_ant_mode]))
- break;
- }
-
- if (bt_force_ant_mode >= ARRAY_SIZE(modes_str))
- return -EINVAL;
-
- ret = 0;
- mutex_lock(&mvm->mutex);
- if (mvm->bt_force_ant_mode == bt_force_ant_mode)
- goto out;
-
- mvm->bt_force_ant_mode = bt_force_ant_mode;
- IWL_DEBUG_COEX(mvm, "Force mode: %s\n",
- modes_str[mvm->bt_force_ant_mode]);
- ret = iwl_send_bt_init_conf(mvm);
-
-out:
- mutex_unlock(&mvm->mutex);
- return ret ?: count;
-}
-
-#define PRINT_STATS_LE32(_struct, _memb) \
- pos += scnprintf(buf + pos, bufsz - pos, \
- fmt_table, #_memb, \
- le32_to_cpu(_struct->_memb))
-
-static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
- char __user *user_buf, size_t count,
- loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- static const char *fmt_table = "\t%-30s %10u\n";
- static const char *fmt_header = "%-32s\n";
- int pos = 0;
- char *buf;
- int ret;
- /* 43 is the size of each data line, 33 is the size of each header */
- size_t bufsz =
- ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) +
- (4 * 33) + 1;
-
- struct mvm_statistics_rx_phy *ofdm;
- struct mvm_statistics_rx_phy *cck;
- struct mvm_statistics_rx_non_phy *general;
- struct mvm_statistics_rx_ht_phy *ht;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- mutex_lock(&mvm->mutex);
-
- ofdm = &mvm->rx_stats.ofdm;
- cck = &mvm->rx_stats.cck;
- general = &mvm->rx_stats.general;
- ht = &mvm->rx_stats.ofdm_ht;
-
- pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
- "Statistics_Rx - OFDM");
- PRINT_STATS_LE32(ofdm, ina_cnt);
- PRINT_STATS_LE32(ofdm, fina_cnt);
- PRINT_STATS_LE32(ofdm, plcp_err);
- PRINT_STATS_LE32(ofdm, crc32_err);
- PRINT_STATS_LE32(ofdm, overrun_err);
- PRINT_STATS_LE32(ofdm, early_overrun_err);
- PRINT_STATS_LE32(ofdm, crc32_good);
- PRINT_STATS_LE32(ofdm, false_alarm_cnt);
- PRINT_STATS_LE32(ofdm, fina_sync_err_cnt);
- PRINT_STATS_LE32(ofdm, sfd_timeout);
- PRINT_STATS_LE32(ofdm, fina_timeout);
- PRINT_STATS_LE32(ofdm, unresponded_rts);
- PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun);
- PRINT_STATS_LE32(ofdm, sent_ack_cnt);
- PRINT_STATS_LE32(ofdm, sent_cts_cnt);
- PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt);
- PRINT_STATS_LE32(ofdm, dsp_self_kill);
- PRINT_STATS_LE32(ofdm, mh_format_err);
- PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum);
- PRINT_STATS_LE32(ofdm, reserved);
-
- pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
- "Statistics_Rx - CCK");
- PRINT_STATS_LE32(cck, ina_cnt);
- PRINT_STATS_LE32(cck, fina_cnt);
- PRINT_STATS_LE32(cck, plcp_err);
- PRINT_STATS_LE32(cck, crc32_err);
- PRINT_STATS_LE32(cck, overrun_err);
- PRINT_STATS_LE32(cck, early_overrun_err);
- PRINT_STATS_LE32(cck, crc32_good);
- PRINT_STATS_LE32(cck, false_alarm_cnt);
- PRINT_STATS_LE32(cck, fina_sync_err_cnt);
- PRINT_STATS_LE32(cck, sfd_timeout);
- PRINT_STATS_LE32(cck, fina_timeout);
- PRINT_STATS_LE32(cck, unresponded_rts);
- PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun);
- PRINT_STATS_LE32(cck, sent_ack_cnt);
- PRINT_STATS_LE32(cck, sent_cts_cnt);
- PRINT_STATS_LE32(cck, sent_ba_rsp_cnt);
- PRINT_STATS_LE32(cck, dsp_self_kill);
- PRINT_STATS_LE32(cck, mh_format_err);
- PRINT_STATS_LE32(cck, re_acq_main_rssi_sum);
- PRINT_STATS_LE32(cck, reserved);
-
- pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
- "Statistics_Rx - GENERAL");
- PRINT_STATS_LE32(general, bogus_cts);
- PRINT_STATS_LE32(general, bogus_ack);
- PRINT_STATS_LE32(general, non_bssid_frames);
- PRINT_STATS_LE32(general, filtered_frames);
- PRINT_STATS_LE32(general, non_channel_beacons);
- PRINT_STATS_LE32(general, channel_beacons);
- PRINT_STATS_LE32(general, num_missed_bcon);
- PRINT_STATS_LE32(general, adc_rx_saturation_time);
- PRINT_STATS_LE32(general, ina_detection_search_time);
- PRINT_STATS_LE32(general, beacon_silence_rssi_a);
- PRINT_STATS_LE32(general, beacon_silence_rssi_b);
- PRINT_STATS_LE32(general, beacon_silence_rssi_c);
- PRINT_STATS_LE32(general, interference_data_flag);
- PRINT_STATS_LE32(general, channel_load);
- PRINT_STATS_LE32(general, dsp_false_alarms);
- PRINT_STATS_LE32(general, beacon_rssi_a);
- PRINT_STATS_LE32(general, beacon_rssi_b);
- PRINT_STATS_LE32(general, beacon_rssi_c);
- PRINT_STATS_LE32(general, beacon_energy_a);
- PRINT_STATS_LE32(general, beacon_energy_b);
- PRINT_STATS_LE32(general, beacon_energy_c);
- PRINT_STATS_LE32(general, num_bt_kills);
- PRINT_STATS_LE32(general, mac_id);
- PRINT_STATS_LE32(general, directed_data_mpdu);
-
- pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
- "Statistics_Rx - HT");
- PRINT_STATS_LE32(ht, plcp_err);
- PRINT_STATS_LE32(ht, overrun_err);
- PRINT_STATS_LE32(ht, early_overrun_err);
- PRINT_STATS_LE32(ht, crc32_good);
- PRINT_STATS_LE32(ht, crc32_err);
- PRINT_STATS_LE32(ht, mh_format_err);
- PRINT_STATS_LE32(ht, agg_crc32_good);
- PRINT_STATS_LE32(ht, agg_mpdu_cnt);
- PRINT_STATS_LE32(ht, agg_cnt);
- PRINT_STATS_LE32(ht, unsupport_mcs);
-
- mutex_unlock(&mvm->mutex);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
-
- return ret;
-}
-#undef PRINT_STAT_LE32
-
-static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm,
- char __user *user_buf, size_t count,
- loff_t *ppos,
- struct iwl_mvm_frame_stats *stats)
-{
- char *buff, *pos, *endpos;
- int idx, i;
- int ret;
- static const size_t bufsz = 1024;
-
- buff = kmalloc(bufsz, GFP_KERNEL);
- if (!buff)
- return -ENOMEM;
-
- spin_lock_bh(&mvm->drv_stats_lock);
-
- pos = buff;
- endpos = pos + bufsz;
-
- pos += scnprintf(pos, endpos - pos,
- "Legacy/HT/VHT\t:\t%d/%d/%d\n",
- stats->legacy_frames,
- stats->ht_frames,
- stats->vht_frames);
- pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n",
- stats->bw_20_frames,
- stats->bw_40_frames,
- stats->bw_80_frames);
- pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n",
- stats->ngi_frames,
- stats->sgi_frames);
- pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n",
- stats->siso_frames,
- stats->mimo2_frames);
- pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n",
- stats->fail_frames,
- stats->success_frames);
- pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n",
- stats->agg_frames);
- pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n",
- stats->ampdu_count);
- pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n",
- stats->ampdu_count > 0 ?
- (stats->agg_frames / stats->ampdu_count) : 0);
-
- pos += scnprintf(pos, endpos - pos, "Last Rates\n");
-
- idx = stats->last_frame_idx - 1;
- for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) {
- idx = (idx + 1) % ARRAY_SIZE(stats->last_rates);
- if (stats->last_rates[idx] == 0)
- continue;
- pos += scnprintf(pos, endpos - pos, "Rate[%d]: ",
- (int)(ARRAY_SIZE(stats->last_rates) - i));
- pos += rs_pretty_print_rate(pos, stats->last_rates[idx]);
- }
- spin_unlock_bh(&mvm->drv_stats_lock);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
- kfree(buff);
-
- return ret;
-}
-
-static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file,
- char __user *user_buf, size_t count,
- loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
-
- return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos,
- &mvm->drv_rx_stats);
-}
-
-static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- int ret;
-
- mutex_lock(&mvm->mutex);
-
- /* allow one more restart that we're provoking here */
- if (mvm->restart_fw >= 0)
- mvm->restart_fw++;
-
- /* take the return value to make compiler happy - it will fail anyway */
- ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL);
-
- mutex_unlock(&mvm->mutex);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_NMI);
- if (ret)
- return ret;
-
- iwl_force_nmi(mvm->trans);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_NMI);
-
- return count;
-}
-
-static ssize_t
-iwl_dbgfs_scan_ant_rxchain_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- int pos = 0;
- char buf[32];
- const size_t bufsz = sizeof(buf);
-
- /* print which antennas were set for the scan command by the user */
- pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: ");
- if (mvm->scan_rx_ant & ANT_A)
- pos += scnprintf(buf + pos, bufsz - pos, "A");
- if (mvm->scan_rx_ant & ANT_B)
- pos += scnprintf(buf + pos, bufsz - pos, "B");
- if (mvm->scan_rx_ant & ANT_C)
- pos += scnprintf(buf + pos, bufsz - pos, "C");
- pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t
-iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- u8 scan_rx_ant;
-
- if (sscanf(buf, "%hhx", &scan_rx_ant) != 1)
- return -EINVAL;
- if (scan_rx_ant > ANT_ABC)
- return -EINVAL;
- if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm)))
- return -EINVAL;
-
- if (mvm->scan_rx_ant != scan_rx_ant) {
- mvm->scan_rx_ant = scan_rx_ant;
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_UMAC_SCAN))
- iwl_mvm_config_scan(mvm);
- }
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- int conf;
- char buf[8];
- const size_t bufsz = sizeof(buf);
- int pos = 0;
-
- mutex_lock(&mvm->mutex);
- conf = mvm->fw_dbg_conf;
- mutex_unlock(&mvm->mutex);
-
- pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
- char *buf, size_t count,
- loff_t *ppos)
-{
- unsigned int conf_id;
- int ret;
-
- ret = kstrtouint(buf, 0, &conf_id);
- if (ret)
- return ret;
-
- if (WARN_ON(conf_id >= FW_DBG_CONF_MAX))
- return -EINVAL;
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id);
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
- char *buf, size_t count,
- loff_t *ppos)
-{
- int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE);
-
- if (ret)
- return ret;
-
- iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, NULL);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
-
- return count;
-}
-
-#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
-#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
-static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- struct iwl_bcast_filter_cmd cmd;
- const struct iwl_fw_bcast_filter *filter;
- char *buf;
- int bufsz = 1024;
- int i, j, pos = 0;
- ssize_t ret;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- mutex_lock(&mvm->mutex);
- if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
- ADD_TEXT("None\n");
- mutex_unlock(&mvm->mutex);
- goto out;
- }
- mutex_unlock(&mvm->mutex);
-
- for (i = 0; cmd.filters[i].attrs[0].mask; i++) {
- filter = &cmd.filters[i];
-
- ADD_TEXT("Filter [%d]:\n", i);
- ADD_TEXT("\tDiscard=%d\n", filter->discard);
- ADD_TEXT("\tFrame Type: %s\n",
- filter->frame_type ? "IPv4" : "Generic");
-
- for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) {
- const struct iwl_fw_bcast_filter_attr *attr;
-
- attr = &filter->attrs[j];
- if (!attr->mask)
- break;
-
- ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n",
- j, attr->offset,
- attr->offset_type ? "IP End" :
- "Payload Start",
- be32_to_cpu(attr->mask),
- be32_to_cpu(attr->val),
- le16_to_cpu(attr->reserved1));
- }
- }
-out:
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- int pos, next_pos;
- struct iwl_fw_bcast_filter filter = {};
- struct iwl_bcast_filter_cmd cmd;
- u32 filter_id, attr_id, mask, value;
- int err = 0;
-
- if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard,
- &filter.frame_type, &pos) != 3)
- return -EINVAL;
-
- if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) ||
- filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4)
- return -EINVAL;
-
- for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs);
- attr_id++) {
- struct iwl_fw_bcast_filter_attr *attr =
- &filter.attrs[attr_id];
-
- if (pos >= count)
- break;
-
- if (sscanf(&buf[pos], "%hhi %hhi %i %i %n",
- &attr->offset, &attr->offset_type,
- &mask, &value, &next_pos) != 4)
- return -EINVAL;
-
- attr->mask = cpu_to_be32(mask);
- attr->val = cpu_to_be32(value);
- if (mask)
- filter.num_attrs++;
-
- pos += next_pos;
- }
-
- mutex_lock(&mvm->mutex);
- memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id],
- &filter, sizeof(filter));
-
- /* send updated bcast filtering configuration */
- if (mvm->dbgfs_bcast_filtering.override &&
- iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
- err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
- sizeof(cmd), &cmd);
- mutex_unlock(&mvm->mutex);
-
- return err ?: count;
-}
-
-static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- struct iwl_bcast_filter_cmd cmd;
- char *buf;
- int bufsz = 1024;
- int i, pos = 0;
- ssize_t ret;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- mutex_lock(&mvm->mutex);
- if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
- ADD_TEXT("None\n");
- mutex_unlock(&mvm->mutex);
- goto out;
- }
- mutex_unlock(&mvm->mutex);
-
- for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) {
- const struct iwl_fw_bcast_mac *mac = &cmd.macs[i];
-
- ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n",
- i, mac->default_discard, mac->attached_filters);
- }
-out:
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
- char *buf, size_t count,
- loff_t *ppos)
-{
- struct iwl_bcast_filter_cmd cmd;
- struct iwl_fw_bcast_mac mac = {};
- u32 mac_id, attached_filters;
- int err = 0;
-
- if (!mvm->bcast_filters)
- return -ENOENT;
-
- if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard,
- &attached_filters) != 3)
- return -EINVAL;
-
- if (mac_id >= ARRAY_SIZE(cmd.macs) ||
- mac.default_discard > 1 ||
- attached_filters >= BIT(ARRAY_SIZE(cmd.filters)))
- return -EINVAL;
-
- mac.attached_filters = cpu_to_le16(attached_filters);
-
- mutex_lock(&mvm->mutex);
- memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id],
- &mac, sizeof(mac));
-
- /* send updated bcast filtering configuration */
- if (mvm->dbgfs_bcast_filtering.override &&
- iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
- err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
- sizeof(cmd), &cmd);
- mutex_unlock(&mvm->mutex);
-
- return err ?: count;
-}
-#endif
-
-#ifdef CONFIG_PM_SLEEP
-static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- int store;
-
- if (sscanf(buf, "%d", &store) != 1)
- return -EINVAL;
-
- mvm->store_d3_resume_sram = store;
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- const struct fw_img *img;
- int ofs, len, pos = 0;
- size_t bufsz, ret;
- char *buf;
- u8 *ptr = mvm->d3_resume_sram;
-
- img = &mvm->fw->img[IWL_UCODE_WOWLAN];
- len = img->sec[IWL_UCODE_SECTION_DATA].len;
-
- bufsz = len * 4 + 256;
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n",
- mvm->store_d3_resume_sram ? "en" : "dis");
-
- if (ptr) {
- for (ofs = 0; ofs < len; ofs += 16) {
- pos += scnprintf(buf + pos, bufsz - pos,
- "0x%.4x %16ph\n", ofs, ptr + ofs);
- }
- } else {
- pos += scnprintf(buf + pos, bufsz - pos,
- "(no data captured)\n");
- }
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-
- kfree(buf);
-
- return ret;
-}
-#endif
-
-#define PRINT_MVM_REF(ref) do { \
- if (mvm->refs[ref]) \
- pos += scnprintf(buf + pos, bufsz - pos, \
- "\t(0x%lx): %d %s\n", \
- BIT(ref), mvm->refs[ref], #ref); \
-} while (0)
-
-static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- int i, pos = 0;
- char buf[256];
- const size_t bufsz = sizeof(buf);
- u32 refs = 0;
-
- for (i = 0; i < IWL_MVM_REF_COUNT; i++)
- if (mvm->refs[i])
- refs |= BIT(i);
-
- pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%x\n",
- refs);
-
- PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
- PRINT_MVM_REF(IWL_MVM_REF_SCAN);
- PRINT_MVM_REF(IWL_MVM_REF_ROC);
- PRINT_MVM_REF(IWL_MVM_REF_ROC_AUX);
- PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT);
- PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS);
- PRINT_MVM_REF(IWL_MVM_REF_USER);
- PRINT_MVM_REF(IWL_MVM_REF_TX);
- PRINT_MVM_REF(IWL_MVM_REF_TX_AGG);
- PRINT_MVM_REF(IWL_MVM_REF_ADD_IF);
- PRINT_MVM_REF(IWL_MVM_REF_START_AP);
- PRINT_MVM_REF(IWL_MVM_REF_BSS_CHANGED);
- PRINT_MVM_REF(IWL_MVM_REF_PREPARE_TX);
- PRINT_MVM_REF(IWL_MVM_REF_PROTECT_TDLS);
- PRINT_MVM_REF(IWL_MVM_REF_CHECK_CTKILL);
- PRINT_MVM_REF(IWL_MVM_REF_PRPH_READ);
- PRINT_MVM_REF(IWL_MVM_REF_PRPH_WRITE);
- PRINT_MVM_REF(IWL_MVM_REF_NMI);
- PRINT_MVM_REF(IWL_MVM_REF_TM_CMD);
- PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
- PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
- PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- unsigned long value;
- int ret;
- bool taken;
-
- ret = kstrtoul(buf, 10, &value);
- if (ret < 0)
- return ret;
-
- mutex_lock(&mvm->mutex);
-
- taken = mvm->refs[IWL_MVM_REF_USER];
- if (value == 1 && !taken)
- iwl_mvm_ref(mvm, IWL_MVM_REF_USER);
- else if (value == 0 && taken)
- iwl_mvm_unref(mvm, IWL_MVM_REF_USER);
- else
- ret = -EINVAL;
-
- mutex_unlock(&mvm->mutex);
-
- if (ret < 0)
- return ret;
- return count;
-}
-
-#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
- _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
-#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
- _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
-#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \
- if (!debugfs_create_file(alias, mode, parent, mvm, \
- &iwl_dbgfs_##name##_ops)) \
- goto err; \
- } while (0)
-#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
- MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
-
-static ssize_t
-iwl_dbgfs_prph_reg_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = file->private_data;
- int pos = 0;
- char buf[32];
- const size_t bufsz = sizeof(buf);
- int ret;
-
- if (!mvm->dbgfs_prph_reg_addr)
- return -EINVAL;
-
- ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_READ);
- if (ret)
- return ret;
-
- pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n",
- mvm->dbgfs_prph_reg_addr,
- iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr));
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_READ);
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t
-iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- u8 args;
- u32 value;
- int ret;
-
- args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value);
- /* if we only want to set the reg address - nothing more to do */
- if (args == 1)
- goto out;
-
- /* otherwise, make sure we have both address and value */
- if (args != 2)
- return -EINVAL;
-
- ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE);
- if (ret)
- return ret;
-
- iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
-out:
- return count;
-}
-
-static ssize_t
-iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,
- size_t count, loff_t *ppos)
-{
- int ret;
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL);
- mutex_unlock(&mvm->mutex);
-
- return ret ?: count;
-}
-
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
-
-/* Device wide debugfs entries */
-MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
-MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8);
-MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64);
-MVM_DEBUGFS_READ_FILE_OPS(nic_temp);
-MVM_DEBUGFS_READ_FILE_OPS(stations);
-MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
-MVM_DEBUGFS_READ_FILE_OPS(bt_cmd);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64);
-MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
-MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);
-MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
-MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
-MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10);
-MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
-MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8);
-
-#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
-#endif
-
-#ifdef CONFIG_PM_SLEEP
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
-#endif
-
-int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
-{
- struct dentry *bcast_dir __maybe_unused;
- char buf[100];
-
- spin_lock_init(&mvm->drv_stats_lock);
-
- mvm->debugfs_dir = dbgfs_dir;
-
- MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir,
- S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
- S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR);
- if (!debugfs_create_bool("enable_scan_iteration_notif",
- S_IRUSR | S_IWUSR,
- mvm->debugfs_dir,
- &mvm->scan_iter_notif_enabled))
- goto err;
-
-#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
- bcast_dir = debugfs_create_dir("bcast_filtering",
- mvm->debugfs_dir);
- if (!bcast_dir)
- goto err;
-
- if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR,
- bcast_dir,
- &mvm->dbgfs_bcast_filtering.override))
- goto err;
-
- MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
- bcast_dir, S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
- bcast_dir, S_IWUSR | S_IRUSR);
- }
-#endif
-
-#ifdef CONFIG_PM_SLEEP
- MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
- if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
- mvm->debugfs_dir, &mvm->d3_wake_sysassert))
- goto err;
- if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR,
- mvm->debugfs_dir, &mvm->last_netdetect_scans))
- goto err;
-#endif
-
- if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR,
- mvm->debugfs_dir,
- &mvm->low_latency_agg_frame_limit))
- goto err;
- if (!debugfs_create_u8("ps_disabled", S_IRUSR,
- mvm->debugfs_dir, &mvm->ps_disabled))
- goto err;
- if (!debugfs_create_blob("nvm_hw", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_hw_blob))
- goto err;
- if (!debugfs_create_blob("nvm_sw", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_sw_blob))
- goto err;
- if (!debugfs_create_blob("nvm_calib", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_calib_blob))
- goto err;
- if (!debugfs_create_blob("nvm_prod", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_prod_blob))
- goto err;
- if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
- goto err;
-
- /*
- * Create a symlink with mac80211. It will be removed when mac80211
- * exists (before the opmode exists which removes the target.)
- */
- snprintf(buf, 100, "../../%s/%s",
- dbgfs_dir->d_parent->d_parent->d_name.name,
- dbgfs_dir->d_parent->d_name.name);
- if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf))
- goto err;
-
- return 0;
-err:
- IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");
- return -ENOMEM;
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/iwlwifi/mvm/debugfs.h
deleted file mode 100644
index 8c4190e7e027..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#define MVM_DEBUGFS_READ_FILE_OPS(name) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .read = iwl_dbgfs_##name##_read, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-}
-
-#define MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
-static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \
- const char __user *user_buf, \
- size_t count, loff_t *ppos) \
-{ \
- argtype *arg = file->private_data; \
- char buf[buflen] = {}; \
- size_t buf_size = min(count, sizeof(buf) - 1); \
- \
- if (copy_from_user(buf, user_buf, buf_size)) \
- return -EFAULT; \
- \
- return iwl_dbgfs_##name##_write(arg, buf, buf_size, ppos); \
-} \
-
-#define _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \
-MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .write = _iwl_dbgfs_##name##_write, \
- .read = iwl_dbgfs_##name##_read, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
-
-#define _MVM_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \
-MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .write = _iwl_dbgfs_##name##_write, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
deleted file mode 100644
index d398a6102805..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
+++ /dev/null
@@ -1,476 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __fw_api_bt_coex_h__
-#define __fw_api_bt_coex_h__
-
-#include <linux/types.h>
-#include <linux/bitops.h>
-
-#define BITS(nb) (BIT(nb) - 1)
-
-/**
- * enum iwl_bt_coex_flags - flags for BT_COEX command
- * @BT_COEX_MODE_POS:
- * @BT_COEX_MODE_MSK:
- * @BT_COEX_DISABLE_OLD:
- * @BT_COEX_2W_OLD:
- * @BT_COEX_3W_OLD:
- * @BT_COEX_NW_OLD:
- * @BT_COEX_AUTO_OLD:
- * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests)
- * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests)
- * @BT_COEX_SYNC2SCO:
- * @BT_COEX_CORUNNING:
- * @BT_COEX_MPLUT:
- * @BT_COEX_TTC:
- * @BT_COEX_RRC:
- *
- * The COEX_MODE must be set for each command. Even if it is not changed.
- */
-enum iwl_bt_coex_flags {
- BT_COEX_MODE_POS = 3,
- BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
- BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS,
- BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS,
- BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS,
- BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS,
- BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS,
- BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS,
- BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS,
- BT_COEX_SYNC2SCO = BIT(7),
- BT_COEX_CORUNNING = BIT(8),
- BT_COEX_MPLUT = BIT(9),
- BT_COEX_TTC = BIT(20),
- BT_COEX_RRC = BIT(21),
-};
-
-/*
- * indicates what has changed in the BT_COEX command.
- * BT_VALID_ENABLE must be set for each command. Commands without this bit will
- * discarded by the firmware
- */
-enum iwl_bt_coex_valid_bit_msk {
- BT_VALID_ENABLE = BIT(0),
- BT_VALID_BT_PRIO_BOOST = BIT(1),
- BT_VALID_MAX_KILL = BIT(2),
- BT_VALID_3W_TMRS = BIT(3),
- BT_VALID_KILL_ACK = BIT(4),
- BT_VALID_KILL_CTS = BIT(5),
- BT_VALID_REDUCED_TX_POWER = BIT(6),
- BT_VALID_LUT = BIT(7),
- BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8),
- BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9),
- BT_VALID_MULTI_PRIO_LUT = BIT(10),
- BT_VALID_TRM_KICK_FILTER = BIT(11),
- BT_VALID_CORUN_LUT_20 = BIT(12),
- BT_VALID_CORUN_LUT_40 = BIT(13),
- BT_VALID_ANT_ISOLATION = BIT(14),
- BT_VALID_ANT_ISOLATION_THRS = BIT(15),
- BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16),
- BT_VALID_TXRX_MAX_FREQ_0 = BIT(17),
- BT_VALID_SYNC_TO_SCO = BIT(18),
- BT_VALID_TTC = BIT(20),
- BT_VALID_RRC = BIT(21),
-};
-
-/**
- * enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames.
- * @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames
- * @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames
- *
- * This mechanism allows to have BT and WiFi run concurrently. Since WiFi
- * reduces its Tx power, it can work along with BT, hence reducing the amount
- * of WiFi frames being killed by BT.
- */
-enum iwl_bt_reduced_tx_power {
- BT_REDUCED_TX_POWER_CTL = BIT(0),
- BT_REDUCED_TX_POWER_DATA = BIT(1),
-};
-
-enum iwl_bt_coex_lut_type {
- BT_COEX_TIGHT_LUT = 0,
- BT_COEX_LOOSE_LUT,
- BT_COEX_TX_DIS_LUT,
-
- BT_COEX_MAX_LUT,
- BT_COEX_INVALID_LUT = 0xff,
-}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */
-
-#define BT_COEX_LUT_SIZE (12)
-#define BT_COEX_CORUN_LUT_SIZE (32)
-#define BT_COEX_MULTI_PRIO_LUT_SIZE (2)
-#define BT_COEX_BOOST_SIZE (4)
-#define BT_REDUCED_TX_POWER_BIT BIT(7)
-
-/**
- * struct iwl_bt_coex_cmd_old - bt coex configuration command
- * @flags:&enum iwl_bt_coex_flags
- * @max_kill:
- * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
- * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
- * should be set by default
- * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
- * should be set by default
- * @bt4_antenna_isolation: antenna isolation
- * @bt4_antenna_isolation_thr: antenna threshold value
- * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
- * @bt4_tx_rx_max_freq0: TxRx max frequency
- * @bt_prio_boost: BT priority boost registers
- * @wifi_tx_prio_boost: SW boost of wifi tx priority
- * @wifi_rx_prio_boost: SW boost of wifi rx priority
- * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
- * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
- * @decision_lut: PTA decision LUT, per Prio-Ch
- * @bt4_multiprio_lut: multi priority LUT configuration
- * @bt4_corun_lut20: co-running 20 MHz LUT configuration
- * @bt4_corun_lut40: co-running 40 MHz LUT configuration
- * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
- *
- * The structure is used for the BT_COEX command.
- */
-struct iwl_bt_coex_cmd_old {
- __le32 flags;
- u8 max_kill;
- u8 bt_reduced_tx_power;
- u8 override_primary_lut;
- u8 override_secondary_lut;
-
- u8 bt4_antenna_isolation;
- u8 bt4_antenna_isolation_thr;
- u8 bt4_tx_tx_delta_freq_thr;
- u8 bt4_tx_rx_max_freq0;
-
- __le32 bt_prio_boost[BT_COEX_BOOST_SIZE];
- __le32 wifi_tx_prio_boost;
- __le32 wifi_rx_prio_boost;
- __le32 kill_ack_msk;
- __le32 kill_cts_msk;
-
- __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
- __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
- __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE];
- __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE];
-
- __le32 valid_bit_msk;
-} __packed; /* BT_COEX_CMD_API_S_VER_5 */
-
-enum iwl_bt_coex_mode {
- BT_COEX_DISABLE = 0x0,
- BT_COEX_NW = 0x1,
- BT_COEX_BT = 0x2,
- BT_COEX_WIFI = 0x3,
-}; /* BT_COEX_MODES_E */
-
-enum iwl_bt_coex_enabled_modules {
- BT_COEX_MPLUT_ENABLED = BIT(0),
- BT_COEX_MPLUT_BOOST_ENABLED = BIT(1),
- BT_COEX_SYNC2SCO_ENABLED = BIT(2),
- BT_COEX_CORUN_ENABLED = BIT(3),
- BT_COEX_HIGH_BAND_RET = BIT(4),
-}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */
-
-/**
- * struct iwl_bt_coex_cmd - bt coex configuration command
- * @mode: enum %iwl_bt_coex_mode
- * @enabled_modules: enum %iwl_bt_coex_enabled_modules
- *
- * The structure is used for the BT_COEX command.
- */
-struct iwl_bt_coex_cmd {
- __le32 mode;
- __le32 enabled_modules;
-} __packed; /* BT_COEX_CMD_API_S_VER_6 */
-
-/**
- * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut
- * @corun_lut20: co-running 20 MHz LUT configuration
- * @corun_lut40: co-running 40 MHz LUT configuration
- *
- * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command.
- */
-struct iwl_bt_coex_corun_lut_update_cmd {
- __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE];
- __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE];
-} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
-
-/**
- * struct iwl_bt_coex_reduced_txp_update_cmd
- * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
- * bits are the sta_id (value)
- */
-struct iwl_bt_coex_reduced_txp_update_cmd {
- __le32 reduced_txp;
-} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */
-
-/**
- * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
- * @bt_primary_ci:
- * @primary_ch_phy_id:
- * @bt_secondary_ci:
- * @secondary_ch_phy_id:
- *
- * Used for BT_COEX_CI command
- */
-struct iwl_bt_coex_ci_cmd {
- __le64 bt_primary_ci;
- __le32 primary_ch_phy_id;
-
- __le64 bt_secondary_ci;
- __le32 secondary_ch_phy_id;
-} __packed; /* BT_CI_MSG_API_S_VER_2 */
-
-#define BT_MBOX(n_dw, _msg, _pos, _nbits) \
- BT_MBOX##n_dw##_##_msg##_POS = (_pos), \
- BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS
-
-enum iwl_bt_mxbox_dw0 {
- BT_MBOX(0, LE_SLAVE_LAT, 0, 3),
- BT_MBOX(0, LE_PROF1, 3, 1),
- BT_MBOX(0, LE_PROF2, 4, 1),
- BT_MBOX(0, LE_PROF_OTHER, 5, 1),
- BT_MBOX(0, CHL_SEQ_N, 8, 4),
- BT_MBOX(0, INBAND_S, 13, 1),
- BT_MBOX(0, LE_MIN_RSSI, 16, 4),
- BT_MBOX(0, LE_SCAN, 20, 1),
- BT_MBOX(0, LE_ADV, 21, 1),
- BT_MBOX(0, LE_MAX_TX_POWER, 24, 4),
- BT_MBOX(0, OPEN_CON_1, 28, 2),
-};
-
-enum iwl_bt_mxbox_dw1 {
- BT_MBOX(1, BR_MAX_TX_POWER, 0, 4),
- BT_MBOX(1, IP_SR, 4, 1),
- BT_MBOX(1, LE_MSTR, 5, 1),
- BT_MBOX(1, AGGR_TRFC_LD, 8, 6),
- BT_MBOX(1, MSG_TYPE, 16, 3),
- BT_MBOX(1, SSN, 19, 2),
-};
-
-enum iwl_bt_mxbox_dw2 {
- BT_MBOX(2, SNIFF_ACT, 0, 3),
- BT_MBOX(2, PAG, 3, 1),
- BT_MBOX(2, INQUIRY, 4, 1),
- BT_MBOX(2, CONN, 5, 1),
- BT_MBOX(2, SNIFF_INTERVAL, 8, 5),
- BT_MBOX(2, DISC, 13, 1),
- BT_MBOX(2, SCO_TX_ACT, 16, 2),
- BT_MBOX(2, SCO_RX_ACT, 18, 2),
- BT_MBOX(2, ESCO_RE_TX, 20, 2),
- BT_MBOX(2, SCO_DURATION, 24, 6),
-};
-
-enum iwl_bt_mxbox_dw3 {
- BT_MBOX(3, SCO_STATE, 0, 1),
- BT_MBOX(3, SNIFF_STATE, 1, 1),
- BT_MBOX(3, A2DP_STATE, 2, 1),
- BT_MBOX(3, ACL_STATE, 3, 1),
- BT_MBOX(3, MSTR_STATE, 4, 1),
- BT_MBOX(3, OBX_STATE, 5, 1),
- BT_MBOX(3, OPEN_CON_2, 8, 2),
- BT_MBOX(3, TRAFFIC_LOAD, 10, 2),
- BT_MBOX(3, CHL_SEQN_LSB, 12, 1),
- BT_MBOX(3, INBAND_P, 13, 1),
- BT_MBOX(3, MSG_TYPE_2, 16, 3),
- BT_MBOX(3, SSN_2, 19, 2),
- BT_MBOX(3, UPDATE_REQUEST, 21, 1),
-};
-
-#define BT_MBOX_MSG(_notif, _num, _field) \
- ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
- >> BT_MBOX##_num##_##_field##_POS)
-
-enum iwl_bt_activity_grading {
- BT_OFF = 0,
- BT_ON_NO_CONNECTION = 1,
- BT_LOW_TRAFFIC = 2,
- BT_HIGH_TRAFFIC = 3,
-
- BT_MAX_AG,
-}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
-
-enum iwl_bt_ci_compliance {
- BT_CI_COMPLIANCE_NONE = 0,
- BT_CI_COMPLIANCE_PRIMARY = 1,
- BT_CI_COMPLIANCE_SECONDARY = 2,
- BT_CI_COMPLIANCE_BOTH = 3,
-}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */
-
-#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \
- (_ttc_rrc_status & BIT(_phy_id))
-
-#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \
- ((_ttc_rrc_status >> 4) & BIT(_phy_id))
-
-/**
- * struct iwl_bt_coex_profile_notif - notification about BT coex
- * @mbox_msg: message from BT to WiFi
- * @msg_idx: the index of the message
- * @bt_ci_compliance: enum %iwl_bt_ci_compliance
- * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type
- * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type
- * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
- * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY
- */
-struct iwl_bt_coex_profile_notif {
- __le32 mbox_msg[4];
- __le32 msg_idx;
- __le32 bt_ci_compliance;
-
- __le32 primary_ch_lut;
- __le32 secondary_ch_lut;
- __le32 bt_activity_grading;
- u8 ttc_rrc_status;
- u8 reserved[3];
-} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */
-
-enum iwl_bt_coex_prio_table_event {
- BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
- BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1,
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2,
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3,
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4,
- BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5,
- BT_COEX_PRIO_TBL_EVT_DTIM = 6,
- BT_COEX_PRIO_TBL_EVT_SCAN52 = 7,
- BT_COEX_PRIO_TBL_EVT_SCAN24 = 8,
- BT_COEX_PRIO_TBL_EVT_IDLE = 9,
- BT_COEX_PRIO_TBL_EVT_MAX = 16,
-}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */
-
-enum iwl_bt_coex_prio_table_prio {
- BT_COEX_PRIO_TBL_DISABLED = 0,
- BT_COEX_PRIO_TBL_PRIO_LOW = 1,
- BT_COEX_PRIO_TBL_PRIO_HIGH = 2,
- BT_COEX_PRIO_TBL_PRIO_BYPASS = 3,
- BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4,
- BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5,
- BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6,
- BT_COEX_PRIO_TBL_MAX = 8,
-}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */
-
-#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0)
-#define BT_COEX_PRIO_TBL_PRIO_POS (1)
-#define BT_COEX_PRIO_TBL_RESERVED_POS (4)
-
-/**
- * struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex
- * @prio_tbl:
- */
-struct iwl_bt_coex_prio_tbl_cmd {
- u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
-} __packed;
-
-/**
- * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command
- * @bt_primary_ci:
- * @bt_secondary_ci:
- * @co_run_bw_primary:
- * @co_run_bw_secondary:
- * @primary_ch_phy_id:
- * @secondary_ch_phy_id:
- *
- * Used for BT_COEX_CI command
- */
-struct iwl_bt_coex_ci_cmd_old {
- __le64 bt_primary_ci;
- __le64 bt_secondary_ci;
-
- u8 co_run_bw_primary;
- u8 co_run_bw_secondary;
- u8 primary_ch_phy_id;
- u8 secondary_ch_phy_id;
-} __packed; /* BT_CI_MSG_API_S_VER_1 */
-
-/**
- * struct iwl_bt_coex_profile_notif_old - notification about BT coex
- * @mbox_msg: message from BT to WiFi
- * @msg_idx: the index of the message
- * @bt_status: 0 - off, 1 - on
- * @bt_open_conn: number of BT connections open
- * @bt_traffic_load: load of BT traffic
- * @bt_agg_traffic_load: aggregated load of BT traffic
- * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
- * @primary_ch_lut: LUT used for primary channel
- * @secondary_ch_lut: LUT used for secondary channel
- * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
- */
-struct iwl_bt_coex_profile_notif_old {
- __le32 mbox_msg[4];
- __le32 msg_idx;
- u8 bt_status;
- u8 bt_open_conn;
- u8 bt_traffic_load;
- u8 bt_agg_traffic_load;
- u8 bt_ci_compliance;
- u8 ttc_enabled;
- u8 rrc_enabled;
- u8 reserved;
-
- __le32 primary_ch_lut;
- __le32 secondary_ch_lut;
- __le32 bt_activity_grading;
-} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
-
-#endif /* __fw_api_bt_coex_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
deleted file mode 100644
index 20521bebb0b1..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ /dev/null
@@ -1,425 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __fw_api_d3_h__
-#define __fw_api_d3_h__
-
-/**
- * enum iwl_d3_wakeup_flags - D3 manager wakeup flags
- * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert
- */
-enum iwl_d3_wakeup_flags {
- IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0),
-}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */
-
-/**
- * struct iwl_d3_manager_config - D3 manager configuration command
- * @min_sleep_time: minimum sleep time (in usec)
- * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags
- * @wakeup_host_timer: force wakeup after this many seconds
- *
- * The structure is used for the D3_CONFIG_CMD command.
- */
-struct iwl_d3_manager_config {
- __le32 min_sleep_time;
- __le32 wakeup_flags;
- __le32 wakeup_host_timer;
-} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */
-
-
-/* TODO: OFFLOADS_QUERY_API_S_VER_1 */
-
-/**
- * enum iwl_d3_proto_offloads - enabled protocol offloads
- * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled
- * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled
- */
-enum iwl_proto_offloads {
- IWL_D3_PROTO_OFFLOAD_ARP = BIT(0),
- IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
-};
-
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12
-
-#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4
-#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2
-
-/**
- * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
- * @enabled: enable flags
- * @remote_ipv4_addr: remote address to answer to (or zero if all)
- * @host_ipv4_addr: our IPv4 address to respond to queries for
- * @arp_mac_addr: our MAC address for ARP responses
- * @reserved: unused
- */
-struct iwl_proto_offload_cmd_common {
- __le32 enabled;
- __be32 remote_ipv4_addr;
- __be32 host_ipv4_addr;
- u8 arp_mac_addr[ETH_ALEN];
- __le16 reserved;
-} __packed;
-
-/**
- * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration
- * @common: common/IPv4 configuration
- * @remote_ipv6_addr: remote address to answer to (or zero if all)
- * @solicited_node_ipv6_addr: broken -- solicited node address exists
- * for each target address
- * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor solicitation response MAC address
- */
-struct iwl_proto_offload_cmd_v1 {
- struct iwl_proto_offload_cmd_common common;
- u8 remote_ipv6_addr[16];
- u8 solicited_node_ipv6_addr[16];
- u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16];
- u8 ndp_mac_addr[ETH_ALEN];
- __le16 reserved2;
-} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
-
-/**
- * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration
- * @common: common/IPv4 configuration
- * @remote_ipv6_addr: remote address to answer to (or zero if all)
- * @solicited_node_ipv6_addr: broken -- solicited node address exists
- * for each target address
- * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor solicitation response MAC address
- */
-struct iwl_proto_offload_cmd_v2 {
- struct iwl_proto_offload_cmd_common common;
- u8 remote_ipv6_addr[16];
- u8 solicited_node_ipv6_addr[16];
- u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16];
- u8 ndp_mac_addr[ETH_ALEN];
- u8 numValidIPv6Addresses;
- u8 reserved2[3];
-} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
-
-struct iwl_ns_config {
- struct in6_addr source_ipv6_addr;
- struct in6_addr dest_ipv6_addr;
- u8 target_mac_addr[ETH_ALEN];
- __le16 reserved;
-} __packed; /* NS_OFFLOAD_CONFIG */
-
-struct iwl_targ_addr {
- struct in6_addr addr;
- __le32 config_num;
-} __packed; /* TARGET_IPV6_ADDRESS */
-
-/**
- * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration
- * @common: common/IPv4 configuration
- * @target_ipv6_addr: target IPv6 addresses
- * @ns_config: NS offload configurations
- */
-struct iwl_proto_offload_cmd_v3_small {
- struct iwl_proto_offload_cmd_common common;
- __le32 num_valid_ipv6_addrs;
- struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S];
- struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S];
-} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
-
-/**
- * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration
- * @common: common/IPv4 configuration
- * @target_ipv6_addr: target IPv6 addresses
- * @ns_config: NS offload configurations
- */
-struct iwl_proto_offload_cmd_v3_large {
- struct iwl_proto_offload_cmd_common common;
- __le32 num_valid_ipv6_addrs;
- struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L];
- struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L];
-} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
-
-/*
- * WOWLAN_PATTERNS
- */
-#define IWL_WOWLAN_MIN_PATTERN_LEN 16
-#define IWL_WOWLAN_MAX_PATTERN_LEN 128
-
-struct iwl_wowlan_pattern {
- u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
- u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN];
- u8 mask_size;
- u8 pattern_size;
- __le16 reserved;
-} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */
-
-#define IWL_WOWLAN_MAX_PATTERNS 20
-
-struct iwl_wowlan_patterns_cmd {
- __le32 n_patterns;
- struct iwl_wowlan_pattern patterns[];
-} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */
-
-enum iwl_wowlan_wakeup_filters {
- IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0),
- IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1),
- IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2),
- IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3),
- IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4),
- IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5),
- IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6),
- IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7),
- IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8),
- IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9),
- IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10),
- IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11),
- IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12),
- IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13),
- IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14),
- IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15),
- IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16),
-}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
-
-struct iwl_wowlan_config_cmd {
- __le32 wakeup_filter;
- __le16 non_qos_seq;
- __le16 qos_seq[8];
- u8 wowlan_ba_teardown_tids;
- u8 is_11n_connection;
- u8 offloading_tid;
- u8 reserved[3];
-} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */
-
-/*
- * WOWLAN_TSC_RSC_PARAMS
- */
-#define IWL_NUM_RSC 16
-
-struct tkip_sc {
- __le16 iv16;
- __le16 pad;
- __le32 iv32;
-} __packed; /* TKIP_SC_API_U_VER_1 */
-
-struct iwl_tkip_rsc_tsc {
- struct tkip_sc unicast_rsc[IWL_NUM_RSC];
- struct tkip_sc multicast_rsc[IWL_NUM_RSC];
- struct tkip_sc tsc;
-} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */
-
-struct aes_sc {
- __le64 pn;
-} __packed; /* TKIP_AES_SC_API_U_VER_1 */
-
-struct iwl_aes_rsc_tsc {
- struct aes_sc unicast_rsc[IWL_NUM_RSC];
- struct aes_sc multicast_rsc[IWL_NUM_RSC];
- struct aes_sc tsc;
-} __packed; /* AES_TSC_RSC_API_S_VER_1 */
-
-union iwl_all_tsc_rsc {
- struct iwl_tkip_rsc_tsc tkip;
- struct iwl_aes_rsc_tsc aes;
-}; /* ALL_TSC_RSC_API_S_VER_2 */
-
-struct iwl_wowlan_rsc_tsc_params_cmd {
- union iwl_all_tsc_rsc all_tsc_rsc;
-} __packed; /* ALL_TSC_RSC_API_S_VER_2 */
-
-#define IWL_MIC_KEY_SIZE 8
-struct iwl_mic_keys {
- u8 tx[IWL_MIC_KEY_SIZE];
- u8 rx_unicast[IWL_MIC_KEY_SIZE];
- u8 rx_mcast[IWL_MIC_KEY_SIZE];
-} __packed; /* MIC_KEYS_API_S_VER_1 */
-
-#define IWL_P1K_SIZE 5
-struct iwl_p1k_cache {
- __le16 p1k[IWL_P1K_SIZE];
-} __packed;
-
-#define IWL_NUM_RX_P1K_CACHE 2
-
-struct iwl_wowlan_tkip_params_cmd {
- struct iwl_mic_keys mic_keys;
- struct iwl_p1k_cache tx;
- struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE];
- struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE];
-} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */
-
-#define IWL_KCK_MAX_SIZE 32
-#define IWL_KEK_MAX_SIZE 32
-
-struct iwl_wowlan_kek_kck_material_cmd {
- u8 kck[IWL_KCK_MAX_SIZE];
- u8 kek[IWL_KEK_MAX_SIZE];
- __le16 kck_len;
- __le16 kek_len;
- __le64 replay_ctr;
-} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */
-
-#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87
-
-enum iwl_wowlan_rekey_status {
- IWL_WOWLAN_REKEY_POST_REKEY = 0,
- IWL_WOWLAN_REKEY_WHILE_REKEY = 1,
-}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */
-
-enum iwl_wowlan_wakeup_reason {
- IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0,
- IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0),
- IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1),
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2),
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3),
- IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4),
- IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5),
- IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6),
- IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7),
- IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8),
- IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9),
- IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10),
- IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11),
- IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12),
- IWL_WOWLAN_WAKEUP_BY_IOAC_MAGIC_PACKET = BIT(13),
- IWL_WOWLAN_WAKEUP_BY_D3_WAKEUP_HOST_TIMER = BIT(14),
- IWL_WOWLAN_WAKEUP_BY_RXFRAME_FILTERED_IN = BIT(15),
- IWL_WOWLAN_WAKEUP_BY_BEACON_FILTERED_IN = BIT(16),
-
-}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
-
-struct iwl_wowlan_gtk_status {
- u8 key_index;
- u8 reserved[3];
- u8 decrypt_key[16];
- u8 tkip_mic_key[8];
- struct iwl_wowlan_rsc_tsc_params_cmd rsc;
-} __packed;
-
-struct iwl_wowlan_status {
- struct iwl_wowlan_gtk_status gtk;
- __le64 replay_ctr;
- __le16 pattern_number;
- __le16 non_qos_seq_ctr;
- __le16 qos_seq_ctr[8];
- __le32 wakeup_reasons;
- __le32 num_of_gtk_rekeys;
- __le32 transmitted_ndps;
- __le32 received_beacons;
- __le32 wake_packet_length;
- __le32 wake_packet_bufsize;
- u8 wake_packet[]; /* can be truncated from _length to _bufsize */
-} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */
-
-#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64
-#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128
-#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048
-
-struct iwl_tcp_packet_info {
- __le16 tcp_pseudo_header_checksum;
- __le16 tcp_payload_length;
-} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */
-
-struct iwl_tcp_packet {
- struct iwl_tcp_packet_info info;
- u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
- u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN];
-} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
-
-struct iwl_remote_wake_packet {
- struct iwl_tcp_packet_info info;
- u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
- u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN];
-} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
-
-struct iwl_wowlan_remote_wake_config {
- __le32 connection_max_time; /* unused */
- /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */
- u8 max_syn_retries;
- u8 max_data_retries;
- u8 tcp_syn_ack_timeout;
- u8 tcp_ack_timeout;
-
- struct iwl_tcp_packet syn_tx;
- struct iwl_tcp_packet synack_rx;
- struct iwl_tcp_packet keepalive_ack_rx;
- struct iwl_tcp_packet fin_tx;
-
- struct iwl_remote_wake_packet keepalive_tx;
- struct iwl_remote_wake_packet wake_rx;
-
- /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */
- u8 sequence_number_offset;
- u8 sequence_number_length;
- u8 token_offset;
- u8 token_length;
- /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */
- __le32 initial_sequence_number;
- __le16 keepalive_interval;
- __le16 num_tokens;
- u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS];
-} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */
-
-/* TODO: NetDetect API */
-
-#endif /* __fw_api_d3_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
deleted file mode 100644
index f3f3ee0a766b..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
+++ /dev/null
@@ -1,387 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __fw_api_mac_h__
-#define __fw_api_mac_h__
-
-/*
- * The first MAC indices (starting from 0)
- * are available to the driver, AUX follows
- */
-#define MAC_INDEX_AUX 4
-#define MAC_INDEX_MIN_DRIVER 0
-#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX
-#define NUM_MAC_INDEX (MAC_INDEX_AUX + 1)
-
-enum iwl_ac {
- AC_BK,
- AC_BE,
- AC_VI,
- AC_VO,
- AC_NUM,
-};
-
-/**
- * enum iwl_mac_protection_flags - MAC context flags
- * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames,
- * this will require CCK RTS/CTS2self.
- * RTS/CTS will protect full burst time.
- * @MAC_PROT_FLG_HT_PROT: enable HT protection
- * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions
- * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self
- */
-enum iwl_mac_protection_flags {
- MAC_PROT_FLG_TGG_PROTECT = BIT(3),
- MAC_PROT_FLG_HT_PROT = BIT(23),
- MAC_PROT_FLG_FAT_PROT = BIT(24),
- MAC_PROT_FLG_SELF_CTS_EN = BIT(30),
-};
-
-#define MAC_FLG_SHORT_SLOT BIT(4)
-#define MAC_FLG_SHORT_PREAMBLE BIT(5)
-
-/**
- * enum iwl_mac_types - Supported MAC types
- * @FW_MAC_TYPE_FIRST: lowest supported MAC type
- * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal)
- * @FW_MAC_TYPE_LISTENER: monitor MAC type (?)
- * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS
- * @FW_MAC_TYPE_IBSS: IBSS
- * @FW_MAC_TYPE_BSS_STA: BSS (managed) station
- * @FW_MAC_TYPE_P2P_DEVICE: P2P Device
- * @FW_MAC_TYPE_P2P_STA: P2P client
- * @FW_MAC_TYPE_GO: P2P GO
- * @FW_MAC_TYPE_TEST: ?
- * @FW_MAC_TYPE_MAX: highest support MAC type
- */
-enum iwl_mac_types {
- FW_MAC_TYPE_FIRST = 1,
- FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST,
- FW_MAC_TYPE_LISTENER,
- FW_MAC_TYPE_PIBSS,
- FW_MAC_TYPE_IBSS,
- FW_MAC_TYPE_BSS_STA,
- FW_MAC_TYPE_P2P_DEVICE,
- FW_MAC_TYPE_P2P_STA,
- FW_MAC_TYPE_GO,
- FW_MAC_TYPE_TEST,
- FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
-}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
-
-/**
- * enum iwl_tsf_id - TSF hw timer ID
- * @TSF_ID_A: use TSF A
- * @TSF_ID_B: use TSF B
- * @TSF_ID_C: use TSF C
- * @TSF_ID_D: use TSF D
- * @NUM_TSF_IDS: number of TSF timers available
- */
-enum iwl_tsf_id {
- TSF_ID_A = 0,
- TSF_ID_B = 1,
- TSF_ID_C = 2,
- TSF_ID_D = 3,
- NUM_TSF_IDS = 4,
-}; /* TSF_ID_API_E_VER_1 */
-
-/**
- * struct iwl_mac_data_ap - configuration data for AP MAC context
- * @beacon_time: beacon transmit time in system time
- * @beacon_tsf: beacon transmit time in TSF
- * @bi: beacon interval in TU
- * @bi_reciprocal: 2^32 / bi
- * @dtim_interval: dtim transmit time in TU
- * @dtim_reciprocal: 2^32 / dtim_interval
- * @mcast_qid: queue ID for multicast traffic
- * @beacon_template: beacon template ID
- */
-struct iwl_mac_data_ap {
- __le32 beacon_time;
- __le64 beacon_tsf;
- __le32 bi;
- __le32 bi_reciprocal;
- __le32 dtim_interval;
- __le32 dtim_reciprocal;
- __le32 mcast_qid;
- __le32 beacon_template;
-} __packed; /* AP_MAC_DATA_API_S_VER_1 */
-
-/**
- * struct iwl_mac_data_ibss - configuration data for IBSS MAC context
- * @beacon_time: beacon transmit time in system time
- * @beacon_tsf: beacon transmit time in TSF
- * @bi: beacon interval in TU
- * @bi_reciprocal: 2^32 / bi
- * @beacon_template: beacon template ID
- */
-struct iwl_mac_data_ibss {
- __le32 beacon_time;
- __le64 beacon_tsf;
- __le32 bi;
- __le32 bi_reciprocal;
- __le32 beacon_template;
-} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
-
-/**
- * struct iwl_mac_data_sta - configuration data for station MAC context
- * @is_assoc: 1 for associated state, 0 otherwise
- * @dtim_time: DTIM arrival time in system time
- * @dtim_tsf: DTIM arrival time in TSF
- * @bi: beacon interval in TU, applicable only when associated
- * @bi_reciprocal: 2^32 / bi , applicable only when associated
- * @dtim_interval: DTIM interval in TU, applicable only when associated
- * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated
- * @listen_interval: in beacon intervals, applicable only when associated
- * @assoc_id: unique ID assigned by the AP during association
- */
-struct iwl_mac_data_sta {
- __le32 is_assoc;
- __le32 dtim_time;
- __le64 dtim_tsf;
- __le32 bi;
- __le32 bi_reciprocal;
- __le32 dtim_interval;
- __le32 dtim_reciprocal;
- __le32 listen_interval;
- __le32 assoc_id;
- __le32 assoc_beacon_arrive_time;
-} __packed; /* STA_MAC_DATA_API_S_VER_1 */
-
-/**
- * struct iwl_mac_data_go - configuration data for P2P GO MAC context
- * @ap: iwl_mac_data_ap struct with most config data
- * @ctwin: client traffic window in TU (period after TBTT when GO is present).
- * 0 indicates that there is no CT window.
- * @opp_ps_enabled: indicate that opportunistic PS allowed
- */
-struct iwl_mac_data_go {
- struct iwl_mac_data_ap ap;
- __le32 ctwin;
- __le32 opp_ps_enabled;
-} __packed; /* GO_MAC_DATA_API_S_VER_1 */
-
-/**
- * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context
- * @sta: iwl_mac_data_sta struct with most config data
- * @ctwin: client traffic window in TU (period after TBTT when GO is present).
- * 0 indicates that there is no CT window.
- */
-struct iwl_mac_data_p2p_sta {
- struct iwl_mac_data_sta sta;
- __le32 ctwin;
-} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */
-
-/**
- * struct iwl_mac_data_pibss - Pseudo IBSS config data
- * @stats_interval: interval in TU between statistics notifications to host.
- */
-struct iwl_mac_data_pibss {
- __le32 stats_interval;
-} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */
-
-/*
- * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC
- * context.
- * @is_disc_extended: if set to true, P2P Device discoverability is enabled on
- * other channels as well. This should be to true only in case that the
- * device is discoverable and there is an active GO. Note that setting this
- * field when not needed, will increase the number of interrupts and have
- * effect on the platform power, as this setting opens the Rx filters on
- * all macs.
- */
-struct iwl_mac_data_p2p_dev {
- __le32 is_disc_extended;
-} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */
-
-/**
- * enum iwl_mac_filter_flags - MAC context filter flags
- * @MAC_FILTER_IN_PROMISC: accept all data frames
- * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and
- * control frames to the host
- * @MAC_FILTER_ACCEPT_GRP: accept multicast frames
- * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
- * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames
- * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host
- * (in station mode when associated)
- * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames
- * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames
- * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host
- */
-enum iwl_mac_filter_flags {
- MAC_FILTER_IN_PROMISC = BIT(0),
- MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1),
- MAC_FILTER_ACCEPT_GRP = BIT(2),
- MAC_FILTER_DIS_DECRYPT = BIT(3),
- MAC_FILTER_DIS_GRP_DECRYPT = BIT(4),
- MAC_FILTER_IN_BEACON = BIT(6),
- MAC_FILTER_OUT_BCAST = BIT(8),
- MAC_FILTER_IN_CRC32 = BIT(11),
- MAC_FILTER_IN_PROBE_REQUEST = BIT(12),
-};
-
-/**
- * enum iwl_mac_qos_flags - QoS flags
- * @MAC_QOS_FLG_UPDATE_EDCA: ?
- * @MAC_QOS_FLG_TGN: HT is enabled
- * @MAC_QOS_FLG_TXOP_TYPE: ?
- *
- */
-enum iwl_mac_qos_flags {
- MAC_QOS_FLG_UPDATE_EDCA = BIT(0),
- MAC_QOS_FLG_TGN = BIT(1),
- MAC_QOS_FLG_TXOP_TYPE = BIT(4),
-};
-
-/**
- * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD
- * @cw_min: Contention window, start value in numbers of slots.
- * Should be a power-of-2, minus 1. Device's default is 0x0f.
- * @cw_max: Contention window, max value in numbers of slots.
- * Should be a power-of-2, minus 1. Device's default is 0x3f.
- * @aifsn: Number of slots in Arbitration Interframe Space (before
- * performing random backoff timing prior to Tx). Device default 1.
- * @fifos_mask: FIFOs used by this MAC for this AC
- * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0.
- *
- * One instance of this config struct for each of 4 EDCA access categories
- * in struct iwl_qosparam_cmd.
- *
- * Device will automatically increase contention window by (2*CW) + 1 for each
- * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW
- * value, to cap the CW value.
- */
-struct iwl_ac_qos {
- __le16 cw_min;
- __le16 cw_max;
- u8 aifsn;
- u8 fifos_mask;
- __le16 edca_txop;
-} __packed; /* AC_QOS_API_S_VER_2 */
-
-/**
- * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts
- * ( MAC_CONTEXT_CMD = 0x28 )
- * @id_and_color: ID and color of the MAC
- * @action: action to perform, one of FW_CTXT_ACTION_*
- * @mac_type: one of FW_MAC_TYPE_*
- * @tsd_id: TSF HW timer, one of TSF_ID_*
- * @node_addr: MAC address
- * @bssid_addr: BSSID
- * @cck_rates: basic rates available for CCK
- * @ofdm_rates: basic rates available for OFDM
- * @protection_flags: combination of MAC_PROT_FLG_FLAG_*
- * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise
- * @short_slot: 0x10 for enabling short slots, 0 otherwise
- * @filter_flags: combination of MAC_FILTER_*
- * @qos_flags: from MAC_QOS_FLG_*
- * @ac: one iwl_mac_qos configuration for each AC
- * @mac_specific: one of struct iwl_mac_data_*, according to mac_type
- */
-struct iwl_mac_ctx_cmd {
- /* COMMON_INDEX_HDR_API_S_VER_1 */
- __le32 id_and_color;
- __le32 action;
- /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */
- __le32 mac_type;
- __le32 tsf_id;
- u8 node_addr[6];
- __le16 reserved_for_node_addr;
- u8 bssid_addr[6];
- __le16 reserved_for_bssid_addr;
- __le32 cck_rates;
- __le32 ofdm_rates;
- __le32 protection_flags;
- __le32 cck_short_preamble;
- __le32 short_slot;
- __le32 filter_flags;
- /* MAC_QOS_PARAM_API_S_VER_1 */
- __le32 qos_flags;
- struct iwl_ac_qos ac[AC_NUM+1];
- /* MAC_CONTEXT_COMMON_DATA_API_S */
- union {
- struct iwl_mac_data_ap ap;
- struct iwl_mac_data_go go;
- struct iwl_mac_data_sta sta;
- struct iwl_mac_data_p2p_sta p2p_sta;
- struct iwl_mac_data_p2p_dev p2p_dev;
- struct iwl_mac_data_pibss pibss;
- struct iwl_mac_data_ibss ibss;
- };
-} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
-
-static inline u32 iwl_mvm_reciprocal(u32 v)
-{
- if (!v)
- return 0;
- return 0xFFFFFFFF / v;
-}
-
-#define IWL_NONQOS_SEQ_GET 0x1
-#define IWL_NONQOS_SEQ_SET 0x2
-struct iwl_nonqos_seq_query_cmd {
- __le32 get_set_flag;
- __le32 mac_id_n_color;
- __le16 value;
- __le16 reserved;
-} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */
-
-#endif /* __fw_api_mac_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
deleted file mode 100644
index c8f3e2536cbb..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ /dev/null
@@ -1,467 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __fw_api_power_h__
-#define __fw_api_power_h__
-
-/* Power Management Commands, Responses, Notifications */
-
-/**
- * enum iwl_ltr_config_flags - masks for LTR config command flags
- * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status
- * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow
- * memory access
- * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR
- * reg change
- * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from
- * D0 to D3
- * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register
- * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register
- * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD
- */
-enum iwl_ltr_config_flags {
- LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0),
- LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1),
- LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2),
- LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3),
- LTR_CFG_FLAG_SW_SET_SHORT = BIT(4),
- LTR_CFG_FLAG_SW_SET_LONG = BIT(5),
- LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6),
-};
-
-/**
- * struct iwl_ltr_config_cmd_v1 - configures the LTR
- * @flags: See %enum iwl_ltr_config_flags
- */
-struct iwl_ltr_config_cmd_v1 {
- __le32 flags;
- __le32 static_long;
- __le32 static_short;
-} __packed; /* LTR_CAPABLE_API_S_VER_1 */
-
-#define LTR_VALID_STATES_NUM 4
-
-/**
- * struct iwl_ltr_config_cmd - configures the LTR
- * @flags: See %enum iwl_ltr_config_flags
- * @static_long:
- * @static_short:
- * @ltr_cfg_values:
- * @ltr_short_idle_timeout:
- */
-struct iwl_ltr_config_cmd {
- __le32 flags;
- __le32 static_long;
- __le32 static_short;
- __le32 ltr_cfg_values[LTR_VALID_STATES_NUM];
- __le32 ltr_short_idle_timeout;
-} __packed; /* LTR_CAPABLE_API_S_VER_2 */
-
-/* Radio LP RX Energy Threshold measured in dBm */
-#define POWER_LPRX_RSSI_THRESHOLD 75
-#define POWER_LPRX_RSSI_THRESHOLD_MAX 94
-#define POWER_LPRX_RSSI_THRESHOLD_MIN 30
-
-/**
- * enum iwl_power_flags - masks for power table command flags
- * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
- * receiver and transmitter. '0' - does not allow.
- * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
- * '1' Driver enables PM (use rest of parameters)
- * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
- * '1' PM could sleep over DTIM till listen Interval.
- * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
- * access categories are both delivery and trigger enabled.
- * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
- * PBW Snoozing enabled
- * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
- * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
- * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
- * detection enablement
-*/
-enum iwl_power_flags {
- POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
- POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1),
- POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2),
- POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5),
- POWER_FLAGS_BT_SCO_ENA = BIT(8),
- POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
- POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
- POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12),
-};
-
-#define IWL_POWER_VEC_SIZE 5
-
-/**
- * struct iwl_powertable_cmd - legacy power command. Beside old API support this
- * is used also with a new power API for device wide power settings.
- * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
- *
- * @flags: Power table command flags from POWER_FLAGS_*
- * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
- * Minimum allowed:- 3 * DTIM. Keep alive period must be
- * set regardless of power scheme or current power state.
- * FW use this value also when PM is disabled.
- * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
- * PSM transition - legacy PM
- * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
- * PSM transition - legacy PM
- * @sleep_interval: not in use
- * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
- * is set. For example, if it is required to skip over
- * one DTIM, this value need to be set to 2 (DTIM periods).
- * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
- * Default: 80dbm
- */
-struct iwl_powertable_cmd {
- /* PM_POWER_TABLE_CMD_API_S_VER_6 */
- __le16 flags;
- u8 keep_alive_seconds;
- u8 debug_flags;
- __le32 rx_data_timeout;
- __le32 tx_data_timeout;
- __le32 sleep_interval[IWL_POWER_VEC_SIZE];
- __le32 skip_dtim_periods;
- __le32 lprx_rssi_threshold;
-} __packed;
-
-/**
- * enum iwl_device_power_flags - masks for device power command flags
- * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
- * receiver and transmitter. '0' - does not allow.
-*/
-enum iwl_device_power_flags {
- DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
-};
-
-/**
- * struct iwl_device_power_cmd - device wide power command.
- * DEVICE_POWER_CMD = 0x77 (command, has simple generic response)
- *
- * @flags: Power table command flags from DEVICE_POWER_FLAGS_*
- */
-struct iwl_device_power_cmd {
- /* PM_POWER_TABLE_CMD_API_S_VER_6 */
- __le16 flags;
- __le16 reserved;
-} __packed;
-
-/**
- * struct iwl_mac_power_cmd - New power command containing uAPSD support
- * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
- * @id_and_color: MAC contex identifier
- * @flags: Power table command flags from POWER_FLAGS_*
- * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
- * Minimum allowed:- 3 * DTIM. Keep alive period must be
- * set regardless of power scheme or current power state.
- * FW use this value also when PM is disabled.
- * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
- * PSM transition - legacy PM
- * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
- * PSM transition - legacy PM
- * @sleep_interval: not in use
- * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
- * is set. For example, if it is required to skip over
- * one DTIM, this value need to be set to 2 (DTIM periods).
- * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
- * PSM transition - uAPSD
- * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
- * PSM transition - uAPSD
- * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
- * Default: 80dbm
- * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set
- * @snooze_interval: Maximum time between attempts to retrieve buffered data
- * from the AP [msec]
- * @snooze_window: A window of time in which PBW snoozing insures that all
- * packets received. It is also the minimum time from last
- * received unicast RX packet, before client stops snoozing
- * for data. [msec]
- * @snooze_step: TBD
- * @qndp_tid: TID client shall use for uAPSD QNDP triggers
- * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for
- * each corresponding AC.
- * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
- * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct
- * values.
- * @heavy_tx_thld_packets: TX threshold measured in number of packets
- * @heavy_rx_thld_packets: RX threshold measured in number of packets
- * @heavy_tx_thld_percentage: TX threshold measured in load's percentage
- * @heavy_rx_thld_percentage: RX threshold measured in load's percentage
- * @limited_ps_threshold:
-*/
-struct iwl_mac_power_cmd {
- /* CONTEXT_DESC_API_T_VER_1 */
- __le32 id_and_color;
-
- /* CLIENT_PM_POWER_TABLE_S_VER_1 */
- __le16 flags;
- __le16 keep_alive_seconds;
- __le32 rx_data_timeout;
- __le32 tx_data_timeout;
- __le32 rx_data_timeout_uapsd;
- __le32 tx_data_timeout_uapsd;
- u8 lprx_rssi_threshold;
- u8 skip_dtim_periods;
- __le16 snooze_interval;
- __le16 snooze_window;
- u8 snooze_step;
- u8 qndp_tid;
- u8 uapsd_ac_flags;
- u8 uapsd_max_sp;
- u8 heavy_tx_thld_packets;
- u8 heavy_rx_thld_packets;
- u8 heavy_tx_thld_percentage;
- u8 heavy_rx_thld_percentage;
- u8 limited_ps_threshold;
- u8 reserved;
-} __packed;
-
-/*
- * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
- * associated AP is identified as improperly implementing uAPSD protocol.
- * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78
- * @sta_id: index of station in uCode's station table - associated AP ID in
- * this context.
- */
-struct iwl_uapsd_misbehaving_ap_notif {
- __le32 sta_id;
- u8 mac_id;
- u8 reserved[3];
-} __packed;
-
-/**
- * struct iwl_reduce_tx_power_cmd - TX power reduction command
- * REDUCE_TX_POWER_CMD = 0x9f
- * @flags: (reserved for future implementation)
- * @mac_context_id: id of the mac ctx for which we are reducing TX power.
- * @pwr_restriction: TX power restriction in dBms.
- */
-struct iwl_reduce_tx_power_cmd {
- u8 flags;
- u8 mac_context_id;
- __le16 pwr_restriction;
-} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */
-
-enum iwl_dev_tx_power_cmd_mode {
- IWL_TX_POWER_MODE_SET_MAC = 0,
- IWL_TX_POWER_MODE_SET_DEVICE = 1,
- IWL_TX_POWER_MODE_SET_CHAINS = 2,
-}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_2 */;
-
-/**
- * struct iwl_dev_tx_power_cmd_v2 - TX power reduction command
- * @set_mode: see &enum iwl_dev_tx_power_cmd_mode
- * @mac_context_id: id of the mac ctx for which we are reducing TX power.
- * @pwr_restriction: TX power restriction in 1/8 dBms.
- * @dev_24: device TX power restriction in 1/8 dBms
- * @dev_52_low: device TX power restriction upper band - low
- * @dev_52_high: device TX power restriction upper band - high
- */
-struct iwl_dev_tx_power_cmd_v2 {
- __le32 set_mode;
- __le32 mac_context_id;
- __le16 pwr_restriction;
- __le16 dev_24;
- __le16 dev_52_low;
- __le16 dev_52_high;
-} __packed; /* TX_REDUCED_POWER_API_S_VER_2 */
-
-#define IWL_NUM_CHAIN_LIMITS 2
-#define IWL_NUM_SUB_BANDS 5
-
-/**
- * struct iwl_dev_tx_power_cmd - TX power reduction command
- * @v2: version 2 of the command, embedded here for easier software handling
- * @per_chain_restriction: per chain restrictions
- */
-struct iwl_dev_tx_power_cmd {
- /* v3 is just an extension of v2 - keep this here */
- struct iwl_dev_tx_power_cmd_v2 v2;
- __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS];
-} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */
-
-#define IWL_DEV_MAX_TX_POWER 0x7FFF
-
-/**
- * struct iwl_beacon_filter_cmd
- * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
- * @id_and_color: MAC contex identifier
- * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon
- * to driver if delta in Energy values calculated for this and last
- * passed beacon is greater than this threshold. Zero value means that
- * the Energy change is ignored for beacon filtering, and beacon will
- * not be forced to be sent to driver regardless of this delta. Typical
- * energy delta 5dB.
- * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state.
- * Send beacon to driver if delta in Energy values calculated for this
- * and last passed beacon is greater than this threshold. Zero value
- * means that the Energy change is ignored for beacon filtering while in
- * Roaming state, typical energy delta 1dB.
- * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values
- * calculated for current beacon is less than the threshold, use
- * Roaming Energy Delta Threshold, otherwise use normal Energy Delta
- * Threshold. Typical energy threshold is -72dBm.
- * @bf_temp_threshold: This threshold determines the type of temperature
- * filtering (Slow or Fast) that is selected (Units are in Celsuis):
- * If the current temperature is above this threshold - Fast filter
- * will be used, If the current temperature is below this threshold -
- * Slow filter will be used.
- * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values
- * calculated for this and the last passed beacon is greater than this
- * threshold. Zero value means that the temperature change is ignored for
- * beacon filtering; beacons will not be forced to be sent to driver
- * regardless of whether its temerature has been changed.
- * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values
- * calculated for this and the last passed beacon is greater than this
- * threshold. Zero value means that the temperature change is ignored for
- * beacon filtering; beacons will not be forced to be sent to driver
- * regardless of whether its temerature has been changed.
- * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
- * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed
- * for a specific period of time. Units: Beacons.
- * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed
- * for a longer period of time then this escape-timeout. Units: Beacons.
- * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled.
- */
-struct iwl_beacon_filter_cmd {
- __le32 bf_energy_delta;
- __le32 bf_roaming_energy_delta;
- __le32 bf_roaming_state;
- __le32 bf_temp_threshold;
- __le32 bf_temp_fast_filter;
- __le32 bf_temp_slow_filter;
- __le32 bf_enable_beacon_filter;
- __le32 bf_debug_flag;
- __le32 bf_escape_timer;
- __le32 ba_escape_timer;
- __le32 ba_enable_beacon_abort;
-} __packed;
-
-/* Beacon filtering and beacon abort */
-#define IWL_BF_ENERGY_DELTA_DEFAULT 5
-#define IWL_BF_ENERGY_DELTA_D0I3 20
-#define IWL_BF_ENERGY_DELTA_MAX 255
-#define IWL_BF_ENERGY_DELTA_MIN 0
-
-#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1
-#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20
-#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255
-#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0
-
-#define IWL_BF_ROAMING_STATE_DEFAULT 72
-#define IWL_BF_ROAMING_STATE_D0I3 72
-#define IWL_BF_ROAMING_STATE_MAX 255
-#define IWL_BF_ROAMING_STATE_MIN 0
-
-#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112
-#define IWL_BF_TEMP_THRESHOLD_D0I3 112
-#define IWL_BF_TEMP_THRESHOLD_MAX 255
-#define IWL_BF_TEMP_THRESHOLD_MIN 0
-
-#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1
-#define IWL_BF_TEMP_FAST_FILTER_D0I3 1
-#define IWL_BF_TEMP_FAST_FILTER_MAX 255
-#define IWL_BF_TEMP_FAST_FILTER_MIN 0
-
-#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5
-#define IWL_BF_TEMP_SLOW_FILTER_D0I3 20
-#define IWL_BF_TEMP_SLOW_FILTER_MAX 255
-#define IWL_BF_TEMP_SLOW_FILTER_MIN 0
-
-#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
-
-#define IWL_BF_DEBUG_FLAG_DEFAULT 0
-#define IWL_BF_DEBUG_FLAG_D0I3 0
-
-#define IWL_BF_ESCAPE_TIMER_DEFAULT 0
-#define IWL_BF_ESCAPE_TIMER_D0I3 0
-#define IWL_BF_ESCAPE_TIMER_MAX 1024
-#define IWL_BF_ESCAPE_TIMER_MIN 0
-
-#define IWL_BA_ESCAPE_TIMER_DEFAULT 6
-#define IWL_BA_ESCAPE_TIMER_D0I3 6
-#define IWL_BA_ESCAPE_TIMER_D3 9
-#define IWL_BA_ESCAPE_TIMER_MAX 1024
-#define IWL_BA_ESCAPE_TIMER_MIN 0
-
-#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
-
-#define IWL_BF_CMD_CONFIG(mode) \
- .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \
- .bf_roaming_energy_delta = \
- cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \
- .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \
- .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \
- .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \
- .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \
- .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \
- .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \
- .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode)
-
-#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT)
-#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3)
-#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
deleted file mode 100644
index 0f1ea80a55ef..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
+++ /dev/null
@@ -1,389 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __fw_api_rs_h__
-#define __fw_api_rs_h__
-
-#include "fw-api-mac.h"
-
-/*
- * These serve as indexes into
- * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
- * TODO: avoid overlap between legacy and HT rates
- */
-enum {
- IWL_RATE_1M_INDEX = 0,
- IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
- IWL_RATE_2M_INDEX,
- IWL_RATE_5M_INDEX,
- IWL_RATE_11M_INDEX,
- IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
- IWL_RATE_6M_INDEX,
- IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
- IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX,
- IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX,
- IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX,
- IWL_RATE_9M_INDEX,
- IWL_RATE_12M_INDEX,
- IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX,
- IWL_RATE_18M_INDEX,
- IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX,
- IWL_RATE_24M_INDEX,
- IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX,
- IWL_RATE_36M_INDEX,
- IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX,
- IWL_RATE_48M_INDEX,
- IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX,
- IWL_RATE_54M_INDEX,
- IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX,
- IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX,
- IWL_RATE_60M_INDEX,
- IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX,
- IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX,
- IWL_RATE_MCS_8_INDEX,
- IWL_RATE_MCS_9_INDEX,
- IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX,
- IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1,
- IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1,
-};
-
-#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX)
-
-/* fw API values for legacy bit rates, both OFDM and CCK */
-enum {
- IWL_RATE_6M_PLCP = 13,
- IWL_RATE_9M_PLCP = 15,
- IWL_RATE_12M_PLCP = 5,
- IWL_RATE_18M_PLCP = 7,
- IWL_RATE_24M_PLCP = 9,
- IWL_RATE_36M_PLCP = 11,
- IWL_RATE_48M_PLCP = 1,
- IWL_RATE_54M_PLCP = 3,
- IWL_RATE_1M_PLCP = 10,
- IWL_RATE_2M_PLCP = 20,
- IWL_RATE_5M_PLCP = 55,
- IWL_RATE_11M_PLCP = 110,
- IWL_RATE_INVM_PLCP = -1,
-};
-
-/*
- * rate_n_flags bit fields
- *
- * The 32-bit value has different layouts in the low 8 bites depending on the
- * format. There are three formats, HT, VHT and legacy (11abg, with subformats
- * for CCK and OFDM).
- *
- * High-throughput (HT) rate format
- * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM)
- * Very High-throughput (VHT) rate format
- * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM)
- * Legacy OFDM rate format for bits 7:0
- * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM)
- * Legacy CCK rate format for bits 7:0:
- * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK)
- */
-
-/* Bit 8: (1) HT format, (0) legacy or VHT format */
-#define RATE_MCS_HT_POS 8
-#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS)
-
-/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */
-#define RATE_MCS_CCK_POS 9
-#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS)
-
-/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */
-#define RATE_MCS_VHT_POS 26
-#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS)
-
-
-/*
- * High-throughput (HT) rate format for bits 7:0
- *
- * 2-0: MCS rate base
- * 0) 6 Mbps
- * 1) 12 Mbps
- * 2) 18 Mbps
- * 3) 24 Mbps
- * 4) 36 Mbps
- * 5) 48 Mbps
- * 6) 54 Mbps
- * 7) 60 Mbps
- * 4-3: 0) Single stream (SISO)
- * 1) Dual stream (MIMO)
- * 2) Triple stream (MIMO)
- * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data
- * (bits 7-6 are zero)
- *
- * Together the low 5 bits work out to the MCS index because we don't
- * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two
- * streams and 16-23 have three streams. We could also support MCS 32
- * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.)
- */
-#define RATE_HT_MCS_RATE_CODE_MSK 0x7
-#define RATE_HT_MCS_NSS_POS 3
-#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS)
-
-/* Bit 10: (1) Use Green Field preamble */
-#define RATE_HT_MCS_GF_POS 10
-#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS)
-
-#define RATE_HT_MCS_INDEX_MSK 0x3f
-
-/*
- * Very High-throughput (VHT) rate format for bits 7:0
- *
- * 3-0: VHT MCS (0-9)
- * 5-4: number of streams - 1:
- * 0) Single stream (SISO)
- * 1) Dual stream (MIMO)
- * 2) Triple stream (MIMO)
- */
-
-/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */
-#define RATE_VHT_MCS_RATE_CODE_MSK 0xf
-#define RATE_VHT_MCS_NSS_POS 4
-#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS)
-
-/*
- * Legacy OFDM rate format for bits 7:0
- *
- * 3-0: 0xD) 6 Mbps
- * 0xF) 9 Mbps
- * 0x5) 12 Mbps
- * 0x7) 18 Mbps
- * 0x9) 24 Mbps
- * 0xB) 36 Mbps
- * 0x1) 48 Mbps
- * 0x3) 54 Mbps
- * (bits 7-4 are 0)
- *
- * Legacy CCK rate format for bits 7:0:
- * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK):
- *
- * 6-0: 10) 1 Mbps
- * 20) 2 Mbps
- * 55) 5.5 Mbps
- * 110) 11 Mbps
- * (bit 7 is 0)
- */
-#define RATE_LEGACY_RATE_MSK 0xff
-
-
-/*
- * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz
- * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT
- */
-#define RATE_MCS_CHAN_WIDTH_POS 11
-#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS)
-#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS)
-#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS)
-#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS)
-#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS)
-
-/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */
-#define RATE_MCS_SGI_POS 13
-#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS)
-
-/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */
-#define RATE_MCS_ANT_POS 14
-#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS)
-#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS)
-#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS)
-#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \
- RATE_MCS_ANT_B_MSK)
-#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \
- RATE_MCS_ANT_C_MSK)
-#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK
-#define RATE_MCS_ANT_NUM 3
-
-/* Bit 17-18: (0) SS, (1) SS*2 */
-#define RATE_MCS_STBC_POS 17
-#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS)
-#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS)
-
-/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */
-#define RATE_MCS_BF_POS 19
-#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS)
-
-/* Bit 20: (0) ZLF is off, (1) ZLF is on */
-#define RATE_MCS_ZLF_POS 20
-#define RATE_MCS_ZLF_MSK (1 << RATE_MCS_ZLF_POS)
-
-/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */
-#define RATE_MCS_DUP_POS 24
-#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS)
-
-/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */
-#define RATE_MCS_LDPC_POS 27
-#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS)
-
-
-/* Link Quality definitions */
-
-/* # entries in rate scale table to support Tx retries */
-#define LQ_MAX_RETRY_NUM 16
-
-/* Link quality command flags bit fields */
-
-/* Bit 0: (0) Don't use RTS (1) Use RTS */
-#define LQ_FLAG_USE_RTS_POS 0
-#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS)
-
-/* Bit 1-3: LQ command color. Used to match responses to LQ commands */
-#define LQ_FLAG_COLOR_POS 1
-#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS)
-
-/* Bit 4-5: Tx RTS BW Signalling
- * (0) No RTS BW signalling
- * (1) Static BW signalling
- * (2) Dynamic BW signalling
- */
-#define LQ_FLAG_RTS_BW_SIG_POS 4
-#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS)
-#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS)
-#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS)
-
-/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection
- * Dyanmic BW selection allows Tx with narrower BW then requested in rates
- */
-#define LQ_FLAG_DYNAMIC_BW_POS 6
-#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS)
-
-/* Single Stream Tx Parameters (lq_cmd->ss_params)
- * Flags to control a smart FW decision about whether BFER/STBC/SISO will be
- * used for single stream Tx.
- */
-
-/* Bit 0-1: Max STBC streams allowed. Can be 0-3.
- * (0) - No STBC allowed
- * (1) - 2x1 STBC allowed (HT/VHT)
- * (2) - 4x2 STBC allowed (HT/VHT)
- * (3) - 3x2 STBC allowed (HT only)
- * All our chips are at most 2 antennas so only (1) is valid for now.
- */
-#define LQ_SS_STBC_ALLOWED_POS 0
-#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK)
-
-/* 2x1 STBC is allowed */
-#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS)
-
-/* Bit 2: Beamformer (VHT only) is allowed */
-#define LQ_SS_BFER_ALLOWED_POS 2
-#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS)
-
-/* Bit 3: Force BFER or STBC for testing
- * If this is set:
- * If BFER is allowed then force the ucode to choose BFER else
- * If STBC is allowed then force the ucode to choose STBC over SISO
- */
-#define LQ_SS_FORCE_POS 3
-#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS)
-
-/* Bit 31: ss_params field is valid. Used for FW backward compatibility
- * with other drivers which don't support the ss_params API yet
- */
-#define LQ_SS_PARAMS_VALID_POS 31
-#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS)
-
-/**
- * struct iwl_lq_cmd - link quality command
- * @sta_id: station to update
- * @control: not used
- * @flags: combination of LQ_FLAG_*
- * @mimo_delim: the first SISO index in rs_table, which separates MIMO
- * and SISO rates
- * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD).
- * Should be ANT_[ABC]
- * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC]
- * @initial_rate_index: first index from rs_table per AC category
- * @agg_time_limit: aggregation max time threshold in usec/100, meaning
- * value of 100 is one usec. Range is 100 to 8000
- * @agg_disable_start_th: try-count threshold for starting aggregation.
- * If a frame has higher try-count, it should not be selected for
- * starting an aggregation sequence.
- * @agg_frame_cnt_limit: max frame count in an aggregation.
- * 0: no limit
- * 1: no aggregation (one frame per aggregation)
- * 2 - 0x3f: maximal number of frames (up to 3f == 63)
- * @rs_table: array of rates for each TX try, each is rate_n_flags,
- * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP
- * @ss_params: single stream features. declare whether STBC or BFER are allowed.
- */
-struct iwl_lq_cmd {
- u8 sta_id;
- u8 reduced_tpc;
- u16 control;
- /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */
- u8 flags;
- u8 mimo_delim;
- u8 single_stream_ant_msk;
- u8 dual_stream_ant_msk;
- u8 initial_rate_index[AC_NUM];
- /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */
- __le16 agg_time_limit;
- u8 agg_disable_start_th;
- u8 agg_frame_cnt_limit;
- __le32 reserved2;
- __le32 rs_table[LQ_MAX_RETRY_NUM];
- __le32 ss_params;
-}; /* LINK_QUALITY_CMD_API_S_VER_1 */
-#endif /* __fw_api_rs_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h
deleted file mode 100644
index 9b7e49d4620f..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __fw_api_rx_h__
-#define __fw_api_rx_h__
-
-#define IWL_RX_INFO_PHY_CNT 8
-#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
-#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
-#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00
-#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000
-#define IWL_RX_INFO_ENERGY_ANT_A_POS 0
-#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
-#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
-
-/**
- * struct iwl_rx_phy_info - phy info
- * (REPLY_RX_PHY_CMD = 0xc0)
- * @non_cfg_phy_cnt: non configurable DSP phy data byte count
- * @cfg_phy_cnt: configurable DSP phy data byte count
- * @stat_id: configurable DSP phy data set ID
- * @reserved1:
- * @system_timestamp: GP2 at on air rise
- * @timestamp: TSF at on air rise
- * @beacon_time_stamp: beacon at on-air rise
- * @phy_flags: general phy flags: band, modulation, ...
- * @channel: channel number
- * @non_cfg_phy_buf: for various implementations of non_cfg_phy
- * @rate_n_flags: RATE_MCS_*
- * @byte_count: frame's byte-count
- * @frame_time: frame's time on the air, based on byte count and frame rate
- * calculation
- * @mac_active_msk: what MACs were active when the frame was received
- *
- * Before each Rx, the device sends this data. It contains PHY information
- * about the reception of the packet.
- */
-struct iwl_rx_phy_info {
- u8 non_cfg_phy_cnt;
- u8 cfg_phy_cnt;
- u8 stat_id;
- u8 reserved1;
- __le32 system_timestamp;
- __le64 timestamp;
- __le32 beacon_time_stamp;
- __le16 phy_flags;
- __le16 channel;
- __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
- __le32 rate_n_flags;
- __le32 byte_count;
- __le16 mac_active_msk;
- __le16 frame_time;
-} __packed;
-
-/*
- * TCP offload Rx assist info
- *
- * bits 0:3 - reserved
- * bits 4:7 - MIC CRC length
- * bits 8:12 - MAC header length
- * bit 13 - Padding indication
- * bit 14 - A-AMSDU indication
- * bit 15 - Offload enabled
- */
-enum iwl_csum_rx_assist_info {
- CSUM_RXA_RESERVED_MASK = 0x000f,
- CSUM_RXA_MICSIZE_MASK = 0x00f0,
- CSUM_RXA_HEADERLEN_MASK = 0x1f00,
- CSUM_RXA_PADD = BIT(13),
- CSUM_RXA_AMSDU = BIT(14),
- CSUM_RXA_ENA = BIT(15)
-};
-
-/**
- * struct iwl_rx_mpdu_res_start - phy info
- * @assist: see CSUM_RX_ASSIST_ above
- */
-struct iwl_rx_mpdu_res_start {
- __le16 byte_count;
- __le16 assist;
-} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */
-
-/**
- * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags
- * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band
- * @RX_RES_PHY_FLAGS_MOD_CCK:
- * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short
- * @RX_RES_PHY_FLAGS_NARROW_BAND:
- * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received
- * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU
- * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame
- * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble
- * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame
- */
-enum iwl_rx_phy_flags {
- RX_RES_PHY_FLAGS_BAND_24 = BIT(0),
- RX_RES_PHY_FLAGS_MOD_CCK = BIT(1),
- RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2),
- RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3),
- RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4),
- RX_RES_PHY_FLAGS_ANTENNA_POS = 4,
- RX_RES_PHY_FLAGS_AGG = BIT(7),
- RX_RES_PHY_FLAGS_OFDM_HT = BIT(8),
- RX_RES_PHY_FLAGS_OFDM_GF = BIT(9),
- RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10),
-};
-
-/**
- * enum iwl_mvm_rx_status - written by fw for each Rx packet
- * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine
- * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow
- * @RX_MPDU_RES_STATUS_SRC_STA_FOUND:
- * @RX_MPDU_RES_STATUS_KEY_VALID:
- * @RX_MPDU_RES_STATUS_KEY_PARAM_OK:
- * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed
- * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked
- * in the driver.
- * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine
- * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or
- * alg = CCM only. Checks replay attack for 11w frames. Relevant only if
- * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set.
- * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted
- * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP
- * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM
- * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP
- * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC
- * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted
- * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm
- * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted
- * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP:
- * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP:
- * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT:
- * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame
- * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw
- * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors
- * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK:
- * @RX_MPDU_RES_STATUS_STA_ID_MSK:
- * @RX_MPDU_RES_STATUS_RRF_KILL:
- * @RX_MPDU_RES_STATUS_FILTERING_MSK:
- * @RX_MPDU_RES_STATUS2_FILTERING_MSK:
- */
-enum iwl_mvm_rx_status {
- RX_MPDU_RES_STATUS_CRC_OK = BIT(0),
- RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1),
- RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2),
- RX_MPDU_RES_STATUS_KEY_VALID = BIT(3),
- RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4),
- RX_MPDU_RES_STATUS_ICV_OK = BIT(5),
- RX_MPDU_RES_STATUS_MIC_OK = BIT(6),
- RX_MPDU_RES_STATUS_TTAK_OK = BIT(7),
- RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7),
- RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8),
- RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8),
- RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8),
- RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8),
- RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8),
- RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8),
- RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8),
- RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8),
- RX_MPDU_RES_STATUS_DEC_DONE = BIT(11),
- RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12),
- RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13),
- RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14),
- RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15),
- RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16),
- RX_MPDU_RES_STATUS_CSUM_OK = BIT(17),
- RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000),
- RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000),
- RX_MPDU_RES_STATUS_RRF_KILL = BIT(29),
- RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000),
- RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000),
-};
-
-#endif /* __fw_api_rx_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
deleted file mode 100644
index 3a657e4b60ac..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ /dev/null
@@ -1,730 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __fw_api_scan_h__
-#define __fw_api_scan_h__
-
-#include "fw-api.h"
-
-/* Scan Commands, Responses, Notifications */
-
-/* Max number of IEs for direct SSID scans in a command */
-#define PROBE_OPTION_MAX 20
-
-/**
- * struct iwl_ssid_ie - directed scan network information element
- *
- * Up to 20 of these may appear in REPLY_SCAN_CMD,
- * selected by "type" bit field in struct iwl_scan_channel;
- * each channel may select different ssids from among the 20 entries.
- * SSID IEs get transmitted in reverse order of entry.
- */
-struct iwl_ssid_ie {
- u8 id;
- u8 len;
- u8 ssid[IEEE80211_MAX_SSID_LEN];
-} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
-
-/* scan offload */
-#define IWL_SCAN_MAX_BLACKLIST_LEN 64
-#define IWL_SCAN_SHORT_BLACKLIST_LEN 16
-#define IWL_SCAN_MAX_PROFILES 11
-#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512
-
-/* Default watchdog (in MS) for scheduled scan iteration */
-#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000)
-
-#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1)
-#define CAN_ABORT_STATUS 1
-
-#define IWL_FULL_SCAN_MULTIPLIER 5
-#define IWL_FAST_SCHED_SCAN_ITERATIONS 3
-#define IWL_MAX_SCHED_SCAN_PLANS 2
-
-enum scan_framework_client {
- SCAN_CLIENT_SCHED_SCAN = BIT(0),
- SCAN_CLIENT_NETDETECT = BIT(1),
- SCAN_CLIENT_ASSET_TRACKING = BIT(2),
-};
-
-/**
- * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S
- * @ssid: MAC address to filter out
- * @reported_rssi: AP rssi reported to the host
- * @client_bitmap: clients ignore this entry - enum scan_framework_client
- */
-struct iwl_scan_offload_blacklist {
- u8 ssid[ETH_ALEN];
- u8 reported_rssi;
- u8 client_bitmap;
-} __packed;
-
-enum iwl_scan_offload_network_type {
- IWL_NETWORK_TYPE_BSS = 1,
- IWL_NETWORK_TYPE_IBSS = 2,
- IWL_NETWORK_TYPE_ANY = 3,
-};
-
-enum iwl_scan_offload_band_selection {
- IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4,
- IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8,
- IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc,
-};
-
-/**
- * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
- * @ssid_index: index to ssid list in fixed part
- * @unicast_cipher: encryption algorithm to match - bitmap
- * @aut_alg: authentication algorithm to match - bitmap
- * @network_type: enum iwl_scan_offload_network_type
- * @band_selection: enum iwl_scan_offload_band_selection
- * @client_bitmap: clients waiting for match - enum scan_framework_client
- */
-struct iwl_scan_offload_profile {
- u8 ssid_index;
- u8 unicast_cipher;
- u8 auth_alg;
- u8 network_type;
- u8 band_selection;
- u8 client_bitmap;
- u8 reserved[2];
-} __packed;
-
-/**
- * iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1
- * @blaclist: AP list to filter off from scan results
- * @profiles: profiles to search for match
- * @blacklist_len: length of blacklist
- * @num_profiles: num of profiles in the list
- * @match_notify: clients waiting for match found notification
- * @pass_match: clients waiting for the results
- * @active_clients: active clients bitmap - enum scan_framework_client
- * @any_beacon_notify: clients waiting for match notification without match
- */
-struct iwl_scan_offload_profile_cfg {
- struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
- u8 blacklist_len;
- u8 num_profiles;
- u8 match_notify;
- u8 pass_match;
- u8 active_clients;
- u8 any_beacon_notify;
- u8 reserved[2];
-} __packed;
-
-/**
- * iwl_scan_schedule_lmac - schedule of scan offload
- * @delay: delay between iterations, in seconds.
- * @iterations: num of scan iterations
- * @full_scan_mul: number of partial scans before each full scan
- */
-struct iwl_scan_schedule_lmac {
- __le16 delay;
- u8 iterations;
- u8 full_scan_mul;
-} __packed; /* SCAN_SCHEDULE_API_S */
-
-enum iwl_scan_offload_complete_status {
- IWL_SCAN_OFFLOAD_COMPLETED = 1,
- IWL_SCAN_OFFLOAD_ABORTED = 2,
-};
-
-enum iwl_scan_ebs_status {
- IWL_SCAN_EBS_SUCCESS,
- IWL_SCAN_EBS_FAILED,
- IWL_SCAN_EBS_CHAN_NOT_FOUND,
- IWL_SCAN_EBS_INACTIVE,
-};
-
-/**
- * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S
- * @tx_flags: combination of TX_CMD_FLG_*
- * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
- * cleared. Combination of RATE_MCS_*
- * @sta_id: index of destination station in FW station table
- * @reserved: for alignment and future use
- */
-struct iwl_scan_req_tx_cmd {
- __le32 tx_flags;
- __le32 rate_n_flags;
- u8 sta_id;
- u8 reserved[3];
-} __packed;
-
-enum iwl_scan_channel_flags_lmac {
- IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27),
- IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28),
-};
-
-/**
- * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2
- * @flags: bits 1-20: directed scan to i'th ssid
- * other bits &enum iwl_scan_channel_flags_lmac
- * @channel_number: channel number 1-13 etc
- * @iter_count: scan iteration on this channel
- * @iter_interval: interval in seconds between iterations on one channel
- */
-struct iwl_scan_channel_cfg_lmac {
- __le32 flags;
- __le16 channel_num;
- __le16 iter_count;
- __le32 iter_interval;
-} __packed;
-
-/*
- * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1
- * @offset: offset in the data block
- * @len: length of the segment
- */
-struct iwl_scan_probe_segment {
- __le16 offset;
- __le16 len;
-} __packed;
-
-/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2
- * @mac_header: first (and common) part of the probe
- * @band_data: band specific data
- * @common_data: last (and common) part of the probe
- * @buf: raw data block
- */
-struct iwl_scan_probe_req {
- struct iwl_scan_probe_segment mac_header;
- struct iwl_scan_probe_segment band_data[2];
- struct iwl_scan_probe_segment common_data;
- u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE];
-} __packed;
-
-enum iwl_scan_channel_flags {
- IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0),
- IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1),
- IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2),
-};
-
-/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S
- * @flags: enum iwl_scan_channel_flags
- * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is
- * involved.
- * 1 - EBS is disabled.
- * 2 - every second scan will be full scan(and so on).
- */
-struct iwl_scan_channel_opt {
- __le16 flags;
- __le16 non_ebs_ratio;
-} __packed;
-
-/**
- * iwl_mvm_lmac_scan_flags
- * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses
- * without filtering.
- * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels
- * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan
- * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification
- * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching
- * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
- * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report
- * and DS parameter set IEs into probe requests.
- * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches
- */
-enum iwl_mvm_lmac_scan_flags {
- IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0),
- IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1),
- IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2),
- IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3),
- IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4),
- IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5),
- IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6),
- IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9),
-};
-
-enum iwl_scan_priority {
- IWL_SCAN_PRIORITY_LOW,
- IWL_SCAN_PRIORITY_MEDIUM,
- IWL_SCAN_PRIORITY_HIGH,
-};
-
-enum iwl_scan_priority_ext {
- IWL_SCAN_PRIORITY_EXT_0_LOWEST,
- IWL_SCAN_PRIORITY_EXT_1,
- IWL_SCAN_PRIORITY_EXT_2,
- IWL_SCAN_PRIORITY_EXT_3,
- IWL_SCAN_PRIORITY_EXT_4,
- IWL_SCAN_PRIORITY_EXT_5,
- IWL_SCAN_PRIORITY_EXT_6,
- IWL_SCAN_PRIORITY_EXT_7_HIGHEST,
-};
-
-/**
- * iwl_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1
- * @reserved1: for alignment and future use
- * @channel_num: num of channels to scan
- * @active-dwell: dwell time for active channels
- * @passive-dwell: dwell time for passive channels
- * @fragmented-dwell: dwell time for fragmented passive scan
- * @reserved2: for alignment and future use
- * @rx_chain_selct: PHY_RX_CHAIN_* flags
- * @scan_flags: &enum iwl_mvm_lmac_scan_flags
- * @max_out_time: max time (in TU) to be out of associated channel
- * @suspend_time: pause scan this long (TUs) when returning to service channel
- * @flags: RXON flags
- * @filter_flags: RXON filter
- * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz
- * @direct_scan: list of SSIDs for directed active scan
- * @scan_prio: enum iwl_scan_priority
- * @iter_num: number of scan iterations
- * @delay: delay in seconds before first iteration
- * @schedule: two scheduling plans. The first one is finite, the second one can
- * be infinite.
- * @channel_opt: channel optimization options, for full and partial scan
- * @data: channel configuration and probe request packet.
- */
-struct iwl_scan_req_lmac {
- /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */
- __le32 reserved1;
- u8 n_channels;
- u8 active_dwell;
- u8 passive_dwell;
- u8 fragmented_dwell;
- __le16 reserved2;
- __le16 rx_chain_select;
- __le32 scan_flags;
- __le32 max_out_time;
- __le32 suspend_time;
- /* RX_ON_FLAGS_API_S_VER_1 */
- __le32 flags;
- __le32 filter_flags;
- struct iwl_scan_req_tx_cmd tx_cmd[2];
- struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
- __le32 scan_prio;
- /* SCAN_REQ_PERIODIC_PARAMS_API_S */
- __le32 iter_num;
- __le32 delay;
- struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS];
- struct iwl_scan_channel_opt channel_opt[2];
- u8 data[];
-} __packed;
-
-/**
- * struct iwl_scan_results_notif - scan results for one channel -
- * SCAN_RESULT_NTF_API_S_VER_3
- * @channel: which channel the results are from
- * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
- * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
- * @num_probe_not_sent: # of request that weren't sent due to not enough time
- * @duration: duration spent in channel, in usecs
- */
-struct iwl_scan_results_notif {
- u8 channel;
- u8 band;
- u8 probe_status;
- u8 num_probe_not_sent;
- __le32 duration;
-} __packed;
-
-/**
- * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels)
- * SCAN_COMPLETE_NTF_API_S_VER_3
- * @scanned_channels: number of channels scanned (and number of valid results)
- * @status: one of SCAN_COMP_STATUS_*
- * @bt_status: BT on/off status
- * @last_channel: last channel that was scanned
- * @tsf_low: TSF timer (lower half) in usecs
- * @tsf_high: TSF timer (higher half) in usecs
- * @results: an array of scan results, only "scanned_channels" of them are valid
- */
-struct iwl_lmac_scan_complete_notif {
- u8 scanned_channels;
- u8 status;
- u8 bt_status;
- u8 last_channel;
- __le32 tsf_low;
- __le32 tsf_high;
- struct iwl_scan_results_notif results[];
-} __packed;
-
-/**
- * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2
- * @last_schedule_line: last schedule line executed (fast or regular)
- * @last_schedule_iteration: last scan iteration executed before scan abort
- * @status: enum iwl_scan_offload_complete_status
- * @ebs_status: EBS success status &enum iwl_scan_ebs_status
- * @time_after_last_iter; time in seconds elapsed after last iteration
- */
-struct iwl_periodic_scan_complete {
- u8 last_schedule_line;
- u8 last_schedule_iteration;
- u8 status;
- u8 ebs_status;
- __le32 time_after_last_iter;
- __le32 reserved;
-} __packed;
-
-/* UMAC Scan API */
-
-/* The maximum of either of these cannot exceed 8, because we use an
- * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h).
- */
-#define IWL_MVM_MAX_UMAC_SCANS 8
-#define IWL_MVM_MAX_LMAC_SCANS 1
-
-enum scan_config_flags {
- SCAN_CONFIG_FLAG_ACTIVATE = BIT(0),
- SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1),
- SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2),
- SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3),
- SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8),
- SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9),
- SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10),
- SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11),
- SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12),
- SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13),
- SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14),
- SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15),
- SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16),
- SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17),
- SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18),
- SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19),
- SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20),
- SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21),
-
- /* Bits 26-31 are for num of channels in channel_array */
-#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26)
-};
-
-enum scan_config_rates {
- /* OFDM basic rates */
- SCAN_CONFIG_RATE_6M = BIT(0),
- SCAN_CONFIG_RATE_9M = BIT(1),
- SCAN_CONFIG_RATE_12M = BIT(2),
- SCAN_CONFIG_RATE_18M = BIT(3),
- SCAN_CONFIG_RATE_24M = BIT(4),
- SCAN_CONFIG_RATE_36M = BIT(5),
- SCAN_CONFIG_RATE_48M = BIT(6),
- SCAN_CONFIG_RATE_54M = BIT(7),
- /* CCK basic rates */
- SCAN_CONFIG_RATE_1M = BIT(8),
- SCAN_CONFIG_RATE_2M = BIT(9),
- SCAN_CONFIG_RATE_5M = BIT(10),
- SCAN_CONFIG_RATE_11M = BIT(11),
-
- /* Bits 16-27 are for supported rates */
-#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16)
-};
-
-enum iwl_channel_flags {
- IWL_CHANNEL_FLAG_EBS = BIT(0),
- IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1),
- IWL_CHANNEL_FLAG_EBS_ADD = BIT(2),
- IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3),
-};
-
-/**
- * struct iwl_scan_config
- * @flags: enum scan_config_flags
- * @tx_chains: valid_tx antenna - ANT_* definitions
- * @rx_chains: valid_rx antenna - ANT_* definitions
- * @legacy_rates: default legacy rates - enum scan_config_rates
- * @out_of_channel_time: default max out of serving channel time
- * @suspend_time: default max suspend time
- * @dwell_active: default dwell time for active scan
- * @dwell_passive: default dwell time for passive scan
- * @dwell_fragmented: default dwell time for fragmented scan
- * @reserved: for future use and alignment
- * @mac_addr: default mac address to be used in probes
- * @bcast_sta_id: the index of the station in the fw
- * @channel_flags: default channel flags - enum iwl_channel_flags
- * scan_config_channel_flag
- * @channel_array: default supported channels
- */
-struct iwl_scan_config {
- __le32 flags;
- __le32 tx_chains;
- __le32 rx_chains;
- __le32 legacy_rates;
- __le32 out_of_channel_time;
- __le32 suspend_time;
- u8 dwell_active;
- u8 dwell_passive;
- u8 dwell_fragmented;
- u8 reserved;
- u8 mac_addr[ETH_ALEN];
- u8 bcast_sta_id;
- u8 channel_flags;
- u8 channel_array[];
-} __packed; /* SCAN_CONFIG_DB_CMD_API_S */
-
-/**
- * iwl_umac_scan_flags
- *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request
- * can be preempted by other scan requests with higher priority.
- * The low priority scan will be resumed when the higher proirity scan is
- * completed.
- *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver
- * when scan starts.
- */
-enum iwl_umac_scan_flags {
- IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0),
- IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1),
-};
-
-enum iwl_umac_scan_uid_offsets {
- IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0,
- IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8,
-};
-
-enum iwl_umac_scan_general_flags {
- IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0),
- IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1),
- IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2),
- IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3),
- IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4),
- IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5),
- IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6),
- IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7),
- IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8),
- IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9)
-};
-
-/**
- * struct iwl_scan_channel_cfg_umac
- * @flags: bitmap - 0-19: directed scan to i'th ssid.
- * @channel_num: channel number 1-13 etc.
- * @iter_count: repetition count for the channel.
- * @iter_interval: interval between two scan iterations on one channel.
- */
-struct iwl_scan_channel_cfg_umac {
- __le32 flags;
- u8 channel_num;
- u8 iter_count;
- __le16 iter_interval;
-} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */
-
-/**
- * struct iwl_scan_umac_schedule
- * @interval: interval in seconds between scan iterations
- * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop
- * @reserved: for alignment and future use
- */
-struct iwl_scan_umac_schedule {
- __le16 interval;
- u8 iter_count;
- u8 reserved;
-} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */
-
-/**
- * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command
- * parameters following channels configuration array.
- * @schedule: two scheduling plans.
- * @delay: delay in TUs before starting the first scan iteration
- * @reserved: for future use and alignment
- * @preq: probe request with IEs blocks
- * @direct_scan: list of SSIDs for directed active scan
- */
-struct iwl_scan_req_umac_tail {
- /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */
- struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS];
- __le16 delay;
- __le16 reserved;
- /* SCAN_PROBE_PARAMS_API_S_VER_1 */
- struct iwl_scan_probe_req preq;
- struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
-} __packed;
-
-/**
- * struct iwl_scan_req_umac
- * @flags: &enum iwl_umac_scan_flags
- * @uid: scan id, &enum iwl_umac_scan_uid_offsets
- * @ooc_priority: out of channel priority - &enum iwl_scan_priority
- * @general_flags: &enum iwl_umac_scan_general_flags
- * @reserved1: for future use and alignment
- * @active_dwell: dwell time for active scan
- * @passive_dwell: dwell time for passive scan
- * @fragmented_dwell: dwell time for fragmented passive scan
- * @max_out_time: max out of serving channel time
- * @suspend_time: max suspend time
- * @scan_priority: scan internal prioritization &enum iwl_scan_priority
- * @channel_flags: &enum iwl_scan_channel_flags
- * @n_channels: num of channels in scan request
- * @reserved2: for future use and alignment
- * @data: &struct iwl_scan_channel_cfg_umac and
- * &struct iwl_scan_req_umac_tail
- */
-struct iwl_scan_req_umac {
- __le32 flags;
- __le32 uid;
- __le32 ooc_priority;
- /* SCAN_GENERAL_PARAMS_API_S_VER_1 */
- __le32 general_flags;
- u8 reserved1;
- u8 active_dwell;
- u8 passive_dwell;
- u8 fragmented_dwell;
- __le32 max_out_time;
- __le32 suspend_time;
- __le32 scan_priority;
- /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
- u8 channel_flags;
- u8 n_channels;
- __le16 reserved2;
- u8 data[];
-} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
-
-/**
- * struct iwl_umac_scan_abort
- * @uid: scan id, &enum iwl_umac_scan_uid_offsets
- * @flags: reserved
- */
-struct iwl_umac_scan_abort {
- __le32 uid;
- __le32 flags;
-} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */
-
-/**
- * struct iwl_umac_scan_complete
- * @uid: scan id, &enum iwl_umac_scan_uid_offsets
- * @last_schedule: last scheduling line
- * @last_iter: last scan iteration number
- * @scan status: &enum iwl_scan_offload_complete_status
- * @ebs_status: &enum iwl_scan_ebs_status
- * @time_from_last_iter: time elapsed from last iteration
- * @reserved: for future use
- */
-struct iwl_umac_scan_complete {
- __le32 uid;
- u8 last_schedule;
- u8 last_iter;
- u8 status;
- u8 ebs_status;
- __le32 time_from_last_iter;
- __le32 reserved;
-} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */
-
-#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5
-/**
- * struct iwl_scan_offload_profile_match - match information
- * @bssid: matched bssid
- * @channel: channel where the match occurred
- * @energy:
- * @matching_feature:
- * @matching_channels: bitmap of channels that matched, referencing
- * the channels passed in tue scan offload request
- */
-struct iwl_scan_offload_profile_match {
- u8 bssid[ETH_ALEN];
- __le16 reserved;
- u8 channel;
- u8 energy;
- u8 matching_feature;
- u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN];
-} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */
-
-/**
- * struct iwl_scan_offload_profiles_query - match results query response
- * @matched_profiles: bitmap of matched profiles, referencing the
- * matches passed in the scan offload request
- * @last_scan_age: age of the last offloaded scan
- * @n_scans_done: number of offloaded scans done
- * @gp2_d0u: GP2 when D0U occurred
- * @gp2_invoked: GP2 when scan offload was invoked
- * @resume_while_scanning: not used
- * @self_recovery: obsolete
- * @reserved: reserved
- * @matches: array of match information, one for each match
- */
-struct iwl_scan_offload_profiles_query {
- __le32 matched_profiles;
- __le32 last_scan_age;
- __le32 n_scans_done;
- __le32 gp2_d0u;
- __le32 gp2_invoked;
- u8 resume_while_scanning;
- u8 self_recovery;
- __le16 reserved;
- struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
-} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */
-
-/**
- * struct iwl_umac_scan_iter_complete_notif - notifies end of scanning iteration
- * @uid: scan id, &enum iwl_umac_scan_uid_offsets
- * @scanned_channels: number of channels scanned and number of valid elements in
- * results array
- * @status: one of SCAN_COMP_STATUS_*
- * @bt_status: BT on/off status
- * @last_channel: last channel that was scanned
- * @tsf_low: TSF timer (lower half) in usecs
- * @tsf_high: TSF timer (higher half) in usecs
- * @results: array of scan results, only "scanned_channels" of them are valid
- */
-struct iwl_umac_scan_iter_complete_notif {
- __le32 uid;
- u8 scanned_channels;
- u8 status;
- u8 bt_status;
- u8 last_channel;
- __le32 tsf_low;
- __le32 tsf_high;
- struct iwl_scan_results_notif results[];
-} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_1 */
-
-#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
deleted file mode 100644
index 493a8bdfbc9e..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
+++ /dev/null
@@ -1,414 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __fw_api_sta_h__
-#define __fw_api_sta_h__
-
-/**
- * enum iwl_sta_flags - flags for the ADD_STA host command
- * @STA_FLG_REDUCED_TX_PWR_CTRL:
- * @STA_FLG_REDUCED_TX_PWR_DATA:
- * @STA_FLG_DISABLE_TX: set if TX should be disabled
- * @STA_FLG_PS: set if STA is in Power Save
- * @STA_FLG_INVALID: set if STA is invalid
- * @STA_FLG_DLP_EN: Direct Link Protocol is enabled
- * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs
- * @STA_FLG_DRAIN_FLOW: drain flow
- * @STA_FLG_PAN: STA is for PAN interface
- * @STA_FLG_CLASS_AUTH:
- * @STA_FLG_CLASS_ASSOC:
- * @STA_FLG_CLASS_MIMO_PROT:
- * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU
- * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation
- * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is
- * initialised by driver and can be updated by fw upon reception of
- * action frames that can change the channel width. When cleared the fw
- * will send all the frames in 20MHz even when FAT channel is requested.
- * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the
- * driver and can be updated by fw upon reception of action frames.
- * @STA_FLG_MFP_EN: Management Frame Protection
- */
-enum iwl_sta_flags {
- STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3),
- STA_FLG_REDUCED_TX_PWR_DATA = BIT(6),
-
- STA_FLG_DISABLE_TX = BIT(4),
-
- STA_FLG_PS = BIT(8),
- STA_FLG_DRAIN_FLOW = BIT(12),
- STA_FLG_PAN = BIT(13),
- STA_FLG_CLASS_AUTH = BIT(14),
- STA_FLG_CLASS_ASSOC = BIT(15),
- STA_FLG_RTS_MIMO_PROT = BIT(17),
-
- STA_FLG_MAX_AGG_SIZE_SHIFT = 19,
- STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
- STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
-
- STA_FLG_AGG_MPDU_DENS_SHIFT = 23,
- STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT),
- STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT),
- STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT),
- STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
- STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
-
- STA_FLG_FAT_EN_20MHZ = (0 << 26),
- STA_FLG_FAT_EN_40MHZ = (1 << 26),
- STA_FLG_FAT_EN_80MHZ = (2 << 26),
- STA_FLG_FAT_EN_160MHZ = (3 << 26),
- STA_FLG_FAT_EN_MSK = (3 << 26),
-
- STA_FLG_MIMO_EN_SISO = (0 << 28),
- STA_FLG_MIMO_EN_MIMO2 = (1 << 28),
- STA_FLG_MIMO_EN_MIMO3 = (2 << 28),
- STA_FLG_MIMO_EN_MSK = (3 << 28),
-};
-
-/**
- * enum iwl_sta_key_flag - key flags for the ADD_STA host command
- * @STA_KEY_FLG_NO_ENC: no encryption
- * @STA_KEY_FLG_WEP: WEP encryption algorithm
- * @STA_KEY_FLG_CCM: CCMP encryption algorithm
- * @STA_KEY_FLG_TKIP: TKIP encryption algorithm
- * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support)
- * @STA_KEY_FLG_CMAC: CMAC encryption algorithm
- * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm
- * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value
- * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
- * station info array (1 - n 1X mode)
- * @STA_KEY_FLG_KEYID_MSK: the index of the key
- * @STA_KEY_NOT_VALID: key is invalid
- * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key
- * @STA_KEY_MULTICAST: set for multical key
- * @STA_KEY_MFP: key is used for Management Frame Protection
- */
-enum iwl_sta_key_flag {
- STA_KEY_FLG_NO_ENC = (0 << 0),
- STA_KEY_FLG_WEP = (1 << 0),
- STA_KEY_FLG_CCM = (2 << 0),
- STA_KEY_FLG_TKIP = (3 << 0),
- STA_KEY_FLG_EXT = (4 << 0),
- STA_KEY_FLG_CMAC = (6 << 0),
- STA_KEY_FLG_ENC_UNKNOWN = (7 << 0),
- STA_KEY_FLG_EN_MSK = (7 << 0),
-
- STA_KEY_FLG_WEP_KEY_MAP = BIT(3),
- STA_KEY_FLG_KEYID_POS = 8,
- STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS),
- STA_KEY_NOT_VALID = BIT(11),
- STA_KEY_FLG_WEP_13BYTES = BIT(12),
- STA_KEY_MULTICAST = BIT(14),
- STA_KEY_MFP = BIT(15),
-};
-
-/**
- * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed
- * @STA_MODIFY_KEY: this command modifies %key
- * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx
- * @STA_MODIFY_TX_RATE: unused
- * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid
- * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid
- * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count
- * @STA_MODIFY_PROT_TH:
- * @STA_MODIFY_QUEUES: modify the queues used by this station
- */
-enum iwl_sta_modify_flag {
- STA_MODIFY_KEY = BIT(0),
- STA_MODIFY_TID_DISABLE_TX = BIT(1),
- STA_MODIFY_TX_RATE = BIT(2),
- STA_MODIFY_ADD_BA_TID = BIT(3),
- STA_MODIFY_REMOVE_BA_TID = BIT(4),
- STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5),
- STA_MODIFY_PROT_TH = BIT(6),
- STA_MODIFY_QUEUES = BIT(7),
-};
-
-#define STA_MODE_MODIFY 1
-
-/**
- * enum iwl_sta_sleep_flag - type of sleep of the station
- * @STA_SLEEP_STATE_AWAKE:
- * @STA_SLEEP_STATE_PS_POLL:
- * @STA_SLEEP_STATE_UAPSD:
- * @STA_SLEEP_STATE_MOREDATA: set more-data bit on
- * (last) released frame
- */
-enum iwl_sta_sleep_flag {
- STA_SLEEP_STATE_AWAKE = 0,
- STA_SLEEP_STATE_PS_POLL = BIT(0),
- STA_SLEEP_STATE_UAPSD = BIT(1),
- STA_SLEEP_STATE_MOREDATA = BIT(2),
-};
-
-/* STA ID and color bits definitions */
-#define STA_ID_SEED (0x0f)
-#define STA_ID_POS (0)
-#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS)
-
-#define STA_COLOR_SEED (0x7)
-#define STA_COLOR_POS (4)
-#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS)
-
-#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \
- (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
-#define STA_ID_N_COLOR_GET_ID(id_n_color) \
- (((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
-
-#define STA_KEY_MAX_NUM (16)
-#define STA_KEY_IDX_INVALID (0xff)
-#define STA_KEY_MAX_DATA_KEY_NUM (4)
-#define IWL_MAX_GLOBAL_KEYS (4)
-#define STA_KEY_LEN_WEP40 (5)
-#define STA_KEY_LEN_WEP104 (13)
-
-/**
- * struct iwl_mvm_keyinfo - key information
- * @key_flags: type %iwl_sta_key_flag
- * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
- * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
- * @key_offset: key offset in the fw's key table
- * @key: 16-byte unicast decryption key
- * @tx_secur_seq_cnt: initial RSC / PN needed for replay check
- * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only
- * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only
- */
-struct iwl_mvm_keyinfo {
- __le16 key_flags;
- u8 tkip_rx_tsc_byte2;
- u8 reserved1;
- __le16 tkip_rx_ttak[5];
- u8 key_offset;
- u8 reserved2;
- u8 key[16];
- __le64 tx_secur_seq_cnt;
- __le64 hw_tkip_mic_rx_key;
- __le64 hw_tkip_mic_tx_key;
-} __packed;
-
-/**
- * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table.
- * ( REPLY_ADD_STA = 0x18 )
- * @add_modify: 1: modify existing, 0: add new station
- * @awake_acs:
- * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
- * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
- * @mac_id_n_color: the Mac context this station belongs to
- * @addr[ETH_ALEN]: station's MAC address
- * @sta_id: index of station in uCode's station table
- * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave
- * alone. 1 - modify, 0 - don't change.
- * @station_flags: look at %iwl_sta_flags
- * @station_flags_msk: what of %station_flags have changed
- * @add_immediate_ba_tid: tid for which to add block-ack support (Rx)
- * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set
- * add_immediate_ba_ssn.
- * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx)
- * Set %STA_MODIFY_REMOVE_BA_TID to use this field
- * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with
- * add_immediate_ba_tid.
- * @sleep_tx_count: number of packets to transmit to station even though it is
- * asleep. Used to synchronise PS-poll and u-APSD responses while ucode
- * keeps track of STA sleep state.
- * @sleep_state_flags: Look at %iwl_sta_sleep_flag.
- * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP
- * mac-addr.
- * @beamform_flags: beam forming controls
- * @tfd_queue_msk: tfd queues used by this station
- *
- * The device contains an internal table of per-station information, with info
- * on security keys, aggregation parameters, and Tx rates for initial Tx
- * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD).
- *
- * ADD_STA sets up the table entry for one station, either creating a new
- * entry, or modifying a pre-existing one.
- */
-struct iwl_mvm_add_sta_cmd {
- u8 add_modify;
- u8 awake_acs;
- __le16 tid_disable_tx;
- __le32 mac_id_n_color;
- u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */
- __le16 reserved2;
- u8 sta_id;
- u8 modify_mask;
- __le16 reserved3;
- __le32 station_flags;
- __le32 station_flags_msk;
- u8 add_immediate_ba_tid;
- u8 remove_immediate_ba_tid;
- __le16 add_immediate_ba_ssn;
- __le16 sleep_tx_count;
- __le16 sleep_state_flags;
- __le16 assoc_id;
- __le16 beamform_flags;
- __le32 tfd_queue_msk;
-} __packed; /* ADD_STA_CMD_API_S_VER_7 */
-
-/**
- * struct iwl_mvm_add_sta_key_cmd - add/modify sta key
- * ( REPLY_ADD_STA_KEY = 0x17 )
- * @sta_id: index of station in uCode's station table
- * @key_offset: key offset in key storage
- * @key_flags: type %iwl_sta_key_flag
- * @key: key material data
- * @key2: key material data
- * @rx_secur_seq_cnt: RX security sequence counter for the key
- * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
- * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
- */
-struct iwl_mvm_add_sta_key_cmd {
- u8 sta_id;
- u8 key_offset;
- __le16 key_flags;
- u8 key[16];
- u8 key2[16];
- u8 rx_secur_seq_cnt[16];
- u8 tkip_rx_tsc_byte2;
- u8 reserved;
- __le16 tkip_rx_ttak[5];
-} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */
-
-/**
- * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command
- * @ADD_STA_SUCCESS: operation was executed successfully
- * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table
- * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session
- * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that
- * doesn't exist.
- */
-enum iwl_mvm_add_sta_rsp_status {
- ADD_STA_SUCCESS = 0x1,
- ADD_STA_STATIONS_OVERLOAD = 0x2,
- ADD_STA_IMMEDIATE_BA_FAILURE = 0x4,
- ADD_STA_MODIFY_NON_EXISTING_STA = 0x8,
-};
-
-/**
- * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table
- * ( REMOVE_STA = 0x19 )
- * @sta_id: the station id of the station to be removed
- */
-struct iwl_mvm_rm_sta_cmd {
- u8 sta_id;
- u8 reserved[3];
-} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */
-
-/**
- * struct iwl_mvm_mgmt_mcast_key_cmd
- * ( MGMT_MCAST_KEY = 0x1f )
- * @ctrl_flags: %iwl_sta_key_flag
- * @IGTK:
- * @K1: unused
- * @K2: unused
- * @sta_id: station ID that support IGTK
- * @key_id:
- * @receive_seq_cnt: initial RSC/PN needed for replay check
- */
-struct iwl_mvm_mgmt_mcast_key_cmd {
- __le32 ctrl_flags;
- u8 IGTK[16];
- u8 K1[16];
- u8 K2[16];
- __le32 key_id;
- __le32 sta_id;
- __le64 receive_seq_cnt;
-} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */
-
-struct iwl_mvm_wep_key {
- u8 key_index;
- u8 key_offset;
- __le16 reserved1;
- u8 key_size;
- u8 reserved2[3];
- u8 key[16];
-} __packed;
-
-struct iwl_mvm_wep_key_cmd {
- __le32 mac_id_n_color;
- u8 num_keys;
- u8 decryption_type;
- u8 flags;
- u8 reserved;
- struct iwl_mvm_wep_key wep_key[0];
-} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
-
-/**
- * struct iwl_mvm_eosp_notification - EOSP notification from firmware
- * @remain_frame_count: # of frames remaining, non-zero if SP was cut
- * short by GO absence
- * @sta_id: station ID
- */
-struct iwl_mvm_eosp_notification {
- __le32 remain_frame_count;
- __le32 sta_id;
-} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */
-
-#endif /* __fw_api_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h
deleted file mode 100644
index 0c321f63ee42..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h
+++ /dev/null
@@ -1,284 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __fw_api_stats_h__
-#define __fw_api_stats_h__
-#include "fw-api-mac.h"
-
-struct mvm_statistics_dbg {
- __le32 burst_check;
- __le32 burst_count;
- __le32 wait_for_silence_timeout_cnt;
- __le32 reserved[3];
-} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */
-
-struct mvm_statistics_div {
- __le32 tx_on_a;
- __le32 tx_on_b;
- __le32 exec_time;
- __le32 probe_time;
- __le32 rssi_ant;
- __le32 reserved2;
-} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
-
-struct mvm_statistics_rx_non_phy {
- __le32 bogus_cts; /* CTS received when not expecting CTS */
- __le32 bogus_ack; /* ACK received when not expecting ACK */
- __le32 non_bssid_frames; /* number of frames with BSSID that
- * doesn't belong to the STA BSSID */
- __le32 filtered_frames; /* count frames that were dumped in the
- * filtering process */
- __le32 non_channel_beacons; /* beacons with our bss id but not on
- * our serving channel */
- __le32 channel_beacons; /* beacons with our bss id and in our
- * serving channel */
- __le32 num_missed_bcon; /* number of missed beacons */
- __le32 adc_rx_saturation_time; /* count in 0.8us units the time the
- * ADC was in saturation */
- __le32 ina_detection_search_time;/* total time (in 0.8us) searched
- * for INA */
- __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */
- __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */
- __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */
- __le32 interference_data_flag; /* flag for interference data
- * availability. 1 when data is
- * available. */
- __le32 channel_load; /* counts RX Enable time in uSec */
- __le32 dsp_false_alarms; /* DSP false alarm (both OFDM
- * and CCK) counter */
- __le32 beacon_rssi_a;
- __le32 beacon_rssi_b;
- __le32 beacon_rssi_c;
- __le32 beacon_energy_a;
- __le32 beacon_energy_b;
- __le32 beacon_energy_c;
- __le32 num_bt_kills;
- __le32 mac_id;
- __le32 directed_data_mpdu;
-} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */
-
-struct mvm_statistics_rx_phy {
- __le32 ina_cnt;
- __le32 fina_cnt;
- __le32 plcp_err;
- __le32 crc32_err;
- __le32 overrun_err;
- __le32 early_overrun_err;
- __le32 crc32_good;
- __le32 false_alarm_cnt;
- __le32 fina_sync_err_cnt;
- __le32 sfd_timeout;
- __le32 fina_timeout;
- __le32 unresponded_rts;
- __le32 rxe_frame_lmt_overrun;
- __le32 sent_ack_cnt;
- __le32 sent_cts_cnt;
- __le32 sent_ba_rsp_cnt;
- __le32 dsp_self_kill;
- __le32 mh_format_err;
- __le32 re_acq_main_rssi_sum;
- __le32 reserved;
-} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */
-
-struct mvm_statistics_rx_ht_phy {
- __le32 plcp_err;
- __le32 overrun_err;
- __le32 early_overrun_err;
- __le32 crc32_good;
- __le32 crc32_err;
- __le32 mh_format_err;
- __le32 agg_crc32_good;
- __le32 agg_mpdu_cnt;
- __le32 agg_cnt;
- __le32 unsupport_mcs;
-} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */
-
-struct mvm_statistics_tx_non_phy {
- __le32 preamble_cnt;
- __le32 rx_detected_cnt;
- __le32 bt_prio_defer_cnt;
- __le32 bt_prio_kill_cnt;
- __le32 few_bytes_cnt;
- __le32 cts_timeout;
- __le32 ack_timeout;
- __le32 expected_ack_cnt;
- __le32 actual_ack_cnt;
- __le32 dump_msdu_cnt;
- __le32 burst_abort_next_frame_mismatch_cnt;
- __le32 burst_abort_missing_next_frame_cnt;
- __le32 cts_timeout_collision;
- __le32 ack_or_ba_timeout_collision;
-} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */
-
-#define MAX_CHAINS 3
-
-struct mvm_statistics_tx_non_phy_agg {
- __le32 ba_timeout;
- __le32 ba_reschedule_frames;
- __le32 scd_query_agg_frame_cnt;
- __le32 scd_query_no_agg;
- __le32 scd_query_agg;
- __le32 scd_query_mismatch;
- __le32 frame_not_ready;
- __le32 underrun;
- __le32 bt_prio_kill;
- __le32 rx_ba_rsp_cnt;
- __s8 txpower[MAX_CHAINS];
- __s8 reserved;
- __le32 reserved2;
-} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */
-
-struct mvm_statistics_tx_channel_width {
- __le32 ext_cca_narrow_ch20[1];
- __le32 ext_cca_narrow_ch40[2];
- __le32 ext_cca_narrow_ch80[3];
- __le32 ext_cca_narrow_ch160[4];
- __le32 last_tx_ch_width_indx;
- __le32 rx_detected_per_ch_width[4];
- __le32 success_per_ch_width[4];
- __le32 fail_per_ch_width[4];
-}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */
-
-struct mvm_statistics_tx {
- struct mvm_statistics_tx_non_phy general;
- struct mvm_statistics_tx_non_phy_agg agg;
- struct mvm_statistics_tx_channel_width channel_width;
-} __packed; /* STATISTICS_TX_API_S_VER_4 */
-
-
-struct mvm_statistics_bt_activity {
- __le32 hi_priority_tx_req_cnt;
- __le32 hi_priority_tx_denied_cnt;
- __le32 lo_priority_tx_req_cnt;
- __le32 lo_priority_tx_denied_cnt;
- __le32 hi_priority_rx_req_cnt;
- __le32 hi_priority_rx_denied_cnt;
- __le32 lo_priority_rx_req_cnt;
- __le32 lo_priority_rx_denied_cnt;
-} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
-
-struct mvm_statistics_general_v8 {
- __le32 radio_temperature;
- __le32 radio_voltage;
- struct mvm_statistics_dbg dbg;
- __le32 sleep_time;
- __le32 slots_out;
- __le32 slots_idle;
- __le32 ttl_timestamp;
- struct mvm_statistics_div slow_div;
- __le32 rx_enable_counter;
- /*
- * num_of_sos_states:
- * count the number of times we have to re-tune
- * in order to get out of bad PHY status
- */
- __le32 num_of_sos_states;
- __le32 beacon_filtered;
- __le32 missed_beacons;
- u8 beacon_filter_average_energy;
- u8 beacon_filter_reason;
- u8 beacon_filter_current_energy;
- u8 beacon_filter_reserved;
- __le32 beacon_filter_delta_time;
- struct mvm_statistics_bt_activity bt_activity;
- __le64 rx_time;
- __le64 on_time_rf;
- __le64 on_time_scan;
- __le64 tx_time;
- __le32 beacon_counter[NUM_MAC_INDEX];
- u8 beacon_average_energy[NUM_MAC_INDEX];
- u8 reserved[4 - (NUM_MAC_INDEX % 4)];
-} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */
-
-struct mvm_statistics_rx {
- struct mvm_statistics_rx_phy ofdm;
- struct mvm_statistics_rx_phy cck;
- struct mvm_statistics_rx_non_phy general;
- struct mvm_statistics_rx_ht_phy ofdm_ht;
-} __packed; /* STATISTICS_RX_API_S_VER_3 */
-
-/*
- * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
- *
- * By default, uCode issues this notification after receiving a beacon
- * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
- * STATISTICS_CMD (0x9c), below.
- */
-
-struct iwl_notif_statistics_v10 {
- __le32 flag;
- struct mvm_statistics_rx rx;
- struct mvm_statistics_tx tx;
- struct mvm_statistics_general_v8 general;
-} __packed; /* STATISTICS_NTFY_API_S_VER_10 */
-
-#define IWL_STATISTICS_FLG_CLEAR 0x1
-#define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2
-
-struct iwl_statistics_cmd {
- __le32 flags;
-} __packed; /* STATISTICS_CMD_API_S_VER_1 */
-
-#endif /* __fw_api_stats_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h
deleted file mode 100644
index eed6271d01a3..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h
+++ /dev/null
@@ -1,386 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __fw_api_tof_h__
-#define __fw_api_tof_h__
-
-#include "fw-api.h"
-
-/* ToF sub-group command IDs */
-enum iwl_mvm_tof_sub_grp_ids {
- TOF_RANGE_REQ_CMD = 0x1,
- TOF_CONFIG_CMD = 0x2,
- TOF_RANGE_ABORT_CMD = 0x3,
- TOF_RANGE_REQ_EXT_CMD = 0x4,
- TOF_RESPONDER_CONFIG_CMD = 0x5,
- TOF_NW_INITIATED_RES_SEND_CMD = 0x6,
- TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7,
- TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC,
- TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD,
- TOF_RANGE_RESPONSE_NOTIF = 0xFE,
- TOF_MCSI_DEBUG_NOTIF = 0xFB,
-};
-
-/**
- * struct iwl_tof_config_cmd - ToF configuration
- * @tof_disabled: 0 enabled, 1 - disabled
- * @one_sided_disabled: 0 enabled, 1 - disabled
- * @is_debug_mode: 1 debug mode, 0 - otherwise
- * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise
- */
-struct iwl_tof_config_cmd {
- __le32 sub_grp_cmd_id;
- u8 tof_disabled;
- u8 one_sided_disabled;
- u8 is_debug_mode;
- u8 is_buf_required;
-} __packed;
-
-/**
- * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug)
- * @burst_period: future use: (currently hard coded in the LMAC)
- * The interval between two sequential bursts.
- * @min_delta_ftm: future use: (currently hard coded in the LMAC)
- * The minimum delay between two sequential FTM Responses
- * in the same burst.
- * @burst_duration: future use: (currently hard coded in the LMAC)
- * The total time for all FTMs handshake in the same burst.
- * Affect the time events duration in the LMAC.
- * @num_of_burst_exp: future use: (currently hard coded in the LMAC)
- * The number of bursts for the current ToF request. Affect
- * the number of events allocations in the current iteration.
- * @get_ch_est: for xVT only, NA for driver
- * @abort_responder: when set to '1' - Responder will terminate its activity
- * (all other fields in the command are ignored)
- * @recv_sta_req_params: 1 - Responder will ignore the other Responder's
- * params and use the recomended Initiator params.
- * 0 - otherwise
- * @channel_num: current AP Channel
- * @bandwidth: current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz
- * @rate: current AP rate
- * @ctrl_ch_position: coding of the control channel position relative to
- * the center frequency.
- * 40MHz 0 below center, 1 above center
- * 80MHz bits [0..1]: 0 the near 20MHz to the center,
- * 1 the far 20MHz to the center
- * bit[2] as above 40MHz
- * @ftm_per_burst: FTMs per Burst
- * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response,
- * '1' - we measure over the Initial FTM Response
- * @asap_mode: ASAP / Non ASAP mode for the current WLS station
- * @sta_id: index of the AP STA when in AP mode
- * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF
- * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug
- * purposes, simulating station movement by adding various values
- * to this field
- * @bssid: Current AP BSSID
- */
-struct iwl_tof_responder_config_cmd {
- __le32 sub_grp_cmd_id;
- __le16 burst_period;
- u8 min_delta_ftm;
- u8 burst_duration;
- u8 num_of_burst_exp;
- u8 get_ch_est;
- u8 abort_responder;
- u8 recv_sta_req_params;
- u8 channel_num;
- u8 bandwidth;
- u8 rate;
- u8 ctrl_ch_position;
- u8 ftm_per_burst;
- u8 ftm_resp_ts_avail;
- u8 asap_mode;
- u8 sta_id;
- __le16 tsf_timer_offset_msecs;
- __le16 toa_offset;
- u8 bssid[ETH_ALEN];
-} __packed;
-
-/**
- * struct iwl_tof_range_request_ext_cmd - extended range req for WLS
- * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF
- * @min_delta_ftm: Minimal time between two consecutive measurements,
- * in units of 100us. 0 means no preference by station
- * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended
- * value be sent to the AP
- * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended
- * value to be sent to the AP
- * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended
- * value to be sent to the AP
- */
-struct iwl_tof_range_req_ext_cmd {
- __le32 sub_grp_cmd_id;
- __le16 tsf_timer_offset_msec;
- __le16 reserved;
- u8 min_delta_ftm;
- u8 ftm_format_and_bw20M;
- u8 ftm_format_and_bw40M;
- u8 ftm_format_and_bw80M;
-} __packed;
-
-#define IWL_MVM_TOF_MAX_APS 21
-
-/**
- * struct iwl_tof_range_req_ap_entry - AP configuration parameters
- * @channel_num: Current AP Channel
- * @bandwidth: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz
- * @tsf_delta_direction: TSF relatively to the subject AP
- * @ctrl_ch_position: Coding of the control channel position relative to the
- * center frequency.
- * 40MHz 0 below center, 1 above center
- * 80MHz bits [0..1]: 0 the near 20MHz to the center,
- * 1 the far 20MHz to the center
- * bit[2] as above 40MHz
- * @bssid: AP's bss id
- * @measure_type: Measurement type: 0 - two sided, 1 - One sided
- * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of the
- * number of measurement iterations (min 2^0 = 1, max 2^14)
- * @burst_period: Recommended value to be sent to the AP. Measurement
- * periodicity In units of 100ms. ignored if num_of_bursts = 0
- * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31)
- * 1-sided: how many rts/cts pairs should be used per burst.
- * @retries_per_sample: Max number of retries that the LMAC should send
- * in case of no replies by the AP.
- * @tsf_delta: TSF Delta in units of microseconds.
- * The difference between the AP TSF and the device local clock.
- * @location_req: Location Request Bit[0] LCI should be sent in the FTMR
- * Bit[1] Civic should be sent in the FTMR
- * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided)
- * @enable_dyn_ack: Enable Dynamic ACK BW.
- * 0 Initiator interact with regular AP
- * 1 Initiator interact with Responder machine: need to send the
- * Initiator Acks with HT 40MHz / 80MHz, since the Responder should
- * use it for its ch est measurement (this flag will be set when we
- * configure the opposite machine to be Responder).
- * @rssi: Last received value
- * leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value.
- */
-struct iwl_tof_range_req_ap_entry {
- u8 channel_num;
- u8 bandwidth;
- u8 tsf_delta_direction;
- u8 ctrl_ch_position;
- u8 bssid[ETH_ALEN];
- u8 measure_type;
- u8 num_of_bursts;
- __le16 burst_period;
- u8 samples_per_burst;
- u8 retries_per_sample;
- __le32 tsf_delta;
- u8 location_req;
- u8 asap_mode;
- u8 enable_dyn_ack;
- s8 rssi;
-} __packed;
-
-/**
- * enum iwl_tof_response_mode
- * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as
- * possible (not supported for this release)
- * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon
- * timeout expiration
- * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the
- * earlier of: measurements completion / timeout
- * expiration.
- */
-enum iwl_tof_response_mode {
- IWL_MVM_TOF_RESPOSE_ASAP = 1,
- IWL_MVM_TOF_RESPOSE_TIMEOUT,
- IWL_MVM_TOF_RESPOSE_COMPLETE,
-};
-
-/**
- * struct iwl_tof_range_req_cmd - start measurement cmd
- * @request_id: A Token incremented per request. The same Token will be
- * sent back in the range response
- * @initiator: 0- NW initiated, 1 - Client Initiated
- * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided,
- * '1' - run ML-Algo for ToF only
- * @req_timeout: Requested timeout of the response in units of 100ms.
- * This is equivalent to the session time configured to the
- * LMAC in Initiator Request
- * @report_policy: Supported partially for this release: For current release -
- * the range report will be uploaded as a batch when ready or
- * when the session is done (successfully / partially).
- * one of iwl_tof_response_mode.
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
- * @macaddr_random: '0' Use default source MAC address (i.e. p2_p),
- * '1' Use MAC Address randomization according to the below
- * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
- * Bits set to 1 shall be randomized by the UMAC
- */
-struct iwl_tof_range_req_cmd {
- __le32 sub_grp_cmd_id;
- u8 request_id;
- u8 initiator;
- u8 one_sided_los_disable;
- u8 req_timeout;
- u8 report_policy;
- u8 los_det_disable;
- u8 num_of_ap;
- u8 macaddr_random;
- u8 macaddr_template[ETH_ALEN];
- u8 macaddr_mask[ETH_ALEN];
- struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS];
-} __packed;
-
-/**
- * struct iwl_tof_gen_resp_cmd - generic ToF response
- */
-struct iwl_tof_gen_resp_cmd {
- __le32 sub_grp_cmd_id;
- u8 data[];
-} __packed;
-
-/**
- * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response)
- * @measure_status: current APs measurement status
- * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz
- * @rtt: The Round Trip Time that took for the last measurement for
- * current AP [nSec]
- * @rtt_variance: The Variance of the RTT values measured for current AP
- * @rtt_spread: The Difference between the maximum and the minimum RTT
- * values measured for current AP in the current session [nsec]
- * @rssi: RSSI as uploaded in the Channel Estimation notification
- * @rssi_spread: The Difference between the maximum and the minimum RSSI values
- * measured for current AP in the current session
- * @range: Measured range [cm]
- * @range_variance: Measured range variance [cm]
- * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was
- * uploaded by the LMAC
- */
-struct iwl_tof_range_rsp_ap_entry_ntfy {
- u8 bssid[ETH_ALEN];
- u8 measure_status;
- u8 measure_bw;
- __le32 rtt;
- __le32 rtt_variance;
- __le32 rtt_spread;
- s8 rssi;
- u8 rssi_spread;
- __le16 reserved;
- __le32 range;
- __le32 range_variance;
- __le32 timestamp;
-} __packed;
-
-/**
- * struct iwl_tof_range_rsp_ntfy -
- * @request_id: A Token ID of the corresponding Range request
- * @request_status: status of current measurement session
- * @last_in_batch: reprot policy (when not all responses are uploaded at once)
- * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
- */
-struct iwl_tof_range_rsp_ntfy {
- u8 request_id;
- u8 request_status;
- u8 last_in_batch;
- u8 num_of_aps;
- struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS];
-} __packed;
-
-#define IWL_MVM_TOF_MCSI_BUF_SIZE (245)
-/**
- * struct iwl_tof_mcsi_notif - used for debug
- * @token: token ID for the current session
- * @role: '0' - initiator, '1' - responder
- * @initiator_bssid: initiator machine
- * @responder_bssid: responder machine
- * @mcsi_buffer: debug data
- */
-struct iwl_tof_mcsi_notif {
- u8 token;
- u8 role;
- __le16 reserved;
- u8 initiator_bssid[ETH_ALEN];
- u8 responder_bssid[ETH_ALEN];
- u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4];
-} __packed;
-
-/**
- * struct iwl_tof_neighbor_report_notif
- * @bssid: BSSID of the AP which sent the report
- * @request_token: same token as the corresponding request
- * @status:
- * @report_ie_len: the length of the response frame starting from the Element ID
- * @data: the IEs
- */
-struct iwl_tof_neighbor_report {
- u8 bssid[ETH_ALEN];
- u8 request_token;
- u8 status;
- __le16 report_ie_len;
- u8 data[];
-} __packed;
-
-/**
- * struct iwl_tof_range_abort_cmd
- * @request_id: corresponds to a range request
- */
-struct iwl_tof_range_abort_cmd {
- __le32 sub_grp_cmd_id;
- u8 request_id;
- u8 reserved[3];
-} __packed;
-
-#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
deleted file mode 100644
index 853698ab8b05..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
+++ /dev/null
@@ -1,646 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef __fw_api_tx_h__
-#define __fw_api_tx_h__
-
-/**
- * enum iwl_tx_flags - bitmasks for tx_flags in TX command
- * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame
- * @TX_CMD_FLG_WRITE_TX_POWER: update current tx power value in the mgmt frame
- * @TX_CMD_FLG_ACK: expect ACK from receiving station
- * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
- * Otherwise, use rate_n_flags from the TX command
- * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
- * Must set TX_CMD_FLG_ACK with this flag.
- * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
- * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
- * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
- * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored
- * on old firmwares).
- * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame
- * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
- * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
- * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
- * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
- * Should be set for beacons and probe responses
- * @TX_CMD_FLG_CALIB: activate PA TX power calibrations
- * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
- * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
- * Should be set for 26/30 length MAC headers
- * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
- * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
- * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
- * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
- * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
- * @TX_CMD_FLG_EXEC_PAPD: execute PAPD
- * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power
- * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk
- */
-enum iwl_tx_flags {
- TX_CMD_FLG_PROT_REQUIRE = BIT(0),
- TX_CMD_FLG_WRITE_TX_POWER = BIT(1),
- TX_CMD_FLG_ACK = BIT(3),
- TX_CMD_FLG_STA_RATE = BIT(4),
- TX_CMD_FLG_BAR = BIT(6),
- TX_CMD_FLG_TXOP_PROT = BIT(7),
- TX_CMD_FLG_VHT_NDPA = BIT(8),
- TX_CMD_FLG_HT_NDPA = BIT(9),
- TX_CMD_FLG_CSI_FDBK2HOST = BIT(10),
- TX_CMD_FLG_BT_PRIO_POS = 11,
- TX_CMD_FLG_BT_DIS = BIT(12),
- TX_CMD_FLG_SEQ_CTL = BIT(13),
- TX_CMD_FLG_MORE_FRAG = BIT(14),
- TX_CMD_FLG_TSF = BIT(16),
- TX_CMD_FLG_CALIB = BIT(17),
- TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18),
- TX_CMD_FLG_MH_PAD = BIT(20),
- TX_CMD_FLG_RESP_TO_DRV = BIT(21),
- TX_CMD_FLG_CCMP_AGG = BIT(22),
- TX_CMD_FLG_TKIP_MIC_DONE = BIT(23),
- TX_CMD_FLG_DUR = BIT(25),
- TX_CMD_FLG_FW_DROP = BIT(26),
- TX_CMD_FLG_EXEC_PAPD = BIT(27),
- TX_CMD_FLG_PAPD_TYPE = BIT(28),
- TX_CMD_FLG_HCCA_CHUNK = BIT(31)
-}; /* TX_FLAGS_BITS_API_S_VER_1 */
-
-/**
- * enum iwl_tx_pm_timeouts - pm timeout values in TX command
- * @PM_FRAME_NONE: no need to suspend sleep mode
- * @PM_FRAME_MGMT: fw suspend sleep mode for 100TU
- * @PM_FRAME_ASSOC: fw suspend sleep mode for 10sec
- */
-enum iwl_tx_pm_timeouts {
- PM_FRAME_NONE = 0,
- PM_FRAME_MGMT = 2,
- PM_FRAME_ASSOC = 3,
-};
-
-/*
- * TX command security control
- */
-#define TX_CMD_SEC_WEP 0x01
-#define TX_CMD_SEC_CCM 0x02
-#define TX_CMD_SEC_TKIP 0x03
-#define TX_CMD_SEC_EXT 0x04
-#define TX_CMD_SEC_MSK 0x07
-#define TX_CMD_SEC_WEP_KEY_IDX_POS 6
-#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0
-#define TX_CMD_SEC_KEY128 0x08
-
-/* TODO: how does these values are OK with only 16 bit variable??? */
-/*
- * TX command next frame info
- *
- * bits 0:2 - security control (TX_CMD_SEC_*)
- * bit 3 - immediate ACK required
- * bit 4 - rate is taken from STA table
- * bit 5 - frame belongs to BA stream
- * bit 6 - immediate BA response expected
- * bit 7 - unused
- * bits 8:15 - Station ID
- * bits 16:31 - rate
- */
-#define TX_CMD_NEXT_FRAME_ACK_MSK (0x8)
-#define TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10)
-#define TX_CMD_NEXT_FRAME_BA_MSK (0x20)
-#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40)
-#define TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8)
-#define TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00)
-#define TX_CMD_NEXT_FRAME_STA_ID_POS (8)
-#define TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000)
-#define TX_CMD_NEXT_FRAME_RATE_POS (16)
-
-/*
- * TX command Frame life time in us - to be written in pm_frame_timeout
- */
-#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF
-#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/
-#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */
-#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0
-
-/*
- * TID for non QoS frames - to be written in tid_tspec
- */
-#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT
-
-/*
- * Limits on the retransmissions - to be written in {data,rts}_retry_limit
- */
-#define IWL_DEFAULT_TX_RETRY 15
-#define IWL_MGMT_DFAULT_RETRY_LIMIT 3
-#define IWL_RTS_DFAULT_RETRY_LIMIT 60
-#define IWL_BAR_DFAULT_RETRY_LIMIT 60
-#define IWL_LOW_RETRY_LIMIT 7
-
-/* TODO: complete documentation for try_cnt and btkill_cnt */
-/**
- * struct iwl_tx_cmd - TX command struct to FW
- * ( TX_CMD = 0x1c )
- * @len: in bytes of the payload, see below for details
- * @tx_flags: combination of TX_CMD_FLG_*
- * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
- * cleared. Combination of RATE_MCS_*
- * @sta_id: index of destination station in FW station table
- * @sec_ctl: security control, TX_CMD_SEC_*
- * @initial_rate_index: index into the the rate table for initial TX attempt.
- * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames.
- * @key: security key
- * @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_*
- * @life_time: frame life time (usecs??)
- * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt +
- * btkill_cnd + reserved), first 32 bits. "0" disables usage.
- * @dram_msb_ptr: upper bits of the scratch physical address
- * @rts_retry_limit: max attempts for RTS
- * @data_retry_limit: max attempts to send the data packet
- * @tid_spec: TID/tspec
- * @pm_frame_timeout: PM TX frame timeout
- *
- * The byte count (both len and next_frame_len) includes MAC header
- * (24/26/30/32 bytes)
- * + 2 bytes pad if 26/30 header size
- * + 8 byte IV for CCM or TKIP (not used for WEP)
- * + Data payload
- * + 8-byte MIC (not used for CCM/WEP)
- * It does not include post-MAC padding, i.e.,
- * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.
- * Range of len: 14-2342 bytes.
- *
- * After the struct fields the MAC header is placed, plus any padding,
- * and then the actial payload.
- */
-struct iwl_tx_cmd {
- __le16 len;
- __le16 next_frame_len;
- __le32 tx_flags;
- 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;
- u8 initial_rate_index;
- u8 reserved2;
- u8 key[16];
- __le32 reserved3;
- __le32 life_time;
- __le32 dram_lsb_ptr;
- u8 dram_msb_ptr;
- u8 rts_retry_limit;
- u8 data_retry_limit;
- u8 tid_tspec;
- __le16 pm_frame_timeout;
- __le16 reserved4;
- u8 payload[0];
- struct ieee80211_hdr hdr[0];
-} __packed; /* TX_CMD_API_S_VER_3 */
-
-/*
- * TX response related data
- */
-
-/*
- * enum iwl_tx_status - status that is returned by the fw after attempts to Tx
- * @TX_STATUS_SUCCESS:
- * @TX_STATUS_DIRECT_DONE:
- * @TX_STATUS_POSTPONE_DELAY:
- * @TX_STATUS_POSTPONE_FEW_BYTES:
- * @TX_STATUS_POSTPONE_BT_PRIO:
- * @TX_STATUS_POSTPONE_QUIET_PERIOD:
- * @TX_STATUS_POSTPONE_CALC_TTAK:
- * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
- * @TX_STATUS_FAIL_SHORT_LIMIT:
- * @TX_STATUS_FAIL_LONG_LIMIT:
- * @TX_STATUS_FAIL_UNDERRUN:
- * @TX_STATUS_FAIL_DRAIN_FLOW:
- * @TX_STATUS_FAIL_RFKILL_FLUSH:
- * @TX_STATUS_FAIL_LIFE_EXPIRE:
- * @TX_STATUS_FAIL_DEST_PS:
- * @TX_STATUS_FAIL_HOST_ABORTED:
- * @TX_STATUS_FAIL_BT_RETRY:
- * @TX_STATUS_FAIL_STA_INVALID:
- * @TX_TATUS_FAIL_FRAG_DROPPED:
- * @TX_STATUS_FAIL_TID_DISABLE:
- * @TX_STATUS_FAIL_FIFO_FLUSHED:
- * @TX_STATUS_FAIL_SMALL_CF_POLL:
- * @TX_STATUS_FAIL_FW_DROP:
- * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and
- * STA table
- * @TX_FRAME_STATUS_INTERNAL_ABORT:
- * @TX_MODE_MSK:
- * @TX_MODE_NO_BURST:
- * @TX_MODE_IN_BURST_SEQ:
- * @TX_MODE_FIRST_IN_BURST:
- * @TX_QUEUE_NUM_MSK:
- *
- * Valid only if frame_count =1
- * TODO: complete documentation
- */
-enum iwl_tx_status {
- TX_STATUS_MSK = 0x000000ff,
- TX_STATUS_SUCCESS = 0x01,
- TX_STATUS_DIRECT_DONE = 0x02,
- /* postpone TX */
- TX_STATUS_POSTPONE_DELAY = 0x40,
- TX_STATUS_POSTPONE_FEW_BYTES = 0x41,
- TX_STATUS_POSTPONE_BT_PRIO = 0x42,
- TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43,
- TX_STATUS_POSTPONE_CALC_TTAK = 0x44,
- /* abort TX */
- TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81,
- TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
- TX_STATUS_FAIL_LONG_LIMIT = 0x83,
- TX_STATUS_FAIL_UNDERRUN = 0x84,
- TX_STATUS_FAIL_DRAIN_FLOW = 0x85,
- TX_STATUS_FAIL_RFKILL_FLUSH = 0x86,
- TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
- TX_STATUS_FAIL_DEST_PS = 0x88,
- TX_STATUS_FAIL_HOST_ABORTED = 0x89,
- TX_STATUS_FAIL_BT_RETRY = 0x8a,
- TX_STATUS_FAIL_STA_INVALID = 0x8b,
- TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
- TX_STATUS_FAIL_TID_DISABLE = 0x8d,
- TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
- TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f,
- TX_STATUS_FAIL_FW_DROP = 0x90,
- TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91,
- TX_STATUS_INTERNAL_ABORT = 0x92,
- TX_MODE_MSK = 0x00000f00,
- TX_MODE_NO_BURST = 0x00000000,
- TX_MODE_IN_BURST_SEQ = 0x00000100,
- TX_MODE_FIRST_IN_BURST = 0x00000200,
- TX_QUEUE_NUM_MSK = 0x0001f000,
- TX_NARROW_BW_MSK = 0x00060000,
- TX_NARROW_BW_1DIV2 = 0x00020000,
- TX_NARROW_BW_1DIV4 = 0x00040000,
- TX_NARROW_BW_1DIV8 = 0x00060000,
-};
-
-/*
- * enum iwl_tx_agg_status - TX aggregation status
- * @AGG_TX_STATE_STATUS_MSK:
- * @AGG_TX_STATE_TRANSMITTED:
- * @AGG_TX_STATE_UNDERRUN:
- * @AGG_TX_STATE_BT_PRIO:
- * @AGG_TX_STATE_FEW_BYTES:
- * @AGG_TX_STATE_ABORT:
- * @AGG_TX_STATE_LAST_SENT_TTL:
- * @AGG_TX_STATE_LAST_SENT_TRY_CNT:
- * @AGG_TX_STATE_LAST_SENT_BT_KILL:
- * @AGG_TX_STATE_SCD_QUERY:
- * @AGG_TX_STATE_TEST_BAD_CRC32:
- * @AGG_TX_STATE_RESPONSE:
- * @AGG_TX_STATE_DUMP_TX:
- * @AGG_TX_STATE_DELAY_TX:
- * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries
- * occur if tx failed for this frame when it was a member of a previous
- * aggregation block). If rate scaling is used, retry count indicates the
- * rate table entry used for all frames in the new agg.
- *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for
- * this frame
- *
- * TODO: complete documentation
- */
-enum iwl_tx_agg_status {
- AGG_TX_STATE_STATUS_MSK = 0x00fff,
- AGG_TX_STATE_TRANSMITTED = 0x000,
- AGG_TX_STATE_UNDERRUN = 0x001,
- AGG_TX_STATE_BT_PRIO = 0x002,
- AGG_TX_STATE_FEW_BYTES = 0x004,
- AGG_TX_STATE_ABORT = 0x008,
- AGG_TX_STATE_LAST_SENT_TTL = 0x010,
- AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020,
- AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040,
- AGG_TX_STATE_SCD_QUERY = 0x080,
- AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100,
- AGG_TX_STATE_RESPONSE = 0x1ff,
- AGG_TX_STATE_DUMP_TX = 0x200,
- AGG_TX_STATE_DELAY_TX = 0x400,
- AGG_TX_STATE_TRY_CNT_POS = 12,
- AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS,
-};
-
-#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \
- AGG_TX_STATE_LAST_SENT_TRY_CNT| \
- AGG_TX_STATE_LAST_SENT_BT_KILL)
-
-/*
- * The mask below describes a status where we are absolutely sure that the MPDU
- * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've
- * written the bytes to the TXE, but we know nothing about what the DSP did.
- */
-#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \
- AGG_TX_STATE_ABORT | \
- AGG_TX_STATE_SCD_QUERY)
-
-/*
- * REPLY_TX = 0x1c (response)
- *
- * This response may be in one of two slightly different formats, indicated
- * by the frame_count field:
- *
- * 1) No aggregation (frame_count == 1). This reports Tx results for a single
- * frame. Multiple attempts, at various bit rates, may have been made for
- * this frame.
- *
- * 2) Aggregation (frame_count > 1). This reports Tx results for two or more
- * frames that used block-acknowledge. All frames were transmitted at
- * same rate. Rate scaling may have been used if first frame in this new
- * agg block failed in previous agg block(s).
- *
- * Note that, for aggregation, ACK (block-ack) status is not delivered
- * here; block-ack has not been received by the time the device records
- * this status.
- * This status relates to reasons the tx might have been blocked or aborted
- * within the device, rather than whether it was received successfully by
- * the destination station.
- */
-
-/**
- * struct agg_tx_status - per packet TX aggregation status
- * @status: enum iwl_tx_agg_status
- * @sequence: Sequence # for this frame's Tx cmd (not SSN!)
- */
-struct agg_tx_status {
- __le16 status;
- __le16 sequence;
-} __packed;
-
-/*
- * definitions for initial rate index field
- * bits [3:0] initial rate index
- * bits [6:4] rate table color, used for the initial rate
- * bit-7 invalid rate indication
- */
-#define TX_RES_INIT_RATE_INDEX_MSK 0x0f
-#define TX_RES_RATE_TABLE_COLOR_MSK 0x70
-#define TX_RES_INV_RATE_INDEX_MSK 0x80
-
-#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f)
-#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4)
-
-/**
- * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet
- * ( REPLY_TX = 0x1c )
- * @frame_count: 1 no aggregation, >1 aggregation
- * @bt_kill_count: num of times blocked by bluetooth (unused for agg)
- * @failure_rts: num of failures due to unsuccessful RTS
- * @failure_frame: num failures due to no ACK (unused for agg)
- * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the
- * Tx of all the batch. RATE_MCS_*
- * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK.
- * for agg: RTS + CTS + aggregation tx time + block-ack time.
- * in usec.
- * @pa_status: tx power info
- * @pa_integ_res_a: tx power info
- * @pa_integ_res_b: tx power info
- * @pa_integ_res_c: tx power info
- * @measurement_req_id: tx power info
- * @tfd_info: TFD information set by the FH
- * @seq_ctl: sequence control from the Tx cmd
- * @byte_cnt: byte count from the Tx cmd
- * @tlc_info: TLC rate info
- * @ra_tid: bits [3:0] = ra, bits [7:4] = tid
- * @frame_ctrl: frame control
- * @status: for non-agg: frame status TX_STATUS_*
- * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields
- * follow this one, up to frame_count.
- *
- * After the array of statuses comes the SSN of the SCD. Look at
- * %iwl_mvm_get_scd_ssn for more details.
- */
-struct iwl_mvm_tx_resp {
- u8 frame_count;
- u8 bt_kill_count;
- u8 failure_rts;
- u8 failure_frame;
- __le32 initial_rate;
- __le16 wireless_media_time;
-
- u8 pa_status;
- u8 pa_integ_res_a[3];
- u8 pa_integ_res_b[3];
- u8 pa_integ_res_c[3];
- __le16 measurement_req_id;
- u8 reduced_tpc;
- u8 reserved;
-
- __le32 tfd_info;
- __le16 seq_ctl;
- __le16 byte_cnt;
- u8 tlc_info;
- u8 ra_tid;
- __le16 frame_ctrl;
-
- struct agg_tx_status status;
-} __packed; /* TX_RSP_API_S_VER_3 */
-
-/**
- * struct iwl_mvm_ba_notif - notifies about reception of BA
- * ( BA_NOTIF = 0xc5 )
- * @sta_addr_lo32: lower 32 bits of the MAC address
- * @sta_addr_hi16: upper 16 bits of the MAC address
- * @sta_id: Index of recipient (BA-sending) station in fw's station table
- * @tid: tid of the session
- * @seq_ctl:
- * @bitmap: the bitmap of the BA notification as seen in the air
- * @scd_flow: the tx queue this BA relates to
- * @scd_ssn: the index of the last contiguously sent packet
- * @txed: number of Txed frames in this batch
- * @txed_2_done: number of Acked frames in this batch
- */
-struct iwl_mvm_ba_notif {
- __le32 sta_addr_lo32;
- __le16 sta_addr_hi16;
- __le16 reserved;
-
- u8 sta_id;
- u8 tid;
- __le16 seq_ctl;
- __le64 bitmap;
- __le16 scd_flow;
- __le16 scd_ssn;
- u8 txed;
- u8 txed_2_done;
- __le16 reserved1;
-} __packed;
-
-/*
- * struct iwl_mac_beacon_cmd - beacon template command
- * @tx: the tx commands associated with the beacon frame
- * @template_id: currently equal to the mac context id of the coresponding
- * mac.
- * @tim_idx: the offset of the tim IE in the beacon
- * @tim_size: the length of the tim IE
- * @frame: the template of the beacon frame
- */
-struct iwl_mac_beacon_cmd {
- struct iwl_tx_cmd tx;
- __le32 template_id;
- __le32 tim_idx;
- __le32 tim_size;
- struct ieee80211_hdr frame[0];
-} __packed;
-
-struct iwl_beacon_notif {
- struct iwl_mvm_tx_resp beacon_notify_hdr;
- __le64 tsf;
- __le32 ibss_mgr_status;
-} __packed;
-
-/**
- * struct iwl_extended_beacon_notif - notifies about beacon transmission
- * @beacon_notify_hdr: tx response command associated with the beacon
- * @tsf: last beacon tsf
- * @ibss_mgr_status: whether IBSS is manager
- * @gp2: last beacon time in gp2
- */
-struct iwl_extended_beacon_notif {
- struct iwl_mvm_tx_resp beacon_notify_hdr;
- __le64 tsf;
- __le32 ibss_mgr_status;
- __le32 gp2;
-} __packed; /* BEACON_NTFY_API_S_VER_5 */
-
-/**
- * enum iwl_dump_control - dump (flush) control flags
- * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
- * and the TFD queues are empty.
- */
-enum iwl_dump_control {
- DUMP_TX_FIFO_FLUSH = BIT(1),
-};
-
-/**
- * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command
- * @queues_ctl: bitmap of queues to flush
- * @flush_ctl: control flags
- * @reserved: reserved
- */
-struct iwl_tx_path_flush_cmd {
- __le32 queues_ctl;
- __le16 flush_ctl;
- __le16 reserved;
-} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */
-
-/**
- * iwl_mvm_get_scd_ssn - returns the SSN of the SCD
- * @tx_resp: the Tx response from the fw (agg or non-agg)
- *
- * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since
- * it can't know that everything will go well until the end of the AMPDU, it
- * can't know in advance the number of MPDUs that will be sent in the current
- * batch. This is why it writes the agg Tx response while it fetches the MPDUs.
- * Hence, it can't know in advance what the SSN of the SCD will be at the end
- * of the batch. This is why the SSN of the SCD is written at the end of the
- * whole struct at a variable offset. This function knows how to cope with the
- * variable offset and returns the SSN of the SCD.
- */
-static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp)
-{
- return le32_to_cpup((__le32 *)&tx_resp->status +
- tx_resp->frame_count) & 0xfff;
-}
-
-/**
- * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command
- * @token:
- * @sta_id: station id
- * @tid:
- * @scd_queue: scheduler queue to confiug
- * @enable: 1 queue enable, 0 queue disable
- * @aggregate: 1 aggregated queue, 0 otherwise
- * @tx_fifo: %enum iwl_mvm_tx_fifo
- * @window: BA window size
- * @ssn: SSN for the BA agreement
- */
-struct iwl_scd_txq_cfg_cmd {
- u8 token;
- u8 sta_id;
- u8 tid;
- u8 scd_queue;
- u8 enable;
- u8 aggregate;
- u8 tx_fifo;
- u8 window;
- __le16 ssn;
- __le16 reserved;
-} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */
-
-/**
- * struct iwl_scd_txq_cfg_rsp
- * @token: taken from the command
- * @sta_id: station id from the command
- * @tid: tid from the command
- * @scd_queue: scd_queue from the command
- */
-struct iwl_scd_txq_cfg_rsp {
- u8 token;
- u8 sta_id;
- u8 tid;
- u8 scd_queue;
-} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */
-
-#endif /* __fw_api_tx_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
deleted file mode 100644
index 181590fbd3b3..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ /dev/null
@@ -1,1773 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __fw_api_h__
-#define __fw_api_h__
-
-#include "fw-api-rs.h"
-#include "fw-api-rx.h"
-#include "fw-api-tx.h"
-#include "fw-api-sta.h"
-#include "fw-api-mac.h"
-#include "fw-api-power.h"
-#include "fw-api-d3.h"
-#include "fw-api-coex.h"
-#include "fw-api-scan.h"
-#include "fw-api-stats.h"
-#include "fw-api-tof.h"
-
-/* Tx queue numbers */
-enum {
- IWL_MVM_OFFCHANNEL_QUEUE = 8,
- IWL_MVM_CMD_QUEUE = 9,
-};
-
-enum iwl_mvm_tx_fifo {
- IWL_MVM_TX_FIFO_BK = 0,
- IWL_MVM_TX_FIFO_BE,
- IWL_MVM_TX_FIFO_VI,
- IWL_MVM_TX_FIFO_VO,
- IWL_MVM_TX_FIFO_MCAST = 5,
- IWL_MVM_TX_FIFO_CMD = 7,
-};
-
-#define IWL_MVM_STATION_COUNT 16
-
-#define IWL_MVM_TDLS_STA_COUNT 4
-
-/* commands */
-enum {
- MVM_ALIVE = 0x1,
- REPLY_ERROR = 0x2,
- ECHO_CMD = 0x3,
-
- INIT_COMPLETE_NOTIF = 0x4,
-
- /* PHY context commands */
- PHY_CONTEXT_CMD = 0x8,
- DBG_CFG = 0x9,
- ANTENNA_COUPLING_NOTIFICATION = 0xa,
-
- /* UMAC scan commands */
- SCAN_ITERATION_COMPLETE_UMAC = 0xb5,
- SCAN_CFG_CMD = 0xc,
- SCAN_REQ_UMAC = 0xd,
- SCAN_ABORT_UMAC = 0xe,
- SCAN_COMPLETE_UMAC = 0xf,
-
- /* station table */
- ADD_STA_KEY = 0x17,
- ADD_STA = 0x18,
- REMOVE_STA = 0x19,
-
- /* paging get item */
- FW_GET_ITEM_CMD = 0x1a,
-
- /* TX */
- TX_CMD = 0x1c,
- TXPATH_FLUSH = 0x1e,
- MGMT_MCAST_KEY = 0x1f,
-
- /* scheduler config */
- SCD_QUEUE_CFG = 0x1d,
-
- /* global key */
- WEP_KEY = 0x20,
-
- /* Memory */
- SHARED_MEM_CFG = 0x25,
-
- /* TDLS */
- TDLS_CHANNEL_SWITCH_CMD = 0x27,
- TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa,
- TDLS_CONFIG_CMD = 0xa7,
-
- /* MAC and Binding commands */
- MAC_CONTEXT_CMD = 0x28,
- TIME_EVENT_CMD = 0x29, /* both CMD and response */
- TIME_EVENT_NOTIFICATION = 0x2a,
- BINDING_CONTEXT_CMD = 0x2b,
- TIME_QUOTA_CMD = 0x2c,
- NON_QOS_TX_COUNTER_CMD = 0x2d,
-
- LQ_CMD = 0x4e,
-
- /* paging block to FW cpu2 */
- FW_PAGING_BLOCK_CMD = 0x4f,
-
- /* Scan offload */
- SCAN_OFFLOAD_REQUEST_CMD = 0x51,
- SCAN_OFFLOAD_ABORT_CMD = 0x52,
- HOT_SPOT_CMD = 0x53,
- SCAN_OFFLOAD_COMPLETE = 0x6D,
- SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
- SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
- MATCH_FOUND_NOTIFICATION = 0xd9,
- SCAN_ITERATION_COMPLETE = 0xe7,
-
- /* Phy */
- PHY_CONFIGURATION_CMD = 0x6a,
- CALIB_RES_NOTIF_PHY_DB = 0x6b,
- /* PHY_DB_CMD = 0x6c, */
-
- /* ToF - 802.11mc FTM */
- TOF_CMD = 0x10,
- TOF_NOTIFICATION = 0x11,
-
- /* Power - legacy power table command */
- POWER_TABLE_CMD = 0x77,
- PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
- LTR_CONFIG = 0xee,
-
- /* Thermal Throttling*/
- REPLY_THERMAL_MNG_BACKOFF = 0x7e,
-
- /* Set/Get DC2DC frequency tune */
- DC2DC_CONFIG_CMD = 0x83,
-
- /* NVM */
- NVM_ACCESS_CMD = 0x88,
-
- SET_CALIB_DEFAULT_CMD = 0x8e,
-
- BEACON_NOTIFICATION = 0x90,
- BEACON_TEMPLATE_CMD = 0x91,
- TX_ANT_CONFIGURATION_CMD = 0x98,
- STATISTICS_CMD = 0x9c,
- STATISTICS_NOTIFICATION = 0x9d,
- EOSP_NOTIFICATION = 0x9e,
- REDUCE_TX_POWER_CMD = 0x9f,
-
- /* RF-KILL commands and notifications */
- CARD_STATE_CMD = 0xa0,
- CARD_STATE_NOTIFICATION = 0xa1,
-
- MISSED_BEACONS_NOTIFICATION = 0xa2,
-
- /* Power - new power table command */
- MAC_PM_POWER_TABLE = 0xa9,
-
- MFUART_LOAD_NOTIFICATION = 0xb1,
-
- REPLY_RX_PHY_CMD = 0xc0,
- REPLY_RX_MPDU_CMD = 0xc1,
- BA_NOTIF = 0xc5,
-
- /* Location Aware Regulatory */
- MCC_UPDATE_CMD = 0xc8,
- MCC_CHUB_UPDATE_CMD = 0xc9,
-
- MARKER_CMD = 0xcb,
-
- /* BT Coex */
- BT_COEX_PRIO_TABLE = 0xcc,
- BT_COEX_PROT_ENV = 0xcd,
- BT_PROFILE_NOTIFICATION = 0xce,
- BT_CONFIG = 0x9b,
- BT_COEX_UPDATE_SW_BOOST = 0x5a,
- BT_COEX_UPDATE_CORUN_LUT = 0x5b,
- BT_COEX_UPDATE_REDUCED_TXP = 0x5c,
- BT_COEX_CI = 0x5d,
-
- REPLY_SF_CFG_CMD = 0xd1,
- REPLY_BEACON_FILTERING_CMD = 0xd2,
-
- /* DTS measurements */
- CMD_DTS_MEASUREMENT_TRIGGER = 0xdc,
- DTS_MEASUREMENT_NOTIFICATION = 0xdd,
-
- REPLY_DEBUG_CMD = 0xf0,
- DEBUG_LOG_MSG = 0xf7,
-
- BCAST_FILTER_CMD = 0xcf,
- MCAST_FILTER_CMD = 0xd0,
-
- /* D3 commands/notifications */
- D3_CONFIG_CMD = 0xd3,
- PROT_OFFLOAD_CONFIG_CMD = 0xd4,
- OFFLOADS_QUERY_CMD = 0xd5,
- REMOTE_WAKE_CONFIG_CMD = 0xd6,
- D0I3_END_CMD = 0xed,
-
- /* for WoWLAN in particular */
- WOWLAN_PATTERNS = 0xe0,
- WOWLAN_CONFIGURATION = 0xe1,
- WOWLAN_TSC_RSC_PARAM = 0xe2,
- WOWLAN_TKIP_PARAM = 0xe3,
- WOWLAN_KEK_KCK_MATERIAL = 0xe4,
- WOWLAN_GET_STATUSES = 0xe5,
- WOWLAN_TX_POWER_PER_DB = 0xe6,
-
- /* and for NetDetect */
- SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56,
- SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58,
- SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59,
-
- REPLY_MAX = 0xff,
-};
-
-enum iwl_phy_ops_subcmd_ids {
- CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
- DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
-};
-
-/* command groups */
-enum {
- PHY_OPS_GROUP = 0x4,
-};
-
-/**
- * struct iwl_cmd_response - generic response struct for most commands
- * @status: status of the command asked, changes for each one
- */
-struct iwl_cmd_response {
- __le32 status;
-};
-
-/*
- * struct iwl_tx_ant_cfg_cmd
- * @valid: valid antenna configuration
- */
-struct iwl_tx_ant_cfg_cmd {
- __le32 valid;
-} __packed;
-
-/*
- * Calibration control struct.
- * Sent as part of the phy configuration command.
- * @flow_trigger: bitmap for which calibrations to perform according to
- * flow triggers.
- * @event_trigger: bitmap for which calibrations to perform according to
- * event triggers.
- */
-struct iwl_calib_ctrl {
- __le32 flow_trigger;
- __le32 event_trigger;
-} __packed;
-
-/* This enum defines the bitmap of various calibrations to enable in both
- * init ucode and runtime ucode through CALIBRATION_CFG_CMD.
- */
-enum iwl_calib_cfg {
- IWL_CALIB_CFG_XTAL_IDX = BIT(0),
- IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1),
- IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2),
- IWL_CALIB_CFG_PAPD_IDX = BIT(3),
- IWL_CALIB_CFG_TX_PWR_IDX = BIT(4),
- IWL_CALIB_CFG_DC_IDX = BIT(5),
- IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6),
- IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7),
- IWL_CALIB_CFG_TX_IQ_IDX = BIT(8),
- IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9),
- IWL_CALIB_CFG_RX_IQ_IDX = BIT(10),
- IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11),
- IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12),
- IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13),
- IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14),
- IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15),
- IWL_CALIB_CFG_DAC_IDX = BIT(16),
- IWL_CALIB_CFG_ABS_IDX = BIT(17),
- IWL_CALIB_CFG_AGC_IDX = BIT(18),
-};
-
-/*
- * Phy configuration command.
- */
-struct iwl_phy_cfg_cmd {
- __le32 phy_cfg;
- struct iwl_calib_ctrl calib_control;
-} __packed;
-
-#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1))
-#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3))
-#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5))
-#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7))
-#define PHY_CFG_TX_CHAIN_A BIT(8)
-#define PHY_CFG_TX_CHAIN_B BIT(9)
-#define PHY_CFG_TX_CHAIN_C BIT(10)
-#define PHY_CFG_RX_CHAIN_A BIT(12)
-#define PHY_CFG_RX_CHAIN_B BIT(13)
-#define PHY_CFG_RX_CHAIN_C BIT(14)
-
-
-/* Target of the NVM_ACCESS_CMD */
-enum {
- NVM_ACCESS_TARGET_CACHE = 0,
- NVM_ACCESS_TARGET_OTP = 1,
- NVM_ACCESS_TARGET_EEPROM = 2,
-};
-
-/* Section types for NVM_ACCESS_CMD */
-enum {
- NVM_SECTION_TYPE_SW = 1,
- NVM_SECTION_TYPE_REGULATORY = 3,
- NVM_SECTION_TYPE_CALIBRATION = 4,
- NVM_SECTION_TYPE_PRODUCTION = 5,
- NVM_SECTION_TYPE_MAC_OVERRIDE = 11,
- NVM_SECTION_TYPE_PHY_SKU = 12,
- NVM_MAX_NUM_SECTIONS = 13,
-};
-
-/**
- * struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section
- * @op_code: 0 - read, 1 - write
- * @target: NVM_ACCESS_TARGET_*
- * @type: NVM_SECTION_TYPE_*
- * @offset: offset in bytes into the section
- * @length: in bytes, to read/write
- * @data: if write operation, the data to write. On read its empty
- */
-struct iwl_nvm_access_cmd {
- u8 op_code;
- u8 target;
- __le16 type;
- __le16 offset;
- __le16 length;
- u8 data[];
-} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */
-
-#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */
-
-/*
- * struct iwl_fw_paging_cmd - paging layout
- *
- * (FW_PAGING_BLOCK_CMD = 0x4f)
- *
- * Send to FW the paging layout in the driver.
- *
- * @flags: various flags for the command
- * @block_size: the block size in powers of 2
- * @block_num: number of blocks specified in the command.
- * @device_phy_addr: virtual addresses from device side
-*/
-struct iwl_fw_paging_cmd {
- __le32 flags;
- __le32 block_size;
- __le32 block_num;
- __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS];
-} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */
-
-/*
- * Fw items ID's
- *
- * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload
- * download
- */
-enum iwl_fw_item_id {
- IWL_FW_ITEM_ID_PAGING = 3,
-};
-
-/*
- * struct iwl_fw_get_item_cmd - get an item from the fw
- */
-struct iwl_fw_get_item_cmd {
- __le32 item_id;
-} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */
-
-struct iwl_fw_get_item_resp {
- __le32 item_id;
- __le32 item_byte_cnt;
- __le32 item_val;
-} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */
-
-/**
- * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD
- * @offset: offset in bytes into the section
- * @length: in bytes, either how much was written or read
- * @type: NVM_SECTION_TYPE_*
- * @status: 0 for success, fail otherwise
- * @data: if read operation, the data returned. Empty on write.
- */
-struct iwl_nvm_access_resp {
- __le16 offset;
- __le16 length;
- __le16 type;
- __le16 status;
- u8 data[];
-} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */
-
-/* MVM_ALIVE 0x1 */
-
-/* alive response is_valid values */
-#define ALIVE_RESP_UCODE_OK BIT(0)
-#define ALIVE_RESP_RFKILL BIT(1)
-
-/* alive response ver_type values */
-enum {
- FW_TYPE_HW = 0,
- FW_TYPE_PROT = 1,
- FW_TYPE_AP = 2,
- FW_TYPE_WOWLAN = 3,
- FW_TYPE_TIMING = 4,
- FW_TYPE_WIPAN = 5
-};
-
-/* alive response ver_subtype values */
-enum {
- FW_SUBTYPE_FULL_FEATURE = 0,
- FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */
- FW_SUBTYPE_REDUCED = 2,
- FW_SUBTYPE_ALIVE_ONLY = 3,
- FW_SUBTYPE_WOWLAN = 4,
- FW_SUBTYPE_AP_SUBTYPE = 5,
- FW_SUBTYPE_WIPAN = 6,
- FW_SUBTYPE_INITIALIZE = 9
-};
-
-#define IWL_ALIVE_STATUS_ERR 0xDEAD
-#define IWL_ALIVE_STATUS_OK 0xCAFE
-
-#define IWL_ALIVE_FLG_RFKILL BIT(0)
-
-struct mvm_alive_resp_ver1 {
- __le16 status;
- __le16 flags;
- u8 ucode_minor;
- u8 ucode_major;
- __le16 id;
- u8 api_minor;
- u8 api_major;
- u8 ver_subtype;
- u8 ver_type;
- u8 mac;
- u8 opt;
- __le16 reserved2;
- __le32 timestamp;
- __le32 error_event_table_ptr; /* SRAM address for error log */
- __le32 log_event_table_ptr; /* SRAM address for event log */
- __le32 cpu_register_ptr;
- __le32 dbgm_config_ptr;
- __le32 alive_counter_ptr;
- __le32 scd_base_ptr; /* SRAM address for SCD */
-} __packed; /* ALIVE_RES_API_S_VER_1 */
-
-struct mvm_alive_resp_ver2 {
- __le16 status;
- __le16 flags;
- u8 ucode_minor;
- u8 ucode_major;
- __le16 id;
- u8 api_minor;
- u8 api_major;
- u8 ver_subtype;
- u8 ver_type;
- u8 mac;
- u8 opt;
- __le16 reserved2;
- __le32 timestamp;
- __le32 error_event_table_ptr; /* SRAM address for error log */
- __le32 log_event_table_ptr; /* SRAM address for LMAC event log */
- __le32 cpu_register_ptr;
- __le32 dbgm_config_ptr;
- __le32 alive_counter_ptr;
- __le32 scd_base_ptr; /* SRAM address for SCD */
- __le32 st_fwrd_addr; /* pointer to Store and forward */
- __le32 st_fwrd_size;
- u8 umac_minor; /* UMAC version: minor */
- u8 umac_major; /* UMAC version: major */
- __le16 umac_id; /* UMAC version: id */
- __le32 error_info_addr; /* SRAM address for UMAC error log */
- __le32 dbg_print_buff_addr;
-} __packed; /* ALIVE_RES_API_S_VER_2 */
-
-struct mvm_alive_resp {
- __le16 status;
- __le16 flags;
- __le32 ucode_minor;
- __le32 ucode_major;
- u8 ver_subtype;
- u8 ver_type;
- u8 mac;
- u8 opt;
- __le32 timestamp;
- __le32 error_event_table_ptr; /* SRAM address for error log */
- __le32 log_event_table_ptr; /* SRAM address for LMAC event log */
- __le32 cpu_register_ptr;
- __le32 dbgm_config_ptr;
- __le32 alive_counter_ptr;
- __le32 scd_base_ptr; /* SRAM address for SCD */
- __le32 st_fwrd_addr; /* pointer to Store and forward */
- __le32 st_fwrd_size;
- __le32 umac_minor; /* UMAC version: minor */
- __le32 umac_major; /* UMAC version: major */
- __le32 error_info_addr; /* SRAM address for UMAC error log */
- __le32 dbg_print_buff_addr;
-} __packed; /* ALIVE_RES_API_S_VER_3 */
-
-/* Error response/notification */
-enum {
- FW_ERR_UNKNOWN_CMD = 0x0,
- FW_ERR_INVALID_CMD_PARAM = 0x1,
- FW_ERR_SERVICE = 0x2,
- FW_ERR_ARC_MEMORY = 0x3,
- FW_ERR_ARC_CODE = 0x4,
- FW_ERR_WATCH_DOG = 0x5,
- FW_ERR_WEP_GRP_KEY_INDX = 0x10,
- FW_ERR_WEP_KEY_SIZE = 0x11,
- FW_ERR_OBSOLETE_FUNC = 0x12,
- FW_ERR_UNEXPECTED = 0xFE,
- FW_ERR_FATAL = 0xFF
-};
-
-/**
- * struct iwl_error_resp - FW error indication
- * ( REPLY_ERROR = 0x2 )
- * @error_type: one of FW_ERR_*
- * @cmd_id: the command ID for which the error occured
- * @bad_cmd_seq_num: sequence number of the erroneous command
- * @error_service: which service created the error, applicable only if
- * error_type = 2, otherwise 0
- * @timestamp: TSF in usecs.
- */
-struct iwl_error_resp {
- __le32 error_type;
- u8 cmd_id;
- u8 reserved1;
- __le16 bad_cmd_seq_num;
- __le32 error_service;
- __le64 timestamp;
-} __packed;
-
-
-/* Common PHY, MAC and Bindings definitions */
-
-#define MAX_MACS_IN_BINDING (3)
-#define MAX_BINDINGS (4)
-#define AUX_BINDING_INDEX (3)
-#define MAX_PHYS (4)
-
-/* Used to extract ID and color from the context dword */
-#define FW_CTXT_ID_POS (0)
-#define FW_CTXT_ID_MSK (0xff << FW_CTXT_ID_POS)
-#define FW_CTXT_COLOR_POS (8)
-#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS)
-#define FW_CTXT_INVALID (0xffffffff)
-
-#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\
- (_color << FW_CTXT_COLOR_POS))
-
-/* Possible actions on PHYs, MACs and Bindings */
-enum {
- FW_CTXT_ACTION_STUB = 0,
- FW_CTXT_ACTION_ADD,
- FW_CTXT_ACTION_MODIFY,
- FW_CTXT_ACTION_REMOVE,
- FW_CTXT_ACTION_NUM
-}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */
-
-/* Time Events */
-
-/* Time Event types, according to MAC type */
-enum iwl_time_event_type {
- /* BSS Station Events */
- TE_BSS_STA_AGGRESSIVE_ASSOC,
- TE_BSS_STA_ASSOC,
- TE_BSS_EAP_DHCP_PROT,
- TE_BSS_QUIET_PERIOD,
-
- /* P2P Device Events */
- TE_P2P_DEVICE_DISCOVERABLE,
- TE_P2P_DEVICE_LISTEN,
- TE_P2P_DEVICE_ACTION_SCAN,
- TE_P2P_DEVICE_FULL_SCAN,
-
- /* P2P Client Events */
- TE_P2P_CLIENT_AGGRESSIVE_ASSOC,
- TE_P2P_CLIENT_ASSOC,
- TE_P2P_CLIENT_QUIET_PERIOD,
-
- /* P2P GO Events */
- TE_P2P_GO_ASSOC_PROT,
- TE_P2P_GO_REPETITIVE_NOA,
- TE_P2P_GO_CT_WINDOW,
-
- /* WiDi Sync Events */
- TE_WIDI_TX_SYNC,
-
- /* Channel Switch NoA */
- TE_CHANNEL_SWITCH_PERIOD,
-
- TE_MAX
-}; /* MAC_EVENT_TYPE_API_E_VER_1 */
-
-
-
-/* Time event - defines for command API v1 */
-
-/*
- * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed.
- * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only
- * the first fragment is scheduled.
- * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only
- * the first 2 fragments are scheduled.
- * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
- * number of fragments are valid.
- *
- * Other than the constant defined above, specifying a fragmentation value 'x'
- * means that the event can be fragmented but only the first 'x' will be
- * scheduled.
- */
-enum {
- TE_V1_FRAG_NONE = 0,
- TE_V1_FRAG_SINGLE = 1,
- TE_V1_FRAG_DUAL = 2,
- TE_V1_FRAG_ENDLESS = 0xffffffff
-};
-
-/* If a Time Event can be fragmented, this is the max number of fragments */
-#define TE_V1_FRAG_MAX_MSK 0x0fffffff
-/* Repeat the time event endlessly (until removed) */
-#define TE_V1_REPEAT_ENDLESS 0xffffffff
-/* If a Time Event has bounded repetitions, this is the maximal value */
-#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff
-
-/* Time Event dependencies: none, on another TE, or in a specific time */
-enum {
- TE_V1_INDEPENDENT = 0,
- TE_V1_DEP_OTHER = BIT(0),
- TE_V1_DEP_TSF = BIT(1),
- TE_V1_EVENT_SOCIOPATHIC = BIT(2),
-}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
-
-/*
- * @TE_V1_NOTIF_NONE: no notifications
- * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start
- * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end
- * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use
- * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use.
- * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start
- * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end
- * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use.
- * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use.
- *
- * Supported Time event notifications configuration.
- * A notification (both event and fragment) includes a status indicating weather
- * the FW was able to schedule the event or not. For fragment start/end
- * notification the status is always success. There is no start/end fragment
- * notification for monolithic events.
- */
-enum {
- TE_V1_NOTIF_NONE = 0,
- TE_V1_NOTIF_HOST_EVENT_START = BIT(0),
- TE_V1_NOTIF_HOST_EVENT_END = BIT(1),
- TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2),
- TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3),
- TE_V1_NOTIF_HOST_FRAG_START = BIT(4),
- TE_V1_NOTIF_HOST_FRAG_END = BIT(5),
- TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6),
- TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7),
-}; /* MAC_EVENT_ACTION_API_E_VER_2 */
-
-/* Time event - defines for command API */
-
-/*
- * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed.
- * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only
- * the first fragment is scheduled.
- * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only
- * the first 2 fragments are scheduled.
- * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
- * number of fragments are valid.
- *
- * Other than the constant defined above, specifying a fragmentation value 'x'
- * means that the event can be fragmented but only the first 'x' will be
- * scheduled.
- */
-enum {
- TE_V2_FRAG_NONE = 0,
- TE_V2_FRAG_SINGLE = 1,
- TE_V2_FRAG_DUAL = 2,
- TE_V2_FRAG_MAX = 0xfe,
- TE_V2_FRAG_ENDLESS = 0xff
-};
-
-/* Repeat the time event endlessly (until removed) */
-#define TE_V2_REPEAT_ENDLESS 0xff
-/* If a Time Event has bounded repetitions, this is the maximal value */
-#define TE_V2_REPEAT_MAX 0xfe
-
-#define TE_V2_PLACEMENT_POS 12
-#define TE_V2_ABSENCE_POS 15
-
-/* Time event policy values
- * A notification (both event and fragment) includes a status indicating weather
- * the FW was able to schedule the event or not. For fragment start/end
- * notification the status is always success. There is no start/end fragment
- * notification for monolithic events.
- *
- * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable
- * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start
- * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end
- * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use
- * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use.
- * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start
- * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end
- * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use.
- * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use.
- * @TE_V2_DEP_OTHER: depends on another time event
- * @TE_V2_DEP_TSF: depends on a specific time
- * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC
- * @TE_V2_ABSENCE: are we present or absent during the Time Event.
- */
-enum {
- TE_V2_DEFAULT_POLICY = 0x0,
-
- /* notifications (event start/stop, fragment start/stop) */
- TE_V2_NOTIF_HOST_EVENT_START = BIT(0),
- TE_V2_NOTIF_HOST_EVENT_END = BIT(1),
- TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2),
- TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3),
-
- TE_V2_NOTIF_HOST_FRAG_START = BIT(4),
- TE_V2_NOTIF_HOST_FRAG_END = BIT(5),
- TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6),
- TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7),
- T2_V2_START_IMMEDIATELY = BIT(11),
-
- TE_V2_NOTIF_MSK = 0xff,
-
- /* placement characteristics */
- TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS),
- TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1),
- TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2),
-
- /* are we present or absent during the Time Event. */
- TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS),
-};
-
-/**
- * struct iwl_time_event_cmd_api - configuring Time Events
- * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also
- * with version 1. determined by IWL_UCODE_TLV_FLAGS)
- * ( TIME_EVENT_CMD = 0x29 )
- * @id_and_color: ID and color of the relevant MAC
- * @action: action to perform, one of FW_CTXT_ACTION_*
- * @id: this field has two meanings, depending on the action:
- * If the action is ADD, then it means the type of event to add.
- * For all other actions it is the unique event ID assigned when the
- * event was added by the FW.
- * @apply_time: When to start the Time Event (in GP2)
- * @max_delay: maximum delay to event's start (apply time), in TU
- * @depends_on: the unique ID of the event we depend on (if any)
- * @interval: interval between repetitions, in TU
- * @duration: duration of event in TU
- * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
- * @max_frags: maximal number of fragments the Time Event can be divided to
- * @policy: defines whether uCode shall notify the host or other uCode modules
- * on event and/or fragment start and/or end
- * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
- * TE_EVENT_SOCIOPATHIC
- * using TE_ABSENCE and using TE_NOTIF_*
- */
-struct iwl_time_event_cmd {
- /* COMMON_INDEX_HDR_API_S_VER_1 */
- __le32 id_and_color;
- __le32 action;
- __le32 id;
- /* MAC_TIME_EVENT_DATA_API_S_VER_2 */
- __le32 apply_time;
- __le32 max_delay;
- __le32 depends_on;
- __le32 interval;
- __le32 duration;
- u8 repeat;
- u8 max_frags;
- __le16 policy;
-} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */
-
-/**
- * struct iwl_time_event_resp - response structure to iwl_time_event_cmd
- * @status: bit 0 indicates success, all others specify errors
- * @id: the Time Event type
- * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE
- * @id_and_color: ID and color of the relevant MAC
- */
-struct iwl_time_event_resp {
- __le32 status;
- __le32 id;
- __le32 unique_id;
- __le32 id_and_color;
-} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */
-
-/**
- * struct iwl_time_event_notif - notifications of time event start/stop
- * ( TIME_EVENT_NOTIFICATION = 0x2a )
- * @timestamp: action timestamp in GP2
- * @session_id: session's unique id
- * @unique_id: unique id of the Time Event itself
- * @id_and_color: ID and color of the relevant MAC
- * @action: one of TE_NOTIF_START or TE_NOTIF_END
- * @status: true if scheduled, false otherwise (not executed)
- */
-struct iwl_time_event_notif {
- __le32 timestamp;
- __le32 session_id;
- __le32 unique_id;
- __le32 id_and_color;
- __le32 action;
- __le32 status;
-} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */
-
-
-/* Bindings and Time Quota */
-
-/**
- * struct iwl_binding_cmd - configuring bindings
- * ( BINDING_CONTEXT_CMD = 0x2b )
- * @id_and_color: ID and color of the relevant Binding
- * @action: action to perform, one of FW_CTXT_ACTION_*
- * @macs: array of MAC id and colors which belong to the binding
- * @phy: PHY id and color which belongs to the binding
- */
-struct iwl_binding_cmd {
- /* COMMON_INDEX_HDR_API_S_VER_1 */
- __le32 id_and_color;
- __le32 action;
- /* BINDING_DATA_API_S_VER_1 */
- __le32 macs[MAX_MACS_IN_BINDING];
- __le32 phy;
-} __packed; /* BINDING_CMD_API_S_VER_1 */
-
-/* The maximal number of fragments in the FW's schedule session */
-#define IWL_MVM_MAX_QUOTA 128
-
-/**
- * struct iwl_time_quota_data - configuration of time quota per binding
- * @id_and_color: ID and color of the relevant Binding
- * @quota: absolute time quota in TU. The scheduler will try to divide the
- * remainig quota (after Time Events) according to this quota.
- * @max_duration: max uninterrupted context duration in TU
- */
-struct iwl_time_quota_data {
- __le32 id_and_color;
- __le32 quota;
- __le32 max_duration;
-} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */
-
-/**
- * struct iwl_time_quota_cmd - configuration of time quota between bindings
- * ( TIME_QUOTA_CMD = 0x2c )
- * @quotas: allocations per binding
- */
-struct iwl_time_quota_cmd {
- struct iwl_time_quota_data quotas[MAX_BINDINGS];
-} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */
-
-
-/* PHY context */
-
-/* Supported bands */
-#define PHY_BAND_5 (0)
-#define PHY_BAND_24 (1)
-
-/* Supported channel width, vary if there is VHT support */
-#define PHY_VHT_CHANNEL_MODE20 (0x0)
-#define PHY_VHT_CHANNEL_MODE40 (0x1)
-#define PHY_VHT_CHANNEL_MODE80 (0x2)
-#define PHY_VHT_CHANNEL_MODE160 (0x3)
-
-/*
- * Control channel position:
- * For legacy set bit means upper channel, otherwise lower.
- * For VHT - bit-2 marks if the control is lower/upper relative to center-freq
- * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0.
- * center_freq
- * |
- * 40Mhz |_______|_______|
- * 80Mhz |_______|_______|_______|_______|
- * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______|
- * code 011 010 001 000 | 100 101 110 111
- */
-#define PHY_VHT_CTRL_POS_1_BELOW (0x0)
-#define PHY_VHT_CTRL_POS_2_BELOW (0x1)
-#define PHY_VHT_CTRL_POS_3_BELOW (0x2)
-#define PHY_VHT_CTRL_POS_4_BELOW (0x3)
-#define PHY_VHT_CTRL_POS_1_ABOVE (0x4)
-#define PHY_VHT_CTRL_POS_2_ABOVE (0x5)
-#define PHY_VHT_CTRL_POS_3_ABOVE (0x6)
-#define PHY_VHT_CTRL_POS_4_ABOVE (0x7)
-
-/*
- * @band: PHY_BAND_*
- * @channel: channel number
- * @width: PHY_[VHT|LEGACY]_CHANNEL_*
- * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_*
- */
-struct iwl_fw_channel_info {
- u8 band;
- u8 channel;
- u8 width;
- u8 ctrl_pos;
-} __packed;
-
-#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0)
-#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \
- (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS)
-#define PHY_RX_CHAIN_VALID_POS (1)
-#define PHY_RX_CHAIN_VALID_MSK \
- (0x7 << PHY_RX_CHAIN_VALID_POS)
-#define PHY_RX_CHAIN_FORCE_SEL_POS (4)
-#define PHY_RX_CHAIN_FORCE_SEL_MSK \
- (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS)
-#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
-#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \
- (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS)
-#define PHY_RX_CHAIN_CNT_POS (10)
-#define PHY_RX_CHAIN_CNT_MSK \
- (0x3 << PHY_RX_CHAIN_CNT_POS)
-#define PHY_RX_CHAIN_MIMO_CNT_POS (12)
-#define PHY_RX_CHAIN_MIMO_CNT_MSK \
- (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS)
-#define PHY_RX_CHAIN_MIMO_FORCE_POS (14)
-#define PHY_RX_CHAIN_MIMO_FORCE_MSK \
- (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS)
-
-/* TODO: fix the value, make it depend on firmware at runtime? */
-#define NUM_PHY_CTX 3
-
-/* TODO: complete missing documentation */
-/**
- * struct iwl_phy_context_cmd - config of the PHY context
- * ( PHY_CONTEXT_CMD = 0x8 )
- * @id_and_color: ID and color of the relevant Binding
- * @action: action to perform, one of FW_CTXT_ACTION_*
- * @apply_time: 0 means immediate apply and context switch.
- * other value means apply new params after X usecs
- * @tx_param_color: ???
- * @channel_info:
- * @txchain_info: ???
- * @rxchain_info: ???
- * @acquisition_data: ???
- * @dsp_cfg_flags: set to 0
- */
-struct iwl_phy_context_cmd {
- /* COMMON_INDEX_HDR_API_S_VER_1 */
- __le32 id_and_color;
- __le32 action;
- /* PHY_CONTEXT_DATA_API_S_VER_1 */
- __le32 apply_time;
- __le32 tx_param_color;
- struct iwl_fw_channel_info ci;
- __le32 txchain_info;
- __le32 rxchain_info;
- __le32 acquisition_data;
- __le32 dsp_cfg_flags;
-} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
-
-/*
- * Aux ROC command
- *
- * Command requests the firmware to create a time event for a certain duration
- * and remain on the given channel. This is done by using the Aux framework in
- * the FW.
- * The command was first used for Hot Spot issues - but can be used regardless
- * to Hot Spot.
- *
- * ( HOT_SPOT_CMD 0x53 )
- *
- * @id_and_color: ID and color of the MAC
- * @action: action to perform, one of FW_CTXT_ACTION_*
- * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the
- * event_unique_id should be the id of the time event assigned by ucode.
- * Otherwise ignore the event_unique_id.
- * @sta_id_and_color: station id and color, resumed during "Remain On Channel"
- * activity.
- * @channel_info: channel info
- * @node_addr: Our MAC Address
- * @reserved: reserved for alignment
- * @apply_time: GP2 value to start (should always be the current GP2 value)
- * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max
- * time by which start of the event is allowed to be postponed.
- * @duration: event duration in TU To calculate event duration:
- * timeEventDuration = min(duration, remainingQuota)
- */
-struct iwl_hs20_roc_req {
- /* COMMON_INDEX_HDR_API_S_VER_1 hdr */
- __le32 id_and_color;
- __le32 action;
- __le32 event_unique_id;
- __le32 sta_id_and_color;
- struct iwl_fw_channel_info channel_info;
- u8 node_addr[ETH_ALEN];
- __le16 reserved;
- __le32 apply_time;
- __le32 apply_time_max_delay;
- __le32 duration;
-} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */
-
-/*
- * values for AUX ROC result values
- */
-enum iwl_mvm_hot_spot {
- HOT_SPOT_RSP_STATUS_OK,
- HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS,
- HOT_SPOT_MAX_NUM_OF_SESSIONS,
-};
-
-/*
- * Aux ROC command response
- *
- * In response to iwl_hs20_roc_req the FW sends this command to notify the
- * driver the uid of the timevent.
- *
- * ( HOT_SPOT_CMD 0x53 )
- *
- * @event_unique_id: Unique ID of time event assigned by ucode
- * @status: Return status 0 is success, all the rest used for specific errors
- */
-struct iwl_hs20_roc_res {
- __le32 event_unique_id;
- __le32 status;
-} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */
-
-/**
- * struct iwl_radio_version_notif - information on the radio version
- * ( RADIO_VERSION_NOTIFICATION = 0x68 )
- * @radio_flavor:
- * @radio_step:
- * @radio_dash:
- */
-struct iwl_radio_version_notif {
- __le32 radio_flavor;
- __le32 radio_step;
- __le32 radio_dash;
-} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */
-
-enum iwl_card_state_flags {
- CARD_ENABLED = 0x00,
- HW_CARD_DISABLED = 0x01,
- SW_CARD_DISABLED = 0x02,
- CT_KILL_CARD_DISABLED = 0x04,
- HALT_CARD_DISABLED = 0x08,
- CARD_DISABLED_MSK = 0x0f,
- CARD_IS_RX_ON = 0x10,
-};
-
-/**
- * struct iwl_radio_version_notif - information on the radio version
- * ( CARD_STATE_NOTIFICATION = 0xa1 )
- * @flags: %iwl_card_state_flags
- */
-struct iwl_card_state_notif {
- __le32 flags;
-} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
-
-/**
- * struct iwl_missed_beacons_notif - information on missed beacons
- * ( MISSED_BEACONS_NOTIFICATION = 0xa2 )
- * @mac_id: interface ID
- * @consec_missed_beacons_since_last_rx: number of consecutive missed
- * beacons since last RX.
- * @consec_missed_beacons: number of consecutive missed beacons
- * @num_expected_beacons:
- * @num_recvd_beacons:
- */
-struct iwl_missed_beacons_notif {
- __le32 mac_id;
- __le32 consec_missed_beacons_since_last_rx;
- __le32 consec_missed_beacons;
- __le32 num_expected_beacons;
- __le32 num_recvd_beacons;
-} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */
-
-/**
- * struct iwl_mfuart_load_notif - mfuart image version & status
- * ( MFUART_LOAD_NOTIFICATION = 0xb1 )
- * @installed_ver: installed image version
- * @external_ver: external image version
- * @status: MFUART loading status
- * @duration: MFUART loading time
-*/
-struct iwl_mfuart_load_notif {
- __le32 installed_ver;
- __le32 external_ver;
- __le32 status;
- __le32 duration;
-} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/
-
-/**
- * struct iwl_set_calib_default_cmd - set default value for calibration.
- * ( SET_CALIB_DEFAULT_CMD = 0x8e )
- * @calib_index: the calibration to set value for
- * @length: of data
- * @data: the value to set for the calibration result
- */
-struct iwl_set_calib_default_cmd {
- __le16 calib_index;
- __le16 length;
- u8 data[0];
-} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
-
-#define MAX_PORT_ID_NUM 2
-#define MAX_MCAST_FILTERING_ADDRESSES 256
-
-/**
- * struct iwl_mcast_filter_cmd - configure multicast filter.
- * @filter_own: Set 1 to filter out multicast packets sent by station itself
- * @port_id: Multicast MAC addresses array specifier. This is a strange way
- * to identify network interface adopted in host-device IF.
- * It is used by FW as index in array of addresses. This array has
- * MAX_PORT_ID_NUM members.
- * @count: Number of MAC addresses in the array
- * @pass_all: Set 1 to pass all multicast packets.
- * @bssid: current association BSSID.
- * @addr_list: Place holder for array of MAC addresses.
- * IMPORTANT: add padding if necessary to ensure DWORD alignment.
- */
-struct iwl_mcast_filter_cmd {
- u8 filter_own;
- u8 port_id;
- u8 count;
- u8 pass_all;
- u8 bssid[6];
- u8 reserved[2];
- u8 addr_list[0];
-} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
-
-#define MAX_BCAST_FILTERS 8
-#define MAX_BCAST_FILTER_ATTRS 2
-
-/**
- * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet
- * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start.
- * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e.
- * start of ip payload).
- */
-enum iwl_mvm_bcast_filter_attr_offset {
- BCAST_FILTER_OFFSET_PAYLOAD_START = 0,
- BCAST_FILTER_OFFSET_IP_END = 1,
-};
-
-/**
- * struct iwl_fw_bcast_filter_attr - broadcast filter attribute
- * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset.
- * @offset: starting offset of this pattern.
- * @val: value to match - big endian (MSB is the first
- * byte to match from offset pos).
- * @mask: mask to match (big endian).
- */
-struct iwl_fw_bcast_filter_attr {
- u8 offset_type;
- u8 offset;
- __le16 reserved1;
- __be32 val;
- __be32 mask;
-} __packed; /* BCAST_FILTER_ATT_S_VER_1 */
-
-/**
- * enum iwl_mvm_bcast_filter_frame_type - filter frame type
- * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames.
- * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames
- */
-enum iwl_mvm_bcast_filter_frame_type {
- BCAST_FILTER_FRAME_TYPE_ALL = 0,
- BCAST_FILTER_FRAME_TYPE_IPV4 = 1,
-};
-
-/**
- * struct iwl_fw_bcast_filter - broadcast filter
- * @discard: discard frame (1) or let it pass (0).
- * @frame_type: &enum iwl_mvm_bcast_filter_frame_type.
- * @num_attrs: number of valid attributes in this filter.
- * @attrs: attributes of this filter. a filter is considered matched
- * only when all its attributes are matched (i.e. AND relationship)
- */
-struct iwl_fw_bcast_filter {
- u8 discard;
- u8 frame_type;
- u8 num_attrs;
- u8 reserved1;
- struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS];
-} __packed; /* BCAST_FILTER_S_VER_1 */
-
-/**
- * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration.
- * @default_discard: default action for this mac (discard (1) / pass (0)).
- * @attached_filters: bitmap of relevant filters for this mac.
- */
-struct iwl_fw_bcast_mac {
- u8 default_discard;
- u8 reserved1;
- __le16 attached_filters;
-} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */
-
-/**
- * struct iwl_bcast_filter_cmd - broadcast filtering configuration
- * @disable: enable (0) / disable (1)
- * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS)
- * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER)
- * @filters: broadcast filters
- * @macs: broadcast filtering configuration per-mac
- */
-struct iwl_bcast_filter_cmd {
- u8 disable;
- u8 max_bcast_filters;
- u8 max_macs;
- u8 reserved1;
- struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS];
- struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER];
-} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */
-
-/*
- * enum iwl_mvm_marker_id - maker ids
- *
- * The ids for different type of markers to insert into the usniffer logs
- */
-enum iwl_mvm_marker_id {
- MARKER_ID_TX_FRAME_LATENCY = 1,
-}; /* MARKER_ID_API_E_VER_1 */
-
-/**
- * struct iwl_mvm_marker - mark info into the usniffer logs
- *
- * (MARKER_CMD = 0xcb)
- *
- * Mark the UTC time stamp into the usniffer logs together with additional
- * metadata, so the usniffer output can be parsed.
- * In the command response the ucode will return the GP2 time.
- *
- * @dw_len: The amount of dwords following this byte including this byte.
- * @marker_id: A unique marker id (iwl_mvm_marker_id).
- * @reserved: reserved.
- * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC
- * @metadata: additional meta data that will be written to the unsiffer log
- */
-struct iwl_mvm_marker {
- u8 dwLen;
- u8 markerId;
- __le16 reserved;
- __le64 timestamp;
- __le32 metadata[0];
-} __packed; /* MARKER_API_S_VER_1 */
-
-/*
- * enum iwl_dc2dc_config_id - flag ids
- *
- * Ids of dc2dc configuration flags
- */
-enum iwl_dc2dc_config_id {
- DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */
- DCDC_FREQ_TUNE_SET = 0x2,
-}; /* MARKER_ID_API_E_VER_1 */
-
-/**
- * struct iwl_dc2dc_config_cmd - configure dc2dc values
- *
- * (DC2DC_CONFIG_CMD = 0x83)
- *
- * Set/Get & configure dc2dc values.
- * The command always returns the current dc2dc values.
- *
- * @flags: set/get dc2dc
- * @enable_low_power_mode: not used.
- * @dc2dc_freq_tune0: frequency divider - digital domain
- * @dc2dc_freq_tune1: frequency divider - analog domain
- */
-struct iwl_dc2dc_config_cmd {
- __le32 flags;
- __le32 enable_low_power_mode; /* not used */
- __le32 dc2dc_freq_tune0;
- __le32 dc2dc_freq_tune1;
-} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */
-
-/**
- * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd
- *
- * Current dc2dc values returned by the FW.
- *
- * @dc2dc_freq_tune0: frequency divider - digital domain
- * @dc2dc_freq_tune1: frequency divider - analog domain
- */
-struct iwl_dc2dc_config_resp {
- __le32 dc2dc_freq_tune0;
- __le32 dc2dc_freq_tune1;
-} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */
-
-/***********************************
- * Smart Fifo API
- ***********************************/
-/* Smart Fifo state */
-enum iwl_sf_state {
- SF_LONG_DELAY_ON = 0, /* should never be called by driver */
- SF_FULL_ON,
- SF_UNINIT,
- SF_INIT_OFF,
- SF_HW_NUM_STATES
-};
-
-/* Smart Fifo possible scenario */
-enum iwl_sf_scenario {
- SF_SCENARIO_SINGLE_UNICAST,
- SF_SCENARIO_AGG_UNICAST,
- SF_SCENARIO_MULTICAST,
- SF_SCENARIO_BA_RESP,
- SF_SCENARIO_TX_RESP,
- SF_NUM_SCENARIO
-};
-
-#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */
-#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */
-
-/* smart FIFO default values */
-#define SF_W_MARK_SISO 6144
-#define SF_W_MARK_MIMO2 8192
-#define SF_W_MARK_MIMO3 6144
-#define SF_W_MARK_LEGACY 4096
-#define SF_W_MARK_SCAN 4096
-
-/* SF Scenarios timers for default configuration (aligned to 32 uSec) */
-#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */
-#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
-#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */
-#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
-#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */
-#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
-#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */
-#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */
-#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */
-#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */
-
-/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */
-#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */
-#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */
-#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */
-#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */
-#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */
-#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */
-#define SF_BA_IDLE_TIMER 320 /* 300 uSec */
-#define SF_BA_AGING_TIMER 2016 /* 2 mSec */
-#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */
-#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */
-
-#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */
-
-#define SF_CFG_DUMMY_NOTIF_OFF BIT(16)
-
-/**
- * Smart Fifo configuration command.
- * @state: smart fifo state, types listed in enum %iwl_sf_sate.
- * @watermark: Minimum allowed availabe free space in RXF for transient state.
- * @long_delay_timeouts: aging and idle timer values for each scenario
- * in long delay state.
- * @full_on_timeouts: timer values for each scenario in full on state.
- */
-struct iwl_sf_cfg_cmd {
- __le32 state;
- __le32 watermark[SF_TRANSIENT_STATES_NUMBER];
- __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
- __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
-} __packed; /* SF_CFG_API_S_VER_2 */
-
-/***********************************
- * Location Aware Regulatory (LAR) API - MCC updates
- ***********************************/
-
-/**
- * struct iwl_mcc_update_cmd - Request the device to update geographic
- * regulatory profile according to the given MCC (Mobile Country Code).
- * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
- * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
- * MCC in the cmd response will be the relevant MCC in the NVM.
- * @mcc: given mobile country code
- * @source_id: the source from where we got the MCC, see iwl_mcc_source
- * @reserved: reserved for alignment
- */
-struct iwl_mcc_update_cmd {
- __le16 mcc;
- u8 source_id;
- u8 reserved;
-} __packed; /* LAR_UPDATE_MCC_CMD_API_S */
-
-/**
- * iwl_mcc_update_resp - response to MCC_UPDATE_CMD.
- * Contains the new channel control profile map, if changed, and the new MCC
- * (mobile country code).
- * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
- * @status: see &enum iwl_mcc_update_status
- * @mcc: the new applied MCC
- * @cap: capabilities for all channels which matches the MCC
- * @source_id: the MCC source, see iwl_mcc_source
- * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
- * channels, depending on platform)
- * @channels: channel control data map, DWORD for each channel. Only the first
- * 16bits are used.
- */
-struct iwl_mcc_update_resp {
- __le32 status;
- __le16 mcc;
- u8 cap;
- u8 source_id;
- __le32 n_channels;
- __le32 channels[0];
-} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */
-
-/**
- * struct iwl_mcc_chub_notif - chub notifies of mcc change
- * (MCC_CHUB_UPDATE_CMD = 0xc9)
- * The Chub (Communication Hub, CommsHUB) is a HW component that connects to
- * the cellular and connectivity cores that gets updates of the mcc, and
- * notifies the ucode directly of any mcc change.
- * The ucode requests the driver to request the device to update geographic
- * regulatory profile according to the given MCC (Mobile Country Code).
- * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
- * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
- * MCC in the cmd response will be the relevant MCC in the NVM.
- * @mcc: given mobile country code
- * @source_id: identity of the change originator, see iwl_mcc_source
- * @reserved1: reserved for alignment
- */
-struct iwl_mcc_chub_notif {
- u16 mcc;
- u8 source_id;
- u8 reserved1;
-} __packed; /* LAR_MCC_NOTIFY_S */
-
-enum iwl_mcc_update_status {
- MCC_RESP_NEW_CHAN_PROFILE,
- MCC_RESP_SAME_CHAN_PROFILE,
- MCC_RESP_INVALID,
- MCC_RESP_NVM_DISABLED,
- MCC_RESP_ILLEGAL,
- MCC_RESP_LOW_PRIORITY,
-};
-
-enum iwl_mcc_source {
- MCC_SOURCE_OLD_FW = 0,
- MCC_SOURCE_ME = 1,
- MCC_SOURCE_BIOS = 2,
- MCC_SOURCE_3G_LTE_HOST = 3,
- MCC_SOURCE_3G_LTE_DEVICE = 4,
- MCC_SOURCE_WIFI = 5,
- MCC_SOURCE_RESERVED = 6,
- MCC_SOURCE_DEFAULT = 7,
- MCC_SOURCE_UNINITIALIZED = 8,
- MCC_SOURCE_GET_CURRENT = 0x10
-};
-
-/* DTS measurements */
-
-enum iwl_dts_measurement_flags {
- DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0),
- DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1),
-};
-
-/**
- * iwl_dts_measurement_cmd - request DTS temperature and/or voltage measurements
- *
- * @flags: indicates which measurements we want as specified in &enum
- * iwl_dts_measurement_flags
- */
-struct iwl_dts_measurement_cmd {
- __le32 flags;
-} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */
-
-/**
-* enum iwl_dts_control_measurement_mode - DTS measurement type
-* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read
-* back (latest value. Not waiting for new value). Use automatic
-* SW DTS configuration.
-* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings,
-* trigger DTS reading and provide read back temperature read
-* when available.
-* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read
-* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result,
-* without measurement trigger.
-*/
-enum iwl_dts_control_measurement_mode {
- DTS_AUTOMATIC = 0,
- DTS_REQUEST_READ = 1,
- DTS_OVER_WRITE = 2,
- DTS_DIRECT_WITHOUT_MEASURE = 3,
-};
-
-/**
-* enum iwl_dts_used - DTS to use or used for measurement in the DTS request
-* @DTS_USE_TOP: Top
-* @DTS_USE_CHAIN_A: chain A
-* @DTS_USE_CHAIN_B: chain B
-* @DTS_USE_CHAIN_C: chain C
-* @XTAL_TEMPERATURE - read temperature from xtal
-*/
-enum iwl_dts_used {
- DTS_USE_TOP = 0,
- DTS_USE_CHAIN_A = 1,
- DTS_USE_CHAIN_B = 2,
- DTS_USE_CHAIN_C = 3,
- XTAL_TEMPERATURE = 4,
-};
-
-/**
-* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode
-* @DTS_BIT6_MODE: bit 6 mode
-* @DTS_BIT8_MODE: bit 8 mode
-*/
-enum iwl_dts_bit_mode {
- DTS_BIT6_MODE = 0,
- DTS_BIT8_MODE = 1,
-};
-
-/**
- * iwl_ext_dts_measurement_cmd - request extended DTS temperature measurements
- * @control_mode: see &enum iwl_dts_control_measurement_mode
- * @temperature: used when over write DTS mode is selected
- * @sensor: set temperature sensor to use. See &enum iwl_dts_used
- * @avg_factor: average factor to DTS in request DTS read mode
- * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode
- * @step_duration: step duration for the DTS
- */
-struct iwl_ext_dts_measurement_cmd {
- __le32 control_mode;
- __le32 temperature;
- __le32 sensor;
- __le32 avg_factor;
- __le32 bit_mode;
- __le32 step_duration;
-} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */
-
-/**
- * iwl_dts_measurement_notif - notification received with the measurements
- *
- * @temp: the measured temperature
- * @voltage: the measured voltage
- */
-struct iwl_dts_measurement_notif {
- __le32 temp;
- __le32 voltage;
-} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */
-
-/***********************************
- * TDLS API
- ***********************************/
-
-/* Type of TDLS request */
-enum iwl_tdls_channel_switch_type {
- TDLS_SEND_CHAN_SW_REQ = 0,
- TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH,
- TDLS_MOVE_CH,
-}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */
-
-/**
- * Switch timing sub-element in a TDLS channel-switch command
- * @frame_timestamp: GP2 timestamp of channel-switch request/response packet
- * received from peer
- * @max_offchan_duration: What amount of microseconds out of a DTIM is given
- * to the TDLS off-channel communication. For instance if the DTIM is
- * 200TU and the TDLS peer is to be given 25% of the time, the value
- * given will be 50TU, or 50 * 1024 if translated into microseconds.
- * @switch_time: switch time the peer sent in its channel switch timing IE
- * @switch_timout: switch timeout the peer sent in its channel switch timing IE
- */
-struct iwl_tdls_channel_switch_timing {
- __le32 frame_timestamp; /* GP2 time of peer packet Rx */
- __le32 max_offchan_duration; /* given in micro-seconds */
- __le32 switch_time; /* given in micro-seconds */
- __le32 switch_timeout; /* given in micro-seconds */
-} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */
-
-#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200
-
-/**
- * TDLS channel switch frame template
- *
- * A template representing a TDLS channel-switch request or response frame
- *
- * @switch_time_offset: offset to the channel switch timing IE in the template
- * @tx_cmd: Tx parameters for the frame
- * @data: frame data
- */
-struct iwl_tdls_channel_switch_frame {
- __le32 switch_time_offset;
- struct iwl_tx_cmd tx_cmd;
- u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE];
-} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */
-
-/**
- * TDLS channel switch command
- *
- * The command is sent to initiate a channel switch and also in response to
- * incoming TDLS channel-switch request/response packets from remote peers.
- *
- * @switch_type: see &enum iwl_tdls_channel_switch_type
- * @peer_sta_id: station id of TDLS peer
- * @ci: channel we switch to
- * @timing: timing related data for command
- * @frame: channel-switch request/response template, depending to switch_type
- */
-struct iwl_tdls_channel_switch_cmd {
- u8 switch_type;
- __le32 peer_sta_id;
- struct iwl_fw_channel_info ci;
- struct iwl_tdls_channel_switch_timing timing;
- struct iwl_tdls_channel_switch_frame frame;
-} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */
-
-/**
- * TDLS channel switch start notification
- *
- * @status: non-zero on success
- * @offchannel_duration: duration given in microseconds
- * @sta_id: peer currently performing the channel-switch with
- */
-struct iwl_tdls_channel_switch_notif {
- __le32 status;
- __le32 offchannel_duration;
- __le32 sta_id;
-} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */
-
-/**
- * TDLS station info
- *
- * @sta_id: station id of the TDLS peer
- * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx
- * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer
- * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise
- */
-struct iwl_tdls_sta_info {
- u8 sta_id;
- u8 tx_to_peer_tid;
- __le16 tx_to_peer_ssn;
- __le32 is_initiator;
-} __packed; /* TDLS_STA_INFO_VER_1 */
-
-/**
- * TDLS basic config command
- *
- * @id_and_color: MAC id and color being configured
- * @tdls_peer_count: amount of currently connected TDLS peers
- * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx
- * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP
- * @sta_info: per-station info. Only the first tdls_peer_count entries are set
- * @pti_req_data_offset: offset of network-level data for the PTI template
- * @pti_req_tx_cmd: Tx parameters for PTI request template
- * @pti_req_template: PTI request template data
- */
-struct iwl_tdls_config_cmd {
- __le32 id_and_color; /* mac id and color */
- u8 tdls_peer_count;
- u8 tx_to_ap_tid;
- __le16 tx_to_ap_ssn;
- struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT];
-
- __le32 pti_req_data_offset;
- struct iwl_tx_cmd pti_req_tx_cmd;
- u8 pti_req_template[0];
-} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */
-
-/**
- * TDLS per-station config information from FW
- *
- * @sta_id: station id of the TDLS peer
- * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to
- * the peer
- */
-struct iwl_tdls_config_sta_info_res {
- __le16 sta_id;
- __le16 tx_to_peer_last_seq;
-} __packed; /* TDLS_STA_INFO_RSP_VER_1 */
-
-/**
- * TDLS config information from FW
- *
- * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP
- * @sta_info: per-station TDLS config information
- */
-struct iwl_tdls_config_res {
- __le32 tx_to_ap_last_seq;
- struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT];
-} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */
-
-#define TX_FIFO_MAX_NUM 8
-#define RX_FIFO_MAX_NUM 2
-
-/**
- * Shared memory configuration information from the FW
- *
- * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not
- * accessible)
- * @shared_mem_size: shared memory size
- * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to
- * 0x0 as accessible only via DBGM RDAT)
- * @sample_buff_size: internal sample buff size
- * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre
- * 8000 HW set to 0x0 as not accessible)
- * @txfifo_size: size of TXF0 ... TXF7
- * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0
- * @page_buff_addr: used by UMAC and performance debug (page miss analysis),
- * when paging is not supported this should be 0
- * @page_buff_size: size of %page_buff_addr
- */
-struct iwl_shared_mem_cfg {
- __le32 shared_mem_addr;
- __le32 shared_mem_size;
- __le32 sample_buff_addr;
- __le32 sample_buff_size;
- __le32 txfifo_addr;
- __le32 txfifo_size[TX_FIFO_MAX_NUM];
- __le32 rxfifo_size[RX_FIFO_MAX_NUM];
- __le32 page_buff_addr;
- __le32 page_buff_size;
-} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */
-
-#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
deleted file mode 100644
index d906fa13ba97..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ /dev/null
@@ -1,1166 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <net/mac80211.h>
-
-#include "iwl-trans.h"
-#include "iwl-op-mode.h"
-#include "iwl-fw.h"
-#include "iwl-debug.h"
-#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */
-#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */
-#include "iwl-prph.h"
-#include "iwl-eeprom-parse.h"
-
-#include "mvm.h"
-#include "iwl-phy-db.h"
-
-#define MVM_UCODE_ALIVE_TIMEOUT HZ
-#define MVM_UCODE_CALIB_TIMEOUT (2*HZ)
-
-#define UCODE_VALID_OK cpu_to_le32(0x1)
-
-struct iwl_mvm_alive_data {
- bool valid;
- u32 scd_base_addr;
-};
-
-static inline const struct fw_img *
-iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type)
-{
- if (ucode_type >= IWL_UCODE_TYPE_MAX)
- return NULL;
-
- return &mvm->fw->img[ucode_type];
-}
-
-static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
-{
- struct iwl_tx_ant_cfg_cmd tx_ant_cmd = {
- .valid = cpu_to_le32(valid_tx_ant),
- };
-
- IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
- return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0,
- sizeof(tx_ant_cmd), &tx_ant_cmd);
-}
-
-static void iwl_free_fw_paging(struct iwl_mvm *mvm)
-{
- int i;
-
- if (!mvm->fw_paging_db[0].fw_paging_block)
- return;
-
- for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) {
- if (!mvm->fw_paging_db[i].fw_paging_block) {
- IWL_DEBUG_FW(mvm,
- "Paging: block %d already freed, continue to next page\n",
- i);
-
- continue;
- }
-
- __free_pages(mvm->fw_paging_db[i].fw_paging_block,
- get_order(mvm->fw_paging_db[i].fw_paging_size));
- }
- kfree(mvm->trans->paging_download_buf);
- memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db));
-}
-
-static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image)
-{
- int sec_idx, idx;
- u32 offset = 0;
-
- /*
- * find where is the paging image start point:
- * if CPU2 exist and it's in paging format, then the image looks like:
- * CPU1 sections (2 or more)
- * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
- * CPU2 sections (not paged)
- * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
- * non paged to CPU2 paging sec
- * CPU2 paging CSS
- * CPU2 paging image (including instruction and data)
- */
- for (sec_idx = 0; sec_idx < IWL_UCODE_SECTION_MAX; sec_idx++) {
- if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) {
- sec_idx++;
- break;
- }
- }
-
- if (sec_idx >= IWL_UCODE_SECTION_MAX) {
- IWL_ERR(mvm, "driver didn't find paging image\n");
- iwl_free_fw_paging(mvm);
- return -EINVAL;
- }
-
- /* copy the CSS block to the dram */
- IWL_DEBUG_FW(mvm, "Paging: load paging CSS to FW, sec = %d\n",
- sec_idx);
-
- memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block),
- image->sec[sec_idx].data,
- mvm->fw_paging_db[0].fw_paging_size);
-
- IWL_DEBUG_FW(mvm,
- "Paging: copied %d CSS bytes to first block\n",
- mvm->fw_paging_db[0].fw_paging_size);
-
- sec_idx++;
-
- /*
- * copy the paging blocks to the dram
- * loop index start from 1 since that CSS block already copied to dram
- * and CSS index is 0.
- * loop stop at num_of_paging_blk since that last block is not full.
- */
- for (idx = 1; idx < mvm->num_of_paging_blk; idx++) {
- memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block),
- image->sec[sec_idx].data + offset,
- mvm->fw_paging_db[idx].fw_paging_size);
-
- IWL_DEBUG_FW(mvm,
- "Paging: copied %d paging bytes to block %d\n",
- mvm->fw_paging_db[idx].fw_paging_size,
- idx);
-
- offset += mvm->fw_paging_db[idx].fw_paging_size;
- }
-
- /* copy the last paging block */
- if (mvm->num_of_pages_in_last_blk > 0) {
- memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block),
- image->sec[sec_idx].data + offset,
- FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk);
-
- IWL_DEBUG_FW(mvm,
- "Paging: copied %d pages in the last block %d\n",
- mvm->num_of_pages_in_last_blk, idx);
- }
-
- return 0;
-}
-
-static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm,
- const struct fw_img *image)
-{
- struct page *block;
- dma_addr_t phys = 0;
- int blk_idx = 0;
- int order, num_of_pages;
- int dma_enabled;
-
- if (mvm->fw_paging_db[0].fw_paging_block)
- return 0;
-
- dma_enabled = is_device_dma_capable(mvm->trans->dev);
-
- /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */
- BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE);
-
- num_of_pages = image->paging_mem_size / FW_PAGING_SIZE;
- mvm->num_of_paging_blk = ((num_of_pages - 1) /
- NUM_OF_PAGE_PER_GROUP) + 1;
-
- mvm->num_of_pages_in_last_blk =
- num_of_pages -
- NUM_OF_PAGE_PER_GROUP * (mvm->num_of_paging_blk - 1);
-
- IWL_DEBUG_FW(mvm,
- "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n",
- mvm->num_of_paging_blk,
- mvm->num_of_pages_in_last_blk);
-
- /* allocate block of 4Kbytes for paging CSS */
- order = get_order(FW_PAGING_SIZE);
- block = alloc_pages(GFP_KERNEL, order);
- if (!block) {
- /* free all the previous pages since we failed */
- iwl_free_fw_paging(mvm);
- return -ENOMEM;
- }
-
- mvm->fw_paging_db[blk_idx].fw_paging_block = block;
- mvm->fw_paging_db[blk_idx].fw_paging_size = FW_PAGING_SIZE;
-
- if (dma_enabled) {
- phys = dma_map_page(mvm->trans->dev, block, 0,
- PAGE_SIZE << order, DMA_BIDIRECTIONAL);
- if (dma_mapping_error(mvm->trans->dev, phys)) {
- /*
- * free the previous pages and the current one since
- * we failed to map_page.
- */
- iwl_free_fw_paging(mvm);
- return -ENOMEM;
- }
- mvm->fw_paging_db[blk_idx].fw_paging_phys = phys;
- } else {
- mvm->fw_paging_db[blk_idx].fw_paging_phys = PAGING_ADDR_SIG |
- blk_idx << BLOCK_2_EXP_SIZE;
- }
-
- IWL_DEBUG_FW(mvm,
- "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n",
- order);
-
- /*
- * allocate blocks in dram.
- * since that CSS allocated in fw_paging_db[0] loop start from index 1
- */
- for (blk_idx = 1; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) {
- /* allocate block of PAGING_BLOCK_SIZE (32K) */
- order = get_order(PAGING_BLOCK_SIZE);
- block = alloc_pages(GFP_KERNEL, order);
- if (!block) {
- /* free all the previous pages since we failed */
- iwl_free_fw_paging(mvm);
- return -ENOMEM;
- }
-
- mvm->fw_paging_db[blk_idx].fw_paging_block = block;
- mvm->fw_paging_db[blk_idx].fw_paging_size = PAGING_BLOCK_SIZE;
-
- if (dma_enabled) {
- phys = dma_map_page(mvm->trans->dev, block, 0,
- PAGE_SIZE << order,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(mvm->trans->dev, phys)) {
- /*
- * free the previous pages and the current one
- * since we failed to map_page.
- */
- iwl_free_fw_paging(mvm);
- return -ENOMEM;
- }
- mvm->fw_paging_db[blk_idx].fw_paging_phys = phys;
- } else {
- mvm->fw_paging_db[blk_idx].fw_paging_phys =
- PAGING_ADDR_SIG |
- blk_idx << BLOCK_2_EXP_SIZE;
- }
-
- IWL_DEBUG_FW(mvm,
- "Paging: allocated 32K bytes (order %d) for firmware paging.\n",
- order);
- }
-
- return 0;
-}
-
-static int iwl_save_fw_paging(struct iwl_mvm *mvm,
- const struct fw_img *fw)
-{
- int ret;
-
- ret = iwl_alloc_fw_paging_mem(mvm, fw);
- if (ret)
- return ret;
-
- return iwl_fill_paging_mem(mvm, fw);
-}
-
-/* send paging cmd to FW in case CPU2 has paging image */
-static int iwl_send_paging_cmd(struct iwl_mvm *mvm, const struct fw_img *fw)
-{
- int blk_idx;
- __le32 dev_phy_addr;
- struct iwl_fw_paging_cmd fw_paging_cmd = {
- .flags =
- cpu_to_le32(PAGING_CMD_IS_SECURED |
- PAGING_CMD_IS_ENABLED |
- (mvm->num_of_pages_in_last_blk <<
- PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
- .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE),
- .block_num = cpu_to_le32(mvm->num_of_paging_blk),
- };
-
- /* loop for for all paging blocks + CSS block */
- for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) {
- dev_phy_addr =
- cpu_to_le32(mvm->fw_paging_db[blk_idx].fw_paging_phys >>
- PAGE_2_EXP_SIZE);
- fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr;
- }
-
- return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(FW_PAGING_BLOCK_CMD,
- IWL_ALWAYS_LONG_GROUP, 0),
- 0, sizeof(fw_paging_cmd), &fw_paging_cmd);
-}
-
-/*
- * Send paging item cmd to FW in case CPU2 has paging image
- */
-static int iwl_trans_get_paging_item(struct iwl_mvm *mvm)
-{
- int ret;
- struct iwl_fw_get_item_cmd fw_get_item_cmd = {
- .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING),
- };
-
- struct iwl_fw_get_item_resp *item_resp;
- struct iwl_host_cmd cmd = {
- .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0),
- .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
- .data = { &fw_get_item_cmd, },
- };
-
- cmd.len[0] = sizeof(struct iwl_fw_get_item_cmd);
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (ret) {
- IWL_ERR(mvm,
- "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n",
- ret);
- return ret;
- }
-
- item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data;
- if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) {
- IWL_ERR(mvm,
- "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n",
- le32_to_cpu(item_resp->item_id));
- ret = -EIO;
- goto exit;
- }
-
- mvm->trans->paging_download_buf = kzalloc(MAX_PAGING_IMAGE_SIZE,
- GFP_KERNEL);
- if (!mvm->trans->paging_download_buf) {
- ret = -ENOMEM;
- goto exit;
- }
- mvm->trans->paging_req_addr = le32_to_cpu(item_resp->item_val);
- mvm->trans->paging_db = mvm->fw_paging_db;
- IWL_DEBUG_FW(mvm,
- "Paging: got paging request address (paging_req_addr 0x%08x)\n",
- mvm->trans->paging_req_addr);
-
-exit:
- iwl_free_resp(&cmd);
-
- return ret;
-}
-
-static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_mvm *mvm =
- container_of(notif_wait, struct iwl_mvm, notif_wait);
- struct iwl_mvm_alive_data *alive_data = data;
- struct mvm_alive_resp_ver1 *palive1;
- struct mvm_alive_resp_ver2 *palive2;
- struct mvm_alive_resp *palive;
-
- if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) {
- palive1 = (void *)pkt->data;
-
- mvm->support_umac_log = false;
- mvm->error_event_table =
- le32_to_cpu(palive1->error_event_table_ptr);
- mvm->log_event_table =
- le32_to_cpu(palive1->log_event_table_ptr);
- alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr);
-
- alive_data->valid = le16_to_cpu(palive1->status) ==
- IWL_ALIVE_STATUS_OK;
- IWL_DEBUG_FW(mvm,
- "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
- le16_to_cpu(palive1->status), palive1->ver_type,
- palive1->ver_subtype, palive1->flags);
- } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) {
- palive2 = (void *)pkt->data;
-
- mvm->error_event_table =
- le32_to_cpu(palive2->error_event_table_ptr);
- mvm->log_event_table =
- le32_to_cpu(palive2->log_event_table_ptr);
- alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
- mvm->umac_error_event_table =
- le32_to_cpu(palive2->error_info_addr);
- mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr);
- mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size);
-
- alive_data->valid = le16_to_cpu(palive2->status) ==
- IWL_ALIVE_STATUS_OK;
- if (mvm->umac_error_event_table)
- mvm->support_umac_log = true;
-
- IWL_DEBUG_FW(mvm,
- "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
- le16_to_cpu(palive2->status), palive2->ver_type,
- palive2->ver_subtype, palive2->flags);
-
- IWL_DEBUG_FW(mvm,
- "UMAC version: Major - 0x%x, Minor - 0x%x\n",
- palive2->umac_major, palive2->umac_minor);
- } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
- palive = (void *)pkt->data;
-
- mvm->error_event_table =
- le32_to_cpu(palive->error_event_table_ptr);
- mvm->log_event_table =
- le32_to_cpu(palive->log_event_table_ptr);
- alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
- mvm->umac_error_event_table =
- le32_to_cpu(palive->error_info_addr);
- mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr);
- mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size);
-
- alive_data->valid = le16_to_cpu(palive->status) ==
- IWL_ALIVE_STATUS_OK;
- if (mvm->umac_error_event_table)
- mvm->support_umac_log = true;
-
- IWL_DEBUG_FW(mvm,
- "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
- le16_to_cpu(palive->status), palive->ver_type,
- palive->ver_subtype, palive->flags);
-
- IWL_DEBUG_FW(mvm,
- "UMAC version: Major - 0x%x, Minor - 0x%x\n",
- le32_to_cpu(palive->umac_major),
- le32_to_cpu(palive->umac_minor));
- }
-
- return true;
-}
-
-static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_phy_db *phy_db = data;
-
- if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) {
- WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF);
- return true;
- }
-
- WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC));
-
- return false;
-}
-
-static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
- enum iwl_ucode_type ucode_type)
-{
- struct iwl_notification_wait alive_wait;
- struct iwl_mvm_alive_data alive_data;
- const struct fw_img *fw;
- int ret, i;
- enum iwl_ucode_type old_type = mvm->cur_ucode;
- static const u16 alive_cmd[] = { MVM_ALIVE };
- struct iwl_sf_region st_fwrd_space;
-
- if (ucode_type == IWL_UCODE_REGULAR &&
- iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))
- fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
- else
- fw = iwl_get_ucode_image(mvm, ucode_type);
- if (WARN_ON(!fw))
- return -EINVAL;
- mvm->cur_ucode = ucode_type;
- mvm->ucode_loaded = false;
-
- iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
- alive_cmd, ARRAY_SIZE(alive_cmd),
- iwl_alive_fn, &alive_data);
-
- ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT);
- if (ret) {
- mvm->cur_ucode = old_type;
- iwl_remove_notification(&mvm->notif_wait, &alive_wait);
- return ret;
- }
-
- /*
- * Some things may run in the background now, but we
- * just wait for the ALIVE notification here.
- */
- ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait,
- MVM_UCODE_ALIVE_TIMEOUT);
- if (ret) {
- if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
- IWL_ERR(mvm,
- "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
- iwl_read_prph(mvm->trans, SB_CPU_1_STATUS),
- iwl_read_prph(mvm->trans, SB_CPU_2_STATUS));
- mvm->cur_ucode = old_type;
- return ret;
- }
-
- if (!alive_data.valid) {
- IWL_ERR(mvm, "Loaded ucode is not valid!\n");
- mvm->cur_ucode = old_type;
- return -EIO;
- }
-
- /*
- * update the sdio allocation according to the pointer we get in the
- * alive notification.
- */
- st_fwrd_space.addr = mvm->sf_space.addr;
- st_fwrd_space.size = mvm->sf_space.size;
- ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space);
- if (ret) {
- IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret);
- return ret;
- }
-
- iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
-
- /*
- * configure and operate fw paging mechanism.
- * driver configures the paging flow only once, CPU2 paging image
- * included in the IWL_UCODE_INIT image.
- */
- if (fw->paging_mem_size) {
- /*
- * When dma is not enabled, the driver needs to copy / write
- * the downloaded / uploaded page to / from the smem.
- * This gets the location of the place were the pages are
- * stored.
- */
- if (!is_device_dma_capable(mvm->trans->dev)) {
- ret = iwl_trans_get_paging_item(mvm);
- if (ret) {
- IWL_ERR(mvm, "failed to get FW paging item\n");
- return ret;
- }
- }
-
- ret = iwl_save_fw_paging(mvm, fw);
- if (ret) {
- IWL_ERR(mvm, "failed to save the FW paging image\n");
- return ret;
- }
-
- ret = iwl_send_paging_cmd(mvm, fw);
- if (ret) {
- IWL_ERR(mvm, "failed to send the paging cmd\n");
- iwl_free_fw_paging(mvm);
- return ret;
- }
- }
-
- /*
- * Note: all the queues are enabled as part of the interface
- * initialization, but in firmware restart scenarios they
- * could be stopped, so wake them up. In firmware restart,
- * mac80211 will have the queues stopped as well until the
- * reconfiguration completes. During normal startup, they
- * will be empty.
- */
-
- memset(&mvm->queue_info, 0, sizeof(mvm->queue_info));
- mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1;
-
- for (i = 0; i < IEEE80211_MAX_QUEUES; i++)
- atomic_set(&mvm->mac80211_queue_stop_count[i], 0);
-
- mvm->ucode_loaded = true;
-
- return 0;
-}
-
-static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
-{
- struct iwl_phy_cfg_cmd phy_cfg_cmd;
- enum iwl_ucode_type ucode_type = mvm->cur_ucode;
-
- /* Set parameters */
- phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm));
- phy_cfg_cmd.calib_control.event_trigger =
- mvm->fw->default_calib[ucode_type].event_trigger;
- phy_cfg_cmd.calib_control.flow_trigger =
- mvm->fw->default_calib[ucode_type].flow_trigger;
-
- IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
- phy_cfg_cmd.phy_cfg);
-
- return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0,
- sizeof(phy_cfg_cmd), &phy_cfg_cmd);
-}
-
-int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
-{
- struct iwl_notification_wait calib_wait;
- static const u16 init_complete[] = {
- INIT_COMPLETE_NOTIF,
- CALIB_RES_NOTIF_PHY_DB
- };
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (WARN_ON_ONCE(mvm->calibrating))
- return 0;
-
- iwl_init_notification_wait(&mvm->notif_wait,
- &calib_wait,
- init_complete,
- ARRAY_SIZE(init_complete),
- iwl_wait_phy_db_entry,
- mvm->phy_db);
-
- /* Will also start the device */
- ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT);
- if (ret) {
- IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret);
- goto error;
- }
-
- ret = iwl_send_bt_init_conf(mvm);
- if (ret)
- goto error;
-
- /* Read the NVM only at driver load time, no need to do this twice */
- if (read_nvm) {
- /* Read nvm */
- ret = iwl_nvm_init(mvm, true);
- if (ret) {
- IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
- goto error;
- }
- }
-
- /* In case we read the NVM from external file, load it to the NIC */
- if (mvm->nvm_file_name)
- iwl_mvm_load_nvm_to_nic(mvm);
-
- ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
- WARN_ON(ret);
-
- /*
- * abort after reading the nvm in case RF Kill is on, we will complete
- * the init seq later when RF kill will switch to off
- */
- if (iwl_mvm_is_radio_hw_killed(mvm)) {
- IWL_DEBUG_RF_KILL(mvm,
- "jump over all phy activities due to RF kill\n");
- iwl_remove_notification(&mvm->notif_wait, &calib_wait);
- ret = 1;
- goto out;
- }
-
- mvm->calibrating = true;
-
- /* Send TX valid antennas before triggering calibrations */
- ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
- if (ret)
- goto error;
-
- /*
- * Send phy configurations command to init uCode
- * to start the 16.0 uCode init image internal calibrations.
- */
- ret = iwl_send_phy_cfg_cmd(mvm);
- if (ret) {
- IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n",
- ret);
- goto error;
- }
-
- /*
- * Some things may run in the background now, but we
- * just wait for the calibration complete notification.
- */
- ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
- MVM_UCODE_CALIB_TIMEOUT);
-
- if (ret && iwl_mvm_is_radio_hw_killed(mvm)) {
- IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
- ret = 1;
- }
- goto out;
-
-error:
- iwl_remove_notification(&mvm->notif_wait, &calib_wait);
-out:
- mvm->calibrating = false;
- if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
- /* we want to debug INIT and we have no NVM - fake */
- mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
- sizeof(struct ieee80211_channel) +
- sizeof(struct ieee80211_rate),
- GFP_KERNEL);
- if (!mvm->nvm_data)
- return -ENOMEM;
- mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels;
- mvm->nvm_data->bands[0].n_channels = 1;
- mvm->nvm_data->bands[0].n_bitrates = 1;
- mvm->nvm_data->bands[0].bitrates =
- (void *)mvm->nvm_data->channels + 1;
- mvm->nvm_data->bands[0].bitrates->hw_value = 10;
- }
-
- return ret;
-}
-
-static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm)
-{
- struct iwl_host_cmd cmd = {
- .id = SHARED_MEM_CFG,
- .flags = CMD_WANT_SKB,
- .data = { NULL, },
- .len = { 0, },
- };
- struct iwl_rx_packet *pkt;
- struct iwl_shared_mem_cfg *mem_cfg;
- u32 i;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd)))
- return;
-
- pkt = cmd.resp_pkt;
- mem_cfg = (void *)pkt->data;
-
- mvm->shared_mem_cfg.shared_mem_addr =
- le32_to_cpu(mem_cfg->shared_mem_addr);
- mvm->shared_mem_cfg.shared_mem_size =
- le32_to_cpu(mem_cfg->shared_mem_size);
- mvm->shared_mem_cfg.sample_buff_addr =
- le32_to_cpu(mem_cfg->sample_buff_addr);
- mvm->shared_mem_cfg.sample_buff_size =
- le32_to_cpu(mem_cfg->sample_buff_size);
- mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr);
- for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++)
- mvm->shared_mem_cfg.txfifo_size[i] =
- le32_to_cpu(mem_cfg->txfifo_size[i]);
- for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++)
- mvm->shared_mem_cfg.rxfifo_size[i] =
- le32_to_cpu(mem_cfg->rxfifo_size[i]);
- mvm->shared_mem_cfg.page_buff_addr =
- le32_to_cpu(mem_cfg->page_buff_addr);
- mvm->shared_mem_cfg.page_buff_size =
- le32_to_cpu(mem_cfg->page_buff_size);
- IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n");
-
- iwl_free_resp(&cmd);
-}
-
-int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
- struct iwl_mvm_dump_desc *desc,
- struct iwl_fw_dbg_trigger_tlv *trigger)
-{
- unsigned int delay = 0;
-
- if (trigger)
- delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
-
- if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
- return -EBUSY;
-
- if (WARN_ON(mvm->fw_dump_desc))
- iwl_mvm_free_fw_dump_desc(mvm);
-
- IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
- le32_to_cpu(desc->trig_desc.type));
-
- mvm->fw_dump_desc = desc;
- mvm->fw_dump_trig = trigger;
-
- queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
-
- return 0;
-}
-
-int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
- const char *str, size_t len,
- struct iwl_fw_dbg_trigger_tlv *trigger)
-{
- struct iwl_mvm_dump_desc *desc;
-
- desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
- if (!desc)
- return -ENOMEM;
-
- desc->len = len;
- desc->trig_desc.type = cpu_to_le32(trig);
- memcpy(desc->trig_desc.data, str, len);
-
- return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger);
-}
-
-int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
- struct iwl_fw_dbg_trigger_tlv *trigger,
- const char *fmt, ...)
-{
- u16 occurrences = le16_to_cpu(trigger->occurrences);
- int ret, len = 0;
- char buf[64];
-
- if (!occurrences)
- return 0;
-
- if (fmt) {
- va_list ap;
-
- buf[sizeof(buf) - 1] = '\0';
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-
- /* check for truncation */
- if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
- buf[sizeof(buf) - 1] = '\0';
-
- len = strlen(buf) + 1;
- }
-
- ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len,
- trigger);
-
- if (ret)
- return ret;
-
- trigger->occurrences = cpu_to_le16(occurrences - 1);
- return 0;
-}
-
-static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
-{
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
- iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
- else
- iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
-}
-
-int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
-{
- u8 *ptr;
- int ret;
- int i;
-
- if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
- "Invalid configuration %d\n", conf_id))
- return -EINVAL;
-
- /* EARLY START - firmware's configuration is hard coded */
- if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
- !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
- conf_id == FW_DBG_START_FROM_ALIVE) {
- iwl_mvm_restart_early_start(mvm);
- return 0;
- }
-
- if (!mvm->fw->dbg_conf_tlv[conf_id])
- return -EINVAL;
-
- if (mvm->fw_dbg_conf != FW_DBG_INVALID)
- IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
- mvm->fw_dbg_conf);
-
- /* Send all HCMDs for configuring the FW debug */
- ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
- for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
- struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
-
- ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
- le16_to_cpu(cmd->len), cmd->data);
- if (ret)
- return ret;
-
- ptr += sizeof(*cmd);
- ptr += le16_to_cpu(cmd->len);
- }
-
- mvm->fw_dbg_conf = conf_id;
- return ret;
-}
-
-static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
-{
- struct iwl_ltr_config_cmd cmd = {
- .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
- };
-
- if (!mvm->trans->ltr_enabled)
- return 0;
-
- return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
- sizeof(cmd), &cmd);
-}
-
-int iwl_mvm_up(struct iwl_mvm *mvm)
-{
- int ret, i;
- struct ieee80211_channel *chan;
- struct cfg80211_chan_def chandef;
-
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_trans_start_hw(mvm->trans);
- if (ret)
- return ret;
-
- /*
- * If we haven't completed the run of the init ucode during
- * module loading, load init ucode now
- * (for example, if we were in RFKILL)
- */
- ret = iwl_run_init_mvm_ucode(mvm, false);
- if (ret && !iwlmvm_mod_params.init_dbg) {
- IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
- /* this can't happen */
- if (WARN_ON(ret > 0))
- ret = -ERFKILL;
- goto error;
- }
- if (!iwlmvm_mod_params.init_dbg) {
- /*
- * Stop and start the transport without entering low power
- * mode. This will save the state of other components on the
- * device that are triggered by the INIT firwmare (MFUART).
- */
- _iwl_trans_stop_device(mvm->trans, false);
- ret = _iwl_trans_start_hw(mvm->trans, false);
- if (ret)
- goto error;
- }
-
- if (iwlmvm_mod_params.init_dbg)
- return 0;
-
- ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
- if (ret) {
- IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
- goto error;
- }
-
- iwl_mvm_get_shared_mem_conf(mvm);
-
- ret = iwl_mvm_sf_update(mvm, NULL, false);
- if (ret)
- IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
-
- mvm->fw_dbg_conf = FW_DBG_INVALID;
- /* if we have a destination, assume EARLY START */
- if (mvm->fw->dbg_dest_tlv)
- mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE;
- iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE);
-
- ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
- if (ret)
- goto error;
-
- ret = iwl_send_bt_init_conf(mvm);
- if (ret)
- goto error;
-
- /* Send phy db control command and then phy db calibration*/
- ret = iwl_send_phy_db_data(mvm->phy_db);
- if (ret)
- goto error;
-
- ret = iwl_send_phy_cfg_cmd(mvm);
- if (ret)
- goto error;
-
- /* init the fw <-> mac80211 STA mapping */
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
- RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
-
- mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
-
- /* reset quota debouncing buffer - 0xff will yield invalid data */
- memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd));
-
- /* Add auxiliary station for scanning */
- ret = iwl_mvm_add_aux_sta(mvm);
- if (ret)
- goto error;
-
- /* Add all the PHY contexts */
- chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
- cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
- for (i = 0; i < NUM_PHY_CTX; i++) {
- /*
- * The channel used here isn't relevant as it's
- * going to be overwritten in the other flows.
- * For now use the first channel we have.
- */
- ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i],
- &chandef, 1, 1);
- if (ret)
- goto error;
- }
-
- /* Initialize tx backoffs to the minimal possible */
- iwl_mvm_tt_tx_backoff(mvm, 0);
-
- WARN_ON(iwl_mvm_config_ltr(mvm));
-
- ret = iwl_mvm_power_update_device(mvm);
- if (ret)
- goto error;
-
- /*
- * RTNL is not taken during Ct-kill, but we don't need to scan/Tx
- * anyway, so don't init MCC.
- */
- if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) {
- ret = iwl_mvm_init_mcc(mvm);
- if (ret)
- goto error;
- }
-
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
- ret = iwl_mvm_config_scan(mvm);
- if (ret)
- goto error;
- }
-
- if (iwl_mvm_is_csum_supported(mvm) &&
- mvm->cfg->features & NETIF_F_RXCSUM)
- iwl_trans_write_prph(mvm->trans, RX_EN_CSUM, 0x3);
-
- /* allow FW/transport low power modes if not during restart */
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
-
- IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
- return 0;
- error:
- iwl_trans_stop_device(mvm->trans);
- return ret;
-}
-
-int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
-{
- int ret, i;
-
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_trans_start_hw(mvm->trans);
- if (ret)
- return ret;
-
- ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN);
- if (ret) {
- IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret);
- goto error;
- }
-
- ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
- if (ret)
- goto error;
-
- /* Send phy db control command and then phy db calibration*/
- ret = iwl_send_phy_db_data(mvm->phy_db);
- if (ret)
- goto error;
-
- ret = iwl_send_phy_cfg_cmd(mvm);
- if (ret)
- goto error;
-
- /* init the fw <-> mac80211 STA mapping */
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
- RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
-
- /* Add auxiliary station for scanning */
- ret = iwl_mvm_add_aux_sta(mvm);
- if (ret)
- goto error;
-
- return 0;
- error:
- iwl_trans_stop_device(mvm->trans);
- return ret;
-}
-
-void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_card_state_notif *card_state_notif = (void *)pkt->data;
- u32 flags = le32_to_cpu(card_state_notif->flags);
-
- IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n",
- (flags & HW_CARD_DISABLED) ? "Kill" : "On",
- (flags & SW_CARD_DISABLED) ? "Kill" : "On",
- (flags & CT_KILL_CARD_DISABLED) ?
- "Reached" : "Not reached");
-}
-
-void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
-
- IWL_DEBUG_INFO(mvm,
- "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n",
- le32_to_cpu(mfuart_notif->installed_ver),
- le32_to_cpu(mfuart_notif->external_ver),
- le32_to_cpu(mfuart_notif->status),
- le32_to_cpu(mfuart_notif->duration));
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c
deleted file mode 100644
index e3b3cf4dbd77..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/led.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/leds.h>
-#include "iwl-io.h"
-#include "iwl-csr.h"
-#include "mvm.h"
-
-/* Set led register on */
-static void iwl_mvm_led_enable(struct iwl_mvm *mvm)
-{
- iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
-}
-
-/* Set led register off */
-static void iwl_mvm_led_disable(struct iwl_mvm *mvm)
-{
- iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF);
-}
-
-static void iwl_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led);
- if (brightness > 0)
- iwl_mvm_led_enable(mvm);
- else
- iwl_mvm_led_disable(mvm);
-}
-
-int iwl_mvm_leds_init(struct iwl_mvm *mvm)
-{
- int mode = iwlwifi_mod_params.led_mode;
- int ret;
-
- switch (mode) {
- case IWL_LED_BLINK:
- IWL_ERR(mvm, "Blink led mode not supported, used default\n");
- case IWL_LED_DEFAULT:
- case IWL_LED_RF_STATE:
- mode = IWL_LED_RF_STATE;
- break;
- case IWL_LED_DISABLE:
- IWL_INFO(mvm, "Led disabled\n");
- return 0;
- default:
- return -EINVAL;
- }
-
- mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
- wiphy_name(mvm->hw->wiphy));
- mvm->led.brightness_set = iwl_led_brightness_set;
- mvm->led.max_brightness = 1;
-
- if (mode == IWL_LED_RF_STATE)
- mvm->led.default_trigger =
- ieee80211_get_radio_led_name(mvm->hw);
-
- ret = led_classdev_register(mvm->trans->dev, &mvm->led);
- if (ret) {
- kfree(mvm->led.name);
- IWL_INFO(mvm, "Failed to enable led\n");
- return ret;
- }
-
- return 0;
-}
-
-void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
-{
- if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE)
- return;
-
- led_classdev_unregister(&mvm->led);
- kfree(mvm->led.name);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
deleted file mode 100644
index ad7ad720d2e7..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ /dev/null
@@ -1,1452 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/etherdevice.h>
-#include <net/mac80211.h>
-#include "iwl-io.h"
-#include "iwl-prph.h"
-#include "fw-api.h"
-#include "mvm.h"
-#include "time-event.h"
-
-const u8 iwl_mvm_ac_to_tx_fifo[] = {
- IWL_MVM_TX_FIFO_VO,
- IWL_MVM_TX_FIFO_VI,
- IWL_MVM_TX_FIFO_BE,
- IWL_MVM_TX_FIFO_BK,
-};
-
-struct iwl_mvm_mac_iface_iterator_data {
- struct iwl_mvm *mvm;
- struct ieee80211_vif *vif;
- unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
- unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
- enum iwl_tsf_id preferred_tsf;
- bool found_vif;
-};
-
-struct iwl_mvm_hw_queues_iface_iterator_data {
- struct ieee80211_vif *exclude_vif;
- unsigned long used_hw_queues;
-};
-
-static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_mac_iface_iterator_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- u16 min_bi;
-
- /* Skip the interface for which we are trying to assign a tsf_id */
- if (vif == data->vif)
- return;
-
- /*
- * The TSF is a hardware/firmware resource, there are 4 and
- * the driver should assign and free them as needed. However,
- * there are cases where 2 MACs should share the same TSF ID
- * for the purpose of clock sync, an optimization to avoid
- * clock drift causing overlapping TBTTs/DTIMs for a GO and
- * client in the system.
- *
- * The firmware will decide according to the MAC type which
- * will be the master and slave. Clients that need to sync
- * with a remote station will be the master, and an AP or GO
- * will be the slave.
- *
- * Depending on the new interface type it can be slaved to
- * or become the master of an existing interface.
- */
- switch (data->vif->type) {
- case NL80211_IFTYPE_STATION:
- /*
- * The new interface is a client, so if the one we're iterating
- * is an AP, and the beacon interval of the AP is a multiple or
- * divisor of the beacon interval of the client, the same TSF
- * should be used to avoid drift between the new client and
- * existing AP. The existing AP will get drift updates from the
- * new client context in this case.
- */
- if (vif->type != NL80211_IFTYPE_AP ||
- data->preferred_tsf != NUM_TSF_IDS ||
- !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
- break;
-
- min_bi = min(data->vif->bss_conf.beacon_int,
- vif->bss_conf.beacon_int);
-
- if (!min_bi)
- break;
-
- if ((data->vif->bss_conf.beacon_int -
- vif->bss_conf.beacon_int) % min_bi == 0) {
- data->preferred_tsf = mvmvif->tsf_id;
- return;
- }
- break;
-
- case NL80211_IFTYPE_AP:
- /*
- * The new interface is AP/GO, so if its beacon interval is a
- * multiple or a divisor of the beacon interval of an existing
- * interface, it should get drift updates from an existing
- * client or use the same TSF as an existing GO. There's no
- * drift between TSFs internally but if they used different
- * TSFs then a new client MAC could update one of them and
- * cause drift that way.
- */
- if ((vif->type != NL80211_IFTYPE_AP &&
- vif->type != NL80211_IFTYPE_STATION) ||
- data->preferred_tsf != NUM_TSF_IDS ||
- !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
- break;
-
- min_bi = min(data->vif->bss_conf.beacon_int,
- vif->bss_conf.beacon_int);
-
- if (!min_bi)
- break;
-
- if ((data->vif->bss_conf.beacon_int -
- vif->bss_conf.beacon_int) % min_bi == 0) {
- data->preferred_tsf = mvmvif->tsf_id;
- return;
- }
- break;
- default:
- /*
- * For all other interface types there's no need to
- * take drift into account. Either they're exclusive
- * like IBSS and monitor, or we don't care much about
- * their TSF (like P2P Device), but we won't be able
- * to share the TSF resource.
- */
- break;
- }
-
- /*
- * Unless we exited above, we can't share the TSF resource
- * that the virtual interface we're iterating over is using
- * with the new one, so clear the available bit and if this
- * was the preferred one, reset that as well.
- */
- __clear_bit(mvmvif->tsf_id, data->available_tsf_ids);
-
- if (data->preferred_tsf == mvmvif->tsf_id)
- data->preferred_tsf = NUM_TSF_IDS;
-}
-
-/*
- * Get the mask of the queues used by the vif
- */
-u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif)
-{
- u32 qmask = 0, ac;
-
- if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
- return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
- if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
- qmask |= BIT(vif->hw_queue[ac]);
- }
-
- if (vif->type == NL80211_IFTYPE_AP)
- qmask |= BIT(vif->cab_queue);
-
- return qmask;
-}
-
-static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
-
- /* exclude the given vif */
- if (vif == data->exclude_vif)
- return;
-
- data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
-}
-
-static void iwl_mvm_mac_sta_hw_queues_iter(void *_data,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-
- /* Mark the queues used by the sta */
- data->used_hw_queues |= mvmsta->tfd_queue_msk;
-}
-
-unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
- struct ieee80211_vif *exclude_vif)
-{
- u8 sta_id;
- struct iwl_mvm_hw_queues_iface_iterator_data data = {
- .exclude_vif = exclude_vif,
- .used_hw_queues =
- BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
- BIT(mvm->aux_queue) |
- BIT(IWL_MVM_CMD_QUEUE),
- };
-
- lockdep_assert_held(&mvm->mutex);
-
- /* mark all VIF used hw queues */
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- iwl_mvm_iface_hw_queues_iter, &data);
-
- /* don't assign the same hw queues as TDLS stations */
- ieee80211_iterate_stations_atomic(mvm->hw,
- iwl_mvm_mac_sta_hw_queues_iter,
- &data);
-
- /*
- * Some TDLS stations may be removed but are in the process of being
- * drained. Don't touch their queues.
- */
- for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT)
- data.used_hw_queues |= mvm->tfd_drained[sta_id];
-
- return data.used_hw_queues;
-}
-
-static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_mac_iface_iterator_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- /* Iterator may already find the interface being added -- skip it */
- if (vif == data->vif) {
- data->found_vif = true;
- return;
- }
-
- /* Mark MAC IDs as used by clearing the available bit, and
- * (below) mark TSFs as used if their existing use is not
- * compatible with the new interface type.
- * No locking or atomic bit operations are needed since the
- * data is on the stack of the caller function.
- */
- __clear_bit(mvmvif->id, data->available_mac_ids);
-
- /* find a suitable tsf_id */
- iwl_mvm_mac_tsf_id_iter(_data, mac, vif);
-}
-
-void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_mac_iface_iterator_data data = {
- .mvm = mvm,
- .vif = vif,
- .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
- /* no preference yet */
- .preferred_tsf = NUM_TSF_IDS,
- };
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- iwl_mvm_mac_tsf_id_iter, &data);
-
- if (data.preferred_tsf != NUM_TSF_IDS)
- mvmvif->tsf_id = data.preferred_tsf;
- else if (!test_bit(mvmvif->tsf_id, data.available_tsf_ids))
- mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
- NUM_TSF_IDS);
-}
-
-static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_mac_iface_iterator_data data = {
- .mvm = mvm,
- .vif = vif,
- .available_mac_ids = { (1 << NUM_MAC_INDEX_DRIVER) - 1 },
- .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
- /* no preference yet */
- .preferred_tsf = NUM_TSF_IDS,
- .found_vif = false,
- };
- u32 ac;
- int ret, i;
- unsigned long used_hw_queues;
-
- /*
- * Allocate a MAC ID and a TSF for this MAC, along with the queues
- * and other resources.
- */
-
- /*
- * Before the iterator, we start with all MAC IDs and TSFs available.
- *
- * During iteration, all MAC IDs are cleared that are in use by other
- * virtual interfaces, and all TSF IDs are cleared that can't be used
- * by this new virtual interface because they're used by an interface
- * that can't share it with the new one.
- * At the same time, we check if there's a preferred TSF in the case
- * that we should share it with another interface.
- */
-
- /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */
- switch (vif->type) {
- case NL80211_IFTYPE_ADHOC:
- break;
- case NL80211_IFTYPE_STATION:
- if (!vif->p2p)
- break;
- /* fall through */
- default:
- __clear_bit(0, data.available_mac_ids);
- }
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- iwl_mvm_mac_iface_iterator, &data);
-
- used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif);
-
- /*
- * In the case we're getting here during resume, it's similar to
- * firmware restart, and with RESUME_ALL the iterator will find
- * the vif being added already.
- * We don't want to reassign any IDs in either case since doing
- * so would probably assign different IDs (as interfaces aren't
- * necessarily added in the same order), but the old IDs were
- * preserved anyway, so skip ID assignment for both resume and
- * recovery.
- */
- if (data.found_vif)
- return 0;
-
- /* Therefore, in recovery, we can't get here */
- if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
- return -EBUSY;
-
- mvmvif->id = find_first_bit(data.available_mac_ids,
- NUM_MAC_INDEX_DRIVER);
- if (mvmvif->id == NUM_MAC_INDEX_DRIVER) {
- IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n");
- ret = -EIO;
- goto exit_fail;
- }
-
- if (data.preferred_tsf != NUM_TSF_IDS)
- mvmvif->tsf_id = data.preferred_tsf;
- else
- mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
- NUM_TSF_IDS);
- if (mvmvif->tsf_id == NUM_TSF_IDS) {
- IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n");
- ret = -EIO;
- goto exit_fail;
- }
-
- mvmvif->color = 0;
-
- INIT_LIST_HEAD(&mvmvif->time_event_data.list);
- mvmvif->time_event_data.id = TE_MAX;
-
- /* No need to allocate data queues to P2P Device MAC.*/
- if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
- vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
-
- return 0;
- }
-
- /* Find available queues, and allocate them to the ACs */
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
- u8 queue = find_first_zero_bit(&used_hw_queues,
- mvm->first_agg_queue);
-
- if (queue >= mvm->first_agg_queue) {
- IWL_ERR(mvm, "Failed to allocate queue\n");
- ret = -EIO;
- goto exit_fail;
- }
-
- __set_bit(queue, &used_hw_queues);
- vif->hw_queue[ac] = queue;
- }
-
- /* Allocate the CAB queue for softAP and GO interfaces */
- if (vif->type == NL80211_IFTYPE_AP) {
- u8 queue = find_first_zero_bit(&used_hw_queues,
- mvm->first_agg_queue);
-
- if (queue >= mvm->first_agg_queue) {
- IWL_ERR(mvm, "Failed to allocate cab queue\n");
- ret = -EIO;
- goto exit_fail;
- }
-
- vif->cab_queue = queue;
- } else {
- vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
- }
-
- mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT;
- mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
-
- for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++)
- mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC;
-
- return 0;
-
-exit_fail:
- memset(mvmvif, 0, sizeof(struct iwl_mvm_vif));
- memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue));
- vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
- return ret;
-}
-
-int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- unsigned int wdg_timeout =
- iwl_mvm_get_wd_timeout(mvm, vif, false, false);
- u32 ac;
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif);
- if (ret)
- return ret;
-
- switch (vif->type) {
- case NL80211_IFTYPE_P2P_DEVICE:
- iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE,
- IWL_MVM_OFFCHANNEL_QUEUE,
- IWL_MVM_TX_FIFO_VO, 0, wdg_timeout);
- break;
- case NL80211_IFTYPE_AP:
- iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue,
- IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout);
- /* fall through */
- default:
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
- iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac],
- vif->hw_queue[ac],
- iwl_mvm_ac_to_tx_fifo[ac], 0,
- wdg_timeout);
- break;
- }
-
- return 0;
-}
-
-void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- int ac;
-
- lockdep_assert_held(&mvm->mutex);
-
- switch (vif->type) {
- case NL80211_IFTYPE_P2P_DEVICE:
- iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE,
- IWL_MVM_OFFCHANNEL_QUEUE, IWL_MAX_TID_COUNT,
- 0);
- break;
- case NL80211_IFTYPE_AP:
- iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue,
- IWL_MAX_TID_COUNT, 0);
- /* fall through */
- default:
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
- iwl_mvm_disable_txq(mvm, vif->hw_queue[ac],
- vif->hw_queue[ac],
- IWL_MAX_TID_COUNT, 0);
- }
-}
-
-static void iwl_mvm_ack_rates(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- enum ieee80211_band band,
- u8 *cck_rates, u8 *ofdm_rates)
-{
- struct ieee80211_supported_band *sband;
- unsigned long basic = vif->bss_conf.basic_rates;
- int lowest_present_ofdm = 100;
- int lowest_present_cck = 100;
- u8 cck = 0;
- u8 ofdm = 0;
- int i;
-
- sband = mvm->hw->wiphy->bands[band];
-
- for_each_set_bit(i, &basic, BITS_PER_LONG) {
- int hw = sband->bitrates[i].hw_value;
- if (hw >= IWL_FIRST_OFDM_RATE) {
- ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
- if (lowest_present_ofdm > hw)
- lowest_present_ofdm = hw;
- } else {
- BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
-
- cck |= BIT(hw);
- if (lowest_present_cck > hw)
- lowest_present_cck = hw;
- }
- }
-
- /*
- * Now we've got the basic rates as bitmaps in the ofdm and cck
- * variables. This isn't sufficient though, as there might not
- * be all the right rates in the bitmap. E.g. if the only basic
- * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
- * and 6 Mbps because the 802.11-2007 standard says in 9.6:
- *
- * [...] a STA responding to a received frame shall transmit
- * its Control Response frame [...] at the highest rate in the
- * BSSBasicRateSet parameter that is less than or equal to the
- * rate of the immediately previous frame in the frame exchange
- * sequence ([...]) and that is of the same modulation class
- * ([...]) as the received frame. If no rate contained in the
- * BSSBasicRateSet parameter meets these conditions, then the
- * control frame sent in response to a received frame shall be
- * transmitted at the highest mandatory rate of the PHY that is
- * less than or equal to the rate of the received frame, and
- * that is of the same modulation class as the received frame.
- *
- * As a consequence, we need to add all mandatory rates that are
- * lower than all of the basic rates to these bitmaps.
- */
-
- if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
- ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
- if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
- ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
- /* 6M already there or needed so always add */
- ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
-
- /*
- * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
- * Note, however:
- * - if no CCK rates are basic, it must be ERP since there must
- * be some basic rates at all, so they're OFDM => ERP PHY
- * (or we're in 5 GHz, and the cck bitmap will never be used)
- * - if 11M is a basic rate, it must be ERP as well, so add 5.5M
- * - if 5.5M is basic, 1M and 2M are mandatory
- * - if 2M is basic, 1M is mandatory
- * - if 1M is basic, that's the only valid ACK rate.
- * As a consequence, it's not as complicated as it sounds, just add
- * any lower rates to the ACK rate bitmap.
- */
- if (IWL_RATE_11M_INDEX < lowest_present_cck)
- cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE;
- if (IWL_RATE_5M_INDEX < lowest_present_cck)
- cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE;
- if (IWL_RATE_2M_INDEX < lowest_present_cck)
- cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE;
- /* 1M already there or needed so always add */
- cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE;
-
- *cck_rates = cck;
- *ofdm_rates = ofdm;
-}
-
-static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mac_ctx_cmd *cmd)
-{
- /* for both sta and ap, ht_operation_mode hold the protection_mode */
- u8 protection_mode = vif->bss_conf.ht_operation_mode &
- IEEE80211_HT_OP_MODE_PROTECTION;
- /* The fw does not distinguish between ht and fat */
- u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT;
-
- IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode);
- /*
- * See section 9.23.3.1 of IEEE 80211-2012.
- * Nongreenfield HT STAs Present is not supported.
- */
- switch (protection_mode) {
- case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
- break;
- case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
- case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
- cmd->protection_flags |= cpu_to_le32(ht_flag);
- break;
- case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
- /* Protect when channel wider than 20MHz */
- if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20)
- cmd->protection_flags |= cpu_to_le32(ht_flag);
- break;
- default:
- IWL_ERR(mvm, "Illegal protection mode %d\n",
- protection_mode);
- break;
- }
-}
-
-static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mac_ctx_cmd *cmd,
- const u8 *bssid_override,
- u32 action)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct ieee80211_chanctx_conf *chanctx;
- bool ht_enabled = !!(vif->bss_conf.ht_operation_mode &
- IEEE80211_HT_OP_MODE_PROTECTION);
- u8 cck_ack_rates, ofdm_ack_rates;
- const u8 *bssid = bssid_override ?: vif->bss_conf.bssid;
- int i;
-
- cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color));
- cmd->action = cpu_to_le32(action);
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- if (vif->p2p)
- cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA);
- else
- cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA);
- break;
- case NL80211_IFTYPE_AP:
- cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO);
- break;
- case NL80211_IFTYPE_MONITOR:
- cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER);
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE);
- break;
- case NL80211_IFTYPE_ADHOC:
- cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS);
- break;
- default:
- WARN_ON_ONCE(1);
- }
-
- cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id);
-
- memcpy(cmd->node_addr, vif->addr, ETH_ALEN);
-
- if (bssid)
- memcpy(cmd->bssid_addr, bssid, ETH_ALEN);
- else
- eth_broadcast_addr(cmd->bssid_addr);
-
- rcu_read_lock();
- chanctx = rcu_dereference(vif->chanctx_conf);
- iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band
- : IEEE80211_BAND_2GHZ,
- &cck_ack_rates, &ofdm_ack_rates);
- rcu_read_unlock();
-
- cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates);
- cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates);
-
- cmd->cck_short_preamble =
- cpu_to_le32(vif->bss_conf.use_short_preamble ?
- MAC_FLG_SHORT_PREAMBLE : 0);
- cmd->short_slot =
- cpu_to_le32(vif->bss_conf.use_short_slot ?
- MAC_FLG_SHORT_SLOT : 0);
-
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- u8 txf = iwl_mvm_ac_to_tx_fifo[i];
-
- cmd->ac[txf].cw_min =
- cpu_to_le16(mvmvif->queue_params[i].cw_min);
- cmd->ac[txf].cw_max =
- cpu_to_le16(mvmvif->queue_params[i].cw_max);
- cmd->ac[txf].edca_txop =
- cpu_to_le16(mvmvif->queue_params[i].txop * 32);
- cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs;
- cmd->ac[txf].fifos_mask = BIT(txf);
- }
-
- /* in AP mode, the MCAST FIFO takes the EDCA params from VO */
- if (vif->type == NL80211_IFTYPE_AP)
- cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |=
- BIT(IWL_MVM_TX_FIFO_MCAST);
-
- if (vif->bss_conf.qos)
- cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
-
- if (vif->bss_conf.use_cts_prot)
- cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
-
- IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n",
- vif->bss_conf.use_cts_prot,
- vif->bss_conf.ht_operation_mode);
- if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
- cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
- if (ht_enabled)
- iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd);
-
- cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP);
-}
-
-static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
- struct iwl_mac_ctx_cmd *cmd)
-{
- int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
- sizeof(*cmd), cmd);
- if (ret)
- IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n",
- le32_to_cpu(cmd->action), ret);
- return ret;
-}
-
-static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 action, bool force_assoc_off,
- const u8 *bssid_override)
-{
- struct iwl_mac_ctx_cmd cmd = {};
- struct iwl_mac_data_sta *ctxt_sta;
-
- WARN_ON(vif->type != NL80211_IFTYPE_STATION);
-
- /* Fill the common data for all mac context types */
- iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, bssid_override, action);
-
- if (vif->p2p) {
- struct ieee80211_p2p_noa_attr *noa =
- &vif->bss_conf.p2p_noa_attr;
-
- cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow &
- IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
- ctxt_sta = &cmd.p2p_sta.sta;
- } else {
- ctxt_sta = &cmd.sta;
- }
-
- /* We need the dtim_period to set the MAC as associated */
- if (vif->bss_conf.assoc && vif->bss_conf.dtim_period &&
- !force_assoc_off) {
- u32 dtim_offs;
-
- /*
- * The DTIM count counts down, so when it is N that means N
- * more beacon intervals happen until the DTIM TBTT. Therefore
- * add this to the current time. If that ends up being in the
- * future, the firmware will handle it.
- *
- * Also note that the system_timestamp (which we get here as
- * "sync_device_ts") and TSF timestamp aren't at exactly the
- * same offset in the frame -- the TSF is at the first symbol
- * of the TSF, the system timestamp is at signal acquisition
- * time. This means there's an offset between them of at most
- * a few hundred microseconds (24 * 8 bits + PLCP time gives
- * 384us in the longest case), this is currently not relevant
- * as the firmware wakes up around 2ms before the TBTT.
- */
- dtim_offs = vif->bss_conf.sync_dtim_count *
- vif->bss_conf.beacon_int;
- /* convert TU to usecs */
- dtim_offs *= 1024;
-
- ctxt_sta->dtim_tsf =
- cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs);
- ctxt_sta->dtim_time =
- cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs);
-
- IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n",
- le64_to_cpu(ctxt_sta->dtim_tsf),
- le32_to_cpu(ctxt_sta->dtim_time),
- dtim_offs);
-
- ctxt_sta->is_assoc = cpu_to_le32(1);
- } else {
- ctxt_sta->is_assoc = cpu_to_le32(0);
-
- /* Allow beacons to pass through as long as we are not
- * associated, or we do not have dtim period information.
- */
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
- }
-
- ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int);
- ctxt_sta->bi_reciprocal =
- cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
- ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
- vif->bss_conf.dtim_period);
- ctxt_sta->dtim_reciprocal =
- cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
- vif->bss_conf.dtim_period));
-
- ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
- ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
-
- if (vif->probe_req_reg && vif->bss_conf.assoc && vif->p2p)
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
-
- return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 action)
-{
- struct iwl_mac_ctx_cmd cmd = {};
-
- WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
-
- iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
-
- cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC |
- MAC_FILTER_IN_CONTROL_AND_MGMT |
- MAC_FILTER_IN_BEACON |
- MAC_FILTER_IN_PROBE_REQUEST |
- MAC_FILTER_IN_CRC32);
- ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS);
-
- return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 action)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_ctx_cmd cmd = {};
-
- WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
-
- iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
-
- cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON |
- MAC_FILTER_IN_PROBE_REQUEST);
-
- /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */
- cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int);
- cmd.ibss.bi_reciprocal =
- cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
-
- /* TODO: Assumes that the beacon id == mac context id */
- cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id);
-
- return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-struct iwl_mvm_go_iterator_data {
- bool go_active;
-};
-
-static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_go_iterator_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (vif->type == NL80211_IFTYPE_AP && vif->p2p &&
- mvmvif->ap_ibss_active)
- data->go_active = true;
-}
-
-static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 action)
-{
- struct iwl_mac_ctx_cmd cmd = {};
- struct iwl_mvm_go_iterator_data data = {};
-
- WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
-
- iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
-
- cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
-
- /* Override the filter flags to accept only probe requests */
- cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
-
- /*
- * This flag should be set to true when the P2P Device is
- * discoverable and there is at least another active P2P GO. Settings
- * this flag will allow the P2P Device to be discoverable on other
- * channels in addition to its listen channel.
- * Note that this flag should not be set in other cases as it opens the
- * Rx filters on all MAC and increases the number of interrupts.
- */
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- iwl_mvm_go_iterator, &data);
-
- cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0);
- return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
- struct iwl_mac_beacon_cmd *beacon_cmd,
- u8 *beacon, u32 frame_size)
-{
- u32 tim_idx;
- struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
-
- /* The index is relative to frame start but we start looking at the
- * variable-length part of the beacon. */
- tim_idx = mgmt->u.beacon.variable - beacon;
-
- /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
- while ((tim_idx < (frame_size - 2)) &&
- (beacon[tim_idx] != WLAN_EID_TIM))
- tim_idx += beacon[tim_idx+1] + 2;
-
- /* If TIM field was found, set variables */
- if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
- beacon_cmd->tim_idx = cpu_to_le32(tim_idx);
- beacon_cmd->tim_size = cpu_to_le32((u32)beacon[tim_idx+1]);
- } else {
- IWL_WARN(mvm, "Unable to find TIM Element in beacon\n");
- }
-}
-
-static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct sk_buff *beacon)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_host_cmd cmd = {
- .id = BEACON_TEMPLATE_CMD,
- .flags = CMD_ASYNC,
- };
- struct iwl_mac_beacon_cmd beacon_cmd = {};
- struct ieee80211_tx_info *info;
- u32 beacon_skb_len;
- u32 rate, tx_flags;
-
- if (WARN_ON(!beacon))
- return -EINVAL;
-
- beacon_skb_len = beacon->len;
-
- /* TODO: for now the beacon template id is set to be the mac context id.
- * Might be better to handle it as another resource ... */
- beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
- info = IEEE80211_SKB_CB(beacon);
-
- /* Set up TX command fields */
- beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len);
- beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id;
- beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
- tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;
- tx_flags |=
- iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) <<
- TX_CMD_FLG_BT_PRIO_POS;
- beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
-
- mvm->mgmt_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
- mvm->mgmt_last_antenna_idx);
-
- beacon_cmd.tx.rate_n_flags =
- cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
- RATE_MCS_ANT_POS);
-
- if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) {
- rate = IWL_FIRST_OFDM_RATE;
- } else {
- rate = IWL_FIRST_CCK_RATE;
- beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK);
- }
- beacon_cmd.tx.rate_n_flags |=
- cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
-
- /* Set up TX beacon command fields */
- if (vif->type == NL80211_IFTYPE_AP)
- iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
- beacon->data,
- beacon_skb_len);
-
- /* Submit command */
- cmd.len[0] = sizeof(beacon_cmd);
- cmd.data[0] = &beacon_cmd;
- cmd.dataflags[0] = 0;
- cmd.len[1] = beacon_skb_len;
- cmd.data[1] = beacon->data;
- cmd.dataflags[1] = IWL_HCMD_DFL_DUP;
-
- return iwl_mvm_send_cmd(mvm, &cmd);
-}
-
-/* The beacon template for the AP/GO/IBSS has changed and needs update */
-int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct sk_buff *beacon;
- int ret;
-
- WARN_ON(vif->type != NL80211_IFTYPE_AP &&
- vif->type != NL80211_IFTYPE_ADHOC);
-
- beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL);
- if (!beacon)
- return -ENOMEM;
-
- ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon);
- dev_kfree_skb(beacon);
- return ret;
-}
-
-struct iwl_mvm_mac_ap_iterator_data {
- struct iwl_mvm *mvm;
- struct ieee80211_vif *vif;
- u32 beacon_device_ts;
- u16 beacon_int;
-};
-
-/* Find the beacon_device_ts and beacon_int for a managed interface */
-static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_mac_ap_iterator_data *data = _data;
-
- if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
- return;
-
- /* Station client has higher priority over P2P client*/
- if (vif->p2p && data->beacon_device_ts)
- return;
-
- data->beacon_device_ts = vif->bss_conf.sync_device_ts;
- data->beacon_int = vif->bss_conf.beacon_int;
-}
-
-/*
- * Fill the specific data for mac context of type AP of P2P GO
- */
-static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mac_data_ap *ctxt_ap,
- bool add)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_mac_ap_iterator_data data = {
- .mvm = mvm,
- .vif = vif,
- .beacon_device_ts = 0
- };
-
- ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
- ctxt_ap->bi_reciprocal =
- cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
- ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
- vif->bss_conf.dtim_period);
- ctxt_ap->dtim_reciprocal =
- cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
- vif->bss_conf.dtim_period));
-
- ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
-
- /*
- * Only set the beacon time when the MAC is being added, when we
- * just modify the MAC then we should keep the time -- the firmware
- * can otherwise have a "jumping" TBTT.
- */
- if (add) {
- /*
- * If there is a station/P2P client interface which is
- * associated, set the AP's TBTT far enough from the station's
- * TBTT. Otherwise, set it to the current system time
- */
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- iwl_mvm_mac_ap_iterator, &data);
-
- if (data.beacon_device_ts) {
- u32 rand = (prandom_u32() % (64 - 36)) + 36;
- mvmvif->ap_beacon_time = data.beacon_device_ts +
- ieee80211_tu_to_usec(data.beacon_int * rand /
- 100);
- } else {
- mvmvif->ap_beacon_time =
- iwl_read_prph(mvm->trans,
- DEVICE_SYSTEM_TIME_REG);
- }
- }
-
- ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time);
- ctxt_ap->beacon_tsf = 0; /* unused */
-
- /* TODO: Assume that the beacon id == mac context id */
- ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 action)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_ctx_cmd cmd = {};
-
- WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p);
-
- /* Fill the common data for all mac context types */
- iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
-
- /*
- * pass probe requests and beacons from other APs (needed
- * for ht protection); when there're no any associated station
- * don't ask FW to pass beacons to prevent unnecessary wake-ups.
- */
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
- if (mvmvif->ap_assoc_sta_count) {
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
- IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n");
- } else {
- IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
- }
-
- /* Fill the data specific for ap mode */
- iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap,
- action == FW_CTXT_ACTION_ADD);
-
- return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 action)
-{
- struct iwl_mac_ctx_cmd cmd = {};
- struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr;
-
- WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p);
-
- /* Fill the common data for all mac context types */
- iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
-
- /*
- * pass probe requests and beacons from other APs (needed
- * for ht protection)
- */
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST |
- MAC_FILTER_IN_BEACON);
-
- /* Fill the data specific for GO mode */
- iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap,
- action == FW_CTXT_ACTION_ADD);
-
- cmd.go.ctwin = cpu_to_le32(noa->oppps_ctwindow &
- IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
- cmd.go.opp_ps_enabled =
- cpu_to_le32(!!(noa->oppps_ctwindow &
- IEEE80211_P2P_OPPPS_ENABLE_BIT));
-
- return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- u32 action, bool force_assoc_off,
- const u8 *bssid_override)
-{
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action,
- force_assoc_off,
- bssid_override);
- break;
- case NL80211_IFTYPE_AP:
- if (!vif->p2p)
- return iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, action);
- else
- return iwl_mvm_mac_ctxt_cmd_go(mvm, vif, action);
- break;
- case NL80211_IFTYPE_MONITOR:
- return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action);
- case NL80211_IFTYPE_P2P_DEVICE:
- return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action);
- case NL80211_IFTYPE_ADHOC:
- return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action);
- default:
- break;
- }
-
- return -EOPNOTSUPP;
-}
-
-int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n",
- vif->addr, ieee80211_vif_type_p2p(vif)))
- return -EIO;
-
- ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD,
- true, NULL);
- if (ret)
- return ret;
-
- /* will only do anything at resume from D3 time */
- iwl_mvm_set_last_nonqos_seq(mvm, vif);
-
- mvmvif->uploaded = true;
- return 0;
-}
-
-int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool force_assoc_off, const u8 *bssid_override)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n",
- vif->addr, ieee80211_vif_type_p2p(vif)))
- return -EIO;
-
- return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY,
- force_assoc_off, bssid_override);
-}
-
-int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_ctx_cmd cmd;
- int ret;
-
- if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n",
- vif->addr, ieee80211_vif_type_p2p(vif)))
- return -EIO;
-
- memset(&cmd, 0, sizeof(cmd));
-
- cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color));
- cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
- sizeof(cmd), &cmd);
- if (ret) {
- IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret);
- return ret;
- }
-
- mvmvif->uploaded = false;
-
- if (vif->type == NL80211_IFTYPE_MONITOR)
- __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags);
-
- return 0;
-}
-
-static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
- struct ieee80211_vif *csa_vif, u32 gp2,
- bool tx_success)
-{
- struct iwl_mvm_vif *mvmvif =
- iwl_mvm_vif_from_mac80211(csa_vif);
-
- /* Don't start to countdown from a failed beacon */
- if (!tx_success && !mvmvif->csa_countdown)
- return;
-
- mvmvif->csa_countdown = true;
-
- if (!ieee80211_csa_is_complete(csa_vif)) {
- int c = ieee80211_csa_update_counter(csa_vif);
-
- iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
- if (csa_vif->p2p &&
- !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 &&
- tx_success) {
- u32 rel_time = (c + 1) *
- csa_vif->bss_conf.beacon_int -
- IWL_MVM_CHANNEL_SWITCH_TIME_GO;
- u32 apply_time = gp2 + rel_time * 1024;
-
- iwl_mvm_schedule_csa_period(mvm, csa_vif,
- IWL_MVM_CHANNEL_SWITCH_TIME_GO -
- IWL_MVM_CHANNEL_SWITCH_MARGIN,
- apply_time);
- }
- } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) {
- /* we don't have CSA NoA scheduled yet, switch now */
- ieee80211_csa_finish(csa_vif);
- RCU_INIT_POINTER(mvm->csa_vif, NULL);
- }
-}
-
-void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
- struct iwl_mvm_tx_resp *beacon_notify_hdr;
- struct ieee80211_vif *csa_vif;
- struct ieee80211_vif *tx_blocked_vif;
- u16 status;
-
- lockdep_assert_held(&mvm->mutex);
-
- beacon_notify_hdr = &beacon->beacon_notify_hdr;
- mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
-
- status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK;
- IWL_DEBUG_RX(mvm,
- "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
- status, beacon_notify_hdr->failure_frame,
- le64_to_cpu(beacon->tsf),
- mvm->ap_last_beacon_gp2,
- le32_to_cpu(beacon_notify_hdr->initial_rate));
-
- csa_vif = rcu_dereference_protected(mvm->csa_vif,
- lockdep_is_held(&mvm->mutex));
- if (unlikely(csa_vif && csa_vif->csa_active))
- iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2,
- (status == TX_STATUS_SUCCESS));
-
- tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
- lockdep_is_held(&mvm->mutex));
- if (unlikely(tx_blocked_vif)) {
- struct iwl_mvm_vif *mvmvif =
- iwl_mvm_vif_from_mac80211(tx_blocked_vif);
-
- /*
- * The channel switch is started and we have blocked the
- * stations. If this is the first beacon (the timeout wasn't
- * set), set the unblock timeout, otherwise countdown
- */
- if (!mvm->csa_tx_block_bcn_timeout)
- mvm->csa_tx_block_bcn_timeout =
- IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
- else
- mvm->csa_tx_block_bcn_timeout--;
-
- /* Check if the timeout is expired, and unblock tx */
- if (mvm->csa_tx_block_bcn_timeout == 0) {
- iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
- RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
- }
- }
-}
-
-static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_missed_beacons_notif *missed_beacons = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = mvmvif->mvm;
- struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig;
- struct iwl_fw_dbg_trigger_tlv *trigger;
- u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx;
- u32 rx_missed_bcon, rx_missed_bcon_since_rx;
-
- if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id))
- return;
-
- rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons);
- rx_missed_bcon_since_rx =
- le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx);
- /*
- * TODO: the threshold should be adjusted based on latency conditions,
- * and/or in case of a CS flow on one of the other AP vifs.
- */
- if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) >
- IWL_MVM_MISSED_BEACONS_THRESHOLD)
- ieee80211_beacon_loss(vif);
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw,
- FW_DBG_TRIGGER_MISSED_BEACONS))
- return;
-
- trigger = iwl_fw_dbg_get_trigger(mvm->fw,
- FW_DBG_TRIGGER_MISSED_BEACONS);
- bcon_trig = (void *)trigger->data;
- stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon);
- stop_trig_missed_bcon_since_rx =
- le32_to_cpu(bcon_trig->stop_consec_missed_bcon_since_rx);
-
- /* TODO: implement start trigger */
-
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
- return;
-
- if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx ||
- rx_missed_bcon >= stop_trig_missed_bcon)
- iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
-}
-
-void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_missed_beacons_notif *mb = (void *)pkt->data;
-
- IWL_DEBUG_INFO(mvm,
- "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n",
- le32_to_cpu(mb->mac_id),
- le32_to_cpu(mb->consec_missed_beacons),
- le32_to_cpu(mb->consec_missed_beacons_since_last_rx),
- le32_to_cpu(mb->num_recvd_beacons),
- le32_to_cpu(mb->num_expected_beacons));
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_beacon_loss_iterator,
- mb);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
deleted file mode 100644
index e88afac51c5d..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ /dev/null
@@ -1,4265 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ip.h>
-#include <linux/if_arp.h>
-#include <linux/devcoredump.h>
-#include <net/mac80211.h>
-#include <net/ieee80211_radiotap.h>
-#include <net/tcp.h>
-
-#include "iwl-op-mode.h"
-#include "iwl-io.h"
-#include "mvm.h"
-#include "sta.h"
-#include "time-event.h"
-#include "iwl-eeprom-parse.h"
-#include "iwl-phy-db.h"
-#include "testmode.h"
-#include "iwl-fw-error-dump.h"
-#include "iwl-prph.h"
-#include "iwl-csr.h"
-#include "iwl-nvm-parse.h"
-
-static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_STATION),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
- },
-};
-
-static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
- {
- .num_different_channels = 2,
- .max_interfaces = 3,
- .limits = iwl_mvm_limits,
- .n_limits = ARRAY_SIZE(iwl_mvm_limits),
- },
-};
-
-#ifdef CONFIG_PM_SLEEP
-static const struct nl80211_wowlan_tcp_data_token_feature
-iwl_mvm_wowlan_tcp_token_feature = {
- .min_len = 0,
- .max_len = 255,
- .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS,
-};
-
-static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
- .tok = &iwl_mvm_wowlan_tcp_token_feature,
- .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN -
- sizeof(struct ethhdr) -
- sizeof(struct iphdr) -
- sizeof(struct tcphdr),
- .data_interval_max = 65535, /* __le16 in API */
- .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN -
- sizeof(struct ethhdr) -
- sizeof(struct iphdr) -
- sizeof(struct tcphdr),
- .seq = true,
-};
-#endif
-
-#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
-/*
- * Use the reserved field to indicate magic values.
- * these values will only be used internally by the driver,
- * and won't make it to the fw (reserved will be 0).
- * BC_FILTER_MAGIC_IP - configure the val of this attribute to
- * be the vif's ip address. in case there is not a single
- * ip address (0, or more than 1), this attribute will
- * be skipped.
- * BC_FILTER_MAGIC_MAC - set the val of this attribute to
- * the LSB bytes of the vif's mac address
- */
-enum {
- BC_FILTER_MAGIC_NONE = 0,
- BC_FILTER_MAGIC_IP,
- BC_FILTER_MAGIC_MAC,
-};
-
-static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
- {
- /* arp */
- .discard = 0,
- .frame_type = BCAST_FILTER_FRAME_TYPE_ALL,
- .attrs = {
- {
- /* frame type - arp, hw type - ethernet */
- .offset_type =
- BCAST_FILTER_OFFSET_PAYLOAD_START,
- .offset = sizeof(rfc1042_header),
- .val = cpu_to_be32(0x08060001),
- .mask = cpu_to_be32(0xffffffff),
- },
- {
- /* arp dest ip */
- .offset_type =
- BCAST_FILTER_OFFSET_PAYLOAD_START,
- .offset = sizeof(rfc1042_header) + 2 +
- sizeof(struct arphdr) +
- ETH_ALEN + sizeof(__be32) +
- ETH_ALEN,
- .mask = cpu_to_be32(0xffffffff),
- /* mark it as special field */
- .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP),
- },
- },
- },
- {
- /* dhcp offer bcast */
- .discard = 0,
- .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4,
- .attrs = {
- {
- /* udp dest port - 68 (bootp client)*/
- .offset_type = BCAST_FILTER_OFFSET_IP_END,
- .offset = offsetof(struct udphdr, dest),
- .val = cpu_to_be32(0x00440000),
- .mask = cpu_to_be32(0xffff0000),
- },
- {
- /* dhcp - lsb bytes of client hw address */
- .offset_type = BCAST_FILTER_OFFSET_IP_END,
- .offset = 38,
- .mask = cpu_to_be32(0xffffffff),
- /* mark it as special field */
- .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC),
- },
- },
- },
- /* last filter must be empty */
- {},
-};
-#endif
-
-void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
-{
- if (!iwl_mvm_is_d0i3_supported(mvm))
- return;
-
- IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
- spin_lock_bh(&mvm->refs_lock);
- mvm->refs[ref_type]++;
- spin_unlock_bh(&mvm->refs_lock);
- iwl_trans_ref(mvm->trans);
-}
-
-void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
-{
- if (!iwl_mvm_is_d0i3_supported(mvm))
- return;
-
- IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
- spin_lock_bh(&mvm->refs_lock);
- WARN_ON(!mvm->refs[ref_type]--);
- spin_unlock_bh(&mvm->refs_lock);
- iwl_trans_unref(mvm->trans);
-}
-
-static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
- enum iwl_mvm_ref_type except_ref)
-{
- int i, j;
-
- if (!iwl_mvm_is_d0i3_supported(mvm))
- return;
-
- spin_lock_bh(&mvm->refs_lock);
- for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
- if (except_ref == i || !mvm->refs[i])
- continue;
-
- IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n",
- i, mvm->refs[i]);
- for (j = 0; j < mvm->refs[i]; j++)
- iwl_trans_unref(mvm->trans);
- mvm->refs[i] = 0;
- }
- spin_unlock_bh(&mvm->refs_lock);
-}
-
-bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
-{
- int i;
- bool taken = false;
-
- if (!iwl_mvm_is_d0i3_supported(mvm))
- return true;
-
- spin_lock_bh(&mvm->refs_lock);
- for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
- if (mvm->refs[i]) {
- taken = true;
- break;
- }
- }
- spin_unlock_bh(&mvm->refs_lock);
-
- return taken;
-}
-
-int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
-{
- iwl_mvm_ref(mvm, ref_type);
-
- if (!wait_event_timeout(mvm->d0i3_exit_waitq,
- !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status),
- HZ)) {
- WARN_ON_ONCE(1);
- iwl_mvm_unref(mvm, ref_type);
- return -EIO;
- }
-
- return 0;
-}
-
-static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
-{
- int i;
-
- memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts));
- for (i = 0; i < NUM_PHY_CTX; i++) {
- mvm->phy_ctxts[i].id = i;
- mvm->phy_ctxts[i].ref = 0;
- }
-}
-
-struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
- const char *alpha2,
- enum iwl_mcc_source src_id,
- bool *changed)
-{
- struct ieee80211_regdomain *regd = NULL;
- struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mcc_update_resp *resp;
-
- IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2);
-
- lockdep_assert_held(&mvm->mutex);
-
- resp = iwl_mvm_update_mcc(mvm, alpha2, src_id);
- if (IS_ERR_OR_NULL(resp)) {
- IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n",
- PTR_ERR_OR_ZERO(resp));
- goto out;
- }
-
- if (changed)
- *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE);
-
- regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
- __le32_to_cpu(resp->n_channels),
- resp->channels,
- __le16_to_cpu(resp->mcc));
- /* Store the return source id */
- src_id = resp->source_id;
- kfree(resp);
- if (IS_ERR_OR_NULL(regd)) {
- IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n",
- PTR_ERR_OR_ZERO(regd));
- goto out;
- }
-
- IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
- regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id);
- mvm->lar_regdom_set = true;
- mvm->mcc_src = src_id;
-
-out:
- return regd;
-}
-
-void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm)
-{
- bool changed;
- struct ieee80211_regdomain *regd;
-
- if (!iwl_mvm_is_lar_supported(mvm))
- return;
-
- regd = iwl_mvm_get_current_regdomain(mvm, &changed);
- if (!IS_ERR_OR_NULL(regd)) {
- /* only update the regulatory core if changed */
- if (changed)
- regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
-
- kfree(regd);
- }
-}
-
-struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
- bool *changed)
-{
- return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ",
- iwl_mvm_is_wifi_mcc_supported(mvm) ?
- MCC_SOURCE_GET_CURRENT :
- MCC_SOURCE_OLD_FW, changed);
-}
-
-int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
-{
- enum iwl_mcc_source used_src;
- struct ieee80211_regdomain *regd;
- int ret;
- bool changed;
- const struct ieee80211_regdomain *r =
- rtnl_dereference(mvm->hw->wiphy->regd);
-
- if (!r)
- return -ENOENT;
-
- /* save the last source in case we overwrite it below */
- used_src = mvm->mcc_src;
- if (iwl_mvm_is_wifi_mcc_supported(mvm)) {
- /* Notify the firmware we support wifi location updates */
- regd = iwl_mvm_get_current_regdomain(mvm, NULL);
- if (!IS_ERR_OR_NULL(regd))
- kfree(regd);
- }
-
- /* Now set our last stored MCC and source */
- regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src,
- &changed);
- if (IS_ERR_OR_NULL(regd))
- return -EIO;
-
- /* update cfg80211 if the regdomain was changed */
- if (changed)
- ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
- else
- ret = 0;
-
- kfree(regd);
- return ret;
-}
-
-int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
-{
- struct ieee80211_hw *hw = mvm->hw;
- int num_mac, ret, i;
- static const u32 mvm_ciphers[] = {
- WLAN_CIPHER_SUITE_WEP40,
- WLAN_CIPHER_SUITE_WEP104,
- WLAN_CIPHER_SUITE_TKIP,
- WLAN_CIPHER_SUITE_CCMP,
- };
-
- /* Tell mac80211 our characteristics */
- ieee80211_hw_set(hw, SIGNAL_DBM);
- ieee80211_hw_set(hw, SPECTRUM_MGMT);
- ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
- ieee80211_hw_set(hw, QUEUE_CONTROL);
- ieee80211_hw_set(hw, WANT_MONITOR_VIF);
- ieee80211_hw_set(hw, SUPPORTS_PS);
- ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
- ieee80211_hw_set(hw, AMPDU_AGGREGATION);
- ieee80211_hw_set(hw, TIMING_BEACON_ONLY);
- ieee80211_hw_set(hw, CONNECTION_MONITOR);
- ieee80211_hw_set(hw, CHANCTX_STA_CSA);
- ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
- ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
-
- hw->queues = mvm->first_agg_queue;
- hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
- hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
- IEEE80211_RADIOTAP_MCS_HAVE_STBC;
- hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
- IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
- hw->rate_control_algorithm = "iwl-mvm-rs";
- hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
- hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
-
- BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 2);
- memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
- hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers);
- hw->wiphy->cipher_suites = mvm->ciphers;
-
- /*
- * Enable 11w if advertised by firmware and software crypto
- * is not enabled (as the firmware will interpret some mgmt
- * packets, so enabling it with software crypto isn't safe)
- */
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP &&
- !iwlwifi_mod_params.sw_crypto) {
- ieee80211_hw_set(hw, MFP_CAPABLE);
- mvm->ciphers[hw->wiphy->n_cipher_suites] =
- WLAN_CIPHER_SUITE_AES_CMAC;
- hw->wiphy->n_cipher_suites++;
- }
-
- /* currently FW API supports only one optional cipher scheme */
- if (mvm->fw->cs[0].cipher) {
- mvm->hw->n_cipher_schemes = 1;
- mvm->hw->cipher_schemes = &mvm->fw->cs[0];
- mvm->ciphers[hw->wiphy->n_cipher_suites] =
- mvm->fw->cs[0].cipher;
- hw->wiphy->n_cipher_suites++;
- }
-
- ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
- hw->wiphy->features |=
- NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
- NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
- NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
-
- hw->sta_data_size = sizeof(struct iwl_mvm_sta);
- hw->vif_data_size = sizeof(struct iwl_mvm_vif);
- hw->chanctx_data_size = sizeof(u16);
-
- hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_DEVICE) |
- BIT(NL80211_IFTYPE_ADHOC);
-
- hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
- hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
- if (iwl_mvm_is_lar_supported(mvm))
- hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
- else
- hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
- REGULATORY_DISABLE_BEACON_HINTS;
-
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
- hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
-
- hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
-
- hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
- hw->wiphy->n_iface_combinations =
- ARRAY_SIZE(iwl_mvm_iface_combinations);
-
- hw->wiphy->max_remain_on_channel_duration = 10000;
- hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
- /* we can compensate an offset of up to 3 channels = 15 MHz */
- hw->wiphy->max_adj_channel_rssi_comp = 3 * 5;
-
- /* Extract MAC address */
- memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
- hw->wiphy->addresses = mvm->addresses;
- hw->wiphy->n_addresses = 1;
-
- /* Extract additional MAC addresses if available */
- num_mac = (mvm->nvm_data->n_hw_addrs > 1) ?
- min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1;
-
- for (i = 1; i < num_mac; i++) {
- memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr,
- ETH_ALEN);
- mvm->addresses[i].addr[5]++;
- hw->wiphy->n_addresses++;
- }
-
- iwl_mvm_reset_phy_ctxts(mvm);
-
- hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
-
- hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
-
- BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK);
- BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) ||
- IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK));
-
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
- mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS;
- else
- mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS;
-
- if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
- if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) {
- hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
- fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_LQ_SS_PARAMS))
- hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |=
- IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
- }
-
- hw->wiphy->hw_version = mvm->trans->hw_id;
-
- if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
- hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
- else
- hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
-
- hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
- hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
- hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
- /* we create the 802.11 header and zero length SSID IE. */
- hw->wiphy->max_sched_scan_ie_len =
- SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
- hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS;
- hw->wiphy->max_sched_scan_plan_interval = U16_MAX;
-
- /*
- * the firmware uses u8 for num of iterations, but 0xff is saved for
- * infinite loop, so the maximum number of iterations is actually 254.
- */
- hw->wiphy->max_sched_scan_plan_iterations = 254;
-
- hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
- NL80211_FEATURE_LOW_PRIORITY_SCAN |
- NL80211_FEATURE_P2P_GO_OPPPS |
- NL80211_FEATURE_DYNAMIC_SMPS |
- NL80211_FEATURE_STATIC_SMPS |
- NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT))
- hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT))
- hw->wiphy->features |= NL80211_FEATURE_QUIET;
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT))
- hw->wiphy->features |=
- NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES;
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT))
- hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES;
-
- mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
-
-#ifdef CONFIG_PM_SLEEP
- if (iwl_mvm_is_d0i3_supported(mvm) &&
- device_can_wakeup(mvm->trans->dev)) {
- mvm->wowlan.flags = WIPHY_WOWLAN_ANY;
- hw->wiphy->wowlan = &mvm->wowlan;
- }
-
- if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
- mvm->trans->ops->d3_suspend &&
- mvm->trans->ops->d3_resume &&
- device_can_wakeup(mvm->trans->dev)) {
- mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
- WIPHY_WOWLAN_DISCONNECT |
- WIPHY_WOWLAN_EAP_IDENTITY_REQ |
- WIPHY_WOWLAN_RFKILL_RELEASE |
- WIPHY_WOWLAN_NET_DETECT;
- if (!iwlwifi_mod_params.sw_crypto)
- 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.max_nd_match_sets = IWL_SCAN_MAX_PROFILES;
- mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
- hw->wiphy->wowlan = &mvm->wowlan;
- }
-#endif
-
-#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
- /* assign default bcast filtering configuration */
- mvm->bcast_filters = iwl_mvm_default_bcast_filters;
-#endif
-
- ret = iwl_mvm_leds_init(mvm);
- if (ret)
- return ret;
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_TDLS_SUPPORT)) {
- IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
- hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
- ieee80211_hw_set(hw, TDLS_WIDER_BW);
- }
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) {
- IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
- hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
- }
-
- hw->netdev_features |= mvm->cfg->features;
- if (!iwl_mvm_is_csum_supported(mvm))
- hw->netdev_features &= ~NETIF_F_RXCSUM;
-
- ret = ieee80211_register_hw(mvm->hw);
- if (ret)
- iwl_mvm_leds_exit(mvm);
-
- return ret;
-}
-
-static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct sk_buff *skb)
-{
- struct iwl_mvm_sta *mvmsta;
- bool defer = false;
-
- /*
- * double check the IN_D0I3 flag both before and after
- * taking the spinlock, in order to prevent taking
- * the spinlock when not needed.
- */
- if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)))
- return false;
-
- spin_lock(&mvm->d0i3_tx_lock);
- /*
- * testing the flag again ensures the skb dequeue
- * loop (on d0i3 exit) hasn't run yet.
- */
- if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))
- goto out;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- if (mvmsta->sta_id == IWL_MVM_STATION_COUNT ||
- mvmsta->sta_id != mvm->d0i3_ap_sta_id)
- goto out;
-
- __skb_queue_tail(&mvm->d0i3_tx, skb);
- ieee80211_stop_queues(mvm->hw);
-
- /* trigger wakeup */
- iwl_mvm_ref(mvm, IWL_MVM_REF_TX);
- iwl_mvm_unref(mvm, IWL_MVM_REF_TX);
-
- defer = true;
-out:
- spin_unlock(&mvm->d0i3_tx_lock);
- return defer;
-}
-
-static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct ieee80211_sta *sta = control->sta;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *hdr = (void *)skb->data;
-
- if (iwl_mvm_is_radio_killed(mvm)) {
- IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
- goto drop;
- }
-
- if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
- !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
- !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
- goto drop;
-
- /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
- if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER &&
- ieee80211_is_mgmt(hdr->frame_control) &&
- !ieee80211_is_deauth(hdr->frame_control) &&
- !ieee80211_is_disassoc(hdr->frame_control) &&
- !ieee80211_is_action(hdr->frame_control)))
- sta = NULL;
-
- if (sta) {
- if (iwl_mvm_defer_tx(mvm, sta, skb))
- return;
- if (iwl_mvm_tx_skb(mvm, skb, sta))
- goto drop;
- return;
- }
-
- if (iwl_mvm_tx_skb_non_sta(mvm, skb))
- goto drop;
- return;
- drop:
- ieee80211_free_txskb(hw, skb);
-}
-
-static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg)
-{
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
- return false;
- return true;
-}
-
-static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
-{
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
- return false;
- if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
- return true;
-
- /* enabled by default */
- return true;
-}
-
-#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \
- do { \
- if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \
- break; \
- iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \
- } while (0)
-
-static void
-iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn,
- enum ieee80211_ampdu_mlme_action action)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_ba *ba_trig;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
- ba_trig = (void *)trig->data;
-
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
- return;
-
- switch (action) {
- case IEEE80211_AMPDU_TX_OPERATIONAL: {
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
-
- CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid,
- "TX AGG START: MAC %pM tid %d ssn %d\n",
- sta->addr, tid, tid_data->ssn);
- break;
- }
- case IEEE80211_AMPDU_TX_STOP_CONT:
- CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid,
- "TX AGG STOP: MAC %pM tid %d\n",
- sta->addr, tid);
- break;
- case IEEE80211_AMPDU_RX_START:
- CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid,
- "RX AGG START: MAC %pM tid %d ssn %d\n",
- sta->addr, tid, rx_ba_ssn);
- break;
- case IEEE80211_AMPDU_RX_STOP:
- CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid,
- "RX AGG STOP: MAC %pM tid %d\n",
- sta->addr, tid);
- break;
- default:
- break;
- }
-}
-
-static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid,
- u16 *ssn, u8 buf_size, bool amsdu)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
- bool tx_agg_ref = false;
-
- IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
- sta->addr, tid, action);
-
- if (!(mvm->nvm_data->sku_cap_11n_enable))
- return -EACCES;
-
- /* return from D0i3 before starting a new Tx aggregation */
- switch (action) {
- case IEEE80211_AMPDU_TX_START:
- case IEEE80211_AMPDU_TX_STOP_CONT:
- case IEEE80211_AMPDU_TX_STOP_FLUSH:
- case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- case IEEE80211_AMPDU_TX_OPERATIONAL:
- /*
- * for tx start, wait synchronously until D0i3 exit to
- * get the correct sequence number for the tid.
- * additionally, some other ampdu actions use direct
- * target access, which is not handled automatically
- * by the trans layer (unlike commands), so wait for
- * d0i3 exit in these cases as well.
- */
- ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG);
- if (ret)
- return ret;
-
- tx_agg_ref = true;
- break;
- default:
- break;
- }
-
- mutex_lock(&mvm->mutex);
-
- switch (action) {
- case IEEE80211_AMPDU_RX_START:
- if (!iwl_enable_rx_ampdu(mvm->cfg)) {
- ret = -EINVAL;
- break;
- }
- ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true);
- break;
- case IEEE80211_AMPDU_RX_STOP:
- ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);
- break;
- case IEEE80211_AMPDU_TX_START:
- if (!iwl_enable_tx_ampdu(mvm->cfg)) {
- ret = -EINVAL;
- break;
- }
- ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
- break;
- case IEEE80211_AMPDU_TX_STOP_CONT:
- ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
- break;
- case IEEE80211_AMPDU_TX_STOP_FLUSH:
- case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid);
- break;
- case IEEE80211_AMPDU_TX_OPERATIONAL:
- ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
- break;
- default:
- WARN_ON_ONCE(1);
- ret = -EINVAL;
- break;
- }
-
- if (!ret) {
- u16 rx_ba_ssn = 0;
-
- if (action == IEEE80211_AMPDU_RX_START)
- rx_ba_ssn = *ssn;
-
- iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid,
- rx_ba_ssn, action);
- }
- mutex_unlock(&mvm->mutex);
-
- /*
- * If the tid is marked as started, we won't use it for offloaded
- * traffic on the next D0i3 entry. It's safe to unref.
- */
- if (tx_agg_ref)
- iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
-
- return ret;
-}
-
-static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- mvmvif->uploaded = false;
- mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
-
- spin_lock_bh(&mvm->time_event_lock);
- iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
- spin_unlock_bh(&mvm->time_event_lock);
-
- mvmvif->phy_ctxt = NULL;
- memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
-}
-
-static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
- const void *data, size_t datalen)
-{
- const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
- ssize_t bytes_read;
- ssize_t bytes_read_trans;
-
- if (offset < dump_ptrs->op_mode_len) {
- bytes_read = min_t(ssize_t, count,
- dump_ptrs->op_mode_len - offset);
- memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
- bytes_read);
- offset += bytes_read;
- count -= bytes_read;
-
- if (count == 0)
- return bytes_read;
- } else {
- bytes_read = 0;
- }
-
- if (!dump_ptrs->trans_ptr)
- return bytes_read;
-
- offset -= dump_ptrs->op_mode_len;
- bytes_read_trans = min_t(ssize_t, count,
- dump_ptrs->trans_ptr->len - offset);
- memcpy(buffer + bytes_read,
- (u8 *)dump_ptrs->trans_ptr->data + offset,
- bytes_read_trans);
-
- return bytes_read + bytes_read_trans;
-}
-
-static void iwl_mvm_free_coredump(const void *data)
-{
- const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
-
- vfree(fw_error_dump->op_mode_ptr);
- vfree(fw_error_dump->trans_ptr);
- kfree(fw_error_dump);
-}
-
-static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
- struct iwl_fw_error_dump_data **dump_data)
-{
- struct iwl_fw_error_dump_fifo *fifo_hdr;
- u32 *fifo_data;
- u32 fifo_len;
- unsigned long flags;
- int i, j;
-
- if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags))
- return;
-
- /* Pull RXF data from all RXFs */
- for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
- /*
- * Keep aside the additional offset that might be needed for
- * next RXF
- */
- u32 offset_diff = RXF_DIFF_FROM_PREV * i;
-
- fifo_hdr = (void *)(*dump_data)->data;
- fifo_data = (void *)fifo_hdr->data;
- fifo_len = mvm->shared_mem_cfg.rxfifo_size[i];
-
- /* No need to try to read the data if the length is 0 */
- if (fifo_len == 0)
- continue;
-
- /* Add a TLV for the RXF */
- (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
- (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
-
- fifo_hdr->fifo_num = cpu_to_le32(i);
- fifo_hdr->available_bytes =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_D_SPACE +
- offset_diff));
- fifo_hdr->wr_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_WR_PTR +
- offset_diff));
- fifo_hdr->rd_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_RD_PTR +
- offset_diff));
- fifo_hdr->fence_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_FENCE_PTR +
- offset_diff));
- fifo_hdr->fence_mode =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_SET_FENCE_MODE +
- offset_diff));
-
- /* Lock fence */
- iwl_trans_write_prph(mvm->trans,
- RXF_SET_FENCE_MODE + offset_diff, 0x1);
- /* Set fence pointer to the same place like WR pointer */
- iwl_trans_write_prph(mvm->trans,
- RXF_LD_WR2FENCE + offset_diff, 0x1);
- /* Set fence offset */
- iwl_trans_write_prph(mvm->trans,
- RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
- 0x0);
-
- /* Read FIFO */
- fifo_len /= sizeof(u32); /* Size in DWORDS */
- for (j = 0; j < fifo_len; j++)
- fifo_data[j] = iwl_trans_read_prph(mvm->trans,
- RXF_FIFO_RD_FENCE_INC +
- offset_diff);
- *dump_data = iwl_fw_error_next_data(*dump_data);
- }
-
- /* Pull TXF data from all TXFs */
- for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
- /* Mark the number of TXF we're pulling now */
- iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
-
- fifo_hdr = (void *)(*dump_data)->data;
- fifo_data = (void *)fifo_hdr->data;
- fifo_len = mvm->shared_mem_cfg.txfifo_size[i];
-
- /* No need to try to read the data if the length is 0 */
- if (fifo_len == 0)
- continue;
-
- /* Add a TLV for the FIFO */
- (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
- (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
-
- fifo_hdr->fifo_num = cpu_to_le32(i);
- fifo_hdr->available_bytes =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_FIFO_ITEM_CNT));
- fifo_hdr->wr_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_WR_PTR));
- fifo_hdr->rd_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_RD_PTR));
- fifo_hdr->fence_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_FENCE_PTR));
- fifo_hdr->fence_mode =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_LOCK_FENCE));
-
- /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
- iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
- TXF_WR_PTR);
-
- /* Dummy-read to advance the read pointer to the head */
- iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA);
-
- /* Read FIFO */
- fifo_len /= sizeof(u32); /* Size in DWORDS */
- for (j = 0; j < fifo_len; j++)
- fifo_data[j] = iwl_trans_read_prph(mvm->trans,
- TXF_READ_MODIFY_DATA);
- *dump_data = iwl_fw_error_next_data(*dump_data);
- }
-
- iwl_trans_release_nic_access(mvm->trans, &flags);
-}
-
-void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
-{
- if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert ||
- !mvm->fw_dump_desc)
- return;
-
- kfree(mvm->fw_dump_desc);
- mvm->fw_dump_desc = NULL;
-}
-
-#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */
-#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */
-
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
-{
- struct iwl_fw_error_dump_file *dump_file;
- struct iwl_fw_error_dump_data *dump_data;
- struct iwl_fw_error_dump_info *dump_info;
- struct iwl_fw_error_dump_mem *dump_mem;
- struct iwl_fw_error_dump_trigger_desc *dump_trig;
- struct iwl_mvm_dump_ptrs *fw_error_dump;
- u32 sram_len, sram_ofs;
- u32 file_len, fifo_data_len = 0;
- u32 smem_len = mvm->cfg->smem_len;
- u32 sram2_len = mvm->cfg->dccm2_len;
- bool monitor_dump_only = false;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* there's no point in fw dump if the bus is dead */
- if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) {
- IWL_ERR(mvm, "Skip fw error dump since bus is dead\n");
- return;
- }
-
- if (mvm->fw_dump_trig &&
- mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
- monitor_dump_only = true;
-
- fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
- if (!fw_error_dump)
- return;
-
- /* SRAM - include stack CCM if driver knows the values for it */
- if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
- const struct fw_img *img;
-
- img = &mvm->fw->img[mvm->cur_ucode];
- sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
- sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
- } else {
- sram_ofs = mvm->cfg->dccm_offset;
- sram_len = mvm->cfg->dccm_len;
- }
-
- /* reading RXF/TXF sizes */
- if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
- struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
- int i;
-
- fifo_data_len = 0;
-
- /* Count RXF size */
- for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
- if (!mem_cfg->rxfifo_size[i])
- continue;
-
- /* Add header info */
- fifo_data_len += mem_cfg->rxfifo_size[i] +
- sizeof(*dump_data) +
- sizeof(struct iwl_fw_error_dump_fifo);
- }
-
- for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
- if (!mem_cfg->txfifo_size[i])
- continue;
-
- /* Add header info */
- fifo_data_len += mem_cfg->txfifo_size[i] +
- sizeof(*dump_data) +
- sizeof(struct iwl_fw_error_dump_fifo);
- }
- }
-
- file_len = sizeof(*dump_file) +
- sizeof(*dump_data) * 2 +
- sram_len + sizeof(*dump_mem) +
- fifo_data_len +
- sizeof(*dump_info);
-
- /* Make room for the SMEM, if it exists */
- if (smem_len)
- file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
-
- /* Make room for the secondary SRAM, if it exists */
- if (sram2_len)
- file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
-
- /* Make room for fw's virtual image pages, if it exists */
- if (mvm->fw->img[mvm->cur_ucode].paging_mem_size)
- file_len += mvm->num_of_paging_blk *
- (sizeof(*dump_data) +
- sizeof(struct iwl_fw_error_dump_paging) +
- PAGING_BLOCK_SIZE);
-
- /* If we only want a monitor dump, reset the file length */
- if (monitor_dump_only) {
- file_len = sizeof(*dump_file) + sizeof(*dump_data) +
- sizeof(*dump_info);
- }
-
- /*
- * In 8000 HW family B-step include the ICCM (which resides separately)
- */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
- CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
- file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
- IWL8260_ICCM_LEN;
-
- if (mvm->fw_dump_desc)
- file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
- mvm->fw_dump_desc->len;
-
- dump_file = vzalloc(file_len);
- if (!dump_file) {
- kfree(fw_error_dump);
- iwl_mvm_free_fw_dump_desc(mvm);
- return;
- }
-
- fw_error_dump->op_mode_ptr = dump_file;
-
- dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
- dump_data = (void *)dump_file->data;
-
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
- dump_data->len = cpu_to_le32(sizeof(*dump_info));
- dump_info = (void *) dump_data->data;
- dump_info->device_family =
- mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
- cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
- cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
- dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
- memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
- sizeof(dump_info->fw_human_readable));
- strncpy(dump_info->dev_human_readable, mvm->cfg->name,
- sizeof(dump_info->dev_human_readable));
- strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
- sizeof(dump_info->bus_human_readable));
-
- dump_data = iwl_fw_error_next_data(dump_data);
- /* We only dump the FIFOs if the FW is in error state */
- if (test_bit(STATUS_FW_ERROR, &mvm->trans->status))
- iwl_mvm_dump_fifos(mvm, &dump_data);
-
- if (mvm->fw_dump_desc) {
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
- dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
- mvm->fw_dump_desc->len);
- dump_trig = (void *)dump_data->data;
- memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
- sizeof(*dump_trig) + mvm->fw_dump_desc->len);
-
- /* now we can free this copy */
- iwl_mvm_free_fw_dump_desc(mvm);
- dump_data = iwl_fw_error_next_data(dump_data);
- }
-
- /* In case we only want monitor dump, skip to dump trasport data */
- if (monitor_dump_only)
- goto dump_trans_data;
-
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
- dump_mem->offset = cpu_to_le32(sram_ofs);
- iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
- sram_len);
-
- if (smem_len) {
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
- dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
- iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
- dump_mem->data, smem_len);
- }
-
- if (sram2_len) {
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
- dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
- iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
- dump_mem->data, sram2_len);
- }
-
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
- CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
- sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
- dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
- iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
- dump_mem->data, IWL8260_ICCM_LEN);
- }
-
- /* Dump fw's virtual image */
- if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) {
- u32 i;
-
- for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
- struct iwl_fw_error_dump_paging *paging;
- struct page *pages =
- mvm->fw_paging_db[i].fw_paging_block;
-
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
- dump_data->len = cpu_to_le32(sizeof(*paging) +
- PAGING_BLOCK_SIZE);
- paging = (void *)dump_data->data;
- paging->index = cpu_to_le32(i);
- memcpy(paging->data, page_address(pages),
- PAGING_BLOCK_SIZE);
- }
- }
-
-dump_trans_data:
- fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans,
- mvm->fw_dump_trig);
- fw_error_dump->op_mode_len = file_len;
- if (fw_error_dump->trans_ptr)
- file_len += fw_error_dump->trans_ptr->len;
- dump_file->file_len = cpu_to_le32(file_len);
-
- dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
- GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
-
- mvm->fw_dump_trig = NULL;
- clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
-}
-
-struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
- .trig_desc = {
- .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
- },
-};
-
-static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
-{
- /* clear the D3 reconfig, we only need it to avoid dumping a
- * firmware coredump on reconfiguration, we shouldn't do that
- * on D3->D0 transition
- */
- if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) {
- mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert;
- iwl_mvm_fw_error_dump(mvm);
- }
-
- /* cleanup all stale references (scan, roc), but keep the
- * ucode_down ref until reconfig is complete
- */
- iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
-
- iwl_trans_stop_device(mvm->trans);
-
- mvm->scan_status = 0;
- mvm->ps_disabled = false;
- mvm->calibrating = false;
-
- /* just in case one was running */
- ieee80211_remain_on_channel_expired(mvm->hw);
-
- /*
- * cleanup all interfaces, even inactive ones, as some might have
- * gone down during the HW restart
- */
- ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm);
-
- mvm->p2p_device_vif = NULL;
- mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
-
- iwl_mvm_reset_phy_ctxts(mvm);
- memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
- memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained));
- memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
- memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
- memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
- memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
- memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk));
- memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk));
-
- ieee80211_wake_queues(mvm->hw);
-
- /* clear any stale d0i3 state */
- clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
-
- mvm->vif_count = 0;
- mvm->rx_ba_sessions = 0;
- mvm->fw_dbg_conf = FW_DBG_INVALID;
-
- /* keep statistics ticking */
- iwl_mvm_accu_radio_stats(mvm);
-}
-
-int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
-{
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Clean up some internal and mac80211 state on restart */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- iwl_mvm_restart_cleanup(mvm);
-
- ret = iwl_mvm_up(mvm);
-
- if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- /* Something went wrong - we need to finish some cleanup
- * that normally iwl_mvm_mac_restart_complete() below
- * would do.
- */
- clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
- iwl_mvm_d0i3_enable_tx(mvm, NULL);
- }
-
- return ret;
-}
-
-static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- /* Some hw restart cleanups must not hold the mutex */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- /*
- * Make sure we are out of d0i3. This is needed
- * to make sure the reference accounting is correct
- * (and there is no stale d0i3_exit_work).
- */
- wait_event_timeout(mvm->d0i3_exit_waitq,
- !test_bit(IWL_MVM_STATUS_IN_D0I3,
- &mvm->status),
- HZ);
- }
-
- mutex_lock(&mvm->mutex);
- ret = __iwl_mvm_mac_start(mvm);
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
-{
- int ret;
-
- mutex_lock(&mvm->mutex);
-
- clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
- iwl_mvm_d0i3_enable_tx(mvm, NULL);
- ret = iwl_mvm_update_quotas(mvm, true, NULL);
- if (ret)
- IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
- ret);
-
- /* allow transport/FW low power modes */
- iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
-
- /*
- * If we have TDLS peers, remove them. We don't know the last seqno/PN
- * of packets the FW sent out, so we must reconnect.
- */
- iwl_mvm_teardown_tdls_peers(mvm);
-
- mutex_unlock(&mvm->mutex);
-}
-
-static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
-{
- if (!iwl_mvm_is_d0i3_supported(mvm))
- return;
-
- if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND)
- if (!wait_event_timeout(mvm->d0i3_exit_waitq,
- !test_bit(IWL_MVM_STATUS_IN_D0I3,
- &mvm->status),
- HZ))
- WARN_ONCE(1, "D0i3 exit on resume timed out\n");
-}
-
-static void
-iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
- enum ieee80211_reconfig_type reconfig_type)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- switch (reconfig_type) {
- case IEEE80211_RECONFIG_TYPE_RESTART:
- iwl_mvm_restart_complete(mvm);
- break;
- case IEEE80211_RECONFIG_TYPE_SUSPEND:
- iwl_mvm_resume_complete(mvm);
- break;
- }
-}
-
-void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
-{
- lockdep_assert_held(&mvm->mutex);
-
- /* firmware counters are obviously reset now, but we shouldn't
- * partially track so also clear the fw_reset_accu counters.
- */
- memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
-
- /*
- * Disallow low power states when the FW is down by taking
- * the UCODE_DOWN ref. in case of ongoing hw restart the
- * ref is already taken, so don't take it again.
- */
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
-
- /* async_handlers_wk is now blocked */
-
- /*
- * The work item could be running or queued if the
- * ROC time event stops just as we get here.
- */
- flush_work(&mvm->roc_done_wk);
-
- iwl_trans_stop_device(mvm->trans);
-
- iwl_mvm_async_handlers_purge(mvm);
- /* async_handlers_list is empty and will stay empty: HW is stopped */
-
- /* the fw is stopped, the aux sta is dead: clean up driver state */
- iwl_mvm_del_aux_sta(mvm);
-
- /*
- * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete()
- * won't be called in this case).
- * But make sure to cleanup interfaces that have gone down before/during
- * HW restart was requested.
- */
- if (test_and_clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- ieee80211_iterate_interfaces(mvm->hw, 0,
- iwl_mvm_cleanup_iterator, mvm);
-
- /* We shouldn't have any UIDs still set. Loop over all the UIDs to
- * make sure there's nothing left there and warn if any is found.
- */
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
- int i;
-
- for (i = 0; i < mvm->max_scans; i++) {
- if (WARN_ONCE(mvm->scan_uid_status[i],
- "UMAC scan UID %d status was not cleaned\n",
- i))
- mvm->scan_uid_status[i] = 0;
- }
- }
-
- mvm->ucode_loaded = false;
-}
-
-static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- flush_work(&mvm->d0i3_exit_work);
- flush_work(&mvm->async_handlers_wk);
- cancel_delayed_work_sync(&mvm->fw_dump_wk);
- iwl_mvm_free_fw_dump_desc(mvm);
-
- mutex_lock(&mvm->mutex);
- __iwl_mvm_mac_stop(mvm);
- mutex_unlock(&mvm->mutex);
-
- /*
- * The worker might have been waiting for the mutex, let it run and
- * discover that its list is now empty.
- */
- cancel_work_sync(&mvm->async_handlers_wk);
-}
-
-static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
-{
- u16 i;
-
- lockdep_assert_held(&mvm->mutex);
-
- for (i = 0; i < NUM_PHY_CTX; i++)
- if (!mvm->phy_ctxts[i].ref)
- return &mvm->phy_ctxts[i];
-
- IWL_ERR(mvm, "No available PHY context\n");
- return NULL;
-}
-
-static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- s16 tx_power)
-{
- struct iwl_dev_tx_power_cmd cmd = {
- .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC),
- .v2.mac_context_id =
- cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id),
- .v2.pwr_restriction = cpu_to_le16(8 * tx_power),
- };
- int len = sizeof(cmd);
-
- if (tx_power == IWL_DEFAULT_MAX_TX_POWER)
- cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER);
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_CHAIN))
- len = sizeof(cmd.v2);
-
- return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd);
-}
-
-static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- mvmvif->mvm = mvm;
-
- /*
- * make sure D0i3 exit is completed, otherwise a target access
- * during tx queue configuration could be done when still in
- * D0i3 state.
- */
- ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF);
- if (ret)
- return ret;
-
- /*
- * Not much to do here. The stack will not allow interface
- * types or combinations that we didn't advertise, so we
- * don't really have to check the types.
- */
-
- mutex_lock(&mvm->mutex);
-
- /* make sure that beacon statistics don't go backwards with FW reset */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- mvmvif->beacon_stats.accu_num_beacons +=
- mvmvif->beacon_stats.num_beacons;
-
- /* Allocate resources for the MAC context, and add it to the fw */
- ret = iwl_mvm_mac_ctxt_init(mvm, vif);
- if (ret)
- goto out_unlock;
-
- /* Counting number of interfaces is needed for legacy PM */
- if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
- mvm->vif_count++;
-
- /*
- * The AP binding flow can be done only after the beacon
- * template is configured (which happens only in the mac80211
- * start_ap() flow), and adding the broadcast station can happen
- * only after the binding.
- * In addition, since modifying the MAC before adding a bcast
- * station is not allowed by the FW, delay the adding of MAC context to
- * the point where we can also add the bcast station.
- * In short: there's not much we can do at this point, other than
- * allocating resources :)
- */
- if (vif->type == NL80211_IFTYPE_AP ||
- vif->type == NL80211_IFTYPE_ADHOC) {
- ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
- if (ret) {
- IWL_ERR(mvm, "Failed to allocate bcast sta\n");
- goto out_release;
- }
-
- iwl_mvm_vif_dbgfs_register(mvm, vif);
- goto out_unlock;
- }
-
- mvmvif->features |= hw->netdev_features;
-
- ret = iwl_mvm_mac_ctxt_add(mvm, vif);
- if (ret)
- goto out_release;
-
- ret = iwl_mvm_power_update_mac(mvm);
- if (ret)
- goto out_remove_mac;
-
- /* beacon filtering */
- ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
- if (ret)
- goto out_remove_mac;
-
- if (!mvm->bf_allowed_vif &&
- vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
- mvm->bf_allowed_vif = mvmvif;
- vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
- IEEE80211_VIF_SUPPORTS_CQM_RSSI;
- }
-
- /*
- * P2P_DEVICE interface does not have a channel context assigned to it,
- * so a dedicated PHY context is allocated to it and the corresponding
- * MAC context is bound to it at this stage.
- */
- if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
-
- mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
- if (!mvmvif->phy_ctxt) {
- ret = -ENOSPC;
- goto out_free_bf;
- }
-
- iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (ret)
- goto out_unref_phy;
-
- ret = iwl_mvm_add_bcast_sta(mvm, vif);
- if (ret)
- goto out_unbind;
-
- /* Save a pointer to p2p device vif, so it can later be used to
- * update the p2p device MAC when a GO is started/stopped */
- mvm->p2p_device_vif = vif;
- }
-
- iwl_mvm_vif_dbgfs_register(mvm, vif);
- goto out_unlock;
-
- out_unbind:
- iwl_mvm_binding_remove_vif(mvm, vif);
- out_unref_phy:
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
- out_free_bf:
- if (mvm->bf_allowed_vif == mvmvif) {
- mvm->bf_allowed_vif = NULL;
- vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
- IEEE80211_VIF_SUPPORTS_CQM_RSSI);
- }
- out_remove_mac:
- mvmvif->phy_ctxt = NULL;
- iwl_mvm_mac_ctxt_remove(mvm, vif);
- out_release:
- if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
- mvm->vif_count--;
-
- iwl_mvm_mac_ctxt_release(mvm, vif);
- out_unlock:
- mutex_unlock(&mvm->mutex);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF);
-
- return ret;
-}
-
-static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
-
- if (tfd_msk) {
- /*
- * mac80211 first removes all the stations of the vif and
- * then removes the vif. When it removes a station it also
- * flushes the AMPDU session. So by now, all the AMPDU sessions
- * of all the stations of this vif are closed, and the queues
- * of these AMPDU sessions are properly closed.
- * We still need to take care of the shared queues of the vif.
- * Flush them here.
- */
- mutex_lock(&mvm->mutex);
- iwl_mvm_flush_tx_path(mvm, tfd_msk, 0);
- mutex_unlock(&mvm->mutex);
-
- /*
- * There are transports that buffer a few frames in the host.
- * For these, the flush above isn't enough since while we were
- * flushing, the transport might have sent more frames to the
- * device. To solve this, wait here until the transport is
- * empty. Technically, this could have replaced the flush
- * above, but flush is much faster than draining. So flush
- * first, and drain to make sure we have no frames in the
- * transport anymore.
- * If a station still had frames on the shared queues, it is
- * already marked as draining, so to complete the draining, we
- * just need to wait until the transport is empty.
- */
- iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk);
- }
-
- if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- /*
- * Flush the ROC worker which will flush the OFFCHANNEL queue.
- * We assume here that all the packets sent to the OFFCHANNEL
- * queue are sent in ROC session.
- */
- flush_work(&mvm->roc_done_wk);
- } else {
- /*
- * 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.
- */
- flush_work(&mvm->sta_drained_wk);
- }
-}
-
-static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- iwl_mvm_prepare_mac_removal(mvm, vif);
-
- mutex_lock(&mvm->mutex);
-
- if (mvm->bf_allowed_vif == mvmvif) {
- mvm->bf_allowed_vif = NULL;
- vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
- IEEE80211_VIF_SUPPORTS_CQM_RSSI);
- }
-
- iwl_mvm_vif_dbgfs_clean(mvm, vif);
-
- /*
- * For AP/GO interface, the tear down of the resources allocated to the
- * interface is be handled as part of the stop_ap flow.
- */
- if (vif->type == NL80211_IFTYPE_AP ||
- vif->type == NL80211_IFTYPE_ADHOC) {
-#ifdef CONFIG_NL80211_TESTMODE
- if (vif == mvm->noa_vif) {
- mvm->noa_vif = NULL;
- mvm->noa_duration = 0;
- }
-#endif
- iwl_mvm_dealloc_bcast_sta(mvm, vif);
- goto out_release;
- }
-
- if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- mvm->p2p_device_vif = NULL;
- iwl_mvm_rm_bcast_sta(mvm, vif);
- iwl_mvm_binding_remove_vif(mvm, vif);
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
- mvmvif->phy_ctxt = NULL;
- }
-
- if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
- mvm->vif_count--;
-
- iwl_mvm_power_update_mac(mvm);
- iwl_mvm_mac_ctxt_remove(mvm, vif);
-
-out_release:
- iwl_mvm_mac_ctxt_release(mvm, vif);
- mutex_unlock(&mvm->mutex);
-}
-
-static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
-{
- return 0;
-}
-
-struct iwl_mvm_mc_iter_data {
- struct iwl_mvm *mvm;
- int port_id;
-};
-
-static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_mc_iter_data *data = _data;
- struct iwl_mvm *mvm = data->mvm;
- struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
- int ret, len;
-
- /* if we don't have free ports, mcast frames will be dropped */
- if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM))
- return;
-
- if (vif->type != NL80211_IFTYPE_STATION ||
- !vif->bss_conf.assoc)
- return;
-
- cmd->port_id = data->port_id++;
- memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
- len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd);
- if (ret)
- IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
-}
-
-static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)
-{
- struct iwl_mvm_mc_iter_data iter_data = {
- .mvm = mvm,
- };
-
- lockdep_assert_held(&mvm->mutex);
-
- if (WARN_ON_ONCE(!mvm->mcast_filter_cmd))
- return;
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_mc_iface_iterator, &iter_data);
-}
-
-static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
- struct netdev_hw_addr_list *mc_list)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mcast_filter_cmd *cmd;
- struct netdev_hw_addr *addr;
- int addr_count;
- bool pass_all;
- int len;
-
- addr_count = netdev_hw_addr_list_count(mc_list);
- pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES ||
- IWL_MVM_FW_MCAST_FILTER_PASS_ALL;
- if (pass_all)
- addr_count = 0;
-
- len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4);
- cmd = kzalloc(len, GFP_ATOMIC);
- if (!cmd)
- return 0;
-
- if (pass_all) {
- cmd->pass_all = 1;
- return (u64)(unsigned long)cmd;
- }
-
- netdev_hw_addr_list_for_each(addr, mc_list) {
- IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n",
- cmd->count, addr->addr);
- memcpy(&cmd->addr_list[cmd->count * ETH_ALEN],
- addr->addr, ETH_ALEN);
- cmd->count++;
- }
-
- return (u64)(unsigned long)cmd;
-}
-
-static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
- unsigned int changed_flags,
- unsigned int *total_flags,
- u64 multicast)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
-
- mutex_lock(&mvm->mutex);
-
- /* replace previous configuration */
- kfree(mvm->mcast_filter_cmd);
- mvm->mcast_filter_cmd = cmd;
-
- if (!cmd)
- goto out;
-
- iwl_mvm_recalc_multicast(mvm);
-out:
- mutex_unlock(&mvm->mutex);
- *total_flags = 0;
-}
-
-static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- unsigned int filter_flags,
- unsigned int changed_flags)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- /* We support only filter for probe requests */
- if (!(changed_flags & FIF_PROBE_REQ))
- return;
-
- /* Supported only for p2p client interfaces */
- if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc ||
- !vif->p2p)
- return;
-
- mutex_lock(&mvm->mutex);
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- mutex_unlock(&mvm->mutex);
-}
-
-#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
-struct iwl_bcast_iter_data {
- struct iwl_mvm *mvm;
- struct iwl_bcast_filter_cmd *cmd;
- u8 current_filter;
-};
-
-static void
-iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif,
- const struct iwl_fw_bcast_filter *in_filter,
- struct iwl_fw_bcast_filter *out_filter)
-{
- struct iwl_fw_bcast_filter_attr *attr;
- int i;
-
- memcpy(out_filter, in_filter, sizeof(*out_filter));
-
- for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) {
- attr = &out_filter->attrs[i];
-
- if (!attr->mask)
- break;
-
- switch (attr->reserved1) {
- case cpu_to_le16(BC_FILTER_MAGIC_IP):
- if (vif->bss_conf.arp_addr_cnt != 1) {
- attr->mask = 0;
- continue;
- }
-
- attr->val = vif->bss_conf.arp_addr_list[0];
- break;
- case cpu_to_le16(BC_FILTER_MAGIC_MAC):
- attr->val = *(__be32 *)&vif->addr[2];
- break;
- default:
- break;
- }
- attr->reserved1 = 0;
- out_filter->num_attrs++;
- }
-}
-
-static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_bcast_iter_data *data = _data;
- struct iwl_mvm *mvm = data->mvm;
- struct iwl_bcast_filter_cmd *cmd = data->cmd;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_fw_bcast_mac *bcast_mac;
- int i;
-
- if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs)))
- return;
-
- bcast_mac = &cmd->macs[mvmvif->id];
-
- /*
- * enable filtering only for associated stations, but not for P2P
- * Clients
- */
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p ||
- !vif->bss_conf.assoc)
- return;
-
- bcast_mac->default_discard = 1;
-
- /* copy all configured filters */
- for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) {
- /*
- * Make sure we don't exceed our filters limit.
- * if there is still a valid filter to be configured,
- * be on the safe side and just allow bcast for this mac.
- */
- if (WARN_ON_ONCE(data->current_filter >=
- ARRAY_SIZE(cmd->filters))) {
- bcast_mac->default_discard = 0;
- bcast_mac->attached_filters = 0;
- break;
- }
-
- iwl_mvm_set_bcast_filter(vif,
- &mvm->bcast_filters[i],
- &cmd->filters[data->current_filter]);
-
- /* skip current filter if it contains no attributes */
- if (!cmd->filters[data->current_filter].num_attrs)
- continue;
-
- /* attach the filter to current mac */
- bcast_mac->attached_filters |=
- cpu_to_le16(BIT(data->current_filter));
-
- data->current_filter++;
- }
-}
-
-bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
- struct iwl_bcast_filter_cmd *cmd)
-{
- struct iwl_bcast_iter_data iter_data = {
- .mvm = mvm,
- .cmd = cmd,
- };
-
- if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL)
- return false;
-
- memset(cmd, 0, sizeof(*cmd));
- cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
- cmd->max_macs = ARRAY_SIZE(cmd->macs);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- /* use debugfs filters/macs if override is configured */
- if (mvm->dbgfs_bcast_filtering.override) {
- memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters,
- sizeof(cmd->filters));
- memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs,
- sizeof(cmd->macs));
- return true;
- }
-#endif
-
- /* if no filters are configured, do nothing */
- if (!mvm->bcast_filters)
- return false;
-
- /* configure and attach these filters for each associated sta vif */
- ieee80211_iterate_active_interfaces(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_bcast_filter_iterator, &iter_data);
-
- return true;
-}
-static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_bcast_filter_cmd cmd;
-
- if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
- return 0;
-
- if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
- return 0;
-
- return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
- sizeof(cmd), &cmd);
-}
-#else
-static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- return 0;
-}
-#endif
-
-static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *bss_conf,
- u32 changes)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- /*
- * Re-calculate the tsf id, as the master-slave relations depend on the
- * beacon interval, which was not known when the station interface was
- * added.
- */
- if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc)
- iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
-
- /*
- * If we're not associated yet, take the (new) BSSID before associating
- * so the firmware knows. If we're already associated, then use the old
- * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC
- * branch for disassociation below.
- */
- if (changes & BSS_CHANGED_BSSID && !mvmvif->associated)
- memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
-
- ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid);
- if (ret)
- IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
-
- /* after sending it once, adopt mac80211 data */
- memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
- mvmvif->associated = bss_conf->assoc;
-
- if (changes & BSS_CHANGED_ASSOC) {
- if (bss_conf->assoc) {
- /* clear statistics to get clean beacon counter */
- iwl_mvm_request_statistics(mvm, true);
- memset(&mvmvif->beacon_stats, 0,
- sizeof(mvmvif->beacon_stats));
-
- /* add quota for this interface */
- ret = iwl_mvm_update_quotas(mvm, true, NULL);
- if (ret) {
- IWL_ERR(mvm, "failed to update quotas\n");
- return;
- }
-
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status)) {
- /*
- * If we're restarting then the firmware will
- * obviously have lost synchronisation with
- * the AP. It will attempt to synchronise by
- * itself, but we can make it more reliable by
- * scheduling a session protection time event.
- *
- * The firmware needs to receive a beacon to
- * catch up with synchronisation, use 110% of
- * the beacon interval.
- *
- * Set a large maximum delay to allow for more
- * than a single interface.
- */
- u32 dur = (11 * vif->bss_conf.beacon_int) / 10;
- iwl_mvm_protect_session(mvm, vif, dur, dur,
- 5 * dur, false);
- }
-
- iwl_mvm_sf_update(mvm, vif, false);
- iwl_mvm_power_vif_assoc(mvm, vif);
- if (vif->p2p) {
- iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);
- iwl_mvm_update_smps(mvm, vif,
- IWL_MVM_SMPS_REQ_PROT,
- IEEE80211_SMPS_DYNAMIC);
- }
- } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
- /*
- * If update fails - SF might be running in associated
- * mode while disassociated - which is forbidden.
- */
- WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
- "Failed to update SF upon disassociation\n");
-
- /* remove AP station now that the MAC is unassoc */
- ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
- if (ret)
- IWL_ERR(mvm, "failed to remove AP station\n");
-
- if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
- mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
- mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
- /* remove quota for this interface */
- ret = iwl_mvm_update_quotas(mvm, false, NULL);
- if (ret)
- IWL_ERR(mvm, "failed to update quotas\n");
-
- if (vif->p2p)
- iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);
-
- /* this will take the cleared BSSID from bss_conf */
- ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- if (ret)
- IWL_ERR(mvm,
- "failed to update MAC %pM (clear after unassoc)\n",
- vif->addr);
- }
-
- iwl_mvm_recalc_multicast(mvm);
- iwl_mvm_configure_bcast_filter(mvm, vif);
-
- /* reset rssi values */
- mvmvif->bf_data.ave_beacon_signal = 0;
-
- iwl_mvm_bt_coex_vif_change(mvm);
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
- IEEE80211_SMPS_AUTOMATIC);
- } else if (changes & BSS_CHANGED_BEACON_INFO) {
- /*
- * We received a beacon _after_ association so
- * remove the session protection.
- */
- iwl_mvm_remove_time_event(mvm, mvmvif,
- &mvmvif->time_event_data);
- }
-
- if (changes & BSS_CHANGED_BEACON_INFO) {
- iwl_mvm_sf_update(mvm, vif, false);
- WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
- }
-
- if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) {
- ret = iwl_mvm_power_update_mac(mvm);
- if (ret)
- IWL_ERR(mvm, "failed to update power mode\n");
- }
-
- if (changes & BSS_CHANGED_TXPOWER) {
- IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
- bss_conf->txpower);
- iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
- }
-
- if (changes & BSS_CHANGED_CQM) {
- IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n");
- /* reset cqm events tracking */
- mvmvif->bf_data.last_cqm_event = 0;
- if (mvmvif->bf_data.bf_enabled) {
- ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
- if (ret)
- IWL_ERR(mvm,
- "failed to update CQM thresholds\n");
- }
- }
-
- if (changes & BSS_CHANGED_ARP_FILTER) {
- IWL_DEBUG_MAC80211(mvm, "arp filter changed\n");
- iwl_mvm_configure_bcast_filter(mvm, vif);
- }
-}
-
-static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- /*
- * iwl_mvm_mac_ctxt_add() might read directly from the device
- * (the system time), so make sure it is available.
- */
- ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP);
- if (ret)
- return ret;
-
- mutex_lock(&mvm->mutex);
-
- /* Send the beacon template */
- ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif);
- if (ret)
- goto out_unlock;
-
- /*
- * Re-calculate the tsf id, as the master-slave relations depend on the
- * beacon interval, which was not known when the AP interface was added.
- */
- if (vif->type == NL80211_IFTYPE_AP)
- iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
-
- mvmvif->ap_assoc_sta_count = 0;
-
- /* Add the mac context */
- ret = iwl_mvm_mac_ctxt_add(mvm, vif);
- if (ret)
- goto out_unlock;
-
- /* Perform the binding */
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (ret)
- goto out_remove;
-
- /* Send the bcast station. At this stage the TBTT and DTIM time events
- * are added and applied to the scheduler */
- ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
- if (ret)
- goto out_unbind;
-
- /* must be set before quota calculations */
- mvmvif->ap_ibss_active = true;
-
- /* power updated needs to be done before quotas */
- iwl_mvm_power_update_mac(mvm);
-
- ret = iwl_mvm_update_quotas(mvm, false, NULL);
- if (ret)
- goto out_quota_failed;
-
- /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
- if (vif->p2p && mvm->p2p_device_vif)
- iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
-
- iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
-
- iwl_mvm_bt_coex_vif_change(mvm);
-
- /* we don't support TDLS during DCM */
- if (iwl_mvm_phy_ctx_count(mvm) > 1)
- iwl_mvm_teardown_tdls_peers(mvm);
-
- goto out_unlock;
-
-out_quota_failed:
- iwl_mvm_power_update_mac(mvm);
- mvmvif->ap_ibss_active = false;
- iwl_mvm_send_rm_bcast_sta(mvm, vif);
-out_unbind:
- iwl_mvm_binding_remove_vif(mvm, vif);
-out_remove:
- iwl_mvm_mac_ctxt_remove(mvm, vif);
-out_unlock:
- mutex_unlock(&mvm->mutex);
- iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP);
- return ret;
-}
-
-static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- iwl_mvm_prepare_mac_removal(mvm, vif);
-
- mutex_lock(&mvm->mutex);
-
- /* Handle AP stop while in CSA */
- if (rcu_access_pointer(mvm->csa_vif) == vif) {
- iwl_mvm_remove_time_event(mvm, mvmvif,
- &mvmvif->time_event_data);
- RCU_INIT_POINTER(mvm->csa_vif, NULL);
- mvmvif->csa_countdown = false;
- }
-
- if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
- RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
- mvm->csa_tx_block_bcn_timeout = 0;
- }
-
- mvmvif->ap_ibss_active = false;
- mvm->ap_last_beacon_gp2 = 0;
-
- iwl_mvm_bt_coex_vif_change(mvm);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
-
- /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
- if (vif->p2p && mvm->p2p_device_vif)
- iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
-
- iwl_mvm_update_quotas(mvm, false, NULL);
- iwl_mvm_send_rm_bcast_sta(mvm, vif);
- iwl_mvm_binding_remove_vif(mvm, vif);
-
- iwl_mvm_power_update_mac(mvm);
-
- iwl_mvm_mac_ctxt_remove(mvm, vif);
-
- mutex_unlock(&mvm->mutex);
-}
-
-static void
-iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *bss_conf,
- u32 changes)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- /* Changes will be applied when the AP/IBSS is started */
- if (!mvmvif->ap_ibss_active)
- return;
-
- if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
- BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS) &&
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL))
- IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
-
- /* Need to send a new beacon template to the FW */
- if (changes & BSS_CHANGED_BEACON &&
- iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
- IWL_WARN(mvm, "Failed updating beacon data\n");
-
- if (changes & BSS_CHANGED_TXPOWER) {
- IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
- bss_conf->txpower);
- iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
- }
-
-}
-
-static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *bss_conf,
- u32 changes)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- /*
- * iwl_mvm_bss_info_changed_station() might call
- * iwl_mvm_protect_session(), which reads directly from
- * the device (the system time), so make sure it is available.
- */
- if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED))
- return;
-
- mutex_lock(&mvm->mutex);
-
- if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
- iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
- break;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_ADHOC:
- iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes);
- break;
- default:
- /* shouldn't happen */
- WARN_ON_ONCE(1);
- }
-
- mutex_unlock(&mvm->mutex);
- iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED);
-}
-
-static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_scan_request *hw_req)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- if (hw_req->req.n_channels == 0 ||
- hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels)
- return -EINVAL;
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies);
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- mutex_lock(&mvm->mutex);
-
- /* Due to a race condition, it's possible that mac80211 asks
- * us to stop a hw_scan when it's already stopped. This can
- * happen, for instance, if we stopped the scan ourselves,
- * called ieee80211_scan_completed() and the userspace called
- * cancel scan scan before ieee80211_scan_work() could run.
- * To handle that, simply return if the scan is not running.
- */
- if (mvm->scan_status & IWL_MVM_SCAN_REGULAR)
- iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
-
- mutex_unlock(&mvm->mutex);
-}
-
-static void
-iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta, u16 tids,
- int num_frames,
- enum ieee80211_frame_release_type reason,
- bool more_data)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- /* Called when we need to transmit (a) frame(s) from mac80211 */
-
- iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
- tids, more_data, false);
-}
-
-static void
-iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta, u16 tids,
- int num_frames,
- enum ieee80211_frame_release_type reason,
- bool more_data)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- /* Called when we need to transmit (a) frame(s) from agg queue */
-
- iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
- tids, more_data, true);
-}
-
-static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum sta_notify_cmd cmd,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- unsigned long txqs = 0, tids = 0;
- int tid;
-
- spin_lock_bh(&mvmsta->lock);
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
- struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
-
- if (tid_data->state != IWL_AGG_ON &&
- tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
- continue;
-
- __set_bit(tid_data->txq_id, &txqs);
-
- if (iwl_mvm_tid_queued(tid_data) == 0)
- continue;
-
- __set_bit(tid, &tids);
- }
-
- switch (cmd) {
- case STA_NOTIFY_SLEEP:
- if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
- ieee80211_sta_block_awake(hw, sta, true);
-
- for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT)
- ieee80211_sta_set_buffered(sta, tid, true);
-
- if (txqs)
- iwl_trans_freeze_txq_timer(mvm->trans, txqs, true);
- /*
- * The fw updates the STA to be asleep. Tx packets on the Tx
- * queues to this station will not be transmitted. The fw will
- * send a Tx response with TX_STATUS_FAIL_DEST_PS.
- */
- break;
- case STA_NOTIFY_AWAKE:
- if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
- break;
-
- if (txqs)
- iwl_trans_freeze_txq_timer(mvm->trans, txqs, false);
- iwl_mvm_sta_modify_ps_wake(mvm, sta);
- break;
- default:
- break;
- }
- spin_unlock_bh(&mvmsta->lock);
-}
-
-static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
-
- /*
- * This is called before mac80211 does RCU synchronisation,
- * so here we already invalidate our internal RCU-protected
- * station pointer. The rest of the code will thus no longer
- * be able to find the station this way, and we don't rely
- * on further RCU synchronisation after the sta_state()
- * callback deleted the station.
- */
- mutex_lock(&mvm->mutex);
- if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id]))
- rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
- ERR_PTR(-ENOENT));
-
- if (mvm_sta->vif->type == NL80211_IFTYPE_AP) {
- mvmvif->ap_assoc_sta_count--;
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- }
-
- mutex_unlock(&mvm->mutex);
-}
-
-static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- const u8 *bssid)
-{
- if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
- return;
-
- if (iwlwifi_mod_params.uapsd_disable) {
- vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
- return;
- }
-
- vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
-}
-
-static int iwl_mvm_mac_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 iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n",
- sta->addr, old_state, new_state);
-
- /* this would be a mac80211 bug ... but don't crash */
- if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
- return -EINVAL;
-
- /* if a STA is being removed, reuse its ID */
- flush_work(&mvm->sta_drained_wk);
-
- mutex_lock(&mvm->mutex);
- if (old_state == IEEE80211_STA_NOTEXIST &&
- new_state == IEEE80211_STA_NONE) {
- /*
- * Firmware bug - it'll crash if the beacon interval is less
- * than 16. We can't avoid connecting at all, so refuse the
- * station state change, this will cause mac80211 to abandon
- * attempts to connect to this AP, and eventually wpa_s will
- * blacklist the AP...
- */
- if (vif->type == NL80211_IFTYPE_STATION &&
- vif->bss_conf.beacon_int < 16) {
- IWL_ERR(mvm,
- "AP %pM beacon interval is %d, refusing due to firmware bug!\n",
- sta->addr, vif->bss_conf.beacon_int);
- ret = -EINVAL;
- goto out_unlock;
- }
-
- if (sta->tdls &&
- (vif->p2p ||
- iwl_mvm_tdls_sta_count(mvm, NULL) ==
- IWL_MVM_TDLS_STA_COUNT ||
- iwl_mvm_phy_ctx_count(mvm) > 1)) {
- IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
- ret = -EBUSY;
- goto out_unlock;
- }
-
- ret = iwl_mvm_add_sta(mvm, vif, sta);
- if (sta->tdls && ret == 0)
- iwl_mvm_recalc_tdls_state(mvm, vif, true);
- } else if (old_state == IEEE80211_STA_NONE &&
- new_state == IEEE80211_STA_AUTH) {
- /*
- * EBS may be disabled due to previous failures reported by FW.
- * Reset EBS status here assuming environment has been changed.
- */
- mvm->last_ebs_successful = true;
- iwl_mvm_check_uapsd(mvm, vif, sta->addr);
- ret = 0;
- } else if (old_state == IEEE80211_STA_AUTH &&
- new_state == IEEE80211_STA_ASSOC) {
- ret = iwl_mvm_update_sta(mvm, vif, sta);
- if (ret == 0)
- iwl_mvm_rs_rate_init(mvm, sta,
- mvmvif->phy_ctxt->channel->band,
- true);
- } else if (old_state == IEEE80211_STA_ASSOC &&
- new_state == IEEE80211_STA_AUTHORIZED) {
-
- /* we don't support TDLS during DCM */
- if (iwl_mvm_phy_ctx_count(mvm) > 1)
- iwl_mvm_teardown_tdls_peers(mvm);
-
- /* enable beacon filtering */
- WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
- ret = 0;
- } else if (old_state == IEEE80211_STA_AUTHORIZED &&
- new_state == IEEE80211_STA_ASSOC) {
- /* disable beacon filtering */
- WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0));
- ret = 0;
- } else if (old_state == IEEE80211_STA_ASSOC &&
- new_state == IEEE80211_STA_AUTH) {
- ret = 0;
- } else if (old_state == IEEE80211_STA_AUTH &&
- new_state == IEEE80211_STA_NONE) {
- ret = 0;
- } else if (old_state == IEEE80211_STA_NONE &&
- new_state == IEEE80211_STA_NOTEXIST) {
- ret = iwl_mvm_rm_sta(mvm, vif, sta);
- if (sta->tdls)
- iwl_mvm_recalc_tdls_state(mvm, vif, false);
- } else {
- ret = -EIO;
- }
- out_unlock:
- mutex_unlock(&mvm->mutex);
-
- if (sta->tdls && ret == 0) {
- if (old_state == IEEE80211_STA_NOTEXIST &&
- new_state == IEEE80211_STA_NONE)
- ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID);
- else if (old_state == IEEE80211_STA_NONE &&
- new_state == IEEE80211_STA_NOTEXIST)
- ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID);
- }
-
- return ret;
-}
-
-static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- mvm->rts_threshold = value;
-
- return 0;
-}
-
-static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u32 changed)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- if (vif->type == NL80211_IFTYPE_STATION &&
- changed & IEEE80211_RC_NSS_CHANGED)
- iwl_mvm_sf_update(mvm, vif, false);
-}
-
-static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, u16 ac,
- const struct ieee80211_tx_queue_params *params)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- mvmvif->queue_params[ac] = *params;
-
- /*
- * No need to update right away, we'll get BSS_CHANGED_QOS
- * The exception is P2P_DEVICE interface which needs immediate update.
- */
- if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- int ret;
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- mutex_unlock(&mvm->mutex);
- return ret;
- }
- return 0;
-}
-
-static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS,
- 200 + vif->bss_conf.beacon_int);
- u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS,
- 100 + vif->bss_conf.beacon_int);
-
- if (WARN_ON_ONCE(vif->bss_conf.assoc))
- return;
-
- /*
- * iwl_mvm_protect_session() reads directly from the device
- * (the system time), so make sure it is available.
- */
- if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX))
- return;
-
- mutex_lock(&mvm->mutex);
- /* Try really hard to protect the session and hear a beacon */
- iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false);
- mutex_unlock(&mvm->mutex);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX);
-}
-
-static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_sched_scan_request *req,
- struct ieee80211_scan_ies *ies)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- int ret;
-
- mutex_lock(&mvm->mutex);
-
- if (!vif->bss_conf.idle) {
- ret = -EBUSY;
- goto out;
- }
-
- ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED);
-
-out:
- mutex_unlock(&mvm->mutex);
- return ret;
-}
-
-static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- mutex_lock(&mvm->mutex);
-
- /* Due to a race condition, it's possible that mac80211 asks
- * us to stop a sched_scan when it's already stopped. This
- * can happen, for instance, if we stopped the scan ourselves,
- * called ieee80211_sched_scan_stopped() and the userspace called
- * stop sched scan scan before ieee80211_sched_scan_stopped_work()
- * could run. To handle this, simply return if the scan is
- * not running.
- */
- if (!(mvm->scan_status & IWL_MVM_SCAN_SCHED)) {
- mutex_unlock(&mvm->mutex);
- return 0;
- }
-
- ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, false);
- mutex_unlock(&mvm->mutex);
- iwl_mvm_wait_for_async_handlers(mvm);
-
- return ret;
-}
-
-static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
- enum set_key_cmd cmd,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
- u8 key_offset;
-
- if (iwlwifi_mod_params.sw_crypto) {
- IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n");
- return -EOPNOTSUPP;
- }
-
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- WARN_ON_ONCE(!ieee80211_hw_check(hw, MFP_CAPABLE));
- break;
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- /* For non-client mode, only use WEP keys for TX as we probably
- * don't have a station yet anyway and would then have to keep
- * track of the keys, linking them to each of the clients/peers
- * as they appear. For now, don't do that, for performance WEP
- * offload doesn't really matter much, but we need it for some
- * other offload features in client mode.
- */
- if (vif->type != NL80211_IFTYPE_STATION)
- return 0;
- break;
- default:
- /* currently FW supports only one optional cipher scheme */
- if (hw->n_cipher_schemes &&
- hw->cipher_schemes->cipher == key->cipher)
- key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
- else
- return -EOPNOTSUPP;
- }
-
- mutex_lock(&mvm->mutex);
-
- switch (cmd) {
- case SET_KEY:
- if ((vif->type == NL80211_IFTYPE_ADHOC ||
- vif->type == NL80211_IFTYPE_AP) && !sta) {
- /*
- * GTK on AP interface is a TX-only key, return 0;
- * on IBSS they're per-station and because we're lazy
- * we don't support them for RX, so do the same.
- */
- ret = 0;
- key->hw_key_idx = STA_KEY_IDX_INVALID;
- break;
- }
-
- /* During FW restart, in order to restore the state as it was,
- * don't try to reprogram keys we previously failed for.
- */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
- key->hw_key_idx == STA_KEY_IDX_INVALID) {
- IWL_DEBUG_MAC80211(mvm,
- "skip invalid idx key programming during restart\n");
- ret = 0;
- break;
- }
-
- /* in HW restart reuse the index, otherwise request a new one */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- key_offset = key->hw_key_idx;
- else
- key_offset = STA_KEY_IDX_INVALID;
-
- IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
- ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, key_offset);
- if (ret) {
- IWL_WARN(mvm, "set key failed\n");
- /*
- * can't add key for RX, but we don't need it
- * in the device for TX so still return 0
- */
- key->hw_key_idx = STA_KEY_IDX_INVALID;
- ret = 0;
- }
-
- break;
- case DISABLE_KEY:
- if (key->hw_key_idx == STA_KEY_IDX_INVALID) {
- ret = 0;
- break;
- }
-
- IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
- ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
- break;
- default:
- ret = -EINVAL;
- }
-
- mutex_unlock(&mvm->mutex);
- return ret;
-}
-
-static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta,
- u32 iv32, u16 *phase1key)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
- return;
-
- iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
-}
-
-
-static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_mvm *mvm =
- container_of(notif_wait, struct iwl_mvm, notif_wait);
- struct iwl_hs20_roc_res *resp;
- int resp_len = iwl_rx_packet_payload_len(pkt);
- struct iwl_mvm_time_event_data *te_data = data;
-
- if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD))
- return true;
-
- if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
- IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n");
- return true;
- }
-
- resp = (void *)pkt->data;
-
- IWL_DEBUG_TE(mvm,
- "Aux ROC: Recieved response from ucode: status=%d uid=%d\n",
- resp->status, resp->event_unique_id);
-
- te_data->uid = le32_to_cpu(resp->event_unique_id);
- IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
- te_data->uid);
-
- spin_lock_bh(&mvm->time_event_lock);
- list_add_tail(&te_data->list, &mvm->aux_roc_te_list);
- spin_unlock_bh(&mvm->time_event_lock);
-
- return true;
-}
-
-#define AUX_ROC_MAX_DELAY_ON_CHANNEL 200
-static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
- struct ieee80211_channel *channel,
- struct ieee80211_vif *vif,
- int duration)
-{
- int res, time_reg = DEVICE_SYSTEM_TIME_REG;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data;
- static const u16 time_event_response[] = { HOT_SPOT_CMD };
- struct iwl_notification_wait wait_time_event;
- struct iwl_hs20_roc_req aux_roc_req = {
- .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
- .id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)),
- .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id),
- /* Set the channel info data */
- .channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ?
- PHY_BAND_24 : PHY_BAND_5,
- .channel_info.channel = channel->hw_value,
- .channel_info.width = PHY_VHT_CHANNEL_MODE20,
- /* Set the time and duration */
- .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)),
- .apply_time_max_delay =
- cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)),
- .duration = cpu_to_le32(MSEC_TO_TU(duration)),
- };
-
- /* Set the node address */
- memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
-
- lockdep_assert_held(&mvm->mutex);
-
- spin_lock_bh(&mvm->time_event_lock);
-
- if (WARN_ON(te_data->id == HOT_SPOT_CMD)) {
- spin_unlock_bh(&mvm->time_event_lock);
- return -EIO;
- }
-
- te_data->vif = vif;
- te_data->duration = duration;
- te_data->id = HOT_SPOT_CMD;
-
- spin_unlock_bh(&mvm->time_event_lock);
-
- /*
- * Use a notification wait, which really just processes the
- * command response and doesn't wait for anything, in order
- * to be able to process the response and get the UID inside
- * the RX path. Using CMD_WANT_SKB doesn't work because it
- * stores the buffer and then wakes up this thread, by which
- * time another notification (that the time event started)
- * might already be processed unsuccessfully.
- */
- iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
- time_event_response,
- ARRAY_SIZE(time_event_response),
- iwl_mvm_rx_aux_roc, te_data);
-
- res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req),
- &aux_roc_req);
-
- if (res) {
- IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res);
- iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
- goto out_clear_te;
- }
-
- /* No need to wait for anything, so just pass 1 (0 isn't valid) */
- res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
- /* should never fail */
- WARN_ON_ONCE(res);
-
- if (res) {
- out_clear_te:
- spin_lock_bh(&mvm->time_event_lock);
- iwl_mvm_te_clear_data(mvm, te_data);
- spin_unlock_bh(&mvm->time_event_lock);
- }
-
- return res;
-}
-
-static int iwl_mvm_roc(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_channel *channel,
- int duration,
- enum ieee80211_roc_type type)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct cfg80211_chan_def chandef;
- struct iwl_mvm_phy_ctxt *phy_ctxt;
- int ret, i;
-
- IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
- duration, type);
-
- flush_work(&mvm->roc_done_wk);
-
- mutex_lock(&mvm->mutex);
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) {
- /* Use aux roc framework (HS20) */
- ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
- vif, duration);
- goto out_unlock;
- }
- IWL_ERR(mvm, "hotspot not supported\n");
- ret = -EINVAL;
- goto out_unlock;
- case NL80211_IFTYPE_P2P_DEVICE:
- /* handle below */
- break;
- default:
- IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
- ret = -EINVAL;
- goto out_unlock;
- }
-
- for (i = 0; i < NUM_PHY_CTX; i++) {
- phy_ctxt = &mvm->phy_ctxts[i];
- if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
- continue;
-
- if (phy_ctxt->ref && channel == phy_ctxt->channel) {
- /*
- * Unbind the P2P_DEVICE from the current PHY context,
- * and if the PHY context is not used remove it.
- */
- ret = iwl_mvm_binding_remove_vif(mvm, vif);
- if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
- goto out_unlock;
-
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
-
- /* Bind the P2P_DEVICE to the current PHY Context */
- mvmvif->phy_ctxt = phy_ctxt;
-
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (WARN(ret, "Failed binding P2P_DEVICE\n"))
- goto out_unlock;
-
- iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
- goto schedule_time_event;
- }
- }
-
- /* Need to update the PHY context only if the ROC channel changed */
- if (channel == mvmvif->phy_ctxt->channel)
- goto schedule_time_event;
-
- cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
-
- /*
- * Change the PHY context configuration as it is currently referenced
- * only by the P2P Device MAC
- */
- if (mvmvif->phy_ctxt->ref == 1) {
- ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
- &chandef, 1, 1);
- if (ret)
- goto out_unlock;
- } else {
- /*
- * The PHY context is shared with other MACs. Need to remove the
- * P2P Device from the binding, allocate an new PHY context and
- * create a new binding
- */
- phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
- if (!phy_ctxt) {
- ret = -ENOSPC;
- goto out_unlock;
- }
-
- ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef,
- 1, 1);
- if (ret) {
- IWL_ERR(mvm, "Failed to change PHY context\n");
- goto out_unlock;
- }
-
- /* Unbind the P2P_DEVICE from the current PHY context */
- ret = iwl_mvm_binding_remove_vif(mvm, vif);
- if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
- goto out_unlock;
-
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
-
- /* Bind the P2P_DEVICE to the new allocated PHY context */
- mvmvif->phy_ctxt = phy_ctxt;
-
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (WARN(ret, "Failed binding P2P_DEVICE\n"))
- goto out_unlock;
-
- iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
- }
-
-schedule_time_event:
- /* Schedule the time events */
- ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
-
-out_unlock:
- mutex_unlock(&mvm->mutex);
- IWL_DEBUG_MAC80211(mvm, "leave\n");
- return ret;
-}
-
-static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- IWL_DEBUG_MAC80211(mvm, "enter\n");
-
- mutex_lock(&mvm->mutex);
- iwl_mvm_stop_roc(mvm);
- mutex_unlock(&mvm->mutex);
-
- IWL_DEBUG_MAC80211(mvm, "leave\n");
- return 0;
-}
-
-static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
- struct ieee80211_chanctx_conf *ctx)
-{
- u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
- struct iwl_mvm_phy_ctxt *phy_ctxt;
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
-
- phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
- if (!phy_ctxt) {
- ret = -ENOSPC;
- goto out;
- }
-
- ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
- ctx->rx_chains_static,
- ctx->rx_chains_dynamic);
- if (ret) {
- IWL_ERR(mvm, "Failed to add PHY context\n");
- goto out;
- }
-
- iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
- *phy_ctxt_id = phy_ctxt->id;
-out:
- return ret;
-}
-
-static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- mutex_lock(&mvm->mutex);
- ret = __iwl_mvm_add_chanctx(mvm, ctx);
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
- struct ieee80211_chanctx_conf *ctx)
-{
- u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
- struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
-
- lockdep_assert_held(&mvm->mutex);
-
- iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
-}
-
-static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- mutex_lock(&mvm->mutex);
- __iwl_mvm_remove_chanctx(mvm, ctx);
- mutex_unlock(&mvm->mutex);
-}
-
-static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx,
- u32 changed)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
- struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
-
- if (WARN_ONCE((phy_ctxt->ref > 1) &&
- (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
- IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
- IEEE80211_CHANCTX_CHANGE_RADAR |
- IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)),
- "Cannot change PHY. Ref=%d, changed=0x%X\n",
- phy_ctxt->ref, changed))
- return;
-
- mutex_lock(&mvm->mutex);
- iwl_mvm_bt_coex_vif_change(mvm);
- iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
- ctx->rx_chains_static,
- ctx->rx_chains_dynamic);
- mutex_unlock(&mvm->mutex);
-}
-
-static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx,
- bool switching_chanctx)
-{
- u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
- struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- mvmvif->phy_ctxt = phy_ctxt;
-
- switch (vif->type) {
- case NL80211_IFTYPE_AP:
- /* only needed if we're switching chanctx (i.e. during CSA) */
- if (switching_chanctx) {
- mvmvif->ap_ibss_active = true;
- break;
- }
- case NL80211_IFTYPE_ADHOC:
- /*
- * The AP binding flow is handled as part of the start_ap flow
- * (in bss_info_changed), similarly for IBSS.
- */
- ret = 0;
- goto out;
- case NL80211_IFTYPE_STATION:
- break;
- case NL80211_IFTYPE_MONITOR:
- /* always disable PS when a monitor interface is active */
- mvmvif->ps_disabled = true;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (ret)
- goto out;
-
- /*
- * Power state must be updated before quotas,
- * otherwise fw will complain.
- */
- iwl_mvm_power_update_mac(mvm);
-
- /* Setting the quota at this stage is only required for monitor
- * interfaces. For the other types, the bss_info changed flow
- * will handle quota settings.
- */
- if (vif->type == NL80211_IFTYPE_MONITOR) {
- mvmvif->monitor_active = true;
- ret = iwl_mvm_update_quotas(mvm, false, NULL);
- if (ret)
- goto out_remove_binding;
- }
-
- /* Handle binding during CSA */
- if (vif->type == NL80211_IFTYPE_AP) {
- iwl_mvm_update_quotas(mvm, false, NULL);
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- }
-
- if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
- u32 duration = 2 * vif->bss_conf.beacon_int;
-
- /* iwl_mvm_protect_session() reads directly from the
- * device (the system time), so make sure it is
- * available.
- */
- ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
- if (ret)
- goto out_remove_binding;
-
- /* Protect the session to make sure we hear the first
- * beacon on the new channel.
- */
- iwl_mvm_protect_session(mvm, vif, duration, duration,
- vif->bss_conf.beacon_int / 2,
- true);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
-
- iwl_mvm_update_quotas(mvm, false, NULL);
- }
-
- goto out;
-
-out_remove_binding:
- iwl_mvm_binding_remove_vif(mvm, vif);
- iwl_mvm_power_update_mac(mvm);
-out:
- if (ret)
- mvmvif->phy_ctxt = NULL;
- return ret;
-}
-static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- mutex_lock(&mvm->mutex);
- ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx,
- bool switching_chanctx)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct ieee80211_vif *disabled_vif = NULL;
-
- lockdep_assert_held(&mvm->mutex);
-
- iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
-
- switch (vif->type) {
- case NL80211_IFTYPE_ADHOC:
- goto out;
- case NL80211_IFTYPE_MONITOR:
- mvmvif->monitor_active = false;
- mvmvif->ps_disabled = false;
- break;
- case NL80211_IFTYPE_AP:
- /* This part is triggered only during CSA */
- if (!switching_chanctx || !mvmvif->ap_ibss_active)
- goto out;
-
- mvmvif->csa_countdown = false;
-
- /* Set CS bit on all the stations */
- iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
-
- /* Save blocked iface, the timeout is set on the next beacon */
- rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
-
- mvmvif->ap_ibss_active = false;
- break;
- case NL80211_IFTYPE_STATION:
- if (!switching_chanctx)
- break;
-
- disabled_vif = vif;
-
- iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
- break;
- default:
- break;
- }
-
- iwl_mvm_update_quotas(mvm, false, disabled_vif);
- iwl_mvm_binding_remove_vif(mvm, vif);
-
-out:
- mvmvif->phy_ctxt = NULL;
- iwl_mvm_power_update_mac(mvm);
-}
-
-static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- mutex_lock(&mvm->mutex);
- __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
- mutex_unlock(&mvm->mutex);
-}
-
-static int
-iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
- struct ieee80211_vif_chanctx_switch *vifs)
-{
- int ret;
-
- mutex_lock(&mvm->mutex);
- __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
- __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
-
- ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
- if (ret) {
- IWL_ERR(mvm, "failed to add new_ctx during channel switch\n");
- goto out_reassign;
- }
-
- ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
- true);
- if (ret) {
- IWL_ERR(mvm,
- "failed to assign new_ctx during channel switch\n");
- goto out_remove;
- }
-
- /* we don't support TDLS during DCM - can be caused by channel switch */
- if (iwl_mvm_phy_ctx_count(mvm) > 1)
- iwl_mvm_teardown_tdls_peers(mvm);
-
- goto out;
-
-out_remove:
- __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
-
-out_reassign:
- if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) {
- IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
- goto out_restart;
- }
-
- if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
- true)) {
- IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
- goto out_restart;
- }
-
- goto out;
-
-out_restart:
- /* things keep failing, better restart the hw */
- iwl_mvm_nic_restart(mvm, false);
-
-out:
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static int
-iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
- struct ieee80211_vif_chanctx_switch *vifs)
-{
- int ret;
-
- mutex_lock(&mvm->mutex);
- __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
-
- ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
- true);
- if (ret) {
- IWL_ERR(mvm,
- "failed to assign new_ctx during channel switch\n");
- goto out_reassign;
- }
-
- goto out;
-
-out_reassign:
- if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
- true)) {
- IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
- goto out_restart;
- }
-
- goto out;
-
-out_restart:
- /* things keep failing, better restart the hw */
- iwl_mvm_nic_restart(mvm, false);
-
-out:
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif_chanctx_switch *vifs,
- int n_vifs,
- enum ieee80211_chanctx_switch_mode mode)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- /* we only support a single-vif right now */
- if (n_vifs > 1)
- return -EOPNOTSUPP;
-
- switch (mode) {
- case CHANCTX_SWMODE_SWAP_CONTEXTS:
- ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
- break;
- case CHANCTX_SWMODE_REASSIGN_VIF:
- ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta,
- bool set)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
-
- if (!mvm_sta || !mvm_sta->vif) {
- IWL_ERR(mvm, "Station is not associated to a vif\n");
- return -EINVAL;
- }
-
- return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
-}
-
-#ifdef CONFIG_NL80211_TESTMODE
-static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
- [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
- [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
- [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
-};
-
-static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- void *data, int len)
-{
- struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
- int err;
- u32 noa_duration;
-
- err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy);
- if (err)
- return err;
-
- if (!tb[IWL_MVM_TM_ATTR_CMD])
- return -EINVAL;
-
- switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
- case IWL_MVM_TM_CMD_SET_NOA:
- if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
- !vif->bss_conf.enable_beacon ||
- !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
- return -EINVAL;
-
- noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
- if (noa_duration >= vif->bss_conf.beacon_int)
- return -EINVAL;
-
- mvm->noa_duration = noa_duration;
- mvm->noa_vif = vif;
-
- return iwl_mvm_update_quotas(mvm, false, NULL);
- case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
- /* must be associated client vif - ignore authorized */
- if (!vif || vif->type != NL80211_IFTYPE_STATION ||
- !vif->bss_conf.assoc || !vif->bss_conf.dtim_period ||
- !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
- return -EINVAL;
-
- if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
- return iwl_mvm_enable_beacon_filter(mvm, vif, 0);
- return iwl_mvm_disable_beacon_filter(mvm, vif, 0);
- }
-
- return -EOPNOTSUPP;
-}
-
-static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- void *data, int len)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int err;
-
- mutex_lock(&mvm->mutex);
- err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
- mutex_unlock(&mvm->mutex);
-
- return err;
-}
-#endif
-
-static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_channel_switch *chsw)
-{
- /* By implementing this operation, we prevent mac80211 from
- * starting its own channel switch timer, so that we can call
- * ieee80211_chswitch_done() ourselves at the right time
- * (which is when the absence time event starts).
- */
-
- IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw),
- "dummy channel switch op\n");
-}
-
-static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_channel_switch *chsw)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct ieee80211_vif *csa_vif;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- u32 apply_time;
- int ret;
-
- mutex_lock(&mvm->mutex);
-
- mvmvif->csa_failed = false;
-
- IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
- chsw->chandef.center_freq1);
-
- iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH);
-
- switch (vif->type) {
- case NL80211_IFTYPE_AP:
- csa_vif =
- rcu_dereference_protected(mvm->csa_vif,
- lockdep_is_held(&mvm->mutex));
- if (WARN_ONCE(csa_vif && csa_vif->csa_active,
- "Another CSA is already in progress")) {
- ret = -EBUSY;
- goto out_unlock;
- }
-
- rcu_assign_pointer(mvm->csa_vif, vif);
-
- if (WARN_ONCE(mvmvif->csa_countdown,
- "Previous CSA countdown didn't complete")) {
- ret = -EBUSY;
- goto out_unlock;
- }
-
- break;
- case NL80211_IFTYPE_STATION:
- /* Schedule the time event to a bit before beacon 1,
- * to make sure we're in the new channel when the
- * GO/AP arrives.
- */
- apply_time = chsw->device_timestamp +
- ((vif->bss_conf.beacon_int * (chsw->count - 1) -
- IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024);
-
- if (chsw->block_tx)
- iwl_mvm_csa_client_absent(mvm, vif);
-
- iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
- apply_time);
- if (mvmvif->bf_data.bf_enabled) {
- ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
- if (ret)
- goto out_unlock;
- }
-
- break;
- default:
- break;
- }
-
- mvmvif->ps_disabled = true;
-
- ret = iwl_mvm_power_update_ps(mvm);
- if (ret)
- goto out_unlock;
-
- /* we won't be on this channel any longer */
- iwl_mvm_teardown_tdls_peers(mvm);
-
-out_unlock:
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- mutex_lock(&mvm->mutex);
-
- if (mvmvif->csa_failed) {
- mvmvif->csa_failed = false;
- ret = -EIO;
- goto out_unlock;
- }
-
- if (vif->type == NL80211_IFTYPE_STATION) {
- struct iwl_mvm_sta *mvmsta;
-
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
- mvmvif->ap_sta_id);
-
- if (WARN_ON(!mvmsta)) {
- ret = -EIO;
- goto out_unlock;
- }
-
- iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
-
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
-
- ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
- if (ret)
- goto out_unlock;
-
- iwl_mvm_stop_session_protection(mvm, vif);
- }
-
- mvmvif->ps_disabled = false;
-
- ret = iwl_mvm_power_update_ps(mvm);
-
-out_unlock:
- mutex_unlock(&mvm->mutex);
-
- return ret;
-}
-
-static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, u32 queues, bool drop)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif;
- struct iwl_mvm_sta *mvmsta;
- struct ieee80211_sta *sta;
- int i;
- u32 msk = 0;
-
- if (!vif || vif->type != NL80211_IFTYPE_STATION)
- return;
-
- mutex_lock(&mvm->mutex);
- mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- /* flush the AP-station and all TDLS peers */
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(sta))
- continue;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- if (mvmsta->vif != vif)
- continue;
-
- /* make sure only TDLS peers or the AP are flushed */
- WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
-
- msk |= mvmsta->tfd_queue_msk;
- }
-
- if (drop) {
- if (iwl_mvm_flush_tx_path(mvm, msk, 0))
- IWL_ERR(mvm, "flush request fail\n");
- mutex_unlock(&mvm->mutex);
- } else {
- mutex_unlock(&mvm->mutex);
-
- /* this can take a while, and we may need/want other operations
- * to succeed while doing this, so do it without the mutex held
- */
- iwl_trans_wait_tx_queue_empty(mvm->trans, msk);
- }
-}
-
-static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
- struct survey_info *survey)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
-
- memset(survey, 0, sizeof(*survey));
-
- /* only support global statistics right now */
- if (idx != 0)
- return -ENOENT;
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
- return -ENOENT;
-
- mutex_lock(&mvm->mutex);
-
- if (mvm->ucode_loaded) {
- ret = iwl_mvm_request_statistics(mvm, false);
- if (ret)
- goto out;
- }
-
- survey->filled = SURVEY_INFO_TIME |
- SURVEY_INFO_TIME_RX |
- SURVEY_INFO_TIME_TX |
- SURVEY_INFO_TIME_SCAN;
- survey->time = mvm->accu_radio_stats.on_time_rf +
- mvm->radio_stats.on_time_rf;
- do_div(survey->time, USEC_PER_MSEC);
-
- survey->time_rx = mvm->accu_radio_stats.rx_time +
- mvm->radio_stats.rx_time;
- do_div(survey->time_rx, USEC_PER_MSEC);
-
- survey->time_tx = mvm->accu_radio_stats.tx_time +
- mvm->radio_stats.tx_time;
- do_div(survey->time_tx, USEC_PER_MSEC);
-
- survey->time_scan = mvm->accu_radio_stats.on_time_scan +
- mvm->radio_stats.on_time_scan;
- do_div(survey->time_scan, USEC_PER_MSEC);
-
- ret = 0;
- out:
- mutex_unlock(&mvm->mutex);
- return ret;
-}
-
-static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct station_info *sinfo)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
- return;
-
- /* if beacon filtering isn't on mac80211 does it anyway */
- if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
- return;
-
- if (!vif->bss_conf.assoc)
- return;
-
- mutex_lock(&mvm->mutex);
-
- if (mvmvif->ap_sta_id != mvmsta->sta_id)
- goto unlock;
-
- if (iwl_mvm_request_statistics(mvm, false))
- goto unlock;
-
- sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons +
- mvmvif->beacon_stats.accu_num_beacons;
- sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX);
- if (mvmvif->beacon_stats.avg_signal) {
- /* firmware only reports a value after RXing a few beacons */
- sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal;
- sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
- }
- unlock:
- mutex_unlock(&mvm->mutex);
-}
-
-static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- const struct ieee80211_event *event)
-{
-#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \
- do { \
- if ((_cnt) && --(_cnt)) \
- break; \
- iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\
- } while (0)
-
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_mlme *trig_mlme;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
- trig_mlme = (void *)trig->data;
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
- return;
-
- if (event->u.mlme.data == ASSOC_EVENT) {
- if (event->u.mlme.status == MLME_DENIED)
- CHECK_MLME_TRIGGER(mvm, trig, buf,
- trig_mlme->stop_assoc_denied,
- "DENIED ASSOC: reason %d",
- event->u.mlme.reason);
- else if (event->u.mlme.status == MLME_TIMEOUT)
- CHECK_MLME_TRIGGER(mvm, trig, buf,
- trig_mlme->stop_assoc_timeout,
- "ASSOC TIMEOUT");
- } else if (event->u.mlme.data == AUTH_EVENT) {
- if (event->u.mlme.status == MLME_DENIED)
- CHECK_MLME_TRIGGER(mvm, trig, buf,
- trig_mlme->stop_auth_denied,
- "DENIED AUTH: reason %d",
- event->u.mlme.reason);
- else if (event->u.mlme.status == MLME_TIMEOUT)
- CHECK_MLME_TRIGGER(mvm, trig, buf,
- trig_mlme->stop_auth_timeout,
- "AUTH TIMEOUT");
- } else if (event->u.mlme.data == DEAUTH_RX_EVENT) {
- CHECK_MLME_TRIGGER(mvm, trig, buf,
- trig_mlme->stop_rx_deauth,
- "DEAUTH RX %d", event->u.mlme.reason);
- } else if (event->u.mlme.data == DEAUTH_TX_EVENT) {
- CHECK_MLME_TRIGGER(mvm, trig, buf,
- trig_mlme->stop_tx_deauth,
- "DEAUTH TX %d", event->u.mlme.reason);
- }
-#undef CHECK_MLME_TRIGGER
-}
-
-static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- const struct ieee80211_event *event)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_ba *ba_trig;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
- ba_trig = (void *)trig->data;
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
- return;
-
- if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid)))
- return;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trig,
- "BAR received from %pM, tid %d, ssn %d",
- event->u.ba.sta->addr, event->u.ba.tid,
- event->u.ba.ssn);
-}
-
-static void
-iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- const struct ieee80211_event *event)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_ba *ba_trig;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
- ba_trig = (void *)trig->data;
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
- return;
-
- if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid)))
- return;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trig,
- "Frame from %pM timed out, tid %d",
- event->u.ba.sta->addr, event->u.ba.tid);
-}
-
-static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- const struct ieee80211_event *event)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- switch (event->type) {
- case MLME_EVENT:
- iwl_mvm_event_mlme_callback(mvm, vif, event);
- break;
- case BAR_RX_EVENT:
- iwl_mvm_event_bar_rx_callback(mvm, vif, event);
- break;
- case BA_FRAME_TIMEOUT:
- iwl_mvm_event_frame_timeout_callback(mvm, vif, event);
- break;
- default:
- break;
- }
-}
-
-const struct ieee80211_ops iwl_mvm_hw_ops = {
- .tx = iwl_mvm_mac_tx,
- .ampdu_action = iwl_mvm_mac_ampdu_action,
- .start = iwl_mvm_mac_start,
- .reconfig_complete = iwl_mvm_mac_reconfig_complete,
- .stop = iwl_mvm_mac_stop,
- .add_interface = iwl_mvm_mac_add_interface,
- .remove_interface = iwl_mvm_mac_remove_interface,
- .config = iwl_mvm_mac_config,
- .prepare_multicast = iwl_mvm_prepare_multicast,
- .configure_filter = iwl_mvm_configure_filter,
- .config_iface_filter = iwl_mvm_config_iface_filter,
- .bss_info_changed = iwl_mvm_bss_info_changed,
- .hw_scan = iwl_mvm_mac_hw_scan,
- .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,
- .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove,
- .sta_state = iwl_mvm_mac_sta_state,
- .sta_notify = iwl_mvm_mac_sta_notify,
- .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
- .release_buffered_frames = iwl_mvm_mac_release_buffered_frames,
- .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
- .sta_rc_update = iwl_mvm_sta_rc_update,
- .conf_tx = iwl_mvm_mac_conf_tx,
- .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
- .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
- .flush = iwl_mvm_mac_flush,
- .sched_scan_start = iwl_mvm_mac_sched_scan_start,
- .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
- .set_key = iwl_mvm_mac_set_key,
- .update_tkip_key = iwl_mvm_mac_update_tkip_key,
- .remain_on_channel = iwl_mvm_roc,
- .cancel_remain_on_channel = iwl_mvm_cancel_roc,
- .add_chanctx = iwl_mvm_add_chanctx,
- .remove_chanctx = iwl_mvm_remove_chanctx,
- .change_chanctx = iwl_mvm_change_chanctx,
- .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
- .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
- .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx,
-
- .start_ap = iwl_mvm_start_ap_ibss,
- .stop_ap = iwl_mvm_stop_ap_ibss,
- .join_ibss = iwl_mvm_start_ap_ibss,
- .leave_ibss = iwl_mvm_stop_ap_ibss,
-
- .set_tim = iwl_mvm_set_tim,
-
- .channel_switch = iwl_mvm_channel_switch,
- .pre_channel_switch = iwl_mvm_pre_channel_switch,
- .post_channel_switch = iwl_mvm_post_channel_switch,
-
- .tdls_channel_switch = iwl_mvm_tdls_channel_switch,
- .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
- .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
-
- .event_callback = iwl_mvm_mac_event_callback,
-
- CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
-
-#ifdef CONFIG_PM_SLEEP
- /* look at d3.c */
- .suspend = iwl_mvm_suspend,
- .resume = iwl_mvm_resume,
- .set_wakeup = iwl_mvm_set_wakeup,
- .set_rekey_data = iwl_mvm_set_rekey_data,
-#if IS_ENABLED(CONFIG_IPV6)
- .ipv6_addr_change = iwl_mvm_ipv6_addr_change,
-#endif
- .set_default_unicast_key = iwl_mvm_set_default_unicast_key,
-#endif
- .get_survey = iwl_mvm_mac_get_survey,
- .sta_statistics = iwl_mvm_mac_sta_statistics,
-};
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
deleted file mode 100644
index 4bde2d027dcd..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ /dev/null
@@ -1,1535 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __IWL_MVM_H__
-#define __IWL_MVM_H__
-
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/leds.h>
-#include <linux/in6.h>
-
-#include "iwl-op-mode.h"
-#include "iwl-trans.h"
-#include "iwl-notif-wait.h"
-#include "iwl-eeprom-parse.h"
-#include "iwl-fw-file.h"
-#include "iwl-config.h"
-#include "sta.h"
-#include "fw-api.h"
-#include "constants.h"
-#include "tof.h"
-
-#define IWL_MVM_MAX_ADDRESSES 5
-/* RSSI offset for WkP */
-#define IWL_RSSI_OFFSET 50
-#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
-/* A TimeUnit is 1024 microsecond */
-#define MSEC_TO_TU(_msec) (_msec*1000/1024)
-
-/* For GO, this value represents the number of TUs before CSA "beacon
- * 0" TBTT when the CSA time-event needs to be scheduled to start. It
- * must be big enough to ensure that we switch in time.
- */
-#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40
-
-/* For client, this value represents the number of TUs before CSA
- * "beacon 1" TBTT, instead. This is because we don't know when the
- * GO/AP will be in the new channel, so we switch early enough.
- */
-#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 10
-
-/*
- * This value (in TUs) is used to fine tune the CSA NoA end time which should
- * be just before "beacon 0" TBTT.
- */
-#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
-
-/*
- * Number of beacons to transmit on a new channel until we unblock tx to
- * the stations, even if we didn't identify them on a new channel
- */
-#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
-
-extern const struct ieee80211_ops iwl_mvm_hw_ops;
-
-/**
- * struct iwl_mvm_mod_params - module parameters for iwlmvm
- * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
- * We will register to mac80211 to have testmode working. The NIC must not
- * be up'ed after the INIT fw asserted. This is useful to be able to use
- * proprietary tools over testmode to debug the INIT fw.
- * @tfd_q_hang_detect: enabled the detection of hung transmit queues
- * @power_scheme: one of enum iwl_power_scheme
- */
-struct iwl_mvm_mod_params {
- bool init_dbg;
- bool tfd_q_hang_detect;
- int power_scheme;
-};
-extern struct iwl_mvm_mod_params iwlmvm_mod_params;
-
-/**
- * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump
- *
- * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode
- * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the
- * transport's data.
- * @trans_len: length of the valid data in trans_ptr
- * @op_mode_len: length of the valid data in op_mode_ptr
- */
-struct iwl_mvm_dump_ptrs {
- struct iwl_trans_dump_data *trans_ptr;
- void *op_mode_ptr;
- u32 op_mode_len;
-};
-
-/**
- * struct iwl_mvm_dump_desc - describes the dump
- * @len: length of trig_desc->data
- * @trig_desc: the description of the dump
- */
-struct iwl_mvm_dump_desc {
- size_t len;
- /* must be last */
- struct iwl_fw_error_dump_trigger_desc trig_desc;
-};
-
-extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert;
-
-struct iwl_mvm_phy_ctxt {
- u16 id;
- u16 color;
- u32 ref;
-
- /*
- * TODO: This should probably be removed. Currently here only for rate
- * scaling algorithm
- */
- struct ieee80211_channel *channel;
-};
-
-struct iwl_mvm_time_event_data {
- struct ieee80211_vif *vif;
- struct list_head list;
- unsigned long end_jiffies;
- u32 duration;
- bool running;
- u32 uid;
-
- /*
- * The access to the 'id' field must be done when the
- * mvm->time_event_lock is held, as it value is used to indicate
- * if the te is in the time event list or not (when id == TE_MAX)
- */
- u32 id;
-};
-
- /* Power management */
-
-/**
- * enum iwl_power_scheme
- * @IWL_POWER_LEVEL_CAM - Continuously Active Mode
- * @IWL_POWER_LEVEL_BPS - Balanced Power Save (default)
- * @IWL_POWER_LEVEL_LP - Low Power
- */
-enum iwl_power_scheme {
- IWL_POWER_SCHEME_CAM = 1,
- IWL_POWER_SCHEME_BPS,
- IWL_POWER_SCHEME_LP
-};
-
-#define IWL_CONN_MAX_LISTEN_INTERVAL 10
-#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-enum iwl_dbgfs_pm_mask {
- MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
- MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1),
- MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2),
- MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3),
- MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4),
- MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
- MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
- MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
- MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9),
- MVM_DEBUGFS_PM_USE_PS_POLL = BIT(10),
-};
-
-struct iwl_dbgfs_pm {
- u16 keep_alive_seconds;
- u32 rx_data_timeout;
- u32 tx_data_timeout;
- bool skip_over_dtim;
- u8 skip_dtim_periods;
- bool lprx_ena;
- u32 lprx_rssi_threshold;
- bool snooze_ena;
- bool uapsd_misbehaving;
- bool use_ps_poll;
- int mask;
-};
-
-/* beacon filtering */
-
-enum iwl_dbgfs_bf_mask {
- MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0),
- MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1),
- MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2),
- MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3),
- MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4),
- MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5),
- MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6),
- MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7),
- MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8),
- MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9),
- MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10),
-};
-
-struct iwl_dbgfs_bf {
- u32 bf_energy_delta;
- u32 bf_roaming_energy_delta;
- u32 bf_roaming_state;
- u32 bf_temp_threshold;
- u32 bf_temp_fast_filter;
- u32 bf_temp_slow_filter;
- u32 bf_enable_beacon_filter;
- u32 bf_debug_flag;
- u32 bf_escape_timer;
- u32 ba_escape_timer;
- u32 ba_enable_beacon_abort;
- int mask;
-};
-#endif
-
-enum iwl_mvm_smps_type_request {
- IWL_MVM_SMPS_REQ_BT_COEX,
- IWL_MVM_SMPS_REQ_TT,
- IWL_MVM_SMPS_REQ_PROT,
- NUM_IWL_MVM_SMPS_REQ,
-};
-
-enum iwl_mvm_ref_type {
- IWL_MVM_REF_UCODE_DOWN,
- IWL_MVM_REF_SCAN,
- IWL_MVM_REF_ROC,
- IWL_MVM_REF_ROC_AUX,
- IWL_MVM_REF_P2P_CLIENT,
- IWL_MVM_REF_AP_IBSS,
- IWL_MVM_REF_USER,
- IWL_MVM_REF_TX,
- IWL_MVM_REF_TX_AGG,
- IWL_MVM_REF_ADD_IF,
- IWL_MVM_REF_START_AP,
- IWL_MVM_REF_BSS_CHANGED,
- IWL_MVM_REF_PREPARE_TX,
- IWL_MVM_REF_PROTECT_TDLS,
- IWL_MVM_REF_CHECK_CTKILL,
- IWL_MVM_REF_PRPH_READ,
- IWL_MVM_REF_PRPH_WRITE,
- IWL_MVM_REF_NMI,
- IWL_MVM_REF_TM_CMD,
- IWL_MVM_REF_EXIT_WORK,
- IWL_MVM_REF_PROTECT_CSA,
- IWL_MVM_REF_FW_DBG_COLLECT,
-
- /* update debugfs.c when changing this */
-
- IWL_MVM_REF_COUNT,
-};
-
-enum iwl_bt_force_ant_mode {
- BT_FORCE_ANT_DIS = 0,
- BT_FORCE_ANT_AUTO,
- BT_FORCE_ANT_BT,
- BT_FORCE_ANT_WIFI,
-
- BT_FORCE_ANT_MAX,
-};
-
-/**
-* struct iwl_mvm_vif_bf_data - beacon filtering related data
-* @bf_enabled: indicates if beacon filtering is enabled
-* @ba_enabled: indicated if beacon abort is enabled
-* @ave_beacon_signal: average beacon signal
-* @last_cqm_event: rssi of the last cqm event
-* @bt_coex_min_thold: minimum threshold for BT coex
-* @bt_coex_max_thold: maximum threshold for BT coex
-* @last_bt_coex_event: rssi of the last BT coex event
-*/
-struct iwl_mvm_vif_bf_data {
- bool bf_enabled;
- bool ba_enabled;
- int ave_beacon_signal;
- int last_cqm_event;
- int bt_coex_min_thold;
- int bt_coex_max_thold;
- int last_bt_coex_event;
-};
-
-/**
- * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
- * @id: between 0 and 3
- * @color: to solve races upon MAC addition and removal
- * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA
- * @bssid: BSSID for this (client) interface
- * @associated: indicates that we're currently associated, used only for
- * managing the firmware state in iwl_mvm_bss_info_changed_station()
- * @ap_assoc_sta_count: count of stations associated to us - valid only
- * if VIF type is AP
- * @uploaded: indicates the MAC context has been added to the device
- * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
- * should get quota etc.
- * @pm_enabled - Indicate if MAC power management is allowed
- * @monitor_active: indicates that monitor context is configured, and that the
- * interface should get quota etc.
- * @low_latency: indicates that this interface is in low-latency mode
- * (VMACLowLatencyMode)
- * @ps_disabled: indicates that this interface requires PS to be disabled
- * @queue_params: QoS params for this MAC
- * @bcast_sta: station used for broadcast packets. Used by the following
- * vifs: P2P_DEVICE, GO and AP.
- * @beacon_skb: the skb used to hold the AP/GO beacon template
- * @smps_requests: the SMPS requests of different parts of the driver,
- * combined on update to yield the overall request to mac80211.
- * @beacon_stats: beacon statistics, containing the # of received beacons,
- * # of received beacons accumulated over FW restart, and the current
- * average signal of beacons retrieved from the firmware
- * @csa_failed: CSA failed to schedule time event, report an error later
- * @features: hw features active for this vif
- */
-struct iwl_mvm_vif {
- struct iwl_mvm *mvm;
- u16 id;
- u16 color;
- u8 ap_sta_id;
-
- u8 bssid[ETH_ALEN];
- bool associated;
- u8 ap_assoc_sta_count;
-
- bool uploaded;
- bool ap_ibss_active;
- bool pm_enabled;
- bool monitor_active;
- bool low_latency;
- bool ps_disabled;
- struct iwl_mvm_vif_bf_data bf_data;
-
- struct {
- u32 num_beacons, accu_num_beacons;
- u8 avg_signal;
- } beacon_stats;
-
- u32 ap_beacon_time;
-
- enum iwl_tsf_id tsf_id;
-
- /*
- * QoS data from mac80211, need to store this here
- * as mac80211 has a separate callback but we need
- * to have the data for the MAC context
- */
- struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
- struct iwl_mvm_time_event_data time_event_data;
- struct iwl_mvm_time_event_data hs_time_event_data;
-
- struct iwl_mvm_int_sta bcast_sta;
-
- /*
- * Assigned while mac80211 has the interface in a channel context,
- * or, for P2P Device, while it exists.
- */
- struct iwl_mvm_phy_ctxt *phy_ctxt;
-
-#ifdef CONFIG_PM_SLEEP
- /* WoWLAN GTK rekey data */
- struct {
- u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
- __le64 replay_ctr;
- bool valid;
- } rekey_data;
-
- int tx_key_idx;
-
- bool seqno_valid;
- u16 seqno;
-#endif
-
-#if IS_ENABLED(CONFIG_IPV6)
- /* IPv6 addresses for WoWLAN */
- struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
- int num_target_ipv6_addrs;
-#endif
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- struct dentry *dbgfs_dir;
- struct dentry *dbgfs_slink;
- struct iwl_dbgfs_pm dbgfs_pm;
- struct iwl_dbgfs_bf dbgfs_bf;
- struct iwl_mac_power_cmd mac_pwr_cmd;
-#endif
-
- enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
-
- /* FW identified misbehaving AP */
- u8 uapsd_misbehaving_bssid[ETH_ALEN];
-
- /* Indicates that CSA countdown may be started */
- bool csa_countdown;
- bool csa_failed;
-
- /* TCP Checksum Offload */
- netdev_features_t features;
-};
-
-static inline struct iwl_mvm_vif *
-iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
-{
- return (void *)vif->drv_priv;
-}
-
-extern const u8 tid_to_mac80211_ac[];
-
-#define IWL_MVM_SCAN_STOPPING_SHIFT 8
-
-enum iwl_scan_status {
- IWL_MVM_SCAN_REGULAR = BIT(0),
- IWL_MVM_SCAN_SCHED = BIT(1),
- IWL_MVM_SCAN_NETDETECT = BIT(2),
-
- IWL_MVM_SCAN_STOPPING_REGULAR = BIT(8),
- IWL_MVM_SCAN_STOPPING_SCHED = BIT(9),
- IWL_MVM_SCAN_STOPPING_NETDETECT = BIT(10),
-
- IWL_MVM_SCAN_REGULAR_MASK = IWL_MVM_SCAN_REGULAR |
- IWL_MVM_SCAN_STOPPING_REGULAR,
- IWL_MVM_SCAN_SCHED_MASK = IWL_MVM_SCAN_SCHED |
- IWL_MVM_SCAN_STOPPING_SCHED,
- IWL_MVM_SCAN_NETDETECT_MASK = IWL_MVM_SCAN_NETDETECT |
- IWL_MVM_SCAN_STOPPING_NETDETECT,
-
- IWL_MVM_SCAN_STOPPING_MASK = 0xff << IWL_MVM_SCAN_STOPPING_SHIFT,
- IWL_MVM_SCAN_MASK = 0xff,
-};
-
-/**
- * struct iwl_nvm_section - describes an NVM section in memory.
- *
- * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
- * and saved for later use by the driver. Not all NVM sections are saved
- * this way, only the needed ones.
- */
-struct iwl_nvm_section {
- u16 length;
- const u8 *data;
-};
-
-/**
- * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure
- * @ct_kill_exit: worker to exit thermal kill
- * @dynamic_smps: Is thermal throttling enabled dynamic_smps?
- * @tx_backoff: The current thremal throttling tx backoff in uSec.
- * @min_backoff: The minimal tx backoff due to power restrictions
- * @params: Parameters to configure the thermal throttling algorithm.
- * @throttle: Is thermal throttling is active?
- */
-struct iwl_mvm_tt_mgmt {
- struct delayed_work ct_kill_exit;
- bool dynamic_smps;
- u32 tx_backoff;
- u32 min_backoff;
- struct iwl_tt_params params;
- bool throttle;
-};
-
-#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8
-
-struct iwl_mvm_frame_stats {
- u32 legacy_frames;
- u32 ht_frames;
- u32 vht_frames;
- u32 bw_20_frames;
- u32 bw_40_frames;
- u32 bw_80_frames;
- u32 bw_160_frames;
- u32 sgi_frames;
- u32 ngi_frames;
- u32 siso_frames;
- u32 mimo2_frames;
- u32 agg_frames;
- u32 ampdu_count;
- u32 success_frames;
- u32 fail_frames;
- u32 last_rates[IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES];
- int last_frame_idx;
-};
-
-enum {
- D0I3_DEFER_WAKEUP,
- D0I3_PENDING_WAKEUP,
-};
-
-#define IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE 0xff
-#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100
-#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200
-
-enum iwl_mvm_tdls_cs_state {
- IWL_MVM_TDLS_SW_IDLE = 0,
- IWL_MVM_TDLS_SW_REQ_SENT,
- IWL_MVM_TDLS_SW_RESP_RCVD,
- IWL_MVM_TDLS_SW_REQ_RCVD,
- IWL_MVM_TDLS_SW_ACTIVE,
-};
-
-struct iwl_mvm_shared_mem_cfg {
- u32 shared_mem_addr;
- u32 shared_mem_size;
- u32 sample_buff_addr;
- u32 sample_buff_size;
- u32 txfifo_addr;
- u32 txfifo_size[TX_FIFO_MAX_NUM];
- u32 rxfifo_size[RX_FIFO_MAX_NUM];
- u32 page_buff_addr;
- u32 page_buff_size;
-};
-
-struct iwl_mvm {
- /* for logger access */
- struct device *dev;
-
- struct iwl_trans *trans;
- const struct iwl_fw *fw;
- const struct iwl_cfg *cfg;
- struct iwl_phy_db *phy_db;
- struct ieee80211_hw *hw;
-
- /* for protecting access to iwl_mvm */
- struct mutex mutex;
- struct list_head async_handlers_list;
- spinlock_t async_handlers_lock;
- struct work_struct async_handlers_wk;
-
- struct work_struct roc_done_wk;
-
- unsigned long status;
-
- /*
- * for beacon filtering -
- * currently only one interface can be supported
- */
- struct iwl_mvm_vif *bf_allowed_vif;
-
- enum iwl_ucode_type cur_ucode;
- bool ucode_loaded;
- bool calibrating;
- u32 error_event_table;
- u32 log_event_table;
- u32 umac_error_event_table;
- bool support_umac_log;
- struct iwl_sf_region sf_space;
-
- u32 ampdu_ref;
-
- struct iwl_notif_wait_data notif_wait;
-
- struct mvm_statistics_rx rx_stats;
-
- struct {
- u64 rx_time;
- u64 tx_time;
- u64 on_time_rf;
- u64 on_time_scan;
- } radio_stats, accu_radio_stats;
-
- struct {
- /* Map to HW queue */
- u32 hw_queue_to_mac80211;
- u8 hw_queue_refcount;
- bool setup_reserved;
- u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */
- } queue_info[IWL_MAX_HW_QUEUES];
- spinlock_t queue_info_lock; /* For syncing queue mgmt operations */
- atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
-
- const char *nvm_file_name;
- struct iwl_nvm_data *nvm_data;
- /* NVM sections */
- struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
-
- /* Paging section */
- struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS];
- u16 num_of_paging_blk;
- u16 num_of_pages_in_last_blk;
-
- /* EEPROM MAC addresses */
- struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
-
- /* data related to data path */
- struct iwl_rx_phy_info last_phy_info;
- struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT];
- struct work_struct sta_drained_wk;
- unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
- atomic_t pending_frames[IWL_MVM_STATION_COUNT];
- u32 tfd_drained[IWL_MVM_STATION_COUNT];
- u8 rx_ba_sessions;
-
- /* configured by mac80211 */
- u32 rts_threshold;
-
- /* Scan status, cmd (pre-allocated) and auxiliary station */
- unsigned int scan_status;
- void *scan_cmd;
- struct iwl_mcast_filter_cmd *mcast_filter_cmd;
-
- /* max number of simultaneous scans the FW supports */
- unsigned int max_scans;
-
- /* UMAC scan tracking */
- u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS];
-
- /* rx chain antennas set through debugfs for the scan command */
- u8 scan_rx_ant;
-
-#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
- /* broadcast filters to configure for each associated station */
- const struct iwl_fw_bcast_filter *bcast_filters;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- struct {
- bool override;
- struct iwl_bcast_filter_cmd cmd;
- } dbgfs_bcast_filtering;
-#endif
-#endif
-
- /* Internal station */
- struct iwl_mvm_int_sta aux_sta;
-
- bool last_ebs_successful;
-
- u8 scan_last_antenna_idx; /* to toggle TX between antennas */
- u8 mgmt_last_antenna_idx;
-
- /* last smart fifo state that was successfully sent to firmware */
- enum iwl_sf_state sf_state;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- struct dentry *debugfs_dir;
- u32 dbgfs_sram_offset, dbgfs_sram_len;
- u32 dbgfs_prph_reg_addr;
- bool disable_power_off;
- bool disable_power_off_d3;
-
- bool scan_iter_notif_enabled;
-
- struct debugfs_blob_wrapper nvm_hw_blob;
- struct debugfs_blob_wrapper nvm_sw_blob;
- struct debugfs_blob_wrapper nvm_calib_blob;
- struct debugfs_blob_wrapper nvm_prod_blob;
- struct debugfs_blob_wrapper nvm_phy_sku_blob;
-
- struct iwl_mvm_frame_stats drv_rx_stats;
- spinlock_t drv_stats_lock;
- u16 dbgfs_rx_phyinfo;
-#endif
-
- struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
-
- struct list_head time_event_list;
- spinlock_t time_event_lock;
-
- /*
- * A bitmap indicating the index of the key in use. The firmware
- * can hold 16 keys at most. Reflect this fact.
- */
- unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
- u8 fw_key_deleted[STA_KEY_MAX_NUM];
-
- /* references taken by the driver and spinlock protecting them */
- spinlock_t refs_lock;
- u8 refs[IWL_MVM_REF_COUNT];
-
- u8 vif_count;
-
- /* -1 for always, 0 for never, >0 for that many times */
- s8 restart_fw;
- u8 fw_dbg_conf;
- struct delayed_work fw_dump_wk;
- struct iwl_mvm_dump_desc *fw_dump_desc;
- struct iwl_fw_dbg_trigger_tlv *fw_dump_trig;
-
-#ifdef CONFIG_IWLWIFI_LEDS
- struct led_classdev led;
-#endif
-
- struct ieee80211_vif *p2p_device_vif;
-
-#ifdef CONFIG_PM_SLEEP
- struct wiphy_wowlan_support wowlan;
- int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
-
- /* sched scan settings for net detect */
- struct cfg80211_sched_scan_request *nd_config;
- struct ieee80211_scan_ies nd_ies;
- struct cfg80211_match_set *nd_match_sets;
- int n_nd_match_sets;
- struct ieee80211_channel **nd_channels;
- int n_nd_channels;
- bool net_detect;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- bool d3_wake_sysassert;
- bool d3_test_active;
- bool store_d3_resume_sram;
- void *d3_resume_sram;
- u32 d3_test_pme_ptr;
- struct ieee80211_vif *keep_vif;
- u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */
-#endif
-#endif
-
- /* d0i3 */
- u8 d0i3_ap_sta_id;
- bool d0i3_offloading;
- struct work_struct d0i3_exit_work;
- struct sk_buff_head d0i3_tx;
- /* protect d0i3_suspend_flags */
- struct mutex d0i3_suspend_mutex;
- unsigned long d0i3_suspend_flags;
- /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */
- spinlock_t d0i3_tx_lock;
- wait_queue_head_t d0i3_exit_waitq;
-
- /* BT-Coex */
- u8 bt_ack_kill_msk[NUM_PHY_CTX];
- u8 bt_cts_kill_msk[NUM_PHY_CTX];
-
- struct iwl_bt_coex_profile_notif_old last_bt_notif_old;
- struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old;
- struct iwl_bt_coex_profile_notif last_bt_notif;
- struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
-
- u32 last_ant_isol;
- u8 last_corun_lut;
- u8 bt_tx_prio;
- enum iwl_bt_force_ant_mode bt_force_ant_mode;
-
- /* Aux ROC */
- struct list_head aux_roc_te_list;
-
- /* Thermal Throttling and CTkill */
- struct iwl_mvm_tt_mgmt thermal_throttle;
- s32 temperature; /* Celsius */
- /*
- * Debug option to set the NIC temperature. This option makes the
- * driver think this is the actual NIC temperature, and ignore the
- * real temperature that is received from the fw
- */
- bool temperature_test; /* Debug test temperature is enabled */
-
- struct iwl_time_quota_cmd last_quota_cmd;
-
-#ifdef CONFIG_NL80211_TESTMODE
- u32 noa_duration;
- struct ieee80211_vif *noa_vif;
-#endif
-
- /* Tx queues */
- u8 aux_queue;
- u8 first_agg_queue;
- u8 last_agg_queue;
-
- /* Indicate if device power save is allowed */
- u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */
-
- struct ieee80211_vif __rcu *csa_vif;
- struct ieee80211_vif __rcu *csa_tx_blocked_vif;
- u8 csa_tx_block_bcn_timeout;
-
- /* system time of last beacon (for AP/GO interface) */
- u32 ap_last_beacon_gp2;
-
- bool lar_regdom_set;
- enum iwl_mcc_source mcc_src;
-
- u8 low_latency_agg_frame_limit;
-
- /* TDLS channel switch data */
- struct {
- struct delayed_work dwork;
- enum iwl_mvm_tdls_cs_state state;
-
- /*
- * Current cs sta - might be different from periodic cs peer
- * station. Value is meaningless when the cs-state is idle.
- */
- u8 cur_sta_id;
-
- /* TDLS periodic channel-switch peer */
- struct {
- u8 sta_id;
- u8 op_class;
- bool initiator; /* are we the link initiator */
- struct cfg80211_chan_def chandef;
- struct sk_buff *skb; /* ch sw template */
- u32 ch_sw_tm_ie;
-
- /* timestamp of last ch-sw request sent (GP2 time) */
- u32 sent_timestamp;
- } peer;
- } tdls_cs;
-
- struct iwl_mvm_shared_mem_cfg shared_mem_cfg;
-
- u32 ciphers[6];
- struct iwl_mvm_tof_data tof_data;
-};
-
-/* Extract MVM priv from op_mode and _hw */
-#define IWL_OP_MODE_GET_MVM(_iwl_op_mode) \
- ((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific)
-
-#define IWL_MAC80211_GET_MVM(_hw) \
- IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
-
-enum iwl_mvm_status {
- IWL_MVM_STATUS_HW_RFKILL,
- IWL_MVM_STATUS_HW_CTKILL,
- IWL_MVM_STATUS_ROC_RUNNING,
- IWL_MVM_STATUS_IN_HW_RESTART,
- IWL_MVM_STATUS_IN_D0I3,
- IWL_MVM_STATUS_ROC_AUX_RUNNING,
- IWL_MVM_STATUS_D3_RECONFIG,
- IWL_MVM_STATUS_DUMPING_FW_LOG,
-};
-
-static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
-{
- return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) ||
- test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
-}
-
-static inline bool iwl_mvm_is_radio_hw_killed(struct iwl_mvm *mvm)
-{
- return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
-}
-
-/* Must be called with rcu_read_lock() held and it can only be
- * released when mvmsta is not needed anymore.
- */
-static inline struct iwl_mvm_sta *
-iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id)
-{
- struct ieee80211_sta *sta;
-
- if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
- return NULL;
-
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
-
- /* This can happen if the station has been removed right now */
- if (IS_ERR_OR_NULL(sta))
- return NULL;
-
- return iwl_mvm_sta_from_mac80211(sta);
-}
-
-static inline struct iwl_mvm_sta *
-iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
-{
- struct ieee80211_sta *sta;
-
- if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
- return NULL;
-
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
-
- /* This can happen if the station has been removed right now */
- if (IS_ERR_OR_NULL(sta))
- return NULL;
-
- return iwl_mvm_sta_from_mac80211(sta);
-}
-
-static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
-{
- return mvm->trans->cfg->d0i3 &&
- mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF &&
- !iwlwifi_mod_params.d0i3_disable &&
- fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
-}
-
-static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
-{
- return fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_DQA_SUPPORT);
-}
-
-static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
-{
- bool nvm_lar = mvm->nvm_data->lar_enabled;
- bool tlv_lar = fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
-
- if (iwlwifi_mod_params.lar_disable)
- return false;
-
- /*
- * Enable LAR only if it is supported by the FW (TLV) &&
- * enabled in the NVM
- */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000)
- return nvm_lar && tlv_lar;
- else
- return tlv_lar;
-}
-
-static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm)
-{
- return fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_WIFI_MCC_UPDATE) ||
- fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC);
-}
-
-static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm)
-{
- return fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) &&
- IWL_MVM_BT_COEX_CORUNNING;
-}
-
-static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm)
-{
- return fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_BT_COEX_RRC) &&
- IWL_MVM_BT_COEX_RRC;
-}
-
-static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm)
-{
- return fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_CSUM_SUPPORT);
-}
-
-static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm)
-{
- /* firmware flag isn't defined yet */
- return false;
-}
-
-extern const u8 iwl_mvm_ac_to_tx_fifo[];
-
-struct iwl_rate_info {
- u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
- u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
- u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
- u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */
- u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
-};
-
-void __iwl_mvm_mac_stop(struct iwl_mvm *mvm);
-int __iwl_mvm_mac_start(struct iwl_mvm *mvm);
-
-/******************
- * MVM Methods
- ******************/
-/* uCode */
-int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm);
-
-/* Utils */
-int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
- enum ieee80211_band band);
-void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
- enum ieee80211_band band,
- struct ieee80211_tx_rate *r);
-u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
-void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
-u8 first_antenna(u8 mask);
-u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
-
-/* Tx / Host Commands */
-int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm,
- struct iwl_host_cmd *cmd);
-int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id,
- u32 flags, u16 len, const void *data);
-int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm,
- struct iwl_host_cmd *cmd,
- u32 *status);
-int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id,
- u16 len, const void *data,
- u32 *status);
-int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct ieee80211_sta *sta);
-int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
-void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info, u8 sta_id);
-void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta, __le16 fc);
-#ifdef CONFIG_IWLWIFI_DEBUG
-const char *iwl_mvm_get_tx_fail_reason(u32 status);
-#else
-static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; }
-#endif
-int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags);
-void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
-
-static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info,
- struct iwl_tx_cmd *tx_cmd)
-{
- struct ieee80211_key_conf *keyconf = info->control.hw_key;
-
- tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
- memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG);
-}
-
-static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm)
-{
- flush_work(&mvm->async_handlers_wk);
-}
-
-/* Statistics */
-void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt);
-void iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear);
-void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm);
-
-/* NVM */
-int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
-int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
-
-static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm)
-{
- return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ?
- mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant :
- mvm->fw->valid_tx_ant;
-}
-
-static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm)
-{
- return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ?
- mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant :
- mvm->fw->valid_rx_ant;
-}
-
-static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm)
-{
- u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
- FW_PHY_CFG_RX_CHAIN);
- u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm);
- u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
-
- phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS |
- valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS;
-
- return mvm->fw->phy_config & phy_config;
-}
-
-int iwl_mvm_up(struct iwl_mvm *mvm);
-int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
-
-int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
-bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
- struct iwl_bcast_filter_cmd *cmd);
-
-/*
- * FW notifications / CMD responses handlers
- * Convention: iwl_mvm_rx_<NAME OF THE CMD>
- */
-void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-
-/* MVM PHY */
-int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
- struct cfg80211_chan_def *chandef,
- u8 chains_static, u8 chains_dynamic);
-int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
- struct cfg80211_chan_def *chandef,
- u8 chains_static, u8 chains_dynamic);
-void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
- struct iwl_mvm_phy_ctxt *ctxt);
-void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
- struct iwl_mvm_phy_ctxt *ctxt);
-int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
-u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef);
-u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef);
-
-/* MAC (virtual interface) programming */
-int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool force_assoc_off, const u8 *bssid_override);
-int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif);
-int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
- struct ieee80211_vif *exclude_vif);
-/* Bindings */
-int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-
-/* Quota management */
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload,
- struct ieee80211_vif *disabled_vif);
-
-/* Scanning */
-int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req,
- struct ieee80211_scan_ies *ies);
-int iwl_mvm_scan_size(struct iwl_mvm *mvm);
-int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify);
-int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm);
-void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
-
-/* Scheduled scan */
-void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct cfg80211_sched_scan_request *req,
- struct ieee80211_scan_ies *ies,
- int type);
-void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-
-/* UMAC scan */
-int iwl_mvm_config_scan(struct iwl_mvm *mvm);
-void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-
-/* MVM debugfs */
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
-void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-#else
-static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm,
- struct dentry *dbgfs_dir)
-{
- return 0;
-}
-static inline void
-iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
-}
-static inline void
-iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
-}
-#endif /* CONFIG_IWLWIFI_DEBUGFS */
-
-/* rate scaling */
-int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
-void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg);
-int rs_pretty_print_rate(char *buf, const u32 rate);
-void rs_update_last_rssi(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_rx_status *rx_status);
-
-/* power management */
-int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
-int iwl_mvm_power_update_mac(struct iwl_mvm *mvm);
-int iwl_mvm_power_update_ps(struct iwl_mvm *mvm);
-int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- char *buf, int bufsz);
-
-void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-
-#ifdef CONFIG_IWLWIFI_LEDS
-int iwl_mvm_leds_init(struct iwl_mvm *mvm);
-void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
-#else
-static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm)
-{
- return 0;
-}
-static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
-{
-}
-#endif
-
-/* D3 (WoWLAN, NetDetect) */
-int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
-int iwl_mvm_resume(struct ieee80211_hw *hw);
-void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled);
-void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_gtk_rekey_data *data);
-void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct inet6_dev *idev);
-void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, int idx);
-extern const struct file_operations iwl_dbgfs_d3_test_ops;
-#ifdef CONFIG_PM_SLEEP
-void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-#else
-static inline void
-iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
-}
-#endif
-void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
- struct iwl_wowlan_config_cmd *cmd);
-int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool disable_offloading,
- u32 cmd_flags);
-
-/* D0i3 */
-void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
-void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
-int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
-bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
-void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
-int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode);
-int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode);
-int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
-
-/* BT Coex */
-int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
-void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- enum ieee80211_rssi_event_data);
-void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
-u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta);
-bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta);
-bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant);
-bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
-bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
- enum ieee80211_band band);
-u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
- struct ieee80211_tx_info *info, u8 ac);
-
-bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
-void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
-int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
-void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- enum ieee80211_rssi_event_data);
-u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta);
-bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta);
-bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
- enum ieee80211_band band);
-void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-
-/* beacon filtering */
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void
-iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
- struct iwl_beacon_filter_cmd *cmd);
-#else
-static inline void
-iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
- struct iwl_beacon_filter_cmd *cmd)
-{}
-#endif
-int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool enable, u32 flags);
-int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 flags);
-int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 flags);
-/* SMPS */
-void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- enum iwl_mvm_smps_type_request req_type,
- enum ieee80211_smps_mode smps_request);
-bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm);
-
-/* Low latency */
-int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool value);
-/* get SystemLowLatencyMode - only needed for beacon threshold? */
-bool iwl_mvm_low_latency(struct iwl_mvm *mvm);
-/* get VMACLowLatencyMode */
-static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
-{
- /*
- * should this consider associated/active/... state?
- *
- * Normally low-latency should only be active on interfaces
- * that are active, but at least with debugfs it can also be
- * enabled on interfaces that aren't active. However, when
- * interface aren't active then they aren't added into the
- * binding, so this has no real impact. For now, just return
- * the current desired low-latency state.
- */
-
- return mvmvif->low_latency;
-}
-
-/* hw scheduler queue config */
-void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
- u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg,
- unsigned int wdg_timeout);
-/*
- * Disable a TXQ.
- * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored.
- */
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
- u8 tid, u8 flags);
-int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq);
-
-static inline
-void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
- u8 fifo, u16 ssn, unsigned int wdg_timeout)
-{
- struct iwl_trans_txq_scd_cfg cfg = {
- .fifo = fifo,
- .tid = IWL_MAX_TID_COUNT,
- .aggregate = false,
- .frame_limit = IWL_FRAME_LIMIT,
- };
-
- iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
-}
-
-static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue,
- int mac80211_queue, int fifo,
- int sta_id, int tid, int frame_limit,
- u16 ssn, unsigned int wdg_timeout)
-{
- struct iwl_trans_txq_scd_cfg cfg = {
- .fifo = fifo,
- .sta_id = sta_id,
- .tid = tid,
- .frame_limit = frame_limit,
- .aggregate = true,
- };
-
- iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
-}
-
-/* Thermal management and CT-kill */
-void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
-void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
-void iwl_mvm_temp_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
-void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
-void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
-int iwl_mvm_get_temp(struct iwl_mvm *mvm);
-
-/* Location Aware Regulatory */
-struct iwl_mcc_update_resp *
-iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
- enum iwl_mcc_source src_id);
-int iwl_mvm_init_mcc(struct iwl_mvm *mvm);
-void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
- const char *alpha2,
- enum iwl_mcc_source src_id,
- bool *changed);
-struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
- bool *changed);
-int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm);
-void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm);
-
-/* smart fifo */
-int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool added_vif);
-
-/* TDLS */
-
-/*
- * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present.
- * This TID is marked as used vs the AP and all connected TDLS peers.
- */
-#define IWL_MVM_TDLS_FW_TID 4
-
-int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm);
-void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool sta_added);
-void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif);
-int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u8 oper_class,
- struct cfg80211_chan_def *chandef,
- struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
-void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_tdls_ch_sw_params *params);
-void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_tdls_ch_switch_work(struct work_struct *work);
-
-struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
-
-void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
-
-int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
-int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
- const char *str, size_t len,
- struct iwl_fw_dbg_trigger_tlv *trigger);
-int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
- struct iwl_mvm_dump_desc *desc,
- struct iwl_fw_dbg_trigger_tlv *trigger);
-void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
-int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
- struct iwl_fw_dbg_trigger_tlv *trigger,
- const char *fmt, ...) __printf(3, 4);
-unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool tdls, bool cmd_q);
-void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- const char *errmsg);
-static inline bool
-iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
- struct ieee80211_vif *vif)
-{
- u32 trig_vif = le32_to_cpu(trig->vif_type);
-
- return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif;
-}
-
-static inline bool
-iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm,
- struct iwl_fw_dbg_trigger_tlv *trig)
-{
- return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
- (mvm->fw_dbg_conf == FW_DBG_INVALID ||
- (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids))));
-}
-
-static inline bool
-iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_fw_dbg_trigger_tlv *trig)
-{
- if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif))
- return false;
-
- return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig);
-}
-
-static inline void
-iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- enum iwl_fw_dbg_trigger trig)
-{
- struct iwl_fw_dbg_trigger_tlv *trigger;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig))
- return;
-
- trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig);
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
- return;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
-}
-
-#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
deleted file mode 100644
index 2ee0f6fe56a1..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ /dev/null
@@ -1,864 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/firmware.h>
-#include <linux/rtnetlink.h>
-#include <linux/pci.h>
-#include <linux/acpi.h>
-#include "iwl-trans.h"
-#include "iwl-csr.h"
-#include "mvm.h"
-#include "iwl-eeprom-parse.h"
-#include "iwl-eeprom-read.h"
-#include "iwl-nvm-parse.h"
-#include "iwl-prph.h"
-
-/* Default NVM size to read */
-#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
-#define IWL_MAX_NVM_SECTION_SIZE 0x1b58
-#define IWL_MAX_NVM_8000_SECTION_SIZE 0x1ffc
-
-#define NVM_WRITE_OPCODE 1
-#define NVM_READ_OPCODE 0
-
-/* load nvm chunk response */
-enum {
- READ_NVM_CHUNK_SUCCEED = 0,
- READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1
-};
-
-/*
- * prepare the NVM host command w/ the pointers to the nvm buffer
- * and send it to fw
- */
-static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
- u16 offset, u16 length, const u8 *data)
-{
- struct iwl_nvm_access_cmd nvm_access_cmd = {
- .offset = cpu_to_le16(offset),
- .length = cpu_to_le16(length),
- .type = cpu_to_le16(section),
- .op_code = NVM_WRITE_OPCODE,
- };
- struct iwl_host_cmd cmd = {
- .id = NVM_ACCESS_CMD,
- .len = { sizeof(struct iwl_nvm_access_cmd), length },
- .flags = CMD_SEND_IN_RFKILL,
- .data = { &nvm_access_cmd, data },
- /* data may come from vmalloc, so use _DUP */
- .dataflags = { 0, IWL_HCMD_DFL_DUP },
- };
-
- return iwl_mvm_send_cmd(mvm, &cmd);
-}
-
-static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
- u16 offset, u16 length, u8 *data)
-{
- struct iwl_nvm_access_cmd nvm_access_cmd = {
- .offset = cpu_to_le16(offset),
- .length = cpu_to_le16(length),
- .type = cpu_to_le16(section),
- .op_code = NVM_READ_OPCODE,
- };
- struct iwl_nvm_access_resp *nvm_resp;
- struct iwl_rx_packet *pkt;
- struct iwl_host_cmd cmd = {
- .id = NVM_ACCESS_CMD,
- .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
- .data = { &nvm_access_cmd, },
- };
- int ret, bytes_read, offset_read;
- u8 *resp_data;
-
- cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (ret)
- return ret;
-
- pkt = cmd.resp_pkt;
-
- /* Extract NVM response */
- nvm_resp = (void *)pkt->data;
- ret = le16_to_cpu(nvm_resp->status);
- bytes_read = le16_to_cpu(nvm_resp->length);
- offset_read = le16_to_cpu(nvm_resp->offset);
- resp_data = nvm_resp->data;
- if (ret) {
- if ((offset != 0) &&
- (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) {
- /*
- * meaning of NOT_VALID_ADDRESS:
- * driver try to read chunk from address that is
- * multiple of 2K and got an error since addr is empty.
- * meaning of (offset != 0): driver already
- * read valid data from another chunk so this case
- * is not an error.
- */
- IWL_DEBUG_EEPROM(mvm->trans->dev,
- "NVM access command failed on offset 0x%x since that section size is multiple 2K\n",
- offset);
- ret = 0;
- } else {
- IWL_DEBUG_EEPROM(mvm->trans->dev,
- "NVM access command failed with status %d (device: %s)\n",
- ret, mvm->cfg->name);
- ret = -EIO;
- }
- goto exit;
- }
-
- if (offset_read != offset) {
- IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n",
- offset_read);
- ret = -EINVAL;
- goto exit;
- }
-
- /* Write data to NVM */
- memcpy(data + offset, resp_data, bytes_read);
- ret = bytes_read;
-
-exit:
- iwl_free_resp(&cmd);
- return ret;
-}
-
-static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
- const u8 *data, u16 length)
-{
- int offset = 0;
-
- /* copy data in chunks of 2k (and remainder if any) */
-
- while (offset < length) {
- int chunk_size, ret;
-
- chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE,
- length - offset);
-
- ret = iwl_nvm_write_chunk(mvm, section, offset,
- chunk_size, data + offset);
- if (ret < 0)
- return ret;
-
- offset += chunk_size;
- }
-
- return 0;
-}
-
-/*
- * Reads an NVM section completely.
- * NICs prior to 7000 family doesn't have a real NVM, but just read
- * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
- * by uCode, we need to manually check in this case that we don't
- * overflow and try to read more than the EEPROM size.
- * For 7000 family NICs, we supply the maximal size we can read, and
- * the uCode fills the response with as much data as we can,
- * without overflowing, so no check is needed.
- */
-static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
- u8 *data, u32 size_read)
-{
- u16 length, offset = 0;
- int ret;
-
- /* Set nvm section read length */
- length = IWL_NVM_DEFAULT_CHUNK_SIZE;
-
- ret = length;
-
- /* Read the NVM until exhausted (reading less than requested) */
- while (ret == length) {
- /* Check no memory assumptions fail and cause an overflow */
- if ((size_read + offset + length) >
- mvm->cfg->base_params->eeprom_size) {
- IWL_ERR(mvm, "EEPROM size is too small for NVM\n");
- return -ENOBUFS;
- }
-
- ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
- if (ret < 0) {
- IWL_DEBUG_EEPROM(mvm->trans->dev,
- "Cannot read NVM from section %d offset %d, length %d\n",
- section, offset, length);
- return ret;
- }
- offset += ret;
- }
-
- IWL_DEBUG_EEPROM(mvm->trans->dev,
- "NVM section %d read completed\n", section);
- return offset;
-}
-
-static struct iwl_nvm_data *
-iwl_parse_nvm_sections(struct iwl_mvm *mvm)
-{
- struct iwl_nvm_section *sections = mvm->nvm_sections;
- const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
- bool lar_enabled;
- u32 mac_addr0, mac_addr1;
-
- /* Checking for required sections */
- if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
- !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
- IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n");
- return NULL;
- }
- } else {
- /* SW and REGULATORY sections are mandatory */
- if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
- !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
- IWL_ERR(mvm,
- "Can't parse empty family 8000 OTP/NVM sections\n");
- return NULL;
- }
- /* MAC_OVERRIDE or at least HW section must exist */
- if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
- !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
- IWL_ERR(mvm,
- "Can't parse mac_address, empty sections\n");
- return NULL;
- }
-
- /* PHY_SKU section is mandatory in B0 */
- if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
- IWL_ERR(mvm,
- "Can't parse phy_sku in B0, empty sections\n");
- return NULL;
- }
- }
-
- if (WARN_ON(!mvm->cfg))
- return NULL;
-
- /* read the mac address from WFMP registers */
- mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0);
- mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1);
-
- hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
- sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
- calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
- regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data;
- mac_override =
- (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data;
- phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data;
-
- lar_enabled = !iwlwifi_mod_params.lar_disable &&
- fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
-
- return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
- regulatory, mac_override, phy_sku,
- mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
- lar_enabled, mac_addr0, mac_addr1,
- mvm->trans->hw_id);
-}
-
-#define MAX_NVM_FILE_LEN 16384
-
-/*
- * Reads external NVM from a file into mvm->nvm_sections
- *
- * HOW TO CREATE THE NVM FILE FORMAT:
- * ------------------------------
- * 1. create hex file, format:
- * 3800 -> header
- * 0000 -> header
- * 5a40 -> data
- *
- * rev - 6 bit (word1)
- * len - 10 bit (word1)
- * id - 4 bit (word2)
- * rsv - 12 bit (word2)
- *
- * 2. flip 8bits with 8 bits per line to get the right NVM file format
- *
- * 3. create binary file from the hex file
- *
- * 4. save as "iNVM_xxx.bin" under /lib/firmware
- */
-static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
-{
- int ret, section_size;
- u16 section_id;
- const struct firmware *fw_entry;
- const struct {
- __le16 word1;
- __le16 word2;
- u8 data[];
- } *file_sec;
- const u8 *eof, *temp;
- int max_section_size;
- const __le32 *dword_buff;
-
-#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
-#define NVM_WORD2_ID(x) (x >> 12)
-#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8))
-#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4)
-#define NVM_HEADER_0 (0x2A504C54)
-#define NVM_HEADER_1 (0x4E564D2A)
-#define NVM_HEADER_SIZE (4 * sizeof(u32))
-
- IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
-
- /* Maximal size depends on HW family and step */
- if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
- max_section_size = IWL_MAX_NVM_SECTION_SIZE;
- else
- max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE;
-
- /*
- * Obtain NVM image via request_firmware. Since we already used
- * request_firmware_nowait() for the firmware binary load and only
- * get here after that we assume the NVM request can be satisfied
- * synchronously.
- */
- ret = request_firmware(&fw_entry, mvm->nvm_file_name,
- mvm->trans->dev);
- if (ret) {
- IWL_ERR(mvm, "ERROR: %s isn't available %d\n",
- mvm->nvm_file_name, ret);
- return ret;
- }
-
- IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
- mvm->nvm_file_name, fw_entry->size);
-
- if (fw_entry->size > MAX_NVM_FILE_LEN) {
- IWL_ERR(mvm, "NVM file too large\n");
- ret = -EINVAL;
- goto out;
- }
-
- eof = fw_entry->data + fw_entry->size;
- dword_buff = (__le32 *)fw_entry->data;
-
- /* some NVM file will contain a header.
- * The header is identified by 2 dwords header as follow:
- * dword[0] = 0x2A504C54
- * dword[1] = 0x4E564D2A
- *
- * This header must be skipped when providing the NVM data to the FW.
- */
- if (fw_entry->size > NVM_HEADER_SIZE &&
- dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
- dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
- file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE);
- IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
- IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
- le32_to_cpu(dword_buff[3]));
-
- /* nvm file validation, dword_buff[2] holds the file version */
- if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP &&
- le32_to_cpu(dword_buff[2]) < 0xE4A) ||
- (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP &&
- le32_to_cpu(dword_buff[2]) >= 0xE4A)) {
- ret = -EFAULT;
- goto out;
- }
- } else {
- file_sec = (void *)fw_entry->data;
- }
-
- while (true) {
- if (file_sec->data > eof) {
- IWL_ERR(mvm,
- "ERROR - NVM file too short for section header\n");
- ret = -EINVAL;
- break;
- }
-
- /* check for EOF marker */
- if (!file_sec->word1 && !file_sec->word2) {
- ret = 0;
- break;
- }
-
- if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- section_size =
- 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
- section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
- } else {
- section_size = 2 * NVM_WORD2_LEN_FAMILY_8000(
- le16_to_cpu(file_sec->word2));
- section_id = NVM_WORD1_ID_FAMILY_8000(
- le16_to_cpu(file_sec->word1));
- }
-
- if (section_size > max_section_size) {
- IWL_ERR(mvm, "ERROR - section too large (%d)\n",
- section_size);
- ret = -EINVAL;
- break;
- }
-
- if (!section_size) {
- IWL_ERR(mvm, "ERROR - section empty\n");
- ret = -EINVAL;
- break;
- }
-
- if (file_sec->data + section_size > eof) {
- IWL_ERR(mvm,
- "ERROR - NVM file too short for section (%d bytes)\n",
- section_size);
- ret = -EINVAL;
- break;
- }
-
- if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
- "Invalid NVM section ID %d\n", section_id)) {
- ret = -EINVAL;
- break;
- }
-
- temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
- if (!temp) {
- ret = -ENOMEM;
- break;
- }
- kfree(mvm->nvm_sections[section_id].data);
- mvm->nvm_sections[section_id].data = temp;
- mvm->nvm_sections[section_id].length = section_size;
-
- /* advance to the next section */
- file_sec = (void *)(file_sec->data + section_size);
- }
-out:
- release_firmware(fw_entry);
- return ret;
-}
-
-/* Loads the NVM data stored in mvm->nvm_sections into the NIC */
-int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
-{
- int i, ret = 0;
- struct iwl_nvm_section *sections = mvm->nvm_sections;
-
- IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n");
-
- for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) {
- if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length)
- continue;
- ret = iwl_nvm_write_section(mvm, i, sections[i].data,
- sections[i].length);
- if (ret < 0) {
- IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
- break;
- }
- }
- return ret;
-}
-
-int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
-{
- int ret, section;
- u32 size_read = 0;
- u8 *nvm_buffer, *temp;
- const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step;
- const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
-
- if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
- return -EINVAL;
-
- /* load NVM values from nic */
- if (read_nvm_from_nic) {
- /* Read From FW NVM */
- IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
-
- nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
- GFP_KERNEL);
- if (!nvm_buffer)
- return -ENOMEM;
- for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) {
- /* we override the constness for initial read */
- ret = iwl_nvm_read_section(mvm, section, nvm_buffer,
- size_read);
- if (ret < 0)
- continue;
- size_read += ret;
- temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
- if (!temp) {
- ret = -ENOMEM;
- break;
- }
- mvm->nvm_sections[section].data = temp;
- mvm->nvm_sections[section].length = ret;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- switch (section) {
- case NVM_SECTION_TYPE_SW:
- mvm->nvm_sw_blob.data = temp;
- mvm->nvm_sw_blob.size = ret;
- break;
- case NVM_SECTION_TYPE_CALIBRATION:
- mvm->nvm_calib_blob.data = temp;
- mvm->nvm_calib_blob.size = ret;
- break;
- case NVM_SECTION_TYPE_PRODUCTION:
- mvm->nvm_prod_blob.data = temp;
- mvm->nvm_prod_blob.size = ret;
- break;
- case NVM_SECTION_TYPE_PHY_SKU:
- mvm->nvm_phy_sku_blob.data = temp;
- mvm->nvm_phy_sku_blob.size = ret;
- break;
- default:
- if (section == mvm->cfg->nvm_hw_section_num) {
- mvm->nvm_hw_blob.data = temp;
- mvm->nvm_hw_blob.size = ret;
- break;
- }
- }
-#endif
- }
- if (!size_read)
- IWL_ERR(mvm, "OTP is blank\n");
- kfree(nvm_buffer);
- }
-
- /* Only if PNVM selected in the mod param - load external NVM */
- if (mvm->nvm_file_name) {
- /* read External NVM file from the mod param */
- ret = iwl_mvm_read_external_nvm(mvm);
- if (ret) {
- /* choose the nvm_file name according to the
- * HW step
- */
- if (CSR_HW_REV_STEP(mvm->trans->hw_rev) ==
- SILICON_B_STEP)
- mvm->nvm_file_name = nvm_file_B;
- else
- mvm->nvm_file_name = nvm_file_C;
-
- if (ret == -EFAULT && mvm->nvm_file_name) {
- /* in case nvm file was failed try again */
- ret = iwl_mvm_read_external_nvm(mvm);
- if (ret)
- return ret;
- } else {
- return ret;
- }
- }
- }
-
- /* parse the relevant nvm sections */
- mvm->nvm_data = iwl_parse_nvm_sections(mvm);
- if (!mvm->nvm_data)
- return -ENODATA;
- IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n",
- mvm->nvm_data->nvm_version);
-
- return 0;
-}
-
-struct iwl_mcc_update_resp *
-iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
- enum iwl_mcc_source src_id)
-{
- struct iwl_mcc_update_cmd mcc_update_cmd = {
- .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
- .source_id = (u8)src_id,
- };
- struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL;
- struct iwl_rx_packet *pkt;
- struct iwl_host_cmd cmd = {
- .id = MCC_UPDATE_CMD,
- .flags = CMD_WANT_SKB,
- .data = { &mcc_update_cmd },
- };
-
- int ret;
- u32 status;
- int resp_len, n_channels;
- u16 mcc;
-
- if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
- return ERR_PTR(-EOPNOTSUPP);
-
- cmd.len[0] = sizeof(struct iwl_mcc_update_cmd);
-
- IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n",
- alpha2[0], alpha2[1], src_id);
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (ret)
- return ERR_PTR(ret);
-
- pkt = cmd.resp_pkt;
-
- /* Extract MCC response */
- mcc_resp = (void *)pkt->data;
- status = le32_to_cpu(mcc_resp->status);
-
- mcc = le16_to_cpu(mcc_resp->mcc);
-
- /* W/A for a FW/NVM issue - returns 0x00 for the world domain */
- if (mcc == 0) {
- mcc = 0x3030; /* "00" - world */
- mcc_resp->mcc = cpu_to_le16(mcc);
- }
-
- n_channels = __le32_to_cpu(mcc_resp->n_channels);
- IWL_DEBUG_LAR(mvm,
- "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n",
- status, mcc, mcc >> 8, mcc & 0xff,
- !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels);
-
- resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32);
- resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL);
- if (!resp_cp) {
- ret = -ENOMEM;
- goto exit;
- }
-
- ret = 0;
-exit:
- iwl_free_resp(&cmd);
- if (ret)
- return ERR_PTR(ret);
- return resp_cp;
-}
-
-#ifdef CONFIG_ACPI
-#define WRD_METHOD "WRDD"
-#define WRDD_WIFI (0x07)
-#define WRDD_WIGIG (0x10)
-
-static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd)
-{
- union acpi_object *mcc_pkg, *domain_type, *mcc_value;
- u32 i;
-
- if (wrdd->type != ACPI_TYPE_PACKAGE ||
- wrdd->package.count < 2 ||
- wrdd->package.elements[0].type != ACPI_TYPE_INTEGER ||
- wrdd->package.elements[0].integer.value != 0) {
- IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n");
- return 0;
- }
-
- for (i = 1 ; i < wrdd->package.count ; ++i) {
- mcc_pkg = &wrdd->package.elements[i];
-
- if (mcc_pkg->type != ACPI_TYPE_PACKAGE ||
- mcc_pkg->package.count < 2 ||
- mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
- mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
- mcc_pkg = NULL;
- continue;
- }
-
- domain_type = &mcc_pkg->package.elements[0];
- if (domain_type->integer.value == WRDD_WIFI)
- break;
-
- mcc_pkg = NULL;
- }
-
- if (mcc_pkg) {
- mcc_value = &mcc_pkg->package.elements[1];
- return mcc_value->integer.value;
- }
-
- return 0;
-}
-
-static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
-{
- acpi_handle root_handle;
- acpi_handle handle;
- struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
- acpi_status status;
- u32 mcc_val;
- struct pci_dev *pdev = to_pci_dev(mvm->dev);
-
- root_handle = ACPI_HANDLE(&pdev->dev);
- if (!root_handle) {
- IWL_DEBUG_LAR(mvm,
- "Could not retrieve root port ACPI handle\n");
- return -ENOENT;
- }
-
- /* Get the method's handle */
- status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle);
- if (ACPI_FAILURE(status)) {
- IWL_DEBUG_LAR(mvm, "WRD method not found\n");
- return -ENOENT;
- }
-
- /* Call WRDD with no arguments */
- status = acpi_evaluate_object(handle, NULL, NULL, &wrdd);
- if (ACPI_FAILURE(status)) {
- IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status);
- return -ENOENT;
- }
-
- mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer);
- kfree(wrdd.pointer);
- if (!mcc_val)
- return -ENOENT;
-
- mcc[0] = (mcc_val >> 8) & 0xff;
- mcc[1] = mcc_val & 0xff;
- mcc[2] = '\0';
- return 0;
-}
-#else /* CONFIG_ACPI */
-static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
-{
- return -ENOENT;
-}
-#endif
-
-int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
-{
- bool tlv_lar;
- bool nvm_lar;
- int retval;
- struct ieee80211_regdomain *regd;
- char mcc[3];
-
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
- tlv_lar = fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
- nvm_lar = mvm->nvm_data->lar_enabled;
- if (tlv_lar != nvm_lar)
- IWL_INFO(mvm,
- "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n",
- tlv_lar ? "enabled" : "disabled",
- nvm_lar ? "enabled" : "disabled");
- }
-
- if (!iwl_mvm_is_lar_supported(mvm))
- return 0;
-
- /*
- * try to replay the last set MCC to FW. If it doesn't exist,
- * queue an update to cfg80211 to retrieve the default alpha2 from FW.
- */
- retval = iwl_mvm_init_fw_regd(mvm);
- if (retval != -ENOENT)
- return retval;
-
- /*
- * Driver regulatory hint for initial update, this also informs the
- * firmware we support wifi location updates.
- * Disallow scans that might crash the FW while the LAR regdomain
- * is not set.
- */
- mvm->lar_regdom_set = false;
-
- regd = iwl_mvm_get_current_regdomain(mvm, NULL);
- if (IS_ERR_OR_NULL(regd))
- return -EIO;
-
- if (iwl_mvm_is_wifi_mcc_supported(mvm) &&
- !iwl_mvm_get_bios_mcc(mvm, mcc)) {
- kfree(regd);
- regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc,
- MCC_SOURCE_BIOS, NULL);
- if (IS_ERR_OR_NULL(regd))
- return -EIO;
- }
-
- retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
- kfree(regd);
- return retval;
-}
-
-void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mcc_chub_notif *notif = (void *)pkt->data;
- enum iwl_mcc_source src;
- char mcc[3];
- struct ieee80211_regdomain *regd;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
- return;
-
- mcc[0] = notif->mcc >> 8;
- mcc[1] = notif->mcc & 0xff;
- mcc[2] = '\0';
- src = notif->source_id;
-
- IWL_DEBUG_LAR(mvm,
- "RX: received chub update mcc cmd (mcc '%s' src %d)\n",
- mcc, src);
- regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL);
- if (IS_ERR_OR_NULL(regd))
- return;
-
- regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
- kfree(regd);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c
deleted file mode 100644
index 68b0169c8892..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/offloading.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <net/ipv6.h>
-#include <net/addrconf.h>
-#include "mvm.h"
-
-void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
- struct iwl_wowlan_config_cmd *cmd)
-{
- int i;
-
- /*
- * For QoS counters, we store the one to use next, so subtract 0x10
- * since the uCode will add 0x10 *before* using the value while we
- * increment after using the value (i.e. store the next value to use).
- */
- for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
- u16 seq = mvm_ap_sta->tid_data[i].seq_number;
- seq -= 0x10;
- cmd->qos_seq[i] = cpu_to_le16(seq);
- }
-}
-
-int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool disable_offloading,
- u32 cmd_flags)
-{
- union {
- struct iwl_proto_offload_cmd_v1 v1;
- struct iwl_proto_offload_cmd_v2 v2;
- struct iwl_proto_offload_cmd_v3_small v3s;
- struct iwl_proto_offload_cmd_v3_large v3l;
- } cmd = {};
- struct iwl_host_cmd hcmd = {
- .id = PROT_OFFLOAD_CONFIG_CMD,
- .flags = cmd_flags,
- .data[0] = &cmd,
- .dataflags[0] = IWL_HCMD_DFL_DUP,
- };
- struct iwl_proto_offload_cmd_common *common;
- u32 enabled = 0, size;
- u32 capa_flags = mvm->fw->ucode_capa.flags;
-#if IS_ENABLED(CONFIG_IPV6)
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int i;
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
- capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
- struct iwl_ns_config *nsc;
- struct iwl_targ_addr *addrs;
- int n_nsc, n_addrs;
- int c;
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
- nsc = cmd.v3s.ns_config;
- n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
- addrs = cmd.v3s.targ_addrs;
- n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
- } else {
- nsc = cmd.v3l.ns_config;
- n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
- addrs = cmd.v3l.targ_addrs;
- n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
- }
-
- if (mvmvif->num_target_ipv6_addrs)
- enabled |= IWL_D3_PROTO_OFFLOAD_NS;
-
- /*
- * For each address we have (and that will fit) fill a target
- * address struct and combine for NS offload structs with the
- * solicited node addresses.
- */
- for (i = 0, c = 0;
- i < mvmvif->num_target_ipv6_addrs &&
- i < n_addrs && c < n_nsc; i++) {
- struct in6_addr solicited_addr;
- int j;
-
- addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
- &solicited_addr);
- for (j = 0; j < c; j++)
- if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
- &solicited_addr) == 0)
- break;
- if (j == c)
- c++;
- addrs[i].addr = mvmvif->target_ipv6_addrs[i];
- addrs[i].config_num = cpu_to_le32(j);
- nsc[j].dest_ipv6_addr = solicited_addr;
- memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
- }
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
- cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
- else
- cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
- } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
- if (mvmvif->num_target_ipv6_addrs) {
- enabled |= IWL_D3_PROTO_OFFLOAD_NS;
- memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
- }
-
- BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
- sizeof(mvmvif->target_ipv6_addrs[0]));
-
- for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
- IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++)
- memcpy(cmd.v2.target_ipv6_addr[i],
- &mvmvif->target_ipv6_addrs[i],
- sizeof(cmd.v2.target_ipv6_addr[i]));
- } else {
- if (mvmvif->num_target_ipv6_addrs) {
- enabled |= IWL_D3_PROTO_OFFLOAD_NS;
- memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
- }
-
- BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
- sizeof(mvmvif->target_ipv6_addrs[0]));
-
- for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
- IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++)
- memcpy(cmd.v1.target_ipv6_addr[i],
- &mvmvif->target_ipv6_addrs[i],
- sizeof(cmd.v1.target_ipv6_addr[i]));
- }
-#endif
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
- common = &cmd.v3s.common;
- size = sizeof(cmd.v3s);
- } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
- common = &cmd.v3l.common;
- size = sizeof(cmd.v3l);
- } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
- common = &cmd.v2.common;
- size = sizeof(cmd.v2);
- } else {
- common = &cmd.v1.common;
- size = sizeof(cmd.v1);
- }
-
- if (vif->bss_conf.arp_addr_cnt) {
- enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
- common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
- memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
- }
-
- if (!disable_offloading)
- common->enabled = cpu_to_le32(enabled);
-
- hcmd.len[0] = size;
- return iwl_mvm_send_cmd(mvm, &hcmd);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
deleted file mode 100644
index 13c97f665ba8..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ /dev/null
@@ -1,1434 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-#include <net/mac80211.h>
-
-#include "iwl-notif-wait.h"
-#include "iwl-trans.h"
-#include "iwl-op-mode.h"
-#include "iwl-fw.h"
-#include "iwl-debug.h"
-#include "iwl-drv.h"
-#include "iwl-modparams.h"
-#include "mvm.h"
-#include "iwl-phy-db.h"
-#include "iwl-eeprom-parse.h"
-#include "iwl-csr.h"
-#include "iwl-io.h"
-#include "iwl-prph.h"
-#include "rs.h"
-#include "fw-api-scan.h"
-#include "time-event.h"
-
-#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux"
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
-MODULE_LICENSE("GPL");
-
-static const struct iwl_op_mode_ops iwl_mvm_ops;
-static const struct iwl_op_mode_ops iwl_mvm_ops_mq;
-
-struct iwl_mvm_mod_params iwlmvm_mod_params = {
- .power_scheme = IWL_POWER_SCHEME_BPS,
- .tfd_q_hang_detect = true
- /* rest of fields are 0 by default */
-};
-
-module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO);
-MODULE_PARM_DESC(init_dbg,
- "set to true to debug an ASSERT in INIT fw (default: false");
-module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO);
-MODULE_PARM_DESC(power_scheme,
- "power management scheme: 1-active, 2-balanced, 3-low power, default: 2");
-module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect,
- bool, S_IRUGO);
-MODULE_PARM_DESC(tfd_q_hang_detect,
- "TFD queues hang detection (default: true");
-
-/*
- * module init and exit functions
- */
-static int __init iwl_mvm_init(void)
-{
- int ret;
-
- ret = iwl_mvm_rate_control_register();
- if (ret) {
- pr_err("Unable to register rate control algorithm: %d\n", ret);
- return ret;
- }
-
- ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops);
-
- if (ret) {
- pr_err("Unable to register MVM op_mode: %d\n", ret);
- iwl_mvm_rate_control_unregister();
- }
-
- return ret;
-}
-module_init(iwl_mvm_init);
-
-static void __exit iwl_mvm_exit(void)
-{
- iwl_opmode_deregister("iwlmvm");
- iwl_mvm_rate_control_unregister();
-}
-module_exit(iwl_mvm_exit);
-
-static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
- u32 reg_val = 0;
- u32 phy_config = iwl_mvm_get_phy_config(mvm);
-
- radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >>
- FW_PHY_CFG_RADIO_TYPE_POS;
- radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >>
- FW_PHY_CFG_RADIO_STEP_POS;
- radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >>
- FW_PHY_CFG_RADIO_DASH_POS;
-
- /* SKU control */
- reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) <<
- CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
- reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) <<
- CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
-
- /* radio configuration */
- reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
- reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
- reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
-
- WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) &
- ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE);
-
- /*
- * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and
- * shouldn't be set to any non-zero value. The same is supposed to be
- * true of the other HW, but unsetting them (such as the 7260) causes
- * automatic tests to fail on seemingly unrelated errors. Need to
- * further investigate this, but for now we'll separate cases.
- */
- if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
- reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
-
- iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
- CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP |
- CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
- CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
- CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH |
- CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
- CSR_HW_IF_CONFIG_REG_BIT_MAC_SI,
- reg_val);
-
- IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
- radio_cfg_step, radio_cfg_dash);
-
- /*
- * W/A : NIC is stuck in a reset state after Early PCIe power off
- * (PCIe power is lost before PERST# is asserted), causing ME FW
- * to lose ownership and not being able to obtain it back.
- */
- if (!mvm->trans->cfg->apmg_not_supported)
- iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
- ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
-}
-
-struct iwl_rx_handlers {
- u16 cmd_id;
- bool async;
- void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
-};
-
-#define RX_HANDLER(_cmd_id, _fn, _async) \
- { .cmd_id = _cmd_id , .fn = _fn , .async = _async }
-#define RX_HANDLER_GRP(_grp, _cmd, _fn, _async) \
- { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .async = _async }
-
-/*
- * Handlers for fw notifications
- * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME
- * This list should be in order of frequency for performance purposes.
- *
- * The handler can be SYNC - this means that it will be called in the Rx path
- * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and
- * only in this case!), it should be set as ASYNC. In that case, it will be
- * called from a worker with mvm->mutex held.
- */
-static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
- RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
- RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
-
- RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
- RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true),
- RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
- RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
- iwl_mvm_rx_ant_coupling_notif, true),
-
- RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
- RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true),
-
- RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
-
- RX_HANDLER(SCAN_ITERATION_COMPLETE,
- iwl_mvm_rx_lmac_scan_iter_complete_notif, false),
- RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
- iwl_mvm_rx_lmac_scan_complete_notif, true),
- RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_match_found,
- false),
- RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
- true),
- RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC,
- iwl_mvm_rx_umac_scan_iter_complete_notif, false),
-
- RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
-
- RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif,
- false),
-
- RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
- RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
- iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
- RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
- RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE,
- iwl_mvm_temp_notif, true),
-
- RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
- true),
- RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false),
- RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true),
-
-};
-#undef RX_HANDLER
-#undef RX_HANDLER_GRP
-#define CMD(x) [x] = #x
-
-static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = {
- CMD(MVM_ALIVE),
- CMD(REPLY_ERROR),
- CMD(ECHO_CMD),
- CMD(INIT_COMPLETE_NOTIF),
- CMD(PHY_CONTEXT_CMD),
- CMD(MGMT_MCAST_KEY),
- CMD(TX_CMD),
- CMD(TXPATH_FLUSH),
- CMD(SHARED_MEM_CFG),
- CMD(MAC_CONTEXT_CMD),
- CMD(TIME_EVENT_CMD),
- CMD(TIME_EVENT_NOTIFICATION),
- CMD(BINDING_CONTEXT_CMD),
- CMD(TIME_QUOTA_CMD),
- CMD(NON_QOS_TX_COUNTER_CMD),
- CMD(DC2DC_CONFIG_CMD),
- CMD(NVM_ACCESS_CMD),
- CMD(PHY_CONFIGURATION_CMD),
- CMD(CALIB_RES_NOTIF_PHY_DB),
- CMD(SET_CALIB_DEFAULT_CMD),
- CMD(FW_PAGING_BLOCK_CMD),
- CMD(ADD_STA_KEY),
- CMD(ADD_STA),
- CMD(FW_GET_ITEM_CMD),
- CMD(REMOVE_STA),
- CMD(LQ_CMD),
- CMD(SCAN_OFFLOAD_CONFIG_CMD),
- CMD(MATCH_FOUND_NOTIFICATION),
- CMD(SCAN_OFFLOAD_REQUEST_CMD),
- CMD(SCAN_OFFLOAD_ABORT_CMD),
- CMD(HOT_SPOT_CMD),
- CMD(SCAN_OFFLOAD_COMPLETE),
- CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
- CMD(SCAN_ITERATION_COMPLETE),
- CMD(POWER_TABLE_CMD),
- CMD(WEP_KEY),
- CMD(REPLY_RX_PHY_CMD),
- CMD(REPLY_RX_MPDU_CMD),
- CMD(BEACON_NOTIFICATION),
- CMD(BEACON_TEMPLATE_CMD),
- CMD(STATISTICS_CMD),
- CMD(STATISTICS_NOTIFICATION),
- CMD(EOSP_NOTIFICATION),
- CMD(REDUCE_TX_POWER_CMD),
- CMD(TX_ANT_CONFIGURATION_CMD),
- CMD(D3_CONFIG_CMD),
- CMD(D0I3_END_CMD),
- CMD(PROT_OFFLOAD_CONFIG_CMD),
- CMD(OFFLOADS_QUERY_CMD),
- CMD(REMOTE_WAKE_CONFIG_CMD),
- CMD(WOWLAN_PATTERNS),
- CMD(WOWLAN_CONFIGURATION),
- CMD(WOWLAN_TSC_RSC_PARAM),
- CMD(WOWLAN_TKIP_PARAM),
- CMD(WOWLAN_KEK_KCK_MATERIAL),
- CMD(WOWLAN_GET_STATUSES),
- CMD(WOWLAN_TX_POWER_PER_DB),
- CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD),
- CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD),
- CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD),
- CMD(CARD_STATE_NOTIFICATION),
- CMD(MISSED_BEACONS_NOTIFICATION),
- CMD(BT_COEX_PRIO_TABLE),
- CMD(BT_COEX_PROT_ENV),
- CMD(BT_PROFILE_NOTIFICATION),
- CMD(BT_CONFIG),
- CMD(MCAST_FILTER_CMD),
- CMD(BCAST_FILTER_CMD),
- CMD(REPLY_SF_CFG_CMD),
- CMD(REPLY_BEACON_FILTERING_CMD),
- CMD(CMD_DTS_MEASUREMENT_TRIGGER),
- CMD(DTS_MEASUREMENT_NOTIFICATION),
- CMD(REPLY_THERMAL_MNG_BACKOFF),
- CMD(MAC_PM_POWER_TABLE),
- CMD(LTR_CONFIG),
- CMD(BT_COEX_CI),
- CMD(BT_COEX_UPDATE_SW_BOOST),
- CMD(BT_COEX_UPDATE_CORUN_LUT),
- CMD(BT_COEX_UPDATE_REDUCED_TXP),
- CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
- CMD(ANTENNA_COUPLING_NOTIFICATION),
- CMD(SCD_QUEUE_CFG),
- CMD(SCAN_CFG_CMD),
- CMD(SCAN_REQ_UMAC),
- CMD(SCAN_ABORT_UMAC),
- CMD(SCAN_COMPLETE_UMAC),
- CMD(TDLS_CHANNEL_SWITCH_CMD),
- CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
- CMD(TDLS_CONFIG_CMD),
- CMD(MCC_UPDATE_CMD),
- CMD(SCAN_ITERATION_COMPLETE_UMAC),
-};
-#undef CMD
-
-/* this forward declaration can avoid to export the function */
-static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
-static void iwl_mvm_d0i3_exit_work(struct work_struct *wk);
-
-static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg)
-{
- const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs;
-
- if (!pwr_tx_backoff)
- return 0;
-
- while (pwr_tx_backoff->pwr) {
- if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr)
- return pwr_tx_backoff->backoff;
-
- pwr_tx_backoff++;
- }
-
- return 0;
-}
-
-static void iwl_mvm_fw_error_dump_wk(struct work_struct *work);
-
-static struct iwl_op_mode *
-iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
- const struct iwl_fw *fw, struct dentry *dbgfs_dir)
-{
- struct ieee80211_hw *hw;
- struct iwl_op_mode *op_mode;
- struct iwl_mvm *mvm;
- struct iwl_trans_config trans_cfg = {};
- static const u8 no_reclaim_cmds[] = {
- TX_CMD,
- };
- int err, scan_size;
- u32 min_backoff;
-
- /*
- * We use IWL_MVM_STATION_COUNT to check the validity of the station
- * index all over the driver - check that its value corresponds to the
- * array size.
- */
- BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT);
-
- /********************************
- * 1. Allocating and configuring HW data
- ********************************/
- hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +
- sizeof(struct iwl_mvm),
- &iwl_mvm_hw_ops);
- if (!hw)
- return NULL;
-
- if (cfg->max_rx_agg_size)
- hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size;
-
- if (cfg->max_tx_agg_size)
- hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size;
-
- op_mode = hw->priv;
-
- mvm = IWL_OP_MODE_GET_MVM(op_mode);
- mvm->dev = trans->dev;
- mvm->trans = trans;
- mvm->cfg = cfg;
- mvm->fw = fw;
- mvm->hw = hw;
-
- if (iwl_mvm_has_new_rx_api(mvm)) {
- op_mode->ops = &iwl_mvm_ops_mq;
- } else {
- op_mode->ops = &iwl_mvm_ops;
-
- if (WARN_ON(trans->num_rx_queues > 1))
- goto out_free;
- }
-
- mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
-
- mvm->aux_queue = 15;
- mvm->first_agg_queue = 16;
- mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1;
- if (mvm->cfg->base_params->num_of_queues == 16) {
- mvm->aux_queue = 11;
- mvm->first_agg_queue = 12;
- }
- mvm->sf_state = SF_UNINIT;
- mvm->low_latency_agg_frame_limit = 6;
- mvm->cur_ucode = IWL_UCODE_INIT;
-
- mutex_init(&mvm->mutex);
- mutex_init(&mvm->d0i3_suspend_mutex);
- spin_lock_init(&mvm->async_handlers_lock);
- INIT_LIST_HEAD(&mvm->time_event_list);
- INIT_LIST_HEAD(&mvm->aux_roc_te_list);
- INIT_LIST_HEAD(&mvm->async_handlers_list);
- spin_lock_init(&mvm->time_event_lock);
- spin_lock_init(&mvm->queue_info_lock);
-
- INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
- INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
- INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
- INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
- INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk);
- INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
-
- spin_lock_init(&mvm->d0i3_tx_lock);
- spin_lock_init(&mvm->refs_lock);
- skb_queue_head_init(&mvm->d0i3_tx);
- init_waitqueue_head(&mvm->d0i3_exit_waitq);
-
- SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
-
- /*
- * Populate the state variables that the transport layer needs
- * to know about.
- */
- trans_cfg.op_mode = op_mode;
- trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
- trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
- trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
- trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_WIDE_CMD_HDR);
-
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE)
- trans_cfg.bc_table_dword = true;
-
- trans_cfg.command_names = iwl_mvm_cmd_strings;
-
- trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE;
- trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD;
- trans_cfg.scd_set_active = true;
-
- trans_cfg.sdio_adma_addr = fw->sdio_adma_addr;
-
- /* Set a short watchdog for the command queue */
- trans_cfg.cmd_q_wdg_timeout =
- iwl_mvm_get_wd_timeout(mvm, NULL, false, true);
-
- snprintf(mvm->hw->wiphy->fw_version,
- sizeof(mvm->hw->wiphy->fw_version),
- "%s", fw->fw_version);
-
- /* Configure transport layer */
- iwl_trans_configure(mvm->trans, &trans_cfg);
-
- trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
- trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
- trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv;
- trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
- memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
- sizeof(trans->dbg_conf_tlv));
- trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv;
-
- /* set up notification wait support */
- iwl_notification_wait_init(&mvm->notif_wait);
-
- /* Init phy db */
- mvm->phy_db = iwl_phy_db_init(trans);
- if (!mvm->phy_db) {
- IWL_ERR(mvm, "Cannot init phy_db\n");
- goto out_free;
- }
-
- IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
- mvm->cfg->name, mvm->trans->hw_rev);
-
- min_backoff = calc_min_backoff(trans, cfg);
- iwl_mvm_tt_initialize(mvm, min_backoff);
-
- if (iwlwifi_mod_params.nvm_file)
- mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
- else
- IWL_DEBUG_EEPROM(mvm->trans->dev,
- "working without external nvm file\n");
-
- if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name,
- "not allowing power-up and not having nvm_file\n"))
- goto out_free;
-
- /*
- * Even if nvm exists in the nvm_file driver should read again the nvm
- * from the nic because there might be entries that exist in the OTP
- * and not in the file.
- * for nics with no_power_up_nic_in_init: rely completley on nvm_file
- */
- if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) {
- err = iwl_nvm_init(mvm, false);
- if (err)
- goto out_free;
- } else {
- err = iwl_trans_start_hw(mvm->trans);
- if (err)
- goto out_free;
-
- mutex_lock(&mvm->mutex);
- err = iwl_run_init_mvm_ucode(mvm, true);
- if (!err || !iwlmvm_mod_params.init_dbg)
- iwl_trans_stop_device(trans);
- mutex_unlock(&mvm->mutex);
- /* returns 0 if successful, 1 if success but in rfkill */
- if (err < 0 && !iwlmvm_mod_params.init_dbg) {
- IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
- goto out_free;
- }
- }
-
- scan_size = iwl_mvm_scan_size(mvm);
-
- mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
- if (!mvm->scan_cmd)
- goto out_free;
-
- /* Set EBS as successful as long as not stated otherwise by the FW. */
- mvm->last_ebs_successful = true;
-
- err = iwl_mvm_mac_setup_register(mvm);
- if (err)
- goto out_free;
-
- err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir);
- if (err)
- goto out_unregister;
-
- memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
-
- /* rpm starts with a taken ref. only set the appropriate bit here. */
- mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1;
-
- iwl_mvm_tof_init(mvm);
-
- return op_mode;
-
- out_unregister:
- ieee80211_unregister_hw(mvm->hw);
- iwl_mvm_leds_exit(mvm);
- out_free:
- flush_delayed_work(&mvm->fw_dump_wk);
- iwl_phy_db_free(mvm->phy_db);
- kfree(mvm->scan_cmd);
- if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name)
- iwl_trans_op_mode_leave(trans);
- ieee80211_free_hw(mvm->hw);
- return NULL;
-}
-
-static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- int i;
-
- iwl_mvm_leds_exit(mvm);
-
- iwl_mvm_tt_exit(mvm);
-
- ieee80211_unregister_hw(mvm->hw);
-
- kfree(mvm->scan_cmd);
- kfree(mvm->mcast_filter_cmd);
- mvm->mcast_filter_cmd = NULL;
-
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
- kfree(mvm->d3_resume_sram);
- if (mvm->nd_config) {
- kfree(mvm->nd_config->match_sets);
- kfree(mvm->nd_config->scan_plans);
- kfree(mvm->nd_config);
- mvm->nd_config = NULL;
- }
-#endif
-
- iwl_trans_op_mode_leave(mvm->trans);
-
- iwl_phy_db_free(mvm->phy_db);
- mvm->phy_db = NULL;
-
- iwl_free_nvm_data(mvm->nvm_data);
- for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
- kfree(mvm->nvm_sections[i].data);
-
- iwl_mvm_tof_clean(mvm);
-
- ieee80211_free_hw(mvm->hw);
-}
-
-struct iwl_async_handler_entry {
- struct list_head list;
- struct iwl_rx_cmd_buffer rxb;
- void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
-};
-
-void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm)
-{
- struct iwl_async_handler_entry *entry, *tmp;
-
- spin_lock_bh(&mvm->async_handlers_lock);
- list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) {
- iwl_free_rxb(&entry->rxb);
- list_del(&entry->list);
- kfree(entry);
- }
- spin_unlock_bh(&mvm->async_handlers_lock);
-}
-
-static void iwl_mvm_async_handlers_wk(struct work_struct *wk)
-{
- struct iwl_mvm *mvm =
- container_of(wk, struct iwl_mvm, async_handlers_wk);
- struct iwl_async_handler_entry *entry, *tmp;
- struct list_head local_list;
-
- INIT_LIST_HEAD(&local_list);
-
- /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */
- mutex_lock(&mvm->mutex);
-
- /*
- * Sync with Rx path with a lock. Remove all the entries from this list,
- * add them to a local one (lock free), and then handle them.
- */
- spin_lock_bh(&mvm->async_handlers_lock);
- list_splice_init(&mvm->async_handlers_list, &local_list);
- spin_unlock_bh(&mvm->async_handlers_lock);
-
- list_for_each_entry_safe(entry, tmp, &local_list, list) {
- entry->fn(mvm, &entry->rxb);
- iwl_free_rxb(&entry->rxb);
- list_del(&entry->list);
- kfree(entry);
- }
- mutex_unlock(&mvm->mutex);
-}
-
-static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_cmd *cmds_trig;
- int i;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF);
- cmds_trig = (void *)trig->data;
-
- if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
- return;
-
- for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) {
- /* don't collect on CMD 0 */
- if (!cmds_trig->cmds[i].cmd_id)
- break;
-
- if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd ||
- cmds_trig->cmds[i].group_id != pkt->hdr.group_id)
- continue;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trig,
- "CMD 0x%02x.%02x received",
- pkt->hdr.group_id, pkt->hdr.cmd);
- break;
- }
-}
-
-static void iwl_mvm_rx_common(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb,
- struct iwl_rx_packet *pkt)
-{
- int i;
-
- iwl_mvm_rx_check_trigger(mvm, pkt);
-
- /*
- * Do the notification wait before RX handlers so
- * even if the RX handler consumes the RXB we have
- * access to it in the notification wait entry.
- */
- iwl_notification_wait_notify(&mvm->notif_wait, pkt);
-
- for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) {
- const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i];
- struct iwl_async_handler_entry *entry;
-
- if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd))
- continue;
-
- if (!rx_h->async) {
- rx_h->fn(mvm, rxb);
- return;
- }
-
- entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
- /* we can't do much... */
- if (!entry)
- return;
-
- entry->rxb._page = rxb_steal_page(rxb);
- entry->rxb._offset = rxb->_offset;
- entry->rxb._rx_page_order = rxb->_rx_page_order;
- entry->fn = rx_h->fn;
- spin_lock(&mvm->async_handlers_lock);
- list_add_tail(&entry->list, &mvm->async_handlers_list);
- spin_unlock(&mvm->async_handlers_lock);
- schedule_work(&mvm->async_handlers_wk);
- break;
- }
-}
-
-static void iwl_mvm_rx(struct iwl_op_mode *op_mode,
- struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-
- if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
- iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
- else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
- iwl_mvm_rx_rx_phy_cmd(mvm, rxb);
- else
- iwl_mvm_rx_common(mvm, rxb, pkt);
-}
-
-static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
- struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-
- if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
- iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
- else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
- iwl_mvm_rx_rx_phy_cmd(mvm, rxb);
- else
- iwl_mvm_rx_common(mvm, rxb, pkt);
-}
-
-static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- unsigned long mq;
- int q;
-
- spin_lock_bh(&mvm->queue_info_lock);
- mq = mvm->queue_info[queue].hw_queue_to_mac80211;
- spin_unlock_bh(&mvm->queue_info_lock);
-
- if (WARN_ON_ONCE(!mq))
- return;
-
- for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
- if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) {
- IWL_DEBUG_TX_QUEUES(mvm,
- "queue %d (mac80211 %d) already stopped\n",
- queue, q);
- continue;
- }
-
- ieee80211_stop_queue(mvm->hw, q);
- }
-}
-
-static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- unsigned long mq;
- int q;
-
- spin_lock_bh(&mvm->queue_info_lock);
- mq = mvm->queue_info[queue].hw_queue_to_mac80211;
- spin_unlock_bh(&mvm->queue_info_lock);
-
- if (WARN_ON_ONCE(!mq))
- return;
-
- for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
- if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) {
- IWL_DEBUG_TX_QUEUES(mvm,
- "queue %d (mac80211 %d) still stopped\n",
- queue, q);
- continue;
- }
-
- ieee80211_wake_queue(mvm->hw, q);
- }
-}
-
-void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
-{
- if (state)
- set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
- else
- clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
-
- wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
-}
-
-static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- bool calibrating = ACCESS_ONCE(mvm->calibrating);
-
- if (state)
- set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
- else
- clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
-
- wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
-
- /* iwl_run_init_mvm_ucode is waiting for results, abort it */
- if (calibrating)
- iwl_abort_notification_waits(&mvm->notif_wait);
-
- /*
- * Stop the device if we run OPERATIONAL firmware or if we are in the
- * middle of the calibrations.
- */
- return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
-}
-
-static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- struct ieee80211_tx_info *info;
-
- info = IEEE80211_SKB_CB(skb);
- iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
- ieee80211_free_txskb(mvm->hw, skb);
-}
-
-struct iwl_mvm_reprobe {
- struct device *dev;
- struct work_struct work;
-};
-
-static void iwl_mvm_reprobe_wk(struct work_struct *wk)
-{
- struct iwl_mvm_reprobe *reprobe;
-
- reprobe = container_of(wk, struct iwl_mvm_reprobe, work);
- if (device_reprobe(reprobe->dev))
- dev_err(reprobe->dev, "reprobe failed!\n");
- kfree(reprobe);
- module_put(THIS_MODULE);
-}
-
-static void iwl_mvm_fw_error_dump_wk(struct work_struct *work)
-{
- struct iwl_mvm *mvm =
- container_of(work, struct iwl_mvm, fw_dump_wk.work);
-
- if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT))
- return;
-
- mutex_lock(&mvm->mutex);
-
- /* stop recording */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
- iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
- } else {
- iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
- /* wait before we collect the data till the DBGC stop */
- udelay(100);
- }
-
- iwl_mvm_fw_error_dump(mvm);
-
- /* start recording again if the firmware is not crashed */
- WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) &&
- mvm->fw->dbg_dest_tlv &&
- iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf));
-
- mutex_unlock(&mvm->mutex);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT);
-}
-
-void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
-{
- iwl_abort_notification_waits(&mvm->notif_wait);
-
- /*
- * This is a bit racy, but worst case we tell mac80211 about
- * a stopped/aborted scan when that was already done which
- * is not a problem. It is necessary to abort any os scan
- * here because mac80211 requires having the scan cleared
- * before restarting.
- * We'll reset the scan_status to NONE in restart cleanup in
- * the next start() call from mac80211. If restart isn't called
- * (no fw restart) scan status will stay busy.
- */
- iwl_mvm_report_scan_aborted(mvm);
-
- /*
- * If we're restarting already, don't cycle restarts.
- * If INIT fw asserted, it will likely fail again.
- * If WoWLAN fw asserted, don't restart either, mac80211
- * can't recover this since we're already half suspended.
- */
- if (!mvm->restart_fw && fw_error) {
- iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert,
- NULL);
- } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status)) {
- struct iwl_mvm_reprobe *reprobe;
-
- IWL_ERR(mvm,
- "Firmware error during reconfiguration - reprobe!\n");
-
- /*
- * get a module reference to avoid doing this while unloading
- * anyway and to avoid scheduling a work with code that's
- * being removed.
- */
- if (!try_module_get(THIS_MODULE)) {
- IWL_ERR(mvm, "Module is being unloaded - abort\n");
- return;
- }
-
- reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC);
- if (!reprobe) {
- module_put(THIS_MODULE);
- return;
- }
- reprobe->dev = mvm->trans->dev;
- INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
- schedule_work(&reprobe->work);
- } else if (mvm->cur_ucode == IWL_UCODE_REGULAR) {
- /* don't let the transport/FW power down */
- iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
-
- if (fw_error && mvm->restart_fw > 0)
- mvm->restart_fw--;
- ieee80211_restart_hw(mvm->hw);
- }
-}
-
-static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-
- iwl_mvm_dump_nic_error_log(mvm);
-
- iwl_mvm_nic_restart(mvm, true);
-}
-
-static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-
- WARN_ON(1);
- iwl_mvm_nic_restart(mvm, true);
-}
-
-struct iwl_d0i3_iter_data {
- struct iwl_mvm *mvm;
- u8 ap_sta_id;
- u8 vif_count;
- u8 offloading_tid;
- bool disable_offloading;
-};
-
-static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_d0i3_iter_data *iter_data)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct ieee80211_sta *ap_sta;
- struct iwl_mvm_sta *mvmsta;
- u32 available_tids = 0;
- u8 tid;
-
- if (WARN_ON(vif->type != NL80211_IFTYPE_STATION ||
- mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
- return false;
-
- ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]);
- if (IS_ERR_OR_NULL(ap_sta))
- return false;
-
- mvmsta = iwl_mvm_sta_from_mac80211(ap_sta);
- spin_lock_bh(&mvmsta->lock);
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
- struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
-
- /*
- * in case of pending tx packets, don't use this tid
- * for offloading in order to prevent reuse of the same
- * qos seq counters.
- */
- if (iwl_mvm_tid_queued(tid_data))
- continue;
-
- if (tid_data->state != IWL_AGG_OFF)
- continue;
-
- available_tids |= BIT(tid);
- }
- spin_unlock_bh(&mvmsta->lock);
-
- /*
- * disallow protocol offloading if we have no available tid
- * (with no pending frames and no active aggregation,
- * as we don't handle "holes" properly - the scheduler needs the
- * frame's seq number and TFD index to match)
- */
- if (!available_tids)
- return true;
-
- /* for simplicity, just use the first available tid */
- iter_data->offloading_tid = ffs(available_tids) - 1;
- return false;
-}
-
-static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_d0i3_iter_data *data = _data;
- struct iwl_mvm *mvm = data->mvm;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
-
- IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr);
- if (vif->type != NL80211_IFTYPE_STATION ||
- !vif->bss_conf.assoc)
- return;
-
- /*
- * in case of pending tx packets or active aggregations,
- * avoid offloading features in order to prevent reuse of
- * the same qos seq counters.
- */
- if (iwl_mvm_disallow_offloading(mvm, vif, data))
- data->disable_offloading = true;
-
- iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags);
- iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags);
-
- /*
- * on init/association, mvm already configures POWER_TABLE_CMD
- * and REPLY_MCAST_FILTER_CMD, so currently don't
- * reconfigure them (we might want to use different
- * params later on, though).
- */
- data->ap_sta_id = mvmvif->ap_sta_id;
- data->vif_count++;
-}
-
-static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
- struct iwl_wowlan_config_cmd *cmd,
- struct iwl_d0i3_iter_data *iter_data)
-{
- struct ieee80211_sta *ap_sta;
- struct iwl_mvm_sta *mvm_ap_sta;
-
- if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT)
- return;
-
- rcu_read_lock();
-
- ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]);
- if (IS_ERR_OR_NULL(ap_sta))
- goto out;
-
- mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
- cmd->is_11n_connection = ap_sta->ht_cap.ht_supported;
- cmd->offloading_tid = iter_data->offloading_tid;
-
- /*
- * The d0i3 uCode takes care of the nonqos counters,
- * so configure only the qos seq ones.
- */
- iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd);
-out:
- rcu_read_unlock();
-}
-
-int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
- int ret;
- struct iwl_d0i3_iter_data d0i3_iter_data = {
- .mvm = mvm,
- };
- struct iwl_wowlan_config_cmd wowlan_config_cmd = {
- .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
- IWL_WOWLAN_WAKEUP_BEACON_MISS |
- IWL_WOWLAN_WAKEUP_LINK_CHANGE |
- IWL_WOWLAN_WAKEUP_BCN_FILTERING),
- };
- struct iwl_d3_manager_config d3_cfg_cmd = {
- .min_sleep_time = cpu_to_le32(1000),
- .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR),
- };
-
- IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
-
- set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
-
- /*
- * iwl_mvm_ref_sync takes a reference before checking the flag.
- * so by checking there is no held reference we prevent a state
- * in which iwl_mvm_ref_sync continues successfully while we
- * configure the firmware to enter d0i3
- */
- if (iwl_mvm_ref_taken(mvm)) {
- IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n");
- clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
- wake_up(&mvm->d0i3_exit_waitq);
- return 1;
- }
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_enter_d0i3_iterator,
- &d0i3_iter_data);
- if (d0i3_iter_data.vif_count == 1) {
- mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id;
- mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading;
- } else {
- WARN_ON_ONCE(d0i3_iter_data.vif_count > 1);
- mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
- mvm->d0i3_offloading = false;
- }
-
- /* make sure we have no running tx while configuring the seqno */
- synchronize_net();
-
- /* configure wowlan configuration only if needed */
- if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) {
- iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd,
- &d0i3_iter_data);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
- sizeof(wowlan_config_cmd),
- &wowlan_config_cmd);
- if (ret)
- return ret;
- }
-
- return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD,
- flags | CMD_MAKE_TRANS_IDLE,
- sizeof(d3_cfg_cmd), &d3_cfg_cmd);
-}
-
-static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = _data;
- u32 flags = CMD_ASYNC | CMD_HIGH_PRIO;
-
- IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr);
- if (vif->type != NL80211_IFTYPE_STATION ||
- !vif->bss_conf.assoc)
- return;
-
- iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags);
-}
-
-struct iwl_mvm_wakeup_reason_iter_data {
- struct iwl_mvm *mvm;
- u32 wakeup_reasons;
-};
-
-static void iwl_mvm_d0i3_wakeup_reason_iter(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_wakeup_reason_iter_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
- data->mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) {
- if (data->wakeup_reasons &
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)
- iwl_mvm_connection_loss(data->mvm, vif, "D0i3");
- else
- ieee80211_beacon_loss(vif);
- }
-}
-
-void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq)
-{
- struct ieee80211_sta *sta = NULL;
- struct iwl_mvm_sta *mvm_ap_sta;
- int i;
- bool wake_queues = false;
-
- lockdep_assert_held(&mvm->mutex);
-
- spin_lock_bh(&mvm->d0i3_tx_lock);
-
- if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT)
- goto out;
-
- IWL_DEBUG_RPM(mvm, "re-enqueue packets\n");
-
- /* get the sta in order to update seq numbers and re-enqueue skbs */
- sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id],
- lockdep_is_held(&mvm->mutex));
-
- if (IS_ERR_OR_NULL(sta)) {
- sta = NULL;
- goto out;
- }
-
- if (mvm->d0i3_offloading && qos_seq) {
- /* update qos seq numbers if offloading was enabled */
- mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta);
- for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
- u16 seq = le16_to_cpu(qos_seq[i]);
- /* firmware stores last-used one, we store next one */
- seq += 0x10;
- mvm_ap_sta->tid_data[i].seq_number = seq;
- }
- }
-out:
- /* re-enqueue (or drop) all packets */
- while (!skb_queue_empty(&mvm->d0i3_tx)) {
- struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx);
-
- if (!sta || iwl_mvm_tx_skb(mvm, skb, sta))
- ieee80211_free_txskb(mvm->hw, skb);
-
- /* if the skb_queue is not empty, we need to wake queues */
- wake_queues = true;
- }
- clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
- wake_up(&mvm->d0i3_exit_waitq);
- mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
- if (wake_queues)
- ieee80211_wake_queues(mvm->hw);
-
- spin_unlock_bh(&mvm->d0i3_tx_lock);
-}
-
-static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
-{
- struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
- struct iwl_host_cmd get_status_cmd = {
- .id = WOWLAN_GET_STATUSES,
- .flags = CMD_HIGH_PRIO | CMD_WANT_SKB,
- };
- struct iwl_wowlan_status *status;
- int ret;
- u32 handled_reasons, wakeup_reasons = 0;
- __le16 *qos_seq = NULL;
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_send_cmd(mvm, &get_status_cmd);
- if (ret)
- goto out;
-
- if (!get_status_cmd.resp_pkt)
- goto out;
-
- status = (void *)get_status_cmd.resp_pkt->data;
- wakeup_reasons = le32_to_cpu(status->wakeup_reasons);
- qos_seq = status->qos_seq_ctr;
-
- IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons);
-
- handled_reasons = IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
- if (wakeup_reasons & handled_reasons) {
- struct iwl_mvm_wakeup_reason_iter_data data = {
- .mvm = mvm,
- .wakeup_reasons = wakeup_reasons,
- };
-
- ieee80211_iterate_active_interfaces(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_d0i3_wakeup_reason_iter, &data);
- }
-out:
- iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
-
- IWL_DEBUG_INFO(mvm, "d0i3 exit completed (wakeup reasons: 0x%x)\n",
- wakeup_reasons);
-
- /* qos_seq might point inside resp_pkt, so free it only now */
- if (get_status_cmd.resp_pkt)
- iwl_free_resp(&get_status_cmd);
-
- /* the FW might have updated the regdomain */
- iwl_mvm_update_changed_regdom(mvm);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK);
- mutex_unlock(&mvm->mutex);
-}
-
-int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm)
-{
- u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
- CMD_WAKE_UP_TRANS;
- int ret;
-
- IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
-
- mutex_lock(&mvm->d0i3_suspend_mutex);
- if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) {
- IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n");
- __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags);
- mutex_unlock(&mvm->d0i3_suspend_mutex);
- return 0;
- }
- mutex_unlock(&mvm->d0i3_suspend_mutex);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
- if (ret)
- goto out;
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_exit_d0i3_iterator,
- mvm);
-out:
- schedule_work(&mvm->d0i3_exit_work);
- return ret;
-}
-
-int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-
- iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK);
- return _iwl_mvm_exit_d0i3(mvm);
-}
-
-#define IWL_MVM_COMMON_OPS \
- /* these could be differentiated */ \
- .queue_full = iwl_mvm_stop_sw_queue, \
- .queue_not_full = iwl_mvm_wake_sw_queue, \
- .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \
- .free_skb = iwl_mvm_free_skb, \
- .nic_error = iwl_mvm_nic_error, \
- .cmd_queue_full = iwl_mvm_cmd_queue_full, \
- .nic_config = iwl_mvm_nic_config, \
- .enter_d0i3 = iwl_mvm_enter_d0i3, \
- .exit_d0i3 = iwl_mvm_exit_d0i3, \
- /* as we only register one, these MUST be common! */ \
- .start = iwl_op_mode_mvm_start, \
- .stop = iwl_op_mode_mvm_stop
-
-static const struct iwl_op_mode_ops iwl_mvm_ops = {
- IWL_MVM_COMMON_OPS,
- .rx = iwl_mvm_rx,
-};
-
-static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode,
- struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb,
- unsigned int queue)
-{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-
- iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
-}
-
-static const struct iwl_op_mode_ops iwl_mvm_ops_mq = {
- IWL_MVM_COMMON_OPS,
- .rx = iwl_mvm_rx_mq,
- .rx_rss = iwl_mvm_rx_mq_rss,
-};
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
deleted file mode 100644
index e68a475e3071..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <net/mac80211.h>
-#include "fw-api.h"
-#include "mvm.h"
-
-/* Maps the driver specific channel width definition to the fw values */
-u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
-{
- switch (chandef->width) {
- case NL80211_CHAN_WIDTH_20_NOHT:
- case NL80211_CHAN_WIDTH_20:
- return PHY_VHT_CHANNEL_MODE20;
- case NL80211_CHAN_WIDTH_40:
- return PHY_VHT_CHANNEL_MODE40;
- case NL80211_CHAN_WIDTH_80:
- return PHY_VHT_CHANNEL_MODE80;
- case NL80211_CHAN_WIDTH_160:
- return PHY_VHT_CHANNEL_MODE160;
- default:
- WARN(1, "Invalid channel width=%u", chandef->width);
- return PHY_VHT_CHANNEL_MODE20;
- }
-}
-
-/*
- * Maps the driver specific control channel position (relative to the center
- * freq) definitions to the the fw values
- */
-u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
-{
- switch (chandef->chan->center_freq - chandef->center_freq1) {
- case -70:
- return PHY_VHT_CTRL_POS_4_BELOW;
- case -50:
- return PHY_VHT_CTRL_POS_3_BELOW;
- case -30:
- return PHY_VHT_CTRL_POS_2_BELOW;
- case -10:
- return PHY_VHT_CTRL_POS_1_BELOW;
- case 10:
- return PHY_VHT_CTRL_POS_1_ABOVE;
- case 30:
- return PHY_VHT_CTRL_POS_2_ABOVE;
- case 50:
- return PHY_VHT_CTRL_POS_3_ABOVE;
- case 70:
- return PHY_VHT_CTRL_POS_4_ABOVE;
- default:
- WARN(1, "Invalid channel definition");
- case 0:
- /*
- * The FW is expected to check the control channel position only
- * when in HT/VHT and the channel width is not 20MHz. Return
- * this value as the default one.
- */
- return PHY_VHT_CTRL_POS_1_BELOW;
- }
-}
-
-/*
- * Construct the generic fields of the PHY context command
- */
-static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
- struct iwl_phy_context_cmd *cmd,
- u32 action, u32 apply_time)
-{
- memset(cmd, 0, sizeof(struct iwl_phy_context_cmd));
-
- cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
- ctxt->color));
- cmd->action = cpu_to_le32(action);
- cmd->apply_time = cpu_to_le32(apply_time);
-}
-
-/*
- * Add the phy configuration to the PHY context command
- */
-static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
- struct iwl_phy_context_cmd *cmd,
- struct cfg80211_chan_def *chandef,
- u8 chains_static, u8 chains_dynamic)
-{
- u8 active_cnt, idle_cnt;
-
- /* Set the channel info data */
- cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
- PHY_BAND_24 : PHY_BAND_5);
-
- cmd->ci.channel = chandef->chan->hw_value;
- cmd->ci.width = iwl_mvm_get_channel_width(chandef);
- cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
-
- /* Set rx the chains */
- idle_cnt = chains_static;
- active_cnt = chains_dynamic;
-
- /* In scenarios where we only ever use a single-stream rates,
- * i.e. legacy 11b/g/a associations, single-stream APs or even
- * static SMPS, enable both chains to get diversity, improving
- * the case where we're far enough from the AP that attenuation
- * between the two antennas is sufficiently different to impact
- * performance.
- */
- if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) {
- idle_cnt = 2;
- active_cnt = 2;
- }
-
- cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
- PHY_RX_CHAIN_VALID_POS);
- cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
- cmd->rxchain_info |= cpu_to_le32(active_cnt <<
- PHY_RX_CHAIN_MIMO_CNT_POS);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (unlikely(mvm->dbgfs_rx_phyinfo))
- cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
-#endif
-
- cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
-}
-
-/*
- * Send a command to apply the current phy configuration. The command is send
- * only if something in the configuration changed: in case that this is the
- * first time that the phy configuration is applied or in case that the phy
- * configuration changed from the previous apply.
- */
-static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
- struct iwl_mvm_phy_ctxt *ctxt,
- struct cfg80211_chan_def *chandef,
- u8 chains_static, u8 chains_dynamic,
- u32 action, u32 apply_time)
-{
- struct iwl_phy_context_cmd cmd;
- int ret;
-
- /* Set the command header fields */
- iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
-
- /* Set the command data */
- iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
- chains_static, chains_dynamic);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0,
- sizeof(struct iwl_phy_context_cmd),
- &cmd);
- if (ret)
- IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
- return ret;
-}
-
-/*
- * Send a command to add a PHY context based on the current HW configuration.
- */
-int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
- struct cfg80211_chan_def *chandef,
- u8 chains_static, u8 chains_dynamic)
-{
- WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
- ctxt->ref);
- lockdep_assert_held(&mvm->mutex);
-
- ctxt->channel = chandef->chan;
-
- return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
- chains_static, chains_dynamic,
- FW_CTXT_ACTION_ADD, 0);
-}
-
-/*
- * Update the number of references to the given PHY context. This is valid only
- * in case the PHY context was already created, i.e., its reference count > 0.
- */
-void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
-{
- lockdep_assert_held(&mvm->mutex);
- ctxt->ref++;
-}
-
-/*
- * Send a command to modify the PHY context based on the current HW
- * configuration. Note that the function does not check that the configuration
- * changed.
- */
-int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
- struct cfg80211_chan_def *chandef,
- u8 chains_static, u8 chains_dynamic)
-{
- lockdep_assert_held(&mvm->mutex);
-
- ctxt->channel = chandef->chan;
- return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
- chains_static, chains_dynamic,
- FW_CTXT_ACTION_MODIFY, 0);
-}
-
-void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
-{
- lockdep_assert_held(&mvm->mutex);
-
- if (WARN_ON_ONCE(!ctxt))
- return;
-
- ctxt->ref--;
-}
-
-static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- unsigned long *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (!mvmvif->phy_ctxt)
- return;
-
- if (vif->type == NL80211_IFTYPE_STATION ||
- vif->type == NL80211_IFTYPE_AP)
- __set_bit(mvmvif->phy_ctxt->id, data);
-}
-
-int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
-{
- unsigned long phy_ctxt_counter = 0;
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_binding_iterator,
- &phy_ctxt_counter);
-
- return hweight8(phy_ctxt_counter);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
deleted file mode 100644
index bed9696ee410..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ /dev/null
@@ -1,1040 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/etherdevice.h>
-
-#include <net/mac80211.h>
-
-#include "iwl-debug.h"
-#include "mvm.h"
-#include "iwl-modparams.h"
-#include "fw-api-power.h"
-
-#define POWER_KEEP_ALIVE_PERIOD_SEC 25
-
-static
-int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
- struct iwl_beacon_filter_cmd *cmd,
- u32 flags)
-{
- IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
- le32_to_cpu(cmd->ba_enable_beacon_abort));
- IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
- le32_to_cpu(cmd->ba_escape_timer));
- IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
- le32_to_cpu(cmd->bf_debug_flag));
- IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
- le32_to_cpu(cmd->bf_enable_beacon_filter));
- IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
- le32_to_cpu(cmd->bf_energy_delta));
- IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
- le32_to_cpu(cmd->bf_escape_timer));
- IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
- le32_to_cpu(cmd->bf_roaming_energy_delta));
- IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
- le32_to_cpu(cmd->bf_roaming_state));
- IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
- le32_to_cpu(cmd->bf_temp_threshold));
- IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
- le32_to_cpu(cmd->bf_temp_fast_filter));
- IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
- le32_to_cpu(cmd->bf_temp_slow_filter));
-
- return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags,
- sizeof(struct iwl_beacon_filter_cmd), cmd);
-}
-
-static
-void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_beacon_filter_cmd *cmd,
- bool d0i3)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (vif->bss_conf.cqm_rssi_thold && !d0i3) {
- cmd->bf_energy_delta =
- cpu_to_le32(vif->bss_conf.cqm_rssi_hyst);
- /* fw uses an absolute value for this */
- cmd->bf_roaming_state =
- cpu_to_le32(-vif->bss_conf.cqm_rssi_thold);
- }
- cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled);
-}
-
-static void iwl_mvm_power_log(struct iwl_mvm *mvm,
- struct iwl_mac_power_cmd *cmd)
-{
- IWL_DEBUG_POWER(mvm,
- "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
- cmd->id_and_color, iwlmvm_mod_params.power_scheme,
- le16_to_cpu(cmd->flags));
- IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
- le16_to_cpu(cmd->keep_alive_seconds));
-
- if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) {
- IWL_DEBUG_POWER(mvm, "Disable power management\n");
- return;
- }
-
- IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
- le32_to_cpu(cmd->rx_data_timeout));
- IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
- le32_to_cpu(cmd->tx_data_timeout));
- if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
- IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
- cmd->skip_dtim_periods);
- if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
- IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
- cmd->lprx_rssi_threshold);
- if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
- IWL_DEBUG_POWER(mvm, "uAPSD enabled\n");
- IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
- le32_to_cpu(cmd->rx_data_timeout_uapsd));
- IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n",
- le32_to_cpu(cmd->tx_data_timeout_uapsd));
- IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid);
- IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags);
- IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp);
- }
-}
-
-static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mac_power_cmd *cmd)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- enum ieee80211_ac_numbers ac;
- bool tid_found = false;
-
- for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
- if (!mvmvif->queue_params[ac].uapsd)
- continue;
-
- if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
- cmd->flags |=
- cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
-
- cmd->uapsd_ac_flags |= BIT(ac);
-
- /* QNDP TID - the highest TID with no admission control */
- if (!tid_found && !mvmvif->queue_params[ac].acm) {
- tid_found = true;
- switch (ac) {
- case IEEE80211_AC_VO:
- cmd->qndp_tid = 6;
- break;
- case IEEE80211_AC_VI:
- cmd->qndp_tid = 5;
- break;
- case IEEE80211_AC_BE:
- cmd->qndp_tid = 0;
- break;
- case IEEE80211_AC_BK:
- cmd->qndp_tid = 1;
- break;
- }
- }
- }
-
- if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- /* set advanced pm flag with no uapsd ACs to enable ps-poll */
- if (mvmvif->dbgfs_pm.use_ps_poll)
- cmd->flags |=
- cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
-#endif
- return;
- }
-
- cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
-
- if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
- BIT(IEEE80211_AC_VI) |
- BIT(IEEE80211_AC_BE) |
- BIT(IEEE80211_AC_BK))) {
- cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
- cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
- cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
- cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
- cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
- }
-
- cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
-
- if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
- cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
- cmd->rx_data_timeout_uapsd =
- cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
- cmd->tx_data_timeout_uapsd =
- cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
- } else {
- cmd->rx_data_timeout_uapsd =
- cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
- cmd->tx_data_timeout_uapsd =
- cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
- }
-
- if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
- cmd->heavy_tx_thld_packets =
- IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
- cmd->heavy_rx_thld_packets =
- IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
- } else {
- cmd->heavy_tx_thld_packets =
- IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
- cmd->heavy_rx_thld_packets =
- IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
- }
- cmd->heavy_tx_thld_percentage =
- IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
- cmd->heavy_rx_thld_percentage =
- IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
-}
-
-static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
- ETH_ALEN))
- return false;
-
- if (vif->p2p &&
- !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
- return false;
- /*
- * Avoid using uAPSD if P2P client is associated to GO that uses
- * opportunistic power save. This is due to current FW limitation.
- */
- if (vif->p2p &&
- (vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
- IEEE80211_P2P_OPPPS_ENABLE_BIT))
- return false;
-
- /*
- * Avoid using uAPSD if client is in DCM -
- * low latency issue in Miracast
- */
- if (iwl_mvm_phy_ctx_count(mvm) >= 2)
- return false;
-
- return true;
-}
-
-static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)
-{
- struct ieee80211_chanctx_conf *chanctx_conf;
- struct ieee80211_channel *chan;
- bool radar_detect = false;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
- WARN_ON(!chanctx_conf);
- if (chanctx_conf) {
- chan = chanctx_conf->def.chan;
- radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
- }
- rcu_read_unlock();
-
- return radar_detect;
-}
-
-static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mac_power_cmd *cmd,
- bool host_awake)
-{
- int dtimper = vif->bss_conf.dtim_period ?: 1;
- int skip;
-
- /* disable, in case we're supposed to override */
- cmd->skip_dtim_periods = 0;
- cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-
- if (iwl_mvm_power_is_radar(vif))
- return;
-
- if (dtimper >= 10)
- return;
-
- /* TODO: check that multicast wake lock is off */
-
- if (host_awake) {
- if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP)
- return;
- skip = 2;
- } else {
- int dtimper_tu = dtimper * vif->bss_conf.beacon_int;
-
- if (WARN_ON(!dtimper_tu))
- return;
- /* configure skip over dtim up to 306TU - 314 msec */
- skip = max_t(u8, 1, 306 / dtimper_tu);
- }
-
- /* the firmware really expects "look at every X DTIMs", so add 1 */
- cmd->skip_dtim_periods = 1 + skip;
- cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-}
-
-static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mac_power_cmd *cmd,
- bool host_awake)
-{
- int dtimper, bi;
- int keep_alive;
- struct iwl_mvm_vif *mvmvif __maybe_unused =
- iwl_mvm_vif_from_mac80211(vif);
-
- cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color));
- dtimper = vif->bss_conf.dtim_period;
- bi = vif->bss_conf.beacon_int;
-
- /*
- * Regardless of power management state the driver must set
- * keep alive period. FW will use it for sending keep alive NDPs
- * immediately after association. Check that keep alive period
- * is at least 3 * DTIM
- */
- keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
- USEC_PER_SEC);
- keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
- cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
-
- if (mvm->ps_disabled)
- return;
-
- cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-
- if (!vif->bss_conf.ps || !mvmvif->pm_enabled)
- return;
-
- if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
- (!fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) ||
- !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE))
- return;
-
- cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
-
- if (vif->bss_conf.beacon_rate &&
- (vif->bss_conf.beacon_rate->bitrate == 10 ||
- vif->bss_conf.beacon_rate->bitrate == 60)) {
- cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
- cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
- }
-
- iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, host_awake);
-
- if (!host_awake) {
- cmd->rx_data_timeout =
- cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
- cmd->tx_data_timeout =
- cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
- } else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
- fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) {
- cmd->tx_data_timeout =
- cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT);
- cmd->rx_data_timeout =
- cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT);
- } else {
- cmd->rx_data_timeout =
- cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
- cmd->tx_data_timeout =
- cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
- }
-
- if (iwl_mvm_power_allow_uapsd(mvm, vif))
- iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
- cmd->keep_alive_seconds =
- cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
- if (mvmvif->dbgfs_pm.skip_over_dtim)
- cmd->flags |=
- cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
- else
- cmd->flags &=
- cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
- }
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
- cmd->rx_data_timeout =
- cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
- cmd->tx_data_timeout =
- cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
- cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
- if (mvmvif->dbgfs_pm.lprx_ena)
- cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
- else
- cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
- }
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
- cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {
- if (mvmvif->dbgfs_pm.snooze_ena)
- cmd->flags |=
- cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
- else
- cmd->flags &=
- cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
- }
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
- u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
- if (mvmvif->dbgfs_pm.uapsd_misbehaving)
- cmd->flags |= cpu_to_le16(flag);
- else
- cmd->flags &= cpu_to_le16(flag);
- }
-#endif /* CONFIG_IWLWIFI_DEBUGFS */
-}
-
-static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_mac_power_cmd cmd = {};
-
- iwl_mvm_power_build_cmd(mvm, vif, &cmd,
- mvm->cur_ucode != IWL_UCODE_WOWLAN);
- iwl_mvm_power_log(mvm, &cmd);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
-#endif
-
- return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0,
- sizeof(cmd), &cmd);
-}
-
-int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
-{
- struct iwl_device_power_cmd cmd = {
- .flags = 0,
- };
-
- if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
- mvm->ps_disabled = true;
-
- if (!mvm->ps_disabled)
- cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 :
- mvm->disable_power_off)
- cmd.flags &=
- cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
-#endif
- IWL_DEBUG_POWER(mvm,
- "Sending device power command with flags = 0x%X\n",
- cmd.flags);
-
- return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd),
- &cmd);
-}
-
-void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid,
- ETH_ALEN))
- eth_zero_addr(mvmvif->uapsd_misbehaving_bssid);
-}
-
-static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- u8 *ap_sta_id = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- /* The ap_sta_id is not expected to change during current association
- * so no explicit protection is needed
- */
- if (mvmvif->ap_sta_id == *ap_sta_id)
- memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
- ETH_ALEN);
-}
-
-void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
- u8 ap_sta_id = le32_to_cpu(notif->sta_id);
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id);
-}
-
-struct iwl_power_vifs {
- struct iwl_mvm *mvm;
- struct ieee80211_vif *bf_vif;
- struct ieee80211_vif *bss_vif;
- struct ieee80211_vif *p2p_vif;
- struct ieee80211_vif *ap_vif;
- struct ieee80211_vif *monitor_vif;
- bool p2p_active;
- bool bss_active;
- bool ap_active;
- bool monitor_active;
-};
-
-static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- mvmvif->pm_enabled = false;
-}
-
-static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- bool *disable_ps = _data;
-
- if (mvmvif->phy_ctxt)
- if (mvmvif->phy_ctxt->id < MAX_PHYS)
- *disable_ps |= mvmvif->ps_disabled;
-}
-
-static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_power_vifs *power_iterator = _data;
-
- switch (ieee80211_vif_type_p2p(vif)) {
- case NL80211_IFTYPE_P2P_DEVICE:
- break;
-
- case NL80211_IFTYPE_P2P_GO:
- case NL80211_IFTYPE_AP:
- /* only a single MAC of the same type */
- WARN_ON(power_iterator->ap_vif);
- power_iterator->ap_vif = vif;
- if (mvmvif->phy_ctxt)
- if (mvmvif->phy_ctxt->id < MAX_PHYS)
- power_iterator->ap_active = true;
- break;
-
- case NL80211_IFTYPE_MONITOR:
- /* only a single MAC of the same type */
- WARN_ON(power_iterator->monitor_vif);
- power_iterator->monitor_vif = vif;
- if (mvmvif->phy_ctxt)
- if (mvmvif->phy_ctxt->id < MAX_PHYS)
- power_iterator->monitor_active = true;
- break;
-
- case NL80211_IFTYPE_P2P_CLIENT:
- /* only a single MAC of the same type */
- WARN_ON(power_iterator->p2p_vif);
- power_iterator->p2p_vif = vif;
- if (mvmvif->phy_ctxt)
- if (mvmvif->phy_ctxt->id < MAX_PHYS)
- power_iterator->p2p_active = true;
- break;
-
- case NL80211_IFTYPE_STATION:
- /* only a single MAC of the same type */
- WARN_ON(power_iterator->bss_vif);
- power_iterator->bss_vif = vif;
- if (mvmvif->phy_ctxt)
- if (mvmvif->phy_ctxt->id < MAX_PHYS)
- power_iterator->bss_active = true;
-
- if (mvmvif->bf_data.bf_enabled &&
- !WARN_ON(power_iterator->bf_vif))
- power_iterator->bf_vif = vif;
-
- break;
-
- default:
- break;
- }
-}
-
-static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
- struct iwl_power_vifs *vifs)
-{
- struct iwl_mvm_vif *bss_mvmvif = NULL;
- struct iwl_mvm_vif *p2p_mvmvif = NULL;
- struct iwl_mvm_vif *ap_mvmvif = NULL;
- bool client_same_channel = false;
- bool ap_same_channel = false;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* set pm_enable to false */
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_disable_pm_iterator,
- NULL);
-
- if (vifs->bss_vif)
- bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif);
-
- if (vifs->p2p_vif)
- p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif);
-
- if (vifs->ap_vif)
- ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
-
- /* don't allow PM if any TDLS stations exist */
- if (iwl_mvm_tdls_sta_count(mvm, NULL))
- return;
-
- /* enable PM on bss if bss stand alone */
- if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
- bss_mvmvif->pm_enabled = true;
- return;
- }
-
- /* enable PM on p2p if p2p stand alone */
- if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
- p2p_mvmvif->pm_enabled = true;
- return;
- }
-
- if (vifs->bss_active && vifs->p2p_active)
- client_same_channel = (bss_mvmvif->phy_ctxt->id ==
- p2p_mvmvif->phy_ctxt->id);
- if (vifs->bss_active && vifs->ap_active)
- ap_same_channel = (bss_mvmvif->phy_ctxt->id ==
- ap_mvmvif->phy_ctxt->id);
-
- /* clients are not stand alone: enable PM if DCM */
- if (!(client_same_channel || ap_same_channel) &&
- (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
- if (vifs->bss_active)
- bss_mvmvif->pm_enabled = true;
- if (vifs->p2p_active &&
- (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM))
- p2p_mvmvif->pm_enabled = true;
- return;
- }
-
- /*
- * There is only one channel in the system and there are only
- * bss and p2p clients that share it
- */
- if (client_same_channel && !vifs->ap_active &&
- (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) {
- /* share same channel*/
- bss_mvmvif->pm_enabled = true;
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
- p2p_mvmvif->pm_enabled = true;
- }
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif, char *buf,
- int bufsz)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_power_cmd cmd = {};
- int pos = 0;
-
- mutex_lock(&mvm->mutex);
- memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
- mutex_unlock(&mvm->mutex);
-
- pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
- iwlmvm_mod_params.power_scheme);
- pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
- le16_to_cpu(cmd.flags));
- pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
- le16_to_cpu(cmd.keep_alive_seconds));
-
- if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)))
- return pos;
-
- pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
- (cmd.flags &
- cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0);
- pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
- cmd.skip_dtim_periods);
- if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
- pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
- le32_to_cpu(cmd.rx_data_timeout));
- pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
- le32_to_cpu(cmd.tx_data_timeout));
- }
- if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
- pos += scnprintf(buf+pos, bufsz-pos,
- "lprx_rssi_threshold = %d\n",
- cmd.lprx_rssi_threshold);
-
- if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
- return pos;
-
- pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n",
- le32_to_cpu(cmd.rx_data_timeout_uapsd));
- pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n",
- le32_to_cpu(cmd.tx_data_timeout_uapsd));
- pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid);
- pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n",
- cmd.uapsd_ac_flags);
- pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n",
- cmd.uapsd_max_sp);
- pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n",
- cmd.heavy_tx_thld_packets);
- pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n",
- cmd.heavy_rx_thld_packets);
- pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n",
- cmd.heavy_tx_thld_percentage);
- pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n",
- cmd.heavy_rx_thld_percentage);
- pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n",
- (cmd.flags &
- cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ?
- 1 : 0);
-
- if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)))
- return pos;
-
- pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n",
- cmd.snooze_interval);
- pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n",
- cmd.snooze_window);
-
- return pos;
-}
-
-void
-iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
- struct iwl_beacon_filter_cmd *cmd)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
-
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
- cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
- cmd->bf_roaming_energy_delta =
- cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
- cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD)
- cmd->bf_temp_threshold =
- cpu_to_le32(dbgfs_bf->bf_temp_threshold);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER)
- cmd->bf_temp_fast_filter =
- cpu_to_le32(dbgfs_bf->bf_temp_fast_filter);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER)
- cmd->bf_temp_slow_filter =
- cpu_to_le32(dbgfs_bf->bf_temp_slow_filter);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
- cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
- cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
- cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
- if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
- cmd->ba_enable_beacon_abort =
- cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort);
-}
-#endif
-
-static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_beacon_filter_cmd *cmd,
- u32 cmd_flags,
- bool d0i3)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period ||
- vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return 0;
-
- iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd, d0i3);
- if (!d0i3)
- iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);
- ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags);
-
- /* don't change bf_enabled in case of temporary d0i3 configuration */
- if (!ret && !d0i3)
- mvmvif->bf_data.bf_enabled = true;
-
- return ret;
-}
-
-int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 flags)
-{
- struct iwl_beacon_filter_cmd cmd = {
- IWL_BF_CMD_CONFIG_DEFAULTS,
- .bf_enable_beacon_filter = cpu_to_le32(1),
- };
-
- return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
-}
-
-static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool enable)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_beacon_filter_cmd cmd = {
- IWL_BF_CMD_CONFIG_DEFAULTS,
- .bf_enable_beacon_filter = cpu_to_le32(1),
- };
-
- if (!mvmvif->bf_data.bf_enabled)
- return 0;
-
- if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
- cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
-
- mvmvif->bf_data.ba_enabled = enable;
- return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false);
-}
-
-int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 flags)
-{
- struct iwl_beacon_filter_cmd cmd = {};
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return 0;
-
- ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
-
- if (!ret)
- mvmvif->bf_data.bf_enabled = false;
-
- return ret;
-}
-
-static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm)
-{
- bool disable_ps;
- int ret;
-
- /* disable PS if CAM */
- disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
- /* ...or if any of the vifs require PS to be off */
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_ps_disabled_iterator,
- &disable_ps);
-
- /* update device power state if it has changed */
- if (mvm->ps_disabled != disable_ps) {
- bool old_ps_disabled = mvm->ps_disabled;
-
- mvm->ps_disabled = disable_ps;
- ret = iwl_mvm_power_update_device(mvm);
- if (ret) {
- mvm->ps_disabled = old_ps_disabled;
- return ret;
- }
- }
-
- return 0;
-}
-
-static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm,
- struct iwl_power_vifs *vifs)
-{
- struct iwl_mvm_vif *mvmvif;
- bool ba_enable;
-
- if (!vifs->bf_vif)
- return 0;
-
- mvmvif = iwl_mvm_vif_from_mac80211(vifs->bf_vif);
-
- ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled ||
- !vifs->bf_vif->bss_conf.ps ||
- iwl_mvm_vif_low_latency(mvmvif));
-
- return iwl_mvm_update_beacon_abort(mvm, vifs->bf_vif, ba_enable);
-}
-
-int iwl_mvm_power_update_ps(struct iwl_mvm *mvm)
-{
- struct iwl_power_vifs vifs = {
- .mvm = mvm,
- };
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* get vifs info */
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_get_vifs_iterator, &vifs);
-
- ret = iwl_mvm_power_set_ps(mvm);
- if (ret)
- return ret;
-
- return iwl_mvm_power_set_ba(mvm, &vifs);
-}
-
-int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
-{
- struct iwl_power_vifs vifs = {
- .mvm = mvm,
- };
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* get vifs info */
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_get_vifs_iterator, &vifs);
-
- iwl_mvm_power_set_pm(mvm, &vifs);
-
- ret = iwl_mvm_power_set_ps(mvm);
- if (ret)
- return ret;
-
- if (vifs.bss_vif) {
- ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif);
- if (ret)
- return ret;
- }
-
- if (vifs.p2p_vif) {
- ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif);
- if (ret)
- return ret;
- }
-
- return iwl_mvm_power_set_ba(mvm, &vifs);
-}
-
-int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool enable, u32 flags)
-{
- int ret;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_power_cmd cmd = {};
-
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return 0;
-
- if (!vif->bss_conf.assoc)
- return 0;
-
- iwl_mvm_power_build_cmd(mvm, vif, &cmd, !enable);
-
- iwl_mvm_power_log(mvm, &cmd);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd));
-#endif
- ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags,
- sizeof(cmd), &cmd);
- if (ret)
- return ret;
-
- /* configure beacon filtering */
- if (mvmvif != mvm->bf_allowed_vif)
- return 0;
-
- if (enable) {
- struct iwl_beacon_filter_cmd cmd_bf = {
- IWL_BF_CMD_CONFIG_D0I3,
- .bf_enable_beacon_filter = cpu_to_le32(1),
- };
- ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
- flags, true);
- } else {
- if (mvmvif->bf_data.bf_enabled)
- ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags);
- else
- ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags);
- }
-
- return ret;
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
deleted file mode 100644
index 509a66d05245..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/quota.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <net/mac80211.h>
-#include "fw-api.h"
-#include "mvm.h"
-
-#define QUOTA_100 IWL_MVM_MAX_QUOTA
-#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
-
-struct iwl_mvm_quota_iterator_data {
- int n_interfaces[MAX_BINDINGS];
- int colors[MAX_BINDINGS];
- int low_latency[MAX_BINDINGS];
- int n_low_latency_bindings;
- struct ieee80211_vif *disabled_vif;
-};
-
-static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_quota_iterator_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- u16 id;
-
- /* skip disabled interfaces here immediately */
- if (vif == data->disabled_vif)
- return;
-
- if (!mvmvif->phy_ctxt)
- return;
-
- /* currently, PHY ID == binding ID */
- id = mvmvif->phy_ctxt->id;
-
- /* need at least one binding per PHY */
- BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);
-
- if (WARN_ON_ONCE(id >= MAX_BINDINGS))
- return;
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- if (vif->bss_conf.assoc)
- break;
- return;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_ADHOC:
- if (mvmvif->ap_ibss_active)
- break;
- return;
- case NL80211_IFTYPE_MONITOR:
- if (mvmvif->monitor_active)
- break;
- return;
- case NL80211_IFTYPE_P2P_DEVICE:
- return;
- default:
- WARN_ON_ONCE(1);
- return;
- }
-
- if (data->colors[id] < 0)
- data->colors[id] = mvmvif->phy_ctxt->color;
- else
- WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
-
- data->n_interfaces[id]++;
-
- if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
- data->n_low_latency_bindings++;
- data->low_latency[id] = true;
- }
-}
-
-static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
- struct iwl_time_quota_cmd *cmd)
-{
-#ifdef CONFIG_NL80211_TESTMODE
- struct iwl_mvm_vif *mvmvif;
- int i, phy_id = -1, beacon_int = 0;
-
- if (!mvm->noa_duration || !mvm->noa_vif)
- return;
-
- mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);
- if (!mvmvif->ap_ibss_active)
- return;
-
- phy_id = mvmvif->phy_ctxt->id;
- beacon_int = mvm->noa_vif->bss_conf.beacon_int;
-
- for (i = 0; i < MAX_BINDINGS; i++) {
- u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color);
- u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;
- u32 quota = le32_to_cpu(cmd->quotas[i].quota);
-
- if (id != phy_id)
- continue;
-
- quota *= (beacon_int - mvm->noa_duration);
- quota /= beacon_int;
-
- IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n",
- le32_to_cpu(cmd->quotas[i].quota), quota);
-
- cmd->quotas[i].quota = cpu_to_le32(quota);
- }
-#endif
-}
-
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
- bool force_update,
- struct ieee80211_vif *disabled_vif)
-{
- struct iwl_time_quota_cmd cmd = {};
- int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat;
- struct iwl_mvm_quota_iterator_data data = {
- .n_interfaces = {},
- .colors = { -1, -1, -1, -1 },
- .disabled_vif = disabled_vif,
- };
- struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd;
- bool send = false;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* update all upon completion */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- return 0;
-
- /* iterator data above must match */
- BUILD_BUG_ON(MAX_BINDINGS != 4);
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_quota_iterator, &data);
-
- /*
- * The FW's scheduling session consists of
- * IWL_MVM_MAX_QUOTA fragments. Divide these fragments
- * equally between all the bindings that require quota
- */
- num_active_macs = 0;
- for (i = 0; i < MAX_BINDINGS; i++) {
- cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
- num_active_macs += data.n_interfaces[i];
- }
-
- n_non_lowlat = num_active_macs;
-
- if (data.n_low_latency_bindings == 1) {
- for (i = 0; i < MAX_BINDINGS; i++) {
- if (data.low_latency[i]) {
- n_non_lowlat -= data.n_interfaces[i];
- break;
- }
- }
- }
-
- if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
- /*
- * Reserve quota for the low latency binding in case that
- * there are several data bindings but only a single
- * low latency one. Split the rest of the quota equally
- * between the other data interfaces.
- */
- quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
- quota_rem = QUOTA_100 - n_non_lowlat * quota -
- QUOTA_LOWLAT_MIN;
- IWL_DEBUG_QUOTA(mvm,
- "quota: low-latency binding active, remaining quota per other binding: %d\n",
- quota);
- } else if (num_active_macs) {
- /*
- * There are 0 or more than 1 low latency bindings, or all the
- * data interfaces belong to the single low latency binding.
- * Split the quota equally between the data interfaces.
- */
- quota = QUOTA_100 / num_active_macs;
- quota_rem = QUOTA_100 % num_active_macs;
- IWL_DEBUG_QUOTA(mvm,
- "quota: splitting evenly per binding: %d\n",
- quota);
- } else {
- /* values don't really matter - won't be used */
- quota = 0;
- quota_rem = 0;
- }
-
- for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
- if (data.colors[i] < 0)
- continue;
-
- cmd.quotas[idx].id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
-
- if (data.n_interfaces[i] <= 0)
- cmd.quotas[idx].quota = cpu_to_le32(0);
- else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
- data.low_latency[i])
- /*
- * There is more than one binding, but only one of the
- * bindings is in low latency. For this case, allocate
- * the minimal required quota for the low latency
- * binding.
- */
- cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
- else
- cmd.quotas[idx].quota =
- cpu_to_le32(quota * data.n_interfaces[i]);
-
- WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100,
- "Binding=%d, quota=%u > max=%u\n",
- idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100);
-
- cmd.quotas[idx].max_duration = cpu_to_le32(0);
-
- idx++;
- }
-
- /* Give the remainder of the session to the first data binding */
- for (i = 0; i < MAX_BINDINGS; i++) {
- if (le32_to_cpu(cmd.quotas[i].quota) != 0) {
- le32_add_cpu(&cmd.quotas[i].quota, quota_rem);
- IWL_DEBUG_QUOTA(mvm,
- "quota: giving remainder of %d to binding %d\n",
- quota_rem, i);
- break;
- }
- }
-
- iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
-
- /* check that we have non-zero quota for all valid bindings */
- for (i = 0; i < MAX_BINDINGS; i++) {
- if (cmd.quotas[i].id_and_color != last->quotas[i].id_and_color)
- send = true;
- if (cmd.quotas[i].max_duration != last->quotas[i].max_duration)
- send = true;
- if (abs((int)le32_to_cpu(cmd.quotas[i].quota) -
- (int)le32_to_cpu(last->quotas[i].quota))
- > IWL_MVM_QUOTA_THRESHOLD)
- send = true;
- if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID))
- continue;
- WARN_ONCE(cmd.quotas[i].quota == 0,
- "zero quota on binding %d\n", i);
- }
-
- if (!send && !force_update) {
- /* don't send a practically unchanged command, the firmware has
- * to re-initialize a lot of state and that can have an adverse
- * impact on it
- */
- return 0;
- }
-
- err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd);
-
- if (err)
- IWL_ERR(mvm, "Failed to send quota: %d\n", err);
- else
- mvm->last_quota_cmd = cmd;
- return err;
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
deleted file mode 100644
index d1ad10391b47..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ /dev/null
@@ -1,3983 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-
-#include <linux/workqueue.h>
-#include "rs.h"
-#include "fw-api.h"
-#include "sta.h"
-#include "iwl-op-mode.h"
-#include "mvm.h"
-#include "debugfs.h"
-
-#define RS_NAME "iwl-mvm-rs"
-
-#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */
-
-/* Calculations of success ratio are done in fixed point where 12800 is 100%.
- * Use this macro when dealing with thresholds consts set as a percentage
- */
-#define RS_PERCENT(x) (128 * x)
-
-static u8 rs_ht_to_legacy[] = {
- [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX,
- [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX,
- [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX,
- [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX,
- [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX,
- [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX,
- [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX,
- [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX,
- [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX,
- [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX,
-};
-
-static const u8 ant_toggle_lookup[] = {
- [ANT_NONE] = ANT_NONE,
- [ANT_A] = ANT_B,
- [ANT_B] = ANT_C,
- [ANT_AB] = ANT_BC,
- [ANT_C] = ANT_A,
- [ANT_AC] = ANT_AB,
- [ANT_BC] = ANT_AC,
- [ANT_ABC] = ANT_ABC,
-};
-
-#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \
- [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
- IWL_RATE_HT_SISO_MCS_##s##_PLCP, \
- IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \
- IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \
- IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\
- IWL_RATE_##rp##M_INDEX, \
- IWL_RATE_##rn##M_INDEX }
-
-#define IWL_DECLARE_MCS_RATE(s) \
- [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \
- IWL_RATE_HT_SISO_MCS_##s##_PLCP, \
- IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \
- IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \
- IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \
- IWL_RATE_INVM_INDEX, \
- IWL_RATE_INVM_INDEX }
-
-/*
- * Parameter order:
- * rate, ht rate, prev rate, next rate
- *
- * If there isn't a valid next or previous rate then INV is used which
- * maps to IWL_RATE_INVALID
- *
- */
-static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
- IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */
- IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */
- IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */
- IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */
- IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */
- IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */
- IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */
- IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */
- IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */
- IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */
- IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */
- IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */
- IWL_DECLARE_MCS_RATE(7), /* MCS 7 */
- IWL_DECLARE_MCS_RATE(8), /* MCS 8 */
- IWL_DECLARE_MCS_RATE(9), /* MCS 9 */
-};
-
-enum rs_action {
- RS_ACTION_STAY = 0,
- RS_ACTION_DOWNSCALE = -1,
- RS_ACTION_UPSCALE = 1,
-};
-
-enum rs_column_mode {
- RS_INVALID = 0,
- RS_LEGACY,
- RS_SISO,
- RS_MIMO2,
-};
-
-#define MAX_NEXT_COLUMNS 7
-#define MAX_COLUMN_CHECKS 3
-
-struct rs_tx_column;
-
-typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct rs_rate *rate,
- const struct rs_tx_column *next_col);
-
-struct rs_tx_column {
- enum rs_column_mode mode;
- u8 ant;
- bool sgi;
- enum rs_column next_columns[MAX_NEXT_COLUMNS];
- allow_column_func_t checks[MAX_COLUMN_CHECKS];
-};
-
-static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct rs_rate *rate,
- const struct rs_tx_column *next_col)
-{
- return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant);
-}
-
-static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct rs_rate *rate,
- const struct rs_tx_column *next_col)
-{
- struct iwl_mvm_sta *mvmsta;
- struct iwl_mvm_vif *mvmvif;
-
- if (!sta->ht_cap.ht_supported)
- return false;
-
- if (sta->smps_mode == IEEE80211_SMPS_STATIC)
- return false;
-
- if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2)
- return false;
-
- if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
- return false;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
-
- if (mvm->nvm_data->sku_cap_mimo_disabled)
- return false;
-
- return true;
-}
-
-static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct rs_rate *rate,
- const struct rs_tx_column *next_col)
-{
- if (!sta->ht_cap.ht_supported)
- return false;
-
- return true;
-}
-
-static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct rs_rate *rate,
- const struct rs_tx_column *next_col)
-{
- struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
-
- if (is_ht20(rate) && (ht_cap->cap &
- IEEE80211_HT_CAP_SGI_20))
- return true;
- if (is_ht40(rate) && (ht_cap->cap &
- IEEE80211_HT_CAP_SGI_40))
- return true;
- if (is_ht80(rate) && (vht_cap->cap &
- IEEE80211_VHT_CAP_SHORT_GI_80))
- return true;
-
- return false;
-}
-
-static const struct rs_tx_column rs_tx_columns[] = {
- [RS_COLUMN_LEGACY_ANT_A] = {
- .mode = RS_LEGACY,
- .ant = ANT_A,
- .next_columns = {
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_SISO_ANT_A,
- RS_COLUMN_MIMO2,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_ant_allow,
- },
- },
- [RS_COLUMN_LEGACY_ANT_B] = {
- .mode = RS_LEGACY,
- .ant = ANT_B,
- .next_columns = {
- RS_COLUMN_LEGACY_ANT_A,
- RS_COLUMN_SISO_ANT_B,
- RS_COLUMN_MIMO2,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_ant_allow,
- },
- },
- [RS_COLUMN_SISO_ANT_A] = {
- .mode = RS_SISO,
- .ant = ANT_A,
- .next_columns = {
- RS_COLUMN_SISO_ANT_B,
- RS_COLUMN_MIMO2,
- RS_COLUMN_SISO_ANT_A_SGI,
- RS_COLUMN_LEGACY_ANT_A,
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_siso_allow,
- rs_ant_allow,
- },
- },
- [RS_COLUMN_SISO_ANT_B] = {
- .mode = RS_SISO,
- .ant = ANT_B,
- .next_columns = {
- RS_COLUMN_SISO_ANT_A,
- RS_COLUMN_MIMO2,
- RS_COLUMN_SISO_ANT_B_SGI,
- RS_COLUMN_LEGACY_ANT_A,
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_siso_allow,
- rs_ant_allow,
- },
- },
- [RS_COLUMN_SISO_ANT_A_SGI] = {
- .mode = RS_SISO,
- .ant = ANT_A,
- .sgi = true,
- .next_columns = {
- RS_COLUMN_SISO_ANT_B_SGI,
- RS_COLUMN_MIMO2_SGI,
- RS_COLUMN_SISO_ANT_A,
- RS_COLUMN_LEGACY_ANT_A,
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_siso_allow,
- rs_ant_allow,
- rs_sgi_allow,
- },
- },
- [RS_COLUMN_SISO_ANT_B_SGI] = {
- .mode = RS_SISO,
- .ant = ANT_B,
- .sgi = true,
- .next_columns = {
- RS_COLUMN_SISO_ANT_A_SGI,
- RS_COLUMN_MIMO2_SGI,
- RS_COLUMN_SISO_ANT_B,
- RS_COLUMN_LEGACY_ANT_A,
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_siso_allow,
- rs_ant_allow,
- rs_sgi_allow,
- },
- },
- [RS_COLUMN_MIMO2] = {
- .mode = RS_MIMO2,
- .ant = ANT_AB,
- .next_columns = {
- RS_COLUMN_SISO_ANT_A,
- RS_COLUMN_MIMO2_SGI,
- RS_COLUMN_LEGACY_ANT_A,
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_mimo_allow,
- },
- },
- [RS_COLUMN_MIMO2_SGI] = {
- .mode = RS_MIMO2,
- .ant = ANT_AB,
- .sgi = true,
- .next_columns = {
- RS_COLUMN_SISO_ANT_A_SGI,
- RS_COLUMN_MIMO2,
- RS_COLUMN_LEGACY_ANT_A,
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- },
- .checks = {
- rs_mimo_allow,
- rs_sgi_allow,
- },
- },
-};
-
-static inline u8 rs_extract_rate(u32 rate_n_flags)
-{
- /* also works for HT because bits 7:6 are zero there */
- return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK);
-}
-
-static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
-{
- int idx = 0;
-
- if (rate_n_flags & RATE_MCS_HT_MSK) {
- idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK;
- idx += IWL_RATE_MCS_0_INDEX;
-
- /* skip 9M not supported in HT*/
- if (idx >= IWL_RATE_9M_INDEX)
- idx += 1;
- if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE))
- return idx;
- } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
- idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
- idx += IWL_RATE_MCS_0_INDEX;
-
- /* skip 9M not supported in VHT*/
- if (idx >= IWL_RATE_9M_INDEX)
- idx++;
- if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE))
- return idx;
- } else {
- /* legacy rate format, search for match in table */
-
- u8 legacy_rate = rs_extract_rate(rate_n_flags);
- for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
- if (iwl_rates[idx].plcp == legacy_rate)
- return idx;
- }
-
- return IWL_RATE_INVALID;
-}
-
-static void rs_rate_scale_perform(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- int tid);
-static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- const struct rs_rate *initial_rate);
-static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
-
-/**
- * The following tables contain the expected throughput metrics for all rates
- *
- * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
- *
- * where invalid entries are zeros.
- *
- * CCK rates are only valid in legacy table and will only be used in G
- * (2.4 GHz) band.
- */
-
-static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
- 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0
-};
-
-/* Expected TpT tables. 4 indexes:
- * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI
- */
-static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0},
- {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0},
- {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0},
- {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0},
-};
-
-static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275},
- {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280},
- {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173},
- {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284},
-};
-
-static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308},
- {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312},
- {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466},
- {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691},
-};
-
-static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0},
- {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0},
- {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0},
- {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0},
-};
-
-static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300},
- {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303},
- {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053},
- {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221},
-};
-
-static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
- {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319},
- {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320},
- {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219},
- {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545},
-};
-
-/* mbps, mcs */
-static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
- { "1", "BPSK DSSS"},
- { "2", "QPSK DSSS"},
- {"5.5", "BPSK CCK"},
- { "11", "QPSK CCK"},
- { "6", "BPSK 1/2"},
- { "9", "BPSK 1/2"},
- { "12", "QPSK 1/2"},
- { "18", "QPSK 3/4"},
- { "24", "16QAM 1/2"},
- { "36", "16QAM 3/4"},
- { "48", "64QAM 2/3"},
- { "54", "64QAM 3/4"},
- { "60", "64QAM 5/6"},
-};
-
-#define MCS_INDEX_PER_STREAM (8)
-
-static const char *rs_pretty_ant(u8 ant)
-{
- static const char * const ant_name[] = {
- [ANT_NONE] = "None",
- [ANT_A] = "A",
- [ANT_B] = "B",
- [ANT_AB] = "AB",
- [ANT_C] = "C",
- [ANT_AC] = "AC",
- [ANT_BC] = "BC",
- [ANT_ABC] = "ABC",
- };
-
- if (ant > ANT_ABC)
- return "UNKNOWN";
-
- return ant_name[ant];
-}
-
-static const char *rs_pretty_lq_type(enum iwl_table_type type)
-{
- static const char * const lq_types[] = {
- [LQ_NONE] = "NONE",
- [LQ_LEGACY_A] = "LEGACY_A",
- [LQ_LEGACY_G] = "LEGACY_G",
- [LQ_HT_SISO] = "HT SISO",
- [LQ_HT_MIMO2] = "HT MIMO",
- [LQ_VHT_SISO] = "VHT SISO",
- [LQ_VHT_MIMO2] = "VHT MIMO",
- };
-
- if (type < LQ_NONE || type >= LQ_MAX)
- return "UNKNOWN";
-
- return lq_types[type];
-}
-
-static char *rs_pretty_rate(const struct rs_rate *rate)
-{
- static char buf[40];
- static const char * const legacy_rates[] = {
- [IWL_RATE_1M_INDEX] = "1M",
- [IWL_RATE_2M_INDEX] = "2M",
- [IWL_RATE_5M_INDEX] = "5.5M",
- [IWL_RATE_11M_INDEX] = "11M",
- [IWL_RATE_6M_INDEX] = "6M",
- [IWL_RATE_9M_INDEX] = "9M",
- [IWL_RATE_12M_INDEX] = "12M",
- [IWL_RATE_18M_INDEX] = "18M",
- [IWL_RATE_24M_INDEX] = "24M",
- [IWL_RATE_36M_INDEX] = "36M",
- [IWL_RATE_48M_INDEX] = "48M",
- [IWL_RATE_54M_INDEX] = "54M",
- };
- static const char *const ht_vht_rates[] = {
- [IWL_RATE_MCS_0_INDEX] = "MCS0",
- [IWL_RATE_MCS_1_INDEX] = "MCS1",
- [IWL_RATE_MCS_2_INDEX] = "MCS2",
- [IWL_RATE_MCS_3_INDEX] = "MCS3",
- [IWL_RATE_MCS_4_INDEX] = "MCS4",
- [IWL_RATE_MCS_5_INDEX] = "MCS5",
- [IWL_RATE_MCS_6_INDEX] = "MCS6",
- [IWL_RATE_MCS_7_INDEX] = "MCS7",
- [IWL_RATE_MCS_8_INDEX] = "MCS8",
- [IWL_RATE_MCS_9_INDEX] = "MCS9",
- };
- const char *rate_str;
-
- if (is_type_legacy(rate->type))
- rate_str = legacy_rates[rate->index];
- else if (is_type_ht(rate->type) || is_type_vht(rate->type))
- rate_str = ht_vht_rates[rate->index];
- else
- rate_str = "BAD_RATE";
-
- sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type),
- rs_pretty_ant(rate->ant), rate_str);
- return buf;
-}
-
-static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate,
- const char *prefix)
-{
- IWL_DEBUG_RATE(mvm,
- "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n",
- prefix, rs_pretty_rate(rate), rate->bw,
- rate->sgi, rate->ldpc, rate->stbc);
-}
-
-static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
-{
- window->data = 0;
- window->success_counter = 0;
- window->success_ratio = IWL_INVALID_VALUE;
- window->counter = 0;
- window->average_tpt = IWL_INVALID_VALUE;
-}
-
-static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm,
- struct iwl_scale_tbl_info *tbl)
-{
- int i;
-
- IWL_DEBUG_RATE(mvm, "Clearing up window stats\n");
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&tbl->win[i]);
-
- for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++)
- rs_rate_scale_clear_window(&tbl->tpc_win[i]);
-}
-
-static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
-{
- return (ant_type & valid_antenna) == ant_type;
-}
-
-static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_data, u8 tid,
- struct ieee80211_sta *sta)
-{
- int ret = -EAGAIN;
-
- IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
- sta->addr, tid);
- ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
- if (ret == -EAGAIN) {
- /*
- * driver and mac80211 is out of sync
- * this might be cause by reloading firmware
- * stop the tx ba session here
- */
- IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n",
- tid);
- ieee80211_stop_tx_ba_session(sta, tid);
- }
- return ret;
-}
-
-static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid,
- struct iwl_lq_sta *lq_data,
- struct ieee80211_sta *sta)
-{
- if (tid < IWL_MAX_TID_COUNT)
- rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta);
- else
- IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n",
- tid, IWL_MAX_TID_COUNT);
-}
-
-static inline int get_num_of_ant_from_rate(u32 rate_n_flags)
-{
- return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) +
- !!(rate_n_flags & RATE_MCS_ANT_B_MSK) +
- !!(rate_n_flags & RATE_MCS_ANT_C_MSK);
-}
-
-/*
- * Static function to get the expected throughput from an iwl_scale_tbl_info
- * that wraps a NULL pointer check
- */
-static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
-{
- if (tbl->expected_tpt)
- return tbl->expected_tpt[rs_index];
- return 0;
-}
-
-/**
- * rs_collect_tx_data - Update the success/failure sliding window
- *
- * We keep a sliding window of the last 62 packets transmitted
- * at this rate. window->data contains the bitmask of successful
- * packets.
- */
-static int _rs_collect_tx_data(struct iwl_mvm *mvm,
- struct iwl_scale_tbl_info *tbl,
- int scale_index, int attempts, int successes,
- struct iwl_rate_scale_data *window)
-{
- static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1));
- s32 fail_count, tpt;
-
- /* Get expected throughput */
- tpt = get_expected_tpt(tbl, scale_index);
-
- /*
- * Keep track of only the latest 62 tx frame attempts in this rate's
- * history window; anything older isn't really relevant any more.
- * If we have filled up the sliding window, drop the oldest attempt;
- * if the oldest attempt (highest bit in bitmap) shows "success",
- * subtract "1" from the success counter (this is the main reason
- * we keep these bitmaps!).
- */
- while (attempts > 0) {
- if (window->counter >= IWL_RATE_MAX_WINDOW) {
- /* remove earliest */
- window->counter = IWL_RATE_MAX_WINDOW - 1;
-
- if (window->data & mask) {
- window->data &= ~mask;
- window->success_counter--;
- }
- }
-
- /* Increment frames-attempted counter */
- window->counter++;
-
- /* Shift bitmap by one frame to throw away oldest history */
- window->data <<= 1;
-
- /* Mark the most recent #successes attempts as successful */
- if (successes > 0) {
- window->success_counter++;
- window->data |= 0x1;
- successes--;
- }
-
- attempts--;
- }
-
- /* Calculate current success ratio, avoid divide-by-0! */
- if (window->counter > 0)
- window->success_ratio = 128 * (100 * window->success_counter)
- / window->counter;
- else
- window->success_ratio = IWL_INVALID_VALUE;
-
- fail_count = window->counter - window->success_counter;
-
- /* Calculate average throughput, if we have enough history. */
- if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) ||
- (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH))
- window->average_tpt = (window->success_ratio * tpt + 64) / 128;
- else
- window->average_tpt = IWL_INVALID_VALUE;
-
- return 0;
-}
-
-static int rs_collect_tx_data(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl,
- int scale_index, int attempts, int successes,
- u8 reduced_txp)
-{
- struct iwl_rate_scale_data *window = NULL;
- int ret;
-
- if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
- return -EINVAL;
-
- if (tbl->column != RS_COLUMN_INVALID) {
- struct lq_sta_pers *pers = &lq_sta->pers;
-
- pers->tx_stats[tbl->column][scale_index].total += attempts;
- pers->tx_stats[tbl->column][scale_index].success += successes;
- }
-
- /* Select window for current tx bit rate */
- window = &(tbl->win[scale_index]);
-
- ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes,
- window);
- if (ret)
- return ret;
-
- if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION))
- return -EINVAL;
-
- window = &tbl->tpc_win[reduced_txp];
- return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes,
- window);
-}
-
-/* Convert rs_rate object into ucode rate bitmask */
-static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm,
- struct rs_rate *rate)
-{
- u32 ucode_rate = 0;
- int index = rate->index;
-
- ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) &
- RATE_MCS_ANT_ABC_MSK);
-
- if (is_legacy(rate)) {
- ucode_rate |= iwl_rates[index].plcp;
- if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
- ucode_rate |= RATE_MCS_CCK_MSK;
- return ucode_rate;
- }
-
- if (is_ht(rate)) {
- if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) {
- IWL_ERR(mvm, "Invalid HT rate index %d\n", index);
- index = IWL_LAST_HT_RATE;
- }
- ucode_rate |= RATE_MCS_HT_MSK;
-
- if (is_ht_siso(rate))
- ucode_rate |= iwl_rates[index].plcp_ht_siso;
- else if (is_ht_mimo2(rate))
- ucode_rate |= iwl_rates[index].plcp_ht_mimo2;
- else
- WARN_ON_ONCE(1);
- } else if (is_vht(rate)) {
- if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) {
- IWL_ERR(mvm, "Invalid VHT rate index %d\n", index);
- index = IWL_LAST_VHT_RATE;
- }
- ucode_rate |= RATE_MCS_VHT_MSK;
- if (is_vht_siso(rate))
- ucode_rate |= iwl_rates[index].plcp_vht_siso;
- else if (is_vht_mimo2(rate))
- ucode_rate |= iwl_rates[index].plcp_vht_mimo2;
- else
- WARN_ON_ONCE(1);
-
- } else {
- IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type);
- }
-
- if (is_siso(rate) && rate->stbc) {
- /* To enable STBC we need to set both a flag and ANT_AB */
- ucode_rate |= RATE_MCS_ANT_AB_MSK;
- ucode_rate |= RATE_MCS_VHT_STBC_MSK;
- }
-
- ucode_rate |= rate->bw;
- if (rate->sgi)
- ucode_rate |= RATE_MCS_SGI_MSK;
- if (rate->ldpc)
- ucode_rate |= RATE_MCS_LDPC_MSK;
-
- return ucode_rate;
-}
-
-/* Convert a ucode rate into an rs_rate object */
-static int rs_rate_from_ucode_rate(const u32 ucode_rate,
- enum ieee80211_band band,
- struct rs_rate *rate)
-{
- u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK;
- u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate);
- u8 nss;
-
- memset(rate, 0, sizeof(*rate));
- rate->index = iwl_hwrate_to_plcp_idx(ucode_rate);
-
- if (rate->index == IWL_RATE_INVALID)
- return -EINVAL;
-
- rate->ant = (ant_msk >> RATE_MCS_ANT_POS);
-
- /* Legacy */
- if (!(ucode_rate & RATE_MCS_HT_MSK) &&
- !(ucode_rate & RATE_MCS_VHT_MSK)) {
- if (num_of_ant == 1) {
- if (band == IEEE80211_BAND_5GHZ)
- rate->type = LQ_LEGACY_A;
- else
- rate->type = LQ_LEGACY_G;
- }
-
- return 0;
- }
-
- /* HT or VHT */
- if (ucode_rate & RATE_MCS_SGI_MSK)
- rate->sgi = true;
- if (ucode_rate & RATE_MCS_LDPC_MSK)
- rate->ldpc = true;
- if (ucode_rate & RATE_MCS_VHT_STBC_MSK)
- rate->stbc = true;
- if (ucode_rate & RATE_MCS_BF_MSK)
- rate->bfer = true;
-
- rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK;
-
- if (ucode_rate & RATE_MCS_HT_MSK) {
- nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >>
- RATE_HT_MCS_NSS_POS) + 1;
-
- if (nss == 1) {
- rate->type = LQ_HT_SISO;
- WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1,
- "stbc %d bfer %d",
- rate->stbc, rate->bfer);
- } else if (nss == 2) {
- rate->type = LQ_HT_MIMO2;
- WARN_ON_ONCE(num_of_ant != 2);
- } else {
- WARN_ON_ONCE(1);
- }
- } else if (ucode_rate & RATE_MCS_VHT_MSK) {
- nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >>
- RATE_VHT_MCS_NSS_POS) + 1;
-
- if (nss == 1) {
- rate->type = LQ_VHT_SISO;
- WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1,
- "stbc %d bfer %d",
- rate->stbc, rate->bfer);
- } else if (nss == 2) {
- rate->type = LQ_VHT_MIMO2;
- WARN_ON_ONCE(num_of_ant != 2);
- } else {
- WARN_ON_ONCE(1);
- }
- }
-
- WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160);
- WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 &&
- !is_vht(rate));
-
- return 0;
-}
-
-/* switch to another antenna/antennas and return 1 */
-/* if no other valid antenna found, return 0 */
-static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate)
-{
- u8 new_ant_type;
-
- if (!rate->ant || rate->ant > ANT_ABC)
- return 0;
-
- if (!rs_is_valid_ant(valid_ant, rate->ant))
- return 0;
-
- new_ant_type = ant_toggle_lookup[rate->ant];
-
- while ((new_ant_type != rate->ant) &&
- !rs_is_valid_ant(valid_ant, new_ant_type))
- new_ant_type = ant_toggle_lookup[new_ant_type];
-
- if (new_ant_type == rate->ant)
- return 0;
-
- rate->ant = new_ant_type;
-
- return 1;
-}
-
-static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
- struct rs_rate *rate)
-{
- if (is_legacy(rate))
- return lq_sta->active_legacy_rate;
- else if (is_siso(rate))
- return lq_sta->active_siso_rate;
- else if (is_mimo2(rate))
- return lq_sta->active_mimo2_rate;
-
- WARN_ON_ONCE(1);
- return 0;
-}
-
-static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
- int rate_type)
-{
- u8 high = IWL_RATE_INVALID;
- u8 low = IWL_RATE_INVALID;
-
- /* 802.11A or ht walks to the next literal adjacent rate in
- * the rate table */
- if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) {
- int i;
- u32 mask;
-
- /* Find the previous rate that is in the rate mask */
- i = index - 1;
- for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
- if (rate_mask & mask) {
- low = i;
- break;
- }
- }
-
- /* Find the next rate that is in the rate mask */
- i = index + 1;
- for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
- if (rate_mask & mask) {
- high = i;
- break;
- }
- }
-
- return (high << 8) | low;
- }
-
- low = index;
- while (low != IWL_RATE_INVALID) {
- low = iwl_rates[low].prev_rs;
- if (low == IWL_RATE_INVALID)
- break;
- if (rate_mask & (1 << low))
- break;
- }
-
- high = index;
- while (high != IWL_RATE_INVALID) {
- high = iwl_rates[high].next_rs;
- if (high == IWL_RATE_INVALID)
- break;
- if (rate_mask & (1 << high))
- break;
- }
-
- return (high << 8) | low;
-}
-
-static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta,
- struct rs_rate *rate)
-{
- return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate);
-}
-
-/* Get the next supported lower rate in the current column.
- * Return true if bottom rate in the current column was reached
- */
-static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
- struct rs_rate *rate)
-{
- u8 low;
- u16 high_low;
- u16 rate_mask;
- struct iwl_mvm *mvm = lq_sta->pers.drv;
-
- rate_mask = rs_get_supported_rates(lq_sta, rate);
- high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
- rate->type);
- low = high_low & 0xff;
-
- /* Bottom rate of column reached */
- if (low == IWL_RATE_INVALID)
- return true;
-
- rate->index = low;
- return false;
-}
-
-/* Get the next rate to use following a column downgrade */
-static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
- struct rs_rate *rate)
-{
- struct iwl_mvm *mvm = lq_sta->pers.drv;
-
- if (is_legacy(rate)) {
- /* No column to downgrade from Legacy */
- return;
- } else if (is_siso(rate)) {
- /* Downgrade to Legacy if we were in SISO */
- if (lq_sta->band == IEEE80211_BAND_5GHZ)
- rate->type = LQ_LEGACY_A;
- else
- rate->type = LQ_LEGACY_G;
-
- rate->bw = RATE_MCS_CHAN_WIDTH_20;
-
- WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX ||
- rate->index > IWL_RATE_MCS_9_INDEX);
-
- rate->index = rs_ht_to_legacy[rate->index];
- rate->ldpc = false;
- } else {
- /* Downgrade to SISO with same MCS if in MIMO */
- rate->type = is_vht_mimo2(rate) ?
- LQ_VHT_SISO : LQ_HT_SISO;
- }
-
- if (num_of_ant(rate->ant) > 1)
- rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm));
-
- /* Relevant in both switching to SISO or Legacy */
- rate->sgi = false;
-
- if (!rs_rate_supported(lq_sta, rate))
- rs_get_lower_rate_in_column(lq_sta, rate);
-}
-
-/* Check if both rates are identical
- * allow_ant_mismatch enables matching a SISO rate on ANT_A or ANT_B
- * with a rate indicating STBC/BFER and ANT_AB.
- */
-static inline bool rs_rate_equal(struct rs_rate *a,
- struct rs_rate *b,
- bool allow_ant_mismatch)
-
-{
- bool ant_match = (a->ant == b->ant) && (a->stbc == b->stbc) &&
- (a->bfer == b->bfer);
-
- if (allow_ant_mismatch) {
- if (a->stbc || a->bfer) {
- WARN_ONCE(a->ant != ANT_AB, "stbc %d bfer %d ant %d",
- a->stbc, a->bfer, a->ant);
- ant_match |= (b->ant == ANT_A || b->ant == ANT_B);
- } else if (b->stbc || b->bfer) {
- WARN_ONCE(b->ant != ANT_AB, "stbc %d bfer %d ant %d",
- b->stbc, b->bfer, b->ant);
- ant_match |= (a->ant == ANT_A || a->ant == ANT_B);
- }
- }
-
- return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) &&
- (a->ldpc == b->ldpc) && (a->index == b->index) && ant_match;
-}
-
-/* Check if both rates share the same column */
-static inline bool rs_rate_column_match(struct rs_rate *a,
- struct rs_rate *b)
-{
- bool ant_match;
-
- if (a->stbc || a->bfer)
- ant_match = (b->ant == ANT_A || b->ant == ANT_B);
- else
- ant_match = (a->ant == b->ant);
-
- return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi)
- && ant_match;
-}
-
-static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate)
-{
- if (is_legacy(rate)) {
- if (rate->ant == ANT_A)
- return RS_COLUMN_LEGACY_ANT_A;
-
- if (rate->ant == ANT_B)
- return RS_COLUMN_LEGACY_ANT_B;
-
- goto err;
- }
-
- if (is_siso(rate)) {
- if (rate->ant == ANT_A || rate->stbc || rate->bfer)
- return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI :
- RS_COLUMN_SISO_ANT_A;
-
- if (rate->ant == ANT_B)
- return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI :
- RS_COLUMN_SISO_ANT_B;
-
- goto err;
- }
-
- if (is_mimo(rate))
- return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2;
-
-err:
- return RS_COLUMN_INVALID;
-}
-
-static u8 rs_get_tid(struct ieee80211_hdr *hdr)
-{
- u8 tid = IWL_MAX_TID_COUNT;
-
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- u8 *qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
- }
-
- if (unlikely(tid > IWL_MAX_TID_COUNT))
- tid = IWL_MAX_TID_COUNT;
-
- return tid;
-}
-
-void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- int tid, struct ieee80211_tx_info *info)
-{
- int legacy_success;
- int retries;
- int i;
- struct iwl_lq_cmd *table;
- u32 lq_hwrate;
- struct rs_rate lq_rate, tx_resp_rate;
- struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
- u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0];
- u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta;
- bool allow_ant_mismatch = fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_LQ_SS_PARAMS);
-
- /* Treat uninitialized rate scaling data same as non-existing. */
- if (!lq_sta) {
- IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
- return;
- } else if (!lq_sta->pers.drv) {
- IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
- return;
- }
-
- /* This packet was aggregated but doesn't carry status info */
- if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
- !(info->flags & IEEE80211_TX_STAT_AMPDU))
- return;
-
- rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate);
-
-#ifdef CONFIG_MAC80211_DEBUGFS
- /* Disable last tx check if we are debugging with fixed rate but
- * update tx stats */
- if (lq_sta->pers.dbg_fixed_rate) {
- int index = tx_resp_rate.index;
- enum rs_column column;
- int attempts, success;
-
- column = rs_get_column_from_rate(&tx_resp_rate);
- if (WARN_ONCE(column == RS_COLUMN_INVALID,
- "Can't map rate 0x%x to column",
- tx_resp_hwrate))
- return;
-
- if (info->flags & IEEE80211_TX_STAT_AMPDU) {
- attempts = info->status.ampdu_len;
- success = info->status.ampdu_ack_len;
- } else {
- attempts = info->status.rates[0].count;
- success = !!(info->flags & IEEE80211_TX_STAT_ACK);
- }
-
- lq_sta->pers.tx_stats[column][index].total += attempts;
- lq_sta->pers.tx_stats[column][index].success += success;
-
- IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
- tx_resp_hwrate, success, attempts);
- return;
- }
-#endif
-
- if (time_after(jiffies,
- (unsigned long)(lq_sta->last_tx +
- (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) {
- int t;
-
- IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
- for (t = 0; t < IWL_MAX_TID_COUNT; t++)
- ieee80211_stop_tx_ba_session(sta, t);
-
- iwl_mvm_rs_rate_init(mvm, sta, info->band, false);
- return;
- }
- lq_sta->last_tx = jiffies;
-
- /* Ignore this Tx frame response if its initial rate doesn't match
- * that of latest Link Quality command. There may be stragglers
- * from a previous Link Quality command, but we're no longer interested
- * in those; they're either from the "active" mode while we're trying
- * to check "search" mode, or a prior "search" mode after we've moved
- * to a new "search" mode (which might become the new "active" mode).
- */
- table = &lq_sta->lq;
- lq_hwrate = le32_to_cpu(table->rs_table[0]);
- rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate);
-
- /* Here we actually compare this rate to the latest LQ command */
- if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) {
- IWL_DEBUG_RATE(mvm,
- "initial tx resp rate 0x%x does not match 0x%x\n",
- tx_resp_hwrate, lq_hwrate);
-
- /*
- * Since rates mis-match, the last LQ command may have failed.
- * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
- * ... driver.
- */
- lq_sta->missed_rate_counter++;
- if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) {
- lq_sta->missed_rate_counter = 0;
- IWL_DEBUG_RATE(mvm,
- "Too many rates mismatch. Send sync LQ. rs_state %d\n",
- lq_sta->rs_state);
- iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
- }
- /* Regardless, ignore this status info for outdated rate */
- return;
- } else
- /* Rate did match, so reset the missed_rate_counter */
- lq_sta->missed_rate_counter = 0;
-
- if (!lq_sta->search_better_tbl) {
- curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
- } else {
- curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
- other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- }
-
- if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) {
- IWL_DEBUG_RATE(mvm,
- "Neither active nor search matches tx rate\n");
- tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE");
- tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
- rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH");
- rs_dump_rate(mvm, &lq_rate, "ACTUAL");
-
- /*
- * no matching table found, let's by-pass the data collection
- * and continue to perform rate scale to find the rate table
- */
- rs_stay_in_table(lq_sta, true);
- goto done;
- }
-
- /*
- * Updating the frame history depends on whether packets were
- * aggregated.
- *
- * For aggregation, all packets were transmitted at the same rate, the
- * first index into rate scale table.
- */
- if (info->flags & IEEE80211_TX_STAT_AMPDU) {
- /* ampdu_ack_len = 0 marks no BA was received. In this case
- * treat it as a single frame loss as we don't want the success
- * ratio to dip too quickly because a BA wasn't received
- */
- if (info->status.ampdu_ack_len == 0)
- info->status.ampdu_len = 1;
-
- rs_collect_tx_data(mvm, lq_sta, curr_tbl, lq_rate.index,
- info->status.ampdu_len,
- info->status.ampdu_ack_len,
- reduced_txp);
-
- /* Update success/fail counts if not searching for new mode */
- if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
- lq_sta->total_success += info->status.ampdu_ack_len;
- lq_sta->total_failed += (info->status.ampdu_len -
- info->status.ampdu_ack_len);
- }
- } else {
- /* For legacy, update frame history with for each Tx retry. */
- retries = info->status.rates[0].count - 1;
- /* HW doesn't send more than 15 retries */
- retries = min(retries, 15);
-
- /* The last transmission may have been successful */
- legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
- /* Collect data for each rate used during failed TX attempts */
- for (i = 0; i <= retries; ++i) {
- lq_hwrate = le32_to_cpu(table->rs_table[i]);
- rs_rate_from_ucode_rate(lq_hwrate, info->band,
- &lq_rate);
- /*
- * Only collect stats if retried rate is in the same RS
- * table as active/search.
- */
- if (rs_rate_column_match(&lq_rate, &curr_tbl->rate))
- tmp_tbl = curr_tbl;
- else if (rs_rate_column_match(&lq_rate,
- &other_tbl->rate))
- tmp_tbl = other_tbl;
- else
- continue;
-
- rs_collect_tx_data(mvm, lq_sta, tmp_tbl, lq_rate.index,
- 1, i < retries ? 0 : legacy_success,
- reduced_txp);
- }
-
- /* Update success/fail counts if not searching for new mode */
- if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
- lq_sta->total_success += legacy_success;
- lq_sta->total_failed += retries + (1 - legacy_success);
- }
- }
- /* The last TX rate is cached in lq_sta; it's set in if/else above */
- lq_sta->last_rate_n_flags = lq_hwrate;
- IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
-done:
- /* See if there's a better rate or modulation mode to try. */
- if (sta->supp_rates[info->band])
- rs_rate_scale_perform(mvm, sta, lq_sta, tid);
-}
-
-/*
- * mac80211 sends us Tx status
- */
-static void rs_mac80211_tx_status(void *mvm_r,
- struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r;
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- if (!iwl_mvm_sta_from_mac80211(sta)->vif)
- return;
-
- if (!ieee80211_is_data(hdr->frame_control) ||
- info->flags & IEEE80211_TX_CTL_NO_ACK)
- return;
-
- iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info);
-}
-
-/*
- * Begin a period of staying with a selected modulation mode.
- * Set "stay_in_tbl" flag to prevent any mode switches.
- * Set frame tx success limits according to legacy vs. high-throughput,
- * and reset overall (spanning all rates) tx success history statistics.
- * These control how long we stay using same modulation mode before
- * searching for a new mode.
- */
-static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy,
- struct iwl_lq_sta *lq_sta)
-{
- IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n");
- lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN;
- if (is_legacy) {
- lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT;
- lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT;
- lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT;
- } else {
- lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT;
- lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT;
- lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT;
- }
- lq_sta->table_count = 0;
- lq_sta->total_failed = 0;
- lq_sta->total_success = 0;
- lq_sta->flush_timer = jiffies;
- lq_sta->visited_columns = 0;
-}
-
-static inline int rs_get_max_rate_from_mask(unsigned long rate_mask)
-{
- if (rate_mask)
- return find_last_bit(&rate_mask, BITS_PER_LONG);
- return IWL_RATE_INVALID;
-}
-
-static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta,
- const struct rs_tx_column *column)
-{
- switch (column->mode) {
- case RS_LEGACY:
- return lq_sta->max_legacy_rate_idx;
- case RS_SISO:
- return lq_sta->max_siso_rate_idx;
- case RS_MIMO2:
- return lq_sta->max_mimo2_rate_idx;
- default:
- WARN_ON_ONCE(1);
- }
-
- return lq_sta->max_legacy_rate_idx;
-}
-
-static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
- const struct rs_tx_column *column,
- u32 bw)
-{
- /* Used to choose among HT tables */
- const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
-
- if (WARN_ON_ONCE(column->mode != RS_LEGACY &&
- column->mode != RS_SISO &&
- column->mode != RS_MIMO2))
- return expected_tpt_legacy;
-
- /* Legacy rates have only one table */
- if (column->mode == RS_LEGACY)
- return expected_tpt_legacy;
-
- ht_tbl_pointer = expected_tpt_mimo2_20MHz;
- /* Choose among many HT tables depending on number of streams
- * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation
- * status */
- if (column->mode == RS_SISO) {
- switch (bw) {
- case RATE_MCS_CHAN_WIDTH_20:
- ht_tbl_pointer = expected_tpt_siso_20MHz;
- break;
- case RATE_MCS_CHAN_WIDTH_40:
- ht_tbl_pointer = expected_tpt_siso_40MHz;
- break;
- case RATE_MCS_CHAN_WIDTH_80:
- ht_tbl_pointer = expected_tpt_siso_80MHz;
- break;
- default:
- WARN_ON_ONCE(1);
- }
- } else if (column->mode == RS_MIMO2) {
- switch (bw) {
- case RATE_MCS_CHAN_WIDTH_20:
- ht_tbl_pointer = expected_tpt_mimo2_20MHz;
- break;
- case RATE_MCS_CHAN_WIDTH_40:
- ht_tbl_pointer = expected_tpt_mimo2_40MHz;
- break;
- case RATE_MCS_CHAN_WIDTH_80:
- ht_tbl_pointer = expected_tpt_mimo2_80MHz;
- break;
- default:
- WARN_ON_ONCE(1);
- }
- } else {
- WARN_ON_ONCE(1);
- }
-
- if (!column->sgi && !lq_sta->is_agg) /* Normal */
- return ht_tbl_pointer[0];
- else if (column->sgi && !lq_sta->is_agg) /* SGI */
- return ht_tbl_pointer[1];
- else if (!column->sgi && lq_sta->is_agg) /* AGG */
- return ht_tbl_pointer[2];
- else /* AGG+SGI */
- return ht_tbl_pointer[3];
-}
-
-static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl)
-{
- struct rs_rate *rate = &tbl->rate;
- const struct rs_tx_column *column = &rs_tx_columns[tbl->column];
-
- tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
-}
-
-static s32 rs_get_best_rate(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl, /* "search" */
- unsigned long rate_mask, s8 index)
-{
- struct iwl_scale_tbl_info *active_tbl =
- &(lq_sta->lq_info[lq_sta->active_tbl]);
- s32 success_ratio = active_tbl->win[index].success_ratio;
- u16 expected_current_tpt = active_tbl->expected_tpt[index];
- const u16 *tpt_tbl = tbl->expected_tpt;
- u16 high_low;
- u32 target_tpt;
- int rate_idx;
-
- if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) {
- target_tpt = 100 * expected_current_tpt;
- IWL_DEBUG_RATE(mvm,
- "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
- success_ratio, target_tpt);
- } else {
- target_tpt = lq_sta->last_tpt;
- IWL_DEBUG_RATE(mvm,
- "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n",
- success_ratio, target_tpt);
- }
-
- rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG);
-
- while (rate_idx != IWL_RATE_INVALID) {
- if (target_tpt < (100 * tpt_tbl[rate_idx]))
- break;
-
- high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask,
- tbl->rate.type);
-
- rate_idx = (high_low >> 8) & 0xff;
- }
-
- IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n",
- rate_idx, target_tpt,
- rate_idx != IWL_RATE_INVALID ?
- 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE);
-
- return rate_idx;
-}
-
-static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta)
-{
- if (sta->bandwidth >= IEEE80211_STA_RX_BW_80)
- return RATE_MCS_CHAN_WIDTH_80;
- else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40)
- return RATE_MCS_CHAN_WIDTH_40;
-
- return RATE_MCS_CHAN_WIDTH_20;
-}
-
-/*
- * Check whether we should continue using same modulation mode, or
- * begin search for a new mode, based on:
- * 1) # tx successes or failures while using this mode
- * 2) # times calling this function
- * 3) elapsed time in this mode (not used, for now)
- */
-static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
-{
- struct iwl_scale_tbl_info *tbl;
- int active_tbl;
- int flush_interval_passed = 0;
- struct iwl_mvm *mvm;
-
- mvm = lq_sta->pers.drv;
- active_tbl = lq_sta->active_tbl;
-
- tbl = &(lq_sta->lq_info[active_tbl]);
-
- /* If we've been disallowing search, see if we should now allow it */
- if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
- /* Elapsed time using current modulation mode */
- if (lq_sta->flush_timer)
- flush_interval_passed =
- time_after(jiffies,
- (unsigned long)(lq_sta->flush_timer +
- (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ)));
-
- /*
- * Check if we should allow search for new modulation mode.
- * If many frames have failed or succeeded, or we've used
- * this same modulation for a long time, allow search, and
- * reset history stats that keep track of whether we should
- * allow a new search. Also (below) reset all bitmaps and
- * stats in active history.
- */
- if (force_search ||
- (lq_sta->total_failed > lq_sta->max_failure_limit) ||
- (lq_sta->total_success > lq_sta->max_success_limit) ||
- ((!lq_sta->search_better_tbl) &&
- (lq_sta->flush_timer) && (flush_interval_passed))) {
- IWL_DEBUG_RATE(mvm,
- "LQ: stay is expired %d %d %d\n",
- lq_sta->total_failed,
- lq_sta->total_success,
- flush_interval_passed);
-
- /* Allow search for new mode */
- lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED;
- IWL_DEBUG_RATE(mvm,
- "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n");
- lq_sta->total_failed = 0;
- lq_sta->total_success = 0;
- lq_sta->flush_timer = 0;
- /* mark the current column as visited */
- lq_sta->visited_columns = BIT(tbl->column);
- /*
- * Else if we've used this modulation mode enough repetitions
- * (regardless of elapsed time or success/failure), reset
- * history bitmaps and rate-specific stats for all rates in
- * active table.
- */
- } else {
- lq_sta->table_count++;
- if (lq_sta->table_count >=
- lq_sta->table_count_limit) {
- lq_sta->table_count = 0;
-
- IWL_DEBUG_RATE(mvm,
- "LQ: stay in table clear win\n");
- rs_rate_scale_clear_tbl_windows(mvm, tbl);
- }
- }
-
- /* If transitioning to allow "search", reset all history
- * bitmaps and stats in active table (this will become the new
- * "search" table). */
- if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) {
- rs_rate_scale_clear_tbl_windows(mvm, tbl);
- }
- }
-}
-
-/*
- * setup rate table in uCode
- */
-static void rs_update_rate_tbl(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl)
-{
- rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
- iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
-}
-
-static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl,
- enum rs_action scale_action)
-{
- if (sta->bandwidth != IEEE80211_STA_RX_BW_80)
- return false;
-
- if (!is_vht_siso(&tbl->rate))
- return false;
-
- if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) &&
- (tbl->rate.index == IWL_RATE_MCS_0_INDEX) &&
- (scale_action == RS_ACTION_DOWNSCALE)) {
- tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20;
- tbl->rate.index = IWL_RATE_MCS_4_INDEX;
- IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n");
- goto tweaked;
- }
-
- /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is
- * sustainable, i.e. we're past the test window. We can't go back
- * if MCS5 is just tested as this will happen always after switching
- * to 20Mhz MCS4 because the rate stats are cleared.
- */
- if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) &&
- (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) &&
- (scale_action == RS_ACTION_STAY)) ||
- ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) &&
- (scale_action == RS_ACTION_UPSCALE)))) {
- tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80;
- tbl->rate.index = IWL_RATE_MCS_1_INDEX;
- IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n");
- goto tweaked;
- }
-
- return false;
-
-tweaked:
- rs_set_expected_tpt_table(lq_sta, tbl);
- rs_rate_scale_clear_tbl_windows(mvm, tbl);
- return true;
-}
-
-static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl)
-{
- int i, j, max_rate;
- enum rs_column next_col_id;
- const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column];
- const struct rs_tx_column *next_col;
- allow_column_func_t allow_func;
- u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm);
- const u16 *expected_tpt_tbl;
- u16 tpt, max_expected_tpt;
-
- for (i = 0; i < MAX_NEXT_COLUMNS; i++) {
- next_col_id = curr_col->next_columns[i];
-
- if (next_col_id == RS_COLUMN_INVALID)
- continue;
-
- if (lq_sta->visited_columns & BIT(next_col_id)) {
- IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n",
- next_col_id);
- continue;
- }
-
- next_col = &rs_tx_columns[next_col_id];
-
- if (!rs_is_valid_ant(valid_ants, next_col->ant)) {
- IWL_DEBUG_RATE(mvm,
- "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n",
- next_col_id, valid_ants, next_col->ant);
- continue;
- }
-
- for (j = 0; j < MAX_COLUMN_CHECKS; j++) {
- allow_func = next_col->checks[j];
- if (allow_func && !allow_func(mvm, sta, &tbl->rate,
- next_col))
- break;
- }
-
- if (j != MAX_COLUMN_CHECKS) {
- IWL_DEBUG_RATE(mvm,
- "Skip column %d: not allowed (check %d failed)\n",
- next_col_id, j);
-
- continue;
- }
-
- tpt = lq_sta->last_tpt / 100;
- expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col,
- rs_bw_from_sta_bw(sta));
- if (WARN_ON_ONCE(!expected_tpt_tbl))
- continue;
-
- max_rate = rs_get_max_allowed_rate(lq_sta, next_col);
- if (max_rate == IWL_RATE_INVALID) {
- IWL_DEBUG_RATE(mvm,
- "Skip column %d: no rate is allowed in this column\n",
- next_col_id);
- continue;
- }
-
- max_expected_tpt = expected_tpt_tbl[max_rate];
- if (tpt >= max_expected_tpt) {
- IWL_DEBUG_RATE(mvm,
- "Skip column %d: can't beat current TPT. Max expected %d current %d\n",
- next_col_id, max_expected_tpt, tpt);
- continue;
- }
-
- IWL_DEBUG_RATE(mvm,
- "Found potential column %d. Max expected %d current %d\n",
- next_col_id, max_expected_tpt, tpt);
- break;
- }
-
- if (i == MAX_NEXT_COLUMNS)
- return RS_COLUMN_INVALID;
-
- return next_col_id;
-}
-
-static int rs_switch_to_column(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_sta *sta,
- enum rs_column col_id)
-{
- struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- struct iwl_scale_tbl_info *search_tbl =
- &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- struct rs_rate *rate = &search_tbl->rate;
- const struct rs_tx_column *column = &rs_tx_columns[col_id];
- const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
- u32 sz = (sizeof(struct iwl_scale_tbl_info) -
- (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
- unsigned long rate_mask = 0;
- u32 rate_idx = 0;
-
- memcpy(search_tbl, tbl, sz);
-
- rate->sgi = column->sgi;
- rate->ant = column->ant;
-
- if (column->mode == RS_LEGACY) {
- if (lq_sta->band == IEEE80211_BAND_5GHZ)
- rate->type = LQ_LEGACY_A;
- else
- rate->type = LQ_LEGACY_G;
-
- rate->bw = RATE_MCS_CHAN_WIDTH_20;
- rate->ldpc = false;
- rate_mask = lq_sta->active_legacy_rate;
- } else if (column->mode == RS_SISO) {
- rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO;
- rate_mask = lq_sta->active_siso_rate;
- } else if (column->mode == RS_MIMO2) {
- rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2;
- rate_mask = lq_sta->active_mimo2_rate;
- } else {
- WARN_ON_ONCE("Bad column mode");
- }
-
- if (column->mode != RS_LEGACY) {
- rate->bw = rs_bw_from_sta_bw(sta);
- rate->ldpc = lq_sta->ldpc;
- }
-
- search_tbl->column = col_id;
- rs_set_expected_tpt_table(lq_sta, search_tbl);
-
- lq_sta->visited_columns |= BIT(col_id);
-
- /* Get the best matching rate if we're changing modes. e.g.
- * SISO->MIMO, LEGACY->SISO, MIMO->SISO
- */
- if (curr_column->mode != column->mode) {
- rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl,
- rate_mask, rate->index);
-
- if ((rate_idx == IWL_RATE_INVALID) ||
- !(BIT(rate_idx) & rate_mask)) {
- IWL_DEBUG_RATE(mvm,
- "can not switch with index %d"
- " rate mask %lx\n",
- rate_idx, rate_mask);
-
- goto err;
- }
-
- rate->index = rate_idx;
- }
-
- IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n",
- col_id, rate->index);
-
- return 0;
-
-err:
- rate->type = LQ_NONE;
- return -1;
-}
-
-static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
- struct iwl_scale_tbl_info *tbl,
- s32 sr, int low, int high,
- int current_tpt,
- int low_tpt, int high_tpt)
-{
- enum rs_action action = RS_ACTION_STAY;
-
- if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) ||
- (current_tpt == 0)) {
- IWL_DEBUG_RATE(mvm,
- "Decrease rate because of low SR\n");
- return RS_ACTION_DOWNSCALE;
- }
-
- if ((low_tpt == IWL_INVALID_VALUE) &&
- (high_tpt == IWL_INVALID_VALUE) &&
- (high != IWL_RATE_INVALID)) {
- IWL_DEBUG_RATE(mvm,
- "No data about high/low rates. Increase rate\n");
- return RS_ACTION_UPSCALE;
- }
-
- if ((high_tpt == IWL_INVALID_VALUE) &&
- (high != IWL_RATE_INVALID) &&
- (low_tpt != IWL_INVALID_VALUE) &&
- (low_tpt < current_tpt)) {
- IWL_DEBUG_RATE(mvm,
- "No data about high rate and low rate is worse. Increase rate\n");
- return RS_ACTION_UPSCALE;
- }
-
- if ((high_tpt != IWL_INVALID_VALUE) &&
- (high_tpt > current_tpt)) {
- IWL_DEBUG_RATE(mvm,
- "Higher rate is better. Increate rate\n");
- return RS_ACTION_UPSCALE;
- }
-
- if ((low_tpt != IWL_INVALID_VALUE) &&
- (high_tpt != IWL_INVALID_VALUE) &&
- (low_tpt < current_tpt) &&
- (high_tpt < current_tpt)) {
- IWL_DEBUG_RATE(mvm,
- "Both high and low are worse. Maintain rate\n");
- return RS_ACTION_STAY;
- }
-
- if ((low_tpt != IWL_INVALID_VALUE) &&
- (low_tpt > current_tpt)) {
- IWL_DEBUG_RATE(mvm,
- "Lower rate is better\n");
- action = RS_ACTION_DOWNSCALE;
- goto out;
- }
-
- if ((low_tpt == IWL_INVALID_VALUE) &&
- (low != IWL_RATE_INVALID)) {
- IWL_DEBUG_RATE(mvm,
- "No data about lower rate\n");
- action = RS_ACTION_DOWNSCALE;
- goto out;
- }
-
- IWL_DEBUG_RATE(mvm, "Maintain rate\n");
-
-out:
- if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) {
- if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) {
- IWL_DEBUG_RATE(mvm,
- "SR is above NO DECREASE. Avoid downscale\n");
- action = RS_ACTION_STAY;
- } else if (current_tpt > (100 * tbl->expected_tpt[low])) {
- IWL_DEBUG_RATE(mvm,
- "Current TPT is higher than max expected in low rate. Avoid downscale\n");
- action = RS_ACTION_STAY;
- } else {
- IWL_DEBUG_RATE(mvm, "Decrease rate\n");
- }
- }
-
- return action;
-}
-
-static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta)
-{
- /* Our chip supports Tx STBC and the peer is an HT/VHT STA which
- * supports STBC of at least 1*SS
- */
- if (!lq_sta->stbc_capable)
- return false;
-
- if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
- return false;
-
- return true;
-}
-
-static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
- int *weaker, int *stronger)
-{
- *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP;
- if (*weaker > TPC_MAX_REDUCTION)
- *weaker = TPC_INVALID;
-
- *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP;
- if (*stronger < 0)
- *stronger = TPC_INVALID;
-}
-
-static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct rs_rate *rate, enum ieee80211_band band)
-{
- int index = rate->index;
- bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
- bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
- !vif->bss_conf.ps);
-
- IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n",
- cam, sta_ps_disabled);
- /*
- * allow tpc only if power management is enabled, or bt coex
- * activity grade allows it and we are on 2.4Ghz.
- */
- if ((cam || sta_ps_disabled) &&
- !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
- return false;
-
- IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type);
- if (is_legacy(rate))
- return index == IWL_RATE_54M_INDEX;
- if (is_ht(rate))
- return index == IWL_RATE_MCS_7_INDEX;
- if (is_vht(rate))
- return index == IWL_RATE_MCS_7_INDEX ||
- index == IWL_RATE_MCS_8_INDEX ||
- index == IWL_RATE_MCS_9_INDEX;
-
- WARN_ON_ONCE(1);
- return false;
-}
-
-enum tpc_action {
- TPC_ACTION_STAY,
- TPC_ACTION_DECREASE,
- TPC_ACTION_INCREASE,
- TPC_ACTION_NO_RESTIRCTION,
-};
-
-static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm,
- s32 sr, int weak, int strong,
- int current_tpt,
- int weak_tpt, int strong_tpt)
-{
- /* stay until we have valid tpt */
- if (current_tpt == IWL_INVALID_VALUE) {
- IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n");
- return TPC_ACTION_STAY;
- }
-
- /* Too many failures, increase txp */
- if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) ||
- current_tpt == 0) {
- IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n");
- return TPC_ACTION_NO_RESTIRCTION;
- }
-
- /* try decreasing first if applicable */
- if (weak != TPC_INVALID) {
- if (weak_tpt == IWL_INVALID_VALUE &&
- (strong_tpt == IWL_INVALID_VALUE ||
- current_tpt >= strong_tpt)) {
- IWL_DEBUG_RATE(mvm,
- "no weak txp measurement. decrease txp\n");
- return TPC_ACTION_DECREASE;
- }
-
- if (weak_tpt > current_tpt) {
- IWL_DEBUG_RATE(mvm,
- "lower txp has better tpt. decrease txp\n");
- return TPC_ACTION_DECREASE;
- }
- }
-
- /* next, increase if needed */
- if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) &&
- strong != TPC_INVALID) {
- if (weak_tpt == IWL_INVALID_VALUE &&
- strong_tpt != IWL_INVALID_VALUE &&
- current_tpt < strong_tpt) {
- IWL_DEBUG_RATE(mvm,
- "higher txp has better tpt. increase txp\n");
- return TPC_ACTION_INCREASE;
- }
-
- if (weak_tpt < current_tpt &&
- (strong_tpt == IWL_INVALID_VALUE ||
- strong_tpt > current_tpt)) {
- IWL_DEBUG_RATE(mvm,
- "lower txp has worse tpt. increase txp\n");
- return TPC_ACTION_INCREASE;
- }
- }
-
- IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n");
- return TPC_ACTION_STAY;
-}
-
-static bool rs_tpc_perform(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- struct iwl_scale_tbl_info *tbl)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct ieee80211_vif *vif = mvm_sta->vif;
- struct ieee80211_chanctx_conf *chanctx_conf;
- enum ieee80211_band band;
- struct iwl_rate_scale_data *window;
- struct rs_rate *rate = &tbl->rate;
- enum tpc_action action;
- s32 sr;
- u8 cur = lq_sta->lq.reduced_tpc;
- int current_tpt;
- int weak, strong;
- int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
- if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
- IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n",
- lq_sta->pers.dbg_fixed_txp_reduction);
- lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction;
- return cur != lq_sta->pers.dbg_fixed_txp_reduction;
- }
-#endif
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
- if (WARN_ON(!chanctx_conf))
- band = IEEE80211_NUM_BANDS;
- else
- band = chanctx_conf->def.chan->band;
- rcu_read_unlock();
-
- if (!rs_tpc_allowed(mvm, vif, rate, band)) {
- IWL_DEBUG_RATE(mvm,
- "tpc is not allowed. remove txp restrictions\n");
- lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
- return cur != TPC_NO_REDUCTION;
- }
-
- rs_get_adjacent_txp(mvm, cur, &weak, &strong);
-
- /* Collect measured throughputs for current and adjacent rates */
- window = tbl->tpc_win;
- sr = window[cur].success_ratio;
- current_tpt = window[cur].average_tpt;
- if (weak != TPC_INVALID)
- weak_tpt = window[weak].average_tpt;
- if (strong != TPC_INVALID)
- strong_tpt = window[strong].average_tpt;
-
- IWL_DEBUG_RATE(mvm,
- "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n",
- cur, current_tpt, sr, weak, strong,
- weak_tpt, strong_tpt);
-
- action = rs_get_tpc_action(mvm, sr, weak, strong,
- current_tpt, weak_tpt, strong_tpt);
-
- /* override actions if we are on the edge */
- if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) {
- IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n");
- action = TPC_ACTION_STAY;
- } else if (strong == TPC_INVALID &&
- (action == TPC_ACTION_INCREASE ||
- action == TPC_ACTION_NO_RESTIRCTION)) {
- IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n");
- action = TPC_ACTION_STAY;
- }
-
- switch (action) {
- case TPC_ACTION_DECREASE:
- lq_sta->lq.reduced_tpc = weak;
- return true;
- case TPC_ACTION_INCREASE:
- lq_sta->lq.reduced_tpc = strong;
- return true;
- case TPC_ACTION_NO_RESTIRCTION:
- lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
- return true;
- case TPC_ACTION_STAY:
- /* do nothing */
- break;
- }
- return false;
-}
-
-/*
- * Do rate scaling and search for new modulation mode.
- */
-static void rs_rate_scale_perform(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- int tid)
-{
- int low = IWL_RATE_INVALID;
- int high = IWL_RATE_INVALID;
- int index;
- struct iwl_rate_scale_data *window = NULL;
- int current_tpt = IWL_INVALID_VALUE;
- int low_tpt = IWL_INVALID_VALUE;
- int high_tpt = IWL_INVALID_VALUE;
- u32 fail_count;
- enum rs_action scale_action = RS_ACTION_STAY;
- u16 rate_mask;
- u8 update_lq = 0;
- struct iwl_scale_tbl_info *tbl, *tbl1;
- u8 active_tbl = 0;
- u8 done_search = 0;
- u16 high_low;
- s32 sr;
- u8 prev_agg = lq_sta->is_agg;
- struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_tid_data *tid_data;
- struct rs_rate *rate;
-
- lq_sta->is_agg = !!sta_priv->agg_tids;
-
- /*
- * Select rate-scale / modulation-mode table to work with in
- * the rest of this function: "search" if searching for better
- * modulation mode, or "active" if doing rate scaling within a mode.
- */
- if (!lq_sta->search_better_tbl)
- active_tbl = lq_sta->active_tbl;
- else
- active_tbl = 1 - lq_sta->active_tbl;
-
- tbl = &(lq_sta->lq_info[active_tbl]);
- rate = &tbl->rate;
-
- if (prev_agg != lq_sta->is_agg) {
- IWL_DEBUG_RATE(mvm,
- "Aggregation changed: prev %d current %d. Update expected TPT table\n",
- prev_agg, lq_sta->is_agg);
- rs_set_expected_tpt_table(lq_sta, tbl);
- rs_rate_scale_clear_tbl_windows(mvm, tbl);
- }
-
- /* current tx rate */
- index = rate->index;
-
- /* rates available for this association, and for modulation mode */
- rate_mask = rs_get_supported_rates(lq_sta, rate);
-
- if (!(BIT(index) & rate_mask)) {
- IWL_ERR(mvm, "Current Rate is not valid\n");
- if (lq_sta->search_better_tbl) {
- /* revert to active table if search table is not valid*/
- rate->type = LQ_NONE;
- lq_sta->search_better_tbl = 0;
- tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
- }
- return;
- }
-
- /* Get expected throughput table and history window for current rate */
- if (!tbl->expected_tpt) {
- IWL_ERR(mvm, "tbl->expected_tpt is NULL\n");
- return;
- }
-
- /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
- window = &(tbl->win[index]);
-
- /*
- * If there is not enough history to calculate actual average
- * throughput, keep analyzing results of more tx frames, without
- * changing rate or mode (bypass most of the rest of this function).
- * Set up new rate table in uCode only if old rate is not supported
- * in current association (use new rate found above).
- */
- fail_count = window->counter - window->success_counter;
- if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) &&
- (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) {
- IWL_DEBUG_RATE(mvm,
- "%s: Test Window: succ %d total %d\n",
- rs_pretty_rate(rate),
- window->success_counter, window->counter);
-
- /* Can't calculate this yet; not enough history */
- window->average_tpt = IWL_INVALID_VALUE;
-
- /* Should we stay with this modulation mode,
- * or search for a new one? */
- rs_stay_in_table(lq_sta, false);
-
- return;
- }
-
- /* If we are searching for better modulation mode, check success. */
- if (lq_sta->search_better_tbl) {
- /* If good success, continue using the "search" mode;
- * no need to send new link quality command, since we're
- * continuing to use the setup that we've been trying. */
- if (window->average_tpt > lq_sta->last_tpt) {
- IWL_DEBUG_RATE(mvm,
- "SWITCHING TO NEW TABLE SR: %d "
- "cur-tpt %d old-tpt %d\n",
- window->success_ratio,
- window->average_tpt,
- lq_sta->last_tpt);
-
- /* Swap tables; "search" becomes "active" */
- lq_sta->active_tbl = active_tbl;
- current_tpt = window->average_tpt;
- /* Else poor success; go back to mode in "active" table */
- } else {
- IWL_DEBUG_RATE(mvm,
- "GOING BACK TO THE OLD TABLE: SR %d "
- "cur-tpt %d old-tpt %d\n",
- window->success_ratio,
- window->average_tpt,
- lq_sta->last_tpt);
-
- /* Nullify "search" table */
- rate->type = LQ_NONE;
-
- /* Revert to "active" table */
- active_tbl = lq_sta->active_tbl;
- tbl = &(lq_sta->lq_info[active_tbl]);
-
- /* Revert to "active" rate and throughput info */
- index = tbl->rate.index;
- current_tpt = lq_sta->last_tpt;
-
- /* Need to set up a new rate table in uCode */
- update_lq = 1;
- }
-
- /* Either way, we've made a decision; modulation mode
- * search is done, allow rate adjustment next time. */
- lq_sta->search_better_tbl = 0;
- done_search = 1; /* Don't switch modes below! */
- goto lq_update;
- }
-
- /* (Else) not in search of better modulation mode, try for better
- * starting rate, while staying in this mode. */
- high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type);
- low = high_low & 0xff;
- high = (high_low >> 8) & 0xff;
-
- /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
-
- sr = window->success_ratio;
-
- /* Collect measured throughputs for current and adjacent rates */
- current_tpt = window->average_tpt;
- if (low != IWL_RATE_INVALID)
- low_tpt = tbl->win[low].average_tpt;
- if (high != IWL_RATE_INVALID)
- high_tpt = tbl->win[high].average_tpt;
-
- IWL_DEBUG_RATE(mvm,
- "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n",
- rs_pretty_rate(rate), current_tpt, sr,
- low, high, low_tpt, high_tpt);
-
- scale_action = rs_get_rate_action(mvm, tbl, sr, low, high,
- current_tpt, low_tpt, high_tpt);
-
- /* Force a search in case BT doesn't like us being in MIMO */
- if (is_mimo(rate) &&
- !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) {
- IWL_DEBUG_RATE(mvm,
- "BT Coex forbids MIMO. Search for new config\n");
- rs_stay_in_table(lq_sta, true);
- goto lq_update;
- }
-
- switch (scale_action) {
- case RS_ACTION_DOWNSCALE:
- /* Decrease starting rate, update uCode's rate table */
- if (low != IWL_RATE_INVALID) {
- update_lq = 1;
- index = low;
- } else {
- IWL_DEBUG_RATE(mvm,
- "At the bottom rate. Can't decrease\n");
- }
-
- break;
- case RS_ACTION_UPSCALE:
- /* Increase starting rate, update uCode's rate table */
- if (high != IWL_RATE_INVALID) {
- update_lq = 1;
- index = high;
- } else {
- IWL_DEBUG_RATE(mvm,
- "At the top rate. Can't increase\n");
- }
-
- break;
- case RS_ACTION_STAY:
- /* No change */
- if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN)
- update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
- break;
- default:
- break;
- }
-
-lq_update:
- /* Replace uCode's rate table for the destination station. */
- if (update_lq) {
- tbl->rate.index = index;
- if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK)
- rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action);
- rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
- }
-
- rs_stay_in_table(lq_sta, false);
-
- /*
- * Search for new modulation mode if we're:
- * 1) Not changing rates right now
- * 2) Not just finishing up a search
- * 3) Allowing a new search
- */
- if (!update_lq && !done_search &&
- lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED
- && window->counter) {
- enum rs_column next_column;
-
- /* Save current throughput to compare with "search" throughput*/
- lq_sta->last_tpt = current_tpt;
-
- IWL_DEBUG_RATE(mvm,
- "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n",
- update_lq, done_search, lq_sta->rs_state,
- window->counter);
-
- next_column = rs_get_next_column(mvm, lq_sta, sta, tbl);
- if (next_column != RS_COLUMN_INVALID) {
- int ret = rs_switch_to_column(mvm, lq_sta, sta,
- next_column);
- if (!ret)
- lq_sta->search_better_tbl = 1;
- } else {
- IWL_DEBUG_RATE(mvm,
- "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n");
- lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED;
- }
-
- /* If new "search" mode was selected, set up in uCode table */
- if (lq_sta->search_better_tbl) {
- /* Access the "search" table, clear its history. */
- tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- rs_rate_scale_clear_tbl_windows(mvm, tbl);
-
- /* Use new "search" start rate */
- index = tbl->rate.index;
-
- rs_dump_rate(mvm, &tbl->rate,
- "Switch to SEARCH TABLE:");
- rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
- } else {
- done_search = 1;
- }
- }
-
- if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) {
- /* If the "active" (non-search) mode was legacy,
- * and we've tried switching antennas,
- * but we haven't been able to try HT modes (not available),
- * stay with best antenna legacy modulation for a while
- * before next round of mode comparisons. */
- tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
- if (is_legacy(&tbl1->rate)) {
- IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n");
-
- if (tid != IWL_MAX_TID_COUNT) {
- tid_data = &sta_priv->tid_data[tid];
- if (tid_data->state != IWL_AGG_OFF) {
- IWL_DEBUG_RATE(mvm,
- "Stop aggregation on tid %d\n",
- tid);
- ieee80211_stop_tx_ba_session(sta, tid);
- }
- }
- rs_set_stay_in_table(mvm, 1, lq_sta);
- } else {
- /* If we're in an HT mode, and all 3 mode switch actions
- * have been tried and compared, stay in this best modulation
- * mode for a while before next round of mode comparisons. */
- if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
- (lq_sta->tx_agg_tid_en & (1 << tid)) &&
- (tid != IWL_MAX_TID_COUNT)) {
- tid_data = &sta_priv->tid_data[tid];
- if (tid_data->state == IWL_AGG_OFF) {
- IWL_DEBUG_RATE(mvm,
- "try to aggregate tid %d\n",
- tid);
- rs_tl_turn_on_agg(mvm, tid,
- lq_sta, sta);
- }
- }
- rs_set_stay_in_table(mvm, 0, lq_sta);
- }
- }
-}
-
-struct rs_init_rate_info {
- s8 rssi;
- u8 rate_idx;
-};
-
-static const struct rs_init_rate_info rs_optimal_rates_24ghz_legacy[] = {
- { -60, IWL_RATE_54M_INDEX },
- { -64, IWL_RATE_48M_INDEX },
- { -68, IWL_RATE_36M_INDEX },
- { -80, IWL_RATE_24M_INDEX },
- { -84, IWL_RATE_18M_INDEX },
- { -85, IWL_RATE_12M_INDEX },
- { -86, IWL_RATE_11M_INDEX },
- { -88, IWL_RATE_5M_INDEX },
- { -90, IWL_RATE_2M_INDEX },
- { S8_MIN, IWL_RATE_1M_INDEX },
-};
-
-static const struct rs_init_rate_info rs_optimal_rates_5ghz_legacy[] = {
- { -60, IWL_RATE_54M_INDEX },
- { -64, IWL_RATE_48M_INDEX },
- { -72, IWL_RATE_36M_INDEX },
- { -80, IWL_RATE_24M_INDEX },
- { -84, IWL_RATE_18M_INDEX },
- { -85, IWL_RATE_12M_INDEX },
- { -87, IWL_RATE_9M_INDEX },
- { S8_MIN, IWL_RATE_6M_INDEX },
-};
-
-static const struct rs_init_rate_info rs_optimal_rates_ht[] = {
- { -60, IWL_RATE_MCS_7_INDEX },
- { -64, IWL_RATE_MCS_6_INDEX },
- { -68, IWL_RATE_MCS_5_INDEX },
- { -72, IWL_RATE_MCS_4_INDEX },
- { -80, IWL_RATE_MCS_3_INDEX },
- { -84, IWL_RATE_MCS_2_INDEX },
- { -85, IWL_RATE_MCS_1_INDEX },
- { S8_MIN, IWL_RATE_MCS_0_INDEX},
-};
-
-static const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = {
- { -60, IWL_RATE_MCS_8_INDEX },
- { -64, IWL_RATE_MCS_7_INDEX },
- { -68, IWL_RATE_MCS_6_INDEX },
- { -72, IWL_RATE_MCS_5_INDEX },
- { -80, IWL_RATE_MCS_4_INDEX },
- { -84, IWL_RATE_MCS_3_INDEX },
- { -85, IWL_RATE_MCS_2_INDEX },
- { -87, IWL_RATE_MCS_1_INDEX },
- { S8_MIN, IWL_RATE_MCS_0_INDEX},
-};
-
-static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = {
- { -60, IWL_RATE_MCS_9_INDEX },
- { -64, IWL_RATE_MCS_8_INDEX },
- { -68, IWL_RATE_MCS_7_INDEX },
- { -72, IWL_RATE_MCS_6_INDEX },
- { -80, IWL_RATE_MCS_5_INDEX },
- { -84, IWL_RATE_MCS_4_INDEX },
- { -85, IWL_RATE_MCS_3_INDEX },
- { -87, IWL_RATE_MCS_2_INDEX },
- { -88, IWL_RATE_MCS_1_INDEX },
- { S8_MIN, IWL_RATE_MCS_0_INDEX },
-};
-
-/* Init the optimal rate based on STA caps
- * This combined with rssi is used to report the last tx rate
- * to userspace when we haven't transmitted enough frames.
- */
-static void rs_init_optimal_rate(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta)
-{
- struct rs_rate *rate = &lq_sta->optimal_rate;
-
- if (lq_sta->max_mimo2_rate_idx != IWL_RATE_INVALID)
- rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2;
- else if (lq_sta->max_siso_rate_idx != IWL_RATE_INVALID)
- rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO;
- else if (lq_sta->band == IEEE80211_BAND_5GHZ)
- rate->type = LQ_LEGACY_A;
- else
- rate->type = LQ_LEGACY_G;
-
- rate->bw = rs_bw_from_sta_bw(sta);
- rate->sgi = rs_sgi_allow(mvm, sta, rate, NULL);
-
- /* ANT/LDPC/STBC aren't relevant for the rate reported to userspace */
-
- if (is_mimo(rate)) {
- lq_sta->optimal_rate_mask = lq_sta->active_mimo2_rate;
- } else if (is_siso(rate)) {
- lq_sta->optimal_rate_mask = lq_sta->active_siso_rate;
- } else {
- lq_sta->optimal_rate_mask = lq_sta->active_legacy_rate;
-
- if (lq_sta->band == IEEE80211_BAND_5GHZ) {
- lq_sta->optimal_rates = rs_optimal_rates_5ghz_legacy;
- lq_sta->optimal_nentries =
- ARRAY_SIZE(rs_optimal_rates_5ghz_legacy);
- } else {
- lq_sta->optimal_rates = rs_optimal_rates_24ghz_legacy;
- lq_sta->optimal_nentries =
- ARRAY_SIZE(rs_optimal_rates_24ghz_legacy);
- }
- }
-
- if (is_vht(rate)) {
- if (rate->bw == RATE_MCS_CHAN_WIDTH_20) {
- lq_sta->optimal_rates = rs_optimal_rates_vht_20mhz;
- lq_sta->optimal_nentries =
- ARRAY_SIZE(rs_optimal_rates_vht_20mhz);
- } else {
- lq_sta->optimal_rates = rs_optimal_rates_vht_40_80mhz;
- lq_sta->optimal_nentries =
- ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz);
- }
- } else if (is_ht(rate)) {
- lq_sta->optimal_rates = rs_optimal_rates_ht;
- lq_sta->optimal_nentries = ARRAY_SIZE(rs_optimal_rates_ht);
- }
-}
-
-/* Compute the optimal rate index based on RSSI */
-static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta)
-{
- struct rs_rate *rate = &lq_sta->optimal_rate;
- int i;
-
- rate->index = find_first_bit(&lq_sta->optimal_rate_mask,
- BITS_PER_LONG);
-
- for (i = 0; i < lq_sta->optimal_nentries; i++) {
- int rate_idx = lq_sta->optimal_rates[i].rate_idx;
-
- if ((lq_sta->pers.last_rssi >= lq_sta->optimal_rates[i].rssi) &&
- (BIT(rate_idx) & lq_sta->optimal_rate_mask)) {
- rate->index = rate_idx;
- break;
- }
- }
-
- return rate;
-}
-
-/* Choose an initial legacy rate and antenna to use based on the RSSI
- * of last Rx
- */
-static void rs_get_initial_rate(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- enum ieee80211_band band,
- struct rs_rate *rate)
-{
- int i, nentries;
- s8 best_rssi = S8_MIN;
- u8 best_ant = ANT_NONE;
- u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
- const struct rs_init_rate_info *initial_rates;
-
- for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) {
- if (!(lq_sta->pers.chains & BIT(i)))
- continue;
-
- if (lq_sta->pers.chain_signal[i] > best_rssi) {
- best_rssi = lq_sta->pers.chain_signal[i];
- best_ant = BIT(i);
- }
- }
-
- IWL_DEBUG_RATE(mvm, "Best ANT: %s Best RSSI: %d\n",
- rs_pretty_ant(best_ant), best_rssi);
-
- if (best_ant != ANT_A && best_ant != ANT_B)
- rate->ant = first_antenna(valid_tx_ant);
- else
- rate->ant = best_ant;
-
- rate->sgi = false;
- rate->ldpc = false;
- rate->bw = RATE_MCS_CHAN_WIDTH_20;
-
- rate->index = find_first_bit(&lq_sta->active_legacy_rate,
- BITS_PER_LONG);
-
- if (band == IEEE80211_BAND_5GHZ) {
- rate->type = LQ_LEGACY_A;
- initial_rates = rs_optimal_rates_5ghz_legacy;
- nentries = ARRAY_SIZE(rs_optimal_rates_5ghz_legacy);
- } else {
- rate->type = LQ_LEGACY_G;
- initial_rates = rs_optimal_rates_24ghz_legacy;
- nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy);
- }
-
- if (IWL_MVM_RS_RSSI_BASED_INIT_RATE) {
- for (i = 0; i < nentries; i++) {
- int rate_idx = initial_rates[i].rate_idx;
- if ((best_rssi >= initial_rates[i].rssi) &&
- (BIT(rate_idx) & lq_sta->active_legacy_rate)) {
- rate->index = rate_idx;
- break;
- }
- }
- }
-
- IWL_DEBUG_RATE(mvm, "rate_idx %d ANT %s\n", rate->index,
- rs_pretty_ant(rate->ant));
-}
-
-/* Save info about RSSI of last Rx */
-void rs_update_last_rssi(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_rx_status *rx_status)
-{
- int i;
-
- lq_sta->pers.chains = rx_status->chains;
- lq_sta->pers.chain_signal[0] = rx_status->chain_signal[0];
- lq_sta->pers.chain_signal[1] = rx_status->chain_signal[1];
- lq_sta->pers.chain_signal[2] = rx_status->chain_signal[2];
- lq_sta->pers.last_rssi = S8_MIN;
-
- for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) {
- if (!(lq_sta->pers.chains & BIT(i)))
- continue;
-
- if (lq_sta->pers.chain_signal[i] > lq_sta->pers.last_rssi)
- lq_sta->pers.last_rssi = lq_sta->pers.chain_signal[i];
- }
-}
-
-/**
- * rs_initialize_lq - Initialize a station's hardware rate table
- *
- * The uCode's station table contains a table of fallback rates
- * for automatic fallback during transmission.
- *
- * NOTE: This sets up a default set of values. These will be replaced later
- * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
- * rc80211_simple.
- *
- * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
- * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
- * which requires station table entry to exist).
- */
-static void rs_initialize_lq(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- enum ieee80211_band band,
- bool init)
-{
- struct iwl_scale_tbl_info *tbl;
- struct rs_rate *rate;
- u8 active_tbl = 0;
-
- if (!sta || !lq_sta)
- return;
-
- if (!lq_sta->search_better_tbl)
- active_tbl = lq_sta->active_tbl;
- else
- active_tbl = 1 - lq_sta->active_tbl;
-
- tbl = &(lq_sta->lq_info[active_tbl]);
- rate = &tbl->rate;
-
- rs_get_initial_rate(mvm, lq_sta, band, rate);
- rs_init_optimal_rate(mvm, sta, lq_sta);
-
- WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B);
- if (rate->ant == ANT_A)
- tbl->column = RS_COLUMN_LEGACY_ANT_A;
- else
- tbl->column = RS_COLUMN_LEGACY_ANT_B;
-
- rs_set_expected_tpt_table(lq_sta, tbl);
- rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
- /* TODO restore station should remember the lq cmd */
- iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init);
-}
-
-static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
- struct ieee80211_tx_rate_control *txrc)
-{
- struct sk_buff *skb = txrc->skb;
- struct iwl_op_mode *op_mode __maybe_unused =
- (struct iwl_op_mode *)mvm_r;
- struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl_lq_sta *lq_sta = mvm_sta;
- struct rs_rate *optimal_rate;
- u32 last_ucode_rate;
-
- if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) {
- /* if vif isn't initialized mvm doesn't know about
- * this station, so don't do anything with the it
- */
- sta = NULL;
- mvm_sta = NULL;
- }
-
- /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
-
- /* Treat uninitialized rate scaling data same as non-existing. */
- if (lq_sta && !lq_sta->pers.drv) {
- IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
- mvm_sta = NULL;
- }
-
- /* Send management frames and NO_ACK data using lowest rate. */
- if (rate_control_send_low(sta, mvm_sta, txrc))
- return;
-
- iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags,
- info->band, &info->control.rates[0]);
- info->control.rates[0].count = 1;
-
- /* Report the optimal rate based on rssi and STA caps if we haven't
- * converged yet (too little traffic) or exploring other modulations
- */
- if (lq_sta->rs_state != RS_STATE_STAY_IN_COLUMN) {
- optimal_rate = rs_get_optimal_rate(mvm, lq_sta);
- last_ucode_rate = ucode_rate_from_rs_rate(mvm,
- optimal_rate);
- iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band,
- &txrc->reported_rate);
- }
-}
-
-static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
- gfp_t gfp)
-{
- struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
-
- IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
-
- lq_sta->pers.drv = mvm;
-#ifdef CONFIG_MAC80211_DEBUGFS
- lq_sta->pers.dbg_fixed_rate = 0;
- lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID;
- lq_sta->pers.ss_force = RS_SS_FORCE_NONE;
-#endif
- lq_sta->pers.chains = 0;
- memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal));
- lq_sta->pers.last_rssi = S8_MIN;
-
- return &sta_priv->lq_sta;
-}
-
-static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
- int nss)
-{
- u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
- (0x3 << (2 * (nss - 1)));
- rx_mcs >>= (2 * (nss - 1));
-
- if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7)
- return IWL_RATE_MCS_7_INDEX;
- else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8)
- return IWL_RATE_MCS_8_INDEX;
- else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9)
- return IWL_RATE_MCS_9_INDEX;
-
- WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED);
- return -1;
-}
-
-static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
- struct ieee80211_sta_vht_cap *vht_cap,
- struct iwl_lq_sta *lq_sta)
-{
- int i;
- int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1);
-
- if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
- for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
- if (i == IWL_RATE_9M_INDEX)
- continue;
-
- /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
- if (i == IWL_RATE_MCS_9_INDEX &&
- sta->bandwidth == IEEE80211_STA_RX_BW_20)
- continue;
-
- lq_sta->active_siso_rate |= BIT(i);
- }
- }
-
- if (sta->rx_nss < 2)
- return;
-
- highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2);
- if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
- for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
- if (i == IWL_RATE_9M_INDEX)
- continue;
-
- /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
- if (i == IWL_RATE_MCS_9_INDEX &&
- sta->bandwidth == IEEE80211_STA_RX_BW_20)
- continue;
-
- lq_sta->active_mimo2_rate |= BIT(i);
- }
- }
-}
-
-static void rs_ht_init(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_sta_ht_cap *ht_cap)
-{
- /* active_siso_rate mask includes 9 MBits (bit 5),
- * and CCK (bits 0-3), supp_rates[] does not;
- * shift to convert format, force 9 MBits off.
- */
- lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
- lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
- lq_sta->active_siso_rate &= ~((u16)0x2);
- lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
-
- lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
- lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
- lq_sta->active_mimo2_rate &= ~((u16)0x2);
- lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
-
- if (mvm->cfg->ht_params->ldpc &&
- (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING))
- lq_sta->ldpc = true;
-
- if (mvm->cfg->ht_params->stbc &&
- (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
- (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC))
- lq_sta->stbc_capable = true;
-
- lq_sta->is_vht = false;
-}
-
-static void rs_vht_init(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_sta_vht_cap *vht_cap)
-{
- rs_vht_set_enabled_rates(sta, vht_cap, lq_sta);
-
- if (mvm->cfg->ht_params->ldpc &&
- (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))
- lq_sta->ldpc = true;
-
- if (mvm->cfg->ht_params->stbc &&
- (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
- (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))
- lq_sta->stbc_capable = true;
-
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
- (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
- (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE))
- lq_sta->bfer_capable = true;
-
- lq_sta->is_vht = true;
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm)
-{
- spin_lock_bh(&mvm->drv_stats_lock);
- memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats));
- spin_unlock_bh(&mvm->drv_stats_lock);
-}
-
-void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)
-{
- u8 nss = 0, mcs = 0;
-
- spin_lock(&mvm->drv_stats_lock);
-
- if (agg)
- mvm->drv_rx_stats.agg_frames++;
-
- mvm->drv_rx_stats.success_frames++;
-
- switch (rate & RATE_MCS_CHAN_WIDTH_MSK) {
- case RATE_MCS_CHAN_WIDTH_20:
- mvm->drv_rx_stats.bw_20_frames++;
- break;
- case RATE_MCS_CHAN_WIDTH_40:
- mvm->drv_rx_stats.bw_40_frames++;
- break;
- case RATE_MCS_CHAN_WIDTH_80:
- mvm->drv_rx_stats.bw_80_frames++;
- break;
- default:
- WARN_ONCE(1, "bad BW. rate 0x%x", rate);
- }
-
- if (rate & RATE_MCS_HT_MSK) {
- mvm->drv_rx_stats.ht_frames++;
- mcs = rate & RATE_HT_MCS_RATE_CODE_MSK;
- nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1;
- } else if (rate & RATE_MCS_VHT_MSK) {
- mvm->drv_rx_stats.vht_frames++;
- mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
- nss = ((rate & RATE_VHT_MCS_NSS_MSK) >>
- RATE_VHT_MCS_NSS_POS) + 1;
- } else {
- mvm->drv_rx_stats.legacy_frames++;
- }
-
- if (nss == 1)
- mvm->drv_rx_stats.siso_frames++;
- else if (nss == 2)
- mvm->drv_rx_stats.mimo2_frames++;
-
- if (rate & RATE_MCS_SGI_MSK)
- mvm->drv_rx_stats.sgi_frames++;
- else
- mvm->drv_rx_stats.ngi_frames++;
-
- mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate;
- mvm->drv_rx_stats.last_frame_idx =
- (mvm->drv_rx_stats.last_frame_idx + 1) %
- ARRAY_SIZE(mvm->drv_rx_stats.last_rates);
-
- spin_unlock(&mvm->drv_stats_lock);
-}
-#endif
-
-/*
- * Called after adding a new station to initialize rate scaling
- */
-void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- enum ieee80211_band band, bool init)
-{
- int i, j;
- struct ieee80211_hw *hw = mvm->hw;
- struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
- struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
- struct ieee80211_supported_band *sband;
- unsigned long supp; /* must be unsigned long for for_each_set_bit */
-
- /* clear all non-persistent lq data */
- memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
-
- sband = hw->wiphy->bands[band];
-
- lq_sta->lq.sta_id = sta_priv->sta_id;
-
- for (j = 0; j < LQ_SIZE; j++)
- rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]);
-
- lq_sta->flush_timer = 0;
- lq_sta->last_tx = jiffies;
-
- IWL_DEBUG_RATE(mvm,
- "LQ: *** rate scale station global init for station %d ***\n",
- sta_priv->sta_id);
- /* TODO: what is a good starting rate for STA? About middle? Maybe not
- * the lowest or the highest rate.. Could consider using RSSI from
- * previous packets? Need to have IEEE 802.1X auth succeed immediately
- * after assoc.. */
-
- lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX;
- lq_sta->band = sband->band;
- /*
- * active legacy rates as per supported rates bitmap
- */
- supp = sta->supp_rates[sband->band];
- lq_sta->active_legacy_rate = 0;
- for_each_set_bit(i, &supp, BITS_PER_LONG)
- lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
-
- /* TODO: should probably account for rx_highest for both HT/VHT */
- if (!vht_cap || !vht_cap->vht_supported)
- rs_ht_init(mvm, sta, lq_sta, ht_cap);
- else
- rs_vht_init(mvm, sta, lq_sta, vht_cap);
-
- lq_sta->max_legacy_rate_idx =
- rs_get_max_rate_from_mask(lq_sta->active_legacy_rate);
- lq_sta->max_siso_rate_idx =
- rs_get_max_rate_from_mask(lq_sta->active_siso_rate);
- lq_sta->max_mimo2_rate_idx =
- rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate);
-
- IWL_DEBUG_RATE(mvm,
- "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n",
- lq_sta->active_legacy_rate,
- lq_sta->active_siso_rate,
- lq_sta->active_mimo2_rate,
- lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable,
- lq_sta->bfer_capable);
- IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n",
- lq_sta->max_legacy_rate_idx,
- lq_sta->max_siso_rate_idx,
- lq_sta->max_mimo2_rate_idx);
-
- /* These values will be overridden later */
- lq_sta->lq.single_stream_ant_msk =
- first_antenna(iwl_mvm_get_valid_tx_ant(mvm));
- lq_sta->lq.dual_stream_ant_msk = ANT_AB;
-
- /* as default allow aggregation for all tids */
- lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
- lq_sta->is_agg = 0;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- iwl_mvm_reset_frame_stats(mvm);
-#endif
- rs_initialize_lq(mvm, sta, lq_sta, band, init);
-}
-
-static void rs_rate_update(void *mvm_r,
- struct ieee80211_supported_band *sband,
- struct cfg80211_chan_def *chandef,
- struct ieee80211_sta *sta, void *priv_sta,
- u32 changed)
-{
- u8 tid;
- struct iwl_op_mode *op_mode =
- (struct iwl_op_mode *)mvm_r;
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-
- if (!iwl_mvm_sta_from_mac80211(sta)->vif)
- return;
-
- /* Stop any ongoing aggregations as rs starts off assuming no agg */
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
- ieee80211_stop_tx_ba_session(sta, tid);
-
- iwl_mvm_rs_rate_init(mvm, sta, sband->band, false);
-}
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
- struct iwl_lq_cmd *lq_cmd,
- enum ieee80211_band band,
- u32 ucode_rate)
-{
- struct rs_rate rate;
- int i;
- int num_rates = ARRAY_SIZE(lq_cmd->rs_table);
- __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate);
- u8 ant = (ucode_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS;
-
- for (i = 0; i < num_rates; i++)
- lq_cmd->rs_table[i] = ucode_rate_le32;
-
- rs_rate_from_ucode_rate(ucode_rate, band, &rate);
-
- if (is_mimo(&rate))
- lq_cmd->mimo_delim = num_rates - 1;
- else
- lq_cmd->mimo_delim = 0;
-
- lq_cmd->reduced_tpc = 0;
-
- if (num_of_ant(ant) == 1)
- lq_cmd->single_stream_ant_msk = ant;
-
- lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
-}
-#endif /* CONFIG_MAC80211_DEBUGFS */
-
-static void rs_fill_rates_for_column(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct rs_rate *rate,
- __le32 *rs_table, int *rs_table_index,
- int num_rates, int num_retries,
- u8 valid_tx_ant, bool toggle_ant)
-{
- int i, j;
- __le32 ucode_rate;
- bool bottom_reached = false;
- int prev_rate_idx = rate->index;
- int end = LINK_QUAL_MAX_RETRY_NUM;
- int index = *rs_table_index;
-
- for (i = 0; i < num_rates && index < end; i++) {
- for (j = 0; j < num_retries && index < end; j++, index++) {
- ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm,
- rate));
- rs_table[index] = ucode_rate;
- if (toggle_ant)
- rs_toggle_antenna(valid_tx_ant, rate);
- }
-
- prev_rate_idx = rate->index;
- bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate);
- if (bottom_reached && !is_legacy(rate))
- break;
- }
-
- if (!bottom_reached && !is_legacy(rate))
- rate->index = prev_rate_idx;
-
- *rs_table_index = index;
-}
-
-/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI
- * column the rate table should look like this:
- *
- * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
- * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
- * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
- * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
- * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
- * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
- * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI
- * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI
- * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI
- * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps
- * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps
- * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps
- * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps
- * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps
- * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps
- * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps
- */
-static void rs_build_rates_table(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- const struct rs_rate *initial_rate)
-{
- struct rs_rate rate;
- int num_rates, num_retries, index = 0;
- u8 valid_tx_ant = 0;
- struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
- bool toggle_ant = false;
-
- memcpy(&rate, initial_rate, sizeof(rate));
-
- valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
-
- /* TODO: remove old API when min FW API hits 14 */
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS) &&
- rs_stbc_allow(mvm, sta, lq_sta))
- rate.stbc = true;
-
- if (is_siso(&rate)) {
- num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES;
- num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE;
- } else if (is_mimo(&rate)) {
- num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES;
- num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE;
- } else {
- num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES;
- num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES;
- toggle_ant = true;
- }
-
- rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
- num_rates, num_retries, valid_tx_ant,
- toggle_ant);
-
- rs_get_lower_rate_down_column(lq_sta, &rate);
-
- if (is_siso(&rate)) {
- num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES;
- num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES;
- lq_cmd->mimo_delim = index;
- } else if (is_legacy(&rate)) {
- num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES;
- num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES;
- } else {
- WARN_ON_ONCE(1);
- }
-
- toggle_ant = true;
-
- rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
- num_rates, num_retries, valid_tx_ant,
- toggle_ant);
-
- rs_get_lower_rate_down_column(lq_sta, &rate);
-
- num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES;
- num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES;
-
- rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
- num_rates, num_retries, valid_tx_ant,
- toggle_ant);
-
-}
-
-struct rs_bfer_active_iter_data {
- struct ieee80211_sta *exclude_sta;
- struct iwl_mvm_sta *bfer_mvmsta;
-};
-
-static void rs_bfer_active_iter(void *_data,
- struct ieee80211_sta *sta)
-{
- struct rs_bfer_active_iter_data *data = _data;
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq;
- u32 ss_params = le32_to_cpu(lq_cmd->ss_params);
-
- if (sta == data->exclude_sta)
- return;
-
- /* The current sta has BFER allowed */
- if (ss_params & LQ_SS_BFER_ALLOWED) {
- WARN_ON_ONCE(data->bfer_mvmsta != NULL);
-
- data->bfer_mvmsta = mvmsta;
- }
-}
-
-static int rs_bfer_priority(struct iwl_mvm_sta *sta)
-{
- int prio = -1;
- enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif);
-
- switch (viftype) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_P2P_GO:
- prio = 3;
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- prio = 2;
- break;
- case NL80211_IFTYPE_STATION:
- prio = 1;
- break;
- default:
- WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id);
- prio = -1;
- }
-
- return prio;
-}
-
-/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */
-static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1,
- struct iwl_mvm_sta *sta2)
-{
- int prio1 = rs_bfer_priority(sta1);
- int prio2 = rs_bfer_priority(sta2);
-
- if (prio1 > prio2)
- return 1;
- if (prio1 < prio2)
- return -1;
- return 0;
-}
-
-static void rs_set_lq_ss_params(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- const struct rs_rate *initial_rate)
-{
- struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct rs_bfer_active_iter_data data = {
- .exclude_sta = sta,
- .bfer_mvmsta = NULL,
- };
- struct iwl_mvm_sta *bfer_mvmsta = NULL;
- u32 ss_params = LQ_SS_PARAMS_VALID;
-
- if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
- goto out;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
- /* Check if forcing the decision is configured.
- * Note that SISO is forced by not allowing STBC or BFER
- */
- if (lq_sta->pers.ss_force == RS_SS_FORCE_STBC)
- ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE);
- else if (lq_sta->pers.ss_force == RS_SS_FORCE_BFER)
- ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE);
-
- if (lq_sta->pers.ss_force != RS_SS_FORCE_NONE) {
- IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n",
- lq_sta->pers.ss_force);
- goto out;
- }
-#endif
-
- if (lq_sta->stbc_capable)
- ss_params |= LQ_SS_STBC_1SS_ALLOWED;
-
- if (!lq_sta->bfer_capable)
- goto out;
-
- ieee80211_iterate_stations_atomic(mvm->hw,
- rs_bfer_active_iter,
- &data);
- bfer_mvmsta = data.bfer_mvmsta;
-
- /* This code is safe as it doesn't run concurrently for different
- * stations. This is guaranteed by the fact that calls to
- * ieee80211_tx_status wouldn't run concurrently for a single HW.
- */
- if (!bfer_mvmsta) {
- IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n");
-
- ss_params |= LQ_SS_BFER_ALLOWED;
- goto out;
- }
-
- IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n",
- bfer_mvmsta->sta_id);
-
- /* Disallow BFER on another STA if active and we're a higher priority */
- if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) {
- struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq;
- u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params);
-
- bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED;
- bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params);
- iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false);
-
- ss_params |= LQ_SS_BFER_ALLOWED;
- IWL_DEBUG_RATE(mvm,
- "Lower priority BFER sta found (%d). Switch BFER\n",
- bfer_mvmsta->sta_id);
- }
-out:
- lq_cmd->ss_params = cpu_to_le32(ss_params);
-}
-
-static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- struct iwl_lq_sta *lq_sta,
- const struct rs_rate *initial_rate)
-{
- struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
- struct iwl_mvm_sta *mvmsta;
- struct iwl_mvm_vif *mvmvif;
-
- lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START;
- lq_cmd->agg_time_limit =
- cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT);
-
-#ifdef CONFIG_MAC80211_DEBUGFS
- if (lq_sta->pers.dbg_fixed_rate) {
- rs_build_rates_table_from_fixed(mvm, lq_cmd,
- lq_sta->band,
- lq_sta->pers.dbg_fixed_rate);
- return;
- }
-#endif
- if (WARN_ON_ONCE(!sta || !initial_rate))
- return;
-
- rs_build_rates_table(mvm, sta, lq_sta, initial_rate);
-
- if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS))
- rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate);
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
-
- if (num_of_ant(initial_rate->ant) == 1)
- lq_cmd->single_stream_ant_msk = initial_rate->ant;
-
- lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
-
- /*
- * In case of low latency, tell the firmware to leave a frame in the
- * Tx Fifo so that it can start a transaction in the same TxOP. This
- * basically allows the firmware to send bursts.
- */
- if (iwl_mvm_vif_low_latency(mvmvif)) {
- lq_cmd->agg_frame_cnt_limit--;
-
- if (mvm->low_latency_agg_frame_limit)
- lq_cmd->agg_frame_cnt_limit =
- min(lq_cmd->agg_frame_cnt_limit,
- mvm->low_latency_agg_frame_limit);
- }
-
- if (mvmsta->vif->p2p)
- lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK;
-
- lq_cmd->agg_time_limit =
- cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta));
-}
-
-static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
-{
- return hw->priv;
-}
-/* rate scale requires free function to be implemented */
-static void rs_free(void *mvm_rate)
-{
- return;
-}
-
-static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
- void *mvm_sta)
-{
- struct iwl_op_mode *op_mode __maybe_unused = mvm_r;
- struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
-
- IWL_DEBUG_RATE(mvm, "enter\n");
- IWL_DEBUG_RATE(mvm, "leave\n");
-}
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-int rs_pretty_print_rate(char *buf, const u32 rate)
-{
-
- char *type, *bw;
- u8 mcs = 0, nss = 0;
- u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS;
-
- if (!(rate & RATE_MCS_HT_MSK) &&
- !(rate & RATE_MCS_VHT_MSK)) {
- int index = iwl_hwrate_to_plcp_idx(rate);
-
- return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n",
- rs_pretty_ant(ant),
- index == IWL_RATE_INVALID ? "BAD" :
- iwl_rate_mcs[index].mbps);
- }
-
- if (rate & RATE_MCS_VHT_MSK) {
- type = "VHT";
- mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
- nss = ((rate & RATE_VHT_MCS_NSS_MSK)
- >> RATE_VHT_MCS_NSS_POS) + 1;
- } else if (rate & RATE_MCS_HT_MSK) {
- type = "HT";
- mcs = rate & RATE_HT_MCS_INDEX_MSK;
- } else {
- type = "Unknown"; /* shouldn't happen */
- }
-
- switch (rate & RATE_MCS_CHAN_WIDTH_MSK) {
- case RATE_MCS_CHAN_WIDTH_20:
- bw = "20Mhz";
- break;
- case RATE_MCS_CHAN_WIDTH_40:
- bw = "40Mhz";
- break;
- case RATE_MCS_CHAN_WIDTH_80:
- bw = "80Mhz";
- break;
- case RATE_MCS_CHAN_WIDTH_160:
- bw = "160Mhz";
- break;
- default:
- bw = "BAD BW";
- }
-
- return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n",
- type, rs_pretty_ant(ant), bw, mcs, nss,
- (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ",
- (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "",
- (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
- (rate & RATE_MCS_BF_MSK) ? "BF " : "",
- (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : "");
-}
-
-/**
- * Program the device to use fixed rate for frame transmit
- * This is for debugging/testing only
- * once the device start use fixed rate, we need to reload the module
- * to being back the normal operation.
- */
-static void rs_program_fix_rate(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta)
-{
- lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */
- lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
- lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
-
- IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
- lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate);
-
- if (lq_sta->pers.dbg_fixed_rate) {
- rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL);
- iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false);
- }
-}
-
-static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct iwl_lq_sta *lq_sta = file->private_data;
- struct iwl_mvm *mvm;
- char buf[64];
- size_t buf_size;
- u32 parsed_rate;
-
- mvm = lq_sta->pers.drv;
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- if (sscanf(buf, "%x", &parsed_rate) == 1)
- lq_sta->pers.dbg_fixed_rate = parsed_rate;
- else
- lq_sta->pers.dbg_fixed_rate = 0;
-
- rs_program_fix_rate(mvm, lq_sta);
-
- return count;
-}
-
-static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
- char __user *user_buf, size_t count, loff_t *ppos)
-{
- char *buff;
- int desc = 0;
- int i = 0;
- ssize_t ret;
-
- struct iwl_lq_sta *lq_sta = file->private_data;
- struct iwl_mvm *mvm;
- struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- struct rs_rate *rate = &tbl->rate;
- u32 ss_params;
- mvm = lq_sta->pers.drv;
- buff = kmalloc(2048, GFP_KERNEL);
- if (!buff)
- return -ENOMEM;
-
- desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
- desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n",
- lq_sta->total_failed, lq_sta->total_success,
- lq_sta->active_legacy_rate);
- desc += sprintf(buff+desc, "fixed rate 0x%X\n",
- lq_sta->pers.dbg_fixed_rate);
- desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
- (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "",
- (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "",
- (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : "");
- desc += sprintf(buff+desc, "lq type %s\n",
- (is_legacy(rate)) ? "legacy" :
- is_vht(rate) ? "VHT" : "HT");
- if (!is_legacy(rate)) {
- desc += sprintf(buff + desc, " %s",
- (is_siso(rate)) ? "SISO" : "MIMO2");
- desc += sprintf(buff + desc, " %s",
- (is_ht20(rate)) ? "20MHz" :
- (is_ht40(rate)) ? "40MHz" :
- (is_ht80(rate)) ? "80Mhz" : "BAD BW");
- desc += sprintf(buff + desc, " %s %s %s\n",
- (rate->sgi) ? "SGI" : "NGI",
- (rate->ldpc) ? "LDPC" : "BCC",
- (lq_sta->is_agg) ? "AGG on" : "");
- }
- desc += sprintf(buff+desc, "last tx rate=0x%X\n",
- lq_sta->last_rate_n_flags);
- desc += sprintf(buff+desc,
- "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n",
- lq_sta->lq.flags,
- lq_sta->lq.mimo_delim,
- lq_sta->lq.single_stream_ant_msk,
- lq_sta->lq.dual_stream_ant_msk);
-
- desc += sprintf(buff+desc,
- "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n",
- le16_to_cpu(lq_sta->lq.agg_time_limit),
- lq_sta->lq.agg_disable_start_th,
- lq_sta->lq.agg_frame_cnt_limit);
-
- desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
- ss_params = le32_to_cpu(lq_sta->lq.ss_params);
- desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n",
- (ss_params & LQ_SS_PARAMS_VALID) ?
- "VALID" : "INVALID",
- (ss_params & LQ_SS_BFER_ALLOWED) ?
- ", BFER" : "",
- (ss_params & LQ_SS_STBC_1SS_ALLOWED) ?
- ", STBC" : "",
- (ss_params & LQ_SS_FORCE) ?
- ", FORCE" : "");
- desc += sprintf(buff+desc,
- "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
- lq_sta->lq.initial_rate_index[0],
- lq_sta->lq.initial_rate_index[1],
- lq_sta->lq.initial_rate_index[2],
- lq_sta->lq.initial_rate_index[3]);
-
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
- u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]);
-
- desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r);
- desc += rs_pretty_print_rate(buff+desc, r);
- }
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
- kfree(buff);
- return ret;
-}
-
-static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
- .write = rs_sta_dbgfs_scale_table_write,
- .read = rs_sta_dbgfs_scale_table_read,
- .open = simple_open,
- .llseek = default_llseek,
-};
-static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
- char __user *user_buf, size_t count, loff_t *ppos)
-{
- char *buff;
- int desc = 0;
- int i, j;
- ssize_t ret;
- struct iwl_scale_tbl_info *tbl;
- struct rs_rate *rate;
- struct iwl_lq_sta *lq_sta = file->private_data;
-
- buff = kmalloc(1024, GFP_KERNEL);
- if (!buff)
- return -ENOMEM;
-
- for (i = 0; i < LQ_SIZE; i++) {
- tbl = &(lq_sta->lq_info[i]);
- rate = &tbl->rate;
- desc += sprintf(buff+desc,
- "%s type=%d SGI=%d BW=%s DUP=0\n"
- "index=%d\n",
- lq_sta->active_tbl == i ? "*" : "x",
- rate->type,
- rate->sgi,
- is_ht20(rate) ? "20Mhz" :
- is_ht40(rate) ? "40Mhz" :
- is_ht80(rate) ? "80Mhz" : "ERR",
- rate->index);
- for (j = 0; j < IWL_RATE_COUNT; j++) {
- desc += sprintf(buff+desc,
- "counter=%d success=%d %%=%d\n",
- tbl->win[j].counter,
- tbl->win[j].success_counter,
- tbl->win[j].success_ratio);
- }
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
- kfree(buff);
- return ret;
-}
-
-static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
- .read = rs_sta_dbgfs_stats_table_read,
- .open = simple_open,
- .llseek = default_llseek,
-};
-
-static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- static const char * const column_name[] = {
- [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A",
- [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B",
- [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A",
- [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B",
- [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI",
- [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI",
- [RS_COLUMN_MIMO2] = "MIMO2",
- [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI",
- };
-
- static const char * const rate_name[] = {
- [IWL_RATE_1M_INDEX] = "1M",
- [IWL_RATE_2M_INDEX] = "2M",
- [IWL_RATE_5M_INDEX] = "5.5M",
- [IWL_RATE_11M_INDEX] = "11M",
- [IWL_RATE_6M_INDEX] = "6M|MCS0",
- [IWL_RATE_9M_INDEX] = "9M",
- [IWL_RATE_12M_INDEX] = "12M|MCS1",
- [IWL_RATE_18M_INDEX] = "18M|MCS2",
- [IWL_RATE_24M_INDEX] = "24M|MCS3",
- [IWL_RATE_36M_INDEX] = "36M|MCS4",
- [IWL_RATE_48M_INDEX] = "48M|MCS5",
- [IWL_RATE_54M_INDEX] = "54M|MCS6",
- [IWL_RATE_MCS_7_INDEX] = "MCS7",
- [IWL_RATE_MCS_8_INDEX] = "MCS8",
- [IWL_RATE_MCS_9_INDEX] = "MCS9",
- };
-
- char *buff, *pos, *endpos;
- int col, rate;
- ssize_t ret;
- struct iwl_lq_sta *lq_sta = file->private_data;
- struct rs_rate_stats *stats;
- static const size_t bufsz = 1024;
-
- buff = kmalloc(bufsz, GFP_KERNEL);
- if (!buff)
- return -ENOMEM;
-
- pos = buff;
- endpos = pos + bufsz;
-
- pos += scnprintf(pos, endpos - pos, "COLUMN,");
- for (rate = 0; rate < IWL_RATE_COUNT; rate++)
- pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]);
- pos += scnprintf(pos, endpos - pos, "\n");
-
- for (col = 0; col < RS_COLUMN_COUNT; col++) {
- pos += scnprintf(pos, endpos - pos,
- "%s,", column_name[col]);
-
- for (rate = 0; rate < IWL_RATE_COUNT; rate++) {
- stats = &(lq_sta->pers.tx_stats[col][rate]);
- pos += scnprintf(pos, endpos - pos,
- "%llu/%llu,",
- stats->success,
- stats->total);
- }
- pos += scnprintf(pos, endpos - pos, "\n");
- }
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
- kfree(buff);
- return ret;
-}
-
-static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_lq_sta *lq_sta = file->private_data;
- memset(lq_sta->pers.tx_stats, 0, sizeof(lq_sta->pers.tx_stats));
-
- return count;
-}
-
-static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
- .read = rs_sta_dbgfs_drv_tx_stats_read,
- .write = rs_sta_dbgfs_drv_tx_stats_write,
- .open = simple_open,
- .llseek = default_llseek,
-};
-
-static ssize_t iwl_dbgfs_ss_force_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_lq_sta *lq_sta = file->private_data;
- char buf[12];
- int bufsz = sizeof(buf);
- int pos = 0;
- static const char * const ss_force_name[] = {
- [RS_SS_FORCE_NONE] = "none",
- [RS_SS_FORCE_STBC] = "stbc",
- [RS_SS_FORCE_BFER] = "bfer",
- [RS_SS_FORCE_SISO] = "siso",
- };
-
- pos += scnprintf(buf+pos, bufsz-pos, "%s\n",
- ss_force_name[lq_sta->pers.ss_force]);
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm *mvm = lq_sta->pers.drv;
- int ret = 0;
-
- if (!strncmp("none", buf, 4)) {
- lq_sta->pers.ss_force = RS_SS_FORCE_NONE;
- } else if (!strncmp("siso", buf, 4)) {
- lq_sta->pers.ss_force = RS_SS_FORCE_SISO;
- } else if (!strncmp("stbc", buf, 4)) {
- if (lq_sta->stbc_capable) {
- lq_sta->pers.ss_force = RS_SS_FORCE_STBC;
- } else {
- IWL_ERR(mvm,
- "can't force STBC. peer doesn't support\n");
- ret = -EINVAL;
- }
- } else if (!strncmp("bfer", buf, 4)) {
- if (lq_sta->bfer_capable) {
- lq_sta->pers.ss_force = RS_SS_FORCE_BFER;
- } else {
- IWL_ERR(mvm,
- "can't force BFER. peer doesn't support\n");
- ret = -EINVAL;
- }
- } else {
- IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n");
- ret = -EINVAL;
- }
- return ret ?: count;
-}
-
-#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
- _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta)
-#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \
- if (!debugfs_create_file(#name, mode, parent, lq_sta, \
- &iwl_dbgfs_##name##_ops)) \
- goto err; \
- } while (0)
-
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32);
-
-static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir)
-{
- struct iwl_lq_sta *lq_sta = priv_sta;
- struct iwl_mvm_sta *mvmsta;
-
- mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
-
- if (!mvmsta->vif)
- return;
-
- debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
- lq_sta, &rs_sta_dbgfs_scale_table_ops);
- debugfs_create_file("rate_stats_table", S_IRUSR, dir,
- lq_sta, &rs_sta_dbgfs_stats_table_ops);
- debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
- lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
- debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
- &lq_sta->tx_agg_tid_en);
- debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
- &lq_sta->pers.dbg_fixed_txp_reduction);
-
- MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR);
- return;
-err:
- IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n");
-}
-
-static void rs_remove_debugfs(void *mvm, void *mvm_sta)
-{
-}
-#endif
-
-/*
- * Initialization of rate scaling information is done by driver after
- * the station is added. Since mac80211 calls this function before a
- * station is added we ignore it.
- */
-static void rs_rate_init_stub(void *mvm_r,
- struct ieee80211_supported_band *sband,
- struct cfg80211_chan_def *chandef,
- struct ieee80211_sta *sta, void *mvm_sta)
-{
-}
-
-static const struct rate_control_ops rs_mvm_ops = {
- .name = RS_NAME,
- .tx_status = rs_mac80211_tx_status,
- .get_rate = rs_get_rate,
- .rate_init = rs_rate_init_stub,
- .alloc = rs_alloc,
- .free = rs_free,
- .alloc_sta = rs_alloc_sta,
- .free_sta = rs_free_sta,
- .rate_update = rs_rate_update,
-#ifdef CONFIG_MAC80211_DEBUGFS
- .add_sta_debugfs = rs_add_debugfs,
- .remove_sta_debugfs = rs_remove_debugfs,
-#endif
-};
-
-int iwl_mvm_rate_control_register(void)
-{
- return ieee80211_rate_control_register(&rs_mvm_ops);
-}
-
-void iwl_mvm_rate_control_unregister(void)
-{
- ieee80211_rate_control_unregister(&rs_mvm_ops);
-}
-
-/**
- * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
- * Tx protection, according to this request and previous requests,
- * and send the LQ command.
- * @mvmsta: The station
- * @enable: Enable Tx protection?
- */
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
- bool enable)
-{
- struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (enable) {
- if (mvmsta->tx_protection == 0)
- lq->flags |= LQ_FLAG_USE_RTS_MSK;
- mvmsta->tx_protection++;
- } else {
- mvmsta->tx_protection--;
- if (mvmsta->tx_protection == 0)
- lq->flags &= ~LQ_FLAG_USE_RTS_MSK;
- }
-
- return iwl_mvm_send_lq_cmd(mvm, lq, false);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
deleted file mode 100644
index 81314ad9ebe0..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ /dev/null
@@ -1,392 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-
-#ifndef __rs_h__
-#define __rs_h__
-
-#include <net/mac80211.h>
-
-#include "iwl-config.h"
-
-#include "fw-api.h"
-#include "iwl-trans.h"
-
-struct iwl_rs_rate_info {
- u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
- u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
- u8 plcp_ht_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */
- u8 plcp_vht_siso;
- u8 plcp_vht_mimo2;
- u8 prev_rs; /* previous rate used in rs algo */
- u8 next_rs; /* next rate used in rs algo */
-};
-
-#define IWL_RATE_60M_PLCP 3
-
-enum {
- IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
- IWL_RATE_INVALID = IWL_RATE_COUNT,
-};
-
-#define LINK_QUAL_MAX_RETRY_NUM 16
-
-enum {
- IWL_RATE_6M_INDEX_TABLE = 0,
- IWL_RATE_9M_INDEX_TABLE,
- IWL_RATE_12M_INDEX_TABLE,
- IWL_RATE_18M_INDEX_TABLE,
- IWL_RATE_24M_INDEX_TABLE,
- IWL_RATE_36M_INDEX_TABLE,
- IWL_RATE_48M_INDEX_TABLE,
- IWL_RATE_54M_INDEX_TABLE,
- IWL_RATE_1M_INDEX_TABLE,
- IWL_RATE_2M_INDEX_TABLE,
- IWL_RATE_5M_INDEX_TABLE,
- IWL_RATE_11M_INDEX_TABLE,
- IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1,
-};
-
-/* #define vs. enum to keep from defaulting to 'large integer' */
-#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX)
-#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX)
-#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX)
-#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX)
-#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX)
-#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX)
-#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX)
-#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX)
-#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX)
-#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX)
-#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX)
-#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX)
-#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX)
-
-
-/* uCode API values for HT/VHT bit rates */
-enum {
- IWL_RATE_HT_SISO_MCS_0_PLCP = 0,
- IWL_RATE_HT_SISO_MCS_1_PLCP = 1,
- IWL_RATE_HT_SISO_MCS_2_PLCP = 2,
- IWL_RATE_HT_SISO_MCS_3_PLCP = 3,
- IWL_RATE_HT_SISO_MCS_4_PLCP = 4,
- IWL_RATE_HT_SISO_MCS_5_PLCP = 5,
- IWL_RATE_HT_SISO_MCS_6_PLCP = 6,
- IWL_RATE_HT_SISO_MCS_7_PLCP = 7,
- IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8,
- IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9,
- IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA,
- IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB,
- IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC,
- IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD,
- IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE,
- IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF,
- IWL_RATE_VHT_SISO_MCS_0_PLCP = 0,
- IWL_RATE_VHT_SISO_MCS_1_PLCP = 1,
- IWL_RATE_VHT_SISO_MCS_2_PLCP = 2,
- IWL_RATE_VHT_SISO_MCS_3_PLCP = 3,
- IWL_RATE_VHT_SISO_MCS_4_PLCP = 4,
- IWL_RATE_VHT_SISO_MCS_5_PLCP = 5,
- IWL_RATE_VHT_SISO_MCS_6_PLCP = 6,
- IWL_RATE_VHT_SISO_MCS_7_PLCP = 7,
- IWL_RATE_VHT_SISO_MCS_8_PLCP = 8,
- IWL_RATE_VHT_SISO_MCS_9_PLCP = 9,
- IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10,
- IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11,
- IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12,
- IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13,
- IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14,
- IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15,
- IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16,
- IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17,
- IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18,
- IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19,
- IWL_RATE_HT_SISO_MCS_INV_PLCP,
- IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
- IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
- IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
- IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
- IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
- IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
- IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
-};
-
-#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
-
-#define IWL_INVALID_VALUE -1
-
-#define TPC_MAX_REDUCTION 15
-#define TPC_NO_REDUCTION 0
-#define TPC_INVALID 0xff
-
-#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63)
-#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63)
-#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0)
-
-#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */
-
-/* load per tid defines for A-MPDU activation */
-#define IWL_AGG_TPT_THREHOLD 0
-#define IWL_AGG_ALL_TID 0xff
-
-enum iwl_table_type {
- LQ_NONE,
- LQ_LEGACY_G, /* legacy types */
- LQ_LEGACY_A,
- LQ_HT_SISO, /* HT types */
- LQ_HT_MIMO2,
- LQ_VHT_SISO, /* VHT types */
- LQ_VHT_MIMO2,
- LQ_MAX,
-};
-
-struct rs_rate {
- int index;
- enum iwl_table_type type;
- u8 ant;
- u32 bw;
- bool sgi;
- bool ldpc;
- bool stbc;
- bool bfer;
-};
-
-
-#define is_type_legacy(type) (((type) == LQ_LEGACY_G) || \
- ((type) == LQ_LEGACY_A))
-#define is_type_ht_siso(type) ((type) == LQ_HT_SISO)
-#define is_type_ht_mimo2(type) ((type) == LQ_HT_MIMO2)
-#define is_type_vht_siso(type) ((type) == LQ_VHT_SISO)
-#define is_type_vht_mimo2(type) ((type) == LQ_VHT_MIMO2)
-#define is_type_siso(type) (is_type_ht_siso(type) || is_type_vht_siso(type))
-#define is_type_mimo2(type) (is_type_ht_mimo2(type) || is_type_vht_mimo2(type))
-#define is_type_mimo(type) (is_type_mimo2(type))
-#define is_type_ht(type) (is_type_ht_siso(type) || is_type_ht_mimo2(type))
-#define is_type_vht(type) (is_type_vht_siso(type) || is_type_vht_mimo2(type))
-#define is_type_a_band(type) ((type) == LQ_LEGACY_A)
-#define is_type_g_band(type) ((type) == LQ_LEGACY_G)
-
-#define is_legacy(rate) is_type_legacy((rate)->type)
-#define is_ht_siso(rate) is_type_ht_siso((rate)->type)
-#define is_ht_mimo2(rate) is_type_ht_mimo2((rate)->type)
-#define is_vht_siso(rate) is_type_vht_siso((rate)->type)
-#define is_vht_mimo2(rate) is_type_vht_mimo2((rate)->type)
-#define is_siso(rate) is_type_siso((rate)->type)
-#define is_mimo2(rate) is_type_mimo2((rate)->type)
-#define is_mimo(rate) is_type_mimo((rate)->type)
-#define is_ht(rate) is_type_ht((rate)->type)
-#define is_vht(rate) is_type_vht((rate)->type)
-#define is_a_band(rate) is_type_a_band((rate)->type)
-#define is_g_band(rate) is_type_g_band((rate)->type)
-
-#define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20)
-#define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40)
-#define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80)
-
-#define IWL_MAX_MCS_DISPLAY_SIZE 12
-
-struct iwl_rate_mcs_info {
- char mbps[IWL_MAX_MCS_DISPLAY_SIZE];
- char mcs[IWL_MAX_MCS_DISPLAY_SIZE];
-};
-
-/**
- * struct iwl_rate_scale_data -- tx success history for one rate
- */
-struct iwl_rate_scale_data {
- u64 data; /* bitmap of successful frames */
- s32 success_counter; /* number of frames successful */
- s32 success_ratio; /* per-cent * 128 */
- s32 counter; /* number of frames attempted */
- s32 average_tpt; /* success ratio * expected throughput */
-};
-
-/* Possible Tx columns
- * Tx Column = a combo of legacy/siso/mimo x antenna x SGI
- */
-enum rs_column {
- RS_COLUMN_LEGACY_ANT_A = 0,
- RS_COLUMN_LEGACY_ANT_B,
- RS_COLUMN_SISO_ANT_A,
- RS_COLUMN_SISO_ANT_B,
- RS_COLUMN_SISO_ANT_A_SGI,
- RS_COLUMN_SISO_ANT_B_SGI,
- RS_COLUMN_MIMO2,
- RS_COLUMN_MIMO2_SGI,
-
- RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI,
- RS_COLUMN_COUNT = RS_COLUMN_LAST + 1,
- RS_COLUMN_INVALID,
-};
-
-enum rs_ss_force_opt {
- RS_SS_FORCE_NONE = 0,
- RS_SS_FORCE_STBC,
- RS_SS_FORCE_BFER,
- RS_SS_FORCE_SISO,
-};
-
-/* Packet stats per rate */
-struct rs_rate_stats {
- u64 success;
- u64 total;
-};
-
-/**
- * struct iwl_scale_tbl_info -- tx params and success history for all rates
- *
- * There are two of these in struct iwl_lq_sta,
- * one for "active", and one for "search".
- */
-struct iwl_scale_tbl_info {
- struct rs_rate rate;
- enum rs_column column;
- const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
- struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
- /* per txpower-reduction history */
- struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1];
-};
-
-enum {
- RS_STATE_SEARCH_CYCLE_STARTED,
- RS_STATE_SEARCH_CYCLE_ENDED,
- RS_STATE_STAY_IN_COLUMN,
-};
-
-/**
- * struct iwl_lq_sta -- driver's rate scaling private structure
- *
- * Pointer to this gets passed back and forth between driver and mac80211.
- */
-struct iwl_lq_sta {
- u8 active_tbl; /* index of active table, range 0-1 */
- u8 rs_state; /* RS_STATE_* */
- u8 search_better_tbl; /* 1: currently trying alternate mode */
- s32 last_tpt;
-
- /* The following determine when to search for a new mode */
- u32 table_count_limit;
- u32 max_failure_limit; /* # failed frames before new search */
- u32 max_success_limit; /* # successful frames before new search */
- u32 table_count;
- u32 total_failed; /* total failed frames, any/all rates */
- u32 total_success; /* total successful frames, any/all rates */
- u64 flush_timer; /* time staying in mode before new search */
-
- u32 visited_columns; /* Bitmask marking which Tx columns were
- * explored during a search cycle
- */
- u64 last_tx;
- bool is_vht;
- bool ldpc; /* LDPC Rx is supported by the STA */
- bool stbc_capable; /* Tx STBC is supported by chip and Rx by STA */
- bool bfer_capable; /* Remote supports beamformee and we BFer */
-
- enum ieee80211_band band;
-
- /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
- unsigned long active_legacy_rate;
- unsigned long active_siso_rate;
- unsigned long active_mimo2_rate;
-
- /* Highest rate per Tx mode */
- u8 max_legacy_rate_idx;
- u8 max_siso_rate_idx;
- u8 max_mimo2_rate_idx;
-
- /* Optimal rate based on RSSI and STA caps.
- * Used only to reflect link speed to userspace.
- */
- struct rs_rate optimal_rate;
- unsigned long optimal_rate_mask;
- const struct rs_init_rate_info *optimal_rates;
- int optimal_nentries;
-
- u8 missed_rate_counter;
-
- struct iwl_lq_cmd lq;
- struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
- u8 tx_agg_tid_en;
-
- /* last tx rate_n_flags */
- u32 last_rate_n_flags;
- /* packets destined for this STA are aggregated */
- u8 is_agg;
-
- /* tx power reduce for this sta */
- int tpc_reduce;
-
- /* persistent fields - initialized only once - keep last! */
- struct lq_sta_pers {
-#ifdef CONFIG_MAC80211_DEBUGFS
- u32 dbg_fixed_rate;
- u8 dbg_fixed_txp_reduction;
-
- /* force STBC/BFER/SISO for testing */
- enum rs_ss_force_opt ss_force;
-#endif
- u8 chains;
- s8 chain_signal[IEEE80211_MAX_CHAINS];
- s8 last_rssi;
- struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT];
- struct iwl_mvm *drv;
- } pers;
-};
-
-/* Initialize station's rate scaling information after adding station */
-void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- enum ieee80211_band band, bool init);
-
-/* Notify RS about Tx status */
-void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- int tid, struct ieee80211_tx_info *info);
-
-/**
- * iwl_rate_control_register - Register the rate control algorithm callbacks
- *
- * Since the rate control algorithm is hardware specific, there is no need
- * or reason to place it as a stand alone module. The driver can call
- * iwl_rate_control_register in order to register the rate control callbacks
- * with the mac80211 subsystem. This should be performed prior to calling
- * ieee80211_register_hw
- *
- */
-int iwl_mvm_rate_control_register(void);
-
-/**
- * iwl_rate_control_unregister - Unregister the rate control callbacks
- *
- * This should be called after calling ieee80211_unregister_hw, but before
- * the driver is unloaded.
- */
-void iwl_mvm_rate_control_unregister(void);
-
-struct iwl_mvm_sta;
-
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
- bool enable);
-
-#endif /* __rs__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
deleted file mode 100644
index 5b58f5320e8d..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-#include <linux/skbuff.h>
-#include "iwl-trans.h"
-#include "mvm.h"
-#include "fw-api.h"
-
-/*
- * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
- *
- * Copies the phy information in mvm->last_phy_info, it will be used when the
- * actual data will come from the fw in the next packet.
- */
-void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
-
- memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
- mvm->ampdu_ref++;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
- spin_lock(&mvm->drv_stats_lock);
- mvm->drv_rx_stats.ampdu_count++;
- spin_unlock(&mvm->drv_stats_lock);
- }
-#endif
-}
-
-/*
- * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
- *
- * Adds the rxb to a new skb and give it to mac80211
- */
-static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
- struct napi_struct *napi,
- struct sk_buff *skb,
- struct ieee80211_hdr *hdr, u16 len,
- u32 ampdu_status, u8 crypt_len,
- struct iwl_rx_cmd_buffer *rxb)
-{
- unsigned int hdrlen, fraglen;
-
- /* If frame is small enough to fit in skb->head, pull it completely.
- * If not, only pull ieee80211_hdr (including crypto if present, and
- * an additional 8 bytes for SNAP/ethertype, see below) so that
- * splice() or TCP coalesce are more efficient.
- *
- * Since, in addition, ieee80211_data_to_8023() always pull in at
- * least 8 bytes (possibly more for mesh) we can do the same here
- * to save the cost of doing it later. That still doesn't pull in
- * the actual IP header since the typical case has a SNAP header.
- * If the latter changes (there are efforts in the standards group
- * to do so) we should revisit this and ieee80211_data_to_8023().
- */
- hdrlen = (len <= skb_tailroom(skb)) ? len :
- sizeof(*hdr) + crypt_len + 8;
-
- memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
- fraglen = len - hdrlen;
-
- if (fraglen) {
- int offset = (void *)hdr + hdrlen -
- rxb_addr(rxb) + rxb_offset(rxb);
-
- skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
- fraglen, rxb->truesize);
- }
-
- ieee80211_rx_napi(mvm->hw, skb, napi);
-}
-
-/*
- * iwl_mvm_get_signal_strength - use new rx PHY INFO API
- * values are reported by the fw as positive values - need to negate
- * to obtain their dBM. Account for missing antennas by replacing 0
- * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
- */
-static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
- struct iwl_rx_phy_info *phy_info,
- struct ieee80211_rx_status *rx_status)
-{
- int energy_a, energy_b, energy_c, max_energy;
- u32 val;
-
- val =
- le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
- energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
- IWL_RX_INFO_ENERGY_ANT_A_POS;
- energy_a = energy_a ? -energy_a : S8_MIN;
- energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
- IWL_RX_INFO_ENERGY_ANT_B_POS;
- energy_b = energy_b ? -energy_b : S8_MIN;
- energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
- IWL_RX_INFO_ENERGY_ANT_C_POS;
- energy_c = energy_c ? -energy_c : S8_MIN;
- max_energy = max(energy_a, energy_b);
- max_energy = max(max_energy, energy_c);
-
- IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
- energy_a, energy_b, energy_c, max_energy);
-
- rx_status->signal = max_energy;
- rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
- RX_RES_PHY_FLAGS_ANTENNA)
- >> RX_RES_PHY_FLAGS_ANTENNA_POS;
- rx_status->chain_signal[0] = energy_a;
- rx_status->chain_signal[1] = energy_b;
- rx_status->chain_signal[2] = energy_c;
-}
-
-/*
- * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
- * @mvm: the mvm object
- * @hdr: 80211 header
- * @stats: status in mac80211's format
- * @rx_pkt_status: status coming from fw
- *
- * returns non 0 value if the packet should be dropped
- */
-static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
- struct ieee80211_hdr *hdr,
- struct ieee80211_rx_status *stats,
- u32 rx_pkt_status,
- u8 *crypt_len)
-{
- if (!ieee80211_has_protected(hdr->frame_control) ||
- (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
- RX_MPDU_RES_STATUS_SEC_NO_ENC)
- return 0;
-
- /* packet was encrypted with unknown alg */
- if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
- RX_MPDU_RES_STATUS_SEC_ENC_ERR)
- return 0;
-
- switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
- case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
- /* alg is CCM: check MIC only */
- if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
- return -1;
-
- stats->flag |= RX_FLAG_DECRYPTED;
- *crypt_len = IEEE80211_CCMP_HDR_LEN;
- return 0;
-
- case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
- /* Don't drop the frame and decrypt it in SW */
- if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
- return 0;
- *crypt_len = IEEE80211_TKIP_IV_LEN;
- /* fall through if TTAK OK */
-
- case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
- if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
- return -1;
-
- stats->flag |= RX_FLAG_DECRYPTED;
- if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
- RX_MPDU_RES_STATUS_SEC_WEP_ENC)
- *crypt_len = IEEE80211_WEP_IV_LEN;
- return 0;
-
- case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
- if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
- return -1;
- stats->flag |= RX_FLAG_DECRYPTED;
- return 0;
-
- default:
- IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
- }
-
- return 0;
-}
-
-static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
- struct sk_buff *skb,
- u32 status)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
-
- if (mvmvif->features & NETIF_F_RXCSUM &&
- status & RX_MPDU_RES_STATUS_CSUM_DONE &&
- status & RX_MPDU_RES_STATUS_CSUM_OK)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-}
-
-/*
- * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
- *
- * Handles the actual data of the Rx packet from the fw
- */
-void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct ieee80211_hdr *hdr;
- struct ieee80211_rx_status *rx_status;
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_rx_phy_info *phy_info;
- struct iwl_rx_mpdu_res_start *rx_res;
- struct ieee80211_sta *sta;
- struct sk_buff *skb;
- u32 len;
- u32 ampdu_status;
- u32 rate_n_flags;
- u32 rx_pkt_status;
- u8 crypt_len = 0;
-
- phy_info = &mvm->last_phy_info;
- rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
- hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
- len = le16_to_cpu(rx_res->byte_count);
- rx_pkt_status = le32_to_cpup((__le32 *)
- (pkt->data + sizeof(*rx_res) + len));
-
- /* Dont use dev_alloc_skb(), we'll have enough headroom once
- * ieee80211_hdr pulled.
- */
- skb = alloc_skb(128, GFP_ATOMIC);
- if (!skb) {
- IWL_ERR(mvm, "alloc_skb failed\n");
- return;
- }
-
- rx_status = IEEE80211_SKB_RXCB(skb);
-
- /*
- * drop the packet if it has failed being decrypted by HW
- */
- if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
- &crypt_len)) {
- IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
- rx_pkt_status);
- kfree_skb(skb);
- return;
- }
-
- /*
- * Keep packets with CRC errors (and with overrun) for monitor mode
- * (otherwise the firmware discards them) but mark them as bad.
- */
- if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
- !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
- IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
- }
-
- /* This will be used in several places later */
- rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
-
- /* rx_status carries information about the packet to mac80211 */
- rx_status->mactime = le64_to_cpu(phy_info->timestamp);
- rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
- rx_status->band =
- (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
- IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
- rx_status->freq =
- ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
- rx_status->band);
- /*
- * TSF as indicated by the fw is at INA time, but mac80211 expects the
- * TSF at the beginning of the MPDU.
- */
- /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/
-
- iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
-
- IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal,
- (unsigned long long)rx_status->mactime);
-
- rcu_read_lock();
- /*
- * We have tx blocked stations (with CS bit). If we heard frames from
- * a blocked station on a new channel we can TX to it again.
- */
- if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
- sta = ieee80211_find_sta(
- rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
- if (sta)
- iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
- }
-
- /* This is fine since we don't support multiple AP interfaces */
- sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
- if (sta) {
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-
- rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
-
- if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
- ieee80211_is_beacon(hdr->frame_control)) {
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
- bool trig_check;
- s32 rssi;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw,
- FW_DBG_TRIGGER_RSSI);
- rssi_trig = (void *)trig->data;
- rssi = le32_to_cpu(rssi_trig->rssi);
-
- trig_check =
- iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
- trig);
- if (trig_check && rx_status->signal < rssi)
- iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
- }
- }
-
- if (sta && ieee80211_is_data(hdr->frame_control))
- iwl_mvm_rx_csum(sta, skb, rx_pkt_status);
-
- rcu_read_unlock();
-
- /* set the preamble flag if appropriate */
- if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
- rx_status->flag |= RX_FLAG_SHORTPRE;
-
- if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
- /*
- * We know which subframes of an A-MPDU belong
- * together since we get a single PHY response
- * from the firmware for all of them
- */
- rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
- rx_status->ampdu_reference = mvm->ampdu_ref;
- }
-
- /* Set up the HT phy flags */
- switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
- case RATE_MCS_CHAN_WIDTH_20:
- break;
- case RATE_MCS_CHAN_WIDTH_40:
- rx_status->flag |= RX_FLAG_40MHZ;
- break;
- case RATE_MCS_CHAN_WIDTH_80:
- rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
- break;
- case RATE_MCS_CHAN_WIDTH_160:
- rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
- break;
- }
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- rx_status->flag |= RX_FLAG_SHORT_GI;
- if (rate_n_flags & RATE_HT_MCS_GF_MSK)
- rx_status->flag |= RX_FLAG_HT_GF;
- if (rate_n_flags & RATE_MCS_LDPC_MSK)
- rx_status->flag |= RX_FLAG_LDPC;
- if (rate_n_flags & RATE_MCS_HT_MSK) {
- u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
- RATE_MCS_STBC_POS;
- rx_status->flag |= RX_FLAG_HT;
- rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
- rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
- } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
- u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
- RATE_MCS_STBC_POS;
- rx_status->vht_nss =
- ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
- RATE_VHT_MCS_NSS_POS) + 1;
- rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
- rx_status->flag |= RX_FLAG_VHT;
- rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
- if (rate_n_flags & RATE_MCS_BF_MSK)
- rx_status->vht_flag |= RX_VHT_FLAG_BF;
- } else {
- rx_status->rate_idx =
- iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
- rx_status->band);
- }
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- iwl_mvm_update_frame_stats(mvm, rate_n_flags,
- rx_status->flag & RX_FLAG_AMPDU_DETAILS);
-#endif
- iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status,
- crypt_len, rxb);
-}
-
-static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
- struct mvm_statistics_rx *rx_stats)
-{
- lockdep_assert_held(&mvm->mutex);
-
- mvm->rx_stats = *rx_stats;
-}
-
-struct iwl_mvm_stat_data {
- struct iwl_mvm *mvm;
- __le32 mac_id;
- u8 beacon_filter_average_energy;
- struct mvm_statistics_general_v8 *general;
-};
-
-static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_stat_data *data = _data;
- struct iwl_mvm *mvm = data->mvm;
- int sig = -data->beacon_filter_average_energy;
- int last_event;
- int thold = vif->bss_conf.cqm_rssi_thold;
- int hyst = vif->bss_conf.cqm_rssi_hyst;
- u16 id = le32_to_cpu(data->mac_id);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- /* This doesn't need the MAC ID check since it's not taking the
- * data copied into the "data" struct, but rather the data from
- * the notification directly.
- */
- if (data->general) {
- mvmvif->beacon_stats.num_beacons =
- le32_to_cpu(data->general->beacon_counter[mvmvif->id]);
- mvmvif->beacon_stats.avg_signal =
- -data->general->beacon_average_energy[mvmvif->id];
- }
-
- if (mvmvif->id != id)
- return;
-
- if (vif->type != NL80211_IFTYPE_STATION)
- return;
-
- if (sig == 0) {
- IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
- return;
- }
-
- mvmvif->bf_data.ave_beacon_signal = sig;
-
- /* BT Coex */
- if (mvmvif->bf_data.bt_coex_min_thold !=
- mvmvif->bf_data.bt_coex_max_thold) {
- last_event = mvmvif->bf_data.last_bt_coex_event;
- if (sig > mvmvif->bf_data.bt_coex_max_thold &&
- (last_event <= mvmvif->bf_data.bt_coex_min_thold ||
- last_event == 0)) {
- mvmvif->bf_data.last_bt_coex_event = sig;
- IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n",
- sig);
- iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH);
- } else if (sig < mvmvif->bf_data.bt_coex_min_thold &&
- (last_event >= mvmvif->bf_data.bt_coex_max_thold ||
- last_event == 0)) {
- mvmvif->bf_data.last_bt_coex_event = sig;
- IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n",
- sig);
- iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW);
- }
- }
-
- if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
- return;
-
- /* CQM Notification */
- last_event = mvmvif->bf_data.last_cqm_event;
- if (thold && sig < thold && (last_event == 0 ||
- sig < last_event - hyst)) {
- mvmvif->bf_data.last_cqm_event = sig;
- IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n",
- sig);
- ieee80211_cqm_rssi_notify(
- vif,
- NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
- GFP_KERNEL);
- } else if (sig > thold &&
- (last_event == 0 || sig > last_event + hyst)) {
- mvmvif->bf_data.last_cqm_event = sig;
- IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n",
- sig);
- ieee80211_cqm_rssi_notify(
- vif,
- NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
- GFP_KERNEL);
- }
-}
-
-static inline void
-iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_stats *trig_stats;
- u32 trig_offset, trig_thold;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS);
- trig_stats = (void *)trig->data;
-
- if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
- return;
-
- trig_offset = le32_to_cpu(trig_stats->stop_offset);
- trig_thold = le32_to_cpu(trig_stats->stop_threshold);
-
- if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt)))
- return;
-
- if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
- return;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
-}
-
-void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data;
- struct iwl_mvm_stat_data data = {
- .mvm = mvm,
- };
- u32 temperature;
-
- if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats))
- goto invalid;
-
- temperature = le32_to_cpu(stats->general.radio_temperature);
- data.mac_id = stats->rx.general.mac_id;
- data.beacon_filter_average_energy =
- stats->general.beacon_filter_average_energy;
-
- iwl_mvm_update_rx_statistics(mvm, &stats->rx);
-
- mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
- mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
- mvm->radio_stats.on_time_rf =
- le64_to_cpu(stats->general.on_time_rf);
- mvm->radio_stats.on_time_scan =
- le64_to_cpu(stats->general.on_time_scan);
-
- data.general = &stats->general;
-
- iwl_mvm_rx_stats_check_trigger(mvm, pkt);
-
- ieee80211_iterate_active_interfaces(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_stat_iterator,
- &data);
- return;
- invalid:
- IWL_ERR(mvm, "received invalid statistics size (%d)!\n",
- iwl_rx_packet_payload_len(pkt));
-}
-
-void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
- iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
deleted file mode 100644
index d6e0c1b5c20c..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ /dev/null
@@ -1,1552 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/etherdevice.h>
-#include <net/mac80211.h>
-
-#include "mvm.h"
-#include "fw-api-scan.h"
-
-#define IWL_DENSE_EBS_SCAN_RATIO 5
-#define IWL_SPARSE_EBS_SCAN_RATIO 1
-
-enum iwl_mvm_scan_type {
- IWL_SCAN_TYPE_UNASSOC,
- IWL_SCAN_TYPE_WILD,
- IWL_SCAN_TYPE_MILD,
- IWL_SCAN_TYPE_FRAGMENTED,
-};
-
-enum iwl_mvm_traffic_load {
- IWL_MVM_TRAFFIC_LOW,
- IWL_MVM_TRAFFIC_MEDIUM,
- IWL_MVM_TRAFFIC_HIGH,
-};
-
-struct iwl_mvm_scan_timing_params {
- u32 dwell_active;
- u32 dwell_passive;
- u32 dwell_fragmented;
- u32 suspend_time;
- u32 max_out_time;
-};
-
-static struct iwl_mvm_scan_timing_params scan_timing[] = {
- [IWL_SCAN_TYPE_UNASSOC] = {
- .dwell_active = 10,
- .dwell_passive = 110,
- .dwell_fragmented = 44,
- .suspend_time = 0,
- .max_out_time = 0,
- },
- [IWL_SCAN_TYPE_WILD] = {
- .dwell_active = 10,
- .dwell_passive = 110,
- .dwell_fragmented = 44,
- .suspend_time = 30,
- .max_out_time = 120,
- },
- [IWL_SCAN_TYPE_MILD] = {
- .dwell_active = 10,
- .dwell_passive = 110,
- .dwell_fragmented = 44,
- .suspend_time = 120,
- .max_out_time = 120,
- },
- [IWL_SCAN_TYPE_FRAGMENTED] = {
- .dwell_active = 10,
- .dwell_passive = 110,
- .dwell_fragmented = 44,
- .suspend_time = 95,
- .max_out_time = 44,
- },
-};
-
-struct iwl_mvm_scan_params {
- enum iwl_mvm_scan_type type;
- u32 n_channels;
- u16 delay;
- int n_ssids;
- struct cfg80211_ssid *ssids;
- struct ieee80211_channel **channels;
- u32 flags;
- u8 *mac_addr;
- u8 *mac_addr_mask;
- bool no_cck;
- bool pass_all;
- int n_match_sets;
- struct iwl_scan_probe_req preq;
- struct cfg80211_match_set *match_sets;
- int n_scan_plans;
- struct cfg80211_sched_scan_plan *scan_plans;
-};
-
-static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
-{
- if (mvm->scan_rx_ant != ANT_NONE)
- return mvm->scan_rx_ant;
- return iwl_mvm_get_valid_rx_ant(mvm);
-}
-
-static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
-{
- u16 rx_chain;
- u8 rx_ant;
-
- rx_ant = iwl_mvm_scan_rx_ant(mvm);
- rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
- rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
- rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
- rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS;
- return cpu_to_le16(rx_chain);
-}
-
-static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band)
-{
- if (band == IEEE80211_BAND_2GHZ)
- return cpu_to_le32(PHY_BAND_24);
- else
- return cpu_to_le32(PHY_BAND_5);
-}
-
-static inline __le32
-iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
- bool no_cck)
-{
- u32 tx_ant;
-
- mvm->scan_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
- mvm->scan_last_antenna_idx);
- tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
-
- if (band == IEEE80211_BAND_2GHZ && !no_cck)
- return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK |
- tx_ant);
- else
- return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant);
-}
-
-static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int *global_cnt = data;
-
- if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt &&
- mvmvif->phy_ctxt->id < MAX_PHYS)
- *global_cnt += 1;
-}
-
-static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm)
-{
- return IWL_MVM_TRAFFIC_LOW;
-}
-
-static enum
-iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mvm_scan_params *params)
-{
- int global_cnt = 0;
- enum iwl_mvm_traffic_load load;
- bool low_latency;
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_scan_condition_iterator,
- &global_cnt);
- if (!global_cnt)
- return IWL_SCAN_TYPE_UNASSOC;
-
- load = iwl_mvm_get_traffic_load(mvm);
- low_latency = iwl_mvm_low_latency(mvm);
-
- if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) &&
- vif->type != NL80211_IFTYPE_P2P_DEVICE &&
- fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN))
- return IWL_SCAN_TYPE_FRAGMENTED;
-
- if (load >= IWL_MVM_TRAFFIC_MEDIUM || low_latency)
- return IWL_SCAN_TYPE_MILD;
-
- return IWL_SCAN_TYPE_WILD;
-}
-
-static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm)
-{
- /* require rrm scan whenever the fw supports it */
- return fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT);
-}
-
-static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm)
-{
- int max_probe_len;
-
- max_probe_len = SCAN_OFFLOAD_PROBE_REQ_SIZE;
-
- /* we create the 802.11 header and SSID element */
- max_probe_len -= 24 + 2;
-
- /* DS parameter set element is added on 2.4GHZ band if required */
- if (iwl_mvm_rrm_scan_needed(mvm))
- max_probe_len -= 3;
-
- return max_probe_len;
-}
-
-int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm)
-{
- int max_ie_len = iwl_mvm_max_scan_ie_fw_cmd_room(mvm);
-
- /* TODO: [BUG] This function should return the maximum allowed size of
- * scan IEs, however the LMAC scan api contains both 2GHZ and 5GHZ IEs
- * in the same command. So the correct implementation of this function
- * is just iwl_mvm_max_scan_ie_fw_cmd_room() / 2. Currently the scan
- * command has only 512 bytes and it would leave us with about 240
- * bytes for scan IEs, which is clearly not enough. So meanwhile
- * we will report an incorrect value. This may result in a failure to
- * issue a scan in unified_scan_lmac and unified_sched_scan_lmac
- * functions with -ENOBUFS, if a large enough probe will be provided.
- */
- return max_ie_len;
-}
-
-static u8 *iwl_mvm_dump_channel_list(struct iwl_scan_results_notif *res,
- int num_res, u8 *buf, size_t buf_size)
-{
- int i;
- u8 *pos = buf, *end = buf + buf_size;
-
- for (i = 0; pos < end && i < num_res; i++)
- pos += snprintf(pos, end - pos, " %u", res[i].channel);
-
- /* terminate the string in case the buffer was too short */
- *(buf + buf_size - 1) = '\0';
-
- return buf;
-}
-
-void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_lmac_scan_complete_notif *notif = (void *)pkt->data;
- u8 buf[256];
-
- IWL_DEBUG_SCAN(mvm,
- "Scan offload iteration complete: status=0x%x scanned channels=%d channels list: %s\n",
- notif->status, notif->scanned_channels,
- iwl_mvm_dump_channel_list(notif->results,
- notif->scanned_channels, buf,
- sizeof(buf)));
-}
-
-void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
- ieee80211_sched_scan_results(mvm->hw);
-}
-
-static const char *iwl_mvm_ebs_status_str(enum iwl_scan_ebs_status status)
-{
- switch (status) {
- case IWL_SCAN_EBS_SUCCESS:
- return "successful";
- case IWL_SCAN_EBS_INACTIVE:
- return "inactive";
- case IWL_SCAN_EBS_FAILED:
- case IWL_SCAN_EBS_CHAN_NOT_FOUND:
- default:
- return "failed";
- }
-}
-
-void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_periodic_scan_complete *scan_notif = (void *)pkt->data;
- bool aborted = (scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED);
-
- /* scan status must be locked for proper checking */
- lockdep_assert_held(&mvm->mutex);
-
- /* We first check if we were stopping a scan, in which case we
- * just clear the stopping flag. Then we check if it was a
- * firmware initiated stop, in which case we need to inform
- * mac80211.
- * Note that we can have a stopping and a running scan
- * simultaneously, but we can't have two different types of
- * scans stopping or running at the same time (since LMAC
- * doesn't support it).
- */
-
- if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_SCHED) {
- WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR);
-
- IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n",
- aborted ? "aborted" : "completed",
- iwl_mvm_ebs_status_str(scan_notif->ebs_status));
- IWL_DEBUG_SCAN(mvm,
- "Last line %d, Last iteration %d, Time after last iteration %d\n",
- scan_notif->last_schedule_line,
- scan_notif->last_schedule_iteration,
- __le32_to_cpu(scan_notif->time_after_last_iter));
-
- mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_SCHED;
- } else if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR) {
- IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s\n",
- aborted ? "aborted" : "completed",
- iwl_mvm_ebs_status_str(scan_notif->ebs_status));
-
- mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_REGULAR;
- } else if (mvm->scan_status & IWL_MVM_SCAN_SCHED) {
- WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_REGULAR);
-
- IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n",
- aborted ? "aborted" : "completed",
- iwl_mvm_ebs_status_str(scan_notif->ebs_status));
- IWL_DEBUG_SCAN(mvm,
- "Last line %d, Last iteration %d, Time after last iteration %d (FW)\n",
- scan_notif->last_schedule_line,
- scan_notif->last_schedule_iteration,
- __le32_to_cpu(scan_notif->time_after_last_iter));
-
- mvm->scan_status &= ~IWL_MVM_SCAN_SCHED;
- ieee80211_sched_scan_stopped(mvm->hw);
- } else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) {
- IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n",
- aborted ? "aborted" : "completed",
- iwl_mvm_ebs_status_str(scan_notif->ebs_status));
-
- mvm->scan_status &= ~IWL_MVM_SCAN_REGULAR;
- ieee80211_scan_completed(mvm->hw,
- scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED);
- iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
- }
-
- mvm->last_ebs_successful =
- scan_notif->ebs_status == IWL_SCAN_EBS_SUCCESS ||
- scan_notif->ebs_status == IWL_SCAN_EBS_INACTIVE;
-}
-
-static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
-{
- int i;
-
- for (i = 0; i < PROBE_OPTION_MAX; i++) {
- if (!ssid_list[i].len)
- break;
- if (ssid_list[i].len == ssid_len &&
- !memcmp(ssid_list->ssid, ssid, ssid_len))
- return i;
- }
- return -1;
-}
-
-/* We insert the SSIDs in an inverted order, because the FW will
- * invert it back.
- */
-static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params,
- struct iwl_ssid_ie *ssids,
- u32 *ssid_bitmap)
-{
- int i, j;
- int index;
-
- /*
- * copy SSIDs from match list.
- * iwl_config_sched_scan_profiles() uses the order of these ssids to
- * config match list.
- */
- for (i = 0, j = params->n_match_sets - 1;
- j >= 0 && i < PROBE_OPTION_MAX;
- i++, j--) {
- /* skip empty SSID matchsets */
- if (!params->match_sets[j].ssid.ssid_len)
- continue;
- ssids[i].id = WLAN_EID_SSID;
- ssids[i].len = params->match_sets[j].ssid.ssid_len;
- memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid,
- ssids[i].len);
- }
-
- /* add SSIDs from scan SSID list */
- *ssid_bitmap = 0;
- for (j = params->n_ssids - 1;
- j >= 0 && i < PROBE_OPTION_MAX;
- i++, j--) {
- index = iwl_ssid_exist(params->ssids[j].ssid,
- params->ssids[j].ssid_len,
- ssids);
- if (index < 0) {
- ssids[i].id = WLAN_EID_SSID;
- ssids[i].len = params->ssids[j].ssid_len;
- memcpy(ssids[i].ssid, params->ssids[j].ssid,
- ssids[i].len);
- *ssid_bitmap |= BIT(i);
- } else {
- *ssid_bitmap |= BIT(index);
- }
- }
-}
-
-static int
-iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
- struct cfg80211_sched_scan_request *req)
-{
- struct iwl_scan_offload_profile *profile;
- struct iwl_scan_offload_profile_cfg *profile_cfg;
- struct iwl_scan_offload_blacklist *blacklist;
- struct iwl_host_cmd cmd = {
- .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
- .len[1] = sizeof(*profile_cfg),
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- .dataflags[1] = IWL_HCMD_DFL_NOCOPY,
- };
- int blacklist_len;
- int i;
- int ret;
-
- if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES))
- return -EIO;
-
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL)
- blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN;
- else
- blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN;
-
- blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL);
- if (!blacklist)
- return -ENOMEM;
-
- profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL);
- if (!profile_cfg) {
- ret = -ENOMEM;
- goto free_blacklist;
- }
-
- cmd.data[0] = blacklist;
- cmd.len[0] = sizeof(*blacklist) * blacklist_len;
- cmd.data[1] = profile_cfg;
-
- /* No blacklist configuration */
-
- profile_cfg->num_profiles = req->n_match_sets;
- profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN;
- profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN;
- profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN;
- if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len)
- profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN;
-
- for (i = 0; i < req->n_match_sets; i++) {
- profile = &profile_cfg->profiles[i];
- profile->ssid_index = i;
- /* Support any cipher and auth algorithm */
- profile->unicast_cipher = 0xff;
- profile->auth_alg = 0xff;
- profile->network_type = IWL_NETWORK_TYPE_ANY;
- profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY;
- profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN;
- }
-
- IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n");
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- kfree(profile_cfg);
-free_blacklist:
- kfree(blacklist);
-
- return ret;
-}
-
-static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm,
- struct cfg80211_sched_scan_request *req)
-{
- if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
- IWL_DEBUG_SCAN(mvm,
- "Sending scheduled scan with filtering, n_match_sets %d\n",
- req->n_match_sets);
- return false;
- }
-
- IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n");
- return true;
-}
-
-static int iwl_mvm_lmac_scan_abort(struct iwl_mvm *mvm)
-{
- int ret;
- struct iwl_host_cmd cmd = {
- .id = SCAN_OFFLOAD_ABORT_CMD,
- };
- u32 status;
-
- ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
- if (ret)
- return ret;
-
- if (status != CAN_ABORT_STATUS) {
- /*
- * The scan abort will return 1 for success or
- * 2 for "failure". A failure condition can be
- * due to simply not being in an active scan which
- * can occur if we send the scan abort before the
- * microcode has notified us that a scan is completed.
- */
- IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status);
- ret = -ENOENT;
- }
-
- return ret;
-}
-
-static void iwl_mvm_scan_fill_tx_cmd(struct iwl_mvm *mvm,
- struct iwl_scan_req_tx_cmd *tx_cmd,
- bool no_cck)
-{
- tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
- TX_CMD_FLG_BT_DIS);
- tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
- IEEE80211_BAND_2GHZ,
- no_cck);
- tx_cmd[0].sta_id = mvm->aux_sta.sta_id;
-
- tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
- TX_CMD_FLG_BT_DIS);
- tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
- IEEE80211_BAND_5GHZ,
- no_cck);
- tx_cmd[1].sta_id = mvm->aux_sta.sta_id;
-}
-
-static void
-iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm,
- struct ieee80211_channel **channels,
- int n_channels, u32 ssid_bitmap,
- struct iwl_scan_req_lmac *cmd)
-{
- struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data;
- int i;
-
- for (i = 0; i < n_channels; i++) {
- channel_cfg[i].channel_num =
- cpu_to_le16(channels[i]->hw_value);
- channel_cfg[i].iter_count = cpu_to_le16(1);
- channel_cfg[i].iter_interval = 0;
- channel_cfg[i].flags =
- cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL |
- ssid_bitmap);
- }
-}
-
-static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies,
- size_t len, u8 *const pos)
-{
- static const u8 before_ds_params[] = {
- WLAN_EID_SSID,
- WLAN_EID_SUPP_RATES,
- WLAN_EID_REQUEST,
- WLAN_EID_EXT_SUPP_RATES,
- };
- size_t offs;
- u8 *newpos = pos;
-
- if (!iwl_mvm_rrm_scan_needed(mvm)) {
- memcpy(newpos, ies, len);
- return newpos + len;
- }
-
- offs = ieee80211_ie_split(ies, len,
- before_ds_params,
- ARRAY_SIZE(before_ds_params),
- 0);
-
- memcpy(newpos, ies, offs);
- newpos += offs;
-
- /* Add a placeholder for DS Parameter Set element */
- *newpos++ = WLAN_EID_DS_PARAMS;
- *newpos++ = 1;
- *newpos++ = 0;
-
- memcpy(newpos, ies + offs, len - offs);
- newpos += len - offs;
-
- return newpos;
-}
-
-static void
-iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_scan_ies *ies,
- struct iwl_mvm_scan_params *params)
-{
- struct ieee80211_mgmt *frame = (void *)params->preq.buf;
- u8 *pos, *newpos;
- const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
- params->mac_addr : NULL;
-
- /*
- * Unfortunately, right now the offload scan doesn't support randomising
- * within the firmware, so until the firmware API is ready we implement
- * it in the driver. This means that the scan iterations won't really be
- * random, only when it's restarted, but at least that helps a bit.
- */
- if (mac_addr)
- get_random_mask_addr(frame->sa, mac_addr,
- params->mac_addr_mask);
- else
- memcpy(frame->sa, vif->addr, ETH_ALEN);
-
- frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
- eth_broadcast_addr(frame->da);
- eth_broadcast_addr(frame->bssid);
- frame->seq_ctrl = 0;
-
- pos = frame->u.probe_req.variable;
- *pos++ = WLAN_EID_SSID;
- *pos++ = 0;
-
- params->preq.mac_header.offset = 0;
- params->preq.mac_header.len = cpu_to_le16(24 + 2);
-
- /* Insert ds parameter set element on 2.4 GHz band */
- newpos = iwl_mvm_copy_and_insert_ds_elem(mvm,
- ies->ies[IEEE80211_BAND_2GHZ],
- ies->len[IEEE80211_BAND_2GHZ],
- pos);
- params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf);
- params->preq.band_data[0].len = cpu_to_le16(newpos - pos);
- pos = newpos;
-
- memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
- ies->len[IEEE80211_BAND_5GHZ]);
- params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf);
- params->preq.band_data[1].len =
- cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]);
- pos += ies->len[IEEE80211_BAND_5GHZ];
-
- memcpy(pos, ies->common_ies, ies->common_ie_len);
- params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf);
- params->preq.common_data.len = cpu_to_le16(ies->common_ie_len);
-}
-
-static __le32 iwl_mvm_scan_priority(struct iwl_mvm *mvm,
- enum iwl_scan_priority_ext prio)
-{
- if (fw_has_api(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY))
- return cpu_to_le32(prio);
-
- if (prio <= IWL_SCAN_PRIORITY_EXT_2)
- return cpu_to_le32(IWL_SCAN_PRIORITY_LOW);
-
- if (prio <= IWL_SCAN_PRIORITY_EXT_4)
- return cpu_to_le32(IWL_SCAN_PRIORITY_MEDIUM);
-
- return cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
-}
-
-static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm,
- struct iwl_scan_req_lmac *cmd,
- struct iwl_mvm_scan_params *params)
-{
- cmd->active_dwell = scan_timing[params->type].dwell_active;
- cmd->passive_dwell = scan_timing[params->type].dwell_passive;
- cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented;
- cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time);
- cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time);
- cmd->scan_prio = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
-}
-
-static inline bool iwl_mvm_scan_fits(struct iwl_mvm *mvm, int n_ssids,
- struct ieee80211_scan_ies *ies,
- int n_channels)
-{
- return ((n_ssids <= PROBE_OPTION_MAX) &&
- (n_channels <= mvm->fw->ucode_capa.n_scan_channels) &
- (ies->common_ie_len +
- ies->len[NL80211_BAND_2GHZ] +
- ies->len[NL80211_BAND_5GHZ] <=
- iwl_mvm_max_scan_ie_fw_cmd_room(mvm)));
-}
-
-static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa;
-
- /* We can only use EBS if:
- * 1. the feature is supported;
- * 2. the last EBS was successful;
- * 3. if only single scan, the single scan EBS API is supported;
- * 4. it's not a p2p find operation.
- */
- return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) &&
- mvm->last_ebs_successful &&
- vif->type != NL80211_IFTYPE_P2P_DEVICE);
-}
-
-static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm,
- struct iwl_mvm_scan_params *params)
-{
- int flags = 0;
-
- if (params->n_ssids == 0)
- flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
-
- if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0)
- flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
-
- if (params->type == IWL_SCAN_TYPE_FRAGMENTED)
- flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
-
- if (iwl_mvm_rrm_scan_needed(mvm))
- flags |= IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED;
-
- if (params->pass_all)
- flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
- else
- flags |= IWL_MVM_LMAC_SCAN_FLAG_MATCH;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvm->scan_iter_notif_enabled)
- flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE;
-#endif
-
- return flags;
-}
-
-static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_mvm_scan_params *params)
-{
- struct iwl_scan_req_lmac *cmd = mvm->scan_cmd;
- struct iwl_scan_probe_req *preq =
- (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
- mvm->fw->ucode_capa.n_scan_channels);
- u32 ssid_bitmap = 0;
- int i;
-
- lockdep_assert_held(&mvm->mutex);
-
- memset(cmd, 0, ksize(cmd));
-
- if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
- return -EINVAL;
-
- iwl_mvm_scan_lmac_dwell(mvm, cmd, params);
-
- cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm);
- cmd->iter_num = cpu_to_le32(1);
- cmd->n_channels = (u8)params->n_channels;
-
- cmd->delay = cpu_to_le32(params->delay);
-
- cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params));
-
- cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band);
- cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
- MAC_FILTER_IN_BEACON);
- iwl_mvm_scan_fill_tx_cmd(mvm, cmd->tx_cmd, params->no_cck);
- iwl_scan_build_ssids(params, cmd->direct_scan, &ssid_bitmap);
-
- /* this API uses bits 1-20 instead of 0-19 */
- ssid_bitmap <<= 1;
-
- for (i = 0; i < params->n_scan_plans; i++) {
- struct cfg80211_sched_scan_plan *scan_plan =
- &params->scan_plans[i];
-
- cmd->schedule[i].delay =
- cpu_to_le16(scan_plan->interval);
- cmd->schedule[i].iterations = scan_plan->iterations;
- cmd->schedule[i].full_scan_mul = 1;
- }
-
- /*
- * If the number of iterations of the last scan plan is set to
- * zero, it should run infinitely. However, this is not always the case.
- * For example, when regular scan is requested the driver sets one scan
- * plan with one iteration.
- */
- if (!cmd->schedule[i - 1].iterations)
- cmd->schedule[i - 1].iterations = 0xff;
-
- if (iwl_mvm_scan_use_ebs(mvm, vif)) {
- cmd->channel_opt[0].flags =
- cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
- IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
- IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
- cmd->channel_opt[0].non_ebs_ratio =
- cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO);
- cmd->channel_opt[1].flags =
- cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
- IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
- IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
- cmd->channel_opt[1].non_ebs_ratio =
- cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO);
- }
-
- iwl_mvm_lmac_scan_cfg_channels(mvm, params->channels,
- params->n_channels, ssid_bitmap, cmd);
-
- *preq = params->preq;
-
- return 0;
-}
-
-static int rate_to_scan_rate_flag(unsigned int rate)
-{
- static const int rate_to_scan_rate[IWL_RATE_COUNT] = {
- [IWL_RATE_1M_INDEX] = SCAN_CONFIG_RATE_1M,
- [IWL_RATE_2M_INDEX] = SCAN_CONFIG_RATE_2M,
- [IWL_RATE_5M_INDEX] = SCAN_CONFIG_RATE_5M,
- [IWL_RATE_11M_INDEX] = SCAN_CONFIG_RATE_11M,
- [IWL_RATE_6M_INDEX] = SCAN_CONFIG_RATE_6M,
- [IWL_RATE_9M_INDEX] = SCAN_CONFIG_RATE_9M,
- [IWL_RATE_12M_INDEX] = SCAN_CONFIG_RATE_12M,
- [IWL_RATE_18M_INDEX] = SCAN_CONFIG_RATE_18M,
- [IWL_RATE_24M_INDEX] = SCAN_CONFIG_RATE_24M,
- [IWL_RATE_36M_INDEX] = SCAN_CONFIG_RATE_36M,
- [IWL_RATE_48M_INDEX] = SCAN_CONFIG_RATE_48M,
- [IWL_RATE_54M_INDEX] = SCAN_CONFIG_RATE_54M,
- };
-
- return rate_to_scan_rate[rate];
-}
-
-static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm)
-{
- struct ieee80211_supported_band *band;
- unsigned int rates = 0;
- int i;
-
- band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
- for (i = 0; i < band->n_bitrates; i++)
- rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
- band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
- for (i = 0; i < band->n_bitrates; i++)
- rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
-
- /* Set both basic rates and supported rates */
- rates |= SCAN_CONFIG_SUPPORTED_RATE(rates);
-
- return cpu_to_le32(rates);
-}
-
-int iwl_mvm_config_scan(struct iwl_mvm *mvm)
-{
- struct iwl_scan_config *scan_config;
- struct ieee80211_supported_band *band;
- int num_channels =
- mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
- mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
- int ret, i, j = 0, cmd_size;
- struct iwl_host_cmd cmd = {
- .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0),
- };
-
- if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels))
- return -ENOBUFS;
-
- cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels;
-
- scan_config = kzalloc(cmd_size, GFP_KERNEL);
- if (!scan_config)
- return -ENOMEM;
-
- scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE |
- SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
- SCAN_CONFIG_FLAG_SET_TX_CHAINS |
- SCAN_CONFIG_FLAG_SET_RX_CHAINS |
- SCAN_CONFIG_FLAG_SET_ALL_TIMES |
- SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
- SCAN_CONFIG_FLAG_SET_MAC_ADDR |
- SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
- SCAN_CONFIG_N_CHANNELS(num_channels));
- scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
- scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
- scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm);
- scan_config->out_of_channel_time = cpu_to_le32(170);
- scan_config->suspend_time = cpu_to_le32(30);
- scan_config->dwell_active = 20;
- scan_config->dwell_passive = 110;
- scan_config->dwell_fragmented = 20;
-
- memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN);
-
- scan_config->bcast_sta_id = mvm->aux_sta.sta_id;
- scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS |
- IWL_CHANNEL_FLAG_ACCURATE_EBS |
- IWL_CHANNEL_FLAG_EBS_ADD |
- IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
-
- band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
- for (i = 0; i < band->n_channels; i++, j++)
- scan_config->channel_array[j] = band->channels[i].hw_value;
- band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
- for (i = 0; i < band->n_channels; i++, j++)
- scan_config->channel_array[j] = band->channels[i].hw_value;
-
- cmd.data[0] = scan_config;
- cmd.len[0] = cmd_size;
- cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
-
- IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n");
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
-
- kfree(scan_config);
- return ret;
-}
-
-static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status)
-{
- int i;
-
- for (i = 0; i < mvm->max_scans; i++)
- if (mvm->scan_uid_status[i] == status)
- return i;
-
- return -ENOENT;
-}
-
-static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params)
-{
- return params->n_scan_plans == 1 &&
- params->scan_plans[0].iterations == 1;
-}
-
-static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
- struct iwl_scan_req_umac *cmd,
- struct iwl_mvm_scan_params *params)
-{
- cmd->active_dwell = scan_timing[params->type].dwell_active;
- cmd->passive_dwell = scan_timing[params->type].dwell_passive;
- cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented;
- cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time);
- cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time);
- cmd->scan_priority =
- iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
-
- if (iwl_mvm_is_regular_scan(params))
- cmd->ooc_priority =
- iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
- else
- cmd->ooc_priority =
- iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_2);
-}
-
-static void
-iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,
- struct ieee80211_channel **channels,
- int n_channels, u32 ssid_bitmap,
- struct iwl_scan_req_umac *cmd)
-{
- struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data;
- int i;
-
- for (i = 0; i < n_channels; i++) {
- channel_cfg[i].flags = cpu_to_le32(ssid_bitmap);
- channel_cfg[i].channel_num = channels[i]->hw_value;
- channel_cfg[i].iter_count = 1;
- channel_cfg[i].iter_interval = 0;
- }
-}
-
-static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
- struct iwl_mvm_scan_params *params)
-{
- int flags = 0;
-
- if (params->n_ssids == 0)
- flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE;
-
- if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0)
- flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT;
-
- if (params->type == IWL_SCAN_TYPE_FRAGMENTED)
- flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED;
-
- if (iwl_mvm_rrm_scan_needed(mvm))
- flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED;
-
- if (params->pass_all)
- flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
- else
- flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH;
-
- if (!iwl_mvm_is_regular_scan(params))
- flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvm->scan_iter_notif_enabled)
- flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE;
-#endif
- return flags;
-}
-
-static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_mvm_scan_params *params,
- int type)
-{
- struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
- struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
- sizeof(struct iwl_scan_channel_cfg_umac) *
- mvm->fw->ucode_capa.n_scan_channels;
- int uid, i;
- u32 ssid_bitmap = 0;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
- return -EINVAL;
-
- uid = iwl_mvm_scan_uid_by_status(mvm, 0);
- if (uid < 0)
- return uid;
-
- memset(cmd, 0, ksize(cmd));
-
- iwl_mvm_scan_umac_dwell(mvm, cmd, params);
-
- mvm->scan_uid_status[uid] = type;
-
- cmd->uid = cpu_to_le32(uid);
- cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params));
-
- if (type == IWL_MVM_SCAN_SCHED)
- cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE);
-
- if (iwl_mvm_scan_use_ebs(mvm, vif))
- cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS |
- IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
- IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
-
- cmd->n_channels = params->n_channels;
-
- iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap);
-
- iwl_mvm_umac_scan_cfg_channels(mvm, params->channels,
- params->n_channels, ssid_bitmap, cmd);
-
- for (i = 0; i < params->n_scan_plans; i++) {
- struct cfg80211_sched_scan_plan *scan_plan =
- &params->scan_plans[i];
-
- sec_part->schedule[i].iter_count = scan_plan->iterations;
- sec_part->schedule[i].interval =
- cpu_to_le16(scan_plan->interval);
- }
-
- /*
- * If the number of iterations of the last scan plan is set to
- * zero, it should run infinitely. However, this is not always the case.
- * For example, when regular scan is requested the driver sets one scan
- * plan with one iteration.
- */
- if (!sec_part->schedule[i - 1].iter_count)
- sec_part->schedule[i - 1].iter_count = 0xff;
-
- sec_part->delay = cpu_to_le16(params->delay);
- sec_part->preq = params->preq;
-
- return 0;
-}
-
-static int iwl_mvm_num_scans(struct iwl_mvm *mvm)
-{
- return hweight32(mvm->scan_status & IWL_MVM_SCAN_MASK);
-}
-
-static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type)
-{
- /* This looks a bit arbitrary, but the idea is that if we run
- * out of possible simultaneous scans and the userspace is
- * trying to run a scan type that is already running, we
- * return -EBUSY. But if the userspace wants to start a
- * different type of scan, we stop the opposite type to make
- * space for the new request. The reason is backwards
- * compatibility with old wpa_supplicant that wouldn't stop a
- * scheduled scan before starting a normal scan.
- */
-
- if (iwl_mvm_num_scans(mvm) < mvm->max_scans)
- return 0;
-
- /* Use a switch, even though this is a bitmask, so that more
- * than one bits set will fall in default and we will warn.
- */
- switch (type) {
- case IWL_MVM_SCAN_REGULAR:
- if (mvm->scan_status & IWL_MVM_SCAN_REGULAR_MASK)
- return -EBUSY;
- return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);
- case IWL_MVM_SCAN_SCHED:
- if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK)
- return -EBUSY;
- iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
- case IWL_MVM_SCAN_NETDETECT:
- /* No need to stop anything for net-detect since the
- * firmware is restarted anyway. This way, any sched
- * scans that were running will be restarted when we
- * resume.
- */
- return 0;
- default:
- WARN_ON(1);
- break;
- }
-
- return -EIO;
-}
-
-int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req,
- struct ieee80211_scan_ies *ies)
-{
- struct iwl_host_cmd hcmd = {
- .len = { iwl_mvm_scan_size(mvm), },
- .data = { mvm->scan_cmd, },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
- struct iwl_mvm_scan_params params = {};
- int ret;
- struct cfg80211_sched_scan_plan scan_plan = { .iterations = 1 };
-
- lockdep_assert_held(&mvm->mutex);
-
- if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
- IWL_ERR(mvm, "scan while LAR regdomain is not set\n");
- return -EBUSY;
- }
-
- ret = iwl_mvm_check_running_scans(mvm, IWL_MVM_SCAN_REGULAR);
- if (ret)
- return ret;
-
- /* we should have failed registration if scan_cmd was NULL */
- if (WARN_ON(!mvm->scan_cmd))
- return -ENOMEM;
-
- if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels))
- return -ENOBUFS;
-
- params.n_ssids = req->n_ssids;
- params.flags = req->flags;
- params.n_channels = req->n_channels;
- params.delay = 0;
- params.ssids = req->ssids;
- params.channels = req->channels;
- params.mac_addr = req->mac_addr;
- params.mac_addr_mask = req->mac_addr_mask;
- params.no_cck = req->no_cck;
- params.pass_all = true;
- params.n_match_sets = 0;
- params.match_sets = NULL;
-
- params.scan_plans = &scan_plan;
- params.n_scan_plans = 1;
-
- params.type = iwl_mvm_get_scan_type(mvm, vif, &params);
-
- iwl_mvm_build_scan_probe(mvm, vif, ies, &params);
-
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
- hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0);
- ret = iwl_mvm_scan_umac(mvm, vif, &params,
- IWL_MVM_SCAN_REGULAR);
- } else {
- hcmd.id = SCAN_OFFLOAD_REQUEST_CMD;
- ret = iwl_mvm_scan_lmac(mvm, vif, &params);
- }
-
- if (ret)
- return ret;
-
- ret = iwl_mvm_send_cmd(mvm, &hcmd);
- if (ret) {
- /* If the scan failed, it usually means that the FW was unable
- * to allocate the time events. Warn on it, but maybe we
- * should try to send the command again with different params.
- */
- IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
- return ret;
- }
-
- IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
- mvm->scan_status |= IWL_MVM_SCAN_REGULAR;
- iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
-
- return 0;
-}
-
-int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct cfg80211_sched_scan_request *req,
- struct ieee80211_scan_ies *ies,
- int type)
-{
- struct iwl_host_cmd hcmd = {
- .len = { iwl_mvm_scan_size(mvm), },
- .data = { mvm->scan_cmd, },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
- struct iwl_mvm_scan_params params = {};
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
- IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n");
- return -EBUSY;
- }
-
- ret = iwl_mvm_check_running_scans(mvm, type);
- if (ret)
- return ret;
-
- /* we should have failed registration if scan_cmd was NULL */
- if (WARN_ON(!mvm->scan_cmd))
- return -ENOMEM;
-
- if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels))
- return -ENOBUFS;
-
- params.n_ssids = req->n_ssids;
- params.flags = req->flags;
- params.n_channels = req->n_channels;
- params.ssids = req->ssids;
- params.channels = req->channels;
- params.mac_addr = req->mac_addr;
- params.mac_addr_mask = req->mac_addr_mask;
- params.no_cck = false;
- params.pass_all = iwl_mvm_scan_pass_all(mvm, req);
- params.n_match_sets = req->n_match_sets;
- params.match_sets = req->match_sets;
- if (!req->n_scan_plans)
- return -EINVAL;
-
- params.n_scan_plans = req->n_scan_plans;
- params.scan_plans = req->scan_plans;
-
- params.type = iwl_mvm_get_scan_type(mvm, vif, &params);
-
- /* In theory, LMAC scans can handle a 32-bit delay, but since
- * waiting for over 18 hours to start the scan is a bit silly
- * and to keep it aligned with UMAC scans (which only support
- * 16-bit delays), trim it down to 16-bits.
- */
- if (req->delay > U16_MAX) {
- IWL_DEBUG_SCAN(mvm,
- "delay value is > 16-bits, set to max possible\n");
- params.delay = U16_MAX;
- } else {
- params.delay = req->delay;
- }
-
- ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
- if (ret)
- return ret;
-
- iwl_mvm_build_scan_probe(mvm, vif, ies, &params);
-
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
- hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0);
- ret = iwl_mvm_scan_umac(mvm, vif, &params, IWL_MVM_SCAN_SCHED);
- } else {
- hcmd.id = SCAN_OFFLOAD_REQUEST_CMD;
- ret = iwl_mvm_scan_lmac(mvm, vif, &params);
- }
-
- if (ret)
- return ret;
-
- ret = iwl_mvm_send_cmd(mvm, &hcmd);
- if (!ret) {
- IWL_DEBUG_SCAN(mvm,
- "Sched scan request was sent successfully\n");
- mvm->scan_status |= type;
- } else {
- /* If the scan failed, it usually means that the FW was unable
- * to allocate the time events. Warn on it, but maybe we
- * should try to send the command again with different params.
- */
- IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret);
- }
-
- return ret;
-}
-
-void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_umac_scan_complete *notif = (void *)pkt->data;
- u32 uid = __le32_to_cpu(notif->uid);
- bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED);
-
- if (WARN_ON(!(mvm->scan_uid_status[uid] & mvm->scan_status)))
- return;
-
- /* if the scan is already stopping, we don't need to notify mac80211 */
- if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_REGULAR) {
- ieee80211_scan_completed(mvm->hw, aborted);
- iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
- } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) {
- ieee80211_sched_scan_stopped(mvm->hw);
- }
-
- mvm->scan_status &= ~mvm->scan_uid_status[uid];
- IWL_DEBUG_SCAN(mvm,
- "Scan completed, uid %u type %u, status %s, EBS status %s\n",
- uid, mvm->scan_uid_status[uid],
- notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
- "completed" : "aborted",
- iwl_mvm_ebs_status_str(notif->ebs_status));
- IWL_DEBUG_SCAN(mvm,
- "Last line %d, Last iteration %d, Time from last iteration %d\n",
- notif->last_schedule, notif->last_iter,
- __le32_to_cpu(notif->time_from_last_iter));
-
- if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS &&
- notif->ebs_status != IWL_SCAN_EBS_INACTIVE)
- mvm->last_ebs_successful = false;
-
- mvm->scan_uid_status[uid] = 0;
-}
-
-void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data;
- u8 buf[256];
-
- IWL_DEBUG_SCAN(mvm,
- "UMAC Scan iteration complete: status=0x%x scanned_channels=%d channels list: %s\n",
- notif->status, notif->scanned_channels,
- iwl_mvm_dump_channel_list(notif->results,
- notif->scanned_channels, buf,
- sizeof(buf)));
-}
-
-static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type)
-{
- struct iwl_umac_scan_abort cmd = {};
- int uid, ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* We should always get a valid index here, because we already
- * checked that this type of scan was running in the generic
- * code.
- */
- uid = iwl_mvm_scan_uid_by_status(mvm, type);
- if (WARN_ON_ONCE(uid < 0))
- return uid;
-
- cmd.uid = cpu_to_le32(uid);
-
- IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid);
-
- ret = iwl_mvm_send_cmd_pdu(mvm,
- iwl_cmd_id(SCAN_ABORT_UMAC,
- IWL_ALWAYS_LONG_GROUP, 0),
- 0, sizeof(cmd), &cmd);
- if (!ret)
- mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT;
-
- return ret;
-}
-
-static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type)
-{
- struct iwl_notification_wait wait_scan_done;
- static const u16 scan_done_notif[] = { SCAN_COMPLETE_UMAC,
- SCAN_OFFLOAD_COMPLETE, };
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
- scan_done_notif,
- ARRAY_SIZE(scan_done_notif),
- NULL, NULL);
-
- IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type);
-
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
- ret = iwl_mvm_umac_scan_abort(mvm, type);
- else
- ret = iwl_mvm_lmac_scan_abort(mvm);
-
- if (ret) {
- IWL_DEBUG_SCAN(mvm, "couldn't stop scan type %d\n", type);
- iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
- return ret;
- }
-
- ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
-
- return ret;
-}
-
-int iwl_mvm_scan_size(struct iwl_mvm *mvm)
-{
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
- return sizeof(struct iwl_scan_req_umac) +
- sizeof(struct iwl_scan_channel_cfg_umac) *
- mvm->fw->ucode_capa.n_scan_channels +
- sizeof(struct iwl_scan_req_umac_tail);
-
- return sizeof(struct iwl_scan_req_lmac) +
- sizeof(struct iwl_scan_channel_cfg_lmac) *
- mvm->fw->ucode_capa.n_scan_channels +
- sizeof(struct iwl_scan_probe_req);
-}
-
-/*
- * This function is used in nic restart flow, to inform mac80211 about scans
- * that was aborted by restart flow or by an assert.
- */
-void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm)
-{
- if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
- int uid, i;
-
- uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_REGULAR);
- if (uid >= 0) {
- ieee80211_scan_completed(mvm->hw, true);
- mvm->scan_uid_status[uid] = 0;
- }
- uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED);
- if (uid >= 0 && !mvm->restart_fw) {
- ieee80211_sched_scan_stopped(mvm->hw);
- mvm->scan_uid_status[uid] = 0;
- }
-
- /* We shouldn't have any UIDs still set. Loop over all the
- * UIDs to make sure there's nothing left there and warn if
- * any is found.
- */
- for (i = 0; i < mvm->max_scans; i++) {
- if (WARN_ONCE(mvm->scan_uid_status[i],
- "UMAC scan UID %d status was not cleaned\n",
- i))
- mvm->scan_uid_status[i] = 0;
- }
- } else {
- if (mvm->scan_status & IWL_MVM_SCAN_REGULAR)
- ieee80211_scan_completed(mvm->hw, true);
-
- /* Sched scan will be restarted by mac80211 in
- * restart_hw, so do not report if FW is about to be
- * restarted.
- */
- if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && !mvm->restart_fw)
- ieee80211_sched_scan_stopped(mvm->hw);
- }
-}
-
-int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify)
-{
- int ret;
-
- if (!(mvm->scan_status & type))
- return 0;
-
- if (iwl_mvm_is_radio_killed(mvm)) {
- ret = 0;
- goto out;
- }
-
- ret = iwl_mvm_scan_stop_wait(mvm, type);
- if (!ret)
- mvm->scan_status |= type << IWL_MVM_SCAN_STOPPING_SHIFT;
-out:
- /* Clear the scan status so the next scan requests will
- * succeed and mark the scan as stopping, so that the Rx
- * handler doesn't do anything, as the scan was stopped from
- * above.
- */
- mvm->scan_status &= ~type;
-
- if (type == IWL_MVM_SCAN_REGULAR) {
- /* Since the rx handler won't do anything now, we have
- * to release the scan reference here.
- */
- iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
- if (notify)
- ieee80211_scan_completed(mvm->hw, true);
- } else if (notify) {
- ieee80211_sched_scan_stopped(mvm->hw);
- }
-
- return ret;
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
deleted file mode 100644
index b0f59fdd287c..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/sf.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include "mvm.h"
-
-/* For counting bound interfaces */
-struct iwl_mvm_active_iface_iterator_data {
- struct ieee80211_vif *ignore_vif;
- u8 sta_vif_ap_sta_id;
- enum iwl_sf_state sta_vif_state;
- int num_active_macs;
-};
-
-/*
- * Count bound interfaces which are not p2p, besides data->ignore_vif.
- * data->station_vif will point to one bound vif of type station, if exists.
- */
-static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_active_iface_iterator_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
- vif->type == NL80211_IFTYPE_P2P_DEVICE)
- return;
-
- data->num_active_macs++;
-
- if (vif->type == NL80211_IFTYPE_STATION) {
- data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
- if (vif->bss_conf.assoc)
- data->sta_vif_state = SF_FULL_ON;
- else
- data->sta_vif_state = SF_INIT_OFF;
- }
-}
-
-/*
- * Aging and idle timeouts for the different possible scenarios
- * in default configuration
- */
-static const
-__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
- {
- cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF),
- cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
- },
- {
- cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF),
- cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF)
- },
- {
- cpu_to_le32(SF_MCAST_AGING_TIMER_DEF),
- cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF)
- },
- {
- cpu_to_le32(SF_BA_AGING_TIMER_DEF),
- cpu_to_le32(SF_BA_IDLE_TIMER_DEF)
- },
- {
- cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF),
- cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF)
- },
-};
-
-/*
- * Aging and idle timeouts for the different possible scenarios
- * in single BSS MAC configuration.
- */
-static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
- {
- cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
- cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
- },
- {
- cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
- cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
- },
- {
- cpu_to_le32(SF_MCAST_AGING_TIMER),
- cpu_to_le32(SF_MCAST_IDLE_TIMER)
- },
- {
- cpu_to_le32(SF_BA_AGING_TIMER),
- cpu_to_le32(SF_BA_IDLE_TIMER)
- },
- {
- cpu_to_le32(SF_TX_RE_AGING_TIMER),
- cpu_to_le32(SF_TX_RE_IDLE_TIMER)
- },
-};
-
-static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,
- struct iwl_sf_cfg_cmd *sf_cmd,
- struct ieee80211_sta *sta)
-{
- int i, j, watermark;
-
- sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
-
- /*
- * If we are in association flow - check antenna configuration
- * capabilities of the AP station, and choose the watermark accordingly.
- */
- if (sta) {
- if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
- switch (sta->rx_nss) {
- case 1:
- watermark = SF_W_MARK_SISO;
- break;
- case 2:
- watermark = SF_W_MARK_MIMO2;
- break;
- default:
- watermark = SF_W_MARK_MIMO3;
- break;
- }
- } else {
- watermark = SF_W_MARK_LEGACY;
- }
- /* default watermark value for unassociated mode. */
- } else {
- watermark = SF_W_MARK_MIMO2;
- }
- sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
-
- for (i = 0; i < SF_NUM_SCENARIO; i++) {
- for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
- sf_cmd->long_delay_timeouts[i][j] =
- cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
- }
- }
-
- if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) {
- BUILD_BUG_ON(sizeof(sf_full_timeout) !=
- sizeof(__le32) * SF_NUM_SCENARIO *
- SF_NUM_TIMEOUT_TYPES);
-
- memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
- sizeof(sf_full_timeout));
- } else {
- BUILD_BUG_ON(sizeof(sf_full_timeout_def) !=
- sizeof(__le32) * SF_NUM_SCENARIO *
- SF_NUM_TIMEOUT_TYPES);
-
- memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
- sizeof(sf_full_timeout_def));
- }
-
-}
-
-static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
- enum iwl_sf_state new_state)
-{
- struct iwl_sf_cfg_cmd sf_cmd = {
- .state = cpu_to_le32(SF_FULL_ON),
- };
- struct ieee80211_sta *sta;
- int ret = 0;
-
- if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13)
- sf_cmd.state = cpu_to_le32(new_state);
-
- if (mvm->cfg->disable_dummy_notification)
- sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF);
-
- /*
- * If an associated AP sta changed its antenna configuration, the state
- * will remain FULL_ON but SF parameters need to be reconsidered.
- */
- if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
- return 0;
-
- switch (new_state) {
- case SF_UNINIT:
- if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13)
- iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
- break;
- case SF_FULL_ON:
- if (sta_id == IWL_MVM_STATION_COUNT) {
- IWL_ERR(mvm,
- "No station: Cannot switch SF to FULL_ON\n");
- return -EINVAL;
- }
- rcu_read_lock();
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
- if (IS_ERR_OR_NULL(sta)) {
- IWL_ERR(mvm, "Invalid station id\n");
- rcu_read_unlock();
- return -EINVAL;
- }
- iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta);
- rcu_read_unlock();
- break;
- case SF_INIT_OFF:
- iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
- break;
- default:
- WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
- new_state);
- return -EINVAL;
- }
-
- ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
- sizeof(sf_cmd), &sf_cmd);
- if (!ret)
- mvm->sf_state = new_state;
-
- return ret;
-}
-
-/*
- * Update Smart fifo:
- * Count bound interfaces that are not to be removed, ignoring p2p devices,
- * and set new state accordingly.
- */
-int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
- bool remove_vif)
-{
- enum iwl_sf_state new_state;
- u8 sta_id = IWL_MVM_STATION_COUNT;
- struct iwl_mvm_vif *mvmvif = NULL;
- struct iwl_mvm_active_iface_iterator_data data = {
- .ignore_vif = changed_vif,
- .sta_vif_state = SF_UNINIT,
- .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
- };
-
- /*
- * Ignore the call if we are in HW Restart flow, or if the handled
- * vif is a p2p device.
- */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
- (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
- return 0;
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_bound_iface_iterator,
- &data);
-
- /* If changed_vif exists and is not to be removed, add to the count */
- if (changed_vif && !remove_vif)
- data.num_active_macs++;
-
- switch (data.num_active_macs) {
- case 0:
- /* If there are no active macs - change state to SF_INIT_OFF */
- new_state = SF_INIT_OFF;
- break;
- case 1:
- if (remove_vif) {
- /* The one active mac left is of type station
- * and we filled the relevant data during iteration
- */
- new_state = data.sta_vif_state;
- sta_id = data.sta_vif_ap_sta_id;
- } else {
- if (WARN_ON(!changed_vif))
- return -EINVAL;
- if (changed_vif->type != NL80211_IFTYPE_STATION) {
- new_state = SF_UNINIT;
- } else if (changed_vif->bss_conf.assoc &&
- changed_vif->bss_conf.dtim_period) {
- mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
- sta_id = mvmvif->ap_sta_id;
- new_state = SF_FULL_ON;
- } else {
- new_state = SF_INIT_OFF;
- }
- }
- break;
- default:
- /* If there are multiple active macs - change to SF_UNINIT */
- new_state = SF_UNINIT;
- }
- return iwl_mvm_sf_config(mvm, sta_id, new_state);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
deleted file mode 100644
index 2b976b110207..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ /dev/null
@@ -1,1819 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <net/mac80211.h>
-
-#include "mvm.h"
-#include "sta.h"
-#include "rs.h"
-
-static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,
- enum nl80211_iftype iftype)
-{
- int sta_id;
- u32 reserved_ids = 0;
-
- BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32);
- WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
-
- lockdep_assert_held(&mvm->mutex);
-
- /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */
- if (iftype != NL80211_IFTYPE_STATION)
- reserved_ids = BIT(0);
-
- /* Don't take rcu_read_lock() since we are protected by mvm->mutex */
- for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) {
- if (BIT(sta_id) & reserved_ids)
- continue;
-
- if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex)))
- return sta_id;
- }
- return IWL_MVM_STATION_COUNT;
-}
-
-/* send station add/update command to firmware */
-int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- bool update)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_add_sta_cmd add_sta_cmd = {
- .sta_id = mvm_sta->sta_id,
- .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color),
- .add_modify = update ? 1 : 0,
- .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK |
- STA_FLG_MIMO_EN_MSK),
- };
- int ret;
- u32 status;
- u32 agg_size = 0, mpdu_dens = 0;
-
- if (!update) {
- add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
- memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN);
- }
-
- switch (sta->bandwidth) {
- case IEEE80211_STA_RX_BW_160:
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ);
- /* fall through */
- case IEEE80211_STA_RX_BW_80:
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ);
- /* fall through */
- case IEEE80211_STA_RX_BW_40:
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ);
- /* fall through */
- case IEEE80211_STA_RX_BW_20:
- if (sta->ht_cap.ht_supported)
- add_sta_cmd.station_flags |=
- cpu_to_le32(STA_FLG_FAT_EN_20MHZ);
- break;
- }
-
- switch (sta->rx_nss) {
- case 1:
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
- break;
- case 2:
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2);
- break;
- case 3 ... 8:
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3);
- break;
- }
-
- switch (sta->smps_mode) {
- case IEEE80211_SMPS_AUTOMATIC:
- case IEEE80211_SMPS_NUM_MODES:
- WARN_ON(1);
- break;
- case IEEE80211_SMPS_STATIC:
- /* override NSS */
- add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK);
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
- break;
- case IEEE80211_SMPS_DYNAMIC:
- add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT);
- break;
- case IEEE80211_SMPS_OFF:
- /* nothing */
- break;
- }
-
- if (sta->ht_cap.ht_supported) {
- add_sta_cmd.station_flags_msk |=
- cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK |
- STA_FLG_AGG_MPDU_DENS_MSK);
-
- mpdu_dens = sta->ht_cap.ampdu_density;
- }
-
- if (sta->vht_cap.vht_supported) {
- agg_size = sta->vht_cap.cap &
- IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
- agg_size >>=
- IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
- } else if (sta->ht_cap.ht_supported) {
- agg_size = sta->ht_cap.ampdu_factor;
- }
-
- add_sta_cmd.station_flags |=
- cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT);
- add_sta_cmd.station_flags |=
- cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT);
-
- status = ADD_STA_SUCCESS;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
- &add_sta_cmd, &status);
- if (ret)
- return ret;
-
- switch (status) {
- case ADD_STA_SUCCESS:
- IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n");
- break;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "ADD_STA failed\n");
- break;
- }
-
- return ret;
-}
-
-static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
-{
- unsigned long used_hw_queues;
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- unsigned int wdg_timeout =
- iwl_mvm_get_wd_timeout(mvm, NULL, true, false);
- u32 ac;
-
- lockdep_assert_held(&mvm->mutex);
-
- used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL);
-
- /* Find available queues, and allocate them to the ACs */
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
- u8 queue = find_first_zero_bit(&used_hw_queues,
- mvm->first_agg_queue);
-
- if (queue >= mvm->first_agg_queue) {
- IWL_ERR(mvm, "Failed to allocate STA queue\n");
- return -EBUSY;
- }
-
- __set_bit(queue, &used_hw_queues);
- mvmsta->hw_queue[ac] = queue;
- }
-
- /* Found a place for all queues - enable them */
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
- iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac],
- mvmsta->hw_queue[ac],
- iwl_mvm_ac_to_tx_fifo[ac], 0,
- wdg_timeout);
- mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]);
- }
-
- return 0;
-}
-
-static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- unsigned long sta_msk;
- int i;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* disable the TDLS STA-specific queues */
- sta_msk = mvmsta->tfd_queue_msk;
- for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE)
- iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0);
-}
-
-int iwl_mvm_add_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- int i, ret, sta_id;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- sta_id = iwl_mvm_find_free_sta_id(mvm,
- ieee80211_vif_type_p2p(vif));
- else
- sta_id = mvm_sta->sta_id;
-
- if (sta_id == IWL_MVM_STATION_COUNT)
- return -ENOSPC;
-
- if (vif->type == NL80211_IFTYPE_AP) {
- mvmvif->ap_assoc_sta_count++;
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- }
-
- spin_lock_init(&mvm_sta->lock);
-
- mvm_sta->sta_id = sta_id;
- mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color);
- mvm_sta->vif = vif;
- mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
- mvm_sta->tx_protection = 0;
- mvm_sta->tt_tx_protection = false;
-
- /* HW restart, don't assume the memory has been zeroed */
- atomic_set(&mvm->pending_frames[sta_id], 0);
- mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */
- mvm_sta->tfd_queue_msk = 0;
-
- /* allocate new queues for a TDLS station */
- if (sta->tdls) {
- ret = iwl_mvm_tdls_sta_init(mvm, sta);
- if (ret)
- return ret;
- } else {
- for (i = 0; i < IEEE80211_NUM_ACS; i++)
- if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
- mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
- }
-
- /* for HW restart - reset everything but the sequence number */
- for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
- u16 seq = mvm_sta->tid_data[i].seq_number;
- memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i]));
- mvm_sta->tid_data[i].seq_number = seq;
- }
- mvm_sta->agg_tids = 0;
-
- ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
- if (ret)
- goto err;
-
- if (vif->type == NL80211_IFTYPE_STATION) {
- if (!sta->tdls) {
- WARN_ON(mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT);
- mvmvif->ap_sta_id = sta_id;
- } else {
- WARN_ON(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT);
- }
- }
-
- rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
-
- return 0;
-
-err:
- iwl_mvm_tdls_sta_deinit(mvm, sta);
- return ret;
-}
-
-int iwl_mvm_update_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- return iwl_mvm_sta_send_to_fw(mvm, sta, true);
-}
-
-int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
- bool drain)
-{
- struct iwl_mvm_add_sta_cmd cmd = {};
- int ret;
- u32 status;
-
- lockdep_assert_held(&mvm->mutex);
-
- cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color);
- cmd.sta_id = mvmsta->sta_id;
- cmd.add_modify = STA_MODE_MODIFY;
- cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0;
- cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW);
-
- status = ADD_STA_SUCCESS;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
- &cmd, &status);
- if (ret)
- return ret;
-
- switch (status) {
- case ADD_STA_SUCCESS:
- IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n",
- mvmsta->sta_id);
- break;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "Couldn't drain frames for staid %d\n",
- mvmsta->sta_id);
- break;
- }
-
- return ret;
-}
-
-/*
- * Remove a station from the FW table. Before sending the command to remove
- * the station validate that the station is indeed known to the driver (sanity
- * only).
- */
-static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)
-{
- struct ieee80211_sta *sta;
- struct iwl_mvm_rm_sta_cmd rm_sta_cmd = {
- .sta_id = sta_id,
- };
- int ret;
-
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
-
- /* Note: internal stations are marked as error values */
- if (!sta) {
- IWL_ERR(mvm, "Invalid station id\n");
- return -EINVAL;
- }
-
- ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0,
- sizeof(rm_sta_cmd), &rm_sta_cmd);
- if (ret) {
- IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
- return ret;
- }
-
- return 0;
-}
-
-void iwl_mvm_sta_drained_wk(struct work_struct *wk)
-{
- struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk);
- u8 sta_id;
-
- /*
- * The mutex is needed because of the SYNC cmd, but not only: if the
- * work would run concurrently with iwl_mvm_rm_sta, it would run before
- * iwl_mvm_rm_sta sets the station as busy, and exit. Then
- * iwl_mvm_rm_sta would set the station as busy, and nobody will clean
- * that later.
- */
- mutex_lock(&mvm->mutex);
-
- for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) {
- int ret;
- struct ieee80211_sta *sta =
- rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
-
- /*
- * This station is in use or RCU-removed; the latter happens in
- * managed mode, where mac80211 removes the station before we
- * can remove it from firmware (we can only do that after the
- * MAC is marked unassociated), and possibly while the deauth
- * frame to disconnect from the AP is still queued. Then, the
- * station pointer is -ENOENT when the last skb is reclaimed.
- */
- if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT)
- continue;
-
- if (PTR_ERR(sta) == -EINVAL) {
- IWL_ERR(mvm, "Drained sta %d, but it is internal?\n",
- sta_id);
- continue;
- }
-
- if (!sta) {
- IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n",
- sta_id);
- continue;
- }
-
- WARN_ON(PTR_ERR(sta) != -EBUSY);
- /* This station was removed and we waited until it got drained,
- * we can now proceed and remove it.
- */
- ret = iwl_mvm_rm_sta_common(mvm, sta_id);
- if (ret) {
- IWL_ERR(mvm,
- "Couldn't remove sta %d after it was drained\n",
- sta_id);
- continue;
- }
- RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
- clear_bit(sta_id, mvm->sta_drained);
-
- if (mvm->tfd_drained[sta_id]) {
- unsigned long i, msk = mvm->tfd_drained[sta_id];
-
- for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE)
- iwl_mvm_disable_txq(mvm, i, i,
- IWL_MAX_TID_COUNT, 0);
-
- mvm->tfd_drained[sta_id] = 0;
- IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n",
- sta_id, msk);
- }
- }
-
- mutex_unlock(&mvm->mutex);
-}
-
-int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (vif->type == NL80211_IFTYPE_STATION &&
- mvmvif->ap_sta_id == mvm_sta->sta_id) {
- ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
- if (ret)
- return ret;
- /* flush its queues here since we are freeing mvm_sta */
- ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0);
- if (ret)
- return ret;
- ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
- mvm_sta->tfd_queue_msk);
- if (ret)
- return ret;
- ret = iwl_mvm_drain_sta(mvm, mvm_sta, false);
-
- /* if we are associated - we can't remove the AP STA now */
- if (vif->bss_conf.assoc)
- return ret;
-
- /* unassoc - go ahead - remove the AP STA now */
- mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
-
- /* clear d0i3_ap_sta_id if no longer relevant */
- if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
- mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
- }
-
- /*
- * This shouldn't happen - the TDLS channel switch should be canceled
- * before the STA is removed.
- */
- if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) {
- mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
- cancel_delayed_work(&mvm->tdls_cs.dwork);
- }
-
- /*
- * Make sure that the tx response code sees the station as -EBUSY and
- * calls the drain worker.
- */
- spin_lock_bh(&mvm_sta->lock);
- /*
- * There are frames pending on the AC queues for this station.
- * We need to wait until all the frames are drained...
- */
- if (atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) {
- rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
- ERR_PTR(-EBUSY));
- spin_unlock_bh(&mvm_sta->lock);
-
- /* disable TDLS sta queues on drain complete */
- if (sta->tdls) {
- mvm->tfd_drained[mvm_sta->sta_id] =
- mvm_sta->tfd_queue_msk;
- IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n",
- mvm_sta->sta_id);
- }
-
- ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
- } else {
- spin_unlock_bh(&mvm_sta->lock);
-
- if (sta->tdls)
- iwl_mvm_tdls_sta_deinit(mvm, sta);
-
- ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
- RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
- }
-
- return ret;
-}
-
-int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u8 sta_id)
-{
- int ret = iwl_mvm_rm_sta_common(mvm, sta_id);
-
- lockdep_assert_held(&mvm->mutex);
-
- RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
- return ret;
-}
-
-static int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,
- struct iwl_mvm_int_sta *sta,
- u32 qmask, enum nl80211_iftype iftype)
-{
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
- if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
- return -ENOSPC;
- }
-
- sta->tfd_queue_msk = qmask;
-
- /* put a non-NULL value so iterating over the stations won't stop */
- rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL));
- return 0;
-}
-
-static void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
- struct iwl_mvm_int_sta *sta)
-{
- RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
- memset(sta, 0, sizeof(struct iwl_mvm_int_sta));
- sta->sta_id = IWL_MVM_STATION_COUNT;
-}
-
-static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
- struct iwl_mvm_int_sta *sta,
- const u8 *addr,
- u16 mac_id, u16 color)
-{
- struct iwl_mvm_add_sta_cmd cmd;
- int ret;
- u32 status;
-
- lockdep_assert_held(&mvm->mutex);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.sta_id = sta->sta_id;
- cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
- color));
-
- cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk);
-
- if (addr)
- memcpy(cmd.addr, addr, ETH_ALEN);
-
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
- &cmd, &status);
- if (ret)
- return ret;
-
- switch (status) {
- case ADD_STA_SUCCESS:
- IWL_DEBUG_INFO(mvm, "Internal station added.\n");
- return 0;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "Add internal station failed, status=0x%x\n",
- status);
- break;
- }
- return ret;
-}
-
-int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
-{
- unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
- mvm->cfg->base_params->wd_timeout :
- IWL_WATCHDOG_DISABLED;
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Map Aux queue to fifo - needs to happen before adding Aux station */
- iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue,
- IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout);
-
- /* Allocate aux station and assign to it the aux queue */
- ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue),
- NL80211_IFTYPE_UNSPECIFIED);
- if (ret)
- return ret;
-
- ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL,
- MAC_INDEX_AUX, 0);
-
- if (ret)
- iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
- return ret;
-}
-
-void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm)
-{
- lockdep_assert_held(&mvm->mutex);
-
- iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
-}
-
-/*
- * Send the add station command for the vif's broadcast station.
- * Assumes that the station was already allocated.
- *
- * @mvm: the mvm component
- * @vif: the interface to which the broadcast station is added
- * @bsta: the broadcast station to add.
- */
-int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta;
- static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
- const u8 *baddr = _baddr;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (vif->type == NL80211_IFTYPE_ADHOC)
- baddr = vif->bss_conf.bssid;
-
- if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT))
- return -ENOSPC;
-
- return iwl_mvm_add_int_sta_common(mvm, bsta, baddr,
- mvmvif->id, mvmvif->color);
-}
-
-/* Send the FW a request to remove the station from it's internal data
- * structures, but DO NOT remove the entry from the local data structures. */
-int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id);
- if (ret)
- IWL_WARN(mvm, "Failed sending remove station\n");
- return ret;
-}
-
-int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- u32 qmask;
-
- lockdep_assert_held(&mvm->mutex);
-
- qmask = iwl_mvm_mac_get_queues_mask(vif);
-
- /*
- * The firmware defines the TFD queue mask to only be relevant
- * for *unicast* queues, so the multicast (CAB) queue shouldn't
- * be included.
- */
- if (vif->type == NL80211_IFTYPE_AP)
- qmask &= ~BIT(vif->cab_queue);
-
- return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask,
- ieee80211_vif_type_p2p(vif));
-}
-
-/* Allocate a new station entry for the broadcast station to the given vif,
- * and send it to the FW.
- * Note that each P2P mac should have its own broadcast station.
- *
- * @mvm: the mvm component
- * @vif: the interface to which the broadcast station is added
- * @bsta: the broadcast station to add. */
-int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta;
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
- if (ret)
- return ret;
-
- ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
-
- if (ret)
- iwl_mvm_dealloc_int_sta(mvm, bsta);
-
- return ret;
-}
-
-void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
-}
-
-/*
- * Send the FW a request to remove the station from it's internal data
- * structures, and in addition remove it from the local data structure.
- */
-int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_mvm_send_rm_bcast_sta(mvm, vif);
-
- iwl_mvm_dealloc_bcast_sta(mvm, vif);
-
- return ret;
-}
-
-#define IWL_MAX_RX_BA_SESSIONS 16
-
-int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- int tid, u16 ssn, bool start)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_add_sta_cmd cmd = {};
- int ret;
- u32 status;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (start && mvm->rx_ba_sessions >= IWL_MAX_RX_BA_SESSIONS) {
- IWL_WARN(mvm, "Not enough RX BA SESSIONS\n");
- return -ENOSPC;
- }
-
- cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
- cmd.sta_id = mvm_sta->sta_id;
- cmd.add_modify = STA_MODE_MODIFY;
- if (start) {
- cmd.add_immediate_ba_tid = (u8) tid;
- cmd.add_immediate_ba_ssn = cpu_to_le16(ssn);
- } else {
- cmd.remove_immediate_ba_tid = (u8) tid;
- }
- cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID :
- STA_MODIFY_REMOVE_BA_TID;
-
- status = ADD_STA_SUCCESS;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
- &cmd, &status);
- if (ret)
- return ret;
-
- switch (status) {
- case ADD_STA_SUCCESS:
- IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n",
- start ? "start" : "stopp");
- break;
- case ADD_STA_IMMEDIATE_BA_FAILURE:
- IWL_WARN(mvm, "RX BA Session refused by fw\n");
- ret = -ENOSPC;
- break;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n",
- start ? "start" : "stopp", status);
- break;
- }
-
- if (!ret) {
- if (start)
- mvm->rx_ba_sessions++;
- else if (mvm->rx_ba_sessions > 0)
- /* check that restart flow didn't zero the counter */
- mvm->rx_ba_sessions--;
- }
-
- return ret;
-}
-
-static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- int tid, u8 queue, bool start)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_add_sta_cmd cmd = {};
- int ret;
- u32 status;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (start) {
- mvm_sta->tfd_queue_msk |= BIT(queue);
- mvm_sta->tid_disable_agg &= ~BIT(tid);
- } else {
- mvm_sta->tfd_queue_msk &= ~BIT(queue);
- mvm_sta->tid_disable_agg |= BIT(tid);
- }
-
- cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
- cmd.sta_id = mvm_sta->sta_id;
- cmd.add_modify = STA_MODE_MODIFY;
- cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX;
- cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
- cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg);
-
- status = ADD_STA_SUCCESS;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
- &cmd, &status);
- if (ret)
- return ret;
-
- switch (status) {
- case ADD_STA_SUCCESS:
- break;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n",
- start ? "start" : "stopp", status);
- break;
- }
-
- return ret;
-}
-
-const u8 tid_to_mac80211_ac[] = {
- IEEE80211_AC_BE,
- IEEE80211_AC_BK,
- IEEE80211_AC_BK,
- IEEE80211_AC_BE,
- IEEE80211_AC_VI,
- IEEE80211_AC_VI,
- IEEE80211_AC_VO,
- IEEE80211_AC_VO,
-};
-
-static const u8 tid_to_ucode_ac[] = {
- AC_BE,
- AC_BK,
- AC_BK,
- AC_BE,
- AC_VI,
- AC_VI,
- AC_VO,
- AC_VO,
-};
-
-int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_tid_data *tid_data;
- int txq_id;
- int ret;
-
- if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
- return -EINVAL;
-
- if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) {
- IWL_ERR(mvm, "Start AGG when state is not IWL_AGG_OFF %d!\n",
- mvmsta->tid_data[tid].state);
- return -ENXIO;
- }
-
- lockdep_assert_held(&mvm->mutex);
-
- spin_lock_bh(&mvmsta->lock);
-
- /* possible race condition - we entered D0i3 while starting agg */
- if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) {
- spin_unlock_bh(&mvmsta->lock);
- IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n");
- return -EIO;
- }
-
- spin_lock_bh(&mvm->queue_info_lock);
-
- txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue,
- mvm->last_agg_queue);
- if (txq_id < 0) {
- ret = txq_id;
- spin_unlock_bh(&mvm->queue_info_lock);
- IWL_ERR(mvm, "Failed to allocate agg queue\n");
- goto release_locks;
- }
- mvm->queue_info[txq_id].setup_reserved = true;
- spin_unlock_bh(&mvm->queue_info_lock);
-
- tid_data = &mvmsta->tid_data[tid];
- tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
- tid_data->txq_id = txq_id;
- *ssn = tid_data->ssn;
-
- IWL_DEBUG_TX_QUEUES(mvm,
- "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n",
- mvmsta->sta_id, tid, txq_id, tid_data->ssn,
- tid_data->next_reclaimed);
-
- if (tid_data->ssn == tid_data->next_reclaimed) {
- tid_data->state = IWL_AGG_STARTING;
- ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- } else {
- tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA;
- }
-
- ret = 0;
-
-release_locks:
- spin_unlock_bh(&mvmsta->lock);
-
- return ret;
-}
-
-int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u8 buf_size)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
- unsigned int wdg_timeout =
- iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false);
- int queue, fifo, ret;
- u16 ssn;
-
- BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE)
- != IWL_MAX_TID_COUNT);
-
- buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
-
- spin_lock_bh(&mvmsta->lock);
- ssn = tid_data->ssn;
- queue = tid_data->txq_id;
- tid_data->state = IWL_AGG_ON;
- mvmsta->agg_tids |= BIT(tid);
- tid_data->ssn = 0xffff;
- spin_unlock_bh(&mvmsta->lock);
-
- fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
-
- iwl_mvm_enable_agg_txq(mvm, queue,
- vif->hw_queue[tid_to_mac80211_ac[tid]], fifo,
- mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout);
-
- ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
- if (ret)
- return -EIO;
-
- /* No need to mark as reserved */
- spin_lock_bh(&mvm->queue_info_lock);
- mvm->queue_info[queue].setup_reserved = false;
- spin_unlock_bh(&mvm->queue_info_lock);
-
- /*
- * Even though in theory the peer could have different
- * aggregation reorder buffer sizes for different sessions,
- * our ucode doesn't allow for that and has a global limit
- * for each station. Therefore, use the minimum of all the
- * aggregation sessions and our default value.
- */
- mvmsta->max_agg_bufsize =
- min(mvmsta->max_agg_bufsize, buf_size);
- mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
-
- IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
- sta->addr, tid);
-
- return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false);
-}
-
-int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
- u16 txq_id;
- int err;
-
-
- /*
- * If mac80211 is cleaning its state, then say that we finished since
- * our state has been cleared anyway.
- */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- return 0;
- }
-
- spin_lock_bh(&mvmsta->lock);
-
- txq_id = tid_data->txq_id;
-
- IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n",
- mvmsta->sta_id, tid, txq_id, tid_data->state);
-
- mvmsta->agg_tids &= ~BIT(tid);
-
- /* No need to mark as reserved anymore */
- spin_lock_bh(&mvm->queue_info_lock);
- mvm->queue_info[txq_id].setup_reserved = false;
- spin_unlock_bh(&mvm->queue_info_lock);
-
- switch (tid_data->state) {
- case IWL_AGG_ON:
- tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
-
- IWL_DEBUG_TX_QUEUES(mvm,
- "ssn = %d, next_recl = %d\n",
- tid_data->ssn, tid_data->next_reclaimed);
-
- /* There are still packets for this RA / TID in the HW */
- if (tid_data->ssn != tid_data->next_reclaimed) {
- tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA;
- err = 0;
- break;
- }
-
- tid_data->ssn = 0xffff;
- tid_data->state = IWL_AGG_OFF;
- spin_unlock_bh(&mvmsta->lock);
-
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-
- iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
-
- iwl_mvm_disable_txq(mvm, txq_id,
- vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
- 0);
- return 0;
- case IWL_AGG_STARTING:
- case IWL_EMPTYING_HW_QUEUE_ADDBA:
- /*
- * The agg session has been stopped before it was set up. This
- * can happen when the AddBA timer times out for example.
- */
-
- /* No barriers since we are under mutex */
- lockdep_assert_held(&mvm->mutex);
-
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- tid_data->state = IWL_AGG_OFF;
- err = 0;
- break;
- default:
- IWL_ERR(mvm,
- "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
- mvmsta->sta_id, tid, tid_data->state);
- IWL_ERR(mvm,
- "\ttid_data->txq_id = %d\n", tid_data->txq_id);
- err = -EINVAL;
- }
-
- spin_unlock_bh(&mvmsta->lock);
-
- return err;
-}
-
-int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
- u16 txq_id;
- enum iwl_mvm_agg_state old_state;
-
- /*
- * First set the agg state to OFF to avoid calling
- * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty.
- */
- spin_lock_bh(&mvmsta->lock);
- txq_id = tid_data->txq_id;
- IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n",
- mvmsta->sta_id, tid, txq_id, tid_data->state);
- old_state = tid_data->state;
- tid_data->state = IWL_AGG_OFF;
- mvmsta->agg_tids &= ~BIT(tid);
- spin_unlock_bh(&mvmsta->lock);
-
- /* No need to mark as reserved */
- spin_lock_bh(&mvm->queue_info_lock);
- mvm->queue_info[txq_id].setup_reserved = false;
- spin_unlock_bh(&mvm->queue_info_lock);
-
- if (old_state >= IWL_AGG_ON) {
- iwl_mvm_drain_sta(mvm, mvmsta, true);
- if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0))
- IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
- iwl_trans_wait_tx_queue_empty(mvm->trans,
- mvmsta->tfd_queue_msk);
- iwl_mvm_drain_sta(mvm, mvmsta, false);
-
- iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
-
- iwl_mvm_disable_txq(mvm, tid_data->txq_id,
- vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
- 0);
- }
-
- return 0;
-}
-
-static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
-{
- int i, max = -1, max_offs = -1;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Pick the unused key offset with the highest 'deleted'
- * counter. Every time a key is deleted, all the counters
- * are incremented and the one that was just deleted is
- * reset to zero. Thus, the highest counter is the one
- * that was deleted longest ago. Pick that one.
- */
- for (i = 0; i < STA_KEY_MAX_NUM; i++) {
- if (test_bit(i, mvm->fw_key_table))
- continue;
- if (mvm->fw_key_deleted[i] > max) {
- max = mvm->fw_key_deleted[i];
- max_offs = i;
- }
- }
-
- if (max_offs < 0)
- return STA_KEY_IDX_INVALID;
-
- __set_bit(max_offs, mvm->fw_key_table);
-
- return max_offs;
-}
-
-static u8 iwl_mvm_get_key_sta_id(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (sta) {
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
-
- return mvm_sta->sta_id;
- }
-
- /*
- * The device expects GTKs for station interfaces to be
- * installed as GTKs for the AP station. If we have no
- * station ID, then use AP's station ID.
- */
- if (vif->type == NL80211_IFTYPE_STATION &&
- mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
- u8 sta_id = mvmvif->ap_sta_id;
-
- sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
- /*
- * It is possible that the 'sta' parameter is NULL,
- * for example when a GTK is removed - the sta_id will then
- * be the AP ID, and no station was passed by mac80211.
- */
- if (IS_ERR_OR_NULL(sta))
- return IWL_MVM_STATION_COUNT;
-
- return sta_id;
- }
-
- return IWL_MVM_STATION_COUNT;
-}
-
-static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
- struct iwl_mvm_sta *mvm_sta,
- struct ieee80211_key_conf *keyconf, bool mcast,
- u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags,
- u8 key_offset)
-{
- struct iwl_mvm_add_sta_key_cmd cmd = {};
- __le16 key_flags;
- int ret;
- u32 status;
- u16 keyidx;
- int i;
- u8 sta_id = mvm_sta->sta_id;
-
- keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
- STA_KEY_FLG_KEYID_MSK;
- key_flags = cpu_to_le16(keyidx);
- key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP);
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP);
- cmd.tkip_rx_tsc_byte2 = tkip_iv32;
- for (i = 0; i < 5; i++)
- cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
- memcpy(cmd.key, keyconf->key, keyconf->keylen);
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
- memcpy(cmd.key, keyconf->key, keyconf->keylen);
- break;
- case WLAN_CIPHER_SUITE_WEP104:
- key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES);
- /* fall through */
- case WLAN_CIPHER_SUITE_WEP40:
- key_flags |= cpu_to_le16(STA_KEY_FLG_WEP);
- memcpy(cmd.key + 3, keyconf->key, keyconf->keylen);
- break;
- default:
- key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
- memcpy(cmd.key, keyconf->key, keyconf->keylen);
- }
-
- if (mcast)
- key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
-
- cmd.key_offset = key_offset;
- cmd.key_flags = key_flags;
- cmd.sta_id = sta_id;
-
- status = ADD_STA_SUCCESS;
- if (cmd_flags & CMD_ASYNC)
- ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC,
- sizeof(cmd), &cmd);
- else
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
- &cmd, &status);
-
- switch (status) {
- case ADD_STA_SUCCESS:
- IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n");
- break;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n");
- break;
- }
-
- return ret;
-}
-
-static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
- struct ieee80211_key_conf *keyconf,
- u8 sta_id, bool remove_key)
-{
- struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {};
-
- /* verify the key details match the required command's expectations */
- if (WARN_ON((keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC) ||
- (keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) ||
- (keyconf->keyidx != 4 && keyconf->keyidx != 5)))
- return -EINVAL;
-
- igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx);
- igtk_cmd.sta_id = cpu_to_le32(sta_id);
-
- if (remove_key) {
- igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID);
- } else {
- struct ieee80211_key_seq seq;
- const u8 *pn;
-
- memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
- ieee80211_get_key_rx_seq(keyconf, 0, &seq);
- pn = seq.aes_cmac.pn;
- igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |
- ((u64) pn[4] << 8) |
- ((u64) pn[3] << 16) |
- ((u64) pn[2] << 24) |
- ((u64) pn[1] << 32) |
- ((u64) pn[0] << 40));
- }
-
- IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n",
- remove_key ? "removing" : "installing",
- igtk_cmd.sta_id);
-
- return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0,
- sizeof(igtk_cmd), &igtk_cmd);
-}
-
-
-static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (sta)
- return sta->addr;
-
- if (vif->type == NL80211_IFTYPE_STATION &&
- mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
- u8 sta_id = mvmvif->ap_sta_id;
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
- return sta->addr;
- }
-
-
- return NULL;
-}
-
-static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *keyconf,
- u8 key_offset,
- bool mcast)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- int ret;
- const u8 *addr;
- struct ieee80211_key_seq seq;
- u16 p1k[5];
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
- /* get phase 1 key from mac80211 */
- ieee80211_get_key_rx_seq(keyconf, 0, &seq);
- ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- seq.tkip.iv32, p1k, 0, key_offset);
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- 0, NULL, 0, key_offset);
- break;
- default:
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- 0, NULL, 0, key_offset);
- }
-
- return ret;
-}
-
-static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
- struct ieee80211_key_conf *keyconf,
- bool mcast)
-{
- struct iwl_mvm_add_sta_key_cmd cmd = {};
- __le16 key_flags;
- int ret;
- u32 status;
-
- key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
- STA_KEY_FLG_KEYID_MSK);
- key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
- key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
-
- if (mcast)
- key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
-
- cmd.key_flags = key_flags;
- cmd.key_offset = keyconf->hw_key_idx;
- cmd.sta_id = sta_id;
-
- status = ADD_STA_SUCCESS;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
- &cmd, &status);
-
- switch (status) {
- case ADD_STA_SUCCESS:
- IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
- break;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
- break;
- }
-
- return ret;
-}
-
-int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *keyconf,
- u8 key_offset)
-{
- bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
- u8 sta_id;
- int ret;
- static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0};
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Get the station id from the mvm local station table */
- sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta);
- if (sta_id == IWL_MVM_STATION_COUNT) {
- IWL_ERR(mvm, "Failed to find station id\n");
- return -EINVAL;
- }
-
- if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
- ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false);
- goto end;
- }
-
- /*
- * It is possible that the 'sta' parameter is NULL, and thus
- * there is a need to retrieve the sta from the local station table.
- */
- if (!sta) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(sta)) {
- IWL_ERR(mvm, "Invalid station id\n");
- return -EINVAL;
- }
- }
-
- if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
- return -EINVAL;
-
- /* If the key_offset is not pre-assigned, we need to find a
- * new offset to use. In normal cases, the offset is not
- * pre-assigned, but during HW_RESTART we want to reuse the
- * same indices, so we pass them when this function is called.
- *
- * In D3 entry, we need to hardcoded the indices (because the
- * firmware hardcodes the PTK offset to 0). In this case, we
- * need to make sure we don't overwrite the hw_key_idx in the
- * keyconf structure, because otherwise we cannot configure
- * the original ones back when resuming.
- */
- if (key_offset == STA_KEY_IDX_INVALID) {
- key_offset = iwl_mvm_set_fw_key_idx(mvm);
- if (key_offset == STA_KEY_IDX_INVALID)
- return -ENOSPC;
- keyconf->hw_key_idx = key_offset;
- }
-
- ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, mcast);
- if (ret) {
- __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
- goto end;
- }
-
- /*
- * For WEP, the same key is used for multicast and unicast. Upload it
- * again, using the same key offset, and now pointing the other one
- * to the same key slot (offset).
- * If this fails, remove the original as well.
- */
- if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
- ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf,
- key_offset, !mcast);
- if (ret) {
- __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
- __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
- }
- }
-
-end:
- IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
- keyconf->cipher, keyconf->keylen, keyconf->keyidx,
- sta ? sta->addr : zero_addr, ret);
- return ret;
-}
-
-int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *keyconf)
-{
- bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
- u8 sta_id;
- int ret, i;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Get the station id from the mvm local station table */
- sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta);
-
- IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n",
- keyconf->keyidx, sta_id);
-
- if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
- return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true);
-
- if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) {
- IWL_ERR(mvm, "offset %d not used in fw key table.\n",
- keyconf->hw_key_idx);
- return -ENOENT;
- }
-
- /* track which key was deleted last */
- for (i = 0; i < STA_KEY_MAX_NUM; i++) {
- if (mvm->fw_key_deleted[i] < U8_MAX)
- mvm->fw_key_deleted[i]++;
- }
- mvm->fw_key_deleted[keyconf->hw_key_idx] = 0;
-
- if (sta_id == IWL_MVM_STATION_COUNT) {
- IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
- return 0;
- }
-
- ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
- if (ret)
- return ret;
-
- /* delete WEP key twice to get rid of (now useless) offset */
- if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
- ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast);
-
- return ret;
-}
-
-void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta, u32 iv32,
- u16 *phase1key)
-{
- struct iwl_mvm_sta *mvm_sta;
- u8 sta_id;
- bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
-
- rcu_read_lock();
-
- sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta);
- if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
- goto unlock;
-
- if (!sta) {
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
- if (WARN_ON(IS_ERR_OR_NULL(sta))) {
- rcu_read_unlock();
- return;
- }
- }
-
- mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx);
-
- unlock:
- rcu_read_unlock();
-}
-
-void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_add_sta_cmd cmd = {
- .add_modify = STA_MODE_MODIFY,
- .sta_id = mvmsta->sta_id,
- .station_flags_msk = cpu_to_le32(STA_FLG_PS),
- .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
- };
- int ret;
-
- 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);
-}
-
-void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- enum ieee80211_frame_release_type reason,
- u16 cnt, u16 tids, bool more_data,
- bool agg)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_add_sta_cmd cmd = {
- .add_modify = STA_MODE_MODIFY,
- .sta_id = mvmsta->sta_id,
- .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
- .sleep_tx_count = cpu_to_le16(cnt),
- .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
- };
- int tid, ret;
- unsigned long _tids = tids;
-
- /* convert TIDs to ACs - we don't support TSPEC so that's OK
- * Note that this field is reserved and unused by firmware not
- * supporting GO uAPSD, so it's safe to always do this.
- */
- for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT)
- cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]);
-
- /* If we're releasing frames from aggregation queues then check if the
- * all queues combined that we're releasing frames from have
- * - more frames than the service period, in which case more_data
- * needs to be set
- * - fewer than 'cnt' frames, in which case we need to adjust the
- * firmware command (but do that unconditionally)
- */
- if (agg) {
- int remaining = cnt;
-
- spin_lock_bh(&mvmsta->lock);
- for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) {
- struct iwl_mvm_tid_data *tid_data;
- u16 n_queued;
-
- tid_data = &mvmsta->tid_data[tid];
- if (WARN(tid_data->state != IWL_AGG_ON &&
- tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA,
- "TID %d state is %d\n",
- tid, tid_data->state)) {
- spin_unlock_bh(&mvmsta->lock);
- ieee80211_sta_eosp(sta);
- return;
- }
-
- n_queued = iwl_mvm_tid_queued(tid_data);
- if (n_queued > remaining) {
- more_data = true;
- remaining = 0;
- break;
- }
- remaining -= n_queued;
- }
- spin_unlock_bh(&mvmsta->lock);
-
- cmd.sleep_tx_count = cpu_to_le16(cnt - remaining);
- if (WARN_ON(cnt - remaining == 0)) {
- ieee80211_sta_eosp(sta);
- return;
- }
- }
-
- /* Note: this is ignored by firmware not supporting GO uAPSD */
- if (more_data)
- cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA);
-
- if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) {
- mvmsta->next_status_eosp = true;
- cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL);
- } else {
- cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
- }
-
- 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);
-}
-
-void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mvm_eosp_notification *notif = (void *)pkt->data;
- struct ieee80211_sta *sta;
- u32 sta_id = le32_to_cpu(notif->sta_id);
-
- if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT))
- return;
-
- rcu_read_lock();
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
- if (!IS_ERR_OR_NULL(sta))
- ieee80211_sta_eosp(sta);
- rcu_read_unlock();
-}
-
-void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
- struct iwl_mvm_sta *mvmsta, bool disable)
-{
- struct iwl_mvm_add_sta_cmd cmd = {
- .add_modify = STA_MODE_MODIFY,
- .sta_id = mvmsta->sta_id,
- .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0,
- .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX),
- .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
- };
- int ret;
-
- 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);
-}
-
-void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- bool disable)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
-
- spin_lock_bh(&mvm_sta->lock);
-
- if (mvm_sta->disable_tx == disable) {
- spin_unlock_bh(&mvm_sta->lock);
- return;
- }
-
- mvm_sta->disable_tx = disable;
-
- /*
- * Tell mac80211 to start/stop queuing tx for this station,
- * but don't stop queuing if there are still pending frames
- * for this station.
- */
- if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
- ieee80211_sta_block_awake(mvm->hw, sta, disable);
-
- iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable);
-
- spin_unlock_bh(&mvm_sta->lock);
-}
-
-void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
- struct iwl_mvm_vif *mvmvif,
- bool disable)
-{
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvm_sta;
- int i;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* Block/unblock all the stations of the given mvmvif */
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(sta))
- continue;
-
- mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- if (mvm_sta->mac_id_n_color !=
- FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
- continue;
-
- iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
- }
-}
-
-void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_sta *mvmsta;
-
- rcu_read_lock();
-
- mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
-
- if (!WARN_ON(!mvmsta))
- iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
-
- rcu_read_unlock();
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
deleted file mode 100644
index 0631cc0a6d3c..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ /dev/null
@@ -1,426 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __sta_h__
-#define __sta_h__
-
-#include <linux/spinlock.h>
-#include <net/mac80211.h>
-#include <linux/wait.h>
-
-#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */
-#include "fw-api.h" /* IWL_MVM_STATION_COUNT */
-#include "rs.h"
-
-struct iwl_mvm;
-struct iwl_mvm_vif;
-
-/**
- * DOC: station table - introduction
- *
- * The station table is a list of data structure that reprensent the stations.
- * In STA/P2P client mode, the driver will hold one station for the AP/ GO.
- * In GO/AP mode, the driver will have as many stations as associated clients.
- * All these stations are reflected in the fw's station table. The driver
- * keeps the fw's station table up to date with the ADD_STA command. Stations
- * can be removed by the REMOVE_STA command.
- *
- * All the data related to a station is held in the structure %iwl_mvm_sta
- * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area.
- * This data includes the index of the station in the fw, per tid information
- * (sequence numbers, Block-ack state machine, etc...). The stations are
- * created and deleted by the %sta_state callback from %ieee80211_ops.
- *
- * The driver holds a map: %fw_id_to_mac_id that allows to fetch a
- * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw
- * station index. That way, the driver is able to get the tid related data in
- * O(1) in time sensitive paths (Tx / Tx response / BA notification). These
- * paths are triggered by the fw, and the driver needs to get a pointer to the
- * %ieee80211 structure. This map helps to get that pointer quickly.
- */
-
-/**
- * DOC: station table - locking
- *
- * As stated before, the station is created / deleted by mac80211's %sta_state
- * callback from %ieee80211_ops which can sleep. The next paragraph explains
- * the locking of a single stations, the next ones relates to the station
- * table.
- *
- * The station holds the sequence number per tid. So this data needs to be
- * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack
- * information (the state machine / and the logic that checks if the queues
- * were drained), so it also needs to be accessible from the Tx response flow.
- * In short, the station needs to be access from sleepable context as well as
- * from tasklets, so the station itself needs a spinlock.
- *
- * The writers of %fw_id_to_mac_id map are serialized by the global mutex of
- * the mvm op_mode. This is possible since %sta_state can sleep.
- * The pointers in this map are RCU protected, hence we won't replace the
- * station while we have Tx / Tx response / BA notification running.
- *
- * If a station is deleted while it still has packets in its A-MPDU queues,
- * then the reclaim flow will notice that there is no station in the map for
- * sta_id and it will dump the responses.
- */
-
-/**
- * DOC: station table - internal stations
- *
- * The FW needs a few internal stations that are not reflected in
- * mac80211, such as broadcast station in AP / GO mode, or AUX sta for
- * scanning and P2P device (during the GO negotiation).
- * For these kind of stations we have %iwl_mvm_int_sta struct which holds the
- * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta.
- * Usually the data for these stations is static, so no locking is required,
- * and no TID data as this is also not needed.
- * One thing to note, is that these stations have an ID in the fw, but not
- * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id
- * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of
- * pointers from this mapping need to check that the value is not error
- * or NULL.
- *
- * Currently there is only one auxiliary station for scanning, initialized
- * on init.
- */
-
-/**
- * DOC: station table - AP Station in STA mode
- *
- * %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
- * %ap_sta_id. To get the point to the corresponding %ieee80211_sta,
- * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
- * the AP station from the fw before setting the MAC context as unassociated.
- * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
- * removed by mac80211, but the station won't be removed in the fw until the
- * VIF is set as unassociated. Then, %ap_sta_id will be invalidated.
- */
-
-/**
- * DOC: station table - Drain vs. Flush
- *
- * Flush means that all the frames in the SCD queue are dumped regardless the
- * station to which they were sent. We do that when we disassociate and before
- * we remove the STA of the AP. The flush can be done synchronously against the
- * fw.
- * Drain means that the fw will drop all the frames sent to a specific station.
- * This is useful when a client (if we are IBSS / GO or AP) disassociates. In
- * that case, we need to drain all the frames for that client from the AC queues
- * that are shared with the other clients. Only then, we can remove the STA in
- * the fw. In order to do so, we track the non-AMPDU packets for each station.
- * If mac80211 removes a STA and if it still has non-AMPDU packets pending in
- * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all
- * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped
- * (we know about it with its Tx response), we remove the station in fw and set
- * it as %NULL in %fw_id_to_mac_id: this is the purpose of
- * %iwl_mvm_sta_drained_wk.
- */
-
-/**
- * DOC: station table - fw restart
- *
- * When the fw asserts, or we have any other issue that requires to reset the
- * driver, we require mac80211 to reconfigure the driver. Since the private
- * data of the stations is embed in mac80211's %ieee80211_sta, that data will
- * not be zeroed and needs to be reinitialized manually.
- * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us
- * that we must not allocate a new sta_id but reuse the previous one. This
- * means that the stations being re-added after the reset will have the same
- * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id
- * map, since the stations aren't in the fw any more. Internal stations that
- * are not added by mac80211 will be re-added in the init flow that is called
- * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to
- * %iwl_mvm_up.
- */
-
-/**
- * DOC: AP mode - PS
- *
- * When a station is asleep, the fw will set it as "asleep". All frames on
- * shared queues (i.e. non-aggregation queues) to that station will be dropped
- * by the fw (%TX_STATUS_FAIL_DEST_PS failure code).
- *
- * AMPDUs are in a separate queue that is stopped by the fw. We just need to
- * let mac80211 know when there are frames in these queues so that it can
- * properly handle trigger frames.
- *
- * When a trigger frame is received, mac80211 tells the driver to send frames
- * from the AMPDU queues or sends frames to non-aggregation queues itself,
- * depending on which ACs are delivery-enabled and what TID has frames to
- * transmit. Note that mac80211 has all the knowledge since all the non-agg
- * frames are buffered / filtered, and the driver tells mac80211 about agg
- * frames). The driver needs to tell the fw to let frames out even if the
- * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
- *
- * When we receive a frame from that station with PM bit unset, the driver
- * needs to let the fw know that this station isn't asleep any more. This is
- * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the
- * station's wakeup.
- *
- * For a GO, the Service Period might be cut short due to an absence period
- * of the GO. In this (and all other cases) the firmware notifies us with the
- * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we
- * already sent to the device will be rejected again.
- *
- * See also "AP support for powersaving clients" in mac80211.h.
- */
-
-/**
- * enum iwl_mvm_agg_state
- *
- * The state machine of the BA agreement establishment / tear down.
- * These states relate to a specific RA / TID.
- *
- * @IWL_AGG_OFF: aggregation is not used
- * @IWL_AGG_STARTING: aggregation are starting (between start and oper)
- * @IWL_AGG_ON: aggregation session is up
- * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
- * HW queue to be empty from packets for this RA /TID.
- * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
- * HW queue to be empty from packets for this RA /TID.
- */
-enum iwl_mvm_agg_state {
- IWL_AGG_OFF = 0,
- IWL_AGG_STARTING,
- IWL_AGG_ON,
- IWL_EMPTYING_HW_QUEUE_ADDBA,
- IWL_EMPTYING_HW_QUEUE_DELBA,
-};
-
-/**
- * struct iwl_mvm_tid_data - holds the states for each RA / TID
- * @seq_number: the next WiFi sequence number to use
- * @next_reclaimed: the WiFi sequence number of the next packet to be acked.
- * This is basically (last acked packet++).
- * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
- * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
- * @reduced_tpc: Reduced tx power. Holds the data between the
- * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
- * @state: state of the BA agreement establishment / tear down.
- * @txq_id: Tx queue used by the BA session
- * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
- * 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.
- * @tx_time: medium time consumed by this A-MPDU
- */
-struct iwl_mvm_tid_data {
- u16 seq_number;
- u16 next_reclaimed;
- /* The rest is Tx AGG related */
- u32 rate_n_flags;
- u8 reduced_tpc;
- enum iwl_mvm_agg_state state;
- u16 txq_id;
- u16 ssn;
- u16 tx_time;
-};
-
-static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
-{
- return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number),
- tid_data->next_reclaimed);
-}
-
-/**
- * struct iwl_mvm_sta - representation of a station in the driver
- * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
- * @tfd_queue_msk: the tfd queues used by the station
- * @hw_queue: per-AC mapping of the TFD queues used by station
- * @mac_id_n_color: the MAC context this station is linked to
- * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
- * tid.
- * @max_agg_bufsize: the maximal size of the AGG buffer for this station
- * @bt_reduced_txpower: is reduced tx power enabled for this station
- * @next_status_eosp: the next reclaimed packet is a PS-Poll response and
- * we need to signal the EOSP
- * @lock: lock to protect the whole struct. Since %tid_data is access from Tx
- * and from Tx response flow, it needs a spinlock.
- * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
- * @tx_protection: reference counter for controlling the Tx protection.
- * @tt_tx_protection: is thermal throttling enable Tx protection?
- * @disable_tx: is tx to this STA disabled?
- * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON)
- *
- * When mac80211 creates a station it reserves some space (hw->sta_data_size)
- * in the structure for use by driver. This structure is placed in that
- * space.
- *
- */
-struct iwl_mvm_sta {
- u32 sta_id;
- u32 tfd_queue_msk;
- u8 hw_queue[IEEE80211_NUM_ACS];
- u32 mac_id_n_color;
- u16 tid_disable_agg;
- u8 max_agg_bufsize;
- bool bt_reduced_txpower;
- bool next_status_eosp;
- spinlock_t lock;
- struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
- struct iwl_lq_sta lq_sta;
- struct ieee80211_vif *vif;
-
- /* Temporary, until the new TLC will control the Tx protection */
- s8 tx_protection;
- bool tt_tx_protection;
-
- bool disable_tx;
- u8 agg_tids;
-};
-
-static inline struct iwl_mvm_sta *
-iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta)
-{
- return (void *)sta->drv_priv;
-}
-
-/**
- * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or
- * broadcast)
- * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
- * @tfd_queue_msk: the tfd queues used by the station
- */
-struct iwl_mvm_int_sta {
- u32 sta_id;
- u32 tfd_queue_msk;
-};
-
-int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- bool update);
-int iwl_mvm_add_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-int iwl_mvm_update_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u8 sta_id);
-int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *keyconf,
- u8 key_offset);
-int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *keyconf);
-
-void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta, u32 iv32,
- u16 *phase1key);
-
-void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-
-/* AMPDU */
-int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- int tid, u16 ssn, bool start);
-int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn);
-int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u8 buf_size);
-int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid);
-int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid);
-
-int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
-void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm);
-
-int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-
-void iwl_mvm_sta_drained_wk(struct work_struct *wk);
-void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta);
-void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- enum ieee80211_frame_release_type reason,
- u16 cnt, u16 tids, bool more_data,
- bool agg);
-int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
- bool drain);
-void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
- struct iwl_mvm_sta *mvmsta, bool disable);
-void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- bool disable);
-void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
- struct iwl_mvm_vif *mvmvif,
- bool disable);
-void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-
-#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c
deleted file mode 100644
index fe2fa5650443..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/tdls.c
+++ /dev/null
@@ -1,732 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/etherdevice.h>
-#include "mvm.h"
-#include "time-event.h"
-#include "iwl-io.h"
-#include "iwl-prph.h"
-
-#define TU_TO_US(x) (x * 1024)
-#define TU_TO_MS(x) (TU_TO_US(x) / 1000)
-
-void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
-{
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvmsta;
- int i;
-
- lockdep_assert_held(&mvm->mutex);
-
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
- lockdep_is_held(&mvm->mutex));
- if (!sta || IS_ERR(sta) || !sta->tdls)
- continue;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
- NL80211_TDLS_TEARDOWN,
- WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
- GFP_KERNEL);
- }
-}
-
-int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvmsta;
- int count = 0;
- int i;
-
- lockdep_assert_held(&mvm->mutex);
-
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
- lockdep_is_held(&mvm->mutex));
- if (!sta || IS_ERR(sta) || !sta->tdls)
- continue;
-
- if (vif) {
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- if (mvmsta->vif != vif)
- continue;
- }
-
- count++;
- }
-
- return count;
-}
-
-static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
- struct iwl_rx_packet *pkt;
- struct iwl_tdls_config_res *resp;
- struct iwl_tdls_config_cmd tdls_cfg_cmd = {};
- struct iwl_host_cmd cmd = {
- .id = TDLS_CONFIG_CMD,
- .flags = CMD_WANT_SKB,
- .data = { &tdls_cfg_cmd, },
- .len = { sizeof(struct iwl_tdls_config_cmd), },
- };
- struct ieee80211_sta *sta;
- int ret, i, cnt;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- lockdep_assert_held(&mvm->mutex);
-
- tdls_cfg_cmd.id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
- tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID;
- tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */
-
- /* for now the Tx cmd is empty and unused */
-
- /* populate TDLS peer data */
- cnt = 0;
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(sta) || !sta->tdls)
- continue;
-
- tdls_cfg_cmd.sta_info[cnt].sta_id = i;
- tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid =
- IWL_MVM_TDLS_FW_TID;
- tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0);
- tdls_cfg_cmd.sta_info[cnt].is_initiator =
- cpu_to_le32(sta->tdls_initiator ? 1 : 0);
-
- cnt++;
- }
-
- tdls_cfg_cmd.tdls_peer_count = cnt;
- IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt);
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (WARN_ON_ONCE(ret))
- return;
-
- pkt = cmd.resp_pkt;
-
- WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp));
-
- /* we don't really care about the response at this point */
-
- iwl_free_resp(&cmd);
-}
-
-void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool sta_added)
-{
- int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
-
- /* when the first peer joins, send a power update first */
- if (tdls_sta_cnt == 1 && sta_added)
- iwl_mvm_power_update_mac(mvm);
-
- /* configure the FW with TDLS peer info */
- iwl_mvm_tdls_config(mvm, vif);
-
- /* when the last peer leaves, send a power update last */
- if (tdls_sta_cnt == 0 && !sta_added)
- iwl_mvm_power_update_mac(mvm);
-}
-
-void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
-
- /*
- * iwl_mvm_protect_session() reads directly from the device
- * (the system time), so make sure it is available.
- */
- if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS))
- return;
-
- mutex_lock(&mvm->mutex);
- /* Protect the session to hear the TDLS setup response on the channel */
- iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true);
- mutex_unlock(&mvm->mutex);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
-}
-
-static const char *
-iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state)
-{
- switch (state) {
- case IWL_MVM_TDLS_SW_IDLE:
- return "IDLE";
- case IWL_MVM_TDLS_SW_REQ_SENT:
- return "REQ SENT";
- case IWL_MVM_TDLS_SW_RESP_RCVD:
- return "RESP RECEIVED";
- case IWL_MVM_TDLS_SW_REQ_RCVD:
- return "REQ RECEIVED";
- case IWL_MVM_TDLS_SW_ACTIVE:
- return "ACTIVE";
- }
-
- return NULL;
-}
-
-static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm,
- enum iwl_mvm_tdls_cs_state state)
-{
- if (mvm->tdls_cs.state == state)
- return;
-
- IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n",
- iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state),
- iwl_mvm_tdls_cs_state_str(state));
- mvm->tdls_cs.state = state;
-
- /* we only send requests to our switching peer - update sent time */
- if (state == IWL_MVM_TDLS_SW_REQ_SENT)
- mvm->tdls_cs.peer.sent_timestamp =
- iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
-
- if (state == IWL_MVM_TDLS_SW_IDLE)
- mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT;
-}
-
-void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data;
- struct ieee80211_sta *sta;
- unsigned int delay;
- struct iwl_mvm_sta *mvmsta;
- struct ieee80211_vif *vif;
- u32 sta_id = le32_to_cpu(notif->sta_id);
-
- lockdep_assert_held(&mvm->mutex);
-
- /* can fail sometimes */
- if (!le32_to_cpu(notif->status)) {
- iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
- return;
- }
-
- if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT))
- return;
-
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
- /* the station may not be here, but if it is, it must be a TDLS peer */
- if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls))
- return;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- vif = mvmsta->vif;
-
- /*
- * Update state and possibly switch again after this is over (DTIM).
- * Also convert TU to msec.
- */
- delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
- msecs_to_jiffies(delay));
-
- iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
-}
-
-static int
-iwl_mvm_tdls_check_action(struct iwl_mvm *mvm,
- enum iwl_tdls_channel_switch_type type,
- const u8 *peer, bool peer_initiator, u32 timestamp)
-{
- bool same_peer = false;
- int ret = 0;
-
- /* get the existing peer if it's there */
- if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE &&
- mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
- struct ieee80211_sta *sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
- lockdep_is_held(&mvm->mutex));
- if (!IS_ERR_OR_NULL(sta))
- same_peer = ether_addr_equal(peer, sta->addr);
- }
-
- switch (mvm->tdls_cs.state) {
- case IWL_MVM_TDLS_SW_IDLE:
- /*
- * might be spurious packet from the peer after the switch is
- * already done
- */
- if (type == TDLS_MOVE_CH)
- ret = -EINVAL;
- break;
- case IWL_MVM_TDLS_SW_REQ_SENT:
- /* only allow requests from the same peer */
- if (!same_peer)
- ret = -EBUSY;
- else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH &&
- !peer_initiator)
- /*
- * We received a ch-switch request while an outgoing
- * one is pending. Allow it if the peer is the link
- * initiator.
- */
- ret = -EBUSY;
- else if (type == TDLS_SEND_CHAN_SW_REQ)
- /* wait for idle before sending another request */
- ret = -EBUSY;
- else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp)
- /* we got a stale response - ignore it */
- ret = -EINVAL;
- break;
- case IWL_MVM_TDLS_SW_RESP_RCVD:
- /*
- * we are waiting for the FW to give an "active" notification,
- * so ignore requests in the meantime
- */
- ret = -EBUSY;
- break;
- case IWL_MVM_TDLS_SW_REQ_RCVD:
- /* as above, allow the link initiator to proceed */
- if (type == TDLS_SEND_CHAN_SW_REQ) {
- if (!same_peer)
- ret = -EBUSY;
- else if (peer_initiator) /* they are the initiator */
- ret = -EBUSY;
- } else if (type == TDLS_MOVE_CH) {
- ret = -EINVAL;
- }
- break;
- case IWL_MVM_TDLS_SW_ACTIVE:
- /*
- * the only valid request when active is a request to return
- * to the base channel by the current off-channel peer
- */
- if (type != TDLS_MOVE_CH || !same_peer)
- ret = -EBUSY;
- break;
- }
-
- if (ret)
- IWL_DEBUG_TDLS(mvm,
- "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n",
- type, mvm->tdls_cs.state, peer, same_peer,
- peer_initiator);
-
- return ret;
-}
-
-static int
-iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- enum iwl_tdls_channel_switch_type type,
- const u8 *peer, bool peer_initiator,
- u8 oper_class,
- struct cfg80211_chan_def *chandef,
- u32 timestamp, u16 switch_time,
- u16 switch_timeout, struct sk_buff *skb,
- u32 ch_sw_tm_ie)
-{
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvmsta;
- struct ieee80211_tx_info *info;
- struct ieee80211_hdr *hdr;
- struct iwl_tdls_channel_switch_cmd cmd = {0};
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator,
- timestamp);
- if (ret)
- return ret;
-
- if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) {
- ret = -EINVAL;
- goto out;
- }
-
- cmd.switch_type = type;
- cmd.timing.frame_timestamp = cpu_to_le32(timestamp);
- cmd.timing.switch_time = cpu_to_le32(switch_time);
- cmd.timing.switch_timeout = cpu_to_le32(switch_timeout);
-
- rcu_read_lock();
- sta = ieee80211_find_sta(vif, peer);
- if (!sta) {
- rcu_read_unlock();
- ret = -ENOENT;
- goto out;
- }
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id);
-
- if (!chandef) {
- if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
- mvm->tdls_cs.peer.chandef.chan) {
- /* actually moving to the channel */
- chandef = &mvm->tdls_cs.peer.chandef;
- } else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE &&
- type == TDLS_MOVE_CH) {
- /* we need to return to base channel */
- struct ieee80211_chanctx_conf *chanctx =
- rcu_dereference(vif->chanctx_conf);
-
- if (WARN_ON_ONCE(!chanctx)) {
- rcu_read_unlock();
- goto out;
- }
-
- chandef = &chanctx->def;
- }
- }
-
- if (chandef) {
- cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
- PHY_BAND_24 : PHY_BAND_5);
- cmd.ci.channel = chandef->chan->hw_value;
- cmd.ci.width = iwl_mvm_get_channel_width(chandef);
- cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
- }
-
- /* keep quota calculation simple for now - 50% of DTIM for TDLS */
- cmd.timing.max_offchan_duration =
- cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
- vif->bss_conf.beacon_int) / 2);
-
- /* Switch time is the first element in the switch-timing IE. */
- cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
-
- info = IEEE80211_SKB_CB(skb);
- hdr = (void *)skb->data;
- if (info->control.hw_key) {
- if (info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP) {
- rcu_read_unlock();
- ret = -EINVAL;
- goto out;
- }
- iwl_mvm_set_tx_cmd_ccmp(info, &cmd.frame.tx_cmd);
- }
-
- iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info,
- mvmsta->sta_id);
-
- iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta,
- hdr->frame_control);
- rcu_read_unlock();
-
- memcpy(cmd.frame.data, skb->data, skb->len);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0,
- sizeof(cmd), &cmd);
- if (ret) {
- IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n",
- ret);
- goto out;
- }
-
- /* channel switch has started, update state */
- if (type != TDLS_MOVE_CH) {
- mvm->tdls_cs.cur_sta_id = mvmsta->sta_id;
- iwl_mvm_tdls_update_cs_state(mvm,
- type == TDLS_SEND_CHAN_SW_REQ ?
- IWL_MVM_TDLS_SW_REQ_SENT :
- IWL_MVM_TDLS_SW_REQ_RCVD);
- } else {
- iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD);
- }
-
-out:
-
- /* channel switch failed - we are idle */
- if (ret)
- iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
-
- return ret;
-}
-
-void iwl_mvm_tdls_ch_switch_work(struct work_struct *work)
-{
- struct iwl_mvm *mvm;
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvmsta;
- struct ieee80211_vif *vif;
- unsigned int delay;
- int ret;
-
- mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work);
- mutex_lock(&mvm->mutex);
-
- /* called after an active channel switch has finished or timed-out */
- iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
-
- /* station might be gone, in that case do nothing */
- if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT)
- goto out;
-
- sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
- lockdep_is_held(&mvm->mutex));
- /* the station may not be here, but if it is, it must be a TDLS peer */
- if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls))
- goto out;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- vif = mvmsta->vif;
- ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
- TDLS_SEND_CHAN_SW_REQ,
- sta->addr,
- mvm->tdls_cs.peer.initiator,
- mvm->tdls_cs.peer.op_class,
- &mvm->tdls_cs.peer.chandef,
- 0, 0, 0,
- mvm->tdls_cs.peer.skb,
- mvm->tdls_cs.peer.ch_sw_tm_ie);
- if (ret)
- IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret);
-
- /* retry after a DTIM if we failed sending now */
- delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
- queue_delayed_work(system_wq, &mvm->tdls_cs.dwork,
- msecs_to_jiffies(delay));
-out:
- mutex_unlock(&mvm->mutex);
-}
-
-int
-iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u8 oper_class,
- struct cfg80211_chan_def *chandef,
- struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_sta *mvmsta;
- unsigned int delay;
- int ret;
-
- mutex_lock(&mvm->mutex);
-
- IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n",
- sta->addr, chandef->chan->center_freq, chandef->width);
-
- /* we only support a single peer for channel switching */
- if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) {
- IWL_DEBUG_TDLS(mvm,
- "Existing peer. Can't start switch with %pM\n",
- sta->addr);
- ret = -EBUSY;
- goto out;
- }
-
- ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
- TDLS_SEND_CHAN_SW_REQ,
- sta->addr, sta->tdls_initiator,
- oper_class, chandef, 0, 0, 0,
- tmpl_skb, ch_sw_tm_ie);
- if (ret)
- goto out;
-
- /*
- * Mark the peer as "in tdls switch" for this vif. We only allow a
- * single such peer per vif.
- */
- mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL);
- if (!mvm->tdls_cs.peer.skb) {
- ret = -ENOMEM;
- goto out;
- }
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- mvm->tdls_cs.peer.sta_id = mvmsta->sta_id;
- mvm->tdls_cs.peer.chandef = *chandef;
- mvm->tdls_cs.peer.initiator = sta->tdls_initiator;
- mvm->tdls_cs.peer.op_class = oper_class;
- mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie;
-
- /*
- * Wait for 2 DTIM periods before attempting the next switch. The next
- * switch will be made sooner if the current one completes before that.
- */
- delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
- vif->bss_conf.beacon_int);
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
- msecs_to_jiffies(delay));
-
-out:
- mutex_unlock(&mvm->mutex);
- return ret;
-}
-
-void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct ieee80211_sta *cur_sta;
- bool wait_for_phy = false;
-
- mutex_lock(&mvm->mutex);
-
- IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr);
-
- /* we only support a single peer for channel switching */
- if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) {
- IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr);
- goto out;
- }
-
- cur_sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
- lockdep_is_held(&mvm->mutex));
- /* make sure it's the same peer */
- if (cur_sta != sta)
- goto out;
-
- /*
- * If we're currently in a switch because of the now canceled peer,
- * wait a DTIM here to make sure the phy is back on the base channel.
- * We can't otherwise force it.
- */
- if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id &&
- mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE)
- wait_for_phy = true;
-
- mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
- dev_kfree_skb(mvm->tdls_cs.peer.skb);
- mvm->tdls_cs.peer.skb = NULL;
-
-out:
- mutex_unlock(&mvm->mutex);
-
- /* make sure the phy is on the base channel */
- if (wait_for_phy)
- msleep(TU_TO_MS(vif->bss_conf.dtim_period *
- vif->bss_conf.beacon_int));
-
- /* flush the channel switch state */
- flush_delayed_work(&mvm->tdls_cs.dwork);
-
- IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr);
-}
-
-void
-iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_tdls_ch_sw_params *params)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- enum iwl_tdls_channel_switch_type type;
- unsigned int delay;
- const char *action_str =
- params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ?
- "REQ" : "RESP";
-
- mutex_lock(&mvm->mutex);
-
- IWL_DEBUG_TDLS(mvm,
- "Received TDLS ch switch action %s from %pM status %d\n",
- action_str, params->sta->addr, params->status);
-
- /*
- * we got a non-zero status from a peer we were switching to - move to
- * the idle state and retry again later
- */
- if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE &&
- params->status != 0 &&
- mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
- mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
- struct ieee80211_sta *cur_sta;
-
- /* make sure it's the same peer */
- cur_sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
- lockdep_is_held(&mvm->mutex));
- if (cur_sta == params->sta) {
- iwl_mvm_tdls_update_cs_state(mvm,
- IWL_MVM_TDLS_SW_IDLE);
- goto retry;
- }
- }
-
- type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ?
- TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH;
-
- iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr,
- params->sta->tdls_initiator, 0,
- params->chandef, params->timestamp,
- params->switch_time,
- params->switch_timeout,
- params->tmpl_skb,
- params->ch_sw_tm_ie);
-
-retry:
- /* register a timeout in case we don't succeed in switching */
- delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
- 1024 / 1000;
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
- msecs_to_jiffies(delay));
- mutex_unlock(&mvm->mutex);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h
deleted file mode 100644
index 79ab6beb6b26..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/testmode.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __IWL_MVM_TESTMODE_H__
-#define __IWL_MVM_TESTMODE_H__
-
-/**
- * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA
- * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute)
- * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32)
- * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32)
- * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32)
- */
-enum iwl_mvm_testmode_attrs {
- IWL_MVM_TM_ATTR_UNSPEC,
- IWL_MVM_TM_ATTR_CMD,
- IWL_MVM_TM_ATTR_NOA_DURATION,
- IWL_MVM_TM_ATTR_BEACON_FILTER_STATE,
-
- /* keep last */
- NUM_IWL_MVM_TM_ATTRS,
- IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1,
-};
-
-/**
- * enum iwl_mvm_testmode_commands - MVM testmode commands
- * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing
- * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on
- */
-enum iwl_mvm_testmode_commands {
- IWL_MVM_TM_CMD_SET_NOA,
- IWL_MVM_TM_CMD_SET_BEACON_FILTER,
-};
-
-#endif /* __IWL_MVM_TESTMODE_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
deleted file mode 100644
index 7530eb23035d..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ /dev/null
@@ -1,872 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/jiffies.h>
-#include <net/mac80211.h>
-
-#include "iwl-notif-wait.h"
-#include "iwl-trans.h"
-#include "fw-api.h"
-#include "time-event.h"
-#include "mvm.h"
-#include "iwl-io.h"
-#include "iwl-prph.h"
-
-/*
- * For the high priority TE use a time event type that has similar priority to
- * the FW's action scan priority.
- */
-#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
-#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
-
-void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
- struct iwl_mvm_time_event_data *te_data)
-{
- lockdep_assert_held(&mvm->time_event_lock);
-
- if (!te_data->vif)
- return;
-
- list_del(&te_data->list);
- te_data->running = false;
- te_data->uid = 0;
- te_data->id = TE_MAX;
- te_data->vif = NULL;
-}
-
-void iwl_mvm_roc_done_wk(struct work_struct *wk)
-{
- struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
- u32 queues = 0;
-
- /*
- * Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit.
- * This will cause the TX path to drop offchannel transmissions.
- * That would also be done by mac80211, but it is racy, in particular
- * in the case that the time event actually completed in the firmware
- * (which is handled in iwl_mvm_te_handle_notif).
- */
- if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) {
- queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE);
- iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
- }
- if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) {
- queues |= BIT(mvm->aux_queue);
- iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX);
- }
-
- synchronize_net();
-
- /*
- * Flush the offchannel queue -- this is called when the time
- * event finishes or is canceled, so that frames queued for it
- * won't get stuck on the queue and be transmitted in the next
- * time event.
- * We have to send the command asynchronously since this cannot
- * be under the mutex for locking reasons, but that's not an
- * issue as it will have to complete before the next command is
- * executed, and a new time event means a new command.
- */
- iwl_mvm_flush_tx_path(mvm, queues, CMD_ASYNC);
-}
-
-static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
-{
- /*
- * Of course, our status bit is just as racy as mac80211, so in
- * addition, fire off the work struct which will drop all frames
- * from the hardware queues that made it through the race. First
- * it will of course synchronize the TX path to make sure that
- * any *new* TX will be rejected.
- */
- schedule_work(&mvm->roc_done_wk);
-}
-
-static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
-{
- struct ieee80211_vif *csa_vif;
-
- rcu_read_lock();
-
- csa_vif = rcu_dereference(mvm->csa_vif);
- if (!csa_vif || !csa_vif->csa_active)
- goto out_unlock;
-
- IWL_DEBUG_TE(mvm, "CSA NOA started\n");
-
- /*
- * CSA NoA is started but we still have beacons to
- * transmit on the current channel.
- * So we just do nothing here and the switch
- * will be performed on the last TBTT.
- */
- if (!ieee80211_csa_is_complete(csa_vif)) {
- IWL_WARN(mvm, "CSA NOA started too early\n");
- goto out_unlock;
- }
-
- ieee80211_csa_finish(csa_vif);
-
- rcu_read_unlock();
-
- RCU_INIT_POINTER(mvm->csa_vif, NULL);
-
- return;
-
-out_unlock:
- rcu_read_unlock();
-}
-
-static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- const char *errmsg)
-{
- if (vif->type != NL80211_IFTYPE_STATION)
- return false;
- if (vif->bss_conf.assoc && vif->bss_conf.dtim_period)
- return false;
- if (errmsg)
- IWL_ERR(mvm, "%s\n", errmsg);
-
- iwl_mvm_connection_loss(mvm, vif, errmsg);
- return true;
-}
-
-static void
-iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
- struct iwl_mvm_time_event_data *te_data,
- struct iwl_time_event_notif *notif)
-{
- struct ieee80211_vif *vif = te_data->vif;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (!notif->status)
- IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
-
- switch (te_data->vif->type) {
- case NL80211_IFTYPE_AP:
- if (!notif->status)
- mvmvif->csa_failed = true;
- iwl_mvm_csa_noa_start(mvm);
- break;
- case NL80211_IFTYPE_STATION:
- if (!notif->status) {
- iwl_mvm_connection_loss(mvm, vif,
- "CSA TE failed to start");
- break;
- }
- iwl_mvm_csa_client_absent(mvm, te_data->vif);
- ieee80211_chswitch_done(te_data->vif, true);
- break;
- default:
- /* should never happen */
- WARN_ON_ONCE(1);
- break;
- }
-
- /* we don't need it anymore */
- iwl_mvm_te_clear_data(mvm, te_data);
-}
-
-static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
- struct iwl_time_event_notif *notif,
- struct iwl_mvm_time_event_data *te_data)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_time_event *te_trig;
- int i;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT);
- te_trig = (void *)trig->data;
-
- if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig))
- return;
-
- for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
- u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
- u32 trig_action_bitmap =
- le32_to_cpu(te_trig->time_events[i].action_bitmap);
- u32 trig_status_bitmap =
- le32_to_cpu(te_trig->time_events[i].status_bitmap);
-
- if (trig_te_id != te_data->id ||
- !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
- !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
- continue;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trig,
- "Time event %d Action 0x%x received status: %d",
- te_data->id,
- le32_to_cpu(notif->action),
- le32_to_cpu(notif->status));
- break;
- }
-}
-
-/*
- * Handles a FW notification for an event that is known to the driver.
- *
- * @mvm: the mvm component
- * @te_data: the time event data
- * @notif: the notification data corresponding the time event data.
- */
-static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
- struct iwl_mvm_time_event_data *te_data,
- struct iwl_time_event_notif *notif)
-{
- lockdep_assert_held(&mvm->time_event_lock);
-
- IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
- le32_to_cpu(notif->unique_id),
- le32_to_cpu(notif->action));
-
- iwl_mvm_te_check_trigger(mvm, notif, te_data);
-
- /*
- * The FW sends the start/end time event notifications even for events
- * that it fails to schedule. This is indicated in the status field of
- * the notification. This happens in cases that the scheduler cannot
- * find a schedule that can handle the event (for example requesting a
- * P2P Device discoveribility, while there are other higher priority
- * events in the system).
- */
- if (!le32_to_cpu(notif->status)) {
- const char *msg;
-
- if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
- msg = "Time Event start notification failure";
- else
- msg = "Time Event end notification failure";
-
- IWL_DEBUG_TE(mvm, "%s\n", msg);
-
- if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
- iwl_mvm_te_clear_data(mvm, te_data);
- return;
- }
- }
-
- if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
- IWL_DEBUG_TE(mvm,
- "TE ended - current time %lu, estimated end %lu\n",
- jiffies, te_data->end_jiffies);
-
- switch (te_data->vif->type) {
- case NL80211_IFTYPE_P2P_DEVICE:
- ieee80211_remain_on_channel_expired(mvm->hw);
- iwl_mvm_roc_finished(mvm);
- break;
- case NL80211_IFTYPE_STATION:
- /*
- * By now, we should have finished association
- * and know the dtim period.
- */
- iwl_mvm_te_check_disconnect(mvm, te_data->vif,
- "No association and the time event is over already...");
- break;
- default:
- break;
- }
-
- iwl_mvm_te_clear_data(mvm, te_data);
- } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
- te_data->running = true;
- te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
-
- if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
- iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
- ieee80211_ready_on_channel(mvm->hw);
- } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
- iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
- }
- } else {
- IWL_WARN(mvm, "Got TE with unknown action\n");
- }
-}
-
-/*
- * Handle A Aux ROC time event
- */
-static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
- struct iwl_time_event_notif *notif)
-{
- struct iwl_mvm_time_event_data *te_data, *tmp;
- bool aux_roc_te = false;
-
- list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) {
- if (le32_to_cpu(notif->unique_id) == te_data->uid) {
- aux_roc_te = true;
- break;
- }
- }
- if (!aux_roc_te) /* Not a Aux ROC time event */
- return -EINVAL;
-
- iwl_mvm_te_check_trigger(mvm, notif, te_data);
-
- if (!le32_to_cpu(notif->status)) {
- IWL_DEBUG_TE(mvm,
- "ERROR: Aux ROC Time Event %s notification failure\n",
- (le32_to_cpu(notif->action) &
- TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end");
- return -EINVAL;
- }
-
- IWL_DEBUG_TE(mvm,
- "Aux ROC time event notification - UID = 0x%x action %d\n",
- le32_to_cpu(notif->unique_id),
- le32_to_cpu(notif->action));
-
- if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
- /* End TE, notify mac80211 */
- ieee80211_remain_on_channel_expired(mvm->hw);
- iwl_mvm_roc_finished(mvm); /* flush aux queue */
- list_del(&te_data->list); /* remove from list */
- te_data->running = false;
- te_data->vif = NULL;
- te_data->uid = 0;
- te_data->id = TE_MAX;
- } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
- set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
- te_data->running = true;
- iwl_mvm_ref(mvm, IWL_MVM_REF_ROC_AUX);
- ieee80211_ready_on_channel(mvm->hw); /* Start TE */
- } else {
- IWL_DEBUG_TE(mvm,
- "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
- le32_to_cpu(notif->action));
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * The Rx handler for time event notifications
- */
-void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_time_event_notif *notif = (void *)pkt->data;
- struct iwl_mvm_time_event_data *te_data, *tmp;
-
- IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
- le32_to_cpu(notif->unique_id),
- le32_to_cpu(notif->action));
-
- spin_lock_bh(&mvm->time_event_lock);
- /* This time event is triggered for Aux ROC request */
- if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
- goto unlock;
-
- list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
- if (le32_to_cpu(notif->unique_id) == te_data->uid)
- iwl_mvm_te_handle_notif(mvm, te_data, notif);
- }
-unlock:
- spin_unlock_bh(&mvm->time_event_lock);
-}
-
-static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_mvm *mvm =
- container_of(notif_wait, struct iwl_mvm, notif_wait);
- struct iwl_mvm_time_event_data *te_data = data;
- struct iwl_time_event_notif *resp;
- int resp_len = iwl_rx_packet_payload_len(pkt);
-
- if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION))
- return true;
-
- if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
- IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n");
- return true;
- }
-
- resp = (void *)pkt->data;
-
- /* te_data->uid is already set in the TIME_EVENT_CMD response */
- if (le32_to_cpu(resp->unique_id) != te_data->uid)
- return false;
-
- IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
- te_data->uid);
- if (!resp->status)
- IWL_ERR(mvm,
- "TIME_EVENT_NOTIFICATION received but not executed\n");
-
- return true;
-}
-
-static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_mvm *mvm =
- container_of(notif_wait, struct iwl_mvm, notif_wait);
- struct iwl_mvm_time_event_data *te_data = data;
- struct iwl_time_event_resp *resp;
- int resp_len = iwl_rx_packet_payload_len(pkt);
-
- if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
- return true;
-
- if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
- IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
- return true;
- }
-
- resp = (void *)pkt->data;
-
- /* we should never get a response to another TIME_EVENT_CMD here */
- if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
- return false;
-
- te_data->uid = le32_to_cpu(resp->unique_id);
- IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
- te_data->uid);
- return true;
-}
-
-static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mvm_time_event_data *te_data,
- struct iwl_time_event_cmd *te_cmd)
-{
- static const u16 time_event_response[] = { TIME_EVENT_CMD };
- struct iwl_notification_wait wait_time_event;
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
- le32_to_cpu(te_cmd->duration));
-
- spin_lock_bh(&mvm->time_event_lock);
- if (WARN_ON(te_data->id != TE_MAX)) {
- spin_unlock_bh(&mvm->time_event_lock);
- return -EIO;
- }
- te_data->vif = vif;
- te_data->duration = le32_to_cpu(te_cmd->duration);
- te_data->id = le32_to_cpu(te_cmd->id);
- list_add_tail(&te_data->list, &mvm->time_event_list);
- spin_unlock_bh(&mvm->time_event_lock);
-
- /*
- * Use a notification wait, which really just processes the
- * command response and doesn't wait for anything, in order
- * to be able to process the response and get the UID inside
- * the RX path. Using CMD_WANT_SKB doesn't work because it
- * stores the buffer and then wakes up this thread, by which
- * time another notification (that the time event started)
- * might already be processed unsuccessfully.
- */
- iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
- time_event_response,
- ARRAY_SIZE(time_event_response),
- iwl_mvm_time_event_response, te_data);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
- sizeof(*te_cmd), te_cmd);
- if (ret) {
- IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
- iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
- goto out_clear_te;
- }
-
- /* No need to wait for anything, so just pass 1 (0 isn't valid) */
- ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
- /* should never fail */
- WARN_ON_ONCE(ret);
-
- if (ret) {
- out_clear_te:
- spin_lock_bh(&mvm->time_event_lock);
- iwl_mvm_te_clear_data(mvm, te_data);
- spin_unlock_bh(&mvm->time_event_lock);
- }
- return ret;
-}
-
-void iwl_mvm_protect_session(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 duration, u32 min_duration,
- u32 max_delay, bool wait_for_notif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
- const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION };
- struct iwl_notification_wait wait_te_notif;
- struct iwl_time_event_cmd time_cmd = {};
-
- lockdep_assert_held(&mvm->mutex);
-
- if (te_data->running &&
- time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
- IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
- jiffies_to_msecs(te_data->end_jiffies - jiffies));
- return;
- }
-
- if (te_data->running) {
- IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
- te_data->uid,
- jiffies_to_msecs(te_data->end_jiffies - jiffies));
- /*
- * we don't have enough time
- * cancel the current TE and issue a new one
- * Of course it would be better to remove the old one only
- * when the new one is added, but we don't care if we are off
- * channel for a bit. All we need to do, is not to return
- * before we actually begin to be on the channel.
- */
- iwl_mvm_stop_session_protection(mvm, vif);
- }
-
- time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
- time_cmd.id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
- time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
-
- time_cmd.apply_time = cpu_to_le32(0);
-
- time_cmd.max_frags = TE_V2_FRAG_NONE;
- time_cmd.max_delay = cpu_to_le32(max_delay);
- /* TODO: why do we need to interval = bi if it is not periodic? */
- time_cmd.interval = cpu_to_le32(1);
- time_cmd.duration = cpu_to_le32(duration);
- time_cmd.repeat = 1;
- time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
- TE_V2_NOTIF_HOST_EVENT_END |
- T2_V2_START_IMMEDIATELY);
-
- if (!wait_for_notif) {
- iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
- return;
- }
-
- /*
- * Create notification_wait for the TIME_EVENT_NOTIFICATION to use
- * right after we send the time event
- */
- iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif,
- te_notif_response,
- ARRAY_SIZE(te_notif_response),
- iwl_mvm_te_notif, te_data);
-
- /* If TE was sent OK - wait for the notification that started */
- if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) {
- IWL_ERR(mvm, "Failed to add TE to protect session\n");
- iwl_remove_notification(&mvm->notif_wait, &wait_te_notif);
- } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif,
- TU_TO_JIFFIES(max_delay))) {
- IWL_ERR(mvm, "Failed to protect session until TE\n");
- }
-}
-
-static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
- struct iwl_mvm_time_event_data *te_data,
- u32 *uid)
-{
- u32 id;
-
- /*
- * It is possible that by the time we got to this point the time
- * event was already removed.
- */
- spin_lock_bh(&mvm->time_event_lock);
-
- /* Save time event uid before clearing its data */
- *uid = te_data->uid;
- id = te_data->id;
-
- /*
- * The clear_data function handles time events that were already removed
- */
- iwl_mvm_te_clear_data(mvm, te_data);
- spin_unlock_bh(&mvm->time_event_lock);
-
- /*
- * It is possible that by the time we try to remove it, the time event
- * has already ended and removed. In such a case there is no need to
- * send a removal command.
- */
- if (id == TE_MAX) {
- IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
- return false;
- }
-
- return true;
-}
-
-/*
- * Explicit request to remove a aux roc time event. The removal of a time
- * event needs to be synchronized with the flow of a time event's end
- * notification, which also removes the time event from the op mode
- * data structures.
- */
-static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
- struct iwl_mvm_vif *mvmvif,
- struct iwl_mvm_time_event_data *te_data)
-{
- struct iwl_hs20_roc_req aux_cmd = {};
- u32 uid;
- int ret;
-
- if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
- return;
-
- aux_cmd.event_unique_id = cpu_to_le32(uid);
- aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
- aux_cmd.id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
- IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
- le32_to_cpu(aux_cmd.event_unique_id));
- ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
- sizeof(aux_cmd), &aux_cmd);
-
- if (WARN_ON(ret))
- return;
-}
-
-/*
- * Explicit request to remove a time event. The removal of a time event needs to
- * be synchronized with the flow of a time event's end notification, which also
- * removes the time event from the op mode data structures.
- */
-void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
- struct iwl_mvm_vif *mvmvif,
- struct iwl_mvm_time_event_data *te_data)
-{
- struct iwl_time_event_cmd time_cmd = {};
- u32 uid;
- int ret;
-
- if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
- return;
-
- /* When we remove a TE, the UID is to be set in the id field */
- time_cmd.id = cpu_to_le32(uid);
- time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
- time_cmd.id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
-
- IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
- ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
- sizeof(time_cmd), &time_cmd);
- if (WARN_ON(ret))
- return;
-}
-
-void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
-
- lockdep_assert_held(&mvm->mutex);
- iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
-}
-
-int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- int duration, enum ieee80211_roc_type type)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
- struct iwl_time_event_cmd time_cmd = {};
-
- lockdep_assert_held(&mvm->mutex);
- if (te_data->running) {
- IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
- return -EBUSY;
- }
-
- /*
- * Flush the done work, just in case it's still pending, so that
- * the work it does can complete and we can accept new frames.
- */
- flush_work(&mvm->roc_done_wk);
-
- time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
- time_cmd.id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
-
- switch (type) {
- case IEEE80211_ROC_TYPE_NORMAL:
- time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
- break;
- case IEEE80211_ROC_TYPE_MGMT_TX:
- time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
- break;
- default:
- WARN_ONCE(1, "Got an invalid ROC type\n");
- return -EINVAL;
- }
-
- time_cmd.apply_time = cpu_to_le32(0);
- time_cmd.interval = cpu_to_le32(1);
-
- /*
- * The P2P Device TEs can have lower priority than other events
- * that are being scheduled by the driver/fw, and thus it might not be
- * scheduled. To improve the chances of it being scheduled, allow them
- * to be fragmented, and in addition allow them to be delayed.
- */
- time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
- time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
- time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
- time_cmd.repeat = 1;
- time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
- TE_V2_NOTIF_HOST_EVENT_END |
- T2_V2_START_IMMEDIATELY);
-
- return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
-}
-
-void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
-{
- struct iwl_mvm_vif *mvmvif = NULL;
- struct iwl_mvm_time_event_data *te_data;
- bool is_p2p = false;
-
- lockdep_assert_held(&mvm->mutex);
-
- spin_lock_bh(&mvm->time_event_lock);
-
- /*
- * Iterate over the list of time events and find the time event that is
- * associated with a P2P_DEVICE interface.
- * This assumes that a P2P_DEVICE interface can have only a single time
- * event at any given time and this time event coresponds to a ROC
- * request
- */
- list_for_each_entry(te_data, &mvm->time_event_list, list) {
- if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
- is_p2p = true;
- goto remove_te;
- }
- }
-
- /* There can only be at most one AUX ROC time event, we just use the
- * list to simplify/unify code. Remove it if it exists.
- */
- te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
- struct iwl_mvm_time_event_data,
- list);
- if (te_data)
- mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
-
-remove_te:
- spin_unlock_bh(&mvm->time_event_lock);
-
- if (!mvmvif) {
- IWL_WARN(mvm, "No remain on channel event\n");
- return;
- }
-
- if (is_p2p)
- iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
- else
- iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
-
- iwl_mvm_roc_finished(mvm);
-}
-
-int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 duration, u32 apply_time)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
- struct iwl_time_event_cmd time_cmd = {};
-
- lockdep_assert_held(&mvm->mutex);
-
- if (te_data->running) {
- IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");
- return -EBUSY;
- }
-
- time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
- time_cmd.id_and_color =
- cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
- time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD);
- time_cmd.apply_time = cpu_to_le32(apply_time);
- time_cmd.max_frags = TE_V2_FRAG_NONE;
- time_cmd.duration = cpu_to_le32(duration);
- time_cmd.repeat = 1;
- time_cmd.interval = cpu_to_le32(1);
- time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
- TE_V2_ABSENCE);
-
- return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
deleted file mode 100644
index cbdf8e52a5f1..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.h
+++ /dev/null
@@ -1,249 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __time_event_h__
-#define __time_event_h__
-
-#include "fw-api.h"
-
-#include "mvm.h"
-
-/**
- * DOC: Time Events - what is it?
- *
- * Time Events are a fw feature that allows the driver to control the presence
- * of the device on the channel. Since the fw supports multiple channels
- * concurrently, the fw may choose to jump to another channel at any time.
- * In order to make sure that the fw is on a specific channel at a certain time
- * and for a certain duration, the driver needs to issue a time event.
- *
- * The simplest example is for BSS association. The driver issues a time event,
- * waits for it to start, and only then tells mac80211 that we can start the
- * association. This way, we make sure that the association will be done
- * smoothly and won't be interrupted by channel switch decided within the fw.
- */
-
- /**
- * DOC: The flow against the fw
- *
- * When the driver needs to make sure we are in a certain channel, at a certain
- * time and for a certain duration, it sends a Time Event. The flow against the
- * fw goes like this:
- * 1) Driver sends a TIME_EVENT_CMD to the fw
- * 2) Driver gets the response for that command. This response contains the
- * Unique ID (UID) of the event.
- * 3) The fw sends notification when the event starts.
- *
- * Of course the API provides various options that allow to cover parameters
- * of the flow.
- * What is the duration of the event?
- * What is the start time of the event?
- * Is there an end-time for the event?
- * How much can the event be delayed?
- * Can the event be split?
- * If yes what is the maximal number of chunks?
- * etc...
- */
-
-/**
- * DOC: Abstraction to the driver
- *
- * In order to simplify the use of time events to the rest of the driver,
- * we abstract the use of time events. This component provides the functions
- * needed by the driver.
- */
-
-#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500
-#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400
-
-/**
- * iwl_mvm_protect_session - start / extend the session protection.
- * @mvm: the mvm component
- * @vif: the virtual interface for which the session is issued
- * @duration: the duration of the session in TU.
- * @min_duration: will start a new session if the current session will end
- * in less than min_duration.
- * @max_delay: maximum delay before starting the time event (in TU)
- * @wait_for_notif: true if it is required that a time event notification be
- * waited for (that the time event has been scheduled before returning)
- *
- * This function can be used to start a session protection which means that the
- * fw will stay on the channel for %duration_ms milliseconds. This function
- * can block (sleep) until the session starts. This function can also be used
- * to extend a currently running session.
- * This function is meant to be used for BSS association for example, where we
- * want to make sure that the fw stays on the channel during the association.
- */
-void iwl_mvm_protect_session(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 duration, u32 min_duration,
- u32 max_delay, bool wait_for_notif);
-
-/**
- * iwl_mvm_stop_session_protection - cancel the session protection.
- * @mvm: the mvm component
- * @vif: the virtual interface for which the session is issued
- *
- * This functions cancels the session protection which is an act of good
- * citizenship. If it is not needed any more it should be canceled because
- * the other bindings wait for the medium during that time.
- * This funtions doesn't sleep.
- */
-void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-
-/*
- * iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION.
- */
-void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-
-/**
- * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality
- * @mvm: the mvm component
- * @vif: the virtual interface for which the roc is requested. It is assumed
- * that the vif type is NL80211_IFTYPE_P2P_DEVICE
- * @duration: the requested duration in millisecond for the fw to be on the
- * channel that is bound to the vif.
- * @type: the remain on channel request type
- *
- * This function can be used to issue a remain on channel session,
- * which means that the fw will stay in the channel for the request %duration
- * milliseconds. The function is async, meaning that it only issues the ROC
- * request but does not wait for it to start. Once the FW is ready to serve the
- * ROC request, it will issue a notification to the driver that it is on the
- * requested channel. Once the FW completes the ROC request it will issue
- * another notification to the driver.
- */
-int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- int duration, enum ieee80211_roc_type type);
-
-/**
- * iwl_mvm_stop_roc - stop remain on channel functionality
- * @mvm: the mvm component
- *
- * This function can be used to cancel an ongoing ROC session.
- * The function is async, it will instruct the FW to stop serving the ROC
- * session, but will not wait for the actual stopping of the session.
- */
-void iwl_mvm_stop_roc(struct iwl_mvm *mvm);
-
-/**
- * iwl_mvm_remove_time_event - general function to clean up of time event
- * @mvm: the mvm component
- * @vif: the vif to which the time event belongs
- * @te_data: the time event data that corresponds to that time event
- *
- * This function can be used to cancel a time event regardless its type.
- * It is useful for cleaning up time events running before removing an
- * interface.
- */
-void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
- struct iwl_mvm_vif *mvmvif,
- struct iwl_mvm_time_event_data *te_data);
-
-/**
- * iwl_mvm_te_clear_data - remove time event from list
- * @mvm: the mvm component
- * @te_data: the time event data to remove
- *
- * This function is mostly internal, it is made available here only
- * for firmware restart purposes.
- */
-void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
- struct iwl_mvm_time_event_data *te_data);
-
-void iwl_mvm_roc_done_wk(struct work_struct *wk);
-
-/**
- * iwl_mvm_schedule_csa_period - request channel switch absence period
- * @mvm: the mvm component
- * @vif: the virtual interface for which the channel switch is issued
- * @duration: the duration of the NoA in TU.
- * @apply_time: NoA start time in GP2.
- *
- * This function is used to schedule NoA time event and is used to perform
- * the channel switch flow.
- */
-int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 duration, u32 apply_time);
-
-/**
- * iwl_mvm_te_scheduled - check if the fw received the TE cmd
- * @te_data: the time event data that corresponds to that time event
- *
- * This function returns true iff this TE is added to the fw.
- */
-static inline bool
-iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data)
-{
- if (!te_data)
- return false;
-
- return !!te_data->uid;
-}
-
-#endif /* __time_event_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.c b/drivers/net/wireless/iwlwifi/mvm/tof.c
deleted file mode 100644
index 4007f1d421dd..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/tof.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include "mvm.h"
-#include "fw-api-tof.h"
-
-#define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256
-
-void iwl_mvm_tof_init(struct iwl_mvm *mvm)
-{
- struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
-
- if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
- return;
-
- memset(tof_data, 0, sizeof(*tof_data));
-
- tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (IWL_MVM_TOF_IS_RESPONDER) {
- tof_data->responder_cfg.sub_grp_cmd_id =
- cpu_to_le32(TOF_RESPONDER_CONFIG_CMD);
- tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT;
- }
-#endif
-
- tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD);
- tof_data->range_req.req_timeout = 1;
- tof_data->range_req.initiator = 1;
- tof_data->range_req.report_policy = 3;
-
- tof_data->range_req_ext.sub_grp_cmd_id =
- cpu_to_le32(TOF_RANGE_REQ_EXT_CMD);
-
- mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-}
-
-void iwl_mvm_tof_clean(struct iwl_mvm *mvm)
-{
- struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
-
- if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
- return;
-
- memset(tof_data, 0, sizeof(*tof_data));
- mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-}
-
-static void iwl_tof_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- bool *enabled = _data;
-
- /* non bss vif exists */
- if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
- *enabled = false;
-}
-
-int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm)
-{
- struct iwl_tof_config_cmd *cmd = &mvm->tof_data.tof_cfg;
- bool enabled;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
- return -EINVAL;
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_tof_iterator, &enabled);
- if (!enabled) {
- IWL_DEBUG_INFO(mvm, "ToF is not supported (non bss vif)\n");
- return -EINVAL;
- }
-
- mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
- return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
- IWL_ALWAYS_LONG_GROUP, 0),
- 0, sizeof(*cmd), cmd);
-}
-
-int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id)
-{
- struct iwl_tof_range_abort_cmd cmd = {
- .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD),
- .request_id = id,
- };
-
- lockdep_assert_held(&mvm->mutex);
-
- if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
- return -EINVAL;
-
- if (id != mvm->tof_data.active_range_request) {
- IWL_ERR(mvm, "Invalid range request id %d (active %d)\n",
- id, mvm->tof_data.active_range_request);
- return -EINVAL;
- }
-
- /* after abort is sent there's no active request anymore */
- mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-
- return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
- IWL_ALWAYS_LONG_GROUP, 0),
- 0, sizeof(cmd), &cmd);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_tof_responder_config_cmd *cmd = &mvm->tof_data.responder_cfg;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- lockdep_assert_held(&mvm->mutex);
-
- if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
- return -EINVAL;
-
- if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
- !mvmvif->ap_ibss_active) {
- IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
- return -EIO;
- }
-
- cmd->sta_id = mvmvif->bcast_sta.sta_id;
- memcpy(cmd->bssid, vif->addr, ETH_ALEN);
- return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
- IWL_ALWAYS_LONG_GROUP, 0),
- 0, sizeof(*cmd), cmd);
-}
-#endif
-
-int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_host_cmd cmd = {
- .id = iwl_cmd_id(TOF_CMD, IWL_ALWAYS_LONG_GROUP, 0),
- .len = { sizeof(mvm->tof_data.range_req), },
- /* no copy because of the command size */
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
-
- lockdep_assert_held(&mvm->mutex);
-
- if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
- return -EINVAL;
-
- if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
- IWL_ERR(mvm, "Cannot send range request, not STA mode\n");
- return -EIO;
- }
-
- /* nesting of range requests is not supported in FW */
- if (mvm->tof_data.active_range_request !=
- IWL_MVM_TOF_RANGE_REQ_MAX_ID) {
- IWL_ERR(mvm, "Cannot send range req, already active req %d\n",
- mvm->tof_data.active_range_request);
- return -EIO;
- }
-
- mvm->tof_data.active_range_request = mvm->tof_data.range_req.request_id;
-
- cmd.data[0] = &mvm->tof_data.range_req;
- return iwl_mvm_send_cmd(mvm, &cmd);
-}
-
-int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- lockdep_assert_held(&mvm->mutex);
-
- if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
- return -EINVAL;
-
- if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
- IWL_ERR(mvm, "Cannot send ext range req, not in STA mode\n");
- return -EIO;
- }
-
- return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
- IWL_ALWAYS_LONG_GROUP, 0),
- 0, sizeof(mvm->tof_data.range_req_ext),
- &mvm->tof_data.range_req_ext);
-}
-
-static int iwl_mvm_tof_range_resp(struct iwl_mvm *mvm, void *data)
-{
- struct iwl_tof_range_rsp_ntfy *resp = (void *)data;
-
- if (resp->request_id != mvm->tof_data.active_range_request) {
- IWL_ERR(mvm, "Request id mismatch, got %d, active %d\n",
- resp->request_id, mvm->tof_data.active_range_request);
- return -EIO;
- }
-
- memcpy(&mvm->tof_data.range_resp, resp,
- sizeof(struct iwl_tof_range_rsp_ntfy));
- mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-
- return 0;
-}
-
-static int iwl_mvm_tof_mcsi_notif(struct iwl_mvm *mvm, void *data)
-{
- struct iwl_tof_mcsi_notif *resp = (struct iwl_tof_mcsi_notif *)data;
-
- IWL_DEBUG_INFO(mvm, "MCSI notification, token %d\n", resp->token);
- return 0;
-}
-
-static int iwl_mvm_tof_nb_report_notif(struct iwl_mvm *mvm, void *data)
-{
- struct iwl_tof_neighbor_report *report =
- (struct iwl_tof_neighbor_report *)data;
-
- IWL_DEBUG_INFO(mvm, "NB report, bssid %pM, token %d, status 0x%x\n",
- report->bssid, report->request_token, report->status);
- return 0;
-}
-
-void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_tof_gen_resp_cmd *resp = (void *)pkt->data;
-
- lockdep_assert_held(&mvm->mutex);
-
- switch (le32_to_cpu(resp->sub_grp_cmd_id)) {
- case TOF_RANGE_RESPONSE_NOTIF:
- iwl_mvm_tof_range_resp(mvm, resp->data);
- break;
- case TOF_MCSI_DEBUG_NOTIF:
- iwl_mvm_tof_mcsi_notif(mvm, resp->data);
- break;
- case TOF_NEIGHBOR_REPORT_RSP_NOTIF:
- iwl_mvm_tof_nb_report_notif(mvm, resp->data);
- break;
- default:
- IWL_ERR(mvm, "Unknown sub-group command 0x%x\n",
- resp->sub_grp_cmd_id);
- break;
- }
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.h b/drivers/net/wireless/iwlwifi/mvm/tof.h
deleted file mode 100644
index 9beebc33cb8d..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/tof.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#ifndef __tof_h__
-#define __tof_h__
-
-#include "fw-api-tof.h"
-
-struct iwl_mvm_tof_data {
- struct iwl_tof_config_cmd tof_cfg;
- struct iwl_tof_range_req_cmd range_req;
- struct iwl_tof_range_req_ext_cmd range_req_ext;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- struct iwl_tof_responder_config_cmd responder_cfg;
-#endif
- struct iwl_tof_range_rsp_ntfy range_resp;
- u8 last_abort_id;
- u16 active_range_request;
-};
-
-void iwl_mvm_tof_init(struct iwl_mvm *mvm);
-void iwl_mvm_tof_clean(struct iwl_mvm *mvm);
-int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm);
-int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id);
-int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
-int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-#endif
-#endif /* __tof_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
deleted file mode 100644
index cadfc0460597..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/tt.c
+++ /dev/null
@@ -1,460 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include "mvm.h"
-
-#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
-
-static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
-{
- struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
- u32 duration = tt->params.ct_kill_duration;
-
- if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
- return;
-
- IWL_ERR(mvm, "Enter CT Kill\n");
- iwl_mvm_set_hw_ctkill_state(mvm, true);
-
- tt->throttle = false;
- tt->dynamic_smps = false;
-
- /* Don't schedule an exit work if we're in test mode, since
- * the temperature will not change unless we manually set it
- * again (or disable testing).
- */
- if (!mvm->temperature_test)
- schedule_delayed_work(&tt->ct_kill_exit,
- round_jiffies_relative(duration * HZ));
-}
-
-static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
-{
- if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
- return;
-
- IWL_ERR(mvm, "Exit CT Kill\n");
- iwl_mvm_set_hw_ctkill_state(mvm, false);
-}
-
-void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
-{
- /* ignore the notification if we are in test mode */
- if (mvm->temperature_test)
- return;
-
- if (mvm->temperature == temp)
- return;
-
- mvm->temperature = temp;
- iwl_mvm_tt_handler(mvm);
-}
-
-static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_dts_measurement_notif *notif;
- int len = iwl_rx_packet_payload_len(pkt);
- int temp;
-
- if (WARN_ON_ONCE(len != sizeof(*notif))) {
- IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
- return -EINVAL;
- }
-
- notif = (void *)pkt->data;
-
- temp = le32_to_cpu(notif->temp);
-
- /* shouldn't be negative, but since it's s32, make sure it isn't */
- if (WARN_ON_ONCE(temp < 0))
- temp = 0;
-
- IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
-
- return temp;
-}
-
-static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
-{
- struct iwl_mvm *mvm =
- container_of(notif_wait, struct iwl_mvm, notif_wait);
- int *temp = data;
- int ret;
-
- ret = iwl_mvm_temp_notif_parse(mvm, pkt);
- if (ret < 0)
- return true;
-
- *temp = ret;
-
- return true;
-}
-
-void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- int temp;
-
- /* the notification is handled synchronously in ctkill, so skip here */
- if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
- return;
-
- temp = iwl_mvm_temp_notif_parse(mvm, pkt);
- if (temp < 0)
- return;
-
- iwl_mvm_tt_temp_changed(mvm, temp);
-}
-
-static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
-{
- struct iwl_dts_measurement_cmd cmd = {
- .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
- };
- struct iwl_ext_dts_measurement_cmd extcmd = {
- .control_mode = cpu_to_le32(DTS_AUTOMATIC),
- };
- u32 cmdid;
-
- if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR))
- cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE,
- PHY_OPS_GROUP, 0);
- else
- cmdid = CMD_DTS_MEASUREMENT_TRIGGER;
-
- if (!fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE))
- return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd);
-
- return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd);
-}
-
-int iwl_mvm_get_temp(struct iwl_mvm *mvm)
-{
- struct iwl_notification_wait wait_temp_notif;
- static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
- DTS_MEASUREMENT_NOTIF_WIDE) };
- int ret, temp;
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR))
- temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION;
-
- lockdep_assert_held(&mvm->mutex);
-
- iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
- temp_notif, ARRAY_SIZE(temp_notif),
- iwl_mvm_temp_notif_wait, &temp);
-
- ret = iwl_mvm_get_temp_cmd(mvm);
- if (ret) {
- IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret);
- iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
- return ret;
- }
-
- ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
- IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
- if (ret) {
- IWL_ERR(mvm, "Getting the temperature timed out\n");
- return ret;
- }
-
- return temp;
-}
-
-static void check_exit_ctkill(struct work_struct *work)
-{
- struct iwl_mvm_tt_mgmt *tt;
- struct iwl_mvm *mvm;
- u32 duration;
- s32 temp;
-
- tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
- mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
-
- duration = tt->params.ct_kill_duration;
-
- mutex_lock(&mvm->mutex);
-
- if (__iwl_mvm_mac_start(mvm))
- goto reschedule;
-
- /* make sure the device is available for direct read/writes */
- if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) {
- __iwl_mvm_mac_stop(mvm);
- goto reschedule;
- }
-
- temp = iwl_mvm_get_temp(mvm);
-
- iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL);
-
- __iwl_mvm_mac_stop(mvm);
-
- if (temp < 0)
- goto reschedule;
-
- IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
-
- if (temp <= tt->params.ct_kill_exit) {
- mutex_unlock(&mvm->mutex);
- iwl_mvm_exit_ctkill(mvm);
- return;
- }
-
-reschedule:
- mutex_unlock(&mvm->mutex);
- schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
- round_jiffies(duration * HZ));
-}
-
-static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = _data;
- enum ieee80211_smps_mode smps_mode;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (mvm->thermal_throttle.dynamic_smps)
- smps_mode = IEEE80211_SMPS_DYNAMIC;
- else
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
-
- if (vif->type != NL80211_IFTYPE_STATION)
- return;
-
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
-}
-
-static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
-{
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvmsta;
- int i, err;
-
- for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(sta))
- continue;
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- if (enable == mvmsta->tt_tx_protection)
- continue;
- err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
- if (err) {
- IWL_ERR(mvm, "Failed to %s Tx protection\n",
- enable ? "enable" : "disable");
- } else {
- IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
- enable ? "Enable" : "Disable");
- mvmsta->tt_tx_protection = enable;
- }
- }
-}
-
-void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
-{
- struct iwl_host_cmd cmd = {
- .id = REPLY_THERMAL_MNG_BACKOFF,
- .len = { sizeof(u32), },
- .data = { &backoff, },
- };
-
- backoff = max(backoff, mvm->thermal_throttle.min_backoff);
-
- if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
- IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
- backoff);
- mvm->thermal_throttle.tx_backoff = backoff;
- } else {
- IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
- }
-}
-
-void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
-{
- struct iwl_tt_params *params = &mvm->thermal_throttle.params;
- struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
- s32 temperature = mvm->temperature;
- bool throttle_enable = false;
- int i;
- u32 tx_backoff;
-
- IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
-
- if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
- iwl_mvm_enter_ctkill(mvm);
- return;
- }
-
- if (params->support_ct_kill &&
- temperature <= params->ct_kill_exit) {
- iwl_mvm_exit_ctkill(mvm);
- return;
- }
-
- if (params->support_dynamic_smps) {
- if (!tt->dynamic_smps &&
- temperature >= params->dynamic_smps_entry) {
- IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
- tt->dynamic_smps = true;
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_tt_smps_iterator, mvm);
- throttle_enable = true;
- } else if (tt->dynamic_smps &&
- temperature <= params->dynamic_smps_exit) {
- IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
- tt->dynamic_smps = false;
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_tt_smps_iterator, mvm);
- }
- }
-
- if (params->support_tx_protection) {
- if (temperature >= params->tx_protection_entry) {
- iwl_mvm_tt_tx_protection(mvm, true);
- throttle_enable = true;
- } else if (temperature <= params->tx_protection_exit) {
- iwl_mvm_tt_tx_protection(mvm, false);
- }
- }
-
- if (params->support_tx_backoff) {
- tx_backoff = tt->min_backoff;
- for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
- if (temperature < params->tx_backoff[i].temperature)
- break;
- tx_backoff = max(tt->min_backoff,
- params->tx_backoff[i].backoff);
- }
- if (tx_backoff != tt->min_backoff)
- throttle_enable = true;
- if (tt->tx_backoff != tx_backoff)
- iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
- }
-
- if (!tt->throttle && throttle_enable) {
- IWL_WARN(mvm,
- "Due to high temperature thermal throttling initiated\n");
- tt->throttle = true;
- } else if (tt->throttle && !tt->dynamic_smps &&
- tt->tx_backoff == tt->min_backoff &&
- temperature <= params->tx_protection_exit) {
- IWL_WARN(mvm,
- "Temperature is back to normal thermal throttling stopped\n");
- tt->throttle = false;
- }
-}
-
-static const struct iwl_tt_params iwl_mvm_default_tt_params = {
- .ct_kill_entry = 118,
- .ct_kill_exit = 96,
- .ct_kill_duration = 5,
- .dynamic_smps_entry = 114,
- .dynamic_smps_exit = 110,
- .tx_protection_entry = 114,
- .tx_protection_exit = 108,
- .tx_backoff = {
- {.temperature = 112, .backoff = 200},
- {.temperature = 113, .backoff = 600},
- {.temperature = 114, .backoff = 1200},
- {.temperature = 115, .backoff = 2000},
- {.temperature = 116, .backoff = 4000},
- {.temperature = 117, .backoff = 10000},
- },
- .support_ct_kill = true,
- .support_dynamic_smps = true,
- .support_tx_protection = true,
- .support_tx_backoff = true,
-};
-
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff)
-{
- struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
-
- IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
-
- if (mvm->cfg->thermal_params)
- tt->params = *mvm->cfg->thermal_params;
- else
- tt->params = iwl_mvm_default_tt_params;
-
- tt->throttle = false;
- tt->dynamic_smps = false;
- tt->min_backoff = min_backoff;
- INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
-}
-
-void iwl_mvm_tt_exit(struct iwl_mvm *mvm)
-{
- cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
- IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
deleted file mode 100644
index c652a66be803..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ /dev/null
@@ -1,1115 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/ieee80211.h>
-#include <linux/etherdevice.h>
-
-#include "iwl-trans.h"
-#include "iwl-eeprom-parse.h"
-#include "mvm.h"
-#include "sta.h"
-
-static void
-iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
- u16 tid, u16 ssn)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_ba *ba_trig;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
- return;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
- ba_trig = (void *)trig->data;
-
- if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
- return;
-
- if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid)))
- return;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trig,
- "BAR sent to %pM, tid %d, ssn %d",
- addr, tid, ssn);
-}
-
-/*
- * Sets most of the Tx cmd's fields
- */
-void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info, u8 sta_id)
-{
- struct ieee80211_hdr *hdr = (void *)skb->data;
- __le16 fc = hdr->frame_control;
- u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
- u32 len = skb->len + FCS_LEN;
- u8 ac;
-
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
- tx_flags |= TX_CMD_FLG_ACK;
- else
- tx_flags &= ~TX_CMD_FLG_ACK;
-
- if (ieee80211_is_probe_resp(fc))
- tx_flags |= TX_CMD_FLG_TSF;
-
- if (ieee80211_has_morefrags(fc))
- tx_flags |= TX_CMD_FLG_MORE_FRAG;
-
- if (ieee80211_is_data_qos(fc)) {
- u8 *qc = ieee80211_get_qos_ctl(hdr);
- tx_cmd->tid_tspec = qc[0] & 0xf;
- tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
- } else if (ieee80211_is_back_req(fc)) {
- struct ieee80211_bar *bar = (void *)skb->data;
- u16 control = le16_to_cpu(bar->control);
- u16 ssn = le16_to_cpu(bar->start_seq_num);
-
- tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
- tx_cmd->tid_tspec = (control &
- IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
- IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
- WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT);
- iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec,
- ssn);
- } else {
- tx_cmd->tid_tspec = IWL_TID_NON_QOS;
- if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
- tx_flags |= TX_CMD_FLG_SEQ_CTL;
- else
- tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
- }
-
- /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */
- if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT)
- ac = tid_to_mac80211_ac[tx_cmd->tid_tspec];
- else
- ac = tid_to_mac80211_ac[0];
-
- tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) <<
- TX_CMD_FLG_BT_PRIO_POS;
-
- if (ieee80211_is_mgmt(fc)) {
- if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
- tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_ASSOC);
- else if (ieee80211_is_action(fc))
- tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE);
- else
- tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT);
-
- /* The spec allows Action frames in A-MPDU, we don't support
- * it
- */
- WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
- } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) {
- tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT);
- } else {
- tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE);
- }
-
- if (ieee80211_is_data(fc) && len > mvm->rts_threshold &&
- !is_multicast_ether_addr(ieee80211_get_DA(hdr)))
- tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
-
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT) &&
- ieee80211_action_contains_tpc(skb))
- tx_flags |= TX_CMD_FLG_WRITE_TX_POWER;
-
- tx_cmd->tx_flags = cpu_to_le32(tx_flags);
- /* Total # bytes to be transmitted */
- tx_cmd->len = cpu_to_le16((u16)skb->len);
- tx_cmd->next_frame_len = 0;
- tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
- tx_cmd->sta_id = sta_id;
-}
-
-/*
- * Sets the fields in the Tx cmd that are rate related
- */
-void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta, __le16 fc)
-{
- u32 rate_flags;
- int rate_idx;
- u8 rate_plcp;
-
- /* Set retry limit on RTS packets */
- tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT;
-
- /* Set retry limit on DATA packets and Probe Responses*/
- if (ieee80211_is_probe_resp(fc)) {
- tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT;
- tx_cmd->rts_retry_limit =
- min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit);
- } else if (ieee80211_is_back_req(fc)) {
- tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT;
- } else {
- tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY;
- }
-
- /*
- * for data packets, rate info comes from the table inside the fw. This
- * table is controlled by LINK_QUALITY commands
- */
-
- if (ieee80211_is_data(fc) && sta) {
- tx_cmd->initial_rate_index = 0;
- tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
- return;
- } else if (ieee80211_is_back_req(fc)) {
- tx_cmd->tx_flags |=
- cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR);
- }
-
- /* HT rate doesn't make sense for a non data frame */
- WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
- "Got an HT rate (flags:0x%x/mcs:%d) for a non data frame (fc:0x%x)\n",
- info->control.rates[0].flags,
- info->control.rates[0].idx,
- le16_to_cpu(fc));
-
- rate_idx = info->control.rates[0].idx;
- /* if the rate isn't a well known legacy rate, take the lowest one */
- if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY)
- rate_idx = rate_lowest_index(
- &mvm->nvm_data->bands[info->band], sta);
-
- /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
- if (info->band == IEEE80211_BAND_5GHZ)
- rate_idx += IWL_FIRST_OFDM_RATE;
-
- /* For 2.4 GHZ band, check that there is no need to remap */
- BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
-
- /* Get PLCP rate for tx_cmd->rate_n_flags */
- rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
-
- mvm->mgmt_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
- mvm->mgmt_last_antenna_idx);
-
- if (info->band == IEEE80211_BAND_2GHZ &&
- !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
- rate_flags = mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS;
- else
- rate_flags =
- BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
-
- /* Set CCK flag as needed */
- if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
- rate_flags |= RATE_MCS_CCK_MSK;
-
- /* Set the rate in the TX cmd */
- tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags);
-}
-
-/*
- * Sets the fields in the Tx cmd that are crypto related
- */
-static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
- struct ieee80211_tx_info *info,
- struct iwl_tx_cmd *tx_cmd,
- struct sk_buff *skb_frag,
- int hdrlen)
-{
- struct ieee80211_key_conf *keyconf = info->control.hw_key;
- u8 *crypto_hdr = skb_frag->data + hdrlen;
- u64 pn;
-
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_CCMP_256:
- iwl_mvm_set_tx_cmd_ccmp(info, tx_cmd);
- pn = atomic64_inc_return(&keyconf->tx_pn);
- crypto_hdr[0] = pn;
- crypto_hdr[2] = 0;
- crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6);
- crypto_hdr[1] = pn >> 8;
- crypto_hdr[4] = pn >> 16;
- crypto_hdr[5] = pn >> 24;
- crypto_hdr[6] = pn >> 32;
- crypto_hdr[7] = pn >> 40;
- break;
-
- case WLAN_CIPHER_SUITE_TKIP:
- tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
- ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
- break;
-
- case WLAN_CIPHER_SUITE_WEP104:
- tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
- /* fall through */
- case WLAN_CIPHER_SUITE_WEP40:
- tx_cmd->sec_ctl |= TX_CMD_SEC_WEP |
- ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) &
- TX_CMD_SEC_WEP_KEY_IDX_MSK);
-
- memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
- break;
- default:
- tx_cmd->sec_ctl |= TX_CMD_SEC_EXT;
- }
-}
-
-/*
- * Allocates and sets the Tx cmd the driver data pointers in the skb
- */
-static struct iwl_device_cmd *
-iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
- int hdrlen, struct ieee80211_sta *sta, u8 sta_id)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl_device_cmd *dev_cmd;
- struct iwl_tx_cmd *tx_cmd;
-
- dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans);
-
- if (unlikely(!dev_cmd))
- return NULL;
-
- memset(dev_cmd, 0, sizeof(*dev_cmd));
- dev_cmd->hdr.cmd = TX_CMD;
- tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
-
- if (info->control.hw_key)
- iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen);
-
- iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id);
-
- iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
-
- memset(&info->status, 0, sizeof(info->status));
-
- info->driver_data[0] = NULL;
- info->driver_data[1] = dev_cmd;
-
- return dev_cmd;
-}
-
-int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl_device_cmd *dev_cmd;
- struct iwl_tx_cmd *tx_cmd;
- u8 sta_id;
- int hdrlen = ieee80211_hdrlen(hdr->frame_control);
-
- if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU))
- return -1;
-
- if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM &&
- (!info->control.vif ||
- info->hw_queue != info->control.vif->cab_queue)))
- return -1;
-
- /*
- * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used
- * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel
- * queue. STATION (HS2.0) uses the auxiliary context of the FW,
- * and hence needs to be sent on the aux queue
- */
- if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
- info->control.vif->type == NL80211_IFTYPE_STATION)
- IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue;
-
- /*
- * If the interface on which the frame is sent is the P2P_DEVICE
- * or an AP/GO interface use the broadcast station associated
- * with it; otherwise if the interface is a managed interface
- * use the AP station associated with it for multicast traffic
- * (this is not possible for unicast packets as a TLDS discovery
- * response are sent without a station entry); otherwise use the
- * AUX station.
- */
- sta_id = mvm->aux_sta.sta_id;
- if (info->control.vif) {
- struct iwl_mvm_vif *mvmvif =
- iwl_mvm_vif_from_mac80211(info->control.vif);
-
- if (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE ||
- info->control.vif->type == NL80211_IFTYPE_AP)
- sta_id = mvmvif->bcast_sta.sta_id;
- else if (info->control.vif->type == NL80211_IFTYPE_STATION &&
- is_multicast_ether_addr(hdr->addr1)) {
- u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id);
-
- if (ap_sta_id != IWL_MVM_STATION_COUNT)
- sta_id = ap_sta_id;
- }
- }
-
- IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue);
-
- dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, NULL, sta_id);
- if (!dev_cmd)
- return -1;
-
- /* From now on, we cannot access info->control */
- tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
-
- /* Copy MAC header from skb into command buffer */
- memcpy(tx_cmd->hdr, hdr, hdrlen);
-
- if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) {
- iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Sets the fields in the Tx cmd that are crypto related
- */
-int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct ieee80211_sta *sta)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl_mvm_sta *mvmsta;
- struct iwl_device_cmd *dev_cmd;
- struct iwl_tx_cmd *tx_cmd;
- __le16 fc;
- u16 seq_number = 0;
- u8 tid = IWL_MAX_TID_COUNT;
- u8 txq_id = info->hw_queue;
- bool is_data_qos = false, is_ampdu = false;
- int hdrlen;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- fc = hdr->frame_control;
- hdrlen = ieee80211_hdrlen(fc);
-
- if (WARN_ON_ONCE(!mvmsta))
- return -1;
-
- if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
- return -1;
-
- dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, sta, mvmsta->sta_id);
- if (!dev_cmd)
- goto drop;
-
- tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
- /* From now on, we cannot access info->control */
-
- /*
- * we handle that entirely ourselves -- for uAPSD the firmware
- * will always send a notification, and for PS-Poll responses
- * we'll notify mac80211 when getting frame status
- */
- info->flags &= ~IEEE80211_TX_STATUS_EOSP;
-
- spin_lock(&mvmsta->lock);
-
- if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
- u8 *qc = NULL;
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
- if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
- goto drop_unlock_sta;
-
- seq_number = mvmsta->tid_data[tid].seq_number;
- seq_number &= IEEE80211_SCTL_SEQ;
- hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
- hdr->seq_ctrl |= cpu_to_le16(seq_number);
- is_data_qos = true;
- is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU;
- }
-
- /* Copy MAC header from skb into command buffer */
- memcpy(tx_cmd->hdr, hdr, hdrlen);
-
- WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
-
- if (sta->tdls) {
- /* default to TID 0 for non-QoS packets */
- u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid;
-
- txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]];
- }
-
- if (is_ampdu) {
- if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
- goto drop_unlock_sta;
- txq_id = mvmsta->tid_data[tid].txq_id;
- }
-
- IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
- tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number));
-
- if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
- goto drop_unlock_sta;
-
- if (is_data_qos && !ieee80211_has_morefrags(fc))
- mvmsta->tid_data[tid].seq_number = seq_number + 0x10;
-
- spin_unlock(&mvmsta->lock);
-
- if (txq_id < mvm->first_agg_queue)
- atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);
-
- return 0;
-
-drop_unlock_sta:
- iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
- spin_unlock(&mvmsta->lock);
-drop:
- return -1;
-}
-
-static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta, u8 tid)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
- struct ieee80211_vif *vif = mvmsta->vif;
-
- lockdep_assert_held(&mvmsta->lock);
-
- if ((tid_data->state == IWL_AGG_ON ||
- tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
- iwl_mvm_tid_queued(tid_data) == 0) {
- /*
- * Now that this aggregation queue is empty tell mac80211 so it
- * knows we no longer have frames buffered for the station on
- * this TID (for the TIM bitmap calculation.)
- */
- ieee80211_sta_set_buffered(sta, tid, false);
- }
-
- if (tid_data->ssn != tid_data->next_reclaimed)
- return;
-
- switch (tid_data->state) {
- case IWL_EMPTYING_HW_QUEUE_ADDBA:
- IWL_DEBUG_TX_QUEUES(mvm,
- "Can continue addBA flow ssn = next_recl = %d\n",
- tid_data->next_reclaimed);
- tid_data->state = IWL_AGG_STARTING;
- ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- break;
-
- case IWL_EMPTYING_HW_QUEUE_DELBA:
- IWL_DEBUG_TX_QUEUES(mvm,
- "Can continue DELBA flow ssn = next_recl = %d\n",
- tid_data->next_reclaimed);
- iwl_mvm_disable_txq(mvm, tid_data->txq_id,
- vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
- CMD_ASYNC);
- tid_data->state = IWL_AGG_OFF;
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- break;
-
- default:
- break;
- }
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-const char *iwl_mvm_get_tx_fail_reason(u32 status)
-{
-#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
-#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
-
- switch (status & TX_STATUS_MSK) {
- case TX_STATUS_SUCCESS:
- return "SUCCESS";
- TX_STATUS_POSTPONE(DELAY);
- TX_STATUS_POSTPONE(FEW_BYTES);
- TX_STATUS_POSTPONE(BT_PRIO);
- TX_STATUS_POSTPONE(QUIET_PERIOD);
- TX_STATUS_POSTPONE(CALC_TTAK);
- TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
- TX_STATUS_FAIL(SHORT_LIMIT);
- TX_STATUS_FAIL(LONG_LIMIT);
- TX_STATUS_FAIL(UNDERRUN);
- TX_STATUS_FAIL(DRAIN_FLOW);
- TX_STATUS_FAIL(RFKILL_FLUSH);
- TX_STATUS_FAIL(LIFE_EXPIRE);
- TX_STATUS_FAIL(DEST_PS);
- TX_STATUS_FAIL(HOST_ABORTED);
- TX_STATUS_FAIL(BT_RETRY);
- TX_STATUS_FAIL(STA_INVALID);
- TX_STATUS_FAIL(FRAG_DROPPED);
- TX_STATUS_FAIL(TID_DISABLE);
- TX_STATUS_FAIL(FIFO_FLUSHED);
- TX_STATUS_FAIL(SMALL_CF_POLL);
- TX_STATUS_FAIL(FW_DROP);
- TX_STATUS_FAIL(STA_COLOR_MISMATCH);
- }
-
- return "UNKNOWN";
-
-#undef TX_STATUS_FAIL
-#undef TX_STATUS_POSTPONE
-}
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
- enum ieee80211_band band,
- struct ieee80211_tx_rate *r)
-{
- if (rate_n_flags & RATE_HT_MCS_GF_MSK)
- r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
- switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
- case RATE_MCS_CHAN_WIDTH_20:
- break;
- case RATE_MCS_CHAN_WIDTH_40:
- r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
- break;
- case RATE_MCS_CHAN_WIDTH_80:
- r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
- break;
- case RATE_MCS_CHAN_WIDTH_160:
- r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
- break;
- }
- if (rate_n_flags & RATE_MCS_SGI_MSK)
- r->flags |= IEEE80211_TX_RC_SHORT_GI;
- if (rate_n_flags & RATE_MCS_HT_MSK) {
- r->flags |= IEEE80211_TX_RC_MCS;
- r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
- } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
- ieee80211_rate_set_vht(
- r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK,
- ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
- RATE_VHT_MCS_NSS_POS) + 1);
- r->flags |= IEEE80211_TX_RC_VHT_MCS;
- } else {
- r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
- band);
- }
-}
-
-/**
- * translate ucode response to mac80211 tx status control values
- */
-static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags,
- struct ieee80211_tx_info *info)
-{
- struct ieee80211_tx_rate *r = &info->status.rates[0];
-
- info->status.antenna =
- ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
- iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r);
-}
-
-static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt)
-{
- struct ieee80211_sta *sta;
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- int txq_id = SEQ_TO_QUEUE(sequence);
- struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
- int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
- int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
- u32 status = le16_to_cpu(tx_resp->status.status);
- u16 ssn = iwl_mvm_get_scd_ssn(tx_resp);
- struct iwl_mvm_sta *mvmsta;
- struct sk_buff_head skbs;
- u8 skb_freed = 0;
- u16 next_reclaimed, seq_ctl;
-
- __skb_queue_head_init(&skbs);
-
- seq_ctl = le16_to_cpu(tx_resp->seq_ctl);
-
- /* we can free until ssn % q.n_bd not inclusive */
- iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs);
-
- while (!skb_queue_empty(&skbs)) {
- struct sk_buff *skb = __skb_dequeue(&skbs);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- skb_freed++;
-
- iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
-
- memset(&info->status, 0, sizeof(info->status));
-
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-
- /* inform mac80211 about what happened with the frame */
- switch (status & TX_STATUS_MSK) {
- case TX_STATUS_SUCCESS:
- case TX_STATUS_DIRECT_DONE:
- info->flags |= IEEE80211_TX_STAT_ACK;
- break;
- case TX_STATUS_FAIL_DEST_PS:
- info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
- break;
- default:
- break;
- }
-
- info->status.rates[0].count = tx_resp->failure_frame + 1;
- iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate),
- info);
- info->status.status_driver_data[1] =
- (void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate);
-
- /* Single frame failure in an AMPDU queue => send BAR */
- if (txq_id >= mvm->first_agg_queue &&
- !(info->flags & IEEE80211_TX_STAT_ACK) &&
- !(info->flags & IEEE80211_TX_STAT_TX_FILTERED))
- info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
-
- /* 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);
- }
-
- /*
- * TODO: this is not accurate if we are freeing more than one
- * packet.
- */
- info->status.tx_time =
- le16_to_cpu(tx_resp->wireless_media_time);
- BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
- info->status.status_driver_data[0] =
- (void *)(uintptr_t)tx_resp->reduced_tpc;
-
- ieee80211_tx_status(mvm->hw, skb);
- }
-
- if (txq_id >= mvm->first_agg_queue) {
- /* If this is an aggregation queue, we use the ssn since:
- * ssn = wifi seq_num % 256.
- * The seq_ctl is the sequence control of the packet to which
- * this Tx response relates. But if there is a hole in the
- * bitmap of the BA we received, this Tx response may allow to
- * reclaim the hole and all the subsequent packets that were
- * already acked. In that case, seq_ctl != ssn, and the next
- * packet to be reclaimed will be ssn and not seq_ctl. In that
- * case, several packets will be reclaimed even if
- * frame_count = 1.
- *
- * The ssn is the index (% 256) of the latest packet that has
- * treated (acked / dropped) + 1.
- */
- next_reclaimed = ssn;
- } else {
- /* The next packet to be reclaimed is the one after this one */
- next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10);
- }
-
- IWL_DEBUG_TX_REPLY(mvm,
- "TXQ %d status %s (0x%08x)\n",
- txq_id, iwl_mvm_get_tx_fail_reason(status), status);
-
- IWL_DEBUG_TX_REPLY(mvm,
- "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n",
- le32_to_cpu(tx_resp->initial_rate),
- tx_resp->failure_frame, SEQ_TO_INDEX(sequence),
- ssn, next_reclaimed, seq_ctl);
-
- rcu_read_lock();
-
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
- /*
- * sta can't be NULL otherwise it'd mean that the sta has been freed in
- * the firmware while we still have packets for it in the Tx queues.
- */
- if (WARN_ON_ONCE(!sta))
- goto out;
-
- if (!IS_ERR(sta)) {
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
-
- if (tid != IWL_TID_NON_QOS) {
- struct iwl_mvm_tid_data *tid_data =
- &mvmsta->tid_data[tid];
-
- spin_lock_bh(&mvmsta->lock);
- tid_data->next_reclaimed = next_reclaimed;
- IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n",
- next_reclaimed);
- iwl_mvm_check_ratid_empty(mvm, sta, tid);
- spin_unlock_bh(&mvmsta->lock);
- }
-
- if (mvmsta->next_status_eosp) {
- mvmsta->next_status_eosp = false;
- ieee80211_sta_eosp(sta);
- }
- } else {
- mvmsta = NULL;
- }
-
- /*
- * If the txq is not an AMPDU queue, there is no chance we freed
- * several skbs. Check that out...
- */
- if (txq_id >= mvm->first_agg_queue)
- goto out;
-
- /* We can't free more than one frame at once on a shared queue */
- WARN_ON(skb_freed > 1);
-
- /* If we have still frames for this STA nothing to do here */
- if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
- goto out;
-
- if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
-
- /*
- * If there are no pending frames for this STA and
- * the tx to this station is not disabled, notify
- * mac80211 that this station can now wake up in its
- * STA table.
- * If mvmsta is not NULL, sta is valid.
- */
-
- spin_lock_bh(&mvmsta->lock);
-
- if (!mvmsta->disable_tx)
- ieee80211_sta_block_awake(mvm->hw, sta, false);
-
- spin_unlock_bh(&mvmsta->lock);
- }
-
- if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
- /*
- * We are draining and this was the last packet - pre_rcu_remove
- * has been called already. We might be after the
- * synchronize_net already.
- * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues.
- */
- set_bit(sta_id, mvm->sta_drained);
- schedule_work(&mvm->sta_drained_wk);
- }
-
-out:
- rcu_read_unlock();
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x
-static const char *iwl_get_agg_tx_status(u16 status)
-{
- switch (status & AGG_TX_STATE_STATUS_MSK) {
- AGG_TX_STATE_(TRANSMITTED);
- AGG_TX_STATE_(UNDERRUN);
- AGG_TX_STATE_(BT_PRIO);
- AGG_TX_STATE_(FEW_BYTES);
- AGG_TX_STATE_(ABORT);
- AGG_TX_STATE_(LAST_SENT_TTL);
- AGG_TX_STATE_(LAST_SENT_TRY_CNT);
- AGG_TX_STATE_(LAST_SENT_BT_KILL);
- AGG_TX_STATE_(SCD_QUERY);
- AGG_TX_STATE_(TEST_BAD_CRC32);
- AGG_TX_STATE_(RESPONSE);
- AGG_TX_STATE_(DUMP_TX);
- AGG_TX_STATE_(DELAY_TX);
- }
-
- return "UNKNOWN";
-}
-
-static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
- struct agg_tx_status *frame_status = &tx_resp->status;
- int i;
-
- for (i = 0; i < tx_resp->frame_count; i++) {
- u16 fstatus = le16_to_cpu(frame_status[i].status);
-
- IWL_DEBUG_TX_REPLY(mvm,
- "status %s (0x%04x), try-count (%d) seq (0x%x)\n",
- iwl_get_agg_tx_status(fstatus),
- fstatus & AGG_TX_STATE_STATUS_MSK,
- (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >>
- AGG_TX_STATE_TRY_CNT_POS,
- le16_to_cpu(frame_status[i].sequence));
- }
-}
-#else
-static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt)
-{}
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
- int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
- int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- struct ieee80211_sta *sta;
-
- if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue))
- return;
-
- if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
- return;
-
- iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt);
-
- rcu_read_lock();
-
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
-
- if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- mvmsta->tid_data[tid].rate_n_flags =
- le32_to_cpu(tx_resp->initial_rate);
- mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc;
- mvmsta->tid_data[tid].tx_time =
- le16_to_cpu(tx_resp->wireless_media_time);
- }
-
- rcu_read_unlock();
-}
-
-void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
-
- if (tx_resp->frame_count == 1)
- iwl_mvm_rx_tx_cmd_single(mvm, pkt);
- else
- iwl_mvm_rx_tx_cmd_agg(mvm, pkt);
-}
-
-static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info,
- struct iwl_mvm_ba_notif *ba_notif,
- struct iwl_mvm_tid_data *tid_data)
-{
- info->flags |= IEEE80211_TX_STAT_AMPDU;
- info->status.ampdu_ack_len = ba_notif->txed_2_done;
- info->status.ampdu_len = ba_notif->txed;
- iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
- info);
- /* TODO: not accounted if the whole A-MPDU failed */
- info->status.tx_time = tid_data->tx_time;
- info->status.status_driver_data[0] =
- (void *)(uintptr_t)tid_data->reduced_tpc;
- info->status.status_driver_data[1] =
- (void *)(uintptr_t)tid_data->rate_n_flags;
-}
-
-void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data;
- struct sk_buff_head reclaimed_skbs;
- struct iwl_mvm_tid_data *tid_data;
- struct ieee80211_sta *sta;
- struct iwl_mvm_sta *mvmsta;
- struct sk_buff *skb;
- int sta_id, tid, freed;
- /* "flow" corresponds to Tx queue */
- u16 scd_flow = le16_to_cpu(ba_notif->scd_flow);
- /* "ssn" is start of block-ack Tx window, corresponds to index
- * (in Tx queue's circular buffer) of first TFD/frame in window */
- u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn);
-
- sta_id = ba_notif->sta_id;
- tid = ba_notif->tid;
-
- if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT ||
- tid >= IWL_MAX_TID_COUNT,
- "sta_id %d tid %d", sta_id, tid))
- return;
-
- rcu_read_lock();
-
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
-
- /* Reclaiming frames for a station that has been deleted ? */
- if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
- rcu_read_unlock();
- return;
- }
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
- tid_data = &mvmsta->tid_data[tid];
-
- if (tid_data->txq_id != scd_flow) {
- IWL_ERR(mvm,
- "invalid BA notification: Q %d, tid %d, flow %d\n",
- tid_data->txq_id, tid, scd_flow);
- rcu_read_unlock();
- return;
- }
-
- spin_lock_bh(&mvmsta->lock);
-
- __skb_queue_head_init(&reclaimed_skbs);
-
- /*
- * Release all TFDs before the SSN, i.e. all TFDs in front of
- * block-ack window (we assume that they've been successfully
- * transmitted ... if not, it's too late anyway).
- */
- iwl_trans_reclaim(mvm->trans, scd_flow, ba_resp_scd_ssn,
- &reclaimed_skbs);
-
- IWL_DEBUG_TX_REPLY(mvm,
- "BA_NOTIFICATION Received from %pM, sta_id = %d\n",
- (u8 *)&ba_notif->sta_addr_lo32,
- ba_notif->sta_id);
- IWL_DEBUG_TX_REPLY(mvm,
- "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
- ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl),
- (unsigned long long)le64_to_cpu(ba_notif->bitmap),
- scd_flow, ba_resp_scd_ssn, ba_notif->txed,
- ba_notif->txed_2_done);
-
- tid_data->next_reclaimed = ba_resp_scd_ssn;
-
- iwl_mvm_check_ratid_empty(mvm, sta, tid);
-
- freed = 0;
-
- skb_queue_walk(&reclaimed_skbs, skb) {
- struct ieee80211_hdr *hdr = (void *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- if (ieee80211_is_data_qos(hdr->frame_control))
- freed++;
- else
- WARN_ON_ONCE(1);
-
- iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
-
- memset(&info->status, 0, sizeof(info->status));
- /* Packet was transmitted successfully, failures come as single
- * frames because before failing a frame the firmware transmits
- * it without aggregation at least once.
- */
- info->flags |= IEEE80211_TX_STAT_ACK;
-
- /* this is the first skb we deliver in this batch */
- /* put the rate scaling data there */
- if (freed == 1)
- iwl_mvm_tx_info_from_ba_notif(info, ba_notif, tid_data);
- }
-
- spin_unlock_bh(&mvmsta->lock);
-
- /* We got a BA notif with 0 acked or scd_ssn didn't progress which is
- * possible (i.e. first MPDU in the aggregation wasn't acked)
- * Still it's important to update RS about sent vs. acked.
- */
- if (skb_queue_empty(&reclaimed_skbs)) {
- struct ieee80211_tx_info ba_info = {};
- struct ieee80211_chanctx_conf *chanctx_conf = NULL;
-
- if (mvmsta->vif)
- chanctx_conf =
- rcu_dereference(mvmsta->vif->chanctx_conf);
-
- if (WARN_ON_ONCE(!chanctx_conf))
- goto out;
-
- ba_info.band = chanctx_conf->def.chan->band;
- iwl_mvm_tx_info_from_ba_notif(&ba_info, ba_notif, tid_data);
-
- IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n");
- iwl_mvm_rs_tx_status(mvm, sta, tid, &ba_info);
- }
-
-out:
- rcu_read_unlock();
-
- while (!skb_queue_empty(&reclaimed_skbs)) {
- skb = __skb_dequeue(&reclaimed_skbs);
- ieee80211_tx_status(mvm->hw, skb);
- }
-}
-
-/*
- * Note that there are transports that buffer frames before they reach
- * the firmware. This means that after flush_tx_path is called, the
- * queue might not be empty. The race-free way to handle this is to:
- * 1) set the station as draining
- * 2) flush the Tx path
- * 3) wait for the transport queues to be empty
- */
-int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags)
-{
- int ret;
- struct iwl_tx_path_flush_cmd flush_cmd = {
- .queues_ctl = cpu_to_le32(tfd_msk),
- .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
- };
-
- ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
- sizeof(flush_cmd), &flush_cmd);
- if (ret)
- IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret);
- return ret;
-}
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
deleted file mode 100644
index ad0f16909e2e..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ /dev/null
@@ -1,1083 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright (C) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <net/mac80211.h>
-
-#include "iwl-debug.h"
-#include "iwl-io.h"
-#include "iwl-prph.h"
-
-#include "mvm.h"
-#include "fw-api-rs.h"
-
-/*
- * Will return 0 even if the cmd failed when RFKILL is asserted unless
- * CMD_WANT_SKB is set in cmd->flags.
- */
-int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
-{
- int ret;
-
-#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
- if (WARN_ON(mvm->d3_test_active))
- return -EIO;
-#endif
-
- /*
- * Synchronous commands from this op-mode must hold
- * the mutex, this ensures we don't try to send two
- * (or more) synchronous commands at a time.
- */
- if (!(cmd->flags & CMD_ASYNC))
- lockdep_assert_held(&mvm->mutex);
-
- ret = iwl_trans_send_cmd(mvm->trans, cmd);
-
- /*
- * If the caller wants the SKB, then don't hide any problems, the
- * caller might access the response buffer which will be NULL if
- * the command failed.
- */
- if (cmd->flags & CMD_WANT_SKB)
- return ret;
-
- /* Silently ignore failures if RFKILL is asserted */
- if (!ret || ret == -ERFKILL)
- return 0;
- return ret;
-}
-
-int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id,
- u32 flags, u16 len, const void *data)
-{
- struct iwl_host_cmd cmd = {
- .id = id,
- .len = { len, },
- .data = { data, },
- .flags = flags,
- };
-
- return iwl_mvm_send_cmd(mvm, &cmd);
-}
-
-/*
- * We assume that the caller set the status to the success value
- */
-int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
- u32 *status)
-{
- struct iwl_rx_packet *pkt;
- struct iwl_cmd_response *resp;
- int ret, resp_len;
-
- lockdep_assert_held(&mvm->mutex);
-
-#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
- if (WARN_ON(mvm->d3_test_active))
- return -EIO;
-#endif
-
- /*
- * Only synchronous commands can wait for status,
- * we use WANT_SKB so the caller can't.
- */
- if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB),
- "cmd flags %x", cmd->flags))
- return -EINVAL;
-
- cmd->flags |= CMD_WANT_SKB;
-
- ret = iwl_trans_send_cmd(mvm->trans, cmd);
- if (ret == -ERFKILL) {
- /*
- * The command failed because of RFKILL, don't update
- * the status, leave it as success and return 0.
- */
- return 0;
- } else if (ret) {
- return ret;
- }
-
- pkt = cmd->resp_pkt;
- /* Can happen if RFKILL is asserted */
- if (!pkt) {
- ret = 0;
- goto out_free_resp;
- }
-
- resp_len = iwl_rx_packet_payload_len(pkt);
- if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
- ret = -EIO;
- goto out_free_resp;
- }
-
- resp = (void *)pkt->data;
- *status = le32_to_cpu(resp->status);
- out_free_resp:
- iwl_free_resp(cmd);
- return ret;
-}
-
-/*
- * We assume that the caller set the status to the sucess value
- */
-int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len,
- const void *data, u32 *status)
-{
- struct iwl_host_cmd cmd = {
- .id = id,
- .len = { len, },
- .data = { data, },
- };
-
- return iwl_mvm_send_cmd_status(mvm, &cmd, status);
-}
-
-#define IWL_DECLARE_RATE_INFO(r) \
- [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
-
-/*
- * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
- */
-static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
- IWL_DECLARE_RATE_INFO(1),
- IWL_DECLARE_RATE_INFO(2),
- IWL_DECLARE_RATE_INFO(5),
- IWL_DECLARE_RATE_INFO(11),
- IWL_DECLARE_RATE_INFO(6),
- IWL_DECLARE_RATE_INFO(9),
- IWL_DECLARE_RATE_INFO(12),
- IWL_DECLARE_RATE_INFO(18),
- IWL_DECLARE_RATE_INFO(24),
- IWL_DECLARE_RATE_INFO(36),
- IWL_DECLARE_RATE_INFO(48),
- IWL_DECLARE_RATE_INFO(54),
-};
-
-int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
- enum ieee80211_band band)
-{
- int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
- int idx;
- int band_offset = 0;
-
- /* Legacy rate format, search for match in table */
- if (band == IEEE80211_BAND_5GHZ)
- band_offset = IWL_FIRST_OFDM_RATE;
- for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
- if (fw_rate_idx_to_plcp[idx] == rate)
- return idx - band_offset;
-
- return -1;
-}
-
-u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx)
-{
- /* Get PLCP rate for tx_cmd->rate_n_flags */
- return fw_rate_idx_to_plcp[rate_idx];
-}
-
-void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_error_resp *err_resp = (void *)pkt->data;
-
- IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n",
- le32_to_cpu(err_resp->error_type), err_resp->cmd_id);
- IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n",
- le16_to_cpu(err_resp->bad_cmd_seq_num),
- le32_to_cpu(err_resp->error_service));
- IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n",
- le64_to_cpu(err_resp->timestamp));
-}
-
-/*
- * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h.
- * The parameter should also be a combination of ANT_[ABC].
- */
-u8 first_antenna(u8 mask)
-{
- BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */
- if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */
- return BIT(0);
- return BIT(ffs(mask) - 1);
-}
-
-/*
- * Toggles between TX antennas to send the probe request on.
- * Receives the bitmask of valid TX antennas and the *index* used
- * for the last TX, and returns the next valid *index* to use.
- * In order to set it in the tx_cmd, must do BIT(idx).
- */
-u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
-{
- u8 ind = last_idx;
- int i;
-
- for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
- ind = (ind + 1) % RATE_MCS_ANT_NUM;
- if (valid & BIT(ind))
- return ind;
- }
-
- WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid);
- return last_idx;
-}
-
-static const struct {
- const char *name;
- u8 num;
-} advanced_lookup[] = {
- { "NMI_INTERRUPT_WDG", 0x34 },
- { "SYSASSERT", 0x35 },
- { "UCODE_VERSION_MISMATCH", 0x37 },
- { "BAD_COMMAND", 0x38 },
- { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
- { "FATAL_ERROR", 0x3D },
- { "NMI_TRM_HW_ERR", 0x46 },
- { "NMI_INTERRUPT_TRM", 0x4C },
- { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
- { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
- { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
- { "NMI_INTERRUPT_HOST", 0x66 },
- { "NMI_INTERRUPT_ACTION_PT", 0x7C },
- { "NMI_INTERRUPT_UNKNOWN", 0x84 },
- { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
- { "ADVANCED_SYSASSERT", 0 },
-};
-
-static const char *desc_lookup(u32 num)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
- if (advanced_lookup[i].num == num)
- return advanced_lookup[i].name;
-
- /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
- return advanced_lookup[i].name;
-}
-
-/*
- * Note: This structure is read from the device with IO accesses,
- * and the reading already does the endian conversion. As it is
- * read with u32-sized accesses, any members with a different size
- * need to be ordered correctly though!
- */
-struct iwl_error_event_table_v1 {
- u32 valid; /* (nonzero) valid, (0) log is empty */
- u32 error_id; /* type of error */
- u32 pc; /* program counter */
- u32 blink1; /* branch link */
- u32 blink2; /* branch link */
- u32 ilink1; /* interrupt link */
- u32 ilink2; /* interrupt link */
- u32 data1; /* error-specific data */
- u32 data2; /* error-specific data */
- u32 data3; /* error-specific data */
- u32 bcon_time; /* beacon timer */
- u32 tsf_low; /* network timestamp function timer */
- u32 tsf_hi; /* network timestamp function timer */
- u32 gp1; /* GP1 timer register */
- u32 gp2; /* GP2 timer register */
- u32 gp3; /* GP3 timer register */
- u32 ucode_ver; /* uCode version */
- u32 hw_ver; /* HW Silicon version */
- u32 brd_ver; /* HW board version */
- u32 log_pc; /* log program counter */
- u32 frame_ptr; /* frame pointer */
- u32 stack_ptr; /* stack pointer */
- u32 hcmd; /* last host command header */
- u32 isr0; /* isr status register LMPM_NIC_ISR0:
- * rxtx_flag */
- u32 isr1; /* isr status register LMPM_NIC_ISR1:
- * host_flag */
- u32 isr2; /* isr status register LMPM_NIC_ISR2:
- * enc_flag */
- u32 isr3; /* isr status register LMPM_NIC_ISR3:
- * time_flag */
- u32 isr4; /* isr status register LMPM_NIC_ISR4:
- * wico interrupt */
- u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
- u32 wait_event; /* wait event() caller address */
- u32 l2p_control; /* L2pControlField */
- u32 l2p_duration; /* L2pDurationField */
- u32 l2p_mhvalid; /* L2pMhValidBits */
- u32 l2p_addr_match; /* L2pAddrMatchStat */
- u32 lmpm_pmg_sel; /* indicate which clocks are turned on
- * (LMPM_PMG_SEL) */
- u32 u_timestamp; /* indicate when the date and time of the
- * compilation */
- u32 flow_handler; /* FH read/write pointers, RX credit */
-} __packed /* LOG_ERROR_TABLE_API_S_VER_1 */;
-
-struct iwl_error_event_table {
- u32 valid; /* (nonzero) valid, (0) log is empty */
- u32 error_id; /* type of error */
- u32 pc; /* program counter */
- u32 blink1; /* branch link */
- u32 blink2; /* branch link */
- u32 ilink1; /* interrupt link */
- u32 ilink2; /* interrupt link */
- u32 data1; /* error-specific data */
- u32 data2; /* error-specific data */
- u32 data3; /* error-specific data */
- u32 bcon_time; /* beacon timer */
- u32 tsf_low; /* network timestamp function timer */
- u32 tsf_hi; /* network timestamp function timer */
- u32 gp1; /* GP1 timer register */
- u32 gp2; /* GP2 timer register */
- u32 gp3; /* GP3 timer register */
- u32 major; /* uCode version major */
- u32 minor; /* uCode version minor */
- u32 hw_ver; /* HW Silicon version */
- u32 brd_ver; /* HW board version */
- u32 log_pc; /* log program counter */
- u32 frame_ptr; /* frame pointer */
- u32 stack_ptr; /* stack pointer */
- u32 hcmd; /* last host command header */
- u32 isr0; /* isr status register LMPM_NIC_ISR0:
- * rxtx_flag */
- u32 isr1; /* isr status register LMPM_NIC_ISR1:
- * host_flag */
- u32 isr2; /* isr status register LMPM_NIC_ISR2:
- * enc_flag */
- u32 isr3; /* isr status register LMPM_NIC_ISR3:
- * time_flag */
- u32 isr4; /* isr status register LMPM_NIC_ISR4:
- * wico interrupt */
- u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
- u32 wait_event; /* wait event() caller address */
- u32 l2p_control; /* L2pControlField */
- u32 l2p_duration; /* L2pDurationField */
- u32 l2p_mhvalid; /* L2pMhValidBits */
- u32 l2p_addr_match; /* L2pAddrMatchStat */
- u32 lmpm_pmg_sel; /* indicate which clocks are turned on
- * (LMPM_PMG_SEL) */
- u32 u_timestamp; /* indicate when the date and time of the
- * compilation */
- u32 flow_handler; /* FH read/write pointers, RX credit */
-} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */;
-
-/*
- * UMAC error struct - relevant starting from family 8000 chip.
- * Note: This structure is read from the device with IO accesses,
- * and the reading already does the endian conversion. As it is
- * read with u32-sized accesses, any members with a different size
- * need to be ordered correctly though!
- */
-struct iwl_umac_error_event_table {
- u32 valid; /* (nonzero) valid, (0) log is empty */
- u32 error_id; /* type of error */
- u32 blink1; /* branch link */
- u32 blink2; /* branch link */
- u32 ilink1; /* interrupt link */
- u32 ilink2; /* interrupt link */
- u32 data1; /* error-specific data */
- u32 data2; /* error-specific data */
- u32 data3; /* error-specific data */
- u32 umac_major;
- u32 umac_minor;
- u32 frame_pointer; /* core register 27*/
- u32 stack_pointer; /* core register 28 */
- u32 cmd_header; /* latest host cmd sent to UMAC */
- u32 nic_isr_pref; /* ISR status register */
-} __packed;
-
-#define ERROR_START_OFFSET (1 * sizeof(u32))
-#define ERROR_ELEM_SIZE (7 * sizeof(u32))
-
-static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
-{
- struct iwl_trans *trans = mvm->trans;
- struct iwl_umac_error_event_table table;
- u32 base;
-
- base = mvm->umac_error_event_table;
-
- if (base < 0x800000) {
- IWL_ERR(mvm,
- "Not valid error log pointer 0x%08X for %s uCode\n",
- base,
- (mvm->cur_ucode == IWL_UCODE_INIT)
- ? "Init" : "RT");
- return;
- }
-
- iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
-
- if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
- IWL_ERR(trans, "Start IWL Error Log Dump:\n");
- IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
- mvm->status, table.valid);
- }
-
- IWL_ERR(mvm, "0x%08X | %s\n", table.error_id,
- desc_lookup(table.error_id));
- IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1);
- IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2);
- IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1);
- IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2);
- IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
- IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
- IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3);
- IWL_ERR(mvm, "0x%08X | umac major\n", table.umac_major);
- IWL_ERR(mvm, "0x%08X | umac minor\n", table.umac_minor);
- IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer);
- IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer);
- IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header);
- IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref);
-}
-
-static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm)
-{
- struct iwl_trans *trans = mvm->trans;
- struct iwl_error_event_table_v1 table;
- u32 base;
-
- base = mvm->error_event_table;
- if (mvm->cur_ucode == IWL_UCODE_INIT) {
- if (!base)
- base = mvm->fw->init_errlog_ptr;
- } else {
- if (!base)
- base = mvm->fw->inst_errlog_ptr;
- }
-
- if (base < 0x800000) {
- IWL_ERR(mvm,
- "Not valid error log pointer 0x%08X for %s uCode\n",
- base,
- (mvm->cur_ucode == IWL_UCODE_INIT)
- ? "Init" : "RT");
- return;
- }
-
- iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
-
- if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
- IWL_ERR(trans, "Start IWL Error Log Dump:\n");
- IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
- mvm->status, table.valid);
- }
-
- /* Do not change this output - scripts rely on it */
-
- IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
-
- trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
- table.data1, table.data2, table.data3,
- table.blink1, table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.ucode_ver, 0,
- table.hw_ver, table.brd_ver);
- IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
- desc_lookup(table.error_id));
- IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
- IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
- IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
- IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
- IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
- IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
- IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
- IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
- IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
- IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
- IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
- IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
- IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
- IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver);
- IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
- IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
- IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
- IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
- IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
- IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
- IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
- IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
- IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
- IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
- IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
- IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
- IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
- IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
- IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
- IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
- IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
-
- if (mvm->support_umac_log)
- iwl_mvm_dump_umac_error_log(mvm);
-}
-
-void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
-{
- struct iwl_trans *trans = mvm->trans;
- struct iwl_error_event_table table;
- u32 base;
-
- if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) {
- iwl_mvm_dump_nic_error_log_old(mvm);
- return;
- }
-
- base = mvm->error_event_table;
- if (mvm->cur_ucode == IWL_UCODE_INIT) {
- if (!base)
- base = mvm->fw->init_errlog_ptr;
- } else {
- if (!base)
- base = mvm->fw->inst_errlog_ptr;
- }
-
- if (base < 0x800000) {
- IWL_ERR(mvm,
- "Not valid error log pointer 0x%08X for %s uCode\n",
- base,
- (mvm->cur_ucode == IWL_UCODE_INIT)
- ? "Init" : "RT");
- return;
- }
-
- iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
-
- if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
- IWL_ERR(trans, "Start IWL Error Log Dump:\n");
- IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
- mvm->status, table.valid);
- }
-
- /* Do not change this output - scripts rely on it */
-
- IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
-
- trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
- table.data1, table.data2, table.data3,
- table.blink1, table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.major,
- table.minor, table.hw_ver, table.brd_ver);
- IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
- desc_lookup(table.error_id));
- IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
- IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
- IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
- IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
- IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
- IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
- IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
- IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
- IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
- IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
- IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
- IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
- IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
- IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major);
- IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor);
- IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
- IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
- IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
- IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
- IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
- IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
- IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
- IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
- IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
- IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
- IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
- IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
- IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
- IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
- IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
- IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
- IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
-
- if (mvm->support_umac_log)
- iwl_mvm_dump_umac_error_log(mvm);
-}
-
-int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq)
-{
- int i;
-
- lockdep_assert_held(&mvm->queue_info_lock);
-
- for (i = minq; i <= maxq; i++)
- if (mvm->queue_info[i].hw_queue_refcount == 0 &&
- !mvm->queue_info[i].setup_reserved)
- return i;
-
- return -ENOSPC;
-}
-
-void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
- u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg,
- unsigned int wdg_timeout)
-{
- bool enable_queue = true;
-
- spin_lock_bh(&mvm->queue_info_lock);
-
- /* Make sure this TID isn't already enabled */
- if (mvm->queue_info[queue].tid_bitmap & BIT(cfg->tid)) {
- spin_unlock_bh(&mvm->queue_info_lock);
- IWL_ERR(mvm, "Trying to enable TXQ with existing TID %d\n",
- cfg->tid);
- return;
- }
-
- /* Update mappings and refcounts */
- mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue);
- mvm->queue_info[queue].hw_queue_refcount++;
- if (mvm->queue_info[queue].hw_queue_refcount > 1)
- enable_queue = false;
- mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid);
-
- IWL_DEBUG_TX_QUEUES(mvm,
- "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n",
- queue, mvm->queue_info[queue].hw_queue_refcount,
- mvm->queue_info[queue].hw_queue_to_mac80211);
-
- spin_unlock_bh(&mvm->queue_info_lock);
-
- /* Send the enabling command if we need to */
- if (enable_queue) {
- struct iwl_scd_txq_cfg_cmd cmd = {
- .scd_queue = queue,
- .enable = 1,
- .window = cfg->frame_limit,
- .sta_id = cfg->sta_id,
- .ssn = cpu_to_le16(ssn),
- .tx_fifo = cfg->fifo,
- .aggregate = cfg->aggregate,
- .tid = cfg->tid,
- };
-
- iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL,
- wdg_timeout);
- WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd),
- &cmd),
- "Failed to configure queue %d on FIFO %d\n", queue,
- cfg->fifo);
- }
-}
-
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
- u8 tid, u8 flags)
-{
- struct iwl_scd_txq_cfg_cmd cmd = {
- .scd_queue = queue,
- .enable = 0,
- };
- bool remove_mac_queue = true;
- int ret;
-
- spin_lock_bh(&mvm->queue_info_lock);
-
- if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) {
- spin_unlock_bh(&mvm->queue_info_lock);
- return;
- }
-
- mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
-
- /*
- * If there is another TID with the same AC - don't remove the MAC queue
- * from the mapping
- */
- if (tid < IWL_MAX_TID_COUNT) {
- unsigned long tid_bitmap =
- mvm->queue_info[queue].tid_bitmap;
- int ac = tid_to_mac80211_ac[tid];
- int i;
-
- for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) {
- if (tid_to_mac80211_ac[i] == ac)
- remove_mac_queue = false;
- }
- }
-
- if (remove_mac_queue)
- mvm->queue_info[queue].hw_queue_to_mac80211 &=
- ~BIT(mac80211_queue);
- mvm->queue_info[queue].hw_queue_refcount--;
-
- cmd.enable = mvm->queue_info[queue].hw_queue_refcount ? 1 : 0;
-
- IWL_DEBUG_TX_QUEUES(mvm,
- "Disabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n",
- queue,
- mvm->queue_info[queue].hw_queue_refcount,
- mvm->queue_info[queue].hw_queue_to_mac80211);
-
- /* If the queue is still enabled - nothing left to do in this func */
- if (cmd.enable) {
- spin_unlock_bh(&mvm->queue_info_lock);
- return;
- }
-
- /* Make sure queue info is correct even though we overwrite it */
- WARN(mvm->queue_info[queue].hw_queue_refcount ||
- mvm->queue_info[queue].tid_bitmap ||
- mvm->queue_info[queue].hw_queue_to_mac80211,
- "TXQ #%d info out-of-sync - refcount=%d, mac map=0x%x, tid=0x%x\n",
- queue, mvm->queue_info[queue].hw_queue_refcount,
- mvm->queue_info[queue].hw_queue_to_mac80211,
- mvm->queue_info[queue].tid_bitmap);
-
- /* If we are here - the queue is freed and we can zero out these vals */
- mvm->queue_info[queue].hw_queue_refcount = 0;
- mvm->queue_info[queue].tid_bitmap = 0;
- mvm->queue_info[queue].hw_queue_to_mac80211 = 0;
-
- spin_unlock_bh(&mvm->queue_info_lock);
-
- iwl_trans_txq_disable(mvm->trans, queue, false);
- ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags,
- sizeof(cmd), &cmd);
- if (ret)
- IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n",
- queue, ret);
-}
-
-/**
- * iwl_mvm_send_lq_cmd() - Send link quality command
- * @init: This command is sent as part of station initialization right
- * after station has been added.
- *
- * The link quality command is sent as the last step of station creation.
- * This is the special case in which init is set and we call a callback in
- * this case to clear the state indicating that station creation is in
- * progress.
- */
-int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init)
-{
- struct iwl_host_cmd cmd = {
- .id = LQ_CMD,
- .len = { sizeof(struct iwl_lq_cmd), },
- .flags = init ? 0 : CMD_ASYNC,
- .data = { lq, },
- };
-
- if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT))
- return -EINVAL;
-
- return iwl_mvm_send_cmd(mvm, &cmd);
-}
-
-/**
- * iwl_mvm_update_smps - Get a request to change the SMPS mode
- * @req_type: The part of the driver who call for a change.
- * @smps_requests: The request to change the SMPS mode.
- *
- * Get a requst to change the SMPS mode,
- * and change it according to all other requests in the driver.
- */
-void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- enum iwl_mvm_smps_type_request req_type,
- enum ieee80211_smps_mode smps_request)
-{
- struct iwl_mvm_vif *mvmvif;
- enum ieee80211_smps_mode smps_mode;
- int i;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */
- if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1)
- return;
-
- if (vif->type == NL80211_IFTYPE_AP)
- smps_mode = IEEE80211_SMPS_OFF;
- else
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
-
- mvmvif = iwl_mvm_vif_from_mac80211(vif);
- mvmvif->smps_requests[req_type] = smps_request;
- for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
- if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) {
- smps_mode = IEEE80211_SMPS_STATIC;
- break;
- }
- if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
- smps_mode = IEEE80211_SMPS_DYNAMIC;
- }
-
- ieee80211_request_smps(vif, smps_mode);
-}
-
-int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear)
-{
- struct iwl_statistics_cmd scmd = {
- .flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0,
- };
- struct iwl_host_cmd cmd = {
- .id = STATISTICS_CMD,
- .len[0] = sizeof(scmd),
- .data[0] = &scmd,
- .flags = CMD_WANT_SKB,
- };
- int ret;
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (ret)
- return ret;
-
- iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt);
- iwl_free_resp(&cmd);
-
- if (clear)
- iwl_mvm_accu_radio_stats(mvm);
-
- return 0;
-}
-
-void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm)
-{
- mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time;
- mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time;
- mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf;
- mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan;
-}
-
-static void iwl_mvm_diversity_iter(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- bool *result = _data;
- int i;
-
- for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
- if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC ||
- mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
- *result = false;
- }
-}
-
-bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm)
-{
- bool result = true;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1)
- return false;
-
- if (mvm->cfg->rx_with_siso_diversity)
- return false;
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_diversity_iter, &result);
-
- return result;
-}
-
-int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool value)
-{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int res;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (mvmvif->low_latency == value)
- return 0;
-
- mvmvif->low_latency = value;
-
- res = iwl_mvm_update_quotas(mvm, false, NULL);
- if (res)
- return res;
-
- iwl_mvm_bt_coex_vif_change(mvm);
-
- return iwl_mvm_power_update_mac(mvm);
-}
-
-static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
-{
- bool *result = _data;
-
- if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif)))
- *result = true;
-}
-
-bool iwl_mvm_low_latency(struct iwl_mvm *mvm)
-{
- bool result = false;
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_ll_iter, &result);
-
- return result;
-}
-
-struct iwl_bss_iter_data {
- struct ieee80211_vif *vif;
- bool error;
-};
-
-static void iwl_mvm_bss_iface_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_bss_iter_data *data = _data;
-
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return;
-
- if (data->vif) {
- data->error = true;
- return;
- }
-
- data->vif = vif;
-}
-
-struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm)
-{
- struct iwl_bss_iter_data bss_iter_data = {};
-
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_bss_iface_iterator, &bss_iter_data);
-
- if (bss_iter_data.error) {
- IWL_ERR(mvm, "More than one managed interface active!\n");
- return ERR_PTR(-EINVAL);
- }
-
- return bss_iter_data.vif;
-}
-
-unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool tdls, bool cmd_q)
-{
- struct iwl_fw_dbg_trigger_tlv *trigger;
- struct iwl_fw_dbg_trigger_txq_timer *txq_timer;
- unsigned int default_timeout =
- cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS))
- return iwlmvm_mod_params.tfd_q_hang_detect ?
- default_timeout : IWL_WATCHDOG_DISABLED;
-
- trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS);
- txq_timer = (void *)trigger->data;
-
- if (tdls)
- return le32_to_cpu(txq_timer->tdls);
-
- if (cmd_q)
- return le32_to_cpu(txq_timer->command_queue);
-
- if (WARN_ON(!vif))
- return default_timeout;
-
- switch (ieee80211_vif_type_p2p(vif)) {
- case NL80211_IFTYPE_ADHOC:
- return le32_to_cpu(txq_timer->ibss);
- case NL80211_IFTYPE_STATION:
- return le32_to_cpu(txq_timer->bss);
- case NL80211_IFTYPE_AP:
- return le32_to_cpu(txq_timer->softap);
- case NL80211_IFTYPE_P2P_CLIENT:
- return le32_to_cpu(txq_timer->p2p_client);
- case NL80211_IFTYPE_P2P_GO:
- return le32_to_cpu(txq_timer->p2p_go);
- case NL80211_IFTYPE_P2P_DEVICE:
- return le32_to_cpu(txq_timer->p2p_device);
- default:
- WARN_ON(1);
- return mvm->cfg->base_params->wd_timeout;
- }
-}
-
-void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- const char *errmsg)
-{
- struct iwl_fw_dbg_trigger_tlv *trig;
- struct iwl_fw_dbg_trigger_mlme *trig_mlme;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
- goto out;
-
- trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
- trig_mlme = (void *)trig->data;
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
- goto out;
-
- if (trig_mlme->stop_connection_loss &&
- --trig_mlme->stop_connection_loss)
- goto out;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg);
-
-out:
- ieee80211_connection_loss(vif);
-}
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
deleted file mode 100644
index 639761fb2bfb..000000000000
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ /dev/null
@@ -1,702 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/pci-aspm.h>
-#include <linux/acpi.h>
-
-#include "iwl-trans.h"
-#include "iwl-drv.h"
-#include "internal.h"
-
-#define IWL_PCI_DEVICE(dev, subdev, cfg) \
- .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
- .subvendor = PCI_ANY_ID, .subdevice = (subdev), \
- .driver_data = (kernel_ulong_t)&(cfg)
-
-/* Hardware specific file defines the PCI IDs table for that hardware module */
-static const struct pci_device_id 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 */
- {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */
-
-/* 5300 Series WiFi */
- {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */
-
-/* 5350 Series WiFi/WiMax */
- {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */
-
-/* 5150 Series Wifi/WiMax */
- {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_abg_cfg)}, /* Half Mini Card */
-
- {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */
-
-/* 6x00 Series */
- {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
- {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
-
-/* 6x05 Series */
- {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */
- {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */
-
-/* 6x30 Series */
- {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)},
-
-/* 6x50 WiFi/WiMax Series */
- {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
-
-/* 6150 WiFi/WiMax Series */
- {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)},
- {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)},
-
-/* 1000 Series WiFi */
- {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)},
-
-/* 100 Series WiFi */
- {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)},
- {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)},
-
-/* 130 Series WiFi */
- {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)},
- {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)},
-
-/* 2x00 Series */
- {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)},
-
-/* 2x30 Series */
- {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)},
-
-/* 6x35 Series */
- {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)},
-
-/* 105 Series */
- {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)},
-
-/* 135 Series */
- {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)
-/* 7260 Series */
- {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
- {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
-
-/* 3160 Series */
- {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)},
-
-/* 3165 Series */
- {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3166, 0x4212, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)},
-
-/* 7265 Series */
- {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)},
-
-/* 8000 Series */
- {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x01F0, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0012, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1012, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0044, iwl8260_2n_cfg)},
- {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)},
-#endif /* CONFIG_IWLMVM */
-
- {0}
-};
-MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
-
-#ifdef CONFIG_ACPI
-#define SPL_METHOD "SPLC"
-#define SPL_DOMAINTYPE_MODULE BIT(0)
-#define SPL_DOMAINTYPE_WIFI BIT(1)
-#define SPL_DOMAINTYPE_WIGIG BIT(2)
-#define SPL_DOMAINTYPE_RFEM BIT(3)
-
-static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
-{
- union acpi_object *limits, *domain_type, *power_limit;
-
- if (splx->type != ACPI_TYPE_PACKAGE ||
- splx->package.count != 2 ||
- splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
- splx->package.elements[0].integer.value != 0) {
- IWL_ERR(trans, "Unsupported splx structure\n");
- return 0;
- }
-
- limits = &splx->package.elements[1];
- if (limits->type != ACPI_TYPE_PACKAGE ||
- limits->package.count < 2 ||
- limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
- limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
- IWL_ERR(trans, "Invalid limits element\n");
- return 0;
- }
-
- domain_type = &limits->package.elements[0];
- power_limit = &limits->package.elements[1];
- if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
- IWL_DEBUG_INFO(trans, "WiFi power is not limited\n");
- return 0;
- }
-
- return power_limit->integer.value;
-}
-
-static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
-{
- acpi_handle pxsx_handle;
- acpi_handle handle;
- struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
- acpi_status status;
-
- pxsx_handle = ACPI_HANDLE(&pdev->dev);
- if (!pxsx_handle) {
- IWL_DEBUG_INFO(trans,
- "Could not retrieve root port ACPI handle\n");
- return;
- }
-
- /* Get the method's handle */
- status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
- if (ACPI_FAILURE(status)) {
- IWL_DEBUG_INFO(trans, "SPL method not found\n");
- return;
- }
-
- /* Call SPLC with no arguments */
- status = acpi_evaluate_object(handle, NULL, NULL, &splx);
- if (ACPI_FAILURE(status)) {
- IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status);
- return;
- }
-
- trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
- IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n",
- trans->dflt_pwr_limit);
- kfree(splx.pointer);
-}
-
-#else /* CONFIG_ACPI */
-static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
-#endif
-
-/* PCI registers */
-#define PCI_CFG_RETRY_TIMEOUT 0x041
-
-static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
- const struct iwl_cfg *cfg_7265d __maybe_unused = NULL;
- struct iwl_trans *iwl_trans;
- struct iwl_trans_pcie *trans_pcie;
- int ret;
-
- iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg);
- if (IS_ERR(iwl_trans))
- return PTR_ERR(iwl_trans);
-
-#if IS_ENABLED(CONFIG_IWLMVM)
- /*
- * special-case 7265D, it has the same PCI IDs.
- *
- * Note that because we already pass the cfg to the transport above,
- * all the parameters that the transport uses must, until that is
- * changed, be identical to the ones in the 7265D configuration.
- */
- if (cfg == &iwl7265_2ac_cfg)
- cfg_7265d = &iwl7265d_2ac_cfg;
- else if (cfg == &iwl7265_2n_cfg)
- cfg_7265d = &iwl7265d_2n_cfg;
- else if (cfg == &iwl7265_n_cfg)
- cfg_7265d = &iwl7265d_n_cfg;
- if (cfg_7265d &&
- (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) {
- cfg = cfg_7265d;
- iwl_trans->cfg = cfg_7265d;
- }
-#endif
-
- pci_set_drvdata(pdev, iwl_trans);
-
- trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
- trans_pcie->drv = iwl_drv_start(iwl_trans, cfg);
-
- if (IS_ERR(trans_pcie->drv)) {
- ret = PTR_ERR(trans_pcie->drv);
- goto out_free_trans;
- }
-
- set_dflt_pwr_limit(iwl_trans, pdev);
-
- /* register transport layer debugfs here */
- ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir);
- if (ret)
- goto out_free_drv;
-
- return 0;
-
-out_free_drv:
- iwl_drv_stop(trans_pcie->drv);
-out_free_trans:
- iwl_trans_pcie_free(iwl_trans);
- return ret;
-}
-
-static void iwl_pci_remove(struct pci_dev *pdev)
-{
- struct iwl_trans *trans = pci_get_drvdata(pdev);
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- iwl_drv_stop(trans_pcie->drv);
- iwl_trans_pcie_free(trans);
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int iwl_pci_suspend(struct device *device)
-{
- /* Before you put code here, think about WoWLAN. You cannot check here
- * whether WoWLAN is enabled or not, and your code will run even if
- * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx.
- */
-
- return 0;
-}
-
-static int iwl_pci_resume(struct device *device)
-{
- struct pci_dev *pdev = to_pci_dev(device);
- struct iwl_trans *trans = pci_get_drvdata(pdev);
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- bool hw_rfkill;
-
- /* Before you put code here, think about WoWLAN. You cannot check here
- * whether WoWLAN is enabled or not, and your code will run even if
- * WoWLAN is enabled - the NIC may be alive.
- */
-
- /*
- * We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state.
- */
- pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
-
- if (!trans->op_mode)
- return 0;
-
- /*
- * Enable rfkill interrupt (in order to keep track of
- * the rfkill status)
- */
- iwl_enable_rfkill_int(trans);
-
- hw_rfkill = iwl_is_rfkill_set(trans);
-
- mutex_lock(&trans_pcie->mutex);
- iwl_trans_pcie_rf_kill(trans, hw_rfkill);
- mutex_unlock(&trans_pcie->mutex);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
-
-#define IWL_PM_OPS (&iwl_dev_pm_ops)
-
-#else
-
-#define IWL_PM_OPS NULL
-
-#endif
-
-static struct pci_driver iwl_pci_driver = {
- .name = DRV_NAME,
- .id_table = iwl_hw_card_ids,
- .probe = iwl_pci_probe,
- .remove = iwl_pci_remove,
- .driver.pm = IWL_PM_OPS,
-};
-
-int __must_check iwl_pci_register_driver(void)
-{
- int ret;
- ret = pci_register_driver(&iwl_pci_driver);
- if (ret)
- pr_err("Unable to initialize PCI module\n");
-
- return ret;
-}
-
-void iwl_pci_unregister_driver(void)
-{
- pci_unregister_driver(&iwl_pci_driver);
-}
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
deleted file mode 100644
index feb2f7e81134..000000000000
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ /dev/null
@@ -1,569 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#ifndef __iwl_trans_int_pcie_h__
-#define __iwl_trans_int_pcie_h__
-
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/skbuff.h>
-#include <linux/wait.h>
-#include <linux/pci.h>
-#include <linux/timer.h>
-
-#include "iwl-fh.h"
-#include "iwl-csr.h"
-#include "iwl-trans.h"
-#include "iwl-debug.h"
-#include "iwl-io.h"
-#include "iwl-op-mode.h"
-
-/* We need 2 entries for the TX command and header, and another one might
- * be needed for potential data in the SKB's head. The remaining ones can
- * be used for frags.
- */
-#define IWL_PCIE_MAX_FRAGS (IWL_NUM_OF_TBS - 3)
-
-/*
- * RX related structures and functions
- */
-#define RX_NUM_QUEUES 1
-#define RX_POST_REQ_ALLOC 2
-#define RX_CLAIM_REQ_ALLOC 8
-#define RX_POOL_SIZE ((RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC) * RX_NUM_QUEUES)
-#define RX_LOW_WATERMARK 8
-
-struct iwl_host_cmd;
-
-/*This file includes the declaration that are internal to the
- * trans_pcie layer */
-
-struct iwl_rx_mem_buffer {
- dma_addr_t page_dma;
- struct page *page;
- struct list_head list;
-};
-
-/**
- * struct isr_statistics - interrupt statistics
- *
- */
-struct isr_statistics {
- u32 hw;
- u32 sw;
- u32 err_code;
- u32 sch;
- u32 alive;
- u32 rfkill;
- u32 ctkill;
- u32 wakeup;
- u32 rx;
- u32 tx;
- u32 unhandled;
-};
-
-/**
- * struct iwl_rxq - Rx queue
- * @bd: driver's pointer to buffer of receive buffer descriptors (rbd)
- * @bd_dma: bus address of buffer of receive buffer descriptors (rbd)
- * @read: Shared index to newest available Rx buffer
- * @write: Shared index to oldest written Rx packet
- * @free_count: Number of pre-allocated buffers in rx_free
- * @used_count: Number of RBDs handled to allocator to use for allocation
- * @write_actual:
- * @rx_free: list of RBDs with allocated RB ready for use
- * @rx_used: list of RBDs with no RB attached
- * @need_update: flag to indicate we need to update read/write index
- * @rb_stts: driver's pointer to receive buffer status
- * @rb_stts_dma: bus address of receive buffer status
- * @lock:
- * @pool: initial pool of iwl_rx_mem_buffer for the queue
- * @queue: actual rx queue
- *
- * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers
- */
-struct iwl_rxq {
- __le32 *bd;
- dma_addr_t bd_dma;
- u32 read;
- u32 write;
- u32 free_count;
- u32 used_count;
- u32 write_actual;
- struct list_head rx_free;
- struct list_head rx_used;
- bool need_update;
- struct iwl_rb_status *rb_stts;
- dma_addr_t rb_stts_dma;
- spinlock_t lock;
- struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE];
- struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE];
-};
-
-/**
- * struct iwl_rb_allocator - Rx allocator
- * @pool: initial pool of allocator
- * @req_pending: number of requests the allcator had not processed yet
- * @req_ready: number of requests honored and ready for claiming
- * @rbd_allocated: RBDs with pages allocated and ready to be handled to
- * the queue. This is a list of &struct iwl_rx_mem_buffer
- * @rbd_empty: RBDs with no page attached for allocator use. This is a list
- * of &struct iwl_rx_mem_buffer
- * @lock: protects the rbd_allocated and rbd_empty lists
- * @alloc_wq: work queue for background calls
- * @rx_alloc: work struct for background calls
- */
-struct iwl_rb_allocator {
- struct iwl_rx_mem_buffer pool[RX_POOL_SIZE];
- atomic_t req_pending;
- atomic_t req_ready;
- struct list_head rbd_allocated;
- struct list_head rbd_empty;
- spinlock_t lock;
- struct workqueue_struct *alloc_wq;
- struct work_struct rx_alloc;
-};
-
-struct iwl_dma_ptr {
- dma_addr_t dma;
- void *addr;
- size_t size;
-};
-
-/**
- * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
- * @index -- current index
- */
-static inline int iwl_queue_inc_wrap(int index)
-{
- return ++index & (TFD_QUEUE_SIZE_MAX - 1);
-}
-
-/**
- * iwl_queue_dec_wrap - decrement queue index, wrap back to end
- * @index -- current index
- */
-static inline int iwl_queue_dec_wrap(int index)
-{
- return --index & (TFD_QUEUE_SIZE_MAX - 1);
-}
-
-struct iwl_cmd_meta {
- /* only for SYNC commands, iff the reply skb is wanted */
- struct iwl_host_cmd *source;
- u32 flags;
-};
-
-/*
- * Generic queue structure
- *
- * Contains common data for Rx and Tx queues.
- *
- * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
- * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
- * there might be HW changes in the future). For the normal TX
- * queues, n_window, which is the size of the software queue data
- * is also 256; however, for the command queue, n_window is only
- * 32 since we don't need so many commands pending. Since the HW
- * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result,
- * the software buffers (in the variables @meta, @txb in struct
- * iwl_txq) only have 32 entries, while the HW buffers (@tfds in
- * the same struct) have 256.
- * This means that we end up with the following:
- * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
- * SW entries: | 0 | ... | 31 |
- * where N is a number between 0 and 7. This means that the SW
- * data is a window overlayed over the HW queue.
- */
-struct iwl_queue {
- int write_ptr; /* 1-st empty entry (index) host_w*/
- int read_ptr; /* last used entry (index) host_r*/
- /* use for monitoring and recovering the stuck queue */
- dma_addr_t dma_addr; /* physical addr for BD's */
- int n_window; /* safe queue window */
- u32 id;
- int low_mark; /* low watermark, resume queue if free
- * space more than this */
- int high_mark; /* high watermark, stop queue if free
- * space less than this */
-};
-
-#define TFD_TX_CMD_SLOTS 256
-#define TFD_CMD_SLOTS 32
-
-/*
- * The FH will write back to the first TB only, so we need
- * to copy some data into the buffer regardless of whether
- * it should be mapped or not. This indicates how big the
- * first TB must be to include the scratch buffer. Since
- * the scratch is 4 bytes at offset 12, it's 16 now. If we
- * make it bigger then allocations will be bigger and copy
- * slower, so that's probably not useful.
- */
-#define IWL_HCMD_SCRATCHBUF_SIZE 16
-
-struct iwl_pcie_txq_entry {
- struct iwl_device_cmd *cmd;
- struct sk_buff *skb;
- /* buffer to free after command completes */
- const void *free_buf;
- struct iwl_cmd_meta meta;
-};
-
-struct iwl_pcie_txq_scratch_buf {
- struct iwl_cmd_header hdr;
- u8 buf[8];
- __le32 scratch;
-};
-
-/**
- * struct iwl_txq - Tx Queue for DMA
- * @q: generic Rx/Tx queue descriptor
- * @tfds: transmit frame descriptors (DMA memory)
- * @scratchbufs: start of command headers, including scratch buffers, for
- * the writeback -- this is DMA memory and an array holding one buffer
- * for each command on the queue
- * @scratchbufs_dma: DMA address for the scratchbufs start
- * @entries: transmit entries (driver state)
- * @lock: queue lock
- * @stuck_timer: timer that fires if queue gets stuck
- * @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
- * @wd_timeout: queue watchdog timeout (jiffies) - per queue
- * @frozen: tx stuck queue timer is frozen
- * @frozen_expiry_remainder: remember how long until the timer fires
- *
- * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
- * descriptors) and required locking structures.
- */
-struct iwl_txq {
- struct iwl_queue q;
- struct iwl_tfd *tfds;
- struct iwl_pcie_txq_scratch_buf *scratchbufs;
- dma_addr_t scratchbufs_dma;
- struct iwl_pcie_txq_entry *entries;
- spinlock_t lock;
- unsigned long frozen_expiry_remainder;
- struct timer_list stuck_timer;
- struct iwl_trans_pcie *trans_pcie;
- bool need_update;
- bool frozen;
- u8 active;
- bool ampdu;
- unsigned long wd_timeout;
-};
-
-static inline dma_addr_t
-iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
-{
- return txq->scratchbufs_dma +
- sizeof(struct iwl_pcie_txq_scratch_buf) * idx;
-}
-
-/**
- * struct iwl_trans_pcie - PCIe transport specific data
- * @rxq: all the RX queue data
- * @rba: allocator for RX replenishing
- * @drv - pointer to iwl_drv
- * @trans: pointer to the generic transport area
- * @scd_base_addr: scheduler sram base address in SRAM
- * @scd_bc_tbls: pointer to the byte count table of the scheduler
- * @kw: keep warm address
- * @pci_dev: basic pci-network driver stuff
- * @hw_base: pci hardware address support
- * @ucode_write_complete: indicates that the ucode has been copied.
- * @ucode_write_waitq: wait queue for uCode load
- * @cmd_queue - command queue number
- * @rx_buf_size_8k: 8 kB RX buffer size
- * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
- * @scd_set_active: should the transport configure the SCD for HCMD queue
- * @wide_cmd_header: true when ucode supports wide command header format
- * @rx_page_order: page order for receive buffer size
- * @reg_lock: protect hw register access
- * @mutex: to protect stop_device / start_fw / start_hw
- * @cmd_in_flight: true when we have a host command in flight
- * @fw_mon_phys: physical address of the buffer for the firmware monitor
- * @fw_mon_page: points to the first page of the buffer for the firmware monitor
- * @fw_mon_size: size of the buffer for the firmware monitor
- */
-struct iwl_trans_pcie {
- struct iwl_rxq rxq;
- struct iwl_rb_allocator rba;
- struct iwl_trans *trans;
- struct iwl_drv *drv;
-
- struct net_device napi_dev;
- struct napi_struct napi;
-
- /* INT ICT Table */
- __le32 *ict_tbl;
- dma_addr_t ict_tbl_dma;
- int ict_index;
- bool use_ict;
- bool is_down;
- struct isr_statistics isr_stats;
-
- spinlock_t irq_lock;
- struct mutex mutex;
- u32 inta_mask;
- u32 scd_base_addr;
- struct iwl_dma_ptr scd_bc_tbls;
- struct iwl_dma_ptr kw;
-
- struct iwl_txq *txq;
- unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
- unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
-
- /* PCI bus related data */
- struct pci_dev *pci_dev;
- void __iomem *hw_base;
-
- bool ucode_write_complete;
- wait_queue_head_t ucode_write_waitq;
- wait_queue_head_t wait_command_queue;
-
- u8 cmd_queue;
- u8 cmd_fifo;
- unsigned int cmd_q_wdg_timeout;
- u8 n_no_reclaim_cmds;
- u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
-
- bool rx_buf_size_8k;
- bool bc_table_dword;
- bool scd_set_active;
- bool wide_cmd_header;
- u32 rx_page_order;
-
- const char *const *command_names;
-
- /*protect hw register */
- spinlock_t reg_lock;
- bool cmd_hold_nic_awake;
- bool ref_cmd_in_flight;
-
- /* protect ref counter */
- spinlock_t ref_lock;
- u32 ref_count;
-
- dma_addr_t fw_mon_phys;
- struct page *fw_mon_page;
- u32 fw_mon_size;
-};
-
-#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
- ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific))
-
-static inline struct iwl_trans *
-iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie)
-{
- return container_of((void *)trans_pcie, struct iwl_trans,
- trans_specific);
-}
-
-/*
- * Convention: trans API functions: iwl_trans_pcie_XXX
- * Other functions: iwl_pcie_XXX
- */
-struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
- const struct pci_device_id *ent,
- const struct iwl_cfg *cfg);
-void iwl_trans_pcie_free(struct iwl_trans *trans);
-
-/*****************************************************
-* RX
-******************************************************/
-int iwl_pcie_rx_init(struct iwl_trans *trans);
-irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id);
-int iwl_pcie_rx_stop(struct iwl_trans *trans);
-void iwl_pcie_rx_free(struct iwl_trans *trans);
-
-/*****************************************************
-* ICT - interrupt handling
-******************************************************/
-irqreturn_t iwl_pcie_isr(int irq, void *data);
-int iwl_pcie_alloc_ict(struct iwl_trans *trans);
-void iwl_pcie_free_ict(struct iwl_trans *trans);
-void iwl_pcie_reset_ict(struct iwl_trans *trans);
-void iwl_pcie_disable_ict(struct iwl_trans *trans);
-
-/*****************************************************
-* TX / HCMD
-******************************************************/
-int iwl_pcie_tx_init(struct iwl_trans *trans);
-void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr);
-int iwl_pcie_tx_stop(struct iwl_trans *trans);
-void iwl_pcie_tx_free(struct iwl_trans *trans);
-void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn,
- const struct iwl_trans_txq_scd_cfg *cfg,
- unsigned int wdg_timeout);
-void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue,
- bool configure_scd);
-int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, int txq_id);
-void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans);
-int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
-void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
- struct iwl_rx_cmd_buffer *rxb);
-void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
- struct sk_buff_head *skbs);
-void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
-
-void iwl_trans_pcie_ref(struct iwl_trans *trans);
-void iwl_trans_pcie_unref(struct iwl_trans *trans);
-
-static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
-{
- struct iwl_tfd_tb *tb = &tfd->tbs[idx];
-
- return le16_to_cpu(tb->hi_n_len) >> 4;
-}
-
-/*****************************************************
-* Error handling
-******************************************************/
-void iwl_pcie_dump_csr(struct iwl_trans *trans);
-
-/*****************************************************
-* Helpers
-******************************************************/
-static inline void iwl_disable_interrupts(struct iwl_trans *trans)
-{
- clear_bit(STATUS_INT_ENABLED, &trans->status);
-
- /* disable interrupts from uCode/NIC to host */
- iwl_write32(trans, CSR_INT_MASK, 0x00000000);
-
- /* acknowledge/clear/reset any interrupts still pending
- * from uCode or flow handler (Rx/Tx DMA) */
- iwl_write32(trans, CSR_INT, 0xffffffff);
- iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff);
- IWL_DEBUG_ISR(trans, "Disabled interrupts\n");
-}
-
-static inline void iwl_enable_interrupts(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- IWL_DEBUG_ISR(trans, "Enabling interrupts\n");
- set_bit(STATUS_INT_ENABLED, &trans->status);
- trans_pcie->inta_mask = CSR_INI_SET_MASK;
- iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
-}
-
-static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n");
- trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL;
- iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
-}
-
-static inline void iwl_wake_queue(struct iwl_trans *trans,
- struct iwl_txq *txq)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) {
- IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id);
- iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id);
- }
-}
-
-static inline void iwl_stop_queue(struct iwl_trans *trans,
- struct iwl_txq *txq)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) {
- iwl_op_mode_queue_full(trans->op_mode, txq->q.id);
- IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id);
- } else
- IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n",
- txq->q.id);
-}
-
-static inline bool iwl_queue_used(const struct iwl_queue *q, int i)
-{
- return q->write_ptr >= q->read_ptr ?
- (i >= q->read_ptr && i < q->write_ptr) :
- !(i < q->read_ptr && i >= q->write_ptr);
-}
-
-static inline u8 get_cmd_index(struct iwl_queue *q, u32 index)
-{
- return index & (q->n_window - 1);
-}
-
-static inline const char *get_cmd_string(struct iwl_trans_pcie *trans_pcie,
- u8 cmd)
-{
- if (!trans_pcie->command_names || !trans_pcie->command_names[cmd])
- return "UNKNOWN";
- return trans_pcie->command_names[cmd];
-}
-
-static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
-{
- return !(iwl_read32(trans, CSR_GP_CNTRL) &
- CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
-}
-
-static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
- u32 reg, u32 mask, u32 value)
-{
- u32 v;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- WARN_ON_ONCE(value & ~mask);
-#endif
-
- v = iwl_read32(trans, reg);
- v &= ~mask;
- v |= value;
- iwl_write32(trans, reg, v);
-}
-
-static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
- u32 reg, u32 mask)
-{
- __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
-}
-
-static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
- u32 reg, u32 mask)
-{
- __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
-}
-
-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state);
-
-#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
deleted file mode 100644
index e06591f625c4..000000000000
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ /dev/null
@@ -1,1548 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/gfp.h>
-
-#include "iwl-prph.h"
-#include "iwl-io.h"
-#include "internal.h"
-#include "iwl-op-mode.h"
-
-/******************************************************************************
- *
- * RX path functions
- *
- ******************************************************************************/
-
-/*
- * Rx theory of operation
- *
- * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
- * each of which point to Receive Buffers to be filled by the NIC. These get
- * used not only for Rx frames, but for any command response or notification
- * from the NIC. The driver and NIC manage the Rx buffers by means
- * of indexes into the circular buffer.
- *
- * Rx Queue Indexes
- * The host/firmware share two index registers for managing the Rx buffers.
- *
- * The READ index maps to the first position that the firmware may be writing
- * to -- the driver can read up to (but not including) this position and get
- * good data.
- * The READ index is managed by the firmware once the card is enabled.
- *
- * The WRITE index maps to the last position the driver has read from -- the
- * position preceding WRITE is the last slot the firmware can place a packet.
- *
- * The queue is empty (no good data) if WRITE = READ - 1, and is full if
- * WRITE = READ.
- *
- * During initialization, the host sets up the READ queue position to the first
- * INDEX position, and WRITE to the last (READ - 1 wrapped)
- *
- * When the firmware places a packet in a buffer, it will advance the READ index
- * and fire the RX interrupt. The driver can then query the READ index and
- * process as many packets as possible, moving the WRITE index forward as it
- * resets the Rx queue buffers with new memory.
- *
- * The management in the driver is as follows:
- * + A list of pre-allocated RBDs is stored in iwl->rxq->rx_free.
- * When the interrupt handler is called, the request is processed.
- * The page is either stolen - transferred to the upper layer
- * or reused - added immediately to the iwl->rxq->rx_free list.
- * + When the page is stolen - the driver updates the matching queue's used
- * count, detaches the RBD and transfers it to the queue used list.
- * When there are two used RBDs - they are transferred to the allocator empty
- * list. Work is then scheduled for the allocator to start allocating
- * eight buffers.
- * When there are another 6 used RBDs - they are transferred to the allocator
- * empty list and the driver tries to claim the pre-allocated buffers and
- * add them to iwl->rxq->rx_free. If it fails - it continues to claim them
- * until ready.
- * When there are 8+ buffers in the free list - either from allocation or from
- * 8 reused unstolen pages - restock is called to update the FW and indexes.
- * + In order to make sure the allocator always has RBDs to use for allocation
- * the allocator has initial pool in the size of num_queues*(8-2) - the
- * maximum missing RBDs per allocation request (request posted with 2
- * empty RBDs, there is no guarantee when the other 6 RBDs are supplied).
- * The queues supplies the recycle of the rest of the RBDs.
- * + A received packet is processed and handed to the kernel network stack,
- * detached from the iwl->rxq. The driver 'processed' index is updated.
- * + If there are no allocated buffers in iwl->rxq->rx_free,
- * the READ INDEX is not incremented and iwl->status(RX_STALLED) is set.
- * If there were enough free buffers and RX_STALLED is set it is cleared.
- *
- *
- * Driver sequence:
- *
- * iwl_rxq_alloc() Allocates rx_free
- * iwl_pcie_rx_replenish() Replenishes rx_free list from rx_used, and calls
- * iwl_pcie_rxq_restock.
- * Used only during initialization.
- * iwl_pcie_rxq_restock() Moves available buffers from rx_free into Rx
- * queue, updates firmware pointers, and updates
- * the WRITE index.
- * iwl_pcie_rx_allocator() Background work for allocating pages.
- *
- * -- enable interrupts --
- * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the
- * READ INDEX, detaching the SKB from the pool.
- * Moves the packet buffer from queue to rx_used.
- * Posts and claims requests to the allocator.
- * Calls iwl_pcie_rxq_restock to refill any empty
- * slots.
- *
- * RBD life-cycle:
- *
- * Init:
- * rxq.pool -> rxq.rx_used -> rxq.rx_free -> rxq.queue
- *
- * Regular Receive interrupt:
- * Page Stolen:
- * rxq.queue -> rxq.rx_used -> allocator.rbd_empty ->
- * allocator.rbd_allocated -> rxq.rx_free -> rxq.queue
- * Page not Stolen:
- * rxq.queue -> rxq.rx_free -> rxq.queue
- * ...
- *
- */
-
-/*
- * iwl_rxq_space - Return number of free slots available in queue.
- */
-static int iwl_rxq_space(const struct iwl_rxq *rxq)
-{
- /* Make sure RX_QUEUE_SIZE is a power of 2 */
- BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1));
-
- /*
- * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity
- * between empty and completely full queues.
- * The following is equivalent to modulo by RX_QUEUE_SIZE and is well
- * defined for negative dividends.
- */
- return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1);
-}
-
-/*
- * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
- */
-static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr)
-{
- return cpu_to_le32((u32)(dma_addr >> 8));
-}
-
-/*
- * iwl_pcie_rx_stop - stops the Rx DMA
- */
-int iwl_pcie_rx_stop(struct iwl_trans *trans)
-{
- iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
- return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG,
- FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
-}
-
-/*
- * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue
- */
-static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- u32 reg;
-
- lockdep_assert_held(&rxq->lock);
-
- /*
- * explicitly wake up the NIC if:
- * 1. shadow registers aren't enabled
- * 2. there is a chance that the NIC is asleep
- */
- if (!trans->cfg->base_params->shadow_reg_enable &&
- test_bit(STATUS_TPOWER_PMI, &trans->status)) {
- reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n",
- reg);
- iwl_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- rxq->need_update = true;
- return;
- }
- }
-
- rxq->write_actual = round_down(rxq->write, 8);
- iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
-}
-
-static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
-
- spin_lock(&rxq->lock);
-
- if (!rxq->need_update)
- goto exit_unlock;
-
- iwl_pcie_rxq_inc_wr_ptr(trans);
- rxq->need_update = false;
-
- exit_unlock:
- spin_unlock(&rxq->lock);
-}
-
-/*
- * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool
- *
- * If there are slots in the RX queue that need to be restocked,
- * and we have free pre-allocated buffers, fill the ranks as much
- * as we can, pulling from rx_free.
- *
- * This moves the 'write' index forward to catch up with 'processed', and
- * also updates the memory address in the firmware to reference the new
- * target buffer.
- */
-static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- struct iwl_rx_mem_buffer *rxb;
-
- /*
- * If the device isn't enabled - not need to try to add buffers...
- * This can happen when we stop the device and still have an interrupt
- * pending. We stop the APM before we sync the interrupts because we
- * have to (see comment there). On the other hand, since the APM is
- * stopped, we cannot access the HW (in particular not prph).
- * So don't try to restock if the APM has been already stopped.
- */
- if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
- return;
-
- spin_lock(&rxq->lock);
- while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) {
- /* The overwritten rxb must be a used one */
- rxb = rxq->queue[rxq->write];
- BUG_ON(rxb && rxb->page);
-
- /* Get next free Rx buffer, remove from free list */
- rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer,
- list);
- list_del(&rxb->list);
-
- /* Point to Rx buffer via next RBD in circular buffer */
- rxq->bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma);
- rxq->queue[rxq->write] = rxb;
- rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
- rxq->free_count--;
- }
- spin_unlock(&rxq->lock);
-
- /* If we've added more space for the firmware to place data, tell it.
- * Increment device's write pointer in multiples of 8. */
- if (rxq->write_actual != (rxq->write & ~0x7)) {
- spin_lock(&rxq->lock);
- iwl_pcie_rxq_inc_wr_ptr(trans);
- spin_unlock(&rxq->lock);
- }
-}
-
-/*
- * iwl_pcie_rx_alloc_page - allocates and returns a page.
- *
- */
-static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans,
- gfp_t priority)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- struct page *page;
- gfp_t gfp_mask = priority;
-
- if (rxq->free_count > RX_LOW_WATERMARK)
- gfp_mask |= __GFP_NOWARN;
-
- if (trans_pcie->rx_page_order > 0)
- gfp_mask |= __GFP_COMP;
-
- /* Alloc a new receive buffer */
- page = alloc_pages(gfp_mask, trans_pcie->rx_page_order);
- if (!page) {
- if (net_ratelimit())
- IWL_DEBUG_INFO(trans, "alloc_pages failed, order: %d\n",
- trans_pcie->rx_page_order);
- /* Issue an error if the hardware has consumed more than half
- * of its free buffer list and we don't have enough
- * pre-allocated buffers.
-` */
- if (rxq->free_count <= RX_LOW_WATERMARK &&
- iwl_rxq_space(rxq) > (RX_QUEUE_SIZE / 2) &&
- net_ratelimit())
- IWL_CRIT(trans,
- "Failed to alloc_pages with GFP_KERNEL. Only %u free buffers remaining.\n",
- rxq->free_count);
- return NULL;
- }
- return page;
-}
-
-/*
- * iwl_pcie_rxq_alloc_rbs - allocate a page for each used RBD
- *
- * A used RBD is an Rx buffer that has been given to the stack. To use it again
- * a page must be allocated and the RBD must point to the page. This function
- * doesn't change the HW pointer but handles the list of pages that is used by
- * iwl_pcie_rxq_restock. The latter function will update the HW to use the newly
- * allocated buffers.
- */
-static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- struct iwl_rx_mem_buffer *rxb;
- struct page *page;
-
- while (1) {
- spin_lock(&rxq->lock);
- if (list_empty(&rxq->rx_used)) {
- spin_unlock(&rxq->lock);
- return;
- }
- spin_unlock(&rxq->lock);
-
- /* Alloc a new receive buffer */
- page = iwl_pcie_rx_alloc_page(trans, priority);
- if (!page)
- return;
-
- spin_lock(&rxq->lock);
-
- if (list_empty(&rxq->rx_used)) {
- spin_unlock(&rxq->lock);
- __free_pages(page, trans_pcie->rx_page_order);
- return;
- }
- rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer,
- list);
- list_del(&rxb->list);
- spin_unlock(&rxq->lock);
-
- BUG_ON(rxb->page);
- rxb->page = page;
- /* Get physical address of the RB */
- rxb->page_dma =
- dma_map_page(trans->dev, page, 0,
- PAGE_SIZE << trans_pcie->rx_page_order,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(trans->dev, rxb->page_dma)) {
- rxb->page = NULL;
- spin_lock(&rxq->lock);
- list_add(&rxb->list, &rxq->rx_used);
- spin_unlock(&rxq->lock);
- __free_pages(page, trans_pcie->rx_page_order);
- return;
- }
- /* dma address must be no more than 36 bits */
- BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
- /* and also 256 byte aligned! */
- BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
-
- spin_lock(&rxq->lock);
-
- list_add_tail(&rxb->list, &rxq->rx_free);
- rxq->free_count++;
-
- spin_unlock(&rxq->lock);
- }
-}
-
-static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- int i;
-
- lockdep_assert_held(&rxq->lock);
-
- for (i = 0; i < RX_QUEUE_SIZE; i++) {
- if (!rxq->pool[i].page)
- continue;
- dma_unmap_page(trans->dev, rxq->pool[i].page_dma,
- PAGE_SIZE << trans_pcie->rx_page_order,
- DMA_FROM_DEVICE);
- __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order);
- rxq->pool[i].page = NULL;
- }
-}
-
-/*
- * iwl_pcie_rx_replenish - Move all used buffers from rx_used to rx_free
- *
- * When moving to rx_free an page is allocated for the slot.
- *
- * Also restock the Rx queue via iwl_pcie_rxq_restock.
- * This is called only during initialization
- */
-static void iwl_pcie_rx_replenish(struct iwl_trans *trans)
-{
- iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL);
-
- iwl_pcie_rxq_restock(trans);
-}
-
-/*
- * iwl_pcie_rx_allocator - Allocates pages in the background for RX queues
- *
- * Allocates for each received request 8 pages
- * Called as a scheduled work item.
- */
-static void iwl_pcie_rx_allocator(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
- struct list_head local_empty;
- int pending = atomic_xchg(&rba->req_pending, 0);
-
- IWL_DEBUG_RX(trans, "Pending allocation requests = %d\n", pending);
-
- /* If we were scheduled - there is at least one request */
- spin_lock(&rba->lock);
- /* swap out the rba->rbd_empty to a local list */
- list_replace_init(&rba->rbd_empty, &local_empty);
- spin_unlock(&rba->lock);
-
- while (pending) {
- int i;
- struct list_head local_allocated;
-
- INIT_LIST_HEAD(&local_allocated);
-
- for (i = 0; i < RX_CLAIM_REQ_ALLOC;) {
- struct iwl_rx_mem_buffer *rxb;
- struct page *page;
-
- /* List should never be empty - each reused RBD is
- * returned to the list, and initial pool covers any
- * possible gap between the time the page is allocated
- * to the time the RBD is added.
- */
- BUG_ON(list_empty(&local_empty));
- /* Get the first rxb from the rbd list */
- rxb = list_first_entry(&local_empty,
- struct iwl_rx_mem_buffer, list);
- BUG_ON(rxb->page);
-
- /* Alloc a new receive buffer */
- page = iwl_pcie_rx_alloc_page(trans, GFP_KERNEL);
- if (!page)
- continue;
- rxb->page = page;
-
- /* Get physical address of the RB */
- rxb->page_dma = dma_map_page(trans->dev, page, 0,
- PAGE_SIZE << trans_pcie->rx_page_order,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(trans->dev, rxb->page_dma)) {
- rxb->page = NULL;
- __free_pages(page, trans_pcie->rx_page_order);
- continue;
- }
- /* dma address must be no more than 36 bits */
- BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
- /* and also 256 byte aligned! */
- BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
-
- /* move the allocated entry to the out list */
- list_move(&rxb->list, &local_allocated);
- i++;
- }
-
- pending--;
- if (!pending) {
- pending = atomic_xchg(&rba->req_pending, 0);
- IWL_DEBUG_RX(trans,
- "Pending allocation requests = %d\n",
- pending);
- }
-
- spin_lock(&rba->lock);
- /* add the allocated rbds to the allocator allocated list */
- list_splice_tail(&local_allocated, &rba->rbd_allocated);
- /* get more empty RBDs for current pending requests */
- list_splice_tail_init(&rba->rbd_empty, &local_empty);
- spin_unlock(&rba->lock);
-
- atomic_inc(&rba->req_ready);
- }
-
- spin_lock(&rba->lock);
- /* return unused rbds to the allocator empty list */
- list_splice_tail(&local_empty, &rba->rbd_empty);
- spin_unlock(&rba->lock);
-}
-
-/*
- * iwl_pcie_rx_allocator_get - Returns the pre-allocated pages
-.*
-.* Called by queue when the queue posted allocation request and
- * has freed 8 RBDs in order to restock itself.
- */
-static int iwl_pcie_rx_allocator_get(struct iwl_trans *trans,
- struct iwl_rx_mem_buffer
- *out[RX_CLAIM_REQ_ALLOC])
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
- int i;
-
- /*
- * atomic_dec_if_positive returns req_ready - 1 for any scenario.
- * If req_ready is 0 atomic_dec_if_positive will return -1 and this
- * function will return -ENOMEM, as there are no ready requests.
- * atomic_dec_if_positive will perofrm the *actual* decrement only if
- * req_ready > 0, i.e. - there are ready requests and the function
- * hands one request to the caller.
- */
- if (atomic_dec_if_positive(&rba->req_ready) < 0)
- return -ENOMEM;
-
- spin_lock(&rba->lock);
- for (i = 0; i < RX_CLAIM_REQ_ALLOC; i++) {
- /* Get next free Rx buffer, remove it from free list */
- out[i] = list_first_entry(&rba->rbd_allocated,
- struct iwl_rx_mem_buffer, list);
- list_del(&out[i]->list);
- }
- spin_unlock(&rba->lock);
-
- return 0;
-}
-
-static void iwl_pcie_rx_allocator_work(struct work_struct *data)
-{
- struct iwl_rb_allocator *rba_p =
- container_of(data, struct iwl_rb_allocator, rx_alloc);
- struct iwl_trans_pcie *trans_pcie =
- container_of(rba_p, struct iwl_trans_pcie, rba);
-
- iwl_pcie_rx_allocator(trans_pcie->trans);
-}
-
-static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
- struct device *dev = trans->dev;
-
- memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq));
-
- spin_lock_init(&rxq->lock);
- spin_lock_init(&rba->lock);
-
- if (WARN_ON(rxq->bd || rxq->rb_stts))
- return -EINVAL;
-
- /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */
- rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
- &rxq->bd_dma, GFP_KERNEL);
- if (!rxq->bd)
- goto err_bd;
-
- /*Allocate the driver's pointer to receive buffer status */
- rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts),
- &rxq->rb_stts_dma, GFP_KERNEL);
- if (!rxq->rb_stts)
- goto err_rb_stts;
-
- return 0;
-
-err_rb_stts:
- dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
- rxq->bd, rxq->bd_dma);
- rxq->bd_dma = 0;
- rxq->bd = NULL;
-err_bd:
- return -ENOMEM;
-}
-
-static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u32 rb_size;
- const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
-
- if (trans_pcie->rx_buf_size_8k)
- rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
- else
- rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
-
- /* Stop Rx DMA */
- iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
- /* reset and flush pointers */
- iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
- iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
- iwl_write_direct32(trans, FH_RSCSR_CHNL0_RDPTR, 0);
-
- /* Reset driver's Rx queue write index */
- iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
-
- /* Tell device where to find RBD circular buffer in DRAM */
- iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG,
- (u32)(rxq->bd_dma >> 8));
-
- /* Tell device where in DRAM to update its Rx status */
- iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG,
- rxq->rb_stts_dma >> 4);
-
- /* Enable Rx DMA
- * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in
- * the credit mechanism in 5000 HW RX FIFO
- * Direct rx interrupts to hosts
- * Rx buffer size 4 or 8k
- * RB timeout 0x10
- * 256 RBDs
- */
- iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG,
- FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
- FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
- FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
- rb_size|
- (RX_RB_TIMEOUT << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
- (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
-
- /* Set interrupt coalescing timer to default (2048 usecs) */
- iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
-
- /* W/A for interrupt coalescing bug in 7260 and 3160 */
- if (trans->cfg->host_interrupt_operation_mode)
- iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE);
-}
-
-static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq)
-{
- int i;
-
- lockdep_assert_held(&rxq->lock);
-
- INIT_LIST_HEAD(&rxq->rx_free);
- INIT_LIST_HEAD(&rxq->rx_used);
- rxq->free_count = 0;
- rxq->used_count = 0;
-
- for (i = 0; i < RX_QUEUE_SIZE; i++)
- list_add(&rxq->pool[i].list, &rxq->rx_used);
-}
-
-static void iwl_pcie_rx_init_rba(struct iwl_rb_allocator *rba)
-{
- int i;
-
- lockdep_assert_held(&rba->lock);
-
- INIT_LIST_HEAD(&rba->rbd_allocated);
- INIT_LIST_HEAD(&rba->rbd_empty);
-
- for (i = 0; i < RX_POOL_SIZE; i++)
- list_add(&rba->pool[i].list, &rba->rbd_empty);
-}
-
-static void iwl_pcie_rx_free_rba(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
- int i;
-
- lockdep_assert_held(&rba->lock);
-
- for (i = 0; i < RX_POOL_SIZE; i++) {
- if (!rba->pool[i].page)
- continue;
- dma_unmap_page(trans->dev, rba->pool[i].page_dma,
- PAGE_SIZE << trans_pcie->rx_page_order,
- DMA_FROM_DEVICE);
- __free_pages(rba->pool[i].page, trans_pcie->rx_page_order);
- rba->pool[i].page = NULL;
- }
-}
-
-int iwl_pcie_rx_init(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
- int i, err;
-
- if (!rxq->bd) {
- err = iwl_pcie_rx_alloc(trans);
- if (err)
- return err;
- }
- if (!rba->alloc_wq)
- rba->alloc_wq = alloc_workqueue("rb_allocator",
- WQ_HIGHPRI | WQ_UNBOUND, 1);
- INIT_WORK(&rba->rx_alloc, iwl_pcie_rx_allocator_work);
-
- spin_lock(&rba->lock);
- atomic_set(&rba->req_pending, 0);
- atomic_set(&rba->req_ready, 0);
- /* free all first - we might be reconfigured for a different size */
- iwl_pcie_rx_free_rba(trans);
- iwl_pcie_rx_init_rba(rba);
- spin_unlock(&rba->lock);
-
- spin_lock(&rxq->lock);
-
- /* free all first - we might be reconfigured for a different size */
- iwl_pcie_rxq_free_rbs(trans);
- iwl_pcie_rx_init_rxb_lists(rxq);
-
- for (i = 0; i < RX_QUEUE_SIZE; i++)
- rxq->queue[i] = NULL;
-
- /* Set us so that we have processed and used all buffers, but have
- * not restocked the Rx queue with fresh buffers */
- rxq->read = rxq->write = 0;
- rxq->write_actual = 0;
- memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
- spin_unlock(&rxq->lock);
-
- iwl_pcie_rx_replenish(trans);
-
- iwl_pcie_rx_hw_init(trans, rxq);
-
- spin_lock(&rxq->lock);
- iwl_pcie_rxq_inc_wr_ptr(trans);
- spin_unlock(&rxq->lock);
-
- return 0;
-}
-
-void iwl_pcie_rx_free(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
-
- /*if rxq->bd is NULL, it means that nothing has been allocated,
- * exit now */
- if (!rxq->bd) {
- IWL_DEBUG_INFO(trans, "Free NULL rx context\n");
- return;
- }
-
- cancel_work_sync(&rba->rx_alloc);
- if (rba->alloc_wq) {
- destroy_workqueue(rba->alloc_wq);
- rba->alloc_wq = NULL;
- }
-
- spin_lock(&rba->lock);
- iwl_pcie_rx_free_rba(trans);
- spin_unlock(&rba->lock);
-
- spin_lock(&rxq->lock);
- iwl_pcie_rxq_free_rbs(trans);
- spin_unlock(&rxq->lock);
-
- dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE,
- rxq->bd, rxq->bd_dma);
- rxq->bd_dma = 0;
- rxq->bd = NULL;
-
- if (rxq->rb_stts)
- dma_free_coherent(trans->dev,
- sizeof(struct iwl_rb_status),
- rxq->rb_stts, rxq->rb_stts_dma);
- else
- IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n");
- rxq->rb_stts_dma = 0;
- rxq->rb_stts = NULL;
-}
-
-/*
- * iwl_pcie_rx_reuse_rbd - Recycle used RBDs
- *
- * Called when a RBD can be reused. The RBD is transferred to the allocator.
- * When there are 2 empty RBDs - a request for allocation is posted
- */
-static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans,
- struct iwl_rx_mem_buffer *rxb,
- struct iwl_rxq *rxq, bool emergency)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
-
- /* Move the RBD to the used list, will be moved to allocator in batches
- * before claiming or posting a request*/
- list_add_tail(&rxb->list, &rxq->rx_used);
-
- if (unlikely(emergency))
- return;
-
- /* Count the allocator owned RBDs */
- rxq->used_count++;
-
- /* If we have RX_POST_REQ_ALLOC new released rx buffers -
- * issue a request for allocator. Modulo RX_CLAIM_REQ_ALLOC is
- * used for the case we failed to claim RX_CLAIM_REQ_ALLOC,
- * after but we still need to post another request.
- */
- if ((rxq->used_count % RX_CLAIM_REQ_ALLOC) == RX_POST_REQ_ALLOC) {
- /* Move the 2 RBDs to the allocator ownership.
- Allocator has another 6 from pool for the request completion*/
- spin_lock(&rba->lock);
- list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty);
- spin_unlock(&rba->lock);
-
- atomic_inc(&rba->req_pending);
- queue_work(rba->alloc_wq, &rba->rx_alloc);
- }
-}
-
-static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
- struct iwl_rx_mem_buffer *rxb,
- bool emergency)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
- bool page_stolen = false;
- int max_len = PAGE_SIZE << trans_pcie->rx_page_order;
- u32 offset = 0;
-
- if (WARN_ON(!rxb))
- return;
-
- dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE);
-
- while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) {
- struct iwl_rx_packet *pkt;
- u16 sequence;
- bool reclaim;
- int index, cmd_index, len;
- struct iwl_rx_cmd_buffer rxcb = {
- ._offset = offset,
- ._rx_page_order = trans_pcie->rx_page_order,
- ._page = rxb->page,
- ._page_stolen = false,
- .truesize = max_len,
- };
-
- pkt = rxb_addr(&rxcb);
-
- if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
- break;
-
- IWL_DEBUG_RX(trans,
- "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n",
- rxcb._offset,
- get_cmd_string(trans_pcie, pkt->hdr.cmd),
- pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence));
-
- len = iwl_rx_packet_len(pkt);
- len += sizeof(u32); /* account for status word */
- trace_iwlwifi_dev_rx(trans->dev, trans, pkt, len);
- trace_iwlwifi_dev_rx_data(trans->dev, trans, pkt, len);
-
- /* Reclaim a command buffer only if this packet is a response
- * to a (driver-originated) command.
- * If the packet (e.g. Rx frame) originated from uCode,
- * there is no command buffer to reclaim.
- * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
- * but apparently a few don't get set; catch them here. */
- reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
- if (reclaim) {
- int i;
-
- for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
- if (trans_pcie->no_reclaim_cmds[i] ==
- pkt->hdr.cmd) {
- reclaim = false;
- break;
- }
- }
- }
-
- sequence = le16_to_cpu(pkt->hdr.sequence);
- index = SEQ_TO_INDEX(sequence);
- cmd_index = get_cmd_index(&txq->q, index);
-
- iwl_op_mode_rx(trans->op_mode, &trans_pcie->napi, &rxcb);
-
- if (reclaim) {
- kzfree(txq->entries[cmd_index].free_buf);
- txq->entries[cmd_index].free_buf = NULL;
- }
-
- /*
- * After here, we should always check rxcb._page_stolen,
- * if it is true then one of the handlers took the page.
- */
-
- if (reclaim) {
- /* Invoke any callbacks, transfer the buffer to caller,
- * and fire off the (possibly) blocking
- * iwl_trans_send_cmd()
- * as we reclaim the driver command queue */
- if (!rxcb._page_stolen)
- iwl_pcie_hcmd_complete(trans, &rxcb);
- else
- IWL_WARN(trans, "Claim null rxb?\n");
- }
-
- page_stolen |= rxcb._page_stolen;
- offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN);
- }
-
- /* page was stolen from us -- free our reference */
- if (page_stolen) {
- __free_pages(rxb->page, trans_pcie->rx_page_order);
- rxb->page = NULL;
- }
-
- /* Reuse the page if possible. For notification packets and
- * SKBs that fail to Rx correctly, add them back into the
- * rx_free list for reuse later. */
- if (rxb->page != NULL) {
- rxb->page_dma =
- dma_map_page(trans->dev, rxb->page, 0,
- PAGE_SIZE << trans_pcie->rx_page_order,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(trans->dev, rxb->page_dma)) {
- /*
- * free the page(s) as well to not break
- * the invariant that the items on the used
- * list have no page(s)
- */
- __free_pages(rxb->page, trans_pcie->rx_page_order);
- rxb->page = NULL;
- iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency);
- } else {
- list_add_tail(&rxb->list, &rxq->rx_free);
- rxq->free_count++;
- }
- } else
- iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency);
-}
-
-/*
- * iwl_pcie_rx_handle - Main entry function for receiving responses from fw
- */
-static void iwl_pcie_rx_handle(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- u32 r, i, j, count = 0;
- bool emergency = false;
-
-restart:
- spin_lock(&rxq->lock);
- /* uCode's read index (stored in shared DRAM) indicates the last Rx
- * buffer that the driver may process (last buffer filled by ucode). */
- r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
- i = rxq->read;
-
- /* Rx interrupt, but nothing sent from uCode */
- if (i == r)
- IWL_DEBUG_RX(trans, "HW = SW = %d\n", r);
-
- while (i != r) {
- struct iwl_rx_mem_buffer *rxb;
-
- if (unlikely(rxq->used_count == RX_QUEUE_SIZE / 2))
- emergency = true;
-
- rxb = rxq->queue[i];
- rxq->queue[i] = NULL;
-
- IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n",
- r, i, rxb);
- iwl_pcie_rx_handle_rb(trans, rxb, emergency);
-
- i = (i + 1) & RX_QUEUE_MASK;
-
- /* If we have RX_CLAIM_REQ_ALLOC released rx buffers -
- * try to claim the pre-allocated buffers from the allocator */
- if (rxq->used_count >= RX_CLAIM_REQ_ALLOC) {
- struct iwl_rb_allocator *rba = &trans_pcie->rba;
- struct iwl_rx_mem_buffer *out[RX_CLAIM_REQ_ALLOC];
-
- if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 &&
- !emergency) {
- /* Add the remaining 6 empty RBDs
- * for allocator use
- */
- spin_lock(&rba->lock);
- list_splice_tail_init(&rxq->rx_used,
- &rba->rbd_empty);
- spin_unlock(&rba->lock);
- }
-
- /* If not ready - continue, will try to reclaim later.
- * No need to reschedule work - allocator exits only on
- * success */
- if (!iwl_pcie_rx_allocator_get(trans, out)) {
- /* If success - then RX_CLAIM_REQ_ALLOC
- * buffers were retrieved and should be added
- * to free list */
- rxq->used_count -= RX_CLAIM_REQ_ALLOC;
- for (j = 0; j < RX_CLAIM_REQ_ALLOC; j++) {
- list_add_tail(&out[j]->list,
- &rxq->rx_free);
- rxq->free_count++;
- }
- }
- }
- if (emergency) {
- count++;
- if (count == 8) {
- count = 0;
- if (rxq->used_count < RX_QUEUE_SIZE / 3)
- emergency = false;
- spin_unlock(&rxq->lock);
- iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC);
- spin_lock(&rxq->lock);
- }
- }
- /* handle restock for three cases, can be all of them at once:
- * - we just pulled buffers from the allocator
- * - we have 8+ unstolen pages accumulated
- * - we are in emergency and allocated buffers
- */
- if (rxq->free_count >= RX_CLAIM_REQ_ALLOC) {
- rxq->read = i;
- spin_unlock(&rxq->lock);
- iwl_pcie_rxq_restock(trans);
- goto restart;
- }
- }
-
- /* Backtrack one entry */
- rxq->read = i;
- spin_unlock(&rxq->lock);
-
- /*
- * handle a case where in emergency there are some unallocated RBDs.
- * those RBDs are in the used list, but are not tracked by the queue's
- * used_count which counts allocator owned RBDs.
- * unallocated emergency RBDs must be allocated on exit, otherwise
- * when called again the function may not be in emergency mode and
- * they will be handed to the allocator with no tracking in the RBD
- * allocator counters, which will lead to them never being claimed back
- * by the queue.
- * by allocating them here, they are now in the queue free list, and
- * will be restocked by the next call of iwl_pcie_rxq_restock.
- */
- if (unlikely(emergency && count))
- iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC);
-
- if (trans_pcie->napi.poll)
- napi_gro_flush(&trans_pcie->napi, false);
-}
-
-/*
- * iwl_pcie_irq_handle_error - called for HW or SW error interrupt from card
- */
-static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int i;
-
- /* W/A for WiFi/WiMAX coex and WiMAX own the RF */
- if (trans->cfg->internal_wimax_coex &&
- !trans->cfg->apmg_not_supported &&
- (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) &
- APMS_CLK_VAL_MRB_FUNC_MODE) ||
- (iwl_read_prph(trans, APMG_PS_CTRL_REG) &
- APMG_PS_CTRL_VAL_RESET_REQ))) {
- clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
- iwl_op_mode_wimax_active(trans->op_mode);
- wake_up(&trans_pcie->wait_command_queue);
- return;
- }
-
- iwl_pcie_dump_csr(trans);
- iwl_dump_fh(trans, NULL);
-
- local_bh_disable();
- /* The STATUS_FW_ERROR bit is set in this function. This must happen
- * before we wake up the command caller, to ensure a proper cleanup. */
- iwl_trans_fw_error(trans);
- local_bh_enable();
-
- for (i = 0; i < trans->cfg->base_params->num_of_queues; i++)
- del_timer(&trans_pcie->txq[i].stuck_timer);
-
- clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
- wake_up(&trans_pcie->wait_command_queue);
-}
-
-static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans)
-{
- u32 inta;
-
- lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock);
-
- trace_iwlwifi_dev_irq(trans->dev);
-
- /* Discover which interrupts are active/pending */
- inta = iwl_read32(trans, CSR_INT);
-
- /* the thread will service interrupts and re-enable them */
- return inta;
-}
-
-/* a device (PCI-E) page is 4096 bytes long */
-#define ICT_SHIFT 12
-#define ICT_SIZE (1 << ICT_SHIFT)
-#define ICT_COUNT (ICT_SIZE / sizeof(u32))
-
-/* interrupt handler using ict table, with this interrupt driver will
- * stop using INTA register to get device's interrupt, reading this register
- * is expensive, device will write interrupts in ICT dram table, increment
- * index then will fire interrupt to driver, driver will OR all ICT table
- * entries from current index up to table entry with 0 value. the result is
- * the interrupt we need to service, driver will set the entries back to 0 and
- * set index.
- */
-static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u32 inta;
- u32 val = 0;
- u32 read;
-
- trace_iwlwifi_dev_irq(trans->dev);
-
- /* Ignore interrupt if there's nothing in NIC to service.
- * This may be due to IRQ shared with another device,
- * or due to sporadic interrupts thrown from our NIC. */
- read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
- trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read);
- if (!read)
- return 0;
-
- /*
- * Collect all entries up to the first 0, starting from ict_index;
- * note we already read at ict_index.
- */
- do {
- val |= read;
- IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n",
- trans_pcie->ict_index, read);
- trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
- trans_pcie->ict_index =
- ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1));
-
- read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
- trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
- read);
- } while (read);
-
- /* We should not get this value, just ignore it. */
- if (val == 0xffffffff)
- val = 0;
-
- /*
- * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
- * (bit 15 before shifting it to 31) to clear when using interrupt
- * coalescing. fortunately, bits 18 and 19 stay set when this happens
- * so we use them to decide on the real state of the Rx bit.
- * In order words, bit 15 is set if bit 18 or bit 19 are set.
- */
- if (val & 0xC0000)
- val |= 0x8000;
-
- inta = (0xff & val) | ((0xff00 & val) << 16);
- return inta;
-}
-
-irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
-{
- struct iwl_trans *trans = dev_id;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
- u32 inta = 0;
- u32 handled = 0;
-
- lock_map_acquire(&trans->sync_cmd_lockdep_map);
-
- spin_lock(&trans_pcie->irq_lock);
-
- /* dram interrupt table not set yet,
- * use legacy interrupt.
- */
- if (likely(trans_pcie->use_ict))
- inta = iwl_pcie_int_cause_ict(trans);
- else
- inta = iwl_pcie_int_cause_non_ict(trans);
-
- if (iwl_have_debug_level(IWL_DL_ISR)) {
- IWL_DEBUG_ISR(trans,
- "ISR inta 0x%08x, enabled 0x%08x(sw), enabled(hw) 0x%08x, fh 0x%08x\n",
- inta, trans_pcie->inta_mask,
- iwl_read32(trans, CSR_INT_MASK),
- iwl_read32(trans, CSR_FH_INT_STATUS));
- if (inta & (~trans_pcie->inta_mask))
- IWL_DEBUG_ISR(trans,
- "We got a masked interrupt (0x%08x)\n",
- inta & (~trans_pcie->inta_mask));
- }
-
- inta &= trans_pcie->inta_mask;
-
- /*
- * Ignore interrupt if there's nothing in NIC to service.
- * This may be due to IRQ shared with another device,
- * or due to sporadic interrupts thrown from our NIC.
- */
- if (unlikely(!inta)) {
- IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
- /*
- * Re-enable interrupts here since we don't
- * have anything to service
- */
- if (test_bit(STATUS_INT_ENABLED, &trans->status))
- iwl_enable_interrupts(trans);
- spin_unlock(&trans_pcie->irq_lock);
- lock_map_release(&trans->sync_cmd_lockdep_map);
- return IRQ_NONE;
- }
-
- if (unlikely(inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
- /*
- * Hardware disappeared. It might have
- * already raised an interrupt.
- */
- IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
- spin_unlock(&trans_pcie->irq_lock);
- goto out;
- }
-
- /* Ack/clear/reset pending uCode interrupts.
- * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
- */
- /* There is a hardware bug in the interrupt mask function that some
- * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if
- * they are disabled in the CSR_INT_MASK register. Furthermore the
- * ICT interrupt handling mechanism has another bug that might cause
- * these unmasked interrupts fail to be detected. We workaround the
- * hardware bugs here by ACKing all the possible interrupts so that
- * interrupt coalescing can still be achieved.
- */
- iwl_write32(trans, CSR_INT, inta | ~trans_pcie->inta_mask);
-
- if (iwl_have_debug_level(IWL_DL_ISR))
- IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
- inta, iwl_read32(trans, CSR_INT_MASK));
-
- spin_unlock(&trans_pcie->irq_lock);
-
- /* Now service all interrupt bits discovered above. */
- if (inta & CSR_INT_BIT_HW_ERR) {
- IWL_ERR(trans, "Hardware error detected. Restarting.\n");
-
- /* Tell the device to stop sending interrupts */
- iwl_disable_interrupts(trans);
-
- isr_stats->hw++;
- iwl_pcie_irq_handle_error(trans);
-
- handled |= CSR_INT_BIT_HW_ERR;
-
- goto out;
- }
-
- 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");
- isr_stats->sch++;
- }
-
- /* Alive notification via Rx interrupt will do the real work */
- if (inta & CSR_INT_BIT_ALIVE) {
- IWL_DEBUG_ISR(trans, "Alive interrupt\n");
- isr_stats->alive++;
- }
- }
-
- /* Safely ignore these bits for debug checks below */
- inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
-
- /* HW RF KILL switch toggled */
- if (inta & CSR_INT_BIT_RF_KILL) {
- bool hw_rfkill;
-
- hw_rfkill = iwl_is_rfkill_set(trans);
- IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
- hw_rfkill ? "disable radio" : "enable radio");
-
- isr_stats->rfkill++;
-
- mutex_lock(&trans_pcie->mutex);
- iwl_trans_pcie_rf_kill(trans, hw_rfkill);
- mutex_unlock(&trans_pcie->mutex);
- if (hw_rfkill) {
- set_bit(STATUS_RFKILL, &trans->status);
- if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
- &trans->status))
- IWL_DEBUG_RF_KILL(trans,
- "Rfkill while SYNC HCMD in flight\n");
- wake_up(&trans_pcie->wait_command_queue);
- } else {
- clear_bit(STATUS_RFKILL, &trans->status);
- }
-
- handled |= CSR_INT_BIT_RF_KILL;
- }
-
- /* Chip got too hot and stopped itself */
- if (inta & CSR_INT_BIT_CT_KILL) {
- IWL_ERR(trans, "Microcode CT kill error detected.\n");
- isr_stats->ctkill++;
- handled |= CSR_INT_BIT_CT_KILL;
- }
-
- /* Error detected by uCode */
- if (inta & CSR_INT_BIT_SW_ERR) {
- IWL_ERR(trans, "Microcode SW error detected. "
- " Restarting 0x%X.\n", inta);
- isr_stats->sw++;
- iwl_pcie_irq_handle_error(trans);
- handled |= CSR_INT_BIT_SW_ERR;
- }
-
- /* uCode wakes up after power-down sleep */
- if (inta & CSR_INT_BIT_WAKEUP) {
- IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
- iwl_pcie_rxq_check_wrptr(trans);
- iwl_pcie_txq_check_wrptrs(trans);
-
- isr_stats->wakeup++;
-
- handled |= CSR_INT_BIT_WAKEUP;
- }
-
- /* All uCode command responses, including Tx command responses,
- * Rx "responses" (frame-received notification), and other
- * notifications from uCode come through here*/
- if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX |
- CSR_INT_BIT_RX_PERIODIC)) {
- IWL_DEBUG_ISR(trans, "Rx interrupt\n");
- if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
- handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
- iwl_write32(trans, CSR_FH_INT_STATUS,
- CSR_FH_INT_RX_MASK);
- }
- if (inta & CSR_INT_BIT_RX_PERIODIC) {
- handled |= CSR_INT_BIT_RX_PERIODIC;
- iwl_write32(trans,
- CSR_INT, CSR_INT_BIT_RX_PERIODIC);
- }
- /* Sending RX interrupt require many steps to be done in the
- * the device:
- * 1- write interrupt to current index in ICT table.
- * 2- dma RX frame.
- * 3- update RX shared data to indicate last write index.
- * 4- send interrupt.
- * This could lead to RX race, driver could receive RX interrupt
- * but the shared data changes does not reflect this;
- * periodic interrupt will detect any dangling Rx activity.
- */
-
- /* Disable periodic interrupt; we use it as just a one-shot. */
- iwl_write8(trans, CSR_INT_PERIODIC_REG,
- CSR_INT_PERIODIC_DIS);
-
- /*
- * Enable periodic interrupt in 8 msec only if we received
- * real RX interrupt (instead of just periodic int), to catch
- * any dangling Rx interrupt. If it was just the periodic
- * interrupt, there was no dangling Rx activity, and no need
- * to extend the periodic interrupt; one-shot is enough.
- */
- if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
- iwl_write8(trans, CSR_INT_PERIODIC_REG,
- CSR_INT_PERIODIC_ENA);
-
- isr_stats->rx++;
-
- local_bh_disable();
- iwl_pcie_rx_handle(trans);
- local_bh_enable();
- }
-
- /* This "Tx" DMA channel is used only for loading uCode */
- if (inta & CSR_INT_BIT_FH_TX) {
- iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK);
- IWL_DEBUG_ISR(trans, "uCode load interrupt\n");
- isr_stats->tx++;
- handled |= CSR_INT_BIT_FH_TX;
- /* Wake up uCode load routine, now that load is complete */
- trans_pcie->ucode_write_complete = true;
- wake_up(&trans_pcie->ucode_write_waitq);
- }
-
- if (inta & ~handled) {
- IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled);
- isr_stats->unhandled++;
- }
-
- if (inta & ~(trans_pcie->inta_mask)) {
- IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n",
- inta & ~trans_pcie->inta_mask);
- }
-
- /* Re-enable all interrupts */
- /* only Re-enable if disabled by irq */
- if (test_bit(STATUS_INT_ENABLED, &trans->status))
- iwl_enable_interrupts(trans);
- /* Re-enable RF_KILL if it occurred */
- else if (handled & CSR_INT_BIT_RF_KILL)
- iwl_enable_rfkill_int(trans);
-
-out:
- lock_map_release(&trans->sync_cmd_lockdep_map);
- return IRQ_HANDLED;
-}
-
-/******************************************************************************
- *
- * ICT functions
- *
- ******************************************************************************/
-
-/* Free dram table */
-void iwl_pcie_free_ict(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (trans_pcie->ict_tbl) {
- dma_free_coherent(trans->dev, ICT_SIZE,
- trans_pcie->ict_tbl,
- trans_pcie->ict_tbl_dma);
- trans_pcie->ict_tbl = NULL;
- trans_pcie->ict_tbl_dma = 0;
- }
-}
-
-/*
- * allocate dram shared table, it is an aligned memory
- * block of ICT_SIZE.
- * also reset all data related to ICT table interrupt.
- */
-int iwl_pcie_alloc_ict(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- trans_pcie->ict_tbl =
- dma_zalloc_coherent(trans->dev, ICT_SIZE,
- &trans_pcie->ict_tbl_dma,
- GFP_KERNEL);
- if (!trans_pcie->ict_tbl)
- return -ENOMEM;
-
- /* just an API sanity check ... it is guaranteed to be aligned */
- if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) {
- iwl_pcie_free_ict(trans);
- return -EINVAL;
- }
-
- IWL_DEBUG_ISR(trans, "ict dma addr %Lx ict vir addr %p\n",
- (unsigned long long)trans_pcie->ict_tbl_dma,
- trans_pcie->ict_tbl);
-
- return 0;
-}
-
-/* Device is going up inform it about using ICT interrupt table,
- * also we need to tell the driver to start using ICT interrupt.
- */
-void iwl_pcie_reset_ict(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u32 val;
-
- if (!trans_pcie->ict_tbl)
- return;
-
- spin_lock(&trans_pcie->irq_lock);
- iwl_disable_interrupts(trans);
-
- memset(trans_pcie->ict_tbl, 0, ICT_SIZE);
-
- val = trans_pcie->ict_tbl_dma >> ICT_SHIFT;
-
- val |= CSR_DRAM_INT_TBL_ENABLE |
- CSR_DRAM_INIT_TBL_WRAP_CHECK |
- CSR_DRAM_INIT_TBL_WRITE_POINTER;
-
- IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val);
-
- iwl_write32(trans, CSR_DRAM_INT_TBL_REG, val);
- trans_pcie->use_ict = true;
- trans_pcie->ict_index = 0;
- iwl_write32(trans, CSR_INT, trans_pcie->inta_mask);
- iwl_enable_interrupts(trans);
- spin_unlock(&trans_pcie->irq_lock);
-}
-
-/* Device is going down disable ict interrupt usage */
-void iwl_pcie_disable_ict(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- spin_lock(&trans_pcie->irq_lock);
- trans_pcie->use_ict = false;
- spin_unlock(&trans_pcie->irq_lock);
-}
-
-irqreturn_t iwl_pcie_isr(int irq, void *data)
-{
- struct iwl_trans *trans = data;
-
- if (!trans)
- return IRQ_NONE;
-
- /* Disable (but don't clear!) interrupts here to avoid
- * back-to-back ISRs and sporadic interrupts from our NIC.
- * If we have something to service, the tasklet will re-enable ints.
- * If we *don't* have something, we'll re-enable before leaving here.
- */
- iwl_write32(trans, CSR_INT_MASK, 0x00000000);
-
- return IRQ_WAKE_THREAD;
-}
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
deleted file mode 100644
index 90283453073c..000000000000
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ /dev/null
@@ -1,2825 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include <linux/pci.h>
-#include <linux/pci-aspm.h>
-#include <linux/interrupt.h>
-#include <linux/debugfs.h>
-#include <linux/sched.h>
-#include <linux/bitops.h>
-#include <linux/gfp.h>
-#include <linux/vmalloc.h>
-
-#include "iwl-drv.h"
-#include "iwl-trans.h"
-#include "iwl-csr.h"
-#include "iwl-prph.h"
-#include "iwl-scd.h"
-#include "iwl-agn-hw.h"
-#include "iwl-fw-error-dump.h"
-#include "internal.h"
-#include "iwl-fh.h"
-
-/* extended range in FW SRAM */
-#define IWL_FW_MEM_EXTENDED_START 0x40000
-#define IWL_FW_MEM_EXTENDED_END 0x57FFF
-
-static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (!trans_pcie->fw_mon_page)
- return;
-
- dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys,
- trans_pcie->fw_mon_size, DMA_FROM_DEVICE);
- __free_pages(trans_pcie->fw_mon_page,
- get_order(trans_pcie->fw_mon_size));
- trans_pcie->fw_mon_page = NULL;
- trans_pcie->fw_mon_phys = 0;
- trans_pcie->fw_mon_size = 0;
-}
-
-static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct page *page = NULL;
- dma_addr_t phys;
- u32 size = 0;
- u8 power;
-
- if (!max_power) {
- /* default max_power is maximum */
- max_power = 26;
- } else {
- max_power += 11;
- }
-
- if (WARN(max_power > 26,
- "External buffer size for monitor is too big %d, check the FW TLV\n",
- max_power))
- return;
-
- if (trans_pcie->fw_mon_page) {
- dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys,
- trans_pcie->fw_mon_size,
- DMA_FROM_DEVICE);
- return;
- }
-
- phys = 0;
- for (power = max_power; power >= 11; power--) {
- int order;
-
- size = BIT(power);
- order = get_order(size);
- page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO,
- order);
- if (!page)
- continue;
-
- phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(trans->dev, phys)) {
- __free_pages(page, order);
- page = NULL;
- continue;
- }
- IWL_INFO(trans,
- "Allocated 0x%08x bytes (order %d) for firmware monitor.\n",
- size, order);
- break;
- }
-
- if (WARN_ON_ONCE(!page))
- return;
-
- if (power != max_power)
- IWL_ERR(trans,
- "Sorry - debug buffer is only %luK while you requested %luK\n",
- (unsigned long)BIT(power - 10),
- (unsigned long)BIT(max_power - 10));
-
- trans_pcie->fw_mon_page = page;
- trans_pcie->fw_mon_phys = phys;
- trans_pcie->fw_mon_size = size;
-}
-
-static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
-{
- iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
- ((reg & 0x0000ffff) | (2 << 28)));
- return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG);
-}
-
-static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val)
-{
- iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val);
- iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
- ((reg & 0x0000ffff) | (3 << 28)));
-}
-
-static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
-{
- if (trans->cfg->apmg_not_supported)
- return;
-
- if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
- iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
- ~APMG_PS_CTRL_MSK_PWR_SRC);
- else
- iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
- ~APMG_PS_CTRL_MSK_PWR_SRC);
-}
-
-/* PCI registers */
-#define PCI_CFG_RETRY_TIMEOUT 0x041
-
-static void iwl_pcie_apm_config(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u16 lctl;
- u16 cap;
-
- /*
- * HW bug W/A for instability in PCIe bus L0S->L1 transition.
- * Check if BIOS (or OS) enabled L1-ASPM on this device.
- * If so (likely), disable L0S, so device moves directly L0->L1;
- * costs negligible amount of power savings.
- * If not (unlikely), enable L0S, so there is at least some
- * power savings, even without L1.
- */
- pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl);
- if (lctl & PCI_EXP_LNKCTL_ASPM_L1)
- iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- else
- iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
-
- pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap);
- trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
- dev_info(trans->dev, "L1 %sabled - LTR %sabled\n",
- (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
- trans->ltr_enabled ? "En" : "Dis");
-}
-
-/*
- * Start up NIC's basic functionality after it has been reset
- * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop())
- * NOTE: This does not load uCode nor start the embedded processor
- */
-static int iwl_pcie_apm_init(struct iwl_trans *trans)
-{
- int ret = 0;
- IWL_DEBUG_INFO(trans, "Init card's basic functions\n");
-
- /*
- * Use "set_bit" below rather than "write", to preserve any hardware
- * bits already set by default after reset.
- */
-
- /* Disable L0S exit timer (platform NMI Work/Around) */
- if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
- iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
-
- /*
- * Disable L0s without affecting L1;
- * don't wait for ICH L0s (ICH bug W/A)
- */
- iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
-
- /* Set FH wait threshold to maximum (HW error during stress W/A) */
- iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
-
- /*
- * Enable HAP INTA (interrupt from management bus) to
- * wake device's PCI Express link L1a -> L0s
- */
- iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
-
- iwl_pcie_apm_config(trans);
-
- /* Configure analog phase-lock-loop before activating to D0A */
- if (trans->cfg->base_params->pll_cfg_val)
- iwl_set_bit(trans, CSR_ANA_PLL_CFG,
- trans->cfg->base_params->pll_cfg_val);
-
- /*
- * Set "initialization complete" bit to move adapter from
- * D0U* --> D0A* (powered-up active) state.
- */
- iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /*
- * Wait for clock stabilization; once stabilized, access to
- * device-internal resources is supported, e.g. iwl_write_prph()
- * and accesses to uCode SRAM.
- */
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (ret < 0) {
- IWL_DEBUG_INFO(trans, "Failed to init the card\n");
- goto out;
- }
-
- if (trans->cfg->host_interrupt_operation_mode) {
- /*
- * This is a bit of an abuse - This is needed for 7260 / 3160
- * only check host_interrupt_operation_mode even if this is
- * not related to host_interrupt_operation_mode.
- *
- * Enable the oscillator to count wake up time for L1 exit. This
- * consumes slightly more power (100uA) - but allows to be sure
- * that we wake up from L1 on time.
- *
- * This looks weird: read twice the same register, discard the
- * value, set a bit, and yet again, read that same register
- * just to discard the value. But that's the way the hardware
- * seems to like it.
- */
- iwl_read_prph(trans, OSC_CLK);
- iwl_read_prph(trans, OSC_CLK);
- iwl_set_bits_prph(trans, OSC_CLK, OSC_CLK_FORCE_CONTROL);
- iwl_read_prph(trans, OSC_CLK);
- iwl_read_prph(trans, OSC_CLK);
- }
-
- /*
- * Enable DMA clock and wait for it to stabilize.
- *
- * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0"
- * bits do not disable clocks. This preserves any hardware
- * bits already set by default in "CLK_CTRL_REG" after reset.
- */
- if (!trans->cfg->apmg_not_supported) {
- iwl_write_prph(trans, APMG_CLK_EN_REG,
- APMG_CLK_VAL_DMA_CLK_RQT);
- udelay(20);
-
- /* Disable L1-Active */
- iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-
- /* Clear the interrupt in APMG if the NIC is in RFKILL */
- iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
- APMG_RTC_INT_STT_RFKILL);
- }
-
- set_bit(STATUS_DEVICE_ENABLED, &trans->status);
-
-out:
- return ret;
-}
-
-/*
- * Enable LP XTAL to avoid HW bug where device may consume much power if
- * FW is not loaded after device reset. LP XTAL is disabled by default
- * after device HW reset. Do it only if XTAL is fed by internal source.
- * Configure device's "persistence" mode to avoid resetting XTAL again when
- * SHRD_HW_RST occurs in S3.
- */
-static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
-{
- int ret;
- u32 apmg_gp1_reg;
- u32 apmg_xtal_cfg_reg;
- u32 dl_cfg_reg;
-
- /* Force XTAL ON */
- __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
-
- /* Reset entire device - do controller reset (results in SHRD_HW_RST) */
- iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
-
- /*
- * Set "initialization complete" bit to move adapter from
- * D0U* --> D0A* (powered-up active) state.
- */
- iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /*
- * Wait for clock stabilization; once stabilized, access to
- * device-internal resources is possible.
- */
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- 25000);
- if (WARN_ON(ret < 0)) {
- IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n");
- /* Release XTAL ON request */
- __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
- return;
- }
-
- /*
- * Clear "disable persistence" to avoid LP XTAL resetting when
- * SHRD_HW_RST is applied in S3.
- */
- iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_PERSIST_DIS);
-
- /*
- * Force APMG XTAL to be active to prevent its disabling by HW
- * caused by APMG idle state.
- */
- apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans,
- SHR_APMG_XTAL_CFG_REG);
- iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG,
- apmg_xtal_cfg_reg |
- SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
-
- /*
- * Reset entire device again - do controller reset (results in
- * SHRD_HW_RST). Turn MAC off before proceeding.
- */
- iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
-
- /* Enable LP XTAL by indirect access through CSR */
- apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG);
- iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg |
- SHR_APMG_GP1_WF_XTAL_LP_EN |
- SHR_APMG_GP1_CHICKEN_BIT_SELECT);
-
- /* Clear delay line clock power up */
- dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG);
- iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg &
- ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP);
-
- /*
- * Enable persistence mode to avoid LP XTAL resetting when
- * SHRD_HW_RST is applied in S3.
- */
- iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
-
- /*
- * Clear "initialization complete" bit to move adapter from
- * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
- */
- iwl_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /* Activates XTAL resources monitor */
- __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG,
- CSR_MONITOR_XTAL_RESOURCES);
-
- /* Release XTAL ON request */
- __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
- udelay(10);
-
- /* Release APMG XTAL */
- iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG,
- apmg_xtal_cfg_reg &
- ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
-}
-
-static int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
-{
- int ret = 0;
-
- /* stop device's busmaster DMA activity */
- iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
-
- ret = iwl_poll_bit(trans, CSR_RESET,
- CSR_RESET_REG_FLAG_MASTER_DISABLED,
- CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
- if (ret < 0)
- IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n");
-
- IWL_DEBUG_INFO(trans, "stop master\n");
-
- return ret;
-}
-
-static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
-{
- IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
-
- if (op_mode_leave) {
- if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
- iwl_pcie_apm_init(trans);
-
- /* inform ME that we are leaving */
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000)
- iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_WAKE_ME);
- else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
- iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
- CSR_RESET_LINK_PWR_MGMT_DISABLED);
- iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_PREPARE |
- CSR_HW_IF_CONFIG_REG_ENABLE_PME);
- mdelay(1);
- iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
- CSR_RESET_LINK_PWR_MGMT_DISABLED);
- }
- mdelay(5);
- }
-
- clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
-
- /* Stop device's DMA activity */
- iwl_pcie_apm_stop_master(trans);
-
- if (trans->cfg->lp_xtal_workaround) {
- iwl_pcie_apm_lp_xtal_enable(trans);
- return;
- }
-
- /* Reset the entire device */
- iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
-
- /*
- * Clear "initialization complete" bit to move adapter from
- * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
- */
- iwl_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-}
-
-static int iwl_pcie_nic_init(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- /* nic_init */
- spin_lock(&trans_pcie->irq_lock);
- iwl_pcie_apm_init(trans);
-
- spin_unlock(&trans_pcie->irq_lock);
-
- iwl_pcie_set_pwr(trans, false);
-
- iwl_op_mode_nic_config(trans->op_mode);
-
- /* Allocate the RX queue, or reset if it is already allocated */
- iwl_pcie_rx_init(trans);
-
- /* Allocate or reset and init all Tx and Command queues */
- if (iwl_pcie_tx_init(trans))
- return -ENOMEM;
-
- if (trans->cfg->base_params->shadow_reg_enable) {
- /* enable shadow regs in HW */
- iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF);
- IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n");
- }
-
- return 0;
-}
-
-#define HW_READY_TIMEOUT (50)
-
-/* Note: returns poll_bit return value, which is >= 0 if success */
-static int iwl_pcie_set_hw_ready(struct iwl_trans *trans)
-{
- int ret;
-
- iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
-
- /* See if we got it */
- ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
- CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
- HW_READY_TIMEOUT);
-
- if (ret >= 0)
- iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE);
-
- IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : "");
- return ret;
-}
-
-/* Note: returns standard 0/-ERROR code */
-static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
-{
- int ret;
- int t = 0;
- int iter;
-
- IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n");
-
- ret = iwl_pcie_set_hw_ready(trans);
- /* If the card is ready, exit 0 */
- if (ret >= 0)
- return 0;
-
- iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
- CSR_RESET_LINK_PWR_MGMT_DISABLED);
- msleep(1);
-
- for (iter = 0; iter < 10; iter++) {
- /* If HW is not ready, prepare the conditions to check again */
- iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_PREPARE);
-
- do {
- ret = iwl_pcie_set_hw_ready(trans);
- if (ret >= 0)
- return 0;
-
- usleep_range(200, 1000);
- t += 200;
- } while (t < 150000);
- msleep(25);
- }
-
- IWL_ERR(trans, "Couldn't prepare the card\n");
-
- return ret;
-}
-
-/*
- * ucode
- */
-static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr,
- dma_addr_t phy_addr, u32 byte_cnt)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int ret;
-
- trans_pcie->ucode_write_complete = false;
-
- iwl_write_direct32(trans,
- FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
-
- iwl_write_direct32(trans,
- FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL),
- dst_addr);
-
- iwl_write_direct32(trans,
- FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
- phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
-
- iwl_write_direct32(trans,
- FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
- (iwl_get_dma_hi_addr(phy_addr)
- << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
-
- iwl_write_direct32(trans,
- FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL),
- 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
- 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
- FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
-
- iwl_write_direct32(trans,
- FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
-
- ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
- trans_pcie->ucode_write_complete, 5 * HZ);
- if (!ret) {
- IWL_ERR(trans, "Failed to load firmware chunk!\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
- const struct fw_desc *section)
-{
- u8 *v_addr;
- dma_addr_t p_addr;
- u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len);
- int ret = 0;
-
- IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
- section_num);
-
- v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr,
- GFP_KERNEL | __GFP_NOWARN);
- if (!v_addr) {
- IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n");
- chunk_sz = PAGE_SIZE;
- v_addr = dma_alloc_coherent(trans->dev, chunk_sz,
- &p_addr, GFP_KERNEL);
- if (!v_addr)
- return -ENOMEM;
- }
-
- for (offset = 0; offset < section->len; offset += chunk_sz) {
- u32 copy_size, dst_addr;
- bool extended_addr = false;
-
- copy_size = min_t(u32, chunk_sz, section->len - offset);
- dst_addr = section->offset + offset;
-
- if (dst_addr >= IWL_FW_MEM_EXTENDED_START &&
- dst_addr <= IWL_FW_MEM_EXTENDED_END)
- extended_addr = true;
-
- if (extended_addr)
- iwl_set_bits_prph(trans, LMPM_CHICK,
- LMPM_CHICK_EXTENDED_ADDR_SPACE);
-
- memcpy(v_addr, (u8 *)section->data + offset, copy_size);
- ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr,
- copy_size);
-
- if (extended_addr)
- iwl_clear_bits_prph(trans, LMPM_CHICK,
- LMPM_CHICK_EXTENDED_ADDR_SPACE);
-
- if (ret) {
- IWL_ERR(trans,
- "Could not load the [%d] uCode section\n",
- section_num);
- break;
- }
- }
-
- dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr);
- return ret;
-}
-
-/*
- * Driver Takes the ownership on secure machine before FW load
- * and prevent race with the BT load.
- * W/A for ROM bug. (should be remove in the next Si step)
- */
-static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans)
-{
- u32 val, loop = 1000;
-
- /*
- * Check the RSA semaphore is accessible.
- * If the HW isn't locked and the rsa semaphore isn't accessible,
- * we are in trouble.
- */
- val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0);
- if (val & (BIT(1) | BIT(17))) {
- IWL_INFO(trans,
- "can't access the RSA semaphore it is write protected\n");
- return 0;
- }
-
- /* take ownership on the AUX IF */
- iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK);
- iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK);
-
- do {
- iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1);
- val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS);
- if (val == 0x1) {
- iwl_write_prph(trans, RSA_ENABLE, 0);
- return 0;
- }
-
- udelay(10);
- loop--;
- } while (loop > 0);
-
- IWL_ERR(trans, "Failed to take ownership on secure machine\n");
- return -EIO;
-}
-
-static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans,
- const struct fw_img *image,
- int cpu,
- int *first_ucode_section)
-{
- int shift_param;
- int i, ret = 0, sec_num = 0x1;
- u32 val, last_read_idx = 0;
-
- if (cpu == 1) {
- shift_param = 0;
- *first_ucode_section = 0;
- } else {
- shift_param = 16;
- (*first_ucode_section)++;
- }
-
- for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
- last_read_idx = i;
-
- /*
- * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
- * CPU1 to CPU2.
- * PAGING_SEPARATOR_SECTION delimiter - separate between
- * CPU2 non paged to CPU2 paging sec.
- */
- if (!image->sec[i].data ||
- image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION ||
- image->sec[i].offset == PAGING_SEPARATOR_SECTION) {
- IWL_DEBUG_FW(trans,
- "Break since Data not valid or Empty section, sec = %d\n",
- i);
- break;
- }
-
- ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
- if (ret)
- return ret;
-
- /* Notify the ucode of the loaded section number and status */
- val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS);
- val = val | (sec_num << shift_param);
- iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val);
- sec_num = (sec_num << 1) | 0x1;
- }
-
- *first_ucode_section = last_read_idx;
-
- if (cpu == 1)
- iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFF);
- else
- iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
-
- return 0;
-}
-
-static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
- const struct fw_img *image,
- int cpu,
- int *first_ucode_section)
-{
- int shift_param;
- int i, ret = 0;
- u32 last_read_idx = 0;
-
- if (cpu == 1) {
- shift_param = 0;
- *first_ucode_section = 0;
- } else {
- shift_param = 16;
- (*first_ucode_section)++;
- }
-
- for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
- last_read_idx = i;
-
- /*
- * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
- * CPU1 to CPU2.
- * PAGING_SEPARATOR_SECTION delimiter - separate between
- * CPU2 non paged to CPU2 paging sec.
- */
- if (!image->sec[i].data ||
- image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION ||
- image->sec[i].offset == PAGING_SEPARATOR_SECTION) {
- IWL_DEBUG_FW(trans,
- "Break since Data not valid or Empty section, sec = %d\n",
- i);
- break;
- }
-
- ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
- if (ret)
- return ret;
- }
-
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
- iwl_set_bits_prph(trans,
- CSR_UCODE_LOAD_STATUS_ADDR,
- (LMPM_CPU_UCODE_LOADING_COMPLETED |
- LMPM_CPU_HDRS_LOADING_COMPLETED |
- LMPM_CPU_UCODE_LOADING_STARTED) <<
- shift_param);
-
- *first_ucode_section = last_read_idx;
-
- return 0;
-}
-
-static void iwl_pcie_apply_destination(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
- int i;
-
- if (dest->version)
- IWL_ERR(trans,
- "DBG DEST version is %d - expect issues\n",
- dest->version);
-
- IWL_INFO(trans, "Applying debug destination %s\n",
- get_fw_dbg_mode_string(dest->monitor_mode));
-
- if (dest->monitor_mode == EXTERNAL_MODE)
- iwl_pcie_alloc_fw_monitor(trans, dest->size_power);
- else
- IWL_WARN(trans, "PCI should have external buffer debug\n");
-
- for (i = 0; i < trans->dbg_dest_reg_num; i++) {
- u32 addr = le32_to_cpu(dest->reg_ops[i].addr);
- u32 val = le32_to_cpu(dest->reg_ops[i].val);
-
- switch (dest->reg_ops[i].op) {
- case CSR_ASSIGN:
- iwl_write32(trans, addr, val);
- break;
- case CSR_SETBIT:
- iwl_set_bit(trans, addr, BIT(val));
- break;
- case CSR_CLEARBIT:
- iwl_clear_bit(trans, addr, BIT(val));
- break;
- case PRPH_ASSIGN:
- iwl_write_prph(trans, addr, val);
- break;
- case PRPH_SETBIT:
- iwl_set_bits_prph(trans, addr, BIT(val));
- break;
- case PRPH_CLEARBIT:
- iwl_clear_bits_prph(trans, addr, BIT(val));
- break;
- case PRPH_BLOCKBIT:
- if (iwl_read_prph(trans, addr) & BIT(val)) {
- IWL_ERR(trans,
- "BIT(%u) in address 0x%x is 1, stopping FW configuration\n",
- val, addr);
- goto monitor;
- }
- break;
- default:
- IWL_ERR(trans, "FW debug - unknown OP %d\n",
- dest->reg_ops[i].op);
- break;
- }
- }
-
-monitor:
- if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
- iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
- trans_pcie->fw_mon_phys >> dest->base_shift);
- iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
- (trans_pcie->fw_mon_phys +
- trans_pcie->fw_mon_size) >> dest->end_shift);
- }
-}
-
-static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
- const struct fw_img *image)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int ret = 0;
- int first_ucode_section;
-
- IWL_DEBUG_FW(trans, "working with %s CPU\n",
- image->is_dual_cpus ? "Dual" : "Single");
-
- /* load to FW the binary non secured sections of CPU1 */
- ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section);
- if (ret)
- return ret;
-
- if (image->is_dual_cpus) {
- /* set CPU2 header address */
- iwl_write_prph(trans,
- LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR,
- LMPM_SECURE_CPU2_HDR_MEM_SPACE);
-
- /* load to FW the binary sections of CPU2 */
- ret = iwl_pcie_load_cpu_sections(trans, image, 2,
- &first_ucode_section);
- if (ret)
- return ret;
- }
-
- /* supported for 7000 only for the moment */
- if (iwlwifi_mod_params.fw_monitor &&
- trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
- iwl_pcie_alloc_fw_monitor(trans, 0);
-
- if (trans_pcie->fw_mon_size) {
- iwl_write_prph(trans, MON_BUFF_BASE_ADDR,
- trans_pcie->fw_mon_phys >> 4);
- iwl_write_prph(trans, MON_BUFF_END_ADDR,
- (trans_pcie->fw_mon_phys +
- trans_pcie->fw_mon_size) >> 4);
- }
- } else if (trans->dbg_dest_tlv) {
- iwl_pcie_apply_destination(trans);
- }
-
- /* release CPU reset */
- iwl_write32(trans, CSR_RESET, 0);
-
- return 0;
-}
-
-static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
- const struct fw_img *image)
-{
- int ret = 0;
- int first_ucode_section;
-
- IWL_DEBUG_FW(trans, "working with %s CPU\n",
- image->is_dual_cpus ? "Dual" : "Single");
-
- if (trans->dbg_dest_tlv)
- iwl_pcie_apply_destination(trans);
-
- /* TODO: remove in the next Si step */
- ret = iwl_pcie_rsa_race_bug_wa(trans);
- if (ret)
- return ret;
-
- /* configure the ucode to be ready to get the secured image */
- /* release CPU reset */
- iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
-
- /* load to FW the binary Secured sections of CPU1 */
- ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1,
- &first_ucode_section);
- if (ret)
- return ret;
-
- /* load to FW the binary sections of CPU2 */
- return iwl_pcie_load_cpu_sections_8000(trans, image, 2,
- &first_ucode_section);
-}
-
-static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
- const struct fw_img *fw, bool run_in_rfkill)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- bool hw_rfkill;
- int ret;
-
- mutex_lock(&trans_pcie->mutex);
-
- /* Someone called stop_device, don't try to start_fw */
- if (trans_pcie->is_down) {
- IWL_WARN(trans,
- "Can't start_fw since the HW hasn't been started\n");
- ret = EIO;
- goto out;
- }
-
- /* This may fail if AMT took ownership of the device */
- if (iwl_pcie_prepare_card_hw(trans)) {
- IWL_WARN(trans, "Exit HW not ready\n");
- ret = -EIO;
- goto out;
- }
-
- iwl_enable_rfkill_int(trans);
-
- /* If platform's RF_KILL switch is NOT set to KILL */
- hw_rfkill = iwl_is_rfkill_set(trans);
- if (hw_rfkill)
- set_bit(STATUS_RFKILL, &trans->status);
- else
- clear_bit(STATUS_RFKILL, &trans->status);
- iwl_trans_pcie_rf_kill(trans, hw_rfkill);
- if (hw_rfkill && !run_in_rfkill) {
- ret = -ERFKILL;
- goto out;
- }
-
- iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
-
- ret = iwl_pcie_nic_init(trans);
- if (ret) {
- IWL_ERR(trans, "Unable to init nic\n");
- goto out;
- }
-
- /* make sure rfkill handshake bits are cleared */
- iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
- iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-
- /* clear (again), then enable host interrupts */
- iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
- iwl_enable_interrupts(trans);
-
- /* really make sure rfkill handshake bits are cleared */
- iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
- iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
-
- /* Load the given image to the HW */
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
- ret = iwl_pcie_load_given_ucode_8000(trans, fw);
- else
- ret = iwl_pcie_load_given_ucode(trans, fw);
-
-out:
- mutex_unlock(&trans_pcie->mutex);
- return ret;
-}
-
-static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
-{
- iwl_pcie_reset_ict(trans);
- iwl_pcie_tx_start(trans, scd_addr);
-}
-
-static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- bool hw_rfkill, was_hw_rfkill;
-
- lockdep_assert_held(&trans_pcie->mutex);
-
- if (trans_pcie->is_down)
- return;
-
- trans_pcie->is_down = true;
-
- was_hw_rfkill = iwl_is_rfkill_set(trans);
-
- /* tell the device to stop sending interrupts */
- spin_lock(&trans_pcie->irq_lock);
- iwl_disable_interrupts(trans);
- spin_unlock(&trans_pcie->irq_lock);
-
- /* device going down, Stop using ICT table */
- iwl_pcie_disable_ict(trans);
-
- /*
- * If a HW restart happens during firmware loading,
- * then the firmware loading might call this function
- * and later it might be called again due to the
- * restart. So don't process again if the device is
- * already dead.
- */
- if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
- IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
- iwl_pcie_tx_stop(trans);
- iwl_pcie_rx_stop(trans);
-
- /* Power-down device's busmaster DMA clocks */
- if (!trans->cfg->apmg_not_supported) {
- iwl_write_prph(trans, APMG_CLK_DIS_REG,
- APMG_CLK_VAL_DMA_CLK_RQT);
- udelay(5);
- }
- }
-
- /* Make sure (redundant) we've released our request to stay awake */
- iwl_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-
- /* Stop the device, and put it in low power state */
- iwl_pcie_apm_stop(trans, false);
-
- /* stop and reset the on-board processor */
- iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
- udelay(20);
-
- /*
- * Upon stop, the APM issues an interrupt if HW RF kill is set.
- * This is a bug in certain verions of the hardware.
- * Certain devices also keep sending HW RF kill interrupt all
- * the time, unless the interrupt is ACKed even if the interrupt
- * should be masked. Re-ACK all the interrupts here.
- */
- spin_lock(&trans_pcie->irq_lock);
- iwl_disable_interrupts(trans);
- spin_unlock(&trans_pcie->irq_lock);
-
-
- /* clear all status bits */
- clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
- clear_bit(STATUS_INT_ENABLED, &trans->status);
- clear_bit(STATUS_TPOWER_PMI, &trans->status);
- clear_bit(STATUS_RFKILL, &trans->status);
-
- /*
- * Even if we stop the HW, we still want the RF kill
- * interrupt
- */
- iwl_enable_rfkill_int(trans);
-
- /*
- * Check again since the RF kill state may have changed while
- * all the interrupts were disabled, in this case we couldn't
- * receive the RF kill interrupt and update the state in the
- * op_mode.
- * Don't call the op_mode if the rkfill state hasn't changed.
- * This allows the op_mode to call stop_device from the rfkill
- * notification without endless recursion. Under very rare
- * circumstances, we might have a small recursion if the rfkill
- * state changed exactly now while we were called from stop_device.
- * This is very unlikely but can happen and is supported.
- */
- hw_rfkill = iwl_is_rfkill_set(trans);
- if (hw_rfkill)
- set_bit(STATUS_RFKILL, &trans->status);
- else
- clear_bit(STATUS_RFKILL, &trans->status);
- if (hw_rfkill != was_hw_rfkill)
- iwl_trans_pcie_rf_kill(trans, hw_rfkill);
-
- /* re-take ownership to prevent other users from stealing the deivce */
- iwl_pcie_prepare_card_hw(trans);
-}
-
-static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- mutex_lock(&trans_pcie->mutex);
- _iwl_trans_pcie_stop_device(trans, low_power);
- mutex_unlock(&trans_pcie->mutex);
-}
-
-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
-{
- struct iwl_trans_pcie __maybe_unused *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
-
- lockdep_assert_held(&trans_pcie->mutex);
-
- if (iwl_op_mode_hw_rf_kill(trans->op_mode, state))
- _iwl_trans_pcie_stop_device(trans, true);
-}
-
-static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (trans->wowlan_d0i3) {
- /* Enable persistence mode to avoid reset */
- iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
- }
-
- iwl_disable_interrupts(trans);
-
- /*
- * in testing mode, the host stays awake and the
- * hardware won't be reset (not even partially)
- */
- if (test)
- return;
-
- iwl_pcie_disable_ict(trans);
-
- synchronize_irq(trans_pcie->pci_dev->irq);
-
- iwl_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- iwl_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- if (!trans->wowlan_d0i3) {
- /*
- * reset TX queues -- some of their registers reset during S3
- * so if we don't reset everything here the D3 image would try
- * to execute some invalid memory upon resume
- */
- iwl_trans_pcie_tx_reset(trans);
- }
-
- iwl_pcie_set_pwr(trans, true);
-}
-
-static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
- enum iwl_d3_status *status,
- bool test)
-{
- u32 val;
- int ret;
-
- if (test) {
- iwl_enable_interrupts(trans);
- *status = IWL_D3_STATUS_ALIVE;
- return 0;
- }
-
- /*
- * Also enables interrupts - none will happen as the device doesn't
- * know we're waking it up, only when the opmode actually tells it
- * after this call.
- */
- iwl_pcie_reset_ict(trans);
-
- iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
- udelay(2);
-
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- 25000);
- if (ret < 0) {
- IWL_ERR(trans, "Failed to resume the device (mac ready)\n");
- return ret;
- }
-
- iwl_pcie_set_pwr(trans, false);
-
- if (trans->wowlan_d0i3) {
- iwl_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- } else {
- iwl_trans_pcie_tx_reset(trans);
-
- ret = iwl_pcie_rx_init(trans);
- if (ret) {
- IWL_ERR(trans,
- "Failed to resume the device (RX reset)\n");
- return ret;
- }
- }
-
- val = iwl_read32(trans, CSR_RESET);
- if (val & CSR_RESET_REG_FLAG_NEVO_RESET)
- *status = IWL_D3_STATUS_RESET;
- else
- *status = IWL_D3_STATUS_ALIVE;
-
- return 0;
-}
-
-static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- bool hw_rfkill;
- int err;
-
- lockdep_assert_held(&trans_pcie->mutex);
-
- err = iwl_pcie_prepare_card_hw(trans);
- if (err) {
- IWL_ERR(trans, "Error while preparing HW: %d\n", err);
- return err;
- }
-
- /* Reset the entire device */
- iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- usleep_range(10, 15);
-
- iwl_pcie_apm_init(trans);
-
- /* From now on, the op_mode will be kept updated about RF kill state */
- iwl_enable_rfkill_int(trans);
-
- /* Set is_down to false here so that...*/
- trans_pcie->is_down = false;
-
- hw_rfkill = iwl_is_rfkill_set(trans);
- if (hw_rfkill)
- set_bit(STATUS_RFKILL, &trans->status);
- else
- clear_bit(STATUS_RFKILL, &trans->status);
- /* ... rfkill can call stop_device and set it false if needed */
- iwl_trans_pcie_rf_kill(trans, hw_rfkill);
-
- return 0;
-}
-
-static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int ret;
-
- mutex_lock(&trans_pcie->mutex);
- ret = _iwl_trans_pcie_start_hw(trans, low_power);
- mutex_unlock(&trans_pcie->mutex);
-
- return ret;
-}
-
-static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- mutex_lock(&trans_pcie->mutex);
-
- /* disable interrupts - don't enable HW RF kill interrupt */
- spin_lock(&trans_pcie->irq_lock);
- iwl_disable_interrupts(trans);
- spin_unlock(&trans_pcie->irq_lock);
-
- iwl_pcie_apm_stop(trans, true);
-
- spin_lock(&trans_pcie->irq_lock);
- iwl_disable_interrupts(trans);
- spin_unlock(&trans_pcie->irq_lock);
-
- iwl_pcie_disable_ict(trans);
-
- mutex_unlock(&trans_pcie->mutex);
-
- synchronize_irq(trans_pcie->pci_dev->irq);
-}
-
-static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
-{
- writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
-}
-
-static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val)
-{
- writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
-}
-
-static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
-{
- return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
-}
-
-static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg)
-{
- iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR,
- ((reg & 0x000FFFFF) | (3 << 24)));
- return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT);
-}
-
-static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr,
- u32 val)
-{
- iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR,
- ((addr & 0x000FFFFF) | (3 << 24)));
- iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
-}
-
-static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget)
-{
- WARN_ON(1);
- return 0;
-}
-
-static void iwl_trans_pcie_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- trans_pcie->cmd_queue = trans_cfg->cmd_queue;
- trans_pcie->cmd_fifo = trans_cfg->cmd_fifo;
- trans_pcie->cmd_q_wdg_timeout = trans_cfg->cmd_q_wdg_timeout;
- if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS))
- trans_pcie->n_no_reclaim_cmds = 0;
- else
- trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds;
- if (trans_pcie->n_no_reclaim_cmds)
- memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
- trans_pcie->n_no_reclaim_cmds * sizeof(u8));
-
- trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k;
- if (trans_pcie->rx_buf_size_8k)
- trans_pcie->rx_page_order = get_order(8 * 1024);
- else
- trans_pcie->rx_page_order = get_order(4 * 1024);
-
- trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header;
- trans_pcie->command_names = trans_cfg->command_names;
- trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
- trans_pcie->scd_set_active = trans_cfg->scd_set_active;
-
- /* init ref_count to 1 (should be cleared when ucode is loaded) */
- trans_pcie->ref_count = 1;
-
- /* Initialize NAPI here - it should be before registering to mac80211
- * in the opmode but after the HW struct is allocated.
- * As this function may be called again in some corner cases don't
- * do anything if NAPI was already initialized.
- */
- if (!trans_pcie->napi.poll) {
- init_dummy_netdev(&trans_pcie->napi_dev);
- netif_napi_add(&trans_pcie->napi_dev, &trans_pcie->napi,
- iwl_pcie_dummy_napi_poll, 64);
- }
-}
-
-void iwl_trans_pcie_free(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- synchronize_irq(trans_pcie->pci_dev->irq);
-
- iwl_pcie_tx_free(trans);
- iwl_pcie_rx_free(trans);
-
- free_irq(trans_pcie->pci_dev->irq, trans);
- iwl_pcie_free_ict(trans);
-
- pci_disable_msi(trans_pcie->pci_dev);
- iounmap(trans_pcie->hw_base);
- pci_release_regions(trans_pcie->pci_dev);
- pci_disable_device(trans_pcie->pci_dev);
-
- if (trans_pcie->napi.poll)
- netif_napi_del(&trans_pcie->napi);
-
- iwl_pcie_free_fw_monitor(trans);
-
- iwl_trans_free(trans);
-}
-
-static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
-{
- if (state)
- set_bit(STATUS_TPOWER_PMI, &trans->status);
- else
- clear_bit(STATUS_TPOWER_PMI, &trans->status);
-}
-
-static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
- unsigned long *flags)
-{
- int ret;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- spin_lock_irqsave(&trans_pcie->reg_lock, *flags);
-
- if (trans_pcie->cmd_hold_nic_awake)
- goto out;
-
- /* this bit wakes up the NIC */
- __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
- udelay(2);
-
- /*
- * These bits say the device is running, and should keep running for
- * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
- * but they do not indicate that embedded SRAM is restored yet;
- * 3945 and 4965 have volatile SRAM, and must save/restore contents
- * to/from host DRAM when sleeping/waking for power-saving.
- * Each direction takes approximately 1/4 millisecond; with this
- * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
- * series of register accesses are expected (e.g. reading Event Log),
- * to keep device from sleeping.
- *
- * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
- * SRAM is okay/restored. We don't check that here because this call
- * is just for hardware register access; but GP1 MAC_SLEEP check is a
- * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
- *
- * 5000 series and later (including 1000 series) have non-volatile SRAM,
- * and do not save/restore SRAM when power cycling.
- */
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
- (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
- CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
- if (unlikely(ret < 0)) {
- iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
- if (!silent) {
- u32 val = iwl_read32(trans, CSR_GP_CNTRL);
- WARN_ONCE(1,
- "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
- val);
- spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
- return false;
- }
- }
-
-out:
- /*
- * Fool sparse by faking we release the lock - sparse will
- * track nic_access anyway.
- */
- __release(&trans_pcie->reg_lock);
- return true;
-}
-
-static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
- unsigned long *flags)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- lockdep_assert_held(&trans_pcie->reg_lock);
-
- /*
- * Fool sparse by faking we acquiring the lock - sparse will
- * track nic_access anyway.
- */
- __acquire(&trans_pcie->reg_lock);
-
- if (trans_pcie->cmd_hold_nic_awake)
- goto out;
-
- __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- /*
- * Above we read the CSR_GP_CNTRL register, which will flush
- * any previous writes, but we need the write that clears the
- * MAC_ACCESS_REQ bit to be performed before any other writes
- * scheduled on different CPUs (after we drop reg_lock).
- */
- mmiowb();
-out:
- spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
-}
-
-static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
- void *buf, int dwords)
-{
- unsigned long flags;
- int offs, ret = 0;
- u32 *vals = buf;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr);
- for (offs = 0; offs < dwords; offs++)
- vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
- iwl_trans_release_nic_access(trans, &flags);
- } else {
- ret = -EBUSY;
- }
- return ret;
-}
-
-static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
- const void *buf, int dwords)
-{
- unsigned long flags;
- int offs, ret = 0;
- const u32 *vals = buf;
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
- for (offs = 0; offs < dwords; offs++)
- iwl_write32(trans, HBUS_TARG_MEM_WDAT,
- vals ? vals[offs] : 0);
- iwl_trans_release_nic_access(trans, &flags);
- } else {
- ret = -EBUSY;
- }
- return ret;
-}
-
-static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans,
- unsigned long txqs,
- bool freeze)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int queue;
-
- for_each_set_bit(queue, &txqs, BITS_PER_LONG) {
- struct iwl_txq *txq = &trans_pcie->txq[queue];
- unsigned long now;
-
- spin_lock_bh(&txq->lock);
-
- now = jiffies;
-
- if (txq->frozen == freeze)
- goto next_queue;
-
- IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n",
- freeze ? "Freezing" : "Waking", queue);
-
- txq->frozen = freeze;
-
- if (txq->q.read_ptr == txq->q.write_ptr)
- goto next_queue;
-
- if (freeze) {
- if (unlikely(time_after(now,
- txq->stuck_timer.expires))) {
- /*
- * The timer should have fired, maybe it is
- * spinning right now on the lock.
- */
- goto next_queue;
- }
- /* remember how long until the timer fires */
- txq->frozen_expiry_remainder =
- txq->stuck_timer.expires - now;
- del_timer(&txq->stuck_timer);
- goto next_queue;
- }
-
- /*
- * Wake a non-empty queue -> arm timer with the
- * remainder before it froze
- */
- mod_timer(&txq->stuck_timer,
- now + txq->frozen_expiry_remainder);
-
-next_queue:
- spin_unlock_bh(&txq->lock);
- }
-}
-
-#define IWL_FLUSH_WAIT_MS 2000
-
-static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq;
- struct iwl_queue *q;
- int cnt;
- unsigned long now = jiffies;
- u32 scd_sram_addr;
- u8 buf[16];
- int ret = 0;
-
- /* waiting for all the tx frames complete might take a while */
- for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
- u8 wr_ptr;
-
- if (cnt == trans_pcie->cmd_queue)
- continue;
- if (!test_bit(cnt, trans_pcie->queue_used))
- continue;
- if (!(BIT(cnt) & txq_bm))
- continue;
-
- IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt);
- txq = &trans_pcie->txq[cnt];
- q = &txq->q;
- wr_ptr = ACCESS_ONCE(q->write_ptr);
-
- while (q->read_ptr != ACCESS_ONCE(q->write_ptr) &&
- !time_after(jiffies,
- now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) {
- u8 write_ptr = ACCESS_ONCE(q->write_ptr);
-
- if (WARN_ONCE(wr_ptr != write_ptr,
- "WR pointer moved while flushing %d -> %d\n",
- wr_ptr, write_ptr))
- return -ETIMEDOUT;
- msleep(1);
- }
-
- if (q->read_ptr != q->write_ptr) {
- IWL_ERR(trans,
- "fail to flush all tx fifo queues Q %d\n", cnt);
- ret = -ETIMEDOUT;
- break;
- }
- IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt);
- }
-
- if (!ret)
- return 0;
-
- IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
- txq->q.read_ptr, txq->q.write_ptr);
-
- scd_sram_addr = trans_pcie->scd_base_addr +
- SCD_TX_STTS_QUEUE_OFFSET(txq->q.id);
- iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
-
- iwl_print_hex_error(trans, buf, sizeof(buf));
-
- for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++)
- IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt,
- iwl_read_direct32(trans, FH_TX_TRB_REG(cnt)));
-
- for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
- u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt));
- u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
- bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
- u32 tbl_dw =
- iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr +
- SCD_TRANS_TBL_OFFSET_QUEUE(cnt));
-
- if (cnt & 0x1)
- tbl_dw = (tbl_dw & 0xFFFF0000) >> 16;
- else
- tbl_dw = tbl_dw & 0x0000FFFF;
-
- IWL_ERR(trans,
- "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
- cnt, active ? "" : "in", fifo, tbl_dw,
- iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) &
- (TFD_QUEUE_SIZE_MAX - 1),
- iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
- }
-
- return ret;
-}
-
-static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
- u32 mask, u32 value)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- unsigned long flags;
-
- spin_lock_irqsave(&trans_pcie->reg_lock, flags);
- __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value);
- spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
-}
-
-void iwl_trans_pcie_ref(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- unsigned long flags;
-
- if (iwlwifi_mod_params.d0i3_disable)
- return;
-
- spin_lock_irqsave(&trans_pcie->ref_lock, flags);
- IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
- trans_pcie->ref_count++;
- spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
-}
-
-void iwl_trans_pcie_unref(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- unsigned long flags;
-
- if (iwlwifi_mod_params.d0i3_disable)
- return;
-
- spin_lock_irqsave(&trans_pcie->ref_lock, flags);
- IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
- if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) {
- spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
- return;
- }
- trans_pcie->ref_count--;
- spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
-}
-
-static const char *get_csr_string(int cmd)
-{
-#define IWL_CMD(x) case x: return #x
- switch (cmd) {
- IWL_CMD(CSR_HW_IF_CONFIG_REG);
- IWL_CMD(CSR_INT_COALESCING);
- IWL_CMD(CSR_INT);
- IWL_CMD(CSR_INT_MASK);
- IWL_CMD(CSR_FH_INT_STATUS);
- IWL_CMD(CSR_GPIO_IN);
- IWL_CMD(CSR_RESET);
- IWL_CMD(CSR_GP_CNTRL);
- IWL_CMD(CSR_HW_REV);
- IWL_CMD(CSR_EEPROM_REG);
- IWL_CMD(CSR_EEPROM_GP);
- IWL_CMD(CSR_OTP_GP_REG);
- IWL_CMD(CSR_GIO_REG);
- IWL_CMD(CSR_GP_UCODE_REG);
- IWL_CMD(CSR_GP_DRIVER_REG);
- IWL_CMD(CSR_UCODE_DRV_GP1);
- IWL_CMD(CSR_UCODE_DRV_GP2);
- IWL_CMD(CSR_LED_REG);
- IWL_CMD(CSR_DRAM_INT_TBL_REG);
- IWL_CMD(CSR_GIO_CHICKEN_BITS);
- IWL_CMD(CSR_ANA_PLL_CFG);
- IWL_CMD(CSR_HW_REV_WA_REG);
- IWL_CMD(CSR_MONITOR_STATUS_REG);
- IWL_CMD(CSR_DBG_HPET_MEM_REG);
- default:
- return "UNKNOWN";
- }
-#undef IWL_CMD
-}
-
-void iwl_pcie_dump_csr(struct iwl_trans *trans)
-{
- int i;
- static const u32 csr_tbl[] = {
- CSR_HW_IF_CONFIG_REG,
- CSR_INT_COALESCING,
- CSR_INT,
- CSR_INT_MASK,
- CSR_FH_INT_STATUS,
- CSR_GPIO_IN,
- CSR_RESET,
- CSR_GP_CNTRL,
- CSR_HW_REV,
- CSR_EEPROM_REG,
- CSR_EEPROM_GP,
- CSR_OTP_GP_REG,
- CSR_GIO_REG,
- CSR_GP_UCODE_REG,
- CSR_GP_DRIVER_REG,
- CSR_UCODE_DRV_GP1,
- CSR_UCODE_DRV_GP2,
- CSR_LED_REG,
- CSR_DRAM_INT_TBL_REG,
- CSR_GIO_CHICKEN_BITS,
- CSR_ANA_PLL_CFG,
- CSR_MONITOR_STATUS_REG,
- CSR_HW_REV_WA_REG,
- CSR_DBG_HPET_MEM_REG
- };
- IWL_ERR(trans, "CSR values:\n");
- IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is "
- "CSR_INT_PERIODIC_REG)\n");
- for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) {
- IWL_ERR(trans, " %25s: 0X%08x\n",
- get_csr_string(csr_tbl[i]),
- iwl_read32(trans, csr_tbl[i]));
- }
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-/* create and remove of files */
-#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
- if (!debugfs_create_file(#name, mode, parent, trans, \
- &iwl_dbgfs_##name##_ops)) \
- goto err; \
-} while (0)
-
-/* file operation */
-#define DEBUGFS_READ_FILE_OPS(name) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .read = iwl_dbgfs_##name##_read, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
-
-#define DEBUGFS_WRITE_FILE_OPS(name) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .write = iwl_dbgfs_##name##_write, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
-
-#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
- .write = iwl_dbgfs_##name##_write, \
- .read = iwl_dbgfs_##name##_read, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-};
-
-static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq;
- struct iwl_queue *q;
- char *buf;
- int pos = 0;
- int cnt;
- int ret;
- size_t bufsz;
-
- bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues;
-
- if (!trans_pcie->txq)
- return -EAGAIN;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
- txq = &trans_pcie->txq[cnt];
- q = &txq->q;
- pos += scnprintf(buf + pos, bufsz - pos,
- "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n",
- cnt, q->read_ptr, q->write_ptr,
- !!test_bit(cnt, trans_pcie->queue_used),
- !!test_bit(cnt, trans_pcie->queue_stopped),
- txq->need_update, txq->frozen,
- (cnt == trans_pcie->cmd_queue ? " HCMD" : ""));
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- char buf[256];
- int pos = 0;
- const size_t bufsz = sizeof(buf);
-
- pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n",
- rxq->read);
- pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n",
- rxq->write);
- pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n",
- rxq->write_actual);
- pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n",
- rxq->need_update);
- pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n",
- rxq->free_count);
- if (rxq->rb_stts) {
- pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n",
- le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF);
- } else {
- pos += scnprintf(buf + pos, bufsz - pos,
- "closed_rb_num: Not Allocated\n");
- }
- return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
-
- int pos = 0;
- char *buf;
- int bufsz = 24 * 64; /* 24 items * 64 char per item */
- ssize_t ret;
-
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Interrupt Statistics Report:\n");
-
- pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n",
- isr_stats->hw);
- pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n",
- isr_stats->sw);
- if (isr_stats->sw || isr_stats->hw) {
- pos += scnprintf(buf + pos, bufsz - pos,
- "\tLast Restarting Code: 0x%X\n",
- isr_stats->err_code);
- }
-#ifdef CONFIG_IWLWIFI_DEBUG
- pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n",
- isr_stats->sch);
- pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n",
- isr_stats->alive);
-#endif
- pos += scnprintf(buf + pos, bufsz - pos,
- "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill);
-
- pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n",
- isr_stats->ctkill);
-
- pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n",
- isr_stats->wakeup);
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Rx command responses:\t\t %u\n", isr_stats->rx);
-
- pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n",
- isr_stats->tx);
-
- pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n",
- isr_stats->unhandled);
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_interrupt_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
-
- char buf[8];
- int buf_size;
- u32 reset_flag;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%x", &reset_flag) != 1)
- return -EFAULT;
- if (reset_flag == 0)
- memset(isr_stats, 0, sizeof(*isr_stats));
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_csr_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- char buf[8];
- int buf_size;
- int csr;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &csr) != 1)
- return -EFAULT;
-
- iwl_pcie_dump_csr(trans);
-
- return count;
-}
-
-static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- char *buf = NULL;
- ssize_t ret;
-
- ret = iwl_dump_fh(trans, &buf);
- if (ret < 0)
- return ret;
- if (!buf)
- return -EINVAL;
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
-}
-
-DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
-DEBUGFS_READ_FILE_OPS(fh_reg);
-DEBUGFS_READ_FILE_OPS(rx_queue);
-DEBUGFS_READ_FILE_OPS(tx_queue);
-DEBUGFS_WRITE_FILE_OPS(csr);
-
-/*
- * Create the debugfs files and directories
- *
- */
-static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
- struct dentry *dir)
-{
- DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR);
- DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR);
- DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
- DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
- return 0;
-
-err:
- IWL_ERR(trans, "failed to create the trans debugfs entry\n");
- return -ENOMEM;
-}
-#else
-static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
- struct dentry *dir)
-{
- return 0;
-}
-#endif /*CONFIG_IWLWIFI_DEBUGFS */
-
-static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
-{
- u32 cmdlen = 0;
- int i;
-
- for (i = 0; i < IWL_NUM_OF_TBS; i++)
- cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i);
-
- return cmdlen;
-}
-
-static const struct {
- u32 start, end;
-} iwl_prph_dump_addr[] = {
- { .start = 0x00a00000, .end = 0x00a00000 },
- { .start = 0x00a0000c, .end = 0x00a00024 },
- { .start = 0x00a0002c, .end = 0x00a0003c },
- { .start = 0x00a00410, .end = 0x00a00418 },
- { .start = 0x00a00420, .end = 0x00a00420 },
- { .start = 0x00a00428, .end = 0x00a00428 },
- { .start = 0x00a00430, .end = 0x00a0043c },
- { .start = 0x00a00444, .end = 0x00a00444 },
- { .start = 0x00a004c0, .end = 0x00a004cc },
- { .start = 0x00a004d8, .end = 0x00a004d8 },
- { .start = 0x00a004e0, .end = 0x00a004f0 },
- { .start = 0x00a00840, .end = 0x00a00840 },
- { .start = 0x00a00850, .end = 0x00a00858 },
- { .start = 0x00a01004, .end = 0x00a01008 },
- { .start = 0x00a01010, .end = 0x00a01010 },
- { .start = 0x00a01018, .end = 0x00a01018 },
- { .start = 0x00a01024, .end = 0x00a01024 },
- { .start = 0x00a0102c, .end = 0x00a01034 },
- { .start = 0x00a0103c, .end = 0x00a01040 },
- { .start = 0x00a01048, .end = 0x00a01094 },
- { .start = 0x00a01c00, .end = 0x00a01c20 },
- { .start = 0x00a01c58, .end = 0x00a01c58 },
- { .start = 0x00a01c7c, .end = 0x00a01c7c },
- { .start = 0x00a01c28, .end = 0x00a01c54 },
- { .start = 0x00a01c5c, .end = 0x00a01c5c },
- { .start = 0x00a01c60, .end = 0x00a01cdc },
- { .start = 0x00a01ce0, .end = 0x00a01d0c },
- { .start = 0x00a01d18, .end = 0x00a01d20 },
- { .start = 0x00a01d2c, .end = 0x00a01d30 },
- { .start = 0x00a01d40, .end = 0x00a01d5c },
- { .start = 0x00a01d80, .end = 0x00a01d80 },
- { .start = 0x00a01d98, .end = 0x00a01d9c },
- { .start = 0x00a01da8, .end = 0x00a01da8 },
- { .start = 0x00a01db8, .end = 0x00a01df4 },
- { .start = 0x00a01dc0, .end = 0x00a01dfc },
- { .start = 0x00a01e00, .end = 0x00a01e2c },
- { .start = 0x00a01e40, .end = 0x00a01e60 },
- { .start = 0x00a01e68, .end = 0x00a01e6c },
- { .start = 0x00a01e74, .end = 0x00a01e74 },
- { .start = 0x00a01e84, .end = 0x00a01e90 },
- { .start = 0x00a01e9c, .end = 0x00a01ec4 },
- { .start = 0x00a01ed0, .end = 0x00a01ee0 },
- { .start = 0x00a01f00, .end = 0x00a01f1c },
- { .start = 0x00a01f44, .end = 0x00a01ffc },
- { .start = 0x00a02000, .end = 0x00a02048 },
- { .start = 0x00a02068, .end = 0x00a020f0 },
- { .start = 0x00a02100, .end = 0x00a02118 },
- { .start = 0x00a02140, .end = 0x00a0214c },
- { .start = 0x00a02168, .end = 0x00a0218c },
- { .start = 0x00a021c0, .end = 0x00a021c0 },
- { .start = 0x00a02400, .end = 0x00a02410 },
- { .start = 0x00a02418, .end = 0x00a02420 },
- { .start = 0x00a02428, .end = 0x00a0242c },
- { .start = 0x00a02434, .end = 0x00a02434 },
- { .start = 0x00a02440, .end = 0x00a02460 },
- { .start = 0x00a02468, .end = 0x00a024b0 },
- { .start = 0x00a024c8, .end = 0x00a024cc },
- { .start = 0x00a02500, .end = 0x00a02504 },
- { .start = 0x00a0250c, .end = 0x00a02510 },
- { .start = 0x00a02540, .end = 0x00a02554 },
- { .start = 0x00a02580, .end = 0x00a025f4 },
- { .start = 0x00a02600, .end = 0x00a0260c },
- { .start = 0x00a02648, .end = 0x00a02650 },
- { .start = 0x00a02680, .end = 0x00a02680 },
- { .start = 0x00a026c0, .end = 0x00a026d0 },
- { .start = 0x00a02700, .end = 0x00a0270c },
- { .start = 0x00a02804, .end = 0x00a02804 },
- { .start = 0x00a02818, .end = 0x00a0281c },
- { .start = 0x00a02c00, .end = 0x00a02db4 },
- { .start = 0x00a02df4, .end = 0x00a02fb0 },
- { .start = 0x00a03000, .end = 0x00a03014 },
- { .start = 0x00a0301c, .end = 0x00a0302c },
- { .start = 0x00a03034, .end = 0x00a03038 },
- { .start = 0x00a03040, .end = 0x00a03048 },
- { .start = 0x00a03060, .end = 0x00a03068 },
- { .start = 0x00a03070, .end = 0x00a03074 },
- { .start = 0x00a0307c, .end = 0x00a0307c },
- { .start = 0x00a03080, .end = 0x00a03084 },
- { .start = 0x00a0308c, .end = 0x00a03090 },
- { .start = 0x00a03098, .end = 0x00a03098 },
- { .start = 0x00a030a0, .end = 0x00a030a0 },
- { .start = 0x00a030a8, .end = 0x00a030b4 },
- { .start = 0x00a030bc, .end = 0x00a030bc },
- { .start = 0x00a030c0, .end = 0x00a0312c },
- { .start = 0x00a03c00, .end = 0x00a03c5c },
- { .start = 0x00a04400, .end = 0x00a04454 },
- { .start = 0x00a04460, .end = 0x00a04474 },
- { .start = 0x00a044c0, .end = 0x00a044ec },
- { .start = 0x00a04500, .end = 0x00a04504 },
- { .start = 0x00a04510, .end = 0x00a04538 },
- { .start = 0x00a04540, .end = 0x00a04548 },
- { .start = 0x00a04560, .end = 0x00a0457c },
- { .start = 0x00a04590, .end = 0x00a04598 },
- { .start = 0x00a045c0, .end = 0x00a045f4 },
-};
-
-static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans,
- struct iwl_fw_error_dump_data **data)
-{
- struct iwl_fw_error_dump_prph *prph;
- unsigned long flags;
- u32 prph_len = 0, i;
-
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
- /* The range includes both boundaries */
- int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
- iwl_prph_dump_addr[i].start + 4;
- int reg;
- __le32 *val;
-
- prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
-
- (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
- (*data)->len = cpu_to_le32(sizeof(*prph) +
- num_bytes_in_chunk);
- prph = (void *)(*data)->data;
- prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
- val = (void *)prph->data;
-
- for (reg = iwl_prph_dump_addr[i].start;
- reg <= iwl_prph_dump_addr[i].end;
- reg += 4)
- *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans,
- reg));
- *data = iwl_fw_error_next_data(*data);
- }
-
- iwl_trans_release_nic_access(trans, &flags);
-
- return prph_len;
-}
-
-static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans,
- struct iwl_fw_error_dump_data **data,
- int allocated_rb_nums)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int max_len = PAGE_SIZE << trans_pcie->rx_page_order;
- struct iwl_rxq *rxq = &trans_pcie->rxq;
- u32 i, r, j, rb_len = 0;
-
- spin_lock(&rxq->lock);
-
- r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
-
- for (i = rxq->read, j = 0;
- i != r && j < allocated_rb_nums;
- i = (i + 1) & RX_QUEUE_MASK, j++) {
- struct iwl_rx_mem_buffer *rxb = rxq->queue[i];
- struct iwl_fw_error_dump_rb *rb;
-
- dma_unmap_page(trans->dev, rxb->page_dma, max_len,
- DMA_FROM_DEVICE);
-
- rb_len += sizeof(**data) + sizeof(*rb) + max_len;
-
- (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RB);
- (*data)->len = cpu_to_le32(sizeof(*rb) + max_len);
- rb = (void *)(*data)->data;
- rb->index = cpu_to_le32(i);
- memcpy(rb->data, page_address(rxb->page), max_len);
- /* remap the page for the free benefit */
- rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0,
- max_len,
- DMA_FROM_DEVICE);
-
- *data = iwl_fw_error_next_data(*data);
- }
-
- spin_unlock(&rxq->lock);
-
- return rb_len;
-}
-#define IWL_CSR_TO_DUMP (0x250)
-
-static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans,
- struct iwl_fw_error_dump_data **data)
-{
- u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP;
- __le32 *val;
- int i;
-
- (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR);
- (*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP);
- val = (void *)(*data)->data;
-
- for (i = 0; i < IWL_CSR_TO_DUMP; i += 4)
- *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
-
- *data = iwl_fw_error_next_data(*data);
-
- return csr_len;
-}
-
-static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
- struct iwl_fw_error_dump_data **data)
-{
- u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND;
- unsigned long flags;
- __le32 *val;
- int i;
-
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
- return 0;
-
- (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS);
- (*data)->len = cpu_to_le32(fh_regs_len);
- val = (void *)(*data)->data;
-
- for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32))
- *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
-
- iwl_trans_release_nic_access(trans, &flags);
-
- *data = iwl_fw_error_next_data(*data);
-
- return sizeof(**data) + fh_regs_len;
-}
-
-static u32
-iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans,
- struct iwl_fw_error_dump_fw_mon *fw_mon_data,
- u32 monitor_len)
-{
- u32 buf_size_in_dwords = (monitor_len >> 2);
- u32 *buffer = (u32 *)fw_mon_data->data;
- unsigned long flags;
- u32 i;
-
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
- return 0;
-
- __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x1);
- for (i = 0; i < buf_size_in_dwords; i++)
- buffer[i] = __iwl_read_prph(trans, MON_DMARB_RD_DATA_ADDR);
- __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x0);
-
- iwl_trans_release_nic_access(trans, &flags);
-
- return monitor_len;
-}
-
-static u32
-iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
- struct iwl_fw_error_dump_data **data,
- u32 monitor_len)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u32 len = 0;
-
- if ((trans_pcie->fw_mon_page &&
- trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
- trans->dbg_dest_tlv) {
- struct iwl_fw_error_dump_fw_mon *fw_mon_data;
- u32 base, write_ptr, wrap_cnt;
-
- /* If there was a dest TLV - use the values from there */
- if (trans->dbg_dest_tlv) {
- write_ptr =
- le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
- wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
- base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
- } else {
- base = MON_BUFF_BASE_ADDR;
- write_ptr = MON_BUFF_WRPTR;
- wrap_cnt = MON_BUFF_CYCLE_CNT;
- }
-
- (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
- fw_mon_data = (void *)(*data)->data;
- fw_mon_data->fw_mon_wr_ptr =
- cpu_to_le32(iwl_read_prph(trans, write_ptr));
- fw_mon_data->fw_mon_cycle_cnt =
- cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
- fw_mon_data->fw_mon_base_ptr =
- cpu_to_le32(iwl_read_prph(trans, base));
-
- len += sizeof(**data) + sizeof(*fw_mon_data);
- if (trans_pcie->fw_mon_page) {
- /*
- * The firmware is now asserted, it won't write anything
- * to the buffer. CPU can take ownership to fetch the
- * data. The buffer will be handed back to the device
- * before the firmware will be restarted.
- */
- dma_sync_single_for_cpu(trans->dev,
- trans_pcie->fw_mon_phys,
- trans_pcie->fw_mon_size,
- DMA_FROM_DEVICE);
- memcpy(fw_mon_data->data,
- page_address(trans_pcie->fw_mon_page),
- trans_pcie->fw_mon_size);
-
- monitor_len = trans_pcie->fw_mon_size;
- } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) {
- /*
- * Update pointers to reflect actual values after
- * shifting
- */
- base = iwl_read_prph(trans, base) <<
- trans->dbg_dest_tlv->base_shift;
- iwl_trans_read_mem(trans, base, fw_mon_data->data,
- monitor_len / sizeof(u32));
- } else if (trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) {
- monitor_len =
- iwl_trans_pci_dump_marbh_monitor(trans,
- fw_mon_data,
- monitor_len);
- } else {
- /* Didn't match anything - output no monitor data */
- monitor_len = 0;
- }
-
- len += monitor_len;
- (*data)->len = cpu_to_le32(monitor_len + sizeof(*fw_mon_data));
- }
-
- return len;
-}
-
-static struct iwl_trans_dump_data
-*iwl_trans_pcie_dump_data(struct iwl_trans *trans,
- struct iwl_fw_dbg_trigger_tlv *trigger)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_fw_error_dump_data *data;
- struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
- struct iwl_fw_error_dump_txcmd *txcmd;
- struct iwl_trans_dump_data *dump_data;
- u32 len, num_rbs;
- u32 monitor_len;
- int i, ptr;
- bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status);
-
- /* transport dump header */
- len = sizeof(*dump_data);
-
- /* host commands */
- len += sizeof(*data) +
- cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
-
- /* FW monitor */
- if (trans_pcie->fw_mon_page) {
- len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
- trans_pcie->fw_mon_size;
- monitor_len = trans_pcie->fw_mon_size;
- } else if (trans->dbg_dest_tlv) {
- u32 base, end;
-
- base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
- end = le32_to_cpu(trans->dbg_dest_tlv->end_reg);
-
- base = iwl_read_prph(trans, base) <<
- trans->dbg_dest_tlv->base_shift;
- end = iwl_read_prph(trans, end) <<
- trans->dbg_dest_tlv->end_shift;
-
- /* Make "end" point to the actual end */
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 ||
- trans->dbg_dest_tlv->monitor_mode == MARBH_MODE)
- end += (1 << trans->dbg_dest_tlv->end_shift);
- monitor_len = end - base;
- len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
- monitor_len;
- } else {
- monitor_len = 0;
- }
-
- if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) {
- dump_data = vzalloc(len);
- if (!dump_data)
- return NULL;
-
- data = (void *)dump_data->data;
- len = iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
- dump_data->len = len;
-
- return dump_data;
- }
-
- /* CSR registers */
- len += sizeof(*data) + IWL_CSR_TO_DUMP;
-
- /* PRPH registers */
- for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
- /* The range includes both boundaries */
- int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
- iwl_prph_dump_addr[i].start + 4;
-
- len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) +
- num_bytes_in_chunk;
- }
-
- /* FH registers */
- len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
-
- if (dump_rbs) {
- /* RBs */
- num_rbs = le16_to_cpu(ACCESS_ONCE(
- trans_pcie->rxq.rb_stts->closed_rb_num))
- & 0x0FFF;
- num_rbs = (num_rbs - trans_pcie->rxq.read) & RX_QUEUE_MASK;
- len += num_rbs * (sizeof(*data) +
- sizeof(struct iwl_fw_error_dump_rb) +
- (PAGE_SIZE << trans_pcie->rx_page_order));
- }
-
- dump_data = vzalloc(len);
- if (!dump_data)
- return NULL;
-
- len = 0;
- data = (void *)dump_data->data;
- data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
- txcmd = (void *)data->data;
- spin_lock_bh(&cmdq->lock);
- ptr = cmdq->q.write_ptr;
- for (i = 0; i < cmdq->q.n_window; i++) {
- u8 idx = get_cmd_index(&cmdq->q, ptr);
- u32 caplen, cmdlen;
-
- cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]);
- caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen);
-
- if (cmdlen) {
- len += sizeof(*txcmd) + caplen;
- txcmd->cmdlen = cpu_to_le32(cmdlen);
- txcmd->caplen = cpu_to_le32(caplen);
- memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen);
- txcmd = (void *)((u8 *)txcmd->data + caplen);
- }
-
- ptr = iwl_queue_dec_wrap(ptr);
- }
- spin_unlock_bh(&cmdq->lock);
-
- data->len = cpu_to_le32(len);
- len += sizeof(*data);
- data = iwl_fw_error_next_data(data);
-
- len += iwl_trans_pcie_dump_prph(trans, &data);
- len += iwl_trans_pcie_dump_csr(trans, &data);
- len += iwl_trans_pcie_fh_regs_dump(trans, &data);
- if (dump_rbs)
- len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
-
- len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
-
- dump_data->len = len;
-
- return dump_data;
-}
-
-static const struct iwl_trans_ops trans_ops_pcie = {
- .start_hw = iwl_trans_pcie_start_hw,
- .op_mode_leave = iwl_trans_pcie_op_mode_leave,
- .fw_alive = iwl_trans_pcie_fw_alive,
- .start_fw = iwl_trans_pcie_start_fw,
- .stop_device = iwl_trans_pcie_stop_device,
-
- .d3_suspend = iwl_trans_pcie_d3_suspend,
- .d3_resume = iwl_trans_pcie_d3_resume,
-
- .send_cmd = iwl_trans_pcie_send_hcmd,
-
- .tx = iwl_trans_pcie_tx,
- .reclaim = iwl_trans_pcie_reclaim,
-
- .txq_disable = iwl_trans_pcie_txq_disable,
- .txq_enable = iwl_trans_pcie_txq_enable,
-
- .dbgfs_register = iwl_trans_pcie_dbgfs_register,
-
- .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
- .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
-
- .write8 = iwl_trans_pcie_write8,
- .write32 = iwl_trans_pcie_write32,
- .read32 = iwl_trans_pcie_read32,
- .read_prph = iwl_trans_pcie_read_prph,
- .write_prph = iwl_trans_pcie_write_prph,
- .read_mem = iwl_trans_pcie_read_mem,
- .write_mem = iwl_trans_pcie_write_mem,
- .configure = iwl_trans_pcie_configure,
- .set_pmi = iwl_trans_pcie_set_pmi,
- .grab_nic_access = iwl_trans_pcie_grab_nic_access,
- .release_nic_access = iwl_trans_pcie_release_nic_access,
- .set_bits_mask = iwl_trans_pcie_set_bits_mask,
-
- .ref = iwl_trans_pcie_ref,
- .unref = iwl_trans_pcie_unref,
-
- .dump_data = iwl_trans_pcie_dump_data,
-};
-
-struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
- const struct pci_device_id *ent,
- const struct iwl_cfg *cfg)
-{
- struct iwl_trans_pcie *trans_pcie;
- struct iwl_trans *trans;
- u16 pci_cmd;
- int ret;
-
- trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie),
- &pdev->dev, cfg, &trans_ops_pcie, 0);
- if (!trans)
- return ERR_PTR(-ENOMEM);
-
- trans->max_skb_frags = IWL_PCIE_MAX_FRAGS;
-
- trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- trans_pcie->trans = trans;
- spin_lock_init(&trans_pcie->irq_lock);
- spin_lock_init(&trans_pcie->reg_lock);
- spin_lock_init(&trans_pcie->ref_lock);
- mutex_init(&trans_pcie->mutex);
- init_waitqueue_head(&trans_pcie->ucode_write_waitq);
-
- ret = pci_enable_device(pdev);
- if (ret)
- goto out_no_pci;
-
- if (!cfg->base_params->pcie_l1_allowed) {
- /*
- * W/A - seems to solve weird behavior. We need to remove this
- * if we don't want to stay in L1 all the time. This wastes a
- * lot of power.
- */
- pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
- PCIE_LINK_STATE_L1 |
- PCIE_LINK_STATE_CLKPM);
- }
-
- pci_set_master(pdev);
-
- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
- if (!ret)
- ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
- if (ret) {
- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (!ret)
- ret = pci_set_consistent_dma_mask(pdev,
- DMA_BIT_MASK(32));
- /* both attempts failed: */
- if (ret) {
- dev_err(&pdev->dev, "No suitable DMA available\n");
- goto out_pci_disable_device;
- }
- }
-
- ret = pci_request_regions(pdev, DRV_NAME);
- if (ret) {
- dev_err(&pdev->dev, "pci_request_regions failed\n");
- goto out_pci_disable_device;
- }
-
- trans_pcie->hw_base = pci_ioremap_bar(pdev, 0);
- if (!trans_pcie->hw_base) {
- dev_err(&pdev->dev, "pci_ioremap_bar failed\n");
- ret = -ENODEV;
- goto out_pci_release_regions;
- }
-
- /* We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state */
- pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
-
- trans->dev = &pdev->dev;
- trans_pcie->pci_dev = pdev;
- iwl_disable_interrupts(trans);
-
- ret = pci_enable_msi(pdev);
- if (ret) {
- dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret);
- /* enable rfkill interrupt: hw bug w/a */
- pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
- if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
- pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
- pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
- }
- }
-
- trans->hw_rev = iwl_read32(trans, CSR_HW_REV);
- /*
- * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
- * changed, and now the revision step also includes bit 0-1 (no more
- * "dash" value). To keep hw_rev backwards compatible - we'll store it
- * in the old format.
- */
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
- unsigned long flags;
-
- trans->hw_rev = (trans->hw_rev & 0xfff0) |
- (CSR_HW_REV_STEP(trans->hw_rev << 2) << 2);
-
- ret = iwl_pcie_prepare_card_hw(trans);
- if (ret) {
- IWL_WARN(trans, "Exit HW not ready\n");
- goto out_pci_disable_msi;
- }
-
- /*
- * in-order to recognize C step driver should read chip version
- * id located at the AUX bus MISC address space.
- */
- iwl_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
- udelay(2);
-
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- 25000);
- if (ret < 0) {
- IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n");
- goto out_pci_disable_msi;
- }
-
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- u32 hw_step;
-
- hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG);
- hw_step |= ENABLE_WFPM;
- __iwl_write_prph(trans, WFPM_CTRL_REG, hw_step);
- hw_step = __iwl_read_prph(trans, AUX_MISC_REG);
- hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF;
- if (hw_step == 0x3)
- trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) |
- (SILICON_C_STEP << 2);
- iwl_trans_release_nic_access(trans, &flags);
- }
- }
-
- trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
- snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
- "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
-
- /* Initialize the wait queue for commands */
- init_waitqueue_head(&trans_pcie->wait_command_queue);
-
- ret = iwl_pcie_alloc_ict(trans);
- if (ret)
- goto out_pci_disable_msi;
-
- ret = request_threaded_irq(pdev->irq, iwl_pcie_isr,
- iwl_pcie_irq_handler,
- IRQF_SHARED, DRV_NAME, trans);
- if (ret) {
- IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
- goto out_free_ict;
- }
-
- trans_pcie->inta_mask = CSR_INI_SET_MASK;
- trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND;
-
- return trans;
-
-out_free_ict:
- iwl_pcie_free_ict(trans);
-out_pci_disable_msi:
- pci_disable_msi(pdev);
-out_pci_release_regions:
- pci_release_regions(pdev);
-out_pci_disable_device:
- pci_disable_device(pdev);
-out_no_pci:
- iwl_trans_free(trans);
- return ERR_PTR(ret);
-}
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
deleted file mode 100644
index a8c8a4a7420b..000000000000
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ /dev/null
@@ -1,1988 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- *****************************************************************************/
-#include <linux/etherdevice.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-
-#include "iwl-debug.h"
-#include "iwl-csr.h"
-#include "iwl-prph.h"
-#include "iwl-io.h"
-#include "iwl-scd.h"
-#include "iwl-op-mode.h"
-#include "internal.h"
-/* FIXME: need to abstract out TX command (once we know what it looks like) */
-#include "dvm/commands.h"
-
-#define IWL_TX_CRC_SIZE 4
-#define IWL_TX_DELIMITER_SIZE 4
-
-/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
- * DMA services
- *
- * Theory of operation
- *
- * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer
- * of buffer descriptors, each of which points to one or more data buffers for
- * the device to read from or fill. Driver and device exchange status of each
- * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty
- * entries in each circular buffer, to protect against confusing empty and full
- * queue states.
- *
- * The device reads or writes the data in the queues via the device's several
- * DMA/FIFO channels. Each queue is mapped to a single DMA channel.
- *
- * For Tx queue, there are low mark and high mark limits. If, after queuing
- * the packet for Tx, free space become < low mark, Tx queue stopped. When
- * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
- * Tx queue resumed.
- *
- ***************************************************/
-static int iwl_queue_space(const struct iwl_queue *q)
-{
- unsigned int max;
- unsigned int used;
-
- /*
- * To avoid ambiguity between empty and completely full queues, there
- * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue.
- * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need
- * to reserve any queue entries for this purpose.
- */
- if (q->n_window < TFD_QUEUE_SIZE_MAX)
- max = q->n_window;
- else
- max = TFD_QUEUE_SIZE_MAX - 1;
-
- /*
- * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to
- * modulo by TFD_QUEUE_SIZE_MAX and is well defined.
- */
- used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1);
-
- if (WARN_ON(used > max))
- return 0;
-
- return max - used;
-}
-
-/*
- * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
- */
-static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id)
-{
- q->n_window = slots_num;
- q->id = id;
-
- /* slots_num must be power-of-two size, otherwise
- * get_cmd_index is broken. */
- if (WARN_ON(!is_power_of_2(slots_num)))
- return -EINVAL;
-
- q->low_mark = q->n_window / 4;
- if (q->low_mark < 4)
- q->low_mark = 4;
-
- q->high_mark = q->n_window / 8;
- if (q->high_mark < 2)
- q->high_mark = 2;
-
- q->write_ptr = 0;
- q->read_ptr = 0;
-
- return 0;
-}
-
-static int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans,
- struct iwl_dma_ptr *ptr, size_t size)
-{
- if (WARN_ON(ptr->addr))
- return -EINVAL;
-
- ptr->addr = dma_alloc_coherent(trans->dev, size,
- &ptr->dma, GFP_KERNEL);
- if (!ptr->addr)
- return -ENOMEM;
- ptr->size = size;
- return 0;
-}
-
-static void iwl_pcie_free_dma_ptr(struct iwl_trans *trans,
- struct iwl_dma_ptr *ptr)
-{
- if (unlikely(!ptr->addr))
- return;
-
- dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma);
- memset(ptr, 0, sizeof(*ptr));
-}
-
-static void iwl_pcie_txq_stuck_timer(unsigned long data)
-{
- struct iwl_txq *txq = (void *)data;
- struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
- struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie);
- u32 scd_sram_addr = trans_pcie->scd_base_addr +
- SCD_TX_STTS_QUEUE_OFFSET(txq->q.id);
- u8 buf[16];
- int i;
-
- spin_lock(&txq->lock);
- /* check if triggered erroneously */
- if (txq->q.read_ptr == txq->q.write_ptr) {
- spin_unlock(&txq->lock);
- return;
- }
- spin_unlock(&txq->lock);
-
- IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id,
- jiffies_to_msecs(txq->wd_timeout));
- IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
- txq->q.read_ptr, txq->q.write_ptr);
-
- iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
-
- iwl_print_hex_error(trans, buf, sizeof(buf));
-
- for (i = 0; i < FH_TCSR_CHNL_NUM; i++)
- IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i,
- iwl_read_direct32(trans, FH_TX_TRB_REG(i)));
-
- for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
- u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i));
- u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
- bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
- u32 tbl_dw =
- iwl_trans_read_mem32(trans,
- trans_pcie->scd_base_addr +
- SCD_TRANS_TBL_OFFSET_QUEUE(i));
-
- if (i & 0x1)
- tbl_dw = (tbl_dw & 0xFFFF0000) >> 16;
- else
- tbl_dw = tbl_dw & 0x0000FFFF;
-
- IWL_ERR(trans,
- "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
- i, active ? "" : "in", fifo, tbl_dw,
- iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) &
- (TFD_QUEUE_SIZE_MAX - 1),
- iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
- }
-
- iwl_force_nmi(trans);
-}
-
-/*
- * iwl_pcie_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
- */
-static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
- struct iwl_txq *txq, u16 byte_cnt)
-{
- struct iwlagn_scd_bc_tbl *scd_bc_tbl;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int write_ptr = txq->q.write_ptr;
- int txq_id = txq->q.id;
- u8 sec_ctl = 0;
- u8 sta_id = 0;
- u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
- __le16 bc_ent;
- struct iwl_tx_cmd *tx_cmd =
- (void *) txq->entries[txq->q.write_ptr].cmd->payload;
-
- scd_bc_tbl = trans_pcie->scd_bc_tbls.addr;
-
- sta_id = tx_cmd->sta_id;
- sec_ctl = tx_cmd->sec_ctl;
-
- switch (sec_ctl & TX_CMD_SEC_MSK) {
- case TX_CMD_SEC_CCM:
- len += IEEE80211_CCMP_MIC_LEN;
- break;
- case TX_CMD_SEC_TKIP:
- len += IEEE80211_TKIP_ICV_LEN;
- break;
- case TX_CMD_SEC_WEP:
- len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN;
- break;
- }
-
- if (trans_pcie->bc_table_dword)
- len = DIV_ROUND_UP(len, 4);
-
- if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX))
- return;
-
- bc_ent = cpu_to_le16(len | (sta_id << 12));
-
- scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
-
- if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
- scd_bc_tbl[txq_id].
- tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
-}
-
-static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans,
- struct iwl_txq *txq)
-{
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr;
- int txq_id = txq->q.id;
- int read_ptr = txq->q.read_ptr;
- u8 sta_id = 0;
- __le16 bc_ent;
- struct iwl_tx_cmd *tx_cmd =
- (void *)txq->entries[txq->q.read_ptr].cmd->payload;
-
- WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
-
- if (txq_id != trans_pcie->cmd_queue)
- sta_id = tx_cmd->sta_id;
-
- bc_ent = cpu_to_le16(1 | (sta_id << 12));
- scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
-
- if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
- scd_bc_tbl[txq_id].
- tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
-}
-
-/*
- * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware
- */
-static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
- struct iwl_txq *txq)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u32 reg = 0;
- int txq_id = txq->q.id;
-
- lockdep_assert_held(&txq->lock);
-
- /*
- * explicitly wake up the NIC if:
- * 1. shadow registers aren't enabled
- * 2. NIC is woken up for CMD regardless of shadow outside this function
- * 3. there is a chance that the NIC is asleep
- */
- if (!trans->cfg->base_params->shadow_reg_enable &&
- txq_id != trans_pcie->cmd_queue &&
- test_bit(STATUS_TPOWER_PMI, &trans->status)) {
- /*
- * wake up nic if it's powered down ...
- * uCode will wake up, and interrupt us again, so next
- * time we'll skip this part.
- */
- reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n",
- txq_id, reg);
- iwl_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- txq->need_update = true;
- return;
- }
- }
-
- /*
- * if not in power-save mode, uCode will never sleep when we're
- * trying to tx (during RFKILL, we're not trying to tx).
- */
- IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr);
- iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
-}
-
-void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int i;
-
- for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
- struct iwl_txq *txq = &trans_pcie->txq[i];
-
- spin_lock_bh(&txq->lock);
- if (trans_pcie->txq[i].need_update) {
- iwl_pcie_txq_inc_wr_ptr(trans, txq);
- trans_pcie->txq[i].need_update = false;
- }
- spin_unlock_bh(&txq->lock);
- }
-}
-
-static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
-{
- struct iwl_tfd_tb *tb = &tfd->tbs[idx];
-
- dma_addr_t addr = get_unaligned_le32(&tb->lo);
- if (sizeof(dma_addr_t) > sizeof(u32))
- addr |=
- ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16;
-
- return addr;
-}
-
-static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
- dma_addr_t addr, u16 len)
-{
- struct iwl_tfd_tb *tb = &tfd->tbs[idx];
- u16 hi_n_len = len << 4;
-
- put_unaligned_le32(addr, &tb->lo);
- if (sizeof(dma_addr_t) > sizeof(u32))
- hi_n_len |= ((addr >> 16) >> 16) & 0xF;
-
- tb->hi_n_len = cpu_to_le16(hi_n_len);
-
- tfd->num_tbs = idx + 1;
-}
-
-static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_tfd *tfd)
-{
- return tfd->num_tbs & 0x1f;
-}
-
-static void iwl_pcie_tfd_unmap(struct iwl_trans *trans,
- struct iwl_cmd_meta *meta,
- struct iwl_tfd *tfd)
-{
- int i;
- int num_tbs;
-
- /* Sanity check on number of chunks */
- num_tbs = iwl_pcie_tfd_get_num_tbs(tfd);
-
- if (num_tbs >= IWL_NUM_OF_TBS) {
- IWL_ERR(trans, "Too many chunks: %i\n", num_tbs);
- /* @todo issue fatal error, it is quite serious situation */
- return;
- }
-
- /* first TB is never freed - it's the scratchbuf data */
-
- for (i = 1; i < num_tbs; i++) {
- if (meta->flags & BIT(i + CMD_TB_BITMAP_POS))
- dma_unmap_page(trans->dev,
- iwl_pcie_tfd_tb_get_addr(tfd, i),
- iwl_pcie_tfd_tb_get_len(tfd, i),
- DMA_TO_DEVICE);
- else
- dma_unmap_single(trans->dev,
- iwl_pcie_tfd_tb_get_addr(tfd, i),
- iwl_pcie_tfd_tb_get_len(tfd, i),
- DMA_TO_DEVICE);
- }
- tfd->num_tbs = 0;
-}
-
-/*
- * iwl_pcie_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
- * @trans - transport private data
- * @txq - tx queue
- * @dma_dir - the direction of the DMA mapping
- *
- * Does NOT advance any TFD circular buffer read/write indexes
- * Does NOT free the TFD itself (which is within circular buffer)
- */
-static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
-{
- struct iwl_tfd *tfd_tmp = txq->tfds;
-
- /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
- * idx is bounded by n_window
- */
- int rd_ptr = txq->q.read_ptr;
- int idx = get_cmd_index(&txq->q, rd_ptr);
-
- lockdep_assert_held(&txq->lock);
-
- /* We have only q->n_window txq->entries, but we use
- * TFD_QUEUE_SIZE_MAX tfds
- */
- iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]);
-
- /* free SKB */
- if (txq->entries) {
- struct sk_buff *skb;
-
- skb = txq->entries[idx].skb;
-
- /* Can be called from irqs-disabled context
- * If skb is not NULL, it means that the whole queue is being
- * freed and that the queue is not empty - free the skb
- */
- if (skb) {
- iwl_op_mode_free_skb(trans->op_mode, skb);
- txq->entries[idx].skb = NULL;
- }
- }
-}
-
-static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
- dma_addr_t addr, u16 len, bool reset)
-{
- struct iwl_queue *q;
- struct iwl_tfd *tfd, *tfd_tmp;
- u32 num_tbs;
-
- q = &txq->q;
- tfd_tmp = txq->tfds;
- tfd = &tfd_tmp[q->write_ptr];
-
- if (reset)
- memset(tfd, 0, sizeof(*tfd));
-
- num_tbs = iwl_pcie_tfd_get_num_tbs(tfd);
-
- /* Each TFD can point to a maximum 20 Tx buffers */
- if (num_tbs >= IWL_NUM_OF_TBS) {
- IWL_ERR(trans, "Error can not send more than %d chunks\n",
- IWL_NUM_OF_TBS);
- return -EINVAL;
- }
-
- if (WARN(addr & ~IWL_TX_DMA_MASK,
- "Unaligned address = %llx\n", (unsigned long long)addr))
- return -EINVAL;
-
- iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len);
-
- return num_tbs;
-}
-
-static int iwl_pcie_txq_alloc(struct iwl_trans *trans,
- struct iwl_txq *txq, int slots_num,
- u32 txq_id)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX;
- size_t scratchbuf_sz;
- int i;
-
- if (WARN_ON(txq->entries || txq->tfds))
- return -EINVAL;
-
- setup_timer(&txq->stuck_timer, iwl_pcie_txq_stuck_timer,
- (unsigned long)txq);
- txq->trans_pcie = trans_pcie;
-
- txq->q.n_window = slots_num;
-
- txq->entries = kcalloc(slots_num,
- sizeof(struct iwl_pcie_txq_entry),
- GFP_KERNEL);
-
- if (!txq->entries)
- goto error;
-
- if (txq_id == trans_pcie->cmd_queue)
- for (i = 0; i < slots_num; i++) {
- txq->entries[i].cmd =
- kmalloc(sizeof(struct iwl_device_cmd),
- GFP_KERNEL);
- if (!txq->entries[i].cmd)
- goto error;
- }
-
- /* Circular buffer of transmit frame descriptors (TFDs),
- * shared with device */
- txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz,
- &txq->q.dma_addr, GFP_KERNEL);
- if (!txq->tfds)
- goto error;
-
- BUILD_BUG_ON(IWL_HCMD_SCRATCHBUF_SIZE != sizeof(*txq->scratchbufs));
- BUILD_BUG_ON(offsetof(struct iwl_pcie_txq_scratch_buf, scratch) !=
- sizeof(struct iwl_cmd_header) +
- offsetof(struct iwl_tx_cmd, scratch));
-
- scratchbuf_sz = sizeof(*txq->scratchbufs) * slots_num;
-
- txq->scratchbufs = dma_alloc_coherent(trans->dev, scratchbuf_sz,
- &txq->scratchbufs_dma,
- GFP_KERNEL);
- if (!txq->scratchbufs)
- goto err_free_tfds;
-
- txq->q.id = txq_id;
-
- return 0;
-err_free_tfds:
- dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->q.dma_addr);
-error:
- if (txq->entries && txq_id == trans_pcie->cmd_queue)
- for (i = 0; i < slots_num; i++)
- kfree(txq->entries[i].cmd);
- kfree(txq->entries);
- txq->entries = NULL;
-
- return -ENOMEM;
-
-}
-
-static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
- int slots_num, u32 txq_id)
-{
- int ret;
-
- txq->need_update = false;
-
- /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
- * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
- BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
-
- /* Initialize queue's high/low-water marks, and head/tail indexes */
- ret = iwl_queue_init(&txq->q, slots_num, txq_id);
- if (ret)
- return ret;
-
- spin_lock_init(&txq->lock);
-
- /*
- * Tell nic where to find circular buffer of Tx Frame Descriptors for
- * given Tx queue, and enable the DMA channel used for that queue.
- * Circular buffer (TFD queue in DRAM) physical base address */
- iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id),
- txq->q.dma_addr >> 8);
-
- return 0;
-}
-
-/*
- * iwl_pcie_txq_unmap - Unmap any remaining DMA mappings and free skb's
- */
-static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = &trans_pcie->txq[txq_id];
- struct iwl_queue *q = &txq->q;
-
- 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);
- }
- txq->active = false;
- spin_unlock_bh(&txq->lock);
-
- /* just in case - this queue may have been stopped */
- iwl_wake_queue(trans, txq);
-}
-
-/*
- * iwl_pcie_txq_free - Deallocate DMA queue.
- * @txq: Transmit queue to deallocate.
- *
- * Empty queue by removing and destroying all BD's.
- * Free all buffers.
- * 0-fill, but do not free "txq" descriptor structure.
- */
-static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = &trans_pcie->txq[txq_id];
- struct device *dev = trans->dev;
- int i;
-
- if (WARN_ON(!txq))
- return;
-
- iwl_pcie_txq_unmap(trans, txq_id);
-
- /* De-alloc array of command/tx buffers */
- if (txq_id == trans_pcie->cmd_queue)
- for (i = 0; i < txq->q.n_window; i++) {
- kzfree(txq->entries[i].cmd);
- kzfree(txq->entries[i].free_buf);
- }
-
- /* De-alloc circular buffer of TFDs */
- if (txq->tfds) {
- dma_free_coherent(dev,
- sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX,
- txq->tfds, txq->q.dma_addr);
- txq->q.dma_addr = 0;
- txq->tfds = NULL;
-
- dma_free_coherent(dev,
- sizeof(*txq->scratchbufs) * txq->q.n_window,
- txq->scratchbufs, txq->scratchbufs_dma);
- }
-
- kfree(txq->entries);
- txq->entries = NULL;
-
- del_timer_sync(&txq->stuck_timer);
-
- /* 0-fill queue descriptor structure */
- memset(txq, 0, sizeof(*txq));
-}
-
-void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int nq = trans->cfg->base_params->num_of_queues;
- int chan;
- u32 reg_val;
- int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) -
- SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(u32);
-
- /* make sure all queue are not stopped/used */
- memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
- memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
-
- trans_pcie->scd_base_addr =
- iwl_read_prph(trans, SCD_SRAM_BASE_ADDR);
-
- WARN_ON(scd_base_addr != 0 &&
- scd_base_addr != trans_pcie->scd_base_addr);
-
- /* reset context data, TX status and translation data */
- iwl_trans_write_mem(trans, trans_pcie->scd_base_addr +
- SCD_CONTEXT_MEM_LOWER_BOUND,
- NULL, clear_dwords);
-
- iwl_write_prph(trans, SCD_DRAM_BASE_ADDR,
- trans_pcie->scd_bc_tbls.dma >> 10);
-
- /* The chain extension of the SCD doesn't work well. This feature is
- * enabled by default by the HW, so we need to disable it manually.
- */
- if (trans->cfg->base_params->scd_chain_ext_wa)
- iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
-
- iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue,
- trans_pcie->cmd_fifo,
- trans_pcie->cmd_q_wdg_timeout);
-
- /* Activate all Tx DMA/FIFO channels */
- iwl_scd_activate_fifos(trans);
-
- /* Enable DMA channel */
- for (chan = 0; chan < FH_TCSR_CHNL_NUM; chan++)
- iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan),
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
-
- /* Update FH chicken bits */
- reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG);
- iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG,
- reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
-
- /* Enable L1-Active */
- if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
- iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-}
-
-void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int txq_id;
-
- for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
- txq_id++) {
- struct iwl_txq *txq = &trans_pcie->txq[txq_id];
-
- iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id),
- txq->q.dma_addr >> 8);
- iwl_pcie_txq_unmap(trans, txq_id);
- txq->q.read_ptr = 0;
- txq->q.write_ptr = 0;
- }
-
- /* Tell NIC where to find the "keep warm" buffer */
- iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
- trans_pcie->kw.dma >> 4);
-
- /*
- * Send 0 as the scd_base_addr since the device may have be reset
- * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will
- * contain garbage.
- */
- iwl_pcie_tx_start(trans, 0);
-}
-
-static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- unsigned long flags;
- int ch, ret;
- u32 mask = 0;
-
- spin_lock(&trans_pcie->irq_lock);
-
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
- goto out;
-
- /* Stop each Tx DMA channel */
- for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) {
- iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
- mask |= FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch);
- }
-
- /* Wait for DMA channels to be idle */
- ret = iwl_poll_bit(trans, FH_TSSR_TX_STATUS_REG, mask, mask, 5000);
- if (ret < 0)
- IWL_ERR(trans,
- "Failing on timeout while stopping DMA channel %d [0x%08x]\n",
- ch, iwl_read32(trans, FH_TSSR_TX_STATUS_REG));
-
- iwl_trans_release_nic_access(trans, &flags);
-
-out:
- spin_unlock(&trans_pcie->irq_lock);
-}
-
-/*
- * iwl_pcie_tx_stop - Stop all Tx DMA channels
- */
-int iwl_pcie_tx_stop(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int txq_id;
-
- /* Turn off all Tx DMA fifos */
- iwl_scd_deactivate_fifos(trans);
-
- /* Turn off all Tx DMA channels */
- iwl_pcie_tx_stop_fh(trans);
-
- /*
- * This function can be called before the op_mode disabled the
- * queues. This happens when we have an rfkill interrupt.
- * Since we stop Tx altogether - mark the queues as stopped.
- */
- memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
- memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
-
- /* This can happen: start_hw, stop_device */
- if (!trans_pcie->txq)
- return 0;
-
- /* Unmap DMA from host system and free skb's */
- for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
- txq_id++)
- iwl_pcie_txq_unmap(trans, txq_id);
-
- return 0;
-}
-
-/*
- * iwl_trans_tx_free - Free TXQ Context
- *
- * Destroy all TX DMA queues and structures
- */
-void iwl_pcie_tx_free(struct iwl_trans *trans)
-{
- int txq_id;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- /* Tx queues */
- if (trans_pcie->txq) {
- for (txq_id = 0;
- txq_id < trans->cfg->base_params->num_of_queues; txq_id++)
- iwl_pcie_txq_free(trans, txq_id);
- }
-
- kfree(trans_pcie->txq);
- trans_pcie->txq = NULL;
-
- iwl_pcie_free_dma_ptr(trans, &trans_pcie->kw);
-
- iwl_pcie_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls);
-}
-
-/*
- * iwl_pcie_tx_alloc - allocate TX context
- * Allocate all Tx DMA structures and initialize them
- */
-static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
-{
- int ret;
- int txq_id, slots_num;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues *
- sizeof(struct iwlagn_scd_bc_tbl);
-
- /*It is not allowed to alloc twice, so warn when this happens.
- * We cannot rely on the previous allocation, so free and fail */
- if (WARN_ON(trans_pcie->txq)) {
- ret = -EINVAL;
- goto error;
- }
-
- ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls,
- scd_bc_tbls_size);
- if (ret) {
- IWL_ERR(trans, "Scheduler BC Table allocation failed\n");
- goto error;
- }
-
- /* Alloc keep-warm buffer */
- ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE);
- if (ret) {
- IWL_ERR(trans, "Keep Warm allocation failed\n");
- goto error;
- }
-
- trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues,
- sizeof(struct iwl_txq), GFP_KERNEL);
- if (!trans_pcie->txq) {
- IWL_ERR(trans, "Not enough memory for txq\n");
- ret = -ENOMEM;
- goto error;
- }
-
- /* Alloc and init all Tx queues, including the command queue (#4/#9) */
- for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
- txq_id++) {
- slots_num = (txq_id == trans_pcie->cmd_queue) ?
- TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
- ret = iwl_pcie_txq_alloc(trans, &trans_pcie->txq[txq_id],
- slots_num, txq_id);
- if (ret) {
- IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
- goto error;
- }
- }
-
- return 0;
-
-error:
- iwl_pcie_tx_free(trans);
-
- return ret;
-}
-int iwl_pcie_tx_init(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int ret;
- int txq_id, slots_num;
- bool alloc = false;
-
- if (!trans_pcie->txq) {
- ret = iwl_pcie_tx_alloc(trans);
- if (ret)
- goto error;
- alloc = true;
- }
-
- spin_lock(&trans_pcie->irq_lock);
-
- /* Turn off all Tx DMA fifos */
- iwl_scd_deactivate_fifos(trans);
-
- /* Tell NIC where to find the "keep warm" buffer */
- iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
- trans_pcie->kw.dma >> 4);
-
- spin_unlock(&trans_pcie->irq_lock);
-
- /* Alloc and init all Tx queues, including the command queue (#4/#9) */
- for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
- txq_id++) {
- slots_num = (txq_id == trans_pcie->cmd_queue) ?
- TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
- ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id],
- slots_num, txq_id);
- if (ret) {
- IWL_ERR(trans, "Tx %d queue init failed\n", txq_id);
- goto error;
- }
- }
-
- iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE);
- if (trans->cfg->base_params->num_of_queues > 20)
- iwl_set_bits_prph(trans, SCD_GP_CTRL,
- SCD_GP_CTRL_ENABLE_31_QUEUES);
-
- return 0;
-error:
- /*Upon error, free only if we allocated something */
- if (alloc)
- iwl_pcie_tx_free(trans);
- return ret;
-}
-
-static inline void iwl_pcie_txq_progress(struct iwl_txq *txq)
-{
- lockdep_assert_held(&txq->lock);
-
- if (!txq->wd_timeout)
- return;
-
- /*
- * station is asleep and we send data - that must
- * be uAPSD or PS-Poll. Don't rearm the timer.
- */
- if (txq->frozen)
- return;
-
- /*
- * if empty delete timer, otherwise move timer forward
- * since we're making progress on this queue
- */
- if (txq->q.read_ptr == txq->q.write_ptr)
- del_timer(&txq->stuck_timer);
- else
- mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
-}
-
-/* Frees buffers until index _not_ inclusive */
-void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
- struct sk_buff_head *skbs)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = &trans_pcie->txq[txq_id];
- int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1);
- struct iwl_queue *q = &txq->q;
- int last_to_free;
-
- /* This function is not meant to release cmd queue*/
- if (WARN_ON(txq_id == trans_pcie->cmd_queue))
- return;
-
- 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;
-
- IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n",
- txq_id, txq->q.read_ptr, tfd_num, ssn);
-
- /*Since we free until index _not_ inclusive, the one before index is
- * the last we will free. This one must be used */
- last_to_free = iwl_queue_dec_wrap(tfd_num);
-
- if (!iwl_queue_used(q, last_to_free)) {
- IWL_ERR(trans,
- "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
- __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX,
- q->write_ptr, q->read_ptr);
- goto out;
- }
-
- if (WARN_ON(!skb_queue_empty(skbs)))
- goto out;
-
- for (;
- q->read_ptr != tfd_num;
- q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
-
- if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL))
- continue;
-
- __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb);
-
- txq->entries[txq->q.read_ptr].skb = NULL;
-
- iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq);
-
- iwl_pcie_txq_free_tfd(trans, txq);
- }
-
- iwl_pcie_txq_progress(txq);
-
- if (iwl_queue_space(&txq->q) > txq->q.low_mark)
- iwl_wake_queue(trans, txq);
-
- if (q->read_ptr == q->write_ptr) {
- IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id);
- iwl_trans_pcie_unref(trans);
- }
-
-out:
- spin_unlock_bh(&txq->lock);
-}
-
-static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
- const struct iwl_host_cmd *cmd)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int ret;
-
- lockdep_assert_held(&trans_pcie->reg_lock);
-
- if (!(cmd->flags & CMD_SEND_IN_IDLE) &&
- !trans_pcie->ref_cmd_in_flight) {
- trans_pcie->ref_cmd_in_flight = true;
- IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n");
- iwl_trans_pcie_ref(trans);
- }
-
- /*
- * wake up the NIC to make sure that the firmware will see the host
- * command - we will let the NIC sleep once all the host commands
- * returned. This needs to be done only on NICs that have
- * apmg_wake_up_wa set.
- */
- if (trans->cfg->base_params->apmg_wake_up_wa &&
- !trans_pcie->cmd_hold_nic_awake) {
- __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
- (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
- CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
- 15000);
- if (ret < 0) {
- __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
- return -EIO;
- }
- trans_pcie->cmd_hold_nic_awake = true;
- }
-
- return 0;
-}
-
-static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
- lockdep_assert_held(&trans_pcie->reg_lock);
-
- if (trans_pcie->ref_cmd_in_flight) {
- trans_pcie->ref_cmd_in_flight = false;
- IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n");
- iwl_trans_pcie_unref(trans);
- }
-
- if (trans->cfg->base_params->apmg_wake_up_wa) {
- if (WARN_ON(!trans_pcie->cmd_hold_nic_awake))
- return 0;
-
- trans_pcie->cmd_hold_nic_awake = false;
- __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- }
- return 0;
-}
-
-/*
- * iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd
- *
- * When FW advances 'R' index, all entries between old and new 'R' index
- * need to be reclaimed. As result, some free space forms. If there is
- * enough free space (> low mark), wake the stack that feeds us.
- */
-static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = &trans_pcie->txq[txq_id];
- struct iwl_queue *q = &txq->q;
- unsigned long flags;
- int nfreed = 0;
-
- lockdep_assert_held(&txq->lock);
-
- if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) {
- IWL_ERR(trans,
- "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
- __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX,
- q->write_ptr, q->read_ptr);
- return;
- }
-
- for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx;
- q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
-
- if (nfreed++ > 0) {
- IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
- idx, q->write_ptr, q->read_ptr);
- iwl_force_nmi(trans);
- }
- }
-
- if (q->read_ptr == q->write_ptr) {
- spin_lock_irqsave(&trans_pcie->reg_lock, flags);
- iwl_pcie_clear_cmd_in_flight(trans);
- spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
- }
-
- iwl_pcie_txq_progress(txq);
-}
-
-static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid,
- u16 txq_id)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u32 tbl_dw_addr;
- u32 tbl_dw;
- u16 scd_q2ratid;
-
- scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK;
-
- tbl_dw_addr = trans_pcie->scd_base_addr +
- SCD_TRANS_TBL_OFFSET_QUEUE(txq_id);
-
- tbl_dw = iwl_trans_read_mem32(trans, tbl_dw_addr);
-
- if (txq_id & 0x1)
- tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
- else
- tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
-
- iwl_trans_write_mem32(trans, tbl_dw_addr, tbl_dw);
-
- return 0;
-}
-
-/* Receiver address (actually, Rx station's index into station table),
- * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
-#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid))
-
-void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn,
- const struct iwl_trans_txq_scd_cfg *cfg,
- unsigned int wdg_timeout)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = &trans_pcie->txq[txq_id];
- int fifo = -1;
-
- if (test_and_set_bit(txq_id, trans_pcie->queue_used))
- WARN_ONCE(1, "queue %d already used - expect issues", txq_id);
-
- txq->wd_timeout = msecs_to_jiffies(wdg_timeout);
-
- if (cfg) {
- fifo = cfg->fifo;
-
- /* Disable the scheduler prior configuring the cmd queue */
- if (txq_id == trans_pcie->cmd_queue &&
- trans_pcie->scd_set_active)
- iwl_scd_enable_set_active(trans, 0);
-
- /* Stop this Tx queue before configuring it */
- iwl_scd_txq_set_inactive(trans, txq_id);
-
- /* Set this queue as a chain-building queue unless it is CMD */
- if (txq_id != trans_pcie->cmd_queue)
- iwl_scd_txq_set_chain(trans, txq_id);
-
- if (cfg->aggregate) {
- u16 ra_tid = BUILD_RAxTID(cfg->sta_id, cfg->tid);
-
- /* Map receiver-address / traffic-ID to this queue */
- iwl_pcie_txq_set_ratid_map(trans, ra_tid, txq_id);
-
- /* enable aggregations for the queue */
- iwl_scd_txq_enable_agg(trans, txq_id);
- txq->ampdu = true;
- } else {
- /*
- * disable aggregations for the queue, this will also
- * make the ra_tid mapping configuration irrelevant
- * since it is now a non-AGG queue.
- */
- iwl_scd_txq_disable_agg(trans, txq_id);
-
- ssn = txq->q.read_ptr;
- }
- }
-
- /* Place first TFD at index corresponding to start sequence number.
- * Assumes that ssn_idx is valid (!= 0xFFF) */
- txq->q.read_ptr = (ssn & 0xff);
- txq->q.write_ptr = (ssn & 0xff);
- iwl_write_direct32(trans, HBUS_TARG_WRPTR,
- (ssn & 0xff) | (txq_id << 8));
-
- if (cfg) {
- u8 frame_limit = cfg->frame_limit;
-
- iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn);
-
- /* Set up Tx window size and frame limit for this queue */
- iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr +
- SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0);
- iwl_trans_write_mem32(trans,
- trans_pcie->scd_base_addr +
- SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
- ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
- SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
- ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
- SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
-
- /* Set up status area in SRAM, map to Tx DMA/FIFO, activate */
- iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
- (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
- (cfg->fifo << SCD_QUEUE_STTS_REG_POS_TXF) |
- (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
- SCD_QUEUE_STTS_REG_MSK);
-
- /* enable the scheduler for this queue (only) */
- if (txq_id == trans_pcie->cmd_queue &&
- trans_pcie->scd_set_active)
- iwl_scd_enable_set_active(trans, BIT(txq_id));
-
- IWL_DEBUG_TX_QUEUES(trans,
- "Activate queue %d on FIFO %d WrPtr: %d\n",
- txq_id, fifo, ssn & 0xff);
- } else {
- IWL_DEBUG_TX_QUEUES(trans,
- "Activate queue %d WrPtr: %d\n",
- txq_id, ssn & 0xff);
- }
-
- txq->active = true;
-}
-
-void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id,
- bool configure_scd)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u32 stts_addr = trans_pcie->scd_base_addr +
- SCD_TX_STTS_QUEUE_OFFSET(txq_id);
- static const u32 zero_val[4] = {};
-
- trans_pcie->txq[txq_id].frozen_expiry_remainder = 0;
- trans_pcie->txq[txq_id].frozen = false;
-
- /*
- * Upon HW Rfkill - we stop the device, and then stop the queues
- * in the op_mode. Just for the sake of the simplicity of the op_mode,
- * allow the op_mode to call txq_disable after it already called
- * stop_device.
- */
- if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) {
- WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status),
- "queue %d not used", txq_id);
- return;
- }
-
- if (configure_scd) {
- iwl_scd_txq_set_inactive(trans, txq_id);
-
- iwl_trans_write_mem(trans, stts_addr, (void *)zero_val,
- 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);
-}
-
-/*************** HOST COMMAND QUEUE FUNCTIONS *****/
-
-/*
- * iwl_pcie_enqueue_hcmd - enqueue a uCode command
- * @priv: device private data point
- * @cmd: a pointer to the ucode command structure
- *
- * The function returns < 0 values to indicate the operation
- * failed. On success, it returns the index (>= 0) of command in the
- * command queue.
- */
-static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
- struct iwl_host_cmd *cmd)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
- struct iwl_queue *q = &txq->q;
- struct iwl_device_cmd *out_cmd;
- struct iwl_cmd_meta *out_meta;
- unsigned long flags;
- void *dup_buf = NULL;
- dma_addr_t phys_addr;
- int idx;
- u16 copy_size, cmd_size, scratch_size;
- bool had_nocopy = false;
- u8 group_id = iwl_cmd_groupid(cmd->id);
- int i, ret;
- u32 cmd_pos;
- const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD];
- u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];
-
- if (WARN(!trans_pcie->wide_cmd_header &&
- group_id > IWL_ALWAYS_LONG_GROUP,
- "unsupported wide command %#x\n", cmd->id))
- return -EINVAL;
-
- if (group_id != 0) {
- copy_size = sizeof(struct iwl_cmd_header_wide);
- cmd_size = sizeof(struct iwl_cmd_header_wide);
- } else {
- copy_size = sizeof(struct iwl_cmd_header);
- cmd_size = sizeof(struct iwl_cmd_header);
- }
-
- /* need one for the header if the first is NOCOPY */
- BUILD_BUG_ON(IWL_MAX_CMD_TBS_PER_TFD > IWL_NUM_OF_TBS - 1);
-
- for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
- cmddata[i] = cmd->data[i];
- cmdlen[i] = cmd->len[i];
-
- if (!cmd->len[i])
- continue;
-
- /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */
- if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
- int copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
-
- if (copy > cmdlen[i])
- copy = cmdlen[i];
- cmdlen[i] -= copy;
- cmddata[i] += copy;
- copy_size += copy;
- }
-
- if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) {
- had_nocopy = true;
- if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) {
- idx = -EINVAL;
- goto free_dup_buf;
- }
- } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) {
- /*
- * This is also a chunk that isn't copied
- * to the static buffer so set had_nocopy.
- */
- had_nocopy = true;
-
- /* only allowed once */
- if (WARN_ON(dup_buf)) {
- idx = -EINVAL;
- goto free_dup_buf;
- }
-
- dup_buf = kmemdup(cmddata[i], cmdlen[i],
- GFP_ATOMIC);
- if (!dup_buf)
- return -ENOMEM;
- } else {
- /* NOCOPY must not be followed by normal! */
- if (WARN_ON(had_nocopy)) {
- idx = -EINVAL;
- goto free_dup_buf;
- }
- copy_size += cmdlen[i];
- }
- cmd_size += cmd->len[i];
- }
-
- /*
- * If any of the command structures end up being larger than
- * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically
- * allocated into separate TFDs, then we will need to
- * increase the size of the buffers.
- */
- if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE,
- "Command %s (%#x) is too large (%d bytes)\n",
- get_cmd_string(trans_pcie, cmd->id), cmd->id, copy_size)) {
- idx = -EINVAL;
- goto free_dup_buf;
- }
-
- spin_lock_bh(&txq->lock);
-
- if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
- spin_unlock_bh(&txq->lock);
-
- IWL_ERR(trans, "No space in command queue\n");
- iwl_op_mode_cmd_queue_full(trans->op_mode);
- idx = -ENOSPC;
- goto free_dup_buf;
- }
-
- idx = get_cmd_index(q, q->write_ptr);
- out_cmd = txq->entries[idx].cmd;
- out_meta = &txq->entries[idx].meta;
-
- memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */
- if (cmd->flags & CMD_WANT_SKB)
- out_meta->source = cmd;
-
- /* set up the header */
- if (group_id != 0) {
- out_cmd->hdr_wide.cmd = iwl_cmd_opcode(cmd->id);
- out_cmd->hdr_wide.group_id = group_id;
- out_cmd->hdr_wide.version = iwl_cmd_version(cmd->id);
- out_cmd->hdr_wide.length =
- cpu_to_le16(cmd_size -
- sizeof(struct iwl_cmd_header_wide));
- out_cmd->hdr_wide.reserved = 0;
- out_cmd->hdr_wide.sequence =
- cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) |
- INDEX_TO_SEQ(q->write_ptr));
-
- cmd_pos = sizeof(struct iwl_cmd_header_wide);
- copy_size = sizeof(struct iwl_cmd_header_wide);
- } else {
- out_cmd->hdr.cmd = iwl_cmd_opcode(cmd->id);
- out_cmd->hdr.sequence =
- cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) |
- INDEX_TO_SEQ(q->write_ptr));
- out_cmd->hdr.group_id = 0;
-
- cmd_pos = sizeof(struct iwl_cmd_header);
- copy_size = sizeof(struct iwl_cmd_header);
- }
-
- /* and copy the data that needs to be copied */
- for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
- int copy;
-
- if (!cmd->len[i])
- continue;
-
- /* copy everything if not nocopy/dup */
- if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
- IWL_HCMD_DFL_DUP))) {
- copy = cmd->len[i];
-
- memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
- cmd_pos += copy;
- copy_size += copy;
- continue;
- }
-
- /*
- * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied
- * in total (for the scratchbuf handling), but copy up to what
- * we can fit into the payload for debug dump purposes.
- */
- copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]);
-
- memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
- cmd_pos += copy;
-
- /* However, treat copy_size the proper way, we need it below */
- if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
- copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
-
- if (copy > cmd->len[i])
- copy = cmd->len[i];
- copy_size += copy;
- }
- }
-
- IWL_DEBUG_HC(trans,
- "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n",
- get_cmd_string(trans_pcie, out_cmd->hdr.cmd),
- group_id, out_cmd->hdr.cmd,
- le16_to_cpu(out_cmd->hdr.sequence),
- cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue);
-
- /* start the TFD with the scratchbuf */
- scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE);
- memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
- iwl_pcie_txq_build_tfd(trans, txq,
- iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
- scratch_size, true);
-
- /* map first command fragment, if any remains */
- if (copy_size > scratch_size) {
- phys_addr = dma_map_single(trans->dev,
- ((u8 *)&out_cmd->hdr) + scratch_size,
- copy_size - scratch_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(trans->dev, phys_addr)) {
- iwl_pcie_tfd_unmap(trans, out_meta,
- &txq->tfds[q->write_ptr]);
- idx = -ENOMEM;
- goto out;
- }
-
- iwl_pcie_txq_build_tfd(trans, txq, phys_addr,
- copy_size - scratch_size, false);
- }
-
- /* map the remaining (adjusted) nocopy/dup fragments */
- for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
- const void *data = cmddata[i];
-
- if (!cmdlen[i])
- continue;
- if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
- IWL_HCMD_DFL_DUP)))
- continue;
- if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP)
- data = dup_buf;
- phys_addr = dma_map_single(trans->dev, (void *)data,
- cmdlen[i], DMA_TO_DEVICE);
- if (dma_mapping_error(trans->dev, phys_addr)) {
- iwl_pcie_tfd_unmap(trans, out_meta,
- &txq->tfds[q->write_ptr]);
- idx = -ENOMEM;
- goto out;
- }
-
- iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false);
- }
-
- BUILD_BUG_ON(IWL_NUM_OF_TBS + CMD_TB_BITMAP_POS >
- sizeof(out_meta->flags) * BITS_PER_BYTE);
- out_meta->flags = cmd->flags;
- if (WARN_ON_ONCE(txq->entries[idx].free_buf))
- kzfree(txq->entries[idx].free_buf);
- txq->entries[idx].free_buf = dup_buf;
-
- trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide);
-
- /* start timer if queue currently empty */
- if (q->read_ptr == q->write_ptr && txq->wd_timeout)
- mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
-
- spin_lock_irqsave(&trans_pcie->reg_lock, flags);
- ret = iwl_pcie_set_cmd_in_flight(trans, cmd);
- if (ret < 0) {
- idx = ret;
- spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
- goto out;
- }
-
- /* Increment and update queue's write index */
- q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
- iwl_pcie_txq_inc_wr_ptr(trans, txq);
-
- spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
-
- out:
- spin_unlock_bh(&txq->lock);
- free_dup_buf:
- if (idx < 0)
- kfree(dup_buf);
- return idx;
-}
-
-/*
- * iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them
- * @rxb: Rx buffer to reclaim
- *
- * If an Rx buffer has an async callback associated with it the callback
- * will be executed. The attached skb (if present) will only be freed
- * if the callback returns 1
- */
-void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u16 sequence = le16_to_cpu(pkt->hdr.sequence);
- int txq_id = SEQ_TO_QUEUE(sequence);
- int index = SEQ_TO_INDEX(sequence);
- int cmd_index;
- struct iwl_device_cmd *cmd;
- struct iwl_cmd_meta *meta;
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
-
- /* If a Tx command is being handled and it isn't in the actual
- * command queue then there a command routing bug has been introduced
- * in the queue management code. */
- if (WARN(txq_id != trans_pcie->cmd_queue,
- "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
- txq_id, trans_pcie->cmd_queue, sequence,
- trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr,
- trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) {
- iwl_print_hex_error(trans, pkt, 32);
- return;
- }
-
- spin_lock_bh(&txq->lock);
-
- cmd_index = get_cmd_index(&txq->q, index);
- cmd = txq->entries[cmd_index].cmd;
- meta = &txq->entries[cmd_index].meta;
-
- iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]);
-
- /* Input error checking is done when commands are added to queue. */
- if (meta->flags & CMD_WANT_SKB) {
- struct page *p = rxb_steal_page(rxb);
-
- meta->source->resp_pkt = pkt;
- meta->source->_rx_page_addr = (unsigned long)page_address(p);
- meta->source->_rx_page_order = trans_pcie->rx_page_order;
- }
-
- iwl_pcie_cmdq_reclaim(trans, txq_id, index);
-
- if (!(meta->flags & CMD_ASYNC)) {
- if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) {
- IWL_WARN(trans,
- "HCMD_ACTIVE already clear for command %s\n",
- get_cmd_string(trans_pcie, cmd->hdr.cmd));
- }
- clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
- IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
- get_cmd_string(trans_pcie, cmd->hdr.cmd));
- wake_up(&trans_pcie->wait_command_queue);
- }
-
- meta->flags = 0;
-
- spin_unlock_bh(&txq->lock);
-}
-
-#define HOST_COMPLETE_TIMEOUT (2 * HZ)
-
-static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
- struct iwl_host_cmd *cmd)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int ret;
-
- /* An asynchronous command can not expect an SKB to be set. */
- if (WARN_ON(cmd->flags & CMD_WANT_SKB))
- return -EINVAL;
-
- ret = iwl_pcie_enqueue_hcmd(trans, cmd);
- if (ret < 0) {
- IWL_ERR(trans,
- "Error sending %s: enqueue_hcmd failed: %d\n",
- get_cmd_string(trans_pcie, cmd->id), ret);
- return ret;
- }
- return 0;
-}
-
-static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
- struct iwl_host_cmd *cmd)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int cmd_idx;
- int ret;
-
- IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
- get_cmd_string(trans_pcie, cmd->id));
-
- if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE,
- &trans->status),
- "Command %s: a command is already active!\n",
- get_cmd_string(trans_pcie, cmd->id)))
- return -EIO;
-
- IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
- get_cmd_string(trans_pcie, cmd->id));
-
- cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
- if (cmd_idx < 0) {
- ret = cmd_idx;
- clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
- IWL_ERR(trans,
- "Error sending %s: enqueue_hcmd failed: %d\n",
- get_cmd_string(trans_pcie, cmd->id), ret);
- return ret;
- }
-
- ret = wait_event_timeout(trans_pcie->wait_command_queue,
- !test_bit(STATUS_SYNC_HCMD_ACTIVE,
- &trans->status),
- HOST_COMPLETE_TIMEOUT);
- if (!ret) {
- struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
- struct iwl_queue *q = &txq->q;
-
- IWL_ERR(trans, "Error sending %s: time out after %dms.\n",
- get_cmd_string(trans_pcie, cmd->id),
- jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
-
- IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n",
- q->read_ptr, q->write_ptr);
-
- clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
- IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
- get_cmd_string(trans_pcie, cmd->id));
- ret = -ETIMEDOUT;
-
- iwl_force_nmi(trans);
- iwl_trans_fw_error(trans);
-
- goto cancel;
- }
-
- if (test_bit(STATUS_FW_ERROR, &trans->status)) {
- IWL_ERR(trans, "FW error in SYNC CMD %s\n",
- get_cmd_string(trans_pcie, cmd->id));
- dump_stack();
- ret = -EIO;
- goto cancel;
- }
-
- if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
- test_bit(STATUS_RFKILL, &trans->status)) {
- IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n");
- ret = -ERFKILL;
- goto cancel;
- }
-
- if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) {
- IWL_ERR(trans, "Error: Response NULL in '%s'\n",
- get_cmd_string(trans_pcie, cmd->id));
- ret = -EIO;
- goto cancel;
- }
-
- return 0;
-
-cancel:
- if (cmd->flags & CMD_WANT_SKB) {
- /*
- * Cancel the CMD_WANT_SKB flag for the cmd in the
- * TX cmd queue. Otherwise in case the cmd comes
- * in later, it will possibly set an invalid
- * address (cmd->meta.source).
- */
- trans_pcie->txq[trans_pcie->cmd_queue].
- entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB;
- }
-
- if (cmd->resp_pkt) {
- iwl_free_resp(cmd);
- cmd->resp_pkt = NULL;
- }
-
- return ret;
-}
-
-int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
-{
- if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
- test_bit(STATUS_RFKILL, &trans->status)) {
- IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
- cmd->id);
- return -ERFKILL;
- }
-
- if (cmd->flags & CMD_ASYNC)
- return iwl_pcie_send_hcmd_async(trans, cmd);
-
- /* We still can fail on RFKILL that can be asserted while we wait */
- return iwl_pcie_send_hcmd_sync(trans, cmd);
-}
-
-int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, int txq_id)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct ieee80211_hdr *hdr;
- struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
- struct iwl_cmd_meta *out_meta;
- struct iwl_txq *txq;
- struct iwl_queue *q;
- dma_addr_t tb0_phys, tb1_phys, scratch_phys;
- void *tb1_addr;
- u16 len, tb1_len, tb2_len;
- bool wait_write_ptr;
- __le16 fc;
- u8 hdr_len;
- u16 wifi_seq;
- int i;
-
- txq = &trans_pcie->txq[txq_id];
- q = &txq->q;
-
- if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used),
- "TX on unused queue %d\n", txq_id))
- return -EINVAL;
-
- if (skb_is_nonlinear(skb) &&
- skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS &&
- __skb_linearize(skb))
- return -ENOMEM;
-
- /* mac80211 always puts the full header into the SKB's head,
- * so there's no need to check if it's readable there
- */
- hdr = (struct ieee80211_hdr *)skb->data;
- fc = hdr->frame_control;
- hdr_len = ieee80211_hdrlen(fc);
-
- spin_lock(&txq->lock);
-
- /* In AGG mode, the index in the ring must correspond to the WiFi
- * sequence number. This is a HW requirements to help the SCD to parse
- * the BA.
- * Check here that the packets are in the right place on the ring.
- */
- wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
- WARN_ONCE(txq->ampdu &&
- (wifi_seq & 0xff) != q->write_ptr,
- "Q: %d WiFi Seq %d tfdNum %d",
- txq_id, wifi_seq, q->write_ptr);
-
- /* Set up driver data for this TFD */
- txq->entries[q->write_ptr].skb = skb;
- txq->entries[q->write_ptr].cmd = dev_cmd;
-
- dev_cmd->hdr.sequence =
- cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |
- INDEX_TO_SEQ(q->write_ptr)));
-
- tb0_phys = iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr);
- scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) +
- offsetof(struct iwl_tx_cmd, scratch);
-
- tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
- tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
-
- /* Set up first empty entry in queue's array of Tx/cmd buffers */
- out_meta = &txq->entries[q->write_ptr].meta;
- out_meta->flags = 0;
-
- /*
- * The second TB (tb1) points to the remainder of the TX command
- * and the 802.11 header - dword aligned size
- * (This calculation modifies the TX command, so do it before the
- * setup of the first TB)
- */
- len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) +
- hdr_len - IWL_HCMD_SCRATCHBUF_SIZE;
- tb1_len = ALIGN(len, 4);
-
- /* Tell NIC about any 2-byte padding after MAC header */
- if (tb1_len != len)
- tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
-
- /* The first TB points to the scratchbuf data - min_copy bytes */
- memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr,
- IWL_HCMD_SCRATCHBUF_SIZE);
- iwl_pcie_txq_build_tfd(trans, txq, tb0_phys,
- IWL_HCMD_SCRATCHBUF_SIZE, true);
-
- /* there must be data left over for TB1 or this code must be changed */
- BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE);
-
- /* map the data for TB1 */
- tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_HCMD_SCRATCHBUF_SIZE;
- tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(trans->dev, tb1_phys)))
- goto out_err;
- iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
-
- /*
- * Set up TFD's third entry to point directly to remainder
- * of skb's head, if any
- */
- tb2_len = skb_headlen(skb) - hdr_len;
- if (tb2_len > 0) {
- dma_addr_t tb2_phys = dma_map_single(trans->dev,
- skb->data + hdr_len,
- tb2_len, DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) {
- iwl_pcie_tfd_unmap(trans, out_meta,
- &txq->tfds[q->write_ptr]);
- goto out_err;
- }
- iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
- }
-
- /* set up the remaining entries to point to the data */
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- dma_addr_t tb_phys;
- int tb_idx;
-
- if (!skb_frag_size(frag))
- continue;
-
- tb_phys = skb_frag_dma_map(trans->dev, frag, 0,
- skb_frag_size(frag), DMA_TO_DEVICE);
-
- if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
- iwl_pcie_tfd_unmap(trans, out_meta,
- &txq->tfds[q->write_ptr]);
- goto out_err;
- }
- tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys,
- skb_frag_size(frag), false);
-
- out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS);
- }
-
- /* Set up entry for this TFD in Tx byte-count array */
- iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len));
-
- trace_iwlwifi_dev_tx(trans->dev, skb,
- &txq->tfds[txq->q.write_ptr],
- sizeof(struct iwl_tfd),
- &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len,
- skb->data + hdr_len, tb2_len);
- trace_iwlwifi_dev_tx_data(trans->dev, skb,
- hdr_len, skb->len - hdr_len);
-
- wait_write_ptr = ieee80211_has_morefrags(fc);
-
- /* start timer if queue currently empty */
- if (q->read_ptr == q->write_ptr) {
- if (txq->wd_timeout) {
- /*
- * If the TXQ is active, then set the timer, if not,
- * set the timer in remainder so that the timer will
- * be armed with the right value when the station will
- * wake up.
- */
- if (!txq->frozen)
- mod_timer(&txq->stuck_timer,
- jiffies + txq->wd_timeout);
- else
- txq->frozen_expiry_remainder = txq->wd_timeout;
- }
- IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id);
- iwl_trans_pcie_ref(trans);
- }
-
- /* Tell device the write index *just past* this latest filled TFD */
- q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
- if (!wait_write_ptr)
- iwl_pcie_txq_inc_wr_ptr(trans, txq);
-
- /*
- * At this point the frame is "transmitted" successfully
- * and we will get a TX status notification eventually.
- */
- if (iwl_queue_space(q) < q->high_mark) {
- if (wait_write_ptr)
- iwl_pcie_txq_inc_wr_ptr(trans, txq);
- else
- iwl_stop_queue(trans, txq);
- }
- spin_unlock(&txq->lock);
- return 0;
-out_err:
- spin_unlock(&txq->lock);
- return -1;
-}
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
deleted file mode 100644
index 8317afd065b4..000000000000
--- a/drivers/net/wireless/libertas/cfg.c
+++ /dev/null
@@ -1,2215 +0,0 @@
-/*
- * Implement cfg80211 ("iw") support.
- *
- * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany
- * Holger Schurig <hs4233@mail.mn-solutions.de>
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/hardirq.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/slab.h>
-#include <linux/ieee80211.h>
-#include <net/cfg80211.h>
-#include <asm/unaligned.h>
-
-#include "decl.h"
-#include "cfg.h"
-#include "cmd.h"
-#include "mesh.h"
-
-
-#define CHAN2G(_channel, _freq, _flags) { \
- .band = IEEE80211_BAND_2GHZ, \
- .center_freq = (_freq), \
- .hw_value = (_channel), \
- .flags = (_flags), \
- .max_antenna_gain = 0, \
- .max_power = 30, \
-}
-
-static struct ieee80211_channel lbs_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),
-};
-
-#define RATETAB_ENT(_rate, _hw_value, _flags) { \
- .bitrate = (_rate), \
- .hw_value = (_hw_value), \
- .flags = (_flags), \
-}
-
-
-/* Table 6 in section 3.2.1.1 */
-static struct ieee80211_rate lbs_rates[] = {
- RATETAB_ENT(10, 0, 0),
- RATETAB_ENT(20, 1, 0),
- RATETAB_ENT(55, 2, 0),
- RATETAB_ENT(110, 3, 0),
- RATETAB_ENT(60, 9, 0),
- RATETAB_ENT(90, 6, 0),
- RATETAB_ENT(120, 7, 0),
- RATETAB_ENT(180, 8, 0),
- RATETAB_ENT(240, 9, 0),
- RATETAB_ENT(360, 10, 0),
- RATETAB_ENT(480, 11, 0),
- RATETAB_ENT(540, 12, 0),
-};
-
-static struct ieee80211_supported_band lbs_band_2ghz = {
- .channels = lbs_2ghz_channels,
- .n_channels = ARRAY_SIZE(lbs_2ghz_channels),
- .bitrates = lbs_rates,
- .n_bitrates = ARRAY_SIZE(lbs_rates),
-};
-
-
-static const u32 cipher_suites[] = {
- WLAN_CIPHER_SUITE_WEP40,
- WLAN_CIPHER_SUITE_WEP104,
- WLAN_CIPHER_SUITE_TKIP,
- WLAN_CIPHER_SUITE_CCMP,
-};
-
-/* Time to stay on the channel */
-#define LBS_DWELL_PASSIVE 100
-#define LBS_DWELL_ACTIVE 40
-
-
-/***************************************************************************
- * Misc utility functions
- *
- * TLVs are Marvell specific. They are very similar to IEs, they have the
- * same structure: type, length, data*. The only difference: for IEs, the
- * type and length are u8, but for TLVs they're __le16.
- */
-
-/*
- * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1
- * in the firmware spec
- */
-static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type)
-{
- int ret = -ENOTSUPP;
-
- switch (auth_type) {
- case NL80211_AUTHTYPE_OPEN_SYSTEM:
- case NL80211_AUTHTYPE_SHARED_KEY:
- ret = auth_type;
- break;
- case NL80211_AUTHTYPE_AUTOMATIC:
- ret = NL80211_AUTHTYPE_OPEN_SYSTEM;
- break;
- case NL80211_AUTHTYPE_NETWORK_EAP:
- ret = 0x80;
- break;
- default:
- /* silence compiler */
- break;
- }
- return ret;
-}
-
-
-/*
- * Various firmware commands need the list of supported rates, but with
- * the hight-bit set for basic rates
- */
-static int lbs_add_rates(u8 *rates)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
- u8 rate = lbs_rates[i].bitrate / 5;
- if (rate == 0x02 || rate == 0x04 ||
- rate == 0x0b || rate == 0x16)
- rate |= 0x80;
- rates[i] = rate;
- }
- return ARRAY_SIZE(lbs_rates);
-}
-
-
-/***************************************************************************
- * TLV utility functions
- *
- * TLVs are Marvell specific. They are very similar to IEs, they have the
- * same structure: type, length, data*. The only difference: for IEs, the
- * type and length are u8, but for TLVs they're __le16.
- */
-
-
-/*
- * Add ssid TLV
- */
-#define LBS_MAX_SSID_TLV_SIZE \
- (sizeof(struct mrvl_ie_header) \
- + IEEE80211_MAX_SSID_LEN)
-
-static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len)
-{
- struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv;
-
- /*
- * TLV-ID SSID 00 00
- * length 06 00
- * ssid 4d 4e 54 45 53 54
- */
- ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
- ssid_tlv->header.len = cpu_to_le16(ssid_len);
- memcpy(ssid_tlv->ssid, ssid, ssid_len);
- return sizeof(ssid_tlv->header) + ssid_len;
-}
-
-
-/*
- * Add channel list TLV (section 8.4.2)
- *
- * Actual channel data comes from priv->wdev->wiphy->channels.
- */
-#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \
- (sizeof(struct mrvl_ie_header) \
- + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset)))
-
-static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv,
- int last_channel, int active_scan)
-{
- int chanscanparamsize = sizeof(struct chanscanparamset) *
- (last_channel - priv->scan_channel);
-
- struct mrvl_ie_header *header = (void *) tlv;
-
- /*
- * TLV-ID CHANLIST 01 01
- * length 0e 00
- * channel 00 01 00 00 00 64 00
- * radio type 00
- * channel 01
- * scan type 00
- * min scan time 00 00
- * max scan time 64 00
- * channel 2 00 02 00 00 00 64 00
- *
- */
-
- header->type = cpu_to_le16(TLV_TYPE_CHANLIST);
- header->len = cpu_to_le16(chanscanparamsize);
- tlv += sizeof(struct mrvl_ie_header);
-
- /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel,
- last_channel); */
- memset(tlv, 0, chanscanparamsize);
-
- while (priv->scan_channel < last_channel) {
- struct chanscanparamset *param = (void *) tlv;
-
- param->radiotype = CMD_SCAN_RADIO_TYPE_BG;
- param->channumber =
- priv->scan_req->channels[priv->scan_channel]->hw_value;
- if (active_scan) {
- param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE);
- } else {
- param->chanscanmode.passivescan = 1;
- param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE);
- }
- tlv += sizeof(struct chanscanparamset);
- priv->scan_channel++;
- }
- return sizeof(struct mrvl_ie_header) + chanscanparamsize;
-}
-
-
-/*
- * Add rates TLV
- *
- * The rates are in lbs_bg_rates[], but for the 802.11b
- * rates the high bit is set. We add this TLV only because
- * there's a firmware which otherwise doesn't report all
- * APs in range.
- */
-#define LBS_MAX_RATES_TLV_SIZE \
- (sizeof(struct mrvl_ie_header) \
- + (ARRAY_SIZE(lbs_rates)))
-
-/* Adds a TLV with all rates the hardware supports */
-static int lbs_add_supported_rates_tlv(u8 *tlv)
-{
- size_t i;
- struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
-
- /*
- * TLV-ID RATES 01 00
- * length 0e 00
- * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c
- */
- rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
- tlv += sizeof(rate_tlv->header);
- i = lbs_add_rates(tlv);
- tlv += i;
- rate_tlv->header.len = cpu_to_le16(i);
- return sizeof(rate_tlv->header) + i;
-}
-
-/* Add common rates from a TLV and return the new end of the TLV */
-static u8 *
-add_ie_rates(u8 *tlv, const u8 *ie, int *nrates)
-{
- int hw, ap, ap_max = ie[1];
- u8 hw_rate;
-
- /* Advance past IE header */
- ie += 2;
-
- lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max);
-
- for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
- hw_rate = lbs_rates[hw].bitrate / 5;
- for (ap = 0; ap < ap_max; ap++) {
- if (hw_rate == (ie[ap] & 0x7f)) {
- *tlv++ = ie[ap];
- *nrates = *nrates + 1;
- }
- }
- }
- return tlv;
-}
-
-/*
- * Adds a TLV with all rates the hardware *and* BSS supports.
- */
-static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
-{
- struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
- const u8 *rates_eid, *ext_rates_eid;
- int n = 0;
-
- rcu_read_lock();
- rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
- ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
-
- /*
- * 01 00 TLV_TYPE_RATES
- * 04 00 len
- * 82 84 8b 96 rates
- */
- rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
- tlv += sizeof(rate_tlv->header);
-
- /* Add basic rates */
- if (rates_eid) {
- tlv = add_ie_rates(tlv, rates_eid, &n);
-
- /* Add extended rates, if any */
- if (ext_rates_eid)
- tlv = add_ie_rates(tlv, ext_rates_eid, &n);
- } else {
- lbs_deb_assoc("assoc: bss had no basic rate IE\n");
- /* Fallback: add basic 802.11b rates */
- *tlv++ = 0x82;
- *tlv++ = 0x84;
- *tlv++ = 0x8b;
- *tlv++ = 0x96;
- n = 4;
- }
- rcu_read_unlock();
-
- rate_tlv->header.len = cpu_to_le16(n);
- return sizeof(rate_tlv->header) + n;
-}
-
-
-/*
- * Add auth type TLV.
- *
- * This is only needed for newer firmware (V9 and up).
- */
-#define LBS_MAX_AUTH_TYPE_TLV_SIZE \
- sizeof(struct mrvl_ie_auth_type)
-
-static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type)
-{
- struct mrvl_ie_auth_type *auth = (void *) tlv;
-
- /*
- * 1f 01 TLV_TYPE_AUTH_TYPE
- * 01 00 len
- * 01 auth type
- */
- auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
- auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header));
- auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type));
- return sizeof(*auth);
-}
-
-
-/*
- * Add channel (phy ds) TLV
- */
-#define LBS_MAX_CHANNEL_TLV_SIZE \
- sizeof(struct mrvl_ie_header)
-
-static int lbs_add_channel_tlv(u8 *tlv, u8 channel)
-{
- struct mrvl_ie_ds_param_set *ds = (void *) tlv;
-
- /*
- * 03 00 TLV_TYPE_PHY_DS
- * 01 00 len
- * 06 channel
- */
- ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
- ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header));
- ds->channel = channel;
- return sizeof(*ds);
-}
-
-
-/*
- * Add (empty) CF param TLV of the form:
- */
-#define LBS_MAX_CF_PARAM_TLV_SIZE \
- sizeof(struct mrvl_ie_header)
-
-static int lbs_add_cf_param_tlv(u8 *tlv)
-{
- struct mrvl_ie_cf_param_set *cf = (void *)tlv;
-
- /*
- * 04 00 TLV_TYPE_CF
- * 06 00 len
- * 00 cfpcnt
- * 00 cfpperiod
- * 00 00 cfpmaxduration
- * 00 00 cfpdurationremaining
- */
- cf->header.type = cpu_to_le16(TLV_TYPE_CF);
- cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header));
- return sizeof(*cf);
-}
-
-/*
- * Add WPA TLV
- */
-#define LBS_MAX_WPA_TLV_SIZE \
- (sizeof(struct mrvl_ie_header) \
- + 128 /* TODO: I guessed the size */)
-
-static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len)
-{
- size_t tlv_len;
-
- /*
- * We need just convert an IE to an TLV. IEs use u8 for the header,
- * u8 type
- * u8 len
- * u8[] data
- * but TLVs use __le16 instead:
- * __le16 type
- * __le16 len
- * u8[] data
- */
- *tlv++ = *ie++;
- *tlv++ = 0;
- tlv_len = *tlv++ = *ie++;
- *tlv++ = 0;
- while (tlv_len--)
- *tlv++ = *ie++;
- /* the TLV is two bytes larger than the IE */
- return ie_len + 2;
-}
-
-/*
- * Set Channel
- */
-
-static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
- struct cfg80211_chan_def *chandef)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- int ret = -ENOTSUPP;
-
- lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
- chandef->chan->center_freq,
- cfg80211_get_chandef_type(chandef));
-
- if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT)
- goto out;
-
- ret = lbs_set_channel(priv, chandef->chan->hw_value);
-
- out:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy,
- struct net_device *netdev,
- struct ieee80211_channel *channel)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- int ret = -ENOTSUPP;
-
- lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d",
- netdev_name(netdev), channel->center_freq);
-
- if (netdev != priv->mesh_dev)
- goto out;
-
- ret = lbs_mesh_set_channel(priv, channel->hw_value);
-
- out:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-
-/*
- * Scanning
- */
-
-/*
- * When scanning, the firmware doesn't send a nul packet with the power-safe
- * bit to the AP. So we cannot stay away from our current channel too long,
- * otherwise we loose data. So take a "nap" while scanning every other
- * while.
- */
-#define LBS_SCAN_BEFORE_NAP 4
-
-
-/*
- * When the firmware reports back a scan-result, it gives us an "u8 rssi",
- * which isn't really an RSSI, as it becomes larger when moving away from
- * the AP. Anyway, we need to convert that into mBm.
- */
-#define LBS_SCAN_RSSI_TO_MBM(rssi) \
- ((-(int)rssi + 3)*100)
-
-static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
- struct cmd_header *resp)
-{
- struct cfg80211_bss *bss;
- struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
- int bsssize;
- const u8 *pos;
- const u8 *tsfdesc;
- int tsfsize;
- int i;
- int ret = -EILSEQ;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
-
- lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
- scanresp->nr_sets, bsssize, le16_to_cpu(resp->size));
-
- if (scanresp->nr_sets == 0) {
- ret = 0;
- goto done;
- }
-
- /*
- * The general layout of the scan response is described in chapter
- * 5.7.1. Basically we have a common part, then any number of BSS
- * descriptor sections. Finally we have section with the same number
- * of TSFs.
- *
- * cmd_ds_802_11_scan_rsp
- * cmd_header
- * pos_size
- * nr_sets
- * bssdesc 1
- * bssid
- * rssi
- * timestamp
- * intvl
- * capa
- * IEs
- * bssdesc 2
- * bssdesc n
- * MrvlIEtypes_TsfFimestamp_t
- * TSF for BSS 1
- * TSF for BSS 2
- * TSF for BSS n
- */
-
- pos = scanresp->bssdesc_and_tlvbuffer;
-
- lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer,
- scanresp->bssdescriptsize);
-
- tsfdesc = pos + bsssize;
- tsfsize = 4 + 8 * scanresp->nr_sets;
- lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize);
-
- /* Validity check: we expect a Marvell-Local TLV */
- i = get_unaligned_le16(tsfdesc);
- tsfdesc += 2;
- if (i != TLV_TYPE_TSFTIMESTAMP) {
- lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i);
- goto done;
- }
-
- /*
- * Validity check: the TLV holds TSF values with 8 bytes each, so
- * the size in the TLV must match the nr_sets value
- */
- i = get_unaligned_le16(tsfdesc);
- tsfdesc += 2;
- if (i / 8 != scanresp->nr_sets) {
- lbs_deb_scan("scan response: invalid number of TSF timestamp "
- "sets (expected %d got %d)\n", scanresp->nr_sets,
- i / 8);
- goto done;
- }
-
- for (i = 0; i < scanresp->nr_sets; i++) {
- const u8 *bssid;
- const u8 *ie;
- int left;
- int ielen;
- int rssi;
- u16 intvl;
- u16 capa;
- int chan_no = -1;
- const u8 *ssid = NULL;
- u8 ssid_len = 0;
-
- int len = get_unaligned_le16(pos);
- pos += 2;
-
- /* BSSID */
- bssid = pos;
- pos += ETH_ALEN;
- /* RSSI */
- rssi = *pos++;
- /* Packet time stamp */
- pos += 8;
- /* Beacon interval */
- intvl = get_unaligned_le16(pos);
- pos += 2;
- /* Capabilities */
- capa = get_unaligned_le16(pos);
- pos += 2;
-
- /* To find out the channel, we must parse the IEs */
- ie = pos;
- /*
- * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon
- * interval, capabilities
- */
- ielen = left = len - (6 + 1 + 8 + 2 + 2);
- while (left >= 2) {
- u8 id, elen;
- id = *pos++;
- elen = *pos++;
- left -= 2;
- if (elen > left) {
- lbs_deb_scan("scan response: invalid IE fmt\n");
- goto done;
- }
-
- if (id == WLAN_EID_DS_PARAMS)
- chan_no = *pos;
- if (id == WLAN_EID_SSID) {
- ssid = pos;
- ssid_len = elen;
- }
- left -= elen;
- pos += elen;
- }
-
- /* No channel, no luck */
- if (chan_no != -1) {
- struct wiphy *wiphy = priv->wdev->wiphy;
- int freq = ieee80211_channel_to_frequency(chan_no,
- IEEE80211_BAND_2GHZ);
- struct ieee80211_channel *channel =
- ieee80211_get_channel(wiphy, freq);
-
- lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %*pE, %d dBm\n",
- bssid, capa, chan_no, ssid_len, ssid,
- LBS_SCAN_RSSI_TO_MBM(rssi)/100);
-
- if (channel &&
- !(channel->flags & IEEE80211_CHAN_DISABLED)) {
- bss = cfg80211_inform_bss(wiphy, channel,
- CFG80211_BSS_FTYPE_UNKNOWN,
- bssid, get_unaligned_le64(tsfdesc),
- capa, intvl, ie, ielen,
- LBS_SCAN_RSSI_TO_MBM(rssi),
- GFP_KERNEL);
- cfg80211_put_bss(wiphy, bss);
- }
- } else
- lbs_deb_scan("scan response: missing BSS channel IE\n");
-
- tsfdesc += 8;
- }
- ret = 0;
-
- done:
- lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
- return ret;
-}
-
-
-/*
- * Our scan command contains a TLV, consting of a SSID TLV, a channel list
- * TLV and a rates TLV. Determine the maximum size of them:
- */
-#define LBS_SCAN_MAX_CMD_SIZE \
- (sizeof(struct cmd_ds_802_11_scan) \
- + LBS_MAX_SSID_TLV_SIZE \
- + LBS_MAX_CHANNEL_LIST_TLV_SIZE \
- + LBS_MAX_RATES_TLV_SIZE)
-
-/*
- * Assumes priv->scan_req is initialized and valid
- * Assumes priv->scan_channel is initialized
- */
-static void lbs_scan_worker(struct work_struct *work)
-{
- struct lbs_private *priv =
- container_of(work, struct lbs_private, scan_work.work);
- struct cmd_ds_802_11_scan *scan_cmd;
- u8 *tlv; /* pointer into our current, growing TLV storage area */
- int last_channel;
- int running, carrier;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL);
- if (scan_cmd == NULL)
- goto out_no_scan_cmd;
-
- /* prepare fixed part of scan command */
- scan_cmd->bsstype = CMD_BSS_TYPE_ANY;
-
- /* stop network while we're away from our main channel */
- running = !netif_queue_stopped(priv->dev);
- carrier = netif_carrier_ok(priv->dev);
- if (running)
- netif_stop_queue(priv->dev);
- if (carrier)
- netif_carrier_off(priv->dev);
-
- /* prepare fixed part of scan command */
- tlv = scan_cmd->tlvbuffer;
-
- /* add SSID TLV */
- if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0)
- tlv += lbs_add_ssid_tlv(tlv,
- priv->scan_req->ssids[0].ssid,
- priv->scan_req->ssids[0].ssid_len);
-
- /* add channel TLVs */
- last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP;
- if (last_channel > priv->scan_req->n_channels)
- last_channel = priv->scan_req->n_channels;
- tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel,
- priv->scan_req->n_ssids);
-
- /* add rates TLV */
- tlv += lbs_add_supported_rates_tlv(tlv);
-
- if (priv->scan_channel < priv->scan_req->n_channels) {
- cancel_delayed_work(&priv->scan_work);
- if (netif_running(priv->dev))
- queue_delayed_work(priv->work_thread, &priv->scan_work,
- msecs_to_jiffies(300));
- }
-
- /* This is the final data we are about to send */
- scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd);
- lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd,
- sizeof(*scan_cmd));
- lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer,
- tlv - scan_cmd->tlvbuffer);
-
- __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr,
- le16_to_cpu(scan_cmd->hdr.size),
- lbs_ret_scan, 0);
-
- if (priv->scan_channel >= priv->scan_req->n_channels) {
- /* Mark scan done */
- cancel_delayed_work(&priv->scan_work);
- lbs_scan_done(priv);
- }
-
- /* Restart network */
- if (carrier)
- netif_carrier_on(priv->dev);
- if (running && !priv->tx_pending_len)
- netif_wake_queue(priv->dev);
-
- kfree(scan_cmd);
-
- /* Wake up anything waiting on scan completion */
- if (priv->scan_req == NULL) {
- lbs_deb_scan("scan: waking up waiters\n");
- wake_up_all(&priv->scan_q);
- }
-
- out_no_scan_cmd:
- lbs_deb_leave(LBS_DEB_SCAN);
-}
-
-static void _internal_start_scan(struct lbs_private *priv, bool internal,
- struct cfg80211_scan_request *request)
-{
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
- request->n_ssids, request->n_channels, request->ie_len);
-
- priv->scan_channel = 0;
- priv->scan_req = request;
- priv->internal_scan = internal;
-
- queue_delayed_work(priv->work_thread, &priv->scan_work,
- msecs_to_jiffies(50));
-
- lbs_deb_leave(LBS_DEB_CFG80211);
-}
-
-/*
- * Clean up priv->scan_req. Should be used to handle the allocation details.
- */
-void lbs_scan_done(struct lbs_private *priv)
-{
- WARN_ON(!priv->scan_req);
-
- if (priv->internal_scan)
- kfree(priv->scan_req);
- else
- cfg80211_scan_done(priv->scan_req, false);
-
- priv->scan_req = NULL;
-}
-
-static int lbs_cfg_scan(struct wiphy *wiphy,
- struct cfg80211_scan_request *request)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- if (priv->scan_req || delayed_work_pending(&priv->scan_work)) {
- /* old scan request not yet processed */
- ret = -EAGAIN;
- goto out;
- }
-
- _internal_start_scan(priv, false, request);
-
- if (priv->surpriseremoved)
- ret = -EIO;
-
- out:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-
-
-/*
- * Events
- */
-
-void lbs_send_disconnect_notification(struct lbs_private *priv,
- bool locally_generated)
-{
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated,
- GFP_KERNEL);
-
- lbs_deb_leave(LBS_DEB_CFG80211);
-}
-
-void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event)
-{
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- cfg80211_michael_mic_failure(priv->dev,
- priv->assoc_bss,
- event == MACREG_INT_CODE_MIC_ERR_MULTICAST ?
- NL80211_KEYTYPE_GROUP :
- NL80211_KEYTYPE_PAIRWISE,
- -1,
- NULL,
- GFP_KERNEL);
-
- lbs_deb_leave(LBS_DEB_CFG80211);
-}
-
-
-
-
-/*
- * Connect/disconnect
- */
-
-
-/*
- * This removes all WEP keys
- */
-static int lbs_remove_wep_keys(struct lbs_private *priv)
-{
- struct cmd_ds_802_11_set_wep cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.keyindex = cpu_to_le16(priv->wep_tx_key);
- cmd.action = cpu_to_le16(CMD_ACT_REMOVE);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
-
- lbs_deb_leave(LBS_DEB_CFG80211);
- return ret;
-}
-
-/*
- * Set WEP keys
- */
-static int lbs_set_wep_keys(struct lbs_private *priv)
-{
- struct cmd_ds_802_11_set_wep cmd;
- int i;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- /*
- * command 13 00
- * size 50 00
- * sequence xx xx
- * result 00 00
- * action 02 00 ACT_ADD
- * transmit key 00 00
- * type for key 1 01 WEP40
- * type for key 2 00
- * type for key 3 00
- * type for key 4 00
- * key 1 39 39 39 39 39 00 00 00
- * 00 00 00 00 00 00 00 00
- * key 2 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * key 3 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * key 4 00 00 00 00 00 00 00 00
- */
- if (priv->wep_key_len[0] || priv->wep_key_len[1] ||
- priv->wep_key_len[2] || priv->wep_key_len[3]) {
- /* Only set wep keys if we have at least one of them */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.keyindex = cpu_to_le16(priv->wep_tx_key);
- cmd.action = cpu_to_le16(CMD_ACT_ADD);
-
- for (i = 0; i < 4; i++) {
- switch (priv->wep_key_len[i]) {
- case WLAN_KEY_LEN_WEP40:
- cmd.keytype[i] = CMD_TYPE_WEP_40_BIT;
- break;
- case WLAN_KEY_LEN_WEP104:
- cmd.keytype[i] = CMD_TYPE_WEP_104_BIT;
- break;
- default:
- cmd.keytype[i] = 0;
- break;
- }
- memcpy(cmd.keymaterial[i], priv->wep_key[i],
- priv->wep_key_len[i]);
- }
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
- } else {
- /* Otherwise remove all wep keys */
- ret = lbs_remove_wep_keys(priv);
- }
-
- lbs_deb_leave(LBS_DEB_CFG80211);
- return ret;
-}
-
-
-/*
- * Enable/Disable RSN status
- */
-static int lbs_enable_rsn(struct lbs_private *priv, int enable)
-{
- struct cmd_ds_802_11_enable_rsn cmd;
- int ret;
-
- lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable);
-
- /*
- * cmd 2f 00
- * size 0c 00
- * sequence xx xx
- * result 00 00
- * action 01 00 ACT_SET
- * enable 01 00
- */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- cmd.enable = cpu_to_le16(enable);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
-
- lbs_deb_leave(LBS_DEB_CFG80211);
- return ret;
-}
-
-
-/*
- * Set WPA/WPA key material
- */
-
-/*
- * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we
- * get rid of WEXT, this should go into host.h
- */
-
-struct cmd_key_material {
- struct cmd_header hdr;
-
- __le16 action;
- struct MrvlIEtype_keyParamSet param;
-} __packed;
-
-static int lbs_set_key_material(struct lbs_private *priv,
- int key_type, int key_info,
- const u8 *key, u16 key_len)
-{
- struct cmd_key_material cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- /*
- * Example for WPA (TKIP):
- *
- * cmd 5e 00
- * size 34 00
- * sequence xx xx
- * result 00 00
- * action 01 00
- * TLV type 00 01 key param
- * length 00 26
- * key type 01 00 TKIP
- * key info 06 00 UNICAST | ENABLED
- * key len 20 00
- * key 32 bytes
- */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
- cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4);
- cmd.param.keytypeid = cpu_to_le16(key_type);
- cmd.param.keyinfo = cpu_to_le16(key_info);
- cmd.param.keylen = cpu_to_le16(key_len);
- if (key && key_len)
- memcpy(cmd.param.key, key, key_len);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
-
- lbs_deb_leave(LBS_DEB_CFG80211);
- return ret;
-}
-
-
-/*
- * Sets the auth type (open, shared, etc) in the firmware. That
- * we use CMD_802_11_AUTHENTICATE is misleading, this firmware
- * command doesn't send an authentication frame at all, it just
- * stores the auth_type.
- */
-static int lbs_set_authtype(struct lbs_private *priv,
- struct cfg80211_connect_params *sme)
-{
- struct cmd_ds_802_11_authenticate cmd;
- int ret;
-
- lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type);
-
- /*
- * cmd 11 00
- * size 19 00
- * sequence xx xx
- * result 00 00
- * BSS id 00 13 19 80 da 30
- * auth type 00
- * reserved 00 00 00 00 00 00 00 00 00 00
- */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- if (sme->bssid)
- memcpy(cmd.bssid, sme->bssid, ETH_ALEN);
- /* convert auth_type */
- ret = lbs_auth_to_authtype(sme->auth_type);
- if (ret < 0)
- goto done;
-
- cmd.authtype = ret;
- ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd);
-
- done:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-/*
- * Create association request
- */
-#define LBS_ASSOC_MAX_CMD_SIZE \
- (sizeof(struct cmd_ds_802_11_associate) \
- - 512 /* cmd_ds_802_11_associate.iebuf */ \
- + LBS_MAX_SSID_TLV_SIZE \
- + LBS_MAX_CHANNEL_TLV_SIZE \
- + LBS_MAX_CF_PARAM_TLV_SIZE \
- + LBS_MAX_AUTH_TYPE_TLV_SIZE \
- + LBS_MAX_WPA_TLV_SIZE)
-
-static int lbs_associate(struct lbs_private *priv,
- struct cfg80211_bss *bss,
- struct cfg80211_connect_params *sme)
-{
- struct cmd_ds_802_11_associate_response *resp;
- struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE,
- GFP_KERNEL);
- const u8 *ssid_eid;
- size_t len, resp_ie_len;
- int status;
- int ret;
- u8 *pos = &(cmd->iebuf[0]);
- u8 *tmp;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- if (!cmd) {
- ret = -ENOMEM;
- goto done;
- }
-
- /*
- * cmd 50 00
- * length 34 00
- * sequence xx xx
- * result 00 00
- * BSS id 00 13 19 80 da 30
- * capabilities 11 00
- * listen interval 0a 00
- * beacon interval 00 00
- * DTIM period 00
- * TLVs xx (up to 512 bytes)
- */
- cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE);
-
- /* Fill in static fields */
- memcpy(cmd->bssid, bss->bssid, ETH_ALEN);
- cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
- cmd->capability = cpu_to_le16(bss->capability);
-
- /* add SSID TLV */
- rcu_read_lock();
- ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
- if (ssid_eid)
- pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]);
- else
- lbs_deb_assoc("no SSID\n");
- rcu_read_unlock();
-
- /* add DS param TLV */
- if (bss->channel)
- pos += lbs_add_channel_tlv(pos, bss->channel->hw_value);
- else
- lbs_deb_assoc("no channel\n");
-
- /* add (empty) CF param TLV */
- pos += lbs_add_cf_param_tlv(pos);
-
- /* add rates TLV */
- tmp = pos + 4; /* skip Marvell IE header */
- pos += lbs_add_common_rates_tlv(pos, bss);
- lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp);
-
- /* add auth type TLV */
- if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9)
- pos += lbs_add_auth_type_tlv(pos, sme->auth_type);
-
- /* add WPA/WPA2 TLV */
- if (sme->ie && sme->ie_len)
- pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len);
-
- len = (sizeof(*cmd) - sizeof(cmd->iebuf)) +
- (u16)(pos - (u8 *) &cmd->iebuf);
- cmd->hdr.size = cpu_to_le16(len);
-
- lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd,
- le16_to_cpu(cmd->hdr.size));
-
- /* store for later use */
- memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd);
- if (ret)
- goto done;
-
- /* generate connect message to cfg80211 */
-
- resp = (void *) cmd; /* recast for easier field access */
- status = le16_to_cpu(resp->statuscode);
-
- /* Older FW versions map the IEEE 802.11 Status Code in the association
- * response to the following values returned in resp->statuscode:
- *
- * IEEE Status Code Marvell Status Code
- * 0 -> 0x0000 ASSOC_RESULT_SUCCESS
- * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * others -> 0x0003 ASSOC_RESULT_REFUSED
- *
- * Other response codes:
- * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
- * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
- * association response from the AP)
- */
- if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
- switch (status) {
- case 0:
- break;
- case 1:
- lbs_deb_assoc("invalid association parameters\n");
- status = WLAN_STATUS_CAPS_UNSUPPORTED;
- break;
- case 2:
- lbs_deb_assoc("timer expired while waiting for AP\n");
- status = WLAN_STATUS_AUTH_TIMEOUT;
- break;
- case 3:
- lbs_deb_assoc("association refused by AP\n");
- status = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
- break;
- case 4:
- lbs_deb_assoc("authentication refused by AP\n");
- status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
- break;
- default:
- lbs_deb_assoc("association failure %d\n", status);
- /* v5 OLPC firmware does return the AP status code if
- * it's not one of the values above. Let that through.
- */
- break;
- }
- }
-
- lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, "
- "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode),
- le16_to_cpu(resp->capability), le16_to_cpu(resp->aid));
-
- resp_ie_len = le16_to_cpu(resp->hdr.size)
- - sizeof(resp->hdr)
- - 6;
- cfg80211_connect_result(priv->dev,
- priv->assoc_bss,
- sme->ie, sme->ie_len,
- resp->iebuf, resp_ie_len,
- status,
- GFP_KERNEL);
-
- if (status == 0) {
- /* TODO: get rid of priv->connect_status */
- priv->connect_status = LBS_CONNECTED;
- netif_carrier_on(priv->dev);
- if (!priv->tx_pending_len)
- netif_tx_wake_all_queues(priv->dev);
- }
-
- kfree(cmd);
-done:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-static struct cfg80211_scan_request *
-_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme)
-{
- struct cfg80211_scan_request *creq = NULL;
- int i, n_channels = ieee80211_get_num_supported_channels(wiphy);
- enum ieee80211_band band;
-
- creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
- n_channels * sizeof(void *),
- GFP_ATOMIC);
- if (!creq)
- return NULL;
-
- /* SSIDs come after channels */
- creq->ssids = (void *)&creq->channels[n_channels];
- creq->n_channels = n_channels;
- creq->n_ssids = 1;
-
- /* Scan all available channels */
- i = 0;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- int j;
-
- if (!wiphy->bands[band])
- continue;
-
- for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
- /* ignore disabled channels */
- if (wiphy->bands[band]->channels[j].flags &
- IEEE80211_CHAN_DISABLED)
- continue;
-
- creq->channels[i] = &wiphy->bands[band]->channels[j];
- i++;
- }
- }
- if (i) {
- /* Set real number of channels specified in creq->channels[] */
- creq->n_channels = i;
-
- /* Scan for the SSID we're going to connect to */
- memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len);
- creq->ssids[0].ssid_len = sme->ssid_len;
- } else {
- /* No channels found... */
- kfree(creq);
- creq = NULL;
- }
-
- return creq;
-}
-
-static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_connect_params *sme)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- struct cfg80211_bss *bss = NULL;
- int ret = 0;
- u8 preamble = RADIO_PREAMBLE_SHORT;
-
- if (dev == priv->mesh_dev)
- return -EOPNOTSUPP;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- if (!sme->bssid) {
- struct cfg80211_scan_request *creq;
-
- /*
- * Scan for the requested network after waiting for existing
- * scans to finish.
- */
- lbs_deb_assoc("assoc: waiting for existing scans\n");
- wait_event_interruptible_timeout(priv->scan_q,
- (priv->scan_req == NULL),
- (15 * HZ));
-
- creq = _new_connect_scan_req(wiphy, sme);
- if (!creq) {
- ret = -EINVAL;
- goto done;
- }
-
- lbs_deb_assoc("assoc: scanning for compatible AP\n");
- _internal_start_scan(priv, true, creq);
-
- lbs_deb_assoc("assoc: waiting for scan to complete\n");
- wait_event_interruptible_timeout(priv->scan_q,
- (priv->scan_req == NULL),
- (15 * HZ));
- lbs_deb_assoc("assoc: scanning completed\n");
- }
-
- /* Find the BSS we want using available scan results */
- bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
- sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS,
- IEEE80211_PRIVACY_ANY);
- if (!bss) {
- wiphy_err(wiphy, "assoc: bss %pM not in scan results\n",
- sme->bssid);
- ret = -ENOENT;
- goto done;
- }
- lbs_deb_assoc("trying %pM\n", bss->bssid);
- lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n",
- sme->crypto.cipher_group,
- sme->key_idx, sme->key_len);
-
- /* As this is a new connection, clear locally stored WEP keys */
- priv->wep_tx_key = 0;
- memset(priv->wep_key, 0, sizeof(priv->wep_key));
- memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
-
- /* set/remove WEP keys */
- switch (sme->crypto.cipher_group) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- /* Store provided WEP keys in priv-> */
- priv->wep_tx_key = sme->key_idx;
- priv->wep_key_len[sme->key_idx] = sme->key_len;
- memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len);
- /* Set WEP keys and WEP mode */
- lbs_set_wep_keys(priv);
- priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
- lbs_set_mac_control(priv);
- /* No RSN mode for WEP */
- lbs_enable_rsn(priv, 0);
- break;
- case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */
- /*
- * If we don't have no WEP, no WPA and no WPA2,
- * we remove all keys like in the WPA/WPA2 setup,
- * we just don't set RSN.
- *
- * Therefore: fall-through
- */
- case WLAN_CIPHER_SUITE_TKIP:
- case WLAN_CIPHER_SUITE_CCMP:
- /* Remove WEP keys and WEP mode */
- lbs_remove_wep_keys(priv);
- priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
- lbs_set_mac_control(priv);
-
- /* clear the WPA/WPA2 keys */
- lbs_set_key_material(priv,
- KEY_TYPE_ID_WEP, /* doesn't matter */
- KEY_INFO_WPA_UNICAST,
- NULL, 0);
- lbs_set_key_material(priv,
- KEY_TYPE_ID_WEP, /* doesn't matter */
- KEY_INFO_WPA_MCAST,
- NULL, 0);
- /* RSN mode for WPA/WPA2 */
- lbs_enable_rsn(priv, sme->crypto.cipher_group != 0);
- break;
- default:
- wiphy_err(wiphy, "unsupported cipher group 0x%x\n",
- sme->crypto.cipher_group);
- ret = -ENOTSUPP;
- goto done;
- }
-
- ret = lbs_set_authtype(priv, sme);
- if (ret == -ENOTSUPP) {
- wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type);
- goto done;
- }
-
- lbs_set_radio(priv, preamble, 1);
-
- /* Do the actual association */
- ret = lbs_associate(priv, bss, sme);
-
- done:
- if (bss)
- cfg80211_put_bss(wiphy, bss);
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-int lbs_disconnect(struct lbs_private *priv, u16 reason)
-{
- struct cmd_ds_802_11_deauthenticate cmd;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- /* Mildly ugly to use a locally store my own BSSID ... */
- memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN);
- cmd.reasoncode = cpu_to_le16(reason);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
- if (ret)
- return ret;
-
- cfg80211_disconnected(priv->dev,
- reason,
- NULL, 0, true,
- GFP_KERNEL);
- priv->connect_status = LBS_DISCONNECTED;
-
- return 0;
-}
-
-static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
- u16 reason_code)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
-
- if (dev == priv->mesh_dev)
- return -EOPNOTSUPP;
-
- lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
-
- /* store for lbs_cfg_ret_disconnect() */
- priv->disassoc_reason = reason_code;
-
- return lbs_disconnect(priv, reason_code);
-}
-
-static int lbs_cfg_set_default_key(struct wiphy *wiphy,
- struct net_device *netdev,
- u8 key_index, bool unicast,
- bool multicast)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
-
- if (netdev == priv->mesh_dev)
- return -EOPNOTSUPP;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- if (key_index != priv->wep_tx_key) {
- lbs_deb_assoc("set_default_key: to %d\n", key_index);
- priv->wep_tx_key = key_index;
- lbs_set_wep_keys(priv);
- }
-
- return 0;
-}
-
-
-static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
- u8 idx, bool pairwise, const u8 *mac_addr,
- struct key_params *params)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- u16 key_info;
- u16 key_type;
- int ret = 0;
-
- if (netdev == priv->mesh_dev)
- return -EOPNOTSUPP;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n",
- params->cipher, mac_addr);
- lbs_deb_assoc("add_key: key index %d, key len %d\n",
- idx, params->key_len);
- if (params->key_len)
- lbs_deb_hex(LBS_DEB_CFG80211, "KEY",
- params->key, params->key_len);
-
- lbs_deb_assoc("add_key: seq len %d\n", params->seq_len);
- if (params->seq_len)
- lbs_deb_hex(LBS_DEB_CFG80211, "SEQ",
- params->seq, params->seq_len);
-
- switch (params->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- /* actually compare if something has changed ... */
- if ((priv->wep_key_len[idx] != params->key_len) ||
- memcmp(priv->wep_key[idx],
- params->key, params->key_len) != 0) {
- priv->wep_key_len[idx] = params->key_len;
- memcpy(priv->wep_key[idx],
- params->key, params->key_len);
- lbs_set_wep_keys(priv);
- }
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- case WLAN_CIPHER_SUITE_CCMP:
- key_info = KEY_INFO_WPA_ENABLED | ((idx == 0)
- ? KEY_INFO_WPA_UNICAST
- : KEY_INFO_WPA_MCAST);
- key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP)
- ? KEY_TYPE_ID_TKIP
- : KEY_TYPE_ID_AES;
- lbs_set_key_material(priv,
- key_type,
- key_info,
- params->key, params->key_len);
- break;
- default:
- wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher);
- ret = -ENOTSUPP;
- break;
- }
-
- return ret;
-}
-
-
-static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev,
- u8 key_index, bool pairwise, const u8 *mac_addr)
-{
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n",
- key_index, mac_addr);
-
-#ifdef TODO
- struct lbs_private *priv = wiphy_priv(wiphy);
- /*
- * I think can keep this a NO-OP, because:
-
- * - we clear all keys whenever we do lbs_cfg_connect() anyway
- * - neither "iw" nor "wpa_supplicant" won't call this during
- * an ongoing connection
- * - TODO: but I have to check if this is still true when
- * I set the AP to periodic re-keying
- * - we've not kzallec() something when we've added a key at
- * lbs_cfg_connect() or lbs_cfg_add_key().
- *
- * This causes lbs_cfg_del_key() only called at disconnect time,
- * where we'd just waste time deleting a key that is not going
- * to be used anyway.
- */
- if (key_index < 3 && priv->wep_key_len[key_index]) {
- priv->wep_key_len[key_index] = 0;
- lbs_set_wep_keys(priv);
- }
-#endif
-
- return 0;
-}
-
-
-/*
- * Get station
- */
-
-static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac, struct station_info *sinfo)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- s8 signal, noise;
- int ret;
- size_t i;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) |
- BIT(NL80211_STA_INFO_TX_PACKETS) |
- BIT(NL80211_STA_INFO_RX_BYTES) |
- BIT(NL80211_STA_INFO_RX_PACKETS);
- sinfo->tx_bytes = priv->dev->stats.tx_bytes;
- sinfo->tx_packets = priv->dev->stats.tx_packets;
- sinfo->rx_bytes = priv->dev->stats.rx_bytes;
- sinfo->rx_packets = priv->dev->stats.rx_packets;
-
- /* Get current RSSI */
- ret = lbs_get_rssi(priv, &signal, &noise);
- if (ret == 0) {
- sinfo->signal = signal;
- sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
- }
-
- /* Convert priv->cur_rate from hw_value to NL80211 value */
- for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
- if (priv->cur_rate == lbs_rates[i].hw_value) {
- sinfo->txrate.legacy = lbs_rates[i].bitrate;
- sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
- break;
- }
- }
-
- return 0;
-}
-
-
-
-
-/*
- * Change interface
- */
-
-static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- int ret = 0;
-
- if (dev == priv->mesh_dev)
- return -EOPNOTSUPP;
-
- switch (type) {
- case NL80211_IFTYPE_MONITOR:
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- if (priv->iface_running)
- ret = lbs_set_iface_type(priv, type);
-
- if (!ret)
- priv->wdev->iftype = type;
-
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-
-/*
- * IBSS (Ad-Hoc)
- */
-
-/*
- * The firmware needs the following bits masked out of the beacon-derived
- * capability field when associating/joining to a BSS:
- * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused)
- */
-#define CAPINFO_MASK (~(0xda00))
-
-
-static void lbs_join_post(struct lbs_private *priv,
- struct cfg80211_ibss_params *params,
- u8 *bssid, u16 capability)
-{
- u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */
- 2 + 4 + /* basic rates */
- 2 + 1 + /* DS parameter */
- 2 + 2 + /* atim */
- 2 + 8]; /* extended rates */
- u8 *fake = fake_ie;
- struct cfg80211_bss *bss;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- /*
- * For cfg80211_inform_bss, we'll need a fake IE, as we can't get
- * the real IE from the firmware. So we fabricate a fake IE based on
- * what the firmware actually sends (sniffed with wireshark).
- */
- /* Fake SSID IE */
- *fake++ = WLAN_EID_SSID;
- *fake++ = params->ssid_len;
- memcpy(fake, params->ssid, params->ssid_len);
- fake += params->ssid_len;
- /* Fake supported basic rates IE */
- *fake++ = WLAN_EID_SUPP_RATES;
- *fake++ = 4;
- *fake++ = 0x82;
- *fake++ = 0x84;
- *fake++ = 0x8b;
- *fake++ = 0x96;
- /* Fake DS channel IE */
- *fake++ = WLAN_EID_DS_PARAMS;
- *fake++ = 1;
- *fake++ = params->chandef.chan->hw_value;
- /* Fake IBSS params IE */
- *fake++ = WLAN_EID_IBSS_PARAMS;
- *fake++ = 2;
- *fake++ = 0; /* ATIM=0 */
- *fake++ = 0;
- /* Fake extended rates IE, TODO: don't add this for 802.11b only,
- * but I don't know how this could be checked */
- *fake++ = WLAN_EID_EXT_SUPP_RATES;
- *fake++ = 8;
- *fake++ = 0x0c;
- *fake++ = 0x12;
- *fake++ = 0x18;
- *fake++ = 0x24;
- *fake++ = 0x30;
- *fake++ = 0x48;
- *fake++ = 0x60;
- *fake++ = 0x6c;
- lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie);
-
- bss = cfg80211_inform_bss(priv->wdev->wiphy,
- params->chandef.chan,
- CFG80211_BSS_FTYPE_UNKNOWN,
- bssid,
- 0,
- capability,
- params->beacon_interval,
- fake_ie, fake - fake_ie,
- 0, GFP_KERNEL);
- cfg80211_put_bss(priv->wdev->wiphy, bss);
-
- memcpy(priv->wdev->ssid, params->ssid, params->ssid_len);
- priv->wdev->ssid_len = params->ssid_len;
-
- cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan,
- GFP_KERNEL);
-
- /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */
- priv->connect_status = LBS_CONNECTED;
- netif_carrier_on(priv->dev);
- if (!priv->tx_pending_len)
- netif_wake_queue(priv->dev);
-
- lbs_deb_leave(LBS_DEB_CFG80211);
-}
-
-static int lbs_ibss_join_existing(struct lbs_private *priv,
- struct cfg80211_ibss_params *params,
- struct cfg80211_bss *bss)
-{
- const u8 *rates_eid;
- struct cmd_ds_802_11_ad_hoc_join cmd;
- u8 preamble = RADIO_PREAMBLE_SHORT;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- /* TODO: set preamble based on scan result */
- ret = lbs_set_radio(priv, preamble, 1);
- if (ret)
- goto out;
-
- /*
- * Example CMD_802_11_AD_HOC_JOIN command:
- *
- * command 2c 00 CMD_802_11_AD_HOC_JOIN
- * size 65 00
- * sequence xx xx
- * result 00 00
- * bssid 02 27 27 97 2f 96
- * ssid 49 42 53 53 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * type 02 CMD_BSS_TYPE_IBSS
- * beacon period 64 00
- * dtim period 00
- * timestamp 00 00 00 00 00 00 00 00
- * localtime 00 00 00 00 00 00 00 00
- * IE DS 03
- * IE DS len 01
- * IE DS channel 01
- * reserveed 00 00 00 00
- * IE IBSS 06
- * IE IBSS len 02
- * IE IBSS atim 00 00
- * reserved 00 00 00 00
- * capability 02 00
- * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00
- * fail timeout ff 00
- * probe delay 00 00
- */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN);
- memcpy(cmd.bss.ssid, params->ssid, params->ssid_len);
- cmd.bss.type = CMD_BSS_TYPE_IBSS;
- cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval);
- cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS;
- cmd.bss.ds.header.len = 1;
- cmd.bss.ds.channel = params->chandef.chan->hw_value;
- cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS;
- cmd.bss.ibss.header.len = 2;
- cmd.bss.ibss.atimwindow = 0;
- cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
-
- /* set rates to the intersection of our rates and the rates in the
- bss */
- rcu_read_lock();
- rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
- if (!rates_eid) {
- lbs_add_rates(cmd.bss.rates);
- } else {
- int hw, i;
- u8 rates_max = rates_eid[1];
- u8 *rates = cmd.bss.rates;
- for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
- u8 hw_rate = lbs_rates[hw].bitrate / 5;
- for (i = 0; i < rates_max; i++) {
- if (hw_rate == (rates_eid[i+2] & 0x7f)) {
- u8 rate = rates_eid[i+2];
- if (rate == 0x02 || rate == 0x04 ||
- rate == 0x0b || rate == 0x16)
- rate |= 0x80;
- *rates++ = rate;
- }
- }
- }
- }
- rcu_read_unlock();
-
- /* Only v8 and below support setting this */
- if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
- cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
- cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
- }
- ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd);
- if (ret)
- goto out;
-
- /*
- * This is a sample response to CMD_802_11_AD_HOC_JOIN:
- *
- * response 2c 80
- * size 09 00
- * sequence xx xx
- * result 00 00
- * reserved 00
- */
- lbs_join_post(priv, params, bss->bssid, bss->capability);
-
- out:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-
-static int lbs_ibss_start_new(struct lbs_private *priv,
- struct cfg80211_ibss_params *params)
-{
- struct cmd_ds_802_11_ad_hoc_start cmd;
- struct cmd_ds_802_11_ad_hoc_result *resp =
- (struct cmd_ds_802_11_ad_hoc_result *) &cmd;
- u8 preamble = RADIO_PREAMBLE_SHORT;
- int ret = 0;
- u16 capability;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- ret = lbs_set_radio(priv, preamble, 1);
- if (ret)
- goto out;
-
- /*
- * Example CMD_802_11_AD_HOC_START command:
- *
- * command 2b 00 CMD_802_11_AD_HOC_START
- * size b1 00
- * sequence xx xx
- * result 00 00
- * ssid 54 45 53 54 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00
- * bss type 02
- * beacon period 64 00
- * dtim period 00
- * IE IBSS 06
- * IE IBSS len 02
- * IE IBSS atim 00 00
- * reserved 00 00 00 00
- * IE DS 03
- * IE DS len 01
- * IE DS channel 01
- * reserved 00 00 00 00
- * probe delay 00 00
- * capability 02 00
- * rates 82 84 8b 96 (basic rates with have bit 7 set)
- * 0c 12 18 24 30 48 60 6c
- * padding 100 bytes
- */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- memcpy(cmd.ssid, params->ssid, params->ssid_len);
- cmd.bsstype = CMD_BSS_TYPE_IBSS;
- cmd.beaconperiod = cpu_to_le16(params->beacon_interval);
- cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS;
- cmd.ibss.header.len = 2;
- cmd.ibss.atimwindow = 0;
- cmd.ds.header.id = WLAN_EID_DS_PARAMS;
- cmd.ds.header.len = 1;
- cmd.ds.channel = params->chandef.chan->hw_value;
- /* Only v8 and below support setting probe delay */
- if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8)
- cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
- /* TODO: mix in WLAN_CAPABILITY_PRIVACY */
- capability = WLAN_CAPABILITY_IBSS;
- cmd.capability = cpu_to_le16(capability);
- lbs_add_rates(cmd.rates);
-
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd);
- if (ret)
- goto out;
-
- /*
- * This is a sample response to CMD_802_11_AD_HOC_JOIN:
- *
- * response 2b 80
- * size 14 00
- * sequence xx xx
- * result 00 00
- * reserved 00
- * bssid 02 2b 7b 0f 86 0e
- */
- lbs_join_post(priv, params, resp->bssid, capability);
-
- out:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_ibss_params *params)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- int ret = 0;
- struct cfg80211_bss *bss;
-
- if (dev == priv->mesh_dev)
- return -EOPNOTSUPP;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- if (!params->chandef.chan) {
- ret = -ENOTSUPP;
- goto out;
- }
-
- ret = lbs_set_channel(priv, params->chandef.chan->hw_value);
- if (ret)
- goto out;
-
- /* Search if someone is beaconing. This assumes that the
- * bss list is populated already */
- bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid,
- params->ssid, params->ssid_len,
- IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
-
- if (bss) {
- ret = lbs_ibss_join_existing(priv, params, bss);
- cfg80211_put_bss(wiphy, bss);
- } else
- ret = lbs_ibss_start_new(priv, params);
-
-
- out:
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
- struct cmd_ds_802_11_ad_hoc_stop cmd;
- int ret = 0;
-
- if (dev == priv->mesh_dev)
- return -EOPNOTSUPP;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd);
-
- /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */
- lbs_mac_event_disconnected(priv, true);
-
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-
-
-
-/*
- * Initialization
- */
-
-static struct cfg80211_ops lbs_cfg80211_ops = {
- .set_monitor_channel = lbs_cfg_set_monitor_channel,
- .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel,
- .scan = lbs_cfg_scan,
- .connect = lbs_cfg_connect,
- .disconnect = lbs_cfg_disconnect,
- .add_key = lbs_cfg_add_key,
- .del_key = lbs_cfg_del_key,
- .set_default_key = lbs_cfg_set_default_key,
- .get_station = lbs_cfg_get_station,
- .change_virtual_intf = lbs_change_intf,
- .join_ibss = lbs_join_ibss,
- .leave_ibss = lbs_leave_ibss,
-};
-
-
-/*
- * At this time lbs_private *priv doesn't even exist, so we just allocate
- * memory and don't initialize the wiphy further. This is postponed until we
- * can talk to the firmware and happens at registration time in
- * lbs_cfg_wiphy_register().
- */
-struct wireless_dev *lbs_cfg_alloc(struct device *dev)
-{
- int ret = 0;
- struct wireless_dev *wdev;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
- if (!wdev)
- return ERR_PTR(-ENOMEM);
-
- wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private));
- if (!wdev->wiphy) {
- dev_err(dev, "cannot allocate wiphy\n");
- ret = -ENOMEM;
- goto err_wiphy_new;
- }
-
- lbs_deb_leave(LBS_DEB_CFG80211);
- return wdev;
-
- err_wiphy_new:
- kfree(wdev);
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ERR_PTR(ret);
-}
-
-
-static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv)
-{
- struct region_code_mapping {
- const char *cn;
- int code;
- };
-
- /* Section 5.17.2 */
- static const struct region_code_mapping regmap[] = {
- {"US ", 0x10}, /* US FCC */
- {"CA ", 0x20}, /* Canada */
- {"EU ", 0x30}, /* ETSI */
- {"ES ", 0x31}, /* Spain */
- {"FR ", 0x32}, /* France */
- {"JP ", 0x40}, /* Japan */
- };
- size_t i;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- for (i = 0; i < ARRAY_SIZE(regmap); i++)
- if (regmap[i].code == priv->regioncode) {
- regulatory_hint(priv->wdev->wiphy, regmap[i].cn);
- break;
- }
-
- lbs_deb_leave(LBS_DEB_CFG80211);
-}
-
-static void lbs_reg_notifier(struct wiphy *wiphy,
- struct regulatory_request *request)
-{
- struct lbs_private *priv = wiphy_priv(wiphy);
-
- lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain "
- "callback for domain %c%c\n", request->alpha2[0],
- request->alpha2[1]);
-
- memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2));
- if (lbs_iface_active(priv))
- lbs_set_11d_domain_info(priv);
-
- lbs_deb_leave(LBS_DEB_CFG80211);
-}
-
-/*
- * This function get's called after lbs_setup_firmware() determined the
- * firmware capabities. So we can setup the wiphy according to our
- * hardware/firmware.
- */
-int lbs_cfg_register(struct lbs_private *priv)
-{
- struct wireless_dev *wdev = priv->wdev;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- wdev->wiphy->max_scan_ssids = 1;
- wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
-
- wdev->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
- if (lbs_rtap_supported(priv))
- wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
- if (lbs_mesh_activated(priv))
- wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
-
- wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;
-
- /*
- * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have
- * never seen a firmware without WPA
- */
- wdev->wiphy->cipher_suites = cipher_suites;
- wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
- wdev->wiphy->reg_notifier = lbs_reg_notifier;
-
- ret = wiphy_register(wdev->wiphy);
- if (ret < 0)
- pr_err("cannot register wiphy device\n");
-
- priv->wiphy_registered = true;
-
- ret = register_netdev(priv->dev);
- if (ret)
- pr_err("cannot register network device\n");
-
- INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
-
- lbs_cfg_set_regulatory_hint(priv);
-
- lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
- return ret;
-}
-
-void lbs_scan_deinit(struct lbs_private *priv)
-{
- lbs_deb_enter(LBS_DEB_CFG80211);
- cancel_delayed_work_sync(&priv->scan_work);
-}
-
-
-void lbs_cfg_free(struct lbs_private *priv)
-{
- struct wireless_dev *wdev = priv->wdev;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- if (!wdev)
- return;
-
- if (priv->wiphy_registered)
- wiphy_unregister(wdev->wiphy);
-
- if (wdev->wiphy)
- wiphy_free(wdev->wiphy);
-
- kfree(wdev);
-}
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
deleted file mode 100644
index 33ceda296c9c..000000000000
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ /dev/null
@@ -1,1453 +0,0 @@
-/*
- * linux/drivers/net/wireless/libertas/if_sdio.c
- *
- * Copyright 2007-2008 Pierre Ossman
- *
- * Inspired by if_cs.c, Copyright 2007 Holger Schurig
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This hardware has more or less no CMD53 support, so all registers
- * must be accessed using sdio_readb()/sdio_writeb().
- *
- * Transfers must be in one transaction or the firmware goes bonkers.
- * This means that the transfer must either be small enough to do a
- * byte based transfer or it must be padded to a multiple of the
- * current block size.
- *
- * As SDIO is still new to the kernel, it is unfortunately common with
- * bugs in the host controllers related to that. One such bug is that
- * controllers cannot do transfers that aren't a multiple of 4 bytes.
- * If you don't have time to fix the host controller driver, you can
- * work around the problem by modifying if_sdio_host_to_card() and
- * if_sdio_card_to_host() to pad the data.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/firmware.h>
-#include <linux/netdevice.h>
-#include <linux/delay.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/mmc/sdio.h>
-#include <linux/mmc/host.h>
-#include <linux/pm_runtime.h>
-
-#include "host.h"
-#include "decl.h"
-#include "defs.h"
-#include "dev.h"
-#include "cmd.h"
-#include "if_sdio.h"
-
-static void if_sdio_interrupt(struct sdio_func *func);
-
-/* The if_sdio_remove() callback function is called when
- * user removes this module from kernel space or ejects
- * the card from the slot. The driver handles these 2 cases
- * differently for SD8688 combo chip.
- * If the user is removing the module, the FUNC_SHUTDOWN
- * command for SD8688 is sent to the firmware.
- * If the card is removed, there is no need to send this command.
- *
- * The variable 'user_rmmod' is used to distinguish these two
- * scenarios. This flag is initialized as FALSE in case the card
- * is removed, and will be set to TRUE for module removal when
- * module_exit function is called.
- */
-static u8 user_rmmod;
-
-static const struct sdio_device_id if_sdio_ids[] = {
- { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
- SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
- { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
- SDIO_DEVICE_ID_MARVELL_8688WLAN) },
- { /* end: all zeroes */ },
-};
-
-MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
-
-#define MODEL_8385 0x04
-#define MODEL_8686 0x0b
-#define MODEL_8688 0x10
-
-static const struct lbs_fw_table fw_table[] = {
- { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" },
- { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" },
- { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" },
- { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" },
- { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" },
- { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" },
- { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" },
- { 0, NULL, NULL }
-};
-MODULE_FIRMWARE("libertas/sd8385_helper.bin");
-MODULE_FIRMWARE("libertas/sd8385.bin");
-MODULE_FIRMWARE("sd8385_helper.bin");
-MODULE_FIRMWARE("sd8385.bin");
-MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin");
-MODULE_FIRMWARE("libertas/sd8686_v9.bin");
-MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin");
-MODULE_FIRMWARE("libertas/sd8686_v8.bin");
-MODULE_FIRMWARE("sd8686_helper.bin");
-MODULE_FIRMWARE("sd8686.bin");
-MODULE_FIRMWARE("libertas/sd8688_helper.bin");
-MODULE_FIRMWARE("libertas/sd8688.bin");
-MODULE_FIRMWARE("sd8688_helper.bin");
-MODULE_FIRMWARE("sd8688.bin");
-
-struct if_sdio_packet {
- struct if_sdio_packet *next;
- u16 nb;
- u8 buffer[0] __attribute__((aligned(4)));
-};
-
-struct if_sdio_card {
- struct sdio_func *func;
- struct lbs_private *priv;
-
- int model;
- unsigned long ioport;
- unsigned int scratch_reg;
- bool started;
- wait_queue_head_t pwron_waitq;
-
- u8 buffer[65536] __attribute__((aligned(4)));
-
- spinlock_t lock;
- struct if_sdio_packet *packets;
-
- struct workqueue_struct *workqueue;
- struct work_struct packet_worker;
-
- u8 rx_unit;
-};
-
-static void if_sdio_finish_power_on(struct if_sdio_card *card);
-static int if_sdio_power_off(struct if_sdio_card *card);
-
-/********************************************************************/
-/* I/O */
-/********************************************************************/
-
-/*
- * For SD8385/SD8686, this function reads firmware status after
- * the image is downloaded, or reads RX packet length when
- * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received.
- * For SD8688, this function reads firmware status only.
- */
-static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
-{
- int ret;
- u16 scratch;
-
- scratch = sdio_readb(card->func, card->scratch_reg, &ret);
- if (!ret)
- scratch |= sdio_readb(card->func, card->scratch_reg + 1,
- &ret) << 8;
-
- if (err)
- *err = ret;
-
- if (ret)
- return 0xffff;
-
- return scratch;
-}
-
-static u8 if_sdio_read_rx_unit(struct if_sdio_card *card)
-{
- int ret;
- u8 rx_unit;
-
- rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret);
-
- if (ret)
- rx_unit = 0;
-
- return rx_unit;
-}
-
-static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err)
-{
- int ret;
- u16 rx_len;
-
- switch (card->model) {
- case MODEL_8385:
- case MODEL_8686:
- rx_len = if_sdio_read_scratch(card, &ret);
- break;
- case MODEL_8688:
- default: /* for newer chipsets */
- rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret);
- if (!ret)
- rx_len <<= card->rx_unit;
- else
- rx_len = 0xffff; /* invalid length */
-
- break;
- }
-
- if (err)
- *err = ret;
-
- return rx_len;
-}
-
-static int if_sdio_handle_cmd(struct if_sdio_card *card,
- u8 *buffer, unsigned size)
-{
- struct lbs_private *priv = card->priv;
- int ret;
- unsigned long flags;
- u8 i;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- if (size > LBS_CMD_BUFFER_SIZE) {
- lbs_deb_sdio("response packet too large (%d bytes)\n",
- (int)size);
- ret = -E2BIG;
- goto out;
- }
-
- spin_lock_irqsave(&priv->driver_lock, flags);
-
- i = (priv->resp_idx == 0) ? 1 : 0;
- BUG_ON(priv->resp_len[i]);
- priv->resp_len[i] = size;
- memcpy(priv->resp_buf[i], buffer, size);
- lbs_notify_command_response(priv, i);
-
- spin_unlock_irqrestore(&card->priv->driver_lock, flags);
-
- ret = 0;
-
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
- return ret;
-}
-
-static int if_sdio_handle_data(struct if_sdio_card *card,
- u8 *buffer, unsigned size)
-{
- int ret;
- struct sk_buff *skb;
- char *data;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
- lbs_deb_sdio("response packet too large (%d bytes)\n",
- (int)size);
- ret = -E2BIG;
- goto out;
- }
-
- skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN);
- if (!skb) {
- ret = -ENOMEM;
- goto out;
- }
-
- skb_reserve(skb, NET_IP_ALIGN);
-
- data = skb_put(skb, size);
-
- memcpy(data, buffer, size);
-
- lbs_process_rxed_packet(card->priv, skb);
-
- ret = 0;
-
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
- return ret;
-}
-
-static int if_sdio_handle_event(struct if_sdio_card *card,
- u8 *buffer, unsigned size)
-{
- int ret;
- u32 event;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- if (card->model == MODEL_8385) {
- event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
- if (ret)
- goto out;
-
- /* right shift 3 bits to get the event id */
- event >>= 3;
- } else {
- if (size < 4) {
- lbs_deb_sdio("event packet too small (%d bytes)\n",
- (int)size);
- ret = -EINVAL;
- goto out;
- }
- event = buffer[3] << 24;
- event |= buffer[2] << 16;
- event |= buffer[1] << 8;
- event |= buffer[0] << 0;
- }
-
- lbs_queue_event(card->priv, event & 0xFF);
- ret = 0;
-
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
- return ret;
-}
-
-static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition)
-{
- u8 status;
- unsigned long timeout;
- int ret = 0;
-
- timeout = jiffies + HZ;
- while (1) {
- status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
- if (ret)
- return ret;
- if ((status & condition) == condition)
- break;
- if (time_after(jiffies, timeout))
- return -ETIMEDOUT;
- mdelay(1);
- }
- return ret;
-}
-
-static int if_sdio_card_to_host(struct if_sdio_card *card)
-{
- int ret;
- u16 size, type, chunk;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- size = if_sdio_read_rx_len(card, &ret);
- if (ret)
- goto out;
-
- if (size < 4) {
- lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n",
- (int)size);
- ret = -EINVAL;
- goto out;
- }
-
- ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY);
- if (ret)
- goto out;
-
- /*
- * The transfer must be in one transaction or the firmware
- * goes suicidal. There's no way to guarantee that for all
- * controllers, but we can at least try.
- */
- chunk = sdio_align_size(card->func, size);
-
- ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
- if (ret)
- goto out;
-
- chunk = card->buffer[0] | (card->buffer[1] << 8);
- type = card->buffer[2] | (card->buffer[3] << 8);
-
- lbs_deb_sdio("packet of type %d and size %d bytes\n",
- (int)type, (int)chunk);
-
- if (chunk > size) {
- lbs_deb_sdio("packet fragment (%d > %d)\n",
- (int)chunk, (int)size);
- ret = -EINVAL;
- goto out;
- }
-
- if (chunk < size) {
- lbs_deb_sdio("packet fragment (%d < %d)\n",
- (int)chunk, (int)size);
- }
-
- switch (type) {
- case MVMS_CMD:
- ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);
- if (ret)
- goto out;
- break;
- case MVMS_DAT:
- ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);
- if (ret)
- goto out;
- break;
- case MVMS_EVENT:
- ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4);
- if (ret)
- goto out;
- break;
- default:
- lbs_deb_sdio("invalid type (%d) from firmware\n",
- (int)type);
- ret = -EINVAL;
- goto out;
- }
-
-out:
- if (ret)
- pr_err("problem fetching packet from firmware\n");
-
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
- return ret;
-}
-
-static void if_sdio_host_to_card_worker(struct work_struct *work)
-{
- struct if_sdio_card *card;
- struct if_sdio_packet *packet;
- int ret;
- unsigned long flags;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- card = container_of(work, struct if_sdio_card, packet_worker);
-
- while (1) {
- spin_lock_irqsave(&card->lock, flags);
- packet = card->packets;
- if (packet)
- card->packets = packet->next;
- spin_unlock_irqrestore(&card->lock, flags);
-
- if (!packet)
- break;
-
- sdio_claim_host(card->func);
-
- ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY);
- if (ret == 0) {
- ret = sdio_writesb(card->func, card->ioport,
- packet->buffer, packet->nb);
- }
-
- if (ret)
- pr_err("error %d sending packet to firmware\n", ret);
-
- sdio_release_host(card->func);
-
- kfree(packet);
- }
-
- lbs_deb_leave(LBS_DEB_SDIO);
-}
-
-/********************************************************************/
-/* Firmware */
-/********************************************************************/
-
-#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY)
-
-static int if_sdio_prog_helper(struct if_sdio_card *card,
- const struct firmware *fw)
-{
- int ret;
- unsigned long timeout;
- u8 *chunk_buffer;
- u32 chunk_size;
- const u8 *firmware;
- size_t size;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- chunk_buffer = kzalloc(64, GFP_KERNEL);
- if (!chunk_buffer) {
- ret = -ENOMEM;
- goto out;
- }
-
- sdio_claim_host(card->func);
-
- ret = sdio_set_block_size(card->func, 32);
- if (ret)
- goto release;
-
- firmware = fw->data;
- size = fw->size;
-
- while (size) {
- ret = if_sdio_wait_status(card, FW_DL_READY_STATUS);
- if (ret)
- goto release;
-
- /* On some platforms (like Davinci) the chip needs more time
- * between helper blocks.
- */
- mdelay(2);
-
- chunk_size = min_t(size_t, size, 60);
-
- *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size);
- memcpy(chunk_buffer + 4, firmware, chunk_size);
-/*
- lbs_deb_sdio("sending %d bytes chunk\n", chunk_size);
-*/
- ret = sdio_writesb(card->func, card->ioport,
- chunk_buffer, 64);
- if (ret)
- goto release;
-
- firmware += chunk_size;
- size -= chunk_size;
- }
-
- /* an empty block marks the end of the transfer */
- memset(chunk_buffer, 0, 4);
- ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64);
- if (ret)
- goto release;
-
- lbs_deb_sdio("waiting for helper to boot...\n");
-
- /* wait for the helper to boot by looking at the size register */
- timeout = jiffies + HZ;
- while (1) {
- u16 req_size;
-
- req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret);
- if (ret)
- goto release;
-
- req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8;
- if (ret)
- goto release;
-
- if (req_size != 0)
- break;
-
- if (time_after(jiffies, timeout)) {
- ret = -ETIMEDOUT;
- goto release;
- }
-
- msleep(10);
- }
-
- ret = 0;
-
-release:
- sdio_release_host(card->func);
- kfree(chunk_buffer);
-
-out:
- if (ret)
- pr_err("failed to load helper firmware\n");
-
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
- return ret;
-}
-
-static int if_sdio_prog_real(struct if_sdio_card *card,
- const struct firmware *fw)
-{
- int ret;
- unsigned long timeout;
- u8 *chunk_buffer;
- u32 chunk_size;
- const u8 *firmware;
- size_t size, req_size;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- chunk_buffer = kzalloc(512, GFP_KERNEL);
- if (!chunk_buffer) {
- ret = -ENOMEM;
- goto out;
- }
-
- sdio_claim_host(card->func);
-
- ret = sdio_set_block_size(card->func, 32);
- if (ret)
- goto release;
-
- firmware = fw->data;
- size = fw->size;
-
- while (size) {
- timeout = jiffies + HZ;
- while (1) {
- ret = if_sdio_wait_status(card, FW_DL_READY_STATUS);
- if (ret)
- goto release;
-
- req_size = sdio_readb(card->func, IF_SDIO_RD_BASE,
- &ret);
- if (ret)
- goto release;
-
- req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1,
- &ret) << 8;
- if (ret)
- goto release;
-
- /*
- * For SD8688 wait until the length is not 0, 1 or 2
- * before downloading the first FW block,
- * since BOOT code writes the register to indicate the
- * helper/FW download winner,
- * the value could be 1 or 2 (Func1 or Func2).
- */
- if ((size != fw->size) || (req_size > 2))
- break;
- if (time_after(jiffies, timeout)) {
- ret = -ETIMEDOUT;
- goto release;
- }
- mdelay(1);
- }
-
-/*
- lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size);
-*/
- if (req_size == 0) {
- lbs_deb_sdio("firmware helper gave up early\n");
- ret = -EIO;
- goto release;
- }
-
- if (req_size & 0x01) {
- lbs_deb_sdio("firmware helper signalled error\n");
- ret = -EIO;
- goto release;
- }
-
- if (req_size > size)
- req_size = size;
-
- while (req_size) {
- chunk_size = min_t(size_t, req_size, 512);
-
- memcpy(chunk_buffer, firmware, chunk_size);
-/*
- lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n",
- chunk_size, (chunk_size + 31) / 32 * 32);
-*/
- ret = sdio_writesb(card->func, card->ioport,
- chunk_buffer, roundup(chunk_size, 32));
- if (ret)
- goto release;
-
- firmware += chunk_size;
- size -= chunk_size;
- req_size -= chunk_size;
- }
- }
-
- ret = 0;
-
- lbs_deb_sdio("waiting for firmware to boot...\n");
-
- /* wait for the firmware to boot */
- timeout = jiffies + HZ;
- while (1) {
- u16 scratch;
-
- scratch = if_sdio_read_scratch(card, &ret);
- if (ret)
- goto release;
-
- if (scratch == IF_SDIO_FIRMWARE_OK)
- break;
-
- if (time_after(jiffies, timeout)) {
- ret = -ETIMEDOUT;
- goto release;
- }
-
- msleep(10);
- }
-
- ret = 0;
-
-release:
- sdio_release_host(card->func);
- kfree(chunk_buffer);
-
-out:
- if (ret)
- pr_err("failed to load firmware\n");
-
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
- return ret;
-}
-
-static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret,
- const struct firmware *helper,
- const struct firmware *mainfw)
-{
- struct if_sdio_card *card = priv->card;
-
- if (ret) {
- pr_err("failed to find firmware (%d)\n", ret);
- return;
- }
-
- ret = if_sdio_prog_helper(card, helper);
- if (ret)
- return;
-
- lbs_deb_sdio("Helper firmware loaded\n");
-
- ret = if_sdio_prog_real(card, mainfw);
- if (ret)
- return;
-
- lbs_deb_sdio("Firmware loaded\n");
- if_sdio_finish_power_on(card);
-}
-
-static int if_sdio_prog_firmware(struct if_sdio_card *card)
-{
- int ret;
- u16 scratch;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- /*
- * Disable interrupts
- */
- sdio_claim_host(card->func);
- sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret);
- sdio_release_host(card->func);
-
- sdio_claim_host(card->func);
- scratch = if_sdio_read_scratch(card, &ret);
- sdio_release_host(card->func);
-
- lbs_deb_sdio("firmware status = %#x\n", scratch);
- lbs_deb_sdio("scratch ret = %d\n", ret);
-
- if (ret)
- goto out;
-
-
- /*
- * The manual clearly describes that FEDC is the right code to use
- * to detect firmware presence, but for SD8686 it is not that simple.
- * Scratch is also used to store the RX packet length, so we lose
- * the FEDC value early on. So we use a non-zero check in order
- * to validate firmware presence.
- * Additionally, the SD8686 in the Gumstix always has the high scratch
- * bit set, even when the firmware is not loaded. So we have to
- * exclude that from the test.
- */
- if (scratch == IF_SDIO_FIRMWARE_OK) {
- lbs_deb_sdio("firmware already loaded\n");
- if_sdio_finish_power_on(card);
- return 0;
- } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) {
- lbs_deb_sdio("firmware may be running\n");
- if_sdio_finish_power_on(card);
- return 0;
- }
-
- ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model,
- fw_table, if_sdio_do_prog_firmware);
-
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
- return ret;
-}
-
-/********************************************************************/
-/* Power management */
-/********************************************************************/
-
-/* Finish power on sequence (after firmware is loaded) */
-static void if_sdio_finish_power_on(struct if_sdio_card *card)
-{
- struct sdio_func *func = card->func;
- struct lbs_private *priv = card->priv;
- int ret;
-
- sdio_claim_host(func);
- sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
-
- /*
- * Get rx_unit if the chip is SD8688 or newer.
- * SD8385 & SD8686 do not have rx_unit.
- */
- if ((card->model != MODEL_8385)
- && (card->model != MODEL_8686))
- card->rx_unit = if_sdio_read_rx_unit(card);
- else
- card->rx_unit = 0;
-
- /*
- * Set up the interrupt handler late.
- *
- * If we set it up earlier, the (buggy) hardware generates a spurious
- * interrupt, even before the interrupt has been enabled, with
- * CCCR_INTx = 0.
- *
- * We register the interrupt handler late so that we can handle any
- * spurious interrupts, and also to avoid generation of that known
- * spurious interrupt in the first place.
- */
- ret = sdio_claim_irq(func, if_sdio_interrupt);
- if (ret)
- goto release;
-
- /*
- * Enable interrupts now that everything is set up
- */
- sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
- if (ret)
- goto release_irq;
-
- sdio_release_host(func);
-
- /* Set fw_ready before queuing any commands so that
- * lbs_thread won't block from sending them to firmware.
- */
- priv->fw_ready = 1;
-
- /*
- * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
- */
- if (card->model == MODEL_8688) {
- struct cmd_header cmd;
-
- memset(&cmd, 0, sizeof(cmd));
-
- lbs_deb_sdio("send function INIT command\n");
- if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
- lbs_cmd_copyback, (unsigned long) &cmd))
- netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
- }
-
- wake_up(&card->pwron_waitq);
-
- if (!card->started) {
- ret = lbs_start_card(priv);
- if_sdio_power_off(card);
- if (ret == 0) {
- card->started = true;
- /* Tell PM core that we don't need the card to be
- * powered now */
- pm_runtime_put(&func->dev);
- }
- }
-
- return;
-
-release_irq:
- sdio_release_irq(func);
-release:
- sdio_release_host(func);
-}
-
-static int if_sdio_power_on(struct if_sdio_card *card)
-{
- struct sdio_func *func = card->func;
- struct mmc_host *host = func->card->host;
- int ret;
-
- sdio_claim_host(func);
-
- ret = sdio_enable_func(func);
- if (ret)
- goto release;
-
- /* For 1-bit transfers to the 8686 model, we need to enable the
- * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
- * bit to allow access to non-vendor registers. */
- if ((card->model == MODEL_8686) &&
- (host->caps & MMC_CAP_SDIO_IRQ) &&
- (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
- u8 reg;
-
- func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
- reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
- if (ret)
- goto disable;
-
- reg |= SDIO_BUS_ECSI;
- sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
- if (ret)
- goto disable;
- }
-
- card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
- if (ret)
- goto disable;
-
- card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
- if (ret)
- goto disable;
-
- card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
- if (ret)
- goto disable;
-
- sdio_release_host(func);
- ret = if_sdio_prog_firmware(card);
- if (ret) {
- sdio_claim_host(func);
- goto disable;
- }
-
- return 0;
-
-disable:
- sdio_disable_func(func);
-release:
- sdio_release_host(func);
- return ret;
-}
-
-static int if_sdio_power_off(struct if_sdio_card *card)
-{
- struct sdio_func *func = card->func;
- struct lbs_private *priv = card->priv;
-
- priv->fw_ready = 0;
-
- sdio_claim_host(func);
- sdio_release_irq(func);
- sdio_disable_func(func);
- sdio_release_host(func);
- return 0;
-}
-
-
-/*******************************************************************/
-/* Libertas callbacks */
-/*******************************************************************/
-
-static int if_sdio_host_to_card(struct lbs_private *priv,
- u8 type, u8 *buf, u16 nb)
-{
- int ret;
- struct if_sdio_card *card;
- struct if_sdio_packet *packet, *cur;
- u16 size;
- unsigned long flags;
-
- lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb);
-
- card = priv->card;
-
- if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) {
- ret = -EINVAL;
- goto out;
- }
-
- /*
- * The transfer must be in one transaction or the firmware
- * goes suicidal. There's no way to guarantee that for all
- * controllers, but we can at least try.
- */
- size = sdio_align_size(card->func, nb + 4);
-
- packet = kzalloc(sizeof(struct if_sdio_packet) + size,
- GFP_ATOMIC);
- if (!packet) {
- ret = -ENOMEM;
- goto out;
- }
-
- packet->next = NULL;
- packet->nb = size;
-
- /*
- * SDIO specific header.
- */
- packet->buffer[0] = (nb + 4) & 0xff;
- packet->buffer[1] = ((nb + 4) >> 8) & 0xff;
- packet->buffer[2] = type;
- packet->buffer[3] = 0;
-
- memcpy(packet->buffer + 4, buf, nb);
-
- spin_lock_irqsave(&card->lock, flags);
-
- if (!card->packets)
- card->packets = packet;
- else {
- cur = card->packets;
- while (cur->next)
- cur = cur->next;
- cur->next = packet;
- }
-
- switch (type) {
- case MVMS_CMD:
- priv->dnld_sent = DNLD_CMD_SENT;
- break;
- case MVMS_DAT:
- priv->dnld_sent = DNLD_DATA_SENT;
- break;
- default:
- lbs_deb_sdio("unknown packet type %d\n", (int)type);
- }
-
- spin_unlock_irqrestore(&card->lock, flags);
-
- queue_work(card->workqueue, &card->packet_worker);
-
- ret = 0;
-
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
- return ret;
-}
-
-static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
-{
- int ret = -1;
- struct cmd_header cmd;
-
- memset(&cmd, 0, sizeof(cmd));
-
- lbs_deb_sdio("send DEEP_SLEEP command\n");
- ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
- lbs_cmd_copyback, (unsigned long) &cmd);
- if (ret)
- netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n");
-
- mdelay(200);
- return ret;
-}
-
-static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
-{
- struct if_sdio_card *card = priv->card;
- int ret = -1;
-
- lbs_deb_enter(LBS_DEB_SDIO);
- sdio_claim_host(card->func);
-
- sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
- if (ret)
- netdev_err(priv->dev, "sdio_writeb failed!\n");
-
- sdio_release_host(card->func);
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
- return ret;
-}
-
-static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
-{
- struct if_sdio_card *card = priv->card;
- int ret = -1;
-
- lbs_deb_enter(LBS_DEB_SDIO);
- sdio_claim_host(card->func);
-
- sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
- if (ret)
- netdev_err(priv->dev, "sdio_writeb failed!\n");
-
- sdio_release_host(card->func);
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
- return ret;
-
-}
-
-static struct mmc_host *reset_host;
-
-static void if_sdio_reset_card_worker(struct work_struct *work)
-{
- /*
- * The actual reset operation must be run outside of lbs_thread. This
- * is because mmc_remove_host() will cause the device to be instantly
- * destroyed, and the libertas driver then needs to end lbs_thread,
- * leading to a deadlock.
- *
- * We run it in a workqueue totally independent from the if_sdio_card
- * instance for that reason.
- */
-
- pr_info("Resetting card...");
- mmc_remove_host(reset_host);
- mmc_add_host(reset_host);
-}
-static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker);
-
-static void if_sdio_reset_card(struct lbs_private *priv)
-{
- struct if_sdio_card *card = priv->card;
-
- if (work_pending(&card_reset_work))
- return;
-
- reset_host = card->func->card->host;
- schedule_work(&card_reset_work);
-}
-
-static int if_sdio_power_save(struct lbs_private *priv)
-{
- struct if_sdio_card *card = priv->card;
- int ret;
-
- flush_workqueue(card->workqueue);
-
- ret = if_sdio_power_off(card);
-
- /* Let runtime PM know the card is powered off */
- pm_runtime_put_sync(&card->func->dev);
-
- return ret;
-}
-
-static int if_sdio_power_restore(struct lbs_private *priv)
-{
- struct if_sdio_card *card = priv->card;
- int r;
-
- /* Make sure the card will not be powered off by runtime PM */
- pm_runtime_get_sync(&card->func->dev);
-
- r = if_sdio_power_on(card);
- if (r)
- return r;
-
- wait_event(card->pwron_waitq, priv->fw_ready);
- return 0;
-}
-
-
-/*******************************************************************/
-/* SDIO callbacks */
-/*******************************************************************/
-
-static void if_sdio_interrupt(struct sdio_func *func)
-{
- int ret;
- struct if_sdio_card *card;
- u8 cause;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- card = sdio_get_drvdata(func);
-
- cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret);
- if (ret || !cause)
- goto out;
-
- lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause);
-
- sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret);
- if (ret)
- goto out;
-
- /*
- * Ignore the define name, this really means the card has
- * successfully received the command.
- */
- card->priv->is_activity_detected = 1;
- if (cause & IF_SDIO_H_INT_DNLD)
- lbs_host_to_card_done(card->priv);
-
-
- if (cause & IF_SDIO_H_INT_UPLD) {
- ret = if_sdio_card_to_host(card);
- if (ret)
- goto out;
- }
-
- ret = 0;
-
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-}
-
-static int if_sdio_probe(struct sdio_func *func,
- const struct sdio_device_id *id)
-{
- struct if_sdio_card *card;
- struct lbs_private *priv;
- int ret, i;
- unsigned int model;
- struct if_sdio_packet *packet;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- for (i = 0;i < func->card->num_info;i++) {
- if (sscanf(func->card->info[i],
- "802.11 SDIO ID: %x", &model) == 1)
- break;
- if (sscanf(func->card->info[i],
- "ID: %x", &model) == 1)
- break;
- if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
- model = MODEL_8385;
- break;
- }
- }
-
- if (i == func->card->num_info) {
- pr_err("unable to identify card model\n");
- return -ENODEV;
- }
-
- card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL);
- if (!card)
- return -ENOMEM;
-
- card->func = func;
- card->model = model;
-
- switch (card->model) {
- case MODEL_8385:
- card->scratch_reg = IF_SDIO_SCRATCH_OLD;
- break;
- case MODEL_8686:
- card->scratch_reg = IF_SDIO_SCRATCH;
- break;
- case MODEL_8688:
- default: /* for newer chipsets */
- card->scratch_reg = IF_SDIO_FW_STATUS;
- break;
- }
-
- spin_lock_init(&card->lock);
- card->workqueue = create_workqueue("libertas_sdio");
- INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
- init_waitqueue_head(&card->pwron_waitq);
-
- /* Check if we support this card */
- for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
- if (card->model == fw_table[i].model)
- break;
- }
- if (i == ARRAY_SIZE(fw_table)) {
- pr_err("unknown card model 0x%x\n", card->model);
- ret = -ENODEV;
- goto free;
- }
-
- sdio_set_drvdata(func, card);
-
- lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
- "device = 0x%X, model = 0x%X, ioport = 0x%X\n",
- func->class, func->vendor, func->device,
- model, (unsigned)card->ioport);
-
-
- priv = lbs_add_card(card, &func->dev);
- if (!priv) {
- ret = -ENOMEM;
- goto free;
- }
-
- card->priv = priv;
-
- priv->card = card;
- priv->hw_host_to_card = if_sdio_host_to_card;
- priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
- priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
- priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
- priv->reset_card = if_sdio_reset_card;
- priv->power_save = if_sdio_power_save;
- priv->power_restore = if_sdio_power_restore;
-
- ret = if_sdio_power_on(card);
- if (ret)
- goto err_activate_card;
-
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
- return ret;
-
-err_activate_card:
- flush_workqueue(card->workqueue);
- lbs_remove_card(priv);
-free:
- destroy_workqueue(card->workqueue);
- while (card->packets) {
- packet = card->packets;
- card->packets = card->packets->next;
- kfree(packet);
- }
-
- kfree(card);
-
- goto out;
-}
-
-static void if_sdio_remove(struct sdio_func *func)
-{
- struct if_sdio_card *card;
- struct if_sdio_packet *packet;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- card = sdio_get_drvdata(func);
-
- /* Undo decrement done above in if_sdio_probe */
- pm_runtime_get_noresume(&func->dev);
-
- if (user_rmmod && (card->model == MODEL_8688)) {
- /*
- * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
- * multiple functions
- */
- struct cmd_header cmd;
-
- memset(&cmd, 0, sizeof(cmd));
-
- lbs_deb_sdio("send function SHUTDOWN command\n");
- if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN,
- &cmd, sizeof(cmd), lbs_cmd_copyback,
- (unsigned long) &cmd))
- pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n");
- }
-
-
- lbs_deb_sdio("call remove card\n");
- lbs_stop_card(card->priv);
- lbs_remove_card(card->priv);
-
- flush_workqueue(card->workqueue);
- destroy_workqueue(card->workqueue);
-
- while (card->packets) {
- packet = card->packets;
- card->packets = card->packets->next;
- kfree(packet);
- }
-
- kfree(card);
- lbs_deb_leave(LBS_DEB_SDIO);
-}
-
-static int if_sdio_suspend(struct device *dev)
-{
- struct sdio_func *func = dev_to_sdio_func(dev);
- int ret;
- struct if_sdio_card *card = sdio_get_drvdata(func);
-
- mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
-
- /* If we're powered off anyway, just let the mmc layer remove the
- * card. */
- if (!lbs_iface_active(card->priv))
- return -ENOSYS;
-
- dev_info(dev, "%s: suspend: PM flags = 0x%x\n",
- sdio_func_id(func), flags);
-
- /* If we aren't being asked to wake on anything, we should bail out
- * and let the SD stack power down the card.
- */
- if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) {
- dev_info(dev, "Suspend without wake params -- powering down card\n");
- return -ENOSYS;
- }
-
- if (!(flags & MMC_PM_KEEP_POWER)) {
- dev_err(dev, "%s: cannot remain alive while host is suspended\n",
- sdio_func_id(func));
- return -ENOSYS;
- }
-
- ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
- if (ret)
- return ret;
-
- ret = lbs_suspend(card->priv);
- if (ret)
- return ret;
-
- return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
-}
-
-static int if_sdio_resume(struct device *dev)
-{
- struct sdio_func *func = dev_to_sdio_func(dev);
- struct if_sdio_card *card = sdio_get_drvdata(func);
- int ret;
-
- dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func));
-
- ret = lbs_resume(card->priv);
-
- return ret;
-}
-
-static const struct dev_pm_ops if_sdio_pm_ops = {
- .suspend = if_sdio_suspend,
- .resume = if_sdio_resume,
-};
-
-static struct sdio_driver if_sdio_driver = {
- .name = "libertas_sdio",
- .id_table = if_sdio_ids,
- .probe = if_sdio_probe,
- .remove = if_sdio_remove,
- .drv = {
- .pm = &if_sdio_pm_ops,
- },
-};
-
-/*******************************************************************/
-/* Module functions */
-/*******************************************************************/
-
-static int __init if_sdio_init_module(void)
-{
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_SDIO);
-
- printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n");
- printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n");
-
- ret = sdio_register_driver(&if_sdio_driver);
-
- /* Clear the flag in case user removes the card. */
- user_rmmod = 0;
-
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
- return ret;
-}
-
-static void __exit if_sdio_exit_module(void)
-{
- lbs_deb_enter(LBS_DEB_SDIO);
-
- /* Set the flag as user is removing this module. */
- user_rmmod = 1;
-
- cancel_work_sync(&card_reset_work);
-
- sdio_unregister_driver(&if_sdio_driver);
-
- lbs_deb_leave(LBS_DEB_SDIO);
-}
-
-module_init(if_sdio_init_module);
-module_exit(if_sdio_exit_module);
-
-MODULE_DESCRIPTION("Libertas SDIO WLAN Driver");
-MODULE_AUTHOR("Pierre Ossman");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index c00a7daaa4bc..c32889a1e39c 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -495,6 +495,9 @@ struct mac80211_hwsim_data {
const struct ieee80211_regdomain *regd;
struct ieee80211_channel *tmp_chan;
+ struct ieee80211_channel *roc_chan;
+ u32 roc_duration;
+ struct delayed_work roc_start;
struct delayed_work roc_done;
struct delayed_work hw_scan;
struct cfg80211_scan_request *hw_scan_request;
@@ -514,6 +517,7 @@ struct mac80211_hwsim_data {
bool ps_poll_pending;
struct dentry *debugfs;
+ uintptr_t pending_cookie;
struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
@@ -810,6 +814,9 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb);
struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
+ if (WARN_ON(!txrate))
+ return;
+
if (!netif_running(hwsim_mon))
return;
@@ -960,6 +967,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
unsigned int hwsim_flags = 0;
int i;
struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
+ uintptr_t cookie;
if (data->ps != PS_DISABLED)
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
@@ -1018,7 +1026,10 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
goto nla_put_failure;
/* We create a cookie to identify this skb */
- if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb))
+ data->pending_cookie++;
+ cookie = data->pending_cookie;
+ info->rate_driver_data[0] = (void *)cookie;
+ if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, cookie))
goto nla_put_failure;
genlmsg_end(skb, msg_head);
@@ -1247,6 +1258,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
{
struct mac80211_hwsim_data *data = hw->priv;
struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *channel;
bool ack;
@@ -1292,6 +1304,22 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
ARRAY_SIZE(txi->control.rates));
txi->rate_driver_data[0] = channel;
+
+ if (skb->len >= 24 + 8 &&
+ ieee80211_is_probe_resp(hdr->frame_control)) {
+ /* fake header transmission time */
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_rate *txrate;
+ u64 ts;
+
+ mgmt = (struct ieee80211_mgmt *)skb->data;
+ txrate = ieee80211_get_tx_rate(hw, txi);
+ ts = mac80211_hwsim_get_tsf_raw();
+ mgmt->u.probe_resp.timestamp =
+ cpu_to_le64(ts + data->tsf_offset +
+ 24 * 8 * 10 / txrate->bitrate);
+ }
+
mac80211_hwsim_monitor_rx(hw, skb, channel);
/* wmediumd mode check */
@@ -1871,7 +1899,8 @@ static void hw_scan_work(struct work_struct *work)
req->channels[hwsim->scan_chan_idx]->center_freq);
hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx];
- if (hwsim->tmp_chan->flags & IEEE80211_CHAN_NO_IR ||
+ if (hwsim->tmp_chan->flags & (IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR) ||
!req->n_ssids) {
dwell = 120;
} else {
@@ -1987,6 +2016,23 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw,
mutex_unlock(&hwsim->mutex);
}
+static void hw_roc_start(struct work_struct *work)
+{
+ struct mac80211_hwsim_data *hwsim =
+ container_of(work, struct mac80211_hwsim_data, roc_start.work);
+
+ mutex_lock(&hwsim->mutex);
+
+ wiphy_debug(hwsim->hw->wiphy, "hwsim ROC begins\n");
+ hwsim->tmp_chan = hwsim->roc_chan;
+ ieee80211_ready_on_channel(hwsim->hw);
+
+ ieee80211_queue_delayed_work(hwsim->hw, &hwsim->roc_done,
+ msecs_to_jiffies(hwsim->roc_duration));
+
+ mutex_unlock(&hwsim->mutex);
+}
+
static void hw_roc_done(struct work_struct *work)
{
struct mac80211_hwsim_data *hwsim =
@@ -2014,16 +2060,14 @@ static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
return -EBUSY;
}
- hwsim->tmp_chan = chan;
+ hwsim->roc_chan = chan;
+ hwsim->roc_duration = duration;
mutex_unlock(&hwsim->mutex);
wiphy_debug(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n",
chan->center_freq, duration);
+ ieee80211_queue_delayed_work(hw, &hwsim->roc_start, HZ/50);
- ieee80211_ready_on_channel(hw);
-
- ieee80211_queue_delayed_work(hw, &hwsim->roc_done,
- msecs_to_jiffies(duration));
return 0;
}
@@ -2031,6 +2075,7 @@ static int mac80211_hwsim_croc(struct ieee80211_hw *hw)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
+ cancel_delayed_work_sync(&hwsim->roc_start);
cancel_delayed_work_sync(&hwsim->roc_done);
mutex_lock(&hwsim->mutex);
@@ -2375,6 +2420,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
}
+ INIT_DELAYED_WORK(&data->roc_start, hw_roc_start);
INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
@@ -2411,6 +2457,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
NL80211_FEATURE_STATIC_SMPS |
NL80211_FEATURE_DYNAMIC_SMPS |
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
@@ -2710,7 +2757,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
struct mac80211_hwsim_data *data2;
struct ieee80211_tx_info *txi;
struct hwsim_tx_rate *tx_attempts;
- unsigned long ret_skb_ptr;
+ u64 ret_skb_cookie;
struct sk_buff *skb, *tmp;
const u8 *src;
unsigned int hwsim_flags;
@@ -2728,7 +2775,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
- ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+ ret_skb_cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
data2 = get_hwsim_data_ref_from_addr(src);
if (!data2)
@@ -2736,7 +2783,12 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
/* look for the skb matching the cookie passed back from user */
skb_queue_walk_safe(&data2->pending, skb, tmp) {
- if ((unsigned long)skb == ret_skb_ptr) {
+ u64 skb_cookie;
+
+ txi = IEEE80211_SKB_CB(skb);
+ skb_cookie = (u64)(uintptr_t)txi->rate_driver_data[0];
+
+ if (skb_cookie == ret_skb_cookie) {
skb_unlink(skb, &data2->pending);
found = true;
break;
@@ -2827,10 +2879,25 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
/* A frame is received from user space */
memset(&rx_status, 0, sizeof(rx_status));
- /* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel
- * packets?
- */
- rx_status.freq = data2->channel->center_freq;
+ if (info->attrs[HWSIM_ATTR_FREQ]) {
+ /* throw away off-channel packets, but allow both the temporary
+ * ("hw" scan/remain-on-channel) and regular channel, since the
+ * internal datapath also allows this
+ */
+ mutex_lock(&data2->mutex);
+ rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]);
+
+ if (rx_status.freq != data2->channel->center_freq &&
+ (!data2->tmp_chan ||
+ rx_status.freq != data2->tmp_chan->center_freq)) {
+ mutex_unlock(&data2->mutex);
+ goto out;
+ }
+ mutex_unlock(&data2->mutex);
+ } else {
+ rx_status.freq = data2->channel->center_freq;
+ }
+
rx_status.band = data2->channel->band;
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig
new file mode 100644
index 000000000000..4938c7ec0009
--- /dev/null
+++ b/drivers/net/wireless/marvell/Kconfig
@@ -0,0 +1,27 @@
+config WLAN_VENDOR_MARVELL
+ bool "Marvell devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_MARVELL
+
+source "drivers/net/wireless/marvell/libertas/Kconfig"
+source "drivers/net/wireless/marvell/libertas_tf/Kconfig"
+source "drivers/net/wireless/marvell/mwifiex/Kconfig"
+
+config MWL8K
+ tristate "Marvell 88W8xxx PCI/PCIe Wireless support"
+ depends on MAC80211 && PCI
+ ---help---
+ This driver supports Marvell TOPDOG 802.11 wireless cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mwl8k. If unsure, say N.
+
+endif # WLAN_VENDOR_MARVELL
diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile
new file mode 100644
index 000000000000..1b0a7d2bc8e6
--- /dev/null
+++ b/drivers/net/wireless/marvell/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_LIBERTAS) += libertas/
+
+obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
+obj-$(CONFIG_MWIFIEX) += mwifiex/
+
+obj-$(CONFIG_MWL8K) += mwl8k.o
diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/marvell/libertas/Kconfig
index e6268ceacbf1..e6268ceacbf1 100644
--- a/drivers/net/wireless/libertas/Kconfig
+++ b/drivers/net/wireless/marvell/libertas/Kconfig
diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/marvell/libertas/LICENSE
index 8862742213b9..8862742213b9 100644
--- a/drivers/net/wireless/libertas/LICENSE
+++ b/drivers/net/wireless/marvell/libertas/LICENSE
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/marvell/libertas/Makefile
index eac72f7bd341..eac72f7bd341 100644
--- a/drivers/net/wireless/libertas/Makefile
+++ b/drivers/net/wireless/marvell/libertas/Makefile
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/marvell/libertas/README
index 1a554a685e91..1a554a685e91 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/marvell/libertas/README
diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c
new file mode 100644
index 000000000000..86955c416b30
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas/cfg.c
@@ -0,0 +1,2216 @@
+/*
+ * Implement cfg80211 ("iw") support.
+ *
+ * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany
+ * Holger Schurig <hs4233@mail.mn-solutions.de>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hardirq.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <asm/unaligned.h>
+
+#include "decl.h"
+#include "cfg.h"
+#include "cmd.h"
+#include "mesh.h"
+
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel lbs_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),
+};
+
+#define RATETAB_ENT(_rate, _hw_value, _flags) { \
+ .bitrate = (_rate), \
+ .hw_value = (_hw_value), \
+ .flags = (_flags), \
+}
+
+
+/* Table 6 in section 3.2.1.1 */
+static struct ieee80211_rate lbs_rates[] = {
+ RATETAB_ENT(10, 0, 0),
+ RATETAB_ENT(20, 1, 0),
+ RATETAB_ENT(55, 2, 0),
+ RATETAB_ENT(110, 3, 0),
+ RATETAB_ENT(60, 9, 0),
+ RATETAB_ENT(90, 6, 0),
+ RATETAB_ENT(120, 7, 0),
+ RATETAB_ENT(180, 8, 0),
+ RATETAB_ENT(240, 9, 0),
+ RATETAB_ENT(360, 10, 0),
+ RATETAB_ENT(480, 11, 0),
+ RATETAB_ENT(540, 12, 0),
+};
+
+static struct ieee80211_supported_band lbs_band_2ghz = {
+ .channels = lbs_2ghz_channels,
+ .n_channels = ARRAY_SIZE(lbs_2ghz_channels),
+ .bitrates = lbs_rates,
+ .n_bitrates = ARRAY_SIZE(lbs_rates),
+};
+
+
+static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+};
+
+/* Time to stay on the channel */
+#define LBS_DWELL_PASSIVE 100
+#define LBS_DWELL_ACTIVE 40
+
+
+/***************************************************************************
+ * Misc utility functions
+ *
+ * TLVs are Marvell specific. They are very similar to IEs, they have the
+ * same structure: type, length, data*. The only difference: for IEs, the
+ * type and length are u8, but for TLVs they're __le16.
+ */
+
+/*
+ * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1
+ * in the firmware spec
+ */
+static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type)
+{
+ int ret = -ENOTSUPP;
+
+ switch (auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ ret = auth_type;
+ break;
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ ret = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ ret = 0x80;
+ break;
+ default:
+ /* silence compiler */
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * Various firmware commands need the list of supported rates, but with
+ * the hight-bit set for basic rates
+ */
+static int lbs_add_rates(u8 *rates)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
+ u8 rate = lbs_rates[i].bitrate / 5;
+ if (rate == 0x02 || rate == 0x04 ||
+ rate == 0x0b || rate == 0x16)
+ rate |= 0x80;
+ rates[i] = rate;
+ }
+ return ARRAY_SIZE(lbs_rates);
+}
+
+
+/***************************************************************************
+ * TLV utility functions
+ *
+ * TLVs are Marvell specific. They are very similar to IEs, they have the
+ * same structure: type, length, data*. The only difference: for IEs, the
+ * type and length are u8, but for TLVs they're __le16.
+ */
+
+
+/*
+ * Add ssid TLV
+ */
+#define LBS_MAX_SSID_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + IEEE80211_MAX_SSID_LEN)
+
+static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len)
+{
+ struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv;
+
+ /*
+ * TLV-ID SSID 00 00
+ * length 06 00
+ * ssid 4d 4e 54 45 53 54
+ */
+ ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
+ ssid_tlv->header.len = cpu_to_le16(ssid_len);
+ memcpy(ssid_tlv->ssid, ssid, ssid_len);
+ return sizeof(ssid_tlv->header) + ssid_len;
+}
+
+
+/*
+ * Add channel list TLV (section 8.4.2)
+ *
+ * Actual channel data comes from priv->wdev->wiphy->channels.
+ */
+#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset)))
+
+static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv,
+ int last_channel, int active_scan)
+{
+ int chanscanparamsize = sizeof(struct chanscanparamset) *
+ (last_channel - priv->scan_channel);
+
+ struct mrvl_ie_header *header = (void *) tlv;
+
+ /*
+ * TLV-ID CHANLIST 01 01
+ * length 0e 00
+ * channel 00 01 00 00 00 64 00
+ * radio type 00
+ * channel 01
+ * scan type 00
+ * min scan time 00 00
+ * max scan time 64 00
+ * channel 2 00 02 00 00 00 64 00
+ *
+ */
+
+ header->type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ header->len = cpu_to_le16(chanscanparamsize);
+ tlv += sizeof(struct mrvl_ie_header);
+
+ /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel,
+ last_channel); */
+ memset(tlv, 0, chanscanparamsize);
+
+ while (priv->scan_channel < last_channel) {
+ struct chanscanparamset *param = (void *) tlv;
+
+ param->radiotype = CMD_SCAN_RADIO_TYPE_BG;
+ param->channumber =
+ priv->scan_req->channels[priv->scan_channel]->hw_value;
+ if (active_scan) {
+ param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE);
+ } else {
+ param->chanscanmode.passivescan = 1;
+ param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE);
+ }
+ tlv += sizeof(struct chanscanparamset);
+ priv->scan_channel++;
+ }
+ return sizeof(struct mrvl_ie_header) + chanscanparamsize;
+}
+
+
+/*
+ * Add rates TLV
+ *
+ * The rates are in lbs_bg_rates[], but for the 802.11b
+ * rates the high bit is set. We add this TLV only because
+ * there's a firmware which otherwise doesn't report all
+ * APs in range.
+ */
+#define LBS_MAX_RATES_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + (ARRAY_SIZE(lbs_rates)))
+
+/* Adds a TLV with all rates the hardware supports */
+static int lbs_add_supported_rates_tlv(u8 *tlv)
+{
+ size_t i;
+ struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
+
+ /*
+ * TLV-ID RATES 01 00
+ * length 0e 00
+ * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c
+ */
+ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
+ tlv += sizeof(rate_tlv->header);
+ i = lbs_add_rates(tlv);
+ tlv += i;
+ rate_tlv->header.len = cpu_to_le16(i);
+ return sizeof(rate_tlv->header) + i;
+}
+
+/* Add common rates from a TLV and return the new end of the TLV */
+static u8 *
+add_ie_rates(u8 *tlv, const u8 *ie, int *nrates)
+{
+ int hw, ap, ap_max = ie[1];
+ u8 hw_rate;
+
+ /* Advance past IE header */
+ ie += 2;
+
+ lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max);
+
+ for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
+ hw_rate = lbs_rates[hw].bitrate / 5;
+ for (ap = 0; ap < ap_max; ap++) {
+ if (hw_rate == (ie[ap] & 0x7f)) {
+ *tlv++ = ie[ap];
+ *nrates = *nrates + 1;
+ }
+ }
+ }
+ return tlv;
+}
+
+/*
+ * Adds a TLV with all rates the hardware *and* BSS supports.
+ */
+static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
+{
+ struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
+ const u8 *rates_eid, *ext_rates_eid;
+ int n = 0;
+
+ rcu_read_lock();
+ rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+
+ /*
+ * 01 00 TLV_TYPE_RATES
+ * 04 00 len
+ * 82 84 8b 96 rates
+ */
+ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
+ tlv += sizeof(rate_tlv->header);
+
+ /* Add basic rates */
+ if (rates_eid) {
+ tlv = add_ie_rates(tlv, rates_eid, &n);
+
+ /* Add extended rates, if any */
+ if (ext_rates_eid)
+ tlv = add_ie_rates(tlv, ext_rates_eid, &n);
+ } else {
+ lbs_deb_assoc("assoc: bss had no basic rate IE\n");
+ /* Fallback: add basic 802.11b rates */
+ *tlv++ = 0x82;
+ *tlv++ = 0x84;
+ *tlv++ = 0x8b;
+ *tlv++ = 0x96;
+ n = 4;
+ }
+ rcu_read_unlock();
+
+ rate_tlv->header.len = cpu_to_le16(n);
+ return sizeof(rate_tlv->header) + n;
+}
+
+
+/*
+ * Add auth type TLV.
+ *
+ * This is only needed for newer firmware (V9 and up).
+ */
+#define LBS_MAX_AUTH_TYPE_TLV_SIZE \
+ sizeof(struct mrvl_ie_auth_type)
+
+static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type)
+{
+ struct mrvl_ie_auth_type *auth = (void *) tlv;
+
+ /*
+ * 1f 01 TLV_TYPE_AUTH_TYPE
+ * 01 00 len
+ * 01 auth type
+ */
+ auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header));
+ auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type));
+ return sizeof(*auth);
+}
+
+
+/*
+ * Add channel (phy ds) TLV
+ */
+#define LBS_MAX_CHANNEL_TLV_SIZE \
+ sizeof(struct mrvl_ie_header)
+
+static int lbs_add_channel_tlv(u8 *tlv, u8 channel)
+{
+ struct mrvl_ie_ds_param_set *ds = (void *) tlv;
+
+ /*
+ * 03 00 TLV_TYPE_PHY_DS
+ * 01 00 len
+ * 06 channel
+ */
+ ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
+ ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header));
+ ds->channel = channel;
+ return sizeof(*ds);
+}
+
+
+/*
+ * Add (empty) CF param TLV of the form:
+ */
+#define LBS_MAX_CF_PARAM_TLV_SIZE \
+ sizeof(struct mrvl_ie_header)
+
+static int lbs_add_cf_param_tlv(u8 *tlv)
+{
+ struct mrvl_ie_cf_param_set *cf = (void *)tlv;
+
+ /*
+ * 04 00 TLV_TYPE_CF
+ * 06 00 len
+ * 00 cfpcnt
+ * 00 cfpperiod
+ * 00 00 cfpmaxduration
+ * 00 00 cfpdurationremaining
+ */
+ cf->header.type = cpu_to_le16(TLV_TYPE_CF);
+ cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header));
+ return sizeof(*cf);
+}
+
+/*
+ * Add WPA TLV
+ */
+#define LBS_MAX_WPA_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + 128 /* TODO: I guessed the size */)
+
+static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len)
+{
+ size_t tlv_len;
+
+ /*
+ * We need just convert an IE to an TLV. IEs use u8 for the header,
+ * u8 type
+ * u8 len
+ * u8[] data
+ * but TLVs use __le16 instead:
+ * __le16 type
+ * __le16 len
+ * u8[] data
+ */
+ *tlv++ = *ie++;
+ *tlv++ = 0;
+ tlv_len = *tlv++ = *ie++;
+ *tlv++ = 0;
+ while (tlv_len--)
+ *tlv++ = *ie++;
+ /* the TLV is two bytes larger than the IE */
+ return ie_len + 2;
+}
+
+/*
+ * Set Channel
+ */
+
+static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = -ENOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
+ chandef->chan->center_freq,
+ cfg80211_get_chandef_type(chandef));
+
+ if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT)
+ goto out;
+
+ ret = lbs_set_channel(priv, chandef->chan->hw_value);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy,
+ struct net_device *netdev,
+ struct ieee80211_channel *channel)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = -ENOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d",
+ netdev_name(netdev), channel->center_freq);
+
+ if (netdev != priv->mesh_dev)
+ goto out;
+
+ ret = lbs_mesh_set_channel(priv, channel->hw_value);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+/*
+ * Scanning
+ */
+
+/*
+ * When scanning, the firmware doesn't send a nul packet with the power-safe
+ * bit to the AP. So we cannot stay away from our current channel too long,
+ * otherwise we loose data. So take a "nap" while scanning every other
+ * while.
+ */
+#define LBS_SCAN_BEFORE_NAP 4
+
+
+/*
+ * When the firmware reports back a scan-result, it gives us an "u8 rssi",
+ * which isn't really an RSSI, as it becomes larger when moving away from
+ * the AP. Anyway, we need to convert that into mBm.
+ */
+#define LBS_SCAN_RSSI_TO_MBM(rssi) \
+ ((-(int)rssi + 3)*100)
+
+static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
+ struct cmd_header *resp)
+{
+ struct cfg80211_bss *bss;
+ struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
+ int bsssize;
+ const u8 *pos;
+ const u8 *tsfdesc;
+ int tsfsize;
+ int i;
+ int ret = -EILSEQ;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
+
+ lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
+ scanresp->nr_sets, bsssize, le16_to_cpu(resp->size));
+
+ if (scanresp->nr_sets == 0) {
+ ret = 0;
+ goto done;
+ }
+
+ /*
+ * The general layout of the scan response is described in chapter
+ * 5.7.1. Basically we have a common part, then any number of BSS
+ * descriptor sections. Finally we have section with the same number
+ * of TSFs.
+ *
+ * cmd_ds_802_11_scan_rsp
+ * cmd_header
+ * pos_size
+ * nr_sets
+ * bssdesc 1
+ * bssid
+ * rssi
+ * timestamp
+ * intvl
+ * capa
+ * IEs
+ * bssdesc 2
+ * bssdesc n
+ * MrvlIEtypes_TsfFimestamp_t
+ * TSF for BSS 1
+ * TSF for BSS 2
+ * TSF for BSS n
+ */
+
+ pos = scanresp->bssdesc_and_tlvbuffer;
+
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer,
+ scanresp->bssdescriptsize);
+
+ tsfdesc = pos + bsssize;
+ tsfsize = 4 + 8 * scanresp->nr_sets;
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize);
+
+ /* Validity check: we expect a Marvell-Local TLV */
+ i = get_unaligned_le16(tsfdesc);
+ tsfdesc += 2;
+ if (i != TLV_TYPE_TSFTIMESTAMP) {
+ lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i);
+ goto done;
+ }
+
+ /*
+ * Validity check: the TLV holds TSF values with 8 bytes each, so
+ * the size in the TLV must match the nr_sets value
+ */
+ i = get_unaligned_le16(tsfdesc);
+ tsfdesc += 2;
+ if (i / 8 != scanresp->nr_sets) {
+ lbs_deb_scan("scan response: invalid number of TSF timestamp "
+ "sets (expected %d got %d)\n", scanresp->nr_sets,
+ i / 8);
+ goto done;
+ }
+
+ for (i = 0; i < scanresp->nr_sets; i++) {
+ const u8 *bssid;
+ const u8 *ie;
+ int left;
+ int ielen;
+ int rssi;
+ u16 intvl;
+ u16 capa;
+ int chan_no = -1;
+ const u8 *ssid = NULL;
+ u8 ssid_len = 0;
+
+ int len = get_unaligned_le16(pos);
+ pos += 2;
+
+ /* BSSID */
+ bssid = pos;
+ pos += ETH_ALEN;
+ /* RSSI */
+ rssi = *pos++;
+ /* Packet time stamp */
+ pos += 8;
+ /* Beacon interval */
+ intvl = get_unaligned_le16(pos);
+ pos += 2;
+ /* Capabilities */
+ capa = get_unaligned_le16(pos);
+ pos += 2;
+
+ /* To find out the channel, we must parse the IEs */
+ ie = pos;
+ /*
+ * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon
+ * interval, capabilities
+ */
+ ielen = left = len - (6 + 1 + 8 + 2 + 2);
+ while (left >= 2) {
+ u8 id, elen;
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+ if (elen > left) {
+ lbs_deb_scan("scan response: invalid IE fmt\n");
+ goto done;
+ }
+
+ if (id == WLAN_EID_DS_PARAMS)
+ chan_no = *pos;
+ if (id == WLAN_EID_SSID) {
+ ssid = pos;
+ ssid_len = elen;
+ }
+ left -= elen;
+ pos += elen;
+ }
+
+ /* No channel, no luck */
+ if (chan_no != -1) {
+ struct wiphy *wiphy = priv->wdev->wiphy;
+ int freq = ieee80211_channel_to_frequency(chan_no,
+ IEEE80211_BAND_2GHZ);
+ struct ieee80211_channel *channel =
+ ieee80211_get_channel(wiphy, freq);
+
+ lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %*pE, %d dBm\n",
+ bssid, capa, chan_no, ssid_len, ssid,
+ LBS_SCAN_RSSI_TO_MBM(rssi)/100);
+
+ if (channel &&
+ !(channel->flags & IEEE80211_CHAN_DISABLED)) {
+ bss = cfg80211_inform_bss(wiphy, channel,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ bssid, get_unaligned_le64(tsfdesc),
+ capa, intvl, ie, ielen,
+ LBS_SCAN_RSSI_TO_MBM(rssi),
+ GFP_KERNEL);
+ cfg80211_put_bss(wiphy, bss);
+ }
+ } else
+ lbs_deb_scan("scan response: missing BSS channel IE\n");
+
+ tsfdesc += 8;
+ }
+ ret = 0;
+
+ done:
+ lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+ return ret;
+}
+
+
+/*
+ * Our scan command contains a TLV, consting of a SSID TLV, a channel list
+ * TLV and a rates TLV. Determine the maximum size of them:
+ */
+#define LBS_SCAN_MAX_CMD_SIZE \
+ (sizeof(struct cmd_ds_802_11_scan) \
+ + LBS_MAX_SSID_TLV_SIZE \
+ + LBS_MAX_CHANNEL_LIST_TLV_SIZE \
+ + LBS_MAX_RATES_TLV_SIZE)
+
+/*
+ * Assumes priv->scan_req is initialized and valid
+ * Assumes priv->scan_channel is initialized
+ */
+static void lbs_scan_worker(struct work_struct *work)
+{
+ struct lbs_private *priv =
+ container_of(work, struct lbs_private, scan_work.work);
+ struct cmd_ds_802_11_scan *scan_cmd;
+ u8 *tlv; /* pointer into our current, growing TLV storage area */
+ int last_channel;
+ int running, carrier;
+
+ lbs_deb_enter(LBS_DEB_SCAN);
+
+ scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL);
+ if (scan_cmd == NULL)
+ goto out_no_scan_cmd;
+
+ /* prepare fixed part of scan command */
+ scan_cmd->bsstype = CMD_BSS_TYPE_ANY;
+
+ /* stop network while we're away from our main channel */
+ running = !netif_queue_stopped(priv->dev);
+ carrier = netif_carrier_ok(priv->dev);
+ if (running)
+ netif_stop_queue(priv->dev);
+ if (carrier)
+ netif_carrier_off(priv->dev);
+
+ /* prepare fixed part of scan command */
+ tlv = scan_cmd->tlvbuffer;
+
+ /* add SSID TLV */
+ if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0)
+ tlv += lbs_add_ssid_tlv(tlv,
+ priv->scan_req->ssids[0].ssid,
+ priv->scan_req->ssids[0].ssid_len);
+
+ /* add channel TLVs */
+ last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP;
+ if (last_channel > priv->scan_req->n_channels)
+ last_channel = priv->scan_req->n_channels;
+ tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel,
+ priv->scan_req->n_ssids);
+
+ /* add rates TLV */
+ tlv += lbs_add_supported_rates_tlv(tlv);
+
+ if (priv->scan_channel < priv->scan_req->n_channels) {
+ cancel_delayed_work(&priv->scan_work);
+ if (netif_running(priv->dev))
+ queue_delayed_work(priv->work_thread, &priv->scan_work,
+ msecs_to_jiffies(300));
+ }
+
+ /* This is the final data we are about to send */
+ scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd);
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd,
+ sizeof(*scan_cmd));
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer,
+ tlv - scan_cmd->tlvbuffer);
+
+ __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr,
+ le16_to_cpu(scan_cmd->hdr.size),
+ lbs_ret_scan, 0);
+
+ if (priv->scan_channel >= priv->scan_req->n_channels) {
+ /* Mark scan done */
+ cancel_delayed_work(&priv->scan_work);
+ lbs_scan_done(priv);
+ }
+
+ /* Restart network */
+ if (carrier)
+ netif_carrier_on(priv->dev);
+ if (running && !priv->tx_pending_len)
+ netif_wake_queue(priv->dev);
+
+ kfree(scan_cmd);
+
+ /* Wake up anything waiting on scan completion */
+ if (priv->scan_req == NULL) {
+ lbs_deb_scan("scan: waking up waiters\n");
+ wake_up_all(&priv->scan_q);
+ }
+
+ out_no_scan_cmd:
+ lbs_deb_leave(LBS_DEB_SCAN);
+}
+
+static void _internal_start_scan(struct lbs_private *priv, bool internal,
+ struct cfg80211_scan_request *request)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
+ request->n_ssids, request->n_channels, request->ie_len);
+
+ priv->scan_channel = 0;
+ priv->scan_req = request;
+ priv->internal_scan = internal;
+
+ queue_delayed_work(priv->work_thread, &priv->scan_work,
+ msecs_to_jiffies(50));
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+/*
+ * Clean up priv->scan_req. Should be used to handle the allocation details.
+ */
+void lbs_scan_done(struct lbs_private *priv)
+{
+ WARN_ON(!priv->scan_req);
+
+ if (priv->internal_scan)
+ kfree(priv->scan_req);
+ else
+ cfg80211_scan_done(priv->scan_req, false);
+
+ priv->scan_req = NULL;
+}
+
+static int lbs_cfg_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (priv->scan_req || delayed_work_pending(&priv->scan_work)) {
+ /* old scan request not yet processed */
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ _internal_start_scan(priv, false, request);
+
+ if (priv->surpriseremoved)
+ ret = -EIO;
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+
+/*
+ * Events
+ */
+
+void lbs_send_disconnect_notification(struct lbs_private *priv,
+ bool locally_generated)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated,
+ GFP_KERNEL);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ cfg80211_michael_mic_failure(priv->dev,
+ priv->assoc_bss,
+ event == MACREG_INT_CODE_MIC_ERR_MULTICAST ?
+ NL80211_KEYTYPE_GROUP :
+ NL80211_KEYTYPE_PAIRWISE,
+ -1,
+ NULL,
+ GFP_KERNEL);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+
+
+
+/*
+ * Connect/disconnect
+ */
+
+
+/*
+ * This removes all WEP keys
+ */
+static int lbs_remove_wep_keys(struct lbs_private *priv)
+{
+ struct cmd_ds_802_11_set_wep cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.keyindex = cpu_to_le16(priv->wep_tx_key);
+ cmd.action = cpu_to_le16(CMD_ACT_REMOVE);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+/*
+ * Set WEP keys
+ */
+static int lbs_set_wep_keys(struct lbs_private *priv)
+{
+ struct cmd_ds_802_11_set_wep cmd;
+ int i;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /*
+ * command 13 00
+ * size 50 00
+ * sequence xx xx
+ * result 00 00
+ * action 02 00 ACT_ADD
+ * transmit key 00 00
+ * type for key 1 01 WEP40
+ * type for key 2 00
+ * type for key 3 00
+ * type for key 4 00
+ * key 1 39 39 39 39 39 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * key 2 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * key 3 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * key 4 00 00 00 00 00 00 00 00
+ */
+ if (priv->wep_key_len[0] || priv->wep_key_len[1] ||
+ priv->wep_key_len[2] || priv->wep_key_len[3]) {
+ /* Only set wep keys if we have at least one of them */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.keyindex = cpu_to_le16(priv->wep_tx_key);
+ cmd.action = cpu_to_le16(CMD_ACT_ADD);
+
+ for (i = 0; i < 4; i++) {
+ switch (priv->wep_key_len[i]) {
+ case WLAN_KEY_LEN_WEP40:
+ cmd.keytype[i] = CMD_TYPE_WEP_40_BIT;
+ break;
+ case WLAN_KEY_LEN_WEP104:
+ cmd.keytype[i] = CMD_TYPE_WEP_104_BIT;
+ break;
+ default:
+ cmd.keytype[i] = 0;
+ break;
+ }
+ memcpy(cmd.keymaterial[i], priv->wep_key[i],
+ priv->wep_key_len[i]);
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
+ } else {
+ /* Otherwise remove all wep keys */
+ ret = lbs_remove_wep_keys(priv);
+ }
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+
+/*
+ * Enable/Disable RSN status
+ */
+static int lbs_enable_rsn(struct lbs_private *priv, int enable)
+{
+ struct cmd_ds_802_11_enable_rsn cmd;
+ int ret;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable);
+
+ /*
+ * cmd 2f 00
+ * size 0c 00
+ * sequence xx xx
+ * result 00 00
+ * action 01 00 ACT_SET
+ * enable 01 00
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.enable = cpu_to_le16(enable);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+
+/*
+ * Set WPA/WPA key material
+ */
+
+/*
+ * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we
+ * get rid of WEXT, this should go into host.h
+ */
+
+struct cmd_key_material {
+ struct cmd_header hdr;
+
+ __le16 action;
+ struct MrvlIEtype_keyParamSet param;
+} __packed;
+
+static int lbs_set_key_material(struct lbs_private *priv,
+ int key_type, int key_info,
+ const u8 *key, u16 key_len)
+{
+ struct cmd_key_material cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /*
+ * Example for WPA (TKIP):
+ *
+ * cmd 5e 00
+ * size 34 00
+ * sequence xx xx
+ * result 00 00
+ * action 01 00
+ * TLV type 00 01 key param
+ * length 00 26
+ * key type 01 00 TKIP
+ * key info 06 00 UNICAST | ENABLED
+ * key len 20 00
+ * key 32 bytes
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
+ cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4);
+ cmd.param.keytypeid = cpu_to_le16(key_type);
+ cmd.param.keyinfo = cpu_to_le16(key_info);
+ cmd.param.keylen = cpu_to_le16(key_len);
+ if (key && key_len)
+ memcpy(cmd.param.key, key, key_len);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+
+/*
+ * Sets the auth type (open, shared, etc) in the firmware. That
+ * we use CMD_802_11_AUTHENTICATE is misleading, this firmware
+ * command doesn't send an authentication frame at all, it just
+ * stores the auth_type.
+ */
+static int lbs_set_authtype(struct lbs_private *priv,
+ struct cfg80211_connect_params *sme)
+{
+ struct cmd_ds_802_11_authenticate cmd;
+ int ret;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type);
+
+ /*
+ * cmd 11 00
+ * size 19 00
+ * sequence xx xx
+ * result 00 00
+ * BSS id 00 13 19 80 da 30
+ * auth type 00
+ * reserved 00 00 00 00 00 00 00 00 00 00
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ if (sme->bssid)
+ memcpy(cmd.bssid, sme->bssid, ETH_ALEN);
+ /* convert auth_type */
+ ret = lbs_auth_to_authtype(sme->auth_type);
+ if (ret < 0)
+ goto done;
+
+ cmd.authtype = ret;
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd);
+
+ done:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+/*
+ * Create association request
+ */
+#define LBS_ASSOC_MAX_CMD_SIZE \
+ (sizeof(struct cmd_ds_802_11_associate) \
+ - 512 /* cmd_ds_802_11_associate.iebuf */ \
+ + LBS_MAX_SSID_TLV_SIZE \
+ + LBS_MAX_CHANNEL_TLV_SIZE \
+ + LBS_MAX_CF_PARAM_TLV_SIZE \
+ + LBS_MAX_AUTH_TYPE_TLV_SIZE \
+ + LBS_MAX_WPA_TLV_SIZE)
+
+static int lbs_associate(struct lbs_private *priv,
+ struct cfg80211_bss *bss,
+ struct cfg80211_connect_params *sme)
+{
+ struct cmd_ds_802_11_associate_response *resp;
+ struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE,
+ GFP_KERNEL);
+ const u8 *ssid_eid;
+ size_t len, resp_ie_len;
+ int status;
+ int ret;
+ u8 *pos;
+ u8 *tmp;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ pos = &cmd->iebuf[0];
+
+ /*
+ * cmd 50 00
+ * length 34 00
+ * sequence xx xx
+ * result 00 00
+ * BSS id 00 13 19 80 da 30
+ * capabilities 11 00
+ * listen interval 0a 00
+ * beacon interval 00 00
+ * DTIM period 00
+ * TLVs xx (up to 512 bytes)
+ */
+ cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE);
+
+ /* Fill in static fields */
+ memcpy(cmd->bssid, bss->bssid, ETH_ALEN);
+ cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
+ cmd->capability = cpu_to_le16(bss->capability);
+
+ /* add SSID TLV */
+ rcu_read_lock();
+ ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ssid_eid)
+ pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]);
+ else
+ lbs_deb_assoc("no SSID\n");
+ rcu_read_unlock();
+
+ /* add DS param TLV */
+ if (bss->channel)
+ pos += lbs_add_channel_tlv(pos, bss->channel->hw_value);
+ else
+ lbs_deb_assoc("no channel\n");
+
+ /* add (empty) CF param TLV */
+ pos += lbs_add_cf_param_tlv(pos);
+
+ /* add rates TLV */
+ tmp = pos + 4; /* skip Marvell IE header */
+ pos += lbs_add_common_rates_tlv(pos, bss);
+ lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp);
+
+ /* add auth type TLV */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9)
+ pos += lbs_add_auth_type_tlv(pos, sme->auth_type);
+
+ /* add WPA/WPA2 TLV */
+ if (sme->ie && sme->ie_len)
+ pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len);
+
+ len = (sizeof(*cmd) - sizeof(cmd->iebuf)) +
+ (u16)(pos - (u8 *) &cmd->iebuf);
+ cmd->hdr.size = cpu_to_le16(len);
+
+ lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd,
+ le16_to_cpu(cmd->hdr.size));
+
+ /* store for later use */
+ memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd);
+ if (ret)
+ goto done;
+
+ /* generate connect message to cfg80211 */
+
+ resp = (void *) cmd; /* recast for easier field access */
+ status = le16_to_cpu(resp->statuscode);
+
+ /* Older FW versions map the IEEE 802.11 Status Code in the association
+ * response to the following values returned in resp->statuscode:
+ *
+ * IEEE Status Code Marvell Status Code
+ * 0 -> 0x0000 ASSOC_RESULT_SUCCESS
+ * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * others -> 0x0003 ASSOC_RESULT_REFUSED
+ *
+ * Other response codes:
+ * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
+ * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
+ * association response from the AP)
+ */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
+ switch (status) {
+ case 0:
+ break;
+ case 1:
+ lbs_deb_assoc("invalid association parameters\n");
+ status = WLAN_STATUS_CAPS_UNSUPPORTED;
+ break;
+ case 2:
+ lbs_deb_assoc("timer expired while waiting for AP\n");
+ status = WLAN_STATUS_AUTH_TIMEOUT;
+ break;
+ case 3:
+ lbs_deb_assoc("association refused by AP\n");
+ status = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ break;
+ case 4:
+ lbs_deb_assoc("authentication refused by AP\n");
+ status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ break;
+ default:
+ lbs_deb_assoc("association failure %d\n", status);
+ /* v5 OLPC firmware does return the AP status code if
+ * it's not one of the values above. Let that through.
+ */
+ break;
+ }
+ }
+
+ lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, "
+ "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode),
+ le16_to_cpu(resp->capability), le16_to_cpu(resp->aid));
+
+ resp_ie_len = le16_to_cpu(resp->hdr.size)
+ - sizeof(resp->hdr)
+ - 6;
+ cfg80211_connect_result(priv->dev,
+ priv->assoc_bss,
+ sme->ie, sme->ie_len,
+ resp->iebuf, resp_ie_len,
+ status,
+ GFP_KERNEL);
+
+ if (status == 0) {
+ /* TODO: get rid of priv->connect_status */
+ priv->connect_status = LBS_CONNECTED;
+ netif_carrier_on(priv->dev);
+ if (!priv->tx_pending_len)
+ netif_tx_wake_all_queues(priv->dev);
+ }
+
+ kfree(cmd);
+done:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+static struct cfg80211_scan_request *
+_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme)
+{
+ struct cfg80211_scan_request *creq = NULL;
+ int i, n_channels = ieee80211_get_num_supported_channels(wiphy);
+ enum ieee80211_band band;
+
+ creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
+ n_channels * sizeof(void *),
+ GFP_ATOMIC);
+ if (!creq)
+ return NULL;
+
+ /* SSIDs come after channels */
+ creq->ssids = (void *)&creq->channels[n_channels];
+ creq->n_channels = n_channels;
+ creq->n_ssids = 1;
+
+ /* Scan all available channels */
+ i = 0;
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ int j;
+
+ if (!wiphy->bands[band])
+ continue;
+
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
+ /* ignore disabled channels */
+ if (wiphy->bands[band]->channels[j].flags &
+ IEEE80211_CHAN_DISABLED)
+ continue;
+
+ creq->channels[i] = &wiphy->bands[band]->channels[j];
+ i++;
+ }
+ }
+ if (i) {
+ /* Set real number of channels specified in creq->channels[] */
+ creq->n_channels = i;
+
+ /* Scan for the SSID we're going to connect to */
+ memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len);
+ creq->ssids[0].ssid_len = sme->ssid_len;
+ } else {
+ /* No channels found... */
+ kfree(creq);
+ creq = NULL;
+ }
+
+ return creq;
+}
+
+static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ struct cfg80211_bss *bss = NULL;
+ int ret = 0;
+ u8 preamble = RADIO_PREAMBLE_SHORT;
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!sme->bssid) {
+ struct cfg80211_scan_request *creq;
+
+ /*
+ * Scan for the requested network after waiting for existing
+ * scans to finish.
+ */
+ lbs_deb_assoc("assoc: waiting for existing scans\n");
+ wait_event_interruptible_timeout(priv->scan_q,
+ (priv->scan_req == NULL),
+ (15 * HZ));
+
+ creq = _new_connect_scan_req(wiphy, sme);
+ if (!creq) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ lbs_deb_assoc("assoc: scanning for compatible AP\n");
+ _internal_start_scan(priv, true, creq);
+
+ lbs_deb_assoc("assoc: waiting for scan to complete\n");
+ wait_event_interruptible_timeout(priv->scan_q,
+ (priv->scan_req == NULL),
+ (15 * HZ));
+ lbs_deb_assoc("assoc: scanning completed\n");
+ }
+
+ /* Find the BSS we want using available scan results */
+ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
+ sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY);
+ if (!bss) {
+ wiphy_err(wiphy, "assoc: bss %pM not in scan results\n",
+ sme->bssid);
+ ret = -ENOENT;
+ goto done;
+ }
+ lbs_deb_assoc("trying %pM\n", bss->bssid);
+ lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n",
+ sme->crypto.cipher_group,
+ sme->key_idx, sme->key_len);
+
+ /* As this is a new connection, clear locally stored WEP keys */
+ priv->wep_tx_key = 0;
+ memset(priv->wep_key, 0, sizeof(priv->wep_key));
+ memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
+
+ /* set/remove WEP keys */
+ switch (sme->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* Store provided WEP keys in priv-> */
+ priv->wep_tx_key = sme->key_idx;
+ priv->wep_key_len[sme->key_idx] = sme->key_len;
+ memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len);
+ /* Set WEP keys and WEP mode */
+ lbs_set_wep_keys(priv);
+ priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
+ lbs_set_mac_control(priv);
+ /* No RSN mode for WEP */
+ lbs_enable_rsn(priv, 0);
+ break;
+ case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */
+ /*
+ * If we don't have no WEP, no WPA and no WPA2,
+ * we remove all keys like in the WPA/WPA2 setup,
+ * we just don't set RSN.
+ *
+ * Therefore: fall-through
+ */
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ /* Remove WEP keys and WEP mode */
+ lbs_remove_wep_keys(priv);
+ priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
+ lbs_set_mac_control(priv);
+
+ /* clear the WPA/WPA2 keys */
+ lbs_set_key_material(priv,
+ KEY_TYPE_ID_WEP, /* doesn't matter */
+ KEY_INFO_WPA_UNICAST,
+ NULL, 0);
+ lbs_set_key_material(priv,
+ KEY_TYPE_ID_WEP, /* doesn't matter */
+ KEY_INFO_WPA_MCAST,
+ NULL, 0);
+ /* RSN mode for WPA/WPA2 */
+ lbs_enable_rsn(priv, sme->crypto.cipher_group != 0);
+ break;
+ default:
+ wiphy_err(wiphy, "unsupported cipher group 0x%x\n",
+ sme->crypto.cipher_group);
+ ret = -ENOTSUPP;
+ goto done;
+ }
+
+ ret = lbs_set_authtype(priv, sme);
+ if (ret == -ENOTSUPP) {
+ wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type);
+ goto done;
+ }
+
+ lbs_set_radio(priv, preamble, 1);
+
+ /* Do the actual association */
+ ret = lbs_associate(priv, bss, sme);
+
+ done:
+ if (bss)
+ cfg80211_put_bss(wiphy, bss);
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+int lbs_disconnect(struct lbs_private *priv, u16 reason)
+{
+ struct cmd_ds_802_11_deauthenticate cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ /* Mildly ugly to use a locally store my own BSSID ... */
+ memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN);
+ cmd.reasoncode = cpu_to_le16(reason);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
+ if (ret)
+ return ret;
+
+ cfg80211_disconnected(priv->dev,
+ reason,
+ NULL, 0, true,
+ GFP_KERNEL);
+ priv->connect_status = LBS_DISCONNECTED;
+
+ return 0;
+}
+
+static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
+
+ /* store for lbs_cfg_ret_disconnect() */
+ priv->disassoc_reason = reason_code;
+
+ return lbs_disconnect(priv, reason_code);
+}
+
+static int lbs_cfg_set_default_key(struct wiphy *wiphy,
+ struct net_device *netdev,
+ u8 key_index, bool unicast,
+ bool multicast)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+
+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (key_index != priv->wep_tx_key) {
+ lbs_deb_assoc("set_default_key: to %d\n", key_index);
+ priv->wep_tx_key = key_index;
+ lbs_set_wep_keys(priv);
+ }
+
+ return 0;
+}
+
+
+static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 idx, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ u16 key_info;
+ u16 key_type;
+ int ret = 0;
+
+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n",
+ params->cipher, mac_addr);
+ lbs_deb_assoc("add_key: key index %d, key len %d\n",
+ idx, params->key_len);
+ if (params->key_len)
+ lbs_deb_hex(LBS_DEB_CFG80211, "KEY",
+ params->key, params->key_len);
+
+ lbs_deb_assoc("add_key: seq len %d\n", params->seq_len);
+ if (params->seq_len)
+ lbs_deb_hex(LBS_DEB_CFG80211, "SEQ",
+ params->seq, params->seq_len);
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* actually compare if something has changed ... */
+ if ((priv->wep_key_len[idx] != params->key_len) ||
+ memcmp(priv->wep_key[idx],
+ params->key, params->key_len) != 0) {
+ priv->wep_key_len[idx] = params->key_len;
+ memcpy(priv->wep_key[idx],
+ params->key, params->key_len);
+ lbs_set_wep_keys(priv);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ key_info = KEY_INFO_WPA_ENABLED | ((idx == 0)
+ ? KEY_INFO_WPA_UNICAST
+ : KEY_INFO_WPA_MCAST);
+ key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+ ? KEY_TYPE_ID_TKIP
+ : KEY_TYPE_ID_AES;
+ lbs_set_key_material(priv,
+ key_type,
+ key_info,
+ params->key, params->key_len);
+ break;
+ default:
+ wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher);
+ ret = -ENOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+
+static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n",
+ key_index, mac_addr);
+
+#ifdef TODO
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ /*
+ * I think can keep this a NO-OP, because:
+
+ * - we clear all keys whenever we do lbs_cfg_connect() anyway
+ * - neither "iw" nor "wpa_supplicant" won't call this during
+ * an ongoing connection
+ * - TODO: but I have to check if this is still true when
+ * I set the AP to periodic re-keying
+ * - we've not kzallec() something when we've added a key at
+ * lbs_cfg_connect() or lbs_cfg_add_key().
+ *
+ * This causes lbs_cfg_del_key() only called at disconnect time,
+ * where we'd just waste time deleting a key that is not going
+ * to be used anyway.
+ */
+ if (key_index < 3 && priv->wep_key_len[key_index]) {
+ priv->wep_key_len[key_index] = 0;
+ lbs_set_wep_keys(priv);
+ }
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Get station
+ */
+
+static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ s8 signal, noise;
+ int ret;
+ size_t i;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) |
+ BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_RX_BYTES) |
+ BIT(NL80211_STA_INFO_RX_PACKETS);
+ sinfo->tx_bytes = priv->dev->stats.tx_bytes;
+ sinfo->tx_packets = priv->dev->stats.tx_packets;
+ sinfo->rx_bytes = priv->dev->stats.rx_bytes;
+ sinfo->rx_packets = priv->dev->stats.rx_packets;
+
+ /* Get current RSSI */
+ ret = lbs_get_rssi(priv, &signal, &noise);
+ if (ret == 0) {
+ sinfo->signal = signal;
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ }
+
+ /* Convert priv->cur_rate from hw_value to NL80211 value */
+ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
+ if (priv->cur_rate == lbs_rates[i].hw_value) {
+ sinfo->txrate.legacy = lbs_rates[i].bitrate;
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+/*
+ * Change interface
+ */
+
+static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = 0;
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (priv->iface_running)
+ ret = lbs_set_iface_type(priv, type);
+
+ if (!ret)
+ priv->wdev->iftype = type;
+
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+/*
+ * IBSS (Ad-Hoc)
+ */
+
+/*
+ * The firmware needs the following bits masked out of the beacon-derived
+ * capability field when associating/joining to a BSS:
+ * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused)
+ */
+#define CAPINFO_MASK (~(0xda00))
+
+
+static void lbs_join_post(struct lbs_private *priv,
+ struct cfg80211_ibss_params *params,
+ u8 *bssid, u16 capability)
+{
+ u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */
+ 2 + 4 + /* basic rates */
+ 2 + 1 + /* DS parameter */
+ 2 + 2 + /* atim */
+ 2 + 8]; /* extended rates */
+ u8 *fake = fake_ie;
+ struct cfg80211_bss *bss;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /*
+ * For cfg80211_inform_bss, we'll need a fake IE, as we can't get
+ * the real IE from the firmware. So we fabricate a fake IE based on
+ * what the firmware actually sends (sniffed with wireshark).
+ */
+ /* Fake SSID IE */
+ *fake++ = WLAN_EID_SSID;
+ *fake++ = params->ssid_len;
+ memcpy(fake, params->ssid, params->ssid_len);
+ fake += params->ssid_len;
+ /* Fake supported basic rates IE */
+ *fake++ = WLAN_EID_SUPP_RATES;
+ *fake++ = 4;
+ *fake++ = 0x82;
+ *fake++ = 0x84;
+ *fake++ = 0x8b;
+ *fake++ = 0x96;
+ /* Fake DS channel IE */
+ *fake++ = WLAN_EID_DS_PARAMS;
+ *fake++ = 1;
+ *fake++ = params->chandef.chan->hw_value;
+ /* Fake IBSS params IE */
+ *fake++ = WLAN_EID_IBSS_PARAMS;
+ *fake++ = 2;
+ *fake++ = 0; /* ATIM=0 */
+ *fake++ = 0;
+ /* Fake extended rates IE, TODO: don't add this for 802.11b only,
+ * but I don't know how this could be checked */
+ *fake++ = WLAN_EID_EXT_SUPP_RATES;
+ *fake++ = 8;
+ *fake++ = 0x0c;
+ *fake++ = 0x12;
+ *fake++ = 0x18;
+ *fake++ = 0x24;
+ *fake++ = 0x30;
+ *fake++ = 0x48;
+ *fake++ = 0x60;
+ *fake++ = 0x6c;
+ lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie);
+
+ bss = cfg80211_inform_bss(priv->wdev->wiphy,
+ params->chandef.chan,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ bssid,
+ 0,
+ capability,
+ params->beacon_interval,
+ fake_ie, fake - fake_ie,
+ 0, GFP_KERNEL);
+ cfg80211_put_bss(priv->wdev->wiphy, bss);
+
+ memcpy(priv->wdev->ssid, params->ssid, params->ssid_len);
+ priv->wdev->ssid_len = params->ssid_len;
+
+ cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan,
+ GFP_KERNEL);
+
+ /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */
+ priv->connect_status = LBS_CONNECTED;
+ netif_carrier_on(priv->dev);
+ if (!priv->tx_pending_len)
+ netif_wake_queue(priv->dev);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+static int lbs_ibss_join_existing(struct lbs_private *priv,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_bss *bss)
+{
+ const u8 *rates_eid;
+ struct cmd_ds_802_11_ad_hoc_join cmd;
+ u8 preamble = RADIO_PREAMBLE_SHORT;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /* TODO: set preamble based on scan result */
+ ret = lbs_set_radio(priv, preamble, 1);
+ if (ret)
+ goto out;
+
+ /*
+ * Example CMD_802_11_AD_HOC_JOIN command:
+ *
+ * command 2c 00 CMD_802_11_AD_HOC_JOIN
+ * size 65 00
+ * sequence xx xx
+ * result 00 00
+ * bssid 02 27 27 97 2f 96
+ * ssid 49 42 53 53 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * type 02 CMD_BSS_TYPE_IBSS
+ * beacon period 64 00
+ * dtim period 00
+ * timestamp 00 00 00 00 00 00 00 00
+ * localtime 00 00 00 00 00 00 00 00
+ * IE DS 03
+ * IE DS len 01
+ * IE DS channel 01
+ * reserveed 00 00 00 00
+ * IE IBSS 06
+ * IE IBSS len 02
+ * IE IBSS atim 00 00
+ * reserved 00 00 00 00
+ * capability 02 00
+ * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00
+ * fail timeout ff 00
+ * probe delay 00 00
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN);
+ memcpy(cmd.bss.ssid, params->ssid, params->ssid_len);
+ cmd.bss.type = CMD_BSS_TYPE_IBSS;
+ cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval);
+ cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS;
+ cmd.bss.ds.header.len = 1;
+ cmd.bss.ds.channel = params->chandef.chan->hw_value;
+ cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS;
+ cmd.bss.ibss.header.len = 2;
+ cmd.bss.ibss.atimwindow = 0;
+ cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
+
+ /* set rates to the intersection of our rates and the rates in the
+ bss */
+ rcu_read_lock();
+ rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ if (!rates_eid) {
+ lbs_add_rates(cmd.bss.rates);
+ } else {
+ int hw, i;
+ u8 rates_max = rates_eid[1];
+ u8 *rates = cmd.bss.rates;
+ for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
+ u8 hw_rate = lbs_rates[hw].bitrate / 5;
+ for (i = 0; i < rates_max; i++) {
+ if (hw_rate == (rates_eid[i+2] & 0x7f)) {
+ u8 rate = rates_eid[i+2];
+ if (rate == 0x02 || rate == 0x04 ||
+ rate == 0x0b || rate == 0x16)
+ rate |= 0x80;
+ *rates++ = rate;
+ }
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ /* Only v8 and below support setting this */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
+ cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
+ cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+ }
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd);
+ if (ret)
+ goto out;
+
+ /*
+ * This is a sample response to CMD_802_11_AD_HOC_JOIN:
+ *
+ * response 2c 80
+ * size 09 00
+ * sequence xx xx
+ * result 00 00
+ * reserved 00
+ */
+ lbs_join_post(priv, params, bss->bssid, bss->capability);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+static int lbs_ibss_start_new(struct lbs_private *priv,
+ struct cfg80211_ibss_params *params)
+{
+ struct cmd_ds_802_11_ad_hoc_start cmd;
+ struct cmd_ds_802_11_ad_hoc_result *resp =
+ (struct cmd_ds_802_11_ad_hoc_result *) &cmd;
+ u8 preamble = RADIO_PREAMBLE_SHORT;
+ int ret = 0;
+ u16 capability;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ ret = lbs_set_radio(priv, preamble, 1);
+ if (ret)
+ goto out;
+
+ /*
+ * Example CMD_802_11_AD_HOC_START command:
+ *
+ * command 2b 00 CMD_802_11_AD_HOC_START
+ * size b1 00
+ * sequence xx xx
+ * result 00 00
+ * ssid 54 45 53 54 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * bss type 02
+ * beacon period 64 00
+ * dtim period 00
+ * IE IBSS 06
+ * IE IBSS len 02
+ * IE IBSS atim 00 00
+ * reserved 00 00 00 00
+ * IE DS 03
+ * IE DS len 01
+ * IE DS channel 01
+ * reserved 00 00 00 00
+ * probe delay 00 00
+ * capability 02 00
+ * rates 82 84 8b 96 (basic rates with have bit 7 set)
+ * 0c 12 18 24 30 48 60 6c
+ * padding 100 bytes
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ memcpy(cmd.ssid, params->ssid, params->ssid_len);
+ cmd.bsstype = CMD_BSS_TYPE_IBSS;
+ cmd.beaconperiod = cpu_to_le16(params->beacon_interval);
+ cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS;
+ cmd.ibss.header.len = 2;
+ cmd.ibss.atimwindow = 0;
+ cmd.ds.header.id = WLAN_EID_DS_PARAMS;
+ cmd.ds.header.len = 1;
+ cmd.ds.channel = params->chandef.chan->hw_value;
+ /* Only v8 and below support setting probe delay */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8)
+ cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+ /* TODO: mix in WLAN_CAPABILITY_PRIVACY */
+ capability = WLAN_CAPABILITY_IBSS;
+ cmd.capability = cpu_to_le16(capability);
+ lbs_add_rates(cmd.rates);
+
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd);
+ if (ret)
+ goto out;
+
+ /*
+ * This is a sample response to CMD_802_11_AD_HOC_JOIN:
+ *
+ * response 2b 80
+ * size 14 00
+ * sequence xx xx
+ * result 00 00
+ * reserved 00
+ * bssid 02 2b 7b 0f 86 0e
+ */
+ lbs_join_post(priv, params, resp->bssid, capability);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = 0;
+ struct cfg80211_bss *bss;
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!params->chandef.chan) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ ret = lbs_set_channel(priv, params->chandef.chan->hw_value);
+ if (ret)
+ goto out;
+
+ /* Search if someone is beaconing. This assumes that the
+ * bss list is populated already */
+ bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid,
+ params->ssid, params->ssid_len,
+ IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
+
+ if (bss) {
+ ret = lbs_ibss_join_existing(priv, params, bss);
+ cfg80211_put_bss(wiphy, bss);
+ } else
+ ret = lbs_ibss_start_new(priv, params);
+
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ struct cmd_ds_802_11_ad_hoc_stop cmd;
+ int ret = 0;
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd);
+
+ /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */
+ lbs_mac_event_disconnected(priv, true);
+
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+
+/*
+ * Initialization
+ */
+
+static struct cfg80211_ops lbs_cfg80211_ops = {
+ .set_monitor_channel = lbs_cfg_set_monitor_channel,
+ .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel,
+ .scan = lbs_cfg_scan,
+ .connect = lbs_cfg_connect,
+ .disconnect = lbs_cfg_disconnect,
+ .add_key = lbs_cfg_add_key,
+ .del_key = lbs_cfg_del_key,
+ .set_default_key = lbs_cfg_set_default_key,
+ .get_station = lbs_cfg_get_station,
+ .change_virtual_intf = lbs_change_intf,
+ .join_ibss = lbs_join_ibss,
+ .leave_ibss = lbs_leave_ibss,
+};
+
+
+/*
+ * At this time lbs_private *priv doesn't even exist, so we just allocate
+ * memory and don't initialize the wiphy further. This is postponed until we
+ * can talk to the firmware and happens at registration time in
+ * lbs_cfg_wiphy_register().
+ */
+struct wireless_dev *lbs_cfg_alloc(struct device *dev)
+{
+ int ret = 0;
+ struct wireless_dev *wdev;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!wdev)
+ return ERR_PTR(-ENOMEM);
+
+ wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private));
+ if (!wdev->wiphy) {
+ dev_err(dev, "cannot allocate wiphy\n");
+ ret = -ENOMEM;
+ goto err_wiphy_new;
+ }
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return wdev;
+
+ err_wiphy_new:
+ kfree(wdev);
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ERR_PTR(ret);
+}
+
+
+static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv)
+{
+ struct region_code_mapping {
+ const char *cn;
+ int code;
+ };
+
+ /* Section 5.17.2 */
+ static const struct region_code_mapping regmap[] = {
+ {"US ", 0x10}, /* US FCC */
+ {"CA ", 0x20}, /* Canada */
+ {"EU ", 0x30}, /* ETSI */
+ {"ES ", 0x31}, /* Spain */
+ {"FR ", 0x32}, /* France */
+ {"JP ", 0x40}, /* Japan */
+ };
+ size_t i;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ for (i = 0; i < ARRAY_SIZE(regmap); i++)
+ if (regmap[i].code == priv->regioncode) {
+ regulatory_hint(priv->wdev->wiphy, regmap[i].cn);
+ break;
+ }
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+static void lbs_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain "
+ "callback for domain %c%c\n", request->alpha2[0],
+ request->alpha2[1]);
+
+ memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2));
+ if (lbs_iface_active(priv))
+ lbs_set_11d_domain_info(priv);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+/*
+ * This function get's called after lbs_setup_firmware() determined the
+ * firmware capabities. So we can setup the wiphy according to our
+ * hardware/firmware.
+ */
+int lbs_cfg_register(struct lbs_private *priv)
+{
+ struct wireless_dev *wdev = priv->wdev;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ wdev->wiphy->max_scan_ssids = 1;
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ wdev->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC);
+ if (lbs_rtap_supported(priv))
+ wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ if (lbs_mesh_activated(priv))
+ wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
+
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;
+
+ /*
+ * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have
+ * never seen a firmware without WPA
+ */
+ wdev->wiphy->cipher_suites = cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+ wdev->wiphy->reg_notifier = lbs_reg_notifier;
+
+ ret = wiphy_register(wdev->wiphy);
+ if (ret < 0)
+ pr_err("cannot register wiphy device\n");
+
+ priv->wiphy_registered = true;
+
+ ret = register_netdev(priv->dev);
+ if (ret)
+ pr_err("cannot register network device\n");
+
+ INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
+
+ lbs_cfg_set_regulatory_hint(priv);
+
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+void lbs_scan_deinit(struct lbs_private *priv)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+ cancel_delayed_work_sync(&priv->scan_work);
+}
+
+
+void lbs_cfg_free(struct lbs_private *priv)
+{
+ struct wireless_dev *wdev = priv->wdev;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!wdev)
+ return;
+
+ if (priv->wiphy_registered)
+ wiphy_unregister(wdev->wiphy);
+
+ if (wdev->wiphy)
+ wiphy_free(wdev->wiphy);
+
+ kfree(wdev);
+}
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/marvell/libertas/cfg.h
index acccc2922401..acccc2922401 100644
--- a/drivers/net/wireless/libertas/cfg.h
+++ b/drivers/net/wireless/marvell/libertas/cfg.h
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c
index 0387a5b380c8..0387a5b380c8 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/marvell/libertas/cmd.c
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/marvell/libertas/cmd.h
index 0c5444b02c64..0c5444b02c64 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/marvell/libertas/cmd.h
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/marvell/libertas/cmdresp.c
index e5442e8956f7..e5442e8956f7 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/marvell/libertas/cmdresp.c
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/marvell/libertas/debugfs.c
index faed1823c58e..faed1823c58e 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/marvell/libertas/debugfs.c
diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/marvell/libertas/debugfs.h
index f2b9c7ffe0fd..f2b9c7ffe0fd 100644
--- a/drivers/net/wireless/libertas/debugfs.h
+++ b/drivers/net/wireless/marvell/libertas/debugfs.h
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/marvell/libertas/decl.h
index 84a3aa7ac570..84a3aa7ac570 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/marvell/libertas/decl.h
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/marvell/libertas/defs.h
index 407784aca627..407784aca627 100644
--- a/drivers/net/wireless/libertas/defs.h
+++ b/drivers/net/wireless/marvell/libertas/defs.h
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/marvell/libertas/dev.h
index 6bd1608992b0..6bd1608992b0 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/marvell/libertas/dev.h
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/marvell/libertas/ethtool.c
index f955b2d66ed6..f955b2d66ed6 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/marvell/libertas/ethtool.c
diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c
index 51b92b5df119..51b92b5df119 100644
--- a/drivers/net/wireless/libertas/firmware.c
+++ b/drivers/net/wireless/marvell/libertas/firmware.c
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/marvell/libertas/host.h
index 96726f79a1dd..96726f79a1dd 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/marvell/libertas/host.h
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/marvell/libertas/if_cs.c
index f499efc6abcf..f499efc6abcf 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/marvell/libertas/if_cs.c
diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.c b/drivers/net/wireless/marvell/libertas/if_sdio.c
new file mode 100644
index 000000000000..68fd3a9779bd
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas/if_sdio.c
@@ -0,0 +1,1453 @@
+/*
+ * linux/drivers/net/wireless/libertas/if_sdio.c
+ *
+ * Copyright 2007-2008 Pierre Ossman
+ *
+ * Inspired by if_cs.c, Copyright 2007 Holger Schurig
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This hardware has more or less no CMD53 support, so all registers
+ * must be accessed using sdio_readb()/sdio_writeb().
+ *
+ * Transfers must be in one transaction or the firmware goes bonkers.
+ * This means that the transfer must either be small enough to do a
+ * byte based transfer or it must be padded to a multiple of the
+ * current block size.
+ *
+ * As SDIO is still new to the kernel, it is unfortunately common with
+ * bugs in the host controllers related to that. One such bug is that
+ * controllers cannot do transfers that aren't a multiple of 4 bytes.
+ * If you don't have time to fix the host controller driver, you can
+ * work around the problem by modifying if_sdio_host_to_card() and
+ * if_sdio_card_to_host() to pad the data.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+#include <linux/pm_runtime.h>
+
+#include "host.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "cmd.h"
+#include "if_sdio.h"
+
+static void if_sdio_interrupt(struct sdio_func *func);
+
+/* The if_sdio_remove() callback function is called when
+ * user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently for SD8688 combo chip.
+ * If the user is removing the module, the FUNC_SHUTDOWN
+ * command for SD8688 is sent to the firmware.
+ * If the card is removed, there is no need to send this command.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
+
+static const struct sdio_device_id if_sdio_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
+ SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
+ SDIO_DEVICE_ID_MARVELL_8688WLAN) },
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
+
+#define MODEL_8385 0x04
+#define MODEL_8686 0x0b
+#define MODEL_8688 0x10
+
+static const struct lbs_fw_table fw_table[] = {
+ { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" },
+ { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" },
+ { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" },
+ { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" },
+ { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" },
+ { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" },
+ { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" },
+ { 0, NULL, NULL }
+};
+MODULE_FIRMWARE("libertas/sd8385_helper.bin");
+MODULE_FIRMWARE("libertas/sd8385.bin");
+MODULE_FIRMWARE("sd8385_helper.bin");
+MODULE_FIRMWARE("sd8385.bin");
+MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin");
+MODULE_FIRMWARE("libertas/sd8686_v9.bin");
+MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin");
+MODULE_FIRMWARE("libertas/sd8686_v8.bin");
+MODULE_FIRMWARE("sd8686_helper.bin");
+MODULE_FIRMWARE("sd8686.bin");
+MODULE_FIRMWARE("libertas/sd8688_helper.bin");
+MODULE_FIRMWARE("libertas/sd8688.bin");
+MODULE_FIRMWARE("sd8688_helper.bin");
+MODULE_FIRMWARE("sd8688.bin");
+
+struct if_sdio_packet {
+ struct if_sdio_packet *next;
+ u16 nb;
+ u8 buffer[0] __attribute__((aligned(4)));
+};
+
+struct if_sdio_card {
+ struct sdio_func *func;
+ struct lbs_private *priv;
+
+ int model;
+ unsigned long ioport;
+ unsigned int scratch_reg;
+ bool started;
+ wait_queue_head_t pwron_waitq;
+
+ u8 buffer[65536] __attribute__((aligned(4)));
+
+ spinlock_t lock;
+ struct if_sdio_packet *packets;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct packet_worker;
+
+ u8 rx_unit;
+};
+
+static void if_sdio_finish_power_on(struct if_sdio_card *card);
+static int if_sdio_power_off(struct if_sdio_card *card);
+
+/********************************************************************/
+/* I/O */
+/********************************************************************/
+
+/*
+ * For SD8385/SD8686, this function reads firmware status after
+ * the image is downloaded, or reads RX packet length when
+ * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received.
+ * For SD8688, this function reads firmware status only.
+ */
+static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
+{
+ int ret;
+ u16 scratch;
+
+ scratch = sdio_readb(card->func, card->scratch_reg, &ret);
+ if (!ret)
+ scratch |= sdio_readb(card->func, card->scratch_reg + 1,
+ &ret) << 8;
+
+ if (err)
+ *err = ret;
+
+ if (ret)
+ return 0xffff;
+
+ return scratch;
+}
+
+static u8 if_sdio_read_rx_unit(struct if_sdio_card *card)
+{
+ int ret;
+ u8 rx_unit;
+
+ rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret);
+
+ if (ret)
+ rx_unit = 0;
+
+ return rx_unit;
+}
+
+static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err)
+{
+ int ret;
+ u16 rx_len;
+
+ switch (card->model) {
+ case MODEL_8385:
+ case MODEL_8686:
+ rx_len = if_sdio_read_scratch(card, &ret);
+ break;
+ case MODEL_8688:
+ default: /* for newer chipsets */
+ rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret);
+ if (!ret)
+ rx_len <<= card->rx_unit;
+ else
+ rx_len = 0xffff; /* invalid length */
+
+ break;
+ }
+
+ if (err)
+ *err = ret;
+
+ return rx_len;
+}
+
+static int if_sdio_handle_cmd(struct if_sdio_card *card,
+ u8 *buffer, unsigned size)
+{
+ struct lbs_private *priv = card->priv;
+ int ret;
+ unsigned long flags;
+ u8 i;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ if (size > LBS_CMD_BUFFER_SIZE) {
+ lbs_deb_sdio("response packet too large (%d bytes)\n",
+ (int)size);
+ ret = -E2BIG;
+ goto out;
+ }
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+
+ i = (priv->resp_idx == 0) ? 1 : 0;
+ BUG_ON(priv->resp_len[i]);
+ priv->resp_len[i] = size;
+ memcpy(priv->resp_buf[i], buffer, size);
+ lbs_notify_command_response(priv, i);
+
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ ret = 0;
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+static int if_sdio_handle_data(struct if_sdio_card *card,
+ u8 *buffer, unsigned size)
+{
+ int ret;
+ struct sk_buff *skb;
+ char *data;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
+ lbs_deb_sdio("response packet too large (%d bytes)\n",
+ (int)size);
+ ret = -E2BIG;
+ goto out;
+ }
+
+ skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ data = skb_put(skb, size);
+
+ memcpy(data, buffer, size);
+
+ lbs_process_rxed_packet(card->priv, skb);
+
+ ret = 0;
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+ return ret;
+}
+
+static int if_sdio_handle_event(struct if_sdio_card *card,
+ u8 *buffer, unsigned size)
+{
+ int ret;
+ u32 event;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ if (card->model == MODEL_8385) {
+ event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
+ if (ret)
+ goto out;
+
+ /* right shift 3 bits to get the event id */
+ event >>= 3;
+ } else {
+ if (size < 4) {
+ lbs_deb_sdio("event packet too small (%d bytes)\n",
+ (int)size);
+ ret = -EINVAL;
+ goto out;
+ }
+ event = buffer[3] << 24;
+ event |= buffer[2] << 16;
+ event |= buffer[1] << 8;
+ event |= buffer[0] << 0;
+ }
+
+ lbs_queue_event(card->priv, event & 0xFF);
+ ret = 0;
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+ return ret;
+}
+
+static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition)
+{
+ u8 status;
+ unsigned long timeout;
+ int ret = 0;
+
+ timeout = jiffies + HZ;
+ while (1) {
+ status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
+ if (ret)
+ return ret;
+ if ((status & condition) == condition)
+ break;
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ mdelay(1);
+ }
+ return ret;
+}
+
+static int if_sdio_card_to_host(struct if_sdio_card *card)
+{
+ int ret;
+ u16 size, type, chunk;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ size = if_sdio_read_rx_len(card, &ret);
+ if (ret)
+ goto out;
+
+ if (size < 4) {
+ lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n",
+ (int)size);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY);
+ if (ret)
+ goto out;
+
+ /*
+ * The transfer must be in one transaction or the firmware
+ * goes suicidal. There's no way to guarantee that for all
+ * controllers, but we can at least try.
+ */
+ chunk = sdio_align_size(card->func, size);
+
+ ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
+ if (ret)
+ goto out;
+
+ chunk = card->buffer[0] | (card->buffer[1] << 8);
+ type = card->buffer[2] | (card->buffer[3] << 8);
+
+ lbs_deb_sdio("packet of type %d and size %d bytes\n",
+ (int)type, (int)chunk);
+
+ if (chunk > size) {
+ lbs_deb_sdio("packet fragment (%d > %d)\n",
+ (int)chunk, (int)size);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (chunk < size) {
+ lbs_deb_sdio("packet fragment (%d < %d)\n",
+ (int)chunk, (int)size);
+ }
+
+ switch (type) {
+ case MVMS_CMD:
+ ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);
+ if (ret)
+ goto out;
+ break;
+ case MVMS_DAT:
+ ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);
+ if (ret)
+ goto out;
+ break;
+ case MVMS_EVENT:
+ ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4);
+ if (ret)
+ goto out;
+ break;
+ default:
+ lbs_deb_sdio("invalid type (%d) from firmware\n",
+ (int)type);
+ ret = -EINVAL;
+ goto out;
+ }
+
+out:
+ if (ret)
+ pr_err("problem fetching packet from firmware\n");
+
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+ return ret;
+}
+
+static void if_sdio_host_to_card_worker(struct work_struct *work)
+{
+ struct if_sdio_card *card;
+ struct if_sdio_packet *packet;
+ int ret;
+ unsigned long flags;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ card = container_of(work, struct if_sdio_card, packet_worker);
+
+ while (1) {
+ spin_lock_irqsave(&card->lock, flags);
+ packet = card->packets;
+ if (packet)
+ card->packets = packet->next;
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (!packet)
+ break;
+
+ sdio_claim_host(card->func);
+
+ ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY);
+ if (ret == 0) {
+ ret = sdio_writesb(card->func, card->ioport,
+ packet->buffer, packet->nb);
+ }
+
+ if (ret)
+ pr_err("error %d sending packet to firmware\n", ret);
+
+ sdio_release_host(card->func);
+
+ kfree(packet);
+ }
+
+ lbs_deb_leave(LBS_DEB_SDIO);
+}
+
+/********************************************************************/
+/* Firmware */
+/********************************************************************/
+
+#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY)
+
+static int if_sdio_prog_helper(struct if_sdio_card *card,
+ const struct firmware *fw)
+{
+ int ret;
+ unsigned long timeout;
+ u8 *chunk_buffer;
+ u32 chunk_size;
+ const u8 *firmware;
+ size_t size;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ chunk_buffer = kzalloc(64, GFP_KERNEL);
+ if (!chunk_buffer) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ sdio_claim_host(card->func);
+
+ ret = sdio_set_block_size(card->func, 32);
+ if (ret)
+ goto release;
+
+ firmware = fw->data;
+ size = fw->size;
+
+ while (size) {
+ ret = if_sdio_wait_status(card, FW_DL_READY_STATUS);
+ if (ret)
+ goto release;
+
+ /* On some platforms (like Davinci) the chip needs more time
+ * between helper blocks.
+ */
+ mdelay(2);
+
+ chunk_size = min_t(size_t, size, 60);
+
+ *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size);
+ memcpy(chunk_buffer + 4, firmware, chunk_size);
+/*
+ lbs_deb_sdio("sending %d bytes chunk\n", chunk_size);
+*/
+ ret = sdio_writesb(card->func, card->ioport,
+ chunk_buffer, 64);
+ if (ret)
+ goto release;
+
+ firmware += chunk_size;
+ size -= chunk_size;
+ }
+
+ /* an empty block marks the end of the transfer */
+ memset(chunk_buffer, 0, 4);
+ ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64);
+ if (ret)
+ goto release;
+
+ lbs_deb_sdio("waiting for helper to boot...\n");
+
+ /* wait for the helper to boot by looking at the size register */
+ timeout = jiffies + HZ;
+ while (1) {
+ u16 req_size;
+
+ req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret);
+ if (ret)
+ goto release;
+
+ req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8;
+ if (ret)
+ goto release;
+
+ if (req_size != 0)
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ ret = -ETIMEDOUT;
+ goto release;
+ }
+
+ msleep(10);
+ }
+
+ ret = 0;
+
+release:
+ sdio_release_host(card->func);
+ kfree(chunk_buffer);
+
+out:
+ if (ret)
+ pr_err("failed to load helper firmware\n");
+
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+static int if_sdio_prog_real(struct if_sdio_card *card,
+ const struct firmware *fw)
+{
+ int ret;
+ unsigned long timeout;
+ u8 *chunk_buffer;
+ u32 chunk_size;
+ const u8 *firmware;
+ size_t size, req_size;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ chunk_buffer = kzalloc(512, GFP_KERNEL);
+ if (!chunk_buffer) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ sdio_claim_host(card->func);
+
+ ret = sdio_set_block_size(card->func, 32);
+ if (ret)
+ goto release;
+
+ firmware = fw->data;
+ size = fw->size;
+
+ while (size) {
+ timeout = jiffies + HZ;
+ while (1) {
+ ret = if_sdio_wait_status(card, FW_DL_READY_STATUS);
+ if (ret)
+ goto release;
+
+ req_size = sdio_readb(card->func, IF_SDIO_RD_BASE,
+ &ret);
+ if (ret)
+ goto release;
+
+ req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1,
+ &ret) << 8;
+ if (ret)
+ goto release;
+
+ /*
+ * For SD8688 wait until the length is not 0, 1 or 2
+ * before downloading the first FW block,
+ * since BOOT code writes the register to indicate the
+ * helper/FW download winner,
+ * the value could be 1 or 2 (Func1 or Func2).
+ */
+ if ((size != fw->size) || (req_size > 2))
+ break;
+ if (time_after(jiffies, timeout)) {
+ ret = -ETIMEDOUT;
+ goto release;
+ }
+ mdelay(1);
+ }
+
+/*
+ lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size);
+*/
+ if (req_size == 0) {
+ lbs_deb_sdio("firmware helper gave up early\n");
+ ret = -EIO;
+ goto release;
+ }
+
+ if (req_size & 0x01) {
+ lbs_deb_sdio("firmware helper signalled error\n");
+ ret = -EIO;
+ goto release;
+ }
+
+ if (req_size > size)
+ req_size = size;
+
+ while (req_size) {
+ chunk_size = min_t(size_t, req_size, 512);
+
+ memcpy(chunk_buffer, firmware, chunk_size);
+/*
+ lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n",
+ chunk_size, (chunk_size + 31) / 32 * 32);
+*/
+ ret = sdio_writesb(card->func, card->ioport,
+ chunk_buffer, roundup(chunk_size, 32));
+ if (ret)
+ goto release;
+
+ firmware += chunk_size;
+ size -= chunk_size;
+ req_size -= chunk_size;
+ }
+ }
+
+ ret = 0;
+
+ lbs_deb_sdio("waiting for firmware to boot...\n");
+
+ /* wait for the firmware to boot */
+ timeout = jiffies + HZ;
+ while (1) {
+ u16 scratch;
+
+ scratch = if_sdio_read_scratch(card, &ret);
+ if (ret)
+ goto release;
+
+ if (scratch == IF_SDIO_FIRMWARE_OK)
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ ret = -ETIMEDOUT;
+ goto release;
+ }
+
+ msleep(10);
+ }
+
+ ret = 0;
+
+release:
+ sdio_release_host(card->func);
+ kfree(chunk_buffer);
+
+out:
+ if (ret)
+ pr_err("failed to load firmware\n");
+
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret,
+ const struct firmware *helper,
+ const struct firmware *mainfw)
+{
+ struct if_sdio_card *card = priv->card;
+
+ if (ret) {
+ pr_err("failed to find firmware (%d)\n", ret);
+ return;
+ }
+
+ ret = if_sdio_prog_helper(card, helper);
+ if (ret)
+ return;
+
+ lbs_deb_sdio("Helper firmware loaded\n");
+
+ ret = if_sdio_prog_real(card, mainfw);
+ if (ret)
+ return;
+
+ lbs_deb_sdio("Firmware loaded\n");
+ if_sdio_finish_power_on(card);
+}
+
+static int if_sdio_prog_firmware(struct if_sdio_card *card)
+{
+ int ret;
+ u16 scratch;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ /*
+ * Disable interrupts
+ */
+ sdio_claim_host(card->func);
+ sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret);
+ sdio_release_host(card->func);
+
+ sdio_claim_host(card->func);
+ scratch = if_sdio_read_scratch(card, &ret);
+ sdio_release_host(card->func);
+
+ lbs_deb_sdio("firmware status = %#x\n", scratch);
+ lbs_deb_sdio("scratch ret = %d\n", ret);
+
+ if (ret)
+ goto out;
+
+
+ /*
+ * The manual clearly describes that FEDC is the right code to use
+ * to detect firmware presence, but for SD8686 it is not that simple.
+ * Scratch is also used to store the RX packet length, so we lose
+ * the FEDC value early on. So we use a non-zero check in order
+ * to validate firmware presence.
+ * Additionally, the SD8686 in the Gumstix always has the high scratch
+ * bit set, even when the firmware is not loaded. So we have to
+ * exclude that from the test.
+ */
+ if (scratch == IF_SDIO_FIRMWARE_OK) {
+ lbs_deb_sdio("firmware already loaded\n");
+ if_sdio_finish_power_on(card);
+ return 0;
+ } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) {
+ lbs_deb_sdio("firmware may be running\n");
+ if_sdio_finish_power_on(card);
+ return 0;
+ }
+
+ ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model,
+ fw_table, if_sdio_do_prog_firmware);
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+/********************************************************************/
+/* Power management */
+/********************************************************************/
+
+/* Finish power on sequence (after firmware is loaded) */
+static void if_sdio_finish_power_on(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct lbs_private *priv = card->priv;
+ int ret;
+
+ sdio_claim_host(func);
+ sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
+
+ /*
+ * Get rx_unit if the chip is SD8688 or newer.
+ * SD8385 & SD8686 do not have rx_unit.
+ */
+ if ((card->model != MODEL_8385)
+ && (card->model != MODEL_8686))
+ card->rx_unit = if_sdio_read_rx_unit(card);
+ else
+ card->rx_unit = 0;
+
+ /*
+ * Set up the interrupt handler late.
+ *
+ * If we set it up earlier, the (buggy) hardware generates a spurious
+ * interrupt, even before the interrupt has been enabled, with
+ * CCCR_INTx = 0.
+ *
+ * We register the interrupt handler late so that we can handle any
+ * spurious interrupts, and also to avoid generation of that known
+ * spurious interrupt in the first place.
+ */
+ ret = sdio_claim_irq(func, if_sdio_interrupt);
+ if (ret)
+ goto release;
+
+ /*
+ * Enable interrupts now that everything is set up
+ */
+ sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
+ if (ret)
+ goto release_irq;
+
+ sdio_release_host(func);
+
+ /* Set fw_ready before queuing any commands so that
+ * lbs_thread won't block from sending them to firmware.
+ */
+ priv->fw_ready = 1;
+
+ /*
+ * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
+ */
+ if (card->model == MODEL_8688) {
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send function INIT command\n");
+ if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
+ lbs_cmd_copyback, (unsigned long) &cmd))
+ netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
+ }
+
+ wake_up(&card->pwron_waitq);
+
+ if (!card->started) {
+ ret = lbs_start_card(priv);
+ if_sdio_power_off(card);
+ if (ret == 0) {
+ card->started = true;
+ /* Tell PM core that we don't need the card to be
+ * powered now */
+ pm_runtime_put(&func->dev);
+ }
+ }
+
+ return;
+
+release_irq:
+ sdio_release_irq(func);
+release:
+ sdio_release_host(func);
+}
+
+static int if_sdio_power_on(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct mmc_host *host = func->card->host;
+ int ret;
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if (ret)
+ goto release;
+
+ /* For 1-bit transfers to the 8686 model, we need to enable the
+ * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
+ * bit to allow access to non-vendor registers. */
+ if ((card->model == MODEL_8686) &&
+ (host->caps & MMC_CAP_SDIO_IRQ) &&
+ (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
+ u8 reg;
+
+ func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+ reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
+ if (ret)
+ goto disable;
+
+ reg |= SDIO_BUS_ECSI;
+ sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
+ if (ret)
+ goto disable;
+ }
+
+ card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
+ if (ret)
+ goto disable;
+
+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
+ if (ret)
+ goto disable;
+
+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
+ if (ret)
+ goto disable;
+
+ sdio_release_host(func);
+ ret = if_sdio_prog_firmware(card);
+ if (ret) {
+ sdio_claim_host(func);
+ goto disable;
+ }
+
+ return 0;
+
+disable:
+ sdio_disable_func(func);
+release:
+ sdio_release_host(func);
+ return ret;
+}
+
+static int if_sdio_power_off(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct lbs_private *priv = card->priv;
+
+ priv->fw_ready = 0;
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ return 0;
+}
+
+
+/*******************************************************************/
+/* Libertas callbacks */
+/*******************************************************************/
+
+static int if_sdio_host_to_card(struct lbs_private *priv,
+ u8 type, u8 *buf, u16 nb)
+{
+ int ret;
+ struct if_sdio_card *card;
+ struct if_sdio_packet *packet, *cur;
+ u16 size;
+ unsigned long flags;
+
+ lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb);
+
+ card = priv->card;
+
+ if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * The transfer must be in one transaction or the firmware
+ * goes suicidal. There's no way to guarantee that for all
+ * controllers, but we can at least try.
+ */
+ size = sdio_align_size(card->func, nb + 4);
+
+ packet = kzalloc(sizeof(struct if_sdio_packet) + size,
+ GFP_ATOMIC);
+ if (!packet) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ packet->next = NULL;
+ packet->nb = size;
+
+ /*
+ * SDIO specific header.
+ */
+ packet->buffer[0] = (nb + 4) & 0xff;
+ packet->buffer[1] = ((nb + 4) >> 8) & 0xff;
+ packet->buffer[2] = type;
+ packet->buffer[3] = 0;
+
+ memcpy(packet->buffer + 4, buf, nb);
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ if (!card->packets)
+ card->packets = packet;
+ else {
+ cur = card->packets;
+ while (cur->next)
+ cur = cur->next;
+ cur->next = packet;
+ }
+
+ switch (type) {
+ case MVMS_CMD:
+ priv->dnld_sent = DNLD_CMD_SENT;
+ break;
+ case MVMS_DAT:
+ priv->dnld_sent = DNLD_DATA_SENT;
+ break;
+ default:
+ lbs_deb_sdio("unknown packet type %d\n", (int)type);
+ }
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ queue_work(card->workqueue, &card->packet_worker);
+
+ ret = 0;
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+ return ret;
+}
+
+static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
+{
+ int ret = -1;
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send DEEP_SLEEP command\n");
+ ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
+ lbs_cmd_copyback, (unsigned long) &cmd);
+ if (ret)
+ netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n");
+
+ mdelay(200);
+ return ret;
+}
+
+static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret = -1;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+ sdio_claim_host(card->func);
+
+ sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+ if (ret)
+ netdev_err(priv->dev, "sdio_writeb failed!\n");
+
+ sdio_release_host(card->func);
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret = -1;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+ sdio_claim_host(card->func);
+
+ sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
+ if (ret)
+ netdev_err(priv->dev, "sdio_writeb failed!\n");
+
+ sdio_release_host(card->func);
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+
+}
+
+static struct mmc_host *reset_host;
+
+static void if_sdio_reset_card_worker(struct work_struct *work)
+{
+ /*
+ * The actual reset operation must be run outside of lbs_thread. This
+ * is because mmc_remove_host() will cause the device to be instantly
+ * destroyed, and the libertas driver then needs to end lbs_thread,
+ * leading to a deadlock.
+ *
+ * We run it in a workqueue totally independent from the if_sdio_card
+ * instance for that reason.
+ */
+
+ pr_info("Resetting card...");
+ mmc_remove_host(reset_host);
+ mmc_add_host(reset_host);
+}
+static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker);
+
+static void if_sdio_reset_card(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+
+ if (work_pending(&card_reset_work))
+ return;
+
+ reset_host = card->func->card->host;
+ schedule_work(&card_reset_work);
+}
+
+static int if_sdio_power_save(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret;
+
+ flush_workqueue(card->workqueue);
+
+ ret = if_sdio_power_off(card);
+
+ /* Let runtime PM know the card is powered off */
+ pm_runtime_put_sync(&card->func->dev);
+
+ return ret;
+}
+
+static int if_sdio_power_restore(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int r;
+
+ /* Make sure the card will not be powered off by runtime PM */
+ pm_runtime_get_sync(&card->func->dev);
+
+ r = if_sdio_power_on(card);
+ if (r)
+ return r;
+
+ wait_event(card->pwron_waitq, priv->fw_ready);
+ return 0;
+}
+
+
+/*******************************************************************/
+/* SDIO callbacks */
+/*******************************************************************/
+
+static void if_sdio_interrupt(struct sdio_func *func)
+{
+ int ret;
+ struct if_sdio_card *card;
+ u8 cause;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ card = sdio_get_drvdata(func);
+
+ cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret);
+ if (ret || !cause)
+ goto out;
+
+ lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause);
+
+ sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret);
+ if (ret)
+ goto out;
+
+ /*
+ * Ignore the define name, this really means the card has
+ * successfully received the command.
+ */
+ card->priv->is_activity_detected = 1;
+ if (cause & IF_SDIO_H_INT_DNLD)
+ lbs_host_to_card_done(card->priv);
+
+
+ if (cause & IF_SDIO_H_INT_UPLD) {
+ ret = if_sdio_card_to_host(card);
+ if (ret)
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+}
+
+static int if_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct if_sdio_card *card;
+ struct lbs_private *priv;
+ int ret, i;
+ unsigned int model;
+ struct if_sdio_packet *packet;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ for (i = 0;i < func->card->num_info;i++) {
+ if (sscanf(func->card->info[i],
+ "802.11 SDIO ID: %x", &model) == 1)
+ break;
+ if (sscanf(func->card->info[i],
+ "ID: %x", &model) == 1)
+ break;
+ if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
+ model = MODEL_8385;
+ break;
+ }
+ }
+
+ if (i == func->card->num_info) {
+ pr_err("unable to identify card model\n");
+ return -ENODEV;
+ }
+
+ card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->func = func;
+ card->model = model;
+
+ switch (card->model) {
+ case MODEL_8385:
+ card->scratch_reg = IF_SDIO_SCRATCH_OLD;
+ break;
+ case MODEL_8686:
+ card->scratch_reg = IF_SDIO_SCRATCH;
+ break;
+ case MODEL_8688:
+ default: /* for newer chipsets */
+ card->scratch_reg = IF_SDIO_FW_STATUS;
+ break;
+ }
+
+ spin_lock_init(&card->lock);
+ card->workqueue = create_workqueue("libertas_sdio");
+ INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
+ init_waitqueue_head(&card->pwron_waitq);
+
+ /* Check if we support this card */
+ for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
+ if (card->model == fw_table[i].model)
+ break;
+ }
+ if (i == ARRAY_SIZE(fw_table)) {
+ pr_err("unknown card model 0x%x\n", card->model);
+ ret = -ENODEV;
+ goto free;
+ }
+
+ sdio_set_drvdata(func, card);
+
+ lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
+ "device = 0x%X, model = 0x%X, ioport = 0x%X\n",
+ func->class, func->vendor, func->device,
+ model, (unsigned)card->ioport);
+
+
+ priv = lbs_add_card(card, &func->dev);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ card->priv = priv;
+
+ priv->card = card;
+ priv->hw_host_to_card = if_sdio_host_to_card;
+ priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
+ priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
+ priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
+ priv->reset_card = if_sdio_reset_card;
+ priv->power_save = if_sdio_power_save;
+ priv->power_restore = if_sdio_power_restore;
+
+ ret = if_sdio_power_on(card);
+ if (ret)
+ goto err_activate_card;
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+ return ret;
+
+err_activate_card:
+ flush_workqueue(card->workqueue);
+ lbs_remove_card(priv);
+free:
+ destroy_workqueue(card->workqueue);
+ while (card->packets) {
+ packet = card->packets;
+ card->packets = card->packets->next;
+ kfree(packet);
+ }
+
+ kfree(card);
+
+ goto out;
+}
+
+static void if_sdio_remove(struct sdio_func *func)
+{
+ struct if_sdio_card *card;
+ struct if_sdio_packet *packet;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ card = sdio_get_drvdata(func);
+
+ /* Undo decrement done above in if_sdio_probe */
+ pm_runtime_get_noresume(&func->dev);
+
+ if (user_rmmod && (card->model == MODEL_8688)) {
+ /*
+ * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
+ * multiple functions
+ */
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send function SHUTDOWN command\n");
+ if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN,
+ &cmd, sizeof(cmd), lbs_cmd_copyback,
+ (unsigned long) &cmd))
+ pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n");
+ }
+
+
+ lbs_deb_sdio("call remove card\n");
+ lbs_stop_card(card->priv);
+ lbs_remove_card(card->priv);
+
+ flush_workqueue(card->workqueue);
+ destroy_workqueue(card->workqueue);
+
+ while (card->packets) {
+ packet = card->packets;
+ card->packets = card->packets->next;
+ kfree(packet);
+ }
+
+ kfree(card);
+ lbs_deb_leave(LBS_DEB_SDIO);
+}
+
+static int if_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ int ret;
+ struct if_sdio_card *card = sdio_get_drvdata(func);
+
+ mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
+
+ /* If we're powered off anyway, just let the mmc layer remove the
+ * card. */
+ if (!lbs_iface_active(card->priv))
+ return -ENOSYS;
+
+ dev_info(dev, "%s: suspend: PM flags = 0x%x\n",
+ sdio_func_id(func), flags);
+
+ /* If we aren't being asked to wake on anything, we should bail out
+ * and let the SD stack power down the card.
+ */
+ if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) {
+ dev_info(dev, "Suspend without wake params -- powering down card\n");
+ return -ENOSYS;
+ }
+
+ if (!(flags & MMC_PM_KEEP_POWER)) {
+ dev_err(dev, "%s: cannot remain alive while host is suspended\n",
+ sdio_func_id(func));
+ return -ENOSYS;
+ }
+
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret)
+ return ret;
+
+ ret = lbs_suspend(card->priv);
+ if (ret)
+ return ret;
+
+ return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+}
+
+static int if_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct if_sdio_card *card = sdio_get_drvdata(func);
+ int ret;
+
+ dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func));
+
+ ret = lbs_resume(card->priv);
+
+ return ret;
+}
+
+static const struct dev_pm_ops if_sdio_pm_ops = {
+ .suspend = if_sdio_suspend,
+ .resume = if_sdio_resume,
+};
+
+static struct sdio_driver if_sdio_driver = {
+ .name = "libertas_sdio",
+ .id_table = if_sdio_ids,
+ .probe = if_sdio_probe,
+ .remove = if_sdio_remove,
+ .drv = {
+ .pm = &if_sdio_pm_ops,
+ },
+};
+
+/*******************************************************************/
+/* Module functions */
+/*******************************************************************/
+
+static int __init if_sdio_init_module(void)
+{
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n");
+ printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n");
+
+ ret = sdio_register_driver(&if_sdio_driver);
+
+ /* Clear the flag in case user removes the card. */
+ user_rmmod = 0;
+
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+ return ret;
+}
+
+static void __exit if_sdio_exit_module(void)
+{
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ /* Set the flag as user is removing this module. */
+ user_rmmod = 1;
+
+ cancel_work_sync(&card_reset_work);
+
+ sdio_unregister_driver(&if_sdio_driver);
+
+ lbs_deb_leave(LBS_DEB_SDIO);
+}
+
+module_init(if_sdio_init_module);
+module_exit(if_sdio_exit_module);
+
+MODULE_DESCRIPTION("Libertas SDIO WLAN Driver");
+MODULE_AUTHOR("Pierre Ossman");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/marvell/libertas/if_sdio.h
index 62fda3592f67..62fda3592f67 100644
--- a/drivers/net/wireless/libertas/if_sdio.h
+++ b/drivers/net/wireless/marvell/libertas/if_sdio.h
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c
index 82c0796377aa..82c0796377aa 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/marvell/libertas/if_spi.c
diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/marvell/libertas/if_spi.h
index e450e31fd11d..e450e31fd11d 100644
--- a/drivers/net/wireless/libertas/if_spi.h
+++ b/drivers/net/wireless/marvell/libertas/if_spi.h
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c
index dff08a2896a3..dff08a2896a3 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas/if_usb.c
diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/marvell/libertas/if_usb.h
index 6e42eac331de..6e42eac331de 100644
--- a/drivers/net/wireless/libertas/if_usb.h
+++ b/drivers/net/wireless/marvell/libertas/if_usb.h
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c
index 8079560f4965..8079560f4965 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/marvell/libertas/main.c
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/marvell/libertas/mesh.c
index d0c881dd5846..d0c881dd5846 100644
--- a/drivers/net/wireless/libertas/mesh.c
+++ b/drivers/net/wireless/marvell/libertas/mesh.c
diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/marvell/libertas/mesh.h
index 6603f341c874..6603f341c874 100644
--- a/drivers/net/wireless/libertas/mesh.h
+++ b/drivers/net/wireless/marvell/libertas/mesh.h
diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/marvell/libertas/radiotap.h
index b3c8ea6d610e..b3c8ea6d610e 100644
--- a/drivers/net/wireless/libertas/radiotap.h
+++ b/drivers/net/wireless/marvell/libertas/radiotap.h
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/marvell/libertas/rx.c
index e446fed7b345..e446fed7b345 100644
--- a/drivers/net/wireless/libertas/rx.c
+++ b/drivers/net/wireless/marvell/libertas/rx.c
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/marvell/libertas/tx.c
index c025f9c18282..c025f9c18282 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/marvell/libertas/tx.c
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/marvell/libertas/types.h
index cf1d9b047ee6..cf1d9b047ee6 100644
--- a/drivers/net/wireless/libertas/types.h
+++ b/drivers/net/wireless/marvell/libertas/types.h
diff --git a/drivers/net/wireless/marvell/libertas_tf/Kconfig b/drivers/net/wireless/marvell/libertas_tf/Kconfig
new file mode 100644
index 000000000000..b5557af90048
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas_tf/Kconfig
@@ -0,0 +1,18 @@
+config LIBERTAS_THINFIRM
+ tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware"
+ depends on MAC80211
+ select FW_LOADER
+ ---help---
+ A library for Marvell Libertas 8xxx devices using thinfirm.
+
+config LIBERTAS_THINFIRM_DEBUG
+ bool "Enable full debugging output in the Libertas thin firmware module."
+ depends on LIBERTAS_THINFIRM
+ ---help---
+ Debugging support.
+
+config LIBERTAS_THINFIRM_USB
+ tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware"
+ depends on LIBERTAS_THINFIRM && USB
+ ---help---
+ A driver for Marvell Libertas 8388 USB devices using thinfirm.
diff --git a/drivers/net/wireless/libertas_tf/Makefile b/drivers/net/wireless/marvell/libertas_tf/Makefile
index ff5544d6ac9d..ff5544d6ac9d 100644
--- a/drivers/net/wireless/libertas_tf/Makefile
+++ b/drivers/net/wireless/marvell/libertas_tf/Makefile
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/marvell/libertas_tf/cmd.c
index 909ac3685010..909ac3685010 100644
--- a/drivers/net/wireless/libertas_tf/cmd.c
+++ b/drivers/net/wireless/marvell/libertas_tf/cmd.c
diff --git a/drivers/net/wireless/libertas_tf/deb_defs.h b/drivers/net/wireless/marvell/libertas_tf/deb_defs.h
index 4bd3dc5adf7c..4bd3dc5adf7c 100644
--- a/drivers/net/wireless/libertas_tf/deb_defs.h
+++ b/drivers/net/wireless/marvell/libertas_tf/deb_defs.h
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
index 799a2efe5793..799a2efe5793 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
diff --git a/drivers/net/wireless/libertas_tf/if_usb.h b/drivers/net/wireless/marvell/libertas_tf/if_usb.h
index 6fa5b3f59efe..6fa5b3f59efe 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.h
+++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.h
diff --git a/drivers/net/wireless/libertas_tf/libertas_tf.h b/drivers/net/wireless/marvell/libertas_tf/libertas_tf.h
index ad77b92d0b41..ad77b92d0b41 100644
--- a/drivers/net/wireless/libertas_tf/libertas_tf.h
+++ b/drivers/net/wireless/marvell/libertas_tf/libertas_tf.h
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c
index a47f0acc099a..a47f0acc099a 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/marvell/libertas_tf/main.c
diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/marvell/mwifiex/11ac.c
index 59d23fb2365f..59d23fb2365f 100644
--- a/drivers/net/wireless/mwifiex/11ac.c
+++ b/drivers/net/wireless/marvell/mwifiex/11ac.c
diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/marvell/mwifiex/11ac.h
index 1ca92c7a8a4a..1ca92c7a8a4a 100644
--- a/drivers/net/wireless/mwifiex/11ac.h
+++ b/drivers/net/wireless/marvell/mwifiex/11ac.h
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c
index 71a1b580796f..71a1b580796f 100644
--- a/drivers/net/wireless/mwifiex/11h.c
+++ b/drivers/net/wireless/marvell/mwifiex/11h.c
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c
index c174e79e6df2..c174e79e6df2 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n.c
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/marvell/mwifiex/11n.h
index afdd58aa90de..afdd58aa90de 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/marvell/mwifiex/11n.h
diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c
new file mode 100644
index 000000000000..1efef3b8273d
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c
@@ -0,0 +1,316 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n Aggregation
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11n_aggr.h"
+
+/*
+ * Creates an AMSDU subframe for aggregation into one AMSDU packet.
+ *
+ * The resultant AMSDU subframe format is -
+ *
+ * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
+ * | DA | SA | Length | SNAP header | MSDU |
+ * | data[0..5] | data[6..11] | | | data[14..] |
+ * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
+ * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes-->
+ *
+ * This function also computes the amount of padding required to make the
+ * buffer length multiple of 4 bytes.
+ *
+ * Data => |DA|SA|SNAP-TYPE|........ .|
+ * MSDU => |DA|SA|Length|SNAP|...... ..|
+ */
+static int
+mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
+ struct sk_buff *skb_src, int *pad)
+
+{
+ int dt_offset;
+ struct rfc_1042_hdr snap = {
+ 0xaa, /* LLC DSAP */
+ 0xaa, /* LLC SSAP */
+ 0x03, /* LLC CTRL */
+ {0x00, 0x00, 0x00}, /* SNAP OUI */
+ 0x0000 /* SNAP type */
+ /*
+ * This field will be overwritten
+ * later with ethertype
+ */
+ };
+ struct tx_packet_hdr *tx_header;
+
+ tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header));
+
+ /* Copy DA and SA */
+ dt_offset = 2 * ETH_ALEN;
+ memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
+
+ /* Copy SNAP header */
+ snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto;
+
+ dt_offset += sizeof(__be16);
+
+ memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
+
+ skb_pull(skb_src, dt_offset);
+
+ /* Update Length field */
+ tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
+
+ /* Add payload */
+ memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len);
+
+ /* Add padding for new MSDU to start from 4 byte boundary */
+ *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4;
+
+ return skb_aggr->len + *pad;
+}
+
+/*
+ * Adds TxPD to AMSDU header.
+ *
+ * Each AMSDU packet will contain one TxPD at the beginning,
+ * followed by multiple AMSDU subframes.
+ */
+static void
+mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct txpd *local_tx_pd;
+ struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
+ unsigned int pad;
+ int headroom = (priv->adapter->iface_type ==
+ MWIFIEX_USB) ? 0 : INTF_HEADER_LEN;
+
+ pad = ((void *)skb->data - sizeof(*local_tx_pd) -
+ headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1);
+ skb_push(skb, pad);
+
+ skb_push(skb, sizeof(*local_tx_pd));
+
+ local_tx_pd = (struct txpd *) skb->data;
+ memset(local_tx_pd, 0, sizeof(struct txpd));
+
+ /* Original priority has been overwritten */
+ local_tx_pd->priority = (u8) skb->priority;
+ local_tx_pd->pkt_delay_2ms =
+ mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
+ local_tx_pd->bss_num = priv->bss_num;
+ local_tx_pd->bss_type = priv->bss_type;
+ /* Always zero as the data is followed by struct txpd */
+ local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) +
+ pad);
+ local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
+ local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
+ sizeof(*local_tx_pd) -
+ pad);
+
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+ local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
+
+ if (local_tx_pd->tx_control == 0)
+ /* TxCtrl set by user or default */
+ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
+ priv->adapter->pps_uapsd_mode) {
+ if (true == mwifiex_check_last_packet_indication(priv)) {
+ priv->adapter->tx_lock_flag = true;
+ local_tx_pd->flags =
+ MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET;
+ }
+ }
+}
+
+/*
+ * Create aggregated packet.
+ *
+ * This function creates an aggregated MSDU packet, by combining buffers
+ * from the RA list. Each individual buffer is encapsulated as an AMSDU
+ * subframe and all such subframes are concatenated together to form the
+ * AMSDU packet.
+ *
+ * A TxPD is also added to the front of the resultant AMSDU packets for
+ * transmission. The resultant packets format is -
+ *
+ * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+
+ * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame|
+ * | | 1 | 2 | .. | n |
+ * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+
+ */
+int
+mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
+ struct mwifiex_ra_list_tbl *pra_list,
+ int ptrindex, unsigned long ra_list_flags)
+ __releases(&priv->wmm.ra_list_spinlock)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct sk_buff *skb_aggr, *skb_src;
+ struct mwifiex_txinfo *tx_info_aggr, *tx_info_src;
+ int pad = 0, aggr_num = 0, ret;
+ struct mwifiex_tx_param tx_param;
+ struct txpd *ptx_pd = NULL;
+ int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
+
+ skb_src = skb_peek(&pra_list->skb_head);
+ if (!skb_src) {
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ ra_list_flags);
+ return 0;
+ }
+
+ tx_info_src = MWIFIEX_SKB_TXCB(skb_src);
+ skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size,
+ GFP_ATOMIC | GFP_DMA);
+ if (!skb_aggr) {
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ ra_list_flags);
+ return -1;
+ }
+ skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN);
+ tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr);
+
+ memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
+ tx_info_aggr->bss_type = tx_info_src->bss_type;
+ tx_info_aggr->bss_num = tx_info_src->bss_num;
+
+ if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+ tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+ tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT;
+ skb_aggr->priority = skb_src->priority;
+ skb_aggr->tstamp = skb_src->tstamp;
+
+ do {
+ /* Check if AMSDU can accommodate this MSDU */
+ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN))
+ break;
+
+ skb_src = skb_dequeue(&pra_list->skb_head);
+ pra_list->total_pkt_count--;
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ aggr_num++;
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ ra_list_flags);
+ mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
+
+ mwifiex_write_data_complete(adapter, skb_src, 0, 0);
+
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
+
+ if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ ra_list_flags);
+ return -1;
+ }
+
+ if (skb_tailroom(skb_aggr) < pad) {
+ pad = 0;
+ break;
+ }
+ skb_put(skb_aggr, pad);
+
+ skb_src = skb_peek(&pra_list->skb_head);
+
+ } while (skb_src);
+
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags);
+
+ /* Last AMSDU packet does not need padding */
+ skb_trim(skb_aggr, skb_aggr->len - pad);
+
+ /* Form AMSDU */
+ mwifiex_11n_form_amsdu_txpd(priv, skb_aggr);
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
+ ptx_pd = (struct txpd *)skb_aggr->data;
+
+ skb_push(skb_aggr, headroom);
+ tx_info_aggr->aggr_num = aggr_num * 2;
+ if (adapter->data_sent || adapter->tx_lock_flag) {
+ atomic_add(aggr_num * 2, &adapter->tx_queued);
+ skb_queue_tail(&adapter->tx_data_q, skb_aggr);
+ return 0;
+ }
+
+ if (adapter->iface_type == MWIFIEX_USB) {
+ ret = adapter->if_ops.host_to_card(adapter, priv->usb_port,
+ skb_aggr, NULL);
+ } else {
+ if (skb_src)
+ tx_param.next_pkt_len =
+ skb_src->len + sizeof(struct txpd);
+ else
+ tx_param.next_pkt_len = 0;
+
+ ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
+ skb_aggr, &tx_param);
+ }
+ switch (ret) {
+ case -EBUSY:
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
+ if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ ra_list_flags);
+ mwifiex_write_data_complete(adapter, skb_aggr, 1, -1);
+ return -1;
+ }
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
+ adapter->pps_uapsd_mode && adapter->tx_lock_flag) {
+ priv->adapter->tx_lock_flag = false;
+ if (ptx_pd)
+ ptx_pd->flags = 0;
+ }
+
+ skb_queue_tail(&pra_list->skb_head, skb_aggr);
+
+ pra_list->total_pkt_count++;
+
+ atomic_inc(&priv->wmm.tx_pkts_queued);
+
+ tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ ra_list_flags);
+ mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
+ break;
+ case -1:
+ mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n",
+ __func__, ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
+ return 0;
+ case -EINPROGRESS:
+ break;
+ case 0:
+ mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
+ break;
+ default:
+ break;
+ }
+ if (ret != -EBUSY) {
+ mwifiex_rotate_priolists(priv, pra_list, ptrindex);
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h
index 0cd2a3eb6c17..0cd2a3eb6c17 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.h
+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h
diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
new file mode 100644
index 000000000000..09578c6cde59
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
@@ -0,0 +1,920 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n RX Re-ordering
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11n_rxreorder.h"
+
+/* This function will dispatch amsdu packet and forward it to kernel/upper
+ * layer.
+ */
+static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
+ int ret;
+
+ if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
+ struct sk_buff_head list;
+ struct sk_buff *rx_skb;
+
+ __skb_queue_head_init(&list);
+
+ skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
+ skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
+
+ ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
+ priv->wdev.iftype, 0, false);
+
+ while (!skb_queue_empty(&list)) {
+ struct rx_packet_hdr *rx_hdr;
+
+ rx_skb = __skb_dequeue(&list);
+ rx_hdr = (struct rx_packet_hdr *)rx_skb->data;
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
+ mwifiex_process_tdls_action_frame(priv,
+ (u8 *)rx_hdr,
+ skb->len);
+ }
+
+ ret = mwifiex_recv_packet(priv, rx_skb);
+ if (ret == -1)
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Rx of A-MSDU failed");
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+/* This function will process the rx packet and forward it to kernel/upper
+ * layer.
+ */
+static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload)
+{
+ int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload);
+
+ if (!ret)
+ return 0;
+
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ return mwifiex_handle_uap_rx_forward(priv, payload);
+
+ return mwifiex_process_rx_packet(priv, payload);
+}
+
+/*
+ * This function dispatches all packets in the Rx reorder table until the
+ * start window.
+ *
+ * There could be holes in the buffer, which are skipped by the function.
+ * Since the buffer is linear, the function uses rotation to simulate
+ * circular buffer.
+ */
+static void
+mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
+ struct mwifiex_rx_reorder_tbl *tbl,
+ int start_win)
+{
+ int pkt_to_send, i;
+ void *rx_tmp_ptr;
+ unsigned long flags;
+
+ pkt_to_send = (start_win > tbl->start_win) ?
+ min((start_win - tbl->start_win), tbl->win_size) :
+ tbl->win_size;
+
+ for (i = 0; i < pkt_to_send; ++i) {
+ spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+ rx_tmp_ptr = NULL;
+ if (tbl->rx_reorder_ptr[i]) {
+ rx_tmp_ptr = tbl->rx_reorder_ptr[i];
+ tbl->rx_reorder_ptr[i] = NULL;
+ }
+ spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+ if (rx_tmp_ptr)
+ mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
+ }
+
+ spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+ /*
+ * We don't have a circular buffer, hence use rotation to simulate
+ * circular buffer
+ */
+ for (i = 0; i < tbl->win_size - pkt_to_send; ++i) {
+ tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i];
+ tbl->rx_reorder_ptr[pkt_to_send + i] = NULL;
+ }
+
+ tbl->start_win = start_win;
+ spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+}
+
+/*
+ * This function dispatches all packets in the Rx reorder table until
+ * a hole is found.
+ *
+ * The start window is adjusted automatically when a hole is located.
+ * Since the buffer is linear, the function uses rotation to simulate
+ * circular buffer.
+ */
+static void
+mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
+ struct mwifiex_rx_reorder_tbl *tbl)
+{
+ int i, j, xchg;
+ void *rx_tmp_ptr;
+ unsigned long flags;
+
+ for (i = 0; i < tbl->win_size; ++i) {
+ spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+ if (!tbl->rx_reorder_ptr[i]) {
+ spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+ break;
+ }
+ rx_tmp_ptr = tbl->rx_reorder_ptr[i];
+ tbl->rx_reorder_ptr[i] = NULL;
+ spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+ mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
+ }
+
+ spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+ /*
+ * We don't have a circular buffer, hence use rotation to simulate
+ * circular buffer
+ */
+ if (i > 0) {
+ xchg = tbl->win_size - i;
+ for (j = 0; j < xchg; ++j) {
+ tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j];
+ tbl->rx_reorder_ptr[i + j] = NULL;
+ }
+ }
+ tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
+ spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+}
+
+/*
+ * This function deletes the Rx reorder table and frees the memory.
+ *
+ * The function stops the associated timer and dispatches all the
+ * pending packets in the Rx reorder table before deletion.
+ */
+static void
+mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
+ struct mwifiex_rx_reorder_tbl *tbl)
+{
+ unsigned long flags;
+ int start_win;
+
+ if (!tbl)
+ return;
+
+ spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
+ priv->adapter->rx_locked = true;
+ if (priv->adapter->rx_processing) {
+ spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
+ flush_workqueue(priv->adapter->rx_workqueue);
+ } else {
+ spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
+ }
+
+ start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
+ mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
+
+ del_timer_sync(&tbl->timer_context.timer);
+ tbl->timer_context.timer_is_set = false;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ list_del(&tbl->list);
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+ kfree(tbl->rx_reorder_ptr);
+ kfree(tbl);
+
+ spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
+ priv->adapter->rx_locked = false;
+ spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
+
+}
+
+/*
+ * This function returns the pointer to an entry in Rx reordering
+ * table which matches the given TA/TID pair.
+ */
+struct mwifiex_rx_reorder_tbl *
+mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
+{
+ struct mwifiex_rx_reorder_tbl *tbl;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) {
+ if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+ flags);
+ return tbl;
+ }
+ }
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+ return NULL;
+}
+
+/* This function retrieves the pointer to an entry in Rx reordering
+ * table which matches the given TA and deletes it.
+ */
+void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
+{
+ struct mwifiex_rx_reorder_tbl *tbl, *tmp;
+ unsigned long flags;
+
+ if (!ta)
+ return;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) {
+ if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+ flags);
+ mwifiex_del_rx_reorder_entry(priv, tbl);
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+ return;
+}
+
+/*
+ * This function finds the last sequence number used in the packets
+ * buffered in Rx reordering table.
+ */
+static int
+mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
+{
+ struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
+ struct mwifiex_private *priv = ctx->priv;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
+ if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+ flags);
+ return i;
+ }
+ }
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+ return -1;
+}
+
+/*
+ * This function flushes all the packets in Rx reordering table.
+ *
+ * The function checks if any packets are currently buffered in the
+ * table or not. In case there are packets available, it dispatches
+ * them and then dumps the Rx reordering table.
+ */
+static void
+mwifiex_flush_data(unsigned long context)
+{
+ struct reorder_tmr_cnxt *ctx =
+ (struct reorder_tmr_cnxt *) context;
+ int start_win, seq_num;
+
+ ctx->timer_is_set = false;
+ seq_num = mwifiex_11n_find_last_seq_num(ctx);
+
+ if (seq_num < 0)
+ return;
+
+ mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num);
+ start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
+ mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
+ start_win);
+}
+
+/*
+ * This function creates an entry in Rx reordering table for the
+ * given TA/TID.
+ *
+ * The function also initializes the entry with sequence number, window
+ * size as well as initializes the timer.
+ *
+ * If the received TA/TID pair is already present, all the packets are
+ * dispatched and the window size is moved until the SSN.
+ */
+static void
+mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
+ int tid, int win_size, int seq_num)
+{
+ int i;
+ struct mwifiex_rx_reorder_tbl *tbl, *new_node;
+ u16 last_seq = 0;
+ unsigned long flags;
+ struct mwifiex_sta_node *node;
+
+ /*
+ * If we get a TID, ta pair which is already present dispatch all the
+ * the packets and move the window size until the ssn
+ */
+ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
+ if (tbl) {
+ mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
+ return;
+ }
+ /* if !tbl then create one */
+ new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL);
+ if (!new_node)
+ return;
+
+ INIT_LIST_HEAD(&new_node->list);
+ new_node->tid = tid;
+ memcpy(new_node->ta, ta, ETH_ALEN);
+ new_node->start_win = seq_num;
+ new_node->init_win = seq_num;
+ new_node->flags = 0;
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ if (mwifiex_queuing_ra_based(priv)) {
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) {
+ node = mwifiex_get_sta_entry(priv, ta);
+ if (node)
+ last_seq = node->rx_seq[tid];
+ }
+ } else {
+ node = mwifiex_get_sta_entry(priv, ta);
+ if (node)
+ last_seq = node->rx_seq[tid];
+ else
+ last_seq = priv->rx_seq[tid];
+ }
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: last_seq=%d start_win=%d\n",
+ last_seq, new_node->start_win);
+
+ if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
+ last_seq >= new_node->start_win) {
+ new_node->start_win = last_seq + 1;
+ new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
+ }
+
+ new_node->win_size = win_size;
+
+ new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size,
+ GFP_KERNEL);
+ if (!new_node->rx_reorder_ptr) {
+ kfree((u8 *) new_node);
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: failed to alloc reorder_ptr\n", __func__);
+ return;
+ }
+
+ new_node->timer_context.ptr = new_node;
+ new_node->timer_context.priv = priv;
+ new_node->timer_context.timer_is_set = false;
+
+ setup_timer(&new_node->timer_context.timer, mwifiex_flush_data,
+ (unsigned long)&new_node->timer_context);
+
+ for (i = 0; i < win_size; ++i)
+ new_node->rx_reorder_ptr[i] = NULL;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr);
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+}
+
+static void
+mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl)
+{
+ u32 min_flush_time;
+
+ if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32)
+ min_flush_time = MIN_FLUSH_TIMER_15_MS;
+ else
+ min_flush_time = MIN_FLUSH_TIMER_MS;
+
+ mod_timer(&tbl->timer_context.timer,
+ jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
+
+ tbl->timer_context.timer_is_set = true;
+}
+
+/*
+ * This function prepares command for adding a BA request.
+ *
+ * Preparation includes -
+ * - Setting command ID and proper size
+ * - Setting add BA request buffer
+ * - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
+{
+ struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ);
+ cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN);
+ memcpy(add_ba_req, data_buf, sizeof(*add_ba_req));
+
+ return 0;
+}
+
+/*
+ * This function prepares command for adding a BA response.
+ *
+ * Preparation includes -
+ * - Setting command ID and proper size
+ * - Setting add BA response buffer
+ * - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct host_cmd_ds_11n_addba_req
+ *cmd_addba_req)
+{
+ struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
+ struct mwifiex_sta_node *sta_ptr;
+ u32 rx_win_size = priv->add_ba_param.rx_win_size;
+ u8 tid;
+ int win_size;
+ unsigned long flags;
+ uint16_t block_ack_param_set;
+
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->is_hw_11ac_capable &&
+ memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_ptr = mwifiex_get_sta_entry(priv,
+ cmd_addba_req->peer_mac_addr);
+ if (!sta_ptr) {
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ mwifiex_dbg(priv->adapter, ERROR,
+ "BA setup with unknown TDLS peer %pM!\n",
+ cmd_addba_req->peer_mac_addr);
+ return -1;
+ }
+ if (sta_ptr->is_11ac_enabled)
+ rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ }
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
+ cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
+
+ memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr,
+ ETH_ALEN);
+ add_ba_rsp->dialog_token = cmd_addba_req->dialog_token;
+ add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo;
+ add_ba_rsp->ssn = cmd_addba_req->ssn;
+
+ block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set);
+ tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
+ >> BLOCKACKPARAM_TID_POS;
+ add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT);
+ block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
+
+ /* If we don't support AMSDU inside AMPDU, reset the bit */
+ if (!priv->add_ba_param.rx_amsdu ||
+ (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED))
+ block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
+ block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
+ add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
+ win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
+ & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
+ >> BLOCKACKPARAM_WINSIZE_POS;
+ cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set);
+
+ mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr,
+ tid, win_size,
+ le16_to_cpu(cmd_addba_req->ssn));
+ return 0;
+}
+
+/*
+ * This function prepares command for deleting a BA request.
+ *
+ * Preparation includes -
+ * - Setting command ID and proper size
+ * - Setting del BA request buffer
+ * - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf)
+{
+ struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA);
+ cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN);
+ memcpy(del_ba, data_buf, sizeof(*del_ba));
+
+ return 0;
+}
+
+/*
+ * This function identifies if Rx reordering is needed for a received packet.
+ *
+ * In case reordering is required, the function will do the reordering
+ * before sending it to kernel.
+ *
+ * The Rx reorder table is checked first with the received TID/TA pair. If
+ * not found, the received packet is dispatched immediately. But if found,
+ * the packet is reordered and all the packets in the updated Rx reordering
+ * table is dispatched until a hole is found.
+ *
+ * For sequence number less than the starting window, the packet is dropped.
+ */
+int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
+ u16 seq_num, u16 tid,
+ u8 *ta, u8 pkt_type, void *payload)
+{
+ struct mwifiex_rx_reorder_tbl *tbl;
+ int prev_start_win, start_win, end_win, win_size;
+ u16 pkt_index;
+ bool init_window_shift = false;
+ int ret = 0;
+
+ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
+ if (!tbl) {
+ if (pkt_type != PKT_TYPE_BAR)
+ mwifiex_11n_dispatch_pkt(priv, payload);
+ return ret;
+ }
+
+ if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
+ mwifiex_11n_dispatch_pkt(priv, payload);
+ return ret;
+ }
+
+ start_win = tbl->start_win;
+ prev_start_win = start_win;
+ win_size = tbl->win_size;
+ end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
+ if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
+ init_window_shift = true;
+ tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
+ }
+
+ if (tbl->flags & RXREOR_FORCE_NO_DROP) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "RXREOR_FORCE_NO_DROP when HS is activated\n");
+ tbl->flags &= ~RXREOR_FORCE_NO_DROP;
+ } else if (init_window_shift && seq_num < start_win &&
+ seq_num >= tbl->init_win) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "Sender TID sequence number reset %d->%d for SSN %d\n",
+ start_win, seq_num, tbl->init_win);
+ tbl->start_win = start_win = seq_num;
+ end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
+ } else {
+ /*
+ * If seq_num is less then starting win then ignore and drop
+ * the packet
+ */
+ if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
+ if (seq_num >= ((start_win + TWOPOW11) &
+ (MAX_TID_VALUE - 1)) &&
+ seq_num < start_win) {
+ ret = -1;
+ goto done;
+ }
+ } else if ((seq_num < start_win) ||
+ (seq_num >= (start_win + TWOPOW11))) {
+ ret = -1;
+ goto done;
+ }
+ }
+
+ /*
+ * If this packet is a BAR we adjust seq_num as
+ * WinStart = seq_num
+ */
+ if (pkt_type == PKT_TYPE_BAR)
+ seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1);
+
+ if (((end_win < start_win) &&
+ (seq_num < start_win) && (seq_num > end_win)) ||
+ ((end_win > start_win) && ((seq_num > end_win) ||
+ (seq_num < start_win)))) {
+ end_win = seq_num;
+ if (((end_win - win_size) + 1) >= 0)
+ start_win = (end_win - win_size) + 1;
+ else
+ start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1;
+ mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
+ }
+
+ if (pkt_type != PKT_TYPE_BAR) {
+ if (seq_num >= start_win)
+ pkt_index = seq_num - start_win;
+ else
+ pkt_index = (seq_num+MAX_TID_VALUE) - start_win;
+
+ if (tbl->rx_reorder_ptr[pkt_index]) {
+ ret = -1;
+ goto done;
+ }
+
+ tbl->rx_reorder_ptr[pkt_index] = payload;
+ }
+
+ /*
+ * Dispatch all packets sequentially from start_win until a
+ * hole is found and adjust the start_win appropriately
+ */
+ mwifiex_11n_scan_and_dispatch(priv, tbl);
+
+done:
+ if (!tbl->timer_context.timer_is_set ||
+ prev_start_win != tbl->start_win)
+ mwifiex_11n_rxreorder_timer_restart(tbl);
+ return ret;
+}
+
+/*
+ * This function deletes an entry for a given TID/TA pair.
+ *
+ * The TID/TA are taken from del BA event body.
+ */
+void
+mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac,
+ u8 type, int initiator)
+{
+ struct mwifiex_rx_reorder_tbl *tbl;
+ struct mwifiex_tx_ba_stream_tbl *ptx_tbl;
+ struct mwifiex_ra_list_tbl *ra_list;
+ u8 cleanup_rx_reorder_tbl;
+ unsigned long flags;
+ int tid_down;
+
+ if (type == TYPE_DELBA_RECEIVE)
+ cleanup_rx_reorder_tbl = (initiator) ? true : false;
+ else
+ cleanup_rx_reorder_tbl = (initiator) ? false : true;
+
+ mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n",
+ peer_mac, tid, initiator);
+
+ if (cleanup_rx_reorder_tbl) {
+ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
+ peer_mac);
+ if (!tbl) {
+ mwifiex_dbg(priv->adapter, EVENT,
+ "event: TID, TA not found in table\n");
+ return;
+ }
+ mwifiex_del_rx_reorder_entry(priv, tbl);
+ } else {
+ ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac);
+ if (!ptx_tbl) {
+ mwifiex_dbg(priv->adapter, EVENT,
+ "event: TID, RA not found in table\n");
+ return;
+ }
+
+ tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
+ ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac);
+ if (ra_list) {
+ ra_list->amsdu_in_ampdu = false;
+ ra_list->ba_status = BA_SETUP_NONE;
+ }
+ spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+ mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
+ spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+ }
+}
+
+/*
+ * This function handles the command response of an add BA response.
+ *
+ * Handling includes changing the header fields into CPU format and
+ * creating the stream, provided the add BA is accepted.
+ */
+int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
+ int tid, win_size;
+ struct mwifiex_rx_reorder_tbl *tbl;
+ uint16_t block_ack_param_set;
+
+ block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
+
+ tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
+ >> BLOCKACKPARAM_TID_POS;
+ /*
+ * Check if we had rejected the ADDBA, if yes then do not create
+ * the stream
+ */
+ if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
+ mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n",
+ add_ba_rsp->peer_mac_addr, tid);
+
+ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
+ add_ba_rsp->peer_mac_addr);
+ if (tbl)
+ mwifiex_del_rx_reorder_entry(priv, tbl);
+
+ return 0;
+ }
+
+ win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
+ >> BLOCKACKPARAM_WINSIZE_POS;
+
+ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
+ add_ba_rsp->peer_mac_addr);
+ if (tbl) {
+ if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
+ priv->add_ba_param.rx_amsdu &&
+ (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
+ tbl->amsdu = true;
+ else
+ tbl->amsdu = false;
+ }
+
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
+ add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size);
+
+ return 0;
+}
+
+/*
+ * This function handles BA stream timeout event by preparing and sending
+ * a command to the firmware.
+ */
+void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv,
+ struct host_cmd_ds_11n_batimeout *event)
+{
+ struct host_cmd_ds_11n_delba delba;
+
+ memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba));
+ memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN);
+
+ delba.del_ba_param_set |=
+ cpu_to_le16((u16) event->tid << DELBA_TID_POS);
+ delba.del_ba_param_set |= cpu_to_le16(
+ (u16) event->origninator << DELBA_INITIATOR_POS);
+ delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
+ mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false);
+}
+
+/*
+ * This function cleans up the Rx reorder table by deleting all the entries
+ * and re-initializing.
+ */
+void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv)
+{
+ struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ list_for_each_entry_safe(del_tbl_ptr, tmp_node,
+ &priv->rx_reorder_tbl_ptr, list) {
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+ mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr);
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ }
+ INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+ mwifiex_reset_11n_rx_seq_num(priv);
+}
+
+/*
+ * This function updates all rx_reorder_tbl's flags.
+ */
+void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
+{
+ struct mwifiex_private *priv;
+ struct mwifiex_rx_reorder_tbl *tbl;
+ unsigned long lock_flags;
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags);
+ if (list_empty(&priv->rx_reorder_tbl_ptr)) {
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+ lock_flags);
+ continue;
+ }
+
+ list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
+ tbl->flags = flags;
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags);
+ }
+
+ return;
+}
+
+/* This function update all the rx_win_size based on coex flag
+ */
+static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter,
+ bool coex_flag)
+{
+ u8 i;
+ u32 rx_win_size;
+ struct mwifiex_private *priv;
+
+ dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i])
+ continue;
+ priv = adapter->priv[i];
+ rx_win_size = priv->add_ba_param.rx_win_size;
+ if (coex_flag) {
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
+ priv->add_ba_param.rx_win_size =
+ MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE;
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
+ priv->add_ba_param.rx_win_size =
+ MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE;
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)
+ priv->add_ba_param.rx_win_size =
+ MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE;
+ } else {
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
+ priv->add_ba_param.rx_win_size =
+ MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
+ priv->add_ba_param.rx_win_size =
+ MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)
+ priv->add_ba_param.rx_win_size =
+ MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;
+ }
+
+ if (adapter->coex_win_size && adapter->coex_rx_win_size)
+ priv->add_ba_param.rx_win_size =
+ adapter->coex_rx_win_size;
+
+ if (rx_win_size != priv->add_ba_param.rx_win_size) {
+ if (!priv->media_connected)
+ continue;
+ for (i = 0; i < MAX_NUM_TID; i++)
+ mwifiex_11n_delba(priv, i);
+ }
+ }
+}
+
+/* This function check coex for RX BA
+ */
+void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter)
+{
+ u8 i;
+ struct mwifiex_private *priv;
+ u8 count = 0;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
+ if (priv->media_connected)
+ count++;
+ }
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+ if (priv->bss_started)
+ count++;
+ }
+ }
+ if (count >= MWIFIEX_BSS_COEX_COUNT)
+ break;
+ }
+ if (count >= MWIFIEX_BSS_COEX_COUNT)
+ mwifiex_update_ampdu_rxwinsize(adapter, true);
+ else
+ mwifiex_update_ampdu_rxwinsize(adapter, false);
+}
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h
index 63ecea89b4ab..63ecea89b4ab 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/marvell/mwifiex/Kconfig
index 279167ddd293..279167ddd293 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/marvell/mwifiex/Kconfig
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/marvell/mwifiex/Makefile
index fdfd9bf15ed4..fdfd9bf15ed4 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/marvell/mwifiex/Makefile
diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/marvell/mwifiex/README
index 2f0f9b5609d0..2f0f9b5609d0 100644
--- a/drivers/net/wireless/mwifiex/README
+++ b/drivers/net/wireless/marvell/mwifiex/README
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
new file mode 100644
index 000000000000..e7adef72c05f
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -0,0 +1,3913 @@
+/*
+ * Marvell Wireless LAN device driver: CFG80211
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "cfg80211.h"
+#include "main.h"
+#include "11n.h"
+
+static char *reg_alpha2;
+module_param(reg_alpha2, charp, 0);
+
+static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
+ {
+ .max = 3, .types = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_AP),
+ },
+};
+
+static const struct ieee80211_iface_combination
+mwifiex_iface_comb_ap_sta = {
+ .limits = mwifiex_ap_sta_limits,
+ .num_different_channels = 1,
+ .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
+ .max_interfaces = MWIFIEX_MAX_BSS_NUM,
+ .beacon_int_infra_match = true,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40),
+};
+
+static const struct ieee80211_iface_combination
+mwifiex_iface_comb_ap_sta_vht = {
+ .limits = mwifiex_ap_sta_limits,
+ .num_different_channels = 1,
+ .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
+ .max_interfaces = MWIFIEX_MAX_BSS_NUM,
+ .beacon_int_infra_match = true,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80),
+};
+
+static const struct
+ieee80211_iface_combination mwifiex_iface_comb_ap_sta_drcs = {
+ .limits = mwifiex_ap_sta_limits,
+ .num_different_channels = 2,
+ .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
+ .max_interfaces = MWIFIEX_MAX_BSS_NUM,
+ .beacon_int_infra_match = true,
+};
+
+/*
+ * This function maps the nl802.11 channel type into driver channel type.
+ *
+ * The mapping is as follows -
+ * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE
+ * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE
+ * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE
+ * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW
+ * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE
+ */
+u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type)
+{
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ return IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ case NL80211_CHAN_HT40PLUS:
+ return IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ case NL80211_CHAN_HT40MINUS:
+ return IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ default:
+ return IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ }
+}
+
+/* This function maps IEEE HT secondary channel type to NL80211 channel type
+ */
+u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset)
+{
+ switch (second_chan_offset) {
+ case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+ return NL80211_CHAN_HT20;
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ return NL80211_CHAN_HT40PLUS;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ return NL80211_CHAN_HT40MINUS;
+ default:
+ return NL80211_CHAN_HT20;
+ }
+}
+
+/*
+ * This function checks whether WEP is set.
+ */
+static int
+mwifiex_is_alg_wep(u32 cipher)
+{
+ switch (cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * This function retrieves the private structure from kernel wiphy structure.
+ */
+static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy)
+{
+ return (void *) (*(unsigned long *) wiphy_priv(wiphy));
+}
+
+/*
+ * CFG802.11 operation handler to delete a network key.
+ */
+static int
+mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev);
+ const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
+
+ if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) {
+ mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n");
+ return -EFAULT;
+ }
+
+ mwifiex_dbg(priv->adapter, INFO, "info: crypto keys deleted\n");
+ return 0;
+}
+
+/*
+ * This function forms an skb for management frame.
+ */
+static int
+mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
+{
+ u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ u16 pkt_len;
+ u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
+
+ pkt_len = len + ETH_ALEN;
+
+ skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
+ memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len));
+
+ memcpy(skb_push(skb, sizeof(tx_control)),
+ &tx_control, sizeof(tx_control));
+
+ memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type));
+
+ /* Add packet data and address4 */
+ memcpy(skb_put(skb, sizeof(struct ieee80211_hdr_3addr)), buf,
+ sizeof(struct ieee80211_hdr_3addr));
+ memcpy(skb_put(skb, ETH_ALEN), addr, ETH_ALEN);
+ memcpy(skb_put(skb, len - sizeof(struct ieee80211_hdr_3addr)),
+ buf + sizeof(struct ieee80211_hdr_3addr),
+ len - sizeof(struct ieee80211_hdr_3addr));
+
+ skb->priority = LOW_PRIO_TID;
+ __net_timestamp(skb);
+
+ return 0;
+}
+
+/*
+ * CFG802.11 operation handler to transmit a management frame.
+ */
+static int
+mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ struct sk_buff *skb;
+ u16 pkt_len;
+ const struct ieee80211_mgmt *mgmt;
+ struct mwifiex_txinfo *tx_info;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+
+ if (!buf || !len) {
+ mwifiex_dbg(priv->adapter, ERROR, "invalid buffer and length\n");
+ return -EFAULT;
+ }
+
+ mgmt = (const struct ieee80211_mgmt *)buf;
+ if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA &&
+ ieee80211_is_probe_resp(mgmt->frame_control)) {
+ /* Since we support offload probe resp, we need to skip probe
+ * resp in AP or GO mode */
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: skip to send probe resp in AP or GO mode\n");
+ return 0;
+ }
+
+ pkt_len = len + ETH_ALEN;
+ skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE +
+ pkt_len + sizeof(pkt_len));
+
+ if (!skb) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "allocate skb failed for management frame\n");
+ return -ENOMEM;
+ }
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->pkt_len = pkt_len;
+
+ mwifiex_form_mgmt_frame(skb, buf, len);
+ *cookie = prandom_u32() | 1;
+
+ if (ieee80211_is_action(mgmt->frame_control))
+ skb = mwifiex_clone_skb_for_tx_status(priv,
+ skb,
+ MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie);
+ else
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+ GFP_ATOMIC);
+
+ mwifiex_queue_tx_pkt(priv, skb);
+
+ mwifiex_dbg(priv->adapter, INFO, "info: management frame transmitted\n");
+ return 0;
+}
+
+/*
+ * CFG802.11 operation handler to register a mgmt frame.
+ */
+static void
+mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u16 frame_type, bool reg)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+ u32 mask;
+
+ if (reg)
+ mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
+ else
+ mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
+
+ if (mask != priv->mgmt_frame_mask) {
+ priv->mgmt_frame_mask = mask;
+ mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false);
+ mwifiex_dbg(priv->adapter, INFO, "info: mgmt frame registered\n");
+ }
+}
+
+/*
+ * CFG802.11 operation handler to remain on channel.
+ */
+static int
+mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+ int ret;
+
+ if (!chan || !cookie) {
+ mwifiex_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n");
+ return -EINVAL;
+ }
+
+ if (priv->roc_cfg.cookie) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ongoing ROC, cookie = 0x%llx\n",
+ priv->roc_cfg.cookie);
+ return -EBUSY;
+ }
+
+ ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan,
+ duration);
+
+ if (!ret) {
+ *cookie = prandom_u32() | 1;
+ priv->roc_cfg.cookie = *cookie;
+ priv->roc_cfg.chan = *chan;
+
+ cfg80211_ready_on_channel(wdev, *cookie, chan,
+ duration, GFP_ATOMIC);
+
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ROC, cookie = 0x%llx\n", *cookie);
+ }
+
+ return ret;
+}
+
+/*
+ * CFG802.11 operation handler to cancel remain on channel.
+ */
+static int
+mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+ int ret;
+
+ if (cookie != priv->roc_cfg.cookie)
+ return -ENOENT;
+
+ ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE,
+ &priv->roc_cfg.chan, 0);
+
+ if (!ret) {
+ cfg80211_remain_on_channel_expired(wdev, cookie,
+ &priv->roc_cfg.chan,
+ GFP_ATOMIC);
+
+ memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg));
+
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: cancel ROC, cookie = 0x%llx\n", cookie);
+ }
+
+ return ret;
+}
+
+/*
+ * CFG802.11 operation handler to set Tx power.
+ */
+static int
+mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type,
+ int mbm)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_private *priv;
+ struct mwifiex_power_cfg power_cfg;
+ int dbm = MBM_TO_DBM(mbm);
+
+ if (type == NL80211_TX_POWER_FIXED) {
+ power_cfg.is_power_auto = 0;
+ power_cfg.power_level = dbm;
+ } else {
+ power_cfg.is_power_auto = 1;
+ }
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+ return mwifiex_set_tx_power(priv, &power_cfg);
+}
+
+/*
+ * CFG802.11 operation handler to set Power Save option.
+ *
+ * The timeout value, if provided, is currently ignored.
+ */
+static int
+mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev,
+ bool enabled, int timeout)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ u32 ps_mode;
+
+ if (timeout)
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ignore timeout value for IEEE Power Save\n");
+
+ ps_mode = enabled;
+
+ return mwifiex_drv_set_power(priv, &ps_mode);
+}
+
+/*
+ * CFG802.11 operation handler to set the default network key.
+ */
+static int
+mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool unicast,
+ bool multicast)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev);
+
+ /* Return if WEP key not configured */
+ if (!priv->sec_info.wep_enabled)
+ return 0;
+
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) {
+ priv->wep_key_curr_index = key_index;
+ } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index,
+ NULL, 0)) {
+ mwifiex_dbg(priv->adapter, ERROR, "set default Tx key index\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*
+ * CFG802.11 operation handler to add a network key.
+ */
+static int
+mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev);
+ struct mwifiex_wep_key *wep_key;
+ const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP &&
+ (params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ params->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ if (params->key && params->key_len) {
+ wep_key = &priv->wep_key[key_index];
+ memset(wep_key, 0, sizeof(struct mwifiex_wep_key));
+ memcpy(wep_key->key_material, params->key,
+ params->key_len);
+ wep_key->key_index = key_index;
+ wep_key->key_length = params->key_len;
+ priv->sec_info.wep_enabled = 1;
+ }
+ return 0;
+ }
+
+ if (mwifiex_set_encode(priv, params, params->key, params->key_len,
+ key_index, peer_mac, 0)) {
+ mwifiex_dbg(priv->adapter, ERROR, "crypto keys added\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*
+ * This function sends domain information to the firmware.
+ *
+ * The following information are passed to the firmware -
+ * - Country codes
+ * - Sub bands (first channel, number of channels, maximum Tx power)
+ */
+int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy)
+{
+ u8 no_of_triplet = 0;
+ struct ieee80211_country_ie_triplet *t;
+ u8 no_of_parsed_chan = 0;
+ u8 first_chan = 0, next_chan = 0, max_pwr = 0;
+ u8 i, flag = 0;
+ enum ieee80211_band band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_private *priv;
+ struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg;
+
+ /* Set country code */
+ domain_info->country_code[0] = adapter->country_code[0];
+ domain_info->country_code[1] = adapter->country_code[1];
+ domain_info->country_code[2] = ' ';
+
+ band = mwifiex_band_to_radio_type(adapter->config_bands);
+ if (!wiphy->bands[band]) {
+ mwifiex_dbg(adapter, ERROR,
+ "11D: setting domain info in FW\n");
+ return -1;
+ }
+
+ sband = wiphy->bands[band];
+
+ for (i = 0; i < sband->n_channels ; i++) {
+ ch = &sband->channels[i];
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (!flag) {
+ flag = 1;
+ first_chan = (u32) ch->hw_value;
+ next_chan = first_chan;
+ max_pwr = ch->max_power;
+ no_of_parsed_chan = 1;
+ continue;
+ }
+
+ if (ch->hw_value == next_chan + 1 &&
+ ch->max_power == max_pwr) {
+ next_chan++;
+ no_of_parsed_chan++;
+ } else {
+ t = &domain_info->triplet[no_of_triplet];
+ t->chans.first_channel = first_chan;
+ t->chans.num_channels = no_of_parsed_chan;
+ t->chans.max_power = max_pwr;
+ no_of_triplet++;
+ first_chan = (u32) ch->hw_value;
+ next_chan = first_chan;
+ max_pwr = ch->max_power;
+ no_of_parsed_chan = 1;
+ }
+ }
+
+ if (flag) {
+ t = &domain_info->triplet[no_of_triplet];
+ t->chans.first_channel = first_chan;
+ t->chans.num_channels = no_of_parsed_chan;
+ t->chans.max_power = max_pwr;
+ no_of_triplet++;
+ }
+
+ domain_info->no_of_triplet = no_of_triplet;
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
+ HostCmd_ACT_GEN_SET, 0, NULL, false)) {
+ mwifiex_dbg(adapter, INFO,
+ "11D: setting domain info in FW\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * CFG802.11 regulatory domain callback function.
+ *
+ * This function is called when the regulatory domain is changed due to the
+ * following reasons -
+ * - Set by driver
+ * - Set by system core
+ * - Set by user
+ * - Set bt Country IE
+ */
+static void mwifiex_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_private *priv = mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_ANY);
+ mwifiex_dbg(adapter, INFO,
+ "info: cfg80211 regulatory domain callback for %c%c\n",
+ request->alpha2[0], request->alpha2[1]);
+
+ switch (request->initiator) {
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ case NL80211_REGDOM_SET_BY_CORE:
+ case NL80211_REGDOM_SET_BY_USER:
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR,
+ "unknown regdom initiator: %d\n",
+ request->initiator);
+ return;
+ }
+
+ /* Don't send world or same regdom info to firmware */
+ if (strncmp(request->alpha2, "00", 2) &&
+ strncmp(request->alpha2, adapter->country_code,
+ sizeof(request->alpha2))) {
+ memcpy(adapter->country_code, request->alpha2,
+ sizeof(request->alpha2));
+ mwifiex_send_domain_info_cmd_fw(wiphy);
+ mwifiex_dnld_txpwr_table(priv);
+ }
+}
+
+/*
+ * This function sets the fragmentation threshold.
+ *
+ * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE
+ * and MWIFIEX_FRAG_MAX_VALUE.
+ */
+static int
+mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr)
+{
+ if (frag_thr < MWIFIEX_FRAG_MIN_VALUE ||
+ frag_thr > MWIFIEX_FRAG_MAX_VALUE)
+ frag_thr = MWIFIEX_FRAG_MAX_VALUE;
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET, FRAG_THRESH_I,
+ &frag_thr, true);
+}
+
+/*
+ * This function sets the RTS threshold.
+
+ * The rts value must lie between MWIFIEX_RTS_MIN_VALUE
+ * and MWIFIEX_RTS_MAX_VALUE.
+ */
+static int
+mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr)
+{
+ if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE)
+ rts_thr = MWIFIEX_RTS_MAX_VALUE;
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET, RTS_THRESH_I,
+ &rts_thr, true);
+}
+
+/*
+ * CFG802.11 operation handler to set wiphy parameters.
+ *
+ * This function can be used to set the RTS threshold and the
+ * Fragmentation threshold of the driver.
+ */
+static int
+mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_private *priv;
+ struct mwifiex_uap_bss_param *bss_cfg;
+ int ret;
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+ switch (priv->bss_role) {
+ case MWIFIEX_BSS_ROLE_UAP:
+ if (priv->bss_started) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot change wiphy params when bss started");
+ return -EINVAL;
+ }
+
+ bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL);
+ if (!bss_cfg)
+ return -ENOMEM;
+
+ mwifiex_set_sys_config_invalid_data(bss_cfg);
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+ bss_cfg->rts_threshold = wiphy->rts_threshold;
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
+ bss_cfg->frag_threshold = wiphy->frag_threshold;
+ if (changed & WIPHY_PARAM_RETRY_LONG)
+ bss_cfg->retry_limit = wiphy->retry_long;
+
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+ HostCmd_ACT_GEN_SET,
+ UAP_BSS_PARAMS_I, bss_cfg,
+ false);
+
+ kfree(bss_cfg);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to set wiphy phy params\n");
+ return ret;
+ }
+ break;
+
+ case MWIFIEX_BSS_ROLE_STA:
+ if (priv->media_connected) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot change wiphy params when connected");
+ return -EINVAL;
+ }
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+ ret = mwifiex_set_rts(priv,
+ wiphy->rts_threshold);
+ if (ret)
+ return ret;
+ }
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+ ret = mwifiex_set_frag(priv,
+ wiphy->frag_threshold);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int
+mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv)
+{
+ u16 mode = P2P_MODE_DISABLE;
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * This function initializes the functionalities for P2P client.
+ * The P2P client initialization sequence is:
+ * disable -> device -> client
+ */
+static int
+mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv)
+{
+ u16 mode;
+
+ if (mwifiex_cfg80211_deinit_p2p(priv))
+ return -1;
+
+ mode = P2P_MODE_DEVICE;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
+ return -1;
+
+ mode = P2P_MODE_CLIENT;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * This function initializes the functionalities for P2P GO.
+ * The P2P GO initialization sequence is:
+ * disable -> device -> GO
+ */
+static int
+mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv)
+{
+ u16 mode;
+
+ if (mwifiex_cfg80211_deinit_p2p(priv))
+ return -1;
+
+ mode = P2P_MODE_DEVICE;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
+ return -1;
+
+ mode = P2P_MODE_GO;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
+ return -1;
+
+ return 0;
+}
+
+static int mwifiex_deinit_priv_params(struct mwifiex_private *priv)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ unsigned long flags;
+
+ priv->mgmt_frame_mask = 0;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false)) {
+ mwifiex_dbg(adapter, ERROR,
+ "could not unregister mgmt frame rx\n");
+ return -1;
+ }
+
+ mwifiex_deauthenticate(priv, NULL);
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ adapter->main_locked = true;
+ if (adapter->mwifiex_processing) {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ flush_workqueue(adapter->workqueue);
+ } else {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ }
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ adapter->rx_locked = true;
+ if (adapter->rx_processing) {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ flush_workqueue(adapter->rx_workqueue);
+ } else {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ }
+
+ mwifiex_free_priv(priv);
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
+
+ return 0;
+}
+
+static int
+mwifiex_init_new_priv_params(struct mwifiex_private *priv,
+ struct net_device *dev,
+ enum nl80211_iftype type)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ unsigned long flags;
+
+ mwifiex_init_priv(priv);
+
+ priv->bss_mode = type;
+ priv->wdev.iftype = type;
+
+ mwifiex_init_priv_params(priv, priv->netdev);
+ priv->bss_started = 0;
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_STA);
+ priv->bss_role = MWIFIEX_BSS_ROLE_STA;
+ priv->bss_type = MWIFIEX_BSS_TYPE_STA;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_P2P);
+ priv->bss_role = MWIFIEX_BSS_ROLE_STA;
+ priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_P2P);
+ priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
+ priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
+ break;
+ case NL80211_IFTYPE_AP:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_UAP);
+ priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
+ priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR,
+ "%s: changing to %d not supported\n",
+ dev->name, type);
+ return -EOPNOTSUPP;
+ }
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ adapter->main_locked = false;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ adapter->rx_locked = false;
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+
+ return 0;
+}
+
+static int
+mwifiex_change_vif_to_p2p(struct net_device *dev,
+ enum nl80211_iftype curr_iftype,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct mwifiex_private *priv;
+ struct mwifiex_adapter *adapter;
+
+ priv = mwifiex_netdev_get_priv(dev);
+
+ if (!priv)
+ return -1;
+
+ adapter = priv->adapter;
+
+ if (adapter->curr_iface_comb.p2p_intf ==
+ adapter->iface_limit.p2p_intf) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create multiple P2P ifaces\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "%s: changing role to p2p\n", dev->name);
+
+ if (mwifiex_deinit_priv_params(priv))
+ return -1;
+ if (mwifiex_init_new_priv_params(priv, dev, type))
+ return -1;
+
+ switch (type) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (mwifiex_cfg80211_init_p2p_client(priv))
+ return -EFAULT;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ if (mwifiex_cfg80211_init_p2p_go(priv))
+ return -EFAULT;
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR,
+ "%s: changing to %d not supported\n",
+ dev->name, type);
+ return -EOPNOTSUPP;
+ }
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+ HostCmd_ACT_GEN_SET, 0, NULL, true))
+ return -1;
+
+ if (mwifiex_sta_init_cmd(priv, false, false))
+ return -1;
+
+ switch (curr_iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ adapter->curr_iface_comb.sta_intf--;
+ break;
+ case NL80211_IFTYPE_AP:
+ adapter->curr_iface_comb.uap_intf--;
+ break;
+ default:
+ break;
+ }
+
+ adapter->curr_iface_comb.p2p_intf++;
+ dev->ieee80211_ptr->iftype = type;
+
+ return 0;
+}
+
+static int
+mwifiex_change_vif_to_sta_adhoc(struct net_device *dev,
+ enum nl80211_iftype curr_iftype,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct mwifiex_private *priv;
+ struct mwifiex_adapter *adapter;
+
+ priv = mwifiex_netdev_get_priv(dev);
+
+ if (!priv)
+ return -1;
+
+ adapter = priv->adapter;
+
+ if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT &&
+ curr_iftype != NL80211_IFTYPE_P2P_GO) &&
+ (adapter->curr_iface_comb.sta_intf ==
+ adapter->iface_limit.sta_intf)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create multiple station/adhoc ifaces\n");
+ return -1;
+ }
+
+ if (type == NL80211_IFTYPE_STATION)
+ mwifiex_dbg(adapter, INFO,
+ "%s: changing role to station\n", dev->name);
+ else
+ mwifiex_dbg(adapter, INFO,
+ "%s: changing role to adhoc\n", dev->name);
+
+ if (mwifiex_deinit_priv_params(priv))
+ return -1;
+ if (mwifiex_init_new_priv_params(priv, dev, type))
+ return -1;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+ HostCmd_ACT_GEN_SET, 0, NULL, true))
+ return -1;
+ if (mwifiex_sta_init_cmd(priv, false, false))
+ return -1;
+
+ switch (curr_iftype) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ adapter->curr_iface_comb.p2p_intf--;
+ break;
+ case NL80211_IFTYPE_AP:
+ adapter->curr_iface_comb.uap_intf--;
+ break;
+ default:
+ break;
+ }
+
+ adapter->curr_iface_comb.sta_intf++;
+ dev->ieee80211_ptr->iftype = type;
+ return 0;
+}
+
+static int
+mwifiex_change_vif_to_ap(struct net_device *dev,
+ enum nl80211_iftype curr_iftype,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct mwifiex_private *priv;
+ struct mwifiex_adapter *adapter;
+
+ priv = mwifiex_netdev_get_priv(dev);
+
+ if (!priv)
+ return -1;
+
+ adapter = priv->adapter;
+
+ if (adapter->curr_iface_comb.uap_intf ==
+ adapter->iface_limit.uap_intf) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create multiple AP ifaces\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "%s: changing role to AP\n", dev->name);
+
+ if (mwifiex_deinit_priv_params(priv))
+ return -1;
+ if (mwifiex_init_new_priv_params(priv, dev, type))
+ return -1;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+ HostCmd_ACT_GEN_SET, 0, NULL, true))
+ return -1;
+ if (mwifiex_sta_init_cmd(priv, false, false))
+ return -1;
+
+ switch (curr_iftype) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ adapter->curr_iface_comb.p2p_intf--;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ adapter->curr_iface_comb.sta_intf--;
+ break;
+ default:
+ break;
+ }
+
+ adapter->curr_iface_comb.uap_intf++;
+ dev->ieee80211_ptr->iftype = type;
+ return 0;
+}
+/*
+ * CFG802.11 operation handler to change interface type.
+ */
+static int
+mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype;
+
+ switch (curr_iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ priv->bss_mode = type;
+ priv->sec_info.authentication_mode =
+ NL80211_AUTHTYPE_OPEN_SYSTEM;
+ dev->ieee80211_ptr->iftype = type;
+ mwifiex_deauthenticate(priv, NULL);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+ HostCmd_ACT_GEN_SET, 0, NULL,
+ true);
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ return mwifiex_change_vif_to_p2p(dev, curr_iftype,
+ type, flags, params);
+ case NL80211_IFTYPE_AP:
+ return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
+ flags, params);
+ case NL80211_IFTYPE_UNSPECIFIED:
+ mwifiex_dbg(priv->adapter, INFO,
+ "%s: kept type as IBSS\n", dev->name);
+ case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */
+ return 0;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: changing to %d not supported\n",
+ dev->name, type);
+ return -EOPNOTSUPP;
+ }
+ break;
+ case NL80211_IFTYPE_STATION:
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ priv->bss_mode = type;
+ priv->sec_info.authentication_mode =
+ NL80211_AUTHTYPE_OPEN_SYSTEM;
+ dev->ieee80211_ptr->iftype = type;
+ mwifiex_deauthenticate(priv, NULL);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+ HostCmd_ACT_GEN_SET, 0, NULL,
+ true);
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ return mwifiex_change_vif_to_p2p(dev, curr_iftype,
+ type, flags, params);
+ case NL80211_IFTYPE_AP:
+ return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
+ flags, params);
+ case NL80211_IFTYPE_UNSPECIFIED:
+ mwifiex_dbg(priv->adapter, INFO,
+ "%s: kept type as STA\n", dev->name);
+ case NL80211_IFTYPE_STATION: /* This shouldn't happen */
+ return 0;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: changing to %d not supported\n",
+ dev->name, type);
+ return -EOPNOTSUPP;
+ }
+ break;
+ case NL80211_IFTYPE_AP:
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype,
+ type, flags,
+ params);
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ return mwifiex_change_vif_to_p2p(dev, curr_iftype,
+ type, flags, params);
+ case NL80211_IFTYPE_UNSPECIFIED:
+ mwifiex_dbg(priv->adapter, INFO,
+ "%s: kept type as AP\n", dev->name);
+ case NL80211_IFTYPE_AP: /* This shouldn't happen */
+ return 0;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: changing to %d not supported\n",
+ dev->name, type);
+ return -EOPNOTSUPP;
+ }
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ if (mwifiex_cfg80211_deinit_p2p(priv))
+ return -EFAULT;
+ priv->adapter->curr_iface_comb.p2p_intf--;
+ priv->adapter->curr_iface_comb.sta_intf++;
+ dev->ieee80211_ptr->iftype = type;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (mwifiex_cfg80211_deinit_p2p(priv))
+ return -EFAULT;
+ return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype,
+ type, flags,
+ params);
+ break;
+ case NL80211_IFTYPE_AP:
+ if (mwifiex_cfg80211_deinit_p2p(priv))
+ return -EFAULT;
+ return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
+ flags, params);
+ case NL80211_IFTYPE_UNSPECIFIED:
+ mwifiex_dbg(priv->adapter, INFO,
+ "%s: kept type as P2P\n", dev->name);
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ return 0;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: changing to %d not supported\n",
+ dev->name, type);
+ return -EOPNOTSUPP;
+ }
+ break;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: unknown iftype: %d\n",
+ dev->name, dev->ieee80211_ptr->iftype);
+ return -EOPNOTSUPP;
+ }
+
+
+ return 0;
+}
+
+static void
+mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo,
+ struct rate_info *rate)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ if (adapter->is_hw_11ac_capable) {
+ /* bit[1-0]: 00=LG 01=HT 10=VHT */
+ if (tx_htinfo & BIT(0)) {
+ /* HT */
+ rate->mcs = priv->tx_rate;
+ rate->flags |= RATE_INFO_FLAGS_MCS;
+ }
+ if (tx_htinfo & BIT(1)) {
+ /* VHT */
+ rate->mcs = priv->tx_rate & 0x0F;
+ rate->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ }
+
+ if (tx_htinfo & (BIT(1) | BIT(0))) {
+ /* HT or VHT */
+ switch (tx_htinfo & (BIT(3) | BIT(2))) {
+ case 0:
+ rate->bw = RATE_INFO_BW_20;
+ break;
+ case (BIT(2)):
+ rate->bw = RATE_INFO_BW_40;
+ break;
+ case (BIT(3)):
+ rate->bw = RATE_INFO_BW_80;
+ break;
+ case (BIT(3) | BIT(2)):
+ rate->bw = RATE_INFO_BW_160;
+ break;
+ }
+
+ if (tx_htinfo & BIT(4))
+ rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+ if ((priv->tx_rate >> 4) == 1)
+ rate->nss = 2;
+ else
+ rate->nss = 1;
+ }
+ } else {
+ /*
+ * Bit 0 in tx_htinfo indicates that current Tx rate
+ * is 11n rate. Valid MCS index values for us are 0 to 15.
+ */
+ if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
+ rate->mcs = priv->tx_rate;
+ rate->flags |= RATE_INFO_FLAGS_MCS;
+ rate->bw = RATE_INFO_BW_20;
+ if (tx_htinfo & BIT(1))
+ rate->bw = RATE_INFO_BW_40;
+ if (tx_htinfo & BIT(2))
+ rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+ }
+ }
+}
+
+/*
+ * This function dumps the station information on a buffer.
+ *
+ * The following information are shown -
+ * - Total bytes transmitted
+ * - Total bytes received
+ * - Total packets transmitted
+ * - Total packets received
+ * - Signal quality level
+ * - Transmission rate
+ */
+static int
+mwifiex_dump_station_info(struct mwifiex_private *priv,
+ struct mwifiex_sta_node *node,
+ struct station_info *sinfo)
+{
+ u32 rate;
+
+ sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) |
+ BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_BITRATE) |
+ BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG);
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+ if (!node)
+ return -ENOENT;
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) |
+ BIT(NL80211_STA_INFO_TX_FAILED);
+ sinfo->inactive_time =
+ jiffies_to_msecs(jiffies - node->stats.last_rx);
+
+ sinfo->signal = node->stats.rssi;
+ sinfo->signal_avg = node->stats.rssi;
+ sinfo->rx_bytes = node->stats.rx_bytes;
+ sinfo->tx_bytes = node->stats.tx_bytes;
+ sinfo->rx_packets = node->stats.rx_packets;
+ sinfo->tx_packets = node->stats.tx_packets;
+ sinfo->tx_failed = node->stats.tx_failed;
+
+ mwifiex_parse_htinfo(priv, node->stats.last_tx_htinfo,
+ &sinfo->txrate);
+ sinfo->txrate.legacy = node->stats.last_tx_rate * 5;
+
+ return 0;
+ }
+
+ /* Get signal information from the firmware */
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
+ HostCmd_ACT_GEN_GET, 0, NULL, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "failed to get signal information\n");
+ return -EFAULT;
+ }
+
+ if (mwifiex_drv_get_data_rate(priv, &rate)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "getting data rate error\n");
+ return -EFAULT;
+ }
+
+ /* Get DTIM period information from firmware */
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, DTIM_PERIOD_I,
+ &priv->dtim_period, true);
+
+ mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate);
+
+ sinfo->signal_avg = priv->bcn_rssi_avg;
+ sinfo->rx_bytes = priv->stats.rx_bytes;
+ sinfo->tx_bytes = priv->stats.tx_bytes;
+ sinfo->rx_packets = priv->stats.rx_packets;
+ sinfo->tx_packets = priv->stats.tx_packets;
+ sinfo->signal = priv->bcn_rssi_avg;
+ /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */
+ sinfo->txrate.legacy = rate * 5;
+
+ if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
+ sinfo->bss_param.flags = 0;
+ if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
+ WLAN_CAPABILITY_SHORT_PREAMBLE)
+ sinfo->bss_param.flags |=
+ BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+ if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
+ WLAN_CAPABILITY_SHORT_SLOT_TIME)
+ sinfo->bss_param.flags |=
+ BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+ sinfo->bss_param.dtim_period = priv->dtim_period;
+ sinfo->bss_param.beacon_interval =
+ priv->curr_bss_params.bss_descriptor.beacon_period;
+ }
+
+ return 0;
+}
+
+/*
+ * CFG802.11 operation handler to get station information.
+ *
+ * This function only works in connected mode, and dumps the
+ * requested station information, if available.
+ */
+static int
+mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (!priv->media_connected)
+ return -ENOENT;
+ if (memcmp(mac, priv->cfg_bssid, ETH_ALEN))
+ return -ENOENT;
+
+ return mwifiex_dump_station_info(priv, NULL, sinfo);
+}
+
+/*
+ * CFG802.11 operation handler to dump station information.
+ */
+static int
+mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ static struct mwifiex_sta_node *node;
+
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ priv->media_connected && idx == 0) {
+ ether_addr_copy(mac, priv->cfg_bssid);
+ return mwifiex_dump_station_info(priv, NULL, sinfo);
+ } else if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+ mwifiex_send_cmd(priv, HOST_CMD_APCMD_STA_LIST,
+ HostCmd_ACT_GEN_GET, 0, NULL, true);
+
+ if (node && (&node->list == &priv->sta_list)) {
+ node = NULL;
+ return -ENOENT;
+ }
+
+ node = list_prepare_entry(node, &priv->sta_list, list);
+ list_for_each_entry_continue(node, &priv->sta_list, list) {
+ ether_addr_copy(mac, node->mac_addr);
+ return mwifiex_dump_station_info(priv, node, sinfo);
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int
+mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
+ int idx, struct survey_info *survey)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_chan_stats *pchan_stats = priv->adapter->chan_stats;
+ enum ieee80211_band band;
+
+ mwifiex_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx);
+
+ memset(survey, 0, sizeof(struct survey_info));
+
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ priv->media_connected && idx == 0) {
+ u8 curr_bss_band = priv->curr_bss_params.band;
+ u32 chan = priv->curr_bss_params.bss_descriptor.channel;
+
+ band = mwifiex_band_to_radio_type(curr_bss_band);
+ survey->channel = ieee80211_get_channel(wiphy,
+ ieee80211_channel_to_frequency(chan, band));
+
+ if (priv->bcn_nf_last) {
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ survey->noise = priv->bcn_nf_last;
+ }
+ return 0;
+ }
+
+ if (idx >= priv->adapter->num_in_chan_stats)
+ return -ENOENT;
+
+ if (!pchan_stats[idx].cca_scan_dur)
+ return 0;
+
+ band = pchan_stats[idx].bandcfg;
+ survey->channel = ieee80211_get_channel(wiphy,
+ ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band));
+ survey->filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_BUSY;
+ survey->noise = pchan_stats[idx].noise;
+ survey->time = pchan_stats[idx].cca_scan_dur;
+ survey->time_busy = pchan_stats[idx].cca_busy_dur;
+
+ return 0;
+}
+
+/* Supported rates to be advertised to the cfg80211 */
+static struct ieee80211_rate mwifiex_rates[] = {
+ {.bitrate = 10, .hw_value = 2, },
+ {.bitrate = 20, .hw_value = 4, },
+ {.bitrate = 55, .hw_value = 11, },
+ {.bitrate = 110, .hw_value = 22, },
+ {.bitrate = 60, .hw_value = 12, },
+ {.bitrate = 90, .hw_value = 18, },
+ {.bitrate = 120, .hw_value = 24, },
+ {.bitrate = 180, .hw_value = 36, },
+ {.bitrate = 240, .hw_value = 48, },
+ {.bitrate = 360, .hw_value = 72, },
+ {.bitrate = 480, .hw_value = 96, },
+ {.bitrate = 540, .hw_value = 108, },
+};
+
+/* Channel definitions to be advertised to cfg80211 */
+static struct ieee80211_channel mwifiex_channels_2ghz[] = {
+ {.center_freq = 2412, .hw_value = 1, },
+ {.center_freq = 2417, .hw_value = 2, },
+ {.center_freq = 2422, .hw_value = 3, },
+ {.center_freq = 2427, .hw_value = 4, },
+ {.center_freq = 2432, .hw_value = 5, },
+ {.center_freq = 2437, .hw_value = 6, },
+ {.center_freq = 2442, .hw_value = 7, },
+ {.center_freq = 2447, .hw_value = 8, },
+ {.center_freq = 2452, .hw_value = 9, },
+ {.center_freq = 2457, .hw_value = 10, },
+ {.center_freq = 2462, .hw_value = 11, },
+ {.center_freq = 2467, .hw_value = 12, },
+ {.center_freq = 2472, .hw_value = 13, },
+ {.center_freq = 2484, .hw_value = 14, },
+};
+
+static struct ieee80211_supported_band mwifiex_band_2ghz = {
+ .channels = mwifiex_channels_2ghz,
+ .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz),
+ .bitrates = mwifiex_rates,
+ .n_bitrates = ARRAY_SIZE(mwifiex_rates),
+};
+
+static struct ieee80211_channel mwifiex_channels_5ghz[] = {
+ {.center_freq = 5040, .hw_value = 8, },
+ {.center_freq = 5060, .hw_value = 12, },
+ {.center_freq = 5080, .hw_value = 16, },
+ {.center_freq = 5170, .hw_value = 34, },
+ {.center_freq = 5190, .hw_value = 38, },
+ {.center_freq = 5210, .hw_value = 42, },
+ {.center_freq = 5230, .hw_value = 46, },
+ {.center_freq = 5180, .hw_value = 36, },
+ {.center_freq = 5200, .hw_value = 40, },
+ {.center_freq = 5220, .hw_value = 44, },
+ {.center_freq = 5240, .hw_value = 48, },
+ {.center_freq = 5260, .hw_value = 52, },
+ {.center_freq = 5280, .hw_value = 56, },
+ {.center_freq = 5300, .hw_value = 60, },
+ {.center_freq = 5320, .hw_value = 64, },
+ {.center_freq = 5500, .hw_value = 100, },
+ {.center_freq = 5520, .hw_value = 104, },
+ {.center_freq = 5540, .hw_value = 108, },
+ {.center_freq = 5560, .hw_value = 112, },
+ {.center_freq = 5580, .hw_value = 116, },
+ {.center_freq = 5600, .hw_value = 120, },
+ {.center_freq = 5620, .hw_value = 124, },
+ {.center_freq = 5640, .hw_value = 128, },
+ {.center_freq = 5660, .hw_value = 132, },
+ {.center_freq = 5680, .hw_value = 136, },
+ {.center_freq = 5700, .hw_value = 140, },
+ {.center_freq = 5745, .hw_value = 149, },
+ {.center_freq = 5765, .hw_value = 153, },
+ {.center_freq = 5785, .hw_value = 157, },
+ {.center_freq = 5805, .hw_value = 161, },
+ {.center_freq = 5825, .hw_value = 165, },
+};
+
+static struct ieee80211_supported_band mwifiex_band_5ghz = {
+ .channels = mwifiex_channels_5ghz,
+ .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz),
+ .bitrates = mwifiex_rates + 4,
+ .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4,
+};
+
+
+/* Supported crypto cipher suits to be advertised to cfg80211 */
+static const u32 mwifiex_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_SMS4,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+};
+
+/* Supported mgmt frame types to be advertised to cfg80211 */
+static const struct ieee80211_txrx_stypes
+mwifiex_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ },
+};
+
+/*
+ * CFG802.11 operation handler for setting bit rates.
+ *
+ * Function configures data rates to firmware using bitrate mask
+ * provided by cfg80211.
+ */
+static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *peer,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ u16 bitmap_rates[MAX_BITMAP_RATES_SIZE];
+ enum ieee80211_band band;
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ if (!priv->media_connected) {
+ mwifiex_dbg(adapter, ERROR,
+ "Can not set Tx data rate in disconnected state\n");
+ return -EINVAL;
+ }
+
+ band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+
+ memset(bitmap_rates, 0, sizeof(bitmap_rates));
+
+ /* Fill HR/DSSS rates. */
+ if (band == IEEE80211_BAND_2GHZ)
+ bitmap_rates[0] = mask->control[band].legacy & 0x000f;
+
+ /* Fill OFDM rates */
+ if (band == IEEE80211_BAND_2GHZ)
+ bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4;
+ else
+ bitmap_rates[1] = mask->control[band].legacy;
+
+ /* Fill HT MCS rates */
+ bitmap_rates[2] = mask->control[band].ht_mcs[0];
+ if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
+ bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8;
+
+ /* Fill VHT MCS rates */
+ if (adapter->fw_api_ver == MWIFIEX_FW_V15) {
+ bitmap_rates[10] = mask->control[band].vht_mcs[0];
+ if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
+ bitmap_rates[11] = mask->control[band].vht_mcs[1];
+ }
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG,
+ HostCmd_ACT_GEN_SET, 0, bitmap_rates, true);
+}
+
+/*
+ * CFG802.11 operation handler for connection quality monitoring.
+ *
+ * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI
+ * events to FW.
+ */
+static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_ds_misc_subsc_evt subsc_evt;
+
+ priv->cqm_rssi_thold = rssi_thold;
+ priv->cqm_rssi_hyst = rssi_hyst;
+
+ memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt));
+ subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+
+ /* Subscribe/unsubscribe low and high rssi events */
+ if (rssi_thold && rssi_hyst) {
+ subsc_evt.action = HostCmd_ACT_BITWISE_SET;
+ subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold);
+ subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold);
+ subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
+ subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
+ return mwifiex_send_cmd(priv,
+ HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt, true);
+ } else {
+ subsc_evt.action = HostCmd_ACT_BITWISE_CLR;
+ return mwifiex_send_cmd(priv,
+ HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt, true);
+ }
+
+ return 0;
+}
+
+/* cfg80211 operation handler for change_beacon.
+ * Function retrieves and sets modified management IEs to FW.
+ */
+static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_beacon_data *data)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: bss_type mismatched\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!priv->bss_started) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: bss not started\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mwifiex_set_mgmt_ies(priv, data)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: setting mgmt ies failed\n", __func__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/* cfg80211 operation handler for del_station.
+ * Function deauthenticates station which value is provided in mac parameter.
+ * If mac is NULL/broadcast, all stations in associated station list are
+ * deauthenticated. If bss is not started or there are no stations in
+ * associated stations list, no action is taken.
+ */
+static int
+mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+ struct station_del_parameters *params)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_sta_node *sta_node;
+ u8 deauth_mac[ETH_ALEN];
+ unsigned long flags;
+
+ if (!priv->bss_started && priv->wdev.cac_started) {
+ mwifiex_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__);
+ mwifiex_abort_cac(priv);
+ }
+
+ if (list_empty(&priv->sta_list) || !priv->bss_started)
+ return 0;
+
+ if (!params->mac || is_broadcast_ether_addr(params->mac))
+ return 0;
+
+ mwifiex_dbg(priv->adapter, INFO, "%s: mac address %pM\n",
+ __func__, params->mac);
+
+ eth_zero_addr(deauth_mac);
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_node = mwifiex_get_sta_entry(priv, params->mac);
+ if (sta_node)
+ ether_addr_copy(deauth_mac, params->mac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (is_valid_ether_addr(deauth_mac)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ deauth_mac, true))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_private *priv = mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_ANY);
+ struct mwifiex_ds_ant_cfg ant_cfg;
+
+ if (!tx_ant || !rx_ant)
+ return -EOPNOTSUPP;
+
+ if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) {
+ /* Not a MIMO chip. User should provide specific antenna number
+ * for Tx/Rx path or enable all antennas for diversity
+ */
+ if (tx_ant != rx_ant)
+ return -EOPNOTSUPP;
+
+ if ((tx_ant & (tx_ant - 1)) &&
+ (tx_ant != BIT(adapter->number_of_antenna) - 1))
+ return -EOPNOTSUPP;
+
+ if ((tx_ant == BIT(adapter->number_of_antenna) - 1) &&
+ (priv->adapter->number_of_antenna > 1)) {
+ tx_ant = RF_ANTENNA_AUTO;
+ rx_ant = RF_ANTENNA_AUTO;
+ }
+ } else {
+ struct ieee80211_sta_ht_cap *ht_info;
+ int rx_mcs_supp;
+ enum ieee80211_band band;
+
+ if ((tx_ant == 0x1 && rx_ant == 0x1)) {
+ adapter->user_dev_mcs_support = HT_STREAM_1X1;
+ if (adapter->is_hw_11ac_capable)
+ adapter->usr_dot_11ac_mcs_support =
+ MWIFIEX_11AC_MCS_MAP_1X1;
+ } else {
+ adapter->user_dev_mcs_support = HT_STREAM_2X2;
+ if (adapter->is_hw_11ac_capable)
+ adapter->usr_dot_11ac_mcs_support =
+ MWIFIEX_11AC_MCS_MAP_2X2;
+ }
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ if (!adapter->wiphy->bands[band])
+ continue;
+
+ ht_info = &adapter->wiphy->bands[band]->ht_cap;
+ rx_mcs_supp =
+ GET_RXMCSSUPP(adapter->user_dev_mcs_support);
+ memset(&ht_info->mcs, 0, adapter->number_of_antenna);
+ memset(&ht_info->mcs, 0xff, rx_mcs_supp);
+ }
+ }
+
+ ant_cfg.tx_ant = tx_ant;
+ ant_cfg.rx_ant = rx_ant;
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA,
+ HostCmd_ACT_GEN_SET, 0, &ant_cfg, true);
+}
+
+/* cfg80211 operation handler for stop ap.
+ * Function stops BSS running at uAP interface.
+ */
+static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ mwifiex_abort_cac(priv);
+
+ if (mwifiex_del_mgmt_ies(priv))
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to delete mgmt IEs!\n");
+
+ priv->ap_11n_enabled = 0;
+ memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg));
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
+ HostCmd_ACT_GEN_SET, 0, NULL, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to stop the BSS\n");
+ return -1;
+ }
+
+ if (mwifiex_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET,
+ HostCmd_ACT_GEN_SET, 0, NULL, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to reset BSS\n");
+ return -1;
+ }
+
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+ mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
+
+ return 0;
+}
+
+/* cfg80211 operation handler for start_ap.
+ * Function sets beacon period, DTIM period, SSID and security into
+ * AP config structure.
+ * AP is configured with these settings and BSS is started.
+ */
+static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_ap_settings *params)
+{
+ struct mwifiex_uap_bss_param *bss_cfg;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP)
+ return -1;
+
+ bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL);
+ if (!bss_cfg)
+ return -ENOMEM;
+
+ mwifiex_set_sys_config_invalid_data(bss_cfg);
+
+ if (params->beacon_interval)
+ bss_cfg->beacon_period = params->beacon_interval;
+ if (params->dtim_period)
+ bss_cfg->dtim_period = params->dtim_period;
+
+ if (params->ssid && params->ssid_len) {
+ memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len);
+ bss_cfg->ssid.ssid_len = params->ssid_len;
+ }
+ if (params->inactivity_timeout > 0) {
+ /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */
+ bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout;
+ bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout;
+ }
+
+ switch (params->hidden_ssid) {
+ case NL80211_HIDDEN_SSID_NOT_IN_USE:
+ bss_cfg->bcast_ssid_ctl = 1;
+ break;
+ case NL80211_HIDDEN_SSID_ZERO_LEN:
+ bss_cfg->bcast_ssid_ctl = 0;
+ break;
+ case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
+ /* firmware doesn't support this type of hidden SSID */
+ default:
+ kfree(bss_cfg);
+ return -EINVAL;
+ }
+
+ mwifiex_uap_set_channel(priv, bss_cfg, params->chandef);
+ mwifiex_set_uap_rates(bss_cfg, params);
+
+ if (mwifiex_set_secure_params(priv, bss_cfg, params)) {
+ kfree(bss_cfg);
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to parse secuirty parameters!\n");
+ return -1;
+ }
+
+ mwifiex_set_ht_params(priv, bss_cfg, params);
+
+ if (priv->adapter->is_hw_11ac_capable) {
+ mwifiex_set_vht_params(priv, bss_cfg, params);
+ mwifiex_set_vht_width(priv, params->chandef.width,
+ priv->ap_11ac_enabled);
+ }
+
+ if (priv->ap_11ac_enabled)
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
+ mwifiex_set_wmm_params(priv, bss_cfg, params);
+
+ if (mwifiex_is_11h_active(priv))
+ mwifiex_set_tpc_params(priv, bss_cfg, params);
+
+ if (mwifiex_is_11h_active(priv) &&
+ !cfg80211_chandef_dfs_required(wiphy, &params->chandef,
+ priv->bss_mode)) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "Disable 11h extensions in FW\n");
+ if (mwifiex_11h_activate(priv, false)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to disable 11h extensions!!");
+ return -1;
+ }
+ priv->state_11h.is_11h_active = false;
+ }
+
+ if (mwifiex_config_start_uap(priv, bss_cfg)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to start AP\n");
+ kfree(bss_cfg);
+ return -1;
+ }
+
+ if (mwifiex_set_mgmt_ies(priv, &params->beacon))
+ return -1;
+
+ if (!netif_carrier_ok(priv->netdev))
+ netif_carrier_on(priv->netdev);
+ mwifiex_wake_up_net_dev_queue(priv->netdev, priv->adapter);
+
+ memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg));
+ kfree(bss_cfg);
+ return 0;
+}
+
+/*
+ * CFG802.11 operation handler for disconnection request.
+ *
+ * This function does not work when there is already a disconnection
+ * procedure going on.
+ */
+static int
+mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (mwifiex_deauthenticate(priv, NULL))
+ return -EFAULT;
+
+ mwifiex_dbg(priv->adapter, MSG,
+ "info: successfully disconnected from %pM:\t"
+ "reason code %d\n", priv->cfg_bssid, reason_code);
+
+ eth_zero_addr(priv->cfg_bssid);
+ priv->hs2_enabled = false;
+
+ return 0;
+}
+
+/*
+ * This function informs the CFG802.11 subsystem of a new IBSS.
+ *
+ * The following information are sent to the CFG802.11 subsystem
+ * to register the new IBSS. If we do not register the new IBSS,
+ * a kernel panic will result.
+ * - SSID
+ * - SSID length
+ * - BSSID
+ * - Channel
+ */
+static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
+{
+ struct ieee80211_channel *chan;
+ struct mwifiex_bss_info bss_info;
+ struct cfg80211_bss *bss;
+ int ie_len;
+ u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)];
+ enum ieee80211_band band;
+
+ if (mwifiex_get_bss_info(priv, &bss_info))
+ return -1;
+
+ ie_buf[0] = WLAN_EID_SSID;
+ ie_buf[1] = bss_info.ssid.ssid_len;
+
+ memcpy(&ie_buf[sizeof(struct ieee_types_header)],
+ &bss_info.ssid.ssid, bss_info.ssid.ssid_len);
+ ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
+
+ band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+ chan = __ieee80211_get_channel(priv->wdev.wiphy,
+ ieee80211_channel_to_frequency(bss_info.bss_chan,
+ band));
+
+ bss = cfg80211_inform_bss(priv->wdev.wiphy, chan,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
+ 0, ie_buf, ie_len, 0, GFP_KERNEL);
+ if (bss) {
+ cfg80211_put_bss(priv->wdev.wiphy, bss);
+ ether_addr_copy(priv->cfg_bssid, bss_info.bssid);
+ }
+
+ return 0;
+}
+
+/*
+ * This function connects with a BSS.
+ *
+ * This function handles both Infra and Ad-Hoc modes. It also performs
+ * validity checking on the provided parameters, disconnects from the
+ * current BSS (if any), sets up the association/scan parameters,
+ * including security settings, and performs specific SSID scan before
+ * trying to connect.
+ *
+ * For Infra mode, the function returns failure if the specified SSID
+ * is not found in scan table. However, for Ad-Hoc mode, it can create
+ * the IBSS if it does not exist. On successful completion in either case,
+ * the function notifies the CFG802.11 subsystem of the new BSS connection.
+ */
+static int
+mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len,
+ const u8 *ssid, const u8 *bssid, int mode,
+ struct ieee80211_channel *channel,
+ struct cfg80211_connect_params *sme, bool privacy)
+{
+ struct cfg80211_ssid req_ssid;
+ int ret, auth_type = 0;
+ struct cfg80211_bss *bss = NULL;
+ u8 is_scanning_required = 0;
+
+ memset(&req_ssid, 0, sizeof(struct cfg80211_ssid));
+
+ req_ssid.ssid_len = ssid_len;
+ if (ssid_len > IEEE80211_MAX_SSID_LEN) {
+ mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n");
+ return -EINVAL;
+ }
+
+ memcpy(req_ssid.ssid, ssid, ssid_len);
+ if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) {
+ mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n");
+ return -EINVAL;
+ }
+
+ /* As this is new association, clear locally stored
+ * keys and security related flags */
+ priv->sec_info.wpa_enabled = false;
+ priv->sec_info.wpa2_enabled = false;
+ priv->wep_key_curr_index = 0;
+ priv->sec_info.encryption_mode = 0;
+ priv->sec_info.is_authtype_auto = 0;
+ ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1);
+
+ if (mode == NL80211_IFTYPE_ADHOC) {
+ /* "privacy" is set only for ad-hoc mode */
+ if (privacy) {
+ /*
+ * Keep WLAN_CIPHER_SUITE_WEP104 for now so that
+ * the firmware can find a matching network from the
+ * scan. The cfg80211 does not give us the encryption
+ * mode at this stage so just setting it to WEP here.
+ */
+ priv->sec_info.encryption_mode =
+ WLAN_CIPHER_SUITE_WEP104;
+ priv->sec_info.authentication_mode =
+ NL80211_AUTHTYPE_OPEN_SYSTEM;
+ }
+
+ goto done;
+ }
+
+ /* Now handle infra mode. "sme" is valid for infra mode only */
+ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
+ auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ priv->sec_info.is_authtype_auto = 1;
+ } else {
+ auth_type = sme->auth_type;
+ }
+
+ if (sme->crypto.n_ciphers_pairwise) {
+ priv->sec_info.encryption_mode =
+ sme->crypto.ciphers_pairwise[0];
+ priv->sec_info.authentication_mode = auth_type;
+ }
+
+ if (sme->crypto.cipher_group) {
+ priv->sec_info.encryption_mode = sme->crypto.cipher_group;
+ priv->sec_info.authentication_mode = auth_type;
+ }
+ if (sme->ie)
+ ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len);
+
+ if (sme->key) {
+ if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: setting wep encryption\t"
+ "with key len %d\n", sme->key_len);
+ priv->wep_key_curr_index = sme->key_idx;
+ ret = mwifiex_set_encode(priv, NULL, sme->key,
+ sme->key_len, sme->key_idx,
+ NULL, 0);
+ }
+ }
+done:
+ /*
+ * Scan entries are valid for some time (15 sec). So we can save one
+ * active scan time if we just try cfg80211_get_bss first. If it fails
+ * then request scan and cfg80211_get_bss() again for final output.
+ */
+ while (1) {
+ if (is_scanning_required) {
+ /* Do specific SSID scanning */
+ if (mwifiex_request_scan(priv, &req_ssid)) {
+ mwifiex_dbg(priv->adapter, ERROR, "scan error\n");
+ return -EFAULT;
+ }
+ }
+
+ /* Find the BSS we want using available scan results */
+ if (mode == NL80211_IFTYPE_ADHOC)
+ bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
+ bssid, ssid, ssid_len,
+ IEEE80211_BSS_TYPE_IBSS,
+ IEEE80211_PRIVACY_ANY);
+ else
+ bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
+ bssid, ssid, ssid_len,
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY);
+
+ if (!bss) {
+ if (is_scanning_required) {
+ mwifiex_dbg(priv->adapter, WARN,
+ "assoc: requested bss not found in scan results\n");
+ break;
+ }
+ is_scanning_required = 1;
+ } else {
+ mwifiex_dbg(priv->adapter, MSG,
+ "info: trying to associate to '%s' bssid %pM\n",
+ (char *)req_ssid.ssid, bss->bssid);
+ memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN);
+ break;
+ }
+ }
+
+ ret = mwifiex_bss_start(priv, bss, &req_ssid);
+ if (ret)
+ return ret;
+
+ if (mode == NL80211_IFTYPE_ADHOC) {
+ /* Inform the BSS information to kernel, otherwise
+ * kernel will give a panic after successful assoc */
+ if (mwifiex_cfg80211_inform_ibss_bss(priv))
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+/*
+ * CFG802.11 operation handler for association request.
+ *
+ * This function does not work when the current mode is set to Ad-Hoc, or
+ * when there is already an association procedure going on. The given BSS
+ * information is used to associate.
+ */
+static int
+mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int ret;
+
+ if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: reject infra assoc request in non-STA role\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ if (priv->wdev.current_bss) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: already connected\n", dev->name);
+ return -EALREADY;
+ }
+
+ if (adapter->surprise_removed || adapter->is_cmd_timedout) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: Ignore connection.\t"
+ "Card removed or FW in bad state\n",
+ dev->name);
+ return -EFAULT;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "info: Trying to associate to %s and bssid %pM\n",
+ (char *)sme->ssid, sme->bssid);
+
+ ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
+ priv->bss_mode, sme->channel, sme, 0);
+ if (!ret) {
+ cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0,
+ NULL, 0, WLAN_STATUS_SUCCESS,
+ GFP_KERNEL);
+ mwifiex_dbg(priv->adapter, MSG,
+ "info: associated to bssid %pM successfully\n",
+ priv->cfg_bssid);
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->auto_tdls &&
+ priv->bss_type == MWIFIEX_BSS_TYPE_STA)
+ mwifiex_setup_auto_tdls_timer(priv);
+ } else {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "info: association to bssid %pM failed\n",
+ priv->cfg_bssid);
+ eth_zero_addr(priv->cfg_bssid);
+
+ if (ret > 0)
+ cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
+ NULL, 0, NULL, 0, ret,
+ GFP_KERNEL);
+ else
+ cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
+ NULL, 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ }
+
+ return 0;
+}
+
+/*
+ * This function sets following parameters for ibss network.
+ * - channel
+ * - start band
+ * - 11n flag
+ * - secondary channel offset
+ */
+static int mwifiex_set_ibss_params(struct mwifiex_private *priv,
+ struct cfg80211_ibss_params *params)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int index = 0, i;
+ u8 config_bands = 0;
+
+ if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) {
+ if (!params->basic_rates) {
+ config_bands = BAND_B | BAND_G;
+ } else {
+ for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) {
+ /*
+ * Rates below 6 Mbps in the table are CCK
+ * rates; 802.11b and from 6 they are OFDM;
+ * 802.11G
+ */
+ if (mwifiex_rates[i].bitrate == 60) {
+ index = 1 << i;
+ break;
+ }
+ }
+
+ if (params->basic_rates < index) {
+ config_bands = BAND_B;
+ } else {
+ config_bands = BAND_G;
+ if (params->basic_rates % index)
+ config_bands |= BAND_B;
+ }
+ }
+
+ if (cfg80211_get_chandef_type(&params->chandef) !=
+ NL80211_CHAN_NO_HT)
+ config_bands |= BAND_G | BAND_GN;
+ } else {
+ if (cfg80211_get_chandef_type(&params->chandef) ==
+ NL80211_CHAN_NO_HT)
+ config_bands = BAND_A;
+ else
+ config_bands = BAND_AN | BAND_A;
+ }
+
+ if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) {
+ adapter->config_bands = config_bands;
+ adapter->adhoc_start_band = config_bands;
+
+ if ((config_bands & BAND_GN) || (config_bands & BAND_AN))
+ adapter->adhoc_11n_enabled = true;
+ else
+ adapter->adhoc_11n_enabled = false;
+ }
+
+ adapter->sec_chan_offset =
+ mwifiex_chan_type_to_sec_chan_offset(
+ cfg80211_get_chandef_type(&params->chandef));
+ priv->adhoc_channel = ieee80211_frequency_to_channel(
+ params->chandef.chan->center_freq);
+
+ mwifiex_dbg(adapter, INFO,
+ "info: set ibss band %d, chan %d, chan offset %d\n",
+ config_bands, priv->adhoc_channel,
+ adapter->sec_chan_offset);
+
+ return 0;
+}
+
+/*
+ * CFG802.11 operation handler to join an IBSS.
+ *
+ * This function does not work in any mode other than Ad-Hoc, or if
+ * a join operation is already in progress.
+ */
+static int
+mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ int ret = 0;
+
+ if (priv->bss_mode != NL80211_IFTYPE_ADHOC) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "request to join ibss received\t"
+ "when station is not in ibss mode\n");
+ goto done;
+ }
+
+ mwifiex_dbg(priv->adapter, MSG,
+ "info: trying to join to %s and bssid %pM\n",
+ (char *)params->ssid, params->bssid);
+
+ mwifiex_set_ibss_params(priv, params);
+
+ ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid,
+ params->bssid, priv->bss_mode,
+ params->chandef.chan, NULL,
+ params->privacy);
+done:
+ if (!ret) {
+ cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid,
+ params->chandef.chan, GFP_KERNEL);
+ mwifiex_dbg(priv->adapter, MSG,
+ "info: joined/created adhoc network with bssid\t"
+ "%pM successfully\n", priv->cfg_bssid);
+ } else {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "info: failed creating/joining adhoc network\n");
+ }
+
+ return ret;
+}
+
+/*
+ * CFG802.11 operation handler to leave an IBSS.
+ *
+ * This function does not work if a leave operation is
+ * already in progress.
+ */
+static int
+mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ mwifiex_dbg(priv->adapter, MSG, "info: disconnecting from essid %pM\n",
+ priv->cfg_bssid);
+ if (mwifiex_deauthenticate(priv, NULL))
+ return -EFAULT;
+
+ eth_zero_addr(priv->cfg_bssid);
+
+ return 0;
+}
+
+/*
+ * CFG802.11 operation handler for scan request.
+ *
+ * This function issues a scan request to the firmware based upon
+ * the user specified scan configuration. On successful completion,
+ * it also informs the results.
+ */
+static int
+mwifiex_cfg80211_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ struct net_device *dev = request->wdev->netdev;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ int i, offset, ret;
+ struct ieee80211_channel *chan;
+ struct ieee_types_header *ie;
+ struct mwifiex_user_scan_cfg *user_scan_cfg;
+
+ mwifiex_dbg(priv->adapter, CMD,
+ "info: received scan request on %s\n", dev->name);
+
+ /* Block scan request if scan operation or scan cleanup when interface
+ * is disabled is in process
+ */
+ if (priv->scan_request || priv->scan_aborting) {
+ mwifiex_dbg(priv->adapter, WARN,
+ "cmd: Scan already in process..\n");
+ return -EBUSY;
+ }
+
+ user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+ if (!user_scan_cfg)
+ return -ENOMEM;
+
+ priv->scan_request = request;
+
+ user_scan_cfg->num_ssids = request->n_ssids;
+ user_scan_cfg->ssid_list = request->ssids;
+
+ if (request->ie && request->ie_len) {
+ offset = 0;
+ for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
+ if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR)
+ continue;
+ priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN;
+ ie = (struct ieee_types_header *)(request->ie + offset);
+ memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len);
+ offset += sizeof(*ie) + ie->len;
+
+ if (offset >= request->ie_len)
+ break;
+ }
+ }
+
+ for (i = 0; i < min_t(u32, request->n_channels,
+ MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
+ chan = request->channels[i];
+ user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
+ user_scan_cfg->chan_list[i].radio_type = chan->band;
+
+ if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids)
+ user_scan_cfg->chan_list[i].scan_type =
+ MWIFIEX_SCAN_TYPE_PASSIVE;
+ else
+ user_scan_cfg->chan_list[i].scan_type =
+ MWIFIEX_SCAN_TYPE_ACTIVE;
+
+ user_scan_cfg->chan_list[i].scan_time = 0;
+ }
+
+ if (priv->adapter->scan_chan_gap_enabled &&
+ mwifiex_is_any_intf_active(priv))
+ user_scan_cfg->scan_chan_gap =
+ priv->adapter->scan_chan_gap_time;
+
+ ret = mwifiex_scan_networks(priv, user_scan_cfg);
+ kfree(user_scan_cfg);
+ if (ret) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "scan failed: %d\n", ret);
+ priv->scan_aborting = false;
+ priv->scan_request = NULL;
+ return ret;
+ }
+
+ if (request->ie && request->ie_len) {
+ for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
+ if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) {
+ priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR;
+ memset(&priv->vs_ie[i].ie, 0,
+ MWIFIEX_MAX_VSIE_LEN);
+ }
+ }
+ }
+ return 0;
+}
+
+static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info,
+ struct mwifiex_private *priv)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ vht_info->vht_supported = true;
+
+ vht_info->cap = adapter->hw_dot_11ac_dev_cap;
+ /* Update MCS support for VHT */
+ vht_info->vht_mcs.rx_mcs_map = cpu_to_le16(
+ adapter->hw_dot_11ac_mcs_support & 0xFFFF);
+ vht_info->vht_mcs.rx_highest = 0;
+ vht_info->vht_mcs.tx_mcs_map = cpu_to_le16(
+ adapter->hw_dot_11ac_mcs_support >> 16);
+ vht_info->vht_mcs.tx_highest = 0;
+}
+
+/*
+ * This function sets up the CFG802.11 specific HT capability fields
+ * with default values.
+ *
+ * The following default values are set -
+ * - HT Supported = True
+ * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K
+ * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE
+ * - HT Capabilities supported by firmware
+ * - MCS information, Rx mask = 0xff
+ * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01)
+ */
+static void
+mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
+ struct mwifiex_private *priv)
+{
+ int rx_mcs_supp;
+ struct ieee80211_mcs_info mcs_set;
+ u8 *mcs = (u8 *)&mcs_set;
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ ht_info->ht_supported = true;
+ ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+
+ /* Fill HT capability information */
+ if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+ if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20;
+
+ if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40;
+
+ if (adapter->user_dev_mcs_support == HT_STREAM_2X2)
+ ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ else
+ ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+
+ if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC;
+
+ if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD;
+
+ if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT;
+
+ if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING;
+
+ ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU;
+ ht_info->cap |= IEEE80211_HT_CAP_SM_PS;
+
+ rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support);
+ /* Set MCS for 1x1/2x2 */
+ memset(mcs, 0xff, rx_mcs_supp);
+ /* Clear all the other values */
+ memset(&mcs[rx_mcs_supp], 0,
+ sizeof(struct ieee80211_mcs_info) - rx_mcs_supp);
+ if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+ ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
+ /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
+ SETHT_MCS32(mcs_set.rx_mask);
+
+ memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info));
+
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+/*
+ * create a new virtual interface with the given name and name assign type
+ */
+struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_private *priv;
+ struct net_device *dev;
+ void *mdev_priv;
+
+ if (!adapter)
+ return ERR_PTR(-EFAULT);
+
+ switch (type) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ if (adapter->curr_iface_comb.sta_intf ==
+ adapter->iface_limit.sta_intf) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create multiple sta/adhoc ifaces\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ priv = mwifiex_get_unused_priv_by_bss_type(
+ adapter, MWIFIEX_BSS_TYPE_STA);
+ if (!priv) {
+ mwifiex_dbg(adapter, ERROR,
+ "could not get free private struct\n");
+ return ERR_PTR(-EFAULT);
+ }
+
+ priv->wdev.wiphy = wiphy;
+ priv->wdev.iftype = NL80211_IFTYPE_STATION;
+
+ if (type == NL80211_IFTYPE_UNSPECIFIED)
+ priv->bss_mode = NL80211_IFTYPE_STATION;
+ else
+ priv->bss_mode = type;
+
+ priv->bss_type = MWIFIEX_BSS_TYPE_STA;
+ priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
+ priv->bss_priority = 0;
+ priv->bss_role = MWIFIEX_BSS_ROLE_STA;
+
+ break;
+ case NL80211_IFTYPE_AP:
+ if (adapter->curr_iface_comb.uap_intf ==
+ adapter->iface_limit.uap_intf) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create multiple AP ifaces\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ priv = mwifiex_get_unused_priv_by_bss_type(
+ adapter, MWIFIEX_BSS_TYPE_UAP);
+ if (!priv) {
+ mwifiex_dbg(adapter, ERROR,
+ "could not get free private struct\n");
+ return ERR_PTR(-EFAULT);
+ }
+
+ priv->wdev.wiphy = wiphy;
+ priv->wdev.iftype = NL80211_IFTYPE_AP;
+
+ priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
+ priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
+ priv->bss_priority = 0;
+ priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
+ priv->bss_started = 0;
+ priv->bss_mode = type;
+
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (adapter->curr_iface_comb.p2p_intf ==
+ adapter->iface_limit.p2p_intf) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create multiple P2P ifaces\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ priv = mwifiex_get_unused_priv_by_bss_type(
+ adapter, MWIFIEX_BSS_TYPE_P2P);
+ if (!priv) {
+ mwifiex_dbg(adapter, ERROR,
+ "could not get free private struct\n");
+ return ERR_PTR(-EFAULT);
+ }
+
+ priv->wdev.wiphy = wiphy;
+ /* At start-up, wpa_supplicant tries to change the interface
+ * to NL80211_IFTYPE_STATION if it is not managed mode.
+ */
+ priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT;
+ priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT;
+
+ /* Setting bss_type to P2P tells firmware that this interface
+ * is receiving P2P peers found during find phase and doing
+ * action frame handshake.
+ */
+ priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
+
+ priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
+ priv->bss_priority = MWIFIEX_BSS_ROLE_STA;
+ priv->bss_role = MWIFIEX_BSS_ROLE_STA;
+ priv->bss_started = 0;
+
+ if (mwifiex_cfg80211_init_p2p_client(priv)) {
+ memset(&priv->wdev, 0, sizeof(priv->wdev));
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ return ERR_PTR(-EFAULT);
+ }
+
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR, "type not supported\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name,
+ name_assign_type, ether_setup,
+ IEEE80211_NUM_ACS, 1);
+ if (!dev) {
+ mwifiex_dbg(adapter, ERROR,
+ "no memory available for netdevice\n");
+ memset(&priv->wdev, 0, sizeof(priv->wdev));
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mwifiex_init_priv_params(priv, dev);
+ priv->netdev = dev;
+
+ mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv);
+ if (adapter->is_hw_11ac_capable)
+ mwifiex_setup_vht_caps(
+ &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv);
+
+ if (adapter->config_bands & BAND_A)
+ mwifiex_setup_ht_caps(
+ &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv);
+
+ if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable)
+ mwifiex_setup_vht_caps(
+ &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv);
+
+ dev_net_set(dev, wiphy_net(wiphy));
+ dev->ieee80211_ptr = &priv->wdev;
+ dev->ieee80211_ptr->iftype = priv->bss_mode;
+ memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN);
+ SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
+
+ dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+ dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT;
+ dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN;
+ dev->ethtool_ops = &mwifiex_ethtool_ops;
+
+ mdev_priv = netdev_priv(dev);
+ *((unsigned long *) mdev_priv) = (unsigned long) priv;
+
+ SET_NETDEV_DEV(dev, adapter->dev);
+
+ /* Register network device */
+ if (register_netdevice(dev)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot register virtual network device\n");
+ free_netdev(dev);
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->netdev = NULL;
+ memset(&priv->wdev, 0, sizeof(priv->wdev));
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ return ERR_PTR(-EFAULT);
+ }
+
+ priv->dfs_cac_workqueue = alloc_workqueue("MWIFIEX_DFS_CAC%s",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM |
+ WQ_UNBOUND, 1, name);
+ if (!priv->dfs_cac_workqueue) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot register virtual network device\n");
+ free_netdev(dev);
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->netdev = NULL;
+ memset(&priv->wdev, 0, sizeof(priv->wdev));
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue);
+
+ priv->dfs_chan_sw_workqueue = alloc_workqueue("MWIFIEX_DFS_CHSW%s",
+ WQ_HIGHPRI | WQ_UNBOUND |
+ WQ_MEM_RECLAIM, 1, name);
+ if (!priv->dfs_chan_sw_workqueue) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot register virtual network device\n");
+ free_netdev(dev);
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->netdev = NULL;
+ memset(&priv->wdev, 0, sizeof(priv->wdev));
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ INIT_DELAYED_WORK(&priv->dfs_chan_sw_work,
+ mwifiex_dfs_chan_sw_work_queue);
+
+ sema_init(&priv->async_sem, 1);
+
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: Marvell 802.11 Adapter\n", dev->name);
+
+#ifdef CONFIG_DEBUG_FS
+ mwifiex_dev_debugfs_init(priv);
+#endif
+
+ switch (type) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ adapter->curr_iface_comb.sta_intf++;
+ break;
+ case NL80211_IFTYPE_AP:
+ adapter->curr_iface_comb.uap_intf++;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ adapter->curr_iface_comb.p2p_intf++;
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR, "type not supported\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &priv->wdev;
+}
+EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf);
+
+/*
+ * del_virtual_intf: remove the virtual interface determined by dev
+ */
+int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct sk_buff *skb, *tmp;
+
+#ifdef CONFIG_DEBUG_FS
+ mwifiex_dev_debugfs_remove(priv);
+#endif
+
+ mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+
+ skb_queue_walk_safe(&priv->bypass_txq, skb, tmp)
+ mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+
+ if (wdev->netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdevice(wdev->netdev);
+
+ if (priv->dfs_cac_workqueue) {
+ flush_workqueue(priv->dfs_cac_workqueue);
+ destroy_workqueue(priv->dfs_cac_workqueue);
+ priv->dfs_cac_workqueue = NULL;
+ }
+
+ if (priv->dfs_chan_sw_workqueue) {
+ flush_workqueue(priv->dfs_chan_sw_workqueue);
+ destroy_workqueue(priv->dfs_chan_sw_workqueue);
+ priv->dfs_chan_sw_workqueue = NULL;
+ }
+ /* Clear the priv in adapter */
+ priv->netdev->ieee80211_ptr = NULL;
+ priv->netdev = NULL;
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+
+ priv->media_connected = false;
+
+ switch (priv->bss_mode) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ adapter->curr_iface_comb.sta_intf--;
+ break;
+ case NL80211_IFTYPE_AP:
+ adapter->curr_iface_comb.uap_intf--;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ adapter->curr_iface_comb.p2p_intf--;
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR,
+ "del_virtual_intf: type not supported\n");
+ break;
+ }
+
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+ GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
+ kfree(priv->hist_data);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
+
+static bool
+mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq,
+ u8 max_byte_seq)
+{
+ int j, k, valid_byte_cnt = 0;
+ bool dont_care_byte = false;
+
+ for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) {
+ for (k = 0; k < 8; k++) {
+ if (pat->mask[j] & 1 << k) {
+ memcpy(byte_seq + valid_byte_cnt,
+ &pat->pattern[j * 8 + k], 1);
+ valid_byte_cnt++;
+ if (dont_care_byte)
+ return false;
+ } else {
+ if (valid_byte_cnt)
+ dont_care_byte = true;
+ }
+
+ /* wildcard bytes record as the offset
+ * before the valid byte
+ */
+ if (!valid_byte_cnt && !dont_care_byte)
+ pat->pkt_offset++;
+
+ if (valid_byte_cnt > max_byte_seq)
+ return false;
+ }
+ }
+
+ byte_seq[max_byte_seq] = valid_byte_cnt;
+
+ return true;
+}
+
+#ifdef CONFIG_PM
+static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv,
+ struct mwifiex_mef_entry *mef_entry)
+{
+ int i, filt_num = 0, num_ipv4 = 0;
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR];
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ mef_entry->mode = MEF_MODE_HOST_SLEEP;
+ mef_entry->action = MEF_ACTION_AUTO_ARP;
+
+ /* Enable ARP offload feature */
+ memset(ips, 0, sizeof(ips));
+ for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
+ if (adapter->priv[i]->netdev) {
+ in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev);
+ if (!in_dev)
+ continue;
+ ifa = in_dev->ifa_list;
+ if (!ifa || !ifa->ifa_local)
+ continue;
+ ips[i] = ifa->ifa_local;
+ num_ipv4++;
+ }
+ }
+
+ for (i = 0; i < num_ipv4; i++) {
+ if (!ips[i])
+ continue;
+ mef_entry->filter[filt_num].repeat = 1;
+ memcpy(mef_entry->filter[filt_num].byte_seq,
+ (u8 *)&ips[i], sizeof(ips[i]));
+ mef_entry->filter[filt_num].
+ byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+ sizeof(ips[i]);
+ mef_entry->filter[filt_num].offset = 46;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ if (filt_num) {
+ mef_entry->filter[filt_num].filt_action =
+ TYPE_OR;
+ }
+ filt_num++;
+ }
+
+ mef_entry->filter[filt_num].repeat = 1;
+ mef_entry->filter[filt_num].byte_seq[0] = 0x08;
+ mef_entry->filter[filt_num].byte_seq[1] = 0x06;
+ mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2;
+ mef_entry->filter[filt_num].offset = 20;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ mef_entry->filter[filt_num].filt_action = TYPE_AND;
+}
+
+static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv,
+ struct mwifiex_ds_mef_cfg *mef_cfg,
+ struct mwifiex_mef_entry *mef_entry,
+ struct cfg80211_wowlan *wowlan)
+{
+ int i, filt_num = 0, ret = 0;
+ bool first_pat = true;
+ u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
+ const u8 ipv4_mc_mac[] = {0x33, 0x33};
+ const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+
+ mef_entry->mode = MEF_MODE_HOST_SLEEP;
+ mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
+
+ for (i = 0; i < wowlan->n_patterns; i++) {
+ memset(byte_seq, 0, sizeof(byte_seq));
+ if (!mwifiex_is_pattern_supported(&wowlan->patterns[i],
+ byte_seq,
+ MWIFIEX_MEF_MAX_BYTESEQ)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Pattern not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!wowlan->patterns[i].pkt_offset) {
+ if (!(byte_seq[0] & 0x01) &&
+ (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) {
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
+ continue;
+ } else if (is_broadcast_ether_addr(byte_seq)) {
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST;
+ continue;
+ } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+ (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) ||
+ (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+ (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) {
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST;
+ continue;
+ }
+ }
+ mef_entry->filter[filt_num].repeat = 1;
+ mef_entry->filter[filt_num].offset =
+ wowlan->patterns[i].pkt_offset;
+ memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq,
+ sizeof(byte_seq));
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+
+ if (first_pat)
+ first_pat = false;
+ else
+ mef_entry->filter[filt_num].filt_action = TYPE_AND;
+
+ filt_num++;
+ }
+
+ if (wowlan->magic_pkt) {
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
+ mef_entry->filter[filt_num].repeat = 16;
+ memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+ ETH_ALEN);
+ mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+ ETH_ALEN;
+ mef_entry->filter[filt_num].offset = 28;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ if (filt_num)
+ mef_entry->filter[filt_num].filt_action = TYPE_OR;
+
+ filt_num++;
+ mef_entry->filter[filt_num].repeat = 16;
+ memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+ ETH_ALEN);
+ mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+ ETH_ALEN;
+ mef_entry->filter[filt_num].offset = 56;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ mef_entry->filter[filt_num].filt_action = TYPE_OR;
+ }
+ return ret;
+}
+
+static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
+ struct cfg80211_wowlan *wowlan)
+{
+ int ret = 0, num_entries = 1;
+ struct mwifiex_ds_mef_cfg mef_cfg;
+ struct mwifiex_mef_entry *mef_entry;
+
+ if (wowlan->n_patterns || wowlan->magic_pkt)
+ num_entries++;
+
+ mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL);
+ if (!mef_entry)
+ return -ENOMEM;
+
+ memset(&mef_cfg, 0, sizeof(mef_cfg));
+ mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST |
+ MWIFIEX_CRITERIA_UNICAST;
+ mef_cfg.num_entries = num_entries;
+ mef_cfg.mef_entry = mef_entry;
+
+ mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]);
+
+ if (wowlan->n_patterns || wowlan->magic_pkt) {
+ ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg,
+ &mef_entry[1], wowlan);
+ if (ret)
+ goto err;
+ }
+
+ if (!mef_cfg.criteria)
+ mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
+ MWIFIEX_CRITERIA_UNICAST |
+ MWIFIEX_CRITERIA_MULTICAST;
+
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
+ HostCmd_ACT_GEN_SET, 0,
+ &mef_cfg, true);
+
+err:
+ kfree(mef_entry);
+ return ret;
+}
+
+static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_ds_hs_cfg hs_cfg;
+ int i, ret = 0;
+ struct mwifiex_private *priv;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ mwifiex_abort_cac(priv);
+ }
+
+ mwifiex_cancel_all_pending_cmd(adapter);
+
+ if (!wowlan) {
+ mwifiex_dbg(adapter, ERROR,
+ "None of the WOWLAN triggers enabled\n");
+ return 0;
+ }
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+ if (!priv->media_connected) {
+ mwifiex_dbg(adapter, ERROR,
+ "Can not configure WOWLAN in disconnected state\n");
+ return 0;
+ }
+
+ ret = mwifiex_set_mef_filter(priv, wowlan);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "Failed to set MEF filter\n");
+ return ret;
+ }
+
+ if (wowlan->disconnect) {
+ memset(&hs_cfg, 0, sizeof(hs_cfg));
+ hs_cfg.is_invoke_hostcmd = false;
+ hs_cfg.conditions = HS_CFG_COND_MAC_EVENT;
+ hs_cfg.gpio = adapter->hs_cfg.gpio;
+ hs_cfg.gap = adapter->hs_cfg.gap;
+ ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
+ MWIFIEX_SYNC_CMD, &hs_cfg);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to set HS params\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int mwifiex_cfg80211_resume(struct wiphy *wiphy)
+{
+ return 0;
+}
+
+static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy,
+ bool enabled)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+
+ device_set_wakeup_enable(adapter->dev, enabled);
+}
+#endif
+
+static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq)
+{
+ const u8 ipv4_mc_mac[] = {0x33, 0x33};
+ const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+ const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff};
+
+ if ((byte_seq[0] & 0x01) &&
+ (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1))
+ return PACKET_TYPE_UNICAST;
+ else if (!memcmp(byte_seq, bc_mac, 4))
+ return PACKET_TYPE_BROADCAST;
+ else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+ byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) ||
+ (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+ byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3))
+ return PACKET_TYPE_MULTICAST;
+
+ return 0;
+}
+
+static int
+mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv,
+ struct cfg80211_coalesce_rules *crule,
+ struct mwifiex_coalesce_rule *mrule)
+{
+ u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1];
+ struct filt_field_param *param;
+ int i;
+
+ mrule->max_coalescing_delay = crule->delay;
+
+ param = mrule->params;
+
+ for (i = 0; i < crule->n_patterns; i++) {
+ memset(byte_seq, 0, sizeof(byte_seq));
+ if (!mwifiex_is_pattern_supported(&crule->patterns[i],
+ byte_seq,
+ MWIFIEX_COALESCE_MAX_BYTESEQ)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Pattern not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!crule->patterns[i].pkt_offset) {
+ u8 pkt_type;
+
+ pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq);
+ if (pkt_type && mrule->pkt_type) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Multiple packet types not allowed\n");
+ return -EOPNOTSUPP;
+ } else if (pkt_type) {
+ mrule->pkt_type = pkt_type;
+ continue;
+ }
+ }
+
+ if (crule->condition == NL80211_COALESCE_CONDITION_MATCH)
+ param->operation = RECV_FILTER_MATCH_TYPE_EQ;
+ else
+ param->operation = RECV_FILTER_MATCH_TYPE_NE;
+
+ param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ];
+ memcpy(param->operand_byte_stream, byte_seq,
+ param->operand_len);
+ param->offset = crule->patterns[i].pkt_offset;
+ param++;
+
+ mrule->num_of_fields++;
+ }
+
+ if (!mrule->pkt_type) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Packet type can not be determined\n");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
+ struct cfg80211_coalesce *coalesce)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ int i, ret;
+ struct mwifiex_ds_coalesce_cfg coalesce_cfg;
+ struct mwifiex_private *priv =
+ mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+ memset(&coalesce_cfg, 0, sizeof(coalesce_cfg));
+ if (!coalesce) {
+ mwifiex_dbg(adapter, WARN,
+ "Disable coalesce and reset all previous rules\n");
+ return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG,
+ HostCmd_ACT_GEN_SET, 0,
+ &coalesce_cfg, true);
+ }
+
+ coalesce_cfg.num_of_rules = coalesce->n_rules;
+ for (i = 0; i < coalesce->n_rules; i++) {
+ ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i],
+ &coalesce_cfg.rule[i]);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "Recheck the patterns provided for rule %d\n",
+ i + 1);
+ return ret;
+ }
+ }
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true);
+}
+
+/* cfg80211 ops handler for tdls_mgmt.
+ * Function prepares TDLS action frame packets and forwards them to FW
+ */
+static int
+mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ int ret;
+
+ if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+ return -ENOTSUPP;
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ mwifiex_dbg(priv->adapter, MSG,
+ "Send TDLS Setup Request to %pM status_code=%d\n",
+ peer, status_code);
+ mwifiex_add_auto_tdls_peer(priv, peer);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_SETUP_RESPONSE:
+ mwifiex_add_auto_tdls_peer(priv, peer);
+ mwifiex_dbg(priv->adapter, MSG,
+ "Send TDLS Setup Response to %pM status_code=%d\n",
+ peer, status_code);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ mwifiex_dbg(priv->adapter, MSG,
+ "Send TDLS Confirm to %pM status_code=%d\n", peer,
+ status_code);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_TEARDOWN:
+ mwifiex_dbg(priv->adapter, MSG,
+ "Send TDLS Tear down to %pM\n", peer);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ mwifiex_dbg(priv->adapter, MSG,
+ "Send TDLS Discovery Request to %pM\n", peer);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ mwifiex_dbg(priv->adapter, MSG,
+ "Send TDLS Discovery Response to %pM\n", peer);
+ ret = mwifiex_send_tdls_action_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Unknown TDLS mgmt/action frame %pM\n", peer);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, enum nl80211_tdls_operation action)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
+ !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+ return -ENOTSUPP;
+
+ mwifiex_dbg(priv->adapter, MSG,
+ "TDLS peer=%pM, oper=%d\n", peer, action);
+
+ switch (action) {
+ case NL80211_TDLS_ENABLE_LINK:
+ action = MWIFIEX_TDLS_ENABLE_LINK;
+ break;
+ case NL80211_TDLS_DISABLE_LINK:
+ action = MWIFIEX_TDLS_DISABLE_LINK;
+ break;
+ case NL80211_TDLS_TEARDOWN:
+ /* shouldn't happen!*/
+ mwifiex_dbg(priv->adapter, ERROR,
+ "tdls_oper: teardown from driver not supported\n");
+ return -EINVAL;
+ case NL80211_TDLS_SETUP:
+ /* shouldn't happen!*/
+ mwifiex_dbg(priv->adapter, ERROR,
+ "tdls_oper: setup from driver not supported\n");
+ return -EINVAL;
+ case NL80211_TDLS_DISCOVERY_REQ:
+ /* shouldn't happen!*/
+ mwifiex_dbg(priv->adapter, ERROR,
+ "tdls_oper: discovery from driver not supported\n");
+ return -EINVAL;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "tdls_oper: operation not supported\n");
+ return -ENOTSUPP;
+ }
+
+ return mwifiex_tdls_oper(priv, peer, action);
+}
+
+static int
+mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ unsigned long flags;
+ u16 chan;
+ u8 second_chan_offset, band;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_ptr = mwifiex_get_sta_entry(priv, addr);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (!sta_ptr) {
+ wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n",
+ __func__, addr);
+ return -ENOENT;
+ }
+
+ if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] &
+ WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) {
+ wiphy_err(wiphy, "%pM do not support tdls cs\n", addr);
+ return -ENOENT;
+ }
+
+ if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING ||
+ sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) {
+ wiphy_err(wiphy, "channel switch is running, abort request\n");
+ return -EALREADY;
+ }
+
+ chan = chandef->chan->hw_value;
+ second_chan_offset = mwifiex_get_sec_chan_offset(chan);
+ band = chandef->chan->band;
+ mwifiex_start_tdls_cs(priv, addr, chan, second_chan_offset, band);
+
+ return 0;
+}
+
+static void
+mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ unsigned long flags;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_ptr = mwifiex_get_sta_entry(priv, addr);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (!sta_ptr) {
+ wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n",
+ __func__, addr);
+ } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING ||
+ sta_ptr->tdls_status == TDLS_IN_BASE_CHAN ||
+ sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) {
+ wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n",
+ addr);
+ } else
+ mwifiex_stop_tdls_cs(priv, addr);
+}
+
+static int
+mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_parameters *params)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
+ return -ENOTSUPP;
+
+ return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK);
+}
+
+static int
+mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct ieee_types_header *chsw_ie;
+ struct ieee80211_channel_sw_ie *channel_sw;
+ int chsw_msec;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (priv->adapter->scan_processing) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "radar detection: scan in process...\n");
+ return -EBUSY;
+ }
+
+ if (priv->wdev.cac_started)
+ return -EBUSY;
+
+ if (cfg80211_chandef_identical(&params->chandef,
+ &priv->dfs_chandef))
+ return -EINVAL;
+
+ chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH,
+ params->beacon_csa.tail,
+ params->beacon_csa.tail_len);
+ if (!chsw_ie) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Could not parse channel switch announcement IE\n");
+ return -EINVAL;
+ }
+
+ channel_sw = (void *)(chsw_ie + 1);
+ if (channel_sw->mode) {
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+ mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
+ }
+
+ if (mwifiex_del_mgmt_ies(priv))
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to delete mgmt IEs!\n");
+
+ if (mwifiex_set_mgmt_ies(priv, &params->beacon_csa)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: setting mgmt ies failed\n", __func__);
+ return -EFAULT;
+ }
+
+ memcpy(&priv->dfs_chandef, &params->chandef, sizeof(priv->dfs_chandef));
+ memcpy(&priv->beacon_after, &params->beacon_after,
+ sizeof(priv->beacon_after));
+
+ chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100);
+ queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work,
+ msecs_to_jiffies(chsw_msec));
+ return 0;
+}
+
+static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+ struct mwifiex_bssdescriptor *curr_bss;
+ struct ieee80211_channel *chan;
+ u8 second_chan_offset;
+ enum nl80211_channel_type chan_type;
+ enum ieee80211_band band;
+ int freq;
+ int ret = -ENODATA;
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP &&
+ cfg80211_chandef_valid(&priv->bss_chandef)) {
+ *chandef = priv->bss_chandef;
+ ret = 0;
+ } else if (priv->media_connected) {
+ curr_bss = &priv->curr_bss_params.bss_descriptor;
+ band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+ freq = ieee80211_channel_to_frequency(curr_bss->channel, band);
+ chan = ieee80211_get_channel(wiphy, freq);
+
+ if (curr_bss->bcn_ht_oper) {
+ second_chan_offset = curr_bss->bcn_ht_oper->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
+ chan_type = mwifiex_sec_chan_offset_to_chan_type
+ (second_chan_offset);
+ cfg80211_chandef_create(chandef, chan, chan_type);
+ } else {
+ cfg80211_chandef_create(chandef, chan,
+ NL80211_CHAN_NO_HT);
+ }
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_radar_params radar_params;
+
+ if (priv->adapter->scan_processing) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "radar detection: scan already in process...\n");
+ return -EBUSY;
+ }
+
+ if (!mwifiex_is_11h_active(priv)) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "Enable 11h extensions in FW\n");
+ if (mwifiex_11h_activate(priv, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to activate 11h extensions!!");
+ return -1;
+ }
+ priv->state_11h.is_11h_active = true;
+ }
+
+ memset(&radar_params, 0, sizeof(struct mwifiex_radar_params));
+ radar_params.chandef = chandef;
+ radar_params.cac_time_ms = cac_time_ms;
+
+ memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef));
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
+ HostCmd_ACT_GEN_SET, 0, &radar_params, true))
+ return -1;
+
+ queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work,
+ msecs_to_jiffies(cac_time_ms));
+ return 0;
+}
+
+static int
+mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac,
+ struct station_parameters *params)
+{
+ int ret;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ /* we support change_station handler only for TDLS peers*/
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
+ return -ENOTSUPP;
+
+ priv->sta_params = params;
+
+ ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK);
+ priv->sta_params = NULL;
+
+ return ret;
+}
+
+/* station cfg80211 operations */
+static struct cfg80211_ops mwifiex_cfg80211_ops = {
+ .add_virtual_intf = mwifiex_add_virtual_intf,
+ .del_virtual_intf = mwifiex_del_virtual_intf,
+ .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf,
+ .scan = mwifiex_cfg80211_scan,
+ .connect = mwifiex_cfg80211_connect,
+ .disconnect = mwifiex_cfg80211_disconnect,
+ .get_station = mwifiex_cfg80211_get_station,
+ .dump_station = mwifiex_cfg80211_dump_station,
+ .dump_survey = mwifiex_cfg80211_dump_survey,
+ .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params,
+ .join_ibss = mwifiex_cfg80211_join_ibss,
+ .leave_ibss = mwifiex_cfg80211_leave_ibss,
+ .add_key = mwifiex_cfg80211_add_key,
+ .del_key = mwifiex_cfg80211_del_key,
+ .mgmt_tx = mwifiex_cfg80211_mgmt_tx,
+ .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register,
+ .remain_on_channel = mwifiex_cfg80211_remain_on_channel,
+ .cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel,
+ .set_default_key = mwifiex_cfg80211_set_default_key,
+ .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt,
+ .set_tx_power = mwifiex_cfg80211_set_tx_power,
+ .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask,
+ .start_ap = mwifiex_cfg80211_start_ap,
+ .stop_ap = mwifiex_cfg80211_stop_ap,
+ .change_beacon = mwifiex_cfg80211_change_beacon,
+ .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
+ .set_antenna = mwifiex_cfg80211_set_antenna,
+ .del_station = mwifiex_cfg80211_del_station,
+#ifdef CONFIG_PM
+ .suspend = mwifiex_cfg80211_suspend,
+ .resume = mwifiex_cfg80211_resume,
+ .set_wakeup = mwifiex_cfg80211_set_wakeup,
+#endif
+ .set_coalesce = mwifiex_cfg80211_set_coalesce,
+ .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt,
+ .tdls_oper = mwifiex_cfg80211_tdls_oper,
+ .tdls_channel_switch = mwifiex_cfg80211_tdls_chan_switch,
+ .tdls_cancel_channel_switch = mwifiex_cfg80211_tdls_cancel_chan_switch,
+ .add_station = mwifiex_cfg80211_add_station,
+ .change_station = mwifiex_cfg80211_change_station,
+ .get_channel = mwifiex_cfg80211_get_channel,
+ .start_radar_detection = mwifiex_cfg80211_start_radar_detection,
+ .channel_switch = mwifiex_cfg80211_channel_switch,
+};
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
+ .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+ .n_patterns = MWIFIEX_MEF_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;
+}
+
+static const struct wiphy_coalesce_support mwifiex_coalesce_support = {
+ .n_rules = MWIFIEX_COALESCE_MAX_RULES,
+ .max_delay = MWIFIEX_MAX_COALESCING_DELAY,
+ .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+ .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+
+int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter)
+{
+ u32 n_channels_bg, n_channels_a = 0;
+
+ n_channels_bg = mwifiex_band_2ghz.n_channels;
+
+ if (adapter->config_bands & BAND_A)
+ n_channels_a = mwifiex_band_5ghz.n_channels;
+
+ adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a);
+ adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) *
+ adapter->num_in_chan_stats);
+
+ if (!adapter->chan_stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * This function registers the device with CFG802.11 subsystem.
+ *
+ * The function creates the wireless device/wiphy, populates it with
+ * default parameters and handler function pointers, and finally
+ * registers the device.
+ */
+
+int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ void *wdev_priv;
+ struct wiphy *wiphy;
+ struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA];
+ u8 *country_code;
+ u32 thr, retry;
+
+ /* create a new wiphy for use with cfg80211 */
+ wiphy = wiphy_new(&mwifiex_cfg80211_ops,
+ sizeof(struct mwifiex_adapter *));
+ if (!wiphy) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: creating new wiphy\n", __func__);
+ return -ENOMEM;
+ }
+ wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH;
+ wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN;
+ wiphy->mgmt_stypes = mwifiex_mgmt_stypes;
+ wiphy->max_remain_on_channel_duration = 5000;
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_AP);
+
+ wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz;
+ if (adapter->config_bands & BAND_A)
+ wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz;
+ else
+ wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+ if (adapter->drcs_enabled && ISSUPP_DRCS_ENABLED(adapter->fw_cap_info))
+ wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_drcs;
+ else if (adapter->is_hw_11ac_capable)
+ wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_vht;
+ else
+ wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta;
+ wiphy->n_iface_combinations = 1;
+
+ /* Initialize cipher suits */
+ wiphy->cipher_suites = mwifiex_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
+
+ if (adapter->region_code)
+ wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
+ REGULATORY_COUNTRY_IE_IGNORE;
+
+ ether_addr_copy(wiphy->perm_addr, adapter->perm_addr);
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
+ WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
+ WIPHY_FLAG_AP_UAPSD |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+ WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
+ WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+
+#ifdef CONFIG_PM
+ wiphy->wowlan = &mwifiex_wowlan_support;
+#endif
+
+ wiphy->coalesce = &mwifiex_coalesce_support;
+
+ wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
+
+ wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1;
+ wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1;
+
+ wiphy->features |= NL80211_FEATURE_HT_IBSS |
+ NL80211_FEATURE_INACTIVITY_TIMER |
+ NL80211_FEATURE_NEED_OBSS_SCAN;
+
+ if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
+ wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+ if (adapter->fw_api_ver == MWIFIEX_FW_V15)
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+
+ /* Reserve space for mwifiex specific private data for BSS */
+ wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
+
+ wiphy->reg_notifier = mwifiex_reg_notifier;
+
+ /* Set struct mwifiex_adapter pointer in wiphy_priv */
+ wdev_priv = wiphy_priv(wiphy);
+ *(unsigned long *)wdev_priv = (unsigned long)adapter;
+
+ set_wiphy_dev(wiphy, priv->adapter->dev);
+
+ ret = wiphy_register(wiphy);
+ if (ret < 0) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: wiphy_register failed: %d\n", __func__, ret);
+ wiphy_free(wiphy);
+ return ret;
+ }
+
+ if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
+ mwifiex_dbg(adapter, INFO,
+ "driver hint alpha2: %2.2s\n", reg_alpha2);
+ regulatory_hint(wiphy, reg_alpha2);
+ } else {
+ if (adapter->region_code == 0x00) {
+ mwifiex_dbg(adapter, WARN, "Ignore world regulatory domain\n");
+ } else {
+ country_code =
+ mwifiex_11d_code_2_region(adapter->region_code);
+ if (country_code &&
+ regulatory_hint(wiphy, country_code))
+ mwifiex_dbg(priv->adapter, ERROR, "regulatory_hint() failed\n");
+ }
+ }
+
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true);
+ wiphy->frag_threshold = thr;
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true);
+ wiphy->rts_threshold = thr;
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true);
+ wiphy->retry_short = (u8) retry;
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true);
+ wiphy->retry_long = (u8) retry;
+
+ adapter->wiphy = wiphy;
+ return ret;
+}
diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/marvell/mwifiex/cfg80211.h
index 908367857d58..908367857d58 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.h
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.h
diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c
new file mode 100644
index 000000000000..09fae27140f7
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/cfp.c
@@ -0,0 +1,537 @@
+/*
+ * Marvell Wireless LAN device driver: Channel, Frequence and Power
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cfg80211.h"
+
+/* 100mW */
+#define MWIFIEX_TX_PWR_DEFAULT 20
+/* 100mW */
+#define MWIFIEX_TX_PWR_US_DEFAULT 20
+/* 50mW */
+#define MWIFIEX_TX_PWR_JP_DEFAULT 16
+/* 100mW */
+#define MWIFIEX_TX_PWR_FR_100MW 20
+/* 10mW */
+#define MWIFIEX_TX_PWR_FR_10MW 10
+/* 100mW */
+#define MWIFIEX_TX_PWR_EMEA_DEFAULT 20
+
+static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 };
+
+static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24,
+ 0xb0, 0x48, 0x60, 0x6c, 0 };
+
+static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96,
+ 0x0c, 0x12, 0x18, 0x24,
+ 0x30, 0x48, 0x60, 0x6c, 0 };
+
+static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24,
+ 0xb0, 0x48, 0x60, 0x6c, 0 };
+static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+ 0xb0, 0x48, 0x60, 0x6c, 0 };
+static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04,
+ 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18,
+ 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90,
+ 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68,
+ 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51,
+ 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 };
+
+static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
+
+static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+ 0x30, 0x48, 0x60, 0x6c, 0 };
+
+static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
+ 0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
+ 0x60, 0x6c, 0 };
+
+u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x00, 0x10, 0x20, 0x30,
+ 0x31, 0x32, 0x40, 0x41, 0x50 };
+
+static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
+
+/* For every mcs_rate line, the first 8 bytes are for stream 1x1,
+ * and all 16 bytes are for stream 2x2.
+ */
+static const u16 mcs_rate[4][16] = {
+ /* LGI 40M */
+ { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
+ 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
+
+ /* SGI 40M */
+ { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
+ 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
+
+ /* LGI 20M */
+ { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
+ 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
+
+ /* SGI 20M */
+ { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
+ 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
+};
+
+/* AC rates */
+static const u16 ac_mcs_rate_nss1[8][10] = {
+ /* LG 160M */
+ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+ 0x492, 0x57C, 0x618 },
+
+ /* SG 160M */
+ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+ 0x514, 0x618, 0x6C6 },
+
+ /* LG 80M */
+ { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
+ 0x249, 0x2BE, 0x30C },
+
+ /* SG 80M */
+ { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
+ 0x28A, 0x30C, 0x363 },
+
+ /* LG 40M */
+ { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
+ 0x10E, 0x144, 0x168 },
+
+ /* SG 40M */
+ { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
+ 0x12C, 0x168, 0x190 },
+
+ /* LG 20M */
+ { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
+
+ /* SG 20M */
+ { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
+};
+
+/* NSS2 note: the value in the table is 2 multiplier of the actual rate */
+static const u16 ac_mcs_rate_nss2[8][10] = {
+ /* LG 160M */
+ { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
+ 0x924, 0xAF8, 0xC30 },
+
+ /* SG 160M */
+ { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
+ 0xA28, 0xC30, 0xD8B },
+
+ /* LG 80M */
+ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+ 0x492, 0x57C, 0x618 },
+
+ /* SG 80M */
+ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+ 0x514, 0x618, 0x6C6 },
+
+ /* LG 40M */
+ { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
+ 0x21C, 0x288, 0x2D0 },
+
+ /* SG 40M */
+ { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
+ 0x258, 0x2D0, 0x320 },
+
+ /* LG 20M */
+ { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
+ 0x138, 0x00 },
+
+ /* SG 20M */
+ { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
+ 0x15B, 0x00 },
+};
+
+struct region_code_mapping {
+ u8 code;
+ u8 region[IEEE80211_COUNTRY_STRING_LEN];
+};
+
+static struct region_code_mapping region_code_mapping_t[] = {
+ { 0x10, "US " }, /* US FCC */
+ { 0x20, "CA " }, /* IC Canada */
+ { 0x30, "FR " }, /* France */
+ { 0x31, "ES " }, /* Spain */
+ { 0x32, "FR " }, /* France */
+ { 0x40, "JP " }, /* Japan */
+ { 0x41, "JP " }, /* Japan */
+ { 0x50, "CN " }, /* China */
+};
+
+/* This function converts integer code to region string */
+u8 *mwifiex_11d_code_2_region(u8 code)
+{
+ u8 i;
+ u8 size = sizeof(region_code_mapping_t)/
+ sizeof(struct region_code_mapping);
+
+ /* Look for code in mapping table */
+ for (i = 0; i < size; i++)
+ if (region_code_mapping_t[i].code == code)
+ return region_code_mapping_t[i].region;
+
+ return NULL;
+}
+
+/*
+ * This function maps an index in supported rates table into
+ * the corresponding data rate.
+ */
+u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
+ u8 index, u8 ht_info)
+{
+ u32 rate = 0;
+ u8 mcs_index = 0;
+ u8 bw = 0;
+ u8 gi = 0;
+
+ if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) {
+ mcs_index = min(index & 0xF, 9);
+
+ /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
+ bw = (ht_info & 0xC) >> 2;
+
+ /* LGI: gi =0, SGI: gi = 1 */
+ gi = (ht_info & 0x10) >> 4;
+
+ if ((index >> 4) == 1) /* NSS = 2 */
+ rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index];
+ else /* NSS = 1 */
+ rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index];
+ } else if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) {
+ /* 20M: bw=0, 40M: bw=1 */
+ bw = (ht_info & 0xC) >> 2;
+
+ /* LGI: gi =0, SGI: gi = 1 */
+ gi = (ht_info & 0x10) >> 4;
+
+ if (index == MWIFIEX_RATE_BITMAP_MCS0) {
+ if (gi == 1)
+ rate = 0x0D; /* MCS 32 SGI rate */
+ else
+ rate = 0x0C; /* MCS 32 LGI rate */
+ } else if (index < 16) {
+ if ((bw == 1) || (bw == 0))
+ rate = mcs_rate[2 * (1 - bw) + gi][index];
+ else
+ rate = mwifiex_data_rates[0];
+ } else {
+ rate = mwifiex_data_rates[0];
+ }
+ } else {
+ /* 11n non-HT rates */
+ if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
+ index = 0;
+ rate = mwifiex_data_rates[index];
+ }
+
+ return rate;
+}
+
+/* This function maps an index in supported rates table into
+ * the corresponding data rate.
+ */
+u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
+ u8 index, u8 ht_info)
+{
+ u32 mcs_num_supp =
+ (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
+ u32 rate;
+
+ if (priv->adapter->is_hw_11ac_capable)
+ return mwifiex_index_to_acs_data_rate(priv, index, ht_info);
+
+ if (ht_info & BIT(0)) {
+ if (index == MWIFIEX_RATE_BITMAP_MCS0) {
+ if (ht_info & BIT(2))
+ rate = 0x0D; /* MCS 32 SGI rate */
+ else
+ rate = 0x0C; /* MCS 32 LGI rate */
+ } else if (index < mcs_num_supp) {
+ if (ht_info & BIT(1)) {
+ if (ht_info & BIT(2))
+ /* SGI, 40M */
+ rate = mcs_rate[1][index];
+ else
+ /* LGI, 40M */
+ rate = mcs_rate[0][index];
+ } else {
+ if (ht_info & BIT(2))
+ /* SGI, 20M */
+ rate = mcs_rate[3][index];
+ else
+ /* LGI, 20M */
+ rate = mcs_rate[2][index];
+ }
+ } else
+ rate = mwifiex_data_rates[0];
+ } else {
+ if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
+ index = 0;
+ rate = mwifiex_data_rates[index];
+ }
+ return rate;
+}
+
+/*
+ * This function returns the current active data rates.
+ *
+ * The result may vary depending upon connection status.
+ */
+u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates)
+{
+ if (!priv->media_connected)
+ return mwifiex_get_supported_rates(priv, rates);
+ else
+ return mwifiex_copy_rates(rates, 0,
+ priv->curr_bss_params.data_rates,
+ priv->curr_bss_params.num_of_rates);
+}
+
+/*
+ * This function locates the Channel-Frequency-Power triplet based upon
+ * band and channel/frequency parameters.
+ */
+struct mwifiex_chan_freq_power *
+mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq)
+{
+ struct mwifiex_chan_freq_power *cfp = NULL;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch = NULL;
+ int i;
+
+ if (!channel && !freq)
+ return cfp;
+
+ if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG)
+ sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ];
+ else
+ sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ if (!sband) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: cannot find cfp by band %d\n",
+ __func__, band);
+ return cfp;
+ }
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (freq) {
+ if (ch->center_freq == freq)
+ break;
+ } else {
+ /* find by valid channel*/
+ if (ch->hw_value == channel ||
+ channel == FIRST_VALID_CHANNEL)
+ break;
+ }
+ }
+ if (i == sband->n_channels) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: cannot find cfp by band %d\t"
+ "& channel=%d freq=%d\n",
+ __func__, band, channel, freq);
+ } else {
+ if (!ch)
+ return cfp;
+
+ priv->cfp.channel = ch->hw_value;
+ priv->cfp.freq = ch->center_freq;
+ priv->cfp.max_tx_power = ch->max_power;
+ cfp = &priv->cfp;
+ }
+
+ return cfp;
+}
+
+/*
+ * This function checks if the data rate is set to auto.
+ */
+u8
+mwifiex_is_rate_auto(struct mwifiex_private *priv)
+{
+ u32 i;
+ int rate_num = 0;
+
+ for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++)
+ if (priv->bitmap_rates[i])
+ rate_num++;
+
+ if (rate_num > 1)
+ return true;
+ else
+ return false;
+}
+
+/* This function gets the supported data rates from bitmask inside
+ * cfg80211_scan_request.
+ */
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+ u8 *rates, u8 radio_type)
+{
+ struct wiphy *wiphy = priv->adapter->wiphy;
+ struct cfg80211_scan_request *request = priv->scan_request;
+ u32 num_rates, rate_mask;
+ struct ieee80211_supported_band *sband;
+ int i;
+
+ if (radio_type) {
+ sband = wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (WARN_ON_ONCE(!sband))
+ return 0;
+ rate_mask = request->rates[IEEE80211_BAND_5GHZ];
+ } else {
+ sband = wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (WARN_ON_ONCE(!sband))
+ return 0;
+ rate_mask = request->rates[IEEE80211_BAND_2GHZ];
+ }
+
+ num_rates = 0;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((BIT(i) & rate_mask) == 0)
+ continue; /* skip rate */
+ rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
+ }
+
+ return num_rates;
+}
+
+/* This function gets the supported data rates. The function works in
+ * both Ad-Hoc and infra mode by printing the band and returning the
+ * data rates.
+ */
+u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
+{
+ u32 k = 0;
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+ priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
+ switch (adapter->config_bands) {
+ case BAND_B:
+ mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_b\n",
+ adapter->config_bands);
+ k = mwifiex_copy_rates(rates, k, supported_rates_b,
+ sizeof(supported_rates_b));
+ break;
+ case BAND_G:
+ case BAND_G | BAND_GN:
+ mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_g\n",
+ adapter->config_bands);
+ k = mwifiex_copy_rates(rates, k, supported_rates_g,
+ sizeof(supported_rates_g));
+ break;
+ case BAND_B | BAND_G:
+ case BAND_A | BAND_B | BAND_G:
+ case BAND_A | BAND_B:
+ case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN:
+ case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC:
+ case BAND_B | BAND_G | BAND_GN:
+ mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_bg\n",
+ adapter->config_bands);
+ k = mwifiex_copy_rates(rates, k, supported_rates_bg,
+ sizeof(supported_rates_bg));
+ break;
+ case BAND_A:
+ case BAND_A | BAND_G:
+ mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_a\n",
+ adapter->config_bands);
+ k = mwifiex_copy_rates(rates, k, supported_rates_a,
+ sizeof(supported_rates_a));
+ break;
+ case BAND_AN:
+ case BAND_A | BAND_AN:
+ case BAND_A | BAND_AN | BAND_AAC:
+ case BAND_A | BAND_G | BAND_AN | BAND_GN:
+ case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC:
+ mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_a\n",
+ adapter->config_bands);
+ k = mwifiex_copy_rates(rates, k, supported_rates_a,
+ sizeof(supported_rates_a));
+ break;
+ case BAND_GN:
+ mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_n\n",
+ adapter->config_bands);
+ k = mwifiex_copy_rates(rates, k, supported_rates_n,
+ sizeof(supported_rates_n));
+ break;
+ }
+ } else {
+ /* Ad-hoc mode */
+ switch (adapter->adhoc_start_band) {
+ case BAND_B:
+ mwifiex_dbg(adapter, INFO, "info: adhoc B\n");
+ k = mwifiex_copy_rates(rates, k, adhoc_rates_b,
+ sizeof(adhoc_rates_b));
+ break;
+ case BAND_G:
+ case BAND_G | BAND_GN:
+ mwifiex_dbg(adapter, INFO, "info: adhoc G only\n");
+ k = mwifiex_copy_rates(rates, k, adhoc_rates_g,
+ sizeof(adhoc_rates_g));
+ break;
+ case BAND_B | BAND_G:
+ case BAND_B | BAND_G | BAND_GN:
+ mwifiex_dbg(adapter, INFO, "info: adhoc BG\n");
+ k = mwifiex_copy_rates(rates, k, adhoc_rates_bg,
+ sizeof(adhoc_rates_bg));
+ break;
+ case BAND_A:
+ case BAND_A | BAND_AN:
+ mwifiex_dbg(adapter, INFO, "info: adhoc A\n");
+ k = mwifiex_copy_rates(rates, k, adhoc_rates_a,
+ sizeof(adhoc_rates_a));
+ break;
+ }
+ }
+
+ return k;
+}
+
+u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
+ u8 rx_rate, u8 rate_info)
+{
+ u8 rate_index = 0;
+
+ /* HT40 */
+ if ((rate_info & BIT(0)) && (rate_info & BIT(1)))
+ rate_index = MWIFIEX_RATE_INDEX_MCS0 +
+ MWIFIEX_BW20_MCS_NUM + rx_rate;
+ else if (rate_info & BIT(0)) /* HT20 */
+ rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate;
+ else
+ rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ?
+ rx_rate - 1 : rx_rate;
+
+ return rate_index;
+}
diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
new file mode 100644
index 000000000000..cb25aa7e90db
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
@@ -0,0 +1,1659 @@
+/*
+ * Marvell Wireless LAN device driver: commands and events
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11ac.h"
+
+/*
+ * This function initializes a command node.
+ *
+ * The actual allocation of the node is not done by this function. It only
+ * initiates a node by filling it with default parameters. Similarly,
+ * allocation of the different buffers used (IOCTL buffer, data buffer) are
+ * not done by this function either.
+ */
+static void
+mwifiex_init_cmd_node(struct mwifiex_private *priv,
+ struct cmd_ctrl_node *cmd_node,
+ u32 cmd_oid, void *data_buf, bool sync)
+{
+ cmd_node->priv = priv;
+ cmd_node->cmd_oid = cmd_oid;
+ if (sync) {
+ cmd_node->wait_q_enabled = true;
+ cmd_node->cmd_wait_q_woken = false;
+ cmd_node->condition = &cmd_node->cmd_wait_q_woken;
+ }
+ cmd_node->data_buf = data_buf;
+ cmd_node->cmd_skb = cmd_node->skb;
+}
+
+/*
+ * This function returns a command node from the free queue depending upon
+ * availability.
+ */
+static struct cmd_ctrl_node *
+mwifiex_get_cmd_node(struct mwifiex_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->cmd_free_q_lock, flags);
+ if (list_empty(&adapter->cmd_free_q)) {
+ mwifiex_dbg(adapter, ERROR,
+ "GET_CMD_NODE: cmd node not available\n");
+ spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
+ return NULL;
+ }
+ cmd_node = list_first_entry(&adapter->cmd_free_q,
+ struct cmd_ctrl_node, list);
+ list_del(&cmd_node->list);
+ spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
+
+ return cmd_node;
+}
+
+/*
+ * This function cleans up a command node.
+ *
+ * The function resets the fields including the buffer pointers.
+ * This function does not try to free the buffers. They must be
+ * freed before calling this function.
+ *
+ * This function will however call the receive completion callback
+ * in case a response buffer is still available before resetting
+ * the pointer.
+ */
+static void
+mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ cmd_node->cmd_oid = 0;
+ cmd_node->cmd_flag = 0;
+ cmd_node->data_buf = NULL;
+ cmd_node->wait_q_enabled = false;
+
+ if (cmd_node->cmd_skb)
+ skb_trim(cmd_node->cmd_skb, 0);
+
+ if (cmd_node->resp_skb) {
+ adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb);
+ cmd_node->resp_skb = NULL;
+ }
+}
+
+/*
+ * This function sends a host command to the firmware.
+ *
+ * The function copies the host command into the driver command
+ * buffer, which will be transferred to the firmware later by the
+ * main thread.
+ */
+static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_ds_misc_cmd *pcmd_ptr)
+{
+ /* Copy the HOST command to command buffer */
+ memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len);
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: host cmd size = %d\n", pcmd_ptr->len);
+ return 0;
+}
+
+/*
+ * This function downloads a command to the firmware.
+ *
+ * The function performs sanity tests, sets the command sequence
+ * number and size, converts the header fields to CPU format before
+ * sending. Afterwards, it logs the command ID and action for debugging
+ * and sets up the command timeout timer.
+ */
+static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
+ struct cmd_ctrl_node *cmd_node)
+{
+
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int ret;
+ struct host_cmd_ds_command *host_cmd;
+ uint16_t cmd_code;
+ uint16_t cmd_size;
+ unsigned long flags;
+ __le32 tmp;
+
+ if (!adapter || !cmd_node)
+ return -1;
+
+ host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+
+ /* Sanity test */
+ if (host_cmd == NULL || host_cmd->size == 0) {
+ mwifiex_dbg(adapter, ERROR,
+ "DNLD_CMD: host_cmd is null\t"
+ "or cmd size is 0, not sending\n");
+ if (cmd_node->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
+ return -1;
+ }
+
+ cmd_code = le16_to_cpu(host_cmd->command);
+ cmd_size = le16_to_cpu(host_cmd->size);
+
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET &&
+ cmd_code != HostCmd_CMD_FUNC_SHUTDOWN &&
+ cmd_code != HostCmd_CMD_FUNC_INIT) {
+ mwifiex_dbg(adapter, ERROR,
+ "DNLD_CMD: FW in reset state, ignore cmd %#x\n",
+ cmd_code);
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
+ queue_work(adapter->workqueue, &adapter->main_work);
+ return -1;
+ }
+
+ /* Set command sequence number */
+ adapter->seq_num++;
+ host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO
+ (adapter->seq_num,
+ cmd_node->priv->bss_num,
+ cmd_node->priv->bss_type));
+
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+ adapter->curr_cmd = cmd_node;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+ /* Adjust skb length */
+ if (cmd_node->cmd_skb->len > cmd_size)
+ /*
+ * cmd_size is less than sizeof(struct host_cmd_ds_command).
+ * Trim off the unused portion.
+ */
+ skb_trim(cmd_node->cmd_skb, cmd_size);
+ else if (cmd_node->cmd_skb->len < cmd_size)
+ /*
+ * cmd_size is larger than sizeof(struct host_cmd_ds_command)
+ * because we have appended custom IE TLV. Increase skb length
+ * accordingly.
+ */
+ skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
+
+ mwifiex_dbg(adapter, CMD,
+ "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
+ cmd_code,
+ le16_to_cpu(*(__le16 *)((u8 *)host_cmd + S_DS_GEN)),
+ cmd_size, le16_to_cpu(host_cmd->seq_num));
+ mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size);
+
+ if (adapter->iface_type == MWIFIEX_USB) {
+ tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD);
+ skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);
+ memcpy(cmd_node->cmd_skb->data, &tmp, MWIFIEX_TYPE_LEN);
+ adapter->cmd_sent = true;
+ ret = adapter->if_ops.host_to_card(adapter,
+ MWIFIEX_USB_EP_CMD_EVENT,
+ cmd_node->cmd_skb, NULL);
+ skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);
+ if (ret == -EBUSY)
+ cmd_node->cmd_skb = NULL;
+ } else {
+ skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN);
+ ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
+ cmd_node->cmd_skb, NULL);
+ skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN);
+ }
+
+ if (ret == -1) {
+ mwifiex_dbg(adapter, ERROR,
+ "DNLD_CMD: host to card failed\n");
+ if (adapter->iface_type == MWIFIEX_USB)
+ adapter->cmd_sent = false;
+ if (cmd_node->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
+
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+ adapter->curr_cmd = NULL;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+ adapter->dbg.num_cmd_host_to_card_failure++;
+ return -1;
+ }
+
+ /* Save the last command id and action to debug log */
+ adapter->dbg.last_cmd_index =
+ (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM;
+ adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code;
+ adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] =
+ le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN));
+
+ /* Clear BSS_NO_BITS from HostCmd */
+ cmd_code &= HostCmd_CMD_ID_MASK;
+
+ /* Setup the timer after transmit command */
+ mod_timer(&adapter->cmd_timer,
+ jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
+
+ return 0;
+}
+
+/*
+ * This function downloads a sleep confirm command to the firmware.
+ *
+ * The function performs sanity tests, sets the command sequence
+ * number and size, converts the header fields to CPU format before
+ * sending.
+ *
+ * No responses are needed for sleep confirm command.
+ */
+static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ struct mwifiex_private *priv;
+ struct mwifiex_opt_sleep_confirm *sleep_cfm_buf =
+ (struct mwifiex_opt_sleep_confirm *)
+ adapter->sleep_cfm->data;
+ struct sk_buff *sleep_cfm_tmp;
+ __le32 tmp;
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+ adapter->seq_num++;
+ sleep_cfm_buf->seq_num =
+ cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO
+ (adapter->seq_num, priv->bss_num,
+ priv->bss_type)));
+
+ mwifiex_dbg(adapter, CMD,
+ "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
+ le16_to_cpu(sleep_cfm_buf->command),
+ le16_to_cpu(sleep_cfm_buf->action),
+ le16_to_cpu(sleep_cfm_buf->size),
+ le16_to_cpu(sleep_cfm_buf->seq_num));
+ mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf,
+ le16_to_cpu(sleep_cfm_buf->size));
+
+ if (adapter->iface_type == MWIFIEX_USB) {
+ sleep_cfm_tmp =
+ dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
+ + MWIFIEX_TYPE_LEN);
+ skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm)
+ + MWIFIEX_TYPE_LEN);
+ tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD);
+ memcpy(sleep_cfm_tmp->data, &tmp, MWIFIEX_TYPE_LEN);
+ memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN,
+ adapter->sleep_cfm->data,
+ sizeof(struct mwifiex_opt_sleep_confirm));
+ ret = adapter->if_ops.host_to_card(adapter,
+ MWIFIEX_USB_EP_CMD_EVENT,
+ sleep_cfm_tmp, NULL);
+ if (ret != -EBUSY)
+ dev_kfree_skb_any(sleep_cfm_tmp);
+ } else {
+ skb_push(adapter->sleep_cfm, INTF_HEADER_LEN);
+ ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
+ adapter->sleep_cfm, NULL);
+ skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN);
+ }
+
+ if (ret == -1) {
+ mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n");
+ adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++;
+ return -1;
+ }
+
+ if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl))
+ /* Response is not needed for sleep confirm command */
+ adapter->ps_state = PS_STATE_SLEEP;
+ else
+ adapter->ps_state = PS_STATE_SLEEP_CFM;
+
+ if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) &&
+ (adapter->is_hs_configured &&
+ !adapter->sleep_period.period)) {
+ adapter->pm_wakeup_card_req = true;
+ mwifiex_hs_activated_event(mwifiex_get_priv
+ (adapter, MWIFIEX_BSS_ROLE_ANY), true);
+ }
+
+ return ret;
+}
+
+/*
+ * This function allocates the command buffers and links them to
+ * the command free queue.
+ *
+ * The driver uses a pre allocated number of command buffers, which
+ * are created at driver initializations and freed at driver cleanup.
+ * Every command needs to obtain a command buffer from this pool before
+ * it can be issued. The command free queue lists the command buffers
+ * currently free to use, while the command pending queue lists the
+ * command buffers already in use and awaiting handling. Command buffers
+ * are returned to the free queue after use.
+ */
+int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_array;
+ u32 i;
+
+ /* Allocate and initialize struct cmd_ctrl_node */
+ cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER,
+ sizeof(struct cmd_ctrl_node), GFP_KERNEL);
+ if (!cmd_array)
+ return -ENOMEM;
+
+ adapter->cmd_pool = cmd_array;
+
+ /* Allocate and initialize command buffers */
+ for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
+ cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER);
+ if (!cmd_array[i].skb) {
+ mwifiex_dbg(adapter, ERROR,
+ "unable to allocate command buffer\n");
+ return -ENOMEM;
+ }
+ }
+
+ for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++)
+ mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]);
+
+ return 0;
+}
+
+/*
+ * This function frees the command buffers.
+ *
+ * The function calls the completion callback for all the command
+ * buffers that still have response buffers associated with them.
+ */
+int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_array;
+ u32 i;
+
+ /* Need to check if cmd pool is allocated or not */
+ if (!adapter->cmd_pool) {
+ mwifiex_dbg(adapter, FATAL,
+ "info: FREE_CMD_BUF: cmd_pool is null\n");
+ return 0;
+ }
+
+ cmd_array = adapter->cmd_pool;
+
+ /* Release shared memory buffers */
+ for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
+ if (cmd_array[i].skb) {
+ mwifiex_dbg(adapter, CMD,
+ "cmd: free cmd buffer %d\n", i);
+ dev_kfree_skb_any(cmd_array[i].skb);
+ }
+ if (!cmd_array[i].resp_skb)
+ continue;
+
+ if (adapter->iface_type == MWIFIEX_USB)
+ adapter->if_ops.cmdrsp_complete(adapter,
+ cmd_array[i].resp_skb);
+ else
+ dev_kfree_skb_any(cmd_array[i].resp_skb);
+ }
+ /* Release struct cmd_ctrl_node */
+ if (adapter->cmd_pool) {
+ mwifiex_dbg(adapter, CMD,
+ "cmd: free cmd pool\n");
+ kfree(adapter->cmd_pool);
+ adapter->cmd_pool = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * This function handles events generated by firmware.
+ *
+ * Event body of events received from firmware are not used (though they are
+ * saved), only the event ID is used. Some events are re-invoked by
+ * the driver, with a new event body.
+ *
+ * After processing, the function calls the completion callback
+ * for cleanup.
+ */
+int mwifiex_process_event(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ struct mwifiex_private *priv =
+ mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ struct sk_buff *skb = adapter->event_skb;
+ u32 eventcause = adapter->event_cause;
+ struct mwifiex_rxinfo *rx_info;
+
+ /* Save the last event to debug log */
+ adapter->dbg.last_event_index =
+ (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM;
+ adapter->dbg.last_event[adapter->dbg.last_event_index] =
+ (u16) eventcause;
+
+ /* Get BSS number and corresponding priv */
+ priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause),
+ EVENT_GET_BSS_TYPE(eventcause));
+ if (!priv)
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+ /* Clear BSS_NO_BITS from event */
+ eventcause &= EVENT_ID_MASK;
+ adapter->event_cause = eventcause;
+
+ if (skb) {
+ rx_info = MWIFIEX_SKB_RXCB(skb);
+ memset(rx_info, 0, sizeof(*rx_info));
+ rx_info->bss_num = priv->bss_num;
+ rx_info->bss_type = priv->bss_type;
+ mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:",
+ skb->data, skb->len);
+ }
+
+ mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause);
+
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ ret = mwifiex_process_uap_event(priv);
+ else
+ ret = mwifiex_process_sta_event(priv);
+
+ adapter->event_cause = 0;
+ adapter->event_skb = NULL;
+ adapter->if_ops.event_complete(adapter, skb);
+
+ return ret;
+}
+
+/*
+ * This function prepares a command and send it to the firmware.
+ *
+ * Preparation includes -
+ * - Sanity tests to make sure the card is still present or the FW
+ * is not reset
+ * - Getting a new command node from the command free queue
+ * - Initializing the command node for default parameters
+ * - Fill up the non-default parameters and buffer pointers
+ * - Add the command to pending queue
+ */
+int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,
+ u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync)
+{
+ int ret;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct cmd_ctrl_node *cmd_node;
+ struct host_cmd_ds_command *cmd_ptr;
+
+ if (!adapter) {
+ pr_err("PREP_CMD: adapter is NULL\n");
+ return -1;
+ }
+
+ if (adapter->is_suspended) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: device in suspended state\n");
+ return -1;
+ }
+
+ if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: host entering sleep state\n");
+ return -1;
+ }
+
+ if (adapter->surprise_removed) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: card is removed\n");
+ return -1;
+ }
+
+ if (adapter->is_cmd_timedout) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: FW is in bad state\n");
+ return -1;
+ }
+
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) {
+ if (cmd_no != HostCmd_CMD_FUNC_INIT) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: FW in reset state\n");
+ return -1;
+ }
+ }
+
+ /* Get a new command node */
+ cmd_node = mwifiex_get_cmd_node(adapter);
+
+ if (!cmd_node) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: no free cmd node\n");
+ return -1;
+ }
+
+ /* Initialize the command node */
+ mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync);
+
+ if (!cmd_node->cmd_skb) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: no free cmd buf\n");
+ return -1;
+ }
+
+ memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)),
+ 0, sizeof(struct host_cmd_ds_command));
+
+ cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+ cmd_ptr->command = cpu_to_le16(cmd_no);
+ cmd_ptr->result = 0;
+
+ /* Prepare command */
+ if (cmd_no) {
+ switch (cmd_no) {
+ case HostCmd_CMD_UAP_SYS_CONFIG:
+ case HostCmd_CMD_UAP_BSS_START:
+ case HostCmd_CMD_UAP_BSS_STOP:
+ case HostCmd_CMD_UAP_STA_DEAUTH:
+ case HOST_CMD_APCMD_SYS_RESET:
+ case HOST_CMD_APCMD_STA_LIST:
+ ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
+ cmd_oid, data_buf,
+ cmd_ptr);
+ break;
+ default:
+ ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action,
+ cmd_oid, data_buf,
+ cmd_ptr);
+ break;
+ }
+ } else {
+ ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf);
+ cmd_node->cmd_flag |= CMD_F_HOSTCMD;
+ }
+
+ /* Return error, since the command preparation failed */
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "PREP_CMD: cmd %#x preparation failed\n",
+ cmd_no);
+ mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ return -1;
+ }
+
+ /* Send command */
+ if (cmd_no == HostCmd_CMD_802_11_SCAN ||
+ cmd_no == HostCmd_CMD_802_11_SCAN_EXT) {
+ mwifiex_queue_scan_cmd(priv, cmd_node);
+ } else {
+ mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
+ queue_work(adapter->workqueue, &adapter->main_work);
+ if (cmd_node->wait_q_enabled)
+ ret = mwifiex_wait_queue_complete(adapter, cmd_node);
+ }
+
+ return ret;
+}
+
+/*
+ * This function returns a command to the command free queue.
+ *
+ * The function also calls the completion callback if required, before
+ * cleaning the command node and re-inserting it into the free queue.
+ */
+void
+mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ unsigned long flags;
+
+ if (!cmd_node)
+ return;
+
+ if (cmd_node->wait_q_enabled)
+ mwifiex_complete_cmd(adapter, cmd_node);
+ /* Clean the node */
+ mwifiex_clean_cmd_node(adapter, cmd_node);
+
+ /* Insert node into cmd_free_q */
+ spin_lock_irqsave(&adapter->cmd_free_q_lock, flags);
+ list_add_tail(&cmd_node->list, &adapter->cmd_free_q);
+ spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
+}
+
+/* This function reuses a command node. */
+void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data;
+
+ mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+
+ atomic_dec(&adapter->cmd_pending);
+ mwifiex_dbg(adapter, CMD,
+ "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n",
+ le16_to_cpu(host_cmd->command),
+ atomic_read(&adapter->cmd_pending));
+}
+
+/*
+ * This function queues a command to the command pending queue.
+ *
+ * This in effect adds the command to the command list to be executed.
+ * Exit PS command is handled specially, by placing it always to the
+ * front of the command queue.
+ */
+void
+mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node, u32 add_tail)
+{
+ struct host_cmd_ds_command *host_cmd = NULL;
+ u16 command;
+ unsigned long flags;
+
+ host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+ if (!host_cmd) {
+ mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n");
+ return;
+ }
+
+ command = le16_to_cpu(host_cmd->command);
+
+ /* Exit_PS command needs to be queued in the header always. */
+ if (command == HostCmd_CMD_802_11_PS_MODE_ENH) {
+ struct host_cmd_ds_802_11_ps_mode_enh *pm =
+ &host_cmd->params.psmode_enh;
+ if ((le16_to_cpu(pm->action) == DIS_PS) ||
+ (le16_to_cpu(pm->action) == DIS_AUTO_PS)) {
+ if (adapter->ps_state != PS_STATE_AWAKE)
+ add_tail = false;
+ }
+ }
+
+ spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+ if (add_tail)
+ list_add_tail(&cmd_node->list, &adapter->cmd_pending_q);
+ else
+ list_add(&cmd_node->list, &adapter->cmd_pending_q);
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+
+ atomic_inc(&adapter->cmd_pending);
+ mwifiex_dbg(adapter, CMD,
+ "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n",
+ command, atomic_read(&adapter->cmd_pending));
+}
+
+/*
+ * This function executes the next command in command pending queue.
+ *
+ * This function will fail if a command is already in processing stage,
+ * otherwise it will dequeue the first command from the command pending
+ * queue and send to the firmware.
+ *
+ * If the device is currently in host sleep mode, any commands, except the
+ * host sleep configuration command will de-activate the host sleep. For PS
+ * mode, the function will put the firmware back to sleep if applicable.
+ */
+int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_private *priv;
+ struct cmd_ctrl_node *cmd_node;
+ int ret = 0;
+ struct host_cmd_ds_command *host_cmd;
+ unsigned long cmd_flags;
+ unsigned long cmd_pending_q_flags;
+
+ /* Check if already in processing */
+ if (adapter->curr_cmd) {
+ mwifiex_dbg(adapter, FATAL,
+ "EXEC_NEXT_CMD: cmd in processing\n");
+ return -1;
+ }
+
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+ /* Check if any command is pending */
+ spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags);
+ if (list_empty(&adapter->cmd_pending_q)) {
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+ cmd_pending_q_flags);
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+ return 0;
+ }
+ cmd_node = list_first_entry(&adapter->cmd_pending_q,
+ struct cmd_ctrl_node, list);
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+ cmd_pending_q_flags);
+
+ host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+ priv = cmd_node->priv;
+
+ if (adapter->ps_state != PS_STATE_AWAKE) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: cannot send cmd in sleep state,\t"
+ "this should not happen\n", __func__);
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+ return ret;
+ }
+
+ spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags);
+ list_del(&cmd_node->list);
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+ cmd_pending_q_flags);
+
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+ ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node);
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ /* Any command sent to the firmware when host is in sleep
+ * mode should de-configure host sleep. We should skip the
+ * host sleep configuration command itself though
+ */
+ if (priv && (host_cmd->command !=
+ cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) {
+ if (adapter->hs_activated) {
+ adapter->is_hs_configured = false;
+ mwifiex_hs_activated_event(priv, false);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * This function handles the command response.
+ *
+ * After processing, the function cleans the command node and puts
+ * it back to the command free queue.
+ */
+int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
+{
+ struct host_cmd_ds_command *resp;
+ struct mwifiex_private *priv =
+ mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ int ret = 0;
+ uint16_t orig_cmdresp_no;
+ uint16_t cmdresp_no;
+ uint16_t cmdresp_result;
+ unsigned long flags;
+
+ /* Now we got response from FW, cancel the command timer */
+ del_timer_sync(&adapter->cmd_timer);
+
+ if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) {
+ resp = (struct host_cmd_ds_command *) adapter->upld_buf;
+ mwifiex_dbg(adapter, ERROR,
+ "CMD_RESP: NULL curr_cmd, %#x\n",
+ le16_to_cpu(resp->command));
+ return -1;
+ }
+
+ adapter->is_cmd_timedout = 0;
+
+ resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data;
+ if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
+ /* Copy original response back to response buffer */
+ struct mwifiex_ds_misc_cmd *hostcmd;
+ uint16_t size = le16_to_cpu(resp->size);
+ mwifiex_dbg(adapter, INFO,
+ "info: host cmd resp size = %d\n", size);
+ size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER);
+ if (adapter->curr_cmd->data_buf) {
+ hostcmd = adapter->curr_cmd->data_buf;
+ hostcmd->len = size;
+ memcpy(hostcmd->cmd, resp, size);
+ }
+ }
+ orig_cmdresp_no = le16_to_cpu(resp->command);
+
+ /* Get BSS number and corresponding priv */
+ priv = mwifiex_get_priv_by_id(adapter,
+ HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)),
+ HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num)));
+ if (!priv)
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ /* Clear RET_BIT from HostCmd */
+ resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK);
+
+ cmdresp_no = le16_to_cpu(resp->command);
+ cmdresp_result = le16_to_cpu(resp->result);
+
+ /* Save the last command response to debug log */
+ adapter->dbg.last_cmd_resp_index =
+ (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM;
+ adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =
+ orig_cmdresp_no;
+
+ mwifiex_dbg(adapter, CMD,
+ "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+ orig_cmdresp_no, cmdresp_result,
+ le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
+ mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp,
+ le16_to_cpu(resp->size));
+
+ if (!(orig_cmdresp_no & HostCmd_RET_BIT)) {
+ mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n");
+ if (adapter->curr_cmd->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+ adapter->curr_cmd = NULL;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+ return -1;
+ }
+
+ if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
+ adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD;
+ if ((cmdresp_result == HostCmd_RESULT_OK) &&
+ (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH))
+ ret = mwifiex_ret_802_11_hs_cfg(priv, resp);
+ } else {
+ /* handle response */
+ ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp);
+ }
+
+ /* Check init command response */
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: cmd %#x failed during\t"
+ "initialization\n", __func__, cmdresp_no);
+ mwifiex_init_fw_complete(adapter);
+ return -1;
+ } else if (adapter->last_init_cmd == cmdresp_no)
+ adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE;
+ }
+
+ if (adapter->curr_cmd) {
+ if (adapter->curr_cmd->wait_q_enabled)
+ adapter->cmd_wait_q.status = ret;
+
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
+
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+ adapter->curr_cmd = NULL;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+ }
+
+ return ret;
+}
+
+/*
+ * This function handles the timeout of command sending.
+ *
+ * It will re-send the same command again.
+ */
+void
+mwifiex_cmd_timeout_func(unsigned long function_context)
+{
+ struct mwifiex_adapter *adapter =
+ (struct mwifiex_adapter *) function_context;
+ struct cmd_ctrl_node *cmd_node;
+
+ adapter->is_cmd_timedout = 1;
+ if (!adapter->curr_cmd) {
+ mwifiex_dbg(adapter, ERROR,
+ "cmd: empty curr_cmd\n");
+ return;
+ }
+ cmd_node = adapter->curr_cmd;
+ if (cmd_node) {
+ adapter->dbg.timeout_cmd_id =
+ adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index];
+ adapter->dbg.timeout_cmd_act =
+ adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index];
+ mwifiex_dbg(adapter, MSG,
+ "%s: Timeout cmd id = %#x, act = %#x\n", __func__,
+ adapter->dbg.timeout_cmd_id,
+ adapter->dbg.timeout_cmd_act);
+
+ mwifiex_dbg(adapter, MSG,
+ "num_data_h2c_failure = %d\n",
+ adapter->dbg.num_tx_host_to_card_failure);
+ mwifiex_dbg(adapter, MSG,
+ "num_cmd_h2c_failure = %d\n",
+ adapter->dbg.num_cmd_host_to_card_failure);
+
+ mwifiex_dbg(adapter, MSG,
+ "is_cmd_timedout = %d\n",
+ adapter->is_cmd_timedout);
+ mwifiex_dbg(adapter, MSG,
+ "num_tx_timeout = %d\n",
+ adapter->dbg.num_tx_timeout);
+
+ mwifiex_dbg(adapter, MSG,
+ "last_cmd_index = %d\n",
+ adapter->dbg.last_cmd_index);
+ mwifiex_dbg(adapter, MSG,
+ "last_cmd_id: %*ph\n",
+ (int)sizeof(adapter->dbg.last_cmd_id),
+ adapter->dbg.last_cmd_id);
+ mwifiex_dbg(adapter, MSG,
+ "last_cmd_act: %*ph\n",
+ (int)sizeof(adapter->dbg.last_cmd_act),
+ adapter->dbg.last_cmd_act);
+
+ mwifiex_dbg(adapter, MSG,
+ "last_cmd_resp_index = %d\n",
+ adapter->dbg.last_cmd_resp_index);
+ mwifiex_dbg(adapter, MSG,
+ "last_cmd_resp_id: %*ph\n",
+ (int)sizeof(adapter->dbg.last_cmd_resp_id),
+ adapter->dbg.last_cmd_resp_id);
+
+ mwifiex_dbg(adapter, MSG,
+ "last_event_index = %d\n",
+ adapter->dbg.last_event_index);
+ mwifiex_dbg(adapter, MSG,
+ "last_event: %*ph\n",
+ (int)sizeof(adapter->dbg.last_event),
+ adapter->dbg.last_event);
+
+ mwifiex_dbg(adapter, MSG,
+ "data_sent=%d cmd_sent=%d\n",
+ adapter->data_sent, adapter->cmd_sent);
+
+ mwifiex_dbg(adapter, MSG,
+ "ps_mode=%d ps_state=%d\n",
+ adapter->ps_mode, adapter->ps_state);
+
+ if (cmd_node->wait_q_enabled) {
+ adapter->cmd_wait_q.status = -ETIMEDOUT;
+ mwifiex_cancel_pending_ioctl(adapter);
+ }
+ }
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
+ mwifiex_init_fw_complete(adapter);
+ return;
+ }
+
+ if (adapter->if_ops.device_dump)
+ adapter->if_ops.device_dump(adapter);
+
+ if (adapter->if_ops.card_reset)
+ adapter->if_ops.card_reset(adapter);
+}
+
+/*
+ * This function cancels all the pending commands.
+ *
+ * The current command, all commands in command pending queue and all scan
+ * commands in scan pending queue are cancelled. All the completion callbacks
+ * are called with failure status to ensure cleanup.
+ */
+void
+mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
+ unsigned long flags, cmd_flags;
+ struct mwifiex_private *priv;
+ int i;
+
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+ /* Cancel current cmd */
+ if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) {
+ adapter->curr_cmd->wait_q_enabled = false;
+ adapter->cmd_wait_q.status = -1;
+ mwifiex_complete_cmd(adapter, adapter->curr_cmd);
+ /* no recycle probably wait for response */
+ }
+ /* Cancel all pending command */
+ spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+ list_for_each_entry_safe(cmd_node, tmp_node,
+ &adapter->cmd_pending_q, list) {
+ list_del(&cmd_node->list);
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+
+ if (cmd_node->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
+ spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+ }
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+
+ /* Cancel all pending scan command */
+ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+ list_for_each_entry_safe(cmd_node, tmp_node,
+ &adapter->scan_pending_q, list) {
+ list_del(&cmd_node->list);
+
+ cmd_node->wait_q_enabled = false;
+ mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ }
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+ if (adapter->scan_processing) {
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+ adapter->scan_processing = false;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ if (priv->scan_request) {
+ mwifiex_dbg(adapter, WARN, "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ }
+ }
+ }
+}
+
+/*
+ * This function cancels all pending commands that matches with
+ * the given IOCTL request.
+ *
+ * Both the current command buffer and the pending command queue are
+ * searched for matching IOCTL request. The completion callback of
+ * the matched command is called with failure status to ensure cleanup.
+ * In case of scan commands, all pending commands in scan pending queue
+ * are cancelled.
+ */
+void
+mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
+ unsigned long cmd_flags;
+ unsigned long scan_pending_q_flags;
+ struct mwifiex_private *priv;
+ int i;
+
+ if ((adapter->curr_cmd) &&
+ (adapter->curr_cmd->wait_q_enabled)) {
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+ cmd_node = adapter->curr_cmd;
+ /* setting curr_cmd to NULL is quite dangerous, because
+ * mwifiex_process_cmdresp checks curr_cmd to be != NULL
+ * at the beginning then relies on it and dereferences
+ * it at will
+ * this probably works since mwifiex_cmd_timeout_func
+ * is the only caller of this function and responses
+ * at that point
+ */
+ adapter->curr_cmd = NULL;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
+ }
+
+ /* Cancel all pending scan command */
+ spin_lock_irqsave(&adapter->scan_pending_q_lock,
+ scan_pending_q_flags);
+ list_for_each_entry_safe(cmd_node, tmp_node,
+ &adapter->scan_pending_q, list) {
+ list_del(&cmd_node->list);
+ cmd_node->wait_q_enabled = false;
+ mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ }
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+ scan_pending_q_flags);
+
+ if (adapter->scan_processing) {
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+ adapter->scan_processing = false;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ if (priv->scan_request) {
+ mwifiex_dbg(adapter, WARN, "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ }
+ }
+ }
+}
+
+/*
+ * This function sends the sleep confirm command to firmware, if
+ * possible.
+ *
+ * The sleep confirm command cannot be issued if command response,
+ * data response or event response is awaiting handling, or if we
+ * are in the middle of sending a command, or expecting a command
+ * response.
+ */
+void
+mwifiex_check_ps_cond(struct mwifiex_adapter *adapter)
+{
+ if (!adapter->cmd_sent &&
+ !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter))
+ mwifiex_dnld_sleep_confirm_cmd(adapter);
+ else
+ mwifiex_dbg(adapter, CMD,
+ "cmd: Delay Sleep Confirm (%s%s%s)\n",
+ (adapter->cmd_sent) ? "D" : "",
+ (adapter->curr_cmd) ? "C" : "",
+ (IS_CARD_RX_RCVD(adapter)) ? "R" : "");
+}
+
+/*
+ * This function sends a Host Sleep activated event to applications.
+ *
+ * This event is generated by the driver, with a blank event body.
+ */
+void
+mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated)
+{
+ if (activated) {
+ if (priv->adapter->is_hs_configured) {
+ priv->adapter->hs_activated = true;
+ mwifiex_update_rxreor_flags(priv->adapter,
+ RXREOR_FORCE_NO_DROP);
+ mwifiex_dbg(priv->adapter, EVENT,
+ "event: hs_activated\n");
+ priv->adapter->hs_activate_wait_q_woken = true;
+ wake_up_interruptible(
+ &priv->adapter->hs_activate_wait_q);
+ } else {
+ mwifiex_dbg(priv->adapter, EVENT,
+ "event: HS not configured\n");
+ }
+ } else {
+ mwifiex_dbg(priv->adapter, EVENT,
+ "event: hs_deactivated\n");
+ priv->adapter->hs_activated = false;
+ }
+}
+
+/*
+ * This function handles the command response of a Host Sleep configuration
+ * command.
+ *
+ * Handling includes changing the header fields into CPU format
+ * and setting the current host sleep activation status in driver.
+ *
+ * In case host sleep status change, the function generates an event to
+ * notify the applications.
+ */
+int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg =
+ &resp->params.opt_hs_cfg;
+ uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);
+
+ if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) &&
+ adapter->iface_type != MWIFIEX_USB) {
+ mwifiex_hs_activated_event(priv, true);
+ return 0;
+ } else {
+ mwifiex_dbg(adapter, CMD,
+ "cmd: CMD_RESP: HS_CFG cmd reply\t"
+ " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n",
+ resp->result, conditions,
+ phs_cfg->params.hs_config.gpio,
+ phs_cfg->params.hs_config.gap);
+ }
+ if (conditions != HS_CFG_CANCEL) {
+ adapter->is_hs_configured = true;
+ if (adapter->iface_type == MWIFIEX_USB)
+ mwifiex_hs_activated_event(priv, true);
+ } else {
+ adapter->is_hs_configured = false;
+ if (adapter->hs_activated)
+ mwifiex_hs_activated_event(priv, false);
+ }
+
+ return 0;
+}
+
+/*
+ * This function wakes up the adapter and generates a Host Sleep
+ * cancel event on receiving the power up interrupt.
+ */
+void
+mwifiex_process_hs_config(struct mwifiex_adapter *adapter)
+{
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: auto cancelling host sleep\t"
+ "since there is interrupt from the firmware\n",
+ __func__);
+
+ adapter->if_ops.wakeup(adapter);
+ adapter->hs_activated = false;
+ adapter->is_hs_configured = false;
+ adapter->is_suspended = false;
+ mwifiex_hs_activated_event(mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_ANY),
+ false);
+}
+EXPORT_SYMBOL_GPL(mwifiex_process_hs_config);
+
+/*
+ * This function handles the command response of a sleep confirm command.
+ *
+ * The function sets the card state to SLEEP if the response indicates success.
+ */
+void
+mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
+ u8 *pbuf, u32 upld_len)
+{
+ struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf;
+ struct mwifiex_private *priv =
+ mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ uint16_t result = le16_to_cpu(cmd->result);
+ uint16_t command = le16_to_cpu(cmd->command);
+ uint16_t seq_num = le16_to_cpu(cmd->seq_num);
+
+ if (!upld_len) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: cmd size is 0\n", __func__);
+ return;
+ }
+
+ mwifiex_dbg(adapter, CMD,
+ "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+ command, result, le16_to_cpu(cmd->size), seq_num);
+
+ /* Get BSS number and corresponding priv */
+ priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num),
+ HostCmd_GET_BSS_TYPE(seq_num));
+ if (!priv)
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+ /* Update sequence number */
+ seq_num = HostCmd_GET_SEQ_NO(seq_num);
+ /* Clear RET_BIT from HostCmd */
+ command &= HostCmd_CMD_ID_MASK;
+
+ if (command != HostCmd_CMD_802_11_PS_MODE_ENH) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: rcvd unexpected resp for cmd %#x, result = %x\n",
+ __func__, command, result);
+ return;
+ }
+
+ if (result) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: sleep confirm cmd failed\n",
+ __func__);
+ adapter->pm_wakeup_card_req = false;
+ adapter->ps_state = PS_STATE_AWAKE;
+ return;
+ }
+ adapter->pm_wakeup_card_req = true;
+ if (adapter->is_hs_configured)
+ mwifiex_hs_activated_event(mwifiex_get_priv
+ (adapter, MWIFIEX_BSS_ROLE_ANY),
+ true);
+ adapter->ps_state = PS_STATE_SLEEP;
+ cmd->command = cpu_to_le16(command);
+ cmd->seq_num = cpu_to_le16(seq_num);
+}
+EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp);
+
+/*
+ * This function prepares an enhanced power mode command.
+ *
+ * This function can be used to disable power save or to configure
+ * power save with auto PS or STA PS or auto deep sleep.
+ *
+ * Preparation includes -
+ * - Setting command ID, action and proper size
+ * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV,
+ * auto deep sleep TLV (as required)
+ * - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, uint16_t ps_bitmap,
+ struct mwifiex_ds_auto_ds *auto_ds)
+{
+ struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh =
+ &cmd->params.psmode_enh;
+ u8 *tlv;
+ u16 cmd_size = 0;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
+ if (cmd_action == DIS_AUTO_PS) {
+ psmode_enh->action = cpu_to_le16(DIS_AUTO_PS);
+ psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
+ cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
+ sizeof(psmode_enh->params.ps_bitmap));
+ } else if (cmd_action == GET_PS) {
+ psmode_enh->action = cpu_to_le16(GET_PS);
+ psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
+ cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
+ sizeof(psmode_enh->params.ps_bitmap));
+ } else if (cmd_action == EN_AUTO_PS) {
+ psmode_enh->action = cpu_to_le16(EN_AUTO_PS);
+ psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
+ cmd_size = S_DS_GEN + sizeof(psmode_enh->action) +
+ sizeof(psmode_enh->params.ps_bitmap);
+ tlv = (u8 *) cmd + cmd_size;
+ if (ps_bitmap & BITMAP_STA_PS) {
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_ie_types_ps_param *ps_tlv =
+ (struct mwifiex_ie_types_ps_param *) tlv;
+ struct mwifiex_ps_param *ps_mode = &ps_tlv->param;
+ ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM);
+ ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) -
+ sizeof(struct mwifiex_ie_types_header));
+ cmd_size += sizeof(*ps_tlv);
+ tlv += sizeof(*ps_tlv);
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: PS Command: Enter PS\n");
+ ps_mode->null_pkt_interval =
+ cpu_to_le16(adapter->null_pkt_interval);
+ ps_mode->multiple_dtims =
+ cpu_to_le16(adapter->multiple_dtim);
+ ps_mode->bcn_miss_timeout =
+ cpu_to_le16(adapter->bcn_miss_time_out);
+ ps_mode->local_listen_interval =
+ cpu_to_le16(adapter->local_listen_interval);
+ ps_mode->adhoc_wake_period =
+ cpu_to_le16(adapter->adhoc_awake_period);
+ ps_mode->delay_to_ps =
+ cpu_to_le16(adapter->delay_to_ps);
+ ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode);
+
+ }
+ if (ps_bitmap & BITMAP_AUTO_DS) {
+ struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv =
+ (struct mwifiex_ie_types_auto_ds_param *) tlv;
+ u16 idletime = 0;
+
+ auto_ds_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM);
+ auto_ds_tlv->header.len =
+ cpu_to_le16(sizeof(*auto_ds_tlv) -
+ sizeof(struct mwifiex_ie_types_header));
+ cmd_size += sizeof(*auto_ds_tlv);
+ tlv += sizeof(*auto_ds_tlv);
+ if (auto_ds)
+ idletime = auto_ds->idle_time;
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: PS Command: Enter Auto Deep Sleep\n");
+ auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime);
+ }
+ cmd->size = cpu_to_le16(cmd_size);
+ }
+ return 0;
+}
+
+/*
+ * This function handles the command response of an enhanced power mode
+ * command.
+ *
+ * Handling includes changing the header fields into CPU format
+ * and setting the current enhanced power mode in driver.
+ */
+int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp,
+ struct mwifiex_ds_pm_cfg *pm_cfg)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =
+ &resp->params.psmode_enh;
+ uint16_t action = le16_to_cpu(ps_mode->action);
+ uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap);
+ uint16_t auto_ps_bitmap =
+ le16_to_cpu(ps_mode->params.ps_bitmap);
+
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: PS_MODE cmd reply result=%#x action=%#X\n",
+ __func__, resp->result, action);
+ if (action == EN_AUTO_PS) {
+ if (auto_ps_bitmap & BITMAP_AUTO_DS) {
+ mwifiex_dbg(adapter, CMD,
+ "cmd: Enabled auto deep sleep\n");
+ priv->adapter->is_deep_sleep = true;
+ }
+ if (auto_ps_bitmap & BITMAP_STA_PS) {
+ mwifiex_dbg(adapter, CMD,
+ "cmd: Enabled STA power save\n");
+ if (adapter->sleep_period.period)
+ mwifiex_dbg(adapter, CMD,
+ "cmd: set to uapsd/pps mode\n");
+ }
+ } else if (action == DIS_AUTO_PS) {
+ if (ps_bitmap & BITMAP_AUTO_DS) {
+ priv->adapter->is_deep_sleep = false;
+ mwifiex_dbg(adapter, CMD,
+ "cmd: Disabled auto deep sleep\n");
+ }
+ if (ps_bitmap & BITMAP_STA_PS) {
+ mwifiex_dbg(adapter, CMD,
+ "cmd: Disabled STA power save\n");
+ if (adapter->sleep_period.period) {
+ adapter->delay_null_pkt = false;
+ adapter->tx_lock_flag = false;
+ adapter->pps_uapsd_mode = false;
+ }
+ }
+ } else if (action == GET_PS) {
+ if (ps_bitmap & BITMAP_STA_PS)
+ adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
+ else
+ adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
+
+ mwifiex_dbg(adapter, CMD,
+ "cmd: ps_bitmap=%#x\n", ps_bitmap);
+
+ if (pm_cfg) {
+ /* This section is for get power save mode */
+ if (ps_bitmap & BITMAP_STA_PS)
+ pm_cfg->param.ps_mode = 1;
+ else
+ pm_cfg->param.ps_mode = 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ * This function prepares command to get hardware specifications.
+ *
+ * Preparation includes -
+ * - Setting command ID, action and proper size
+ * - Setting permanent address parameter
+ * - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd)
+{
+ struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC);
+ cmd->size =
+ cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN);
+ memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN);
+
+ return 0;
+}
+
+/*
+ * This function handles the command response of get hardware
+ * specifications.
+ *
+ * Handling includes changing the header fields into CPU format
+ * and saving/updating the following parameters in driver -
+ * - Firmware capability information
+ * - Firmware band settings
+ * - Ad-hoc start band and channel
+ * - Ad-hoc 11n activation status
+ * - Firmware release number
+ * - Number of antennas
+ * - Hardware address
+ * - Hardware interface version
+ * - Firmware version
+ * - Region code
+ * - 11n capabilities
+ * - MCS support fields
+ * - MP end port
+ */
+int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_ie_types_header *tlv;
+ struct hw_spec_api_rev *api_rev;
+ u16 resp_size, api_id;
+ int i, left_len, parsed_len = 0;
+
+ adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info);
+
+ if (IS_SUPPORT_MULTI_BANDS(adapter))
+ adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter);
+ else
+ adapter->fw_bands = BAND_B;
+
+ adapter->config_bands = adapter->fw_bands;
+
+ if (adapter->fw_bands & BAND_A) {
+ if (adapter->fw_bands & BAND_GN) {
+ adapter->config_bands |= BAND_AN;
+ adapter->fw_bands |= BAND_AN;
+ }
+ if (adapter->fw_bands & BAND_AN) {
+ adapter->adhoc_start_band = BAND_A | BAND_AN;
+ adapter->adhoc_11n_enabled = true;
+ } else {
+ adapter->adhoc_start_band = BAND_A;
+ }
+ priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A;
+ } else if (adapter->fw_bands & BAND_GN) {
+ adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN;
+ priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
+ adapter->adhoc_11n_enabled = true;
+ } else if (adapter->fw_bands & BAND_G) {
+ adapter->adhoc_start_band = BAND_G | BAND_B;
+ priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
+ } else if (adapter->fw_bands & BAND_B) {
+ adapter->adhoc_start_band = BAND_B;
+ priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
+ }
+
+ adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
+ adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff;
+ adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna);
+
+ if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {
+ adapter->is_hw_11ac_capable = true;
+
+ /* Copy 11AC cap */
+ adapter->hw_dot_11ac_dev_cap =
+ le32_to_cpu(hw_spec->dot_11ac_dev_cap);
+ adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap
+ & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
+ adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap
+ & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
+
+ /* Copy 11AC mcs */
+ adapter->hw_dot_11ac_mcs_support =
+ le32_to_cpu(hw_spec->dot_11ac_mcs_support);
+ adapter->usr_dot_11ac_mcs_support =
+ adapter->hw_dot_11ac_mcs_support;
+ } else {
+ adapter->is_hw_11ac_capable = false;
+ }
+
+ resp_size = le16_to_cpu(resp->size) - S_DS_GEN;
+ if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) {
+ /* we have variable HW SPEC information */
+ left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec);
+ while (left_len > sizeof(struct mwifiex_ie_types_header)) {
+ tlv = (void *)&hw_spec->tlvs + parsed_len;
+ switch (le16_to_cpu(tlv->type)) {
+ case TLV_TYPE_API_REV:
+ api_rev = (struct hw_spec_api_rev *)tlv;
+ api_id = le16_to_cpu(api_rev->api_id);
+ switch (api_id) {
+ case KEY_API_VER_ID:
+ adapter->key_api_major_ver =
+ api_rev->major_ver;
+ adapter->key_api_minor_ver =
+ api_rev->minor_ver;
+ mwifiex_dbg(adapter, INFO,
+ "key_api v%d.%d\n",
+ adapter->key_api_major_ver,
+ adapter->key_api_minor_ver);
+ break;
+ case FW_API_VER_ID:
+ adapter->fw_api_ver =
+ api_rev->major_ver;
+ mwifiex_dbg(adapter, INFO,
+ "Firmware api version %d\n",
+ adapter->fw_api_ver);
+ break;
+ default:
+ mwifiex_dbg(adapter, FATAL,
+ "Unknown api_id: %d\n",
+ api_id);
+ break;
+ }
+ break;
+ default:
+ mwifiex_dbg(adapter, FATAL,
+ "Unknown GET_HW_SPEC TLV type: %#x\n",
+ le16_to_cpu(tlv->type));
+ break;
+ }
+ parsed_len += le16_to_cpu(tlv->len) +
+ sizeof(struct mwifiex_ie_types_header);
+ left_len -= le16_to_cpu(tlv->len) +
+ sizeof(struct mwifiex_ie_types_header);
+ }
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "info: GET_HW_SPEC: fw_release_number- %#x\n",
+ adapter->fw_release_number);
+ mwifiex_dbg(adapter, INFO,
+ "info: GET_HW_SPEC: permanent addr: %pM\n",
+ hw_spec->permanent_addr);
+ mwifiex_dbg(adapter, INFO,
+ "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n",
+ le16_to_cpu(hw_spec->hw_if_version),
+ le16_to_cpu(hw_spec->version));
+
+ ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr);
+ adapter->region_code = le16_to_cpu(hw_spec->region_code);
+
+ for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++)
+ /* Use the region code to search for the index */
+ if (adapter->region_code == region_code_index[i])
+ break;
+
+ /* If it's unidentified region code, use the default (world) */
+ if (i >= MWIFIEX_MAX_REGION_CODE) {
+ adapter->region_code = 0x00;
+ mwifiex_dbg(adapter, WARN,
+ "cmd: unknown region code, use default (USA)\n");
+ }
+
+ adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap);
+ adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support;
+ adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support;
+
+ if (adapter->if_ops.update_mp_end_port)
+ adapter->if_ops.update_mp_end_port(adapter,
+ le16_to_cpu(hw_spec->mp_end_port));
+
+ if (adapter->fw_api_ver == MWIFIEX_FW_V15)
+ adapter->scan_chan_gap_enabled = true;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c
new file mode 100644
index 000000000000..0b9c580af988
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c
@@ -0,0 +1,1003 @@
+/*
+ * Marvell Wireless LAN device driver: debugfs
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 <linux/debugfs.h>
+
+#include "main.h"
+#include "11n.h"
+
+
+static struct dentry *mwifiex_dfs_dir;
+
+static char *bss_modes[] = {
+ "UNSPECIFIED",
+ "ADHOC",
+ "STATION",
+ "AP",
+ "AP_VLAN",
+ "WDS",
+ "MONITOR",
+ "MESH_POINT",
+ "P2P_CLIENT",
+ "P2P_GO",
+ "P2P_DEVICE",
+};
+
+/*
+ * Proc info file read handler.
+ *
+ * This function is called when the 'info' file is opened for reading.
+ * It prints the following driver related information -
+ * - Driver name
+ * - Driver version
+ * - Driver extended version
+ * - Interface name
+ * - BSS mode
+ * - Media state (connected or disconnected)
+ * - MAC address
+ * - Total number of Tx bytes
+ * - Total number of Rx bytes
+ * - Total number of Tx packets
+ * - Total number of Rx packets
+ * - Total number of dropped Tx packets
+ * - Total number of dropped Rx packets
+ * - Total number of corrupted Tx packets
+ * - Total number of corrupted Rx packets
+ * - Carrier status (on or off)
+ * - Tx queue status (started or stopped)
+ *
+ * For STA mode drivers, it also prints the following extra -
+ * - ESSID
+ * - BSSID
+ * - Channel
+ * - Region code
+ * - Multicast count
+ * - Multicast addresses
+ */
+static ssize_t
+mwifiex_info_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv =
+ (struct mwifiex_private *) file->private_data;
+ struct net_device *netdev = priv->netdev;
+ struct netdev_hw_addr *ha;
+ struct netdev_queue *txq;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *) page, fmt[64];
+ struct mwifiex_bss_info info;
+ ssize_t ret;
+ int i = 0;
+
+ if (!p)
+ return -ENOMEM;
+
+ memset(&info, 0, sizeof(info));
+ ret = mwifiex_get_bss_info(priv, &info);
+ if (ret)
+ goto free_and_exit;
+
+ mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1);
+
+ if (!priv->version_str[0])
+ mwifiex_get_ver_ext(priv);
+
+ p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
+ p += sprintf(p, "driver_version = %s", fmt);
+ p += sprintf(p, "\nverext = %s", priv->version_str);
+ p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name);
+
+ if (info.bss_mode >= ARRAY_SIZE(bss_modes))
+ p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode);
+ else
+ p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]);
+
+ p += sprintf(p, "media_state=\"%s\"\n",
+ (!priv->media_connected ? "Disconnected" : "Connected"));
+ p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr);
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
+ p += sprintf(p, "multicast_count=\"%d\"\n",
+ netdev_mc_count(netdev));
+ p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid);
+ p += sprintf(p, "bssid=\"%pM\"\n", info.bssid);
+ p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);
+ p += sprintf(p, "country_code = \"%s\"\n", info.country_code);
+
+ netdev_for_each_mc_addr(ha, netdev)
+ p += sprintf(p, "multicast_address[%d]=\"%pM\"\n",
+ i++, ha->addr);
+ }
+
+ p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes);
+ p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes);
+ p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets);
+ p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets);
+ p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped);
+ p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped);
+ p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors);
+ p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors);
+ p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev))
+ ? "on" : "off"));
+ p += sprintf(p, "tx queue");
+ for (i = 0; i < netdev->num_tx_queues; i++) {
+ txq = netdev_get_tx_queue(netdev, i);
+ p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ?
+ "stopped" : "started");
+ }
+ p += sprintf(p, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
+ (unsigned long) p - page);
+
+free_and_exit:
+ free_page(page);
+ return ret;
+}
+
+/*
+ * Proc device dump read handler.
+ *
+ * This function is called when the 'device_dump' file is opened for
+ * reading.
+ * This function dumps driver information and firmware memory segments
+ * (ex. DTCM, ITCM, SQRAM etc.) for
+ * debugging.
+ */
+static ssize_t
+mwifiex_device_dump_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = file->private_data;
+
+ if (!priv->adapter->if_ops.device_dump)
+ return -EIO;
+
+ priv->adapter->if_ops.device_dump(priv->adapter);
+
+ return 0;
+}
+
+/*
+ * Proc getlog file read handler.
+ *
+ * This function is called when the 'getlog' file is opened for reading
+ * It prints the following log information -
+ * - Number of multicast Tx frames
+ * - Number of failed packets
+ * - Number of Tx retries
+ * - Number of multicast Tx retries
+ * - Number of duplicate frames
+ * - Number of RTS successes
+ * - Number of RTS failures
+ * - Number of ACK failures
+ * - Number of fragmented Rx frames
+ * - Number of multicast Rx frames
+ * - Number of FCS errors
+ * - Number of Tx frames
+ * - WEP ICV error counts
+ * - Number of received beacons
+ * - Number of missed beacons
+ */
+static ssize_t
+mwifiex_getlog_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv =
+ (struct mwifiex_private *) file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *) page;
+ ssize_t ret;
+ struct mwifiex_ds_get_stats stats;
+
+ if (!p)
+ return -ENOMEM;
+
+ memset(&stats, 0, sizeof(stats));
+ ret = mwifiex_get_stats_info(priv, &stats);
+ if (ret)
+ goto free_and_exit;
+
+ p += sprintf(p, "\n"
+ "mcasttxframe %u\n"
+ "failed %u\n"
+ "retry %u\n"
+ "multiretry %u\n"
+ "framedup %u\n"
+ "rtssuccess %u\n"
+ "rtsfailure %u\n"
+ "ackfailure %u\n"
+ "rxfrag %u\n"
+ "mcastrxframe %u\n"
+ "fcserror %u\n"
+ "txframe %u\n"
+ "wepicverrcnt-1 %u\n"
+ "wepicverrcnt-2 %u\n"
+ "wepicverrcnt-3 %u\n"
+ "wepicverrcnt-4 %u\n"
+ "bcn_rcv_cnt %u\n"
+ "bcn_miss_cnt %u\n",
+ stats.mcast_tx_frame,
+ stats.failed,
+ stats.retry,
+ stats.multi_retry,
+ stats.frame_dup,
+ stats.rts_success,
+ stats.rts_failure,
+ stats.ack_failure,
+ stats.rx_frag,
+ stats.mcast_rx_frame,
+ stats.fcs_error,
+ stats.tx_frame,
+ stats.wep_icv_error[0],
+ stats.wep_icv_error[1],
+ stats.wep_icv_error[2],
+ stats.wep_icv_error[3],
+ stats.bcn_rcv_cnt,
+ stats.bcn_miss_cnt);
+
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
+ (unsigned long) p - page);
+
+free_and_exit:
+ free_page(page);
+ return ret;
+}
+
+/* Sysfs histogram file read handler.
+ *
+ * This function is called when the 'histogram' file is opened for reading
+ * It prints the following histogram information -
+ * - Number of histogram samples
+ * - Receive packet number of each rx_rate
+ * - Receive packet number of each snr
+ * - Receive packet number of each nosie_flr
+ * - Receive packet number of each signal streath
+ */
+static ssize_t
+mwifiex_histogram_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv =
+ (struct mwifiex_private *)file->private_data;
+ ssize_t ret;
+ struct mwifiex_histogram_data *phist_data;
+ int i, value;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+
+ if (!p)
+ return -ENOMEM;
+
+ if (!priv || !priv->hist_data)
+ return -EFAULT;
+ phist_data = priv->hist_data;
+
+ p += sprintf(p, "\n"
+ "total samples = %d\n",
+ atomic_read(&phist_data->num_samples));
+
+ p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M");
+ p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n");
+ p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M");
+ p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+ p += sprintf(p, "44-53=MCS0-9(VHT:BW20)");
+ p += sprintf(p, "54-63=MCS0-9(VHT:BW40)");
+ p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n");
+ } else {
+ p += sprintf(p, "\n");
+ }
+
+ for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) {
+ value = atomic_read(&phist_data->rx_rate[i]);
+ if (value)
+ p += sprintf(p, "rx_rate[%02d] = %d\n", i, value);
+ }
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+ for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES;
+ i++) {
+ value = atomic_read(&phist_data->rx_rate[i]);
+ if (value)
+ p += sprintf(p, "rx_rate[%02d] = %d\n",
+ i, value);
+ }
+ }
+
+ for (i = 0; i < MWIFIEX_MAX_SNR; i++) {
+ value = atomic_read(&phist_data->snr[i]);
+ if (value)
+ p += sprintf(p, "snr[%02ddB] = %d\n", i, value);
+ }
+ for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {
+ value = atomic_read(&phist_data->noise_flr[i]);
+ if (value)
+ p += sprintf(p, "noise_flr[-%02ddBm] = %d\n",
+ (int)(i-128), value);
+ }
+ for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {
+ value = atomic_read(&phist_data->sig_str[i]);
+ if (value)
+ p += sprintf(p, "sig_strength[-%02ddBm] = %d\n",
+ i, value);
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+ (unsigned long)p - page);
+
+ return ret;
+}
+
+static ssize_t
+mwifiex_histogram_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = (void *)file->private_data;
+
+ if (priv && priv->hist_data)
+ mwifiex_hist_data_reset(priv);
+ return 0;
+}
+
+static struct mwifiex_debug_info info;
+
+/*
+ * Proc debug file read handler.
+ *
+ * This function is called when the 'debug' file is opened for reading
+ * It prints the following log information -
+ * - Interrupt count
+ * - WMM AC VO packets count
+ * - WMM AC VI packets count
+ * - WMM AC BE packets count
+ * - WMM AC BK packets count
+ * - Maximum Tx buffer size
+ * - Tx buffer size
+ * - Current Tx buffer size
+ * - Power Save mode
+ * - Power Save state
+ * - Deep Sleep status
+ * - Device wakeup required status
+ * - Number of wakeup tries
+ * - Host Sleep configured status
+ * - Host Sleep activated status
+ * - Number of Tx timeouts
+ * - Number of command timeouts
+ * - Last timed out command ID
+ * - Last timed out command action
+ * - Last command ID
+ * - Last command action
+ * - Last command index
+ * - Last command response ID
+ * - Last command response index
+ * - Last event
+ * - Last event index
+ * - Number of host to card command failures
+ * - Number of sleep confirm command failures
+ * - Number of host to card data failure
+ * - Number of deauthentication events
+ * - Number of disassociation events
+ * - Number of link lost events
+ * - Number of deauthentication commands
+ * - Number of association success commands
+ * - Number of association failure commands
+ * - Number of commands sent
+ * - Number of data packets sent
+ * - Number of command responses received
+ * - Number of events received
+ * - Tx BA stream table (TID, RA)
+ * - Rx reorder table (TID, TA, Start window, Window size, Buffer)
+ */
+static ssize_t
+mwifiex_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv =
+ (struct mwifiex_private *) file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *) page;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ ret = mwifiex_get_debug_info(priv, &info);
+ if (ret)
+ goto free_and_exit;
+
+ p += mwifiex_debug_info_to_buffer(priv, p, &info);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
+ (unsigned long) p - page);
+
+free_and_exit:
+ free_page(page);
+ return ret;
+}
+
+static u32 saved_reg_type, saved_reg_offset, saved_reg_value;
+
+/*
+ * Proc regrdwr file write handler.
+ *
+ * This function is called when the 'regrdwr' file is opened for writing
+ *
+ * This function can be used to write to a register.
+ */
+static ssize_t
+mwifiex_regrdwr_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ char *buf;
+ int ret;
+ u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ sscanf(buf, "%u %x %x", &reg_type, &reg_offset, &reg_value);
+
+ if (reg_type == 0 || reg_offset == 0) {
+ ret = -EINVAL;
+ goto done;
+ } else {
+ saved_reg_type = reg_type;
+ saved_reg_offset = reg_offset;
+ saved_reg_value = reg_value;
+ ret = count;
+ }
+done:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * Proc regrdwr file read handler.
+ *
+ * This function is called when the 'regrdwr' file is opened for reading
+ *
+ * This function can be used to read from a register.
+ */
+static ssize_t
+mwifiex_regrdwr_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv =
+ (struct mwifiex_private *) file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *) addr;
+ int pos = 0, ret = 0;
+ u32 reg_value;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (!saved_reg_type) {
+ /* No command has been given */
+ pos += snprintf(buf, PAGE_SIZE, "0");
+ goto done;
+ }
+ /* Set command has been given */
+ if (saved_reg_value != UINT_MAX) {
+ ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset,
+ saved_reg_value);
+
+ pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n",
+ saved_reg_type, saved_reg_offset,
+ saved_reg_value);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ goto done;
+ }
+ /* Get command has been given */
+ ret = mwifiex_reg_read(priv, saved_reg_type,
+ saved_reg_offset, &reg_value);
+ if (ret) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type,
+ saved_reg_offset, reg_value);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+done:
+ free_page(addr);
+ return ret;
+}
+
+/* Proc debug_mask file read handler.
+ * This function is called when the 'debug_mask' file is opened for reading
+ * This function can be used read driver debugging mask value.
+ */
+static ssize_t
+mwifiex_debug_mask_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv =
+ (struct mwifiex_private *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)page;
+ size_t ret = 0;
+ int pos = 0;
+
+ if (!buf)
+ return -ENOMEM;
+
+ pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n",
+ priv->adapter->debug_mask);
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ free_page(page);
+ return ret;
+}
+
+/* Proc debug_mask file read handler.
+ * This function is called when the 'debug_mask' file is opened for reading
+ * This function can be used read driver debugging mask value.
+ */
+static ssize_t
+mwifiex_debug_mask_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned long debug_mask;
+ struct mwifiex_private *priv = (void *)file->private_data;
+ char *buf;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ if (kstrtoul(buf, 0, &debug_mask)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ priv->adapter->debug_mask = debug_mask;
+ ret = count;
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* Proc memrw file write handler.
+ * This function is called when the 'memrw' file is opened for writing
+ * This function can be used to write to a memory location.
+ */
+static ssize_t
+mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count,
+ loff_t *ppos)
+{
+ int ret;
+ char cmd;
+ struct mwifiex_ds_mem_rw mem_rw;
+ u16 cmd_action;
+ struct mwifiex_private *priv = (void *)file->private_data;
+ char *buf;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value);
+ if (ret != 3) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((cmd == 'r') || (cmd == 'R')) {
+ cmd_action = HostCmd_ACT_GEN_GET;
+ mem_rw.value = 0;
+ } else if ((cmd == 'w') || (cmd == 'W')) {
+ cmd_action = HostCmd_ACT_GEN_SET;
+ } else {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw));
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0,
+ &mem_rw, true))
+ ret = -1;
+ else
+ ret = count;
+
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* Proc memrw file read handler.
+ * This function is called when the 'memrw' file is opened for reading
+ * This function can be used to read from a memory location.
+ */
+static ssize_t
+mwifiex_memrw_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = (void *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ int ret, pos = 0;
+
+ if (!buf)
+ return -ENOMEM;
+
+ pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr,
+ priv->mem_rw.value);
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ free_page(addr);
+ return ret;
+}
+
+static u32 saved_offset = -1, saved_bytes = -1;
+
+/*
+ * Proc rdeeprom file write handler.
+ *
+ * This function is called when the 'rdeeprom' file is opened for writing
+ *
+ * This function can be used to write to a RDEEPROM location.
+ */
+static ssize_t
+mwifiex_rdeeprom_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ char *buf;
+ int ret = 0;
+ int offset = -1, bytes = -1;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ sscanf(buf, "%d %d", &offset, &bytes);
+
+ if (offset == -1 || bytes == -1) {
+ ret = -EINVAL;
+ goto done;
+ } else {
+ saved_offset = offset;
+ saved_bytes = bytes;
+ ret = count;
+ }
+done:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * Proc rdeeprom read write handler.
+ *
+ * This function is called when the 'rdeeprom' file is opened for reading
+ *
+ * This function can be used to read from a RDEEPROM location.
+ */
+static ssize_t
+mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv =
+ (struct mwifiex_private *) file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *) addr;
+ int pos, ret, i;
+ u8 value[MAX_EEPROM_DATA];
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (saved_offset == -1) {
+ /* No command has been given */
+ pos = snprintf(buf, PAGE_SIZE, "0");
+ goto done;
+ }
+
+ /* Get command has been given */
+ ret = mwifiex_eeprom_read(priv, (u16) saved_offset,
+ (u16) saved_bytes, value);
+ if (ret) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
+
+ for (i = 0; i < saved_bytes; i++)
+ pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]);
+
+done:
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+out_free:
+ free_page(addr);
+ return ret;
+}
+
+/* Proc hscfg file write handler
+ * This function can be used to configure the host sleep parameters.
+ */
+static ssize_t
+mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = (void *)file->private_data;
+ char *buf;
+ int ret, arg_num;
+ struct mwifiex_ds_hs_cfg hscfg;
+ int conditions = HS_CFG_COND_DEF;
+ u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
+
+ memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
+
+ if (arg_num > 3) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Too many arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (arg_num >= 1 && arg_num < 3)
+ mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
+ MWIFIEX_SYNC_CMD, &hscfg);
+
+ if (arg_num) {
+ if (conditions == HS_CFG_CANCEL) {
+ mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);
+ ret = count;
+ goto done;
+ }
+ hscfg.conditions = conditions;
+ }
+ if (arg_num >= 2)
+ hscfg.gpio = gpio;
+ if (arg_num == 3)
+ hscfg.gap = gap;
+
+ hscfg.is_invoke_hostcmd = false;
+ mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
+ MWIFIEX_SYNC_CMD, &hscfg);
+
+ mwifiex_enable_hs(priv->adapter);
+ priv->adapter->hs_enabling = false;
+ ret = count;
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* Proc hscfg file read handler
+ * This function can be used to read host sleep configuration
+ * parameters from driver.
+ */
+static ssize_t
+mwifiex_hscfg_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = (void *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ int pos, ret;
+ struct mwifiex_ds_hs_cfg hscfg;
+
+ if (!buf)
+ return -ENOMEM;
+
+ mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
+ MWIFIEX_SYNC_CMD, &hscfg);
+
+ pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,
+ hscfg.gpio, hscfg.gap);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ free_page(addr);
+ return ret;
+}
+
+static ssize_t
+mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = file->private_data;
+ char buf[3];
+ bool timeshare_coex;
+ int ret;
+ unsigned int len;
+
+ if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
+ return -EOPNOTSUPP;
+
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
+ HostCmd_ACT_GEN_GET, 0, &timeshare_coex, true);
+ if (ret)
+ return ret;
+
+ len = sprintf(buf, "%d\n", timeshare_coex);
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static ssize_t
+mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ bool timeshare_coex;
+ struct mwifiex_private *priv = file->private_data;
+ char kbuf[16];
+ int ret;
+
+ if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
+ return -EOPNOTSUPP;
+
+ memset(kbuf, 0, sizeof(kbuf));
+
+ if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count)))
+ return -EFAULT;
+
+ if (strtobool(kbuf, &timeshare_coex))
+ return -EINVAL;
+
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
+ HostCmd_ACT_GEN_SET, 0, &timeshare_coex, true);
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static ssize_t
+mwifiex_reset_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = file->private_data;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ char cmd;
+ bool result;
+
+ if (copy_from_user(&cmd, ubuf, sizeof(cmd)))
+ return -EFAULT;
+
+ if (strtobool(&cmd, &result))
+ return -EINVAL;
+
+ if (!result)
+ return -EINVAL;
+
+ if (adapter->if_ops.card_reset) {
+ dev_info(adapter->dev, "Resetting per request\n");
+ adapter->hw_status = MWIFIEX_HW_STATUS_RESET;
+ mwifiex_cancel_all_pending_cmd(adapter);
+ adapter->if_ops.card_reset(adapter);
+ }
+
+ return count;
+}
+
+#define MWIFIEX_DFS_ADD_FILE(name) do { \
+ if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \
+ priv, &mwifiex_dfs_##name##_fops)) \
+ return; \
+} while (0);
+
+#define MWIFIEX_DFS_FILE_OPS(name) \
+static const struct file_operations mwifiex_dfs_##name##_fops = { \
+ .read = mwifiex_##name##_read, \
+ .write = mwifiex_##name##_write, \
+ .open = simple_open, \
+};
+
+#define MWIFIEX_DFS_FILE_READ_OPS(name) \
+static const struct file_operations mwifiex_dfs_##name##_fops = { \
+ .read = mwifiex_##name##_read, \
+ .open = simple_open, \
+};
+
+#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \
+static const struct file_operations mwifiex_dfs_##name##_fops = { \
+ .write = mwifiex_##name##_write, \
+ .open = simple_open, \
+};
+
+
+MWIFIEX_DFS_FILE_READ_OPS(info);
+MWIFIEX_DFS_FILE_READ_OPS(debug);
+MWIFIEX_DFS_FILE_READ_OPS(getlog);
+MWIFIEX_DFS_FILE_READ_OPS(device_dump);
+MWIFIEX_DFS_FILE_OPS(regrdwr);
+MWIFIEX_DFS_FILE_OPS(rdeeprom);
+MWIFIEX_DFS_FILE_OPS(memrw);
+MWIFIEX_DFS_FILE_OPS(hscfg);
+MWIFIEX_DFS_FILE_OPS(histogram);
+MWIFIEX_DFS_FILE_OPS(debug_mask);
+MWIFIEX_DFS_FILE_OPS(timeshare_coex);
+MWIFIEX_DFS_FILE_WRITE_OPS(reset);
+
+/*
+ * This function creates the debug FS directory structure and the files.
+ */
+void
+mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
+{
+ if (!mwifiex_dfs_dir || !priv)
+ return;
+
+ priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name,
+ mwifiex_dfs_dir);
+
+ if (!priv->dfs_dev_dir)
+ return;
+
+ MWIFIEX_DFS_ADD_FILE(info);
+ MWIFIEX_DFS_ADD_FILE(debug);
+ MWIFIEX_DFS_ADD_FILE(getlog);
+ MWIFIEX_DFS_ADD_FILE(regrdwr);
+ MWIFIEX_DFS_ADD_FILE(rdeeprom);
+ MWIFIEX_DFS_ADD_FILE(device_dump);
+ MWIFIEX_DFS_ADD_FILE(memrw);
+ MWIFIEX_DFS_ADD_FILE(hscfg);
+ MWIFIEX_DFS_ADD_FILE(histogram);
+ MWIFIEX_DFS_ADD_FILE(debug_mask);
+ MWIFIEX_DFS_ADD_FILE(timeshare_coex);
+ MWIFIEX_DFS_ADD_FILE(reset);
+}
+
+/*
+ * This function removes the debug FS directory structure and the files.
+ */
+void
+mwifiex_dev_debugfs_remove(struct mwifiex_private *priv)
+{
+ if (!priv)
+ return;
+
+ debugfs_remove_recursive(priv->dfs_dev_dir);
+}
+
+/*
+ * This function creates the top level proc directory.
+ */
+void
+mwifiex_debugfs_init(void)
+{
+ if (!mwifiex_dfs_dir)
+ mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL);
+}
+
+/*
+ * This function removes the top level proc directory.
+ */
+void
+mwifiex_debugfs_remove(void)
+{
+ if (mwifiex_dfs_dir)
+ debugfs_remove(mwifiex_dfs_dir);
+}
diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h
new file mode 100644
index 000000000000..d9c15cd36f12
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/decl.h
@@ -0,0 +1,273 @@
+/*
+ * Marvell Wireless LAN device driver: generic data structures and APIs
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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.
+ */
+
+#ifndef _MWIFIEX_DECL_H_
+#define _MWIFIEX_DECL_H_
+
+#undef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/ieee80211.h>
+#include <uapi/linux/if_arp.h>
+#include <net/mac80211.h>
+
+#define MWIFIEX_BSS_COEX_COUNT 2
+#define MWIFIEX_MAX_BSS_NUM (3)
+
+#define MWIFIEX_DMA_ALIGN_SZ 64
+#define MWIFIEX_RX_HEADROOM 64
+#define MAX_TXPD_SZ 32
+#define INTF_HDR_ALIGN 4
+
+#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \
+ MAX_TXPD_SZ)
+#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type)
+ * + sizeof(tx_control)
+ */
+
+#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2
+#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16
+#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8
+
+#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64
+#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64
+#define MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE 16
+
+#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32
+
+#define MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE 16
+
+#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16
+#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64
+#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64
+#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 64
+#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 64
+
+#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff
+
+#define MWIFIEX_RATE_BITMAP_MCS0 32
+
+#define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024)
+#define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024)
+
+#define MAX_BEACON_PERIOD (4000)
+#define MIN_BEACON_PERIOD (50)
+#define MAX_DTIM_PERIOD (100)
+#define MIN_DTIM_PERIOD (1)
+
+#define MWIFIEX_RTS_MIN_VALUE (0)
+#define MWIFIEX_RTS_MAX_VALUE (2347)
+#define MWIFIEX_FRAG_MIN_VALUE (256)
+#define MWIFIEX_FRAG_MAX_VALUE (2346)
+#define MWIFIEX_WMM_VERSION 0x01
+#define MWIFIEX_WMM_SUBTYPE 0x01
+
+#define MWIFIEX_RETRY_LIMIT 14
+#define MWIFIEX_SDIO_BLOCK_SIZE 256
+
+#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
+#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
+#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
+#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3)
+#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4)
+#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5)
+
+#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
+#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
+
+#define MWIFIEX_TDLS_DISABLE_LINK 0x00
+#define MWIFIEX_TDLS_ENABLE_LINK 0x01
+#define MWIFIEX_TDLS_CREATE_LINK 0x02
+#define MWIFIEX_TDLS_CONFIG_LINK 0x03
+
+#define MWIFIEX_TDLS_RSSI_HIGH 50
+#define MWIFIEX_TDLS_RSSI_LOW 55
+#define MWIFIEX_TDLS_MAX_FAIL_COUNT 4
+#define MWIFIEX_AUTO_TDLS_IDLE_TIME 10
+
+/* 54M rates, index from 0 to 11 */
+#define MWIFIEX_RATE_INDEX_MCS0 12
+/* 12-27=MCS0-15(BW20) */
+#define MWIFIEX_BW20_MCS_NUM 15
+
+/* Rate index for OFDM 0 */
+#define MWIFIEX_RATE_INDEX_OFDM0 4
+
+#define MWIFIEX_MAX_STA_NUM 3
+#define MWIFIEX_MAX_UAP_NUM 3
+#define MWIFIEX_MAX_P2P_NUM 3
+
+#define MWIFIEX_A_BAND_START_FREQ 5000
+
+/* SDIO Aggr data packet special info */
+#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255)
+#define BLOCK_NUMBER_OFFSET 15
+#define SDIO_HEADER_OFFSET 28
+
+enum mwifiex_bss_type {
+ MWIFIEX_BSS_TYPE_STA = 0,
+ MWIFIEX_BSS_TYPE_UAP = 1,
+ MWIFIEX_BSS_TYPE_P2P = 2,
+ MWIFIEX_BSS_TYPE_ANY = 0xff,
+};
+
+enum mwifiex_bss_role {
+ MWIFIEX_BSS_ROLE_STA = 0,
+ MWIFIEX_BSS_ROLE_UAP = 1,
+ MWIFIEX_BSS_ROLE_ANY = 0xff,
+};
+
+enum mwifiex_tdls_status {
+ TDLS_NOT_SETUP = 0,
+ TDLS_SETUP_INPROGRESS,
+ TDLS_SETUP_COMPLETE,
+ TDLS_SETUP_FAILURE,
+ TDLS_LINK_TEARDOWN,
+ TDLS_CHAN_SWITCHING,
+ TDLS_IN_BASE_CHAN,
+ TDLS_IN_OFF_CHAN,
+};
+
+enum mwifiex_tdls_error_code {
+ TDLS_ERR_NO_ERROR = 0,
+ TDLS_ERR_INTERNAL_ERROR,
+ TDLS_ERR_MAX_LINKS_EST,
+ TDLS_ERR_LINK_EXISTS,
+ TDLS_ERR_LINK_NONEXISTENT,
+ TDLS_ERR_PEER_STA_UNREACHABLE = 25,
+};
+
+#define BSS_ROLE_BIT_MASK BIT(0)
+
+#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK)
+
+enum mwifiex_data_frame_type {
+ MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0,
+ MWIFIEX_DATA_FRAME_TYPE_802_11,
+};
+
+struct mwifiex_fw_image {
+ u8 *helper_buf;
+ u32 helper_len;
+ u8 *fw_buf;
+ u32 fw_len;
+};
+
+struct mwifiex_802_11_ssid {
+ u32 ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+};
+
+struct mwifiex_wait_queue {
+ wait_queue_head_t wait;
+ int status;
+};
+
+struct mwifiex_rxinfo {
+ struct sk_buff *parent;
+ u8 bss_num;
+ u8 bss_type;
+ u8 use_count;
+ u8 buf_type;
+};
+
+struct mwifiex_txinfo {
+ u32 status_code;
+ u8 flags;
+ u8 bss_num;
+ u8 bss_type;
+ u8 aggr_num;
+ u32 pkt_len;
+ u8 ack_frame_id;
+ u64 cookie;
+};
+
+enum mwifiex_wmm_ac_e {
+ WMM_AC_BK,
+ WMM_AC_BE,
+ WMM_AC_VI,
+ WMM_AC_VO
+} __packed;
+
+struct ieee_types_wmm_ac_parameters {
+ u8 aci_aifsn_bitmap;
+ u8 ecw_bitmap;
+ __le16 tx_op_limit;
+} __packed;
+
+struct mwifiex_types_wmm_info {
+ u8 oui[4];
+ u8 subtype;
+ u8 version;
+ u8 qos_info;
+ u8 reserved;
+ struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
+} __packed;
+
+struct mwifiex_arp_eth_header {
+ struct arphdr hdr;
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+} __packed;
+
+struct mwifiex_chan_stats {
+ u8 chan_num;
+ u8 bandcfg;
+ u8 flags;
+ s8 noise;
+ u16 total_bss;
+ u16 cca_scan_dur;
+ u16 cca_busy_dur;
+} __packed;
+
+#define MWIFIEX_HIST_MAX_SAMPLES 1048576
+#define MWIFIEX_MAX_RX_RATES 44
+#define MWIFIEX_MAX_AC_RX_RATES 74
+#define MWIFIEX_MAX_SNR 256
+#define MWIFIEX_MAX_NOISE_FLR 256
+#define MWIFIEX_MAX_SIG_STRENGTH 256
+
+struct mwifiex_histogram_data {
+ atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES];
+ atomic_t snr[MWIFIEX_MAX_SNR];
+ atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR];
+ atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH];
+ atomic_t num_samples;
+};
+
+struct mwifiex_iface_comb {
+ u8 sta_intf;
+ u8 uap_intf;
+ u8 p2p_intf;
+};
+
+struct mwifiex_radar_params {
+ struct cfg80211_chan_def *chandef;
+ u32 cac_time_ms;
+} __packed;
+
+struct mwifiex_11h_intf_state {
+ bool is_11h_enabled;
+ bool is_11h_active;
+} __packed;
+#endif /* !_MWIFIEX_DECL_H_ */
diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/marvell/mwifiex/ethtool.c
index 58400c69ab26..58400c69ab26 100644
--- a/drivers/net/wireless/mwifiex/ethtool.c
+++ b/drivers/net/wireless/marvell/mwifiex/ethtool.c
diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h
new file mode 100644
index 000000000000..ced7af2be29a
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/fw.h
@@ -0,0 +1,2184 @@
+/*
+ * Marvell Wireless LAN device driver: Firmware specific macros & structures
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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.
+ */
+
+#ifndef _MWIFIEX_FW_H_
+#define _MWIFIEX_FW_H_
+
+#include <linux/if_ether.h>
+
+
+#define INTF_HEADER_LEN 4
+
+struct rfc_1042_hdr {
+ u8 llc_dsap;
+ u8 llc_ssap;
+ u8 llc_ctrl;
+ u8 snap_oui[3];
+ __be16 snap_type;
+};
+
+struct rx_packet_hdr {
+ struct ethhdr eth803_hdr;
+ struct rfc_1042_hdr rfc1042_hdr;
+};
+
+struct tx_packet_hdr {
+ struct ethhdr eth803_hdr;
+ struct rfc_1042_hdr rfc1042_hdr;
+};
+
+#define B_SUPPORTED_RATES 5
+#define G_SUPPORTED_RATES 9
+#define BG_SUPPORTED_RATES 13
+#define A_SUPPORTED_RATES 9
+#define HOSTCMD_SUPPORTED_RATES 14
+#define N_SUPPORTED_RATES 3
+#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \
+ BAND_AN | BAND_AAC)
+
+#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \
+ BIT(13))
+#define IS_SUPPORT_MULTI_BANDS(adapter) \
+ (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT)
+
+/* bit 13: 11ac BAND_AAC
+ * bit 12: reserved for lab testing, will be reused for BAND_AN
+ * bit 11: 11n BAND_GN
+ * bit 10: 11a BAND_A
+ * bit 9: 11g BAND_G
+ * bit 8: 11b BAND_B
+ * Map these bits to band capability by right shifting 8 bits.
+ */
+#define GET_FW_DEFAULT_BANDS(adapter) \
+ (((adapter->fw_cap_info & 0x2f00) >> 8) & \
+ ALL_802_11_BANDS)
+
+#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff
+
+#define KEY_INFO_ENABLED 0x01
+enum KEY_TYPE_ID {
+ KEY_TYPE_ID_WEP = 0,
+ KEY_TYPE_ID_TKIP,
+ KEY_TYPE_ID_AES,
+ KEY_TYPE_ID_WAPI,
+ KEY_TYPE_ID_AES_CMAC,
+};
+
+#define WPA_PN_SIZE 8
+#define KEY_PARAMS_FIXED_LEN 10
+#define KEY_INDEX_MASK 0xf
+#define KEY_API_VER_MAJOR_V2 2
+
+#define KEY_MCAST BIT(0)
+#define KEY_UNICAST BIT(1)
+#define KEY_ENABLED BIT(2)
+#define KEY_DEFAULT BIT(3)
+#define KEY_TX_KEY BIT(4)
+#define KEY_RX_KEY BIT(5)
+#define KEY_IGTK BIT(10)
+
+#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2)
+
+#define MAX_POLL_TRIES 100
+#define MAX_FIRMWARE_POLL_TRIES 100
+
+#define FIRMWARE_READY_SDIO 0xfedc
+#define FIRMWARE_READY_PCIE 0xfedcba00
+
+#define MWIFIEX_COEX_MODE_TIMESHARE 0x01
+#define MWIFIEX_COEX_MODE_SPATIAL 0x82
+
+enum mwifiex_usb_ep {
+ MWIFIEX_USB_EP_CMD_EVENT = 1,
+ MWIFIEX_USB_EP_DATA = 2,
+ MWIFIEX_USB_EP_DATA_CH2 = 3,
+};
+
+enum MWIFIEX_802_11_PRIVACY_FILTER {
+ MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL,
+ MWIFIEX_802_11_PRIV_FILTER_8021X_WEP
+};
+
+#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF)))
+#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR)+(s16)(NF)))
+
+#define UAP_BSS_PARAMS_I 0
+#define UAP_CUSTOM_IE_I 1
+#define MWIFIEX_AUTO_IDX_MASK 0xffff
+#define MWIFIEX_DELETE_MASK 0x0000
+#define MGMT_MASK_ASSOC_REQ 0x01
+#define MGMT_MASK_REASSOC_REQ 0x04
+#define MGMT_MASK_ASSOC_RESP 0x02
+#define MGMT_MASK_REASSOC_RESP 0x08
+#define MGMT_MASK_PROBE_REQ 0x10
+#define MGMT_MASK_PROBE_RESP 0x20
+#define MGMT_MASK_BEACON 0x100
+
+#define TLV_TYPE_UAP_SSID 0x0000
+#define TLV_TYPE_UAP_RATES 0x0001
+#define TLV_TYPE_PWR_CONSTRAINT 0x0020
+
+#define PROPRIETARY_TLV_BASE_ID 0x0100
+#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0)
+#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1)
+#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2)
+#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4)
+#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10)
+#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16)
+#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18)
+#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19)
+#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22)
+#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31)
+#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32)
+#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35)
+#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42)
+#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44)
+#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45)
+#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48)
+#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51)
+#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57)
+#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59)
+#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60)
+#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64)
+#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65)
+#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70)
+#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82)
+#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83)
+#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84)
+#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86)
+#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87)
+#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91)
+#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93)
+#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94)
+#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96)
+#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104)
+#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105)
+#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113)
+#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114)
+#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123)
+#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145)
+#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146)
+#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148)
+#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154)
+#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156)
+#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183)
+#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184)
+#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194)
+#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197)
+#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199)
+#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198)
+#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202)
+#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203)
+#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206)
+
+#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048
+
+#define SSN_MASK 0xfff0
+
+#define BA_RESULT_SUCCESS 0x0
+#define BA_RESULT_TIMEOUT 0x2
+
+#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status)
+
+#define BA_STREAM_NOT_ALLOWED 0xff
+
+#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \
+ priv->adapter->config_bands & BAND_AN) && \
+ priv->curr_bss_params.bss_descriptor.bcn_ht_cap)
+#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\
+ BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS)
+
+#define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096
+#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192
+
+#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
+#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
+#define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15))
+#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16))
+
+#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
+ IEEE80211_HT_CAP_SM_PS)
+
+#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008
+
+#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR
+
+#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC)
+#define MWIFIEX_RX_STBC1 0x0100
+#define MWIFIEX_RX_STBC12 0x0200
+#define MWIFIEX_RX_STBC123 0x0300
+
+/* dev_cap bitmap
+ * BIT
+ * 0-16 reserved
+ * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40
+ * 18-22 reserved
+ * 23 IEEE80211_HT_CAP_SGI_20
+ * 24 IEEE80211_HT_CAP_SGI_40
+ * 25 IEEE80211_HT_CAP_TX_STBC
+ * 26 IEEE80211_HT_CAP_RX_STBC
+ * 27-28 reserved
+ * 29 IEEE80211_HT_CAP_GRN_FLD
+ * 30-31 reserved
+ */
+#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17))
+#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23))
+#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24))
+#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25))
+#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26))
+#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29))
+#define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8))
+#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
+#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
+#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2))
+#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF)
+
+/* httxcfg bitmap
+ * 0 reserved
+ * 1 20/40 Mhz enable(1)/disable(0)
+ * 2-3 reserved
+ * 4 green field enable(1)/disable(0)
+ * 5 short GI in 20 Mhz enable(1)/disable(0)
+ * 6 short GI in 40 Mhz enable(1)/disable(0)
+ * 7-15 reserved
+ */
+#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6))
+
+/* 11AC Tx and Rx MCS map for 1x1 mode:
+ * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1
+ * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams
+ */
+#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe
+
+/* 11AC Tx and Rx MCS map for 2x2 mode:
+ * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2
+ * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams
+ */
+#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa
+
+#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f)
+#define SETHT_MCS32(x) (x[4] |= 1)
+#define HT_STREAM_1X1 0x11
+#define HT_STREAM_2X2 0x22
+
+#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4))
+
+#define LLC_SNAP_LEN 8
+
+/* HW_SPEC fw_cap_info */
+
+#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13))
+
+#define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3)
+#define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3)
+#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \
+ (2 * (nss - 1)))
+#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16)
+#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF)
+
+/* Clear SU Beanformer, MU beanformer, MU beanformee and
+ * sounding dimensions bits
+ */
+#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \
+ (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \
+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \
+ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK)
+
+#define MOD_CLASS_HR_DSSS 0x03
+#define MOD_CLASS_OFDM 0x07
+#define MOD_CLASS_HT 0x08
+#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
+#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010
+#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059
+#define HostCmd_CMD_802_11_ASSOCIATE 0x0012
+#define HostCmd_CMD_802_11_SNMP_MIB 0x0016
+#define HostCmd_CMD_MAC_REG_ACCESS 0x0019
+#define HostCmd_CMD_BBP_REG_ACCESS 0x001a
+#define HostCmd_CMD_RF_REG_ACCESS 0x001b
+#define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad
+#define HostCmd_CMD_RF_TX_PWR 0x001e
+#define HostCmd_CMD_RF_ANTENNA 0x0020
+#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024
+#define HostCmd_CMD_MAC_CONTROL 0x0028
+#define HostCmd_CMD_802_11_AD_HOC_START 0x002b
+#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c
+#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040
+#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D
+#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b
+#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e
+#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c
+#define HostCmd_CMD_WMM_GET_STATUS 0x0071
+#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075
+#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f
+#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083
+#define HostCmd_CMD_MEM_ACCESS 0x0086
+#define HostCmd_CMD_CFG_DATA 0x008f
+#define HostCmd_CMD_VERSION_EXT 0x0097
+#define HostCmd_CMD_MEF_CFG 0x009a
+#define HostCmd_CMD_RSSI_INFO 0x00a4
+#define HostCmd_CMD_FUNC_INIT 0x00a9
+#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa
+#define HOST_CMD_APCMD_SYS_RESET 0x00af
+#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0
+#define HostCmd_CMD_UAP_BSS_START 0x00b1
+#define HostCmd_CMD_UAP_BSS_STOP 0x00b2
+#define HOST_CMD_APCMD_STA_LIST 0x00b3
+#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5
+#define HostCmd_CMD_11N_CFG 0x00cd
+#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce
+#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf
+#define HostCmd_CMD_11N_DELBA 0x00d0
+#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9
+#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd
+#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df
+#define HostCmd_CMD_TXPWR_CFG 0x00d1
+#define HostCmd_CMD_TX_RATE_CFG 0x00d6
+#define HostCmd_CMD_ROBUST_COEX 0x00e0
+#define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4
+#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5
+#define HostCmd_CMD_P2P_MODE_CFG 0x00eb
+#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed
+#define HostCmd_CMD_SET_BSS_MODE 0x00f7
+#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa
+#define HostCmd_CMD_802_11_SCAN_EXT 0x0107
+#define HostCmd_CMD_COALESCE_CFG 0x010a
+#define HostCmd_CMD_MGMT_FRAME_REG 0x010c
+#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d
+#define HostCmd_CMD_11AC_CFG 0x0112
+#define HostCmd_CMD_TDLS_CONFIG 0x0100
+#define HostCmd_CMD_MC_POLICY 0x0121
+#define HostCmd_CMD_TDLS_OPER 0x0122
+#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223
+
+#define PROTOCOL_NO_SECURITY 0x01
+#define PROTOCOL_STATIC_WEP 0x02
+#define PROTOCOL_WPA 0x08
+#define PROTOCOL_WPA2 0x20
+#define PROTOCOL_WPA2_MIXED 0x28
+#define PROTOCOL_EAP 0x40
+#define KEY_MGMT_NONE 0x04
+#define KEY_MGMT_PSK 0x02
+#define KEY_MGMT_EAP 0x01
+#define CIPHER_TKIP 0x04
+#define CIPHER_AES_CCMP 0x08
+#define VALID_CIPHER_BITMAP 0x0c
+
+enum ENH_PS_MODES {
+ EN_PS = 1,
+ DIS_PS = 2,
+ EN_AUTO_DS = 3,
+ DIS_AUTO_DS = 4,
+ SLEEP_CONFIRM = 5,
+ GET_PS = 0,
+ EN_AUTO_PS = 0xff,
+ DIS_AUTO_PS = 0xfe,
+};
+
+enum P2P_MODES {
+ P2P_MODE_DISABLE = 0,
+ P2P_MODE_DEVICE = 1,
+ P2P_MODE_GO = 2,
+ P2P_MODE_CLIENT = 3,
+};
+
+#define HostCmd_RET_BIT 0x8000
+#define HostCmd_ACT_GEN_GET 0x0000
+#define HostCmd_ACT_GEN_SET 0x0001
+#define HostCmd_ACT_GEN_REMOVE 0x0004
+#define HostCmd_ACT_BITWISE_SET 0x0002
+#define HostCmd_ACT_BITWISE_CLR 0x0003
+#define HostCmd_RESULT_OK 0x0000
+
+#define HostCmd_ACT_MAC_RX_ON 0x0001
+#define HostCmd_ACT_MAC_TX_ON 0x0002
+#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008
+#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010
+#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080
+#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100
+#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000
+
+#define HostCmd_BSS_MODE_IBSS 0x0002
+#define HostCmd_BSS_MODE_ANY 0x0003
+
+#define HostCmd_SCAN_RADIO_TYPE_BG 0
+#define HostCmd_SCAN_RADIO_TYPE_A 1
+
+#define HS_CFG_CANCEL 0xffffffff
+#define HS_CFG_COND_DEF 0x00000000
+#define HS_CFG_GPIO_DEF 0xff
+#define HS_CFG_GAP_DEF 0xff
+#define HS_CFG_COND_BROADCAST_DATA 0x00000001
+#define HS_CFG_COND_UNICAST_DATA 0x00000002
+#define HS_CFG_COND_MAC_EVENT 0x00000004
+#define HS_CFG_COND_MULTICAST_DATA 0x00000008
+
+#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB
+#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC
+#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD
+#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE
+#define CONNECT_ERR_STA_FAILURE 0xFFFF
+
+
+#define CMD_F_HOSTCMD (1 << 0)
+
+#define HostCmd_CMD_ID_MASK 0x0fff
+
+#define HostCmd_SEQ_NUM_MASK 0x00ff
+
+#define HostCmd_BSS_NUM_MASK 0x0f00
+
+#define HostCmd_BSS_TYPE_MASK 0xf000
+
+#define HostCmd_ACT_SET_RX 0x0001
+#define HostCmd_ACT_SET_TX 0x0002
+#define HostCmd_ACT_SET_BOTH 0x0003
+
+#define RF_ANTENNA_AUTO 0xFFFF
+
+#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \
+ (((seq) & 0x00ff) | \
+ (((num) & 0x000f) << 8)) | \
+ (((type) & 0x000f) << 12); }
+
+#define HostCmd_GET_SEQ_NO(seq) \
+ ((seq) & HostCmd_SEQ_NUM_MASK)
+
+#define HostCmd_GET_BSS_NO(seq) \
+ (((seq) & HostCmd_BSS_NUM_MASK) >> 8)
+
+#define HostCmd_GET_BSS_TYPE(seq) \
+ (((seq) & HostCmd_BSS_TYPE_MASK) >> 12)
+
+#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001
+#define EVENT_LINK_LOST 0x00000003
+#define EVENT_LINK_SENSED 0x00000004
+#define EVENT_MIB_CHANGED 0x00000006
+#define EVENT_INIT_DONE 0x00000007
+#define EVENT_DEAUTHENTICATED 0x00000008
+#define EVENT_DISASSOCIATED 0x00000009
+#define EVENT_PS_AWAKE 0x0000000a
+#define EVENT_PS_SLEEP 0x0000000b
+#define EVENT_MIC_ERR_MULTICAST 0x0000000d
+#define EVENT_MIC_ERR_UNICAST 0x0000000e
+#define EVENT_DEEP_SLEEP_AWAKE 0x00000010
+#define EVENT_ADHOC_BCN_LOST 0x00000011
+
+#define EVENT_WMM_STATUS_CHANGE 0x00000017
+#define EVENT_BG_SCAN_REPORT 0x00000018
+#define EVENT_RSSI_LOW 0x00000019
+#define EVENT_SNR_LOW 0x0000001a
+#define EVENT_MAX_FAIL 0x0000001b
+#define EVENT_RSSI_HIGH 0x0000001c
+#define EVENT_SNR_HIGH 0x0000001d
+#define EVENT_IBSS_COALESCED 0x0000001e
+#define EVENT_DATA_RSSI_LOW 0x00000024
+#define EVENT_DATA_SNR_LOW 0x00000025
+#define EVENT_DATA_RSSI_HIGH 0x00000026
+#define EVENT_DATA_SNR_HIGH 0x00000027
+#define EVENT_LINK_QUALITY 0x00000028
+#define EVENT_PORT_RELEASE 0x0000002b
+#define EVENT_UAP_STA_DEAUTH 0x0000002c
+#define EVENT_UAP_STA_ASSOC 0x0000002d
+#define EVENT_UAP_BSS_START 0x0000002e
+#define EVENT_PRE_BEACON_LOST 0x00000031
+#define EVENT_ADDBA 0x00000033
+#define EVENT_DELBA 0x00000034
+#define EVENT_BA_STREAM_TIEMOUT 0x00000037
+#define EVENT_AMSDU_AGGR_CTRL 0x00000042
+#define EVENT_UAP_BSS_IDLE 0x00000043
+#define EVENT_UAP_BSS_ACTIVE 0x00000044
+#define EVENT_WEP_ICV_ERR 0x00000046
+#define EVENT_HS_ACT_REQ 0x00000047
+#define EVENT_BW_CHANGE 0x00000048
+#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c
+#define EVENT_HOSTWAKE_STAIE 0x0000004d
+#define EVENT_CHANNEL_SWITCH_ANN 0x00000050
+#define EVENT_TDLS_GENERIC_EVENT 0x00000052
+#define EVENT_RADAR_DETECTED 0x00000053
+#define EVENT_CHANNEL_REPORT_RDY 0x00000054
+#define EVENT_TX_DATA_PAUSE 0x00000055
+#define EVENT_EXT_SCAN_REPORT 0x00000058
+#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f
+#define EVENT_MULTI_CHAN_INFO 0x0000006a
+#define EVENT_TX_STATUS_REPORT 0x00000074
+#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076
+
+#define EVENT_ID_MASK 0xffff
+#define BSS_NUM_MASK 0xf
+
+#define EVENT_GET_BSS_NUM(event_cause) \
+ (((event_cause) >> 16) & BSS_NUM_MASK)
+
+#define EVENT_GET_BSS_TYPE(event_cause) \
+ (((event_cause) >> 24) & 0x00ff)
+
+#define MWIFIEX_MAX_PATTERN_LEN 40
+#define MWIFIEX_MAX_OFFSET_LEN 100
+#define STACK_NBYTES 100
+#define TYPE_DNUM 1
+#define TYPE_BYTESEQ 2
+#define MAX_OPERAND 0x40
+#define TYPE_EQ (MAX_OPERAND+1)
+#define TYPE_EQ_DNUM (MAX_OPERAND+2)
+#define TYPE_EQ_BIT (MAX_OPERAND+3)
+#define TYPE_AND (MAX_OPERAND+4)
+#define TYPE_OR (MAX_OPERAND+5)
+#define MEF_MODE_HOST_SLEEP 1
+#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3
+#define MEF_ACTION_AUTO_ARP 0x10
+#define MWIFIEX_CRITERIA_BROADCAST BIT(0)
+#define MWIFIEX_CRITERIA_UNICAST BIT(1)
+#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
+#define MWIFIEX_MAX_SUPPORTED_IPADDR 4
+
+#define ACT_TDLS_DELETE 0x00
+#define ACT_TDLS_CREATE 0x01
+#define ACT_TDLS_CONFIG 0x02
+
+#define TDLS_EVENT_LINK_TEAR_DOWN 3
+#define TDLS_EVENT_CHAN_SWITCH_RESULT 7
+#define TDLS_EVENT_START_CHAN_SWITCH 8
+#define TDLS_EVENT_CHAN_SWITCH_STOPPED 9
+
+#define TDLS_BASE_CHANNEL 0
+#define TDLS_OFF_CHANNEL 1
+
+#define ACT_TDLS_CS_ENABLE_CONFIG 0x00
+#define ACT_TDLS_CS_INIT 0x06
+#define ACT_TDLS_CS_STOP 0x07
+#define ACT_TDLS_CS_PARAMS 0x08
+
+#define MWIFIEX_DEF_CS_UNIT_TIME 2
+#define MWIFIEX_DEF_CS_THR_OTHERLINK 10
+#define MWIFIEX_DEF_THR_DIRECTLINK 0
+#define MWIFIEX_DEF_CS_TIME 10
+#define MWIFIEX_DEF_CS_TIMEOUT 16
+#define MWIFIEX_DEF_CS_REG_CLASS 12
+#define MWIFIEX_DEF_CS_PERIODICITY 1
+
+#define MWIFIEX_FW_V15 15
+
+#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1)
+
+struct mwifiex_ie_types_header {
+ __le16 type;
+ __le16 len;
+} __packed;
+
+struct mwifiex_ie_types_data {
+ struct mwifiex_ie_types_header header;
+ u8 data[1];
+} __packed;
+
+#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
+#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
+#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10
+#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01
+#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20
+
+struct txpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 tx_pkt_length;
+ __le16 tx_pkt_offset;
+ __le16 tx_pkt_type;
+ __le32 tx_control;
+ u8 priority;
+ u8 flags;
+ u8 pkt_delay_2ms;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
+} __packed;
+
+struct rxpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 rx_pkt_length;
+ __le16 rx_pkt_offset;
+ __le16 rx_pkt_type;
+ __le16 seq_num;
+ u8 priority;
+ u8 rx_rate;
+ s8 snr;
+ s8 nf;
+
+ /* For: Non-802.11 AC cards
+ *
+ * Ht Info [Bit 0] RxRate format: LG=0, HT=1
+ * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1
+ * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1
+ *
+ * For: 802.11 AC cards
+ * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10
+ * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01
+ * BW80 = 10 BW160 = 11
+ * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1
+ * [Bit 5] STBC support Enabled = 1
+ * [Bit 6] LDPC support Enabled = 1
+ * [Bit 7] Reserved
+ */
+ u8 ht_info;
+ u8 reserved[3];
+ u8 flags;
+} __packed;
+
+struct uap_txpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 tx_pkt_length;
+ __le16 tx_pkt_offset;
+ __le16 tx_pkt_type;
+ __le32 tx_control;
+ u8 priority;
+ u8 flags;
+ u8 pkt_delay_2ms;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
+};
+
+struct uap_rxpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 rx_pkt_length;
+ __le16 rx_pkt_offset;
+ __le16 rx_pkt_type;
+ __le16 seq_num;
+ u8 priority;
+ u8 rx_rate;
+ s8 snr;
+ s8 nf;
+ u8 ht_info;
+ u8 reserved[3];
+ u8 flags;
+};
+
+struct mwifiex_fw_chan_stats {
+ u8 chan_num;
+ u8 bandcfg;
+ u8 flags;
+ s8 noise;
+ __le16 total_bss;
+ __le16 cca_scan_dur;
+ __le16 cca_busy_dur;
+} __packed;
+
+enum mwifiex_chan_scan_mode_bitmasks {
+ MWIFIEX_PASSIVE_SCAN = BIT(0),
+ MWIFIEX_DISABLE_CHAN_FILT = BIT(1),
+ MWIFIEX_HIDDEN_SSID_REPORT = BIT(4),
+};
+
+struct mwifiex_chan_scan_param_set {
+ u8 radio_type;
+ u8 chan_number;
+ u8 chan_scan_mode_bitmap;
+ __le16 min_scan_time;
+ __le16 max_scan_time;
+} __packed;
+
+struct mwifiex_ie_types_chan_list_param_set {
+ struct mwifiex_ie_types_header header;
+ struct mwifiex_chan_scan_param_set chan_scan_param[1];
+} __packed;
+
+struct chan_band_param_set {
+ u8 radio_type;
+ u8 chan_number;
+};
+
+struct mwifiex_ie_types_chan_band_list_param_set {
+ struct mwifiex_ie_types_header header;
+ struct chan_band_param_set chan_band_param[1];
+} __packed;
+
+struct mwifiex_ie_types_rates_param_set {
+ struct mwifiex_ie_types_header header;
+ u8 rates[1];
+} __packed;
+
+struct mwifiex_ie_types_ssid_param_set {
+ struct mwifiex_ie_types_header header;
+ u8 ssid[1];
+} __packed;
+
+struct mwifiex_ie_types_num_probes {
+ struct mwifiex_ie_types_header header;
+ __le16 num_probes;
+} __packed;
+
+struct mwifiex_ie_types_scan_chan_gap {
+ struct mwifiex_ie_types_header header;
+ /* time gap in TUs to be used between two consecutive channels scan */
+ __le16 chan_gap;
+} __packed;
+
+struct mwifiex_ietypes_chanstats {
+ struct mwifiex_ie_types_header header;
+ struct mwifiex_fw_chan_stats chanstats[0];
+} __packed;
+
+struct mwifiex_ie_types_wildcard_ssid_params {
+ struct mwifiex_ie_types_header header;
+ u8 max_ssid_length;
+ u8 ssid[1];
+} __packed;
+
+#define TSF_DATA_SIZE 8
+struct mwifiex_ie_types_tsf_timestamp {
+ struct mwifiex_ie_types_header header;
+ u8 tsf_data[1];
+} __packed;
+
+struct mwifiex_cf_param_set {
+ u8 cfp_cnt;
+ u8 cfp_period;
+ __le16 cfp_max_duration;
+ __le16 cfp_duration_remaining;
+} __packed;
+
+struct mwifiex_ibss_param_set {
+ __le16 atim_window;
+} __packed;
+
+struct mwifiex_ie_types_ss_param_set {
+ struct mwifiex_ie_types_header header;
+ union {
+ struct mwifiex_cf_param_set cf_param_set[1];
+ struct mwifiex_ibss_param_set ibss_param_set[1];
+ } cf_ibss;
+} __packed;
+
+struct mwifiex_fh_param_set {
+ __le16 dwell_time;
+ u8 hop_set;
+ u8 hop_pattern;
+ u8 hop_index;
+} __packed;
+
+struct mwifiex_ds_param_set {
+ u8 current_chan;
+} __packed;
+
+struct mwifiex_ie_types_phy_param_set {
+ struct mwifiex_ie_types_header header;
+ union {
+ struct mwifiex_fh_param_set fh_param_set[1];
+ struct mwifiex_ds_param_set ds_param_set[1];
+ } fh_ds;
+} __packed;
+
+struct mwifiex_ie_types_auth_type {
+ struct mwifiex_ie_types_header header;
+ __le16 auth_type;
+} __packed;
+
+struct mwifiex_ie_types_vendor_param_set {
+ struct mwifiex_ie_types_header header;
+ u8 ie[MWIFIEX_MAX_VSIE_LEN];
+};
+
+#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC 60
+
+struct mwifiex_ie_types_tdls_idle_timeout {
+ struct mwifiex_ie_types_header header;
+ __le16 value;
+} __packed;
+
+struct mwifiex_ie_types_rsn_param_set {
+ struct mwifiex_ie_types_header header;
+ u8 rsn_ie[1];
+} __packed;
+
+#define KEYPARAMSET_FIXED_LEN 6
+
+struct mwifiex_ie_type_key_param_set {
+ __le16 type;
+ __le16 length;
+ __le16 key_type_id;
+ __le16 key_info;
+ __le16 key_len;
+ u8 key[50];
+} __packed;
+
+#define IGTK_PN_LEN 8
+
+struct mwifiex_cmac_param {
+ u8 ipn[IGTK_PN_LEN];
+ u8 key[WLAN_KEY_LEN_AES_CMAC];
+} __packed;
+
+struct mwifiex_wep_param {
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_WEP104];
+} __packed;
+
+struct mwifiex_tkip_param {
+ u8 pn[WPA_PN_SIZE];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_TKIP];
+} __packed;
+
+struct mwifiex_aes_param {
+ u8 pn[WPA_PN_SIZE];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_CCMP];
+} __packed;
+
+struct mwifiex_wapi_param {
+ u8 pn[PN_LEN];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_SMS4];
+} __packed;
+
+struct mwifiex_cmac_aes_param {
+ u8 ipn[IGTK_PN_LEN];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_AES_CMAC];
+} __packed;
+
+struct mwifiex_ie_type_key_param_set_v2 {
+ __le16 type;
+ __le16 len;
+ u8 mac_addr[ETH_ALEN];
+ u8 key_idx;
+ u8 key_type;
+ __le16 key_info;
+ union {
+ struct mwifiex_wep_param wep;
+ struct mwifiex_tkip_param tkip;
+ struct mwifiex_aes_param aes;
+ struct mwifiex_wapi_param wapi;
+ struct mwifiex_cmac_aes_param cmac_aes;
+ } key_params;
+} __packed;
+
+struct host_cmd_ds_802_11_key_material_v2 {
+ __le16 action;
+ struct mwifiex_ie_type_key_param_set_v2 key_param_set;
+} __packed;
+
+struct host_cmd_ds_802_11_key_material {
+ __le16 action;
+ struct mwifiex_ie_type_key_param_set key_param_set;
+} __packed;
+
+struct host_cmd_ds_gen {
+ __le16 command;
+ __le16 size;
+ __le16 seq_num;
+ __le16 result;
+};
+
+#define S_DS_GEN sizeof(struct host_cmd_ds_gen)
+
+enum sleep_resp_ctrl {
+ RESP_NOT_NEEDED = 0,
+ RESP_NEEDED,
+};
+
+struct mwifiex_ps_param {
+ __le16 null_pkt_interval;
+ __le16 multiple_dtims;
+ __le16 bcn_miss_timeout;
+ __le16 local_listen_interval;
+ __le16 adhoc_wake_period;
+ __le16 mode;
+ __le16 delay_to_ps;
+};
+
+#define BITMAP_AUTO_DS 0x01
+#define BITMAP_STA_PS 0x10
+
+struct mwifiex_ie_types_auto_ds_param {
+ struct mwifiex_ie_types_header header;
+ __le16 deep_sleep_timeout;
+} __packed;
+
+struct mwifiex_ie_types_ps_param {
+ struct mwifiex_ie_types_header header;
+ struct mwifiex_ps_param param;
+} __packed;
+
+struct host_cmd_ds_802_11_ps_mode_enh {
+ __le16 action;
+
+ union {
+ struct mwifiex_ps_param opt_ps;
+ __le16 ps_bitmap;
+ } params;
+} __packed;
+
+enum API_VER_ID {
+ KEY_API_VER_ID = 1,
+ FW_API_VER_ID = 2,
+};
+
+struct hw_spec_api_rev {
+ struct mwifiex_ie_types_header header;
+ __le16 api_id;
+ u8 major_ver;
+ u8 minor_ver;
+} __packed;
+
+struct host_cmd_ds_get_hw_spec {
+ __le16 hw_if_version;
+ __le16 version;
+ __le16 reserved;
+ __le16 num_of_mcast_adr;
+ u8 permanent_addr[ETH_ALEN];
+ __le16 region_code;
+ __le16 number_of_antenna;
+ __le32 fw_release_number;
+ __le32 reserved_1;
+ __le32 reserved_2;
+ __le32 reserved_3;
+ __le32 fw_cap_info;
+ __le32 dot_11n_dev_cap;
+ u8 dev_mcs_support;
+ __le16 mp_end_port; /* SDIO only, reserved for other interfacces */
+ __le16 mgmt_buf_count; /* mgmt IE buffer count */
+ __le32 reserved_5;
+ __le32 reserved_6;
+ __le32 dot_11ac_dev_cap;
+ __le32 dot_11ac_mcs_support;
+ u8 tlvs[0];
+} __packed;
+
+struct host_cmd_ds_802_11_rssi_info {
+ __le16 action;
+ __le16 ndata;
+ __le16 nbcn;
+ __le16 reserved[9];
+ long long reserved_1;
+};
+
+struct host_cmd_ds_802_11_rssi_info_rsp {
+ __le16 action;
+ __le16 ndata;
+ __le16 nbcn;
+ __le16 data_rssi_last;
+ __le16 data_nf_last;
+ __le16 data_rssi_avg;
+ __le16 data_nf_avg;
+ __le16 bcn_rssi_last;
+ __le16 bcn_nf_last;
+ __le16 bcn_rssi_avg;
+ __le16 bcn_nf_avg;
+ long long tsf_bcn;
+};
+
+struct host_cmd_ds_802_11_mac_address {
+ __le16 action;
+ u8 mac_addr[ETH_ALEN];
+};
+
+struct host_cmd_ds_mac_control {
+ __le16 action;
+ __le16 reserved;
+};
+
+struct host_cmd_ds_mac_multicast_adr {
+ __le16 action;
+ __le16 num_of_adrs;
+ u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
+} __packed;
+
+struct host_cmd_ds_802_11_deauthenticate {
+ u8 mac_addr[ETH_ALEN];
+ __le16 reason_code;
+} __packed;
+
+struct host_cmd_ds_802_11_associate {
+ u8 peer_sta_addr[ETH_ALEN];
+ __le16 cap_info_bitmap;
+ __le16 listen_interval;
+ __le16 beacon_period;
+ u8 dtim_period;
+} __packed;
+
+struct ieee_types_assoc_rsp {
+ __le16 cap_info_bitmap;
+ __le16 status_code;
+ __le16 a_id;
+ u8 ie_buffer[1];
+} __packed;
+
+struct host_cmd_ds_802_11_associate_rsp {
+ struct ieee_types_assoc_rsp assoc_rsp;
+} __packed;
+
+struct ieee_types_cf_param_set {
+ u8 element_id;
+ u8 len;
+ u8 cfp_cnt;
+ u8 cfp_period;
+ __le16 cfp_max_duration;
+ __le16 cfp_duration_remaining;
+} __packed;
+
+struct ieee_types_ibss_param_set {
+ u8 element_id;
+ u8 len;
+ __le16 atim_window;
+} __packed;
+
+union ieee_types_ss_param_set {
+ struct ieee_types_cf_param_set cf_param_set;
+ struct ieee_types_ibss_param_set ibss_param_set;
+} __packed;
+
+struct ieee_types_fh_param_set {
+ u8 element_id;
+ u8 len;
+ __le16 dwell_time;
+ u8 hop_set;
+ u8 hop_pattern;
+ u8 hop_index;
+} __packed;
+
+struct ieee_types_ds_param_set {
+ u8 element_id;
+ u8 len;
+ u8 current_chan;
+} __packed;
+
+union ieee_types_phy_param_set {
+ struct ieee_types_fh_param_set fh_param_set;
+ struct ieee_types_ds_param_set ds_param_set;
+} __packed;
+
+struct ieee_types_oper_mode_ntf {
+ u8 element_id;
+ u8 len;
+ u8 oper_mode;
+} __packed;
+
+struct host_cmd_ds_802_11_ad_hoc_start {
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 bss_mode;
+ __le16 beacon_period;
+ u8 dtim_period;
+ union ieee_types_ss_param_set ss_param_set;
+ union ieee_types_phy_param_set phy_param_set;
+ u16 reserved1;
+ __le16 cap_info_bitmap;
+ u8 data_rate[HOSTCMD_SUPPORTED_RATES];
+} __packed;
+
+struct host_cmd_ds_802_11_ad_hoc_start_result {
+ u8 pad[3];
+ u8 bssid[ETH_ALEN];
+ u8 pad2[2];
+ u8 result;
+} __packed;
+
+struct host_cmd_ds_802_11_ad_hoc_join_result {
+ u8 result;
+} __packed;
+
+struct adhoc_bss_desc {
+ u8 bssid[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 bss_mode;
+ __le16 beacon_period;
+ u8 dtim_period;
+ u8 time_stamp[8];
+ u8 local_time[8];
+ union ieee_types_phy_param_set phy_param_set;
+ union ieee_types_ss_param_set ss_param_set;
+ __le16 cap_info_bitmap;
+ u8 data_rates[HOSTCMD_SUPPORTED_RATES];
+
+ /*
+ * DO NOT ADD ANY FIELDS TO THIS STRUCTURE.
+ * It is used in the Adhoc join command and will cause a
+ * binary layout mismatch with the firmware
+ */
+} __packed;
+
+struct host_cmd_ds_802_11_ad_hoc_join {
+ struct adhoc_bss_desc bss_descriptor;
+ u16 reserved1;
+ u16 reserved2;
+} __packed;
+
+struct host_cmd_ds_802_11_get_log {
+ __le32 mcast_tx_frame;
+ __le32 failed;
+ __le32 retry;
+ __le32 multi_retry;
+ __le32 frame_dup;
+ __le32 rts_success;
+ __le32 rts_failure;
+ __le32 ack_failure;
+ __le32 rx_frag;
+ __le32 mcast_rx_frame;
+ __le32 fcs_error;
+ __le32 tx_frame;
+ __le32 reserved;
+ __le32 wep_icv_err_cnt[4];
+ __le32 bcn_rcv_cnt;
+ __le32 bcn_miss_cnt;
+};
+
+/* Enumeration for rate format */
+enum _mwifiex_rate_format {
+ MWIFIEX_RATE_FORMAT_LG = 0,
+ MWIFIEX_RATE_FORMAT_HT,
+ MWIFIEX_RATE_FORMAT_VHT,
+ MWIFIEX_RATE_FORMAT_AUTO = 0xFF,
+};
+
+struct host_cmd_ds_tx_rate_query {
+ u8 tx_rate;
+ /* Tx Rate Info: For 802.11 AC cards
+ *
+ * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2
+ * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3
+ * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1
+ *
+ * For non-802.11 AC cards
+ * Ht Info [Bit 0] RxRate format: LG=0, HT=1
+ * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1
+ * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1
+ */
+ u8 ht_info;
+} __packed;
+
+struct mwifiex_tx_pause_tlv {
+ struct mwifiex_ie_types_header header;
+ u8 peermac[ETH_ALEN];
+ u8 tx_pause;
+ u8 pkt_cnt;
+} __packed;
+
+enum Host_Sleep_Action {
+ HS_CONFIGURE = 0x0001,
+ HS_ACTIVATE = 0x0002,
+};
+
+struct mwifiex_hs_config_param {
+ __le32 conditions;
+ u8 gpio;
+ u8 gap;
+} __packed;
+
+struct hs_activate_param {
+ __le16 resp_ctrl;
+} __packed;
+
+struct host_cmd_ds_802_11_hs_cfg_enh {
+ __le16 action;
+
+ union {
+ struct mwifiex_hs_config_param hs_config;
+ struct hs_activate_param hs_activate;
+ } params;
+} __packed;
+
+enum SNMP_MIB_INDEX {
+ OP_RATE_SET_I = 1,
+ DTIM_PERIOD_I = 3,
+ RTS_THRESH_I = 5,
+ SHORT_RETRY_LIM_I = 6,
+ LONG_RETRY_LIM_I = 7,
+ FRAG_THRESH_I = 8,
+ DOT11D_I = 9,
+ DOT11H_I = 10,
+};
+
+enum mwifiex_assocmd_failurepoint {
+ MWIFIEX_ASSOC_CMD_SUCCESS = 0,
+ MWIFIEX_ASSOC_CMD_FAILURE_ASSOC,
+ MWIFIEX_ASSOC_CMD_FAILURE_AUTH,
+ MWIFIEX_ASSOC_CMD_FAILURE_JOIN
+};
+
+#define MAX_SNMP_BUF_SIZE 128
+
+struct host_cmd_ds_802_11_snmp_mib {
+ __le16 query_type;
+ __le16 oid;
+ __le16 buf_size;
+ u8 value[1];
+} __packed;
+
+struct mwifiex_rate_scope {
+ __le16 type;
+ __le16 length;
+ __le16 hr_dsss_rate_bitmap;
+ __le16 ofdm_rate_bitmap;
+ __le16 ht_mcs_rate_bitmap[8];
+ __le16 vht_mcs_rate_bitmap[8];
+} __packed;
+
+struct mwifiex_rate_drop_pattern {
+ __le16 type;
+ __le16 length;
+ __le32 rate_drop_mode;
+} __packed;
+
+struct host_cmd_ds_tx_rate_cfg {
+ __le16 action;
+ __le16 cfg_index;
+} __packed;
+
+struct mwifiex_power_group {
+ u8 modulation_class;
+ u8 first_rate_code;
+ u8 last_rate_code;
+ s8 power_step;
+ s8 power_min;
+ s8 power_max;
+ u8 ht_bandwidth;
+ u8 reserved;
+} __packed;
+
+struct mwifiex_types_power_group {
+ __le16 type;
+ __le16 length;
+} __packed;
+
+struct host_cmd_ds_txpwr_cfg {
+ __le16 action;
+ __le16 cfg_index;
+ __le32 mode;
+} __packed;
+
+struct host_cmd_ds_rf_tx_pwr {
+ __le16 action;
+ __le16 cur_level;
+ u8 max_power;
+ u8 min_power;
+} __packed;
+
+struct host_cmd_ds_rf_ant_mimo {
+ __le16 action_tx;
+ __le16 tx_ant_mode;
+ __le16 action_rx;
+ __le16 rx_ant_mode;
+};
+
+struct host_cmd_ds_rf_ant_siso {
+ __le16 action;
+ __le16 ant_mode;
+};
+
+struct host_cmd_ds_tdls_oper {
+ __le16 tdls_action;
+ __le16 reason;
+ u8 peer_mac[ETH_ALEN];
+} __packed;
+
+struct mwifiex_tdls_config {
+ __le16 enable;
+};
+
+struct mwifiex_tdls_config_cs_params {
+ u8 unit_time;
+ u8 thr_otherlink;
+ u8 thr_directlink;
+};
+
+struct mwifiex_tdls_init_cs_params {
+ u8 peer_mac[ETH_ALEN];
+ u8 primary_chan;
+ u8 second_chan_offset;
+ u8 band;
+ __le16 switch_time;
+ __le16 switch_timeout;
+ u8 reg_class;
+ u8 periodicity;
+} __packed;
+
+struct mwifiex_tdls_stop_cs_params {
+ u8 peer_mac[ETH_ALEN];
+};
+
+struct host_cmd_ds_tdls_config {
+ __le16 tdls_action;
+ u8 tdls_data[1];
+} __packed;
+
+struct mwifiex_chan_desc {
+ __le16 start_freq;
+ u8 chan_width;
+ u8 chan_num;
+} __packed;
+
+struct host_cmd_ds_chan_rpt_req {
+ struct mwifiex_chan_desc chan_desc;
+ __le32 msec_dwell_time;
+} __packed;
+
+struct host_cmd_ds_chan_rpt_event {
+ __le32 result;
+ __le64 start_tsf;
+ __le32 duration;
+ u8 tlvbuf[0];
+} __packed;
+
+struct host_cmd_sdio_sp_rx_aggr_cfg {
+ u8 action;
+ u8 enable;
+ __le16 block_size;
+} __packed;
+
+struct mwifiex_fixed_bcn_param {
+ __le64 timestamp;
+ __le16 beacon_period;
+ __le16 cap_info_bitmap;
+} __packed;
+
+struct mwifiex_event_scan_result {
+ __le16 event_id;
+ u8 bss_index;
+ u8 bss_type;
+ u8 more_event;
+ u8 reserved[3];
+ __le16 buf_size;
+ u8 num_of_set;
+} __packed;
+
+struct tx_status_event {
+ u8 packet_type;
+ u8 tx_token_id;
+ u8 status;
+} __packed;
+
+#define MWIFIEX_USER_SCAN_CHAN_MAX 50
+
+#define MWIFIEX_MAX_SSID_LIST_LENGTH 10
+
+struct mwifiex_scan_cmd_config {
+ /*
+ * BSS mode to be sent in the firmware command
+ */
+ u8 bss_mode;
+
+ /* Specific BSSID used to filter scan results in the firmware */
+ u8 specific_bssid[ETH_ALEN];
+
+ /* Length of TLVs sent in command starting at tlvBuffer */
+ u32 tlv_buf_len;
+
+ /*
+ * SSID TLV(s) and ChanList TLVs to be sent in the firmware command
+ *
+ * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set
+ * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set
+ */
+ u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored
+ here */
+} __packed;
+
+struct mwifiex_user_scan_chan {
+ u8 chan_number;
+ u8 radio_type;
+ u8 scan_type;
+ u8 reserved;
+ u32 scan_time;
+} __packed;
+
+struct mwifiex_user_scan_cfg {
+ /*
+ * BSS mode to be sent in the firmware command
+ */
+ u8 bss_mode;
+ /* Configure the number of probe requests for active chan scans */
+ u8 num_probes;
+ u8 reserved;
+ /* BSSID filter sent in the firmware command to limit the results */
+ u8 specific_bssid[ETH_ALEN];
+ /* SSID filter list used in the firmware to limit the scan results */
+ struct cfg80211_ssid *ssid_list;
+ u8 num_ssids;
+ /* Variable number (fixed maximum) of channels to scan up */
+ struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX];
+ u16 scan_chan_gap;
+} __packed;
+
+struct ie_body {
+ u8 grp_key_oui[4];
+ u8 ptk_cnt[2];
+ u8 ptk_body[4];
+} __packed;
+
+struct host_cmd_ds_802_11_scan {
+ u8 bss_mode;
+ u8 bssid[ETH_ALEN];
+ u8 tlv_buffer[1];
+} __packed;
+
+struct host_cmd_ds_802_11_scan_rsp {
+ __le16 bss_descript_size;
+ u8 number_of_sets;
+ u8 bss_desc_and_tlv_buffer[1];
+} __packed;
+
+struct host_cmd_ds_802_11_scan_ext {
+ u32 reserved;
+ u8 tlv_buffer[1];
+} __packed;
+
+struct mwifiex_ie_types_bss_mode {
+ struct mwifiex_ie_types_header header;
+ u8 bss_mode;
+} __packed;
+
+struct mwifiex_ie_types_bss_scan_rsp {
+ struct mwifiex_ie_types_header header;
+ u8 bssid[ETH_ALEN];
+ u8 frame_body[1];
+} __packed;
+
+struct mwifiex_ie_types_bss_scan_info {
+ struct mwifiex_ie_types_header header;
+ __le16 rssi;
+ __le16 anpi;
+ u8 cca_busy_fraction;
+ u8 radio_type;
+ u8 channel;
+ u8 reserved;
+ __le64 tsf;
+} __packed;
+
+struct host_cmd_ds_802_11_bg_scan_query {
+ u8 flush;
+} __packed;
+
+struct host_cmd_ds_802_11_bg_scan_query_rsp {
+ __le32 report_condition;
+ struct host_cmd_ds_802_11_scan_rsp scan_resp;
+} __packed;
+
+struct mwifiex_ietypes_domain_param_set {
+ struct mwifiex_ie_types_header header;
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ struct ieee80211_country_ie_triplet triplet[1];
+} __packed;
+
+struct host_cmd_ds_802_11d_domain_info {
+ __le16 action;
+ struct mwifiex_ietypes_domain_param_set domain;
+} __packed;
+
+struct host_cmd_ds_802_11d_domain_info_rsp {
+ __le16 action;
+ struct mwifiex_ietypes_domain_param_set domain;
+} __packed;
+
+struct host_cmd_ds_11n_addba_req {
+ u8 add_req_result;
+ u8 peer_mac_addr[ETH_ALEN];
+ u8 dialog_token;
+ __le16 block_ack_param_set;
+ __le16 block_ack_tmo;
+ __le16 ssn;
+} __packed;
+
+struct host_cmd_ds_11n_addba_rsp {
+ u8 add_rsp_result;
+ u8 peer_mac_addr[ETH_ALEN];
+ u8 dialog_token;
+ __le16 status_code;
+ __le16 block_ack_param_set;
+ __le16 block_ack_tmo;
+ __le16 ssn;
+} __packed;
+
+struct host_cmd_ds_11n_delba {
+ u8 del_result;
+ u8 peer_mac_addr[ETH_ALEN];
+ __le16 del_ba_param_set;
+ __le16 reason_code;
+ u8 reserved;
+} __packed;
+
+struct host_cmd_ds_11n_batimeout {
+ u8 tid;
+ u8 peer_mac_addr[ETH_ALEN];
+ u8 origninator;
+} __packed;
+
+struct host_cmd_ds_11n_cfg {
+ __le16 action;
+ __le16 ht_tx_cap;
+ __le16 ht_tx_info;
+ __le16 misc_config; /* Needed for 802.11AC cards only */
+} __packed;
+
+struct host_cmd_ds_txbuf_cfg {
+ __le16 action;
+ __le16 buff_size;
+ __le16 mp_end_port; /* SDIO only, reserved for other interfacces */
+ __le16 reserved3;
+} __packed;
+
+struct host_cmd_ds_amsdu_aggr_ctrl {
+ __le16 action;
+ __le16 enable;
+ __le16 curr_buf_size;
+} __packed;
+
+struct host_cmd_ds_sta_deauth {
+ u8 mac[ETH_ALEN];
+ __le16 reason;
+} __packed;
+
+struct mwifiex_ie_types_sta_info {
+ struct mwifiex_ie_types_header header;
+ u8 mac[ETH_ALEN];
+ u8 power_mfg_status;
+ s8 rssi;
+};
+
+struct host_cmd_ds_sta_list {
+ u16 sta_count;
+ u8 tlv[0];
+} __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];
+};
+
+struct mwifiex_ie_types_wmm_queue_status {
+ struct mwifiex_ie_types_header header;
+ u8 queue_index;
+ u8 disabled;
+ __le16 medium_time;
+ u8 flow_required;
+ u8 flow_created;
+ u32 reserved;
+};
+
+struct ieee_types_vendor_header {
+ u8 element_id;
+ u8 len;
+ u8 oui[4]; /* 0~2: oui, 3: oui_type */
+ u8 oui_subtype;
+ u8 version;
+} __packed;
+
+struct ieee_types_wmm_parameter {
+ /*
+ * WMM Parameter IE - Vendor Specific Header:
+ * element_id [221/0xdd]
+ * Len [24]
+ * Oui [00:50:f2]
+ * OuiType [2]
+ * OuiSubType [1]
+ * Version [1]
+ */
+ struct ieee_types_vendor_header vend_hdr;
+ u8 qos_info_bitmap;
+ u8 reserved;
+ struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
+} __packed;
+
+struct ieee_types_wmm_info {
+
+ /*
+ * WMM Info IE - Vendor Specific Header:
+ * element_id [221/0xdd]
+ * Len [7]
+ * Oui [00:50:f2]
+ * OuiType [2]
+ * OuiSubType [0]
+ * Version [1]
+ */
+ struct ieee_types_vendor_header vend_hdr;
+
+ u8 qos_info_bitmap;
+} __packed;
+
+struct host_cmd_ds_wmm_get_status {
+ u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) *
+ IEEE80211_NUM_ACS];
+ u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2];
+} __packed;
+
+struct mwifiex_wmm_ac_status {
+ u8 disabled;
+ u8 flow_required;
+ u8 flow_created;
+};
+
+struct mwifiex_ie_types_htcap {
+ struct mwifiex_ie_types_header header;
+ struct ieee80211_ht_cap ht_cap;
+} __packed;
+
+struct mwifiex_ie_types_vhtcap {
+ struct mwifiex_ie_types_header header;
+ struct ieee80211_vht_cap vht_cap;
+} __packed;
+
+struct mwifiex_ie_types_aid {
+ struct mwifiex_ie_types_header header;
+ __le16 aid;
+} __packed;
+
+struct mwifiex_ie_types_oper_mode_ntf {
+ struct mwifiex_ie_types_header header;
+ u8 oper_mode;
+} __packed;
+
+/* VHT Operations IE */
+struct mwifiex_ie_types_vht_oper {
+ struct mwifiex_ie_types_header header;
+ u8 chan_width;
+ u8 chan_center_freq_1;
+ u8 chan_center_freq_2;
+ /* Basic MCS set map, each 2 bits stands for a NSS */
+ __le16 basic_mcs_map;
+} __packed;
+
+struct mwifiex_ie_types_wmmcap {
+ struct mwifiex_ie_types_header header;
+ struct mwifiex_types_wmm_info wmm_info;
+} __packed;
+
+struct mwifiex_ie_types_htinfo {
+ struct mwifiex_ie_types_header header;
+ struct ieee80211_ht_operation ht_oper;
+} __packed;
+
+struct mwifiex_ie_types_2040bssco {
+ struct mwifiex_ie_types_header header;
+ u8 bss_co_2040;
+} __packed;
+
+struct mwifiex_ie_types_extcap {
+ struct mwifiex_ie_types_header header;
+ u8 ext_capab[0];
+} __packed;
+
+struct host_cmd_ds_mem_access {
+ __le16 action;
+ __le16 reserved;
+ __le32 addr;
+ __le32 value;
+};
+
+struct mwifiex_ie_types_qos_info {
+ struct mwifiex_ie_types_header header;
+ u8 qos_info;
+} __packed;
+
+struct host_cmd_ds_mac_reg_access {
+ __le16 action;
+ __le16 offset;
+ __le32 value;
+} __packed;
+
+struct host_cmd_ds_bbp_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __packed;
+
+struct host_cmd_ds_rf_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __packed;
+
+struct host_cmd_ds_pmic_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __packed;
+
+struct host_cmd_ds_802_11_eeprom_access {
+ __le16 action;
+
+ __le16 offset;
+ __le16 byte_count;
+ u8 value;
+} __packed;
+
+struct mwifiex_assoc_event {
+ u8 sta_addr[ETH_ALEN];
+ __le16 type;
+ __le16 len;
+ __le16 frame_control;
+ __le16 cap_info;
+ __le16 listen_interval;
+ u8 data[0];
+} __packed;
+
+struct host_cmd_ds_sys_config {
+ __le16 action;
+ u8 tlv[0];
+};
+
+struct host_cmd_11ac_vht_cfg {
+ __le16 action;
+ u8 band_config;
+ u8 misc_config;
+ __le32 cap_info;
+ __le32 mcs_tx_set;
+ __le32 mcs_rx_set;
+} __packed;
+
+struct host_cmd_tlv_akmp {
+ struct mwifiex_ie_types_header header;
+ __le16 key_mgmt;
+ __le16 key_mgmt_operation;
+} __packed;
+
+struct host_cmd_tlv_pwk_cipher {
+ struct mwifiex_ie_types_header header;
+ __le16 proto;
+ u8 cipher;
+ u8 reserved;
+} __packed;
+
+struct host_cmd_tlv_gwk_cipher {
+ struct mwifiex_ie_types_header header;
+ u8 cipher;
+ u8 reserved;
+} __packed;
+
+struct host_cmd_tlv_passphrase {
+ struct mwifiex_ie_types_header header;
+ u8 passphrase[0];
+} __packed;
+
+struct host_cmd_tlv_wep_key {
+ struct mwifiex_ie_types_header header;
+ u8 key_index;
+ u8 is_default;
+ u8 key[1];
+};
+
+struct host_cmd_tlv_auth_type {
+ struct mwifiex_ie_types_header header;
+ u8 auth_type;
+} __packed;
+
+struct host_cmd_tlv_encrypt_protocol {
+ struct mwifiex_ie_types_header header;
+ __le16 proto;
+} __packed;
+
+struct host_cmd_tlv_ssid {
+ struct mwifiex_ie_types_header header;
+ u8 ssid[0];
+} __packed;
+
+struct host_cmd_tlv_rates {
+ struct mwifiex_ie_types_header header;
+ u8 rates[0];
+} __packed;
+
+struct mwifiex_ie_types_bssid_list {
+ struct mwifiex_ie_types_header header;
+ u8 bssid[ETH_ALEN];
+} __packed;
+
+struct host_cmd_tlv_bcast_ssid {
+ struct mwifiex_ie_types_header header;
+ u8 bcast_ctl;
+} __packed;
+
+struct host_cmd_tlv_beacon_period {
+ struct mwifiex_ie_types_header header;
+ __le16 period;
+} __packed;
+
+struct host_cmd_tlv_dtim_period {
+ struct mwifiex_ie_types_header header;
+ u8 period;
+} __packed;
+
+struct host_cmd_tlv_frag_threshold {
+ struct mwifiex_ie_types_header header;
+ __le16 frag_thr;
+} __packed;
+
+struct host_cmd_tlv_rts_threshold {
+ struct mwifiex_ie_types_header header;
+ __le16 rts_thr;
+} __packed;
+
+struct host_cmd_tlv_retry_limit {
+ struct mwifiex_ie_types_header header;
+ u8 limit;
+} __packed;
+
+struct host_cmd_tlv_mac_addr {
+ struct mwifiex_ie_types_header header;
+ u8 mac_addr[ETH_ALEN];
+} __packed;
+
+struct host_cmd_tlv_channel_band {
+ struct mwifiex_ie_types_header header;
+ u8 band_config;
+ u8 channel;
+} __packed;
+
+struct host_cmd_tlv_ageout_timer {
+ struct mwifiex_ie_types_header header;
+ __le32 sta_ao_timer;
+} __packed;
+
+struct host_cmd_tlv_power_constraint {
+ struct mwifiex_ie_types_header header;
+ u8 constraint;
+} __packed;
+
+struct mwifiex_ie_types_btcoex_scan_time {
+ struct mwifiex_ie_types_header header;
+ u8 coex_scan;
+ u8 reserved;
+ u16 min_scan_time;
+ u16 max_scan_time;
+} __packed;
+
+struct mwifiex_ie_types_btcoex_aggr_win_size {
+ struct mwifiex_ie_types_header header;
+ u8 coex_win_size;
+ u8 tx_win_size;
+ u8 rx_win_size;
+ u8 reserved;
+} __packed;
+
+struct mwifiex_ie_types_robust_coex {
+ struct mwifiex_ie_types_header header;
+ __le32 mode;
+} __packed;
+
+struct host_cmd_ds_version_ext {
+ u8 version_str_sel;
+ char version_str[128];
+} __packed;
+
+struct host_cmd_ds_mgmt_frame_reg {
+ __le16 action;
+ __le32 mask;
+} __packed;
+
+struct host_cmd_ds_p2p_mode_cfg {
+ __le16 action;
+ __le16 mode;
+} __packed;
+
+struct host_cmd_ds_remain_on_chan {
+ __le16 action;
+ u8 status;
+ u8 reserved;
+ u8 band_cfg;
+ u8 channel;
+ __le32 duration;
+} __packed;
+
+struct host_cmd_ds_802_11_ibss_status {
+ __le16 action;
+ __le16 enable;
+ u8 bssid[ETH_ALEN];
+ __le16 beacon_interval;
+ __le16 atim_window;
+ __le16 use_g_rate_protect;
+} __packed;
+
+struct mwifiex_fw_mef_entry {
+ u8 mode;
+ u8 action;
+ __le16 exprsize;
+ u8 expr[0];
+} __packed;
+
+struct host_cmd_ds_mef_cfg {
+ __le32 criteria;
+ __le16 num_entries;
+ struct mwifiex_fw_mef_entry mef_entry[0];
+} __packed;
+
+#define CONNECTION_TYPE_INFRA 0
+#define CONNECTION_TYPE_ADHOC 1
+#define CONNECTION_TYPE_AP 2
+
+struct host_cmd_ds_set_bss_mode {
+ u8 con_type;
+} __packed;
+
+struct host_cmd_ds_pcie_details {
+ /* TX buffer descriptor ring address */
+ u32 txbd_addr_lo;
+ u32 txbd_addr_hi;
+ /* TX buffer descriptor ring count */
+ u32 txbd_count;
+
+ /* RX buffer descriptor ring address */
+ u32 rxbd_addr_lo;
+ u32 rxbd_addr_hi;
+ /* RX buffer descriptor ring count */
+ u32 rxbd_count;
+
+ /* Event buffer descriptor ring address */
+ u32 evtbd_addr_lo;
+ u32 evtbd_addr_hi;
+ /* Event buffer descriptor ring count */
+ u32 evtbd_count;
+
+ /* Sleep cookie buffer physical address */
+ u32 sleep_cookie_addr_lo;
+ u32 sleep_cookie_addr_hi;
+} __packed;
+
+struct mwifiex_ie_types_rssi_threshold {
+ struct mwifiex_ie_types_header header;
+ u8 abs_value;
+ u8 evt_freq;
+} __packed;
+
+#define MWIFIEX_DFS_REC_HDR_LEN 8
+#define MWIFIEX_DFS_REC_HDR_NUM 10
+#define MWIFIEX_BIN_COUNTER_LEN 7
+
+struct mwifiex_radar_det_event {
+ __le32 detect_count;
+ u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/
+ u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/
+ __le16 pw_chirp_type;
+ u8 pw_chirp_idx;
+ u8 pw_value;
+ u8 pri_radar_type;
+ u8 pri_bincnt;
+ u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN];
+ u8 num_dfs_records;
+ u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN];
+ __le32 passed;
+} __packed;
+
+struct mwifiex_ie_types_multi_chan_info {
+ struct mwifiex_ie_types_header header;
+ __le16 status;
+ u8 tlv_buffer[0];
+} __packed;
+
+struct mwifiex_ie_types_mc_group_info {
+ struct mwifiex_ie_types_header header;
+ u8 chan_group_id;
+ u8 chan_buf_weight;
+ u8 band_config;
+ u8 chan_num;
+ u32 chan_time;
+ u32 reserved;
+ union {
+ u8 sdio_func_num;
+ u8 usb_ep_num;
+ } hid_num;
+ u8 intf_num;
+ u8 bss_type_numlist[0];
+} __packed;
+
+struct meas_rpt_map {
+ u8 rssi:3;
+ u8 unmeasured:1;
+ u8 radar:1;
+ u8 unidentified_sig:1;
+ u8 ofdm_preamble:1;
+ u8 bss:1;
+} __packed;
+
+struct mwifiex_ie_types_chan_rpt_data {
+ struct mwifiex_ie_types_header header;
+ struct meas_rpt_map map;
+} __packed;
+
+struct host_cmd_ds_802_11_subsc_evt {
+ __le16 action;
+ __le16 events;
+} __packed;
+
+struct chan_switch_result {
+ u8 cur_chan;
+ u8 status;
+ u8 reason;
+} __packed;
+
+struct mwifiex_tdls_generic_event {
+ __le16 type;
+ u8 peer_mac[ETH_ALEN];
+ union {
+ struct chan_switch_result switch_result;
+ u8 cs_stop_reason;
+ __le16 reason_code;
+ __le16 reserved;
+ } u;
+} __packed;
+
+struct mwifiex_ie {
+ __le16 ie_index;
+ __le16 mgmt_subtype_mask;
+ __le16 ie_length;
+ u8 ie_buffer[IEEE_MAX_IE_SIZE];
+} __packed;
+
+#define MAX_MGMT_IE_INDEX 16
+struct mwifiex_ie_list {
+ __le16 type;
+ __le16 len;
+ struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX];
+} __packed;
+
+struct coalesce_filt_field_param {
+ u8 operation;
+ u8 operand_len;
+ __le16 offset;
+ u8 operand_byte_stream[4];
+};
+
+struct coalesce_receive_filt_rule {
+ struct mwifiex_ie_types_header header;
+ u8 num_of_fields;
+ u8 pkt_type;
+ __le16 max_coalescing_delay;
+ struct coalesce_filt_field_param params[0];
+} __packed;
+
+struct host_cmd_ds_coalesce_cfg {
+ __le16 action;
+ __le16 num_of_rules;
+ struct coalesce_receive_filt_rule rule[0];
+} __packed;
+
+struct host_cmd_ds_multi_chan_policy {
+ __le16 action;
+ __le16 policy;
+} __packed;
+
+struct host_cmd_ds_robust_coex {
+ __le16 action;
+ __le16 reserved;
+} __packed;
+
+struct host_cmd_ds_command {
+ __le16 command;
+ __le16 size;
+ __le16 seq_num;
+ __le16 result;
+ union {
+ struct host_cmd_ds_get_hw_spec hw_spec;
+ struct host_cmd_ds_mac_control mac_ctrl;
+ struct host_cmd_ds_802_11_mac_address mac_addr;
+ struct host_cmd_ds_mac_multicast_adr mc_addr;
+ struct host_cmd_ds_802_11_get_log get_log;
+ struct host_cmd_ds_802_11_rssi_info rssi_info;
+ struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp;
+ struct host_cmd_ds_802_11_snmp_mib smib;
+ struct host_cmd_ds_tx_rate_query tx_rate;
+ struct host_cmd_ds_tx_rate_cfg tx_rate_cfg;
+ struct host_cmd_ds_txpwr_cfg txp_cfg;
+ struct host_cmd_ds_rf_tx_pwr txp;
+ struct host_cmd_ds_rf_ant_mimo ant_mimo;
+ struct host_cmd_ds_rf_ant_siso ant_siso;
+ struct host_cmd_ds_802_11_ps_mode_enh psmode_enh;
+ struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg;
+ struct host_cmd_ds_802_11_scan scan;
+ struct host_cmd_ds_802_11_scan_ext ext_scan;
+ struct host_cmd_ds_802_11_scan_rsp scan_resp;
+ struct host_cmd_ds_802_11_bg_scan_query bg_scan_query;
+ struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp;
+ struct host_cmd_ds_802_11_associate associate;
+ struct host_cmd_ds_802_11_associate_rsp associate_rsp;
+ struct host_cmd_ds_802_11_deauthenticate deauth;
+ struct host_cmd_ds_802_11_ad_hoc_start adhoc_start;
+ struct host_cmd_ds_802_11_ad_hoc_start_result start_result;
+ struct host_cmd_ds_802_11_ad_hoc_join_result join_result;
+ struct host_cmd_ds_802_11_ad_hoc_join adhoc_join;
+ struct host_cmd_ds_802_11d_domain_info domain_info;
+ struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp;
+ struct host_cmd_ds_11n_addba_req add_ba_req;
+ struct host_cmd_ds_11n_addba_rsp add_ba_rsp;
+ struct host_cmd_ds_11n_delba del_ba;
+ struct host_cmd_ds_txbuf_cfg tx_buf;
+ struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl;
+ struct host_cmd_ds_11n_cfg htcfg;
+ struct host_cmd_ds_wmm_get_status get_wmm_status;
+ struct host_cmd_ds_802_11_key_material key_material;
+ struct host_cmd_ds_802_11_key_material_v2 key_material_v2;
+ struct host_cmd_ds_version_ext verext;
+ struct host_cmd_ds_mgmt_frame_reg reg_mask;
+ struct host_cmd_ds_remain_on_chan roc_cfg;
+ struct host_cmd_ds_p2p_mode_cfg mode_cfg;
+ struct host_cmd_ds_802_11_ibss_status ibss_coalescing;
+ struct host_cmd_ds_mef_cfg mef_cfg;
+ struct host_cmd_ds_mem_access mem;
+ struct host_cmd_ds_mac_reg_access mac_reg;
+ struct host_cmd_ds_bbp_reg_access bbp_reg;
+ struct host_cmd_ds_rf_reg_access rf_reg;
+ struct host_cmd_ds_pmic_reg_access pmic_reg;
+ struct host_cmd_ds_set_bss_mode bss_mode;
+ struct host_cmd_ds_pcie_details pcie_host_spec;
+ struct host_cmd_ds_802_11_eeprom_access eeprom;
+ struct host_cmd_ds_802_11_subsc_evt subsc_evt;
+ struct host_cmd_ds_sys_config uap_sys_config;
+ struct host_cmd_ds_sta_deauth sta_deauth;
+ struct host_cmd_ds_sta_list sta_list;
+ struct host_cmd_11ac_vht_cfg vht_cfg;
+ struct host_cmd_ds_coalesce_cfg coalesce_cfg;
+ struct host_cmd_ds_tdls_config tdls_config;
+ struct host_cmd_ds_tdls_oper tdls_oper;
+ struct host_cmd_ds_chan_rpt_req chan_rpt_req;
+ struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
+ struct host_cmd_ds_multi_chan_policy mc_policy;
+ struct host_cmd_ds_robust_coex coex;
+ } params;
+} __packed;
+
+struct mwifiex_opt_sleep_confirm {
+ __le16 command;
+ __le16 size;
+ __le16 seq_num;
+ __le16 result;
+ __le16 action;
+ __le16 resp_ctrl;
+} __packed;
+#endif /* !_MWIFIEX_FW_H_ */
diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c
new file mode 100644
index 000000000000..c488c3068abc
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/ie.c
@@ -0,0 +1,488 @@
+/*
+ * Marvell Wireless LAN device driver: management IE handling- setting and
+ * deleting IE.
+ *
+ * Copyright (C) 2012-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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"
+
+/* This function checks if current IE index is used by any on other interface.
+ * Return: -1: yes, current IE index is used by someone else.
+ * 0: no, current IE index is NOT used by other interface.
+ */
+static int
+mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx)
+{
+ int i;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_ie *ie;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i] != priv) {
+ ie = &adapter->priv[i]->mgmt_ie[idx];
+ if (ie->mgmt_subtype_mask && ie->ie_length)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Get unused IE index. This index will be used for setting new IE */
+static int
+mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask,
+ struct mwifiex_ie *ie, u16 *index)
+{
+ u16 mask, len, i;
+
+ for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
+ mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
+ len = le16_to_cpu(ie->ie_length);
+
+ if (mask == MWIFIEX_AUTO_IDX_MASK)
+ continue;
+
+ if (mask == subtype_mask) {
+ if (len > IEEE_MAX_IE_SIZE)
+ continue;
+
+ *index = i;
+ return 0;
+ }
+
+ if (!priv->mgmt_ie[i].ie_length) {
+ if (mwifiex_ie_index_used_by_other_intf(priv, i))
+ continue;
+
+ *index = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/* This function prepares IE data buffer for command to be sent to FW */
+static int
+mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
+ struct mwifiex_ie_list *ie_list)
+{
+ u16 travel_len, index, mask;
+ s16 input_len, tlv_len;
+ struct mwifiex_ie *ie;
+ u8 *tmp;
+
+ input_len = le16_to_cpu(ie_list->len);
+ travel_len = sizeof(struct mwifiex_ie_types_header);
+
+ ie_list->len = 0;
+
+ while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
+ ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
+ tlv_len = le16_to_cpu(ie->ie_length);
+ travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
+
+ if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
+ return -1;
+ index = le16_to_cpu(ie->ie_index);
+ mask = le16_to_cpu(ie->mgmt_subtype_mask);
+
+ if (index == MWIFIEX_AUTO_IDX_MASK) {
+ /* automatic addition */
+ if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
+ return -1;
+ if (index == MWIFIEX_AUTO_IDX_MASK)
+ return -1;
+
+ tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
+ memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
+ priv->mgmt_ie[index].ie_length = ie->ie_length;
+ priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
+ priv->mgmt_ie[index].mgmt_subtype_mask =
+ cpu_to_le16(mask);
+
+ ie->ie_index = cpu_to_le16(index);
+ } else {
+ if (mask != MWIFIEX_DELETE_MASK)
+ return -1;
+ /*
+ * Check if this index is being used on any
+ * other interface.
+ */
+ if (mwifiex_ie_index_used_by_other_intf(priv, index))
+ return -1;
+
+ ie->ie_length = 0;
+ memcpy(&priv->mgmt_ie[index], ie,
+ sizeof(struct mwifiex_ie));
+ }
+
+ le16_add_cpu(&ie_list->len,
+ le16_to_cpu(priv->mgmt_ie[index].ie_length) +
+ MWIFIEX_IE_HDR_SIZE);
+ input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
+ }
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
+ return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+ HostCmd_ACT_GEN_SET,
+ UAP_CUSTOM_IE_I, ie_list, true);
+
+ return 0;
+}
+
+/* Copy individual custom IEs for beacon, probe response and assoc response
+ * and prepare single structure for IE setting.
+ * This function also updates allocated IE indices from driver.
+ */
+static int
+mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
+ struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
+ struct mwifiex_ie *pr_ie, u16 *probe_idx,
+ struct mwifiex_ie *ar_ie, u16 *assoc_idx)
+{
+ struct mwifiex_ie_list *ap_custom_ie;
+ u8 *pos;
+ u16 len;
+ int ret;
+
+ ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
+ if (!ap_custom_ie)
+ return -ENOMEM;
+
+ ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
+ pos = (u8 *)ap_custom_ie->ie_list;
+
+ if (beacon_ie) {
+ len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(beacon_ie->ie_length);
+ memcpy(pos, beacon_ie, len);
+ pos += len;
+ le16_add_cpu(&ap_custom_ie->len, len);
+ }
+ if (pr_ie) {
+ len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(pr_ie->ie_length);
+ memcpy(pos, pr_ie, len);
+ pos += len;
+ le16_add_cpu(&ap_custom_ie->len, len);
+ }
+ if (ar_ie) {
+ len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(ar_ie->ie_length);
+ memcpy(pos, ar_ie, len);
+ pos += len;
+ le16_add_cpu(&ap_custom_ie->len, len);
+ }
+
+ ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
+
+ pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
+ if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
+ /* save beacon ie index after auto-indexing */
+ *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
+ len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(beacon_ie->ie_length);
+ pos += len;
+ }
+ if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
+ /* save probe resp ie index after auto-indexing */
+ *probe_idx = *((u16 *)pos);
+ len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(pr_ie->ie_length);
+ pos += len;
+ }
+ if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
+ /* save assoc resp ie index after auto-indexing */
+ *assoc_idx = *((u16 *)pos);
+
+ kfree(ap_custom_ie);
+ return ret;
+}
+
+/* This function checks if the vendor specified IE is present in passed buffer
+ * and copies it to mwifiex_ie structure.
+ * Function takes pointer to struct mwifiex_ie pointer as argument.
+ * If the vendor specified IE is present then memory is allocated for
+ * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
+ * this memory.
+ */
+static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
+ struct mwifiex_ie **ie_ptr, u16 mask,
+ unsigned int oui, u8 oui_type)
+{
+ struct ieee_types_header *vs_ie;
+ struct mwifiex_ie *ie = *ie_ptr;
+ const u8 *vendor_ie;
+
+ vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
+ if (vendor_ie) {
+ if (!*ie_ptr) {
+ *ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
+ GFP_KERNEL);
+ if (!*ie_ptr)
+ return -ENOMEM;
+ ie = *ie_ptr;
+ }
+
+ vs_ie = (struct ieee_types_header *)vendor_ie;
+ memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
+ vs_ie, vs_ie->len + 2);
+ le16_add_cpu(&ie->ie_length, vs_ie->len + 2);
+ ie->mgmt_subtype_mask = cpu_to_le16(mask);
+ ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
+ }
+
+ *ie_ptr = ie;
+ return 0;
+}
+
+/* This function parses beacon IEs, probe response IEs, association response IEs
+ * from cfg80211_ap_settings->beacon and sets these IE to FW.
+ */
+static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
+ struct cfg80211_beacon_data *data)
+{
+ struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
+ u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
+ u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
+ int ret = 0;
+
+ if (data->beacon_ies && data->beacon_ies_len) {
+ mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
+ &beacon_ie, MGMT_MASK_BEACON,
+ WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS);
+ mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
+ &beacon_ie, MGMT_MASK_BEACON,
+ WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
+ }
+
+ if (data->proberesp_ies && data->proberesp_ies_len) {
+ mwifiex_update_vs_ie(data->proberesp_ies,
+ data->proberesp_ies_len, &pr_ie,
+ MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS);
+ mwifiex_update_vs_ie(data->proberesp_ies,
+ data->proberesp_ies_len, &pr_ie,
+ MGMT_MASK_PROBE_RESP,
+ WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
+ }
+
+ if (data->assocresp_ies && data->assocresp_ies_len) {
+ mwifiex_update_vs_ie(data->assocresp_ies,
+ data->assocresp_ies_len, &ar_ie,
+ MGMT_MASK_ASSOC_RESP |
+ MGMT_MASK_REASSOC_RESP,
+ WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS);
+ mwifiex_update_vs_ie(data->assocresp_ies,
+ data->assocresp_ies_len, &ar_ie,
+ MGMT_MASK_ASSOC_RESP |
+ MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
+ WLAN_OUI_TYPE_WFA_P2P);
+ }
+
+ if (beacon_ie || pr_ie || ar_ie) {
+ ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
+ &beacon_idx, pr_ie,
+ &pr_idx, ar_ie, &ar_idx);
+ if (ret)
+ goto done;
+ }
+
+ priv->beacon_idx = beacon_idx;
+ priv->proberesp_idx = pr_idx;
+ priv->assocresp_idx = ar_idx;
+
+done:
+ kfree(beacon_ie);
+ kfree(pr_ie);
+ kfree(ar_ie);
+
+ return ret;
+}
+
+/* This function parses head and tail IEs, from cfg80211_beacon_data and sets
+ * these IE to FW.
+ */
+static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
+ struct cfg80211_beacon_data *info)
+{
+ struct mwifiex_ie *gen_ie;
+ struct ieee_types_header *hdr;
+ struct ieee80211_vendor_ie *vendorhdr;
+ u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
+ int left_len, parsed_len = 0;
+
+ if (!info->tail || !info->tail_len)
+ return 0;
+
+ gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
+ if (!gen_ie)
+ return -ENOMEM;
+
+ left_len = info->tail_len;
+
+ /* Many IEs are generated in FW by parsing bss configuration.
+ * Let's not add them here; else we may end up duplicating these IEs
+ */
+ while (left_len > sizeof(struct ieee_types_header)) {
+ hdr = (void *)(info->tail + parsed_len);
+ switch (hdr->element_id) {
+ case WLAN_EID_SSID:
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_COUNTRY:
+ case WLAN_EID_PWR_CONSTRAINT:
+ case WLAN_EID_EXT_SUPP_RATES:
+ case WLAN_EID_HT_CAPABILITY:
+ case WLAN_EID_HT_OPERATION:
+ case WLAN_EID_VHT_CAPABILITY:
+ case WLAN_EID_VHT_OPERATION:
+ case WLAN_EID_VENDOR_SPECIFIC:
+ break;
+ default:
+ memcpy(gen_ie->ie_buffer + ie_len, hdr,
+ hdr->len + sizeof(struct ieee_types_header));
+ ie_len += hdr->len + sizeof(struct ieee_types_header);
+ break;
+ }
+ left_len -= hdr->len + sizeof(struct ieee_types_header);
+ parsed_len += hdr->len + sizeof(struct ieee_types_header);
+ }
+
+ /* parse only WPA vendor IE from tail, WMM IE is configured by
+ * bss_config command
+ */
+ vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ info->tail, info->tail_len);
+ if (vendorhdr) {
+ memcpy(gen_ie->ie_buffer + ie_len, vendorhdr,
+ vendorhdr->len + sizeof(struct ieee_types_header));
+ ie_len += vendorhdr->len + sizeof(struct ieee_types_header);
+ }
+
+ if (!ie_len) {
+ kfree(gen_ie);
+ return 0;
+ }
+
+ gen_ie->ie_index = cpu_to_le16(gen_idx);
+ gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
+ MGMT_MASK_PROBE_RESP |
+ MGMT_MASK_ASSOC_RESP);
+ gen_ie->ie_length = cpu_to_le16(ie_len);
+
+ if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
+ NULL, NULL)) {
+ kfree(gen_ie);
+ return -1;
+ }
+
+ priv->gen_idx = gen_idx;
+ kfree(gen_ie);
+ return 0;
+}
+
+/* This function parses different IEs-head & tail IEs, beacon IEs,
+ * probe response IEs, association response IEs from cfg80211_ap_settings
+ * function and sets these IE to FW.
+ */
+int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
+ struct cfg80211_beacon_data *info)
+{
+ int ret;
+
+ ret = mwifiex_uap_parse_tail_ies(priv, info);
+
+ if (ret)
+ return ret;
+
+ return mwifiex_set_mgmt_beacon_data_ies(priv, info);
+}
+
+/* This function removes management IE set */
+int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
+{
+ struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
+ struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL;
+ int ret = 0;
+
+ if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) {
+ gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
+ if (!gen_ie)
+ return -ENOMEM;
+
+ gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
+ gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
+ gen_ie->ie_length = 0;
+ if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
+ NULL, &priv->proberesp_idx,
+ NULL, &priv->assocresp_idx)) {
+ ret = -1;
+ goto done;
+ }
+
+ priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
+ }
+
+ if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
+ beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
+ if (!beacon_ie) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
+ beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
+ beacon_ie->ie_length = 0;
+ }
+ if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
+ pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
+ if (!pr_ie) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
+ pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
+ pr_ie->ie_length = 0;
+ }
+ if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
+ ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
+ if (!ar_ie) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
+ ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
+ ar_ie->ie_length = 0;
+ }
+
+ if (beacon_ie || pr_ie || ar_ie)
+ ret = mwifiex_update_uap_custom_ie(priv,
+ beacon_ie, &priv->beacon_idx,
+ pr_ie, &priv->proberesp_idx,
+ ar_ie, &priv->assocresp_idx);
+
+done:
+ kfree(gen_ie);
+ kfree(beacon_ie);
+ kfree(pr_ie);
+ kfree(ar_ie);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c
new file mode 100644
index 000000000000..6f7876ec31b7
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/init.c
@@ -0,0 +1,782 @@
+/*
+ * Marvell Wireless LAN device driver: HW/FW Initialization
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+
+/*
+ * This function adds a BSS priority table to the table list.
+ *
+ * The function allocates a new BSS priority table node and adds it to
+ * the end of BSS priority table list, kept in driver memory.
+ */
+static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_bss_prio_node *bss_prio;
+ struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
+ unsigned long flags;
+
+ bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL);
+ if (!bss_prio)
+ return -ENOMEM;
+
+ bss_prio->priv = priv;
+ INIT_LIST_HEAD(&bss_prio->list);
+
+ spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags);
+ list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head);
+ spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags);
+
+ return 0;
+}
+
+static void wakeup_timer_fn(unsigned long data)
+{
+ struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data;
+
+ mwifiex_dbg(adapter, ERROR, "Firmware wakeup failed\n");
+ adapter->hw_status = MWIFIEX_HW_STATUS_RESET;
+ mwifiex_cancel_all_pending_cmd(adapter);
+
+ if (adapter->if_ops.card_reset)
+ adapter->if_ops.card_reset(adapter);
+}
+
+/*
+ * This function initializes the private structure and sets default
+ * values to the members.
+ *
+ * Additionally, it also initializes all the locks and sets up all the
+ * lists.
+ */
+int mwifiex_init_priv(struct mwifiex_private *priv)
+{
+ u32 i;
+
+ priv->media_connected = false;
+ eth_broadcast_addr(priv->curr_addr);
+ priv->port_open = false;
+ priv->usb_port = MWIFIEX_USB_EP_DATA;
+ priv->pkt_tx_ctrl = 0;
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->data_rate = 0; /* Initially indicate the rate as auto */
+ priv->is_data_rate_auto = true;
+ priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
+ priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
+
+ priv->sec_info.wep_enabled = 0;
+ priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ priv->sec_info.encryption_mode = 0;
+ for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++)
+ memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key));
+ priv->wep_key_curr_index = 0;
+ priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON |
+ HostCmd_ACT_MAC_ETHERNETII_ENABLE;
+
+ priv->beacon_period = 100; /* beacon interval */
+ priv->attempted_bss_desc = NULL;
+ memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params));
+ priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL;
+
+ memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid));
+ memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid));
+ memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf));
+ priv->assoc_rsp_size = 0;
+ priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
+ priv->atim_window = 0;
+ priv->adhoc_state = ADHOC_IDLE;
+ priv->tx_power_level = 0;
+ priv->max_tx_power_level = 0;
+ priv->min_tx_power_level = 0;
+ priv->tx_rate = 0;
+ priv->rxpd_htinfo = 0;
+ priv->rxpd_rate = 0;
+ priv->rate_bitmap = 0;
+ priv->data_rssi_last = 0;
+ priv->data_rssi_avg = 0;
+ priv->data_nf_avg = 0;
+ priv->data_nf_last = 0;
+ priv->bcn_rssi_last = 0;
+ priv->bcn_rssi_avg = 0;
+ priv->bcn_nf_avg = 0;
+ priv->bcn_nf_last = 0;
+ memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie));
+ memset(&priv->aes_key, 0, sizeof(priv->aes_key));
+ priv->wpa_ie_len = 0;
+ priv->wpa_is_gtk_set = false;
+
+ memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf));
+ priv->assoc_tlv_buf_len = 0;
+ memset(&priv->wps, 0, sizeof(priv->wps));
+ memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf));
+ priv->gen_ie_buf_len = 0;
+ memset(priv->vs_ie, 0, sizeof(priv->vs_ie));
+
+ priv->wmm_required = true;
+ priv->wmm_enabled = false;
+ priv->wmm_qosinfo = 0;
+ priv->curr_bcn_buf = NULL;
+ priv->curr_bcn_size = 0;
+ priv->wps_ie = NULL;
+ priv->wps_ie_len = 0;
+ priv->ap_11n_enabled = 0;
+ memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg));
+
+ priv->scan_block = false;
+
+ priv->csa_chan = 0;
+ priv->csa_expire_time = 0;
+ priv->del_list_idx = 0;
+ priv->hs2_enabled = false;
+ priv->check_tdls_tx = false;
+ memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
+
+ mwifiex_init_11h_params(priv);
+
+ return mwifiex_add_bss_prio_tbl(priv);
+}
+
+/*
+ * This function allocates buffers for members of the adapter
+ * structure.
+ *
+ * The memory allocated includes scan table, command buffers, and
+ * sleep confirm command buffer. In addition, the queues are
+ * also initialized.
+ */
+static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter)
+{
+ int ret;
+
+ /* Allocate command buffer */
+ ret = mwifiex_alloc_cmd_buffer(adapter);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: failed to alloc cmd buffer\n",
+ __func__);
+ return -1;
+ }
+
+ adapter->sleep_cfm =
+ dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
+ + INTF_HEADER_LEN);
+
+ if (!adapter->sleep_cfm) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: failed to alloc sleep cfm\t"
+ " cmd buffer\n", __func__);
+ return -1;
+ }
+ skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN);
+
+ return 0;
+}
+
+/*
+ * This function initializes the adapter structure and sets default
+ * values to the members of adapter.
+ *
+ * This also initializes the WMM related parameters in the driver private
+ * structures.
+ */
+static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL;
+
+ skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm));
+
+ adapter->cmd_sent = false;
+
+ if (adapter->iface_type == MWIFIEX_SDIO)
+ adapter->data_sent = true;
+ else
+ adapter->data_sent = false;
+
+ adapter->cmd_resp_received = false;
+ adapter->event_received = false;
+ adapter->data_received = false;
+
+ adapter->surprise_removed = false;
+
+ adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
+
+ adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->need_to_wakeup = false;
+
+ adapter->scan_mode = HostCmd_BSS_MODE_ANY;
+ adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME;
+ adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME;
+ adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME;
+ adapter->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME;
+
+ adapter->scan_probes = 1;
+
+ adapter->multiple_dtim = 1;
+
+ adapter->local_listen_interval = 0; /* default value in firmware
+ will be used */
+
+ adapter->is_deep_sleep = false;
+
+ adapter->delay_null_pkt = false;
+ adapter->delay_to_ps = 1000;
+ adapter->enhanced_ps_mode = PS_MODE_AUTO;
+
+ adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by
+ default */
+ adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by
+ default */
+ adapter->pm_wakeup_card_req = false;
+
+ adapter->pm_wakeup_fw_try = false;
+
+ adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
+
+ adapter->is_hs_configured = false;
+ adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF);
+ adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF;
+ adapter->hs_cfg.gap = HS_CFG_GAP_DEF;
+ adapter->hs_activated = false;
+
+ memset(adapter->event_body, 0, sizeof(adapter->event_body));
+ adapter->hw_dot_11n_dev_cap = 0;
+ adapter->hw_dev_mcs_support = 0;
+ adapter->sec_chan_offset = 0;
+ adapter->adhoc_11n_enabled = false;
+
+ mwifiex_wmm_init(adapter);
+
+ sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
+ adapter->sleep_cfm->data;
+ memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
+ sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
+ sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len);
+ sleep_cfm_buf->result = 0;
+ sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
+ sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
+
+ memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params));
+ memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
+ adapter->tx_lock_flag = false;
+ adapter->null_pkt_interval = 0;
+ adapter->fw_bands = 0;
+ adapter->config_bands = 0;
+ adapter->adhoc_start_band = 0;
+ adapter->scan_channels = NULL;
+ adapter->fw_release_number = 0;
+ adapter->fw_cap_info = 0;
+ memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf));
+ adapter->event_cause = 0;
+ adapter->region_code = 0;
+ adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT;
+ adapter->adhoc_awake_period = 0;
+ memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
+ adapter->arp_filter_size = 0;
+ adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
+ adapter->key_api_major_ver = 0;
+ adapter->key_api_minor_ver = 0;
+ eth_broadcast_addr(adapter->perm_addr);
+ adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM;
+ adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM;
+ adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM;
+ adapter->active_scan_triggered = false;
+ setup_timer(&adapter->wakeup_timer, wakeup_timer_fn,
+ (unsigned long)adapter);
+}
+
+/*
+ * This function sets trans_start per tx_queue
+ */
+void mwifiex_set_trans_start(struct net_device *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->num_tx_queues; i++)
+ netdev_get_tx_queue(dev, i)->trans_start = jiffies;
+
+ dev->trans_start = jiffies;
+}
+
+/*
+ * This function wakes up all queues in net_device
+ */
+void mwifiex_wake_up_net_dev_queue(struct net_device *netdev,
+ struct mwifiex_adapter *adapter)
+{
+ unsigned long dev_queue_flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags);
+
+ for (i = 0; i < netdev->num_tx_queues; i++) {
+ struct netdev_queue *txq = netdev_get_tx_queue(netdev, i);
+
+ if (netif_tx_queue_stopped(txq))
+ netif_tx_wake_queue(txq);
+ }
+
+ spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags);
+}
+
+/*
+ * This function stops all queues in net_device
+ */
+void mwifiex_stop_net_dev_queue(struct net_device *netdev,
+ struct mwifiex_adapter *adapter)
+{
+ unsigned long dev_queue_flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags);
+
+ for (i = 0; i < netdev->num_tx_queues; i++) {
+ struct netdev_queue *txq = netdev_get_tx_queue(netdev, i);
+
+ if (!netif_tx_queue_stopped(txq))
+ netif_tx_stop_queue(txq);
+ }
+
+ spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags);
+}
+
+/*
+ * This function releases the lock variables and frees the locks and
+ * associated locks.
+ */
+static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_private *priv;
+ s32 i, j;
+
+ /* Free lists */
+ list_del(&adapter->cmd_free_q);
+ list_del(&adapter->cmd_pending_q);
+ list_del(&adapter->scan_pending_q);
+
+ for (i = 0; i < adapter->priv_num; i++)
+ list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+ for (j = 0; j < MAX_NUM_TID; ++j)
+ list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
+ list_del(&priv->tx_ba_stream_tbl_ptr);
+ list_del(&priv->rx_reorder_tbl_ptr);
+ list_del(&priv->sta_list);
+ list_del(&priv->auto_tdls_list);
+ }
+ }
+}
+
+/*
+ * This function performs cleanup for adapter structure.
+ *
+ * The cleanup is done recursively, by canceling all pending
+ * commands, freeing the member buffers previously allocated
+ * (command buffers, scan table buffer, sleep confirm command
+ * buffer), stopping the timers and calling the cleanup routines
+ * for every interface.
+ */
+static void
+mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
+{
+ int idx;
+
+ if (!adapter) {
+ pr_err("%s: adapter is NULL\n", __func__);
+ return;
+ }
+
+ del_timer(&adapter->wakeup_timer);
+ mwifiex_cancel_all_pending_cmd(adapter);
+ wake_up_interruptible(&adapter->cmd_wait_q.wait);
+ wake_up_interruptible(&adapter->hs_activate_wait_q);
+
+ /* Free lock variables */
+ mwifiex_free_lock_list(adapter);
+
+ /* Free command buffer */
+ mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n");
+ mwifiex_free_cmd_buffer(adapter);
+
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ if (adapter->drv_info_dump) {
+ vfree(adapter->drv_info_dump);
+ adapter->drv_info_dump = NULL;
+ adapter->drv_info_size = 0;
+ }
+
+ if (adapter->sleep_cfm)
+ dev_kfree_skb_any(adapter->sleep_cfm);
+}
+
+/*
+ * This function intializes the lock variables and
+ * the list heads.
+ */
+int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_private *priv;
+ s32 i, j;
+
+ spin_lock_init(&adapter->mwifiex_lock);
+ spin_lock_init(&adapter->int_lock);
+ spin_lock_init(&adapter->main_proc_lock);
+ spin_lock_init(&adapter->mwifiex_cmd_lock);
+ spin_lock_init(&adapter->queue_lock);
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+ spin_lock_init(&priv->rx_pkt_lock);
+ spin_lock_init(&priv->wmm.ra_list_spinlock);
+ spin_lock_init(&priv->curr_bcn_buf_lock);
+ spin_lock_init(&priv->sta_list_spinlock);
+ spin_lock_init(&priv->auto_tdls_lock);
+ }
+ }
+
+ /* Initialize cmd_free_q */
+ INIT_LIST_HEAD(&adapter->cmd_free_q);
+ /* Initialize cmd_pending_q */
+ INIT_LIST_HEAD(&adapter->cmd_pending_q);
+ /* Initialize scan_pending_q */
+ INIT_LIST_HEAD(&adapter->scan_pending_q);
+
+ spin_lock_init(&adapter->cmd_free_q_lock);
+ spin_lock_init(&adapter->cmd_pending_q_lock);
+ spin_lock_init(&adapter->scan_pending_q_lock);
+ spin_lock_init(&adapter->rx_proc_lock);
+
+ skb_queue_head_init(&adapter->rx_data_q);
+ skb_queue_head_init(&adapter->tx_data_q);
+
+ for (i = 0; i < adapter->priv_num; ++i) {
+ INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
+ spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock);
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i])
+ continue;
+ priv = adapter->priv[i];
+ for (j = 0; j < MAX_NUM_TID; ++j)
+ INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
+ INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
+ INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
+ INIT_LIST_HEAD(&priv->sta_list);
+ INIT_LIST_HEAD(&priv->auto_tdls_list);
+ skb_queue_head_init(&priv->tdls_txq);
+ skb_queue_head_init(&priv->bypass_txq);
+
+ spin_lock_init(&priv->tx_ba_stream_tbl_lock);
+ spin_lock_init(&priv->rx_reorder_tbl_lock);
+
+ spin_lock_init(&priv->ack_status_lock);
+ idr_init(&priv->ack_status_frames);
+ }
+
+ return 0;
+}
+
+/*
+ * This function initializes the firmware.
+ *
+ * The following operations are performed sequentially -
+ * - Allocate adapter structure
+ * - Initialize the adapter structure
+ * - Initialize the private structure
+ * - Add BSS priority tables to the adapter structure
+ * - For each interface, send the init commands to firmware
+ * - Send the first command in command pending queue, if available
+ */
+int mwifiex_init_fw(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ struct mwifiex_private *priv;
+ u8 i, first_sta = true;
+ int is_cmd_pend_q_empty;
+ unsigned long flags;
+
+ adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
+
+ /* Allocate memory for member of adapter structure */
+ ret = mwifiex_allocate_adapter(adapter);
+ if (ret)
+ return -1;
+
+ /* Initialize adapter structure */
+ mwifiex_init_adapter(adapter);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+
+ /* Initialize private structure */
+ ret = mwifiex_init_priv(priv);
+ if (ret)
+ return -1;
+ }
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta,
+ true);
+ if (ret == -1)
+ return -1;
+
+ first_sta = false;
+ }
+ }
+
+ spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+ is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+ if (!is_cmd_pend_q_empty) {
+ /* Send the first command in queue and return */
+ if (mwifiex_main_process(adapter) != -1)
+ ret = -EINPROGRESS;
+ } else {
+ adapter->hw_status = MWIFIEX_HW_STATUS_READY;
+ }
+
+ return ret;
+}
+
+/*
+ * This function deletes the BSS priority tables.
+ *
+ * The function traverses through all the allocated BSS priority nodes
+ * in every BSS priority table and frees them.
+ */
+static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv)
+{
+ int i;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_bss_prio_node *bssprio_node, *tmp_node;
+ struct list_head *head;
+ spinlock_t *lock; /* bss priority lock */
+ unsigned long flags;
+
+ for (i = 0; i < adapter->priv_num; ++i) {
+ head = &adapter->bss_prio_tbl[i].bss_prio_head;
+ lock = &adapter->bss_prio_tbl[i].bss_prio_lock;
+ mwifiex_dbg(adapter, INFO,
+ "info: delete BSS priority table,\t"
+ "bss_type = %d, bss_num = %d, i = %d,\t"
+ "head = %p\n",
+ priv->bss_type, priv->bss_num, i, head);
+
+ {
+ spin_lock_irqsave(lock, flags);
+ if (list_empty(head)) {
+ spin_unlock_irqrestore(lock, flags);
+ continue;
+ }
+ list_for_each_entry_safe(bssprio_node, tmp_node, head,
+ list) {
+ if (bssprio_node->priv == priv) {
+ mwifiex_dbg(adapter, INFO,
+ "info: Delete\t"
+ "node %p, next = %p\n",
+ bssprio_node, tmp_node);
+ list_del(&bssprio_node->list);
+ kfree(bssprio_node);
+ }
+ }
+ spin_unlock_irqrestore(lock, flags);
+ }
+ }
+}
+
+/*
+ * This function frees the private structure, including cleans
+ * up the TX and RX queues and frees the BSS priority tables.
+ */
+void mwifiex_free_priv(struct mwifiex_private *priv)
+{
+ mwifiex_clean_txrx(priv);
+ mwifiex_delete_bss_prio_tbl(priv);
+ mwifiex_free_curr_bcn(priv);
+}
+
+/*
+ * This function is used to shutdown the driver.
+ *
+ * The following operations are performed sequentially -
+ * - Check if already shut down
+ * - Make sure the main process has stopped
+ * - Clean up the Tx and Rx queues
+ * - Delete BSS priority tables
+ * - Free the adapter
+ * - Notify completion
+ */
+int
+mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
+{
+ int ret = -EINPROGRESS;
+ struct mwifiex_private *priv;
+ s32 i;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ /* mwifiex already shutdown */
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)
+ return 0;
+
+ adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING;
+ /* wait for mwifiex_process to complete */
+ if (adapter->mwifiex_processing) {
+ mwifiex_dbg(adapter, WARN,
+ "main process is still running\n");
+ return ret;
+ }
+
+ /* cancel current command */
+ if (adapter->curr_cmd) {
+ mwifiex_dbg(adapter, WARN,
+ "curr_cmd is still in processing\n");
+ del_timer_sync(&adapter->cmd_timer);
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
+ adapter->curr_cmd = NULL;
+ }
+
+ /* shut down mwifiex */
+ mwifiex_dbg(adapter, MSG,
+ "info: shutdown mwifiex...\n");
+
+ /* Clean up Tx/Rx queues and delete BSS priority table */
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+
+ mwifiex_clean_auto_tdls(priv);
+ mwifiex_abort_cac(priv);
+ mwifiex_clean_txrx(priv);
+ mwifiex_delete_bss_prio_tbl(priv);
+ }
+ }
+
+ atomic_set(&adapter->tx_queued, 0);
+ while ((skb = skb_dequeue(&adapter->tx_data_q)))
+ mwifiex_write_data_complete(adapter, skb, 0, 0);
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+
+ while ((skb = skb_dequeue(&adapter->rx_data_q))) {
+ struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
+
+ atomic_dec(&adapter->rx_pending);
+ priv = adapter->priv[rx_info->bss_num];
+ if (priv)
+ priv->stats.rx_dropped++;
+
+ dev_kfree_skb_any(skb);
+ }
+
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+
+ spin_lock(&adapter->mwifiex_lock);
+
+ mwifiex_adapter_cleanup(adapter);
+
+ spin_unlock(&adapter->mwifiex_lock);
+
+ /* Notify completion */
+ ret = mwifiex_shutdown_fw_complete(adapter);
+
+ return ret;
+}
+
+/*
+ * This function downloads the firmware to the card.
+ *
+ * The actual download is preceded by two sanity checks -
+ * - Check if firmware is already running
+ * - Check if the interface is the winner to download the firmware
+ *
+ * ...and followed by another -
+ * - Check if the firmware is downloaded successfully
+ *
+ * After download is successfully completed, the host interrupts are enabled.
+ */
+int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
+ struct mwifiex_fw_image *pmfw)
+{
+ int ret;
+ u32 poll_num = 1;
+
+ if (adapter->if_ops.check_fw_status) {
+ adapter->winner = 0;
+
+ /* check if firmware is already running */
+ ret = adapter->if_ops.check_fw_status(adapter, poll_num);
+ if (!ret) {
+ mwifiex_dbg(adapter, MSG,
+ "WLAN FW already running! Skip FW dnld\n");
+ return 0;
+ }
+
+ poll_num = MAX_FIRMWARE_POLL_TRIES;
+
+ /* check if we are the winner for downloading FW */
+ if (!adapter->winner) {
+ mwifiex_dbg(adapter, MSG,
+ "FW already running! Skip FW dnld\n");
+ goto poll_fw;
+ }
+ }
+
+ if (pmfw) {
+ /* Download firmware with helper */
+ ret = adapter->if_ops.prog_fw(adapter, pmfw);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "prog_fw failed ret=%#x\n", ret);
+ return ret;
+ }
+ }
+
+poll_fw:
+ /* Check if the firmware is downloaded successfully or not */
+ ret = adapter->if_ops.check_fw_status(adapter, poll_num);
+ if (ret)
+ mwifiex_dbg(adapter, ERROR,
+ "FW failed to be active in time\n");
+
+ return ret;
+}
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h
index 4f0174c64946..4f0174c64946 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h
diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c
new file mode 100644
index 000000000000..cc09a81dbf6a
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/join.c
@@ -0,0 +1,1531 @@
+/*
+ * Marvell Wireless LAN device driver: association and ad-hoc start/join
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11ac.h"
+
+#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9)))
+
+/*
+ * Append a generic IE as a pass through TLV to a TLV buffer.
+ *
+ * This function is called from the network join command preparation routine.
+ *
+ * If the IE buffer has been setup by the application, this routine appends
+ * the buffer as a pass through TLV type to the request.
+ */
+static int
+mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer)
+{
+ int ret_len = 0;
+ struct mwifiex_ie_types_header ie_header;
+
+ /* Null Checks */
+ if (!buffer)
+ return 0;
+ if (!(*buffer))
+ return 0;
+
+ /*
+ * If there is a generic ie buffer setup, append it to the return
+ * parameter buffer pointer.
+ */
+ if (priv->gen_ie_buf_len) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: %s: append generic ie len %d to %p\n",
+ __func__, priv->gen_ie_buf_len, *buffer);
+
+ /* Wrap the generic IE buffer with a pass through TLV type */
+ ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+ ie_header.len = cpu_to_le16(priv->gen_ie_buf_len);
+ memcpy(*buffer, &ie_header, sizeof(ie_header));
+
+ /* Increment the return size and the return buffer pointer
+ param */
+ *buffer += sizeof(ie_header);
+ ret_len += sizeof(ie_header);
+
+ /* Copy the generic IE buffer to the output buffer, advance
+ pointer */
+ memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len);
+
+ /* Increment the return size and the return buffer pointer
+ param */
+ *buffer += priv->gen_ie_buf_len;
+ ret_len += priv->gen_ie_buf_len;
+
+ /* Reset the generic IE buffer */
+ priv->gen_ie_buf_len = 0;
+ }
+
+ /* return the length appended to the buffer */
+ return ret_len;
+}
+
+/*
+ * Append TSF tracking info from the scan table for the target AP.
+ *
+ * This function is called from the network join command preparation routine.
+ *
+ * The TSF table TSF sent to the firmware contains two TSF values:
+ * - The TSF of the target AP from its previous beacon/probe response
+ * - The TSF timestamp of our local MAC at the time we observed the
+ * beacon/probe response.
+ *
+ * The firmware uses the timestamp values to set an initial TSF value
+ * in the MAC for the new association after a reassociation attempt.
+ */
+static int
+mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ struct mwifiex_ie_types_tsf_timestamp tsf_tlv;
+ __le64 tsf_val;
+
+ /* Null Checks */
+ if (buffer == NULL)
+ return 0;
+ if (*buffer == NULL)
+ return 0;
+
+ memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp));
+
+ tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP);
+ tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val));
+
+ memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header));
+ *buffer += sizeof(tsf_tlv.header);
+
+ /* TSF at the time when beacon/probe_response was received */
+ tsf_val = cpu_to_le64(bss_desc->fw_tsf);
+ memcpy(*buffer, &tsf_val, sizeof(tsf_val));
+ *buffer += sizeof(tsf_val);
+
+ tsf_val = cpu_to_le64(bss_desc->timestamp);
+
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: %s: TSF offset calc: %016llx - %016llx\n",
+ __func__, bss_desc->timestamp, bss_desc->fw_tsf);
+
+ memcpy(*buffer, &tsf_val, sizeof(tsf_val));
+ *buffer += sizeof(tsf_val);
+
+ return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val));
+}
+
+/*
+ * This function finds out the common rates between rate1 and rate2.
+ *
+ * It will fill common rates in rate1 as output if found.
+ *
+ * NOTE: Setting the MSB of the basic rates needs to be taken
+ * care of, either before or after calling this function.
+ */
+static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1,
+ u32 rate1_size, u8 *rate2, u32 rate2_size)
+{
+ int ret;
+ u8 *ptr = rate1, *tmp;
+ u32 i, j;
+
+ tmp = kmemdup(rate1, rate1_size, GFP_KERNEL);
+ if (!tmp) {
+ mwifiex_dbg(priv->adapter, ERROR, "failed to alloc tmp buf\n");
+ return -ENOMEM;
+ }
+
+ memset(rate1, 0, rate1_size);
+
+ for (i = 0; i < rate2_size && rate2[i]; i++) {
+ for (j = 0; j < rate1_size && tmp[j]; j++) {
+ /* Check common rate, excluding the bit for
+ basic rate */
+ if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) {
+ *rate1++ = tmp[j];
+ break;
+ }
+ }
+ }
+
+ mwifiex_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n",
+ priv->data_rate);
+
+ if (!priv->is_data_rate_auto) {
+ while (*ptr) {
+ if ((*ptr & 0x7f) == priv->data_rate) {
+ ret = 0;
+ goto done;
+ }
+ ptr++;
+ }
+ mwifiex_dbg(priv->adapter, ERROR,
+ "previously set fixed data rate %#x\t"
+ "is not compatible with the network\n",
+ priv->data_rate);
+
+ ret = -1;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ kfree(tmp);
+ return ret;
+}
+
+/*
+ * This function creates the intersection of the rates supported by a
+ * target BSS and our adapter settings for use in an assoc/join command.
+ */
+static int
+mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc,
+ u8 *out_rates, u32 *out_rates_size)
+{
+ u8 card_rates[MWIFIEX_SUPPORTED_RATES];
+ u32 card_rates_size;
+
+ /* Copy AP supported rates */
+ memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES);
+ /* Get the STA supported rates */
+ card_rates_size = mwifiex_get_active_data_rates(priv, card_rates);
+ /* Get the common rates between AP and STA supported rates */
+ if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES,
+ card_rates, card_rates_size)) {
+ *out_rates_size = 0;
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%s: cannot get common rates\n",
+ __func__);
+ return -1;
+ }
+
+ *out_rates_size =
+ min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES);
+
+ return 0;
+}
+
+/*
+ * This function appends a WPS IE. It is called from the network join command
+ * preparation routine.
+ *
+ * If the IE buffer has been setup by the application, this routine appends
+ * the buffer as a WPS TLV type to the request.
+ */
+static int
+mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer)
+{
+ int retLen = 0;
+ struct mwifiex_ie_types_header ie_header;
+
+ if (!buffer || !*buffer)
+ return 0;
+
+ /*
+ * If there is a wps ie buffer setup, append it to the return
+ * parameter buffer pointer.
+ */
+ if (priv->wps_ie_len) {
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: append wps ie %d to %p\n",
+ priv->wps_ie_len, *buffer);
+
+ /* Wrap the generic IE buffer with a pass through TLV type */
+ ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE);
+ ie_header.len = cpu_to_le16(priv->wps_ie_len);
+ memcpy(*buffer, &ie_header, sizeof(ie_header));
+ *buffer += sizeof(ie_header);
+ retLen += sizeof(ie_header);
+
+ memcpy(*buffer, priv->wps_ie, priv->wps_ie_len);
+ *buffer += priv->wps_ie_len;
+ retLen += priv->wps_ie_len;
+
+ }
+
+ kfree(priv->wps_ie);
+ priv->wps_ie_len = 0;
+ return retLen;
+}
+
+/*
+ * This function appends a WAPI IE.
+ *
+ * This function is called from the network join command preparation routine.
+ *
+ * If the IE buffer has been setup by the application, this routine appends
+ * the buffer as a WAPI TLV type to the request.
+ */
+static int
+mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer)
+{
+ int retLen = 0;
+ struct mwifiex_ie_types_header ie_header;
+
+ /* Null Checks */
+ if (buffer == NULL)
+ return 0;
+ if (*buffer == NULL)
+ return 0;
+
+ /*
+ * If there is a wapi ie buffer setup, append it to the return
+ * parameter buffer pointer.
+ */
+ if (priv->wapi_ie_len) {
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: append wapi ie %d to %p\n",
+ priv->wapi_ie_len, *buffer);
+
+ /* Wrap the generic IE buffer with a pass through TLV type */
+ ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE);
+ ie_header.len = cpu_to_le16(priv->wapi_ie_len);
+ memcpy(*buffer, &ie_header, sizeof(ie_header));
+
+ /* Increment the return size and the return buffer pointer
+ param */
+ *buffer += sizeof(ie_header);
+ retLen += sizeof(ie_header);
+
+ /* Copy the wapi IE buffer to the output buffer, advance
+ pointer */
+ memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len);
+
+ /* Increment the return size and the return buffer pointer
+ param */
+ *buffer += priv->wapi_ie_len;
+ retLen += priv->wapi_ie_len;
+
+ }
+ /* return the length appended to the buffer */
+ return retLen;
+}
+
+/*
+ * This function appends rsn ie tlv for wpa/wpa2 security modes.
+ * It is called from the network join command preparation routine.
+ */
+static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv,
+ u8 **buffer)
+{
+ struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv;
+ int rsn_ie_len;
+
+ if (!buffer || !(*buffer))
+ return 0;
+
+ rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer);
+ rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]);
+ rsn_ie_tlv->header.type = cpu_to_le16(
+ le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF);
+ rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]);
+ rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len)
+ & 0x00FF);
+ if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2))
+ memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2],
+ le16_to_cpu(rsn_ie_tlv->header.len));
+ else
+ return -1;
+
+ rsn_ie_len = sizeof(rsn_ie_tlv->header) +
+ le16_to_cpu(rsn_ie_tlv->header.len);
+ *buffer += rsn_ie_len;
+
+ return rsn_ie_len;
+}
+
+/*
+ * This function prepares command for association.
+ *
+ * This sets the following parameters -
+ * - Peer MAC address
+ * - Listen interval
+ * - Beacon interval
+ * - Capability information
+ *
+ * ...and the following TLVs, as required -
+ * - SSID TLV
+ * - PHY TLV
+ * - SS TLV
+ * - Rates TLV
+ * - Authentication TLV
+ * - Channel TLV
+ * - WPA/WPA2 IE
+ * - 11n TLV
+ * - Vendor specific TLV
+ * - WMM TLV
+ * - WAPI IE
+ * - Generic IE
+ * - TSF TLV
+ *
+ * Preparation also includes -
+ * - Setting command ID and proper size
+ * - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate;
+ struct mwifiex_ie_types_ssid_param_set *ssid_tlv;
+ struct mwifiex_ie_types_phy_param_set *phy_tlv;
+ struct mwifiex_ie_types_ss_param_set *ss_tlv;
+ struct mwifiex_ie_types_rates_param_set *rates_tlv;
+ struct mwifiex_ie_types_auth_type *auth_tlv;
+ struct mwifiex_ie_types_chan_list_param_set *chan_tlv;
+ u8 rates[MWIFIEX_SUPPORTED_RATES];
+ u32 rates_size;
+ u16 tmp_cap;
+ u8 *pos;
+ int rsn_ie_len = 0;
+
+ pos = (u8 *) assoc;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE);
+
+ /* Save so we know which BSS Desc to use in the response handler */
+ priv->attempted_bss_desc = bss_desc;
+
+ memcpy(assoc->peer_sta_addr,
+ bss_desc->mac_address, sizeof(assoc->peer_sta_addr));
+ pos += sizeof(assoc->peer_sta_addr);
+
+ /* Set the listen interval */
+ assoc->listen_interval = cpu_to_le16(priv->listen_interval);
+ /* Set the beacon period */
+ assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period);
+
+ pos += sizeof(assoc->cap_info_bitmap);
+ pos += sizeof(assoc->listen_interval);
+ pos += sizeof(assoc->beacon_period);
+ pos += sizeof(assoc->dtim_period);
+
+ ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos;
+ ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID);
+ ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len);
+ memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid,
+ le16_to_cpu(ssid_tlv->header.len));
+ pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len);
+
+ phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos;
+ phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS);
+ phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set));
+ memcpy(&phy_tlv->fh_ds.ds_param_set,
+ &bss_desc->phy_param_set.ds_param_set.current_chan,
+ sizeof(phy_tlv->fh_ds.ds_param_set));
+ pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len);
+
+ ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos;
+ ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS);
+ ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set));
+ pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len);
+
+ /* Get the common rates supported between the driver and the BSS Desc */
+ if (mwifiex_setup_rates_from_bssdesc
+ (priv, bss_desc, rates, &rates_size))
+ return -1;
+
+ /* Save the data rates into Current BSS state structure */
+ priv->curr_bss_params.num_of_rates = rates_size;
+ memcpy(&priv->curr_bss_params.data_rates, rates, rates_size);
+
+ /* Setup the Rates TLV in the association command */
+ rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos;
+ rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
+ rates_tlv->header.len = cpu_to_le16((u16) rates_size);
+ memcpy(rates_tlv->rates, rates, rates_size);
+ pos += sizeof(rates_tlv->header) + rates_size;
+ mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_CMD: rates size = %d\n",
+ rates_size);
+
+ /* Add the Authentication type to be used for Auth frames */
+ auth_tlv = (struct mwifiex_ie_types_auth_type *) pos;
+ auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type));
+ if (priv->sec_info.wep_enabled)
+ auth_tlv->auth_type = cpu_to_le16(
+ (u16) priv->sec_info.authentication_mode);
+ else
+ auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM);
+
+ pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len);
+
+ if (IS_SUPPORT_MULTI_BANDS(priv->adapter) &&
+ !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
+ (!bss_desc->disable_11n) &&
+ (priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->config_bands & BAND_AN) &&
+ (bss_desc->bcn_ht_cap)
+ )
+ ) {
+ /* Append a channel TLV for the channel the attempted AP was
+ found on */
+ chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos;
+ chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ chan_tlv->header.len =
+ cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set));
+
+ memset(chan_tlv->chan_scan_param, 0x00,
+ sizeof(struct mwifiex_chan_scan_param_set));
+ chan_tlv->chan_scan_param[0].chan_number =
+ (bss_desc->phy_param_set.ds_param_set.current_chan);
+ mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Chan = %d\n",
+ chan_tlv->chan_scan_param[0].chan_number);
+
+ chan_tlv->chan_scan_param[0].radio_type =
+ mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
+
+ mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Band = %d\n",
+ chan_tlv->chan_scan_param[0].radio_type);
+ pos += sizeof(chan_tlv->header) +
+ sizeof(struct mwifiex_chan_scan_param_set);
+ }
+
+ if (!priv->wps.session_enable) {
+ if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
+ rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos);
+
+ if (rsn_ie_len == -1)
+ return -1;
+ }
+
+ if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
+ (!bss_desc->disable_11n) &&
+ (priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->config_bands & BAND_AN))
+ mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos);
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ priv->adapter->config_bands & BAND_AAC)
+ mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos);
+
+ /* Append vendor specific IE TLV */
+ mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos);
+
+ mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie,
+ bss_desc->bcn_ht_cap);
+ if (priv->sec_info.wapi_enabled && priv->wapi_ie_len)
+ mwifiex_cmd_append_wapi_ie(priv, &pos);
+
+ if (priv->wps.session_enable && priv->wps_ie_len)
+ mwifiex_cmd_append_wps_ie(priv, &pos);
+
+ mwifiex_cmd_append_generic_ie(priv, &pos);
+
+ 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 */
+ tmp_cap = bss_desc->cap_info_bitmap;
+
+ if (priv->adapter->config_bands == BAND_B)
+ tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+ tmp_cap &= CAPINFO_MASK;
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n",
+ tmp_cap, CAPINFO_MASK);
+ assoc->cap_info_bitmap = cpu_to_le16(tmp_cap);
+
+ return 0;
+}
+
+static const char *assoc_failure_reason_to_str(u16 cap_info)
+{
+ switch (cap_info) {
+ case CONNECT_ERR_AUTH_ERR_STA_FAILURE:
+ return "CONNECT_ERR_AUTH_ERR_STA_FAILURE";
+ case CONNECT_ERR_AUTH_MSG_UNHANDLED:
+ return "CONNECT_ERR_AUTH_MSG_UNHANDLED";
+ case CONNECT_ERR_ASSOC_ERR_TIMEOUT:
+ return "CONNECT_ERR_ASSOC_ERR_TIMEOUT";
+ case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED:
+ return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED";
+ case CONNECT_ERR_STA_FAILURE:
+ return "CONNECT_ERR_STA_FAILURE";
+ }
+
+ return "Unknown connect failure";
+}
+/*
+ * Association firmware command response handler
+ *
+ * The response buffer for the association command has the following
+ * memory layout.
+ *
+ * For cases where an association response was not received (indicated
+ * by the CapInfo and AId field):
+ *
+ * .------------------------------------------------------------.
+ * | Header(4 * sizeof(t_u16)): Standard command response hdr |
+ * .------------------------------------------------------------.
+ * | cap_info/Error Return(t_u16): |
+ * | 0xFFFF(-1): Internal error |
+ * | 0xFFFE(-2): Authentication unhandled message |
+ * | 0xFFFD(-3): Authentication refused |
+ * | 0xFFFC(-4): Timeout waiting for AP response |
+ * .------------------------------------------------------------.
+ * | status_code(t_u16): |
+ * | If cap_info is -1: |
+ * | An internal firmware failure prevented the |
+ * | command from being processed. The status_code |
+ * | will be set to 1. |
+ * | |
+ * | If cap_info is -2: |
+ * | An authentication frame was received but was |
+ * | not handled by the firmware. IEEE Status |
+ * | code for the failure is returned. |
+ * | |
+ * | If cap_info is -3: |
+ * | An authentication frame was received and the |
+ * | status_code is the IEEE Status reported in the |
+ * | response. |
+ * | |
+ * | If cap_info is -4: |
+ * | (1) Association response timeout |
+ * | (2) Authentication response timeout |
+ * .------------------------------------------------------------.
+ * | a_id(t_u16): 0xFFFF |
+ * .------------------------------------------------------------.
+ *
+ *
+ * For cases where an association response was received, the IEEE
+ * standard association response frame is returned:
+ *
+ * .------------------------------------------------------------.
+ * | Header(4 * sizeof(t_u16)): Standard command response hdr |
+ * .------------------------------------------------------------.
+ * | cap_info(t_u16): IEEE Capability |
+ * .------------------------------------------------------------.
+ * | status_code(t_u16): IEEE Status Code |
+ * .------------------------------------------------------------.
+ * | a_id(t_u16): IEEE Association ID |
+ * .------------------------------------------------------------.
+ * | IEEE IEs(variable): Any received IEs comprising the |
+ * | remaining portion of a received |
+ * | association response frame. |
+ * .------------------------------------------------------------.
+ *
+ * For simplistic handling, the status_code field can be used to determine
+ * an association success (0) or failure (non-zero).
+ */
+int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int ret = 0;
+ struct ieee_types_assoc_rsp *assoc_rsp;
+ struct mwifiex_bssdescriptor *bss_desc;
+ bool enable_data = true;
+ u16 cap_info, status_code, aid;
+
+ assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params;
+
+ cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap);
+ status_code = le16_to_cpu(assoc_rsp->status_code);
+ aid = le16_to_cpu(assoc_rsp->a_id);
+
+ if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
+ dev_err(priv->adapter->dev,
+ "invalid AID value 0x%x; bits 15:14 not set\n",
+ aid);
+
+ aid &= ~(BIT(15) | BIT(14));
+
+ priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
+ sizeof(priv->assoc_rsp_buf));
+
+ memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
+
+ assoc_rsp->a_id = cpu_to_le16(aid);
+
+ if (status_code) {
+ priv->adapter->dbg.num_cmd_assoc_failure++;
+ mwifiex_dbg(priv->adapter, ERROR,
+ "ASSOC_RESP: failed,\t"
+ "status code=%d err=%#x a_id=%#x\n",
+ status_code, cap_info,
+ le16_to_cpu(assoc_rsp->a_id));
+
+ mwifiex_dbg(priv->adapter, ERROR, "assoc failure: reason %s\n",
+ assoc_failure_reason_to_str(cap_info));
+ if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) {
+ if (status_code == MWIFIEX_ASSOC_CMD_FAILURE_AUTH) {
+ ret = WLAN_STATUS_AUTH_TIMEOUT;
+ mwifiex_dbg(priv->adapter, ERROR,
+ "ASSOC_RESP: AUTH timeout\n");
+ } else {
+ ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ mwifiex_dbg(priv->adapter, ERROR,
+ "ASSOC_RESP: UNSPECIFIED failure\n");
+ }
+ } else {
+ ret = status_code;
+ }
+
+ goto done;
+ }
+
+ /* Send a Media Connected event, according to the Spec */
+ priv->media_connected = true;
+
+ priv->adapter->ps_state = PS_STATE_AWAKE;
+ priv->adapter->pps_uapsd_mode = false;
+ priv->adapter->tx_lock_flag = false;
+
+ /* Set the attempted BSSID Index to current */
+ bss_desc = priv->attempted_bss_desc;
+
+ mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: %s\n",
+ bss_desc->ssid.ssid);
+
+ /* Make a copy of current BSSID descriptor */
+ memcpy(&priv->curr_bss_params.bss_descriptor,
+ bss_desc, sizeof(struct mwifiex_bssdescriptor));
+
+ /* Update curr_bss_params */
+ priv->curr_bss_params.bss_descriptor.channel
+ = bss_desc->phy_param_set.ds_param_set.current_chan;
+
+ priv->curr_bss_params.band = (u8) bss_desc->bss_band;
+
+ if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)
+ priv->curr_bss_params.wmm_enabled = true;
+ else
+ priv->curr_bss_params.wmm_enabled = false;
+
+ if ((priv->wmm_required || bss_desc->bcn_ht_cap) &&
+ priv->curr_bss_params.wmm_enabled)
+ priv->wmm_enabled = true;
+ else
+ priv->wmm_enabled = false;
+
+ priv->curr_bss_params.wmm_uapsd_enabled = false;
+
+ if (priv->wmm_enabled)
+ priv->curr_bss_params.wmm_uapsd_enabled
+ = ((bss_desc->wmm_ie.qos_info_bitmap &
+ IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0);
+
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ASSOC_RESP: curr_pkt_filter is %#x\n",
+ priv->curr_pkt_filter);
+ if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
+ priv->wpa_is_gtk_set = false;
+
+ if (priv->wmm_enabled) {
+ /* Don't re-enable carrier until we get the WMM_GET_STATUS
+ event */
+ enable_data = false;
+ } else {
+ /* Since WMM is not enabled, setup the queues with the
+ defaults */
+ mwifiex_wmm_setup_queue_priorities(priv, NULL);
+ mwifiex_wmm_setup_ac_downgrade(priv);
+ }
+
+ if (enable_data)
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: post association, re-enabling data flow\n");
+
+ /* Reset SNR/NF/RSSI values */
+ priv->data_rssi_last = 0;
+ priv->data_nf_last = 0;
+ priv->data_rssi_avg = 0;
+ priv->data_nf_avg = 0;
+ priv->bcn_rssi_last = 0;
+ priv->bcn_nf_last = 0;
+ priv->bcn_rssi_avg = 0;
+ priv->bcn_nf_avg = 0;
+ priv->rxpd_rate = 0;
+ priv->rxpd_htinfo = 0;
+
+ mwifiex_save_curr_bcn(priv);
+
+ priv->adapter->dbg.num_cmd_assoc_success++;
+
+ mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: associated\n");
+
+ /* Add the ra_list here for infra mode as there will be only 1 ra
+ always */
+ mwifiex_ralist_add(priv,
+ priv->curr_bss_params.bss_descriptor.mac_address);
+
+ if (!netif_carrier_ok(priv->netdev))
+ netif_carrier_on(priv->netdev);
+ mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
+
+ if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
+ priv->scan_block = true;
+ else
+ priv->port_open = true;
+
+done:
+ /* Need to indicate IOCTL complete */
+ if (adapter->curr_cmd->wait_q_enabled) {
+ if (ret)
+ adapter->cmd_wait_q.status = -1;
+ else
+ adapter->cmd_wait_q.status = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * This function prepares command for ad-hoc start.
+ *
+ * Driver will fill up SSID, BSS mode, IBSS parameters, physical
+ * parameters, probe delay, and capability information. Firmware
+ * will fill up beacon period, basic rates and operational rates.
+ *
+ * In addition, the following TLVs are added -
+ * - Channel TLV
+ * - Vendor specific IE
+ * - WPA/WPA2 IE
+ * - HT Capabilities IE
+ * - HT Information IE
+ *
+ * Preparation also includes -
+ * - Setting command ID and proper size
+ * - Ensuring correct endian-ness
+ */
+int
+mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct cfg80211_ssid *req_ssid)
+{
+ int rsn_ie_len = 0;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start =
+ &cmd->params.adhoc_start;
+ struct mwifiex_bssdescriptor *bss_desc;
+ u32 cmd_append_size = 0;
+ u32 i;
+ u16 tmp_cap;
+ struct mwifiex_ie_types_chan_list_param_set *chan_tlv;
+ u8 radio_type;
+
+ struct mwifiex_ie_types_htcap *ht_cap;
+ struct mwifiex_ie_types_htinfo *ht_info;
+ u8 *pos = (u8 *) adhoc_start +
+ sizeof(struct host_cmd_ds_802_11_ad_hoc_start);
+
+ if (!adapter)
+ return -1;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START);
+
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
+ priv->attempted_bss_desc = bss_desc;
+
+ /*
+ * Fill in the parameters for 2 data structures:
+ * 1. struct host_cmd_ds_802_11_ad_hoc_start command
+ * 2. bss_desc
+ * Driver will fill up SSID, bss_mode,IBSS param, Physical Param,
+ * probe delay, and Cap info.
+ * Firmware will fill up beacon period, Basic rates
+ * and operational rates.
+ */
+
+ memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN);
+
+ memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len);
+
+ mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: SSID = %s\n",
+ adhoc_start->ssid);
+
+ memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN);
+ memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len);
+
+ bss_desc->ssid.ssid_len = req_ssid->ssid_len;
+
+ /* Set the BSS mode */
+ adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS;
+ bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
+ adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period);
+ bss_desc->beacon_period = priv->beacon_period;
+
+ /* Set Physical param set */
+/* Parameter IE Id */
+#define DS_PARA_IE_ID 3
+/* Parameter IE length */
+#define DS_PARA_IE_LEN 1
+
+ adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID;
+ adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN;
+
+ if (!mwifiex_get_cfp(priv, adapter->adhoc_start_band,
+ (u16) priv->adhoc_channel, 0)) {
+ struct mwifiex_chan_freq_power *cfp;
+ cfp = mwifiex_get_cfp(priv, adapter->adhoc_start_band,
+ FIRST_VALID_CHANNEL, 0);
+ if (cfp)
+ priv->adhoc_channel = (u8) cfp->channel;
+ }
+
+ if (!priv->adhoc_channel) {
+ mwifiex_dbg(adapter, ERROR,
+ "ADHOC_S_CMD: adhoc_channel cannot be 0\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "info: ADHOC_S_CMD: creating ADHOC on channel %d\n",
+ priv->adhoc_channel);
+
+ priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel;
+ priv->curr_bss_params.band = adapter->adhoc_start_band;
+
+ bss_desc->channel = priv->adhoc_channel;
+ adhoc_start->phy_param_set.ds_param_set.current_chan =
+ priv->adhoc_channel;
+
+ memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set,
+ sizeof(union ieee_types_phy_param_set));
+
+ /* Set IBSS param set */
+/* IBSS parameter IE Id */
+#define IBSS_PARA_IE_ID 6
+/* IBSS parameter IE length */
+#define IBSS_PARA_IE_LEN 2
+
+ adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID;
+ adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN;
+ adhoc_start->ss_param_set.ibss_param_set.atim_window
+ = cpu_to_le16(priv->atim_window);
+ memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set,
+ sizeof(union ieee_types_ss_param_set));
+
+ /* Set Capability info */
+ bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS;
+ tmp_cap = WLAN_CAPABILITY_IBSS;
+
+ /* Set up privacy in bss_desc */
+ if (priv->sec_info.encryption_mode) {
+ /* Ad-Hoc capability privacy on */
+ mwifiex_dbg(adapter, INFO,
+ "info: ADHOC_S_CMD: wep_status set privacy to WEP\n");
+ bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
+ tmp_cap |= WLAN_CAPABILITY_PRIVACY;
+ } else {
+ mwifiex_dbg(adapter, INFO,
+ "info: ADHOC_S_CMD: wep_status NOT set,\t"
+ "setting privacy to ACCEPT ALL\n");
+ bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
+ }
+
+ memset(adhoc_start->data_rate, 0, sizeof(adhoc_start->data_rate));
+ mwifiex_get_active_data_rates(priv, adhoc_start->data_rate);
+ if ((adapter->adhoc_start_band & BAND_G) &&
+ (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, false)) {
+ mwifiex_dbg(adapter, ERROR,
+ "ADHOC_S_CMD: G Protection config failed\n");
+ return -1;
+ }
+ }
+ /* Find the last non zero */
+ for (i = 0; i < sizeof(adhoc_start->data_rate); i++)
+ if (!adhoc_start->data_rate[i])
+ break;
+
+ priv->curr_bss_params.num_of_rates = i;
+
+ /* Copy the ad-hoc creating rates into Current BSS rate structure */
+ memcpy(&priv->curr_bss_params.data_rates,
+ &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates);
+
+ mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: rates=%4ph\n",
+ adhoc_start->data_rate);
+
+ mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n");
+
+ if (IS_SUPPORT_MULTI_BANDS(adapter)) {
+ /* Append a channel TLV */
+ chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos;
+ chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ chan_tlv->header.len =
+ cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set));
+
+ memset(chan_tlv->chan_scan_param, 0x00,
+ sizeof(struct mwifiex_chan_scan_param_set));
+ chan_tlv->chan_scan_param[0].chan_number =
+ (u8) priv->curr_bss_params.bss_descriptor.channel;
+
+ mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Chan = %d\n",
+ chan_tlv->chan_scan_param[0].chan_number);
+
+ chan_tlv->chan_scan_param[0].radio_type
+ = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+ if (adapter->adhoc_start_band & BAND_GN ||
+ adapter->adhoc_start_band & BAND_AN) {
+ if (adapter->sec_chan_offset ==
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
+ chan_tlv->chan_scan_param[0].radio_type |=
+ (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4);
+ else if (adapter->sec_chan_offset ==
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW)
+ chan_tlv->chan_scan_param[0].radio_type |=
+ (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4);
+ }
+ mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Band = %d\n",
+ chan_tlv->chan_scan_param[0].radio_type);
+ pos += sizeof(chan_tlv->header) +
+ sizeof(struct mwifiex_chan_scan_param_set);
+ cmd_append_size +=
+ sizeof(chan_tlv->header) +
+ sizeof(struct mwifiex_chan_scan_param_set);
+ }
+
+ /* Append vendor specific IE TLV */
+ cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv,
+ MWIFIEX_VSIE_MASK_ADHOC, &pos);
+
+ if (priv->sec_info.wpa_enabled) {
+ rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos);
+ if (rsn_ie_len == -1)
+ return -1;
+ cmd_append_size += rsn_ie_len;
+ }
+
+ if (adapter->adhoc_11n_enabled) {
+ /* Fill HT CAPABILITY */
+ ht_cap = (struct mwifiex_ie_types_htcap *) pos;
+ memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap));
+ ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
+ ht_cap->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_ht_cap));
+ radio_type = mwifiex_band_to_radio_type(
+ priv->adapter->config_bands);
+ mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
+
+ if (adapter->sec_chan_offset ==
+ IEEE80211_HT_PARAM_CHA_SEC_NONE) {
+ u16 tmp_ht_cap;
+
+ tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info);
+ tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40;
+ ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap);
+ }
+
+ pos += sizeof(struct mwifiex_ie_types_htcap);
+ cmd_append_size += sizeof(struct mwifiex_ie_types_htcap);
+
+ /* Fill HT INFORMATION */
+ ht_info = (struct mwifiex_ie_types_htinfo *) pos;
+ memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo));
+ ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION);
+ ht_info->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_ht_operation));
+
+ ht_info->ht_oper.primary_chan =
+ (u8) priv->curr_bss_params.bss_descriptor.channel;
+ if (adapter->sec_chan_offset) {
+ ht_info->ht_oper.ht_param = adapter->sec_chan_offset;
+ ht_info->ht_oper.ht_param |=
+ IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+ }
+ ht_info->ht_oper.operation_mode =
+ cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+ ht_info->ht_oper.basic_set[0] = 0xff;
+ pos += sizeof(struct mwifiex_ie_types_htinfo);
+ cmd_append_size +=
+ sizeof(struct mwifiex_ie_types_htinfo);
+ }
+
+ cmd->size =
+ cpu_to_le16((u16)(sizeof(struct host_cmd_ds_802_11_ad_hoc_start)
+ + S_DS_GEN + cmd_append_size));
+
+ if (adapter->adhoc_start_band == BAND_B)
+ tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ else
+ tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+ adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap);
+
+ return 0;
+}
+
+/*
+ * This function prepares command for ad-hoc join.
+ *
+ * Most of the parameters are set up by copying from the target BSS descriptor
+ * from the scan response.
+ *
+ * In addition, the following TLVs are added -
+ * - Channel TLV
+ * - Vendor specific IE
+ * - WPA/WPA2 IE
+ * - 11n IE
+ *
+ * Preparation also includes -
+ * - Setting command ID and proper size
+ * - Ensuring correct endian-ness
+ */
+int
+mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ int rsn_ie_len = 0;
+ struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join =
+ &cmd->params.adhoc_join;
+ struct mwifiex_ie_types_chan_list_param_set *chan_tlv;
+ u32 cmd_append_size = 0;
+ u16 tmp_cap;
+ u32 i, rates_size = 0;
+ u16 curr_pkt_filter;
+ u8 *pos =
+ (u8 *) adhoc_join +
+ sizeof(struct host_cmd_ds_802_11_ad_hoc_join);
+
+/* Use G protection */
+#define USE_G_PROTECTION 0x02
+ if (bss_desc->erp_flags & USE_G_PROTECTION) {
+ curr_pkt_filter =
+ priv->
+ curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON;
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &curr_pkt_filter, false)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "ADHOC_J_CMD: G Protection config failed\n");
+ return -1;
+ }
+ }
+
+ priv->attempted_bss_desc = bss_desc;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN);
+
+ adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS;
+
+ adhoc_join->bss_descriptor.beacon_period
+ = cpu_to_le16(bss_desc->beacon_period);
+
+ memcpy(&adhoc_join->bss_descriptor.bssid,
+ &bss_desc->mac_address, ETH_ALEN);
+
+ memcpy(&adhoc_join->bss_descriptor.ssid,
+ &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len);
+
+ memcpy(&adhoc_join->bss_descriptor.phy_param_set,
+ &bss_desc->phy_param_set,
+ sizeof(union ieee_types_phy_param_set));
+
+ memcpy(&adhoc_join->bss_descriptor.ss_param_set,
+ &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set));
+
+ tmp_cap = bss_desc->cap_info_bitmap;
+
+ tmp_cap &= CAPINFO_MASK;
+
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n",
+ tmp_cap, CAPINFO_MASK);
+
+ /* Information on BSSID descriptor passed to FW */
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ADHOC_J_CMD: BSSID=%pM, SSID='%s'\n",
+ adhoc_join->bss_descriptor.bssid,
+ adhoc_join->bss_descriptor.ssid);
+
+ for (i = 0; i < MWIFIEX_SUPPORTED_RATES &&
+ bss_desc->supported_rates[i]; i++)
+ ;
+ rates_size = i;
+
+ /* Copy Data Rates from the Rates recorded in scan response */
+ memset(adhoc_join->bss_descriptor.data_rates, 0,
+ sizeof(adhoc_join->bss_descriptor.data_rates));
+ memcpy(adhoc_join->bss_descriptor.data_rates,
+ bss_desc->supported_rates, rates_size);
+
+ /* Copy the adhoc join rates into Current BSS state structure */
+ priv->curr_bss_params.num_of_rates = rates_size;
+ memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates,
+ rates_size);
+
+ /* Copy the channel information */
+ priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel;
+ priv->curr_bss_params.band = (u8) bss_desc->bss_band;
+
+ if (priv->sec_info.wep_enabled || priv->sec_info.wpa_enabled)
+ tmp_cap |= WLAN_CAPABILITY_PRIVACY;
+
+ if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) {
+ /* Append a channel TLV */
+ chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos;
+ chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ chan_tlv->header.len =
+ cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set));
+
+ memset(chan_tlv->chan_scan_param, 0x00,
+ sizeof(struct mwifiex_chan_scan_param_set));
+ chan_tlv->chan_scan_param[0].chan_number =
+ (bss_desc->phy_param_set.ds_param_set.current_chan);
+ mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Chan=%d\n",
+ chan_tlv->chan_scan_param[0].chan_number);
+
+ chan_tlv->chan_scan_param[0].radio_type =
+ mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
+
+ mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Band=%d\n",
+ chan_tlv->chan_scan_param[0].radio_type);
+ pos += sizeof(chan_tlv->header) +
+ sizeof(struct mwifiex_chan_scan_param_set);
+ cmd_append_size += sizeof(chan_tlv->header) +
+ sizeof(struct mwifiex_chan_scan_param_set);
+ }
+
+ if (priv->sec_info.wpa_enabled)
+ rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos);
+ if (rsn_ie_len == -1)
+ return -1;
+ cmd_append_size += rsn_ie_len;
+
+ if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info))
+ cmd_append_size += mwifiex_cmd_append_11n_tlv(priv,
+ bss_desc, &pos);
+
+ /* Append vendor specific IE TLV */
+ cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv,
+ MWIFIEX_VSIE_MASK_ADHOC, &pos);
+
+ cmd->size = cpu_to_le16
+ ((u16) (sizeof(struct host_cmd_ds_802_11_ad_hoc_join)
+ + S_DS_GEN + cmd_append_size));
+
+ adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap);
+
+ return 0;
+}
+
+/*
+ * This function handles the command response of ad-hoc start and
+ * ad-hoc join.
+ *
+ * The function generates a device-connected event to notify
+ * the applications, in case of successful ad-hoc start/join, and
+ * saves the beacon buffer.
+ */
+int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ int ret = 0;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_ad_hoc_start_result *start_result =
+ &resp->params.start_result;
+ struct host_cmd_ds_802_11_ad_hoc_join_result *join_result =
+ &resp->params.join_result;
+ struct mwifiex_bssdescriptor *bss_desc;
+ u16 cmd = le16_to_cpu(resp->command);
+ u8 result;
+
+ if (cmd == HostCmd_CMD_802_11_AD_HOC_START)
+ result = start_result->result;
+ else
+ result = join_result->result;
+
+ bss_desc = priv->attempted_bss_desc;
+
+ /* Join result code 0 --> SUCCESS */
+ if (result) {
+ mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n");
+ if (priv->media_connected)
+ mwifiex_reset_connect_state(priv, result);
+
+ memset(&priv->curr_bss_params.bss_descriptor,
+ 0x00, sizeof(struct mwifiex_bssdescriptor));
+
+ ret = -1;
+ goto done;
+ }
+
+ /* Send a Media Connected event, according to the Spec */
+ priv->media_connected = true;
+
+ if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) {
+ mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_S_RESP %s\n",
+ bss_desc->ssid.ssid);
+
+ /* Update the created network descriptor with the new BSSID */
+ memcpy(bss_desc->mac_address,
+ start_result->bssid, ETH_ALEN);
+
+ priv->adhoc_state = ADHOC_STARTED;
+ } else {
+ /*
+ * Now the join cmd should be successful.
+ * If BSSID has changed use SSID to compare instead of BSSID
+ */
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ADHOC_J_RESP %s\n",
+ bss_desc->ssid.ssid);
+
+ /*
+ * Make a copy of current BSSID descriptor, only needed for
+ * join since the current descriptor is already being used
+ * for adhoc start
+ */
+ memcpy(&priv->curr_bss_params.bss_descriptor,
+ bss_desc, sizeof(struct mwifiex_bssdescriptor));
+
+ priv->adhoc_state = ADHOC_JOINED;
+ }
+
+ mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: channel = %d\n",
+ priv->adhoc_channel);
+ mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: BSSID = %pM\n",
+ priv->curr_bss_params.bss_descriptor.mac_address);
+
+ if (!netif_carrier_ok(priv->netdev))
+ netif_carrier_on(priv->netdev);
+ mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
+
+ mwifiex_save_curr_bcn(priv);
+
+done:
+ /* Need to indicate IOCTL complete */
+ if (adapter->curr_cmd->wait_q_enabled) {
+ if (ret)
+ adapter->cmd_wait_q.status = -1;
+ else
+ adapter->cmd_wait_q.status = 0;
+
+ }
+
+ return ret;
+}
+
+/*
+ * This function associates to a specific BSS discovered in a scan.
+ *
+ * It clears any past association response stored for application
+ * retrieval and calls the command preparation routine to send the
+ * command to firmware.
+ */
+int mwifiex_associate(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ /* Return error if the adapter is not STA role or table entry
+ * is not marked as infra.
+ */
+ if ((GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) ||
+ (bss_desc->bss_mode != NL80211_IFTYPE_STATION))
+ return -1;
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ priv->adapter->config_bands & BAND_AAC)
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
+ /* Clear any past association response stored for application
+ retrieval */
+ priv->assoc_rsp_size = 0;
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE,
+ HostCmd_ACT_GEN_SET, 0, bss_desc, true);
+}
+
+/*
+ * This function starts an ad-hoc network.
+ *
+ * It calls the command preparation routine to send the command to firmware.
+ */
+int
+mwifiex_adhoc_start(struct mwifiex_private *priv,
+ struct cfg80211_ssid *adhoc_ssid)
+{
+ mwifiex_dbg(priv->adapter, INFO, "info: Adhoc Channel = %d\n",
+ priv->adhoc_channel);
+ mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.channel = %d\n",
+ priv->curr_bss_params.bss_descriptor.channel);
+ mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.band = %d\n",
+ priv->curr_bss_params.band);
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->config_bands & BAND_AAC)
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START,
+ HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true);
+}
+
+/*
+ * This function joins an ad-hoc network found in a previous scan.
+ *
+ * It calls the command preparation routine to send the command to firmware,
+ * if already not connected to the requested SSID.
+ */
+int mwifiex_adhoc_join(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: adhoc join: curr_bss ssid =%s\n",
+ priv->curr_bss_params.bss_descriptor.ssid.ssid);
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: adhoc join: curr_bss ssid_len =%u\n",
+ priv->curr_bss_params.bss_descriptor.ssid.ssid_len);
+ mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid =%s\n",
+ bss_desc->ssid.ssid);
+ mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid_len =%u\n",
+ bss_desc->ssid.ssid_len);
+
+ /* Check if the requested SSID is already joined */
+ if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len &&
+ !mwifiex_ssid_cmp(&bss_desc->ssid,
+ &priv->curr_bss_params.bss_descriptor.ssid) &&
+ (priv->curr_bss_params.bss_descriptor.bss_mode ==
+ NL80211_IFTYPE_ADHOC)) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: ADHOC_J_CMD: new ad-hoc SSID\t"
+ "is the same as current; not attempting to re-join\n");
+ return -1;
+ }
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ priv->adapter->config_bands & BAND_AAC)
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: curr_bss_params.channel = %d\n",
+ priv->curr_bss_params.bss_descriptor.channel);
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: curr_bss_params.band = %c\n",
+ priv->curr_bss_params.band);
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN,
+ HostCmd_ACT_GEN_SET, 0, bss_desc, true);
+}
+
+/*
+ * This function deauthenticates/disconnects from infra network by sending
+ * deauthentication request.
+ */
+static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac)
+{
+ u8 mac_address[ETH_ALEN];
+ int ret;
+
+ if (!mac || is_zero_ether_addr(mac))
+ memcpy(mac_address,
+ priv->curr_bss_params.bss_descriptor.mac_address,
+ ETH_ALEN);
+ else
+ memcpy(mac_address, mac, ETH_ALEN);
+
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE,
+ HostCmd_ACT_GEN_SET, 0, mac_address, true);
+
+ return ret;
+}
+
+/*
+ * This function deauthenticates/disconnects from a BSS.
+ *
+ * In case of infra made, it sends deauthentication request, and
+ * in case of ad-hoc mode, a stop network request is sent to the firmware.
+ * In AP mode, a command to stop bss is sent to firmware.
+ */
+int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
+{
+ int ret = 0;
+
+ if (!priv->media_connected)
+ return 0;
+
+ switch (priv->bss_mode) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ ret = mwifiex_deauthenticate_infra(priv, mac);
+ if (ret)
+ cfg80211_disconnected(priv->netdev, 0, NULL, 0,
+ true, GFP_KERNEL);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
+ case NL80211_IFTYPE_AP:
+ return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* This function deauthenticates/disconnects from all BSS. */
+void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_private *priv;
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv)
+ mwifiex_deauthenticate(priv, NULL);
+ }
+}
+EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all);
+
+/*
+ * This function converts band to radio type used in channel TLV.
+ */
+u8
+mwifiex_band_to_radio_type(u8 band)
+{
+ switch (band) {
+ case BAND_A:
+ case BAND_AN:
+ case BAND_A | BAND_AN:
+ case BAND_A | BAND_AN | BAND_AAC:
+ return HostCmd_SCAN_RADIO_TYPE_A;
+ case BAND_B:
+ case BAND_G:
+ case BAND_B | BAND_G:
+ default:
+ return HostCmd_SCAN_RADIO_TYPE_BG;
+ }
+}
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
new file mode 100644
index 000000000000..79c16de8743e
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -0,0 +1,1552 @@
+/*
+ * Marvell Wireless LAN device driver: major functions
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "wmm.h"
+#include "cfg80211.h"
+#include "11n.h"
+
+#define VERSION "1.0"
+
+static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK;
+module_param(debug_mask, uint, 0);
+MODULE_PARM_DESC(debug_mask, "bitmap for debug flags");
+
+const char driver_version[] = "mwifiex " VERSION " (%s) ";
+static char *cal_data_cfg;
+module_param(cal_data_cfg, charp, 0);
+
+static unsigned short driver_mode;
+module_param(driver_mode, ushort, 0);
+MODULE_PARM_DESC(driver_mode,
+ "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7");
+
+/*
+ * This function registers the device and performs all the necessary
+ * initializations.
+ *
+ * The following initialization operations are performed -
+ * - Allocate adapter structure
+ * - Save interface specific operations table in adapter
+ * - Call interface specific initialization routine
+ * - Allocate private structures
+ * - Set default adapter structure parameters
+ * - Initialize locks
+ *
+ * In case of any errors during inittialization, this function also ensures
+ * proper cleanup before exiting.
+ */
+static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
+ void **padapter)
+{
+ struct mwifiex_adapter *adapter;
+ int i;
+
+ adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL);
+ if (!adapter)
+ return -ENOMEM;
+
+ *padapter = adapter;
+ adapter->card = card;
+
+ /* Save interface specific operations in adapter */
+ memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops));
+ adapter->debug_mask = debug_mask;
+
+ /* card specific initialization has been deferred until now .. */
+ if (adapter->if_ops.init_if)
+ if (adapter->if_ops.init_if(adapter))
+ goto error;
+
+ adapter->priv_num = 0;
+
+ for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
+ /* Allocate memory for private structure */
+ adapter->priv[i] =
+ kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL);
+ if (!adapter->priv[i])
+ goto error;
+
+ adapter->priv[i]->adapter = adapter;
+ adapter->priv_num++;
+ }
+ mwifiex_init_lock_list(adapter);
+
+ setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func,
+ (unsigned long)adapter);
+
+ return 0;
+
+error:
+ mwifiex_dbg(adapter, ERROR,
+ "info: leave mwifiex_register with error\n");
+
+ for (i = 0; i < adapter->priv_num; i++)
+ kfree(adapter->priv[i]);
+
+ kfree(adapter);
+
+ return -1;
+}
+
+/*
+ * This function unregisters the device and performs all the necessary
+ * cleanups.
+ *
+ * The following cleanup operations are performed -
+ * - Free the timers
+ * - Free beacon buffers
+ * - Free private structures
+ * - Free adapter structure
+ */
+static int mwifiex_unregister(struct mwifiex_adapter *adapter)
+{
+ s32 i;
+
+ if (adapter->if_ops.cleanup_if)
+ adapter->if_ops.cleanup_if(adapter);
+
+ del_timer_sync(&adapter->cmd_timer);
+
+ /* Free private structures */
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ mwifiex_free_curr_bcn(adapter->priv[i]);
+ kfree(adapter->priv[i]);
+ }
+ }
+
+ vfree(adapter->chan_stats);
+ kfree(adapter);
+ return 0;
+}
+
+void mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ if (adapter->mwifiex_processing) {
+ adapter->more_task_flag = true;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ queue_work(adapter->workqueue, &adapter->main_work);
+ }
+}
+EXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
+
+static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ if (adapter->rx_processing) {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ queue_work(adapter->rx_workqueue, &adapter->rx_work);
+ }
+}
+
+static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+ struct mwifiex_rxinfo *rx_info;
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ if (adapter->rx_processing || adapter->rx_locked) {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ goto exit_rx_proc;
+ } else {
+ adapter->rx_processing = true;
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ }
+
+ /* Check for Rx data */
+ while ((skb = skb_dequeue(&adapter->rx_data_q))) {
+ atomic_dec(&adapter->rx_pending);
+ if ((adapter->delay_main_work ||
+ adapter->iface_type == MWIFIEX_USB) &&
+ (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
+ if (adapter->if_ops.submit_rem_rx_urbs)
+ adapter->if_ops.submit_rem_rx_urbs(adapter);
+ adapter->delay_main_work = false;
+ mwifiex_queue_main_work(adapter);
+ }
+ rx_info = MWIFIEX_SKB_RXCB(skb);
+ if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
+ if (adapter->if_ops.deaggr_pkt)
+ adapter->if_ops.deaggr_pkt(adapter, skb);
+ dev_kfree_skb_any(skb);
+ } else {
+ mwifiex_handle_rx_packet(adapter, skb);
+ }
+ }
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ adapter->rx_processing = false;
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+
+exit_rx_proc:
+ return 0;
+}
+
+/*
+ * The main process.
+ *
+ * This function is the main procedure of the driver and handles various driver
+ * operations. It runs in a loop and provides the core functionalities.
+ *
+ * The main responsibilities of this function are -
+ * - Ensure concurrency control
+ * - Handle pending interrupts and call interrupt handlers
+ * - Wake up the card if required
+ * - Handle command responses and call response handlers
+ * - Handle events and call event handlers
+ * - Execute pending commands
+ * - Transmit pending data packets
+ */
+int mwifiex_main_process(struct mwifiex_adapter *adapter)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+
+ /* Check if already processing */
+ if (adapter->mwifiex_processing || adapter->main_locked) {
+ adapter->more_task_flag = true;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ goto exit_main_proc;
+ } else {
+ adapter->mwifiex_processing = true;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ }
+process_start:
+ do {
+ if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) ||
+ (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
+ break;
+
+ /* For non-USB interfaces, If we process interrupts first, it
+ * would increase RX pending even further. Avoid this by
+ * checking if rx_pending has crossed high threshold and
+ * schedule rx work queue and then process interrupts.
+ * For USB interface, there are no interrupts. We already have
+ * HIGH_RX_PENDING check in usb.c
+ */
+ if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
+ adapter->iface_type != MWIFIEX_USB) {
+ adapter->delay_main_work = true;
+ mwifiex_queue_rx_work(adapter);
+ break;
+ }
+
+ /* Handle pending interrupt if any */
+ if (adapter->int_status) {
+ if (adapter->hs_activated)
+ mwifiex_process_hs_config(adapter);
+ if (adapter->if_ops.process_int_status)
+ adapter->if_ops.process_int_status(adapter);
+ }
+
+ if (adapter->rx_work_enabled && adapter->data_received)
+ mwifiex_queue_rx_work(adapter);
+
+ /* Need to wake up the card ? */
+ if ((adapter->ps_state == PS_STATE_SLEEP) &&
+ (adapter->pm_wakeup_card_req &&
+ !adapter->pm_wakeup_fw_try) &&
+ (is_command_pending(adapter) ||
+ !skb_queue_empty(&adapter->tx_data_q) ||
+ !mwifiex_bypass_txlist_empty(adapter) ||
+ !mwifiex_wmm_lists_empty(adapter))) {
+ adapter->pm_wakeup_fw_try = true;
+ mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
+ adapter->if_ops.wakeup(adapter);
+ continue;
+ }
+
+ if (IS_CARD_RX_RCVD(adapter)) {
+ adapter->data_received = false;
+ adapter->pm_wakeup_fw_try = false;
+ del_timer(&adapter->wakeup_timer);
+ if (adapter->ps_state == PS_STATE_SLEEP)
+ adapter->ps_state = PS_STATE_AWAKE;
+ } else {
+ /* We have tried to wakeup the card already */
+ if (adapter->pm_wakeup_fw_try)
+ break;
+ if (adapter->ps_state != PS_STATE_AWAKE)
+ break;
+ if (adapter->tx_lock_flag) {
+ if (adapter->iface_type == MWIFIEX_USB) {
+ if (!adapter->usb_mc_setup)
+ break;
+ } else
+ break;
+ }
+
+ if ((!adapter->scan_chan_gap_enabled &&
+ adapter->scan_processing) || adapter->data_sent ||
+ mwifiex_is_tdls_chan_switching
+ (mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_STA)) ||
+ (mwifiex_wmm_lists_empty(adapter) &&
+ mwifiex_bypass_txlist_empty(adapter) &&
+ skb_queue_empty(&adapter->tx_data_q))) {
+ if (adapter->cmd_sent || adapter->curr_cmd ||
+ !mwifiex_is_send_cmd_allowed
+ (mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_STA)) ||
+ (!is_command_pending(adapter)))
+ break;
+ }
+ }
+
+ /* Check for event */
+ if (adapter->event_received) {
+ adapter->event_received = false;
+ mwifiex_process_event(adapter);
+ }
+
+ /* Check for Cmd Resp */
+ if (adapter->cmd_resp_received) {
+ adapter->cmd_resp_received = false;
+ mwifiex_process_cmdresp(adapter);
+
+ /* call mwifiex back when init_fw is done */
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) {
+ adapter->hw_status = MWIFIEX_HW_STATUS_READY;
+ mwifiex_init_fw_complete(adapter);
+ }
+ }
+
+ /* Check if we need to confirm Sleep Request
+ received previously */
+ if (adapter->ps_state == PS_STATE_PRE_SLEEP) {
+ if (!adapter->cmd_sent && !adapter->curr_cmd)
+ mwifiex_check_ps_cond(adapter);
+ }
+
+ /* * The ps_state may have been changed during processing of
+ * Sleep Request event.
+ */
+ if ((adapter->ps_state == PS_STATE_SLEEP) ||
+ (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
+ (adapter->ps_state == PS_STATE_SLEEP_CFM)) {
+ continue;
+ }
+
+ if (adapter->tx_lock_flag) {
+ if (adapter->iface_type == MWIFIEX_USB) {
+ if (!adapter->usb_mc_setup)
+ continue;
+ } else
+ continue;
+ }
+
+ if (!adapter->cmd_sent && !adapter->curr_cmd &&
+ mwifiex_is_send_cmd_allowed
+ (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
+ if (mwifiex_exec_next_cmd(adapter) == -1) {
+ ret = -1;
+ break;
+ }
+ }
+
+ /** If USB Multi channel setup ongoing,
+ * wait for ready to tx data.
+ */
+ if (adapter->iface_type == MWIFIEX_USB &&
+ adapter->usb_mc_setup)
+ continue;
+
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent &&
+ !skb_queue_empty(&adapter->tx_data_q)) {
+ mwifiex_process_tx_queue(adapter);
+ if (adapter->hs_activated) {
+ adapter->is_hs_configured = false;
+ mwifiex_hs_activated_event
+ (mwifiex_get_priv
+ (adapter, MWIFIEX_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent &&
+ !mwifiex_bypass_txlist_empty(adapter) &&
+ !mwifiex_is_tdls_chan_switching
+ (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
+ mwifiex_process_bypass_tx(adapter);
+ if (adapter->hs_activated) {
+ adapter->is_hs_configured = false;
+ mwifiex_hs_activated_event
+ (mwifiex_get_priv
+ (adapter, MWIFIEX_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) &&
+ !mwifiex_is_tdls_chan_switching
+ (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
+ mwifiex_wmm_process_tx(adapter);
+ if (adapter->hs_activated) {
+ adapter->is_hs_configured = false;
+ mwifiex_hs_activated_event
+ (mwifiex_get_priv
+ (adapter, MWIFIEX_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
+ if (adapter->delay_null_pkt && !adapter->cmd_sent &&
+ !adapter->curr_cmd && !is_command_pending(adapter) &&
+ (mwifiex_wmm_lists_empty(adapter) &&
+ mwifiex_bypass_txlist_empty(adapter) &&
+ skb_queue_empty(&adapter->tx_data_q))) {
+ if (!mwifiex_send_null_packet
+ (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+ MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
+ MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) {
+ adapter->delay_null_pkt = false;
+ adapter->ps_state = PS_STATE_SLEEP;
+ }
+ break;
+ }
+ } while (true);
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ if (adapter->more_task_flag) {
+ adapter->more_task_flag = false;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ goto process_start;
+ }
+ adapter->mwifiex_processing = false;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+
+exit_main_proc:
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING)
+ mwifiex_shutdown_drv(adapter);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mwifiex_main_process);
+
+/*
+ * This function frees the adapter structure.
+ *
+ * Additionally, this closes the netlink socket, frees the timers
+ * and private structures.
+ */
+static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
+{
+ if (!adapter) {
+ pr_err("%s: adapter is NULL\n", __func__);
+ return;
+ }
+
+ mwifiex_unregister(adapter);
+ pr_debug("info: %s: free adapter\n", __func__);
+}
+
+/*
+ * This function cancels all works in the queue and destroys
+ * the main workqueue.
+ */
+static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
+{
+ flush_workqueue(adapter->workqueue);
+ destroy_workqueue(adapter->workqueue);
+ adapter->workqueue = NULL;
+
+ if (adapter->rx_workqueue) {
+ flush_workqueue(adapter->rx_workqueue);
+ destroy_workqueue(adapter->rx_workqueue);
+ adapter->rx_workqueue = NULL;
+ }
+}
+
+/*
+ * This function gets firmware and initializes it.
+ *
+ * The main initialization steps followed are -
+ * - Download the correct firmware to card
+ * - Issue the init commands to firmware
+ */
+static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
+{
+ int ret;
+ char fmt[64];
+ struct mwifiex_private *priv;
+ struct mwifiex_adapter *adapter = context;
+ struct mwifiex_fw_image fw;
+ struct semaphore *sem = adapter->card_sem;
+ bool init_failed = false;
+ struct wireless_dev *wdev;
+
+ if (!firmware) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to get firmware %s\n", adapter->fw_name);
+ goto err_dnld_fw;
+ }
+
+ memset(&fw, 0, sizeof(struct mwifiex_fw_image));
+ adapter->firmware = firmware;
+ fw.fw_buf = (u8 *) adapter->firmware->data;
+ fw.fw_len = adapter->firmware->size;
+
+ if (adapter->if_ops.dnld_fw)
+ ret = adapter->if_ops.dnld_fw(adapter, &fw);
+ else
+ ret = mwifiex_dnld_fw(adapter, &fw);
+ if (ret == -1)
+ goto err_dnld_fw;
+
+ mwifiex_dbg(adapter, MSG, "WLAN FW is active\n");
+
+ if (cal_data_cfg) {
+ if ((request_firmware(&adapter->cal_data, cal_data_cfg,
+ adapter->dev)) < 0)
+ mwifiex_dbg(adapter, ERROR,
+ "Cal data request_firmware() failed\n");
+ }
+
+ /* enable host interrupt after fw dnld is successful */
+ if (adapter->if_ops.enable_int) {
+ if (adapter->if_ops.enable_int(adapter))
+ goto err_dnld_fw;
+ }
+
+ adapter->init_wait_q_woken = false;
+ ret = mwifiex_init_fw(adapter);
+ if (ret == -1) {
+ goto err_init_fw;
+ } else if (!ret) {
+ adapter->hw_status = MWIFIEX_HW_STATUS_READY;
+ goto done;
+ }
+ /* Wait for mwifiex_init to complete */
+ wait_event_interruptible(adapter->init_wait_q,
+ adapter->init_wait_q_woken);
+ if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
+ goto err_init_fw;
+
+ priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
+ if (mwifiex_register_cfg80211(adapter)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot register with cfg80211\n");
+ goto err_init_fw;
+ }
+
+ if (mwifiex_init_channel_scan_gap(adapter)) {
+ mwifiex_dbg(adapter, ERROR,
+ "could not init channel stats table\n");
+ goto err_init_fw;
+ }
+
+ if (driver_mode) {
+ driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK;
+ driver_mode |= MWIFIEX_DRIVER_MODE_STA;
+ }
+
+ rtnl_lock();
+ /* Create station interface by default */
+ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
+ NL80211_IFTYPE_STATION, NULL, NULL);
+ if (IS_ERR(wdev)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create default STA interface\n");
+ rtnl_unlock();
+ goto err_add_intf;
+ }
+
+ if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
+ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
+ NL80211_IFTYPE_AP, NULL, NULL);
+ if (IS_ERR(wdev)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create AP interface\n");
+ rtnl_unlock();
+ goto err_add_intf;
+ }
+ }
+
+ if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
+ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
+ NL80211_IFTYPE_P2P_CLIENT, NULL,
+ NULL);
+ if (IS_ERR(wdev)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot create p2p client interface\n");
+ rtnl_unlock();
+ goto err_add_intf;
+ }
+ }
+ rtnl_unlock();
+
+ mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
+ mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt);
+ goto done;
+
+err_add_intf:
+ wiphy_unregister(adapter->wiphy);
+ wiphy_free(adapter->wiphy);
+err_init_fw:
+ if (adapter->if_ops.disable_int)
+ adapter->if_ops.disable_int(adapter);
+err_dnld_fw:
+ mwifiex_dbg(adapter, ERROR,
+ "info: %s: unregister device\n", __func__);
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
+ pr_debug("info: %s: shutdown mwifiex\n", __func__);
+ adapter->init_wait_q_woken = false;
+
+ if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
+ wait_event_interruptible(adapter->init_wait_q,
+ adapter->init_wait_q_woken);
+ }
+ adapter->surprise_removed = true;
+ mwifiex_terminate_workqueue(adapter);
+ init_failed = true;
+done:
+ if (adapter->cal_data) {
+ release_firmware(adapter->cal_data);
+ adapter->cal_data = NULL;
+ }
+ if (adapter->firmware) {
+ release_firmware(adapter->firmware);
+ adapter->firmware = NULL;
+ }
+ if (init_failed)
+ mwifiex_free_adapter(adapter);
+ up(sem);
+ return;
+}
+
+/*
+ * This function initializes the hardware and gets firmware.
+ */
+static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
+{
+ int ret;
+
+ ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
+ adapter->dev, GFP_KERNEL, adapter,
+ mwifiex_fw_dpc);
+ if (ret < 0)
+ mwifiex_dbg(adapter, ERROR,
+ "request_firmware_nowait error %d\n", ret);
+ return ret;
+}
+
+/*
+ * CFG802.11 network device handler for open.
+ *
+ * Starts the data queue.
+ */
+static int
+mwifiex_open(struct net_device *dev)
+{
+ netif_carrier_off(dev);
+
+ return 0;
+}
+
+/*
+ * CFG802.11 network device handler for close.
+ */
+static int
+mwifiex_close(struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (priv->scan_request) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "aborting scan on ndo_stop\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ priv->scan_aborting = true;
+ }
+
+ return 0;
+}
+
+static bool
+mwifiex_bypass_tx_queue(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+
+ if (ntohs(eth_hdr->h_proto) == ETH_P_PAE ||
+ mwifiex_is_skb_mgmt_frame(skb) ||
+ (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
+ ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) {
+ mwifiex_dbg(priv->adapter, DATA,
+ "bypass txqueue; eth type %#x, mgmt %d\n",
+ ntohs(eth_hdr->h_proto),
+ mwifiex_is_skb_mgmt_frame(skb));
+ return true;
+ }
+
+ return false;
+}
+/*
+ * Add buffer into wmm tx queue and queue work to transmit it.
+ */
+int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
+{
+ struct netdev_queue *txq;
+ int index = mwifiex_1d_to_wmm_queue[skb->priority];
+
+ if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
+ txq = netdev_get_tx_queue(priv->netdev, index);
+ if (!netif_tx_queue_stopped(txq)) {
+ netif_tx_stop_queue(txq);
+ mwifiex_dbg(priv->adapter, DATA,
+ "stop queue: %d\n", index);
+ }
+ }
+
+ if (mwifiex_bypass_tx_queue(priv, skb)) {
+ atomic_inc(&priv->adapter->tx_pending);
+ atomic_inc(&priv->adapter->bypass_tx_pending);
+ mwifiex_wmm_add_buf_bypass_txqueue(priv, skb);
+ } else {
+ atomic_inc(&priv->adapter->tx_pending);
+ mwifiex_wmm_add_buf_txqueue(priv, skb);
+ }
+
+ mwifiex_queue_main_work(priv->adapter);
+
+ return 0;
+}
+
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie)
+{
+ struct sk_buff *orig_skb = skb;
+ struct mwifiex_txinfo *tx_info, *orig_tx_info;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (skb) {
+ unsigned long flags;
+ int id;
+
+ spin_lock_irqsave(&priv->ack_status_lock, flags);
+ id = idr_alloc(&priv->ack_status_frames, orig_skb,
+ 1, 0x10, GFP_ATOMIC);
+ spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+ if (id >= 0) {
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tx_info->ack_frame_id = id;
+ tx_info->flags |= flag;
+ orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
+ orig_tx_info->ack_frame_id = id;
+ orig_tx_info->flags |= flag;
+
+ if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
+ orig_tx_info->cookie = *cookie;
+
+ } else if (skb_shared(skb)) {
+ kfree_skb(orig_skb);
+ } else {
+ kfree_skb(skb);
+ skb = orig_skb;
+ }
+ } else {
+ /* couldn't clone -- lose tx status ... */
+ skb = orig_skb;
+ }
+
+ return skb;
+}
+
+/*
+ * CFG802.11 network device handler for data transmission.
+ */
+static int
+mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct sk_buff *new_skb;
+ struct mwifiex_txinfo *tx_info;
+ bool multicast;
+
+ mwifiex_dbg(priv->adapter, DATA,
+ "data: %lu BSS(%d-%d): Data <= kernel\n",
+ jiffies, priv->bss_type, priv->bss_num);
+
+ if (priv->adapter->surprise_removed) {
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+ if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Tx: bad skb len %d\n", skb->len);
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+ if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
+ mwifiex_dbg(priv->adapter, DATA,
+ "data: Tx: insufficient skb headroom %d\n",
+ skb_headroom(skb));
+ /* Insufficient skb headroom - allocate a new skb */
+ new_skb =
+ skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
+ if (unlikely(!new_skb)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Tx: cannot alloca new_skb\n");
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+ kfree_skb(skb);
+ skb = new_skb;
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: new skb headroomd %d\n",
+ skb_headroom(skb));
+ }
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->pkt_len = skb->len;
+
+ multicast = is_multicast_ether_addr(skb->data);
+
+ if (unlikely(!multicast && skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
+ priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
+ skb = mwifiex_clone_skb_for_tx_status(priv,
+ skb,
+ MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
+
+ /* Record the current time the packet was queued; used to
+ * determine the amount of time the packet was queued in
+ * the driver before it was sent to the firmware.
+ * The delay is then sent along with the packet to the
+ * firmware for aggregate delay calculation for stats and
+ * MSDU lifetime expiry.
+ */
+ __net_timestamp(skb);
+
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->bss_type == MWIFIEX_BSS_TYPE_STA &&
+ !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) {
+ if (priv->adapter->auto_tdls && priv->check_tdls_tx)
+ mwifiex_tdls_check_tx(priv, skb);
+ }
+
+ mwifiex_queue_tx_pkt(priv, skb);
+
+ return 0;
+}
+
+/*
+ * CFG802.11 network device handler for setting MAC address.
+ */
+static int
+mwifiex_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct sockaddr *hw_addr = addr;
+ int ret;
+
+ memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN);
+
+ /* Send request to firmware */
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
+
+ if (!ret)
+ memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN);
+ else
+ mwifiex_dbg(priv->adapter, ERROR,
+ "set mac address failed: ret=%d\n", ret);
+
+ memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
+
+ return ret;
+}
+
+/*
+ * CFG802.11 network device handler for setting multicast list.
+ */
+static void mwifiex_set_multicast_list(struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_multicast_list mcast_list;
+
+ if (dev->flags & IFF_PROMISC) {
+ mcast_list.mode = MWIFIEX_PROMISC_MODE;
+ } else if (dev->flags & IFF_ALLMULTI ||
+ netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) {
+ mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
+ } else {
+ mcast_list.mode = MWIFIEX_MULTICAST_MODE;
+ mcast_list.num_multicast_addr =
+ mwifiex_copy_mcast_addr(&mcast_list, dev);
+ }
+ mwifiex_request_set_multicast_list(priv, &mcast_list);
+}
+
+/*
+ * CFG802.11 network device handler for transmission timeout.
+ */
+static void
+mwifiex_tx_timeout(struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ priv->num_tx_timeout++;
+ priv->tx_timeout_cnt++;
+ mwifiex_dbg(priv->adapter, ERROR,
+ "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
+ jiffies, priv->tx_timeout_cnt, priv->bss_type,
+ priv->bss_num);
+ mwifiex_set_trans_start(dev);
+
+ if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
+ priv->adapter->if_ops.card_reset) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "tx_timeout_cnt exceeds threshold.\t"
+ "Triggering card reset!\n");
+ priv->adapter->if_ops.card_reset(priv->adapter);
+ }
+}
+
+void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter)
+{
+ struct usb_card_rec *card = adapter->card;
+ struct mwifiex_private *priv;
+ u16 tx_buf_size;
+ int i, ret;
+
+ card->mc_resync_flag = true;
+ for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
+ if (atomic_read(&card->port[i].tx_data_urb_pending)) {
+ mwifiex_dbg(adapter, WARN, "pending data urb in sys\n");
+ return;
+ }
+ }
+
+ card->mc_resync_flag = false;
+ tx_buf_size = 0xffff;
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
+ HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false);
+ if (ret)
+ mwifiex_dbg(adapter, ERROR,
+ "send reconfig tx buf size cmd err\n");
+}
+EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync);
+
+void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter)
+{
+ void *p;
+ char drv_version[64];
+ struct usb_card_rec *cardp;
+ struct sdio_mmc_card *sdio_card;
+ struct mwifiex_private *priv;
+ int i, idx;
+ struct netdev_queue *txq;
+ struct mwifiex_debug_info *debug_info;
+
+ if (adapter->drv_info_dump) {
+ vfree(adapter->drv_info_dump);
+ adapter->drv_info_dump = NULL;
+ adapter->drv_info_size = 0;
+ }
+
+ mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n");
+
+ adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX);
+
+ if (!adapter->drv_info_dump)
+ return;
+
+ p = (char *)(adapter->drv_info_dump);
+ p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
+
+ mwifiex_drv_get_driver_version(adapter, drv_version,
+ sizeof(drv_version) - 1);
+ p += sprintf(p, "driver_version = %s\n", drv_version);
+
+ if (adapter->iface_type == MWIFIEX_USB) {
+ cardp = (struct usb_card_rec *)adapter->card;
+ p += sprintf(p, "tx_cmd_urb_pending = %d\n",
+ atomic_read(&cardp->tx_cmd_urb_pending));
+ p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n",
+ atomic_read(&cardp->port[0].tx_data_urb_pending));
+ p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n",
+ atomic_read(&cardp->port[1].tx_data_urb_pending));
+ p += sprintf(p, "rx_cmd_urb_pending = %d\n",
+ atomic_read(&cardp->rx_cmd_urb_pending));
+ p += sprintf(p, "rx_data_urb_pending = %d\n",
+ atomic_read(&cardp->rx_data_urb_pending));
+ }
+
+ p += sprintf(p, "tx_pending = %d\n",
+ atomic_read(&adapter->tx_pending));
+ p += sprintf(p, "rx_pending = %d\n",
+ atomic_read(&adapter->rx_pending));
+
+ if (adapter->iface_type == MWIFIEX_SDIO) {
+ sdio_card = (struct sdio_mmc_card *)adapter->card;
+ p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
+ sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
+ p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
+ sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i] || !adapter->priv[i]->netdev)
+ continue;
+ priv = adapter->priv[i];
+ p += sprintf(p, "\n[interface : \"%s\"]\n",
+ priv->netdev->name);
+ p += sprintf(p, "wmm_tx_pending[0] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[0]));
+ p += sprintf(p, "wmm_tx_pending[1] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[1]));
+ p += sprintf(p, "wmm_tx_pending[2] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[2]));
+ p += sprintf(p, "wmm_tx_pending[3] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[3]));
+ p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
+ "Disconnected" : "Connected");
+ p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
+ ? "on" : "off"));
+ for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
+ txq = netdev_get_tx_queue(priv->netdev, idx);
+ p += sprintf(p, "tx queue %d:%s ", idx,
+ netif_tx_queue_stopped(txq) ?
+ "stopped" : "started");
+ }
+ p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
+ priv->netdev->name, priv->num_tx_timeout);
+ }
+
+ if (adapter->iface_type == MWIFIEX_SDIO) {
+ p += sprintf(p, "\n=== SDIO register dump===\n");
+ if (adapter->if_ops.reg_dump)
+ p += adapter->if_ops.reg_dump(adapter, p);
+ }
+
+ p += sprintf(p, "\n=== more debug information\n");
+ debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
+ if (debug_info) {
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i] || !adapter->priv[i]->netdev)
+ continue;
+ priv = adapter->priv[i];
+ mwifiex_get_debug_info(priv, debug_info);
+ p += mwifiex_debug_info_to_buffer(priv, p, debug_info);
+ break;
+ }
+ kfree(debug_info);
+ }
+
+ adapter->drv_info_size = p - adapter->drv_info_dump;
+ mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n");
+}
+EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump);
+
+void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter)
+{
+ u8 idx, *dump_data, *fw_dump_ptr;
+ u32 dump_len;
+
+ dump_len = (strlen("========Start dump driverinfo========\n") +
+ adapter->drv_info_size +
+ strlen("\n========End dump========\n"));
+
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ dump_len += (strlen("========Start dump ") +
+ strlen(entry->mem_name) +
+ strlen("========\n") +
+ (entry->mem_size + 1) +
+ strlen("\n========End dump========\n"));
+ }
+ }
+
+ dump_data = vzalloc(dump_len + 1);
+ if (!dump_data)
+ goto done;
+
+ fw_dump_ptr = dump_data;
+
+ /* Dump all the memory data into single file, a userspace script will
+ * be used to split all the memory data to multiple files
+ */
+ mwifiex_dbg(adapter, MSG,
+ "== mwifiex dump information to /sys/class/devcoredump start");
+
+ strcpy(fw_dump_ptr, "========Start dump driverinfo========\n");
+ fw_dump_ptr += strlen("========Start dump driverinfo========\n");
+ memcpy(fw_dump_ptr, adapter->drv_info_dump, adapter->drv_info_size);
+ fw_dump_ptr += adapter->drv_info_size;
+ strcpy(fw_dump_ptr, "\n========End dump========\n");
+ fw_dump_ptr += strlen("\n========End dump========\n");
+
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ strcpy(fw_dump_ptr, "========Start dump ");
+ fw_dump_ptr += strlen("========Start dump ");
+
+ strcpy(fw_dump_ptr, entry->mem_name);
+ fw_dump_ptr += strlen(entry->mem_name);
+
+ strcpy(fw_dump_ptr, "========\n");
+ fw_dump_ptr += strlen("========\n");
+
+ memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
+ fw_dump_ptr += entry->mem_size;
+
+ strcpy(fw_dump_ptr, "\n========End dump========\n");
+ fw_dump_ptr += strlen("\n========End dump========\n");
+ }
+ }
+
+ /* device dump data will be free in device coredump release function
+ * after 5 min
+ */
+ dev_coredumpv(adapter->dev, dump_data, dump_len, GFP_KERNEL);
+ mwifiex_dbg(adapter, MSG,
+ "== mwifiex dump information to /sys/class/devcoredump end");
+
+done:
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ if (adapter->drv_info_dump) {
+ vfree(adapter->drv_info_dump);
+ adapter->drv_info_dump = NULL;
+ adapter->drv_info_size = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump);
+
+/*
+ * CFG802.11 network device handler for statistics retrieval.
+ */
+static struct net_device_stats *mwifiex_get_stats(struct net_device *dev)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ return &priv->stats;
+}
+
+static u16
+mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb,
+ void *accel_priv, select_queue_fallback_t fallback)
+{
+ skb->priority = cfg80211_classify8021d(skb, NULL);
+ return mwifiex_1d_to_wmm_queue[skb->priority];
+}
+
+/* Network device handlers */
+static const struct net_device_ops mwifiex_netdev_ops = {
+ .ndo_open = mwifiex_open,
+ .ndo_stop = mwifiex_close,
+ .ndo_start_xmit = mwifiex_hard_start_xmit,
+ .ndo_set_mac_address = mwifiex_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = mwifiex_tx_timeout,
+ .ndo_get_stats = mwifiex_get_stats,
+ .ndo_set_rx_mode = mwifiex_set_multicast_list,
+ .ndo_select_queue = mwifiex_netdev_select_wmm_queue,
+};
+
+/*
+ * This function initializes the private structure parameters.
+ *
+ * The following wait queues are initialized -
+ * - IOCTL wait queue
+ * - Command wait queue
+ * - Statistics wait queue
+ *
+ * ...and the following default parameters are set -
+ * - Current key index : Set to 0
+ * - Rate index : Set to auto
+ * - Media connected : Set to disconnected
+ * - Adhoc link sensed : Set to false
+ * - Nick name : Set to null
+ * - Number of Tx timeout : Set to 0
+ * - Device address : Set to current address
+ * - Rx histogram statistc : Set to 0
+ *
+ * In addition, the CFG80211 work queue is also created.
+ */
+void mwifiex_init_priv_params(struct mwifiex_private *priv,
+ struct net_device *dev)
+{
+ dev->netdev_ops = &mwifiex_netdev_ops;
+ dev->destructor = free_netdev;
+ /* Initialize private structure */
+ priv->current_key_index = 0;
+ priv->media_connected = false;
+ memset(priv->mgmt_ie, 0,
+ sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX);
+ priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK;
+ priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK;
+ priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
+ priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
+ priv->num_tx_timeout = 0;
+ ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
+ memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+ GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+ priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
+ if (priv->hist_data)
+ mwifiex_hist_data_reset(priv);
+ }
+}
+
+/*
+ * This function check if command is pending.
+ */
+int is_command_pending(struct mwifiex_adapter *adapter)
+{
+ unsigned long flags;
+ int is_cmd_pend_q_empty;
+
+ spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+ is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
+ spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+
+ return !is_cmd_pend_q_empty;
+}
+
+/*
+ * This is the RX work queue function.
+ *
+ * It handles the RX operations.
+ */
+static void mwifiex_rx_work_queue(struct work_struct *work)
+{
+ struct mwifiex_adapter *adapter =
+ container_of(work, struct mwifiex_adapter, rx_work);
+
+ if (adapter->surprise_removed)
+ return;
+ mwifiex_process_rx(adapter);
+}
+
+/*
+ * This is the main work queue function.
+ *
+ * It handles the main process, which in turn handles the complete
+ * driver operations.
+ */
+static void mwifiex_main_work_queue(struct work_struct *work)
+{
+ struct mwifiex_adapter *adapter =
+ container_of(work, struct mwifiex_adapter, main_work);
+
+ if (adapter->surprise_removed)
+ return;
+ mwifiex_main_process(adapter);
+}
+
+/*
+ * This function adds the card.
+ *
+ * This function follows the following major steps to set up the device -
+ * - Initialize software. This includes probing the card, registering
+ * the interface operations table, and allocating/initializing the
+ * adapter structure
+ * - Set up the netlink socket
+ * - Create and start the main work queue
+ * - Register the device
+ * - Initialize firmware and hardware
+ * - Add logical interfaces
+ */
+int
+mwifiex_add_card(void *card, struct semaphore *sem,
+ struct mwifiex_if_ops *if_ops, u8 iface_type)
+{
+ struct mwifiex_adapter *adapter;
+
+ if (down_interruptible(sem))
+ goto exit_sem_err;
+
+ if (mwifiex_register(card, if_ops, (void **)&adapter)) {
+ pr_err("%s: software init failed\n", __func__);
+ goto err_init_sw;
+ }
+
+ adapter->iface_type = iface_type;
+ adapter->card_sem = sem;
+
+ adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
+ adapter->surprise_removed = false;
+ init_waitqueue_head(&adapter->init_wait_q);
+ adapter->is_suspended = false;
+ adapter->hs_activated = false;
+ init_waitqueue_head(&adapter->hs_activate_wait_q);
+ init_waitqueue_head(&adapter->cmd_wait_q.wait);
+ adapter->cmd_wait_q.status = 0;
+ adapter->scan_wait_q_woken = false;
+
+ if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) {
+ adapter->rx_work_enabled = true;
+ pr_notice("rx work enabled, cpus %d\n", num_possible_cpus());
+ }
+
+ adapter->workqueue =
+ alloc_workqueue("MWIFIEX_WORK_QUEUE",
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
+ if (!adapter->workqueue)
+ goto err_kmalloc;
+
+ INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
+
+ if (adapter->rx_work_enabled) {
+ adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM |
+ WQ_UNBOUND, 1);
+ if (!adapter->rx_workqueue)
+ goto err_kmalloc;
+
+ INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
+ }
+
+ /* Register the device. Fill up the private data structure with relevant
+ information from the card. */
+ if (adapter->if_ops.register_dev(adapter)) {
+ pr_err("%s: failed to register mwifiex device\n", __func__);
+ goto err_registerdev;
+ }
+
+ if (mwifiex_init_hw_fw(adapter)) {
+ pr_err("%s: firmware init failed\n", __func__);
+ goto err_init_fw;
+ }
+
+ return 0;
+
+err_init_fw:
+ pr_debug("info: %s: unregister device\n", __func__);
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
+ pr_debug("info: %s: shutdown mwifiex\n", __func__);
+ adapter->init_wait_q_woken = false;
+
+ if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
+ wait_event_interruptible(adapter->init_wait_q,
+ adapter->init_wait_q_woken);
+ }
+err_registerdev:
+ adapter->surprise_removed = true;
+ mwifiex_terminate_workqueue(adapter);
+err_kmalloc:
+ mwifiex_free_adapter(adapter);
+
+err_init_sw:
+ up(sem);
+
+exit_sem_err:
+ return -1;
+}
+EXPORT_SYMBOL_GPL(mwifiex_add_card);
+
+/*
+ * This function removes the card.
+ *
+ * This function follows the following major steps to remove the device -
+ * - Stop data traffic
+ * - Shutdown firmware
+ * - Remove the logical interfaces
+ * - Terminate the work queue
+ * - Unregister the device
+ * - Free the adapter structure
+ */
+int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
+{
+ struct mwifiex_private *priv = NULL;
+ int i;
+
+ if (down_interruptible(sem))
+ goto exit_sem_err;
+
+ if (!adapter)
+ goto exit_remove;
+
+ /* We can no longer handle interrupts once we start doing the teardown
+ * below. */
+ if (adapter->if_ops.disable_int)
+ adapter->if_ops.disable_int(adapter);
+
+ adapter->surprise_removed = true;
+
+ mwifiex_terminate_workqueue(adapter);
+
+ /* Stop data */
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv && priv->netdev) {
+ mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+ }
+ }
+
+ mwifiex_dbg(adapter, CMD,
+ "cmd: calling mwifiex_shutdown_drv...\n");
+ adapter->init_wait_q_woken = false;
+
+ if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
+ wait_event_interruptible(adapter->init_wait_q,
+ adapter->init_wait_q_woken);
+ mwifiex_dbg(adapter, CMD,
+ "cmd: mwifiex_shutdown_drv done\n");
+ if (atomic_read(&adapter->rx_pending) ||
+ atomic_read(&adapter->tx_pending) ||
+ atomic_read(&adapter->cmd_pending)) {
+ mwifiex_dbg(adapter, ERROR,
+ "rx_pending=%d, tx_pending=%d,\t"
+ "cmd_pending=%d\n",
+ atomic_read(&adapter->rx_pending),
+ atomic_read(&adapter->tx_pending),
+ atomic_read(&adapter->cmd_pending));
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+
+ if (!priv)
+ continue;
+
+ rtnl_lock();
+ if (priv->netdev &&
+ priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED)
+ mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
+ rtnl_unlock();
+ }
+
+ wiphy_unregister(adapter->wiphy);
+ wiphy_free(adapter->wiphy);
+
+ /* Unregister device */
+ mwifiex_dbg(adapter, INFO,
+ "info: unregister device\n");
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+ /* Free adapter structure */
+ mwifiex_dbg(adapter, INFO,
+ "info: free adapter\n");
+ mwifiex_free_adapter(adapter);
+
+exit_remove:
+ up(sem);
+exit_sem_err:
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mwifiex_remove_card);
+
+void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (!adapter->dev || !(adapter->debug_mask & mask))
+ return;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ dev_info(adapter->dev, "%pV", &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(_mwifiex_dbg);
+
+/*
+ * This function initializes the module.
+ *
+ * The debug FS is also initialized if configured.
+ */
+static int
+mwifiex_init_module(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ mwifiex_debugfs_init();
+#endif
+ return 0;
+}
+
+/*
+ * This function cleans up the module.
+ *
+ * The debug FS is removed if available.
+ */
+static void
+mwifiex_cleanup_module(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ mwifiex_debugfs_remove();
+#endif
+}
+
+module_init(mwifiex_init_module);
+module_exit(mwifiex_cleanup_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h
new file mode 100644
index 000000000000..2f7f478ce04b
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
@@ -0,0 +1,1605 @@
+/*
+ * Marvell Wireless LAN device driver: major data structures and prototypes
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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.
+ */
+
+#ifndef _MWIFIEX_MAIN_H_
+#define _MWIFIEX_MAIN_H_
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+#include <net/lib80211.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/ctype.h>
+#include <linux/of.h>
+#include <linux/idr.h>
+#include <linux/inetdevice.h>
+#include <linux/devcoredump.h>
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "pcie.h"
+#include "usb.h"
+#include "sdio.h"
+
+extern const char driver_version[];
+
+struct mwifiex_adapter;
+struct mwifiex_private;
+
+enum {
+ MWIFIEX_ASYNC_CMD,
+ MWIFIEX_SYNC_CMD
+};
+
+#define MWIFIEX_DRIVER_MODE_STA BIT(0)
+#define MWIFIEX_DRIVER_MODE_UAP BIT(1)
+#define MWIFIEX_DRIVER_MODE_P2P BIT(2)
+#define MWIFIEX_DRIVER_MODE_BITMASK (BIT(0) | BIT(1) | BIT(2))
+
+#define MWIFIEX_MAX_AP 64
+
+#define MWIFIEX_MAX_PKTS_TXQ 16
+
+#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ)
+
+#define MWIFIEX_TIMER_10S 10000
+#define MWIFIEX_TIMER_1S 1000
+
+#define MAX_TX_PENDING 100
+#define LOW_TX_PENDING 80
+
+#define HIGH_RX_PENDING 50
+#define LOW_RX_PENDING 20
+
+#define MWIFIEX_UPLD_SIZE (2312)
+
+#define MAX_EVENT_SIZE 2048
+
+#define ARP_FILTER_MAX_BUF_SIZE 68
+
+#define MWIFIEX_KEY_BUFFER_SIZE 16
+#define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10
+#define MWIFIEX_MAX_REGION_CODE 9
+
+#define DEFAULT_BCN_AVG_FACTOR 8
+#define DEFAULT_DATA_AVG_FACTOR 8
+
+#define FIRST_VALID_CHANNEL 0xff
+#define DEFAULT_AD_HOC_CHANNEL 6
+#define DEFAULT_AD_HOC_CHANNEL_A 36
+
+#define DEFAULT_BCN_MISS_TIMEOUT 5
+
+#define MAX_SCAN_BEACON_BUFFER 8000
+
+#define SCAN_BEACON_ENTRY_PAD 6
+
+#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110
+#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30
+#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30
+#define MWIFIEX_DEF_SCAN_CHAN_GAP_TIME 50
+
+#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI)))
+
+#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S)
+
+#define RSN_GTK_OUI_OFFSET 2
+
+#define MWIFIEX_OUI_NOT_PRESENT 0
+#define MWIFIEX_OUI_PRESENT 1
+
+#define PKT_TYPE_MGMT 0xE5
+
+/*
+ * Do not check for data_received for USB, as data_received
+ * is handled in mwifiex_usb_recv for USB
+ */
+#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \
+ adapter->event_received || \
+ adapter->data_received)
+
+#define MWIFIEX_TYPE_CMD 1
+#define MWIFIEX_TYPE_DATA 0
+#define MWIFIEX_TYPE_AGGR_DATA 10
+#define MWIFIEX_TYPE_EVENT 3
+
+#define MAX_BITMAP_RATES_SIZE 18
+
+#define MAX_CHANNEL_BAND_BG 14
+#define MAX_CHANNEL_BAND_A 165
+
+#define MAX_FREQUENCY_BAND_BG 2484
+
+#define MWIFIEX_EVENT_HEADER_LEN 4
+#define MWIFIEX_UAP_EVENT_EXTRA_HEADER 2
+
+#define MWIFIEX_TYPE_LEN 4
+#define MWIFIEX_USB_TYPE_CMD 0xF00DFACE
+#define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE
+#define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE
+
+/* Threshold for tx_timeout_cnt before we trigger a card reset */
+#define TX_TIMEOUT_THRESHOLD 6
+
+#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000
+
+/* Address alignment */
+#define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1))
+
+/**
+ *enum mwifiex_debug_level - marvell wifi debug level
+ */
+enum MWIFIEX_DEBUG_LEVEL {
+ MWIFIEX_DBG_MSG = 0x00000001,
+ MWIFIEX_DBG_FATAL = 0x00000002,
+ MWIFIEX_DBG_ERROR = 0x00000004,
+ MWIFIEX_DBG_DATA = 0x00000008,
+ MWIFIEX_DBG_CMD = 0x00000010,
+ MWIFIEX_DBG_EVENT = 0x00000020,
+ MWIFIEX_DBG_INTR = 0x00000040,
+ MWIFIEX_DBG_IOCTL = 0x00000080,
+
+ MWIFIEX_DBG_MPA_D = 0x00008000,
+ MWIFIEX_DBG_DAT_D = 0x00010000,
+ MWIFIEX_DBG_CMD_D = 0x00020000,
+ MWIFIEX_DBG_EVT_D = 0x00040000,
+ MWIFIEX_DBG_FW_D = 0x00080000,
+ MWIFIEX_DBG_IF_D = 0x00100000,
+
+ MWIFIEX_DBG_ENTRY = 0x10000000,
+ MWIFIEX_DBG_WARN = 0x20000000,
+ MWIFIEX_DBG_INFO = 0x40000000,
+ MWIFIEX_DBG_DUMP = 0x80000000,
+
+ MWIFIEX_DBG_ANY = 0xffffffff
+};
+
+#define MWIFIEX_DEFAULT_DEBUG_MASK (MWIFIEX_DBG_MSG | \
+ MWIFIEX_DBG_FATAL | \
+ MWIFIEX_DBG_ERROR)
+
+__printf(3, 4)
+void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
+ const char *fmt, ...);
+#define mwifiex_dbg(adapter, mask, fmt, ...) \
+ _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__)
+
+#define DEBUG_DUMP_DATA_MAX_LEN 128
+#define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \
+do { \
+ if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \
+ print_hex_dump(KERN_DEBUG, str, \
+ DUMP_PREFIX_OFFSET, 16, 1, \
+ buf, len, false); \
+} while (0)
+
+struct mwifiex_dbg {
+ u32 num_cmd_host_to_card_failure;
+ u32 num_cmd_sleep_cfm_host_to_card_failure;
+ u32 num_tx_host_to_card_failure;
+ u32 num_event_deauth;
+ u32 num_event_disassoc;
+ u32 num_event_link_lost;
+ u32 num_cmd_deauth;
+ u32 num_cmd_assoc_success;
+ u32 num_cmd_assoc_failure;
+ u32 num_tx_timeout;
+ u16 timeout_cmd_id;
+ u16 timeout_cmd_act;
+ u16 last_cmd_id[DBG_CMD_NUM];
+ u16 last_cmd_act[DBG_CMD_NUM];
+ u16 last_cmd_index;
+ u16 last_cmd_resp_id[DBG_CMD_NUM];
+ u16 last_cmd_resp_index;
+ u16 last_event[DBG_CMD_NUM];
+ u16 last_event_index;
+};
+
+enum MWIFIEX_HARDWARE_STATUS {
+ MWIFIEX_HW_STATUS_READY,
+ MWIFIEX_HW_STATUS_INITIALIZING,
+ MWIFIEX_HW_STATUS_INIT_DONE,
+ MWIFIEX_HW_STATUS_RESET,
+ MWIFIEX_HW_STATUS_CLOSING,
+ MWIFIEX_HW_STATUS_NOT_READY
+};
+
+enum MWIFIEX_802_11_POWER_MODE {
+ MWIFIEX_802_11_POWER_MODE_CAM,
+ MWIFIEX_802_11_POWER_MODE_PSP
+};
+
+struct mwifiex_tx_param {
+ u32 next_pkt_len;
+};
+
+enum MWIFIEX_PS_STATE {
+ PS_STATE_AWAKE,
+ PS_STATE_PRE_SLEEP,
+ PS_STATE_SLEEP_CFM,
+ PS_STATE_SLEEP
+};
+
+enum mwifiex_iface_type {
+ MWIFIEX_SDIO,
+ MWIFIEX_PCIE,
+ MWIFIEX_USB
+};
+
+struct mwifiex_add_ba_param {
+ u32 tx_win_size;
+ u32 rx_win_size;
+ u32 timeout;
+ u8 tx_amsdu;
+ u8 rx_amsdu;
+};
+
+struct mwifiex_tx_aggr {
+ u8 ampdu_user;
+ u8 ampdu_ap;
+ u8 amsdu;
+};
+
+enum mwifiex_ba_status {
+ BA_SETUP_NONE = 0,
+ BA_SETUP_INPROGRESS,
+ BA_SETUP_COMPLETE
+};
+
+struct mwifiex_ra_list_tbl {
+ struct list_head list;
+ struct sk_buff_head skb_head;
+ u8 ra[ETH_ALEN];
+ u32 is_11n_enabled;
+ u16 max_amsdu;
+ u16 ba_pkt_count;
+ u8 ba_packet_thr;
+ enum mwifiex_ba_status ba_status;
+ u8 amsdu_in_ampdu;
+ u16 total_pkt_count;
+ bool tdls_link;
+ bool tx_paused;
+};
+
+struct mwifiex_tid_tbl {
+ struct list_head ra_list;
+};
+
+#define WMM_HIGHEST_PRIORITY 7
+#define HIGH_PRIO_TID 7
+#define LOW_PRIO_TID 0
+
+struct mwifiex_wmm_desc {
+ struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID];
+ u32 packets_out[MAX_NUM_TID];
+ u32 pkts_paused[MAX_NUM_TID];
+ /* spin lock to protect ra_list */
+ spinlock_t ra_list_spinlock;
+ struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS];
+ enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS];
+ u32 drv_pkt_delay_max;
+ u8 queue_priority[IEEE80211_NUM_ACS];
+ u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */
+ /* Number of transmit packets queued */
+ atomic_t tx_pkts_queued;
+ /* Tracks highest priority with a packet queued */
+ atomic_t highest_queued_prio;
+};
+
+struct mwifiex_802_11_security {
+ u8 wpa_enabled;
+ u8 wpa2_enabled;
+ u8 wapi_enabled;
+ u8 wapi_key_on;
+ u8 wep_enabled;
+ u32 authentication_mode;
+ u8 is_authtype_auto;
+ u32 encryption_mode;
+};
+
+struct ieee_types_header {
+ u8 element_id;
+ u8 len;
+} __packed;
+
+struct ieee_types_vendor_specific {
+ struct ieee_types_vendor_header vend_hdr;
+ u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)];
+} __packed;
+
+struct ieee_types_generic {
+ struct ieee_types_header ieee_hdr;
+ u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)];
+} __packed;
+
+struct ieee_types_bss_co_2040 {
+ struct ieee_types_header ieee_hdr;
+ u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+ struct ieee_types_header ieee_hdr;
+ u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+ struct ieee_types_header ieee_hdr;
+ u16 aid;
+} __packed;
+
+struct mwifiex_bssdescriptor {
+ u8 mac_address[ETH_ALEN];
+ struct cfg80211_ssid ssid;
+ u32 privacy;
+ s32 rssi;
+ u32 channel;
+ u32 freq;
+ u16 beacon_period;
+ u8 erp_flags;
+ u32 bss_mode;
+ u8 supported_rates[MWIFIEX_SUPPORTED_RATES];
+ u8 data_rates[MWIFIEX_SUPPORTED_RATES];
+ /* Network band.
+ * BAND_B(0x01): 'b' band
+ * BAND_G(0x02): 'g' band
+ * BAND_A(0X04): 'a' band
+ */
+ u16 bss_band;
+ u64 fw_tsf;
+ u64 timestamp;
+ union ieee_types_phy_param_set phy_param_set;
+ union ieee_types_ss_param_set ss_param_set;
+ u16 cap_info_bitmap;
+ struct ieee_types_wmm_parameter wmm_ie;
+ u8 disable_11n;
+ struct ieee80211_ht_cap *bcn_ht_cap;
+ u16 ht_cap_offset;
+ struct ieee80211_ht_operation *bcn_ht_oper;
+ u16 ht_info_offset;
+ u8 *bcn_bss_co_2040;
+ u16 bss_co_2040_offset;
+ u8 *bcn_ext_cap;
+ u16 ext_cap_offset;
+ struct ieee80211_vht_cap *bcn_vht_cap;
+ u16 vht_cap_offset;
+ struct ieee80211_vht_operation *bcn_vht_oper;
+ u16 vht_info_offset;
+ struct ieee_types_oper_mode_ntf *oper_mode;
+ u16 oper_mode_offset;
+ u8 disable_11ac;
+ struct ieee_types_vendor_specific *bcn_wpa_ie;
+ u16 wpa_offset;
+ struct ieee_types_generic *bcn_rsn_ie;
+ u16 rsn_offset;
+ struct ieee_types_generic *bcn_wapi_ie;
+ 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 {
+ struct mwifiex_bssdescriptor bss_descriptor;
+ u8 wmm_enabled;
+ u8 wmm_uapsd_enabled;
+ u8 band;
+ u32 num_of_rates;
+ u8 data_rates[MWIFIEX_SUPPORTED_RATES];
+};
+
+struct mwifiex_sleep_params {
+ u16 sp_error;
+ u16 sp_offset;
+ u16 sp_stable_time;
+ u8 sp_cal_control;
+ u8 sp_ext_sleep_clk;
+ u16 sp_reserved;
+};
+
+struct mwifiex_sleep_period {
+ u16 period;
+ u16 reserved;
+};
+
+struct mwifiex_wep_key {
+ u32 length;
+ u32 key_index;
+ u32 key_length;
+ u8 key_material[MWIFIEX_KEY_BUFFER_SIZE];
+};
+
+#define MAX_REGION_CHANNEL_NUM 2
+
+struct mwifiex_chan_freq_power {
+ u16 channel;
+ u32 freq;
+ u16 max_tx_power;
+ u8 unsupported;
+};
+
+enum state_11d_t {
+ DISABLE_11D = 0,
+ ENABLE_11D = 1,
+};
+
+#define MWIFIEX_MAX_TRIPLET_802_11D 83
+
+struct mwifiex_802_11d_domain_reg {
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ u8 no_of_triplet;
+ struct ieee80211_country_ie_triplet
+ triplet[MWIFIEX_MAX_TRIPLET_802_11D];
+};
+
+struct mwifiex_vendor_spec_cfg_ie {
+ u16 mask;
+ u16 flag;
+ u8 ie[MWIFIEX_MAX_VSIE_LEN];
+};
+
+struct wps {
+ u8 session_enable;
+};
+
+struct mwifiex_roc_cfg {
+ u64 cookie;
+ struct ieee80211_channel chan;
+};
+
+#define MWIFIEX_FW_DUMP_IDX 0xff
+#define MWIFIEX_DRV_INFO_IDX 20
+#define FW_DUMP_MAX_NAME_LEN 8
+#define FW_DUMP_HOST_READY 0xEE
+#define FW_DUMP_DONE 0xFF
+#define FW_DUMP_READ_DONE 0xFE
+
+struct memory_type_mapping {
+ u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+ u8 *mem_ptr;
+ u32 mem_size;
+ u8 done_flag;
+};
+
+enum rdwr_status {
+ RDWR_STATUS_SUCCESS = 0,
+ RDWR_STATUS_FAILURE = 1,
+ RDWR_STATUS_DONE = 2
+};
+
+enum mwifiex_iface_work_flags {
+ MWIFIEX_IFACE_WORK_DEVICE_DUMP,
+ MWIFIEX_IFACE_WORK_CARD_RESET,
+};
+
+struct mwifiex_private {
+ struct mwifiex_adapter *adapter;
+ u8 bss_type;
+ u8 bss_role;
+ u8 bss_priority;
+ u8 bss_num;
+ u8 bss_started;
+ u8 frame_type;
+ u8 curr_addr[ETH_ALEN];
+ u8 media_connected;
+ u8 port_open;
+ u8 usb_port;
+ u32 num_tx_timeout;
+ /* track consecutive timeout */
+ u8 tx_timeout_cnt;
+ struct net_device *netdev;
+ struct net_device_stats stats;
+ u16 curr_pkt_filter;
+ u32 bss_mode;
+ u32 pkt_tx_ctrl;
+ u16 tx_power_level;
+ u8 max_tx_power_level;
+ u8 min_tx_power_level;
+ u8 tx_rate;
+ u8 tx_htinfo;
+ u8 rxpd_htinfo;
+ u8 rxpd_rate;
+ u16 rate_bitmap;
+ u16 bitmap_rates[MAX_BITMAP_RATES_SIZE];
+ u32 data_rate;
+ u8 is_data_rate_auto;
+ u16 bcn_avg_factor;
+ u16 data_avg_factor;
+ s16 data_rssi_last;
+ s16 data_nf_last;
+ s16 data_rssi_avg;
+ s16 data_nf_avg;
+ s16 bcn_rssi_last;
+ s16 bcn_nf_last;
+ s16 bcn_rssi_avg;
+ s16 bcn_nf_avg;
+ struct mwifiex_bssdescriptor *attempted_bss_desc;
+ struct cfg80211_ssid prev_ssid;
+ u8 prev_bssid[ETH_ALEN];
+ struct mwifiex_current_bss_params curr_bss_params;
+ u16 beacon_period;
+ u8 dtim_period;
+ u16 listen_interval;
+ u16 atim_window;
+ u8 adhoc_channel;
+ u8 adhoc_is_link_sensed;
+ u8 adhoc_state;
+ struct mwifiex_802_11_security sec_info;
+ struct mwifiex_wep_key wep_key[NUM_WEP_KEYS];
+ u16 wep_key_curr_index;
+ u8 wpa_ie[256];
+ u16 wpa_ie_len;
+ u8 wpa_is_gtk_set;
+ struct host_cmd_ds_802_11_key_material aes_key;
+ struct host_cmd_ds_802_11_key_material_v2 aes_key_v2;
+ u8 wapi_ie[256];
+ u16 wapi_ie_len;
+ u8 *wps_ie;
+ u16 wps_ie_len;
+ u8 wmm_required;
+ u8 wmm_enabled;
+ u8 wmm_qosinfo;
+ struct mwifiex_wmm_desc wmm;
+ atomic_t wmm_tx_pending[IEEE80211_NUM_ACS];
+ struct list_head sta_list;
+ /* spin lock for associated station/TDLS peers list */
+ spinlock_t sta_list_spinlock;
+ struct list_head auto_tdls_list;
+ /* spin lock for auto TDLS peer list */
+ spinlock_t auto_tdls_lock;
+ struct list_head tx_ba_stream_tbl_ptr;
+ /* spin lock for tx_ba_stream_tbl_ptr queue */
+ spinlock_t tx_ba_stream_tbl_lock;
+ struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID];
+ struct mwifiex_add_ba_param add_ba_param;
+ u16 rx_seq[MAX_NUM_TID];
+ u8 tos_to_tid_inv[MAX_NUM_TID];
+ struct list_head rx_reorder_tbl_ptr;
+ /* spin lock for rx_reorder_tbl_ptr queue */
+ spinlock_t rx_reorder_tbl_lock;
+ /* spin lock for Rx packets */
+ spinlock_t rx_pkt_lock;
+
+#define MWIFIEX_ASSOC_RSP_BUF_SIZE 500
+ u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE];
+ u32 assoc_rsp_size;
+
+#define MWIFIEX_GENIE_BUF_SIZE 256
+ u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE];
+ u8 gen_ie_buf_len;
+
+ struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM];
+
+#define MWIFIEX_ASSOC_TLV_BUF_SIZE 256
+ u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE];
+ u8 assoc_tlv_buf_len;
+
+ u8 *curr_bcn_buf;
+ u32 curr_bcn_size;
+ /* spin lock for beacon buffer */
+ spinlock_t curr_bcn_buf_lock;
+ struct wireless_dev wdev;
+ struct mwifiex_chan_freq_power cfp;
+ char version_str[128];
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dfs_dev_dir;
+#endif
+ u16 current_key_index;
+ struct semaphore async_sem;
+ struct cfg80211_scan_request *scan_request;
+ u8 cfg_bssid[6];
+ struct wps wps;
+ u8 scan_block;
+ s32 cqm_rssi_thold;
+ u32 cqm_rssi_hyst;
+ u8 subsc_evt_rssi_state;
+ struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage;
+ struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX];
+ u16 beacon_idx;
+ u16 proberesp_idx;
+ u16 assocresp_idx;
+ u16 gen_idx;
+ u8 ap_11n_enabled;
+ u8 ap_11ac_enabled;
+ u32 mgmt_frame_mask;
+ struct mwifiex_roc_cfg roc_cfg;
+ bool scan_aborting;
+ u8 csa_chan;
+ unsigned long csa_expire_time;
+ u8 del_list_idx;
+ bool hs2_enabled;
+ struct mwifiex_uap_bss_param bss_cfg;
+ struct cfg80211_chan_def bss_chandef;
+ struct station_parameters *sta_params;
+ struct sk_buff_head tdls_txq;
+ u8 check_tdls_tx;
+ struct timer_list auto_tdls_timer;
+ bool auto_tdls_timer_active;
+ struct idr ack_status_frames;
+ /* spin lock for ack status */
+ spinlock_t ack_status_lock;
+ /** rx histogram data */
+ struct mwifiex_histogram_data *hist_data;
+ struct cfg80211_chan_def dfs_chandef;
+ struct workqueue_struct *dfs_cac_workqueue;
+ struct delayed_work dfs_cac_work;
+ struct timer_list dfs_chan_switch_timer;
+ struct workqueue_struct *dfs_chan_sw_workqueue;
+ struct delayed_work dfs_chan_sw_work;
+ struct cfg80211_beacon_data beacon_after;
+ struct mwifiex_11h_intf_state state_11h;
+ struct mwifiex_ds_mem_rw mem_rw;
+ struct sk_buff_head bypass_txq;
+ struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX];
+};
+
+
+struct mwifiex_tx_ba_stream_tbl {
+ struct list_head list;
+ int tid;
+ u8 ra[ETH_ALEN];
+ enum mwifiex_ba_status ba_status;
+ u8 amsdu;
+};
+
+struct mwifiex_rx_reorder_tbl;
+
+struct reorder_tmr_cnxt {
+ struct timer_list timer;
+ struct mwifiex_rx_reorder_tbl *ptr;
+ struct mwifiex_private *priv;
+ u8 timer_is_set;
+};
+
+struct mwifiex_rx_reorder_tbl {
+ struct list_head list;
+ int tid;
+ u8 ta[ETH_ALEN];
+ int init_win;
+ int start_win;
+ int win_size;
+ void **rx_reorder_ptr;
+ struct reorder_tmr_cnxt timer_context;
+ u8 amsdu;
+ u8 flags;
+};
+
+struct mwifiex_bss_prio_node {
+ struct list_head list;
+ struct mwifiex_private *priv;
+};
+
+struct mwifiex_bss_prio_tbl {
+ struct list_head bss_prio_head;
+ /* spin lock for bss priority */
+ spinlock_t bss_prio_lock;
+ struct mwifiex_bss_prio_node *bss_prio_cur;
+};
+
+struct cmd_ctrl_node {
+ struct list_head list;
+ struct mwifiex_private *priv;
+ u32 cmd_oid;
+ u32 cmd_flag;
+ struct sk_buff *cmd_skb;
+ struct sk_buff *resp_skb;
+ void *data_buf;
+ u32 wait_q_enabled;
+ struct sk_buff *skb;
+ u8 *condition;
+ u8 cmd_wait_q_woken;
+};
+
+struct mwifiex_bss_priv {
+ u8 band;
+ u64 fw_tsf;
+};
+
+struct mwifiex_tdls_capab {
+ __le16 capab;
+ u8 rates[32];
+ u8 rates_len;
+ u8 qos_info;
+ u8 coex_2040;
+ u16 aid;
+ struct ieee80211_ht_cap ht_capb;
+ struct ieee80211_ht_operation ht_oper;
+ struct ieee_types_extcap extcap;
+ struct ieee_types_generic rsn_ie;
+ struct ieee80211_vht_cap vhtcap;
+ struct ieee80211_vht_operation vhtoper;
+};
+
+struct mwifiex_station_stats {
+ u64 last_rx;
+ s8 rssi;
+ u64 rx_bytes;
+ u64 tx_bytes;
+ u32 rx_packets;
+ u32 tx_packets;
+ u32 tx_failed;
+ u8 last_tx_rate;
+ u8 last_tx_htinfo;
+};
+
+/* This is AP/TDLS specific structure which stores information
+ * about associated/peer STA
+ */
+struct mwifiex_sta_node {
+ struct list_head list;
+ u8 mac_addr[ETH_ALEN];
+ u8 is_wmm_enabled;
+ u8 is_11n_enabled;
+ u8 is_11ac_enabled;
+ u8 ampdu_sta[MAX_NUM_TID];
+ u16 rx_seq[MAX_NUM_TID];
+ u16 max_amsdu;
+ u8 tdls_status;
+ struct mwifiex_tdls_capab tdls_cap;
+ struct mwifiex_station_stats stats;
+ u8 tx_pause;
+};
+
+struct mwifiex_auto_tdls_peer {
+ struct list_head list;
+ u8 mac_addr[ETH_ALEN];
+ u8 tdls_status;
+ int rssi;
+ long rssi_jiffies;
+ u8 failure_count;
+ u8 do_discover;
+ u8 do_setup;
+};
+
+struct mwifiex_if_ops {
+ int (*init_if) (struct mwifiex_adapter *);
+ void (*cleanup_if) (struct mwifiex_adapter *);
+ int (*check_fw_status) (struct mwifiex_adapter *, u32);
+ int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
+ int (*register_dev) (struct mwifiex_adapter *);
+ void (*unregister_dev) (struct mwifiex_adapter *);
+ int (*enable_int) (struct mwifiex_adapter *);
+ void (*disable_int) (struct mwifiex_adapter *);
+ int (*process_int_status) (struct mwifiex_adapter *);
+ int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *,
+ struct mwifiex_tx_param *);
+ int (*wakeup) (struct mwifiex_adapter *);
+ int (*wakeup_complete) (struct mwifiex_adapter *);
+
+ /* Interface specific functions */
+ void (*update_mp_end_port) (struct mwifiex_adapter *, u16);
+ void (*cleanup_mpa_buf) (struct mwifiex_adapter *);
+ int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *);
+ int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
+ int (*init_fw_port) (struct mwifiex_adapter *);
+ int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
+ void (*card_reset) (struct mwifiex_adapter *);
+ int (*reg_dump)(struct mwifiex_adapter *, char *);
+ void (*device_dump)(struct mwifiex_adapter *);
+ int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
+ void (*iface_work)(struct work_struct *work);
+ void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
+ void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *);
+ void (*multi_port_resync)(struct mwifiex_adapter *);
+ bool (*is_port_ready)(struct mwifiex_private *);
+};
+
+struct mwifiex_adapter {
+ u8 iface_type;
+ unsigned int debug_mask;
+ struct mwifiex_iface_comb iface_limit;
+ struct mwifiex_iface_comb curr_iface_comb;
+ struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM];
+ u8 priv_num;
+ const struct firmware *firmware;
+ char fw_name[32];
+ int winner;
+ struct device *dev;
+ struct wiphy *wiphy;
+ u8 perm_addr[ETH_ALEN];
+ bool surprise_removed;
+ u32 fw_release_number;
+ u16 init_wait_q_woken;
+ wait_queue_head_t init_wait_q;
+ void *card;
+ struct mwifiex_if_ops if_ops;
+ atomic_t bypass_tx_pending;
+ atomic_t rx_pending;
+ atomic_t tx_pending;
+ atomic_t cmd_pending;
+ struct workqueue_struct *workqueue;
+ struct work_struct main_work;
+ struct workqueue_struct *rx_workqueue;
+ struct work_struct rx_work;
+ struct workqueue_struct *dfs_workqueue;
+ struct work_struct dfs_work;
+ bool rx_work_enabled;
+ bool rx_processing;
+ bool delay_main_work;
+ bool rx_locked;
+ bool main_locked;
+ struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM];
+ /* spin lock for init/shutdown */
+ spinlock_t mwifiex_lock;
+ /* spin lock for main process */
+ spinlock_t main_proc_lock;
+ u32 mwifiex_processing;
+ u8 more_task_flag;
+ u16 tx_buf_size;
+ u16 curr_tx_buf_size;
+ /* sdio single port rx aggregation capability */
+ bool host_disable_sdio_rx_aggr;
+ bool sdio_rx_aggr_enable;
+ u16 sdio_rx_block_size;
+ u32 ioport;
+ enum MWIFIEX_HARDWARE_STATUS hw_status;
+ u16 number_of_antenna;
+ u32 fw_cap_info;
+ /* spin lock for interrupt handling */
+ spinlock_t int_lock;
+ u8 int_status;
+ u32 event_cause;
+ struct sk_buff *event_skb;
+ u8 upld_buf[MWIFIEX_UPLD_SIZE];
+ u8 data_sent;
+ u8 cmd_sent;
+ u8 cmd_resp_received;
+ u8 event_received;
+ u8 data_received;
+ u16 seq_num;
+ struct cmd_ctrl_node *cmd_pool;
+ struct cmd_ctrl_node *curr_cmd;
+ /* spin lock for command */
+ spinlock_t mwifiex_cmd_lock;
+ u8 is_cmd_timedout;
+ u16 last_init_cmd;
+ struct timer_list cmd_timer;
+ struct list_head cmd_free_q;
+ /* spin lock for cmd_free_q */
+ spinlock_t cmd_free_q_lock;
+ struct list_head cmd_pending_q;
+ /* spin lock for cmd_pending_q */
+ spinlock_t cmd_pending_q_lock;
+ struct list_head scan_pending_q;
+ /* spin lock for scan_pending_q */
+ spinlock_t scan_pending_q_lock;
+ /* spin lock for RX processing routine */
+ spinlock_t rx_proc_lock;
+ struct sk_buff_head tx_data_q;
+ atomic_t tx_queued;
+ u32 scan_processing;
+ u16 region_code;
+ struct mwifiex_802_11d_domain_reg domain_reg;
+ u16 scan_probes;
+ u32 scan_mode;
+ u16 specific_scan_time;
+ u16 active_scan_time;
+ u16 passive_scan_time;
+ u16 scan_chan_gap_time;
+ u8 fw_bands;
+ u8 adhoc_start_band;
+ u8 config_bands;
+ struct mwifiex_chan_scan_param_set *scan_channels;
+ u8 tx_lock_flag;
+ struct mwifiex_sleep_params sleep_params;
+ struct mwifiex_sleep_period sleep_period;
+ u16 ps_mode;
+ u32 ps_state;
+ u8 need_to_wakeup;
+ u16 multiple_dtim;
+ u16 local_listen_interval;
+ u16 null_pkt_interval;
+ struct sk_buff *sleep_cfm;
+ u16 bcn_miss_time_out;
+ u16 adhoc_awake_period;
+ u8 is_deep_sleep;
+ u8 delay_null_pkt;
+ u16 delay_to_ps;
+ u16 enhanced_ps_mode;
+ u8 pm_wakeup_card_req;
+ u16 gen_null_pkt;
+ u16 pps_uapsd_mode;
+ u32 pm_wakeup_fw_try;
+ struct timer_list wakeup_timer;
+ u8 is_hs_configured;
+ struct mwifiex_hs_config_param hs_cfg;
+ u8 hs_activated;
+ u16 hs_activate_wait_q_woken;
+ wait_queue_head_t hs_activate_wait_q;
+ bool is_suspended;
+ bool hs_enabling;
+ u8 event_body[MAX_EVENT_SIZE];
+ u32 hw_dot_11n_dev_cap;
+ u8 hw_dev_mcs_support;
+ u8 user_dev_mcs_support;
+ u8 adhoc_11n_enabled;
+ u8 sec_chan_offset;
+ struct mwifiex_dbg dbg;
+ u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE];
+ u32 arp_filter_size;
+ struct mwifiex_wait_queue cmd_wait_q;
+ u8 scan_wait_q_woken;
+ spinlock_t queue_lock; /* lock for tx queues */
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ u16 max_mgmt_ie_index;
+ const struct firmware *cal_data;
+ struct device_node *dt_node;
+
+ /* 11AC */
+ u32 is_hw_11ac_capable;
+ u32 hw_dot_11ac_dev_cap;
+ u32 hw_dot_11ac_mcs_support;
+ u32 usr_dot_11ac_dev_cap_bg;
+ u32 usr_dot_11ac_dev_cap_a;
+ u32 usr_dot_11ac_mcs_support;
+
+ atomic_t pending_bridged_pkts;
+ struct semaphore *card_sem;
+ bool ext_scan;
+ u8 fw_api_ver;
+ u8 key_api_major_ver, key_api_minor_ver;
+ struct memory_type_mapping *mem_type_mapping_tbl;
+ u8 num_mem_types;
+ void *drv_info_dump;
+ u32 drv_info_size;
+ bool scan_chan_gap_enabled;
+ struct sk_buff_head rx_data_q;
+ struct mwifiex_chan_stats *chan_stats;
+ u32 num_in_chan_stats;
+ int survey_idx;
+ bool auto_tdls;
+ u8 coex_scan;
+ u8 coex_min_scan_time;
+ u8 coex_max_scan_time;
+ u8 coex_win_size;
+ u8 coex_tx_win_size;
+ u8 coex_rx_win_size;
+ bool drcs_enabled;
+ u8 active_scan_triggered;
+ bool usb_mc_status;
+ bool usb_mc_setup;
+};
+
+void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);
+
+int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
+
+void mwifiex_set_trans_start(struct net_device *dev);
+
+void mwifiex_stop_net_dev_queue(struct net_device *netdev,
+ struct mwifiex_adapter *adapter);
+
+void mwifiex_wake_up_net_dev_queue(struct net_device *netdev,
+ struct mwifiex_adapter *adapter);
+
+int mwifiex_init_priv(struct mwifiex_private *priv);
+void mwifiex_free_priv(struct mwifiex_private *priv);
+
+int mwifiex_init_fw(struct mwifiex_adapter *adapter);
+
+int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter);
+
+int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter);
+
+int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter);
+
+int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *);
+
+int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb);
+
+int mwifiex_process_mgmt_packet(struct mwifiex_private *priv,
+ struct sk_buff *skb);
+
+int mwifiex_process_event(struct mwifiex_adapter *adapter);
+
+int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
+
+int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,
+ u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync);
+
+void mwifiex_cmd_timeout_func(unsigned long function_context);
+
+int mwifiex_get_debug_info(struct mwifiex_private *,
+ struct mwifiex_debug_info *);
+
+int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter);
+int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter);
+void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter);
+void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter);
+
+void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
+void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
+
+void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node,
+ u32 addtail);
+
+int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter);
+int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter);
+int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb);
+int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
+ struct mwifiex_tx_param *tx_param);
+int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags);
+int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb, int aggr, int status);
+void mwifiex_clean_txrx(struct mwifiex_private *priv);
+u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv);
+void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter);
+void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *,
+ u32);
+int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, uint16_t ps_bitmap,
+ struct mwifiex_ds_auto_ds *auto_ds);
+int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp,
+ struct mwifiex_ds_pm_cfg *pm_cfg);
+void mwifiex_process_hs_config(struct mwifiex_adapter *adapter);
+void mwifiex_hs_activated_event(struct mwifiex_private *priv,
+ u8 activated);
+int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
+ int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg);
+int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp);
+int mwifiex_process_rx_packet(struct mwifiex_private *priv,
+ struct sk_buff *skb);
+int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no,
+ u16 cmd_action, u32 cmd_oid,
+ void *data_buf, void *cmd_buf);
+int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
+ u16 cmd_action, u32 cmd_oid,
+ void *data_buf, void *cmd_buf);
+int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no,
+ struct host_cmd_ds_command *resp);
+int mwifiex_process_sta_rx_packet(struct mwifiex_private *,
+ struct sk_buff *skb);
+int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
+ struct sk_buff *skb);
+int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
+ struct sk_buff *skb);
+int mwifiex_process_sta_event(struct mwifiex_private *);
+int mwifiex_process_uap_event(struct mwifiex_private *);
+void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
+void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv,
+ const u8 *ra_addr);
+void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
+void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
+int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init);
+int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
+ struct mwifiex_scan_cmd_config *scan_cfg);
+void mwifiex_queue_scan_cmd(struct mwifiex_private *priv,
+ struct cmd_ctrl_node *cmd_node);
+int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp);
+s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2);
+int mwifiex_associate(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp);
+void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason);
+u8 mwifiex_band_to_radio_type(u8 band);
+int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac);
+void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter);
+int mwifiex_adhoc_start(struct mwifiex_private *priv,
+ struct cfg80211_ssid *adhoc_ssid);
+int mwifiex_adhoc_join(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct cfg80211_ssid *req_ssid);
+int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp);
+int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd);
+struct mwifiex_chan_freq_power *mwifiex_get_cfp(struct mwifiex_private *priv,
+ u8 band, u16 channel, u32 freq);
+u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
+ u8 index, u8 ht_info);
+u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
+ u8 index, u8 ht_info);
+u32 mwifiex_find_freq_from_band_chan(u8, u8);
+int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask,
+ u8 **buffer);
+u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv,
+ u8 *rates);
+u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates);
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+ u8 *rates, u8 radio_type);
+u8 mwifiex_is_rate_auto(struct mwifiex_private *priv);
+extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE];
+void mwifiex_save_curr_bcn(struct mwifiex_private *priv);
+void mwifiex_free_curr_bcn(struct mwifiex_private *priv);
+int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd);
+int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp);
+int is_command_pending(struct mwifiex_adapter *adapter);
+void mwifiex_init_priv_params(struct mwifiex_private *priv,
+ struct net_device *dev);
+int mwifiex_set_secure_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_config,
+ struct cfg80211_ap_settings *params);
+void mwifiex_set_ht_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void mwifiex_set_vht_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void mwifiex_set_tpc_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void mwifiex_set_vht_width(struct mwifiex_private *priv,
+ enum nl80211_chan_width width,
+ bool ap_11ac_disable);
+void
+mwifiex_set_wmm_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void mwifiex_set_ba_params(struct mwifiex_private *priv);
+
+void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *pmadapter);
+void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv,
+ struct sk_buff *event_skb);
+
+void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv);
+int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf);
+int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp);
+int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
+ void *buf);
+
+/*
+ * This function checks if the queuing is RA based or not.
+ */
+static inline u8
+mwifiex_queuing_ra_based(struct mwifiex_private *priv)
+{
+ /*
+ * Currently we assume if we are in Infra, then DA=RA. This might not be
+ * true in the future
+ */
+ if ((priv->bss_mode == NL80211_IFTYPE_STATION) &&
+ (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA))
+ return false;
+
+ return true;
+}
+
+/*
+ * This function copies rates.
+ */
+static inline u32
+mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len)
+{
+ int i;
+
+ for (i = 0; i < len && src[i]; i++, pos++) {
+ if (pos >= MWIFIEX_SUPPORTED_RATES)
+ break;
+ dest[pos] = src[i];
+ }
+
+ return pos;
+}
+
+/*
+ * This function returns the correct private structure pointer based
+ * upon the BSS type and BSS number.
+ */
+static inline struct mwifiex_private *
+mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter,
+ u8 bss_num, u8 bss_type)
+{
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ if ((adapter->priv[i]->bss_num == bss_num) &&
+ (adapter->priv[i]->bss_type == bss_type))
+ break;
+ }
+ }
+ return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
+}
+
+/*
+ * This function returns the first available private structure pointer
+ * based upon the BSS role.
+ */
+static inline struct mwifiex_private *
+mwifiex_get_priv(struct mwifiex_adapter *adapter,
+ enum mwifiex_bss_role bss_role)
+{
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ if (bss_role == MWIFIEX_BSS_ROLE_ANY ||
+ GET_BSS_ROLE(adapter->priv[i]) == bss_role)
+ break;
+ }
+ }
+
+ return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
+}
+
+/*
+ * This function checks available bss_num when adding new interface or
+ * changing interface type.
+ */
+static inline u8
+mwifiex_get_unused_bss_num(struct mwifiex_adapter *adapter, u8 bss_type)
+{
+ u8 i, j;
+ int index[MWIFIEX_MAX_BSS_NUM];
+
+ memset(index, 0, sizeof(index));
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i]) {
+ if (adapter->priv[i]->bss_type == bss_type &&
+ !(adapter->priv[i]->bss_mode ==
+ NL80211_IFTYPE_UNSPECIFIED)) {
+ index[adapter->priv[i]->bss_num] = 1;
+ }
+ }
+ for (j = 0; j < MWIFIEX_MAX_BSS_NUM; j++)
+ if (!index[j])
+ return j;
+ return -1;
+}
+
+/*
+ * This function returns the first available unused private structure pointer.
+ */
+static inline struct mwifiex_private *
+mwifiex_get_unused_priv_by_bss_type(struct mwifiex_adapter *adapter,
+ u8 bss_type)
+{
+ u8 i;
+
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i]->bss_mode ==
+ NL80211_IFTYPE_UNSPECIFIED) {
+ adapter->priv[i]->bss_num =
+ mwifiex_get_unused_bss_num(adapter, bss_type);
+ break;
+ }
+
+ return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
+}
+
+/*
+ * This function returns the driver private structure of a network device.
+ */
+static inline struct mwifiex_private *
+mwifiex_netdev_get_priv(struct net_device *dev)
+{
+ return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev));
+}
+
+/*
+ * This function checks if a skb holds a management frame.
+ */
+static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb)
+{
+ return (le32_to_cpu(*(__le32 *)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 (time_after(jiffies, priv->csa_expire_time)) {
+ priv->csa_chan = 0;
+ priv->csa_expire_time = 0;
+ }
+
+ return priv->csa_chan;
+}
+
+static inline u8 mwifiex_is_any_intf_active(struct mwifiex_private *priv)
+{
+ struct mwifiex_private *priv_num;
+ int i;
+
+ for (i = 0; i < priv->adapter->priv_num; i++) {
+ priv_num = priv->adapter->priv[i];
+ if (priv_num) {
+ if ((GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_UAP &&
+ priv_num->bss_started) ||
+ (GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_STA &&
+ priv_num->media_connected))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static inline u8 mwifiex_is_tdls_link_setup(u8 status)
+{
+ switch (status) {
+ case TDLS_SETUP_COMPLETE:
+ case TDLS_CHAN_SWITCHING:
+ case TDLS_IN_BASE_CHAN:
+ case TDLS_IN_OFF_CHAN:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+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);
+int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *);
+
+void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version,
+ int maxlen);
+int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
+ struct mwifiex_multicast_list *mcast_list);
+int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
+ struct net_device *dev);
+int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_queued);
+int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
+ struct cfg80211_ssid *req_ssid);
+int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type);
+int mwifiex_enable_hs(struct mwifiex_adapter *adapter);
+int mwifiex_disable_auto_ds(struct mwifiex_private *priv);
+int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate);
+int mwifiex_request_scan(struct mwifiex_private *priv,
+ struct cfg80211_ssid *req_ssid);
+int mwifiex_scan_networks(struct mwifiex_private *priv,
+ const struct mwifiex_user_scan_cfg *user_scan_in);
+int mwifiex_set_radio(struct mwifiex_private *priv, u8 option);
+
+int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
+ const u8 *key, int key_len, u8 key_index,
+ const u8 *mac_addr, int disable);
+
+int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len);
+
+int mwifiex_get_ver_ext(struct mwifiex_private *priv);
+
+int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
+ struct ieee80211_channel *chan,
+ unsigned int duration);
+
+int mwifiex_get_stats_info(struct mwifiex_private *priv,
+ struct mwifiex_ds_get_stats *log);
+
+int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type,
+ u32 reg_offset, u32 reg_value);
+
+int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type,
+ u32 reg_offset, u32 *value);
+
+int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
+ u8 *value);
+
+int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data);
+
+int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data);
+
+int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index);
+
+int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index);
+
+int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode);
+
+int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter,
+ char *version, int max_len);
+
+int mwifiex_set_tx_power(struct mwifiex_private *priv,
+ struct mwifiex_power_cfg *power_cfg);
+
+int mwifiex_main_process(struct mwifiex_adapter *);
+
+int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb);
+
+int mwifiex_get_bss_info(struct mwifiex_private *,
+ struct mwifiex_bss_info *);
+int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
+ struct cfg80211_bss *bss,
+ struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
+ struct mwifiex_bssdescriptor *bss_entry);
+int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
+ struct mwifiex_bssdescriptor *bss_desc);
+
+u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type);
+u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset);
+
+struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params);
+int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev);
+
+void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config);
+
+int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter);
+
+int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
+ struct cfg80211_beacon_data *data);
+int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
+u8 *mwifiex_11d_code_2_region(u8 code);
+void mwifiex_uap_set_channel(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_chan_def chandef);
+int mwifiex_config_start_uap(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg);
+void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
+ struct mwifiex_sta_node *node);
+
+void mwifiex_init_11h_params(struct mwifiex_private *priv);
+int mwifiex_is_11h_active(struct mwifiex_private *priv);
+int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag);
+
+void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
+ struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv);
+int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
+ struct device_node *node, const char *prefix);
+void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
+
+extern const struct ethtool_ops mwifiex_ethtool_ops;
+
+void mwifiex_del_all_sta_list(struct mwifiex_private *priv);
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac);
+void
+mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
+ int ies_len, struct mwifiex_sta_node *node);
+struct mwifiex_sta_node *
+mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac);
+struct mwifiex_sta_node *
+mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac);
+u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv);
+u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv);
+u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv);
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *extra_ies,
+ size_t extra_ies_len);
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *extra_ies,
+ size_t extra_ies_len);
+void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
+ u8 *buf, int len);
+int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action);
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac);
+int mwifiex_get_tdls_list(struct mwifiex_private *priv,
+ struct tdls_peer_info *buf);
+void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv);
+bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv);
+u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
+ u32 pri_chan, u8 chan_bw);
+int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter);
+
+int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb);
+void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv);
+void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
+ const u8 *mac, u8 link_status);
+void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv,
+ u8 *mac, s8 snr, s8 nflr);
+void mwifiex_check_auto_tdls(unsigned long context);
+void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
+void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
+void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
+int mwifiex_config_tdls_enable(struct mwifiex_private *priv);
+int mwifiex_config_tdls_disable(struct mwifiex_private *priv);
+int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv);
+int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac);
+int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac,
+ u8 primary_chan, u8 second_chan_offset, u8 band);
+
+int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf);
+int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv,
+ struct sk_buff *skb);
+
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+ void *event_body);
+
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie);
+void mwifiex_dfs_cac_work_queue(struct work_struct *work);
+void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work);
+void mwifiex_abort_cac(struct mwifiex_private *priv);
+int mwifiex_stop_radar_detection(struct mwifiex_private *priv,
+ struct cfg80211_chan_def *chandef);
+int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv,
+ struct sk_buff *skb);
+
+void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr,
+ s8 nflr);
+void mwifiex_hist_data_reset(struct mwifiex_private *priv);
+void mwifiex_hist_data_add(struct mwifiex_private *priv,
+ u8 rx_rate, s8 snr, s8 nflr);
+u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
+ u8 rx_rate, u8 ht_info);
+
+void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter);
+void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter);
+void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags);
+void mwifiex_queue_main_work(struct mwifiex_adapter *adapter);
+void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter);
+void mwifiex_11n_delba(struct mwifiex_private *priv, int tid);
+int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy);
+void mwifiex_process_tx_pause_event(struct mwifiex_private *priv,
+ struct sk_buff *event);
+void mwifiex_process_multi_chan_event(struct mwifiex_private *priv,
+ struct sk_buff *event_skb);
+void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter);
+
+#ifdef CONFIG_DEBUG_FS
+void mwifiex_debugfs_init(void);
+void mwifiex_debugfs_remove(void);
+
+void mwifiex_dev_debugfs_init(struct mwifiex_private *priv);
+void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv);
+#endif
+#endif /* !_MWIFIEX_MAIN_H_ */
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
new file mode 100644
index 000000000000..6d0dc40e20e5
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -0,0 +1,2753 @@
+/*
+ * Marvell Wireless LAN device driver: PCIE specific handling
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 <linux/firmware.h>
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "pcie.h"
+
+#define PCIE_VERSION "1.0"
+#define DRV_NAME "Marvell mwifiex PCIe"
+
+static u8 user_rmmod;
+
+static struct mwifiex_if_ops pcie_ops;
+
+static struct semaphore add_remove_card_sem;
+
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ {"ITCM", NULL, 0, 0xF0},
+ {"DTCM", NULL, 0, 0xF1},
+ {"SQRAM", NULL, 0, 0xF2},
+ {"IRAM", NULL, 0, 0xF3},
+ {"APU", NULL, 0, 0xF4},
+ {"CIU", NULL, 0, 0xF5},
+ {"ICU", NULL, 0, 0xF6},
+ {"MAC", NULL, 0, 0xF7},
+};
+
+static int
+mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
+ size_t size, int flags)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct mwifiex_dma_mapping mapping;
+
+ mapping.addr = pci_map_single(card->dev, skb->data, size, flags);
+ if (pci_dma_mapping_error(card->dev, mapping.addr)) {
+ mwifiex_dbg(adapter, ERROR, "failed to map pci memory!\n");
+ return -1;
+ }
+ mapping.len = size;
+ mwifiex_store_mapping(skb, &mapping);
+ return 0;
+}
+
+static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb, int flags)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct mwifiex_dma_mapping mapping;
+
+ mwifiex_get_mapping(skb, &mapping);
+ pci_unmap_single(card->dev, mapping.addr, mapping.len, flags);
+}
+
+/*
+ * This function reads sleep cookie and checks if FW is ready
+ */
+static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
+{
+ u32 *cookie_addr;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ if (!reg->sleep_cookie)
+ return true;
+
+ if (card->sleep_cookie_vbase) {
+ cookie_addr = (u32 *)card->sleep_cookie_vbase;
+ mwifiex_dbg(adapter, INFO,
+ "info: ACCESS_HW: sleep cookie=0x%x\n",
+ *cookie_addr);
+ if (*cookie_addr == FW_AWAKE_COOKIE)
+ return true;
+ }
+
+ return false;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not suspended, this function allocates and sends a host
+ * sleep activate request to the firmware and turns off the traffic.
+ */
+static int mwifiex_pcie_suspend(struct device *dev)
+{
+ struct mwifiex_adapter *adapter;
+ struct pcie_service_card *card;
+ int hs_actived;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (pdev) {
+ card = pci_get_drvdata(pdev);
+ if (!card || !card->adapter) {
+ pr_err("Card or adapter structure is not valid\n");
+ return 0;
+ }
+ } else {
+ pr_err("PCIE device is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ hs_actived = mwifiex_enable_hs(adapter);
+
+ /* Indicate device suspended */
+ adapter->is_suspended = true;
+ adapter->hs_enabling = false;
+
+ return 0;
+}
+
+/*
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int mwifiex_pcie_resume(struct device *dev)
+{
+ struct mwifiex_adapter *adapter;
+ struct pcie_service_card *card;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (pdev) {
+ card = pci_get_drvdata(pdev);
+ if (!card || !card->adapter) {
+ pr_err("Card or adapter structure is not valid\n");
+ return 0;
+ }
+ } else {
+ pr_err("PCIE device is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ if (!adapter->is_suspended) {
+ mwifiex_dbg(adapter, WARN,
+ "Device already resumed\n");
+ return 0;
+ }
+
+ adapter->is_suspended = false;
+
+ mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+ MWIFIEX_ASYNC_CMD);
+
+ return 0;
+}
+#endif
+
+/*
+ * This function probes an mwifiex device and registers it. It allocates
+ * the card structure, enables PCIE function number and initiates the
+ * device registration and initialization procedure by adding a logical
+ * interface.
+ */
+static int mwifiex_pcie_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct pcie_service_card *card;
+
+ pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
+ pdev->vendor, pdev->device, pdev->revision);
+
+ card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = pdev;
+
+ if (ent->driver_data) {
+ struct mwifiex_pcie_device *data = (void *)ent->driver_data;
+ card->pcie.firmware = data->firmware;
+ card->pcie.reg = data->reg;
+ card->pcie.blksz_fw_dl = data->blksz_fw_dl;
+ card->pcie.tx_buf_size = data->tx_buf_size;
+ card->pcie.can_dump_fw = data->can_dump_fw;
+ card->pcie.can_ext_scan = data->can_ext_scan;
+ }
+
+ if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
+ MWIFIEX_PCIE)) {
+ pr_err("%s failed\n", __func__);
+ kfree(card);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function removes the interface and frees up the card structure.
+ */
+static void mwifiex_pcie_remove(struct pci_dev *pdev)
+{
+ struct pcie_service_card *card;
+ struct mwifiex_adapter *adapter;
+ struct mwifiex_private *priv;
+
+ card = pci_get_drvdata(pdev);
+ if (!card)
+ return;
+
+ adapter = card->adapter;
+ if (!adapter || !adapter->priv_num)
+ return;
+
+ if (user_rmmod) {
+#ifdef CONFIG_PM_SLEEP
+ if (adapter->is_suspended)
+ mwifiex_pcie_resume(&pdev->dev);
+#endif
+
+ mwifiex_deauthenticate_all(adapter);
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+ mwifiex_disable_auto_ds(priv);
+
+ mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN);
+ }
+
+ mwifiex_remove_card(card->adapter, &add_remove_card_sem);
+}
+
+static void mwifiex_pcie_shutdown(struct pci_dev *pdev)
+{
+ user_rmmod = 1;
+ mwifiex_pcie_remove(pdev);
+
+ return;
+}
+
+static const struct pci_device_id mwifiex_ids[] = {
+ {
+ PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ .driver_data = (unsigned long)&mwifiex_pcie8766,
+ },
+ {
+ PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ .driver_data = (unsigned long)&mwifiex_pcie8897,
+ },
+ {
+ PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ .driver_data = (unsigned long)&mwifiex_pcie8997,
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(pci, mwifiex_ids);
+
+#ifdef CONFIG_PM_SLEEP
+/* Power Management Hooks */
+static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend,
+ mwifiex_pcie_resume);
+#endif
+
+/* PCI Device Driver */
+static struct pci_driver __refdata mwifiex_pcie = {
+ .name = "mwifiex_pcie",
+ .id_table = mwifiex_ids,
+ .probe = mwifiex_pcie_probe,
+ .remove = mwifiex_pcie_remove,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &mwifiex_pcie_pm_ops,
+ },
+#endif
+ .shutdown = mwifiex_pcie_shutdown,
+};
+
+/*
+ * This function writes data into PCIE card register.
+ */
+static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ iowrite32(data, card->pci_mmap1 + reg);
+
+ return 0;
+}
+
+/*
+ * This function reads data from PCIE card register.
+ */
+static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ *data = ioread32(card->pci_mmap1 + reg);
+
+ return 0;
+}
+
+/* This function reads u8 data from PCIE card register. */
+static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter,
+ int reg, u8 *data)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ *data = ioread8(card->pci_mmap1 + reg);
+
+ return 0;
+}
+
+/*
+ * This function adds delay loop to ensure FW is awake before proceeding.
+ */
+static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter)
+{
+ int i = 0;
+
+ while (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ i++;
+ usleep_range(10, 20);
+ /* 50ms max wait */
+ if (i == 5000)
+ break;
+ }
+
+ return;
+}
+
+static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter,
+ u32 max_delay_loop_cnt)
+{
+ struct pcie_service_card *card = adapter->card;
+ u8 *buffer;
+ u32 sleep_cookie, count;
+
+ for (count = 0; count < max_delay_loop_cnt; count++) {
+ buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN;
+ sleep_cookie = *(u32 *)buffer;
+
+ if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) {
+ mwifiex_dbg(adapter, INFO,
+ "sleep cookie found at count %d\n", count);
+ break;
+ }
+ usleep_range(20, 30);
+ }
+
+ if (count >= max_delay_loop_cnt)
+ mwifiex_dbg(adapter, INFO,
+ "max count reached while accessing sleep cookie\n");
+}
+
+/* This function wakes up the card by reading fw_status register. */
+static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
+{
+ u32 fw_status;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ mwifiex_dbg(adapter, EVENT,
+ "event: Wakeup device...\n");
+
+ if (reg->sleep_cookie)
+ mwifiex_pcie_dev_wakeup_delay(adapter);
+
+ /* Reading fw_status register will wakeup device */
+ if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Reading fw_status register failed\n");
+ return -1;
+ }
+
+ if (reg->sleep_cookie) {
+ mwifiex_pcie_dev_wakeup_delay(adapter);
+ mwifiex_dbg(adapter, INFO,
+ "PCIE wakeup: Setting PS_STATE_AWAKE\n");
+ adapter->ps_state = PS_STATE_AWAKE;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called after the card has woken up.
+ *
+ * The card configuration register is reset.
+ */
+static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
+{
+ mwifiex_dbg(adapter, CMD,
+ "cmd: Wakeup device completed\n");
+
+ return 0;
+}
+
+/*
+ * This function disables the host interrupt.
+ *
+ * The host interrupt mask is read, the disable bit is reset and
+ * written back to the card host interrupt mask register.
+ */
+static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter)
+{
+ if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK,
+ 0x00000000)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Disable host interrupt failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function enables the host interrupt.
+ *
+ * The host interrupt enable mask is written to the card
+ * host interrupt mask register.
+ */
+static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter)
+{
+ if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ /* Simply write the mask to the register */
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK,
+ HOST_INTR_MASK)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Enable host interrupt failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function initializes TX buffer ring descriptors
+ */
+static int mwifiex_init_txq_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ struct mwifiex_pcie_buf_desc *desc;
+ struct mwifiex_pfu_buf_desc *desc2;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ card->tx_buf_list[i] = NULL;
+ if (reg->pfu_enabled) {
+ card->txbd_ring[i] = (void *)card->txbd_ring_vbase +
+ (sizeof(*desc2) * i);
+ desc2 = card->txbd_ring[i];
+ memset(desc2, 0, sizeof(*desc2));
+ } else {
+ card->txbd_ring[i] = (void *)card->txbd_ring_vbase +
+ (sizeof(*desc) * i);
+ desc = card->txbd_ring[i];
+ memset(desc, 0, sizeof(*desc));
+ }
+ }
+
+ return 0;
+}
+
+/* This function initializes RX buffer ring descriptors. Each SKB is allocated
+ * here and after mapping PCI memory, its physical address is assigned to
+ * PCIE Rx buffer descriptor's physical address.
+ */
+static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ struct sk_buff *skb;
+ struct mwifiex_pcie_buf_desc *desc;
+ struct mwifiex_pfu_buf_desc *desc2;
+ dma_addr_t buf_pa;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ /* Allocate skb here so that firmware can DMA data from it */
+ skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!skb) {
+ mwifiex_dbg(adapter, ERROR,
+ "Unable to allocate skb for RX ring.\n");
+ kfree(card->rxbd_ring_vbase);
+ return -ENOMEM;
+ }
+
+ if (mwifiex_map_pci_memory(adapter, skb,
+ MWIFIEX_RX_DATA_BUF_SIZE,
+ PCI_DMA_FROMDEVICE))
+ return -1;
+
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
+
+ mwifiex_dbg(adapter, INFO,
+ "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
+ skb, skb->len, skb->data, (u32)buf_pa,
+ (u32)((u64)buf_pa >> 32));
+
+ card->rx_buf_list[i] = skb;
+ if (reg->pfu_enabled) {
+ card->rxbd_ring[i] = (void *)card->rxbd_ring_vbase +
+ (sizeof(*desc2) * i);
+ desc2 = card->rxbd_ring[i];
+ desc2->paddr = buf_pa;
+ desc2->len = (u16)skb->len;
+ desc2->frag_len = (u16)skb->len;
+ desc2->flags = reg->ring_flag_eop | reg->ring_flag_sop;
+ desc2->offset = 0;
+ } else {
+ card->rxbd_ring[i] = (void *)(card->rxbd_ring_vbase +
+ (sizeof(*desc) * i));
+ desc = card->rxbd_ring[i];
+ desc->paddr = buf_pa;
+ desc->len = (u16)skb->len;
+ desc->flags = 0;
+ }
+ }
+
+ return 0;
+}
+
+/* This function initializes event buffer ring descriptors. Each SKB is
+ * allocated here and after mapping PCI memory, its physical address is assigned
+ * to PCIE Rx buffer descriptor's physical address
+ */
+static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct mwifiex_evt_buf_desc *desc;
+ struct sk_buff *skb;
+ dma_addr_t buf_pa;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
+ /* Allocate skb here so that firmware can DMA data from it */
+ skb = dev_alloc_skb(MAX_EVENT_SIZE);
+ if (!skb) {
+ mwifiex_dbg(adapter, ERROR,
+ "Unable to allocate skb for EVENT buf.\n");
+ kfree(card->evtbd_ring_vbase);
+ return -ENOMEM;
+ }
+ skb_put(skb, MAX_EVENT_SIZE);
+
+ if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE,
+ PCI_DMA_FROMDEVICE))
+ return -1;
+
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
+
+ mwifiex_dbg(adapter, EVENT,
+ "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
+ skb, skb->len, skb->data, (u32)buf_pa,
+ (u32)((u64)buf_pa >> 32));
+
+ card->evt_buf_list[i] = skb;
+ card->evtbd_ring[i] = (void *)(card->evtbd_ring_vbase +
+ (sizeof(*desc) * i));
+ desc = card->evtbd_ring[i];
+ desc->paddr = buf_pa;
+ desc->len = (u16)skb->len;
+ desc->flags = 0;
+ }
+
+ return 0;
+}
+
+/* This function cleans up TX buffer rings. If any of the buffer list has valid
+ * SKB address, associated SKB is freed.
+ */
+static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ struct sk_buff *skb;
+ struct mwifiex_pcie_buf_desc *desc;
+ struct mwifiex_pfu_buf_desc *desc2;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ if (reg->pfu_enabled) {
+ desc2 = card->txbd_ring[i];
+ if (card->tx_buf_list[i]) {
+ skb = card->tx_buf_list[i];
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
+ }
+ memset(desc2, 0, sizeof(*desc2));
+ } else {
+ desc = card->txbd_ring[i];
+ if (card->tx_buf_list[i]) {
+ skb = card->tx_buf_list[i];
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
+ }
+ memset(desc, 0, sizeof(*desc));
+ }
+ card->tx_buf_list[i] = NULL;
+ }
+
+ return;
+}
+
+/* This function cleans up RX buffer rings. If any of the buffer list has valid
+ * SKB address, associated SKB is freed.
+ */
+static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ struct mwifiex_pcie_buf_desc *desc;
+ struct mwifiex_pfu_buf_desc *desc2;
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
+ if (reg->pfu_enabled) {
+ desc2 = card->rxbd_ring[i];
+ if (card->rx_buf_list[i]) {
+ skb = card->rx_buf_list[i];
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(skb);
+ }
+ memset(desc2, 0, sizeof(*desc2));
+ } else {
+ desc = card->rxbd_ring[i];
+ if (card->rx_buf_list[i]) {
+ skb = card->rx_buf_list[i];
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(skb);
+ }
+ memset(desc, 0, sizeof(*desc));
+ }
+ card->rx_buf_list[i] = NULL;
+ }
+
+ return;
+}
+
+/* This function cleans up event buffer rings. If any of the buffer list has
+ * valid SKB address, associated SKB is freed.
+ */
+static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct mwifiex_evt_buf_desc *desc;
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
+ desc = card->evtbd_ring[i];
+ if (card->evt_buf_list[i]) {
+ skb = card->evt_buf_list[i];
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(skb);
+ }
+ card->evt_buf_list[i] = NULL;
+ memset(desc, 0, sizeof(*desc));
+ }
+
+ return;
+}
+
+/* This function creates buffer descriptor ring for TX
+ */
+static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ /*
+ * driver maintaines the write pointer and firmware maintaines the read
+ * pointer. The write pointer starts at 0 (zero) while the read pointer
+ * starts at zero with rollover bit set
+ */
+ card->txbd_wrptr = 0;
+
+ if (reg->pfu_enabled)
+ card->txbd_rdptr = 0;
+ else
+ card->txbd_rdptr |= reg->tx_rollover_ind;
+
+ /* allocate shared memory for the BD ring and divide the same in to
+ several descriptors */
+ if (reg->pfu_enabled)
+ card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) *
+ MWIFIEX_MAX_TXRX_BD;
+ else
+ card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
+ MWIFIEX_MAX_TXRX_BD;
+
+ mwifiex_dbg(adapter, INFO,
+ "info: txbd_ring: Allocating %d bytes\n",
+ card->txbd_ring_size);
+ card->txbd_ring_vbase = pci_alloc_consistent(card->dev,
+ card->txbd_ring_size,
+ &card->txbd_ring_pbase);
+ if (!card->txbd_ring_vbase) {
+ mwifiex_dbg(adapter, ERROR,
+ "allocate consistent memory (%d bytes) failed!\n",
+ card->txbd_ring_size);
+ return -ENOMEM;
+ }
+ mwifiex_dbg(adapter, DATA,
+ "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n",
+ card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase,
+ (u32)((u64)card->txbd_ring_pbase >> 32),
+ card->txbd_ring_size);
+
+ return mwifiex_init_txq_ring(adapter);
+}
+
+static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ mwifiex_cleanup_txq_ring(adapter);
+
+ if (card->txbd_ring_vbase)
+ pci_free_consistent(card->dev, card->txbd_ring_size,
+ card->txbd_ring_vbase,
+ card->txbd_ring_pbase);
+ card->txbd_ring_size = 0;
+ card->txbd_wrptr = 0;
+ card->txbd_rdptr = 0 | reg->tx_rollover_ind;
+ card->txbd_ring_vbase = NULL;
+ card->txbd_ring_pbase = 0;
+
+ return 0;
+}
+
+/*
+ * This function creates buffer descriptor ring for RX
+ */
+static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ /*
+ * driver maintaines the read pointer and firmware maintaines the write
+ * pointer. The write pointer starts at 0 (zero) while the read pointer
+ * starts at zero with rollover bit set
+ */
+ card->rxbd_wrptr = 0;
+ card->rxbd_rdptr = reg->rx_rollover_ind;
+
+ if (reg->pfu_enabled)
+ card->rxbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) *
+ MWIFIEX_MAX_TXRX_BD;
+ else
+ card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
+ MWIFIEX_MAX_TXRX_BD;
+
+ mwifiex_dbg(adapter, INFO,
+ "info: rxbd_ring: Allocating %d bytes\n",
+ card->rxbd_ring_size);
+ card->rxbd_ring_vbase = pci_alloc_consistent(card->dev,
+ card->rxbd_ring_size,
+ &card->rxbd_ring_pbase);
+ if (!card->rxbd_ring_vbase) {
+ mwifiex_dbg(adapter, ERROR,
+ "allocate consistent memory (%d bytes) failed!\n",
+ card->rxbd_ring_size);
+ return -ENOMEM;
+ }
+
+ mwifiex_dbg(adapter, DATA,
+ "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n",
+ card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase,
+ (u32)((u64)card->rxbd_ring_pbase >> 32),
+ card->rxbd_ring_size);
+
+ return mwifiex_init_rxq_ring(adapter);
+}
+
+/*
+ * This function deletes Buffer descriptor ring for RX
+ */
+static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ mwifiex_cleanup_rxq_ring(adapter);
+
+ if (card->rxbd_ring_vbase)
+ pci_free_consistent(card->dev, card->rxbd_ring_size,
+ card->rxbd_ring_vbase,
+ card->rxbd_ring_pbase);
+ card->rxbd_ring_size = 0;
+ card->rxbd_wrptr = 0;
+ card->rxbd_rdptr = 0 | reg->rx_rollover_ind;
+ card->rxbd_ring_vbase = NULL;
+ card->rxbd_ring_pbase = 0;
+
+ return 0;
+}
+
+/*
+ * This function creates buffer descriptor ring for Events
+ */
+static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ /*
+ * driver maintaines the read pointer and firmware maintaines the write
+ * pointer. The write pointer starts at 0 (zero) while the read pointer
+ * starts at zero with rollover bit set
+ */
+ card->evtbd_wrptr = 0;
+ card->evtbd_rdptr = reg->evt_rollover_ind;
+
+ card->evtbd_ring_size = sizeof(struct mwifiex_evt_buf_desc) *
+ MWIFIEX_MAX_EVT_BD;
+
+ mwifiex_dbg(adapter, INFO,
+ "info: evtbd_ring: Allocating %d bytes\n",
+ card->evtbd_ring_size);
+ card->evtbd_ring_vbase = pci_alloc_consistent(card->dev,
+ card->evtbd_ring_size,
+ &card->evtbd_ring_pbase);
+ if (!card->evtbd_ring_vbase) {
+ mwifiex_dbg(adapter, ERROR,
+ "allocate consistent memory (%d bytes) failed!\n",
+ card->evtbd_ring_size);
+ return -ENOMEM;
+ }
+
+ mwifiex_dbg(adapter, EVENT,
+ "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n",
+ card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase,
+ (u32)((u64)card->evtbd_ring_pbase >> 32),
+ card->evtbd_ring_size);
+
+ return mwifiex_pcie_init_evt_ring(adapter);
+}
+
+/*
+ * This function deletes Buffer descriptor ring for Events
+ */
+static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ mwifiex_cleanup_evt_ring(adapter);
+
+ if (card->evtbd_ring_vbase)
+ pci_free_consistent(card->dev, card->evtbd_ring_size,
+ card->evtbd_ring_vbase,
+ card->evtbd_ring_pbase);
+ card->evtbd_wrptr = 0;
+ card->evtbd_rdptr = 0 | reg->evt_rollover_ind;
+ card->evtbd_ring_size = 0;
+ card->evtbd_ring_vbase = NULL;
+ card->evtbd_ring_pbase = 0;
+
+ return 0;
+}
+
+/*
+ * This function allocates a buffer for CMDRSP
+ */
+static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct sk_buff *skb;
+
+ /* Allocate memory for receiving command response data */
+ skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE);
+ if (!skb) {
+ mwifiex_dbg(adapter, ERROR,
+ "Unable to allocate skb for command response data.\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, MWIFIEX_UPLD_SIZE);
+ if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
+ PCI_DMA_FROMDEVICE))
+ return -1;
+
+ card->cmdrsp_buf = skb;
+
+ return 0;
+}
+
+/*
+ * This function deletes a buffer for CMDRSP
+ */
+static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card;
+
+ if (!adapter)
+ return 0;
+
+ card = adapter->card;
+
+ if (card && card->cmdrsp_buf) {
+ mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(card->cmdrsp_buf);
+ }
+
+ if (card && card->cmd_buf) {
+ mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
+ PCI_DMA_TODEVICE);
+ }
+ return 0;
+}
+
+/*
+ * This function allocates a buffer for sleep cookie
+ */
+static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32),
+ &card->sleep_cookie_pbase);
+ if (!card->sleep_cookie_vbase) {
+ mwifiex_dbg(adapter, ERROR,
+ "pci_alloc_consistent failed!\n");
+ return -ENOMEM;
+ }
+ /* Init val of Sleep Cookie */
+ *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE;
+
+ mwifiex_dbg(adapter, INFO,
+ "alloc_scook: sleep cookie=0x%x\n",
+ *((u32 *)card->sleep_cookie_vbase));
+
+ return 0;
+}
+
+/*
+ * This function deletes buffer for sleep cookie
+ */
+static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card;
+
+ if (!adapter)
+ return 0;
+
+ card = adapter->card;
+
+ if (card && card->sleep_cookie_vbase) {
+ pci_free_consistent(card->dev, sizeof(u32),
+ card->sleep_cookie_vbase,
+ card->sleep_cookie_pbase);
+ card->sleep_cookie_vbase = NULL;
+ }
+
+ return 0;
+}
+
+/* This function flushes the TX buffer descriptor ring
+ * This function defined as handler is also called while cleaning TXRX
+ * during disconnect/ bss stop.
+ */
+static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) {
+ card->txbd_flush = 1;
+ /* write pointer already set at last send
+ * send dnld-rdy intr again, wait for completion.
+ */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DNLD_RDY)) {
+ mwifiex_dbg(adapter, ERROR,
+ "failed to assert dnld-rdy interrupt.\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * This function unmaps and frees downloaded data buffer
+ */
+static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
+{
+ struct sk_buff *skb;
+ u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0;
+ struct mwifiex_pcie_buf_desc *desc;
+ struct mwifiex_pfu_buf_desc *desc2;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ mwifiex_pm_wakeup_card(adapter);
+
+ /* Read the TX ring read pointer set by firmware */
+ if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) {
+ mwifiex_dbg(adapter, ERROR,
+ "SEND COMP: failed to read reg->tx_rdptr\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, DATA,
+ "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n",
+ card->txbd_rdptr, rdptr);
+
+ num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr;
+ /* free from previous txbd_rdptr to current txbd_rdptr */
+ while (((card->txbd_rdptr & reg->tx_mask) !=
+ (rdptr & reg->tx_mask)) ||
+ ((card->txbd_rdptr & reg->tx_rollover_ind) !=
+ (rdptr & reg->tx_rollover_ind))) {
+ wrdoneidx = (card->txbd_rdptr & reg->tx_mask) >>
+ reg->tx_start_ptr;
+
+ skb = card->tx_buf_list[wrdoneidx];
+
+ if (skb) {
+ mwifiex_dbg(adapter, DATA,
+ "SEND COMP: Detach skb %p at txbd_rdidx=%d\n",
+ skb, wrdoneidx);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
+
+ unmap_count++;
+
+ if (card->txbd_flush)
+ mwifiex_write_data_complete(adapter, skb, 0,
+ -1);
+ else
+ mwifiex_write_data_complete(adapter, skb, 0, 0);
+ }
+
+ card->tx_buf_list[wrdoneidx] = NULL;
+
+ if (reg->pfu_enabled) {
+ desc2 = card->txbd_ring[wrdoneidx];
+ memset(desc2, 0, sizeof(*desc2));
+ } else {
+ desc = card->txbd_ring[wrdoneidx];
+ memset(desc, 0, sizeof(*desc));
+ }
+ switch (card->dev->device) {
+ case PCIE_DEVICE_ID_MARVELL_88W8766P:
+ card->txbd_rdptr++;
+ break;
+ case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
+ card->txbd_rdptr += reg->ring_tx_start_ptr;
+ break;
+ }
+
+
+ if ((card->txbd_rdptr & reg->tx_mask) == num_tx_buffs)
+ card->txbd_rdptr = ((card->txbd_rdptr &
+ reg->tx_rollover_ind) ^
+ reg->tx_rollover_ind);
+ }
+
+ if (unmap_count)
+ adapter->data_sent = false;
+
+ if (card->txbd_flush) {
+ if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr))
+ card->txbd_flush = 0;
+ else
+ mwifiex_clean_pcie_ring_buf(adapter);
+ }
+
+ return 0;
+}
+
+/* This function sends data buffer to device. First 4 bytes of payload
+ * are filled with payload length and payload type. Then this payload
+ * is mapped to PCI device memory. Tx ring pointers are advanced accordingly.
+ * Download ready interrupt to FW is deffered if Tx ring is not full and
+ * additional payload can be accomodated.
+ * Caller must ensure tx_param parameter to this function is not NULL.
+ */
+static int
+mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
+ struct mwifiex_tx_param *tx_param)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ u32 wrindx, num_tx_buffs, rx_val;
+ int ret;
+ dma_addr_t buf_pa;
+ struct mwifiex_pcie_buf_desc *desc = NULL;
+ struct mwifiex_pfu_buf_desc *desc2 = NULL;
+ __le16 *tmp;
+
+ if (!(skb->data && skb->len)) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s(): invalid parameter <%p, %#x>\n",
+ __func__, skb->data, skb->len);
+ return -1;
+ }
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ mwifiex_pm_wakeup_card(adapter);
+
+ num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr;
+ mwifiex_dbg(adapter, DATA,
+ "info: SEND DATA: <Rd: %#x, Wr: %#x>\n",
+ card->txbd_rdptr, card->txbd_wrptr);
+ if (mwifiex_pcie_txbd_not_full(card)) {
+ u8 *payload;
+
+ adapter->data_sent = true;
+ payload = skb->data;
+ tmp = (__le16 *)&payload[0];
+ *tmp = cpu_to_le16((u16)skb->len);
+ tmp = (__le16 *)&payload[2];
+ *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA);
+
+ if (mwifiex_map_pci_memory(adapter, skb, skb->len,
+ PCI_DMA_TODEVICE))
+ return -1;
+
+ wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr;
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
+ card->tx_buf_list[wrindx] = skb;
+
+ if (reg->pfu_enabled) {
+ desc2 = card->txbd_ring[wrindx];
+ desc2->paddr = buf_pa;
+ desc2->len = (u16)skb->len;
+ desc2->frag_len = (u16)skb->len;
+ desc2->offset = 0;
+ desc2->flags = MWIFIEX_BD_FLAG_FIRST_DESC |
+ MWIFIEX_BD_FLAG_LAST_DESC;
+ } else {
+ desc = card->txbd_ring[wrindx];
+ desc->paddr = buf_pa;
+ desc->len = (u16)skb->len;
+ desc->flags = MWIFIEX_BD_FLAG_FIRST_DESC |
+ MWIFIEX_BD_FLAG_LAST_DESC;
+ }
+
+ switch (card->dev->device) {
+ case PCIE_DEVICE_ID_MARVELL_88W8766P:
+ card->txbd_wrptr++;
+ break;
+ case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
+ card->txbd_wrptr += reg->ring_tx_start_ptr;
+ break;
+ }
+
+ if ((card->txbd_wrptr & reg->tx_mask) == num_tx_buffs)
+ card->txbd_wrptr = ((card->txbd_wrptr &
+ reg->tx_rollover_ind) ^
+ reg->tx_rollover_ind);
+
+ rx_val = card->rxbd_rdptr & reg->rx_wrap_mask;
+ /* Write the TX ring write pointer in to reg->tx_wrptr */
+ if (mwifiex_write_reg(adapter, reg->tx_wrptr,
+ card->txbd_wrptr | rx_val)) {
+ mwifiex_dbg(adapter, ERROR,
+ "SEND DATA: failed to write reg->tx_wrptr\n");
+ ret = -1;
+ goto done_unmap;
+ }
+ if ((mwifiex_pcie_txbd_not_full(card)) &&
+ tx_param->next_pkt_len) {
+ /* have more packets and TxBD still can hold more */
+ mwifiex_dbg(adapter, DATA,
+ "SEND DATA: delay dnld-rdy interrupt.\n");
+ adapter->data_sent = false;
+ } else {
+ /* Send the TX ready interrupt */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DNLD_RDY)) {
+ mwifiex_dbg(adapter, ERROR,
+ "SEND DATA: failed to assert dnld-rdy interrupt.\n");
+ ret = -1;
+ goto done_unmap;
+ }
+ }
+ mwifiex_dbg(adapter, DATA,
+ "info: SEND DATA: Updated <Rd: %#x, Wr:\t"
+ "%#x> and sent packet to firmware successfully\n",
+ card->txbd_rdptr, card->txbd_wrptr);
+ } else {
+ mwifiex_dbg(adapter, DATA,
+ "info: TX Ring full, can't send packets to fw\n");
+ adapter->data_sent = true;
+ /* Send the TX ready interrupt */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DNLD_RDY))
+ mwifiex_dbg(adapter, ERROR,
+ "SEND DATA: failed to assert door-bell intr\n");
+ return -EBUSY;
+ }
+
+ return -EINPROGRESS;
+done_unmap:
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
+ card->tx_buf_list[wrindx] = NULL;
+ if (reg->pfu_enabled)
+ memset(desc2, 0, sizeof(*desc2));
+ else
+ memset(desc, 0, sizeof(*desc));
+
+ return ret;
+}
+
+/*
+ * This function handles received buffer ring and
+ * dispatches packets to upper
+ */
+static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ u32 wrptr, rd_index, tx_val;
+ dma_addr_t buf_pa;
+ int ret = 0;
+ struct sk_buff *skb_tmp = NULL;
+ struct mwifiex_pcie_buf_desc *desc;
+ struct mwifiex_pfu_buf_desc *desc2;
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ mwifiex_pm_wakeup_card(adapter);
+
+ /* Read the RX ring Write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) {
+ mwifiex_dbg(adapter, ERROR,
+ "RECV DATA: failed to read reg->rx_wrptr\n");
+ ret = -1;
+ goto done;
+ }
+ card->rxbd_wrptr = wrptr;
+
+ while (((wrptr & reg->rx_mask) !=
+ (card->rxbd_rdptr & reg->rx_mask)) ||
+ ((wrptr & reg->rx_rollover_ind) ==
+ (card->rxbd_rdptr & reg->rx_rollover_ind))) {
+ struct sk_buff *skb_data;
+ u16 rx_len;
+ __le16 pkt_len;
+
+ rd_index = card->rxbd_rdptr & reg->rx_mask;
+ skb_data = card->rx_buf_list[rd_index];
+
+ /* If skb allocation was failed earlier for Rx packet,
+ * rx_buf_list[rd_index] would have been left with a NULL.
+ */
+ if (!skb_data)
+ return -ENOMEM;
+
+ mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE);
+ card->rx_buf_list[rd_index] = NULL;
+
+ /* Get data length from interface header -
+ * first 2 bytes for len, next 2 bytes is for type
+ */
+ pkt_len = *((__le16 *)skb_data->data);
+ rx_len = le16_to_cpu(pkt_len);
+ if (WARN_ON(rx_len <= INTF_HEADER_LEN ||
+ rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Invalid RX len %d, Rd=%#x, Wr=%#x\n",
+ rx_len, card->rxbd_rdptr, wrptr);
+ dev_kfree_skb_any(skb_data);
+ } else {
+ skb_put(skb_data, rx_len);
+ mwifiex_dbg(adapter, DATA,
+ "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n",
+ card->rxbd_rdptr, wrptr, rx_len);
+ skb_pull(skb_data, INTF_HEADER_LEN);
+ if (adapter->rx_work_enabled) {
+ skb_queue_tail(&adapter->rx_data_q, skb_data);
+ adapter->data_received = true;
+ atomic_inc(&adapter->rx_pending);
+ } else {
+ mwifiex_handle_rx_packet(adapter, skb_data);
+ }
+ }
+
+ skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!skb_tmp) {
+ mwifiex_dbg(adapter, ERROR,
+ "Unable to allocate skb.\n");
+ return -ENOMEM;
+ }
+
+ if (mwifiex_map_pci_memory(adapter, skb_tmp,
+ MWIFIEX_RX_DATA_BUF_SIZE,
+ PCI_DMA_FROMDEVICE))
+ return -1;
+
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp);
+
+ mwifiex_dbg(adapter, INFO,
+ "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n",
+ skb_tmp, rd_index);
+ card->rx_buf_list[rd_index] = skb_tmp;
+
+ if (reg->pfu_enabled) {
+ desc2 = card->rxbd_ring[rd_index];
+ desc2->paddr = buf_pa;
+ desc2->len = skb_tmp->len;
+ desc2->frag_len = skb_tmp->len;
+ desc2->offset = 0;
+ desc2->flags = reg->ring_flag_sop | reg->ring_flag_eop;
+ } else {
+ desc = card->rxbd_ring[rd_index];
+ desc->paddr = buf_pa;
+ desc->len = skb_tmp->len;
+ desc->flags = 0;
+ }
+
+ if ((++card->rxbd_rdptr & reg->rx_mask) ==
+ MWIFIEX_MAX_TXRX_BD) {
+ card->rxbd_rdptr = ((card->rxbd_rdptr &
+ reg->rx_rollover_ind) ^
+ reg->rx_rollover_ind);
+ }
+ mwifiex_dbg(adapter, DATA,
+ "info: RECV DATA: <Rd: %#x, Wr: %#x>\n",
+ card->rxbd_rdptr, wrptr);
+
+ tx_val = card->txbd_wrptr & reg->tx_wrap_mask;
+ /* Write the RX ring read pointer in to reg->rx_rdptr */
+ if (mwifiex_write_reg(adapter, reg->rx_rdptr,
+ card->rxbd_rdptr | tx_val)) {
+ mwifiex_dbg(adapter, DATA,
+ "RECV DATA: failed to write reg->rx_rdptr\n");
+ ret = -1;
+ goto done;
+ }
+
+ /* Read the RX ring Write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) {
+ mwifiex_dbg(adapter, ERROR,
+ "RECV DATA: failed to read reg->rx_wrptr\n");
+ ret = -1;
+ goto done;
+ }
+ mwifiex_dbg(adapter, DATA,
+ "info: RECV DATA: Rcvd packet from fw successfully\n");
+ card->rxbd_wrptr = wrptr;
+ }
+
+done:
+ return ret;
+}
+
+/*
+ * This function downloads the boot command to device
+ */
+static int
+mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+{
+ dma_addr_t buf_pa;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ if (!(skb->data && skb->len)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Invalid parameter in %s <%p. len %d>\n",
+ __func__, skb->data, skb->len);
+ return -1;
+ }
+
+ if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE))
+ return -1;
+
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
+
+ /* Write the lower 32bits of the physical address to low command
+ * address scratch register
+ */
+ if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa)) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: failed to write download command to boot code.\n",
+ __func__);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
+ return -1;
+ }
+
+ /* Write the upper 32bits of the physical address to high command
+ * address scratch register
+ */
+ if (mwifiex_write_reg(adapter, reg->cmd_addr_hi,
+ (u32)((u64)buf_pa >> 32))) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: failed to write download command to boot code.\n",
+ __func__);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
+ return -1;
+ }
+
+ /* Write the command length to cmd_size scratch register */
+ if (mwifiex_write_reg(adapter, reg->cmd_size, skb->len)) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: failed to write command len to cmd_size scratch reg\n",
+ __func__);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
+ return -1;
+ }
+
+ /* Ring the door bell */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DOOR_BELL)) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: failed to assert door-bell intr\n", __func__);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* This function init rx port in firmware which in turn enables to receive data
+ * from device before transmitting any packet.
+ */
+static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask;
+
+ /* Write the RX ring read pointer in to reg->rx_rdptr */
+ if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr |
+ tx_wrap)) {
+ mwifiex_dbg(adapter, ERROR,
+ "RECV DATA: failed to write reg->rx_rdptr\n");
+ return -1;
+ }
+ return 0;
+}
+
+/* This function downloads commands to the device
+ */
+static int
+mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ int ret = 0;
+ dma_addr_t cmd_buf_pa, cmdrsp_buf_pa;
+ u8 *payload = (u8 *)skb->data;
+
+ if (!(skb->data && skb->len)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Invalid parameter in %s <%p, %#x>\n",
+ __func__, skb->data, skb->len);
+ return -1;
+ }
+
+ /* Make sure a command response buffer is available */
+ if (!card->cmdrsp_buf) {
+ mwifiex_dbg(adapter, ERROR,
+ "No response buffer available, send command failed\n");
+ return -EBUSY;
+ }
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ mwifiex_pm_wakeup_card(adapter);
+
+ adapter->cmd_sent = true;
+
+ *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len);
+ *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD);
+
+ if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE))
+ return -1;
+
+ card->cmd_buf = skb;
+
+ /* To send a command, the driver will:
+ 1. Write the 64bit physical address of the data buffer to
+ cmd response address low + cmd response address high
+ 2. Ring the door bell (i.e. set the door bell interrupt)
+
+ In response to door bell interrupt, the firmware will perform
+ the DMA of the command packet (first header to obtain the total
+ length and then rest of the command).
+ */
+
+ if (card->cmdrsp_buf) {
+ cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf);
+ /* Write the lower 32bits of the cmdrsp buffer physical
+ address */
+ if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo,
+ (u32)cmdrsp_buf_pa)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to write download cmd to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+ /* Write the upper 32bits of the cmdrsp buffer physical
+ address */
+ if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi,
+ (u32)((u64)cmdrsp_buf_pa >> 32))) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to write download cmd to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+ }
+
+ cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf);
+ /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */
+ if (mwifiex_write_reg(adapter, reg->cmd_addr_lo,
+ (u32)cmd_buf_pa)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to write download cmd to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+ /* Write the upper 32bits of the physical address to reg->cmd_addr_hi */
+ if (mwifiex_write_reg(adapter, reg->cmd_addr_hi,
+ (u32)((u64)cmd_buf_pa >> 32))) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to write download cmd to boot code.\n");
+ ret = -1;
+ goto done;
+ }
+
+ /* Write the command length to reg->cmd_size */
+ if (mwifiex_write_reg(adapter, reg->cmd_size,
+ card->cmd_buf->len)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to write cmd len to reg->cmd_size\n");
+ ret = -1;
+ goto done;
+ }
+
+ /* Ring the door bell */
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_DOOR_BELL)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to assert door-bell intr\n");
+ ret = -1;
+ goto done;
+ }
+
+done:
+ if (ret)
+ adapter->cmd_sent = false;
+
+ return 0;
+}
+
+/*
+ * This function handles command complete interrupt
+ */
+static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ struct sk_buff *skb = card->cmdrsp_buf;
+ int count = 0;
+ u16 rx_len;
+ __le16 pkt_len;
+
+ mwifiex_dbg(adapter, CMD,
+ "info: Rx CMD Response\n");
+
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE);
+
+ /* Unmap the command as a response has been received. */
+ if (card->cmd_buf) {
+ mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
+ PCI_DMA_TODEVICE);
+ card->cmd_buf = NULL;
+ }
+
+ pkt_len = *((__le16 *)skb->data);
+ rx_len = le16_to_cpu(pkt_len);
+ skb_trim(skb, rx_len);
+ skb_pull(skb, INTF_HEADER_LEN);
+
+ if (!adapter->curr_cmd) {
+ if (adapter->ps_state == PS_STATE_SLEEP_CFM) {
+ mwifiex_process_sleep_confirm_resp(adapter, skb->data,
+ skb->len);
+ mwifiex_pcie_enable_host_int(adapter);
+ if (mwifiex_write_reg(adapter,
+ PCIE_CPU_INT_EVENT,
+ CPU_INTR_SLEEP_CFM_DONE)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Write register failed\n");
+ return -1;
+ }
+ mwifiex_delay_for_sleep_cookie(adapter,
+ MWIFIEX_MAX_DELAY_COUNT);
+ while (reg->sleep_cookie && (count++ < 10) &&
+ mwifiex_pcie_ok_to_access_hw(adapter))
+ usleep_range(50, 60);
+ } else {
+ mwifiex_dbg(adapter, ERROR,
+ "There is no command but got cmdrsp\n");
+ }
+ memcpy(adapter->upld_buf, skb->data,
+ min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len));
+ skb_push(skb, INTF_HEADER_LEN);
+ if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
+ PCI_DMA_FROMDEVICE))
+ return -1;
+ } else if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ adapter->curr_cmd->resp_skb = skb;
+ adapter->cmd_resp_received = true;
+ /* Take the pointer and set it to CMD node and will
+ return in the response complete callback */
+ card->cmdrsp_buf = NULL;
+
+ /* Clear the cmd-rsp buffer address in scratch registers. This
+ will prevent firmware from writing to the same response
+ buffer again. */
+ if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cmd_done: failed to clear cmd_rsp_addr_lo\n");
+ return -1;
+ }
+ /* Write the upper 32bits of the cmdrsp buffer physical
+ address */
+ if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cmd_done: failed to clear cmd_rsp_addr_hi\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Command Response processing complete handler
+ */
+static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ if (skb) {
+ card->cmdrsp_buf = skb;
+ skb_push(card->cmdrsp_buf, INTF_HEADER_LEN);
+ if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
+ PCI_DMA_FROMDEVICE))
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function handles firmware event ready interrupt
+ */
+static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
+ u32 wrptr, event;
+ struct mwifiex_evt_buf_desc *desc;
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ mwifiex_pm_wakeup_card(adapter);
+
+ if (adapter->event_received) {
+ mwifiex_dbg(adapter, EVENT,
+ "info: Event being processed,\t"
+ "do not process this interrupt just yet\n");
+ return 0;
+ }
+
+ if (rdptr >= MWIFIEX_MAX_EVT_BD) {
+ mwifiex_dbg(adapter, ERROR,
+ "info: Invalid read pointer...\n");
+ return -1;
+ }
+
+ /* Read the event ring write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) {
+ mwifiex_dbg(adapter, ERROR,
+ "EventReady: failed to read reg->evt_wrptr\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, EVENT,
+ "info: EventReady: Initial <Rd: 0x%x, Wr: 0x%x>",
+ card->evtbd_rdptr, wrptr);
+ if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr
+ & MWIFIEX_EVTBD_MASK)) ||
+ ((wrptr & reg->evt_rollover_ind) ==
+ (card->evtbd_rdptr & reg->evt_rollover_ind))) {
+ struct sk_buff *skb_cmd;
+ __le16 data_len = 0;
+ u16 evt_len;
+
+ mwifiex_dbg(adapter, INFO,
+ "info: Read Index: %d\n", rdptr);
+ skb_cmd = card->evt_buf_list[rdptr];
+ mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE);
+
+ /* Take the pointer and set it to event pointer in adapter
+ and will return back after event handling callback */
+ card->evt_buf_list[rdptr] = NULL;
+ desc = card->evtbd_ring[rdptr];
+ memset(desc, 0, sizeof(*desc));
+
+ event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN];
+ adapter->event_cause = event;
+ /* The first 4bytes will be the event transfer header
+ len is 2 bytes followed by type which is 2 bytes */
+ memcpy(&data_len, skb_cmd->data, sizeof(__le16));
+ evt_len = le16_to_cpu(data_len);
+ skb_trim(skb_cmd, evt_len);
+ skb_pull(skb_cmd, INTF_HEADER_LEN);
+ mwifiex_dbg(adapter, EVENT,
+ "info: Event length: %d\n", evt_len);
+
+ if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE))
+ memcpy(adapter->event_body, skb_cmd->data +
+ MWIFIEX_EVENT_HEADER_LEN, evt_len -
+ MWIFIEX_EVENT_HEADER_LEN);
+
+ adapter->event_received = true;
+ adapter->event_skb = skb_cmd;
+
+ /* Do not update the event read pointer here, wait till the
+ buffer is released. This is just to make things simpler,
+ we need to find a better method of managing these buffers.
+ */
+ } else {
+ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+ CPU_INTR_EVENT_DONE)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Write register failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Event processing complete handler
+ */
+static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ int ret = 0;
+ u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
+ u32 wrptr;
+ struct mwifiex_evt_buf_desc *desc;
+
+ if (!skb)
+ return 0;
+
+ if (rdptr >= MWIFIEX_MAX_EVT_BD) {
+ mwifiex_dbg(adapter, ERROR,
+ "event_complete: Invalid rdptr 0x%x\n",
+ rdptr);
+ return -EINVAL;
+ }
+
+ /* Read the event ring write pointer set by firmware */
+ if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) {
+ mwifiex_dbg(adapter, ERROR,
+ "event_complete: failed to read reg->evt_wrptr\n");
+ return -1;
+ }
+
+ if (!card->evt_buf_list[rdptr]) {
+ skb_push(skb, INTF_HEADER_LEN);
+ skb_put(skb, MAX_EVENT_SIZE - skb->len);
+ if (mwifiex_map_pci_memory(adapter, skb,
+ MAX_EVENT_SIZE,
+ PCI_DMA_FROMDEVICE))
+ return -1;
+ card->evt_buf_list[rdptr] = skb;
+ desc = card->evtbd_ring[rdptr];
+ desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb);
+ desc->len = (u16)skb->len;
+ desc->flags = 0;
+ skb = NULL;
+ } else {
+ mwifiex_dbg(adapter, ERROR,
+ "info: ERROR: buf still valid at index %d, <%p, %p>\n",
+ rdptr, card->evt_buf_list[rdptr], skb);
+ }
+
+ if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) {
+ card->evtbd_rdptr = ((card->evtbd_rdptr &
+ reg->evt_rollover_ind) ^
+ reg->evt_rollover_ind);
+ }
+
+ mwifiex_dbg(adapter, EVENT,
+ "info: Updated <Rd: 0x%x, Wr: 0x%x>",
+ card->evtbd_rdptr, wrptr);
+
+ /* Write the event ring read pointer in to reg->evt_rdptr */
+ if (mwifiex_write_reg(adapter, reg->evt_rdptr,
+ card->evtbd_rdptr)) {
+ mwifiex_dbg(adapter, ERROR,
+ "event_complete: failed to read reg->evt_rdptr\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, EVENT,
+ "info: Check Events Again\n");
+ ret = mwifiex_pcie_process_event_ready(adapter);
+
+ return ret;
+}
+
+/*
+ * This function downloads the firmware to the card.
+ *
+ * Firmware is downloaded to the card in blocks. Every block download
+ * is tested for CRC errors, and retried a number of times before
+ * returning failure.
+ */
+static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
+ struct mwifiex_fw_image *fw)
+{
+ int ret;
+ u8 *firmware = fw->fw_buf;
+ u32 firmware_len = fw->fw_len;
+ u32 offset = 0;
+ struct sk_buff *skb;
+ u32 txlen, tx_blocks = 0, tries, len;
+ u32 block_retry_cnt = 0;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ if (!firmware || !firmware_len) {
+ mwifiex_dbg(adapter, ERROR,
+ "No firmware image found! Terminating download\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "info: Downloading FW image (%d bytes)\n",
+ firmware_len);
+
+ if (mwifiex_pcie_disable_host_int(adapter)) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: Disabling interrupts failed.\n", __func__);
+ return -1;
+ }
+
+ skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Perform firmware data transfer */
+ do {
+ u32 ireg_intr = 0;
+
+ /* More data? */
+ if (offset >= firmware_len)
+ break;
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ret = mwifiex_read_reg(adapter, reg->cmd_size,
+ &len);
+ if (ret) {
+ mwifiex_dbg(adapter, FATAL,
+ "Failed reading len from boot code\n");
+ goto done;
+ }
+ if (len)
+ break;
+ usleep_range(10, 20);
+ }
+
+ if (!len) {
+ break;
+ } else if (len > MWIFIEX_UPLD_SIZE) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW download failure @ %d, invalid length %d\n",
+ offset, len);
+ ret = -1;
+ goto done;
+ }
+
+ txlen = len;
+
+ if (len & BIT(0)) {
+ block_retry_cnt++;
+ if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW download failure @ %d, over max\t"
+ "retry count\n", offset);
+ ret = -1;
+ goto done;
+ }
+ mwifiex_dbg(adapter, ERROR,
+ "FW CRC error indicated by the\t"
+ "helper: len = 0x%04X, txlen = %d\n",
+ len, txlen);
+ len &= ~BIT(0);
+ /* Setting this to 0 to resend from same offset */
+ txlen = 0;
+ } else {
+ block_retry_cnt = 0;
+ /* Set blocksize to transfer - checking for
+ last block */
+ if (firmware_len - offset < txlen)
+ txlen = firmware_len - offset;
+
+ mwifiex_dbg(adapter, INFO, ".");
+
+ tx_blocks = (txlen + card->pcie.blksz_fw_dl - 1) /
+ card->pcie.blksz_fw_dl;
+
+ /* Copy payload to buffer */
+ memmove(skb->data, &firmware[offset], txlen);
+ }
+
+ skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len);
+ skb_trim(skb, tx_blocks * card->pcie.blksz_fw_dl);
+
+ /* Send the boot command to device */
+ if (mwifiex_pcie_send_boot_cmd(adapter, skb)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to send firmware download command\n");
+ ret = -1;
+ goto done;
+ }
+
+ /* Wait for the command done interrupt */
+ do {
+ if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS,
+ &ireg_intr)) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: Failed to read\t"
+ "interrupt status during fw dnld.\n",
+ __func__);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
+ ret = -1;
+ goto done;
+ }
+ } while ((ireg_intr & CPU_INTR_DOOR_BELL) ==
+ CPU_INTR_DOOR_BELL);
+
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
+
+ offset += txlen;
+ } while (true);
+
+ mwifiex_dbg(adapter, MSG,
+ "info: FW download over, size %d bytes\n", offset);
+
+ ret = 0;
+
+done:
+ dev_kfree_skb_any(skb);
+ return ret;
+}
+
+/*
+ * This function checks the firmware status in card.
+ *
+ * The winner interface is also determined by this function.
+ */
+static int
+mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num)
+{
+ int ret = 0;
+ u32 firmware_stat, winner_status;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ u32 tries;
+
+ /* Mask spurios interrupts */
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK,
+ HOST_INTR_MASK)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Write register failed\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "Setting driver ready signature\n");
+ if (mwifiex_write_reg(adapter, reg->drv_rdy,
+ FIRMWARE_READY_PCIE)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to write driver ready signature\n");
+ return -1;
+ }
+
+ /* Wait for firmware initialization event */
+ for (tries = 0; tries < poll_num; tries++) {
+ if (mwifiex_read_reg(adapter, reg->fw_status,
+ &firmware_stat))
+ ret = -1;
+ else
+ ret = 0;
+ if (ret)
+ continue;
+ if (firmware_stat == FIRMWARE_READY_PCIE) {
+ ret = 0;
+ break;
+ } else {
+ msleep(100);
+ ret = -1;
+ }
+ }
+
+ if (ret) {
+ if (mwifiex_read_reg(adapter, reg->fw_status,
+ &winner_status))
+ ret = -1;
+ else if (!winner_status) {
+ mwifiex_dbg(adapter, INFO,
+ "PCI-E is the winner\n");
+ adapter->winner = 1;
+ } else {
+ mwifiex_dbg(adapter, ERROR,
+ "PCI-E is not the winner <%#x,%d>, exit dnld\n",
+ ret, adapter->winner);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * This function reads the interrupt status from card.
+ */
+static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
+{
+ u32 pcie_ireg;
+ unsigned long flags;
+
+ if (!mwifiex_pcie_ok_to_access_hw(adapter))
+ return;
+
+ if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) {
+ mwifiex_dbg(adapter, ERROR, "Read register failed\n");
+ return;
+ }
+
+ if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) {
+
+ mwifiex_pcie_disable_host_int(adapter);
+
+ /* Clear the pending interrupts */
+ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS,
+ ~pcie_ireg)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Write register failed\n");
+ return;
+ }
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ adapter->int_status |= pcie_ireg;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+
+ if (!adapter->pps_uapsd_mode &&
+ adapter->ps_state == PS_STATE_SLEEP &&
+ mwifiex_pcie_ok_to_access_hw(adapter)) {
+ /* Potentially for PCIe we could get other
+ * interrupts like shared. Don't change power
+ * state until cookie is set */
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->pm_wakeup_fw_try = false;
+ del_timer(&adapter->wakeup_timer);
+ }
+ }
+}
+
+/*
+ * Interrupt handler for PCIe root port
+ *
+ * This function reads the interrupt status from firmware and assigns
+ * the main process in workqueue which will handle the interrupt.
+ */
+static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context)
+{
+ struct pci_dev *pdev = (struct pci_dev *)context;
+ struct pcie_service_card *card;
+ struct mwifiex_adapter *adapter;
+
+ if (!pdev) {
+ pr_err("info: %s: pdev is NULL\n", __func__);
+ goto exit;
+ }
+
+ card = pci_get_drvdata(pdev);
+ if (!card || !card->adapter) {
+ pr_err("info: %s: card=%p adapter=%p\n", __func__, card,
+ card ? card->adapter : NULL);
+ goto exit;
+ }
+ adapter = card->adapter;
+
+ if (adapter->surprise_removed)
+ goto exit;
+
+ mwifiex_interrupt_status(adapter);
+ mwifiex_queue_main_work(adapter);
+
+exit:
+ return IRQ_HANDLED;
+}
+
+/*
+ * This function checks the current interrupt status.
+ *
+ * The following interrupts are checked and handled by this function -
+ * - Data sent
+ * - Command sent
+ * - Command received
+ * - Packets received
+ * - Events received
+ *
+ * In case of Rx packets received, the packets are uploaded from card to
+ * host and processed accordingly.
+ */
+static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ u32 pcie_ireg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ /* Clear out unused interrupts */
+ pcie_ireg = adapter->int_status;
+ adapter->int_status = 0;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+
+ while (pcie_ireg & HOST_INTR_MASK) {
+ if (pcie_ireg & HOST_INTR_DNLD_DONE) {
+ pcie_ireg &= ~HOST_INTR_DNLD_DONE;
+ mwifiex_dbg(adapter, INTR,
+ "info: TX DNLD Done\n");
+ ret = mwifiex_pcie_send_data_complete(adapter);
+ if (ret)
+ return ret;
+ }
+ if (pcie_ireg & HOST_INTR_UPLD_RDY) {
+ pcie_ireg &= ~HOST_INTR_UPLD_RDY;
+ mwifiex_dbg(adapter, INTR,
+ "info: Rx DATA\n");
+ ret = mwifiex_pcie_process_recv_data(adapter);
+ if (ret)
+ return ret;
+ }
+ if (pcie_ireg & HOST_INTR_EVENT_RDY) {
+ pcie_ireg &= ~HOST_INTR_EVENT_RDY;
+ mwifiex_dbg(adapter, INTR,
+ "info: Rx EVENT\n");
+ ret = mwifiex_pcie_process_event_ready(adapter);
+ if (ret)
+ return ret;
+ }
+
+ if (pcie_ireg & HOST_INTR_CMD_DONE) {
+ pcie_ireg &= ~HOST_INTR_CMD_DONE;
+ if (adapter->cmd_sent) {
+ mwifiex_dbg(adapter, INTR,
+ "info: CMD sent Interrupt\n");
+ adapter->cmd_sent = false;
+ }
+ /* Handle command response */
+ ret = mwifiex_pcie_process_cmd_complete(adapter);
+ if (ret)
+ return ret;
+ }
+
+ if (mwifiex_pcie_ok_to_access_hw(adapter)) {
+ if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS,
+ &pcie_ireg)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Read register failed\n");
+ return -1;
+ }
+
+ if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) {
+ if (mwifiex_write_reg(adapter,
+ PCIE_HOST_INT_STATUS,
+ ~pcie_ireg)) {
+ mwifiex_dbg(adapter, ERROR,
+ "Write register failed\n");
+ return -1;
+ }
+ }
+
+ }
+ }
+ mwifiex_dbg(adapter, INTR,
+ "info: cmd_sent=%d data_sent=%d\n",
+ adapter->cmd_sent, adapter->data_sent);
+ if (adapter->ps_state != PS_STATE_SLEEP)
+ mwifiex_pcie_enable_host_int(adapter);
+
+ return 0;
+}
+
+/*
+ * This function downloads data from driver to card.
+ *
+ * Both commands and data packets are transferred to the card by this
+ * function.
+ *
+ * This function adds the PCIE specific header to the front of the buffer
+ * before transferring. The header contains the length of the packet and
+ * the type. The firmware handles the packets based upon this set type.
+ */
+static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type,
+ struct sk_buff *skb,
+ struct mwifiex_tx_param *tx_param)
+{
+ if (!skb) {
+ mwifiex_dbg(adapter, ERROR,
+ "Passed NULL skb to %s\n", __func__);
+ return -1;
+ }
+
+ if (type == MWIFIEX_TYPE_DATA)
+ return mwifiex_pcie_send_data(adapter, skb, tx_param);
+ else if (type == MWIFIEX_TYPE_CMD)
+ return mwifiex_pcie_send_cmd(adapter, skb);
+
+ return 0;
+}
+
+/* This function read/write firmware */
+static enum rdwr_status
+mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag)
+{
+ int ret, tries;
+ u8 ctrl_data;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "PCIE write err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data);
+ if (ctrl_data == FW_DUMP_DONE)
+ return RDWR_STATUS_SUCCESS;
+ if (doneflag && ctrl_data == doneflag)
+ return RDWR_STATUS_DONE;
+ if (ctrl_data != FW_DUMP_HOST_READY) {
+ mwifiex_dbg(adapter, WARN,
+ "The ctrl reg was changed, re-try again!\n");
+ ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl,
+ FW_DUMP_HOST_READY);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "PCIE write err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ }
+ usleep_range(100, 200);
+ }
+
+ mwifiex_dbg(adapter, ERROR, "Fail to pull ctrl_data\n");
+ return RDWR_STATUS_FAILURE;
+}
+
+/* This function dump firmware memory to file */
+static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *creg = card->pcie.reg;
+ unsigned int reg, reg_start, reg_end;
+ u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
+ enum rdwr_status stat;
+ u32 memory_size;
+ int ret;
+
+ if (!card->pcie.can_dump_fw)
+ return;
+
+ for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump start ==\n");
+
+ /* Read the number of the memories which will dump */
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ return;
+
+ reg = creg->fw_dump_start;
+ mwifiex_read_reg_byte(adapter, reg, &dump_num);
+
+ /* Read the length of every memory which will dump */
+ for (idx = 0; idx < dump_num; idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ return;
+
+ memory_size = 0;
+ reg = creg->fw_dump_start;
+ for (i = 0; i < 4; i++) {
+ mwifiex_read_reg_byte(adapter, reg, &read_reg);
+ memory_size |= (read_reg << (i * 8));
+ reg++;
+ }
+
+ if (memory_size == 0) {
+ mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n");
+ ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl,
+ FW_DUMP_READ_DONE);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "PCIE write err\n");
+ return;
+ }
+ break;
+ }
+
+ mwifiex_dbg(adapter, DUMP,
+ "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
+ entry->mem_ptr = vmalloc(memory_size + 1);
+ entry->mem_size = memory_size;
+ if (!entry->mem_ptr) {
+ mwifiex_dbg(adapter, ERROR,
+ "Vmalloc %s failed\n", entry->mem_name);
+ return;
+ }
+ dbg_ptr = entry->mem_ptr;
+ end_ptr = dbg_ptr + memory_size;
+
+ doneflag = entry->done_flag;
+ mwifiex_dbg(adapter, DUMP, "Start %s output, please wait...\n",
+ entry->mem_name);
+
+ do {
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+ if (RDWR_STATUS_FAILURE == stat)
+ return;
+
+ reg_start = creg->fw_dump_start;
+ reg_end = creg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ mwifiex_read_reg_byte(adapter, reg, dbg_ptr);
+ if (dbg_ptr < end_ptr) {
+ dbg_ptr++;
+ } else {
+ mwifiex_dbg(adapter, ERROR,
+ "Allocated buf not enough\n");
+ return;
+ }
+ }
+
+ if (stat != RDWR_STATUS_DONE)
+ continue;
+
+ mwifiex_dbg(adapter, DUMP,
+ "%s done: size=0x%tx\n",
+ entry->mem_name, dbg_ptr - entry->mem_ptr);
+ break;
+ } while (true);
+ }
+ mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump end ==\n");
+}
+
+static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter)
+{
+ mwifiex_drv_info_dump(adapter);
+ mwifiex_pcie_fw_dump(adapter);
+ mwifiex_upload_device_dump(adapter);
+}
+
+static unsigned long iface_work_flags;
+static struct mwifiex_adapter *save_adapter;
+static void mwifiex_pcie_work(struct work_struct *work)
+{
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP,
+ &iface_work_flags))
+ mwifiex_pcie_device_dump_work(save_adapter);
+}
+
+static DECLARE_WORK(pcie_work, mwifiex_pcie_work);
+/* This function dumps FW information */
+static void mwifiex_pcie_device_dump(struct mwifiex_adapter *adapter)
+{
+ save_adapter = adapter;
+ if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags))
+ return;
+
+ set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags);
+
+ schedule_work(&pcie_work);
+}
+
+/*
+ * This function initializes the PCI-E host memory space, WCB rings, etc.
+ *
+ * The following initializations steps are followed -
+ * - Allocate TXBD ring buffers
+ * - Allocate RXBD ring buffers
+ * - Allocate event BD ring buffers
+ * - Allocate command response ring buffer
+ * - Allocate sleep cookie buffer
+ */
+static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ int ret;
+ struct pci_dev *pdev = card->dev;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ pci_set_drvdata(pdev, card);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto err_enable_dev;
+
+ pci_set_master(pdev);
+
+ pr_notice("try set_consistent_dma_mask(32)\n");
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ pr_err("set_dma_mask(32) failed\n");
+ goto err_set_dma_mask;
+ }
+
+ ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ pr_err("set_consistent_dma_mask(64) failed\n");
+ goto err_set_dma_mask;
+ }
+
+ ret = pci_request_region(pdev, 0, DRV_NAME);
+ if (ret) {
+ pr_err("req_reg(0) error\n");
+ goto err_req_region0;
+ }
+ card->pci_mmap = pci_iomap(pdev, 0, 0);
+ if (!card->pci_mmap) {
+ pr_err("iomap(0) error\n");
+ ret = -EIO;
+ goto err_iomap0;
+ }
+ ret = pci_request_region(pdev, 2, DRV_NAME);
+ if (ret) {
+ pr_err("req_reg(2) error\n");
+ goto err_req_region2;
+ }
+ card->pci_mmap1 = pci_iomap(pdev, 2, 0);
+ if (!card->pci_mmap1) {
+ pr_err("iomap(2) error\n");
+ ret = -EIO;
+ goto err_iomap2;
+ }
+
+ pr_notice("PCI memory map Virt0: %p PCI memory map Virt2: %p\n",
+ card->pci_mmap, card->pci_mmap1);
+
+ card->cmdrsp_buf = NULL;
+ ret = mwifiex_pcie_create_txbd_ring(adapter);
+ if (ret)
+ goto err_cre_txbd;
+ ret = mwifiex_pcie_create_rxbd_ring(adapter);
+ if (ret)
+ goto err_cre_rxbd;
+ ret = mwifiex_pcie_create_evtbd_ring(adapter);
+ if (ret)
+ goto err_cre_evtbd;
+ ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter);
+ if (ret)
+ goto err_alloc_cmdbuf;
+ if (reg->sleep_cookie) {
+ ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter);
+ if (ret)
+ goto err_alloc_cookie;
+ } else {
+ card->sleep_cookie_vbase = NULL;
+ }
+ return ret;
+
+err_alloc_cookie:
+ mwifiex_pcie_delete_cmdrsp_buf(adapter);
+err_alloc_cmdbuf:
+ mwifiex_pcie_delete_evtbd_ring(adapter);
+err_cre_evtbd:
+ mwifiex_pcie_delete_rxbd_ring(adapter);
+err_cre_rxbd:
+ mwifiex_pcie_delete_txbd_ring(adapter);
+err_cre_txbd:
+ pci_iounmap(pdev, card->pci_mmap1);
+err_iomap2:
+ pci_release_region(pdev, 2);
+err_req_region2:
+ pci_iounmap(pdev, card->pci_mmap);
+err_iomap0:
+ pci_release_region(pdev, 0);
+err_req_region0:
+err_set_dma_mask:
+ pci_disable_device(pdev);
+err_enable_dev:
+ pci_set_drvdata(pdev, NULL);
+ return ret;
+}
+
+/*
+ * This function cleans up the allocated card buffers.
+ *
+ * The following are freed by this function -
+ * - TXBD ring buffers
+ * - RXBD ring buffers
+ * - Event BD ring buffers
+ * - Command response ring buffer
+ * - Sleep cookie buffer
+ */
+static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct pci_dev *pdev = card->dev;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ if (user_rmmod) {
+ mwifiex_dbg(adapter, INFO,
+ "Clearing driver ready signature\n");
+ if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000))
+ mwifiex_dbg(adapter, ERROR,
+ "Failed to write driver not-ready signature\n");
+ }
+
+ if (pdev) {
+ pci_iounmap(pdev, card->pci_mmap);
+ pci_iounmap(pdev, card->pci_mmap1);
+ pci_disable_device(pdev);
+ pci_release_region(pdev, 2);
+ pci_release_region(pdev, 0);
+ pci_set_drvdata(pdev, NULL);
+ }
+ kfree(card);
+}
+
+static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ struct pcie_service_card *card = adapter->card;
+ struct pci_dev *pdev = card->dev;
+
+ if (pci_enable_msi(pdev) != 0)
+ pci_disable_msi(pdev);
+ else
+ card->msi_enable = 1;
+
+ mwifiex_dbg(adapter, INFO, "msi_enable = %d\n", card->msi_enable);
+
+ ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED,
+ "MRVL_PCIE", pdev);
+ if (ret) {
+ pr_err("request_irq failed: ret=%d\n", ret);
+ adapter->card = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This function registers the PCIE device.
+ *
+ * PCIE IRQ is claimed, block size is set and driver data is initialized.
+ */
+static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct pci_dev *pdev = card->dev;
+
+ /* save adapter pointer in card */
+ card->adapter = adapter;
+ adapter->dev = &pdev->dev;
+
+ if (mwifiex_pcie_request_irq(adapter))
+ return -1;
+
+ adapter->tx_buf_size = card->pcie.tx_buf_size;
+ adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+ adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
+ strcpy(adapter->fw_name, card->pcie.firmware);
+ adapter->ext_scan = card->pcie.can_ext_scan;
+
+ return 0;
+}
+
+/*
+ * This function unregisters the PCIE device.
+ *
+ * The PCIE IRQ is released, the function is disabled and driver
+ * data is set to null.
+ */
+static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg;
+
+ if (card) {
+ mwifiex_dbg(adapter, INFO,
+ "%s(): calling free_irq()\n", __func__);
+ free_irq(card->dev->irq, card->dev);
+
+ reg = card->pcie.reg;
+ if (reg->sleep_cookie)
+ mwifiex_pcie_delete_sleep_cookie_buf(adapter);
+
+ mwifiex_pcie_delete_cmdrsp_buf(adapter);
+ mwifiex_pcie_delete_evtbd_ring(adapter);
+ mwifiex_pcie_delete_rxbd_ring(adapter);
+ mwifiex_pcie_delete_txbd_ring(adapter);
+ card->cmdrsp_buf = NULL;
+ }
+}
+
+static struct mwifiex_if_ops pcie_ops = {
+ .init_if = mwifiex_pcie_init,
+ .cleanup_if = mwifiex_pcie_cleanup,
+ .check_fw_status = mwifiex_check_fw_status,
+ .prog_fw = mwifiex_prog_fw_w_helper,
+ .register_dev = mwifiex_register_dev,
+ .unregister_dev = mwifiex_unregister_dev,
+ .enable_int = mwifiex_pcie_enable_host_int,
+ .process_int_status = mwifiex_process_int_status,
+ .host_to_card = mwifiex_pcie_host_to_card,
+ .wakeup = mwifiex_pm_wakeup_card,
+ .wakeup_complete = mwifiex_pm_wakeup_card_complete,
+
+ /* PCIE specific */
+ .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete,
+ .event_complete = mwifiex_pcie_event_complete,
+ .update_mp_end_port = NULL,
+ .cleanup_mpa_buf = NULL,
+ .init_fw_port = mwifiex_pcie_init_fw_port,
+ .clean_pcie_ring = mwifiex_clean_pcie_ring_buf,
+ .device_dump = mwifiex_pcie_device_dump,
+};
+
+/*
+ * This function initializes the PCIE driver module.
+ *
+ * This initiates the semaphore and registers the device with
+ * PCIE bus.
+ */
+static int mwifiex_pcie_init_module(void)
+{
+ int ret;
+
+ pr_debug("Marvell PCIe Driver\n");
+
+ sema_init(&add_remove_card_sem, 1);
+
+ /* Clear the flag in case user removes the card. */
+ user_rmmod = 0;
+
+ ret = pci_register_driver(&mwifiex_pcie);
+ if (ret)
+ pr_err("Driver register failed!\n");
+ else
+ pr_debug("info: Driver registered successfully!\n");
+
+ return ret;
+}
+
+/*
+ * This function cleans up the PCIE driver.
+ *
+ * The following major steps are followed for cleanup -
+ * - Resume the device if its suspended
+ * - Disconnect the device if connected
+ * - Shutdown the firmware
+ * - Unregister the device from PCIE bus.
+ */
+static void mwifiex_pcie_cleanup_module(void)
+{
+ if (!down_interruptible(&add_remove_card_sem))
+ up(&add_remove_card_sem);
+
+ /* Set the flag as user is removing this module. */
+ user_rmmod = 1;
+
+ cancel_work_sync(&pcie_work);
+ pci_unregister_driver(&mwifiex_pcie);
+}
+
+module_init(mwifiex_pcie_init_module);
+module_exit(mwifiex_pcie_cleanup_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION);
+MODULE_VERSION(PCIE_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(PCIE8766_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(PCIE8897_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(PCIE8997_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h
new file mode 100644
index 000000000000..6fc28737b576
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.h
@@ -0,0 +1,384 @@
+/* @file mwifiex_pcie.h
+ *
+ * @brief This file contains definitions for PCI-E interface.
+ * driver.
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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.
+ */
+
+#ifndef _MWIFIEX_PCIE_H
+#define _MWIFIEX_PCIE_H
+
+#include <linux/pci.h>
+#include <linux/pcieport_if.h>
+#include <linux/interrupt.h>
+
+#include "main.h"
+
+#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin"
+#define PCIE8897_DEFAULT_FW_NAME "mrvl/pcie8897_uapsta.bin"
+#define PCIE8997_DEFAULT_FW_NAME "mrvl/pcie8997_uapsta.bin"
+
+#define PCIE_VENDOR_ID_MARVELL (0x11ab)
+#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30)
+#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38)
+#define PCIE_DEVICE_ID_MARVELL_88W8997 (0x2b42)
+
+/* Constants for Buffer Descriptor (BD) rings */
+#define MWIFIEX_MAX_TXRX_BD 0x20
+#define MWIFIEX_TXBD_MASK 0x3F
+#define MWIFIEX_RXBD_MASK 0x3F
+
+#define MWIFIEX_MAX_EVT_BD 0x08
+#define MWIFIEX_EVTBD_MASK 0x0f
+
+/* PCIE INTERNAL REGISTERS */
+#define PCIE_SCRATCH_0_REG 0xC10
+#define PCIE_SCRATCH_1_REG 0xC14
+#define PCIE_CPU_INT_EVENT 0xC18
+#define PCIE_CPU_INT_STATUS 0xC1C
+#define PCIE_HOST_INT_STATUS 0xC30
+#define PCIE_HOST_INT_MASK 0xC34
+#define PCIE_HOST_INT_STATUS_MASK 0xC3C
+#define PCIE_SCRATCH_2_REG 0xC40
+#define PCIE_SCRATCH_3_REG 0xC44
+#define PCIE_SCRATCH_4_REG 0xCD0
+#define PCIE_SCRATCH_5_REG 0xCD4
+#define PCIE_SCRATCH_6_REG 0xCD8
+#define PCIE_SCRATCH_7_REG 0xCDC
+#define PCIE_SCRATCH_8_REG 0xCE0
+#define PCIE_SCRATCH_9_REG 0xCE4
+#define PCIE_SCRATCH_10_REG 0xCE8
+#define PCIE_SCRATCH_11_REG 0xCEC
+#define PCIE_SCRATCH_12_REG 0xCF0
+#define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C
+#define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C
+
+#define CPU_INTR_DNLD_RDY BIT(0)
+#define CPU_INTR_DOOR_BELL BIT(1)
+#define CPU_INTR_SLEEP_CFM_DONE BIT(2)
+#define CPU_INTR_RESET BIT(3)
+#define CPU_INTR_EVENT_DONE BIT(5)
+
+#define HOST_INTR_DNLD_DONE BIT(0)
+#define HOST_INTR_UPLD_RDY BIT(1)
+#define HOST_INTR_CMD_DONE BIT(2)
+#define HOST_INTR_EVENT_RDY BIT(3)
+#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \
+ HOST_INTR_UPLD_RDY | \
+ HOST_INTR_CMD_DONE | \
+ HOST_INTR_EVENT_RDY)
+
+#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7)
+#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0)
+#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1)
+#define MWIFIEX_BD_FLAG_SOP BIT(0)
+#define MWIFIEX_BD_FLAG_EOP BIT(1)
+#define MWIFIEX_BD_FLAG_XS_SOP BIT(2)
+#define MWIFIEX_BD_FLAG_XS_EOP BIT(3)
+#define MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND BIT(7)
+#define MWIFIEX_BD_FLAG_RX_ROLLOVER_IND BIT(10)
+#define MWIFIEX_BD_FLAG_TX_START_PTR BIT(16)
+#define MWIFIEX_BD_FLAG_TX_ROLLOVER_IND BIT(26)
+
+/* Max retry number of command write */
+#define MAX_WRITE_IOMEM_RETRY 2
+/* Define PCIE block size for firmware download */
+#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256
+/* FW awake cookie after FW ready */
+#define FW_AWAKE_COOKIE (0xAA55AA55)
+#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF
+#define MWIFIEX_MAX_DELAY_COUNT 5
+
+struct mwifiex_pcie_card_reg {
+ u16 cmd_addr_lo;
+ u16 cmd_addr_hi;
+ u16 fw_status;
+ u16 cmd_size;
+ u16 cmdrsp_addr_lo;
+ u16 cmdrsp_addr_hi;
+ u16 tx_rdptr;
+ u16 tx_wrptr;
+ u16 rx_rdptr;
+ u16 rx_wrptr;
+ u16 evt_rdptr;
+ u16 evt_wrptr;
+ u16 drv_rdy;
+ u16 tx_start_ptr;
+ u32 tx_mask;
+ u32 tx_wrap_mask;
+ u32 rx_mask;
+ u32 rx_wrap_mask;
+ u32 tx_rollover_ind;
+ u32 rx_rollover_ind;
+ u32 evt_rollover_ind;
+ u8 ring_flag_sop;
+ u8 ring_flag_eop;
+ u8 ring_flag_xs_sop;
+ u8 ring_flag_xs_eop;
+ u32 ring_tx_start_ptr;
+ u8 pfu_enabled;
+ u8 sleep_cookie;
+ u16 fw_dump_ctrl;
+ u16 fw_dump_start;
+ u16 fw_dump_end;
+};
+
+static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = {
+ .cmd_addr_lo = PCIE_SCRATCH_0_REG,
+ .cmd_addr_hi = PCIE_SCRATCH_1_REG,
+ .cmd_size = PCIE_SCRATCH_2_REG,
+ .fw_status = PCIE_SCRATCH_3_REG,
+ .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
+ .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
+ .tx_rdptr = PCIE_SCRATCH_6_REG,
+ .tx_wrptr = PCIE_SCRATCH_7_REG,
+ .rx_rdptr = PCIE_SCRATCH_8_REG,
+ .rx_wrptr = PCIE_SCRATCH_9_REG,
+ .evt_rdptr = PCIE_SCRATCH_10_REG,
+ .evt_wrptr = PCIE_SCRATCH_11_REG,
+ .drv_rdy = PCIE_SCRATCH_12_REG,
+ .tx_start_ptr = 0,
+ .tx_mask = MWIFIEX_TXBD_MASK,
+ .tx_wrap_mask = 0,
+ .rx_mask = MWIFIEX_RXBD_MASK,
+ .rx_wrap_mask = 0,
+ .tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
+ .rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
+ .evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
+ .ring_flag_sop = 0,
+ .ring_flag_eop = 0,
+ .ring_flag_xs_sop = 0,
+ .ring_flag_xs_eop = 0,
+ .ring_tx_start_ptr = 0,
+ .pfu_enabled = 0,
+ .sleep_cookie = 1,
+};
+
+static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = {
+ .cmd_addr_lo = PCIE_SCRATCH_0_REG,
+ .cmd_addr_hi = PCIE_SCRATCH_1_REG,
+ .cmd_size = PCIE_SCRATCH_2_REG,
+ .fw_status = PCIE_SCRATCH_3_REG,
+ .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
+ .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
+ .tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1,
+ .tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1,
+ .rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1,
+ .rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1,
+ .evt_rdptr = PCIE_SCRATCH_10_REG,
+ .evt_wrptr = PCIE_SCRATCH_11_REG,
+ .drv_rdy = PCIE_SCRATCH_12_REG,
+ .tx_start_ptr = 16,
+ .tx_mask = 0x03FF0000,
+ .tx_wrap_mask = 0x07FF0000,
+ .rx_mask = 0x000003FF,
+ .rx_wrap_mask = 0x000007FF,
+ .tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND,
+ .rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND,
+ .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND,
+ .ring_flag_sop = MWIFIEX_BD_FLAG_SOP,
+ .ring_flag_eop = MWIFIEX_BD_FLAG_EOP,
+ .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP,
+ .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP,
+ .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR,
+ .pfu_enabled = 1,
+ .sleep_cookie = 0,
+ .fw_dump_ctrl = 0xcf4,
+ .fw_dump_start = 0xcf8,
+ .fw_dump_end = 0xcff,
+};
+
+static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = {
+ .cmd_addr_lo = PCIE_SCRATCH_0_REG,
+ .cmd_addr_hi = PCIE_SCRATCH_1_REG,
+ .cmd_size = PCIE_SCRATCH_2_REG,
+ .fw_status = PCIE_SCRATCH_3_REG,
+ .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
+ .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
+ .tx_rdptr = 0xC1A4,
+ .tx_wrptr = 0xC174,
+ .rx_rdptr = 0xC174,
+ .rx_wrptr = 0xC1A4,
+ .evt_rdptr = PCIE_SCRATCH_10_REG,
+ .evt_wrptr = PCIE_SCRATCH_11_REG,
+ .drv_rdy = PCIE_SCRATCH_12_REG,
+ .tx_start_ptr = 16,
+ .tx_mask = 0x0FFF0000,
+ .tx_wrap_mask = 0x1FFF0000,
+ .rx_mask = 0x00000FFF,
+ .rx_wrap_mask = 0x00001FFF,
+ .tx_rollover_ind = BIT(28),
+ .rx_rollover_ind = BIT(12),
+ .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND,
+ .ring_flag_sop = MWIFIEX_BD_FLAG_SOP,
+ .ring_flag_eop = MWIFIEX_BD_FLAG_EOP,
+ .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP,
+ .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP,
+ .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR,
+ .pfu_enabled = 1,
+ .sleep_cookie = 0,
+};
+
+struct mwifiex_pcie_device {
+ const char *firmware;
+ const struct mwifiex_pcie_card_reg *reg;
+ u16 blksz_fw_dl;
+ u16 tx_buf_size;
+ bool can_dump_fw;
+ bool can_ext_scan;
+};
+
+static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
+ .firmware = PCIE8766_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_8766,
+ .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+ .can_dump_fw = false,
+ .can_ext_scan = true,
+};
+
+static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
+ .firmware = PCIE8897_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_8897,
+ .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+ .can_dump_fw = true,
+ .can_ext_scan = true,
+};
+
+static const struct mwifiex_pcie_device mwifiex_pcie8997 = {
+ .firmware = PCIE8997_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_8997,
+ .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+ .can_dump_fw = false,
+ .can_ext_scan = true,
+};
+
+struct mwifiex_evt_buf_desc {
+ u64 paddr;
+ u16 len;
+ u16 flags;
+} __packed;
+
+struct mwifiex_pcie_buf_desc {
+ u64 paddr;
+ u16 len;
+ u16 flags;
+} __packed;
+
+struct mwifiex_pfu_buf_desc {
+ u16 flags;
+ u16 offset;
+ u16 frag_len;
+ u16 len;
+ u64 paddr;
+ u32 reserved;
+} __packed;
+
+struct pcie_service_card {
+ struct pci_dev *dev;
+ struct mwifiex_adapter *adapter;
+ struct mwifiex_pcie_device pcie;
+
+ u8 txbd_flush;
+ u32 txbd_wrptr;
+ u32 txbd_rdptr;
+ u32 txbd_ring_size;
+ u8 *txbd_ring_vbase;
+ dma_addr_t txbd_ring_pbase;
+ void *txbd_ring[MWIFIEX_MAX_TXRX_BD];
+ struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD];
+
+ u32 rxbd_wrptr;
+ u32 rxbd_rdptr;
+ u32 rxbd_ring_size;
+ u8 *rxbd_ring_vbase;
+ dma_addr_t rxbd_ring_pbase;
+ void *rxbd_ring[MWIFIEX_MAX_TXRX_BD];
+ struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD];
+
+ u32 evtbd_wrptr;
+ u32 evtbd_rdptr;
+ u32 evtbd_ring_size;
+ u8 *evtbd_ring_vbase;
+ dma_addr_t evtbd_ring_pbase;
+ void *evtbd_ring[MWIFIEX_MAX_EVT_BD];
+ struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD];
+
+ struct sk_buff *cmd_buf;
+ struct sk_buff *cmdrsp_buf;
+ u8 *sleep_cookie_vbase;
+ dma_addr_t sleep_cookie_pbase;
+ void __iomem *pci_mmap;
+ void __iomem *pci_mmap1;
+ int msi_enable;
+};
+
+static inline int
+mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr)
+{
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ switch (card->dev->device) {
+ case PCIE_DEVICE_ID_MARVELL_88W8766P:
+ if (((card->txbd_wrptr & reg->tx_mask) ==
+ (rdptr & reg->tx_mask)) &&
+ ((card->txbd_wrptr & reg->tx_rollover_ind) !=
+ (rdptr & reg->tx_rollover_ind)))
+ return 1;
+ break;
+ case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
+ if (((card->txbd_wrptr & reg->tx_mask) ==
+ (rdptr & reg->tx_mask)) &&
+ ((card->txbd_wrptr & reg->tx_rollover_ind) ==
+ (rdptr & reg->tx_rollover_ind)))
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+static inline int
+mwifiex_pcie_txbd_not_full(struct pcie_service_card *card)
+{
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ switch (card->dev->device) {
+ case PCIE_DEVICE_ID_MARVELL_88W8766P:
+ if (((card->txbd_wrptr & reg->tx_mask) !=
+ (card->txbd_rdptr & reg->tx_mask)) ||
+ ((card->txbd_wrptr & reg->tx_rollover_ind) !=
+ (card->txbd_rdptr & reg->tx_rollover_ind)))
+ return 1;
+ break;
+ case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
+ if (((card->txbd_wrptr & reg->tx_mask) !=
+ (card->txbd_rdptr & reg->tx_mask)) ||
+ ((card->txbd_wrptr & reg->tx_rollover_ind) ==
+ (card->txbd_rdptr & reg->tx_rollover_ind)))
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* _MWIFIEX_PCIE_H */
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c
index c20017ced566..c20017ced566 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/marvell/mwifiex/scan.c
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
new file mode 100644
index 000000000000..4c8cae682c89
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -0,0 +1,2695 @@
+/*
+ * Marvell Wireless LAN device driver: SDIO specific handling
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 <linux/firmware.h>
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "sdio.h"
+
+
+#define SDIO_VERSION "1.0"
+
+/* The mwifiex_sdio_remove() callback function is called when
+ * user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently.
+ * If the user is removing the module, the few commands (FUNC_SHUTDOWN,
+ * HS_CANCEL etc.) are sent to the firmware.
+ * If the card is removed, there is no need to send these command.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
+
+static struct mwifiex_if_ops sdio_ops;
+static unsigned long iface_work_flags;
+
+static struct semaphore add_remove_card_sem;
+
+static struct memory_type_mapping generic_mem_type_map[] = {
+ {"DUMP", NULL, 0, 0xDD},
+};
+
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ {"ITCM", NULL, 0, 0xF0},
+ {"DTCM", NULL, 0, 0xF1},
+ {"SQRAM", NULL, 0, 0xF2},
+ {"APU", NULL, 0, 0xF3},
+ {"CIU", NULL, 0, 0xF4},
+ {"ICU", NULL, 0, 0xF5},
+ {"MAC", NULL, 0, 0xF6},
+ {"EXT7", NULL, 0, 0xF7},
+ {"EXT8", NULL, 0, 0xF8},
+ {"EXT9", NULL, 0, 0xF9},
+ {"EXT10", NULL, 0, 0xFA},
+ {"EXT11", NULL, 0, 0xFB},
+ {"EXT12", NULL, 0, 0xFC},
+ {"EXT13", NULL, 0, 0xFD},
+ {"EXTLAST", NULL, 0, 0xFE},
+};
+
+/*
+ * SDIO probe.
+ *
+ * This function probes an mwifiex device and registers it. It allocates
+ * the card structure, enables SDIO function number and initiates the
+ * device registration and initialization procedure by adding a logical
+ * interface.
+ */
+static int
+mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+{
+ int ret;
+ struct sdio_mmc_card *card = NULL;
+
+ pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n",
+ func->vendor, func->device, func->class, func->num);
+
+ card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->func = func;
+ card->device_id = id;
+
+ func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+
+ if (id->driver_data) {
+ struct mwifiex_sdio_device *data = (void *)id->driver_data;
+
+ card->firmware = data->firmware;
+ card->reg = data->reg;
+ card->max_ports = data->max_ports;
+ card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+ card->supports_sdio_new_mode = data->supports_sdio_new_mode;
+ card->has_control_mask = data->has_control_mask;
+ card->tx_buf_size = data->tx_buf_size;
+ card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
+ card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
+ card->can_dump_fw = data->can_dump_fw;
+ card->fw_dump_enh = data->fw_dump_enh;
+ card->can_auto_tdls = data->can_auto_tdls;
+ card->can_ext_scan = data->can_ext_scan;
+ }
+
+ sdio_claim_host(func);
+ ret = sdio_enable_func(func);
+ sdio_release_host(func);
+
+ if (ret) {
+ pr_err("%s: failed to enable function\n", __func__);
+ kfree(card);
+ return -EIO;
+ }
+
+ if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops,
+ MWIFIEX_SDIO)) {
+ pr_err("%s: add card failed\n", __func__);
+ kfree(card);
+ sdio_claim_host(func);
+ ret = sdio_disable_func(func);
+ sdio_release_host(func);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/*
+ * SDIO resume.
+ *
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int mwifiex_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sdio_mmc_card *card;
+ struct mwifiex_adapter *adapter;
+ mmc_pm_flag_t pm_flag = 0;
+
+ if (func) {
+ pm_flag = sdio_get_host_pm_caps(func);
+ card = sdio_get_drvdata(func);
+ if (!card || !card->adapter) {
+ pr_err("resume: invalid card or adapter\n");
+ return 0;
+ }
+ } else {
+ pr_err("resume: sdio_func is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ if (!adapter->is_suspended) {
+ mwifiex_dbg(adapter, WARN,
+ "device already resumed\n");
+ return 0;
+ }
+
+ adapter->is_suspended = false;
+
+ /* Disable Host Sleep */
+ mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+ MWIFIEX_ASYNC_CMD);
+
+ return 0;
+}
+
+/*
+ * SDIO remove.
+ *
+ * This function removes the interface and frees up the card structure.
+ */
+static void
+mwifiex_sdio_remove(struct sdio_func *func)
+{
+ struct sdio_mmc_card *card;
+ struct mwifiex_adapter *adapter;
+ struct mwifiex_private *priv;
+
+ card = sdio_get_drvdata(func);
+ if (!card)
+ return;
+
+ adapter = card->adapter;
+ if (!adapter || !adapter->priv_num)
+ return;
+
+ mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num);
+
+ if (user_rmmod) {
+ if (adapter->is_suspended)
+ mwifiex_sdio_resume(adapter->dev);
+
+ mwifiex_deauthenticate_all(adapter);
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ mwifiex_disable_auto_ds(priv);
+ mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN);
+ }
+
+ mwifiex_remove_card(card->adapter, &add_remove_card_sem);
+}
+
+/*
+ * SDIO suspend.
+ *
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not suspended, this function allocates and sends a host
+ * sleep activate request to the firmware and turns off the traffic.
+ */
+static int mwifiex_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sdio_mmc_card *card;
+ struct mwifiex_adapter *adapter;
+ mmc_pm_flag_t pm_flag = 0;
+ int ret = 0;
+
+ if (func) {
+ pm_flag = sdio_get_host_pm_caps(func);
+ pr_debug("cmd: %s: suspend: PM flag = 0x%x\n",
+ sdio_func_id(func), pm_flag);
+ if (!(pm_flag & MMC_PM_KEEP_POWER)) {
+ pr_err("%s: cannot remain alive while host is"
+ " suspended\n", sdio_func_id(func));
+ return -ENOSYS;
+ }
+
+ card = sdio_get_drvdata(func);
+ if (!card || !card->adapter) {
+ pr_err("suspend: invalid card or adapter\n");
+ return 0;
+ }
+ } else {
+ pr_err("suspend: sdio_func is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ /* Enable the Host Sleep */
+ if (!mwifiex_enable_hs(adapter)) {
+ mwifiex_dbg(adapter, ERROR,
+ "cmd: failed to suspend\n");
+ adapter->hs_enabling = false;
+ return -EFAULT;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "cmd: suspend with MMC_PM_KEEP_POWER\n");
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+
+ /* Indicate device suspended */
+ adapter->is_suspended = true;
+ adapter->hs_enabling = false;
+
+ return ret;
+}
+
+/* Device ID for SD8786 */
+#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116)
+/* Device ID for SD8787 */
+#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119)
+/* Device ID for SD8797 */
+#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129)
+/* Device ID for SD8897 */
+#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d)
+/* Device ID for SD8887 */
+#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135)
+/* Device ID for SD8801 */
+#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139)
+/* Device ID for SD8997 */
+#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141)
+
+
+/* WLAN IDs */
+static const struct sdio_device_id mwifiex_ids[] = {
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8786},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8787},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8797},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8897},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887),
+ .driver_data = (unsigned long)&mwifiex_sdio_sd8887},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
+ .driver_data = (unsigned long)&mwifiex_sdio_sd8801},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997),
+ .driver_data = (unsigned long)&mwifiex_sdio_sd8997},
+ {},
+};
+
+MODULE_DEVICE_TABLE(sdio, mwifiex_ids);
+
+static const struct dev_pm_ops mwifiex_sdio_pm_ops = {
+ .suspend = mwifiex_sdio_suspend,
+ .resume = mwifiex_sdio_resume,
+};
+
+static struct sdio_driver mwifiex_sdio = {
+ .name = "mwifiex_sdio",
+ .id_table = mwifiex_ids,
+ .probe = mwifiex_sdio_probe,
+ .remove = mwifiex_sdio_remove,
+ .drv = {
+ .owner = THIS_MODULE,
+ .pm = &mwifiex_sdio_pm_ops,
+ }
+};
+
+/* Write data into SDIO card register. Caller claims SDIO device. */
+static int
+mwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data)
+{
+ int ret = -1;
+ sdio_writeb(func, data, reg, &ret);
+ return ret;
+}
+
+/*
+ * This function writes data into SDIO card register.
+ */
+static int
+mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+
+ sdio_claim_host(card->func);
+ ret = mwifiex_write_reg_locked(card->func, reg, data);
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+/*
+ * This function reads data from SDIO card register.
+ */
+static int
+mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = -1;
+ u8 val;
+
+ sdio_claim_host(card->func);
+ val = sdio_readb(card->func, reg, &ret);
+ sdio_release_host(card->func);
+
+ *data = val;
+
+ return ret;
+}
+
+/*
+ * This function writes multiple data into SDIO card memory.
+ *
+ * This does not work in suspended mode.
+ */
+static int
+mwifiex_write_data_sync(struct mwifiex_adapter *adapter,
+ u8 *buffer, u32 pkt_len, u32 port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+ u8 blk_mode =
+ (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE;
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
+ u32 blk_cnt =
+ (blk_mode ==
+ BLOCK_MODE) ? (pkt_len /
+ MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len;
+ u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK);
+
+ if (adapter->is_suspended) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: not allowed while suspended\n", __func__);
+ return -1;
+ }
+
+ sdio_claim_host(card->func);
+
+ ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size);
+
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+/*
+ * This function reads multiple data from SDIO card memory.
+ */
+static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer,
+ u32 len, u32 port, u8 claim)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+ u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE
+ : BLOCK_MODE;
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
+ u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE)
+ : len;
+ u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK);
+
+ if (claim)
+ sdio_claim_host(card->func);
+
+ ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size);
+
+ if (claim)
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+/*
+ * This function wakes up the card.
+ *
+ * A host power up command is written to the card configuration
+ * register to wake up the card.
+ */
+static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
+{
+ mwifiex_dbg(adapter, EVENT,
+ "event: wakeup device...\n");
+
+ return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP);
+}
+
+/*
+ * This function is called after the card has woken up.
+ *
+ * The card configuration register is reset.
+ */
+static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
+{
+ mwifiex_dbg(adapter, EVENT,
+ "cmd: wakeup device completed\n");
+
+ return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0);
+}
+
+/*
+ * This function is used to initialize IO ports for the
+ * chipsets supporting SDIO new mode eg SD8897.
+ */
+static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter)
+{
+ u8 reg;
+ struct sdio_mmc_card *card = adapter->card;
+
+ adapter->ioport = MEM_PORT;
+
+ /* enable sdio new mode */
+ if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg,
+ reg | CMD53_NEW_MODE))
+ return -1;
+
+ /* Configure cmd port and enable reading rx length from the register */
+ if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0,
+ reg | CMD_PORT_RD_LEN_EN))
+ return -1;
+
+ /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
+ * completed
+ */
+ if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1,
+ reg | CMD_PORT_AUTO_EN))
+ return -1;
+
+ return 0;
+}
+
+/* This function initializes the IO ports.
+ *
+ * The following operations are performed -
+ * - Read the IO ports (0, 1 and 2)
+ * - Set host interrupt Reset-To-Read to clear
+ * - Set auto re-enable interrupt
+ */
+static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
+{
+ u8 reg;
+ struct sdio_mmc_card *card = adapter->card;
+
+ adapter->ioport = 0;
+
+ if (card->supports_sdio_new_mode) {
+ if (mwifiex_init_sdio_new_mode(adapter))
+ return -1;
+ goto cont;
+ }
+
+ /* Read the IO port */
+ if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, &reg))
+ adapter->ioport |= (reg & 0xff);
+ else
+ return -1;
+
+ if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, &reg))
+ adapter->ioport |= ((reg & 0xff) << 8);
+ else
+ return -1;
+
+ if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, &reg))
+ adapter->ioport |= ((reg & 0xff) << 16);
+ else
+ return -1;
+cont:
+ mwifiex_dbg(adapter, INFO,
+ "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
+
+ /* Set Host interrupt reset to read to clear */
+ if (!mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, &reg))
+ mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg,
+ reg | card->reg->sdio_int_mask);
+ else
+ return -1;
+
+ /* Dnld/Upld ready set to auto reset */
+ if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, &reg))
+ mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg,
+ reg | AUTO_RE_ENABLE_INT);
+ else
+ return -1;
+
+ return 0;
+}
+
+/*
+ * This function sends data to the card.
+ */
+static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter,
+ u8 *payload, u32 pkt_len, u32 port)
+{
+ u32 i = 0;
+ int ret;
+
+ do {
+ ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port);
+ if (ret) {
+ i++;
+ mwifiex_dbg(adapter, ERROR,
+ "host_to_card, write iomem\t"
+ "(%d) failed: %d\n", i, ret);
+ if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04))
+ mwifiex_dbg(adapter, ERROR,
+ "write CFG reg failed\n");
+
+ ret = -1;
+ if (i > MAX_WRITE_IOMEM_RETRY)
+ return ret;
+ }
+ } while (ret == -1);
+
+ return ret;
+}
+
+/*
+ * This function gets the read port.
+ *
+ * If control port bit is set in MP read bitmap, the control port
+ * is returned, otherwise the current read port is returned and
+ * the value is increased (provided it does not reach the maximum
+ * limit, in which case it is reset to 1)
+ */
+static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u32 rd_bitmap = card->mp_rd_bitmap;
+
+ mwifiex_dbg(adapter, DATA,
+ "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
+
+ if (card->supports_sdio_new_mode) {
+ if (!(rd_bitmap & reg->data_port_mask))
+ return -1;
+ } else {
+ if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
+ return -1;
+ }
+
+ if ((card->has_control_mask) &&
+ (card->mp_rd_bitmap & CTRL_PORT_MASK)) {
+ card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
+ *port = CTRL_PORT;
+ mwifiex_dbg(adapter, DATA,
+ "data: port=%d mp_rd_bitmap=0x%08x\n",
+ *port, card->mp_rd_bitmap);
+ return 0;
+ }
+
+ if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port)))
+ return -1;
+
+ /* We are now handling the SDIO data ports */
+ card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port));
+ *port = card->curr_rd_port;
+
+ if (++card->curr_rd_port == card->max_ports)
+ card->curr_rd_port = reg->start_rd_port;
+
+ mwifiex_dbg(adapter, DATA,
+ "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n",
+ *port, rd_bitmap, card->mp_rd_bitmap);
+
+ return 0;
+}
+
+/*
+ * This function gets the write port for data.
+ *
+ * The current write port is returned if available and the value is
+ * increased (provided it does not reach the maximum limit, in which
+ * case it is reset to 1)
+ */
+static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u32 wr_bitmap = card->mp_wr_bitmap;
+
+ mwifiex_dbg(adapter, DATA,
+ "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
+
+ if (!(wr_bitmap & card->mp_data_port_mask)) {
+ adapter->data_sent = true;
+ return -EBUSY;
+ }
+
+ if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
+ card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port));
+ *port = card->curr_wr_port;
+ if (++card->curr_wr_port == card->mp_end_port)
+ card->curr_wr_port = reg->start_wr_port;
+ } else {
+ adapter->data_sent = true;
+ return -EBUSY;
+ }
+
+ if ((card->has_control_mask) && (*port == CTRL_PORT)) {
+ mwifiex_dbg(adapter, ERROR,
+ "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
+ *port, card->curr_wr_port, wr_bitmap,
+ card->mp_wr_bitmap);
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, DATA,
+ "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
+ *port, wr_bitmap, card->mp_wr_bitmap);
+
+ return 0;
+}
+
+/*
+ * This function polls the card status.
+ */
+static int
+mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u32 tries;
+ u8 cs;
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs))
+ break;
+ else if ((cs & bits) == bits)
+ return 0;
+
+ usleep_range(10, 20);
+ }
+
+ mwifiex_dbg(adapter, ERROR,
+ "poll card status failed, tries = %d\n", tries);
+
+ return -1;
+}
+
+/*
+ * This function reads the firmware status.
+ */
+static int
+mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u8 fws0, fws1;
+
+ if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0))
+ return -1;
+
+ if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1))
+ return -1;
+
+ *dat = (u16) ((fws1 << 8) | fws0);
+
+ return 0;
+}
+
+/*
+ * This function disables the host interrupt.
+ *
+ * The host interrupt mask is read, the disable bit is reset and
+ * written back to the card host interrupt mask register.
+ */
+static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
+
+ sdio_claim_host(func);
+ mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+}
+
+/*
+ * This function reads the interrupt status from card.
+ */
+static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u8 sdio_ireg;
+ unsigned long flags;
+
+ if (mwifiex_read_data_sync(adapter, card->mp_regs,
+ card->reg->max_mp_regs,
+ REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
+ mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n");
+ return;
+ }
+
+ sdio_ireg = card->mp_regs[card->reg->host_int_status_reg];
+ if (sdio_ireg) {
+ /*
+ * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+ * For SDIO new mode CMD port interrupts
+ * DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+ * UP_LD_CMD_PORT_HOST_INT_STATUS
+ * Clear the interrupt status register
+ */
+ mwifiex_dbg(adapter, INTR,
+ "int: sdio_ireg = %#x\n", sdio_ireg);
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ adapter->int_status |= sdio_ireg;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+ }
+}
+
+/*
+ * SDIO interrupt handler.
+ *
+ * This function reads the interrupt status from firmware and handles
+ * the interrupt in current thread (ksdioirqd) right away.
+ */
+static void
+mwifiex_sdio_interrupt(struct sdio_func *func)
+{
+ struct mwifiex_adapter *adapter;
+ struct sdio_mmc_card *card;
+
+ card = sdio_get_drvdata(func);
+ if (!card || !card->adapter) {
+ pr_err("int: func=%p card=%p adapter=%p\n",
+ func, card, card ? card->adapter : NULL);
+ return;
+ }
+ adapter = card->adapter;
+
+ if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
+ adapter->ps_state = PS_STATE_AWAKE;
+
+ mwifiex_interrupt_status(adapter);
+ mwifiex_main_process(adapter);
+}
+
+/*
+ * This function enables the host interrupt.
+ *
+ * The host interrupt enable mask is written to the card
+ * host interrupt mask register.
+ */
+static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
+ int ret;
+
+ sdio_claim_host(func);
+
+ /* Request the SDIO IRQ */
+ ret = sdio_claim_irq(func, mwifiex_sdio_interrupt);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "claim irq failed: ret=%d\n", ret);
+ goto out;
+ }
+
+ /* Simply write the mask to the register */
+ ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg,
+ card->reg->host_int_enable);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "enable host interrupt failed\n");
+ sdio_release_irq(func);
+ }
+
+out:
+ sdio_release_host(func);
+ return ret;
+}
+
+/*
+ * This function sends a data buffer to the card.
+ */
+static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter,
+ u32 *type, u8 *buffer,
+ u32 npayload, u32 ioport)
+{
+ int ret;
+ u32 nb;
+
+ if (!buffer) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: buffer is NULL\n", __func__);
+ return -1;
+ }
+
+ ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1);
+
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: read iomem failed: %d\n", __func__,
+ ret);
+ return -1;
+ }
+
+ nb = le16_to_cpu(*(__le16 *) (buffer));
+ if (nb > npayload) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: invalid packet, nb=%d npayload=%d\n",
+ __func__, nb, npayload);
+ return -1;
+ }
+
+ *type = le16_to_cpu(*(__le16 *) (buffer + 2));
+
+ return ret;
+}
+
+/*
+ * This function downloads the firmware to the card.
+ *
+ * Firmware is downloaded to the card in blocks. Every block download
+ * is tested for CRC errors, and retried a number of times before
+ * returning failure.
+ */
+static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
+ struct mwifiex_fw_image *fw)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ int ret;
+ u8 *firmware = fw->fw_buf;
+ u32 firmware_len = fw->fw_len;
+ u32 offset = 0;
+ u8 base0, base1;
+ u8 *fwbuf;
+ u16 len = 0;
+ u32 txlen, tx_blocks = 0, tries;
+ u32 i = 0;
+
+ if (!firmware_len) {
+ mwifiex_dbg(adapter, ERROR,
+ "firmware image not found! Terminating download\n");
+ return -1;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "info: downloading FW image (%d bytes)\n",
+ firmware_len);
+
+ /* Assume that the allocated buffer is 8-byte aligned */
+ fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL);
+ if (!fwbuf)
+ return -ENOMEM;
+
+ sdio_claim_host(card->func);
+
+ /* Perform firmware data transfer */
+ do {
+ /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY
+ bits */
+ ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY |
+ DN_LD_CARD_RDY);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW download with helper:\t"
+ "poll status timeout @ %d\n", offset);
+ goto done;
+ }
+
+ /* More data? */
+ if (offset >= firmware_len)
+ break;
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ret = mwifiex_read_reg(adapter, reg->base_0_reg,
+ &base0);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "dev BASE0 register read failed:\t"
+ "base0=%#04X(%d). Terminating dnld\n",
+ base0, base0);
+ goto done;
+ }
+ ret = mwifiex_read_reg(adapter, reg->base_1_reg,
+ &base1);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "dev BASE1 register read failed:\t"
+ "base1=%#04X(%d). Terminating dnld\n",
+ base1, base1);
+ goto done;
+ }
+ len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff));
+
+ if (len)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (!len) {
+ break;
+ } else if (len > MWIFIEX_UPLD_SIZE) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW dnld failed @ %d, invalid length %d\n",
+ offset, len);
+ ret = -1;
+ goto done;
+ }
+
+ txlen = len;
+
+ if (len & BIT(0)) {
+ i++;
+ if (i > MAX_WRITE_IOMEM_RETRY) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW dnld failed @ %d, over max retry\n",
+ offset);
+ ret = -1;
+ goto done;
+ }
+ mwifiex_dbg(adapter, ERROR,
+ "CRC indicated by the helper:\t"
+ "len = 0x%04X, txlen = %d\n", len, txlen);
+ len &= ~BIT(0);
+ /* Setting this to 0 to resend from same offset */
+ txlen = 0;
+ } else {
+ i = 0;
+
+ /* Set blocksize to transfer - checking for last
+ block */
+ if (firmware_len - offset < txlen)
+ txlen = firmware_len - offset;
+
+ tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1)
+ / MWIFIEX_SDIO_BLOCK_SIZE;
+
+ /* Copy payload to buffer */
+ memmove(fwbuf, &firmware[offset], txlen);
+ }
+
+ ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks *
+ MWIFIEX_SDIO_BLOCK_SIZE,
+ adapter->ioport);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW download, write iomem (%d) failed @ %d\n",
+ i, offset);
+ if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04))
+ mwifiex_dbg(adapter, ERROR,
+ "write CFG reg failed\n");
+
+ ret = -1;
+ goto done;
+ }
+
+ offset += txlen;
+ } while (true);
+
+ sdio_release_host(card->func);
+
+ mwifiex_dbg(adapter, MSG,
+ "info: FW download over, size %d bytes\n", offset);
+
+ ret = 0;
+done:
+ kfree(fwbuf);
+ return ret;
+}
+
+/*
+ * This function checks the firmware status in card.
+ *
+ * The winner interface is also determined by this function.
+ */
+static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
+ u32 poll_num)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+ u16 firmware_stat;
+ u32 tries;
+ u8 winner_status;
+
+ /* Wait for firmware initialization event */
+ for (tries = 0; tries < poll_num; tries++) {
+ ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat);
+ if (ret)
+ continue;
+ if (firmware_stat == FIRMWARE_READY_SDIO) {
+ ret = 0;
+ break;
+ } else {
+ msleep(100);
+ ret = -1;
+ }
+ }
+
+ if (ret) {
+ if (mwifiex_read_reg
+ (adapter, card->reg->status_reg_0, &winner_status))
+ winner_status = 0;
+
+ if (winner_status)
+ adapter->winner = 0;
+ else
+ adapter->winner = 1;
+ }
+ return ret;
+}
+
+/*
+ * This function decode sdio aggreation pkt.
+ *
+ * Based on the the data block size and pkt_len,
+ * skb data will be decoded to few packets.
+ */
+static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ u32 total_pkt_len, pkt_len;
+ struct sk_buff *skb_deaggr;
+ u32 pkt_type;
+ u16 blk_size;
+ u8 blk_num;
+ u8 *data;
+
+ data = skb->data;
+ total_pkt_len = skb->len;
+
+ while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) {
+ if (total_pkt_len < adapter->sdio_rx_block_size)
+ break;
+ blk_num = *(data + BLOCK_NUMBER_OFFSET);
+ blk_size = adapter->sdio_rx_block_size * blk_num;
+ if (blk_size > total_pkt_len) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: error in blk_size,\t"
+ "blk_num=%d, blk_size=%d, total_pkt_len=%d\n",
+ __func__, blk_num, blk_size, total_pkt_len);
+ break;
+ }
+ pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET));
+ pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET +
+ 2));
+ if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: error in pkt_len,\t"
+ "pkt_len=%d, blk_size=%d\n",
+ __func__, pkt_len, blk_size);
+ break;
+ }
+ skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len,
+ GFP_KERNEL | GFP_DMA);
+ if (!skb_deaggr)
+ break;
+ skb_put(skb_deaggr, pkt_len);
+ memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len);
+ skb_pull(skb_deaggr, INTF_HEADER_LEN);
+
+ mwifiex_handle_rx_packet(adapter, skb_deaggr);
+ data += blk_size;
+ total_pkt_len -= blk_size;
+ }
+}
+
+/*
+ * This function decodes a received packet.
+ *
+ * Based on the type, the packet is treated as either a data, or
+ * a command response, or an event, and the correct handler
+ * function is invoked.
+ */
+static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb, u32 upld_typ)
+{
+ u8 *cmd_buf;
+ __le16 *curr_ptr = (__le16 *)skb->data;
+ u16 pkt_len = le16_to_cpu(*curr_ptr);
+ struct mwifiex_rxinfo *rx_info;
+
+ if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) {
+ skb_trim(skb, pkt_len);
+ skb_pull(skb, INTF_HEADER_LEN);
+ }
+
+ switch (upld_typ) {
+ case MWIFIEX_TYPE_AGGR_DATA:
+ mwifiex_dbg(adapter, INFO,
+ "info: --- Rx: Aggr Data packet ---\n");
+ rx_info = MWIFIEX_SKB_RXCB(skb);
+ rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA;
+ if (adapter->rx_work_enabled) {
+ skb_queue_tail(&adapter->rx_data_q, skb);
+ atomic_inc(&adapter->rx_pending);
+ adapter->data_received = true;
+ } else {
+ mwifiex_deaggr_sdio_pkt(adapter, skb);
+ dev_kfree_skb_any(skb);
+ }
+ break;
+
+ case MWIFIEX_TYPE_DATA:
+ mwifiex_dbg(adapter, DATA,
+ "info: --- Rx: Data packet ---\n");
+ if (adapter->rx_work_enabled) {
+ skb_queue_tail(&adapter->rx_data_q, skb);
+ adapter->data_received = true;
+ atomic_inc(&adapter->rx_pending);
+ } else {
+ mwifiex_handle_rx_packet(adapter, skb);
+ }
+ break;
+
+ case MWIFIEX_TYPE_CMD:
+ mwifiex_dbg(adapter, CMD,
+ "info: --- Rx: Cmd Response ---\n");
+ /* take care of curr_cmd = NULL case */
+ if (!adapter->curr_cmd) {
+ cmd_buf = adapter->upld_buf;
+
+ if (adapter->ps_state == PS_STATE_SLEEP_CFM)
+ mwifiex_process_sleep_confirm_resp(adapter,
+ skb->data,
+ skb->len);
+
+ memcpy(cmd_buf, skb->data,
+ min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER,
+ skb->len));
+
+ dev_kfree_skb_any(skb);
+ } else {
+ adapter->cmd_resp_received = true;
+ adapter->curr_cmd->resp_skb = skb;
+ }
+ break;
+
+ case MWIFIEX_TYPE_EVENT:
+ mwifiex_dbg(adapter, EVENT,
+ "info: --- Rx: Event ---\n");
+ adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data);
+
+ if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE))
+ memcpy(adapter->event_body,
+ skb->data + MWIFIEX_EVENT_HEADER_LEN,
+ skb->len);
+
+ /* event cause has been saved to adapter->event_cause */
+ adapter->event_received = true;
+ adapter->event_skb = skb;
+
+ break;
+
+ default:
+ mwifiex_dbg(adapter, ERROR,
+ "unknown upload type %#x\n", upld_typ);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * This function transfers received packets from card to driver, performing
+ * aggregation if required.
+ *
+ * For data received on control port, or if aggregation is disabled, the
+ * received buffers are uploaded as separate packets. However, if aggregation
+ * is enabled and required, the buffers are copied onto an aggregation buffer,
+ * provided there is space left, processed and finally uploaded.
+ */
+static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
+ u16 rx_len, u8 port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ s32 f_do_rx_aggr = 0;
+ s32 f_do_rx_cur = 0;
+ s32 f_aggr_cur = 0;
+ s32 f_post_aggr_cur = 0;
+ struct sk_buff *skb_deaggr;
+ struct sk_buff *skb = NULL;
+ u32 pkt_len, pkt_type, mport, pind;
+ u8 *curr_ptr;
+
+ if ((card->has_control_mask) && (port == CTRL_PORT)) {
+ /* Read the command Resp without aggr */
+ mwifiex_dbg(adapter, CMD,
+ "info: %s: no aggregation for cmd\t"
+ "response\n", __func__);
+
+ f_do_rx_cur = 1;
+ goto rx_curr_single;
+ }
+
+ if (!card->mpa_rx.enabled) {
+ mwifiex_dbg(adapter, WARN,
+ "info: %s: rx aggregation disabled\n",
+ __func__);
+
+ f_do_rx_cur = 1;
+ goto rx_curr_single;
+ }
+
+ if ((!card->has_control_mask && (card->mp_rd_bitmap &
+ card->reg->data_port_mask)) ||
+ (card->has_control_mask && (card->mp_rd_bitmap &
+ (~((u32) CTRL_PORT_MASK))))) {
+ /* Some more data RX pending */
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: not last packet\n", __func__);
+
+ if (MP_RX_AGGR_IN_PROGRESS(card)) {
+ if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) {
+ f_aggr_cur = 1;
+ } else {
+ /* No room in Aggr buf, do rx aggr now */
+ f_do_rx_aggr = 1;
+ f_post_aggr_cur = 1;
+ }
+ } else {
+ /* Rx aggr not in progress */
+ f_aggr_cur = 1;
+ }
+
+ } else {
+ /* No more data RX pending */
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: last packet\n", __func__);
+
+ if (MP_RX_AGGR_IN_PROGRESS(card)) {
+ f_do_rx_aggr = 1;
+ if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len))
+ f_aggr_cur = 1;
+ else
+ /* No room in Aggr buf, do rx aggr now */
+ f_do_rx_cur = 1;
+ } else {
+ f_do_rx_cur = 1;
+ }
+ }
+
+ if (f_aggr_cur) {
+ mwifiex_dbg(adapter, INFO,
+ "info: current packet aggregation\n");
+ /* Curr pkt can be aggregated */
+ mp_rx_aggr_setup(card, rx_len, port);
+
+ if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
+ mp_rx_aggr_port_limit_reached(card)) {
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: aggregated packet\t"
+ "limit reached\n", __func__);
+ /* No more pkts allowed in Aggr buf, rx it */
+ f_do_rx_aggr = 1;
+ }
+ }
+
+ if (f_do_rx_aggr) {
+ /* do aggr RX now */
+ mwifiex_dbg(adapter, DATA,
+ "info: do_rx_aggr: num of packets: %d\n",
+ card->mpa_rx.pkt_cnt);
+
+ if (card->supports_sdio_new_mode) {
+ int i;
+ u32 port_count;
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_rx.ports & BIT(i))
+ port_count++;
+
+ /* Reading data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_rx.start_port;
+ } else {
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (card->mpa_rx.ports << 4)) +
+ card->mpa_rx.start_port;
+ }
+
+ if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
+ card->mpa_rx.buf_len, mport, 1))
+ goto error;
+
+ curr_ptr = card->mpa_rx.buf;
+
+ for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
+ u32 *len_arr = card->mpa_rx.len_arr;
+
+ /* get curr PKT len & type */
+ pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]);
+ pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]);
+
+ /* copy pkt to deaggr buf */
+ skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind],
+ GFP_KERNEL |
+ GFP_DMA);
+ if (!skb_deaggr) {
+ mwifiex_dbg(adapter, ERROR, "skb allocation failure\t"
+ "drop pkt len=%d type=%d\n",
+ pkt_len, pkt_type);
+ curr_ptr += len_arr[pind];
+ continue;
+ }
+
+ skb_put(skb_deaggr, len_arr[pind]);
+
+ if ((pkt_type == MWIFIEX_TYPE_DATA ||
+ (pkt_type == MWIFIEX_TYPE_AGGR_DATA &&
+ adapter->sdio_rx_aggr_enable)) &&
+ (pkt_len <= len_arr[pind])) {
+
+ memcpy(skb_deaggr->data, curr_ptr, pkt_len);
+
+ skb_trim(skb_deaggr, pkt_len);
+
+ /* Process de-aggr packet */
+ mwifiex_decode_rx_packet(adapter, skb_deaggr,
+ pkt_type);
+ } else {
+ mwifiex_dbg(adapter, ERROR,
+ "drop wrong aggr pkt:\t"
+ "sdio_single_port_rx_aggr=%d\t"
+ "type=%d len=%d max_len=%d\n",
+ adapter->sdio_rx_aggr_enable,
+ pkt_type, pkt_len, len_arr[pind]);
+ dev_kfree_skb_any(skb_deaggr);
+ }
+ curr_ptr += len_arr[pind];
+ }
+ MP_RX_AGGR_BUF_RESET(card);
+ }
+
+rx_curr_single:
+ if (f_do_rx_cur) {
+ mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n",
+ port, rx_len);
+
+ skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
+ if (!skb) {
+ mwifiex_dbg(adapter, ERROR,
+ "single skb allocated fail,\t"
+ "drop pkt port=%d len=%d\n", port, rx_len);
+ if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
+ card->mpa_rx.buf, rx_len,
+ adapter->ioport + port))
+ goto error;
+ return 0;
+ }
+
+ skb_put(skb, rx_len);
+
+ if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
+ skb->data, skb->len,
+ adapter->ioport + port))
+ goto error;
+ if (!adapter->sdio_rx_aggr_enable &&
+ pkt_type == MWIFIEX_TYPE_AGGR_DATA) {
+ mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t"
+ "current SDIO RX Aggr not enabled\n",
+ pkt_type);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ mwifiex_decode_rx_packet(adapter, skb, pkt_type);
+ }
+ if (f_post_aggr_cur) {
+ mwifiex_dbg(adapter, INFO,
+ "info: current packet aggregation\n");
+ /* Curr pkt can be aggregated */
+ mp_rx_aggr_setup(card, rx_len, port);
+ }
+
+ return 0;
+error:
+ if (MP_RX_AGGR_IN_PROGRESS(card))
+ MP_RX_AGGR_BUF_RESET(card);
+
+ if (f_do_rx_cur && skb)
+ /* Single transfer pending. Free curr buff also */
+ dev_kfree_skb_any(skb);
+
+ return -1;
+}
+
+/*
+ * This function checks the current interrupt status.
+ *
+ * The following interrupts are checked and handled by this function -
+ * - Data sent
+ * - Command sent
+ * - Packets received
+ *
+ * Since the firmware does not generate download ready interrupt if the
+ * port updated is command port only, command sent interrupt checking
+ * should be done manually, and for every SDIO interrupt.
+ *
+ * In case of Rx packets received, the packets are uploaded from card to
+ * host and processed accordingly.
+ */
+static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ int ret = 0;
+ u8 sdio_ireg;
+ struct sk_buff *skb;
+ u8 port = CTRL_PORT;
+ u32 len_reg_l, len_reg_u;
+ u32 rx_blocks;
+ u16 rx_len;
+ unsigned long flags;
+ u32 bitmap;
+ u8 cr;
+
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ sdio_ireg = adapter->int_status;
+ adapter->int_status = 0;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+
+ if (!sdio_ireg)
+ return ret;
+
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
+ adapter->cmd_sent = false;
+
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
+ u32 pkt_type;
+
+ /* read the len of control packet */
+ rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8;
+ rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0];
+ rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE);
+ if (rx_len <= INTF_HEADER_LEN ||
+ (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
+ MWIFIEX_RX_DATA_BUF_SIZE)
+ return -1;
+ rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+ mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len);
+
+ skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
+ if (!skb)
+ return -1;
+
+ skb_put(skb, rx_len);
+
+ if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data,
+ skb->len, adapter->ioport |
+ CMD_PORT_SLCT)) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: failed to card_to_host", __func__);
+ dev_kfree_skb_any(skb);
+ goto term_cmd;
+ }
+
+ if ((pkt_type != MWIFIEX_TYPE_CMD) &&
+ (pkt_type != MWIFIEX_TYPE_EVENT))
+ mwifiex_dbg(adapter, ERROR,
+ "%s:Received wrong packet on cmd port",
+ __func__);
+
+ mwifiex_decode_rx_packet(adapter, skb, pkt_type);
+ }
+
+ if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
+ bitmap = (u32) card->mp_regs[reg->wr_bitmap_l];
+ bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
+ if (card->supports_sdio_new_mode) {
+ bitmap |=
+ ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16;
+ bitmap |=
+ ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24;
+ }
+ card->mp_wr_bitmap = bitmap;
+
+ mwifiex_dbg(adapter, INTR,
+ "int: DNLD: wr_bitmap=0x%x\n",
+ card->mp_wr_bitmap);
+ if (adapter->data_sent &&
+ (card->mp_wr_bitmap & card->mp_data_port_mask)) {
+ mwifiex_dbg(adapter, INTR,
+ "info: <--- Tx DONE Interrupt --->\n");
+ adapter->data_sent = false;
+ }
+ }
+
+ /* As firmware will not generate download ready interrupt if the port
+ updated is command port only, cmd_sent should be done for any SDIO
+ interrupt. */
+ if (card->has_control_mask && adapter->cmd_sent) {
+ /* Check if firmware has attach buffer at command port and
+ update just that in wr_bit_map. */
+ card->mp_wr_bitmap |=
+ (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK;
+ if (card->mp_wr_bitmap & CTRL_PORT_MASK)
+ adapter->cmd_sent = false;
+ }
+
+ mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n",
+ adapter->cmd_sent, adapter->data_sent);
+ if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
+ bitmap = (u32) card->mp_regs[reg->rd_bitmap_l];
+ bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
+ if (card->supports_sdio_new_mode) {
+ bitmap |=
+ ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16;
+ bitmap |=
+ ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24;
+ }
+ card->mp_rd_bitmap = bitmap;
+ mwifiex_dbg(adapter, INTR,
+ "int: UPLD: rd_bitmap=0x%x\n",
+ card->mp_rd_bitmap);
+
+ while (true) {
+ ret = mwifiex_get_rd_port(adapter, &port);
+ if (ret) {
+ mwifiex_dbg(adapter, INFO,
+ "info: no more rd_port available\n");
+ break;
+ }
+ len_reg_l = reg->rd_len_p0_l + (port << 1);
+ len_reg_u = reg->rd_len_p0_u + (port << 1);
+ rx_len = ((u16) card->mp_regs[len_reg_u]) << 8;
+ rx_len |= (u16) card->mp_regs[len_reg_l];
+ mwifiex_dbg(adapter, INFO,
+ "info: RX: port=%d rx_len=%u\n",
+ port, rx_len);
+ rx_blocks =
+ (rx_len + MWIFIEX_SDIO_BLOCK_SIZE -
+ 1) / MWIFIEX_SDIO_BLOCK_SIZE;
+ if (rx_len <= INTF_HEADER_LEN ||
+ (card->mpa_rx.enabled &&
+ ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
+ card->mpa_rx.buf_size))) {
+ mwifiex_dbg(adapter, ERROR,
+ "invalid rx_len=%d\n",
+ rx_len);
+ return -1;
+ }
+
+ rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+ mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n",
+ rx_len);
+
+ if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len,
+ port)) {
+ mwifiex_dbg(adapter, ERROR,
+ "card_to_host_mpa failed: int status=%#x\n",
+ sdio_ireg);
+ goto term_cmd;
+ }
+ }
+ }
+
+ return 0;
+
+term_cmd:
+ /* terminate cmd */
+ if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+ mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n");
+ else
+ mwifiex_dbg(adapter, INFO,
+ "info: CFG reg val = %d\n", cr);
+
+ if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
+ mwifiex_dbg(adapter, ERROR,
+ "write CFG reg failed\n");
+ else
+ mwifiex_dbg(adapter, INFO, "info: write success\n");
+
+ if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+ mwifiex_dbg(adapter, ERROR,
+ "read CFG reg failed\n");
+ else
+ mwifiex_dbg(adapter, INFO,
+ "info: CFG reg val =%x\n", cr);
+
+ return -1;
+}
+
+/*
+ * This function aggregates transmission buffers in driver and downloads
+ * the aggregated packet to card.
+ *
+ * The individual packets are aggregated by copying into an aggregation
+ * buffer and then downloaded to the card. Previous unsent packets in the
+ * aggregation buffer are pre-copied first before new packets are added.
+ * Aggregation is done till there is space left in the aggregation buffer,
+ * or till new packets are available.
+ *
+ * The function will only download the packet to the card when aggregation
+ * stops, otherwise it will just aggregate the packet in aggregation buffer
+ * and return.
+ */
+static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
+ u8 *payload, u32 pkt_len, u32 port,
+ u32 next_pkt_len)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+ s32 f_send_aggr_buf = 0;
+ s32 f_send_cur_buf = 0;
+ s32 f_precopy_cur_buf = 0;
+ s32 f_postcopy_cur_buf = 0;
+ u32 mport;
+
+ if (!card->mpa_tx.enabled ||
+ (card->has_control_mask && (port == CTRL_PORT)) ||
+ (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) {
+ mwifiex_dbg(adapter, WARN,
+ "info: %s: tx aggregation disabled\n",
+ __func__);
+
+ f_send_cur_buf = 1;
+ goto tx_curr_single;
+ }
+
+ if (next_pkt_len) {
+ /* More pkt in TX queue */
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: more packets in queue.\n",
+ __func__);
+
+ if (MP_TX_AGGR_IN_PROGRESS(card)) {
+ if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
+ f_precopy_cur_buf = 1;
+
+ if (!(card->mp_wr_bitmap &
+ (1 << card->curr_wr_port)) ||
+ !MP_TX_AGGR_BUF_HAS_ROOM(
+ card, pkt_len + next_pkt_len))
+ f_send_aggr_buf = 1;
+ } else {
+ /* No room in Aggr buf, send it */
+ f_send_aggr_buf = 1;
+
+ if (!(card->mp_wr_bitmap &
+ (1 << card->curr_wr_port)))
+ f_send_cur_buf = 1;
+ else
+ f_postcopy_cur_buf = 1;
+ }
+ } else {
+ if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) &&
+ (card->mp_wr_bitmap & (1 << card->curr_wr_port)))
+ f_precopy_cur_buf = 1;
+ else
+ f_send_cur_buf = 1;
+ }
+ } else {
+ /* Last pkt in TX queue */
+ mwifiex_dbg(adapter, INFO,
+ "info: %s: Last packet in Tx Queue.\n",
+ __func__);
+
+ if (MP_TX_AGGR_IN_PROGRESS(card)) {
+ /* some packs in Aggr buf already */
+ f_send_aggr_buf = 1;
+
+ if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len))
+ f_precopy_cur_buf = 1;
+ else
+ /* No room in Aggr buf, send it */
+ f_send_cur_buf = 1;
+ } else {
+ f_send_cur_buf = 1;
+ }
+ }
+
+ if (f_precopy_cur_buf) {
+ mwifiex_dbg(adapter, DATA,
+ "data: %s: precopy current buffer\n",
+ __func__);
+ MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
+
+ if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) ||
+ mp_tx_aggr_port_limit_reached(card))
+ /* No more pkts allowed in Aggr buf, send it */
+ f_send_aggr_buf = 1;
+ }
+
+ if (f_send_aggr_buf) {
+ mwifiex_dbg(adapter, DATA,
+ "data: %s: send aggr buffer: %d %d\n",
+ __func__, card->mpa_tx.start_port,
+ card->mpa_tx.ports);
+ if (card->supports_sdio_new_mode) {
+ u32 port_count;
+ int i;
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_tx.ports & BIT(i))
+ port_count++;
+
+ /* Writing data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_tx.start_port;
+ } else {
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (card->mpa_tx.ports << 4)) +
+ card->mpa_tx.start_port;
+ }
+
+ ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
+ card->mpa_tx.buf_len, mport);
+
+ MP_TX_AGGR_BUF_RESET(card);
+ }
+
+tx_curr_single:
+ if (f_send_cur_buf) {
+ mwifiex_dbg(adapter, DATA,
+ "data: %s: send current buffer %d\n",
+ __func__, port);
+ ret = mwifiex_write_data_to_card(adapter, payload, pkt_len,
+ adapter->ioport + port);
+ }
+
+ if (f_postcopy_cur_buf) {
+ mwifiex_dbg(adapter, DATA,
+ "data: %s: postcopy current buffer\n",
+ __func__);
+ MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
+ }
+
+ return ret;
+}
+
+/*
+ * This function downloads data from driver to card.
+ *
+ * Both commands and data packets are transferred to the card by this
+ * function.
+ *
+ * This function adds the SDIO specific header to the front of the buffer
+ * before transferring. The header contains the length of the packet and
+ * the type. The firmware handles the packets based upon this set type.
+ */
+static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
+ u8 type, struct sk_buff *skb,
+ struct mwifiex_tx_param *tx_param)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+ u32 buf_block_len;
+ u32 blk_size;
+ u32 port = CTRL_PORT;
+ u8 *payload = (u8 *)skb->data;
+ u32 pkt_len = skb->len;
+
+ /* Allocate buffer and copy payload */
+ blk_size = MWIFIEX_SDIO_BLOCK_SIZE;
+ buf_block_len = (pkt_len + blk_size - 1) / blk_size;
+ *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len);
+ *(__le16 *)&payload[2] = cpu_to_le16(type);
+
+ /*
+ * This is SDIO specific header
+ * u16 length,
+ * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1,
+ * MWIFIEX_TYPE_EVENT = 3)
+ */
+ if (type == MWIFIEX_TYPE_DATA) {
+ ret = mwifiex_get_wr_port_data(adapter, &port);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "%s: no wr_port available\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ adapter->cmd_sent = true;
+ /* Type must be MWIFIEX_TYPE_CMD */
+
+ if (pkt_len <= INTF_HEADER_LEN ||
+ pkt_len > MWIFIEX_UPLD_SIZE)
+ mwifiex_dbg(adapter, ERROR,
+ "%s: payload=%p, nb=%d\n",
+ __func__, payload, pkt_len);
+
+ if (card->supports_sdio_new_mode)
+ port = CMD_PORT_SLCT;
+ }
+
+ /* Transfer data to card */
+ pkt_len = buf_block_len * blk_size;
+
+ if (tx_param)
+ ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len,
+ port, tx_param->next_pkt_len
+ );
+ else
+ ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len,
+ port, 0);
+
+ if (ret) {
+ if (type == MWIFIEX_TYPE_CMD)
+ adapter->cmd_sent = false;
+ if (type == MWIFIEX_TYPE_DATA) {
+ adapter->data_sent = false;
+ /* restore curr_wr_port in error cases */
+ card->curr_wr_port = port;
+ card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port);
+ }
+ } else {
+ if (type == MWIFIEX_TYPE_DATA) {
+ if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port)))
+ adapter->data_sent = true;
+ else
+ adapter->data_sent = false;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * This function allocates the MPA Tx and Rx buffers.
+ */
+static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter,
+ u32 mpa_tx_buf_size, u32 mpa_rx_buf_size)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u32 rx_buf_size;
+ int ret = 0;
+
+ card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL);
+ if (!card->mpa_tx.buf) {
+ ret = -1;
+ goto error;
+ }
+
+ card->mpa_tx.buf_size = mpa_tx_buf_size;
+
+ rx_buf_size = max_t(u32, mpa_rx_buf_size,
+ (u32)SDIO_MAX_AGGR_BUF_SIZE);
+ card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL);
+ if (!card->mpa_rx.buf) {
+ ret = -1;
+ goto error;
+ }
+
+ card->mpa_rx.buf_size = rx_buf_size;
+
+error:
+ if (ret) {
+ kfree(card->mpa_tx.buf);
+ kfree(card->mpa_rx.buf);
+ card->mpa_tx.buf_size = 0;
+ card->mpa_rx.buf_size = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * This function unregisters the SDIO device.
+ *
+ * The SDIO IRQ is released, the function is disabled and driver
+ * data is set to null.
+ */
+static void
+mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ if (adapter->card) {
+ sdio_claim_host(card->func);
+ sdio_disable_func(card->func);
+ sdio_release_host(card->func);
+ }
+}
+
+/*
+ * This function registers the SDIO device.
+ *
+ * SDIO IRQ is claimed, block size is set and driver data is initialized.
+ */
+static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
+
+ /* save adapter pointer in card */
+ card->adapter = adapter;
+ adapter->tx_buf_size = card->tx_buf_size;
+
+ sdio_claim_host(func);
+
+ /* Set block size */
+ ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE);
+ sdio_release_host(func);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "cannot set SDIO block size\n");
+ return ret;
+ }
+
+
+ adapter->dev = &func->dev;
+
+ strcpy(adapter->fw_name, card->firmware);
+ if (card->fw_dump_enh) {
+ adapter->mem_type_mapping_tbl = generic_mem_type_map;
+ adapter->num_mem_types = 1;
+ } else {
+ adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+ adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
+ }
+
+ return 0;
+}
+
+/*
+ * This function initializes the SDIO driver.
+ *
+ * The following initializations steps are followed -
+ * - Read the Host interrupt status register to acknowledge
+ * the first interrupt got from bootloader
+ * - Disable host interrupt mask register
+ * - Get SDIO port
+ * - Initialize SDIO variables in card
+ * - Allocate MP registers
+ * - Allocate MPA Tx and Rx buffers
+ */
+static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ int ret;
+ u8 sdio_ireg;
+
+ sdio_set_drvdata(card->func, card);
+
+ /*
+ * Read the host_int_status_reg for ACK the first interrupt got
+ * from the bootloader. If we don't do this we get a interrupt
+ * as soon as we register the irq.
+ */
+ mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg);
+
+ /* Get SDIO ioport */
+ mwifiex_init_sdio_ioport(adapter);
+
+ /* Initialize SDIO variables in card */
+ card->mp_rd_bitmap = 0;
+ card->mp_wr_bitmap = 0;
+ card->curr_rd_port = reg->start_rd_port;
+ card->curr_wr_port = reg->start_wr_port;
+
+ card->mp_data_port_mask = reg->data_port_mask;
+
+ card->mpa_tx.buf_len = 0;
+ card->mpa_tx.pkt_cnt = 0;
+ card->mpa_tx.start_port = 0;
+
+ card->mpa_tx.enabled = 1;
+ card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit;
+
+ card->mpa_rx.buf_len = 0;
+ card->mpa_rx.pkt_cnt = 0;
+ card->mpa_rx.start_port = 0;
+
+ card->mpa_rx.enabled = 1;
+ card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit;
+
+ /* Allocate buffers for SDIO MP-A */
+ card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL);
+ if (!card->mp_regs)
+ return -ENOMEM;
+
+ /* Allocate skb pointer buffers */
+ card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) *
+ card->mp_agg_pkt_limit, GFP_KERNEL);
+ if (!card->mpa_rx.skb_arr) {
+ kfree(card->mp_regs);
+ return -ENOMEM;
+ }
+
+ card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
+ card->mp_agg_pkt_limit, GFP_KERNEL);
+ if (!card->mpa_rx.len_arr) {
+ kfree(card->mp_regs);
+ kfree(card->mpa_rx.skb_arr);
+ return -ENOMEM;
+ }
+
+ ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
+ card->mp_tx_agg_buf_size,
+ card->mp_rx_agg_buf_size);
+
+ /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */
+ if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX ||
+ card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) {
+ /* Disable rx single port aggregation */
+ adapter->host_disable_sdio_rx_aggr = true;
+
+ ret = mwifiex_alloc_sdio_mpa_buffers
+ (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+ MWIFIEX_MP_AGGR_BUF_SIZE_32K);
+ if (ret) {
+ /* Disable multi port aggregation */
+ card->mpa_tx.enabled = 0;
+ card->mpa_rx.enabled = 0;
+ }
+ }
+
+ adapter->auto_tdls = card->can_auto_tdls;
+ adapter->ext_scan = card->can_ext_scan;
+ return 0;
+}
+
+/*
+ * This function resets the MPA Tx and Rx buffers.
+ */
+static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ MP_TX_AGGR_BUF_RESET(card);
+ MP_RX_AGGR_BUF_RESET(card);
+}
+
+/*
+ * This function cleans up the allocated card buffers.
+ *
+ * The following are freed by this function -
+ * - MP registers
+ * - MPA Tx buffer
+ * - MPA Rx buffer
+ */
+static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ kfree(card->mp_regs);
+ kfree(card->mpa_rx.skb_arr);
+ kfree(card->mpa_rx.len_arr);
+ kfree(card->mpa_tx.buf);
+ kfree(card->mpa_rx.buf);
+ sdio_set_drvdata(card->func, NULL);
+ kfree(card);
+}
+
+/*
+ * This function updates the MP end port in card.
+ */
+static void
+mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ int i;
+
+ card->mp_end_port = port;
+
+ card->mp_data_port_mask = reg->data_port_mask;
+
+ if (reg->start_wr_port) {
+ for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
+ card->mp_data_port_mask &=
+ ~(1 << (card->max_ports - i));
+ }
+
+ card->curr_wr_port = reg->start_wr_port;
+
+ mwifiex_dbg(adapter, CMD,
+ "cmd: mp_end_port %d, data port mask 0x%x\n",
+ port, card->mp_data_port_mask);
+}
+
+static void mwifiex_recreate_adapter(struct sdio_mmc_card *card)
+{
+ struct sdio_func *func = card->func;
+ const struct sdio_device_id *device_id = card->device_id;
+
+ /* TODO mmc_hw_reset does not require destroying and re-probing the
+ * whole adapter. Hence there was no need to for this rube-goldberg
+ * design to reload the fw from an external workqueue. If we don't
+ * destroy the adapter we could reload the fw from
+ * mwifiex_main_work_queue directly.
+ * The real difficulty with fw reset is to restore all the user
+ * settings applied through ioctl. By destroying and recreating the
+ * adapter, we take the easy way out, since we rely on user space to
+ * restore them. We assume that user space will treat the new
+ * incarnation of the adapter(interfaces) as if they had been just
+ * discovered and initializes them from scratch.
+ */
+
+ mwifiex_sdio_remove(func);
+
+ /* power cycle the adapter */
+ sdio_claim_host(func);
+ mmc_hw_reset(func->card->host);
+ sdio_release_host(func);
+
+ mwifiex_sdio_probe(func, device_id);
+}
+
+static struct mwifiex_adapter *save_adapter;
+static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ /* TODO card pointer is unprotected. If the adapter is removed
+ * physically, sdio core might trigger mwifiex_sdio_remove, before this
+ * workqueue is run, which will destroy the adapter struct. When this
+ * workqueue eventually exceutes it will dereference an invalid adapter
+ * pointer
+ */
+ mwifiex_recreate_adapter(card);
+}
+
+/* This function read/write firmware */
+static enum
+rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter,
+ u8 doneflag)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret, tries;
+ u8 ctrl_data = 0;
+
+ sdio_writeb(card->func, card->reg->fw_dump_host_ready,
+ card->reg->fw_dump_ctrl, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
+ &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "SDIO read err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ if (ctrl_data == FW_DUMP_DONE)
+ break;
+ if (doneflag && ctrl_data == doneflag)
+ return RDWR_STATUS_DONE;
+ if (ctrl_data != card->reg->fw_dump_host_ready) {
+ mwifiex_dbg(adapter, WARN,
+ "The ctrl reg was changed, re-try again\n");
+ sdio_writeb(card->func, card->reg->fw_dump_host_ready,
+ card->reg->fw_dump_ctrl, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "SDIO write err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ }
+ usleep_range(100, 200);
+ }
+ if (ctrl_data == card->reg->fw_dump_host_ready) {
+ mwifiex_dbg(adapter, ERROR,
+ "Fail to pull ctrl_data\n");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ return RDWR_STATUS_SUCCESS;
+}
+
+/* This function dump firmware memory to file */
+static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+ unsigned int reg, reg_start, reg_end;
+ u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
+ enum rdwr_status stat;
+ u32 memory_size;
+
+ if (!card->can_dump_fw)
+ return;
+
+ for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ mwifiex_pm_wakeup_card(adapter);
+ sdio_claim_host(card->func);
+
+ mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n");
+
+ stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg = card->reg->fw_dump_start;
+ /* Read the number of the memories which will dump */
+ dump_num = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n");
+ goto done;
+ }
+
+ /* Read the length of every memory which will dump */
+ for (idx = 0; idx < dump_num; idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ memory_size = 0;
+ reg = card->reg->fw_dump_start;
+ for (i = 0; i < 4; i++) {
+ read_reg = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "SDIO read err\n");
+ goto done;
+ }
+ memory_size |= (read_reg << i*8);
+ reg++;
+ }
+
+ if (memory_size == 0) {
+ mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n");
+ ret = mwifiex_write_reg(adapter,
+ card->reg->fw_dump_ctrl,
+ FW_DUMP_READ_DONE);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "SDIO write err\n");
+ return;
+ }
+ break;
+ }
+
+ mwifiex_dbg(adapter, DUMP,
+ "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
+ entry->mem_ptr = vmalloc(memory_size + 1);
+ entry->mem_size = memory_size;
+ if (!entry->mem_ptr) {
+ mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n",
+ entry->mem_name);
+ goto done;
+ }
+ dbg_ptr = entry->mem_ptr;
+ end_ptr = dbg_ptr + memory_size;
+
+ doneflag = entry->done_flag;
+ mwifiex_dbg(adapter, DUMP,
+ "Start %s output, please wait...\n",
+ entry->mem_name);
+
+ do {
+ stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg_start = card->reg->fw_dump_start;
+ reg_end = card->reg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ *dbg_ptr = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ if (dbg_ptr < end_ptr)
+ dbg_ptr++;
+ else
+ mwifiex_dbg(adapter, ERROR,
+ "Allocated buf not enough\n");
+ }
+
+ if (stat != RDWR_STATUS_DONE)
+ continue;
+
+ mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n",
+ entry->mem_name, dbg_ptr - entry->mem_ptr);
+ break;
+ } while (1);
+ }
+ mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n");
+
+done:
+ sdio_release_host(card->func);
+}
+
+static void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct memory_type_mapping *entry = &generic_mem_type_map[0];
+ unsigned int reg, reg_start, reg_end;
+ u8 start_flag = 0, done_flag = 0;
+ u8 *dbg_ptr, *end_ptr;
+ enum rdwr_status stat;
+ int ret = -1, tries;
+
+ if (!card->fw_dump_enh)
+ return;
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+
+ mwifiex_pm_wakeup_card(adapter);
+ sdio_claim_host(card->func);
+
+ mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n");
+
+ stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg_start = card->reg->fw_dump_start;
+ reg_end = card->reg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ start_flag = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ if (start_flag == 0)
+ break;
+ if (tries == MAX_POLL_TRIES) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW not ready to dump\n");
+ ret = -1;
+ goto done;
+ }
+ }
+ usleep_range(100, 200);
+ }
+
+ entry->mem_ptr = vmalloc(0xf0000 + 1);
+ if (!entry->mem_ptr) {
+ ret = -1;
+ goto done;
+ }
+ dbg_ptr = entry->mem_ptr;
+ entry->mem_size = 0xf0000;
+ end_ptr = dbg_ptr + entry->mem_size;
+
+ done_flag = entry->done_flag;
+ mwifiex_dbg(adapter, DUMP,
+ "Start %s output, please wait...\n", entry->mem_name);
+
+ while (true) {
+ stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ *dbg_ptr = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ dbg_ptr++;
+ if (dbg_ptr >= end_ptr) {
+ u8 *tmp_ptr;
+
+ tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1);
+ if (!tmp_ptr)
+ goto done;
+
+ memcpy(tmp_ptr, entry->mem_ptr,
+ entry->mem_size);
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = tmp_ptr;
+ tmp_ptr = NULL;
+ dbg_ptr = entry->mem_ptr + entry->mem_size;
+ entry->mem_size += 0x4000;
+ end_ptr = entry->mem_ptr + entry->mem_size;
+ }
+ }
+ if (stat == RDWR_STATUS_DONE) {
+ entry->mem_size = dbg_ptr - entry->mem_ptr;
+ mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n",
+ entry->mem_name, entry->mem_size);
+ ret = 0;
+ break;
+ }
+ }
+ mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n");
+
+done:
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "firmware dump failed\n");
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+ sdio_release_host(card->func);
+}
+
+static void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ mwifiex_drv_info_dump(adapter);
+ if (card->fw_dump_enh)
+ mwifiex_sdio_generic_fw_dump(adapter);
+ else
+ mwifiex_sdio_fw_dump(adapter);
+ mwifiex_upload_device_dump(adapter);
+}
+
+static void mwifiex_sdio_work(struct work_struct *work)
+{
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP,
+ &iface_work_flags))
+ mwifiex_sdio_device_dump_work(save_adapter);
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
+ &iface_work_flags))
+ mwifiex_sdio_card_reset_work(save_adapter);
+}
+
+static DECLARE_WORK(sdio_work, mwifiex_sdio_work);
+/* This function resets the card */
+static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter)
+{
+ save_adapter = adapter;
+ if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags))
+ return;
+
+ set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags);
+
+ schedule_work(&sdio_work);
+}
+
+/* This function dumps FW information */
+static void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter)
+{
+ save_adapter = adapter;
+ if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags))
+ return;
+
+ set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags);
+ schedule_work(&sdio_work);
+}
+
+/* Function to dump SDIO function registers and SDIO scratch registers in case
+ * of FW crash
+ */
+static int
+mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf)
+{
+ char *p = drv_buf;
+ struct sdio_mmc_card *cardp = adapter->card;
+ int ret = 0;
+ u8 count, func, data, index = 0, size = 0;
+ u8 reg, reg_start, reg_end;
+ char buf[256], *ptr;
+
+ if (!p)
+ return 0;
+
+ mwifiex_dbg(adapter, MSG, "SDIO register dump start\n");
+
+ mwifiex_pm_wakeup_card(adapter);
+
+ sdio_claim_host(cardp->func);
+
+ for (count = 0; count < 5; count++) {
+ memset(buf, 0, sizeof(buf));
+ ptr = buf;
+
+ switch (count) {
+ case 0:
+ /* Read the registers of SDIO function0 */
+ func = count;
+ reg_start = 0;
+ reg_end = 9;
+ break;
+ case 1:
+ /* Read the registers of SDIO function1 */
+ func = count;
+ reg_start = cardp->reg->func1_dump_reg_start;
+ reg_end = cardp->reg->func1_dump_reg_end;
+ break;
+ case 2:
+ index = 0;
+ func = 1;
+ reg_start = cardp->reg->func1_spec_reg_table[index++];
+ size = cardp->reg->func1_spec_reg_num;
+ reg_end = cardp->reg->func1_spec_reg_table[size-1];
+ break;
+ default:
+ /* Read the scratch registers of SDIO function1 */
+ if (count == 4)
+ mdelay(100);
+ func = 1;
+ reg_start = cardp->reg->func1_scratch_reg;
+ reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE;
+ }
+
+ if (count != 2)
+ ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
+ func, reg_start, reg_end);
+ else
+ ptr += sprintf(ptr, "SDIO Func%d: ", func);
+
+ for (reg = reg_start; reg <= reg_end;) {
+ if (func == 0)
+ data = sdio_f0_readb(cardp->func, reg, &ret);
+ else
+ data = sdio_readb(cardp->func, reg, &ret);
+
+ if (count == 2)
+ ptr += sprintf(ptr, "(%#x) ", reg);
+ if (!ret) {
+ ptr += sprintf(ptr, "%02x ", data);
+ } else {
+ ptr += sprintf(ptr, "ERR");
+ break;
+ }
+
+ if (count == 2 && reg < reg_end)
+ reg = cardp->reg->func1_spec_reg_table[index++];
+ else
+ reg++;
+ }
+
+ mwifiex_dbg(adapter, MSG, "%s\n", buf);
+ p += sprintf(p, "%s\n", buf);
+ }
+
+ sdio_release_host(cardp->func);
+
+ mwifiex_dbg(adapter, MSG, "SDIO register dump end\n");
+
+ return p - drv_buf;
+}
+
+static struct mwifiex_if_ops sdio_ops = {
+ .init_if = mwifiex_init_sdio,
+ .cleanup_if = mwifiex_cleanup_sdio,
+ .check_fw_status = mwifiex_check_fw_status,
+ .prog_fw = mwifiex_prog_fw_w_helper,
+ .register_dev = mwifiex_register_dev,
+ .unregister_dev = mwifiex_unregister_dev,
+ .enable_int = mwifiex_sdio_enable_host_int,
+ .disable_int = mwifiex_sdio_disable_host_int,
+ .process_int_status = mwifiex_process_int_status,
+ .host_to_card = mwifiex_sdio_host_to_card,
+ .wakeup = mwifiex_pm_wakeup_card,
+ .wakeup_complete = mwifiex_pm_wakeup_card_complete,
+
+ /* SDIO specific */
+ .update_mp_end_port = mwifiex_update_mp_end_port,
+ .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf,
+ .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
+ .event_complete = mwifiex_sdio_event_complete,
+ .card_reset = mwifiex_sdio_card_reset,
+ .reg_dump = mwifiex_sdio_reg_dump,
+ .device_dump = mwifiex_sdio_device_dump,
+ .deaggr_pkt = mwifiex_deaggr_sdio_pkt,
+};
+
+/*
+ * This function initializes the SDIO driver.
+ *
+ * This initiates the semaphore and registers the device with
+ * SDIO bus.
+ */
+static int
+mwifiex_sdio_init_module(void)
+{
+ sema_init(&add_remove_card_sem, 1);
+
+ /* Clear the flag in case user removes the card. */
+ user_rmmod = 0;
+
+ return sdio_register_driver(&mwifiex_sdio);
+}
+
+/*
+ * This function cleans up the SDIO driver.
+ *
+ * The following major steps are followed for cleanup -
+ * - Resume the device if its suspended
+ * - Disconnect the device if connected
+ * - Shutdown the firmware
+ * - Unregister the device from SDIO bus.
+ */
+static void
+mwifiex_sdio_cleanup_module(void)
+{
+ if (!down_interruptible(&add_remove_card_sem))
+ up(&add_remove_card_sem);
+
+ /* Set the flag as user is removing this module. */
+ user_rmmod = 1;
+ cancel_work_sync(&sdio_work);
+
+ sdio_unregister_driver(&mwifiex_sdio);
+}
+
+module_init(mwifiex_sdio_init_module);
+module_exit(mwifiex_sdio_cleanup_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION);
+MODULE_VERSION(SDIO_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
index b9fbc5cf6262..b9fbc5cf6262 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
index e486867a4c67..e486867a4c67 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
index 9ac7aa2431b4..9ac7aa2431b4 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c
index ff3ee9dfbbd5..ff3ee9dfbbd5 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
new file mode 100644
index 000000000000..6a4fc5d183cf
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -0,0 +1,1452 @@
+/*
+ * Marvell Wireless LAN device driver: functions for station ioctl
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "cfg80211.h"
+
+static int disconnect_on_suspend;
+module_param(disconnect_on_suspend, int, 0644);
+
+/*
+ * Copies the multicast address list from device to driver.
+ *
+ * This function does not validate the destination memory for
+ * size, and the calling function must ensure enough memory is
+ * available.
+ */
+int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
+ struct net_device *dev)
+{
+ int i = 0;
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, dev)
+ memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
+
+ return i;
+}
+
+/*
+ * Wait queue completion handler.
+ *
+ * This function waits on a cmd wait queue. It also cancels the pending
+ * request after waking up, in case of errors.
+ */
+int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_queued)
+{
+ int status;
+
+ /* Wait for completion */
+ status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
+ *(cmd_queued->condition),
+ (12 * HZ));
+ if (status <= 0) {
+ if (status == 0)
+ status = -ETIMEDOUT;
+ mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
+ status);
+ mwifiex_cancel_all_pending_cmd(adapter);
+ return status;
+ }
+
+ status = adapter->cmd_wait_q.status;
+ adapter->cmd_wait_q.status = 0;
+
+ return status;
+}
+
+/*
+ * This function prepares the correct firmware command and
+ * issues it to set the multicast list.
+ *
+ * This function can be used to enable promiscuous mode, or enable all
+ * multicast packets, or to enable selective multicast.
+ */
+int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
+ struct mwifiex_multicast_list *mcast_list)
+{
+ int ret = 0;
+ u16 old_pkt_filter;
+
+ old_pkt_filter = priv->curr_pkt_filter;
+
+ if (mcast_list->mode == MWIFIEX_PROMISC_MODE) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: Enable Promiscuous mode\n");
+ priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
+ priv->curr_pkt_filter &=
+ ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
+ } else {
+ /* Multicast */
+ priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
+ if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: Enabling All Multicast!\n");
+ priv->curr_pkt_filter |=
+ HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
+ } else {
+ priv->curr_pkt_filter &=
+ ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: Set multicast list=%d\n",
+ mcast_list->num_multicast_addr);
+ /* Send multicast addresses to firmware */
+ ret = mwifiex_send_cmd(priv,
+ HostCmd_CMD_MAC_MULTICAST_ADR,
+ HostCmd_ACT_GEN_SET, 0,
+ mcast_list, false);
+ }
+ }
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
+ old_pkt_filter, priv->curr_pkt_filter);
+ if (old_pkt_filter != priv->curr_pkt_filter) {
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET,
+ 0, &priv->curr_pkt_filter, false);
+ }
+
+ return ret;
+}
+
+/*
+ * This function fills bss descriptor structure using provided
+ * information.
+ * beacon_ie buffer is allocated in this function. It is caller's
+ * responsibility to free the memory.
+ */
+int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
+ struct cfg80211_bss *bss,
+ struct mwifiex_bssdescriptor *bss_desc)
+{
+ u8 *beacon_ie;
+ size_t beacon_ie_len;
+ struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
+ const struct cfg80211_bss_ies *ies;
+
+ rcu_read_lock();
+ ies = rcu_dereference(bss->ies);
+ beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
+ beacon_ie_len = ies->len;
+ bss_desc->timestamp = ies->tsf;
+ rcu_read_unlock();
+
+ if (!beacon_ie) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ " failed to alloc beacon_ie\n");
+ return -ENOMEM;
+ }
+
+ memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
+ bss_desc->rssi = bss->signal;
+ /* The caller of this function will free beacon_ie */
+ bss_desc->beacon_buf = beacon_ie;
+ bss_desc->beacon_buf_size = beacon_ie_len;
+ bss_desc->beacon_period = bss->beacon_interval;
+ bss_desc->cap_info_bitmap = bss->capability;
+ bss_desc->bss_band = bss_priv->band;
+ bss_desc->fw_tsf = bss_priv->fw_tsf;
+ if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: InterpretIE: AP WEP enabled\n");
+ bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
+ } else {
+ bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
+ }
+ if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS)
+ bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
+ else
+ bss_desc->bss_mode = NL80211_IFTYPE_STATION;
+
+ /* Disable 11ac by default. Enable it only where there
+ * exist VHT_CAP IE in AP beacon
+ */
+ 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);
+}
+
+void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
+{
+ if (priv->adapter->dt_node) {
+ char txpwr[] = {"marvell,00_txpwrlimit"};
+
+ memcpy(&txpwr[8], priv->adapter->country_code, 2);
+ mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr);
+ }
+}
+
+static int mwifiex_process_country_ie(struct mwifiex_private *priv,
+ struct cfg80211_bss *bss)
+{
+ const u8 *country_ie;
+ u8 country_ie_len;
+ struct mwifiex_802_11d_domain_reg *domain_info =
+ &priv->adapter->domain_reg;
+
+ rcu_read_lock();
+ country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+ if (!country_ie) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ country_ie_len = country_ie[1];
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
+ rcu_read_unlock();
+ mwifiex_dbg(priv->adapter, INFO,
+ "11D: skip setting domain info in FW\n");
+ return 0;
+ }
+ memcpy(priv->adapter->country_code, &country_ie[2], 2);
+
+ domain_info->country_code[0] = country_ie[2];
+ domain_info->country_code[1] = country_ie[3];
+ domain_info->country_code[2] = ' ';
+
+ country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
+
+ domain_info->no_of_triplet =
+ country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
+
+ memcpy((u8 *)domain_info->triplet,
+ &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
+
+ rcu_read_unlock();
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
+ HostCmd_ACT_GEN_SET, 0, NULL, false)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "11D: setting domain info in FW fail\n");
+ return -1;
+ }
+
+ mwifiex_dnld_txpwr_table(priv);
+
+ return 0;
+}
+
+/*
+ * In Ad-Hoc mode, the IBSS is created if not found in scan list.
+ * In both Ad-Hoc and infra mode, an deauthentication is performed
+ * first.
+ */
+int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
+ struct cfg80211_ssid *req_ssid)
+{
+ int ret;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_bssdescriptor *bss_desc = NULL;
+
+ priv->scan_block = false;
+
+ if (bss) {
+ if (adapter->region_code == 0x00)
+ mwifiex_process_country_ie(priv, bss);
+
+ /* Allocate and fill new bss descriptor */
+ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
+ GFP_KERNEL);
+ if (!bss_desc)
+ return -ENOMEM;
+
+ ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
+ if (ret)
+ goto done;
+ }
+
+ if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+ priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
+ u8 config_bands;
+
+ if (!bss_desc)
+ return -1;
+
+ if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
+ HostCmd_SCAN_RADIO_TYPE_BG) {
+ config_bands = BAND_B | BAND_G | BAND_GN;
+ } else {
+ config_bands = BAND_A | BAND_AN;
+ if (adapter->fw_bands & BAND_AAC)
+ config_bands |= BAND_AAC;
+ }
+
+ 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) {
+ mwifiex_dbg(adapter, ERROR,
+ "Attempt to reconnect on csa closed chan(%d)\n",
+ bss_desc->channel);
+ goto done;
+ }
+
+ mwifiex_dbg(adapter, INFO,
+ "info: SSID found in scan list ...\t"
+ "associating...\n");
+
+ mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+
+ /* Clear any past association response stored for
+ * application retrieval */
+ priv->assoc_rsp_size = 0;
+ ret = mwifiex_associate(priv, bss_desc);
+
+ /* If auth type is auto and association fails using open mode,
+ * try to connect using shared mode */
+ if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
+ priv->sec_info.is_authtype_auto &&
+ priv->sec_info.wep_enabled) {
+ priv->sec_info.authentication_mode =
+ NL80211_AUTHTYPE_SHARED_KEY;
+ ret = mwifiex_associate(priv, bss_desc);
+ }
+
+ if (bss)
+ cfg80211_put_bss(priv->adapter->wiphy, bss);
+ } else {
+ /* Adhoc mode */
+ /* If the requested SSID matches current SSID, return */
+ if (bss_desc && bss_desc->ssid.ssid_len &&
+ (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor.
+ ssid, &bss_desc->ssid))) {
+ ret = 0;
+ goto done;
+ }
+
+ priv->adhoc_is_link_sensed = false;
+
+ ret = mwifiex_check_network_compatibility(priv, bss_desc);
+
+ mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+
+ if (!ret) {
+ mwifiex_dbg(adapter, INFO,
+ "info: network found in scan\t"
+ " list. Joining...\n");
+ ret = mwifiex_adhoc_join(priv, bss_desc);
+ if (bss)
+ cfg80211_put_bss(priv->adapter->wiphy, bss);
+ } else {
+ mwifiex_dbg(adapter, INFO,
+ "info: Network not found in\t"
+ "the list, creating adhoc with ssid = %s\n",
+ req_ssid->ssid);
+ ret = mwifiex_adhoc_start(priv, req_ssid);
+ }
+ }
+
+done:
+ /* beacon_ie buffer was allocated in function
+ * mwifiex_fill_new_bss_desc(). Free it now.
+ */
+ if (bss_desc)
+ kfree(bss_desc->beacon_buf);
+ kfree(bss_desc);
+ return ret;
+}
+
+/*
+ * IOCTL request handler to set host sleep configuration.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ */
+int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
+ int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
+
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int status = 0;
+ u32 prev_cond = 0;
+
+ if (!hs_cfg)
+ return -ENOMEM;
+
+ switch (action) {
+ case HostCmd_ACT_GEN_SET:
+ if (adapter->pps_uapsd_mode) {
+ mwifiex_dbg(adapter, INFO,
+ "info: Host Sleep IOCTL\t"
+ "is blocked in UAPSD/PPS mode\n");
+ status = -1;
+ break;
+ }
+ if (hs_cfg->is_invoke_hostcmd) {
+ if (hs_cfg->conditions == HS_CFG_CANCEL) {
+ if (!adapter->is_hs_configured)
+ /* Already cancelled */
+ break;
+ /* Save previous condition */
+ prev_cond = le32_to_cpu(adapter->hs_cfg
+ .conditions);
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(hs_cfg->conditions);
+ } else if (hs_cfg->conditions) {
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(hs_cfg->conditions);
+ adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
+ if (hs_cfg->gap)
+ adapter->hs_cfg.gap = (u8)hs_cfg->gap;
+ } else if (adapter->hs_cfg.conditions ==
+ cpu_to_le32(HS_CFG_CANCEL)) {
+ /* Return failure if no parameters for HS
+ enable */
+ status = -1;
+ break;
+ }
+
+ status = mwifiex_send_cmd(priv,
+ HostCmd_CMD_802_11_HS_CFG_ENH,
+ HostCmd_ACT_GEN_SET, 0,
+ &adapter->hs_cfg,
+ cmd_type == MWIFIEX_SYNC_CMD);
+
+ if (hs_cfg->conditions == HS_CFG_CANCEL)
+ /* Restore previous condition */
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(prev_cond);
+ } else {
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(hs_cfg->conditions);
+ adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
+ adapter->hs_cfg.gap = (u8)hs_cfg->gap;
+ }
+ break;
+ case HostCmd_ACT_GEN_GET:
+ hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
+ hs_cfg->gpio = adapter->hs_cfg.gpio;
+ hs_cfg->gap = adapter->hs_cfg.gap;
+ break;
+ default:
+ status = -1;
+ break;
+ }
+
+ return status;
+}
+
+/*
+ * Sends IOCTL request to cancel the existing Host Sleep configuration.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type)
+{
+ struct mwifiex_ds_hs_cfg hscfg;
+
+ hscfg.conditions = HS_CFG_CANCEL;
+ hscfg.is_invoke_hostcmd = true;
+
+ return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
+ cmd_type, &hscfg);
+}
+EXPORT_SYMBOL_GPL(mwifiex_cancel_hs);
+
+/*
+ * Sends IOCTL request to cancel the existing Host Sleep configuration.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_ds_hs_cfg hscfg;
+ struct mwifiex_private *priv;
+ int i;
+
+ if (disconnect_on_suspend) {
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv)
+ mwifiex_deauthenticate(priv, NULL);
+ }
+ }
+
+ if (adapter->hs_activated) {
+ mwifiex_dbg(adapter, CMD,
+ "cmd: HS Already activated\n");
+ return true;
+ }
+
+ adapter->hs_activate_wait_q_woken = false;
+
+ memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
+ hscfg.is_invoke_hostcmd = true;
+
+ adapter->hs_enabling = true;
+ mwifiex_cancel_all_pending_cmd(adapter);
+
+ if (mwifiex_set_hs_params(mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_STA),
+ HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD,
+ &hscfg)) {
+ mwifiex_dbg(adapter, ERROR,
+ "IOCTL request HS enable failed\n");
+ return false;
+ }
+
+ if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
+ adapter->hs_activate_wait_q_woken,
+ (10 * HZ)) <= 0) {
+ mwifiex_dbg(adapter, ERROR,
+ "hs_activate_wait_q terminated\n");
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(mwifiex_enable_hs);
+
+/*
+ * IOCTL request handler to get BSS information.
+ *
+ * This function collates the information from different driver structures
+ * to send to the user.
+ */
+int mwifiex_get_bss_info(struct mwifiex_private *priv,
+ struct mwifiex_bss_info *info)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_bssdescriptor *bss_desc;
+
+ if (!info)
+ return -1;
+
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
+
+ info->bss_mode = priv->bss_mode;
+
+ memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
+
+ memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
+
+ info->bss_chan = bss_desc->channel;
+
+ memcpy(info->country_code, adapter->country_code,
+ IEEE80211_COUNTRY_STRING_LEN);
+
+ info->media_connected = priv->media_connected;
+
+ info->max_power_level = priv->max_tx_power_level;
+ info->min_power_level = priv->min_tx_power_level;
+
+ info->adhoc_state = priv->adhoc_state;
+
+ info->bcn_nf_last = priv->bcn_nf_last;
+
+ if (priv->sec_info.wep_enabled)
+ info->wep_status = true;
+ else
+ info->wep_status = false;
+
+ info->is_hs_configured = adapter->is_hs_configured;
+ info->is_deep_sleep = adapter->is_deep_sleep;
+
+ return 0;
+}
+
+/*
+ * The function disables auto deep sleep mode.
+ */
+int mwifiex_disable_auto_ds(struct mwifiex_private *priv)
+{
+ struct mwifiex_ds_auto_ds auto_ds;
+
+ auto_ds.auto_ds = DEEP_SLEEP_OFF;
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
+}
+EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds);
+
+/*
+ * Sends IOCTL request to get the data rate.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate)
+{
+ int ret;
+
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
+ HostCmd_ACT_GEN_GET, 0, NULL, true);
+
+ if (!ret) {
+ if (priv->is_data_rate_auto)
+ *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate,
+ priv->tx_htinfo);
+ else
+ *rate = priv->data_rate;
+ }
+
+ return ret;
+}
+
+/*
+ * IOCTL request handler to set tx power configuration.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ *
+ * For non-auto power mode, all the following power groups are set -
+ * - Modulation class HR/DSSS
+ * - Modulation class OFDM
+ * - Modulation class HTBW20
+ * - Modulation class HTBW40
+ */
+int mwifiex_set_tx_power(struct mwifiex_private *priv,
+ struct mwifiex_power_cfg *power_cfg)
+{
+ int ret;
+ struct host_cmd_ds_txpwr_cfg *txp_cfg;
+ struct mwifiex_types_power_group *pg_tlv;
+ struct mwifiex_power_group *pg;
+ u8 *buf;
+ u16 dbm = 0;
+
+ if (!power_cfg->is_power_auto) {
+ dbm = (u16) power_cfg->power_level;
+ if ((dbm < priv->min_tx_power_level) ||
+ (dbm > priv->max_tx_power_level)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "txpower value %d dBm\t"
+ "is out of range (%d dBm-%d dBm)\n",
+ dbm, priv->min_tx_power_level,
+ priv->max_tx_power_level);
+ return -1;
+ }
+ }
+ buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf;
+ txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
+ if (!power_cfg->is_power_auto) {
+ txp_cfg->mode = cpu_to_le32(1);
+ pg_tlv = (struct mwifiex_types_power_group *)
+ (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
+ pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
+ pg_tlv->length =
+ cpu_to_le16(4 * sizeof(struct mwifiex_power_group));
+ pg = (struct mwifiex_power_group *)
+ (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
+ + sizeof(struct mwifiex_types_power_group));
+ /* Power group for modulation class HR/DSSS */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x03;
+ pg->modulation_class = MOD_CLASS_HR_DSSS;
+ pg->power_step = 0;
+ pg->power_min = (s8) dbm;
+ pg->power_max = (s8) dbm;
+ pg++;
+ /* Power group for modulation class OFDM */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x07;
+ pg->modulation_class = MOD_CLASS_OFDM;
+ pg->power_step = 0;
+ pg->power_min = (s8) dbm;
+ pg->power_max = (s8) dbm;
+ pg++;
+ /* Power group for modulation class HTBW20 */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x20;
+ pg->modulation_class = MOD_CLASS_HT;
+ pg->power_step = 0;
+ pg->power_min = (s8) dbm;
+ pg->power_max = (s8) dbm;
+ pg->ht_bandwidth = HT_BW_20;
+ pg++;
+ /* Power group for modulation class HTBW40 */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x20;
+ pg->modulation_class = MOD_CLASS_HT;
+ pg->power_step = 0;
+ pg->power_min = (s8) dbm;
+ pg->power_max = (s8) dbm;
+ pg->ht_bandwidth = HT_BW_40;
+ }
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG,
+ HostCmd_ACT_GEN_SET, 0, buf, true);
+
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * IOCTL request handler to get power save mode.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ */
+int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode)
+{
+ int ret;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u16 sub_cmd;
+
+ if (*ps_mode)
+ adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
+ else
+ adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
+ sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ sub_cmd, BITMAP_STA_PS, NULL, true);
+ if ((!ret) && (sub_cmd == DIS_AUTO_PS))
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ GET_PS, 0, NULL, false);
+
+ return ret;
+}
+
+/*
+ * IOCTL request handler to set/reset WPA IE.
+ *
+ * The supplied WPA IE is treated as a opaque buffer. Only the first field
+ * is checked to determine WPA version. If buffer length is zero, the existing
+ * WPA IE is reset.
+ */
+static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv,
+ u8 *ie_data_ptr, u16 ie_len)
+{
+ if (ie_len) {
+ if (ie_len > sizeof(priv->wpa_ie)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "failed to copy WPA IE, too big\n");
+ return -1;
+ }
+ memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
+ priv->wpa_ie_len = ie_len;
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: Set Wpa_ie_len=%d IE=%#x\n",
+ priv->wpa_ie_len, priv->wpa_ie[0]);
+
+ if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
+ priv->sec_info.wpa_enabled = true;
+ } else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
+ priv->sec_info.wpa2_enabled = true;
+ } else {
+ priv->sec_info.wpa_enabled = false;
+ priv->sec_info.wpa2_enabled = false;
+ }
+ } else {
+ memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
+ priv->wpa_ie_len = 0;
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: reset wpa_ie_len=%d IE=%#x\n",
+ priv->wpa_ie_len, priv->wpa_ie[0]);
+ priv->sec_info.wpa_enabled = false;
+ priv->sec_info.wpa2_enabled = false;
+ }
+
+ return 0;
+}
+
+/*
+ * IOCTL request handler to set/reset WAPI IE.
+ *
+ * The supplied WAPI IE is treated as a opaque buffer. Only the first field
+ * is checked to internally enable WAPI. If buffer length is zero, the existing
+ * WAPI IE is reset.
+ */
+static int mwifiex_set_wapi_ie(struct mwifiex_private *priv,
+ u8 *ie_data_ptr, u16 ie_len)
+{
+ if (ie_len) {
+ if (ie_len > sizeof(priv->wapi_ie)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "info: failed to copy WAPI IE, too big\n");
+ return -1;
+ }
+ memcpy(priv->wapi_ie, ie_data_ptr, ie_len);
+ priv->wapi_ie_len = ie_len;
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: Set wapi_ie_len=%d IE=%#x\n",
+ priv->wapi_ie_len, priv->wapi_ie[0]);
+
+ if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY)
+ priv->sec_info.wapi_enabled = true;
+ } else {
+ memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie));
+ priv->wapi_ie_len = ie_len;
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: Reset wapi_ie_len=%d IE=%#x\n",
+ priv->wapi_ie_len, priv->wapi_ie[0]);
+ priv->sec_info.wapi_enabled = false;
+ }
+ return 0;
+}
+
+/*
+ * IOCTL request handler to set/reset WPS IE.
+ *
+ * The supplied WPS IE is treated as a opaque buffer. Only the first field
+ * is checked to internally enable WPS. If buffer length is zero, the existing
+ * WPS IE is reset.
+ */
+static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
+ u8 *ie_data_ptr, u16 ie_len)
+{
+ if (ie_len) {
+ if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "info: failed to copy WPS IE, too big\n");
+ return -1;
+ }
+
+ priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
+ if (!priv->wps_ie)
+ return -ENOMEM;
+
+ memcpy(priv->wps_ie, ie_data_ptr, ie_len);
+ priv->wps_ie_len = ie_len;
+ mwifiex_dbg(priv->adapter, CMD,
+ "cmd: Set wps_ie_len=%d IE=%#x\n",
+ priv->wps_ie_len, priv->wps_ie[0]);
+ } else {
+ kfree(priv->wps_ie);
+ priv->wps_ie_len = ie_len;
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: Reset wps_ie_len=%d\n", priv->wps_ie_len);
+ }
+ return 0;
+}
+
+/*
+ * IOCTL request handler to set WAPI key.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ */
+static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
+ struct mwifiex_ds_encrypt_key *encrypt_key)
+{
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED,
+ encrypt_key, true);
+}
+
+/*
+ * IOCTL request handler to set WEP network key.
+ *
+ * This function prepares the correct firmware command and
+ * issues it, after validation checks.
+ */
+static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
+ struct mwifiex_ds_encrypt_key *encrypt_key)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int ret;
+ struct mwifiex_wep_key *wep_key;
+ int index;
+
+ if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
+ priv->wep_key_curr_index = 0;
+ wep_key = &priv->wep_key[priv->wep_key_curr_index];
+ index = encrypt_key->key_index;
+ if (encrypt_key->key_disable) {
+ priv->sec_info.wep_enabled = 0;
+ } else if (!encrypt_key->key_len) {
+ /* Copy the required key as the current key */
+ wep_key = &priv->wep_key[index];
+ if (!wep_key->key_length) {
+ mwifiex_dbg(adapter, ERROR,
+ "key not set, so cannot enable it\n");
+ return -1;
+ }
+
+ if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) {
+ memcpy(encrypt_key->key_material,
+ wep_key->key_material, wep_key->key_length);
+ encrypt_key->key_len = wep_key->key_length;
+ }
+
+ priv->wep_key_curr_index = (u16) index;
+ priv->sec_info.wep_enabled = 1;
+ } else {
+ wep_key = &priv->wep_key[index];
+ memset(wep_key, 0, sizeof(struct mwifiex_wep_key));
+ /* Copy the key in the driver */
+ memcpy(wep_key->key_material,
+ encrypt_key->key_material,
+ encrypt_key->key_len);
+ wep_key->key_index = index;
+ wep_key->key_length = encrypt_key->key_len;
+ priv->sec_info.wep_enabled = 1;
+ }
+ if (wep_key->key_length) {
+ void *enc_key;
+
+ if (encrypt_key->key_disable) {
+ memset(&priv->wep_key[index], 0,
+ sizeof(struct mwifiex_wep_key));
+ goto done;
+ }
+
+ if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
+ enc_key = encrypt_key;
+ else
+ enc_key = NULL;
+
+ /* Send request to firmware */
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET, 0, enc_key, false);
+ if (ret)
+ return ret;
+ }
+
+done:
+ if (priv->sec_info.wep_enabled)
+ priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
+ else
+ priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
+
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true);
+
+ return ret;
+}
+
+/*
+ * IOCTL request handler to set WPA key.
+ *
+ * This function prepares the correct firmware command and
+ * issues it, after validation checks.
+ *
+ * Current driver only supports key length of up to 32 bytes.
+ *
+ * This function can also be used to disable a currently set key.
+ */
+static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv,
+ struct mwifiex_ds_encrypt_key *encrypt_key)
+{
+ int ret;
+ u8 remove_key = false;
+ struct host_cmd_ds_802_11_key_material *ibss_key;
+
+ /* Current driver only supports key length of up to 32 bytes */
+ if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "key length too long\n");
+ return -1;
+ }
+
+ if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
+ /*
+ * IBSS/WPA-None uses only one key (Group) for both receiving
+ * and sending unicast and multicast packets.
+ */
+ /* Send the key as PTK to firmware */
+ encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET,
+ KEY_INFO_ENABLED, encrypt_key, false);
+ if (ret)
+ return ret;
+
+ ibss_key = &priv->aes_key;
+ memset(ibss_key, 0,
+ sizeof(struct host_cmd_ds_802_11_key_material));
+ /* Copy the key in the driver */
+ memcpy(ibss_key->key_param_set.key, encrypt_key->key_material,
+ encrypt_key->key_len);
+ memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len,
+ sizeof(ibss_key->key_param_set.key_len));
+ ibss_key->key_param_set.key_type_id
+ = cpu_to_le16(KEY_TYPE_ID_TKIP);
+ ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED);
+
+ /* Send the key as GTK to firmware */
+ encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST;
+ }
+
+ if (!encrypt_key->key_index)
+ encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
+
+ if (remove_key)
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET,
+ !KEY_INFO_ENABLED, encrypt_key, true);
+ else
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET,
+ KEY_INFO_ENABLED, encrypt_key, true);
+
+ return ret;
+}
+
+/*
+ * IOCTL request handler to set/get network keys.
+ *
+ * This is a generic key handling function which supports WEP, WPA
+ * and WAPI.
+ */
+static int
+mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv,
+ struct mwifiex_ds_encrypt_key *encrypt_key)
+{
+ int status;
+
+ if (encrypt_key->is_wapi_key)
+ status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key);
+ else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
+ status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key);
+ else
+ status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key);
+ return status;
+}
+
+/*
+ * This function returns the driver version.
+ */
+int
+mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
+ int max_len)
+{
+ union {
+ __le32 l;
+ u8 c[4];
+ } ver;
+ char fw_ver[32];
+
+ ver.l = cpu_to_le32(adapter->fw_release_number);
+ sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
+
+ snprintf(version, max_len, driver_version, fw_ver);
+
+ mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version);
+
+ return 0;
+}
+
+/*
+ * Sends IOCTL request to set encoding parameters.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
+ const u8 *key, int key_len, u8 key_index,
+ const u8 *mac_addr, int disable)
+{
+ struct mwifiex_ds_encrypt_key encrypt_key;
+
+ memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key));
+ encrypt_key.key_len = key_len;
+ encrypt_key.key_index = key_index;
+
+ if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ encrypt_key.is_igtk_key = true;
+
+ if (!disable) {
+ if (key_len)
+ memcpy(encrypt_key.key_material, key, key_len);
+ else
+ encrypt_key.is_current_wep_key = true;
+
+ if (mac_addr)
+ memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
+ if (kp && kp->seq && kp->seq_len) {
+ memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
+ encrypt_key.pn_len = kp->seq_len;
+ encrypt_key.is_rx_seq_valid = true;
+ }
+ } else {
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
+ return 0;
+ encrypt_key.key_disable = true;
+ if (mac_addr)
+ memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
+ }
+
+ return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key);
+}
+
+/*
+ * Sends IOCTL request to get extended version.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+mwifiex_get_ver_ext(struct mwifiex_private *priv)
+{
+ struct mwifiex_ver_ext ver_ext;
+
+ memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext));
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
+ HostCmd_ACT_GEN_GET, 0, &ver_ext, true))
+ return -1;
+
+ return 0;
+}
+
+int
+mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
+ struct ieee80211_channel *chan,
+ unsigned int duration)
+{
+ struct host_cmd_ds_remain_on_chan roc_cfg;
+ u8 sc;
+
+ memset(&roc_cfg, 0, sizeof(roc_cfg));
+ roc_cfg.action = cpu_to_le16(action);
+ if (action == HostCmd_ACT_GEN_SET) {
+ roc_cfg.band_cfg = chan->band;
+ sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
+ roc_cfg.band_cfg |= (sc << 2);
+
+ roc_cfg.channel =
+ ieee80211_frequency_to_channel(chan->center_freq);
+ roc_cfg.duration = cpu_to_le32(duration);
+ }
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN,
+ action, 0, &roc_cfg, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "failed to remain on channel\n");
+ return -1;
+ }
+
+ return roc_cfg.status;
+}
+
+/*
+ * Sends IOCTL request to get statistics information.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+mwifiex_get_stats_info(struct mwifiex_private *priv,
+ struct mwifiex_ds_get_stats *log)
+{
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG,
+ HostCmd_ACT_GEN_GET, 0, log, true);
+}
+
+/*
+ * IOCTL request handler to read/write register.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ *
+ * Access to the following registers are supported -
+ * - MAC
+ * - BBP
+ * - RF
+ * - PMIC
+ * - CAU
+ */
+static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv,
+ struct mwifiex_ds_reg_rw *reg_rw,
+ u16 action)
+{
+ u16 cmd_no;
+
+ switch (le32_to_cpu(reg_rw->type)) {
+ case MWIFIEX_REG_MAC:
+ cmd_no = HostCmd_CMD_MAC_REG_ACCESS;
+ break;
+ case MWIFIEX_REG_BBP:
+ cmd_no = HostCmd_CMD_BBP_REG_ACCESS;
+ break;
+ case MWIFIEX_REG_RF:
+ cmd_no = HostCmd_CMD_RF_REG_ACCESS;
+ break;
+ case MWIFIEX_REG_PMIC:
+ cmd_no = HostCmd_CMD_PMIC_REG_ACCESS;
+ break;
+ case MWIFIEX_REG_CAU:
+ cmd_no = HostCmd_CMD_CAU_REG_ACCESS;
+ break;
+ default:
+ return -1;
+ }
+
+ return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
+}
+
+/*
+ * Sends IOCTL request to write to a register.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type,
+ u32 reg_offset, u32 reg_value)
+{
+ struct mwifiex_ds_reg_rw reg_rw;
+
+ reg_rw.type = cpu_to_le32(reg_type);
+ reg_rw.offset = cpu_to_le32(reg_offset);
+ reg_rw.value = cpu_to_le32(reg_value);
+
+ return mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_SET);
+}
+
+/*
+ * Sends IOCTL request to read from a register.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type,
+ u32 reg_offset, u32 *value)
+{
+ int ret;
+ struct mwifiex_ds_reg_rw reg_rw;
+
+ reg_rw.type = cpu_to_le32(reg_type);
+ reg_rw.offset = cpu_to_le32(reg_offset);
+ ret = mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_GET);
+
+ if (ret)
+ goto done;
+
+ *value = le32_to_cpu(reg_rw.value);
+
+done:
+ return ret;
+}
+
+/*
+ * Sends IOCTL request to read from EEPROM.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
+ u8 *value)
+{
+ int ret;
+ struct mwifiex_ds_read_eeprom rd_eeprom;
+
+ rd_eeprom.offset = cpu_to_le16((u16) offset);
+ rd_eeprom.byte_count = cpu_to_le16((u16) bytes);
+
+ /* Send request to firmware */
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS,
+ HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true);
+
+ if (!ret)
+ memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA);
+ return ret;
+}
+
+/*
+ * This function sets a generic IE. In addition to generic IE, it can
+ * also handle WPA, WPA2 and WAPI IEs.
+ */
+static int
+mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
+ u16 ie_len)
+{
+ int ret = 0;
+ struct ieee_types_vendor_header *pvendor_ie;
+ const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
+ const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
+ u16 unparsed_len = ie_len;
+ int find_wpa_ie = 0;
+
+ /* If the passed length is zero, reset the buffer */
+ if (!ie_len) {
+ priv->gen_ie_buf_len = 0;
+ priv->wps.session_enable = false;
+
+ return 0;
+ } else if (!ie_data_ptr) {
+ return -1;
+ }
+ pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
+
+ while (pvendor_ie) {
+ if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
+ /* Test to see if it is a WPA IE, if not, then it is a
+ * gen IE
+ */
+ if (!memcmp(pvendor_ie->oui, wpa_oui,
+ sizeof(wpa_oui))) {
+ find_wpa_ie = 1;
+ break;
+ }
+
+ /* Test to see if it is a WPS IE, if so, enable
+ * wps session flag
+ */
+ if (!memcmp(pvendor_ie->oui, wps_oui,
+ sizeof(wps_oui))) {
+ priv->wps.session_enable = true;
+ mwifiex_dbg(priv->adapter, MSG,
+ "info: WPS Session Enabled.\n");
+ ret = mwifiex_set_wps_ie(priv,
+ (u8 *)pvendor_ie,
+ unparsed_len);
+ }
+ }
+
+ if (pvendor_ie->element_id == WLAN_EID_RSN) {
+ find_wpa_ie = 1;
+ break;
+ }
+
+ if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) {
+ /* IE is a WAPI IE so call set_wapi function */
+ ret = mwifiex_set_wapi_ie(priv, (u8 *)pvendor_ie,
+ unparsed_len);
+ return ret;
+ }
+
+ unparsed_len -= (pvendor_ie->len +
+ sizeof(struct ieee_types_header));
+
+ if (unparsed_len <= sizeof(struct ieee_types_header))
+ pvendor_ie = NULL;
+ else
+ pvendor_ie = (struct ieee_types_vendor_header *)
+ (((u8 *)pvendor_ie) + pvendor_ie->len +
+ sizeof(struct ieee_types_header));
+ }
+
+ if (find_wpa_ie) {
+ /* IE is a WPA/WPA2 IE so call set_wpa function */
+ ret = mwifiex_set_wpa_ie_helper(priv, (u8 *)pvendor_ie,
+ unparsed_len);
+ priv->wps.session_enable = false;
+ return ret;
+ }
+
+ /*
+ * Verify that the passed length is not larger than the
+ * available space remaining in the buffer
+ */
+ if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
+
+ /* Append the passed data to the end of the
+ genIeBuffer */
+ memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr,
+ ie_len);
+ /* Increment the stored buffer length by the
+ size passed */
+ priv->gen_ie_buf_len += ie_len;
+ } else {
+ /* Passed data does not fit in the remaining
+ buffer space */
+ ret = -1;
+ }
+
+ /* Return 0, or -1 for error case */
+ return ret;
+}
+
+/*
+ * IOCTL request handler to set/get generic IE.
+ *
+ * In addition to various generic IEs, this function can also be
+ * used to set the ARP filter.
+ */
+static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv,
+ struct mwifiex_ds_misc_gen_ie *gen_ie,
+ u16 action)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ switch (gen_ie->type) {
+ case MWIFIEX_IE_TYPE_GEN_IE:
+ if (action == HostCmd_ACT_GEN_GET) {
+ gen_ie->len = priv->wpa_ie_len;
+ memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
+ } else {
+ mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data,
+ (u16) gen_ie->len);
+ }
+ break;
+ case MWIFIEX_IE_TYPE_ARP_FILTER:
+ memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
+ if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
+ adapter->arp_filter_size = 0;
+ mwifiex_dbg(adapter, ERROR,
+ "invalid ARP filter size\n");
+ return -1;
+ } else {
+ memcpy(adapter->arp_filter, gen_ie->ie_data,
+ gen_ie->len);
+ adapter->arp_filter_size = gen_ie->len;
+ }
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR, "invalid IE type\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Sends IOCTL request to set a generic IE.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len)
+{
+ struct mwifiex_ds_misc_gen_ie gen_ie;
+
+ if (ie_len > IEEE_MAX_IE_SIZE)
+ return -EFAULT;
+
+ gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE;
+ gen_ie.len = ie_len;
+ memcpy(gen_ie.ie_data, ie, ie_len);
+ if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET))
+ return -EFAULT;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_rx.c b/drivers/net/wireless/marvell/mwifiex/sta_rx.c
new file mode 100644
index 000000000000..00fcbda09349
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/sta_rx.c
@@ -0,0 +1,267 @@
+/*
+ * Marvell Wireless LAN device driver: station RX data handling
+ *
+ * Copyright (C) 2011-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 <uapi/linux/ipv6.h>
+#include <net/ndisc.h>
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "11n_aggr.h"
+#include "11n_rxreorder.h"
+
+/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement
+ * frame. If frame has both source and destination mac address as same, this
+ * function drops such gratuitous frames.
+ */
+static bool
+mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ const struct mwifiex_arp_eth_header *arp;
+ struct ethhdr *eth;
+ struct ipv6hdr *ipv6;
+ struct icmp6hdr *icmpv6;
+
+ eth = (struct ethhdr *)skb->data;
+ switch (ntohs(eth->h_proto)) {
+ case ETH_P_ARP:
+ arp = (void *)(skb->data + sizeof(struct ethhdr));
+ if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
+ arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
+ if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
+ return true;
+ }
+ break;
+ case ETH_P_IPV6:
+ ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
+ icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
+ sizeof(struct ipv6hdr));
+ if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) {
+ if (!memcmp(&ipv6->saddr, &ipv6->daddr,
+ sizeof(struct in6_addr)))
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/*
+ * This function processes the received packet and forwards it
+ * to kernel/upper layer.
+ *
+ * This function parses through the received packet and determines
+ * if it is a debug packet or normal packet.
+ *
+ * For non-debug packets, the function chops off unnecessary leading
+ * header bytes, reconstructs the packet as an ethernet frame or
+ * 802.2/llc/snap frame as required, and sends it to kernel/upper layer.
+ *
+ * The completion callback is called after processing in complete.
+ */
+int mwifiex_process_rx_packet(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ int ret;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ struct rxpd *local_rx_pd;
+ int hdr_chop;
+ struct ethhdr *eth;
+ u16 rx_pkt_off, rx_pkt_len;
+ u8 *offset;
+ u8 adj_rx_rate = 0;
+
+ local_rx_pd = (struct rxpd *) (skb->data);
+
+ rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset);
+ rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length);
+ rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off;
+
+ if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
+ sizeof(bridge_tunnel_header))) ||
+ (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
+ sizeof(rfc1042_header)) &&
+ ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP &&
+ ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) {
+ /*
+ * Replace the 803 header and rfc1042 header (llc/snap) with an
+ * EthernetII header, keep the src/dst and snap_type
+ * (ethertype).
+ * The firmware only passes up SNAP frames converting
+ * all RX Data from 802.11 to 802.2/LLC/SNAP frames.
+ * To create the Ethernet II, just move the src, dst address
+ * right before the snap_type.
+ */
+ eth = (struct ethhdr *)
+ ((u8 *) &rx_pkt_hdr->eth803_hdr
+ + sizeof(rx_pkt_hdr->eth803_hdr) +
+ sizeof(rx_pkt_hdr->rfc1042_hdr)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
+ - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
+
+ memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source,
+ sizeof(eth->h_source));
+ memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
+ sizeof(eth->h_dest));
+
+ /* Chop off the rxpd + the excess memory from the 802.2/llc/snap
+ header that was removed. */
+ hdr_chop = (u8 *) eth - (u8 *) local_rx_pd;
+ } else {
+ /* Chop off the rxpd */
+ hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr -
+ (u8 *) local_rx_pd;
+ }
+
+ /* Chop off the leading header bytes so the it points to the start of
+ either the reconstructed EthII frame or the 802.2/llc/snap frame */
+ skb_pull(skb, hdr_chop);
+
+ if (priv->hs2_enabled &&
+ mwifiex_discard_gratuitous_arp(priv, skb)) {
+ mwifiex_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n");
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
+ offset = (u8 *)local_rx_pd + rx_pkt_off;
+ mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len);
+ }
+
+ priv->rxpd_rate = local_rx_pd->rx_rate;
+
+ priv->rxpd_htinfo = local_rx_pd->ht_info;
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+ GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+ adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate,
+ priv->rxpd_htinfo);
+ mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr,
+ local_rx_pd->nf);
+ }
+
+ ret = mwifiex_recv_packet(priv, skb);
+ if (ret == -1)
+ mwifiex_dbg(priv->adapter, ERROR,
+ "recv packet failed\n");
+
+ return ret;
+}
+
+/*
+ * This function processes the received buffer.
+ *
+ * The function looks into the RxPD and performs sanity tests on the
+ * received buffer to ensure its a valid packet, before processing it
+ * further. If the packet is determined to be aggregated, it is
+ * de-aggregated accordingly. Non-unicast packets are sent directly to
+ * the kernel/upper layers. Unicast packets are handed over to the
+ * Rx reordering routine if 11n is enabled.
+ *
+ * The completion callback is called after processing in complete.
+ */
+int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int ret = 0;
+ struct rxpd *local_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u8 ta[ETH_ALEN];
+ u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
+ struct mwifiex_sta_node *sta_ptr;
+
+ local_rx_pd = (struct rxpd *) (skb->data);
+ rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
+ rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset);
+ rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length);
+ seq_num = le16_to_cpu(local_rx_pd->seq_num);
+
+ rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset;
+
+ if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) {
+ mwifiex_dbg(adapter, ERROR,
+ "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n",
+ skb->len, rx_pkt_offset, rx_pkt_length);
+ priv->stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ if (rx_pkt_type == PKT_TYPE_MGMT) {
+ ret = mwifiex_process_mgmt_packet(priv, skb);
+ if (ret)
+ mwifiex_dbg(adapter, DATA, "Rx of mgmt packet failed");
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ /*
+ * If the packet is not an unicast packet then send the packet
+ * directly to os. Don't pass thru rx reordering
+ */
+ if ((!IS_11N_ENABLED(priv) &&
+ !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) ||
+ !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) {
+ mwifiex_process_rx_packet(priv, skb);
+ return ret;
+ }
+
+ if (mwifiex_queuing_ra_based(priv) ||
+ (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) {
+ memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
+ if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET &&
+ local_rx_pd->priority < MAX_NUM_TID) {
+ sta_ptr = mwifiex_get_sta_entry(priv, ta);
+ if (sta_ptr)
+ sta_ptr->rx_seq[local_rx_pd->priority] =
+ le16_to_cpu(local_rx_pd->seq_num);
+ mwifiex_auto_tdls_update_peer_signal(priv, ta,
+ local_rx_pd->snr,
+ local_rx_pd->nf);
+ }
+ } else {
+ if (rx_pkt_type != PKT_TYPE_BAR)
+ priv->rx_seq[local_rx_pd->priority] = seq_num;
+ memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address,
+ ETH_ALEN);
+ }
+
+ /* Reorder and send to OS */
+ ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority,
+ ta, (u8) rx_pkt_type, skb);
+
+ if (ret || (rx_pkt_type == PKT_TYPE_BAR))
+ dev_kfree_skb_any(skb);
+
+ if (ret)
+ priv->stats.rx_dropped++;
+
+ return ret;
+}
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/marvell/mwifiex/sta_tx.c
index f6683ea6bd5d..f6683ea6bd5d 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_tx.c
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c
index 9275f9c3f869..9275f9c3f869 100644
--- a/drivers/net/wireless/mwifiex/tdls.c
+++ b/drivers/net/wireless/marvell/mwifiex/tdls.c
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c
index bf6182b646a5..bf6182b646a5 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/marvell/mwifiex/txrx.c
diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
new file mode 100644
index 000000000000..e791166d90c4
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
@@ -0,0 +1,885 @@
+/*
+ * Marvell Wireless LAN device driver: AP specific command handling
+ *
+ * Copyright (C) 2012-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "11ac.h"
+
+/* This function parses security related parameters from cfg80211_ap_settings
+ * and sets into FW understandable bss_config structure.
+ */
+int mwifiex_set_secure_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_config,
+ struct cfg80211_ap_settings *params) {
+ int i;
+ struct mwifiex_wep_key wep_key;
+
+ if (!params->privacy) {
+ bss_config->protocol = PROTOCOL_NO_SECURITY;
+ bss_config->key_mgmt = KEY_MGMT_NONE;
+ bss_config->wpa_cfg.length = 0;
+ priv->sec_info.wep_enabled = 0;
+ priv->sec_info.wpa_enabled = 0;
+ priv->sec_info.wpa2_enabled = 0;
+
+ return 0;
+ }
+
+ switch (params->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ bss_config->auth_mode = WLAN_AUTH_OPEN;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ bss_config->auth_mode = WLAN_AUTH_SHARED_KEY;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ bss_config->auth_mode = WLAN_AUTH_LEAP;
+ break;
+ default:
+ bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO;
+ break;
+ }
+
+ bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST;
+
+ for (i = 0; i < params->crypto.n_akm_suites; i++) {
+ switch (params->crypto.akm_suites[i]) {
+ case WLAN_AKM_SUITE_8021X:
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_1) {
+ bss_config->protocol = PROTOCOL_WPA;
+ bss_config->key_mgmt = KEY_MGMT_EAP;
+ }
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_2) {
+ bss_config->protocol |= PROTOCOL_WPA2;
+ bss_config->key_mgmt = KEY_MGMT_EAP;
+ }
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_1) {
+ bss_config->protocol = PROTOCOL_WPA;
+ bss_config->key_mgmt = KEY_MGMT_PSK;
+ }
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_2) {
+ bss_config->protocol |= PROTOCOL_WPA2;
+ bss_config->key_mgmt = KEY_MGMT_PSK;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) {
+ switch (params->crypto.ciphers_pairwise[i]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ bss_config->wpa_cfg.pairwise_cipher_wpa |=
+ CIPHER_TKIP;
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
+ CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ bss_config->wpa_cfg.pairwise_cipher_wpa |=
+ CIPHER_AES_CCMP;
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
+ CIPHER_AES_CCMP;
+ default:
+ break;
+ }
+ }
+
+ switch (params->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (priv->sec_info.wep_enabled) {
+ bss_config->protocol = PROTOCOL_STATIC_WEP;
+ bss_config->key_mgmt = KEY_MGMT_NONE;
+ bss_config->wpa_cfg.length = 0;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ wep_key = priv->wep_key[i];
+ bss_config->wep_cfg[i].key_index = i;
+
+ if (priv->wep_key_curr_index == i)
+ bss_config->wep_cfg[i].is_default = 1;
+ else
+ bss_config->wep_cfg[i].is_default = 0;
+
+ bss_config->wep_cfg[i].length =
+ wep_key.key_length;
+ memcpy(&bss_config->wep_cfg[i].key,
+ &wep_key.key_material,
+ wep_key.key_length);
+ }
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ bss_config->wpa_cfg.group_cipher = CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* This function updates 11n related parameters from IE and sets them into
+ * bss_config structure.
+ */
+void
+mwifiex_set_ht_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *ht_ie;
+ u16 cap_info;
+
+ if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info))
+ return;
+
+ ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail,
+ params->beacon.tail_len);
+ if (ht_ie) {
+ memcpy(&bss_cfg->ht_cap, ht_ie + 2,
+ sizeof(struct ieee80211_ht_cap));
+ cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info);
+ memset(&bss_cfg->ht_cap.mcs, 0,
+ priv->adapter->number_of_antenna);
+ switch (GET_RXSTBC(cap_info)) {
+ case MWIFIEX_RX_STBC1:
+ /* HT_CAP 1X1 mode */
+ bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
+ break;
+ case MWIFIEX_RX_STBC12: /* fall through */
+ case MWIFIEX_RX_STBC123:
+ /* HT_CAP 2X2 mode */
+ bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
+ bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
+ break;
+ default:
+ mwifiex_dbg(priv->adapter, WARN,
+ "Unsupported RX-STBC, default to 2x2\n");
+ bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
+ bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
+ break;
+ }
+ priv->ap_11n_enabled = 1;
+ } else {
+ memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap));
+ bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP);
+ bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU;
+ }
+
+ return;
+}
+
+/* This function updates 11ac related parameters from IE
+ * and sets them into bss_config structure.
+ */
+void mwifiex_set_vht_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *vht_ie;
+
+ vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail,
+ params->beacon.tail_len);
+ if (vht_ie) {
+ memcpy(&bss_cfg->vht_cap, vht_ie + 2,
+ sizeof(struct ieee80211_vht_cap));
+ priv->ap_11ac_enabled = 1;
+ } else {
+ priv->ap_11ac_enabled = 0;
+ }
+
+ return;
+}
+
+/* This function updates 11ac related parameters from IE
+ * and sets them into bss_config structure.
+ */
+void mwifiex_set_tpc_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *tpc_ie;
+
+ tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail,
+ params->beacon.tail_len);
+ if (tpc_ie)
+ bss_cfg->power_constraint = *(tpc_ie + 2);
+ else
+ bss_cfg->power_constraint = 0;
+}
+
+/* Enable VHT only when cfg80211_ap_settings has VHT IE.
+ * Otherwise disable VHT.
+ */
+void mwifiex_set_vht_width(struct mwifiex_private *priv,
+ enum nl80211_chan_width width,
+ bool ap_11ac_enable)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_11ac_vht_cfg vht_cfg;
+
+ vht_cfg.band_config = VHT_CFG_5GHZ;
+ vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap;
+
+ if (!ap_11ac_enable) {
+ vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET;
+ vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET;
+ } else {
+ vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET;
+ vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET;
+ }
+
+ vht_cfg.misc_config = VHT_CAP_UAP_ONLY;
+
+ if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80)
+ vht_cfg.misc_config |= VHT_BW_80_160_80P80;
+
+ mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG,
+ HostCmd_ACT_GEN_SET, 0, &vht_cfg, true);
+
+ return;
+}
+
+/* This function finds supported rates IE from beacon parameter and sets
+ * these rates into bss_config structure.
+ */
+void
+mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ struct ieee_types_header *rate_ie;
+ int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ const u8 *var_pos = params->beacon.head + var_offset;
+ int len = params->beacon.head_len - var_offset;
+ u8 rate_len = 0;
+
+ rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+ if (rate_ie) {
+ memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len);
+ rate_len = rate_ie->len;
+ }
+
+ rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES,
+ params->beacon.tail,
+ params->beacon.tail_len);
+ if (rate_ie)
+ memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len);
+
+ return;
+}
+
+/* This function initializes some of mwifiex_uap_bss_param variables.
+ * This helps FW in ignoring invalid values. These values may or may not
+ * be get updated to valid ones at later stage.
+ */
+void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config)
+{
+ config->bcast_ssid_ctl = 0x7F;
+ config->radio_ctl = 0x7F;
+ config->dtim_period = 0x7F;
+ config->beacon_period = 0x7FFF;
+ config->auth_mode = 0x7F;
+ config->rts_threshold = 0x7FFF;
+ config->frag_threshold = 0x7FFF;
+ config->retry_limit = 0x7F;
+ config->qos_info = 0xFF;
+}
+
+/* This function parses BSS related parameters from structure
+ * and prepares TLVs specific to WPA/WPA2 security.
+ * These TLVs are appended to command buffer.
+ */
+static void
+mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
+{
+ struct host_cmd_tlv_pwk_cipher *pwk_cipher;
+ struct host_cmd_tlv_gwk_cipher *gwk_cipher;
+ struct host_cmd_tlv_passphrase *passphrase;
+ struct host_cmd_tlv_akmp *tlv_akmp;
+ struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
+ u16 cmd_size = *param_size;
+ u8 *tlv = *tlv_buf;
+
+ tlv_akmp = (struct host_cmd_tlv_akmp *)tlv;
+ tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
+ tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
+ sizeof(struct mwifiex_ie_types_header));
+ tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation);
+ tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt);
+ cmd_size += sizeof(struct host_cmd_tlv_akmp);
+ tlv += sizeof(struct host_cmd_tlv_akmp);
+
+ if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) {
+ pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
+ pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+ pwk_cipher->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
+ sizeof(struct mwifiex_ie_types_header));
+ pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA);
+ pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa;
+ cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
+ tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
+ }
+
+ if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) {
+ pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
+ pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+ pwk_cipher->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
+ sizeof(struct mwifiex_ie_types_header));
+ pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2);
+ pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2;
+ cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
+ tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
+ }
+
+ if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) {
+ gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv;
+ gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
+ gwk_cipher->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) -
+ sizeof(struct mwifiex_ie_types_header));
+ gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher;
+ cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher);
+ tlv += sizeof(struct host_cmd_tlv_gwk_cipher);
+ }
+
+ if (bss_cfg->wpa_cfg.length) {
+ passphrase = (struct host_cmd_tlv_passphrase *)tlv;
+ passphrase->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
+ passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
+ memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase,
+ bss_cfg->wpa_cfg.length);
+ cmd_size += sizeof(struct mwifiex_ie_types_header) +
+ bss_cfg->wpa_cfg.length;
+ tlv += sizeof(struct mwifiex_ie_types_header) +
+ bss_cfg->wpa_cfg.length;
+ }
+
+ *param_size = cmd_size;
+ *tlv_buf = tlv;
+
+ return;
+}
+
+/* This function parses WMM related parameters from cfg80211_ap_settings
+ * structure and updates bss_config structure.
+ */
+void
+mwifiex_set_wmm_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *vendor_ie;
+ struct ieee_types_header *wmm_ie;
+ u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02};
+
+ vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WMM,
+ params->beacon.tail,
+ params->beacon.tail_len);
+ if (vendor_ie) {
+ wmm_ie = (struct ieee_types_header *)vendor_ie;
+ memcpy(&bss_cfg->wmm_info, wmm_ie + 1,
+ sizeof(bss_cfg->wmm_info));
+ priv->wmm_enabled = 1;
+ } else {
+ memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info));
+ memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui));
+ bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE;
+ bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION;
+ priv->wmm_enabled = 0;
+ }
+
+ bss_cfg->qos_info = 0x00;
+ return;
+}
+/* This function parses BSS related parameters from structure
+ * and prepares TLVs specific to WEP encryption.
+ * These TLVs are appended to command buffer.
+ */
+static void
+mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
+{
+ struct host_cmd_tlv_wep_key *wep_key;
+ u16 cmd_size = *param_size;
+ int i;
+ u8 *tlv = *tlv_buf;
+ struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (bss_cfg->wep_cfg[i].length &&
+ (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 ||
+ bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) {
+ wep_key = (struct host_cmd_tlv_wep_key *)tlv;
+ wep_key->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
+ wep_key->header.len =
+ cpu_to_le16(bss_cfg->wep_cfg[i].length + 2);
+ wep_key->key_index = bss_cfg->wep_cfg[i].key_index;
+ wep_key->is_default = bss_cfg->wep_cfg[i].is_default;
+ memcpy(wep_key->key, bss_cfg->wep_cfg[i].key,
+ bss_cfg->wep_cfg[i].length);
+ cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 +
+ bss_cfg->wep_cfg[i].length;
+ tlv += sizeof(struct mwifiex_ie_types_header) + 2 +
+ bss_cfg->wep_cfg[i].length;
+ }
+ }
+
+ *param_size = cmd_size;
+ *tlv_buf = tlv;
+
+ return;
+}
+
+/* This function parses BSS related parameters from structure
+ * and prepares TLVs. These TLVs are appended to command buffer.
+*/
+static int
+mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
+{
+ struct host_cmd_tlv_dtim_period *dtim_period;
+ struct host_cmd_tlv_beacon_period *beacon_period;
+ struct host_cmd_tlv_ssid *ssid;
+ struct host_cmd_tlv_bcast_ssid *bcast_ssid;
+ struct host_cmd_tlv_channel_band *chan_band;
+ struct host_cmd_tlv_frag_threshold *frag_threshold;
+ struct host_cmd_tlv_rts_threshold *rts_threshold;
+ struct host_cmd_tlv_retry_limit *retry_limit;
+ struct host_cmd_tlv_encrypt_protocol *encrypt_protocol;
+ struct host_cmd_tlv_auth_type *auth_type;
+ struct host_cmd_tlv_rates *tlv_rates;
+ struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer;
+ struct host_cmd_tlv_power_constraint *pwr_ct;
+ struct mwifiex_ie_types_htcap *htcap;
+ struct mwifiex_ie_types_wmmcap *wmm_cap;
+ struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
+ int i;
+ u16 cmd_size = *param_size;
+
+ if (bss_cfg->ssid.ssid_len) {
+ ssid = (struct host_cmd_tlv_ssid *)tlv;
+ ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
+ ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
+ memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len);
+ cmd_size += sizeof(struct mwifiex_ie_types_header) +
+ bss_cfg->ssid.ssid_len;
+ tlv += sizeof(struct mwifiex_ie_types_header) +
+ bss_cfg->ssid.ssid_len;
+
+ bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv;
+ bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
+ bcast_ssid->header.len =
+ cpu_to_le16(sizeof(bcast_ssid->bcast_ctl));
+ bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl;
+ cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid);
+ tlv += sizeof(struct host_cmd_tlv_bcast_ssid);
+ }
+ if (bss_cfg->rates[0]) {
+ tlv_rates = (struct host_cmd_tlv_rates *)tlv;
+ tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
+
+ for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i];
+ i++)
+ tlv_rates->rates[i] = bss_cfg->rates[i];
+
+ tlv_rates->header.len = cpu_to_le16(i);
+ cmd_size += sizeof(struct host_cmd_tlv_rates) + i;
+ tlv += sizeof(struct host_cmd_tlv_rates) + i;
+ }
+ if (bss_cfg->channel &&
+ ((bss_cfg->band_cfg == BAND_CONFIG_BG &&
+ bss_cfg->channel <= MAX_CHANNEL_BAND_BG) ||
+ (bss_cfg->band_cfg == BAND_CONFIG_A &&
+ bss_cfg->channel <= MAX_CHANNEL_BAND_A))) {
+ chan_band = (struct host_cmd_tlv_channel_band *)tlv;
+ chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
+ chan_band->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) -
+ sizeof(struct mwifiex_ie_types_header));
+ chan_band->band_config = bss_cfg->band_cfg;
+ chan_band->channel = bss_cfg->channel;
+ cmd_size += sizeof(struct host_cmd_tlv_channel_band);
+ tlv += sizeof(struct host_cmd_tlv_channel_band);
+ }
+ if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD &&
+ bss_cfg->beacon_period <= MAX_BEACON_PERIOD) {
+ beacon_period = (struct host_cmd_tlv_beacon_period *)tlv;
+ beacon_period->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD);
+ beacon_period->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) -
+ sizeof(struct mwifiex_ie_types_header));
+ beacon_period->period = cpu_to_le16(bss_cfg->beacon_period);
+ cmd_size += sizeof(struct host_cmd_tlv_beacon_period);
+ tlv += sizeof(struct host_cmd_tlv_beacon_period);
+ }
+ if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD &&
+ bss_cfg->dtim_period <= MAX_DTIM_PERIOD) {
+ dtim_period = (struct host_cmd_tlv_dtim_period *)tlv;
+ dtim_period->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
+ dtim_period->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) -
+ sizeof(struct mwifiex_ie_types_header));
+ dtim_period->period = bss_cfg->dtim_period;
+ cmd_size += sizeof(struct host_cmd_tlv_dtim_period);
+ tlv += sizeof(struct host_cmd_tlv_dtim_period);
+ }
+ if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) {
+ rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv;
+ rts_threshold->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD);
+ rts_threshold->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) -
+ sizeof(struct mwifiex_ie_types_header));
+ rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold);
+ cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
+ tlv += sizeof(struct host_cmd_tlv_frag_threshold);
+ }
+ if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) &&
+ (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) {
+ frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv;
+ frag_threshold->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD);
+ frag_threshold->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) -
+ sizeof(struct mwifiex_ie_types_header));
+ frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold);
+ cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
+ tlv += sizeof(struct host_cmd_tlv_frag_threshold);
+ }
+ if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) {
+ retry_limit = (struct host_cmd_tlv_retry_limit *)tlv;
+ retry_limit->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
+ retry_limit->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) -
+ sizeof(struct mwifiex_ie_types_header));
+ retry_limit->limit = (u8)bss_cfg->retry_limit;
+ cmd_size += sizeof(struct host_cmd_tlv_retry_limit);
+ tlv += sizeof(struct host_cmd_tlv_retry_limit);
+ }
+ if ((bss_cfg->protocol & PROTOCOL_WPA) ||
+ (bss_cfg->protocol & PROTOCOL_WPA2) ||
+ (bss_cfg->protocol & PROTOCOL_EAP))
+ mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size);
+ else
+ mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size);
+
+ if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) ||
+ (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) {
+ auth_type = (struct host_cmd_tlv_auth_type *)tlv;
+ auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth_type->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) -
+ sizeof(struct mwifiex_ie_types_header));
+ auth_type->auth_type = (u8)bss_cfg->auth_mode;
+ cmd_size += sizeof(struct host_cmd_tlv_auth_type);
+ tlv += sizeof(struct host_cmd_tlv_auth_type);
+ }
+ if (bss_cfg->protocol) {
+ encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv;
+ encrypt_protocol->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL);
+ encrypt_protocol->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol)
+ - sizeof(struct mwifiex_ie_types_header));
+ encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol);
+ cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol);
+ tlv += sizeof(struct host_cmd_tlv_encrypt_protocol);
+ }
+
+ if (bss_cfg->ht_cap.cap_info) {
+ htcap = (struct mwifiex_ie_types_htcap *)tlv;
+ htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
+ htcap->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_ht_cap));
+ htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info;
+ htcap->ht_cap.ampdu_params_info =
+ bss_cfg->ht_cap.ampdu_params_info;
+ memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs,
+ sizeof(struct ieee80211_mcs_info));
+ htcap->ht_cap.extended_ht_cap_info =
+ bss_cfg->ht_cap.extended_ht_cap_info;
+ htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info;
+ htcap->ht_cap.antenna_selection_info =
+ bss_cfg->ht_cap.antenna_selection_info;
+ cmd_size += sizeof(struct mwifiex_ie_types_htcap);
+ tlv += sizeof(struct mwifiex_ie_types_htcap);
+ }
+
+ if (bss_cfg->wmm_info.qos_info != 0xFF) {
+ wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv;
+ wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC);
+ wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info));
+ memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info,
+ sizeof(wmm_cap->wmm_info));
+ cmd_size += sizeof(struct mwifiex_ie_types_wmmcap);
+ tlv += sizeof(struct mwifiex_ie_types_wmmcap);
+ }
+
+ if (bss_cfg->sta_ao_timer) {
+ ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
+ ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
+ ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) -
+ sizeof(struct mwifiex_ie_types_header));
+ ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer);
+ cmd_size += sizeof(*ao_timer);
+ tlv += sizeof(*ao_timer);
+ }
+
+ if (bss_cfg->power_constraint) {
+ pwr_ct = (void *)tlv;
+ pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT);
+ pwr_ct->header.len = cpu_to_le16(sizeof(u8));
+ pwr_ct->constraint = bss_cfg->power_constraint;
+ cmd_size += sizeof(*pwr_ct);
+ tlv += sizeof(*pwr_ct);
+ }
+
+ if (bss_cfg->ps_sta_ao_timer) {
+ ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
+ ps_ao_timer->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
+ ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) -
+ sizeof(struct mwifiex_ie_types_header));
+ ps_ao_timer->sta_ao_timer =
+ cpu_to_le32(bss_cfg->ps_sta_ao_timer);
+ cmd_size += sizeof(*ps_ao_timer);
+ tlv += sizeof(*ps_ao_timer);
+ }
+
+ *param_size = cmd_size;
+
+ return 0;
+}
+
+/* This function parses custom IEs from IE list and prepares command buffer */
+static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size)
+{
+ struct mwifiex_ie_list *ap_ie = cmd_buf;
+ struct mwifiex_ie_types_header *tlv_ie = (void *)tlv;
+
+ if (!ap_ie || !ap_ie->len || !ap_ie->ie_list)
+ return -1;
+
+ *ie_size += le16_to_cpu(ap_ie->len) +
+ sizeof(struct mwifiex_ie_types_header);
+
+ tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
+ tlv_ie->len = ap_ie->len;
+ tlv += sizeof(struct mwifiex_ie_types_header);
+
+ memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len));
+
+ return 0;
+}
+
+/* Parse AP config structure and prepare TLV based command structure
+ * to be sent to FW for uAP configuration
+ */
+static int
+mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action,
+ u32 type, void *cmd_buf)
+{
+ u8 *tlv;
+ u16 cmd_size, param_size, ie_size;
+ struct host_cmd_ds_sys_config *sys_cfg;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG);
+ cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN);
+ sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config;
+ sys_cfg->action = cpu_to_le16(cmd_action);
+ tlv = sys_cfg->tlv;
+
+ switch (type) {
+ case UAP_BSS_PARAMS_I:
+ param_size = cmd_size;
+ if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, &param_size))
+ return -1;
+ cmd->size = cpu_to_le16(param_size);
+ break;
+ case UAP_CUSTOM_IE_I:
+ ie_size = cmd_size;
+ if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size))
+ return -1;
+ cmd->size = cpu_to_le16(ie_size);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/* This function prepares AP specific deauth command with mac supplied in
+ * function parameter.
+ */
+static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd, u8 *mac)
+{
+ struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH);
+ memcpy(sta_deauth->mac, mac, ETH_ALEN);
+ sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
+
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
+ S_DS_GEN);
+ return 0;
+}
+
+/* This function prepares the AP specific commands before sending them
+ * to the firmware.
+ * This is a generic function which calls specific command preparation
+ * routines based upon the command number.
+ */
+int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
+ u16 cmd_action, u32 type,
+ void *data_buf, void *cmd_buf)
+{
+ struct host_cmd_ds_command *cmd = cmd_buf;
+
+ switch (cmd_no) {
+ case HostCmd_CMD_UAP_SYS_CONFIG:
+ if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf))
+ return -1;
+ break;
+ case HostCmd_CMD_UAP_BSS_START:
+ case HostCmd_CMD_UAP_BSS_STOP:
+ case HOST_CMD_APCMD_SYS_RESET:
+ case HOST_CMD_APCMD_STA_LIST:
+ cmd->command = cpu_to_le16(cmd_no);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+ break;
+ case HostCmd_CMD_UAP_STA_DEAUTH:
+ if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
+ return -1;
+ break;
+ case HostCmd_CMD_CHAN_REPORT_REQUEST:
+ if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf,
+ data_buf))
+ return -1;
+ break;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "PREP_CMD: unknown cmd %#x\n", cmd_no);
+ return -1;
+ }
+
+ return 0;
+}
+
+void mwifiex_uap_set_channel(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_chan_def chandef)
+{
+ u8 config_bands = 0, old_bands = priv->adapter->config_bands;
+
+ priv->bss_chandef = chandef;
+
+ bss_cfg->channel = ieee80211_frequency_to_channel(
+ chandef.chan->center_freq);
+
+ /* Set appropriate bands */
+ if (chandef.chan->band == IEEE80211_BAND_2GHZ) {
+ bss_cfg->band_cfg = BAND_CONFIG_BG;
+ config_bands = BAND_B | BAND_G;
+
+ if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
+ config_bands |= BAND_GN;
+ } else {
+ bss_cfg->band_cfg = BAND_CONFIG_A;
+ config_bands = BAND_A;
+
+ if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
+ config_bands |= BAND_AN;
+
+ if (chandef.width > NL80211_CHAN_WIDTH_40)
+ config_bands |= BAND_AAC;
+ }
+
+ priv->adapter->config_bands = config_bands;
+
+ if (old_bands != config_bands) {
+ mwifiex_send_domain_info_cmd_fw(priv->adapter->wiphy);
+ mwifiex_dnld_txpwr_table(priv);
+ }
+}
+
+int mwifiex_config_start_uap(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg)
+{
+ enum state_11d_t state_11d;
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+ HostCmd_ACT_GEN_SET,
+ UAP_BSS_PARAMS_I, bss_cfg, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to set AP configuration\n");
+ return -1;
+ }
+
+ /* Send cmd to FW to enable 11D function */
+ state_11d = ENABLE_11D;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET, DOT11D_I,
+ &state_11d, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "11D: failed to enable 11D\n");
+ return -1;
+ }
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
+ HostCmd_ACT_GEN_SET, 0, NULL, true)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Failed to start the BSS\n");
+ return -1;
+ }
+
+ if (priv->sec_info.wep_enabled)
+ priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
+ else
+ priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
+
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true))
+ return -1;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c
index 86ff54296f39..86ff54296f39 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c
diff --git a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c
new file mode 100644
index 000000000000..52f7981a8afc
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c
@@ -0,0 +1,436 @@
+/*
+ * Marvell Wireless LAN device driver: AP TX and RX data handling
+ *
+ * Copyright (C) 2012-2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available 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 "decl.h"
+#include "ioctl.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n_aggr.h"
+#include "11n_rxreorder.h"
+
+/* This function checks if particular RA list has packets more than low bridge
+ * packet threshold and then deletes packet from this RA list.
+ * Function deletes packets from such RA list and returns true. If no such list
+ * is found, false is returned.
+ */
+static bool
+mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
+ struct list_head *ra_list_head,
+ int tid)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ struct sk_buff *skb, *tmp;
+ bool pkt_deleted = false;
+ struct mwifiex_txinfo *tx_info;
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ list_for_each_entry(ra_list, ra_list_head, list) {
+ if (skb_queue_empty(&ra_list->skb_head))
+ continue;
+
+ skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
+ __skb_unlink(skb, &ra_list->skb_head);
+ mwifiex_write_data_complete(adapter, skb, 0,
+ -1);
+ if (ra_list->tx_paused)
+ priv->wmm.pkts_paused[tid]--;
+ else
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ pkt_deleted = true;
+ }
+ if ((atomic_read(&adapter->pending_bridged_pkts) <=
+ MWIFIEX_BRIDGED_PKTS_THR_LOW))
+ break;
+ }
+ }
+
+ return pkt_deleted;
+}
+
+/* This function deletes packets from particular RA List. RA list index
+ * from which packets are deleted is preserved so that packets from next RA
+ * list are deleted upon subsequent call thus maintaining fairness.
+ */
+static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv)
+{
+ unsigned long flags;
+ struct list_head *ra_list;
+ int i;
+
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
+ if (priv->del_list_idx == MAX_NUM_TID)
+ priv->del_list_idx = 0;
+ ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
+ if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) {
+ priv->del_list_idx++;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
+
+static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ struct sk_buff *new_skb;
+ struct mwifiex_txinfo *tx_info;
+ int hdr_chop;
+ struct ethhdr *p_ethhdr;
+ struct mwifiex_sta_node *src_node;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ if ((atomic_read(&adapter->pending_bridged_pkts) >=
+ MWIFIEX_BRIDGED_PKTS_THR_HIGH)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Tx: Bridge packet limit reached. Drop packet!\n");
+ kfree_skb(skb);
+ mwifiex_uap_cleanup_tx_queues(priv);
+ return;
+ }
+
+ if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
+ sizeof(bridge_tunnel_header))) ||
+ (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
+ sizeof(rfc1042_header)) &&
+ ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP &&
+ ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) {
+ /* Replace the 803 header and rfc1042 header (llc/snap) with
+ * an Ethernet II header, keep the src/dst and snap_type
+ * (ethertype).
+ *
+ * The firmware only passes up SNAP frames converting all RX
+ * data from 802.11 to 802.2/LLC/SNAP frames.
+ *
+ * To create the Ethernet II, just move the src, dst address
+ * right before the snap_type.
+ */
+ p_ethhdr = (struct ethhdr *)
+ ((u8 *)(&rx_pkt_hdr->eth803_hdr)
+ + sizeof(rx_pkt_hdr->eth803_hdr)
+ + sizeof(rx_pkt_hdr->rfc1042_hdr)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
+ - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
+ memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
+ sizeof(p_ethhdr->h_source));
+ memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
+ sizeof(p_ethhdr->h_dest));
+ /* Chop off the rxpd + the excess memory from
+ * 802.2/llc/snap header that was removed.
+ */
+ hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd;
+ } else {
+ /* Chop off the rxpd */
+ hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
+ }
+
+ /* Chop off the leading header bytes so that it points
+ * to the start of either the reconstructed EthII frame
+ * or the 802.2/llc/snap frame.
+ */
+ skb_pull(skb, hdr_chop);
+
+ if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "data: Tx: insufficient skb headroom %d\n",
+ skb_headroom(skb));
+ /* Insufficient skb headroom - allocate a new skb */
+ new_skb =
+ skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
+ if (unlikely(!new_skb)) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Tx: cannot allocate new_skb\n");
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return;
+ }
+
+ kfree_skb(skb);
+ skb = new_skb;
+ mwifiex_dbg(priv->adapter, INFO,
+ "info: new skb headroom %d\n",
+ skb_headroom(skb));
+ }
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT;
+
+ src_node = mwifiex_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source);
+ if (src_node) {
+ src_node->stats.last_rx = jiffies;
+ src_node->stats.rx_bytes += skb->len;
+ src_node->stats.rx_packets++;
+ src_node->stats.last_tx_rate = uap_rx_pd->rx_rate;
+ src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info;
+ }
+
+ if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) {
+ /* Update bridge packet statistics as the
+ * packet is not going to kernel/upper layer.
+ */
+ priv->stats.rx_bytes += skb->len;
+ priv->stats.rx_packets++;
+
+ /* Sending bridge packet to TX queue, so save the packet
+ * length in TXCB to update statistics in TX complete.
+ */
+ tx_info->pkt_len = skb->len;
+ }
+
+ __net_timestamp(skb);
+ mwifiex_wmm_add_buf_txqueue(priv, skb);
+ atomic_inc(&adapter->tx_pending);
+ atomic_inc(&adapter->pending_bridged_pkts);
+
+ return;
+}
+
+/*
+ * This function contains logic for AP packet forwarding.
+ *
+ * If a packet is multicast/broadcast, it is sent to kernel/upper layer
+ * as well as queued back to AP TX queue so that it can be sent to other
+ * associated stations.
+ * If a packet is unicast and RA is present in associated station list,
+ * it is again requeued into AP TX queue.
+ * If a packet is unicast and RA is not in associated station list,
+ * packet is forwarded to kernel to handle routing logic.
+ */
+int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u8 ra[ETH_ALEN];
+ struct sk_buff *skb_uap;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ /* don't do packet forwarding in disconnected state */
+ if (!priv->media_connected) {
+ mwifiex_dbg(adapter, ERROR,
+ "drop packet in disconnected state.\n");
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN);
+
+ if (is_multicast_ether_addr(ra)) {
+ skb_uap = skb_copy(skb, GFP_ATOMIC);
+ mwifiex_uap_queue_bridged_pkt(priv, skb_uap);
+ } else {
+ if (mwifiex_get_sta_entry(priv, ra)) {
+ /* Requeue Intra-BSS packet */
+ mwifiex_uap_queue_bridged_pkt(priv, skb);
+ return 0;
+ }
+ }
+
+ /* Forward unicat/Inter-BSS packets to kernel. */
+ return mwifiex_process_rx_packet(priv, skb);
+}
+
+/*
+ * This function processes the packet received on AP interface.
+ *
+ * The function looks into the RxPD and performs sanity tests on the
+ * received buffer to ensure its a valid packet before processing it
+ * further. If the packet is determined to be aggregated, it is
+ * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic.
+ *
+ * The completion callback is called after processing is complete.
+ */
+int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int ret;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u16 rx_pkt_type;
+ u8 ta[ETH_ALEN], pkt_type;
+ unsigned long flags;
+ struct mwifiex_sta_node *node;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source);
+
+ if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
+ le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) {
+ mwifiex_dbg(adapter, ERROR,
+ "wrong rx packet: len=%d, offset=%d, length=%d\n",
+ skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset),
+ le16_to_cpu(uap_rx_pd->rx_pkt_length));
+ priv->stats.rx_dropped++;
+
+ node = mwifiex_get_sta_entry(priv, ta);
+ if (node)
+ node->stats.tx_failed++;
+
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ if (rx_pkt_type == PKT_TYPE_MGMT) {
+ ret = mwifiex_process_mgmt_packet(priv, skb);
+ if (ret)
+ mwifiex_dbg(adapter, DATA, "Rx of mgmt packet failed");
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+
+ if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ node = mwifiex_get_sta_entry(priv, ta);
+ if (node)
+ node->rx_seq[uap_rx_pd->priority] =
+ le16_to_cpu(uap_rx_pd->seq_num);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ }
+
+ if (!priv->ap_11n_enabled ||
+ (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) &&
+ (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) {
+ ret = mwifiex_handle_uap_rx_forward(priv, skb);
+ return ret;
+ }
+
+ /* Reorder and send to kernel */
+ pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type);
+ ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num),
+ uap_rx_pd->priority, ta, pkt_type,
+ skb);
+
+ if (ret || (rx_pkt_type == PKT_TYPE_BAR))
+ dev_kfree_skb_any(skb);
+
+ if (ret)
+ priv->stats.rx_dropped++;
+
+ return ret;
+}
+
+/*
+ * This function fills the TxPD for AP tx packets.
+ *
+ * The Tx buffer received by this function should already have the
+ * header space allocated for TxPD.
+ *
+ * This function inserts the TxPD in between interface header and actual
+ * data and adjusts the buffer pointers accordingly.
+ *
+ * The following TxPD fields are set by this function, as required -
+ * - BSS number
+ * - Tx packet length and offset
+ * - Priority
+ * - Packet delay
+ * - Priority specific Tx control
+ * - Flags
+ */
+void *mwifiex_process_uap_txpd(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct uap_txpd *txpd;
+ struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
+ int pad;
+ u16 pkt_type, pkt_offset;
+ int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 :
+ INTF_HEADER_LEN;
+
+ if (!skb->len) {
+ mwifiex_dbg(adapter, ERROR,
+ "Tx: bad packet length: %d\n", skb->len);
+ tx_info->status_code = -1;
+ return skb->data;
+ }
+
+ BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN);
+
+ pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
+
+ pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) &
+ (MWIFIEX_DMA_ALIGN_SZ - 1);
+
+ skb_push(skb, sizeof(*txpd) + pad);
+
+ txpd = (struct uap_txpd *)skb->data;
+ memset(txpd, 0, sizeof(*txpd));
+ txpd->bss_num = priv->bss_num;
+ txpd->bss_type = priv->bss_type;
+ txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) +
+ pad)));
+ txpd->priority = (u8)skb->priority;
+
+ txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
+
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+ tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+ txpd->tx_token_id = tx_info->ack_frame_id;
+ txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+ }
+
+ if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
+ /*
+ * Set the priority specific tx_control field, setting of 0 will
+ * cause the default value to be used later in this function.
+ */
+ txpd->tx_control =
+ cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]);
+
+ /* Offset of actual data */
+ pkt_offset = sizeof(*txpd) + pad;
+ if (pkt_type == PKT_TYPE_MGMT) {
+ /* Set the packet type and add header for management frame */
+ txpd->tx_pkt_type = cpu_to_le16(pkt_type);
+ pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE;
+ }
+
+ txpd->tx_pkt_offset = cpu_to_le16(pkt_offset);
+
+ /* make space for INTF_HEADER_LEN */
+ skb_push(skb, hroom);
+
+ if (!txpd->tx_control)
+ /* TxCtrl set by user or default */
+ txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
+
+ return skb->data;
+}
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c
index e43aff932360..e43aff932360 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/marvell/mwifiex/usb.c
diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h
index b4e9246bbcdc..b4e9246bbcdc 100644
--- a/drivers/net/wireless/mwifiex/usb.h
+++ b/drivers/net/wireless/marvell/mwifiex/usb.h
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c
index 0cec8a64473e..0cec8a64473e 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/marvell/mwifiex/util.c
diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/marvell/mwifiex/util.h
index b541d66c01eb..b541d66c01eb 100644
--- a/drivers/net/wireless/mwifiex/util.h
+++ b/drivers/net/wireless/marvell/mwifiex/util.h
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c
index acccd6734e3b..acccd6734e3b 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/marvell/mwifiex/wmm.c
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/marvell/mwifiex/wmm.h
index 38f09762bd2f..38f09762bd2f 100644
--- a/drivers/net/wireless/mwifiex/wmm.h
+++ b/drivers/net/wireless/marvell/mwifiex/wmm.h
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c
index 30e3aaae32e2..30e3aaae32e2 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/marvell/mwl8k.c
diff --git a/drivers/net/wireless/mediatek/Kconfig b/drivers/net/wireless/mediatek/Kconfig
index cba300c6b5da..28843fed750a 100644
--- a/drivers/net/wireless/mediatek/Kconfig
+++ b/drivers/net/wireless/mediatek/Kconfig
@@ -1,10 +1,14 @@
-menuconfig WL_MEDIATEK
- bool "Mediatek Wireless LAN support"
+config WLAN_VENDOR_MEDIATEK
+ bool "MediaTek devices"
+ default y
---help---
- Enable community drivers for MediaTek WiFi devices.
- Those drivers make use of the Linux mac80211 stack.
+ If you have a wireless card belonging to this class, say Y.
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
-if WL_MEDIATEK
+if WLAN_VENDOR_MEDIATEK
source "drivers/net/wireless/mediatek/mt7601u/Kconfig"
-endif # WL_MEDIATEK
+endif # WLAN_VENDOR_MEDIATEK
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
deleted file mode 100644
index aa498e0d2204..000000000000
--- a/drivers/net/wireless/mwifiex/11n_aggr.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: 802.11n Aggregation
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-#include "11n_aggr.h"
-
-/*
- * Creates an AMSDU subframe for aggregation into one AMSDU packet.
- *
- * The resultant AMSDU subframe format is -
- *
- * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
- * | DA | SA | Length | SNAP header | MSDU |
- * | data[0..5] | data[6..11] | | | data[14..] |
- * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
- * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes-->
- *
- * This function also computes the amount of padding required to make the
- * buffer length multiple of 4 bytes.
- *
- * Data => |DA|SA|SNAP-TYPE|........ .|
- * MSDU => |DA|SA|Length|SNAP|...... ..|
- */
-static int
-mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
- struct sk_buff *skb_src, int *pad)
-
-{
- int dt_offset;
- struct rfc_1042_hdr snap = {
- 0xaa, /* LLC DSAP */
- 0xaa, /* LLC SSAP */
- 0x03, /* LLC CTRL */
- {0x00, 0x00, 0x00}, /* SNAP OUI */
- 0x0000 /* SNAP type */
- /*
- * This field will be overwritten
- * later with ethertype
- */
- };
- struct tx_packet_hdr *tx_header;
-
- tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header));
-
- /* Copy DA and SA */
- dt_offset = 2 * ETH_ALEN;
- memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
-
- /* Copy SNAP header */
- snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto;
-
- dt_offset += sizeof(__be16);
-
- memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
-
- skb_pull(skb_src, dt_offset);
-
- /* Update Length field */
- tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
-
- /* Add payload */
- memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len);
-
- /* Add padding for new MSDU to start from 4 byte boundary */
- *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4;
-
- return skb_aggr->len + *pad;
-}
-
-/*
- * Adds TxPD to AMSDU header.
- *
- * Each AMSDU packet will contain one TxPD at the beginning,
- * followed by multiple AMSDU subframes.
- */
-static void
-mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct txpd *local_tx_pd;
- struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
- unsigned int pad;
- int headroom = (priv->adapter->iface_type ==
- MWIFIEX_USB) ? 0 : INTF_HEADER_LEN;
-
- pad = ((void *)skb->data - sizeof(*local_tx_pd) -
- headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1);
- skb_push(skb, pad);
-
- skb_push(skb, sizeof(*local_tx_pd));
-
- local_tx_pd = (struct txpd *) skb->data;
- memset(local_tx_pd, 0, sizeof(struct txpd));
-
- /* Original priority has been overwritten */
- local_tx_pd->priority = (u8) skb->priority;
- local_tx_pd->pkt_delay_2ms =
- mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
- local_tx_pd->bss_num = priv->bss_num;
- local_tx_pd->bss_type = priv->bss_type;
- /* Always zero as the data is followed by struct txpd */
- local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) +
- pad);
- local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
- local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
- sizeof(*local_tx_pd) -
- pad);
-
- if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
- local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
-
- if (local_tx_pd->tx_control == 0)
- /* TxCtrl set by user or default */
- local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
- priv->adapter->pps_uapsd_mode) {
- if (true == mwifiex_check_last_packet_indication(priv)) {
- priv->adapter->tx_lock_flag = true;
- local_tx_pd->flags =
- MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET;
- }
- }
-}
-
-/*
- * Create aggregated packet.
- *
- * This function creates an aggregated MSDU packet, by combining buffers
- * from the RA list. Each individual buffer is encapsulated as an AMSDU
- * subframe and all such subframes are concatenated together to form the
- * AMSDU packet.
- *
- * A TxPD is also added to the front of the resultant AMSDU packets for
- * transmission. The resultant packets format is -
- *
- * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+
- * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame|
- * | | 1 | 2 | .. | n |
- * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+
- */
-int
-mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
- struct mwifiex_ra_list_tbl *pra_list,
- int ptrindex, unsigned long ra_list_flags)
- __releases(&priv->wmm.ra_list_spinlock)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct sk_buff *skb_aggr, *skb_src;
- struct mwifiex_txinfo *tx_info_aggr, *tx_info_src;
- int pad = 0, aggr_num = 0, ret;
- struct mwifiex_tx_param tx_param;
- struct txpd *ptx_pd = NULL;
- int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
-
- skb_src = skb_peek(&pra_list->skb_head);
- if (!skb_src) {
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
- ra_list_flags);
- return 0;
- }
-
- tx_info_src = MWIFIEX_SKB_TXCB(skb_src);
- skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size,
- GFP_ATOMIC | GFP_DMA);
- if (!skb_aggr) {
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
- ra_list_flags);
- return -1;
- }
- skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN);
- tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr);
-
- memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
- tx_info_aggr->bss_type = tx_info_src->bss_type;
- tx_info_aggr->bss_num = tx_info_src->bss_num;
-
- if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
- tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
- tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT;
- skb_aggr->priority = skb_src->priority;
- skb_aggr->tstamp = skb_src->tstamp;
-
- skb_aggr->tstamp = ktime_get_real();
-
- do {
- /* Check if AMSDU can accommodate this MSDU */
- if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN))
- break;
-
- skb_src = skb_dequeue(&pra_list->skb_head);
- pra_list->total_pkt_count--;
- atomic_dec(&priv->wmm.tx_pkts_queued);
- aggr_num++;
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
- ra_list_flags);
- mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
-
- mwifiex_write_data_complete(adapter, skb_src, 0, 0);
-
- spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
-
- if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
- ra_list_flags);
- return -1;
- }
-
- if (skb_tailroom(skb_aggr) < pad) {
- pad = 0;
- break;
- }
- skb_put(skb_aggr, pad);
-
- skb_src = skb_peek(&pra_list->skb_head);
-
- } while (skb_src);
-
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags);
-
- /* Last AMSDU packet does not need padding */
- skb_trim(skb_aggr, skb_aggr->len - pad);
-
- /* Form AMSDU */
- mwifiex_11n_form_amsdu_txpd(priv, skb_aggr);
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
- ptx_pd = (struct txpd *)skb_aggr->data;
-
- skb_push(skb_aggr, headroom);
- tx_info_aggr->aggr_num = aggr_num * 2;
- if (adapter->data_sent || adapter->tx_lock_flag) {
- atomic_add(aggr_num * 2, &adapter->tx_queued);
- skb_queue_tail(&adapter->tx_data_q, skb_aggr);
- return 0;
- }
-
- if (adapter->iface_type == MWIFIEX_USB) {
- ret = adapter->if_ops.host_to_card(adapter, priv->usb_port,
- skb_aggr, NULL);
- } else {
- if (skb_src)
- tx_param.next_pkt_len =
- skb_src->len + sizeof(struct txpd);
- else
- tx_param.next_pkt_len = 0;
-
- ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
- skb_aggr, &tx_param);
- }
- switch (ret) {
- case -EBUSY:
- spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
- if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
- ra_list_flags);
- mwifiex_write_data_complete(adapter, skb_aggr, 1, -1);
- return -1;
- }
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
- adapter->pps_uapsd_mode && adapter->tx_lock_flag) {
- priv->adapter->tx_lock_flag = false;
- if (ptx_pd)
- ptx_pd->flags = 0;
- }
-
- skb_queue_tail(&pra_list->skb_head, skb_aggr);
-
- pra_list->total_pkt_count++;
-
- atomic_inc(&priv->wmm.tx_pkts_queued);
-
- tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
- ra_list_flags);
- mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
- break;
- case -1:
- mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n",
- __func__, ret);
- adapter->dbg.num_tx_host_to_card_failure++;
- mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
- return 0;
- case -EINPROGRESS:
- break;
- case 0:
- mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
- break;
- default:
- break;
- }
- if (ret != -EBUSY) {
- mwifiex_rotate_priolists(priv, pra_list, ptrindex);
- }
-
- return 0;
-}
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
deleted file mode 100644
index b3970a8c9e48..000000000000
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: 802.11n RX Re-ordering
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-#include "11n_rxreorder.h"
-
-/* This function will dispatch amsdu packet and forward it to kernel/upper
- * layer.
- */
-static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
- int ret;
-
- if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
- struct sk_buff_head list;
- struct sk_buff *rx_skb;
-
- __skb_queue_head_init(&list);
-
- skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
- skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
-
- ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
- priv->wdev.iftype, 0, false);
-
- while (!skb_queue_empty(&list)) {
- rx_skb = __skb_dequeue(&list);
- ret = mwifiex_recv_packet(priv, rx_skb);
- if (ret == -1)
- mwifiex_dbg(priv->adapter, ERROR,
- "Rx of A-MSDU failed");
- }
- return 0;
- }
-
- return -1;
-}
-
-/* This function will process the rx packet and forward it to kernel/upper
- * layer.
- */
-static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload)
-{
- int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload);
-
- if (!ret)
- return 0;
-
- if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
- return mwifiex_handle_uap_rx_forward(priv, payload);
-
- return mwifiex_process_rx_packet(priv, payload);
-}
-
-/*
- * This function dispatches all packets in the Rx reorder table until the
- * start window.
- *
- * There could be holes in the buffer, which are skipped by the function.
- * Since the buffer is linear, the function uses rotation to simulate
- * circular buffer.
- */
-static void
-mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
- struct mwifiex_rx_reorder_tbl *tbl,
- int start_win)
-{
- int pkt_to_send, i;
- void *rx_tmp_ptr;
- unsigned long flags;
-
- pkt_to_send = (start_win > tbl->start_win) ?
- min((start_win - tbl->start_win), tbl->win_size) :
- tbl->win_size;
-
- for (i = 0; i < pkt_to_send; ++i) {
- spin_lock_irqsave(&priv->rx_pkt_lock, flags);
- rx_tmp_ptr = NULL;
- if (tbl->rx_reorder_ptr[i]) {
- rx_tmp_ptr = tbl->rx_reorder_ptr[i];
- tbl->rx_reorder_ptr[i] = NULL;
- }
- spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- if (rx_tmp_ptr)
- mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
- }
-
- spin_lock_irqsave(&priv->rx_pkt_lock, flags);
- /*
- * We don't have a circular buffer, hence use rotation to simulate
- * circular buffer
- */
- for (i = 0; i < tbl->win_size - pkt_to_send; ++i) {
- tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i];
- tbl->rx_reorder_ptr[pkt_to_send + i] = NULL;
- }
-
- tbl->start_win = start_win;
- spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
-}
-
-/*
- * This function dispatches all packets in the Rx reorder table until
- * a hole is found.
- *
- * The start window is adjusted automatically when a hole is located.
- * Since the buffer is linear, the function uses rotation to simulate
- * circular buffer.
- */
-static void
-mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
- struct mwifiex_rx_reorder_tbl *tbl)
-{
- int i, j, xchg;
- void *rx_tmp_ptr;
- unsigned long flags;
-
- for (i = 0; i < tbl->win_size; ++i) {
- spin_lock_irqsave(&priv->rx_pkt_lock, flags);
- if (!tbl->rx_reorder_ptr[i]) {
- spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- break;
- }
- rx_tmp_ptr = tbl->rx_reorder_ptr[i];
- tbl->rx_reorder_ptr[i] = NULL;
- spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
- }
-
- spin_lock_irqsave(&priv->rx_pkt_lock, flags);
- /*
- * We don't have a circular buffer, hence use rotation to simulate
- * circular buffer
- */
- if (i > 0) {
- xchg = tbl->win_size - i;
- for (j = 0; j < xchg; ++j) {
- tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j];
- tbl->rx_reorder_ptr[i + j] = NULL;
- }
- }
- tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
- spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
-}
-
-/*
- * This function deletes the Rx reorder table and frees the memory.
- *
- * The function stops the associated timer and dispatches all the
- * pending packets in the Rx reorder table before deletion.
- */
-static void
-mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
- struct mwifiex_rx_reorder_tbl *tbl)
-{
- unsigned long flags;
- int start_win;
-
- if (!tbl)
- return;
-
- spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
- priv->adapter->rx_locked = true;
- if (priv->adapter->rx_processing) {
- spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
- flush_workqueue(priv->adapter->rx_workqueue);
- } else {
- spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
- }
-
- start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
- mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
-
- del_timer_sync(&tbl->timer_context.timer);
- tbl->timer_context.timer_is_set = false;
-
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- list_del(&tbl->list);
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
-
- kfree(tbl->rx_reorder_ptr);
- kfree(tbl);
-
- spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
- priv->adapter->rx_locked = false;
- spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
-
-}
-
-/*
- * This function returns the pointer to an entry in Rx reordering
- * table which matches the given TA/TID pair.
- */
-struct mwifiex_rx_reorder_tbl *
-mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
-{
- struct mwifiex_rx_reorder_tbl *tbl;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) {
- if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
- flags);
- return tbl;
- }
- }
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
-
- return NULL;
-}
-
-/* This function retrieves the pointer to an entry in Rx reordering
- * table which matches the given TA and deletes it.
- */
-void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
-{
- struct mwifiex_rx_reorder_tbl *tbl, *tmp;
- unsigned long flags;
-
- if (!ta)
- return;
-
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) {
- if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
- flags);
- mwifiex_del_rx_reorder_entry(priv, tbl);
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- }
- }
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
-
- return;
-}
-
-/*
- * This function finds the last sequence number used in the packets
- * buffered in Rx reordering table.
- */
-static int
-mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
-{
- struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
- struct mwifiex_private *priv = ctx->priv;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
- if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
- flags);
- return i;
- }
- }
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
-
- return -1;
-}
-
-/*
- * This function flushes all the packets in Rx reordering table.
- *
- * The function checks if any packets are currently buffered in the
- * table or not. In case there are packets available, it dispatches
- * them and then dumps the Rx reordering table.
- */
-static void
-mwifiex_flush_data(unsigned long context)
-{
- struct reorder_tmr_cnxt *ctx =
- (struct reorder_tmr_cnxt *) context;
- int start_win, seq_num;
-
- ctx->timer_is_set = false;
- seq_num = mwifiex_11n_find_last_seq_num(ctx);
-
- if (seq_num < 0)
- return;
-
- mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num);
- start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
- mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
- start_win);
-}
-
-/*
- * This function creates an entry in Rx reordering table for the
- * given TA/TID.
- *
- * The function also initializes the entry with sequence number, window
- * size as well as initializes the timer.
- *
- * If the received TA/TID pair is already present, all the packets are
- * dispatched and the window size is moved until the SSN.
- */
-static void
-mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
- int tid, int win_size, int seq_num)
-{
- int i;
- struct mwifiex_rx_reorder_tbl *tbl, *new_node;
- u16 last_seq = 0;
- unsigned long flags;
- struct mwifiex_sta_node *node;
-
- /*
- * If we get a TID, ta pair which is already present dispatch all the
- * the packets and move the window size until the ssn
- */
- tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
- if (tbl) {
- mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
- return;
- }
- /* if !tbl then create one */
- new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL);
- if (!new_node)
- return;
-
- INIT_LIST_HEAD(&new_node->list);
- new_node->tid = tid;
- memcpy(new_node->ta, ta, ETH_ALEN);
- new_node->start_win = seq_num;
- new_node->init_win = seq_num;
- new_node->flags = 0;
-
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- if (mwifiex_queuing_ra_based(priv)) {
- if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) {
- node = mwifiex_get_sta_entry(priv, ta);
- if (node)
- last_seq = node->rx_seq[tid];
- }
- } else {
- node = mwifiex_get_sta_entry(priv, ta);
- if (node)
- last_seq = node->rx_seq[tid];
- else
- last_seq = priv->rx_seq[tid];
- }
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-
- mwifiex_dbg(priv->adapter, INFO,
- "info: last_seq=%d start_win=%d\n",
- last_seq, new_node->start_win);
-
- if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
- last_seq >= new_node->start_win) {
- new_node->start_win = last_seq + 1;
- new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
- }
-
- new_node->win_size = win_size;
-
- new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size,
- GFP_KERNEL);
- if (!new_node->rx_reorder_ptr) {
- kfree((u8 *) new_node);
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: failed to alloc reorder_ptr\n", __func__);
- return;
- }
-
- new_node->timer_context.ptr = new_node;
- new_node->timer_context.priv = priv;
- new_node->timer_context.timer_is_set = false;
-
- setup_timer(&new_node->timer_context.timer, mwifiex_flush_data,
- (unsigned long)&new_node->timer_context);
-
- for (i = 0; i < win_size; ++i)
- new_node->rx_reorder_ptr[i] = NULL;
-
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr);
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
-}
-
-static void
-mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl)
-{
- u32 min_flush_time;
-
- if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32)
- min_flush_time = MIN_FLUSH_TIMER_15_MS;
- else
- min_flush_time = MIN_FLUSH_TIMER_MS;
-
- mod_timer(&tbl->timer_context.timer,
- jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
-
- tbl->timer_context.timer_is_set = true;
-}
-
-/*
- * This function prepares command for adding a BA request.
- *
- * Preparation includes -
- * - Setting command ID and proper size
- * - Setting add BA request buffer
- * - Ensuring correct endian-ness
- */
-int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
-{
- struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ);
- cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN);
- memcpy(add_ba_req, data_buf, sizeof(*add_ba_req));
-
- return 0;
-}
-
-/*
- * This function prepares command for adding a BA response.
- *
- * Preparation includes -
- * - Setting command ID and proper size
- * - Setting add BA response buffer
- * - Ensuring correct endian-ness
- */
-int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct host_cmd_ds_11n_addba_req
- *cmd_addba_req)
-{
- struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
- struct mwifiex_sta_node *sta_ptr;
- u32 rx_win_size = priv->add_ba_param.rx_win_size;
- u8 tid;
- int win_size;
- unsigned long flags;
- uint16_t block_ack_param_set;
-
- if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
- ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
- priv->adapter->is_hw_11ac_capable &&
- memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- sta_ptr = mwifiex_get_sta_entry(priv,
- cmd_addba_req->peer_mac_addr);
- if (!sta_ptr) {
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- mwifiex_dbg(priv->adapter, ERROR,
- "BA setup with unknown TDLS peer %pM!\n",
- cmd_addba_req->peer_mac_addr);
- return -1;
- }
- if (sta_ptr->is_11ac_enabled)
- rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- }
-
- cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
- cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
-
- memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr,
- ETH_ALEN);
- add_ba_rsp->dialog_token = cmd_addba_req->dialog_token;
- add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo;
- add_ba_rsp->ssn = cmd_addba_req->ssn;
-
- block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set);
- tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
- >> BLOCKACKPARAM_TID_POS;
- add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT);
- block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
-
- /* If we don't support AMSDU inside AMPDU, reset the bit */
- if (!priv->add_ba_param.rx_amsdu ||
- (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED))
- block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
- block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
- add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
- win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
- & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
- >> BLOCKACKPARAM_WINSIZE_POS;
- cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set);
-
- mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr,
- tid, win_size,
- le16_to_cpu(cmd_addba_req->ssn));
- return 0;
-}
-
-/*
- * This function prepares command for deleting a BA request.
- *
- * Preparation includes -
- * - Setting command ID and proper size
- * - Setting del BA request buffer
- * - Ensuring correct endian-ness
- */
-int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf)
-{
- struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA);
- cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN);
- memcpy(del_ba, data_buf, sizeof(*del_ba));
-
- return 0;
-}
-
-/*
- * This function identifies if Rx reordering is needed for a received packet.
- *
- * In case reordering is required, the function will do the reordering
- * before sending it to kernel.
- *
- * The Rx reorder table is checked first with the received TID/TA pair. If
- * not found, the received packet is dispatched immediately. But if found,
- * the packet is reordered and all the packets in the updated Rx reordering
- * table is dispatched until a hole is found.
- *
- * For sequence number less than the starting window, the packet is dropped.
- */
-int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
- u16 seq_num, u16 tid,
- u8 *ta, u8 pkt_type, void *payload)
-{
- struct mwifiex_rx_reorder_tbl *tbl;
- int prev_start_win, start_win, end_win, win_size;
- u16 pkt_index;
- bool init_window_shift = false;
- int ret = 0;
-
- tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
- if (!tbl) {
- if (pkt_type != PKT_TYPE_BAR)
- mwifiex_11n_dispatch_pkt(priv, payload);
- return ret;
- }
-
- if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
- mwifiex_11n_dispatch_pkt(priv, payload);
- return ret;
- }
-
- start_win = tbl->start_win;
- prev_start_win = start_win;
- win_size = tbl->win_size;
- end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
- if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
- init_window_shift = true;
- tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
- }
-
- if (tbl->flags & RXREOR_FORCE_NO_DROP) {
- mwifiex_dbg(priv->adapter, INFO,
- "RXREOR_FORCE_NO_DROP when HS is activated\n");
- tbl->flags &= ~RXREOR_FORCE_NO_DROP;
- } else if (init_window_shift && seq_num < start_win &&
- seq_num >= tbl->init_win) {
- mwifiex_dbg(priv->adapter, INFO,
- "Sender TID sequence number reset %d->%d for SSN %d\n",
- start_win, seq_num, tbl->init_win);
- tbl->start_win = start_win = seq_num;
- end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
- } else {
- /*
- * If seq_num is less then starting win then ignore and drop
- * the packet
- */
- if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
- if (seq_num >= ((start_win + TWOPOW11) &
- (MAX_TID_VALUE - 1)) &&
- seq_num < start_win) {
- ret = -1;
- goto done;
- }
- } else if ((seq_num < start_win) ||
- (seq_num >= (start_win + TWOPOW11))) {
- ret = -1;
- goto done;
- }
- }
-
- /*
- * If this packet is a BAR we adjust seq_num as
- * WinStart = seq_num
- */
- if (pkt_type == PKT_TYPE_BAR)
- seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1);
-
- if (((end_win < start_win) &&
- (seq_num < start_win) && (seq_num > end_win)) ||
- ((end_win > start_win) && ((seq_num > end_win) ||
- (seq_num < start_win)))) {
- end_win = seq_num;
- if (((end_win - win_size) + 1) >= 0)
- start_win = (end_win - win_size) + 1;
- else
- start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1;
- mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
- }
-
- if (pkt_type != PKT_TYPE_BAR) {
- if (seq_num >= start_win)
- pkt_index = seq_num - start_win;
- else
- pkt_index = (seq_num+MAX_TID_VALUE) - start_win;
-
- if (tbl->rx_reorder_ptr[pkt_index]) {
- ret = -1;
- goto done;
- }
-
- tbl->rx_reorder_ptr[pkt_index] = payload;
- }
-
- /*
- * Dispatch all packets sequentially from start_win until a
- * hole is found and adjust the start_win appropriately
- */
- mwifiex_11n_scan_and_dispatch(priv, tbl);
-
-done:
- if (!tbl->timer_context.timer_is_set ||
- prev_start_win != tbl->start_win)
- mwifiex_11n_rxreorder_timer_restart(tbl);
- return ret;
-}
-
-/*
- * This function deletes an entry for a given TID/TA pair.
- *
- * The TID/TA are taken from del BA event body.
- */
-void
-mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac,
- u8 type, int initiator)
-{
- struct mwifiex_rx_reorder_tbl *tbl;
- struct mwifiex_tx_ba_stream_tbl *ptx_tbl;
- struct mwifiex_ra_list_tbl *ra_list;
- u8 cleanup_rx_reorder_tbl;
- unsigned long flags;
- int tid_down;
-
- if (type == TYPE_DELBA_RECEIVE)
- cleanup_rx_reorder_tbl = (initiator) ? true : false;
- else
- cleanup_rx_reorder_tbl = (initiator) ? false : true;
-
- mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n",
- peer_mac, tid, initiator);
-
- if (cleanup_rx_reorder_tbl) {
- tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
- peer_mac);
- if (!tbl) {
- mwifiex_dbg(priv->adapter, EVENT,
- "event: TID, TA not found in table\n");
- return;
- }
- mwifiex_del_rx_reorder_entry(priv, tbl);
- } else {
- ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac);
- if (!ptx_tbl) {
- mwifiex_dbg(priv->adapter, EVENT,
- "event: TID, RA not found in table\n");
- return;
- }
-
- tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
- ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac);
- if (ra_list) {
- ra_list->amsdu_in_ampdu = false;
- ra_list->ba_status = BA_SETUP_NONE;
- }
- spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
- mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
- spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
- }
-}
-
-/*
- * This function handles the command response of an add BA response.
- *
- * Handling includes changing the header fields into CPU format and
- * creating the stream, provided the add BA is accepted.
- */
-int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp)
-{
- struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
- int tid, win_size;
- struct mwifiex_rx_reorder_tbl *tbl;
- uint16_t block_ack_param_set;
-
- block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
-
- tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
- >> BLOCKACKPARAM_TID_POS;
- /*
- * Check if we had rejected the ADDBA, if yes then do not create
- * the stream
- */
- if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
- mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n",
- add_ba_rsp->peer_mac_addr, tid);
-
- tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
- add_ba_rsp->peer_mac_addr);
- if (tbl)
- mwifiex_del_rx_reorder_entry(priv, tbl);
-
- return 0;
- }
-
- win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
- >> BLOCKACKPARAM_WINSIZE_POS;
-
- tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
- add_ba_rsp->peer_mac_addr);
- if (tbl) {
- if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
- priv->add_ba_param.rx_amsdu &&
- (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
- tbl->amsdu = true;
- else
- tbl->amsdu = false;
- }
-
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
- add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size);
-
- return 0;
-}
-
-/*
- * This function handles BA stream timeout event by preparing and sending
- * a command to the firmware.
- */
-void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv,
- struct host_cmd_ds_11n_batimeout *event)
-{
- struct host_cmd_ds_11n_delba delba;
-
- memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba));
- memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN);
-
- delba.del_ba_param_set |=
- cpu_to_le16((u16) event->tid << DELBA_TID_POS);
- delba.del_ba_param_set |= cpu_to_le16(
- (u16) event->origninator << DELBA_INITIATOR_POS);
- delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
- mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false);
-}
-
-/*
- * This function cleans up the Rx reorder table by deleting all the entries
- * and re-initializing.
- */
-void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv)
-{
- struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- list_for_each_entry_safe(del_tbl_ptr, tmp_node,
- &priv->rx_reorder_tbl_ptr, list) {
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
- mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr);
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
- }
- INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
-
- mwifiex_reset_11n_rx_seq_num(priv);
-}
-
-/*
- * This function updates all rx_reorder_tbl's flags.
- */
-void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
-{
- struct mwifiex_private *priv;
- struct mwifiex_rx_reorder_tbl *tbl;
- unsigned long lock_flags;
- int i;
-
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
- if (!priv)
- continue;
-
- spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags);
- if (list_empty(&priv->rx_reorder_tbl_ptr)) {
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
- lock_flags);
- continue;
- }
-
- list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
- tbl->flags = flags;
- spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags);
- }
-
- return;
-}
-
-/* This function update all the rx_win_size based on coex flag
- */
-static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter,
- bool coex_flag)
-{
- u8 i;
- u32 rx_win_size;
- struct mwifiex_private *priv;
-
- dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag);
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (!adapter->priv[i])
- continue;
- priv = adapter->priv[i];
- rx_win_size = priv->add_ba_param.rx_win_size;
- if (coex_flag) {
- if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
- priv->add_ba_param.rx_win_size =
- MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE;
- if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
- priv->add_ba_param.rx_win_size =
- MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE;
- if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)
- priv->add_ba_param.rx_win_size =
- MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE;
- } else {
- if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
- priv->add_ba_param.rx_win_size =
- MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
- if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
- priv->add_ba_param.rx_win_size =
- MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
- if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)
- priv->add_ba_param.rx_win_size =
- MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;
- }
-
- if (adapter->coex_win_size && adapter->coex_rx_win_size)
- priv->add_ba_param.rx_win_size =
- adapter->coex_rx_win_size;
-
- if (rx_win_size != priv->add_ba_param.rx_win_size) {
- if (!priv->media_connected)
- continue;
- for (i = 0; i < MAX_NUM_TID; i++)
- mwifiex_11n_delba(priv, i);
- }
- }
-}
-
-/* This function check coex for RX BA
- */
-void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter)
-{
- u8 i;
- struct mwifiex_private *priv;
- u8 count = 0;
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- priv = adapter->priv[i];
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
- if (priv->media_connected)
- count++;
- }
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
- if (priv->bss_started)
- count++;
- }
- }
- if (count >= MWIFIEX_BSS_COEX_COUNT)
- break;
- }
- if (count >= MWIFIEX_BSS_COEX_COUNT)
- mwifiex_update_ampdu_rxwinsize(adapter, true);
- else
- mwifiex_update_ampdu_rxwinsize(adapter, false);
-}
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
deleted file mode 100644
index 4073116e6e9f..000000000000
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ /dev/null
@@ -1,3887 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: CFG80211
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "cfg80211.h"
-#include "main.h"
-#include "11n.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) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_CLIENT),
- },
- {
- .max = 1, .types = BIT(NL80211_IFTYPE_AP),
- },
-};
-
-static const struct ieee80211_iface_combination
-mwifiex_iface_comb_ap_sta = {
- .limits = mwifiex_ap_sta_limits,
- .num_different_channels = 1,
- .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
- .max_interfaces = MWIFIEX_MAX_BSS_NUM,
- .beacon_int_infra_match = true,
- .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- BIT(NL80211_CHAN_WIDTH_20) |
- BIT(NL80211_CHAN_WIDTH_40),
-};
-
-static const struct ieee80211_iface_combination
-mwifiex_iface_comb_ap_sta_vht = {
- .limits = mwifiex_ap_sta_limits,
- .num_different_channels = 1,
- .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
- .max_interfaces = MWIFIEX_MAX_BSS_NUM,
- .beacon_int_infra_match = true,
- .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- BIT(NL80211_CHAN_WIDTH_20) |
- BIT(NL80211_CHAN_WIDTH_40) |
- BIT(NL80211_CHAN_WIDTH_80),
-};
-
-static const struct
-ieee80211_iface_combination mwifiex_iface_comb_ap_sta_drcs = {
- .limits = mwifiex_ap_sta_limits,
- .num_different_channels = 2,
- .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
- .max_interfaces = MWIFIEX_MAX_BSS_NUM,
- .beacon_int_infra_match = true,
-};
-
-/*
- * This function maps the nl802.11 channel type into driver channel type.
- *
- * The mapping is as follows -
- * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE
- * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE
- * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE
- * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW
- * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE
- */
-u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type)
-{
- switch (chan_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
- return IEEE80211_HT_PARAM_CHA_SEC_NONE;
- case NL80211_CHAN_HT40PLUS:
- return IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- case NL80211_CHAN_HT40MINUS:
- return IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- default:
- return IEEE80211_HT_PARAM_CHA_SEC_NONE;
- }
-}
-
-/* This function maps IEEE HT secondary channel type to NL80211 channel type
- */
-u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset)
-{
- switch (second_chan_offset) {
- case IEEE80211_HT_PARAM_CHA_SEC_NONE:
- return NL80211_CHAN_HT20;
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- return NL80211_CHAN_HT40PLUS;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- return NL80211_CHAN_HT40MINUS;
- default:
- return NL80211_CHAN_HT20;
- }
-}
-
-/*
- * This function checks whether WEP is set.
- */
-static int
-mwifiex_is_alg_wep(u32 cipher)
-{
- switch (cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * This function retrieves the private structure from kernel wiphy structure.
- */
-static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy)
-{
- return (void *) (*(unsigned long *) wiphy_priv(wiphy));
-}
-
-/*
- * CFG802.11 operation handler to delete a network key.
- */
-static int
-mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
- u8 key_index, bool pairwise, const u8 *mac_addr)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev);
- const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
-
- if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) {
- mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n");
- return -EFAULT;
- }
-
- mwifiex_dbg(priv->adapter, INFO, "info: crypto keys deleted\n");
- return 0;
-}
-
-/*
- * This function forms an skb for management frame.
- */
-static int
-mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
-{
- u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
- u16 pkt_len;
- u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
-
- pkt_len = len + ETH_ALEN;
-
- skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN +
- MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
- memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len));
-
- memcpy(skb_push(skb, sizeof(tx_control)),
- &tx_control, sizeof(tx_control));
-
- memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type));
-
- /* Add packet data and address4 */
- memcpy(skb_put(skb, sizeof(struct ieee80211_hdr_3addr)), buf,
- sizeof(struct ieee80211_hdr_3addr));
- memcpy(skb_put(skb, ETH_ALEN), addr, ETH_ALEN);
- memcpy(skb_put(skb, len - sizeof(struct ieee80211_hdr_3addr)),
- buf + sizeof(struct ieee80211_hdr_3addr),
- len - sizeof(struct ieee80211_hdr_3addr));
-
- skb->priority = LOW_PRIO_TID;
- __net_timestamp(skb);
-
- return 0;
-}
-
-/*
- * CFG802.11 operation handler to transmit a management frame.
- */
-static int
-mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct cfg80211_mgmt_tx_params *params, u64 *cookie)
-{
- const u8 *buf = params->buf;
- size_t len = params->len;
- struct sk_buff *skb;
- u16 pkt_len;
- const struct ieee80211_mgmt *mgmt;
- struct mwifiex_txinfo *tx_info;
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
-
- if (!buf || !len) {
- mwifiex_dbg(priv->adapter, ERROR, "invalid buffer and length\n");
- return -EFAULT;
- }
-
- mgmt = (const struct ieee80211_mgmt *)buf;
- if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA &&
- ieee80211_is_probe_resp(mgmt->frame_control)) {
- /* Since we support offload probe resp, we need to skip probe
- * resp in AP or GO mode */
- mwifiex_dbg(priv->adapter, INFO,
- "info: skip to send probe resp in AP or GO mode\n");
- return 0;
- }
-
- pkt_len = len + ETH_ALEN;
- skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN +
- MWIFIEX_MGMT_FRAME_HEADER_SIZE +
- pkt_len + sizeof(pkt_len));
-
- if (!skb) {
- mwifiex_dbg(priv->adapter, ERROR,
- "allocate skb failed for management frame\n");
- return -ENOMEM;
- }
-
- tx_info = MWIFIEX_SKB_TXCB(skb);
- memset(tx_info, 0, sizeof(*tx_info));
- tx_info->bss_num = priv->bss_num;
- tx_info->bss_type = priv->bss_type;
- tx_info->pkt_len = pkt_len;
-
- mwifiex_form_mgmt_frame(skb, buf, len);
- *cookie = prandom_u32() | 1;
-
- if (ieee80211_is_action(mgmt->frame_control))
- skb = mwifiex_clone_skb_for_tx_status(priv,
- skb,
- MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie);
- else
- cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
- GFP_ATOMIC);
-
- mwifiex_queue_tx_pkt(priv, skb);
-
- mwifiex_dbg(priv->adapter, INFO, "info: management frame transmitted\n");
- return 0;
-}
-
-/*
- * CFG802.11 operation handler to register a mgmt frame.
- */
-static void
-mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u16 frame_type, bool reg)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
- u32 mask;
-
- if (reg)
- mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
- else
- mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
-
- if (mask != priv->mgmt_frame_mask) {
- priv->mgmt_frame_mask = mask;
- mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
- HostCmd_ACT_GEN_SET, 0,
- &priv->mgmt_frame_mask, false);
- mwifiex_dbg(priv->adapter, INFO, "info: mgmt frame registered\n");
- }
-}
-
-/*
- * CFG802.11 operation handler to remain on channel.
- */
-static int
-mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- struct ieee80211_channel *chan,
- unsigned int duration, u64 *cookie)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
- int ret;
-
- if (!chan || !cookie) {
- mwifiex_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n");
- return -EINVAL;
- }
-
- if (priv->roc_cfg.cookie) {
- mwifiex_dbg(priv->adapter, INFO,
- "info: ongoing ROC, cookie = 0x%llx\n",
- priv->roc_cfg.cookie);
- return -EBUSY;
- }
-
- ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan,
- duration);
-
- if (!ret) {
- *cookie = prandom_u32() | 1;
- priv->roc_cfg.cookie = *cookie;
- priv->roc_cfg.chan = *chan;
-
- cfg80211_ready_on_channel(wdev, *cookie, chan,
- duration, GFP_ATOMIC);
-
- mwifiex_dbg(priv->adapter, INFO,
- "info: ROC, cookie = 0x%llx\n", *cookie);
- }
-
- return ret;
-}
-
-/*
- * CFG802.11 operation handler to cancel remain on channel.
- */
-static int
-mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev, u64 cookie)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
- int ret;
-
- if (cookie != priv->roc_cfg.cookie)
- return -ENOENT;
-
- ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE,
- &priv->roc_cfg.chan, 0);
-
- if (!ret) {
- cfg80211_remain_on_channel_expired(wdev, cookie,
- &priv->roc_cfg.chan,
- GFP_ATOMIC);
-
- memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg));
-
- mwifiex_dbg(priv->adapter, INFO,
- "info: cancel ROC, cookie = 0x%llx\n", cookie);
- }
-
- return ret;
-}
-
-/*
- * CFG802.11 operation handler to set Tx power.
- */
-static int
-mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- enum nl80211_tx_power_setting type,
- int mbm)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_private *priv;
- struct mwifiex_power_cfg power_cfg;
- int dbm = MBM_TO_DBM(mbm);
-
- if (type == NL80211_TX_POWER_FIXED) {
- power_cfg.is_power_auto = 0;
- power_cfg.power_level = dbm;
- } else {
- power_cfg.is_power_auto = 1;
- }
-
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-
- return mwifiex_set_tx_power(priv, &power_cfg);
-}
-
-/*
- * CFG802.11 operation handler to set Power Save option.
- *
- * The timeout value, if provided, is currently ignored.
- */
-static int
-mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy,
- struct net_device *dev,
- bool enabled, int timeout)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- u32 ps_mode;
-
- if (timeout)
- mwifiex_dbg(priv->adapter, INFO,
- "info: ignore timeout value for IEEE Power Save\n");
-
- ps_mode = enabled;
-
- return mwifiex_drv_set_power(priv, &ps_mode);
-}
-
-/*
- * CFG802.11 operation handler to set the default network key.
- */
-static int
-mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
- u8 key_index, bool unicast,
- bool multicast)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev);
-
- /* Return if WEP key not configured */
- if (!priv->sec_info.wep_enabled)
- return 0;
-
- if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) {
- priv->wep_key_curr_index = key_index;
- } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index,
- NULL, 0)) {
- mwifiex_dbg(priv->adapter, ERROR, "set default Tx key index\n");
- return -EFAULT;
- }
-
- return 0;
-}
-
-/*
- * CFG802.11 operation handler to add a network key.
- */
-static int
-mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
- u8 key_index, bool pairwise, const u8 *mac_addr,
- struct key_params *params)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev);
- struct mwifiex_wep_key *wep_key;
- const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP &&
- (params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- params->cipher == WLAN_CIPHER_SUITE_WEP104)) {
- if (params->key && params->key_len) {
- wep_key = &priv->wep_key[key_index];
- memset(wep_key, 0, sizeof(struct mwifiex_wep_key));
- memcpy(wep_key->key_material, params->key,
- params->key_len);
- wep_key->key_index = key_index;
- wep_key->key_length = params->key_len;
- priv->sec_info.wep_enabled = 1;
- }
- return 0;
- }
-
- if (mwifiex_set_encode(priv, params, params->key, params->key_len,
- key_index, peer_mac, 0)) {
- mwifiex_dbg(priv->adapter, ERROR, "crypto keys added\n");
- return -EFAULT;
- }
-
- return 0;
-}
-
-/*
- * This function sends domain information to the firmware.
- *
- * The following information are passed to the firmware -
- * - Country codes
- * - Sub bands (first channel, number of channels, maximum Tx power)
- */
-int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy)
-{
- u8 no_of_triplet = 0;
- struct ieee80211_country_ie_triplet *t;
- u8 no_of_parsed_chan = 0;
- u8 first_chan = 0, next_chan = 0, max_pwr = 0;
- u8 i, flag = 0;
- enum ieee80211_band band;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch;
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_private *priv;
- struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg;
-
- /* Set country code */
- domain_info->country_code[0] = adapter->country_code[0];
- domain_info->country_code[1] = adapter->country_code[1];
- domain_info->country_code[2] = ' ';
-
- band = mwifiex_band_to_radio_type(adapter->config_bands);
- if (!wiphy->bands[band]) {
- mwifiex_dbg(adapter, ERROR,
- "11D: setting domain info in FW\n");
- return -1;
- }
-
- sband = wiphy->bands[band];
-
- for (i = 0; i < sband->n_channels ; i++) {
- ch = &sband->channels[i];
- if (ch->flags & IEEE80211_CHAN_DISABLED)
- continue;
-
- if (!flag) {
- flag = 1;
- first_chan = (u32) ch->hw_value;
- next_chan = first_chan;
- max_pwr = ch->max_power;
- no_of_parsed_chan = 1;
- continue;
- }
-
- if (ch->hw_value == next_chan + 1 &&
- ch->max_power == max_pwr) {
- next_chan++;
- no_of_parsed_chan++;
- } else {
- t = &domain_info->triplet[no_of_triplet];
- t->chans.first_channel = first_chan;
- t->chans.num_channels = no_of_parsed_chan;
- t->chans.max_power = max_pwr;
- no_of_triplet++;
- first_chan = (u32) ch->hw_value;
- next_chan = first_chan;
- max_pwr = ch->max_power;
- no_of_parsed_chan = 1;
- }
- }
-
- if (flag) {
- t = &domain_info->triplet[no_of_triplet];
- t->chans.first_channel = first_chan;
- t->chans.num_channels = no_of_parsed_chan;
- t->chans.max_power = max_pwr;
- no_of_triplet++;
- }
-
- domain_info->no_of_triplet = no_of_triplet;
-
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
- HostCmd_ACT_GEN_SET, 0, NULL, false)) {
- mwifiex_dbg(adapter, INFO,
- "11D: setting domain info in FW\n");
- return -1;
- }
-
- return 0;
-}
-
-/*
- * CFG802.11 regulatory domain callback function.
- *
- * This function is called when the regulatory domain is changed due to the
- * following reasons -
- * - Set by driver
- * - Set by system core
- * - Set by user
- * - Set bt Country IE
- */
-static void mwifiex_reg_notifier(struct wiphy *wiphy,
- struct regulatory_request *request)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_private *priv = mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_ANY);
- mwifiex_dbg(adapter, INFO,
- "info: cfg80211 regulatory domain callback for %c%c\n",
- request->alpha2[0], request->alpha2[1]);
-
- switch (request->initiator) {
- case NL80211_REGDOM_SET_BY_DRIVER:
- case NL80211_REGDOM_SET_BY_CORE:
- case NL80211_REGDOM_SET_BY_USER:
- case NL80211_REGDOM_SET_BY_COUNTRY_IE:
- break;
- default:
- mwifiex_dbg(adapter, ERROR,
- "unknown regdom initiator: %d\n",
- request->initiator);
- return;
- }
-
- /* Don't send world or same regdom info to firmware */
- if (strncmp(request->alpha2, "00", 2) &&
- strncmp(request->alpha2, adapter->country_code,
- sizeof(request->alpha2))) {
- memcpy(adapter->country_code, request->alpha2,
- sizeof(request->alpha2));
- mwifiex_send_domain_info_cmd_fw(wiphy);
- mwifiex_dnld_txpwr_table(priv);
- }
-}
-
-/*
- * This function sets the fragmentation threshold.
- *
- * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE
- * and MWIFIEX_FRAG_MAX_VALUE.
- */
-static int
-mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr)
-{
- if (frag_thr < MWIFIEX_FRAG_MIN_VALUE ||
- frag_thr > MWIFIEX_FRAG_MAX_VALUE)
- frag_thr = MWIFIEX_FRAG_MAX_VALUE;
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_SET, FRAG_THRESH_I,
- &frag_thr, true);
-}
-
-/*
- * This function sets the RTS threshold.
-
- * The rts value must lie between MWIFIEX_RTS_MIN_VALUE
- * and MWIFIEX_RTS_MAX_VALUE.
- */
-static int
-mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr)
-{
- if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE)
- rts_thr = MWIFIEX_RTS_MAX_VALUE;
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_SET, RTS_THRESH_I,
- &rts_thr, true);
-}
-
-/*
- * CFG802.11 operation handler to set wiphy parameters.
- *
- * This function can be used to set the RTS threshold and the
- * Fragmentation threshold of the driver.
- */
-static int
-mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_private *priv;
- struct mwifiex_uap_bss_param *bss_cfg;
- int ret;
-
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-
- switch (priv->bss_role) {
- case MWIFIEX_BSS_ROLE_UAP:
- if (priv->bss_started) {
- mwifiex_dbg(adapter, ERROR,
- "cannot change wiphy params when bss started");
- return -EINVAL;
- }
-
- bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL);
- if (!bss_cfg)
- return -ENOMEM;
-
- mwifiex_set_sys_config_invalid_data(bss_cfg);
-
- if (changed & WIPHY_PARAM_RTS_THRESHOLD)
- bss_cfg->rts_threshold = wiphy->rts_threshold;
- if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
- bss_cfg->frag_threshold = wiphy->frag_threshold;
- if (changed & WIPHY_PARAM_RETRY_LONG)
- bss_cfg->retry_limit = wiphy->retry_long;
-
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
- HostCmd_ACT_GEN_SET,
- UAP_BSS_PARAMS_I, bss_cfg,
- false);
-
- kfree(bss_cfg);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to set wiphy phy params\n");
- return ret;
- }
- break;
-
- case MWIFIEX_BSS_ROLE_STA:
- if (priv->media_connected) {
- mwifiex_dbg(adapter, ERROR,
- "cannot change wiphy params when connected");
- return -EINVAL;
- }
- if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
- ret = mwifiex_set_rts(priv,
- wiphy->rts_threshold);
- if (ret)
- return ret;
- }
- if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
- ret = mwifiex_set_frag(priv,
- wiphy->frag_threshold);
- if (ret)
- return ret;
- }
- break;
- }
-
- return 0;
-}
-
-static int
-mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv)
-{
- u16 mode = P2P_MODE_DISABLE;
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode, true))
- return -1;
-
- return 0;
-}
-
-/*
- * This function initializes the functionalities for P2P client.
- * The P2P client initialization sequence is:
- * disable -> device -> client
- */
-static int
-mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv)
-{
- u16 mode;
-
- if (mwifiex_cfg80211_deinit_p2p(priv))
- return -1;
-
- mode = P2P_MODE_DEVICE;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode, true))
- return -1;
-
- mode = P2P_MODE_CLIENT;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode, true))
- return -1;
-
- return 0;
-}
-
-/*
- * This function initializes the functionalities for P2P GO.
- * The P2P GO initialization sequence is:
- * disable -> device -> GO
- */
-static int
-mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv)
-{
- u16 mode;
-
- if (mwifiex_cfg80211_deinit_p2p(priv))
- return -1;
-
- mode = P2P_MODE_DEVICE;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode, true))
- return -1;
-
- mode = P2P_MODE_GO;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode, true))
- return -1;
-
- return 0;
-}
-
-static int mwifiex_deinit_priv_params(struct mwifiex_private *priv)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- unsigned long flags;
-
- priv->mgmt_frame_mask = 0;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
- HostCmd_ACT_GEN_SET, 0,
- &priv->mgmt_frame_mask, false)) {
- mwifiex_dbg(adapter, ERROR,
- "could not unregister mgmt frame rx\n");
- return -1;
- }
-
- mwifiex_deauthenticate(priv, NULL);
-
- spin_lock_irqsave(&adapter->main_proc_lock, flags);
- adapter->main_locked = true;
- if (adapter->mwifiex_processing) {
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
- flush_workqueue(adapter->workqueue);
- } else {
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
- }
-
- spin_lock_irqsave(&adapter->rx_proc_lock, flags);
- adapter->rx_locked = true;
- if (adapter->rx_processing) {
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
- flush_workqueue(adapter->rx_workqueue);
- } else {
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
- }
-
- mwifiex_free_priv(priv);
- priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
- priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
-
- return 0;
-}
-
-static int
-mwifiex_init_new_priv_params(struct mwifiex_private *priv,
- struct net_device *dev,
- enum nl80211_iftype type)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- unsigned long flags;
-
- mwifiex_init_priv(priv);
-
- priv->bss_mode = type;
- priv->wdev.iftype = type;
-
- mwifiex_init_priv_params(priv, priv->netdev);
- priv->bss_started = 0;
-
- switch (type) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- priv->bss_role = MWIFIEX_BSS_ROLE_STA;
- priv->bss_type = MWIFIEX_BSS_TYPE_STA;
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- priv->bss_role = MWIFIEX_BSS_ROLE_STA;
- priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
- break;
- case NL80211_IFTYPE_P2P_GO:
- priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
- priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
- break;
- case NL80211_IFTYPE_AP:
- priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
- priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
- break;
- default:
- mwifiex_dbg(adapter, ERROR,
- "%s: changing to %d not supported\n",
- dev->name, type);
- return -EOPNOTSUPP;
- }
-
- spin_lock_irqsave(&adapter->main_proc_lock, flags);
- adapter->main_locked = false;
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
-
- spin_lock_irqsave(&adapter->rx_proc_lock, flags);
- adapter->rx_locked = false;
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
-
- return 0;
-}
-
-static int
-mwifiex_change_vif_to_p2p(struct net_device *dev,
- enum nl80211_iftype curr_iftype,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params)
-{
- struct mwifiex_private *priv;
- struct mwifiex_adapter *adapter;
-
- priv = mwifiex_netdev_get_priv(dev);
-
- if (!priv)
- return -1;
-
- adapter = priv->adapter;
-
- if (adapter->curr_iface_comb.p2p_intf ==
- adapter->iface_limit.p2p_intf) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create multiple P2P ifaces\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, INFO,
- "%s: changing role to p2p\n", dev->name);
-
- if (mwifiex_deinit_priv_params(priv))
- return -1;
- if (mwifiex_init_new_priv_params(priv, dev, type))
- return -1;
-
- switch (type) {
- case NL80211_IFTYPE_P2P_CLIENT:
- if (mwifiex_cfg80211_init_p2p_client(priv))
- return -EFAULT;
- break;
- case NL80211_IFTYPE_P2P_GO:
- if (mwifiex_cfg80211_init_p2p_go(priv))
- return -EFAULT;
- break;
- default:
- mwifiex_dbg(adapter, ERROR,
- "%s: changing to %d not supported\n",
- dev->name, type);
- return -EOPNOTSUPP;
- }
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
- HostCmd_ACT_GEN_SET, 0, NULL, true))
- return -1;
-
- if (mwifiex_sta_init_cmd(priv, false, false))
- return -1;
-
- switch (curr_iftype) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- adapter->curr_iface_comb.sta_intf--;
- break;
- case NL80211_IFTYPE_AP:
- adapter->curr_iface_comb.uap_intf--;
- break;
- default:
- break;
- }
-
- adapter->curr_iface_comb.p2p_intf++;
- dev->ieee80211_ptr->iftype = type;
-
- return 0;
-}
-
-static int
-mwifiex_change_vif_to_sta_adhoc(struct net_device *dev,
- enum nl80211_iftype curr_iftype,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params)
-{
- struct mwifiex_private *priv;
- struct mwifiex_adapter *adapter;
-
- priv = mwifiex_netdev_get_priv(dev);
-
- if (!priv)
- return -1;
-
- adapter = priv->adapter;
-
- if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT &&
- curr_iftype != NL80211_IFTYPE_P2P_GO) &&
- (adapter->curr_iface_comb.sta_intf ==
- adapter->iface_limit.sta_intf)) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create multiple station/adhoc ifaces\n");
- return -1;
- }
-
- if (type == NL80211_IFTYPE_STATION)
- mwifiex_dbg(adapter, INFO,
- "%s: changing role to station\n", dev->name);
- else
- mwifiex_dbg(adapter, INFO,
- "%s: changing role to adhoc\n", dev->name);
-
- if (mwifiex_deinit_priv_params(priv))
- return -1;
- if (mwifiex_init_new_priv_params(priv, dev, type))
- return -1;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
- HostCmd_ACT_GEN_SET, 0, NULL, true))
- return -1;
- if (mwifiex_sta_init_cmd(priv, false, false))
- return -1;
-
- switch (curr_iftype) {
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- adapter->curr_iface_comb.p2p_intf--;
- break;
- case NL80211_IFTYPE_AP:
- adapter->curr_iface_comb.uap_intf--;
- break;
- default:
- break;
- }
-
- adapter->curr_iface_comb.sta_intf++;
- dev->ieee80211_ptr->iftype = type;
- return 0;
-}
-
-static int
-mwifiex_change_vif_to_ap(struct net_device *dev,
- enum nl80211_iftype curr_iftype,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params)
-{
- struct mwifiex_private *priv;
- struct mwifiex_adapter *adapter;
-
- priv = mwifiex_netdev_get_priv(dev);
-
- if (!priv)
- return -1;
-
- adapter = priv->adapter;
-
- if (adapter->curr_iface_comb.uap_intf ==
- adapter->iface_limit.uap_intf) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create multiple AP ifaces\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, INFO,
- "%s: changing role to AP\n", dev->name);
-
- if (mwifiex_deinit_priv_params(priv))
- return -1;
- if (mwifiex_init_new_priv_params(priv, dev, type))
- return -1;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
- HostCmd_ACT_GEN_SET, 0, NULL, true))
- return -1;
- if (mwifiex_sta_init_cmd(priv, false, false))
- return -1;
-
- switch (curr_iftype) {
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- adapter->curr_iface_comb.p2p_intf--;
- break;
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- adapter->curr_iface_comb.sta_intf--;
- break;
- default:
- break;
- }
-
- adapter->curr_iface_comb.uap_intf++;
- dev->ieee80211_ptr->iftype = type;
- return 0;
-}
-/*
- * CFG802.11 operation handler to change interface type.
- */
-static int
-mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
- struct net_device *dev,
- enum nl80211_iftype type, u32 *flags,
- struct vif_params *params)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype;
-
- switch (curr_iftype) {
- case NL80211_IFTYPE_ADHOC:
- switch (type) {
- case NL80211_IFTYPE_STATION:
- priv->bss_mode = type;
- priv->sec_info.authentication_mode =
- NL80211_AUTHTYPE_OPEN_SYSTEM;
- dev->ieee80211_ptr->iftype = type;
- mwifiex_deauthenticate(priv, NULL);
- return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
- HostCmd_ACT_GEN_SET, 0, NULL,
- true);
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- return mwifiex_change_vif_to_p2p(dev, curr_iftype,
- type, flags, params);
- case NL80211_IFTYPE_AP:
- return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
- flags, params);
- case NL80211_IFTYPE_UNSPECIFIED:
- mwifiex_dbg(priv->adapter, INFO,
- "%s: kept type as IBSS\n", dev->name);
- case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */
- return 0;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: changing to %d not supported\n",
- dev->name, type);
- return -EOPNOTSUPP;
- }
- break;
- case NL80211_IFTYPE_STATION:
- switch (type) {
- case NL80211_IFTYPE_ADHOC:
- priv->bss_mode = type;
- priv->sec_info.authentication_mode =
- NL80211_AUTHTYPE_OPEN_SYSTEM;
- dev->ieee80211_ptr->iftype = type;
- mwifiex_deauthenticate(priv, NULL);
- return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
- HostCmd_ACT_GEN_SET, 0, NULL,
- true);
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- return mwifiex_change_vif_to_p2p(dev, curr_iftype,
- type, flags, params);
- case NL80211_IFTYPE_AP:
- return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
- flags, params);
- case NL80211_IFTYPE_UNSPECIFIED:
- mwifiex_dbg(priv->adapter, INFO,
- "%s: kept type as STA\n", dev->name);
- case NL80211_IFTYPE_STATION: /* This shouldn't happen */
- return 0;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: changing to %d not supported\n",
- dev->name, type);
- return -EOPNOTSUPP;
- }
- break;
- case NL80211_IFTYPE_AP:
- switch (type) {
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_STATION:
- return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype,
- type, flags,
- params);
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- return mwifiex_change_vif_to_p2p(dev, curr_iftype,
- type, flags, params);
- case NL80211_IFTYPE_UNSPECIFIED:
- mwifiex_dbg(priv->adapter, INFO,
- "%s: kept type as AP\n", dev->name);
- case NL80211_IFTYPE_AP: /* This shouldn't happen */
- return 0;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: changing to %d not supported\n",
- dev->name, type);
- return -EOPNOTSUPP;
- }
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- switch (type) {
- case NL80211_IFTYPE_STATION:
- if (mwifiex_cfg80211_deinit_p2p(priv))
- return -EFAULT;
- priv->adapter->curr_iface_comb.p2p_intf--;
- priv->adapter->curr_iface_comb.sta_intf++;
- dev->ieee80211_ptr->iftype = type;
- break;
- case NL80211_IFTYPE_ADHOC:
- if (mwifiex_cfg80211_deinit_p2p(priv))
- return -EFAULT;
- return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype,
- type, flags,
- params);
- break;
- case NL80211_IFTYPE_AP:
- if (mwifiex_cfg80211_deinit_p2p(priv))
- return -EFAULT;
- return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
- flags, params);
- case NL80211_IFTYPE_UNSPECIFIED:
- mwifiex_dbg(priv->adapter, INFO,
- "%s: kept type as P2P\n", dev->name);
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- return 0;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: changing to %d not supported\n",
- dev->name, type);
- return -EOPNOTSUPP;
- }
- break;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: unknown iftype: %d\n",
- dev->name, dev->ieee80211_ptr->iftype);
- return -EOPNOTSUPP;
- }
-
-
- return 0;
-}
-
-static void
-mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo,
- struct rate_info *rate)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
-
- if (adapter->is_hw_11ac_capable) {
- /* bit[1-0]: 00=LG 01=HT 10=VHT */
- if (tx_htinfo & BIT(0)) {
- /* HT */
- rate->mcs = priv->tx_rate;
- rate->flags |= RATE_INFO_FLAGS_MCS;
- }
- if (tx_htinfo & BIT(1)) {
- /* VHT */
- rate->mcs = priv->tx_rate & 0x0F;
- rate->flags |= RATE_INFO_FLAGS_VHT_MCS;
- }
-
- if (tx_htinfo & (BIT(1) | BIT(0))) {
- /* HT or VHT */
- switch (tx_htinfo & (BIT(3) | BIT(2))) {
- case 0:
- rate->bw = RATE_INFO_BW_20;
- break;
- case (BIT(2)):
- rate->bw = RATE_INFO_BW_40;
- break;
- case (BIT(3)):
- rate->bw = RATE_INFO_BW_80;
- break;
- case (BIT(3) | BIT(2)):
- rate->bw = RATE_INFO_BW_160;
- break;
- }
-
- if (tx_htinfo & BIT(4))
- rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
-
- if ((priv->tx_rate >> 4) == 1)
- rate->nss = 2;
- else
- rate->nss = 1;
- }
- } else {
- /*
- * Bit 0 in tx_htinfo indicates that current Tx rate
- * is 11n rate. Valid MCS index values for us are 0 to 15.
- */
- if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
- rate->mcs = priv->tx_rate;
- rate->flags |= RATE_INFO_FLAGS_MCS;
- rate->bw = RATE_INFO_BW_20;
- if (tx_htinfo & BIT(1))
- rate->bw = RATE_INFO_BW_40;
- if (tx_htinfo & BIT(2))
- rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
- }
- }
-}
-
-/*
- * This function dumps the station information on a buffer.
- *
- * The following information are shown -
- * - Total bytes transmitted
- * - Total bytes received
- * - Total packets transmitted
- * - Total packets received
- * - Signal quality level
- * - Transmission rate
- */
-static int
-mwifiex_dump_station_info(struct mwifiex_private *priv,
- struct mwifiex_sta_node *node,
- struct station_info *sinfo)
-{
- u32 rate;
-
- sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) |
- BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) |
- BIT(NL80211_STA_INFO_TX_BITRATE) |
- BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG);
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
- if (!node)
- return -ENOENT;
-
- sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) |
- BIT(NL80211_STA_INFO_TX_FAILED);
- sinfo->inactive_time =
- jiffies_to_msecs(jiffies - node->stats.last_rx);
-
- sinfo->signal = node->stats.rssi;
- sinfo->signal_avg = node->stats.rssi;
- sinfo->rx_bytes = node->stats.rx_bytes;
- sinfo->tx_bytes = node->stats.tx_bytes;
- sinfo->rx_packets = node->stats.rx_packets;
- sinfo->tx_packets = node->stats.tx_packets;
- sinfo->tx_failed = node->stats.tx_failed;
-
- mwifiex_parse_htinfo(priv, node->stats.last_tx_htinfo,
- &sinfo->txrate);
- sinfo->txrate.legacy = node->stats.last_tx_rate * 5;
-
- return 0;
- }
-
- /* Get signal information from the firmware */
- if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
- HostCmd_ACT_GEN_GET, 0, NULL, true)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "failed to get signal information\n");
- return -EFAULT;
- }
-
- if (mwifiex_drv_get_data_rate(priv, &rate)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "getting data rate error\n");
- return -EFAULT;
- }
-
- /* Get DTIM period information from firmware */
- mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, DTIM_PERIOD_I,
- &priv->dtim_period, true);
-
- mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate);
-
- sinfo->signal_avg = priv->bcn_rssi_avg;
- sinfo->rx_bytes = priv->stats.rx_bytes;
- sinfo->tx_bytes = priv->stats.tx_bytes;
- sinfo->rx_packets = priv->stats.rx_packets;
- sinfo->tx_packets = priv->stats.tx_packets;
- sinfo->signal = priv->bcn_rssi_avg;
- /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */
- sinfo->txrate.legacy = rate * 5;
-
- if (priv->bss_mode == NL80211_IFTYPE_STATION) {
- sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
- sinfo->bss_param.flags = 0;
- if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
- WLAN_CAPABILITY_SHORT_PREAMBLE)
- sinfo->bss_param.flags |=
- BSS_PARAM_FLAGS_SHORT_PREAMBLE;
- if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
- WLAN_CAPABILITY_SHORT_SLOT_TIME)
- sinfo->bss_param.flags |=
- BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
- sinfo->bss_param.dtim_period = priv->dtim_period;
- sinfo->bss_param.beacon_interval =
- priv->curr_bss_params.bss_descriptor.beacon_period;
- }
-
- return 0;
-}
-
-/*
- * CFG802.11 operation handler to get station information.
- *
- * This function only works in connected mode, and dumps the
- * requested station information, if available.
- */
-static int
-mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac, struct station_info *sinfo)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (!priv->media_connected)
- return -ENOENT;
- if (memcmp(mac, priv->cfg_bssid, ETH_ALEN))
- return -ENOENT;
-
- return mwifiex_dump_station_info(priv, NULL, sinfo);
-}
-
-/*
- * CFG802.11 operation handler to dump station information.
- */
-static int
-mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
- int idx, u8 *mac, struct station_info *sinfo)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- static struct mwifiex_sta_node *node;
-
- if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
- priv->media_connected && idx == 0) {
- ether_addr_copy(mac, priv->cfg_bssid);
- return mwifiex_dump_station_info(priv, NULL, sinfo);
- } else if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
- mwifiex_send_cmd(priv, HOST_CMD_APCMD_STA_LIST,
- HostCmd_ACT_GEN_GET, 0, NULL, true);
-
- if (node && (&node->list == &priv->sta_list)) {
- node = NULL;
- return -ENOENT;
- }
-
- node = list_prepare_entry(node, &priv->sta_list, list);
- list_for_each_entry_continue(node, &priv->sta_list, list) {
- ether_addr_copy(mac, node->mac_addr);
- return mwifiex_dump_station_info(priv, node, sinfo);
- }
- }
-
- return -ENOENT;
-}
-
-static int
-mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
- int idx, struct survey_info *survey)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct mwifiex_chan_stats *pchan_stats = priv->adapter->chan_stats;
- enum ieee80211_band band;
-
- mwifiex_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx);
-
- memset(survey, 0, sizeof(struct survey_info));
-
- if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
- priv->media_connected && idx == 0) {
- u8 curr_bss_band = priv->curr_bss_params.band;
- u32 chan = priv->curr_bss_params.bss_descriptor.channel;
-
- band = mwifiex_band_to_radio_type(curr_bss_band);
- survey->channel = ieee80211_get_channel(wiphy,
- ieee80211_channel_to_frequency(chan, band));
-
- if (priv->bcn_nf_last) {
- survey->filled = SURVEY_INFO_NOISE_DBM;
- survey->noise = priv->bcn_nf_last;
- }
- return 0;
- }
-
- if (idx >= priv->adapter->num_in_chan_stats)
- return -ENOENT;
-
- if (!pchan_stats[idx].cca_scan_dur)
- return 0;
-
- band = pchan_stats[idx].bandcfg;
- survey->channel = ieee80211_get_channel(wiphy,
- ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band));
- survey->filled = SURVEY_INFO_NOISE_DBM |
- SURVEY_INFO_TIME |
- SURVEY_INFO_TIME_BUSY;
- survey->noise = pchan_stats[idx].noise;
- survey->time = pchan_stats[idx].cca_scan_dur;
- survey->time_busy = pchan_stats[idx].cca_busy_dur;
-
- return 0;
-}
-
-/* Supported rates to be advertised to the cfg80211 */
-static struct ieee80211_rate mwifiex_rates[] = {
- {.bitrate = 10, .hw_value = 2, },
- {.bitrate = 20, .hw_value = 4, },
- {.bitrate = 55, .hw_value = 11, },
- {.bitrate = 110, .hw_value = 22, },
- {.bitrate = 60, .hw_value = 12, },
- {.bitrate = 90, .hw_value = 18, },
- {.bitrate = 120, .hw_value = 24, },
- {.bitrate = 180, .hw_value = 36, },
- {.bitrate = 240, .hw_value = 48, },
- {.bitrate = 360, .hw_value = 72, },
- {.bitrate = 480, .hw_value = 96, },
- {.bitrate = 540, .hw_value = 108, },
-};
-
-/* Channel definitions to be advertised to cfg80211 */
-static struct ieee80211_channel mwifiex_channels_2ghz[] = {
- {.center_freq = 2412, .hw_value = 1, },
- {.center_freq = 2417, .hw_value = 2, },
- {.center_freq = 2422, .hw_value = 3, },
- {.center_freq = 2427, .hw_value = 4, },
- {.center_freq = 2432, .hw_value = 5, },
- {.center_freq = 2437, .hw_value = 6, },
- {.center_freq = 2442, .hw_value = 7, },
- {.center_freq = 2447, .hw_value = 8, },
- {.center_freq = 2452, .hw_value = 9, },
- {.center_freq = 2457, .hw_value = 10, },
- {.center_freq = 2462, .hw_value = 11, },
- {.center_freq = 2467, .hw_value = 12, },
- {.center_freq = 2472, .hw_value = 13, },
- {.center_freq = 2484, .hw_value = 14, },
-};
-
-static struct ieee80211_supported_band mwifiex_band_2ghz = {
- .channels = mwifiex_channels_2ghz,
- .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz),
- .bitrates = mwifiex_rates,
- .n_bitrates = ARRAY_SIZE(mwifiex_rates),
-};
-
-static struct ieee80211_channel mwifiex_channels_5ghz[] = {
- {.center_freq = 5040, .hw_value = 8, },
- {.center_freq = 5060, .hw_value = 12, },
- {.center_freq = 5080, .hw_value = 16, },
- {.center_freq = 5170, .hw_value = 34, },
- {.center_freq = 5190, .hw_value = 38, },
- {.center_freq = 5210, .hw_value = 42, },
- {.center_freq = 5230, .hw_value = 46, },
- {.center_freq = 5180, .hw_value = 36, },
- {.center_freq = 5200, .hw_value = 40, },
- {.center_freq = 5220, .hw_value = 44, },
- {.center_freq = 5240, .hw_value = 48, },
- {.center_freq = 5260, .hw_value = 52, },
- {.center_freq = 5280, .hw_value = 56, },
- {.center_freq = 5300, .hw_value = 60, },
- {.center_freq = 5320, .hw_value = 64, },
- {.center_freq = 5500, .hw_value = 100, },
- {.center_freq = 5520, .hw_value = 104, },
- {.center_freq = 5540, .hw_value = 108, },
- {.center_freq = 5560, .hw_value = 112, },
- {.center_freq = 5580, .hw_value = 116, },
- {.center_freq = 5600, .hw_value = 120, },
- {.center_freq = 5620, .hw_value = 124, },
- {.center_freq = 5640, .hw_value = 128, },
- {.center_freq = 5660, .hw_value = 132, },
- {.center_freq = 5680, .hw_value = 136, },
- {.center_freq = 5700, .hw_value = 140, },
- {.center_freq = 5745, .hw_value = 149, },
- {.center_freq = 5765, .hw_value = 153, },
- {.center_freq = 5785, .hw_value = 157, },
- {.center_freq = 5805, .hw_value = 161, },
- {.center_freq = 5825, .hw_value = 165, },
-};
-
-static struct ieee80211_supported_band mwifiex_band_5ghz = {
- .channels = mwifiex_channels_5ghz,
- .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz),
- .bitrates = mwifiex_rates + 4,
- .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4,
-};
-
-
-/* Supported crypto cipher suits to be advertised to cfg80211 */
-static const u32 mwifiex_cipher_suites[] = {
- WLAN_CIPHER_SUITE_WEP40,
- WLAN_CIPHER_SUITE_WEP104,
- WLAN_CIPHER_SUITE_TKIP,
- WLAN_CIPHER_SUITE_CCMP,
- WLAN_CIPHER_SUITE_AES_CMAC,
-};
-
-/* Supported mgmt frame types to be advertised to cfg80211 */
-static const struct ieee80211_txrx_stypes
-mwifiex_mgmt_stypes[NUM_NL80211_IFTYPES] = {
- [NL80211_IFTYPE_STATION] = {
- .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
- },
- [NL80211_IFTYPE_AP] = {
- .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
- },
- [NL80211_IFTYPE_P2P_CLIENT] = {
- .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
- },
- [NL80211_IFTYPE_P2P_GO] = {
- .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
- },
-};
-
-/*
- * CFG802.11 operation handler for setting bit rates.
- *
- * Function configures data rates to firmware using bitrate mask
- * provided by cfg80211.
- */
-static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
- struct net_device *dev,
- const u8 *peer,
- const struct cfg80211_bitrate_mask *mask)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- u16 bitmap_rates[MAX_BITMAP_RATES_SIZE];
- enum ieee80211_band band;
- struct mwifiex_adapter *adapter = priv->adapter;
-
- if (!priv->media_connected) {
- mwifiex_dbg(adapter, ERROR,
- "Can not set Tx data rate in disconnected state\n");
- return -EINVAL;
- }
-
- band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
-
- memset(bitmap_rates, 0, sizeof(bitmap_rates));
-
- /* Fill HR/DSSS rates. */
- if (band == IEEE80211_BAND_2GHZ)
- bitmap_rates[0] = mask->control[band].legacy & 0x000f;
-
- /* Fill OFDM rates */
- if (band == IEEE80211_BAND_2GHZ)
- bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4;
- else
- bitmap_rates[1] = mask->control[band].legacy;
-
- /* Fill HT MCS rates */
- bitmap_rates[2] = mask->control[band].ht_mcs[0];
- if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
- bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8;
-
- /* Fill VHT MCS rates */
- if (adapter->fw_api_ver == MWIFIEX_FW_V15) {
- bitmap_rates[10] = mask->control[band].vht_mcs[0];
- if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
- bitmap_rates[11] = mask->control[band].vht_mcs[1];
- }
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG,
- HostCmd_ACT_GEN_SET, 0, bitmap_rates, true);
-}
-
-/*
- * CFG802.11 operation handler for connection quality monitoring.
- *
- * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI
- * events to FW.
- */
-static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
- struct net_device *dev,
- s32 rssi_thold, u32 rssi_hyst)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct mwifiex_ds_misc_subsc_evt subsc_evt;
-
- priv->cqm_rssi_thold = rssi_thold;
- priv->cqm_rssi_hyst = rssi_hyst;
-
- memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt));
- subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
-
- /* Subscribe/unsubscribe low and high rssi events */
- if (rssi_thold && rssi_hyst) {
- subsc_evt.action = HostCmd_ACT_BITWISE_SET;
- subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold);
- subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold);
- subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
- subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
- return mwifiex_send_cmd(priv,
- HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
- 0, 0, &subsc_evt, true);
- } else {
- subsc_evt.action = HostCmd_ACT_BITWISE_CLR;
- return mwifiex_send_cmd(priv,
- HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
- 0, 0, &subsc_evt, true);
- }
-
- return 0;
-}
-
-/* cfg80211 operation handler for change_beacon.
- * Function retrieves and sets modified management IEs to FW.
- */
-static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_beacon_data *data)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) {
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: bss_type mismatched\n", __func__);
- return -EINVAL;
- }
-
- if (!priv->bss_started) {
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: bss not started\n", __func__);
- return -EINVAL;
- }
-
- if (mwifiex_set_mgmt_ies(priv, data)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: setting mgmt ies failed\n", __func__);
- return -EFAULT;
- }
-
- return 0;
-}
-
-/* cfg80211 operation handler for del_station.
- * Function deauthenticates station which value is provided in mac parameter.
- * If mac is NULL/broadcast, all stations in associated station list are
- * deauthenticated. If bss is not started or there are no stations in
- * associated stations list, no action is taken.
- */
-static int
-mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
- struct station_del_parameters *params)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct mwifiex_sta_node *sta_node;
- u8 deauth_mac[ETH_ALEN];
- unsigned long flags;
-
- if (list_empty(&priv->sta_list) || !priv->bss_started)
- return 0;
-
- if (!params->mac || is_broadcast_ether_addr(params->mac))
- return 0;
-
- mwifiex_dbg(priv->adapter, INFO, "%s: mac address %pM\n",
- __func__, params->mac);
-
- eth_zero_addr(deauth_mac);
-
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- sta_node = mwifiex_get_sta_entry(priv, params->mac);
- if (sta_node)
- ether_addr_copy(deauth_mac, params->mac);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-
- if (is_valid_ether_addr(deauth_mac)) {
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
- HostCmd_ACT_GEN_SET, 0,
- deauth_mac, true))
- return -1;
- }
-
- return 0;
-}
-
-static int
-mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_private *priv = mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_ANY);
- struct mwifiex_ds_ant_cfg ant_cfg;
-
- if (!tx_ant || !rx_ant)
- return -EOPNOTSUPP;
-
- if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) {
- /* Not a MIMO chip. User should provide specific antenna number
- * for Tx/Rx path or enable all antennas for diversity
- */
- if (tx_ant != rx_ant)
- return -EOPNOTSUPP;
-
- if ((tx_ant & (tx_ant - 1)) &&
- (tx_ant != BIT(adapter->number_of_antenna) - 1))
- return -EOPNOTSUPP;
-
- if ((tx_ant == BIT(adapter->number_of_antenna) - 1) &&
- (priv->adapter->number_of_antenna > 1)) {
- tx_ant = RF_ANTENNA_AUTO;
- rx_ant = RF_ANTENNA_AUTO;
- }
- } else {
- struct ieee80211_sta_ht_cap *ht_info;
- int rx_mcs_supp;
- enum ieee80211_band band;
-
- if ((tx_ant == 0x1 && rx_ant == 0x1)) {
- adapter->user_dev_mcs_support = HT_STREAM_1X1;
- if (adapter->is_hw_11ac_capable)
- adapter->usr_dot_11ac_mcs_support =
- MWIFIEX_11AC_MCS_MAP_1X1;
- } else {
- adapter->user_dev_mcs_support = HT_STREAM_2X2;
- if (adapter->is_hw_11ac_capable)
- adapter->usr_dot_11ac_mcs_support =
- MWIFIEX_11AC_MCS_MAP_2X2;
- }
-
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- if (!adapter->wiphy->bands[band])
- continue;
-
- ht_info = &adapter->wiphy->bands[band]->ht_cap;
- rx_mcs_supp =
- GET_RXMCSSUPP(adapter->user_dev_mcs_support);
- memset(&ht_info->mcs, 0, adapter->number_of_antenna);
- memset(&ht_info->mcs, 0xff, rx_mcs_supp);
- }
- }
-
- ant_cfg.tx_ant = tx_ant;
- ant_cfg.rx_ant = rx_ant;
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA,
- HostCmd_ACT_GEN_SET, 0, &ant_cfg, true);
-}
-
-/* cfg80211 operation handler for stop ap.
- * Function stops BSS running at uAP interface.
- */
-static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- mwifiex_abort_cac(priv);
-
- if (mwifiex_del_mgmt_ies(priv))
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to delete mgmt IEs!\n");
-
- priv->ap_11n_enabled = 0;
- memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg));
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
- HostCmd_ACT_GEN_SET, 0, NULL, true)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to stop the BSS\n");
- return -1;
- }
-
- if (mwifiex_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET,
- HostCmd_ACT_GEN_SET, 0, NULL, true)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to reset BSS\n");
- return -1;
- }
-
- if (netif_carrier_ok(priv->netdev))
- netif_carrier_off(priv->netdev);
- mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
-
- return 0;
-}
-
-/* cfg80211 operation handler for start_ap.
- * Function sets beacon period, DTIM period, SSID and security into
- * AP config structure.
- * AP is configured with these settings and BSS is started.
- */
-static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_ap_settings *params)
-{
- struct mwifiex_uap_bss_param *bss_cfg;
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP)
- return -1;
-
- bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL);
- if (!bss_cfg)
- return -ENOMEM;
-
- mwifiex_set_sys_config_invalid_data(bss_cfg);
-
- if (params->beacon_interval)
- bss_cfg->beacon_period = params->beacon_interval;
- if (params->dtim_period)
- bss_cfg->dtim_period = params->dtim_period;
-
- if (params->ssid && params->ssid_len) {
- memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len);
- bss_cfg->ssid.ssid_len = params->ssid_len;
- }
- if (params->inactivity_timeout > 0) {
- /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */
- bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout;
- bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout;
- }
-
- switch (params->hidden_ssid) {
- case NL80211_HIDDEN_SSID_NOT_IN_USE:
- bss_cfg->bcast_ssid_ctl = 1;
- break;
- case NL80211_HIDDEN_SSID_ZERO_LEN:
- bss_cfg->bcast_ssid_ctl = 0;
- break;
- case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
- /* firmware doesn't support this type of hidden SSID */
- default:
- kfree(bss_cfg);
- return -EINVAL;
- }
-
- mwifiex_uap_set_channel(priv, bss_cfg, params->chandef);
- mwifiex_set_uap_rates(bss_cfg, params);
-
- if (mwifiex_set_secure_params(priv, bss_cfg, params)) {
- kfree(bss_cfg);
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to parse secuirty parameters!\n");
- return -1;
- }
-
- mwifiex_set_ht_params(priv, bss_cfg, params);
-
- if (priv->adapter->is_hw_11ac_capable) {
- mwifiex_set_vht_params(priv, bss_cfg, params);
- mwifiex_set_vht_width(priv, params->chandef.width,
- priv->ap_11ac_enabled);
- }
-
- if (priv->ap_11ac_enabled)
- mwifiex_set_11ac_ba_params(priv);
- else
- mwifiex_set_ba_params(priv);
-
- mwifiex_set_wmm_params(priv, bss_cfg, params);
-
- if (mwifiex_is_11h_active(priv))
- mwifiex_set_tpc_params(priv, bss_cfg, params);
-
- if (mwifiex_is_11h_active(priv) &&
- !cfg80211_chandef_dfs_required(wiphy, &params->chandef,
- priv->bss_mode)) {
- mwifiex_dbg(priv->adapter, INFO,
- "Disable 11h extensions in FW\n");
- if (mwifiex_11h_activate(priv, false)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to disable 11h extensions!!");
- return -1;
- }
- priv->state_11h.is_11h_active = false;
- }
-
- if (mwifiex_config_start_uap(priv, bss_cfg)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to start AP\n");
- kfree(bss_cfg);
- return -1;
- }
-
- if (mwifiex_set_mgmt_ies(priv, &params->beacon))
- return -1;
-
- if (!netif_carrier_ok(priv->netdev))
- netif_carrier_on(priv->netdev);
- mwifiex_wake_up_net_dev_queue(priv->netdev, priv->adapter);
-
- memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg));
- kfree(bss_cfg);
- return 0;
-}
-
-/*
- * CFG802.11 operation handler for disconnection request.
- *
- * This function does not work when there is already a disconnection
- * procedure going on.
- */
-static int
-mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
- u16 reason_code)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (mwifiex_deauthenticate(priv, NULL))
- return -EFAULT;
-
- mwifiex_dbg(priv->adapter, MSG,
- "info: successfully disconnected from %pM:\t"
- "reason code %d\n", priv->cfg_bssid, reason_code);
-
- eth_zero_addr(priv->cfg_bssid);
- priv->hs2_enabled = false;
-
- return 0;
-}
-
-/*
- * This function informs the CFG802.11 subsystem of a new IBSS.
- *
- * The following information are sent to the CFG802.11 subsystem
- * to register the new IBSS. If we do not register the new IBSS,
- * a kernel panic will result.
- * - SSID
- * - SSID length
- * - BSSID
- * - Channel
- */
-static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
-{
- struct ieee80211_channel *chan;
- struct mwifiex_bss_info bss_info;
- struct cfg80211_bss *bss;
- int ie_len;
- u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)];
- enum ieee80211_band band;
-
- if (mwifiex_get_bss_info(priv, &bss_info))
- return -1;
-
- ie_buf[0] = WLAN_EID_SSID;
- ie_buf[1] = bss_info.ssid.ssid_len;
-
- memcpy(&ie_buf[sizeof(struct ieee_types_header)],
- &bss_info.ssid.ssid, bss_info.ssid.ssid_len);
- ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
-
- band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
- chan = __ieee80211_get_channel(priv->wdev.wiphy,
- ieee80211_channel_to_frequency(bss_info.bss_chan,
- band));
-
- bss = cfg80211_inform_bss(priv->wdev.wiphy, chan,
- CFG80211_BSS_FTYPE_UNKNOWN,
- bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
- 0, ie_buf, ie_len, 0, GFP_KERNEL);
- if (bss) {
- cfg80211_put_bss(priv->wdev.wiphy, bss);
- ether_addr_copy(priv->cfg_bssid, bss_info.bssid);
- }
-
- return 0;
-}
-
-/*
- * This function connects with a BSS.
- *
- * This function handles both Infra and Ad-Hoc modes. It also performs
- * validity checking on the provided parameters, disconnects from the
- * current BSS (if any), sets up the association/scan parameters,
- * including security settings, and performs specific SSID scan before
- * trying to connect.
- *
- * For Infra mode, the function returns failure if the specified SSID
- * is not found in scan table. However, for Ad-Hoc mode, it can create
- * the IBSS if it does not exist. On successful completion in either case,
- * the function notifies the CFG802.11 subsystem of the new BSS connection.
- */
-static int
-mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len,
- const u8 *ssid, const u8 *bssid, int mode,
- struct ieee80211_channel *channel,
- struct cfg80211_connect_params *sme, bool privacy)
-{
- struct cfg80211_ssid req_ssid;
- int ret, auth_type = 0;
- struct cfg80211_bss *bss = NULL;
- u8 is_scanning_required = 0;
-
- memset(&req_ssid, 0, sizeof(struct cfg80211_ssid));
-
- req_ssid.ssid_len = ssid_len;
- if (ssid_len > IEEE80211_MAX_SSID_LEN) {
- mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n");
- return -EINVAL;
- }
-
- memcpy(req_ssid.ssid, ssid, ssid_len);
- if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) {
- mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n");
- return -EINVAL;
- }
-
- /* As this is new association, clear locally stored
- * keys and security related flags */
- priv->sec_info.wpa_enabled = false;
- priv->sec_info.wpa2_enabled = false;
- priv->wep_key_curr_index = 0;
- priv->sec_info.encryption_mode = 0;
- priv->sec_info.is_authtype_auto = 0;
- ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1);
-
- if (mode == NL80211_IFTYPE_ADHOC) {
- /* "privacy" is set only for ad-hoc mode */
- if (privacy) {
- /*
- * Keep WLAN_CIPHER_SUITE_WEP104 for now so that
- * the firmware can find a matching network from the
- * scan. The cfg80211 does not give us the encryption
- * mode at this stage so just setting it to WEP here.
- */
- priv->sec_info.encryption_mode =
- WLAN_CIPHER_SUITE_WEP104;
- priv->sec_info.authentication_mode =
- NL80211_AUTHTYPE_OPEN_SYSTEM;
- }
-
- goto done;
- }
-
- /* Now handle infra mode. "sme" is valid for infra mode only */
- if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
- auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- priv->sec_info.is_authtype_auto = 1;
- } else {
- auth_type = sme->auth_type;
- }
-
- if (sme->crypto.n_ciphers_pairwise) {
- priv->sec_info.encryption_mode =
- sme->crypto.ciphers_pairwise[0];
- priv->sec_info.authentication_mode = auth_type;
- }
-
- if (sme->crypto.cipher_group) {
- priv->sec_info.encryption_mode = sme->crypto.cipher_group;
- priv->sec_info.authentication_mode = auth_type;
- }
- if (sme->ie)
- ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len);
-
- if (sme->key) {
- if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) {
- mwifiex_dbg(priv->adapter, INFO,
- "info: setting wep encryption\t"
- "with key len %d\n", sme->key_len);
- priv->wep_key_curr_index = sme->key_idx;
- ret = mwifiex_set_encode(priv, NULL, sme->key,
- sme->key_len, sme->key_idx,
- NULL, 0);
- }
- }
-done:
- /*
- * Scan entries are valid for some time (15 sec). So we can save one
- * active scan time if we just try cfg80211_get_bss first. If it fails
- * then request scan and cfg80211_get_bss() again for final output.
- */
- while (1) {
- if (is_scanning_required) {
- /* Do specific SSID scanning */
- if (mwifiex_request_scan(priv, &req_ssid)) {
- mwifiex_dbg(priv->adapter, ERROR, "scan error\n");
- return -EFAULT;
- }
- }
-
- /* Find the BSS we want using available scan results */
- if (mode == NL80211_IFTYPE_ADHOC)
- bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
- bssid, ssid, ssid_len,
- IEEE80211_BSS_TYPE_IBSS,
- IEEE80211_PRIVACY_ANY);
- else
- bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
- bssid, ssid, ssid_len,
- IEEE80211_BSS_TYPE_ESS,
- IEEE80211_PRIVACY_ANY);
-
- if (!bss) {
- if (is_scanning_required) {
- mwifiex_dbg(priv->adapter, WARN,
- "assoc: requested bss not found in scan results\n");
- break;
- }
- is_scanning_required = 1;
- } else {
- mwifiex_dbg(priv->adapter, MSG,
- "info: trying to associate to '%s' bssid %pM\n",
- (char *)req_ssid.ssid, bss->bssid);
- memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN);
- break;
- }
- }
-
- ret = mwifiex_bss_start(priv, bss, &req_ssid);
- if (ret)
- return ret;
-
- if (mode == NL80211_IFTYPE_ADHOC) {
- /* Inform the BSS information to kernel, otherwise
- * kernel will give a panic after successful assoc */
- if (mwifiex_cfg80211_inform_ibss_bss(priv))
- return -EFAULT;
- }
-
- return ret;
-}
-
-/*
- * CFG802.11 operation handler for association request.
- *
- * This function does not work when the current mode is set to Ad-Hoc, or
- * when there is already an association procedure going on. The given BSS
- * information is used to associate.
- */
-static int
-mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_connect_params *sme)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct mwifiex_adapter *adapter = priv->adapter;
- int ret;
-
- if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) {
- mwifiex_dbg(adapter, ERROR,
- "%s: reject infra assoc request in non-STA role\n",
- dev->name);
- return -EINVAL;
- }
-
- if (priv->wdev.current_bss) {
- mwifiex_dbg(adapter, ERROR,
- "%s: already connected\n", dev->name);
- return -EALREADY;
- }
-
- if (adapter->surprise_removed || adapter->is_cmd_timedout) {
- mwifiex_dbg(adapter, ERROR,
- "%s: Ignore connection.\t"
- "Card removed or FW in bad state\n",
- dev->name);
- return -EFAULT;
- }
-
- mwifiex_dbg(adapter, INFO,
- "info: Trying to associate to %s and bssid %pM\n",
- (char *)sme->ssid, sme->bssid);
-
- ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
- priv->bss_mode, sme->channel, sme, 0);
- if (!ret) {
- cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0,
- NULL, 0, WLAN_STATUS_SUCCESS,
- GFP_KERNEL);
- mwifiex_dbg(priv->adapter, MSG,
- "info: associated to bssid %pM successfully\n",
- priv->cfg_bssid);
- if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
- priv->adapter->auto_tdls &&
- priv->bss_type == MWIFIEX_BSS_TYPE_STA)
- mwifiex_setup_auto_tdls_timer(priv);
- } else {
- mwifiex_dbg(priv->adapter, ERROR,
- "info: association to bssid %pM failed\n",
- priv->cfg_bssid);
- eth_zero_addr(priv->cfg_bssid);
-
- if (ret > 0)
- cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
- NULL, 0, NULL, 0, ret,
- GFP_KERNEL);
- else
- cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
- NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
- }
-
- return 0;
-}
-
-/*
- * This function sets following parameters for ibss network.
- * - channel
- * - start band
- * - 11n flag
- * - secondary channel offset
- */
-static int mwifiex_set_ibss_params(struct mwifiex_private *priv,
- struct cfg80211_ibss_params *params)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- int index = 0, i;
- u8 config_bands = 0;
-
- if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) {
- if (!params->basic_rates) {
- config_bands = BAND_B | BAND_G;
- } else {
- for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) {
- /*
- * Rates below 6 Mbps in the table are CCK
- * rates; 802.11b and from 6 they are OFDM;
- * 802.11G
- */
- if (mwifiex_rates[i].bitrate == 60) {
- index = 1 << i;
- break;
- }
- }
-
- if (params->basic_rates < index) {
- config_bands = BAND_B;
- } else {
- config_bands = BAND_G;
- if (params->basic_rates % index)
- config_bands |= BAND_B;
- }
- }
-
- if (cfg80211_get_chandef_type(&params->chandef) !=
- NL80211_CHAN_NO_HT)
- config_bands |= BAND_G | BAND_GN;
- } else {
- if (cfg80211_get_chandef_type(&params->chandef) ==
- NL80211_CHAN_NO_HT)
- config_bands = BAND_A;
- else
- config_bands = BAND_AN | BAND_A;
- }
-
- if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) {
- adapter->config_bands = config_bands;
- adapter->adhoc_start_band = config_bands;
-
- if ((config_bands & BAND_GN) || (config_bands & BAND_AN))
- adapter->adhoc_11n_enabled = true;
- else
- adapter->adhoc_11n_enabled = false;
- }
-
- adapter->sec_chan_offset =
- mwifiex_chan_type_to_sec_chan_offset(
- cfg80211_get_chandef_type(&params->chandef));
- priv->adhoc_channel = ieee80211_frequency_to_channel(
- params->chandef.chan->center_freq);
-
- mwifiex_dbg(adapter, INFO,
- "info: set ibss band %d, chan %d, chan offset %d\n",
- config_bands, priv->adhoc_channel,
- adapter->sec_chan_offset);
-
- return 0;
-}
-
-/*
- * CFG802.11 operation handler to join an IBSS.
- *
- * This function does not work in any mode other than Ad-Hoc, or if
- * a join operation is already in progress.
- */
-static int
-mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_ibss_params *params)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- int ret = 0;
-
- if (priv->bss_mode != NL80211_IFTYPE_ADHOC) {
- mwifiex_dbg(priv->adapter, ERROR,
- "request to join ibss received\t"
- "when station is not in ibss mode\n");
- goto done;
- }
-
- mwifiex_dbg(priv->adapter, MSG,
- "info: trying to join to %s and bssid %pM\n",
- (char *)params->ssid, params->bssid);
-
- mwifiex_set_ibss_params(priv, params);
-
- ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid,
- params->bssid, priv->bss_mode,
- params->chandef.chan, NULL,
- params->privacy);
-done:
- if (!ret) {
- cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid,
- params->chandef.chan, GFP_KERNEL);
- mwifiex_dbg(priv->adapter, MSG,
- "info: joined/created adhoc network with bssid\t"
- "%pM successfully\n", priv->cfg_bssid);
- } else {
- mwifiex_dbg(priv->adapter, ERROR,
- "info: failed creating/joining adhoc network\n");
- }
-
- return ret;
-}
-
-/*
- * CFG802.11 operation handler to leave an IBSS.
- *
- * This function does not work if a leave operation is
- * already in progress.
- */
-static int
-mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- mwifiex_dbg(priv->adapter, MSG, "info: disconnecting from essid %pM\n",
- priv->cfg_bssid);
- if (mwifiex_deauthenticate(priv, NULL))
- return -EFAULT;
-
- eth_zero_addr(priv->cfg_bssid);
-
- return 0;
-}
-
-/*
- * CFG802.11 operation handler for scan request.
- *
- * This function issues a scan request to the firmware based upon
- * the user specified scan configuration. On successful completion,
- * it also informs the results.
- */
-static int
-mwifiex_cfg80211_scan(struct wiphy *wiphy,
- struct cfg80211_scan_request *request)
-{
- struct net_device *dev = request->wdev->netdev;
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- int i, offset, ret;
- struct ieee80211_channel *chan;
- struct ieee_types_header *ie;
- struct mwifiex_user_scan_cfg *user_scan_cfg;
-
- mwifiex_dbg(priv->adapter, CMD,
- "info: received scan request on %s\n", dev->name);
-
- /* Block scan request if scan operation or scan cleanup when interface
- * is disabled is in process
- */
- if (priv->scan_request || priv->scan_aborting) {
- mwifiex_dbg(priv->adapter, WARN,
- "cmd: Scan already in process..\n");
- return -EBUSY;
- }
-
- user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
- if (!user_scan_cfg)
- return -ENOMEM;
-
- priv->scan_request = request;
-
- user_scan_cfg->num_ssids = request->n_ssids;
- user_scan_cfg->ssid_list = request->ssids;
-
- if (request->ie && request->ie_len) {
- offset = 0;
- for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
- if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR)
- continue;
- priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN;
- ie = (struct ieee_types_header *)(request->ie + offset);
- memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len);
- offset += sizeof(*ie) + ie->len;
-
- if (offset >= request->ie_len)
- break;
- }
- }
-
- for (i = 0; i < min_t(u32, request->n_channels,
- MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
- chan = request->channels[i];
- user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
- user_scan_cfg->chan_list[i].radio_type = chan->band;
-
- if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids)
- user_scan_cfg->chan_list[i].scan_type =
- MWIFIEX_SCAN_TYPE_PASSIVE;
- else
- user_scan_cfg->chan_list[i].scan_type =
- MWIFIEX_SCAN_TYPE_ACTIVE;
-
- user_scan_cfg->chan_list[i].scan_time = 0;
- }
-
- if (priv->adapter->scan_chan_gap_enabled &&
- mwifiex_is_any_intf_active(priv))
- user_scan_cfg->scan_chan_gap =
- priv->adapter->scan_chan_gap_time;
-
- ret = mwifiex_scan_networks(priv, user_scan_cfg);
- kfree(user_scan_cfg);
- if (ret) {
- mwifiex_dbg(priv->adapter, ERROR,
- "scan failed: %d\n", ret);
- priv->scan_aborting = false;
- priv->scan_request = NULL;
- return ret;
- }
-
- if (request->ie && request->ie_len) {
- for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
- if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) {
- priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR;
- memset(&priv->vs_ie[i].ie, 0,
- MWIFIEX_MAX_VSIE_LEN);
- }
- }
- }
- return 0;
-}
-
-static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info,
- struct mwifiex_private *priv)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
-
- vht_info->vht_supported = true;
-
- vht_info->cap = adapter->hw_dot_11ac_dev_cap;
- /* Update MCS support for VHT */
- vht_info->vht_mcs.rx_mcs_map = cpu_to_le16(
- adapter->hw_dot_11ac_mcs_support & 0xFFFF);
- vht_info->vht_mcs.rx_highest = 0;
- vht_info->vht_mcs.tx_mcs_map = cpu_to_le16(
- adapter->hw_dot_11ac_mcs_support >> 16);
- vht_info->vht_mcs.tx_highest = 0;
-}
-
-/*
- * This function sets up the CFG802.11 specific HT capability fields
- * with default values.
- *
- * The following default values are set -
- * - HT Supported = True
- * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K
- * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE
- * - HT Capabilities supported by firmware
- * - MCS information, Rx mask = 0xff
- * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01)
- */
-static void
-mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
- struct mwifiex_private *priv)
-{
- int rx_mcs_supp;
- struct ieee80211_mcs_info mcs_set;
- u8 *mcs = (u8 *)&mcs_set;
- struct mwifiex_adapter *adapter = priv->adapter;
-
- ht_info->ht_supported = true;
- ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
- ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
-
- memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
-
- /* Fill HT capability information */
- if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- else
- ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-
- if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
- else
- ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20;
-
- if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
- else
- ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40;
-
- if (adapter->user_dev_mcs_support == HT_STREAM_2X2)
- ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
- else
- ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
-
- if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
- else
- ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC;
-
- if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
- else
- ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD;
-
- if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT;
- else
- ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT;
-
- if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
- else
- ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING;
-
- ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU;
- ht_info->cap |= IEEE80211_HT_CAP_SM_PS;
-
- rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support);
- /* Set MCS for 1x1/2x2 */
- memset(mcs, 0xff, rx_mcs_supp);
- /* Clear all the other values */
- memset(&mcs[rx_mcs_supp], 0,
- sizeof(struct ieee80211_mcs_info) - rx_mcs_supp);
- if (priv->bss_mode == NL80211_IFTYPE_STATION ||
- ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
- /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
- SETHT_MCS32(mcs_set.rx_mask);
-
- memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info));
-
- ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
-}
-
-/*
- * create a new virtual interface with the given name and name assign type
- */
-struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
- const char *name,
- unsigned char name_assign_type,
- enum nl80211_iftype type,
- u32 *flags,
- struct vif_params *params)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_private *priv;
- struct net_device *dev;
- void *mdev_priv;
-
- if (!adapter)
- return ERR_PTR(-EFAULT);
-
- switch (type) {
- case NL80211_IFTYPE_UNSPECIFIED:
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- if (adapter->curr_iface_comb.sta_intf ==
- adapter->iface_limit.sta_intf) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create multiple sta/adhoc ifaces\n");
- return ERR_PTR(-EINVAL);
- }
-
- priv = mwifiex_get_unused_priv(adapter);
- if (!priv) {
- mwifiex_dbg(adapter, ERROR,
- "could not get free private struct\n");
- return ERR_PTR(-EFAULT);
- }
-
- priv->wdev.wiphy = wiphy;
- priv->wdev.iftype = NL80211_IFTYPE_STATION;
-
- if (type == NL80211_IFTYPE_UNSPECIFIED)
- priv->bss_mode = NL80211_IFTYPE_STATION;
- else
- priv->bss_mode = type;
-
- priv->bss_type = MWIFIEX_BSS_TYPE_STA;
- priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
- priv->bss_priority = 0;
- priv->bss_role = MWIFIEX_BSS_ROLE_STA;
- priv->bss_num = adapter->curr_iface_comb.sta_intf;
-
- break;
- case NL80211_IFTYPE_AP:
- if (adapter->curr_iface_comb.uap_intf ==
- adapter->iface_limit.uap_intf) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create multiple AP ifaces\n");
- return ERR_PTR(-EINVAL);
- }
-
- priv = mwifiex_get_unused_priv(adapter);
- if (!priv) {
- mwifiex_dbg(adapter, ERROR,
- "could not get free private struct\n");
- return ERR_PTR(-EFAULT);
- }
-
- priv->wdev.wiphy = wiphy;
- priv->wdev.iftype = NL80211_IFTYPE_AP;
-
- priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
- priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
- priv->bss_priority = 0;
- priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
- priv->bss_started = 0;
- priv->bss_num = adapter->curr_iface_comb.uap_intf;
- priv->bss_mode = type;
-
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- if (adapter->curr_iface_comb.p2p_intf ==
- adapter->iface_limit.p2p_intf) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create multiple P2P ifaces\n");
- return ERR_PTR(-EINVAL);
- }
-
- priv = mwifiex_get_unused_priv(adapter);
- if (!priv) {
- mwifiex_dbg(adapter, ERROR,
- "could not get free private struct\n");
- return ERR_PTR(-EFAULT);
- }
-
- priv->wdev.wiphy = wiphy;
- /* At start-up, wpa_supplicant tries to change the interface
- * to NL80211_IFTYPE_STATION if it is not managed mode.
- */
- priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT;
- priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT;
-
- /* Setting bss_type to P2P tells firmware that this interface
- * is receiving P2P peers found during find phase and doing
- * action frame handshake.
- */
- priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
-
- priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
- priv->bss_priority = MWIFIEX_BSS_ROLE_STA;
- priv->bss_role = MWIFIEX_BSS_ROLE_STA;
- priv->bss_started = 0;
- priv->bss_num = adapter->curr_iface_comb.p2p_intf;
-
- if (mwifiex_cfg80211_init_p2p_client(priv)) {
- memset(&priv->wdev, 0, sizeof(priv->wdev));
- priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
- return ERR_PTR(-EFAULT);
- }
-
- break;
- default:
- mwifiex_dbg(adapter, ERROR, "type not supported\n");
- return ERR_PTR(-EINVAL);
- }
-
- dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name,
- name_assign_type, ether_setup,
- IEEE80211_NUM_ACS, 1);
- if (!dev) {
- mwifiex_dbg(adapter, ERROR,
- "no memory available for netdevice\n");
- memset(&priv->wdev, 0, sizeof(priv->wdev));
- priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
- priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
- return ERR_PTR(-ENOMEM);
- }
-
- mwifiex_init_priv_params(priv, dev);
- priv->netdev = dev;
-
- mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv);
- if (adapter->is_hw_11ac_capable)
- mwifiex_setup_vht_caps(
- &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv);
-
- if (adapter->config_bands & BAND_A)
- mwifiex_setup_ht_caps(
- &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv);
-
- if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable)
- mwifiex_setup_vht_caps(
- &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv);
-
- dev_net_set(dev, wiphy_net(wiphy));
- dev->ieee80211_ptr = &priv->wdev;
- dev->ieee80211_ptr->iftype = priv->bss_mode;
- memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN);
- SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
-
- dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT;
- dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN;
- dev->ethtool_ops = &mwifiex_ethtool_ops;
-
- mdev_priv = netdev_priv(dev);
- *((unsigned long *) mdev_priv) = (unsigned long) priv;
-
- SET_NETDEV_DEV(dev, adapter->dev);
-
- /* Register network device */
- if (register_netdevice(dev)) {
- mwifiex_dbg(adapter, ERROR,
- "cannot register virtual network device\n");
- free_netdev(dev);
- priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->netdev = NULL;
- memset(&priv->wdev, 0, sizeof(priv->wdev));
- priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
- return ERR_PTR(-EFAULT);
- }
-
- priv->dfs_cac_workqueue = alloc_workqueue("MWIFIEX_DFS_CAC%s",
- WQ_HIGHPRI |
- WQ_MEM_RECLAIM |
- WQ_UNBOUND, 1, name);
- if (!priv->dfs_cac_workqueue) {
- mwifiex_dbg(adapter, ERROR,
- "cannot register virtual network device\n");
- free_netdev(dev);
- priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->netdev = NULL;
- memset(&priv->wdev, 0, sizeof(priv->wdev));
- priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
- return ERR_PTR(-ENOMEM);
- }
-
- INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue);
-
- priv->dfs_chan_sw_workqueue = alloc_workqueue("MWIFIEX_DFS_CHSW%s",
- WQ_HIGHPRI | WQ_UNBOUND |
- WQ_MEM_RECLAIM, 1, name);
- if (!priv->dfs_chan_sw_workqueue) {
- mwifiex_dbg(adapter, ERROR,
- "cannot register virtual network device\n");
- free_netdev(dev);
- priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->netdev = NULL;
- memset(&priv->wdev, 0, sizeof(priv->wdev));
- priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
- return ERR_PTR(-ENOMEM);
- }
-
- INIT_DELAYED_WORK(&priv->dfs_chan_sw_work,
- mwifiex_dfs_chan_sw_work_queue);
-
- sema_init(&priv->async_sem, 1);
-
- mwifiex_dbg(adapter, INFO,
- "info: %s: Marvell 802.11 Adapter\n", dev->name);
-
-#ifdef CONFIG_DEBUG_FS
- mwifiex_dev_debugfs_init(priv);
-#endif
-
- switch (type) {
- case NL80211_IFTYPE_UNSPECIFIED:
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- adapter->curr_iface_comb.sta_intf++;
- break;
- case NL80211_IFTYPE_AP:
- adapter->curr_iface_comb.uap_intf++;
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- adapter->curr_iface_comb.p2p_intf++;
- break;
- default:
- mwifiex_dbg(adapter, ERROR, "type not supported\n");
- return ERR_PTR(-EINVAL);
- }
-
- return &priv->wdev;
-}
-EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf);
-
-/*
- * del_virtual_intf: remove the virtual interface determined by dev
- */
-int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
- struct mwifiex_adapter *adapter = priv->adapter;
- struct sk_buff *skb, *tmp;
-
-#ifdef CONFIG_DEBUG_FS
- mwifiex_dev_debugfs_remove(priv);
-#endif
-
- mwifiex_stop_net_dev_queue(priv->netdev, adapter);
-
- skb_queue_walk_safe(&priv->bypass_txq, skb, tmp)
- mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
-
- if (netif_carrier_ok(priv->netdev))
- netif_carrier_off(priv->netdev);
-
- if (wdev->netdev->reg_state == NETREG_REGISTERED)
- unregister_netdevice(wdev->netdev);
-
- if (priv->dfs_cac_workqueue) {
- flush_workqueue(priv->dfs_cac_workqueue);
- destroy_workqueue(priv->dfs_cac_workqueue);
- priv->dfs_cac_workqueue = NULL;
- }
-
- if (priv->dfs_chan_sw_workqueue) {
- flush_workqueue(priv->dfs_chan_sw_workqueue);
- destroy_workqueue(priv->dfs_chan_sw_workqueue);
- priv->dfs_chan_sw_workqueue = NULL;
- }
- /* Clear the priv in adapter */
- priv->netdev->ieee80211_ptr = NULL;
- priv->netdev = NULL;
- priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
-
- priv->media_connected = false;
-
- switch (priv->bss_mode) {
- case NL80211_IFTYPE_UNSPECIFIED:
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- adapter->curr_iface_comb.sta_intf--;
- break;
- case NL80211_IFTYPE_AP:
- adapter->curr_iface_comb.uap_intf--;
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- adapter->curr_iface_comb.p2p_intf--;
- break;
- default:
- mwifiex_dbg(adapter, ERROR,
- "del_virtual_intf: type not supported\n");
- break;
- }
-
- priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
- GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
- kfree(priv->hist_data);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
-
-static bool
-mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq,
- u8 max_byte_seq)
-{
- int j, k, valid_byte_cnt = 0;
- bool dont_care_byte = false;
-
- for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) {
- for (k = 0; k < 8; k++) {
- if (pat->mask[j] & 1 << k) {
- memcpy(byte_seq + valid_byte_cnt,
- &pat->pattern[j * 8 + k], 1);
- valid_byte_cnt++;
- if (dont_care_byte)
- return false;
- } else {
- if (valid_byte_cnt)
- dont_care_byte = true;
- }
-
- if (valid_byte_cnt > max_byte_seq)
- return false;
- }
- }
-
- byte_seq[max_byte_seq] = valid_byte_cnt;
-
- return true;
-}
-
-#ifdef CONFIG_PM
-static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv,
- struct mwifiex_mef_entry *mef_entry)
-{
- int i, filt_num = 0, num_ipv4 = 0;
- struct in_device *in_dev;
- struct in_ifaddr *ifa;
- __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR];
- struct mwifiex_adapter *adapter = priv->adapter;
-
- mef_entry->mode = MEF_MODE_HOST_SLEEP;
- mef_entry->action = MEF_ACTION_AUTO_ARP;
-
- /* Enable ARP offload feature */
- memset(ips, 0, sizeof(ips));
- for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
- if (adapter->priv[i]->netdev) {
- in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev);
- if (!in_dev)
- continue;
- ifa = in_dev->ifa_list;
- if (!ifa || !ifa->ifa_local)
- continue;
- ips[i] = ifa->ifa_local;
- num_ipv4++;
- }
- }
-
- for (i = 0; i < num_ipv4; i++) {
- if (!ips[i])
- continue;
- mef_entry->filter[filt_num].repeat = 1;
- memcpy(mef_entry->filter[filt_num].byte_seq,
- (u8 *)&ips[i], sizeof(ips[i]));
- mef_entry->filter[filt_num].
- byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
- sizeof(ips[i]);
- mef_entry->filter[filt_num].offset = 46;
- mef_entry->filter[filt_num].filt_type = TYPE_EQ;
- if (filt_num) {
- mef_entry->filter[filt_num].filt_action =
- TYPE_OR;
- }
- filt_num++;
- }
-
- mef_entry->filter[filt_num].repeat = 1;
- mef_entry->filter[filt_num].byte_seq[0] = 0x08;
- mef_entry->filter[filt_num].byte_seq[1] = 0x06;
- mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2;
- mef_entry->filter[filt_num].offset = 20;
- mef_entry->filter[filt_num].filt_type = TYPE_EQ;
- mef_entry->filter[filt_num].filt_action = TYPE_AND;
-}
-
-static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv,
- struct mwifiex_ds_mef_cfg *mef_cfg,
- struct mwifiex_mef_entry *mef_entry,
- struct cfg80211_wowlan *wowlan)
-{
- int i, filt_num = 0, ret = 0;
- bool first_pat = true;
- u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
- const u8 ipv4_mc_mac[] = {0x33, 0x33};
- const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
-
- mef_entry->mode = MEF_MODE_HOST_SLEEP;
- mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
-
- for (i = 0; i < wowlan->n_patterns; i++) {
- memset(byte_seq, 0, sizeof(byte_seq));
- if (!mwifiex_is_pattern_supported(&wowlan->patterns[i],
- byte_seq,
- MWIFIEX_MEF_MAX_BYTESEQ)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Pattern not supported\n");
- return -EOPNOTSUPP;
- }
-
- if (!wowlan->patterns[i].pkt_offset) {
- if (!(byte_seq[0] & 0x01) &&
- (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) {
- mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
- continue;
- } else if (is_broadcast_ether_addr(byte_seq)) {
- mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST;
- continue;
- } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
- (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) ||
- (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
- (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) {
- mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST;
- continue;
- }
- }
- mef_entry->filter[filt_num].repeat = 1;
- mef_entry->filter[filt_num].offset =
- wowlan->patterns[i].pkt_offset;
- memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq,
- sizeof(byte_seq));
- mef_entry->filter[filt_num].filt_type = TYPE_EQ;
-
- if (first_pat)
- first_pat = false;
- else
- mef_entry->filter[filt_num].filt_action = TYPE_AND;
-
- filt_num++;
- }
-
- if (wowlan->magic_pkt) {
- mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
- mef_entry->filter[filt_num].repeat = 16;
- memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
- ETH_ALEN);
- mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
- ETH_ALEN;
- mef_entry->filter[filt_num].offset = 28;
- mef_entry->filter[filt_num].filt_type = TYPE_EQ;
- if (filt_num)
- mef_entry->filter[filt_num].filt_action = TYPE_OR;
-
- filt_num++;
- mef_entry->filter[filt_num].repeat = 16;
- memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
- ETH_ALEN);
- mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
- ETH_ALEN;
- mef_entry->filter[filt_num].offset = 56;
- mef_entry->filter[filt_num].filt_type = TYPE_EQ;
- mef_entry->filter[filt_num].filt_action = TYPE_OR;
- }
- return ret;
-}
-
-static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
- struct cfg80211_wowlan *wowlan)
-{
- int ret = 0, num_entries = 1;
- struct mwifiex_ds_mef_cfg mef_cfg;
- struct mwifiex_mef_entry *mef_entry;
-
- if (wowlan->n_patterns || wowlan->magic_pkt)
- num_entries++;
-
- mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL);
- if (!mef_entry)
- return -ENOMEM;
-
- memset(&mef_cfg, 0, sizeof(mef_cfg));
- mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST |
- MWIFIEX_CRITERIA_UNICAST;
- mef_cfg.num_entries = num_entries;
- mef_cfg.mef_entry = mef_entry;
-
- mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]);
-
- if (wowlan->n_patterns || wowlan->magic_pkt) {
- ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg,
- &mef_entry[1], wowlan);
- if (ret)
- goto err;
- }
-
- if (!mef_cfg.criteria)
- mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
- MWIFIEX_CRITERIA_UNICAST |
- MWIFIEX_CRITERIA_MULTICAST;
-
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
- HostCmd_ACT_GEN_SET, 0,
- &mef_cfg, true);
-
-err:
- kfree(mef_entry);
- return ret;
-}
-
-static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
- struct cfg80211_wowlan *wowlan)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_ds_hs_cfg hs_cfg;
- int i, ret = 0;
- struct mwifiex_private *priv;
-
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
- mwifiex_abort_cac(priv);
- }
-
- mwifiex_cancel_all_pending_cmd(adapter);
-
- if (!wowlan) {
- mwifiex_dbg(adapter, ERROR,
- "None of the WOWLAN triggers enabled\n");
- return 0;
- }
-
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
-
- if (!priv->media_connected) {
- mwifiex_dbg(adapter, ERROR,
- "Can not configure WOWLAN in disconnected state\n");
- return 0;
- }
-
- ret = mwifiex_set_mef_filter(priv, wowlan);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "Failed to set MEF filter\n");
- return ret;
- }
-
- if (wowlan->disconnect) {
- memset(&hs_cfg, 0, sizeof(hs_cfg));
- hs_cfg.is_invoke_hostcmd = false;
- hs_cfg.conditions = HS_CFG_COND_MAC_EVENT;
- hs_cfg.gpio = HS_CFG_GPIO_DEF;
- hs_cfg.gap = HS_CFG_GAP_DEF;
- ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
- MWIFIEX_SYNC_CMD, &hs_cfg);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to set HS params\n");
- return ret;
- }
- }
-
- return ret;
-}
-
-static int mwifiex_cfg80211_resume(struct wiphy *wiphy)
-{
- return 0;
-}
-
-static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy,
- bool enabled)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
-
- device_set_wakeup_enable(adapter->dev, enabled);
-}
-#endif
-
-static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq)
-{
- const u8 ipv4_mc_mac[] = {0x33, 0x33};
- const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
- const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff};
-
- if ((byte_seq[0] & 0x01) &&
- (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1))
- return PACKET_TYPE_UNICAST;
- else if (!memcmp(byte_seq, bc_mac, 4))
- return PACKET_TYPE_BROADCAST;
- else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
- byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) ||
- (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
- byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3))
- return PACKET_TYPE_MULTICAST;
-
- return 0;
-}
-
-static int
-mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv,
- struct cfg80211_coalesce_rules *crule,
- struct mwifiex_coalesce_rule *mrule)
-{
- u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1];
- struct filt_field_param *param;
- int i;
-
- mrule->max_coalescing_delay = crule->delay;
-
- param = mrule->params;
-
- for (i = 0; i < crule->n_patterns; i++) {
- memset(byte_seq, 0, sizeof(byte_seq));
- if (!mwifiex_is_pattern_supported(&crule->patterns[i],
- byte_seq,
- MWIFIEX_COALESCE_MAX_BYTESEQ)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Pattern not supported\n");
- return -EOPNOTSUPP;
- }
-
- if (!crule->patterns[i].pkt_offset) {
- u8 pkt_type;
-
- pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq);
- if (pkt_type && mrule->pkt_type) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Multiple packet types not allowed\n");
- return -EOPNOTSUPP;
- } else if (pkt_type) {
- mrule->pkt_type = pkt_type;
- continue;
- }
- }
-
- if (crule->condition == NL80211_COALESCE_CONDITION_MATCH)
- param->operation = RECV_FILTER_MATCH_TYPE_EQ;
- else
- param->operation = RECV_FILTER_MATCH_TYPE_NE;
-
- param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ];
- memcpy(param->operand_byte_stream, byte_seq,
- param->operand_len);
- param->offset = crule->patterns[i].pkt_offset;
- param++;
-
- mrule->num_of_fields++;
- }
-
- if (!mrule->pkt_type) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Packet type can not be determined\n");
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
- struct cfg80211_coalesce *coalesce)
-{
- struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- int i, ret;
- struct mwifiex_ds_coalesce_cfg coalesce_cfg;
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
-
- memset(&coalesce_cfg, 0, sizeof(coalesce_cfg));
- if (!coalesce) {
- mwifiex_dbg(adapter, WARN,
- "Disable coalesce and reset all previous rules\n");
- return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG,
- HostCmd_ACT_GEN_SET, 0,
- &coalesce_cfg, true);
- }
-
- coalesce_cfg.num_of_rules = coalesce->n_rules;
- for (i = 0; i < coalesce->n_rules; i++) {
- ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i],
- &coalesce_cfg.rule[i]);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "Recheck the patterns provided for rule %d\n",
- i + 1);
- return ret;
- }
- }
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG,
- HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true);
-}
-
-/* cfg80211 ops handler for tdls_mgmt.
- * Function prepares TDLS action frame packets and forwards them to FW
- */
-static int
-mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, u32 peer_capability,
- bool initiator, const u8 *extra_ies,
- size_t extra_ies_len)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- int ret;
-
- if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
- return -ENOTSUPP;
-
- /* make sure we are in station mode and connected */
- if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
- return -ENOTSUPP;
-
- switch (action_code) {
- case WLAN_TDLS_SETUP_REQUEST:
- mwifiex_dbg(priv->adapter, MSG,
- "Send TDLS Setup Request to %pM status_code=%d\n",
- peer, status_code);
- mwifiex_add_auto_tdls_peer(priv, peer);
- ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
- dialog_token, status_code,
- extra_ies, extra_ies_len);
- break;
- case WLAN_TDLS_SETUP_RESPONSE:
- mwifiex_add_auto_tdls_peer(priv, peer);
- mwifiex_dbg(priv->adapter, MSG,
- "Send TDLS Setup Response to %pM status_code=%d\n",
- peer, status_code);
- ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
- dialog_token, status_code,
- extra_ies, extra_ies_len);
- break;
- case WLAN_TDLS_SETUP_CONFIRM:
- mwifiex_dbg(priv->adapter, MSG,
- "Send TDLS Confirm to %pM status_code=%d\n", peer,
- status_code);
- ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
- dialog_token, status_code,
- extra_ies, extra_ies_len);
- break;
- case WLAN_TDLS_TEARDOWN:
- mwifiex_dbg(priv->adapter, MSG,
- "Send TDLS Tear down to %pM\n", peer);
- ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
- dialog_token, status_code,
- extra_ies, extra_ies_len);
- break;
- case WLAN_TDLS_DISCOVERY_REQUEST:
- mwifiex_dbg(priv->adapter, MSG,
- "Send TDLS Discovery Request to %pM\n", peer);
- ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
- dialog_token, status_code,
- extra_ies, extra_ies_len);
- break;
- case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
- mwifiex_dbg(priv->adapter, MSG,
- "Send TDLS Discovery Response to %pM\n", peer);
- ret = mwifiex_send_tdls_action_frame(priv, peer, action_code,
- dialog_token, status_code,
- extra_ies, extra_ies_len);
- break;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "Unknown TDLS mgmt/action frame %pM\n", peer);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int
-mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, enum nl80211_tdls_operation action)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
- !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
- return -ENOTSUPP;
-
- /* make sure we are in station mode and connected */
- if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
- return -ENOTSUPP;
-
- mwifiex_dbg(priv->adapter, MSG,
- "TDLS peer=%pM, oper=%d\n", peer, action);
-
- switch (action) {
- case NL80211_TDLS_ENABLE_LINK:
- action = MWIFIEX_TDLS_ENABLE_LINK;
- break;
- case NL80211_TDLS_DISABLE_LINK:
- action = MWIFIEX_TDLS_DISABLE_LINK;
- break;
- case NL80211_TDLS_TEARDOWN:
- /* shouldn't happen!*/
- mwifiex_dbg(priv->adapter, ERROR,
- "tdls_oper: teardown from driver not supported\n");
- return -EINVAL;
- case NL80211_TDLS_SETUP:
- /* shouldn't happen!*/
- mwifiex_dbg(priv->adapter, ERROR,
- "tdls_oper: setup from driver not supported\n");
- return -EINVAL;
- case NL80211_TDLS_DISCOVERY_REQ:
- /* shouldn't happen!*/
- mwifiex_dbg(priv->adapter, ERROR,
- "tdls_oper: discovery from driver not supported\n");
- return -EINVAL;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "tdls_oper: operation not supported\n");
- return -ENOTSUPP;
- }
-
- return mwifiex_tdls_oper(priv, peer, action);
-}
-
-static int
-mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev,
- const u8 *addr, u8 oper_class,
- struct cfg80211_chan_def *chandef)
-{
- struct mwifiex_sta_node *sta_ptr;
- unsigned long flags;
- u16 chan;
- u8 second_chan_offset, band;
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- sta_ptr = mwifiex_get_sta_entry(priv, addr);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-
- if (!sta_ptr) {
- wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n",
- __func__, addr);
- return -ENOENT;
- }
-
- if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] &
- WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) {
- wiphy_err(wiphy, "%pM do not support tdls cs\n", addr);
- return -ENOENT;
- }
-
- if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING ||
- sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) {
- wiphy_err(wiphy, "channel switch is running, abort request\n");
- return -EALREADY;
- }
-
- chan = chandef->chan->hw_value;
- second_chan_offset = mwifiex_get_sec_chan_offset(chan);
- band = chandef->chan->band;
- mwifiex_start_tdls_cs(priv, addr, chan, second_chan_offset, band);
-
- return 0;
-}
-
-static void
-mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy,
- struct net_device *dev,
- const u8 *addr)
-{
- struct mwifiex_sta_node *sta_ptr;
- unsigned long flags;
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- sta_ptr = mwifiex_get_sta_entry(priv, addr);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-
- if (!sta_ptr) {
- wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n",
- __func__, addr);
- } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING ||
- sta_ptr->tdls_status == TDLS_IN_BASE_CHAN ||
- sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) {
- wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n",
- addr);
- } else
- mwifiex_stop_tdls_cs(priv, addr);
-}
-
-static int
-mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac, struct station_parameters *params)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
- return -ENOTSUPP;
-
- /* make sure we are in station mode and connected */
- if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
- return -ENOTSUPP;
-
- return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK);
-}
-
-static int
-mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_csa_settings *params)
-{
- struct ieee_types_header *chsw_ie;
- struct ieee80211_channel_sw_ie *channel_sw;
- int chsw_msec;
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (priv->adapter->scan_processing) {
- mwifiex_dbg(priv->adapter, ERROR,
- "radar detection: scan in process...\n");
- return -EBUSY;
- }
-
- if (priv->wdev.cac_started)
- return -EBUSY;
-
- if (cfg80211_chandef_identical(&params->chandef,
- &priv->dfs_chandef))
- return -EINVAL;
-
- chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH,
- params->beacon_csa.tail,
- params->beacon_csa.tail_len);
- if (!chsw_ie) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Could not parse channel switch announcement IE\n");
- return -EINVAL;
- }
-
- channel_sw = (void *)(chsw_ie + 1);
- if (channel_sw->mode) {
- if (netif_carrier_ok(priv->netdev))
- netif_carrier_off(priv->netdev);
- mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
- }
-
- if (mwifiex_del_mgmt_ies(priv))
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to delete mgmt IEs!\n");
-
- if (mwifiex_set_mgmt_ies(priv, &params->beacon_csa)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: setting mgmt ies failed\n", __func__);
- return -EFAULT;
- }
-
- memcpy(&priv->dfs_chandef, &params->chandef, sizeof(priv->dfs_chandef));
- memcpy(&priv->beacon_after, &params->beacon_after,
- sizeof(priv->beacon_after));
-
- chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100);
- queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work,
- msecs_to_jiffies(chsw_msec));
- return 0;
-}
-
-static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- struct cfg80211_chan_def *chandef)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
- struct mwifiex_bssdescriptor *curr_bss;
- struct ieee80211_channel *chan;
- u8 second_chan_offset;
- enum nl80211_channel_type chan_type;
- enum ieee80211_band band;
- int freq;
- int ret = -ENODATA;
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP &&
- cfg80211_chandef_valid(&priv->bss_chandef)) {
- *chandef = priv->bss_chandef;
- ret = 0;
- } else if (priv->media_connected) {
- curr_bss = &priv->curr_bss_params.bss_descriptor;
- band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
- freq = ieee80211_channel_to_frequency(curr_bss->channel, band);
- chan = ieee80211_get_channel(wiphy, freq);
-
- if (curr_bss->bcn_ht_oper) {
- second_chan_offset = curr_bss->bcn_ht_oper->ht_param &
- IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
- chan_type = mwifiex_sec_chan_offset_to_chan_type
- (second_chan_offset);
- cfg80211_chandef_create(chandef, chan, chan_type);
- } else {
- cfg80211_chandef_create(chandef, chan,
- NL80211_CHAN_NO_HT);
- }
- ret = 0;
- }
-
- return ret;
-}
-
-static int
-mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_chan_def *chandef,
- u32 cac_time_ms)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct mwifiex_radar_params radar_params;
-
- if (priv->adapter->scan_processing) {
- mwifiex_dbg(priv->adapter, ERROR,
- "radar detection: scan already in process...\n");
- return -EBUSY;
- }
-
- if (!mwifiex_is_11h_active(priv)) {
- mwifiex_dbg(priv->adapter, INFO,
- "Enable 11h extensions in FW\n");
- if (mwifiex_11h_activate(priv, true)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to activate 11h extensions!!");
- return -1;
- }
- priv->state_11h.is_11h_active = true;
- }
-
- memset(&radar_params, 0, sizeof(struct mwifiex_radar_params));
- radar_params.chandef = chandef;
- radar_params.cac_time_ms = cac_time_ms;
-
- memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef));
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
- HostCmd_ACT_GEN_SET, 0, &radar_params, true))
- return -1;
-
- queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work,
- msecs_to_jiffies(cac_time_ms));
- return 0;
-}
-
-static int
-mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac,
- struct station_parameters *params)
-{
- int ret;
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- /* we support change_station handler only for TDLS peers*/
- if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
- return -ENOTSUPP;
-
- /* make sure we are in station mode and connected */
- if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
- return -ENOTSUPP;
-
- priv->sta_params = params;
-
- ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK);
- priv->sta_params = NULL;
-
- return ret;
-}
-
-/* station cfg80211 operations */
-static struct cfg80211_ops mwifiex_cfg80211_ops = {
- .add_virtual_intf = mwifiex_add_virtual_intf,
- .del_virtual_intf = mwifiex_del_virtual_intf,
- .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf,
- .scan = mwifiex_cfg80211_scan,
- .connect = mwifiex_cfg80211_connect,
- .disconnect = mwifiex_cfg80211_disconnect,
- .get_station = mwifiex_cfg80211_get_station,
- .dump_station = mwifiex_cfg80211_dump_station,
- .dump_survey = mwifiex_cfg80211_dump_survey,
- .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params,
- .join_ibss = mwifiex_cfg80211_join_ibss,
- .leave_ibss = mwifiex_cfg80211_leave_ibss,
- .add_key = mwifiex_cfg80211_add_key,
- .del_key = mwifiex_cfg80211_del_key,
- .mgmt_tx = mwifiex_cfg80211_mgmt_tx,
- .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register,
- .remain_on_channel = mwifiex_cfg80211_remain_on_channel,
- .cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel,
- .set_default_key = mwifiex_cfg80211_set_default_key,
- .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt,
- .set_tx_power = mwifiex_cfg80211_set_tx_power,
- .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask,
- .start_ap = mwifiex_cfg80211_start_ap,
- .stop_ap = mwifiex_cfg80211_stop_ap,
- .change_beacon = mwifiex_cfg80211_change_beacon,
- .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
- .set_antenna = mwifiex_cfg80211_set_antenna,
- .del_station = mwifiex_cfg80211_del_station,
-#ifdef CONFIG_PM
- .suspend = mwifiex_cfg80211_suspend,
- .resume = mwifiex_cfg80211_resume,
- .set_wakeup = mwifiex_cfg80211_set_wakeup,
-#endif
- .set_coalesce = mwifiex_cfg80211_set_coalesce,
- .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt,
- .tdls_oper = mwifiex_cfg80211_tdls_oper,
- .tdls_channel_switch = mwifiex_cfg80211_tdls_chan_switch,
- .tdls_cancel_channel_switch = mwifiex_cfg80211_tdls_cancel_chan_switch,
- .add_station = mwifiex_cfg80211_add_station,
- .change_station = mwifiex_cfg80211_change_station,
- .get_channel = mwifiex_cfg80211_get_channel,
- .start_radar_detection = mwifiex_cfg80211_start_radar_detection,
- .channel_switch = mwifiex_cfg80211_channel_switch,
-};
-
-#ifdef CONFIG_PM
-static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
- .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
- .n_patterns = MWIFIEX_MEF_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;
-}
-
-static const struct wiphy_coalesce_support mwifiex_coalesce_support = {
- .n_rules = MWIFIEX_COALESCE_MAX_RULES,
- .max_delay = MWIFIEX_MAX_COALESCING_DELAY,
- .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS,
- .pattern_min_len = 1,
- .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
- .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
-};
-
-int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter)
-{
- u32 n_channels_bg, n_channels_a = 0;
-
- n_channels_bg = mwifiex_band_2ghz.n_channels;
-
- if (adapter->config_bands & BAND_A)
- n_channels_a = mwifiex_band_5ghz.n_channels;
-
- adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a);
- adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) *
- adapter->num_in_chan_stats);
-
- if (!adapter->chan_stats)
- return -ENOMEM;
-
- return 0;
-}
-
-/*
- * This function registers the device with CFG802.11 subsystem.
- *
- * The function creates the wireless device/wiphy, populates it with
- * default parameters and handler function pointers, and finally
- * registers the device.
- */
-
-int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
-{
- int ret;
- void *wdev_priv;
- struct wiphy *wiphy;
- struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA];
- u8 *country_code;
- u32 thr, retry;
-
- /* create a new wiphy for use with cfg80211 */
- wiphy = wiphy_new(&mwifiex_cfg80211_ops,
- sizeof(struct mwifiex_adapter *));
- if (!wiphy) {
- mwifiex_dbg(adapter, ERROR,
- "%s: creating new wiphy\n", __func__);
- return -ENOMEM;
- }
- wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH;
- wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN;
- wiphy->mgmt_stypes = mwifiex_mgmt_stypes;
- wiphy->max_remain_on_channel_duration = 5000;
- wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_AP);
-
- wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz;
- if (adapter->config_bands & BAND_A)
- wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz;
- else
- wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
-
- if (adapter->drcs_enabled && ISSUPP_DRCS_ENABLED(adapter->fw_cap_info))
- wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_drcs;
- else if (adapter->is_hw_11ac_capable)
- wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_vht;
- else
- wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta;
- wiphy->n_iface_combinations = 1;
-
- /* Initialize cipher suits */
- wiphy->cipher_suites = mwifiex_cipher_suites;
- wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
-
- ether_addr_copy(wiphy->perm_addr, adapter->perm_addr);
- wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
- WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
- WIPHY_FLAG_AP_UAPSD |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
- WIPHY_FLAG_HAS_CHANNEL_SWITCH |
- WIPHY_FLAG_PS_ON_BY_DEFAULT;
-
- if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
- wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
- WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
-
-#ifdef CONFIG_PM
- wiphy->wowlan = &mwifiex_wowlan_support;
-#endif
-
- wiphy->coalesce = &mwifiex_coalesce_support;
-
- wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
- NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
- NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
-
- wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1;
- wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1;
-
- wiphy->features |= NL80211_FEATURE_HT_IBSS |
- NL80211_FEATURE_INACTIVITY_TIMER |
- NL80211_FEATURE_NEED_OBSS_SCAN;
-
- if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
- wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
-
- if (adapter->fw_api_ver == MWIFIEX_FW_V15)
- wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
-
- /* Reserve space for mwifiex specific private data for BSS */
- wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
-
- wiphy->reg_notifier = mwifiex_reg_notifier;
-
- /* Set struct mwifiex_adapter pointer in wiphy_priv */
- wdev_priv = wiphy_priv(wiphy);
- *(unsigned long *)wdev_priv = (unsigned long)adapter;
-
- set_wiphy_dev(wiphy, priv->adapter->dev);
-
- ret = wiphy_register(wiphy);
- if (ret < 0) {
- mwifiex_dbg(adapter, ERROR,
- "%s: wiphy_register failed: %d\n", __func__, ret);
- wiphy_free(wiphy);
- return ret;
- }
-
- if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
- mwifiex_dbg(adapter, INFO,
- "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)
- mwifiex_dbg(adapter, WARN,
- "ignoring F/W country code %2.2s\n",
- country_code);
- }
-
- mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true);
- wiphy->frag_threshold = thr;
- mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true);
- wiphy->rts_threshold = thr;
- mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true);
- wiphy->retry_short = (u8) retry;
- mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true);
- wiphy->retry_long = (u8) retry;
-
- adapter->wiphy = wiphy;
- return ret;
-}
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c
deleted file mode 100644
index 3ddb8ec676ed..000000000000
--- a/drivers/net/wireless/mwifiex/cfp.c
+++ /dev/null
@@ -1,537 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: Channel, Frequence and Power
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "cfg80211.h"
-
-/* 100mW */
-#define MWIFIEX_TX_PWR_DEFAULT 20
-/* 100mW */
-#define MWIFIEX_TX_PWR_US_DEFAULT 20
-/* 50mW */
-#define MWIFIEX_TX_PWR_JP_DEFAULT 16
-/* 100mW */
-#define MWIFIEX_TX_PWR_FR_100MW 20
-/* 10mW */
-#define MWIFIEX_TX_PWR_FR_10MW 10
-/* 100mW */
-#define MWIFIEX_TX_PWR_EMEA_DEFAULT 20
-
-static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 };
-
-static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24,
- 0xb0, 0x48, 0x60, 0x6c, 0 };
-
-static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96,
- 0x0c, 0x12, 0x18, 0x24,
- 0x30, 0x48, 0x60, 0x6c, 0 };
-
-static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24,
- 0xb0, 0x48, 0x60, 0x6c, 0 };
-static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
- 0xb0, 0x48, 0x60, 0x6c, 0 };
-static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04,
- 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18,
- 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90,
- 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68,
- 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51,
- 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 };
-
-static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
-
-static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
- 0x30, 0x48, 0x60, 0x6c, 0 };
-
-static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
- 0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
- 0x60, 0x6c, 0 };
-
-u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30,
- 0x32, 0x40, 0x41, 0xff };
-
-static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
-
-/* For every mcs_rate line, the first 8 bytes are for stream 1x1,
- * and all 16 bytes are for stream 2x2.
- */
-static const u16 mcs_rate[4][16] = {
- /* LGI 40M */
- { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
- 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
-
- /* SGI 40M */
- { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
- 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
-
- /* LGI 20M */
- { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
- 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
-
- /* SGI 20M */
- { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
- 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
-};
-
-/* AC rates */
-static const u16 ac_mcs_rate_nss1[8][10] = {
- /* LG 160M */
- { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
- 0x492, 0x57C, 0x618 },
-
- /* SG 160M */
- { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
- 0x514, 0x618, 0x6C6 },
-
- /* LG 80M */
- { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
- 0x249, 0x2BE, 0x30C },
-
- /* SG 80M */
- { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
- 0x28A, 0x30C, 0x363 },
-
- /* LG 40M */
- { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
- 0x10E, 0x144, 0x168 },
-
- /* SG 40M */
- { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
- 0x12C, 0x168, 0x190 },
-
- /* LG 20M */
- { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
-
- /* SG 20M */
- { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
-};
-
-/* NSS2 note: the value in the table is 2 multiplier of the actual rate */
-static const u16 ac_mcs_rate_nss2[8][10] = {
- /* LG 160M */
- { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
- 0x924, 0xAF8, 0xC30 },
-
- /* SG 160M */
- { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
- 0xA28, 0xC30, 0xD8B },
-
- /* LG 80M */
- { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
- 0x492, 0x57C, 0x618 },
-
- /* SG 80M */
- { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
- 0x514, 0x618, 0x6C6 },
-
- /* LG 40M */
- { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
- 0x21C, 0x288, 0x2D0 },
-
- /* SG 40M */
- { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
- 0x258, 0x2D0, 0x320 },
-
- /* LG 20M */
- { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
- 0x138, 0x00 },
-
- /* SG 20M */
- { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
- 0x15B, 0x00 },
-};
-
-struct region_code_mapping {
- u8 code;
- u8 region[IEEE80211_COUNTRY_STRING_LEN];
-};
-
-static struct region_code_mapping region_code_mapping_t[] = {
- { 0x10, "US " }, /* US FCC */
- { 0x20, "CA " }, /* IC Canada */
- { 0x30, "EU " }, /* ETSI */
- { 0x31, "ES " }, /* Spain */
- { 0x32, "FR " }, /* France */
- { 0x40, "JP " }, /* Japan */
- { 0x41, "JP " }, /* Japan */
- { 0x50, "CN " }, /* China */
-};
-
-/* This function converts integer code to region string */
-u8 *mwifiex_11d_code_2_region(u8 code)
-{
- u8 i;
- u8 size = sizeof(region_code_mapping_t)/
- sizeof(struct region_code_mapping);
-
- /* Look for code in mapping table */
- for (i = 0; i < size; i++)
- if (region_code_mapping_t[i].code == code)
- return region_code_mapping_t[i].region;
-
- return NULL;
-}
-
-/*
- * This function maps an index in supported rates table into
- * the corresponding data rate.
- */
-u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
- u8 index, u8 ht_info)
-{
- u32 rate = 0;
- u8 mcs_index = 0;
- u8 bw = 0;
- u8 gi = 0;
-
- if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) {
- mcs_index = min(index & 0xF, 9);
-
- /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
- bw = (ht_info & 0xC) >> 2;
-
- /* LGI: gi =0, SGI: gi = 1 */
- gi = (ht_info & 0x10) >> 4;
-
- if ((index >> 4) == 1) /* NSS = 2 */
- rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index];
- else /* NSS = 1 */
- rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index];
- } else if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) {
- /* 20M: bw=0, 40M: bw=1 */
- bw = (ht_info & 0xC) >> 2;
-
- /* LGI: gi =0, SGI: gi = 1 */
- gi = (ht_info & 0x10) >> 4;
-
- if (index == MWIFIEX_RATE_BITMAP_MCS0) {
- if (gi == 1)
- rate = 0x0D; /* MCS 32 SGI rate */
- else
- rate = 0x0C; /* MCS 32 LGI rate */
- } else if (index < 16) {
- if ((bw == 1) || (bw == 0))
- rate = mcs_rate[2 * (1 - bw) + gi][index];
- else
- rate = mwifiex_data_rates[0];
- } else {
- rate = mwifiex_data_rates[0];
- }
- } else {
- /* 11n non-HT rates */
- if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
- index = 0;
- rate = mwifiex_data_rates[index];
- }
-
- return rate;
-}
-
-/* This function maps an index in supported rates table into
- * the corresponding data rate.
- */
-u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
- u8 index, u8 ht_info)
-{
- u32 mcs_num_supp =
- (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
- u32 rate;
-
- if (priv->adapter->is_hw_11ac_capable)
- return mwifiex_index_to_acs_data_rate(priv, index, ht_info);
-
- if (ht_info & BIT(0)) {
- if (index == MWIFIEX_RATE_BITMAP_MCS0) {
- if (ht_info & BIT(2))
- rate = 0x0D; /* MCS 32 SGI rate */
- else
- rate = 0x0C; /* MCS 32 LGI rate */
- } else if (index < mcs_num_supp) {
- if (ht_info & BIT(1)) {
- if (ht_info & BIT(2))
- /* SGI, 40M */
- rate = mcs_rate[1][index];
- else
- /* LGI, 40M */
- rate = mcs_rate[0][index];
- } else {
- if (ht_info & BIT(2))
- /* SGI, 20M */
- rate = mcs_rate[3][index];
- else
- /* LGI, 20M */
- rate = mcs_rate[2][index];
- }
- } else
- rate = mwifiex_data_rates[0];
- } else {
- if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
- index = 0;
- rate = mwifiex_data_rates[index];
- }
- return rate;
-}
-
-/*
- * This function returns the current active data rates.
- *
- * The result may vary depending upon connection status.
- */
-u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates)
-{
- if (!priv->media_connected)
- return mwifiex_get_supported_rates(priv, rates);
- else
- return mwifiex_copy_rates(rates, 0,
- priv->curr_bss_params.data_rates,
- priv->curr_bss_params.num_of_rates);
-}
-
-/*
- * This function locates the Channel-Frequency-Power triplet based upon
- * band and channel/frequency parameters.
- */
-struct mwifiex_chan_freq_power *
-mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq)
-{
- struct mwifiex_chan_freq_power *cfp = NULL;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch = NULL;
- int i;
-
- if (!channel && !freq)
- return cfp;
-
- if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG)
- sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ];
- else
- sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ];
-
- if (!sband) {
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: cannot find cfp by band %d\n",
- __func__, band);
- return cfp;
- }
-
- for (i = 0; i < sband->n_channels; i++) {
- ch = &sband->channels[i];
-
- if (ch->flags & IEEE80211_CHAN_DISABLED)
- continue;
-
- if (freq) {
- if (ch->center_freq == freq)
- break;
- } else {
- /* find by valid channel*/
- if (ch->hw_value == channel ||
- channel == FIRST_VALID_CHANNEL)
- break;
- }
- }
- if (i == sband->n_channels) {
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: cannot find cfp by band %d\t"
- "& channel=%d freq=%d\n",
- __func__, band, channel, freq);
- } else {
- if (!ch)
- return cfp;
-
- priv->cfp.channel = ch->hw_value;
- priv->cfp.freq = ch->center_freq;
- priv->cfp.max_tx_power = ch->max_power;
- cfp = &priv->cfp;
- }
-
- return cfp;
-}
-
-/*
- * This function checks if the data rate is set to auto.
- */
-u8
-mwifiex_is_rate_auto(struct mwifiex_private *priv)
-{
- u32 i;
- int rate_num = 0;
-
- for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++)
- if (priv->bitmap_rates[i])
- rate_num++;
-
- if (rate_num > 1)
- return true;
- else
- return false;
-}
-
-/* This function gets the supported data rates from bitmask inside
- * cfg80211_scan_request.
- */
-u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
- u8 *rates, u8 radio_type)
-{
- struct wiphy *wiphy = priv->adapter->wiphy;
- struct cfg80211_scan_request *request = priv->scan_request;
- u32 num_rates, rate_mask;
- struct ieee80211_supported_band *sband;
- int i;
-
- if (radio_type) {
- sband = wiphy->bands[IEEE80211_BAND_5GHZ];
- if (WARN_ON_ONCE(!sband))
- return 0;
- rate_mask = request->rates[IEEE80211_BAND_5GHZ];
- } else {
- sband = wiphy->bands[IEEE80211_BAND_2GHZ];
- if (WARN_ON_ONCE(!sband))
- return 0;
- rate_mask = request->rates[IEEE80211_BAND_2GHZ];
- }
-
- num_rates = 0;
- for (i = 0; i < sband->n_bitrates; i++) {
- if ((BIT(i) & rate_mask) == 0)
- continue; /* skip rate */
- rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
- }
-
- return num_rates;
-}
-
-/* This function gets the supported data rates. The function works in
- * both Ad-Hoc and infra mode by printing the band and returning the
- * data rates.
- */
-u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
-{
- u32 k = 0;
- struct mwifiex_adapter *adapter = priv->adapter;
-
- if (priv->bss_mode == NL80211_IFTYPE_STATION ||
- priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
- switch (adapter->config_bands) {
- case BAND_B:
- mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
- "supported_rates_b\n",
- adapter->config_bands);
- k = mwifiex_copy_rates(rates, k, supported_rates_b,
- sizeof(supported_rates_b));
- break;
- case BAND_G:
- case BAND_G | BAND_GN:
- mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
- "supported_rates_g\n",
- adapter->config_bands);
- k = mwifiex_copy_rates(rates, k, supported_rates_g,
- sizeof(supported_rates_g));
- break;
- case BAND_B | BAND_G:
- case BAND_A | BAND_B | BAND_G:
- case BAND_A | BAND_B:
- case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN:
- case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC:
- case BAND_B | BAND_G | BAND_GN:
- mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
- "supported_rates_bg\n",
- adapter->config_bands);
- k = mwifiex_copy_rates(rates, k, supported_rates_bg,
- sizeof(supported_rates_bg));
- break;
- case BAND_A:
- case BAND_A | BAND_G:
- mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
- "supported_rates_a\n",
- adapter->config_bands);
- k = mwifiex_copy_rates(rates, k, supported_rates_a,
- sizeof(supported_rates_a));
- break;
- case BAND_AN:
- case BAND_A | BAND_AN:
- case BAND_A | BAND_AN | BAND_AAC:
- case BAND_A | BAND_G | BAND_AN | BAND_GN:
- case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC:
- mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
- "supported_rates_a\n",
- adapter->config_bands);
- k = mwifiex_copy_rates(rates, k, supported_rates_a,
- sizeof(supported_rates_a));
- break;
- case BAND_GN:
- mwifiex_dbg(adapter, INFO, "info: infra band=%d\t"
- "supported_rates_n\n",
- adapter->config_bands);
- k = mwifiex_copy_rates(rates, k, supported_rates_n,
- sizeof(supported_rates_n));
- break;
- }
- } else {
- /* Ad-hoc mode */
- switch (adapter->adhoc_start_band) {
- case BAND_B:
- mwifiex_dbg(adapter, INFO, "info: adhoc B\n");
- k = mwifiex_copy_rates(rates, k, adhoc_rates_b,
- sizeof(adhoc_rates_b));
- break;
- case BAND_G:
- case BAND_G | BAND_GN:
- mwifiex_dbg(adapter, INFO, "info: adhoc G only\n");
- k = mwifiex_copy_rates(rates, k, adhoc_rates_g,
- sizeof(adhoc_rates_g));
- break;
- case BAND_B | BAND_G:
- case BAND_B | BAND_G | BAND_GN:
- mwifiex_dbg(adapter, INFO, "info: adhoc BG\n");
- k = mwifiex_copy_rates(rates, k, adhoc_rates_bg,
- sizeof(adhoc_rates_bg));
- break;
- case BAND_A:
- case BAND_A | BAND_AN:
- mwifiex_dbg(adapter, INFO, "info: adhoc A\n");
- k = mwifiex_copy_rates(rates, k, adhoc_rates_a,
- sizeof(adhoc_rates_a));
- break;
- }
- }
-
- return k;
-}
-
-u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
- u8 rx_rate, u8 rate_info)
-{
- u8 rate_index = 0;
-
- /* HT40 */
- if ((rate_info & BIT(0)) && (rate_info & BIT(1)))
- rate_index = MWIFIEX_RATE_INDEX_MCS0 +
- MWIFIEX_BW20_MCS_NUM + rx_rate;
- else if (rate_info & BIT(0)) /* HT20 */
- rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate;
- else
- rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ?
- rx_rate - 1 : rx_rate;
-
- return rate_index;
-}
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
deleted file mode 100644
index 45ae38e32621..000000000000
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ /dev/null
@@ -1,1659 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: commands and events
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-#include "11ac.h"
-
-/*
- * This function initializes a command node.
- *
- * The actual allocation of the node is not done by this function. It only
- * initiates a node by filling it with default parameters. Similarly,
- * allocation of the different buffers used (IOCTL buffer, data buffer) are
- * not done by this function either.
- */
-static void
-mwifiex_init_cmd_node(struct mwifiex_private *priv,
- struct cmd_ctrl_node *cmd_node,
- u32 cmd_oid, void *data_buf, bool sync)
-{
- cmd_node->priv = priv;
- cmd_node->cmd_oid = cmd_oid;
- if (sync) {
- cmd_node->wait_q_enabled = true;
- cmd_node->cmd_wait_q_woken = false;
- cmd_node->condition = &cmd_node->cmd_wait_q_woken;
- }
- cmd_node->data_buf = data_buf;
- cmd_node->cmd_skb = cmd_node->skb;
-}
-
-/*
- * This function returns a command node from the free queue depending upon
- * availability.
- */
-static struct cmd_ctrl_node *
-mwifiex_get_cmd_node(struct mwifiex_adapter *adapter)
-{
- struct cmd_ctrl_node *cmd_node;
- unsigned long flags;
-
- spin_lock_irqsave(&adapter->cmd_free_q_lock, flags);
- if (list_empty(&adapter->cmd_free_q)) {
- mwifiex_dbg(adapter, ERROR,
- "GET_CMD_NODE: cmd node not available\n");
- spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
- return NULL;
- }
- cmd_node = list_first_entry(&adapter->cmd_free_q,
- struct cmd_ctrl_node, list);
- list_del(&cmd_node->list);
- spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
-
- return cmd_node;
-}
-
-/*
- * This function cleans up a command node.
- *
- * The function resets the fields including the buffer pointers.
- * This function does not try to free the buffers. They must be
- * freed before calling this function.
- *
- * This function will however call the receive completion callback
- * in case a response buffer is still available before resetting
- * the pointer.
- */
-static void
-mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node)
-{
- cmd_node->cmd_oid = 0;
- cmd_node->cmd_flag = 0;
- cmd_node->data_buf = NULL;
- cmd_node->wait_q_enabled = false;
-
- if (cmd_node->cmd_skb)
- skb_trim(cmd_node->cmd_skb, 0);
-
- if (cmd_node->resp_skb) {
- adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb);
- cmd_node->resp_skb = NULL;
- }
-}
-
-/*
- * This function sends a host command to the firmware.
- *
- * The function copies the host command into the driver command
- * buffer, which will be transferred to the firmware later by the
- * main thread.
- */
-static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct mwifiex_ds_misc_cmd *pcmd_ptr)
-{
- /* Copy the HOST command to command buffer */
- memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len);
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: host cmd size = %d\n", pcmd_ptr->len);
- return 0;
-}
-
-/*
- * This function downloads a command to the firmware.
- *
- * The function performs sanity tests, sets the command sequence
- * number and size, converts the header fields to CPU format before
- * sending. Afterwards, it logs the command ID and action for debugging
- * and sets up the command timeout timer.
- */
-static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
- struct cmd_ctrl_node *cmd_node)
-{
-
- struct mwifiex_adapter *adapter = priv->adapter;
- int ret;
- struct host_cmd_ds_command *host_cmd;
- uint16_t cmd_code;
- uint16_t cmd_size;
- unsigned long flags;
- __le32 tmp;
-
- if (!adapter || !cmd_node)
- return -1;
-
- host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
-
- /* Sanity test */
- if (host_cmd == NULL || host_cmd->size == 0) {
- mwifiex_dbg(adapter, ERROR,
- "DNLD_CMD: host_cmd is null\t"
- "or cmd size is 0, not sending\n");
- if (cmd_node->wait_q_enabled)
- adapter->cmd_wait_q.status = -1;
- mwifiex_recycle_cmd_node(adapter, cmd_node);
- return -1;
- }
-
- cmd_code = le16_to_cpu(host_cmd->command);
- cmd_size = le16_to_cpu(host_cmd->size);
-
- if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET &&
- cmd_code != HostCmd_CMD_FUNC_SHUTDOWN &&
- cmd_code != HostCmd_CMD_FUNC_INIT) {
- mwifiex_dbg(adapter, ERROR,
- "DNLD_CMD: FW in reset state, ignore cmd %#x\n",
- cmd_code);
- mwifiex_recycle_cmd_node(adapter, cmd_node);
- queue_work(adapter->workqueue, &adapter->main_work);
- return -1;
- }
-
- /* Set command sequence number */
- adapter->seq_num++;
- host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO
- (adapter->seq_num,
- cmd_node->priv->bss_num,
- cmd_node->priv->bss_type));
-
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
- adapter->curr_cmd = cmd_node;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
-
- /* Adjust skb length */
- if (cmd_node->cmd_skb->len > cmd_size)
- /*
- * cmd_size is less than sizeof(struct host_cmd_ds_command).
- * Trim off the unused portion.
- */
- skb_trim(cmd_node->cmd_skb, cmd_size);
- else if (cmd_node->cmd_skb->len < cmd_size)
- /*
- * cmd_size is larger than sizeof(struct host_cmd_ds_command)
- * because we have appended custom IE TLV. Increase skb length
- * accordingly.
- */
- skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
-
- mwifiex_dbg(adapter, CMD,
- "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
- cmd_code,
- le16_to_cpu(*(__le16 *)((u8 *)host_cmd + S_DS_GEN)),
- cmd_size, le16_to_cpu(host_cmd->seq_num));
- mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size);
-
- if (adapter->iface_type == MWIFIEX_USB) {
- tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD);
- skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);
- memcpy(cmd_node->cmd_skb->data, &tmp, MWIFIEX_TYPE_LEN);
- adapter->cmd_sent = true;
- ret = adapter->if_ops.host_to_card(adapter,
- MWIFIEX_USB_EP_CMD_EVENT,
- cmd_node->cmd_skb, NULL);
- skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);
- if (ret == -EBUSY)
- cmd_node->cmd_skb = NULL;
- } else {
- skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN);
- ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
- cmd_node->cmd_skb, NULL);
- skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN);
- }
-
- if (ret == -1) {
- mwifiex_dbg(adapter, ERROR,
- "DNLD_CMD: host to card failed\n");
- if (adapter->iface_type == MWIFIEX_USB)
- adapter->cmd_sent = false;
- if (cmd_node->wait_q_enabled)
- adapter->cmd_wait_q.status = -1;
- mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
-
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
- adapter->curr_cmd = NULL;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
-
- adapter->dbg.num_cmd_host_to_card_failure++;
- return -1;
- }
-
- /* Save the last command id and action to debug log */
- adapter->dbg.last_cmd_index =
- (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM;
- adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code;
- adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] =
- le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN));
-
- /* Clear BSS_NO_BITS from HostCmd */
- cmd_code &= HostCmd_CMD_ID_MASK;
-
- /* Setup the timer after transmit command */
- mod_timer(&adapter->cmd_timer,
- jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
-
- return 0;
-}
-
-/*
- * This function downloads a sleep confirm command to the firmware.
- *
- * The function performs sanity tests, sets the command sequence
- * number and size, converts the header fields to CPU format before
- * sending.
- *
- * No responses are needed for sleep confirm command.
- */
-static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
-{
- int ret;
- struct mwifiex_private *priv;
- struct mwifiex_opt_sleep_confirm *sleep_cfm_buf =
- (struct mwifiex_opt_sleep_confirm *)
- adapter->sleep_cfm->data;
- struct sk_buff *sleep_cfm_tmp;
- __le32 tmp;
-
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-
- adapter->seq_num++;
- sleep_cfm_buf->seq_num =
- cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO
- (adapter->seq_num, priv->bss_num,
- priv->bss_type)));
-
- mwifiex_dbg(adapter, CMD,
- "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
- le16_to_cpu(sleep_cfm_buf->command),
- le16_to_cpu(sleep_cfm_buf->action),
- le16_to_cpu(sleep_cfm_buf->size),
- le16_to_cpu(sleep_cfm_buf->seq_num));
- mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf,
- le16_to_cpu(sleep_cfm_buf->size));
-
- if (adapter->iface_type == MWIFIEX_USB) {
- sleep_cfm_tmp =
- dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
- + MWIFIEX_TYPE_LEN);
- skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm)
- + MWIFIEX_TYPE_LEN);
- tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD);
- memcpy(sleep_cfm_tmp->data, &tmp, MWIFIEX_TYPE_LEN);
- memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN,
- adapter->sleep_cfm->data,
- sizeof(struct mwifiex_opt_sleep_confirm));
- ret = adapter->if_ops.host_to_card(adapter,
- MWIFIEX_USB_EP_CMD_EVENT,
- sleep_cfm_tmp, NULL);
- if (ret != -EBUSY)
- dev_kfree_skb_any(sleep_cfm_tmp);
- } else {
- skb_push(adapter->sleep_cfm, INTF_HEADER_LEN);
- ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
- adapter->sleep_cfm, NULL);
- skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN);
- }
-
- if (ret == -1) {
- mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n");
- adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++;
- return -1;
- }
-
- if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl))
- /* Response is not needed for sleep confirm command */
- adapter->ps_state = PS_STATE_SLEEP;
- else
- adapter->ps_state = PS_STATE_SLEEP_CFM;
-
- if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) &&
- (adapter->is_hs_configured &&
- !adapter->sleep_period.period)) {
- adapter->pm_wakeup_card_req = true;
- mwifiex_hs_activated_event(mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY), true);
- }
-
- return ret;
-}
-
-/*
- * This function allocates the command buffers and links them to
- * the command free queue.
- *
- * The driver uses a pre allocated number of command buffers, which
- * are created at driver initializations and freed at driver cleanup.
- * Every command needs to obtain a command buffer from this pool before
- * it can be issued. The command free queue lists the command buffers
- * currently free to use, while the command pending queue lists the
- * command buffers already in use and awaiting handling. Command buffers
- * are returned to the free queue after use.
- */
-int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter)
-{
- struct cmd_ctrl_node *cmd_array;
- u32 i;
-
- /* Allocate and initialize struct cmd_ctrl_node */
- cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER,
- sizeof(struct cmd_ctrl_node), GFP_KERNEL);
- if (!cmd_array)
- return -ENOMEM;
-
- adapter->cmd_pool = cmd_array;
-
- /* Allocate and initialize command buffers */
- for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
- cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER);
- if (!cmd_array[i].skb) {
- mwifiex_dbg(adapter, ERROR,
- "unable to allocate command buffer\n");
- return -ENOMEM;
- }
- }
-
- for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++)
- mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]);
-
- return 0;
-}
-
-/*
- * This function frees the command buffers.
- *
- * The function calls the completion callback for all the command
- * buffers that still have response buffers associated with them.
- */
-int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter)
-{
- struct cmd_ctrl_node *cmd_array;
- u32 i;
-
- /* Need to check if cmd pool is allocated or not */
- if (!adapter->cmd_pool) {
- mwifiex_dbg(adapter, FATAL,
- "info: FREE_CMD_BUF: cmd_pool is null\n");
- return 0;
- }
-
- cmd_array = adapter->cmd_pool;
-
- /* Release shared memory buffers */
- for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
- if (cmd_array[i].skb) {
- mwifiex_dbg(adapter, CMD,
- "cmd: free cmd buffer %d\n", i);
- dev_kfree_skb_any(cmd_array[i].skb);
- }
- if (!cmd_array[i].resp_skb)
- continue;
-
- if (adapter->iface_type == MWIFIEX_USB)
- adapter->if_ops.cmdrsp_complete(adapter,
- cmd_array[i].resp_skb);
- else
- dev_kfree_skb_any(cmd_array[i].resp_skb);
- }
- /* Release struct cmd_ctrl_node */
- if (adapter->cmd_pool) {
- mwifiex_dbg(adapter, CMD,
- "cmd: free cmd pool\n");
- kfree(adapter->cmd_pool);
- adapter->cmd_pool = NULL;
- }
-
- return 0;
-}
-
-/*
- * This function handles events generated by firmware.
- *
- * Event body of events received from firmware are not used (though they are
- * saved), only the event ID is used. Some events are re-invoked by
- * the driver, with a new event body.
- *
- * After processing, the function calls the completion callback
- * for cleanup.
- */
-int mwifiex_process_event(struct mwifiex_adapter *adapter)
-{
- int ret;
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- struct sk_buff *skb = adapter->event_skb;
- u32 eventcause = adapter->event_cause;
- struct mwifiex_rxinfo *rx_info;
-
- /* Save the last event to debug log */
- adapter->dbg.last_event_index =
- (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM;
- adapter->dbg.last_event[adapter->dbg.last_event_index] =
- (u16) eventcause;
-
- /* Get BSS number and corresponding priv */
- priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause),
- EVENT_GET_BSS_TYPE(eventcause));
- if (!priv)
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-
- /* Clear BSS_NO_BITS from event */
- eventcause &= EVENT_ID_MASK;
- adapter->event_cause = eventcause;
-
- if (skb) {
- rx_info = MWIFIEX_SKB_RXCB(skb);
- memset(rx_info, 0, sizeof(*rx_info));
- rx_info->bss_num = priv->bss_num;
- rx_info->bss_type = priv->bss_type;
- mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:",
- skb->data, skb->len);
- }
-
- mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause);
-
- if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
- ret = mwifiex_process_uap_event(priv);
- else
- ret = mwifiex_process_sta_event(priv);
-
- adapter->event_cause = 0;
- adapter->event_skb = NULL;
- adapter->if_ops.event_complete(adapter, skb);
-
- return ret;
-}
-
-/*
- * This function prepares a command and send it to the firmware.
- *
- * Preparation includes -
- * - Sanity tests to make sure the card is still present or the FW
- * is not reset
- * - Getting a new command node from the command free queue
- * - Initializing the command node for default parameters
- * - Fill up the non-default parameters and buffer pointers
- * - Add the command to pending queue
- */
-int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,
- u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync)
-{
- int ret;
- struct mwifiex_adapter *adapter = priv->adapter;
- struct cmd_ctrl_node *cmd_node;
- struct host_cmd_ds_command *cmd_ptr;
-
- if (!adapter) {
- pr_err("PREP_CMD: adapter is NULL\n");
- return -1;
- }
-
- if (adapter->is_suspended) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: device in suspended state\n");
- return -1;
- }
-
- if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: host entering sleep state\n");
- return -1;
- }
-
- if (adapter->surprise_removed) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: card is removed\n");
- return -1;
- }
-
- if (adapter->is_cmd_timedout) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: FW is in bad state\n");
- return -1;
- }
-
- if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) {
- if (cmd_no != HostCmd_CMD_FUNC_INIT) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: FW in reset state\n");
- return -1;
- }
- }
-
- /* Get a new command node */
- cmd_node = mwifiex_get_cmd_node(adapter);
-
- if (!cmd_node) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: no free cmd node\n");
- return -1;
- }
-
- /* Initialize the command node */
- mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync);
-
- if (!cmd_node->cmd_skb) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: no free cmd buf\n");
- return -1;
- }
-
- memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)),
- 0, sizeof(struct host_cmd_ds_command));
-
- cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
- cmd_ptr->command = cpu_to_le16(cmd_no);
- cmd_ptr->result = 0;
-
- /* Prepare command */
- if (cmd_no) {
- switch (cmd_no) {
- case HostCmd_CMD_UAP_SYS_CONFIG:
- case HostCmd_CMD_UAP_BSS_START:
- case HostCmd_CMD_UAP_BSS_STOP:
- case HostCmd_CMD_UAP_STA_DEAUTH:
- case HOST_CMD_APCMD_SYS_RESET:
- case HOST_CMD_APCMD_STA_LIST:
- ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
- cmd_oid, data_buf,
- cmd_ptr);
- break;
- default:
- ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action,
- cmd_oid, data_buf,
- cmd_ptr);
- break;
- }
- } else {
- ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf);
- cmd_node->cmd_flag |= CMD_F_HOSTCMD;
- }
-
- /* Return error, since the command preparation failed */
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "PREP_CMD: cmd %#x preparation failed\n",
- cmd_no);
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
- return -1;
- }
-
- /* Send command */
- if (cmd_no == HostCmd_CMD_802_11_SCAN ||
- cmd_no == HostCmd_CMD_802_11_SCAN_EXT) {
- mwifiex_queue_scan_cmd(priv, cmd_node);
- } else {
- mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
- queue_work(adapter->workqueue, &adapter->main_work);
- if (cmd_node->wait_q_enabled)
- ret = mwifiex_wait_queue_complete(adapter, cmd_node);
- }
-
- return ret;
-}
-
-/*
- * This function returns a command to the command free queue.
- *
- * The function also calls the completion callback if required, before
- * cleaning the command node and re-inserting it into the free queue.
- */
-void
-mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node)
-{
- unsigned long flags;
-
- if (!cmd_node)
- return;
-
- if (cmd_node->wait_q_enabled)
- mwifiex_complete_cmd(adapter, cmd_node);
- /* Clean the node */
- mwifiex_clean_cmd_node(adapter, cmd_node);
-
- /* Insert node into cmd_free_q */
- spin_lock_irqsave(&adapter->cmd_free_q_lock, flags);
- list_add_tail(&cmd_node->list, &adapter->cmd_free_q);
- spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
-}
-
-/* This function reuses a command node. */
-void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node)
-{
- struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data;
-
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
-
- atomic_dec(&adapter->cmd_pending);
- mwifiex_dbg(adapter, CMD,
- "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n",
- le16_to_cpu(host_cmd->command),
- atomic_read(&adapter->cmd_pending));
-}
-
-/*
- * This function queues a command to the command pending queue.
- *
- * This in effect adds the command to the command list to be executed.
- * Exit PS command is handled specially, by placing it always to the
- * front of the command queue.
- */
-void
-mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node, u32 add_tail)
-{
- struct host_cmd_ds_command *host_cmd = NULL;
- u16 command;
- unsigned long flags;
-
- host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
- if (!host_cmd) {
- mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n");
- return;
- }
-
- command = le16_to_cpu(host_cmd->command);
-
- /* Exit_PS command needs to be queued in the header always. */
- if (command == HostCmd_CMD_802_11_PS_MODE_ENH) {
- struct host_cmd_ds_802_11_ps_mode_enh *pm =
- &host_cmd->params.psmode_enh;
- if ((le16_to_cpu(pm->action) == DIS_PS) ||
- (le16_to_cpu(pm->action) == DIS_AUTO_PS)) {
- if (adapter->ps_state != PS_STATE_AWAKE)
- add_tail = false;
- }
- }
-
- spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
- if (add_tail)
- list_add_tail(&cmd_node->list, &adapter->cmd_pending_q);
- else
- list_add(&cmd_node->list, &adapter->cmd_pending_q);
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
-
- atomic_inc(&adapter->cmd_pending);
- mwifiex_dbg(adapter, CMD,
- "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n",
- command, atomic_read(&adapter->cmd_pending));
-}
-
-/*
- * This function executes the next command in command pending queue.
- *
- * This function will fail if a command is already in processing stage,
- * otherwise it will dequeue the first command from the command pending
- * queue and send to the firmware.
- *
- * If the device is currently in host sleep mode, any commands, except the
- * host sleep configuration command will de-activate the host sleep. For PS
- * mode, the function will put the firmware back to sleep if applicable.
- */
-int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
-{
- struct mwifiex_private *priv;
- struct cmd_ctrl_node *cmd_node;
- int ret = 0;
- struct host_cmd_ds_command *host_cmd;
- unsigned long cmd_flags;
- unsigned long cmd_pending_q_flags;
-
- /* Check if already in processing */
- if (adapter->curr_cmd) {
- mwifiex_dbg(adapter, FATAL,
- "EXEC_NEXT_CMD: cmd in processing\n");
- return -1;
- }
-
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
- /* Check if any command is pending */
- spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags);
- if (list_empty(&adapter->cmd_pending_q)) {
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
- cmd_pending_q_flags);
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
- return 0;
- }
- cmd_node = list_first_entry(&adapter->cmd_pending_q,
- struct cmd_ctrl_node, list);
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
- cmd_pending_q_flags);
-
- host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
- priv = cmd_node->priv;
-
- if (adapter->ps_state != PS_STATE_AWAKE) {
- mwifiex_dbg(adapter, ERROR,
- "%s: cannot send cmd in sleep state,\t"
- "this should not happen\n", __func__);
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
- return ret;
- }
-
- spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags);
- list_del(&cmd_node->list);
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
- cmd_pending_q_flags);
-
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
- ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node);
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- /* Any command sent to the firmware when host is in sleep
- * mode should de-configure host sleep. We should skip the
- * host sleep configuration command itself though
- */
- if (priv && (host_cmd->command !=
- cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) {
- if (adapter->hs_activated) {
- adapter->is_hs_configured = false;
- mwifiex_hs_activated_event(priv, false);
- }
- }
-
- return ret;
-}
-
-/*
- * This function handles the command response.
- *
- * After processing, the function cleans the command node and puts
- * it back to the command free queue.
- */
-int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
-{
- struct host_cmd_ds_command *resp;
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- int ret = 0;
- uint16_t orig_cmdresp_no;
- uint16_t cmdresp_no;
- uint16_t cmdresp_result;
- unsigned long flags;
-
- /* Now we got response from FW, cancel the command timer */
- del_timer_sync(&adapter->cmd_timer);
-
- if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) {
- resp = (struct host_cmd_ds_command *) adapter->upld_buf;
- mwifiex_dbg(adapter, ERROR,
- "CMD_RESP: NULL curr_cmd, %#x\n",
- le16_to_cpu(resp->command));
- return -1;
- }
-
- adapter->is_cmd_timedout = 0;
-
- resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data;
- if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
- /* Copy original response back to response buffer */
- struct mwifiex_ds_misc_cmd *hostcmd;
- uint16_t size = le16_to_cpu(resp->size);
- mwifiex_dbg(adapter, INFO,
- "info: host cmd resp size = %d\n", size);
- size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER);
- if (adapter->curr_cmd->data_buf) {
- hostcmd = adapter->curr_cmd->data_buf;
- hostcmd->len = size;
- memcpy(hostcmd->cmd, resp, size);
- }
- }
- orig_cmdresp_no = le16_to_cpu(resp->command);
-
- /* Get BSS number and corresponding priv */
- priv = mwifiex_get_priv_by_id(adapter,
- HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)),
- HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num)));
- if (!priv)
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- /* Clear RET_BIT from HostCmd */
- resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK);
-
- cmdresp_no = le16_to_cpu(resp->command);
- cmdresp_result = le16_to_cpu(resp->result);
-
- /* Save the last command response to debug log */
- adapter->dbg.last_cmd_resp_index =
- (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM;
- adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =
- orig_cmdresp_no;
-
- mwifiex_dbg(adapter, CMD,
- "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
- orig_cmdresp_no, cmdresp_result,
- le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
- mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp,
- le16_to_cpu(resp->size));
-
- if (!(orig_cmdresp_no & HostCmd_RET_BIT)) {
- mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n");
- if (adapter->curr_cmd->wait_q_enabled)
- adapter->cmd_wait_q.status = -1;
-
- mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
- adapter->curr_cmd = NULL;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
- return -1;
- }
-
- if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
- adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD;
- if ((cmdresp_result == HostCmd_RESULT_OK) &&
- (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH))
- ret = mwifiex_ret_802_11_hs_cfg(priv, resp);
- } else {
- /* handle response */
- ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp);
- }
-
- /* Check init command response */
- if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "%s: cmd %#x failed during\t"
- "initialization\n", __func__, cmdresp_no);
- mwifiex_init_fw_complete(adapter);
- return -1;
- } else if (adapter->last_init_cmd == cmdresp_no)
- adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE;
- }
-
- if (adapter->curr_cmd) {
- if (adapter->curr_cmd->wait_q_enabled)
- adapter->cmd_wait_q.status = ret;
-
- mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
-
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
- adapter->curr_cmd = NULL;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
- }
-
- return ret;
-}
-
-/*
- * This function handles the timeout of command sending.
- *
- * It will re-send the same command again.
- */
-void
-mwifiex_cmd_timeout_func(unsigned long function_context)
-{
- struct mwifiex_adapter *adapter =
- (struct mwifiex_adapter *) function_context;
- struct cmd_ctrl_node *cmd_node;
-
- adapter->is_cmd_timedout = 1;
- if (!adapter->curr_cmd) {
- mwifiex_dbg(adapter, ERROR,
- "cmd: empty curr_cmd\n");
- return;
- }
- cmd_node = adapter->curr_cmd;
- if (cmd_node) {
- adapter->dbg.timeout_cmd_id =
- adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index];
- adapter->dbg.timeout_cmd_act =
- adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index];
- mwifiex_dbg(adapter, MSG,
- "%s: Timeout cmd id = %#x, act = %#x\n", __func__,
- adapter->dbg.timeout_cmd_id,
- adapter->dbg.timeout_cmd_act);
-
- mwifiex_dbg(adapter, MSG,
- "num_data_h2c_failure = %d\n",
- adapter->dbg.num_tx_host_to_card_failure);
- mwifiex_dbg(adapter, MSG,
- "num_cmd_h2c_failure = %d\n",
- adapter->dbg.num_cmd_host_to_card_failure);
-
- mwifiex_dbg(adapter, MSG,
- "is_cmd_timedout = %d\n",
- adapter->is_cmd_timedout);
- mwifiex_dbg(adapter, MSG,
- "num_tx_timeout = %d\n",
- adapter->dbg.num_tx_timeout);
-
- mwifiex_dbg(adapter, MSG,
- "last_cmd_index = %d\n",
- adapter->dbg.last_cmd_index);
- mwifiex_dbg(adapter, MSG,
- "last_cmd_id: %*ph\n",
- (int)sizeof(adapter->dbg.last_cmd_id),
- adapter->dbg.last_cmd_id);
- mwifiex_dbg(adapter, MSG,
- "last_cmd_act: %*ph\n",
- (int)sizeof(adapter->dbg.last_cmd_act),
- adapter->dbg.last_cmd_act);
-
- mwifiex_dbg(adapter, MSG,
- "last_cmd_resp_index = %d\n",
- adapter->dbg.last_cmd_resp_index);
- mwifiex_dbg(adapter, MSG,
- "last_cmd_resp_id: %*ph\n",
- (int)sizeof(adapter->dbg.last_cmd_resp_id),
- adapter->dbg.last_cmd_resp_id);
-
- mwifiex_dbg(adapter, MSG,
- "last_event_index = %d\n",
- adapter->dbg.last_event_index);
- mwifiex_dbg(adapter, MSG,
- "last_event: %*ph\n",
- (int)sizeof(adapter->dbg.last_event),
- adapter->dbg.last_event);
-
- mwifiex_dbg(adapter, MSG,
- "data_sent=%d cmd_sent=%d\n",
- adapter->data_sent, adapter->cmd_sent);
-
- mwifiex_dbg(adapter, MSG,
- "ps_mode=%d ps_state=%d\n",
- adapter->ps_mode, adapter->ps_state);
-
- if (cmd_node->wait_q_enabled) {
- adapter->cmd_wait_q.status = -ETIMEDOUT;
- mwifiex_cancel_pending_ioctl(adapter);
- }
- }
- if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
- mwifiex_init_fw_complete(adapter);
- return;
- }
-
- if (adapter->if_ops.device_dump)
- adapter->if_ops.device_dump(adapter);
-
- if (adapter->if_ops.card_reset)
- adapter->if_ops.card_reset(adapter);
-}
-
-/*
- * This function cancels all the pending commands.
- *
- * The current command, all commands in command pending queue and all scan
- * commands in scan pending queue are cancelled. All the completion callbacks
- * are called with failure status to ensure cleanup.
- */
-void
-mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
-{
- struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
- unsigned long flags, cmd_flags;
- struct mwifiex_private *priv;
- int i;
-
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
- /* Cancel current cmd */
- if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) {
- adapter->curr_cmd->wait_q_enabled = false;
- adapter->cmd_wait_q.status = -1;
- mwifiex_complete_cmd(adapter, adapter->curr_cmd);
- /* no recycle probably wait for response */
- }
- /* Cancel all pending command */
- spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
- list_for_each_entry_safe(cmd_node, tmp_node,
- &adapter->cmd_pending_q, list) {
- list_del(&cmd_node->list);
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
-
- if (cmd_node->wait_q_enabled)
- adapter->cmd_wait_q.status = -1;
- mwifiex_recycle_cmd_node(adapter, cmd_node);
- spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
- }
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
-
- /* Cancel all pending scan command */
- spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
- list_for_each_entry_safe(cmd_node, tmp_node,
- &adapter->scan_pending_q, list) {
- list_del(&cmd_node->list);
-
- cmd_node->wait_q_enabled = false;
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
- }
- spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
-
- if (adapter->scan_processing) {
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
- adapter->scan_processing = false;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
- if (!priv)
- continue;
- if (priv->scan_request) {
- mwifiex_dbg(adapter, WARN, "info: aborting scan\n");
- cfg80211_scan_done(priv->scan_request, 1);
- priv->scan_request = NULL;
- }
- }
- }
-}
-
-/*
- * This function cancels all pending commands that matches with
- * the given IOCTL request.
- *
- * Both the current command buffer and the pending command queue are
- * searched for matching IOCTL request. The completion callback of
- * the matched command is called with failure status to ensure cleanup.
- * In case of scan commands, all pending commands in scan pending queue
- * are cancelled.
- */
-void
-mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
-{
- struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
- unsigned long cmd_flags;
- unsigned long scan_pending_q_flags;
- struct mwifiex_private *priv;
- int i;
-
- if ((adapter->curr_cmd) &&
- (adapter->curr_cmd->wait_q_enabled)) {
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
- cmd_node = adapter->curr_cmd;
- /* setting curr_cmd to NULL is quite dangerous, because
- * mwifiex_process_cmdresp checks curr_cmd to be != NULL
- * at the beginning then relies on it and dereferences
- * it at will
- * this probably works since mwifiex_cmd_timeout_func
- * is the only caller of this function and responses
- * at that point
- */
- adapter->curr_cmd = NULL;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
-
- mwifiex_recycle_cmd_node(adapter, cmd_node);
- }
-
- /* Cancel all pending scan command */
- spin_lock_irqsave(&adapter->scan_pending_q_lock,
- scan_pending_q_flags);
- list_for_each_entry_safe(cmd_node, tmp_node,
- &adapter->scan_pending_q, list) {
- list_del(&cmd_node->list);
- cmd_node->wait_q_enabled = false;
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
- }
- spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
- scan_pending_q_flags);
-
- if (adapter->scan_processing) {
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
- adapter->scan_processing = false;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
- if (!priv)
- continue;
- if (priv->scan_request) {
- mwifiex_dbg(adapter, WARN, "info: aborting scan\n");
- cfg80211_scan_done(priv->scan_request, 1);
- priv->scan_request = NULL;
- }
- }
- }
-}
-
-/*
- * This function sends the sleep confirm command to firmware, if
- * possible.
- *
- * The sleep confirm command cannot be issued if command response,
- * data response or event response is awaiting handling, or if we
- * are in the middle of sending a command, or expecting a command
- * response.
- */
-void
-mwifiex_check_ps_cond(struct mwifiex_adapter *adapter)
-{
- if (!adapter->cmd_sent &&
- !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter))
- mwifiex_dnld_sleep_confirm_cmd(adapter);
- else
- mwifiex_dbg(adapter, CMD,
- "cmd: Delay Sleep Confirm (%s%s%s)\n",
- (adapter->cmd_sent) ? "D" : "",
- (adapter->curr_cmd) ? "C" : "",
- (IS_CARD_RX_RCVD(adapter)) ? "R" : "");
-}
-
-/*
- * This function sends a Host Sleep activated event to applications.
- *
- * This event is generated by the driver, with a blank event body.
- */
-void
-mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated)
-{
- if (activated) {
- if (priv->adapter->is_hs_configured) {
- priv->adapter->hs_activated = true;
- mwifiex_update_rxreor_flags(priv->adapter,
- RXREOR_FORCE_NO_DROP);
- mwifiex_dbg(priv->adapter, EVENT,
- "event: hs_activated\n");
- priv->adapter->hs_activate_wait_q_woken = true;
- wake_up_interruptible(
- &priv->adapter->hs_activate_wait_q);
- } else {
- mwifiex_dbg(priv->adapter, EVENT,
- "event: HS not configured\n");
- }
- } else {
- mwifiex_dbg(priv->adapter, EVENT,
- "event: hs_deactivated\n");
- priv->adapter->hs_activated = false;
- }
-}
-
-/*
- * This function handles the command response of a Host Sleep configuration
- * command.
- *
- * Handling includes changing the header fields into CPU format
- * and setting the current host sleep activation status in driver.
- *
- * In case host sleep status change, the function generates an event to
- * notify the applications.
- */
-int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg =
- &resp->params.opt_hs_cfg;
- uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);
-
- if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) &&
- adapter->iface_type != MWIFIEX_USB) {
- mwifiex_hs_activated_event(priv, true);
- return 0;
- } else {
- mwifiex_dbg(adapter, CMD,
- "cmd: CMD_RESP: HS_CFG cmd reply\t"
- " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n",
- resp->result, conditions,
- phs_cfg->params.hs_config.gpio,
- phs_cfg->params.hs_config.gap);
- }
- if (conditions != HS_CFG_CANCEL) {
- adapter->is_hs_configured = true;
- if (adapter->iface_type == MWIFIEX_USB)
- mwifiex_hs_activated_event(priv, true);
- } else {
- adapter->is_hs_configured = false;
- if (adapter->hs_activated)
- mwifiex_hs_activated_event(priv, false);
- }
-
- return 0;
-}
-
-/*
- * This function wakes up the adapter and generates a Host Sleep
- * cancel event on receiving the power up interrupt.
- */
-void
-mwifiex_process_hs_config(struct mwifiex_adapter *adapter)
-{
- mwifiex_dbg(adapter, INFO,
- "info: %s: auto cancelling host sleep\t"
- "since there is interrupt from the firmware\n",
- __func__);
-
- adapter->if_ops.wakeup(adapter);
- adapter->hs_activated = false;
- adapter->is_hs_configured = false;
- adapter->is_suspended = false;
- mwifiex_hs_activated_event(mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_ANY),
- false);
-}
-EXPORT_SYMBOL_GPL(mwifiex_process_hs_config);
-
-/*
- * This function handles the command response of a sleep confirm command.
- *
- * The function sets the card state to SLEEP if the response indicates success.
- */
-void
-mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
- u8 *pbuf, u32 upld_len)
-{
- struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf;
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- uint16_t result = le16_to_cpu(cmd->result);
- uint16_t command = le16_to_cpu(cmd->command);
- uint16_t seq_num = le16_to_cpu(cmd->seq_num);
-
- if (!upld_len) {
- mwifiex_dbg(adapter, ERROR,
- "%s: cmd size is 0\n", __func__);
- return;
- }
-
- mwifiex_dbg(adapter, CMD,
- "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
- command, result, le16_to_cpu(cmd->size), seq_num);
-
- /* Get BSS number and corresponding priv */
- priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num),
- HostCmd_GET_BSS_TYPE(seq_num));
- if (!priv)
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-
- /* Update sequence number */
- seq_num = HostCmd_GET_SEQ_NO(seq_num);
- /* Clear RET_BIT from HostCmd */
- command &= HostCmd_CMD_ID_MASK;
-
- if (command != HostCmd_CMD_802_11_PS_MODE_ENH) {
- mwifiex_dbg(adapter, ERROR,
- "%s: rcvd unexpected resp for cmd %#x, result = %x\n",
- __func__, command, result);
- return;
- }
-
- if (result) {
- mwifiex_dbg(adapter, ERROR,
- "%s: sleep confirm cmd failed\n",
- __func__);
- adapter->pm_wakeup_card_req = false;
- adapter->ps_state = PS_STATE_AWAKE;
- return;
- }
- adapter->pm_wakeup_card_req = true;
- if (adapter->is_hs_configured)
- mwifiex_hs_activated_event(mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- true);
- adapter->ps_state = PS_STATE_SLEEP;
- cmd->command = cpu_to_le16(command);
- cmd->seq_num = cpu_to_le16(seq_num);
-}
-EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp);
-
-/*
- * This function prepares an enhanced power mode command.
- *
- * This function can be used to disable power save or to configure
- * power save with auto PS or STA PS or auto deep sleep.
- *
- * Preparation includes -
- * - Setting command ID, action and proper size
- * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV,
- * auto deep sleep TLV (as required)
- * - Ensuring correct endian-ness
- */
-int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- u16 cmd_action, uint16_t ps_bitmap,
- struct mwifiex_ds_auto_ds *auto_ds)
-{
- struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh =
- &cmd->params.psmode_enh;
- u8 *tlv;
- u16 cmd_size = 0;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
- if (cmd_action == DIS_AUTO_PS) {
- psmode_enh->action = cpu_to_le16(DIS_AUTO_PS);
- psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
- cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
- sizeof(psmode_enh->params.ps_bitmap));
- } else if (cmd_action == GET_PS) {
- psmode_enh->action = cpu_to_le16(GET_PS);
- psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
- cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
- sizeof(psmode_enh->params.ps_bitmap));
- } else if (cmd_action == EN_AUTO_PS) {
- psmode_enh->action = cpu_to_le16(EN_AUTO_PS);
- psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
- cmd_size = S_DS_GEN + sizeof(psmode_enh->action) +
- sizeof(psmode_enh->params.ps_bitmap);
- tlv = (u8 *) cmd + cmd_size;
- if (ps_bitmap & BITMAP_STA_PS) {
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_ie_types_ps_param *ps_tlv =
- (struct mwifiex_ie_types_ps_param *) tlv;
- struct mwifiex_ps_param *ps_mode = &ps_tlv->param;
- ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM);
- ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) -
- sizeof(struct mwifiex_ie_types_header));
- cmd_size += sizeof(*ps_tlv);
- tlv += sizeof(*ps_tlv);
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: PS Command: Enter PS\n");
- ps_mode->null_pkt_interval =
- cpu_to_le16(adapter->null_pkt_interval);
- ps_mode->multiple_dtims =
- cpu_to_le16(adapter->multiple_dtim);
- ps_mode->bcn_miss_timeout =
- cpu_to_le16(adapter->bcn_miss_time_out);
- ps_mode->local_listen_interval =
- cpu_to_le16(adapter->local_listen_interval);
- ps_mode->adhoc_wake_period =
- cpu_to_le16(adapter->adhoc_awake_period);
- ps_mode->delay_to_ps =
- cpu_to_le16(adapter->delay_to_ps);
- ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode);
-
- }
- if (ps_bitmap & BITMAP_AUTO_DS) {
- struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv =
- (struct mwifiex_ie_types_auto_ds_param *) tlv;
- u16 idletime = 0;
-
- auto_ds_tlv->header.type =
- cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM);
- auto_ds_tlv->header.len =
- cpu_to_le16(sizeof(*auto_ds_tlv) -
- sizeof(struct mwifiex_ie_types_header));
- cmd_size += sizeof(*auto_ds_tlv);
- tlv += sizeof(*auto_ds_tlv);
- if (auto_ds)
- idletime = auto_ds->idle_time;
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: PS Command: Enter Auto Deep Sleep\n");
- auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime);
- }
- cmd->size = cpu_to_le16(cmd_size);
- }
- return 0;
-}
-
-/*
- * This function handles the command response of an enhanced power mode
- * command.
- *
- * Handling includes changing the header fields into CPU format
- * and setting the current enhanced power mode in driver.
- */
-int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp,
- struct mwifiex_ds_pm_cfg *pm_cfg)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =
- &resp->params.psmode_enh;
- uint16_t action = le16_to_cpu(ps_mode->action);
- uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap);
- uint16_t auto_ps_bitmap =
- le16_to_cpu(ps_mode->params.ps_bitmap);
-
- mwifiex_dbg(adapter, INFO,
- "info: %s: PS_MODE cmd reply result=%#x action=%#X\n",
- __func__, resp->result, action);
- if (action == EN_AUTO_PS) {
- if (auto_ps_bitmap & BITMAP_AUTO_DS) {
- mwifiex_dbg(adapter, CMD,
- "cmd: Enabled auto deep sleep\n");
- priv->adapter->is_deep_sleep = true;
- }
- if (auto_ps_bitmap & BITMAP_STA_PS) {
- mwifiex_dbg(adapter, CMD,
- "cmd: Enabled STA power save\n");
- if (adapter->sleep_period.period)
- mwifiex_dbg(adapter, CMD,
- "cmd: set to uapsd/pps mode\n");
- }
- } else if (action == DIS_AUTO_PS) {
- if (ps_bitmap & BITMAP_AUTO_DS) {
- priv->adapter->is_deep_sleep = false;
- mwifiex_dbg(adapter, CMD,
- "cmd: Disabled auto deep sleep\n");
- }
- if (ps_bitmap & BITMAP_STA_PS) {
- mwifiex_dbg(adapter, CMD,
- "cmd: Disabled STA power save\n");
- if (adapter->sleep_period.period) {
- adapter->delay_null_pkt = false;
- adapter->tx_lock_flag = false;
- adapter->pps_uapsd_mode = false;
- }
- }
- } else if (action == GET_PS) {
- if (ps_bitmap & BITMAP_STA_PS)
- adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
- else
- adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
-
- mwifiex_dbg(adapter, CMD,
- "cmd: ps_bitmap=%#x\n", ps_bitmap);
-
- if (pm_cfg) {
- /* This section is for get power save mode */
- if (ps_bitmap & BITMAP_STA_PS)
- pm_cfg->param.ps_mode = 1;
- else
- pm_cfg->param.ps_mode = 0;
- }
- }
- return 0;
-}
-
-/*
- * This function prepares command to get hardware specifications.
- *
- * Preparation includes -
- * - Setting command ID, action and proper size
- * - Setting permanent address parameter
- * - Ensuring correct endian-ness
- */
-int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd)
-{
- struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC);
- cmd->size =
- cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN);
- memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN);
-
- return 0;
-}
-
-/*
- * This function handles the command response of get hardware
- * specifications.
- *
- * Handling includes changing the header fields into CPU format
- * and saving/updating the following parameters in driver -
- * - Firmware capability information
- * - Firmware band settings
- * - Ad-hoc start band and channel
- * - Ad-hoc 11n activation status
- * - Firmware release number
- * - Number of antennas
- * - Hardware address
- * - Hardware interface version
- * - Firmware version
- * - Region code
- * - 11n capabilities
- * - MCS support fields
- * - MP end port
- */
-int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp)
-{
- struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_ie_types_header *tlv;
- struct hw_spec_api_rev *api_rev;
- u16 resp_size, api_id;
- int i, left_len, parsed_len = 0;
-
- adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info);
-
- if (IS_SUPPORT_MULTI_BANDS(adapter))
- adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter);
- else
- adapter->fw_bands = BAND_B;
-
- adapter->config_bands = adapter->fw_bands;
-
- if (adapter->fw_bands & BAND_A) {
- if (adapter->fw_bands & BAND_GN) {
- adapter->config_bands |= BAND_AN;
- adapter->fw_bands |= BAND_AN;
- }
- if (adapter->fw_bands & BAND_AN) {
- adapter->adhoc_start_band = BAND_A | BAND_AN;
- adapter->adhoc_11n_enabled = true;
- } else {
- adapter->adhoc_start_band = BAND_A;
- }
- priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A;
- } else if (adapter->fw_bands & BAND_GN) {
- adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN;
- priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
- adapter->adhoc_11n_enabled = true;
- } else if (adapter->fw_bands & BAND_G) {
- adapter->adhoc_start_band = BAND_G | BAND_B;
- priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
- } else if (adapter->fw_bands & BAND_B) {
- adapter->adhoc_start_band = BAND_B;
- priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
- }
-
- adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
- adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff;
- adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna);
-
- if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {
- adapter->is_hw_11ac_capable = true;
-
- /* Copy 11AC cap */
- adapter->hw_dot_11ac_dev_cap =
- le32_to_cpu(hw_spec->dot_11ac_dev_cap);
- adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap
- & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
- adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap
- & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
-
- /* Copy 11AC mcs */
- adapter->hw_dot_11ac_mcs_support =
- le32_to_cpu(hw_spec->dot_11ac_mcs_support);
- adapter->usr_dot_11ac_mcs_support =
- adapter->hw_dot_11ac_mcs_support;
- } else {
- adapter->is_hw_11ac_capable = false;
- }
-
- resp_size = le16_to_cpu(resp->size) - S_DS_GEN;
- if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) {
- /* we have variable HW SPEC information */
- left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec);
- while (left_len > sizeof(struct mwifiex_ie_types_header)) {
- tlv = (void *)&hw_spec->tlvs + parsed_len;
- switch (le16_to_cpu(tlv->type)) {
- case TLV_TYPE_API_REV:
- api_rev = (struct hw_spec_api_rev *)tlv;
- api_id = le16_to_cpu(api_rev->api_id);
- switch (api_id) {
- case KEY_API_VER_ID:
- adapter->key_api_major_ver =
- api_rev->major_ver;
- adapter->key_api_minor_ver =
- api_rev->minor_ver;
- mwifiex_dbg(adapter, INFO,
- "key_api v%d.%d\n",
- adapter->key_api_major_ver,
- adapter->key_api_minor_ver);
- break;
- case FW_API_VER_ID:
- adapter->fw_api_ver =
- api_rev->major_ver;
- mwifiex_dbg(adapter, INFO,
- "Firmware api version %d\n",
- adapter->fw_api_ver);
- break;
- default:
- mwifiex_dbg(adapter, FATAL,
- "Unknown api_id: %d\n",
- api_id);
- break;
- }
- break;
- default:
- mwifiex_dbg(adapter, FATAL,
- "Unknown GET_HW_SPEC TLV type: %#x\n",
- le16_to_cpu(tlv->type));
- break;
- }
- parsed_len += le16_to_cpu(tlv->len) +
- sizeof(struct mwifiex_ie_types_header);
- left_len -= le16_to_cpu(tlv->len) +
- sizeof(struct mwifiex_ie_types_header);
- }
- }
-
- mwifiex_dbg(adapter, INFO,
- "info: GET_HW_SPEC: fw_release_number- %#x\n",
- adapter->fw_release_number);
- mwifiex_dbg(adapter, INFO,
- "info: GET_HW_SPEC: permanent addr: %pM\n",
- hw_spec->permanent_addr);
- mwifiex_dbg(adapter, INFO,
- "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n",
- le16_to_cpu(hw_spec->hw_if_version),
- le16_to_cpu(hw_spec->version));
-
- ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr);
- adapter->region_code = le16_to_cpu(hw_spec->region_code);
-
- for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++)
- /* Use the region code to search for the index */
- if (adapter->region_code == region_code_index[i])
- break;
-
- /* If it's unidentified region code, use the default (USA) */
- if (i >= MWIFIEX_MAX_REGION_CODE) {
- adapter->region_code = 0x10;
- mwifiex_dbg(adapter, WARN,
- "cmd: unknown region code, use default (USA)\n");
- }
-
- adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap);
- adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support;
- adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support;
-
- if (adapter->if_ops.update_mp_end_port)
- adapter->if_ops.update_mp_end_port(adapter,
- le16_to_cpu(hw_spec->mp_end_port));
-
- if (adapter->fw_api_ver == MWIFIEX_FW_V15)
- adapter->scan_chan_gap_enabled = true;
-
- return 0;
-}
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
deleted file mode 100644
index 241e1c3fbf08..000000000000
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ /dev/null
@@ -1,973 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: debugfs
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 <linux/debugfs.h>
-
-#include "main.h"
-#include "11n.h"
-
-
-static struct dentry *mwifiex_dfs_dir;
-
-static char *bss_modes[] = {
- "UNSPECIFIED",
- "ADHOC",
- "STATION",
- "AP",
- "AP_VLAN",
- "WDS",
- "MONITOR",
- "MESH_POINT",
- "P2P_CLIENT",
- "P2P_GO",
- "P2P_DEVICE",
-};
-
-/*
- * Proc info file read handler.
- *
- * This function is called when the 'info' file is opened for reading.
- * It prints the following driver related information -
- * - Driver name
- * - Driver version
- * - Driver extended version
- * - Interface name
- * - BSS mode
- * - Media state (connected or disconnected)
- * - MAC address
- * - Total number of Tx bytes
- * - Total number of Rx bytes
- * - Total number of Tx packets
- * - Total number of Rx packets
- * - Total number of dropped Tx packets
- * - Total number of dropped Rx packets
- * - Total number of corrupted Tx packets
- * - Total number of corrupted Rx packets
- * - Carrier status (on or off)
- * - Tx queue status (started or stopped)
- *
- * For STA mode drivers, it also prints the following extra -
- * - ESSID
- * - BSSID
- * - Channel
- * - Region code
- * - Multicast count
- * - Multicast addresses
- */
-static ssize_t
-mwifiex_info_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv =
- (struct mwifiex_private *) file->private_data;
- struct net_device *netdev = priv->netdev;
- struct netdev_hw_addr *ha;
- struct netdev_queue *txq;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *) page, fmt[64];
- struct mwifiex_bss_info info;
- ssize_t ret;
- int i = 0;
-
- if (!p)
- return -ENOMEM;
-
- memset(&info, 0, sizeof(info));
- ret = mwifiex_get_bss_info(priv, &info);
- if (ret)
- goto free_and_exit;
-
- mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1);
-
- if (!priv->version_str[0])
- mwifiex_get_ver_ext(priv);
-
- p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
- p += sprintf(p, "driver_version = %s", fmt);
- p += sprintf(p, "\nverext = %s", priv->version_str);
- p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name);
-
- if (info.bss_mode >= ARRAY_SIZE(bss_modes))
- p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode);
- else
- p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]);
-
- p += sprintf(p, "media_state=\"%s\"\n",
- (!priv->media_connected ? "Disconnected" : "Connected"));
- p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr);
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
- p += sprintf(p, "multicast_count=\"%d\"\n",
- netdev_mc_count(netdev));
- p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid);
- p += sprintf(p, "bssid=\"%pM\"\n", info.bssid);
- p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);
- p += sprintf(p, "country_code = \"%s\"\n", info.country_code);
-
- netdev_for_each_mc_addr(ha, netdev)
- p += sprintf(p, "multicast_address[%d]=\"%pM\"\n",
- i++, ha->addr);
- }
-
- p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes);
- p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes);
- p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets);
- p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets);
- p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped);
- p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped);
- p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors);
- p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors);
- p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev))
- ? "on" : "off"));
- p += sprintf(p, "tx queue");
- for (i = 0; i < netdev->num_tx_queues; i++) {
- txq = netdev_get_tx_queue(netdev, i);
- p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ?
- "stopped" : "started");
- }
- p += sprintf(p, "\n");
-
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
- (unsigned long) p - page);
-
-free_and_exit:
- free_page(page);
- return ret;
-}
-
-/*
- * Proc device dump read handler.
- *
- * This function is called when the 'device_dump' file is opened for
- * reading.
- * This function dumps driver information and firmware memory segments
- * (ex. DTCM, ITCM, SQRAM etc.) for
- * debugging.
- */
-static ssize_t
-mwifiex_device_dump_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv = file->private_data;
-
- if (!priv->adapter->if_ops.device_dump)
- return -EIO;
-
- priv->adapter->if_ops.device_dump(priv->adapter);
-
- return 0;
-}
-
-/*
- * Proc getlog file read handler.
- *
- * This function is called when the 'getlog' file is opened for reading
- * It prints the following log information -
- * - Number of multicast Tx frames
- * - Number of failed packets
- * - Number of Tx retries
- * - Number of multicast Tx retries
- * - Number of duplicate frames
- * - Number of RTS successes
- * - Number of RTS failures
- * - Number of ACK failures
- * - Number of fragmented Rx frames
- * - Number of multicast Rx frames
- * - Number of FCS errors
- * - Number of Tx frames
- * - WEP ICV error counts
- * - Number of received beacons
- * - Number of missed beacons
- */
-static ssize_t
-mwifiex_getlog_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv =
- (struct mwifiex_private *) file->private_data;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *) page;
- ssize_t ret;
- struct mwifiex_ds_get_stats stats;
-
- if (!p)
- return -ENOMEM;
-
- memset(&stats, 0, sizeof(stats));
- ret = mwifiex_get_stats_info(priv, &stats);
- if (ret)
- goto free_and_exit;
-
- p += sprintf(p, "\n"
- "mcasttxframe %u\n"
- "failed %u\n"
- "retry %u\n"
- "multiretry %u\n"
- "framedup %u\n"
- "rtssuccess %u\n"
- "rtsfailure %u\n"
- "ackfailure %u\n"
- "rxfrag %u\n"
- "mcastrxframe %u\n"
- "fcserror %u\n"
- "txframe %u\n"
- "wepicverrcnt-1 %u\n"
- "wepicverrcnt-2 %u\n"
- "wepicverrcnt-3 %u\n"
- "wepicverrcnt-4 %u\n"
- "bcn_rcv_cnt %u\n"
- "bcn_miss_cnt %u\n",
- stats.mcast_tx_frame,
- stats.failed,
- stats.retry,
- stats.multi_retry,
- stats.frame_dup,
- stats.rts_success,
- stats.rts_failure,
- stats.ack_failure,
- stats.rx_frag,
- stats.mcast_rx_frame,
- stats.fcs_error,
- stats.tx_frame,
- stats.wep_icv_error[0],
- stats.wep_icv_error[1],
- stats.wep_icv_error[2],
- stats.wep_icv_error[3],
- stats.bcn_rcv_cnt,
- stats.bcn_miss_cnt);
-
-
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
- (unsigned long) p - page);
-
-free_and_exit:
- free_page(page);
- return ret;
-}
-
-/* Sysfs histogram file read handler.
- *
- * This function is called when the 'histogram' file is opened for reading
- * It prints the following histogram information -
- * - Number of histogram samples
- * - Receive packet number of each rx_rate
- * - Receive packet number of each snr
- * - Receive packet number of each nosie_flr
- * - Receive packet number of each signal streath
- */
-static ssize_t
-mwifiex_histogram_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv =
- (struct mwifiex_private *)file->private_data;
- ssize_t ret;
- struct mwifiex_histogram_data *phist_data;
- int i, value;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *)page;
-
- if (!p)
- return -ENOMEM;
-
- if (!priv || !priv->hist_data)
- return -EFAULT;
- phist_data = priv->hist_data;
-
- p += sprintf(p, "\n"
- "total samples = %d\n",
- atomic_read(&phist_data->num_samples));
-
- p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M");
- p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n");
- p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M");
- p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
-
- if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
- p += sprintf(p, "44-53=MCS0-9(VHT:BW20)");
- p += sprintf(p, "54-63=MCS0-9(VHT:BW40)");
- p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n");
- } else {
- p += sprintf(p, "\n");
- }
-
- for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) {
- value = atomic_read(&phist_data->rx_rate[i]);
- if (value)
- p += sprintf(p, "rx_rate[%02d] = %d\n", i, value);
- }
-
- if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
- for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES;
- i++) {
- value = atomic_read(&phist_data->rx_rate[i]);
- if (value)
- p += sprintf(p, "rx_rate[%02d] = %d\n",
- i, value);
- }
- }
-
- for (i = 0; i < MWIFIEX_MAX_SNR; i++) {
- value = atomic_read(&phist_data->snr[i]);
- if (value)
- p += sprintf(p, "snr[%02ddB] = %d\n", i, value);
- }
- for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {
- value = atomic_read(&phist_data->noise_flr[i]);
- if (value)
- p += sprintf(p, "noise_flr[-%02ddBm] = %d\n",
- (int)(i-128), value);
- }
- for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {
- value = atomic_read(&phist_data->sig_str[i]);
- if (value)
- p += sprintf(p, "sig_strength[-%02ddBm] = %d\n",
- i, value);
- }
-
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
-
- return ret;
-}
-
-static ssize_t
-mwifiex_histogram_write(struct file *file, const char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv = (void *)file->private_data;
-
- if (priv && priv->hist_data)
- mwifiex_hist_data_reset(priv);
- return 0;
-}
-
-static struct mwifiex_debug_info info;
-
-/*
- * Proc debug file read handler.
- *
- * This function is called when the 'debug' file is opened for reading
- * It prints the following log information -
- * - Interrupt count
- * - WMM AC VO packets count
- * - WMM AC VI packets count
- * - WMM AC BE packets count
- * - WMM AC BK packets count
- * - Maximum Tx buffer size
- * - Tx buffer size
- * - Current Tx buffer size
- * - Power Save mode
- * - Power Save state
- * - Deep Sleep status
- * - Device wakeup required status
- * - Number of wakeup tries
- * - Host Sleep configured status
- * - Host Sleep activated status
- * - Number of Tx timeouts
- * - Number of command timeouts
- * - Last timed out command ID
- * - Last timed out command action
- * - Last command ID
- * - Last command action
- * - Last command index
- * - Last command response ID
- * - Last command response index
- * - Last event
- * - Last event index
- * - Number of host to card command failures
- * - Number of sleep confirm command failures
- * - Number of host to card data failure
- * - Number of deauthentication events
- * - Number of disassociation events
- * - Number of link lost events
- * - Number of deauthentication commands
- * - Number of association success commands
- * - Number of association failure commands
- * - Number of commands sent
- * - Number of data packets sent
- * - Number of command responses received
- * - Number of events received
- * - Tx BA stream table (TID, RA)
- * - Rx reorder table (TID, TA, Start window, Window size, Buffer)
- */
-static ssize_t
-mwifiex_debug_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv =
- (struct mwifiex_private *) file->private_data;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *) page;
- ssize_t ret;
-
- if (!p)
- return -ENOMEM;
-
- ret = mwifiex_get_debug_info(priv, &info);
- if (ret)
- goto free_and_exit;
-
- p += mwifiex_debug_info_to_buffer(priv, p, &info);
-
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
- (unsigned long) p - page);
-
-free_and_exit:
- free_page(page);
- return ret;
-}
-
-static u32 saved_reg_type, saved_reg_offset, saved_reg_value;
-
-/*
- * Proc regrdwr file write handler.
- *
- * This function is called when the 'regrdwr' file is opened for writing
- *
- * This function can be used to write to a register.
- */
-static ssize_t
-mwifiex_regrdwr_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos)
-{
- char *buf;
- int ret;
- u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
-
- buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
- if (IS_ERR(buf))
- return PTR_ERR(buf);
-
- sscanf(buf, "%u %x %x", &reg_type, &reg_offset, &reg_value);
-
- if (reg_type == 0 || reg_offset == 0) {
- ret = -EINVAL;
- goto done;
- } else {
- saved_reg_type = reg_type;
- saved_reg_offset = reg_offset;
- saved_reg_value = reg_value;
- ret = count;
- }
-done:
- kfree(buf);
- return ret;
-}
-
-/*
- * Proc regrdwr file read handler.
- *
- * This function is called when the 'regrdwr' file is opened for reading
- *
- * This function can be used to read from a register.
- */
-static ssize_t
-mwifiex_regrdwr_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv =
- (struct mwifiex_private *) file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *) addr;
- int pos = 0, ret = 0;
- u32 reg_value;
-
- if (!buf)
- return -ENOMEM;
-
- if (!saved_reg_type) {
- /* No command has been given */
- pos += snprintf(buf, PAGE_SIZE, "0");
- goto done;
- }
- /* Set command has been given */
- if (saved_reg_value != UINT_MAX) {
- ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset,
- saved_reg_value);
-
- pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n",
- saved_reg_type, saved_reg_offset,
- saved_reg_value);
-
- ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
-
- goto done;
- }
- /* Get command has been given */
- ret = mwifiex_reg_read(priv, saved_reg_type,
- saved_reg_offset, &reg_value);
- if (ret) {
- ret = -EINVAL;
- goto done;
- }
-
- pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type,
- saved_reg_offset, reg_value);
-
- ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
-
-done:
- free_page(addr);
- return ret;
-}
-
-/* Proc debug_mask file read handler.
- * This function is called when the 'debug_mask' file is opened for reading
- * This function can be used read driver debugging mask value.
- */
-static ssize_t
-mwifiex_debug_mask_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv =
- (struct mwifiex_private *)file->private_data;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)page;
- size_t ret = 0;
- int pos = 0;
-
- if (!buf)
- return -ENOMEM;
-
- pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n",
- priv->adapter->debug_mask);
- ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
-
- free_page(page);
- return ret;
-}
-
-/* Proc debug_mask file read handler.
- * This function is called when the 'debug_mask' file is opened for reading
- * This function can be used read driver debugging mask value.
- */
-static ssize_t
-mwifiex_debug_mask_write(struct file *file, const char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- int ret;
- unsigned long debug_mask;
- struct mwifiex_private *priv = (void *)file->private_data;
- char *buf;
-
- buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
- if (IS_ERR(buf))
- return PTR_ERR(buf);
-
- if (kstrtoul(buf, 0, &debug_mask)) {
- ret = -EINVAL;
- goto done;
- }
-
- priv->adapter->debug_mask = debug_mask;
- ret = count;
-done:
- kfree(buf);
- return ret;
-}
-
-/* Proc memrw file write handler.
- * This function is called when the 'memrw' file is opened for writing
- * This function can be used to write to a memory location.
- */
-static ssize_t
-mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count,
- loff_t *ppos)
-{
- int ret;
- char cmd;
- struct mwifiex_ds_mem_rw mem_rw;
- u16 cmd_action;
- struct mwifiex_private *priv = (void *)file->private_data;
- char *buf;
-
- buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
- if (IS_ERR(buf))
- return PTR_ERR(buf);
-
- ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value);
- if (ret != 3) {
- ret = -EINVAL;
- goto done;
- }
-
- if ((cmd == 'r') || (cmd == 'R')) {
- cmd_action = HostCmd_ACT_GEN_GET;
- mem_rw.value = 0;
- } else if ((cmd == 'w') || (cmd == 'W')) {
- cmd_action = HostCmd_ACT_GEN_SET;
- } else {
- ret = -EINVAL;
- goto done;
- }
-
- memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw));
- if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0,
- &mem_rw, true))
- ret = -1;
- else
- ret = count;
-
-done:
- kfree(buf);
- return ret;
-}
-
-/* Proc memrw file read handler.
- * This function is called when the 'memrw' file is opened for reading
- * This function can be used to read from a memory location.
- */
-static ssize_t
-mwifiex_memrw_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv = (void *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- int ret, pos = 0;
-
- if (!buf)
- return -ENOMEM;
-
- pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr,
- priv->mem_rw.value);
- ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
-
- free_page(addr);
- return ret;
-}
-
-static u32 saved_offset = -1, saved_bytes = -1;
-
-/*
- * Proc rdeeprom file write handler.
- *
- * This function is called when the 'rdeeprom' file is opened for writing
- *
- * This function can be used to write to a RDEEPROM location.
- */
-static ssize_t
-mwifiex_rdeeprom_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos)
-{
- char *buf;
- int ret = 0;
- int offset = -1, bytes = -1;
-
- buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
- if (IS_ERR(buf))
- return PTR_ERR(buf);
-
- sscanf(buf, "%d %d", &offset, &bytes);
-
- if (offset == -1 || bytes == -1) {
- ret = -EINVAL;
- goto done;
- } else {
- saved_offset = offset;
- saved_bytes = bytes;
- ret = count;
- }
-done:
- kfree(buf);
- return ret;
-}
-
-/*
- * Proc rdeeprom read write handler.
- *
- * This function is called when the 'rdeeprom' file is opened for reading
- *
- * This function can be used to read from a RDEEPROM location.
- */
-static ssize_t
-mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv =
- (struct mwifiex_private *) file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *) addr;
- int pos, ret, i;
- u8 value[MAX_EEPROM_DATA];
-
- if (!buf)
- return -ENOMEM;
-
- if (saved_offset == -1) {
- /* No command has been given */
- pos = snprintf(buf, PAGE_SIZE, "0");
- goto done;
- }
-
- /* Get command has been given */
- ret = mwifiex_eeprom_read(priv, (u16) saved_offset,
- (u16) saved_bytes, value);
- if (ret) {
- ret = -EINVAL;
- goto out_free;
- }
-
- pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
-
- for (i = 0; i < saved_bytes; i++)
- pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]);
-
-done:
- ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
-out_free:
- free_page(addr);
- return ret;
-}
-
-/* Proc hscfg file write handler
- * This function can be used to configure the host sleep parameters.
- */
-static ssize_t
-mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv = (void *)file->private_data;
- char *buf;
- int ret, arg_num;
- struct mwifiex_ds_hs_cfg hscfg;
- int conditions = HS_CFG_COND_DEF;
- u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
-
- buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
- if (IS_ERR(buf))
- return PTR_ERR(buf);
-
- arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
-
- memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
-
- if (arg_num > 3) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Too many arguments\n");
- ret = -EINVAL;
- goto done;
- }
-
- if (arg_num >= 1 && arg_num < 3)
- mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
- MWIFIEX_SYNC_CMD, &hscfg);
-
- if (arg_num) {
- if (conditions == HS_CFG_CANCEL) {
- mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);
- ret = count;
- goto done;
- }
- hscfg.conditions = conditions;
- }
- if (arg_num >= 2)
- hscfg.gpio = gpio;
- if (arg_num == 3)
- hscfg.gap = gap;
-
- hscfg.is_invoke_hostcmd = false;
- mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
- MWIFIEX_SYNC_CMD, &hscfg);
-
- mwifiex_enable_hs(priv->adapter);
- priv->adapter->hs_enabling = false;
- ret = count;
-done:
- kfree(buf);
- return ret;
-}
-
-/* Proc hscfg file read handler
- * This function can be used to read host sleep configuration
- * parameters from driver.
- */
-static ssize_t
-mwifiex_hscfg_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv = (void *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- int pos, ret;
- struct mwifiex_ds_hs_cfg hscfg;
-
- if (!buf)
- return -ENOMEM;
-
- mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
- MWIFIEX_SYNC_CMD, &hscfg);
-
- pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,
- hscfg.gpio, hscfg.gap);
-
- ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
-
- free_page(addr);
- return ret;
-}
-
-static ssize_t
-mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct mwifiex_private *priv = file->private_data;
- char buf[3];
- bool timeshare_coex;
- int ret;
- unsigned int len;
-
- if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
- return -EOPNOTSUPP;
-
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
- HostCmd_ACT_GEN_GET, 0, &timeshare_coex, true);
- if (ret)
- return ret;
-
- len = sprintf(buf, "%d\n", timeshare_coex);
- return simple_read_from_buffer(ubuf, count, ppos, buf, len);
-}
-
-static ssize_t
-mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- bool timeshare_coex;
- struct mwifiex_private *priv = file->private_data;
- char kbuf[16];
- int ret;
-
- if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
- return -EOPNOTSUPP;
-
- memset(kbuf, 0, sizeof(kbuf));
-
- if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count)))
- return -EFAULT;
-
- if (strtobool(kbuf, &timeshare_coex))
- return -EINVAL;
-
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
- HostCmd_ACT_GEN_SET, 0, &timeshare_coex, true);
- if (ret)
- return ret;
- else
- return count;
-}
-
-#define MWIFIEX_DFS_ADD_FILE(name) do { \
- if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \
- priv, &mwifiex_dfs_##name##_fops)) \
- return; \
-} while (0);
-
-#define MWIFIEX_DFS_FILE_OPS(name) \
-static const struct file_operations mwifiex_dfs_##name##_fops = { \
- .read = mwifiex_##name##_read, \
- .write = mwifiex_##name##_write, \
- .open = simple_open, \
-};
-
-#define MWIFIEX_DFS_FILE_READ_OPS(name) \
-static const struct file_operations mwifiex_dfs_##name##_fops = { \
- .read = mwifiex_##name##_read, \
- .open = simple_open, \
-};
-
-#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \
-static const struct file_operations mwifiex_dfs_##name##_fops = { \
- .write = mwifiex_##name##_write, \
- .open = simple_open, \
-};
-
-
-MWIFIEX_DFS_FILE_READ_OPS(info);
-MWIFIEX_DFS_FILE_READ_OPS(debug);
-MWIFIEX_DFS_FILE_READ_OPS(getlog);
-MWIFIEX_DFS_FILE_READ_OPS(device_dump);
-MWIFIEX_DFS_FILE_OPS(regrdwr);
-MWIFIEX_DFS_FILE_OPS(rdeeprom);
-MWIFIEX_DFS_FILE_OPS(memrw);
-MWIFIEX_DFS_FILE_OPS(hscfg);
-MWIFIEX_DFS_FILE_OPS(histogram);
-MWIFIEX_DFS_FILE_OPS(debug_mask);
-MWIFIEX_DFS_FILE_OPS(timeshare_coex);
-
-/*
- * This function creates the debug FS directory structure and the files.
- */
-void
-mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
-{
- if (!mwifiex_dfs_dir || !priv)
- return;
-
- priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name,
- mwifiex_dfs_dir);
-
- if (!priv->dfs_dev_dir)
- return;
-
- MWIFIEX_DFS_ADD_FILE(info);
- MWIFIEX_DFS_ADD_FILE(debug);
- MWIFIEX_DFS_ADD_FILE(getlog);
- MWIFIEX_DFS_ADD_FILE(regrdwr);
- MWIFIEX_DFS_ADD_FILE(rdeeprom);
- MWIFIEX_DFS_ADD_FILE(device_dump);
- MWIFIEX_DFS_ADD_FILE(memrw);
- MWIFIEX_DFS_ADD_FILE(hscfg);
- MWIFIEX_DFS_ADD_FILE(histogram);
- MWIFIEX_DFS_ADD_FILE(debug_mask);
- MWIFIEX_DFS_ADD_FILE(timeshare_coex);
-}
-
-/*
- * This function removes the debug FS directory structure and the files.
- */
-void
-mwifiex_dev_debugfs_remove(struct mwifiex_private *priv)
-{
- if (!priv)
- return;
-
- debugfs_remove_recursive(priv->dfs_dev_dir);
-}
-
-/*
- * This function creates the top level proc directory.
- */
-void
-mwifiex_debugfs_init(void)
-{
- if (!mwifiex_dfs_dir)
- mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL);
-}
-
-/*
- * This function removes the top level proc directory.
- */
-void
-mwifiex_debugfs_remove(void)
-{
- if (mwifiex_dfs_dir)
- debugfs_remove(mwifiex_dfs_dir);
-}
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
deleted file mode 100644
index 098e1f14dc9a..000000000000
--- a/drivers/net/wireless/mwifiex/decl.h
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: generic data structures and APIs
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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.
- */
-
-#ifndef _MWIFIEX_DECL_H_
-#define _MWIFIEX_DECL_H_
-
-#undef pr_fmt
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/wait.h>
-#include <linux/timer.h>
-#include <linux/ieee80211.h>
-#include <uapi/linux/if_arp.h>
-#include <net/mac80211.h>
-
-#define MWIFIEX_BSS_COEX_COUNT 2
-#define MWIFIEX_MAX_BSS_NUM (3)
-
-#define MWIFIEX_DMA_ALIGN_SZ 64
-#define MWIFIEX_RX_HEADROOM 64
-#define MAX_TXPD_SZ 32
-#define INTF_HDR_ALIGN 4
-
-#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \
- MAX_TXPD_SZ)
-#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type)
- * + sizeof(tx_control)
- */
-
-#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2
-#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16
-#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8
-
-#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64
-#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64
-#define MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE 16
-
-#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32
-
-#define MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE 16
-
-#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16
-#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64
-#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64
-#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 64
-#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 64
-
-#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff
-
-#define MWIFIEX_RATE_BITMAP_MCS0 32
-
-#define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024)
-#define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024)
-
-#define MAX_BEACON_PERIOD (4000)
-#define MIN_BEACON_PERIOD (50)
-#define MAX_DTIM_PERIOD (100)
-#define MIN_DTIM_PERIOD (1)
-
-#define MWIFIEX_RTS_MIN_VALUE (0)
-#define MWIFIEX_RTS_MAX_VALUE (2347)
-#define MWIFIEX_FRAG_MIN_VALUE (256)
-#define MWIFIEX_FRAG_MAX_VALUE (2346)
-#define MWIFIEX_WMM_VERSION 0x01
-#define MWIFIEX_WMM_SUBTYPE 0x01
-
-#define MWIFIEX_RETRY_LIMIT 14
-#define MWIFIEX_SDIO_BLOCK_SIZE 256
-
-#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
-#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
-#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
-#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3)
-#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4)
-#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5)
-
-#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
-#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
-
-#define MWIFIEX_TDLS_DISABLE_LINK 0x00
-#define MWIFIEX_TDLS_ENABLE_LINK 0x01
-#define MWIFIEX_TDLS_CREATE_LINK 0x02
-#define MWIFIEX_TDLS_CONFIG_LINK 0x03
-
-#define MWIFIEX_TDLS_RSSI_HIGH 50
-#define MWIFIEX_TDLS_RSSI_LOW 55
-#define MWIFIEX_TDLS_MAX_FAIL_COUNT 4
-#define MWIFIEX_AUTO_TDLS_IDLE_TIME 10
-
-/* 54M rates, index from 0 to 11 */
-#define MWIFIEX_RATE_INDEX_MCS0 12
-/* 12-27=MCS0-15(BW20) */
-#define MWIFIEX_BW20_MCS_NUM 15
-
-/* Rate index for OFDM 0 */
-#define MWIFIEX_RATE_INDEX_OFDM0 4
-
-#define MWIFIEX_MAX_STA_NUM 1
-#define MWIFIEX_MAX_UAP_NUM 1
-#define MWIFIEX_MAX_P2P_NUM 1
-
-#define MWIFIEX_A_BAND_START_FREQ 5000
-
-/* SDIO Aggr data packet special info */
-#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255)
-#define BLOCK_NUMBER_OFFSET 15
-#define SDIO_HEADER_OFFSET 28
-
-enum mwifiex_bss_type {
- MWIFIEX_BSS_TYPE_STA = 0,
- MWIFIEX_BSS_TYPE_UAP = 1,
- MWIFIEX_BSS_TYPE_P2P = 2,
- MWIFIEX_BSS_TYPE_ANY = 0xff,
-};
-
-enum mwifiex_bss_role {
- MWIFIEX_BSS_ROLE_STA = 0,
- MWIFIEX_BSS_ROLE_UAP = 1,
- MWIFIEX_BSS_ROLE_ANY = 0xff,
-};
-
-enum mwifiex_tdls_status {
- TDLS_NOT_SETUP = 0,
- TDLS_SETUP_INPROGRESS,
- TDLS_SETUP_COMPLETE,
- TDLS_SETUP_FAILURE,
- TDLS_LINK_TEARDOWN,
- TDLS_CHAN_SWITCHING,
- TDLS_IN_BASE_CHAN,
- TDLS_IN_OFF_CHAN,
-};
-
-enum mwifiex_tdls_error_code {
- TDLS_ERR_NO_ERROR = 0,
- TDLS_ERR_INTERNAL_ERROR,
- TDLS_ERR_MAX_LINKS_EST,
- TDLS_ERR_LINK_EXISTS,
- TDLS_ERR_LINK_NONEXISTENT,
- TDLS_ERR_PEER_STA_UNREACHABLE = 25,
-};
-
-#define BSS_ROLE_BIT_MASK BIT(0)
-
-#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK)
-
-enum mwifiex_data_frame_type {
- MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0,
- MWIFIEX_DATA_FRAME_TYPE_802_11,
-};
-
-struct mwifiex_fw_image {
- u8 *helper_buf;
- u32 helper_len;
- u8 *fw_buf;
- u32 fw_len;
-};
-
-struct mwifiex_802_11_ssid {
- u32 ssid_len;
- u8 ssid[IEEE80211_MAX_SSID_LEN];
-};
-
-struct mwifiex_wait_queue {
- wait_queue_head_t wait;
- int status;
-};
-
-struct mwifiex_rxinfo {
- struct sk_buff *parent;
- u8 bss_num;
- u8 bss_type;
- u8 use_count;
- u8 buf_type;
-};
-
-struct mwifiex_txinfo {
- u32 status_code;
- u8 flags;
- u8 bss_num;
- u8 bss_type;
- u8 aggr_num;
- u32 pkt_len;
- u8 ack_frame_id;
- u64 cookie;
-};
-
-enum mwifiex_wmm_ac_e {
- WMM_AC_BK,
- WMM_AC_BE,
- WMM_AC_VI,
- WMM_AC_VO
-} __packed;
-
-struct ieee_types_wmm_ac_parameters {
- u8 aci_aifsn_bitmap;
- u8 ecw_bitmap;
- __le16 tx_op_limit;
-} __packed;
-
-struct mwifiex_types_wmm_info {
- u8 oui[4];
- u8 subtype;
- u8 version;
- u8 qos_info;
- u8 reserved;
- struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
-} __packed;
-
-struct mwifiex_arp_eth_header {
- struct arphdr hdr;
- u8 ar_sha[ETH_ALEN];
- u8 ar_sip[4];
- u8 ar_tha[ETH_ALEN];
- u8 ar_tip[4];
-} __packed;
-
-struct mwifiex_chan_stats {
- u8 chan_num;
- u8 bandcfg;
- u8 flags;
- s8 noise;
- u16 total_bss;
- u16 cca_scan_dur;
- u16 cca_busy_dur;
-} __packed;
-
-#define MWIFIEX_HIST_MAX_SAMPLES 1048576
-#define MWIFIEX_MAX_RX_RATES 44
-#define MWIFIEX_MAX_AC_RX_RATES 74
-#define MWIFIEX_MAX_SNR 256
-#define MWIFIEX_MAX_NOISE_FLR 256
-#define MWIFIEX_MAX_SIG_STRENGTH 256
-
-struct mwifiex_histogram_data {
- atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES];
- atomic_t snr[MWIFIEX_MAX_SNR];
- atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR];
- atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH];
- atomic_t num_samples;
-};
-
-struct mwifiex_iface_comb {
- u8 sta_intf;
- u8 uap_intf;
- u8 p2p_intf;
-};
-
-struct mwifiex_radar_params {
- struct cfg80211_chan_def *chandef;
- u32 cac_time_ms;
-} __packed;
-
-struct mwifiex_11h_intf_state {
- bool is_11h_enabled;
- bool is_11h_active;
-} __packed;
-#endif /* !_MWIFIEX_DECL_H_ */
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
deleted file mode 100644
index 1e1e81a0a8d4..000000000000
--- a/drivers/net/wireless/mwifiex/fw.h
+++ /dev/null
@@ -1,2177 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: Firmware specific macros & structures
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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.
- */
-
-#ifndef _MWIFIEX_FW_H_
-#define _MWIFIEX_FW_H_
-
-#include <linux/if_ether.h>
-
-
-#define INTF_HEADER_LEN 4
-
-struct rfc_1042_hdr {
- u8 llc_dsap;
- u8 llc_ssap;
- u8 llc_ctrl;
- u8 snap_oui[3];
- __be16 snap_type;
-};
-
-struct rx_packet_hdr {
- struct ethhdr eth803_hdr;
- struct rfc_1042_hdr rfc1042_hdr;
-};
-
-struct tx_packet_hdr {
- struct ethhdr eth803_hdr;
- struct rfc_1042_hdr rfc1042_hdr;
-};
-
-#define B_SUPPORTED_RATES 5
-#define G_SUPPORTED_RATES 9
-#define BG_SUPPORTED_RATES 13
-#define A_SUPPORTED_RATES 9
-#define HOSTCMD_SUPPORTED_RATES 14
-#define N_SUPPORTED_RATES 3
-#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \
- BAND_AN | BAND_AAC)
-
-#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \
- BIT(13))
-#define IS_SUPPORT_MULTI_BANDS(adapter) \
- (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT)
-
-/* bit 13: 11ac BAND_AAC
- * bit 12: reserved for lab testing, will be reused for BAND_AN
- * bit 11: 11n BAND_GN
- * bit 10: 11a BAND_A
- * bit 9: 11g BAND_G
- * bit 8: 11b BAND_B
- * Map these bits to band capability by right shifting 8 bits.
- */
-#define GET_FW_DEFAULT_BANDS(adapter) \
- (((adapter->fw_cap_info & 0x2f00) >> 8) & \
- ALL_802_11_BANDS)
-
-#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff
-
-#define KEY_INFO_ENABLED 0x01
-enum KEY_TYPE_ID {
- KEY_TYPE_ID_WEP = 0,
- KEY_TYPE_ID_TKIP,
- KEY_TYPE_ID_AES,
- KEY_TYPE_ID_WAPI,
- KEY_TYPE_ID_AES_CMAC,
-};
-
-#define WPA_PN_SIZE 8
-#define KEY_PARAMS_FIXED_LEN 10
-#define KEY_INDEX_MASK 0xf
-#define KEY_API_VER_MAJOR_V2 2
-
-#define KEY_MCAST BIT(0)
-#define KEY_UNICAST BIT(1)
-#define KEY_ENABLED BIT(2)
-#define KEY_DEFAULT BIT(3)
-#define KEY_TX_KEY BIT(4)
-#define KEY_RX_KEY BIT(5)
-#define KEY_IGTK BIT(10)
-
-#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2)
-
-#define MAX_POLL_TRIES 100
-#define MAX_FIRMWARE_POLL_TRIES 100
-
-#define FIRMWARE_READY_SDIO 0xfedc
-#define FIRMWARE_READY_PCIE 0xfedcba00
-
-#define MWIFIEX_COEX_MODE_TIMESHARE 0x01
-#define MWIFIEX_COEX_MODE_SPATIAL 0x82
-
-enum mwifiex_usb_ep {
- MWIFIEX_USB_EP_CMD_EVENT = 1,
- MWIFIEX_USB_EP_DATA = 2,
- MWIFIEX_USB_EP_DATA_CH2 = 3,
-};
-
-enum MWIFIEX_802_11_PRIVACY_FILTER {
- MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL,
- MWIFIEX_802_11_PRIV_FILTER_8021X_WEP
-};
-
-#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF)))
-#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR)+(s16)(NF)))
-
-#define UAP_BSS_PARAMS_I 0
-#define UAP_CUSTOM_IE_I 1
-#define MWIFIEX_AUTO_IDX_MASK 0xffff
-#define MWIFIEX_DELETE_MASK 0x0000
-#define MGMT_MASK_ASSOC_REQ 0x01
-#define MGMT_MASK_REASSOC_REQ 0x04
-#define MGMT_MASK_ASSOC_RESP 0x02
-#define MGMT_MASK_REASSOC_RESP 0x08
-#define MGMT_MASK_PROBE_REQ 0x10
-#define MGMT_MASK_PROBE_RESP 0x20
-#define MGMT_MASK_BEACON 0x100
-
-#define TLV_TYPE_UAP_SSID 0x0000
-#define TLV_TYPE_UAP_RATES 0x0001
-#define TLV_TYPE_PWR_CONSTRAINT 0x0020
-
-#define PROPRIETARY_TLV_BASE_ID 0x0100
-#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0)
-#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1)
-#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2)
-#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4)
-#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10)
-#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16)
-#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18)
-#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19)
-#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22)
-#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31)
-#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32)
-#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35)
-#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42)
-#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44)
-#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45)
-#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48)
-#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51)
-#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57)
-#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59)
-#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60)
-#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64)
-#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65)
-#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70)
-#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82)
-#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83)
-#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84)
-#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86)
-#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87)
-#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91)
-#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93)
-#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94)
-#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96)
-#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104)
-#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105)
-#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113)
-#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114)
-#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123)
-#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145)
-#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146)
-#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148)
-#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154)
-#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156)
-#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183)
-#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184)
-#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194)
-#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197)
-#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199)
-#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198)
-#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202)
-#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203)
-#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206)
-
-#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048
-
-#define SSN_MASK 0xfff0
-
-#define BA_RESULT_SUCCESS 0x0
-#define BA_RESULT_TIMEOUT 0x2
-
-#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status)
-
-#define BA_STREAM_NOT_ALLOWED 0xff
-
-#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \
- priv->adapter->config_bands & BAND_AN) && \
- priv->curr_bss_params.bss_descriptor.bcn_ht_cap)
-#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\
- BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS)
-
-#define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096
-#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192
-
-#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
-#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
-#define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15))
-#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16))
-
-#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \
- (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
- IEEE80211_HT_CAP_SM_PS)
-
-#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008
-
-#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR
-
-#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC)
-#define MWIFIEX_RX_STBC1 0x0100
-#define MWIFIEX_RX_STBC12 0x0200
-#define MWIFIEX_RX_STBC123 0x0300
-
-/* dev_cap bitmap
- * BIT
- * 0-16 reserved
- * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40
- * 18-22 reserved
- * 23 IEEE80211_HT_CAP_SGI_20
- * 24 IEEE80211_HT_CAP_SGI_40
- * 25 IEEE80211_HT_CAP_TX_STBC
- * 26 IEEE80211_HT_CAP_RX_STBC
- * 27-28 reserved
- * 29 IEEE80211_HT_CAP_GRN_FLD
- * 30-31 reserved
- */
-#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17))
-#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23))
-#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24))
-#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25))
-#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26))
-#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29))
-#define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8))
-#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
-#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
-#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2))
-#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF)
-
-/* httxcfg bitmap
- * 0 reserved
- * 1 20/40 Mhz enable(1)/disable(0)
- * 2-3 reserved
- * 4 green field enable(1)/disable(0)
- * 5 short GI in 20 Mhz enable(1)/disable(0)
- * 6 short GI in 40 Mhz enable(1)/disable(0)
- * 7-15 reserved
- */
-#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6))
-
-/* 11AC Tx and Rx MCS map for 1x1 mode:
- * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1
- * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams
- */
-#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe
-
-/* 11AC Tx and Rx MCS map for 2x2 mode:
- * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2
- * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams
- */
-#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa
-
-#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f)
-#define SETHT_MCS32(x) (x[4] |= 1)
-#define HT_STREAM_1X1 0x11
-#define HT_STREAM_2X2 0x22
-
-#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4))
-
-#define LLC_SNAP_LEN 8
-
-/* HW_SPEC fw_cap_info */
-
-#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13))
-
-#define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3)
-#define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3)
-#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \
- (2 * (nss - 1)))
-#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16)
-#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF)
-
-/* Clear SU Beanformer, MU beanformer, MU beanformee and
- * sounding dimensions bits
- */
-#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \
- (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \
- IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \
- IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \
- IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK)
-
-#define MOD_CLASS_HR_DSSS 0x03
-#define MOD_CLASS_OFDM 0x07
-#define MOD_CLASS_HT 0x08
-#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
-#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010
-#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059
-#define HostCmd_CMD_802_11_ASSOCIATE 0x0012
-#define HostCmd_CMD_802_11_SNMP_MIB 0x0016
-#define HostCmd_CMD_MAC_REG_ACCESS 0x0019
-#define HostCmd_CMD_BBP_REG_ACCESS 0x001a
-#define HostCmd_CMD_RF_REG_ACCESS 0x001b
-#define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad
-#define HostCmd_CMD_RF_TX_PWR 0x001e
-#define HostCmd_CMD_RF_ANTENNA 0x0020
-#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024
-#define HostCmd_CMD_MAC_CONTROL 0x0028
-#define HostCmd_CMD_802_11_AD_HOC_START 0x002b
-#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c
-#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040
-#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D
-#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b
-#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e
-#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c
-#define HostCmd_CMD_WMM_GET_STATUS 0x0071
-#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075
-#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f
-#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083
-#define HostCmd_CMD_MEM_ACCESS 0x0086
-#define HostCmd_CMD_CFG_DATA 0x008f
-#define HostCmd_CMD_VERSION_EXT 0x0097
-#define HostCmd_CMD_MEF_CFG 0x009a
-#define HostCmd_CMD_RSSI_INFO 0x00a4
-#define HostCmd_CMD_FUNC_INIT 0x00a9
-#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa
-#define HOST_CMD_APCMD_SYS_RESET 0x00af
-#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0
-#define HostCmd_CMD_UAP_BSS_START 0x00b1
-#define HostCmd_CMD_UAP_BSS_STOP 0x00b2
-#define HOST_CMD_APCMD_STA_LIST 0x00b3
-#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5
-#define HostCmd_CMD_11N_CFG 0x00cd
-#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce
-#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf
-#define HostCmd_CMD_11N_DELBA 0x00d0
-#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9
-#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd
-#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df
-#define HostCmd_CMD_TXPWR_CFG 0x00d1
-#define HostCmd_CMD_TX_RATE_CFG 0x00d6
-#define HostCmd_CMD_ROBUST_COEX 0x00e0
-#define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4
-#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5
-#define HostCmd_CMD_P2P_MODE_CFG 0x00eb
-#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed
-#define HostCmd_CMD_SET_BSS_MODE 0x00f7
-#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa
-#define HostCmd_CMD_802_11_SCAN_EXT 0x0107
-#define HostCmd_CMD_COALESCE_CFG 0x010a
-#define HostCmd_CMD_MGMT_FRAME_REG 0x010c
-#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d
-#define HostCmd_CMD_11AC_CFG 0x0112
-#define HostCmd_CMD_TDLS_CONFIG 0x0100
-#define HostCmd_CMD_MC_POLICY 0x0121
-#define HostCmd_CMD_TDLS_OPER 0x0122
-#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223
-
-#define PROTOCOL_NO_SECURITY 0x01
-#define PROTOCOL_STATIC_WEP 0x02
-#define PROTOCOL_WPA 0x08
-#define PROTOCOL_WPA2 0x20
-#define PROTOCOL_WPA2_MIXED 0x28
-#define PROTOCOL_EAP 0x40
-#define KEY_MGMT_NONE 0x04
-#define KEY_MGMT_PSK 0x02
-#define KEY_MGMT_EAP 0x01
-#define CIPHER_TKIP 0x04
-#define CIPHER_AES_CCMP 0x08
-#define VALID_CIPHER_BITMAP 0x0c
-
-enum ENH_PS_MODES {
- EN_PS = 1,
- DIS_PS = 2,
- EN_AUTO_DS = 3,
- DIS_AUTO_DS = 4,
- SLEEP_CONFIRM = 5,
- GET_PS = 0,
- EN_AUTO_PS = 0xff,
- DIS_AUTO_PS = 0xfe,
-};
-
-enum P2P_MODES {
- P2P_MODE_DISABLE = 0,
- P2P_MODE_DEVICE = 1,
- P2P_MODE_GO = 2,
- P2P_MODE_CLIENT = 3,
-};
-
-#define HostCmd_RET_BIT 0x8000
-#define HostCmd_ACT_GEN_GET 0x0000
-#define HostCmd_ACT_GEN_SET 0x0001
-#define HostCmd_ACT_GEN_REMOVE 0x0004
-#define HostCmd_ACT_BITWISE_SET 0x0002
-#define HostCmd_ACT_BITWISE_CLR 0x0003
-#define HostCmd_RESULT_OK 0x0000
-
-#define HostCmd_ACT_MAC_RX_ON 0x0001
-#define HostCmd_ACT_MAC_TX_ON 0x0002
-#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008
-#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010
-#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080
-#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100
-#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000
-
-#define HostCmd_BSS_MODE_IBSS 0x0002
-#define HostCmd_BSS_MODE_ANY 0x0003
-
-#define HostCmd_SCAN_RADIO_TYPE_BG 0
-#define HostCmd_SCAN_RADIO_TYPE_A 1
-
-#define HS_CFG_CANCEL 0xffffffff
-#define HS_CFG_COND_DEF 0x00000000
-#define HS_CFG_GPIO_DEF 0xff
-#define HS_CFG_GAP_DEF 0xff
-#define HS_CFG_COND_BROADCAST_DATA 0x00000001
-#define HS_CFG_COND_UNICAST_DATA 0x00000002
-#define HS_CFG_COND_MAC_EVENT 0x00000004
-#define HS_CFG_COND_MULTICAST_DATA 0x00000008
-
-#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB
-#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC
-#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD
-#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE
-#define CONNECT_ERR_STA_FAILURE 0xFFFF
-
-
-#define CMD_F_HOSTCMD (1 << 0)
-
-#define HostCmd_CMD_ID_MASK 0x0fff
-
-#define HostCmd_SEQ_NUM_MASK 0x00ff
-
-#define HostCmd_BSS_NUM_MASK 0x0f00
-
-#define HostCmd_BSS_TYPE_MASK 0xf000
-
-#define HostCmd_ACT_SET_RX 0x0001
-#define HostCmd_ACT_SET_TX 0x0002
-#define HostCmd_ACT_SET_BOTH 0x0003
-
-#define RF_ANTENNA_AUTO 0xFFFF
-
-#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \
- (((seq) & 0x00ff) | \
- (((num) & 0x000f) << 8)) | \
- (((type) & 0x000f) << 12); }
-
-#define HostCmd_GET_SEQ_NO(seq) \
- ((seq) & HostCmd_SEQ_NUM_MASK)
-
-#define HostCmd_GET_BSS_NO(seq) \
- (((seq) & HostCmd_BSS_NUM_MASK) >> 8)
-
-#define HostCmd_GET_BSS_TYPE(seq) \
- (((seq) & HostCmd_BSS_TYPE_MASK) >> 12)
-
-#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001
-#define EVENT_LINK_LOST 0x00000003
-#define EVENT_LINK_SENSED 0x00000004
-#define EVENT_MIB_CHANGED 0x00000006
-#define EVENT_INIT_DONE 0x00000007
-#define EVENT_DEAUTHENTICATED 0x00000008
-#define EVENT_DISASSOCIATED 0x00000009
-#define EVENT_PS_AWAKE 0x0000000a
-#define EVENT_PS_SLEEP 0x0000000b
-#define EVENT_MIC_ERR_MULTICAST 0x0000000d
-#define EVENT_MIC_ERR_UNICAST 0x0000000e
-#define EVENT_DEEP_SLEEP_AWAKE 0x00000010
-#define EVENT_ADHOC_BCN_LOST 0x00000011
-
-#define EVENT_WMM_STATUS_CHANGE 0x00000017
-#define EVENT_BG_SCAN_REPORT 0x00000018
-#define EVENT_RSSI_LOW 0x00000019
-#define EVENT_SNR_LOW 0x0000001a
-#define EVENT_MAX_FAIL 0x0000001b
-#define EVENT_RSSI_HIGH 0x0000001c
-#define EVENT_SNR_HIGH 0x0000001d
-#define EVENT_IBSS_COALESCED 0x0000001e
-#define EVENT_DATA_RSSI_LOW 0x00000024
-#define EVENT_DATA_SNR_LOW 0x00000025
-#define EVENT_DATA_RSSI_HIGH 0x00000026
-#define EVENT_DATA_SNR_HIGH 0x00000027
-#define EVENT_LINK_QUALITY 0x00000028
-#define EVENT_PORT_RELEASE 0x0000002b
-#define EVENT_UAP_STA_DEAUTH 0x0000002c
-#define EVENT_UAP_STA_ASSOC 0x0000002d
-#define EVENT_UAP_BSS_START 0x0000002e
-#define EVENT_PRE_BEACON_LOST 0x00000031
-#define EVENT_ADDBA 0x00000033
-#define EVENT_DELBA 0x00000034
-#define EVENT_BA_STREAM_TIEMOUT 0x00000037
-#define EVENT_AMSDU_AGGR_CTRL 0x00000042
-#define EVENT_UAP_BSS_IDLE 0x00000043
-#define EVENT_UAP_BSS_ACTIVE 0x00000044
-#define EVENT_WEP_ICV_ERR 0x00000046
-#define EVENT_HS_ACT_REQ 0x00000047
-#define EVENT_BW_CHANGE 0x00000048
-#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c
-#define EVENT_HOSTWAKE_STAIE 0x0000004d
-#define EVENT_CHANNEL_SWITCH_ANN 0x00000050
-#define EVENT_TDLS_GENERIC_EVENT 0x00000052
-#define EVENT_RADAR_DETECTED 0x00000053
-#define EVENT_CHANNEL_REPORT_RDY 0x00000054
-#define EVENT_TX_DATA_PAUSE 0x00000055
-#define EVENT_EXT_SCAN_REPORT 0x00000058
-#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f
-#define EVENT_MULTI_CHAN_INFO 0x0000006a
-#define EVENT_TX_STATUS_REPORT 0x00000074
-#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076
-
-#define EVENT_ID_MASK 0xffff
-#define BSS_NUM_MASK 0xf
-
-#define EVENT_GET_BSS_NUM(event_cause) \
- (((event_cause) >> 16) & BSS_NUM_MASK)
-
-#define EVENT_GET_BSS_TYPE(event_cause) \
- (((event_cause) >> 24) & 0x00ff)
-
-#define MWIFIEX_MAX_PATTERN_LEN 20
-#define MWIFIEX_MAX_OFFSET_LEN 100
-#define STACK_NBYTES 100
-#define TYPE_DNUM 1
-#define TYPE_BYTESEQ 2
-#define MAX_OPERAND 0x40
-#define TYPE_EQ (MAX_OPERAND+1)
-#define TYPE_EQ_DNUM (MAX_OPERAND+2)
-#define TYPE_EQ_BIT (MAX_OPERAND+3)
-#define TYPE_AND (MAX_OPERAND+4)
-#define TYPE_OR (MAX_OPERAND+5)
-#define MEF_MODE_HOST_SLEEP 1
-#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3
-#define MEF_ACTION_AUTO_ARP 0x10
-#define MWIFIEX_CRITERIA_BROADCAST BIT(0)
-#define MWIFIEX_CRITERIA_UNICAST BIT(1)
-#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
-#define MWIFIEX_MAX_SUPPORTED_IPADDR 4
-
-#define ACT_TDLS_DELETE 0x00
-#define ACT_TDLS_CREATE 0x01
-#define ACT_TDLS_CONFIG 0x02
-
-#define TDLS_EVENT_LINK_TEAR_DOWN 3
-#define TDLS_EVENT_CHAN_SWITCH_RESULT 7
-#define TDLS_EVENT_START_CHAN_SWITCH 8
-#define TDLS_EVENT_CHAN_SWITCH_STOPPED 9
-
-#define TDLS_BASE_CHANNEL 0
-#define TDLS_OFF_CHANNEL 1
-
-#define ACT_TDLS_CS_ENABLE_CONFIG 0x00
-#define ACT_TDLS_CS_INIT 0x06
-#define ACT_TDLS_CS_STOP 0x07
-#define ACT_TDLS_CS_PARAMS 0x08
-
-#define MWIFIEX_DEF_CS_UNIT_TIME 2
-#define MWIFIEX_DEF_CS_THR_OTHERLINK 10
-#define MWIFIEX_DEF_THR_DIRECTLINK 0
-#define MWIFIEX_DEF_CS_TIME 10
-#define MWIFIEX_DEF_CS_TIMEOUT 16
-#define MWIFIEX_DEF_CS_REG_CLASS 12
-#define MWIFIEX_DEF_CS_PERIODICITY 1
-
-#define MWIFIEX_FW_V15 15
-
-#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1)
-
-struct mwifiex_ie_types_header {
- __le16 type;
- __le16 len;
-} __packed;
-
-struct mwifiex_ie_types_data {
- struct mwifiex_ie_types_header header;
- u8 data[1];
-} __packed;
-
-#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
-#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
-#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10
-#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01
-#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20
-
-struct txpd {
- u8 bss_type;
- u8 bss_num;
- __le16 tx_pkt_length;
- __le16 tx_pkt_offset;
- __le16 tx_pkt_type;
- __le32 tx_control;
- u8 priority;
- u8 flags;
- u8 pkt_delay_2ms;
- u8 reserved1[2];
- u8 tx_token_id;
- u8 reserved[2];
-} __packed;
-
-struct rxpd {
- u8 bss_type;
- u8 bss_num;
- __le16 rx_pkt_length;
- __le16 rx_pkt_offset;
- __le16 rx_pkt_type;
- __le16 seq_num;
- u8 priority;
- u8 rx_rate;
- s8 snr;
- s8 nf;
-
- /* For: Non-802.11 AC cards
- *
- * Ht Info [Bit 0] RxRate format: LG=0, HT=1
- * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1
- * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1
- *
- * For: 802.11 AC cards
- * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10
- * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01
- * BW80 = 10 BW160 = 11
- * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1
- * [Bit 5] STBC support Enabled = 1
- * [Bit 6] LDPC support Enabled = 1
- * [Bit 7] Reserved
- */
- u8 ht_info;
- u8 reserved[3];
- u8 flags;
-} __packed;
-
-struct uap_txpd {
- u8 bss_type;
- u8 bss_num;
- __le16 tx_pkt_length;
- __le16 tx_pkt_offset;
- __le16 tx_pkt_type;
- __le32 tx_control;
- u8 priority;
- u8 flags;
- u8 pkt_delay_2ms;
- u8 reserved1[2];
- u8 tx_token_id;
- u8 reserved[2];
-};
-
-struct uap_rxpd {
- u8 bss_type;
- u8 bss_num;
- __le16 rx_pkt_length;
- __le16 rx_pkt_offset;
- __le16 rx_pkt_type;
- __le16 seq_num;
- u8 priority;
- u8 rx_rate;
- s8 snr;
- s8 nf;
- u8 ht_info;
- u8 reserved[3];
- u8 flags;
-};
-
-struct mwifiex_fw_chan_stats {
- u8 chan_num;
- u8 bandcfg;
- u8 flags;
- s8 noise;
- __le16 total_bss;
- __le16 cca_scan_dur;
- __le16 cca_busy_dur;
-} __packed;
-
-enum mwifiex_chan_scan_mode_bitmasks {
- MWIFIEX_PASSIVE_SCAN = BIT(0),
- MWIFIEX_DISABLE_CHAN_FILT = BIT(1),
- MWIFIEX_HIDDEN_SSID_REPORT = BIT(4),
-};
-
-struct mwifiex_chan_scan_param_set {
- u8 radio_type;
- u8 chan_number;
- u8 chan_scan_mode_bitmap;
- __le16 min_scan_time;
- __le16 max_scan_time;
-} __packed;
-
-struct mwifiex_ie_types_chan_list_param_set {
- struct mwifiex_ie_types_header header;
- struct mwifiex_chan_scan_param_set chan_scan_param[1];
-} __packed;
-
-struct chan_band_param_set {
- u8 radio_type;
- u8 chan_number;
-};
-
-struct mwifiex_ie_types_chan_band_list_param_set {
- struct mwifiex_ie_types_header header;
- struct chan_band_param_set chan_band_param[1];
-} __packed;
-
-struct mwifiex_ie_types_rates_param_set {
- struct mwifiex_ie_types_header header;
- u8 rates[1];
-} __packed;
-
-struct mwifiex_ie_types_ssid_param_set {
- struct mwifiex_ie_types_header header;
- u8 ssid[1];
-} __packed;
-
-struct mwifiex_ie_types_num_probes {
- struct mwifiex_ie_types_header header;
- __le16 num_probes;
-} __packed;
-
-struct mwifiex_ie_types_scan_chan_gap {
- struct mwifiex_ie_types_header header;
- /* time gap in TUs to be used between two consecutive channels scan */
- __le16 chan_gap;
-} __packed;
-
-struct mwifiex_ietypes_chanstats {
- struct mwifiex_ie_types_header header;
- struct mwifiex_fw_chan_stats chanstats[0];
-} __packed;
-
-struct mwifiex_ie_types_wildcard_ssid_params {
- struct mwifiex_ie_types_header header;
- u8 max_ssid_length;
- u8 ssid[1];
-} __packed;
-
-#define TSF_DATA_SIZE 8
-struct mwifiex_ie_types_tsf_timestamp {
- struct mwifiex_ie_types_header header;
- u8 tsf_data[1];
-} __packed;
-
-struct mwifiex_cf_param_set {
- u8 cfp_cnt;
- u8 cfp_period;
- __le16 cfp_max_duration;
- __le16 cfp_duration_remaining;
-} __packed;
-
-struct mwifiex_ibss_param_set {
- __le16 atim_window;
-} __packed;
-
-struct mwifiex_ie_types_ss_param_set {
- struct mwifiex_ie_types_header header;
- union {
- struct mwifiex_cf_param_set cf_param_set[1];
- struct mwifiex_ibss_param_set ibss_param_set[1];
- } cf_ibss;
-} __packed;
-
-struct mwifiex_fh_param_set {
- __le16 dwell_time;
- u8 hop_set;
- u8 hop_pattern;
- u8 hop_index;
-} __packed;
-
-struct mwifiex_ds_param_set {
- u8 current_chan;
-} __packed;
-
-struct mwifiex_ie_types_phy_param_set {
- struct mwifiex_ie_types_header header;
- union {
- struct mwifiex_fh_param_set fh_param_set[1];
- struct mwifiex_ds_param_set ds_param_set[1];
- } fh_ds;
-} __packed;
-
-struct mwifiex_ie_types_auth_type {
- struct mwifiex_ie_types_header header;
- __le16 auth_type;
-} __packed;
-
-struct mwifiex_ie_types_vendor_param_set {
- struct mwifiex_ie_types_header header;
- u8 ie[MWIFIEX_MAX_VSIE_LEN];
-};
-
-#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC 60
-
-struct mwifiex_ie_types_tdls_idle_timeout {
- struct mwifiex_ie_types_header header;
- __le16 value;
-} __packed;
-
-struct mwifiex_ie_types_rsn_param_set {
- struct mwifiex_ie_types_header header;
- u8 rsn_ie[1];
-} __packed;
-
-#define KEYPARAMSET_FIXED_LEN 6
-
-struct mwifiex_ie_type_key_param_set {
- __le16 type;
- __le16 length;
- __le16 key_type_id;
- __le16 key_info;
- __le16 key_len;
- u8 key[50];
-} __packed;
-
-#define IGTK_PN_LEN 8
-
-struct mwifiex_cmac_param {
- u8 ipn[IGTK_PN_LEN];
- u8 key[WLAN_KEY_LEN_AES_CMAC];
-} __packed;
-
-struct mwifiex_wep_param {
- __le16 key_len;
- u8 key[WLAN_KEY_LEN_WEP104];
-} __packed;
-
-struct mwifiex_tkip_param {
- u8 pn[WPA_PN_SIZE];
- __le16 key_len;
- u8 key[WLAN_KEY_LEN_TKIP];
-} __packed;
-
-struct mwifiex_aes_param {
- u8 pn[WPA_PN_SIZE];
- __le16 key_len;
- u8 key[WLAN_KEY_LEN_CCMP];
-} __packed;
-
-struct mwifiex_wapi_param {
- u8 pn[PN_LEN];
- __le16 key_len;
- u8 key[WLAN_KEY_LEN_SMS4];
-} __packed;
-
-struct mwifiex_cmac_aes_param {
- u8 ipn[IGTK_PN_LEN];
- __le16 key_len;
- u8 key[WLAN_KEY_LEN_AES_CMAC];
-} __packed;
-
-struct mwifiex_ie_type_key_param_set_v2 {
- __le16 type;
- __le16 len;
- u8 mac_addr[ETH_ALEN];
- u8 key_idx;
- u8 key_type;
- __le16 key_info;
- union {
- struct mwifiex_wep_param wep;
- struct mwifiex_tkip_param tkip;
- struct mwifiex_aes_param aes;
- struct mwifiex_wapi_param wapi;
- struct mwifiex_cmac_aes_param cmac_aes;
- } key_params;
-} __packed;
-
-struct host_cmd_ds_802_11_key_material_v2 {
- __le16 action;
- struct mwifiex_ie_type_key_param_set_v2 key_param_set;
-} __packed;
-
-struct host_cmd_ds_802_11_key_material {
- __le16 action;
- struct mwifiex_ie_type_key_param_set key_param_set;
-} __packed;
-
-struct host_cmd_ds_gen {
- __le16 command;
- __le16 size;
- __le16 seq_num;
- __le16 result;
-};
-
-#define S_DS_GEN sizeof(struct host_cmd_ds_gen)
-
-enum sleep_resp_ctrl {
- RESP_NOT_NEEDED = 0,
- RESP_NEEDED,
-};
-
-struct mwifiex_ps_param {
- __le16 null_pkt_interval;
- __le16 multiple_dtims;
- __le16 bcn_miss_timeout;
- __le16 local_listen_interval;
- __le16 adhoc_wake_period;
- __le16 mode;
- __le16 delay_to_ps;
-};
-
-#define BITMAP_AUTO_DS 0x01
-#define BITMAP_STA_PS 0x10
-
-struct mwifiex_ie_types_auto_ds_param {
- struct mwifiex_ie_types_header header;
- __le16 deep_sleep_timeout;
-} __packed;
-
-struct mwifiex_ie_types_ps_param {
- struct mwifiex_ie_types_header header;
- struct mwifiex_ps_param param;
-} __packed;
-
-struct host_cmd_ds_802_11_ps_mode_enh {
- __le16 action;
-
- union {
- struct mwifiex_ps_param opt_ps;
- __le16 ps_bitmap;
- } params;
-} __packed;
-
-enum API_VER_ID {
- KEY_API_VER_ID = 1,
- FW_API_VER_ID = 2,
-};
-
-struct hw_spec_api_rev {
- struct mwifiex_ie_types_header header;
- __le16 api_id;
- u8 major_ver;
- u8 minor_ver;
-} __packed;
-
-struct host_cmd_ds_get_hw_spec {
- __le16 hw_if_version;
- __le16 version;
- __le16 reserved;
- __le16 num_of_mcast_adr;
- u8 permanent_addr[ETH_ALEN];
- __le16 region_code;
- __le16 number_of_antenna;
- __le32 fw_release_number;
- __le32 reserved_1;
- __le32 reserved_2;
- __le32 reserved_3;
- __le32 fw_cap_info;
- __le32 dot_11n_dev_cap;
- u8 dev_mcs_support;
- __le16 mp_end_port; /* SDIO only, reserved for other interfacces */
- __le16 mgmt_buf_count; /* mgmt IE buffer count */
- __le32 reserved_5;
- __le32 reserved_6;
- __le32 dot_11ac_dev_cap;
- __le32 dot_11ac_mcs_support;
- u8 tlvs[0];
-} __packed;
-
-struct host_cmd_ds_802_11_rssi_info {
- __le16 action;
- __le16 ndata;
- __le16 nbcn;
- __le16 reserved[9];
- long long reserved_1;
-};
-
-struct host_cmd_ds_802_11_rssi_info_rsp {
- __le16 action;
- __le16 ndata;
- __le16 nbcn;
- __le16 data_rssi_last;
- __le16 data_nf_last;
- __le16 data_rssi_avg;
- __le16 data_nf_avg;
- __le16 bcn_rssi_last;
- __le16 bcn_nf_last;
- __le16 bcn_rssi_avg;
- __le16 bcn_nf_avg;
- long long tsf_bcn;
-};
-
-struct host_cmd_ds_802_11_mac_address {
- __le16 action;
- u8 mac_addr[ETH_ALEN];
-};
-
-struct host_cmd_ds_mac_control {
- __le16 action;
- __le16 reserved;
-};
-
-struct host_cmd_ds_mac_multicast_adr {
- __le16 action;
- __le16 num_of_adrs;
- u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
-} __packed;
-
-struct host_cmd_ds_802_11_deauthenticate {
- u8 mac_addr[ETH_ALEN];
- __le16 reason_code;
-} __packed;
-
-struct host_cmd_ds_802_11_associate {
- u8 peer_sta_addr[ETH_ALEN];
- __le16 cap_info_bitmap;
- __le16 listen_interval;
- __le16 beacon_period;
- u8 dtim_period;
-} __packed;
-
-struct ieee_types_assoc_rsp {
- __le16 cap_info_bitmap;
- __le16 status_code;
- __le16 a_id;
- u8 ie_buffer[1];
-} __packed;
-
-struct host_cmd_ds_802_11_associate_rsp {
- struct ieee_types_assoc_rsp assoc_rsp;
-} __packed;
-
-struct ieee_types_cf_param_set {
- u8 element_id;
- u8 len;
- u8 cfp_cnt;
- u8 cfp_period;
- __le16 cfp_max_duration;
- __le16 cfp_duration_remaining;
-} __packed;
-
-struct ieee_types_ibss_param_set {
- u8 element_id;
- u8 len;
- __le16 atim_window;
-} __packed;
-
-union ieee_types_ss_param_set {
- struct ieee_types_cf_param_set cf_param_set;
- struct ieee_types_ibss_param_set ibss_param_set;
-} __packed;
-
-struct ieee_types_fh_param_set {
- u8 element_id;
- u8 len;
- __le16 dwell_time;
- u8 hop_set;
- u8 hop_pattern;
- u8 hop_index;
-} __packed;
-
-struct ieee_types_ds_param_set {
- u8 element_id;
- u8 len;
- u8 current_chan;
-} __packed;
-
-union ieee_types_phy_param_set {
- struct ieee_types_fh_param_set fh_param_set;
- struct ieee_types_ds_param_set ds_param_set;
-} __packed;
-
-struct ieee_types_oper_mode_ntf {
- u8 element_id;
- u8 len;
- u8 oper_mode;
-} __packed;
-
-struct host_cmd_ds_802_11_ad_hoc_start {
- u8 ssid[IEEE80211_MAX_SSID_LEN];
- u8 bss_mode;
- __le16 beacon_period;
- u8 dtim_period;
- union ieee_types_ss_param_set ss_param_set;
- union ieee_types_phy_param_set phy_param_set;
- u16 reserved1;
- __le16 cap_info_bitmap;
- u8 data_rate[HOSTCMD_SUPPORTED_RATES];
-} __packed;
-
-struct host_cmd_ds_802_11_ad_hoc_result {
- u8 pad[3];
- u8 bssid[ETH_ALEN];
-} __packed;
-
-struct adhoc_bss_desc {
- u8 bssid[ETH_ALEN];
- u8 ssid[IEEE80211_MAX_SSID_LEN];
- u8 bss_mode;
- __le16 beacon_period;
- u8 dtim_period;
- u8 time_stamp[8];
- u8 local_time[8];
- union ieee_types_phy_param_set phy_param_set;
- union ieee_types_ss_param_set ss_param_set;
- __le16 cap_info_bitmap;
- u8 data_rates[HOSTCMD_SUPPORTED_RATES];
-
- /*
- * DO NOT ADD ANY FIELDS TO THIS STRUCTURE.
- * It is used in the Adhoc join command and will cause a
- * binary layout mismatch with the firmware
- */
-} __packed;
-
-struct host_cmd_ds_802_11_ad_hoc_join {
- struct adhoc_bss_desc bss_descriptor;
- u16 reserved1;
- u16 reserved2;
-} __packed;
-
-struct host_cmd_ds_802_11_get_log {
- __le32 mcast_tx_frame;
- __le32 failed;
- __le32 retry;
- __le32 multi_retry;
- __le32 frame_dup;
- __le32 rts_success;
- __le32 rts_failure;
- __le32 ack_failure;
- __le32 rx_frag;
- __le32 mcast_rx_frame;
- __le32 fcs_error;
- __le32 tx_frame;
- __le32 reserved;
- __le32 wep_icv_err_cnt[4];
- __le32 bcn_rcv_cnt;
- __le32 bcn_miss_cnt;
-};
-
-/* Enumeration for rate format */
-enum _mwifiex_rate_format {
- MWIFIEX_RATE_FORMAT_LG = 0,
- MWIFIEX_RATE_FORMAT_HT,
- MWIFIEX_RATE_FORMAT_VHT,
- MWIFIEX_RATE_FORMAT_AUTO = 0xFF,
-};
-
-struct host_cmd_ds_tx_rate_query {
- u8 tx_rate;
- /* Tx Rate Info: For 802.11 AC cards
- *
- * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2
- * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3
- * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1
- *
- * For non-802.11 AC cards
- * Ht Info [Bit 0] RxRate format: LG=0, HT=1
- * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1
- * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1
- */
- u8 ht_info;
-} __packed;
-
-struct mwifiex_tx_pause_tlv {
- struct mwifiex_ie_types_header header;
- u8 peermac[ETH_ALEN];
- u8 tx_pause;
- u8 pkt_cnt;
-} __packed;
-
-enum Host_Sleep_Action {
- HS_CONFIGURE = 0x0001,
- HS_ACTIVATE = 0x0002,
-};
-
-struct mwifiex_hs_config_param {
- __le32 conditions;
- u8 gpio;
- u8 gap;
-} __packed;
-
-struct hs_activate_param {
- __le16 resp_ctrl;
-} __packed;
-
-struct host_cmd_ds_802_11_hs_cfg_enh {
- __le16 action;
-
- union {
- struct mwifiex_hs_config_param hs_config;
- struct hs_activate_param hs_activate;
- } params;
-} __packed;
-
-enum SNMP_MIB_INDEX {
- OP_RATE_SET_I = 1,
- DTIM_PERIOD_I = 3,
- RTS_THRESH_I = 5,
- SHORT_RETRY_LIM_I = 6,
- LONG_RETRY_LIM_I = 7,
- FRAG_THRESH_I = 8,
- DOT11D_I = 9,
- DOT11H_I = 10,
-};
-
-enum mwifiex_assocmd_failurepoint {
- MWIFIEX_ASSOC_CMD_SUCCESS = 0,
- MWIFIEX_ASSOC_CMD_FAILURE_ASSOC,
- MWIFIEX_ASSOC_CMD_FAILURE_AUTH,
- MWIFIEX_ASSOC_CMD_FAILURE_JOIN
-};
-
-#define MAX_SNMP_BUF_SIZE 128
-
-struct host_cmd_ds_802_11_snmp_mib {
- __le16 query_type;
- __le16 oid;
- __le16 buf_size;
- u8 value[1];
-} __packed;
-
-struct mwifiex_rate_scope {
- __le16 type;
- __le16 length;
- __le16 hr_dsss_rate_bitmap;
- __le16 ofdm_rate_bitmap;
- __le16 ht_mcs_rate_bitmap[8];
- __le16 vht_mcs_rate_bitmap[8];
-} __packed;
-
-struct mwifiex_rate_drop_pattern {
- __le16 type;
- __le16 length;
- __le32 rate_drop_mode;
-} __packed;
-
-struct host_cmd_ds_tx_rate_cfg {
- __le16 action;
- __le16 cfg_index;
-} __packed;
-
-struct mwifiex_power_group {
- u8 modulation_class;
- u8 first_rate_code;
- u8 last_rate_code;
- s8 power_step;
- s8 power_min;
- s8 power_max;
- u8 ht_bandwidth;
- u8 reserved;
-} __packed;
-
-struct mwifiex_types_power_group {
- __le16 type;
- __le16 length;
-} __packed;
-
-struct host_cmd_ds_txpwr_cfg {
- __le16 action;
- __le16 cfg_index;
- __le32 mode;
-} __packed;
-
-struct host_cmd_ds_rf_tx_pwr {
- __le16 action;
- __le16 cur_level;
- u8 max_power;
- u8 min_power;
-} __packed;
-
-struct host_cmd_ds_rf_ant_mimo {
- __le16 action_tx;
- __le16 tx_ant_mode;
- __le16 action_rx;
- __le16 rx_ant_mode;
-};
-
-struct host_cmd_ds_rf_ant_siso {
- __le16 action;
- __le16 ant_mode;
-};
-
-struct host_cmd_ds_tdls_oper {
- __le16 tdls_action;
- __le16 reason;
- u8 peer_mac[ETH_ALEN];
-} __packed;
-
-struct mwifiex_tdls_config {
- __le16 enable;
-};
-
-struct mwifiex_tdls_config_cs_params {
- u8 unit_time;
- u8 thr_otherlink;
- u8 thr_directlink;
-};
-
-struct mwifiex_tdls_init_cs_params {
- u8 peer_mac[ETH_ALEN];
- u8 primary_chan;
- u8 second_chan_offset;
- u8 band;
- __le16 switch_time;
- __le16 switch_timeout;
- u8 reg_class;
- u8 periodicity;
-} __packed;
-
-struct mwifiex_tdls_stop_cs_params {
- u8 peer_mac[ETH_ALEN];
-};
-
-struct host_cmd_ds_tdls_config {
- __le16 tdls_action;
- u8 tdls_data[1];
-} __packed;
-
-struct mwifiex_chan_desc {
- __le16 start_freq;
- u8 chan_width;
- u8 chan_num;
-} __packed;
-
-struct host_cmd_ds_chan_rpt_req {
- struct mwifiex_chan_desc chan_desc;
- __le32 msec_dwell_time;
-} __packed;
-
-struct host_cmd_ds_chan_rpt_event {
- __le32 result;
- __le64 start_tsf;
- __le32 duration;
- u8 tlvbuf[0];
-} __packed;
-
-struct host_cmd_sdio_sp_rx_aggr_cfg {
- u8 action;
- u8 enable;
- __le16 block_size;
-} __packed;
-
-struct mwifiex_fixed_bcn_param {
- __le64 timestamp;
- __le16 beacon_period;
- __le16 cap_info_bitmap;
-} __packed;
-
-struct mwifiex_event_scan_result {
- __le16 event_id;
- u8 bss_index;
- u8 bss_type;
- u8 more_event;
- u8 reserved[3];
- __le16 buf_size;
- u8 num_of_set;
-} __packed;
-
-struct tx_status_event {
- u8 packet_type;
- u8 tx_token_id;
- u8 status;
-} __packed;
-
-#define MWIFIEX_USER_SCAN_CHAN_MAX 50
-
-#define MWIFIEX_MAX_SSID_LIST_LENGTH 10
-
-struct mwifiex_scan_cmd_config {
- /*
- * BSS mode to be sent in the firmware command
- */
- u8 bss_mode;
-
- /* Specific BSSID used to filter scan results in the firmware */
- u8 specific_bssid[ETH_ALEN];
-
- /* Length of TLVs sent in command starting at tlvBuffer */
- u32 tlv_buf_len;
-
- /*
- * SSID TLV(s) and ChanList TLVs to be sent in the firmware command
- *
- * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set
- * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set
- */
- u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored
- here */
-} __packed;
-
-struct mwifiex_user_scan_chan {
- u8 chan_number;
- u8 radio_type;
- u8 scan_type;
- u8 reserved;
- u32 scan_time;
-} __packed;
-
-struct mwifiex_user_scan_cfg {
- /*
- * BSS mode to be sent in the firmware command
- */
- u8 bss_mode;
- /* Configure the number of probe requests for active chan scans */
- u8 num_probes;
- u8 reserved;
- /* BSSID filter sent in the firmware command to limit the results */
- u8 specific_bssid[ETH_ALEN];
- /* SSID filter list used in the firmware to limit the scan results */
- struct cfg80211_ssid *ssid_list;
- u8 num_ssids;
- /* Variable number (fixed maximum) of channels to scan up */
- struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX];
- u16 scan_chan_gap;
-} __packed;
-
-struct ie_body {
- u8 grp_key_oui[4];
- u8 ptk_cnt[2];
- u8 ptk_body[4];
-} __packed;
-
-struct host_cmd_ds_802_11_scan {
- u8 bss_mode;
- u8 bssid[ETH_ALEN];
- u8 tlv_buffer[1];
-} __packed;
-
-struct host_cmd_ds_802_11_scan_rsp {
- __le16 bss_descript_size;
- u8 number_of_sets;
- u8 bss_desc_and_tlv_buffer[1];
-} __packed;
-
-struct host_cmd_ds_802_11_scan_ext {
- u32 reserved;
- u8 tlv_buffer[1];
-} __packed;
-
-struct mwifiex_ie_types_bss_mode {
- struct mwifiex_ie_types_header header;
- u8 bss_mode;
-} __packed;
-
-struct mwifiex_ie_types_bss_scan_rsp {
- struct mwifiex_ie_types_header header;
- u8 bssid[ETH_ALEN];
- u8 frame_body[1];
-} __packed;
-
-struct mwifiex_ie_types_bss_scan_info {
- struct mwifiex_ie_types_header header;
- __le16 rssi;
- __le16 anpi;
- u8 cca_busy_fraction;
- u8 radio_type;
- u8 channel;
- u8 reserved;
- __le64 tsf;
-} __packed;
-
-struct host_cmd_ds_802_11_bg_scan_query {
- u8 flush;
-} __packed;
-
-struct host_cmd_ds_802_11_bg_scan_query_rsp {
- __le32 report_condition;
- struct host_cmd_ds_802_11_scan_rsp scan_resp;
-} __packed;
-
-struct mwifiex_ietypes_domain_param_set {
- struct mwifiex_ie_types_header header;
- u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
- struct ieee80211_country_ie_triplet triplet[1];
-} __packed;
-
-struct host_cmd_ds_802_11d_domain_info {
- __le16 action;
- struct mwifiex_ietypes_domain_param_set domain;
-} __packed;
-
-struct host_cmd_ds_802_11d_domain_info_rsp {
- __le16 action;
- struct mwifiex_ietypes_domain_param_set domain;
-} __packed;
-
-struct host_cmd_ds_11n_addba_req {
- u8 add_req_result;
- u8 peer_mac_addr[ETH_ALEN];
- u8 dialog_token;
- __le16 block_ack_param_set;
- __le16 block_ack_tmo;
- __le16 ssn;
-} __packed;
-
-struct host_cmd_ds_11n_addba_rsp {
- u8 add_rsp_result;
- u8 peer_mac_addr[ETH_ALEN];
- u8 dialog_token;
- __le16 status_code;
- __le16 block_ack_param_set;
- __le16 block_ack_tmo;
- __le16 ssn;
-} __packed;
-
-struct host_cmd_ds_11n_delba {
- u8 del_result;
- u8 peer_mac_addr[ETH_ALEN];
- __le16 del_ba_param_set;
- __le16 reason_code;
- u8 reserved;
-} __packed;
-
-struct host_cmd_ds_11n_batimeout {
- u8 tid;
- u8 peer_mac_addr[ETH_ALEN];
- u8 origninator;
-} __packed;
-
-struct host_cmd_ds_11n_cfg {
- __le16 action;
- __le16 ht_tx_cap;
- __le16 ht_tx_info;
- __le16 misc_config; /* Needed for 802.11AC cards only */
-} __packed;
-
-struct host_cmd_ds_txbuf_cfg {
- __le16 action;
- __le16 buff_size;
- __le16 mp_end_port; /* SDIO only, reserved for other interfacces */
- __le16 reserved3;
-} __packed;
-
-struct host_cmd_ds_amsdu_aggr_ctrl {
- __le16 action;
- __le16 enable;
- __le16 curr_buf_size;
-} __packed;
-
-struct host_cmd_ds_sta_deauth {
- u8 mac[ETH_ALEN];
- __le16 reason;
-} __packed;
-
-struct mwifiex_ie_types_sta_info {
- struct mwifiex_ie_types_header header;
- u8 mac[ETH_ALEN];
- u8 power_mfg_status;
- s8 rssi;
-};
-
-struct host_cmd_ds_sta_list {
- u16 sta_count;
- u8 tlv[0];
-} __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];
-};
-
-struct mwifiex_ie_types_wmm_queue_status {
- struct mwifiex_ie_types_header header;
- u8 queue_index;
- u8 disabled;
- __le16 medium_time;
- u8 flow_required;
- u8 flow_created;
- u32 reserved;
-};
-
-struct ieee_types_vendor_header {
- u8 element_id;
- u8 len;
- u8 oui[4]; /* 0~2: oui, 3: oui_type */
- u8 oui_subtype;
- u8 version;
-} __packed;
-
-struct ieee_types_wmm_parameter {
- /*
- * WMM Parameter IE - Vendor Specific Header:
- * element_id [221/0xdd]
- * Len [24]
- * Oui [00:50:f2]
- * OuiType [2]
- * OuiSubType [1]
- * Version [1]
- */
- struct ieee_types_vendor_header vend_hdr;
- u8 qos_info_bitmap;
- u8 reserved;
- struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
-} __packed;
-
-struct ieee_types_wmm_info {
-
- /*
- * WMM Info IE - Vendor Specific Header:
- * element_id [221/0xdd]
- * Len [7]
- * Oui [00:50:f2]
- * OuiType [2]
- * OuiSubType [0]
- * Version [1]
- */
- struct ieee_types_vendor_header vend_hdr;
-
- u8 qos_info_bitmap;
-} __packed;
-
-struct host_cmd_ds_wmm_get_status {
- u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) *
- IEEE80211_NUM_ACS];
- u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2];
-} __packed;
-
-struct mwifiex_wmm_ac_status {
- u8 disabled;
- u8 flow_required;
- u8 flow_created;
-};
-
-struct mwifiex_ie_types_htcap {
- struct mwifiex_ie_types_header header;
- struct ieee80211_ht_cap ht_cap;
-} __packed;
-
-struct mwifiex_ie_types_vhtcap {
- struct mwifiex_ie_types_header header;
- struct ieee80211_vht_cap vht_cap;
-} __packed;
-
-struct mwifiex_ie_types_aid {
- struct mwifiex_ie_types_header header;
- __le16 aid;
-} __packed;
-
-struct mwifiex_ie_types_oper_mode_ntf {
- struct mwifiex_ie_types_header header;
- u8 oper_mode;
-} __packed;
-
-/* VHT Operations IE */
-struct mwifiex_ie_types_vht_oper {
- struct mwifiex_ie_types_header header;
- u8 chan_width;
- u8 chan_center_freq_1;
- u8 chan_center_freq_2;
- /* Basic MCS set map, each 2 bits stands for a NSS */
- __le16 basic_mcs_map;
-} __packed;
-
-struct mwifiex_ie_types_wmmcap {
- struct mwifiex_ie_types_header header;
- struct mwifiex_types_wmm_info wmm_info;
-} __packed;
-
-struct mwifiex_ie_types_htinfo {
- struct mwifiex_ie_types_header header;
- struct ieee80211_ht_operation ht_oper;
-} __packed;
-
-struct mwifiex_ie_types_2040bssco {
- struct mwifiex_ie_types_header header;
- u8 bss_co_2040;
-} __packed;
-
-struct mwifiex_ie_types_extcap {
- struct mwifiex_ie_types_header header;
- u8 ext_capab[0];
-} __packed;
-
-struct host_cmd_ds_mem_access {
- __le16 action;
- __le16 reserved;
- __le32 addr;
- __le32 value;
-};
-
-struct mwifiex_ie_types_qos_info {
- struct mwifiex_ie_types_header header;
- u8 qos_info;
-} __packed;
-
-struct host_cmd_ds_mac_reg_access {
- __le16 action;
- __le16 offset;
- __le32 value;
-} __packed;
-
-struct host_cmd_ds_bbp_reg_access {
- __le16 action;
- __le16 offset;
- u8 value;
- u8 reserved[3];
-} __packed;
-
-struct host_cmd_ds_rf_reg_access {
- __le16 action;
- __le16 offset;
- u8 value;
- u8 reserved[3];
-} __packed;
-
-struct host_cmd_ds_pmic_reg_access {
- __le16 action;
- __le16 offset;
- u8 value;
- u8 reserved[3];
-} __packed;
-
-struct host_cmd_ds_802_11_eeprom_access {
- __le16 action;
-
- __le16 offset;
- __le16 byte_count;
- u8 value;
-} __packed;
-
-struct mwifiex_assoc_event {
- u8 sta_addr[ETH_ALEN];
- __le16 type;
- __le16 len;
- __le16 frame_control;
- __le16 cap_info;
- __le16 listen_interval;
- u8 data[0];
-} __packed;
-
-struct host_cmd_ds_sys_config {
- __le16 action;
- u8 tlv[0];
-};
-
-struct host_cmd_11ac_vht_cfg {
- __le16 action;
- u8 band_config;
- u8 misc_config;
- __le32 cap_info;
- __le32 mcs_tx_set;
- __le32 mcs_rx_set;
-} __packed;
-
-struct host_cmd_tlv_akmp {
- struct mwifiex_ie_types_header header;
- __le16 key_mgmt;
- __le16 key_mgmt_operation;
-} __packed;
-
-struct host_cmd_tlv_pwk_cipher {
- struct mwifiex_ie_types_header header;
- __le16 proto;
- u8 cipher;
- u8 reserved;
-} __packed;
-
-struct host_cmd_tlv_gwk_cipher {
- struct mwifiex_ie_types_header header;
- u8 cipher;
- u8 reserved;
-} __packed;
-
-struct host_cmd_tlv_passphrase {
- struct mwifiex_ie_types_header header;
- u8 passphrase[0];
-} __packed;
-
-struct host_cmd_tlv_wep_key {
- struct mwifiex_ie_types_header header;
- u8 key_index;
- u8 is_default;
- u8 key[1];
-};
-
-struct host_cmd_tlv_auth_type {
- struct mwifiex_ie_types_header header;
- u8 auth_type;
-} __packed;
-
-struct host_cmd_tlv_encrypt_protocol {
- struct mwifiex_ie_types_header header;
- __le16 proto;
-} __packed;
-
-struct host_cmd_tlv_ssid {
- struct mwifiex_ie_types_header header;
- u8 ssid[0];
-} __packed;
-
-struct host_cmd_tlv_rates {
- struct mwifiex_ie_types_header header;
- u8 rates[0];
-} __packed;
-
-struct mwifiex_ie_types_bssid_list {
- struct mwifiex_ie_types_header header;
- u8 bssid[ETH_ALEN];
-} __packed;
-
-struct host_cmd_tlv_bcast_ssid {
- struct mwifiex_ie_types_header header;
- u8 bcast_ctl;
-} __packed;
-
-struct host_cmd_tlv_beacon_period {
- struct mwifiex_ie_types_header header;
- __le16 period;
-} __packed;
-
-struct host_cmd_tlv_dtim_period {
- struct mwifiex_ie_types_header header;
- u8 period;
-} __packed;
-
-struct host_cmd_tlv_frag_threshold {
- struct mwifiex_ie_types_header header;
- __le16 frag_thr;
-} __packed;
-
-struct host_cmd_tlv_rts_threshold {
- struct mwifiex_ie_types_header header;
- __le16 rts_thr;
-} __packed;
-
-struct host_cmd_tlv_retry_limit {
- struct mwifiex_ie_types_header header;
- u8 limit;
-} __packed;
-
-struct host_cmd_tlv_mac_addr {
- struct mwifiex_ie_types_header header;
- u8 mac_addr[ETH_ALEN];
-} __packed;
-
-struct host_cmd_tlv_channel_band {
- struct mwifiex_ie_types_header header;
- u8 band_config;
- u8 channel;
-} __packed;
-
-struct host_cmd_tlv_ageout_timer {
- struct mwifiex_ie_types_header header;
- __le32 sta_ao_timer;
-} __packed;
-
-struct host_cmd_tlv_power_constraint {
- struct mwifiex_ie_types_header header;
- u8 constraint;
-} __packed;
-
-struct mwifiex_ie_types_btcoex_scan_time {
- struct mwifiex_ie_types_header header;
- u8 coex_scan;
- u8 reserved;
- u16 min_scan_time;
- u16 max_scan_time;
-} __packed;
-
-struct mwifiex_ie_types_btcoex_aggr_win_size {
- struct mwifiex_ie_types_header header;
- u8 coex_win_size;
- u8 tx_win_size;
- u8 rx_win_size;
- u8 reserved;
-} __packed;
-
-struct mwifiex_ie_types_robust_coex {
- struct mwifiex_ie_types_header header;
- __le32 mode;
-} __packed;
-
-struct host_cmd_ds_version_ext {
- u8 version_str_sel;
- char version_str[128];
-} __packed;
-
-struct host_cmd_ds_mgmt_frame_reg {
- __le16 action;
- __le32 mask;
-} __packed;
-
-struct host_cmd_ds_p2p_mode_cfg {
- __le16 action;
- __le16 mode;
-} __packed;
-
-struct host_cmd_ds_remain_on_chan {
- __le16 action;
- u8 status;
- u8 reserved;
- u8 band_cfg;
- u8 channel;
- __le32 duration;
-} __packed;
-
-struct host_cmd_ds_802_11_ibss_status {
- __le16 action;
- __le16 enable;
- u8 bssid[ETH_ALEN];
- __le16 beacon_interval;
- __le16 atim_window;
- __le16 use_g_rate_protect;
-} __packed;
-
-struct mwifiex_fw_mef_entry {
- u8 mode;
- u8 action;
- __le16 exprsize;
- u8 expr[0];
-} __packed;
-
-struct host_cmd_ds_mef_cfg {
- __le32 criteria;
- __le16 num_entries;
- struct mwifiex_fw_mef_entry mef_entry[0];
-} __packed;
-
-#define CONNECTION_TYPE_INFRA 0
-#define CONNECTION_TYPE_ADHOC 1
-#define CONNECTION_TYPE_AP 2
-
-struct host_cmd_ds_set_bss_mode {
- u8 con_type;
-} __packed;
-
-struct host_cmd_ds_pcie_details {
- /* TX buffer descriptor ring address */
- u32 txbd_addr_lo;
- u32 txbd_addr_hi;
- /* TX buffer descriptor ring count */
- u32 txbd_count;
-
- /* RX buffer descriptor ring address */
- u32 rxbd_addr_lo;
- u32 rxbd_addr_hi;
- /* RX buffer descriptor ring count */
- u32 rxbd_count;
-
- /* Event buffer descriptor ring address */
- u32 evtbd_addr_lo;
- u32 evtbd_addr_hi;
- /* Event buffer descriptor ring count */
- u32 evtbd_count;
-
- /* Sleep cookie buffer physical address */
- u32 sleep_cookie_addr_lo;
- u32 sleep_cookie_addr_hi;
-} __packed;
-
-struct mwifiex_ie_types_rssi_threshold {
- struct mwifiex_ie_types_header header;
- u8 abs_value;
- u8 evt_freq;
-} __packed;
-
-#define MWIFIEX_DFS_REC_HDR_LEN 8
-#define MWIFIEX_DFS_REC_HDR_NUM 10
-#define MWIFIEX_BIN_COUNTER_LEN 7
-
-struct mwifiex_radar_det_event {
- __le32 detect_count;
- u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/
- u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/
- __le16 pw_chirp_type;
- u8 pw_chirp_idx;
- u8 pw_value;
- u8 pri_radar_type;
- u8 pri_bincnt;
- u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN];
- u8 num_dfs_records;
- u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN];
- __le32 passed;
-} __packed;
-
-struct mwifiex_ie_types_multi_chan_info {
- struct mwifiex_ie_types_header header;
- __le16 status;
- u8 tlv_buffer[0];
-} __packed;
-
-struct mwifiex_ie_types_mc_group_info {
- struct mwifiex_ie_types_header header;
- u8 chan_group_id;
- u8 chan_buf_weight;
- u8 band_config;
- u8 chan_num;
- u32 chan_time;
- u32 reserved;
- union {
- u8 sdio_func_num;
- u8 usb_ep_num;
- } hid_num;
- u8 intf_num;
- u8 bss_type_numlist[0];
-} __packed;
-
-struct meas_rpt_map {
- u8 rssi:3;
- u8 unmeasured:1;
- u8 radar:1;
- u8 unidentified_sig:1;
- u8 ofdm_preamble:1;
- u8 bss:1;
-} __packed;
-
-struct mwifiex_ie_types_chan_rpt_data {
- struct mwifiex_ie_types_header header;
- struct meas_rpt_map map;
-} __packed;
-
-struct host_cmd_ds_802_11_subsc_evt {
- __le16 action;
- __le16 events;
-} __packed;
-
-struct chan_switch_result {
- u8 cur_chan;
- u8 status;
- u8 reason;
-} __packed;
-
-struct mwifiex_tdls_generic_event {
- __le16 type;
- u8 peer_mac[ETH_ALEN];
- union {
- struct chan_switch_result switch_result;
- u8 cs_stop_reason;
- __le16 reason_code;
- __le16 reserved;
- } u;
-} __packed;
-
-struct mwifiex_ie {
- __le16 ie_index;
- __le16 mgmt_subtype_mask;
- __le16 ie_length;
- u8 ie_buffer[IEEE_MAX_IE_SIZE];
-} __packed;
-
-#define MAX_MGMT_IE_INDEX 16
-struct mwifiex_ie_list {
- __le16 type;
- __le16 len;
- struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX];
-} __packed;
-
-struct coalesce_filt_field_param {
- u8 operation;
- u8 operand_len;
- __le16 offset;
- u8 operand_byte_stream[4];
-};
-
-struct coalesce_receive_filt_rule {
- struct mwifiex_ie_types_header header;
- u8 num_of_fields;
- u8 pkt_type;
- __le16 max_coalescing_delay;
- struct coalesce_filt_field_param params[0];
-} __packed;
-
-struct host_cmd_ds_coalesce_cfg {
- __le16 action;
- __le16 num_of_rules;
- struct coalesce_receive_filt_rule rule[0];
-} __packed;
-
-struct host_cmd_ds_multi_chan_policy {
- __le16 action;
- __le16 policy;
-} __packed;
-
-struct host_cmd_ds_robust_coex {
- __le16 action;
- __le16 reserved;
-} __packed;
-
-struct host_cmd_ds_command {
- __le16 command;
- __le16 size;
- __le16 seq_num;
- __le16 result;
- union {
- struct host_cmd_ds_get_hw_spec hw_spec;
- struct host_cmd_ds_mac_control mac_ctrl;
- struct host_cmd_ds_802_11_mac_address mac_addr;
- struct host_cmd_ds_mac_multicast_adr mc_addr;
- struct host_cmd_ds_802_11_get_log get_log;
- struct host_cmd_ds_802_11_rssi_info rssi_info;
- struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp;
- struct host_cmd_ds_802_11_snmp_mib smib;
- struct host_cmd_ds_tx_rate_query tx_rate;
- struct host_cmd_ds_tx_rate_cfg tx_rate_cfg;
- struct host_cmd_ds_txpwr_cfg txp_cfg;
- struct host_cmd_ds_rf_tx_pwr txp;
- struct host_cmd_ds_rf_ant_mimo ant_mimo;
- struct host_cmd_ds_rf_ant_siso ant_siso;
- struct host_cmd_ds_802_11_ps_mode_enh psmode_enh;
- struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg;
- struct host_cmd_ds_802_11_scan scan;
- struct host_cmd_ds_802_11_scan_ext ext_scan;
- struct host_cmd_ds_802_11_scan_rsp scan_resp;
- struct host_cmd_ds_802_11_bg_scan_query bg_scan_query;
- struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp;
- struct host_cmd_ds_802_11_associate associate;
- struct host_cmd_ds_802_11_associate_rsp associate_rsp;
- struct host_cmd_ds_802_11_deauthenticate deauth;
- struct host_cmd_ds_802_11_ad_hoc_start adhoc_start;
- struct host_cmd_ds_802_11_ad_hoc_result adhoc_result;
- struct host_cmd_ds_802_11_ad_hoc_join adhoc_join;
- struct host_cmd_ds_802_11d_domain_info domain_info;
- struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp;
- struct host_cmd_ds_11n_addba_req add_ba_req;
- struct host_cmd_ds_11n_addba_rsp add_ba_rsp;
- struct host_cmd_ds_11n_delba del_ba;
- struct host_cmd_ds_txbuf_cfg tx_buf;
- struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl;
- struct host_cmd_ds_11n_cfg htcfg;
- struct host_cmd_ds_wmm_get_status get_wmm_status;
- struct host_cmd_ds_802_11_key_material key_material;
- struct host_cmd_ds_802_11_key_material_v2 key_material_v2;
- struct host_cmd_ds_version_ext verext;
- struct host_cmd_ds_mgmt_frame_reg reg_mask;
- struct host_cmd_ds_remain_on_chan roc_cfg;
- struct host_cmd_ds_p2p_mode_cfg mode_cfg;
- struct host_cmd_ds_802_11_ibss_status ibss_coalescing;
- struct host_cmd_ds_mef_cfg mef_cfg;
- struct host_cmd_ds_mem_access mem;
- struct host_cmd_ds_mac_reg_access mac_reg;
- struct host_cmd_ds_bbp_reg_access bbp_reg;
- struct host_cmd_ds_rf_reg_access rf_reg;
- struct host_cmd_ds_pmic_reg_access pmic_reg;
- struct host_cmd_ds_set_bss_mode bss_mode;
- struct host_cmd_ds_pcie_details pcie_host_spec;
- struct host_cmd_ds_802_11_eeprom_access eeprom;
- struct host_cmd_ds_802_11_subsc_evt subsc_evt;
- struct host_cmd_ds_sys_config uap_sys_config;
- struct host_cmd_ds_sta_deauth sta_deauth;
- struct host_cmd_ds_sta_list sta_list;
- struct host_cmd_11ac_vht_cfg vht_cfg;
- struct host_cmd_ds_coalesce_cfg coalesce_cfg;
- struct host_cmd_ds_tdls_config tdls_config;
- struct host_cmd_ds_tdls_oper tdls_oper;
- struct host_cmd_ds_chan_rpt_req chan_rpt_req;
- struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
- struct host_cmd_ds_multi_chan_policy mc_policy;
- struct host_cmd_ds_robust_coex coex;
- } params;
-} __packed;
-
-struct mwifiex_opt_sleep_confirm {
- __le16 command;
- __le16 size;
- __le16 seq_num;
- __le16 result;
- __le16 action;
- __le16 resp_ctrl;
-} __packed;
-#endif /* !_MWIFIEX_FW_H_ */
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c
deleted file mode 100644
index abf52d25b981..000000000000
--- a/drivers/net/wireless/mwifiex/ie.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: management IE handling- setting and
- * deleting IE.
- *
- * Copyright (C) 2012-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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"
-
-/* This function checks if current IE index is used by any on other interface.
- * Return: -1: yes, current IE index is used by someone else.
- * 0: no, current IE index is NOT used by other interface.
- */
-static int
-mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx)
-{
- int i;
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_ie *ie;
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i] != priv) {
- ie = &adapter->priv[i]->mgmt_ie[idx];
- if (ie->mgmt_subtype_mask && ie->ie_length)
- return -1;
- }
- }
-
- return 0;
-}
-
-/* Get unused IE index. This index will be used for setting new IE */
-static int
-mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask,
- struct mwifiex_ie *ie, u16 *index)
-{
- u16 mask, len, i;
-
- for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
- mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
- len = le16_to_cpu(ie->ie_length);
-
- if (mask == MWIFIEX_AUTO_IDX_MASK)
- continue;
-
- if (mask == subtype_mask) {
- if (len > IEEE_MAX_IE_SIZE)
- continue;
-
- *index = i;
- return 0;
- }
-
- if (!priv->mgmt_ie[i].ie_length) {
- if (mwifiex_ie_index_used_by_other_intf(priv, i))
- continue;
-
- *index = i;
- return 0;
- }
- }
-
- return -1;
-}
-
-/* This function prepares IE data buffer for command to be sent to FW */
-static int
-mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
- struct mwifiex_ie_list *ie_list)
-{
- u16 travel_len, index, mask;
- s16 input_len, tlv_len;
- struct mwifiex_ie *ie;
- u8 *tmp;
-
- input_len = le16_to_cpu(ie_list->len);
- travel_len = sizeof(struct mwifiex_ie_types_header);
-
- ie_list->len = 0;
-
- while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
- ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
- tlv_len = le16_to_cpu(ie->ie_length);
- travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
-
- if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
- return -1;
- index = le16_to_cpu(ie->ie_index);
- mask = le16_to_cpu(ie->mgmt_subtype_mask);
-
- if (index == MWIFIEX_AUTO_IDX_MASK) {
- /* automatic addition */
- if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
- return -1;
- if (index == MWIFIEX_AUTO_IDX_MASK)
- return -1;
-
- tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
- memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
- priv->mgmt_ie[index].ie_length = ie->ie_length;
- priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
- priv->mgmt_ie[index].mgmt_subtype_mask =
- cpu_to_le16(mask);
-
- ie->ie_index = cpu_to_le16(index);
- } else {
- if (mask != MWIFIEX_DELETE_MASK)
- return -1;
- /*
- * Check if this index is being used on any
- * other interface.
- */
- if (mwifiex_ie_index_used_by_other_intf(priv, index))
- return -1;
-
- ie->ie_length = 0;
- memcpy(&priv->mgmt_ie[index], ie,
- sizeof(struct mwifiex_ie));
- }
-
- le16_add_cpu(&ie_list->len,
- le16_to_cpu(priv->mgmt_ie[index].ie_length) +
- MWIFIEX_IE_HDR_SIZE);
- input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
- }
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
- return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
- HostCmd_ACT_GEN_SET,
- UAP_CUSTOM_IE_I, ie_list, false);
-
- return 0;
-}
-
-/* Copy individual custom IEs for beacon, probe response and assoc response
- * and prepare single structure for IE setting.
- * This function also updates allocated IE indices from driver.
- */
-static int
-mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
- struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
- struct mwifiex_ie *pr_ie, u16 *probe_idx,
- struct mwifiex_ie *ar_ie, u16 *assoc_idx)
-{
- struct mwifiex_ie_list *ap_custom_ie;
- u8 *pos;
- u16 len;
- int ret;
-
- ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
- if (!ap_custom_ie)
- return -ENOMEM;
-
- ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
- pos = (u8 *)ap_custom_ie->ie_list;
-
- if (beacon_ie) {
- len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
- le16_to_cpu(beacon_ie->ie_length);
- memcpy(pos, beacon_ie, len);
- pos += len;
- le16_add_cpu(&ap_custom_ie->len, len);
- }
- if (pr_ie) {
- len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
- le16_to_cpu(pr_ie->ie_length);
- memcpy(pos, pr_ie, len);
- pos += len;
- le16_add_cpu(&ap_custom_ie->len, len);
- }
- if (ar_ie) {
- len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
- le16_to_cpu(ar_ie->ie_length);
- memcpy(pos, ar_ie, len);
- pos += len;
- le16_add_cpu(&ap_custom_ie->len, len);
- }
-
- ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
-
- pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
- if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
- /* save beacon ie index after auto-indexing */
- *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
- len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
- le16_to_cpu(beacon_ie->ie_length);
- pos += len;
- }
- if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
- /* save probe resp ie index after auto-indexing */
- *probe_idx = *((u16 *)pos);
- len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
- le16_to_cpu(pr_ie->ie_length);
- pos += len;
- }
- if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
- /* save assoc resp ie index after auto-indexing */
- *assoc_idx = *((u16 *)pos);
-
- kfree(ap_custom_ie);
- return ret;
-}
-
-/* This function checks if the vendor specified IE is present in passed buffer
- * and copies it to mwifiex_ie structure.
- * Function takes pointer to struct mwifiex_ie pointer as argument.
- * If the vendor specified IE is present then memory is allocated for
- * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
- * this memory.
- */
-static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
- struct mwifiex_ie **ie_ptr, u16 mask,
- unsigned int oui, u8 oui_type)
-{
- struct ieee_types_header *vs_ie;
- struct mwifiex_ie *ie = *ie_ptr;
- const u8 *vendor_ie;
-
- vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
- if (vendor_ie) {
- if (!*ie_ptr) {
- *ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
- GFP_KERNEL);
- if (!*ie_ptr)
- return -ENOMEM;
- ie = *ie_ptr;
- }
-
- vs_ie = (struct ieee_types_header *)vendor_ie;
- memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
- vs_ie, vs_ie->len + 2);
- le16_add_cpu(&ie->ie_length, vs_ie->len + 2);
- ie->mgmt_subtype_mask = cpu_to_le16(mask);
- ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
- }
-
- *ie_ptr = ie;
- return 0;
-}
-
-/* This function parses beacon IEs, probe response IEs, association response IEs
- * from cfg80211_ap_settings->beacon and sets these IE to FW.
- */
-static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
- struct cfg80211_beacon_data *data)
-{
- struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
- u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
- u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
- int ret = 0;
-
- if (data->beacon_ies && data->beacon_ies_len) {
- mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
- &beacon_ie, MGMT_MASK_BEACON,
- WLAN_OUI_MICROSOFT,
- WLAN_OUI_TYPE_MICROSOFT_WPS);
- mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
- &beacon_ie, MGMT_MASK_BEACON,
- WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
- }
-
- if (data->proberesp_ies && data->proberesp_ies_len) {
- mwifiex_update_vs_ie(data->proberesp_ies,
- data->proberesp_ies_len, &pr_ie,
- MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
- WLAN_OUI_TYPE_MICROSOFT_WPS);
- mwifiex_update_vs_ie(data->proberesp_ies,
- data->proberesp_ies_len, &pr_ie,
- MGMT_MASK_PROBE_RESP,
- WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
- }
-
- if (data->assocresp_ies && data->assocresp_ies_len) {
- mwifiex_update_vs_ie(data->assocresp_ies,
- data->assocresp_ies_len, &ar_ie,
- MGMT_MASK_ASSOC_RESP |
- MGMT_MASK_REASSOC_RESP,
- WLAN_OUI_MICROSOFT,
- WLAN_OUI_TYPE_MICROSOFT_WPS);
- mwifiex_update_vs_ie(data->assocresp_ies,
- data->assocresp_ies_len, &ar_ie,
- MGMT_MASK_ASSOC_RESP |
- MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
- WLAN_OUI_TYPE_WFA_P2P);
- }
-
- if (beacon_ie || pr_ie || ar_ie) {
- ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
- &beacon_idx, pr_ie,
- &pr_idx, ar_ie, &ar_idx);
- if (ret)
- goto done;
- }
-
- priv->beacon_idx = beacon_idx;
- priv->proberesp_idx = pr_idx;
- priv->assocresp_idx = ar_idx;
-
-done:
- kfree(beacon_ie);
- kfree(pr_ie);
- kfree(ar_ie);
-
- return ret;
-}
-
-/* This function parses head and tail IEs, from cfg80211_beacon_data and sets
- * these IE to FW.
- */
-static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
- struct cfg80211_beacon_data *info)
-{
- struct mwifiex_ie *gen_ie;
- struct ieee_types_header *hdr;
- struct ieee80211_vendor_ie *vendorhdr;
- u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
- int left_len, parsed_len = 0;
-
- if (!info->tail || !info->tail_len)
- return 0;
-
- gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
- if (!gen_ie)
- return -ENOMEM;
-
- left_len = info->tail_len;
-
- /* Many IEs are generated in FW by parsing bss configuration.
- * Let's not add them here; else we may end up duplicating these IEs
- */
- while (left_len > sizeof(struct ieee_types_header)) {
- hdr = (void *)(info->tail + parsed_len);
- switch (hdr->element_id) {
- case WLAN_EID_SSID:
- case WLAN_EID_SUPP_RATES:
- case WLAN_EID_COUNTRY:
- case WLAN_EID_PWR_CONSTRAINT:
- case WLAN_EID_EXT_SUPP_RATES:
- case WLAN_EID_HT_CAPABILITY:
- case WLAN_EID_HT_OPERATION:
- case WLAN_EID_VHT_CAPABILITY:
- case WLAN_EID_VHT_OPERATION:
- case WLAN_EID_VENDOR_SPECIFIC:
- break;
- default:
- memcpy(gen_ie->ie_buffer + ie_len, hdr,
- hdr->len + sizeof(struct ieee_types_header));
- ie_len += hdr->len + sizeof(struct ieee_types_header);
- break;
- }
- left_len -= hdr->len + sizeof(struct ieee_types_header);
- parsed_len += hdr->len + sizeof(struct ieee_types_header);
- }
-
- /* parse only WPA vendor IE from tail, WMM IE is configured by
- * bss_config command
- */
- vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
- WLAN_OUI_TYPE_MICROSOFT_WPA,
- info->tail, info->tail_len);
- if (vendorhdr) {
- memcpy(gen_ie->ie_buffer + ie_len, vendorhdr,
- vendorhdr->len + sizeof(struct ieee_types_header));
- ie_len += vendorhdr->len + sizeof(struct ieee_types_header);
- }
-
- if (!ie_len) {
- kfree(gen_ie);
- return 0;
- }
-
- gen_ie->ie_index = cpu_to_le16(gen_idx);
- gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
- MGMT_MASK_PROBE_RESP |
- MGMT_MASK_ASSOC_RESP);
- gen_ie->ie_length = cpu_to_le16(ie_len);
-
- if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
- NULL, NULL)) {
- kfree(gen_ie);
- return -1;
- }
-
- priv->gen_idx = gen_idx;
- kfree(gen_ie);
- return 0;
-}
-
-/* This function parses different IEs-head & tail IEs, beacon IEs,
- * probe response IEs, association response IEs from cfg80211_ap_settings
- * function and sets these IE to FW.
- */
-int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
- struct cfg80211_beacon_data *info)
-{
- int ret;
-
- ret = mwifiex_uap_parse_tail_ies(priv, info);
-
- if (ret)
- return ret;
-
- return mwifiex_set_mgmt_beacon_data_ies(priv, info);
-}
-
-/* This function removes management IE set */
-int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
-{
- struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
- struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL;
- int ret = 0;
-
- if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) {
- gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
- if (!gen_ie)
- return -ENOMEM;
-
- gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
- gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
- gen_ie->ie_length = 0;
- if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
- NULL, &priv->proberesp_idx,
- NULL, &priv->assocresp_idx)) {
- ret = -1;
- goto done;
- }
-
- priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
- }
-
- if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
- beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
- if (!beacon_ie) {
- ret = -ENOMEM;
- goto done;
- }
- beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
- beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
- beacon_ie->ie_length = 0;
- }
- if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
- pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
- if (!pr_ie) {
- ret = -ENOMEM;
- goto done;
- }
- pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
- pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
- pr_ie->ie_length = 0;
- }
- if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
- ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
- if (!ar_ie) {
- ret = -ENOMEM;
- goto done;
- }
- ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
- ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
- ar_ie->ie_length = 0;
- }
-
- if (beacon_ie || pr_ie || ar_ie)
- ret = mwifiex_update_uap_custom_ie(priv,
- beacon_ie, &priv->beacon_idx,
- pr_ie, &priv->proberesp_idx,
- ar_ie, &priv->assocresp_idx);
-
-done:
- kfree(gen_ie);
- kfree(beacon_ie);
- kfree(pr_ie);
- kfree(ar_ie);
-
- return ret;
-}
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
deleted file mode 100644
index de74a7773fb6..000000000000
--- a/drivers/net/wireless/mwifiex/init.c
+++ /dev/null
@@ -1,782 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: HW/FW Initialization
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-
-/*
- * This function adds a BSS priority table to the table list.
- *
- * The function allocates a new BSS priority table node and adds it to
- * the end of BSS priority table list, kept in driver memory.
- */
-static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bss_prio_node *bss_prio;
- struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
- unsigned long flags;
-
- bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL);
- if (!bss_prio)
- return -ENOMEM;
-
- bss_prio->priv = priv;
- INIT_LIST_HEAD(&bss_prio->list);
-
- spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags);
- list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head);
- spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags);
-
- return 0;
-}
-
-static void wakeup_timer_fn(unsigned long data)
-{
- struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data;
-
- mwifiex_dbg(adapter, ERROR, "Firmware wakeup failed\n");
- adapter->hw_status = MWIFIEX_HW_STATUS_RESET;
- mwifiex_cancel_all_pending_cmd(adapter);
-
- if (adapter->if_ops.card_reset)
- adapter->if_ops.card_reset(adapter);
-}
-
-/*
- * This function initializes the private structure and sets default
- * values to the members.
- *
- * Additionally, it also initializes all the locks and sets up all the
- * lists.
- */
-int mwifiex_init_priv(struct mwifiex_private *priv)
-{
- u32 i;
-
- priv->media_connected = false;
- eth_broadcast_addr(priv->curr_addr);
- priv->port_open = false;
- priv->usb_port = MWIFIEX_USB_EP_DATA;
- priv->pkt_tx_ctrl = 0;
- priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->data_rate = 0; /* Initially indicate the rate as auto */
- priv->is_data_rate_auto = true;
- priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
- priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
-
- priv->sec_info.wep_enabled = 0;
- priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
- priv->sec_info.encryption_mode = 0;
- for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++)
- memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key));
- priv->wep_key_curr_index = 0;
- priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON |
- HostCmd_ACT_MAC_ETHERNETII_ENABLE;
-
- priv->beacon_period = 100; /* beacon interval */ ;
- priv->attempted_bss_desc = NULL;
- memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params));
- priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL;
-
- memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid));
- memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid));
- memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf));
- priv->assoc_rsp_size = 0;
- priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
- priv->atim_window = 0;
- priv->adhoc_state = ADHOC_IDLE;
- priv->tx_power_level = 0;
- priv->max_tx_power_level = 0;
- priv->min_tx_power_level = 0;
- priv->tx_rate = 0;
- priv->rxpd_htinfo = 0;
- priv->rxpd_rate = 0;
- priv->rate_bitmap = 0;
- priv->data_rssi_last = 0;
- priv->data_rssi_avg = 0;
- priv->data_nf_avg = 0;
- priv->data_nf_last = 0;
- priv->bcn_rssi_last = 0;
- priv->bcn_rssi_avg = 0;
- priv->bcn_nf_avg = 0;
- priv->bcn_nf_last = 0;
- memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie));
- memset(&priv->aes_key, 0, sizeof(priv->aes_key));
- priv->wpa_ie_len = 0;
- priv->wpa_is_gtk_set = false;
-
- memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf));
- priv->assoc_tlv_buf_len = 0;
- memset(&priv->wps, 0, sizeof(priv->wps));
- memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf));
- priv->gen_ie_buf_len = 0;
- memset(priv->vs_ie, 0, sizeof(priv->vs_ie));
-
- priv->wmm_required = true;
- priv->wmm_enabled = false;
- priv->wmm_qosinfo = 0;
- priv->curr_bcn_buf = NULL;
- priv->curr_bcn_size = 0;
- priv->wps_ie = NULL;
- priv->wps_ie_len = 0;
- priv->ap_11n_enabled = 0;
- memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg));
-
- priv->scan_block = false;
-
- priv->csa_chan = 0;
- priv->csa_expire_time = 0;
- priv->del_list_idx = 0;
- priv->hs2_enabled = false;
- priv->check_tdls_tx = false;
- memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
-
- mwifiex_init_11h_params(priv);
-
- return mwifiex_add_bss_prio_tbl(priv);
-}
-
-/*
- * This function allocates buffers for members of the adapter
- * structure.
- *
- * The memory allocated includes scan table, command buffers, and
- * sleep confirm command buffer. In addition, the queues are
- * also initialized.
- */
-static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter)
-{
- int ret;
-
- /* Allocate command buffer */
- ret = mwifiex_alloc_cmd_buffer(adapter);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "%s: failed to alloc cmd buffer\n",
- __func__);
- return -1;
- }
-
- adapter->sleep_cfm =
- dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
- + INTF_HEADER_LEN);
-
- if (!adapter->sleep_cfm) {
- mwifiex_dbg(adapter, ERROR,
- "%s: failed to alloc sleep cfm\t"
- " cmd buffer\n", __func__);
- return -1;
- }
- skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN);
-
- return 0;
-}
-
-/*
- * This function initializes the adapter structure and sets default
- * values to the members of adapter.
- *
- * This also initializes the WMM related parameters in the driver private
- * structures.
- */
-static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
-{
- struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL;
-
- skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm));
-
- adapter->cmd_sent = false;
-
- if (adapter->iface_type == MWIFIEX_SDIO)
- adapter->data_sent = true;
- else
- adapter->data_sent = false;
-
- adapter->cmd_resp_received = false;
- adapter->event_received = false;
- adapter->data_received = false;
-
- adapter->surprise_removed = false;
-
- adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
-
- adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
- adapter->ps_state = PS_STATE_AWAKE;
- adapter->need_to_wakeup = false;
-
- adapter->scan_mode = HostCmd_BSS_MODE_ANY;
- adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME;
- adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME;
- adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME;
- adapter->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME;
-
- adapter->scan_probes = 1;
-
- adapter->multiple_dtim = 1;
-
- adapter->local_listen_interval = 0; /* default value in firmware
- will be used */
-
- adapter->is_deep_sleep = false;
-
- adapter->delay_null_pkt = false;
- adapter->delay_to_ps = 1000;
- adapter->enhanced_ps_mode = PS_MODE_AUTO;
-
- adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by
- default */
- adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by
- default */
- adapter->pm_wakeup_card_req = false;
-
- adapter->pm_wakeup_fw_try = false;
-
- adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
-
- adapter->is_hs_configured = false;
- adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF);
- adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF;
- adapter->hs_cfg.gap = HS_CFG_GAP_DEF;
- adapter->hs_activated = false;
-
- memset(adapter->event_body, 0, sizeof(adapter->event_body));
- adapter->hw_dot_11n_dev_cap = 0;
- adapter->hw_dev_mcs_support = 0;
- adapter->sec_chan_offset = 0;
- adapter->adhoc_11n_enabled = false;
-
- mwifiex_wmm_init(adapter);
-
- sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
- adapter->sleep_cfm->data;
- memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
- sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
- sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len);
- sleep_cfm_buf->result = 0;
- sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
- sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
-
- memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params));
- memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
- adapter->tx_lock_flag = false;
- adapter->null_pkt_interval = 0;
- adapter->fw_bands = 0;
- adapter->config_bands = 0;
- adapter->adhoc_start_band = 0;
- adapter->scan_channels = NULL;
- adapter->fw_release_number = 0;
- adapter->fw_cap_info = 0;
- memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf));
- adapter->event_cause = 0;
- adapter->region_code = 0;
- adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT;
- adapter->adhoc_awake_period = 0;
- memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
- adapter->arp_filter_size = 0;
- adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
- adapter->key_api_major_ver = 0;
- adapter->key_api_minor_ver = 0;
- eth_broadcast_addr(adapter->perm_addr);
- adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM;
- adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM;
- adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM;
- adapter->active_scan_triggered = false;
- setup_timer(&adapter->wakeup_timer, wakeup_timer_fn,
- (unsigned long)adapter);
-}
-
-/*
- * This function sets trans_start per tx_queue
- */
-void mwifiex_set_trans_start(struct net_device *dev)
-{
- int i;
-
- for (i = 0; i < dev->num_tx_queues; i++)
- netdev_get_tx_queue(dev, i)->trans_start = jiffies;
-
- dev->trans_start = jiffies;
-}
-
-/*
- * This function wakes up all queues in net_device
- */
-void mwifiex_wake_up_net_dev_queue(struct net_device *netdev,
- struct mwifiex_adapter *adapter)
-{
- unsigned long dev_queue_flags;
- unsigned int i;
-
- spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags);
-
- for (i = 0; i < netdev->num_tx_queues; i++) {
- struct netdev_queue *txq = netdev_get_tx_queue(netdev, i);
-
- if (netif_tx_queue_stopped(txq))
- netif_tx_wake_queue(txq);
- }
-
- spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags);
-}
-
-/*
- * This function stops all queues in net_device
- */
-void mwifiex_stop_net_dev_queue(struct net_device *netdev,
- struct mwifiex_adapter *adapter)
-{
- unsigned long dev_queue_flags;
- unsigned int i;
-
- spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags);
-
- for (i = 0; i < netdev->num_tx_queues; i++) {
- struct netdev_queue *txq = netdev_get_tx_queue(netdev, i);
-
- if (!netif_tx_queue_stopped(txq))
- netif_tx_stop_queue(txq);
- }
-
- spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags);
-}
-
-/*
- * This function releases the lock variables and frees the locks and
- * associated locks.
- */
-static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
-{
- struct mwifiex_private *priv;
- s32 i, j;
-
- /* Free lists */
- list_del(&adapter->cmd_free_q);
- list_del(&adapter->cmd_pending_q);
- list_del(&adapter->scan_pending_q);
-
- for (i = 0; i < adapter->priv_num; i++)
- list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- priv = adapter->priv[i];
- for (j = 0; j < MAX_NUM_TID; ++j)
- list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
- list_del(&priv->tx_ba_stream_tbl_ptr);
- list_del(&priv->rx_reorder_tbl_ptr);
- list_del(&priv->sta_list);
- list_del(&priv->auto_tdls_list);
- }
- }
-}
-
-/*
- * This function performs cleanup for adapter structure.
- *
- * The cleanup is done recursively, by canceling all pending
- * commands, freeing the member buffers previously allocated
- * (command buffers, scan table buffer, sleep confirm command
- * buffer), stopping the timers and calling the cleanup routines
- * for every interface.
- */
-static void
-mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
-{
- int idx;
-
- if (!adapter) {
- pr_err("%s: adapter is NULL\n", __func__);
- return;
- }
-
- del_timer(&adapter->wakeup_timer);
- mwifiex_cancel_all_pending_cmd(adapter);
- wake_up_interruptible(&adapter->cmd_wait_q.wait);
- wake_up_interruptible(&adapter->hs_activate_wait_q);
-
- /* Free lock variables */
- mwifiex_free_lock_list(adapter);
-
- /* Free command buffer */
- mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n");
- mwifiex_free_cmd_buffer(adapter);
-
- for (idx = 0; idx < adapter->num_mem_types; idx++) {
- struct memory_type_mapping *entry =
- &adapter->mem_type_mapping_tbl[idx];
-
- if (entry->mem_ptr) {
- vfree(entry->mem_ptr);
- entry->mem_ptr = NULL;
- }
- entry->mem_size = 0;
- }
-
- if (adapter->drv_info_dump) {
- vfree(adapter->drv_info_dump);
- adapter->drv_info_dump = NULL;
- adapter->drv_info_size = 0;
- }
-
- if (adapter->sleep_cfm)
- dev_kfree_skb_any(adapter->sleep_cfm);
-}
-
-/*
- * This function intializes the lock variables and
- * the list heads.
- */
-int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
-{
- struct mwifiex_private *priv;
- s32 i, j;
-
- spin_lock_init(&adapter->mwifiex_lock);
- spin_lock_init(&adapter->int_lock);
- spin_lock_init(&adapter->main_proc_lock);
- spin_lock_init(&adapter->mwifiex_cmd_lock);
- spin_lock_init(&adapter->queue_lock);
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- priv = adapter->priv[i];
- spin_lock_init(&priv->rx_pkt_lock);
- spin_lock_init(&priv->wmm.ra_list_spinlock);
- spin_lock_init(&priv->curr_bcn_buf_lock);
- spin_lock_init(&priv->sta_list_spinlock);
- spin_lock_init(&priv->auto_tdls_lock);
- }
- }
-
- /* Initialize cmd_free_q */
- INIT_LIST_HEAD(&adapter->cmd_free_q);
- /* Initialize cmd_pending_q */
- INIT_LIST_HEAD(&adapter->cmd_pending_q);
- /* Initialize scan_pending_q */
- INIT_LIST_HEAD(&adapter->scan_pending_q);
-
- spin_lock_init(&adapter->cmd_free_q_lock);
- spin_lock_init(&adapter->cmd_pending_q_lock);
- spin_lock_init(&adapter->scan_pending_q_lock);
- spin_lock_init(&adapter->rx_proc_lock);
-
- skb_queue_head_init(&adapter->rx_data_q);
- skb_queue_head_init(&adapter->tx_data_q);
-
- for (i = 0; i < adapter->priv_num; ++i) {
- INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
- spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock);
- }
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (!adapter->priv[i])
- continue;
- priv = adapter->priv[i];
- for (j = 0; j < MAX_NUM_TID; ++j)
- INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
- INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
- INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
- INIT_LIST_HEAD(&priv->sta_list);
- INIT_LIST_HEAD(&priv->auto_tdls_list);
- skb_queue_head_init(&priv->tdls_txq);
- skb_queue_head_init(&priv->bypass_txq);
-
- spin_lock_init(&priv->tx_ba_stream_tbl_lock);
- spin_lock_init(&priv->rx_reorder_tbl_lock);
-
- spin_lock_init(&priv->ack_status_lock);
- idr_init(&priv->ack_status_frames);
- }
-
- return 0;
-}
-
-/*
- * This function initializes the firmware.
- *
- * The following operations are performed sequentially -
- * - Allocate adapter structure
- * - Initialize the adapter structure
- * - Initialize the private structure
- * - Add BSS priority tables to the adapter structure
- * - For each interface, send the init commands to firmware
- * - Send the first command in command pending queue, if available
- */
-int mwifiex_init_fw(struct mwifiex_adapter *adapter)
-{
- int ret;
- struct mwifiex_private *priv;
- u8 i, first_sta = true;
- int is_cmd_pend_q_empty;
- unsigned long flags;
-
- adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
-
- /* Allocate memory for member of adapter structure */
- ret = mwifiex_allocate_adapter(adapter);
- if (ret)
- return -1;
-
- /* Initialize adapter structure */
- mwifiex_init_adapter(adapter);
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- priv = adapter->priv[i];
-
- /* Initialize private structure */
- ret = mwifiex_init_priv(priv);
- if (ret)
- return -1;
- }
- }
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta,
- true);
- if (ret == -1)
- return -1;
-
- first_sta = false;
- }
- }
-
- spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
- is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
- if (!is_cmd_pend_q_empty) {
- /* Send the first command in queue and return */
- if (mwifiex_main_process(adapter) != -1)
- ret = -EINPROGRESS;
- } else {
- adapter->hw_status = MWIFIEX_HW_STATUS_READY;
- }
-
- return ret;
-}
-
-/*
- * This function deletes the BSS priority tables.
- *
- * The function traverses through all the allocated BSS priority nodes
- * in every BSS priority table and frees them.
- */
-static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv)
-{
- int i;
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bss_prio_node *bssprio_node, *tmp_node;
- struct list_head *head;
- spinlock_t *lock; /* bss priority lock */
- unsigned long flags;
-
- for (i = 0; i < adapter->priv_num; ++i) {
- head = &adapter->bss_prio_tbl[i].bss_prio_head;
- lock = &adapter->bss_prio_tbl[i].bss_prio_lock;
- mwifiex_dbg(adapter, INFO,
- "info: delete BSS priority table,\t"
- "bss_type = %d, bss_num = %d, i = %d,\t"
- "head = %p\n",
- priv->bss_type, priv->bss_num, i, head);
-
- {
- spin_lock_irqsave(lock, flags);
- if (list_empty(head)) {
- spin_unlock_irqrestore(lock, flags);
- continue;
- }
- list_for_each_entry_safe(bssprio_node, tmp_node, head,
- list) {
- if (bssprio_node->priv == priv) {
- mwifiex_dbg(adapter, INFO,
- "info: Delete\t"
- "node %p, next = %p\n",
- bssprio_node, tmp_node);
- list_del(&bssprio_node->list);
- kfree(bssprio_node);
- }
- }
- spin_unlock_irqrestore(lock, flags);
- }
- }
-}
-
-/*
- * This function frees the private structure, including cleans
- * up the TX and RX queues and frees the BSS priority tables.
- */
-void mwifiex_free_priv(struct mwifiex_private *priv)
-{
- mwifiex_clean_txrx(priv);
- mwifiex_delete_bss_prio_tbl(priv);
- mwifiex_free_curr_bcn(priv);
-}
-
-/*
- * This function is used to shutdown the driver.
- *
- * The following operations are performed sequentially -
- * - Check if already shut down
- * - Make sure the main process has stopped
- * - Clean up the Tx and Rx queues
- * - Delete BSS priority tables
- * - Free the adapter
- * - Notify completion
- */
-int
-mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
-{
- int ret = -EINPROGRESS;
- struct mwifiex_private *priv;
- s32 i;
- unsigned long flags;
- struct sk_buff *skb;
-
- /* mwifiex already shutdown */
- if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)
- return 0;
-
- adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING;
- /* wait for mwifiex_process to complete */
- if (adapter->mwifiex_processing) {
- mwifiex_dbg(adapter, WARN,
- "main process is still running\n");
- return ret;
- }
-
- /* cancel current command */
- if (adapter->curr_cmd) {
- mwifiex_dbg(adapter, WARN,
- "curr_cmd is still in processing\n");
- del_timer_sync(&adapter->cmd_timer);
- mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
- adapter->curr_cmd = NULL;
- }
-
- /* shut down mwifiex */
- mwifiex_dbg(adapter, MSG,
- "info: shutdown mwifiex...\n");
-
- /* Clean up Tx/Rx queues and delete BSS priority table */
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- priv = adapter->priv[i];
-
- mwifiex_clean_auto_tdls(priv);
- mwifiex_abort_cac(priv);
- mwifiex_clean_txrx(priv);
- mwifiex_delete_bss_prio_tbl(priv);
- }
- }
-
- atomic_set(&adapter->tx_queued, 0);
- while ((skb = skb_dequeue(&adapter->tx_data_q)))
- mwifiex_write_data_complete(adapter, skb, 0, 0);
-
- spin_lock_irqsave(&adapter->rx_proc_lock, flags);
-
- while ((skb = skb_dequeue(&adapter->rx_data_q))) {
- struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
-
- atomic_dec(&adapter->rx_pending);
- priv = adapter->priv[rx_info->bss_num];
- if (priv)
- priv->stats.rx_dropped++;
-
- dev_kfree_skb_any(skb);
- }
-
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
-
- spin_lock(&adapter->mwifiex_lock);
-
- mwifiex_adapter_cleanup(adapter);
-
- spin_unlock(&adapter->mwifiex_lock);
-
- /* Notify completion */
- ret = mwifiex_shutdown_fw_complete(adapter);
-
- return ret;
-}
-
-/*
- * This function downloads the firmware to the card.
- *
- * The actual download is preceded by two sanity checks -
- * - Check if firmware is already running
- * - Check if the interface is the winner to download the firmware
- *
- * ...and followed by another -
- * - Check if the firmware is downloaded successfully
- *
- * After download is successfully completed, the host interrupts are enabled.
- */
-int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
- struct mwifiex_fw_image *pmfw)
-{
- int ret;
- u32 poll_num = 1;
-
- if (adapter->if_ops.check_fw_status) {
- adapter->winner = 0;
-
- /* check if firmware is already running */
- ret = adapter->if_ops.check_fw_status(adapter, poll_num);
- if (!ret) {
- mwifiex_dbg(adapter, MSG,
- "WLAN FW already running! Skip FW dnld\n");
- return 0;
- }
-
- poll_num = MAX_FIRMWARE_POLL_TRIES;
-
- /* check if we are the winner for downloading FW */
- if (!adapter->winner) {
- mwifiex_dbg(adapter, MSG,
- "FW already running! Skip FW dnld\n");
- goto poll_fw;
- }
- }
-
- if (pmfw) {
- /* Download firmware with helper */
- ret = adapter->if_ops.prog_fw(adapter, pmfw);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "prog_fw failed ret=%#x\n", ret);
- return ret;
- }
- }
-
-poll_fw:
- /* Check if the firmware is downloaded successfully or not */
- ret = adapter->if_ops.check_fw_status(adapter, poll_num);
- if (ret)
- mwifiex_dbg(adapter, ERROR,
- "FW failed to be active in time\n");
-
- return ret;
-}
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
deleted file mode 100644
index 3cda1f956f0b..000000000000
--- a/drivers/net/wireless/mwifiex/join.c
+++ /dev/null
@@ -1,1525 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: association and ad-hoc start/join
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-#include "11ac.h"
-
-#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9)))
-
-/*
- * Append a generic IE as a pass through TLV to a TLV buffer.
- *
- * This function is called from the network join command preparation routine.
- *
- * If the IE buffer has been setup by the application, this routine appends
- * the buffer as a pass through TLV type to the request.
- */
-static int
-mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer)
-{
- int ret_len = 0;
- struct mwifiex_ie_types_header ie_header;
-
- /* Null Checks */
- if (!buffer)
- return 0;
- if (!(*buffer))
- return 0;
-
- /*
- * If there is a generic ie buffer setup, append it to the return
- * parameter buffer pointer.
- */
- if (priv->gen_ie_buf_len) {
- mwifiex_dbg(priv->adapter, INFO,
- "info: %s: append generic ie len %d to %p\n",
- __func__, priv->gen_ie_buf_len, *buffer);
-
- /* Wrap the generic IE buffer with a pass through TLV type */
- ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
- ie_header.len = cpu_to_le16(priv->gen_ie_buf_len);
- memcpy(*buffer, &ie_header, sizeof(ie_header));
-
- /* Increment the return size and the return buffer pointer
- param */
- *buffer += sizeof(ie_header);
- ret_len += sizeof(ie_header);
-
- /* Copy the generic IE buffer to the output buffer, advance
- pointer */
- memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len);
-
- /* Increment the return size and the return buffer pointer
- param */
- *buffer += priv->gen_ie_buf_len;
- ret_len += priv->gen_ie_buf_len;
-
- /* Reset the generic IE buffer */
- priv->gen_ie_buf_len = 0;
- }
-
- /* return the length appended to the buffer */
- return ret_len;
-}
-
-/*
- * Append TSF tracking info from the scan table for the target AP.
- *
- * This function is called from the network join command preparation routine.
- *
- * The TSF table TSF sent to the firmware contains two TSF values:
- * - The TSF of the target AP from its previous beacon/probe response
- * - The TSF timestamp of our local MAC at the time we observed the
- * beacon/probe response.
- *
- * The firmware uses the timestamp values to set an initial TSF value
- * in the MAC for the new association after a reassociation attempt.
- */
-static int
-mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer,
- struct mwifiex_bssdescriptor *bss_desc)
-{
- struct mwifiex_ie_types_tsf_timestamp tsf_tlv;
- __le64 tsf_val;
-
- /* Null Checks */
- if (buffer == NULL)
- return 0;
- if (*buffer == NULL)
- return 0;
-
- memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp));
-
- tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP);
- tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val));
-
- memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header));
- *buffer += sizeof(tsf_tlv.header);
-
- /* TSF at the time when beacon/probe_response was received */
- tsf_val = cpu_to_le64(bss_desc->fw_tsf);
- memcpy(*buffer, &tsf_val, sizeof(tsf_val));
- *buffer += sizeof(tsf_val);
-
- tsf_val = cpu_to_le64(bss_desc->timestamp);
-
- mwifiex_dbg(priv->adapter, INFO,
- "info: %s: TSF offset calc: %016llx - %016llx\n",
- __func__, bss_desc->timestamp, bss_desc->fw_tsf);
-
- memcpy(*buffer, &tsf_val, sizeof(tsf_val));
- *buffer += sizeof(tsf_val);
-
- return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val));
-}
-
-/*
- * This function finds out the common rates between rate1 and rate2.
- *
- * It will fill common rates in rate1 as output if found.
- *
- * NOTE: Setting the MSB of the basic rates needs to be taken
- * care of, either before or after calling this function.
- */
-static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1,
- u32 rate1_size, u8 *rate2, u32 rate2_size)
-{
- int ret;
- u8 *ptr = rate1, *tmp;
- u32 i, j;
-
- tmp = kmemdup(rate1, rate1_size, GFP_KERNEL);
- if (!tmp) {
- mwifiex_dbg(priv->adapter, ERROR, "failed to alloc tmp buf\n");
- return -ENOMEM;
- }
-
- memset(rate1, 0, rate1_size);
-
- for (i = 0; i < rate2_size && rate2[i]; i++) {
- for (j = 0; j < rate1_size && tmp[j]; j++) {
- /* Check common rate, excluding the bit for
- basic rate */
- if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) {
- *rate1++ = tmp[j];
- break;
- }
- }
- }
-
- mwifiex_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n",
- priv->data_rate);
-
- if (!priv->is_data_rate_auto) {
- while (*ptr) {
- if ((*ptr & 0x7f) == priv->data_rate) {
- ret = 0;
- goto done;
- }
- ptr++;
- }
- mwifiex_dbg(priv->adapter, ERROR,
- "previously set fixed data rate %#x\t"
- "is not compatible with the network\n",
- priv->data_rate);
-
- ret = -1;
- goto done;
- }
-
- ret = 0;
-done:
- kfree(tmp);
- return ret;
-}
-
-/*
- * This function creates the intersection of the rates supported by a
- * target BSS and our adapter settings for use in an assoc/join command.
- */
-static int
-mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc,
- u8 *out_rates, u32 *out_rates_size)
-{
- u8 card_rates[MWIFIEX_SUPPORTED_RATES];
- u32 card_rates_size;
-
- /* Copy AP supported rates */
- memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES);
- /* Get the STA supported rates */
- card_rates_size = mwifiex_get_active_data_rates(priv, card_rates);
- /* Get the common rates between AP and STA supported rates */
- if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES,
- card_rates, card_rates_size)) {
- *out_rates_size = 0;
- mwifiex_dbg(priv->adapter, ERROR,
- "%s: cannot get common rates\n",
- __func__);
- return -1;
- }
-
- *out_rates_size =
- min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES);
-
- return 0;
-}
-
-/*
- * This function appends a WPS IE. It is called from the network join command
- * preparation routine.
- *
- * If the IE buffer has been setup by the application, this routine appends
- * the buffer as a WPS TLV type to the request.
- */
-static int
-mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer)
-{
- int retLen = 0;
- struct mwifiex_ie_types_header ie_header;
-
- if (!buffer || !*buffer)
- return 0;
-
- /*
- * If there is a wps ie buffer setup, append it to the return
- * parameter buffer pointer.
- */
- if (priv->wps_ie_len) {
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: append wps ie %d to %p\n",
- priv->wps_ie_len, *buffer);
-
- /* Wrap the generic IE buffer with a pass through TLV type */
- ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE);
- ie_header.len = cpu_to_le16(priv->wps_ie_len);
- memcpy(*buffer, &ie_header, sizeof(ie_header));
- *buffer += sizeof(ie_header);
- retLen += sizeof(ie_header);
-
- memcpy(*buffer, priv->wps_ie, priv->wps_ie_len);
- *buffer += priv->wps_ie_len;
- retLen += priv->wps_ie_len;
-
- }
-
- kfree(priv->wps_ie);
- priv->wps_ie_len = 0;
- return retLen;
-}
-
-/*
- * This function appends a WAPI IE.
- *
- * This function is called from the network join command preparation routine.
- *
- * If the IE buffer has been setup by the application, this routine appends
- * the buffer as a WAPI TLV type to the request.
- */
-static int
-mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer)
-{
- int retLen = 0;
- struct mwifiex_ie_types_header ie_header;
-
- /* Null Checks */
- if (buffer == NULL)
- return 0;
- if (*buffer == NULL)
- return 0;
-
- /*
- * If there is a wapi ie buffer setup, append it to the return
- * parameter buffer pointer.
- */
- if (priv->wapi_ie_len) {
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: append wapi ie %d to %p\n",
- priv->wapi_ie_len, *buffer);
-
- /* Wrap the generic IE buffer with a pass through TLV type */
- ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE);
- ie_header.len = cpu_to_le16(priv->wapi_ie_len);
- memcpy(*buffer, &ie_header, sizeof(ie_header));
-
- /* Increment the return size and the return buffer pointer
- param */
- *buffer += sizeof(ie_header);
- retLen += sizeof(ie_header);
-
- /* Copy the wapi IE buffer to the output buffer, advance
- pointer */
- memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len);
-
- /* Increment the return size and the return buffer pointer
- param */
- *buffer += priv->wapi_ie_len;
- retLen += priv->wapi_ie_len;
-
- }
- /* return the length appended to the buffer */
- return retLen;
-}
-
-/*
- * This function appends rsn ie tlv for wpa/wpa2 security modes.
- * It is called from the network join command preparation routine.
- */
-static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv,
- u8 **buffer)
-{
- struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv;
- int rsn_ie_len;
-
- if (!buffer || !(*buffer))
- return 0;
-
- rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer);
- rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]);
- rsn_ie_tlv->header.type = cpu_to_le16(
- le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF);
- rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]);
- rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len)
- & 0x00FF);
- if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2))
- memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2],
- le16_to_cpu(rsn_ie_tlv->header.len));
- else
- return -1;
-
- rsn_ie_len = sizeof(rsn_ie_tlv->header) +
- le16_to_cpu(rsn_ie_tlv->header.len);
- *buffer += rsn_ie_len;
-
- return rsn_ie_len;
-}
-
-/*
- * This function prepares command for association.
- *
- * This sets the following parameters -
- * - Peer MAC address
- * - Listen interval
- * - Beacon interval
- * - Capability information
- *
- * ...and the following TLVs, as required -
- * - SSID TLV
- * - PHY TLV
- * - SS TLV
- * - Rates TLV
- * - Authentication TLV
- * - Channel TLV
- * - WPA/WPA2 IE
- * - 11n TLV
- * - Vendor specific TLV
- * - WMM TLV
- * - WAPI IE
- * - Generic IE
- * - TSF TLV
- *
- * Preparation also includes -
- * - Setting command ID and proper size
- * - Ensuring correct endian-ness
- */
-int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct mwifiex_bssdescriptor *bss_desc)
-{
- struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate;
- struct mwifiex_ie_types_ssid_param_set *ssid_tlv;
- struct mwifiex_ie_types_phy_param_set *phy_tlv;
- struct mwifiex_ie_types_ss_param_set *ss_tlv;
- struct mwifiex_ie_types_rates_param_set *rates_tlv;
- struct mwifiex_ie_types_auth_type *auth_tlv;
- struct mwifiex_ie_types_chan_list_param_set *chan_tlv;
- u8 rates[MWIFIEX_SUPPORTED_RATES];
- u32 rates_size;
- u16 tmp_cap;
- u8 *pos;
- int rsn_ie_len = 0;
-
- pos = (u8 *) assoc;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE);
-
- /* Save so we know which BSS Desc to use in the response handler */
- priv->attempted_bss_desc = bss_desc;
-
- memcpy(assoc->peer_sta_addr,
- bss_desc->mac_address, sizeof(assoc->peer_sta_addr));
- pos += sizeof(assoc->peer_sta_addr);
-
- /* Set the listen interval */
- assoc->listen_interval = cpu_to_le16(priv->listen_interval);
- /* Set the beacon period */
- assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period);
-
- pos += sizeof(assoc->cap_info_bitmap);
- pos += sizeof(assoc->listen_interval);
- pos += sizeof(assoc->beacon_period);
- pos += sizeof(assoc->dtim_period);
-
- ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos;
- ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID);
- ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len);
- memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid,
- le16_to_cpu(ssid_tlv->header.len));
- pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len);
-
- phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos;
- phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS);
- phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set));
- memcpy(&phy_tlv->fh_ds.ds_param_set,
- &bss_desc->phy_param_set.ds_param_set.current_chan,
- sizeof(phy_tlv->fh_ds.ds_param_set));
- pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len);
-
- ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos;
- ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS);
- ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set));
- pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len);
-
- /* Get the common rates supported between the driver and the BSS Desc */
- if (mwifiex_setup_rates_from_bssdesc
- (priv, bss_desc, rates, &rates_size))
- return -1;
-
- /* Save the data rates into Current BSS state structure */
- priv->curr_bss_params.num_of_rates = rates_size;
- memcpy(&priv->curr_bss_params.data_rates, rates, rates_size);
-
- /* Setup the Rates TLV in the association command */
- rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos;
- rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
- rates_tlv->header.len = cpu_to_le16((u16) rates_size);
- memcpy(rates_tlv->rates, rates, rates_size);
- pos += sizeof(rates_tlv->header) + rates_size;
- mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_CMD: rates size = %d\n",
- rates_size);
-
- /* Add the Authentication type to be used for Auth frames */
- auth_tlv = (struct mwifiex_ie_types_auth_type *) pos;
- auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
- auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type));
- if (priv->sec_info.wep_enabled)
- auth_tlv->auth_type = cpu_to_le16(
- (u16) priv->sec_info.authentication_mode);
- else
- auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM);
-
- pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len);
-
- if (IS_SUPPORT_MULTI_BANDS(priv->adapter) &&
- !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
- (!bss_desc->disable_11n) &&
- (priv->adapter->config_bands & BAND_GN ||
- priv->adapter->config_bands & BAND_AN) &&
- (bss_desc->bcn_ht_cap)
- )
- ) {
- /* Append a channel TLV for the channel the attempted AP was
- found on */
- chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos;
- chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
- chan_tlv->header.len =
- cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set));
-
- memset(chan_tlv->chan_scan_param, 0x00,
- sizeof(struct mwifiex_chan_scan_param_set));
- chan_tlv->chan_scan_param[0].chan_number =
- (bss_desc->phy_param_set.ds_param_set.current_chan);
- mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Chan = %d\n",
- chan_tlv->chan_scan_param[0].chan_number);
-
- chan_tlv->chan_scan_param[0].radio_type =
- mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
-
- mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Band = %d\n",
- chan_tlv->chan_scan_param[0].radio_type);
- pos += sizeof(chan_tlv->header) +
- sizeof(struct mwifiex_chan_scan_param_set);
- }
-
- if (!priv->wps.session_enable) {
- if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
- rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos);
-
- if (rsn_ie_len == -1)
- return -1;
- }
-
- if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
- (!bss_desc->disable_11n) &&
- (priv->adapter->config_bands & BAND_GN ||
- priv->adapter->config_bands & BAND_AN))
- mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos);
-
- if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
- !bss_desc->disable_11n && !bss_desc->disable_11ac &&
- priv->adapter->config_bands & BAND_AAC)
- mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos);
-
- /* Append vendor specific IE TLV */
- mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos);
-
- mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie,
- bss_desc->bcn_ht_cap);
- if (priv->sec_info.wapi_enabled && priv->wapi_ie_len)
- mwifiex_cmd_append_wapi_ie(priv, &pos);
-
- if (priv->wps.session_enable && priv->wps_ie_len)
- mwifiex_cmd_append_wps_ie(priv, &pos);
-
- mwifiex_cmd_append_generic_ie(priv, &pos);
-
- 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 */
- tmp_cap = bss_desc->cap_info_bitmap;
-
- if (priv->adapter->config_bands == BAND_B)
- tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
-
- tmp_cap &= CAPINFO_MASK;
- mwifiex_dbg(priv->adapter, INFO,
- "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n",
- tmp_cap, CAPINFO_MASK);
- assoc->cap_info_bitmap = cpu_to_le16(tmp_cap);
-
- return 0;
-}
-
-static const char *assoc_failure_reason_to_str(u16 cap_info)
-{
- switch (cap_info) {
- case CONNECT_ERR_AUTH_ERR_STA_FAILURE:
- return "CONNECT_ERR_AUTH_ERR_STA_FAILURE";
- case CONNECT_ERR_AUTH_MSG_UNHANDLED:
- return "CONNECT_ERR_AUTH_MSG_UNHANDLED";
- case CONNECT_ERR_ASSOC_ERR_TIMEOUT:
- return "CONNECT_ERR_ASSOC_ERR_TIMEOUT";
- case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED:
- return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED";
- case CONNECT_ERR_STA_FAILURE:
- return "CONNECT_ERR_STA_FAILURE";
- }
-
- return "Unknown connect failure";
-}
-/*
- * Association firmware command response handler
- *
- * The response buffer for the association command has the following
- * memory layout.
- *
- * For cases where an association response was not received (indicated
- * by the CapInfo and AId field):
- *
- * .------------------------------------------------------------.
- * | Header(4 * sizeof(t_u16)): Standard command response hdr |
- * .------------------------------------------------------------.
- * | cap_info/Error Return(t_u16): |
- * | 0xFFFF(-1): Internal error |
- * | 0xFFFE(-2): Authentication unhandled message |
- * | 0xFFFD(-3): Authentication refused |
- * | 0xFFFC(-4): Timeout waiting for AP response |
- * .------------------------------------------------------------.
- * | status_code(t_u16): |
- * | If cap_info is -1: |
- * | An internal firmware failure prevented the |
- * | command from being processed. The status_code |
- * | will be set to 1. |
- * | |
- * | If cap_info is -2: |
- * | An authentication frame was received but was |
- * | not handled by the firmware. IEEE Status |
- * | code for the failure is returned. |
- * | |
- * | If cap_info is -3: |
- * | An authentication frame was received and the |
- * | status_code is the IEEE Status reported in the |
- * | response. |
- * | |
- * | If cap_info is -4: |
- * | (1) Association response timeout |
- * | (2) Authentication response timeout |
- * .------------------------------------------------------------.
- * | a_id(t_u16): 0xFFFF |
- * .------------------------------------------------------------.
- *
- *
- * For cases where an association response was received, the IEEE
- * standard association response frame is returned:
- *
- * .------------------------------------------------------------.
- * | Header(4 * sizeof(t_u16)): Standard command response hdr |
- * .------------------------------------------------------------.
- * | cap_info(t_u16): IEEE Capability |
- * .------------------------------------------------------------.
- * | status_code(t_u16): IEEE Status Code |
- * .------------------------------------------------------------.
- * | a_id(t_u16): IEEE Association ID |
- * .------------------------------------------------------------.
- * | IEEE IEs(variable): Any received IEs comprising the |
- * | remaining portion of a received |
- * | association response frame. |
- * .------------------------------------------------------------.
- *
- * For simplistic handling, the status_code field can be used to determine
- * an association success (0) or failure (non-zero).
- */
-int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- int ret = 0;
- struct ieee_types_assoc_rsp *assoc_rsp;
- struct mwifiex_bssdescriptor *bss_desc;
- bool enable_data = true;
- u16 cap_info, status_code, aid;
-
- assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params;
-
- cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap);
- status_code = le16_to_cpu(assoc_rsp->status_code);
- aid = le16_to_cpu(assoc_rsp->a_id);
-
- if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
- dev_err(priv->adapter->dev,
- "invalid AID value 0x%x; bits 15:14 not set\n",
- aid);
-
- aid &= ~(BIT(15) | BIT(14));
-
- priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
- sizeof(priv->assoc_rsp_buf));
-
- memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
-
- assoc_rsp->a_id = cpu_to_le16(aid);
-
- if (status_code) {
- priv->adapter->dbg.num_cmd_assoc_failure++;
- mwifiex_dbg(priv->adapter, ERROR,
- "ASSOC_RESP: failed,\t"
- "status code=%d err=%#x a_id=%#x\n",
- status_code, cap_info,
- le16_to_cpu(assoc_rsp->a_id));
-
- mwifiex_dbg(priv->adapter, ERROR, "assoc failure: reason %s\n",
- assoc_failure_reason_to_str(cap_info));
- if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) {
- if (status_code == MWIFIEX_ASSOC_CMD_FAILURE_AUTH) {
- ret = WLAN_STATUS_AUTH_TIMEOUT;
- mwifiex_dbg(priv->adapter, ERROR,
- "ASSOC_RESP: AUTH timeout\n");
- } else {
- ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
- mwifiex_dbg(priv->adapter, ERROR,
- "ASSOC_RESP: UNSPECIFIED failure\n");
- }
- } else {
- ret = status_code;
- }
-
- goto done;
- }
-
- /* Send a Media Connected event, according to the Spec */
- priv->media_connected = true;
-
- priv->adapter->ps_state = PS_STATE_AWAKE;
- priv->adapter->pps_uapsd_mode = false;
- priv->adapter->tx_lock_flag = false;
-
- /* Set the attempted BSSID Index to current */
- bss_desc = priv->attempted_bss_desc;
-
- mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: %s\n",
- bss_desc->ssid.ssid);
-
- /* Make a copy of current BSSID descriptor */
- memcpy(&priv->curr_bss_params.bss_descriptor,
- bss_desc, sizeof(struct mwifiex_bssdescriptor));
-
- /* Update curr_bss_params */
- priv->curr_bss_params.bss_descriptor.channel
- = bss_desc->phy_param_set.ds_param_set.current_chan;
-
- priv->curr_bss_params.band = (u8) bss_desc->bss_band;
-
- if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)
- priv->curr_bss_params.wmm_enabled = true;
- else
- priv->curr_bss_params.wmm_enabled = false;
-
- if ((priv->wmm_required || bss_desc->bcn_ht_cap) &&
- priv->curr_bss_params.wmm_enabled)
- priv->wmm_enabled = true;
- else
- priv->wmm_enabled = false;
-
- priv->curr_bss_params.wmm_uapsd_enabled = false;
-
- if (priv->wmm_enabled)
- priv->curr_bss_params.wmm_uapsd_enabled
- = ((bss_desc->wmm_ie.qos_info_bitmap &
- IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0);
-
- mwifiex_dbg(priv->adapter, INFO,
- "info: ASSOC_RESP: curr_pkt_filter is %#x\n",
- priv->curr_pkt_filter);
- if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
- priv->wpa_is_gtk_set = false;
-
- if (priv->wmm_enabled) {
- /* Don't re-enable carrier until we get the WMM_GET_STATUS
- event */
- enable_data = false;
- } else {
- /* Since WMM is not enabled, setup the queues with the
- defaults */
- mwifiex_wmm_setup_queue_priorities(priv, NULL);
- mwifiex_wmm_setup_ac_downgrade(priv);
- }
-
- if (enable_data)
- mwifiex_dbg(priv->adapter, INFO,
- "info: post association, re-enabling data flow\n");
-
- /* Reset SNR/NF/RSSI values */
- priv->data_rssi_last = 0;
- priv->data_nf_last = 0;
- priv->data_rssi_avg = 0;
- priv->data_nf_avg = 0;
- priv->bcn_rssi_last = 0;
- priv->bcn_nf_last = 0;
- priv->bcn_rssi_avg = 0;
- priv->bcn_nf_avg = 0;
- priv->rxpd_rate = 0;
- priv->rxpd_htinfo = 0;
-
- mwifiex_save_curr_bcn(priv);
-
- priv->adapter->dbg.num_cmd_assoc_success++;
-
- mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: associated\n");
-
- /* Add the ra_list here for infra mode as there will be only 1 ra
- always */
- mwifiex_ralist_add(priv,
- priv->curr_bss_params.bss_descriptor.mac_address);
-
- if (!netif_carrier_ok(priv->netdev))
- netif_carrier_on(priv->netdev);
- mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
-
- if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
- priv->scan_block = true;
- else
- priv->port_open = true;
-
-done:
- /* Need to indicate IOCTL complete */
- if (adapter->curr_cmd->wait_q_enabled) {
- if (ret)
- adapter->cmd_wait_q.status = -1;
- else
- adapter->cmd_wait_q.status = 0;
- }
-
- return ret;
-}
-
-/*
- * This function prepares command for ad-hoc start.
- *
- * Driver will fill up SSID, BSS mode, IBSS parameters, physical
- * parameters, probe delay, and capability information. Firmware
- * will fill up beacon period, basic rates and operational rates.
- *
- * In addition, the following TLVs are added -
- * - Channel TLV
- * - Vendor specific IE
- * - WPA/WPA2 IE
- * - HT Capabilities IE
- * - HT Information IE
- *
- * Preparation also includes -
- * - Setting command ID and proper size
- * - Ensuring correct endian-ness
- */
-int
-mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct cfg80211_ssid *req_ssid)
-{
- int rsn_ie_len = 0;
- struct mwifiex_adapter *adapter = priv->adapter;
- struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start =
- &cmd->params.adhoc_start;
- struct mwifiex_bssdescriptor *bss_desc;
- u32 cmd_append_size = 0;
- u32 i;
- u16 tmp_cap;
- struct mwifiex_ie_types_chan_list_param_set *chan_tlv;
- u8 radio_type;
-
- struct mwifiex_ie_types_htcap *ht_cap;
- struct mwifiex_ie_types_htinfo *ht_info;
- u8 *pos = (u8 *) adhoc_start +
- sizeof(struct host_cmd_ds_802_11_ad_hoc_start);
-
- if (!adapter)
- return -1;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START);
-
- bss_desc = &priv->curr_bss_params.bss_descriptor;
- priv->attempted_bss_desc = bss_desc;
-
- /*
- * Fill in the parameters for 2 data structures:
- * 1. struct host_cmd_ds_802_11_ad_hoc_start command
- * 2. bss_desc
- * Driver will fill up SSID, bss_mode,IBSS param, Physical Param,
- * probe delay, and Cap info.
- * Firmware will fill up beacon period, Basic rates
- * and operational rates.
- */
-
- memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN);
-
- memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len);
-
- mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: SSID = %s\n",
- adhoc_start->ssid);
-
- memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN);
- memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len);
-
- bss_desc->ssid.ssid_len = req_ssid->ssid_len;
-
- /* Set the BSS mode */
- adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS;
- bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
- adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period);
- bss_desc->beacon_period = priv->beacon_period;
-
- /* Set Physical param set */
-/* Parameter IE Id */
-#define DS_PARA_IE_ID 3
-/* Parameter IE length */
-#define DS_PARA_IE_LEN 1
-
- adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID;
- adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN;
-
- if (!mwifiex_get_cfp(priv, adapter->adhoc_start_band,
- (u16) priv->adhoc_channel, 0)) {
- struct mwifiex_chan_freq_power *cfp;
- cfp = mwifiex_get_cfp(priv, adapter->adhoc_start_band,
- FIRST_VALID_CHANNEL, 0);
- if (cfp)
- priv->adhoc_channel = (u8) cfp->channel;
- }
-
- if (!priv->adhoc_channel) {
- mwifiex_dbg(adapter, ERROR,
- "ADHOC_S_CMD: adhoc_channel cannot be 0\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, INFO,
- "info: ADHOC_S_CMD: creating ADHOC on channel %d\n",
- priv->adhoc_channel);
-
- priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel;
- priv->curr_bss_params.band = adapter->adhoc_start_band;
-
- bss_desc->channel = priv->adhoc_channel;
- adhoc_start->phy_param_set.ds_param_set.current_chan =
- priv->adhoc_channel;
-
- memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set,
- sizeof(union ieee_types_phy_param_set));
-
- /* Set IBSS param set */
-/* IBSS parameter IE Id */
-#define IBSS_PARA_IE_ID 6
-/* IBSS parameter IE length */
-#define IBSS_PARA_IE_LEN 2
-
- adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID;
- adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN;
- adhoc_start->ss_param_set.ibss_param_set.atim_window
- = cpu_to_le16(priv->atim_window);
- memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set,
- sizeof(union ieee_types_ss_param_set));
-
- /* Set Capability info */
- bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS;
- tmp_cap = WLAN_CAPABILITY_IBSS;
-
- /* Set up privacy in bss_desc */
- if (priv->sec_info.encryption_mode) {
- /* Ad-Hoc capability privacy on */
- mwifiex_dbg(adapter, INFO,
- "info: ADHOC_S_CMD: wep_status set privacy to WEP\n");
- bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
- tmp_cap |= WLAN_CAPABILITY_PRIVACY;
- } else {
- mwifiex_dbg(adapter, INFO,
- "info: ADHOC_S_CMD: wep_status NOT set,\t"
- "setting privacy to ACCEPT ALL\n");
- bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
- }
-
- memset(adhoc_start->data_rate, 0, sizeof(adhoc_start->data_rate));
- mwifiex_get_active_data_rates(priv, adhoc_start->data_rate);
- if ((adapter->adhoc_start_band & BAND_G) &&
- (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) {
- if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &priv->curr_pkt_filter, false)) {
- mwifiex_dbg(adapter, ERROR,
- "ADHOC_S_CMD: G Protection config failed\n");
- return -1;
- }
- }
- /* Find the last non zero */
- for (i = 0; i < sizeof(adhoc_start->data_rate); i++)
- if (!adhoc_start->data_rate[i])
- break;
-
- priv->curr_bss_params.num_of_rates = i;
-
- /* Copy the ad-hoc creating rates into Current BSS rate structure */
- memcpy(&priv->curr_bss_params.data_rates,
- &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates);
-
- mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: rates=%4ph\n",
- adhoc_start->data_rate);
-
- mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n");
-
- if (IS_SUPPORT_MULTI_BANDS(adapter)) {
- /* Append a channel TLV */
- chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos;
- chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
- chan_tlv->header.len =
- cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set));
-
- memset(chan_tlv->chan_scan_param, 0x00,
- sizeof(struct mwifiex_chan_scan_param_set));
- chan_tlv->chan_scan_param[0].chan_number =
- (u8) priv->curr_bss_params.bss_descriptor.channel;
-
- mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Chan = %d\n",
- chan_tlv->chan_scan_param[0].chan_number);
-
- chan_tlv->chan_scan_param[0].radio_type
- = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
- if (adapter->adhoc_start_band & BAND_GN ||
- adapter->adhoc_start_band & BAND_AN) {
- if (adapter->sec_chan_offset ==
- IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
- chan_tlv->chan_scan_param[0].radio_type |=
- (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4);
- else if (adapter->sec_chan_offset ==
- IEEE80211_HT_PARAM_CHA_SEC_BELOW)
- chan_tlv->chan_scan_param[0].radio_type |=
- (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4);
- }
- mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Band = %d\n",
- chan_tlv->chan_scan_param[0].radio_type);
- pos += sizeof(chan_tlv->header) +
- sizeof(struct mwifiex_chan_scan_param_set);
- cmd_append_size +=
- sizeof(chan_tlv->header) +
- sizeof(struct mwifiex_chan_scan_param_set);
- }
-
- /* Append vendor specific IE TLV */
- cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv,
- MWIFIEX_VSIE_MASK_ADHOC, &pos);
-
- if (priv->sec_info.wpa_enabled) {
- rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos);
- if (rsn_ie_len == -1)
- return -1;
- cmd_append_size += rsn_ie_len;
- }
-
- if (adapter->adhoc_11n_enabled) {
- /* Fill HT CAPABILITY */
- ht_cap = (struct mwifiex_ie_types_htcap *) pos;
- memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap));
- ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
- ht_cap->header.len =
- cpu_to_le16(sizeof(struct ieee80211_ht_cap));
- radio_type = mwifiex_band_to_radio_type(
- priv->adapter->config_bands);
- mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
-
- if (adapter->sec_chan_offset ==
- IEEE80211_HT_PARAM_CHA_SEC_NONE) {
- u16 tmp_ht_cap;
-
- tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info);
- tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40;
- ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap);
- }
-
- pos += sizeof(struct mwifiex_ie_types_htcap);
- cmd_append_size += sizeof(struct mwifiex_ie_types_htcap);
-
- /* Fill HT INFORMATION */
- ht_info = (struct mwifiex_ie_types_htinfo *) pos;
- memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo));
- ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION);
- ht_info->header.len =
- cpu_to_le16(sizeof(struct ieee80211_ht_operation));
-
- ht_info->ht_oper.primary_chan =
- (u8) priv->curr_bss_params.bss_descriptor.channel;
- if (adapter->sec_chan_offset) {
- ht_info->ht_oper.ht_param = adapter->sec_chan_offset;
- ht_info->ht_oper.ht_param |=
- IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
- }
- ht_info->ht_oper.operation_mode =
- cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
- ht_info->ht_oper.basic_set[0] = 0xff;
- pos += sizeof(struct mwifiex_ie_types_htinfo);
- cmd_append_size +=
- sizeof(struct mwifiex_ie_types_htinfo);
- }
-
- cmd->size =
- cpu_to_le16((u16)(sizeof(struct host_cmd_ds_802_11_ad_hoc_start)
- + S_DS_GEN + cmd_append_size));
-
- if (adapter->adhoc_start_band == BAND_B)
- tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
- else
- tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-
- adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap);
-
- return 0;
-}
-
-/*
- * This function prepares command for ad-hoc join.
- *
- * Most of the parameters are set up by copying from the target BSS descriptor
- * from the scan response.
- *
- * In addition, the following TLVs are added -
- * - Channel TLV
- * - Vendor specific IE
- * - WPA/WPA2 IE
- * - 11n IE
- *
- * Preparation also includes -
- * - Setting command ID and proper size
- * - Ensuring correct endian-ness
- */
-int
-mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct mwifiex_bssdescriptor *bss_desc)
-{
- int rsn_ie_len = 0;
- struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join =
- &cmd->params.adhoc_join;
- struct mwifiex_ie_types_chan_list_param_set *chan_tlv;
- u32 cmd_append_size = 0;
- u16 tmp_cap;
- u32 i, rates_size = 0;
- u16 curr_pkt_filter;
- u8 *pos =
- (u8 *) adhoc_join +
- sizeof(struct host_cmd_ds_802_11_ad_hoc_join);
-
-/* Use G protection */
-#define USE_G_PROTECTION 0x02
- if (bss_desc->erp_flags & USE_G_PROTECTION) {
- curr_pkt_filter =
- priv->
- curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON;
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &curr_pkt_filter, false)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "ADHOC_J_CMD: G Protection config failed\n");
- return -1;
- }
- }
-
- priv->attempted_bss_desc = bss_desc;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN);
-
- adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS;
-
- adhoc_join->bss_descriptor.beacon_period
- = cpu_to_le16(bss_desc->beacon_period);
-
- memcpy(&adhoc_join->bss_descriptor.bssid,
- &bss_desc->mac_address, ETH_ALEN);
-
- memcpy(&adhoc_join->bss_descriptor.ssid,
- &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len);
-
- memcpy(&adhoc_join->bss_descriptor.phy_param_set,
- &bss_desc->phy_param_set,
- sizeof(union ieee_types_phy_param_set));
-
- memcpy(&adhoc_join->bss_descriptor.ss_param_set,
- &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set));
-
- tmp_cap = bss_desc->cap_info_bitmap;
-
- tmp_cap &= CAPINFO_MASK;
-
- mwifiex_dbg(priv->adapter, INFO,
- "info: ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n",
- tmp_cap, CAPINFO_MASK);
-
- /* Information on BSSID descriptor passed to FW */
- mwifiex_dbg(priv->adapter, INFO,
- "info: ADHOC_J_CMD: BSSID=%pM, SSID='%s'\n",
- adhoc_join->bss_descriptor.bssid,
- adhoc_join->bss_descriptor.ssid);
-
- for (i = 0; i < MWIFIEX_SUPPORTED_RATES &&
- bss_desc->supported_rates[i]; i++)
- ;
- rates_size = i;
-
- /* Copy Data Rates from the Rates recorded in scan response */
- memset(adhoc_join->bss_descriptor.data_rates, 0,
- sizeof(adhoc_join->bss_descriptor.data_rates));
- memcpy(adhoc_join->bss_descriptor.data_rates,
- bss_desc->supported_rates, rates_size);
-
- /* Copy the adhoc join rates into Current BSS state structure */
- priv->curr_bss_params.num_of_rates = rates_size;
- memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates,
- rates_size);
-
- /* Copy the channel information */
- priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel;
- priv->curr_bss_params.band = (u8) bss_desc->bss_band;
-
- if (priv->sec_info.wep_enabled || priv->sec_info.wpa_enabled)
- tmp_cap |= WLAN_CAPABILITY_PRIVACY;
-
- if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) {
- /* Append a channel TLV */
- chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos;
- chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
- chan_tlv->header.len =
- cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set));
-
- memset(chan_tlv->chan_scan_param, 0x00,
- sizeof(struct mwifiex_chan_scan_param_set));
- chan_tlv->chan_scan_param[0].chan_number =
- (bss_desc->phy_param_set.ds_param_set.current_chan);
- mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Chan=%d\n",
- chan_tlv->chan_scan_param[0].chan_number);
-
- chan_tlv->chan_scan_param[0].radio_type =
- mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
-
- mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Band=%d\n",
- chan_tlv->chan_scan_param[0].radio_type);
- pos += sizeof(chan_tlv->header) +
- sizeof(struct mwifiex_chan_scan_param_set);
- cmd_append_size += sizeof(chan_tlv->header) +
- sizeof(struct mwifiex_chan_scan_param_set);
- }
-
- if (priv->sec_info.wpa_enabled)
- rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos);
- if (rsn_ie_len == -1)
- return -1;
- cmd_append_size += rsn_ie_len;
-
- if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info))
- cmd_append_size += mwifiex_cmd_append_11n_tlv(priv,
- bss_desc, &pos);
-
- /* Append vendor specific IE TLV */
- cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv,
- MWIFIEX_VSIE_MASK_ADHOC, &pos);
-
- cmd->size = cpu_to_le16
- ((u16) (sizeof(struct host_cmd_ds_802_11_ad_hoc_join)
- + S_DS_GEN + cmd_append_size));
-
- adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap);
-
- return 0;
-}
-
-/*
- * This function handles the command response of ad-hoc start and
- * ad-hoc join.
- *
- * The function generates a device-connected event to notify
- * the applications, in case of successful ad-hoc start/join, and
- * saves the beacon buffer.
- */
-int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp)
-{
- int ret = 0;
- struct mwifiex_adapter *adapter = priv->adapter;
- struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result;
- struct mwifiex_bssdescriptor *bss_desc;
- u16 reason_code;
-
- adhoc_result = &resp->params.adhoc_result;
-
- bss_desc = priv->attempted_bss_desc;
-
- /* Join result code 0 --> SUCCESS */
- reason_code = le16_to_cpu(resp->result);
- if (reason_code) {
- mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n");
- if (priv->media_connected)
- mwifiex_reset_connect_state(priv, reason_code);
-
- memset(&priv->curr_bss_params.bss_descriptor,
- 0x00, sizeof(struct mwifiex_bssdescriptor));
-
- ret = -1;
- goto done;
- }
-
- /* Send a Media Connected event, according to the Spec */
- priv->media_connected = true;
-
- if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) {
- mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_S_RESP %s\n",
- bss_desc->ssid.ssid);
-
- /* Update the created network descriptor with the new BSSID */
- memcpy(bss_desc->mac_address,
- adhoc_result->bssid, ETH_ALEN);
-
- priv->adhoc_state = ADHOC_STARTED;
- } else {
- /*
- * Now the join cmd should be successful.
- * If BSSID has changed use SSID to compare instead of BSSID
- */
- mwifiex_dbg(priv->adapter, INFO,
- "info: ADHOC_J_RESP %s\n",
- bss_desc->ssid.ssid);
-
- /*
- * Make a copy of current BSSID descriptor, only needed for
- * join since the current descriptor is already being used
- * for adhoc start
- */
- memcpy(&priv->curr_bss_params.bss_descriptor,
- bss_desc, sizeof(struct mwifiex_bssdescriptor));
-
- priv->adhoc_state = ADHOC_JOINED;
- }
-
- mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: channel = %d\n",
- priv->adhoc_channel);
- mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: BSSID = %pM\n",
- priv->curr_bss_params.bss_descriptor.mac_address);
-
- if (!netif_carrier_ok(priv->netdev))
- netif_carrier_on(priv->netdev);
- mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
-
- mwifiex_save_curr_bcn(priv);
-
-done:
- /* Need to indicate IOCTL complete */
- if (adapter->curr_cmd->wait_q_enabled) {
- if (ret)
- adapter->cmd_wait_q.status = -1;
- else
- adapter->cmd_wait_q.status = 0;
-
- }
-
- return ret;
-}
-
-/*
- * This function associates to a specific BSS discovered in a scan.
- *
- * It clears any past association response stored for application
- * retrieval and calls the command preparation routine to send the
- * command to firmware.
- */
-int mwifiex_associate(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc)
-{
- /* Return error if the adapter is not STA role or table entry
- * is not marked as infra.
- */
- if ((GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) ||
- (bss_desc->bss_mode != NL80211_IFTYPE_STATION))
- return -1;
-
- if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
- !bss_desc->disable_11n && !bss_desc->disable_11ac &&
- priv->adapter->config_bands & BAND_AAC)
- mwifiex_set_11ac_ba_params(priv);
- else
- mwifiex_set_ba_params(priv);
-
- /* Clear any past association response stored for application
- retrieval */
- priv->assoc_rsp_size = 0;
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE,
- HostCmd_ACT_GEN_SET, 0, bss_desc, true);
-}
-
-/*
- * This function starts an ad-hoc network.
- *
- * It calls the command preparation routine to send the command to firmware.
- */
-int
-mwifiex_adhoc_start(struct mwifiex_private *priv,
- struct cfg80211_ssid *adhoc_ssid)
-{
- mwifiex_dbg(priv->adapter, INFO, "info: Adhoc Channel = %d\n",
- priv->adhoc_channel);
- mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.channel = %d\n",
- priv->curr_bss_params.bss_descriptor.channel);
- mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.band = %d\n",
- priv->curr_bss_params.band);
-
- if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
- priv->adapter->config_bands & BAND_AAC)
- mwifiex_set_11ac_ba_params(priv);
- else
- mwifiex_set_ba_params(priv);
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START,
- HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true);
-}
-
-/*
- * This function joins an ad-hoc network found in a previous scan.
- *
- * It calls the command preparation routine to send the command to firmware,
- * if already not connected to the requested SSID.
- */
-int mwifiex_adhoc_join(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc)
-{
- mwifiex_dbg(priv->adapter, INFO,
- "info: adhoc join: curr_bss ssid =%s\n",
- priv->curr_bss_params.bss_descriptor.ssid.ssid);
- mwifiex_dbg(priv->adapter, INFO,
- "info: adhoc join: curr_bss ssid_len =%u\n",
- priv->curr_bss_params.bss_descriptor.ssid.ssid_len);
- mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid =%s\n",
- bss_desc->ssid.ssid);
- mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid_len =%u\n",
- bss_desc->ssid.ssid_len);
-
- /* Check if the requested SSID is already joined */
- if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len &&
- !mwifiex_ssid_cmp(&bss_desc->ssid,
- &priv->curr_bss_params.bss_descriptor.ssid) &&
- (priv->curr_bss_params.bss_descriptor.bss_mode ==
- NL80211_IFTYPE_ADHOC)) {
- mwifiex_dbg(priv->adapter, INFO,
- "info: ADHOC_J_CMD: new ad-hoc SSID\t"
- "is the same as current; not attempting to re-join\n");
- return -1;
- }
-
- if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
- !bss_desc->disable_11n && !bss_desc->disable_11ac &&
- priv->adapter->config_bands & BAND_AAC)
- mwifiex_set_11ac_ba_params(priv);
- else
- mwifiex_set_ba_params(priv);
-
- mwifiex_dbg(priv->adapter, INFO,
- "info: curr_bss_params.channel = %d\n",
- priv->curr_bss_params.bss_descriptor.channel);
- mwifiex_dbg(priv->adapter, INFO,
- "info: curr_bss_params.band = %c\n",
- priv->curr_bss_params.band);
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN,
- HostCmd_ACT_GEN_SET, 0, bss_desc, true);
-}
-
-/*
- * This function deauthenticates/disconnects from infra network by sending
- * deauthentication request.
- */
-static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac)
-{
- u8 mac_address[ETH_ALEN];
- int ret;
-
- if (!mac || is_zero_ether_addr(mac))
- memcpy(mac_address,
- priv->curr_bss_params.bss_descriptor.mac_address,
- ETH_ALEN);
- else
- memcpy(mac_address, mac, ETH_ALEN);
-
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE,
- HostCmd_ACT_GEN_SET, 0, mac_address, true);
-
- return ret;
-}
-
-/*
- * This function deauthenticates/disconnects from a BSS.
- *
- * In case of infra made, it sends deauthentication request, and
- * in case of ad-hoc mode, a stop network request is sent to the firmware.
- * In AP mode, a command to stop bss is sent to firmware.
- */
-int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
-{
- int ret = 0;
-
- if (!priv->media_connected)
- return 0;
-
- switch (priv->bss_mode) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_CLIENT:
- ret = mwifiex_deauthenticate_infra(priv, mac);
- if (ret)
- cfg80211_disconnected(priv->netdev, 0, NULL, 0,
- true, GFP_KERNEL);
- break;
- case NL80211_IFTYPE_ADHOC:
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP,
- HostCmd_ACT_GEN_SET, 0, NULL, true);
- case NL80211_IFTYPE_AP:
- return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
- HostCmd_ACT_GEN_SET, 0, NULL, true);
- default:
- break;
- }
-
- return ret;
-}
-
-/* This function deauthenticates/disconnects from all BSS. */
-void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter)
-{
- struct mwifiex_private *priv;
- int i;
-
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
- if (priv)
- mwifiex_deauthenticate(priv, NULL);
- }
-}
-EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all);
-
-/*
- * This function converts band to radio type used in channel TLV.
- */
-u8
-mwifiex_band_to_radio_type(u8 band)
-{
- switch (band) {
- case BAND_A:
- case BAND_AN:
- case BAND_A | BAND_AN:
- case BAND_A | BAND_AN | BAND_AAC:
- return HostCmd_SCAN_RADIO_TYPE_A;
- case BAND_B:
- case BAND_G:
- case BAND_B | BAND_G:
- default:
- return HostCmd_SCAN_RADIO_TYPE_BG;
- }
-}
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
deleted file mode 100644
index 969ca1e1f3e9..000000000000
--- a/drivers/net/wireless/mwifiex/main.c
+++ /dev/null
@@ -1,1552 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: major functions
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "wmm.h"
-#include "cfg80211.h"
-#include "11n.h"
-
-#define VERSION "1.0"
-
-static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK;
-module_param(debug_mask, uint, 0);
-MODULE_PARM_DESC(debug_mask, "bitmap for debug flags");
-
-const char driver_version[] = "mwifiex " VERSION " (%s) ";
-static char *cal_data_cfg;
-module_param(cal_data_cfg, charp, 0);
-
-static unsigned short driver_mode;
-module_param(driver_mode, ushort, 0);
-MODULE_PARM_DESC(driver_mode,
- "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7");
-
-/*
- * This function registers the device and performs all the necessary
- * initializations.
- *
- * The following initialization operations are performed -
- * - Allocate adapter structure
- * - Save interface specific operations table in adapter
- * - Call interface specific initialization routine
- * - Allocate private structures
- * - Set default adapter structure parameters
- * - Initialize locks
- *
- * In case of any errors during inittialization, this function also ensures
- * proper cleanup before exiting.
- */
-static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
- void **padapter)
-{
- struct mwifiex_adapter *adapter;
- int i;
-
- adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL);
- if (!adapter)
- return -ENOMEM;
-
- *padapter = adapter;
- adapter->card = card;
-
- /* Save interface specific operations in adapter */
- memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops));
- adapter->debug_mask = debug_mask;
-
- /* card specific initialization has been deferred until now .. */
- if (adapter->if_ops.init_if)
- if (adapter->if_ops.init_if(adapter))
- goto error;
-
- adapter->priv_num = 0;
-
- for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
- /* Allocate memory for private structure */
- adapter->priv[i] =
- kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL);
- if (!adapter->priv[i])
- goto error;
-
- adapter->priv[i]->adapter = adapter;
- adapter->priv_num++;
- }
- mwifiex_init_lock_list(adapter);
-
- setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func,
- (unsigned long)adapter);
-
- return 0;
-
-error:
- mwifiex_dbg(adapter, ERROR,
- "info: leave mwifiex_register with error\n");
-
- for (i = 0; i < adapter->priv_num; i++)
- kfree(adapter->priv[i]);
-
- kfree(adapter);
-
- return -1;
-}
-
-/*
- * This function unregisters the device and performs all the necessary
- * cleanups.
- *
- * The following cleanup operations are performed -
- * - Free the timers
- * - Free beacon buffers
- * - Free private structures
- * - Free adapter structure
- */
-static int mwifiex_unregister(struct mwifiex_adapter *adapter)
-{
- s32 i;
-
- if (adapter->if_ops.cleanup_if)
- adapter->if_ops.cleanup_if(adapter);
-
- del_timer_sync(&adapter->cmd_timer);
-
- /* Free private structures */
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- mwifiex_free_curr_bcn(adapter->priv[i]);
- kfree(adapter->priv[i]);
- }
- }
-
- vfree(adapter->chan_stats);
- kfree(adapter);
- return 0;
-}
-
-void mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&adapter->main_proc_lock, flags);
- if (adapter->mwifiex_processing) {
- adapter->more_task_flag = true;
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
- } else {
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
- queue_work(adapter->workqueue, &adapter->main_work);
- }
-}
-EXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
-
-static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&adapter->rx_proc_lock, flags);
- if (adapter->rx_processing) {
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
- } else {
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
- queue_work(adapter->rx_workqueue, &adapter->rx_work);
- }
-}
-
-static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
-{
- unsigned long flags;
- struct sk_buff *skb;
- struct mwifiex_rxinfo *rx_info;
-
- spin_lock_irqsave(&adapter->rx_proc_lock, flags);
- if (adapter->rx_processing || adapter->rx_locked) {
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
- goto exit_rx_proc;
- } else {
- adapter->rx_processing = true;
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
- }
-
- /* Check for Rx data */
- while ((skb = skb_dequeue(&adapter->rx_data_q))) {
- atomic_dec(&adapter->rx_pending);
- if ((adapter->delay_main_work ||
- adapter->iface_type == MWIFIEX_USB) &&
- (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
- if (adapter->if_ops.submit_rem_rx_urbs)
- adapter->if_ops.submit_rem_rx_urbs(adapter);
- adapter->delay_main_work = false;
- mwifiex_queue_main_work(adapter);
- }
- rx_info = MWIFIEX_SKB_RXCB(skb);
- if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
- if (adapter->if_ops.deaggr_pkt)
- adapter->if_ops.deaggr_pkt(adapter, skb);
- dev_kfree_skb_any(skb);
- } else {
- mwifiex_handle_rx_packet(adapter, skb);
- }
- }
- spin_lock_irqsave(&adapter->rx_proc_lock, flags);
- adapter->rx_processing = false;
- spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
-
-exit_rx_proc:
- return 0;
-}
-
-/*
- * The main process.
- *
- * This function is the main procedure of the driver and handles various driver
- * operations. It runs in a loop and provides the core functionalities.
- *
- * The main responsibilities of this function are -
- * - Ensure concurrency control
- * - Handle pending interrupts and call interrupt handlers
- * - Wake up the card if required
- * - Handle command responses and call response handlers
- * - Handle events and call event handlers
- * - Execute pending commands
- * - Transmit pending data packets
- */
-int mwifiex_main_process(struct mwifiex_adapter *adapter)
-{
- int ret = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&adapter->main_proc_lock, flags);
-
- /* Check if already processing */
- if (adapter->mwifiex_processing || adapter->main_locked) {
- adapter->more_task_flag = true;
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
- goto exit_main_proc;
- } else {
- adapter->mwifiex_processing = true;
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
- }
-process_start:
- do {
- if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) ||
- (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
- break;
-
- /* For non-USB interfaces, If we process interrupts first, it
- * would increase RX pending even further. Avoid this by
- * checking if rx_pending has crossed high threshold and
- * schedule rx work queue and then process interrupts.
- * For USB interface, there are no interrupts. We already have
- * HIGH_RX_PENDING check in usb.c
- */
- if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
- adapter->iface_type != MWIFIEX_USB) {
- adapter->delay_main_work = true;
- mwifiex_queue_rx_work(adapter);
- break;
- }
-
- /* Handle pending interrupt if any */
- if (adapter->int_status) {
- if (adapter->hs_activated)
- mwifiex_process_hs_config(adapter);
- if (adapter->if_ops.process_int_status)
- adapter->if_ops.process_int_status(adapter);
- }
-
- if (adapter->rx_work_enabled && adapter->data_received)
- mwifiex_queue_rx_work(adapter);
-
- /* Need to wake up the card ? */
- if ((adapter->ps_state == PS_STATE_SLEEP) &&
- (adapter->pm_wakeup_card_req &&
- !adapter->pm_wakeup_fw_try) &&
- (is_command_pending(adapter) ||
- !skb_queue_empty(&adapter->tx_data_q) ||
- !mwifiex_bypass_txlist_empty(adapter) ||
- !mwifiex_wmm_lists_empty(adapter))) {
- adapter->pm_wakeup_fw_try = true;
- mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
- adapter->if_ops.wakeup(adapter);
- continue;
- }
-
- if (IS_CARD_RX_RCVD(adapter)) {
- adapter->data_received = false;
- adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
- if (adapter->ps_state == PS_STATE_SLEEP)
- adapter->ps_state = PS_STATE_AWAKE;
- } else {
- /* We have tried to wakeup the card already */
- if (adapter->pm_wakeup_fw_try)
- break;
- if (adapter->ps_state != PS_STATE_AWAKE)
- break;
- if (adapter->tx_lock_flag) {
- if (adapter->iface_type == MWIFIEX_USB) {
- if (!adapter->usb_mc_setup)
- break;
- } else
- break;
- }
-
- if ((!adapter->scan_chan_gap_enabled &&
- adapter->scan_processing) || adapter->data_sent ||
- mwifiex_is_tdls_chan_switching
- (mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_STA)) ||
- (mwifiex_wmm_lists_empty(adapter) &&
- mwifiex_bypass_txlist_empty(adapter) &&
- skb_queue_empty(&adapter->tx_data_q))) {
- if (adapter->cmd_sent || adapter->curr_cmd ||
- !mwifiex_is_send_cmd_allowed
- (mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_STA)) ||
- (!is_command_pending(adapter)))
- break;
- }
- }
-
- /* Check for event */
- if (adapter->event_received) {
- adapter->event_received = false;
- mwifiex_process_event(adapter);
- }
-
- /* Check for Cmd Resp */
- if (adapter->cmd_resp_received) {
- adapter->cmd_resp_received = false;
- mwifiex_process_cmdresp(adapter);
-
- /* call mwifiex back when init_fw is done */
- if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) {
- adapter->hw_status = MWIFIEX_HW_STATUS_READY;
- mwifiex_init_fw_complete(adapter);
- }
- }
-
- /* Check if we need to confirm Sleep Request
- received previously */
- if (adapter->ps_state == PS_STATE_PRE_SLEEP) {
- if (!adapter->cmd_sent && !adapter->curr_cmd)
- mwifiex_check_ps_cond(adapter);
- }
-
- /* * The ps_state may have been changed during processing of
- * Sleep Request event.
- */
- if ((adapter->ps_state == PS_STATE_SLEEP) ||
- (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
- (adapter->ps_state == PS_STATE_SLEEP_CFM)) {
- continue;
- }
-
- if (adapter->tx_lock_flag) {
- if (adapter->iface_type == MWIFIEX_USB) {
- if (!adapter->usb_mc_setup)
- continue;
- } else
- continue;
- }
-
- if (!adapter->cmd_sent && !adapter->curr_cmd &&
- mwifiex_is_send_cmd_allowed
- (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
- if (mwifiex_exec_next_cmd(adapter) == -1) {
- ret = -1;
- break;
- }
- }
-
- /** If USB Multi channel setup ongoing,
- * wait for ready to tx data.
- */
- if (adapter->iface_type == MWIFIEX_USB &&
- adapter->usb_mc_setup)
- continue;
-
- if ((adapter->scan_chan_gap_enabled ||
- !adapter->scan_processing) &&
- !adapter->data_sent &&
- !skb_queue_empty(&adapter->tx_data_q)) {
- mwifiex_process_tx_queue(adapter);
- if (adapter->hs_activated) {
- adapter->is_hs_configured = false;
- mwifiex_hs_activated_event
- (mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- false);
- }
- }
-
- if ((adapter->scan_chan_gap_enabled ||
- !adapter->scan_processing) &&
- !adapter->data_sent &&
- !mwifiex_bypass_txlist_empty(adapter) &&
- !mwifiex_is_tdls_chan_switching
- (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
- mwifiex_process_bypass_tx(adapter);
- if (adapter->hs_activated) {
- adapter->is_hs_configured = false;
- mwifiex_hs_activated_event
- (mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- false);
- }
- }
-
- if ((adapter->scan_chan_gap_enabled ||
- !adapter->scan_processing) &&
- !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) &&
- !mwifiex_is_tdls_chan_switching
- (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
- mwifiex_wmm_process_tx(adapter);
- if (adapter->hs_activated) {
- adapter->is_hs_configured = false;
- mwifiex_hs_activated_event
- (mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- false);
- }
- }
-
- if (adapter->delay_null_pkt && !adapter->cmd_sent &&
- !adapter->curr_cmd && !is_command_pending(adapter) &&
- (mwifiex_wmm_lists_empty(adapter) &&
- mwifiex_bypass_txlist_empty(adapter) &&
- skb_queue_empty(&adapter->tx_data_q))) {
- if (!mwifiex_send_null_packet
- (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
- MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
- MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) {
- adapter->delay_null_pkt = false;
- adapter->ps_state = PS_STATE_SLEEP;
- }
- break;
- }
- } while (true);
-
- spin_lock_irqsave(&adapter->main_proc_lock, flags);
- if (adapter->more_task_flag) {
- adapter->more_task_flag = false;
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
- goto process_start;
- }
- adapter->mwifiex_processing = false;
- spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
-
-exit_main_proc:
- if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING)
- mwifiex_shutdown_drv(adapter);
- return ret;
-}
-EXPORT_SYMBOL_GPL(mwifiex_main_process);
-
-/*
- * This function frees the adapter structure.
- *
- * Additionally, this closes the netlink socket, frees the timers
- * and private structures.
- */
-static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
-{
- if (!adapter) {
- pr_err("%s: adapter is NULL\n", __func__);
- return;
- }
-
- mwifiex_unregister(adapter);
- pr_debug("info: %s: free adapter\n", __func__);
-}
-
-/*
- * This function cancels all works in the queue and destroys
- * the main workqueue.
- */
-static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
-{
- flush_workqueue(adapter->workqueue);
- destroy_workqueue(adapter->workqueue);
- adapter->workqueue = NULL;
-
- if (adapter->rx_workqueue) {
- flush_workqueue(adapter->rx_workqueue);
- destroy_workqueue(adapter->rx_workqueue);
- adapter->rx_workqueue = NULL;
- }
-}
-
-/*
- * This function gets firmware and initializes it.
- *
- * The main initialization steps followed are -
- * - Download the correct firmware to card
- * - Issue the init commands to firmware
- */
-static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
-{
- int ret;
- char fmt[64];
- struct mwifiex_private *priv;
- struct mwifiex_adapter *adapter = context;
- struct mwifiex_fw_image fw;
- struct semaphore *sem = adapter->card_sem;
- bool init_failed = false;
- struct wireless_dev *wdev;
-
- if (!firmware) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to get firmware %s\n", adapter->fw_name);
- goto err_dnld_fw;
- }
-
- memset(&fw, 0, sizeof(struct mwifiex_fw_image));
- adapter->firmware = firmware;
- fw.fw_buf = (u8 *) adapter->firmware->data;
- fw.fw_len = adapter->firmware->size;
-
- if (adapter->if_ops.dnld_fw)
- ret = adapter->if_ops.dnld_fw(adapter, &fw);
- else
- ret = mwifiex_dnld_fw(adapter, &fw);
- if (ret == -1)
- goto err_dnld_fw;
-
- mwifiex_dbg(adapter, MSG, "WLAN FW is active\n");
-
- if (cal_data_cfg) {
- if ((request_firmware(&adapter->cal_data, cal_data_cfg,
- adapter->dev)) < 0)
- mwifiex_dbg(adapter, ERROR,
- "Cal data request_firmware() failed\n");
- }
-
- /* enable host interrupt after fw dnld is successful */
- if (adapter->if_ops.enable_int) {
- if (adapter->if_ops.enable_int(adapter))
- goto err_dnld_fw;
- }
-
- adapter->init_wait_q_woken = false;
- ret = mwifiex_init_fw(adapter);
- if (ret == -1) {
- goto err_init_fw;
- } else if (!ret) {
- adapter->hw_status = MWIFIEX_HW_STATUS_READY;
- goto done;
- }
- /* Wait for mwifiex_init to complete */
- wait_event_interruptible(adapter->init_wait_q,
- adapter->init_wait_q_woken);
- if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
- goto err_init_fw;
-
- priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
- if (mwifiex_register_cfg80211(adapter)) {
- mwifiex_dbg(adapter, ERROR,
- "cannot register with cfg80211\n");
- goto err_init_fw;
- }
-
- if (mwifiex_init_channel_scan_gap(adapter)) {
- mwifiex_dbg(adapter, ERROR,
- "could not init channel stats table\n");
- goto err_init_fw;
- }
-
- if (driver_mode) {
- driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK;
- driver_mode |= MWIFIEX_DRIVER_MODE_STA;
- }
-
- rtnl_lock();
- /* Create station interface by default */
- wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
- NL80211_IFTYPE_STATION, NULL, NULL);
- if (IS_ERR(wdev)) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create default STA interface\n");
- rtnl_unlock();
- goto err_add_intf;
- }
-
- if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
- wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
- NL80211_IFTYPE_AP, NULL, NULL);
- if (IS_ERR(wdev)) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create AP interface\n");
- rtnl_unlock();
- goto err_add_intf;
- }
- }
-
- if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
- wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
- NL80211_IFTYPE_P2P_CLIENT, NULL,
- NULL);
- if (IS_ERR(wdev)) {
- mwifiex_dbg(adapter, ERROR,
- "cannot create p2p client interface\n");
- rtnl_unlock();
- goto err_add_intf;
- }
- }
- rtnl_unlock();
-
- mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
- mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt);
- goto done;
-
-err_add_intf:
- wiphy_unregister(adapter->wiphy);
- wiphy_free(adapter->wiphy);
-err_init_fw:
- if (adapter->if_ops.disable_int)
- adapter->if_ops.disable_int(adapter);
-err_dnld_fw:
- mwifiex_dbg(adapter, ERROR,
- "info: %s: unregister device\n", __func__);
- if (adapter->if_ops.unregister_dev)
- adapter->if_ops.unregister_dev(adapter);
-
- if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
- pr_debug("info: %s: shutdown mwifiex\n", __func__);
- adapter->init_wait_q_woken = false;
-
- if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
- wait_event_interruptible(adapter->init_wait_q,
- adapter->init_wait_q_woken);
- }
- adapter->surprise_removed = true;
- mwifiex_terminate_workqueue(adapter);
- init_failed = true;
-done:
- if (adapter->cal_data) {
- release_firmware(adapter->cal_data);
- adapter->cal_data = NULL;
- }
- if (adapter->firmware) {
- release_firmware(adapter->firmware);
- adapter->firmware = NULL;
- }
- if (init_failed)
- mwifiex_free_adapter(adapter);
- up(sem);
- return;
-}
-
-/*
- * This function initializes the hardware and gets firmware.
- */
-static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
-{
- int ret;
-
- ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
- adapter->dev, GFP_KERNEL, adapter,
- mwifiex_fw_dpc);
- if (ret < 0)
- mwifiex_dbg(adapter, ERROR,
- "request_firmware_nowait error %d\n", ret);
- return ret;
-}
-
-/*
- * CFG802.11 network device handler for open.
- *
- * Starts the data queue.
- */
-static int
-mwifiex_open(struct net_device *dev)
-{
- netif_carrier_off(dev);
-
- return 0;
-}
-
-/*
- * CFG802.11 network device handler for close.
- */
-static int
-mwifiex_close(struct net_device *dev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- if (priv->scan_request) {
- mwifiex_dbg(priv->adapter, INFO,
- "aborting scan on ndo_stop\n");
- cfg80211_scan_done(priv->scan_request, 1);
- priv->scan_request = NULL;
- priv->scan_aborting = true;
- }
-
- return 0;
-}
-
-static bool
-mwifiex_bypass_tx_queue(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
-
- if (ntohs(eth_hdr->h_proto) == ETH_P_PAE ||
- mwifiex_is_skb_mgmt_frame(skb) ||
- (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
- ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
- (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) {
- mwifiex_dbg(priv->adapter, DATA,
- "bypass txqueue; eth type %#x, mgmt %d\n",
- ntohs(eth_hdr->h_proto),
- mwifiex_is_skb_mgmt_frame(skb));
- return true;
- }
-
- return false;
-}
-/*
- * Add buffer into wmm tx queue and queue work to transmit it.
- */
-int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
-{
- struct netdev_queue *txq;
- int index = mwifiex_1d_to_wmm_queue[skb->priority];
-
- if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
- txq = netdev_get_tx_queue(priv->netdev, index);
- if (!netif_tx_queue_stopped(txq)) {
- netif_tx_stop_queue(txq);
- mwifiex_dbg(priv->adapter, DATA,
- "stop queue: %d\n", index);
- }
- }
-
- if (mwifiex_bypass_tx_queue(priv, skb)) {
- atomic_inc(&priv->adapter->tx_pending);
- atomic_inc(&priv->adapter->bypass_tx_pending);
- mwifiex_wmm_add_buf_bypass_txqueue(priv, skb);
- } else {
- atomic_inc(&priv->adapter->tx_pending);
- mwifiex_wmm_add_buf_txqueue(priv, skb);
- }
-
- mwifiex_queue_main_work(priv->adapter);
-
- return 0;
-}
-
-struct sk_buff *
-mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
- struct sk_buff *skb, u8 flag, u64 *cookie)
-{
- struct sk_buff *orig_skb = skb;
- struct mwifiex_txinfo *tx_info, *orig_tx_info;
-
- skb = skb_clone(skb, GFP_ATOMIC);
- if (skb) {
- unsigned long flags;
- int id;
-
- spin_lock_irqsave(&priv->ack_status_lock, flags);
- id = idr_alloc(&priv->ack_status_frames, orig_skb,
- 1, 0xff, GFP_ATOMIC);
- spin_unlock_irqrestore(&priv->ack_status_lock, flags);
-
- if (id >= 0) {
- tx_info = MWIFIEX_SKB_TXCB(skb);
- tx_info->ack_frame_id = id;
- tx_info->flags |= flag;
- orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
- orig_tx_info->ack_frame_id = id;
- orig_tx_info->flags |= flag;
-
- if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
- orig_tx_info->cookie = *cookie;
-
- } else if (skb_shared(skb)) {
- kfree_skb(orig_skb);
- } else {
- kfree_skb(skb);
- skb = orig_skb;
- }
- } else {
- /* couldn't clone -- lose tx status ... */
- skb = orig_skb;
- }
-
- return skb;
-}
-
-/*
- * CFG802.11 network device handler for data transmission.
- */
-static int
-mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct sk_buff *new_skb;
- struct mwifiex_txinfo *tx_info;
- bool multicast;
-
- mwifiex_dbg(priv->adapter, DATA,
- "data: %lu BSS(%d-%d): Data <= kernel\n",
- jiffies, priv->bss_type, priv->bss_num);
-
- if (priv->adapter->surprise_removed) {
- kfree_skb(skb);
- priv->stats.tx_dropped++;
- return 0;
- }
- if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Tx: bad skb len %d\n", skb->len);
- kfree_skb(skb);
- priv->stats.tx_dropped++;
- return 0;
- }
- if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
- mwifiex_dbg(priv->adapter, DATA,
- "data: Tx: insufficient skb headroom %d\n",
- skb_headroom(skb));
- /* Insufficient skb headroom - allocate a new skb */
- new_skb =
- skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
- if (unlikely(!new_skb)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Tx: cannot alloca new_skb\n");
- kfree_skb(skb);
- priv->stats.tx_dropped++;
- return 0;
- }
- kfree_skb(skb);
- skb = new_skb;
- mwifiex_dbg(priv->adapter, INFO,
- "info: new skb headroomd %d\n",
- skb_headroom(skb));
- }
-
- tx_info = MWIFIEX_SKB_TXCB(skb);
- memset(tx_info, 0, sizeof(*tx_info));
- tx_info->bss_num = priv->bss_num;
- tx_info->bss_type = priv->bss_type;
- tx_info->pkt_len = skb->len;
-
- multicast = is_multicast_ether_addr(skb->data);
-
- if (unlikely(!multicast && skb->sk &&
- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
- priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
- skb = mwifiex_clone_skb_for_tx_status(priv,
- skb,
- MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
-
- /* Record the current time the packet was queued; used to
- * determine the amount of time the packet was queued in
- * the driver before it was sent to the firmware.
- * The delay is then sent along with the packet to the
- * firmware for aggregate delay calculation for stats and
- * MSDU lifetime expiry.
- */
- __net_timestamp(skb);
-
- if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
- priv->bss_type == MWIFIEX_BSS_TYPE_STA &&
- !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) {
- if (priv->adapter->auto_tdls && priv->check_tdls_tx)
- mwifiex_tdls_check_tx(priv, skb);
- }
-
- mwifiex_queue_tx_pkt(priv, skb);
-
- return 0;
-}
-
-/*
- * CFG802.11 network device handler for setting MAC address.
- */
-static int
-mwifiex_set_mac_address(struct net_device *dev, void *addr)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct sockaddr *hw_addr = addr;
- int ret;
-
- memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN);
-
- /* Send request to firmware */
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
- HostCmd_ACT_GEN_SET, 0, NULL, true);
-
- if (!ret)
- memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN);
- else
- mwifiex_dbg(priv->adapter, ERROR,
- "set mac address failed: ret=%d\n", ret);
-
- memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
-
- return ret;
-}
-
-/*
- * CFG802.11 network device handler for setting multicast list.
- */
-static void mwifiex_set_multicast_list(struct net_device *dev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct mwifiex_multicast_list mcast_list;
-
- if (dev->flags & IFF_PROMISC) {
- mcast_list.mode = MWIFIEX_PROMISC_MODE;
- } else if (dev->flags & IFF_ALLMULTI ||
- netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) {
- mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
- } else {
- mcast_list.mode = MWIFIEX_MULTICAST_MODE;
- mcast_list.num_multicast_addr =
- mwifiex_copy_mcast_addr(&mcast_list, dev);
- }
- mwifiex_request_set_multicast_list(priv, &mcast_list);
-}
-
-/*
- * CFG802.11 network device handler for transmission timeout.
- */
-static void
-mwifiex_tx_timeout(struct net_device *dev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- priv->num_tx_timeout++;
- priv->tx_timeout_cnt++;
- mwifiex_dbg(priv->adapter, ERROR,
- "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
- jiffies, priv->tx_timeout_cnt, priv->bss_type,
- priv->bss_num);
- mwifiex_set_trans_start(dev);
-
- if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
- priv->adapter->if_ops.card_reset) {
- mwifiex_dbg(priv->adapter, ERROR,
- "tx_timeout_cnt exceeds threshold.\t"
- "Triggering card reset!\n");
- priv->adapter->if_ops.card_reset(priv->adapter);
- }
-}
-
-void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter)
-{
- struct usb_card_rec *card = adapter->card;
- struct mwifiex_private *priv;
- u16 tx_buf_size;
- int i, ret;
-
- card->mc_resync_flag = true;
- for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
- if (atomic_read(&card->port[i].tx_data_urb_pending)) {
- mwifiex_dbg(adapter, WARN, "pending data urb in sys\n");
- return;
- }
- }
-
- card->mc_resync_flag = false;
- tx_buf_size = 0xffff;
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
- HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false);
- if (ret)
- mwifiex_dbg(adapter, ERROR,
- "send reconfig tx buf size cmd err\n");
-}
-EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync);
-
-void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter)
-{
- void *p;
- char drv_version[64];
- struct usb_card_rec *cardp;
- struct sdio_mmc_card *sdio_card;
- struct mwifiex_private *priv;
- int i, idx;
- struct netdev_queue *txq;
- struct mwifiex_debug_info *debug_info;
-
- if (adapter->drv_info_dump) {
- vfree(adapter->drv_info_dump);
- adapter->drv_info_dump = NULL;
- adapter->drv_info_size = 0;
- }
-
- mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n");
-
- adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX);
-
- if (!adapter->drv_info_dump)
- return;
-
- p = (char *)(adapter->drv_info_dump);
- p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
-
- mwifiex_drv_get_driver_version(adapter, drv_version,
- sizeof(drv_version) - 1);
- p += sprintf(p, "driver_version = %s\n", drv_version);
-
- if (adapter->iface_type == MWIFIEX_USB) {
- cardp = (struct usb_card_rec *)adapter->card;
- p += sprintf(p, "tx_cmd_urb_pending = %d\n",
- atomic_read(&cardp->tx_cmd_urb_pending));
- p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n",
- atomic_read(&cardp->port[0].tx_data_urb_pending));
- p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n",
- atomic_read(&cardp->port[1].tx_data_urb_pending));
- p += sprintf(p, "rx_cmd_urb_pending = %d\n",
- atomic_read(&cardp->rx_cmd_urb_pending));
- p += sprintf(p, "rx_data_urb_pending = %d\n",
- atomic_read(&cardp->rx_data_urb_pending));
- }
-
- p += sprintf(p, "tx_pending = %d\n",
- atomic_read(&adapter->tx_pending));
- p += sprintf(p, "rx_pending = %d\n",
- atomic_read(&adapter->rx_pending));
-
- if (adapter->iface_type == MWIFIEX_SDIO) {
- sdio_card = (struct sdio_mmc_card *)adapter->card;
- p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
- sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
- p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
- sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
- }
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (!adapter->priv[i] || !adapter->priv[i]->netdev)
- continue;
- priv = adapter->priv[i];
- p += sprintf(p, "\n[interface : \"%s\"]\n",
- priv->netdev->name);
- p += sprintf(p, "wmm_tx_pending[0] = %d\n",
- atomic_read(&priv->wmm_tx_pending[0]));
- p += sprintf(p, "wmm_tx_pending[1] = %d\n",
- atomic_read(&priv->wmm_tx_pending[1]));
- p += sprintf(p, "wmm_tx_pending[2] = %d\n",
- atomic_read(&priv->wmm_tx_pending[2]));
- p += sprintf(p, "wmm_tx_pending[3] = %d\n",
- atomic_read(&priv->wmm_tx_pending[3]));
- p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
- "Disconnected" : "Connected");
- p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
- ? "on" : "off"));
- for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
- txq = netdev_get_tx_queue(priv->netdev, idx);
- p += sprintf(p, "tx queue %d:%s ", idx,
- netif_tx_queue_stopped(txq) ?
- "stopped" : "started");
- }
- p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
- priv->netdev->name, priv->num_tx_timeout);
- }
-
- if (adapter->iface_type == MWIFIEX_SDIO) {
- p += sprintf(p, "\n=== SDIO register dump===\n");
- if (adapter->if_ops.reg_dump)
- p += adapter->if_ops.reg_dump(adapter, p);
- }
-
- p += sprintf(p, "\n=== more debug information\n");
- debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
- if (debug_info) {
- for (i = 0; i < adapter->priv_num; i++) {
- if (!adapter->priv[i] || !adapter->priv[i]->netdev)
- continue;
- priv = adapter->priv[i];
- mwifiex_get_debug_info(priv, debug_info);
- p += mwifiex_debug_info_to_buffer(priv, p, debug_info);
- break;
- }
- kfree(debug_info);
- }
-
- adapter->drv_info_size = p - adapter->drv_info_dump;
- mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n");
-}
-EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump);
-
-void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter)
-{
- u8 idx, *dump_data, *fw_dump_ptr;
- u32 dump_len;
-
- dump_len = (strlen("========Start dump driverinfo========\n") +
- adapter->drv_info_size +
- strlen("\n========End dump========\n"));
-
- for (idx = 0; idx < adapter->num_mem_types; idx++) {
- struct memory_type_mapping *entry =
- &adapter->mem_type_mapping_tbl[idx];
-
- if (entry->mem_ptr) {
- dump_len += (strlen("========Start dump ") +
- strlen(entry->mem_name) +
- strlen("========\n") +
- (entry->mem_size + 1) +
- strlen("\n========End dump========\n"));
- }
- }
-
- dump_data = vzalloc(dump_len + 1);
- if (!dump_data)
- goto done;
-
- fw_dump_ptr = dump_data;
-
- /* Dump all the memory data into single file, a userspace script will
- * be used to split all the memory data to multiple files
- */
- mwifiex_dbg(adapter, MSG,
- "== mwifiex dump information to /sys/class/devcoredump start");
-
- strcpy(fw_dump_ptr, "========Start dump driverinfo========\n");
- fw_dump_ptr += strlen("========Start dump driverinfo========\n");
- memcpy(fw_dump_ptr, adapter->drv_info_dump, adapter->drv_info_size);
- fw_dump_ptr += adapter->drv_info_size;
- strcpy(fw_dump_ptr, "\n========End dump========\n");
- fw_dump_ptr += strlen("\n========End dump========\n");
-
- for (idx = 0; idx < adapter->num_mem_types; idx++) {
- struct memory_type_mapping *entry =
- &adapter->mem_type_mapping_tbl[idx];
-
- if (entry->mem_ptr) {
- strcpy(fw_dump_ptr, "========Start dump ");
- fw_dump_ptr += strlen("========Start dump ");
-
- strcpy(fw_dump_ptr, entry->mem_name);
- fw_dump_ptr += strlen(entry->mem_name);
-
- strcpy(fw_dump_ptr, "========\n");
- fw_dump_ptr += strlen("========\n");
-
- memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
- fw_dump_ptr += entry->mem_size;
-
- strcpy(fw_dump_ptr, "\n========End dump========\n");
- fw_dump_ptr += strlen("\n========End dump========\n");
- }
- }
-
- /* device dump data will be free in device coredump release function
- * after 5 min
- */
- dev_coredumpv(adapter->dev, dump_data, dump_len, GFP_KERNEL);
- mwifiex_dbg(adapter, MSG,
- "== mwifiex dump information to /sys/class/devcoredump end");
-
-done:
- for (idx = 0; idx < adapter->num_mem_types; idx++) {
- struct memory_type_mapping *entry =
- &adapter->mem_type_mapping_tbl[idx];
-
- if (entry->mem_ptr) {
- vfree(entry->mem_ptr);
- entry->mem_ptr = NULL;
- }
- entry->mem_size = 0;
- }
-
- if (adapter->drv_info_dump) {
- vfree(adapter->drv_info_dump);
- adapter->drv_info_dump = NULL;
- adapter->drv_info_size = 0;
- }
-}
-EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump);
-
-/*
- * CFG802.11 network device handler for statistics retrieval.
- */
-static struct net_device_stats *mwifiex_get_stats(struct net_device *dev)
-{
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-
- return &priv->stats;
-}
-
-static u16
-mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb,
- void *accel_priv, select_queue_fallback_t fallback)
-{
- skb->priority = cfg80211_classify8021d(skb, NULL);
- return mwifiex_1d_to_wmm_queue[skb->priority];
-}
-
-/* Network device handlers */
-static const struct net_device_ops mwifiex_netdev_ops = {
- .ndo_open = mwifiex_open,
- .ndo_stop = mwifiex_close,
- .ndo_start_xmit = mwifiex_hard_start_xmit,
- .ndo_set_mac_address = mwifiex_set_mac_address,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_tx_timeout = mwifiex_tx_timeout,
- .ndo_get_stats = mwifiex_get_stats,
- .ndo_set_rx_mode = mwifiex_set_multicast_list,
- .ndo_select_queue = mwifiex_netdev_select_wmm_queue,
-};
-
-/*
- * This function initializes the private structure parameters.
- *
- * The following wait queues are initialized -
- * - IOCTL wait queue
- * - Command wait queue
- * - Statistics wait queue
- *
- * ...and the following default parameters are set -
- * - Current key index : Set to 0
- * - Rate index : Set to auto
- * - Media connected : Set to disconnected
- * - Adhoc link sensed : Set to false
- * - Nick name : Set to null
- * - Number of Tx timeout : Set to 0
- * - Device address : Set to current address
- * - Rx histogram statistc : Set to 0
- *
- * In addition, the CFG80211 work queue is also created.
- */
-void mwifiex_init_priv_params(struct mwifiex_private *priv,
- struct net_device *dev)
-{
- dev->netdev_ops = &mwifiex_netdev_ops;
- dev->destructor = free_netdev;
- /* Initialize private structure */
- priv->current_key_index = 0;
- priv->media_connected = false;
- memset(priv->mgmt_ie, 0,
- sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX);
- priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK;
- priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK;
- priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
- priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
- priv->num_tx_timeout = 0;
- ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
- memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
- GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
- priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
- if (priv->hist_data)
- mwifiex_hist_data_reset(priv);
- }
-}
-
-/*
- * This function check if command is pending.
- */
-int is_command_pending(struct mwifiex_adapter *adapter)
-{
- unsigned long flags;
- int is_cmd_pend_q_empty;
-
- spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
- is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
- spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
-
- return !is_cmd_pend_q_empty;
-}
-
-/*
- * This is the RX work queue function.
- *
- * It handles the RX operations.
- */
-static void mwifiex_rx_work_queue(struct work_struct *work)
-{
- struct mwifiex_adapter *adapter =
- container_of(work, struct mwifiex_adapter, rx_work);
-
- if (adapter->surprise_removed)
- return;
- mwifiex_process_rx(adapter);
-}
-
-/*
- * This is the main work queue function.
- *
- * It handles the main process, which in turn handles the complete
- * driver operations.
- */
-static void mwifiex_main_work_queue(struct work_struct *work)
-{
- struct mwifiex_adapter *adapter =
- container_of(work, struct mwifiex_adapter, main_work);
-
- if (adapter->surprise_removed)
- return;
- mwifiex_main_process(adapter);
-}
-
-/*
- * This function adds the card.
- *
- * This function follows the following major steps to set up the device -
- * - Initialize software. This includes probing the card, registering
- * the interface operations table, and allocating/initializing the
- * adapter structure
- * - Set up the netlink socket
- * - Create and start the main work queue
- * - Register the device
- * - Initialize firmware and hardware
- * - Add logical interfaces
- */
-int
-mwifiex_add_card(void *card, struct semaphore *sem,
- struct mwifiex_if_ops *if_ops, u8 iface_type)
-{
- struct mwifiex_adapter *adapter;
-
- if (down_interruptible(sem))
- goto exit_sem_err;
-
- if (mwifiex_register(card, if_ops, (void **)&adapter)) {
- pr_err("%s: software init failed\n", __func__);
- goto err_init_sw;
- }
-
- adapter->iface_type = iface_type;
- adapter->card_sem = sem;
-
- adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
- adapter->surprise_removed = false;
- init_waitqueue_head(&adapter->init_wait_q);
- adapter->is_suspended = false;
- adapter->hs_activated = false;
- init_waitqueue_head(&adapter->hs_activate_wait_q);
- init_waitqueue_head(&adapter->cmd_wait_q.wait);
- adapter->cmd_wait_q.status = 0;
- adapter->scan_wait_q_woken = false;
-
- if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) {
- adapter->rx_work_enabled = true;
- pr_notice("rx work enabled, cpus %d\n", num_possible_cpus());
- }
-
- adapter->workqueue =
- alloc_workqueue("MWIFIEX_WORK_QUEUE",
- WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
- if (!adapter->workqueue)
- goto err_kmalloc;
-
- INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
-
- if (adapter->rx_work_enabled) {
- adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
- WQ_HIGHPRI |
- WQ_MEM_RECLAIM |
- WQ_UNBOUND, 1);
- if (!adapter->rx_workqueue)
- goto err_kmalloc;
-
- INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
- }
-
- /* Register the device. Fill up the private data structure with relevant
- information from the card. */
- if (adapter->if_ops.register_dev(adapter)) {
- pr_err("%s: failed to register mwifiex device\n", __func__);
- goto err_registerdev;
- }
-
- if (mwifiex_init_hw_fw(adapter)) {
- pr_err("%s: firmware init failed\n", __func__);
- goto err_init_fw;
- }
-
- return 0;
-
-err_init_fw:
- pr_debug("info: %s: unregister device\n", __func__);
- if (adapter->if_ops.unregister_dev)
- adapter->if_ops.unregister_dev(adapter);
- if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
- pr_debug("info: %s: shutdown mwifiex\n", __func__);
- adapter->init_wait_q_woken = false;
-
- if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
- wait_event_interruptible(adapter->init_wait_q,
- adapter->init_wait_q_woken);
- }
-err_registerdev:
- adapter->surprise_removed = true;
- mwifiex_terminate_workqueue(adapter);
-err_kmalloc:
- mwifiex_free_adapter(adapter);
-
-err_init_sw:
- up(sem);
-
-exit_sem_err:
- return -1;
-}
-EXPORT_SYMBOL_GPL(mwifiex_add_card);
-
-/*
- * This function removes the card.
- *
- * This function follows the following major steps to remove the device -
- * - Stop data traffic
- * - Shutdown firmware
- * - Remove the logical interfaces
- * - Terminate the work queue
- * - Unregister the device
- * - Free the adapter structure
- */
-int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
-{
- struct mwifiex_private *priv = NULL;
- int i;
-
- if (down_interruptible(sem))
- goto exit_sem_err;
-
- if (!adapter)
- goto exit_remove;
-
- /* We can no longer handle interrupts once we start doing the teardown
- * below. */
- if (adapter->if_ops.disable_int)
- adapter->if_ops.disable_int(adapter);
-
- adapter->surprise_removed = true;
-
- mwifiex_terminate_workqueue(adapter);
-
- /* Stop data */
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
- if (priv && priv->netdev) {
- mwifiex_stop_net_dev_queue(priv->netdev, adapter);
- if (netif_carrier_ok(priv->netdev))
- netif_carrier_off(priv->netdev);
- }
- }
-
- mwifiex_dbg(adapter, CMD,
- "cmd: calling mwifiex_shutdown_drv...\n");
- adapter->init_wait_q_woken = false;
-
- if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
- wait_event_interruptible(adapter->init_wait_q,
- adapter->init_wait_q_woken);
- mwifiex_dbg(adapter, CMD,
- "cmd: mwifiex_shutdown_drv done\n");
- if (atomic_read(&adapter->rx_pending) ||
- atomic_read(&adapter->tx_pending) ||
- atomic_read(&adapter->cmd_pending)) {
- mwifiex_dbg(adapter, ERROR,
- "rx_pending=%d, tx_pending=%d,\t"
- "cmd_pending=%d\n",
- atomic_read(&adapter->rx_pending),
- atomic_read(&adapter->tx_pending),
- atomic_read(&adapter->cmd_pending));
- }
-
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
-
- if (!priv)
- continue;
-
- rtnl_lock();
- if (priv->netdev &&
- priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED)
- mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
- rtnl_unlock();
- }
-
- wiphy_unregister(adapter->wiphy);
- wiphy_free(adapter->wiphy);
-
- /* Unregister device */
- mwifiex_dbg(adapter, INFO,
- "info: unregister device\n");
- if (adapter->if_ops.unregister_dev)
- adapter->if_ops.unregister_dev(adapter);
- /* Free adapter structure */
- mwifiex_dbg(adapter, INFO,
- "info: free adapter\n");
- mwifiex_free_adapter(adapter);
-
-exit_remove:
- up(sem);
-exit_sem_err:
- return 0;
-}
-EXPORT_SYMBOL_GPL(mwifiex_remove_card);
-
-void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
- const char *fmt, ...)
-{
- struct va_format vaf;
- va_list args;
-
- if (!adapter->dev || !(adapter->debug_mask & mask))
- return;
-
- va_start(args, fmt);
-
- vaf.fmt = fmt;
- vaf.va = &args;
-
- dev_info(adapter->dev, "%pV", &vaf);
-
- va_end(args);
-}
-EXPORT_SYMBOL_GPL(_mwifiex_dbg);
-
-/*
- * This function initializes the module.
- *
- * The debug FS is also initialized if configured.
- */
-static int
-mwifiex_init_module(void)
-{
-#ifdef CONFIG_DEBUG_FS
- mwifiex_debugfs_init();
-#endif
- return 0;
-}
-
-/*
- * This function cleans up the module.
- *
- * The debug FS is removed if available.
- */
-static void
-mwifiex_cleanup_module(void)
-{
-#ifdef CONFIG_DEBUG_FS
- mwifiex_debugfs_remove();
-#endif
-}
-
-module_init(mwifiex_init_module);
-module_exit(mwifiex_cleanup_module);
-
-MODULE_AUTHOR("Marvell International Ltd.");
-MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION);
-MODULE_VERSION(VERSION);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
deleted file mode 100644
index 3959f1c97f4e..000000000000
--- a/drivers/net/wireless/mwifiex/main.h
+++ /dev/null
@@ -1,1579 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: major data structures and prototypes
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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.
- */
-
-#ifndef _MWIFIEX_MAIN_H_
-#define _MWIFIEX_MAIN_H_
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/semaphore.h>
-#include <linux/ip.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/etherdevice.h>
-#include <net/sock.h>
-#include <net/lib80211.h>
-#include <linux/vmalloc.h>
-#include <linux/firmware.h>
-#include <linux/ctype.h>
-#include <linux/of.h>
-#include <linux/idr.h>
-#include <linux/inetdevice.h>
-#include <linux/devcoredump.h>
-
-#include "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "pcie.h"
-#include "usb.h"
-#include "sdio.h"
-
-extern const char driver_version[];
-
-struct mwifiex_adapter;
-struct mwifiex_private;
-
-enum {
- MWIFIEX_ASYNC_CMD,
- MWIFIEX_SYNC_CMD
-};
-
-#define MWIFIEX_DRIVER_MODE_STA BIT(0)
-#define MWIFIEX_DRIVER_MODE_UAP BIT(1)
-#define MWIFIEX_DRIVER_MODE_P2P BIT(2)
-#define MWIFIEX_DRIVER_MODE_BITMASK (BIT(0) | BIT(1) | BIT(2))
-
-#define MWIFIEX_MAX_AP 64
-
-#define MWIFIEX_MAX_PKTS_TXQ 16
-
-#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ)
-
-#define MWIFIEX_TIMER_10S 10000
-#define MWIFIEX_TIMER_1S 1000
-
-#define MAX_TX_PENDING 100
-#define LOW_TX_PENDING 80
-
-#define HIGH_RX_PENDING 50
-#define LOW_RX_PENDING 20
-
-#define MWIFIEX_UPLD_SIZE (2312)
-
-#define MAX_EVENT_SIZE 2048
-
-#define ARP_FILTER_MAX_BUF_SIZE 68
-
-#define MWIFIEX_KEY_BUFFER_SIZE 16
-#define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10
-#define MWIFIEX_MAX_REGION_CODE 7
-
-#define DEFAULT_BCN_AVG_FACTOR 8
-#define DEFAULT_DATA_AVG_FACTOR 8
-
-#define FIRST_VALID_CHANNEL 0xff
-#define DEFAULT_AD_HOC_CHANNEL 6
-#define DEFAULT_AD_HOC_CHANNEL_A 36
-
-#define DEFAULT_BCN_MISS_TIMEOUT 5
-
-#define MAX_SCAN_BEACON_BUFFER 8000
-
-#define SCAN_BEACON_ENTRY_PAD 6
-
-#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110
-#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30
-#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30
-#define MWIFIEX_DEF_SCAN_CHAN_GAP_TIME 50
-
-#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI)))
-
-#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S)
-
-#define RSN_GTK_OUI_OFFSET 2
-
-#define MWIFIEX_OUI_NOT_PRESENT 0
-#define MWIFIEX_OUI_PRESENT 1
-
-#define PKT_TYPE_MGMT 0xE5
-
-/*
- * Do not check for data_received for USB, as data_received
- * is handled in mwifiex_usb_recv for USB
- */
-#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \
- adapter->event_received || \
- adapter->data_received)
-
-#define MWIFIEX_TYPE_CMD 1
-#define MWIFIEX_TYPE_DATA 0
-#define MWIFIEX_TYPE_AGGR_DATA 10
-#define MWIFIEX_TYPE_EVENT 3
-
-#define MAX_BITMAP_RATES_SIZE 18
-
-#define MAX_CHANNEL_BAND_BG 14
-#define MAX_CHANNEL_BAND_A 165
-
-#define MAX_FREQUENCY_BAND_BG 2484
-
-#define MWIFIEX_EVENT_HEADER_LEN 4
-#define MWIFIEX_UAP_EVENT_EXTRA_HEADER 2
-
-#define MWIFIEX_TYPE_LEN 4
-#define MWIFIEX_USB_TYPE_CMD 0xF00DFACE
-#define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE
-#define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE
-
-/* Threshold for tx_timeout_cnt before we trigger a card reset */
-#define TX_TIMEOUT_THRESHOLD 6
-
-#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000
-
-/* Address alignment */
-#define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1))
-
-/**
- *enum mwifiex_debug_level - marvell wifi debug level
- */
-enum MWIFIEX_DEBUG_LEVEL {
- MWIFIEX_DBG_MSG = 0x00000001,
- MWIFIEX_DBG_FATAL = 0x00000002,
- MWIFIEX_DBG_ERROR = 0x00000004,
- MWIFIEX_DBG_DATA = 0x00000008,
- MWIFIEX_DBG_CMD = 0x00000010,
- MWIFIEX_DBG_EVENT = 0x00000020,
- MWIFIEX_DBG_INTR = 0x00000040,
- MWIFIEX_DBG_IOCTL = 0x00000080,
-
- MWIFIEX_DBG_MPA_D = 0x00008000,
- MWIFIEX_DBG_DAT_D = 0x00010000,
- MWIFIEX_DBG_CMD_D = 0x00020000,
- MWIFIEX_DBG_EVT_D = 0x00040000,
- MWIFIEX_DBG_FW_D = 0x00080000,
- MWIFIEX_DBG_IF_D = 0x00100000,
-
- MWIFIEX_DBG_ENTRY = 0x10000000,
- MWIFIEX_DBG_WARN = 0x20000000,
- MWIFIEX_DBG_INFO = 0x40000000,
- MWIFIEX_DBG_DUMP = 0x80000000,
-
- MWIFIEX_DBG_ANY = 0xffffffff
-};
-
-#define MWIFIEX_DEFAULT_DEBUG_MASK (MWIFIEX_DBG_MSG | \
- MWIFIEX_DBG_FATAL | \
- MWIFIEX_DBG_ERROR)
-
-__printf(3, 4)
-void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
- const char *fmt, ...);
-#define mwifiex_dbg(adapter, mask, fmt, ...) \
- _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__)
-
-#define DEBUG_DUMP_DATA_MAX_LEN 128
-#define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \
-do { \
- if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \
- print_hex_dump(KERN_DEBUG, str, \
- DUMP_PREFIX_OFFSET, 16, 1, \
- buf, len, false); \
-} while (0)
-
-struct mwifiex_dbg {
- u32 num_cmd_host_to_card_failure;
- u32 num_cmd_sleep_cfm_host_to_card_failure;
- u32 num_tx_host_to_card_failure;
- u32 num_event_deauth;
- u32 num_event_disassoc;
- u32 num_event_link_lost;
- u32 num_cmd_deauth;
- u32 num_cmd_assoc_success;
- u32 num_cmd_assoc_failure;
- u32 num_tx_timeout;
- u16 timeout_cmd_id;
- u16 timeout_cmd_act;
- u16 last_cmd_id[DBG_CMD_NUM];
- u16 last_cmd_act[DBG_CMD_NUM];
- u16 last_cmd_index;
- u16 last_cmd_resp_id[DBG_CMD_NUM];
- u16 last_cmd_resp_index;
- u16 last_event[DBG_CMD_NUM];
- u16 last_event_index;
-};
-
-enum MWIFIEX_HARDWARE_STATUS {
- MWIFIEX_HW_STATUS_READY,
- MWIFIEX_HW_STATUS_INITIALIZING,
- MWIFIEX_HW_STATUS_INIT_DONE,
- MWIFIEX_HW_STATUS_RESET,
- MWIFIEX_HW_STATUS_CLOSING,
- MWIFIEX_HW_STATUS_NOT_READY
-};
-
-enum MWIFIEX_802_11_POWER_MODE {
- MWIFIEX_802_11_POWER_MODE_CAM,
- MWIFIEX_802_11_POWER_MODE_PSP
-};
-
-struct mwifiex_tx_param {
- u32 next_pkt_len;
-};
-
-enum MWIFIEX_PS_STATE {
- PS_STATE_AWAKE,
- PS_STATE_PRE_SLEEP,
- PS_STATE_SLEEP_CFM,
- PS_STATE_SLEEP
-};
-
-enum mwifiex_iface_type {
- MWIFIEX_SDIO,
- MWIFIEX_PCIE,
- MWIFIEX_USB
-};
-
-struct mwifiex_add_ba_param {
- u32 tx_win_size;
- u32 rx_win_size;
- u32 timeout;
- u8 tx_amsdu;
- u8 rx_amsdu;
-};
-
-struct mwifiex_tx_aggr {
- u8 ampdu_user;
- u8 ampdu_ap;
- u8 amsdu;
-};
-
-enum mwifiex_ba_status {
- BA_SETUP_NONE = 0,
- BA_SETUP_INPROGRESS,
- BA_SETUP_COMPLETE
-};
-
-struct mwifiex_ra_list_tbl {
- struct list_head list;
- struct sk_buff_head skb_head;
- u8 ra[ETH_ALEN];
- u32 is_11n_enabled;
- u16 max_amsdu;
- u16 ba_pkt_count;
- u8 ba_packet_thr;
- enum mwifiex_ba_status ba_status;
- u8 amsdu_in_ampdu;
- u16 total_pkt_count;
- bool tdls_link;
- bool tx_paused;
-};
-
-struct mwifiex_tid_tbl {
- struct list_head ra_list;
-};
-
-#define WMM_HIGHEST_PRIORITY 7
-#define HIGH_PRIO_TID 7
-#define LOW_PRIO_TID 0
-
-struct mwifiex_wmm_desc {
- struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID];
- u32 packets_out[MAX_NUM_TID];
- u32 pkts_paused[MAX_NUM_TID];
- /* spin lock to protect ra_list */
- spinlock_t ra_list_spinlock;
- struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS];
- enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS];
- u32 drv_pkt_delay_max;
- u8 queue_priority[IEEE80211_NUM_ACS];
- u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */
- /* Number of transmit packets queued */
- atomic_t tx_pkts_queued;
- /* Tracks highest priority with a packet queued */
- atomic_t highest_queued_prio;
-};
-
-struct mwifiex_802_11_security {
- u8 wpa_enabled;
- u8 wpa2_enabled;
- u8 wapi_enabled;
- u8 wapi_key_on;
- u8 wep_enabled;
- u32 authentication_mode;
- u8 is_authtype_auto;
- u32 encryption_mode;
-};
-
-struct ieee_types_header {
- u8 element_id;
- u8 len;
-} __packed;
-
-struct ieee_types_vendor_specific {
- struct ieee_types_vendor_header vend_hdr;
- u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)];
-} __packed;
-
-struct ieee_types_generic {
- struct ieee_types_header ieee_hdr;
- u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)];
-} __packed;
-
-struct ieee_types_bss_co_2040 {
- struct ieee_types_header ieee_hdr;
- u8 bss_2040co;
-} __packed;
-
-struct ieee_types_extcap {
- struct ieee_types_header ieee_hdr;
- u8 ext_capab[8];
-} __packed;
-
-struct ieee_types_vht_cap {
- struct ieee_types_header ieee_hdr;
- struct ieee80211_vht_cap vhtcap;
-} __packed;
-
-struct ieee_types_vht_oper {
- struct ieee_types_header ieee_hdr;
- struct ieee80211_vht_operation vhtoper;
-} __packed;
-
-struct ieee_types_aid {
- struct ieee_types_header ieee_hdr;
- u16 aid;
-} __packed;
-
-struct mwifiex_bssdescriptor {
- u8 mac_address[ETH_ALEN];
- struct cfg80211_ssid ssid;
- u32 privacy;
- s32 rssi;
- u32 channel;
- u32 freq;
- u16 beacon_period;
- u8 erp_flags;
- u32 bss_mode;
- u8 supported_rates[MWIFIEX_SUPPORTED_RATES];
- u8 data_rates[MWIFIEX_SUPPORTED_RATES];
- /* Network band.
- * BAND_B(0x01): 'b' band
- * BAND_G(0x02): 'g' band
- * BAND_A(0X04): 'a' band
- */
- u16 bss_band;
- u64 fw_tsf;
- u64 timestamp;
- union ieee_types_phy_param_set phy_param_set;
- union ieee_types_ss_param_set ss_param_set;
- u16 cap_info_bitmap;
- struct ieee_types_wmm_parameter wmm_ie;
- u8 disable_11n;
- struct ieee80211_ht_cap *bcn_ht_cap;
- u16 ht_cap_offset;
- struct ieee80211_ht_operation *bcn_ht_oper;
- u16 ht_info_offset;
- u8 *bcn_bss_co_2040;
- u16 bss_co_2040_offset;
- u8 *bcn_ext_cap;
- u16 ext_cap_offset;
- struct ieee80211_vht_cap *bcn_vht_cap;
- u16 vht_cap_offset;
- struct ieee80211_vht_operation *bcn_vht_oper;
- u16 vht_info_offset;
- struct ieee_types_oper_mode_ntf *oper_mode;
- u16 oper_mode_offset;
- u8 disable_11ac;
- struct ieee_types_vendor_specific *bcn_wpa_ie;
- u16 wpa_offset;
- struct ieee_types_generic *bcn_rsn_ie;
- u16 rsn_offset;
- struct ieee_types_generic *bcn_wapi_ie;
- 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 {
- struct mwifiex_bssdescriptor bss_descriptor;
- u8 wmm_enabled;
- u8 wmm_uapsd_enabled;
- u8 band;
- u32 num_of_rates;
- u8 data_rates[MWIFIEX_SUPPORTED_RATES];
-};
-
-struct mwifiex_sleep_params {
- u16 sp_error;
- u16 sp_offset;
- u16 sp_stable_time;
- u8 sp_cal_control;
- u8 sp_ext_sleep_clk;
- u16 sp_reserved;
-};
-
-struct mwifiex_sleep_period {
- u16 period;
- u16 reserved;
-};
-
-struct mwifiex_wep_key {
- u32 length;
- u32 key_index;
- u32 key_length;
- u8 key_material[MWIFIEX_KEY_BUFFER_SIZE];
-};
-
-#define MAX_REGION_CHANNEL_NUM 2
-
-struct mwifiex_chan_freq_power {
- u16 channel;
- u32 freq;
- u16 max_tx_power;
- u8 unsupported;
-};
-
-enum state_11d_t {
- DISABLE_11D = 0,
- ENABLE_11D = 1,
-};
-
-#define MWIFIEX_MAX_TRIPLET_802_11D 83
-
-struct mwifiex_802_11d_domain_reg {
- u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
- u8 no_of_triplet;
- struct ieee80211_country_ie_triplet
- triplet[MWIFIEX_MAX_TRIPLET_802_11D];
-};
-
-struct mwifiex_vendor_spec_cfg_ie {
- u16 mask;
- u16 flag;
- u8 ie[MWIFIEX_MAX_VSIE_LEN];
-};
-
-struct wps {
- u8 session_enable;
-};
-
-struct mwifiex_roc_cfg {
- u64 cookie;
- struct ieee80211_channel chan;
-};
-
-#define MWIFIEX_FW_DUMP_IDX 0xff
-#define MWIFIEX_DRV_INFO_IDX 20
-#define FW_DUMP_MAX_NAME_LEN 8
-#define FW_DUMP_HOST_READY 0xEE
-#define FW_DUMP_DONE 0xFF
-#define FW_DUMP_READ_DONE 0xFE
-
-struct memory_type_mapping {
- u8 mem_name[FW_DUMP_MAX_NAME_LEN];
- u8 *mem_ptr;
- u32 mem_size;
- u8 done_flag;
-};
-
-enum rdwr_status {
- RDWR_STATUS_SUCCESS = 0,
- RDWR_STATUS_FAILURE = 1,
- RDWR_STATUS_DONE = 2
-};
-
-enum mwifiex_iface_work_flags {
- MWIFIEX_IFACE_WORK_DEVICE_DUMP,
- MWIFIEX_IFACE_WORK_CARD_RESET,
-};
-
-struct mwifiex_private {
- struct mwifiex_adapter *adapter;
- u8 bss_type;
- u8 bss_role;
- u8 bss_priority;
- u8 bss_num;
- u8 bss_started;
- u8 frame_type;
- u8 curr_addr[ETH_ALEN];
- u8 media_connected;
- u8 port_open;
- u8 usb_port;
- u32 num_tx_timeout;
- /* track consecutive timeout */
- u8 tx_timeout_cnt;
- struct net_device *netdev;
- struct net_device_stats stats;
- u16 curr_pkt_filter;
- u32 bss_mode;
- u32 pkt_tx_ctrl;
- u16 tx_power_level;
- u8 max_tx_power_level;
- u8 min_tx_power_level;
- u8 tx_rate;
- u8 tx_htinfo;
- u8 rxpd_htinfo;
- u8 rxpd_rate;
- u16 rate_bitmap;
- u16 bitmap_rates[MAX_BITMAP_RATES_SIZE];
- u32 data_rate;
- u8 is_data_rate_auto;
- u16 bcn_avg_factor;
- u16 data_avg_factor;
- s16 data_rssi_last;
- s16 data_nf_last;
- s16 data_rssi_avg;
- s16 data_nf_avg;
- s16 bcn_rssi_last;
- s16 bcn_nf_last;
- s16 bcn_rssi_avg;
- s16 bcn_nf_avg;
- struct mwifiex_bssdescriptor *attempted_bss_desc;
- struct cfg80211_ssid prev_ssid;
- u8 prev_bssid[ETH_ALEN];
- struct mwifiex_current_bss_params curr_bss_params;
- u16 beacon_period;
- u8 dtim_period;
- u16 listen_interval;
- u16 atim_window;
- u8 adhoc_channel;
- u8 adhoc_is_link_sensed;
- u8 adhoc_state;
- struct mwifiex_802_11_security sec_info;
- struct mwifiex_wep_key wep_key[NUM_WEP_KEYS];
- u16 wep_key_curr_index;
- u8 wpa_ie[256];
- u8 wpa_ie_len;
- u8 wpa_is_gtk_set;
- struct host_cmd_ds_802_11_key_material aes_key;
- struct host_cmd_ds_802_11_key_material_v2 aes_key_v2;
- u8 wapi_ie[256];
- u8 wapi_ie_len;
- u8 *wps_ie;
- u8 wps_ie_len;
- u8 wmm_required;
- u8 wmm_enabled;
- u8 wmm_qosinfo;
- struct mwifiex_wmm_desc wmm;
- atomic_t wmm_tx_pending[IEEE80211_NUM_ACS];
- struct list_head sta_list;
- /* spin lock for associated station/TDLS peers list */
- spinlock_t sta_list_spinlock;
- struct list_head auto_tdls_list;
- /* spin lock for auto TDLS peer list */
- spinlock_t auto_tdls_lock;
- struct list_head tx_ba_stream_tbl_ptr;
- /* spin lock for tx_ba_stream_tbl_ptr queue */
- spinlock_t tx_ba_stream_tbl_lock;
- struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID];
- struct mwifiex_add_ba_param add_ba_param;
- u16 rx_seq[MAX_NUM_TID];
- u8 tos_to_tid_inv[MAX_NUM_TID];
- struct list_head rx_reorder_tbl_ptr;
- /* spin lock for rx_reorder_tbl_ptr queue */
- spinlock_t rx_reorder_tbl_lock;
- /* spin lock for Rx packets */
- spinlock_t rx_pkt_lock;
-
-#define MWIFIEX_ASSOC_RSP_BUF_SIZE 500
- u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE];
- u32 assoc_rsp_size;
-
-#define MWIFIEX_GENIE_BUF_SIZE 256
- u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE];
- u8 gen_ie_buf_len;
-
- struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM];
-
-#define MWIFIEX_ASSOC_TLV_BUF_SIZE 256
- u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE];
- u8 assoc_tlv_buf_len;
-
- u8 *curr_bcn_buf;
- u32 curr_bcn_size;
- /* spin lock for beacon buffer */
- spinlock_t curr_bcn_buf_lock;
- struct wireless_dev wdev;
- struct mwifiex_chan_freq_power cfp;
- char version_str[128];
-#ifdef CONFIG_DEBUG_FS
- struct dentry *dfs_dev_dir;
-#endif
- u16 current_key_index;
- struct semaphore async_sem;
- struct cfg80211_scan_request *scan_request;
- u8 cfg_bssid[6];
- struct wps wps;
- u8 scan_block;
- s32 cqm_rssi_thold;
- u32 cqm_rssi_hyst;
- u8 subsc_evt_rssi_state;
- struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage;
- struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX];
- u16 beacon_idx;
- u16 proberesp_idx;
- u16 assocresp_idx;
- u16 gen_idx;
- u8 ap_11n_enabled;
- u8 ap_11ac_enabled;
- u32 mgmt_frame_mask;
- struct mwifiex_roc_cfg roc_cfg;
- bool scan_aborting;
- u8 csa_chan;
- unsigned long csa_expire_time;
- u8 del_list_idx;
- bool hs2_enabled;
- struct mwifiex_uap_bss_param bss_cfg;
- struct cfg80211_chan_def bss_chandef;
- struct station_parameters *sta_params;
- struct sk_buff_head tdls_txq;
- u8 check_tdls_tx;
- struct timer_list auto_tdls_timer;
- bool auto_tdls_timer_active;
- struct idr ack_status_frames;
- /* spin lock for ack status */
- spinlock_t ack_status_lock;
- /** rx histogram data */
- struct mwifiex_histogram_data *hist_data;
- struct cfg80211_chan_def dfs_chandef;
- struct workqueue_struct *dfs_cac_workqueue;
- struct delayed_work dfs_cac_work;
- struct timer_list dfs_chan_switch_timer;
- struct workqueue_struct *dfs_chan_sw_workqueue;
- struct delayed_work dfs_chan_sw_work;
- struct cfg80211_beacon_data beacon_after;
- struct mwifiex_11h_intf_state state_11h;
- struct mwifiex_ds_mem_rw mem_rw;
- struct sk_buff_head bypass_txq;
- struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX];
-};
-
-
-struct mwifiex_tx_ba_stream_tbl {
- struct list_head list;
- int tid;
- u8 ra[ETH_ALEN];
- enum mwifiex_ba_status ba_status;
- u8 amsdu;
-};
-
-struct mwifiex_rx_reorder_tbl;
-
-struct reorder_tmr_cnxt {
- struct timer_list timer;
- struct mwifiex_rx_reorder_tbl *ptr;
- struct mwifiex_private *priv;
- u8 timer_is_set;
-};
-
-struct mwifiex_rx_reorder_tbl {
- struct list_head list;
- int tid;
- u8 ta[ETH_ALEN];
- int init_win;
- int start_win;
- int win_size;
- void **rx_reorder_ptr;
- struct reorder_tmr_cnxt timer_context;
- u8 amsdu;
- u8 flags;
-};
-
-struct mwifiex_bss_prio_node {
- struct list_head list;
- struct mwifiex_private *priv;
-};
-
-struct mwifiex_bss_prio_tbl {
- struct list_head bss_prio_head;
- /* spin lock for bss priority */
- spinlock_t bss_prio_lock;
- struct mwifiex_bss_prio_node *bss_prio_cur;
-};
-
-struct cmd_ctrl_node {
- struct list_head list;
- struct mwifiex_private *priv;
- u32 cmd_oid;
- u32 cmd_flag;
- struct sk_buff *cmd_skb;
- struct sk_buff *resp_skb;
- void *data_buf;
- u32 wait_q_enabled;
- struct sk_buff *skb;
- u8 *condition;
- u8 cmd_wait_q_woken;
-};
-
-struct mwifiex_bss_priv {
- u8 band;
- u64 fw_tsf;
-};
-
-struct mwifiex_tdls_capab {
- __le16 capab;
- u8 rates[32];
- u8 rates_len;
- u8 qos_info;
- u8 coex_2040;
- u16 aid;
- struct ieee80211_ht_cap ht_capb;
- struct ieee80211_ht_operation ht_oper;
- struct ieee_types_extcap extcap;
- struct ieee_types_generic rsn_ie;
- struct ieee80211_vht_cap vhtcap;
- struct ieee80211_vht_operation vhtoper;
-};
-
-struct mwifiex_station_stats {
- u64 last_rx;
- s8 rssi;
- u64 rx_bytes;
- u64 tx_bytes;
- u32 rx_packets;
- u32 tx_packets;
- u32 tx_failed;
- u8 last_tx_rate;
- u8 last_tx_htinfo;
-};
-
-/* This is AP/TDLS specific structure which stores information
- * about associated/peer STA
- */
-struct mwifiex_sta_node {
- struct list_head list;
- u8 mac_addr[ETH_ALEN];
- u8 is_wmm_enabled;
- u8 is_11n_enabled;
- u8 is_11ac_enabled;
- u8 ampdu_sta[MAX_NUM_TID];
- u16 rx_seq[MAX_NUM_TID];
- u16 max_amsdu;
- u8 tdls_status;
- struct mwifiex_tdls_capab tdls_cap;
- struct mwifiex_station_stats stats;
- u8 tx_pause;
-};
-
-struct mwifiex_auto_tdls_peer {
- struct list_head list;
- u8 mac_addr[ETH_ALEN];
- u8 tdls_status;
- int rssi;
- long rssi_jiffies;
- u8 failure_count;
- u8 do_discover;
- u8 do_setup;
-};
-
-struct mwifiex_if_ops {
- int (*init_if) (struct mwifiex_adapter *);
- void (*cleanup_if) (struct mwifiex_adapter *);
- int (*check_fw_status) (struct mwifiex_adapter *, u32);
- int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
- int (*register_dev) (struct mwifiex_adapter *);
- void (*unregister_dev) (struct mwifiex_adapter *);
- int (*enable_int) (struct mwifiex_adapter *);
- void (*disable_int) (struct mwifiex_adapter *);
- int (*process_int_status) (struct mwifiex_adapter *);
- int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *,
- struct mwifiex_tx_param *);
- int (*wakeup) (struct mwifiex_adapter *);
- int (*wakeup_complete) (struct mwifiex_adapter *);
-
- /* Interface specific functions */
- void (*update_mp_end_port) (struct mwifiex_adapter *, u16);
- void (*cleanup_mpa_buf) (struct mwifiex_adapter *);
- int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *);
- int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
- int (*init_fw_port) (struct mwifiex_adapter *);
- int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
- void (*card_reset) (struct mwifiex_adapter *);
- int (*reg_dump)(struct mwifiex_adapter *, char *);
- void (*device_dump)(struct mwifiex_adapter *);
- int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
- void (*iface_work)(struct work_struct *work);
- void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
- void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *);
- void (*multi_port_resync)(struct mwifiex_adapter *);
- bool (*is_port_ready)(struct mwifiex_private *);
-};
-
-struct mwifiex_adapter {
- u8 iface_type;
- unsigned int debug_mask;
- struct mwifiex_iface_comb iface_limit;
- struct mwifiex_iface_comb curr_iface_comb;
- struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM];
- u8 priv_num;
- const struct firmware *firmware;
- char fw_name[32];
- int winner;
- struct device *dev;
- struct wiphy *wiphy;
- u8 perm_addr[ETH_ALEN];
- bool surprise_removed;
- u32 fw_release_number;
- u16 init_wait_q_woken;
- wait_queue_head_t init_wait_q;
- void *card;
- struct mwifiex_if_ops if_ops;
- atomic_t bypass_tx_pending;
- atomic_t rx_pending;
- atomic_t tx_pending;
- atomic_t cmd_pending;
- struct workqueue_struct *workqueue;
- struct work_struct main_work;
- struct workqueue_struct *rx_workqueue;
- struct work_struct rx_work;
- struct workqueue_struct *dfs_workqueue;
- struct work_struct dfs_work;
- bool rx_work_enabled;
- bool rx_processing;
- bool delay_main_work;
- bool rx_locked;
- bool main_locked;
- struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM];
- /* spin lock for init/shutdown */
- spinlock_t mwifiex_lock;
- /* spin lock for main process */
- spinlock_t main_proc_lock;
- u32 mwifiex_processing;
- u8 more_task_flag;
- u16 tx_buf_size;
- u16 curr_tx_buf_size;
- /* sdio single port rx aggregation capability */
- bool host_disable_sdio_rx_aggr;
- bool sdio_rx_aggr_enable;
- u16 sdio_rx_block_size;
- u32 ioport;
- enum MWIFIEX_HARDWARE_STATUS hw_status;
- u16 number_of_antenna;
- u32 fw_cap_info;
- /* spin lock for interrupt handling */
- spinlock_t int_lock;
- u8 int_status;
- u32 event_cause;
- struct sk_buff *event_skb;
- u8 upld_buf[MWIFIEX_UPLD_SIZE];
- u8 data_sent;
- u8 cmd_sent;
- u8 cmd_resp_received;
- u8 event_received;
- u8 data_received;
- u16 seq_num;
- struct cmd_ctrl_node *cmd_pool;
- struct cmd_ctrl_node *curr_cmd;
- /* spin lock for command */
- spinlock_t mwifiex_cmd_lock;
- u8 is_cmd_timedout;
- u16 last_init_cmd;
- struct timer_list cmd_timer;
- struct list_head cmd_free_q;
- /* spin lock for cmd_free_q */
- spinlock_t cmd_free_q_lock;
- struct list_head cmd_pending_q;
- /* spin lock for cmd_pending_q */
- spinlock_t cmd_pending_q_lock;
- struct list_head scan_pending_q;
- /* spin lock for scan_pending_q */
- spinlock_t scan_pending_q_lock;
- /* spin lock for RX processing routine */
- spinlock_t rx_proc_lock;
- struct sk_buff_head tx_data_q;
- atomic_t tx_queued;
- u32 scan_processing;
- u16 region_code;
- struct mwifiex_802_11d_domain_reg domain_reg;
- u16 scan_probes;
- u32 scan_mode;
- u16 specific_scan_time;
- u16 active_scan_time;
- u16 passive_scan_time;
- u16 scan_chan_gap_time;
- u8 fw_bands;
- u8 adhoc_start_band;
- u8 config_bands;
- struct mwifiex_chan_scan_param_set *scan_channels;
- u8 tx_lock_flag;
- struct mwifiex_sleep_params sleep_params;
- struct mwifiex_sleep_period sleep_period;
- u16 ps_mode;
- u32 ps_state;
- u8 need_to_wakeup;
- u16 multiple_dtim;
- u16 local_listen_interval;
- u16 null_pkt_interval;
- struct sk_buff *sleep_cfm;
- u16 bcn_miss_time_out;
- u16 adhoc_awake_period;
- u8 is_deep_sleep;
- u8 delay_null_pkt;
- u16 delay_to_ps;
- u16 enhanced_ps_mode;
- u8 pm_wakeup_card_req;
- u16 gen_null_pkt;
- u16 pps_uapsd_mode;
- u32 pm_wakeup_fw_try;
- struct timer_list wakeup_timer;
- u8 is_hs_configured;
- struct mwifiex_hs_config_param hs_cfg;
- u8 hs_activated;
- u16 hs_activate_wait_q_woken;
- wait_queue_head_t hs_activate_wait_q;
- bool is_suspended;
- bool hs_enabling;
- u8 event_body[MAX_EVENT_SIZE];
- u32 hw_dot_11n_dev_cap;
- u8 hw_dev_mcs_support;
- u8 user_dev_mcs_support;
- u8 adhoc_11n_enabled;
- u8 sec_chan_offset;
- struct mwifiex_dbg dbg;
- u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE];
- u32 arp_filter_size;
- struct mwifiex_wait_queue cmd_wait_q;
- u8 scan_wait_q_woken;
- spinlock_t queue_lock; /* lock for tx queues */
- u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
- u16 max_mgmt_ie_index;
- const struct firmware *cal_data;
- struct device_node *dt_node;
-
- /* 11AC */
- u32 is_hw_11ac_capable;
- u32 hw_dot_11ac_dev_cap;
- u32 hw_dot_11ac_mcs_support;
- u32 usr_dot_11ac_dev_cap_bg;
- u32 usr_dot_11ac_dev_cap_a;
- u32 usr_dot_11ac_mcs_support;
-
- atomic_t pending_bridged_pkts;
- struct semaphore *card_sem;
- bool ext_scan;
- u8 fw_api_ver;
- u8 key_api_major_ver, key_api_minor_ver;
- struct memory_type_mapping *mem_type_mapping_tbl;
- u8 num_mem_types;
- void *drv_info_dump;
- u32 drv_info_size;
- bool scan_chan_gap_enabled;
- struct sk_buff_head rx_data_q;
- struct mwifiex_chan_stats *chan_stats;
- u32 num_in_chan_stats;
- int survey_idx;
- bool auto_tdls;
- u8 coex_scan;
- u8 coex_min_scan_time;
- u8 coex_max_scan_time;
- u8 coex_win_size;
- u8 coex_tx_win_size;
- u8 coex_rx_win_size;
- bool drcs_enabled;
- u8 active_scan_triggered;
- bool usb_mc_status;
- bool usb_mc_setup;
-};
-
-void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);
-
-int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
-
-void mwifiex_set_trans_start(struct net_device *dev);
-
-void mwifiex_stop_net_dev_queue(struct net_device *netdev,
- struct mwifiex_adapter *adapter);
-
-void mwifiex_wake_up_net_dev_queue(struct net_device *netdev,
- struct mwifiex_adapter *adapter);
-
-int mwifiex_init_priv(struct mwifiex_private *priv);
-void mwifiex_free_priv(struct mwifiex_private *priv);
-
-int mwifiex_init_fw(struct mwifiex_adapter *adapter);
-
-int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter);
-
-int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter);
-
-int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter);
-
-int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *);
-
-int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb);
-
-int mwifiex_process_mgmt_packet(struct mwifiex_private *priv,
- struct sk_buff *skb);
-
-int mwifiex_process_event(struct mwifiex_adapter *adapter);
-
-int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node);
-
-int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,
- u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync);
-
-void mwifiex_cmd_timeout_func(unsigned long function_context);
-
-int mwifiex_get_debug_info(struct mwifiex_private *,
- struct mwifiex_debug_info *);
-
-int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter);
-int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter);
-void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter);
-void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter);
-
-void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node);
-void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node);
-
-void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_node,
- u32 addtail);
-
-int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter);
-int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter);
-int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter,
- struct sk_buff *skb);
-int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
- struct mwifiex_tx_param *tx_param);
-int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags);
-int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
- struct sk_buff *skb, int aggr, int status);
-void mwifiex_clean_txrx(struct mwifiex_private *priv);
-u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv);
-void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter);
-void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *,
- u32);
-int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- u16 cmd_action, uint16_t ps_bitmap,
- struct mwifiex_ds_auto_ds *auto_ds);
-int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp,
- struct mwifiex_ds_pm_cfg *pm_cfg);
-void mwifiex_process_hs_config(struct mwifiex_adapter *adapter);
-void mwifiex_hs_activated_event(struct mwifiex_private *priv,
- u8 activated);
-int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
- int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg);
-int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp);
-int mwifiex_process_rx_packet(struct mwifiex_private *priv,
- struct sk_buff *skb);
-int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no,
- u16 cmd_action, u32 cmd_oid,
- void *data_buf, void *cmd_buf);
-int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
- u16 cmd_action, u32 cmd_oid,
- void *data_buf, void *cmd_buf);
-int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no,
- struct host_cmd_ds_command *resp);
-int mwifiex_process_sta_rx_packet(struct mwifiex_private *,
- struct sk_buff *skb);
-int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
- struct sk_buff *skb);
-int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
- struct sk_buff *skb);
-int mwifiex_process_sta_event(struct mwifiex_private *);
-int mwifiex_process_uap_event(struct mwifiex_private *);
-void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
-void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv,
- const u8 *ra_addr);
-void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
-void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
-int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init);
-int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
- struct mwifiex_scan_cmd_config *scan_cfg);
-void mwifiex_queue_scan_cmd(struct mwifiex_private *priv,
- struct cmd_ctrl_node *cmd_node);
-int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp);
-s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2);
-int mwifiex_associate(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc);
-int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct mwifiex_bssdescriptor *bss_desc);
-int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp);
-void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason);
-u8 mwifiex_band_to_radio_type(u8 band);
-int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac);
-void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter);
-int mwifiex_adhoc_start(struct mwifiex_private *priv,
- struct cfg80211_ssid *adhoc_ssid);
-int mwifiex_adhoc_join(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc);
-int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct cfg80211_ssid *req_ssid);
-int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- struct mwifiex_bssdescriptor *bss_desc);
-int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp);
-int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd);
-struct mwifiex_chan_freq_power *mwifiex_get_cfp(struct mwifiex_private *priv,
- u8 band, u16 channel, u32 freq);
-u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
- u8 index, u8 ht_info);
-u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
- u8 index, u8 ht_info);
-u32 mwifiex_find_freq_from_band_chan(u8, u8);
-int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask,
- u8 **buffer);
-u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv,
- u8 *rates);
-u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates);
-u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
- u8 *rates, u8 radio_type);
-u8 mwifiex_is_rate_auto(struct mwifiex_private *priv);
-extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE];
-void mwifiex_save_curr_bcn(struct mwifiex_private *priv);
-void mwifiex_free_curr_bcn(struct mwifiex_private *priv);
-int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd);
-int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp);
-int is_command_pending(struct mwifiex_adapter *adapter);
-void mwifiex_init_priv_params(struct mwifiex_private *priv,
- struct net_device *dev);
-int mwifiex_set_secure_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_config,
- struct cfg80211_ap_settings *params);
-void mwifiex_set_ht_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params);
-void mwifiex_set_vht_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params);
-void mwifiex_set_tpc_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params);
-void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params);
-void mwifiex_set_vht_width(struct mwifiex_private *priv,
- enum nl80211_chan_width width,
- bool ap_11ac_disable);
-void
-mwifiex_set_wmm_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params);
-void mwifiex_set_ba_params(struct mwifiex_private *priv);
-
-void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *pmadapter);
-void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv,
- struct sk_buff *event_skb);
-
-void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv);
-int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- void *data_buf);
-int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp);
-int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
- void *buf);
-
-/*
- * This function checks if the queuing is RA based or not.
- */
-static inline u8
-mwifiex_queuing_ra_based(struct mwifiex_private *priv)
-{
- /*
- * Currently we assume if we are in Infra, then DA=RA. This might not be
- * true in the future
- */
- if ((priv->bss_mode == NL80211_IFTYPE_STATION) &&
- (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA))
- return false;
-
- return true;
-}
-
-/*
- * This function copies rates.
- */
-static inline u32
-mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len)
-{
- int i;
-
- for (i = 0; i < len && src[i]; i++, pos++) {
- if (pos >= MWIFIEX_SUPPORTED_RATES)
- break;
- dest[pos] = src[i];
- }
-
- return pos;
-}
-
-/*
- * This function returns the correct private structure pointer based
- * upon the BSS type and BSS number.
- */
-static inline struct mwifiex_private *
-mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter,
- u8 bss_num, u8 bss_type)
-{
- int i;
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- if ((adapter->priv[i]->bss_num == bss_num) &&
- (adapter->priv[i]->bss_type == bss_type))
- break;
- }
- }
- return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
-}
-
-/*
- * This function returns the first available private structure pointer
- * based upon the BSS role.
- */
-static inline struct mwifiex_private *
-mwifiex_get_priv(struct mwifiex_adapter *adapter,
- enum mwifiex_bss_role bss_role)
-{
- int i;
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- if (bss_role == MWIFIEX_BSS_ROLE_ANY ||
- GET_BSS_ROLE(adapter->priv[i]) == bss_role)
- break;
- }
- }
-
- return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
-}
-
-/*
- * This function returns the first available unused private structure pointer.
- */
-static inline struct mwifiex_private *
-mwifiex_get_unused_priv(struct mwifiex_adapter *adapter)
-{
- int i;
-
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- if (adapter->priv[i]->bss_mode ==
- NL80211_IFTYPE_UNSPECIFIED)
- break;
- }
- }
-
- return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
-}
-
-/*
- * This function returns the driver private structure of a network device.
- */
-static inline struct mwifiex_private *
-mwifiex_netdev_get_priv(struct net_device *dev)
-{
- return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev));
-}
-
-/*
- * This function checks if a skb holds a management frame.
- */
-static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb)
-{
- return (le32_to_cpu(*(__le32 *)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 (time_after(jiffies, priv->csa_expire_time)) {
- priv->csa_chan = 0;
- priv->csa_expire_time = 0;
- }
-
- return priv->csa_chan;
-}
-
-static inline u8 mwifiex_is_any_intf_active(struct mwifiex_private *priv)
-{
- struct mwifiex_private *priv_num;
- int i;
-
- for (i = 0; i < priv->adapter->priv_num; i++) {
- priv_num = priv->adapter->priv[i];
- if (priv_num) {
- if ((GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_UAP &&
- priv_num->bss_started) ||
- (GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_STA &&
- priv_num->media_connected))
- return 1;
- }
- }
-
- return 0;
-}
-
-static inline u8 mwifiex_is_tdls_link_setup(u8 status)
-{
- switch (status) {
- case TDLS_SETUP_COMPLETE:
- case TDLS_CHAN_SWITCHING:
- case TDLS_IN_BASE_CHAN:
- case TDLS_IN_OFF_CHAN:
- return true;
- default:
- break;
- }
-
- return false;
-}
-
-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);
-int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *);
-
-void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version,
- int maxlen);
-int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
- struct mwifiex_multicast_list *mcast_list);
-int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
- struct net_device *dev);
-int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_queued);
-int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
- struct cfg80211_ssid *req_ssid);
-int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type);
-int mwifiex_enable_hs(struct mwifiex_adapter *adapter);
-int mwifiex_disable_auto_ds(struct mwifiex_private *priv);
-int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate);
-int mwifiex_request_scan(struct mwifiex_private *priv,
- struct cfg80211_ssid *req_ssid);
-int mwifiex_scan_networks(struct mwifiex_private *priv,
- const struct mwifiex_user_scan_cfg *user_scan_in);
-int mwifiex_set_radio(struct mwifiex_private *priv, u8 option);
-
-int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
- const u8 *key, int key_len, u8 key_index,
- const u8 *mac_addr, int disable);
-
-int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len);
-
-int mwifiex_get_ver_ext(struct mwifiex_private *priv);
-
-int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
- struct ieee80211_channel *chan,
- unsigned int duration);
-
-int mwifiex_get_stats_info(struct mwifiex_private *priv,
- struct mwifiex_ds_get_stats *log);
-
-int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type,
- u32 reg_offset, u32 reg_value);
-
-int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type,
- u32 reg_offset, u32 *value);
-
-int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
- u8 *value);
-
-int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data);
-
-int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data);
-
-int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index);
-
-int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index);
-
-int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode);
-
-int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter,
- char *version, int max_len);
-
-int mwifiex_set_tx_power(struct mwifiex_private *priv,
- struct mwifiex_power_cfg *power_cfg);
-
-int mwifiex_main_process(struct mwifiex_adapter *);
-
-int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb);
-
-int mwifiex_get_bss_info(struct mwifiex_private *,
- struct mwifiex_bss_info *);
-int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
- struct cfg80211_bss *bss,
- struct mwifiex_bssdescriptor *bss_desc);
-int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
- struct mwifiex_bssdescriptor *bss_entry);
-int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
- struct mwifiex_bssdescriptor *bss_desc);
-
-u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type);
-u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset);
-
-struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
- const char *name,
- unsigned char name_assign_type,
- enum nl80211_iftype type,
- u32 *flags,
- struct vif_params *params);
-int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev);
-
-void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config);
-
-int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter);
-
-int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
- struct cfg80211_beacon_data *data);
-int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
-u8 *mwifiex_11d_code_2_region(u8 code);
-void mwifiex_uap_set_channel(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_chan_def chandef);
-int mwifiex_config_start_uap(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg);
-void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
- struct mwifiex_sta_node *node);
-
-void mwifiex_init_11h_params(struct mwifiex_private *priv);
-int mwifiex_is_11h_active(struct mwifiex_private *priv);
-int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag);
-
-void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
- struct mwifiex_bssdescriptor *bss_desc);
-int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv);
-int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
- struct device_node *node, const char *prefix);
-void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
-
-extern const struct ethtool_ops mwifiex_ethtool_ops;
-
-void mwifiex_del_all_sta_list(struct mwifiex_private *priv);
-void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac);
-void
-mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
- int ies_len, struct mwifiex_sta_node *node);
-struct mwifiex_sta_node *
-mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac);
-struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac);
-u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv);
-u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv);
-u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv);
-int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
- u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *extra_ies,
- size_t extra_ies_len);
-int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
- u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *extra_ies,
- size_t extra_ies_len);
-void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
- u8 *buf, int len);
-int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action);
-int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac);
-int mwifiex_get_tdls_list(struct mwifiex_private *priv,
- struct tdls_peer_info *buf);
-void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv);
-bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv);
-u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
- u32 pri_chan, u8 chan_bw);
-int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter);
-
-int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb);
-void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv);
-void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
- const u8 *mac, u8 link_status);
-void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv,
- u8 *mac, s8 snr, s8 nflr);
-void mwifiex_check_auto_tdls(unsigned long context);
-void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
-void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
-void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
-int mwifiex_config_tdls_enable(struct mwifiex_private *priv);
-int mwifiex_config_tdls_disable(struct mwifiex_private *priv);
-int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv);
-int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac);
-int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac,
- u8 primary_chan, u8 second_chan_offset, u8 band);
-
-int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- void *data_buf);
-int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv,
- struct sk_buff *skb);
-
-void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
- void *event_body);
-
-struct sk_buff *
-mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
- struct sk_buff *skb, u8 flag, u64 *cookie);
-void mwifiex_dfs_cac_work_queue(struct work_struct *work);
-void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work);
-void mwifiex_abort_cac(struct mwifiex_private *priv);
-int mwifiex_stop_radar_detection(struct mwifiex_private *priv,
- struct cfg80211_chan_def *chandef);
-int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv,
- struct sk_buff *skb);
-
-void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr,
- s8 nflr);
-void mwifiex_hist_data_reset(struct mwifiex_private *priv);
-void mwifiex_hist_data_add(struct mwifiex_private *priv,
- u8 rx_rate, s8 snr, s8 nflr);
-u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
- u8 rx_rate, u8 ht_info);
-
-void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter);
-void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter);
-void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags);
-void mwifiex_queue_main_work(struct mwifiex_adapter *adapter);
-void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter);
-void mwifiex_11n_delba(struct mwifiex_private *priv, int tid);
-int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy);
-void mwifiex_process_tx_pause_event(struct mwifiex_private *priv,
- struct sk_buff *event);
-void mwifiex_process_multi_chan_event(struct mwifiex_private *priv,
- struct sk_buff *event_skb);
-void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter);
-
-#ifdef CONFIG_DEBUG_FS
-void mwifiex_debugfs_init(void);
-void mwifiex_debugfs_remove(void);
-
-void mwifiex_dev_debugfs_init(struct mwifiex_private *priv);
-void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv);
-#endif
-#endif /* !_MWIFIEX_MAIN_H_ */
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
deleted file mode 100644
index 21192b6f9c64..000000000000
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ /dev/null
@@ -1,2742 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: PCIE specific handling
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 <linux/firmware.h>
-
-#include "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-#include "pcie.h"
-
-#define PCIE_VERSION "1.0"
-#define DRV_NAME "Marvell mwifiex PCIe"
-
-static u8 user_rmmod;
-
-static struct mwifiex_if_ops pcie_ops;
-
-static struct semaphore add_remove_card_sem;
-
-static struct memory_type_mapping mem_type_mapping_tbl[] = {
- {"ITCM", NULL, 0, 0xF0},
- {"DTCM", NULL, 0, 0xF1},
- {"SQRAM", NULL, 0, 0xF2},
- {"IRAM", NULL, 0, 0xF3},
- {"APU", NULL, 0, 0xF4},
- {"CIU", NULL, 0, 0xF5},
- {"ICU", NULL, 0, 0xF6},
- {"MAC", NULL, 0, 0xF7},
-};
-
-static int
-mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
- size_t size, int flags)
-{
- struct pcie_service_card *card = adapter->card;
- struct mwifiex_dma_mapping mapping;
-
- mapping.addr = pci_map_single(card->dev, skb->data, size, flags);
- if (pci_dma_mapping_error(card->dev, mapping.addr)) {
- mwifiex_dbg(adapter, ERROR, "failed to map pci memory!\n");
- return -1;
- }
- mapping.len = size;
- mwifiex_store_mapping(skb, &mapping);
- return 0;
-}
-
-static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter,
- struct sk_buff *skb, int flags)
-{
- struct pcie_service_card *card = adapter->card;
- struct mwifiex_dma_mapping mapping;
-
- mwifiex_get_mapping(skb, &mapping);
- pci_unmap_single(card->dev, mapping.addr, mapping.len, flags);
-}
-
-/*
- * This function reads sleep cookie and checks if FW is ready
- */
-static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
-{
- u32 *cookie_addr;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- if (!reg->sleep_cookie)
- return true;
-
- if (card->sleep_cookie_vbase) {
- cookie_addr = (u32 *)card->sleep_cookie_vbase;
- mwifiex_dbg(adapter, INFO,
- "info: ACCESS_HW: sleep cookie=0x%x\n",
- *cookie_addr);
- if (*cookie_addr == FW_AWAKE_COOKIE)
- return true;
- }
-
- return false;
-}
-
-#ifdef CONFIG_PM_SLEEP
-/*
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not suspended, this function allocates and sends a host
- * sleep activate request to the firmware and turns off the traffic.
- */
-static int mwifiex_pcie_suspend(struct device *dev)
-{
- struct mwifiex_adapter *adapter;
- struct pcie_service_card *card;
- int hs_actived;
- struct pci_dev *pdev = to_pci_dev(dev);
-
- if (pdev) {
- card = pci_get_drvdata(pdev);
- if (!card || !card->adapter) {
- pr_err("Card or adapter structure is not valid\n");
- return 0;
- }
- } else {
- pr_err("PCIE device is not specified\n");
- return 0;
- }
-
- adapter = card->adapter;
-
- hs_actived = mwifiex_enable_hs(adapter);
-
- /* Indicate device suspended */
- adapter->is_suspended = true;
- adapter->hs_enabling = false;
-
- return 0;
-}
-
-/*
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not resumed, this function turns on the traffic and
- * sends a host sleep cancel request to the firmware.
- */
-static int mwifiex_pcie_resume(struct device *dev)
-{
- struct mwifiex_adapter *adapter;
- struct pcie_service_card *card;
- struct pci_dev *pdev = to_pci_dev(dev);
-
- if (pdev) {
- card = pci_get_drvdata(pdev);
- if (!card || !card->adapter) {
- pr_err("Card or adapter structure is not valid\n");
- return 0;
- }
- } else {
- pr_err("PCIE device is not specified\n");
- return 0;
- }
-
- adapter = card->adapter;
-
- if (!adapter->is_suspended) {
- mwifiex_dbg(adapter, WARN,
- "Device already resumed\n");
- return 0;
- }
-
- adapter->is_suspended = false;
-
- mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
- MWIFIEX_ASYNC_CMD);
-
- return 0;
-}
-#endif
-
-/*
- * This function probes an mwifiex device and registers it. It allocates
- * the card structure, enables PCIE function number and initiates the
- * device registration and initialization procedure by adding a logical
- * interface.
- */
-static int mwifiex_pcie_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct pcie_service_card *card;
-
- pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
- pdev->vendor, pdev->device, pdev->revision);
-
- card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL);
- if (!card)
- return -ENOMEM;
-
- card->dev = pdev;
-
- if (ent->driver_data) {
- struct mwifiex_pcie_device *data = (void *)ent->driver_data;
- card->pcie.firmware = data->firmware;
- card->pcie.reg = data->reg;
- card->pcie.blksz_fw_dl = data->blksz_fw_dl;
- card->pcie.tx_buf_size = data->tx_buf_size;
- card->pcie.can_dump_fw = data->can_dump_fw;
- card->pcie.can_ext_scan = data->can_ext_scan;
- }
-
- if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
- MWIFIEX_PCIE)) {
- pr_err("%s failed\n", __func__);
- kfree(card);
- return -1;
- }
-
- return 0;
-}
-
-/*
- * This function removes the interface and frees up the card structure.
- */
-static void mwifiex_pcie_remove(struct pci_dev *pdev)
-{
- struct pcie_service_card *card;
- struct mwifiex_adapter *adapter;
- struct mwifiex_private *priv;
-
- card = pci_get_drvdata(pdev);
- if (!card)
- return;
-
- adapter = card->adapter;
- if (!adapter || !adapter->priv_num)
- return;
-
- if (user_rmmod) {
-#ifdef CONFIG_PM_SLEEP
- if (adapter->is_suspended)
- mwifiex_pcie_resume(&pdev->dev);
-#endif
-
- mwifiex_deauthenticate_all(adapter);
-
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-
- mwifiex_disable_auto_ds(priv);
-
- mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN);
- }
-
- mwifiex_remove_card(card->adapter, &add_remove_card_sem);
-}
-
-static void mwifiex_pcie_shutdown(struct pci_dev *pdev)
-{
- user_rmmod = 1;
- mwifiex_pcie_remove(pdev);
-
- return;
-}
-
-static const struct pci_device_id mwifiex_ids[] = {
- {
- PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- .driver_data = (unsigned long)&mwifiex_pcie8766,
- },
- {
- PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- .driver_data = (unsigned long)&mwifiex_pcie8897,
- },
- {
- PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- .driver_data = (unsigned long)&mwifiex_pcie8997,
- },
- {},
-};
-
-MODULE_DEVICE_TABLE(pci, mwifiex_ids);
-
-#ifdef CONFIG_PM_SLEEP
-/* Power Management Hooks */
-static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend,
- mwifiex_pcie_resume);
-#endif
-
-/* PCI Device Driver */
-static struct pci_driver __refdata mwifiex_pcie = {
- .name = "mwifiex_pcie",
- .id_table = mwifiex_ids,
- .probe = mwifiex_pcie_probe,
- .remove = mwifiex_pcie_remove,
-#ifdef CONFIG_PM_SLEEP
- .driver = {
- .pm = &mwifiex_pcie_pm_ops,
- },
-#endif
- .shutdown = mwifiex_pcie_shutdown,
-};
-
-/*
- * This function writes data into PCIE card register.
- */
-static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data)
-{
- struct pcie_service_card *card = adapter->card;
-
- iowrite32(data, card->pci_mmap1 + reg);
-
- return 0;
-}
-
-/*
- * This function reads data from PCIE card register.
- */
-static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
-{
- struct pcie_service_card *card = adapter->card;
-
- *data = ioread32(card->pci_mmap1 + reg);
-
- return 0;
-}
-
-/* This function reads u8 data from PCIE card register. */
-static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter,
- int reg, u8 *data)
-{
- struct pcie_service_card *card = adapter->card;
-
- *data = ioread8(card->pci_mmap1 + reg);
-
- return 0;
-}
-
-/*
- * This function adds delay loop to ensure FW is awake before proceeding.
- */
-static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter)
-{
- int i = 0;
-
- while (mwifiex_pcie_ok_to_access_hw(adapter)) {
- i++;
- usleep_range(10, 20);
- /* 50ms max wait */
- if (i == 5000)
- break;
- }
-
- return;
-}
-
-static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter,
- u32 max_delay_loop_cnt)
-{
- struct pcie_service_card *card = adapter->card;
- u8 *buffer;
- u32 sleep_cookie, count;
-
- for (count = 0; count < max_delay_loop_cnt; count++) {
- buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN;
- sleep_cookie = *(u32 *)buffer;
-
- if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) {
- mwifiex_dbg(adapter, INFO,
- "sleep cookie found at count %d\n", count);
- break;
- }
- usleep_range(20, 30);
- }
-
- if (count >= max_delay_loop_cnt)
- mwifiex_dbg(adapter, INFO,
- "max count reached while accessing sleep cookie\n");
-}
-
-/* This function wakes up the card by reading fw_status register. */
-static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
-{
- u32 fw_status;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- mwifiex_dbg(adapter, EVENT,
- "event: Wakeup device...\n");
-
- if (reg->sleep_cookie)
- mwifiex_pcie_dev_wakeup_delay(adapter);
-
- /* Reading fw_status register will wakeup device */
- if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) {
- mwifiex_dbg(adapter, ERROR,
- "Reading fw_status register failed\n");
- return -1;
- }
-
- if (reg->sleep_cookie) {
- mwifiex_pcie_dev_wakeup_delay(adapter);
- mwifiex_dbg(adapter, INFO,
- "PCIE wakeup: Setting PS_STATE_AWAKE\n");
- adapter->ps_state = PS_STATE_AWAKE;
- }
-
- return 0;
-}
-
-/*
- * This function is called after the card has woken up.
- *
- * The card configuration register is reset.
- */
-static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
-{
- mwifiex_dbg(adapter, CMD,
- "cmd: Wakeup device completed\n");
-
- return 0;
-}
-
-/*
- * This function disables the host interrupt.
- *
- * The host interrupt mask is read, the disable bit is reset and
- * written back to the card host interrupt mask register.
- */
-static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter)
-{
- if (mwifiex_pcie_ok_to_access_hw(adapter)) {
- if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK,
- 0x00000000)) {
- mwifiex_dbg(adapter, ERROR,
- "Disable host interrupt failed\n");
- return -1;
- }
- }
-
- return 0;
-}
-
-/*
- * This function enables the host interrupt.
- *
- * The host interrupt enable mask is written to the card
- * host interrupt mask register.
- */
-static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter)
-{
- if (mwifiex_pcie_ok_to_access_hw(adapter)) {
- /* Simply write the mask to the register */
- if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK,
- HOST_INTR_MASK)) {
- mwifiex_dbg(adapter, ERROR,
- "Enable host interrupt failed\n");
- return -1;
- }
- }
-
- return 0;
-}
-
-/*
- * This function initializes TX buffer ring descriptors
- */
-static int mwifiex_init_txq_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- struct mwifiex_pcie_buf_desc *desc;
- struct mwifiex_pfu_buf_desc *desc2;
- int i;
-
- for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
- card->tx_buf_list[i] = NULL;
- if (reg->pfu_enabled) {
- card->txbd_ring[i] = (void *)card->txbd_ring_vbase +
- (sizeof(*desc2) * i);
- desc2 = card->txbd_ring[i];
- memset(desc2, 0, sizeof(*desc2));
- } else {
- card->txbd_ring[i] = (void *)card->txbd_ring_vbase +
- (sizeof(*desc) * i);
- desc = card->txbd_ring[i];
- memset(desc, 0, sizeof(*desc));
- }
- }
-
- return 0;
-}
-
-/* This function initializes RX buffer ring descriptors. Each SKB is allocated
- * here and after mapping PCI memory, its physical address is assigned to
- * PCIE Rx buffer descriptor's physical address.
- */
-static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- struct sk_buff *skb;
- struct mwifiex_pcie_buf_desc *desc;
- struct mwifiex_pfu_buf_desc *desc2;
- dma_addr_t buf_pa;
- int i;
-
- for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
- /* Allocate skb here so that firmware can DMA data from it */
- skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
- GFP_KERNEL | GFP_DMA);
- if (!skb) {
- mwifiex_dbg(adapter, ERROR,
- "Unable to allocate skb for RX ring.\n");
- kfree(card->rxbd_ring_vbase);
- return -ENOMEM;
- }
-
- if (mwifiex_map_pci_memory(adapter, skb,
- MWIFIEX_RX_DATA_BUF_SIZE,
- PCI_DMA_FROMDEVICE))
- return -1;
-
- buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
-
- mwifiex_dbg(adapter, INFO,
- "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
- skb, skb->len, skb->data, (u32)buf_pa,
- (u32)((u64)buf_pa >> 32));
-
- card->rx_buf_list[i] = skb;
- if (reg->pfu_enabled) {
- card->rxbd_ring[i] = (void *)card->rxbd_ring_vbase +
- (sizeof(*desc2) * i);
- desc2 = card->rxbd_ring[i];
- desc2->paddr = buf_pa;
- desc2->len = (u16)skb->len;
- desc2->frag_len = (u16)skb->len;
- desc2->flags = reg->ring_flag_eop | reg->ring_flag_sop;
- desc2->offset = 0;
- } else {
- card->rxbd_ring[i] = (void *)(card->rxbd_ring_vbase +
- (sizeof(*desc) * i));
- desc = card->rxbd_ring[i];
- desc->paddr = buf_pa;
- desc->len = (u16)skb->len;
- desc->flags = 0;
- }
- }
-
- return 0;
-}
-
-/* This function initializes event buffer ring descriptors. Each SKB is
- * allocated here and after mapping PCI memory, its physical address is assigned
- * to PCIE Rx buffer descriptor's physical address
- */
-static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- struct mwifiex_evt_buf_desc *desc;
- struct sk_buff *skb;
- dma_addr_t buf_pa;
- int i;
-
- for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
- /* Allocate skb here so that firmware can DMA data from it */
- skb = dev_alloc_skb(MAX_EVENT_SIZE);
- if (!skb) {
- mwifiex_dbg(adapter, ERROR,
- "Unable to allocate skb for EVENT buf.\n");
- kfree(card->evtbd_ring_vbase);
- return -ENOMEM;
- }
- skb_put(skb, MAX_EVENT_SIZE);
-
- if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE,
- PCI_DMA_FROMDEVICE))
- return -1;
-
- buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
-
- mwifiex_dbg(adapter, EVENT,
- "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
- skb, skb->len, skb->data, (u32)buf_pa,
- (u32)((u64)buf_pa >> 32));
-
- card->evt_buf_list[i] = skb;
- card->evtbd_ring[i] = (void *)(card->evtbd_ring_vbase +
- (sizeof(*desc) * i));
- desc = card->evtbd_ring[i];
- desc->paddr = buf_pa;
- desc->len = (u16)skb->len;
- desc->flags = 0;
- }
-
- return 0;
-}
-
-/* This function cleans up TX buffer rings. If any of the buffer list has valid
- * SKB address, associated SKB is freed.
- */
-static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- struct sk_buff *skb;
- struct mwifiex_pcie_buf_desc *desc;
- struct mwifiex_pfu_buf_desc *desc2;
- int i;
-
- for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
- if (reg->pfu_enabled) {
- desc2 = card->txbd_ring[i];
- if (card->tx_buf_list[i]) {
- skb = card->tx_buf_list[i];
- mwifiex_unmap_pci_memory(adapter, skb,
- PCI_DMA_TODEVICE);
- dev_kfree_skb_any(skb);
- }
- memset(desc2, 0, sizeof(*desc2));
- } else {
- desc = card->txbd_ring[i];
- if (card->tx_buf_list[i]) {
- skb = card->tx_buf_list[i];
- mwifiex_unmap_pci_memory(adapter, skb,
- PCI_DMA_TODEVICE);
- dev_kfree_skb_any(skb);
- }
- memset(desc, 0, sizeof(*desc));
- }
- card->tx_buf_list[i] = NULL;
- }
-
- return;
-}
-
-/* This function cleans up RX buffer rings. If any of the buffer list has valid
- * SKB address, associated SKB is freed.
- */
-static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- struct mwifiex_pcie_buf_desc *desc;
- struct mwifiex_pfu_buf_desc *desc2;
- struct sk_buff *skb;
- int i;
-
- for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
- if (reg->pfu_enabled) {
- desc2 = card->rxbd_ring[i];
- if (card->rx_buf_list[i]) {
- skb = card->rx_buf_list[i];
- mwifiex_unmap_pci_memory(adapter, skb,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb_any(skb);
- }
- memset(desc2, 0, sizeof(*desc2));
- } else {
- desc = card->rxbd_ring[i];
- if (card->rx_buf_list[i]) {
- skb = card->rx_buf_list[i];
- mwifiex_unmap_pci_memory(adapter, skb,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb_any(skb);
- }
- memset(desc, 0, sizeof(*desc));
- }
- card->rx_buf_list[i] = NULL;
- }
-
- return;
-}
-
-/* This function cleans up event buffer rings. If any of the buffer list has
- * valid SKB address, associated SKB is freed.
- */
-static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- struct mwifiex_evt_buf_desc *desc;
- struct sk_buff *skb;
- int i;
-
- for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
- desc = card->evtbd_ring[i];
- if (card->evt_buf_list[i]) {
- skb = card->evt_buf_list[i];
- mwifiex_unmap_pci_memory(adapter, skb,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb_any(skb);
- }
- card->evt_buf_list[i] = NULL;
- memset(desc, 0, sizeof(*desc));
- }
-
- return;
-}
-
-/* This function creates buffer descriptor ring for TX
- */
-static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- /*
- * driver maintaines the write pointer and firmware maintaines the read
- * pointer. The write pointer starts at 0 (zero) while the read pointer
- * starts at zero with rollover bit set
- */
- card->txbd_wrptr = 0;
-
- if (reg->pfu_enabled)
- card->txbd_rdptr = 0;
- else
- card->txbd_rdptr |= reg->tx_rollover_ind;
-
- /* allocate shared memory for the BD ring and divide the same in to
- several descriptors */
- if (reg->pfu_enabled)
- card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) *
- MWIFIEX_MAX_TXRX_BD;
- else
- card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
- MWIFIEX_MAX_TXRX_BD;
-
- mwifiex_dbg(adapter, INFO,
- "info: txbd_ring: Allocating %d bytes\n",
- card->txbd_ring_size);
- card->txbd_ring_vbase = pci_alloc_consistent(card->dev,
- card->txbd_ring_size,
- &card->txbd_ring_pbase);
- if (!card->txbd_ring_vbase) {
- mwifiex_dbg(adapter, ERROR,
- "allocate consistent memory (%d bytes) failed!\n",
- card->txbd_ring_size);
- return -ENOMEM;
- }
- mwifiex_dbg(adapter, DATA,
- "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n",
- card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase,
- (u32)((u64)card->txbd_ring_pbase >> 32),
- card->txbd_ring_size);
-
- return mwifiex_init_txq_ring(adapter);
-}
-
-static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- mwifiex_cleanup_txq_ring(adapter);
-
- if (card->txbd_ring_vbase)
- pci_free_consistent(card->dev, card->txbd_ring_size,
- card->txbd_ring_vbase,
- card->txbd_ring_pbase);
- card->txbd_ring_size = 0;
- card->txbd_wrptr = 0;
- card->txbd_rdptr = 0 | reg->tx_rollover_ind;
- card->txbd_ring_vbase = NULL;
- card->txbd_ring_pbase = 0;
-
- return 0;
-}
-
-/*
- * This function creates buffer descriptor ring for RX
- */
-static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- /*
- * driver maintaines the read pointer and firmware maintaines the write
- * pointer. The write pointer starts at 0 (zero) while the read pointer
- * starts at zero with rollover bit set
- */
- card->rxbd_wrptr = 0;
- card->rxbd_rdptr = reg->rx_rollover_ind;
-
- if (reg->pfu_enabled)
- card->rxbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) *
- MWIFIEX_MAX_TXRX_BD;
- else
- card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
- MWIFIEX_MAX_TXRX_BD;
-
- mwifiex_dbg(adapter, INFO,
- "info: rxbd_ring: Allocating %d bytes\n",
- card->rxbd_ring_size);
- card->rxbd_ring_vbase = pci_alloc_consistent(card->dev,
- card->rxbd_ring_size,
- &card->rxbd_ring_pbase);
- if (!card->rxbd_ring_vbase) {
- mwifiex_dbg(adapter, ERROR,
- "allocate consistent memory (%d bytes) failed!\n",
- card->rxbd_ring_size);
- return -ENOMEM;
- }
-
- mwifiex_dbg(adapter, DATA,
- "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n",
- card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase,
- (u32)((u64)card->rxbd_ring_pbase >> 32),
- card->rxbd_ring_size);
-
- return mwifiex_init_rxq_ring(adapter);
-}
-
-/*
- * This function deletes Buffer descriptor ring for RX
- */
-static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- mwifiex_cleanup_rxq_ring(adapter);
-
- if (card->rxbd_ring_vbase)
- pci_free_consistent(card->dev, card->rxbd_ring_size,
- card->rxbd_ring_vbase,
- card->rxbd_ring_pbase);
- card->rxbd_ring_size = 0;
- card->rxbd_wrptr = 0;
- card->rxbd_rdptr = 0 | reg->rx_rollover_ind;
- card->rxbd_ring_vbase = NULL;
- card->rxbd_ring_pbase = 0;
-
- return 0;
-}
-
-/*
- * This function creates buffer descriptor ring for Events
- */
-static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- /*
- * driver maintaines the read pointer and firmware maintaines the write
- * pointer. The write pointer starts at 0 (zero) while the read pointer
- * starts at zero with rollover bit set
- */
- card->evtbd_wrptr = 0;
- card->evtbd_rdptr = reg->evt_rollover_ind;
-
- card->evtbd_ring_size = sizeof(struct mwifiex_evt_buf_desc) *
- MWIFIEX_MAX_EVT_BD;
-
- mwifiex_dbg(adapter, INFO,
- "info: evtbd_ring: Allocating %d bytes\n",
- card->evtbd_ring_size);
- card->evtbd_ring_vbase = pci_alloc_consistent(card->dev,
- card->evtbd_ring_size,
- &card->evtbd_ring_pbase);
- if (!card->evtbd_ring_vbase) {
- mwifiex_dbg(adapter, ERROR,
- "allocate consistent memory (%d bytes) failed!\n",
- card->evtbd_ring_size);
- return -ENOMEM;
- }
-
- mwifiex_dbg(adapter, EVENT,
- "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n",
- card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase,
- (u32)((u64)card->evtbd_ring_pbase >> 32),
- card->evtbd_ring_size);
-
- return mwifiex_pcie_init_evt_ring(adapter);
-}
-
-/*
- * This function deletes Buffer descriptor ring for Events
- */
-static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- mwifiex_cleanup_evt_ring(adapter);
-
- if (card->evtbd_ring_vbase)
- pci_free_consistent(card->dev, card->evtbd_ring_size,
- card->evtbd_ring_vbase,
- card->evtbd_ring_pbase);
- card->evtbd_wrptr = 0;
- card->evtbd_rdptr = 0 | reg->evt_rollover_ind;
- card->evtbd_ring_size = 0;
- card->evtbd_ring_vbase = NULL;
- card->evtbd_ring_pbase = 0;
-
- return 0;
-}
-
-/*
- * This function allocates a buffer for CMDRSP
- */
-static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- struct sk_buff *skb;
-
- /* Allocate memory for receiving command response data */
- skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE);
- if (!skb) {
- mwifiex_dbg(adapter, ERROR,
- "Unable to allocate skb for command response data.\n");
- return -ENOMEM;
- }
- skb_put(skb, MWIFIEX_UPLD_SIZE);
- if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
- PCI_DMA_FROMDEVICE))
- return -1;
-
- card->cmdrsp_buf = skb;
-
- return 0;
-}
-
-/*
- * This function deletes a buffer for CMDRSP
- */
-static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card;
-
- if (!adapter)
- return 0;
-
- card = adapter->card;
-
- if (card && card->cmdrsp_buf) {
- mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb_any(card->cmdrsp_buf);
- }
-
- if (card && card->cmd_buf) {
- mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
- PCI_DMA_TODEVICE);
- }
- return 0;
-}
-
-/*
- * This function allocates a buffer for sleep cookie
- */
-static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
-
- card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32),
- &card->sleep_cookie_pbase);
- if (!card->sleep_cookie_vbase) {
- mwifiex_dbg(adapter, ERROR,
- "pci_alloc_consistent failed!\n");
- return -ENOMEM;
- }
- /* Init val of Sleep Cookie */
- *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE;
-
- mwifiex_dbg(adapter, INFO,
- "alloc_scook: sleep cookie=0x%x\n",
- *((u32 *)card->sleep_cookie_vbase));
-
- return 0;
-}
-
-/*
- * This function deletes buffer for sleep cookie
- */
-static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card;
-
- if (!adapter)
- return 0;
-
- card = adapter->card;
-
- if (card && card->sleep_cookie_vbase) {
- pci_free_consistent(card->dev, sizeof(u32),
- card->sleep_cookie_vbase,
- card->sleep_cookie_pbase);
- card->sleep_cookie_vbase = NULL;
- }
-
- return 0;
-}
-
-/* This function flushes the TX buffer descriptor ring
- * This function defined as handler is also called while cleaning TXRX
- * during disconnect/ bss stop.
- */
-static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
-
- if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) {
- card->txbd_flush = 1;
- /* write pointer already set at last send
- * send dnld-rdy intr again, wait for completion.
- */
- if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
- CPU_INTR_DNLD_RDY)) {
- mwifiex_dbg(adapter, ERROR,
- "failed to assert dnld-rdy interrupt.\n");
- return -1;
- }
- }
- return 0;
-}
-
-/*
- * This function unmaps and frees downloaded data buffer
- */
-static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
-{
- struct sk_buff *skb;
- u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0;
- struct mwifiex_pcie_buf_desc *desc;
- struct mwifiex_pfu_buf_desc *desc2;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- if (!mwifiex_pcie_ok_to_access_hw(adapter))
- mwifiex_pm_wakeup_card(adapter);
-
- /* Read the TX ring read pointer set by firmware */
- if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) {
- mwifiex_dbg(adapter, ERROR,
- "SEND COMP: failed to read reg->tx_rdptr\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, DATA,
- "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n",
- card->txbd_rdptr, rdptr);
-
- num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr;
- /* free from previous txbd_rdptr to current txbd_rdptr */
- while (((card->txbd_rdptr & reg->tx_mask) !=
- (rdptr & reg->tx_mask)) ||
- ((card->txbd_rdptr & reg->tx_rollover_ind) !=
- (rdptr & reg->tx_rollover_ind))) {
- wrdoneidx = (card->txbd_rdptr & reg->tx_mask) >>
- reg->tx_start_ptr;
-
- skb = card->tx_buf_list[wrdoneidx];
-
- if (skb) {
- mwifiex_dbg(adapter, DATA,
- "SEND COMP: Detach skb %p at txbd_rdidx=%d\n",
- skb, wrdoneidx);
- mwifiex_unmap_pci_memory(adapter, skb,
- PCI_DMA_TODEVICE);
-
- unmap_count++;
-
- if (card->txbd_flush)
- mwifiex_write_data_complete(adapter, skb, 0,
- -1);
- else
- mwifiex_write_data_complete(adapter, skb, 0, 0);
- }
-
- card->tx_buf_list[wrdoneidx] = NULL;
-
- if (reg->pfu_enabled) {
- desc2 = card->txbd_ring[wrdoneidx];
- memset(desc2, 0, sizeof(*desc2));
- } else {
- desc = card->txbd_ring[wrdoneidx];
- memset(desc, 0, sizeof(*desc));
- }
- switch (card->dev->device) {
- case PCIE_DEVICE_ID_MARVELL_88W8766P:
- card->txbd_rdptr++;
- break;
- case PCIE_DEVICE_ID_MARVELL_88W8897:
- case PCIE_DEVICE_ID_MARVELL_88W8997:
- card->txbd_rdptr += reg->ring_tx_start_ptr;
- break;
- }
-
-
- if ((card->txbd_rdptr & reg->tx_mask) == num_tx_buffs)
- card->txbd_rdptr = ((card->txbd_rdptr &
- reg->tx_rollover_ind) ^
- reg->tx_rollover_ind);
- }
-
- if (unmap_count)
- adapter->data_sent = false;
-
- if (card->txbd_flush) {
- if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr))
- card->txbd_flush = 0;
- else
- mwifiex_clean_pcie_ring_buf(adapter);
- }
-
- return 0;
-}
-
-/* This function sends data buffer to device. First 4 bytes of payload
- * are filled with payload length and payload type. Then this payload
- * is mapped to PCI device memory. Tx ring pointers are advanced accordingly.
- * Download ready interrupt to FW is deffered if Tx ring is not full and
- * additional payload can be accomodated.
- * Caller must ensure tx_param parameter to this function is not NULL.
- */
-static int
-mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
- struct mwifiex_tx_param *tx_param)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- u32 wrindx, num_tx_buffs, rx_val;
- int ret;
- dma_addr_t buf_pa;
- struct mwifiex_pcie_buf_desc *desc = NULL;
- struct mwifiex_pfu_buf_desc *desc2 = NULL;
- __le16 *tmp;
-
- if (!(skb->data && skb->len)) {
- mwifiex_dbg(adapter, ERROR,
- "%s(): invalid parameter <%p, %#x>\n",
- __func__, skb->data, skb->len);
- return -1;
- }
-
- if (!mwifiex_pcie_ok_to_access_hw(adapter))
- mwifiex_pm_wakeup_card(adapter);
-
- num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr;
- mwifiex_dbg(adapter, DATA,
- "info: SEND DATA: <Rd: %#x, Wr: %#x>\n",
- card->txbd_rdptr, card->txbd_wrptr);
- if (mwifiex_pcie_txbd_not_full(card)) {
- u8 *payload;
-
- adapter->data_sent = true;
- payload = skb->data;
- tmp = (__le16 *)&payload[0];
- *tmp = cpu_to_le16((u16)skb->len);
- tmp = (__le16 *)&payload[2];
- *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA);
-
- if (mwifiex_map_pci_memory(adapter, skb, skb->len,
- PCI_DMA_TODEVICE))
- return -1;
-
- wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr;
- buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
- card->tx_buf_list[wrindx] = skb;
-
- if (reg->pfu_enabled) {
- desc2 = card->txbd_ring[wrindx];
- desc2->paddr = buf_pa;
- desc2->len = (u16)skb->len;
- desc2->frag_len = (u16)skb->len;
- desc2->offset = 0;
- desc2->flags = MWIFIEX_BD_FLAG_FIRST_DESC |
- MWIFIEX_BD_FLAG_LAST_DESC;
- } else {
- desc = card->txbd_ring[wrindx];
- desc->paddr = buf_pa;
- desc->len = (u16)skb->len;
- desc->flags = MWIFIEX_BD_FLAG_FIRST_DESC |
- MWIFIEX_BD_FLAG_LAST_DESC;
- }
-
- switch (card->dev->device) {
- case PCIE_DEVICE_ID_MARVELL_88W8766P:
- card->txbd_wrptr++;
- break;
- case PCIE_DEVICE_ID_MARVELL_88W8897:
- case PCIE_DEVICE_ID_MARVELL_88W8997:
- card->txbd_wrptr += reg->ring_tx_start_ptr;
- break;
- }
-
- if ((card->txbd_wrptr & reg->tx_mask) == num_tx_buffs)
- card->txbd_wrptr = ((card->txbd_wrptr &
- reg->tx_rollover_ind) ^
- reg->tx_rollover_ind);
-
- rx_val = card->rxbd_rdptr & reg->rx_wrap_mask;
- /* Write the TX ring write pointer in to reg->tx_wrptr */
- if (mwifiex_write_reg(adapter, reg->tx_wrptr,
- card->txbd_wrptr | rx_val)) {
- mwifiex_dbg(adapter, ERROR,
- "SEND DATA: failed to write reg->tx_wrptr\n");
- ret = -1;
- goto done_unmap;
- }
- if ((mwifiex_pcie_txbd_not_full(card)) &&
- tx_param->next_pkt_len) {
- /* have more packets and TxBD still can hold more */
- mwifiex_dbg(adapter, DATA,
- "SEND DATA: delay dnld-rdy interrupt.\n");
- adapter->data_sent = false;
- } else {
- /* Send the TX ready interrupt */
- if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
- CPU_INTR_DNLD_RDY)) {
- mwifiex_dbg(adapter, ERROR,
- "SEND DATA: failed to assert dnld-rdy interrupt.\n");
- ret = -1;
- goto done_unmap;
- }
- }
- mwifiex_dbg(adapter, DATA,
- "info: SEND DATA: Updated <Rd: %#x, Wr:\t"
- "%#x> and sent packet to firmware successfully\n",
- card->txbd_rdptr, card->txbd_wrptr);
- } else {
- mwifiex_dbg(adapter, DATA,
- "info: TX Ring full, can't send packets to fw\n");
- adapter->data_sent = true;
- /* Send the TX ready interrupt */
- if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
- CPU_INTR_DNLD_RDY))
- mwifiex_dbg(adapter, ERROR,
- "SEND DATA: failed to assert door-bell intr\n");
- return -EBUSY;
- }
-
- return -EINPROGRESS;
-done_unmap:
- mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
- card->tx_buf_list[wrindx] = NULL;
- if (reg->pfu_enabled)
- memset(desc2, 0, sizeof(*desc2));
- else
- memset(desc, 0, sizeof(*desc));
-
- return ret;
-}
-
-/*
- * This function handles received buffer ring and
- * dispatches packets to upper
- */
-static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- u32 wrptr, rd_index, tx_val;
- dma_addr_t buf_pa;
- int ret = 0;
- struct sk_buff *skb_tmp = NULL;
- struct mwifiex_pcie_buf_desc *desc;
- struct mwifiex_pfu_buf_desc *desc2;
-
- if (!mwifiex_pcie_ok_to_access_hw(adapter))
- mwifiex_pm_wakeup_card(adapter);
-
- /* Read the RX ring Write pointer set by firmware */
- if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) {
- mwifiex_dbg(adapter, ERROR,
- "RECV DATA: failed to read reg->rx_wrptr\n");
- ret = -1;
- goto done;
- }
- card->rxbd_wrptr = wrptr;
-
- while (((wrptr & reg->rx_mask) !=
- (card->rxbd_rdptr & reg->rx_mask)) ||
- ((wrptr & reg->rx_rollover_ind) ==
- (card->rxbd_rdptr & reg->rx_rollover_ind))) {
- struct sk_buff *skb_data;
- u16 rx_len;
- __le16 pkt_len;
-
- rd_index = card->rxbd_rdptr & reg->rx_mask;
- skb_data = card->rx_buf_list[rd_index];
-
- /* If skb allocation was failed earlier for Rx packet,
- * rx_buf_list[rd_index] would have been left with a NULL.
- */
- if (!skb_data)
- return -ENOMEM;
-
- mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE);
- card->rx_buf_list[rd_index] = NULL;
-
- /* Get data length from interface header -
- * first 2 bytes for len, next 2 bytes is for type
- */
- pkt_len = *((__le16 *)skb_data->data);
- rx_len = le16_to_cpu(pkt_len);
- if (WARN_ON(rx_len <= INTF_HEADER_LEN ||
- rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) {
- mwifiex_dbg(adapter, ERROR,
- "Invalid RX len %d, Rd=%#x, Wr=%#x\n",
- rx_len, card->rxbd_rdptr, wrptr);
- dev_kfree_skb_any(skb_data);
- } else {
- skb_put(skb_data, rx_len);
- mwifiex_dbg(adapter, DATA,
- "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n",
- card->rxbd_rdptr, wrptr, rx_len);
- skb_pull(skb_data, INTF_HEADER_LEN);
- if (adapter->rx_work_enabled) {
- skb_queue_tail(&adapter->rx_data_q, skb_data);
- adapter->data_received = true;
- atomic_inc(&adapter->rx_pending);
- } else {
- mwifiex_handle_rx_packet(adapter, skb_data);
- }
- }
-
- skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
- GFP_KERNEL | GFP_DMA);
- if (!skb_tmp) {
- mwifiex_dbg(adapter, ERROR,
- "Unable to allocate skb.\n");
- return -ENOMEM;
- }
-
- if (mwifiex_map_pci_memory(adapter, skb_tmp,
- MWIFIEX_RX_DATA_BUF_SIZE,
- PCI_DMA_FROMDEVICE))
- return -1;
-
- buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp);
-
- mwifiex_dbg(adapter, INFO,
- "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n",
- skb_tmp, rd_index);
- card->rx_buf_list[rd_index] = skb_tmp;
-
- if (reg->pfu_enabled) {
- desc2 = card->rxbd_ring[rd_index];
- desc2->paddr = buf_pa;
- desc2->len = skb_tmp->len;
- desc2->frag_len = skb_tmp->len;
- desc2->offset = 0;
- desc2->flags = reg->ring_flag_sop | reg->ring_flag_eop;
- } else {
- desc = card->rxbd_ring[rd_index];
- desc->paddr = buf_pa;
- desc->len = skb_tmp->len;
- desc->flags = 0;
- }
-
- if ((++card->rxbd_rdptr & reg->rx_mask) ==
- MWIFIEX_MAX_TXRX_BD) {
- card->rxbd_rdptr = ((card->rxbd_rdptr &
- reg->rx_rollover_ind) ^
- reg->rx_rollover_ind);
- }
- mwifiex_dbg(adapter, DATA,
- "info: RECV DATA: <Rd: %#x, Wr: %#x>\n",
- card->rxbd_rdptr, wrptr);
-
- tx_val = card->txbd_wrptr & reg->tx_wrap_mask;
- /* Write the RX ring read pointer in to reg->rx_rdptr */
- if (mwifiex_write_reg(adapter, reg->rx_rdptr,
- card->rxbd_rdptr | tx_val)) {
- mwifiex_dbg(adapter, DATA,
- "RECV DATA: failed to write reg->rx_rdptr\n");
- ret = -1;
- goto done;
- }
-
- /* Read the RX ring Write pointer set by firmware */
- if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) {
- mwifiex_dbg(adapter, ERROR,
- "RECV DATA: failed to read reg->rx_wrptr\n");
- ret = -1;
- goto done;
- }
- mwifiex_dbg(adapter, DATA,
- "info: RECV DATA: Rcvd packet from fw successfully\n");
- card->rxbd_wrptr = wrptr;
- }
-
-done:
- return ret;
-}
-
-/*
- * This function downloads the boot command to device
- */
-static int
-mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
-{
- dma_addr_t buf_pa;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- if (!(skb->data && skb->len)) {
- mwifiex_dbg(adapter, ERROR,
- "Invalid parameter in %s <%p. len %d>\n",
- __func__, skb->data, skb->len);
- return -1;
- }
-
- if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE))
- return -1;
-
- buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
-
- /* Write the lower 32bits of the physical address to low command
- * address scratch register
- */
- if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa)) {
- mwifiex_dbg(adapter, ERROR,
- "%s: failed to write download command to boot code.\n",
- __func__);
- mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
- return -1;
- }
-
- /* Write the upper 32bits of the physical address to high command
- * address scratch register
- */
- if (mwifiex_write_reg(adapter, reg->cmd_addr_hi,
- (u32)((u64)buf_pa >> 32))) {
- mwifiex_dbg(adapter, ERROR,
- "%s: failed to write download command to boot code.\n",
- __func__);
- mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
- return -1;
- }
-
- /* Write the command length to cmd_size scratch register */
- if (mwifiex_write_reg(adapter, reg->cmd_size, skb->len)) {
- mwifiex_dbg(adapter, ERROR,
- "%s: failed to write command len to cmd_size scratch reg\n",
- __func__);
- mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
- return -1;
- }
-
- /* Ring the door bell */
- if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
- CPU_INTR_DOOR_BELL)) {
- mwifiex_dbg(adapter, ERROR,
- "%s: failed to assert door-bell intr\n", __func__);
- mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
- return -1;
- }
-
- return 0;
-}
-
-/* This function init rx port in firmware which in turn enables to receive data
- * from device before transmitting any packet.
- */
-static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask;
-
- /* Write the RX ring read pointer in to reg->rx_rdptr */
- if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr |
- tx_wrap)) {
- mwifiex_dbg(adapter, ERROR,
- "RECV DATA: failed to write reg->rx_rdptr\n");
- return -1;
- }
- return 0;
-}
-
-/* This function downloads commands to the device
- */
-static int
-mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- int ret = 0;
- dma_addr_t cmd_buf_pa, cmdrsp_buf_pa;
- u8 *payload = (u8 *)skb->data;
-
- if (!(skb->data && skb->len)) {
- mwifiex_dbg(adapter, ERROR,
- "Invalid parameter in %s <%p, %#x>\n",
- __func__, skb->data, skb->len);
- return -1;
- }
-
- /* Make sure a command response buffer is available */
- if (!card->cmdrsp_buf) {
- mwifiex_dbg(adapter, ERROR,
- "No response buffer available, send command failed\n");
- return -EBUSY;
- }
-
- if (!mwifiex_pcie_ok_to_access_hw(adapter))
- mwifiex_pm_wakeup_card(adapter);
-
- adapter->cmd_sent = true;
-
- *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len);
- *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD);
-
- if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE))
- return -1;
-
- card->cmd_buf = skb;
-
- /* To send a command, the driver will:
- 1. Write the 64bit physical address of the data buffer to
- cmd response address low + cmd response address high
- 2. Ring the door bell (i.e. set the door bell interrupt)
-
- In response to door bell interrupt, the firmware will perform
- the DMA of the command packet (first header to obtain the total
- length and then rest of the command).
- */
-
- if (card->cmdrsp_buf) {
- cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf);
- /* Write the lower 32bits of the cmdrsp buffer physical
- address */
- if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo,
- (u32)cmdrsp_buf_pa)) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to write download cmd to boot code.\n");
- ret = -1;
- goto done;
- }
- /* Write the upper 32bits of the cmdrsp buffer physical
- address */
- if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi,
- (u32)((u64)cmdrsp_buf_pa >> 32))) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to write download cmd to boot code.\n");
- ret = -1;
- goto done;
- }
- }
-
- cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf);
- /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */
- if (mwifiex_write_reg(adapter, reg->cmd_addr_lo,
- (u32)cmd_buf_pa)) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to write download cmd to boot code.\n");
- ret = -1;
- goto done;
- }
- /* Write the upper 32bits of the physical address to reg->cmd_addr_hi */
- if (mwifiex_write_reg(adapter, reg->cmd_addr_hi,
- (u32)((u64)cmd_buf_pa >> 32))) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to write download cmd to boot code.\n");
- ret = -1;
- goto done;
- }
-
- /* Write the command length to reg->cmd_size */
- if (mwifiex_write_reg(adapter, reg->cmd_size,
- card->cmd_buf->len)) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to write cmd len to reg->cmd_size\n");
- ret = -1;
- goto done;
- }
-
- /* Ring the door bell */
- if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
- CPU_INTR_DOOR_BELL)) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to assert door-bell intr\n");
- ret = -1;
- goto done;
- }
-
-done:
- if (ret)
- adapter->cmd_sent = false;
-
- return 0;
-}
-
-/*
- * This function handles command complete interrupt
- */
-static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- struct sk_buff *skb = card->cmdrsp_buf;
- int count = 0;
- u16 rx_len;
- __le16 pkt_len;
-
- mwifiex_dbg(adapter, CMD,
- "info: Rx CMD Response\n");
-
- mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE);
-
- /* Unmap the command as a response has been received. */
- if (card->cmd_buf) {
- mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
- PCI_DMA_TODEVICE);
- card->cmd_buf = NULL;
- }
-
- pkt_len = *((__le16 *)skb->data);
- rx_len = le16_to_cpu(pkt_len);
- skb_trim(skb, rx_len);
- skb_pull(skb, INTF_HEADER_LEN);
-
- if (!adapter->curr_cmd) {
- if (adapter->ps_state == PS_STATE_SLEEP_CFM) {
- mwifiex_process_sleep_confirm_resp(adapter, skb->data,
- skb->len);
- mwifiex_pcie_enable_host_int(adapter);
- if (mwifiex_write_reg(adapter,
- PCIE_CPU_INT_EVENT,
- CPU_INTR_SLEEP_CFM_DONE)) {
- mwifiex_dbg(adapter, ERROR,
- "Write register failed\n");
- return -1;
- }
- mwifiex_delay_for_sleep_cookie(adapter,
- MWIFIEX_MAX_DELAY_COUNT);
- while (reg->sleep_cookie && (count++ < 10) &&
- mwifiex_pcie_ok_to_access_hw(adapter))
- usleep_range(50, 60);
- } else {
- mwifiex_dbg(adapter, ERROR,
- "There is no command but got cmdrsp\n");
- }
- memcpy(adapter->upld_buf, skb->data,
- min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len));
- skb_push(skb, INTF_HEADER_LEN);
- if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
- PCI_DMA_FROMDEVICE))
- return -1;
- } else if (mwifiex_pcie_ok_to_access_hw(adapter)) {
- adapter->curr_cmd->resp_skb = skb;
- adapter->cmd_resp_received = true;
- /* Take the pointer and set it to CMD node and will
- return in the response complete callback */
- card->cmdrsp_buf = NULL;
-
- /* Clear the cmd-rsp buffer address in scratch registers. This
- will prevent firmware from writing to the same response
- buffer again. */
- if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0)) {
- mwifiex_dbg(adapter, ERROR,
- "cmd_done: failed to clear cmd_rsp_addr_lo\n");
- return -1;
- }
- /* Write the upper 32bits of the cmdrsp buffer physical
- address */
- if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0)) {
- mwifiex_dbg(adapter, ERROR,
- "cmd_done: failed to clear cmd_rsp_addr_hi\n");
- return -1;
- }
- }
-
- return 0;
-}
-
-/*
- * Command Response processing complete handler
- */
-static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
- struct sk_buff *skb)
-{
- struct pcie_service_card *card = adapter->card;
-
- if (skb) {
- card->cmdrsp_buf = skb;
- skb_push(card->cmdrsp_buf, INTF_HEADER_LEN);
- if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
- PCI_DMA_FROMDEVICE))
- return -1;
- }
-
- return 0;
-}
-
-/*
- * This function handles firmware event ready interrupt
- */
-static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
- u32 wrptr, event;
- struct mwifiex_evt_buf_desc *desc;
-
- if (!mwifiex_pcie_ok_to_access_hw(adapter))
- mwifiex_pm_wakeup_card(adapter);
-
- if (adapter->event_received) {
- mwifiex_dbg(adapter, EVENT,
- "info: Event being processed,\t"
- "do not process this interrupt just yet\n");
- return 0;
- }
-
- if (rdptr >= MWIFIEX_MAX_EVT_BD) {
- mwifiex_dbg(adapter, ERROR,
- "info: Invalid read pointer...\n");
- return -1;
- }
-
- /* Read the event ring write pointer set by firmware */
- if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) {
- mwifiex_dbg(adapter, ERROR,
- "EventReady: failed to read reg->evt_wrptr\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, EVENT,
- "info: EventReady: Initial <Rd: 0x%x, Wr: 0x%x>",
- card->evtbd_rdptr, wrptr);
- if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr
- & MWIFIEX_EVTBD_MASK)) ||
- ((wrptr & reg->evt_rollover_ind) ==
- (card->evtbd_rdptr & reg->evt_rollover_ind))) {
- struct sk_buff *skb_cmd;
- __le16 data_len = 0;
- u16 evt_len;
-
- mwifiex_dbg(adapter, INFO,
- "info: Read Index: %d\n", rdptr);
- skb_cmd = card->evt_buf_list[rdptr];
- mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE);
-
- /* Take the pointer and set it to event pointer in adapter
- and will return back after event handling callback */
- card->evt_buf_list[rdptr] = NULL;
- desc = card->evtbd_ring[rdptr];
- memset(desc, 0, sizeof(*desc));
-
- event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN];
- adapter->event_cause = event;
- /* The first 4bytes will be the event transfer header
- len is 2 bytes followed by type which is 2 bytes */
- memcpy(&data_len, skb_cmd->data, sizeof(__le16));
- evt_len = le16_to_cpu(data_len);
- skb_trim(skb_cmd, evt_len);
- skb_pull(skb_cmd, INTF_HEADER_LEN);
- mwifiex_dbg(adapter, EVENT,
- "info: Event length: %d\n", evt_len);
-
- if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE))
- memcpy(adapter->event_body, skb_cmd->data +
- MWIFIEX_EVENT_HEADER_LEN, evt_len -
- MWIFIEX_EVENT_HEADER_LEN);
-
- adapter->event_received = true;
- adapter->event_skb = skb_cmd;
-
- /* Do not update the event read pointer here, wait till the
- buffer is released. This is just to make things simpler,
- we need to find a better method of managing these buffers.
- */
- } else {
- if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
- CPU_INTR_EVENT_DONE)) {
- mwifiex_dbg(adapter, ERROR,
- "Write register failed\n");
- return -1;
- }
- }
-
- return 0;
-}
-
-/*
- * Event processing complete handler
- */
-static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
- struct sk_buff *skb)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- int ret = 0;
- u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
- u32 wrptr;
- struct mwifiex_evt_buf_desc *desc;
-
- if (!skb)
- return 0;
-
- if (rdptr >= MWIFIEX_MAX_EVT_BD) {
- mwifiex_dbg(adapter, ERROR,
- "event_complete: Invalid rdptr 0x%x\n",
- rdptr);
- return -EINVAL;
- }
-
- /* Read the event ring write pointer set by firmware */
- if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) {
- mwifiex_dbg(adapter, ERROR,
- "event_complete: failed to read reg->evt_wrptr\n");
- return -1;
- }
-
- if (!card->evt_buf_list[rdptr]) {
- skb_push(skb, INTF_HEADER_LEN);
- skb_put(skb, MAX_EVENT_SIZE - skb->len);
- if (mwifiex_map_pci_memory(adapter, skb,
- MAX_EVENT_SIZE,
- PCI_DMA_FROMDEVICE))
- return -1;
- card->evt_buf_list[rdptr] = skb;
- desc = card->evtbd_ring[rdptr];
- desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb);
- desc->len = (u16)skb->len;
- desc->flags = 0;
- skb = NULL;
- } else {
- mwifiex_dbg(adapter, ERROR,
- "info: ERROR: buf still valid at index %d, <%p, %p>\n",
- rdptr, card->evt_buf_list[rdptr], skb);
- }
-
- if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) {
- card->evtbd_rdptr = ((card->evtbd_rdptr &
- reg->evt_rollover_ind) ^
- reg->evt_rollover_ind);
- }
-
- mwifiex_dbg(adapter, EVENT,
- "info: Updated <Rd: 0x%x, Wr: 0x%x>",
- card->evtbd_rdptr, wrptr);
-
- /* Write the event ring read pointer in to reg->evt_rdptr */
- if (mwifiex_write_reg(adapter, reg->evt_rdptr,
- card->evtbd_rdptr)) {
- mwifiex_dbg(adapter, ERROR,
- "event_complete: failed to read reg->evt_rdptr\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, EVENT,
- "info: Check Events Again\n");
- ret = mwifiex_pcie_process_event_ready(adapter);
-
- return ret;
-}
-
-/*
- * This function downloads the firmware to the card.
- *
- * Firmware is downloaded to the card in blocks. Every block download
- * is tested for CRC errors, and retried a number of times before
- * returning failure.
- */
-static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
- struct mwifiex_fw_image *fw)
-{
- int ret;
- u8 *firmware = fw->fw_buf;
- u32 firmware_len = fw->fw_len;
- u32 offset = 0;
- struct sk_buff *skb;
- u32 txlen, tx_blocks = 0, tries, len;
- u32 block_retry_cnt = 0;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- if (!firmware || !firmware_len) {
- mwifiex_dbg(adapter, ERROR,
- "No firmware image found! Terminating download\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, INFO,
- "info: Downloading FW image (%d bytes)\n",
- firmware_len);
-
- if (mwifiex_pcie_disable_host_int(adapter)) {
- mwifiex_dbg(adapter, ERROR,
- "%s: Disabling interrupts failed.\n", __func__);
- return -1;
- }
-
- skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE);
- if (!skb) {
- ret = -ENOMEM;
- goto done;
- }
-
- /* Perform firmware data transfer */
- do {
- u32 ireg_intr = 0;
-
- /* More data? */
- if (offset >= firmware_len)
- break;
-
- for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- ret = mwifiex_read_reg(adapter, reg->cmd_size,
- &len);
- if (ret) {
- mwifiex_dbg(adapter, FATAL,
- "Failed reading len from boot code\n");
- goto done;
- }
- if (len)
- break;
- usleep_range(10, 20);
- }
-
- if (!len) {
- break;
- } else if (len > MWIFIEX_UPLD_SIZE) {
- mwifiex_dbg(adapter, ERROR,
- "FW download failure @ %d, invalid length %d\n",
- offset, len);
- ret = -1;
- goto done;
- }
-
- txlen = len;
-
- if (len & BIT(0)) {
- block_retry_cnt++;
- if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) {
- mwifiex_dbg(adapter, ERROR,
- "FW download failure @ %d, over max\t"
- "retry count\n", offset);
- ret = -1;
- goto done;
- }
- mwifiex_dbg(adapter, ERROR,
- "FW CRC error indicated by the\t"
- "helper: len = 0x%04X, txlen = %d\n",
- len, txlen);
- len &= ~BIT(0);
- /* Setting this to 0 to resend from same offset */
- txlen = 0;
- } else {
- block_retry_cnt = 0;
- /* Set blocksize to transfer - checking for
- last block */
- if (firmware_len - offset < txlen)
- txlen = firmware_len - offset;
-
- mwifiex_dbg(adapter, INFO, ".");
-
- tx_blocks = (txlen + card->pcie.blksz_fw_dl - 1) /
- card->pcie.blksz_fw_dl;
-
- /* Copy payload to buffer */
- memmove(skb->data, &firmware[offset], txlen);
- }
-
- skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len);
- skb_trim(skb, tx_blocks * card->pcie.blksz_fw_dl);
-
- /* Send the boot command to device */
- if (mwifiex_pcie_send_boot_cmd(adapter, skb)) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to send firmware download command\n");
- ret = -1;
- goto done;
- }
-
- /* Wait for the command done interrupt */
- do {
- if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS,
- &ireg_intr)) {
- mwifiex_dbg(adapter, ERROR,
- "%s: Failed to read\t"
- "interrupt status during fw dnld.\n",
- __func__);
- mwifiex_unmap_pci_memory(adapter, skb,
- PCI_DMA_TODEVICE);
- ret = -1;
- goto done;
- }
- } while ((ireg_intr & CPU_INTR_DOOR_BELL) ==
- CPU_INTR_DOOR_BELL);
-
- mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
-
- offset += txlen;
- } while (true);
-
- mwifiex_dbg(adapter, MSG,
- "info: FW download over, size %d bytes\n", offset);
-
- ret = 0;
-
-done:
- dev_kfree_skb_any(skb);
- return ret;
-}
-
-/*
- * This function checks the firmware status in card.
- *
- * The winner interface is also determined by this function.
- */
-static int
-mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num)
-{
- int ret = 0;
- u32 firmware_stat, winner_status;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- u32 tries;
-
- /* Mask spurios interrupts */
- if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK,
- HOST_INTR_MASK)) {
- mwifiex_dbg(adapter, ERROR,
- "Write register failed\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, INFO,
- "Setting driver ready signature\n");
- if (mwifiex_write_reg(adapter, reg->drv_rdy,
- FIRMWARE_READY_PCIE)) {
- mwifiex_dbg(adapter, ERROR,
- "Failed to write driver ready signature\n");
- return -1;
- }
-
- /* Wait for firmware initialization event */
- for (tries = 0; tries < poll_num; tries++) {
- if (mwifiex_read_reg(adapter, reg->fw_status,
- &firmware_stat))
- ret = -1;
- else
- ret = 0;
- if (ret)
- continue;
- if (firmware_stat == FIRMWARE_READY_PCIE) {
- ret = 0;
- break;
- } else {
- msleep(100);
- ret = -1;
- }
- }
-
- if (ret) {
- if (mwifiex_read_reg(adapter, reg->fw_status,
- &winner_status))
- ret = -1;
- else if (!winner_status) {
- mwifiex_dbg(adapter, INFO,
- "PCI-E is the winner\n");
- adapter->winner = 1;
- } else {
- mwifiex_dbg(adapter, ERROR,
- "PCI-E is not the winner <%#x,%d>, exit dnld\n",
- ret, adapter->winner);
- }
- }
-
- return ret;
-}
-
-/*
- * This function reads the interrupt status from card.
- */
-static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
-{
- u32 pcie_ireg;
- unsigned long flags;
-
- if (!mwifiex_pcie_ok_to_access_hw(adapter))
- return;
-
- if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) {
- mwifiex_dbg(adapter, ERROR, "Read register failed\n");
- return;
- }
-
- if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) {
-
- mwifiex_pcie_disable_host_int(adapter);
-
- /* Clear the pending interrupts */
- if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS,
- ~pcie_ireg)) {
- mwifiex_dbg(adapter, ERROR,
- "Write register failed\n");
- return;
- }
- spin_lock_irqsave(&adapter->int_lock, flags);
- adapter->int_status |= pcie_ireg;
- spin_unlock_irqrestore(&adapter->int_lock, flags);
-
- if (!adapter->pps_uapsd_mode &&
- adapter->ps_state == PS_STATE_SLEEP &&
- mwifiex_pcie_ok_to_access_hw(adapter)) {
- /* Potentially for PCIe we could get other
- * interrupts like shared. Don't change power
- * state until cookie is set */
- adapter->ps_state = PS_STATE_AWAKE;
- adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
- }
- }
-}
-
-/*
- * Interrupt handler for PCIe root port
- *
- * This function reads the interrupt status from firmware and assigns
- * the main process in workqueue which will handle the interrupt.
- */
-static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context)
-{
- struct pci_dev *pdev = (struct pci_dev *)context;
- struct pcie_service_card *card;
- struct mwifiex_adapter *adapter;
-
- if (!pdev) {
- pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev);
- goto exit;
- }
-
- card = pci_get_drvdata(pdev);
- if (!card || !card->adapter) {
- pr_debug("info: %s: card=%p adapter=%p\n", __func__, card,
- card ? card->adapter : NULL);
- goto exit;
- }
- adapter = card->adapter;
-
- if (adapter->surprise_removed)
- goto exit;
-
- mwifiex_interrupt_status(adapter);
- mwifiex_queue_main_work(adapter);
-
-exit:
- return IRQ_HANDLED;
-}
-
-/*
- * This function checks the current interrupt status.
- *
- * The following interrupts are checked and handled by this function -
- * - Data sent
- * - Command sent
- * - Command received
- * - Packets received
- * - Events received
- *
- * In case of Rx packets received, the packets are uploaded from card to
- * host and processed accordingly.
- */
-static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
-{
- int ret;
- u32 pcie_ireg;
- unsigned long flags;
-
- spin_lock_irqsave(&adapter->int_lock, flags);
- /* Clear out unused interrupts */
- pcie_ireg = adapter->int_status;
- adapter->int_status = 0;
- spin_unlock_irqrestore(&adapter->int_lock, flags);
-
- while (pcie_ireg & HOST_INTR_MASK) {
- if (pcie_ireg & HOST_INTR_DNLD_DONE) {
- pcie_ireg &= ~HOST_INTR_DNLD_DONE;
- mwifiex_dbg(adapter, INTR,
- "info: TX DNLD Done\n");
- ret = mwifiex_pcie_send_data_complete(adapter);
- if (ret)
- return ret;
- }
- if (pcie_ireg & HOST_INTR_UPLD_RDY) {
- pcie_ireg &= ~HOST_INTR_UPLD_RDY;
- mwifiex_dbg(adapter, INTR,
- "info: Rx DATA\n");
- ret = mwifiex_pcie_process_recv_data(adapter);
- if (ret)
- return ret;
- }
- if (pcie_ireg & HOST_INTR_EVENT_RDY) {
- pcie_ireg &= ~HOST_INTR_EVENT_RDY;
- mwifiex_dbg(adapter, INTR,
- "info: Rx EVENT\n");
- ret = mwifiex_pcie_process_event_ready(adapter);
- if (ret)
- return ret;
- }
-
- if (pcie_ireg & HOST_INTR_CMD_DONE) {
- pcie_ireg &= ~HOST_INTR_CMD_DONE;
- if (adapter->cmd_sent) {
- mwifiex_dbg(adapter, INTR,
- "info: CMD sent Interrupt\n");
- adapter->cmd_sent = false;
- }
- /* Handle command response */
- ret = mwifiex_pcie_process_cmd_complete(adapter);
- if (ret)
- return ret;
- }
-
- if (mwifiex_pcie_ok_to_access_hw(adapter)) {
- if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS,
- &pcie_ireg)) {
- mwifiex_dbg(adapter, ERROR,
- "Read register failed\n");
- return -1;
- }
-
- if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) {
- if (mwifiex_write_reg(adapter,
- PCIE_HOST_INT_STATUS,
- ~pcie_ireg)) {
- mwifiex_dbg(adapter, ERROR,
- "Write register failed\n");
- return -1;
- }
- }
-
- }
- }
- mwifiex_dbg(adapter, INTR,
- "info: cmd_sent=%d data_sent=%d\n",
- adapter->cmd_sent, adapter->data_sent);
- if (adapter->ps_state != PS_STATE_SLEEP)
- mwifiex_pcie_enable_host_int(adapter);
-
- return 0;
-}
-
-/*
- * This function downloads data from driver to card.
- *
- * Both commands and data packets are transferred to the card by this
- * function.
- *
- * This function adds the PCIE specific header to the front of the buffer
- * before transferring. The header contains the length of the packet and
- * the type. The firmware handles the packets based upon this set type.
- */
-static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type,
- struct sk_buff *skb,
- struct mwifiex_tx_param *tx_param)
-{
- if (!skb) {
- mwifiex_dbg(adapter, ERROR,
- "Passed NULL skb to %s\n", __func__);
- return -1;
- }
-
- if (type == MWIFIEX_TYPE_DATA)
- return mwifiex_pcie_send_data(adapter, skb, tx_param);
- else if (type == MWIFIEX_TYPE_CMD)
- return mwifiex_pcie_send_cmd(adapter, skb);
-
- return 0;
-}
-
-/* This function read/write firmware */
-static enum rdwr_status
-mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag)
-{
- int ret, tries;
- u8 ctrl_data;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "PCIE write err\n");
- return RDWR_STATUS_FAILURE;
- }
-
- for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data);
- if (ctrl_data == FW_DUMP_DONE)
- return RDWR_STATUS_SUCCESS;
- if (doneflag && ctrl_data == doneflag)
- return RDWR_STATUS_DONE;
- if (ctrl_data != FW_DUMP_HOST_READY) {
- mwifiex_dbg(adapter, WARN,
- "The ctrl reg was changed, re-try again!\n");
- ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl,
- FW_DUMP_HOST_READY);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "PCIE write err\n");
- return RDWR_STATUS_FAILURE;
- }
- }
- usleep_range(100, 200);
- }
-
- mwifiex_dbg(adapter, ERROR, "Fail to pull ctrl_data\n");
- return RDWR_STATUS_FAILURE;
-}
-
-/* This function dump firmware memory to file */
-static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *creg = card->pcie.reg;
- unsigned int reg, reg_start, reg_end;
- u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
- enum rdwr_status stat;
- u32 memory_size;
- int ret;
-
- if (!card->pcie.can_dump_fw)
- return;
-
- for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
- struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
-
- if (entry->mem_ptr) {
- vfree(entry->mem_ptr);
- entry->mem_ptr = NULL;
- }
- entry->mem_size = 0;
- }
-
- mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump start ==\n");
-
- /* Read the number of the memories which will dump */
- stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
- if (stat == RDWR_STATUS_FAILURE)
- return;
-
- reg = creg->fw_dump_start;
- mwifiex_read_reg_byte(adapter, reg, &dump_num);
-
- /* Read the length of every memory which will dump */
- for (idx = 0; idx < dump_num; idx++) {
- struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
-
- stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
- if (stat == RDWR_STATUS_FAILURE)
- return;
-
- memory_size = 0;
- reg = creg->fw_dump_start;
- for (i = 0; i < 4; i++) {
- mwifiex_read_reg_byte(adapter, reg, &read_reg);
- memory_size |= (read_reg << (i * 8));
- reg++;
- }
-
- if (memory_size == 0) {
- mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n");
- ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl,
- FW_DUMP_READ_DONE);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "PCIE write err\n");
- return;
- }
- break;
- }
-
- mwifiex_dbg(adapter, DUMP,
- "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
- entry->mem_ptr = vmalloc(memory_size + 1);
- entry->mem_size = memory_size;
- if (!entry->mem_ptr) {
- mwifiex_dbg(adapter, ERROR,
- "Vmalloc %s failed\n", entry->mem_name);
- return;
- }
- dbg_ptr = entry->mem_ptr;
- end_ptr = dbg_ptr + memory_size;
-
- doneflag = entry->done_flag;
- mwifiex_dbg(adapter, DUMP, "Start %s output, please wait...\n",
- entry->mem_name);
-
- do {
- stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
- if (RDWR_STATUS_FAILURE == stat)
- return;
-
- reg_start = creg->fw_dump_start;
- reg_end = creg->fw_dump_end;
- for (reg = reg_start; reg <= reg_end; reg++) {
- mwifiex_read_reg_byte(adapter, reg, dbg_ptr);
- if (dbg_ptr < end_ptr) {
- dbg_ptr++;
- } else {
- mwifiex_dbg(adapter, ERROR,
- "Allocated buf not enough\n");
- return;
- }
- }
-
- if (stat != RDWR_STATUS_DONE)
- continue;
-
- mwifiex_dbg(adapter, DUMP,
- "%s done: size=0x%tx\n",
- entry->mem_name, dbg_ptr - entry->mem_ptr);
- break;
- } while (true);
- }
- mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump end ==\n");
-}
-
-static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter)
-{
- mwifiex_drv_info_dump(adapter);
- mwifiex_pcie_fw_dump(adapter);
- mwifiex_upload_device_dump(adapter);
-}
-
-static unsigned long iface_work_flags;
-static struct mwifiex_adapter *save_adapter;
-static void mwifiex_pcie_work(struct work_struct *work)
-{
- if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP,
- &iface_work_flags))
- mwifiex_pcie_device_dump_work(save_adapter);
-}
-
-static DECLARE_WORK(pcie_work, mwifiex_pcie_work);
-/* This function dumps FW information */
-static void mwifiex_pcie_device_dump(struct mwifiex_adapter *adapter)
-{
- save_adapter = adapter;
- if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags))
- return;
-
- set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags);
-
- schedule_work(&pcie_work);
-}
-
-/*
- * This function initializes the PCI-E host memory space, WCB rings, etc.
- *
- * The following initializations steps are followed -
- * - Allocate TXBD ring buffers
- * - Allocate RXBD ring buffers
- * - Allocate event BD ring buffers
- * - Allocate command response ring buffer
- * - Allocate sleep cookie buffer
- */
-static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- int ret;
- struct pci_dev *pdev = card->dev;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- pci_set_drvdata(pdev, card);
-
- ret = pci_enable_device(pdev);
- if (ret)
- goto err_enable_dev;
-
- pci_set_master(pdev);
-
- mwifiex_dbg(adapter, INFO,
- "try set_consistent_dma_mask(32)\n");
- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "set_dma_mask(32) failed\n");
- goto err_set_dma_mask;
- }
-
- ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "set_consistent_dma_mask(64) failed\n");
- goto err_set_dma_mask;
- }
-
- ret = pci_request_region(pdev, 0, DRV_NAME);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "req_reg(0) error\n");
- goto err_req_region0;
- }
- card->pci_mmap = pci_iomap(pdev, 0, 0);
- if (!card->pci_mmap) {
- mwifiex_dbg(adapter, ERROR, "iomap(0) error\n");
- ret = -EIO;
- goto err_iomap0;
- }
- ret = pci_request_region(pdev, 2, DRV_NAME);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n");
- goto err_req_region2;
- }
- card->pci_mmap1 = pci_iomap(pdev, 2, 0);
- if (!card->pci_mmap1) {
- mwifiex_dbg(adapter, ERROR,
- "iomap(2) error\n");
- ret = -EIO;
- goto err_iomap2;
- }
-
- mwifiex_dbg(adapter, INFO,
- "PCI memory map Virt0: %p PCI memory map Virt2: %p\n",
- card->pci_mmap, card->pci_mmap1);
-
- card->cmdrsp_buf = NULL;
- ret = mwifiex_pcie_create_txbd_ring(adapter);
- if (ret)
- goto err_cre_txbd;
- ret = mwifiex_pcie_create_rxbd_ring(adapter);
- if (ret)
- goto err_cre_rxbd;
- ret = mwifiex_pcie_create_evtbd_ring(adapter);
- if (ret)
- goto err_cre_evtbd;
- ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter);
- if (ret)
- goto err_alloc_cmdbuf;
- if (reg->sleep_cookie) {
- ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter);
- if (ret)
- goto err_alloc_cookie;
- } else {
- card->sleep_cookie_vbase = NULL;
- }
- return ret;
-
-err_alloc_cookie:
- mwifiex_pcie_delete_cmdrsp_buf(adapter);
-err_alloc_cmdbuf:
- mwifiex_pcie_delete_evtbd_ring(adapter);
-err_cre_evtbd:
- mwifiex_pcie_delete_rxbd_ring(adapter);
-err_cre_rxbd:
- mwifiex_pcie_delete_txbd_ring(adapter);
-err_cre_txbd:
- pci_iounmap(pdev, card->pci_mmap1);
-err_iomap2:
- pci_release_region(pdev, 2);
-err_req_region2:
- pci_iounmap(pdev, card->pci_mmap);
-err_iomap0:
- pci_release_region(pdev, 0);
-err_req_region0:
-err_set_dma_mask:
- pci_disable_device(pdev);
-err_enable_dev:
- pci_set_drvdata(pdev, NULL);
- return ret;
-}
-
-/*
- * This function cleans up the allocated card buffers.
- *
- * The following are freed by this function -
- * - TXBD ring buffers
- * - RXBD ring buffers
- * - Event BD ring buffers
- * - Command response ring buffer
- * - Sleep cookie buffer
- */
-static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- struct pci_dev *pdev = card->dev;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- if (user_rmmod) {
- mwifiex_dbg(adapter, INFO,
- "Clearing driver ready signature\n");
- if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000))
- mwifiex_dbg(adapter, ERROR,
- "Failed to write driver not-ready signature\n");
- }
-
- if (pdev) {
- pci_iounmap(pdev, card->pci_mmap);
- pci_iounmap(pdev, card->pci_mmap1);
- pci_disable_device(pdev);
- pci_release_region(pdev, 2);
- pci_release_region(pdev, 0);
- pci_set_drvdata(pdev, NULL);
- }
- kfree(card);
-}
-
-/*
- * This function registers the PCIE device.
- *
- * PCIE IRQ is claimed, block size is set and driver data is initialized.
- */
-static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
-{
- int ret;
- struct pcie_service_card *card = adapter->card;
- struct pci_dev *pdev = card->dev;
-
- /* save adapter pointer in card */
- card->adapter = adapter;
-
- ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED,
- "MRVL_PCIE", pdev);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "request_irq failed: ret=%d\n", ret);
- adapter->card = NULL;
- return -1;
- }
-
- adapter->dev = &pdev->dev;
- adapter->tx_buf_size = card->pcie.tx_buf_size;
- adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
- adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
- strcpy(adapter->fw_name, card->pcie.firmware);
- adapter->ext_scan = card->pcie.can_ext_scan;
-
- return 0;
-}
-
-/*
- * This function unregisters the PCIE device.
- *
- * The PCIE IRQ is released, the function is disabled and driver
- * data is set to null.
- */
-static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
-{
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg;
-
- if (card) {
- mwifiex_dbg(adapter, INFO,
- "%s(): calling free_irq()\n", __func__);
- free_irq(card->dev->irq, card->dev);
-
- reg = card->pcie.reg;
- if (reg->sleep_cookie)
- mwifiex_pcie_delete_sleep_cookie_buf(adapter);
-
- mwifiex_pcie_delete_cmdrsp_buf(adapter);
- mwifiex_pcie_delete_evtbd_ring(adapter);
- mwifiex_pcie_delete_rxbd_ring(adapter);
- mwifiex_pcie_delete_txbd_ring(adapter);
- card->cmdrsp_buf = NULL;
- }
-}
-
-static struct mwifiex_if_ops pcie_ops = {
- .init_if = mwifiex_pcie_init,
- .cleanup_if = mwifiex_pcie_cleanup,
- .check_fw_status = mwifiex_check_fw_status,
- .prog_fw = mwifiex_prog_fw_w_helper,
- .register_dev = mwifiex_register_dev,
- .unregister_dev = mwifiex_unregister_dev,
- .enable_int = mwifiex_pcie_enable_host_int,
- .process_int_status = mwifiex_process_int_status,
- .host_to_card = mwifiex_pcie_host_to_card,
- .wakeup = mwifiex_pm_wakeup_card,
- .wakeup_complete = mwifiex_pm_wakeup_card_complete,
-
- /* PCIE specific */
- .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete,
- .event_complete = mwifiex_pcie_event_complete,
- .update_mp_end_port = NULL,
- .cleanup_mpa_buf = NULL,
- .init_fw_port = mwifiex_pcie_init_fw_port,
- .clean_pcie_ring = mwifiex_clean_pcie_ring_buf,
- .device_dump = mwifiex_pcie_device_dump,
-};
-
-/*
- * This function initializes the PCIE driver module.
- *
- * This initiates the semaphore and registers the device with
- * PCIE bus.
- */
-static int mwifiex_pcie_init_module(void)
-{
- int ret;
-
- pr_debug("Marvell PCIe Driver\n");
-
- sema_init(&add_remove_card_sem, 1);
-
- /* Clear the flag in case user removes the card. */
- user_rmmod = 0;
-
- ret = pci_register_driver(&mwifiex_pcie);
- if (ret)
- pr_err("Driver register failed!\n");
- else
- pr_debug("info: Driver registered successfully!\n");
-
- return ret;
-}
-
-/*
- * This function cleans up the PCIE driver.
- *
- * The following major steps are followed for cleanup -
- * - Resume the device if its suspended
- * - Disconnect the device if connected
- * - Shutdown the firmware
- * - Unregister the device from PCIE bus.
- */
-static void mwifiex_pcie_cleanup_module(void)
-{
- if (!down_interruptible(&add_remove_card_sem))
- up(&add_remove_card_sem);
-
- /* Set the flag as user is removing this module. */
- user_rmmod = 1;
-
- cancel_work_sync(&pcie_work);
- pci_unregister_driver(&mwifiex_pcie);
-}
-
-module_init(mwifiex_pcie_init_module);
-module_exit(mwifiex_pcie_cleanup_module);
-
-MODULE_AUTHOR("Marvell International Ltd.");
-MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION);
-MODULE_VERSION(PCIE_VERSION);
-MODULE_LICENSE("GPL v2");
-MODULE_FIRMWARE(PCIE8766_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(PCIE8897_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(PCIE8997_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h
deleted file mode 100644
index 48e549c3b285..000000000000
--- a/drivers/net/wireless/mwifiex/pcie.h
+++ /dev/null
@@ -1,382 +0,0 @@
-/* @file mwifiex_pcie.h
- *
- * @brief This file contains definitions for PCI-E interface.
- * driver.
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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.
- */
-
-#ifndef _MWIFIEX_PCIE_H
-#define _MWIFIEX_PCIE_H
-
-#include <linux/pci.h>
-#include <linux/pcieport_if.h>
-#include <linux/interrupt.h>
-
-#include "main.h"
-
-#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin"
-#define PCIE8897_DEFAULT_FW_NAME "mrvl/pcie8897_uapsta.bin"
-#define PCIE8997_DEFAULT_FW_NAME "mrvl/pcie8997_uapsta.bin"
-
-#define PCIE_VENDOR_ID_MARVELL (0x11ab)
-#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30)
-#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38)
-#define PCIE_DEVICE_ID_MARVELL_88W8997 (0x2b42)
-
-/* Constants for Buffer Descriptor (BD) rings */
-#define MWIFIEX_MAX_TXRX_BD 0x20
-#define MWIFIEX_TXBD_MASK 0x3F
-#define MWIFIEX_RXBD_MASK 0x3F
-
-#define MWIFIEX_MAX_EVT_BD 0x08
-#define MWIFIEX_EVTBD_MASK 0x0f
-
-/* PCIE INTERNAL REGISTERS */
-#define PCIE_SCRATCH_0_REG 0xC10
-#define PCIE_SCRATCH_1_REG 0xC14
-#define PCIE_CPU_INT_EVENT 0xC18
-#define PCIE_CPU_INT_STATUS 0xC1C
-#define PCIE_HOST_INT_STATUS 0xC30
-#define PCIE_HOST_INT_MASK 0xC34
-#define PCIE_HOST_INT_STATUS_MASK 0xC3C
-#define PCIE_SCRATCH_2_REG 0xC40
-#define PCIE_SCRATCH_3_REG 0xC44
-#define PCIE_SCRATCH_4_REG 0xCD0
-#define PCIE_SCRATCH_5_REG 0xCD4
-#define PCIE_SCRATCH_6_REG 0xCD8
-#define PCIE_SCRATCH_7_REG 0xCDC
-#define PCIE_SCRATCH_8_REG 0xCE0
-#define PCIE_SCRATCH_9_REG 0xCE4
-#define PCIE_SCRATCH_10_REG 0xCE8
-#define PCIE_SCRATCH_11_REG 0xCEC
-#define PCIE_SCRATCH_12_REG 0xCF0
-#define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C
-#define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C
-
-#define CPU_INTR_DNLD_RDY BIT(0)
-#define CPU_INTR_DOOR_BELL BIT(1)
-#define CPU_INTR_SLEEP_CFM_DONE BIT(2)
-#define CPU_INTR_RESET BIT(3)
-#define CPU_INTR_EVENT_DONE BIT(5)
-
-#define HOST_INTR_DNLD_DONE BIT(0)
-#define HOST_INTR_UPLD_RDY BIT(1)
-#define HOST_INTR_CMD_DONE BIT(2)
-#define HOST_INTR_EVENT_RDY BIT(3)
-#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \
- HOST_INTR_UPLD_RDY | \
- HOST_INTR_CMD_DONE | \
- HOST_INTR_EVENT_RDY)
-
-#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7)
-#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0)
-#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1)
-#define MWIFIEX_BD_FLAG_SOP BIT(0)
-#define MWIFIEX_BD_FLAG_EOP BIT(1)
-#define MWIFIEX_BD_FLAG_XS_SOP BIT(2)
-#define MWIFIEX_BD_FLAG_XS_EOP BIT(3)
-#define MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND BIT(7)
-#define MWIFIEX_BD_FLAG_RX_ROLLOVER_IND BIT(10)
-#define MWIFIEX_BD_FLAG_TX_START_PTR BIT(16)
-#define MWIFIEX_BD_FLAG_TX_ROLLOVER_IND BIT(26)
-
-/* Max retry number of command write */
-#define MAX_WRITE_IOMEM_RETRY 2
-/* Define PCIE block size for firmware download */
-#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256
-/* FW awake cookie after FW ready */
-#define FW_AWAKE_COOKIE (0xAA55AA55)
-#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF
-#define MWIFIEX_MAX_DELAY_COUNT 5
-
-struct mwifiex_pcie_card_reg {
- u16 cmd_addr_lo;
- u16 cmd_addr_hi;
- u16 fw_status;
- u16 cmd_size;
- u16 cmdrsp_addr_lo;
- u16 cmdrsp_addr_hi;
- u16 tx_rdptr;
- u16 tx_wrptr;
- u16 rx_rdptr;
- u16 rx_wrptr;
- u16 evt_rdptr;
- u16 evt_wrptr;
- u16 drv_rdy;
- u16 tx_start_ptr;
- u32 tx_mask;
- u32 tx_wrap_mask;
- u32 rx_mask;
- u32 rx_wrap_mask;
- u32 tx_rollover_ind;
- u32 rx_rollover_ind;
- u32 evt_rollover_ind;
- u8 ring_flag_sop;
- u8 ring_flag_eop;
- u8 ring_flag_xs_sop;
- u8 ring_flag_xs_eop;
- u32 ring_tx_start_ptr;
- u8 pfu_enabled;
- u8 sleep_cookie;
- u16 fw_dump_ctrl;
- u16 fw_dump_start;
- u16 fw_dump_end;
-};
-
-static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = {
- .cmd_addr_lo = PCIE_SCRATCH_0_REG,
- .cmd_addr_hi = PCIE_SCRATCH_1_REG,
- .cmd_size = PCIE_SCRATCH_2_REG,
- .fw_status = PCIE_SCRATCH_3_REG,
- .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
- .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
- .tx_rdptr = PCIE_SCRATCH_6_REG,
- .tx_wrptr = PCIE_SCRATCH_7_REG,
- .rx_rdptr = PCIE_SCRATCH_8_REG,
- .rx_wrptr = PCIE_SCRATCH_9_REG,
- .evt_rdptr = PCIE_SCRATCH_10_REG,
- .evt_wrptr = PCIE_SCRATCH_11_REG,
- .drv_rdy = PCIE_SCRATCH_12_REG,
- .tx_start_ptr = 0,
- .tx_mask = MWIFIEX_TXBD_MASK,
- .tx_wrap_mask = 0,
- .rx_mask = MWIFIEX_RXBD_MASK,
- .rx_wrap_mask = 0,
- .tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
- .rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
- .evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
- .ring_flag_sop = 0,
- .ring_flag_eop = 0,
- .ring_flag_xs_sop = 0,
- .ring_flag_xs_eop = 0,
- .ring_tx_start_ptr = 0,
- .pfu_enabled = 0,
- .sleep_cookie = 1,
-};
-
-static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = {
- .cmd_addr_lo = PCIE_SCRATCH_0_REG,
- .cmd_addr_hi = PCIE_SCRATCH_1_REG,
- .cmd_size = PCIE_SCRATCH_2_REG,
- .fw_status = PCIE_SCRATCH_3_REG,
- .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
- .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
- .tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1,
- .tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1,
- .rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1,
- .rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1,
- .evt_rdptr = PCIE_SCRATCH_10_REG,
- .evt_wrptr = PCIE_SCRATCH_11_REG,
- .drv_rdy = PCIE_SCRATCH_12_REG,
- .tx_start_ptr = 16,
- .tx_mask = 0x03FF0000,
- .tx_wrap_mask = 0x07FF0000,
- .rx_mask = 0x000003FF,
- .rx_wrap_mask = 0x000007FF,
- .tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND,
- .rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND,
- .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND,
- .ring_flag_sop = MWIFIEX_BD_FLAG_SOP,
- .ring_flag_eop = MWIFIEX_BD_FLAG_EOP,
- .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP,
- .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP,
- .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR,
- .pfu_enabled = 1,
- .sleep_cookie = 0,
- .fw_dump_ctrl = 0xcf4,
- .fw_dump_start = 0xcf8,
- .fw_dump_end = 0xcff,
-};
-
-static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = {
- .cmd_addr_lo = PCIE_SCRATCH_0_REG,
- .cmd_addr_hi = PCIE_SCRATCH_1_REG,
- .cmd_size = PCIE_SCRATCH_2_REG,
- .fw_status = PCIE_SCRATCH_3_REG,
- .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
- .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
- .tx_rdptr = 0xC1A4,
- .tx_wrptr = 0xC1A8,
- .rx_rdptr = 0xC1A8,
- .rx_wrptr = 0xC1A4,
- .evt_rdptr = PCIE_SCRATCH_10_REG,
- .evt_wrptr = PCIE_SCRATCH_11_REG,
- .drv_rdy = PCIE_SCRATCH_12_REG,
- .tx_start_ptr = 16,
- .tx_mask = 0x0FFF0000,
- .tx_wrap_mask = 0x01FF0000,
- .rx_mask = 0x00000FFF,
- .rx_wrap_mask = 0x000001FF,
- .tx_rollover_ind = BIT(28),
- .rx_rollover_ind = BIT(12),
- .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND,
- .ring_flag_sop = MWIFIEX_BD_FLAG_SOP,
- .ring_flag_eop = MWIFIEX_BD_FLAG_EOP,
- .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP,
- .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP,
- .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR,
- .pfu_enabled = 1,
- .sleep_cookie = 0,
-};
-
-struct mwifiex_pcie_device {
- const char *firmware;
- const struct mwifiex_pcie_card_reg *reg;
- u16 blksz_fw_dl;
- u16 tx_buf_size;
- bool can_dump_fw;
- bool can_ext_scan;
-};
-
-static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
- .firmware = PCIE8766_DEFAULT_FW_NAME,
- .reg = &mwifiex_reg_8766,
- .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
- .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
- .can_dump_fw = false,
- .can_ext_scan = true,
-};
-
-static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
- .firmware = PCIE8897_DEFAULT_FW_NAME,
- .reg = &mwifiex_reg_8897,
- .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
- .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
- .can_dump_fw = true,
- .can_ext_scan = true,
-};
-
-static const struct mwifiex_pcie_device mwifiex_pcie8997 = {
- .firmware = PCIE8997_DEFAULT_FW_NAME,
- .reg = &mwifiex_reg_8997,
- .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
- .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
- .can_dump_fw = false,
- .can_ext_scan = true,
-};
-
-struct mwifiex_evt_buf_desc {
- u64 paddr;
- u16 len;
- u16 flags;
-} __packed;
-
-struct mwifiex_pcie_buf_desc {
- u64 paddr;
- u16 len;
- u16 flags;
-} __packed;
-
-struct mwifiex_pfu_buf_desc {
- u16 flags;
- u16 offset;
- u16 frag_len;
- u16 len;
- u64 paddr;
- u32 reserved;
-} __packed;
-
-struct pcie_service_card {
- struct pci_dev *dev;
- struct mwifiex_adapter *adapter;
- struct mwifiex_pcie_device pcie;
-
- u8 txbd_flush;
- u32 txbd_wrptr;
- u32 txbd_rdptr;
- u32 txbd_ring_size;
- u8 *txbd_ring_vbase;
- dma_addr_t txbd_ring_pbase;
- void *txbd_ring[MWIFIEX_MAX_TXRX_BD];
- struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD];
-
- u32 rxbd_wrptr;
- u32 rxbd_rdptr;
- u32 rxbd_ring_size;
- u8 *rxbd_ring_vbase;
- dma_addr_t rxbd_ring_pbase;
- void *rxbd_ring[MWIFIEX_MAX_TXRX_BD];
- struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD];
-
- u32 evtbd_wrptr;
- u32 evtbd_rdptr;
- u32 evtbd_ring_size;
- u8 *evtbd_ring_vbase;
- dma_addr_t evtbd_ring_pbase;
- void *evtbd_ring[MWIFIEX_MAX_EVT_BD];
- struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD];
-
- struct sk_buff *cmd_buf;
- struct sk_buff *cmdrsp_buf;
- u8 *sleep_cookie_vbase;
- dma_addr_t sleep_cookie_pbase;
- void __iomem *pci_mmap;
- void __iomem *pci_mmap1;
-};
-
-static inline int
-mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr)
-{
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- switch (card->dev->device) {
- case PCIE_DEVICE_ID_MARVELL_88W8766P:
- if (((card->txbd_wrptr & reg->tx_mask) ==
- (rdptr & reg->tx_mask)) &&
- ((card->txbd_wrptr & reg->tx_rollover_ind) !=
- (rdptr & reg->tx_rollover_ind)))
- return 1;
- break;
- case PCIE_DEVICE_ID_MARVELL_88W8897:
- if (((card->txbd_wrptr & reg->tx_mask) ==
- (rdptr & reg->tx_mask)) &&
- ((card->txbd_wrptr & reg->tx_rollover_ind) ==
- (rdptr & reg->tx_rollover_ind)))
- return 1;
- break;
- }
-
- return 0;
-}
-
-static inline int
-mwifiex_pcie_txbd_not_full(struct pcie_service_card *card)
-{
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
-
- switch (card->dev->device) {
- case PCIE_DEVICE_ID_MARVELL_88W8766P:
- if (((card->txbd_wrptr & reg->tx_mask) !=
- (card->txbd_rdptr & reg->tx_mask)) ||
- ((card->txbd_wrptr & reg->tx_rollover_ind) !=
- (card->txbd_rdptr & reg->tx_rollover_ind)))
- return 1;
- break;
- case PCIE_DEVICE_ID_MARVELL_88W8897:
- case PCIE_DEVICE_ID_MARVELL_88W8997:
- if (((card->txbd_wrptr & reg->tx_mask) !=
- (card->txbd_rdptr & reg->tx_mask)) ||
- ((card->txbd_wrptr & reg->tx_rollover_ind) ==
- (card->txbd_rdptr & reg->tx_rollover_ind)))
- return 1;
- break;
- }
-
- return 0;
-}
-
-#endif /* _MWIFIEX_PCIE_H */
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
deleted file mode 100644
index 78a8474e1a3d..000000000000
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ /dev/null
@@ -1,2684 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: SDIO specific handling
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 <linux/firmware.h>
-
-#include "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-#include "sdio.h"
-
-
-#define SDIO_VERSION "1.0"
-
-/* The mwifiex_sdio_remove() callback function is called when
- * user removes this module from kernel space or ejects
- * the card from the slot. The driver handles these 2 cases
- * differently.
- * If the user is removing the module, the few commands (FUNC_SHUTDOWN,
- * HS_CANCEL etc.) are sent to the firmware.
- * If the card is removed, there is no need to send these command.
- *
- * The variable 'user_rmmod' is used to distinguish these two
- * scenarios. This flag is initialized as FALSE in case the card
- * is removed, and will be set to TRUE for module removal when
- * module_exit function is called.
- */
-static u8 user_rmmod;
-
-static struct mwifiex_if_ops sdio_ops;
-static unsigned long iface_work_flags;
-
-static struct semaphore add_remove_card_sem;
-
-static struct memory_type_mapping generic_mem_type_map[] = {
- {"DUMP", NULL, 0, 0xDD},
-};
-
-static struct memory_type_mapping mem_type_mapping_tbl[] = {
- {"ITCM", NULL, 0, 0xF0},
- {"DTCM", NULL, 0, 0xF1},
- {"SQRAM", NULL, 0, 0xF2},
- {"APU", NULL, 0, 0xF3},
- {"CIU", NULL, 0, 0xF4},
- {"ICU", NULL, 0, 0xF5},
- {"MAC", NULL, 0, 0xF6},
- {"EXT7", NULL, 0, 0xF7},
- {"EXT8", NULL, 0, 0xF8},
- {"EXT9", NULL, 0, 0xF9},
- {"EXT10", NULL, 0, 0xFA},
- {"EXT11", NULL, 0, 0xFB},
- {"EXT12", NULL, 0, 0xFC},
- {"EXT13", NULL, 0, 0xFD},
- {"EXTLAST", NULL, 0, 0xFE},
-};
-
-/*
- * SDIO probe.
- *
- * This function probes an mwifiex device and registers it. It allocates
- * the card structure, enables SDIO function number and initiates the
- * device registration and initialization procedure by adding a logical
- * interface.
- */
-static int
-mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
-{
- int ret;
- struct sdio_mmc_card *card = NULL;
-
- pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n",
- func->vendor, func->device, func->class, func->num);
-
- card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
- if (!card)
- return -ENOMEM;
-
- card->func = func;
- card->device_id = id;
-
- func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
-
- if (id->driver_data) {
- struct mwifiex_sdio_device *data = (void *)id->driver_data;
-
- card->firmware = data->firmware;
- card->reg = data->reg;
- card->max_ports = data->max_ports;
- card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
- card->supports_sdio_new_mode = data->supports_sdio_new_mode;
- card->has_control_mask = data->has_control_mask;
- card->tx_buf_size = data->tx_buf_size;
- card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
- card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
- card->can_dump_fw = data->can_dump_fw;
- card->fw_dump_enh = data->fw_dump_enh;
- card->can_auto_tdls = data->can_auto_tdls;
- card->can_ext_scan = data->can_ext_scan;
- }
-
- sdio_claim_host(func);
- ret = sdio_enable_func(func);
- sdio_release_host(func);
-
- if (ret) {
- pr_err("%s: failed to enable function\n", __func__);
- kfree(card);
- return -EIO;
- }
-
- if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops,
- MWIFIEX_SDIO)) {
- pr_err("%s: add card failed\n", __func__);
- kfree(card);
- sdio_claim_host(func);
- ret = sdio_disable_func(func);
- sdio_release_host(func);
- ret = -1;
- }
-
- return ret;
-}
-
-/*
- * SDIO resume.
- *
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not resumed, this function turns on the traffic and
- * sends a host sleep cancel request to the firmware.
- */
-static int mwifiex_sdio_resume(struct device *dev)
-{
- struct sdio_func *func = dev_to_sdio_func(dev);
- struct sdio_mmc_card *card;
- struct mwifiex_adapter *adapter;
- mmc_pm_flag_t pm_flag = 0;
-
- if (func) {
- pm_flag = sdio_get_host_pm_caps(func);
- card = sdio_get_drvdata(func);
- if (!card || !card->adapter) {
- pr_err("resume: invalid card or adapter\n");
- return 0;
- }
- } else {
- pr_err("resume: sdio_func is not specified\n");
- return 0;
- }
-
- adapter = card->adapter;
-
- if (!adapter->is_suspended) {
- mwifiex_dbg(adapter, WARN,
- "device already resumed\n");
- return 0;
- }
-
- adapter->is_suspended = false;
-
- /* Disable Host Sleep */
- mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
- MWIFIEX_ASYNC_CMD);
-
- return 0;
-}
-
-/*
- * SDIO remove.
- *
- * This function removes the interface and frees up the card structure.
- */
-static void
-mwifiex_sdio_remove(struct sdio_func *func)
-{
- struct sdio_mmc_card *card;
- struct mwifiex_adapter *adapter;
- struct mwifiex_private *priv;
-
- card = sdio_get_drvdata(func);
- if (!card)
- return;
-
- adapter = card->adapter;
- if (!adapter || !adapter->priv_num)
- return;
-
- mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num);
-
- if (user_rmmod) {
- if (adapter->is_suspended)
- mwifiex_sdio_resume(adapter->dev);
-
- mwifiex_deauthenticate_all(adapter);
-
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- mwifiex_disable_auto_ds(priv);
- mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN);
- }
-
- mwifiex_remove_card(card->adapter, &add_remove_card_sem);
-}
-
-/*
- * SDIO suspend.
- *
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not suspended, this function allocates and sends a host
- * sleep activate request to the firmware and turns off the traffic.
- */
-static int mwifiex_sdio_suspend(struct device *dev)
-{
- struct sdio_func *func = dev_to_sdio_func(dev);
- struct sdio_mmc_card *card;
- struct mwifiex_adapter *adapter;
- mmc_pm_flag_t pm_flag = 0;
- int ret = 0;
-
- if (func) {
- pm_flag = sdio_get_host_pm_caps(func);
- pr_debug("cmd: %s: suspend: PM flag = 0x%x\n",
- sdio_func_id(func), pm_flag);
- if (!(pm_flag & MMC_PM_KEEP_POWER)) {
- pr_err("%s: cannot remain alive while host is"
- " suspended\n", sdio_func_id(func));
- return -ENOSYS;
- }
-
- card = sdio_get_drvdata(func);
- if (!card || !card->adapter) {
- pr_err("suspend: invalid card or adapter\n");
- return 0;
- }
- } else {
- pr_err("suspend: sdio_func is not specified\n");
- return 0;
- }
-
- adapter = card->adapter;
-
- /* Enable the Host Sleep */
- if (!mwifiex_enable_hs(adapter)) {
- mwifiex_dbg(adapter, ERROR,
- "cmd: failed to suspend\n");
- adapter->hs_enabling = false;
- return -EFAULT;
- }
-
- mwifiex_dbg(adapter, INFO,
- "cmd: suspend with MMC_PM_KEEP_POWER\n");
- ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
-
- /* Indicate device suspended */
- adapter->is_suspended = true;
- adapter->hs_enabling = false;
-
- return ret;
-}
-
-/* Device ID for SD8786 */
-#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116)
-/* Device ID for SD8787 */
-#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119)
-/* Device ID for SD8797 */
-#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129)
-/* Device ID for SD8897 */
-#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d)
-/* Device ID for SD8887 */
-#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135)
-/* Device ID for SD8801 */
-#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139)
-/* Device ID for SD8997 */
-#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141)
-
-
-/* WLAN IDs */
-static const struct sdio_device_id mwifiex_ids[] = {
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
- .driver_data = (unsigned long) &mwifiex_sdio_sd8786},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
- .driver_data = (unsigned long) &mwifiex_sdio_sd8787},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
- .driver_data = (unsigned long) &mwifiex_sdio_sd8797},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
- .driver_data = (unsigned long) &mwifiex_sdio_sd8897},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887),
- .driver_data = (unsigned long)&mwifiex_sdio_sd8887},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
- .driver_data = (unsigned long)&mwifiex_sdio_sd8801},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997),
- .driver_data = (unsigned long)&mwifiex_sdio_sd8997},
- {},
-};
-
-MODULE_DEVICE_TABLE(sdio, mwifiex_ids);
-
-static const struct dev_pm_ops mwifiex_sdio_pm_ops = {
- .suspend = mwifiex_sdio_suspend,
- .resume = mwifiex_sdio_resume,
-};
-
-static struct sdio_driver mwifiex_sdio = {
- .name = "mwifiex_sdio",
- .id_table = mwifiex_ids,
- .probe = mwifiex_sdio_probe,
- .remove = mwifiex_sdio_remove,
- .drv = {
- .owner = THIS_MODULE,
- .pm = &mwifiex_sdio_pm_ops,
- }
-};
-
-/* Write data into SDIO card register. Caller claims SDIO device. */
-static int
-mwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data)
-{
- int ret = -1;
- sdio_writeb(func, data, reg, &ret);
- return ret;
-}
-
-/*
- * This function writes data into SDIO card register.
- */
-static int
-mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret;
-
- sdio_claim_host(card->func);
- ret = mwifiex_write_reg_locked(card->func, reg, data);
- sdio_release_host(card->func);
-
- return ret;
-}
-
-/*
- * This function reads data from SDIO card register.
- */
-static int
-mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret = -1;
- u8 val;
-
- sdio_claim_host(card->func);
- val = sdio_readb(card->func, reg, &ret);
- sdio_release_host(card->func);
-
- *data = val;
-
- return ret;
-}
-
-/*
- * This function writes multiple data into SDIO card memory.
- *
- * This does not work in suspended mode.
- */
-static int
-mwifiex_write_data_sync(struct mwifiex_adapter *adapter,
- u8 *buffer, u32 pkt_len, u32 port)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret;
- u8 blk_mode =
- (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE;
- u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
- u32 blk_cnt =
- (blk_mode ==
- BLOCK_MODE) ? (pkt_len /
- MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len;
- u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK);
-
- if (adapter->is_suspended) {
- mwifiex_dbg(adapter, ERROR,
- "%s: not allowed while suspended\n", __func__);
- return -1;
- }
-
- sdio_claim_host(card->func);
-
- ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size);
-
- sdio_release_host(card->func);
-
- return ret;
-}
-
-/*
- * This function reads multiple data from SDIO card memory.
- */
-static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer,
- u32 len, u32 port, u8 claim)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret;
- u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE
- : BLOCK_MODE;
- u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
- u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE)
- : len;
- u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK);
-
- if (claim)
- sdio_claim_host(card->func);
-
- ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size);
-
- if (claim)
- sdio_release_host(card->func);
-
- return ret;
-}
-
-/*
- * This function wakes up the card.
- *
- * A host power up command is written to the card configuration
- * register to wake up the card.
- */
-static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
-{
- mwifiex_dbg(adapter, EVENT,
- "event: wakeup device...\n");
-
- return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP);
-}
-
-/*
- * This function is called after the card has woken up.
- *
- * The card configuration register is reset.
- */
-static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
-{
- mwifiex_dbg(adapter, EVENT,
- "cmd: wakeup device completed\n");
-
- return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0);
-}
-
-/*
- * This function is used to initialize IO ports for the
- * chipsets supporting SDIO new mode eg SD8897.
- */
-static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter)
-{
- u8 reg;
- struct sdio_mmc_card *card = adapter->card;
-
- adapter->ioport = MEM_PORT;
-
- /* enable sdio new mode */
- if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, &reg))
- return -1;
- if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg,
- reg | CMD53_NEW_MODE))
- return -1;
-
- /* Configure cmd port and enable reading rx length from the register */
- if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, &reg))
- return -1;
- if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0,
- reg | CMD_PORT_RD_LEN_EN))
- return -1;
-
- /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
- * completed
- */
- if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, &reg))
- return -1;
- if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1,
- reg | CMD_PORT_AUTO_EN))
- return -1;
-
- return 0;
-}
-
-/* This function initializes the IO ports.
- *
- * The following operations are performed -
- * - Read the IO ports (0, 1 and 2)
- * - Set host interrupt Reset-To-Read to clear
- * - Set auto re-enable interrupt
- */
-static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
-{
- u8 reg;
- struct sdio_mmc_card *card = adapter->card;
-
- adapter->ioport = 0;
-
- if (card->supports_sdio_new_mode) {
- if (mwifiex_init_sdio_new_mode(adapter))
- return -1;
- goto cont;
- }
-
- /* Read the IO port */
- if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, &reg))
- adapter->ioport |= (reg & 0xff);
- else
- return -1;
-
- if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, &reg))
- adapter->ioport |= ((reg & 0xff) << 8);
- else
- return -1;
-
- if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, &reg))
- adapter->ioport |= ((reg & 0xff) << 16);
- else
- return -1;
-cont:
- mwifiex_dbg(adapter, INFO,
- "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
-
- /* Set Host interrupt reset to read to clear */
- if (!mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, &reg))
- mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg,
- reg | card->reg->sdio_int_mask);
- else
- return -1;
-
- /* Dnld/Upld ready set to auto reset */
- if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, &reg))
- mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg,
- reg | AUTO_RE_ENABLE_INT);
- else
- return -1;
-
- return 0;
-}
-
-/*
- * This function sends data to the card.
- */
-static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter,
- u8 *payload, u32 pkt_len, u32 port)
-{
- u32 i = 0;
- int ret;
-
- do {
- ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port);
- if (ret) {
- i++;
- mwifiex_dbg(adapter, ERROR,
- "host_to_card, write iomem\t"
- "(%d) failed: %d\n", i, ret);
- if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04))
- mwifiex_dbg(adapter, ERROR,
- "write CFG reg failed\n");
-
- ret = -1;
- if (i > MAX_WRITE_IOMEM_RETRY)
- return ret;
- }
- } while (ret == -1);
-
- return ret;
-}
-
-/*
- * This function gets the read port.
- *
- * If control port bit is set in MP read bitmap, the control port
- * is returned, otherwise the current read port is returned and
- * the value is increased (provided it does not reach the maximum
- * limit, in which case it is reset to 1)
- */
-static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
-{
- struct sdio_mmc_card *card = adapter->card;
- const struct mwifiex_sdio_card_reg *reg = card->reg;
- u32 rd_bitmap = card->mp_rd_bitmap;
-
- mwifiex_dbg(adapter, DATA,
- "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
-
- if (card->supports_sdio_new_mode) {
- if (!(rd_bitmap & reg->data_port_mask))
- return -1;
- } else {
- if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
- return -1;
- }
-
- if ((card->has_control_mask) &&
- (card->mp_rd_bitmap & CTRL_PORT_MASK)) {
- card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
- *port = CTRL_PORT;
- mwifiex_dbg(adapter, DATA,
- "data: port=%d mp_rd_bitmap=0x%08x\n",
- *port, card->mp_rd_bitmap);
- return 0;
- }
-
- if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port)))
- return -1;
-
- /* We are now handling the SDIO data ports */
- card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port));
- *port = card->curr_rd_port;
-
- if (++card->curr_rd_port == card->max_ports)
- card->curr_rd_port = reg->start_rd_port;
-
- mwifiex_dbg(adapter, DATA,
- "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n",
- *port, rd_bitmap, card->mp_rd_bitmap);
-
- return 0;
-}
-
-/*
- * This function gets the write port for data.
- *
- * The current write port is returned if available and the value is
- * increased (provided it does not reach the maximum limit, in which
- * case it is reset to 1)
- */
-static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port)
-{
- struct sdio_mmc_card *card = adapter->card;
- const struct mwifiex_sdio_card_reg *reg = card->reg;
- u32 wr_bitmap = card->mp_wr_bitmap;
-
- mwifiex_dbg(adapter, DATA,
- "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
-
- if (!(wr_bitmap & card->mp_data_port_mask)) {
- adapter->data_sent = true;
- return -EBUSY;
- }
-
- if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
- card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port));
- *port = card->curr_wr_port;
- if (++card->curr_wr_port == card->mp_end_port)
- card->curr_wr_port = reg->start_wr_port;
- } else {
- adapter->data_sent = true;
- return -EBUSY;
- }
-
- if ((card->has_control_mask) && (*port == CTRL_PORT)) {
- mwifiex_dbg(adapter, ERROR,
- "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
- *port, card->curr_wr_port, wr_bitmap,
- card->mp_wr_bitmap);
- return -1;
- }
-
- mwifiex_dbg(adapter, DATA,
- "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
- *port, wr_bitmap, card->mp_wr_bitmap);
-
- return 0;
-}
-
-/*
- * This function polls the card status.
- */
-static int
-mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
-{
- struct sdio_mmc_card *card = adapter->card;
- u32 tries;
- u8 cs;
-
- for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs))
- break;
- else if ((cs & bits) == bits)
- return 0;
-
- usleep_range(10, 20);
- }
-
- mwifiex_dbg(adapter, ERROR,
- "poll card status failed, tries = %d\n", tries);
-
- return -1;
-}
-
-/*
- * This function reads the firmware status.
- */
-static int
-mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
-{
- struct sdio_mmc_card *card = adapter->card;
- const struct mwifiex_sdio_card_reg *reg = card->reg;
- u8 fws0, fws1;
-
- if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0))
- return -1;
-
- if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1))
- return -1;
-
- *dat = (u16) ((fws1 << 8) | fws0);
-
- return 0;
-}
-
-/*
- * This function disables the host interrupt.
- *
- * The host interrupt mask is read, the disable bit is reset and
- * written back to the card host interrupt mask register.
- */
-static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- struct sdio_func *func = card->func;
-
- sdio_claim_host(func);
- mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0);
- sdio_release_irq(func);
- sdio_release_host(func);
-}
-
-/*
- * This function reads the interrupt status from card.
- */
-static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- u8 sdio_ireg;
- unsigned long flags;
-
- if (mwifiex_read_data_sync(adapter, card->mp_regs,
- card->reg->max_mp_regs,
- REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
- mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n");
- return;
- }
-
- sdio_ireg = card->mp_regs[card->reg->host_int_status_reg];
- if (sdio_ireg) {
- /*
- * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
- * For SDIO new mode CMD port interrupts
- * DN_LD_CMD_PORT_HOST_INT_STATUS and/or
- * UP_LD_CMD_PORT_HOST_INT_STATUS
- * Clear the interrupt status register
- */
- mwifiex_dbg(adapter, INTR,
- "int: sdio_ireg = %#x\n", sdio_ireg);
- spin_lock_irqsave(&adapter->int_lock, flags);
- adapter->int_status |= sdio_ireg;
- spin_unlock_irqrestore(&adapter->int_lock, flags);
- }
-}
-
-/*
- * SDIO interrupt handler.
- *
- * This function reads the interrupt status from firmware and handles
- * the interrupt in current thread (ksdioirqd) right away.
- */
-static void
-mwifiex_sdio_interrupt(struct sdio_func *func)
-{
- struct mwifiex_adapter *adapter;
- struct sdio_mmc_card *card;
-
- card = sdio_get_drvdata(func);
- if (!card || !card->adapter) {
- pr_debug("int: func=%p card=%p adapter=%p\n",
- func, card, card ? card->adapter : NULL);
- return;
- }
- adapter = card->adapter;
-
- if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
- adapter->ps_state = PS_STATE_AWAKE;
-
- mwifiex_interrupt_status(adapter);
- mwifiex_main_process(adapter);
-}
-
-/*
- * This function enables the host interrupt.
- *
- * The host interrupt enable mask is written to the card
- * host interrupt mask register.
- */
-static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- struct sdio_func *func = card->func;
- int ret;
-
- sdio_claim_host(func);
-
- /* Request the SDIO IRQ */
- ret = sdio_claim_irq(func, mwifiex_sdio_interrupt);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "claim irq failed: ret=%d\n", ret);
- goto out;
- }
-
- /* Simply write the mask to the register */
- ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg,
- card->reg->host_int_enable);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "enable host interrupt failed\n");
- sdio_release_irq(func);
- }
-
-out:
- sdio_release_host(func);
- return ret;
-}
-
-/*
- * This function sends a data buffer to the card.
- */
-static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter,
- u32 *type, u8 *buffer,
- u32 npayload, u32 ioport)
-{
- int ret;
- u32 nb;
-
- if (!buffer) {
- mwifiex_dbg(adapter, ERROR,
- "%s: buffer is NULL\n", __func__);
- return -1;
- }
-
- ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1);
-
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "%s: read iomem failed: %d\n", __func__,
- ret);
- return -1;
- }
-
- nb = le16_to_cpu(*(__le16 *) (buffer));
- if (nb > npayload) {
- mwifiex_dbg(adapter, ERROR,
- "%s: invalid packet, nb=%d npayload=%d\n",
- __func__, nb, npayload);
- return -1;
- }
-
- *type = le16_to_cpu(*(__le16 *) (buffer + 2));
-
- return ret;
-}
-
-/*
- * This function downloads the firmware to the card.
- *
- * Firmware is downloaded to the card in blocks. Every block download
- * is tested for CRC errors, and retried a number of times before
- * returning failure.
- */
-static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
- struct mwifiex_fw_image *fw)
-{
- struct sdio_mmc_card *card = adapter->card;
- const struct mwifiex_sdio_card_reg *reg = card->reg;
- int ret;
- u8 *firmware = fw->fw_buf;
- u32 firmware_len = fw->fw_len;
- u32 offset = 0;
- u8 base0, base1;
- u8 *fwbuf;
- u16 len = 0;
- u32 txlen, tx_blocks = 0, tries;
- u32 i = 0;
-
- if (!firmware_len) {
- mwifiex_dbg(adapter, ERROR,
- "firmware image not found! Terminating download\n");
- return -1;
- }
-
- mwifiex_dbg(adapter, INFO,
- "info: downloading FW image (%d bytes)\n",
- firmware_len);
-
- /* Assume that the allocated buffer is 8-byte aligned */
- fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL);
- if (!fwbuf)
- return -ENOMEM;
-
- sdio_claim_host(card->func);
-
- /* Perform firmware data transfer */
- do {
- /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY
- bits */
- ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY |
- DN_LD_CARD_RDY);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "FW download with helper:\t"
- "poll status timeout @ %d\n", offset);
- goto done;
- }
-
- /* More data? */
- if (offset >= firmware_len)
- break;
-
- for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- ret = mwifiex_read_reg(adapter, reg->base_0_reg,
- &base0);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "dev BASE0 register read failed:\t"
- "base0=%#04X(%d). Terminating dnld\n",
- base0, base0);
- goto done;
- }
- ret = mwifiex_read_reg(adapter, reg->base_1_reg,
- &base1);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "dev BASE1 register read failed:\t"
- "base1=%#04X(%d). Terminating dnld\n",
- base1, base1);
- goto done;
- }
- len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff));
-
- if (len)
- break;
-
- usleep_range(10, 20);
- }
-
- if (!len) {
- break;
- } else if (len > MWIFIEX_UPLD_SIZE) {
- mwifiex_dbg(adapter, ERROR,
- "FW dnld failed @ %d, invalid length %d\n",
- offset, len);
- ret = -1;
- goto done;
- }
-
- txlen = len;
-
- if (len & BIT(0)) {
- i++;
- if (i > MAX_WRITE_IOMEM_RETRY) {
- mwifiex_dbg(adapter, ERROR,
- "FW dnld failed @ %d, over max retry\n",
- offset);
- ret = -1;
- goto done;
- }
- mwifiex_dbg(adapter, ERROR,
- "CRC indicated by the helper:\t"
- "len = 0x%04X, txlen = %d\n", len, txlen);
- len &= ~BIT(0);
- /* Setting this to 0 to resend from same offset */
- txlen = 0;
- } else {
- i = 0;
-
- /* Set blocksize to transfer - checking for last
- block */
- if (firmware_len - offset < txlen)
- txlen = firmware_len - offset;
-
- tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1)
- / MWIFIEX_SDIO_BLOCK_SIZE;
-
- /* Copy payload to buffer */
- memmove(fwbuf, &firmware[offset], txlen);
- }
-
- ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks *
- MWIFIEX_SDIO_BLOCK_SIZE,
- adapter->ioport);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "FW download, write iomem (%d) failed @ %d\n",
- i, offset);
- if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04))
- mwifiex_dbg(adapter, ERROR,
- "write CFG reg failed\n");
-
- ret = -1;
- goto done;
- }
-
- offset += txlen;
- } while (true);
-
- sdio_release_host(card->func);
-
- mwifiex_dbg(adapter, MSG,
- "info: FW download over, size %d bytes\n", offset);
-
- ret = 0;
-done:
- kfree(fwbuf);
- return ret;
-}
-
-/*
- * This function checks the firmware status in card.
- *
- * The winner interface is also determined by this function.
- */
-static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
- u32 poll_num)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret = 0;
- u16 firmware_stat;
- u32 tries;
- u8 winner_status;
-
- /* Wait for firmware initialization event */
- for (tries = 0; tries < poll_num; tries++) {
- ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat);
- if (ret)
- continue;
- if (firmware_stat == FIRMWARE_READY_SDIO) {
- ret = 0;
- break;
- } else {
- msleep(100);
- ret = -1;
- }
- }
-
- if (ret) {
- if (mwifiex_read_reg
- (adapter, card->reg->status_reg_0, &winner_status))
- winner_status = 0;
-
- if (winner_status)
- adapter->winner = 0;
- else
- adapter->winner = 1;
- }
- return ret;
-}
-
-/*
- * This function decode sdio aggreation pkt.
- *
- * Based on the the data block size and pkt_len,
- * skb data will be decoded to few packets.
- */
-static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter,
- struct sk_buff *skb)
-{
- u32 total_pkt_len, pkt_len;
- struct sk_buff *skb_deaggr;
- u32 pkt_type;
- u16 blk_size;
- u8 blk_num;
- u8 *data;
-
- data = skb->data;
- total_pkt_len = skb->len;
-
- while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) {
- if (total_pkt_len < adapter->sdio_rx_block_size)
- break;
- blk_num = *(data + BLOCK_NUMBER_OFFSET);
- blk_size = adapter->sdio_rx_block_size * blk_num;
- if (blk_size > total_pkt_len) {
- mwifiex_dbg(adapter, ERROR,
- "%s: error in blk_size,\t"
- "blk_num=%d, blk_size=%d, total_pkt_len=%d\n",
- __func__, blk_num, blk_size, total_pkt_len);
- break;
- }
- pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET));
- pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET +
- 2));
- if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) {
- mwifiex_dbg(adapter, ERROR,
- "%s: error in pkt_len,\t"
- "pkt_len=%d, blk_size=%d\n",
- __func__, pkt_len, blk_size);
- break;
- }
- skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len,
- GFP_KERNEL | GFP_DMA);
- if (!skb_deaggr)
- break;
- skb_put(skb_deaggr, pkt_len);
- memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len);
- skb_pull(skb_deaggr, INTF_HEADER_LEN);
-
- mwifiex_handle_rx_packet(adapter, skb_deaggr);
- data += blk_size;
- total_pkt_len -= blk_size;
- }
-}
-
-/*
- * This function decodes a received packet.
- *
- * Based on the type, the packet is treated as either a data, or
- * a command response, or an event, and the correct handler
- * function is invoked.
- */
-static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
- struct sk_buff *skb, u32 upld_typ)
-{
- u8 *cmd_buf;
- __le16 *curr_ptr = (__le16 *)skb->data;
- u16 pkt_len = le16_to_cpu(*curr_ptr);
- struct mwifiex_rxinfo *rx_info;
-
- if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) {
- skb_trim(skb, pkt_len);
- skb_pull(skb, INTF_HEADER_LEN);
- }
-
- switch (upld_typ) {
- case MWIFIEX_TYPE_AGGR_DATA:
- mwifiex_dbg(adapter, INFO,
- "info: --- Rx: Aggr Data packet ---\n");
- rx_info = MWIFIEX_SKB_RXCB(skb);
- rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA;
- if (adapter->rx_work_enabled) {
- skb_queue_tail(&adapter->rx_data_q, skb);
- atomic_inc(&adapter->rx_pending);
- adapter->data_received = true;
- } else {
- mwifiex_deaggr_sdio_pkt(adapter, skb);
- dev_kfree_skb_any(skb);
- }
- break;
-
- case MWIFIEX_TYPE_DATA:
- mwifiex_dbg(adapter, DATA,
- "info: --- Rx: Data packet ---\n");
- if (adapter->rx_work_enabled) {
- skb_queue_tail(&adapter->rx_data_q, skb);
- adapter->data_received = true;
- atomic_inc(&adapter->rx_pending);
- } else {
- mwifiex_handle_rx_packet(adapter, skb);
- }
- break;
-
- case MWIFIEX_TYPE_CMD:
- mwifiex_dbg(adapter, CMD,
- "info: --- Rx: Cmd Response ---\n");
- /* take care of curr_cmd = NULL case */
- if (!adapter->curr_cmd) {
- cmd_buf = adapter->upld_buf;
-
- if (adapter->ps_state == PS_STATE_SLEEP_CFM)
- mwifiex_process_sleep_confirm_resp(adapter,
- skb->data,
- skb->len);
-
- memcpy(cmd_buf, skb->data,
- min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER,
- skb->len));
-
- dev_kfree_skb_any(skb);
- } else {
- adapter->cmd_resp_received = true;
- adapter->curr_cmd->resp_skb = skb;
- }
- break;
-
- case MWIFIEX_TYPE_EVENT:
- mwifiex_dbg(adapter, EVENT,
- "info: --- Rx: Event ---\n");
- adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data);
-
- if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE))
- memcpy(adapter->event_body,
- skb->data + MWIFIEX_EVENT_HEADER_LEN,
- skb->len);
-
- /* event cause has been saved to adapter->event_cause */
- adapter->event_received = true;
- adapter->event_skb = skb;
-
- break;
-
- default:
- mwifiex_dbg(adapter, ERROR,
- "unknown upload type %#x\n", upld_typ);
- dev_kfree_skb_any(skb);
- break;
- }
-
- return 0;
-}
-
-/*
- * This function transfers received packets from card to driver, performing
- * aggregation if required.
- *
- * For data received on control port, or if aggregation is disabled, the
- * received buffers are uploaded as separate packets. However, if aggregation
- * is enabled and required, the buffers are copied onto an aggregation buffer,
- * provided there is space left, processed and finally uploaded.
- */
-static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
- u16 rx_len, u8 port)
-{
- struct sdio_mmc_card *card = adapter->card;
- s32 f_do_rx_aggr = 0;
- s32 f_do_rx_cur = 0;
- s32 f_aggr_cur = 0;
- s32 f_post_aggr_cur = 0;
- struct sk_buff *skb_deaggr;
- struct sk_buff *skb = NULL;
- u32 pkt_len, pkt_type, mport, pind;
- u8 *curr_ptr;
-
- if ((card->has_control_mask) && (port == CTRL_PORT)) {
- /* Read the command Resp without aggr */
- mwifiex_dbg(adapter, CMD,
- "info: %s: no aggregation for cmd\t"
- "response\n", __func__);
-
- f_do_rx_cur = 1;
- goto rx_curr_single;
- }
-
- if (!card->mpa_rx.enabled) {
- mwifiex_dbg(adapter, WARN,
- "info: %s: rx aggregation disabled\n",
- __func__);
-
- f_do_rx_cur = 1;
- goto rx_curr_single;
- }
-
- if ((!card->has_control_mask && (card->mp_rd_bitmap &
- card->reg->data_port_mask)) ||
- (card->has_control_mask && (card->mp_rd_bitmap &
- (~((u32) CTRL_PORT_MASK))))) {
- /* Some more data RX pending */
- mwifiex_dbg(adapter, INFO,
- "info: %s: not last packet\n", __func__);
-
- if (MP_RX_AGGR_IN_PROGRESS(card)) {
- if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) {
- f_aggr_cur = 1;
- } else {
- /* No room in Aggr buf, do rx aggr now */
- f_do_rx_aggr = 1;
- f_post_aggr_cur = 1;
- }
- } else {
- /* Rx aggr not in progress */
- f_aggr_cur = 1;
- }
-
- } else {
- /* No more data RX pending */
- mwifiex_dbg(adapter, INFO,
- "info: %s: last packet\n", __func__);
-
- if (MP_RX_AGGR_IN_PROGRESS(card)) {
- f_do_rx_aggr = 1;
- if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len))
- f_aggr_cur = 1;
- else
- /* No room in Aggr buf, do rx aggr now */
- f_do_rx_cur = 1;
- } else {
- f_do_rx_cur = 1;
- }
- }
-
- if (f_aggr_cur) {
- mwifiex_dbg(adapter, INFO,
- "info: current packet aggregation\n");
- /* Curr pkt can be aggregated */
- mp_rx_aggr_setup(card, rx_len, port);
-
- if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
- mp_rx_aggr_port_limit_reached(card)) {
- mwifiex_dbg(adapter, INFO,
- "info: %s: aggregated packet\t"
- "limit reached\n", __func__);
- /* No more pkts allowed in Aggr buf, rx it */
- f_do_rx_aggr = 1;
- }
- }
-
- if (f_do_rx_aggr) {
- /* do aggr RX now */
- mwifiex_dbg(adapter, DATA,
- "info: do_rx_aggr: num of packets: %d\n",
- card->mpa_rx.pkt_cnt);
-
- if (card->supports_sdio_new_mode) {
- int i;
- u32 port_count;
-
- for (i = 0, port_count = 0; i < card->max_ports; i++)
- if (card->mpa_rx.ports & BIT(i))
- port_count++;
-
- /* Reading data from "start_port + 0" to "start_port +
- * port_count -1", so decrease the count by 1
- */
- port_count--;
- mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
- (port_count << 8)) + card->mpa_rx.start_port;
- } else {
- mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
- (card->mpa_rx.ports << 4)) +
- card->mpa_rx.start_port;
- }
-
- if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
- card->mpa_rx.buf_len, mport, 1))
- goto error;
-
- curr_ptr = card->mpa_rx.buf;
-
- for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
- u32 *len_arr = card->mpa_rx.len_arr;
-
- /* get curr PKT len & type */
- pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]);
- pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]);
-
- /* copy pkt to deaggr buf */
- skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind],
- GFP_KERNEL |
- GFP_DMA);
- if (!skb_deaggr) {
- mwifiex_dbg(adapter, ERROR, "skb allocation failure\t"
- "drop pkt len=%d type=%d\n",
- pkt_len, pkt_type);
- curr_ptr += len_arr[pind];
- continue;
- }
-
- skb_put(skb_deaggr, len_arr[pind]);
-
- if ((pkt_type == MWIFIEX_TYPE_DATA ||
- (pkt_type == MWIFIEX_TYPE_AGGR_DATA &&
- adapter->sdio_rx_aggr_enable)) &&
- (pkt_len <= len_arr[pind])) {
-
- memcpy(skb_deaggr->data, curr_ptr, pkt_len);
-
- skb_trim(skb_deaggr, pkt_len);
-
- /* Process de-aggr packet */
- mwifiex_decode_rx_packet(adapter, skb_deaggr,
- pkt_type);
- } else {
- mwifiex_dbg(adapter, ERROR,
- "drop wrong aggr pkt:\t"
- "sdio_single_port_rx_aggr=%d\t"
- "type=%d len=%d max_len=%d\n",
- adapter->sdio_rx_aggr_enable,
- pkt_type, pkt_len, len_arr[pind]);
- dev_kfree_skb_any(skb_deaggr);
- }
- curr_ptr += len_arr[pind];
- }
- MP_RX_AGGR_BUF_RESET(card);
- }
-
-rx_curr_single:
- if (f_do_rx_cur) {
- mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n",
- port, rx_len);
-
- skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
- if (!skb) {
- mwifiex_dbg(adapter, ERROR,
- "single skb allocated fail,\t"
- "drop pkt port=%d len=%d\n", port, rx_len);
- if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
- card->mpa_rx.buf, rx_len,
- adapter->ioport + port))
- goto error;
- return 0;
- }
-
- skb_put(skb, rx_len);
-
- if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
- skb->data, skb->len,
- adapter->ioport + port))
- goto error;
- if (!adapter->sdio_rx_aggr_enable &&
- pkt_type == MWIFIEX_TYPE_AGGR_DATA) {
- mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t"
- "current SDIO RX Aggr not enabled\n",
- pkt_type);
- dev_kfree_skb_any(skb);
- return 0;
- }
-
- mwifiex_decode_rx_packet(adapter, skb, pkt_type);
- }
- if (f_post_aggr_cur) {
- mwifiex_dbg(adapter, INFO,
- "info: current packet aggregation\n");
- /* Curr pkt can be aggregated */
- mp_rx_aggr_setup(card, rx_len, port);
- }
-
- return 0;
-error:
- if (MP_RX_AGGR_IN_PROGRESS(card))
- MP_RX_AGGR_BUF_RESET(card);
-
- if (f_do_rx_cur && skb)
- /* Single transfer pending. Free curr buff also */
- dev_kfree_skb_any(skb);
-
- return -1;
-}
-
-/*
- * This function checks the current interrupt status.
- *
- * The following interrupts are checked and handled by this function -
- * - Data sent
- * - Command sent
- * - Packets received
- *
- * Since the firmware does not generate download ready interrupt if the
- * port updated is command port only, command sent interrupt checking
- * should be done manually, and for every SDIO interrupt.
- *
- * In case of Rx packets received, the packets are uploaded from card to
- * host and processed accordingly.
- */
-static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- const struct mwifiex_sdio_card_reg *reg = card->reg;
- int ret = 0;
- u8 sdio_ireg;
- struct sk_buff *skb;
- u8 port = CTRL_PORT;
- u32 len_reg_l, len_reg_u;
- u32 rx_blocks;
- u16 rx_len;
- unsigned long flags;
- u32 bitmap;
- u8 cr;
-
- spin_lock_irqsave(&adapter->int_lock, flags);
- sdio_ireg = adapter->int_status;
- adapter->int_status = 0;
- spin_unlock_irqrestore(&adapter->int_lock, flags);
-
- if (!sdio_ireg)
- return ret;
-
- /* Following interrupt is only for SDIO new mode */
- if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
- adapter->cmd_sent = false;
-
- /* Following interrupt is only for SDIO new mode */
- if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
- u32 pkt_type;
-
- /* read the len of control packet */
- rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8;
- rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0];
- rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE);
- if (rx_len <= INTF_HEADER_LEN ||
- (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
- MWIFIEX_RX_DATA_BUF_SIZE)
- return -1;
- rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
- mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len);
-
- skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
- if (!skb)
- return -1;
-
- skb_put(skb, rx_len);
-
- if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data,
- skb->len, adapter->ioport |
- CMD_PORT_SLCT)) {
- mwifiex_dbg(adapter, ERROR,
- "%s: failed to card_to_host", __func__);
- dev_kfree_skb_any(skb);
- goto term_cmd;
- }
-
- if ((pkt_type != MWIFIEX_TYPE_CMD) &&
- (pkt_type != MWIFIEX_TYPE_EVENT))
- mwifiex_dbg(adapter, ERROR,
- "%s:Received wrong packet on cmd port",
- __func__);
-
- mwifiex_decode_rx_packet(adapter, skb, pkt_type);
- }
-
- if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
- bitmap = (u32) card->mp_regs[reg->wr_bitmap_l];
- bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
- if (card->supports_sdio_new_mode) {
- bitmap |=
- ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16;
- bitmap |=
- ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24;
- }
- card->mp_wr_bitmap = bitmap;
-
- mwifiex_dbg(adapter, INTR,
- "int: DNLD: wr_bitmap=0x%x\n",
- card->mp_wr_bitmap);
- if (adapter->data_sent &&
- (card->mp_wr_bitmap & card->mp_data_port_mask)) {
- mwifiex_dbg(adapter, INTR,
- "info: <--- Tx DONE Interrupt --->\n");
- adapter->data_sent = false;
- }
- }
-
- /* As firmware will not generate download ready interrupt if the port
- updated is command port only, cmd_sent should be done for any SDIO
- interrupt. */
- if (card->has_control_mask && adapter->cmd_sent) {
- /* Check if firmware has attach buffer at command port and
- update just that in wr_bit_map. */
- card->mp_wr_bitmap |=
- (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK;
- if (card->mp_wr_bitmap & CTRL_PORT_MASK)
- adapter->cmd_sent = false;
- }
-
- mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n",
- adapter->cmd_sent, adapter->data_sent);
- if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
- bitmap = (u32) card->mp_regs[reg->rd_bitmap_l];
- bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
- if (card->supports_sdio_new_mode) {
- bitmap |=
- ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16;
- bitmap |=
- ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24;
- }
- card->mp_rd_bitmap = bitmap;
- mwifiex_dbg(adapter, INTR,
- "int: UPLD: rd_bitmap=0x%x\n",
- card->mp_rd_bitmap);
-
- while (true) {
- ret = mwifiex_get_rd_port(adapter, &port);
- if (ret) {
- mwifiex_dbg(adapter, INFO,
- "info: no more rd_port available\n");
- break;
- }
- len_reg_l = reg->rd_len_p0_l + (port << 1);
- len_reg_u = reg->rd_len_p0_u + (port << 1);
- rx_len = ((u16) card->mp_regs[len_reg_u]) << 8;
- rx_len |= (u16) card->mp_regs[len_reg_l];
- mwifiex_dbg(adapter, INFO,
- "info: RX: port=%d rx_len=%u\n",
- port, rx_len);
- rx_blocks =
- (rx_len + MWIFIEX_SDIO_BLOCK_SIZE -
- 1) / MWIFIEX_SDIO_BLOCK_SIZE;
- if (rx_len <= INTF_HEADER_LEN ||
- (card->mpa_rx.enabled &&
- ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
- card->mpa_rx.buf_size))) {
- mwifiex_dbg(adapter, ERROR,
- "invalid rx_len=%d\n",
- rx_len);
- return -1;
- }
-
- rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
- mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n",
- rx_len);
-
- if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len,
- port)) {
- mwifiex_dbg(adapter, ERROR,
- "card_to_host_mpa failed: int status=%#x\n",
- sdio_ireg);
- goto term_cmd;
- }
- }
- }
-
- return 0;
-
-term_cmd:
- /* terminate cmd */
- if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
- mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n");
- else
- mwifiex_dbg(adapter, INFO,
- "info: CFG reg val = %d\n", cr);
-
- if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
- mwifiex_dbg(adapter, ERROR,
- "write CFG reg failed\n");
- else
- mwifiex_dbg(adapter, INFO, "info: write success\n");
-
- if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
- mwifiex_dbg(adapter, ERROR,
- "read CFG reg failed\n");
- else
- mwifiex_dbg(adapter, INFO,
- "info: CFG reg val =%x\n", cr);
-
- return -1;
-}
-
-/*
- * This function aggregates transmission buffers in driver and downloads
- * the aggregated packet to card.
- *
- * The individual packets are aggregated by copying into an aggregation
- * buffer and then downloaded to the card. Previous unsent packets in the
- * aggregation buffer are pre-copied first before new packets are added.
- * Aggregation is done till there is space left in the aggregation buffer,
- * or till new packets are available.
- *
- * The function will only download the packet to the card when aggregation
- * stops, otherwise it will just aggregate the packet in aggregation buffer
- * and return.
- */
-static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
- u8 *payload, u32 pkt_len, u32 port,
- u32 next_pkt_len)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret = 0;
- s32 f_send_aggr_buf = 0;
- s32 f_send_cur_buf = 0;
- s32 f_precopy_cur_buf = 0;
- s32 f_postcopy_cur_buf = 0;
- u32 mport;
-
- if (!card->mpa_tx.enabled ||
- (card->has_control_mask && (port == CTRL_PORT)) ||
- (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) {
- mwifiex_dbg(adapter, WARN,
- "info: %s: tx aggregation disabled\n",
- __func__);
-
- f_send_cur_buf = 1;
- goto tx_curr_single;
- }
-
- if (next_pkt_len) {
- /* More pkt in TX queue */
- mwifiex_dbg(adapter, INFO,
- "info: %s: more packets in queue.\n",
- __func__);
-
- if (MP_TX_AGGR_IN_PROGRESS(card)) {
- if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
- f_precopy_cur_buf = 1;
-
- if (!(card->mp_wr_bitmap &
- (1 << card->curr_wr_port)) ||
- !MP_TX_AGGR_BUF_HAS_ROOM(
- card, pkt_len + next_pkt_len))
- f_send_aggr_buf = 1;
- } else {
- /* No room in Aggr buf, send it */
- f_send_aggr_buf = 1;
-
- if (!(card->mp_wr_bitmap &
- (1 << card->curr_wr_port)))
- f_send_cur_buf = 1;
- else
- f_postcopy_cur_buf = 1;
- }
- } else {
- if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) &&
- (card->mp_wr_bitmap & (1 << card->curr_wr_port)))
- f_precopy_cur_buf = 1;
- else
- f_send_cur_buf = 1;
- }
- } else {
- /* Last pkt in TX queue */
- mwifiex_dbg(adapter, INFO,
- "info: %s: Last packet in Tx Queue.\n",
- __func__);
-
- if (MP_TX_AGGR_IN_PROGRESS(card)) {
- /* some packs in Aggr buf already */
- f_send_aggr_buf = 1;
-
- if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len))
- f_precopy_cur_buf = 1;
- else
- /* No room in Aggr buf, send it */
- f_send_cur_buf = 1;
- } else {
- f_send_cur_buf = 1;
- }
- }
-
- if (f_precopy_cur_buf) {
- mwifiex_dbg(adapter, DATA,
- "data: %s: precopy current buffer\n",
- __func__);
- MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
-
- if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) ||
- mp_tx_aggr_port_limit_reached(card))
- /* No more pkts allowed in Aggr buf, send it */
- f_send_aggr_buf = 1;
- }
-
- if (f_send_aggr_buf) {
- mwifiex_dbg(adapter, DATA,
- "data: %s: send aggr buffer: %d %d\n",
- __func__, card->mpa_tx.start_port,
- card->mpa_tx.ports);
- if (card->supports_sdio_new_mode) {
- u32 port_count;
- int i;
-
- for (i = 0, port_count = 0; i < card->max_ports; i++)
- if (card->mpa_tx.ports & BIT(i))
- port_count++;
-
- /* Writing data from "start_port + 0" to "start_port +
- * port_count -1", so decrease the count by 1
- */
- port_count--;
- mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
- (port_count << 8)) + card->mpa_tx.start_port;
- } else {
- mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
- (card->mpa_tx.ports << 4)) +
- card->mpa_tx.start_port;
- }
-
- ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
- card->mpa_tx.buf_len, mport);
-
- MP_TX_AGGR_BUF_RESET(card);
- }
-
-tx_curr_single:
- if (f_send_cur_buf) {
- mwifiex_dbg(adapter, DATA,
- "data: %s: send current buffer %d\n",
- __func__, port);
- ret = mwifiex_write_data_to_card(adapter, payload, pkt_len,
- adapter->ioport + port);
- }
-
- if (f_postcopy_cur_buf) {
- mwifiex_dbg(adapter, DATA,
- "data: %s: postcopy current buffer\n",
- __func__);
- MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
- }
-
- return ret;
-}
-
-/*
- * This function downloads data from driver to card.
- *
- * Both commands and data packets are transferred to the card by this
- * function.
- *
- * This function adds the SDIO specific header to the front of the buffer
- * before transferring. The header contains the length of the packet and
- * the type. The firmware handles the packets based upon this set type.
- */
-static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
- u8 type, struct sk_buff *skb,
- struct mwifiex_tx_param *tx_param)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret;
- u32 buf_block_len;
- u32 blk_size;
- u32 port = CTRL_PORT;
- u8 *payload = (u8 *)skb->data;
- u32 pkt_len = skb->len;
-
- /* Allocate buffer and copy payload */
- blk_size = MWIFIEX_SDIO_BLOCK_SIZE;
- buf_block_len = (pkt_len + blk_size - 1) / blk_size;
- *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len);
- *(__le16 *)&payload[2] = cpu_to_le16(type);
-
- /*
- * This is SDIO specific header
- * u16 length,
- * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1,
- * MWIFIEX_TYPE_EVENT = 3)
- */
- if (type == MWIFIEX_TYPE_DATA) {
- ret = mwifiex_get_wr_port_data(adapter, &port);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "%s: no wr_port available\n",
- __func__);
- return ret;
- }
- } else {
- adapter->cmd_sent = true;
- /* Type must be MWIFIEX_TYPE_CMD */
-
- if (pkt_len <= INTF_HEADER_LEN ||
- pkt_len > MWIFIEX_UPLD_SIZE)
- mwifiex_dbg(adapter, ERROR,
- "%s: payload=%p, nb=%d\n",
- __func__, payload, pkt_len);
-
- if (card->supports_sdio_new_mode)
- port = CMD_PORT_SLCT;
- }
-
- /* Transfer data to card */
- pkt_len = buf_block_len * blk_size;
-
- if (tx_param)
- ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len,
- port, tx_param->next_pkt_len
- );
- else
- ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len,
- port, 0);
-
- if (ret) {
- if (type == MWIFIEX_TYPE_CMD)
- adapter->cmd_sent = false;
- if (type == MWIFIEX_TYPE_DATA) {
- adapter->data_sent = false;
- /* restore curr_wr_port in error cases */
- card->curr_wr_port = port;
- card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port);
- }
- } else {
- if (type == MWIFIEX_TYPE_DATA) {
- if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port)))
- adapter->data_sent = true;
- else
- adapter->data_sent = false;
- }
- }
-
- return ret;
-}
-
-/*
- * This function allocates the MPA Tx and Rx buffers.
- */
-static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter,
- u32 mpa_tx_buf_size, u32 mpa_rx_buf_size)
-{
- struct sdio_mmc_card *card = adapter->card;
- u32 rx_buf_size;
- int ret = 0;
-
- card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL);
- if (!card->mpa_tx.buf) {
- ret = -1;
- goto error;
- }
-
- card->mpa_tx.buf_size = mpa_tx_buf_size;
-
- rx_buf_size = max_t(u32, mpa_rx_buf_size,
- (u32)SDIO_MAX_AGGR_BUF_SIZE);
- card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL);
- if (!card->mpa_rx.buf) {
- ret = -1;
- goto error;
- }
-
- card->mpa_rx.buf_size = rx_buf_size;
-
-error:
- if (ret) {
- kfree(card->mpa_tx.buf);
- kfree(card->mpa_rx.buf);
- card->mpa_tx.buf_size = 0;
- card->mpa_rx.buf_size = 0;
- }
-
- return ret;
-}
-
-/*
- * This function unregisters the SDIO device.
- *
- * The SDIO IRQ is released, the function is disabled and driver
- * data is set to null.
- */
-static void
-mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
-
- if (adapter->card) {
- sdio_claim_host(card->func);
- sdio_disable_func(card->func);
- sdio_release_host(card->func);
- }
-}
-
-/*
- * This function registers the SDIO device.
- *
- * SDIO IRQ is claimed, block size is set and driver data is initialized.
- */
-static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
-{
- int ret;
- struct sdio_mmc_card *card = adapter->card;
- struct sdio_func *func = card->func;
-
- /* save adapter pointer in card */
- card->adapter = adapter;
- adapter->tx_buf_size = card->tx_buf_size;
-
- sdio_claim_host(func);
-
- /* Set block size */
- ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE);
- sdio_release_host(func);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "cannot set SDIO block size\n");
- return ret;
- }
-
-
- adapter->dev = &func->dev;
-
- strcpy(adapter->fw_name, card->firmware);
- if (card->fw_dump_enh) {
- adapter->mem_type_mapping_tbl = generic_mem_type_map;
- adapter->num_mem_types = 1;
- } else {
- adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
- adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
- }
-
- return 0;
-}
-
-/*
- * This function initializes the SDIO driver.
- *
- * The following initializations steps are followed -
- * - Read the Host interrupt status register to acknowledge
- * the first interrupt got from bootloader
- * - Disable host interrupt mask register
- * - Get SDIO port
- * - Initialize SDIO variables in card
- * - Allocate MP registers
- * - Allocate MPA Tx and Rx buffers
- */
-static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- const struct mwifiex_sdio_card_reg *reg = card->reg;
- int ret;
- u8 sdio_ireg;
-
- sdio_set_drvdata(card->func, card);
-
- /*
- * Read the host_int_status_reg for ACK the first interrupt got
- * from the bootloader. If we don't do this we get a interrupt
- * as soon as we register the irq.
- */
- mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg);
-
- /* Get SDIO ioport */
- mwifiex_init_sdio_ioport(adapter);
-
- /* Initialize SDIO variables in card */
- card->mp_rd_bitmap = 0;
- card->mp_wr_bitmap = 0;
- card->curr_rd_port = reg->start_rd_port;
- card->curr_wr_port = reg->start_wr_port;
-
- card->mp_data_port_mask = reg->data_port_mask;
-
- card->mpa_tx.buf_len = 0;
- card->mpa_tx.pkt_cnt = 0;
- card->mpa_tx.start_port = 0;
-
- card->mpa_tx.enabled = 1;
- card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit;
-
- card->mpa_rx.buf_len = 0;
- card->mpa_rx.pkt_cnt = 0;
- card->mpa_rx.start_port = 0;
-
- card->mpa_rx.enabled = 1;
- card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit;
-
- /* Allocate buffers for SDIO MP-A */
- card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL);
- if (!card->mp_regs)
- return -ENOMEM;
-
- /* Allocate skb pointer buffers */
- card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) *
- card->mp_agg_pkt_limit, GFP_KERNEL);
- card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
- card->mp_agg_pkt_limit, GFP_KERNEL);
- ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
- card->mp_tx_agg_buf_size,
- card->mp_rx_agg_buf_size);
-
- /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */
- if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX ||
- card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) {
- /* Disable rx single port aggregation */
- adapter->host_disable_sdio_rx_aggr = true;
-
- ret = mwifiex_alloc_sdio_mpa_buffers
- (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K,
- MWIFIEX_MP_AGGR_BUF_SIZE_32K);
- if (ret) {
- /* Disable multi port aggregation */
- card->mpa_tx.enabled = 0;
- card->mpa_rx.enabled = 0;
- }
- }
-
- adapter->auto_tdls = card->can_auto_tdls;
- adapter->ext_scan = card->can_ext_scan;
- return 0;
-}
-
-/*
- * This function resets the MPA Tx and Rx buffers.
- */
-static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
-
- MP_TX_AGGR_BUF_RESET(card);
- MP_RX_AGGR_BUF_RESET(card);
-}
-
-/*
- * This function cleans up the allocated card buffers.
- *
- * The following are freed by this function -
- * - MP registers
- * - MPA Tx buffer
- * - MPA Rx buffer
- */
-static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
-
- kfree(card->mp_regs);
- kfree(card->mpa_rx.skb_arr);
- kfree(card->mpa_rx.len_arr);
- kfree(card->mpa_tx.buf);
- kfree(card->mpa_rx.buf);
- sdio_set_drvdata(card->func, NULL);
- kfree(card);
-}
-
-/*
- * This function updates the MP end port in card.
- */
-static void
-mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
-{
- struct sdio_mmc_card *card = adapter->card;
- const struct mwifiex_sdio_card_reg *reg = card->reg;
- int i;
-
- card->mp_end_port = port;
-
- card->mp_data_port_mask = reg->data_port_mask;
-
- if (reg->start_wr_port) {
- for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
- card->mp_data_port_mask &=
- ~(1 << (card->max_ports - i));
- }
-
- card->curr_wr_port = reg->start_wr_port;
-
- mwifiex_dbg(adapter, CMD,
- "cmd: mp_end_port %d, data port mask 0x%x\n",
- port, card->mp_data_port_mask);
-}
-
-static void mwifiex_recreate_adapter(struct sdio_mmc_card *card)
-{
- struct sdio_func *func = card->func;
- const struct sdio_device_id *device_id = card->device_id;
-
- /* TODO mmc_hw_reset does not require destroying and re-probing the
- * whole adapter. Hence there was no need to for this rube-goldberg
- * design to reload the fw from an external workqueue. If we don't
- * destroy the adapter we could reload the fw from
- * mwifiex_main_work_queue directly.
- * The real difficulty with fw reset is to restore all the user
- * settings applied through ioctl. By destroying and recreating the
- * adapter, we take the easy way out, since we rely on user space to
- * restore them. We assume that user space will treat the new
- * incarnation of the adapter(interfaces) as if they had been just
- * discovered and initializes them from scratch.
- */
-
- mwifiex_sdio_remove(func);
-
- /* power cycle the adapter */
- sdio_claim_host(func);
- mmc_hw_reset(func->card->host);
- sdio_release_host(func);
-
- mwifiex_sdio_probe(func, device_id);
-}
-
-static struct mwifiex_adapter *save_adapter;
-static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
-
- /* TODO card pointer is unprotected. If the adapter is removed
- * physically, sdio core might trigger mwifiex_sdio_remove, before this
- * workqueue is run, which will destroy the adapter struct. When this
- * workqueue eventually exceutes it will dereference an invalid adapter
- * pointer
- */
- mwifiex_recreate_adapter(card);
-}
-
-/* This function read/write firmware */
-static enum
-rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter,
- u8 doneflag)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret, tries;
- u8 ctrl_data = 0;
-
- sdio_writeb(card->func, card->reg->fw_dump_host_ready,
- card->reg->fw_dump_ctrl, &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n");
- return RDWR_STATUS_FAILURE;
- }
- for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
- &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "SDIO read err\n");
- return RDWR_STATUS_FAILURE;
- }
- if (ctrl_data == FW_DUMP_DONE)
- break;
- if (doneflag && ctrl_data == doneflag)
- return RDWR_STATUS_DONE;
- if (ctrl_data != card->reg->fw_dump_host_ready) {
- mwifiex_dbg(adapter, WARN,
- "The ctrl reg was changed, re-try again\n");
- sdio_writeb(card->func, card->reg->fw_dump_host_ready,
- card->reg->fw_dump_ctrl, &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "SDIO write err\n");
- return RDWR_STATUS_FAILURE;
- }
- }
- usleep_range(100, 200);
- }
- if (ctrl_data == card->reg->fw_dump_host_ready) {
- mwifiex_dbg(adapter, ERROR,
- "Fail to pull ctrl_data\n");
- return RDWR_STATUS_FAILURE;
- }
-
- return RDWR_STATUS_SUCCESS;
-}
-
-/* This function dump firmware memory to file */
-static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- int ret = 0;
- unsigned int reg, reg_start, reg_end;
- u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
- enum rdwr_status stat;
- u32 memory_size;
-
- if (!card->can_dump_fw)
- return;
-
- for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
- struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
-
- if (entry->mem_ptr) {
- vfree(entry->mem_ptr);
- entry->mem_ptr = NULL;
- }
- entry->mem_size = 0;
- }
-
- mwifiex_pm_wakeup_card(adapter);
- sdio_claim_host(card->func);
-
- mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n");
-
- stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
- if (stat == RDWR_STATUS_FAILURE)
- goto done;
-
- reg = card->reg->fw_dump_start;
- /* Read the number of the memories which will dump */
- dump_num = sdio_readb(card->func, reg, &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n");
- goto done;
- }
-
- /* Read the length of every memory which will dump */
- for (idx = 0; idx < dump_num; idx++) {
- struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
-
- stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
- if (stat == RDWR_STATUS_FAILURE)
- goto done;
-
- memory_size = 0;
- reg = card->reg->fw_dump_start;
- for (i = 0; i < 4; i++) {
- read_reg = sdio_readb(card->func, reg, &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "SDIO read err\n");
- goto done;
- }
- memory_size |= (read_reg << i*8);
- reg++;
- }
-
- if (memory_size == 0) {
- mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n");
- ret = mwifiex_write_reg(adapter,
- card->reg->fw_dump_ctrl,
- FW_DUMP_READ_DONE);
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "SDIO write err\n");
- return;
- }
- break;
- }
-
- mwifiex_dbg(adapter, DUMP,
- "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
- entry->mem_ptr = vmalloc(memory_size + 1);
- entry->mem_size = memory_size;
- if (!entry->mem_ptr) {
- mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n",
- entry->mem_name);
- goto done;
- }
- dbg_ptr = entry->mem_ptr;
- end_ptr = dbg_ptr + memory_size;
-
- doneflag = entry->done_flag;
- mwifiex_dbg(adapter, DUMP,
- "Start %s output, please wait...\n",
- entry->mem_name);
-
- do {
- stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
- if (stat == RDWR_STATUS_FAILURE)
- goto done;
-
- reg_start = card->reg->fw_dump_start;
- reg_end = card->reg->fw_dump_end;
- for (reg = reg_start; reg <= reg_end; reg++) {
- *dbg_ptr = sdio_readb(card->func, reg, &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "SDIO read err\n");
- goto done;
- }
- if (dbg_ptr < end_ptr)
- dbg_ptr++;
- else
- mwifiex_dbg(adapter, ERROR,
- "Allocated buf not enough\n");
- }
-
- if (stat != RDWR_STATUS_DONE)
- continue;
-
- mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n",
- entry->mem_name, dbg_ptr - entry->mem_ptr);
- break;
- } while (1);
- }
- mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n");
-
-done:
- sdio_release_host(card->func);
-}
-
-static void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- struct memory_type_mapping *entry = &generic_mem_type_map[0];
- unsigned int reg, reg_start, reg_end;
- u8 start_flag = 0, done_flag = 0;
- u8 *dbg_ptr, *end_ptr;
- enum rdwr_status stat;
- int ret = -1, tries;
-
- if (!card->fw_dump_enh)
- return;
-
- if (entry->mem_ptr) {
- vfree(entry->mem_ptr);
- entry->mem_ptr = NULL;
- }
- entry->mem_size = 0;
-
- mwifiex_pm_wakeup_card(adapter);
- sdio_claim_host(card->func);
-
- mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n");
-
- stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag);
- if (stat == RDWR_STATUS_FAILURE)
- goto done;
-
- reg_start = card->reg->fw_dump_start;
- reg_end = card->reg->fw_dump_end;
- for (reg = reg_start; reg <= reg_end; reg++) {
- for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- start_flag = sdio_readb(card->func, reg, &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "SDIO read err\n");
- goto done;
- }
- if (start_flag == 0)
- break;
- if (tries == MAX_POLL_TRIES) {
- mwifiex_dbg(adapter, ERROR,
- "FW not ready to dump\n");
- ret = -1;
- goto done;
- }
- }
- usleep_range(100, 200);
- }
-
- entry->mem_ptr = vmalloc(0xf0000 + 1);
- if (!entry->mem_ptr) {
- ret = -1;
- goto done;
- }
- dbg_ptr = entry->mem_ptr;
- entry->mem_size = 0xf0000;
- end_ptr = dbg_ptr + entry->mem_size;
-
- done_flag = entry->done_flag;
- mwifiex_dbg(adapter, DUMP,
- "Start %s output, please wait...\n", entry->mem_name);
-
- while (true) {
- stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag);
- if (stat == RDWR_STATUS_FAILURE)
- goto done;
- for (reg = reg_start; reg <= reg_end; reg++) {
- *dbg_ptr = sdio_readb(card->func, reg, &ret);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "SDIO read err\n");
- goto done;
- }
- dbg_ptr++;
- if (dbg_ptr >= end_ptr) {
- u8 *tmp_ptr;
-
- tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1);
- if (!tmp_ptr)
- goto done;
-
- memcpy(tmp_ptr, entry->mem_ptr,
- entry->mem_size);
- vfree(entry->mem_ptr);
- entry->mem_ptr = tmp_ptr;
- tmp_ptr = NULL;
- dbg_ptr = entry->mem_ptr + entry->mem_size;
- entry->mem_size += 0x4000;
- end_ptr = entry->mem_ptr + entry->mem_size;
- }
- }
- if (stat == RDWR_STATUS_DONE) {
- entry->mem_size = dbg_ptr - entry->mem_ptr;
- mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n",
- entry->mem_name, entry->mem_size);
- ret = 0;
- break;
- }
- }
- mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n");
-
-done:
- if (ret) {
- mwifiex_dbg(adapter, ERROR, "firmware dump failed\n");
- if (entry->mem_ptr) {
- vfree(entry->mem_ptr);
- entry->mem_ptr = NULL;
- }
- entry->mem_size = 0;
- }
- sdio_release_host(card->func);
-}
-
-static void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
-
- mwifiex_drv_info_dump(adapter);
- if (card->fw_dump_enh)
- mwifiex_sdio_generic_fw_dump(adapter);
- else
- mwifiex_sdio_fw_dump(adapter);
- mwifiex_upload_device_dump(adapter);
-}
-
-static void mwifiex_sdio_work(struct work_struct *work)
-{
- if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP,
- &iface_work_flags))
- mwifiex_sdio_device_dump_work(save_adapter);
- if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
- &iface_work_flags))
- mwifiex_sdio_card_reset_work(save_adapter);
-}
-
-static DECLARE_WORK(sdio_work, mwifiex_sdio_work);
-/* This function resets the card */
-static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter)
-{
- save_adapter = adapter;
- if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags))
- return;
-
- set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags);
-
- schedule_work(&sdio_work);
-}
-
-/* This function dumps FW information */
-static void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter)
-{
- save_adapter = adapter;
- if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags))
- return;
-
- set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags);
- schedule_work(&sdio_work);
-}
-
-/* Function to dump SDIO function registers and SDIO scratch registers in case
- * of FW crash
- */
-static int
-mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf)
-{
- char *p = drv_buf;
- struct sdio_mmc_card *cardp = adapter->card;
- int ret = 0;
- u8 count, func, data, index = 0, size = 0;
- u8 reg, reg_start, reg_end;
- char buf[256], *ptr;
-
- if (!p)
- return 0;
-
- mwifiex_dbg(adapter, MSG, "SDIO register dump start\n");
-
- mwifiex_pm_wakeup_card(adapter);
-
- sdio_claim_host(cardp->func);
-
- for (count = 0; count < 5; count++) {
- memset(buf, 0, sizeof(buf));
- ptr = buf;
-
- switch (count) {
- case 0:
- /* Read the registers of SDIO function0 */
- func = count;
- reg_start = 0;
- reg_end = 9;
- break;
- case 1:
- /* Read the registers of SDIO function1 */
- func = count;
- reg_start = cardp->reg->func1_dump_reg_start;
- reg_end = cardp->reg->func1_dump_reg_end;
- break;
- case 2:
- index = 0;
- func = 1;
- reg_start = cardp->reg->func1_spec_reg_table[index++];
- size = cardp->reg->func1_spec_reg_num;
- reg_end = cardp->reg->func1_spec_reg_table[size-1];
- break;
- default:
- /* Read the scratch registers of SDIO function1 */
- if (count == 4)
- mdelay(100);
- func = 1;
- reg_start = cardp->reg->func1_scratch_reg;
- reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE;
- }
-
- if (count != 2)
- ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
- func, reg_start, reg_end);
- else
- ptr += sprintf(ptr, "SDIO Func%d: ", func);
-
- for (reg = reg_start; reg <= reg_end;) {
- if (func == 0)
- data = sdio_f0_readb(cardp->func, reg, &ret);
- else
- data = sdio_readb(cardp->func, reg, &ret);
-
- if (count == 2)
- ptr += sprintf(ptr, "(%#x) ", reg);
- if (!ret) {
- ptr += sprintf(ptr, "%02x ", data);
- } else {
- ptr += sprintf(ptr, "ERR");
- break;
- }
-
- if (count == 2 && reg < reg_end)
- reg = cardp->reg->func1_spec_reg_table[index++];
- else
- reg++;
- }
-
- mwifiex_dbg(adapter, MSG, "%s\n", buf);
- p += sprintf(p, "%s\n", buf);
- }
-
- sdio_release_host(cardp->func);
-
- mwifiex_dbg(adapter, MSG, "SDIO register dump end\n");
-
- return p - drv_buf;
-}
-
-static struct mwifiex_if_ops sdio_ops = {
- .init_if = mwifiex_init_sdio,
- .cleanup_if = mwifiex_cleanup_sdio,
- .check_fw_status = mwifiex_check_fw_status,
- .prog_fw = mwifiex_prog_fw_w_helper,
- .register_dev = mwifiex_register_dev,
- .unregister_dev = mwifiex_unregister_dev,
- .enable_int = mwifiex_sdio_enable_host_int,
- .disable_int = mwifiex_sdio_disable_host_int,
- .process_int_status = mwifiex_process_int_status,
- .host_to_card = mwifiex_sdio_host_to_card,
- .wakeup = mwifiex_pm_wakeup_card,
- .wakeup_complete = mwifiex_pm_wakeup_card_complete,
-
- /* SDIO specific */
- .update_mp_end_port = mwifiex_update_mp_end_port,
- .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf,
- .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
- .event_complete = mwifiex_sdio_event_complete,
- .card_reset = mwifiex_sdio_card_reset,
- .reg_dump = mwifiex_sdio_reg_dump,
- .device_dump = mwifiex_sdio_device_dump,
- .deaggr_pkt = mwifiex_deaggr_sdio_pkt,
-};
-
-/*
- * This function initializes the SDIO driver.
- *
- * This initiates the semaphore and registers the device with
- * SDIO bus.
- */
-static int
-mwifiex_sdio_init_module(void)
-{
- sema_init(&add_remove_card_sem, 1);
-
- /* Clear the flag in case user removes the card. */
- user_rmmod = 0;
-
- return sdio_register_driver(&mwifiex_sdio);
-}
-
-/*
- * This function cleans up the SDIO driver.
- *
- * The following major steps are followed for cleanup -
- * - Resume the device if its suspended
- * - Disconnect the device if connected
- * - Shutdown the firmware
- * - Unregister the device from SDIO bus.
- */
-static void
-mwifiex_sdio_cleanup_module(void)
-{
- if (!down_interruptible(&add_remove_card_sem))
- up(&add_remove_card_sem);
-
- /* Set the flag as user is removing this module. */
- user_rmmod = 1;
- cancel_work_sync(&sdio_work);
-
- sdio_unregister_driver(&mwifiex_sdio);
-}
-
-module_init(mwifiex_sdio_init_module);
-module_exit(mwifiex_sdio_cleanup_module);
-
-MODULE_AUTHOR("Marvell International Ltd.");
-MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION);
-MODULE_VERSION(SDIO_VERSION);
-MODULE_LICENSE("GPL v2");
-MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
deleted file mode 100644
index a6c8a4f7bfe9..000000000000
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ /dev/null
@@ -1,1421 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: functions for station ioctl
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n.h"
-#include "cfg80211.h"
-
-static int disconnect_on_suspend;
-module_param(disconnect_on_suspend, int, 0644);
-
-/*
- * Copies the multicast address list from device to driver.
- *
- * This function does not validate the destination memory for
- * size, and the calling function must ensure enough memory is
- * available.
- */
-int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
- struct net_device *dev)
-{
- int i = 0;
- struct netdev_hw_addr *ha;
-
- netdev_for_each_mc_addr(ha, dev)
- memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
-
- return i;
-}
-
-/*
- * Wait queue completion handler.
- *
- * This function waits on a cmd wait queue. It also cancels the pending
- * request after waking up, in case of errors.
- */
-int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
- struct cmd_ctrl_node *cmd_queued)
-{
- int status;
-
- /* Wait for completion */
- status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
- *(cmd_queued->condition),
- (12 * HZ));
- if (status <= 0) {
- if (status == 0)
- status = -ETIMEDOUT;
- mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
- status);
- mwifiex_cancel_all_pending_cmd(adapter);
- return status;
- }
-
- status = adapter->cmd_wait_q.status;
- adapter->cmd_wait_q.status = 0;
-
- return status;
-}
-
-/*
- * This function prepares the correct firmware command and
- * issues it to set the multicast list.
- *
- * This function can be used to enable promiscuous mode, or enable all
- * multicast packets, or to enable selective multicast.
- */
-int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
- struct mwifiex_multicast_list *mcast_list)
-{
- int ret = 0;
- u16 old_pkt_filter;
-
- old_pkt_filter = priv->curr_pkt_filter;
-
- if (mcast_list->mode == MWIFIEX_PROMISC_MODE) {
- mwifiex_dbg(priv->adapter, INFO,
- "info: Enable Promiscuous mode\n");
- priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
- priv->curr_pkt_filter &=
- ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
- } else {
- /* Multicast */
- priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
- if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) {
- mwifiex_dbg(priv->adapter, INFO,
- "info: Enabling All Multicast!\n");
- priv->curr_pkt_filter |=
- HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
- } else {
- priv->curr_pkt_filter &=
- ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
- mwifiex_dbg(priv->adapter, INFO,
- "info: Set multicast list=%d\n",
- mcast_list->num_multicast_addr);
- /* Send multicast addresses to firmware */
- ret = mwifiex_send_cmd(priv,
- HostCmd_CMD_MAC_MULTICAST_ADR,
- HostCmd_ACT_GEN_SET, 0,
- mcast_list, false);
- }
- }
- mwifiex_dbg(priv->adapter, INFO,
- "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
- old_pkt_filter, priv->curr_pkt_filter);
- if (old_pkt_filter != priv->curr_pkt_filter) {
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET,
- 0, &priv->curr_pkt_filter, false);
- }
-
- return ret;
-}
-
-/*
- * This function fills bss descriptor structure using provided
- * information.
- * beacon_ie buffer is allocated in this function. It is caller's
- * responsibility to free the memory.
- */
-int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
- struct cfg80211_bss *bss,
- struct mwifiex_bssdescriptor *bss_desc)
-{
- u8 *beacon_ie;
- size_t beacon_ie_len;
- struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
- const struct cfg80211_bss_ies *ies;
-
- rcu_read_lock();
- ies = rcu_dereference(bss->ies);
- beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
- beacon_ie_len = ies->len;
- bss_desc->timestamp = ies->tsf;
- rcu_read_unlock();
-
- if (!beacon_ie) {
- mwifiex_dbg(priv->adapter, ERROR,
- " failed to alloc beacon_ie\n");
- return -ENOMEM;
- }
-
- memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
- bss_desc->rssi = bss->signal;
- /* The caller of this function will free beacon_ie */
- bss_desc->beacon_buf = beacon_ie;
- bss_desc->beacon_buf_size = beacon_ie_len;
- bss_desc->beacon_period = bss->beacon_interval;
- bss_desc->cap_info_bitmap = bss->capability;
- bss_desc->bss_band = bss_priv->band;
- bss_desc->fw_tsf = bss_priv->fw_tsf;
- if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
- mwifiex_dbg(priv->adapter, INFO,
- "info: InterpretIE: AP WEP enabled\n");
- bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
- } else {
- bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
- }
- if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS)
- bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
- else
- bss_desc->bss_mode = NL80211_IFTYPE_STATION;
-
- /* Disable 11ac by default. Enable it only where there
- * exist VHT_CAP IE in AP beacon
- */
- 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);
-}
-
-void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
-{
- if (priv->adapter->dt_node) {
- char txpwr[] = {"marvell,00_txpwrlimit"};
-
- memcpy(&txpwr[8], priv->adapter->country_code, 2);
- mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr);
- }
-}
-
-static int mwifiex_process_country_ie(struct mwifiex_private *priv,
- struct cfg80211_bss *bss)
-{
- const u8 *country_ie;
- u8 country_ie_len;
- struct mwifiex_802_11d_domain_reg *domain_info =
- &priv->adapter->domain_reg;
-
- rcu_read_lock();
- country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
- if (!country_ie) {
- rcu_read_unlock();
- return 0;
- }
-
- country_ie_len = country_ie[1];
- if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
- rcu_read_unlock();
- return 0;
- }
-
- if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
- rcu_read_unlock();
- mwifiex_dbg(priv->adapter, INFO,
- "11D: skip setting domain info in FW\n");
- return 0;
- }
- memcpy(priv->adapter->country_code, &country_ie[2], 2);
-
- domain_info->country_code[0] = country_ie[2];
- domain_info->country_code[1] = country_ie[3];
- domain_info->country_code[2] = ' ';
-
- country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
-
- domain_info->no_of_triplet =
- country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
-
- memcpy((u8 *)domain_info->triplet,
- &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
-
- rcu_read_unlock();
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
- HostCmd_ACT_GEN_SET, 0, NULL, false)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "11D: setting domain info in FW fail\n");
- return -1;
- }
-
- mwifiex_dnld_txpwr_table(priv);
-
- return 0;
-}
-
-/*
- * In Ad-Hoc mode, the IBSS is created if not found in scan list.
- * In both Ad-Hoc and infra mode, an deauthentication is performed
- * first.
- */
-int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
- struct cfg80211_ssid *req_ssid)
-{
- int ret;
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bssdescriptor *bss_desc = NULL;
-
- priv->scan_block = false;
-
- if (bss) {
- mwifiex_process_country_ie(priv, bss);
-
- /* Allocate and fill new bss descriptor */
- bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
- GFP_KERNEL);
- if (!bss_desc)
- return -ENOMEM;
-
- ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
- if (ret)
- goto done;
- }
-
- if (priv->bss_mode == NL80211_IFTYPE_STATION ||
- priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
- u8 config_bands;
-
- if (!bss_desc)
- return -1;
-
- if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
- HostCmd_SCAN_RADIO_TYPE_BG) {
- config_bands = BAND_B | BAND_G | BAND_GN;
- } else {
- config_bands = BAND_A | BAND_AN;
- if (adapter->fw_bands & BAND_AAC)
- config_bands |= BAND_AAC;
- }
-
- 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) {
- mwifiex_dbg(adapter, ERROR,
- "Attempt to reconnect on csa closed chan(%d)\n",
- bss_desc->channel);
- goto done;
- }
-
- mwifiex_dbg(adapter, INFO,
- "info: SSID found in scan list ...\t"
- "associating...\n");
-
- mwifiex_stop_net_dev_queue(priv->netdev, adapter);
- if (netif_carrier_ok(priv->netdev))
- netif_carrier_off(priv->netdev);
-
- /* Clear any past association response stored for
- * application retrieval */
- priv->assoc_rsp_size = 0;
- ret = mwifiex_associate(priv, bss_desc);
-
- /* If auth type is auto and association fails using open mode,
- * try to connect using shared mode */
- if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
- priv->sec_info.is_authtype_auto &&
- priv->sec_info.wep_enabled) {
- priv->sec_info.authentication_mode =
- NL80211_AUTHTYPE_SHARED_KEY;
- ret = mwifiex_associate(priv, bss_desc);
- }
-
- if (bss)
- cfg80211_put_bss(priv->adapter->wiphy, bss);
- } else {
- /* Adhoc mode */
- /* If the requested SSID matches current SSID, return */
- if (bss_desc && bss_desc->ssid.ssid_len &&
- (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor.
- ssid, &bss_desc->ssid))) {
- ret = 0;
- goto done;
- }
-
- priv->adhoc_is_link_sensed = false;
-
- ret = mwifiex_check_network_compatibility(priv, bss_desc);
-
- mwifiex_stop_net_dev_queue(priv->netdev, adapter);
- if (netif_carrier_ok(priv->netdev))
- netif_carrier_off(priv->netdev);
-
- if (!ret) {
- mwifiex_dbg(adapter, INFO,
- "info: network found in scan\t"
- " list. Joining...\n");
- ret = mwifiex_adhoc_join(priv, bss_desc);
- if (bss)
- cfg80211_put_bss(priv->adapter->wiphy, bss);
- } else {
- mwifiex_dbg(adapter, INFO,
- "info: Network not found in\t"
- "the list, creating adhoc with ssid = %s\n",
- req_ssid->ssid);
- ret = mwifiex_adhoc_start(priv, req_ssid);
- }
- }
-
-done:
- /* beacon_ie buffer was allocated in function
- * mwifiex_fill_new_bss_desc(). Free it now.
- */
- if (bss_desc)
- kfree(bss_desc->beacon_buf);
- kfree(bss_desc);
- return ret;
-}
-
-/*
- * IOCTL request handler to set host sleep configuration.
- *
- * This function prepares the correct firmware command and
- * issues it.
- */
-int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
- int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
-
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- int status = 0;
- u32 prev_cond = 0;
-
- if (!hs_cfg)
- return -ENOMEM;
-
- switch (action) {
- case HostCmd_ACT_GEN_SET:
- if (adapter->pps_uapsd_mode) {
- mwifiex_dbg(adapter, INFO,
- "info: Host Sleep IOCTL\t"
- "is blocked in UAPSD/PPS mode\n");
- status = -1;
- break;
- }
- if (hs_cfg->is_invoke_hostcmd) {
- if (hs_cfg->conditions == HS_CFG_CANCEL) {
- if (!adapter->is_hs_configured)
- /* Already cancelled */
- break;
- /* Save previous condition */
- prev_cond = le32_to_cpu(adapter->hs_cfg
- .conditions);
- adapter->hs_cfg.conditions =
- cpu_to_le32(hs_cfg->conditions);
- } else if (hs_cfg->conditions) {
- adapter->hs_cfg.conditions =
- cpu_to_le32(hs_cfg->conditions);
- adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
- if (hs_cfg->gap)
- adapter->hs_cfg.gap = (u8)hs_cfg->gap;
- } else if (adapter->hs_cfg.conditions ==
- cpu_to_le32(HS_CFG_CANCEL)) {
- /* Return failure if no parameters for HS
- enable */
- status = -1;
- break;
- }
-
- status = mwifiex_send_cmd(priv,
- HostCmd_CMD_802_11_HS_CFG_ENH,
- HostCmd_ACT_GEN_SET, 0,
- &adapter->hs_cfg,
- cmd_type == MWIFIEX_SYNC_CMD);
-
- if (hs_cfg->conditions == HS_CFG_CANCEL)
- /* Restore previous condition */
- adapter->hs_cfg.conditions =
- cpu_to_le32(prev_cond);
- } else {
- adapter->hs_cfg.conditions =
- cpu_to_le32(hs_cfg->conditions);
- adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
- adapter->hs_cfg.gap = (u8)hs_cfg->gap;
- }
- break;
- case HostCmd_ACT_GEN_GET:
- hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
- hs_cfg->gpio = adapter->hs_cfg.gpio;
- hs_cfg->gap = adapter->hs_cfg.gap;
- break;
- default:
- status = -1;
- break;
- }
-
- return status;
-}
-
-/*
- * Sends IOCTL request to cancel the existing Host Sleep configuration.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type)
-{
- struct mwifiex_ds_hs_cfg hscfg;
-
- hscfg.conditions = HS_CFG_CANCEL;
- hscfg.is_invoke_hostcmd = true;
-
- return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
- cmd_type, &hscfg);
-}
-EXPORT_SYMBOL_GPL(mwifiex_cancel_hs);
-
-/*
- * Sends IOCTL request to cancel the existing Host Sleep configuration.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
-{
- struct mwifiex_ds_hs_cfg hscfg;
- struct mwifiex_private *priv;
- int i;
-
- if (disconnect_on_suspend) {
- for (i = 0; i < adapter->priv_num; i++) {
- priv = adapter->priv[i];
- if (priv)
- mwifiex_deauthenticate(priv, NULL);
- }
- }
-
- if (adapter->hs_activated) {
- mwifiex_dbg(adapter, CMD,
- "cmd: HS Already activated\n");
- return true;
- }
-
- adapter->hs_activate_wait_q_woken = false;
-
- memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
- hscfg.is_invoke_hostcmd = true;
-
- adapter->hs_enabling = true;
- mwifiex_cancel_all_pending_cmd(adapter);
-
- if (mwifiex_set_hs_params(mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_STA),
- HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD,
- &hscfg)) {
- mwifiex_dbg(adapter, ERROR,
- "IOCTL request HS enable failed\n");
- return false;
- }
-
- if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
- adapter->hs_activate_wait_q_woken,
- (10 * HZ)) <= 0) {
- mwifiex_dbg(adapter, ERROR,
- "hs_activate_wait_q terminated\n");
- return false;
- }
-
- return true;
-}
-EXPORT_SYMBOL_GPL(mwifiex_enable_hs);
-
-/*
- * IOCTL request handler to get BSS information.
- *
- * This function collates the information from different driver structures
- * to send to the user.
- */
-int mwifiex_get_bss_info(struct mwifiex_private *priv,
- struct mwifiex_bss_info *info)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_bssdescriptor *bss_desc;
-
- if (!info)
- return -1;
-
- bss_desc = &priv->curr_bss_params.bss_descriptor;
-
- info->bss_mode = priv->bss_mode;
-
- memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
-
- memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
-
- info->bss_chan = bss_desc->channel;
-
- memcpy(info->country_code, adapter->country_code,
- IEEE80211_COUNTRY_STRING_LEN);
-
- info->media_connected = priv->media_connected;
-
- info->max_power_level = priv->max_tx_power_level;
- info->min_power_level = priv->min_tx_power_level;
-
- info->adhoc_state = priv->adhoc_state;
-
- info->bcn_nf_last = priv->bcn_nf_last;
-
- if (priv->sec_info.wep_enabled)
- info->wep_status = true;
- else
- info->wep_status = false;
-
- info->is_hs_configured = adapter->is_hs_configured;
- info->is_deep_sleep = adapter->is_deep_sleep;
-
- return 0;
-}
-
-/*
- * The function disables auto deep sleep mode.
- */
-int mwifiex_disable_auto_ds(struct mwifiex_private *priv)
-{
- struct mwifiex_ds_auto_ds auto_ds;
-
- auto_ds.auto_ds = DEEP_SLEEP_OFF;
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
- DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
-}
-EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds);
-
-/*
- * Sends IOCTL request to get the data rate.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate)
-{
- int ret;
-
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
- HostCmd_ACT_GEN_GET, 0, NULL, true);
-
- if (!ret) {
- if (priv->is_data_rate_auto)
- *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate,
- priv->tx_htinfo);
- else
- *rate = priv->data_rate;
- }
-
- return ret;
-}
-
-/*
- * IOCTL request handler to set tx power configuration.
- *
- * This function prepares the correct firmware command and
- * issues it.
- *
- * For non-auto power mode, all the following power groups are set -
- * - Modulation class HR/DSSS
- * - Modulation class OFDM
- * - Modulation class HTBW20
- * - Modulation class HTBW40
- */
-int mwifiex_set_tx_power(struct mwifiex_private *priv,
- struct mwifiex_power_cfg *power_cfg)
-{
- int ret;
- struct host_cmd_ds_txpwr_cfg *txp_cfg;
- struct mwifiex_types_power_group *pg_tlv;
- struct mwifiex_power_group *pg;
- u8 *buf;
- u16 dbm = 0;
-
- if (!power_cfg->is_power_auto) {
- dbm = (u16) power_cfg->power_level;
- if ((dbm < priv->min_tx_power_level) ||
- (dbm > priv->max_tx_power_level)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "txpower value %d dBm\t"
- "is out of range (%d dBm-%d dBm)\n",
- dbm, priv->min_tx_power_level,
- priv->max_tx_power_level);
- return -1;
- }
- }
- buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf;
- txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
- if (!power_cfg->is_power_auto) {
- txp_cfg->mode = cpu_to_le32(1);
- pg_tlv = (struct mwifiex_types_power_group *)
- (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
- pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
- pg_tlv->length =
- cpu_to_le16(4 * sizeof(struct mwifiex_power_group));
- pg = (struct mwifiex_power_group *)
- (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
- + sizeof(struct mwifiex_types_power_group));
- /* Power group for modulation class HR/DSSS */
- pg->first_rate_code = 0x00;
- pg->last_rate_code = 0x03;
- pg->modulation_class = MOD_CLASS_HR_DSSS;
- pg->power_step = 0;
- pg->power_min = (s8) dbm;
- pg->power_max = (s8) dbm;
- pg++;
- /* Power group for modulation class OFDM */
- pg->first_rate_code = 0x00;
- pg->last_rate_code = 0x07;
- pg->modulation_class = MOD_CLASS_OFDM;
- pg->power_step = 0;
- pg->power_min = (s8) dbm;
- pg->power_max = (s8) dbm;
- pg++;
- /* Power group for modulation class HTBW20 */
- pg->first_rate_code = 0x00;
- pg->last_rate_code = 0x20;
- pg->modulation_class = MOD_CLASS_HT;
- pg->power_step = 0;
- pg->power_min = (s8) dbm;
- pg->power_max = (s8) dbm;
- pg->ht_bandwidth = HT_BW_20;
- pg++;
- /* Power group for modulation class HTBW40 */
- pg->first_rate_code = 0x00;
- pg->last_rate_code = 0x20;
- pg->modulation_class = MOD_CLASS_HT;
- pg->power_step = 0;
- pg->power_min = (s8) dbm;
- pg->power_max = (s8) dbm;
- pg->ht_bandwidth = HT_BW_40;
- }
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG,
- HostCmd_ACT_GEN_SET, 0, buf, true);
-
- kfree(buf);
- return ret;
-}
-
-/*
- * IOCTL request handler to get power save mode.
- *
- * This function prepares the correct firmware command and
- * issues it.
- */
-int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode)
-{
- int ret;
- struct mwifiex_adapter *adapter = priv->adapter;
- u16 sub_cmd;
-
- if (*ps_mode)
- adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
- else
- adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
- sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
- sub_cmd, BITMAP_STA_PS, NULL, true);
- if ((!ret) && (sub_cmd == DIS_AUTO_PS))
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
- GET_PS, 0, NULL, false);
-
- return ret;
-}
-
-/*
- * IOCTL request handler to set/reset WPA IE.
- *
- * The supplied WPA IE is treated as a opaque buffer. Only the first field
- * is checked to determine WPA version. If buffer length is zero, the existing
- * WPA IE is reset.
- */
-static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv,
- u8 *ie_data_ptr, u16 ie_len)
-{
- if (ie_len) {
- if (ie_len > sizeof(priv->wpa_ie)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "failed to copy WPA IE, too big\n");
- return -1;
- }
- memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
- priv->wpa_ie_len = (u8) ie_len;
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: Set Wpa_ie_len=%d IE=%#x\n",
- priv->wpa_ie_len, priv->wpa_ie[0]);
-
- if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
- priv->sec_info.wpa_enabled = true;
- } else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
- priv->sec_info.wpa2_enabled = true;
- } else {
- priv->sec_info.wpa_enabled = false;
- priv->sec_info.wpa2_enabled = false;
- }
- } else {
- memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
- priv->wpa_ie_len = 0;
- mwifiex_dbg(priv->adapter, INFO,
- "info: reset wpa_ie_len=%d IE=%#x\n",
- priv->wpa_ie_len, priv->wpa_ie[0]);
- priv->sec_info.wpa_enabled = false;
- priv->sec_info.wpa2_enabled = false;
- }
-
- return 0;
-}
-
-/*
- * IOCTL request handler to set/reset WAPI IE.
- *
- * The supplied WAPI IE is treated as a opaque buffer. Only the first field
- * is checked to internally enable WAPI. If buffer length is zero, the existing
- * WAPI IE is reset.
- */
-static int mwifiex_set_wapi_ie(struct mwifiex_private *priv,
- u8 *ie_data_ptr, u16 ie_len)
-{
- if (ie_len) {
- if (ie_len > sizeof(priv->wapi_ie)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "info: failed to copy WAPI IE, too big\n");
- return -1;
- }
- memcpy(priv->wapi_ie, ie_data_ptr, ie_len);
- priv->wapi_ie_len = ie_len;
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: Set wapi_ie_len=%d IE=%#x\n",
- priv->wapi_ie_len, priv->wapi_ie[0]);
-
- if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY)
- priv->sec_info.wapi_enabled = true;
- } else {
- memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie));
- priv->wapi_ie_len = ie_len;
- mwifiex_dbg(priv->adapter, INFO,
- "info: Reset wapi_ie_len=%d IE=%#x\n",
- priv->wapi_ie_len, priv->wapi_ie[0]);
- priv->sec_info.wapi_enabled = false;
- }
- return 0;
-}
-
-/*
- * IOCTL request handler to set/reset WPS IE.
- *
- * The supplied WPS IE is treated as a opaque buffer. Only the first field
- * is checked to internally enable WPS. If buffer length is zero, the existing
- * WPS IE is reset.
- */
-static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
- u8 *ie_data_ptr, u16 ie_len)
-{
- if (ie_len) {
- if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
- mwifiex_dbg(priv->adapter, ERROR,
- "info: failed to copy WPS IE, too big\n");
- return -1;
- }
-
- priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
- if (!priv->wps_ie)
- return -ENOMEM;
-
- memcpy(priv->wps_ie, ie_data_ptr, ie_len);
- priv->wps_ie_len = ie_len;
- mwifiex_dbg(priv->adapter, CMD,
- "cmd: Set wps_ie_len=%d IE=%#x\n",
- priv->wps_ie_len, priv->wps_ie[0]);
- } else {
- kfree(priv->wps_ie);
- priv->wps_ie_len = ie_len;
- mwifiex_dbg(priv->adapter, INFO,
- "info: Reset wps_ie_len=%d\n", priv->wps_ie_len);
- }
- return 0;
-}
-
-/*
- * IOCTL request handler to set WAPI key.
- *
- * This function prepares the correct firmware command and
- * issues it.
- */
-static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
- struct mwifiex_ds_encrypt_key *encrypt_key)
-{
-
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED,
- encrypt_key, true);
-}
-
-/*
- * IOCTL request handler to set WEP network key.
- *
- * This function prepares the correct firmware command and
- * issues it, after validation checks.
- */
-static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
- struct mwifiex_ds_encrypt_key *encrypt_key)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- int ret;
- struct mwifiex_wep_key *wep_key;
- int index;
-
- if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
- priv->wep_key_curr_index = 0;
- wep_key = &priv->wep_key[priv->wep_key_curr_index];
- index = encrypt_key->key_index;
- if (encrypt_key->key_disable) {
- priv->sec_info.wep_enabled = 0;
- } else if (!encrypt_key->key_len) {
- /* Copy the required key as the current key */
- wep_key = &priv->wep_key[index];
- if (!wep_key->key_length) {
- mwifiex_dbg(adapter, ERROR,
- "key not set, so cannot enable it\n");
- return -1;
- }
-
- if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) {
- memcpy(encrypt_key->key_material,
- wep_key->key_material, wep_key->key_length);
- encrypt_key->key_len = wep_key->key_length;
- }
-
- priv->wep_key_curr_index = (u16) index;
- priv->sec_info.wep_enabled = 1;
- } else {
- wep_key = &priv->wep_key[index];
- memset(wep_key, 0, sizeof(struct mwifiex_wep_key));
- /* Copy the key in the driver */
- memcpy(wep_key->key_material,
- encrypt_key->key_material,
- encrypt_key->key_len);
- wep_key->key_index = index;
- wep_key->key_length = encrypt_key->key_len;
- priv->sec_info.wep_enabled = 1;
- }
- if (wep_key->key_length) {
- void *enc_key;
-
- if (encrypt_key->key_disable) {
- memset(&priv->wep_key[index], 0,
- sizeof(struct mwifiex_wep_key));
- if (wep_key->key_length)
- goto done;
- }
-
- if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
- enc_key = encrypt_key;
- else
- enc_key = NULL;
-
- /* Send request to firmware */
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET, 0, enc_key, false);
- if (ret)
- return ret;
- }
-
-done:
- if (priv->sec_info.wep_enabled)
- priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
- else
- priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
-
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &priv->curr_pkt_filter, true);
-
- return ret;
-}
-
-/*
- * IOCTL request handler to set WPA key.
- *
- * This function prepares the correct firmware command and
- * issues it, after validation checks.
- *
- * Current driver only supports key length of up to 32 bytes.
- *
- * This function can also be used to disable a currently set key.
- */
-static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv,
- struct mwifiex_ds_encrypt_key *encrypt_key)
-{
- int ret;
- u8 remove_key = false;
- struct host_cmd_ds_802_11_key_material *ibss_key;
-
- /* Current driver only supports key length of up to 32 bytes */
- if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
- mwifiex_dbg(priv->adapter, ERROR,
- "key length too long\n");
- return -1;
- }
-
- if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
- /*
- * IBSS/WPA-None uses only one key (Group) for both receiving
- * and sending unicast and multicast packets.
- */
- /* Send the key as PTK to firmware */
- encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET,
- KEY_INFO_ENABLED, encrypt_key, false);
- if (ret)
- return ret;
-
- ibss_key = &priv->aes_key;
- memset(ibss_key, 0,
- sizeof(struct host_cmd_ds_802_11_key_material));
- /* Copy the key in the driver */
- memcpy(ibss_key->key_param_set.key, encrypt_key->key_material,
- encrypt_key->key_len);
- memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len,
- sizeof(ibss_key->key_param_set.key_len));
- ibss_key->key_param_set.key_type_id
- = cpu_to_le16(KEY_TYPE_ID_TKIP);
- ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED);
-
- /* Send the key as GTK to firmware */
- encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST;
- }
-
- if (!encrypt_key->key_index)
- encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
-
- if (remove_key)
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET,
- !KEY_INFO_ENABLED, encrypt_key, true);
- else
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET,
- KEY_INFO_ENABLED, encrypt_key, true);
-
- return ret;
-}
-
-/*
- * IOCTL request handler to set/get network keys.
- *
- * This is a generic key handling function which supports WEP, WPA
- * and WAPI.
- */
-static int
-mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv,
- struct mwifiex_ds_encrypt_key *encrypt_key)
-{
- int status;
-
- if (encrypt_key->is_wapi_key)
- status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key);
- else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
- status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key);
- else
- status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key);
- return status;
-}
-
-/*
- * This function returns the driver version.
- */
-int
-mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
- int max_len)
-{
- union {
- __le32 l;
- u8 c[4];
- } ver;
- char fw_ver[32];
-
- ver.l = cpu_to_le32(adapter->fw_release_number);
- sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
-
- snprintf(version, max_len, driver_version, fw_ver);
-
- mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version);
-
- return 0;
-}
-
-/*
- * Sends IOCTL request to set encoding parameters.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
- const u8 *key, int key_len, u8 key_index,
- const u8 *mac_addr, int disable)
-{
- struct mwifiex_ds_encrypt_key encrypt_key;
-
- memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key));
- encrypt_key.key_len = key_len;
- encrypt_key.key_index = key_index;
-
- if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
- encrypt_key.is_igtk_key = true;
-
- if (!disable) {
- if (key_len)
- memcpy(encrypt_key.key_material, key, key_len);
- else
- encrypt_key.is_current_wep_key = true;
-
- if (mac_addr)
- memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
- if (kp && kp->seq && kp->seq_len) {
- memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
- encrypt_key.pn_len = kp->seq_len;
- encrypt_key.is_rx_seq_valid = true;
- }
- } else {
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
- return 0;
- encrypt_key.key_disable = true;
- if (mac_addr)
- memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
- }
-
- return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key);
-}
-
-/*
- * Sends IOCTL request to get extended version.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int
-mwifiex_get_ver_ext(struct mwifiex_private *priv)
-{
- struct mwifiex_ver_ext ver_ext;
-
- memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext));
- if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
- HostCmd_ACT_GEN_GET, 0, &ver_ext, true))
- return -1;
-
- return 0;
-}
-
-int
-mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
- struct ieee80211_channel *chan,
- unsigned int duration)
-{
- struct host_cmd_ds_remain_on_chan roc_cfg;
- u8 sc;
-
- memset(&roc_cfg, 0, sizeof(roc_cfg));
- roc_cfg.action = cpu_to_le16(action);
- if (action == HostCmd_ACT_GEN_SET) {
- roc_cfg.band_cfg = chan->band;
- sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
- roc_cfg.band_cfg |= (sc << 2);
-
- roc_cfg.channel =
- ieee80211_frequency_to_channel(chan->center_freq);
- roc_cfg.duration = cpu_to_le32(duration);
- }
- if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN,
- action, 0, &roc_cfg, true)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "failed to remain on channel\n");
- return -1;
- }
-
- return roc_cfg.status;
-}
-
-/*
- * Sends IOCTL request to get statistics information.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int
-mwifiex_get_stats_info(struct mwifiex_private *priv,
- struct mwifiex_ds_get_stats *log)
-{
- return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG,
- HostCmd_ACT_GEN_GET, 0, log, true);
-}
-
-/*
- * IOCTL request handler to read/write register.
- *
- * This function prepares the correct firmware command and
- * issues it.
- *
- * Access to the following registers are supported -
- * - MAC
- * - BBP
- * - RF
- * - PMIC
- * - CAU
- */
-static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv,
- struct mwifiex_ds_reg_rw *reg_rw,
- u16 action)
-{
- u16 cmd_no;
-
- switch (le32_to_cpu(reg_rw->type)) {
- case MWIFIEX_REG_MAC:
- cmd_no = HostCmd_CMD_MAC_REG_ACCESS;
- break;
- case MWIFIEX_REG_BBP:
- cmd_no = HostCmd_CMD_BBP_REG_ACCESS;
- break;
- case MWIFIEX_REG_RF:
- cmd_no = HostCmd_CMD_RF_REG_ACCESS;
- break;
- case MWIFIEX_REG_PMIC:
- cmd_no = HostCmd_CMD_PMIC_REG_ACCESS;
- break;
- case MWIFIEX_REG_CAU:
- cmd_no = HostCmd_CMD_CAU_REG_ACCESS;
- break;
- default:
- return -1;
- }
-
- return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
-}
-
-/*
- * Sends IOCTL request to write to a register.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int
-mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type,
- u32 reg_offset, u32 reg_value)
-{
- struct mwifiex_ds_reg_rw reg_rw;
-
- reg_rw.type = cpu_to_le32(reg_type);
- reg_rw.offset = cpu_to_le32(reg_offset);
- reg_rw.value = cpu_to_le32(reg_value);
-
- return mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_SET);
-}
-
-/*
- * Sends IOCTL request to read from a register.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int
-mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type,
- u32 reg_offset, u32 *value)
-{
- int ret;
- struct mwifiex_ds_reg_rw reg_rw;
-
- reg_rw.type = cpu_to_le32(reg_type);
- reg_rw.offset = cpu_to_le32(reg_offset);
- ret = mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_GET);
-
- if (ret)
- goto done;
-
- *value = le32_to_cpu(reg_rw.value);
-
-done:
- return ret;
-}
-
-/*
- * Sends IOCTL request to read from EEPROM.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int
-mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
- u8 *value)
-{
- int ret;
- struct mwifiex_ds_read_eeprom rd_eeprom;
-
- rd_eeprom.offset = cpu_to_le16((u16) offset);
- rd_eeprom.byte_count = cpu_to_le16((u16) bytes);
-
- /* Send request to firmware */
- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS,
- HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true);
-
- if (!ret)
- memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA);
- return ret;
-}
-
-/*
- * This function sets a generic IE. In addition to generic IE, it can
- * also handle WPA, WPA2 and WAPI IEs.
- */
-static int
-mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
- u16 ie_len)
-{
- int ret = 0;
- struct ieee_types_vendor_header *pvendor_ie;
- const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
- const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
-
- /* If the passed length is zero, reset the buffer */
- if (!ie_len) {
- priv->gen_ie_buf_len = 0;
- priv->wps.session_enable = false;
-
- return 0;
- } else if (!ie_data_ptr) {
- return -1;
- }
- pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
- /* Test to see if it is a WPA IE, if not, then it is a gen IE */
- if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) &&
- (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) ||
- (pvendor_ie->element_id == WLAN_EID_RSN)) {
-
- /* IE is a WPA/WPA2 IE so call set_wpa function */
- ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len);
- priv->wps.session_enable = false;
-
- return ret;
- } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) {
- /* IE is a WAPI IE so call set_wapi function */
- ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len);
-
- return ret;
- }
- /*
- * Verify that the passed length is not larger than the
- * available space remaining in the buffer
- */
- if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
-
- /* Test to see if it is a WPS IE, if so, enable
- * wps session flag
- */
- pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
- if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) &&
- (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) {
- priv->wps.session_enable = true;
- mwifiex_dbg(priv->adapter, INFO,
- "info: WPS Session Enabled.\n");
- ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len);
- }
-
- /* Append the passed data to the end of the
- genIeBuffer */
- memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr,
- ie_len);
- /* Increment the stored buffer length by the
- size passed */
- priv->gen_ie_buf_len += ie_len;
- } else {
- /* Passed data does not fit in the remaining
- buffer space */
- ret = -1;
- }
-
- /* Return 0, or -1 for error case */
- return ret;
-}
-
-/*
- * IOCTL request handler to set/get generic IE.
- *
- * In addition to various generic IEs, this function can also be
- * used to set the ARP filter.
- */
-static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv,
- struct mwifiex_ds_misc_gen_ie *gen_ie,
- u16 action)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
-
- switch (gen_ie->type) {
- case MWIFIEX_IE_TYPE_GEN_IE:
- if (action == HostCmd_ACT_GEN_GET) {
- gen_ie->len = priv->wpa_ie_len;
- memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
- } else {
- mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data,
- (u16) gen_ie->len);
- }
- break;
- case MWIFIEX_IE_TYPE_ARP_FILTER:
- memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
- if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
- adapter->arp_filter_size = 0;
- mwifiex_dbg(adapter, ERROR,
- "invalid ARP filter size\n");
- return -1;
- } else {
- memcpy(adapter->arp_filter, gen_ie->ie_data,
- gen_ie->len);
- adapter->arp_filter_size = gen_ie->len;
- }
- break;
- default:
- mwifiex_dbg(adapter, ERROR, "invalid IE type\n");
- return -1;
- }
- return 0;
-}
-
-/*
- * Sends IOCTL request to set a generic IE.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int
-mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len)
-{
- struct mwifiex_ds_misc_gen_ie gen_ie;
-
- if (ie_len > IEEE_MAX_IE_SIZE)
- return -EFAULT;
-
- gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE;
- gen_ie.len = ie_len;
- memcpy(gen_ie.ie_data, ie, ie_len);
- if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET))
- return -EFAULT;
-
- return 0;
-}
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c
deleted file mode 100644
index d4d4cb1ce95b..000000000000
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: station RX data handling
- *
- * Copyright (C) 2011-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 <uapi/linux/ipv6.h>
-#include <net/ndisc.h>
-#include "decl.h"
-#include "ioctl.h"
-#include "util.h"
-#include "fw.h"
-#include "main.h"
-#include "11n_aggr.h"
-#include "11n_rxreorder.h"
-
-/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement
- * frame. If frame has both source and destination mac address as same, this
- * function drops such gratuitous frames.
- */
-static bool
-mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- const struct mwifiex_arp_eth_header *arp;
- struct ethhdr *eth;
- struct ipv6hdr *ipv6;
- struct icmp6hdr *icmpv6;
-
- eth = (struct ethhdr *)skb->data;
- switch (ntohs(eth->h_proto)) {
- case ETH_P_ARP:
- arp = (void *)(skb->data + sizeof(struct ethhdr));
- if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
- arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
- if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
- return true;
- }
- break;
- case ETH_P_IPV6:
- ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
- icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
- sizeof(struct ipv6hdr));
- if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) {
- if (!memcmp(&ipv6->saddr, &ipv6->daddr,
- sizeof(struct in6_addr)))
- return true;
- }
- break;
- default:
- break;
- }
-
- return false;
-}
-
-/*
- * This function processes the received packet and forwards it
- * to kernel/upper layer.
- *
- * This function parses through the received packet and determines
- * if it is a debug packet or normal packet.
- *
- * For non-debug packets, the function chops off unnecessary leading
- * header bytes, reconstructs the packet as an ethernet frame or
- * 802.2/llc/snap frame as required, and sends it to kernel/upper layer.
- *
- * The completion callback is called after processing in complete.
- */
-int mwifiex_process_rx_packet(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- int ret;
- struct rx_packet_hdr *rx_pkt_hdr;
- struct rxpd *local_rx_pd;
- int hdr_chop;
- struct ethhdr *eth;
- u16 rx_pkt_off, rx_pkt_len;
- u8 *offset;
- u8 adj_rx_rate = 0;
-
- local_rx_pd = (struct rxpd *) (skb->data);
-
- rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset);
- rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length);
- rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off;
-
- if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
- sizeof(bridge_tunnel_header))) ||
- (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
- sizeof(rfc1042_header)) &&
- ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP &&
- ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) {
- /*
- * Replace the 803 header and rfc1042 header (llc/snap) with an
- * EthernetII header, keep the src/dst and snap_type
- * (ethertype).
- * The firmware only passes up SNAP frames converting
- * all RX Data from 802.11 to 802.2/LLC/SNAP frames.
- * To create the Ethernet II, just move the src, dst address
- * right before the snap_type.
- */
- eth = (struct ethhdr *)
- ((u8 *) &rx_pkt_hdr->eth803_hdr
- + sizeof(rx_pkt_hdr->eth803_hdr) +
- sizeof(rx_pkt_hdr->rfc1042_hdr)
- - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
- - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
- - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
-
- memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source,
- sizeof(eth->h_source));
- memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
- sizeof(eth->h_dest));
-
- /* Chop off the rxpd + the excess memory from the 802.2/llc/snap
- header that was removed. */
- hdr_chop = (u8 *) eth - (u8 *) local_rx_pd;
- } else {
- /* Chop off the rxpd */
- hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr -
- (u8 *) local_rx_pd;
- }
-
- /* Chop off the leading header bytes so the it points to the start of
- either the reconstructed EthII frame or the 802.2/llc/snap frame */
- skb_pull(skb, hdr_chop);
-
- if (priv->hs2_enabled &&
- mwifiex_discard_gratuitous_arp(priv, skb)) {
- mwifiex_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n");
- dev_kfree_skb_any(skb);
- return 0;
- }
-
- if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
- ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
- offset = (u8 *)local_rx_pd + rx_pkt_off;
- mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len);
- }
-
- priv->rxpd_rate = local_rx_pd->rx_rate;
-
- priv->rxpd_htinfo = local_rx_pd->ht_info;
-
- if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
- GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
- adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate,
- priv->rxpd_htinfo);
- mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr,
- local_rx_pd->nf);
- }
-
- ret = mwifiex_recv_packet(priv, skb);
- if (ret == -1)
- mwifiex_dbg(priv->adapter, ERROR,
- "recv packet failed\n");
-
- return ret;
-}
-
-/*
- * This function processes the received buffer.
- *
- * The function looks into the RxPD and performs sanity tests on the
- * received buffer to ensure its a valid packet, before processing it
- * further. If the packet is determined to be aggregated, it is
- * de-aggregated accordingly. Non-unicast packets are sent directly to
- * the kernel/upper layers. Unicast packets are handed over to the
- * Rx reordering routine if 11n is enabled.
- *
- * The completion callback is called after processing in complete.
- */
-int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- int ret = 0;
- struct rxpd *local_rx_pd;
- struct rx_packet_hdr *rx_pkt_hdr;
- u8 ta[ETH_ALEN];
- u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
- struct mwifiex_sta_node *sta_ptr;
-
- local_rx_pd = (struct rxpd *) (skb->data);
- rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
- rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset);
- rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length);
- seq_num = le16_to_cpu(local_rx_pd->seq_num);
-
- rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset;
-
- if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) {
- mwifiex_dbg(adapter, ERROR,
- "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n",
- skb->len, rx_pkt_offset, rx_pkt_length);
- priv->stats.rx_dropped++;
- dev_kfree_skb_any(skb);
- return ret;
- }
-
- if (rx_pkt_type == PKT_TYPE_MGMT) {
- ret = mwifiex_process_mgmt_packet(priv, skb);
- if (ret)
- mwifiex_dbg(adapter, ERROR, "Rx of mgmt packet failed");
- dev_kfree_skb_any(skb);
- return ret;
- }
-
- /*
- * If the packet is not an unicast packet then send the packet
- * directly to os. Don't pass thru rx reordering
- */
- if ((!IS_11N_ENABLED(priv) &&
- !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
- !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) ||
- !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) {
- mwifiex_process_rx_packet(priv, skb);
- return ret;
- }
-
- if (mwifiex_queuing_ra_based(priv) ||
- (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
- local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) {
- memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
- if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET &&
- local_rx_pd->priority < MAX_NUM_TID) {
- sta_ptr = mwifiex_get_sta_entry(priv, ta);
- if (sta_ptr)
- sta_ptr->rx_seq[local_rx_pd->priority] =
- le16_to_cpu(local_rx_pd->seq_num);
- mwifiex_auto_tdls_update_peer_signal(priv, ta,
- local_rx_pd->snr,
- local_rx_pd->nf);
- }
- } else {
- if (rx_pkt_type != PKT_TYPE_BAR)
- priv->rx_seq[local_rx_pd->priority] = seq_num;
- memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address,
- ETH_ALEN);
- }
-
- /* Reorder and send to OS */
- ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority,
- ta, (u8) rx_pkt_type, skb);
-
- if (ret || (rx_pkt_type == PKT_TYPE_BAR))
- dev_kfree_skb_any(skb);
-
- if (ret)
- priv->stats.rx_dropped++;
-
- return ret;
-}
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
deleted file mode 100644
index 759a6ada5b0f..000000000000
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ /dev/null
@@ -1,885 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: AP specific command handling
- *
- * Copyright (C) 2012-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "11ac.h"
-
-/* This function parses security related parameters from cfg80211_ap_settings
- * and sets into FW understandable bss_config structure.
- */
-int mwifiex_set_secure_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_config,
- struct cfg80211_ap_settings *params) {
- int i;
- struct mwifiex_wep_key wep_key;
-
- if (!params->privacy) {
- bss_config->protocol = PROTOCOL_NO_SECURITY;
- bss_config->key_mgmt = KEY_MGMT_NONE;
- bss_config->wpa_cfg.length = 0;
- priv->sec_info.wep_enabled = 0;
- priv->sec_info.wpa_enabled = 0;
- priv->sec_info.wpa2_enabled = 0;
-
- return 0;
- }
-
- switch (params->auth_type) {
- case NL80211_AUTHTYPE_OPEN_SYSTEM:
- bss_config->auth_mode = WLAN_AUTH_OPEN;
- break;
- case NL80211_AUTHTYPE_SHARED_KEY:
- bss_config->auth_mode = WLAN_AUTH_SHARED_KEY;
- break;
- case NL80211_AUTHTYPE_NETWORK_EAP:
- bss_config->auth_mode = WLAN_AUTH_LEAP;
- break;
- default:
- bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO;
- break;
- }
-
- bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST;
-
- for (i = 0; i < params->crypto.n_akm_suites; i++) {
- switch (params->crypto.akm_suites[i]) {
- case WLAN_AKM_SUITE_8021X:
- if (params->crypto.wpa_versions &
- NL80211_WPA_VERSION_1) {
- bss_config->protocol = PROTOCOL_WPA;
- bss_config->key_mgmt = KEY_MGMT_EAP;
- }
- if (params->crypto.wpa_versions &
- NL80211_WPA_VERSION_2) {
- bss_config->protocol |= PROTOCOL_WPA2;
- bss_config->key_mgmt = KEY_MGMT_EAP;
- }
- break;
- case WLAN_AKM_SUITE_PSK:
- if (params->crypto.wpa_versions &
- NL80211_WPA_VERSION_1) {
- bss_config->protocol = PROTOCOL_WPA;
- bss_config->key_mgmt = KEY_MGMT_PSK;
- }
- if (params->crypto.wpa_versions &
- NL80211_WPA_VERSION_2) {
- bss_config->protocol |= PROTOCOL_WPA2;
- bss_config->key_mgmt = KEY_MGMT_PSK;
- }
- break;
- default:
- break;
- }
- }
- for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) {
- switch (params->crypto.ciphers_pairwise[i]) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
- bss_config->wpa_cfg.pairwise_cipher_wpa |=
- CIPHER_TKIP;
- if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
- bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
- CIPHER_TKIP;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
- bss_config->wpa_cfg.pairwise_cipher_wpa |=
- CIPHER_AES_CCMP;
- if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
- bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
- CIPHER_AES_CCMP;
- default:
- break;
- }
- }
-
- switch (params->crypto.cipher_group) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- if (priv->sec_info.wep_enabled) {
- bss_config->protocol = PROTOCOL_STATIC_WEP;
- bss_config->key_mgmt = KEY_MGMT_NONE;
- bss_config->wpa_cfg.length = 0;
-
- for (i = 0; i < NUM_WEP_KEYS; i++) {
- wep_key = priv->wep_key[i];
- bss_config->wep_cfg[i].key_index = i;
-
- if (priv->wep_key_curr_index == i)
- bss_config->wep_cfg[i].is_default = 1;
- else
- bss_config->wep_cfg[i].is_default = 0;
-
- bss_config->wep_cfg[i].length =
- wep_key.key_length;
- memcpy(&bss_config->wep_cfg[i].key,
- &wep_key.key_material,
- wep_key.key_length);
- }
- }
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- bss_config->wpa_cfg.group_cipher = CIPHER_TKIP;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-/* This function updates 11n related parameters from IE and sets them into
- * bss_config structure.
- */
-void
-mwifiex_set_ht_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params)
-{
- const u8 *ht_ie;
- u16 cap_info;
-
- if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info))
- return;
-
- ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail,
- params->beacon.tail_len);
- if (ht_ie) {
- memcpy(&bss_cfg->ht_cap, ht_ie + 2,
- sizeof(struct ieee80211_ht_cap));
- cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info);
- memset(&bss_cfg->ht_cap.mcs, 0,
- priv->adapter->number_of_antenna);
- switch (GET_RXSTBC(cap_info)) {
- case MWIFIEX_RX_STBC1:
- /* HT_CAP 1X1 mode */
- bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
- break;
- case MWIFIEX_RX_STBC12: /* fall through */
- case MWIFIEX_RX_STBC123:
- /* HT_CAP 2X2 mode */
- bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
- bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
- break;
- default:
- mwifiex_dbg(priv->adapter, WARN,
- "Unsupported RX-STBC, default to 2x2\n");
- bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
- bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
- break;
- }
- priv->ap_11n_enabled = 1;
- } else {
- memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap));
- bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP);
- bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU;
- }
-
- return;
-}
-
-/* This function updates 11ac related parameters from IE
- * and sets them into bss_config structure.
- */
-void mwifiex_set_vht_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params)
-{
- const u8 *vht_ie;
-
- vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail,
- params->beacon.tail_len);
- if (vht_ie) {
- memcpy(&bss_cfg->vht_cap, vht_ie + 2,
- sizeof(struct ieee80211_vht_cap));
- priv->ap_11ac_enabled = 1;
- } else {
- priv->ap_11ac_enabled = 0;
- }
-
- return;
-}
-
-/* This function updates 11ac related parameters from IE
- * and sets them into bss_config structure.
- */
-void mwifiex_set_tpc_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params)
-{
- const u8 *tpc_ie;
-
- tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail,
- params->beacon.tail_len);
- if (tpc_ie)
- bss_cfg->power_constraint = *(tpc_ie + 2);
- else
- bss_cfg->power_constraint = 0;
-}
-
-/* Enable VHT only when cfg80211_ap_settings has VHT IE.
- * Otherwise disable VHT.
- */
-void mwifiex_set_vht_width(struct mwifiex_private *priv,
- enum nl80211_chan_width width,
- bool ap_11ac_enable)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct mwifiex_11ac_vht_cfg vht_cfg;
-
- vht_cfg.band_config = VHT_CFG_5GHZ;
- vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap;
-
- if (!ap_11ac_enable) {
- vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET;
- vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET;
- } else {
- vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET;
- vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET;
- }
-
- vht_cfg.misc_config = VHT_CAP_UAP_ONLY;
-
- if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80)
- vht_cfg.misc_config |= VHT_BW_80_160_80P80;
-
- mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG,
- HostCmd_ACT_GEN_SET, 0, &vht_cfg, true);
-
- return;
-}
-
-/* This function finds supported rates IE from beacon parameter and sets
- * these rates into bss_config structure.
- */
-void
-mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params)
-{
- struct ieee_types_header *rate_ie;
- int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
- const u8 *var_pos = params->beacon.head + var_offset;
- int len = params->beacon.head_len - var_offset;
- u8 rate_len = 0;
-
- rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
- if (rate_ie) {
- memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len);
- rate_len = rate_ie->len;
- }
-
- rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES,
- params->beacon.tail,
- params->beacon.tail_len);
- if (rate_ie)
- memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len);
-
- return;
-}
-
-/* This function initializes some of mwifiex_uap_bss_param variables.
- * This helps FW in ignoring invalid values. These values may or may not
- * be get updated to valid ones at later stage.
- */
-void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config)
-{
- config->bcast_ssid_ctl = 0x7F;
- config->radio_ctl = 0x7F;
- config->dtim_period = 0x7F;
- config->beacon_period = 0x7FFF;
- config->auth_mode = 0x7F;
- config->rts_threshold = 0x7FFF;
- config->frag_threshold = 0x7FFF;
- config->retry_limit = 0x7F;
- config->qos_info = 0xFF;
-}
-
-/* This function parses BSS related parameters from structure
- * and prepares TLVs specific to WPA/WPA2 security.
- * These TLVs are appended to command buffer.
- */
-static void
-mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
-{
- struct host_cmd_tlv_pwk_cipher *pwk_cipher;
- struct host_cmd_tlv_gwk_cipher *gwk_cipher;
- struct host_cmd_tlv_passphrase *passphrase;
- struct host_cmd_tlv_akmp *tlv_akmp;
- struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
- u16 cmd_size = *param_size;
- u8 *tlv = *tlv_buf;
-
- tlv_akmp = (struct host_cmd_tlv_akmp *)tlv;
- tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
- tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
- sizeof(struct mwifiex_ie_types_header));
- tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation);
- tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt);
- cmd_size += sizeof(struct host_cmd_tlv_akmp);
- tlv += sizeof(struct host_cmd_tlv_akmp);
-
- if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) {
- pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
- pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
- pwk_cipher->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
- sizeof(struct mwifiex_ie_types_header));
- pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA);
- pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa;
- cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
- tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
- }
-
- if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) {
- pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
- pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
- pwk_cipher->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
- sizeof(struct mwifiex_ie_types_header));
- pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2);
- pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2;
- cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
- tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
- }
-
- if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) {
- gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv;
- gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
- gwk_cipher->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) -
- sizeof(struct mwifiex_ie_types_header));
- gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher;
- cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher);
- tlv += sizeof(struct host_cmd_tlv_gwk_cipher);
- }
-
- if (bss_cfg->wpa_cfg.length) {
- passphrase = (struct host_cmd_tlv_passphrase *)tlv;
- passphrase->header.type =
- cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
- passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
- memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase,
- bss_cfg->wpa_cfg.length);
- cmd_size += sizeof(struct mwifiex_ie_types_header) +
- bss_cfg->wpa_cfg.length;
- tlv += sizeof(struct mwifiex_ie_types_header) +
- bss_cfg->wpa_cfg.length;
- }
-
- *param_size = cmd_size;
- *tlv_buf = tlv;
-
- return;
-}
-
-/* This function parses WMM related parameters from cfg80211_ap_settings
- * structure and updates bss_config structure.
- */
-void
-mwifiex_set_wmm_params(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_ap_settings *params)
-{
- const u8 *vendor_ie;
- struct ieee_types_header *wmm_ie;
- u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02};
-
- vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
- WLAN_OUI_TYPE_MICROSOFT_WMM,
- params->beacon.tail,
- params->beacon.tail_len);
- if (vendor_ie) {
- wmm_ie = (struct ieee_types_header *)vendor_ie;
- memcpy(&bss_cfg->wmm_info, wmm_ie + 1,
- sizeof(bss_cfg->wmm_info));
- priv->wmm_enabled = 1;
- } else {
- memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info));
- memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui));
- bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE;
- bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION;
- priv->wmm_enabled = 0;
- }
-
- bss_cfg->qos_info = 0x00;
- return;
-}
-/* This function parses BSS related parameters from structure
- * and prepares TLVs specific to WEP encryption.
- * These TLVs are appended to command buffer.
- */
-static void
-mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
-{
- struct host_cmd_tlv_wep_key *wep_key;
- u16 cmd_size = *param_size;
- int i;
- u8 *tlv = *tlv_buf;
- struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
-
- for (i = 0; i < NUM_WEP_KEYS; i++) {
- if (bss_cfg->wep_cfg[i].length &&
- (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 ||
- bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) {
- wep_key = (struct host_cmd_tlv_wep_key *)tlv;
- wep_key->header.type =
- cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
- wep_key->header.len =
- cpu_to_le16(bss_cfg->wep_cfg[i].length + 2);
- wep_key->key_index = bss_cfg->wep_cfg[i].key_index;
- wep_key->is_default = bss_cfg->wep_cfg[i].is_default;
- memcpy(wep_key->key, bss_cfg->wep_cfg[i].key,
- bss_cfg->wep_cfg[i].length);
- cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 +
- bss_cfg->wep_cfg[i].length;
- tlv += sizeof(struct mwifiex_ie_types_header) + 2 +
- bss_cfg->wep_cfg[i].length;
- }
- }
-
- *param_size = cmd_size;
- *tlv_buf = tlv;
-
- return;
-}
-
-/* This function parses BSS related parameters from structure
- * and prepares TLVs. These TLVs are appended to command buffer.
-*/
-static int
-mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
-{
- struct host_cmd_tlv_dtim_period *dtim_period;
- struct host_cmd_tlv_beacon_period *beacon_period;
- struct host_cmd_tlv_ssid *ssid;
- struct host_cmd_tlv_bcast_ssid *bcast_ssid;
- struct host_cmd_tlv_channel_band *chan_band;
- struct host_cmd_tlv_frag_threshold *frag_threshold;
- struct host_cmd_tlv_rts_threshold *rts_threshold;
- struct host_cmd_tlv_retry_limit *retry_limit;
- struct host_cmd_tlv_encrypt_protocol *encrypt_protocol;
- struct host_cmd_tlv_auth_type *auth_type;
- struct host_cmd_tlv_rates *tlv_rates;
- struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer;
- struct host_cmd_tlv_power_constraint *pwr_ct;
- struct mwifiex_ie_types_htcap *htcap;
- struct mwifiex_ie_types_wmmcap *wmm_cap;
- struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
- int i;
- u16 cmd_size = *param_size;
-
- if (bss_cfg->ssid.ssid_len) {
- ssid = (struct host_cmd_tlv_ssid *)tlv;
- ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
- ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
- memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len);
- cmd_size += sizeof(struct mwifiex_ie_types_header) +
- bss_cfg->ssid.ssid_len;
- tlv += sizeof(struct mwifiex_ie_types_header) +
- bss_cfg->ssid.ssid_len;
-
- bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv;
- bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
- bcast_ssid->header.len =
- cpu_to_le16(sizeof(bcast_ssid->bcast_ctl));
- bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl;
- cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid);
- tlv += sizeof(struct host_cmd_tlv_bcast_ssid);
- }
- if (bss_cfg->rates[0]) {
- tlv_rates = (struct host_cmd_tlv_rates *)tlv;
- tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
-
- for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i];
- i++)
- tlv_rates->rates[i] = bss_cfg->rates[i];
-
- tlv_rates->header.len = cpu_to_le16(i);
- cmd_size += sizeof(struct host_cmd_tlv_rates) + i;
- tlv += sizeof(struct host_cmd_tlv_rates) + i;
- }
- if (bss_cfg->channel &&
- ((bss_cfg->band_cfg == BAND_CONFIG_BG &&
- bss_cfg->channel <= MAX_CHANNEL_BAND_BG) ||
- (bss_cfg->band_cfg == BAND_CONFIG_A &&
- bss_cfg->channel <= MAX_CHANNEL_BAND_A))) {
- chan_band = (struct host_cmd_tlv_channel_band *)tlv;
- chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
- chan_band->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) -
- sizeof(struct mwifiex_ie_types_header));
- chan_band->band_config = bss_cfg->band_cfg;
- chan_band->channel = bss_cfg->channel;
- cmd_size += sizeof(struct host_cmd_tlv_channel_band);
- tlv += sizeof(struct host_cmd_tlv_channel_band);
- }
- if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD &&
- bss_cfg->beacon_period <= MAX_BEACON_PERIOD) {
- beacon_period = (struct host_cmd_tlv_beacon_period *)tlv;
- beacon_period->header.type =
- cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD);
- beacon_period->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) -
- sizeof(struct mwifiex_ie_types_header));
- beacon_period->period = cpu_to_le16(bss_cfg->beacon_period);
- cmd_size += sizeof(struct host_cmd_tlv_beacon_period);
- tlv += sizeof(struct host_cmd_tlv_beacon_period);
- }
- if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD &&
- bss_cfg->dtim_period <= MAX_DTIM_PERIOD) {
- dtim_period = (struct host_cmd_tlv_dtim_period *)tlv;
- dtim_period->header.type =
- cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
- dtim_period->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) -
- sizeof(struct mwifiex_ie_types_header));
- dtim_period->period = bss_cfg->dtim_period;
- cmd_size += sizeof(struct host_cmd_tlv_dtim_period);
- tlv += sizeof(struct host_cmd_tlv_dtim_period);
- }
- if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) {
- rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv;
- rts_threshold->header.type =
- cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD);
- rts_threshold->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) -
- sizeof(struct mwifiex_ie_types_header));
- rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold);
- cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
- tlv += sizeof(struct host_cmd_tlv_frag_threshold);
- }
- if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) &&
- (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) {
- frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv;
- frag_threshold->header.type =
- cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD);
- frag_threshold->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) -
- sizeof(struct mwifiex_ie_types_header));
- frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold);
- cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
- tlv += sizeof(struct host_cmd_tlv_frag_threshold);
- }
- if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) {
- retry_limit = (struct host_cmd_tlv_retry_limit *)tlv;
- retry_limit->header.type =
- cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
- retry_limit->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) -
- sizeof(struct mwifiex_ie_types_header));
- retry_limit->limit = (u8)bss_cfg->retry_limit;
- cmd_size += sizeof(struct host_cmd_tlv_retry_limit);
- tlv += sizeof(struct host_cmd_tlv_retry_limit);
- }
- if ((bss_cfg->protocol & PROTOCOL_WPA) ||
- (bss_cfg->protocol & PROTOCOL_WPA2) ||
- (bss_cfg->protocol & PROTOCOL_EAP))
- mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size);
- else
- mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size);
-
- if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) ||
- (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) {
- auth_type = (struct host_cmd_tlv_auth_type *)tlv;
- auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
- auth_type->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) -
- sizeof(struct mwifiex_ie_types_header));
- auth_type->auth_type = (u8)bss_cfg->auth_mode;
- cmd_size += sizeof(struct host_cmd_tlv_auth_type);
- tlv += sizeof(struct host_cmd_tlv_auth_type);
- }
- if (bss_cfg->protocol) {
- encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv;
- encrypt_protocol->header.type =
- cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL);
- encrypt_protocol->header.len =
- cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol)
- - sizeof(struct mwifiex_ie_types_header));
- encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol);
- cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol);
- tlv += sizeof(struct host_cmd_tlv_encrypt_protocol);
- }
-
- if (bss_cfg->ht_cap.cap_info) {
- htcap = (struct mwifiex_ie_types_htcap *)tlv;
- htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
- htcap->header.len =
- cpu_to_le16(sizeof(struct ieee80211_ht_cap));
- htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info;
- htcap->ht_cap.ampdu_params_info =
- bss_cfg->ht_cap.ampdu_params_info;
- memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs,
- sizeof(struct ieee80211_mcs_info));
- htcap->ht_cap.extended_ht_cap_info =
- bss_cfg->ht_cap.extended_ht_cap_info;
- htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info;
- htcap->ht_cap.antenna_selection_info =
- bss_cfg->ht_cap.antenna_selection_info;
- cmd_size += sizeof(struct mwifiex_ie_types_htcap);
- tlv += sizeof(struct mwifiex_ie_types_htcap);
- }
-
- if (bss_cfg->wmm_info.qos_info != 0xFF) {
- wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv;
- wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC);
- wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info));
- memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info,
- sizeof(wmm_cap->wmm_info));
- cmd_size += sizeof(struct mwifiex_ie_types_wmmcap);
- tlv += sizeof(struct mwifiex_ie_types_wmmcap);
- }
-
- if (bss_cfg->sta_ao_timer) {
- ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
- ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
- ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) -
- sizeof(struct mwifiex_ie_types_header));
- ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer);
- cmd_size += sizeof(*ao_timer);
- tlv += sizeof(*ao_timer);
- }
-
- if (bss_cfg->power_constraint) {
- pwr_ct = (void *)tlv;
- pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT);
- pwr_ct->header.len = cpu_to_le16(sizeof(u8));
- pwr_ct->constraint = bss_cfg->power_constraint;
- cmd_size += sizeof(*pwr_ct);
- tlv += sizeof(*pwr_ct);
- }
-
- if (bss_cfg->ps_sta_ao_timer) {
- ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
- ps_ao_timer->header.type =
- cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
- ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) -
- sizeof(struct mwifiex_ie_types_header));
- ps_ao_timer->sta_ao_timer =
- cpu_to_le32(bss_cfg->ps_sta_ao_timer);
- cmd_size += sizeof(*ps_ao_timer);
- tlv += sizeof(*ps_ao_timer);
- }
-
- *param_size = cmd_size;
-
- return 0;
-}
-
-/* This function parses custom IEs from IE list and prepares command buffer */
-static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size)
-{
- struct mwifiex_ie_list *ap_ie = cmd_buf;
- struct mwifiex_ie_types_header *tlv_ie = (void *)tlv;
-
- if (!ap_ie || !ap_ie->len || !ap_ie->ie_list)
- return -1;
-
- *ie_size += le16_to_cpu(ap_ie->len) +
- sizeof(struct mwifiex_ie_types_header);
-
- tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
- tlv_ie->len = ap_ie->len;
- tlv += sizeof(struct mwifiex_ie_types_header);
-
- memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len));
-
- return 0;
-}
-
-/* Parse AP config structure and prepare TLV based command structure
- * to be sent to FW for uAP configuration
- */
-static int
-mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action,
- u32 type, void *cmd_buf)
-{
- u8 *tlv;
- u16 cmd_size, param_size, ie_size;
- struct host_cmd_ds_sys_config *sys_cfg;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG);
- cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN);
- sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config;
- sys_cfg->action = cpu_to_le16(cmd_action);
- tlv = sys_cfg->tlv;
-
- switch (type) {
- case UAP_BSS_PARAMS_I:
- param_size = cmd_size;
- if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, &param_size))
- return -1;
- cmd->size = cpu_to_le16(param_size);
- break;
- case UAP_CUSTOM_IE_I:
- ie_size = cmd_size;
- if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size))
- return -1;
- cmd->size = cpu_to_le16(ie_size);
- break;
- default:
- return -1;
- }
-
- return 0;
-}
-
-/* This function prepares AP specific deauth command with mac supplied in
- * function parameter.
- */
-static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd, u8 *mac)
-{
- struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
-
- cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH);
- memcpy(sta_deauth->mac, mac, ETH_ALEN);
- sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
-
- cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
- S_DS_GEN);
- return 0;
-}
-
-/* This function prepares the AP specific commands before sending them
- * to the firmware.
- * This is a generic function which calls specific command preparation
- * routines based upon the command number.
- */
-int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
- u16 cmd_action, u32 type,
- void *data_buf, void *cmd_buf)
-{
- struct host_cmd_ds_command *cmd = cmd_buf;
-
- switch (cmd_no) {
- case HostCmd_CMD_UAP_SYS_CONFIG:
- if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf))
- return -1;
- break;
- case HostCmd_CMD_UAP_BSS_START:
- case HostCmd_CMD_UAP_BSS_STOP:
- case HOST_CMD_APCMD_SYS_RESET:
- case HOST_CMD_APCMD_STA_LIST:
- cmd->command = cpu_to_le16(cmd_no);
- cmd->size = cpu_to_le16(S_DS_GEN);
- break;
- case HostCmd_CMD_UAP_STA_DEAUTH:
- if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
- return -1;
- break;
- case HostCmd_CMD_CHAN_REPORT_REQUEST:
- if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf,
- data_buf))
- return -1;
- break;
- default:
- mwifiex_dbg(priv->adapter, ERROR,
- "PREP_CMD: unknown cmd %#x\n", cmd_no);
- return -1;
- }
-
- return 0;
-}
-
-void mwifiex_uap_set_channel(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg,
- struct cfg80211_chan_def chandef)
-{
- u8 config_bands = 0, old_bands = priv->adapter->config_bands;
-
- priv->bss_chandef = chandef;
-
- bss_cfg->channel = ieee80211_frequency_to_channel(
- chandef.chan->center_freq);
-
- /* Set appropriate bands */
- if (chandef.chan->band == IEEE80211_BAND_2GHZ) {
- bss_cfg->band_cfg = BAND_CONFIG_BG;
- config_bands = BAND_B | BAND_G;
-
- if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
- config_bands |= BAND_GN;
- } else {
- bss_cfg->band_cfg = BAND_CONFIG_A;
- config_bands = BAND_A;
-
- if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
- config_bands |= BAND_AN;
-
- if (chandef.width > NL80211_CHAN_WIDTH_40)
- config_bands |= BAND_AAC;
- }
-
- priv->adapter->config_bands = config_bands;
-
- if (old_bands != config_bands) {
- mwifiex_send_domain_info_cmd_fw(priv->adapter->wiphy);
- mwifiex_dnld_txpwr_table(priv);
- }
-}
-
-int mwifiex_config_start_uap(struct mwifiex_private *priv,
- struct mwifiex_uap_bss_param *bss_cfg)
-{
- enum state_11d_t state_11d;
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
- HostCmd_ACT_GEN_SET,
- UAP_BSS_PARAMS_I, bss_cfg, false)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to set the SSID\n");
- return -1;
- }
-
- /* Send cmd to FW to enable 11D function */
- state_11d = ENABLE_11D;
- if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_SET, DOT11D_I,
- &state_11d, true)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "11D: failed to enable 11D\n");
- return -1;
- }
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
- HostCmd_ACT_GEN_SET, 0, NULL, false)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Failed to start the BSS\n");
- return -1;
- }
-
- if (priv->sec_info.wep_enabled)
- priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
- else
- priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
-
- if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &priv->curr_pkt_filter, true))
- return -1;
-
- return 0;
-}
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
deleted file mode 100644
index 74d5d7238633..000000000000
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Marvell Wireless LAN device driver: AP TX and RX data handling
- *
- * Copyright (C) 2012-2014, Marvell International Ltd.
- *
- * This software file (the "File") is distributed by Marvell International
- * Ltd. under the terms of the GNU General Public License Version 2, June 1991
- * (the "License"). You may use, redistribute and/or modify this File in
- * accordance with the terms and conditions of the License, a copy of which
- * is available 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 "decl.h"
-#include "ioctl.h"
-#include "main.h"
-#include "wmm.h"
-#include "11n_aggr.h"
-#include "11n_rxreorder.h"
-
-/* This function checks if particular RA list has packets more than low bridge
- * packet threshold and then deletes packet from this RA list.
- * Function deletes packets from such RA list and returns true. If no such list
- * is found, false is returned.
- */
-static bool
-mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
- struct list_head *ra_list_head,
- int tid)
-{
- struct mwifiex_ra_list_tbl *ra_list;
- struct sk_buff *skb, *tmp;
- bool pkt_deleted = false;
- struct mwifiex_txinfo *tx_info;
- struct mwifiex_adapter *adapter = priv->adapter;
-
- list_for_each_entry(ra_list, ra_list_head, list) {
- if (skb_queue_empty(&ra_list->skb_head))
- continue;
-
- skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
- tx_info = MWIFIEX_SKB_TXCB(skb);
- if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
- __skb_unlink(skb, &ra_list->skb_head);
- mwifiex_write_data_complete(adapter, skb, 0,
- -1);
- if (ra_list->tx_paused)
- priv->wmm.pkts_paused[tid]--;
- else
- atomic_dec(&priv->wmm.tx_pkts_queued);
- pkt_deleted = true;
- }
- if ((atomic_read(&adapter->pending_bridged_pkts) <=
- MWIFIEX_BRIDGED_PKTS_THR_LOW))
- break;
- }
- }
-
- return pkt_deleted;
-}
-
-/* This function deletes packets from particular RA List. RA list index
- * from which packets are deleted is preserved so that packets from next RA
- * list are deleted upon subsequent call thus maintaining fairness.
- */
-static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv)
-{
- unsigned long flags;
- struct list_head *ra_list;
- int i;
-
- spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
-
- for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
- if (priv->del_list_idx == MAX_NUM_TID)
- priv->del_list_idx = 0;
- ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
- if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) {
- priv->del_list_idx++;
- break;
- }
- }
-
- spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
-}
-
-
-static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct uap_rxpd *uap_rx_pd;
- struct rx_packet_hdr *rx_pkt_hdr;
- struct sk_buff *new_skb;
- struct mwifiex_txinfo *tx_info;
- int hdr_chop;
- struct ethhdr *p_ethhdr;
- struct mwifiex_sta_node *src_node;
-
- uap_rx_pd = (struct uap_rxpd *)(skb->data);
- rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
-
- if ((atomic_read(&adapter->pending_bridged_pkts) >=
- MWIFIEX_BRIDGED_PKTS_THR_HIGH)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Tx: Bridge packet limit reached. Drop packet!\n");
- kfree_skb(skb);
- mwifiex_uap_cleanup_tx_queues(priv);
- return;
- }
-
- if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
- sizeof(bridge_tunnel_header))) ||
- (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
- sizeof(rfc1042_header)) &&
- ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP &&
- ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) {
- /* Replace the 803 header and rfc1042 header (llc/snap) with
- * an Ethernet II header, keep the src/dst and snap_type
- * (ethertype).
- *
- * The firmware only passes up SNAP frames converting all RX
- * data from 802.11 to 802.2/LLC/SNAP frames.
- *
- * To create the Ethernet II, just move the src, dst address
- * right before the snap_type.
- */
- p_ethhdr = (struct ethhdr *)
- ((u8 *)(&rx_pkt_hdr->eth803_hdr)
- + sizeof(rx_pkt_hdr->eth803_hdr)
- + sizeof(rx_pkt_hdr->rfc1042_hdr)
- - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
- - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
- - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
- memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
- sizeof(p_ethhdr->h_source));
- memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
- sizeof(p_ethhdr->h_dest));
- /* Chop off the rxpd + the excess memory from
- * 802.2/llc/snap header that was removed.
- */
- hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd;
- } else {
- /* Chop off the rxpd */
- hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
- }
-
- /* Chop off the leading header bytes so that it points
- * to the start of either the reconstructed EthII frame
- * or the 802.2/llc/snap frame.
- */
- skb_pull(skb, hdr_chop);
-
- if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
- mwifiex_dbg(priv->adapter, ERROR,
- "data: Tx: insufficient skb headroom %d\n",
- skb_headroom(skb));
- /* Insufficient skb headroom - allocate a new skb */
- new_skb =
- skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
- if (unlikely(!new_skb)) {
- mwifiex_dbg(priv->adapter, ERROR,
- "Tx: cannot allocate new_skb\n");
- kfree_skb(skb);
- priv->stats.tx_dropped++;
- return;
- }
-
- kfree_skb(skb);
- skb = new_skb;
- mwifiex_dbg(priv->adapter, INFO,
- "info: new skb headroom %d\n",
- skb_headroom(skb));
- }
-
- tx_info = MWIFIEX_SKB_TXCB(skb);
- memset(tx_info, 0, sizeof(*tx_info));
- tx_info->bss_num = priv->bss_num;
- tx_info->bss_type = priv->bss_type;
- tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT;
-
- src_node = mwifiex_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source);
- if (src_node) {
- src_node->stats.last_rx = jiffies;
- src_node->stats.rx_bytes += skb->len;
- src_node->stats.rx_packets++;
- src_node->stats.last_tx_rate = uap_rx_pd->rx_rate;
- src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info;
- }
-
- if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) {
- /* Update bridge packet statistics as the
- * packet is not going to kernel/upper layer.
- */
- priv->stats.rx_bytes += skb->len;
- priv->stats.rx_packets++;
-
- /* Sending bridge packet to TX queue, so save the packet
- * length in TXCB to update statistics in TX complete.
- */
- tx_info->pkt_len = skb->len;
- }
-
- __net_timestamp(skb);
- mwifiex_wmm_add_buf_txqueue(priv, skb);
- atomic_inc(&adapter->tx_pending);
- atomic_inc(&adapter->pending_bridged_pkts);
-
- return;
-}
-
-/*
- * This function contains logic for AP packet forwarding.
- *
- * If a packet is multicast/broadcast, it is sent to kernel/upper layer
- * as well as queued back to AP TX queue so that it can be sent to other
- * associated stations.
- * If a packet is unicast and RA is present in associated station list,
- * it is again requeued into AP TX queue.
- * If a packet is unicast and RA is not in associated station list,
- * packet is forwarded to kernel to handle routing logic.
- */
-int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct uap_rxpd *uap_rx_pd;
- struct rx_packet_hdr *rx_pkt_hdr;
- u8 ra[ETH_ALEN];
- struct sk_buff *skb_uap;
-
- uap_rx_pd = (struct uap_rxpd *)(skb->data);
- rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
-
- /* don't do packet forwarding in disconnected state */
- if (!priv->media_connected) {
- mwifiex_dbg(adapter, ERROR,
- "drop packet in disconnected state.\n");
- dev_kfree_skb_any(skb);
- return 0;
- }
-
- memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN);
-
- if (is_multicast_ether_addr(ra)) {
- skb_uap = skb_copy(skb, GFP_ATOMIC);
- mwifiex_uap_queue_bridged_pkt(priv, skb_uap);
- } else {
- if (mwifiex_get_sta_entry(priv, ra)) {
- /* Requeue Intra-BSS packet */
- mwifiex_uap_queue_bridged_pkt(priv, skb);
- return 0;
- }
- }
-
- /* Forward unicat/Inter-BSS packets to kernel. */
- return mwifiex_process_rx_packet(priv, skb);
-}
-
-/*
- * This function processes the packet received on AP interface.
- *
- * The function looks into the RxPD and performs sanity tests on the
- * received buffer to ensure its a valid packet before processing it
- * further. If the packet is determined to be aggregated, it is
- * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic.
- *
- * The completion callback is called after processing is complete.
- */
-int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- int ret;
- struct uap_rxpd *uap_rx_pd;
- struct rx_packet_hdr *rx_pkt_hdr;
- u16 rx_pkt_type;
- u8 ta[ETH_ALEN], pkt_type;
- unsigned long flags;
- struct mwifiex_sta_node *node;
-
- uap_rx_pd = (struct uap_rxpd *)(skb->data);
- rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type);
- rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
-
- ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source);
-
- if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
- le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) {
- mwifiex_dbg(adapter, ERROR,
- "wrong rx packet: len=%d, offset=%d, length=%d\n",
- skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset),
- le16_to_cpu(uap_rx_pd->rx_pkt_length));
- priv->stats.rx_dropped++;
-
- node = mwifiex_get_sta_entry(priv, ta);
- if (node)
- node->stats.tx_failed++;
-
- dev_kfree_skb_any(skb);
- return 0;
- }
-
- if (rx_pkt_type == PKT_TYPE_MGMT) {
- ret = mwifiex_process_mgmt_packet(priv, skb);
- if (ret)
- mwifiex_dbg(adapter, ERROR,
- "Rx of mgmt packet failed");
- dev_kfree_skb_any(skb);
- return ret;
- }
-
-
- if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- node = mwifiex_get_sta_entry(priv, ta);
- if (node)
- node->rx_seq[uap_rx_pd->priority] =
- le16_to_cpu(uap_rx_pd->seq_num);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- }
-
- if (!priv->ap_11n_enabled ||
- (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) &&
- (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) {
- ret = mwifiex_handle_uap_rx_forward(priv, skb);
- return ret;
- }
-
- /* Reorder and send to kernel */
- pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type);
- ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num),
- uap_rx_pd->priority, ta, pkt_type,
- skb);
-
- if (ret || (rx_pkt_type == PKT_TYPE_BAR))
- dev_kfree_skb_any(skb);
-
- if (ret)
- priv->stats.rx_dropped++;
-
- return ret;
-}
-
-/*
- * This function fills the TxPD for AP tx packets.
- *
- * The Tx buffer received by this function should already have the
- * header space allocated for TxPD.
- *
- * This function inserts the TxPD in between interface header and actual
- * data and adjusts the buffer pointers accordingly.
- *
- * The following TxPD fields are set by this function, as required -
- * - BSS number
- * - Tx packet length and offset
- * - Priority
- * - Packet delay
- * - Priority specific Tx control
- * - Flags
- */
-void *mwifiex_process_uap_txpd(struct mwifiex_private *priv,
- struct sk_buff *skb)
-{
- struct mwifiex_adapter *adapter = priv->adapter;
- struct uap_txpd *txpd;
- struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
- int pad;
- u16 pkt_type, pkt_offset;
- int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 :
- INTF_HEADER_LEN;
-
- if (!skb->len) {
- mwifiex_dbg(adapter, ERROR,
- "Tx: bad packet length: %d\n", skb->len);
- tx_info->status_code = -1;
- return skb->data;
- }
-
- BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN);
-
- pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
-
- pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) &
- (MWIFIEX_DMA_ALIGN_SZ - 1);
-
- skb_push(skb, sizeof(*txpd) + pad);
-
- txpd = (struct uap_txpd *)skb->data;
- memset(txpd, 0, sizeof(*txpd));
- txpd->bss_num = priv->bss_num;
- txpd->bss_type = priv->bss_type;
- txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) +
- pad)));
- txpd->priority = (u8)skb->priority;
-
- txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
-
- if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
- tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
- txpd->tx_token_id = tx_info->ack_frame_id;
- txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
- }
-
- if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
- /*
- * Set the priority specific tx_control field, setting of 0 will
- * cause the default value to be used later in this function.
- */
- txpd->tx_control =
- cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]);
-
- /* Offset of actual data */
- pkt_offset = sizeof(*txpd) + pad;
- if (pkt_type == PKT_TYPE_MGMT) {
- /* Set the packet type and add header for management frame */
- txpd->tx_pkt_type = cpu_to_le16(pkt_type);
- pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE;
- }
-
- txpd->tx_pkt_offset = cpu_to_le16(pkt_offset);
-
- /* make space for INTF_HEADER_LEN */
- skb_push(skb, hroom);
-
- if (!txpd->tx_control)
- /* TxCtrl set by user or default */
- txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
-
- return skb->data;
-}
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
deleted file mode 100644
index ecbb0546cf3e..000000000000
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ /dev/null
@@ -1,2910 +0,0 @@
-/*
- * Copyright (C) 2002 Intersil Americas Inc.
- * (C) 2003,2004 Aurelien Alleaume <slts@free.fr>
- * (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
- * (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <linux/capability.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/if_arp.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/etherdevice.h>
-
-#include <asm/uaccess.h>
-
-#include "prismcompat.h"
-#include "isl_ioctl.h"
-#include "islpci_mgt.h"
-#include "isl_oid.h" /* additional types and defs for isl38xx fw */
-#include "oid_mgt.h"
-
-#include <net/iw_handler.h> /* New driver API */
-
-#define KEY_SIZE_WEP104 13 /* 104/128-bit WEP keys */
-#define KEY_SIZE_WEP40 5 /* 40/64-bit WEP keys */
-/* KEY_SIZE_TKIP should match isl_oid.h, struct obj_key.key[] size */
-#define KEY_SIZE_TKIP 32 /* TKIP keys */
-
-static void prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid,
- u8 *wpa_ie, size_t wpa_ie_len);
-static size_t prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie);
-static int prism54_set_wpa(struct net_device *, struct iw_request_info *,
- __u32 *, char *);
-
-/* In 500 kbps */
-static const unsigned char scan_rate_list[] = { 2, 4, 11, 22,
- 12, 18, 24, 36,
- 48, 72, 96, 108 };
-
-/**
- * prism54_mib_mode_helper - MIB change mode helper function
- * @mib: the &struct islpci_mib object to modify
- * @iw_mode: new mode (%IW_MODE_*)
- *
- * This is a helper function, hence it does not lock. Make sure
- * caller deals with locking *if* necessary. This function sets the
- * mode-dependent mib values and does the mapping of the Linux
- * Wireless API modes to Device firmware modes. It also checks for
- * correct valid Linux wireless modes.
- */
-static int
-prism54_mib_mode_helper(islpci_private *priv, u32 iw_mode)
-{
- u32 config = INL_CONFIG_MANUALRUN;
- u32 mode, bsstype;
-
- /* For now, just catch early the Repeater and Secondary modes here */
- if (iw_mode == IW_MODE_REPEAT || iw_mode == IW_MODE_SECOND) {
- printk(KERN_DEBUG
- "%s(): Sorry, Repeater mode and Secondary mode "
- "are not yet supported by this driver.\n", __func__);
- return -EINVAL;
- }
-
- priv->iw_mode = iw_mode;
-
- switch (iw_mode) {
- case IW_MODE_AUTO:
- mode = INL_MODE_CLIENT;
- bsstype = DOT11_BSSTYPE_ANY;
- break;
- case IW_MODE_ADHOC:
- mode = INL_MODE_CLIENT;
- bsstype = DOT11_BSSTYPE_IBSS;
- break;
- case IW_MODE_INFRA:
- mode = INL_MODE_CLIENT;
- bsstype = DOT11_BSSTYPE_INFRA;
- break;
- case IW_MODE_MASTER:
- mode = INL_MODE_AP;
- bsstype = DOT11_BSSTYPE_INFRA;
- break;
- case IW_MODE_MONITOR:
- mode = INL_MODE_PROMISCUOUS;
- bsstype = DOT11_BSSTYPE_ANY;
- config |= INL_CONFIG_RXANNEX;
- break;
- default:
- return -EINVAL;
- }
-
- if (init_wds)
- config |= INL_CONFIG_WDS;
- mgt_set(priv, DOT11_OID_BSSTYPE, &bsstype);
- mgt_set(priv, OID_INL_CONFIG, &config);
- mgt_set(priv, OID_INL_MODE, &mode);
-
- return 0;
-}
-
-/**
- * prism54_mib_init - fill MIB cache with defaults
- *
- * this function initializes the struct given as @mib with defaults,
- * of which many are retrieved from the global module parameter
- * variables.
- */
-
-void
-prism54_mib_init(islpci_private *priv)
-{
- u32 channel, authen, wep, filter, dot1x, mlme, conformance, power, mode;
- struct obj_buffer psm_buffer = {
- .size = PSM_BUFFER_SIZE,
- .addr = priv->device_psm_buffer
- };
-
- channel = CARD_DEFAULT_CHANNEL;
- authen = CARD_DEFAULT_AUTHEN;
- wep = CARD_DEFAULT_WEP;
- filter = CARD_DEFAULT_FILTER; /* (0) Do not filter un-encrypted data */
- dot1x = CARD_DEFAULT_DOT1X;
- mlme = CARD_DEFAULT_MLME_MODE;
- conformance = CARD_DEFAULT_CONFORMANCE;
- power = 127;
- mode = CARD_DEFAULT_IW_MODE;
-
- mgt_set(priv, DOT11_OID_CHANNEL, &channel);
- mgt_set(priv, DOT11_OID_AUTHENABLE, &authen);
- mgt_set(priv, DOT11_OID_PRIVACYINVOKED, &wep);
- mgt_set(priv, DOT11_OID_PSMBUFFER, &psm_buffer);
- mgt_set(priv, DOT11_OID_EXUNENCRYPTED, &filter);
- mgt_set(priv, DOT11_OID_DOT1XENABLE, &dot1x);
- mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlme);
- mgt_set(priv, OID_INL_DOT11D_CONFORMANCE, &conformance);
- mgt_set(priv, OID_INL_OUTPUTPOWER, &power);
-
- /* This sets all of the mode-dependent values */
- prism54_mib_mode_helper(priv, mode);
-}
-
-/* this will be executed outside of atomic context thanks to
- * schedule_work(), thus we can as well use sleeping semaphore
- * locking */
-void
-prism54_update_stats(struct work_struct *work)
-{
- islpci_private *priv = container_of(work, islpci_private, stats_work);
- char *data;
- int j;
- struct obj_bss bss, *bss2;
- union oid_res_t r;
-
- mutex_lock(&priv->stats_lock);
-
-/* Noise floor.
- * I'm not sure if the unit is dBm.
- * Note : If we are not connected, this value seems to be irrelevant. */
-
- mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
- priv->local_iwstatistics.qual.noise = r.u;
-
-/* Get the rssi of the link. To do this we need to retrieve a bss. */
-
- /* First get the MAC address of the AP we are associated with. */
- mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
- data = r.ptr;
-
- /* copy this MAC to the bss */
- memcpy(bss.address, data, ETH_ALEN);
- kfree(data);
-
- /* now ask for the corresponding bss */
- j = mgt_get_request(priv, DOT11_OID_BSSFIND, 0, (void *) &bss, &r);
- bss2 = r.ptr;
- /* report the rssi and use it to calculate
- * link quality through a signal-noise
- * ratio */
- priv->local_iwstatistics.qual.level = bss2->rssi;
- priv->local_iwstatistics.qual.qual =
- bss2->rssi - priv->iwstatistics.qual.noise;
-
- kfree(bss2);
-
- /* report that the stats are new */
- priv->local_iwstatistics.qual.updated = 0x7;
-
-/* Rx : unable to decrypt the MPDU */
- mgt_get_request(priv, DOT11_OID_PRIVRXFAILED, 0, NULL, &r);
- priv->local_iwstatistics.discard.code = r.u;
-
-/* Tx : Max MAC retries num reached */
- mgt_get_request(priv, DOT11_OID_MPDUTXFAILED, 0, NULL, &r);
- priv->local_iwstatistics.discard.retries = r.u;
-
- mutex_unlock(&priv->stats_lock);
-}
-
-struct iw_statistics *
-prism54_get_wireless_stats(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- /* If the stats are being updated return old data */
- if (mutex_trylock(&priv->stats_lock)) {
- memcpy(&priv->iwstatistics, &priv->local_iwstatistics,
- sizeof (struct iw_statistics));
- /* They won't be marked updated for the next time */
- priv->local_iwstatistics.qual.updated = 0;
- mutex_unlock(&priv->stats_lock);
- } else
- priv->iwstatistics.qual.updated = 0;
-
- /* Update our wireless stats, but do not schedule to often
- * (max 1 HZ) */
- if ((priv->stats_timestamp == 0) ||
- time_after(jiffies, priv->stats_timestamp + 1 * HZ)) {
- schedule_work(&priv->stats_work);
- priv->stats_timestamp = jiffies;
- }
-
- return &priv->iwstatistics;
-}
-
-static int
-prism54_commit(struct net_device *ndev, struct iw_request_info *info,
- char *cwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- /* simply re-set the last set SSID, this should commit most stuff */
-
- /* Commit in Monitor mode is not necessary, also setting essid
- * in Monitor mode does not make sense and isn't allowed for this
- * device's firmware */
- if (priv->iw_mode != IW_MODE_MONITOR)
- return mgt_set_request(priv, DOT11_OID_SSID, 0, NULL);
- return 0;
-}
-
-static int
-prism54_get_name(struct net_device *ndev, struct iw_request_info *info,
- char *cwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- char *capabilities;
- union oid_res_t r;
- int rvalue;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT) {
- strncpy(cwrq, "NOT READY!", IFNAMSIZ);
- return 0;
- }
- rvalue = mgt_get_request(priv, OID_INL_PHYCAPABILITIES, 0, NULL, &r);
-
- switch (r.u) {
- case INL_PHYCAP_5000MHZ:
- capabilities = "IEEE 802.11a/b/g";
- break;
- case INL_PHYCAP_FAA:
- capabilities = "IEEE 802.11b/g - FAA Support";
- break;
- case INL_PHYCAP_2400MHZ:
- default:
- capabilities = "IEEE 802.11b/g"; /* Default */
- break;
- }
- strncpy(cwrq, capabilities, IFNAMSIZ);
- return rvalue;
-}
-
-static int
-prism54_set_freq(struct net_device *ndev, struct iw_request_info *info,
- struct iw_freq *fwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- int rvalue;
- u32 c;
-
- if (fwrq->m < 1000)
- /* we have a channel number */
- c = fwrq->m;
- else
- c = (fwrq->e == 1) ? channel_of_freq(fwrq->m / 100000) : 0;
-
- rvalue = c ? mgt_set_request(priv, DOT11_OID_CHANNEL, 0, &c) : -EINVAL;
-
- /* Call commit handler */
- return (rvalue ? rvalue : -EINPROGRESS);
-}
-
-static int
-prism54_get_freq(struct net_device *ndev, struct iw_request_info *info,
- struct iw_freq *fwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- union oid_res_t r;
- int rvalue;
-
- rvalue = mgt_get_request(priv, DOT11_OID_CHANNEL, 0, NULL, &r);
- fwrq->i = r.u;
- rvalue |= mgt_get_request(priv, DOT11_OID_FREQUENCY, 0, NULL, &r);
- fwrq->m = r.u;
- fwrq->e = 3;
-
- return rvalue;
-}
-
-static int
-prism54_set_mode(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- u32 mlmeautolevel = CARD_DEFAULT_MLME_MODE;
-
- /* Let's see if the user passed a valid Linux Wireless mode */
- if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) {
- printk(KERN_DEBUG
- "%s: %s() You passed a non-valid init_mode.\n",
- priv->ndev->name, __func__);
- return -EINVAL;
- }
-
- down_write(&priv->mib_sem);
-
- if (prism54_mib_mode_helper(priv, *uwrq)) {
- up_write(&priv->mib_sem);
- return -EOPNOTSUPP;
- }
-
- /* the ACL code needs an intermediate mlmeautolevel. The wpa stuff an
- * extended one.
- */
- if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN))
- mlmeautolevel = DOT11_MLME_INTERMEDIATE;
- if (priv->wpa)
- mlmeautolevel = DOT11_MLME_EXTENDED;
-
- mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
-
- if (mgt_commit(priv)) {
- up_write(&priv->mib_sem);
- return -EIO;
- }
- priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR)
- ? priv->monitor_type : ARPHRD_ETHER;
- up_write(&priv->mib_sem);
-
- return 0;
-}
-
-/* Use mib cache */
-static int
-prism54_get_mode(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- BUG_ON((priv->iw_mode < IW_MODE_AUTO) || (priv->iw_mode >
- IW_MODE_MONITOR));
- *uwrq = priv->iw_mode;
-
- return 0;
-}
-
-/* we use DOT11_OID_EDTHRESHOLD. From what I guess the card will not try to
- * emit data if (sensitivity > rssi - noise) (in dBm).
- * prism54_set_sens does not seem to work.
- */
-
-static int
-prism54_set_sens(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- u32 sens;
-
- /* by default the card sets this to 20. */
- sens = vwrq->disabled ? 20 : vwrq->value;
-
- return mgt_set_request(priv, DOT11_OID_EDTHRESHOLD, 0, &sens);
-}
-
-static int
-prism54_get_sens(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- union oid_res_t r;
- int rvalue;
-
- rvalue = mgt_get_request(priv, DOT11_OID_EDTHRESHOLD, 0, NULL, &r);
-
- vwrq->value = r.u;
- vwrq->disabled = (vwrq->value == 0);
- vwrq->fixed = 1;
-
- return rvalue;
-}
-
-static int
-prism54_get_range(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct iw_range *range = (struct iw_range *) extra;
- islpci_private *priv = netdev_priv(ndev);
- u8 *data;
- int i, m, rvalue;
- struct obj_frequencies *freq;
- union oid_res_t r;
-
- memset(range, 0, sizeof (struct iw_range));
- dwrq->length = sizeof (struct iw_range);
-
- /* set the wireless extension version number */
- range->we_version_source = SUPPORTED_WIRELESS_EXT;
- range->we_version_compiled = WIRELESS_EXT;
-
- /* Now the encoding capabilities */
- range->num_encoding_sizes = 3;
- /* 64(40) bits WEP */
- range->encoding_size[0] = 5;
- /* 128(104) bits WEP */
- range->encoding_size[1] = 13;
- /* 256 bits for WPA-PSK */
- range->encoding_size[2] = 32;
- /* 4 keys are allowed */
- range->max_encoding_tokens = 4;
-
- /* we don't know the quality range... */
- range->max_qual.level = 0;
- range->max_qual.noise = 0;
- range->max_qual.qual = 0;
- /* these value describe an average quality. Needs more tweaking... */
- range->avg_qual.level = -80; /* -80 dBm */
- range->avg_qual.noise = 0; /* don't know what to put here */
- range->avg_qual.qual = 0;
-
- range->sensitivity = 200;
-
- /* retry limit capabilities */
- range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
- range->retry_flags = IW_RETRY_LIMIT;
- range->r_time_flags = IW_RETRY_LIFETIME;
-
- /* I don't know the range. Put stupid things here */
- range->min_retry = 1;
- range->max_retry = 65535;
- range->min_r_time = 1024;
- range->max_r_time = 65535 * 1024;
-
- /* txpower is supported in dBm's */
- range->txpower_capa = IW_TXPOW_DBM;
-
- /* Event capability (kernel + driver) */
- range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
- IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
- IW_EVENT_CAPA_MASK(SIOCGIWAP));
- range->event_capa[1] = IW_EVENT_CAPA_K_1;
- range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVCUSTOM);
-
- range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
- IW_ENC_CAPA_CIPHER_TKIP;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return 0;
-
- /* Request the device for the supported frequencies
- * not really relevant since some devices will report the 5 GHz band
- * frequencies even if they don't support them.
- */
- rvalue =
- mgt_get_request(priv, DOT11_OID_SUPPORTEDFREQUENCIES, 0, NULL, &r);
- freq = r.ptr;
-
- range->num_channels = freq->nr;
- range->num_frequency = freq->nr;
-
- m = min(IW_MAX_FREQUENCIES, (int) freq->nr);
- for (i = 0; i < m; i++) {
- range->freq[i].m = freq->mhz[i];
- range->freq[i].e = 6;
- range->freq[i].i = channel_of_freq(freq->mhz[i]);
- }
- kfree(freq);
-
- rvalue |= mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r);
- data = r.ptr;
-
- /* We got an array of char. It is NULL terminated. */
- i = 0;
- while ((i < IW_MAX_BITRATES) && (*data != 0)) {
- /* the result must be in bps. The card gives us 500Kbps */
- range->bitrate[i] = *data * 500000;
- i++;
- data++;
- }
- range->num_bitrates = i;
- kfree(r.ptr);
-
- return rvalue;
-}
-
-/* Set AP address*/
-
-static int
-prism54_set_wap(struct net_device *ndev, struct iw_request_info *info,
- struct sockaddr *awrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- char bssid[6];
- int rvalue;
-
- if (awrq->sa_family != ARPHRD_ETHER)
- return -EINVAL;
-
- /* prepare the structure for the set object */
- memcpy(&bssid[0], awrq->sa_data, ETH_ALEN);
-
- /* set the bssid -- does this make sense when in AP mode? */
- rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid);
-
- return (rvalue ? rvalue : -EINPROGRESS); /* Call commit handler */
-}
-
-/* get AP address*/
-
-static int
-prism54_get_wap(struct net_device *ndev, struct iw_request_info *info,
- struct sockaddr *awrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- union oid_res_t r;
- int rvalue;
-
- rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
- memcpy(awrq->sa_data, r.ptr, ETH_ALEN);
- awrq->sa_family = ARPHRD_ETHER;
- kfree(r.ptr);
-
- return rvalue;
-}
-
-static int
-prism54_set_scan(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- /* hehe the device does this automagicaly */
- return 0;
-}
-
-/* a little helper that will translate our data into a card independent
- * format that the Wireless Tools will understand. This was inspired by
- * the "Aironet driver for 4500 and 4800 series cards" (GPL)
- */
-
-static char *
-prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info,
- char *current_ev, char *end_buf, struct obj_bss *bss,
- char noise)
-{
- struct iw_event iwe; /* Temporary buffer */
- short cap;
- islpci_private *priv = netdev_priv(ndev);
- u8 wpa_ie[MAX_WPA_IE_LEN];
- size_t wpa_ie_len;
-
- /* The first entry must be the MAC address */
- memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN);
- iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
- iwe.cmd = SIOCGIWAP;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_ADDR_LEN);
-
- /* The following entries will be displayed in the same order we give them */
-
- /* The ESSID. */
- iwe.u.data.length = bss->ssid.length;
- iwe.u.data.flags = 1;
- iwe.cmd = SIOCGIWESSID;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, bss->ssid.octets);
-
- /* Capabilities */
-#define CAP_ESS 0x01
-#define CAP_IBSS 0x02
-#define CAP_CRYPT 0x10
-
- /* Mode */
- cap = bss->capinfo;
- iwe.u.mode = 0;
- if (cap & CAP_ESS)
- iwe.u.mode = IW_MODE_MASTER;
- else if (cap & CAP_IBSS)
- iwe.u.mode = IW_MODE_ADHOC;
- iwe.cmd = SIOCGIWMODE;
- if (iwe.u.mode)
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_UINT_LEN);
-
- /* Encryption capability */
- if (cap & CAP_CRYPT)
- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- else
- iwe.u.data.flags = IW_ENCODE_DISABLED;
- iwe.u.data.length = 0;
- iwe.cmd = SIOCGIWENCODE;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, NULL);
-
- /* Add frequency. (short) bss->channel is the frequency in MHz */
- iwe.u.freq.m = bss->channel;
- iwe.u.freq.e = 6;
- iwe.cmd = SIOCGIWFREQ;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_FREQ_LEN);
-
- /* Add quality statistics */
- iwe.u.qual.level = bss->rssi;
- iwe.u.qual.noise = noise;
- /* do a simple SNR for quality */
- iwe.u.qual.qual = bss->rssi - noise;
- iwe.cmd = IWEVQUAL;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_QUAL_LEN);
-
- /* Add WPA/RSN Information Element, if any */
- wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie);
- if (wpa_ie_len > 0) {
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = min_t(size_t, wpa_ie_len, MAX_WPA_IE_LEN);
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, wpa_ie);
- }
- /* Do the bitrates */
- {
- char *current_val = current_ev + iwe_stream_lcp_len(info);
- int i;
- int mask;
-
- iwe.cmd = SIOCGIWRATE;
- /* Those two flags are ignored... */
- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
-
- /* Parse the bitmask */
- mask = 0x1;
- for(i = 0; i < sizeof(scan_rate_list); i++) {
- if(bss->rates & mask) {
- iwe.u.bitrate.value = (scan_rate_list[i] * 500000);
- current_val = iwe_stream_add_value(
- info, current_ev, current_val,
- end_buf, &iwe, IW_EV_PARAM_LEN);
- }
- mask <<= 1;
- }
- /* Check if we added any event */
- if ((current_val - current_ev) > iwe_stream_lcp_len(info))
- current_ev = current_val;
- }
-
- return current_ev;
-}
-
-static int
-prism54_get_scan(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- int i, rvalue;
- struct obj_bsslist *bsslist;
- u32 noise = 0;
- char *current_ev = extra;
- union oid_res_t r;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT) {
- /* device is not ready, fail gently */
- dwrq->length = 0;
- return 0;
- }
-
- /* first get the noise value. We will use it to report the link quality */
- rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
- noise = r.u;
-
- /* Ask the device for a list of known bss.
- * The old API, using SIOCGIWAPLIST, had a hard limit of IW_MAX_AP=64.
- * The new API, using SIOCGIWSCAN, is only limited by the buffer size.
- * WE-14->WE-16, the buffer is limited to IW_SCAN_MAX_DATA bytes.
- * Starting with WE-17, the buffer can be as big as needed.
- * But the device won't repport anything if you change the value
- * of IWMAX_BSS=24. */
-
- rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r);
- bsslist = r.ptr;
-
- /* ok now, scan the list and translate its info */
- for (i = 0; i < (int) bsslist->nr; i++) {
- current_ev = prism54_translate_bss(ndev, info, current_ev,
- extra + dwrq->length,
- &(bsslist->bsslist[i]),
- noise);
-
- /* Check if there is space for one more entry */
- if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
- /* Ask user space to try again with a bigger buffer */
- rvalue = -E2BIG;
- break;
- }
- }
-
- kfree(bsslist);
- dwrq->length = (current_ev - extra);
- dwrq->flags = 0; /* todo */
-
- return rvalue;
-}
-
-static int
-prism54_set_essid(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct obj_ssid essid;
-
- memset(essid.octets, 0, 33);
-
- /* Check if we were asked for `any' */
- if (dwrq->flags && dwrq->length) {
- if (dwrq->length > 32)
- return -E2BIG;
- essid.length = dwrq->length;
- memcpy(essid.octets, extra, dwrq->length);
- } else
- essid.length = 0;
-
- if (priv->iw_mode != IW_MODE_MONITOR)
- return mgt_set_request(priv, DOT11_OID_SSID, 0, &essid);
-
- /* If in monitor mode, just save to mib */
- mgt_set(priv, DOT11_OID_SSID, &essid);
- return 0;
-
-}
-
-static int
-prism54_get_essid(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct obj_ssid *essid;
- union oid_res_t r;
- int rvalue;
-
- rvalue = mgt_get_request(priv, DOT11_OID_SSID, 0, NULL, &r);
- essid = r.ptr;
-
- if (essid->length) {
- dwrq->flags = 1; /* set ESSID to ON for Wireless Extensions */
- /* if it is too big, trunk it */
- dwrq->length = min((u8)IW_ESSID_MAX_SIZE, essid->length);
- } else {
- dwrq->flags = 0;
- dwrq->length = 0;
- }
- essid->octets[dwrq->length] = '\0';
- memcpy(extra, essid->octets, dwrq->length);
- kfree(essid);
-
- return rvalue;
-}
-
-/* Provides no functionality, just completes the ioctl. In essence this is a
- * just a cosmetic ioctl.
- */
-static int
-prism54_set_nick(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- if (dwrq->length > IW_ESSID_MAX_SIZE)
- return -E2BIG;
-
- down_write(&priv->mib_sem);
- memset(priv->nickname, 0, sizeof (priv->nickname));
- memcpy(priv->nickname, extra, dwrq->length);
- up_write(&priv->mib_sem);
-
- return 0;
-}
-
-static int
-prism54_get_nick(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- dwrq->length = 0;
-
- down_read(&priv->mib_sem);
- dwrq->length = strlen(priv->nickname);
- memcpy(extra, priv->nickname, dwrq->length);
- up_read(&priv->mib_sem);
-
- return 0;
-}
-
-/* Set the allowed Bitrates */
-
-static int
-prism54_set_rate(struct net_device *ndev,
- struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
-
- islpci_private *priv = netdev_priv(ndev);
- u32 rate, profile;
- char *data;
- int ret, i;
- union oid_res_t r;
-
- if (vwrq->value == -1) {
- /* auto mode. No limit. */
- profile = 1;
- return mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
- }
-
- ret = mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r);
- if (ret) {
- kfree(r.ptr);
- return ret;
- }
-
- rate = (u32) (vwrq->value / 500000);
- data = r.ptr;
- i = 0;
-
- while (data[i]) {
- if (rate && (data[i] == rate)) {
- break;
- }
- if (vwrq->value == i) {
- break;
- }
- data[i] |= 0x80;
- i++;
- }
-
- if (!data[i]) {
- kfree(r.ptr);
- return -EINVAL;
- }
-
- data[i] |= 0x80;
- data[i + 1] = 0;
-
- /* Now, check if we want a fixed or auto value */
- if (vwrq->fixed) {
- data[0] = data[i];
- data[1] = 0;
- }
-
-/*
- i = 0;
- printk("prism54 rate: ");
- while(data[i]) {
- printk("%u ", data[i]);
- i++;
- }
- printk("0\n");
-*/
- profile = -1;
- ret = mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
- ret |= mgt_set_request(priv, DOT11_OID_EXTENDEDRATES, 0, data);
- ret |= mgt_set_request(priv, DOT11_OID_RATES, 0, data);
-
- kfree(r.ptr);
-
- return ret;
-}
-
-/* Get the current bit rate */
-static int
-prism54_get_rate(struct net_device *ndev,
- struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- int rvalue;
- char *data;
- union oid_res_t r;
-
- /* Get the current bit rate */
- if ((rvalue = mgt_get_request(priv, GEN_OID_LINKSTATE, 0, NULL, &r)))
- return rvalue;
- vwrq->value = r.u * 500000;
-
- /* request the device for the enabled rates */
- rvalue = mgt_get_request(priv, DOT11_OID_RATES, 0, NULL, &r);
- if (rvalue) {
- kfree(r.ptr);
- return rvalue;
- }
- data = r.ptr;
- vwrq->fixed = (data[0] != 0) && (data[1] == 0);
- kfree(r.ptr);
-
- return 0;
-}
-
-static int
-prism54_set_rts(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- return mgt_set_request(priv, DOT11_OID_RTSTHRESH, 0, &vwrq->value);
-}
-
-static int
-prism54_get_rts(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- union oid_res_t r;
- int rvalue;
-
- /* get the rts threshold */
- rvalue = mgt_get_request(priv, DOT11_OID_RTSTHRESH, 0, NULL, &r);
- vwrq->value = r.u;
-
- return rvalue;
-}
-
-static int
-prism54_set_frag(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- return mgt_set_request(priv, DOT11_OID_FRAGTHRESH, 0, &vwrq->value);
-}
-
-static int
-prism54_get_frag(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- union oid_res_t r;
- int rvalue;
-
- rvalue = mgt_get_request(priv, DOT11_OID_FRAGTHRESH, 0, NULL, &r);
- vwrq->value = r.u;
-
- return rvalue;
-}
-
-/* Here we have (min,max) = max retries for (small frames, big frames). Where
- * big frame <=> bigger than the rts threshold
- * small frame <=> smaller than the rts threshold
- * This is not really the behavior expected by the wireless tool but it seems
- * to be a common behavior in other drivers.
- */
-
-static int
-prism54_set_retry(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- u32 slimit = 0, llimit = 0; /* short and long limit */
- u32 lifetime = 0;
- int rvalue = 0;
-
- if (vwrq->disabled)
- /* we cannot disable this feature */
- return -EINVAL;
-
- if (vwrq->flags & IW_RETRY_LIMIT) {
- if (vwrq->flags & IW_RETRY_SHORT)
- slimit = vwrq->value;
- else if (vwrq->flags & IW_RETRY_LONG)
- llimit = vwrq->value;
- else {
- /* we are asked to set both */
- slimit = vwrq->value;
- llimit = vwrq->value;
- }
- }
- if (vwrq->flags & IW_RETRY_LIFETIME)
- /* Wireless tools use us unit while the device uses 1024 us unit */
- lifetime = vwrq->value / 1024;
-
- /* now set what is requested */
- if (slimit)
- rvalue =
- mgt_set_request(priv, DOT11_OID_SHORTRETRIES, 0, &slimit);
- if (llimit)
- rvalue |=
- mgt_set_request(priv, DOT11_OID_LONGRETRIES, 0, &llimit);
- if (lifetime)
- rvalue |=
- mgt_set_request(priv, DOT11_OID_MAXTXLIFETIME, 0,
- &lifetime);
- return rvalue;
-}
-
-static int
-prism54_get_retry(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- union oid_res_t r;
- int rvalue = 0;
- vwrq->disabled = 0; /* It cannot be disabled */
-
- if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
- /* we are asked for the life time */
- rvalue =
- mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r);
- vwrq->value = r.u * 1024;
- vwrq->flags = IW_RETRY_LIFETIME;
- } else if ((vwrq->flags & IW_RETRY_LONG)) {
- /* we are asked for the long retry limit */
- rvalue |=
- mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r);
- vwrq->value = r.u;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- } else {
- /* default. get the short retry limit */
- rvalue |=
- mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r);
- vwrq->value = r.u;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
- }
-
- return rvalue;
-}
-
-static int
-prism54_set_encode(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- int rvalue = 0, force = 0;
- int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
- union oid_res_t r;
-
- /* with the new API, it's impossible to get a NULL pointer.
- * New version of iwconfig set the IW_ENCODE_NOKEY flag
- * when no key is given, but older versions don't. */
-
- if (dwrq->length > 0) {
- /* we have a key to set */
- int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
- int current_index;
- struct obj_key key = { DOT11_PRIV_WEP, 0, "" };
-
- /* get the current key index */
- rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
- current_index = r.u;
- /* Verify that the key is not marked as invalid */
- if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
- if (dwrq->length > KEY_SIZE_TKIP) {
- /* User-provided key data too big */
- return -EINVAL;
- }
- if (dwrq->length > KEY_SIZE_WEP104) {
- /* WPA-PSK TKIP */
- key.type = DOT11_PRIV_TKIP;
- key.length = KEY_SIZE_TKIP;
- } else if (dwrq->length > KEY_SIZE_WEP40) {
- /* WEP 104/128 */
- key.length = KEY_SIZE_WEP104;
- } else {
- /* WEP 40/64 */
- key.length = KEY_SIZE_WEP40;
- }
- memset(key.key, 0, sizeof (key.key));
- memcpy(key.key, extra, dwrq->length);
-
- if ((index < 0) || (index > 3))
- /* no index provided use the current one */
- index = current_index;
-
- /* now send the key to the card */
- rvalue |=
- mgt_set_request(priv, DOT11_OID_DEFKEYX, index,
- &key);
- }
- /*
- * If a valid key is set, encryption should be enabled
- * (user may turn it off later).
- * This is also how "iwconfig ethX key on" works
- */
- if ((index == current_index) && (key.length > 0))
- force = 1;
- } else {
- int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
- if ((index >= 0) && (index <= 3)) {
- /* we want to set the key index */
- rvalue |=
- mgt_set_request(priv, DOT11_OID_DEFKEYID, 0,
- &index);
- } else {
- if (!(dwrq->flags & IW_ENCODE_MODE)) {
- /* we cannot do anything. Complain. */
- return -EINVAL;
- }
- }
- }
- /* now read the flags */
- if (dwrq->flags & IW_ENCODE_DISABLED) {
- /* Encoding disabled,
- * authen = DOT11_AUTH_OS;
- * invoke = 0;
- * exunencrypt = 0; */
- }
- if (dwrq->flags & IW_ENCODE_OPEN)
- /* Encode but accept non-encoded packets. No auth */
- invoke = 1;
- if ((dwrq->flags & IW_ENCODE_RESTRICTED) || force) {
- /* Refuse non-encoded packets. Auth */
- authen = DOT11_AUTH_BOTH;
- invoke = 1;
- exunencrypt = 1;
- }
- /* do the change if requested */
- if ((dwrq->flags & IW_ENCODE_MODE) || force) {
- rvalue |=
- mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
- rvalue |=
- mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke);
- rvalue |=
- mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
- &exunencrypt);
- }
- return rvalue;
-}
-
-static int
-prism54_get_encode(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct obj_key *key;
- u32 devindex, index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
- u32 authen = 0, invoke = 0, exunencrypt = 0;
- int rvalue;
- union oid_res_t r;
-
- /* first get the flags */
- rvalue = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
- authen = r.u;
- rvalue |= mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
- invoke = r.u;
- rvalue |= mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
- exunencrypt = r.u;
-
- if (invoke && (authen == DOT11_AUTH_BOTH) && exunencrypt)
- dwrq->flags = IW_ENCODE_RESTRICTED;
- else if ((authen == DOT11_AUTH_OS) && !exunencrypt) {
- if (invoke)
- dwrq->flags = IW_ENCODE_OPEN;
- else
- dwrq->flags = IW_ENCODE_DISABLED;
- } else
- /* The card should not work in this state */
- dwrq->flags = 0;
-
- /* get the current device key index */
- rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
- devindex = r.u;
- /* Now get the key, return it */
- if (index == -1 || index > 3)
- /* no index provided, use the current one */
- index = devindex;
- rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r);
- key = r.ptr;
- dwrq->length = key->length;
- memcpy(extra, key->key, dwrq->length);
- kfree(key);
- /* return the used key index */
- dwrq->flags |= devindex + 1;
-
- return rvalue;
-}
-
-static int
-prism54_get_txpower(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- union oid_res_t r;
- int rvalue;
-
- rvalue = mgt_get_request(priv, OID_INL_OUTPUTPOWER, 0, NULL, &r);
- /* intersil firmware operates in 0.25 dBm (1/4 dBm) */
- vwrq->value = (s32) r.u / 4;
- vwrq->fixed = 1;
- /* radio is not turned of
- * btw: how is possible to turn off only the radio
- */
- vwrq->disabled = 0;
-
- return rvalue;
-}
-
-static int
-prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- s32 u = vwrq->value;
-
- /* intersil firmware operates in 0.25 dBm (1/4) */
- u *= 4;
- if (vwrq->disabled) {
- /* don't know how to disable radio */
- printk(KERN_DEBUG
- "%s: %s() disabling radio is not yet supported.\n",
- priv->ndev->name, __func__);
- return -ENOTSUPP;
- } else if (vwrq->fixed)
- /* currently only fixed value is supported */
- return mgt_set_request(priv, OID_INL_OUTPUTPOWER, 0, &u);
- else {
- printk(KERN_DEBUG
- "%s: %s() auto power will be implemented later.\n",
- priv->ndev->name, __func__);
- return -ENOTSUPP;
- }
-}
-
-static int prism54_set_genie(struct net_device *ndev,
- struct iw_request_info *info,
- struct iw_point *data, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- int alen, ret = 0;
- struct obj_attachment *attach;
-
- if (data->length > MAX_WPA_IE_LEN ||
- (data->length && extra == NULL))
- return -EINVAL;
-
- memcpy(priv->wpa_ie, extra, data->length);
- priv->wpa_ie_len = data->length;
-
- alen = sizeof(*attach) + priv->wpa_ie_len;
- attach = kzalloc(alen, GFP_KERNEL);
- if (attach == NULL)
- return -ENOMEM;
-
-#define WLAN_FC_TYPE_MGMT 0
-#define WLAN_FC_STYPE_ASSOC_REQ 0
-#define WLAN_FC_STYPE_REASSOC_REQ 2
-
- /* Note: endianness is covered by mgt_set_varlen */
- attach->type = (WLAN_FC_TYPE_MGMT << 2) |
- (WLAN_FC_STYPE_ASSOC_REQ << 4);
- attach->id = -1;
- attach->size = priv->wpa_ie_len;
- memcpy(attach->data, extra, priv->wpa_ie_len);
-
- ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach,
- priv->wpa_ie_len);
- if (ret == 0) {
- attach->type = (WLAN_FC_TYPE_MGMT << 2) |
- (WLAN_FC_STYPE_REASSOC_REQ << 4);
-
- ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach,
- priv->wpa_ie_len);
- if (ret == 0)
- printk(KERN_DEBUG "%s: WPA IE Attachment was set\n",
- ndev->name);
- }
-
- kfree(attach);
- return ret;
-}
-
-
-static int prism54_get_genie(struct net_device *ndev,
- struct iw_request_info *info,
- struct iw_point *data, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- int len = priv->wpa_ie_len;
-
- if (len <= 0) {
- data->length = 0;
- return 0;
- }
-
- if (data->length < len)
- return -E2BIG;
-
- data->length = len;
- memcpy(extra, priv->wpa_ie, len);
-
- return 0;
-}
-
-static int prism54_set_auth(struct net_device *ndev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct iw_param *param = &wrqu->param;
- u32 mlmelevel = 0, authen = 0, dot1x = 0;
- u32 exunencrypt = 0, privinvoked = 0, wpa = 0;
- u32 old_wpa;
- int ret = 0;
- union oid_res_t r;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return 0;
-
- /* first get the flags */
- down_write(&priv->mib_sem);
- wpa = old_wpa = priv->wpa;
- up_write(&priv->mib_sem);
- ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
- authen = r.u;
- ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
- privinvoked = r.u;
- ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
- exunencrypt = r.u;
- ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r);
- dot1x = r.u;
- ret = mgt_get_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, NULL, &r);
- mlmelevel = r.u;
-
- if (ret < 0)
- goto out;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- break;
-
- case IW_AUTH_WPA_ENABLED:
- /* Do the same thing as IW_AUTH_WPA_VERSION */
- if (param->value) {
- wpa = 1;
- privinvoked = 1; /* For privacy invoked */
- exunencrypt = 1; /* Filter out all unencrypted frames */
- dot1x = 0x01; /* To enable eap filter */
- mlmelevel = DOT11_MLME_EXTENDED;
- authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */
- } else {
- wpa = 0;
- privinvoked = 0;
- exunencrypt = 0; /* Do not filter un-encrypted data */
- dot1x = 0;
- mlmelevel = DOT11_MLME_AUTO;
- }
- break;
-
- case IW_AUTH_WPA_VERSION:
- if (param->value & IW_AUTH_WPA_VERSION_DISABLED) {
- wpa = 0;
- privinvoked = 0;
- exunencrypt = 0; /* Do not filter un-encrypted data */
- dot1x = 0;
- mlmelevel = DOT11_MLME_AUTO;
- } else {
- if (param->value & IW_AUTH_WPA_VERSION_WPA)
- wpa = 1;
- else if (param->value & IW_AUTH_WPA_VERSION_WPA2)
- wpa = 2;
- privinvoked = 1; /* For privacy invoked */
- exunencrypt = 1; /* Filter out all unencrypted frames */
- dot1x = 0x01; /* To enable eap filter */
- mlmelevel = DOT11_MLME_EXTENDED;
- authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */
- }
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- /* dot1x should be the opposite of RX_UNENCRYPTED_EAPOL;
- * turn off dot1x when allowing receipt of unencrypted EAPOL
- * frames, turn on dot1x when receipt should be disallowed
- */
- dot1x = param->value ? 0 : 0x01;
- break;
-
- case IW_AUTH_PRIVACY_INVOKED:
- privinvoked = param->value ? 1 : 0;
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:
- exunencrypt = param->value ? 1 : 0;
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- if (param->value & IW_AUTH_ALG_SHARED_KEY) {
- /* Only WEP uses _SK and _BOTH */
- if (wpa > 0) {
- ret = -EINVAL;
- goto out;
- }
- authen = DOT11_AUTH_SK;
- } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
- authen = DOT11_AUTH_OS;
- } else {
- ret = -EINVAL;
- goto out;
- }
- break;
-
- default:
- return -EOPNOTSUPP;
- }
-
- /* Set all the values */
- down_write(&priv->mib_sem);
- priv->wpa = wpa;
- up_write(&priv->mib_sem);
- mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
- mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &privinvoked);
- mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &exunencrypt);
- mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x);
- mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlmelevel);
-
-out:
- return ret;
-}
-
-static int prism54_get_auth(struct net_device *ndev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct iw_param *param = &wrqu->param;
- u32 wpa = 0;
- int ret = 0;
- union oid_res_t r;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return 0;
-
- /* first get the flags */
- down_write(&priv->mib_sem);
- wpa = priv->wpa;
- up_write(&priv->mib_sem);
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- /*
- * wpa_supplicant will control these internally
- */
- ret = -EOPNOTSUPP;
- break;
-
- case IW_AUTH_WPA_VERSION:
- switch (wpa) {
- case 1:
- param->value = IW_AUTH_WPA_VERSION_WPA;
- break;
- case 2:
- param->value = IW_AUTH_WPA_VERSION_WPA2;
- break;
- case 0:
- default:
- param->value = IW_AUTH_WPA_VERSION_DISABLED;
- break;
- }
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:
- ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
- if (ret >= 0)
- param->value = r.u > 0 ? 1 : 0;
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
- if (ret >= 0) {
- switch (r.u) {
- case DOT11_AUTH_OS:
- param->value = IW_AUTH_ALG_OPEN_SYSTEM;
- break;
- case DOT11_AUTH_BOTH:
- case DOT11_AUTH_SK:
- param->value = IW_AUTH_ALG_SHARED_KEY;
- break;
- case DOT11_AUTH_NONE:
- default:
- param->value = 0;
- break;
- }
- }
- break;
-
- case IW_AUTH_WPA_ENABLED:
- param->value = wpa > 0 ? 1 : 0;
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r);
- if (ret >= 0)
- param->value = r.u > 0 ? 1 : 0;
- break;
-
- case IW_AUTH_PRIVACY_INVOKED:
- ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
- if (ret >= 0)
- param->value = r.u > 0 ? 1 : 0;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return ret;
-}
-
-static int prism54_set_encodeext(struct net_device *ndev,
- struct iw_request_info *info,
- union iwreq_data *wrqu,
- char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int idx, alg = ext->alg, set_key = 1;
- union oid_res_t r;
- int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
- int ret = 0;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return 0;
-
- /* Determine and validate the key index */
- idx = (encoding->flags & IW_ENCODE_INDEX) - 1;
- if (idx) {
- if (idx < 0 || idx > 3)
- return -EINVAL;
- } else {
- ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
- if (ret < 0)
- goto out;
- idx = r.u;
- }
-
- if (encoding->flags & IW_ENCODE_DISABLED)
- alg = IW_ENCODE_ALG_NONE;
-
- if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
- /* Only set transmit key index here, actual
- * key is set below if needed.
- */
- ret = mgt_set_request(priv, DOT11_OID_DEFKEYID, 0, &idx);
- set_key = ext->key_len > 0 ? 1 : 0;
- }
-
- if (set_key) {
- struct obj_key key = { DOT11_PRIV_WEP, 0, "" };
- switch (alg) {
- case IW_ENCODE_ALG_NONE:
- break;
- case IW_ENCODE_ALG_WEP:
- if (ext->key_len > KEY_SIZE_WEP104) {
- ret = -EINVAL;
- goto out;
- }
- if (ext->key_len > KEY_SIZE_WEP40)
- key.length = KEY_SIZE_WEP104;
- else
- key.length = KEY_SIZE_WEP40;
- break;
- case IW_ENCODE_ALG_TKIP:
- if (ext->key_len > KEY_SIZE_TKIP) {
- ret = -EINVAL;
- goto out;
- }
- key.type = DOT11_PRIV_TKIP;
- key.length = KEY_SIZE_TKIP;
- break;
- default:
- return -EINVAL;
- }
-
- if (key.length) {
- memset(key.key, 0, sizeof(key.key));
- memcpy(key.key, ext->key, ext->key_len);
- ret = mgt_set_request(priv, DOT11_OID_DEFKEYX, idx,
- &key);
- if (ret < 0)
- goto out;
- }
- }
-
- /* Read the flags */
- if (encoding->flags & IW_ENCODE_DISABLED) {
- /* Encoding disabled,
- * authen = DOT11_AUTH_OS;
- * invoke = 0;
- * exunencrypt = 0; */
- }
- if (encoding->flags & IW_ENCODE_OPEN) {
- /* Encode but accept non-encoded packets. No auth */
- invoke = 1;
- }
- if (encoding->flags & IW_ENCODE_RESTRICTED) {
- /* Refuse non-encoded packets. Auth */
- authen = DOT11_AUTH_BOTH;
- invoke = 1;
- exunencrypt = 1;
- }
-
- /* do the change if requested */
- if (encoding->flags & IW_ENCODE_MODE) {
- ret = mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0,
- &authen);
- ret = mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0,
- &invoke);
- ret = mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
- &exunencrypt);
- }
-
-out:
- return ret;
-}
-
-
-static int prism54_get_encodeext(struct net_device *ndev,
- struct iw_request_info *info,
- union iwreq_data *wrqu,
- char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int idx, max_key_len;
- union oid_res_t r;
- int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0, wpa = 0;
- int ret = 0;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return 0;
-
- /* first get the flags */
- ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
- authen = r.u;
- ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
- invoke = r.u;
- ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
- exunencrypt = r.u;
- if (ret < 0)
- goto out;
-
- max_key_len = encoding->length - sizeof(*ext);
- if (max_key_len < 0)
- return -EINVAL;
-
- idx = (encoding->flags & IW_ENCODE_INDEX) - 1;
- if (idx) {
- if (idx < 0 || idx > 3)
- return -EINVAL;
- } else {
- ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
- if (ret < 0)
- goto out;
- idx = r.u;
- }
-
- encoding->flags = idx + 1;
- memset(ext, 0, sizeof(*ext));
-
- switch (authen) {
- case DOT11_AUTH_BOTH:
- case DOT11_AUTH_SK:
- wrqu->encoding.flags |= IW_ENCODE_RESTRICTED;
- case DOT11_AUTH_OS:
- default:
- wrqu->encoding.flags |= IW_ENCODE_OPEN;
- break;
- }
-
- down_write(&priv->mib_sem);
- wpa = priv->wpa;
- up_write(&priv->mib_sem);
-
- if (authen == DOT11_AUTH_OS && !exunencrypt && !invoke && !wpa) {
- /* No encryption */
- ext->alg = IW_ENCODE_ALG_NONE;
- ext->key_len = 0;
- wrqu->encoding.flags |= IW_ENCODE_DISABLED;
- } else {
- struct obj_key *key;
-
- ret = mgt_get_request(priv, DOT11_OID_DEFKEYX, idx, NULL, &r);
- if (ret < 0)
- goto out;
- key = r.ptr;
- if (max_key_len < key->length) {
- ret = -E2BIG;
- goto out;
- }
- memcpy(ext->key, key->key, key->length);
- ext->key_len = key->length;
-
- switch (key->type) {
- case DOT11_PRIV_TKIP:
- ext->alg = IW_ENCODE_ALG_TKIP;
- break;
- default:
- case DOT11_PRIV_WEP:
- ext->alg = IW_ENCODE_ALG_WEP;
- break;
- }
- wrqu->encoding.flags |= IW_ENCODE_ENABLED;
- }
-
-out:
- return ret;
-}
-
-
-static int
-prism54_reset(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_reset(netdev_priv(ndev), 0);
-
- return 0;
-}
-
-static int
-prism54_get_oid(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- union oid_res_t r;
- int rvalue;
- enum oid_num_t n = dwrq->flags;
-
- rvalue = mgt_get_request(netdev_priv(ndev), n, 0, NULL, &r);
- dwrq->length = mgt_response_to_str(n, &r, extra);
- if ((isl_oid[n].flags & OID_FLAG_TYPE) != OID_TYPE_U32)
- kfree(r.ptr);
- return rvalue;
-}
-
-static int
-prism54_set_u32(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- u32 oid = uwrq[0], u = uwrq[1];
-
- return mgt_set_request(netdev_priv(ndev), oid, 0, &u);
-}
-
-static int
-prism54_set_raw(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- u32 oid = dwrq->flags;
-
- return mgt_set_request(netdev_priv(ndev), oid, 0, extra);
-}
-
-void
-prism54_acl_init(struct islpci_acl *acl)
-{
- mutex_init(&acl->lock);
- INIT_LIST_HEAD(&acl->mac_list);
- acl->size = 0;
- acl->policy = MAC_POLICY_OPEN;
-}
-
-static void
-prism54_clear_mac(struct islpci_acl *acl)
-{
- struct list_head *ptr, *next;
- struct mac_entry *entry;
-
- mutex_lock(&acl->lock);
-
- if (acl->size == 0) {
- mutex_unlock(&acl->lock);
- return;
- }
-
- for (ptr = acl->mac_list.next, next = ptr->next;
- ptr != &acl->mac_list; ptr = next, next = ptr->next) {
- entry = list_entry(ptr, struct mac_entry, _list);
- list_del(ptr);
- kfree(entry);
- }
- acl->size = 0;
- mutex_unlock(&acl->lock);
-}
-
-void
-prism54_acl_clean(struct islpci_acl *acl)
-{
- prism54_clear_mac(acl);
-}
-
-static int
-prism54_add_mac(struct net_device *ndev, struct iw_request_info *info,
- struct sockaddr *awrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct islpci_acl *acl = &priv->acl;
- struct mac_entry *entry;
- struct sockaddr *addr = (struct sockaddr *) extra;
-
- if (addr->sa_family != ARPHRD_ETHER)
- return -EOPNOTSUPP;
-
- entry = kmalloc(sizeof (struct mac_entry), GFP_KERNEL);
- if (entry == NULL)
- return -ENOMEM;
-
- memcpy(entry->addr, addr->sa_data, ETH_ALEN);
-
- if (mutex_lock_interruptible(&acl->lock)) {
- kfree(entry);
- return -ERESTARTSYS;
- }
- list_add_tail(&entry->_list, &acl->mac_list);
- acl->size++;
- mutex_unlock(&acl->lock);
-
- return 0;
-}
-
-static int
-prism54_del_mac(struct net_device *ndev, struct iw_request_info *info,
- struct sockaddr *awrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct islpci_acl *acl = &priv->acl;
- struct mac_entry *entry;
- struct sockaddr *addr = (struct sockaddr *) extra;
-
- if (addr->sa_family != ARPHRD_ETHER)
- return -EOPNOTSUPP;
-
- if (mutex_lock_interruptible(&acl->lock))
- return -ERESTARTSYS;
- list_for_each_entry(entry, &acl->mac_list, _list) {
- if (ether_addr_equal(entry->addr, addr->sa_data)) {
- list_del(&entry->_list);
- acl->size--;
- kfree(entry);
- mutex_unlock(&acl->lock);
- return 0;
- }
- }
- mutex_unlock(&acl->lock);
- return -EINVAL;
-}
-
-static int
-prism54_get_mac(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct islpci_acl *acl = &priv->acl;
- struct mac_entry *entry;
- struct sockaddr *dst = (struct sockaddr *) extra;
-
- dwrq->length = 0;
-
- if (mutex_lock_interruptible(&acl->lock))
- return -ERESTARTSYS;
-
- list_for_each_entry(entry, &acl->mac_list, _list) {
- memcpy(dst->sa_data, entry->addr, ETH_ALEN);
- dst->sa_family = ARPHRD_ETHER;
- dwrq->length++;
- dst++;
- }
- mutex_unlock(&acl->lock);
- return 0;
-}
-
-/* Setting policy also clears the MAC acl, even if we don't change the default
- * policy
- */
-
-static int
-prism54_set_policy(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct islpci_acl *acl = &priv->acl;
- u32 mlmeautolevel;
-
- prism54_clear_mac(acl);
-
- if ((*uwrq < MAC_POLICY_OPEN) || (*uwrq > MAC_POLICY_REJECT))
- return -EINVAL;
-
- down_write(&priv->mib_sem);
-
- acl->policy = *uwrq;
-
- /* the ACL code needs an intermediate mlmeautolevel */
- if ((priv->iw_mode == IW_MODE_MASTER) &&
- (acl->policy != MAC_POLICY_OPEN))
- mlmeautolevel = DOT11_MLME_INTERMEDIATE;
- else
- mlmeautolevel = CARD_DEFAULT_MLME_MODE;
- if (priv->wpa)
- mlmeautolevel = DOT11_MLME_EXTENDED;
- mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
- /* restart the card with our new policy */
- if (mgt_commit(priv)) {
- up_write(&priv->mib_sem);
- return -EIO;
- }
- up_write(&priv->mib_sem);
-
- return 0;
-}
-
-static int
-prism54_get_policy(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct islpci_acl *acl = &priv->acl;
-
- *uwrq = acl->policy;
-
- return 0;
-}
-
-/* Return 1 only if client should be accepted. */
-
-static int
-prism54_mac_accept(struct islpci_acl *acl, char *mac)
-{
- struct mac_entry *entry;
- int res = 0;
-
- if (mutex_lock_interruptible(&acl->lock))
- return -ERESTARTSYS;
-
- if (acl->policy == MAC_POLICY_OPEN) {
- mutex_unlock(&acl->lock);
- return 1;
- }
-
- list_for_each_entry(entry, &acl->mac_list, _list) {
- if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
- res = 1;
- break;
- }
- }
- res = (acl->policy == MAC_POLICY_ACCEPT) ? !res : res;
- mutex_unlock(&acl->lock);
-
- return res;
-}
-
-static int
-prism54_kick_all(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct obj_mlme *mlme;
- int rvalue;
-
- mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
- if (mlme == NULL)
- return -ENOMEM;
-
- /* Tell the card to kick every client */
- mlme->id = 0;
- rvalue =
- mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
- kfree(mlme);
-
- return rvalue;
-}
-
-static int
-prism54_kick_mac(struct net_device *ndev, struct iw_request_info *info,
- struct sockaddr *awrq, char *extra)
-{
- struct obj_mlme *mlme;
- struct sockaddr *addr = (struct sockaddr *) extra;
- int rvalue;
-
- if (addr->sa_family != ARPHRD_ETHER)
- return -EOPNOTSUPP;
-
- mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
- if (mlme == NULL)
- return -ENOMEM;
-
- /* Tell the card to only kick the corresponding bastard */
- memcpy(mlme->address, addr->sa_data, ETH_ALEN);
- mlme->id = -1;
- rvalue =
- mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
-
- kfree(mlme);
-
- return rvalue;
-}
-
-/* Translate a TRAP oid into a wireless event. Called in islpci_mgt_receive. */
-
-static void
-format_event(islpci_private *priv, char *dest, const char *str,
- const struct obj_mlme *mlme, u16 *length, int error)
-{
- int n = snprintf(dest, IW_CUSTOM_MAX,
- "%s %s %pM %s (%2.2X)",
- str,
- ((priv->iw_mode == IW_MODE_MASTER) ? "from" : "to"),
- mlme->address,
- (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")
- : ""), mlme->code);
- BUG_ON(n > IW_CUSTOM_MAX);
- *length = n;
-}
-
-static void
-send_formatted_event(islpci_private *priv, const char *str,
- const struct obj_mlme *mlme, int error)
-{
- union iwreq_data wrqu;
- char *memptr;
-
- memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
- if (!memptr)
- return;
- wrqu.data.pointer = memptr;
- wrqu.data.length = 0;
- format_event(priv, memptr, str, mlme, &wrqu.data.length,
- error);
- wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr);
- kfree(memptr);
-}
-
-static void
-send_simple_event(islpci_private *priv, const char *str)
-{
- union iwreq_data wrqu;
- char *memptr;
- int n = strlen(str);
-
- memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
- if (!memptr)
- return;
- BUG_ON(n >= IW_CUSTOM_MAX);
- wrqu.data.pointer = memptr;
- wrqu.data.length = n;
- strcpy(memptr, str);
- wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr);
- kfree(memptr);
-}
-
-static void
-link_changed(struct net_device *ndev, u32 bitrate)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- if (bitrate) {
- netif_carrier_on(ndev);
- if (priv->iw_mode == IW_MODE_INFRA) {
- union iwreq_data uwrq;
- prism54_get_wap(ndev, NULL, (struct sockaddr *) &uwrq,
- NULL);
- wireless_send_event(ndev, SIOCGIWAP, &uwrq, NULL);
- } else
- send_simple_event(netdev_priv(ndev),
- "Link established");
- } else {
- netif_carrier_off(ndev);
- send_simple_event(netdev_priv(ndev), "Link lost");
- }
-}
-
-/* Beacon/ProbeResp payload header */
-struct ieee80211_beacon_phdr {
- u8 timestamp[8];
- u16 beacon_int;
- u16 capab_info;
-} __packed;
-
-#define WLAN_EID_GENERIC 0xdd
-static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 };
-
-static void
-prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid,
- u8 *wpa_ie, size_t wpa_ie_len)
-{
- struct list_head *ptr;
- struct islpci_bss_wpa_ie *bss = NULL;
-
- if (wpa_ie_len > MAX_WPA_IE_LEN)
- wpa_ie_len = MAX_WPA_IE_LEN;
-
- mutex_lock(&priv->wpa_lock);
-
- /* try to use existing entry */
- list_for_each(ptr, &priv->bss_wpa_list) {
- bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
- if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {
- list_move(&bss->list, &priv->bss_wpa_list);
- break;
- }
- bss = NULL;
- }
-
- if (bss == NULL) {
- /* add a new BSS entry; if max number of entries is already
- * reached, replace the least recently updated */
- if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) {
- bss = list_entry(priv->bss_wpa_list.prev,
- struct islpci_bss_wpa_ie, list);
- list_del(&bss->list);
- } else {
- bss = kzalloc(sizeof (*bss), GFP_ATOMIC);
- if (bss != NULL)
- priv->num_bss_wpa++;
- }
- if (bss != NULL) {
- memcpy(bss->bssid, bssid, ETH_ALEN);
- list_add(&bss->list, &priv->bss_wpa_list);
- }
- }
-
- if (bss != NULL) {
- memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);
- bss->wpa_ie_len = wpa_ie_len;
- bss->last_update = jiffies;
- } else {
- printk(KERN_DEBUG "Failed to add BSS WPA entry for "
- "%pM\n", bssid);
- }
-
- /* expire old entries from WPA list */
- while (priv->num_bss_wpa > 0) {
- bss = list_entry(priv->bss_wpa_list.prev,
- struct islpci_bss_wpa_ie, list);
- if (!time_after(jiffies, bss->last_update + 60 * HZ))
- break;
-
- list_del(&bss->list);
- priv->num_bss_wpa--;
- kfree(bss);
- }
-
- mutex_unlock(&priv->wpa_lock);
-}
-
-static size_t
-prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie)
-{
- struct list_head *ptr;
- struct islpci_bss_wpa_ie *bss = NULL;
- size_t len = 0;
-
- mutex_lock(&priv->wpa_lock);
-
- list_for_each(ptr, &priv->bss_wpa_list) {
- bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
- if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
- break;
- bss = NULL;
- }
- if (bss) {
- len = bss->wpa_ie_len;
- memcpy(wpa_ie, bss->wpa_ie, len);
- }
- mutex_unlock(&priv->wpa_lock);
-
- return len;
-}
-
-void
-prism54_wpa_bss_ie_init(islpci_private *priv)
-{
- INIT_LIST_HEAD(&priv->bss_wpa_list);
- mutex_init(&priv->wpa_lock);
-}
-
-void
-prism54_wpa_bss_ie_clean(islpci_private *priv)
-{
- struct islpci_bss_wpa_ie *bss, *n;
-
- list_for_each_entry_safe(bss, n, &priv->bss_wpa_list, list) {
- kfree(bss);
- }
-}
-
-static void
-prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
- u8 *payload, size_t len)
-{
- struct ieee80211_beacon_phdr *hdr;
- u8 *pos, *end;
-
- if (!priv->wpa)
- return;
-
- hdr = (struct ieee80211_beacon_phdr *) payload;
- pos = (u8 *) (hdr + 1);
- end = payload + len;
- while (pos < end) {
- if (pos + 2 + pos[1] > end) {
- printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed "
- "for %pM\n", addr);
- return;
- }
- if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 &&
- memcmp(pos + 2, wpa_oid, 4) == 0) {
- prism54_wpa_bss_ie_add(priv, addr, pos, pos[1] + 2);
- return;
- }
- pos += 2 + pos[1];
- }
-}
-
-static void
-handle_request(islpci_private *priv, struct obj_mlme *mlme, enum oid_num_t oid)
-{
- if (((mlme->state == DOT11_STATE_AUTHING) ||
- (mlme->state == DOT11_STATE_ASSOCING))
- && mgt_mlme_answer(priv)) {
- /* Someone is requesting auth and we must respond. Just send back
- * the trap with error code set accordingly.
- */
- mlme->code = prism54_mac_accept(&priv->acl,
- mlme->address) ? 0 : 1;
- mgt_set_request(priv, oid, 0, mlme);
- }
-}
-
-static int
-prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
- char *data)
-{
- struct obj_mlme *mlme = (struct obj_mlme *) data;
- struct obj_mlmeex *mlmeex = (struct obj_mlmeex *) data;
- struct obj_mlmeex *confirm;
- u8 wpa_ie[MAX_WPA_IE_LEN];
- int wpa_ie_len;
- size_t len = 0; /* u16, better? */
- u8 *payload = NULL, *pos = NULL;
- int ret;
-
- /* I think all trapable objects are listed here.
- * Some oids have a EX version. The difference is that they are emitted
- * in DOT11_MLME_EXTENDED mode (set with DOT11_OID_MLMEAUTOLEVEL)
- * with more info.
- * The few events already defined by the wireless tools are not really
- * suited. We use the more flexible custom event facility.
- */
-
- if (oid >= DOT11_OID_BEACON) {
- len = mlmeex->size;
- payload = pos = mlmeex->data;
- }
-
- /* I fear prism54_process_bss_data won't work with big endian data */
- if ((oid == DOT11_OID_BEACON) || (oid == DOT11_OID_PROBE))
- prism54_process_bss_data(priv, oid, mlmeex->address,
- payload, len);
-
- mgt_le_to_cpu(isl_oid[oid].flags & OID_FLAG_TYPE, (void *) mlme);
-
- switch (oid) {
-
- case GEN_OID_LINKSTATE:
- link_changed(priv->ndev, (u32) *data);
- break;
-
- case DOT11_OID_MICFAILURE:
- send_simple_event(priv, "Mic failure");
- break;
-
- case DOT11_OID_DEAUTHENTICATE:
- send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
- break;
-
- case DOT11_OID_AUTHENTICATE:
- handle_request(priv, mlme, oid);
- send_formatted_event(priv, "Authenticate request", mlme, 1);
- break;
-
- case DOT11_OID_DISASSOCIATE:
- send_formatted_event(priv, "Disassociate request", mlme, 0);
- break;
-
- case DOT11_OID_ASSOCIATE:
- handle_request(priv, mlme, oid);
- send_formatted_event(priv, "Associate request", mlme, 1);
- break;
-
- case DOT11_OID_REASSOCIATE:
- handle_request(priv, mlme, oid);
- send_formatted_event(priv, "ReAssociate request", mlme, 1);
- break;
-
- case DOT11_OID_BEACON:
- send_formatted_event(priv,
- "Received a beacon from an unknown AP",
- mlme, 0);
- break;
-
- case DOT11_OID_PROBE:
- /* we received a probe from a client. */
- send_formatted_event(priv, "Received a probe from client", mlme,
- 0);
- break;
-
- /* Note : "mlme" is actually a "struct obj_mlmeex *" here, but this
- * is backward compatible layout-wise with "struct obj_mlme".
- */
-
- case DOT11_OID_DEAUTHENTICATEEX:
- send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
- break;
-
- case DOT11_OID_AUTHENTICATEEX:
- handle_request(priv, mlme, oid);
- send_formatted_event(priv, "Authenticate request (ex)", mlme, 1);
-
- if (priv->iw_mode != IW_MODE_MASTER
- && mlmeex->state != DOT11_STATE_AUTHING)
- break;
-
- confirm = kmalloc(sizeof(struct obj_mlmeex) + 6, GFP_ATOMIC);
-
- if (!confirm)
- break;
-
- memcpy(&confirm->address, mlmeex->address, ETH_ALEN);
- printk(KERN_DEBUG "Authenticate from: address:\t%pM\n",
- mlmeex->address);
- confirm->id = -1; /* or mlmeex->id ? */
- confirm->state = 0; /* not used */
- confirm->code = 0;
- confirm->size = 6;
- confirm->data[0] = 0x00;
- confirm->data[1] = 0x00;
- confirm->data[2] = 0x02;
- confirm->data[3] = 0x00;
- confirm->data[4] = 0x00;
- confirm->data[5] = 0x00;
-
- ret = mgt_set_varlen(priv, DOT11_OID_ASSOCIATEEX, confirm, 6);
-
- kfree(confirm);
- if (ret)
- return ret;
- break;
-
- case DOT11_OID_DISASSOCIATEEX:
- send_formatted_event(priv, "Disassociate request (ex)", mlme, 0);
- break;
-
- case DOT11_OID_ASSOCIATEEX:
- handle_request(priv, mlme, oid);
- send_formatted_event(priv, "Associate request (ex)", mlme, 1);
-
- if (priv->iw_mode != IW_MODE_MASTER
- && mlmeex->state != DOT11_STATE_ASSOCING)
- break;
-
- confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC);
-
- if (!confirm)
- break;
-
- memcpy(&confirm->address, mlmeex->address, ETH_ALEN);
-
- confirm->id = ((struct obj_mlmeex *)mlme)->id;
- confirm->state = 0; /* not used */
- confirm->code = 0;
-
- wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie);
-
- if (!wpa_ie_len) {
- printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n",
- mlmeex->address);
- kfree(confirm);
- break;
- }
-
- confirm->size = wpa_ie_len;
- memcpy(&confirm->data, wpa_ie, wpa_ie_len);
-
- mgt_set_varlen(priv, oid, confirm, wpa_ie_len);
-
- kfree(confirm);
-
- break;
-
- case DOT11_OID_REASSOCIATEEX:
- handle_request(priv, mlme, oid);
- send_formatted_event(priv, "Reassociate request (ex)", mlme, 1);
-
- if (priv->iw_mode != IW_MODE_MASTER
- && mlmeex->state != DOT11_STATE_ASSOCING)
- break;
-
- confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC);
-
- if (!confirm)
- break;
-
- memcpy(&confirm->address, mlmeex->address, ETH_ALEN);
-
- confirm->id = mlmeex->id;
- confirm->state = 0; /* not used */
- confirm->code = 0;
-
- wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie);
-
- if (!wpa_ie_len) {
- printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n",
- mlmeex->address);
- kfree(confirm);
- break;
- }
-
- confirm->size = wpa_ie_len;
- memcpy(&confirm->data, wpa_ie, wpa_ie_len);
-
- mgt_set_varlen(priv, oid, confirm, wpa_ie_len);
-
- kfree(confirm);
-
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * Process a device trap. This is called via schedule_work(), outside of
- * interrupt context, no locks held.
- */
-void
-prism54_process_trap(struct work_struct *work)
-{
- struct islpci_mgmtframe *frame =
- container_of(work, struct islpci_mgmtframe, ws);
- struct net_device *ndev = frame->ndev;
- enum oid_num_t n = mgt_oidtonum(frame->header->oid);
-
- if (n != OID_NUM_LAST)
- prism54_process_trap_helper(netdev_priv(ndev), n, frame->data);
- islpci_mgt_release(frame);
-}
-
-int
-prism54_set_mac_address(struct net_device *ndev, void *addr)
-{
- islpci_private *priv = netdev_priv(ndev);
- int ret;
-
- if (ndev->addr_len != 6)
- return -EINVAL;
- ret = mgt_set_request(priv, GEN_OID_MACADDRESS, 0,
- &((struct sockaddr *) addr)->sa_data);
- if (!ret)
- memcpy(priv->ndev->dev_addr,
- &((struct sockaddr *) addr)->sa_data, ETH_ALEN);
-
- return ret;
-}
-
-#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12
-
-static int
-prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- u32 mlme, authen, dot1x, filter, wep;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return 0;
-
- wep = 1; /* For privacy invoked */
- filter = 1; /* Filter out all unencrypted frames */
- dot1x = 0x01; /* To enable eap filter */
- mlme = DOT11_MLME_EXTENDED;
- authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */
-
- down_write(&priv->mib_sem);
- priv->wpa = *uwrq;
-
- switch (priv->wpa) {
- default:
- case 0: /* Clears/disables WPA and friends */
- wep = 0;
- filter = 0; /* Do not filter un-encrypted data */
- dot1x = 0;
- mlme = DOT11_MLME_AUTO;
- printk("%s: Disabling WPA\n", ndev->name);
- break;
- case 2:
- case 1: /* WPA */
- printk("%s: Enabling WPA\n", ndev->name);
- break;
- }
- up_write(&priv->mib_sem);
-
- mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
- mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &wep);
- mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &filter);
- mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x);
- mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlme);
-
- return 0;
-}
-
-static int
-prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- *uwrq = priv->wpa;
- return 0;
-}
-
-static int
-prism54_set_prismhdr(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- priv->monitor_type =
- (*uwrq ? ARPHRD_IEEE80211_PRISM : ARPHRD_IEEE80211);
- if (priv->iw_mode == IW_MODE_MONITOR)
- priv->ndev->type = priv->monitor_type;
-
- return 0;
-}
-
-static int
-prism54_get_prismhdr(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- *uwrq = (priv->monitor_type == ARPHRD_IEEE80211_PRISM);
- return 0;
-}
-
-static int
-prism54_debug_oid(struct net_device *ndev, struct iw_request_info *info,
- __u32 * uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- priv->priv_oid = *uwrq;
- printk("%s: oid 0x%08X\n", ndev->name, *uwrq);
-
- return 0;
-}
-
-static int
-prism54_debug_get_oid(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *data, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct islpci_mgmtframe *response;
- int ret = -EIO;
-
- printk("%s: get_oid 0x%08X\n", ndev->name, priv->priv_oid);
- data->length = 0;
-
- if (islpci_get_state(priv) >= PRV_STATE_INIT) {
- ret =
- islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
- priv->priv_oid, extra, 256,
- &response);
- printk("%s: ret: %i\n", ndev->name, ret);
- if (ret || !response
- || response->header->operation == PIMFOR_OP_ERROR) {
- if (response) {
- islpci_mgt_release(response);
- }
- printk("%s: EIO\n", ndev->name);
- ret = -EIO;
- }
- if (!ret) {
- data->length = response->header->length;
- memcpy(extra, response->data, data->length);
- islpci_mgt_release(response);
- printk("%s: len: %i\n", ndev->name, data->length);
- }
- }
-
- return ret;
-}
-
-static int
-prism54_debug_set_oid(struct net_device *ndev, struct iw_request_info *info,
- struct iw_point *data, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- struct islpci_mgmtframe *response;
- int ret = 0, response_op = PIMFOR_OP_ERROR;
-
- printk("%s: set_oid 0x%08X\tlen: %d\n", ndev->name, priv->priv_oid,
- data->length);
-
- if (islpci_get_state(priv) >= PRV_STATE_INIT) {
- ret =
- islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET,
- priv->priv_oid, extra, data->length,
- &response);
- printk("%s: ret: %i\n", ndev->name, ret);
- if (ret || !response
- || response->header->operation == PIMFOR_OP_ERROR) {
- if (response) {
- islpci_mgt_release(response);
- }
- printk("%s: EIO\n", ndev->name);
- ret = -EIO;
- }
- if (!ret) {
- response_op = response->header->operation;
- printk("%s: response_op: %i\n", ndev->name,
- response_op);
- islpci_mgt_release(response);
- }
- }
-
- return (ret ? ret : -EINPROGRESS);
-}
-
-static int
-prism54_set_spy(struct net_device *ndev,
- struct iw_request_info *info,
- union iwreq_data *uwrq, char *extra)
-{
- islpci_private *priv = netdev_priv(ndev);
- u32 u;
- enum oid_num_t oid = OID_INL_CONFIG;
-
- down_write(&priv->mib_sem);
- mgt_get(priv, OID_INL_CONFIG, &u);
-
- if ((uwrq->data.length == 0) && (priv->spy_data.spy_number > 0))
- /* disable spy */
- u &= ~INL_CONFIG_RXANNEX;
- else if ((uwrq->data.length > 0) && (priv->spy_data.spy_number == 0))
- /* enable spy */
- u |= INL_CONFIG_RXANNEX;
-
- mgt_set(priv, OID_INL_CONFIG, &u);
- mgt_commit_list(priv, &oid, 1);
- up_write(&priv->mib_sem);
-
- return iw_handler_set_spy(ndev, info, uwrq, extra);
-}
-
-static const iw_handler prism54_handler[] = {
- (iw_handler) prism54_commit, /* SIOCSIWCOMMIT */
- (iw_handler) prism54_get_name, /* SIOCGIWNAME */
- (iw_handler) NULL, /* SIOCSIWNWID */
- (iw_handler) NULL, /* SIOCGIWNWID */
- (iw_handler) prism54_set_freq, /* SIOCSIWFREQ */
- (iw_handler) prism54_get_freq, /* SIOCGIWFREQ */
- (iw_handler) prism54_set_mode, /* SIOCSIWMODE */
- (iw_handler) prism54_get_mode, /* SIOCGIWMODE */
- (iw_handler) prism54_set_sens, /* SIOCSIWSENS */
- (iw_handler) prism54_get_sens, /* SIOCGIWSENS */
- (iw_handler) NULL, /* SIOCSIWRANGE */
- (iw_handler) prism54_get_range, /* SIOCGIWRANGE */
- (iw_handler) NULL, /* SIOCSIWPRIV */
- (iw_handler) NULL, /* SIOCGIWPRIV */
- (iw_handler) NULL, /* SIOCSIWSTATS */
- (iw_handler) NULL, /* SIOCGIWSTATS */
- prism54_set_spy, /* SIOCSIWSPY */
- iw_handler_get_spy, /* SIOCGIWSPY */
- iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
- iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
- (iw_handler) prism54_set_wap, /* SIOCSIWAP */
- (iw_handler) prism54_get_wap, /* SIOCGIWAP */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* SIOCGIWAPLIST deprecated */
- (iw_handler) prism54_set_scan, /* SIOCSIWSCAN */
- (iw_handler) prism54_get_scan, /* SIOCGIWSCAN */
- (iw_handler) prism54_set_essid, /* SIOCSIWESSID */
- (iw_handler) prism54_get_essid, /* SIOCGIWESSID */
- (iw_handler) prism54_set_nick, /* SIOCSIWNICKN */
- (iw_handler) prism54_get_nick, /* SIOCGIWNICKN */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) prism54_set_rate, /* SIOCSIWRATE */
- (iw_handler) prism54_get_rate, /* SIOCGIWRATE */
- (iw_handler) prism54_set_rts, /* SIOCSIWRTS */
- (iw_handler) prism54_get_rts, /* SIOCGIWRTS */
- (iw_handler) prism54_set_frag, /* SIOCSIWFRAG */
- (iw_handler) prism54_get_frag, /* SIOCGIWFRAG */
- (iw_handler) prism54_set_txpower, /* SIOCSIWTXPOW */
- (iw_handler) prism54_get_txpower, /* SIOCGIWTXPOW */
- (iw_handler) prism54_set_retry, /* SIOCSIWRETRY */
- (iw_handler) prism54_get_retry, /* SIOCGIWRETRY */
- (iw_handler) prism54_set_encode, /* SIOCSIWENCODE */
- (iw_handler) prism54_get_encode, /* SIOCGIWENCODE */
- (iw_handler) NULL, /* SIOCSIWPOWER */
- (iw_handler) NULL, /* SIOCGIWPOWER */
- NULL, /* -- hole -- */
- NULL, /* -- hole -- */
- (iw_handler) prism54_set_genie, /* SIOCSIWGENIE */
- (iw_handler) prism54_get_genie, /* SIOCGIWGENIE */
- (iw_handler) prism54_set_auth, /* SIOCSIWAUTH */
- (iw_handler) prism54_get_auth, /* SIOCGIWAUTH */
- (iw_handler) prism54_set_encodeext, /* SIOCSIWENCODEEXT */
- (iw_handler) prism54_get_encodeext, /* SIOCGIWENCODEEXT */
- NULL, /* SIOCSIWPMKSA */
-};
-
-/* The low order bit identify a SET (0) or a GET (1) ioctl. */
-
-#define PRISM54_RESET SIOCIWFIRSTPRIV
-#define PRISM54_GET_POLICY SIOCIWFIRSTPRIV+1
-#define PRISM54_SET_POLICY SIOCIWFIRSTPRIV+2
-#define PRISM54_GET_MAC SIOCIWFIRSTPRIV+3
-#define PRISM54_ADD_MAC SIOCIWFIRSTPRIV+4
-
-#define PRISM54_DEL_MAC SIOCIWFIRSTPRIV+6
-
-#define PRISM54_KICK_MAC SIOCIWFIRSTPRIV+8
-
-#define PRISM54_KICK_ALL SIOCIWFIRSTPRIV+10
-
-#define PRISM54_GET_WPA SIOCIWFIRSTPRIV+11
-#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12
-
-#define PRISM54_DBG_OID SIOCIWFIRSTPRIV+14
-#define PRISM54_DBG_GET_OID SIOCIWFIRSTPRIV+15
-#define PRISM54_DBG_SET_OID SIOCIWFIRSTPRIV+16
-
-#define PRISM54_GET_OID SIOCIWFIRSTPRIV+17
-#define PRISM54_SET_OID_U32 SIOCIWFIRSTPRIV+18
-#define PRISM54_SET_OID_STR SIOCIWFIRSTPRIV+20
-#define PRISM54_SET_OID_ADDR SIOCIWFIRSTPRIV+22
-
-#define PRISM54_GET_PRISMHDR SIOCIWFIRSTPRIV+23
-#define PRISM54_SET_PRISMHDR SIOCIWFIRSTPRIV+24
-
-#define IWPRIV_SET_U32(n,x) { n, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x }
-#define IWPRIV_SET_SSID(n,x) { n, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x }
-#define IWPRIV_SET_ADDR(n,x) { n, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x }
-#define IWPRIV_GET(n,x) { n, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, "g_"x }
-
-#define IWPRIV_U32(n,x) IWPRIV_SET_U32(n,x), IWPRIV_GET(n,x)
-#define IWPRIV_SSID(n,x) IWPRIV_SET_SSID(n,x), IWPRIV_GET(n,x)
-#define IWPRIV_ADDR(n,x) IWPRIV_SET_ADDR(n,x), IWPRIV_GET(n,x)
-
-/* Note : limited to 128 private ioctls (wireless tools 26) */
-
-static const struct iw_priv_args prism54_private_args[] = {
-/*{ cmd, set_args, get_args, name } */
- {PRISM54_RESET, 0, 0, "reset"},
- {PRISM54_GET_PRISMHDR, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- "get_prismhdr"},
- {PRISM54_SET_PRISMHDR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
- "set_prismhdr"},
- {PRISM54_GET_POLICY, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- "getPolicy"},
- {PRISM54_SET_POLICY, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
- "setPolicy"},
- {PRISM54_GET_MAC, 0, IW_PRIV_TYPE_ADDR | 64, "getMac"},
- {PRISM54_ADD_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
- "addMac"},
- {PRISM54_DEL_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
- "delMac"},
- {PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
- "kickMac"},
- {PRISM54_KICK_ALL, 0, 0, "kickAll"},
- {PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- "get_wpa"},
- {PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
- "set_wpa"},
- {PRISM54_DBG_OID, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
- "dbg_oid"},
- {PRISM54_DBG_GET_OID, 0, IW_PRIV_TYPE_BYTE | 256, "dbg_get_oid"},
- {PRISM54_DBG_SET_OID, IW_PRIV_TYPE_BYTE | 256, 0, "dbg_set_oid"},
- /* --- sub-ioctls handlers --- */
- {PRISM54_GET_OID,
- 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, ""},
- {PRISM54_SET_OID_U32,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ""},
- {PRISM54_SET_OID_STR,
- IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, ""},
- {PRISM54_SET_OID_ADDR,
- IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, ""},
- /* --- sub-ioctls definitions --- */
- IWPRIV_ADDR(GEN_OID_MACADDRESS, "addr"),
- IWPRIV_GET(GEN_OID_LINKSTATE, "linkstate"),
- IWPRIV_U32(DOT11_OID_BSSTYPE, "bsstype"),
- IWPRIV_ADDR(DOT11_OID_BSSID, "bssid"),
- IWPRIV_U32(DOT11_OID_STATE, "state"),
- IWPRIV_U32(DOT11_OID_AID, "aid"),
-
- IWPRIV_SSID(DOT11_OID_SSIDOVERRIDE, "ssidoverride"),
-
- IWPRIV_U32(DOT11_OID_MEDIUMLIMIT, "medlimit"),
- IWPRIV_U32(DOT11_OID_BEACONPERIOD, "beacon"),
- IWPRIV_U32(DOT11_OID_DTIMPERIOD, "dtimperiod"),
-
- IWPRIV_U32(DOT11_OID_AUTHENABLE, "authenable"),
- IWPRIV_U32(DOT11_OID_PRIVACYINVOKED, "privinvok"),
- IWPRIV_U32(DOT11_OID_EXUNENCRYPTED, "exunencrypt"),
-
- IWPRIV_U32(DOT11_OID_REKEYTHRESHOLD, "rekeythresh"),
-
- IWPRIV_U32(DOT11_OID_MAXTXLIFETIME, "maxtxlife"),
- IWPRIV_U32(DOT11_OID_MAXRXLIFETIME, "maxrxlife"),
- IWPRIV_U32(DOT11_OID_ALOFT_FIXEDRATE, "fixedrate"),
- IWPRIV_U32(DOT11_OID_MAXFRAMEBURST, "frameburst"),
- IWPRIV_U32(DOT11_OID_PSM, "psm"),
-
- IWPRIV_U32(DOT11_OID_BRIDGELOCAL, "bridge"),
- IWPRIV_U32(DOT11_OID_CLIENTS, "clients"),
- IWPRIV_U32(DOT11_OID_CLIENTSASSOCIATED, "clientassoc"),
- IWPRIV_U32(DOT11_OID_DOT1XENABLE, "dot1xenable"),
- IWPRIV_U32(DOT11_OID_ANTENNARX, "rxant"),
- IWPRIV_U32(DOT11_OID_ANTENNATX, "txant"),
- IWPRIV_U32(DOT11_OID_ANTENNADIVERSITY, "antdivers"),
- IWPRIV_U32(DOT11_OID_EDTHRESHOLD, "edthresh"),
- IWPRIV_U32(DOT11_OID_PREAMBLESETTINGS, "preamble"),
- IWPRIV_GET(DOT11_OID_RATES, "rates"),
- IWPRIV_U32(DOT11_OID_OUTPUTPOWER, ".11outpower"),
- IWPRIV_GET(DOT11_OID_SUPPORTEDRATES, "supprates"),
- IWPRIV_GET(DOT11_OID_SUPPORTEDFREQUENCIES, "suppfreq"),
-
- IWPRIV_U32(DOT11_OID_NOISEFLOOR, "noisefloor"),
- IWPRIV_GET(DOT11_OID_FREQUENCYACTIVITY, "freqactivity"),
- IWPRIV_U32(DOT11_OID_NONERPPROTECTION, "nonerpprotec"),
- IWPRIV_U32(DOT11_OID_PROFILES, "profile"),
- IWPRIV_GET(DOT11_OID_EXTENDEDRATES, "extrates"),
- IWPRIV_U32(DOT11_OID_MLMEAUTOLEVEL, "mlmelevel"),
-
- IWPRIV_GET(DOT11_OID_BSSS, "bsss"),
- IWPRIV_GET(DOT11_OID_BSSLIST, "bsslist"),
- IWPRIV_U32(OID_INL_MODE, "mode"),
- IWPRIV_U32(OID_INL_CONFIG, "config"),
- IWPRIV_U32(OID_INL_DOT11D_CONFORMANCE, ".11dconform"),
- IWPRIV_GET(OID_INL_PHYCAPABILITIES, "phycapa"),
- IWPRIV_U32(OID_INL_OUTPUTPOWER, "outpower"),
-};
-
-static const iw_handler prism54_private_handler[] = {
- (iw_handler) prism54_reset,
- (iw_handler) prism54_get_policy,
- (iw_handler) prism54_set_policy,
- (iw_handler) prism54_get_mac,
- (iw_handler) prism54_add_mac,
- (iw_handler) NULL,
- (iw_handler) prism54_del_mac,
- (iw_handler) NULL,
- (iw_handler) prism54_kick_mac,
- (iw_handler) NULL,
- (iw_handler) prism54_kick_all,
- (iw_handler) prism54_get_wpa,
- (iw_handler) prism54_set_wpa,
- (iw_handler) NULL,
- (iw_handler) prism54_debug_oid,
- (iw_handler) prism54_debug_get_oid,
- (iw_handler) prism54_debug_set_oid,
- (iw_handler) prism54_get_oid,
- (iw_handler) prism54_set_u32,
- (iw_handler) NULL,
- (iw_handler) prism54_set_raw,
- (iw_handler) NULL,
- (iw_handler) prism54_set_raw,
- (iw_handler) prism54_get_prismhdr,
- (iw_handler) prism54_set_prismhdr,
-};
-
-const struct iw_handler_def prism54_handler_def = {
- .num_standard = ARRAY_SIZE(prism54_handler),
- .num_private = ARRAY_SIZE(prism54_private_handler),
- .num_private_args = ARRAY_SIZE(prism54_private_args),
- .standard = (iw_handler *) prism54_handler,
- .private = (iw_handler *) prism54_private_handler,
- .private_args = (struct iw_priv_args *) prism54_private_args,
- .get_wireless_stats = prism54_get_wireless_stats,
-};
diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c
deleted file mode 100644
index 931cf440ff18..000000000000
--- a/drivers/net/wireless/prism54/islpci_dev.c
+++ /dev/null
@@ -1,963 +0,0 @@
-/*
- * Copyright (C) 2002 Intersil Americas Inc.
- * Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
- * Copyright (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <linux/hardirq.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/pci.h>
-#include <linux/sched.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-#include <linux/if_arp.h>
-
-#include <asm/io.h>
-
-#include "prismcompat.h"
-#include "isl_38xx.h"
-#include "isl_ioctl.h"
-#include "islpci_dev.h"
-#include "islpci_mgt.h"
-#include "islpci_eth.h"
-#include "oid_mgt.h"
-
-#define ISL3877_IMAGE_FILE "isl3877"
-#define ISL3886_IMAGE_FILE "isl3886"
-#define ISL3890_IMAGE_FILE "isl3890"
-MODULE_FIRMWARE(ISL3877_IMAGE_FILE);
-MODULE_FIRMWARE(ISL3886_IMAGE_FILE);
-MODULE_FIRMWARE(ISL3890_IMAGE_FILE);
-
-static int prism54_bring_down(islpci_private *);
-static int islpci_alloc_memory(islpci_private *);
-
-/* Temporary dummy MAC address to use until firmware is loaded.
- * The idea there is that some tools (such as nameif) may query
- * the MAC address before the netdev is 'open'. By using a valid
- * OUI prefix, they can process the netdev properly.
- * Of course, this is not the final/real MAC address. It doesn't
- * matter, as you are suppose to be able to change it anytime via
- * ndev->set_mac_address. Jean II */
-static const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 };
-
-static int
-isl_upload_firmware(islpci_private *priv)
-{
- u32 reg, rc;
- void __iomem *device_base = priv->device_base;
-
- /* clear the RAMBoot and the Reset bit */
- reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
- reg &= ~ISL38XX_CTRL_STAT_RESET;
- reg &= ~ISL38XX_CTRL_STAT_RAMBOOT;
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- wmb();
- udelay(ISL38XX_WRITEIO_DELAY);
-
- /* set the Reset bit without reading the register ! */
- reg |= ISL38XX_CTRL_STAT_RESET;
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- wmb();
- udelay(ISL38XX_WRITEIO_DELAY);
-
- /* clear the Reset bit */
- reg &= ~ISL38XX_CTRL_STAT_RESET;
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- wmb();
-
- /* wait a while for the device to reboot */
- mdelay(50);
-
- {
- const struct firmware *fw_entry = NULL;
- long fw_len;
- const u32 *fw_ptr;
-
- rc = request_firmware(&fw_entry, priv->firmware, PRISM_FW_PDEV);
- if (rc) {
- printk(KERN_ERR
- "%s: request_firmware() failed for '%s'\n",
- "prism54", priv->firmware);
- return rc;
- }
- /* prepare the Direct Memory Base register */
- reg = ISL38XX_DEV_FIRMWARE_ADDRES;
-
- fw_ptr = (u32 *) fw_entry->data;
- fw_len = fw_entry->size;
-
- if (fw_len % 4) {
- printk(KERN_ERR
- "%s: firmware '%s' size is not multiple of 32bit, aborting!\n",
- "prism54", priv->firmware);
- release_firmware(fw_entry);
- return -EILSEQ; /* Illegal byte sequence */;
- }
-
- while (fw_len > 0) {
- long _fw_len =
- (fw_len >
- ISL38XX_MEMORY_WINDOW_SIZE) ?
- ISL38XX_MEMORY_WINDOW_SIZE : fw_len;
- u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN;
-
- /* set the card's base address for writing the data */
- isl38xx_w32_flush(device_base, reg,
- ISL38XX_DIR_MEM_BASE_REG);
- wmb(); /* be paranoid */
-
- /* increment the write address for next iteration */
- reg += _fw_len;
- fw_len -= _fw_len;
-
- /* write the data to the Direct Memory Window 32bit-wise */
- /* memcpy_toio() doesn't guarantee 32bit writes :-| */
- while (_fw_len > 0) {
- /* use non-swapping writel() */
- __raw_writel(*fw_ptr, dev_fw_ptr);
- fw_ptr++, dev_fw_ptr++;
- _fw_len -= 4;
- }
-
- /* flush PCI posting */
- (void) readl(device_base + ISL38XX_PCI_POSTING_FLUSH);
- wmb(); /* be paranoid again */
-
- BUG_ON(_fw_len != 0);
- }
-
- BUG_ON(fw_len != 0);
-
- /* Firmware version is at offset 40 (also for "newmac") */
- printk(KERN_DEBUG "%s: firmware version: %.8s\n",
- priv->ndev->name, fw_entry->data + 40);
-
- release_firmware(fw_entry);
- }
-
- /* now reset the device
- * clear the Reset & ClkRun bit, set the RAMBoot bit */
- reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
- reg &= ~ISL38XX_CTRL_STAT_CLKRUN;
- reg &= ~ISL38XX_CTRL_STAT_RESET;
- reg |= ISL38XX_CTRL_STAT_RAMBOOT;
- isl38xx_w32_flush(device_base, reg, ISL38XX_CTRL_STAT_REG);
- wmb();
- udelay(ISL38XX_WRITEIO_DELAY);
-
- /* set the reset bit latches the host override and RAMBoot bits
- * into the device for operation when the reset bit is reset */
- reg |= ISL38XX_CTRL_STAT_RESET;
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- /* don't do flush PCI posting here! */
- wmb();
- udelay(ISL38XX_WRITEIO_DELAY);
-
- /* clear the reset bit should start the whole circus */
- reg &= ~ISL38XX_CTRL_STAT_RESET;
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- /* don't do flush PCI posting here! */
- wmb();
- udelay(ISL38XX_WRITEIO_DELAY);
-
- return 0;
-}
-
-/******************************************************************************
- Device Interrupt Handler
-******************************************************************************/
-
-irqreturn_t
-islpci_interrupt(int irq, void *config)
-{
- u32 reg;
- islpci_private *priv = config;
- struct net_device *ndev = priv->ndev;
- void __iomem *device = priv->device_base;
- int powerstate = ISL38XX_PSM_POWERSAVE_STATE;
-
- /* lock the interrupt handler */
- spin_lock(&priv->slock);
-
- /* received an interrupt request on a shared IRQ line
- * first check whether the device is in sleep mode */
- reg = readl(device + ISL38XX_CTRL_STAT_REG);
- if (reg & ISL38XX_CTRL_STAT_SLEEPMODE)
- /* device is in sleep mode, IRQ was generated by someone else */
- {
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
-#endif
- spin_unlock(&priv->slock);
- return IRQ_NONE;
- }
-
-
- /* check whether there is any source of interrupt on the device */
- reg = readl(device + ISL38XX_INT_IDENT_REG);
-
- /* also check the contents of the Interrupt Enable Register, because this
- * will filter out interrupt sources from other devices on the same irq ! */
- reg &= readl(device + ISL38XX_INT_EN_REG);
- reg &= ISL38XX_INT_SOURCES;
-
- if (reg != 0) {
- if (islpci_get_state(priv) != PRV_STATE_SLEEP)
- powerstate = ISL38XX_PSM_ACTIVE_STATE;
-
- /* reset the request bits in the Identification register */
- isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG);
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS,
- "IRQ: Identification register 0x%p 0x%x\n", device, reg);
-#endif
-
- /* check for each bit in the register separately */
- if (reg & ISL38XX_INT_IDENT_UPDATE) {
-#if VERBOSE > SHOW_ERROR_MESSAGES
- /* Queue has been updated */
- DEBUG(SHOW_TRACING, "IRQ: Update flag\n");
-
- DEBUG(SHOW_QUEUE_INDEXES,
- "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n",
- le32_to_cpu(priv->control_block->
- driver_curr_frag[0]),
- le32_to_cpu(priv->control_block->
- driver_curr_frag[1]),
- le32_to_cpu(priv->control_block->
- driver_curr_frag[2]),
- le32_to_cpu(priv->control_block->
- driver_curr_frag[3]),
- le32_to_cpu(priv->control_block->
- driver_curr_frag[4]),
- le32_to_cpu(priv->control_block->
- driver_curr_frag[5])
- );
-
- DEBUG(SHOW_QUEUE_INDEXES,
- "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n",
- le32_to_cpu(priv->control_block->
- device_curr_frag[0]),
- le32_to_cpu(priv->control_block->
- device_curr_frag[1]),
- le32_to_cpu(priv->control_block->
- device_curr_frag[2]),
- le32_to_cpu(priv->control_block->
- device_curr_frag[3]),
- le32_to_cpu(priv->control_block->
- device_curr_frag[4]),
- le32_to_cpu(priv->control_block->
- device_curr_frag[5])
- );
-#endif
-
- /* cleanup the data low transmit queue */
- islpci_eth_cleanup_transmit(priv, priv->control_block);
-
- /* device is in active state, update the
- * powerstate flag if necessary */
- powerstate = ISL38XX_PSM_ACTIVE_STATE;
-
- /* check all three queues in priority order
- * call the PIMFOR receive function until the
- * queue is empty */
- if (isl38xx_in_queue(priv->control_block,
- ISL38XX_CB_RX_MGMTQ) != 0) {
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING,
- "Received frame in Management Queue\n");
-#endif
- islpci_mgt_receive(ndev);
-
- islpci_mgt_cleanup_transmit(ndev);
-
- /* Refill slots in receive queue */
- islpci_mgmt_rx_fill(ndev);
-
- /* no need to trigger the device, next
- islpci_mgt_transaction does it */
- }
-
- while (isl38xx_in_queue(priv->control_block,
- ISL38XX_CB_RX_DATA_LQ) != 0) {
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING,
- "Received frame in Data Low Queue\n");
-#endif
- islpci_eth_receive(priv);
- }
-
- /* check whether the data transmit queues were full */
- if (priv->data_low_tx_full) {
- /* check whether the transmit is not full anymore */
- if (ISL38XX_CB_TX_QSIZE -
- isl38xx_in_queue(priv->control_block,
- ISL38XX_CB_TX_DATA_LQ) >=
- ISL38XX_MIN_QTHRESHOLD) {
- /* nope, the driver is ready for more network frames */
- netif_wake_queue(priv->ndev);
-
- /* reset the full flag */
- priv->data_low_tx_full = 0;
- }
- }
- }
-
- if (reg & ISL38XX_INT_IDENT_INIT) {
- /* Device has been initialized */
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING,
- "IRQ: Init flag, device initialized\n");
-#endif
- wake_up(&priv->reset_done);
- }
-
- if (reg & ISL38XX_INT_IDENT_SLEEP) {
- /* Device intends to move to powersave state */
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "IRQ: Sleep flag\n");
-#endif
- isl38xx_handle_sleep_request(priv->control_block,
- &powerstate,
- priv->device_base);
- }
-
- if (reg & ISL38XX_INT_IDENT_WAKEUP) {
- /* Device has been woken up to active state */
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "IRQ: Wakeup flag\n");
-#endif
-
- isl38xx_handle_wakeup(priv->control_block,
- &powerstate, priv->device_base);
- }
- } else {
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
-#endif
- spin_unlock(&priv->slock);
- return IRQ_NONE;
- }
-
- /* sleep -> ready */
- if (islpci_get_state(priv) == PRV_STATE_SLEEP
- && powerstate == ISL38XX_PSM_ACTIVE_STATE)
- islpci_set_state(priv, PRV_STATE_READY);
-
- /* !sleep -> sleep */
- if (islpci_get_state(priv) != PRV_STATE_SLEEP
- && powerstate == ISL38XX_PSM_POWERSAVE_STATE)
- islpci_set_state(priv, PRV_STATE_SLEEP);
-
- /* unlock the interrupt handler */
- spin_unlock(&priv->slock);
-
- return IRQ_HANDLED;
-}
-
-/******************************************************************************
- Network Interface Control & Statistical functions
-******************************************************************************/
-static int
-islpci_open(struct net_device *ndev)
-{
- u32 rc;
- islpci_private *priv = netdev_priv(ndev);
-
- /* reset data structures, upload firmware and reset device */
- rc = islpci_reset(priv,1);
- if (rc) {
- prism54_bring_down(priv);
- return rc; /* Returns informative message */
- }
-
- netif_start_queue(ndev);
-
- /* Turn off carrier if in STA or Ad-hoc mode. It will be turned on
- * once the firmware receives a trap of being associated
- * (GEN_OID_LINKSTATE). In other modes (AP or WDS or monitor) we
- * should just leave the carrier on as its expected the firmware
- * won't send us a trigger. */
- if (priv->iw_mode == IW_MODE_INFRA || priv->iw_mode == IW_MODE_ADHOC)
- netif_carrier_off(ndev);
- else
- netif_carrier_on(ndev);
-
- return 0;
-}
-
-static int
-islpci_close(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- printk(KERN_DEBUG "%s: islpci_close ()\n", ndev->name);
-
- netif_stop_queue(ndev);
-
- return prism54_bring_down(priv);
-}
-
-static int
-prism54_bring_down(islpci_private *priv)
-{
- void __iomem *device_base = priv->device_base;
- u32 reg;
- /* we are going to shutdown the device */
- islpci_set_state(priv, PRV_STATE_PREBOOT);
-
- /* disable all device interrupts in case they weren't */
- isl38xx_disable_interrupts(priv->device_base);
-
- /* For safety reasons, we may want to ensure that no DMA transfer is
- * currently in progress by emptying the TX and RX queues. */
-
- /* wait until interrupts have finished executing on other CPUs */
- synchronize_irq(priv->pdev->irq);
-
- reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
- reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT);
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- wmb();
- udelay(ISL38XX_WRITEIO_DELAY);
-
- reg |= ISL38XX_CTRL_STAT_RESET;
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- wmb();
- udelay(ISL38XX_WRITEIO_DELAY);
-
- /* clear the Reset bit */
- reg &= ~ISL38XX_CTRL_STAT_RESET;
- writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
- wmb();
-
- /* wait a while for the device to reset */
- schedule_timeout_uninterruptible(msecs_to_jiffies(50));
-
- return 0;
-}
-
-static int
-islpci_upload_fw(islpci_private *priv)
-{
- islpci_state_t old_state;
- u32 rc;
-
- old_state = islpci_set_state(priv, PRV_STATE_BOOT);
-
- printk(KERN_DEBUG "%s: uploading firmware...\n", priv->ndev->name);
-
- rc = isl_upload_firmware(priv);
- if (rc) {
- /* error uploading the firmware */
- printk(KERN_ERR "%s: could not upload firmware ('%s')\n",
- priv->ndev->name, priv->firmware);
-
- islpci_set_state(priv, old_state);
- return rc;
- }
-
- printk(KERN_DEBUG "%s: firmware upload complete\n",
- priv->ndev->name);
-
- islpci_set_state(priv, PRV_STATE_POSTBOOT);
-
- return 0;
-}
-
-static int
-islpci_reset_if(islpci_private *priv)
-{
- long remaining;
- int result = -ETIME;
- int count;
-
- DEFINE_WAIT(wait);
- prepare_to_wait(&priv->reset_done, &wait, TASK_UNINTERRUPTIBLE);
-
- /* now the last step is to reset the interface */
- isl38xx_interface_reset(priv->device_base, priv->device_host_address);
- islpci_set_state(priv, PRV_STATE_PREINIT);
-
- for(count = 0; count < 2 && result; count++) {
- /* The software reset acknowledge needs about 220 msec here.
- * Be conservative and wait for up to one second. */
-
- remaining = schedule_timeout_uninterruptible(HZ);
-
- if(remaining > 0) {
- result = 0;
- break;
- }
-
- /* If we're here it's because our IRQ hasn't yet gone through.
- * Retry a bit more...
- */
- printk(KERN_ERR "%s: no 'reset complete' IRQ seen - retrying\n",
- priv->ndev->name);
- }
-
- finish_wait(&priv->reset_done, &wait);
-
- if (result) {
- printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name);
- return result;
- }
-
- islpci_set_state(priv, PRV_STATE_INIT);
-
- /* Now that the device is 100% up, let's allow
- * for the other interrupts --
- * NOTE: this is not *yet* true since we've only allowed the
- * INIT interrupt on the IRQ line. We can perhaps poll
- * the IRQ line until we know for sure the reset went through */
- isl38xx_enable_common_interrupts(priv->device_base);
-
- down_write(&priv->mib_sem);
- result = mgt_commit(priv);
- if (result) {
- printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name);
- up_write(&priv->mib_sem);
- return result;
- }
- up_write(&priv->mib_sem);
-
- islpci_set_state(priv, PRV_STATE_READY);
-
- printk(KERN_DEBUG "%s: interface reset complete\n", priv->ndev->name);
- return 0;
-}
-
-int
-islpci_reset(islpci_private *priv, int reload_firmware)
-{
- isl38xx_control_block *cb = /* volatile not needed */
- (isl38xx_control_block *) priv->control_block;
- unsigned counter;
- int rc;
-
- if (reload_firmware)
- islpci_set_state(priv, PRV_STATE_PREBOOT);
- else
- islpci_set_state(priv, PRV_STATE_POSTBOOT);
-
- printk(KERN_DEBUG "%s: resetting device...\n", priv->ndev->name);
-
- /* disable all device interrupts in case they weren't */
- isl38xx_disable_interrupts(priv->device_base);
-
- /* flush all management queues */
- priv->index_mgmt_tx = 0;
- priv->index_mgmt_rx = 0;
-
- /* clear the indexes in the frame pointer */
- for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) {
- cb->driver_curr_frag[counter] = cpu_to_le32(0);
- cb->device_curr_frag[counter] = cpu_to_le32(0);
- }
-
- /* reset the mgmt receive queue */
- for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
- isl38xx_fragment *frag = &cb->rx_data_mgmt[counter];
- frag->size = cpu_to_le16(MGMT_FRAME_SIZE);
- frag->flags = 0;
- frag->address = cpu_to_le32(priv->mgmt_rx[counter].pci_addr);
- }
-
- for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
- cb->rx_data_low[counter].address =
- cpu_to_le32((u32) priv->pci_map_rx_address[counter]);
- }
-
- /* since the receive queues are filled with empty fragments, now we can
- * set the corresponding indexes in the Control Block */
- priv->control_block->driver_curr_frag[ISL38XX_CB_RX_DATA_LQ] =
- cpu_to_le32(ISL38XX_CB_RX_QSIZE);
- priv->control_block->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] =
- cpu_to_le32(ISL38XX_CB_MGMT_QSIZE);
-
- /* reset the remaining real index registers and full flags */
- priv->free_data_rx = 0;
- priv->free_data_tx = 0;
- priv->data_low_tx_full = 0;
-
- if (reload_firmware) { /* Should we load the firmware ? */
- /* now that the data structures are cleaned up, upload
- * firmware and reset interface */
- rc = islpci_upload_fw(priv);
- if (rc) {
- printk(KERN_ERR "%s: islpci_reset: failure\n",
- priv->ndev->name);
- return rc;
- }
- }
-
- /* finally reset interface */
- rc = islpci_reset_if(priv);
- if (rc)
- printk(KERN_ERR "prism54: Your card/socket may be faulty, or IRQ line too busy :(\n");
- return rc;
-}
-
-/******************************************************************************
- Network device configuration functions
-******************************************************************************/
-static int
-islpci_alloc_memory(islpci_private *priv)
-{
- int counter;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- printk(KERN_DEBUG "islpci_alloc_memory\n");
-#endif
-
- /* remap the PCI device base address to accessible */
- if (!(priv->device_base =
- ioremap(pci_resource_start(priv->pdev, 0),
- ISL38XX_PCI_MEM_SIZE))) {
- /* error in remapping the PCI device memory address range */
- printk(KERN_ERR "PCI memory remapping failed\n");
- return -1;
- }
-
- /* memory layout for consistent DMA region:
- *
- * Area 1: Control Block for the device interface
- * Area 2: Power Save Mode Buffer for temporary frame storage. Be aware that
- * the number of supported stations in the AP determines the minimal
- * size of the buffer !
- */
-
- /* perform the allocation */
- priv->driver_mem_address = pci_alloc_consistent(priv->pdev,
- HOST_MEM_BLOCK,
- &priv->
- device_host_address);
-
- if (!priv->driver_mem_address) {
- /* error allocating the block of PCI memory */
- printk(KERN_ERR "%s: could not allocate DMA memory, aborting!",
- "prism54");
- return -1;
- }
-
- /* assign the Control Block to the first address of the allocated area */
- priv->control_block =
- (isl38xx_control_block *) priv->driver_mem_address;
-
- /* set the Power Save Buffer pointer directly behind the CB */
- priv->device_psm_buffer =
- priv->device_host_address + CONTROL_BLOCK_SIZE;
-
- /* make sure all buffer pointers are initialized */
- for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) {
- priv->control_block->driver_curr_frag[counter] = cpu_to_le32(0);
- priv->control_block->device_curr_frag[counter] = cpu_to_le32(0);
- }
-
- priv->index_mgmt_rx = 0;
- memset(priv->mgmt_rx, 0, sizeof(priv->mgmt_rx));
- memset(priv->mgmt_tx, 0, sizeof(priv->mgmt_tx));
-
- /* allocate rx queue for management frames */
- if (islpci_mgmt_rx_fill(priv->ndev) < 0)
- goto out_free;
-
- /* now get the data rx skb's */
- memset(priv->data_low_rx, 0, sizeof (priv->data_low_rx));
- memset(priv->pci_map_rx_address, 0, sizeof (priv->pci_map_rx_address));
-
- for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
- struct sk_buff *skb;
-
- /* allocate an sk_buff for received data frames storage
- * each frame on receive size consists of 1 fragment
- * include any required allignment operations */
- if (!(skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2))) {
- /* error allocating an sk_buff structure elements */
- printk(KERN_ERR "Error allocating skb.\n");
- skb = NULL;
- goto out_free;
- }
- skb_reserve(skb, (4 - (long) skb->data) & 0x03);
- /* add the new allocated sk_buff to the buffer array */
- priv->data_low_rx[counter] = skb;
-
- /* map the allocated skb data area to pci */
- priv->pci_map_rx_address[counter] =
- pci_map_single(priv->pdev, (void *) skb->data,
- MAX_FRAGMENT_SIZE_RX + 2,
- PCI_DMA_FROMDEVICE);
- if (!priv->pci_map_rx_address[counter]) {
- /* error mapping the buffer to device
- accessible memory address */
- printk(KERN_ERR "failed to map skb DMA'able\n");
- goto out_free;
- }
- }
-
- prism54_acl_init(&priv->acl);
- prism54_wpa_bss_ie_init(priv);
- if (mgt_init(priv))
- goto out_free;
-
- return 0;
- out_free:
- islpci_free_memory(priv);
- return -1;
-}
-
-int
-islpci_free_memory(islpci_private *priv)
-{
- int counter;
-
- if (priv->device_base)
- iounmap(priv->device_base);
- priv->device_base = NULL;
-
- /* free consistent DMA area... */
- if (priv->driver_mem_address)
- pci_free_consistent(priv->pdev, HOST_MEM_BLOCK,
- priv->driver_mem_address,
- priv->device_host_address);
-
- /* clear some dangling pointers */
- priv->driver_mem_address = NULL;
- priv->device_host_address = 0;
- priv->device_psm_buffer = 0;
- priv->control_block = NULL;
-
- /* clean up mgmt rx buffers */
- for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
- struct islpci_membuf *buf = &priv->mgmt_rx[counter];
- if (buf->pci_addr)
- pci_unmap_single(priv->pdev, buf->pci_addr,
- buf->size, PCI_DMA_FROMDEVICE);
- buf->pci_addr = 0;
- kfree(buf->mem);
- buf->size = 0;
- buf->mem = NULL;
- }
-
- /* clean up data rx buffers */
- for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
- if (priv->pci_map_rx_address[counter])
- pci_unmap_single(priv->pdev,
- priv->pci_map_rx_address[counter],
- MAX_FRAGMENT_SIZE_RX + 2,
- PCI_DMA_FROMDEVICE);
- priv->pci_map_rx_address[counter] = 0;
-
- if (priv->data_low_rx[counter])
- dev_kfree_skb(priv->data_low_rx[counter]);
- priv->data_low_rx[counter] = NULL;
- }
-
- /* Free the access control list and the WPA list */
- prism54_acl_clean(&priv->acl);
- prism54_wpa_bss_ie_clean(priv);
- mgt_clean(priv);
-
- return 0;
-}
-
-#if 0
-static void
-islpci_set_multicast_list(struct net_device *dev)
-{
- /* put device into promisc mode and let network layer handle it */
-}
-#endif
-
-static void islpci_ethtool_get_drvinfo(struct net_device *dev,
- 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 islpci_ethtool_ops = {
- .get_drvinfo = islpci_ethtool_get_drvinfo,
-};
-
-static const struct net_device_ops islpci_netdev_ops = {
- .ndo_open = islpci_open,
- .ndo_stop = islpci_close,
- .ndo_start_xmit = islpci_eth_transmit,
- .ndo_tx_timeout = islpci_eth_tx_timeout,
- .ndo_set_mac_address = prism54_set_mac_address,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static struct device_type wlan_type = {
- .name = "wlan",
-};
-
-struct net_device *
-islpci_setup(struct pci_dev *pdev)
-{
- islpci_private *priv;
- struct net_device *ndev = alloc_etherdev(sizeof (islpci_private));
-
- if (!ndev)
- return ndev;
-
- pci_set_drvdata(pdev, ndev);
- SET_NETDEV_DEV(ndev, &pdev->dev);
- SET_NETDEV_DEVTYPE(ndev, &wlan_type);
-
- /* setup the structure members */
- ndev->base_addr = pci_resource_start(pdev, 0);
- ndev->irq = pdev->irq;
-
- /* initialize the function pointers */
- ndev->netdev_ops = &islpci_netdev_ops;
- ndev->wireless_handlers = &prism54_handler_def;
- ndev->ethtool_ops = &islpci_ethtool_ops;
-
- /* ndev->set_multicast_list = &islpci_set_multicast_list; */
- ndev->addr_len = ETH_ALEN;
- /* Get a non-zero dummy MAC address for nameif. Jean II */
- memcpy(ndev->dev_addr, dummy_mac, ETH_ALEN);
-
- ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT;
-
- /* allocate a private device structure to the network device */
- priv = netdev_priv(ndev);
- priv->ndev = ndev;
- priv->pdev = pdev;
- priv->monitor_type = ARPHRD_IEEE80211;
- priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) ?
- priv->monitor_type : ARPHRD_ETHER;
-
- /* Add pointers to enable iwspy support. */
- priv->wireless_data.spy_data = &priv->spy_data;
- ndev->wireless_data = &priv->wireless_data;
-
- /* save the start and end address of the PCI memory area */
- ndev->mem_start = (unsigned long) priv->device_base;
- ndev->mem_end = ndev->mem_start + ISL38XX_PCI_MEM_SIZE;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "PCI Memory remapped to 0x%p\n", priv->device_base);
-#endif
-
- init_waitqueue_head(&priv->reset_done);
-
- /* init the queue read locks, process wait counter */
- mutex_init(&priv->mgmt_lock);
- priv->mgmt_received = NULL;
- init_waitqueue_head(&priv->mgmt_wqueue);
- mutex_init(&priv->stats_lock);
- spin_lock_init(&priv->slock);
-
- /* init state machine with off#1 state */
- priv->state = PRV_STATE_OFF;
- priv->state_off = 1;
-
- /* initialize workqueue's */
- INIT_WORK(&priv->stats_work, prism54_update_stats);
- priv->stats_timestamp = 0;
-
- INIT_WORK(&priv->reset_task, islpci_do_reset_and_wake);
- priv->reset_task_pending = 0;
-
- /* allocate various memory areas */
- if (islpci_alloc_memory(priv))
- goto do_free_netdev;
-
- /* select the firmware file depending on the device id */
- switch (pdev->device) {
- case 0x3877:
- strcpy(priv->firmware, ISL3877_IMAGE_FILE);
- break;
-
- case 0x3886:
- strcpy(priv->firmware, ISL3886_IMAGE_FILE);
- break;
-
- default:
- strcpy(priv->firmware, ISL3890_IMAGE_FILE);
- break;
- }
-
- if (register_netdev(ndev)) {
- DEBUG(SHOW_ERROR_MESSAGES,
- "ERROR: register_netdev() failed\n");
- goto do_islpci_free_memory;
- }
-
- return ndev;
-
- do_islpci_free_memory:
- islpci_free_memory(priv);
- do_free_netdev:
- free_netdev(ndev);
- priv = NULL;
- return NULL;
-}
-
-islpci_state_t
-islpci_set_state(islpci_private *priv, islpci_state_t new_state)
-{
- islpci_state_t old_state;
-
- /* lock */
- old_state = priv->state;
-
- /* this means either a race condition or some serious error in
- * the driver code */
- switch (new_state) {
- case PRV_STATE_OFF:
- priv->state_off++;
- default:
- priv->state = new_state;
- break;
-
- case PRV_STATE_PREBOOT:
- /* there are actually many off-states, enumerated by
- * state_off */
- if (old_state == PRV_STATE_OFF)
- priv->state_off--;
-
- /* only if hw_unavailable is zero now it means we either
- * were in off#1 state, or came here from
- * somewhere else */
- if (!priv->state_off)
- priv->state = new_state;
- break;
- }
-#if 0
- printk(KERN_DEBUG "%s: state transition %d -> %d (off#%d)\n",
- priv->ndev->name, old_state, new_state, priv->state_off);
-#endif
-
- /* invariants */
- BUG_ON(priv->state_off < 0);
- BUG_ON(priv->state_off && (priv->state != PRV_STATE_OFF));
- BUG_ON(!priv->state_off && (priv->state == PRV_STATE_OFF));
-
- /* unlock */
- return old_state;
-}
diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c
deleted file mode 100644
index 674658f2e6ef..000000000000
--- a/drivers/net/wireless/prism54/islpci_eth.c
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * Copyright (C) 2002 Intersil Americas Inc.
- * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <linux/module.h>
-#include <linux/gfp.h>
-
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <asm/byteorder.h>
-
-#include "prismcompat.h"
-#include "isl_38xx.h"
-#include "islpci_eth.h"
-#include "islpci_mgt.h"
-#include "oid_mgt.h"
-
-/******************************************************************************
- Network Interface functions
-******************************************************************************/
-void
-islpci_eth_cleanup_transmit(islpci_private *priv,
- isl38xx_control_block *control_block)
-{
- struct sk_buff *skb;
- u32 index;
-
- /* compare the control block read pointer with the free pointer */
- while (priv->free_data_tx !=
- le32_to_cpu(control_block->
- device_curr_frag[ISL38XX_CB_TX_DATA_LQ])) {
- /* read the index of the first fragment to be freed */
- index = priv->free_data_tx % ISL38XX_CB_TX_QSIZE;
-
- /* check for holes in the arrays caused by multi fragment frames
- * searching for the last fragment of a frame */
- if (priv->pci_map_tx_address[index]) {
- /* entry is the last fragment of a frame
- * free the skb structure and unmap pci memory */
- skb = priv->data_low_tx[index];
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING,
- "cleanup skb %p skb->data %p skb->len %u truesize %u\n ",
- skb, skb->data, skb->len, skb->truesize);
-#endif
-
- pci_unmap_single(priv->pdev,
- priv->pci_map_tx_address[index],
- skb->len, PCI_DMA_TODEVICE);
- dev_kfree_skb_irq(skb);
- skb = NULL;
- }
- /* increment the free data low queue pointer */
- priv->free_data_tx++;
- }
-}
-
-netdev_tx_t
-islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb = priv->control_block;
- u32 index;
- dma_addr_t pci_map_address;
- int frame_size;
- isl38xx_fragment *fragment;
- int offset;
- struct sk_buff *newskb;
- int newskb_offset;
- unsigned long flags;
- unsigned char wds_mac[6];
- u32 curr_frag;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit\n");
-#endif
-
- /* lock the driver code */
- spin_lock_irqsave(&priv->slock, flags);
-
- /* check whether the destination queue has enough fragments for the frame */
- curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]);
- if (unlikely(curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE)) {
- printk(KERN_ERR "%s: transmit device queue full when awake\n",
- ndev->name);
- netif_stop_queue(ndev);
-
- /* trigger the device */
- isl38xx_w32_flush(priv->device_base, ISL38XX_DEV_INT_UPDATE,
- ISL38XX_DEV_INT_REG);
- udelay(ISL38XX_WRITEIO_DELAY);
- goto drop_free;
- }
- /* Check alignment and WDS frame formatting. The start of the packet should
- * be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes
- * and add WDS address information */
- if (likely(((long) skb->data & 0x03) | init_wds)) {
- /* get the number of bytes to add and re-align */
- offset = (4 - (long) skb->data) & 0x03;
- offset += init_wds ? 6 : 0;
-
- /* check whether the current skb can be used */
- if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) {
- unsigned char *src = skb->data;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "skb offset %i wds %i\n", offset,
- init_wds);
-#endif
-
- /* align the buffer on 4-byte boundary */
- skb_reserve(skb, (4 - (long) skb->data) & 0x03);
- if (init_wds) {
- /* wds requires an additional address field of 6 bytes */
- skb_put(skb, 6);
-#ifdef ISLPCI_ETH_DEBUG
- printk("islpci_eth_transmit:wds_mac\n");
-#endif
- memmove(skb->data + 6, src, skb->len);
- skb_copy_to_linear_data(skb, wds_mac, 6);
- } else {
- memmove(skb->data, src, skb->len);
- }
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "memmove %p %p %i\n", skb->data,
- src, skb->len);
-#endif
- } else {
- newskb =
- dev_alloc_skb(init_wds ? skb->len + 6 : skb->len);
- if (unlikely(newskb == NULL)) {
- printk(KERN_ERR "%s: Cannot allocate skb\n",
- ndev->name);
- goto drop_free;
- }
- newskb_offset = (4 - (long) newskb->data) & 0x03;
-
- /* Check if newskb->data is aligned */
- if (newskb_offset)
- skb_reserve(newskb, newskb_offset);
-
- skb_put(newskb, init_wds ? skb->len + 6 : skb->len);
- if (init_wds) {
- skb_copy_from_linear_data(skb,
- newskb->data + 6,
- skb->len);
- skb_copy_to_linear_data(newskb, wds_mac, 6);
-#ifdef ISLPCI_ETH_DEBUG
- printk("islpci_eth_transmit:wds_mac\n");
-#endif
- } else
- skb_copy_from_linear_data(skb, newskb->data,
- skb->len);
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n",
- newskb->data, skb->data, skb->len, init_wds);
-#endif
-
- newskb->dev = skb->dev;
- dev_kfree_skb_irq(skb);
- skb = newskb;
- }
- }
- /* display the buffer contents for debugging */
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_BUFFER_CONTENTS, "\ntx %p ", skb->data);
- display_buffer((char *) skb->data, skb->len);
-#endif
-
- /* map the skb buffer to pci memory for DMA operation */
- pci_map_address = pci_map_single(priv->pdev,
- (void *) skb->data, skb->len,
- PCI_DMA_TODEVICE);
- if (unlikely(pci_map_address == 0)) {
- printk(KERN_WARNING "%s: cannot map buffer to PCI\n",
- ndev->name);
- goto drop_free;
- }
- /* Place the fragment in the control block structure. */
- index = curr_frag % ISL38XX_CB_TX_QSIZE;
- fragment = &cb->tx_data_low[index];
-
- priv->pci_map_tx_address[index] = pci_map_address;
- /* store the skb address for future freeing */
- priv->data_low_tx[index] = skb;
- /* set the proper fragment start address and size information */
- frame_size = skb->len;
- fragment->size = cpu_to_le16(frame_size);
- fragment->flags = cpu_to_le16(0); /* set to 1 if more fragments */
- fragment->address = cpu_to_le32(pci_map_address);
- curr_frag++;
-
- /* The fragment address in the control block must have been
- * written before announcing the frame buffer to device. */
- wmb();
- cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ] = cpu_to_le32(curr_frag);
-
- if (curr_frag - priv->free_data_tx + ISL38XX_MIN_QTHRESHOLD
- > ISL38XX_CB_TX_QSIZE) {
- /* stop sends from upper layers */
- netif_stop_queue(ndev);
-
- /* set the full flag for the transmission queue */
- priv->data_low_tx_full = 1;
- }
-
- ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
-
- /* trigger the device */
- islpci_trigger(priv);
-
- /* unlock the driver code */
- spin_unlock_irqrestore(&priv->slock, flags);
-
- return NETDEV_TX_OK;
-
- drop_free:
- ndev->stats.tx_dropped++;
- spin_unlock_irqrestore(&priv->slock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-}
-
-static inline int
-islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb)
-{
- /* The card reports full 802.11 packets but with a 20 bytes
- * header and without the FCS. But there a is a bit that
- * indicates if the packet is corrupted :-) */
- struct rfmon_header *hdr = (struct rfmon_header *) (*skb)->data;
-
- if (hdr->flags & 0x01)
- /* This one is bad. Drop it ! */
- return -1;
- if (priv->ndev->type == ARPHRD_IEEE80211_PRISM) {
- struct avs_80211_1_header *avs;
- /* extract the relevant data from the header */
- u32 clock = le32_to_cpu(hdr->clock);
- u8 rate = hdr->rate;
- u16 freq = le16_to_cpu(hdr->freq);
- u8 rssi = hdr->rssi;
-
- skb_pull(*skb, sizeof (struct rfmon_header));
-
- if (skb_headroom(*skb) < sizeof (struct avs_80211_1_header)) {
- struct sk_buff *newskb = skb_copy_expand(*skb,
- sizeof (struct
- avs_80211_1_header),
- 0, GFP_ATOMIC);
- if (newskb) {
- dev_kfree_skb_irq(*skb);
- *skb = newskb;
- } else
- return -1;
- /* This behavior is not very subtile... */
- }
-
- /* make room for the new header and fill it. */
- avs =
- (struct avs_80211_1_header *) skb_push(*skb,
- sizeof (struct
- avs_80211_1_header));
-
- avs->version = cpu_to_be32(P80211CAPTURE_VERSION);
- avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header));
- avs->mactime = cpu_to_be64(clock);
- avs->hosttime = cpu_to_be64(jiffies);
- avs->phytype = cpu_to_be32(6); /*OFDM: 6 for (g), 8 for (a) */
- avs->channel = cpu_to_be32(channel_of_freq(freq));
- avs->datarate = cpu_to_be32(rate * 5);
- avs->antenna = cpu_to_be32(0); /*unknown */
- avs->priority = cpu_to_be32(0); /*unknown */
- avs->ssi_type = cpu_to_be32(3); /*2: dBm, 3: raw RSSI */
- avs->ssi_signal = cpu_to_be32(rssi & 0x7f);
- avs->ssi_noise = cpu_to_be32(priv->local_iwstatistics.qual.noise); /*better than 'undefined', I assume */
- avs->preamble = cpu_to_be32(0); /*unknown */
- avs->encoding = cpu_to_be32(0); /*unknown */
- } else
- skb_pull(*skb, sizeof (struct rfmon_header));
-
- (*skb)->protocol = htons(ETH_P_802_2);
- skb_reset_mac_header(*skb);
- (*skb)->pkt_type = PACKET_OTHERHOST;
-
- return 0;
-}
-
-int
-islpci_eth_receive(islpci_private *priv)
-{
- struct net_device *ndev = priv->ndev;
- isl38xx_control_block *control_block = priv->control_block;
- struct sk_buff *skb;
- u16 size;
- u32 index, offset;
- unsigned char *src;
- int discard = 0;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_receive\n");
-#endif
-
- /* the device has written an Ethernet frame in the data area
- * of the sk_buff without updating the structure, do it now */
- index = priv->free_data_rx % ISL38XX_CB_RX_QSIZE;
- size = le16_to_cpu(control_block->rx_data_low[index].size);
- skb = priv->data_low_rx[index];
- offset = ((unsigned long)
- le32_to_cpu(control_block->rx_data_low[index].address) -
- (unsigned long) skb->data) & 3;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING,
- "frq->addr %x skb->data %p skb->len %u offset %u truesize %u\n ",
- control_block->rx_data_low[priv->free_data_rx].address, skb->data,
- skb->len, offset, skb->truesize);
-#endif
-
- /* delete the streaming DMA mapping before processing the skb */
- pci_unmap_single(priv->pdev,
- priv->pci_map_rx_address[index],
- MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE);
-
- /* update the skb structure and align the buffer */
- skb_put(skb, size);
- if (offset) {
- /* shift the buffer allocation offset bytes to get the right frame */
- skb_pull(skb, 2);
- skb_put(skb, 2);
- }
-#if VERBOSE > SHOW_ERROR_MESSAGES
- /* display the buffer contents for debugging */
- DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
- display_buffer((char *) skb->data, skb->len);
-#endif
-
- /* check whether WDS is enabled and whether the data frame is a WDS frame */
-
- if (init_wds) {
- /* WDS enabled, check for the wds address on the first 6 bytes of the buffer */
- src = skb->data + 6;
- memmove(skb->data, src, skb->len - 6);
- skb_trim(skb, skb->len - 6);
- }
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "Fragment size %i in skb at %p\n", size, skb);
- DEBUG(SHOW_TRACING, "Skb data at %p, length %i\n", skb->data, skb->len);
-
- /* display the buffer contents for debugging */
- DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
- display_buffer((char *) skb->data, skb->len);
-#endif
- /* take care of monitor mode and spy monitoring. */
- if (unlikely(priv->iw_mode == IW_MODE_MONITOR)) {
- skb->dev = ndev;
- discard = islpci_monitor_rx(priv, &skb);
- } else {
- if (unlikely(skb->data[2 * ETH_ALEN] == 0)) {
- /* The packet has a rx_annex. Read it for spy monitoring, Then
- * remove it, while keeping the 2 leading MAC addr.
- */
- struct iw_quality wstats;
- struct rx_annex_header *annex =
- (struct rx_annex_header *) skb->data;
- wstats.level = annex->rfmon.rssi;
- /* The noise value can be a bit outdated if nobody's
- * reading wireless stats... */
- wstats.noise = priv->local_iwstatistics.qual.noise;
- wstats.qual = wstats.level - wstats.noise;
- wstats.updated = 0x07;
- /* Update spy records */
- wireless_spy_update(ndev, annex->addr2, &wstats);
-
- skb_copy_from_linear_data(skb,
- (skb->data +
- sizeof(struct rfmon_header)),
- 2 * ETH_ALEN);
- skb_pull(skb, sizeof (struct rfmon_header));
- }
- skb->protocol = eth_type_trans(skb, ndev);
- }
- skb->ip_summed = CHECKSUM_NONE;
- ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += size;
-
- /* deliver the skb to the network layer */
-#ifdef ISLPCI_ETH_DEBUG
- printk
- ("islpci_eth_receive:netif_rx %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- skb->data[0], skb->data[1], skb->data[2], skb->data[3],
- skb->data[4], skb->data[5]);
-#endif
- if (unlikely(discard)) {
- dev_kfree_skb_irq(skb);
- skb = NULL;
- } else
- netif_rx(skb);
-
- /* increment the read index for the rx data low queue */
- priv->free_data_rx++;
-
- /* add one or more sk_buff structures */
- while (index =
- le32_to_cpu(control_block->
- driver_curr_frag[ISL38XX_CB_RX_DATA_LQ]),
- index - priv->free_data_rx < ISL38XX_CB_RX_QSIZE) {
- /* allocate an sk_buff for received data frames storage
- * include any required allignment operations */
- skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2);
- if (unlikely(skb == NULL)) {
- /* error allocating an sk_buff structure elements */
- DEBUG(SHOW_ERROR_MESSAGES, "Error allocating skb\n");
- break;
- }
- skb_reserve(skb, (4 - (long) skb->data) & 0x03);
- /* store the new skb structure pointer */
- index = index % ISL38XX_CB_RX_QSIZE;
- priv->data_low_rx[index] = skb;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING,
- "new alloc skb %p skb->data %p skb->len %u index %u truesize %u\n ",
- skb, skb->data, skb->len, index, skb->truesize);
-#endif
-
- /* set the streaming DMA mapping for proper PCI bus operation */
- priv->pci_map_rx_address[index] =
- pci_map_single(priv->pdev, (void *) skb->data,
- MAX_FRAGMENT_SIZE_RX + 2,
- PCI_DMA_FROMDEVICE);
- if (unlikely(!priv->pci_map_rx_address[index])) {
- /* error mapping the buffer to device accessible memory address */
- DEBUG(SHOW_ERROR_MESSAGES,
- "Error mapping DMA address\n");
-
- /* free the skbuf structure before aborting */
- dev_kfree_skb_irq(skb);
- skb = NULL;
- break;
- }
- /* update the fragment address */
- control_block->rx_data_low[index].address =
- cpu_to_le32((u32)priv->pci_map_rx_address[index]);
- wmb();
-
- /* increment the driver read pointer */
- le32_add_cpu(&control_block->
- driver_curr_frag[ISL38XX_CB_RX_DATA_LQ], 1);
- }
-
- /* trigger the device */
- islpci_trigger(priv);
-
- return 0;
-}
-
-void
-islpci_do_reset_and_wake(struct work_struct *work)
-{
- islpci_private *priv = container_of(work, islpci_private, reset_task);
-
- islpci_reset(priv, 1);
- priv->reset_task_pending = 0;
- smp_wmb();
- netif_wake_queue(priv->ndev);
-}
-
-void
-islpci_eth_tx_timeout(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
-
- /* increment the transmit error counter */
- ndev->stats.tx_errors++;
-
- if (!priv->reset_task_pending) {
- printk(KERN_WARNING
- "%s: tx_timeout, scheduling reset", ndev->name);
- netif_stop_queue(ndev);
- priv->reset_task_pending = 1;
- schedule_work(&priv->reset_task);
- } else {
- printk(KERN_WARNING
- "%s: tx_timeout, waiting for reset", ndev->name);
- }
-}
diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/prism54/islpci_mgt.c
deleted file mode 100644
index 0de14dfa68cc..000000000000
--- a/drivers/net/wireless/prism54/islpci_mgt.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * Copyright (C) 2002 Intersil Americas Inc.
- * Copyright 2004 Jens Maurer <Jens.Maurer@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <linux/netdevice.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <asm/io.h>
-#include <linux/if_arp.h>
-
-#include "prismcompat.h"
-#include "isl_38xx.h"
-#include "islpci_mgt.h"
-#include "isl_oid.h" /* additional types and defs for isl38xx fw */
-#include "isl_ioctl.h"
-
-#include <net/iw_handler.h>
-
-/******************************************************************************
- Global variable definition section
-******************************************************************************/
-int pc_debug = VERBOSE;
-module_param(pc_debug, int, 0);
-
-/******************************************************************************
- Driver general functions
-******************************************************************************/
-#if VERBOSE > SHOW_ERROR_MESSAGES
-void
-display_buffer(char *buffer, int length)
-{
- if ((pc_debug & SHOW_BUFFER_CONTENTS) == 0)
- return;
-
- while (length > 0) {
- printk("[%02x]", *buffer & 255);
- length--;
- buffer++;
- }
-
- printk("\n");
-}
-#endif
-
-/*****************************************************************************
- Queue handling for management frames
-******************************************************************************/
-
-/*
- * Helper function to create a PIMFOR management frame header.
- */
-static void
-pimfor_encode_header(int operation, u32 oid, u32 length, pimfor_header_t *h)
-{
- h->version = PIMFOR_VERSION;
- h->operation = operation;
- h->device_id = PIMFOR_DEV_ID_MHLI_MIB;
- h->flags = 0;
- h->oid = cpu_to_be32(oid);
- h->length = cpu_to_be32(length);
-}
-
-/*
- * Helper function to analyze a PIMFOR management frame header.
- */
-static pimfor_header_t *
-pimfor_decode_header(void *data, int len)
-{
- pimfor_header_t *h = data;
-
- while ((void *) h < data + len) {
- if (h->flags & PIMFOR_FLAG_LITTLE_ENDIAN) {
- le32_to_cpus(&h->oid);
- le32_to_cpus(&h->length);
- } else {
- be32_to_cpus(&h->oid);
- be32_to_cpus(&h->length);
- }
- if (h->oid != OID_INL_TUNNEL)
- return h;
- h++;
- }
- return NULL;
-}
-
-/*
- * Fill the receive queue for management frames with fresh buffers.
- */
-int
-islpci_mgmt_rx_fill(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb = /* volatile not needed */
- (isl38xx_control_block *) priv->control_block;
- u32 curr = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ]);
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgmt_rx_fill\n");
-#endif
-
- while (curr - priv->index_mgmt_rx < ISL38XX_CB_MGMT_QSIZE) {
- u32 index = curr % ISL38XX_CB_MGMT_QSIZE;
- struct islpci_membuf *buf = &priv->mgmt_rx[index];
- isl38xx_fragment *frag = &cb->rx_data_mgmt[index];
-
- if (buf->mem == NULL) {
- buf->mem = kmalloc(MGMT_FRAME_SIZE, GFP_ATOMIC);
- if (!buf->mem)
- return -ENOMEM;
- buf->size = MGMT_FRAME_SIZE;
- }
- if (buf->pci_addr == 0) {
- buf->pci_addr = pci_map_single(priv->pdev, buf->mem,
- MGMT_FRAME_SIZE,
- PCI_DMA_FROMDEVICE);
- if (!buf->pci_addr) {
- printk(KERN_WARNING
- "Failed to make memory DMA'able.\n");
- return -ENOMEM;
- }
- }
-
- /* be safe: always reset control block information */
- frag->size = cpu_to_le16(MGMT_FRAME_SIZE);
- frag->flags = 0;
- frag->address = cpu_to_le32(buf->pci_addr);
- curr++;
-
- /* The fragment address in the control block must have
- * been written before announcing the frame buffer to
- * device */
- wmb();
- cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] = cpu_to_le32(curr);
- }
- return 0;
-}
-
-/*
- * Create and transmit a management frame using "operation" and "oid",
- * with arguments data/length.
- * We either return an error and free the frame, or we return 0 and
- * islpci_mgt_cleanup_transmit() frees the frame in the tx-done
- * interrupt.
- */
-static int
-islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid,
- void *data, int length)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb =
- (isl38xx_control_block *) priv->control_block;
- void *p;
- int err = -EINVAL;
- unsigned long flags;
- isl38xx_fragment *frag;
- struct islpci_membuf buf;
- u32 curr_frag;
- int index;
- int frag_len = length + PIMFOR_HEADER_SIZE;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_transmit\n");
-#endif
-
- if (frag_len > MGMT_FRAME_SIZE) {
- printk(KERN_DEBUG "%s: mgmt frame too large %d\n",
- ndev->name, frag_len);
- goto error;
- }
-
- err = -ENOMEM;
- p = buf.mem = kmalloc(frag_len, GFP_KERNEL);
- if (!buf.mem)
- goto error;
-
- buf.size = frag_len;
-
- /* create the header directly in the fragment data area */
- pimfor_encode_header(operation, oid, length, (pimfor_header_t *) p);
- p += PIMFOR_HEADER_SIZE;
-
- if (data)
- memcpy(p, data, length);
- else
- memset(p, 0, length);
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- {
- pimfor_header_t *h = buf.mem;
- DEBUG(SHOW_PIMFOR_FRAMES,
- "PIMFOR: op %i, oid 0x%08lx, device %i, flags 0x%x length 0x%x\n",
- h->operation, oid, h->device_id, h->flags, length);
-
- /* display the buffer contents for debugging */
- display_buffer((char *) h, sizeof (pimfor_header_t));
- display_buffer(p, length);
- }
-#endif
-
- err = -ENOMEM;
- buf.pci_addr = pci_map_single(priv->pdev, buf.mem, frag_len,
- PCI_DMA_TODEVICE);
- if (!buf.pci_addr) {
- printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n",
- ndev->name);
- goto error_free;
- }
-
- /* Protect the control block modifications against interrupts. */
- spin_lock_irqsave(&priv->slock, flags);
- curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ]);
- if (curr_frag - priv->index_mgmt_tx >= ISL38XX_CB_MGMT_QSIZE) {
- printk(KERN_WARNING "%s: mgmt tx queue is still full\n",
- ndev->name);
- goto error_unlock;
- }
-
- /* commit the frame to the tx device queue */
- index = curr_frag % ISL38XX_CB_MGMT_QSIZE;
- priv->mgmt_tx[index] = buf;
- frag = &cb->tx_data_mgmt[index];
- frag->size = cpu_to_le16(frag_len);
- frag->flags = 0; /* for any other than the last fragment, set to 1 */
- frag->address = cpu_to_le32(buf.pci_addr);
-
- /* The fragment address in the control block must have
- * been written before announcing the frame buffer to
- * device */
- wmb();
- cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ] = cpu_to_le32(curr_frag + 1);
- spin_unlock_irqrestore(&priv->slock, flags);
-
- /* trigger the device */
- islpci_trigger(priv);
- return 0;
-
- error_unlock:
- spin_unlock_irqrestore(&priv->slock, flags);
- error_free:
- kfree(buf.mem);
- error:
- return err;
-}
-
-/*
- * Receive a management frame from the device.
- * This can be an arbitrary number of traps, and at most one response
- * frame for a previous request sent via islpci_mgt_transmit().
- */
-int
-islpci_mgt_receive(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb =
- (isl38xx_control_block *) priv->control_block;
- u32 curr_frag;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_receive\n");
-#endif
-
- /* Only once per interrupt, determine fragment range to
- * process. This avoids an endless loop (i.e. lockup) if
- * frames come in faster than we can process them. */
- curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_RX_MGMTQ]);
- barrier();
-
- for (; priv->index_mgmt_rx < curr_frag; priv->index_mgmt_rx++) {
- pimfor_header_t *header;
- u32 index = priv->index_mgmt_rx % ISL38XX_CB_MGMT_QSIZE;
- struct islpci_membuf *buf = &priv->mgmt_rx[index];
- u16 frag_len;
- int size;
- struct islpci_mgmtframe *frame;
-
- /* I have no idea (and no documentation) if flags != 0
- * is possible. Drop the frame, reuse the buffer. */
- if (le16_to_cpu(cb->rx_data_mgmt[index].flags) != 0) {
- printk(KERN_WARNING "%s: unknown flags 0x%04x\n",
- ndev->name,
- le16_to_cpu(cb->rx_data_mgmt[index].flags));
- continue;
- }
-
- /* The device only returns the size of the header(s) here. */
- frag_len = le16_to_cpu(cb->rx_data_mgmt[index].size);
-
- /*
- * We appear to have no way to tell the device the
- * size of a receive buffer. Thus, if this check
- * triggers, we likely have kernel heap corruption. */
- if (frag_len > MGMT_FRAME_SIZE) {
- printk(KERN_WARNING
- "%s: Bogus packet size of %d (%#x).\n",
- ndev->name, frag_len, frag_len);
- frag_len = MGMT_FRAME_SIZE;
- }
-
- /* Ensure the results of device DMA are visible to the CPU. */
- pci_dma_sync_single_for_cpu(priv->pdev, buf->pci_addr,
- buf->size, PCI_DMA_FROMDEVICE);
-
- /* Perform endianess conversion for PIMFOR header in-place. */
- header = pimfor_decode_header(buf->mem, frag_len);
- if (!header) {
- printk(KERN_WARNING "%s: no PIMFOR header found\n",
- ndev->name);
- continue;
- }
-
- /* The device ID from the PIMFOR packet received from
- * the MVC is always 0. We forward a sensible device_id.
- * Not that anyone upstream would care... */
- header->device_id = priv->ndev->ifindex;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_PIMFOR_FRAMES,
- "PIMFOR: op %i, oid 0x%08x, device %i, flags 0x%x length 0x%x\n",
- header->operation, header->oid, header->device_id,
- header->flags, header->length);
-
- /* display the buffer contents for debugging */
- display_buffer((char *) header, PIMFOR_HEADER_SIZE);
- display_buffer((char *) header + PIMFOR_HEADER_SIZE,
- header->length);
-#endif
-
- /* nobody sends these */
- if (header->flags & PIMFOR_FLAG_APPLIC_ORIGIN) {
- printk(KERN_DEBUG
- "%s: errant PIMFOR application frame\n",
- ndev->name);
- continue;
- }
-
- /* Determine frame size, skipping OID_INL_TUNNEL headers. */
- size = PIMFOR_HEADER_SIZE + header->length;
- frame = kmalloc(sizeof(struct islpci_mgmtframe) + size,
- GFP_ATOMIC);
- if (!frame)
- continue;
-
- frame->ndev = ndev;
- memcpy(&frame->buf, header, size);
- frame->header = (pimfor_header_t *) frame->buf;
- frame->data = frame->buf + PIMFOR_HEADER_SIZE;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_PIMFOR_FRAMES,
- "frame: header: %p, data: %p, size: %d\n",
- frame->header, frame->data, size);
-#endif
-
- if (header->operation == PIMFOR_OP_TRAP) {
-#if VERBOSE > SHOW_ERROR_MESSAGES
- printk(KERN_DEBUG
- "TRAP: oid 0x%x, device %i, flags 0x%x length %i\n",
- header->oid, header->device_id, header->flags,
- header->length);
-#endif
-
- /* Create work to handle trap out of interrupt
- * context. */
- INIT_WORK(&frame->ws, prism54_process_trap);
- schedule_work(&frame->ws);
-
- } else {
- /* Signal the one waiting process that a response
- * has been received. */
- if ((frame = xchg(&priv->mgmt_received, frame)) != NULL) {
- printk(KERN_WARNING
- "%s: mgmt response not collected\n",
- ndev->name);
- kfree(frame);
- }
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "Wake up Mgmt Queue\n");
-#endif
- wake_up(&priv->mgmt_wqueue);
- }
-
- }
-
- return 0;
-}
-
-/*
- * Cleanup the transmit queue by freeing all frames handled by the device.
- */
-void
-islpci_mgt_cleanup_transmit(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb = /* volatile not needed */
- (isl38xx_control_block *) priv->control_block;
- u32 curr_frag;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_cleanup_transmit\n");
-#endif
-
- /* Only once per cleanup, determine fragment range to
- * process. This avoids an endless loop (i.e. lockup) if
- * the device became confused, incrementing device_curr_frag
- * rapidly. */
- curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_TX_MGMTQ]);
- barrier();
-
- for (; priv->index_mgmt_tx < curr_frag; priv->index_mgmt_tx++) {
- int index = priv->index_mgmt_tx % ISL38XX_CB_MGMT_QSIZE;
- struct islpci_membuf *buf = &priv->mgmt_tx[index];
- pci_unmap_single(priv->pdev, buf->pci_addr, buf->size,
- PCI_DMA_TODEVICE);
- buf->pci_addr = 0;
- kfree(buf->mem);
- buf->mem = NULL;
- buf->size = 0;
- }
-}
-
-/*
- * Perform one request-response transaction to the device.
- */
-int
-islpci_mgt_transaction(struct net_device *ndev,
- int operation, unsigned long oid,
- void *senddata, int sendlen,
- struct islpci_mgmtframe **recvframe)
-{
- islpci_private *priv = netdev_priv(ndev);
- const long wait_cycle_jiffies = msecs_to_jiffies(ISL38XX_WAIT_CYCLE * 10);
- long timeout_left = ISL38XX_MAX_WAIT_CYCLES * wait_cycle_jiffies;
- int err;
- DEFINE_WAIT(wait);
-
- *recvframe = NULL;
-
- if (mutex_lock_interruptible(&priv->mgmt_lock))
- return -ERESTARTSYS;
-
- prepare_to_wait(&priv->mgmt_wqueue, &wait, TASK_UNINTERRUPTIBLE);
- err = islpci_mgt_transmit(ndev, operation, oid, senddata, sendlen);
- if (err)
- goto out;
-
- err = -ETIMEDOUT;
- while (timeout_left > 0) {
- int timeleft;
- struct islpci_mgmtframe *frame;
-
- timeleft = schedule_timeout_uninterruptible(wait_cycle_jiffies);
- frame = xchg(&priv->mgmt_received, NULL);
- if (frame) {
- if (frame->header->oid == oid) {
- *recvframe = frame;
- err = 0;
- goto out;
- } else {
- printk(KERN_DEBUG
- "%s: expecting oid 0x%x, received 0x%x.\n",
- ndev->name, (unsigned int) oid,
- frame->header->oid);
- kfree(frame);
- frame = NULL;
- }
- }
- if (timeleft == 0) {
- printk(KERN_DEBUG
- "%s: timeout waiting for mgmt response %lu, "
- "triggering device\n",
- ndev->name, timeout_left);
- islpci_trigger(priv);
- }
- timeout_left += timeleft - wait_cycle_jiffies;
- }
- printk(KERN_WARNING "%s: timeout waiting for mgmt response\n",
- ndev->name);
-
- /* TODO: we should reset the device here */
- out:
- finish_wait(&priv->mgmt_wqueue, &wait);
- mutex_unlock(&priv->mgmt_lock);
- return err;
-}
-
diff --git a/drivers/net/wireless/prism54/oid_mgt.c b/drivers/net/wireless/prism54/oid_mgt.c
deleted file mode 100644
index 3a8d2dbcfecd..000000000000
--- a/drivers/net/wireless/prism54/oid_mgt.c
+++ /dev/null
@@ -1,901 +0,0 @@
-/*
- * Copyright (C) 2003,2004 Aurelien Alleaume <slts@free.fr>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-
-#include "prismcompat.h"
-#include "islpci_dev.h"
-#include "islpci_mgt.h"
-#include "isl_oid.h"
-#include "oid_mgt.h"
-#include "isl_ioctl.h"
-
-/* to convert between channel and freq */
-static const int frequency_list_bg[] = { 2412, 2417, 2422, 2427, 2432,
- 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484
-};
-
-int
-channel_of_freq(int f)
-{
- int c = 0;
-
- if ((f >= 2412) && (f <= 2484)) {
- while ((c < 14) && (f != frequency_list_bg[c]))
- c++;
- return (c >= 14) ? 0 : ++c;
- } else if ((f >= (int) 5000) && (f <= (int) 6000)) {
- return ( (f - 5000) / 5 );
- } else
- return 0;
-}
-
-#define OID_STRUCT(name,oid,s,t) [name] = {oid, 0, sizeof(s), t}
-#define OID_STRUCT_C(name,oid,s,t) OID_STRUCT(name,oid,s,t | OID_FLAG_CACHED)
-#define OID_U32(name,oid) OID_STRUCT(name,oid,u32,OID_TYPE_U32)
-#define OID_U32_C(name,oid) OID_STRUCT_C(name,oid,u32,OID_TYPE_U32)
-#define OID_STRUCT_MLME(name,oid) OID_STRUCT(name,oid,struct obj_mlme,OID_TYPE_MLME)
-#define OID_STRUCT_MLMEEX(name,oid) OID_STRUCT(name,oid,struct obj_mlmeex,OID_TYPE_MLMEEX)
-
-#define OID_UNKNOWN(name,oid) OID_STRUCT(name,oid,0,0)
-
-struct oid_t isl_oid[] = {
- OID_STRUCT(GEN_OID_MACADDRESS, 0x00000000, u8[6], OID_TYPE_ADDR),
- OID_U32(GEN_OID_LINKSTATE, 0x00000001),
- OID_UNKNOWN(GEN_OID_WATCHDOG, 0x00000002),
- OID_UNKNOWN(GEN_OID_MIBOP, 0x00000003),
- OID_UNKNOWN(GEN_OID_OPTIONS, 0x00000004),
- OID_UNKNOWN(GEN_OID_LEDCONFIG, 0x00000005),
-
- /* 802.11 */
- OID_U32_C(DOT11_OID_BSSTYPE, 0x10000000),
- OID_STRUCT_C(DOT11_OID_BSSID, 0x10000001, u8[6], OID_TYPE_RAW),
- OID_STRUCT_C(DOT11_OID_SSID, 0x10000002, struct obj_ssid,
- OID_TYPE_SSID),
- OID_U32(DOT11_OID_STATE, 0x10000003),
- OID_U32(DOT11_OID_AID, 0x10000004),
- OID_STRUCT(DOT11_OID_COUNTRYSTRING, 0x10000005, u8[4], OID_TYPE_RAW),
- OID_STRUCT_C(DOT11_OID_SSIDOVERRIDE, 0x10000006, struct obj_ssid,
- OID_TYPE_SSID),
-
- OID_U32(DOT11_OID_MEDIUMLIMIT, 0x11000000),
- OID_U32_C(DOT11_OID_BEACONPERIOD, 0x11000001),
- OID_U32(DOT11_OID_DTIMPERIOD, 0x11000002),
- OID_U32(DOT11_OID_ATIMWINDOW, 0x11000003),
- OID_U32(DOT11_OID_LISTENINTERVAL, 0x11000004),
- OID_U32(DOT11_OID_CFPPERIOD, 0x11000005),
- OID_U32(DOT11_OID_CFPDURATION, 0x11000006),
-
- OID_U32_C(DOT11_OID_AUTHENABLE, 0x12000000),
- OID_U32_C(DOT11_OID_PRIVACYINVOKED, 0x12000001),
- OID_U32_C(DOT11_OID_EXUNENCRYPTED, 0x12000002),
- OID_U32_C(DOT11_OID_DEFKEYID, 0x12000003),
- [DOT11_OID_DEFKEYX] = {0x12000004, 3, sizeof (struct obj_key),
- OID_FLAG_CACHED | OID_TYPE_KEY}, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */
- OID_UNKNOWN(DOT11_OID_STAKEY, 0x12000008),
- OID_U32(DOT11_OID_REKEYTHRESHOLD, 0x12000009),
- OID_UNKNOWN(DOT11_OID_STASC, 0x1200000a),
-
- OID_U32(DOT11_OID_PRIVTXREJECTED, 0x1a000000),
- OID_U32(DOT11_OID_PRIVRXPLAIN, 0x1a000001),
- OID_U32(DOT11_OID_PRIVRXFAILED, 0x1a000002),
- OID_U32(DOT11_OID_PRIVRXNOKEY, 0x1a000003),
-
- OID_U32_C(DOT11_OID_RTSTHRESH, 0x13000000),
- OID_U32_C(DOT11_OID_FRAGTHRESH, 0x13000001),
- OID_U32_C(DOT11_OID_SHORTRETRIES, 0x13000002),
- OID_U32_C(DOT11_OID_LONGRETRIES, 0x13000003),
- OID_U32_C(DOT11_OID_MAXTXLIFETIME, 0x13000004),
- OID_U32(DOT11_OID_MAXRXLIFETIME, 0x13000005),
- OID_U32(DOT11_OID_AUTHRESPTIMEOUT, 0x13000006),
- OID_U32(DOT11_OID_ASSOCRESPTIMEOUT, 0x13000007),
-
- OID_UNKNOWN(DOT11_OID_ALOFT_TABLE, 0x1d000000),
- OID_UNKNOWN(DOT11_OID_ALOFT_CTRL_TABLE, 0x1d000001),
- OID_UNKNOWN(DOT11_OID_ALOFT_RETREAT, 0x1d000002),
- OID_UNKNOWN(DOT11_OID_ALOFT_PROGRESS, 0x1d000003),
- OID_U32(DOT11_OID_ALOFT_FIXEDRATE, 0x1d000004),
- OID_UNKNOWN(DOT11_OID_ALOFT_RSSIGRAPH, 0x1d000005),
- OID_UNKNOWN(DOT11_OID_ALOFT_CONFIG, 0x1d000006),
-
- [DOT11_OID_VDCFX] = {0x1b000000, 7, 0, 0},
- OID_U32(DOT11_OID_MAXFRAMEBURST, 0x1b000008),
-
- OID_U32(DOT11_OID_PSM, 0x14000000),
- OID_U32(DOT11_OID_CAMTIMEOUT, 0x14000001),
- OID_U32(DOT11_OID_RECEIVEDTIMS, 0x14000002),
- OID_U32(DOT11_OID_ROAMPREFERENCE, 0x14000003),
-
- OID_U32(DOT11_OID_BRIDGELOCAL, 0x15000000),
- OID_U32(DOT11_OID_CLIENTS, 0x15000001),
- OID_U32(DOT11_OID_CLIENTSASSOCIATED, 0x15000002),
- [DOT11_OID_CLIENTX] = {0x15000003, 2006, 0, 0}, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */
-
- OID_STRUCT(DOT11_OID_CLIENTFIND, 0x150007DB, u8[6], OID_TYPE_ADDR),
- OID_STRUCT(DOT11_OID_WDSLINKADD, 0x150007DC, u8[6], OID_TYPE_ADDR),
- OID_STRUCT(DOT11_OID_WDSLINKREMOVE, 0x150007DD, u8[6], OID_TYPE_ADDR),
- OID_STRUCT(DOT11_OID_EAPAUTHSTA, 0x150007DE, u8[6], OID_TYPE_ADDR),
- OID_STRUCT(DOT11_OID_EAPUNAUTHSTA, 0x150007DF, u8[6], OID_TYPE_ADDR),
- OID_U32_C(DOT11_OID_DOT1XENABLE, 0x150007E0),
- OID_UNKNOWN(DOT11_OID_MICFAILURE, 0x150007E1),
- OID_UNKNOWN(DOT11_OID_REKEYINDICATE, 0x150007E2),
-
- OID_U32(DOT11_OID_MPDUTXSUCCESSFUL, 0x16000000),
- OID_U32(DOT11_OID_MPDUTXONERETRY, 0x16000001),
- OID_U32(DOT11_OID_MPDUTXMULTIPLERETRIES, 0x16000002),
- OID_U32(DOT11_OID_MPDUTXFAILED, 0x16000003),
- OID_U32(DOT11_OID_MPDURXSUCCESSFUL, 0x16000004),
- OID_U32(DOT11_OID_MPDURXDUPS, 0x16000005),
- OID_U32(DOT11_OID_RTSSUCCESSFUL, 0x16000006),
- OID_U32(DOT11_OID_RTSFAILED, 0x16000007),
- OID_U32(DOT11_OID_ACKFAILED, 0x16000008),
- OID_U32(DOT11_OID_FRAMERECEIVES, 0x16000009),
- OID_U32(DOT11_OID_FRAMEERRORS, 0x1600000A),
- OID_U32(DOT11_OID_FRAMEABORTS, 0x1600000B),
- OID_U32(DOT11_OID_FRAMEABORTSPHY, 0x1600000C),
-
- OID_U32(DOT11_OID_SLOTTIME, 0x17000000),
- OID_U32(DOT11_OID_CWMIN, 0x17000001),
- OID_U32(DOT11_OID_CWMAX, 0x17000002),
- OID_U32(DOT11_OID_ACKWINDOW, 0x17000003),
- OID_U32(DOT11_OID_ANTENNARX, 0x17000004),
- OID_U32(DOT11_OID_ANTENNATX, 0x17000005),
- OID_U32(DOT11_OID_ANTENNADIVERSITY, 0x17000006),
- OID_U32_C(DOT11_OID_CHANNEL, 0x17000007),
- OID_U32_C(DOT11_OID_EDTHRESHOLD, 0x17000008),
- OID_U32(DOT11_OID_PREAMBLESETTINGS, 0x17000009),
- OID_STRUCT(DOT11_OID_RATES, 0x1700000A, u8[IWMAX_BITRATES + 1],
- OID_TYPE_RAW),
- OID_U32(DOT11_OID_CCAMODESUPPORTED, 0x1700000B),
- OID_U32(DOT11_OID_CCAMODE, 0x1700000C),
- OID_UNKNOWN(DOT11_OID_RSSIVECTOR, 0x1700000D),
- OID_UNKNOWN(DOT11_OID_OUTPUTPOWERTABLE, 0x1700000E),
- OID_U32(DOT11_OID_OUTPUTPOWER, 0x1700000F),
- OID_STRUCT(DOT11_OID_SUPPORTEDRATES, 0x17000010,
- u8[IWMAX_BITRATES + 1], OID_TYPE_RAW),
- OID_U32_C(DOT11_OID_FREQUENCY, 0x17000011),
- [DOT11_OID_SUPPORTEDFREQUENCIES] =
- {0x17000012, 0, sizeof (struct obj_frequencies)
- + sizeof (u16) * IWMAX_FREQ, OID_TYPE_FREQUENCIES},
-
- OID_U32(DOT11_OID_NOISEFLOOR, 0x17000013),
- OID_STRUCT(DOT11_OID_FREQUENCYACTIVITY, 0x17000014, u8[IWMAX_FREQ + 1],
- OID_TYPE_RAW),
- OID_UNKNOWN(DOT11_OID_IQCALIBRATIONTABLE, 0x17000015),
- OID_U32(DOT11_OID_NONERPPROTECTION, 0x17000016),
- OID_U32(DOT11_OID_SLOTSETTINGS, 0x17000017),
- OID_U32(DOT11_OID_NONERPTIMEOUT, 0x17000018),
- OID_U32(DOT11_OID_PROFILES, 0x17000019),
- OID_STRUCT(DOT11_OID_EXTENDEDRATES, 0x17000020,
- u8[IWMAX_BITRATES + 1], OID_TYPE_RAW),
-
- OID_STRUCT_MLME(DOT11_OID_DEAUTHENTICATE, 0x18000000),
- OID_STRUCT_MLME(DOT11_OID_AUTHENTICATE, 0x18000001),
- OID_STRUCT_MLME(DOT11_OID_DISASSOCIATE, 0x18000002),
- OID_STRUCT_MLME(DOT11_OID_ASSOCIATE, 0x18000003),
- OID_UNKNOWN(DOT11_OID_SCAN, 0x18000004),
- OID_STRUCT_MLMEEX(DOT11_OID_BEACON, 0x18000005),
- OID_STRUCT_MLMEEX(DOT11_OID_PROBE, 0x18000006),
- OID_STRUCT_MLMEEX(DOT11_OID_DEAUTHENTICATEEX, 0x18000007),
- OID_STRUCT_MLMEEX(DOT11_OID_AUTHENTICATEEX, 0x18000008),
- OID_STRUCT_MLMEEX(DOT11_OID_DISASSOCIATEEX, 0x18000009),
- OID_STRUCT_MLMEEX(DOT11_OID_ASSOCIATEEX, 0x1800000A),
- OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATE, 0x1800000B),
- OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATEEX, 0x1800000C),
-
- OID_U32(DOT11_OID_NONERPSTATUS, 0x1E000000),
-
- OID_U32(DOT11_OID_STATIMEOUT, 0x19000000),
- OID_U32_C(DOT11_OID_MLMEAUTOLEVEL, 0x19000001),
- OID_U32(DOT11_OID_BSSTIMEOUT, 0x19000002),
- [DOT11_OID_ATTACHMENT] = {0x19000003, 0,
- sizeof(struct obj_attachment), OID_TYPE_ATTACH},
- OID_STRUCT_C(DOT11_OID_PSMBUFFER, 0x19000004, struct obj_buffer,
- OID_TYPE_BUFFER),
-
- OID_U32(DOT11_OID_BSSS, 0x1C000000),
- [DOT11_OID_BSSX] = {0x1C000001, 63, sizeof (struct obj_bss),
- OID_TYPE_BSS}, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */
- OID_STRUCT(DOT11_OID_BSSFIND, 0x1C000042, struct obj_bss, OID_TYPE_BSS),
- [DOT11_OID_BSSLIST] = {0x1C000043, 0, sizeof (struct
- obj_bsslist) +
- sizeof (struct obj_bss[IWMAX_BSS]),
- OID_TYPE_BSSLIST},
-
- OID_UNKNOWN(OID_INL_TUNNEL, 0xFF020000),
- OID_UNKNOWN(OID_INL_MEMADDR, 0xFF020001),
- OID_UNKNOWN(OID_INL_MEMORY, 0xFF020002),
- OID_U32_C(OID_INL_MODE, 0xFF020003),
- OID_UNKNOWN(OID_INL_COMPONENT_NR, 0xFF020004),
- OID_STRUCT(OID_INL_VERSION, 0xFF020005, u8[8], OID_TYPE_RAW),
- OID_UNKNOWN(OID_INL_INTERFACE_ID, 0xFF020006),
- OID_UNKNOWN(OID_INL_COMPONENT_ID, 0xFF020007),
- OID_U32_C(OID_INL_CONFIG, 0xFF020008),
- OID_U32_C(OID_INL_DOT11D_CONFORMANCE, 0xFF02000C),
- OID_U32(OID_INL_PHYCAPABILITIES, 0xFF02000D),
- OID_U32_C(OID_INL_OUTPUTPOWER, 0xFF02000F),
-
-};
-
-int
-mgt_init(islpci_private *priv)
-{
- int i;
-
- priv->mib = kcalloc(OID_NUM_LAST, sizeof (void *), GFP_KERNEL);
- if (!priv->mib)
- return -ENOMEM;
-
- /* Alloc the cache */
- for (i = 0; i < OID_NUM_LAST; i++) {
- if (isl_oid[i].flags & OID_FLAG_CACHED) {
- priv->mib[i] = kzalloc(isl_oid[i].size *
- (isl_oid[i].range + 1),
- GFP_KERNEL);
- if (!priv->mib[i])
- return -ENOMEM;
- } else
- priv->mib[i] = NULL;
- }
-
- init_rwsem(&priv->mib_sem);
- prism54_mib_init(priv);
-
- return 0;
-}
-
-void
-mgt_clean(islpci_private *priv)
-{
- int i;
-
- if (!priv->mib)
- return;
- for (i = 0; i < OID_NUM_LAST; i++) {
- kfree(priv->mib[i]);
- priv->mib[i] = NULL;
- }
- kfree(priv->mib);
- priv->mib = NULL;
-}
-
-void
-mgt_le_to_cpu(int type, void *data)
-{
- switch (type) {
- case OID_TYPE_U32:
- *(u32 *) data = le32_to_cpu(*(u32 *) data);
- break;
- case OID_TYPE_BUFFER:{
- struct obj_buffer *buff = data;
- buff->size = le32_to_cpu(buff->size);
- buff->addr = le32_to_cpu(buff->addr);
- break;
- }
- case OID_TYPE_BSS:{
- struct obj_bss *bss = data;
- bss->age = le16_to_cpu(bss->age);
- bss->channel = le16_to_cpu(bss->channel);
- bss->capinfo = le16_to_cpu(bss->capinfo);
- bss->rates = le16_to_cpu(bss->rates);
- bss->basic_rates = le16_to_cpu(bss->basic_rates);
- break;
- }
- case OID_TYPE_BSSLIST:{
- struct obj_bsslist *list = data;
- int i;
- list->nr = le32_to_cpu(list->nr);
- for (i = 0; i < list->nr; i++)
- mgt_le_to_cpu(OID_TYPE_BSS, &list->bsslist[i]);
- break;
- }
- case OID_TYPE_FREQUENCIES:{
- struct obj_frequencies *freq = data;
- int i;
- freq->nr = le16_to_cpu(freq->nr);
- for (i = 0; i < freq->nr; i++)
- freq->mhz[i] = le16_to_cpu(freq->mhz[i]);
- break;
- }
- case OID_TYPE_MLME:{
- struct obj_mlme *mlme = data;
- mlme->id = le16_to_cpu(mlme->id);
- mlme->state = le16_to_cpu(mlme->state);
- mlme->code = le16_to_cpu(mlme->code);
- break;
- }
- case OID_TYPE_MLMEEX:{
- struct obj_mlmeex *mlme = data;
- mlme->id = le16_to_cpu(mlme->id);
- mlme->state = le16_to_cpu(mlme->state);
- mlme->code = le16_to_cpu(mlme->code);
- mlme->size = le16_to_cpu(mlme->size);
- break;
- }
- case OID_TYPE_ATTACH:{
- struct obj_attachment *attach = data;
- attach->id = le16_to_cpu(attach->id);
- attach->size = le16_to_cpu(attach->size);
- break;
- }
- case OID_TYPE_SSID:
- case OID_TYPE_KEY:
- case OID_TYPE_ADDR:
- case OID_TYPE_RAW:
- break;
- default:
- BUG();
- }
-}
-
-static void
-mgt_cpu_to_le(int type, void *data)
-{
- switch (type) {
- case OID_TYPE_U32:
- *(u32 *) data = cpu_to_le32(*(u32 *) data);
- break;
- case OID_TYPE_BUFFER:{
- struct obj_buffer *buff = data;
- buff->size = cpu_to_le32(buff->size);
- buff->addr = cpu_to_le32(buff->addr);
- break;
- }
- case OID_TYPE_BSS:{
- struct obj_bss *bss = data;
- bss->age = cpu_to_le16(bss->age);
- bss->channel = cpu_to_le16(bss->channel);
- bss->capinfo = cpu_to_le16(bss->capinfo);
- bss->rates = cpu_to_le16(bss->rates);
- bss->basic_rates = cpu_to_le16(bss->basic_rates);
- break;
- }
- case OID_TYPE_BSSLIST:{
- struct obj_bsslist *list = data;
- int i;
- list->nr = cpu_to_le32(list->nr);
- for (i = 0; i < list->nr; i++)
- mgt_cpu_to_le(OID_TYPE_BSS, &list->bsslist[i]);
- break;
- }
- case OID_TYPE_FREQUENCIES:{
- struct obj_frequencies *freq = data;
- int i;
- freq->nr = cpu_to_le16(freq->nr);
- for (i = 0; i < freq->nr; i++)
- freq->mhz[i] = cpu_to_le16(freq->mhz[i]);
- break;
- }
- case OID_TYPE_MLME:{
- struct obj_mlme *mlme = data;
- mlme->id = cpu_to_le16(mlme->id);
- mlme->state = cpu_to_le16(mlme->state);
- mlme->code = cpu_to_le16(mlme->code);
- break;
- }
- case OID_TYPE_MLMEEX:{
- struct obj_mlmeex *mlme = data;
- mlme->id = cpu_to_le16(mlme->id);
- mlme->state = cpu_to_le16(mlme->state);
- mlme->code = cpu_to_le16(mlme->code);
- mlme->size = cpu_to_le16(mlme->size);
- break;
- }
- case OID_TYPE_ATTACH:{
- struct obj_attachment *attach = data;
- attach->id = cpu_to_le16(attach->id);
- attach->size = cpu_to_le16(attach->size);
- break;
- }
- case OID_TYPE_SSID:
- case OID_TYPE_KEY:
- case OID_TYPE_ADDR:
- case OID_TYPE_RAW:
- break;
- default:
- BUG();
- }
-}
-
-/* Note : data is modified during this function */
-
-int
-mgt_set_request(islpci_private *priv, enum oid_num_t n, int extra, void *data)
-{
- int ret = 0;
- struct islpci_mgmtframe *response = NULL;
- int response_op = PIMFOR_OP_ERROR;
- int dlen;
- void *cache, *_data = data;
- u32 oid;
-
- BUG_ON(OID_NUM_LAST <= n);
- BUG_ON(extra > isl_oid[n].range);
-
- if (!priv->mib)
- /* memory has been freed */
- return -1;
-
- dlen = isl_oid[n].size;
- cache = priv->mib[n];
- cache += (cache ? extra * dlen : 0);
- oid = isl_oid[n].oid + extra;
-
- if (_data == NULL)
- /* we are requested to re-set a cached value */
- _data = cache;
- else
- mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, _data);
- /* If we are going to write to the cache, we don't want anyone to read
- * it -> acquire write lock.
- * Else we could acquire a read lock to be sure we don't bother the
- * commit process (which takes a write lock). But I'm not sure if it's
- * needed.
- */
- if (cache)
- down_write(&priv->mib_sem);
-
- if (islpci_get_state(priv) >= PRV_STATE_READY) {
- ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid,
- _data, dlen, &response);
- if (!ret) {
- response_op = response->header->operation;
- islpci_mgt_release(response);
- }
- if (ret || response_op == PIMFOR_OP_ERROR)
- ret = -EIO;
- } else if (!cache)
- ret = -EIO;
-
- if (cache) {
- if (!ret && data)
- memcpy(cache, _data, dlen);
- up_write(&priv->mib_sem);
- }
-
- /* re-set given data to what it was */
- if (data)
- mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data);
-
- return ret;
-}
-
-/* None of these are cached */
-int
-mgt_set_varlen(islpci_private *priv, enum oid_num_t n, void *data, int extra_len)
-{
- int ret = 0;
- struct islpci_mgmtframe *response;
- int response_op = PIMFOR_OP_ERROR;
- int dlen;
- u32 oid;
-
- BUG_ON(OID_NUM_LAST <= n);
-
- dlen = isl_oid[n].size;
- oid = isl_oid[n].oid;
-
- mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, data);
-
- if (islpci_get_state(priv) >= PRV_STATE_READY) {
- ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid,
- data, dlen + extra_len, &response);
- if (!ret) {
- response_op = response->header->operation;
- islpci_mgt_release(response);
- }
- if (ret || response_op == PIMFOR_OP_ERROR)
- ret = -EIO;
- } else
- ret = -EIO;
-
- /* re-set given data to what it was */
- if (data)
- mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data);
-
- return ret;
-}
-
-int
-mgt_get_request(islpci_private *priv, enum oid_num_t n, int extra, void *data,
- union oid_res_t *res)
-{
-
- int ret = -EIO;
- int reslen = 0;
- struct islpci_mgmtframe *response = NULL;
-
- int dlen;
- void *cache, *_res = NULL;
- u32 oid;
-
- BUG_ON(OID_NUM_LAST <= n);
- BUG_ON(extra > isl_oid[n].range);
-
- res->ptr = NULL;
-
- if (!priv->mib)
- /* memory has been freed */
- return -1;
-
- dlen = isl_oid[n].size;
- cache = priv->mib[n];
- cache += cache ? extra * dlen : 0;
- oid = isl_oid[n].oid + extra;
- reslen = dlen;
-
- if (cache)
- down_read(&priv->mib_sem);
-
- if (islpci_get_state(priv) >= PRV_STATE_READY) {
- ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
- oid, data, dlen, &response);
- if (ret || !response ||
- response->header->operation == PIMFOR_OP_ERROR) {
- if (response)
- islpci_mgt_release(response);
- ret = -EIO;
- }
- if (!ret) {
- _res = response->data;
- reslen = response->header->length;
- }
- } else if (cache) {
- _res = cache;
- ret = 0;
- }
- if ((isl_oid[n].flags & OID_FLAG_TYPE) == OID_TYPE_U32)
- res->u = ret ? 0 : le32_to_cpu(*(u32 *) _res);
- else {
- res->ptr = kmalloc(reslen, GFP_KERNEL);
- BUG_ON(res->ptr == NULL);
- if (ret)
- memset(res->ptr, 0, reslen);
- else {
- memcpy(res->ptr, _res, reslen);
- mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE,
- res->ptr);
- }
- }
- if (cache)
- up_read(&priv->mib_sem);
-
- if (response && !ret)
- islpci_mgt_release(response);
-
- if (reslen > isl_oid[n].size)
- printk(KERN_DEBUG
- "mgt_get_request(0x%x): received data length was bigger "
- "than expected (%d > %d). Memory is probably corrupted...",
- oid, reslen, isl_oid[n].size);
-
- return ret;
-}
-
-/* lock outside */
-int
-mgt_commit_list(islpci_private *priv, enum oid_num_t *l, int n)
-{
- int i, ret = 0;
- struct islpci_mgmtframe *response;
-
- for (i = 0; i < n; i++) {
- struct oid_t *t = &(isl_oid[l[i]]);
- void *data = priv->mib[l[i]];
- int j = 0;
- u32 oid = t->oid;
- BUG_ON(data == NULL);
- while (j <= t->range) {
- int r = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET,
- oid, data, t->size,
- &response);
- if (response) {
- r |= (response->header->operation == PIMFOR_OP_ERROR);
- islpci_mgt_release(response);
- }
- if (r)
- printk(KERN_ERR "%s: mgt_commit_list: failure. "
- "oid=%08x err=%d\n",
- priv->ndev->name, oid, r);
- ret |= r;
- j++;
- oid++;
- data += t->size;
- }
- }
- return ret;
-}
-
-/* Lock outside */
-
-void
-mgt_set(islpci_private *priv, enum oid_num_t n, void *data)
-{
- BUG_ON(OID_NUM_LAST <= n);
- BUG_ON(priv->mib[n] == NULL);
-
- memcpy(priv->mib[n], data, isl_oid[n].size);
- mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, priv->mib[n]);
-}
-
-void
-mgt_get(islpci_private *priv, enum oid_num_t n, void *res)
-{
- BUG_ON(OID_NUM_LAST <= n);
- BUG_ON(priv->mib[n] == NULL);
- BUG_ON(res == NULL);
-
- memcpy(res, priv->mib[n], isl_oid[n].size);
- mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, res);
-}
-
-/* Commits the cache. Lock outside. */
-
-static enum oid_num_t commit_part1[] = {
- OID_INL_CONFIG,
- OID_INL_MODE,
- DOT11_OID_BSSTYPE,
- DOT11_OID_CHANNEL,
- DOT11_OID_MLMEAUTOLEVEL
-};
-
-static enum oid_num_t commit_part2[] = {
- DOT11_OID_SSID,
- DOT11_OID_PSMBUFFER,
- DOT11_OID_AUTHENABLE,
- DOT11_OID_PRIVACYINVOKED,
- DOT11_OID_EXUNENCRYPTED,
- DOT11_OID_DEFKEYX, /* MULTIPLE */
- DOT11_OID_DEFKEYID,
- DOT11_OID_DOT1XENABLE,
- OID_INL_DOT11D_CONFORMANCE,
- /* Do not initialize this - fw < 1.0.4.3 rejects it
- OID_INL_OUTPUTPOWER,
- */
-};
-
-/* update the MAC addr. */
-static int
-mgt_update_addr(islpci_private *priv)
-{
- struct islpci_mgmtframe *res;
- int ret;
-
- ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
- isl_oid[GEN_OID_MACADDRESS].oid, NULL,
- isl_oid[GEN_OID_MACADDRESS].size, &res);
-
- if ((ret == 0) && res && (res->header->operation != PIMFOR_OP_ERROR))
- memcpy(priv->ndev->dev_addr, res->data, ETH_ALEN);
- else
- ret = -EIO;
- if (res)
- islpci_mgt_release(res);
-
- if (ret)
- printk(KERN_ERR "%s: mgt_update_addr: failure\n", priv->ndev->name);
- return ret;
-}
-
-int
-mgt_commit(islpci_private *priv)
-{
- int rvalue;
- enum oid_num_t u;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return 0;
-
- rvalue = mgt_commit_list(priv, commit_part1, ARRAY_SIZE(commit_part1));
-
- if (priv->iw_mode != IW_MODE_MONITOR)
- rvalue |= mgt_commit_list(priv, commit_part2, ARRAY_SIZE(commit_part2));
-
- u = OID_INL_MODE;
- rvalue |= mgt_commit_list(priv, &u, 1);
- rvalue |= mgt_update_addr(priv);
-
- if (rvalue) {
- /* some request have failed. The device might be in an
- incoherent state. We should reset it ! */
- printk(KERN_DEBUG "%s: mgt_commit: failure\n", priv->ndev->name);
- }
- return rvalue;
-}
-
-/* The following OIDs need to be "unlatched":
- *
- * MEDIUMLIMIT,BEACONPERIOD,DTIMPERIOD,ATIMWINDOW,LISTENINTERVAL
- * FREQUENCY,EXTENDEDRATES.
- *
- * The way to do this is to set ESSID. Note though that they may get
- * unlatch before though by setting another OID. */
-#if 0
-void
-mgt_unlatch_all(islpci_private *priv)
-{
- u32 u;
- int rvalue = 0;
-
- if (islpci_get_state(priv) < PRV_STATE_INIT)
- return;
-
- u = DOT11_OID_SSID;
- rvalue = mgt_commit_list(priv, &u, 1);
- /* Necessary if in MANUAL RUN mode? */
-#if 0
- u = OID_INL_MODE;
- rvalue |= mgt_commit_list(priv, &u, 1);
-
- u = DOT11_OID_MLMEAUTOLEVEL;
- rvalue |= mgt_commit_list(priv, &u, 1);
-
- u = OID_INL_MODE;
- rvalue |= mgt_commit_list(priv, &u, 1);
-#endif
-
- if (rvalue)
- printk(KERN_DEBUG "%s: Unlatching OIDs failed\n", priv->ndev->name);
-}
-#endif
-
-/* This will tell you if you are allowed to answer a mlme(ex) request .*/
-
-int
-mgt_mlme_answer(islpci_private *priv)
-{
- u32 mlmeautolevel;
- /* Acquire a read lock because if we are in a mode change, it's
- * possible to answer true, while the card is leaving master to managed
- * mode. Answering to a mlme in this situation could hang the card.
- */
- down_read(&priv->mib_sem);
- mlmeautolevel =
- le32_to_cpu(*(u32 *) priv->mib[DOT11_OID_MLMEAUTOLEVEL]);
- up_read(&priv->mib_sem);
-
- return ((priv->iw_mode == IW_MODE_MASTER) &&
- (mlmeautolevel >= DOT11_MLME_INTERMEDIATE));
-}
-
-enum oid_num_t
-mgt_oidtonum(u32 oid)
-{
- int i;
-
- for (i = 0; i < OID_NUM_LAST; i++)
- if (isl_oid[i].oid == oid)
- return i;
-
- printk(KERN_DEBUG "looking for an unknown oid 0x%x", oid);
-
- return OID_NUM_LAST;
-}
-
-int
-mgt_response_to_str(enum oid_num_t n, union oid_res_t *r, char *str)
-{
- switch (isl_oid[n].flags & OID_FLAG_TYPE) {
- case OID_TYPE_U32:
- return snprintf(str, PRIV_STR_SIZE, "%u\n", r->u);
- case OID_TYPE_BUFFER:{
- struct obj_buffer *buff = r->ptr;
- return snprintf(str, PRIV_STR_SIZE,
- "size=%u\naddr=0x%X\n", buff->size,
- buff->addr);
- }
- break;
- case OID_TYPE_BSS:{
- struct obj_bss *bss = r->ptr;
- return snprintf(str, PRIV_STR_SIZE,
- "age=%u\nchannel=%u\n"
- "capinfo=0x%X\nrates=0x%X\n"
- "basic_rates=0x%X\n", bss->age,
- bss->channel, bss->capinfo,
- bss->rates, bss->basic_rates);
- }
- break;
- case OID_TYPE_BSSLIST:{
- struct obj_bsslist *list = r->ptr;
- int i, k;
- k = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", list->nr);
- for (i = 0; i < list->nr; i++)
- k += snprintf(str + k, PRIV_STR_SIZE - k,
- "bss[%u] :\nage=%u\nchannel=%u\n"
- "capinfo=0x%X\nrates=0x%X\n"
- "basic_rates=0x%X\n",
- i, list->bsslist[i].age,
- list->bsslist[i].channel,
- list->bsslist[i].capinfo,
- list->bsslist[i].rates,
- list->bsslist[i].basic_rates);
- return k;
- }
- break;
- case OID_TYPE_FREQUENCIES:{
- struct obj_frequencies *freq = r->ptr;
- int i, t;
- printk("nr : %u\n", freq->nr);
- t = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", freq->nr);
- for (i = 0; i < freq->nr; i++)
- t += snprintf(str + t, PRIV_STR_SIZE - t,
- "mhz[%u]=%u\n", i, freq->mhz[i]);
- return t;
- }
- break;
- case OID_TYPE_MLME:{
- struct obj_mlme *mlme = r->ptr;
- return snprintf(str, PRIV_STR_SIZE,
- "id=0x%X\nstate=0x%X\ncode=0x%X\n",
- mlme->id, mlme->state, mlme->code);
- }
- break;
- case OID_TYPE_MLMEEX:{
- struct obj_mlmeex *mlme = r->ptr;
- return snprintf(str, PRIV_STR_SIZE,
- "id=0x%X\nstate=0x%X\n"
- "code=0x%X\nsize=0x%X\n", mlme->id,
- mlme->state, mlme->code, mlme->size);
- }
- break;
- case OID_TYPE_ATTACH:{
- struct obj_attachment *attach = r->ptr;
- return snprintf(str, PRIV_STR_SIZE,
- "id=%d\nsize=%d\n",
- attach->id,
- attach->size);
- }
- break;
- case OID_TYPE_SSID:{
- struct obj_ssid *ssid = r->ptr;
- return snprintf(str, PRIV_STR_SIZE,
- "length=%u\noctets=%.*s\n",
- ssid->length, ssid->length,
- ssid->octets);
- }
- break;
- case OID_TYPE_KEY:{
- struct obj_key *key = r->ptr;
- int t, i;
- t = snprintf(str, PRIV_STR_SIZE,
- "type=0x%X\nlength=0x%X\nkey=0x",
- key->type, key->length);
- for (i = 0; i < key->length; i++)
- t += snprintf(str + t, PRIV_STR_SIZE - t,
- "%02X:", key->key[i]);
- t += snprintf(str + t, PRIV_STR_SIZE - t, "\n");
- return t;
- }
- break;
- case OID_TYPE_RAW:
- case OID_TYPE_ADDR:{
- unsigned char *buff = r->ptr;
- int t, i;
- t = snprintf(str, PRIV_STR_SIZE, "hex data=");
- for (i = 0; i < isl_oid[n].size; i++)
- t += snprintf(str + t, PRIV_STR_SIZE - t,
- "%02X:", buff[i]);
- t += snprintf(str + t, PRIV_STR_SIZE - t, "\n");
- return t;
- }
- break;
- default:
- BUG();
- }
- return 0;
-}
diff --git a/drivers/net/wireless/ralink/Kconfig b/drivers/net/wireless/ralink/Kconfig
new file mode 100644
index 000000000000..41dbf3130e2b
--- /dev/null
+++ b/drivers/net/wireless/ralink/Kconfig
@@ -0,0 +1,16 @@
+config WLAN_VENDOR_RALINK
+ bool "Ralink devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_RALINK
+
+source "drivers/net/wireless/ralink/rt2x00/Kconfig"
+
+endif # WLAN_VENDOR_RALINK
diff --git a/drivers/net/wireless/ralink/Makefile b/drivers/net/wireless/ralink/Makefile
new file mode 100644
index 000000000000..f84c0a2e4f4d
--- /dev/null
+++ b/drivers/net/wireless/ralink/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_RT2X00) += rt2x00/
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/ralink/rt2x00/Kconfig
index de62f5dcb62f..de62f5dcb62f 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/ralink/rt2x00/Kconfig
diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/ralink/rt2x00/Makefile
index 24a66015a495..24a66015a495 100644
--- a/drivers/net/wireless/rt2x00/Makefile
+++ b/drivers/net/wireless/ralink/rt2x00/Makefile
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
index 9a3966cd6fbe..9a3966cd6fbe 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h
index 0fd3a9d01a60..0fd3a9d01a60 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
index 1a6740b4d396..1a6740b4d396 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h
index 573e87bcc553..573e87bcc553 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
new file mode 100644
index 000000000000..d26018f30b7d
--- /dev/null
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
@@ -0,0 +1,2004 @@
+/*
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ Module: rt2500usb
+ Abstract: rt2500usb device specific routines.
+ Supported chipsets: RT2570.
+ */
+
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "rt2x00.h"
+#include "rt2x00usb.h"
+#include "rt2500usb.h"
+
+/*
+ * Allow hardware encryption to be disabled.
+ */
+static bool modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+/*
+ * Register access.
+ * All access to the CSR registers will go through the methods
+ * rt2500usb_register_read and rt2500usb_register_write.
+ * BBP and RF register require indirect register access,
+ * and use the CSR registers BBPCSR and RFCSR to achieve this.
+ * These indirect registers work with busy bits,
+ * and we will try maximal REGISTER_USB_BUSY_COUNT times to access
+ * the register while taking a REGISTER_BUSY_DELAY us delay
+ * between each attampt. When the busy bit is still set at that time,
+ * the access attempt is considered to have failed,
+ * and we will print an error.
+ * If the csr_mutex is already held then the _lock variants must
+ * be used instead.
+ */
+static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u16 *value)
+{
+ __le16 reg;
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
+ USB_VENDOR_REQUEST_IN, offset,
+ &reg, sizeof(reg));
+ *value = le16_to_cpu(reg);
+}
+
+static inline void rt2500usb_register_read_lock(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u16 *value)
+{
+ __le16 reg;
+ rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ,
+ USB_VENDOR_REQUEST_IN, offset,
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
+ *value = le16_to_cpu(reg);
+}
+
+static inline void rt2500usb_register_multiread(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ void *value, const u16 length)
+{
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
+ USB_VENDOR_REQUEST_IN, offset,
+ value, length);
+}
+
+static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u16 value)
+{
+ __le16 reg = cpu_to_le16(value);
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
+ USB_VENDOR_REQUEST_OUT, offset,
+ &reg, sizeof(reg));
+}
+
+static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u16 value)
+{
+ __le16 reg = cpu_to_le16(value);
+ rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE,
+ USB_VENDOR_REQUEST_OUT, offset,
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
+}
+
+static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ void *value, const u16 length)
+{
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
+ USB_VENDOR_REQUEST_OUT, offset,
+ value, length);
+}
+
+static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ struct rt2x00_field16 field,
+ u16 *reg)
+{
+ unsigned int i;
+
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
+ rt2500usb_register_read_lock(rt2x00dev, offset, reg);
+ if (!rt2x00_get_field16(*reg, field))
+ return 1;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n",
+ offset, *reg);
+ *reg = ~0;
+
+ return 0;
+}
+
+#define WAIT_FOR_BBP(__dev, __reg) \
+ rt2500usb_regbusy_read((__dev), PHY_CSR8, PHY_CSR8_BUSY, (__reg))
+#define WAIT_FOR_RF(__dev, __reg) \
+ rt2500usb_regbusy_read((__dev), PHY_CSR10, PHY_CSR10_RF_BUSY, (__reg))
+
+static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, const u8 value)
+{
+ u16 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR7_DATA, value);
+ rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
+ rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 0);
+
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
+ }
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, u8 *value)
+{
+ u16 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
+ */
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
+ rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 1);
+
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
+
+ if (WAIT_FOR_BBP(rt2x00dev, &reg))
+ rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, &reg);
+ }
+
+ *value = rt2x00_get_field16(reg, PHY_CSR7_DATA);
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, const u32 value)
+{
+ u16 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the RF becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_RF(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR9_RF_VALUE, value);
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg);
+
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_VALUE, value >> 16);
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_NUMBER_OF_BITS, 20);
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_IF_SELECT, 0);
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_BUSY, 1);
+
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg);
+ rt2x00_rf_write(rt2x00dev, word, value);
+ }
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
+static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 *value)
+{
+ u16 tmp;
+
+ rt2500usb_register_read(rt2x00dev, offset, &tmp);
+ *value = tmp;
+}
+
+static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 value)
+{
+ rt2500usb_register_write(rt2x00dev, offset, value);
+}
+
+static const struct rt2x00debug rt2500usb_rt2x00debug = {
+ .owner = THIS_MODULE,
+ .csr = {
+ .read = _rt2500usb_register_read,
+ .write = _rt2500usb_register_write,
+ .flags = RT2X00DEBUGFS_OFFSET,
+ .word_base = CSR_REG_BASE,
+ .word_size = sizeof(u16),
+ .word_count = CSR_REG_SIZE / sizeof(u16),
+ },
+ .eeprom = {
+ .read = rt2x00_eeprom_read,
+ .write = rt2x00_eeprom_write,
+ .word_base = EEPROM_BASE,
+ .word_size = sizeof(u16),
+ .word_count = EEPROM_SIZE / sizeof(u16),
+ },
+ .bbp = {
+ .read = rt2500usb_bbp_read,
+ .write = rt2500usb_bbp_write,
+ .word_base = BBP_BASE,
+ .word_size = sizeof(u8),
+ .word_count = BBP_SIZE / sizeof(u8),
+ },
+ .rf = {
+ .read = rt2x00_rf_read,
+ .write = rt2500usb_rf_write,
+ .word_base = RF_BASE,
+ .word_size = sizeof(u32),
+ .word_count = RF_SIZE / sizeof(u32),
+ },
+};
+#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
+
+static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev)
+{
+ u16 reg;
+
+ rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
+ return rt2x00_get_field16(reg, MAC_CSR19_VAL7);
+}
+
+#ifdef CONFIG_RT2X00_LIB_LEDS
+static void rt2500usb_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct rt2x00_led *led =
+ container_of(led_cdev, struct rt2x00_led, led_dev);
+ unsigned int enabled = brightness != LED_OFF;
+ u16 reg;
+
+ rt2500usb_register_read(led->rt2x00dev, MAC_CSR20, &reg);
+
+ if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC)
+ rt2x00_set_field16(&reg, MAC_CSR20_LINK, enabled);
+ else if (led->type == LED_TYPE_ACTIVITY)
+ rt2x00_set_field16(&reg, MAC_CSR20_ACTIVITY, enabled);
+
+ rt2500usb_register_write(led->rt2x00dev, MAC_CSR20, reg);
+}
+
+static int rt2500usb_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct rt2x00_led *led =
+ container_of(led_cdev, struct rt2x00_led, led_dev);
+ u16 reg;
+
+ rt2500usb_register_read(led->rt2x00dev, MAC_CSR21, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR21_ON_PERIOD, *delay_on);
+ rt2x00_set_field16(&reg, MAC_CSR21_OFF_PERIOD, *delay_off);
+ rt2500usb_register_write(led->rt2x00dev, MAC_CSR21, reg);
+
+ return 0;
+}
+
+static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00_led *led,
+ enum led_type type)
+{
+ led->rt2x00dev = rt2x00dev;
+ led->type = type;
+ led->led_dev.brightness_set = rt2500usb_brightness_set;
+ led->led_dev.blink_set = rt2500usb_blink_set;
+ led->flags = LED_INITIALIZED;
+}
+#endif /* CONFIG_RT2X00_LIB_LEDS */
+
+/*
+ * Configuration handlers.
+ */
+
+/*
+ * rt2500usb does not differentiate between shared and pairwise
+ * keys, so we should use the same function for both key types.
+ */
+static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key)
+{
+ u32 mask;
+ u16 reg;
+ enum cipher curr_cipher;
+
+ if (crypto->cmd == SET_KEY) {
+ /*
+ * Disallow to set WEP key other than with index 0,
+ * it is known that not work at least on some hardware.
+ * SW crypto will be used in that case.
+ */
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
+ key->keyidx != 0)
+ return -EOPNOTSUPP;
+
+ /*
+ * Pairwise key will always be entry 0, but this
+ * could collide with a shared key on the same
+ * position...
+ */
+ mask = TXRX_CSR0_KEY_ID.bit_mask;
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ curr_cipher = rt2x00_get_field16(reg, TXRX_CSR0_ALGORITHM);
+ reg &= mask;
+
+ if (reg && reg == mask)
+ return -ENOSPC;
+
+ reg = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID);
+
+ key->hw_key_idx += reg ? ffz(reg) : 0;
+ /*
+ * Hardware requires that all keys use the same cipher
+ * (e.g. TKIP-only, AES-only, but not TKIP+AES).
+ * If this is not the first key, compare the cipher with the
+ * first one and fall back to SW crypto if not the same.
+ */
+ if (key->hw_key_idx > 0 && crypto->cipher != curr_cipher)
+ return -EOPNOTSUPP;
+
+ rt2500usb_register_multiwrite(rt2x00dev, KEY_ENTRY(key->hw_key_idx),
+ crypto->key, sizeof(crypto->key));
+
+ /*
+ * The driver does not support the IV/EIV generation
+ * in hardware. However it demands the data to be provided
+ * both separately as well as inside the frame.
+ * We already provided the CONFIG_CRYPTO_COPY_IV to rt2x00lib
+ * to ensure rt2x00lib will not strip the data from the
+ * frame after the copy, now we must tell mac80211
+ * to generate the IV/EIV data.
+ */
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ }
+
+ /*
+ * TXRX_CSR0_KEY_ID contains only single-bit fields to indicate
+ * a particular key is valid.
+ */
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR0_ALGORITHM, crypto->cipher);
+ rt2x00_set_field16(&reg, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER);
+
+ mask = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID);
+ if (crypto->cmd == SET_KEY)
+ mask |= 1 << key->hw_key_idx;
+ else if (crypto->cmd == DISABLE_KEY)
+ mask &= ~(1 << key->hw_key_idx);
+ rt2x00_set_field16(&reg, TXRX_CSR0_KEY_ID, mask);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+
+ return 0;
+}
+
+static void rt2500usb_config_filter(struct rt2x00_dev *rt2x00dev,
+ const unsigned int filter_flags)
+{
+ u16 reg;
+
+ /*
+ * Start configuration steps.
+ * Note that the version error will always be dropped
+ * and broadcast frames will always be accepted since
+ * there is no filter for it at this time.
+ */
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_CRC,
+ !(filter_flags & FIF_FCSFAIL));
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_PHYSICAL,
+ !(filter_flags & FIF_PLCPFAIL));
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_CONTROL,
+ !(filter_flags & FIF_CONTROL));
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_NOT_TO_ME, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_TODS,
+ !rt2x00dev->intf_ap_count);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_VERSION_ERROR, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_MULTICAST,
+ !(filter_flags & FIF_ALLMULTI));
+ rt2x00_set_field16(&reg, TXRX_CSR2_DROP_BROADCAST, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
+}
+
+static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00_intf *intf,
+ struct rt2x00intf_conf *conf,
+ const unsigned int flags)
+{
+ unsigned int bcn_preload;
+ u16 reg;
+
+ if (flags & CONFIG_UPDATE_TYPE) {
+ /*
+ * Enable beacon config
+ */
+ bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20);
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR20, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR20_OFFSET, bcn_preload >> 6);
+ rt2x00_set_field16(&reg, TXRX_CSR20_BCN_EXPECT_WINDOW,
+ 2 * (conf->type != NL80211_IFTYPE_STATION));
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR20, reg);
+
+ /*
+ * Enable synchronisation.
+ */
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR18_OFFSET, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TSF_SYNC, conf->sync);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+ }
+
+ if (flags & CONFIG_UPDATE_MAC)
+ rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac,
+ (3 * sizeof(__le16)));
+
+ if (flags & CONFIG_UPDATE_BSSID)
+ rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR5, conf->bssid,
+ (3 * sizeof(__le16)));
+}
+
+static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_erp *erp,
+ u32 changed)
+{
+ u16 reg;
+
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR10, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR10_AUTORESPOND_PREAMBLE,
+ !!erp->short_preamble);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg);
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES)
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR11,
+ erp->basic_rates);
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL,
+ erp->beacon_int * 4);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
+ }
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
+ }
+}
+
+static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev,
+ struct antenna_setup *ant)
+{
+ u8 r2;
+ u8 r14;
+ u16 csr5;
+ u16 csr6;
+
+ /*
+ * We should never come here because rt2x00lib is supposed
+ * to catch this and send us the correct antenna explicitely.
+ */
+ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY ||
+ ant->tx == ANTENNA_SW_DIVERSITY);
+
+ rt2500usb_bbp_read(rt2x00dev, 2, &r2);
+ rt2500usb_bbp_read(rt2x00dev, 14, &r14);
+ rt2500usb_register_read(rt2x00dev, PHY_CSR5, &csr5);
+ rt2500usb_register_read(rt2x00dev, PHY_CSR6, &csr6);
+
+ /*
+ * Configure the TX antenna.
+ */
+ switch (ant->tx) {
+ case ANTENNA_HW_DIVERSITY:
+ rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 1);
+ rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 1);
+ rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 1);
+ break;
+ case ANTENNA_A:
+ rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0);
+ rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 0);
+ rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 0);
+ break;
+ case ANTENNA_B:
+ default:
+ rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2);
+ rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 2);
+ rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 2);
+ break;
+ }
+
+ /*
+ * Configure the RX antenna.
+ */
+ switch (ant->rx) {
+ case ANTENNA_HW_DIVERSITY:
+ rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 1);
+ break;
+ case ANTENNA_A:
+ rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0);
+ break;
+ case ANTENNA_B:
+ default:
+ rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2);
+ break;
+ }
+
+ /*
+ * RT2525E and RT5222 need to flip TX I/Q
+ */
+ if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) {
+ rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1);
+ rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1);
+ rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1);
+
+ /*
+ * RT2525E does not need RX I/Q Flip.
+ */
+ if (rt2x00_rf(rt2x00dev, RF2525E))
+ rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0);
+ } else {
+ rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0);
+ rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 0);
+ }
+
+ rt2500usb_bbp_write(rt2x00dev, 2, r2);
+ rt2500usb_bbp_write(rt2x00dev, 14, r14);
+ rt2500usb_register_write(rt2x00dev, PHY_CSR5, csr5);
+ rt2500usb_register_write(rt2x00dev, PHY_CSR6, csr6);
+}
+
+static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev,
+ struct rf_channel *rf, const int txpower)
+{
+ /*
+ * Set TXpower.
+ */
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
+
+ /*
+ * For RT2525E we should first set the channel to half band higher.
+ */
+ if (rt2x00_rf(rt2x00dev, RF2525E)) {
+ static const u32 vals[] = {
+ 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2,
+ 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba,
+ 0x000008ba, 0x000008be, 0x000008b7, 0x00000902,
+ 0x00000902, 0x00000906
+ };
+
+ rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]);
+ if (rf->rf4)
+ rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
+ }
+
+ rt2500usb_rf_write(rt2x00dev, 1, rf->rf1);
+ rt2500usb_rf_write(rt2x00dev, 2, rf->rf2);
+ rt2500usb_rf_write(rt2x00dev, 3, rf->rf3);
+ if (rf->rf4)
+ rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
+}
+
+static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev,
+ const int txpower)
+{
+ u32 rf3;
+
+ rt2x00_rf_read(rt2x00dev, 3, &rf3);
+ rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
+ rt2500usb_rf_write(rt2x00dev, 3, rf3);
+}
+
+static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
+ enum dev_state state =
+ (libconf->conf->flags & IEEE80211_CONF_PS) ?
+ STATE_SLEEP : STATE_AWAKE;
+ u16 reg;
+
+ if (state == STATE_SLEEP) {
+ rt2500usb_register_read(rt2x00dev, MAC_CSR18, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR18_DELAY_AFTER_BEACON,
+ rt2x00dev->beacon_int - 20);
+ rt2x00_set_field16(&reg, MAC_CSR18_BEACONS_BEFORE_WAKEUP,
+ libconf->conf->listen_interval - 1);
+
+ /* We must first disable autowake before it can be enabled */
+ rt2x00_set_field16(&reg, MAC_CSR18_AUTO_WAKE, 0);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
+
+ rt2x00_set_field16(&reg, MAC_CSR18_AUTO_WAKE, 1);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
+ } else {
+ rt2500usb_register_read(rt2x00dev, MAC_CSR18, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR18_AUTO_WAKE, 0);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
+ }
+
+ rt2x00dev->ops->lib->set_device_state(rt2x00dev, state);
+}
+
+static void rt2500usb_config(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf,
+ const unsigned int flags)
+{
+ if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
+ rt2500usb_config_channel(rt2x00dev, &libconf->rf,
+ libconf->conf->power_level);
+ if ((flags & IEEE80211_CONF_CHANGE_POWER) &&
+ !(flags & IEEE80211_CONF_CHANGE_CHANNEL))
+ rt2500usb_config_txpower(rt2x00dev,
+ libconf->conf->power_level);
+ if (flags & IEEE80211_CONF_CHANGE_PS)
+ rt2500usb_config_ps(rt2x00dev, libconf);
+}
+
+/*
+ * Link tuning
+ */
+static void rt2500usb_link_stats(struct rt2x00_dev *rt2x00dev,
+ struct link_qual *qual)
+{
+ u16 reg;
+
+ /*
+ * Update FCS error count from register.
+ */
+ rt2500usb_register_read(rt2x00dev, STA_CSR0, &reg);
+ qual->rx_failed = rt2x00_get_field16(reg, STA_CSR0_FCS_ERROR);
+
+ /*
+ * Update False CCA count from register.
+ */
+ rt2500usb_register_read(rt2x00dev, STA_CSR3, &reg);
+ qual->false_cca = rt2x00_get_field16(reg, STA_CSR3_FALSE_CCA_ERROR);
+}
+
+static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev,
+ struct link_qual *qual)
+{
+ u16 eeprom;
+ u16 value;
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &eeprom);
+ value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R24_LOW);
+ rt2500usb_bbp_write(rt2x00dev, 24, value);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &eeprom);
+ value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R25_LOW);
+ rt2500usb_bbp_write(rt2x00dev, 25, value);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &eeprom);
+ value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R61_LOW);
+ rt2500usb_bbp_write(rt2x00dev, 61, value);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &eeprom);
+ value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_VGCUPPER);
+ rt2500usb_bbp_write(rt2x00dev, 17, value);
+
+ qual->vgc_level = value;
+}
+
+/*
+ * Queue handlers.
+ */
+static void rt2500usb_start_queue(struct data_queue *queue)
+{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+ u16 reg;
+
+ switch (queue->qid) {
+ case QID_RX:
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DISABLE_RX, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
+ break;
+ case QID_BEACON:
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 1);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rt2500usb_stop_queue(struct data_queue *queue)
+{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+ u16 reg;
+
+ switch (queue->qid) {
+ case QID_RX:
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DISABLE_RX, 1);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
+ break;
+ case QID_BEACON:
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 0);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 0);
+ rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Initialization functions.
+ */
+static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev)
+{
+ u16 reg;
+
+ rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0x0001,
+ USB_MODE_TEST, REGISTER_TIMEOUT);
+ rt2x00usb_vendor_request_sw(rt2x00dev, USB_SINGLE_WRITE, 0x0308,
+ 0x00f0, REGISTER_TIMEOUT);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR2_DISABLE_RX, 1);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
+
+ rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x1111);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x1e11);
+
+ rt2500usb_register_read(rt2x00dev, MAC_CSR1, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR1_SOFT_RESET, 1);
+ rt2x00_set_field16(&reg, MAC_CSR1_BBP_RESET, 1);
+ rt2x00_set_field16(&reg, MAC_CSR1_HOST_READY, 0);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg);
+
+ rt2500usb_register_read(rt2x00dev, MAC_CSR1, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR1_SOFT_RESET, 0);
+ rt2x00_set_field16(&reg, MAC_CSR1_BBP_RESET, 0);
+ rt2x00_set_field16(&reg, MAC_CSR1_HOST_READY, 0);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR5, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID0, 13);
+ rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID0_VALID, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID1, 12);
+ rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID1_VALID, 1);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR5, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR6, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID0, 10);
+ rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID0_VALID, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID1, 11);
+ rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID1_VALID, 1);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR6, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR7, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID0, 7);
+ rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID0_VALID, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID1, 6);
+ rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID1_VALID, 1);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR7, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR8, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID0, 5);
+ rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID0_VALID, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID1, 0);
+ rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID1_VALID, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR8, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 0);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TSF_SYNC, 0);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 0);
+ rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR21, 0xe78f);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR9, 0xff1d);
+
+ if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE))
+ return -EBUSY;
+
+ rt2500usb_register_read(rt2x00dev, MAC_CSR1, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR1_SOFT_RESET, 0);
+ rt2x00_set_field16(&reg, MAC_CSR1_BBP_RESET, 0);
+ rt2x00_set_field16(&reg, MAC_CSR1_HOST_READY, 1);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg);
+
+ if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) {
+ rt2500usb_register_read(rt2x00dev, PHY_CSR2, &reg);
+ rt2x00_set_field16(&reg, PHY_CSR2_LNA, 0);
+ } else {
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR2_LNA, 1);
+ rt2x00_set_field16(&reg, PHY_CSR2_LNA_MODE, 3);
+ }
+ rt2500usb_register_write(rt2x00dev, PHY_CSR2, reg);
+
+ rt2500usb_register_write(rt2x00dev, MAC_CSR11, 0x0002);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR22, 0x0053);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR15, 0x01ee);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR16, 0x0000);
+
+ rt2500usb_register_read(rt2x00dev, MAC_CSR8, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR8_MAX_FRAME_UNIT,
+ rt2x00dev->rx->data_size);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR8, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR0_ALGORITHM, CIPHER_NONE);
+ rt2x00_set_field16(&reg, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER);
+ rt2x00_set_field16(&reg, TXRX_CSR0_KEY_ID, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+
+ rt2500usb_register_read(rt2x00dev, MAC_CSR18, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR18_DELAY_AFTER_BEACON, 90);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
+
+ rt2500usb_register_read(rt2x00dev, PHY_CSR4, &reg);
+ rt2x00_set_field16(&reg, PHY_CSR4_LOW_RF_LE, 1);
+ rt2500usb_register_write(rt2x00dev, PHY_CSR4, reg);
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR1, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR1_AUTO_SEQUENCE, 1);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR1, reg);
+
+ return 0;
+}
+
+static int rt2500usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u8 value;
+
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
+ rt2500usb_bbp_read(rt2x00dev, 0, &value);
+ if ((value != 0xff) && (value != 0x00))
+ return 0;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n");
+ return -EACCES;
+}
+
+static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u16 eeprom;
+ u8 value;
+ u8 reg_id;
+
+ if (unlikely(rt2500usb_wait_bbp_ready(rt2x00dev)))
+ return -EACCES;
+
+ rt2500usb_bbp_write(rt2x00dev, 3, 0x02);
+ rt2500usb_bbp_write(rt2x00dev, 4, 0x19);
+ rt2500usb_bbp_write(rt2x00dev, 14, 0x1c);
+ rt2500usb_bbp_write(rt2x00dev, 15, 0x30);
+ rt2500usb_bbp_write(rt2x00dev, 16, 0xac);
+ rt2500usb_bbp_write(rt2x00dev, 18, 0x18);
+ rt2500usb_bbp_write(rt2x00dev, 19, 0xff);
+ rt2500usb_bbp_write(rt2x00dev, 20, 0x1e);
+ rt2500usb_bbp_write(rt2x00dev, 21, 0x08);
+ rt2500usb_bbp_write(rt2x00dev, 22, 0x08);
+ rt2500usb_bbp_write(rt2x00dev, 23, 0x08);
+ rt2500usb_bbp_write(rt2x00dev, 24, 0x80);
+ rt2500usb_bbp_write(rt2x00dev, 25, 0x50);
+ rt2500usb_bbp_write(rt2x00dev, 26, 0x08);
+ rt2500usb_bbp_write(rt2x00dev, 27, 0x23);
+ rt2500usb_bbp_write(rt2x00dev, 30, 0x10);
+ rt2500usb_bbp_write(rt2x00dev, 31, 0x2b);
+ rt2500usb_bbp_write(rt2x00dev, 32, 0xb9);
+ rt2500usb_bbp_write(rt2x00dev, 34, 0x12);
+ rt2500usb_bbp_write(rt2x00dev, 35, 0x50);
+ rt2500usb_bbp_write(rt2x00dev, 39, 0xc4);
+ rt2500usb_bbp_write(rt2x00dev, 40, 0x02);
+ rt2500usb_bbp_write(rt2x00dev, 41, 0x60);
+ rt2500usb_bbp_write(rt2x00dev, 53, 0x10);
+ rt2500usb_bbp_write(rt2x00dev, 54, 0x18);
+ rt2500usb_bbp_write(rt2x00dev, 56, 0x08);
+ rt2500usb_bbp_write(rt2x00dev, 57, 0x10);
+ rt2500usb_bbp_write(rt2x00dev, 58, 0x08);
+ rt2500usb_bbp_write(rt2x00dev, 61, 0x60);
+ rt2500usb_bbp_write(rt2x00dev, 62, 0x10);
+ rt2500usb_bbp_write(rt2x00dev, 75, 0xff);
+
+ for (i = 0; i < EEPROM_BBP_SIZE; i++) {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
+
+ if (eeprom != 0xffff && eeprom != 0x0000) {
+ reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID);
+ value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE);
+ rt2500usb_bbp_write(rt2x00dev, reg_id, value);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Device state switch handlers.
+ */
+static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev)
+{
+ /*
+ * Initialize all registers.
+ */
+ if (unlikely(rt2500usb_init_registers(rt2x00dev) ||
+ rt2500usb_init_bbp(rt2x00dev)))
+ return -EIO;
+
+ return 0;
+}
+
+static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev)
+{
+ rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x2121);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x2121);
+
+ /*
+ * Disable synchronisation.
+ */
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0);
+
+ rt2x00usb_disable_radio(rt2x00dev);
+}
+
+static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev,
+ enum dev_state state)
+{
+ u16 reg;
+ u16 reg2;
+ unsigned int i;
+ char put_to_sleep;
+ char bbp_state;
+ char rf_state;
+
+ put_to_sleep = (state != STATE_AWAKE);
+
+ reg = 0;
+ rt2x00_set_field16(&reg, MAC_CSR17_BBP_DESIRE_STATE, state);
+ rt2x00_set_field16(&reg, MAC_CSR17_RF_DESIRE_STATE, state);
+ rt2x00_set_field16(&reg, MAC_CSR17_PUT_TO_SLEEP, put_to_sleep);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg);
+ rt2x00_set_field16(&reg, MAC_CSR17_SET_STATE, 1);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg);
+
+ /*
+ * Device is not guaranteed to be in the requested state yet.
+ * We must wait until the register indicates that the
+ * device has entered the correct state.
+ */
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
+ rt2500usb_register_read(rt2x00dev, MAC_CSR17, &reg2);
+ bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE);
+ rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE);
+ if (bbp_state == state && rf_state == state)
+ return 0;
+ rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg);
+ msleep(30);
+ }
+
+ return -EBUSY;
+}
+
+static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev,
+ enum dev_state state)
+{
+ int retval = 0;
+
+ switch (state) {
+ case STATE_RADIO_ON:
+ retval = rt2500usb_enable_radio(rt2x00dev);
+ break;
+ case STATE_RADIO_OFF:
+ rt2500usb_disable_radio(rt2x00dev);
+ break;
+ case STATE_RADIO_IRQ_ON:
+ case STATE_RADIO_IRQ_OFF:
+ /* No support, but no error either */
+ break;
+ case STATE_DEEP_SLEEP:
+ case STATE_SLEEP:
+ case STATE_STANDBY:
+ case STATE_AWAKE:
+ retval = rt2500usb_set_state(rt2x00dev, state);
+ break;
+ default:
+ retval = -ENOTSUPP;
+ break;
+ }
+
+ if (unlikely(retval))
+ rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n",
+ state, retval);
+
+ return retval;
+}
+
+/*
+ * TX descriptor initialization
+ */
+static void rt2500usb_write_tx_desc(struct queue_entry *entry,
+ struct txentry_desc *txdesc)
+{
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ __le32 *txd = (__le32 *) entry->skb->data;
+ u32 word;
+
+ /*
+ * Start writing the descriptor words.
+ */
+ rt2x00_desc_read(txd, 0, &word);
+ rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, txdesc->retry_limit);
+ rt2x00_set_field32(&word, TXD_W0_MORE_FRAG,
+ test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
+ rt2x00_set_field32(&word, TXD_W0_ACK,
+ test_bit(ENTRY_TXD_ACK, &txdesc->flags));
+ rt2x00_set_field32(&word, TXD_W0_TIMESTAMP,
+ test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
+ rt2x00_set_field32(&word, TXD_W0_OFDM,
+ (txdesc->rate_mode == RATE_MODE_OFDM));
+ rt2x00_set_field32(&word, TXD_W0_NEW_SEQ,
+ test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags));
+ rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs);
+ rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length);
+ rt2x00_set_field32(&word, TXD_W0_CIPHER, !!txdesc->cipher);
+ rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx);
+ rt2x00_desc_write(txd, 0, word);
+
+ rt2x00_desc_read(txd, 1, &word);
+ rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset);
+ rt2x00_set_field32(&word, TXD_W1_AIFS, entry->queue->aifs);
+ rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min);
+ rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max);
+ rt2x00_desc_write(txd, 1, word);
+
+ rt2x00_desc_read(txd, 2, &word);
+ rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal);
+ rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service);
+ rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW,
+ txdesc->u.plcp.length_low);
+ rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH,
+ txdesc->u.plcp.length_high);
+ rt2x00_desc_write(txd, 2, word);
+
+ if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) {
+ _rt2x00_desc_write(txd, 3, skbdesc->iv[0]);
+ _rt2x00_desc_write(txd, 4, skbdesc->iv[1]);
+ }
+
+ /*
+ * Register descriptor details in skb frame descriptor.
+ */
+ skbdesc->flags |= SKBDESC_DESC_IN_SKB;
+ skbdesc->desc = txd;
+ skbdesc->desc_len = TXD_DESC_SIZE;
+}
+
+/*
+ * TX data initialization
+ */
+static void rt2500usb_beacondone(struct urb *urb);
+
+static void rt2500usb_write_beacon(struct queue_entry *entry,
+ struct txentry_desc *txdesc)
+{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
+ struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
+ int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint);
+ int length;
+ u16 reg, reg0;
+
+ /*
+ * Disable beaconing while we are reloading the beacon data,
+ * otherwise we might be sending out invalid data.
+ */
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+
+ /*
+ * Add space for the descriptor in front of the skb.
+ */
+ skb_push(entry->skb, TXD_DESC_SIZE);
+ memset(entry->skb->data, 0, TXD_DESC_SIZE);
+
+ /*
+ * Write the TX descriptor for the beacon.
+ */
+ rt2500usb_write_tx_desc(entry, txdesc);
+
+ /*
+ * Dump beacon to userspace through debugfs.
+ */
+ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb);
+
+ /*
+ * USB devices cannot blindly pass the skb->len as the
+ * length of the data to usb_fill_bulk_urb. Pass the skb
+ * to the driver to determine what the length should be.
+ */
+ length = rt2x00dev->ops->lib->get_tx_data_len(entry);
+
+ usb_fill_bulk_urb(bcn_priv->urb, usb_dev, pipe,
+ entry->skb->data, length, rt2500usb_beacondone,
+ entry);
+
+ /*
+ * Second we need to create the guardian byte.
+ * We only need a single byte, so lets recycle
+ * the 'flags' field we are not using for beacons.
+ */
+ bcn_priv->guardian_data = 0;
+ usb_fill_bulk_urb(bcn_priv->guardian_urb, usb_dev, pipe,
+ &bcn_priv->guardian_data, 1, rt2500usb_beacondone,
+ entry);
+
+ /*
+ * Send out the guardian byte.
+ */
+ usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC);
+
+ /*
+ * Enable beaconing again.
+ */
+ rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 1);
+ rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 1);
+ reg0 = reg;
+ rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 1);
+ /*
+ * Beacon generation will fail initially.
+ * To prevent this we need to change the TXRX_CSR19
+ * register several times (reg0 is the same as reg
+ * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0
+ * and 1 in reg).
+ */
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+}
+
+static int rt2500usb_get_tx_data_len(struct queue_entry *entry)
+{
+ int length;
+
+ /*
+ * The length _must_ be a multiple of 2,
+ * but it must _not_ be a multiple of the USB packet size.
+ */
+ length = roundup(entry->skb->len, 2);
+ length += (2 * !(length % entry->queue->usb_maxpacket));
+
+ return length;
+}
+
+/*
+ * RX control handlers
+ */
+static void rt2500usb_fill_rxdone(struct queue_entry *entry,
+ struct rxdone_entry_desc *rxdesc)
+{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct queue_entry_priv_usb *entry_priv = entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ __le32 *rxd =
+ (__le32 *)(entry->skb->data +
+ (entry_priv->urb->actual_length -
+ entry->queue->desc_size));
+ u32 word0;
+ u32 word1;
+
+ /*
+ * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of
+ * frame data in rt2x00usb.
+ */
+ memcpy(skbdesc->desc, rxd, skbdesc->desc_len);
+ rxd = (__le32 *)skbdesc->desc;
+
+ /*
+ * It is now safe to read the descriptor on all architectures.
+ */
+ rt2x00_desc_read(rxd, 0, &word0);
+ rt2x00_desc_read(rxd, 1, &word1);
+
+ if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR))
+ rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
+ if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR))
+ rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC;
+
+ rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER);
+ if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR))
+ rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY;
+
+ if (rxdesc->cipher != CIPHER_NONE) {
+ _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]);
+ _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]);
+ rxdesc->dev_flags |= RXDONE_CRYPTO_IV;
+
+ /* ICV is located at the end of frame */
+
+ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
+ if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
+ rxdesc->flags |= RX_FLAG_DECRYPTED;
+ else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
+ rxdesc->flags |= RX_FLAG_MMIC_ERROR;
+ }
+
+ /*
+ * Obtain the status about this packet.
+ * When frame was received with an OFDM bitrate,
+ * the signal is the PLCP value. If it was received with
+ * a CCK bitrate the signal is the rate in 100kbit/s.
+ */
+ rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL);
+ rxdesc->rssi =
+ rt2x00_get_field32(word1, RXD_W1_RSSI) - rt2x00dev->rssi_offset;
+ rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT);
+
+ if (rt2x00_get_field32(word0, RXD_W0_OFDM))
+ rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP;
+ else
+ rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE;
+ if (rt2x00_get_field32(word0, RXD_W0_MY_BSS))
+ rxdesc->dev_flags |= RXDONE_MY_BSS;
+
+ /*
+ * Adjust the skb memory window to the frame boundaries.
+ */
+ skb_trim(entry->skb, rxdesc->size);
+}
+
+/*
+ * Interrupt functions.
+ */
+static void rt2500usb_beacondone(struct urb *urb)
+{
+ struct queue_entry *entry = (struct queue_entry *)urb->context;
+ struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
+
+ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &entry->queue->rt2x00dev->flags))
+ return;
+
+ /*
+ * Check if this was the guardian beacon,
+ * if that was the case we need to send the real beacon now.
+ * Otherwise we should free the sk_buffer, the device
+ * should be doing the rest of the work now.
+ */
+ if (bcn_priv->guardian_urb == urb) {
+ usb_submit_urb(bcn_priv->urb, GFP_ATOMIC);
+ } else if (bcn_priv->urb == urb) {
+ dev_kfree_skb(entry->skb);
+ entry->skb = NULL;
+ }
+}
+
+/*
+ * Device probe functions.
+ */
+static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
+{
+ u16 word;
+ u8 *mac;
+ u8 bbp;
+
+ rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE);
+
+ /*
+ * Start validation of the data that has been read.
+ */
+ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
+ if (!is_valid_ether_addr(mac)) {
+ eth_random_addr(mac);
+ rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT,
+ ANTENNA_SW_DIVERSITY);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT,
+ ANTENNA_SW_DIVERSITY);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE,
+ LED_MODE_DEFAULT);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI,
+ DEFAULT_RSSI_OFFSET);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n",
+ word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_THRESHOLD, 45);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "BBPtune: 0x%04x\n", word);
+ }
+
+ /*
+ * Switch lower vgc bound to current BBP R17 value,
+ * lower the value a bit for better quality.
+ */
+ rt2500usb_bbp_read(rt2x00dev, 17, &bbp);
+ bbp -= 6;
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCUPPER, 0x40);
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "BBPtune vgc: 0x%04x\n", word);
+ } else {
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_LOW, 0x48);
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_HIGH, 0x41);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R17, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r17: 0x%04x\n", word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_LOW, 0x40);
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_HIGH, 0x80);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R24, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r24: 0x%04x\n", word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_LOW, 0x40);
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_HIGH, 0x50);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R25, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r25: 0x%04x\n", word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_LOW, 0x60);
+ rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_HIGH, 0x6d);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R61, word);
+ rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r61: 0x%04x\n", word);
+ }
+
+ return 0;
+}
+
+static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
+{
+ u16 reg;
+ u16 value;
+ u16 eeprom;
+
+ /*
+ * Read EEPROM word for configuration.
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom);
+
+ /*
+ * Identify RF chipset.
+ */
+ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
+ rt2500usb_register_read(rt2x00dev, MAC_CSR0, &reg);
+ rt2x00_set_chip(rt2x00dev, RT2570, value, reg);
+
+ if (((reg & 0xfff0) != 0) || ((reg & 0x0000000f) == 0)) {
+ rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n");
+ return -ENODEV;
+ }
+
+ if (!rt2x00_rf(rt2x00dev, RF2522) &&
+ !rt2x00_rf(rt2x00dev, RF2523) &&
+ !rt2x00_rf(rt2x00dev, RF2524) &&
+ !rt2x00_rf(rt2x00dev, RF2525) &&
+ !rt2x00_rf(rt2x00dev, RF2525E) &&
+ !rt2x00_rf(rt2x00dev, RF5222)) {
+ rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Identify default antenna configuration.
+ */
+ rt2x00dev->default_ant.tx =
+ rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT);
+ rt2x00dev->default_ant.rx =
+ rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT);
+
+ /*
+ * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead.
+ * I am not 100% sure about this, but the legacy drivers do not
+ * indicate antenna swapping in software is required when
+ * diversity is enabled.
+ */
+ if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY)
+ rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY;
+ if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY)
+ rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY;
+
+ /*
+ * Store led mode, for correct led behaviour.
+ */
+#ifdef CONFIG_RT2X00_LIB_LEDS
+ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
+
+ rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
+ if (value == LED_MODE_TXRX_ACTIVITY ||
+ value == LED_MODE_DEFAULT ||
+ value == LED_MODE_ASUS)
+ rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual,
+ LED_TYPE_ACTIVITY);
+#endif /* CONFIG_RT2X00_LIB_LEDS */
+
+ /*
+ * Detect if this device has an hardware controlled radio.
+ */
+ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
+ __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags);
+
+ /*
+ * Read the RSSI <-> dBm offset information.
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom);
+ rt2x00dev->rssi_offset =
+ rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI);
+
+ return 0;
+}
+
+/*
+ * RF value list for RF2522
+ * Supports: 2.4 GHz
+ */
+static const struct rf_channel rf_vals_bg_2522[] = {
+ { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 },
+ { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 },
+ { 3, 0x00002050, 0x000c2002, 0x00000101, 0 },
+ { 4, 0x00002050, 0x000c2016, 0x00000101, 0 },
+ { 5, 0x00002050, 0x000c202a, 0x00000101, 0 },
+ { 6, 0x00002050, 0x000c203e, 0x00000101, 0 },
+ { 7, 0x00002050, 0x000c2052, 0x00000101, 0 },
+ { 8, 0x00002050, 0x000c2066, 0x00000101, 0 },
+ { 9, 0x00002050, 0x000c207a, 0x00000101, 0 },
+ { 10, 0x00002050, 0x000c208e, 0x00000101, 0 },
+ { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 },
+ { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 },
+ { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 },
+ { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 },
+};
+
+/*
+ * RF value list for RF2523
+ * Supports: 2.4 GHz
+ */
+static const struct rf_channel rf_vals_bg_2523[] = {
+ { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b },
+ { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b },
+ { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b },
+ { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b },
+ { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b },
+ { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b },
+ { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b },
+ { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b },
+ { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b },
+ { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b },
+ { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b },
+ { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b },
+ { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b },
+ { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 },
+};
+
+/*
+ * RF value list for RF2524
+ * Supports: 2.4 GHz
+ */
+static const struct rf_channel rf_vals_bg_2524[] = {
+ { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b },
+ { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b },
+ { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b },
+ { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b },
+ { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b },
+ { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b },
+ { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b },
+ { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b },
+ { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b },
+ { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b },
+ { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b },
+ { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b },
+ { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b },
+ { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 },
+};
+
+/*
+ * RF value list for RF2525
+ * Supports: 2.4 GHz
+ */
+static const struct rf_channel rf_vals_bg_2525[] = {
+ { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b },
+ { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b },
+ { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b },
+ { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b },
+ { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b },
+ { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b },
+ { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b },
+ { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b },
+ { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b },
+ { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b },
+ { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b },
+ { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b },
+ { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b },
+ { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 },
+};
+
+/*
+ * RF value list for RF2525e
+ * Supports: 2.4 GHz
+ */
+static const struct rf_channel rf_vals_bg_2525e[] = {
+ { 1, 0x00022010, 0x0000089a, 0x00060111, 0x00000e1b },
+ { 2, 0x00022010, 0x0000089e, 0x00060111, 0x00000e07 },
+ { 3, 0x00022010, 0x0000089e, 0x00060111, 0x00000e1b },
+ { 4, 0x00022010, 0x000008a2, 0x00060111, 0x00000e07 },
+ { 5, 0x00022010, 0x000008a2, 0x00060111, 0x00000e1b },
+ { 6, 0x00022010, 0x000008a6, 0x00060111, 0x00000e07 },
+ { 7, 0x00022010, 0x000008a6, 0x00060111, 0x00000e1b },
+ { 8, 0x00022010, 0x000008aa, 0x00060111, 0x00000e07 },
+ { 9, 0x00022010, 0x000008aa, 0x00060111, 0x00000e1b },
+ { 10, 0x00022010, 0x000008ae, 0x00060111, 0x00000e07 },
+ { 11, 0x00022010, 0x000008ae, 0x00060111, 0x00000e1b },
+ { 12, 0x00022010, 0x000008b2, 0x00060111, 0x00000e07 },
+ { 13, 0x00022010, 0x000008b2, 0x00060111, 0x00000e1b },
+ { 14, 0x00022010, 0x000008b6, 0x00060111, 0x00000e23 },
+};
+
+/*
+ * RF value list for RF5222
+ * Supports: 2.4 GHz & 5.2 GHz
+ */
+static const struct rf_channel rf_vals_5222[] = {
+ { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b },
+ { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b },
+ { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b },
+ { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b },
+ { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b },
+ { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b },
+ { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b },
+ { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b },
+ { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b },
+ { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b },
+ { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b },
+ { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b },
+ { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b },
+ { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b },
+
+ /* 802.11 UNI / HyperLan 2 */
+ { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f },
+ { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f },
+ { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f },
+ { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f },
+ { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f },
+ { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f },
+ { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f },
+ { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f },
+
+ /* 802.11 HyperLan 2 */
+ { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f },
+ { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f },
+ { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f },
+ { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f },
+ { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f },
+ { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f },
+ { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f },
+ { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f },
+ { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f },
+ { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f },
+
+ /* 802.11 UNII */
+ { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f },
+ { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 },
+ { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 },
+ { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 },
+ { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 },
+};
+
+static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
+{
+ struct hw_mode_spec *spec = &rt2x00dev->spec;
+ struct channel_info *info;
+ char *tx_power;
+ unsigned int i;
+
+ /*
+ * Initialize all hw fields.
+ *
+ * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are
+ * capable of sending the buffered frames out after the DTIM
+ * transmission using rt2x00lib_beacondone. This will send out
+ * multicast and broadcast traffic immediately instead of buffering it
+ * infinitly and thus dropping it after some time.
+ */
+ ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK);
+ ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS);
+ ieee80211_hw_set(rt2x00dev->hw, RX_INCLUDES_FCS);
+ ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM);
+
+ /*
+ * Disable powersaving as default.
+ */
+ rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
+ SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
+ rt2x00_eeprom_addr(rt2x00dev,
+ EEPROM_MAC_ADDR_0));
+
+ /*
+ * Initialize hw_mode information.
+ */
+ spec->supported_bands = SUPPORT_BAND_2GHZ;
+ spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
+
+ if (rt2x00_rf(rt2x00dev, RF2522)) {
+ spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522);
+ spec->channels = rf_vals_bg_2522;
+ } else if (rt2x00_rf(rt2x00dev, RF2523)) {
+ spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523);
+ spec->channels = rf_vals_bg_2523;
+ } else if (rt2x00_rf(rt2x00dev, RF2524)) {
+ spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524);
+ spec->channels = rf_vals_bg_2524;
+ } else if (rt2x00_rf(rt2x00dev, RF2525)) {
+ spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525);
+ spec->channels = rf_vals_bg_2525;
+ } else if (rt2x00_rf(rt2x00dev, RF2525E)) {
+ spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e);
+ spec->channels = rf_vals_bg_2525e;
+ } else if (rt2x00_rf(rt2x00dev, RF5222)) {
+ spec->supported_bands |= SUPPORT_BAND_5GHZ;
+ spec->num_channels = ARRAY_SIZE(rf_vals_5222);
+ spec->channels = rf_vals_5222;
+ }
+
+ /*
+ * Create channel information array
+ */
+ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ spec->channels_info = info;
+
+ tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
+ for (i = 0; i < 14; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
+
+ if (spec->num_channels > 14) {
+ for (i = 14; i < spec->num_channels; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = DEFAULT_TXPOWER;
+ }
+ }
+
+ return 0;
+}
+
+static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
+{
+ int retval;
+ u16 reg;
+
+ /*
+ * Allocate eeprom data.
+ */
+ retval = rt2500usb_validate_eeprom(rt2x00dev);
+ if (retval)
+ return retval;
+
+ retval = rt2500usb_init_eeprom(rt2x00dev);
+ if (retval)
+ return retval;
+
+ /*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR19_DIR0, 0);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg);
+
+ /*
+ * Initialize hw specifications.
+ */
+ retval = rt2500usb_probe_hw_mode(rt2x00dev);
+ if (retval)
+ return retval;
+
+ /*
+ * This device requires the atim queue
+ */
+ __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags);
+ __set_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags);
+ if (!modparam_nohwcrypt) {
+ __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags);
+ __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags);
+ }
+ __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags);
+ __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags);
+
+ /*
+ * Set the rssi offset.
+ */
+ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
+
+ return 0;
+}
+
+static const struct ieee80211_ops rt2500usb_mac80211_ops = {
+ .tx = rt2x00mac_tx,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+ .remove_interface = rt2x00mac_remove_interface,
+ .config = rt2x00mac_config,
+ .configure_filter = rt2x00mac_configure_filter,
+ .set_tim = rt2x00mac_set_tim,
+ .set_key = rt2x00mac_set_key,
+ .sw_scan_start = rt2x00mac_sw_scan_start,
+ .sw_scan_complete = rt2x00mac_sw_scan_complete,
+ .get_stats = rt2x00mac_get_stats,
+ .bss_info_changed = rt2x00mac_bss_info_changed,
+ .conf_tx = rt2x00mac_conf_tx,
+ .rfkill_poll = rt2x00mac_rfkill_poll,
+ .flush = rt2x00mac_flush,
+ .set_antenna = rt2x00mac_set_antenna,
+ .get_antenna = rt2x00mac_get_antenna,
+ .get_ringparam = rt2x00mac_get_ringparam,
+ .tx_frames_pending = rt2x00mac_tx_frames_pending,
+};
+
+static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
+ .probe_hw = rt2500usb_probe_hw,
+ .initialize = rt2x00usb_initialize,
+ .uninitialize = rt2x00usb_uninitialize,
+ .clear_entry = rt2x00usb_clear_entry,
+ .set_device_state = rt2500usb_set_device_state,
+ .rfkill_poll = rt2500usb_rfkill_poll,
+ .link_stats = rt2500usb_link_stats,
+ .reset_tuner = rt2500usb_reset_tuner,
+ .watchdog = rt2x00usb_watchdog,
+ .start_queue = rt2500usb_start_queue,
+ .kick_queue = rt2x00usb_kick_queue,
+ .stop_queue = rt2500usb_stop_queue,
+ .flush_queue = rt2x00usb_flush_queue,
+ .write_tx_desc = rt2500usb_write_tx_desc,
+ .write_beacon = rt2500usb_write_beacon,
+ .get_tx_data_len = rt2500usb_get_tx_data_len,
+ .fill_rxdone = rt2500usb_fill_rxdone,
+ .config_shared_key = rt2500usb_config_key,
+ .config_pairwise_key = rt2500usb_config_key,
+ .config_filter = rt2500usb_config_filter,
+ .config_intf = rt2500usb_config_intf,
+ .config_erp = rt2500usb_config_erp,
+ .config_ant = rt2500usb_config_ant,
+ .config = rt2500usb_config,
+};
+
+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;
+
+ 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;
+
+ 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;
+
+ 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,
+ .max_ap_intf = 1,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .queue_init = rt2500usb_queue_init,
+ .lib = &rt2500usb_rt2x00_ops,
+ .hw = &rt2500usb_mac80211_ops,
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
+ .debugfs = &rt2500usb_rt2x00debug,
+#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
+};
+
+/*
+ * rt2500usb module information.
+ */
+static struct usb_device_id rt2500usb_device_table[] = {
+ /* ASUS */
+ { USB_DEVICE(0x0b05, 0x1706) },
+ { USB_DEVICE(0x0b05, 0x1707) },
+ /* Belkin */
+ { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050A ver. 2.x */
+ { USB_DEVICE(0x050d, 0x7051) },
+ /* Cisco Systems */
+ { USB_DEVICE(0x13b1, 0x000d) },
+ { USB_DEVICE(0x13b1, 0x0011) },
+ { USB_DEVICE(0x13b1, 0x001a) },
+ /* Conceptronic */
+ { USB_DEVICE(0x14b2, 0x3c02) },
+ /* D-LINK */
+ { USB_DEVICE(0x2001, 0x3c00) },
+ /* Gigabyte */
+ { USB_DEVICE(0x1044, 0x8001) },
+ { USB_DEVICE(0x1044, 0x8007) },
+ /* Hercules */
+ { USB_DEVICE(0x06f8, 0xe000) },
+ /* Melco */
+ { USB_DEVICE(0x0411, 0x005e) },
+ { USB_DEVICE(0x0411, 0x0066) },
+ { USB_DEVICE(0x0411, 0x0067) },
+ { USB_DEVICE(0x0411, 0x008b) },
+ { USB_DEVICE(0x0411, 0x0097) },
+ /* MSI */
+ { USB_DEVICE(0x0db0, 0x6861) },
+ { USB_DEVICE(0x0db0, 0x6865) },
+ { USB_DEVICE(0x0db0, 0x6869) },
+ /* Ralink */
+ { USB_DEVICE(0x148f, 0x1706) },
+ { USB_DEVICE(0x148f, 0x2570) },
+ { USB_DEVICE(0x148f, 0x9020) },
+ /* Sagem */
+ { USB_DEVICE(0x079b, 0x004b) },
+ /* Siemens */
+ { USB_DEVICE(0x0681, 0x3c06) },
+ /* SMC */
+ { USB_DEVICE(0x0707, 0xee13) },
+ /* Spairon */
+ { USB_DEVICE(0x114b, 0x0110) },
+ /* SURECOM */
+ { USB_DEVICE(0x0769, 0x11f3) },
+ /* Trust */
+ { USB_DEVICE(0x0eb0, 0x9020) },
+ /* VTech */
+ { USB_DEVICE(0x0f88, 0x3012) },
+ /* Zinwell */
+ { USB_DEVICE(0x5a57, 0x0260) },
+ { 0, }
+};
+
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("Ralink RT2500 USB Wireless LAN driver.");
+MODULE_SUPPORTED_DEVICE("Ralink RT2570 USB chipset based cards");
+MODULE_DEVICE_TABLE(usb, rt2500usb_device_table);
+MODULE_LICENSE("GPL");
+
+static int rt2500usb_probe(struct usb_interface *usb_intf,
+ const struct usb_device_id *id)
+{
+ return rt2x00usb_probe(usb_intf, &rt2500usb_ops);
+}
+
+static struct usb_driver rt2500usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rt2500usb_device_table,
+ .probe = rt2500usb_probe,
+ .disconnect = rt2x00usb_disconnect,
+ .suspend = rt2x00usb_suspend,
+ .resume = rt2x00usb_resume,
+ .reset_resume = rt2x00usb_resume,
+ .disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(rt2500usb_driver);
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h
index 78cc035b2d17..78cc035b2d17 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
index 95c1d7c0a2f3..95c1d7c0a2f3 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 9733b31a780d..9733b31a780d 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
index 440790b92b19..440790b92b19 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
index de4790b41be7..de4790b41be7 100644
--- a/drivers/net/wireless/rt2x00/rt2800mmio.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h
index b63312ce3f27..b63312ce3f27 100644
--- a/drivers/net/wireless/rt2x00/rt2800mmio.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
index 0af22573a2eb..0af22573a2eb 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/ralink/rt2x00/rt2800pci.h
index 9dfef4607d6b..9dfef4607d6b 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.h
diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
index a985a5a7945e..a985a5a7945e 100644
--- a/drivers/net/wireless/rt2x00/rt2800soc.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index bf9afbf46c1b..bf9afbf46c1b 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
index ea7cac095997..ea7cac095997 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index 3282ddb766f4..3282ddb766f4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/ralink/rt2x00/rt2x00config.c
index 7e8bb1198ae9..7e8bb1198ae9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00config.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c
index a2fd05ba25ca..a2fd05ba25ca 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
index 90fdb02b55e7..90fdb02b55e7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.h
index e65712c235bd..e65712c235bd 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 5639ed816813..5639ed816813 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00dump.h b/drivers/net/wireless/ralink/rt2x00/rt2x00dump.h
index 4c0e01b5d515..4c0e01b5d515 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dump.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dump.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c
index 5813300f68a2..5813300f68a2 100644
--- a/drivers/net/wireless/rt2x00/rt2x00firmware.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
index c681d04b506c..c681d04b506c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.h b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.h
index b2c5269570da..b2c5269570da 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
index fb7c349ccc9c..fb7c349ccc9c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
index 017188e5a736..017188e5a736 100644
--- a/drivers/net/wireless/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
index 3c26ee65a415..3c26ee65a415 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
index f0178fd4fe5f..f0178fd4fe5f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mmio.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h
index 701c3127efb9..701c3127efb9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mmio.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c
new file mode 100644
index 000000000000..eb6dbcd4fddf
--- /dev/null
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c
@@ -0,0 +1,223 @@
+/*
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ Module: rt2x00pci
+ Abstract: rt2x00 generic pci device routines.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "rt2x00.h"
+#include "rt2x00pci.h"
+
+/*
+ * PCI driver handlers.
+ */
+static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev)
+{
+ kfree(rt2x00dev->rf);
+ rt2x00dev->rf = NULL;
+
+ kfree(rt2x00dev->eeprom);
+ rt2x00dev->eeprom = NULL;
+
+ if (rt2x00dev->csr.base) {
+ iounmap(rt2x00dev->csr.base);
+ rt2x00dev->csr.base = NULL;
+ }
+}
+
+static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev);
+
+ rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0);
+ if (!rt2x00dev->csr.base)
+ goto exit;
+
+ rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL);
+ if (!rt2x00dev->eeprom)
+ goto exit;
+
+ rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL);
+ if (!rt2x00dev->rf)
+ goto exit;
+
+ return 0;
+
+exit:
+ rt2x00_probe_err("Failed to allocate registers\n");
+
+ rt2x00pci_free_reg(rt2x00dev);
+
+ return -ENOMEM;
+}
+
+int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
+{
+ struct ieee80211_hw *hw;
+ struct rt2x00_dev *rt2x00dev;
+ int retval;
+ u16 chip;
+
+ retval = pci_enable_device(pci_dev);
+ if (retval) {
+ rt2x00_probe_err("Enable device failed\n");
+ return retval;
+ }
+
+ retval = pci_request_regions(pci_dev, pci_name(pci_dev));
+ if (retval) {
+ rt2x00_probe_err("PCI request regions failed\n");
+ goto exit_disable_device;
+ }
+
+ pci_set_master(pci_dev);
+
+ if (pci_set_mwi(pci_dev))
+ rt2x00_probe_err("MWI not available\n");
+
+ if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) {
+ rt2x00_probe_err("PCI DMA not supported\n");
+ retval = -EIO;
+ goto exit_release_regions;
+ }
+
+ hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
+ if (!hw) {
+ rt2x00_probe_err("Failed to allocate hardware\n");
+ retval = -ENOMEM;
+ goto exit_release_regions;
+ }
+
+ pci_set_drvdata(pci_dev, hw);
+
+ rt2x00dev = hw->priv;
+ rt2x00dev->dev = &pci_dev->dev;
+ rt2x00dev->ops = ops;
+ rt2x00dev->hw = hw;
+ rt2x00dev->irq = pci_dev->irq;
+ rt2x00dev->name = ops->name;
+
+ if (pci_is_pcie(pci_dev))
+ rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE);
+ else
+ rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI);
+
+ retval = rt2x00pci_alloc_reg(rt2x00dev);
+ if (retval)
+ goto exit_free_device;
+
+ /*
+ * Because rt3290 chip use different efuse offset to read efuse data.
+ * So before read efuse it need to indicate it is the
+ * rt3290 or not.
+ */
+ pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip);
+ rt2x00dev->chip.rt = chip;
+
+ retval = rt2x00lib_probe_dev(rt2x00dev);
+ if (retval)
+ goto exit_free_reg;
+
+ return 0;
+
+exit_free_reg:
+ rt2x00pci_free_reg(rt2x00dev);
+
+exit_free_device:
+ ieee80211_free_hw(hw);
+
+exit_release_regions:
+ pci_clear_mwi(pci_dev);
+ pci_release_regions(pci_dev);
+
+exit_disable_device:
+ pci_disable_device(pci_dev);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(rt2x00pci_probe);
+
+void rt2x00pci_remove(struct pci_dev *pci_dev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pci_dev);
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+
+ /*
+ * Free all allocated data.
+ */
+ rt2x00lib_remove_dev(rt2x00dev);
+ rt2x00pci_free_reg(rt2x00dev);
+ ieee80211_free_hw(hw);
+
+ /*
+ * Free the PCI device data.
+ */
+ pci_clear_mwi(pci_dev);
+ pci_disable_device(pci_dev);
+ pci_release_regions(pci_dev);
+}
+EXPORT_SYMBOL_GPL(rt2x00pci_remove);
+
+#ifdef CONFIG_PM
+int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pci_dev);
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ int retval;
+
+ retval = rt2x00lib_suspend(rt2x00dev, state);
+ if (retval)
+ return retval;
+
+ pci_save_state(pci_dev);
+ pci_disable_device(pci_dev);
+ return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
+}
+EXPORT_SYMBOL_GPL(rt2x00pci_suspend);
+
+int rt2x00pci_resume(struct pci_dev *pci_dev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pci_dev);
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+
+ if (pci_set_power_state(pci_dev, PCI_D0) ||
+ pci_enable_device(pci_dev)) {
+ rt2x00_err(rt2x00dev, "Failed to resume device\n");
+ return -EIO;
+ }
+
+ pci_restore_state(pci_dev);
+ return rt2x00lib_resume(rt2x00dev);
+}
+EXPORT_SYMBOL_GPL(rt2x00pci_resume);
+#endif /* CONFIG_PM */
+
+/*
+ * rt2x00pci module information.
+ */
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("rt2x00 pci library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.h
index bc0ca5f58f38..bc0ca5f58f38 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
index 68b620b2462f..68b620b2462f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h
index 2233b911a1d7..2233b911a1d7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/ralink/rt2x00/rt2x00reg.h
index 3cc541d13d67..3cc541d13d67 100644
--- a/drivers/net/wireless/rt2x00/rt2x00reg.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00reg.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
index 69a0cdadb07f..69a0cdadb07f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00soc.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
index 9948d355e9a4..9948d355e9a4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00soc.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
index 7627af6098eb..7627af6098eb 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.h
index 569363da00a2..569363da00a2 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.h
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
index c0e730ea1b69..c0e730ea1b69 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/ralink/rt2x00/rt61pci.h
index 1442075a8382..1442075a8382 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.h
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
index 7081e13b4fd6..7081e13b4fd6 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/ralink/rt2x00/rt73usb.h
index 4a4f235466d1..4a4f235466d1 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.h
diff --git a/drivers/net/wireless/realtek/Kconfig b/drivers/net/wireless/realtek/Kconfig
new file mode 100644
index 000000000000..8a8ba2003964
--- /dev/null
+++ b/drivers/net/wireless/realtek/Kconfig
@@ -0,0 +1,18 @@
+config WLAN_VENDOR_REALTEK
+ bool "Realtek devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_REALTEK
+
+source "drivers/net/wireless/realtek/rtl818x/Kconfig"
+source "drivers/net/wireless/realtek/rtlwifi/Kconfig"
+source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig"
+
+endif # WLAN_VENDOR_REALTEK
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c
index 53261d6f8578..451456835f87 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c
@@ -3356,9 +3356,8 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist)
"Dot11 channel / HsMode(HsChnl)",
wifi_dot11_chnl, bt_hson, wifi_hs_chnl);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ",
- "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info[0],
- coex_dm->wifi_chnl_info[1], coex_dm->wifi_chnl_info[2]);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ",
+ "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifirssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -3409,17 +3408,9 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8192E_2ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x ",
+ "\r\n %-35s = %7ph(%d)",
GLBtInfoSrc8192e2Ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3]);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "%02x %02x %02x(%d)",
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -3453,10 +3444,8 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist)
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
- "PS TDMA", coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4],
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
+ "PS TDMA", coex_dm->ps_tdma_para,
ps_tdma_case, coex_dm->auto_tdma_adjust);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ",
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
index c4acd403e5f6..7e239d3cea26 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
@@ -2457,10 +2457,9 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist)
"Dot11 channel / HsChnl(HsMode)",
wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ",
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ",
"H2C Wifi inform bt chnl Info",
- coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1],
- coex_dm->wifi_chnl_info[2]);
+ coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -2525,15 +2524,9 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8723B_1ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
GLBtInfoSrc8723b1Ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -2569,10 +2562,8 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist)
pstdmacase = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
- "PS TDMA", coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4],
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
+ "PS TDMA", coex_dm->ps_tdma_para,
pstdmacase, coex_dm->auto_tdma_adjust);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d ",
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
index f2b9d11adc9e..c43ab59a690a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
@@ -3215,9 +3215,8 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist)
"Dot11 channel / HsChnl(HsMode)",
wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ",
- "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info[0],
- coex_dm->wifi_chnl_info[1], coex_dm->wifi_chnl_info[2]);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ",
+ "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -3259,16 +3258,9 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8723B_2ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x "
- "%02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
glbt_info_src_8723b_2ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -3296,10 +3288,8 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist)
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
- "PS TDMA", coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4],
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
+ "PS TDMA", coex_dm->ps_tdma_para,
ps_tdma_case, coex_dm->auto_tdma_adjust);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ",
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
index b72e5377bdbc..9cecf174a37d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
@@ -2302,10 +2302,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist)
wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x ",
+ "\r\n %-35s = %3ph ",
"H2C Wifi inform bt chnl Info",
- coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1],
- coex_dm->wifi_chnl_info[2]);
+ coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -2366,15 +2365,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8821A_1ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
glbt_info_src_8821a_1ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -2412,13 +2405,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist)
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
"PS TDMA",
- coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1],
- coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3],
- coex_dm->ps_tdma_para[4],
+ coex_dm->ps_tdma_para,
ps_tdma_case,
coex_dm->auto_tdma_adjust);
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
index cf819f02ed23..044d914291c0 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
@@ -3393,10 +3393,9 @@ ex_halbtc8821a2ant_display_coex_info(
wifi_dot_11_chnl, bt_hs_on, wifi_hs_chnl);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x ",
+ "\r\n %-35s = %3ph ",
"H2C Wifi inform bt chnl Info",
- coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1],
- coex_dm->wifi_chnl_info[2]);
+ coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -3454,15 +3453,9 @@ ex_halbtc8821a2ant_display_coex_info(
for (i = 0; i < BT_INFO_SRC_8821A_2ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
glbt_info_src_8821a_2ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -3494,11 +3487,9 @@ ex_halbtc8821a2ant_display_coex_info(
if (!btcoexist->manual_control) {
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d",
+ "\r\n %-35s = %5ph case-%d",
"PS TDMA",
- coex_dm->ps_tdma_para[0], coex_dm->ps_tdma_para[1],
- coex_dm->ps_tdma_para[2], coex_dm->ps_tdma_para[3],
- coex_dm->ps_tdma_para[4], ps_tdma_case);
+ coex_dm->ps_tdma_para, ps_tdma_case);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
"\r\n %-35s = %d/ %d ", "DecBtPwr/ IgnWlanAct",
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index c925a4dff599..4ae421ef30d9 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -1833,8 +1833,7 @@ bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb)
spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
pskb = __skb_dequeue(&ring->queue);
- if (pskb)
- kfree_skb(pskb);
+ kfree_skb(pskb);
/*this is wrong, fill_tx_cmddesc needs update*/
pdesc = &ring->desc[0];
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index f46c9d7f6528..7f471bff435c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -801,7 +801,9 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
hw_queue);
if (rx_remained_cnt == 0)
return;
-
+ buffer_desc = &rtlpci->rx_ring[rxring_idx].buffer_desc[
+ rtlpci->rx_ring[rxring_idx].idx];
+ pdesc = (struct rtl_rx_desc *)skb->data;
} else { /* rx descriptor */
pdesc = &rtlpci->rx_ring[rxring_idx].desc[
rtlpci->rx_ring[rxring_idx].idx];
@@ -824,13 +826,6 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
if (unlikely(!new_skb))
goto no_new;
- if (rtlpriv->use_new_trx_flow) {
- buffer_desc =
- &rtlpci->rx_ring[rxring_idx].buffer_desc
- [rtlpci->rx_ring[rxring_idx].idx];
- /*means rx wifi info*/
- pdesc = (struct rtl_rx_desc *)skb->data;
- }
memset(&rx_status , 0 , sizeof(rx_status));
rtlpriv->cfg->ops->query_rx_desc(hw, &stats,
&rx_status, (u8 *)pdesc, skb);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
index 11344121c55e..47e32cb0ec1a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
@@ -88,8 +88,6 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
u8 tid;
rtl8188ee_bt_reg_init(hw);
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
-
rtlpriv->dm.dm_initialgain_enable = 1;
rtlpriv->dm.dm_flag = 0;
rtlpriv->dm.disable_framebursting = 0;
@@ -138,6 +136,11 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
if (!rtlpriv->psc.inactiveps)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c
index de6cb6c3a48c..4780bdc63b2b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c
@@ -139,6 +139,8 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
if (!rtlpriv->psc.inactiveps)
pr_info("rtl8192ce: Power Save off (module option)\n");
if (!rtlpriv->psc.fwctrl_lps)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c
index fd4a5353d216..7c6f7f0d18c6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c
@@ -65,6 +65,8 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->dm.disable_framebursting = false;
rtlpriv->dm.thermalvalue = 0;
rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
/* for firmware buf */
rtlpriv->rtlhal.pfirmware = vzalloc(0x4000);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
index b19d0398215f..c6e09a19de1a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
@@ -376,8 +376,8 @@ module_param_named(swlps, rtl92de_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl92de_mod_params.fwctrl_lps, bool, 0444);
MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
-MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
-MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 1)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 0)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c
index e1fd27c888bf..31baca41ac2f 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c
@@ -187,6 +187,8 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
if (!rtlpriv->psc.inactiveps)
pr_info("Power Save off (module option)\n");
if (!rtlpriv->psc.fwctrl_lps)
@@ -425,8 +427,8 @@ module_param_named(swlps, rtl92se_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl92se_mod_params.fwctrl_lps, bool, 0444);
MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
-MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
-MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 1)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 0)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c
index 3859b3e3d158..ff49a8c0ff61 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c
@@ -150,6 +150,11 @@ int rtl8723e_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
@@ -267,6 +272,8 @@ static struct rtl_mod_params rtl8723e_mod_params = {
.swctrl_lps = false,
.fwctrl_lps = true,
.debug = DBG_EMERG,
+ .msi_support = false,
+ .disable_watchdog = false,
};
static struct rtl_hal_cfg rtl8723e_hal_cfg = {
@@ -383,12 +390,14 @@ module_param_named(debug, rtl8723e_mod_params.debug, int, 0444);
module_param_named(ips, rtl8723e_mod_params.inactiveps, bool, 0444);
module_param_named(swlps, rtl8723e_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl8723e_mod_params.fwctrl_lps, bool, 0444);
+module_param_named(msi, rtl8723e_mod_params.msi_support, bool, 0444);
module_param_named(disable_watchdog, rtl8723e_mod_params.disable_watchdog,
bool, 0444);
MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n");
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
index d091f1d5f91e..a78eaeda0008 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
@@ -93,7 +93,6 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw)
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
rtl8723be_bt_reg_init(hw);
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
rtlpriv->dm.dm_initialgain_enable = 1;
@@ -151,6 +150,10 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
@@ -267,6 +270,9 @@ static struct rtl_mod_params rtl8723be_mod_params = {
.inactiveps = true,
.swctrl_lps = false,
.fwctrl_lps = true,
+ .msi_support = false,
+ .disable_watchdog = false,
+ .debug = DBG_EMERG,
};
static struct rtl_hal_cfg rtl8723be_hal_cfg = {
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c
index a2f5e89bedfe..6e518625edbe 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c
@@ -318,9 +318,7 @@ bool rtl8723_cmd_send_packet(struct ieee80211_hw *hw,
ring = &rtlpci->tx_ring[BEACON_QUEUE];
pskb = __skb_dequeue(&ring->queue);
- if (pskb)
- kfree_skb(pskb);
-
+ kfree_skb(pskb);
spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
pdesc = &ring->desc[0];
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
index 142bdff4ed60..4159f9b14db6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
@@ -95,8 +95,6 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw)
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
rtl8821ae_bt_reg_init(hw);
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
- rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear;
rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
rtlpriv->dm.dm_initialgain_enable = 1;
@@ -168,12 +166,15 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
- rtlpci->msi_support = rtlpriv->cfg->mod_params->int_clear;
+ rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
rtlpriv->psc.reg_max_lps_awakeintvl = 5;
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
/* for ASPM, you can close aspm through
* set const_support_pciaspm = 0
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index 2721cf89fb16..aac1ed3f7bb4 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -531,6 +531,8 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw,
ieee80211_rx(hw, skb);
else
dev_kfree_skb_any(skb);
+ } else {
+ dev_kfree_skb_any(skb);
}
}
diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig
index 35245f994c10..7c5e4ca4e3d0 100644
--- a/drivers/net/wireless/rsi/Kconfig
+++ b/drivers/net/wireless/rsi/Kconfig
@@ -1,3 +1,16 @@
+config WLAN_VENDOR_RSI
+ bool "Redpine Signals Inc devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_RSI
+
config RSI_91X
tristate "Redpine Signals Inc 91x WLAN driver support"
depends on MAC80211
@@ -28,3 +41,5 @@ config RSI_USB
---help---
This option enables the USB bus support in rsi drivers.
Select M (recommended), if you have a RSI 1x1 wireless module.
+
+endif # WLAN_VENDOR_RSI
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 8d110fd9eba1..e43b59d5b53b 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -1023,7 +1023,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
return -ENOMEM;
}
- selected_rates = kmalloc(2 * RSI_TBL_SZ, GFP_KERNEL);
+ selected_rates = kzalloc(2 * RSI_TBL_SZ, GFP_KERNEL);
if (!selected_rates) {
rsi_dbg(ERR_ZONE, "%s: Failed in allocation of mem\n",
__func__);
@@ -1032,7 +1032,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
}
memset(skb->data, 0, sizeof(struct rsi_auto_rate));
- memset(selected_rates, 0, 2 * RSI_TBL_SZ);
auto_rate = (struct rsi_auto_rate *)skb->data;
@@ -1227,7 +1226,7 @@ int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event)
mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE);
- if (block_event == true) {
+ if (block_event) {
rsi_dbg(INFO_ZONE, "blocking the data qs\n");
mgmt_frame->desc_word[4] = cpu_to_le16(0xf);
} else {
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
deleted file mode 100644
index b50d873145d5..000000000000
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ /dev/null
@@ -1,2001 +0,0 @@
-/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
- <http://rt2x00.serialmonkey.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- Module: rt2500usb
- Abstract: rt2500usb device specific routines.
- Supported chipsets: RT2570.
- */
-
-#include <linux/delay.h>
-#include <linux/etherdevice.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-
-#include "rt2x00.h"
-#include "rt2x00usb.h"
-#include "rt2500usb.h"
-
-/*
- * Allow hardware encryption to be disabled.
- */
-static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
-MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
-
-/*
- * Register access.
- * All access to the CSR registers will go through the methods
- * rt2500usb_register_read and rt2500usb_register_write.
- * BBP and RF register require indirect register access,
- * and use the CSR registers BBPCSR and RFCSR to achieve this.
- * These indirect registers work with busy bits,
- * and we will try maximal REGISTER_USB_BUSY_COUNT times to access
- * the register while taking a REGISTER_BUSY_DELAY us delay
- * between each attampt. When the busy bit is still set at that time,
- * the access attempt is considered to have failed,
- * and we will print an error.
- * If the csr_mutex is already held then the _lock variants must
- * be used instead.
- */
-static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- u16 *value)
-{
- __le16 reg;
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
- USB_VENDOR_REQUEST_IN, offset,
- &reg, sizeof(reg));
- *value = le16_to_cpu(reg);
-}
-
-static inline void rt2500usb_register_read_lock(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- u16 *value)
-{
- __le16 reg;
- rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ,
- USB_VENDOR_REQUEST_IN, offset,
- &reg, sizeof(reg), REGISTER_TIMEOUT);
- *value = le16_to_cpu(reg);
-}
-
-static inline void rt2500usb_register_multiread(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- void *value, const u16 length)
-{
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
- USB_VENDOR_REQUEST_IN, offset,
- value, length);
-}
-
-static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- u16 value)
-{
- __le16 reg = cpu_to_le16(value);
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT, offset,
- &reg, sizeof(reg));
-}
-
-static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- u16 value)
-{
- __le16 reg = cpu_to_le16(value);
- rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT, offset,
- &reg, sizeof(reg), REGISTER_TIMEOUT);
-}
-
-static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- void *value, const u16 length)
-{
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT, offset,
- value, length);
-}
-
-static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- struct rt2x00_field16 field,
- u16 *reg)
-{
- unsigned int i;
-
- for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
- rt2500usb_register_read_lock(rt2x00dev, offset, reg);
- if (!rt2x00_get_field16(*reg, field))
- return 1;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n",
- offset, *reg);
- *reg = ~0;
-
- return 0;
-}
-
-#define WAIT_FOR_BBP(__dev, __reg) \
- rt2500usb_regbusy_read((__dev), PHY_CSR8, PHY_CSR8_BUSY, (__reg))
-#define WAIT_FOR_RF(__dev, __reg) \
- rt2500usb_regbusy_read((__dev), PHY_CSR10, PHY_CSR10_RF_BUSY, (__reg))
-
-static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, const u8 value)
-{
- u16 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the BBP becomes available, afterwards we
- * can safely write the new data into the register.
- */
- if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR7_DATA, value);
- rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
- rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 0);
-
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
- }
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u8 *value)
-{
- u16 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the BBP becomes available, afterwards we
- * can safely write the read request into the register.
- * After the data has been written, we wait until hardware
- * returns the correct value, if at any time the register
- * doesn't become available in time, reg will be 0xffffffff
- * which means we return 0xff to the caller.
- */
- if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
- rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 1);
-
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
-
- if (WAIT_FOR_BBP(rt2x00dev, &reg))
- rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, &reg);
- }
-
- *value = rt2x00_get_field16(reg, PHY_CSR7_DATA);
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, const u32 value)
-{
- u16 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the RF becomes available, afterwards we
- * can safely write the new data into the register.
- */
- if (WAIT_FOR_RF(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR9_RF_VALUE, value);
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg);
-
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR10_RF_VALUE, value >> 16);
- rt2x00_set_field16(&reg, PHY_CSR10_RF_NUMBER_OF_BITS, 20);
- rt2x00_set_field16(&reg, PHY_CSR10_RF_IF_SELECT, 0);
- rt2x00_set_field16(&reg, PHY_CSR10_RF_BUSY, 1);
-
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg);
- rt2x00_rf_write(rt2x00dev, word, value);
- }
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- u32 *value)
-{
- rt2500usb_register_read(rt2x00dev, offset, (u16 *)value);
-}
-
-static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- u32 value)
-{
- rt2500usb_register_write(rt2x00dev, offset, value);
-}
-
-static const struct rt2x00debug rt2500usb_rt2x00debug = {
- .owner = THIS_MODULE,
- .csr = {
- .read = _rt2500usb_register_read,
- .write = _rt2500usb_register_write,
- .flags = RT2X00DEBUGFS_OFFSET,
- .word_base = CSR_REG_BASE,
- .word_size = sizeof(u16),
- .word_count = CSR_REG_SIZE / sizeof(u16),
- },
- .eeprom = {
- .read = rt2x00_eeprom_read,
- .write = rt2x00_eeprom_write,
- .word_base = EEPROM_BASE,
- .word_size = sizeof(u16),
- .word_count = EEPROM_SIZE / sizeof(u16),
- },
- .bbp = {
- .read = rt2500usb_bbp_read,
- .write = rt2500usb_bbp_write,
- .word_base = BBP_BASE,
- .word_size = sizeof(u8),
- .word_count = BBP_SIZE / sizeof(u8),
- },
- .rf = {
- .read = rt2x00_rf_read,
- .write = rt2500usb_rf_write,
- .word_base = RF_BASE,
- .word_size = sizeof(u32),
- .word_count = RF_SIZE / sizeof(u32),
- },
-};
-#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
-
-static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev)
-{
- u16 reg;
-
- rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
- return rt2x00_get_field16(reg, MAC_CSR19_VAL7);
-}
-
-#ifdef CONFIG_RT2X00_LIB_LEDS
-static void rt2500usb_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct rt2x00_led *led =
- container_of(led_cdev, struct rt2x00_led, led_dev);
- unsigned int enabled = brightness != LED_OFF;
- u16 reg;
-
- rt2500usb_register_read(led->rt2x00dev, MAC_CSR20, &reg);
-
- if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC)
- rt2x00_set_field16(&reg, MAC_CSR20_LINK, enabled);
- else if (led->type == LED_TYPE_ACTIVITY)
- rt2x00_set_field16(&reg, MAC_CSR20_ACTIVITY, enabled);
-
- rt2500usb_register_write(led->rt2x00dev, MAC_CSR20, reg);
-}
-
-static int rt2500usb_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on,
- unsigned long *delay_off)
-{
- struct rt2x00_led *led =
- container_of(led_cdev, struct rt2x00_led, led_dev);
- u16 reg;
-
- rt2500usb_register_read(led->rt2x00dev, MAC_CSR21, &reg);
- rt2x00_set_field16(&reg, MAC_CSR21_ON_PERIOD, *delay_on);
- rt2x00_set_field16(&reg, MAC_CSR21_OFF_PERIOD, *delay_off);
- rt2500usb_register_write(led->rt2x00dev, MAC_CSR21, reg);
-
- return 0;
-}
-
-static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev,
- struct rt2x00_led *led,
- enum led_type type)
-{
- led->rt2x00dev = rt2x00dev;
- led->type = type;
- led->led_dev.brightness_set = rt2500usb_brightness_set;
- led->led_dev.blink_set = rt2500usb_blink_set;
- led->flags = LED_INITIALIZED;
-}
-#endif /* CONFIG_RT2X00_LIB_LEDS */
-
-/*
- * Configuration handlers.
- */
-
-/*
- * rt2500usb does not differentiate between shared and pairwise
- * keys, so we should use the same function for both key types.
- */
-static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_crypto *crypto,
- struct ieee80211_key_conf *key)
-{
- u32 mask;
- u16 reg;
- enum cipher curr_cipher;
-
- if (crypto->cmd == SET_KEY) {
- /*
- * Disallow to set WEP key other than with index 0,
- * it is known that not work at least on some hardware.
- * SW crypto will be used in that case.
- */
- if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
- key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
- key->keyidx != 0)
- return -EOPNOTSUPP;
-
- /*
- * Pairwise key will always be entry 0, but this
- * could collide with a shared key on the same
- * position...
- */
- mask = TXRX_CSR0_KEY_ID.bit_mask;
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
- curr_cipher = rt2x00_get_field16(reg, TXRX_CSR0_ALGORITHM);
- reg &= mask;
-
- if (reg && reg == mask)
- return -ENOSPC;
-
- reg = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID);
-
- key->hw_key_idx += reg ? ffz(reg) : 0;
- /*
- * Hardware requires that all keys use the same cipher
- * (e.g. TKIP-only, AES-only, but not TKIP+AES).
- * If this is not the first key, compare the cipher with the
- * first one and fall back to SW crypto if not the same.
- */
- if (key->hw_key_idx > 0 && crypto->cipher != curr_cipher)
- return -EOPNOTSUPP;
-
- rt2500usb_register_multiwrite(rt2x00dev, KEY_ENTRY(key->hw_key_idx),
- crypto->key, sizeof(crypto->key));
-
- /*
- * The driver does not support the IV/EIV generation
- * in hardware. However it demands the data to be provided
- * both separately as well as inside the frame.
- * We already provided the CONFIG_CRYPTO_COPY_IV to rt2x00lib
- * to ensure rt2x00lib will not strip the data from the
- * frame after the copy, now we must tell mac80211
- * to generate the IV/EIV data.
- */
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
- }
-
- /*
- * TXRX_CSR0_KEY_ID contains only single-bit fields to indicate
- * a particular key is valid.
- */
- rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR0_ALGORITHM, crypto->cipher);
- rt2x00_set_field16(&reg, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER);
-
- mask = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID);
- if (crypto->cmd == SET_KEY)
- mask |= 1 << key->hw_key_idx;
- else if (crypto->cmd == DISABLE_KEY)
- mask &= ~(1 << key->hw_key_idx);
- rt2x00_set_field16(&reg, TXRX_CSR0_KEY_ID, mask);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg);
-
- return 0;
-}
-
-static void rt2500usb_config_filter(struct rt2x00_dev *rt2x00dev,
- const unsigned int filter_flags)
-{
- u16 reg;
-
- /*
- * Start configuration steps.
- * Note that the version error will always be dropped
- * and broadcast frames will always be accepted since
- * there is no filter for it at this time.
- */
- rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_CRC,
- !(filter_flags & FIF_FCSFAIL));
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_PHYSICAL,
- !(filter_flags & FIF_PLCPFAIL));
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_CONTROL,
- !(filter_flags & FIF_CONTROL));
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_NOT_TO_ME, 1);
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_TODS,
- !rt2x00dev->intf_ap_count);
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_VERSION_ERROR, 1);
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_MULTICAST,
- !(filter_flags & FIF_ALLMULTI));
- rt2x00_set_field16(&reg, TXRX_CSR2_DROP_BROADCAST, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
-}
-
-static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev,
- struct rt2x00_intf *intf,
- struct rt2x00intf_conf *conf,
- const unsigned int flags)
-{
- unsigned int bcn_preload;
- u16 reg;
-
- if (flags & CONFIG_UPDATE_TYPE) {
- /*
- * Enable beacon config
- */
- bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20);
- rt2500usb_register_read(rt2x00dev, TXRX_CSR20, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR20_OFFSET, bcn_preload >> 6);
- rt2x00_set_field16(&reg, TXRX_CSR20_BCN_EXPECT_WINDOW,
- 2 * (conf->type != NL80211_IFTYPE_STATION));
- rt2500usb_register_write(rt2x00dev, TXRX_CSR20, reg);
-
- /*
- * Enable synchronisation.
- */
- rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR18_OFFSET, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR19_TSF_SYNC, conf->sync);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
- }
-
- if (flags & CONFIG_UPDATE_MAC)
- rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac,
- (3 * sizeof(__le16)));
-
- if (flags & CONFIG_UPDATE_BSSID)
- rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR5, conf->bssid,
- (3 * sizeof(__le16)));
-}
-
-static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_erp *erp,
- u32 changed)
-{
- u16 reg;
-
- if (changed & BSS_CHANGED_ERP_PREAMBLE) {
- rt2500usb_register_read(rt2x00dev, TXRX_CSR10, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR10_AUTORESPOND_PREAMBLE,
- !!erp->short_preamble);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg);
- }
-
- if (changed & BSS_CHANGED_BASIC_RATES)
- rt2500usb_register_write(rt2x00dev, TXRX_CSR11,
- erp->basic_rates);
-
- if (changed & BSS_CHANGED_BEACON_INT) {
- rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL,
- erp->beacon_int * 4);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
- }
-
- if (changed & BSS_CHANGED_ERP_SLOT) {
- rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
- rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
- rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
- }
-}
-
-static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev,
- struct antenna_setup *ant)
-{
- u8 r2;
- u8 r14;
- u16 csr5;
- u16 csr6;
-
- /*
- * We should never come here because rt2x00lib is supposed
- * to catch this and send us the correct antenna explicitely.
- */
- BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY ||
- ant->tx == ANTENNA_SW_DIVERSITY);
-
- rt2500usb_bbp_read(rt2x00dev, 2, &r2);
- rt2500usb_bbp_read(rt2x00dev, 14, &r14);
- rt2500usb_register_read(rt2x00dev, PHY_CSR5, &csr5);
- rt2500usb_register_read(rt2x00dev, PHY_CSR6, &csr6);
-
- /*
- * Configure the TX antenna.
- */
- switch (ant->tx) {
- case ANTENNA_HW_DIVERSITY:
- rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 1);
- rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 1);
- rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 1);
- break;
- case ANTENNA_A:
- rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0);
- rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 0);
- rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 0);
- break;
- case ANTENNA_B:
- default:
- rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2);
- rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 2);
- rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 2);
- break;
- }
-
- /*
- * Configure the RX antenna.
- */
- switch (ant->rx) {
- case ANTENNA_HW_DIVERSITY:
- rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 1);
- break;
- case ANTENNA_A:
- rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0);
- break;
- case ANTENNA_B:
- default:
- rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2);
- break;
- }
-
- /*
- * RT2525E and RT5222 need to flip TX I/Q
- */
- if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) {
- rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1);
- rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1);
- rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1);
-
- /*
- * RT2525E does not need RX I/Q Flip.
- */
- if (rt2x00_rf(rt2x00dev, RF2525E))
- rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0);
- } else {
- rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0);
- rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 0);
- }
-
- rt2500usb_bbp_write(rt2x00dev, 2, r2);
- rt2500usb_bbp_write(rt2x00dev, 14, r14);
- rt2500usb_register_write(rt2x00dev, PHY_CSR5, csr5);
- rt2500usb_register_write(rt2x00dev, PHY_CSR6, csr6);
-}
-
-static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev,
- struct rf_channel *rf, const int txpower)
-{
- /*
- * Set TXpower.
- */
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
-
- /*
- * For RT2525E we should first set the channel to half band higher.
- */
- if (rt2x00_rf(rt2x00dev, RF2525E)) {
- static const u32 vals[] = {
- 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2,
- 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba,
- 0x000008ba, 0x000008be, 0x000008b7, 0x00000902,
- 0x00000902, 0x00000906
- };
-
- rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]);
- if (rf->rf4)
- rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
- }
-
- rt2500usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt2500usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt2500usb_rf_write(rt2x00dev, 3, rf->rf3);
- if (rf->rf4)
- rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
-}
-
-static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev,
- const int txpower)
-{
- u32 rf3;
-
- rt2x00_rf_read(rt2x00dev, 3, &rf3);
- rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
- rt2500usb_rf_write(rt2x00dev, 3, rf3);
-}
-
-static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf)
-{
- enum dev_state state =
- (libconf->conf->flags & IEEE80211_CONF_PS) ?
- STATE_SLEEP : STATE_AWAKE;
- u16 reg;
-
- if (state == STATE_SLEEP) {
- rt2500usb_register_read(rt2x00dev, MAC_CSR18, &reg);
- rt2x00_set_field16(&reg, MAC_CSR18_DELAY_AFTER_BEACON,
- rt2x00dev->beacon_int - 20);
- rt2x00_set_field16(&reg, MAC_CSR18_BEACONS_BEFORE_WAKEUP,
- libconf->conf->listen_interval - 1);
-
- /* We must first disable autowake before it can be enabled */
- rt2x00_set_field16(&reg, MAC_CSR18_AUTO_WAKE, 0);
- rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
-
- rt2x00_set_field16(&reg, MAC_CSR18_AUTO_WAKE, 1);
- rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
- } else {
- rt2500usb_register_read(rt2x00dev, MAC_CSR18, &reg);
- rt2x00_set_field16(&reg, MAC_CSR18_AUTO_WAKE, 0);
- rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
- }
-
- rt2x00dev->ops->lib->set_device_state(rt2x00dev, state);
-}
-
-static void rt2500usb_config(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf,
- const unsigned int flags)
-{
- if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
- rt2500usb_config_channel(rt2x00dev, &libconf->rf,
- libconf->conf->power_level);
- if ((flags & IEEE80211_CONF_CHANGE_POWER) &&
- !(flags & IEEE80211_CONF_CHANGE_CHANNEL))
- rt2500usb_config_txpower(rt2x00dev,
- libconf->conf->power_level);
- if (flags & IEEE80211_CONF_CHANGE_PS)
- rt2500usb_config_ps(rt2x00dev, libconf);
-}
-
-/*
- * Link tuning
- */
-static void rt2500usb_link_stats(struct rt2x00_dev *rt2x00dev,
- struct link_qual *qual)
-{
- u16 reg;
-
- /*
- * Update FCS error count from register.
- */
- rt2500usb_register_read(rt2x00dev, STA_CSR0, &reg);
- qual->rx_failed = rt2x00_get_field16(reg, STA_CSR0_FCS_ERROR);
-
- /*
- * Update False CCA count from register.
- */
- rt2500usb_register_read(rt2x00dev, STA_CSR3, &reg);
- qual->false_cca = rt2x00_get_field16(reg, STA_CSR3_FALSE_CCA_ERROR);
-}
-
-static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev,
- struct link_qual *qual)
-{
- u16 eeprom;
- u16 value;
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &eeprom);
- value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R24_LOW);
- rt2500usb_bbp_write(rt2x00dev, 24, value);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &eeprom);
- value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R25_LOW);
- rt2500usb_bbp_write(rt2x00dev, 25, value);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &eeprom);
- value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R61_LOW);
- rt2500usb_bbp_write(rt2x00dev, 61, value);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &eeprom);
- value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_VGCUPPER);
- rt2500usb_bbp_write(rt2x00dev, 17, value);
-
- qual->vgc_level = value;
-}
-
-/*
- * Queue handlers.
- */
-static void rt2500usb_start_queue(struct data_queue *queue)
-{
- struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
- u16 reg;
-
- switch (queue->qid) {
- case QID_RX:
- rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR2_DISABLE_RX, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
- break;
- case QID_BEACON:
- rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 1);
- rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 1);
- rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 1);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
- break;
- default:
- break;
- }
-}
-
-static void rt2500usb_stop_queue(struct data_queue *queue)
-{
- struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
- u16 reg;
-
- switch (queue->qid) {
- case QID_RX:
- rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR2_DISABLE_RX, 1);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
- break;
- case QID_BEACON:
- rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 0);
- rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 0);
- rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
- break;
- default:
- break;
- }
-}
-
-/*
- * Initialization functions.
- */
-static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev)
-{
- u16 reg;
-
- rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0x0001,
- USB_MODE_TEST, REGISTER_TIMEOUT);
- rt2x00usb_vendor_request_sw(rt2x00dev, USB_SINGLE_WRITE, 0x0308,
- 0x00f0, REGISTER_TIMEOUT);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR2_DISABLE_RX, 1);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg);
-
- rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x1111);
- rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x1e11);
-
- rt2500usb_register_read(rt2x00dev, MAC_CSR1, &reg);
- rt2x00_set_field16(&reg, MAC_CSR1_SOFT_RESET, 1);
- rt2x00_set_field16(&reg, MAC_CSR1_BBP_RESET, 1);
- rt2x00_set_field16(&reg, MAC_CSR1_HOST_READY, 0);
- rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg);
-
- rt2500usb_register_read(rt2x00dev, MAC_CSR1, &reg);
- rt2x00_set_field16(&reg, MAC_CSR1_SOFT_RESET, 0);
- rt2x00_set_field16(&reg, MAC_CSR1_BBP_RESET, 0);
- rt2x00_set_field16(&reg, MAC_CSR1_HOST_READY, 0);
- rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR5, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID0, 13);
- rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID0_VALID, 1);
- rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID1, 12);
- rt2x00_set_field16(&reg, TXRX_CSR5_BBP_ID1_VALID, 1);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR5, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR6, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID0, 10);
- rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID0_VALID, 1);
- rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID1, 11);
- rt2x00_set_field16(&reg, TXRX_CSR6_BBP_ID1_VALID, 1);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR6, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR7, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID0, 7);
- rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID0_VALID, 1);
- rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID1, 6);
- rt2x00_set_field16(&reg, TXRX_CSR7_BBP_ID1_VALID, 1);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR7, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR8, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID0, 5);
- rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID0_VALID, 1);
- rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID1, 0);
- rt2x00_set_field16(&reg, TXRX_CSR8_BBP_ID1_VALID, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR8, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 0);
- rt2x00_set_field16(&reg, TXRX_CSR19_TSF_SYNC, 0);
- rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 0);
- rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
-
- rt2500usb_register_write(rt2x00dev, TXRX_CSR21, 0xe78f);
- rt2500usb_register_write(rt2x00dev, MAC_CSR9, 0xff1d);
-
- if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE))
- return -EBUSY;
-
- rt2500usb_register_read(rt2x00dev, MAC_CSR1, &reg);
- rt2x00_set_field16(&reg, MAC_CSR1_SOFT_RESET, 0);
- rt2x00_set_field16(&reg, MAC_CSR1_BBP_RESET, 0);
- rt2x00_set_field16(&reg, MAC_CSR1_HOST_READY, 1);
- rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg);
-
- if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) {
- rt2500usb_register_read(rt2x00dev, PHY_CSR2, &reg);
- rt2x00_set_field16(&reg, PHY_CSR2_LNA, 0);
- } else {
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR2_LNA, 1);
- rt2x00_set_field16(&reg, PHY_CSR2_LNA_MODE, 3);
- }
- rt2500usb_register_write(rt2x00dev, PHY_CSR2, reg);
-
- rt2500usb_register_write(rt2x00dev, MAC_CSR11, 0x0002);
- rt2500usb_register_write(rt2x00dev, MAC_CSR22, 0x0053);
- rt2500usb_register_write(rt2x00dev, MAC_CSR15, 0x01ee);
- rt2500usb_register_write(rt2x00dev, MAC_CSR16, 0x0000);
-
- rt2500usb_register_read(rt2x00dev, MAC_CSR8, &reg);
- rt2x00_set_field16(&reg, MAC_CSR8_MAX_FRAME_UNIT,
- rt2x00dev->rx->data_size);
- rt2500usb_register_write(rt2x00dev, MAC_CSR8, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR0_ALGORITHM, CIPHER_NONE);
- rt2x00_set_field16(&reg, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER);
- rt2x00_set_field16(&reg, TXRX_CSR0_KEY_ID, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg);
-
- rt2500usb_register_read(rt2x00dev, MAC_CSR18, &reg);
- rt2x00_set_field16(&reg, MAC_CSR18_DELAY_AFTER_BEACON, 90);
- rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg);
-
- rt2500usb_register_read(rt2x00dev, PHY_CSR4, &reg);
- rt2x00_set_field16(&reg, PHY_CSR4_LOW_RF_LE, 1);
- rt2500usb_register_write(rt2x00dev, PHY_CSR4, reg);
-
- rt2500usb_register_read(rt2x00dev, TXRX_CSR1, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR1_AUTO_SEQUENCE, 1);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR1, reg);
-
- return 0;
-}
-
-static int rt2500usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
-{
- unsigned int i;
- u8 value;
-
- for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
- rt2500usb_bbp_read(rt2x00dev, 0, &value);
- if ((value != 0xff) && (value != 0x00))
- return 0;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n");
- return -EACCES;
-}
-
-static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev)
-{
- unsigned int i;
- u16 eeprom;
- u8 value;
- u8 reg_id;
-
- if (unlikely(rt2500usb_wait_bbp_ready(rt2x00dev)))
- return -EACCES;
-
- rt2500usb_bbp_write(rt2x00dev, 3, 0x02);
- rt2500usb_bbp_write(rt2x00dev, 4, 0x19);
- rt2500usb_bbp_write(rt2x00dev, 14, 0x1c);
- rt2500usb_bbp_write(rt2x00dev, 15, 0x30);
- rt2500usb_bbp_write(rt2x00dev, 16, 0xac);
- rt2500usb_bbp_write(rt2x00dev, 18, 0x18);
- rt2500usb_bbp_write(rt2x00dev, 19, 0xff);
- rt2500usb_bbp_write(rt2x00dev, 20, 0x1e);
- rt2500usb_bbp_write(rt2x00dev, 21, 0x08);
- rt2500usb_bbp_write(rt2x00dev, 22, 0x08);
- rt2500usb_bbp_write(rt2x00dev, 23, 0x08);
- rt2500usb_bbp_write(rt2x00dev, 24, 0x80);
- rt2500usb_bbp_write(rt2x00dev, 25, 0x50);
- rt2500usb_bbp_write(rt2x00dev, 26, 0x08);
- rt2500usb_bbp_write(rt2x00dev, 27, 0x23);
- rt2500usb_bbp_write(rt2x00dev, 30, 0x10);
- rt2500usb_bbp_write(rt2x00dev, 31, 0x2b);
- rt2500usb_bbp_write(rt2x00dev, 32, 0xb9);
- rt2500usb_bbp_write(rt2x00dev, 34, 0x12);
- rt2500usb_bbp_write(rt2x00dev, 35, 0x50);
- rt2500usb_bbp_write(rt2x00dev, 39, 0xc4);
- rt2500usb_bbp_write(rt2x00dev, 40, 0x02);
- rt2500usb_bbp_write(rt2x00dev, 41, 0x60);
- rt2500usb_bbp_write(rt2x00dev, 53, 0x10);
- rt2500usb_bbp_write(rt2x00dev, 54, 0x18);
- rt2500usb_bbp_write(rt2x00dev, 56, 0x08);
- rt2500usb_bbp_write(rt2x00dev, 57, 0x10);
- rt2500usb_bbp_write(rt2x00dev, 58, 0x08);
- rt2500usb_bbp_write(rt2x00dev, 61, 0x60);
- rt2500usb_bbp_write(rt2x00dev, 62, 0x10);
- rt2500usb_bbp_write(rt2x00dev, 75, 0xff);
-
- for (i = 0; i < EEPROM_BBP_SIZE; i++) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
-
- if (eeprom != 0xffff && eeprom != 0x0000) {
- reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID);
- value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE);
- rt2500usb_bbp_write(rt2x00dev, reg_id, value);
- }
- }
-
- return 0;
-}
-
-/*
- * Device state switch handlers.
- */
-static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev)
-{
- /*
- * Initialize all registers.
- */
- if (unlikely(rt2500usb_init_registers(rt2x00dev) ||
- rt2500usb_init_bbp(rt2x00dev)))
- return -EIO;
-
- return 0;
-}
-
-static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev)
-{
- rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x2121);
- rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x2121);
-
- /*
- * Disable synchronisation.
- */
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0);
-
- rt2x00usb_disable_radio(rt2x00dev);
-}
-
-static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev,
- enum dev_state state)
-{
- u16 reg;
- u16 reg2;
- unsigned int i;
- char put_to_sleep;
- char bbp_state;
- char rf_state;
-
- put_to_sleep = (state != STATE_AWAKE);
-
- reg = 0;
- rt2x00_set_field16(&reg, MAC_CSR17_BBP_DESIRE_STATE, state);
- rt2x00_set_field16(&reg, MAC_CSR17_RF_DESIRE_STATE, state);
- rt2x00_set_field16(&reg, MAC_CSR17_PUT_TO_SLEEP, put_to_sleep);
- rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg);
- rt2x00_set_field16(&reg, MAC_CSR17_SET_STATE, 1);
- rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg);
-
- /*
- * Device is not guaranteed to be in the requested state yet.
- * We must wait until the register indicates that the
- * device has entered the correct state.
- */
- for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
- rt2500usb_register_read(rt2x00dev, MAC_CSR17, &reg2);
- bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE);
- rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE);
- if (bbp_state == state && rf_state == state)
- return 0;
- rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg);
- msleep(30);
- }
-
- return -EBUSY;
-}
-
-static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev,
- enum dev_state state)
-{
- int retval = 0;
-
- switch (state) {
- case STATE_RADIO_ON:
- retval = rt2500usb_enable_radio(rt2x00dev);
- break;
- case STATE_RADIO_OFF:
- rt2500usb_disable_radio(rt2x00dev);
- break;
- case STATE_RADIO_IRQ_ON:
- case STATE_RADIO_IRQ_OFF:
- /* No support, but no error either */
- break;
- case STATE_DEEP_SLEEP:
- case STATE_SLEEP:
- case STATE_STANDBY:
- case STATE_AWAKE:
- retval = rt2500usb_set_state(rt2x00dev, state);
- break;
- default:
- retval = -ENOTSUPP;
- break;
- }
-
- if (unlikely(retval))
- rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n",
- state, retval);
-
- return retval;
-}
-
-/*
- * TX descriptor initialization
- */
-static void rt2500usb_write_tx_desc(struct queue_entry *entry,
- struct txentry_desc *txdesc)
-{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
- __le32 *txd = (__le32 *) entry->skb->data;
- u32 word;
-
- /*
- * Start writing the descriptor words.
- */
- rt2x00_desc_read(txd, 0, &word);
- rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, txdesc->retry_limit);
- rt2x00_set_field32(&word, TXD_W0_MORE_FRAG,
- test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
- rt2x00_set_field32(&word, TXD_W0_ACK,
- test_bit(ENTRY_TXD_ACK, &txdesc->flags));
- rt2x00_set_field32(&word, TXD_W0_TIMESTAMP,
- test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
- rt2x00_set_field32(&word, TXD_W0_OFDM,
- (txdesc->rate_mode == RATE_MODE_OFDM));
- rt2x00_set_field32(&word, TXD_W0_NEW_SEQ,
- test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags));
- rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs);
- rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length);
- rt2x00_set_field32(&word, TXD_W0_CIPHER, !!txdesc->cipher);
- rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx);
- rt2x00_desc_write(txd, 0, word);
-
- rt2x00_desc_read(txd, 1, &word);
- rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset);
- rt2x00_set_field32(&word, TXD_W1_AIFS, entry->queue->aifs);
- rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min);
- rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max);
- rt2x00_desc_write(txd, 1, word);
-
- rt2x00_desc_read(txd, 2, &word);
- rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal);
- rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service);
- rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW,
- txdesc->u.plcp.length_low);
- rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH,
- txdesc->u.plcp.length_high);
- rt2x00_desc_write(txd, 2, word);
-
- if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) {
- _rt2x00_desc_write(txd, 3, skbdesc->iv[0]);
- _rt2x00_desc_write(txd, 4, skbdesc->iv[1]);
- }
-
- /*
- * Register descriptor details in skb frame descriptor.
- */
- skbdesc->flags |= SKBDESC_DESC_IN_SKB;
- skbdesc->desc = txd;
- skbdesc->desc_len = TXD_DESC_SIZE;
-}
-
-/*
- * TX data initialization
- */
-static void rt2500usb_beacondone(struct urb *urb);
-
-static void rt2500usb_write_beacon(struct queue_entry *entry,
- struct txentry_desc *txdesc)
-{
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
- struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
- int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint);
- int length;
- u16 reg, reg0;
-
- /*
- * Disable beaconing while we are reloading the beacon data,
- * otherwise we might be sending out invalid data.
- */
- rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
- rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
-
- /*
- * Add space for the descriptor in front of the skb.
- */
- skb_push(entry->skb, TXD_DESC_SIZE);
- memset(entry->skb->data, 0, TXD_DESC_SIZE);
-
- /*
- * Write the TX descriptor for the beacon.
- */
- rt2500usb_write_tx_desc(entry, txdesc);
-
- /*
- * Dump beacon to userspace through debugfs.
- */
- rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb);
-
- /*
- * USB devices cannot blindly pass the skb->len as the
- * length of the data to usb_fill_bulk_urb. Pass the skb
- * to the driver to determine what the length should be.
- */
- length = rt2x00dev->ops->lib->get_tx_data_len(entry);
-
- usb_fill_bulk_urb(bcn_priv->urb, usb_dev, pipe,
- entry->skb->data, length, rt2500usb_beacondone,
- entry);
-
- /*
- * Second we need to create the guardian byte.
- * We only need a single byte, so lets recycle
- * the 'flags' field we are not using for beacons.
- */
- bcn_priv->guardian_data = 0;
- usb_fill_bulk_urb(bcn_priv->guardian_urb, usb_dev, pipe,
- &bcn_priv->guardian_data, 1, rt2500usb_beacondone,
- entry);
-
- /*
- * Send out the guardian byte.
- */
- usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC);
-
- /*
- * Enable beaconing again.
- */
- rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 1);
- rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 1);
- reg0 = reg;
- rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 1);
- /*
- * Beacon generation will fail initially.
- * To prevent this we need to change the TXRX_CSR19
- * register several times (reg0 is the same as reg
- * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0
- * and 1 in reg).
- */
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
- rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
-}
-
-static int rt2500usb_get_tx_data_len(struct queue_entry *entry)
-{
- int length;
-
- /*
- * The length _must_ be a multiple of 2,
- * but it must _not_ be a multiple of the USB packet size.
- */
- length = roundup(entry->skb->len, 2);
- length += (2 * !(length % entry->queue->usb_maxpacket));
-
- return length;
-}
-
-/*
- * RX control handlers
- */
-static void rt2500usb_fill_rxdone(struct queue_entry *entry,
- struct rxdone_entry_desc *rxdesc)
-{
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct queue_entry_priv_usb *entry_priv = entry->priv_data;
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
- __le32 *rxd =
- (__le32 *)(entry->skb->data +
- (entry_priv->urb->actual_length -
- entry->queue->desc_size));
- u32 word0;
- u32 word1;
-
- /*
- * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of
- * frame data in rt2x00usb.
- */
- memcpy(skbdesc->desc, rxd, skbdesc->desc_len);
- rxd = (__le32 *)skbdesc->desc;
-
- /*
- * It is now safe to read the descriptor on all architectures.
- */
- rt2x00_desc_read(rxd, 0, &word0);
- rt2x00_desc_read(rxd, 1, &word1);
-
- if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR))
- rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
- if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR))
- rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC;
-
- rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER);
- if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR))
- rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY;
-
- if (rxdesc->cipher != CIPHER_NONE) {
- _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]);
- _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]);
- rxdesc->dev_flags |= RXDONE_CRYPTO_IV;
-
- /* ICV is located at the end of frame */
-
- rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
- if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
- rxdesc->flags |= RX_FLAG_DECRYPTED;
- else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
- rxdesc->flags |= RX_FLAG_MMIC_ERROR;
- }
-
- /*
- * Obtain the status about this packet.
- * When frame was received with an OFDM bitrate,
- * the signal is the PLCP value. If it was received with
- * a CCK bitrate the signal is the rate in 100kbit/s.
- */
- rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL);
- rxdesc->rssi =
- rt2x00_get_field32(word1, RXD_W1_RSSI) - rt2x00dev->rssi_offset;
- rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT);
-
- if (rt2x00_get_field32(word0, RXD_W0_OFDM))
- rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP;
- else
- rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE;
- if (rt2x00_get_field32(word0, RXD_W0_MY_BSS))
- rxdesc->dev_flags |= RXDONE_MY_BSS;
-
- /*
- * Adjust the skb memory window to the frame boundaries.
- */
- skb_trim(entry->skb, rxdesc->size);
-}
-
-/*
- * Interrupt functions.
- */
-static void rt2500usb_beacondone(struct urb *urb)
-{
- struct queue_entry *entry = (struct queue_entry *)urb->context;
- struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
-
- if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &entry->queue->rt2x00dev->flags))
- return;
-
- /*
- * Check if this was the guardian beacon,
- * if that was the case we need to send the real beacon now.
- * Otherwise we should free the sk_buffer, the device
- * should be doing the rest of the work now.
- */
- if (bcn_priv->guardian_urb == urb) {
- usb_submit_urb(bcn_priv->urb, GFP_ATOMIC);
- } else if (bcn_priv->urb == urb) {
- dev_kfree_skb(entry->skb);
- entry->skb = NULL;
- }
-}
-
-/*
- * Device probe functions.
- */
-static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
-{
- u16 word;
- u8 *mac;
- u8 bbp;
-
- rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE);
-
- /*
- * Start validation of the data that has been read.
- */
- mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
- if (!is_valid_ether_addr(mac)) {
- eth_random_addr(mac);
- rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT,
- ANTENNA_SW_DIVERSITY);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT,
- ANTENNA_SW_DIVERSITY);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE,
- LED_MODE_DEFAULT);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word);
- rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word);
- rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI,
- DEFAULT_RSSI_OFFSET);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word);
- rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n",
- word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_THRESHOLD, 45);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE, word);
- rt2x00_eeprom_dbg(rt2x00dev, "BBPtune: 0x%04x\n", word);
- }
-
- /*
- * Switch lower vgc bound to current BBP R17 value,
- * lower the value a bit for better quality.
- */
- rt2500usb_bbp_read(rt2x00dev, 17, &bbp);
- bbp -= 6;
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCUPPER, 0x40);
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word);
- rt2x00_eeprom_dbg(rt2x00dev, "BBPtune vgc: 0x%04x\n", word);
- } else {
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_LOW, 0x48);
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_HIGH, 0x41);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R17, word);
- rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r17: 0x%04x\n", word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_LOW, 0x40);
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_HIGH, 0x80);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R24, word);
- rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r24: 0x%04x\n", word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_LOW, 0x40);
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_HIGH, 0x50);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R25, word);
- rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r25: 0x%04x\n", word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_LOW, 0x60);
- rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_HIGH, 0x6d);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R61, word);
- rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r61: 0x%04x\n", word);
- }
-
- return 0;
-}
-
-static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
-{
- u16 reg;
- u16 value;
- u16 eeprom;
-
- /*
- * Read EEPROM word for configuration.
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom);
-
- /*
- * Identify RF chipset.
- */
- value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
- rt2500usb_register_read(rt2x00dev, MAC_CSR0, &reg);
- rt2x00_set_chip(rt2x00dev, RT2570, value, reg);
-
- if (((reg & 0xfff0) != 0) || ((reg & 0x0000000f) == 0)) {
- rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n");
- return -ENODEV;
- }
-
- if (!rt2x00_rf(rt2x00dev, RF2522) &&
- !rt2x00_rf(rt2x00dev, RF2523) &&
- !rt2x00_rf(rt2x00dev, RF2524) &&
- !rt2x00_rf(rt2x00dev, RF2525) &&
- !rt2x00_rf(rt2x00dev, RF2525E) &&
- !rt2x00_rf(rt2x00dev, RF5222)) {
- rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n");
- return -ENODEV;
- }
-
- /*
- * Identify default antenna configuration.
- */
- rt2x00dev->default_ant.tx =
- rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT);
- rt2x00dev->default_ant.rx =
- rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT);
-
- /*
- * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead.
- * I am not 100% sure about this, but the legacy drivers do not
- * indicate antenna swapping in software is required when
- * diversity is enabled.
- */
- if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY)
- rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY;
- if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY)
- rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY;
-
- /*
- * Store led mode, for correct led behaviour.
- */
-#ifdef CONFIG_RT2X00_LIB_LEDS
- value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
-
- rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
- if (value == LED_MODE_TXRX_ACTIVITY ||
- value == LED_MODE_DEFAULT ||
- value == LED_MODE_ASUS)
- rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual,
- LED_TYPE_ACTIVITY);
-#endif /* CONFIG_RT2X00_LIB_LEDS */
-
- /*
- * Detect if this device has an hardware controlled radio.
- */
- if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
- __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags);
-
- /*
- * Read the RSSI <-> dBm offset information.
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom);
- rt2x00dev->rssi_offset =
- rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI);
-
- return 0;
-}
-
-/*
- * RF value list for RF2522
- * Supports: 2.4 GHz
- */
-static const struct rf_channel rf_vals_bg_2522[] = {
- { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 },
- { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 },
- { 3, 0x00002050, 0x000c2002, 0x00000101, 0 },
- { 4, 0x00002050, 0x000c2016, 0x00000101, 0 },
- { 5, 0x00002050, 0x000c202a, 0x00000101, 0 },
- { 6, 0x00002050, 0x000c203e, 0x00000101, 0 },
- { 7, 0x00002050, 0x000c2052, 0x00000101, 0 },
- { 8, 0x00002050, 0x000c2066, 0x00000101, 0 },
- { 9, 0x00002050, 0x000c207a, 0x00000101, 0 },
- { 10, 0x00002050, 0x000c208e, 0x00000101, 0 },
- { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 },
- { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 },
- { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 },
- { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 },
-};
-
-/*
- * RF value list for RF2523
- * Supports: 2.4 GHz
- */
-static const struct rf_channel rf_vals_bg_2523[] = {
- { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b },
- { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b },
- { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b },
- { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b },
- { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b },
- { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b },
- { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b },
- { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b },
- { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b },
- { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b },
- { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b },
- { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b },
- { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b },
- { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 },
-};
-
-/*
- * RF value list for RF2524
- * Supports: 2.4 GHz
- */
-static const struct rf_channel rf_vals_bg_2524[] = {
- { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b },
- { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b },
- { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b },
- { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b },
- { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b },
- { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b },
- { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b },
- { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b },
- { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b },
- { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b },
- { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b },
- { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b },
- { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b },
- { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 },
-};
-
-/*
- * RF value list for RF2525
- * Supports: 2.4 GHz
- */
-static const struct rf_channel rf_vals_bg_2525[] = {
- { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b },
- { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b },
- { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b },
- { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b },
- { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b },
- { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b },
- { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b },
- { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b },
- { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b },
- { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b },
- { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b },
- { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b },
- { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b },
- { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 },
-};
-
-/*
- * RF value list for RF2525e
- * Supports: 2.4 GHz
- */
-static const struct rf_channel rf_vals_bg_2525e[] = {
- { 1, 0x00022010, 0x0000089a, 0x00060111, 0x00000e1b },
- { 2, 0x00022010, 0x0000089e, 0x00060111, 0x00000e07 },
- { 3, 0x00022010, 0x0000089e, 0x00060111, 0x00000e1b },
- { 4, 0x00022010, 0x000008a2, 0x00060111, 0x00000e07 },
- { 5, 0x00022010, 0x000008a2, 0x00060111, 0x00000e1b },
- { 6, 0x00022010, 0x000008a6, 0x00060111, 0x00000e07 },
- { 7, 0x00022010, 0x000008a6, 0x00060111, 0x00000e1b },
- { 8, 0x00022010, 0x000008aa, 0x00060111, 0x00000e07 },
- { 9, 0x00022010, 0x000008aa, 0x00060111, 0x00000e1b },
- { 10, 0x00022010, 0x000008ae, 0x00060111, 0x00000e07 },
- { 11, 0x00022010, 0x000008ae, 0x00060111, 0x00000e1b },
- { 12, 0x00022010, 0x000008b2, 0x00060111, 0x00000e07 },
- { 13, 0x00022010, 0x000008b2, 0x00060111, 0x00000e1b },
- { 14, 0x00022010, 0x000008b6, 0x00060111, 0x00000e23 },
-};
-
-/*
- * RF value list for RF5222
- * Supports: 2.4 GHz & 5.2 GHz
- */
-static const struct rf_channel rf_vals_5222[] = {
- { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b },
- { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b },
- { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b },
- { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b },
- { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b },
- { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b },
- { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b },
- { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b },
- { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b },
- { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b },
- { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b },
- { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b },
- { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b },
- { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b },
-
- /* 802.11 UNI / HyperLan 2 */
- { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f },
- { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f },
- { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f },
- { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f },
- { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f },
- { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f },
- { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f },
- { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f },
-
- /* 802.11 HyperLan 2 */
- { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f },
- { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f },
- { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f },
- { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f },
- { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f },
- { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f },
- { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f },
- { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f },
- { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f },
- { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f },
-
- /* 802.11 UNII */
- { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f },
- { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 },
- { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 },
- { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 },
- { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 },
-};
-
-static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
-{
- struct hw_mode_spec *spec = &rt2x00dev->spec;
- struct channel_info *info;
- char *tx_power;
- unsigned int i;
-
- /*
- * Initialize all hw fields.
- *
- * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are
- * capable of sending the buffered frames out after the DTIM
- * transmission using rt2x00lib_beacondone. This will send out
- * multicast and broadcast traffic immediately instead of buffering it
- * infinitly and thus dropping it after some time.
- */
- ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK);
- ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS);
- ieee80211_hw_set(rt2x00dev->hw, RX_INCLUDES_FCS);
- ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM);
-
- /*
- * Disable powersaving as default.
- */
- rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
-
- SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
- SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
- rt2x00_eeprom_addr(rt2x00dev,
- EEPROM_MAC_ADDR_0));
-
- /*
- * Initialize hw_mode information.
- */
- spec->supported_bands = SUPPORT_BAND_2GHZ;
- spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
-
- if (rt2x00_rf(rt2x00dev, RF2522)) {
- spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522);
- spec->channels = rf_vals_bg_2522;
- } else if (rt2x00_rf(rt2x00dev, RF2523)) {
- spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523);
- spec->channels = rf_vals_bg_2523;
- } else if (rt2x00_rf(rt2x00dev, RF2524)) {
- spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524);
- spec->channels = rf_vals_bg_2524;
- } else if (rt2x00_rf(rt2x00dev, RF2525)) {
- spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525);
- spec->channels = rf_vals_bg_2525;
- } else if (rt2x00_rf(rt2x00dev, RF2525E)) {
- spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e);
- spec->channels = rf_vals_bg_2525e;
- } else if (rt2x00_rf(rt2x00dev, RF5222)) {
- spec->supported_bands |= SUPPORT_BAND_5GHZ;
- spec->num_channels = ARRAY_SIZE(rf_vals_5222);
- spec->channels = rf_vals_5222;
- }
-
- /*
- * Create channel information array
- */
- info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- spec->channels_info = info;
-
- tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
- for (i = 0; i < 14; i++) {
- info[i].max_power = MAX_TXPOWER;
- info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
- }
-
- if (spec->num_channels > 14) {
- for (i = 14; i < spec->num_channels; i++) {
- info[i].max_power = MAX_TXPOWER;
- info[i].default_power1 = DEFAULT_TXPOWER;
- }
- }
-
- return 0;
-}
-
-static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
-{
- int retval;
- u16 reg;
-
- /*
- * Allocate eeprom data.
- */
- retval = rt2500usb_validate_eeprom(rt2x00dev);
- if (retval)
- return retval;
-
- retval = rt2500usb_init_eeprom(rt2x00dev);
- if (retval)
- return retval;
-
- /*
- * Enable rfkill polling by setting GPIO direction of the
- * rfkill switch GPIO pin correctly.
- */
- rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
- rt2x00_set_field16(&reg, MAC_CSR19_DIR0, 0);
- rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg);
-
- /*
- * Initialize hw specifications.
- */
- retval = rt2500usb_probe_hw_mode(rt2x00dev);
- if (retval)
- return retval;
-
- /*
- * This device requires the atim queue
- */
- __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags);
- __set_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags);
- if (!modparam_nohwcrypt) {
- __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags);
- __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags);
- }
- __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags);
- __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags);
-
- /*
- * Set the rssi offset.
- */
- rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
-
- return 0;
-}
-
-static const struct ieee80211_ops rt2500usb_mac80211_ops = {
- .tx = rt2x00mac_tx,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
- .remove_interface = rt2x00mac_remove_interface,
- .config = rt2x00mac_config,
- .configure_filter = rt2x00mac_configure_filter,
- .set_tim = rt2x00mac_set_tim,
- .set_key = rt2x00mac_set_key,
- .sw_scan_start = rt2x00mac_sw_scan_start,
- .sw_scan_complete = rt2x00mac_sw_scan_complete,
- .get_stats = rt2x00mac_get_stats,
- .bss_info_changed = rt2x00mac_bss_info_changed,
- .conf_tx = rt2x00mac_conf_tx,
- .rfkill_poll = rt2x00mac_rfkill_poll,
- .flush = rt2x00mac_flush,
- .set_antenna = rt2x00mac_set_antenna,
- .get_antenna = rt2x00mac_get_antenna,
- .get_ringparam = rt2x00mac_get_ringparam,
- .tx_frames_pending = rt2x00mac_tx_frames_pending,
-};
-
-static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
- .probe_hw = rt2500usb_probe_hw,
- .initialize = rt2x00usb_initialize,
- .uninitialize = rt2x00usb_uninitialize,
- .clear_entry = rt2x00usb_clear_entry,
- .set_device_state = rt2500usb_set_device_state,
- .rfkill_poll = rt2500usb_rfkill_poll,
- .link_stats = rt2500usb_link_stats,
- .reset_tuner = rt2500usb_reset_tuner,
- .watchdog = rt2x00usb_watchdog,
- .start_queue = rt2500usb_start_queue,
- .kick_queue = rt2x00usb_kick_queue,
- .stop_queue = rt2500usb_stop_queue,
- .flush_queue = rt2x00usb_flush_queue,
- .write_tx_desc = rt2500usb_write_tx_desc,
- .write_beacon = rt2500usb_write_beacon,
- .get_tx_data_len = rt2500usb_get_tx_data_len,
- .fill_rxdone = rt2500usb_fill_rxdone,
- .config_shared_key = rt2500usb_config_key,
- .config_pairwise_key = rt2500usb_config_key,
- .config_filter = rt2500usb_config_filter,
- .config_intf = rt2500usb_config_intf,
- .config_erp = rt2500usb_config_erp,
- .config_ant = rt2500usb_config_ant,
- .config = rt2500usb_config,
-};
-
-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;
-
- 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;
-
- 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;
-
- 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,
- .max_ap_intf = 1,
- .eeprom_size = EEPROM_SIZE,
- .rf_size = RF_SIZE,
- .tx_queues = NUM_TX_QUEUES,
- .queue_init = rt2500usb_queue_init,
- .lib = &rt2500usb_rt2x00_ops,
- .hw = &rt2500usb_mac80211_ops,
-#ifdef CONFIG_RT2X00_LIB_DEBUGFS
- .debugfs = &rt2500usb_rt2x00debug,
-#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
-};
-
-/*
- * rt2500usb module information.
- */
-static struct usb_device_id rt2500usb_device_table[] = {
- /* ASUS */
- { USB_DEVICE(0x0b05, 0x1706) },
- { USB_DEVICE(0x0b05, 0x1707) },
- /* Belkin */
- { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050A ver. 2.x */
- { USB_DEVICE(0x050d, 0x7051) },
- /* Cisco Systems */
- { USB_DEVICE(0x13b1, 0x000d) },
- { USB_DEVICE(0x13b1, 0x0011) },
- { USB_DEVICE(0x13b1, 0x001a) },
- /* Conceptronic */
- { USB_DEVICE(0x14b2, 0x3c02) },
- /* D-LINK */
- { USB_DEVICE(0x2001, 0x3c00) },
- /* Gigabyte */
- { USB_DEVICE(0x1044, 0x8001) },
- { USB_DEVICE(0x1044, 0x8007) },
- /* Hercules */
- { USB_DEVICE(0x06f8, 0xe000) },
- /* Melco */
- { USB_DEVICE(0x0411, 0x005e) },
- { USB_DEVICE(0x0411, 0x0066) },
- { USB_DEVICE(0x0411, 0x0067) },
- { USB_DEVICE(0x0411, 0x008b) },
- { USB_DEVICE(0x0411, 0x0097) },
- /* MSI */
- { USB_DEVICE(0x0db0, 0x6861) },
- { USB_DEVICE(0x0db0, 0x6865) },
- { USB_DEVICE(0x0db0, 0x6869) },
- /* Ralink */
- { USB_DEVICE(0x148f, 0x1706) },
- { USB_DEVICE(0x148f, 0x2570) },
- { USB_DEVICE(0x148f, 0x9020) },
- /* Sagem */
- { USB_DEVICE(0x079b, 0x004b) },
- /* Siemens */
- { USB_DEVICE(0x0681, 0x3c06) },
- /* SMC */
- { USB_DEVICE(0x0707, 0xee13) },
- /* Spairon */
- { USB_DEVICE(0x114b, 0x0110) },
- /* SURECOM */
- { USB_DEVICE(0x0769, 0x11f3) },
- /* Trust */
- { USB_DEVICE(0x0eb0, 0x9020) },
- /* VTech */
- { USB_DEVICE(0x0f88, 0x3012) },
- /* Zinwell */
- { USB_DEVICE(0x5a57, 0x0260) },
- { 0, }
-};
-
-MODULE_AUTHOR(DRV_PROJECT);
-MODULE_VERSION(DRV_VERSION);
-MODULE_DESCRIPTION("Ralink RT2500 USB Wireless LAN driver.");
-MODULE_SUPPORTED_DEVICE("Ralink RT2570 USB chipset based cards");
-MODULE_DEVICE_TABLE(usb, rt2500usb_device_table);
-MODULE_LICENSE("GPL");
-
-static int rt2500usb_probe(struct usb_interface *usb_intf,
- const struct usb_device_id *id)
-{
- return rt2x00usb_probe(usb_intf, &rt2500usb_ops);
-}
-
-static struct usb_driver rt2500usb_driver = {
- .name = KBUILD_MODNAME,
- .id_table = rt2500usb_device_table,
- .probe = rt2500usb_probe,
- .disconnect = rt2x00usb_disconnect,
- .suspend = rt2x00usb_suspend,
- .resume = rt2x00usb_resume,
- .reset_resume = rt2x00usb_resume,
- .disable_hub_initiated_lpm = 1,
-};
-
-module_usb_driver(rt2500usb_driver);
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c
deleted file mode 100644
index d93db4b0371b..000000000000
--- a/drivers/net/wireless/rt2x00/rt2x00pci.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
- <http://rt2x00.serialmonkey.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- Module: rt2x00pci
- Abstract: rt2x00 generic pci device routines.
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-
-#include "rt2x00.h"
-#include "rt2x00pci.h"
-
-/*
- * PCI driver handlers.
- */
-static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev)
-{
- kfree(rt2x00dev->rf);
- rt2x00dev->rf = NULL;
-
- kfree(rt2x00dev->eeprom);
- rt2x00dev->eeprom = NULL;
-
- if (rt2x00dev->csr.base) {
- iounmap(rt2x00dev->csr.base);
- rt2x00dev->csr.base = NULL;
- }
-}
-
-static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev)
-{
- struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev);
-
- rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0);
- if (!rt2x00dev->csr.base)
- goto exit;
-
- rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL);
- if (!rt2x00dev->eeprom)
- goto exit;
-
- rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL);
- if (!rt2x00dev->rf)
- goto exit;
-
- return 0;
-
-exit:
- rt2x00_probe_err("Failed to allocate registers\n");
-
- rt2x00pci_free_reg(rt2x00dev);
-
- return -ENOMEM;
-}
-
-int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
-{
- struct ieee80211_hw *hw;
- struct rt2x00_dev *rt2x00dev;
- int retval;
- u16 chip;
-
- retval = pci_enable_device(pci_dev);
- if (retval) {
- rt2x00_probe_err("Enable device failed\n");
- return retval;
- }
-
- retval = pci_request_regions(pci_dev, pci_name(pci_dev));
- if (retval) {
- rt2x00_probe_err("PCI request regions failed\n");
- goto exit_disable_device;
- }
-
- pci_set_master(pci_dev);
-
- if (pci_set_mwi(pci_dev))
- rt2x00_probe_err("MWI not available\n");
-
- if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) {
- rt2x00_probe_err("PCI DMA not supported\n");
- retval = -EIO;
- goto exit_release_regions;
- }
-
- hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
- if (!hw) {
- rt2x00_probe_err("Failed to allocate hardware\n");
- retval = -ENOMEM;
- goto exit_release_regions;
- }
-
- pci_set_drvdata(pci_dev, hw);
-
- rt2x00dev = hw->priv;
- rt2x00dev->dev = &pci_dev->dev;
- rt2x00dev->ops = ops;
- rt2x00dev->hw = hw;
- rt2x00dev->irq = pci_dev->irq;
- rt2x00dev->name = ops->name;
-
- if (pci_is_pcie(pci_dev))
- rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE);
- else
- rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI);
-
- retval = rt2x00pci_alloc_reg(rt2x00dev);
- if (retval)
- goto exit_free_device;
-
- /*
- * Because rt3290 chip use different efuse offset to read efuse data.
- * So before read efuse it need to indicate it is the
- * rt3290 or not.
- */
- pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip);
- rt2x00dev->chip.rt = chip;
-
- retval = rt2x00lib_probe_dev(rt2x00dev);
- if (retval)
- goto exit_free_reg;
-
- return 0;
-
-exit_free_reg:
- rt2x00pci_free_reg(rt2x00dev);
-
-exit_free_device:
- ieee80211_free_hw(hw);
-
-exit_release_regions:
- pci_release_regions(pci_dev);
-
-exit_disable_device:
- pci_disable_device(pci_dev);
-
- return retval;
-}
-EXPORT_SYMBOL_GPL(rt2x00pci_probe);
-
-void rt2x00pci_remove(struct pci_dev *pci_dev)
-{
- struct ieee80211_hw *hw = pci_get_drvdata(pci_dev);
- struct rt2x00_dev *rt2x00dev = hw->priv;
-
- /*
- * Free all allocated data.
- */
- rt2x00lib_remove_dev(rt2x00dev);
- rt2x00pci_free_reg(rt2x00dev);
- ieee80211_free_hw(hw);
-
- /*
- * Free the PCI device data.
- */
- pci_disable_device(pci_dev);
- pci_release_regions(pci_dev);
-}
-EXPORT_SYMBOL_GPL(rt2x00pci_remove);
-
-#ifdef CONFIG_PM
-int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state)
-{
- struct ieee80211_hw *hw = pci_get_drvdata(pci_dev);
- struct rt2x00_dev *rt2x00dev = hw->priv;
- int retval;
-
- retval = rt2x00lib_suspend(rt2x00dev, state);
- if (retval)
- return retval;
-
- pci_save_state(pci_dev);
- pci_disable_device(pci_dev);
- return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
-}
-EXPORT_SYMBOL_GPL(rt2x00pci_suspend);
-
-int rt2x00pci_resume(struct pci_dev *pci_dev)
-{
- struct ieee80211_hw *hw = pci_get_drvdata(pci_dev);
- struct rt2x00_dev *rt2x00dev = hw->priv;
-
- if (pci_set_power_state(pci_dev, PCI_D0) ||
- pci_enable_device(pci_dev)) {
- rt2x00_err(rt2x00dev, "Failed to resume device\n");
- return -EIO;
- }
-
- pci_restore_state(pci_dev);
- return rt2x00lib_resume(rt2x00dev);
-}
-EXPORT_SYMBOL_GPL(rt2x00pci_resume);
-#endif /* CONFIG_PM */
-
-/*
- * rt2x00pci module information.
- */
-MODULE_AUTHOR(DRV_PROJECT);
-MODULE_VERSION(DRV_VERSION);
-MODULE_DESCRIPTION("rt2x00 pci library");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/st/Kconfig b/drivers/net/wireless/st/Kconfig
new file mode 100644
index 000000000000..969b4f6e53b5
--- /dev/null
+++ b/drivers/net/wireless/st/Kconfig
@@ -0,0 +1,16 @@
+config WLAN_VENDOR_ST
+ bool "STMicroelectronics devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ST
+
+source "drivers/net/wireless/st/cw1200/Kconfig"
+
+endif # WLAN_VENDOR_ST
diff --git a/drivers/net/wireless/st/Makefile b/drivers/net/wireless/st/Makefile
new file mode 100644
index 000000000000..a60d6350ba46
--- /dev/null
+++ b/drivers/net/wireless/st/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CW1200) += cw1200/
diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/st/cw1200/Kconfig
index 0880742eab17..0880742eab17 100644
--- a/drivers/net/wireless/cw1200/Kconfig
+++ b/drivers/net/wireless/st/cw1200/Kconfig
diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/st/cw1200/Makefile
index b086aac6547a..b086aac6547a 100644
--- a/drivers/net/wireless/cw1200/Makefile
+++ b/drivers/net/wireless/st/cw1200/Makefile
diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c
index 92d299aa257c..92d299aa257c 100644
--- a/drivers/net/wireless/cw1200/bh.c
+++ b/drivers/net/wireless/st/cw1200/bh.c
diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/st/cw1200/bh.h
index af6a4853728f..af6a4853728f 100644
--- a/drivers/net/wireless/cw1200/bh.h
+++ b/drivers/net/wireless/st/cw1200/bh.h
diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/st/cw1200/cw1200.h
index 1ad7d3602520..1ad7d3602520 100644
--- a/drivers/net/wireless/cw1200/cw1200.h
+++ b/drivers/net/wireless/st/cw1200/cw1200.h
diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c
index d3acc85932a5..d3acc85932a5 100644
--- a/drivers/net/wireless/cw1200/cw1200_sdio.c
+++ b/drivers/net/wireless/st/cw1200/cw1200_sdio.c
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c
index a740083634d8..a740083634d8 100644
--- a/drivers/net/wireless/cw1200/cw1200_spi.c
+++ b/drivers/net/wireless/st/cw1200/cw1200_spi.c
diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/st/cw1200/debug.c
index 34f97c31eecf..34f97c31eecf 100644
--- a/drivers/net/wireless/cw1200/debug.c
+++ b/drivers/net/wireless/st/cw1200/debug.c
diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/st/cw1200/debug.h
index b525aba53bfc..b525aba53bfc 100644
--- a/drivers/net/wireless/cw1200/debug.h
+++ b/drivers/net/wireless/st/cw1200/debug.h
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/st/cw1200/fwio.c
index 30e7646d04af..30e7646d04af 100644
--- a/drivers/net/wireless/cw1200/fwio.c
+++ b/drivers/net/wireless/st/cw1200/fwio.c
diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/st/cw1200/fwio.h
index ea3099362cdf..ea3099362cdf 100644
--- a/drivers/net/wireless/cw1200/fwio.h
+++ b/drivers/net/wireless/st/cw1200/fwio.h
diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/st/cw1200/hwbus.h
index 8b2fc831c3de..8b2fc831c3de 100644
--- a/drivers/net/wireless/cw1200/hwbus.h
+++ b/drivers/net/wireless/st/cw1200/hwbus.h
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/st/cw1200/hwio.c
index ff230b7aeedd..ff230b7aeedd 100644
--- a/drivers/net/wireless/cw1200/hwio.c
+++ b/drivers/net/wireless/st/cw1200/hwio.c
diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/st/cw1200/hwio.h
index ddf52669dc5b..ddf52669dc5b 100644
--- a/drivers/net/wireless/cw1200/hwio.h
+++ b/drivers/net/wireless/st/cw1200/hwio.h
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c
index 0e51e27d2e3f..0e51e27d2e3f 100644
--- a/drivers/net/wireless/cw1200/main.c
+++ b/drivers/net/wireless/st/cw1200/main.c
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c
index d2202ae92bdd..d2202ae92bdd 100644
--- a/drivers/net/wireless/cw1200/pm.c
+++ b/drivers/net/wireless/st/cw1200/pm.c
diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/st/cw1200/pm.h
index 3ed90ff22bb8..3ed90ff22bb8 100644
--- a/drivers/net/wireless/cw1200/pm.h
+++ b/drivers/net/wireless/st/cw1200/pm.h
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c
index 0ba5ef9b3e7b..0ba5ef9b3e7b 100644
--- a/drivers/net/wireless/cw1200/queue.c
+++ b/drivers/net/wireless/st/cw1200/queue.c
diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/st/cw1200/queue.h
index 119f9c79c14e..119f9c79c14e 100644
--- a/drivers/net/wireless/cw1200/queue.h
+++ b/drivers/net/wireless/st/cw1200/queue.h
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/st/cw1200/scan.c
index bff81b8d4164..bff81b8d4164 100644
--- a/drivers/net/wireless/cw1200/scan.c
+++ b/drivers/net/wireless/st/cw1200/scan.c
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/st/cw1200/scan.h
index cc75459e5784..cc75459e5784 100644
--- a/drivers/net/wireless/cw1200/scan.h
+++ b/drivers/net/wireless/st/cw1200/scan.h
diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c
new file mode 100644
index 000000000000..06321c799c90
--- /dev/null
+++ b/drivers/net/wireless/st/cw1200/sta.c
@@ -0,0 +1,2393 @@
+/*
+ * Mac80211 STA API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+
+#include "cw1200.h"
+#include "sta.h"
+#include "fwio.h"
+#include "bh.h"
+#include "debug.h"
+
+#ifndef ERP_INFO_BYTE_OFFSET
+#define ERP_INFO_BYTE_OFFSET 2
+#endif
+
+static void cw1200_do_join(struct cw1200_common *priv);
+static void cw1200_do_unjoin(struct cw1200_common *priv);
+
+static int cw1200_upload_beacon(struct cw1200_common *priv);
+static int cw1200_upload_pspoll(struct cw1200_common *priv);
+static int cw1200_upload_null(struct cw1200_common *priv);
+static int cw1200_upload_qosnull(struct cw1200_common *priv);
+static int cw1200_start_ap(struct cw1200_common *priv);
+static int cw1200_update_beaconing(struct cw1200_common *priv);
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+ bool enable);
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ int link_id);
+static int __cw1200_flush(struct cw1200_common *priv, bool drop);
+
+static inline void __cw1200_free_event_queue(struct list_head *list)
+{
+ struct cw1200_wsm_event *event, *tmp;
+ list_for_each_entry_safe(event, tmp, list, link) {
+ list_del(&event->link);
+ kfree(event);
+ }
+}
+
+/* ******************************************************************** */
+/* STA API */
+
+int cw1200_start(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+
+ cw1200_pm_stay_awake(&priv->pm_state, HZ);
+
+ mutex_lock(&priv->conf_mutex);
+
+ /* default EDCA */
+ WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ if (ret)
+ goto out;
+
+ ret = cw1200_set_uapsd_param(priv, &priv->edca);
+ if (ret)
+ goto out;
+
+ priv->setbssparams_done = false;
+
+ memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ priv->wep_default_key_id = -1;
+
+ priv->cqm_beacon_loss_count = 10;
+
+ ret = cw1200_setup_mac(priv);
+ if (ret)
+ goto out;
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_stop(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ LIST_HEAD(list);
+ int i;
+
+ wsm_lock_tx(priv);
+
+ while (down_trylock(&priv->scan.lock)) {
+ /* Scan is in progress. Force it to stop. */
+ priv->scan.req = NULL;
+ schedule();
+ }
+ up(&priv->scan.lock);
+
+ cancel_delayed_work_sync(&priv->scan.probe_work);
+ cancel_delayed_work_sync(&priv->scan.timeout);
+ cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+ cancel_delayed_work_sync(&priv->link_id_gc_work);
+ flush_workqueue(priv->workqueue);
+ del_timer_sync(&priv->mcast_timeout);
+ mutex_lock(&priv->conf_mutex);
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->listening = false;
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+ __cw1200_free_event_queue(&list);
+
+
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ priv->join_pending = false;
+
+ for (i = 0; i < 4; i++)
+ cw1200_queue_clear(&priv->tx_queue[i]);
+ mutex_unlock(&priv->conf_mutex);
+ tx_policy_clean(priv);
+
+ /* HACK! */
+ if (atomic_xchg(&priv->tx_lock, 1) != 1)
+ pr_debug("[STA] TX is force-unlocked due to stop request.\n");
+
+ wsm_unlock_tx(priv);
+ atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
+}
+
+static int cw1200_bssloss_mitigation = 1;
+module_param(cw1200_bssloss_mitigation, int, 0644);
+MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
+
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+ int init, int good, int bad)
+{
+ int tx = 0;
+
+ priv->delayed_link_loss = 0;
+ cancel_work_sync(&priv->bss_params_work);
+
+ pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
+ priv->bss_loss_state,
+ init, good, bad,
+ atomic_read(&priv->tx_lock),
+ priv->delayed_unjoin);
+
+ /* If we have a pending unjoin */
+ if (priv->delayed_unjoin)
+ return;
+
+ if (init) {
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work,
+ HZ);
+ priv->bss_loss_state = 0;
+
+ /* Skip the confimration procedure in P2P case */
+ if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
+ tx = 1;
+ } else if (good) {
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ priv->bss_loss_state = 0;
+ queue_work(priv->workqueue, &priv->bss_params_work);
+ } else if (bad) {
+ /* XXX Should we just keep going until we time out? */
+ if (priv->bss_loss_state < 3)
+ tx = 1;
+ } else {
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ priv->bss_loss_state = 0;
+ }
+
+ /* Bypass mitigation if it's disabled */
+ if (!cw1200_bssloss_mitigation)
+ tx = 0;
+
+ /* Spit out a NULL packet to our AP if necessary */
+ if (tx) {
+ struct sk_buff *skb;
+
+ priv->bss_loss_state++;
+
+ skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+ WARN_ON(!skb);
+ if (skb)
+ cw1200_tx(priv->hw, NULL, skb);
+ }
+}
+
+int cw1200_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ int ret;
+ struct cw1200_common *priv = dev->priv;
+ /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_UAPSD |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (priv->mode != NL80211_IFTYPE_MONITOR) {
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ priv->mode = vif->type;
+ break;
+ default:
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ priv->vif = vif;
+ memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
+ ret = cw1200_setup_mac(priv);
+ /* Enable auto-calibration */
+ /* Exception in subsequent channel switch; disabled.
+ * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+ * &auto_calibration_mode, sizeof(auto_calibration_mode));
+ */
+
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ int i;
+
+ mutex_lock(&priv->conf_mutex);
+ switch (priv->join_status) {
+ case CW1200_JOIN_STATUS_JOINING:
+ case CW1200_JOIN_STATUS_PRE_STA:
+ case CW1200_JOIN_STATUS_STA:
+ case CW1200_JOIN_STATUS_IBSS:
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ break;
+ case CW1200_JOIN_STATUS_AP:
+ for (i = 0; priv->link_id_map; ++i) {
+ if (priv->link_id_map & BIT(i)) {
+ reset.link_id = i;
+ wsm_reset(priv, &reset);
+ priv->link_id_map &= ~BIT(i);
+ }
+ }
+ memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
+ priv->sta_asleep_mask = 0;
+ priv->enable_beacon = false;
+ priv->tx_multicast = false;
+ priv->aid0_bit_set = false;
+ priv->buffered_multicasts = false;
+ priv->pspoll_mask = 0;
+ reset.link_id = 0;
+ wsm_reset(priv, &reset);
+ break;
+ case CW1200_JOIN_STATUS_MONITOR:
+ cw1200_update_listening(priv, false);
+ break;
+ default:
+ break;
+ }
+ priv->vif = NULL;
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ eth_zero_addr(priv->mac_addr);
+ memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
+ cw1200_free_keys(priv);
+ cw1200_setup_mac(priv);
+ priv->listening = false;
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ if (!__cw1200_flush(priv, true))
+ wsm_unlock_tx(priv);
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+int cw1200_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p)
+{
+ int ret = 0;
+ pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
+ p2p, vif->type, vif->p2p);
+
+ if (new_type != vif->type || vif->p2p != p2p) {
+ cw1200_remove_interface(dev, vif);
+ vif->type = new_type;
+ vif->p2p = p2p;
+ ret = cw1200_add_interface(dev, vif);
+ }
+
+ return ret;
+}
+
+int cw1200_config(struct ieee80211_hw *dev, u32 changed)
+{
+ int ret = 0;
+ struct cw1200_common *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+
+ pr_debug("CONFIG CHANGED: %08x\n", changed);
+
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+ /* TODO: IEEE80211_CONF_CHANGE_QOS */
+ /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ priv->output_power = conf->power_level;
+ pr_debug("[STA] TX power: %d\n", priv->output_power);
+ wsm_set_output_power(priv, priv->output_power * 10);
+ }
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
+ (priv->channel != conf->chandef.chan)) {
+ struct ieee80211_channel *ch = conf->chandef.chan;
+ struct wsm_switch_channel channel = {
+ .channel_number = ch->hw_value,
+ };
+ pr_debug("[STA] Freq %d (wsm ch: %d).\n",
+ ch->center_freq, ch->hw_value);
+
+ /* __cw1200_flush() implicitly locks tx, if successful */
+ if (!__cw1200_flush(priv, false)) {
+ if (!wsm_switch_channel(priv, &channel)) {
+ ret = wait_event_timeout(priv->channel_switch_done,
+ !priv->channel_switch_in_progress,
+ 3 * HZ);
+ if (ret) {
+ /* Already unlocks if successful */
+ priv->channel = ch;
+ ret = 0;
+ } else {
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ /* Unlock if switch channel fails */
+ wsm_unlock_tx(priv);
+ }
+ }
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ if (!(conf->flags & IEEE80211_CONF_PS))
+ priv->powersave_mode.mode = WSM_PSM_ACTIVE;
+ else if (conf->dynamic_ps_timeout <= 0)
+ priv->powersave_mode.mode = WSM_PSM_PS;
+ else
+ priv->powersave_mode.mode = WSM_PSM_FAST_PS;
+
+ /* Firmware requires that value for this 1-byte field must
+ * be specified in units of 500us. Values above the 128ms
+ * threshold are not supported.
+ */
+ if (conf->dynamic_ps_timeout >= 0x80)
+ priv->powersave_mode.fast_psm_idle_period = 0xFF;
+ else
+ priv->powersave_mode.fast_psm_idle_period =
+ conf->dynamic_ps_timeout << 1;
+
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ priv->bss_params.aid)
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+ /* TBD: It looks like it's transparent
+ * there's a monitor interface present -- use this
+ * to determine for example whether to calculate
+ * timestamps for packets or not, do not use instead
+ * of filter flags!
+ */
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ wsm_lock_tx(priv);
+ /* Disable p2p-dev mode forced by TX request */
+ if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
+ (conf->flags & IEEE80211_CONF_IDLE) &&
+ !priv->listening) {
+ cw1200_disable_listening(priv);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ }
+ wsm_set_operational_mode(priv, &mode);
+ wsm_unlock_tx(priv);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+ pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
+ conf->long_frame_max_tx_count,
+ conf->short_frame_max_tx_count);
+ spin_lock_bh(&priv->tx_policy_cache.lock);
+ priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
+ priv->short_frame_max_tx_count =
+ (conf->short_frame_max_tx_count < 0x0F) ?
+ conf->short_frame_max_tx_count : 0x0F;
+ priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
+ spin_unlock_bh(&priv->tx_policy_cache.lock);
+ }
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+ return ret;
+}
+
+void cw1200_update_filtering(struct cw1200_common *priv)
+{
+ int ret;
+ bool bssid_filtering = !priv->rx_filter.bssid;
+ bool is_p2p = priv->vif && priv->vif->p2p;
+ bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
+
+ static struct wsm_beacon_filter_control bf_ctrl;
+ static struct wsm_mib_beacon_filter_table bf_tbl = {
+ .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
+ .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ .entry[0].oui[0] = 0x50,
+ .entry[0].oui[1] = 0x6F,
+ .entry[0].oui[2] = 0x9A,
+ .entry[1].ie_id = WLAN_EID_HT_OPERATION,
+ .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ .entry[2].ie_id = WLAN_EID_ERP_INFO,
+ .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ };
+
+ if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
+ return;
+ else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+ bssid_filtering = false;
+
+ if (priv->disable_beacon_filter) {
+ bf_ctrl.enabled = 0;
+ bf_ctrl.bcn_count = 1;
+ bf_tbl.num = __cpu_to_le32(0);
+ } else if (is_p2p || !is_sta) {
+ bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
+ WSM_BEACON_FILTER_AUTO_ERP;
+ bf_ctrl.bcn_count = 0;
+ bf_tbl.num = __cpu_to_le32(2);
+ } else {
+ bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
+ bf_ctrl.bcn_count = 0;
+ bf_tbl.num = __cpu_to_le32(3);
+ }
+
+ /* When acting as p2p client being connected to p2p GO, in order to
+ * receive frames from a different p2p device, turn off bssid filter.
+ *
+ * WARNING: FW dependency!
+ * This can only be used with FW WSM371 and its successors.
+ * In that FW version even with bssid filter turned off,
+ * device will block most of the unwanted frames.
+ */
+ if (is_p2p)
+ bssid_filtering = false;
+
+ ret = wsm_set_rx_filter(priv, &priv->rx_filter);
+ if (!ret)
+ ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
+ if (!ret)
+ ret = wsm_beacon_filter_control(priv, &bf_ctrl);
+ if (!ret)
+ ret = wsm_set_bssid_filtering(priv, bssid_filtering);
+ if (!ret)
+ ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
+ if (ret)
+ wiphy_err(priv->hw->wiphy,
+ "Update filtering failed: %d.\n", ret);
+ return;
+}
+
+void cw1200_update_filtering_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ update_filtering_work);
+
+ cw1200_update_filtering(priv);
+}
+
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ set_beacon_wakeup_period_work);
+
+ wsm_set_beacon_wakeup_period(priv,
+ priv->beacon_int * priv->join_dtim_period >
+ MAX_BEACON_SKIP_TIME_MS ? 1 :
+ priv->join_dtim_period, 0);
+}
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ static u8 broadcast_ipv6[ETH_ALEN] = {
+ 0x33, 0x33, 0x00, 0x00, 0x00, 0x01
+ };
+ static u8 broadcast_ipv4[ETH_ALEN] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
+ };
+ struct cw1200_common *priv = hw->priv;
+ struct netdev_hw_addr *ha;
+ int count = 0;
+
+ /* Disable multicast filtering */
+ priv->has_multicast_subscription = false;
+ memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
+
+ if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
+ return 0;
+
+ /* Enable if requested */
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ pr_debug("[STA] multicast: %pM\n", ha->addr);
+ memcpy(&priv->multicast_filter.macaddrs[count],
+ ha->addr, ETH_ALEN);
+ if (!ether_addr_equal(ha->addr, broadcast_ipv4) &&
+ !ether_addr_equal(ha->addr, broadcast_ipv6))
+ priv->has_multicast_subscription = true;
+ count++;
+ }
+
+ if (count) {
+ priv->multicast_filter.enable = __cpu_to_le32(1);
+ priv->multicast_filter.num_addrs = __cpu_to_le32(count);
+ }
+
+ return netdev_hw_addr_list_count(mc_list);
+}
+
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct cw1200_common *priv = dev->priv;
+ bool listening = !!(*total_flags &
+ (FIF_OTHER_BSS |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ));
+
+ *total_flags &= FIF_OTHER_BSS |
+ FIF_FCSFAIL |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ;
+
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+
+ priv->rx_filter.promiscuous = 0;
+ priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
+ FIF_PROBE_REQ)) ? 1 : 0;
+ priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
+ priv->disable_beacon_filter = !(*total_flags &
+ (FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ));
+ if (priv->listening != listening) {
+ priv->listening = listening;
+ wsm_lock_tx(priv);
+ cw1200_update_listening(priv, listening);
+ wsm_unlock_tx(priv);
+ }
+ cw1200_update_filtering(priv);
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+}
+
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+ /* To prevent re-applying PM request OID again and again*/
+ bool old_uapsd_flags;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (queue < dev->queues) {
+ old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags);
+
+ WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
+ ret = wsm_set_tx_queue_params(priv,
+ &priv->tx_queue_params.params[queue], queue);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+ params->cw_min, params->cw_max,
+ params->txop, 0xc8,
+ params->uapsd);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ ret = cw1200_set_uapsd_param(priv, &priv->edca);
+ if (!ret && priv->setbssparams_done &&
+ (priv->join_status == CW1200_JOIN_STATUS_STA) &&
+ (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags)))
+ ret = cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+int cw1200_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct cw1200_common *priv = dev->priv;
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+ return 0;
+}
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+ struct wsm_set_pm pm = *arg;
+
+ if (priv->uapsd_info.uapsd_flags != 0)
+ pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
+
+ if (memcmp(&pm, &priv->firmware_ps_mode,
+ sizeof(struct wsm_set_pm))) {
+ priv->firmware_ps_mode = pm;
+ return wsm_set_pm(priv, &pm);
+ } else {
+ return 0;
+ }
+}
+
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int ret = -EOPNOTSUPP;
+ struct cw1200_common *priv = dev->priv;
+ struct ieee80211_key_seq seq;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (cmd == SET_KEY) {
+ u8 *peer_addr = NULL;
+ int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ 1 : 0;
+ int idx = cw1200_alloc_key(priv);
+ struct wsm_add_key *wsm_key = &priv->keys[idx];
+
+ if (idx < 0) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (sta)
+ peer_addr = sta->addr;
+
+ key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+ IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (key->keylen > 16) {
+ cw1200_free_key(priv, idx);
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+ memcpy(wsm_key->wep_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wep_pairwise.keydata,
+ &key->key[0], key->keylen);
+ wsm_key->wep_pairwise.keylen = key->keylen;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+ memcpy(wsm_key->wep_group.keydata,
+ &key->key[0], key->keylen);
+ wsm_key->wep_group.keylen = key->keylen;
+ wsm_key->wep_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ ieee80211_get_key_rx_seq(key, 0, &seq);
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+ memcpy(wsm_key->tkip_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->tkip_pairwise.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkip_pairwise.tx_mic_key,
+ &key->key[16], 8);
+ memcpy(wsm_key->tkip_pairwise.rx_mic_key,
+ &key->key[24], 8);
+ } else {
+ size_t mic_offset =
+ (priv->mode == NL80211_IFTYPE_AP) ?
+ 16 : 24;
+ wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+ memcpy(wsm_key->tkip_group.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkip_group.rx_mic_key,
+ &key->key[mic_offset], 8);
+
+ wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
+ wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
+ wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[6] = 0;
+ wsm_key->tkip_group.rx_seqnum[7] = 0;
+
+ wsm_key->tkip_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ ieee80211_get_key_rx_seq(key, 0, &seq);
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+ memcpy(wsm_key->aes_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->aes_pairwise.keydata,
+ &key->key[0], 16);
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+ memcpy(wsm_key->aes_group.keydata,
+ &key->key[0], 16);
+
+ wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
+ wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
+ wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
+ wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
+ wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
+ wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
+ wsm_key->aes_group.rx_seqnum[6] = 0;
+ wsm_key->aes_group.rx_seqnum[7] = 0;
+ wsm_key->aes_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+ memcpy(wsm_key->wapi_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wapi_pairwise.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapi_pairwise.mic_key,
+ &key->key[16], 16);
+ wsm_key->wapi_pairwise.keyid = key->keyidx;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+ memcpy(wsm_key->wapi_group.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapi_group.mic_key,
+ &key->key[16], 16);
+ wsm_key->wapi_group.keyid = key->keyidx;
+ }
+ break;
+ default:
+ pr_warn("Unhandled key type %d\n", key->cipher);
+ cw1200_free_key(priv, idx);
+ ret = -EOPNOTSUPP;
+ goto finally;
+ }
+ ret = wsm_add_key(priv, wsm_key);
+ if (!ret)
+ key->hw_key_idx = idx;
+ else
+ cw1200_free_key(priv, idx);
+ } else if (cmd == DISABLE_KEY) {
+ struct wsm_remove_key wsm_key = {
+ .index = key->hw_key_idx,
+ };
+
+ if (wsm_key.index > WSM_KEY_MAX_INDEX) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ cw1200_free_key(priv, wsm_key.index);
+ ret = wsm_remove_key(priv, &wsm_key);
+ } else {
+ pr_warn("Unhandled key command %d\n", cmd);
+ }
+
+finally:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_wep_key_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, wep_key_work);
+ u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+ struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+ __le32 wep_default_key_id = __cpu_to_le32(
+ priv->wep_default_key_id);
+
+ pr_debug("[STA] Setting default WEP key: %d\n",
+ priv->wep_default_key_id);
+ wsm_flush_tx(priv);
+ wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+ &wep_default_key_id, sizeof(wep_default_key_id));
+ cw1200_queue_requeue(queue, priv->pending_frame_id);
+ wsm_unlock_tx(priv);
+}
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ int ret = 0;
+ __le32 val32;
+ struct cw1200_common *priv = hw->priv;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ return 0;
+
+ if (value != (u32) -1)
+ val32 = __cpu_to_le32(value);
+ else
+ val32 = 0; /* disabled */
+
+ if (priv->rts_threshold == value)
+ goto out;
+
+ pr_debug("[STA] Setting RTS threshold: %d\n",
+ priv->rts_threshold);
+
+ /* mutex_lock(&priv->conf_mutex); */
+ ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+ &val32, sizeof(val32));
+ if (!ret)
+ priv->rts_threshold = value;
+ /* mutex_unlock(&priv->conf_mutex); */
+
+out:
+ return ret;
+}
+
+/* If successful, LOCKS the TX queue! */
+static int __cw1200_flush(struct cw1200_common *priv, bool drop)
+{
+ int i, ret;
+
+ for (;;) {
+ /* TODO: correct flush handling is required when dev_stop.
+ * Temporary workaround: 2s
+ */
+ if (drop) {
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_clear(&priv->tx_queue[i]);
+ } else {
+ ret = wait_event_timeout(
+ priv->tx_queue_stats.wait_link_id_empty,
+ cw1200_queue_stats_is_empty(
+ &priv->tx_queue_stats, -1),
+ 2 * HZ);
+ }
+
+ if (!drop && ret <= 0) {
+ ret = -ETIMEDOUT;
+ break;
+ } else {
+ ret = 0;
+ }
+
+ wsm_lock_tx(priv);
+ if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
+ /* Highly unlikely: WSM requeued frames. */
+ wsm_unlock_tx(priv);
+ continue;
+ }
+ break;
+ }
+ return ret;
+}
+
+void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
+{
+ struct cw1200_common *priv = hw->priv;
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_MONITOR:
+ drop = true;
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!priv->enable_beacon)
+ drop = true;
+ break;
+ }
+
+ if (!__cw1200_flush(priv, drop))
+ wsm_unlock_tx(priv);
+
+ return;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void cw1200_free_event_queue(struct cw1200_common *priv)
+{
+ LIST_HEAD(list);
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_event_handler(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, event_handler);
+ struct cw1200_wsm_event *event;
+ LIST_HEAD(list);
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+
+ list_for_each_entry(event, &list, link) {
+ switch (event->evt.id) {
+ case WSM_EVENT_ERROR:
+ pr_err("Unhandled WSM Error from LMAC\n");
+ break;
+ case WSM_EVENT_BSS_LOST:
+ pr_debug("[CQM] BSS lost.\n");
+ cancel_work_sync(&priv->unjoin_work);
+ if (!down_trylock(&priv->scan.lock)) {
+ cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+ up(&priv->scan.lock);
+ } else {
+ /* Scan is in progress. Delay reporting.
+ * Scan complete will trigger bss_loss_work
+ */
+ priv->delayed_link_loss = 1;
+ /* Also start a watchdog. */
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work, 5*HZ);
+ }
+ break;
+ case WSM_EVENT_BSS_REGAINED:
+ pr_debug("[CQM] BSS regained.\n");
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+ break;
+ case WSM_EVENT_RADAR_DETECTED:
+ wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
+ break;
+ case WSM_EVENT_RCPI_RSSI:
+ {
+ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+ * RSSI = RCPI / 2 - 110
+ */
+ int rcpi_rssi = (int)(event->evt.data & 0xFF);
+ int cqm_evt;
+ if (priv->cqm_use_rssi)
+ rcpi_rssi = (s8)rcpi_rssi;
+ else
+ rcpi_rssi = rcpi_rssi / 2 - 110;
+
+ cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+ pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
+ ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+ GFP_KERNEL);
+ break;
+ }
+ case WSM_EVENT_BT_INACTIVE:
+ pr_warn("Unhandled BT INACTIVE from LMAC\n");
+ break;
+ case WSM_EVENT_BT_ACTIVE:
+ pr_warn("Unhandled BT ACTIVE from LMAC\n");
+ break;
+ }
+ }
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_bss_loss_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bss_loss_work.work);
+
+ pr_debug("[CQM] Reporting connection loss.\n");
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+}
+
+void cw1200_bss_params_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bss_params_work);
+ mutex_lock(&priv->conf_mutex);
+
+ priv->bss_params.reset_beacon_loss = 1;
+ wsm_set_bss_params(priv, &priv->bss_params);
+ priv->bss_params.reset_beacon_loss = 0;
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+/* ******************************************************************** */
+/* Internal API */
+
+/* This function is called to Parse the SDD file
+ * to extract listen_interval and PTA related information
+ * sdd is a TLV: u8 id, u8 len, u8 data[]
+ */
+static int cw1200_parse_sdd_file(struct cw1200_common *priv)
+{
+ const u8 *p = priv->sdd->data;
+ int ret = 0;
+
+ while (p + 2 <= priv->sdd->data + priv->sdd->size) {
+ if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
+ pr_warn("Malformed sdd structure\n");
+ return -1;
+ }
+ switch (p[0]) {
+ case SDD_PTA_CFG_ELT_ID: {
+ u16 v;
+ if (p[1] < 4) {
+ pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
+ ret = -1;
+ break;
+ }
+ v = le16_to_cpu(*((__le16 *)(p + 2)));
+ if (!v) /* non-zero means this is enabled */
+ break;
+
+ v = le16_to_cpu(*((__le16 *)(p + 4)));
+ priv->conf_listen_interval = (v >> 7) & 0x1F;
+ pr_debug("PTA found; Listen Interval %d\n",
+ priv->conf_listen_interval);
+ break;
+ }
+ case SDD_REFERENCE_FREQUENCY_ELT_ID: {
+ u16 clk = le16_to_cpu(*((__le16 *)(p + 2)));
+ if (clk != priv->hw_refclk)
+ pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
+ clk, priv->hw_refclk);
+ break;
+ }
+ default:
+ break;
+ }
+ p += p[1] + 2;
+ }
+
+ if (!priv->bt_present) {
+ pr_debug("PTA element NOT found.\n");
+ priv->conf_listen_interval = 0;
+ }
+ return ret;
+}
+
+int cw1200_setup_mac(struct cw1200_common *priv)
+{
+ int ret = 0;
+
+ /* NOTE: There is a bug in FW: it reports signal
+ * as RSSI if RSSI subscription is enabled.
+ * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
+ *
+ * NOTE2: RSSI based reports have been switched to RCPI, since
+ * FW has a bug and RSSI reported values are not stable,
+ * what can leads to signal level oscilations in user-end applications
+ */
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER,
+ .rollingAverageCount = 16,
+ };
+
+ struct wsm_configuration cfg = {
+ .dot11StationId = &priv->mac_addr[0],
+ };
+
+ /* Remember the decission here to make sure, we will handle
+ * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
+ */
+ if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
+ priv->cqm_use_rssi = true;
+
+ if (!priv->sdd) {
+ ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
+ if (ret) {
+ pr_err("Can't load sdd file %s.\n", priv->sdd_path);
+ return ret;
+ }
+ cw1200_parse_sdd_file(priv);
+ }
+
+ cfg.dpdData = priv->sdd->data;
+ cfg.dpdData_size = priv->sdd->size;
+ ret = wsm_configuration(priv, &cfg);
+ if (ret)
+ return ret;
+
+ /* Configure RSSI/SCPI reporting as RSSI. */
+ wsm_set_rcpi_rssi_threshold(priv, &threshold);
+
+ return 0;
+}
+
+static void cw1200_join_complete(struct cw1200_common *priv)
+{
+ pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
+
+ priv->join_pending = false;
+ if (priv->join_complete_status) {
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ cw1200_update_listening(priv, priv->listening);
+ cw1200_do_unjoin(priv);
+ ieee80211_connection_loss(priv->vif);
+ } else {
+ if (priv->mode == NL80211_IFTYPE_ADHOC)
+ priv->join_status = CW1200_JOIN_STATUS_IBSS;
+ else
+ priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
+ }
+ wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
+}
+
+void cw1200_join_complete_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_complete_work);
+ mutex_lock(&priv->conf_mutex);
+ cw1200_join_complete(priv);
+ mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+ struct wsm_join_complete *arg)
+{
+ pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
+ arg->status);
+
+ if (cancel_delayed_work(&priv->join_timeout)) {
+ priv->join_complete_status = arg->status;
+ queue_work(priv->workqueue, &priv->join_complete_work);
+ }
+}
+
+/* MUST be called with tx_lock held! It will be unlocked for us. */
+static void cw1200_do_join(struct cw1200_common *priv)
+{
+ const u8 *bssid;
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct cfg80211_bss *bss = NULL;
+ struct wsm_protected_mgmt_policy mgmt_policy;
+ struct wsm_join join = {
+ .mode = conf->ibss_joined ?
+ WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+ .preamble_type = WSM_JOIN_PREAMBLE_LONG,
+ .probe_for_join = 1,
+ .atim_window = 0,
+ .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+ conf->basic_rates),
+ };
+ if (delayed_work_pending(&priv->join_timeout)) {
+ pr_warn("[STA] - Join request already pending, skipping..\n");
+ wsm_unlock_tx(priv);
+ return;
+ }
+
+ if (priv->join_status)
+ cw1200_do_unjoin(priv);
+
+ bssid = priv->vif->bss_conf.bssid;
+
+ bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
+
+ if (!bss && !conf->ibss_joined) {
+ wsm_unlock_tx(priv);
+ return;
+ }
+
+ mutex_lock(&priv->conf_mutex);
+
+ /* Under the conf lock: check scan status and
+ * bail out if it is in progress.
+ */
+ if (atomic_read(&priv->scan.in_progress)) {
+ wsm_unlock_tx(priv);
+ goto done_put;
+ }
+
+ priv->join_pending = true;
+
+ /* Sanity check basic rates */
+ if (!join.basic_rate_set)
+ join.basic_rate_set = 7;
+
+ /* Sanity check beacon interval */
+ if (!priv->beacon_int)
+ priv->beacon_int = 1;
+
+ join.beacon_interval = priv->beacon_int;
+
+ /* BT Coex related changes */
+ if (priv->bt_present) {
+ if (((priv->conf_listen_interval * 100) %
+ priv->beacon_int) == 0)
+ priv->listen_interval =
+ ((priv->conf_listen_interval * 100) /
+ priv->beacon_int);
+ else
+ priv->listen_interval =
+ ((priv->conf_listen_interval * 100) /
+ priv->beacon_int + 1);
+ }
+
+ if (priv->hw->conf.ps_dtim_period)
+ priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
+ join.dtim_period = priv->join_dtim_period;
+
+ join.channel_number = priv->channel->hw_value;
+ join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+
+ memcpy(join.bssid, bssid, sizeof(join.bssid));
+
+ pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
+ join.bssid,
+ join.dtim_period, priv->beacon_int);
+
+ if (!conf->ibss_joined) {
+ const u8 *ssidie;
+ rcu_read_lock();
+ ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ssidie) {
+ join.ssid_len = ssidie[1];
+ memcpy(join.ssid, &ssidie[2], join.ssid_len);
+ }
+ rcu_read_unlock();
+ }
+
+ if (priv->vif->p2p) {
+ join.flags |= WSM_JOIN_FLAGS_P2P_GO;
+ join.basic_rate_set =
+ cw1200_rate_mask_to_wsm(priv, 0xFF0);
+ }
+
+ /* Enable asynchronous join calls */
+ if (!conf->ibss_joined) {
+ join.flags |= WSM_JOIN_FLAGS_FORCE;
+ join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
+ }
+
+ wsm_flush_tx(priv);
+
+ /* Stay Awake for Join and Auth Timeouts and a bit more */
+ cw1200_pm_stay_awake(&priv->pm_state,
+ CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
+
+ cw1200_update_listening(priv, false);
+
+ /* Turn on Block ACKs */
+ wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
+ priv->ba_rx_tid_mask);
+
+ /* Set up timeout */
+ if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
+ priv->join_status = CW1200_JOIN_STATUS_JOINING;
+ queue_delayed_work(priv->workqueue,
+ &priv->join_timeout,
+ CW1200_JOIN_TIMEOUT);
+ }
+
+ /* 802.11w protected mgmt frames */
+ mgmt_policy.protectedMgmtEnable = 0;
+ mgmt_policy.unprotectedMgmtFramesAllowed = 1;
+ mgmt_policy.encryptionForAuthFrame = 1;
+ wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
+
+ /* Perform actual join */
+ if (wsm_join(priv, &join)) {
+ pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cw1200_update_listening(priv, priv->listening);
+ /* Tx lock still held, unjoin will clear it. */
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else {
+ if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
+ cw1200_join_complete(priv); /* Will clear tx_lock */
+
+ /* Upload keys */
+ cw1200_upload_keys(priv);
+
+ /* Due to beacon filtering it is possible that the
+ * AP's beacon is not known for the mac80211 stack.
+ * Disable filtering temporary to make sure the stack
+ * receives at least one
+ */
+ priv->disable_beacon_filter = true;
+ }
+ cw1200_update_filtering(priv);
+
+done_put:
+ mutex_unlock(&priv->conf_mutex);
+ if (bss)
+ cfg80211_put_bss(priv->hw->wiphy, bss);
+}
+
+void cw1200_join_timeout(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_timeout.work);
+ pr_debug("[WSM] Join timed out.\n");
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+}
+
+static void cw1200_do_unjoin(struct cw1200_common *priv)
+{
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+
+ cancel_delayed_work_sync(&priv->join_timeout);
+
+ mutex_lock(&priv->conf_mutex);
+ priv->join_pending = false;
+
+ if (atomic_read(&priv->scan.in_progress)) {
+ if (priv->delayed_unjoin)
+ wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
+ else
+ priv->delayed_unjoin = true;
+ goto done;
+ }
+
+ priv->delayed_link_loss = false;
+
+ if (!priv->join_status)
+ goto done;
+
+ if (priv->join_status == CW1200_JOIN_STATUS_AP)
+ goto done;
+
+ cancel_work_sync(&priv->update_filtering_work);
+ cancel_work_sync(&priv->set_beacon_wakeup_period_work);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+
+ /* Unjoin is a reset. */
+ wsm_flush_tx(priv);
+ wsm_keep_alive_period(priv, 0);
+ wsm_reset(priv, &reset);
+ wsm_set_output_power(priv, priv->output_power * 10);
+ priv->join_dtim_period = 0;
+ cw1200_setup_mac(priv);
+ cw1200_free_event_queue(priv);
+ cancel_work_sync(&priv->event_handler);
+ cw1200_update_listening(priv, priv->listening);
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+
+ /* Disable Block ACKs */
+ wsm_set_block_ack_policy(priv, 0, 0);
+
+ priv->disable_beacon_filter = false;
+ cw1200_update_filtering(priv);
+ memset(&priv->association_mode, 0,
+ sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ priv->setbssparams_done = false;
+ memset(&priv->firmware_ps_mode, 0,
+ sizeof(priv->firmware_ps_mode));
+
+ pr_debug("[STA] Unjoin completed.\n");
+
+done:
+ mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_unjoin_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, unjoin_work);
+
+ cw1200_do_unjoin(priv);
+
+ /* Tell the stack we're dead */
+ ieee80211_connection_loss(priv->vif);
+
+ wsm_unlock_tx(priv);
+}
+
+int cw1200_enable_listening(struct cw1200_common *priv)
+{
+ struct wsm_start start = {
+ .mode = WSM_START_MODE_P2P_DEV,
+ .band = WSM_PHY_BAND_2_4G,
+ .beacon_interval = 100,
+ .dtim_period = 1,
+ .probe_delay = 0,
+ .basic_rate_set = 0x0F,
+ };
+
+ if (priv->channel) {
+ start.band = priv->channel->band == IEEE80211_BAND_5GHZ ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+ start.channel_number = priv->channel->hw_value;
+ } else {
+ start.band = WSM_PHY_BAND_2_4G;
+ start.channel_number = 1;
+ }
+
+ return wsm_start(priv, &start);
+}
+
+int cw1200_disable_listening(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ ret = wsm_reset(priv, &reset);
+ return ret;
+}
+
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
+{
+ if (enabled) {
+ if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
+ if (!cw1200_enable_listening(priv))
+ priv->join_status = CW1200_JOIN_STATUS_MONITOR;
+ wsm_set_probe_responder(priv, true);
+ }
+ } else {
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+ if (!cw1200_disable_listening(priv))
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ wsm_set_probe_responder(priv, false);
+ }
+ }
+}
+
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg)
+{
+ int ret;
+ u16 uapsd_flags = 0;
+
+ /* Here's the mapping AC [queue, bit]
+ * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
+ */
+
+ if (arg->uapsd_enable[0])
+ uapsd_flags |= 1 << 3;
+
+ if (arg->uapsd_enable[1])
+ uapsd_flags |= 1 << 2;
+
+ if (arg->uapsd_enable[2])
+ uapsd_flags |= 1 << 1;
+
+ if (arg->uapsd_enable[3])
+ uapsd_flags |= 1;
+
+ /* Currently pseudo U-APSD operation is not supported, so setting
+ * MinAutoTriggerInterval, MaxAutoTriggerInterval and
+ * AutoTriggerStep to 0
+ */
+
+ priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
+ priv->uapsd_info.min_auto_trigger_interval = 0;
+ priv->uapsd_info.max_auto_trigger_interval = 0;
+ priv->uapsd_info.auto_trigger_step = 0;
+
+ ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
+ return ret;
+}
+
+/* ******************************************************************** */
+/* AP API */
+
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+ struct cw1200_link_entry *entry;
+ struct sk_buff *skb;
+
+ if (priv->mode != NL80211_IFTYPE_AP)
+ return 0;
+
+ sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
+ if (WARN_ON(!sta_priv->link_id)) {
+ wiphy_info(priv->hw->wiphy,
+ "[AP] No more link IDs available.\n");
+ return -ENOENT;
+ }
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+ priv->sta_asleep_mask |= BIT(sta_priv->link_id);
+ entry->status = CW1200_LINK_HARD;
+ while ((skb = skb_dequeue(&entry->rx_queue)))
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+ return 0;
+}
+
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+ struct cw1200_link_entry *entry;
+
+ if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
+ return 0;
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ entry->status = CW1200_LINK_RESERVE;
+ entry->timestamp = jiffies;
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ spin_unlock_bh(&priv->ps_state_lock);
+ flush_workqueue(priv->workqueue);
+ return 0;
+}
+
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ int link_id)
+{
+ struct cw1200_common *priv = dev->priv;
+ u32 bit, prev;
+
+ /* Zero link id means "for all link IDs" */
+ if (link_id)
+ bit = BIT(link_id);
+ else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
+ bit = 0;
+ else
+ bit = priv->link_id_map;
+ prev = priv->sta_asleep_mask & bit;
+
+ switch (notify_cmd) {
+ case STA_NOTIFY_SLEEP:
+ if (!prev) {
+ if (priv->buffered_multicasts &&
+ !priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ priv->sta_asleep_mask |= bit;
+ }
+ break;
+ case STA_NOTIFY_AWAKE:
+ if (prev) {
+ priv->sta_asleep_mask &= ~bit;
+ priv->pspoll_mask &= ~bit;
+ if (priv->tx_multicast && link_id &&
+ !priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_stop_work);
+ cw1200_bh_wakeup(priv);
+ }
+ break;
+ }
+}
+
+void cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+static void cw1200_ps_notify(struct cw1200_common *priv,
+ int link_id, bool ps)
+{
+ if (link_id > CW1200_MAX_STA_IN_AP_MODE)
+ return;
+
+ pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
+ ps ? "Stop" : "Start",
+ link_id, priv->sta_asleep_mask);
+
+ __cw1200_sta_notify(priv->hw, priv->vif,
+ ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
+}
+
+static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
+{
+ struct sk_buff *skb;
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ };
+ u16 tim_offset, tim_length;
+
+ pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
+
+ skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+ &tim_offset, &tim_length);
+ if (!skb) {
+ if (!__cw1200_flush(priv, true))
+ wsm_unlock_tx(priv);
+ return -ENOENT;
+ }
+
+ if (tim_offset && tim_length >= 6) {
+ /* Ignore DTIM count from mac80211:
+ * firmware handles DTIM internally.
+ */
+ skb->data[tim_offset + 2] = 0;
+
+ /* Set/reset aid0 bit */
+ if (aid0_bit_set)
+ skb->data[tim_offset + 4] |= 1;
+ else
+ skb->data[tim_offset + 4] &= ~1;
+ }
+
+ update_ie.ies = &skb->data[tim_offset];
+ update_ie.length = tim_length;
+ wsm_update_ie(priv, &update_ie);
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+void cw1200_set_tim_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, set_tim_work);
+ (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
+}
+
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set)
+{
+ struct cw1200_common *priv = dev->priv;
+ queue_work(priv->workqueue, &priv->set_tim_work);
+ return 0;
+}
+
+void cw1200_set_cts_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, set_cts_work);
+
+ u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ .ies = erp_ie,
+ .length = 3,
+ };
+ u32 erp_info;
+ __le32 use_cts_prot;
+ mutex_lock(&priv->conf_mutex);
+ erp_info = priv->erp_info;
+ mutex_unlock(&priv->conf_mutex);
+ use_cts_prot =
+ erp_info & WLAN_ERP_USE_PROTECTION ?
+ __cpu_to_le32(1) : 0;
+
+ erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
+
+ pr_debug("[STA] ERP information 0x%x\n", erp_info);
+
+ wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
+ &use_cts_prot, sizeof(use_cts_prot));
+ wsm_update_ie(priv, &update_ie);
+
+ return;
+}
+
+static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
+{
+ struct wsm_override_internal_txrate arg;
+ int ret = 0;
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ /* Plumb PSPOLL and NULL template */
+ cw1200_upload_pspoll(priv);
+ cw1200_upload_null(priv);
+ cw1200_upload_qosnull(priv);
+ } else {
+ return 0;
+ }
+
+ memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
+
+ if (!priv->vif->p2p) {
+ /* STATION mode */
+ if (priv->bss_params.operational_rate_set & ~0xF) {
+ pr_debug("[STA] STA has ERP rates\n");
+ /* G or BG mode */
+ arg.internalTxRate = (__ffs(
+ priv->bss_params.operational_rate_set & ~0xF));
+ } else {
+ pr_debug("[STA] STA has non ERP rates\n");
+ /* B only mode */
+ arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
+ }
+ arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
+ } else {
+ /* P2P mode */
+ arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+ arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+ }
+
+ pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
+ priv->mode,
+ arg.internalTxRate,
+ arg.nonErpInternalTxRate);
+
+ ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ &arg, sizeof(arg));
+
+ return ret;
+}
+
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct cw1200_common *priv = dev->priv;
+ bool do_join = false;
+
+ mutex_lock(&priv->conf_mutex);
+
+ pr_debug("BSS CHANGED: %08x\n", changed);
+
+ /* TODO: BSS_CHANGED_QOS */
+ /* TODO: BSS_CHANGED_TXPOWER */
+
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+ struct wsm_mib_arp_ipv4_filter filter = {0};
+ int i;
+
+ pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
+ info->arp_addr_cnt);
+
+ /* Currently only one IP address is supported by firmware.
+ * In case of more IPs arp filtering will be disabled.
+ */
+ if (info->arp_addr_cnt > 0 &&
+ info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
+ for (i = 0; i < info->arp_addr_cnt; i++) {
+ filter.ipv4addrs[i] = info->arp_addr_list[i];
+ pr_debug("[STA] addr[%d]: 0x%X\n",
+ i, filter.ipv4addrs[i]);
+ }
+ filter.enable = __cpu_to_le32(1);
+ }
+
+ pr_debug("[STA] arp ip filter enable: %d\n",
+ __le32_to_cpu(filter.enable));
+
+ wsm_set_arp_ipv4_filter(priv, &filter);
+ }
+
+ if (changed &
+ (BSS_CHANGED_BEACON |
+ BSS_CHANGED_AP_PROBE_RESP |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_SSID |
+ BSS_CHANGED_IBSS)) {
+ pr_debug("BSS_CHANGED_BEACON\n");
+ priv->beacon_int = info->beacon_int;
+ cw1200_update_beaconing(priv);
+ cw1200_upload_beacon(priv);
+ }
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
+ pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
+
+ if (priv->enable_beacon != info->enable_beacon) {
+ cw1200_enable_beaconing(priv, info->enable_beacon);
+ priv->enable_beacon = info->enable_beacon;
+ }
+ }
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ pr_debug("CHANGED_BEACON_INT\n");
+ if (info->ibss_joined)
+ do_join = true;
+ else if (priv->join_status == CW1200_JOIN_STATUS_AP)
+ cw1200_update_beaconing(priv);
+ }
+
+ /* assoc/disassoc, or maybe AID changed */
+ if (changed & BSS_CHANGED_ASSOC) {
+ wsm_lock_tx(priv);
+ priv->wep_default_key_id = -1;
+ wsm_unlock_tx(priv);
+ }
+
+ if (changed & BSS_CHANGED_BSSID) {
+ pr_debug("BSS_CHANGED_BSSID\n");
+ do_join = true;
+ }
+
+ if (changed &
+ (BSS_CHANGED_ASSOC |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_IBSS |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_HT)) {
+ pr_debug("BSS_CHANGED_ASSOC\n");
+ if (info->assoc) {
+ if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
+ ieee80211_connection_loss(vif);
+ mutex_unlock(&priv->conf_mutex);
+ return;
+ } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
+ priv->join_status = CW1200_JOIN_STATUS_STA;
+ }
+ } else {
+ do_join = true;
+ }
+
+ if (info->assoc || info->ibss_joined) {
+ struct ieee80211_sta *sta = NULL;
+ __le32 htprot = 0;
+
+ if (info->dtim_period)
+ priv->join_dtim_period = info->dtim_period;
+ priv->beacon_int = info->beacon_int;
+
+ rcu_read_lock();
+
+ if (info->bssid && !info->ibss_joined)
+ sta = ieee80211_find_sta(vif, info->bssid);
+ if (sta) {
+ priv->ht_info.ht_cap = sta->ht_cap;
+ priv->bss_params.operational_rate_set =
+ cw1200_rate_mask_to_wsm(priv,
+ sta->supp_rates[priv->channel->band]);
+ priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
+ priv->ht_info.operation_mode = info->ht_operation_mode;
+ } else {
+ memset(&priv->ht_info, 0,
+ sizeof(priv->ht_info));
+ priv->bss_params.operational_rate_set = -1;
+ }
+ rcu_read_unlock();
+
+ /* Non Greenfield stations present */
+ if (priv->ht_info.operation_mode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+ htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT);
+
+ /* Set HT protection method */
+ htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2);
+
+ /* TODO:
+ * STBC_param.dual_cts
+ * STBC_param.LSIG_TXOP_FILL
+ */
+
+ wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
+ &htprot, sizeof(htprot));
+
+ priv->association_mode.greenfield =
+ cw1200_ht_greenfield(&priv->ht_info);
+ priv->association_mode.flags =
+ WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
+ WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
+ WSM_ASSOCIATION_MODE_USE_HT_MODE |
+ WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
+ WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
+ priv->association_mode.preamble =
+ info->use_short_preamble ?
+ WSM_JOIN_PREAMBLE_SHORT :
+ WSM_JOIN_PREAMBLE_LONG;
+ priv->association_mode.basic_rate_set = __cpu_to_le32(
+ cw1200_rate_mask_to_wsm(priv,
+ info->basic_rates));
+ priv->association_mode.mpdu_start_spacing =
+ cw1200_ht_ampdu_density(&priv->ht_info);
+
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+
+ priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
+ priv->bss_params.aid = info->aid;
+
+ if (priv->join_dtim_period < 1)
+ priv->join_dtim_period = 1;
+
+ pr_debug("[STA] DTIM %d, interval: %d\n",
+ priv->join_dtim_period, priv->beacon_int);
+ pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
+ priv->association_mode.preamble,
+ priv->association_mode.greenfield,
+ priv->bss_params.aid,
+ priv->bss_params.operational_rate_set,
+ priv->association_mode.basic_rate_set);
+ wsm_set_association_mode(priv, &priv->association_mode);
+
+ if (!info->ibss_joined) {
+ wsm_keep_alive_period(priv, 30 /* sec */);
+ wsm_set_bss_params(priv, &priv->bss_params);
+ priv->setbssparams_done = true;
+ cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+ if (priv->vif->p2p) {
+ pr_debug("[STA] Setting p2p powersave configuration.\n");
+ wsm_set_p2p_ps_modeinfo(priv,
+ &priv->p2p_ps_modeinfo);
+ }
+ if (priv->bt_present)
+ cw1200_set_btcoexinfo(priv);
+ } else {
+ memset(&priv->association_mode, 0,
+ sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ }
+ }
+
+ /* ERP Protection */
+ if (changed & (BSS_CHANGED_ASSOC |
+ BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE)) {
+ u32 prev_erp_info = priv->erp_info;
+ if (info->use_cts_prot)
+ priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+ else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
+ priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+
+ if (info->use_short_preamble)
+ priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
+ else
+ priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
+
+ pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
+
+ if (prev_erp_info != priv->erp_info)
+ queue_work(priv->workqueue, &priv->set_cts_work);
+ }
+
+ /* ERP Slottime */
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
+ __le32 slot_time = info->use_short_slot ?
+ __cpu_to_le32(9) : __cpu_to_le32(20);
+ pr_debug("[STA] Slot time: %d us.\n",
+ __le32_to_cpu(slot_time));
+ wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
+ &slot_time, sizeof(slot_time));
+ }
+
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rollingAverageCount = 8,
+ };
+ pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
+ info->cqm_rssi_thold, info->cqm_rssi_hyst);
+ priv->cqm_rssi_thold = info->cqm_rssi_thold;
+ priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
+
+ if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
+ /* RSSI subscription enabled */
+ /* TODO: It's not a correct way of setting threshold.
+ * Upper and lower must be set equal here and adjusted
+ * in callback. However current implementation is much
+ * more relaible and stable.
+ */
+
+ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+ * RSSI = RCPI / 2 - 110
+ */
+ if (priv->cqm_use_rssi) {
+ threshold.upperThreshold =
+ info->cqm_rssi_thold + info->cqm_rssi_hyst;
+ threshold.lowerThreshold =
+ info->cqm_rssi_thold;
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+ } else {
+ threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
+ threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
+ }
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
+ } else {
+ /* There is a bug in FW, see sta.c. We have to enable
+ * dummy subscription to get correct RSSI values.
+ */
+ threshold.rssiRcpiMode |=
+ WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER;
+ if (priv->cqm_use_rssi)
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+ }
+ wsm_set_rcpi_rssi_threshold(priv, &threshold);
+ }
+ mutex_unlock(&priv->conf_mutex);
+
+ if (do_join) {
+ wsm_lock_tx(priv);
+ cw1200_do_join(priv); /* Will unlock it for us */
+ }
+}
+
+void cw1200_multicast_start_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_start_work);
+ long tmo = priv->join_dtim_period *
+ (priv->beacon_int + 20) * HZ / 1024;
+
+ cancel_work_sync(&priv->multicast_stop_work);
+
+ if (!priv->aid0_bit_set) {
+ wsm_lock_tx(priv);
+ cw1200_set_tim_impl(priv, true);
+ priv->aid0_bit_set = true;
+ mod_timer(&priv->mcast_timeout, jiffies + tmo);
+ wsm_unlock_tx(priv);
+ }
+}
+
+void cw1200_multicast_stop_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_stop_work);
+
+ if (priv->aid0_bit_set) {
+ del_timer_sync(&priv->mcast_timeout);
+ wsm_lock_tx(priv);
+ priv->aid0_bit_set = false;
+ cw1200_set_tim_impl(priv, false);
+ wsm_unlock_tx(priv);
+ }
+}
+
+void cw1200_mcast_timeout(unsigned long arg)
+{
+ struct cw1200_common *priv =
+ (struct cw1200_common *)arg;
+
+ wiphy_warn(priv->hw->wiphy,
+ "Multicast delivery timeout.\n");
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->tx_multicast = priv->aid0_bit_set &&
+ priv->buffered_multicasts;
+ if (priv->tx_multicast)
+ cw1200_bh_wakeup(priv);
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size, bool amsdu)
+{
+ /* Aggregation is implemented fully in firmware,
+ * including block ack negotiation. Do not allow
+ * mac80211 stack to do anything: it interferes with
+ * the firmware.
+ */
+
+ /* Note that we still need this function stubbed. */
+ return -ENOTSUPP;
+}
+
+/* ******************************************************************** */
+/* WSM callback */
+void cw1200_suspend_resume(struct cw1200_common *priv,
+ struct wsm_suspend_resume *arg)
+{
+ pr_debug("[AP] %s: %s\n",
+ arg->stop ? "stop" : "start",
+ arg->multicast ? "broadcast" : "unicast");
+
+ if (arg->multicast) {
+ bool cancel_tmo = false;
+ spin_lock_bh(&priv->ps_state_lock);
+ if (arg->stop) {
+ priv->tx_multicast = false;
+ } else {
+ /* Firmware sends this indication every DTIM if there
+ * is a STA in powersave connected. There is no reason
+ * to suspend, following wakeup will consume much more
+ * power than it could be saved.
+ */
+ cw1200_pm_stay_awake(&priv->pm_state,
+ priv->join_dtim_period *
+ (priv->beacon_int + 20) * HZ / 1024);
+ priv->tx_multicast = (priv->aid0_bit_set &&
+ priv->buffered_multicasts);
+ if (priv->tx_multicast) {
+ cancel_tmo = true;
+ cw1200_bh_wakeup(priv);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (cancel_tmo)
+ del_timer_sync(&priv->mcast_timeout);
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ cw1200_ps_notify(priv, arg->link_id, arg->stop);
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (!arg->stop)
+ cw1200_bh_wakeup(priv);
+ }
+ return;
+}
+
+/* ******************************************************************** */
+/* AP privates */
+
+static int cw1200_upload_beacon(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct ieee80211_mgmt *mgmt;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_BEACON,
+ };
+
+ u16 tim_offset;
+ u16 tim_len;
+
+ if (priv->mode == NL80211_IFTYPE_STATION ||
+ priv->mode == NL80211_IFTYPE_MONITOR ||
+ priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ goto done;
+
+ if (priv->vif->p2p)
+ frame.rate = WSM_TRANSMIT_RATE_6;
+
+ frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+ &tim_offset, &tim_len);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ if (ret)
+ goto done;
+
+ /* TODO: Distill probe resp; remove TIM
+ * and any other beacon-specific IEs
+ */
+ mgmt = (void *)frame.skb->data;
+ mgmt->frame_control =
+ __cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+
+ frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
+ if (priv->vif->p2p) {
+ ret = wsm_set_probe_responder(priv, true);
+ } else {
+ ret = wsm_set_template_frame(priv, &frame);
+ wsm_set_probe_responder(priv, false);
+ }
+
+done:
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_pspoll(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PS_POLL,
+ .rate = 0xFF,
+ };
+
+
+ frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_null(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_NULL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_qosnull(struct cw1200_common *priv)
+{
+ /* TODO: This needs to be implemented
+
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_QOS_NULL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ */
+ return 0;
+}
+
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+ bool enable)
+{
+ struct wsm_beacon_transmit transmit = {
+ .enable_beaconing = enable,
+ };
+
+ return wsm_beacon_transmit(priv, &transmit);
+}
+
+static int cw1200_start_ap(struct cw1200_common *priv)
+{
+ int ret;
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct wsm_start start = {
+ .mode = priv->vif->p2p ?
+ WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
+ .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+ .channel_number = priv->channel->hw_value,
+ .beacon_interval = conf->beacon_int,
+ .dtim_period = conf->dtim_period,
+ .preamble = conf->use_short_preamble ?
+ WSM_JOIN_PREAMBLE_SHORT :
+ WSM_JOIN_PREAMBLE_LONG,
+ .probe_delay = 100,
+ .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+ conf->basic_rates),
+ };
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ memset(start.ssid, 0, sizeof(start.ssid));
+ if (!conf->hidden_ssid) {
+ start.ssid_len = conf->ssid_len;
+ memcpy(start.ssid, conf->ssid, start.ssid_len);
+ }
+
+ priv->beacon_int = conf->beacon_int;
+ priv->join_dtim_period = conf->dtim_period;
+
+ memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
+
+ pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
+ start.channel_number, start.band,
+ start.beacon_interval, start.dtim_period,
+ start.basic_rate_set,
+ start.ssid_len, start.ssid);
+ ret = wsm_start(priv, &start);
+ if (!ret)
+ ret = cw1200_upload_keys(priv);
+ if (!ret && priv->vif->p2p) {
+ pr_debug("[AP] Setting p2p powersave configuration.\n");
+ wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
+ }
+ if (!ret) {
+ wsm_set_block_ack_policy(priv, 0, 0);
+ priv->join_status = CW1200_JOIN_STATUS_AP;
+ cw1200_update_filtering(priv);
+ }
+ wsm_set_operational_mode(priv, &mode);
+ return ret;
+}
+
+static int cw1200_update_beaconing(struct cw1200_common *priv)
+{
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct wsm_reset reset = {
+ .link_id = 0,
+ .reset_statistics = true,
+ };
+
+ if (priv->mode == NL80211_IFTYPE_AP) {
+ /* TODO: check if changed channel, band */
+ if (priv->join_status != CW1200_JOIN_STATUS_AP ||
+ priv->beacon_int != conf->beacon_int) {
+ pr_debug("ap restarting\n");
+ wsm_lock_tx(priv);
+ if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
+ wsm_reset(priv, &reset);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ cw1200_start_ap(priv);
+ wsm_unlock_tx(priv);
+ } else
+ pr_debug("ap started join_status: %d\n",
+ priv->join_status);
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/st/cw1200/sta.h
index bebb3379017f..bebb3379017f 100644
--- a/drivers/net/wireless/cw1200/sta.h
+++ b/drivers/net/wireless/st/cw1200/sta.h
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c
index d28bd49cb5fd..d28bd49cb5fd 100644
--- a/drivers/net/wireless/cw1200/txrx.c
+++ b/drivers/net/wireless/st/cw1200/txrx.c
diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/st/cw1200/txrx.h
index 492a4e14213b..492a4e14213b 100644
--- a/drivers/net/wireless/cw1200/txrx.h
+++ b/drivers/net/wireless/st/cw1200/txrx.h
diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/st/cw1200/wsm.c
index 9e0ca3048657..9e0ca3048657 100644
--- a/drivers/net/wireless/cw1200/wsm.c
+++ b/drivers/net/wireless/st/cw1200/wsm.c
diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/st/cw1200/wsm.h
index 48086e849515..48086e849515 100644
--- a/drivers/net/wireless/cw1200/wsm.h
+++ b/drivers/net/wireless/st/cw1200/wsm.h
diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig
index cbe1e7fef61b..92fbd6597e34 100644
--- a/drivers/net/wireless/ti/Kconfig
+++ b/drivers/net/wireless/ti/Kconfig
@@ -1,11 +1,15 @@
-menuconfig WL_TI
- bool "TI Wireless LAN support"
+config WLAN_VENDOR_TI
+ bool "Texas Instrument devices"
+ default y
---help---
- This section contains support for all the wireless drivers
- for Texas Instruments WLAN chips, such as wl1251 and the wl12xx
- family.
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
-if WL_TI
+if WLAN_VENDOR_TI
source "drivers/net/wireless/ti/wl1251/Kconfig"
source "drivers/net/wireless/ti/wl12xx/Kconfig"
source "drivers/net/wireless/ti/wl18xx/Kconfig"
@@ -21,4 +25,4 @@ config WILINK_PLATFORM_DATA
Small platform data bit needed to pass data to the sdio modules.
-endif # WL_TI
+endif # WLAN_VENDOR_TI
diff --git a/drivers/net/wireless/ti/wl1251/Kconfig b/drivers/net/wireless/ti/wl1251/Kconfig
index 477a206c098e..7142ccf3a425 100644
--- a/drivers/net/wireless/ti/wl1251/Kconfig
+++ b/drivers/net/wireless/ti/wl1251/Kconfig
@@ -1,4 +1,4 @@
-menuconfig WL1251
+config WL1251
tristate "TI wl1251 driver support"
depends on MAC80211
select FW_LOADER
diff --git a/drivers/net/wireless/ti/wl12xx/conf.h b/drivers/net/wireless/ti/wl12xx/conf.h
index 75e29897a0f5..a606ba9ef041 100644
--- a/drivers/net/wireless/ti/wl12xx/conf.h
+++ b/drivers/net/wireless/ti/wl12xx/conf.h
@@ -47,4 +47,237 @@ struct wl12xx_priv_conf {
struct conf_memory_settings mem_wl127x;
};
+enum wl12xx_sg_params {
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT master basic rate
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR = 0,
+ WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT slave basic rate
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR,
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT master EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR,
+ WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT slave EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR,
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR,
+
+ /*
+ * The maximum time WLAN can gain the antenna
+ * in WLAN PSM / BT master/slave BR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR,
+ WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR,
+
+ /*
+ * The maximum time WLAN can gain the antenna
+ * in WLAN PSM / BT master/slave EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR,
+
+ /* TODO: explain these values */
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR,
+
+ WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR,
+ WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR,
+
+ /*
+ * Compensation percentage of probe requests when scan initiated
+ * during BT voice/ACL link.
+ *
+ * Range: 0 - 255 (%)
+ */
+ WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ,
+
+ /*
+ * Compensation percentage of probe requests when active scan initiated
+ * during BT voice
+ *
+ * Range: 0 - 255 (%)
+ */
+ WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
+
+ /*
+ * Compensation percentage of WLAN active scan window if initiated
+ * during BT A2DP
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT A2DP BR
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT A2DP EDR
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT voice
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3,
+
+ /* TODO: explain these values */
+ WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN,
+ WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN,
+ WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN,
+
+ /*
+ * Defines whether the SG will force WLAN host to enter/exit PSM
+ *
+ * Range: 1 - SG can force, 0 - host handles PSM
+ */
+ WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO,
+
+ /*
+ * Defines antenna configuration (single/dual antenna)
+ *
+ * Range: 0 - single antenna, 1 - dual antenna
+ */
+ WL12XX_CONF_SG_ANTENNA_CONFIGURATION,
+
+ /*
+ * The threshold (percent) of max consecutive beacon misses before
+ * increasing priority of beacon reception.
+ *
+ * Range: 0 - 100 (%)
+ */
+ WL12XX_CONF_SG_BEACON_MISS_PERCENT,
+
+ /*
+ * Protection time of the DHCP procedure.
+ *
+ * Range: 0 - 100000 (ms)
+ */
+ WL12XX_CONF_SG_DHCP_TIME,
+
+ /*
+ * RX guard time before the beginning of a new BT voice frame during
+ * which no new WLAN trigger frame is transmitted.
+ *
+ * Range: 0 - 100000 (us)
+ */
+ WL12XX_CONF_SG_RXT,
+
+ /*
+ * TX guard time before the beginning of a new BT voice frame during
+ * which no new WLAN frame is transmitted.
+ *
+ * Range: 0 - 100000 (us)
+ */
+ WL12XX_CONF_SG_TXT,
+
+ /*
+ * Enable adaptive RXT/TXT algorithm. If disabled, the host values
+ * will be utilized.
+ *
+ * Range: 0 - disable, 1 - enable
+ */
+ WL12XX_CONF_SG_ADAPTIVE_RXT_TXT,
+
+ /* TODO: explain this value */
+ WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP,
+
+ /*
+ * Number of consecutive BT voice frames not interrupted by WLAN
+ *
+ * Range: 0 - 100
+ */
+ WL12XX_CONF_SG_HV3_MAX_SERVED,
+
+ /*
+ * The used WLAN legacy service period during active BT ACL link
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_PS_POLL_TIMEOUT,
+
+ /*
+ * The used WLAN UPSD service period during active BT ACL link
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_UPSD_TIMEOUT,
+
+ WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD,
+ WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM,
+ WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME,
+
+ /* AP params */
+ WL12XX_CONF_AP_BEACON_MISS_TX,
+ WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON,
+ WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL,
+ WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME,
+ WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME,
+ WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME,
+
+ /* CTS Diluting params */
+ WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
+ WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
+
+ WL12XX_CONF_SG_TEMP_PARAM_1,
+ WL12XX_CONF_SG_TEMP_PARAM_2,
+ WL12XX_CONF_SG_TEMP_PARAM_3,
+ WL12XX_CONF_SG_TEMP_PARAM_4,
+ WL12XX_CONF_SG_TEMP_PARAM_5,
+ WL12XX_CONF_SG_TEMP_PARAM_6,
+ WL12XX_CONF_SG_TEMP_PARAM_7,
+ WL12XX_CONF_SG_TEMP_PARAM_8,
+ WL12XX_CONF_SG_TEMP_PARAM_9,
+ WL12XX_CONF_SG_TEMP_PARAM_10,
+
+ WL12XX_CONF_SG_PARAMS_MAX,
+ WL12XX_CONF_SG_PARAMS_ALL = 0xff
+};
+
#endif /* __WL12XX_CONF_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index af0fe2e17151..a0d6cccc56f3 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -39,6 +39,7 @@
#include "scan.h"
#include "event.h"
#include "debugfs.h"
+#include "conf.h"
static char *fref_param;
static char *tcxo_param;
@@ -46,69 +47,69 @@ static char *tcxo_param;
static struct wlcore_conf wl12xx_conf = {
.sg = {
.params = {
- [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
- [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
- [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
- [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
- [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
- [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
+ [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
/* active scan params */
- [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
+ [WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+ [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+ [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
/* passive scan params */
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR] = 800,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR] = 200,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3] = 200,
/* passive scan in dual antenna params */
- [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN] = 0,
/* general params */
- [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
- [CONF_SG_ANTENNA_CONFIGURATION] = 0,
- [CONF_SG_BEACON_MISS_PERCENT] = 60,
- [CONF_SG_DHCP_TIME] = 5000,
- [CONF_SG_RXT] = 1200,
- [CONF_SG_TXT] = 1000,
- [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
- [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
- [CONF_SG_HV3_MAX_SERVED] = 6,
- [CONF_SG_PS_POLL_TIMEOUT] = 10,
- [CONF_SG_UPSD_TIMEOUT] = 10,
- [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
- [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
- [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
+ [WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+ [WL12XX_CONF_SG_ANTENNA_CONFIGURATION] = 0,
+ [WL12XX_CONF_SG_BEACON_MISS_PERCENT] = 60,
+ [WL12XX_CONF_SG_DHCP_TIME] = 5000,
+ [WL12XX_CONF_SG_RXT] = 1200,
+ [WL12XX_CONF_SG_TXT] = 1000,
+ [WL12XX_CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+ [WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+ [WL12XX_CONF_SG_HV3_MAX_SERVED] = 6,
+ [WL12XX_CONF_SG_PS_POLL_TIMEOUT] = 10,
+ [WL12XX_CONF_SG_UPSD_TIMEOUT] = 10,
+ [WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+ [WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
+ [WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
/* AP params */
- [CONF_AP_BEACON_MISS_TX] = 3,
- [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
- [CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
- [CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
- [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
- [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
+ [WL12XX_CONF_AP_BEACON_MISS_TX] = 3,
+ [WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
+ [WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
+ [WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
+ [WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
+ [WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
/* CTS Diluting params */
- [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
- [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+ [WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+ [WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
},
.state = CONF_SG_PROTECTIVE,
},
@@ -1809,6 +1810,7 @@ static int wl12xx_setup(struct wl1271 *wl)
BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS);
BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS);
+ BUILD_BUG_ON(WL12XX_CONF_SG_PARAMS_MAX > WLCORE_CONF_SG_PARAMS_MAX);
wl->rtable = wl12xx_rtable;
wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
index 71f1ec448ba5..7aa880f14ccb 100644
--- a/drivers/net/wireless/ti/wl18xx/conf.h
+++ b/drivers/net/wireless/ti/wl18xx/conf.h
@@ -139,4 +139,94 @@ struct wl18xx_priv_conf {
struct conf_ap_sleep_settings ap_sleep;
} __packed;
+enum wl18xx_sg_params {
+ WL18XX_CONF_SG_PARAM_0 = 0,
+
+ /* Configuration Parameters */
+ WL18XX_CONF_SG_ANTENNA_CONFIGURATION,
+ WL18XX_CONF_SG_ZIGBEE_COEX,
+ WL18XX_CONF_SG_TIME_SYNC,
+
+ WL18XX_CONF_SG_PARAM_4,
+ WL18XX_CONF_SG_PARAM_5,
+ WL18XX_CONF_SG_PARAM_6,
+ WL18XX_CONF_SG_PARAM_7,
+ WL18XX_CONF_SG_PARAM_8,
+ WL18XX_CONF_SG_PARAM_9,
+ WL18XX_CONF_SG_PARAM_10,
+ WL18XX_CONF_SG_PARAM_11,
+ WL18XX_CONF_SG_PARAM_12,
+ WL18XX_CONF_SG_PARAM_13,
+ WL18XX_CONF_SG_PARAM_14,
+ WL18XX_CONF_SG_PARAM_15,
+ WL18XX_CONF_SG_PARAM_16,
+ WL18XX_CONF_SG_PARAM_17,
+ WL18XX_CONF_SG_PARAM_18,
+ WL18XX_CONF_SG_PARAM_19,
+ WL18XX_CONF_SG_PARAM_20,
+ WL18XX_CONF_SG_PARAM_21,
+ WL18XX_CONF_SG_PARAM_22,
+ WL18XX_CONF_SG_PARAM_23,
+ WL18XX_CONF_SG_PARAM_24,
+ WL18XX_CONF_SG_PARAM_25,
+
+ /* Active Scan Parameters */
+ WL18XX_CONF_SG_AUTO_SCAN_PROBE_REQ,
+ WL18XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
+
+ WL18XX_CONF_SG_PARAM_28,
+
+ /* Passive Scan Parameters */
+ WL18XX_CONF_SG_PARAM_29,
+ WL18XX_CONF_SG_PARAM_30,
+ WL18XX_CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3,
+
+ /* Passive Scan in Dual Antenna Parameters */
+ WL18XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN,
+ WL18XX_CONF_SG_BEACON_HV3_COLL_TH_IN_PASSIVE_SCAN,
+ WL18XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN,
+
+ /* General Parameters */
+ WL18XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO,
+ WL18XX_CONF_SG_PARAM_36,
+ WL18XX_CONF_SG_BEACON_MISS_PERCENT,
+ WL18XX_CONF_SG_PARAM_38,
+ WL18XX_CONF_SG_RXT,
+ WL18XX_CONF_SG_UNUSED,
+ WL18XX_CONF_SG_ADAPTIVE_RXT_TXT,
+ WL18XX_CONF_SG_GENERAL_USAGE_BIT_MAP,
+ WL18XX_CONF_SG_HV3_MAX_SERVED,
+ WL18XX_CONF_SG_PARAM_44,
+ WL18XX_CONF_SG_PARAM_45,
+ WL18XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD,
+ WL18XX_CONF_SG_GEMINI_PARAM_47,
+ WL18XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME,
+
+ /* AP Parameters */
+ WL18XX_CONF_SG_AP_BEACON_MISS_TX,
+ WL18XX_CONF_SG_PARAM_50,
+ WL18XX_CONF_SG_AP_BEACON_WINDOW_INTERVAL,
+ WL18XX_CONF_SG_AP_CONNECTION_PROTECTION_TIME,
+ WL18XX_CONF_SG_PARAM_53,
+ WL18XX_CONF_SG_PARAM_54,
+
+ /* CTS Diluting Parameters */
+ WL18XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
+ WL18XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
+
+ WL18XX_CONF_SG_TEMP_PARAM_1,
+ WL18XX_CONF_SG_TEMP_PARAM_2,
+ WL18XX_CONF_SG_TEMP_PARAM_3,
+ WL18XX_CONF_SG_TEMP_PARAM_4,
+ WL18XX_CONF_SG_TEMP_PARAM_5,
+ WL18XX_CONF_SG_TEMP_PARAM_6,
+ WL18XX_CONF_SG_TEMP_PARAM_7,
+ WL18XX_CONF_SG_TEMP_PARAM_8,
+ WL18XX_CONF_SG_TEMP_PARAM_9,
+ WL18XX_CONF_SG_TEMP_PARAM_10,
+
+ WL18XX_CONF_SG_PARAMS_MAX,
+ WL18XX_CONF_SG_PARAMS_ALL = 0xff
+};
+
#endif /* __WL18XX_CONF_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
index 09c7e098f460..719907a0a2c2 100644
--- a/drivers/net/wireless/ti/wl18xx/event.c
+++ b/drivers/net/wireless/ti/wl18xx/event.c
@@ -205,6 +205,8 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
mbox->sc_ssid,
mbox->sc_pwd_len,
mbox->sc_pwd);
+ if (vector & FW_LOGGER_INDICATION)
+ wlcore_event_fw_logger(wl);
return 0;
}
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
index f3d4f13379cb..070de1274694 100644
--- a/drivers/net/wireless/ti/wl18xx/event.h
+++ b/drivers/net/wireless/ti/wl18xx/event.h
@@ -41,6 +41,7 @@ enum {
SMART_CONFIG_SYNC_EVENT_ID = BIT(22),
SMART_CONFIG_DECODE_EVENT_ID = BIT(23),
TIME_SYNC_EVENT_ID = BIT(24),
+ FW_LOGGER_INDICATION = BIT(25),
};
enum wl18xx_radar_types {
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 50cce42089a5..1bf26cc7374e 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -177,69 +177,80 @@ enum wl18xx_hw_rates {
static struct wlcore_conf wl18xx_conf = {
.sg = {
.params = {
- [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
- [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
- [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
- [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
- [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
- [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
- /* active scan params */
- [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
- /* passive scan params */
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
- /* passive scan in dual antenna params */
- [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
- /* general params */
- [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
- [CONF_SG_ANTENNA_CONFIGURATION] = 0,
- [CONF_SG_BEACON_MISS_PERCENT] = 60,
- [CONF_SG_DHCP_TIME] = 5000,
- [CONF_SG_RXT] = 1200,
- [CONF_SG_TXT] = 1000,
- [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
- [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
- [CONF_SG_HV3_MAX_SERVED] = 6,
- [CONF_SG_PS_POLL_TIMEOUT] = 10,
- [CONF_SG_UPSD_TIMEOUT] = 10,
- [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
- [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
- [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
- /* AP params */
- [CONF_AP_BEACON_MISS_TX] = 3,
- [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
- [CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
- [CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
- [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
- [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
- /* CTS Diluting params */
- [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
- [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+ [WL18XX_CONF_SG_PARAM_0] = 0,
+ /* Configuartion Parameters */
+ [WL18XX_CONF_SG_ANTENNA_CONFIGURATION] = 0,
+ [WL18XX_CONF_SG_ZIGBEE_COEX] = 0,
+ [WL18XX_CONF_SG_TIME_SYNC] = 0,
+ [WL18XX_CONF_SG_PARAM_4] = 0,
+ [WL18XX_CONF_SG_PARAM_5] = 0,
+ [WL18XX_CONF_SG_PARAM_6] = 0,
+ [WL18XX_CONF_SG_PARAM_7] = 0,
+ [WL18XX_CONF_SG_PARAM_8] = 0,
+ [WL18XX_CONF_SG_PARAM_9] = 0,
+ [WL18XX_CONF_SG_PARAM_10] = 0,
+ [WL18XX_CONF_SG_PARAM_11] = 0,
+ [WL18XX_CONF_SG_PARAM_12] = 0,
+ [WL18XX_CONF_SG_PARAM_13] = 0,
+ [WL18XX_CONF_SG_PARAM_14] = 0,
+ [WL18XX_CONF_SG_PARAM_15] = 0,
+ [WL18XX_CONF_SG_PARAM_16] = 0,
+ [WL18XX_CONF_SG_PARAM_17] = 0,
+ [WL18XX_CONF_SG_PARAM_18] = 0,
+ [WL18XX_CONF_SG_PARAM_19] = 0,
+ [WL18XX_CONF_SG_PARAM_20] = 0,
+ [WL18XX_CONF_SG_PARAM_21] = 0,
+ [WL18XX_CONF_SG_PARAM_22] = 0,
+ [WL18XX_CONF_SG_PARAM_23] = 0,
+ [WL18XX_CONF_SG_PARAM_24] = 0,
+ [WL18XX_CONF_SG_PARAM_25] = 0,
+ /* Active Scan Parameters */
+ [WL18XX_CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+ [WL18XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+ [WL18XX_CONF_SG_PARAM_28] = 0,
+ /* Passive Scan Parameters */
+ [WL18XX_CONF_SG_PARAM_29] = 0,
+ [WL18XX_CONF_SG_PARAM_30] = 0,
+ [WL18XX_CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
+ /* Passive Scan in Dual Antenna Parameters */
+ [WL18XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+ [WL18XX_CONF_SG_BEACON_HV3_COLL_TH_IN_PASSIVE_SCAN] = 0,
+ [WL18XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN] = 0,
+ /* General Parameters */
+ [WL18XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+ [WL18XX_CONF_SG_PARAM_36] = 0,
+ [WL18XX_CONF_SG_BEACON_MISS_PERCENT] = 60,
+ [WL18XX_CONF_SG_PARAM_38] = 0,
+ [WL18XX_CONF_SG_RXT] = 1200,
+ [WL18XX_CONF_SG_UNUSED] = 0,
+ [WL18XX_CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+ [WL18XX_CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+ [WL18XX_CONF_SG_HV3_MAX_SERVED] = 6,
+ [WL18XX_CONF_SG_PARAM_44] = 0,
+ [WL18XX_CONF_SG_PARAM_45] = 0,
+ [WL18XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+ [WL18XX_CONF_SG_GEMINI_PARAM_47] = 0,
+ [WL18XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 0,
+ /* AP Parameters */
+ [WL18XX_CONF_SG_AP_BEACON_MISS_TX] = 3,
+ [WL18XX_CONF_SG_PARAM_50] = 0,
+ [WL18XX_CONF_SG_AP_BEACON_WINDOW_INTERVAL] = 2,
+ [WL18XX_CONF_SG_AP_CONNECTION_PROTECTION_TIME] = 30,
+ [WL18XX_CONF_SG_PARAM_53] = 0,
+ [WL18XX_CONF_SG_PARAM_54] = 0,
+ /* CTS Diluting Parameters */
+ [WL18XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+ [WL18XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_1] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_2] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_3] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_4] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_5] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_6] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_7] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_8] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_9] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_10] = 0,
},
.state = CONF_SG_PROTECTIVE,
},
@@ -461,7 +472,7 @@ static struct wlcore_conf wl18xx_conf = {
},
.fwlog = {
.mode = WL12XX_FWLOG_CONTINUOUS,
- .mem_blocks = 2,
+ .mem_blocks = 0,
.severity = 0,
.timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED,
.output = WL12XX_FWLOG_OUTPUT_DBG_PINS,
@@ -584,7 +595,7 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
.mem = { .start = 0x00A00000, .size = 0x00012000 },
.reg = { .start = 0x00807000, .size = 0x00005000 },
.mem2 = { .start = 0x00800000, .size = 0x0000B000 },
- .mem3 = { .start = 0x00000000, .size = 0x00000000 },
+ .mem3 = { .start = 0x00401594, .size = 0x00001020 },
},
[PART_DOWN] = {
.mem = { .start = 0x00000000, .size = 0x00014000 },
@@ -602,7 +613,7 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
.mem = { .start = 0x00800000, .size = 0x000050FC },
.reg = { .start = 0x00B00404, .size = 0x00001000 },
.mem2 = { .start = 0x00C00000, .size = 0x00000400 },
- .mem3 = { .start = 0x00000000, .size = 0x00000000 },
+ .mem3 = { .start = 0x00401594, .size = 0x00001020 },
},
[PART_PHY_INIT] = {
.mem = { .start = WL18XX_PHY_INIT_MEM_ADDR,
@@ -1029,7 +1040,8 @@ static int wl18xx_boot(struct wl1271 *wl)
DFS_CHANNELS_CONFIG_COMPLETE_EVENT |
SMART_CONFIG_SYNC_EVENT_ID |
SMART_CONFIG_DECODE_EVENT_ID |
- TIME_SYNC_EVENT_ID;
+ TIME_SYNC_EVENT_ID |
+ FW_LOGGER_INDICATION;
wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
@@ -1895,6 +1907,7 @@ static int wl18xx_setup(struct wl1271 *wl)
BUILD_BUG_ON(WL18XX_MAX_LINKS > WLCORE_MAX_LINKS);
BUILD_BUG_ON(WL18XX_MAX_AP_STATIONS > WL18XX_MAX_LINKS);
+ BUILD_BUG_ON(WL18XX_CONF_SG_PARAMS_MAX > WLCORE_CONF_SG_PARAMS_MAX);
wl->rtable = wl18xx_rtable;
wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig
index 7c099542b214..969c9d79bfc8 100644
--- a/drivers/net/wireless/ti/wlcore/Kconfig
+++ b/drivers/net/wireless/ti/wlcore/Kconfig
@@ -1,6 +1,6 @@
config WLCORE
tristate "TI wlcore support"
- depends on WL_TI && MAC80211
+ depends on MAC80211
select FW_LOADER
---help---
This module contains the main code for TI WLAN chips. It abstracts
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index f28fa3b5029d..26cc23f32241 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -534,9 +534,9 @@ int wl12xx_acx_sg_cfg(struct wl1271 *wl)
}
/* BT-WLAN coext parameters */
- for (i = 0; i < CONF_SG_PARAMS_MAX; i++)
+ for (i = 0; i < WLCORE_CONF_SG_PARAMS_MAX; i++)
param->params[i] = cpu_to_le32(c->params[i]);
- param->param_idx = CONF_SG_PARAMS_ALL;
+ param->param_idx = WLCORE_CONF_SG_PARAMS_ALL;
ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
if (ret < 0) {
diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h
index 954d57ec98f4..0d61fae88dcb 100644
--- a/drivers/net/wireless/ti/wlcore/acx.h
+++ b/drivers/net/wireless/ti/wlcore/acx.h
@@ -300,7 +300,7 @@ struct acx_bt_wlan_coex {
struct acx_bt_wlan_coex_param {
struct acx_header header;
- __le32 params[CONF_SG_PARAMS_MAX];
+ __le32 params[WLCORE_CONF_SG_PARAMS_MAX];
u8 param_idx;
u8 padding[3];
} __packed;
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 8dc46c0a489a..e28e2f2303ce 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -626,7 +626,6 @@ struct wl12xx_cmd_remove_peer {
*/
enum wl12xx_fwlogger_log_mode {
WL12XX_FWLOG_CONTINUOUS,
- WL12XX_FWLOG_ON_DEMAND
};
/* Include/exclude timestamps from the log messages */
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index 52a9d1b14020..44d898fe0afc 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -110,242 +110,11 @@ enum {
CONF_SG_OPPORTUNISTIC
};
-enum {
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT master basic rate
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_MASTER_MIN_BR = 0,
- CONF_SG_ACL_BT_MASTER_MAX_BR,
-
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT slave basic rate
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_SLAVE_MIN_BR,
- CONF_SG_ACL_BT_SLAVE_MAX_BR,
-
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT master EDR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_MASTER_MIN_EDR,
- CONF_SG_ACL_BT_MASTER_MAX_EDR,
-
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT slave EDR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_SLAVE_MIN_EDR,
- CONF_SG_ACL_BT_SLAVE_MAX_EDR,
-
- /*
- * The maximum time WLAN can gain the antenna
- * in WLAN PSM / BT master/slave BR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_WLAN_PS_MASTER_BR,
- CONF_SG_ACL_WLAN_PS_SLAVE_BR,
-
- /*
- * The maximum time WLAN can gain the antenna
- * in WLAN PSM / BT master/slave EDR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_WLAN_PS_MASTER_EDR,
- CONF_SG_ACL_WLAN_PS_SLAVE_EDR,
-
- /* TODO: explain these values */
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR,
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR,
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR,
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR,
-
- CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR,
- CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR,
- CONF_SG_ACL_PASSIVE_SCAN_BT_BR,
- CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR,
- CONF_SG_ACL_PASSIVE_SCAN_BT_EDR,
- CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR,
-
- /*
- * Compensation percentage of probe requests when scan initiated
- * during BT voice/ACL link.
- *
- * Range: 0 - 255 (%)
- */
- CONF_SG_AUTO_SCAN_PROBE_REQ,
-
- /*
- * Compensation percentage of probe requests when active scan initiated
- * during BT voice
- *
- * Range: 0 - 255 (%)
- */
- CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
-
- /*
- * Compensation percentage of WLAN active scan window if initiated
- * during BT A2DP
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP,
-
- /*
- * Compensation percentage of WLAN passive scan window if initiated
- * during BT A2DP BR
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR,
-
- /*
- * Compensation percentage of WLAN passive scan window if initiated
- * during BT A2DP EDR
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR,
-
- /*
- * Compensation percentage of WLAN passive scan window if initiated
- * during BT voice
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3,
-
- /* TODO: explain these values */
- CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN,
- CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN,
- CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN,
-
- /*
- * Defines whether the SG will force WLAN host to enter/exit PSM
- *
- * Range: 1 - SG can force, 0 - host handles PSM
- */
- CONF_SG_STA_FORCE_PS_IN_BT_SCO,
-
- /*
- * Defines antenna configuration (single/dual antenna)
- *
- * Range: 0 - single antenna, 1 - dual antenna
- */
- CONF_SG_ANTENNA_CONFIGURATION,
-
- /*
- * The threshold (percent) of max consecutive beacon misses before
- * increasing priority of beacon reception.
- *
- * Range: 0 - 100 (%)
- */
- CONF_SG_BEACON_MISS_PERCENT,
-
- /*
- * Protection time of the DHCP procedure.
- *
- * Range: 0 - 100000 (ms)
- */
- CONF_SG_DHCP_TIME,
-
- /*
- * RX guard time before the beginning of a new BT voice frame during
- * which no new WLAN trigger frame is transmitted.
- *
- * Range: 0 - 100000 (us)
- */
- CONF_SG_RXT,
-
- /*
- * TX guard time before the beginning of a new BT voice frame during
- * which no new WLAN frame is transmitted.
- *
- * Range: 0 - 100000 (us)
- */
-
- CONF_SG_TXT,
-
- /*
- * Enable adaptive RXT/TXT algorithm. If disabled, the host values
- * will be utilized.
- *
- * Range: 0 - disable, 1 - enable
- */
- CONF_SG_ADAPTIVE_RXT_TXT,
-
- /* TODO: explain this value */
- CONF_SG_GENERAL_USAGE_BIT_MAP,
-
- /*
- * Number of consecutive BT voice frames not interrupted by WLAN
- *
- * Range: 0 - 100
- */
- CONF_SG_HV3_MAX_SERVED,
-
- /*
- * The used WLAN legacy service period during active BT ACL link
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_PS_POLL_TIMEOUT,
-
- /*
- * The used WLAN UPSD service period during active BT ACL link
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_UPSD_TIMEOUT,
-
- CONF_SG_CONSECUTIVE_CTS_THRESHOLD,
- CONF_SG_STA_RX_WINDOW_AFTER_DTIM,
- CONF_SG_STA_CONNECTION_PROTECTION_TIME,
-
- /* AP params */
- CONF_AP_BEACON_MISS_TX,
- CONF_AP_RX_WINDOW_AFTER_BEACON,
- CONF_AP_BEACON_WINDOW_INTERVAL,
- CONF_AP_CONNECTION_PROTECTION_TIME,
- CONF_AP_BT_ACL_VAL_BT_SERVE_TIME,
- CONF_AP_BT_ACL_VAL_WL_SERVE_TIME,
-
- /* CTS Diluting params */
- CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
- CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
-
- CONF_SG_TEMP_PARAM_1,
- CONF_SG_TEMP_PARAM_2,
- CONF_SG_TEMP_PARAM_3,
- CONF_SG_TEMP_PARAM_4,
- CONF_SG_TEMP_PARAM_5,
- CONF_SG_TEMP_PARAM_6,
- CONF_SG_TEMP_PARAM_7,
- CONF_SG_TEMP_PARAM_8,
- CONF_SG_TEMP_PARAM_9,
- CONF_SG_TEMP_PARAM_10,
-
- CONF_SG_PARAMS_MAX,
- CONF_SG_PARAMS_ALL = 0xff
-};
+#define WLCORE_CONF_SG_PARAMS_MAX 67
+#define WLCORE_CONF_SG_PARAMS_ALL 0xff
struct conf_sg_settings {
- u32 params[CONF_SG_PARAMS_MAX];
+ u32 params[WLCORE_CONF_SG_PARAMS_MAX];
u8 state;
} __packed;
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index be72306f8c69..7f672f6879d0 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -1219,6 +1219,65 @@ static const struct file_operations dev_mem_ops = {
.llseek = dev_mem_seek,
};
+static ssize_t fw_logger_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+
+ return wl1271_format_buffer(user_buf, count,
+ ppos, "%d\n",
+ wl->conf.fwlog.output);
+}
+
+static ssize_t fw_logger_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul_from_user(user_buf, count, 0, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal value in fw_logger");
+ return -EINVAL;
+ }
+
+ if ((value > 2) || (value == 0)) {
+ wl1271_warning("fw_logger value must be 1-UART 2-SDIO");
+ return -ERANGE;
+ }
+
+ if (wl->conf.fwlog.output == 0) {
+ wl1271_warning("iligal opperation - fw logger disabled by default, please change mode via wlconf");
+ return -EINVAL;
+ }
+
+ mutex_lock(&wl->mutex);
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0) {
+ count = ret;
+ goto out;
+ }
+
+ wl->conf.fwlog.output = value;
+
+ ret = wl12xx_cmd_config_fwlog(wl);
+
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+static const struct file_operations fw_logger_ops = {
+ .open = simple_open,
+ .read = fw_logger_read,
+ .write = fw_logger_write,
+ .llseek = default_llseek,
+};
+
static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
@@ -1245,6 +1304,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(irq_timeout, rootdir);
DEBUGFS_ADD(fw_stats_raw, rootdir);
DEBUGFS_ADD(sleep_auth, rootdir);
+ DEBUGFS_ADD(fw_logger, rootdir);
streaming = debugfs_create_dir("rx_streaming", rootdir);
if (!streaming || IS_ERR(streaming))
diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index c42e78955e7b..c96405498bf4 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -28,6 +28,88 @@
#include "ps.h"
#include "scan.h"
#include "wl12xx_80211.h"
+#include "hw_ops.h"
+
+#define WL18XX_LOGGER_SDIO_BUFF_MAX (0x1020)
+#define WL18XX_DATA_RAM_BASE_ADDRESS (0x20000000)
+#define WL18XX_LOGGER_SDIO_BUFF_ADDR (0x40159c)
+#define WL18XX_LOGGER_BUFF_OFFSET (sizeof(struct fw_logger_information))
+#define WL18XX_LOGGER_READ_POINT_OFFSET (12)
+
+int wlcore_event_fw_logger(struct wl1271 *wl)
+{
+ u32 ret;
+ struct fw_logger_information fw_log;
+ u8 *buffer;
+ u32 internal_fw_addrbase = WL18XX_DATA_RAM_BASE_ADDRESS;
+ u32 addr = WL18XX_LOGGER_SDIO_BUFF_ADDR;
+ u32 end_buff_addr = WL18XX_LOGGER_SDIO_BUFF_ADDR +
+ WL18XX_LOGGER_BUFF_OFFSET;
+ u32 available_len;
+ u32 actual_len;
+ u32 clear_addr;
+ size_t len;
+ u32 start_loc;
+
+ buffer = kzalloc(WL18XX_LOGGER_SDIO_BUFF_MAX, GFP_KERNEL);
+ if (!buffer) {
+ wl1271_error("Fail to allocate fw logger memory");
+ fw_log.actual_buff_size = cpu_to_le32(0);
+ goto out;
+ }
+
+ ret = wlcore_read(wl, addr, buffer, WL18XX_LOGGER_SDIO_BUFF_MAX,
+ false);
+ if (ret < 0) {
+ wl1271_error("Fail to read logger buffer, error_id = %d",
+ ret);
+ fw_log.actual_buff_size = cpu_to_le32(0);
+ goto free_out;
+ }
+
+ memcpy(&fw_log, buffer, sizeof(fw_log));
+
+ if (le32_to_cpu(fw_log.actual_buff_size) == 0)
+ goto free_out;
+
+ actual_len = le32_to_cpu(fw_log.actual_buff_size);
+ start_loc = (le32_to_cpu(fw_log.buff_read_ptr) -
+ internal_fw_addrbase) - addr;
+ end_buff_addr += le32_to_cpu(fw_log.max_buff_size);
+ available_len = end_buff_addr -
+ (le32_to_cpu(fw_log.buff_read_ptr) -
+ internal_fw_addrbase);
+ actual_len = min(actual_len, available_len);
+ len = actual_len;
+
+ wl12xx_copy_fwlog(wl, &buffer[start_loc], len);
+ clear_addr = addr + start_loc + le32_to_cpu(fw_log.actual_buff_size) +
+ internal_fw_addrbase;
+
+ len = le32_to_cpu(fw_log.actual_buff_size) - len;
+ if (len) {
+ wl12xx_copy_fwlog(wl,
+ &buffer[WL18XX_LOGGER_BUFF_OFFSET],
+ len);
+ clear_addr = addr + WL18XX_LOGGER_BUFF_OFFSET + len +
+ internal_fw_addrbase;
+ }
+
+ /* double check that clear address and write pointer are the same */
+ if (clear_addr != le32_to_cpu(fw_log.buff_write_ptr)) {
+ wl1271_error("Calculate of clear addr Clear = %x, write = %x",
+ clear_addr, le32_to_cpu(fw_log.buff_write_ptr));
+ }
+
+ /* indicate FW about Clear buffer */
+ ret = wlcore_write32(wl, addr + WL18XX_LOGGER_READ_POINT_OFFSET,
+ fw_log.buff_write_ptr);
+free_out:
+ kfree(buffer);
+out:
+ return le32_to_cpu(fw_log.actual_buff_size);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_fw_logger);
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
{
diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h
index acc7a59d3828..75e8e98da2fe 100644
--- a/drivers/net/wireless/ti/wlcore/event.h
+++ b/drivers/net/wireless/ti/wlcore/event.h
@@ -64,6 +64,14 @@ enum {
#define NUM_OF_RSSI_SNR_TRIGGERS 8
+struct fw_logger_information {
+ __le32 max_buff_size;
+ __le32 actual_buff_size;
+ __le32 num_trace_drop;
+ __le32 buff_read_ptr;
+ __le32 buff_write_ptr;
+} __packed;
+
struct wl1271;
int wl1271_event_unmask(struct wl1271 *wl);
@@ -84,4 +92,5 @@ void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_roc_complete(struct wl1271 *wl);
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr);
+int wlcore_event_fw_logger(struct wl1271 *wl);
#endif
diff --git a/drivers/net/wireless/ti/wlcore/io.c b/drivers/net/wireless/ti/wlcore/io.c
index 68e74eefd296..9ac118e727e9 100644
--- a/drivers/net/wireless/ti/wlcore/io.c
+++ b/drivers/net/wireless/ti/wlcore/io.c
@@ -175,12 +175,13 @@ int wlcore_set_partition(struct wl1271 *wl,
if (ret < 0)
goto out;
- /*
- * We don't need the size of the last partition, as it is
- * automatically calculated based on the total memory size and
- * the sizes of the previous partitions.
- */
ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start);
+ if (ret < 0)
+ goto out;
+
+ ret = wlcore_raw_write32(wl, HW_PART3_SIZE_ADDR, p->mem3.size);
+ if (ret < 0)
+ goto out;
out:
return ret;
diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h
index 0305729d0986..6c257b54f415 100644
--- a/drivers/net/wireless/ti/wlcore/io.h
+++ b/drivers/net/wireless/ti/wlcore/io.h
@@ -36,8 +36,8 @@
#define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12)
#define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16)
#define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20)
-#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
-
+#define HW_PART3_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
+#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 28)
#define HW_ACCESS_REGISTER_SIZE 4
#define HW_ACCESS_PRAM_MAX_RANGE 0x3c000
@@ -207,19 +207,23 @@ static inline int __must_check wlcore_write_reg(struct wl1271 *wl, int reg,
static inline void wl1271_power_off(struct wl1271 *wl)
{
- int ret;
+ int ret = 0;
if (!test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags))
return;
- ret = wl->if_ops->power(wl->dev, false);
+ if (wl->if_ops->power)
+ ret = wl->if_ops->power(wl->dev, false);
if (!ret)
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
}
static inline int wl1271_power_on(struct wl1271 *wl)
{
- int ret = wl->if_ops->power(wl->dev, true);
+ int ret = 0;
+
+ if (wl->if_ops->power)
+ ret = wl->if_ops->power(wl->dev, true);
if (ret == 0)
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index ec7f6af3fab2..d1109c4f0f0d 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -1,4 +1,3 @@
-
/*
* This file is part of wlcore
*
@@ -303,25 +302,11 @@ out:
static void wlcore_adjust_conf(struct wl1271 *wl)
{
- /* Adjust settings according to optional module parameters */
-
- /* Firmware Logger params */
- if (fwlog_mem_blocks != -1) {
- if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS &&
- fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) {
- wl->conf.fwlog.mem_blocks = fwlog_mem_blocks;
- } else {
- wl1271_error(
- "Illegal fwlog_mem_blocks=%d using default %d",
- fwlog_mem_blocks, wl->conf.fwlog.mem_blocks);
- }
- }
if (fwlog_param) {
if (!strcmp(fwlog_param, "continuous")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
- } else if (!strcmp(fwlog_param, "ondemand")) {
- wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
+ wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_HOST;
} else if (!strcmp(fwlog_param, "dbgpins")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
@@ -825,91 +810,32 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
{
- struct wlcore_partition_set part, old_part;
- u32 addr;
- u32 offset;
- u32 end_of_log;
- u8 *block;
- int ret;
+ u32 end_of_log = 0;
- if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
- (wl->conf.fwlog.mem_blocks == 0))
+ if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED)
return;
wl1271_info("Reading FW panic log");
- block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL);
- if (!block)
- return;
-
/*
* Make sure the chip is awake and the logger isn't active.
* Do not send a stop fwlog command if the fw is hanged or if
* dbgpins are used (due to some fw bug).
*/
if (wl1271_ps_elp_wakeup(wl))
- goto out;
+ return;
if (!wl->watchdog_recovery &&
wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
wl12xx_cmd_stop_fwlog(wl);
- /* Read the first memory block address */
- ret = wlcore_fw_status(wl, wl->fw_status);
- if (ret < 0)
- goto out;
-
- addr = wl->fw_status->log_start_addr;
- if (!addr)
- goto out;
-
- if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
- offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
- end_of_log = wl->fwlog_end;
- } else {
- offset = sizeof(addr);
- end_of_log = addr;
- }
-
- old_part = wl->curr_part;
- memset(&part, 0, sizeof(part));
-
/* Traverse the memory blocks linked list */
do {
- part.mem.start = wlcore_hw_convert_hwaddr(wl, addr);
- part.mem.size = PAGE_SIZE;
-
- ret = wlcore_set_partition(wl, &part);
- if (ret < 0) {
- wl1271_error("%s: set_partition start=0x%X size=%d",
- __func__, part.mem.start, part.mem.size);
- goto out;
+ end_of_log = wlcore_event_fw_logger(wl);
+ if (end_of_log == 0) {
+ msleep(100);
+ end_of_log = wlcore_event_fw_logger(wl);
}
-
- memset(block, 0, wl->fw_mem_block_size);
- ret = wlcore_read_hwaddr(wl, addr, block,
- wl->fw_mem_block_size, false);
-
- if (ret < 0)
- goto out;
-
- /*
- * Memory blocks are linked to one another. The first 4 bytes
- * of each memory block hold the hardware address of the next
- * one. The last memory block points to the first one in
- * on demand mode and is equal to 0x2000000 in continuous mode.
- */
- addr = le32_to_cpup((__le32 *)block);
-
- if (!wl12xx_copy_fwlog(wl, block + offset,
- wl->fw_mem_block_size - offset))
- break;
- } while (addr && (addr != end_of_log));
-
- wake_up_interruptible(&wl->fwlog_waitq);
-
-out:
- kfree(block);
- wlcore_set_partition(wl, &old_part);
+ } while (end_of_log != 0);
}
static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif,
@@ -6291,7 +6217,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
wl->active_sta_count = 0;
wl->active_link_count = 0;
wl->fwlog_size = 0;
- init_waitqueue_head(&wl->fwlog_waitq);
/* The system link is always allocated */
__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
@@ -6377,7 +6302,6 @@ int wlcore_free_hw(struct wl1271 *wl)
/* Unblock any fwlog readers */
mutex_lock(&wl->mutex);
wl->fwlog_size = -1;
- wake_up_interruptible_all(&wl->fwlog_waitq);
mutex_unlock(&wl->mutex);
wlcore_sysfs_free(wl);
@@ -6584,7 +6508,7 @@ MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
module_param_named(fwlog, fwlog_param, charp, 0);
MODULE_PARM_DESC(fwlog,
- "FW logger options: continuous, ondemand, dbgpins or disable");
+ "FW logger options: continuous, dbgpins or disable");
module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c
index 5b2927391d1c..34e7e938ede4 100644
--- a/drivers/net/wireless/ti/wlcore/rx.c
+++ b/drivers/net/wireless/ti/wlcore/rx.c
@@ -149,7 +149,6 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) {
size_t len = length - sizeof(*desc);
wl12xx_copy_fwlog(wl, data + sizeof(*desc), len);
- wake_up_interruptible(&wl->fwlog_waitq);
return 0;
}
diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 236b41090827..44f059f7f34e 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -73,7 +73,10 @@
*/
#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
-#define WSPI_MAX_NUM_OF_CHUNKS (SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE)
+/* Maximum number of SPI write chunks */
+#define WSPI_MAX_NUM_OF_CHUNKS \
+ ((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
+
struct wl12xx_spi_glue {
struct device *dev;
@@ -268,9 +271,10 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
void *buf, size_t len, bool fixed)
{
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
- struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)];
+ /* SPI write buffers - 2 for each chunk */
+ struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
struct spi_message m;
- u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
+ u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
u32 *cmd;
u32 chunk_len;
int i;
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
index 24dd288d6809..a9218e5b0efc 100644
--- a/drivers/net/wireless/ti/wlcore/sysfs.c
+++ b/drivers/net/wireless/ti/wlcore/sysfs.c
@@ -119,32 +119,6 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
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);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 906be6aa4eb6..dda01b118c26 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -310,9 +310,6 @@ struct wl1271 {
/* FW memory block size */
u32 fw_mem_block_size;
- /* Sysfs FW log entry readers wait queue */
- wait_queue_head_t fwlog_waitq;
-
/* Hardware recovery work */
struct work_struct recovery_work;
bool watchdog_recovery;
diff --git a/drivers/net/wireless/zydas/Kconfig b/drivers/net/wireless/zydas/Kconfig
new file mode 100644
index 000000000000..a58c0f65e376
--- /dev/null
+++ b/drivers/net/wireless/zydas/Kconfig
@@ -0,0 +1,35 @@
+config WLAN_VENDOR_ZYDAS
+ bool "ZyDAS devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ 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 cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ZYDAS
+
+config USB_ZD1201
+ tristate "USB ZD1201 based Wireless device support"
+ depends on CFG80211 && USB
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select FW_LOADER
+ ---help---
+ Say Y if you want to use wireless LAN adapters based on the ZyDAS
+ ZD1201 chip.
+
+ This driver makes the adapter appear as a normal Ethernet interface,
+ typically on wlan0.
+
+ The zd1201 device requires external firmware to be loaded.
+ This can be found at http://linux-lc100020.sourceforge.net/
+
+ To compile this driver as a module, choose M here: the
+ module will be called zd1201.
+
+source "drivers/net/wireless/zydas/zd1211rw/Kconfig"
+
+endif # WLAN_VENDOR_ZYDAS
diff --git a/drivers/net/wireless/zydas/Makefile b/drivers/net/wireless/zydas/Makefile
new file mode 100644
index 000000000000..679fbbf3a6cd
--- /dev/null
+++ b/drivers/net/wireless/zydas/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_ZD1211RW) += zd1211rw/
+
+obj-$(CONFIG_USB_ZD1201) += zd1201.o
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zydas/zd1201.c
index 6f5c793a7855..6f5c793a7855 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zydas/zd1201.c
diff --git a/drivers/net/wireless/zd1201.h b/drivers/net/wireless/zydas/zd1201.h
index dd7ea1f35bef..dd7ea1f35bef 100644
--- a/drivers/net/wireless/zd1201.h
+++ b/drivers/net/wireless/zydas/zd1201.h
diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zydas/zd1211rw/Kconfig
index 95920581860a..95920581860a 100644
--- a/drivers/net/wireless/zd1211rw/Kconfig
+++ b/drivers/net/wireless/zydas/zd1211rw/Kconfig
diff --git a/drivers/net/wireless/zd1211rw/Makefile b/drivers/net/wireless/zydas/zd1211rw/Makefile
index 5728a918e508..5728a918e508 100644
--- a/drivers/net/wireless/zd1211rw/Makefile
+++ b/drivers/net/wireless/zydas/zd1211rw/Makefile
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c
index 07b94eda9604..07b94eda9604 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h
index b03786c9f3aa..b03786c9f3aa 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h
diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zydas/zd1211rw/zd_def.h
index 41bd755bc135..41bd755bc135 100644
--- a/drivers/net/wireless/zd1211rw/zd_def.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_def.h
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
index e539d9b1b562..e539d9b1b562 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h
index 5a484235308f..5a484235308f 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c
index dc179c414518..dc179c414518 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h
index 8f14e25e1041..8f14e25e1041 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c
index 99aed7d78952..99aed7d78952 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c
index 5fea485be574..5fea485be574 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c
index a93f657a41c7..a93f657a41c7 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c
index 61b924027356..61b924027356 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
index a912dc051111..a912dc051111 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h
index a9075f225178..a9075f225178 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 0d6003dee3af..7437c9dfd8fc 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -76,4 +76,5 @@ source "drivers/nfc/st21nfca/Kconfig"
source "drivers/nfc/st-nci/Kconfig"
source "drivers/nfc/nxp-nci/Kconfig"
source "drivers/nfc/s3fwrn5/Kconfig"
+source "drivers/nfc/st95hf/Kconfig"
endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index e3621416a48e..0a99e67daa10 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/
obj-$(CONFIG_NFC_ST_NCI) += st-nci/
obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/
+obj-$(CONFIG_NFC_ST95HF) += st95hf/
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index 532db28145c7..5e797d5c38ed 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -298,6 +298,12 @@ static int fdp_nci_i2c_probe(struct i2c_client *client,
return -ENODEV;
}
+ /* Checking if we have an irq */
+ if (client->irq <= 0) {
+ nfc_err(dev, "IRQ not present\n");
+ return -ENODEV;
+ }
+
phy = devm_kzalloc(dev, sizeof(struct fdp_i2c_phy),
GFP_KERNEL);
if (!phy)
@@ -307,12 +313,6 @@ static int fdp_nci_i2c_probe(struct i2c_client *client,
phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
i2c_set_clientdata(client, phy);
- /* Checking if we have an irq */
- if (client->irq <= 0) {
- dev_err(dev, "IRQ not present\n");
- return -ENODEV;
- }
-
r = request_threaded_irq(client->irq, NULL, fdp_nci_i2c_irq_thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
FDP_I2C_DRIVER_NAME, phy);
diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c
index daf352597ef8..918e8f2eac47 100644
--- a/drivers/nfc/microread/i2c.c
+++ b/drivers/nfc/microread/i2c.c
@@ -50,8 +50,6 @@ struct microread_i2c_phy {
struct i2c_client *i2c_dev;
struct nfc_hci_dev *hdev;
- int irq;
-
int hard_fault; /*
* < 0 if hardware error occured (e.g. i2c err)
* and prevents normal operation.
diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
index 26ac9e5fa1ab..93aaca586858 100644
--- a/drivers/nfc/nfcsim.c
+++ b/drivers/nfc/nfcsim.c
@@ -32,6 +32,8 @@
#define NFCSIM_POLL_TARGET 2
#define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET)
+#define RX_DEFAULT_DELAY 5
+
struct nfcsim {
struct nfc_dev *nfc_dev;
@@ -51,6 +53,8 @@ struct nfcsim {
u8 initiator;
+ u32 rx_delay;
+
data_exchange_cb_t cb;
void *cb_context;
@@ -320,10 +324,9 @@ static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
* 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));
+ queue_delayed_work(wq, &peer->recv_work,
+ msecs_to_jiffies(dev->rx_delay));
mutex_unlock(&peer->lock);
@@ -461,6 +464,7 @@ static struct nfcsim *nfcsim_init_dev(void)
if (rc)
goto free_nfc_dev;
+ dev->rx_delay = RX_DEFAULT_DELAY;
return dev;
free_nfc_dev:
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
index df4333c7ee0f..11520f472f98 100644
--- a/drivers/nfc/nxp-nci/i2c.c
+++ b/drivers/nfc/nxp-nci/i2c.c
@@ -52,7 +52,6 @@ struct nxp_nci_i2c_phy {
unsigned int gpio_en;
unsigned int gpio_fw;
- unsigned int gpio_irq;
int hard_fault; /*
* < 0 if hardware error occurred (e.g. i2c err)
@@ -85,7 +84,7 @@ static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
return phy->hard_fault;
r = i2c_master_send(client, skb->data, skb->len);
- if (r == -EREMOTEIO) {
+ if (r < 0) {
/* Retry, chip was in standby */
usleep_range(110000, 120000);
r = i2c_master_send(client, skb->data, skb->len);
@@ -264,8 +263,6 @@ exit_irq_none:
return IRQ_NONE;
}
-#ifdef CONFIG_OF
-
static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
{
struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
@@ -294,48 +291,24 @@ static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
}
phy->gpio_fw = r;
- r = irq_of_parse_and_map(pp, 0);
- if (r < 0) {
- nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
- return r;
- }
- client->irq = r;
-
return 0;
}
-#else
-
-static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
-{
- return -ENODEV;
-}
-
-#endif
-
static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy)
{
struct i2c_client *client = phy->i2c_dev;
- struct gpio_desc *gpiod_en, *gpiod_fw, *gpiod_irq;
+ struct gpio_desc *gpiod_en, *gpiod_fw;
gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2, GPIOD_OUT_LOW);
gpiod_fw = devm_gpiod_get_index(&client->dev, NULL, 1, GPIOD_OUT_LOW);
- gpiod_irq = devm_gpiod_get_index(&client->dev, NULL, 0, GPIOD_IN);
- if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw) || IS_ERR(gpiod_irq)) {
+ if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw)) {
nfc_err(&client->dev, "No GPIOs\n");
return -EINVAL;
}
- client->irq = gpiod_to_irq(gpiod_irq);
- if (client->irq < 0) {
- nfc_err(&client->dev, "No IRQ\n");
- return -EINVAL;
- }
-
phy->gpio_en = desc_to_gpio(gpiod_en);
phy->gpio_fw = desc_to_gpio(gpiod_fw);
- phy->gpio_irq = desc_to_gpio(gpiod_irq);
return 0;
}
@@ -374,7 +347,6 @@ static int nxp_nci_i2c_probe(struct i2c_client *client,
} else if (pdata) {
phy->gpio_en = pdata->gpio_en;
phy->gpio_fw = pdata->gpio_fw;
- client->irq = pdata->irq;
} else if (ACPI_HANDLE(&client->dev)) {
r = nxp_nci_i2c_acpi_config(phy);
if (r < 0)
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index fa75c53f3fa5..76c318444304 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -166,7 +166,6 @@ struct pn544_i2c_phy {
struct nfc_hci_dev *hdev;
unsigned int gpio_en;
- unsigned int gpio_irq;
unsigned int gpio_fw;
unsigned int en_polarity;
@@ -879,9 +878,8 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
{
struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
const struct acpi_device_id *id;
- struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw;
+ struct gpio_desc *gpiod_en, *gpiod_fw;
struct device *dev;
- int ret;
if (!client)
return -EINVAL;
@@ -914,32 +912,9 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
phy->gpio_fw = desc_to_gpio(gpiod_fw);
- /* Get IRQ GPIO */
- gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0,
- GPIOD_IN);
- if (IS_ERR(gpiod_irq)) {
- nfc_err(dev, "Unable to get IRQ GPIO\n");
- return -ENODEV;
- }
-
- phy->gpio_irq = desc_to_gpio(gpiod_irq);
-
- /* Map the pin to an IRQ */
- ret = gpiod_to_irq(gpiod_irq);
- if (ret < 0) {
- nfc_err(dev, "Fail pin IRQ mapping\n");
- return ret;
- }
-
- nfc_info(dev, "GPIO resource, no:%d irq:%d\n",
- desc_to_gpio(gpiod_irq), ret);
- client->irq = ret;
-
return 0;
}
-#ifdef CONFIG_OF
-
static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
{
struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
@@ -996,15 +971,6 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
goto err_gpio_fw;
}
- /* IRQ */
- ret = irq_of_parse_and_map(pp, 0);
- if (ret < 0) {
- nfc_err(&client->dev,
- "Unable to get irq, error: %d\n", ret);
- goto err_gpio_fw;
- }
- client->irq = ret;
-
return 0;
err_gpio_fw:
@@ -1015,15 +981,6 @@ err_dt:
return ret;
}
-#else
-
-static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
-{
- return -ENODEV;
-}
-
-#endif
-
static int pn544_hci_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1076,7 +1033,6 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
- phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
/* Using ACPI */
} else if (ACPI_HANDLE(&client->dev)) {
r = pn544_hci_i2c_acpi_request_resources(client);
diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c
index 0d866ca295e3..9d9c8d57a042 100644
--- a/drivers/nfc/s3fwrn5/core.c
+++ b/drivers/nfc/s3fwrn5/core.c
@@ -147,7 +147,7 @@ static struct nci_ops s3fwrn5_nci_ops = {
};
int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
- struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
+ const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
{
struct s3fwrn5_info *info;
int ret;
diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c
index c61d8a308da4..3ed0adf6479b 100644
--- a/drivers/nfc/s3fwrn5/i2c.c
+++ b/drivers/nfc/s3fwrn5/i2c.c
@@ -125,7 +125,7 @@ static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb)
return 0;
}
-static struct s3fwrn5_phy_ops i2c_phy_ops = {
+static const struct s3fwrn5_phy_ops i2c_phy_ops = {
.set_wake = s3fwrn5_i2c_set_wake,
.set_mode = s3fwrn5_i2c_set_mode,
.get_mode = s3fwrn5_i2c_get_mode,
diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h
index 89210d4828b8..7d5e516036fb 100644
--- a/drivers/nfc/s3fwrn5/s3fwrn5.h
+++ b/drivers/nfc/s3fwrn5/s3fwrn5.h
@@ -44,7 +44,7 @@ struct s3fwrn5_info {
void *phy_id;
struct device *pdev;
- struct s3fwrn5_phy_ops *phy_ops;
+ const struct s3fwrn5_phy_ops *phy_ops;
unsigned int max_payload;
struct s3fwrn5_fw_info fw_info;
@@ -90,7 +90,7 @@ static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb)
}
int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
- struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload);
+ const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload);
void s3fwrn5_remove(struct nci_dev *ndev);
int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
diff --git a/drivers/nfc/st-nci/Kconfig b/drivers/nfc/st-nci/Kconfig
index e7c6db9c5860..dc9b777d78f6 100644
--- a/drivers/nfc/st-nci/Kconfig
+++ b/drivers/nfc/st-nci/Kconfig
@@ -1,19 +1,14 @@
config NFC_ST_NCI
- tristate "STMicroelectronics ST NCI NFC driver"
- depends on NFC_NCI
- default n
+ tristate
---help---
STMicroelectronics NFC NCI chips core driver. It implements the chipset
NCI logic and hooks into the NFC kernel APIs. Physical layers will
register against it.
- To compile this driver as a module, choose m here. The module will
- be called st-nci.
- Say N if unsure.
-
config NFC_ST_NCI_I2C
- tristate "NFC ST NCI i2c support"
- depends on NFC_ST_NCI && I2C
+ tristate "STMicroelectronics ST NCI NFC driver (I2C)"
+ depends on NFC_NCI && I2C
+ select NFC_ST_NCI
---help---
This module adds support for an I2C interface to the
STMicroelectronics NFC NCI chips familly.
@@ -23,8 +18,9 @@ config NFC_ST_NCI_I2C
Say N if unsure.
config NFC_ST_NCI_SPI
- tristate "NFC ST NCI spi support"
- depends on NFC_ST_NCI && SPI
+ tristate "STMicroelectronics ST NCI NFC driver (SPI)"
+ depends on NFC_NCI && SPI
+ select NFC_ST_NCI
---help---
This module adds support for an SPI interface to the
STMicroelectronics NFC NCI chips familly.
diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c
index 15e3ce2d274c..8a56b5c6e4c4 100644
--- a/drivers/nfc/st-nci/i2c.c
+++ b/drivers/nfc/st-nci/i2c.c
@@ -20,8 +20,10 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nfc.h>
@@ -40,11 +42,7 @@
#define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c"
-static struct i2c_device_id st_nci_i2c_id_table[] = {
- {ST_NCI_DRIVER_NAME, 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table);
+#define ST_NCI_GPIO_NAME_RESET "clf_reset"
struct st_nci_i2c_phy {
struct i2c_client *i2c_dev;
@@ -210,7 +208,43 @@ static struct nfc_phy_ops i2c_phy_ops = {
.disable = st_nci_i2c_disable,
};
-#ifdef CONFIG_OF
+static int st_nci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+ struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
+ const struct acpi_device_id *id;
+ struct gpio_desc *gpiod_reset;
+ struct device *dev;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* Match the struct device against a given list of ACPI IDs */
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Get RESET GPIO from ACPI */
+ gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_reset)) {
+ nfc_err(dev, "Unable to get RESET GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_reset = desc_to_gpio(gpiod_reset);
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present =
+ device_property_present(dev, "ese-present");
+ phy->se_status.is_uicc_present =
+ device_property_present(dev, "uicc-present");
+
+ return 0;
+}
+
static int st_nci_i2c_of_request_resources(struct i2c_client *client)
{
struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
@@ -232,7 +266,7 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client)
/* GPIO request and configuration */
r = devm_gpio_request_one(&client->dev, gpio,
- GPIOF_OUT_INIT_HIGH, "clf_reset");
+ GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET);
if (r) {
nfc_err(&client->dev, "Failed to request reset pin\n");
return r;
@@ -248,12 +282,6 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client)
return 0;
}
-#else
-static int st_nci_i2c_of_request_resources(struct i2c_client *client)
-{
- return -ENODEV;
-}
-#endif
static int st_nci_i2c_request_resources(struct i2c_client *client)
{
@@ -272,7 +300,8 @@ static int st_nci_i2c_request_resources(struct i2c_client *client)
phy->irq_polarity = pdata->irq_polarity;
r = devm_gpio_request_one(&client->dev,
- phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset");
+ phy->gpio_reset, GPIOF_OUT_INIT_HIGH,
+ ST_NCI_GPIO_NAME_RESET);
if (r) {
pr_err("%s : reset gpio_request failed\n", __FILE__);
return r;
@@ -322,6 +351,12 @@ static int st_nci_i2c_probe(struct i2c_client *client,
"Cannot get platform resources\n");
return r;
}
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = st_nci_i2c_acpi_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "Cannot get ACPI data\n");
+ return r;
+ }
} else {
nfc_err(&client->dev,
"st_nci platform resources not available\n");
@@ -358,7 +393,19 @@ static int st_nci_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_OF
+static struct i2c_device_id st_nci_i2c_id_table[] = {
+ {ST_NCI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table);
+
+static const struct acpi_device_id st_nci_i2c_acpi_match[] = {
+ {"SMO2101"},
+ {"SMO2102"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st_nci_i2c_acpi_match);
+
static const struct of_device_id of_st_nci_i2c_match[] = {
{ .compatible = "st,st21nfcb-i2c", },
{ .compatible = "st,st21nfcb_i2c", },
@@ -366,19 +413,18 @@ static const struct of_device_id of_st_nci_i2c_match[] = {
{}
};
MODULE_DEVICE_TABLE(of, of_st_nci_i2c_match);
-#endif
static struct i2c_driver st_nci_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ST_NCI_I2C_DRIVER_NAME,
.of_match_table = of_match_ptr(of_st_nci_i2c_match),
+ .acpi_match_table = ACPI_PTR(st_nci_i2c_acpi_match),
},
.probe = st_nci_i2c_probe,
.id_table = st_nci_i2c_id_table,
.remove = st_nci_i2c_remove,
};
-
module_i2c_driver(st_nci_i2c_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/st-nci/ndlc.c b/drivers/nfc/st-nci/ndlc.c
index 0884b11001ef..50880d747b02 100644
--- a/drivers/nfc/st-nci/ndlc.c
+++ b/drivers/nfc/st-nci/ndlc.c
@@ -20,7 +20,6 @@
#include <net/nfc/nci_core.h>
#include "st-nci.h"
-#include "ndlc.h"
#define NDLC_TIMER_T1 100
#define NDLC_TIMER_T1_WAIT 400
diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c
index dbab722a0654..a53e5df803eb 100644
--- a/drivers/nfc/st-nci/se.c
+++ b/drivers/nfc/st-nci/se.c
@@ -331,7 +331,7 @@ static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev,
switch (event) {
case ST_NCI_EVT_CONNECTIVITY:
-
+ r = nfc_se_connectivity(ndev->nfc_dev, host);
break;
case ST_NCI_EVT_TRANSACTION:
/* According to specification etsi 102 622
@@ -392,7 +392,6 @@ void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
}
EXPORT_SYMBOL_GPL(st_nci_hci_event_received);
-
void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
struct sk_buff *skb)
{
diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c
index d6519bb9dba5..821dfa950fa8 100644
--- a/drivers/nfc/st-nci/spi.c
+++ b/drivers/nfc/st-nci/spi.c
@@ -20,8 +20,10 @@
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nfc.h>
@@ -34,18 +36,14 @@
/* ndlc header */
#define ST_NCI_FRAME_HEADROOM 1
-#define ST_NCI_FRAME_TAILROOM 0
+#define ST_NCI_FRAME_TAILROOM 0
#define ST_NCI_SPI_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */
#define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */
#define ST_NCI_SPI_DRIVER_NAME "st_nci_spi"
-static struct spi_device_id st_nci_spi_id_table[] = {
- {ST_NCI_SPI_DRIVER_NAME, 0},
- {}
-};
-MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table);
+#define ST_NCI_GPIO_NAME_RESET "clf_reset"
struct st_nci_spi_phy {
struct spi_device *spi_dev;
@@ -225,7 +223,43 @@ static struct nfc_phy_ops spi_phy_ops = {
.disable = st_nci_spi_disable,
};
-#ifdef CONFIG_OF
+static int st_nci_spi_acpi_request_resources(struct spi_device *spi_dev)
+{
+ struct st_nci_spi_phy *phy = spi_get_drvdata(spi_dev);
+ const struct acpi_device_id *id;
+ struct gpio_desc *gpiod_reset;
+ struct device *dev;
+
+ if (!spi_dev)
+ return -EINVAL;
+
+ dev = &spi_dev->dev;
+
+ /* Match the struct device against a given list of ACPI IDs */
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Get RESET GPIO from ACPI */
+ gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_reset)) {
+ nfc_err(dev, "Unable to get RESET GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_reset = desc_to_gpio(gpiod_reset);
+
+ phy->irq_polarity = irq_get_trigger_type(spi_dev->irq);
+
+ phy->se_status.is_ese_present =
+ device_property_present(dev, "ese-present");
+ phy->se_status.is_uicc_present =
+ device_property_present(dev, "uicc-present");
+
+ return 0;
+}
+
static int st_nci_spi_of_request_resources(struct spi_device *dev)
{
struct st_nci_spi_phy *phy = spi_get_drvdata(dev);
@@ -247,7 +281,7 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev)
/* GPIO request and configuration */
r = devm_gpio_request_one(&dev->dev, gpio,
- GPIOF_OUT_INIT_HIGH, "clf_reset");
+ GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET);
if (r) {
nfc_err(&dev->dev, "Failed to request reset pin\n");
return r;
@@ -263,12 +297,6 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev)
return 0;
}
-#else
-static int st_nci_spi_of_request_resources(struct spi_device *dev)
-{
- return -ENODEV;
-}
-#endif
static int st_nci_spi_request_resources(struct spi_device *dev)
{
@@ -287,7 +315,8 @@ static int st_nci_spi_request_resources(struct spi_device *dev)
phy->irq_polarity = pdata->irq_polarity;
r = devm_gpio_request_one(&dev->dev,
- phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset");
+ phy->gpio_reset, GPIOF_OUT_INIT_HIGH,
+ ST_NCI_GPIO_NAME_RESET);
if (r) {
pr_err("%s : reset gpio_request failed\n", __FILE__);
return r;
@@ -338,6 +367,12 @@ static int st_nci_spi_probe(struct spi_device *dev)
"Cannot get platform resources\n");
return r;
}
+ } else if (ACPI_HANDLE(&dev->dev)) {
+ r = st_nci_spi_acpi_request_resources(dev);
+ if (r) {
+ nfc_err(&dev->dev, "Cannot get ACPI data\n");
+ return r;
+ }
} else {
nfc_err(&dev->dev,
"st_nci platform resources not available\n");
@@ -374,24 +409,34 @@ static int st_nci_spi_remove(struct spi_device *dev)
return 0;
}
-#ifdef CONFIG_OF
+static struct spi_device_id st_nci_spi_id_table[] = {
+ {ST_NCI_SPI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table);
+
+static const struct acpi_device_id st_nci_spi_acpi_match[] = {
+ {"SMO2101", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st_nci_spi_acpi_match);
+
static const struct of_device_id of_st_nci_spi_match[] = {
{ .compatible = "st,st21nfcb-spi", },
{}
};
MODULE_DEVICE_TABLE(of, of_st_nci_spi_match);
-#endif
static struct spi_driver st_nci_spi_driver = {
.driver = {
.name = ST_NCI_SPI_DRIVER_NAME,
.of_match_table = of_match_ptr(of_st_nci_spi_match),
+ .acpi_match_table = ACPI_PTR(st_nci_spi_acpi_match),
},
.probe = st_nci_spi_probe,
.id_table = st_nci_spi_id_table,
.remove = st_nci_spi_remove,
};
-
module_spi_driver(st_nci_spi_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig
index ee459f066ade..cc3bd5658901 100644
--- a/drivers/nfc/st21nfca/Kconfig
+++ b/drivers/nfc/st21nfca/Kconfig
@@ -1,20 +1,15 @@
config NFC_ST21NFCA
- tristate "STMicroelectronics ST21NFCA NFC driver"
- depends on NFC_HCI
+ tristate
select CRC_CCITT
- default n
---help---
STMicroelectronics ST21NFCA core driver. It implements the chipset
HCI logic and hooks into the NFC kernel APIs. Physical layers will
register against it.
- To compile this driver as a module, choose m here. The module will
- be called st21nfca.
- Say N if unsure.
-
config NFC_ST21NFCA_I2C
- tristate "NFC ST21NFCA i2c support"
- depends on NFC_ST21NFCA && I2C && NFC_SHDLC
+ tristate "STMicroelectronics ST21NFCA NFC driver (I2C)"
+ depends on NFC_HCI && I2C && NFC_SHDLC
+ select NFC_ST21NFCA
---help---
This module adds support for the STMicroelectronics st21nfca i2c interface.
Select this if your platform is using the i2c bus.
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
index a98da33e680a..1f44a151d206 100644
--- a/drivers/nfc/st21nfca/i2c.c
+++ b/drivers/nfc/st21nfca/i2c.c
@@ -21,8 +21,10 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -60,12 +62,7 @@
#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c"
-static struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
- {ST21NFCA_HCI_DRIVER_NAME, 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
+#define ST21NFCA_GPIO_NAME_EN "clf_enable"
struct st21nfca_i2c_phy {
struct i2c_client *i2c_dev;
@@ -167,7 +164,6 @@ static void st21nfca_hci_i2c_disable(void *phy_id)
{
struct st21nfca_i2c_phy *phy = phy_id;
- pr_info("\n");
gpio_set_value(phy->gpio_ena, 0);
phy->powered = 0;
@@ -210,7 +206,6 @@ static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb)
I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb);
-
if (phy->hard_fault != 0)
return phy->hard_fault;
@@ -509,7 +504,41 @@ static struct nfc_phy_ops i2c_phy_ops = {
.disable = st21nfca_hci_i2c_disable,
};
-#ifdef CONFIG_OF
+static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+ struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+ const struct acpi_device_id *id;
+ struct gpio_desc *gpiod_ena;
+ struct device *dev;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* Match the struct device against a given list of ACPI IDs */
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Get EN GPIO from ACPI */
+ gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1,
+ GPIOD_OUT_LOW);
+ if (!IS_ERR(gpiod_ena))
+ phy->gpio_ena = desc_to_gpio(gpiod_ena);
+
+ phy->gpio_ena = desc_to_gpio(gpiod_ena);
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present =
+ device_property_present(dev, "ese-present");
+ phy->se_status.is_uicc_present =
+ device_property_present(dev, "uicc-present");
+
+ return 0;
+}
+
static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
{
struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
@@ -530,7 +559,7 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
/* GPIO request and configuration */
r = devm_gpio_request_one(&client->dev, gpio, GPIOF_OUT_INIT_HIGH,
- "clf_enable");
+ ST21NFCA_GPIO_NAME_EN);
if (r) {
nfc_err(&client->dev, "Failed to request enable pin\n");
return r;
@@ -547,12 +576,6 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
return 0;
}
-#else
-static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
-{
- return -ENODEV;
-}
-#endif
static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
{
@@ -572,7 +595,8 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
if (phy->gpio_ena > 0) {
r = devm_gpio_request_one(&client->dev, phy->gpio_ena,
- GPIOF_OUT_INIT_HIGH, "clf_enable");
+ GPIOF_OUT_INIT_HIGH,
+ ST21NFCA_GPIO_NAME_EN);
if (r) {
pr_err("%s : ena gpio_request failed\n", __FILE__);
return r;
@@ -628,6 +652,12 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client,
nfc_err(&client->dev, "Cannot get platform resources\n");
return r;
}
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = st21nfca_hci_i2c_acpi_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "Cannot get ACPI data\n");
+ return r;
+ }
} else {
nfc_err(&client->dev, "st21nfca platform resources not available\n");
return -ENODEV;
@@ -670,26 +700,36 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_OF
+static struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
+ {ST21NFCA_HCI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
+
+static const struct acpi_device_id st21nfca_hci_i2c_acpi_match[] = {
+ {"SMO2100", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st21nfca_hci_i2c_acpi_match);
+
static const struct of_device_id of_st21nfca_i2c_match[] = {
{ .compatible = "st,st21nfca-i2c", },
{ .compatible = "st,st21nfca_i2c", },
{}
};
MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match);
-#endif
static struct i2c_driver st21nfca_hci_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ST21NFCA_HCI_I2C_DRIVER_NAME,
.of_match_table = of_match_ptr(of_st21nfca_i2c_match),
+ .acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match),
},
.probe = st21nfca_hci_i2c_probe,
.id_table = st21nfca_hci_i2c_id_table,
.remove = st21nfca_hci_i2c_remove,
};
-
module_i2c_driver(st21nfca_hci_i2c_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c
index c79d99b24c96..bd56a16e4007 100644
--- a/drivers/nfc/st21nfca/se.c
+++ b/drivers/nfc/st21nfca/se.c
@@ -312,7 +312,8 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
switch (event) {
case ST21NFCA_EVT_CONNECTIVITY:
- break;
+ r = nfc_se_connectivity(hdev->ndev, host);
+ break;
case ST21NFCA_EVT_TRANSACTION:
/*
* According to specification etsi 102 622
@@ -342,7 +343,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
transaction->aid_len + 4, transaction->params_len);
r = nfc_se_transaction(hdev->ndev, host, transaction);
- break;
+ break;
default:
nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n");
return 1;
diff --git a/drivers/nfc/st95hf/Kconfig b/drivers/nfc/st95hf/Kconfig
new file mode 100644
index 000000000000..224f266fdcb6
--- /dev/null
+++ b/drivers/nfc/st95hf/Kconfig
@@ -0,0 +1,10 @@
+config NFC_ST95HF
+ tristate "ST95HF NFC Transceiver driver"
+ depends on SPI && NFC_DIGITAL
+ help
+ This enables the ST NFC driver for ST95HF NFC transceiver.
+ This makes use of SPI framework to communicate with transceiver
+ and registered with NFC digital core to support Linux NFC framework.
+
+ Say Y here to compile support for ST NFC transceiver ST95HF
+ linux driver into the kernel or say M to compile it as module.
diff --git a/drivers/nfc/st95hf/Makefile b/drivers/nfc/st95hf/Makefile
new file mode 100644
index 000000000000..00760b38ab7e
--- /dev/null
+++ b/drivers/nfc/st95hf/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for STMicroelectronics NFC transceiver ST95HF
+#
+
+obj-$(CONFIG_NFC_ST95HF) += st95hf.o
+st95hf-objs := spi.o core.o
diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c
new file mode 100644
index 000000000000..c2840e412962
--- /dev/null
+++ b/drivers/nfc/st95hf/core.c
@@ -0,0 +1,1273 @@
+/*
+ * --------------------------------------------------------------------
+ * Driver for ST NFC Transceiver ST95HF
+ * --------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. 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 that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nfc.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/wait.h>
+#include <net/nfc/digital.h>
+#include <net/nfc/nfc.h>
+
+#include "spi.h"
+
+/* supported protocols */
+#define ST95HF_SUPPORTED_PROT (NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_ISO14443_B_MASK | \
+ NFC_PROTO_ISO15693_MASK)
+/* driver capabilities */
+#define ST95HF_CAPABILITIES NFC_DIGITAL_DRV_CAPS_IN_CRC
+
+/* Command Send Interface */
+/* ST95HF_COMMAND_SEND CMD Ids */
+#define ECHO_CMD 0x55
+#define WRITE_REGISTER_CMD 0x9
+#define PROTOCOL_SELECT_CMD 0x2
+#define SEND_RECEIVE_CMD 0x4
+
+/* Select protocol codes */
+#define ISO15693_PROTOCOL_CODE 0x1
+#define ISO14443A_PROTOCOL_CODE 0x2
+#define ISO14443B_PROTOCOL_CODE 0x3
+
+/*
+ * head room len is 3
+ * 1 byte for control byte
+ * 1 byte for cmd
+ * 1 byte for size
+ */
+#define ST95HF_HEADROOM_LEN 3
+
+/*
+ * tailroom is 1 for ISO14443A
+ * and 0 for ISO14443B/ISO15693,
+ * hence the max value 1 should be
+ * taken.
+ */
+#define ST95HF_TAILROOM_LEN 1
+
+/* Command Response interface */
+#define MAX_RESPONSE_BUFFER_SIZE 280
+#define ECHORESPONSE 0x55
+#define ST95HF_ERR_MASK 0xF
+#define ST95HF_TIMEOUT_ERROR 0x87
+#define ST95HF_NFCA_CRC_ERR_MASK 0x20
+#define ST95HF_NFCB_CRC_ERR_MASK 0x01
+
+/* ST95HF transmission flag values */
+#define TRFLAG_NFCA_SHORT_FRAME 0x07
+#define TRFLAG_NFCA_STD_FRAME 0x08
+#define TRFLAG_NFCA_STD_FRAME_CRC 0x28
+
+/* Misc defs */
+#define HIGH 1
+#define LOW 0
+#define ISO14443A_RATS_REQ 0xE0
+#define RATS_TB1_PRESENT_MASK 0x20
+#define RATS_TA1_PRESENT_MASK 0x10
+#define TB1_FWI_MASK 0xF0
+#define WTX_REQ_FROM_TAG 0xF2
+
+#define MAX_CMD_LEN 0x7
+
+#define MAX_CMD_PARAMS 4
+struct cmd {
+ int cmd_len;
+ unsigned char cmd_id;
+ unsigned char no_cmd_params;
+ unsigned char cmd_params[MAX_CMD_PARAMS];
+ enum req_type req;
+};
+
+struct param_list {
+ int param_offset;
+ int new_param_val;
+};
+
+/*
+ * List of top-level cmds to be used internally by the driver.
+ * All these commands are build on top of ST95HF basic commands
+ * such as SEND_RECEIVE_CMD, PROTOCOL_SELECT_CMD, etc.
+ * These top level cmds are used internally while implementing various ops of
+ * digital layer/driver probe or extending the digital framework layer for
+ * features that are not yet implemented there, for example, WTX cmd handling.
+ */
+enum st95hf_cmd_list {
+ CMD_ECHO,
+ CMD_ISO14443A_CONFIG,
+ CMD_ISO14443A_DEMOGAIN,
+ CMD_ISO14443B_DEMOGAIN,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ CMD_WTX_RESPONSE,
+ CMD_FIELD_OFF,
+ CMD_ISO15693_PROTOCOL_SELECT,
+};
+
+static const struct cmd cmd_array[] = {
+ [CMD_ECHO] = {
+ .cmd_len = 0x2,
+ .cmd_id = ECHO_CMD,
+ .no_cmd_params = 0,
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_CONFIG] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x3A, 0x00, 0x5A, 0x04},
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_DEMOGAIN] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x68, 0x01, 0x01, 0xDF},
+ .req = SYNC,
+ },
+ [CMD_ISO14443B_DEMOGAIN] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x68, 0x01, 0x01, 0x51},
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_PROTOCOL_SELECT] = {
+ .cmd_len = 0x7,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {ISO14443A_PROTOCOL_CODE, 0x00, 0x01, 0xA0},
+ .req = SYNC,
+ },
+ [CMD_ISO14443B_PROTOCOL_SELECT] = {
+ .cmd_len = 0x7,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {ISO14443B_PROTOCOL_CODE, 0x01, 0x03, 0xFF},
+ .req = SYNC,
+ },
+ [CMD_WTX_RESPONSE] = {
+ .cmd_len = 0x6,
+ .cmd_id = SEND_RECEIVE_CMD,
+ .no_cmd_params = 0x3,
+ .cmd_params = {0xF2, 0x00, TRFLAG_NFCA_STD_FRAME_CRC},
+ .req = ASYNC,
+ },
+ [CMD_FIELD_OFF] = {
+ .cmd_len = 0x5,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x2,
+ .cmd_params = {0x0, 0x0},
+ .req = SYNC,
+ },
+ [CMD_ISO15693_PROTOCOL_SELECT] = {
+ .cmd_len = 0x5,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x2,
+ .cmd_params = {ISO15693_PROTOCOL_CODE, 0x0D},
+ .req = SYNC,
+ },
+};
+
+/* st95_digital_cmd_complete_arg stores client context */
+struct st95_digital_cmd_complete_arg {
+ struct sk_buff *skb_resp;
+ nfc_digital_cmd_complete_t complete_cb;
+ void *cb_usrarg;
+ bool rats;
+};
+
+/*
+ * structure containing ST95HF driver specific data.
+ * @spicontext: structure containing information required
+ * for spi communication between st95hf and host.
+ * @ddev: nfc digital device object.
+ * @nfcdev: nfc device object.
+ * @enable_gpio: gpio used to enable st95hf transceiver.
+ * @complete_cb_arg: structure to store various context information
+ * that is passed from nfc requesting thread to the threaded ISR.
+ * @st95hf_supply: regulator "consumer" for NFC device.
+ * @sendrcv_trflag: last byte of frame send by sendrecv command
+ * of st95hf. This byte contains transmission flag info.
+ * @exchange_lock: semaphore used for signaling the st95hf_remove
+ * function that the last outstanding async nfc request is finished.
+ * @rm_lock: mutex for ensuring safe access of nfc digital object
+ * from threaded ISR. Usage of this mutex avoids any race between
+ * deletion of the object from st95hf_remove() and its access from
+ * the threaded ISR.
+ * @nfcdev_free: flag to have the state of nfc device object.
+ * [alive | died]
+ * @current_protocol: current nfc protocol.
+ * @current_rf_tech: current rf technology.
+ * @fwi: frame waiting index, received in reply of RATS according to
+ * digital protocol.
+ */
+struct st95hf_context {
+ struct st95hf_spi_context spicontext;
+ struct nfc_digital_dev *ddev;
+ struct nfc_dev *nfcdev;
+ unsigned int enable_gpio;
+ struct st95_digital_cmd_complete_arg complete_cb_arg;
+ struct regulator *st95hf_supply;
+ unsigned char sendrcv_trflag;
+ struct semaphore exchange_lock;
+ struct mutex rm_lock;
+ bool nfcdev_free;
+ u8 current_protocol;
+ u8 current_rf_tech;
+ int fwi;
+};
+
+/*
+ * st95hf_send_recv_cmd() is for sending commands to ST95HF
+ * that are described in the cmd_array[]. It can optionally
+ * receive the response if the cmd request is of type
+ * SYNC. For that to happen caller must pass true to recv_res.
+ * For ASYNC request, recv_res is ignored and the
+ * function will never try to receive the response on behalf
+ * of the caller.
+ */
+static int st95hf_send_recv_cmd(struct st95hf_context *st95context,
+ enum st95hf_cmd_list cmd,
+ int no_modif,
+ struct param_list *list_array,
+ bool recv_res)
+{
+ unsigned char spi_cmd_buffer[MAX_CMD_LEN];
+ int i, ret;
+ struct device *dev = &st95context->spicontext.spidev->dev;
+
+ if (cmd_array[cmd].cmd_len > MAX_CMD_LEN)
+ return -EINVAL;
+ if (cmd_array[cmd].no_cmd_params < no_modif)
+ return -EINVAL;
+ if (no_modif && !list_array)
+ return -EINVAL;
+
+ spi_cmd_buffer[0] = ST95HF_COMMAND_SEND;
+ spi_cmd_buffer[1] = cmd_array[cmd].cmd_id;
+ spi_cmd_buffer[2] = cmd_array[cmd].no_cmd_params;
+
+ memcpy(&spi_cmd_buffer[3], cmd_array[cmd].cmd_params,
+ spi_cmd_buffer[2]);
+
+ for (i = 0; i < no_modif; i++) {
+ if (list_array[i].param_offset >= cmd_array[cmd].no_cmd_params)
+ return -EINVAL;
+ spi_cmd_buffer[3 + list_array[i].param_offset] =
+ list_array[i].new_param_val;
+ }
+
+ ret = st95hf_spi_send(&st95context->spicontext,
+ spi_cmd_buffer,
+ cmd_array[cmd].cmd_len,
+ cmd_array[cmd].req);
+ if (ret) {
+ dev_err(dev, "st95hf_spi_send failed with error %d\n", ret);
+ return ret;
+ }
+
+ if (cmd_array[cmd].req == SYNC && recv_res) {
+ unsigned char st95hf_response_arr[2];
+
+ ret = st95hf_spi_recv_response(&st95context->spicontext,
+ st95hf_response_arr);
+ if (ret < 0) {
+ dev_err(dev, "spi error from st95hf_spi_recv_response(), err = 0x%x\n",
+ ret);
+ return ret;
+ }
+
+ if (st95hf_response_arr[0]) {
+ dev_err(dev, "st95hf error from st95hf_spi_recv_response(), err = 0x%x\n",
+ st95hf_response_arr[0]);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int st95hf_echo_command(struct st95hf_context *st95context)
+{
+ int result = 0;
+ unsigned char echo_response;
+
+ result = st95hf_send_recv_cmd(st95context, CMD_ECHO, 0, NULL, false);
+ if (result)
+ return result;
+
+ /* If control reached here, response can be taken */
+ result = st95hf_spi_recv_echo_res(&st95context->spicontext,
+ &echo_response);
+ if (result) {
+ dev_err(&st95context->spicontext.spidev->dev,
+ "err: echo response receieve error = 0x%x\n", result);
+ return result;
+ }
+
+ if (echo_response == ECHORESPONSE)
+ return 0;
+
+ dev_err(&st95context->spicontext.spidev->dev, "err: echo res is 0x%x\n",
+ echo_response);
+
+ return -EIO;
+}
+
+static int secondary_configuration_type4a(struct st95hf_context *stcontext)
+{
+ int result = 0;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ /* 14443A config setting after select protocol */
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_CONFIG,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "type a config cmd, err = 0x%x\n", result);
+ return result;
+ }
+
+ /* 14443A demo gain setting */
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_DEMOGAIN,
+ 0,
+ NULL,
+ true);
+ if (result)
+ dev_err(dev, "type a demogain cmd, err = 0x%x\n", result);
+
+ return result;
+}
+
+static int secondary_configuration_type4b(struct st95hf_context *stcontext)
+{
+ int result = 0;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443B_DEMOGAIN,
+ 0,
+ NULL,
+ true);
+ if (result)
+ dev_err(dev, "type b demogain cmd, err = 0x%x\n", result);
+
+ return result;
+}
+
+static int st95hf_select_protocol(struct st95hf_context *stcontext, int type)
+{
+ int result = 0;
+ struct device *dev;
+
+ dev = &stcontext->nfcdev->dev;
+
+ switch (type) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106A;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4A after protocol select */
+ result = secondary_configuration_type4a(stcontext);
+ if (result) {
+ dev_err(dev, "type a secondary config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106B;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel send, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /*
+ * delay of 5-6 ms is required after select protocol
+ * command in case of ISO14443 Type B
+ */
+ usleep_range(50000, 60000);
+
+ /* secondary config. for 14443Type 4B after protocol select */
+ result = secondary_configuration_type4b(stcontext);
+ if (result) {
+ dev_err(dev, "type b secondary config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO15693_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel send, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con)
+{
+ /* First make irq_in pin high */
+ gpio_set_value(st95con->enable_gpio, HIGH);
+
+ /* wait for 1 milisecond */
+ usleep_range(1000, 2000);
+
+ /* Make irq_in pin low */
+ gpio_set_value(st95con->enable_gpio, LOW);
+
+ /* wait for minimum interrupt pulse to make st95 active */
+ usleep_range(1000, 2000);
+
+ /* At end make it high */
+ gpio_set_value(st95con->enable_gpio, HIGH);
+}
+
+/*
+ * Send a reset sequence over SPI bus (Reset command + wait 3ms +
+ * negative pulse on st95hf enable gpio
+ */
+static int st95hf_send_spi_reset_sequence(struct st95hf_context *st95context)
+{
+ int result = 0;
+ unsigned char reset_cmd = ST95HF_COMMAND_RESET;
+
+ result = st95hf_spi_send(&st95context->spicontext,
+ &reset_cmd,
+ ST95HF_RESET_CMD_LEN,
+ ASYNC);
+ if (result) {
+ dev_err(&st95context->spicontext.spidev->dev,
+ "spi reset sequence cmd error = %d", result);
+ return result;
+ }
+
+ /* wait for 3 milisecond to complete the controller reset process */
+ usleep_range(3000, 4000);
+
+ /* send negative pulse to make st95hf active */
+ st95hf_send_st95enable_negativepulse(st95context);
+
+ /* wait for 10 milisecond : HFO setup time */
+ usleep_range(10000, 20000);
+
+ return result;
+}
+
+static int st95hf_por_sequence(struct st95hf_context *st95context)
+{
+ int nth_attempt = 1;
+ int result;
+
+ st95hf_send_st95enable_negativepulse(st95context);
+
+ usleep_range(5000, 6000);
+ do {
+ /* send an ECHO command and checks ST95HF response */
+ result = st95hf_echo_command(st95context);
+
+ dev_dbg(&st95context->spicontext.spidev->dev,
+ "response from echo function = 0x%x, attempt = %d\n",
+ result, nth_attempt);
+
+ if (!result)
+ return 0;
+
+ /* send an pulse on IRQ in case of the chip is on sleep state */
+ if (nth_attempt == 2)
+ st95hf_send_st95enable_negativepulse(st95context);
+ else
+ st95hf_send_spi_reset_sequence(st95context);
+
+ /* delay of 50 milisecond */
+ usleep_range(50000, 51000);
+ } while (nth_attempt++ < 3);
+
+ return -ETIMEDOUT;
+}
+
+static int iso14443_config_fdt(struct st95hf_context *st95context, int wtxm)
+{
+ int result = 0;
+ struct device *dev = &st95context->spicontext.spidev->dev;
+ struct nfc_digital_dev *nfcddev = st95context->ddev;
+ unsigned char pp_typeb;
+ struct param_list new_params[2];
+
+ pp_typeb = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[2];
+
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 &&
+ st95context->fwi < 4)
+ st95context->fwi = 4;
+
+ new_params[0].param_offset = 2;
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443)
+ new_params[0].new_param_val = st95context->fwi;
+ else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B)
+ new_params[0].new_param_val = pp_typeb;
+
+ new_params[1].param_offset = 3;
+ new_params[1].new_param_val = wtxm;
+
+ switch (nfcddev->curr_protocol) {
+ case NFC_PROTO_ISO14443:
+ result = st95hf_send_recv_cmd(st95context,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ 2,
+ new_params,
+ true);
+ if (result) {
+ dev_err(dev, "WTX type a sel proto, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4A after protocol select */
+ result = secondary_configuration_type4a(st95context);
+ if (result) {
+ dev_err(dev, "WTX type a second. config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_PROTO_ISO14443_B:
+ result = st95hf_send_recv_cmd(st95context,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ 2,
+ new_params,
+ true);
+ if (result) {
+ dev_err(dev, "WTX type b sel proto, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4B after protocol select */
+ result = secondary_configuration_type4b(st95context);
+ if (result) {
+ dev_err(dev, "WTX type b second. config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int st95hf_handle_wtx(struct st95hf_context *stcontext,
+ bool new_wtx,
+ int wtx_val)
+{
+ int result = 0;
+ unsigned char val_mm = 0;
+ struct param_list new_params[1];
+ struct nfc_digital_dev *nfcddev = stcontext->ddev;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ if (new_wtx) {
+ result = iso14443_config_fdt(stcontext, wtx_val & 0x3f);
+ if (result) {
+ dev_err(dev, "Config. setting error on WTX req, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* Send response of wtx with ASYNC as no response expected */
+ new_params[0].param_offset = 1;
+ new_params[0].new_param_val = wtx_val;
+
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_WTX_RESPONSE,
+ 1,
+ new_params,
+ false);
+ if (result)
+ dev_err(dev, "WTX response send, err = 0x%x\n", result);
+ return result;
+ }
+
+ /* if no new wtx, cofigure with default values */
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443)
+ val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3];
+ else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B)
+ val_mm = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[3];
+
+ result = iso14443_config_fdt(stcontext, val_mm);
+ if (result)
+ dev_err(dev, "Default config. setting error after WTX processing, err = 0x%x\n",
+ result);
+
+ return result;
+}
+
+static int st95hf_error_handling(struct st95hf_context *stcontext,
+ struct sk_buff *skb_resp,
+ int res_len)
+{
+ int result = 0;
+ unsigned char error_byte;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ /* First check ST95HF specific error */
+ if (skb_resp->data[0] & ST95HF_ERR_MASK) {
+ if (skb_resp->data[0] == ST95HF_TIMEOUT_ERROR)
+ result = -ETIMEDOUT;
+ else
+ result = -EIO;
+ return result;
+ }
+
+ /* Check for CRC err only if CRC is present in the tag response */
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) {
+ error_byte = skb_resp->data[res_len - 3];
+ if (error_byte & ST95HF_NFCA_CRC_ERR_MASK) {
+ /* CRC error occurred */
+ dev_err(dev, "CRC error, byte received = 0x%x\n",
+ error_byte);
+ result = -EIO;
+ }
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ error_byte = skb_resp->data[res_len - 1];
+ if (error_byte & ST95HF_NFCB_CRC_ERR_MASK) {
+ /* CRC error occurred */
+ dev_err(dev, "CRC error, byte received = 0x%x\n",
+ error_byte);
+ result = -EIO;
+ }
+ break;
+ }
+
+ return result;
+}
+
+static int st95hf_response_handler(struct st95hf_context *stcontext,
+ struct sk_buff *skb_resp,
+ int res_len)
+{
+ int result = 0;
+ int skb_len;
+ unsigned char val_mm;
+ struct nfc_digital_dev *nfcddev = stcontext->ddev;
+ struct device *dev = &stcontext->nfcdev->dev;
+ struct st95_digital_cmd_complete_arg *cb_arg;
+
+ cb_arg = &stcontext->complete_cb_arg;
+
+ /* Process the response */
+ skb_put(skb_resp, res_len);
+
+ /* Remove st95 header */
+ skb_pull(skb_resp, 2);
+
+ skb_len = skb_resp->len;
+
+ /* check if it is case of RATS request reply & FWI is present */
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && cb_arg->rats &&
+ (skb_resp->data[1] & RATS_TB1_PRESENT_MASK)) {
+ if (skb_resp->data[1] & RATS_TA1_PRESENT_MASK)
+ stcontext->fwi =
+ (skb_resp->data[3] & TB1_FWI_MASK) >> 4;
+ else
+ stcontext->fwi =
+ (skb_resp->data[2] & TB1_FWI_MASK) >> 4;
+
+ val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3];
+
+ result = iso14443_config_fdt(stcontext, val_mm);
+ if (result) {
+ dev_err(dev, "error in config_fdt to handle fwi of ATS, error=%d\n",
+ result);
+ return result;
+ }
+ }
+ cb_arg->rats = false;
+
+ /* Remove CRC bytes only if received frames data has an eod (CRC) */
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC)
+ skb_trim(skb_resp, (skb_len - 5));
+ else
+ skb_trim(skb_resp, (skb_len - 3));
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ skb_trim(skb_resp, (skb_len - 3));
+ break;
+ }
+
+ return result;
+}
+
+static irqreturn_t st95hf_irq_handler(int irq, void *st95hfcontext)
+{
+ struct st95hf_context *stcontext =
+ (struct st95hf_context *)st95hfcontext;
+
+ if (stcontext->spicontext.req_issync) {
+ complete(&stcontext->spicontext.done);
+ stcontext->spicontext.req_issync = false;
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext)
+{
+ int result = 0;
+ int res_len;
+ static bool wtx;
+ struct device *dev;
+ struct device *spidevice;
+ struct nfc_digital_dev *nfcddev;
+ struct sk_buff *skb_resp;
+ struct st95hf_context *stcontext =
+ (struct st95hf_context *)st95hfcontext;
+ struct st95_digital_cmd_complete_arg *cb_arg;
+
+ spidevice = &stcontext->spicontext.spidev->dev;
+
+ /*
+ * check semaphore, if not down() already, then we don't
+ * know in which context the ISR is called and surely it
+ * will be a bug. Note that down() of the semaphore is done
+ * in the corresponding st95hf_in_send_cmd() and then
+ * only this ISR should be called. ISR will up() the
+ * semaphore before leaving. Hence when the ISR is called
+ * the correct behaviour is down_trylock() should always
+ * return 1 (indicating semaphore cant be taken and hence no
+ * change in semaphore count).
+ * If not, then we up() the semaphore and crash on
+ * a BUG() !
+ */
+ if (!down_trylock(&stcontext->exchange_lock)) {
+ up(&stcontext->exchange_lock);
+ WARN(1, "unknown context in ST95HF ISR");
+ return IRQ_NONE;
+ }
+
+ cb_arg = &stcontext->complete_cb_arg;
+ skb_resp = cb_arg->skb_resp;
+
+ mutex_lock(&stcontext->rm_lock);
+ res_len = st95hf_spi_recv_response(&stcontext->spicontext,
+ skb_resp->data);
+ if (res_len < 0) {
+ dev_err(spidevice, "TISR spi response err = 0x%x\n", res_len);
+ result = res_len;
+ goto end;
+ }
+
+ /* if stcontext->nfcdev_free is true, it means remove already ran */
+ if (stcontext->nfcdev_free) {
+ result = -ENODEV;
+ goto end;
+ }
+
+ dev = &stcontext->nfcdev->dev;
+ nfcddev = stcontext->ddev;
+ if (skb_resp->data[2] == WTX_REQ_FROM_TAG) {
+ /* Request for new FWT from tag */
+ result = st95hf_handle_wtx(stcontext, true, skb_resp->data[3]);
+ if (result)
+ goto end;
+
+ wtx = true;
+ mutex_unlock(&stcontext->rm_lock);
+ return IRQ_HANDLED;
+ }
+
+ result = st95hf_error_handling(stcontext, skb_resp, res_len);
+ if (result)
+ goto end;
+
+ result = st95hf_response_handler(stcontext, skb_resp, res_len);
+ if (result)
+ goto end;
+
+ /*
+ * If select protocol is done on wtx req. do select protocol
+ * again with default values
+ */
+ if (wtx) {
+ wtx = false;
+ result = st95hf_handle_wtx(stcontext, false, 0);
+ if (result)
+ goto end;
+ }
+
+ /* call digital layer callback */
+ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp);
+
+ /* up the semaphore before returning */
+ up(&stcontext->exchange_lock);
+ mutex_unlock(&stcontext->rm_lock);
+
+ return IRQ_HANDLED;
+
+end:
+ kfree_skb(skb_resp);
+ wtx = false;
+ cb_arg->rats = false;
+ skb_resp = ERR_PTR(result);
+ /* call of callback with error */
+ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp);
+ /* up the semaphore before returning */
+ up(&stcontext->exchange_lock);
+ mutex_unlock(&stcontext->rm_lock);
+ return IRQ_HANDLED;
+}
+
+/* NFC ops functions definition */
+static int st95hf_in_configure_hw(struct nfc_digital_dev *ddev,
+ int type,
+ int param)
+{
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+
+ if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+ return st95hf_select_protocol(stcontext, param);
+
+ if (type == NFC_DIGITAL_CONFIG_FRAMING) {
+ switch (param) {
+ case NFC_DIGITAL_FRAMING_NFCA_SHORT:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_T4T:
+ case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP:
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCB:
+ case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY:
+ case NFC_DIGITAL_FRAMING_ISO15693_T5T:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rf_off(struct st95hf_context *stcontext)
+{
+ int rc;
+ struct device *dev;
+
+ dev = &stcontext->nfcdev->dev;
+
+ rc = st95hf_send_recv_cmd(stcontext, CMD_FIELD_OFF, 0, NULL, true);
+ if (rc)
+ dev_err(dev, "protocol sel send field off, err = 0x%x\n", rc);
+
+ return rc;
+}
+
+static int st95hf_in_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+ int rc;
+ struct sk_buff *skb_resp;
+ int len_data_to_tag = 0;
+
+ skb_resp = nfc_alloc_recv_skb(MAX_RESPONSE_BUFFER_SIZE, GFP_KERNEL);
+ if (!skb_resp) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ len_data_to_tag = skb->len + 1;
+ *skb_put(skb, 1) = stcontext->sendrcv_trflag;
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ len_data_to_tag = skb->len;
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_skb_resp;
+ }
+
+ skb_push(skb, 3);
+ skb->data[0] = ST95HF_COMMAND_SEND;
+ skb->data[1] = SEND_RECEIVE_CMD;
+ skb->data[2] = len_data_to_tag;
+
+ stcontext->complete_cb_arg.skb_resp = skb_resp;
+ stcontext->complete_cb_arg.cb_usrarg = arg;
+ stcontext->complete_cb_arg.complete_cb = cb;
+
+ if ((skb->data[3] == ISO14443A_RATS_REQ) &&
+ ddev->curr_protocol == NFC_PROTO_ISO14443)
+ stcontext->complete_cb_arg.rats = true;
+
+ /*
+ * down the semaphore to indicate to remove func that an
+ * ISR is pending, note that it will not block here in any case.
+ * If found blocked, it is a BUG!
+ */
+ rc = down_killable(&stcontext->exchange_lock);
+ if (rc) {
+ WARN(1, "Semaphore is not found up in st95hf_in_send_cmd\n");
+ return rc;
+ }
+
+ rc = st95hf_spi_send(&stcontext->spicontext, skb->data,
+ skb->len,
+ ASYNC);
+ if (rc) {
+ dev_err(&stcontext->nfcdev->dev,
+ "Error %d trying to perform data_exchange", rc);
+ /* up the semaphore since ISR will never come in this case */
+ up(&stcontext->exchange_lock);
+ goto free_skb_resp;
+ }
+
+ kfree_skb(skb);
+
+ return rc;
+
+free_skb_resp:
+ kfree_skb(skb_resp);
+error:
+ return rc;
+}
+
+/* p2p will be supported in a later release ! */
+static int st95hf_tg_configure_hw(struct nfc_digital_dev *ddev,
+ int type,
+ int param)
+{
+ return 0;
+}
+
+static int st95hf_tg_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ return 0;
+}
+
+static int st95hf_tg_listen(struct nfc_digital_dev *ddev,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ return 0;
+}
+
+static int st95hf_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech)
+{
+ return 0;
+}
+
+static int st95hf_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+ u8 rf_tech;
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+
+ rf_tech = ddev->curr_rf_tech;
+
+ if (on)
+ /* switch on RF field */
+ return st95hf_select_protocol(stcontext, rf_tech);
+
+ /* switch OFF RF field */
+ return rf_off(stcontext);
+}
+
+/* TODO st95hf_abort_cmd */
+static void st95hf_abort_cmd(struct nfc_digital_dev *ddev)
+{
+}
+
+static struct nfc_digital_ops st95hf_nfc_digital_ops = {
+ .in_configure_hw = st95hf_in_configure_hw,
+ .in_send_cmd = st95hf_in_send_cmd,
+
+ .tg_listen = st95hf_tg_listen,
+ .tg_configure_hw = st95hf_tg_configure_hw,
+ .tg_send_cmd = st95hf_tg_send_cmd,
+ .tg_get_rf_tech = st95hf_tg_get_rf_tech,
+
+ .switch_rf = st95hf_switch_rf,
+ .abort_cmd = st95hf_abort_cmd,
+};
+
+static const struct spi_device_id st95hf_id[] = {
+ { "st95hf", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, st95hf_id);
+
+static int st95hf_probe(struct spi_device *nfc_spi_dev)
+{
+ int ret;
+
+ struct st95hf_context *st95context;
+ struct st95hf_spi_context *spicontext;
+
+ nfc_info(&nfc_spi_dev->dev, "ST95HF driver probe called.\n");
+
+ st95context = devm_kzalloc(&nfc_spi_dev->dev,
+ sizeof(struct st95hf_context),
+ GFP_KERNEL);
+ if (!st95context)
+ return -ENOMEM;
+
+ spicontext = &st95context->spicontext;
+
+ spicontext->spidev = nfc_spi_dev;
+
+ st95context->fwi =
+ cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[2];
+
+ if (device_property_present(&nfc_spi_dev->dev, "st95hfvin")) {
+ st95context->st95hf_supply =
+ devm_regulator_get(&nfc_spi_dev->dev,
+ "st95hfvin");
+ if (IS_ERR(st95context->st95hf_supply)) {
+ dev_err(&nfc_spi_dev->dev, "failed to acquire regulator\n");
+ return PTR_ERR(st95context->st95hf_supply);
+ }
+
+ ret = regulator_enable(st95context->st95hf_supply);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "failed to enable regulator\n");
+ return ret;
+ }
+ }
+
+ init_completion(&spicontext->done);
+ mutex_init(&spicontext->spi_lock);
+
+ /*
+ * Store spicontext in spi device object for using it in
+ * remove function
+ */
+ dev_set_drvdata(&nfc_spi_dev->dev, spicontext);
+
+ st95context->enable_gpio =
+ of_get_named_gpio(nfc_spi_dev->dev.of_node,
+ "enable-gpio",
+ 0);
+ if (!gpio_is_valid(st95context->enable_gpio)) {
+ dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n");
+ ret = st95context->enable_gpio;
+ goto err_disable_regulator;
+ }
+
+ ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
+ "enable_gpio");
+ if (ret)
+ goto err_disable_regulator;
+
+ if (nfc_spi_dev->irq > 0) {
+ if (devm_request_threaded_irq(&nfc_spi_dev->dev,
+ nfc_spi_dev->irq,
+ st95hf_irq_handler,
+ st95hf_irq_thread_handler,
+ IRQF_TRIGGER_FALLING,
+ "st95hf",
+ (void *)st95context) < 0) {
+ dev_err(&nfc_spi_dev->dev, "err: irq request for st95hf is failed\n");
+ ret = -EINVAL;
+ goto err_disable_regulator;
+ }
+ } else {
+ dev_err(&nfc_spi_dev->dev, "not a valid IRQ associated with ST95HF\n");
+ ret = -EINVAL;
+ goto err_disable_regulator;
+ }
+
+ /*
+ * First reset SPI to handle warm reset of the system.
+ * It will put the ST95HF device in Power ON state
+ * which make the state of device identical to state
+ * at the time of cold reset of the system.
+ */
+ ret = st95hf_send_spi_reset_sequence(st95context);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "err: spi_reset_sequence failed\n");
+ goto err_disable_regulator;
+ }
+
+ /* call PowerOnReset sequence of ST95hf to activate it */
+ ret = st95hf_por_sequence(st95context);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "err: por seq failed for st95hf\n");
+ goto err_disable_regulator;
+ }
+
+ /* create NFC dev object and register with NFC Subsystem */
+ st95context->ddev = nfc_digital_allocate_device(&st95hf_nfc_digital_ops,
+ ST95HF_SUPPORTED_PROT,
+ ST95HF_CAPABILITIES,
+ ST95HF_HEADROOM_LEN,
+ ST95HF_TAILROOM_LEN);
+ if (!st95context->ddev) {
+ ret = -ENOMEM;
+ goto err_disable_regulator;
+ }
+
+ st95context->nfcdev = st95context->ddev->nfc_dev;
+ nfc_digital_set_parent_dev(st95context->ddev, &nfc_spi_dev->dev);
+
+ ret = nfc_digital_register_device(st95context->ddev);
+ if (ret) {
+ dev_err(&st95context->nfcdev->dev, "st95hf registration failed\n");
+ goto err_free_digital_device;
+ }
+
+ /* store st95context in nfc device object */
+ nfc_digital_set_drvdata(st95context->ddev, st95context);
+
+ sema_init(&st95context->exchange_lock, 1);
+ mutex_init(&st95context->rm_lock);
+
+ return ret;
+
+err_free_digital_device:
+ nfc_digital_free_device(st95context->ddev);
+err_disable_regulator:
+ if (st95context->st95hf_supply)
+ regulator_disable(st95context->st95hf_supply);
+
+ return ret;
+}
+
+static int st95hf_remove(struct spi_device *nfc_spi_dev)
+{
+ int result = 0;
+ unsigned char reset_cmd = ST95HF_COMMAND_RESET;
+ struct st95hf_spi_context *spictx = dev_get_drvdata(&nfc_spi_dev->dev);
+
+ struct st95hf_context *stcontext = container_of(spictx,
+ struct st95hf_context,
+ spicontext);
+
+ mutex_lock(&stcontext->rm_lock);
+
+ nfc_digital_unregister_device(stcontext->ddev);
+ nfc_digital_free_device(stcontext->ddev);
+ stcontext->nfcdev_free = true;
+
+ mutex_unlock(&stcontext->rm_lock);
+
+ /* if last in_send_cmd's ISR is pending, wait for it to finish */
+ result = down_killable(&stcontext->exchange_lock);
+ if (result == -EINTR)
+ dev_err(&spictx->spidev->dev, "sleep for semaphore interrupted by signal\n");
+
+ /* next reset the ST95HF controller */
+ result = st95hf_spi_send(&stcontext->spicontext,
+ &reset_cmd,
+ ST95HF_RESET_CMD_LEN,
+ ASYNC);
+ if (result) {
+ dev_err(&spictx->spidev->dev,
+ "ST95HF reset failed in remove() err = %d\n", result);
+ return result;
+ }
+
+ /* wait for 3 ms to complete the controller reset process */
+ usleep_range(3000, 4000);
+
+ /* disable regulator */
+ if (stcontext->st95hf_supply)
+ regulator_disable(stcontext->st95hf_supply);
+
+ return result;
+}
+
+/* Register as SPI protocol driver */
+static struct spi_driver st95hf_driver = {
+ .driver = {
+ .name = "st95hf",
+ .owner = THIS_MODULE,
+ },
+ .id_table = st95hf_id,
+ .probe = st95hf_probe,
+ .remove = st95hf_remove,
+};
+
+module_spi_driver(st95hf_driver);
+
+MODULE_AUTHOR("Shikha Singh <shikha.singh@st.com>");
+MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/nfc/st95hf/spi.c b/drivers/nfc/st95hf/spi.c
new file mode 100644
index 000000000000..e2d3bbcc8c34
--- /dev/null
+++ b/drivers/nfc/st95hf/spi.c
@@ -0,0 +1,167 @@
+/*
+ * ----------------------------------------------------------------------------
+ * drivers/nfc/st95hf/spi.c function definitions for SPI communication
+ * ----------------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. 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 that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "spi.h"
+
+/* Function to send user provided buffer to ST95HF through SPI */
+int st95hf_spi_send(struct st95hf_spi_context *spicontext,
+ unsigned char *buffertx,
+ int datalen,
+ enum req_type reqtype)
+{
+ struct spi_message m;
+ int result = 0;
+ struct spi_device *spidev = spicontext->spidev;
+ struct spi_transfer tx_transfer = {
+ .tx_buf = buffertx,
+ .len = datalen,
+ };
+
+ mutex_lock(&spicontext->spi_lock);
+
+ if (reqtype == SYNC) {
+ spicontext->req_issync = true;
+ reinit_completion(&spicontext->done);
+ } else {
+ spicontext->req_issync = false;
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&tx_transfer, &m);
+
+ result = spi_sync(spidev, &m);
+ if (result) {
+ dev_err(&spidev->dev, "error: sending cmd to st95hf using SPI = %d\n",
+ result);
+ mutex_unlock(&spicontext->spi_lock);
+ return result;
+ }
+
+ /* return for asynchronous or no-wait case */
+ if (reqtype == ASYNC) {
+ mutex_unlock(&spicontext->spi_lock);
+ return 0;
+ }
+
+ result = wait_for_completion_timeout(&spicontext->done,
+ msecs_to_jiffies(1000));
+ /* check for timeout or success */
+ if (!result) {
+ dev_err(&spidev->dev, "error: response not ready timeout\n");
+ result = -ETIMEDOUT;
+ } else {
+ result = 0;
+ }
+
+ mutex_unlock(&spicontext->spi_lock);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_send);
+
+/* Function to Receive command Response */
+int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff)
+{
+ int len = 0;
+ struct spi_transfer tx_takedata;
+ struct spi_message m;
+ struct spi_device *spidev = spicontext->spidev;
+ unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
+ struct spi_transfer t[2] = {
+ {.tx_buf = &readdata_cmd, .len = 1,},
+ {.rx_buf = receivebuff, .len = 2, .cs_change = 1,},
+ };
+
+ int ret = 0;
+
+ memset(&tx_takedata, 0x0, sizeof(struct spi_transfer));
+
+ mutex_lock(&spicontext->spi_lock);
+
+ /* First spi transfer to know the length of valid data */
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(spidev, &m);
+ if (ret) {
+ dev_err(&spidev->dev, "spi_recv_resp, data length error = %d\n",
+ ret);
+ mutex_unlock(&spicontext->spi_lock);
+ return ret;
+ }
+
+ /* As 2 bytes are already read */
+ len = 2;
+
+ /* Support of long frame */
+ if (receivebuff[0] & 0x60)
+ len += (((receivebuff[0] & 0x60) >> 5) << 8) | receivebuff[1];
+ else
+ len += receivebuff[1];
+
+ /* Now make a transfer to read only relevant bytes */
+ tx_takedata.rx_buf = &receivebuff[2];
+ tx_takedata.len = len - 2;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&tx_takedata, &m);
+
+ ret = spi_sync(spidev, &m);
+
+ mutex_unlock(&spicontext->spi_lock);
+ if (ret) {
+ dev_err(&spidev->dev, "spi_recv_resp, data read error = %d\n",
+ ret);
+ return ret;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_recv_response);
+
+int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff)
+{
+ unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
+ struct spi_transfer t[2] = {
+ {.tx_buf = &readdata_cmd, .len = 1,},
+ {.rx_buf = receivebuff, .len = 1,},
+ };
+ struct spi_message m;
+ struct spi_device *spidev = spicontext->spidev;
+ int ret = 0;
+
+ mutex_lock(&spicontext->spi_lock);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+ ret = spi_sync(spidev, &m);
+
+ mutex_unlock(&spicontext->spi_lock);
+
+ if (ret)
+ dev_err(&spidev->dev, "recv_echo_res, data read error = %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_recv_echo_res);
diff --git a/drivers/nfc/st95hf/spi.h b/drivers/nfc/st95hf/spi.h
new file mode 100644
index 000000000000..552d220747cd
--- /dev/null
+++ b/drivers/nfc/st95hf/spi.h
@@ -0,0 +1,64 @@
+/*
+ * ---------------------------------------------------------------------------
+ * drivers/nfc/st95hf/spi.h functions declarations for SPI communication
+ * ---------------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics – 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 that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_ST95HF_SPI_H
+#define __LINUX_ST95HF_SPI_H
+
+#include <linux/spi/spi.h>
+
+/* Basic ST95HF SPI CMDs */
+#define ST95HF_COMMAND_SEND 0x0
+#define ST95HF_COMMAND_RESET 0x1
+#define ST95HF_COMMAND_RECEIVE 0x2
+
+#define ST95HF_RESET_CMD_LEN 0x1
+
+/*
+ * structure to contain st95hf spi communication specific information.
+ * @req_issync: true for synchronous calls.
+ * @spidev: st95hf spi device object.
+ * @done: completion structure to wait for st95hf response
+ * for synchronous calls.
+ * @spi_lock: mutex to allow only one spi transfer at a time.
+ */
+struct st95hf_spi_context {
+ bool req_issync;
+ struct spi_device *spidev;
+ struct completion done;
+ struct mutex spi_lock;
+};
+
+/* flag to differentiate synchronous & asynchronous spi request */
+enum req_type {
+ SYNC,
+ ASYNC,
+};
+
+int st95hf_spi_send(struct st95hf_spi_context *spicontext,
+ unsigned char *buffertx,
+ int datalen,
+ enum req_type reqtype);
+
+int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff);
+
+int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff);
+
+#endif
diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
index f857feb2b573..10842b7051b3 100644
--- a/drivers/nfc/trf7970a.c
+++ b/drivers/nfc/trf7970a.c
@@ -2139,7 +2139,7 @@ static int trf7970a_remove(struct spi_device *spi)
#ifdef CONFIG_PM_SLEEP
static int trf7970a_suspend(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
dev_dbg(dev, "Suspend\n");
@@ -2155,7 +2155,7 @@ static int trf7970a_suspend(struct device *dev)
static int trf7970a_resume(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
int ret;
@@ -2174,7 +2174,7 @@ static int trf7970a_resume(struct device *dev)
#ifdef CONFIG_PM
static int trf7970a_pm_runtime_suspend(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
int ret;
@@ -2191,7 +2191,7 @@ static int trf7970a_pm_runtime_suspend(struct device *dev)
static int trf7970a_pm_runtime_resume(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
int ret;
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index a87a868fed64..86829f8064a6 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -62,11 +62,9 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
rc = irq_of_parse_and_map(child, 0);
if (rc > 0) {
phy->irq = rc;
- if (mdio->irq)
- mdio->irq[addr] = rc;
+ mdio->irq[addr] = rc;
} else {
- if (mdio->irq)
- phy->irq = mdio->irq[addr];
+ phy->irq = mdio->irq[addr];
}
if (of_property_read_bool(child, "broken-turn-around"))
@@ -75,7 +73,7 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
/* Associate the OF node with the device structure so it
* can be looked up later */
of_node_get(child);
- phy->dev.of_node = child;
+ phy->mdio.dev.of_node = child;
/* All data is now stored in the phy struct;
* register it */
@@ -92,6 +90,37 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
return 0;
}
+static int of_mdiobus_register_device(struct mii_bus *mdio,
+ struct device_node *child,
+ u32 addr)
+{
+ struct mdio_device *mdiodev;
+ int rc;
+
+ mdiodev = mdio_device_create(mdio, addr);
+ if (!mdiodev || IS_ERR(mdiodev))
+ return 1;
+
+ /* Associate the OF node with the device structure so it
+ * can be looked up later.
+ */
+ of_node_get(child);
+ mdiodev->dev.of_node = child;
+
+ /* All data is now stored in the mdiodev struct; register it. */
+ rc = mdio_device_register(mdiodev);
+ if (rc) {
+ mdio_device_free(mdiodev);
+ of_node_put(child);
+ return 1;
+ }
+
+ dev_dbg(&mdio->dev, "registered mdio device %s at address %i\n",
+ child->name, addr);
+
+ return 0;
+}
+
int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
{
u32 addr;
@@ -114,6 +143,35 @@ int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
}
EXPORT_SYMBOL(of_mdio_parse_addr);
+/*
+ * Return true if the child node is for a phy. It must either:
+ * o Compatible string of "ethernet-phy-idX.X"
+ * o Compatible string of "ethernet-phy-ieee802.3-c45"
+ * o Compatible string of "ethernet-phy-ieee802.3-c22"
+ * o No compatibility string
+ *
+ * A device which is not a phy is expected to have a compatible string
+ * indicating what sort of device it is.
+ */
+static bool of_mdiobus_child_is_phy(struct device_node *child)
+{
+ u32 phy_id;
+
+ if (of_get_phy_id(child, &phy_id) != -EINVAL)
+ return true;
+
+ if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"))
+ return true;
+
+ if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c22"))
+ return true;
+
+ if (!of_find_property(child, "compatible", NULL))
+ return true;
+
+ return false;
+}
+
/**
* of_mdiobus_register - Register mii_bus and create PHYs from the device tree
* @mdio: pointer to mii_bus structure
@@ -127,17 +185,12 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
struct device_node *child;
const __be32 *paddr;
bool scanphys = false;
- int addr, rc, i;
+ int addr, rc;
/* Mask out all PHYs from auto probing. Instead the PHYs listed in
* the device tree are populated after the bus has been registered */
mdio->phy_mask = ~0;
- /* Clear all the IRQ properties */
- if (mdio->irq)
- for (i=0; i<PHY_MAX_ADDR; i++)
- mdio->irq[i] = PHY_POLL;
-
mdio->dev.of_node = np;
/* Register the MDIO bus */
@@ -145,7 +198,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
if (rc)
return rc;
- /* Loop over the child nodes and register a phy_device for each one */
+ /* Loop over the child nodes and register a phy_device for each phy */
for_each_available_child_of_node(np, child) {
addr = of_mdio_parse_addr(&mdio->dev, child);
if (addr < 0) {
@@ -153,9 +206,10 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
continue;
}
- rc = of_mdiobus_register_phy(mdio, child, addr);
- if (rc)
- continue;
+ if (of_mdiobus_child_is_phy(child))
+ of_mdiobus_register_phy(mdio, child, addr);
+ else
+ of_mdiobus_register_device(mdio, child, addr);
}
if (!scanphys)
@@ -170,16 +224,15 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
/* skip already registered PHYs */
- if (mdio->phy_map[addr])
+ if (mdiobus_is_registered_device(mdio, addr))
continue;
/* be noisy to encourage people to set reg property */
dev_info(&mdio->dev, "scan phy %s at address %i\n",
child->name, addr);
- rc = of_mdiobus_register_phy(mdio, child, addr);
- if (rc)
- continue;
+ if (of_mdiobus_child_is_phy(child))
+ of_mdiobus_register_phy(mdio, child, addr);
}
}
@@ -238,7 +291,7 @@ struct phy_device *of_phy_connect(struct net_device *dev,
ret = phy_connect_direct(dev, phy, hndlr, iface);
/* refcount is held by phy_connect_direct() on success */
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
return ret ? NULL : phy;
}
@@ -268,7 +321,7 @@ struct phy_device *of_phy_attach(struct net_device *dev,
ret = phy_attach_direct(dev, phy, flags, iface);
/* refcount is held by phy_attach_direct() on success */
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
return ret ? NULL : phy;
}
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index 05c37d6d4afe..c3e22523faf3 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -1677,11 +1677,8 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
ccw_device_set_offline(cgdev->cdev[1]);
ccw_device_set_offline(cgdev->cdev[0]);
-
- if (priv->channel[CTCM_READ])
- channel_remove(priv->channel[CTCM_READ]);
- if (priv->channel[CTCM_WRITE])
- channel_remove(priv->channel[CTCM_WRITE]);
+ channel_remove(priv->channel[CTCM_READ]);
+ channel_remove(priv->channel[CTCM_WRITE]);
priv->channel[CTCM_READ] = priv->channel[CTCM_WRITE] = NULL;
return 0;
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 1766a20ebcb1..ec2e014e885c 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -981,6 +981,10 @@ int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16,
int (*reply_cb)(struct qeth_card *,
struct qeth_reply *, unsigned long),
void *);
+struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *,
+ enum qeth_ipa_funcs,
+ __u16, __u16,
+ enum qeth_prot_versions);
int qeth_start_ipa_tx_checksum(struct qeth_card *);
int qeth_set_rx_csum(struct qeth_card *, int);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 31ac53fa5cee..787153764120 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -2684,8 +2684,6 @@ void qeth_print_status_message(struct qeth_card *card)
sprintf(card->info.mcl_level, "%02x%02x",
card->info.mcl_level[2],
card->info.mcl_level[3]);
-
- card->info.mcl_level[QETH_MCL_LENGTH] = 0;
break;
}
/* fallthrough */
@@ -5297,10 +5295,10 @@ static int qeth_setassparms_cb(struct qeth_card *card,
return 0;
}
-static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
- enum qeth_ipa_funcs ipa_func,
- __u16 cmd_code, __u16 len,
- enum qeth_prot_versions prot)
+struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, __u16 len,
+ enum qeth_prot_versions prot)
{
struct qeth_cmd_buffer *iob;
struct qeth_ipa_cmd *cmd;
@@ -5319,6 +5317,7 @@ static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
return iob;
}
+EXPORT_SYMBOL_GPL(qeth_get_setassparms_cmd);
int qeth_send_setassparms(struct qeth_card *card,
struct qeth_cmd_buffer *iob, __u16 len, long data,
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 8f1b091e1732..80b1979e8d95 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1126,6 +1126,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
qeth_l2_request_initial_mac(card);
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT);
+ netif_carrier_off(card->dev);
return register_netdev(card->dev);
}
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 543960e96b42..7c8c68c26540 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1043,28 +1043,6 @@ static int qeth_l3_default_setassparms_cb(struct qeth_card *card,
return 0;
}
-static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd(
- struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code,
- __u16 len, enum qeth_prot_versions prot)
-{
- struct qeth_cmd_buffer *iob;
- struct qeth_ipa_cmd *cmd;
-
- QETH_CARD_TEXT(card, 4, "getasscm");
- iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot);
-
- if (iob) {
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- cmd->data.setassparms.hdr.assist_no = ipa_func;
- cmd->data.setassparms.hdr.length = 8 + len;
- cmd->data.setassparms.hdr.command_code = cmd_code;
- cmd->data.setassparms.hdr.return_code = 0;
- cmd->data.setassparms.hdr.seq_no = 0;
- }
-
- return iob;
-}
-
#ifdef CONFIG_QETH_IPV6
static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
enum qeth_ipa_funcs ipa_func, __u16 cmd_code)
@@ -1073,7 +1051,7 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
struct qeth_cmd_buffer *iob;
QETH_CARD_TEXT(card, 4, "simassp6");
- iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code,
+ iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
0, QETH_PROT_IPV6);
if (!iob)
return -ENOMEM;
@@ -2344,10 +2322,11 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot);
- iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_QUERY_INFO,
- sizeof(struct qeth_arp_query_data) - sizeof(char),
- prot);
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_QUERY_INFO,
+ sizeof(struct qeth_arp_query_data)
+ - sizeof(char),
+ prot);
if (!iob)
return -ENOMEM;
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -2439,7 +2418,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card,
return -EOPNOTSUPP;
}
- iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_ADD_ENTRY,
sizeof(struct qeth_arp_cache_entry),
QETH_PROT_IPV4);
@@ -2480,7 +2459,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card,
return -EOPNOTSUPP;
}
memcpy(buf, entry, 12);
- iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_REMOVE_ENTRY,
12,
QETH_PROT_IPV4);
@@ -2818,7 +2797,7 @@ static inline int qeth_l3_tso_elements(struct sk_buff *skb)
{
unsigned long tcpd = (unsigned long)tcp_hdr(skb) +
tcp_hdr(skb)->doff * 4;
- int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data);
+ int tcpd_len = skb_headlen(skb) - (tcpd - (unsigned long)skb->data);
int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd);
elements += qeth_get_elements_for_frags(skb);
@@ -3220,6 +3199,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
netif_napi_add(card->dev, &card->napi, qeth_l3_poll, QETH_NAPI_WEIGHT);
+ netif_carrier_off(card->dev);
return register_netdev(card->dev);
}
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index f4424063b860..0efe7112fc1f 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1625,7 +1625,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
/* crc offload */
if (likely(lport->crc_offload)) {
- skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb);
skb->csum_offset = skb->len;
crc = 0;
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 149214beeda9..0c675861623f 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -82,7 +82,7 @@ config SSB_SDIOHOST
config SSB_HOST_SOC
bool "Support for SSB bus on SoC"
- depends on SSB
+ depends on SSB && BCM47XX_NVRAM
help
Host interface for a SSB directly mapped into memory. This is
for some Broadcom SoCs from the BCM47xx and BCM53xx lines.
diff --git a/drivers/ssb/host_soc.c b/drivers/ssb/host_soc.c
index c809f255af34..d62992dc08b2 100644
--- a/drivers/ssb/host_soc.c
+++ b/drivers/ssb/host_soc.c
@@ -8,6 +8,7 @@
* Licensed under the GNU/GPL. See COPYING for details.
*/
+#include <linux/bcm47xx_nvram.h>
#include <linux/ssb/ssb.h>
#include "ssb_private.h"
@@ -171,3 +172,39 @@ const struct ssb_bus_ops ssb_host_soc_ops = {
.block_write = ssb_host_soc_block_write,
#endif
};
+
+int ssb_host_soc_get_invariants(struct ssb_bus *bus,
+ struct ssb_init_invariants *iv)
+{
+ char buf[20];
+ int len, err;
+
+ /* Fill boardinfo structure */
+ memset(&iv->boardinfo, 0, sizeof(struct ssb_boardinfo));
+
+ len = bcm47xx_nvram_getenv("boardvendor", buf, sizeof(buf));
+ if (len > 0) {
+ err = kstrtou16(strim(buf), 0, &iv->boardinfo.vendor);
+ if (err)
+ pr_warn("Couldn't parse nvram board vendor entry with value \"%s\"\n",
+ buf);
+ }
+ if (!iv->boardinfo.vendor)
+ iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM;
+
+ len = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf));
+ if (len > 0) {
+ err = kstrtou16(strim(buf), 0, &iv->boardinfo.type);
+ if (err)
+ pr_warn("Couldn't parse nvram board type entry with value \"%s\"\n",
+ buf);
+ }
+
+ memset(&iv->sprom, 0, sizeof(struct ssb_sprom));
+ ssb_fill_sprom_with_fallback(bus, &iv->sprom);
+
+ if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
+ iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10);
+
+ return 0;
+}
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 5d1e9a0fc389..cde5ff7529eb 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -762,15 +762,14 @@ EXPORT_SYMBOL(ssb_bus_sdiobus_register);
#endif /* CONFIG_SSB_PCMCIAHOST */
#ifdef CONFIG_SSB_HOST_SOC
-int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr,
- ssb_invariants_func_t get_invariants)
+int ssb_bus_host_soc_register(struct ssb_bus *bus, unsigned long baseaddr)
{
int err;
bus->bustype = SSB_BUSTYPE_SSB;
bus->ops = &ssb_host_soc_ops;
- err = ssb_bus_register(bus, get_invariants, baseaddr);
+ err = ssb_bus_register(bus, ssb_host_soc_get_invariants, baseaddr);
if (!err) {
ssb_info("Sonics Silicon Backplane found at address 0x%08lX\n",
baseaddr);
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 15bfd5c7d2d7..c2f5d3969c8b 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -163,6 +163,9 @@ static inline int ssb_sdio_init(struct ssb_bus *bus)
#ifdef CONFIG_SSB_HOST_SOC
extern const struct ssb_bus_ops ssb_host_soc_ops;
+
+extern int ssb_host_soc_get_invariants(struct ssb_bus *bus,
+ struct ssb_init_invariants *iv);
#endif
/* scan.c */
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
index 679785b0209c..9de4f23910d8 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
@@ -69,7 +69,7 @@ ksocknal_lib_zc_capable(ksock_conn_t *conn)
/* ZC if the socket supports scatter/gather and doesn't need software
* checksums */
- return ((caps & NETIF_F_SG) != 0 && (caps & NETIF_F_ALL_CSUM) != 0);
+ return ((caps & NETIF_F_SG) != 0 && (caps & NETIF_F_CSUM_MASK) != 0);
}
int
diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c
index 8ae01753b011..0b4e819f5164 100644
--- a/drivers/staging/netlogic/xlr_net.c
+++ b/drivers/staging/netlogic/xlr_net.c
@@ -165,13 +165,18 @@ static void xlr_net_fmn_handler(int bkt, int src_stnid, int size,
}
}
+static struct phy_device *xlr_get_phydev(struct xlr_net_priv *priv)
+{
+ return mdiobus_get_phy(priv->mii_bus, priv->phy_addr);
+}
+
/*
* Ethtool operation
*/
static int xlr_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (!phydev)
return -ENODEV;
@@ -181,7 +186,7 @@ static int xlr_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
static int xlr_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (!phydev)
return -ENODEV;
@@ -218,7 +223,7 @@ static int xlr_net_open(struct net_device *ndev)
{
u32 err;
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
/* schedule a link state check */
phy_start(phydev);
@@ -239,7 +244,7 @@ static int xlr_net_open(struct net_device *ndev)
static int xlr_net_stop(struct net_device *ndev)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
phy_stop(phydev);
netif_tx_stop_all_queues(ndev);
@@ -268,7 +273,7 @@ static void __maybe_unused xlr_wakeup_queue(unsigned long dev)
{
struct net_device *ndev = (struct net_device *) dev;
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (phydev->link)
netif_tx_wake_queue(netdev_get_tx_queue(ndev, priv->wakeup_q));
@@ -771,7 +776,7 @@ static void xlr_sgmii_init(struct xlr_net_priv *priv)
void xlr_set_gmac_speed(struct xlr_net_priv *priv)
{
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
int speed;
if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
@@ -813,7 +818,7 @@ void xlr_set_gmac_speed(struct xlr_net_priv *priv)
static void xlr_gmac_link_adjust(struct net_device *ndev)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
u32 intreg;
intreg = xlr_nae_rdreg(priv->base_addr, R_INTREG);
@@ -830,7 +835,7 @@ static void xlr_gmac_link_adjust(struct net_device *ndev)
static int xlr_mii_probe(struct xlr_net_priv *priv)
{
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (!phydev) {
pr_err("no PHY found on phy_addr %d\n", priv->phy_addr);
@@ -838,8 +843,8 @@ static int xlr_mii_probe(struct xlr_net_priv *priv)
}
/* Attach MAC to PHY */
- phydev = phy_connect(priv->ndev, dev_name(&phydev->dev),
- &xlr_gmac_link_adjust, priv->nd->phy_interface);
+ phydev = phy_connect(priv->ndev, phydev_name(phydev),
+ &xlr_gmac_link_adjust, priv->nd->phy_interface);
if (IS_ERR(phydev)) {
pr_err("could not attach PHY\n");
@@ -854,8 +859,7 @@ static int xlr_mii_probe(struct xlr_net_priv *priv)
| ADVERTISED_MII);
phydev->advertising = phydev->supported;
- pr_info("attached PHY driver [%s] (mii_bus:phy_addr=%s\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
}
@@ -877,14 +881,6 @@ static int xlr_setup_mdio(struct xlr_net_priv *priv,
priv->mii_bus->read = xlr_mii_read;
priv->mii_bus->write = xlr_mii_write;
priv->mii_bus->parent = &pdev->dev;
- priv->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (priv->mii_bus->irq == NULL) {
- pr_err("irq alloc failed\n");
- mdiobus_free(priv->mii_bus);
- return -ENOMEM;
- }
-
- priv->mii_bus->irq[priv->phy_addr] = priv->ndev->irq;
/* Scan only the enabled address */
priv->mii_bus->phy_mask = ~(1 << priv->phy_addr);
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c
index 613344b886e1..1055ee14b66a 100644
--- a/drivers/staging/octeon/ethernet-rgmii.c
+++ b/drivers/staging/octeon/ethernet-rgmii.c
@@ -78,7 +78,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
*/
spin_lock_irqsave(&global_register_lock, flags);
} else {
- mutex_lock(&priv->phydev->bus->mdio_lock);
+ mutex_lock(&priv->phydev->mdio.bus->mdio_lock);
}
link_info = cvmx_helper_link_get(priv->port);
@@ -113,7 +113,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
if (use_global_register_lock)
spin_unlock_irqrestore(&global_register_lock, flags);
else
- mutex_unlock(&priv->phydev->bus->mdio_lock);
+ mutex_unlock(&priv->phydev->mdio.bus->mdio_lock);
return;
}
@@ -132,7 +132,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
if (use_global_register_lock)
spin_unlock_irqrestore(&global_register_lock, flags);
else
- mutex_unlock(&priv->phydev->bus->mdio_lock);
+ mutex_unlock(&priv->phydev->mdio.bus->mdio_lock);
if (priv->phydev == NULL) {
/* Tell core. */
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 91e004518237..742bf4a230e8 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -694,6 +694,29 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
return NULL;
}
+static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
+ const unsigned char *path,
+ const void *ns)
+{
+ static char path_buf[PATH_MAX]; /* protected by kernfs_mutex */
+ size_t len = strlcpy(path_buf, path, PATH_MAX);
+ char *p = path_buf;
+ char *name;
+
+ lockdep_assert_held(&kernfs_mutex);
+
+ if (len >= PATH_MAX)
+ return NULL;
+
+ while ((name = strsep(&p, "/")) && parent) {
+ if (*name == '\0')
+ continue;
+ parent = kernfs_find_ns(parent, name, ns);
+ }
+
+ return parent;
+}
+
/**
* kernfs_find_and_get_ns - find and get kernfs_node with the given name
* @parent: kernfs_node to search under
@@ -719,6 +742,29 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
/**
+ * kernfs_walk_and_get_ns - find and get kernfs_node with the given path
+ * @parent: kernfs_node to search under
+ * @path: path to look for
+ * @ns: the namespace tag to use
+ *
+ * Look for kernfs_node with path @path under @parent and get a reference
+ * if found. This function may sleep and returns pointer to the found
+ * kernfs_node on success, %NULL on failure.
+ */
+struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
+ const char *path, const void *ns)
+{
+ struct kernfs_node *kn;
+
+ mutex_lock(&kernfs_mutex);
+ kn = kernfs_walk_ns(parent, path, ns);
+ kernfs_get(kn);
+ mutex_unlock(&kernfs_mutex);
+
+ return kn;
+}
+
+/**
* kernfs_create_root - create a new kernfs hierarchy
* @scops: optional syscall operations for the hierarchy
* @flags: KERNFS_ROOT_* flags
diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h
index 59f4a7304419..f0ba9c2ec639 100644
--- a/include/linux/brcmphy.h
+++ b/include/linux/brcmphy.h
@@ -26,6 +26,7 @@
#define PHY_ID_BCM7366 0x600d8490
#define PHY_ID_BCM7425 0x600d86b0
#define PHY_ID_BCM7429 0x600d8730
+#define PHY_ID_BCM7435 0x600d8750
#define PHY_ID_BCM7439 0x600d8480
#define PHY_ID_BCM7439_2 0xae025080
#define PHY_ID_BCM7445 0x600d8510
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index 06b77f9dd3f2..e5f4164cbd99 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -231,6 +231,14 @@ struct cgroup {
int id;
/*
+ * The depth this cgroup is at. The root is at depth zero and each
+ * step down the hierarchy increments the level. This along with
+ * ancestor_ids[] can determine whether a given cgroup is a
+ * descendant of another without traversing the hierarchy.
+ */
+ int level;
+
+ /*
* Each non-empty css_set associated with this cgroup contributes
* one to populated_cnt. All children with non-zero popuplated_cnt
* of their own contribute one. The count is zero iff there's no
@@ -285,6 +293,9 @@ struct cgroup {
/* used to schedule release agent */
struct work_struct release_agent_work;
+
+ /* ids of the ancestors at each level including self */
+ int ancestor_ids[];
};
/*
@@ -304,6 +315,9 @@ struct cgroup_root {
/* The root cgroup. Root is destroyed on its release. */
struct cgroup cgrp;
+ /* for cgrp->ancestor_ids[0] */
+ int cgrp_ancestor_id_storage;
+
/* Number of cgroups in the hierarchy, used only for /proc/cgroups */
atomic_t nr_cgrps;
@@ -521,4 +535,116 @@ static inline void cgroup_threadgroup_change_end(struct task_struct *tsk) {}
#endif /* CONFIG_CGROUPS */
+#ifdef CONFIG_SOCK_CGROUP_DATA
+
+/*
+ * sock_cgroup_data is embedded at sock->sk_cgrp_data and contains
+ * per-socket cgroup information except for memcg association.
+ *
+ * On legacy hierarchies, net_prio and net_cls controllers directly set
+ * attributes on each sock which can then be tested by the network layer.
+ * On the default hierarchy, each sock is associated with the cgroup it was
+ * created in and the networking layer can match the cgroup directly.
+ *
+ * To avoid carrying all three cgroup related fields separately in sock,
+ * sock_cgroup_data overloads (prioidx, classid) and the cgroup pointer.
+ * On boot, sock_cgroup_data records the cgroup that the sock was created
+ * in so that cgroup2 matches can be made; however, once either net_prio or
+ * net_cls starts being used, the area is overriden to carry prioidx and/or
+ * classid. The two modes are distinguished by whether the lowest bit is
+ * set. Clear bit indicates cgroup pointer while set bit prioidx and
+ * classid.
+ *
+ * While userland may start using net_prio or net_cls at any time, once
+ * either is used, cgroup2 matching no longer works. There is no reason to
+ * mix the two and this is in line with how legacy and v2 compatibility is
+ * handled. On mode switch, cgroup references which are already being
+ * pointed to by socks may be leaked. While this can be remedied by adding
+ * synchronization around sock_cgroup_data, given that the number of leaked
+ * cgroups is bound and highly unlikely to be high, this seems to be the
+ * better trade-off.
+ */
+struct sock_cgroup_data {
+ union {
+#ifdef __LITTLE_ENDIAN
+ struct {
+ u8 is_data;
+ u8 padding;
+ u16 prioidx;
+ u32 classid;
+ } __packed;
+#else
+ struct {
+ u32 classid;
+ u16 prioidx;
+ u8 padding;
+ u8 is_data;
+ } __packed;
+#endif
+ u64 val;
+ };
+};
+
+/*
+ * There's a theoretical window where the following accessors race with
+ * updaters and return part of the previous pointer as the prioidx or
+ * classid. Such races are short-lived and the result isn't critical.
+ */
+static inline u16 sock_cgroup_prioidx(struct sock_cgroup_data *skcd)
+{
+ /* fallback to 1 which is always the ID of the root cgroup */
+ return (skcd->is_data & 1) ? skcd->prioidx : 1;
+}
+
+static inline u32 sock_cgroup_classid(struct sock_cgroup_data *skcd)
+{
+ /* fallback to 0 which is the unconfigured default classid */
+ return (skcd->is_data & 1) ? skcd->classid : 0;
+}
+
+/*
+ * If invoked concurrently, the updaters may clobber each other. The
+ * caller is responsible for synchronization.
+ */
+static inline void sock_cgroup_set_prioidx(struct sock_cgroup_data *skcd,
+ u16 prioidx)
+{
+ struct sock_cgroup_data skcd_buf = {{ .val = READ_ONCE(skcd->val) }};
+
+ if (sock_cgroup_prioidx(&skcd_buf) == prioidx)
+ return;
+
+ if (!(skcd_buf.is_data & 1)) {
+ skcd_buf.val = 0;
+ skcd_buf.is_data = 1;
+ }
+
+ skcd_buf.prioidx = prioidx;
+ WRITE_ONCE(skcd->val, skcd_buf.val); /* see sock_cgroup_ptr() */
+}
+
+static inline void sock_cgroup_set_classid(struct sock_cgroup_data *skcd,
+ u32 classid)
+{
+ struct sock_cgroup_data skcd_buf = {{ .val = READ_ONCE(skcd->val) }};
+
+ if (sock_cgroup_classid(&skcd_buf) == classid)
+ return;
+
+ if (!(skcd_buf.is_data & 1)) {
+ skcd_buf.val = 0;
+ skcd_buf.is_data = 1;
+ }
+
+ skcd_buf.classid = classid;
+ WRITE_ONCE(skcd->val, skcd_buf.val); /* see sock_cgroup_ptr() */
+}
+
+#else /* CONFIG_SOCK_CGROUP_DATA */
+
+struct sock_cgroup_data {
+};
+
+#endif /* CONFIG_SOCK_CGROUP_DATA */
+
#endif /* _LINUX_CGROUP_DEFS_H */
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index cb91b44f5f78..322a28482745 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -81,7 +81,8 @@ struct cgroup_subsys_state *cgroup_get_e_css(struct cgroup *cgroup,
struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry,
struct cgroup_subsys *ss);
-bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor);
+struct cgroup *cgroup_get_from_path(const char *path);
+
int cgroup_attach_task_all(struct task_struct *from, struct task_struct *);
int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from);
@@ -364,6 +365,11 @@ static inline void css_put_many(struct cgroup_subsys_state *css, unsigned int n)
percpu_ref_put_many(&css->refcnt, n);
}
+static inline void cgroup_put(struct cgroup *cgrp)
+{
+ css_put(&cgrp->self);
+}
+
/**
* task_css_set_check - obtain a task's css_set with extra access conditions
* @task: the task to obtain css_set for
@@ -471,6 +477,23 @@ static inline struct cgroup *task_cgroup(struct task_struct *task,
return task_css(task, subsys_id)->cgroup;
}
+/**
+ * cgroup_is_descendant - test ancestry
+ * @cgrp: the cgroup to be tested
+ * @ancestor: possible ancestor of @cgrp
+ *
+ * Test whether @cgrp is a descendant of @ancestor. It also returns %true
+ * if @cgrp == @ancestor. This function is safe to call as long as @cgrp
+ * and @ancestor are accessible.
+ */
+static inline bool cgroup_is_descendant(struct cgroup *cgrp,
+ struct cgroup *ancestor)
+{
+ if (cgrp->root != ancestor->root || cgrp->level < ancestor->level)
+ return false;
+ return cgrp->ancestor_ids[ancestor->level] == ancestor->id;
+}
+
/* no synchronization, the result can only be used as a hint */
static inline bool cgroup_is_populated(struct cgroup *cgrp)
{
@@ -554,4 +577,45 @@ static inline int cgroup_init(void) { return 0; }
#endif /* !CONFIG_CGROUPS */
+/*
+ * sock->sk_cgrp_data handling. For more info, see sock_cgroup_data
+ * definition in cgroup-defs.h.
+ */
+#ifdef CONFIG_SOCK_CGROUP_DATA
+
+#if defined(CONFIG_CGROUP_NET_PRIO) || defined(CONFIG_CGROUP_NET_CLASSID)
+extern spinlock_t cgroup_sk_update_lock;
+#endif
+
+void cgroup_sk_alloc_disable(void);
+void cgroup_sk_alloc(struct sock_cgroup_data *skcd);
+void cgroup_sk_free(struct sock_cgroup_data *skcd);
+
+static inline struct cgroup *sock_cgroup_ptr(struct sock_cgroup_data *skcd)
+{
+#if defined(CONFIG_CGROUP_NET_PRIO) || defined(CONFIG_CGROUP_NET_CLASSID)
+ unsigned long v;
+
+ /*
+ * @skcd->val is 64bit but the following is safe on 32bit too as we
+ * just need the lower ulong to be written and read atomically.
+ */
+ v = READ_ONCE(skcd->val);
+
+ if (v & 1)
+ return &cgrp_dfl_root.cgrp;
+
+ return (struct cgroup *)(unsigned long)v ?: &cgrp_dfl_root.cgrp;
+#else
+ return (struct cgroup *)(unsigned long)skcd->val;
+#endif
+}
+
+#else /* CONFIG_CGROUP_DATA */
+
+static inline void cgroup_sk_alloc(struct sock_cgroup_data *skcd) {}
+static inline void cgroup_sk_free(struct sock_cgroup_data *skcd) {}
+
+#endif /* CONFIG_CGROUP_DATA */
+
#endif /* _LINUX_CGROUP_H */
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index eb049c622208..37ff4a6faa9a 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -29,6 +29,9 @@
#include <asm/bitsperlong.h>
#ifdef __KERNEL__
+struct device;
+int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr);
+unsigned char *arch_get_platform_get_mac_address(void);
u32 eth_get_headlen(void *data, unsigned int max_len);
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
extern const struct header_ops eth_header_ops;
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 5972ffe5719a..43aa1f8855c7 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -350,25 +350,43 @@ struct sk_filter {
#define BPF_PROG_RUN(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi)
+#define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN
+
+static inline u8 *bpf_skb_cb(struct sk_buff *skb)
+{
+ /* eBPF programs may read/write skb->cb[] area to transfer meta
+ * data between tail calls. Since this also needs to work with
+ * tc, that scratch memory is mapped to qdisc_skb_cb's data area.
+ *
+ * In some socket filter cases, the cb unfortunately needs to be
+ * saved/restored so that protocol specific skb->cb[] data won't
+ * be lost. In any case, due to unpriviledged eBPF programs
+ * attached to sockets, we need to clear the bpf_skb_cb() area
+ * to not leak previous contents to user space.
+ */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) != BPF_SKB_CB_LEN);
+ BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) !=
+ FIELD_SIZEOF(struct qdisc_skb_cb, data));
+
+ return qdisc_skb_cb(skb)->data;
+}
+
static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
struct sk_buff *skb)
{
- u8 *cb_data = qdisc_skb_cb(skb)->data;
- u8 saved_cb[QDISC_CB_PRIV_LEN];
+ u8 *cb_data = bpf_skb_cb(skb);
+ u8 cb_saved[BPF_SKB_CB_LEN];
u32 res;
- BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) !=
- QDISC_CB_PRIV_LEN);
-
if (unlikely(prog->cb_access)) {
- memcpy(saved_cb, cb_data, sizeof(saved_cb));
- memset(cb_data, 0, sizeof(saved_cb));
+ memcpy(cb_saved, cb_data, sizeof(cb_saved));
+ memset(cb_data, 0, sizeof(cb_saved));
}
res = BPF_PROG_RUN(prog, skb);
if (unlikely(prog->cb_access))
- memcpy(cb_data, saved_cb, sizeof(saved_cb));
+ memcpy(cb_data, cb_saved, sizeof(cb_saved));
return res;
}
@@ -376,10 +394,11 @@ static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog,
struct sk_buff *skb)
{
- u8 *cb_data = qdisc_skb_cb(skb)->data;
+ u8 *cb_data = bpf_skb_cb(skb);
if (unlikely(prog->cb_access))
- memset(cb_data, 0, QDISC_CB_PRIV_LEN);
+ memset(cb_data, 0, BPF_SKB_CB_LEN);
+
return BPF_PROG_RUN(prog, skb);
}
@@ -447,6 +466,8 @@ void bpf_prog_destroy(struct bpf_prog *fp);
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
int sk_attach_bpf(u32 ufd, struct sock *sk);
+int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk);
int sk_detach_filter(struct sock *sk);
int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
unsigned int len);
diff --git a/include/linux/hashtable.h b/include/linux/hashtable.h
index 519b6e2d769e..661e5c2a8e2a 100644
--- a/include/linux/hashtable.h
+++ b/include/linux/hashtable.h
@@ -16,6 +16,10 @@
struct hlist_head name[1 << (bits)] = \
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+#define DEFINE_READ_MOSTLY_HASHTABLE(name, bits) \
+ struct hlist_head name[1 << (bits)] __read_mostly = \
+ { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+
#define DECLARE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)]
diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h
index 1acb1445e05f..e31bcd4c7859 100644
--- a/include/linux/hdlc.h
+++ b/include/linux/hdlc.h
@@ -101,7 +101,7 @@ netdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev);
int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
size_t size);
/* May be used by hardware driver to gain control over HDLC device */
-void detach_hdlc_protocol(struct net_device *dev);
+int detach_hdlc_protocol(struct net_device *dev);
static __inline__ __be16 hdlc_type_trans(struct sk_buff *skb,
struct net_device *dev)
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index b49cf923becc..ba7a9b0c7c57 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -91,7 +91,6 @@ enum {
PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */
PPPOX_BOUND = 2, /* bound to ppp device */
PPPOX_RELAY = 4, /* forwarding is enabled */
- PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */
PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/
};
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index a6aa970758a2..b84e49c3a738 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -164,6 +164,7 @@ struct team_mode {
size_t priv_size;
size_t port_priv_size;
const struct team_mode_ops *ops;
+ enum netdev_lag_tx_type lag_tx_type;
};
#define TEAM_PORT_HASHBITS 4
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index 67ce5bd3b56a..a5f6ce6b578c 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -73,7 +73,7 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb)
/* found in socket.c */
extern void vlan_ioctl_set(int (*hook)(struct net *, void __user *));
-static inline bool is_vlan_dev(struct net_device *dev)
+static inline bool is_vlan_dev(const struct net_device *dev)
{
return dev->priv_flags & IFF_802_1Q_VLAN;
}
@@ -621,7 +621,7 @@ static inline netdev_features_t vlan_features_check(const struct sk_buff *skb,
NETIF_F_SG |
NETIF_F_HIGHDMA |
NETIF_F_FRAGLIST |
- NETIF_F_GEN_CSUM |
+ NETIF_F_HW_CSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);
diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h
index 0e707f0c1a3e..7c27fa1030e8 100644
--- a/include/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -3,6 +3,7 @@
#include <uapi/linux/inet_diag.h>
+struct net;
struct sock;
struct inet_hashinfo;
struct nlattr;
@@ -23,6 +24,10 @@ struct inet_diag_handler {
void (*idiag_get_info)(struct sock *sk,
struct inet_diag_msg *r,
void *info);
+
+ int (*destroy)(struct sk_buff *in_skb,
+ const struct inet_diag_req_v2 *req);
+
__u16 idiag_type;
__u16 idiag_info_size;
};
@@ -41,6 +46,10 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
struct sk_buff *in_skb, const struct nlmsghdr *nlh,
const struct inet_diag_req_v2 *req);
+struct sock *inet_diag_find_one_icsk(struct net *net,
+ struct inet_hashinfo *hashinfo,
+ const struct inet_diag_req_v2 *req);
+
int inet_diag_bc_sk(const struct nlattr *_bc, struct sock *sk);
extern int inet_diag_register(const struct inet_diag_handler *handler);
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 5d4e9c4b821d..af51df35d749 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -274,6 +274,8 @@ void pr_cont_kernfs_path(struct kernfs_node *kn);
struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn);
struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
const char *name, const void *ns);
+struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
+ const char *path, const void *ns);
void kernfs_get(struct kernfs_node *kn);
void kernfs_put(struct kernfs_node *kn);
@@ -350,6 +352,10 @@ static inline struct kernfs_node *
kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name,
const void *ns)
{ return NULL; }
+static inline struct kernfs_node *
+kernfs_walk_and_get_ns(struct kernfs_node *parent, const char *path,
+ const void *ns)
+{ return NULL; }
static inline void kernfs_get(struct kernfs_node *kn) { }
static inline void kernfs_put(struct kernfs_node *kn) { }
@@ -431,6 +437,12 @@ kernfs_find_and_get(struct kernfs_node *kn, const char *name)
}
static inline struct kernfs_node *
+kernfs_walk_and_get(struct kernfs_node *kn, const char *path)
+{
+ return kernfs_walk_and_get_ns(kn, path, NULL);
+}
+
+static inline struct kernfs_node *
kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode,
void *priv)
{
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index b42963bc81dd..5bfd99d1a40a 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -11,6 +11,55 @@
#include <uapi/linux/mdio.h>
+struct mii_bus;
+
+struct mdio_device {
+ struct device dev;
+
+ const struct dev_pm_ops *pm_ops;
+ struct mii_bus *bus;
+
+ int (*bus_match)(struct device *dev, struct device_driver *drv);
+ void (*device_free)(struct mdio_device *mdiodev);
+ void (*device_remove)(struct mdio_device *mdiodev);
+
+ /* Bus address of the MDIO device (0-31) */
+ int addr;
+ int flags;
+};
+#define to_mdio_device(d) container_of(d, struct mdio_device, dev)
+
+/* struct mdio_driver_common: Common to all MDIO drivers */
+struct mdio_driver_common {
+ struct device_driver driver;
+ int flags;
+};
+#define MDIO_DEVICE_FLAG_PHY 1
+#define to_mdio_common_driver(d) \
+ container_of(d, struct mdio_driver_common, driver)
+
+/* struct mdio_driver: Generic MDIO driver */
+struct mdio_driver {
+ struct mdio_driver_common mdiodrv;
+
+ /*
+ * Called during discovery. Used to set
+ * up device-specific structures, if any
+ */
+ int (*probe)(struct mdio_device *mdiodev);
+
+ /* Clears up any memory if needed */
+ void (*remove)(struct mdio_device *mdiodev);
+};
+#define to_mdio_driver(d) \
+ container_of(to_mdio_common_driver(d), struct mdio_driver, mdiodrv)
+
+void mdio_device_free(struct mdio_device *mdiodev);
+struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
+int mdio_device_register(struct mdio_device *mdiodev);
+void mdio_device_remove(struct mdio_device *mdiodev);
+int mdio_driver_register(struct mdio_driver *drv);
+void mdio_driver_unregister(struct mdio_driver *drv);
static inline bool mdio_phy_id_is_c45(int phy_id)
{
@@ -173,4 +222,33 @@ static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv)
return reg;
}
+int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
+int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+
+int mdiobus_register_device(struct mdio_device *mdiodev);
+int mdiobus_unregister_device(struct mdio_device *mdiodev);
+bool mdiobus_is_registered_device(struct mii_bus *bus, int addr);
+struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr);
+
+/**
+ * module_mdio_driver() - Helper macro for registering mdio drivers
+ *
+ * Helper macro for MDIO drivers which do not do anything special in module
+ * init/exit. Each module may only use this macro once, and calling it
+ * replaces module_init() and module_exit().
+ */
+#define mdio_module_driver(_mdio_driver) \
+static int __init mdio_module_init(void) \
+{ \
+ return mdio_driver_register(&_mdio_driver); \
+} \
+module_init(mdio_module_init); \
+static void __exit mdio_module_exit(void) \
+{ \
+ mdio_driver_unregister(&_mdio_driver); \
+} \
+module_exit(mdio_module_exit)
+
#endif /* __LINUX_MDIO_H__ */
diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h
index 5a06d969338e..2e8af001c5da 100644
--- a/include/linux/mlx4/driver.h
+++ b/include/linux/mlx4/driver.h
@@ -75,6 +75,11 @@ static inline int mlx4_is_bonded(struct mlx4_dev *dev)
return !!(dev->flags & MLX4_FLAG_BONDED);
}
+static inline int mlx4_is_mf_bonded(struct mlx4_dev *dev)
+{
+ return (mlx4_is_bonded(dev) && mlx4_is_mfunc(dev));
+}
+
struct mlx4_port_map {
u8 port1;
u8 port2;
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index 0b473cbfa7ef..7be845e30689 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -251,6 +251,7 @@ enum mlx5_event {
MLX5_EVENT_TYPE_PAGE_REQUEST = 0xb,
MLX5_EVENT_TYPE_PAGE_FAULT = 0xc,
+ MLX5_EVENT_TYPE_NIC_VPORT_CHANGE = 0xd,
};
enum {
@@ -442,9 +443,12 @@ struct mlx5_init_seg {
__be32 rsvd1[120];
__be32 initializing;
struct health_buffer health;
- __be32 rsvd2[884];
+ __be32 rsvd2[880];
+ __be32 internal_timer_h;
+ __be32 internal_timer_l;
+ __be32 rsrv3[2];
__be32 health_counter;
- __be32 rsvd3[1019];
+ __be32 rsvd4[1019];
__be64 ieee1588_clk;
__be32 ieee1588_clk_type;
__be32 clr_intx;
@@ -520,6 +524,12 @@ struct mlx5_eqe_page_fault {
__be32 flags_qpn;
} __packed;
+struct mlx5_eqe_vport_change {
+ u8 rsvd0[2];
+ __be16 vport_num;
+ __be32 rsvd1[6];
+} __packed;
+
union ev_data {
__be32 raw[7];
struct mlx5_eqe_cmd cmd;
@@ -532,6 +542,7 @@ union ev_data {
struct mlx5_eqe_stall_vl stall_vl;
struct mlx5_eqe_page_req req_pages;
struct mlx5_eqe_page_fault page_fault;
+ struct mlx5_eqe_vport_change vport_change;
} __packed;
struct mlx5_eqe {
@@ -593,7 +604,8 @@ struct mlx5_cqe64 {
__be32 imm_inval_pkey;
u8 rsvd40[4];
__be32 byte_cnt;
- __be64 timestamp;
+ __be32 timestamp_h;
+ __be32 timestamp_l;
__be32 sop_drop_qpn;
__be16 wqe_counter;
u8 signature;
@@ -615,6 +627,16 @@ static inline int cqe_has_vlan(struct mlx5_cqe64 *cqe)
return !!(cqe->l4_hdr_type_etc & 0x1);
}
+static inline u64 get_cqe_ts(struct mlx5_cqe64 *cqe)
+{
+ u32 hi, lo;
+
+ hi = be32_to_cpu(cqe->timestamp_h);
+ lo = be32_to_cpu(cqe->timestamp_l);
+
+ return (u64)lo | ((u64)hi << 32);
+}
+
enum {
CQE_L4_HDR_TYPE_NONE = 0x0,
CQE_L4_HDR_TYPE_TCP_NO_ACK = 0x1,
@@ -1067,6 +1089,12 @@ enum {
};
enum {
+ MLX5_ESW_VPORT_ADMIN_STATE_DOWN = 0x0,
+ MLX5_ESW_VPORT_ADMIN_STATE_UP = 0x1,
+ MLX5_ESW_VPORT_ADMIN_STATE_AUTO = 0x2,
+};
+
+enum {
MLX5_L3_PROT_TYPE_IPV4 = 0,
MLX5_L3_PROT_TYPE_IPV6 = 1,
};
@@ -1102,6 +1130,12 @@ enum {
MLX5_FLOW_CONTEXT_DEST_TYPE_TIR = 2,
};
+enum mlx5_list_type {
+ MLX5_NVPRT_LIST_TYPE_UC = 0x0,
+ MLX5_NVPRT_LIST_TYPE_MC = 0x1,
+ MLX5_NVPRT_LIST_TYPE_VLAN = 0x2,
+};
+
enum {
MLX5_RQC_RQ_TYPE_MEMORY_RQ_INLINE = 0x0,
MLX5_RQC_RQ_TYPE_MEMORY_RQ_RPM = 0x1,
@@ -1124,6 +1158,8 @@ enum mlx5_cap_type {
MLX5_CAP_IPOIB_OFFLOADS,
MLX5_CAP_EOIB_OFFLOADS,
MLX5_CAP_FLOW_TABLE,
+ MLX5_CAP_ESWITCH_FLOW_TABLE,
+ MLX5_CAP_ESWITCH,
/* NUM OF CAP Types */
MLX5_CAP_NUM
};
@@ -1161,6 +1197,28 @@ enum mlx5_cap_type {
#define MLX5_CAP_FLOWTABLE_MAX(mdev, cap) \
MLX5_GET(flow_table_nic_cap, mdev->hca_caps_max[MLX5_CAP_FLOW_TABLE], cap)
+#define MLX5_CAP_ESW_FLOWTABLE(mdev, cap) \
+ MLX5_GET(flow_table_eswitch_cap, \
+ mdev->hca_caps_cur[MLX5_CAP_ESWITCH_FLOW_TABLE], cap)
+
+#define MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, cap) \
+ MLX5_GET(flow_table_eswitch_cap, \
+ mdev->hca_caps_max[MLX5_CAP_ESWITCH_FLOW_TABLE], cap)
+
+#define MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, cap) \
+ MLX5_CAP_ESW_FLOWTABLE(mdev, flow_table_properties_nic_esw_fdb.cap)
+
+#define MLX5_CAP_ESW_FLOWTABLE_FDB_MAX(mdev, cap) \
+ MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, flow_table_properties_nic_esw_fdb.cap)
+
+#define MLX5_CAP_ESW(mdev, cap) \
+ MLX5_GET(e_switch_cap, \
+ mdev->hca_caps_cur[MLX5_CAP_ESWITCH], cap)
+
+#define MLX5_CAP_ESW_MAX(mdev, cap) \
+ MLX5_GET(e_switch_cap, \
+ mdev->hca_caps_max[MLX5_CAP_ESWITCH], cap)
+
#define MLX5_CAP_ODP(mdev, cap)\
MLX5_GET(odp_cap, mdev->hca_caps_cur[MLX5_CAP_ODP], cap)
@@ -1200,4 +1258,6 @@ static inline u16 mlx5_to_sw_pkey_sz(int pkey_sz)
return MLX5_MIN_PKEY_TABLE_SIZE << pkey_sz;
}
+#define MLX5_BY_PASS_NUM_PRIOS 9
+
#endif /* MLX5_DEVICE_H */
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 5c857f2a20d7..2fd7019f69db 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -426,11 +426,23 @@ struct mlx5_mr_table {
struct radix_tree_root tree;
};
+struct mlx5_vf_context {
+ int enabled;
+};
+
+struct mlx5_core_sriov {
+ struct mlx5_vf_context *vfs_ctx;
+ int num_vfs;
+ int enabled_vfs;
+};
+
struct mlx5_irq_info {
cpumask_var_t mask;
char name[MLX5_MAX_IRQ_NAME];
};
+struct mlx5_eswitch;
+
struct mlx5_priv {
char name[MLX5_MAX_NAME_LEN];
struct mlx5_eq_table eq_table;
@@ -447,6 +459,7 @@ struct mlx5_priv {
int fw_pages;
atomic_t reg_pages;
struct list_head free_list;
+ int vfs_pages;
struct mlx5_core_health health;
@@ -485,6 +498,12 @@ struct mlx5_priv {
struct list_head dev_list;
struct list_head ctx_list;
spinlock_t ctx_lock;
+
+ struct mlx5_eswitch *eswitch;
+ struct mlx5_core_sriov sriov;
+ unsigned long pci_dev_data;
+ struct mlx5_flow_root_namespace *root_ns;
+ struct mlx5_flow_root_namespace *fdb_root_ns;
};
enum mlx5_device_state {
@@ -739,6 +758,8 @@ void mlx5_pagealloc_init(struct mlx5_core_dev *dev);
void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev);
int mlx5_pagealloc_start(struct mlx5_core_dev *dev);
void mlx5_pagealloc_stop(struct mlx5_core_dev *dev);
+int mlx5_sriov_init(struct mlx5_core_dev *dev);
+int mlx5_sriov_cleanup(struct mlx5_core_dev *dev);
void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id,
s32 npages);
int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot);
@@ -884,6 +905,15 @@ struct mlx5_profile {
} mr_cache[MAX_MR_CACHE_ENTRIES];
};
+enum {
+ MLX5_PCI_DEV_IS_VF = 1 << 0,
+};
+
+static inline int mlx5_core_is_pf(struct mlx5_core_dev *dev)
+{
+ return !(dev->priv.pci_dev_data & MLX5_PCI_DEV_IS_VF);
+}
+
static inline int mlx5_get_gid_table_len(u16 param)
{
if (param > 4) {
diff --git a/include/linux/mlx5/flow_table.h b/include/linux/mlx5/flow_table.h
deleted file mode 100644
index 5f922c6d4fc2..000000000000
--- a/include/linux/mlx5/flow_table.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses. You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef MLX5_FLOW_TABLE_H
-#define MLX5_FLOW_TABLE_H
-
-#include <linux/mlx5/driver.h>
-
-struct mlx5_flow_table_group {
- u8 log_sz;
- u8 match_criteria_enable;
- u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)];
-};
-
-void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 table_type,
- u16 num_groups,
- struct mlx5_flow_table_group *group);
-void mlx5_destroy_flow_table(void *flow_table);
-int mlx5_add_flow_table_entry(void *flow_table, u8 match_criteria_enable,
- void *match_criteria, void *flow_context,
- u32 *flow_index);
-void mlx5_del_flow_table_entry(void *flow_table, u32 flow_index);
-u32 mlx5_get_flow_table_id(void *flow_table);
-
-#endif /* MLX5_FLOW_TABLE_H */
diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h
new file mode 100644
index 000000000000..8230caa3fb6e
--- /dev/null
+++ b/include/linux/mlx5/fs.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MLX5_FS_
+#define _MLX5_FS_
+
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/mlx5_ifc.h>
+
+#define MLX5_FS_DEFAULT_FLOW_TAG 0x0
+
+#define LEFTOVERS_RULE_NUM 2
+static inline void build_leftovers_ft_param(int *priority,
+ int *n_ent,
+ int *n_grp)
+{
+ *priority = 0; /* Priority of leftovers_prio-0 */
+ *n_ent = LEFTOVERS_RULE_NUM;
+ *n_grp = LEFTOVERS_RULE_NUM;
+}
+
+enum mlx5_flow_namespace_type {
+ MLX5_FLOW_NAMESPACE_BYPASS,
+ MLX5_FLOW_NAMESPACE_KERNEL,
+ MLX5_FLOW_NAMESPACE_LEFTOVERS,
+ MLX5_FLOW_NAMESPACE_FDB,
+};
+
+struct mlx5_flow_table;
+struct mlx5_flow_group;
+struct mlx5_flow_rule;
+struct mlx5_flow_namespace;
+
+struct mlx5_flow_destination {
+ enum mlx5_flow_destination_type type;
+ union {
+ u32 tir_num;
+ struct mlx5_flow_table *ft;
+ u32 vport_num;
+ };
+};
+
+struct mlx5_flow_namespace *
+mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
+ enum mlx5_flow_namespace_type type);
+
+struct mlx5_flow_table *
+mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int num_flow_table_entries,
+ int max_num_groups);
+
+struct mlx5_flow_table *
+mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int num_flow_table_entries);
+int mlx5_destroy_flow_table(struct mlx5_flow_table *ft);
+
+/* inbox should be set with the following values:
+ * start_flow_index
+ * end_flow_index
+ * match_criteria_enable
+ * match_criteria
+ */
+struct mlx5_flow_group *
+mlx5_create_flow_group(struct mlx5_flow_table *ft, u32 *in);
+void mlx5_destroy_flow_group(struct mlx5_flow_group *fg);
+
+/* Single destination per rule.
+ * Group ID is implied by the match criteria.
+ */
+struct mlx5_flow_rule *
+mlx5_add_flow_rule(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria,
+ u32 *match_value,
+ u32 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest);
+void mlx5_del_flow_rule(struct mlx5_flow_rule *fr);
+
+#endif
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 1565324eb620..68d73f82e009 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -185,6 +185,7 @@ enum {
MLX5_CMD_OP_MODIFY_RQT = 0x917,
MLX5_CMD_OP_DESTROY_RQT = 0x918,
MLX5_CMD_OP_QUERY_RQT = 0x919,
+ MLX5_CMD_OP_SET_FLOW_TABLE_ROOT = 0x92f,
MLX5_CMD_OP_CREATE_FLOW_TABLE = 0x930,
MLX5_CMD_OP_DESTROY_FLOW_TABLE = 0x931,
MLX5_CMD_OP_QUERY_FLOW_TABLE = 0x932,
@@ -193,7 +194,8 @@ enum {
MLX5_CMD_OP_QUERY_FLOW_GROUP = 0x935,
MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY = 0x936,
MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY = 0x937,
- MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY = 0x938
+ MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY = 0x938,
+ MLX5_CMD_OP_MODIFY_FLOW_TABLE = 0x93c
};
struct mlx5_ifc_flow_table_fields_supported_bits {
@@ -256,25 +258,30 @@ struct mlx5_ifc_flow_table_fields_supported_bits {
struct mlx5_ifc_flow_table_prop_layout_bits {
u8 ft_support[0x1];
- u8 reserved_0[0x1f];
+ u8 reserved_0[0x2];
+ u8 flow_modify_en[0x1];
+ u8 modify_root[0x1];
+ u8 identified_miss_table_mode[0x1];
+ u8 flow_table_modify[0x1];
+ u8 reserved_1[0x19];
- u8 reserved_1[0x2];
+ u8 reserved_2[0x2];
u8 log_max_ft_size[0x6];
- u8 reserved_2[0x10];
+ u8 reserved_3[0x10];
u8 max_ft_level[0x8];
- u8 reserved_3[0x20];
+ u8 reserved_4[0x20];
- u8 reserved_4[0x18];
+ u8 reserved_5[0x18];
u8 log_max_ft_num[0x8];
- u8 reserved_5[0x18];
+ u8 reserved_6[0x18];
u8 log_max_destination[0x8];
- u8 reserved_6[0x18];
+ u8 reserved_7[0x18];
u8 log_max_flow[0x8];
- u8 reserved_7[0x40];
+ u8 reserved_8[0x40];
struct mlx5_ifc_flow_table_fields_supported_bits ft_field_support;
@@ -291,6 +298,22 @@ struct mlx5_ifc_odp_per_transport_service_cap_bits {
u8 reserved_1[0x1a];
};
+struct mlx5_ifc_ipv4_layout_bits {
+ u8 reserved_0[0x60];
+
+ u8 ipv4[0x20];
+};
+
+struct mlx5_ifc_ipv6_layout_bits {
+ u8 ipv6[16][0x8];
+};
+
+union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits {
+ struct mlx5_ifc_ipv6_layout_bits ipv6_layout;
+ struct mlx5_ifc_ipv4_layout_bits ipv4_layout;
+ u8 reserved_0[0x80];
+};
+
struct mlx5_ifc_fte_match_set_lyr_2_4_bits {
u8 smac_47_16[0x20];
@@ -321,9 +344,9 @@ struct mlx5_ifc_fte_match_set_lyr_2_4_bits {
u8 udp_sport[0x10];
u8 udp_dport[0x10];
- u8 src_ip[4][0x20];
+ union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits src_ipv4_src_ipv6;
- u8 dst_ip[4][0x20];
+ union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits dst_ipv4_dst_ipv6;
};
struct mlx5_ifc_fte_match_set_misc_bits {
@@ -447,6 +470,29 @@ struct mlx5_ifc_flow_table_nic_cap_bits {
u8 reserved_3[0x7200];
};
+struct mlx5_ifc_flow_table_eswitch_cap_bits {
+ u8 reserved_0[0x200];
+
+ struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_nic_esw_fdb;
+
+ struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_esw_acl_ingress;
+
+ struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_esw_acl_egress;
+
+ u8 reserved_1[0x7800];
+};
+
+struct mlx5_ifc_e_switch_cap_bits {
+ u8 vport_svlan_strip[0x1];
+ u8 vport_cvlan_strip[0x1];
+ u8 vport_svlan_insert[0x1];
+ u8 vport_cvlan_insert_if_not_exist[0x1];
+ u8 vport_cvlan_insert_overwrite[0x1];
+ u8 reserved_0[0x1b];
+
+ u8 reserved_1[0x7e0];
+};
+
struct mlx5_ifc_per_protocol_networking_offload_caps_bits {
u8 csum_cap[0x1];
u8 vlan_cap[0x1];
@@ -665,7 +711,9 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_17[0x1];
u8 ets[0x1];
u8 nic_flow_table[0x1];
- u8 reserved_18[0x4];
+ u8 eswitch_flow_table[0x1];
+ u8 early_vf_enable;
+ u8 reserved_18[0x2];
u8 local_ca_ack_delay[0x5];
u8 reserved_19[0x6];
u8 port_type[0x2];
@@ -787,27 +835,36 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_60[0x1b];
u8 log_max_wq_sz[0x5];
- u8 reserved_61[0xa0];
-
+ u8 nic_vport_change_event[0x1];
+ u8 reserved_61[0xa];
+ u8 log_max_vlan_list[0x5];
u8 reserved_62[0x3];
+ u8 log_max_current_mc_list[0x5];
+ u8 reserved_63[0x3];
+ u8 log_max_current_uc_list[0x5];
+
+ u8 reserved_64[0x80];
+
+ u8 reserved_65[0x3];
u8 log_max_l2_table[0x5];
- u8 reserved_63[0x8];
+ u8 reserved_66[0x8];
u8 log_uar_page_sz[0x10];
- u8 reserved_64[0x100];
-
- u8 reserved_65[0x1f];
+ u8 reserved_67[0x40];
+ u8 device_frequency_khz[0x20];
+ u8 reserved_68[0x5f];
u8 cqe_zip[0x1];
u8 cqe_zip_timeout[0x10];
u8 cqe_zip_max_num[0x10];
- u8 reserved_66[0x220];
+ u8 reserved_69[0x220];
};
-enum {
- MLX5_DEST_FORMAT_STRUCT_DESTINATION_TYPE_FLOW_TABLE_ = 0x1,
- MLX5_DEST_FORMAT_STRUCT_DESTINATION_TYPE_TIR = 0x2,
+enum mlx5_flow_destination_type {
+ MLX5_FLOW_DESTINATION_TYPE_VPORT = 0x0,
+ MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE = 0x1,
+ MLX5_FLOW_DESTINATION_TYPE_TIR = 0x2,
};
struct mlx5_ifc_dest_format_struct_bits {
@@ -900,6 +957,13 @@ struct mlx5_ifc_mac_address_layout_bits {
u8 mac_addr_31_0[0x20];
};
+struct mlx5_ifc_vlan_layout_bits {
+ u8 reserved_0[0x14];
+ u8 vlan[0x0c];
+
+ u8 reserved_1[0x20];
+};
+
struct mlx5_ifc_cong_control_r_roce_ecn_np_bits {
u8 reserved_0[0xa0];
@@ -1829,6 +1893,8 @@ union mlx5_ifc_hca_cap_union_bits {
struct mlx5_ifc_roce_cap_bits roce_cap;
struct mlx5_ifc_per_protocol_networking_offload_caps_bits per_protocol_networking_offload_caps;
struct mlx5_ifc_flow_table_nic_cap_bits flow_table_nic_cap;
+ struct mlx5_ifc_flow_table_eswitch_cap_bits flow_table_eswitch_cap;
+ struct mlx5_ifc_e_switch_cap_bits e_switch_cap;
u8 reserved_0[0x8000];
};
@@ -2133,24 +2199,35 @@ struct mlx5_ifc_rmpc_bits {
struct mlx5_ifc_wq_bits wq;
};
-enum {
- MLX5_NIC_VPORT_CONTEXT_ALLOWED_LIST_TYPE_CURRENT_UC_MAC_ADDRESS = 0x0,
-};
-
struct mlx5_ifc_nic_vport_context_bits {
u8 reserved_0[0x1f];
u8 roce_en[0x1];
- u8 reserved_1[0x760];
+ u8 arm_change_event[0x1];
+ u8 reserved_1[0x1a];
+ u8 event_on_mtu[0x1];
+ u8 event_on_promisc_change[0x1];
+ u8 event_on_vlan_change[0x1];
+ u8 event_on_mc_address_change[0x1];
+ u8 event_on_uc_address_change[0x1];
- u8 reserved_2[0x5];
+ u8 reserved_2[0xf0];
+
+ u8 mtu[0x10];
+
+ u8 reserved_3[0x640];
+
+ u8 promisc_uc[0x1];
+ u8 promisc_mc[0x1];
+ u8 promisc_all[0x1];
+ u8 reserved_4[0x2];
u8 allowed_list_type[0x3];
- u8 reserved_3[0xc];
+ u8 reserved_5[0xc];
u8 allowed_list_size[0xc];
struct mlx5_ifc_mac_address_layout_bits permanent_address;
- u8 reserved_4[0x20];
+ u8 reserved_6[0x20];
u8 current_uc_mac_address[0][0x40];
};
@@ -2263,6 +2340,26 @@ struct mlx5_ifc_hca_vport_context_bits {
u8 reserved_6[0xca0];
};
+struct mlx5_ifc_esw_vport_context_bits {
+ u8 reserved_0[0x3];
+ u8 vport_svlan_strip[0x1];
+ u8 vport_cvlan_strip[0x1];
+ u8 vport_svlan_insert[0x1];
+ u8 vport_cvlan_insert[0x2];
+ u8 reserved_1[0x18];
+
+ u8 reserved_2[0x20];
+
+ u8 svlan_cfi[0x1];
+ u8 svlan_pcp[0x3];
+ u8 svlan_id[0xc];
+ u8 cvlan_cfi[0x1];
+ u8 cvlan_pcp[0x3];
+ u8 cvlan_id[0xc];
+
+ u8 reserved_3[0x7a0];
+};
+
enum {
MLX5_EQC_STATUS_OK = 0x0,
MLX5_EQC_STATUS_EQ_WRITE_FAILURE = 0xa,
@@ -2769,6 +2866,13 @@ struct mlx5_ifc_set_hca_cap_in_bits {
union mlx5_ifc_hca_cap_union_bits capability;
};
+enum {
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION = 0x0,
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_TAG = 0x1,
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST = 0x2,
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS = 0x3
+};
+
struct mlx5_ifc_set_fte_out_bits {
u8 status[0x8];
u8 reserved_0[0x18];
@@ -2793,11 +2897,14 @@ struct mlx5_ifc_set_fte_in_bits {
u8 reserved_4[0x8];
u8 table_id[0x18];
- u8 reserved_5[0x40];
+ u8 reserved_5[0x18];
+ u8 modify_enable_mask[0x8];
+
+ u8 reserved_6[0x20];
u8 flow_index[0x20];
- u8 reserved_6[0xe0];
+ u8 reserved_7[0xe0];
struct mlx5_ifc_flow_context_bits flow_context;
};
@@ -2940,6 +3047,7 @@ struct mlx5_ifc_query_vport_state_out_bits {
enum {
MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT = 0x0,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT = 0x1,
};
struct mlx5_ifc_query_vport_state_in_bits {
@@ -3700,6 +3808,64 @@ struct mlx5_ifc_query_flow_group_in_bits {
u8 reserved_5[0x120];
};
+struct mlx5_ifc_query_esw_vport_context_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+
+ struct mlx5_ifc_esw_vport_context_bits esw_vport_context;
+};
+
+struct mlx5_ifc_query_esw_vport_context_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 other_vport[0x1];
+ u8 reserved_2[0xf];
+ u8 vport_number[0x10];
+
+ u8 reserved_3[0x20];
+};
+
+struct mlx5_ifc_modify_esw_vport_context_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+};
+
+struct mlx5_ifc_esw_vport_context_fields_select_bits {
+ u8 reserved[0x1c];
+ u8 vport_cvlan_insert[0x1];
+ u8 vport_svlan_insert[0x1];
+ u8 vport_cvlan_strip[0x1];
+ u8 vport_svlan_strip[0x1];
+};
+
+struct mlx5_ifc_modify_esw_vport_context_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 other_vport[0x1];
+ u8 reserved_2[0xf];
+ u8 vport_number[0x10];
+
+ struct mlx5_ifc_esw_vport_context_fields_select_bits field_select;
+
+ struct mlx5_ifc_esw_vport_context_bits esw_vport_context;
+};
+
struct mlx5_ifc_query_eq_out_bits {
u8 status[0x8];
u8 reserved_0[0x18];
@@ -4228,7 +4394,10 @@ struct mlx5_ifc_modify_nic_vport_context_out_bits {
};
struct mlx5_ifc_modify_nic_vport_field_select_bits {
- u8 reserved_0[0x1c];
+ u8 reserved_0[0x19];
+ u8 mtu[0x1];
+ u8 change_event[0x1];
+ u8 promisc[0x1];
u8 permanent_address[0x1];
u8 addresses_list[0x1];
u8 roce_en[0x1];
@@ -5519,12 +5688,16 @@ struct mlx5_ifc_create_flow_table_in_bits {
u8 reserved_4[0x20];
- u8 reserved_5[0x8];
+ u8 reserved_5[0x4];
+ u8 table_miss_mode[0x4];
u8 level[0x8];
u8 reserved_6[0x8];
u8 log_size[0x8];
- u8 reserved_7[0x120];
+ u8 reserved_7[0x8];
+ u8 table_miss_id[0x18];
+
+ u8 reserved_8[0x100];
};
struct mlx5_ifc_create_flow_group_out_bits {
@@ -6798,4 +6971,72 @@ union mlx5_ifc_uplink_pci_interface_document_bits {
u8 reserved_0[0x20060];
};
+struct mlx5_ifc_set_flow_table_root_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+};
+
+struct mlx5_ifc_set_flow_table_root_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 reserved_2[0x40];
+
+ u8 table_type[0x8];
+ u8 reserved_3[0x18];
+
+ u8 reserved_4[0x8];
+ u8 table_id[0x18];
+
+ u8 reserved_5[0x140];
+};
+
+enum {
+ MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID = 0x1,
+};
+
+struct mlx5_ifc_modify_flow_table_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+};
+
+struct mlx5_ifc_modify_flow_table_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 reserved_2[0x20];
+
+ u8 reserved_3[0x10];
+ u8 modify_field_select[0x10];
+
+ u8 table_type[0x8];
+ u8 reserved_4[0x18];
+
+ u8 reserved_5[0x8];
+ u8 table_id[0x18];
+
+ u8 reserved_6[0x4];
+ u8 table_miss_mode[0x4];
+ u8 reserved_7[0x18];
+
+ u8 reserved_8[0x8];
+ u8 table_miss_id[0x18];
+
+ u8 reserved_9[0x100];
+};
+
#endif /* MLX5_IFC_H */
diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h
index 967e0fd06e89..638f2ca7a527 100644
--- a/include/linux/mlx5/vport.h
+++ b/include/linux/mlx5/vport.h
@@ -34,9 +34,17 @@
#define __MLX5_VPORT_H__
#include <linux/mlx5/driver.h>
+#include <linux/mlx5/device.h>
-u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod);
-void mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u8 *addr);
+u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport);
+u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport);
+int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport, u8 state);
+int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
+ u16 vport, u8 *addr);
+int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *dev,
+ u16 vport, u8 *addr);
int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport,
u8 port_num, u16 vf_num, u16 gid_index,
union ib_gid *gid);
@@ -51,5 +59,30 @@ int mlx5_query_hca_vport_system_image_guid(struct mlx5_core_dev *dev,
u64 *sys_image_guid);
int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev,
u64 *node_guid);
+int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ u32 vport,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int *list_size);
+int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int list_size);
+int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ u32 vport,
+ int *promisc_uc,
+ int *promisc_mc,
+ int *promisc_all);
+int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ int promisc_uc,
+ int promisc_mc,
+ int promisc_all);
+int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u32 vport,
+ u16 vlans[],
+ int *size);
+int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u16 vlans[],
+ int list_size);
#endif /* __MLX5_VPORT_H__ */
diff --git a/include/linux/mroute.h b/include/linux/mroute.h
index 79aaa9fc1a15..bf9b322cb0b0 100644
--- a/include/linux/mroute.h
+++ b/include/linux/mroute.h
@@ -9,38 +9,28 @@
#ifdef CONFIG_IP_MROUTE
static inline int ip_mroute_opt(int opt)
{
- return (opt >= MRT_BASE) && (opt <= MRT_MAX);
+ return opt >= MRT_BASE && opt <= MRT_MAX;
}
-#else
-static inline int ip_mroute_opt(int opt)
-{
- return 0;
-}
-#endif
-#ifdef CONFIG_IP_MROUTE
-extern int ip_mroute_setsockopt(struct sock *, int, char __user *, unsigned int);
-extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *);
-extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg);
-extern int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
-extern int ip_mr_init(void);
+int ip_mroute_setsockopt(struct sock *, int, char __user *, unsigned int);
+int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *);
+int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg);
+int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
+int ip_mr_init(void);
#else
-static inline
-int ip_mroute_setsockopt(struct sock *sock,
- int optname, char __user *optval, unsigned int optlen)
+static inline int ip_mroute_setsockopt(struct sock *sock, int optname,
+ char __user *optval, unsigned int optlen)
{
return -ENOPROTOOPT;
}
-static inline
-int ip_mroute_getsockopt(struct sock *sock,
- int optname, char __user *optval, int __user *optlen)
+static inline int ip_mroute_getsockopt(struct sock *sock, int optname,
+ char __user *optval, int __user *optlen)
{
return -ENOPROTOOPT;
}
-static inline
-int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
+static inline int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
{
return -ENOIOCTLCMD;
}
@@ -49,6 +39,11 @@ static inline int ip_mr_init(void)
{
return 0;
}
+
+static inline int ip_mroute_opt(int opt)
+{
+ return 0;
+}
#endif
struct vif_device {
@@ -64,6 +59,32 @@ struct vif_device {
#define VIFF_STATIC 0x8000
+#define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
+#define MFC_LINES 64
+
+struct mr_table {
+ struct list_head list;
+ possible_net_t net;
+ u32 id;
+ struct sock __rcu *mroute_sk;
+ struct timer_list ipmr_expire_timer;
+ struct list_head mfc_unres_queue;
+ struct list_head mfc_cache_array[MFC_LINES];
+ struct vif_device vif_table[MAXVIFS];
+ int maxvif;
+ atomic_t cache_resolve_queue_len;
+ bool mroute_do_assert;
+ bool mroute_do_pim;
+ int mroute_reg_vif_num;
+};
+
+/* mfc_flags:
+ * MFC_STATIC - the entry was added statically (not by a routing daemon)
+ */
+enum {
+ MFC_STATIC = BIT(0),
+};
+
struct mfc_cache {
struct list_head list;
__be32 mfc_mcastgrp; /* Group the entry belongs to */
@@ -89,19 +110,14 @@ struct mfc_cache {
struct rcu_head rcu;
};
-#define MFC_STATIC 1
-#define MFC_NOTIFY 2
-
-#define MFC_LINES 64
-
#ifdef __BIG_ENDIAN
#define MFC_HASH(a,b) (((((__force u32)(__be32)a)>>24)^(((__force u32)(__be32)b)>>26))&(MFC_LINES-1))
#else
#define MFC_HASH(a,b) ((((__force u32)(__be32)a)^(((__force u32)(__be32)b)>>2))&(MFC_LINES-1))
-#endif
+#endif
struct rtmsg;
-extern int ipmr_get_route(struct net *net, struct sk_buff *skb,
- __be32 saddr, __be32 daddr,
- struct rtmsg *rtm, int nowait);
+int ipmr_get_route(struct net *net, struct sk_buff *skb,
+ __be32 saddr, __be32 daddr,
+ struct rtmsg *rtm, int nowait);
#endif
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index f0d87347df19..d9654f0eecb3 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -52,7 +52,7 @@ enum {
NETIF_F_GSO_TUNNEL_REMCSUM_BIT,
NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */
- NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */
+ NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */
NETIF_F_FCOE_MTU_BIT, /* Supports max FCoE MTU, 2158 bytes*/
NETIF_F_NTUPLE_BIT, /* N-tuple filters supported */
NETIF_F_RXHASH_BIT, /* Receive hashing offload */
@@ -103,7 +103,7 @@ enum {
#define NETIF_F_NTUPLE __NETIF_F(NTUPLE)
#define NETIF_F_RXCSUM __NETIF_F(RXCSUM)
#define NETIF_F_RXHASH __NETIF_F(RXHASH)
-#define NETIF_F_SCTP_CSUM __NETIF_F(SCTP_CSUM)
+#define NETIF_F_SCTP_CRC __NETIF_F(SCTP_CRC)
#define NETIF_F_SG __NETIF_F(SG)
#define NETIF_F_TSO6 __NETIF_F(TSO6)
#define NETIF_F_TSO_ECN __NETIF_F(TSO_ECN)
@@ -146,10 +146,12 @@ enum {
#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \
NETIF_F_TSO6 | NETIF_F_UFO)
-#define NETIF_F_GEN_CSUM NETIF_F_HW_CSUM
-#define NETIF_F_V4_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
-#define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
-#define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
+/* List of IP checksum features. Note that NETIF_F_ HW_CSUM should not be
+ * set in features when NETIF_F_IP_CSUM or NETIF_F_IPV6_CSUM are set--
+ * this would be contradictory
+ */
+#define NETIF_F_CSUM_MASK (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
+ NETIF_F_HW_CSUM)
#define NETIF_F_ALL_TSO (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 3143c847bddb..5ac140dcb789 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -132,7 +132,9 @@ static inline bool dev_xmit_complete(int rc)
* used.
*/
-#if defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25)
+#if defined(CONFIG_HYPERV_NET)
+# define LL_MAX_HEADER 128
+#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25)
# if defined(CONFIG_MAC80211_MESH)
# define LL_MAX_HEADER 128
# else
@@ -326,7 +328,8 @@ enum {
NAPI_STATE_SCHED, /* Poll is scheduled */
NAPI_STATE_DISABLE, /* Disable pending */
NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */
- NAPI_STATE_HASHED, /* In NAPI hash */
+ NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */
+ NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
};
enum gro_result {
@@ -461,19 +464,13 @@ static inline void napi_complete(struct napi_struct *n)
}
/**
- * napi_by_id - lookup a NAPI by napi_id
- * @napi_id: hashed napi_id
- *
- * lookup @napi_id in napi_hash table
- * must be called under rcu_read_lock()
- */
-struct napi_struct *napi_by_id(unsigned int napi_id);
-
-/**
* napi_hash_add - add a NAPI to global hashtable
* @napi: napi context
*
* generate a new napi_id and store a @napi under it in napi_hash
+ * Used for busy polling (CONFIG_NET_RX_BUSY_POLL)
+ * Note: This is normally automatically done from netif_napi_add(),
+ * so might disappear in a future linux version.
*/
void napi_hash_add(struct napi_struct *napi);
@@ -482,9 +479,14 @@ void napi_hash_add(struct napi_struct *napi);
* @napi: napi context
*
* Warning: caller must observe rcu grace period
- * before freeing memory containing @napi
+ * before freeing memory containing @napi, if
+ * this function returns true.
+ * Note: core networking stack automatically calls it
+ * from netif_napi_del()
+ * Drivers might want to call this helper to combine all
+ * the needed rcu grace periods into a single one.
*/
-void napi_hash_del(struct napi_struct *napi);
+bool napi_hash_del(struct napi_struct *napi);
/**
* napi_disable - prevent NAPI from scheduling
@@ -810,6 +812,12 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* (can also return NETDEV_TX_LOCKED iff NETIF_F_LLTX)
* Required can not be NULL.
*
+ * netdev_features_t (*ndo_fix_features)(struct net_device *dev,
+ * netdev_features_t features);
+ * Adjusts the requested feature flags according to device-specific
+ * constraints, and returns the resulting flags. Must not modify
+ * the device state.
+ *
* u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
* void *accel_priv, select_queue_fallback_t fallback);
* Called to decide which queue to when device supports multiple
@@ -957,12 +965,6 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* Called to release previously enslaved netdev.
*
* Feature/offload setting functions.
- * netdev_features_t (*ndo_fix_features)(struct net_device *dev,
- * netdev_features_t features);
- * Adjusts the requested feature flags according to device-specific
- * constraints, and returns the resulting flags. Must not modify
- * the device state.
- *
* int (*ndo_set_features)(struct net_device *dev, netdev_features_t features);
* Called to update device configuration to new features. Passed
* feature set might be less than what was returned by ndo_fix_features()).
@@ -1011,6 +1013,19 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* a new port starts listening. The operation is protected by the
* vxlan_net->sock_lock.
*
+ * void (*ndo_add_geneve_port)(struct net_device *dev,
+ * sa_family_t sa_family, __be16 port);
+ * Called by geneve to notify a driver about the UDP port and socket
+ * address family that geneve is listnening to. It is called only when
+ * a new port starts listening. The operation is protected by the
+ * geneve_net->sock_lock.
+ *
+ * void (*ndo_del_geneve_port)(struct net_device *dev,
+ * sa_family_t sa_family, __be16 port);
+ * Called by geneve to notify the driver about a UDP port and socket
+ * address family that geneve is not listening to anymore. The operation
+ * is protected by the geneve_net->sock_lock.
+ *
* void (*ndo_del_vxlan_port)(struct net_device *dev,
* sa_family_t sa_family, __be16 port);
* Called by vxlan to notify the driver about a UDP port and socket
@@ -1066,8 +1081,11 @@ struct net_device_ops {
void (*ndo_uninit)(struct net_device *dev);
int (*ndo_open)(struct net_device *dev);
int (*ndo_stop)(struct net_device *dev);
- netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
- struct net_device *dev);
+ netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ struct net_device *dev);
+ netdev_features_t (*ndo_features_check)(struct sk_buff *skb,
+ struct net_device *dev,
+ netdev_features_t features);
u16 (*ndo_select_queue)(struct net_device *dev,
struct sk_buff *skb,
void *accel_priv,
@@ -1215,7 +1233,12 @@ struct net_device_ops {
void (*ndo_del_vxlan_port)(struct net_device *dev,
sa_family_t sa_family,
__be16 port);
-
+ void (*ndo_add_geneve_port)(struct net_device *dev,
+ sa_family_t sa_family,
+ __be16 port);
+ void (*ndo_del_geneve_port)(struct net_device *dev,
+ sa_family_t sa_family,
+ __be16 port);
void* (*ndo_dfwd_add_station)(struct net_device *pdev,
struct net_device *dev);
void (*ndo_dfwd_del_station)(struct net_device *pdev,
@@ -1225,9 +1248,6 @@ struct net_device_ops {
struct net_device *dev,
void *priv);
int (*ndo_get_lock_subclass)(struct net_device *dev);
- netdev_features_t (*ndo_features_check) (struct sk_buff *skb,
- struct net_device *dev,
- netdev_features_t features);
int (*ndo_set_tx_maxrate)(struct net_device *dev,
int queue_index,
u32 maxrate);
@@ -1271,6 +1291,7 @@ struct net_device_ops {
* @IFF_NO_QUEUE: device can run without qdisc attached
* @IFF_OPENVSWITCH: device is a Open vSwitch master
* @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device
+ * @IFF_TEAM: device is a team device
*/
enum netdev_priv_flags {
IFF_802_1Q_VLAN = 1<<0,
@@ -1297,6 +1318,7 @@ enum netdev_priv_flags {
IFF_NO_QUEUE = 1<<21,
IFF_OPENVSWITCH = 1<<22,
IFF_L3MDEV_SLAVE = 1<<23,
+ IFF_TEAM = 1<<24,
};
#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
@@ -1323,6 +1345,7 @@ enum netdev_priv_flags {
#define IFF_NO_QUEUE IFF_NO_QUEUE
#define IFF_OPENVSWITCH IFF_OPENVSWITCH
#define IFF_L3MDEV_SLAVE IFF_L3MDEV_SLAVE
+#define IFF_TEAM IFF_TEAM
/**
* struct net_device - The DEVICE structure.
@@ -1716,7 +1739,9 @@ struct net_device {
#ifdef CONFIG_XPS
struct xps_dev_maps __rcu *xps_maps;
#endif
-
+#ifdef CONFIG_NET_CLS_ACT
+ struct tcf_proto __rcu *egress_cl_list;
+#endif
#ifdef CONFIG_NET_SWITCHDEV
u32 offload_fwd_mark;
#endif
@@ -1949,6 +1974,26 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight);
/**
+ * netif_tx_napi_add - initialize a napi context
+ * @dev: network device
+ * @napi: napi context
+ * @poll: polling function
+ * @weight: default weight
+ *
+ * This variant of netif_napi_add() should be used from drivers using NAPI
+ * to exclusively poll a TX queue.
+ * This will avoid we add it into napi_hash[], thus polluting this hash table.
+ */
+static inline void netif_tx_napi_add(struct net_device *dev,
+ struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int),
+ int weight)
+{
+ set_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state);
+ netif_napi_add(dev, napi, poll, weight);
+}
+
+/**
* netif_napi_del - remove a napi context
* @napi: napi context
*
@@ -2086,6 +2131,24 @@ struct pcpu_sw_netstats {
#define netdev_alloc_pcpu_stats(type) \
__netdev_alloc_pcpu_stats(type, GFP_KERNEL)
+enum netdev_lag_tx_type {
+ NETDEV_LAG_TX_TYPE_UNKNOWN,
+ NETDEV_LAG_TX_TYPE_RANDOM,
+ NETDEV_LAG_TX_TYPE_BROADCAST,
+ NETDEV_LAG_TX_TYPE_ROUNDROBIN,
+ NETDEV_LAG_TX_TYPE_ACTIVEBACKUP,
+ NETDEV_LAG_TX_TYPE_HASH,
+};
+
+struct netdev_lag_upper_info {
+ enum netdev_lag_tx_type tx_type;
+};
+
+struct netdev_lag_lower_state_info {
+ u8 link_up : 1,
+ tx_enabled : 1;
+};
+
#include <linux/notifier.h>
/* netdevice notifier chain. Please remember to update the rtnetlink
@@ -2121,6 +2184,7 @@ struct pcpu_sw_netstats {
#define NETDEV_CHANGEINFODATA 0x0018
#define NETDEV_BONDING_INFO 0x0019
#define NETDEV_PRECHANGEUPPER 0x001A
+#define NETDEV_CHANGELOWERSTATE 0x001B
int register_netdevice_notifier(struct notifier_block *nb);
int unregister_netdevice_notifier(struct notifier_block *nb);
@@ -2139,6 +2203,12 @@ struct netdev_notifier_changeupper_info {
struct net_device *upper_dev; /* new upper dev */
bool master; /* is upper dev master */
bool linking; /* is the nofication for link or unlink */
+ void *upper_info; /* upper dev info */
+};
+
+struct netdev_notifier_changelowerstate_info {
+ struct netdev_notifier_info info; /* must be first */
+ void *lower_state_info; /* is lower dev state */
};
static inline void netdev_notifier_info_init(struct netdev_notifier_info *info,
@@ -2472,6 +2542,71 @@ static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
remcsum_unadjust((__sum16 *)ptr, grc->delta);
}
+struct skb_csum_offl_spec {
+ __u16 ipv4_okay:1,
+ ipv6_okay:1,
+ encap_okay:1,
+ ip_options_okay:1,
+ ext_hdrs_okay:1,
+ tcp_okay:1,
+ udp_okay:1,
+ sctp_okay:1,
+ vlan_okay:1,
+ no_encapped_ipv6:1,
+ no_not_encapped:1;
+};
+
+bool __skb_csum_offload_chk(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec,
+ bool *csum_encapped,
+ bool csum_help);
+
+static inline bool skb_csum_offload_chk(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec,
+ bool *csum_encapped,
+ bool csum_help)
+{
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return false;
+
+ return __skb_csum_offload_chk(skb, spec, csum_encapped, csum_help);
+}
+
+static inline bool skb_csum_offload_chk_help(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec)
+{
+ bool csum_encapped;
+
+ return skb_csum_offload_chk(skb, spec, &csum_encapped, true);
+}
+
+static inline bool skb_csum_off_chk_help_cmn(struct sk_buff *skb)
+{
+ static const struct skb_csum_offl_spec csum_offl_spec = {
+ .ipv4_okay = 1,
+ .ip_options_okay = 1,
+ .ipv6_okay = 1,
+ .vlan_okay = 1,
+ .tcp_okay = 1,
+ .udp_okay = 1,
+ };
+
+ return skb_csum_offload_chk_help(skb, &csum_offl_spec);
+}
+
+static inline bool skb_csum_off_chk_help_cmn_v4_only(struct sk_buff *skb)
+{
+ static const struct skb_csum_offl_spec csum_offl_spec = {
+ .ipv4_okay = 1,
+ .ip_options_okay = 1,
+ .tcp_okay = 1,
+ .udp_okay = 1,
+ .vlan_okay = 1,
+ };
+
+ return skb_csum_offload_chk_help(skb, &csum_offl_spec);
+}
+
static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type,
const void *daddr, const void *saddr,
@@ -3595,15 +3730,15 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev);
struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev);
int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev);
int netdev_master_upper_dev_link(struct net_device *dev,
- struct net_device *upper_dev);
-int netdev_master_upper_dev_link_private(struct net_device *dev,
- struct net_device *upper_dev,
- void *private);
+ struct net_device *upper_dev,
+ void *upper_priv, void *upper_info);
void netdev_upper_dev_unlink(struct net_device *dev,
struct net_device *upper_dev);
void netdev_adjacent_rename_links(struct net_device *dev, char *oldname);
void *netdev_lower_dev_get_private(struct net_device *dev,
struct net_device *lower_dev);
+void netdev_lower_state_changed(struct net_device *lower_dev,
+ void *lower_state_info);
/* RSS keys are 40 or 52 bytes long */
#define NETDEV_RSS_KEY_LEN 52
@@ -3611,7 +3746,7 @@ extern u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];
void netdev_rss_key_fill(void *buffer, size_t len);
int dev_get_nest_level(struct net_device *dev,
- bool (*type_check)(struct net_device *dev));
+ bool (*type_check)(const struct net_device *dev));
int skb_checksum_help(struct sk_buff *skb);
struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
netdev_features_t features, bool tx_path);
@@ -3641,13 +3776,37 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth);
static inline bool can_checksum_protocol(netdev_features_t features,
__be16 protocol)
{
- return ((features & NETIF_F_GEN_CSUM) ||
- ((features & NETIF_F_V4_CSUM) &&
- protocol == htons(ETH_P_IP)) ||
- ((features & NETIF_F_V6_CSUM) &&
- protocol == htons(ETH_P_IPV6)) ||
- ((features & NETIF_F_FCOE_CRC) &&
- protocol == htons(ETH_P_FCOE)));
+ if (protocol == htons(ETH_P_FCOE))
+ return !!(features & NETIF_F_FCOE_CRC);
+
+ /* Assume this is an IP checksum (not SCTP CRC) */
+
+ if (features & NETIF_F_HW_CSUM) {
+ /* Can checksum everything */
+ return true;
+ }
+
+ switch (protocol) {
+ case htons(ETH_P_IP):
+ return !!(features & NETIF_F_IP_CSUM);
+ case htons(ETH_P_IPV6):
+ return !!(features & NETIF_F_IPV6_CSUM);
+ default:
+ return false;
+ }
+}
+
+/* Map an ethertype into IP protocol if possible */
+static inline int eproto_to_ipproto(int eproto)
+{
+ switch (eproto) {
+ case htons(ETH_P_IP):
+ return IPPROTO_IP;
+ case htons(ETH_P_IPV6):
+ return IPPROTO_IPV6;
+ default:
+ return -1;
+ }
}
#ifdef CONFIG_BUG
@@ -3712,15 +3871,14 @@ void linkwatch_run_queue(void);
static inline netdev_features_t netdev_intersect_features(netdev_features_t f1,
netdev_features_t f2)
{
- if (f1 & NETIF_F_GEN_CSUM)
- f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
- if (f2 & NETIF_F_GEN_CSUM)
- f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
- f1 &= f2;
- if (f1 & NETIF_F_GEN_CSUM)
- f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+ if ((f1 ^ f2) & NETIF_F_HW_CSUM) {
+ if (f1 & NETIF_F_HW_CSUM)
+ f1 |= (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
+ else
+ f2 |= (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
+ }
- return f1;
+ return f1 & f2;
}
static inline netdev_features_t netdev_get_wanted_features(
@@ -3808,32 +3966,32 @@ static inline void skb_gso_error_unwind(struct sk_buff *skb, __be16 protocol,
skb->mac_len = mac_len;
}
-static inline bool netif_is_macvlan(struct net_device *dev)
+static inline bool netif_is_macvlan(const struct net_device *dev)
{
return dev->priv_flags & IFF_MACVLAN;
}
-static inline bool netif_is_macvlan_port(struct net_device *dev)
+static inline bool netif_is_macvlan_port(const struct net_device *dev)
{
return dev->priv_flags & IFF_MACVLAN_PORT;
}
-static inline bool netif_is_ipvlan(struct net_device *dev)
+static inline bool netif_is_ipvlan(const struct net_device *dev)
{
return dev->priv_flags & IFF_IPVLAN_SLAVE;
}
-static inline bool netif_is_ipvlan_port(struct net_device *dev)
+static inline bool netif_is_ipvlan_port(const struct net_device *dev)
{
return dev->priv_flags & IFF_IPVLAN_MASTER;
}
-static inline bool netif_is_bond_master(struct net_device *dev)
+static inline bool netif_is_bond_master(const struct net_device *dev)
{
return dev->flags & IFF_MASTER && dev->priv_flags & IFF_BONDING;
}
-static inline bool netif_is_bond_slave(struct net_device *dev)
+static inline bool netif_is_bond_slave(const struct net_device *dev)
{
return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING;
}
@@ -3868,6 +4026,26 @@ static inline bool netif_is_ovs_master(const struct net_device *dev)
return dev->priv_flags & IFF_OPENVSWITCH;
}
+static inline bool netif_is_team_master(const struct net_device *dev)
+{
+ return dev->priv_flags & IFF_TEAM;
+}
+
+static inline bool netif_is_team_port(const struct net_device *dev)
+{
+ return dev->priv_flags & IFF_TEAM_PORT;
+}
+
+static inline bool netif_is_lag_master(const struct net_device *dev)
+{
+ return netif_is_bond_master(dev) || netif_is_team_master(dev);
+}
+
+static inline bool netif_is_lag_port(const struct net_device *dev)
+{
+ return netif_is_bond_slave(dev) || netif_is_team_port(dev);
+}
+
/* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */
static inline void netif_keep_dst(struct net_device *dev)
{
diff --git a/include/linux/netfilter/nf_conntrack_sctp.h b/include/linux/netfilter/nf_conntrack_sctp.h
new file mode 100644
index 000000000000..22a16a23cd8a
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_sctp.h
@@ -0,0 +1,13 @@
+#ifndef _NF_CONNTRACK_SCTP_H
+#define _NF_CONNTRACK_SCTP_H
+/* SCTP tracking. */
+
+#include <uapi/linux/netfilter/nf_conntrack_sctp.h>
+
+struct ip_ct_sctp {
+ enum sctp_conntrack state;
+
+ __be32 vtag[IP_CT_DIR_MAX];
+};
+
+#endif /* _NF_CONNTRACK_SCTP_H */
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 5646b24bfc64..ba0d9789eb6e 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -8,12 +8,12 @@
#include <uapi/linux/netfilter/nfnetlink.h>
struct nfnl_callback {
- int (*call)(struct sock *nl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[]);
- int (*call_rcu)(struct sock *nl, struct sk_buff *skb,
+ int (*call)(struct net *net, struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
+ int (*call_rcu)(struct net *net, struct sock *nl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[]);
int (*call_batch)(struct net *net, struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
@@ -26,8 +26,8 @@ struct nfnetlink_subsystem {
__u8 subsys_id; /* nfnetlink subsystem ID */
__u8 cb_count; /* number of callbacks */
const struct nfnl_callback *cb; /* callback for individual types */
- int (*commit)(struct sk_buff *skb);
- int (*abort)(struct sk_buff *skb);
+ int (*commit)(struct net *net, struct sk_buff *skb);
+ int (*abort)(struct net *net, struct sk_buff *skb);
};
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 639e9b8b0e4d..0b41959aab9f 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -131,6 +131,7 @@ netlink_skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
struct netlink_callback {
struct sk_buff *skb;
const struct nlmsghdr *nlh;
+ int (*start)(struct netlink_callback *);
int (*dump)(struct sk_buff * skb,
struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
@@ -153,6 +154,7 @@ struct nlmsghdr *
__nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int flags);
struct netlink_dump_control {
+ int (*start)(struct netlink_callback *);
int (*dump)(struct sk_buff *skb, struct netlink_callback *);
int (*done)(struct netlink_callback *);
void *data;
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index d9ba49cedc5d..1acbefc4bbda 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2495,6 +2495,8 @@
#define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700
#define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff
+#define PCI_VENDOR_ID_NETRONOME 0x19ee
+
#define PCI_VENDOR_ID_QMI 0x1a32
#define PCI_VENDOR_ID_AZWAVE 0x1a3b
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 05fde31b6dc6..d6f3641e7933 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -16,8 +16,10 @@
#ifndef __PHY_H
#define __PHY_H
+#include <linux/compiler.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
+#include <linux/mdio.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/timer.h>
@@ -58,6 +60,7 @@
#define PHY_HAS_INTERRUPT 0x00000001
#define PHY_HAS_MAGICANEG 0x00000002
#define PHY_IS_INTERNAL 0x00000004
+#define MDIO_DEVICE_IS_PHY 0x80000000
/* Interface Mode definitions */
typedef enum {
@@ -158,8 +161,8 @@ struct mii_bus {
const char *name;
char id[MII_BUS_ID_SIZE];
void *priv;
- int (*read)(struct mii_bus *bus, int phy_id, int regnum);
- int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);
+ int (*read)(struct mii_bus *bus, int addr, int regnum);
+ int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
int (*reset)(struct mii_bus *bus);
/*
@@ -178,7 +181,7 @@ struct mii_bus {
struct device dev;
/* list of all PHYs on bus */
- struct phy_device *phy_map[PHY_MAX_ADDR];
+ struct mdio_device *mdio_map[PHY_MAX_ADDR];
/* PHY addresses to be ignored when probing */
u32 phy_mask;
@@ -187,10 +190,10 @@ struct mii_bus {
u32 phy_ignore_ta_mask;
/*
- * Pointer to an array of interrupts, each PHY's
- * interrupt at the index matching its address
+ * An array of interrupts, each PHY's interrupt at the index
+ * matching its address
*/
- int *irq;
+ int irq[PHY_MAX_ADDR];
};
#define to_mii_bus(d) container_of(d, struct mii_bus, dev)
@@ -212,11 +215,6 @@ static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev)
void devm_mdiobus_free(struct device *dev, struct mii_bus *bus);
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
-int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
-int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
-int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
-int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val);
-
#define PHY_INTERRUPT_DISABLED 0x0
#define PHY_INTERRUPT_ENABLED 0x80000000
@@ -361,14 +359,12 @@ struct phy_c45_device_ids {
* handling, as well as handling shifts in PHY hardware state
*/
struct phy_device {
+ struct mdio_device mdio;
+
/* Information about the PHY type */
/* And management functions */
struct phy_driver *drv;
- struct mii_bus *bus;
-
- struct device dev;
-
u32 phy_id;
struct phy_c45_device_ids c45_ids;
@@ -384,9 +380,6 @@ struct phy_device {
phy_interface_t interface;
- /* Bus address of the PHY (0-31) */
- int addr;
-
/*
* forced speed & duplex (no autoneg)
* partner speed & duplex & pause (autoneg)
@@ -435,10 +428,12 @@ struct phy_device {
void (*adjust_link)(struct net_device *dev);
};
-#define to_phy_device(d) container_of(d, struct phy_device, dev)
+#define to_phy_device(d) container_of(to_mdio_device(d), \
+ struct phy_device, mdio)
/* struct phy_driver: Driver structure for a particular PHY type
*
+ * driver_data: static driver data
* phy_id: The result of reading the UID registers of this PHY
* type, and ANDing them with the phy_id_mask. This driver
* only works for PHYs with IDs which match this field
@@ -448,7 +443,6 @@ struct phy_device {
* by this PHY
* flags: A bitfield defining certain other features this PHY
* supports (like interrupts)
- * driver_data: static driver data
*
* The drivers must implement config_aneg and read_status. All
* other functions are optional. Note that none of these
@@ -459,6 +453,7 @@ struct phy_device {
* supported in the driver).
*/
struct phy_driver {
+ struct mdio_driver_common mdiodrv;
u32 phy_id;
char *name;
unsigned int phy_id_mask;
@@ -589,9 +584,14 @@ struct phy_driver {
int (*module_eeprom)(struct phy_device *dev,
struct ethtool_eeprom *ee, u8 *data);
- struct device_driver driver;
+ /* Get statistics from the phy using ethtool */
+ int (*get_sset_count)(struct phy_device *dev);
+ void (*get_strings)(struct phy_device *dev, u8 *data);
+ void (*get_stats)(struct phy_device *dev,
+ struct ethtool_stats *stats, u64 *data);
};
-#define to_phy_driver(d) container_of(d, struct phy_driver, driver)
+#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
+ struct phy_driver, mdiodrv)
#define PHY_ANY_ID "MATCH ANY PHY"
#define PHY_ANY_UID 0xffffffff
@@ -619,7 +619,7 @@ static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
if (!phydev->is_c45)
return -EOPNOTSUPP;
- return mdiobus_read(phydev->bus, phydev->addr,
+ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff));
}
@@ -627,14 +627,12 @@ static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
* phy_read_mmd_indirect - reads data from the MMD registers
* @phydev: The PHY device bus
* @prtad: MMD Address
- * @devad: MMD DEVAD
* @addr: PHY address on the MII bus
*
* Description: it reads data from the MMD registers (clause 22 to access to
* clause 45) of the specified phy address.
*/
-int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr);
+int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad);
/**
* phy_read - Convenience function for reading a given PHY register
@@ -647,7 +645,7 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
*/
static inline int phy_read(struct phy_device *phydev, u32 regnum)
{
- return mdiobus_read(phydev->bus, phydev->addr, regnum);
+ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum);
}
/**
@@ -662,7 +660,7 @@ static inline int phy_read(struct phy_device *phydev, u32 regnum)
*/
static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val)
{
- return mdiobus_write(phydev->bus, phydev->addr, regnum, val);
+ return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
}
/**
@@ -725,7 +723,7 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad,
regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff);
- return mdiobus_write(phydev->bus, phydev->addr, regnum, val);
+ return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
}
/**
@@ -733,14 +731,13 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad,
* @phydev: The PHY device
* @prtad: MMD Address
* @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
* @data: data to write in the MMD register
*
* Description: Write data from the MMD registers of the specified
* phy address.
*/
void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr, u32 data);
+ int devad, u32 data);
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
bool is_c45,
@@ -775,6 +772,20 @@ static inline int phy_read_status(struct phy_device *phydev)
return phydev->drv->read_status(phydev);
}
+#define phydev_err(_phydev, format, args...) \
+ dev_err(&_phydev->mdio.dev, format, ##args)
+
+#define phydev_dbg(_phydev, format, args...) \
+ dev_dbg(&_phydev->mdio.dev, format, ##args);
+
+static inline const char *phydev_name(const struct phy_device *phydev)
+{
+ return dev_name(&phydev->mdio.dev);
+}
+
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+ __printf(2, 3);
+void phy_attached_info(struct phy_device *phydev);
int genphy_config_init(struct phy_device *phydev);
int genphy_setup_forced(struct phy_device *phydev);
int genphy_restart_aneg(struct phy_device *phydev);
@@ -787,8 +798,9 @@ int genphy_resume(struct phy_device *phydev);
int genphy_soft_reset(struct phy_device *phydev);
void phy_driver_unregister(struct phy_driver *drv);
void phy_drivers_unregister(struct phy_driver *drv, int n);
-int phy_driver_register(struct phy_driver *new_driver);
-int phy_drivers_register(struct phy_driver *new_driver, int n);
+int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
+int phy_drivers_register(struct phy_driver *new_driver, int n,
+ struct module *owner);
void phy_state_machine(struct work_struct *work);
void phy_change(struct work_struct *work);
void phy_mac_interrupt(struct phy_device *phydev, int new_link);
@@ -833,7 +845,7 @@ extern struct bus_type mdio_bus_type;
#define phy_module_driver(__phy_drivers, __count) \
static int __init phy_module_init(void) \
{ \
- return phy_drivers_register(__phy_drivers, __count); \
+ return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \
} \
module_init(phy_module_init); \
static void __exit phy_module_exit(void) \
diff --git a/include/linux/pim.h b/include/linux/pim.h
index 252bf6644c51..e1d756f81348 100644
--- a/include/linux/pim.h
+++ b/include/linux/pim.h
@@ -13,6 +13,11 @@
#define PIM_NULL_REGISTER cpu_to_be32(0x40000000)
+static inline bool ipmr_pimsm_enabled(void)
+{
+ return IS_BUILTIN(CONFIG_IP_PIMSM_V1) || IS_BUILTIN(CONFIG_IP_PIMSM_V2);
+}
+
/* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */
struct pimreghdr
{
diff --git a/include/linux/platform_data/microread.h b/include/linux/platform_data/microread.h
index cfda59b226ee..ca13992089b8 100644
--- a/include/linux/platform_data/microread.h
+++ b/include/linux/platform_data/microread.h
@@ -1,5 +1,5 @@
/*
- * Driver include for the PN544 NFC chip.
+ * Driver include for the Inside Secure microread NFC Chip.
*
* Copyright (C) 2011 Tieto Poland
* Copyright (C) 2012 Intel Corporation. All rights reserved.
diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h
index dc9a1353f971..d4a32e878180 100644
--- a/include/linux/qed/qed_if.h
+++ b/include/linux/qed/qed_if.h
@@ -25,6 +25,12 @@
#include <linux/qed/common_hsi.h>
#include <linux/qed/qed_chain.h>
+enum qed_led_mode {
+ QED_LED_MODE_OFF,
+ QED_LED_MODE_ON,
+ QED_LED_MODE_RESTORE
+};
+
#define DIRECT_REG_WR(reg_addr, val) writel((u32)val, \
(void __iomem *)(reg_addr))
@@ -252,6 +258,17 @@ struct qed_common_ops {
void (*chain_free)(struct qed_dev *cdev,
struct qed_chain *p_chain);
+
+/**
+ * @brief set_led - Configure LED mode
+ *
+ * @param cdev
+ * @param mode - LED mode
+ *
+ * @return 0 on success, error otherwise.
+ */
+ int (*set_led)(struct qed_dev *cdev,
+ enum qed_led_mode mode);
};
/**
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
index e50b31d18462..63bd7601b6de 100644
--- a/include/linux/rhashtable.h
+++ b/include/linux/rhashtable.h
@@ -823,4 +823,86 @@ out:
return err;
}
+/* Internal function, please use rhashtable_replace_fast() instead */
+static inline int __rhashtable_replace_fast(
+ struct rhashtable *ht, struct bucket_table *tbl,
+ struct rhash_head *obj_old, struct rhash_head *obj_new,
+ const struct rhashtable_params params)
+{
+ struct rhash_head __rcu **pprev;
+ struct rhash_head *he;
+ spinlock_t *lock;
+ unsigned int hash;
+ int err = -ENOENT;
+
+ /* Minimally, the old and new objects must have same hash
+ * (which should mean identifiers are the same).
+ */
+ hash = rht_head_hashfn(ht, tbl, obj_old, params);
+ if (hash != rht_head_hashfn(ht, tbl, obj_new, params))
+ return -EINVAL;
+
+ lock = rht_bucket_lock(tbl, hash);
+
+ spin_lock_bh(lock);
+
+ pprev = &tbl->buckets[hash];
+ rht_for_each(he, tbl, hash) {
+ if (he != obj_old) {
+ pprev = &he->next;
+ continue;
+ }
+
+ rcu_assign_pointer(obj_new->next, obj_old->next);
+ rcu_assign_pointer(*pprev, obj_new);
+ err = 0;
+ break;
+ }
+
+ spin_unlock_bh(lock);
+
+ return err;
+}
+
+/**
+ * rhashtable_replace_fast - replace an object in hash table
+ * @ht: hash table
+ * @obj_old: pointer to hash head inside object being replaced
+ * @obj_new: pointer to hash head inside object which is new
+ * @params: hash table parameters
+ *
+ * Replacing an object doesn't affect the number of elements in the hash table
+ * or bucket, so we don't need to worry about shrinking or expanding the
+ * table here.
+ *
+ * Returns zero on success, -ENOENT if the entry could not be found,
+ * -EINVAL if hash is not the same for the old and new objects.
+ */
+static inline int rhashtable_replace_fast(
+ struct rhashtable *ht, struct rhash_head *obj_old,
+ struct rhash_head *obj_new,
+ const struct rhashtable_params params)
+{
+ struct bucket_table *tbl;
+ int err;
+
+ rcu_read_lock();
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+
+ /* Because we have already taken (and released) the bucket
+ * lock in old_tbl, if we find that future_tbl is not yet
+ * visible then that guarantees the entry to still be in
+ * the old tbl if it exists.
+ */
+ while ((err = __rhashtable_replace_fast(ht, tbl, obj_old,
+ obj_new, params)) &&
+ (tbl = rht_dereference_rcu(tbl->future_tbl, ht)))
+ ;
+
+ rcu_read_unlock();
+
+ return err;
+}
+
#endif /* _LINUX_RHASHTABLE_H */
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 4be5048b1fbe..c006cc900c44 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -84,6 +84,11 @@ void net_inc_ingress_queue(void);
void net_dec_ingress_queue(void);
#endif
+#ifdef CONFIG_NET_EGRESS
+void net_inc_egress_queue(void);
+void net_dec_egress_queue(void);
+#endif
+
extern void rtnetlink_init(void);
extern void __rtnl_unlock(void);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 4bae8ab3b893..61aa9bbea871 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -834,6 +834,7 @@ struct user_struct {
unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */
#endif
unsigned long locked_shm; /* How many pages of mlocked shm ? */
+ unsigned long unix_inflight; /* How many files in flight in unix sockets */
#ifdef CONFIG_KEYS
struct key *uid_keyring; /* UID specific keyring */
diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h
index 8c9131db2b25..f2e27e078362 100644
--- a/include/linux/sh_eth.h
+++ b/include/linux/sh_eth.h
@@ -4,7 +4,7 @@
#include <linux/phy.h>
#include <linux/if_ether.h>
-enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN};
+enum {EDMAC_LITTLE_ENDIAN};
struct sh_eth_plat_data {
int phy;
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 4355129fff91..07f9ccd28654 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -39,11 +39,55 @@
#include <linux/in6.h>
#include <net/flow.h>
-/* A. Checksumming of received packets by device.
+/* The interface for checksum offload between the stack and networking drivers
+ * is as follows...
+ *
+ * A. IP checksum related features
+ *
+ * Drivers advertise checksum offload capabilities in the features of a device.
+ * From the stack's point of view these are capabilities offered by the driver,
+ * a driver typically only advertises features that it is capable of offloading
+ * to its device.
+ *
+ * The checksum related features are:
+ *
+ * NETIF_F_HW_CSUM - The driver (or its device) is able to compute one
+ * IP (one's complement) checksum for any combination
+ * of protocols or protocol layering. The checksum is
+ * computed and set in a packet per the CHECKSUM_PARTIAL
+ * interface (see below).
+ *
+ * NETIF_F_IP_CSUM - Driver (device) is only able to checksum plain
+ * TCP or UDP packets over IPv4. These are specifically
+ * unencapsulated packets of the form IPv4|TCP or
+ * IPv4|UDP where the Protocol field in the IPv4 header
+ * is TCP or UDP. The IPv4 header may contain IP options
+ * This feature cannot be set in features for a device
+ * with NETIF_F_HW_CSUM also set. This feature is being
+ * DEPRECATED (see below).
+ *
+ * NETIF_F_IPV6_CSUM - Driver (device) is only able to checksum plain
+ * TCP or UDP packets over IPv6. These are specifically
+ * unencapsulated packets of the form IPv6|TCP or
+ * IPv4|UDP where the Next Header field in the IPv6
+ * header is either TCP or UDP. IPv6 extension headers
+ * are not supported with this feature. This feature
+ * cannot be set in features for a device with
+ * NETIF_F_HW_CSUM also set. This feature is being
+ * DEPRECATED (see below).
+ *
+ * NETIF_F_RXCSUM - Driver (device) performs receive checksum offload.
+ * This flag is used only used to disable the RX checksum
+ * feature for a device. The stack will accept receive
+ * checksum indication in packets received on a device
+ * regardless of whether NETIF_F_RXCSUM is set.
+ *
+ * B. Checksumming of received packets by device. Indication of checksum
+ * verification is in set skb->ip_summed. Possible values are:
*
* CHECKSUM_NONE:
*
- * Device failed to checksum this packet e.g. due to lack of capabilities.
+ * Device did not checksum this packet e.g. due to lack of capabilities.
* The packet contains full (though not verified) checksum in packet but
* not in skb->csum. Thus, skb->csum is undefined in this case.
*
@@ -53,9 +97,8 @@
* (as in CHECKSUM_COMPLETE), but it does parse headers and verify checksums
* for specific protocols. For such packets it will set CHECKSUM_UNNECESSARY
* if their checksums are okay. skb->csum is still undefined in this case
- * though. It is a bad option, but, unfortunately, nowadays most vendors do
- * this. Apparently with the secret goal to sell you new devices, when you
- * will add new protocol to your host, f.e. IPv6 8)
+ * though. A driver or device must never modify the checksum field in the
+ * packet even if checksum is verified.
*
* CHECKSUM_UNNECESSARY is applicable to following protocols:
* TCP: IPv6 and IPv4.
@@ -96,40 +139,77 @@
* packet that are after the checksum being offloaded are not considered to
* be verified.
*
- * B. Checksumming on output.
- *
- * CHECKSUM_NONE:
- *
- * The skb was already checksummed by the protocol, or a checksum is not
- * required.
+ * C. Checksumming on transmit for non-GSO. The stack requests checksum offload
+ * in the skb->ip_summed for a packet. Values are:
*
* CHECKSUM_PARTIAL:
*
- * The device is required to checksum the packet as seen by hard_start_xmit()
+ * The driver is required to checksum the packet as seen by hard_start_xmit()
* from skb->csum_start up to the end, and to record/write the checksum at
- * offset skb->csum_start + skb->csum_offset.
+ * offset skb->csum_start + skb->csum_offset. A driver may verify that the
+ * csum_start and csum_offset values are valid values given the length and
+ * offset of the packet, however they should not attempt to validate that the
+ * checksum refers to a legitimate transport layer checksum-- it is the
+ * purview of the stack to validate that csum_start and csum_offset are set
+ * correctly.
+ *
+ * When the stack requests checksum offload for a packet, the driver MUST
+ * ensure that the checksum is set correctly. A driver can either offload the
+ * checksum calculation to the device, or call skb_checksum_help (in the case
+ * that the device does not support offload for a particular checksum).
+ *
+ * NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM are being deprecated in favor of
+ * NETIF_F_HW_CSUM. New devices should use NETIF_F_HW_CSUM to indicate
+ * checksum offload capability. If a device has limited checksum capabilities
+ * (for instance can only perform NETIF_F_IP_CSUM or NETIF_F_IPV6_CSUM as
+ * described above) a helper function can be called to resolve
+ * CHECKSUM_PARTIAL. The helper functions are skb_csum_off_chk*. The helper
+ * function takes a spec argument that describes the protocol layer that is
+ * supported for checksum offload and can be called for each packet. If a
+ * packet does not match the specification for offload, skb_checksum_help
+ * is called to resolve the checksum.
*
- * The device must show its capabilities in dev->features, set up at device
- * setup time, e.g. netdev_features.h:
+ * CHECKSUM_NONE:
*
- * NETIF_F_HW_CSUM - It's a clever device, it's able to checksum everything.
- * NETIF_F_IP_CSUM - Device is dumb, it's able to checksum only TCP/UDP over
- * IPv4. Sigh. Vendors like this way for an unknown reason.
- * Though, see comment above about CHECKSUM_UNNECESSARY. 8)
- * NETIF_F_IPV6_CSUM - About as dumb as the last one but does IPv6 instead.
- * NETIF_F_... - Well, you get the picture.
+ * The skb was already checksummed by the protocol, or a checksum is not
+ * required.
*
* CHECKSUM_UNNECESSARY:
*
- * Normally, the device will do per protocol specific checksumming. Protocol
- * implementations that do not want the NIC to perform the checksum
- * calculation should use this flag in their outgoing skbs.
- *
- * NETIF_F_FCOE_CRC - This indicates that the device can do FCoE FC CRC
- * offload. Correspondingly, the FCoE protocol driver
- * stack should use CHECKSUM_UNNECESSARY.
+ * This has the same meaning on as CHECKSUM_NONE for checksum offload on
+ * output.
*
- * Any questions? No questions, good. --ANK
+ * CHECKSUM_COMPLETE:
+ * Not used in checksum output. If a driver observes a packet with this value
+ * set in skbuff, if should treat as CHECKSUM_NONE being set.
+ *
+ * D. Non-IP checksum (CRC) offloads
+ *
+ * NETIF_F_SCTP_CRC - This feature indicates that a device is capable of
+ * offloading the SCTP CRC in a packet. To perform this offload the stack
+ * will set ip_summed to CHECKSUM_PARTIAL and set csum_start and csum_offset
+ * accordingly. Note the there is no indication in the skbuff that the
+ * CHECKSUM_PARTIAL refers to an SCTP checksum, a driver that supports
+ * both IP checksum offload and SCTP CRC offload must verify which offload
+ * is configured for a packet presumably by inspecting packet headers.
+ *
+ * NETIF_F_FCOE_CRC - This feature indicates that a device is capable of
+ * offloading the FCOE CRC in a packet. To perform this offload the stack
+ * will set ip_summed to CHECKSUM_PARTIAL and set csum_start and csum_offset
+ * accordingly. Note the there is no indication in the skbuff that the
+ * CHECKSUM_PARTIAL refers to an FCOE checksum, a driver that supports
+ * both IP checksum offload and FCOE CRC offload must verify which offload
+ * is configured for a packet presumably by inspecting packet headers.
+ *
+ * E. Checksumming on output with GSO.
+ *
+ * In the case of a GSO packet (skb_is_gso(skb) is true), checksum offload
+ * is implied by the SKB_GSO_* flags in gso_type. Most obviously, if the
+ * gso_type is SKB_GSO_TCPV4 or SKB_GSO_TCPV6, TCP checksum offload as
+ * part of the GSO operation is implied. If a checksum is being offloaded
+ * with GSO then ip_summed is CHECKSUM_PARTIAL, csum_start and csum_offset
+ * are set to refer to the outermost checksum being offload (two offloaded
+ * checksums are possible with UDP encapsulation).
*/
/* Don't change this without changing skb_csum_unnecessary! */
@@ -833,7 +913,7 @@ struct sk_buff_fclones {
* skb_fclone_busy - check if fclone is busy
* @skb: buffer
*
- * Returns true is skb is a fast clone, and its clone is not freed.
+ * Returns true if skb is a fast clone, and its clone is not freed.
* Some drivers call skb_orphan() in their ndo_start_xmit(),
* so we also check that this didnt happen.
*/
@@ -1082,9 +1162,6 @@ static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from)
static inline void skb_sender_cpu_clear(struct sk_buff *skb)
{
-#ifdef CONFIG_XPS
- skb->sender_cpu = 0;
-#endif
}
#ifdef NET_SKBUFF_DATA_USES_OFFSET
@@ -1942,6 +2019,11 @@ static inline unsigned char *skb_inner_transport_header(const struct sk_buff
return skb->head + skb->inner_transport_header;
}
+static inline int skb_inner_transport_offset(const struct sk_buff *skb)
+{
+ return skb_inner_transport_header(skb) - skb->data;
+}
+
static inline void skb_reset_inner_transport_header(struct sk_buff *skb)
{
skb->inner_transport_header = skb->data - skb->head;
@@ -2723,6 +2805,23 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb,
unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len);
+static inline void skb_postpush_rcsum(struct sk_buff *skb,
+ const void *start, unsigned int len)
+{
+ /* For performing the reverse operation to skb_postpull_rcsum(),
+ * we can instead of ...
+ *
+ * skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
+ *
+ * ... just use this equivalent version here to save a few
+ * instructions. Feeding csum of 0 in csum_partial() and later
+ * on adding skb->csum is equivalent to feed skb->csum in the
+ * first place.
+ */
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->csum = csum_partial(start, len, skb->csum);
+}
+
/**
* pskb_trim_rcsum - trim received skb and update checksum
* @skb: buffer to trim
@@ -2788,6 +2887,12 @@ static inline void skb_frag_list_init(struct sk_buff *skb)
#define skb_walk_frags(skb, iter) \
for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next)
+
+int __skb_wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
+ const struct sk_buff *skb);
+struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned flags,
+ int *peeked, int *off, int *err,
+ struct sk_buff **last);
struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
int *peeked, int *off, int *err);
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
diff --git a/include/linux/soc/ti/knav_dma.h b/include/linux/soc/ti/knav_dma.h
index dad035c16d94..343c13ac4f71 100644
--- a/include/linux/soc/ti/knav_dma.h
+++ b/include/linux/soc/ti/knav_dma.h
@@ -144,17 +144,17 @@ struct knav_dma_cfg {
* @psdata: Protocol specific
*/
struct knav_dma_desc {
- u32 desc_info;
- u32 tag_info;
- u32 packet_info;
- u32 buff_len;
- u32 buff;
- u32 next_desc;
- u32 orig_len;
- u32 orig_buff;
- u32 epib[KNAV_DMA_NUM_EPIB_WORDS];
- u32 psdata[KNAV_DMA_NUM_PS_WORDS];
- u32 pad[4];
+ __le32 desc_info;
+ __le32 tag_info;
+ __le32 packet_info;
+ __le32 buff_len;
+ __le32 buff;
+ __le32 next_desc;
+ __le32 orig_len;
+ __le32 orig_buff;
+ __le32 epib[KNAV_DMA_NUM_EPIB_WORDS];
+ __le32 psdata[KNAV_DMA_NUM_PS_WORDS];
+ __le32 pad[4];
} ____cacheline_aligned;
#if IS_ENABLED(CONFIG_KEYSTONE_NAVIGATOR_DMA)
diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h
index fddebc617469..4018b48f2b3b 100644
--- a/include/linux/sock_diag.h
+++ b/include/linux/sock_diag.h
@@ -15,6 +15,7 @@ struct sock_diag_handler {
__u8 family;
int (*dump)(struct sk_buff *skb, struct nlmsghdr *nlh);
int (*get_info)(struct sk_buff *skb, struct sock *sk);
+ int (*destroy)(struct sk_buff *skb, struct nlmsghdr *nlh);
};
int sock_diag_register(const struct sock_diag_handler *h);
@@ -68,4 +69,5 @@ bool sock_diag_has_destroy_listeners(const struct sock *sk)
}
void sock_diag_broadcast_destroy(struct sock *sk);
+int sock_diag_destroy(struct sock *sk, int err);
#endif
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index c3d1a525bacc..26a0b3c3ce5f 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -524,13 +524,9 @@ struct ssb_init_invariants {
typedef int (*ssb_invariants_func_t)(struct ssb_bus *bus,
struct ssb_init_invariants *iv);
-/* Register a SSB system bus. get_invariants() is called after the
- * basic system devices are initialized.
- * The invariants are usually fetched from some NVRAM.
- * Put the invariants into the struct pointed to by iv. */
-extern int ssb_bus_ssbbus_register(struct ssb_bus *bus,
- unsigned long baseaddr,
- ssb_invariants_func_t get_invariants);
+/* Register SoC bus. */
+extern int ssb_bus_host_soc_register(struct ssb_bus *bus,
+ unsigned long baseaddr);
#ifdef CONFIG_SSB_PCIHOST
extern int ssb_bus_pcibus_register(struct ssb_bus *bus,
struct pci_dev *host_pci);
diff --git a/include/linux/wait.h b/include/linux/wait.h
index d2f4ec7dba7c..ae71a769b89e 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -137,6 +137,27 @@ static inline int waitqueue_active(wait_queue_head_t *q)
return !list_empty(&q->task_list);
}
+/**
+ * wq_has_sleeper - check if there are any waiting processes
+ * @wq: wait queue head
+ *
+ * Returns true if wq has waiting processes
+ *
+ * Please refer to the comment for waitqueue_active.
+ */
+static inline bool wq_has_sleeper(wait_queue_head_t *wq)
+{
+ /*
+ * We need to be sure we are in sync with the
+ * add_wait_queue modifications to the wait queue.
+ *
+ * This memory barrier should be paired with one on the
+ * waiting side.
+ */
+ smp_mb();
+ return waitqueue_active(wq);
+}
+
extern void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
extern void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);
extern void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index cf3bc564ac03..2f6a3f2233ed 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -53,6 +53,8 @@
#ifndef __6LOWPAN_H__
#define __6LOWPAN_H__
+#include <linux/debugfs.h>
+
#include <net/ipv6.h>
#include <net/net_namespace.h>
@@ -98,6 +100,7 @@ enum lowpan_lltypes {
struct lowpan_priv {
enum lowpan_lltypes lltype;
+ struct dentry *iface_debugfs;
/* must be last */
u8 priv[0] __aligned(sizeof(void *));
@@ -185,7 +188,12 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
*hc_ptr += len;
}
-void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype);
+int lowpan_register_netdevice(struct net_device *dev,
+ enum lowpan_lltypes lltype);
+int lowpan_register_netdev(struct net_device *dev,
+ enum lowpan_lltypes lltype);
+void lowpan_unregister_netdevice(struct net_device *dev);
+void lowpan_unregister_netdev(struct net_device *dev);
/**
* lowpan_header_decompress - replace 6LoWPAN header with IPv6 header
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 78003dfb8539..47f52d3cd8df 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -87,7 +87,8 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
u32 banned_flags);
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
u32 banned_flags);
-int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2);
+int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
+ bool match_wildcard);
void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr);
void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr);
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 42844d7b154a..bfd1590821d6 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -29,6 +29,8 @@
#include <net/sock.h>
#include <linux/seq_file.h>
+#define BT_SUBSYS_VERSION "2.21"
+
#ifndef AF_BLUETOOTH
#define AF_BLUETOOTH 31
#define PF_BLUETOOTH AF_BLUETOOTH
@@ -296,12 +298,17 @@ typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb);
+#define HCI_REQ_START BIT(0)
+#define HCI_REQ_SKB BIT(1)
+
struct hci_ctrl {
__u16 opcode;
- bool req_start;
+ u8 req_flags;
u8 req_event;
- hci_req_complete_t req_complete;
- hci_req_complete_skb_t req_complete_skb;
+ union {
+ hci_req_complete_t req_complete;
+ hci_req_complete_skb_t req_complete_skb;
+ };
};
struct bt_skb_cb {
@@ -316,15 +323,17 @@ struct bt_skb_cb {
};
#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
+#define hci_skb_pkt_type(skb) bt_cb((skb))->pkt_type
+#define hci_skb_expect(skb) bt_cb((skb))->expect
+#define hci_skb_opcode(skb) bt_cb((skb))->hci.opcode
+
static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how)
{
struct sk_buff *skb;
skb = alloc_skb(len + BT_SKB_RESERVE, how);
- if (skb) {
+ if (skb)
skb_reserve(skb, BT_SKB_RESERVE);
- bt_cb(skb)->incoming = 0;
- }
return skb;
}
@@ -334,10 +343,8 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk,
struct sk_buff *skb;
skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err);
- if (skb) {
+ if (skb)
skb_reserve(skb, BT_SKB_RESERVE);
- bt_cb(skb)->incoming = 0;
- }
if (!skb && *err)
return NULL;
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 0205b80cc90b..339ea57be423 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -239,7 +239,6 @@ enum {
HCI_LE_ENABLED,
HCI_ADVERTISING,
HCI_ADVERTISING_CONNECTABLE,
- HCI_ADVERTISING_INSTANCE,
HCI_CONNECTABLE,
HCI_DISCOVERABLE,
HCI_LIMITED_DISCOVERABLE,
@@ -452,7 +451,8 @@ enum {
#define HCI_ERROR_REMOTE_POWER_OFF 0x15
#define HCI_ERROR_LOCAL_HOST_TERM 0x16
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
-#define HCI_ERROR_INVALID_LL_PARAMS 0x1E
+#define HCI_ERROR_INVALID_LL_PARAMS 0x1e
+#define HCI_ERROR_UNSPECIFIED 0x1f
#define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c
/* Flow control modes */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 1878d0a96333..d4f82edb5cff 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -77,6 +77,7 @@ struct discovery_state {
u8 last_adv_data_len;
bool report_invalid_rssi;
bool result_filtering;
+ bool limited;
s8 rssi;
u16 uuid_count;
u8 (*uuids)[16];
@@ -327,6 +328,14 @@ struct hci_dev {
struct work_struct cmd_work;
struct work_struct tx_work;
+ struct work_struct discov_update;
+ struct work_struct bg_scan_update;
+ struct work_struct scan_update;
+ struct work_struct connectable_update;
+ struct work_struct discoverable_update;
+ struct delayed_work le_scan_disable;
+ struct delayed_work le_scan_restart;
+
struct sk_buff_head rx_q;
struct sk_buff_head raw_q;
struct sk_buff_head cmd_q;
@@ -370,9 +379,6 @@ struct hci_dev {
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
- struct delayed_work le_scan_disable;
- struct delayed_work le_scan_restart;
-
__s8 adv_tx_power;
__u8 adv_data[HCI_MAX_AD_LENGTH];
__u8 adv_data_len;
@@ -875,7 +881,7 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level,
- u16 conn_timeout, u8 role);
+ u16 conn_timeout);
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u16 conn_timeout,
u8 role);
@@ -1036,7 +1042,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type);
void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
-void hci_conn_params_clear_all(struct hci_dev *hdev);
void hci_conn_params_clear_disabled(struct hci_dev *hdev);
struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
@@ -1279,31 +1284,41 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
mutex_unlock(&hci_cb_list_lock);
}
-static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
+static inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type,
+ size_t *data_len)
{
size_t parsed = 0;
- if (data_len < 2)
- return false;
+ if (eir_len < 2)
+ return NULL;
- while (parsed < data_len - 1) {
- u8 field_len = data[0];
+ while (parsed < eir_len - 1) {
+ u8 field_len = eir[0];
if (field_len == 0)
break;
parsed += field_len + 1;
- if (parsed > data_len)
+ if (parsed > eir_len)
break;
- if (data[1] == type)
- return true;
+ if (eir[1] != type) {
+ eir += field_len + 1;
+ continue;
+ }
+
+ /* Zero length data */
+ if (field_len == 1)
+ return NULL;
- data += field_len + 1;
+ if (data_len)
+ *data_len = field_len - 1;
+
+ return &eir[2];
}
- return false;
+ return NULL;
}
static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
@@ -1431,10 +1446,8 @@ int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_index_added(struct hci_dev *hdev);
void mgmt_index_removed(struct hci_dev *hdev);
void mgmt_set_powered_failed(struct hci_dev *hdev, int err);
-int mgmt_powered(struct hci_dev *hdev, u8 powered);
-int mgmt_update_adv_data(struct hci_dev *hdev);
-void mgmt_discoverable_timeout(struct hci_dev *hdev);
-void mgmt_adv_timeout_expired(struct hci_dev *hdev);
+void mgmt_power_on(struct hci_dev *hdev, int err);
+void __mgmt_power_off(struct hci_dev *hdev);
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
@@ -1473,6 +1486,8 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
u8 status);
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
+void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status);
+void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status);
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
@@ -1487,8 +1502,15 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 bdaddr_type, u8 store_hint, u16 min_interval,
u16 max_interval, u16 latency, u16 timeout);
-void mgmt_reenable_advertising(struct hci_dev *hdev);
void mgmt_smp_complete(struct hci_conn *conn, bool complete);
+bool mgmt_get_connectable(struct hci_dev *hdev);
+void mgmt_set_connectable_complete(struct hci_dev *hdev, u8 status);
+void mgmt_set_discoverable_complete(struct hci_dev *hdev, u8 status);
+u8 mgmt_get_adv_discov_flags(struct hci_dev *hdev);
+void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev,
+ u8 instance);
+void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
+ u8 instance);
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier);
diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h
index 2b67567cf28d..587d0131b349 100644
--- a/include/net/bluetooth/hci_mon.h
+++ b/include/net/bluetooth/hci_mon.h
@@ -43,6 +43,8 @@ struct hci_mon_hdr {
#define HCI_MON_CLOSE_INDEX 9
#define HCI_MON_INDEX_INFO 10
#define HCI_MON_VENDOR_DIAG 11
+#define HCI_MON_SYSTEM_NOTE 12
+#define HCI_MON_USER_LOGGING 13
struct hci_mon_new_index {
__u8 type;
diff --git a/include/net/bluetooth/hci_sock.h b/include/net/bluetooth/hci_sock.h
index 9a46d665c1b5..8e9138acdae1 100644
--- a/include/net/bluetooth/hci_sock.h
+++ b/include/net/bluetooth/hci_sock.h
@@ -45,6 +45,7 @@ struct sockaddr_hci {
#define HCI_CHANNEL_USER 1
#define HCI_CHANNEL_MONITOR 2
#define HCI_CHANNEL_CONTROL 3
+#define HCI_CHANNEL_LOGGING 4
struct hci_filter {
unsigned long type_mask;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index b831242d48a4..ea73e0826aa7 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -571,6 +571,21 @@ struct mgmt_rp_remove_advertising {
__u8 instance;
} __packed;
+#define MGMT_OP_GET_ADV_SIZE_INFO 0x0040
+struct mgmt_cp_get_adv_size_info {
+ __u8 instance;
+ __le32 flags;
+} __packed;
+#define MGMT_GET_ADV_SIZE_INFO_SIZE 5
+struct mgmt_rp_get_adv_size_info {
+ __u8 instance;
+ __le32 flags;
+ __u8 max_adv_data_len;
+ __u8 max_scan_rsp_len;
+} __packed;
+
+#define MGMT_OP_START_LIMITED_DISCOVERY 0x0041
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/include/net/bonding.h b/include/net/bonding.h
index c1740a2794a3..ee6c52053aa3 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -165,7 +165,8 @@ struct slave {
u8 backup:1, /* indicates backup slave. Value corresponds with
BOND_STATE_ACTIVE and BOND_STATE_BACKUP */
inactive:1, /* indicates inactive slave */
- should_notify:1; /* indicateds whether the state changed */
+ should_notify:1, /* indicates whether the state changed */
+ should_notify_link:1; /* indicates whether the link changed */
u8 duplex;
u32 original_mtu;
u32 link_failure_count;
@@ -246,6 +247,7 @@ struct bonding {
((struct slave *) rtnl_dereference(dev->rx_handler_data))
void bond_queue_slave_event(struct slave *slave);
+void bond_lower_state_changed(struct slave *slave);
struct bond_vlan_tag {
__be16 vlan_proto;
@@ -327,6 +329,7 @@ static inline void bond_set_active_slave(struct slave *slave)
if (slave->backup) {
slave->backup = 0;
bond_queue_slave_event(slave);
+ bond_lower_state_changed(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
@@ -336,6 +339,7 @@ static inline void bond_set_backup_slave(struct slave *slave)
if (!slave->backup) {
slave->backup = 1;
bond_queue_slave_event(slave);
+ bond_lower_state_changed(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
@@ -348,6 +352,7 @@ static inline void bond_set_slave_state(struct slave *slave,
slave->backup = slave_state;
if (notify) {
+ bond_lower_state_changed(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
bond_queue_slave_event(slave);
slave->should_notify = 0;
@@ -379,6 +384,7 @@ static inline void bond_slave_state_notify(struct bonding *bond)
bond_for_each_slave(bond, tmp, iter) {
if (tmp->should_notify) {
+ bond_lower_state_changed(tmp);
rtmsg_ifinfo(RTM_NEWLINK, tmp->dev, 0, GFP_ATOMIC);
tmp->should_notify = 0;
}
@@ -504,10 +510,37 @@ static inline bool bond_is_slave_inactive(struct slave *slave)
return slave->inactive;
}
-static inline void bond_set_slave_link_state(struct slave *slave, int state)
+static inline void bond_set_slave_link_state(struct slave *slave, int state,
+ bool notify)
{
+ if (slave->link == state)
+ return;
+
slave->link = state;
- bond_queue_slave_event(slave);
+ if (notify) {
+ bond_queue_slave_event(slave);
+ bond_lower_state_changed(slave);
+ slave->should_notify_link = 0;
+ } else {
+ if (slave->should_notify_link)
+ slave->should_notify_link = 0;
+ else
+ slave->should_notify_link = 1;
+ }
+}
+
+static inline void bond_slave_link_notify(struct bonding *bond)
+{
+ struct list_head *iter;
+ struct slave *tmp;
+
+ bond_for_each_slave(bond, tmp, iter) {
+ if (tmp->should_notify_link) {
+ bond_queue_slave_event(tmp);
+ bond_lower_state_changed(tmp);
+ tmp->should_notify_link = 0;
+ }
+ }
}
static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be32 local)
diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h
index 1d67fb6b23a0..2fbeb1313c0f 100644
--- a/include/net/busy_poll.h
+++ b/include/net/busy_poll.h
@@ -72,50 +72,7 @@ static inline bool busy_loop_timeout(unsigned long end_time)
return time_after(now, 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_busy_loop(struct sock *sk, int nonblock)
-{
- unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0;
- const struct net_device_ops *ops;
- struct napi_struct *napi;
- int rc = false;
-
- /*
- * rcu read lock for napi hash
- * bh so we don't race with net_rx_action
- */
- rcu_read_lock_bh();
-
- napi = napi_by_id(sk->sk_napi_id);
- if (!napi)
- goto out;
-
- ops = napi->dev->netdev_ops;
- if (!ops->ndo_busy_poll)
- goto out;
-
- do {
- rc = ops->ndo_busy_poll(napi);
-
- if (rc == LL_FLUSH_FAILED)
- break; /* permanent failure */
-
- if (rc > 0)
- /* local bh are disabled so it is ok to use _BH */
- NET_ADD_STATS_BH(sock_net(sk),
- LINUX_MIB_BUSYPOLLRXPACKETS, rc);
- cpu_relax();
-
- } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) &&
- !need_resched() && !busy_loop_timeout(end_time));
-
- rc = !skb_queue_empty(&sk->sk_receive_queue);
-out:
- rcu_read_unlock_bh();
- return rc;
-}
+bool sk_busy_loop(struct sock *sk, int nonblock);
/* used in the NIC receive handler to mark the skb */
static inline void skb_mark_napi_id(struct sk_buff *skb,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 2c7bdb81d30c..9bcaaf7cd15a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2321,6 +2321,8 @@ struct cfg80211_qos_map {
* the driver, and will be valid until passed to cfg80211_scan_done().
* For scan results, call cfg80211_inform_bss(); you can call this outside
* the scan/scan_done bracket too.
+ * @abort_scan: Tell the driver to abort an ongoing scan. The driver shall
+ * indicate the status of the scan through cfg80211_scan_done().
*
* @auth: Request to authenticate with the specified peer
* (invoked with the wireless_dev mutex held)
@@ -2593,6 +2595,7 @@ struct cfg80211_ops {
int (*scan)(struct wiphy *wiphy,
struct cfg80211_scan_request *request);
+ void (*abort_scan)(struct wiphy *wiphy, struct wireless_dev *wdev);
int (*auth)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_auth_request *req);
@@ -5173,8 +5176,11 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
* buffer starts, which may be @ielen if the entire (remainder)
* of the buffer should be used.
*/
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset);
+static inline size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids, size_t offset)
+{
+ return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
/**
* cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
diff --git a/include/net/checksum.h b/include/net/checksum.h
index 9fcaedf994ee..10a16b5bd1c7 100644
--- a/include/net/checksum.h
+++ b/include/net/checksum.h
@@ -165,7 +165,8 @@ static inline __wsum remcsum_adjust(void *ptr, __wsum csum,
csum = csum_sub(csum, csum_partial(ptr, start, 0));
/* Set derived checksum in packet */
- delta = csum_sub(csum_fold(csum), *psum);
+ delta = csum_sub((__force __wsum)csum_fold(csum),
+ (__force __wsum)*psum);
*psum = csum_fold(csum);
return delta;
diff --git a/include/net/cls_cgroup.h b/include/net/cls_cgroup.h
index ccd6d8bffa4d..c0a92e2c286d 100644
--- a/include/net/cls_cgroup.h
+++ b/include/net/cls_cgroup.h
@@ -41,13 +41,12 @@ static inline u32 task_cls_classid(struct task_struct *p)
return classid;
}
-static inline void sock_update_classid(struct sock *sk)
+static inline void sock_update_classid(struct sock_cgroup_data *skcd)
{
u32 classid;
classid = task_cls_classid(current);
- if (classid != sk->sk_classid)
- sk->sk_classid = classid;
+ sock_cgroup_set_classid(skcd, classid);
}
static inline u32 task_get_classid(const struct sk_buff *skb)
@@ -64,17 +63,17 @@ static inline u32 task_get_classid(const struct sk_buff *skb)
* softirqs always disables bh.
*/
if (in_serving_softirq()) {
- /* If there is an sk_classid we'll use that. */
+ /* If there is an sock_cgroup_classid we'll use that. */
if (!skb->sk)
return 0;
- classid = skb->sk->sk_classid;
+ classid = sock_cgroup_classid(&skb->sk->sk_cgrp_data);
}
return classid;
}
#else /* !CONFIG_CGROUP_NET_CLASSID */
-static inline void sock_update_classid(struct sock *sk)
+static inline void sock_update_classid(struct sock_cgroup_data *skcd)
{
}
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 82a4c6011173..26a0e86e611e 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -16,6 +16,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/ethtool.h>
@@ -64,6 +65,13 @@ struct dsa_chip_data {
* NULL if there is only one switch chip.
*/
s8 *rtable;
+
+ /*
+ * A switch may have a GPIO line tied to its reset pin. Parse
+ * this from the device tree, and use it before performing
+ * switch soft reset.
+ */
+ struct gpio_desc *reset;
};
struct dsa_platform_data {
@@ -109,13 +117,6 @@ struct dsa_switch_tree {
s8 cpu_port;
/*
- * Link state polling.
- */
- int link_poll_needed;
- struct work_struct link_poll_work;
- struct timer_list link_poll_timer;
-
- /*
* Data for the individual switch chips.
*/
struct dsa_switch *ds[DSA_MAX_SWITCHES];
@@ -224,11 +225,6 @@ struct dsa_switch_driver {
int regnum, u16 val);
/*
- * Link state polling and IRQ handling.
- */
- void (*poll_link)(struct dsa_switch *ds);
-
- /*
* Link state adjustment (called from libphy)
*/
void (*adjust_link)(struct dsa_switch *ds, int port,
diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index 1b6b6dcb018d..43c0e771f417 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -114,6 +114,7 @@ static inline void genl_info_net_set(struct genl_info *info, struct net *net)
* @flags: flags
* @policy: attribute validation policy
* @doit: standard command callback
+ * @start: start callback for dumps
* @dumpit: callback for dumpers
* @done: completion callback for dumps
* @ops_list: operations list
@@ -122,6 +123,7 @@ struct genl_ops {
const struct nla_policy *policy;
int (*doit)(struct sk_buff *skb,
struct genl_info *info);
+ int (*start)(struct netlink_callback *cb);
int (*dumpit)(struct sk_buff *skb,
struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
diff --git a/include/net/geneve.h b/include/net/geneve.h
index 3106ed6eae0d..e6c23dc765f7 100644
--- a/include/net/geneve.h
+++ b/include/net/geneve.h
@@ -62,6 +62,14 @@ struct genevehdr {
struct geneve_opt options[];
};
+#if IS_ENABLED(CONFIG_GENEVE)
+void geneve_get_rx_port(struct net_device *netdev);
+#else
+static inline void geneve_get_rx_port(struct net_device *netdev)
+{
+}
+#endif
+
#ifdef CONFIG_INET
struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
u8 name_assign_type, u16 dst_port);
diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h
index a62a051a3a2f..c4b31601cd53 100644
--- a/include/net/ieee802154_netdev.h
+++ b/include/net/ieee802154_netdev.h
@@ -337,7 +337,7 @@ struct ieee802154_mlme_ops {
void (*get_mac_params)(struct net_device *dev,
struct ieee802154_mac_params *params);
- struct ieee802154_llsec_ops *llsec;
+ const struct ieee802154_llsec_ops *llsec;
};
static inline struct ieee802154_mlme_ops *
diff --git a/include/net/ila.h b/include/net/ila.h
new file mode 100644
index 000000000000..9f4f43e94ae4
--- /dev/null
+++ b/include/net/ila.h
@@ -0,0 +1,18 @@
+/*
+ * ILA kernel interface
+ *
+ * Copyright (c) 2015 Tom Herbert <tom@herbertland.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _NET_ILA_H
+#define _NET_ILA_H
+
+int ila_xlat_outgoing(struct sk_buff *skb);
+int ila_xlat_incoming(struct sk_buff *skb);
+
+#endif /* _NET_ILA_H */
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index ac42bbb37b2d..12aac0fd6ee7 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -99,7 +99,6 @@ struct inet_frags {
void (*constructor)(struct inet_frag_queue *q,
const void *arg);
void (*destructor)(struct inet_frag_queue *);
- void (*skb_free)(struct sk_buff *);
void (*frag_expire)(unsigned long data);
struct kmem_cache *frags_cachep;
const char *frags_cache_name;
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index 625bdf95d673..012b1f91f3ec 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -28,6 +28,7 @@
#include <net/request_sock.h>
#include <net/netns/hash.h>
#include <net/tcp_states.h>
+#include <net/l3mdev.h>
/** struct ip_options - IP Options
*
@@ -113,6 +114,19 @@ static inline u32 inet_request_mark(const struct sock *sk, struct sk_buff *skb)
return sk->sk_mark;
}
+static inline int inet_request_bound_dev_if(const struct sock *sk,
+ struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_L3_MASTER_DEV
+ struct net *net = sock_net(sk);
+
+ if (!sk->sk_bound_dev_if && net->ipv4.sysctl_tcp_l3mdev_accept)
+ return l3mdev_master_ifindex_by_index(net, skb->skb_iif);
+#endif
+
+ return sk->sk_bound_dev_if;
+}
+
struct inet_cork {
unsigned int flags;
__be32 addr;
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index ff788b665277..0d0ce0b2d870 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -5,6 +5,7 @@
#include <linux/netdevice.h>
#include <linux/if_tunnel.h>
#include <linux/ip6_tunnel.h>
+#include <net/ip_tunnels.h>
#define IP6TUNNEL_ERR_TIMEO (30*HZ)
@@ -80,25 +81,17 @@ __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
struct net *ip6_tnl_get_link_net(const struct net_device *dev);
int ip6_tnl_get_iflink(const struct net_device *dev);
+#ifdef CONFIG_INET
static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
struct net_device *dev)
{
- struct net_device_stats *stats = &dev->stats;
int pkt_len, err;
pkt_len = skb->len - skb_inner_network_offset(skb);
err = ip6_local_out(dev_net(skb_dst(skb)->dev), sk, skb);
-
- if (net_xmit_eval(err) == 0) {
- struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats);
- u64_stats_update_begin(&tstats->syncp);
- tstats->tx_bytes += pkt_len;
- tstats->tx_packets++;
- u64_stats_update_end(&tstats->syncp);
- put_cpu_ptr(tstats);
- } else {
- stats->tx_errors++;
- stats->tx_aborted_errors++;
- }
+ if (unlikely(net_xmit_eval(err)))
+ pkt_len = -1;
+ iptunnel_xmit_stats(dev, pkt_len);
}
#endif
+#endif
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 9f4df68105ab..7029527725dd 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -325,7 +325,8 @@ extern u32 fib_multipath_secret __read_mostly;
static inline int fib_multipath_hash(__be32 saddr, __be32 daddr)
{
- return jhash_2words(saddr, daddr, fib_multipath_secret) >> 1;
+ return jhash_2words((__force u32)saddr, (__force u32)daddr,
+ fib_multipath_secret) >> 1;
}
void fib_select_multipath(struct fib_result *res, int hash);
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 62a750a6a8f8..6db96ea0144f 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -273,32 +273,34 @@ static inline u8 ip_tunnel_ecn_encap(u8 tos, const struct iphdr *iph,
}
int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto);
-int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
- __be32 src, __be32 dst, u8 proto,
- u8 tos, u8 ttl, __be16 df, bool xnet);
+void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
+ __be32 src, __be32 dst, u8 proto,
+ u8 tos, u8 ttl, __be16 df, bool xnet);
struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
gfp_t flags);
struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum,
int gso_type_mask);
-static inline void iptunnel_xmit_stats(int err,
- struct net_device_stats *err_stats,
- struct pcpu_sw_netstats __percpu *stats)
+static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len)
{
- if (err > 0) {
- struct pcpu_sw_netstats *tstats = get_cpu_ptr(stats);
+ if (pkt_len > 0) {
+ struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats);
u64_stats_update_begin(&tstats->syncp);
- tstats->tx_bytes += err;
+ tstats->tx_bytes += pkt_len;
tstats->tx_packets++;
u64_stats_update_end(&tstats->syncp);
put_cpu_ptr(tstats);
- } else if (err < 0) {
- err_stats->tx_errors++;
- err_stats->tx_aborted_errors++;
} else {
- err_stats->tx_dropped++;
+ struct net_device_stats *err_stats = &dev->stats;
+
+ if (pkt_len < 0) {
+ err_stats->tx_errors++;
+ err_stats->tx_aborted_errors++;
+ } else {
+ err_stats->tx_dropped++;
+ }
}
}
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 9a5c9f013784..6570f379aba2 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -401,6 +401,21 @@ static inline void ipv6_addr_prefix(struct in6_addr *pfx,
pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b);
}
+static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
+ const struct in6_addr *pfx,
+ int plen)
+{
+ /* caller must guarantee 0 <= plen <= 128 */
+ int o = plen >> 3,
+ b = plen & 0x7;
+
+ memcpy(addr->s6_addr, pfx, o);
+ if (b != 0) {
+ addr->s6_addr[o] &= ~(0xff00 >> b);
+ addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
+ }
+}
+
static inline void __ipv6_addr_set_half(__be32 *addr,
__be32 wh, __be32 wl)
{
diff --git a/include/net/l3mdev.h b/include/net/l3mdev.h
index 5689a0c749f7..5567d46b3cff 100644
--- a/include/net/l3mdev.h
+++ b/include/net/l3mdev.h
@@ -51,6 +51,24 @@ static inline int l3mdev_master_ifindex(struct net_device *dev)
return ifindex;
}
+static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
+{
+ struct net_device *dev;
+ int rc = 0;
+
+ if (likely(ifindex)) {
+ rcu_read_lock();
+
+ dev = dev_get_by_index_rcu(net, ifindex);
+ if (dev)
+ rc = l3mdev_master_ifindex_rcu(dev);
+
+ rcu_read_unlock();
+ }
+
+ return rc;
+}
+
/* get index of an interface to use for FIB lookups. For devices
* enslaved to an L3 master device FIB lookups are based on the
* master index
@@ -170,6 +188,11 @@ static inline int l3mdev_master_ifindex(struct net_device *dev)
return 0;
}
+static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
+{
+ return 0;
+}
+
static inline int l3mdev_fib_oif_rcu(struct net_device *dev)
{
return dev ? dev->ifindex : 0;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 760bc4d5a2cf..7c30faff245f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1321,11 +1321,15 @@ struct ieee80211_channel_switch {
* interface. This flag should be set during interface addition,
* but may be set/cleared as late as authentication to an AP. It is
* only valid for managed/station mode interfaces.
+ * @IEEE80211_VIF_GET_NOA_UPDATE: request to handle NOA attributes
+ * and send P2P_PS notification to the driver if NOA changed, even
+ * this is not pure P2P vif.
*/
enum ieee80211_vif_flags {
IEEE80211_VIF_BEACON_FILTER = BIT(0),
IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1),
IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2),
+ IEEE80211_VIF_GET_NOA_UPDATE = BIT(3),
};
/**
@@ -1901,6 +1905,11 @@ struct ieee80211_txq {
* @IEEE80211_HW_BEACON_TX_STATUS: The device/driver provides TX status
* for sent beacons.
*
+ * @IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR: Hardware (or driver) requires that each
+ * station has a unique address, i.e. each station entry can be identified
+ * by just its MAC address; this prevents, for example, the same station
+ * from connecting to two virtual AP interfaces at the same time.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -1936,6 +1945,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_TDLS_WIDER_BW,
IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU,
IEEE80211_HW_BEACON_TX_STATUS,
+ IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -4863,6 +4873,28 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
void ieee80211_sta_eosp(struct ieee80211_sta *pubsta);
/**
+ * ieee80211_send_eosp_nullfunc - ask mac80211 to send NDP with EOSP
+ * @pubsta: the station
+ * @tid: the tid of the NDP
+ *
+ * Sometimes the device understands that it needs to close
+ * the Service Period unexpectedly. This can happen when
+ * sending frames that are filling holes in the BA window.
+ * In this case, the device can ask mac80211 to send a
+ * Nullfunc frame with EOSP set. When that happens, the
+ * driver must have called ieee80211_sta_set_buffered() to
+ * let mac80211 know that there are no buffered frames any
+ * more, otherwise mac80211 will get the more_data bit wrong.
+ * The low level driver must have made sure that the frame
+ * will be sent despite the station being in power-save.
+ * Mac80211 won't call allow_buffered_frames().
+ * Note that calling this function, doesn't exempt the driver
+ * from closing the EOSP properly, it will still have to call
+ * ieee80211_sta_eosp when the NDP is sent.
+ */
+void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
+
+/**
* ieee80211_iter_keys - iterate keys programmed into the device
* @hw: pointer obtained from ieee80211_alloc_hw()
* @vif: virtual interface to iterate, may be %NULL for all
@@ -4890,6 +4922,30 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
void *iter_data);
/**
+ * ieee80211_iter_keys_rcu - iterate keys programmed into the device
+ * @hw: pointer obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface to iterate, may be %NULL for all
+ * @iter: iterator function that will be called for each key
+ * @iter_data: custom data to pass to the iterator function
+ *
+ * This function can be used to iterate all the keys known to
+ * mac80211, even those that weren't previously programmed into
+ * the device. Note that due to locking reasons, keys of station
+ * in removal process will be skipped.
+ *
+ * This function requires being called in an RCU critical section,
+ * and thus iter must be atomic.
+ */
+void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data);
+
+/**
* ieee80211_iter_chan_contexts_atomic - iterate channel contexts
* @hw: pointre obtained from ieee80211_alloc_hw().
* @iter: iterator function
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 2dcea635ecce..4089abc6e9c0 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -121,6 +121,9 @@ struct net {
#if IS_ENABLED(CONFIG_NETFILTER_NETLINK_ACCT)
struct list_head nfnl_acct_list;
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ struct list_head nfct_timeout_list;
+#endif
#endif
#ifdef CONFIG_WEXT_CORE
struct sk_buff_head wext_nlevents;
diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h
index fb7da5bb76cc..ddf162f7966f 100644
--- a/include/net/netfilter/ipv6/nf_defrag_ipv6.h
+++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h
@@ -5,8 +5,7 @@ void nf_defrag_ipv6_enable(void);
int nf_ct_frag6_init(void);
void nf_ct_frag6_cleanup(void);
-struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user);
-void nf_ct_frag6_consume_orig(struct sk_buff *skb);
+int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user);
struct inet_frags_ctl;
diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h
index f72be38860a7..5cc5e9e6171a 100644
--- a/include/net/netfilter/nf_conntrack_timeout.h
+++ b/include/net/netfilter/nf_conntrack_timeout.h
@@ -104,7 +104,7 @@ static inline void nf_conntrack_timeout_fini(void)
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
-extern struct ctnl_timeout *(*nf_ct_timeout_find_get_hook)(const char *name);
+extern struct ctnl_timeout *(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name);
extern void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout);
#endif
diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h
new file mode 100644
index 000000000000..397dcae349f9
--- /dev/null
+++ b/include/net/netfilter/nf_dup_netdev.h
@@ -0,0 +1,6 @@
+#ifndef _NF_DUP_NETDEV_H_
+#define _NF_DUP_NETDEV_H_
+
+void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif);
+
+#endif
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 4bd7508bedc9..f6b1daf2e698 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -19,8 +19,6 @@ struct nft_pktinfo {
const struct net_device *out;
u8 pf;
u8 hook;
- u8 nhoff;
- u8 thoff;
u8 tprot;
/* for x_tables compatibility */
struct xt_action_param xt;
@@ -293,6 +291,8 @@ void nft_unregister_set(struct nft_set_ops *ops);
* @timeout: default timeout value in msecs
* @gc_int: garbage collection interval in msecs
* @policy: set parameterization (see enum nft_set_policies)
+ * @udlen: user data length
+ * @udata: user data
* @ops: set ops
* @pnet: network namespace
* @flags: set flags
@@ -312,6 +312,8 @@ struct nft_set {
u64 timeout;
u32 gc_int;
u16 policy;
+ u16 udlen;
+ unsigned char *udata;
/* runtime data below here */
const struct nft_set_ops *ops ____cacheline_aligned;
possible_net_t pnet;
@@ -823,10 +825,7 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai
return container_of(chain, struct nft_base_chain, chain);
}
-int nft_register_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops);
-void nft_unregister_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops);
+int __nft_release_basechain(struct nft_ctx *ctx);
unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);
@@ -882,7 +881,7 @@ struct nft_af_info {
};
int nft_register_afinfo(struct net *, struct nft_af_info *);
-void nft_unregister_afinfo(struct nft_af_info *);
+void nft_unregister_afinfo(struct net *, struct nft_af_info *);
int nft_register_chain_type(const struct nf_chain_type *);
void nft_unregister_chain_type(const struct nf_chain_type *);
@@ -890,6 +889,38 @@ void nft_unregister_chain_type(const struct nf_chain_type *);
int nft_register_expr(struct nft_expr_type *);
void nft_unregister_expr(struct nft_expr_type *);
+int nft_verdict_dump(struct sk_buff *skb, int type,
+ const struct nft_verdict *v);
+
+/**
+ * struct nft_traceinfo - nft tracing information and state
+ *
+ * @pkt: pktinfo currently processed
+ * @basechain: base chain currently processed
+ * @chain: chain currently processed
+ * @rule: rule that was evaluated
+ * @verdict: verdict given by rule
+ * @type: event type (enum nft_trace_types)
+ * @packet_dumped: packet headers sent in a previous traceinfo message
+ * @trace: other struct members are initialised
+ */
+struct nft_traceinfo {
+ const struct nft_pktinfo *pkt;
+ const struct nft_base_chain *basechain;
+ const struct nft_chain *chain;
+ const struct nft_rule *rule;
+ const struct nft_verdict *verdict;
+ enum nft_trace_types type;
+ bool packet_dumped;
+ bool trace;
+};
+
+void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
+ const struct nft_verdict *verdict,
+ const struct nft_chain *basechain);
+
+void nft_trace_notify(struct nft_traceinfo *info);
+
#define nft_dereference(p) \
nfnl_dereference(p, NFNL_SUBSYS_NFTABLES)
diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h
index c6f400cfaac8..a9060dd99db7 100644
--- a/include/net/netfilter/nf_tables_core.h
+++ b/include/net/netfilter/nf_tables_core.h
@@ -47,7 +47,17 @@ struct nft_payload {
enum nft_registers dreg:8;
};
+struct nft_payload_set {
+ enum nft_payload_bases base:8;
+ u8 offset;
+ u8 len;
+ enum nft_registers sreg:8;
+ u8 csum_type;
+ u8 csum_offset;
+};
+
extern const struct nft_expr_ops nft_payload_fast_ops;
+extern struct static_key_false nft_trace_enabled;
int nft_payload_module_init(void);
void nft_payload_module_exit(void);
diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h
index 711887a09e91..d27588c8dbd9 100644
--- a/include/net/netfilter/nft_meta.h
+++ b/include/net/netfilter/nft_meta.h
@@ -33,4 +33,7 @@ void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt);
+void nft_meta_set_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr);
+
#endif
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index c68926b4899c..2b7907a35568 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -86,11 +86,18 @@ struct netns_ipv4 {
int sysctl_fwmark_reflect;
int sysctl_tcp_fwmark_accept;
+#ifdef CONFIG_NET_L3_MASTER_DEV
+ int sysctl_tcp_l3mdev_accept;
+#endif
int sysctl_tcp_mtu_probing;
int sysctl_tcp_base_mss;
int sysctl_tcp_probe_threshold;
u32 sysctl_tcp_probe_interval;
+ int sysctl_tcp_keepalive_time;
+ int sysctl_tcp_keepalive_probes;
+ int sysctl_tcp_keepalive_intvl;
+
struct ping_group_range ping_group_range;
atomic_t dev_addr_genid;
diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h
index 8ba379f9e467..c501d67172b1 100644
--- a/include/net/netns/sctp.h
+++ b/include/net/netns/sctp.h
@@ -89,6 +89,13 @@ struct netns_sctp {
int pf_retrans;
/*
+ * Disable Potentially-Failed feature, the feature is enabled by default
+ * pf_enable - 0 : disable pf
+ * - >0 : enable pf
+ */
+ int pf_enable;
+
+ /*
* Policy for preforming sctp/socket accounting
* 0 - do socket level accounting, all assocs share sk_sndbuf
* 1 - do sctp accounting, each asoc may use sk_sndbuf bytes
diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h
index f2a9597ff53c..604190596cde 100644
--- a/include/net/netprio_cgroup.h
+++ b/include/net/netprio_cgroup.h
@@ -25,8 +25,6 @@ struct netprio_map {
u32 priomap[];
};
-void sock_update_netprioidx(struct sock *sk);
-
static inline u32 task_netprioidx(struct task_struct *p)
{
struct cgroup_subsys_state *css;
@@ -38,13 +36,25 @@ static inline u32 task_netprioidx(struct task_struct *p)
rcu_read_unlock();
return idx;
}
+
+static inline void sock_update_netprioidx(struct sock_cgroup_data *skcd)
+{
+ if (in_interrupt())
+ return;
+
+ sock_cgroup_set_prioidx(skcd, task_netprioidx(current));
+}
+
#else /* !CONFIG_CGROUP_NET_PRIO */
+
static inline u32 task_netprioidx(struct task_struct *p)
{
return 0;
}
-#define sock_update_netprioidx(sk)
+static inline void sock_update_netprioidx(struct sock_cgroup_data *skcd)
+{
+}
#endif /* CONFIG_CGROUP_NET_PRIO */
#endif /* _NET_CLS_CGROUP_H */
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index dcfcfc9c00bf..1a3de8b34ad2 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -299,6 +299,7 @@ void nfc_driver_failure(struct nfc_dev *dev, int err);
int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx,
struct nfc_evt_transaction *evt_transaction);
+int nfc_se_connectivity(struct nfc_dev *dev, u8 se_idx);
int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx);
diff --git a/include/net/protocol.h b/include/net/protocol.h
index d6fcc1fcdb5b..da689f5432de 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -107,7 +107,7 @@ int inet_del_offload(const struct net_offload *prot, unsigned char num);
void inet_register_protosw(struct inet_protosw *p);
void inet_unregister_protosw(struct inet_protosw *p);
-int udp_add_offload(struct udp_offload *prot);
+int udp_add_offload(struct net *net, struct udp_offload *prot);
void udp_del_offload(struct udp_offload *prot);
#if IS_ENABLED(CONFIG_IPV6)
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index a0dde04eb178..f49759decb28 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -68,7 +68,7 @@ struct request_sock {
u32 peer_secid;
};
-static inline struct request_sock *inet_reqsk(struct sock *sk)
+static inline struct request_sock *inet_reqsk(const struct sock *sk)
{
return (struct request_sock *)sk;
}
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index b2a8e6338576..636a362a0e03 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -407,6 +407,15 @@ bool tcf_destroy(struct tcf_proto *tp, bool force);
void tcf_destroy_chain(struct tcf_proto __rcu **fl);
int skb_do_redirect(struct sk_buff *);
+static inline bool skb_at_tc_ingress(const struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ return G_TC_AT(skb->tc_verd) & AT_INGRESS;
+#else
+ return false;
+#endif
+}
+
/* Reset all TX qdiscs greater then index of a device. */
static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
{
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index ce13cf20f625..835aa2ed9870 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -126,8 +126,6 @@ int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg);
*/
int sctp_rcv(struct sk_buff *skb);
void sctp_v4_err(struct sk_buff *skb, u32 info);
-void sctp_hash_established(struct sctp_association *);
-void sctp_unhash_established(struct sctp_association *);
void sctp_hash_endpoint(struct sctp_endpoint *);
void sctp_unhash_endpoint(struct sctp_endpoint *);
struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
@@ -143,6 +141,17 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
struct sctp_transport *t);
void sctp_backlog_migrate(struct sctp_association *assoc,
struct sock *oldsk, struct sock *newsk);
+int sctp_transport_hashtable_init(void);
+void sctp_transport_hashtable_destroy(void);
+void sctp_hash_transport(struct sctp_transport *t);
+void sctp_unhash_transport(struct sctp_transport *t);
+struct sctp_transport *sctp_addrs_lookup_transport(
+ struct net *net,
+ const union sctp_addr *laddr,
+ const union sctp_addr *paddr);
+struct sctp_transport *sctp_epaddr_lookup_transport(
+ const struct sctp_endpoint *ep,
+ const union sctp_addr *paddr);
/*
* sctp/proc.c
@@ -519,25 +528,6 @@ static inline int sctp_ep_hashfn(struct net *net, __u16 lport)
return (net_hash_mix(net) + lport) & (sctp_ep_hashsize - 1);
}
-/* This is the hash function for the association hash table. */
-static inline int sctp_assoc_hashfn(struct net *net, __u16 lport, __u16 rport)
-{
- int h = (lport << 16) + rport + net_hash_mix(net);
- h ^= h>>8;
- return h & (sctp_assoc_hashsize - 1);
-}
-
-/* This is the hash function for the association hash table. This is
- * not used yet, but could be used as a better hash function when
- * we have a vtag.
- */
-static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag)
-{
- int h = (lport << 16) + rport;
- h ^= vtag;
- return h & (sctp_assoc_hashsize - 1);
-}
-
#define sctp_for_each_hentry(epb, head) \
hlist_for_each_entry(epb, head, node)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index eea9bdeecba2..20e72129be1c 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -48,6 +48,7 @@
#define __sctp_structs_h__
#include <linux/ktime.h>
+#include <linux/rhashtable.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 */
@@ -119,14 +120,13 @@ extern struct sctp_globals {
/* This is the hash of all endpoints. */
struct sctp_hashbucket *ep_hashtable;
- /* This is the hash of all associations. */
- struct sctp_hashbucket *assoc_hashtable;
/* This is the sctp port control hash. */
struct sctp_bind_hashbucket *port_hashtable;
+ /* This is the hash of all transports. */
+ struct rhashtable transport_hashtable;
/* Sizes of above hashtables. */
int ep_hashsize;
- int assoc_hashsize;
int port_hashsize;
/* Default initialization values to be applied to new associations. */
@@ -143,10 +143,9 @@ extern struct sctp_globals {
#define sctp_address_families (sctp_globals.address_families)
#define sctp_ep_hashsize (sctp_globals.ep_hashsize)
#define sctp_ep_hashtable (sctp_globals.ep_hashtable)
-#define sctp_assoc_hashsize (sctp_globals.assoc_hashsize)
-#define sctp_assoc_hashtable (sctp_globals.assoc_hashtable)
#define sctp_port_hashsize (sctp_globals.port_hashsize)
#define sctp_port_hashtable (sctp_globals.port_hashtable)
+#define sctp_transport_hashtable (sctp_globals.transport_hashtable)
#define sctp_checksum_disable (sctp_globals.checksum_disable)
/* SCTP Socket type: UDP or TCP style. */
@@ -753,6 +752,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet)
struct sctp_transport {
/* A list of transports. */
struct list_head transports;
+ struct rhash_head node;
/* Reference counting. */
atomic_t refcnt;
diff --git a/include/net/sock.h b/include/net/sock.h
index 14d3c0734007..e830c1006935 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -58,6 +58,8 @@
#include <linux/memcontrol.h>
#include <linux/static_key.h>
#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/cgroup-defs.h>
#include <linux/filter.h>
#include <linux/rculist_nulls.h>
@@ -287,7 +289,6 @@ struct cg_proto;
* @sk_ack_backlog: current listen backlog
* @sk_max_ack_backlog: listen backlog set in listen()
* @sk_priority: %SO_PRIORITY setting
- * @sk_cgrp_prioidx: socket group's priority map index
* @sk_type: socket type (%SOCK_STREAM, etc)
* @sk_protocol: which protocol this socket belongs in this network family
* @sk_peer_pid: &struct pid for this socket's peer
@@ -308,7 +309,7 @@ struct cg_proto;
* @sk_send_head: front of stuff to transmit
* @sk_security: used by security modules
* @sk_mark: generic packet mark
- * @sk_classid: this socket's cgroup classid
+ * @sk_cgrp_data: cgroup data for this cgroup
* @sk_cgrp: this socket's cgroup-specific proto data
* @sk_write_pending: a write to stream socket waits to start
* @sk_state_change: callback to indicate change in the state of the sock
@@ -317,6 +318,7 @@ struct cg_proto;
* @sk_error_report: callback to indicate errors (e.g. %MSG_ERRQUEUE)
* @sk_backlog_rcv: callback to process the backlog
* @sk_destruct: called at sock freeing time, i.e. when all refcnt == 0
+ * @sk_reuseport_cb: reuseport group container
*/
struct sock {
/*
@@ -425,9 +427,7 @@ struct sock {
u32 sk_ack_backlog;
u32 sk_max_ack_backlog;
__u32 sk_priority;
-#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
- __u32 sk_cgrp_prioidx;
-#endif
+ __u32 sk_mark;
struct pid *sk_peer_pid;
const struct cred *sk_peer_cred;
long sk_rcvtimeo;
@@ -445,10 +445,7 @@ struct sock {
#ifdef CONFIG_SECURITY
void *sk_security;
#endif
- __u32 sk_mark;
-#ifdef CONFIG_CGROUP_NET_CLASSID
- u32 sk_classid;
-#endif
+ struct sock_cgroup_data sk_cgrp_data;
struct cg_proto *sk_cgrp;
void (*sk_state_change)(struct sock *sk);
void (*sk_data_ready)(struct sock *sk);
@@ -457,6 +454,7 @@ struct sock {
int (*sk_backlog_rcv)(struct sock *sk,
struct sk_buff *skb);
void (*sk_destruct)(struct sock *sk);
+ struct sock_reuseport __rcu *sk_reuseport_cb;
};
#define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data)))
@@ -778,9 +776,9 @@ static inline int sk_memalloc_socks(void)
#endif
-static inline gfp_t sk_gfp_atomic(const struct sock *sk, gfp_t gfp_mask)
+static inline gfp_t sk_gfp_mask(const struct sock *sk, gfp_t gfp_mask)
{
- return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC);
+ return gfp_mask | (sk->sk_allocation & __GFP_MEMALLOC);
}
static inline void sk_acceptq_removed(struct sock *sk)
@@ -1067,6 +1065,7 @@ struct proto {
void (*destroy_cgroup)(struct mem_cgroup *memcg);
struct cg_proto *(*proto_cgroup)(struct mem_cgroup *memcg);
#endif
+ int (*diag_destroy)(struct sock *sk, int err);
};
int proto_register(struct proto *prot, int alloc_slab);
@@ -1798,6 +1797,15 @@ static inline void sk_nocaps_add(struct sock *sk, netdev_features_t flags)
sk->sk_route_caps &= ~flags;
}
+static inline bool sk_check_csum_caps(struct sock *sk)
+{
+ return (sk->sk_route_caps & NETIF_F_HW_CSUM) ||
+ (sk->sk_family == PF_INET &&
+ (sk->sk_route_caps & NETIF_F_IP_CSUM)) ||
+ (sk->sk_family == PF_INET6 &&
+ (sk->sk_route_caps & NETIF_F_IPV6_CSUM));
+}
+
static inline int skb_do_copy_data_nocache(struct sock *sk, struct sk_buff *skb,
struct iov_iter *from, char *to,
int copy, int offset)
@@ -1883,12 +1891,12 @@ static inline bool sk_has_allocations(const struct sock *sk)
}
/**
- * wq_has_sleeper - check if there are any waiting processes
+ * skwq_has_sleeper - check if there are any waiting processes
* @wq: struct socket_wq
*
* Returns true if socket_wq has waiting processes
*
- * The purpose of the wq_has_sleeper and sock_poll_wait is to wrap the memory
+ * The purpose of the skwq_has_sleeper and sock_poll_wait is to wrap the memory
* barrier call. They were added due to the race found within the tcp code.
*
* Consider following tcp code paths:
@@ -1914,15 +1922,9 @@ static inline bool sk_has_allocations(const struct sock *sk)
* data on the socket.
*
*/
-static inline bool wq_has_sleeper(struct socket_wq *wq)
+static inline bool skwq_has_sleeper(struct socket_wq *wq)
{
- /* We need to be sure we are in sync with the
- * add_wait_queue modifications to the wait queue.
- *
- * This memory barrier is paired in the sock_poll_wait.
- */
- smp_mb();
- return wq && waitqueue_active(&wq->wait);
+ return wq && wq_has_sleeper(&wq->wait);
}
/**
diff --git a/include/net/sock_reuseport.h b/include/net/sock_reuseport.h
new file mode 100644
index 000000000000..7dda3d7adba8
--- /dev/null
+++ b/include/net/sock_reuseport.h
@@ -0,0 +1,28 @@
+#ifndef _SOCK_REUSEPORT_H
+#define _SOCK_REUSEPORT_H
+
+#include <linux/filter.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+struct sock_reuseport {
+ struct rcu_head rcu;
+
+ u16 max_socks; /* length of socks */
+ u16 num_socks; /* elements in socks */
+ struct bpf_prog __rcu *prog; /* optional BPF sock selector */
+ struct sock *socks[0]; /* array of sock pointers */
+};
+
+extern int reuseport_alloc(struct sock *sk);
+extern int reuseport_add_sock(struct sock *sk, const struct sock *sk2);
+extern void reuseport_detach_sock(struct sock *sk);
+extern struct sock *reuseport_select_sock(struct sock *sk,
+ u32 hash,
+ struct sk_buff *skb,
+ int hdr_len);
+extern struct bpf_prog *reuseport_attach_prog(struct sock *sk,
+ struct bpf_prog *prog);
+
+#endif /* _SOCK_REUSEPORT_H */
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 1d22ce9f352e..d451122e8404 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -47,9 +47,11 @@ enum switchdev_attr_id {
SWITCHDEV_ATTR_ID_PORT_STP_STATE,
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
+ SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
};
struct switchdev_attr {
+ struct net_device *orig_dev;
enum switchdev_attr_id id;
u32 flags;
union {
@@ -57,6 +59,7 @@ struct switchdev_attr {
u8 stp_state; /* PORT_STP_STATE */
unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */
u32 ageing_time; /* BRIDGE_AGEING_TIME */
+ bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */
} u;
};
@@ -65,9 +68,11 @@ enum switchdev_obj_id {
SWITCHDEV_OBJ_ID_PORT_VLAN,
SWITCHDEV_OBJ_ID_IPV4_FIB,
SWITCHDEV_OBJ_ID_PORT_FDB,
+ SWITCHDEV_OBJ_ID_PORT_MDB,
};
struct switchdev_obj {
+ struct net_device *orig_dev;
enum switchdev_obj_id id;
u32 flags;
};
@@ -109,6 +114,16 @@ struct switchdev_obj_port_fdb {
#define SWITCHDEV_OBJ_PORT_FDB(obj) \
container_of(obj, struct switchdev_obj_port_fdb, obj)
+/* SWITCHDEV_OBJ_ID_PORT_MDB */
+struct switchdev_obj_port_mdb {
+ struct switchdev_obj obj;
+ unsigned char addr[ETH_ALEN];
+ u16 vid;
+};
+
+#define SWITCHDEV_OBJ_PORT_MDB(obj) \
+ container_of(obj, struct switchdev_obj_port_mdb, obj)
+
void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
void *data, void (*destructor)(void const *),
struct switchdev_trans_item *tritem);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index f80e74c5ad18..a80255f4ca33 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -240,9 +240,6 @@ extern int sysctl_tcp_timestamps;
extern int sysctl_tcp_window_scaling;
extern int sysctl_tcp_sack;
extern int sysctl_tcp_fin_timeout;
-extern int sysctl_tcp_keepalive_time;
-extern int sysctl_tcp_keepalive_probes;
-extern int sysctl_tcp_keepalive_intvl;
extern int sysctl_tcp_syn_retries;
extern int sysctl_tcp_synack_retries;
extern int sysctl_tcp_retries1;
@@ -1170,6 +1167,8 @@ void tcp_set_state(struct sock *sk, int state);
void tcp_done(struct sock *sk);
+int tcp_abort(struct sock *sk, int err);
+
static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
{
rx_opt->dsack = 0;
@@ -1223,17 +1222,23 @@ void tcp_enter_memory_pressure(struct sock *sk);
static inline int keepalive_intvl_when(const struct tcp_sock *tp)
{
- return tp->keepalive_intvl ? : sysctl_tcp_keepalive_intvl;
+ struct net *net = sock_net((struct sock *)tp);
+
+ return tp->keepalive_intvl ? : net->ipv4.sysctl_tcp_keepalive_intvl;
}
static inline int keepalive_time_when(const struct tcp_sock *tp)
{
- return tp->keepalive_time ? : sysctl_tcp_keepalive_time;
+ struct net *net = sock_net((struct sock *)tp);
+
+ return tp->keepalive_time ? : net->ipv4.sysctl_tcp_keepalive_time;
}
static inline int keepalive_probes(const struct tcp_sock *tp)
{
- return tp->keepalive_probes ? : sysctl_tcp_keepalive_probes;
+ struct net *net = sock_net((struct sock *)tp);
+
+ return tp->keepalive_probes ? : net->ipv4.sysctl_tcp_keepalive_probes;
}
static inline u32 keepalive_time_elapsed(const struct tcp_sock *tp)
@@ -1618,6 +1623,18 @@ static inline void tcp_highest_sack_combine(struct sock *sk,
tcp_sk(sk)->highest_sack = new;
}
+/* This helper checks if socket has IP_TRANSPARENT set */
+static inline bool inet_sk_transparent(const struct sock *sk)
+{
+ switch (sk->sk_state) {
+ case TCP_TIME_WAIT:
+ return inet_twsk(sk)->tw_transparent;
+ case TCP_NEW_SYN_RECV:
+ return inet_rsk(inet_reqsk(sk))->no_srccheck;
+ }
+ return inet_sk(sk)->transparent;
+}
+
/* Determines whether this is a thin stream (which may suffer from
* increased latency). Used to trigger latency-reducing mechanisms.
*/
diff --git a/include/net/udp.h b/include/net/udp.h
index 6d4ed18e1427..2842541e28e7 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -191,7 +191,7 @@ static inline void udp_lib_close(struct sock *sk, long timeout)
}
int udp_lib_get_port(struct sock *sk, unsigned short snum,
- int (*)(const struct sock *, const struct sock *),
+ int (*)(const struct sock *, const struct sock *, bool),
unsigned int hash2_nulladdr);
u32 udp_flow_hashrnd(void);
@@ -258,7 +258,7 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif);
struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif,
- struct udp_table *tbl);
+ struct udp_table *tbl, struct sk_buff *skb);
struct sock *udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
@@ -266,7 +266,8 @@ struct sock *udp6_lib_lookup(struct net *net,
struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
- int dif, struct udp_table *tbl);
+ int dif, struct udp_table *tbl,
+ struct sk_buff *skb);
/*
* SNMP statistics for UDP and UDP-Lite
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index cb2f89f20f5c..cca2ad3082c3 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -78,10 +78,10 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
struct udp_tunnel_sock_cfg *sock_cfg);
/* Transmit the skb using UDP encapsulation. */
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
- __be32 src, __be32 dst, __u8 tos, __u8 ttl,
- __be16 df, __be16 src_port, __be16 dst_port,
- bool xnet, bool nocheck);
+void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
+ __be32 src, __be32 dst, __u8 tos, __u8 ttl,
+ __be16 df, __be16 src_port, __be16 dst_port,
+ bool xnet, bool nocheck);
#if IS_ENABLED(CONFIG_IPV6)
int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index e289ada6adf6..0fb86442544b 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -232,7 +232,7 @@ static inline netdev_features_t vxlan_features_check(struct sk_buff *skb,
skb->inner_protocol != htons(ETH_P_TEB) ||
(skb_inner_mac_header(skb) - skb_transport_header(skb) !=
sizeof(struct udphdr) + sizeof(struct vxlanhdr))))
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
return features;
}
diff --git a/include/trace/events/fib6.h b/include/trace/events/fib6.h
new file mode 100644
index 000000000000..4cf6bac4686d
--- /dev/null
+++ b/include/trace/events/fib6.h
@@ -0,0 +1,76 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fib6
+
+#if !defined(_TRACE_FIB6_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FIB6_H
+
+#include <linux/in6.h>
+#include <net/flow.h>
+#include <net/ip6_fib.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fib6_table_lookup,
+
+ TP_PROTO(const struct net *net, const struct rt6_info *rt,
+ u32 tb_id, const struct flowi6 *flp),
+
+ TP_ARGS(net, rt, tb_id, flp),
+
+ TP_STRUCT__entry(
+ __field( u32, tb_id )
+
+ __field( int, oif )
+ __field( int, iif )
+ __field( __u8, tos )
+ __field( __u8, scope )
+ __field( __u8, flags )
+ __array( __u8, src, 16 )
+ __array( __u8, dst, 16 )
+
+ __dynamic_array( char, name, IFNAMSIZ )
+ __array( __u8, gw, 16 )
+ ),
+
+ TP_fast_assign(
+ struct in6_addr *in6;
+
+ __entry->tb_id = tb_id;
+ __entry->oif = flp->flowi6_oif;
+ __entry->iif = flp->flowi6_iif;
+ __entry->tos = flp->flowi6_tos;
+ __entry->scope = flp->flowi6_scope;
+ __entry->flags = flp->flowi6_flags;
+
+ in6 = (struct in6_addr *)__entry->src;
+ *in6 = flp->saddr;
+
+ in6 = (struct in6_addr *)__entry->dst;
+ *in6 = flp->daddr;
+
+ if (rt->rt6i_idev) {
+ __assign_str(name, rt->rt6i_idev->dev->name);
+ } else {
+ __assign_str(name, "");
+ }
+ if (rt == net->ipv6.ip6_null_entry) {
+ struct in6_addr in6_zero = {};
+
+ in6 = (struct in6_addr *)__entry->gw;
+ *in6 = in6_zero;
+
+ } else if (rt) {
+ in6 = (struct in6_addr *)__entry->gw;
+ *in6 = rt->rt6i_gateway;
+ }
+ ),
+
+ TP_printk("table %3u oif %d iif %d src %pI6c dst %pI6c tos %d scope %d flags %x ==> dev %s gw %pI6c",
+ __entry->tb_id, __entry->oif, __entry->iif,
+ __entry->src, __entry->dst, __entry->tos, __entry->scope,
+ __entry->flags, __get_str(name), __entry->gw)
+);
+
+#endif /* _TRACE_FIB6_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index 5c15c2a5c123..fb8a41668382 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -87,4 +87,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* __ASM_GENERIC_SOCKET_H */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 9ea2d22fa2cb..aa6f8571de13 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -269,9 +269,29 @@ enum bpf_func_id {
* Return: 0 on success
*/
BPF_FUNC_perf_event_output,
+ BPF_FUNC_skb_load_bytes,
__BPF_FUNC_MAX_ID,
};
+/* All flags used by eBPF helper functions, placed here. */
+
+/* BPF_FUNC_skb_store_bytes flags. */
+#define BPF_F_RECOMPUTE_CSUM (1ULL << 0)
+
+/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags.
+ * First 4 bits are for passing the header field size.
+ */
+#define BPF_F_HDR_FIELD_MASK 0xfULL
+
+/* BPF_FUNC_l4_csum_replace flags. */
+#define BPF_F_PSEUDO_HDR (1ULL << 4)
+
+/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
+#define BPF_F_INGRESS (1ULL << 0)
+
+/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
+#define BPF_F_TUNINFO_IPV6 (1ULL << 0)
+
/* user accessible mirror of in-kernel sk_buff.
* new fields can only be added to the end of this structure
*/
@@ -295,7 +315,12 @@ struct __sk_buff {
struct bpf_tunnel_key {
__u32 tunnel_id;
- __u32 remote_ipv4;
+ union {
+ __u32 remote_ipv4;
+ __u32 remote_ipv6[4];
+ };
+ __u8 tunnel_tos;
+ __u8 tunnel_ttl;
};
#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index cd1629170103..57fa39005e79 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -542,6 +542,7 @@ struct ethtool_pauseparam {
* now deprecated
* @ETH_SS_FEATURES: Device feature names
* @ETH_SS_RSS_HASH_FUNCS: RSS hush function names
+ * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS
*/
enum ethtool_stringset {
ETH_SS_TEST = 0,
@@ -551,6 +552,7 @@ enum ethtool_stringset {
ETH_SS_FEATURES,
ETH_SS_RSS_HASH_FUNCS,
ETH_SS_TUNABLES,
+ ETH_SS_PHY_STATS,
};
/**
@@ -1225,6 +1227,7 @@ enum ethtool_sfeatures_retval_bits {
#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */
#define ETHTOOL_GTUNABLE 0x00000048 /* Get tunable configuration */
#define ETHTOOL_STUNABLE 0x00000049 /* Set tunable configuration */
+#define ETHTOOL_GPHYSTATS 0x0000004a /* get PHY-specific statistics */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 5ad57375a99f..a30b78090594 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -218,6 +218,7 @@ enum in6_addr_gen_mode {
IN6_ADDR_GEN_MODE_EUI64,
IN6_ADDR_GEN_MODE_NONE,
IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+ IN6_ADDR_GEN_MODE_RANDOM,
};
/* Bridge section */
@@ -462,6 +463,9 @@ enum {
IFLA_GENEVE_PORT, /* destination port */
IFLA_GENEVE_COLLECT_METADATA,
IFLA_GENEVE_REMOTE6,
+ IFLA_GENEVE_UDP_CSUM,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
__IFLA_GENEVE_MAX
};
#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h
index 7ed9e670814e..abde7bbd6f3b 100644
--- a/include/uapi/linux/ila.h
+++ b/include/uapi/linux/ila.h
@@ -3,13 +3,35 @@
#ifndef _UAPI_LINUX_ILA_H
#define _UAPI_LINUX_ILA_H
+/* NETLINK_GENERIC related info */
+#define ILA_GENL_NAME "ila"
+#define ILA_GENL_VERSION 0x1
+
enum {
ILA_ATTR_UNSPEC,
ILA_ATTR_LOCATOR, /* u64 */
+ ILA_ATTR_IDENTIFIER, /* u64 */
+ ILA_ATTR_LOCATOR_MATCH, /* u64 */
+ ILA_ATTR_IFINDEX, /* s32 */
+ ILA_ATTR_DIR, /* u32 */
__ILA_ATTR_MAX,
};
#define ILA_ATTR_MAX (__ILA_ATTR_MAX - 1)
+enum {
+ ILA_CMD_UNSPEC,
+ ILA_CMD_ADD,
+ ILA_CMD_DEL,
+ ILA_CMD_GET,
+
+ __ILA_CMD_MAX,
+};
+
+#define ILA_CMD_MAX (__ILA_CMD_MAX - 1)
+
+#define ILA_DIR_IN (1 << 0)
+#define ILA_DIR_OUT (1 << 1)
+
#endif /* _UAPI_LINUX_ILA_H */
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 79b12b004ade..318a4828bf98 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -196,6 +196,7 @@ struct in6_flowlabel_req {
#define IPV6_IPSEC_POLICY 34
#define IPV6_XFRM_POLICY 35
+#define IPV6_HDRINCL 36
#endif
/*
diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
index a382d2c04a42..cf943016930f 100644
--- a/include/uapi/linux/mroute.h
+++ b/include/uapi/linux/mroute.h
@@ -4,15 +4,13 @@
#include <linux/sockios.h>
#include <linux/types.h>
-/*
- * Based on the MROUTING 3.5 defines primarily to keep
- * source compatibility with BSD.
+/* Based on the MROUTING 3.5 defines primarily to keep
+ * source compatibility with BSD.
*
- * See the mrouted code for the original history.
- *
- * Protocol Independent Multicast (PIM) data structures included
- * Carlos Picoto (cap@di.fc.ul.pt)
+ * See the mrouted code for the original history.
*
+ * Protocol Independent Multicast (PIM) data structures included
+ * Carlos Picoto (cap@di.fc.ul.pt)
*/
#define MRT_BASE 200
@@ -34,15 +32,13 @@
#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
#define SIOCGETRPF (SIOCPROTOPRIVATE+2)
-#define MAXVIFS 32
+#define MAXVIFS 32
typedef unsigned long vifbitmap_t; /* User mode code depends on this lot */
typedef unsigned short vifi_t;
#define ALL_VIFS ((vifi_t)(-1))
-/*
- * Same idea as select
- */
-
+/* Same idea as select */
+
#define VIFM_SET(n,m) ((m)|=(1<<(n)))
#define VIFM_CLR(n,m) ((m)&=~(1<<(n)))
#define VIFM_ISSET(n,m) ((m)&(1<<(n)))
@@ -50,11 +46,9 @@ typedef unsigned short vifi_t;
#define VIFM_COPY(mfrom,mto) ((mto)=(mfrom))
#define VIFM_SAME(m1,m2) ((m1)==(m2))
-/*
- * Passed by mrouted for an MRT_ADD_VIF - again we use the
- * mrouted 3.6 structures for compatibility
+/* Passed by mrouted for an MRT_ADD_VIF - again we use the
+ * mrouted 3.6 structures for compatibility
*/
-
struct vifctl {
vifi_t vifc_vifi; /* Index of VIF */
unsigned char vifc_flags; /* VIFF_ flags */
@@ -73,10 +67,7 @@ struct vifctl {
#define VIFF_USE_IFINDEX 0x8 /* use vifc_lcl_ifindex instead of
vifc_lcl_addr to find an interface */
-/*
- * Cache manipulation structures for mrouted and PIMd
- */
-
+/* Cache manipulation structures for mrouted and PIMd */
struct mfcctl {
struct in_addr mfcc_origin; /* Origin of mcast */
struct in_addr mfcc_mcastgrp; /* Group in question */
@@ -88,10 +79,7 @@ struct mfcctl {
int mfcc_expire;
};
-/*
- * Group count retrieval for mrouted
- */
-
+/* Group count retrieval for mrouted */
struct sioc_sg_req {
struct in_addr src;
struct in_addr grp;
@@ -100,10 +88,7 @@ struct sioc_sg_req {
unsigned long wrong_if;
};
-/*
- * To get vif packet counts
- */
-
+/* To get vif packet counts */
struct sioc_vif_req {
vifi_t vifi; /* Which iface */
unsigned long icount; /* In packets */
@@ -112,11 +97,9 @@ struct sioc_vif_req {
unsigned long obytes; /* Out bytes */
};
-/*
- * This is the format the mroute daemon expects to see IGMP control
- * data. Magically happens to be like an IP packet as per the original
+/* This is the format the mroute daemon expects to see IGMP control
+ * data. Magically happens to be like an IP packet as per the original
*/
-
struct igmpmsg {
__u32 unused1,unused2;
unsigned char im_msgtype; /* What is this */
@@ -126,21 +109,13 @@ struct igmpmsg {
struct in_addr im_src,im_dst;
};
-/*
- * That's all usermode folks
- */
-
-
+/* That's all usermode folks */
#define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */
-/*
- * Pseudo messages used by mrouted
- */
-
+/* Pseudo messages used by mrouted */
#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */
#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */
#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */
-
#endif /* _UAPI__LINUX_MROUTE_H */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h b/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h
index 6a2c038d1888..fd5024d26269 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h
@@ -1,6 +1,8 @@
#ifndef _UAPI__IP_SET_BITMAP_H
#define _UAPI__IP_SET_BITMAP_H
+#include <linux/netfilter/ipset/ip_set.h>
+
/* Bitmap type specific error codes */
enum {
/* The element is out of the range of the set */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set_hash.h b/include/uapi/linux/netfilter/ipset/ip_set_hash.h
index 352eeccdc7f2..82deeb883ac4 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set_hash.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set_hash.h
@@ -1,6 +1,8 @@
#ifndef _UAPI__IP_SET_HASH_H
#define _UAPI__IP_SET_HASH_H
+#include <linux/netfilter/ipset/ip_set.h>
+
/* Hash type specific error codes */
enum {
/* Hash is full */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set_list.h b/include/uapi/linux/netfilter/ipset/ip_set_list.h
index a44efaa98213..84d430368266 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set_list.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set_list.h
@@ -1,6 +1,8 @@
#ifndef _UAPI__IP_SET_LIST_H
#define _UAPI__IP_SET_LIST_H
+#include <linux/netfilter/ipset/ip_set.h>
+
/* List type specific error codes */
enum {
/* Set name to be added/deleted/tested does not exist. */
diff --git a/include/uapi/linux/netfilter/nf_conntrack_sctp.h b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
index ed4e776e1242..2cbc366c3fb4 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_sctp.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
@@ -1,5 +1,5 @@
-#ifndef _NF_CONNTRACK_SCTP_H
-#define _NF_CONNTRACK_SCTP_H
+#ifndef _UAPI_NF_CONNTRACK_SCTP_H
+#define _UAPI_NF_CONNTRACK_SCTP_H
/* SCTP tracking. */
#include <linux/netfilter/nf_conntrack_tuple_common.h>
@@ -18,10 +18,4 @@ enum sctp_conntrack {
SCTP_CONNTRACK_MAX
};
-struct ip_ct_sctp {
- enum sctp_conntrack state;
-
- __be32 vtag[IP_CT_DIR_MAX];
-};
-
-#endif /* _NF_CONNTRACK_SCTP_H */
+#endif /* _UAPI_NF_CONNTRACK_SCTP_H */
diff --git a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
index 2f6bbc5b8125..a9c3834abdd4 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
@@ -1,6 +1,9 @@
#ifndef _NF_CONNTRACK_TUPLE_COMMON_H
#define _NF_CONNTRACK_TUPLE_COMMON_H
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
enum ip_conntrack_dir {
IP_CT_DIR_ORIGINAL,
IP_CT_DIR_REPLY,
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index d8c8a7c9d88a..be41ffc128b8 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -83,6 +83,7 @@ enum nft_verdicts {
* @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes)
* @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes)
* @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes)
+ * @NFT_MSG_TRACE: trace event (enum nft_trace_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -102,6 +103,7 @@ enum nf_tables_msg_types {
NFT_MSG_DELSETELEM,
NFT_MSG_NEWGEN,
NFT_MSG_GETGEN,
+ NFT_MSG_TRACE,
NFT_MSG_MAX,
};
@@ -289,6 +291,7 @@ enum nft_set_desc_attributes {
* @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
* @NFTA_SET_TIMEOUT: default timeout value (NLA_U64)
* @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32)
+ * @NFTA_SET_USERDATA: user data (NLA_BINARY)
*/
enum nft_set_attributes {
NFTA_SET_UNSPEC,
@@ -304,6 +307,7 @@ enum nft_set_attributes {
NFTA_SET_ID,
NFTA_SET_TIMEOUT,
NFTA_SET_GC_INTERVAL,
+ NFTA_SET_USERDATA,
__NFTA_SET_MAX
};
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
@@ -598,12 +602,26 @@ enum nft_payload_bases {
};
/**
+ * enum nft_payload_csum_types - nf_tables payload expression checksum types
+ *
+ * @NFT_PAYLOAD_CSUM_NONE: no checksumming
+ * @NFT_PAYLOAD_CSUM_INET: internet checksum (RFC 791)
+ */
+enum nft_payload_csum_types {
+ NFT_PAYLOAD_CSUM_NONE,
+ NFT_PAYLOAD_CSUM_INET,
+};
+
+/**
* enum nft_payload_attributes - nf_tables payload expression netlink attributes
*
* @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers)
* @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases)
* @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32)
* @NFTA_PAYLOAD_LEN: payload length (NLA_U32)
+ * @NFTA_PAYLOAD_SREG: source register to load data from (NLA_U32: nft_registers)
+ * @NFTA_PAYLOAD_CSUM_TYPE: checksum type (NLA_U32)
+ * @NFTA_PAYLOAD_CSUM_OFFSET: checksum offset relative to base (NLA_U32)
*/
enum nft_payload_attributes {
NFTA_PAYLOAD_UNSPEC,
@@ -611,6 +629,9 @@ enum nft_payload_attributes {
NFTA_PAYLOAD_BASE,
NFTA_PAYLOAD_OFFSET,
NFTA_PAYLOAD_LEN,
+ NFTA_PAYLOAD_SREG,
+ NFTA_PAYLOAD_CSUM_TYPE,
+ NFTA_PAYLOAD_CSUM_OFFSET,
__NFTA_PAYLOAD_MAX
};
#define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1)
@@ -736,6 +757,8 @@ enum nft_ct_keys {
NFT_CT_PROTO_SRC,
NFT_CT_PROTO_DST,
NFT_CT_LABELS,
+ NFT_CT_PKTS,
+ NFT_CT_BYTES,
};
/**
@@ -761,6 +784,10 @@ enum nft_limit_type {
NFT_LIMIT_PKT_BYTES
};
+enum nft_limit_flags {
+ NFT_LIMIT_F_INV = (1 << 0),
+};
+
/**
* enum nft_limit_attributes - nf_tables limit expression netlink attributes
*
@@ -768,6 +795,7 @@ enum nft_limit_type {
* @NFTA_LIMIT_UNIT: refill unit (NLA_U64)
* @NFTA_LIMIT_BURST: burst (NLA_U32)
* @NFTA_LIMIT_TYPE: type of limit (NLA_U32: enum nft_limit_type)
+ * @NFTA_LIMIT_FLAGS: flags (NLA_U32: enum nft_limit_flags)
*/
enum nft_limit_attributes {
NFTA_LIMIT_UNSPEC,
@@ -775,6 +803,7 @@ enum nft_limit_attributes {
NFTA_LIMIT_UNIT,
NFTA_LIMIT_BURST,
NFTA_LIMIT_TYPE,
+ NFTA_LIMIT_FLAGS,
__NFTA_LIMIT_MAX
};
#define NFTA_LIMIT_MAX (__NFTA_LIMIT_MAX - 1)
@@ -959,6 +988,18 @@ enum nft_dup_attributes {
#define NFTA_DUP_MAX (__NFTA_DUP_MAX - 1)
/**
+ * enum nft_fwd_attributes - nf_tables fwd expression netlink attributes
+ *
+ * @NFTA_FWD_SREG_DEV: source register of output interface (NLA_U32: nft_register)
+ */
+enum nft_fwd_attributes {
+ NFTA_FWD_UNSPEC,
+ NFTA_FWD_SREG_DEV,
+ __NFTA_FWD_MAX
+};
+#define NFTA_FWD_MAX (__NFTA_FWD_MAX - 1)
+
+/**
* enum nft_gen_attributes - nf_tables ruleset generation attributes
*
* @NFTA_GEN_ID: Ruleset generation ID (NLA_U32)
@@ -970,4 +1011,54 @@ enum nft_gen_attributes {
};
#define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1)
+/**
+ * enum nft_trace_attributes - nf_tables trace netlink attributes
+ *
+ * @NFTA_TRACE_TABLE: name of the table (NLA_STRING)
+ * @NFTA_TRACE_CHAIN: name of the chain (NLA_STRING)
+ * @NFTA_TRACE_RULE_HANDLE: numeric handle of the rule (NLA_U64)
+ * @NFTA_TRACE_TYPE: type of the event (NLA_U32: nft_trace_types)
+ * @NFTA_TRACE_VERDICT: verdict returned by hook (NLA_NESTED: nft_verdicts)
+ * @NFTA_TRACE_ID: pseudo-id, same for each skb traced (NLA_U32)
+ * @NFTA_TRACE_LL_HEADER: linklayer header (NLA_BINARY)
+ * @NFTA_TRACE_NETWORK_HEADER: network header (NLA_BINARY)
+ * @NFTA_TRACE_TRANSPORT_HEADER: transport header (NLA_BINARY)
+ * @NFTA_TRACE_IIF: indev ifindex (NLA_U32)
+ * @NFTA_TRACE_IIFTYPE: netdev->type of indev (NLA_U16)
+ * @NFTA_TRACE_OIF: outdev ifindex (NLA_U32)
+ * @NFTA_TRACE_OIFTYPE: netdev->type of outdev (NLA_U16)
+ * @NFTA_TRACE_MARK: nfmark (NLA_U32)
+ * @NFTA_TRACE_NFPROTO: nf protocol processed (NLA_U32)
+ * @NFTA_TRACE_POLICY: policy that decided fate of packet (NLA_U32)
+ */
+enum nft_trace_attibutes {
+ NFTA_TRACE_UNSPEC,
+ NFTA_TRACE_TABLE,
+ NFTA_TRACE_CHAIN,
+ NFTA_TRACE_RULE_HANDLE,
+ NFTA_TRACE_TYPE,
+ NFTA_TRACE_VERDICT,
+ NFTA_TRACE_ID,
+ NFTA_TRACE_LL_HEADER,
+ NFTA_TRACE_NETWORK_HEADER,
+ NFTA_TRACE_TRANSPORT_HEADER,
+ NFTA_TRACE_IIF,
+ NFTA_TRACE_IIFTYPE,
+ NFTA_TRACE_OIF,
+ NFTA_TRACE_OIFTYPE,
+ NFTA_TRACE_MARK,
+ NFTA_TRACE_NFPROTO,
+ NFTA_TRACE_POLICY,
+ __NFTA_TRACE_MAX
+};
+#define NFTA_TRACE_MAX (__NFTA_TRACE_MAX - 1)
+
+enum nft_trace_types {
+ NFT_TRACETYPE_UNSPEC,
+ NFT_TRACETYPE_POLICY,
+ NFT_TRACETYPE_RETURN,
+ NFT_TRACETYPE_RULE,
+ __NFT_TRACETYPE_MAX
+};
+#define NFT_TRACETYPE_MAX (__NFT_TRACETYPE_MAX - 1)
#endif /* _LINUX_NF_TABLES_H */
diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h
index 354a7e5e50f2..4bb8cb7730e7 100644
--- a/include/uapi/linux/netfilter/nfnetlink.h
+++ b/include/uapi/linux/netfilter/nfnetlink.h
@@ -22,6 +22,8 @@ enum nfnetlink_groups {
#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES
NFNLGRP_ACCT_QUOTA,
#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA
+ NFNLGRP_NFTRACE,
+#define NFNLGRP_NFTRACE NFNLGRP_NFTRACE
__NFNLGRP_MAX,
};
#define NFNLGRP_MAX (__NFNLGRP_MAX - 1)
diff --git a/include/uapi/linux/netfilter/xt_HMARK.h b/include/uapi/linux/netfilter/xt_HMARK.h
index 826fc5807577..3fb48c8d8d78 100644
--- a/include/uapi/linux/netfilter/xt_HMARK.h
+++ b/include/uapi/linux/netfilter/xt_HMARK.h
@@ -2,6 +2,7 @@
#define XT_HMARK_H_
#include <linux/types.h>
+#include <linux/netfilter.h>
enum {
XT_HMARK_SADDR_MASK,
diff --git a/include/uapi/linux/netfilter/xt_RATEEST.h b/include/uapi/linux/netfilter/xt_RATEEST.h
index 6605e20ad8cf..ec1b57047e03 100644
--- a/include/uapi/linux/netfilter/xt_RATEEST.h
+++ b/include/uapi/linux/netfilter/xt_RATEEST.h
@@ -2,6 +2,7 @@
#define _XT_RATEEST_TARGET_H
#include <linux/types.h>
+#include <linux/if.h>
struct xt_rateest_target_info {
char name[IFNAMSIZ];
diff --git a/include/uapi/linux/netfilter/xt_TEE.h b/include/uapi/linux/netfilter/xt_TEE.h
index 5c21d5c829af..01092023404b 100644
--- a/include/uapi/linux/netfilter/xt_TEE.h
+++ b/include/uapi/linux/netfilter/xt_TEE.h
@@ -1,6 +1,8 @@
#ifndef _XT_TEE_TARGET_H
#define _XT_TEE_TARGET_H
+#include <linux/netfilter.h>
+
struct xt_tee_tginfo {
union nf_inet_addr gw;
char oif[16];
diff --git a/include/uapi/linux/netfilter/xt_TPROXY.h b/include/uapi/linux/netfilter/xt_TPROXY.h
index 902043c2073f..8d693eefdc1f 100644
--- a/include/uapi/linux/netfilter/xt_TPROXY.h
+++ b/include/uapi/linux/netfilter/xt_TPROXY.h
@@ -2,6 +2,7 @@
#define _XT_TPROXY_H
#include <linux/types.h>
+#include <linux/netfilter.h>
/* TPROXY target is capable of marking the packet to perform
* redirection. We can get rid of that whenever we get support for
diff --git a/include/uapi/linux/netfilter/xt_cgroup.h b/include/uapi/linux/netfilter/xt_cgroup.h
index 43acb7e175f6..1e4b37b93bef 100644
--- a/include/uapi/linux/netfilter/xt_cgroup.h
+++ b/include/uapi/linux/netfilter/xt_cgroup.h
@@ -2,10 +2,23 @@
#define _UAPI_XT_CGROUP_H
#include <linux/types.h>
+#include <linux/limits.h>
-struct xt_cgroup_info {
+struct xt_cgroup_info_v0 {
__u32 id;
__u32 invert;
};
+struct xt_cgroup_info_v1 {
+ __u8 has_path;
+ __u8 has_classid;
+ __u8 invert_path;
+ __u8 invert_classid;
+ char path[PATH_MAX];
+ __u32 classid;
+
+ /* kernel internal data */
+ void *priv __attribute__((aligned(8)));
+};
+
#endif /* _UAPI_XT_CGROUP_H */
diff --git a/include/uapi/linux/netfilter/xt_hashlimit.h b/include/uapi/linux/netfilter/xt_hashlimit.h
index cbfc43d1af68..6db90372f09c 100644
--- a/include/uapi/linux/netfilter/xt_hashlimit.h
+++ b/include/uapi/linux/netfilter/xt_hashlimit.h
@@ -2,6 +2,7 @@
#define _UAPI_XT_HASHLIMIT_H
#include <linux/types.h>
+#include <linux/if.h>
/* timings are in milliseconds. */
#define XT_HASHLIMIT_SCALE 10000
diff --git a/include/uapi/linux/netfilter/xt_ipvs.h b/include/uapi/linux/netfilter/xt_ipvs.h
index eff34ac18808..e03b9c31a39d 100644
--- a/include/uapi/linux/netfilter/xt_ipvs.h
+++ b/include/uapi/linux/netfilter/xt_ipvs.h
@@ -2,6 +2,7 @@
#define _XT_IPVS_H
#include <linux/types.h>
+#include <linux/netfilter.h>
enum {
XT_IPVS_IPVS_PROPERTY = 1 << 0, /* all other options imply this one */
diff --git a/include/uapi/linux/netfilter/xt_mac.h b/include/uapi/linux/netfilter/xt_mac.h
index b892cdc67e06..9a19a08a9181 100644
--- a/include/uapi/linux/netfilter/xt_mac.h
+++ b/include/uapi/linux/netfilter/xt_mac.h
@@ -1,6 +1,8 @@
#ifndef _XT_MAC_H
#define _XT_MAC_H
+#include <linux/if_ether.h>
+
struct xt_mac_info {
unsigned char srcaddr[ETH_ALEN];
int invert;
diff --git a/include/uapi/linux/netfilter/xt_osf.h b/include/uapi/linux/netfilter/xt_osf.h
index 5d66caeba3ee..e6159958b2fb 100644
--- a/include/uapi/linux/netfilter/xt_osf.h
+++ b/include/uapi/linux/netfilter/xt_osf.h
@@ -20,6 +20,8 @@
#define _XT_OSF_H
#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
#define MAXGENRELEN 32
diff --git a/include/uapi/linux/netfilter/xt_physdev.h b/include/uapi/linux/netfilter/xt_physdev.h
index db7a2982e9c0..ccdde87da214 100644
--- a/include/uapi/linux/netfilter/xt_physdev.h
+++ b/include/uapi/linux/netfilter/xt_physdev.h
@@ -2,7 +2,7 @@
#define _UAPI_XT_PHYSDEV_H
#include <linux/types.h>
-
+#include <linux/if.h>
#define XT_PHYSDEV_OP_IN 0x01
#define XT_PHYSDEV_OP_OUT 0x02
diff --git a/include/uapi/linux/netfilter/xt_policy.h b/include/uapi/linux/netfilter/xt_policy.h
index be8ead05c316..d8a9800dce61 100644
--- a/include/uapi/linux/netfilter/xt_policy.h
+++ b/include/uapi/linux/netfilter/xt_policy.h
@@ -2,6 +2,8 @@
#define _XT_POLICY_H
#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/in6.h>
#define XT_POLICY_MAX_ELEM 4
diff --git a/include/uapi/linux/netfilter/xt_rateest.h b/include/uapi/linux/netfilter/xt_rateest.h
index d40a6196842a..13fe50d4e4b3 100644
--- a/include/uapi/linux/netfilter/xt_rateest.h
+++ b/include/uapi/linux/netfilter/xt_rateest.h
@@ -2,6 +2,7 @@
#define _XT_RATEEST_MATCH_H
#include <linux/types.h>
+#include <linux/if.h>
enum xt_rateest_match_flags {
XT_RATEEST_MATCH_INVERT = 1<<0,
diff --git a/include/uapi/linux/netfilter/xt_recent.h b/include/uapi/linux/netfilter/xt_recent.h
index 6ef36c113e89..955d562031cc 100644
--- a/include/uapi/linux/netfilter/xt_recent.h
+++ b/include/uapi/linux/netfilter/xt_recent.h
@@ -2,6 +2,7 @@
#define _LINUX_NETFILTER_XT_RECENT_H 1
#include <linux/types.h>
+#include <linux/netfilter.h>
enum {
XT_RECENT_CHECK = 1 << 0,
diff --git a/include/uapi/linux/netfilter/xt_sctp.h b/include/uapi/linux/netfilter/xt_sctp.h
index 29287be696a2..58ffcfb7978e 100644
--- a/include/uapi/linux/netfilter/xt_sctp.h
+++ b/include/uapi/linux/netfilter/xt_sctp.h
@@ -66,26 +66,26 @@ struct xt_sctp_info {
#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap) \
__sctp_chunkmap_is_clear((chunkmap), ARRAY_SIZE(chunkmap))
-static inline bool
+static inline _Bool
__sctp_chunkmap_is_clear(const __u32 *chunkmap, unsigned int n)
{
unsigned int i;
for (i = 0; i < n; ++i)
if (chunkmap[i])
- return false;
- return true;
+ return 0;
+ return 1;
}
#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap) \
__sctp_chunkmap_is_all_set((chunkmap), ARRAY_SIZE(chunkmap))
-static inline bool
+static inline _Bool
__sctp_chunkmap_is_all_set(const __u32 *chunkmap, unsigned int n)
{
unsigned int i;
for (i = 0; i < n; ++i)
if (chunkmap[i] != ~0U)
- return false;
- return true;
+ return 0;
+ return 1;
}
#endif /* _XT_SCTP_H_ */
diff --git a/include/uapi/linux/netfilter_arp/arp_tables.h b/include/uapi/linux/netfilter_arp/arp_tables.h
index a5a86a4db6b3..ece3ad4eecda 100644
--- a/include/uapi/linux/netfilter_arp/arp_tables.h
+++ b/include/uapi/linux/netfilter_arp/arp_tables.h
@@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/compiler.h>
+#include <linux/if.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter/x_tables.h>
diff --git a/include/uapi/linux/netfilter_bridge.h b/include/uapi/linux/netfilter_bridge.h
index a5eda6db8d79..514519b47651 100644
--- a/include/uapi/linux/netfilter_bridge.h
+++ b/include/uapi/linux/netfilter_bridge.h
@@ -4,6 +4,7 @@
/* bridge-specific defines for netfilter.
*/
+#include <linux/in.h>
#include <linux/netfilter.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
diff --git a/include/uapi/linux/netfilter_bridge/ebt_arp.h b/include/uapi/linux/netfilter_bridge/ebt_arp.h
index 522f3e427f49..dd4df25330e8 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_arp.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_arp.h
@@ -2,6 +2,7 @@
#define __LINUX_BRIDGE_EBT_ARP_H
#include <linux/types.h>
+#include <linux/if_ether.h>
#define EBT_ARP_OPCODE 0x01
#define EBT_ARP_HTYPE 0x02
diff --git a/include/uapi/linux/netfilter_bridge/ebt_arpreply.h b/include/uapi/linux/netfilter_bridge/ebt_arpreply.h
index 7e77896e1fbf..6fee3402e307 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_arpreply.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_arpreply.h
@@ -1,6 +1,8 @@
#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
#define __LINUX_BRIDGE_EBT_ARPREPLY_H
+#include <linux/if_ether.h>
+
struct ebt_arpreply_info {
unsigned char mac[ETH_ALEN];
int target;
diff --git a/include/uapi/linux/netfilter_bridge/ebt_ip6.h b/include/uapi/linux/netfilter_bridge/ebt_ip6.h
index 42b889682721..a062f0ce95f9 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_ip6.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_ip6.h
@@ -13,6 +13,7 @@
#define __LINUX_BRIDGE_EBT_IP6_H
#include <linux/types.h>
+#include <linux/in6.h>
#define EBT_IP6_SOURCE 0x01
#define EBT_IP6_DEST 0x02
diff --git a/include/uapi/linux/netfilter_bridge/ebt_nat.h b/include/uapi/linux/netfilter_bridge/ebt_nat.h
index 5e74e3b03bd6..c990d74ee966 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_nat.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_nat.h
@@ -1,6 +1,8 @@
#ifndef __LINUX_BRIDGE_EBT_NAT_H
#define __LINUX_BRIDGE_EBT_NAT_H
+#include <linux/if_ether.h>
+
#define NAT_ARP_BIT (0x00000010)
struct ebt_nat_info {
unsigned char mac[ETH_ALEN];
diff --git a/include/uapi/linux/netfilter_bridge/ebtables.h b/include/uapi/linux/netfilter_bridge/ebtables.h
index fd2ee501726d..e3cdf9f1a259 100644
--- a/include/uapi/linux/netfilter_bridge/ebtables.h
+++ b/include/uapi/linux/netfilter_bridge/ebtables.h
@@ -12,6 +12,8 @@
#ifndef _UAPI__LINUX_BRIDGE_EFF_H
#define _UAPI__LINUX_BRIDGE_EFF_H
+#include <linux/types.h>
+#include <linux/if.h>
#include <linux/netfilter_bridge.h>
#define EBT_TABLE_MAXNAMELEN 32
@@ -33,8 +35,8 @@ struct xt_match;
struct xt_target;
struct ebt_counter {
- uint64_t pcnt;
- uint64_t bcnt;
+ __u64 pcnt;
+ __u64 bcnt;
};
struct ebt_replace {
diff --git a/include/uapi/linux/netfilter_ipv4/ip_tables.h b/include/uapi/linux/netfilter_ipv4/ip_tables.h
index f1e6ef256034..d0da53d96d93 100644
--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h
+++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/compiler.h>
+#include <linux/if.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/x_tables.h>
diff --git a/include/uapi/linux/netfilter_ipv6/ip6_tables.h b/include/uapi/linux/netfilter_ipv6/ip6_tables.h
index 649c68062dca..d1b22653daf2 100644
--- a/include/uapi/linux/netfilter_ipv6/ip6_tables.h
+++ b/include/uapi/linux/netfilter_ipv6/ip6_tables.h
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/compiler.h>
+#include <linux/if.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/x_tables.h>
diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_rt.h b/include/uapi/linux/netfilter_ipv6/ip6t_rt.h
index 7605a5ff81cd..558f81e46fb9 100644
--- a/include/uapi/linux/netfilter_ipv6/ip6t_rt.h
+++ b/include/uapi/linux/netfilter_ipv6/ip6t_rt.h
@@ -2,7 +2,7 @@
#define _IP6T_RT_H
#include <linux/types.h>
-/*#include <linux/in6.h>*/
+#include <linux/in6.h>
#define IP6T_RT_HOPS 16
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 1f0b4cf5dd03..5b7b5ebe7ca8 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -820,6 +820,10 @@
* as an event to indicate changes for devices with wiphy-specific regdom
* management.
*
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ * not running. The driver indicates the status of the scan through
+ * cfg80211_scan_done().
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1006,6 +1010,8 @@ enum nl80211_commands {
NL80211_CMD_WIPHY_REG_CHANGE,
+ NL80211_CMD_ABORT_SCAN,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1764,8 +1770,9 @@ enum nl80211_commands {
* over all channels.
*
* @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
- * scheduled scan (or a WoWLAN net-detect scan) is started, u32
- * in seconds.
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 8d2530daca9f..8cb18b44968e 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -72,6 +72,10 @@ struct tc_estimator {
#define TC_H_UNSPEC (0U)
#define TC_H_ROOT (0xFFFFFFFFU)
#define TC_H_INGRESS (0xFFFFFFF1U)
+#define TC_H_CLSACT TC_H_INGRESS
+
+#define TC_H_MIN_INGRESS 0xFFF2U
+#define TC_H_MIN_EGRESS 0xFFF3U
/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */
enum tc_link_layer {
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 123a5af4e8bb..ca764b5da86d 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -311,6 +311,7 @@ enum rtattr_type_t {
RTA_PREF,
RTA_ENCAP_TYPE,
RTA_ENCAP,
+ RTA_EXPIRES,
__RTA_MAX
};
diff --git a/include/uapi/linux/sock_diag.h b/include/uapi/linux/sock_diag.h
index 49230d36f9ce..bae2d80034d4 100644
--- a/include/uapi/linux/sock_diag.h
+++ b/include/uapi/linux/sock_diag.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#define SOCK_DIAG_BY_FAMILY 20
+#define SOCK_DESTROY 21
struct sock_diag_req {
__u8 sdiag_family;
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
index e888b1aed69f..8e7890b26d9a 100644
--- a/include/uapi/linux/sockios.h
+++ b/include/uapi/linux/sockios.h
@@ -27,7 +27,7 @@
/* Routing table calls. */
#define SIOCADDRT 0x890B /* add routing table entry */
#define SIOCDELRT 0x890C /* delete routing table entry */
-#define SIOCRTMSG 0x890D /* call to routing system */
+#define SIOCRTMSG 0x890D /* unused */
/* Socket configuration controls. */
#define SIOCGIFNAME 0x8910 /* get iface name */
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 334b1bdd572c..972d9a8e4ac4 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -306,10 +306,6 @@ static unsigned int __bpf_prog_run(void *ctx, const struct bpf_insn *insn)
FP = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
ARG1 = (u64) (unsigned long) ctx;
- /* Registers used in classic BPF programs need to be reset first. */
- regs[BPF_REG_A] = 0;
- regs[BPF_REG_X] = 0;
-
select_insn:
goto *jumptable[insn->code];
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 34777b3746fa..c5b30fd8a315 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -14,11 +14,15 @@
#include <linux/filter.h>
#include <linux/vmalloc.h>
+struct bucket {
+ struct hlist_head head;
+ raw_spinlock_t lock;
+};
+
struct bpf_htab {
struct bpf_map map;
- struct hlist_head *buckets;
- raw_spinlock_t lock;
- u32 count; /* number of elements in this hashtable */
+ struct bucket *buckets;
+ atomic_t count; /* number of elements in this hashtable */
u32 n_buckets; /* number of hash buckets */
u32 elem_size; /* size of each element in bytes */
};
@@ -79,34 +83,35 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
/* prevent zero size kmalloc and check for u32 overflow */
if (htab->n_buckets == 0 ||
- htab->n_buckets > U32_MAX / sizeof(struct hlist_head))
+ htab->n_buckets > U32_MAX / sizeof(struct bucket))
goto free_htab;
- if ((u64) htab->n_buckets * sizeof(struct hlist_head) +
+ if ((u64) htab->n_buckets * sizeof(struct bucket) +
(u64) htab->elem_size * htab->map.max_entries >=
U32_MAX - PAGE_SIZE)
/* make sure page count doesn't overflow */
goto free_htab;
- htab->map.pages = round_up(htab->n_buckets * sizeof(struct hlist_head) +
+ htab->map.pages = round_up(htab->n_buckets * sizeof(struct bucket) +
htab->elem_size * htab->map.max_entries,
PAGE_SIZE) >> PAGE_SHIFT;
err = -ENOMEM;
- htab->buckets = kmalloc_array(htab->n_buckets, sizeof(struct hlist_head),
+ htab->buckets = kmalloc_array(htab->n_buckets, sizeof(struct bucket),
GFP_USER | __GFP_NOWARN);
if (!htab->buckets) {
- htab->buckets = vmalloc(htab->n_buckets * sizeof(struct hlist_head));
+ htab->buckets = vmalloc(htab->n_buckets * sizeof(struct bucket));
if (!htab->buckets)
goto free_htab;
}
- for (i = 0; i < htab->n_buckets; i++)
- INIT_HLIST_HEAD(&htab->buckets[i]);
+ for (i = 0; i < htab->n_buckets; i++) {
+ INIT_HLIST_HEAD(&htab->buckets[i].head);
+ raw_spin_lock_init(&htab->buckets[i].lock);
+ }
- raw_spin_lock_init(&htab->lock);
- htab->count = 0;
+ atomic_set(&htab->count, 0);
return &htab->map;
@@ -120,11 +125,16 @@ static inline u32 htab_map_hash(const void *key, u32 key_len)
return jhash(key, key_len, 0);
}
-static inline struct hlist_head *select_bucket(struct bpf_htab *htab, u32 hash)
+static inline struct bucket *__select_bucket(struct bpf_htab *htab, u32 hash)
{
return &htab->buckets[hash & (htab->n_buckets - 1)];
}
+static inline struct hlist_head *select_bucket(struct bpf_htab *htab, u32 hash)
+{
+ return &__select_bucket(htab, hash)->head;
+}
+
static struct htab_elem *lookup_elem_raw(struct hlist_head *head, u32 hash,
void *key, u32 key_size)
{
@@ -227,6 +237,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
struct htab_elem *l_new, *l_old;
struct hlist_head *head;
+ struct bucket *b;
unsigned long flags;
u32 key_size;
int ret;
@@ -248,15 +259,15 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
memcpy(l_new->key + round_up(key_size, 8), value, map->value_size);
l_new->hash = htab_map_hash(l_new->key, key_size);
+ b = __select_bucket(htab, l_new->hash);
+ head = &b->head;
/* bpf_map_update_elem() can be called in_irq() */
- raw_spin_lock_irqsave(&htab->lock, flags);
-
- head = select_bucket(htab, l_new->hash);
+ raw_spin_lock_irqsave(&b->lock, flags);
l_old = lookup_elem_raw(head, l_new->hash, key, key_size);
- if (!l_old && unlikely(htab->count >= map->max_entries)) {
+ if (!l_old && unlikely(atomic_read(&htab->count) >= map->max_entries)) {
/* if elem with this 'key' doesn't exist and we've reached
* max_entries limit, fail insertion of new elem
*/
@@ -284,13 +295,13 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
hlist_del_rcu(&l_old->hash_node);
kfree_rcu(l_old, rcu);
} else {
- htab->count++;
+ atomic_inc(&htab->count);
}
- raw_spin_unlock_irqrestore(&htab->lock, flags);
+ raw_spin_unlock_irqrestore(&b->lock, flags);
return 0;
err:
- raw_spin_unlock_irqrestore(&htab->lock, flags);
+ raw_spin_unlock_irqrestore(&b->lock, flags);
kfree(l_new);
return ret;
}
@@ -300,6 +311,7 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
{
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
struct hlist_head *head;
+ struct bucket *b;
struct htab_elem *l;
unsigned long flags;
u32 hash, key_size;
@@ -310,21 +322,21 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
key_size = map->key_size;
hash = htab_map_hash(key, key_size);
+ b = __select_bucket(htab, hash);
+ head = &b->head;
- raw_spin_lock_irqsave(&htab->lock, flags);
-
- head = select_bucket(htab, hash);
+ raw_spin_lock_irqsave(&b->lock, flags);
l = lookup_elem_raw(head, hash, key, key_size);
if (l) {
hlist_del_rcu(&l->hash_node);
- htab->count--;
+ atomic_dec(&htab->count);
kfree_rcu(l, rcu);
ret = 0;
}
- raw_spin_unlock_irqrestore(&htab->lock, flags);
+ raw_spin_unlock_irqrestore(&b->lock, flags);
return ret;
}
@@ -339,7 +351,7 @@ static void delete_all_elements(struct bpf_htab *htab)
hlist_for_each_entry_safe(l, n, head, hash_node) {
hlist_del_rcu(&l->hash_node);
- htab->count--;
+ atomic_dec(&htab->count);
kfree(l);
}
}
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 5a8a797d50b7..f2ece3c174a5 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -187,11 +187,31 @@ static int bpf_mkobj(struct inode *dir, struct dentry *dentry, umode_t mode,
}
}
+static int bpf_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ if (bpf_dname_reserved(new_dentry))
+ return -EPERM;
+
+ return simple_link(old_dentry, dir, new_dentry);
+}
+
+static int bpf_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ if (bpf_dname_reserved(new_dentry))
+ return -EPERM;
+
+ return simple_rename(old_dir, old_dentry, new_dir, new_dentry);
+}
+
static const struct inode_operations bpf_dir_iops = {
.lookup = simple_lookup,
.mknod = bpf_mkobj,
.mkdir = bpf_mkdir,
.rmdir = simple_rmdir,
+ .rename = bpf_rename,
+ .link = bpf_link,
.unlink = simple_unlink,
};
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 3b39550d8485..637397059f76 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -113,8 +113,28 @@ static int bpf_map_release(struct inode *inode, struct file *filp)
return 0;
}
+#ifdef CONFIG_PROC_FS
+static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp)
+{
+ const struct bpf_map *map = filp->private_data;
+
+ seq_printf(m,
+ "map_type:\t%u\n"
+ "key_size:\t%u\n"
+ "value_size:\t%u\n"
+ "max_entries:\t%u\n",
+ map->map_type,
+ map->key_size,
+ map->value_size,
+ map->max_entries);
+}
+#endif
+
static const struct file_operations bpf_map_fops = {
- .release = bpf_map_release,
+#ifdef CONFIG_PROC_FS
+ .show_fdinfo = bpf_map_show_fdinfo,
+#endif
+ .release = bpf_map_release,
};
int bpf_map_new_fd(struct bpf_map *map)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a7945d10b378..d1d3e8f57de9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1121,6 +1121,16 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
return -EINVAL;
}
+ if ((opcode == BPF_LSH || opcode == BPF_RSH ||
+ opcode == BPF_ARSH) && BPF_SRC(insn->code) == BPF_K) {
+ int size = BPF_CLASS(insn->code) == BPF_ALU64 ? 64 : 32;
+
+ if (insn->imm < 0 || insn->imm >= size) {
+ verbose("invalid shift %d\n", insn->imm);
+ return -EINVAL;
+ }
+ }
+
/* pattern match 'bpf_add Rx, imm' instruction */
if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 &&
regs[insn->dst_reg].type == FRAME_PTR &&
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 470f6536b9e8..fe95970b1f79 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -57,8 +57,8 @@
#include <linux/vmalloc.h> /* TODO: replace with more sophisticated array */
#include <linux/kthread.h>
#include <linux/delay.h>
-
#include <linux/atomic.h>
+#include <net/sock.h>
/*
* pidlists linger the following amount before being destroyed. The goal
@@ -440,11 +440,6 @@ static bool cgroup_tryget(struct cgroup *cgrp)
return css_tryget(&cgrp->self);
}
-static void cgroup_put(struct cgroup *cgrp)
-{
- css_put(&cgrp->self);
-}
-
struct cgroup_subsys_state *of_css(struct kernfs_open_file *of)
{
struct cgroup *cgrp = of->kn->parent->priv;
@@ -465,25 +460,6 @@ struct cgroup_subsys_state *of_css(struct kernfs_open_file *of)
}
EXPORT_SYMBOL_GPL(of_css);
-/**
- * cgroup_is_descendant - test ancestry
- * @cgrp: the cgroup to be tested
- * @ancestor: possible ancestor of @cgrp
- *
- * Test whether @cgrp is a descendant of @ancestor. It also returns %true
- * if @cgrp == @ancestor. This function is safe to call as long as @cgrp
- * and @ancestor are accessible.
- */
-bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor)
-{
- while (cgrp) {
- if (cgrp == ancestor)
- return true;
- cgrp = cgroup_parent(cgrp);
- }
- return false;
-}
-
static int notify_on_release(const struct cgroup *cgrp)
{
return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
@@ -1924,6 +1900,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask)
if (ret < 0)
goto out;
root_cgrp->id = ret;
+ root_cgrp->ancestor_ids[0] = ret;
ret = percpu_ref_init(&root_cgrp->self.refcnt, css_release, 0,
GFP_KERNEL);
@@ -4903,11 +4880,11 @@ err_free_css:
static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
umode_t mode)
{
- struct cgroup *parent, *cgrp;
+ struct cgroup *parent, *cgrp, *tcgrp;
struct cgroup_root *root;
struct cgroup_subsys *ss;
struct kernfs_node *kn;
- int ssid, ret;
+ int level, ssid, ret;
/* Do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable.
*/
@@ -4918,9 +4895,11 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
if (!parent)
return -ENODEV;
root = parent->root;
+ level = parent->level + 1;
/* allocate the cgroup and its ID, 0 is reserved for the root */
- cgrp = kzalloc(sizeof(*cgrp), GFP_KERNEL);
+ cgrp = kzalloc(sizeof(*cgrp) +
+ sizeof(cgrp->ancestor_ids[0]) * (level + 1), GFP_KERNEL);
if (!cgrp) {
ret = -ENOMEM;
goto out_unlock;
@@ -4944,6 +4923,10 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
cgrp->self.parent = &parent->self;
cgrp->root = root;
+ cgrp->level = level;
+
+ for (tcgrp = cgrp; tcgrp; tcgrp = cgroup_parent(tcgrp))
+ cgrp->ancestor_ids[tcgrp->level] = tcgrp->id;
if (notify_on_release(parent))
set_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
@@ -5822,6 +5805,93 @@ struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss)
return id > 0 ? idr_find(&ss->css_idr, id) : NULL;
}
+/**
+ * cgroup_get_from_path - lookup and get a cgroup from its default hierarchy path
+ * @path: path on the default hierarchy
+ *
+ * Find the cgroup at @path on the default hierarchy, increment its
+ * reference count and return it. Returns pointer to the found cgroup on
+ * success, ERR_PTR(-ENOENT) if @path doens't exist and ERR_PTR(-ENOTDIR)
+ * if @path points to a non-directory.
+ */
+struct cgroup *cgroup_get_from_path(const char *path)
+{
+ struct kernfs_node *kn;
+ struct cgroup *cgrp;
+
+ mutex_lock(&cgroup_mutex);
+
+ kn = kernfs_walk_and_get(cgrp_dfl_root.cgrp.kn, path);
+ if (kn) {
+ if (kernfs_type(kn) == KERNFS_DIR) {
+ cgrp = kn->priv;
+ cgroup_get(cgrp);
+ } else {
+ cgrp = ERR_PTR(-ENOTDIR);
+ }
+ kernfs_put(kn);
+ } else {
+ cgrp = ERR_PTR(-ENOENT);
+ }
+
+ mutex_unlock(&cgroup_mutex);
+ return cgrp;
+}
+EXPORT_SYMBOL_GPL(cgroup_get_from_path);
+
+/*
+ * sock->sk_cgrp_data handling. For more info, see sock_cgroup_data
+ * definition in cgroup-defs.h.
+ */
+#ifdef CONFIG_SOCK_CGROUP_DATA
+
+#if defined(CONFIG_CGROUP_NET_PRIO) || defined(CONFIG_CGROUP_NET_CLASSID)
+
+DEFINE_SPINLOCK(cgroup_sk_update_lock);
+static bool cgroup_sk_alloc_disabled __read_mostly;
+
+void cgroup_sk_alloc_disable(void)
+{
+ if (cgroup_sk_alloc_disabled)
+ return;
+ pr_info("cgroup: disabling cgroup2 socket matching due to net_prio or net_cls activation\n");
+ cgroup_sk_alloc_disabled = true;
+}
+
+#else
+
+#define cgroup_sk_alloc_disabled false
+
+#endif
+
+void cgroup_sk_alloc(struct sock_cgroup_data *skcd)
+{
+ if (cgroup_sk_alloc_disabled)
+ return;
+
+ rcu_read_lock();
+
+ while (true) {
+ struct css_set *cset;
+
+ cset = task_css_set(current);
+ if (likely(cgroup_tryget(cset->dfl_cgrp))) {
+ skcd->val = (unsigned long)cset->dfl_cgrp;
+ break;
+ }
+ cpu_relax();
+ }
+
+ rcu_read_unlock();
+}
+
+void cgroup_sk_free(struct sock_cgroup_data *skcd)
+{
+ cgroup_put(sock_cgroup_ptr(skcd));
+}
+
+#endif /* CONFIG_SOCK_CGROUP_DATA */
+
#ifdef CONFIG_CGROUP_DEBUG
static struct cgroup_subsys_state *
debug_css_alloc(struct cgroup_subsys_state *parent_css)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index c98e93c0a084..e2a236a7341f 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1495,6 +1495,29 @@ config OF_RECONFIG_NOTIFIER_ERROR_INJECT
If unsure, say N.
+config NETDEV_NOTIFIER_ERROR_INJECT
+ tristate "Netdev notifier error injection module"
+ depends on NET && NOTIFIER_ERROR_INJECTION
+ help
+ This option provides the ability to inject artificial errors to
+ netdevice notifier chain callbacks. It is controlled through debugfs
+ interface /sys/kernel/debug/notifier-error-inject/netdev
+
+ If the notifier call chain should be failed with some events
+ notified, write the error code to "actions/<notifier event>/error".
+
+ Example: Inject netdevice mtu change error (-22 = -EINVAL)
+
+ # cd /sys/kernel/debug/notifier-error-inject/netdev
+ # echo -22 > actions/NETDEV_CHANGEMTU/error
+ # ip link set eth0 mtu 1024
+ RTNETLINK answers: Invalid argument
+
+ To compile this code as a module, choose M here: the module will
+ be called netdev-notifier-error-inject.
+
+ If unsure, say N.
+
config FAULT_INJECTION
bool "Fault-injection framework"
depends on DEBUG_KERNEL
diff --git a/lib/Makefile b/lib/Makefile
index 7f1de26613d2..180dd4d0dd41 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o
obj-$(CONFIG_NOTIFIER_ERROR_INJECTION) += notifier-error-inject.o
obj-$(CONFIG_CPU_NOTIFIER_ERROR_INJECT) += cpu-notifier-error-inject.o
obj-$(CONFIG_PM_NOTIFIER_ERROR_INJECT) += pm-notifier-error-inject.o
+obj-$(CONFIG_NETDEV_NOTIFIER_ERROR_INJECT) += netdev-notifier-error-inject.o
obj-$(CONFIG_MEMORY_NOTIFIER_ERROR_INJECT) += memory-notifier-error-inject.o
obj-$(CONFIG_OF_RECONFIG_NOTIFIER_ERROR_INJECT) += \
of-reconfig-notifier-error-inject.o
diff --git a/lib/netdev-notifier-error-inject.c b/lib/netdev-notifier-error-inject.c
new file mode 100644
index 000000000000..13e9c62e216f
--- /dev/null
+++ b/lib/netdev-notifier-error-inject.c
@@ -0,0 +1,55 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "notifier-error-inject.h"
+
+static int priority;
+module_param(priority, int, 0);
+MODULE_PARM_DESC(priority, "specify netdevice notifier priority");
+
+static struct notifier_err_inject netdev_notifier_err_inject = {
+ .actions = {
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_REGISTER) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_CHANGEMTU) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_CHANGENAME) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRE_UP) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRE_TYPE_CHANGE) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_POST_INIT) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRECHANGEMTU) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRECHANGEUPPER) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_CHANGEUPPER) },
+ {}
+ }
+};
+
+static struct dentry *dir;
+
+static int netdev_err_inject_init(void)
+{
+ int err;
+
+ dir = notifier_err_inject_init("netdev", notifier_err_inject_dir,
+ &netdev_notifier_err_inject, priority);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ err = register_netdevice_notifier(&netdev_notifier_err_inject.nb);
+ if (err)
+ debugfs_remove_recursive(dir);
+
+ return err;
+}
+
+static void netdev_err_inject_exit(void)
+{
+ unregister_netdevice_notifier(&netdev_notifier_err_inject.nb);
+ debugfs_remove_recursive(dir);
+}
+
+module_init(netdev_err_inject_init);
+module_exit(netdev_err_inject_exit);
+
+MODULE_DESCRIPTION("Netdevice notifier error injection module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nikolay Aleksandrov <razor@blackwall.org>");
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 51282f579760..cc808707d1cf 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -231,9 +231,6 @@ static int rhashtable_rehash_attach(struct rhashtable *ht,
*/
rcu_assign_pointer(old_tbl->future_tbl, new_tbl);
- /* Ensure the new table is visible to readers. */
- smp_wmb();
-
spin_unlock_bh(old_tbl->locks);
return 0;
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 10cd1860e5b0..27a7a26b1ece 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -1685,6 +1685,126 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 0x35d97ef2 } }
},
+ { /* Mainly checking JIT here. */
+ "MOV REG64",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0xffffffffffffffffLL),
+ BPF_MOV64_REG(R1, R0),
+ BPF_MOV64_REG(R2, R1),
+ BPF_MOV64_REG(R3, R2),
+ BPF_MOV64_REG(R4, R3),
+ BPF_MOV64_REG(R5, R4),
+ BPF_MOV64_REG(R6, R5),
+ BPF_MOV64_REG(R7, R6),
+ BPF_MOV64_REG(R8, R7),
+ BPF_MOV64_REG(R9, R8),
+ BPF_ALU64_IMM(BPF_MOV, R0, 0),
+ BPF_ALU64_IMM(BPF_MOV, R1, 0),
+ BPF_ALU64_IMM(BPF_MOV, R2, 0),
+ BPF_ALU64_IMM(BPF_MOV, R3, 0),
+ BPF_ALU64_IMM(BPF_MOV, R4, 0),
+ BPF_ALU64_IMM(BPF_MOV, R5, 0),
+ BPF_ALU64_IMM(BPF_MOV, R6, 0),
+ BPF_ALU64_IMM(BPF_MOV, R7, 0),
+ BPF_ALU64_IMM(BPF_MOV, R8, 0),
+ BPF_ALU64_IMM(BPF_MOV, R9, 0),
+ BPF_ALU64_REG(BPF_ADD, R0, R0),
+ BPF_ALU64_REG(BPF_ADD, R0, R1),
+ BPF_ALU64_REG(BPF_ADD, R0, R2),
+ BPF_ALU64_REG(BPF_ADD, R0, R3),
+ BPF_ALU64_REG(BPF_ADD, R0, R4),
+ BPF_ALU64_REG(BPF_ADD, R0, R5),
+ BPF_ALU64_REG(BPF_ADD, R0, R6),
+ BPF_ALU64_REG(BPF_ADD, R0, R7),
+ BPF_ALU64_REG(BPF_ADD, R0, R8),
+ BPF_ALU64_REG(BPF_ADD, R0, R9),
+ BPF_ALU64_IMM(BPF_ADD, R0, 0xfefe),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xfefe } }
+ },
+ { /* Mainly checking JIT here. */
+ "MOV REG32",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0xffffffffffffffffLL),
+ BPF_MOV64_REG(R1, R0),
+ BPF_MOV64_REG(R2, R1),
+ BPF_MOV64_REG(R3, R2),
+ BPF_MOV64_REG(R4, R3),
+ BPF_MOV64_REG(R5, R4),
+ BPF_MOV64_REG(R6, R5),
+ BPF_MOV64_REG(R7, R6),
+ BPF_MOV64_REG(R8, R7),
+ BPF_MOV64_REG(R9, R8),
+ BPF_ALU32_IMM(BPF_MOV, R0, 0),
+ BPF_ALU32_IMM(BPF_MOV, R1, 0),
+ BPF_ALU32_IMM(BPF_MOV, R2, 0),
+ BPF_ALU32_IMM(BPF_MOV, R3, 0),
+ BPF_ALU32_IMM(BPF_MOV, R4, 0),
+ BPF_ALU32_IMM(BPF_MOV, R5, 0),
+ BPF_ALU32_IMM(BPF_MOV, R6, 0),
+ BPF_ALU32_IMM(BPF_MOV, R7, 0),
+ BPF_ALU32_IMM(BPF_MOV, R8, 0),
+ BPF_ALU32_IMM(BPF_MOV, R9, 0),
+ BPF_ALU64_REG(BPF_ADD, R0, R0),
+ BPF_ALU64_REG(BPF_ADD, R0, R1),
+ BPF_ALU64_REG(BPF_ADD, R0, R2),
+ BPF_ALU64_REG(BPF_ADD, R0, R3),
+ BPF_ALU64_REG(BPF_ADD, R0, R4),
+ BPF_ALU64_REG(BPF_ADD, R0, R5),
+ BPF_ALU64_REG(BPF_ADD, R0, R6),
+ BPF_ALU64_REG(BPF_ADD, R0, R7),
+ BPF_ALU64_REG(BPF_ADD, R0, R8),
+ BPF_ALU64_REG(BPF_ADD, R0, R9),
+ BPF_ALU64_IMM(BPF_ADD, R0, 0xfefe),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xfefe } }
+ },
+ { /* Mainly checking JIT here. */
+ "LD IMM64",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0xffffffffffffffffLL),
+ BPF_MOV64_REG(R1, R0),
+ BPF_MOV64_REG(R2, R1),
+ BPF_MOV64_REG(R3, R2),
+ BPF_MOV64_REG(R4, R3),
+ BPF_MOV64_REG(R5, R4),
+ BPF_MOV64_REG(R6, R5),
+ BPF_MOV64_REG(R7, R6),
+ BPF_MOV64_REG(R8, R7),
+ BPF_MOV64_REG(R9, R8),
+ BPF_LD_IMM64(R0, 0x0LL),
+ BPF_LD_IMM64(R1, 0x0LL),
+ BPF_LD_IMM64(R2, 0x0LL),
+ BPF_LD_IMM64(R3, 0x0LL),
+ BPF_LD_IMM64(R4, 0x0LL),
+ BPF_LD_IMM64(R5, 0x0LL),
+ BPF_LD_IMM64(R6, 0x0LL),
+ BPF_LD_IMM64(R7, 0x0LL),
+ BPF_LD_IMM64(R8, 0x0LL),
+ BPF_LD_IMM64(R9, 0x0LL),
+ BPF_ALU64_REG(BPF_ADD, R0, R0),
+ BPF_ALU64_REG(BPF_ADD, R0, R1),
+ BPF_ALU64_REG(BPF_ADD, R0, R2),
+ BPF_ALU64_REG(BPF_ADD, R0, R3),
+ BPF_ALU64_REG(BPF_ADD, R0, R4),
+ BPF_ALU64_REG(BPF_ADD, R0, R5),
+ BPF_ALU64_REG(BPF_ADD, R0, R6),
+ BPF_ALU64_REG(BPF_ADD, R0, R7),
+ BPF_ALU64_REG(BPF_ADD, R0, R8),
+ BPF_ALU64_REG(BPF_ADD, R0, R9),
+ BPF_ALU64_IMM(BPF_ADD, R0, 0xfefe),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xfefe } }
+ },
{
"INT: ALU MIX",
.u.insns_int = {
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c
index 8c1ad1ced72c..270bf7289b1e 100644
--- a/lib/test_rhashtable.c
+++ b/lib/test_rhashtable.c
@@ -36,9 +36,9 @@ static int runs = 4;
module_param(runs, int, 0);
MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
-static int max_size = 65536;
+static int max_size = 0;
module_param(max_size, int, 0);
-MODULE_PARM_DESC(runs, "Maximum table size (default: 65536)");
+MODULE_PARM_DESC(runs, "Maximum table size (default: calculated)");
static bool shrinking = false;
module_param(shrinking, bool, 0);
@@ -52,6 +52,10 @@ static int tcount = 10;
module_param(tcount, int, 0);
MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
+static bool enomem_retry = false;
+module_param(enomem_retry, bool, 0);
+MODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
+
struct test_obj {
int value;
struct rhash_head node;
@@ -76,6 +80,28 @@ static struct rhashtable_params test_rht_params = {
static struct semaphore prestart_sem;
static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0);
+static int insert_retry(struct rhashtable *ht, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ int err, retries = -1, enomem_retries = 0;
+
+ do {
+ retries++;
+ cond_resched();
+ err = rhashtable_insert_fast(ht, obj, params);
+ if (err == -ENOMEM && enomem_retry) {
+ enomem_retries++;
+ err = -EBUSY;
+ }
+ } while (err == -EBUSY);
+
+ if (enomem_retries)
+ pr_info(" %u insertions retried after -ENOMEM\n",
+ enomem_retries);
+
+ return err ? : retries;
+}
+
static int __init test_rht_lookup(struct rhashtable *ht)
{
unsigned int i;
@@ -157,7 +183,7 @@ static s64 __init test_rhashtable(struct rhashtable *ht)
{
struct test_obj *obj;
int err;
- unsigned int i, insert_fails = 0;
+ unsigned int i, insert_retries = 0;
s64 start, end;
/*
@@ -170,22 +196,16 @@ static s64 __init test_rhashtable(struct rhashtable *ht)
struct test_obj *obj = &array[i];
obj->value = i * 2;
-
- err = rhashtable_insert_fast(ht, &obj->node, test_rht_params);
- if (err == -ENOMEM || err == -EBUSY) {
- /* Mark failed inserts but continue */
- obj->value = TEST_INSERT_FAIL;
- insert_fails++;
- } else if (err) {
+ err = insert_retry(ht, &obj->node, test_rht_params);
+ if (err > 0)
+ insert_retries += err;
+ else if (err)
return err;
- }
-
- cond_resched();
}
- if (insert_fails)
- pr_info(" %u insertions failed due to memory pressure\n",
- insert_fails);
+ if (insert_retries)
+ pr_info(" %u insertions retried due to memory pressure\n",
+ insert_retries);
test_bucket_stats(ht);
rcu_read_lock();
@@ -236,13 +256,15 @@ static int thread_lookup_test(struct thread_data *tdata)
obj->value, key);
err++;
}
+
+ cond_resched();
}
return err;
}
static int threadfunc(void *data)
{
- int i, step, err = 0, insert_fails = 0;
+ int i, step, err = 0, insert_retries = 0;
struct thread_data *tdata = data;
up(&prestart_sem);
@@ -251,20 +273,18 @@ static int threadfunc(void *data)
for (i = 0; i < entries; i++) {
tdata->objs[i].value = (tdata->id << 16) | i;
- err = rhashtable_insert_fast(&ht, &tdata->objs[i].node,
- test_rht_params);
- if (err == -ENOMEM || err == -EBUSY) {
- tdata->objs[i].value = TEST_INSERT_FAIL;
- insert_fails++;
+ err = insert_retry(&ht, &tdata->objs[i].node, test_rht_params);
+ if (err > 0) {
+ insert_retries += err;
} else if (err) {
pr_err(" thread[%d]: rhashtable_insert_fast failed\n",
tdata->id);
goto out;
}
}
- if (insert_fails)
- pr_info(" thread[%d]: %d insert failures\n",
- tdata->id, insert_fails);
+ if (insert_retries)
+ pr_info(" thread[%d]: %u insertions retried due to memory pressure\n",
+ tdata->id, insert_retries);
err = thread_lookup_test(tdata);
if (err) {
@@ -285,6 +305,8 @@ static int threadfunc(void *data)
goto out;
}
tdata->objs[i].value = TEST_INSERT_FAIL;
+
+ cond_resched();
}
err = thread_lookup_test(tdata);
if (err) {
@@ -311,7 +333,7 @@ static int __init test_rht_init(void)
entries = min(entries, MAX_ENTRIES);
test_rht_params.automatic_shrinking = shrinking;
- test_rht_params.max_size = max_size;
+ test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
test_rht_params.nelem_hint = size;
pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
@@ -357,6 +379,8 @@ static int __init test_rht_init(void)
return -ENOMEM;
}
+ test_rht_params.max_size = max_size ? :
+ roundup_pow_of_two(tcount * entries);
err = rhashtable_init(&ht, &test_rht_params);
if (err < 0) {
pr_warn("Test failed: Unable to initialize hashtable: %d\n",
diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
new file mode 100644
index 000000000000..d16bb4b14aa1
--- /dev/null
+++ b/net/6lowpan/6lowpan_i.h
@@ -0,0 +1,28 @@
+#ifndef __6LOWPAN_I_H
+#define __6LOWPAN_I_H
+
+#include <linux/netdevice.h>
+
+#ifdef CONFIG_6LOWPAN_DEBUGFS
+int lowpan_dev_debugfs_init(struct net_device *dev);
+void lowpan_dev_debugfs_exit(struct net_device *dev);
+
+int __init lowpan_debugfs_init(void);
+void lowpan_debugfs_exit(void);
+#else
+static inline int lowpan_dev_debugfs_init(struct net_device *dev)
+{
+ return 0;
+}
+
+static inline void lowpan_dev_debugfs_exit(struct net_device *dev) { }
+
+static inline int __init lowpan_debugfs_init(void)
+{
+ return 0;
+}
+
+static inline void lowpan_debugfs_exit(void) { }
+#endif /* CONFIG_6LOWPAN_DEBUGFS */
+
+#endif /* __6LOWPAN_I_H */
diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
index 7fa0f382e7d1..9c051512d14f 100644
--- a/net/6lowpan/Kconfig
+++ b/net/6lowpan/Kconfig
@@ -5,12 +5,21 @@ menuconfig 6LOWPAN
This enables IPv6 over Low power Wireless Personal Area Network -
"6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
+config 6LOWPAN_DEBUGFS
+ bool "6LoWPAN debugfs support"
+ depends on 6LOWPAN
+ depends on DEBUG_FS
+ ---help---
+ This enables 6LoWPAN debugfs support. For example to manipulate
+ IPHC context information at runtime.
+
menuconfig 6LOWPAN_NHC
- tristate "Next Header Compression Support"
+ tristate "Next Header and Generic Header Compression Support"
depends on 6LOWPAN
default y
---help---
- Support for next header compression.
+ Support for next header and generic header compression defined in
+ RFC6282 and RFC7400.
if 6LOWPAN_NHC
@@ -58,4 +67,38 @@ config 6LOWPAN_NHC_UDP
---help---
6LoWPAN IPv6 UDP Header compression according to RFC6282.
+config 6LOWPAN_GHC_EXT_HDR_HOP
+ tristate "GHC Hop-by-Hop Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 Hop-by-Hop option generic header compression according
+ to RFC7400.
+
+config 6LOWPAN_GHC_UDP
+ tristate "GHC UDP Support"
+ ---help---
+ 6LoWPAN IPv6 UDP generic header compression according to RFC7400.
+
+config 6LOWPAN_GHC_ICMPV6
+ tristate "GHC ICMPv6 Support"
+ ---help---
+ 6LoWPAN IPv6 ICMPv6 generic header compression according to RFC7400.
+
+config 6LOWPAN_GHC_EXT_HDR_DEST
+ tristate "GHC Destination Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 destination option generic header compression according
+ to RFC7400.
+
+config 6LOWPAN_GHC_EXT_HDR_FRAG
+ tristate "GHC Fragmentation Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 fragmentation option generic header compression
+ according to RFC7400.
+
+config 6LOWPAN_GHC_EXT_HDR_ROUTE
+ tristate "GHC Routing Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 routing option generic header compression according
+ to RFC7400.
+
endif
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
index c6ffc55ee0d7..e44f3bf2dd42 100644
--- a/net/6lowpan/Makefile
+++ b/net/6lowpan/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_6LOWPAN) += 6lowpan.o
6lowpan-y := core.o iphc.o nhc.o
+6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o
#rfc6282 nhcs
obj-$(CONFIG_6LOWPAN_NHC_DEST) += nhc_dest.o
@@ -10,3 +11,11 @@ obj-$(CONFIG_6LOWPAN_NHC_IPV6) += nhc_ipv6.o
obj-$(CONFIG_6LOWPAN_NHC_MOBILITY) += nhc_mobility.o
obj-$(CONFIG_6LOWPAN_NHC_ROUTING) += nhc_routing.o
obj-$(CONFIG_6LOWPAN_NHC_UDP) += nhc_udp.o
+
+#rfc7400 ghcs
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_HOP) += nhc_ghc_ext_hop.o
+obj-$(CONFIG_6LOWPAN_GHC_UDP) += nhc_ghc_udp.o
+obj-$(CONFIG_6LOWPAN_GHC_ICMPV6) += nhc_ghc_icmpv6.o
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_DEST) += nhc_ghc_ext_dest.o
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG) += nhc_ghc_ext_frag.o
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE) += nhc_ghc_ext_route.o
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 83b19e072224..faf65baed617 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -15,19 +15,67 @@
#include <net/6lowpan.h>
-void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
+#include "6lowpan_i.h"
+
+int lowpan_register_netdevice(struct net_device *dev,
+ enum lowpan_lltypes lltype)
{
+ int ret;
+
dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
dev->mtu = IPV6_MIN_MTU;
dev->priv_flags |= IFF_NO_QUEUE;
lowpan_priv(dev)->lltype = lltype;
+
+ ret = register_netdevice(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = lowpan_dev_debugfs_init(dev);
+ if (ret < 0)
+ unregister_netdevice(dev);
+
+ return ret;
}
-EXPORT_SYMBOL(lowpan_netdev_setup);
+EXPORT_SYMBOL(lowpan_register_netdevice);
+
+int lowpan_register_netdev(struct net_device *dev,
+ enum lowpan_lltypes lltype)
+{
+ int ret;
+
+ rtnl_lock();
+ ret = lowpan_register_netdevice(dev, lltype);
+ rtnl_unlock();
+ return ret;
+}
+EXPORT_SYMBOL(lowpan_register_netdev);
+
+void lowpan_unregister_netdevice(struct net_device *dev)
+{
+ unregister_netdevice(dev);
+ lowpan_dev_debugfs_exit(dev);
+}
+EXPORT_SYMBOL(lowpan_unregister_netdevice);
+
+void lowpan_unregister_netdev(struct net_device *dev)
+{
+ rtnl_lock();
+ lowpan_unregister_netdevice(dev);
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(lowpan_unregister_netdev);
static int __init lowpan_module_init(void)
{
+ int ret;
+
+ ret = lowpan_debugfs_init();
+ if (ret < 0)
+ return ret;
+
request_module_nowait("ipv6");
request_module_nowait("nhc_dest");
@@ -40,6 +88,13 @@ static int __init lowpan_module_init(void)
return 0;
}
+
+static void __exit lowpan_module_exit(void)
+{
+ lowpan_debugfs_exit();
+}
+
module_init(lowpan_module_init);
+module_exit(lowpan_module_exit);
MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
new file mode 100644
index 000000000000..88eef84df0fc
--- /dev/null
+++ b/net/6lowpan/debugfs.c
@@ -0,0 +1,53 @@
+/* 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.
+ *
+ * Authors:
+ * (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
+ * Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
+ */
+
+#include <net/6lowpan.h>
+
+#include "6lowpan_i.h"
+
+static struct dentry *lowpan_debugfs;
+
+int lowpan_dev_debugfs_init(struct net_device *dev)
+{
+ struct lowpan_priv *lpriv = lowpan_priv(dev);
+
+ /* creating the root */
+ lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
+ if (!lpriv->iface_debugfs)
+ goto fail;
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+void lowpan_dev_debugfs_exit(struct net_device *dev)
+{
+ debugfs_remove_recursive(lowpan_priv(dev)->iface_debugfs);
+}
+
+int __init lowpan_debugfs_init(void)
+{
+ lowpan_debugfs = debugfs_create_dir("6lowpan", NULL);
+ if (!lowpan_debugfs)
+ return -EINVAL;
+
+ return 0;
+}
+
+void lowpan_debugfs_exit(void)
+{
+ debugfs_remove_recursive(lowpan_debugfs);
+}
diff --git a/net/6lowpan/nhc_ghc_ext_dest.c b/net/6lowpan/nhc_ghc_ext_dest.c
new file mode 100644
index 000000000000..9887b3a15348
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_dest.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_GHC_EXT_DEST_IDLEN 1
+#define LOWPAN_GHC_EXT_DEST_ID_0 0xb6
+#define LOWPAN_GHC_EXT_DEST_MASK_0 0xfe
+
+static void dest_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_DEST_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_DEST_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_dest, "RFC7400 Destination Extension Header", NEXTHDR_DEST,
+ 0, dest_ghid_setup, LOWPAN_GHC_EXT_DEST_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_dest);
+MODULE_DESCRIPTION("6LoWPAN generic header destination extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_ext_frag.c b/net/6lowpan/nhc_ghc_ext_frag.c
new file mode 100644
index 000000000000..1308b79e939d
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_frag.c
@@ -0,0 +1,28 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_GHC_EXT_FRAG_IDLEN 1
+#define LOWPAN_GHC_EXT_FRAG_ID_0 0xb4
+#define LOWPAN_GHC_EXT_FRAG_MASK_0 0xfe
+
+static void frag_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_FRAG_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_FRAG_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_frag, "RFC7400 Fragmentation Extension Header",
+ NEXTHDR_FRAGMENT, 0, frag_ghid_setup,
+ LOWPAN_GHC_EXT_FRAG_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_frag);
+MODULE_DESCRIPTION("6LoWPAN generic header fragmentation extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_ext_hop.c b/net/6lowpan/nhc_ghc_ext_hop.c
new file mode 100644
index 000000000000..baec86fd1974
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_hop.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_GHC_EXT_HOP_IDLEN 1
+#define LOWPAN_GHC_EXT_HOP_ID_0 0xb0
+#define LOWPAN_GHC_EXT_HOP_MASK_0 0xfe
+
+static void hop_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_HOP_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_HOP_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_hop, "RFC7400 Hop-by-Hop Extension Header", NEXTHDR_HOP, 0,
+ hop_ghid_setup, LOWPAN_GHC_EXT_HOP_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_hop);
+MODULE_DESCRIPTION("6LoWPAN generic header hop-by-hop extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_ext_route.c b/net/6lowpan/nhc_ghc_ext_route.c
new file mode 100644
index 000000000000..d7e5bd791c62
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_route.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_GHC_EXT_ROUTE_IDLEN 1
+#define LOWPAN_GHC_EXT_ROUTE_ID_0 0xb2
+#define LOWPAN_GHC_EXT_ROUTE_MASK_0 0xfe
+
+static void route_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_ROUTE_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_ROUTE_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_route, "RFC7400 Routing Extension Header", NEXTHDR_ROUTING,
+ 0, route_ghid_setup, LOWPAN_GHC_EXT_ROUTE_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_route);
+MODULE_DESCRIPTION("6LoWPAN generic header routing extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_icmpv6.c b/net/6lowpan/nhc_ghc_icmpv6.c
new file mode 100644
index 000000000000..32e7c2c66bbc
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_icmpv6.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN ICMPv6 compression according to RFC7400
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_GHC_ICMPV6_IDLEN 1
+#define LOWPAN_GHC_ICMPV6_ID_0 0xdf
+#define LOWPAN_GHC_ICMPV6_MASK_0 0xff
+
+static void icmpv6_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_ICMPV6_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_ICMPV6_MASK_0;
+}
+
+LOWPAN_NHC(ghc_icmpv6, "RFC7400 ICMPv6", NEXTHDR_ICMP, 0,
+ icmpv6_ghid_setup, LOWPAN_GHC_ICMPV6_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_icmpv6);
+MODULE_DESCRIPTION("6LoWPAN generic header ICMPv6 compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_udp.c b/net/6lowpan/nhc_ghc_udp.c
new file mode 100644
index 000000000000..17beefa52ca8
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_udp.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN UDP compression according to RFC7400
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_GHC_UDP_IDLEN 1
+#define LOWPAN_GHC_UDP_ID_0 0xd0
+#define LOWPAN_GHC_UDP_MASK_0 0xf8
+
+static void udp_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_UDP_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_UDP_MASK_0;
+}
+
+LOWPAN_NHC(ghc_udp, "RFC7400 UDP", NEXTHDR_UDP, 0,
+ udp_ghid_setup, LOWPAN_GHC_UDP_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_udp);
+MODULE_DESCRIPTION("6LoWPAN generic header UDP compression");
+MODULE_LICENSE("GPL");
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index fded86508117..ad5e2fd1012c 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -30,6 +30,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <net/arp.h>
+#include <net/switchdev.h>
#include "vlan.h"
#include "vlanproc.h"
@@ -542,9 +543,9 @@ static int vlan_dev_init(struct net_device *dev)
(1<<__LINK_STATE_DORMANT))) |
(1<<__LINK_STATE_PRESENT);
- dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG |
+ dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG |
NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |
- NETIF_F_HIGHDMA | NETIF_F_SCTP_CSUM |
+ NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |
NETIF_F_ALL_FCOE;
dev->features |= real_dev->vlan_features | NETIF_F_LLTX |
@@ -774,6 +775,12 @@ static const struct net_device_ops vlan_netdev_ops = {
.ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup,
#endif
.ndo_fix_features = vlan_dev_fix_features,
+ .ndo_fdb_add = switchdev_port_fdb_add,
+ .ndo_fdb_del = switchdev_port_fdb_del,
+ .ndo_fdb_dump = switchdev_port_fdb_dump,
+ .ndo_bridge_setlink = switchdev_port_bridge_setlink,
+ .ndo_bridge_getlink = switchdev_port_bridge_getlink,
+ .ndo_bridge_dellink = switchdev_port_bridge_dellink,
.ndo_get_lock_subclass = vlan_dev_get_lock_subclass,
.ndo_get_iflink = vlan_dev_get_iflink,
};
diff --git a/net/Kconfig b/net/Kconfig
index 127da94ae25e..174354618f8a 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -48,6 +48,9 @@ config COMPAT_NETLINK_MESSAGES
config NET_INGRESS
bool
+config NET_EGRESS
+ bool
+
menu "Networking options"
source "net/packet/Kconfig"
@@ -250,9 +253,14 @@ config XPS
depends on SMP
default y
+config SOCK_CGROUP_DATA
+ bool
+ default n
+
config CGROUP_NET_PRIO
bool "Network priority cgroup"
depends on CGROUPS
+ select SOCK_CGROUP_DATA
---help---
Cgroup subsystem for use in assigning processes to network priorities on
a per-interface basis.
@@ -260,6 +268,7 @@ config CGROUP_NET_PRIO
config CGROUP_NET_CLASSID
bool "Network classid cgroup"
depends on CGROUPS
+ select SOCK_CGROUP_DATA
---help---
Cgroup subsystem for use as general purpose socket classid marker that is
being used in cls_cgroup and for netfilter matching.
diff --git a/net/atm/common.c b/net/atm/common.c
index 49a872db7e42..6dc12305799e 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -96,7 +96,7 @@ static void vcc_def_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up(&wq->wait);
rcu_read_unlock();
}
@@ -117,7 +117,7 @@ static void vcc_write_space(struct sock *sk)
if (vcc_writable(sk)) {
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible(&wq->wait);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
diff --git a/net/atm/mpc.h b/net/atm/mpc.h
index 0919a88bbc70..cfc7b745aa91 100644
--- a/net/atm/mpc.h
+++ b/net/atm/mpc.h
@@ -21,11 +21,11 @@ struct mpoa_client {
uint8_t our_ctrl_addr[ATM_ESA_LEN]; /* MPC's control ATM address */
rwlock_t ingress_lock;
- struct in_cache_ops *in_ops; /* ingress cache operations */
+ const struct in_cache_ops *in_ops; /* ingress cache operations */
in_cache_entry *in_cache; /* the ingress cache of this MPC */
rwlock_t egress_lock;
- struct eg_cache_ops *eg_ops; /* egress cache operations */
+ const struct eg_cache_ops *eg_ops; /* egress cache operations */
eg_cache_entry *eg_cache; /* the egress cache of this MPC */
uint8_t *mps_macs; /* array of MPS MAC addresses, >=1 */
diff --git a/net/atm/mpoa_caches.c b/net/atm/mpoa_caches.c
index d1b2d9a03144..9e60e74c807d 100644
--- a/net/atm/mpoa_caches.c
+++ b/net/atm/mpoa_caches.c
@@ -534,7 +534,7 @@ static void eg_destroy_cache(struct mpoa_client *mpc)
}
-static struct in_cache_ops ingress_ops = {
+static const struct in_cache_ops ingress_ops = {
in_cache_add_entry, /* add_entry */
in_cache_get, /* get */
in_cache_get_with_mask, /* get_with_mask */
@@ -548,7 +548,7 @@ static struct in_cache_ops ingress_ops = {
in_destroy_cache /* destroy_cache */
};
-static struct eg_cache_ops egress_ops = {
+static const struct eg_cache_ops egress_ops = {
eg_cache_add_entry, /* add_entry */
eg_cache_get_by_cache_id, /* get_by_cache_id */
eg_cache_get_by_tag, /* get_by_tag */
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 912d9c36fb1c..df625de55ef2 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -185,7 +185,8 @@ unlock:
static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
int max_if_num, int del_if_num)
{
- int chunk_size, ret = -ENOMEM, if_offset;
+ int ret = -ENOMEM;
+ size_t chunk_size, if_offset;
void *data_ptr = NULL;
spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
@@ -203,8 +204,9 @@ static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
memcpy(data_ptr, orig_node->bat_iv.bcast_own, del_if_num * chunk_size);
/* copy second part */
+ if_offset = (del_if_num + 1) * chunk_size;
memcpy((char *)data_ptr + del_if_num * chunk_size,
- orig_node->bat_iv.bcast_own + ((del_if_num + 1) * chunk_size),
+ (uint8_t *)orig_node->bat_iv.bcast_own + if_offset,
(max_if_num - del_if_num) * chunk_size);
free_bcast_own:
@@ -361,7 +363,6 @@ batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface)
unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
- batadv_ogm_packet->flags = BATADV_PRIMARIES_FIRST_HOP;
batadv_ogm_packet->ttl = BATADV_TTL;
}
@@ -842,8 +843,6 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
"Forwarding packet: tq: %i, ttl: %i\n",
batadv_ogm_packet->tq, batadv_ogm_packet->ttl);
- /* switch of primaries first hop flag when forwarding */
- batadv_ogm_packet->flags &= ~BATADV_PRIMARIES_FIRST_HOP;
if (is_single_hop_neigh)
batadv_ogm_packet->flags |= BATADV_DIRECTLINK;
else
@@ -1379,6 +1378,7 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
struct batadv_neigh_node *router = NULL;
struct batadv_neigh_node *router_router = NULL;
struct batadv_orig_node *orig_neigh_node;
@@ -1423,6 +1423,13 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
goto out;
}
+ if (is_single_hop_neigh) {
+ hardif_neigh = batadv_hardif_neigh_get(if_incoming,
+ ethhdr->h_source);
+ if (hardif_neigh)
+ hardif_neigh->last_seen = jiffies;
+ }
+
router = batadv_orig_router_get(orig_node, if_outgoing);
if (router) {
router_router = batadv_orig_router_get(router->orig_node,
@@ -1557,6 +1564,8 @@ out:
batadv_neigh_node_free_ref(router_router);
if (orig_neigh_router)
batadv_neigh_node_free_ref(orig_neigh_router);
+ if (hardif_neigh)
+ batadv_hardif_neigh_free_ref(hardif_neigh);
kfree_skb(skb_priv);
}
@@ -1862,6 +1871,58 @@ next:
}
/**
+ * batadv_iv_hardif_neigh_print - print a single hop neighbour node
+ * @seq: neighbour table seq_file struct
+ * @hardif_neigh: hardif neighbour information
+ */
+static void
+batadv_iv_hardif_neigh_print(struct seq_file *seq,
+ struct batadv_hardif_neigh_node *hardif_neigh)
+{
+ int last_secs, last_msecs;
+
+ last_secs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) / 1000;
+ last_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) % 1000;
+
+ seq_printf(seq, " %10s %pM %4i.%03is\n",
+ hardif_neigh->if_incoming->net_dev->name,
+ hardif_neigh->addr, last_secs, last_msecs);
+}
+
+/**
+ * batadv_iv_ogm_neigh_print - print the single hop neighbour list
+ * @bat_priv: the bat priv with all the soft interface information
+ * @seq: neighbour table seq_file struct
+ */
+static void batadv_iv_neigh_print(struct batadv_priv *bat_priv,
+ struct seq_file *seq)
+{
+ struct net_device *net_dev = (struct net_device *)seq->private;
+ struct batadv_hardif_neigh_node *hardif_neigh;
+ struct batadv_hard_iface *hard_iface;
+ int batman_count = 0;
+
+ seq_printf(seq, " %10s %-13s %s\n",
+ "IF", "Neighbor", "last-seen");
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
+ if (hard_iface->soft_iface != net_dev)
+ continue;
+
+ hlist_for_each_entry_rcu(hardif_neigh,
+ &hard_iface->neigh_list, list) {
+ batadv_iv_hardif_neigh_print(seq, hardif_neigh);
+ batman_count++;
+ }
+ }
+ rcu_read_unlock();
+
+ if (batman_count == 0)
+ seq_puts(seq, "No batman nodes in range ...\n");
+}
+
+/**
* batadv_iv_ogm_neigh_cmp - compare the metrics of two neighbors
* @neigh1: the first neighbor object of the comparison
* @if_outgoing1: outgoing interface for the first neighbor
@@ -1902,8 +1963,8 @@ out:
}
/**
- * batadv_iv_ogm_neigh_is_eob - check if neigh1 is equally good or better than
- * neigh2 from the metric prospective
+ * batadv_iv_ogm_neigh_is_sob - check if neigh1 is similarly good or better
+ * than neigh2 from the metric prospective
* @neigh1: the first neighbor object of the comparison
* @if_outgoing1: outgoing interface for the first neighbor
* @neigh2: the second neighbor object of the comparison
@@ -1913,7 +1974,7 @@ out:
* the metric via neigh2, false otherwise.
*/
static bool
-batadv_iv_ogm_neigh_is_eob(struct batadv_neigh_node *neigh1,
+batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2)
@@ -1953,7 +2014,8 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.bat_ogm_schedule = batadv_iv_ogm_schedule,
.bat_ogm_emit = batadv_iv_ogm_emit,
.bat_neigh_cmp = batadv_iv_ogm_neigh_cmp,
- .bat_neigh_is_equiv_or_better = batadv_iv_ogm_neigh_is_eob,
+ .bat_neigh_is_similar_or_better = batadv_iv_ogm_neigh_is_sob,
+ .bat_neigh_print = batadv_iv_neigh_print,
.bat_orig_print = batadv_iv_ogm_orig_print,
.bat_orig_free = batadv_iv_ogm_orig_free,
.bat_orig_add_if = batadv_iv_ogm_orig_add_if,
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 191a70290dca..d5d71ac96c8a 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -260,7 +260,9 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
}
/* all claims gone, initialize CRC */
+ spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc = BATADV_BLA_CRC_INIT;
+ spin_unlock_bh(&backbone_gw->crc_lock);
}
/**
@@ -408,6 +410,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
entry->lasttime = jiffies;
entry->crc = BATADV_BLA_CRC_INIT;
entry->bat_priv = bat_priv;
+ spin_lock_init(&entry->crc_lock);
atomic_set(&entry->request_sent, 0);
atomic_set(&entry->wait_periods, 0);
ether_addr_copy(entry->orig, orig);
@@ -557,7 +560,9 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv,
__be16 crc;
memcpy(mac, batadv_announce_mac, 4);
+ spin_lock_bh(&backbone_gw->crc_lock);
crc = htons(backbone_gw->crc);
+ spin_unlock_bh(&backbone_gw->crc_lock);
memcpy(&mac[4], &crc, 2);
batadv_bla_send_claim(bat_priv, mac, backbone_gw->vid,
@@ -618,14 +623,18 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
"bla_add_claim(): changing ownership for %pM, vid %d\n",
mac, BATADV_PRINT_VID(vid));
+ spin_lock_bh(&claim->backbone_gw->crc_lock);
claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ spin_unlock_bh(&claim->backbone_gw->crc_lock);
batadv_backbone_gw_free_ref(claim->backbone_gw);
}
/* set (new) backbone gw */
atomic_inc(&backbone_gw->refcount);
claim->backbone_gw = backbone_gw;
+ spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ spin_unlock_bh(&backbone_gw->crc_lock);
backbone_gw->lasttime = jiffies;
claim_free_ref:
@@ -653,7 +662,9 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
batadv_choose_claim, claim);
batadv_claim_free_ref(claim); /* reference from the hash is gone */
+ spin_lock_bh(&claim->backbone_gw->crc_lock);
claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ spin_unlock_bh(&claim->backbone_gw->crc_lock);
/* don't need the reference from hash_find() anymore */
batadv_claim_free_ref(claim);
@@ -664,7 +675,7 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
u8 *backbone_addr, unsigned short vid)
{
struct batadv_bla_backbone_gw *backbone_gw;
- u16 crc;
+ u16 backbone_crc, crc;
if (memcmp(an_addr, batadv_announce_mac, 4) != 0)
return 0;
@@ -683,12 +694,16 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
"handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %#.4x\n",
BATADV_PRINT_VID(vid), backbone_gw->orig, crc);
- if (backbone_gw->crc != crc) {
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_crc = backbone_gw->crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
+
+ if (backbone_crc != crc) {
batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
"handle_announce(): CRC FAILED for %pM/%d (my = %#.4x, sent = %#.4x)\n",
backbone_gw->orig,
BATADV_PRINT_VID(backbone_gw->vid),
- backbone_gw->crc, crc);
+ backbone_crc, crc);
batadv_bla_send_request(backbone_gw);
} else {
@@ -1153,6 +1168,26 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
}
}
+/**
+ * batadv_bla_status_update - purge bla interfaces if necessary
+ * @net_dev: the soft interface net device
+ */
+void batadv_bla_status_update(struct net_device *net_dev)
+{
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ struct batadv_hard_iface *primary_if;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ return;
+
+ /* this function already purges everything when bla is disabled,
+ * so just call that one.
+ */
+ batadv_bla_update_orig_address(bat_priv, primary_if, primary_if);
+ batadv_hardif_free_ref(primary_if);
+}
+
/* periodic work to do:
* * purge structures when they are too old
* * send announcements
@@ -1647,6 +1682,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_bla_claim *claim;
struct batadv_hard_iface *primary_if;
struct hlist_head *head;
+ u16 backbone_crc;
u32 i;
bool is_own;
u8 *primary_addr;
@@ -1669,11 +1705,15 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
hlist_for_each_entry_rcu(claim, head, hash_entry) {
is_own = batadv_compare_eth(claim->backbone_gw->orig,
primary_addr);
+
+ spin_lock_bh(&claim->backbone_gw->crc_lock);
+ backbone_crc = claim->backbone_gw->crc;
+ spin_unlock_bh(&claim->backbone_gw->crc_lock);
seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n",
claim->addr, BATADV_PRINT_VID(claim->vid),
claim->backbone_gw->orig,
(is_own ? 'x' : ' '),
- claim->backbone_gw->crc);
+ backbone_crc);
}
rcu_read_unlock();
}
@@ -1692,6 +1732,7 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_hard_iface *primary_if;
struct hlist_head *head;
int secs, msecs;
+ u16 backbone_crc;
u32 i;
bool is_own;
u8 *primary_addr;
@@ -1722,10 +1763,14 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
if (is_own)
continue;
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_crc = backbone_gw->crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
+
seq_printf(seq, " * %pM on %5d %4i.%03is (%#.4x)\n",
backbone_gw->orig,
BATADV_PRINT_VID(backbone_gw->vid), secs,
- msecs, backbone_gw->crc);
+ msecs, backbone_crc);
}
rcu_read_unlock();
}
diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h
index 025152b34282..7ea199b8b5ab 100644
--- a/net/batman-adv/bridge_loop_avoidance.h
+++ b/net/batman-adv/bridge_loop_avoidance.h
@@ -22,6 +22,7 @@
#include <linux/types.h>
+struct net_device;
struct seq_file;
struct sk_buff;
@@ -42,6 +43,7 @@ int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
struct batadv_hard_iface *oldif);
+void batadv_bla_status_update(struct net_device *net_dev);
int batadv_bla_init(struct batadv_priv *bat_priv);
void batadv_bla_free(struct batadv_priv *bat_priv);
diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c
index c4c1e8030ba0..037ad0a5f485 100644
--- a/net/batman-adv/debugfs.c
+++ b/net/batman-adv/debugfs.c
@@ -262,6 +262,13 @@ static int batadv_algorithms_open(struct inode *inode, struct file *file)
return single_open(file, batadv_algo_seq_print_text, NULL);
}
+static int neighbors_open(struct inode *inode, struct file *file)
+{
+ struct net_device *net_dev = (struct net_device *)inode->i_private;
+
+ return single_open(file, batadv_hardif_neigh_seq_print_text, net_dev);
+}
+
static int batadv_originators_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
@@ -375,6 +382,7 @@ static struct batadv_debuginfo *batadv_general_debuginfos[] = {
};
/* The following attributes are per soft interface */
+static BATADV_DEBUGINFO(neighbors, S_IRUGO, neighbors_open);
static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open);
static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open);
static BATADV_DEBUGINFO(transtable_global, S_IRUGO,
@@ -394,6 +402,7 @@ static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
#endif
static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
+ &batadv_debuginfo_neighbors,
&batadv_debuginfo_originators,
&batadv_debuginfo_gateways,
&batadv_debuginfo_transtable_global,
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 700c96c82a15..20d9282f895b 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -71,14 +71,14 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
chain = &orig_node->fragments[i];
- spin_lock_bh(&orig_node->fragments[i].lock);
+ spin_lock_bh(&chain->lock);
if (!check_cb || check_cb(chain)) {
- batadv_frag_clear_chain(&orig_node->fragments[i].head);
- orig_node->fragments[i].size = 0;
+ batadv_frag_clear_chain(&chain->head);
+ chain->size = 0;
}
- spin_unlock_bh(&orig_node->fragments[i].lock);
+ spin_unlock_bh(&chain->lock);
}
}
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c
index 0cb5e6b6f6d4..b51bface8bdd 100644
--- a/net/batman-adv/gateway_common.c
+++ b/net/batman-adv/gateway_common.c
@@ -31,27 +31,23 @@
#include "packet.h"
/**
- * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
- * and upload bandwidth information
+ * batadv_parse_throughput - parse supplied string buffer to extract throughput
+ * information
* @net_dev: the soft interface net device
* @buff: string buffer to parse
- * @down: pointer holding the returned download bandwidth information
- * @up: pointer holding the returned upload bandwidth information
+ * @description: text shown when throughput string cannot be parsed
+ * @throughput: pointer holding the returned throughput information
*
* Returns false on parse error and true otherwise.
*/
-static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
- u32 *down, u32 *up)
+static bool batadv_parse_throughput(struct net_device *net_dev, char *buff,
+ const char *description, u32 *throughput)
{
enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
- char *slash_ptr, *tmp_ptr;
- u64 ldown, lup;
+ u64 lthroughput;
+ char *tmp_ptr;
int ret;
- slash_ptr = strchr(buff, '/');
- if (slash_ptr)
- *slash_ptr = 0;
-
if (strlen(buff) > 4) {
tmp_ptr = buff + strlen(buff) - 4;
@@ -63,90 +59,75 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
*tmp_ptr = '\0';
}
- ret = kstrtou64(buff, 10, &ldown);
+ ret = kstrtou64(buff, 10, &lthroughput);
if (ret) {
batadv_err(net_dev,
- "Download speed of gateway mode invalid: %s\n",
- buff);
+ "Invalid throughput speed for %s: %s\n",
+ description, buff);
return false;
}
switch (bw_unit_type) {
case BATADV_BW_UNIT_MBIT:
/* prevent overflow */
- if (U64_MAX / 10 < ldown) {
+ if (U64_MAX / 10 < lthroughput) {
batadv_err(net_dev,
- "Download speed of gateway mode too large: %s\n",
- buff);
+ "Throughput speed for %s too large: %s\n",
+ description, buff);
return false;
}
- ldown *= 10;
+ lthroughput *= 10;
break;
case BATADV_BW_UNIT_KBIT:
default:
- ldown = div_u64(ldown, 100);
+ lthroughput = div_u64(lthroughput, 100);
break;
}
- if (U32_MAX < ldown) {
+ if (lthroughput > U32_MAX) {
batadv_err(net_dev,
- "Download speed of gateway mode too large: %s\n",
- buff);
+ "Throughput speed for %s too large: %s\n",
+ description, buff);
return false;
}
- *down = ldown;
-
- /* we also got some upload info */
- if (slash_ptr) {
- bw_unit_type = BATADV_BW_UNIT_KBIT;
-
- if (strlen(slash_ptr + 1) > 4) {
- tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1);
+ *throughput = lthroughput;
- if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
- bw_unit_type = BATADV_BW_UNIT_MBIT;
+ return true;
+}
- if ((strncasecmp(tmp_ptr, "kbit", 4) == 0) ||
- (bw_unit_type == BATADV_BW_UNIT_MBIT))
- *tmp_ptr = '\0';
- }
+/**
+ * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
+ * and upload bandwidth information
+ * @net_dev: the soft interface net device
+ * @buff: string buffer to parse
+ * @down: pointer holding the returned download bandwidth information
+ * @up: pointer holding the returned upload bandwidth information
+ *
+ * Return: false on parse error and true otherwise.
+ */
+static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
+ u32 *down, u32 *up)
+{
+ char *slash_ptr;
+ bool ret;
- ret = kstrtou64(slash_ptr + 1, 10, &lup);
- if (ret) {
- batadv_err(net_dev,
- "Upload speed of gateway mode invalid: %s\n",
- slash_ptr + 1);
- return false;
- }
+ slash_ptr = strchr(buff, '/');
+ if (slash_ptr)
+ *slash_ptr = 0;
- switch (bw_unit_type) {
- case BATADV_BW_UNIT_MBIT:
- /* prevent overflow */
- if (U64_MAX / 10 < lup) {
- batadv_err(net_dev,
- "Upload speed of gateway mode too large: %s\n",
- slash_ptr + 1);
- return false;
- }
-
- lup *= 10;
- break;
- case BATADV_BW_UNIT_KBIT:
- default:
- lup = div_u64(lup, 100);
- break;
- }
+ ret = batadv_parse_throughput(net_dev, buff, "download gateway speed",
+ down);
+ if (!ret)
+ return false;
- if (U32_MAX < lup) {
- batadv_err(net_dev,
- "Upload speed of gateway mode too large: %s\n",
- slash_ptr + 1);
+ /* we also got some upload info */
+ if (slash_ptr) {
+ ret = batadv_parse_throughput(net_dev, slash_ptr + 1,
+ "upload gateway speed", up);
+ if (!ret)
return false;
- }
-
- *up = lup;
}
return true;
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index f11345e163d7..01acccc4d218 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -32,6 +32,7 @@
#include <linux/rculist.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <net/net_namespace.h>
@@ -464,7 +465,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
hard_iface->soft_iface = soft_iface;
bat_priv = netdev_priv(hard_iface->soft_iface);
- ret = netdev_master_upper_dev_link(hard_iface->net_dev, soft_iface);
+ ret = netdev_master_upper_dev_link(hard_iface->net_dev,
+ soft_iface, NULL, NULL);
if (ret)
goto err_dev;
@@ -638,9 +640,12 @@ batadv_hardif_add_interface(struct net_device *net_dev)
goto free_sysfs;
INIT_LIST_HEAD(&hard_iface->list);
+ INIT_HLIST_HEAD(&hard_iface->neigh_list);
INIT_WORK(&hard_iface->cleanup_work,
batadv_hardif_remove_interface_finish);
+ spin_lock_init(&hard_iface->neigh_list_lock);
+
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
if (batadv_is_wifi_netdev(net_dev))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
@@ -708,7 +713,8 @@ static int batadv_hard_if_event(struct notifier_block *this,
}
hard_iface = batadv_hardif_get_by_netdev(net_dev);
- if (!hard_iface && event == NETDEV_REGISTER)
+ if (!hard_iface && (event == NETDEV_REGISTER ||
+ event == NETDEV_POST_TYPE_CHANGE))
hard_iface = batadv_hardif_add_interface(net_dev);
if (!hard_iface)
@@ -723,6 +729,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_hardif_deactivate_interface(hard_iface);
break;
case NETDEV_UNREGISTER:
+ case NETDEV_PRE_TYPE_CHANGE:
list_del_rcu(&hard_iface->list);
batadv_hardif_remove_interface(hard_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index d7f17c1aa4a4..4b5d61fbadb1 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -552,7 +552,7 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
!bat_algo_ops->bat_ogm_schedule ||
!bat_algo_ops->bat_ogm_emit ||
!bat_algo_ops->bat_neigh_cmp ||
- !bat_algo_ops->bat_neigh_is_equiv_or_better) {
+ !bat_algo_ops->bat_neigh_is_similar_or_better) {
pr_info("Routing algo '%s' does not implement required ops\n",
bat_algo_ops->name);
return -EINVAL;
@@ -747,7 +747,7 @@ static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
static void batadv_tvlv_container_remove(struct batadv_priv *bat_priv,
struct batadv_tvlv_container *tvlv)
{
- lockdep_assert_held(&bat_priv->tvlv.handler_list_lock);
+ lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
if (!tvlv)
return;
@@ -908,7 +908,7 @@ end:
* appropriate handlers
* @bat_priv: the bat priv with all the soft interface information
* @tvlv_handler: tvlv callback function handling the tvlv content
- * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @ogm_source: flag indicating whether the tvlv is an ogm or a unicast packet
* @orig_node: orig node emitting the ogm packet
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
@@ -961,7 +961,7 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
* batadv_tvlv_containers_process - parse the given tvlv buffer to call the
* appropriate handlers
* @bat_priv: the bat priv with all the soft interface information
- * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @ogm_source: flag indicating whether the tvlv is an ogm or a unicast packet
* @orig_node: orig node emitting the ogm packet
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
@@ -1143,15 +1143,14 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src,
struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
struct batadv_tvlv_hdr *tvlv_hdr;
struct batadv_orig_node *orig_node;
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb;
unsigned char *tvlv_buff;
unsigned int tvlv_len;
ssize_t hdr_len = sizeof(*unicast_tvlv_packet);
- bool ret = false;
orig_node = batadv_orig_hash_find(bat_priv, dst);
if (!orig_node)
- goto out;
+ return;
tvlv_len = sizeof(*tvlv_hdr) + tvlv_value_len;
@@ -1180,14 +1179,10 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src,
tvlv_buff += sizeof(*tvlv_hdr);
memcpy(tvlv_buff, tvlv_value, tvlv_value_len);
- if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
- ret = true;
-
-out:
- if (skb && !ret)
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP)
kfree_skb(skb);
- if (orig_node)
- batadv_orig_node_free_ref(orig_node);
+out:
+ batadv_orig_node_free_ref(orig_node);
}
/**
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index ebd8af0a1eb0..9dbd9107e7e1 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -24,7 +24,7 @@
#define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2015.2"
+#define BATADV_SOURCE_VERSION "2016.0"
#endif
/* B.A.T.M.A.N. parameters */
@@ -109,7 +109,7 @@
#define BATADV_MAX_AGGREGATION_MS 100
#define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */
-#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 3)
+#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 6)
#define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10)
#define BATADV_BLA_WAIT_PERIODS 3
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
index f5276be2c77c..c98b0ab85449 100644
--- a/net/batman-adv/network-coding.c
+++ b/net/batman-adv/network-coding.c
@@ -244,9 +244,7 @@ static void batadv_nc_path_free_ref(struct batadv_nc_path *nc_path)
*/
static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet)
{
- if (nc_packet->skb)
- kfree_skb(nc_packet->skb);
-
+ kfree_skb(nc_packet->skb);
batadv_nc_path_free_ref(nc_packet->nc_path);
kfree(nc_packet);
}
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 7486df9ed48d..3c782a33bdac 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -202,6 +202,47 @@ void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo)
}
/**
+ * batadv_hardif_neigh_free_rcu - free the hardif neigh_node
+ * @rcu: rcu pointer of the neigh_node
+ */
+static void batadv_hardif_neigh_free_rcu(struct rcu_head *rcu)
+{
+ struct batadv_hardif_neigh_node *hardif_neigh;
+
+ hardif_neigh = container_of(rcu, struct batadv_hardif_neigh_node, rcu);
+
+ spin_lock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
+ hlist_del_init_rcu(&hardif_neigh->list);
+ spin_unlock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
+
+ batadv_hardif_free_ref_now(hardif_neigh->if_incoming);
+ kfree(hardif_neigh);
+}
+
+/**
+ * batadv_hardif_neigh_free_now - decrement the hardif neighbors refcounter
+ * and possibly free it (without rcu callback)
+ * @hardif_neigh: hardif neigh neighbor to free
+ */
+static void
+batadv_hardif_neigh_free_now(struct batadv_hardif_neigh_node *hardif_neigh)
+{
+ if (atomic_dec_and_test(&hardif_neigh->refcount))
+ batadv_hardif_neigh_free_rcu(&hardif_neigh->rcu);
+}
+
+/**
+ * batadv_hardif_neigh_free_ref - decrement the hardif neighbors refcounter
+ * and possibly free it
+ * @hardif_neigh: hardif neigh neighbor to free
+ */
+void batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh)
+{
+ if (atomic_dec_and_test(&hardif_neigh->refcount))
+ call_rcu(&hardif_neigh->rcu, batadv_hardif_neigh_free_rcu);
+}
+
+/**
* batadv_neigh_node_free_rcu - free the neigh_node
* @rcu: rcu pointer of the neigh_node
*/
@@ -209,6 +250,7 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu)
{
struct hlist_node *node_tmp;
struct batadv_neigh_node *neigh_node;
+ struct batadv_hardif_neigh_node *hardif_neigh;
struct batadv_neigh_ifinfo *neigh_ifinfo;
struct batadv_algo_ops *bao;
@@ -220,6 +262,14 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu)
batadv_neigh_ifinfo_free_ref_now(neigh_ifinfo);
}
+ hardif_neigh = batadv_hardif_neigh_get(neigh_node->if_incoming,
+ neigh_node->addr);
+ if (hardif_neigh) {
+ /* batadv_hardif_neigh_get() increases refcount too */
+ batadv_hardif_neigh_free_now(hardif_neigh);
+ batadv_hardif_neigh_free_now(hardif_neigh);
+ }
+
if (bao->bat_neigh_free)
bao->bat_neigh_free(neigh_node);
@@ -479,6 +529,106 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
}
/**
+ * batadv_hardif_neigh_create - create a hardif neighbour node
+ * @hard_iface: the interface this neighbour is connected to
+ * @neigh_addr: the interface address of the neighbour to retrieve
+ *
+ * Returns the hardif neighbour node if found or created or NULL otherwise.
+ */
+static struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr)
+{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
+
+ spin_lock_bh(&hard_iface->neigh_list_lock);
+
+ /* check if neighbor hasn't been added in the meantime */
+ hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);
+ if (hardif_neigh)
+ goto out;
+
+ if (!atomic_inc_not_zero(&hard_iface->refcount))
+ goto out;
+
+ hardif_neigh = kzalloc(sizeof(*hardif_neigh), GFP_ATOMIC);
+ if (!hardif_neigh) {
+ batadv_hardif_free_ref(hard_iface);
+ goto out;
+ }
+
+ INIT_HLIST_NODE(&hardif_neigh->list);
+ ether_addr_copy(hardif_neigh->addr, neigh_addr);
+ hardif_neigh->if_incoming = hard_iface;
+ hardif_neigh->last_seen = jiffies;
+
+ atomic_set(&hardif_neigh->refcount, 1);
+
+ if (bat_priv->bat_algo_ops->bat_hardif_neigh_init)
+ bat_priv->bat_algo_ops->bat_hardif_neigh_init(hardif_neigh);
+
+ hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
+
+out:
+ spin_unlock_bh(&hard_iface->neigh_list_lock);
+ return hardif_neigh;
+}
+
+/**
+ * batadv_hardif_neigh_get_or_create - retrieve or create a hardif neighbour
+ * node
+ * @hard_iface: the interface this neighbour is connected to
+ * @neigh_addr: the interface address of the neighbour to retrieve
+ *
+ * Returns the hardif neighbour node if found or created or NULL otherwise.
+ */
+static struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr)
+{
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
+
+ /* first check without locking to avoid the overhead */
+ hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);
+ if (hardif_neigh)
+ return hardif_neigh;
+
+ return batadv_hardif_neigh_create(hard_iface, neigh_addr);
+}
+
+/**
+ * batadv_hardif_neigh_get - retrieve a hardif neighbour from the list
+ * @hard_iface: the interface where this neighbour is connected to
+ * @neigh_addr: the address of the neighbour
+ *
+ * Looks for and possibly returns a neighbour belonging to this hard interface.
+ * Returns NULL if the neighbour is not found.
+ */
+struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr)
+{
+ struct batadv_hardif_neigh_node *tmp_hardif_neigh, *hardif_neigh = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tmp_hardif_neigh,
+ &hard_iface->neigh_list, list) {
+ if (!batadv_compare_eth(tmp_hardif_neigh->addr, neigh_addr))
+ continue;
+
+ if (!atomic_inc_not_zero(&tmp_hardif_neigh->refcount))
+ continue;
+
+ hardif_neigh = tmp_hardif_neigh;
+ break;
+ }
+ rcu_read_unlock();
+
+ return hardif_neigh;
+}
+
+/**
* batadv_neigh_node_new - create and init a new neigh_node object
* @orig_node: originator object representing the neighbour
* @hard_iface: the interface where the neighbour is connected to
@@ -493,11 +643,17 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
const u8 *neigh_addr)
{
struct batadv_neigh_node *neigh_node;
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr);
if (neigh_node)
goto out;
+ hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface,
+ neigh_addr);
+ if (!hardif_neigh)
+ goto out;
+
neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC);
if (!neigh_node)
goto out;
@@ -523,15 +679,54 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
spin_unlock_bh(&orig_node->neigh_list_lock);
+ /* increment unique neighbor refcount */
+ atomic_inc(&hardif_neigh->refcount);
+
batadv_dbg(BATADV_DBG_BATMAN, orig_node->bat_priv,
"Creating new neighbor %pM for orig_node %pM on interface %s\n",
neigh_addr, orig_node->orig, hard_iface->net_dev->name);
out:
+ if (hardif_neigh)
+ batadv_hardif_neigh_free_ref(hardif_neigh);
return neigh_node;
}
/**
+ * batadv_hardif_neigh_seq_print_text - print the single hop neighbour list
+ * @seq: neighbour table seq_file struct
+ * @offset: not used
+ *
+ * Always returns 0.
+ */
+int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset)
+{
+ struct net_device *net_dev = (struct net_device *)seq->private;
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ struct batadv_hard_iface *primary_if;
+
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
+ return 0;
+
+ seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n",
+ BATADV_SOURCE_VERSION, primary_if->net_dev->name,
+ primary_if->net_dev->dev_addr, net_dev->name,
+ bat_priv->bat_algo_ops->name);
+
+ batadv_hardif_free_ref(primary_if);
+
+ if (!bat_priv->bat_algo_ops->bat_neigh_print) {
+ seq_puts(seq,
+ "No printing function for this routing protocol\n");
+ return 0;
+ }
+
+ bat_priv->bat_algo_ops->bat_neigh_print(bat_priv, seq);
+ return 0;
+}
+
+/**
* batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object
* @rcu: rcu pointer of the orig_ifinfo object
*/
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h
index fa18f9bf266b..29557753d552 100644
--- a/net/batman-adv/originator.h
+++ b/net/batman-adv/originator.h
@@ -41,6 +41,11 @@ void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const u8 *addr);
+struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr);
+void
+batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh);
struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *hard_iface,
@@ -57,6 +62,8 @@ batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
struct batadv_hard_iface *if_outgoing);
void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo);
+int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset);
+
struct batadv_orig_ifinfo *
batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_outgoing);
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 11f996b39fef..0558e3237e0e 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -72,8 +72,7 @@ enum batadv_subtype {
* enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets
* @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was
* previously received from someone else than the best neighbor.
- * @BATADV_PRIMARIES_FIRST_HOP: flag is set when the primary interface address
- * is used, and the packet travels its first hop.
+ * @BATADV_PRIMARIES_FIRST_HOP: flag unused.
* @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a
* one hop neighbor on the interface where it was originally received.
*/
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 3207667e69de..e4f2646d9246 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -497,9 +497,9 @@ batadv_find_router(struct batadv_priv *bat_priv,
/* alternative candidate should be good enough to be
* considered
*/
- if (!bao->bat_neigh_is_equiv_or_better(cand_router,
- cand->if_outgoing,
- router, recv_if))
+ if (!bao->bat_neigh_is_similar_or_better(cand_router,
+ cand->if_outgoing,
+ router, recv_if))
goto next;
/* don't use the same router twice */
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index f664324805eb..782fa33ec296 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -407,8 +407,7 @@ void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
static void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet)
{
- if (forw_packet->skb)
- kfree_skb(forw_packet->skb);
+ kfree_skb(forw_packet->skb);
if (forw_packet->if_incoming)
batadv_hardif_free_ref(forw_packet->if_incoming);
if (forw_packet->if_outgoing)
diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
index 9de3c8804ff4..fe87777fda8a 100644
--- a/net/batman-adv/sysfs.c
+++ b/net/batman-adv/sysfs.c
@@ -40,6 +40,7 @@
#include "distributed-arp-table.h"
#include "gateway_client.h"
#include "gateway_common.h"
+#include "bridge_loop_avoidance.h"
#include "hard-interface.h"
#include "network-coding.h"
#include "packet.h"
@@ -241,10 +242,13 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \
static int batadv_store_bool_attr(char *buff, size_t count,
struct net_device *net_dev,
- const char *attr_name, atomic_t *attr)
+ const char *attr_name, atomic_t *attr,
+ bool *changed)
{
int enabled = -1;
+ *changed = false;
+
if (buff[count - 1] == '\n')
buff[count - 1] = '\0';
@@ -271,6 +275,8 @@ static int batadv_store_bool_attr(char *buff, size_t count,
atomic_read(attr) == 1 ? "enabled" : "disabled",
enabled == 1 ? "enabled" : "disabled");
+ *changed = true;
+
atomic_set(attr, (unsigned int)enabled);
return count;
}
@@ -281,11 +287,12 @@ __batadv_store_bool_attr(char *buff, size_t count,
struct attribute *attr,
atomic_t *attr_store, struct net_device *net_dev)
{
+ bool changed;
int ret;
ret = batadv_store_bool_attr(buff, count, net_dev, attr->name,
- attr_store);
- if (post_func && ret)
+ attr_store, &changed);
+ if (post_func && changed)
post_func(net_dev);
return ret;
@@ -549,7 +556,8 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
BATADV_ATTR_SIF_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL);
BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
#ifdef CONFIG_BATMAN_ADV_BLA
-BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL);
+BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR,
+ batadv_bla_status_update);
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR,
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 76f19ba62462..a22080c53401 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -1443,7 +1443,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
* TT_CLIENT_WIFI, therefore they have to be copied in the
* client entry
*/
- tt_global_entry->common.flags |= flags;
+ common->flags |= flags;
/* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only
* one originator left in the list and we previously received a
@@ -2419,8 +2419,8 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
{
struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp;
struct batadv_orig_node_vlan *vlan;
+ int i, orig_num_vlan;
u32 crc;
- int i;
/* check if each received CRC matches the locally stored one */
for (i = 0; i < num_vlan; i++) {
@@ -2446,6 +2446,18 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
return false;
}
+ /* check if any excess VLANs exist locally for the originator
+ * which are not mentioned in the TVLV from the originator.
+ */
+ rcu_read_lock();
+ orig_num_vlan = 0;
+ hlist_for_each_entry_rcu(vlan, &orig_node->vlan_list, list)
+ orig_num_vlan++;
+ rcu_read_unlock();
+
+ if (orig_num_vlan > num_vlan)
+ return false;
+
return true;
}
@@ -3327,7 +3339,10 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
bool ret = false;
vlan = batadv_softif_vlan_get(bat_priv, vid);
- if (!vlan || !atomic_read(&vlan->ap_isolation))
+ if (!vlan)
+ return false;
+
+ if (!atomic_read(&vlan->ap_isolation))
goto out;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid);
@@ -3344,8 +3359,7 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
ret = true;
out:
- if (vlan)
- batadv_softif_vlan_free_ref(vlan);
+ batadv_softif_vlan_free_ref(vlan);
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
if (tt_local_entry)
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index d260efd70499..3437b667a2cd 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -100,6 +100,8 @@ struct batadv_hard_iface_bat_iv {
* @bat_iv: BATMAN IV specific per hard interface data
* @cleanup_work: work queue callback item for hard interface deinit
* @debug_dir: dentry for nc subdir in batman-adv directory in debugfs
+ * @neigh_list: list of unique single hop neighbors via this interface
+ * @neigh_list_lock: lock protecting neigh_list
*/
struct batadv_hard_iface {
struct list_head list;
@@ -115,6 +117,9 @@ struct batadv_hard_iface {
struct batadv_hard_iface_bat_iv bat_iv;
struct work_struct cleanup_work;
struct dentry *debug_dir;
+ struct hlist_head neigh_list;
+ /* neigh_list_lock protects: neigh_list */
+ spinlock_t neigh_list_lock;
};
/**
@@ -218,12 +223,12 @@ struct batadv_orig_bat_iv {
* @orig: originator ethernet address
* @ifinfo_list: list for routers per outgoing interface
* @last_bonding_candidate: pointer to last ifinfo of last used router
- * @batadv_dat_addr_t: address of the orig node in the distributed hash
+ * @dat_addr: address of the orig node in the distributed hash
* @last_seen: time when last packet from this node was received
* @bcast_seqno_reset: time when the broadcast seqno window was reset
* @mcast_handler_lock: synchronizes mcast-capability and -flag changes
* @mcast_flags: multicast flags announced by the orig node
- * @mcast_want_all_unsnoop_node: a list node for the
+ * @mcast_want_all_unsnoopables_node: a list node for the
* mcast.want_all_unsnoopables list
* @mcast_want_all_ipv4_node: a list node for the mcast.want_all_ipv4 list
* @mcast_want_all_ipv6_node: a list node for the mcast.want_all_ipv6 list
@@ -341,6 +346,23 @@ struct batadv_gw_node {
};
/**
+ * batadv_hardif_neigh_node - unique neighbor per hard interface
+ * @list: list node for batadv_hard_iface::neigh_list
+ * @addr: the MAC address of the neighboring interface
+ * @if_incoming: pointer to incoming hard interface
+ * @refcount: number of contexts the object is used
+ * @rcu: struct used for freeing in a RCU-safe manner
+ */
+struct batadv_hardif_neigh_node {
+ struct hlist_node list;
+ u8 addr[ETH_ALEN];
+ struct batadv_hard_iface *if_incoming;
+ unsigned long last_seen;
+ atomic_t refcount;
+ struct rcu_head rcu;
+};
+
+/**
* struct batadv_neigh_node - structure for single hops neighbors
* @list: list node for batadv_orig_node::neigh_list
* @orig_node: pointer to corresponding orig_node
@@ -349,9 +371,8 @@ struct batadv_gw_node {
* @ifinfo_lock: lock protecting private ifinfo members and list
* @if_incoming: pointer to incoming hard interface
* @last_seen: when last packet via this neighbor was received
- * @last_ttl: last received ttl from this neigh node
+ * @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
- * @bat_iv: B.A.T.M.A.N. IV private structure
*/
struct batadv_neigh_node {
struct hlist_node list;
@@ -401,13 +422,14 @@ struct batadv_neigh_ifinfo {
struct rcu_head rcu;
};
+#ifdef CONFIG_BATMAN_ADV_BLA
+
/**
* struct batadv_bcast_duplist_entry - structure for LAN broadcast suppression
- * @orig[ETH_ALEN]: mac address of orig node orginating the broadcast
+ * @orig: mac address of orig node orginating the broadcast
* @crc: crc32 checksum of broadcast payload
* @entrytime: time when the broadcast packet was received
*/
-#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_bcast_duplist_entry {
u8 orig[ETH_ALEN];
__be32 crc;
@@ -549,9 +571,11 @@ struct batadv_priv_tt {
struct delayed_work work;
};
+#ifdef CONFIG_BATMAN_ADV_BLA
+
/**
* struct batadv_priv_bla - per mesh interface bridge loope avoidance data
- * @num_requests; number of bla requests in flight
+ * @num_requests: number of bla requests in flight
* @claim_hash: hash table containing mesh nodes this host has claimed
* @backbone_hash: hash table containing all detected backbone gateways
* @bcast_duplist: recently received broadcast packets array (for broadcast
@@ -561,7 +585,6 @@ struct batadv_priv_tt {
* @claim_dest: local claim data (e.g. claim group)
* @work: work queue callback item for cleanups & bla announcements
*/
-#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_priv_bla {
atomic_t num_requests;
struct batadv_hashtable *claim_hash;
@@ -575,6 +598,8 @@ struct batadv_priv_bla {
};
#endif
+#ifdef CONFIG_BATMAN_ADV_DEBUG
+
/**
* struct batadv_priv_debug_log - debug logging data
* @log_buff: buffer holding the logs (ring bufer)
@@ -583,7 +608,6 @@ struct batadv_priv_bla {
* @lock: lock protecting log_buff, log_start & log_end
* @queue_wait: log reader's wait queue
*/
-#ifdef CONFIG_BATMAN_ADV_DEBUG
struct batadv_priv_debug_log {
char log_buff[BATADV_LOG_BUF_LEN];
unsigned long log_start;
@@ -625,13 +649,14 @@ struct batadv_priv_tvlv {
spinlock_t handler_list_lock; /* protects handler_list */
};
+#ifdef CONFIG_BATMAN_ADV_DAT
+
/**
* struct batadv_priv_dat - per mesh interface DAT private data
* @addr: node DAT address
* @hash: hashtable representing the local ARP cache
* @work: work queue callback item for cache purging
*/
-#ifdef CONFIG_BATMAN_ADV_DAT
struct batadv_priv_dat {
batadv_dat_addr_t addr;
struct batadv_hashtable *hash;
@@ -773,7 +798,7 @@ struct batadv_softif_vlan {
* @dat: distributed arp table data
* @mcast: multicast data
* @network_coding: bool indicating whether network coding is enabled
- * @batadv_priv_nc: network coding data
+ * @nc: network coding data
*/
struct batadv_priv {
atomic_t mesh_state;
@@ -871,6 +896,8 @@ struct batadv_socket_packet {
u8 icmp_packet[BATADV_ICMP_MAX_PACKET_SIZE];
};
+#ifdef CONFIG_BATMAN_ADV_BLA
+
/**
* struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
* @orig: originator address of backbone node (mac address of primary iface)
@@ -884,10 +911,10 @@ struct batadv_socket_packet {
* backbone gateway - no bcast traffic is formwared until the situation was
* resolved
* @crc: crc16 checksum over all claims
+ * @crc_lock: lock protecting crc
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
*/
-#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_bla_backbone_gw {
u8 orig[ETH_ALEN];
unsigned short vid;
@@ -897,6 +924,7 @@ struct batadv_bla_backbone_gw {
atomic_t wait_periods;
atomic_t request_sent;
u16 crc;
+ spinlock_t crc_lock; /* protects crc */
atomic_t refcount;
struct rcu_head rcu;
};
@@ -905,7 +933,7 @@ struct batadv_bla_backbone_gw {
* struct batadv_bla_claim - claimed non-mesh client structure
* @addr: mac address of claimed non-mesh client
* @vid: vlan id this client was detected on
- * @batadv_bla_backbone_gw: pointer to backbone gw claiming this client
+ * @backbone_gw: pointer to backbone gw claiming this client
* @lasttime: last time we heard of claim (locals only)
* @hash_entry: hlist node for batadv_priv_bla::claim_hash
* @refcount: number of contexts the object is used
@@ -1131,11 +1159,13 @@ struct batadv_forw_packet {
* @bat_primary_iface_set: called when primary interface is selected / changed
* @bat_ogm_schedule: prepare a new outgoing OGM for the send queue
* @bat_ogm_emit: send scheduled OGM
+ * @bat_hardif_neigh_init: called on creation of single hop entry
* @bat_neigh_cmp: compare the metrics of two neighbors for their respective
* outgoing interfaces
- * @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or better
- * than neigh2 for their respective outgoing interface from the metric
+ * @bat_neigh_is_similar_or_better: check if neigh1 is equally similar or
+ * better than neigh2 for their respective outgoing interface from the metric
* prospective
+ * @bat_neigh_print: print the single hop neighbor list (optional)
* @bat_neigh_free: free the resources allocated by the routing algorithm for a
* neigh_node object
* @bat_orig_print: print the originator table (optional)
@@ -1156,15 +1186,17 @@ struct batadv_algo_ops {
void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface);
void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet);
/* neigh_node handling API */
+ void (*bat_hardif_neigh_init)(struct batadv_hardif_neigh_node *neigh);
int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2);
- bool (*bat_neigh_is_equiv_or_better)
+ bool (*bat_neigh_is_similar_or_better)
(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2);
+ void (*bat_neigh_print)(struct batadv_priv *priv, struct seq_file *seq);
void (*bat_neigh_free)(struct batadv_neigh_node *neigh);
/* orig_node handling API */
void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq,
@@ -1224,8 +1256,6 @@ struct batadv_dat_candidate {
* struct batadv_tvlv_container - container for tvlv appended to OGMs
* @list: hlist node for batadv_priv_tvlv::container_list
* @tvlv_hdr: tvlv header information needed to construct the tvlv
- * @value_len: length of the buffer following this struct which contains
- * the actual tvlv payload
* @refcount: number of contexts the object is used
*/
struct batadv_tvlv_container {
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 9e9cca3689a0..d040365ba98e 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -825,9 +825,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
spin_unlock(&devices_lock);
- lowpan_netdev_setup(netdev, LOWPAN_LLTYPE_BTLE);
-
- err = register_netdev(netdev);
+ err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
if (err < 0) {
BT_INFO("register_netdev failed %d", err);
spin_lock(&devices_lock);
@@ -890,7 +888,7 @@ static void delete_netdev(struct work_struct *work)
struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
delete_netdev);
- unregister_netdev(entry->netdev);
+ lowpan_unregister_netdev(entry->netdev);
/* The entry pointer is deleted by the netdev destructor. */
}
@@ -1348,7 +1346,7 @@ static void disconnect_devices(void)
ifdown(entry->netdev);
BT_DBG("Unregistering netdev %s %p",
entry->netdev->name, entry->netdev);
- unregister_netdev(entry->netdev);
+ lowpan_unregister_netdev(entry->netdev);
kfree(entry);
}
}
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 70306cc9d814..955eda93e66f 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -33,8 +33,6 @@
#include "selftest.h"
-#define VERSION "2.21"
-
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
static const struct net_proto_family *bt_proto[BT_MAX_PROTO];
@@ -176,20 +174,20 @@ EXPORT_SYMBOL(bt_accept_unlink);
struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
{
- struct list_head *p, *n;
+ struct bt_sock *s, *n;
struct sock *sk;
BT_DBG("parent %p", parent);
- list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
- sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
+ list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
+ sk = (struct sock *)s;
lock_sock(sk);
/* FIXME: Is this check still needed */
if (sk->sk_state == BT_CLOSED) {
- release_sock(sk);
bt_accept_unlink(sk);
+ release_sock(sk);
continue;
}
@@ -390,11 +388,11 @@ EXPORT_SYMBOL(bt_sock_stream_recvmsg);
static inline unsigned int bt_accept_poll(struct sock *parent)
{
- struct list_head *p, *n;
+ struct bt_sock *s, *n;
struct sock *sk;
- list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
- sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
+ list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
+ sk = (struct sock *)s;
if (sk->sk_state == BT_CONNECTED ||
(test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags) &&
sk->sk_state == BT_CONNECT2))
@@ -671,7 +669,7 @@ static const struct file_operations bt_fops = {
};
int bt_procfs_init(struct net *net, const char *name,
- struct bt_sock_list* sk_list,
+ struct bt_sock_list *sk_list,
int (* seq_show)(struct seq_file *, void *))
{
sk_list->custom_seq_show = seq_show;
@@ -687,7 +685,7 @@ void bt_procfs_cleanup(struct net *net, const char *name)
}
#else
int bt_procfs_init(struct net *net, const char *name,
- struct bt_sock_list* sk_list,
+ struct bt_sock_list *sk_list,
int (* seq_show)(struct seq_file *, void *))
{
return 0;
@@ -715,7 +713,7 @@ static int __init bt_init(void)
sock_skb_cb_check_size(sizeof(struct bt_skb_cb));
- BT_INFO("Core ver %s", VERSION);
+ BT_INFO("Core ver %s", BT_SUBSYS_VERSION);
err = bt_selftest();
if (err < 0)
@@ -789,7 +787,7 @@ subsys_initcall(bt_init);
module_exit(bt_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
-MODULE_DESCRIPTION("Bluetooth Core ver " VERSION);
-MODULE_VERSION(VERSION);
+MODULE_DESCRIPTION("Bluetooth Core ver " BT_SUBSYS_VERSION);
+MODULE_VERSION(BT_SUBSYS_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_BLUETOOTH);
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 1641367e54ca..fbf251fef70f 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -608,8 +608,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
s->msg.msg_flags = MSG_NOSIGNAL;
#ifdef CONFIG_BT_BNEP_MC_FILTER
- /* Set default mc filter */
- set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
+ /* Set default mc filter to not filter out any mc addresses
+ * as defined in the BNEP specification (revision 0.95a)
+ * http://grouper.ieee.org/groups/802/15/Bluetooth/BNEP.pdf
+ */
+ s->mc_filter = ~0LL;
#endif
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 9a50338772f3..46ac686c8911 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -100,10 +100,8 @@ static void cmtp_application_del(struct cmtp_session *session, struct cmtp_appli
static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
{
struct cmtp_application *app;
- struct list_head *p;
- list_for_each(p, &session->applications) {
- app = list_entry(p, struct cmtp_application, list);
+ list_for_each_entry(app, &session->applications, list) {
switch (pattern) {
case CMTP_MSGNUM:
if (app->msgnum == value)
@@ -511,14 +509,12 @@ static int cmtp_proc_show(struct seq_file *m, void *v)
struct capi_ctr *ctrl = m->private;
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *app;
- struct list_head *p;
seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
seq_printf(m, "addr %s\n", session->name);
seq_printf(m, "ctrl %d\n", session->num);
- list_for_each(p, &session->applications) {
- app = list_entry(p, struct cmtp_application, list);
+ list_for_each_entry(app, &session->applications, list) {
seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
}
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 298ed37010e6..9e59b6654126 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -178,8 +178,7 @@ static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
break;
default:
- if (session->reassembly[id] != NULL)
- kfree_skb(session->reassembly[id]);
+ kfree_skb(session->reassembly[id]);
session->reassembly[id] = NULL;
break;
}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 85b82f7adbd2..32575b49f4a0 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -178,6 +178,10 @@ static void hci_connect_le_scan_remove(struct hci_conn *conn)
hci_dev_hold(conn->hdev);
hci_conn_get(conn);
+ /* Even though we hold a reference to the hdev, many other
+ * things might get cleaned up meanwhile, including the hdev's
+ * own workqueue, so we can't use that for scheduling.
+ */
schedule_work(&conn->le_scan_cleanup);
}
@@ -664,8 +668,16 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
conn->state = BT_CLOSED;
- mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- status);
+ /* If the status indicates successful cancellation of
+ * the attempt (i.e. Unkown Connection Id) there's no point of
+ * notifying failure since we'll go back to keep trying to
+ * connect. The only exception is explicit connect requests
+ * where a timeout + cancel does indicate an actual failure.
+ */
+ if (status != HCI_ERROR_UNKNOWN_CONN_ID ||
+ (params && params->explicit_connect))
+ mgmt_connect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, status);
hci_connect_cfm(conn, status);
@@ -679,7 +691,7 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
/* Re-enable advertising in case this was a failed connection
* attempt as a peripheral.
*/
- mgmt_reenable_advertising(hdev);
+ hci_req_reenable_advertising(hdev);
}
static void create_le_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
@@ -722,8 +734,12 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
if (hci_update_random_address(req, false, &own_addr_type))
return;
+ /* Set window to be the same value as the interval to enable
+ * continuous scanning.
+ */
cp.scan_interval = cpu_to_le16(hdev->le_scan_interval);
- cp.scan_window = cpu_to_le16(hdev->le_scan_window);
+ cp.scan_window = cp.scan_interval;
+
bacpy(&cp.peer_addr, &conn->dst);
cp.peer_addr_type = conn->dst_type;
cp.own_address_type = own_addr_type;
@@ -781,7 +797,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 role)
{
struct hci_conn_params *params;
- struct hci_conn *conn, *conn_unfinished;
+ struct hci_conn *conn;
struct smp_irk *irk;
struct hci_request req;
int err;
@@ -794,35 +810,22 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
return ERR_PTR(-EOPNOTSUPP);
}
- /* Some devices send ATT messages as soon as the physical link is
- * established. To be able to handle these ATT messages, the user-
- * space first establishes the connection and then starts the pairing
- * process.
- *
- * So if a hci_conn object already exists for the following connection
- * attempt, we simply update pending_sec_level and auth_type fields
- * and return the object found.
- */
- conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
- conn_unfinished = NULL;
- if (conn) {
- if (conn->state == BT_CONNECT &&
- test_bit(HCI_CONN_SCANNING, &conn->flags)) {
- BT_DBG("will continue unfinished conn %pMR", dst);
- conn_unfinished = conn;
- } else {
- if (conn->pending_sec_level < sec_level)
- conn->pending_sec_level = sec_level;
- goto done;
- }
- }
-
/* Since the controller supports only one LE connection attempt at a
* time, we return -EBUSY if there is any connection attempt running.
*/
if (hci_lookup_le_connect(hdev))
return ERR_PTR(-EBUSY);
+ /* If there's already a connection object but it's not in
+ * scanning state it means it must already be established, in
+ * which case we can't do anything else except report a failure
+ * to connect.
+ */
+ conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
+ if (conn && !test_bit(HCI_CONN_SCANNING, &conn->flags)) {
+ return ERR_PTR(-EBUSY);
+ }
+
/* When given an identity address with existing identity
* resolving key, the connection needs to be established
* to a resolvable random address.
@@ -838,23 +841,20 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
dst_type = ADDR_LE_DEV_RANDOM;
}
- if (conn_unfinished) {
- conn = conn_unfinished;
+ if (conn) {
bacpy(&conn->dst, dst);
} else {
conn = hci_conn_add(hdev, LE_LINK, dst, role);
+ if (!conn)
+ return ERR_PTR(-ENOMEM);
+ hci_conn_hold(conn);
+ conn->pending_sec_level = sec_level;
}
- if (!conn)
- return ERR_PTR(-ENOMEM);
-
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->conn_timeout = conn_timeout;
- if (!conn_unfinished)
- conn->pending_sec_level = sec_level;
-
hci_req_init(&req, hdev);
/* Disable advertising if we're active. For master role
@@ -918,37 +918,9 @@ create_conn:
return ERR_PTR(err);
}
-done:
- /* If this is continuation of connect started by hci_connect_le_scan,
- * it already called hci_conn_hold and calling it again would mess the
- * counter.
- */
- if (!conn_unfinished)
- hci_conn_hold(conn);
-
return conn;
}
-static void hci_connect_le_scan_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- struct hci_conn *conn;
-
- if (!status)
- return;
-
- BT_ERR("Failed to add device to auto conn whitelist: status 0x%2.2x",
- status);
-
- hci_dev_lock(hdev);
-
- conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
- if (conn)
- hci_le_conn_failed(conn, status);
-
- hci_dev_unlock(hdev);
-}
-
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
{
struct hci_conn *conn;
@@ -964,10 +936,9 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
}
/* This function requires the caller holds hdev->lock */
-static int hci_explicit_conn_params_set(struct hci_request *req,
+static int hci_explicit_conn_params_set(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
if (is_connected(hdev, addr, addr_type))
@@ -995,7 +966,6 @@ static int hci_explicit_conn_params_set(struct hci_request *req,
}
params->explicit_connect = true;
- __hci_update_background_scan(req);
BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
params->auto_connect);
@@ -1006,11 +976,9 @@ static int hci_explicit_conn_params_set(struct hci_request *req,
/* This function requires the caller holds hdev->lock */
struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level,
- u16 conn_timeout, u8 role)
+ u16 conn_timeout)
{
struct hci_conn *conn;
- struct hci_request req;
- int err;
/* Let's make sure that le is enabled.*/
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
@@ -1038,29 +1006,22 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
BT_DBG("requesting refresh of dst_addr");
- conn = hci_conn_add(hdev, LE_LINK, dst, role);
+ conn = hci_conn_add(hdev, LE_LINK, dst, HCI_ROLE_MASTER);
if (!conn)
return ERR_PTR(-ENOMEM);
- hci_req_init(&req, hdev);
-
- if (hci_explicit_conn_params_set(&req, dst, dst_type) < 0)
+ if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0)
return ERR_PTR(-EBUSY);
conn->state = BT_CONNECT;
set_bit(HCI_CONN_SCANNING, &conn->flags);
-
- err = hci_req_run(&req, hci_connect_le_scan_complete);
- if (err && err != -ENODATA) {
- hci_conn_del(conn);
- return ERR_PTR(err);
- }
-
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->conn_timeout = conn_timeout;
+ hci_update_background_scan(hdev);
+
done:
hci_conn_hold(conn);
return conn;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 62edbf1b114e..47bcef754796 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -56,15 +56,6 @@ DEFINE_MUTEX(hci_cb_list_lock);
/* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida);
-/* ----- HCI requests ----- */
-
-#define HCI_REQ_DONE 0
-#define HCI_REQ_PEND 1
-#define HCI_REQ_CANCELED 2
-
-#define hci_req_lock(d) mutex_lock(&d->req_lock)
-#define hci_req_unlock(d) mutex_unlock(&d->req_lock)
-
/* ---- HCI debugfs entries ---- */
static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
@@ -73,7 +64,7 @@ static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
struct hci_dev *hdev = file->private_data;
char buf[3];
- buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y': 'N';
+ buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -101,14 +92,14 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
if (enable == hci_dev_test_flag(hdev, HCI_DUT_MODE))
return -EALREADY;
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
if (enable)
skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
HCI_CMD_TIMEOUT);
else
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
HCI_CMD_TIMEOUT);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -133,7 +124,7 @@ static ssize_t vendor_diag_read(struct file *file, char __user *user_buf,
struct hci_dev *hdev = file->private_data;
char buf[3];
- buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y': 'N';
+ buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -165,9 +156,9 @@ static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf,
!test_bit(HCI_RUNNING, &hdev->flags))
goto done;
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
err = hdev->set_diag(hdev, enable);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
if (err < 0)
return err;
@@ -198,197 +189,14 @@ static void hci_debugfs_create_basic(struct hci_dev *hdev)
&vendor_diag_fops);
}
-/* ---- HCI requests ---- */
-
-static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
- struct sk_buff *skb)
-{
- BT_DBG("%s result 0x%2.2x", hdev->name, result);
-
- if (hdev->req_status == HCI_REQ_PEND) {
- hdev->req_result = result;
- hdev->req_status = HCI_REQ_DONE;
- if (skb)
- hdev->req_skb = skb_get(skb);
- wake_up_interruptible(&hdev->req_wait_q);
- }
-}
-
-static void hci_req_cancel(struct hci_dev *hdev, int err)
-{
- BT_DBG("%s err 0x%2.2x", hdev->name, err);
-
- if (hdev->req_status == HCI_REQ_PEND) {
- hdev->req_result = err;
- hdev->req_status = HCI_REQ_CANCELED;
- wake_up_interruptible(&hdev->req_wait_q);
- }
-}
-
-struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
- const void *param, u8 event, u32 timeout)
-{
- DECLARE_WAITQUEUE(wait, current);
- struct hci_request req;
- struct sk_buff *skb;
- int err = 0;
-
- BT_DBG("%s", hdev->name);
-
- hci_req_init(&req, hdev);
-
- hci_req_add_ev(&req, opcode, plen, param, event);
-
- hdev->req_status = HCI_REQ_PEND;
-
- add_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- err = hci_req_run_skb(&req, hci_req_sync_complete);
- if (err < 0) {
- remove_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_RUNNING);
- return ERR_PTR(err);
- }
-
- schedule_timeout(timeout);
-
- remove_wait_queue(&hdev->req_wait_q, &wait);
-
- if (signal_pending(current))
- return ERR_PTR(-EINTR);
-
- switch (hdev->req_status) {
- case HCI_REQ_DONE:
- err = -bt_to_errno(hdev->req_result);
- break;
-
- case HCI_REQ_CANCELED:
- err = -hdev->req_result;
- break;
-
- default:
- err = -ETIMEDOUT;
- break;
- }
-
- hdev->req_status = hdev->req_result = 0;
- skb = hdev->req_skb;
- hdev->req_skb = NULL;
-
- BT_DBG("%s end: err %d", hdev->name, err);
-
- if (err < 0) {
- kfree_skb(skb);
- return ERR_PTR(err);
- }
-
- if (!skb)
- return ERR_PTR(-ENODATA);
-
- return skb;
-}
-EXPORT_SYMBOL(__hci_cmd_sync_ev);
-
-struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
- const void *param, u32 timeout)
-{
- return __hci_cmd_sync_ev(hdev, opcode, plen, param, 0, timeout);
-}
-EXPORT_SYMBOL(__hci_cmd_sync);
-
-/* Execute request and wait for completion. */
-static int __hci_req_sync(struct hci_dev *hdev,
- void (*func)(struct hci_request *req,
- unsigned long opt),
- unsigned long opt, __u32 timeout)
-{
- struct hci_request req;
- DECLARE_WAITQUEUE(wait, current);
- int err = 0;
-
- BT_DBG("%s start", hdev->name);
-
- hci_req_init(&req, hdev);
-
- hdev->req_status = HCI_REQ_PEND;
-
- func(&req, opt);
-
- add_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- err = hci_req_run_skb(&req, hci_req_sync_complete);
- if (err < 0) {
- hdev->req_status = 0;
-
- remove_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_RUNNING);
-
- /* ENODATA means the HCI request command queue is empty.
- * This can happen when a request with conditionals doesn't
- * trigger any commands to be sent. This is normal behavior
- * and should not trigger an error return.
- */
- if (err == -ENODATA)
- return 0;
-
- return err;
- }
-
- schedule_timeout(timeout);
-
- remove_wait_queue(&hdev->req_wait_q, &wait);
-
- if (signal_pending(current))
- return -EINTR;
-
- switch (hdev->req_status) {
- case HCI_REQ_DONE:
- err = -bt_to_errno(hdev->req_result);
- break;
-
- case HCI_REQ_CANCELED:
- err = -hdev->req_result;
- break;
-
- default:
- err = -ETIMEDOUT;
- break;
- }
-
- hdev->req_status = hdev->req_result = 0;
-
- BT_DBG("%s end: err %d", hdev->name, err);
-
- return err;
-}
-
-static int hci_req_sync(struct hci_dev *hdev,
- void (*req)(struct hci_request *req,
- unsigned long opt),
- unsigned long opt, __u32 timeout)
-{
- int ret;
-
- if (!test_bit(HCI_UP, &hdev->flags))
- return -ENETDOWN;
-
- /* Serialize all requests */
- hci_req_lock(hdev);
- ret = __hci_req_sync(hdev, req, opt, timeout);
- hci_req_unlock(hdev);
-
- return ret;
-}
-
-static void hci_reset_req(struct hci_request *req, unsigned long opt)
+static int hci_reset_req(struct hci_request *req, unsigned long opt)
{
BT_DBG("%s %ld", req->hdev->name, opt);
/* Reset device */
set_bit(HCI_RESET, &req->hdev->flags);
hci_req_add(req, HCI_OP_RESET, 0, NULL);
+ return 0;
}
static void bredr_init(struct hci_request *req)
@@ -428,7 +236,7 @@ static void amp_init1(struct hci_request *req)
hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
}
-static void amp_init2(struct hci_request *req)
+static int amp_init2(struct hci_request *req)
{
/* Read Local Supported Features. Not all AMP controllers
* support this so it's placed conditionally in the second
@@ -436,9 +244,11 @@ static void amp_init2(struct hci_request *req)
*/
if (req->hdev->commands[14] & 0x20)
hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+
+ return 0;
}
-static void hci_init1_req(struct hci_request *req, unsigned long opt)
+static int hci_init1_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -461,6 +271,8 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
BT_ERR("Unknown device type %d", hdev->dev_type);
break;
}
+
+ return 0;
}
static void bredr_setup(struct hci_request *req)
@@ -531,20 +343,30 @@ static void hci_setup_event_mask(struct hci_request *req)
if (lmp_bredr_capable(hdev)) {
events[4] |= 0x01; /* Flow Specification Complete */
- events[4] |= 0x02; /* Inquiry Result with RSSI */
- events[4] |= 0x04; /* Read Remote Extended Features Complete */
- events[5] |= 0x08; /* Synchronous Connection Complete */
- events[5] |= 0x10; /* Synchronous Connection Changed */
} else {
/* Use a different default for LE-only devices */
memset(events, 0, sizeof(events));
- events[0] |= 0x10; /* Disconnection Complete */
- events[1] |= 0x08; /* Read Remote Version Information Complete */
events[1] |= 0x20; /* Command Complete */
events[1] |= 0x40; /* Command Status */
events[1] |= 0x80; /* Hardware Error */
- events[2] |= 0x04; /* Number of Completed Packets */
- events[3] |= 0x02; /* Data Buffer Overflow */
+
+ /* If the controller supports the Disconnect command, enable
+ * the corresponding event. In addition enable packet flow
+ * control related events.
+ */
+ if (hdev->commands[0] & 0x20) {
+ events[0] |= 0x10; /* Disconnection Complete */
+ events[2] |= 0x04; /* Number of Completed Packets */
+ events[3] |= 0x02; /* Data Buffer Overflow */
+ }
+
+ /* If the controller supports the Read Remote Version
+ * Information command, enable the corresponding event.
+ */
+ if (hdev->commands[2] & 0x80)
+ events[1] |= 0x08; /* Read Remote Version Information
+ * Complete
+ */
if (hdev->le_features[0] & HCI_LE_ENCRYPTION) {
events[0] |= 0x80; /* Encryption Change */
@@ -552,9 +374,18 @@ static void hci_setup_event_mask(struct hci_request *req)
}
}
- if (lmp_inq_rssi_capable(hdev))
+ if (lmp_inq_rssi_capable(hdev) ||
+ test_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks))
events[4] |= 0x02; /* Inquiry Result with RSSI */
+ if (lmp_ext_feat_capable(hdev))
+ events[4] |= 0x04; /* Read Remote Extended Features Complete */
+
+ if (lmp_esco_capable(hdev)) {
+ events[5] |= 0x08; /* Synchronous Connection Complete */
+ events[5] |= 0x10; /* Synchronous Connection Changed */
+ }
+
if (lmp_sniffsubr_capable(hdev))
events[5] |= 0x20; /* Sniff Subrating */
@@ -590,7 +421,7 @@ static void hci_setup_event_mask(struct hci_request *req)
hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
}
-static void hci_init2_req(struct hci_request *req, unsigned long opt)
+static int hci_init2_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -670,6 +501,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
&enable);
}
+
+ return 0;
}
static void hci_setup_link_policy(struct hci_request *req)
@@ -744,7 +577,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
}
-static void hci_init3_req(struct hci_request *req, unsigned long opt)
+static int hci_init3_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
u8 p;
@@ -777,7 +610,6 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
u8 events[8];
memset(events, 0, sizeof(events));
- events[0] = 0x0f;
if (hdev->le_features[0] & HCI_LE_ENCRYPTION)
events[0] |= 0x10; /* LE Long Term Key Request */
@@ -804,6 +636,34 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
* Report
*/
+ /* If the controller supports the LE Set Scan Enable command,
+ * enable the corresponding advertising report event.
+ */
+ if (hdev->commands[26] & 0x08)
+ events[0] |= 0x02; /* LE Advertising Report */
+
+ /* If the controller supports the LE Create Connection
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[26] & 0x10)
+ events[0] |= 0x01; /* LE Connection Complete */
+
+ /* If the controller supports the LE Connection Update
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[27] & 0x04)
+ events[0] |= 0x04; /* LE Connection Update
+ * Complete
+ */
+
+ /* If the controller supports the LE Read Remote Used Features
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[27] & 0x20)
+ events[0] |= 0x08; /* LE Read Remote Used
+ * Features Complete
+ */
+
/* If the controller supports the LE Read Local P-256
* Public Key command, enable the corresponding event.
*/
@@ -856,9 +716,11 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_READ_LOCAL_EXT_FEATURES,
sizeof(cp), &cp);
}
+
+ return 0;
}
-static void hci_init4_req(struct hci_request *req, unsigned long opt)
+static int hci_init4_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -909,20 +771,22 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
sizeof(support), &support);
}
+
+ return 0;
}
static int __hci_init(struct hci_dev *hdev)
{
int err;
- err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
if (hci_dev_test_flag(hdev, HCI_SETUP))
hci_debugfs_create_basic(hdev);
- err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -933,11 +797,11 @@ static int __hci_init(struct hci_dev *hdev)
if (hdev->dev_type != HCI_BREDR)
return 0;
- err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
- err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -968,7 +832,7 @@ static int __hci_init(struct hci_dev *hdev)
return 0;
}
-static void hci_init0_req(struct hci_request *req, unsigned long opt)
+static int hci_init0_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -984,6 +848,8 @@ static void hci_init0_req(struct hci_request *req, unsigned long opt)
/* Read BD Address */
if (hdev->set_bdaddr)
hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
+
+ return 0;
}
static int __hci_unconf_init(struct hci_dev *hdev)
@@ -993,7 +859,7 @@ static int __hci_unconf_init(struct hci_dev *hdev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return 0;
- err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -1003,7 +869,7 @@ static int __hci_unconf_init(struct hci_dev *hdev)
return 0;
}
-static void hci_scan_req(struct hci_request *req, unsigned long opt)
+static int hci_scan_req(struct hci_request *req, unsigned long opt)
{
__u8 scan = opt;
@@ -1011,9 +877,10 @@ static void hci_scan_req(struct hci_request *req, unsigned long opt)
/* Inquiry and Page scans */
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ return 0;
}
-static void hci_auth_req(struct hci_request *req, unsigned long opt)
+static int hci_auth_req(struct hci_request *req, unsigned long opt)
{
__u8 auth = opt;
@@ -1021,9 +888,10 @@ static void hci_auth_req(struct hci_request *req, unsigned long opt)
/* Authentication */
hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth);
+ return 0;
}
-static void hci_encrypt_req(struct hci_request *req, unsigned long opt)
+static int hci_encrypt_req(struct hci_request *req, unsigned long opt)
{
__u8 encrypt = opt;
@@ -1031,9 +899,10 @@ static void hci_encrypt_req(struct hci_request *req, unsigned long opt)
/* Encryption */
hci_req_add(req, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);
+ return 0;
}
-static void hci_linkpol_req(struct hci_request *req, unsigned long opt)
+static int hci_linkpol_req(struct hci_request *req, unsigned long opt)
{
__le16 policy = cpu_to_le16(opt);
@@ -1041,6 +910,7 @@ static void hci_linkpol_req(struct hci_request *req, unsigned long opt)
/* Default link policy */
hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
+ return 0;
}
/* Get HCI device by index.
@@ -1285,7 +1155,7 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
return copied;
}
-static void hci_inq_req(struct hci_request *req, unsigned long opt)
+static int hci_inq_req(struct hci_request *req, unsigned long opt)
{
struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
struct hci_dev *hdev = req->hdev;
@@ -1294,13 +1164,15 @@ static void hci_inq_req(struct hci_request *req, unsigned long opt)
BT_DBG("%s", hdev->name);
if (test_bit(HCI_INQUIRY, &hdev->flags))
- return;
+ return 0;
/* Start Inquiry */
memcpy(&cp.lap, &ir->lap, 3);
cp.length = ir->length;
cp.num_rsp = ir->num_rsp;
hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+ return 0;
}
int hci_inquiry(void __user *arg)
@@ -1351,7 +1223,7 @@ int hci_inquiry(void __user *arg)
if (do_inquiry) {
err = hci_req_sync(hdev, hci_inq_req, (unsigned long) &ir,
- timeo);
+ timeo, NULL);
if (err < 0)
goto done;
@@ -1404,7 +1276,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
BT_DBG("%s %p", hdev->name, hdev);
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
ret = -ENODEV;
@@ -1527,10 +1399,10 @@ static int hci_dev_do_open(struct hci_dev *hdev)
!hci_dev_test_flag(hdev, HCI_CONFIG) &&
!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
+ hci_dev_test_flag(hdev, HCI_MGMT) &&
hdev->dev_type == HCI_BREDR) {
- hci_dev_lock(hdev);
- mgmt_powered(hdev, 1);
- hci_dev_unlock(hdev);
+ ret = __hci_req_hci_power_on(hdev);
+ mgmt_power_on(hdev, ret);
}
} else {
/* Init failed, cleanup */
@@ -1557,7 +1429,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
}
done:
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return ret;
}
@@ -1651,12 +1523,12 @@ int hci_dev_do_close(struct hci_dev *hdev)
cancel_delayed_work(&hdev->power_off);
- hci_req_cancel(hdev, ENODEV);
- hci_req_lock(hdev);
+ hci_request_cancel_all(hdev);
+ hci_req_sync_lock(hdev);
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
cancel_delayed_work_sync(&hdev->cmd_timer);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return 0;
}
@@ -1665,7 +1537,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
flush_work(&hdev->rx_work);
if (hdev->discov_timeout > 0) {
- cancel_delayed_work(&hdev->discov_off);
hdev->discov_timeout = 0;
hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
@@ -1674,17 +1545,9 @@ int hci_dev_do_close(struct hci_dev *hdev)
if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
cancel_delayed_work(&hdev->service_cache);
- cancel_delayed_work_sync(&hdev->le_scan_disable);
- cancel_delayed_work_sync(&hdev->le_scan_restart);
-
if (hci_dev_test_flag(hdev, HCI_MGMT))
cancel_delayed_work_sync(&hdev->rpa_expired);
- if (hdev->adv_instance_timeout) {
- cancel_delayed_work_sync(&hdev->adv_instance_expire);
- hdev->adv_instance_timeout = 0;
- }
-
/* Avoid potential lockdep warnings from the *_flush() calls by
* ensuring the workqueue is empty up front.
*/
@@ -1696,8 +1559,9 @@ int hci_dev_do_close(struct hci_dev *hdev)
auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
- if (!auto_off && hdev->dev_type == HCI_BREDR)
- mgmt_powered(hdev, 0);
+ if (!auto_off && hdev->dev_type == HCI_BREDR &&
+ hci_dev_test_flag(hdev, HCI_MGMT))
+ __mgmt_power_off(hdev);
hci_inquiry_cache_flush(hdev);
hci_pend_le_actions_clear(hdev);
@@ -1717,7 +1581,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
if (test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks) &&
!auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
set_bit(HCI_INIT, &hdev->flags);
- __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
+ __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT, NULL);
clear_bit(HCI_INIT, &hdev->flags);
}
@@ -1754,7 +1618,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
bacpy(&hdev->random_addr, BDADDR_ANY);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
hci_dev_put(hdev);
return 0;
@@ -1790,7 +1654,7 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
BT_DBG("%s %p", hdev->name, hdev);
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
/* Drop queues */
skb_queue_purge(&hdev->rx_q);
@@ -1812,9 +1676,9 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
atomic_set(&hdev->cmd_cnt, 1);
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
- ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+ ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT, NULL);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return ret;
}
@@ -1905,7 +1769,7 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan)
hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
- mgmt_update_adv_data(hdev);
+ hci_req_update_adv_data(hdev, hdev->cur_adv_instance);
mgmt_new_settings(hdev);
}
@@ -1947,7 +1811,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
switch (cmd) {
case HCISETAUTH:
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETENCRYPT:
@@ -1959,18 +1823,18 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
if (!test_bit(HCI_AUTH, &hdev->flags)) {
/* Auth must be enabled first */
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
if (err)
break;
}
err = hci_req_sync(hdev, hci_encrypt_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETSCAN:
err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
/* Ensure that the connectable and discoverable states
* get correctly modified as this was a non-mgmt change.
@@ -1981,7 +1845,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
case HCISETLINKPOL:
err = hci_req_sync(hdev, hci_linkpol_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETLINKMODE:
@@ -2150,6 +2014,16 @@ static void hci_power_on(struct work_struct *work)
BT_DBG("%s", hdev->name);
+ if (test_bit(HCI_UP, &hdev->flags) &&
+ hci_dev_test_flag(hdev, HCI_MGMT) &&
+ hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
+ hci_req_sync_lock(hdev);
+ err = __hci_req_hci_power_on(hdev);
+ hci_req_sync_unlock(hdev);
+ mgmt_power_on(hdev, err);
+ return;
+ }
+
err = hci_dev_do_open(hdev);
if (err < 0) {
hci_dev_lock(hdev);
@@ -2232,28 +2106,6 @@ static void hci_error_reset(struct work_struct *work)
hci_dev_do_open(hdev);
}
-static void hci_discov_off(struct work_struct *work)
-{
- struct hci_dev *hdev;
-
- hdev = container_of(work, struct hci_dev, discov_off.work);
-
- BT_DBG("%s", hdev->name);
-
- mgmt_discoverable_timeout(hdev);
-}
-
-static void hci_adv_timeout_expire(struct work_struct *work)
-{
- struct hci_dev *hdev;
-
- hdev = container_of(work, struct hci_dev, adv_instance_expire.work);
-
- BT_DBG("%s", hdev->name);
-
- mgmt_adv_timeout_expired(hdev);
-}
-
void hci_uuids_clear(struct hci_dev *hdev)
{
struct bt_uuid *uuid, *tmp;
@@ -2731,7 +2583,8 @@ struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance)
}
/* This function requires the caller holds hdev->lock */
-struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance) {
+struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance)
+{
struct adv_info *cur_instance;
cur_instance = hci_find_adv_instance(hdev, instance);
@@ -2757,9 +2610,12 @@ int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance)
BT_DBG("%s removing %dMR", hdev->name, instance);
- if (hdev->cur_adv_instance == instance && hdev->adv_instance_timeout) {
- cancel_delayed_work(&hdev->adv_instance_expire);
- hdev->adv_instance_timeout = 0;
+ if (hdev->cur_adv_instance == instance) {
+ if (hdev->adv_instance_timeout) {
+ cancel_delayed_work(&hdev->adv_instance_expire);
+ hdev->adv_instance_timeout = 0;
+ }
+ hdev->cur_adv_instance = 0x00;
}
list_del(&adv_instance->list);
@@ -2786,6 +2642,7 @@ void hci_adv_instances_clear(struct hci_dev *hdev)
}
hdev->adv_instance_cnt = 0;
+ hdev->cur_adv_instance = 0x00;
}
/* This function requires the caller holds hdev->lock */
@@ -2856,12 +2713,10 @@ struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list,
void hci_bdaddr_list_clear(struct list_head *bdaddr_list)
{
- struct list_head *p, *n;
+ struct bdaddr_list *b, *n;
- list_for_each_safe(p, n, bdaddr_list) {
- struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
-
- list_del(p);
+ list_for_each_entry_safe(b, n, bdaddr_list, list) {
+ list_del(&b->list);
kfree(b);
}
}
@@ -3024,181 +2879,16 @@ void hci_conn_params_clear_disabled(struct hci_dev *hdev)
}
/* This function requires the caller holds hdev->lock */
-void hci_conn_params_clear_all(struct hci_dev *hdev)
+static void hci_conn_params_clear_all(struct hci_dev *hdev)
{
struct hci_conn_params *params, *tmp;
list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list)
hci_conn_params_free(params);
- hci_update_background_scan(hdev);
-
BT_DBG("All LE connection parameters were removed");
}
-static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- if (status) {
- BT_ERR("Failed to start inquiry: status %d", status);
-
- hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- hci_dev_unlock(hdev);
- return;
- }
-}
-
-static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- /* General inquiry access code (GIAC) */
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
- struct hci_cp_inquiry cp;
- int err;
-
- if (status) {
- BT_ERR("Failed to disable LE scanning: status %d", status);
- return;
- }
-
- hdev->discovery.scan_start = 0;
-
- switch (hdev->discovery.type) {
- case DISCOV_TYPE_LE:
- hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- hci_dev_unlock(hdev);
- break;
-
- case DISCOV_TYPE_INTERLEAVED:
- hci_dev_lock(hdev);
-
- if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
- &hdev->quirks)) {
- /* If we were running LE only scan, change discovery
- * state. If we were running both LE and BR/EDR inquiry
- * simultaneously, and BR/EDR inquiry is already
- * finished, stop discovery, otherwise BR/EDR inquiry
- * will stop discovery when finished. If we will resolve
- * remote device name, do not change discovery state.
- */
- if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
- hdev->discovery.state != DISCOVERY_RESOLVING)
- hci_discovery_set_state(hdev,
- DISCOVERY_STOPPED);
- } else {
- struct hci_request req;
-
- hci_inquiry_cache_flush(hdev);
-
- hci_req_init(&req, hdev);
-
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, sizeof(cp.lap));
- cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
- hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
-
- err = hci_req_run(&req, inquiry_complete);
- if (err) {
- BT_ERR("Inquiry request failed: err %d", err);
- hci_discovery_set_state(hdev,
- DISCOVERY_STOPPED);
- }
- }
-
- hci_dev_unlock(hdev);
- break;
- }
-}
-
-static void le_scan_disable_work(struct work_struct *work)
-{
- struct hci_dev *hdev = container_of(work, struct hci_dev,
- le_scan_disable.work);
- struct hci_request req;
- int err;
-
- BT_DBG("%s", hdev->name);
-
- cancel_delayed_work_sync(&hdev->le_scan_restart);
-
- hci_req_init(&req, hdev);
-
- hci_req_add_le_scan_disable(&req);
-
- err = hci_req_run(&req, le_scan_disable_work_complete);
- if (err)
- BT_ERR("Disable LE scanning request failed: err %d", err);
-}
-
-static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- unsigned long timeout, duration, scan_start, now;
-
- BT_DBG("%s", hdev->name);
-
- if (status) {
- BT_ERR("Failed to restart LE scan: status %d", status);
- return;
- }
-
- if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
- !hdev->discovery.scan_start)
- return;
-
- /* When the scan was started, hdev->le_scan_disable has been queued
- * after duration from scan_start. During scan restart this job
- * has been canceled, and we need to queue it again after proper
- * timeout, to make sure that scan does not run indefinitely.
- */
- duration = hdev->discovery.scan_duration;
- scan_start = hdev->discovery.scan_start;
- now = jiffies;
- if (now - scan_start <= duration) {
- int elapsed;
-
- if (now >= scan_start)
- elapsed = now - scan_start;
- else
- elapsed = ULONG_MAX - scan_start + now;
-
- timeout = duration - elapsed;
- } else {
- timeout = 0;
- }
- queue_delayed_work(hdev->workqueue,
- &hdev->le_scan_disable, timeout);
-}
-
-static void le_scan_restart_work(struct work_struct *work)
-{
- struct hci_dev *hdev = container_of(work, struct hci_dev,
- le_scan_restart.work);
- struct hci_request req;
- struct hci_cp_le_set_scan_enable cp;
- int err;
-
- BT_DBG("%s", hdev->name);
-
- /* If controller is not scanning we are done. */
- if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
- return;
-
- hci_req_init(&req, hdev);
-
- hci_req_add_le_scan_disable(&req);
-
- memset(&cp, 0, sizeof(cp));
- cp.enable = LE_SCAN_ENABLE;
- cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
-
- err = hci_req_run(&req, le_scan_restart_work_complete);
- if (err)
- BT_ERR("Restart LE scan request failed: err %d", err);
-}
-
/* Copy the Identity Address of the controller.
*
* If the controller has a public BD_ADDR, then by default use that one.
@@ -3297,10 +2987,6 @@ struct hci_dev *hci_alloc_dev(void)
INIT_WORK(&hdev->error_reset, hci_error_reset);
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
- INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
- INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
- INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
- INIT_DELAYED_WORK(&hdev->adv_instance_expire, hci_adv_timeout_expire);
skb_queue_head_init(&hdev->rx_q);
skb_queue_head_init(&hdev->cmd_q);
@@ -3310,6 +2996,8 @@ struct hci_dev *hci_alloc_dev(void)
INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout);
+ hci_request_setup(hdev);
+
hci_init_sysfs(hdev);
discovery_init(hdev);
@@ -3520,7 +3208,7 @@ int hci_reset_dev(struct hci_dev *hdev)
if (!skb)
return -ENOMEM;
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
memcpy(skb_put(skb, 3), hw_err, 3);
/* Send Hardware Error to upper stack */
@@ -3537,9 +3225,9 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
return -ENXIO;
}
- if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
kfree_skb(skb);
return -EINVAL;
}
@@ -3561,7 +3249,7 @@ EXPORT_SYMBOL(hci_recv_frame);
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb)
{
/* Mark as diagnostic packet */
- bt_cb(skb)->pkt_type = HCI_DIAG_PKT;
+ hci_skb_pkt_type(skb) = HCI_DIAG_PKT;
/* Time stamp */
__net_timestamp(skb);
@@ -3603,7 +3291,8 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
int err;
- BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
+ BT_DBG("%s type %d len %d", hdev->name, hci_skb_pkt_type(skb),
+ skb->len);
/* Time stamp */
__net_timestamp(skb);
@@ -3648,7 +3337,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->hci.req_start = true;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_START;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -3685,9 +3374,9 @@ struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen);
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return skb;
}
@@ -3716,7 +3405,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
skb->len = skb_headlen(skb);
skb->data_len = 0;
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+ hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
switch (hdev->dev_type) {
case HCI_BREDR:
@@ -3756,7 +3445,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
do {
skb = list; list = list->next;
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+ hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
hci_add_acl_hdr(skb, conn->handle, flags);
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
@@ -3794,7 +3483,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
skb_reset_transport_header(skb);
memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE);
- bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+ hci_skb_pkt_type(skb) = HCI_SCODATA_PKT;
skb_queue_tail(&conn->data_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
@@ -4345,7 +4034,7 @@ static bool hci_req_is_complete(struct hci_dev *hdev)
if (!skb)
return true;
- return bt_cb(skb)->hci.req_start;
+ return (bt_cb(skb)->hci.req_flags & HCI_REQ_START);
}
static void hci_resend_last(struct hci_dev *hdev)
@@ -4405,20 +4094,20 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
* callback would be found in hdev->sent_cmd instead of the
* command queue (hdev->cmd_q).
*/
- if (bt_cb(hdev->sent_cmd)->hci.req_complete) {
- *req_complete = bt_cb(hdev->sent_cmd)->hci.req_complete;
+ if (bt_cb(hdev->sent_cmd)->hci.req_flags & HCI_REQ_SKB) {
+ *req_complete_skb = bt_cb(hdev->sent_cmd)->hci.req_complete_skb;
return;
}
- if (bt_cb(hdev->sent_cmd)->hci.req_complete_skb) {
- *req_complete_skb = bt_cb(hdev->sent_cmd)->hci.req_complete_skb;
+ if (bt_cb(hdev->sent_cmd)->hci.req_complete) {
+ *req_complete = bt_cb(hdev->sent_cmd)->hci.req_complete;
return;
}
/* Remove all pending commands belonging to this request */
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
- if (bt_cb(skb)->hci.req_start) {
+ if (bt_cb(skb)->hci.req_flags & HCI_REQ_START) {
__skb_queue_head(&hdev->cmd_q, skb);
break;
}
@@ -4453,7 +4142,7 @@ static void hci_rx_work(struct work_struct *work)
if (test_bit(HCI_INIT, &hdev->flags)) {
/* Don't process data packets in this states. */
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
kfree_skb(skb);
@@ -4462,7 +4151,7 @@ static void hci_rx_work(struct work_struct *work)
}
/* Process frame */
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_EVENT_PKT:
BT_DBG("%s Event packet", hdev->name);
hci_event_packet(hdev, skb);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d57c11c1c6b5..c162af5d16bf 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1183,7 +1183,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) &&
hdev->discovery.state == DISCOVERY_FINDING)
- mgmt_reenable_advertising(hdev);
+ hci_req_reenable_advertising(hdev);
break;
@@ -2176,7 +2176,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
sizeof(cp), &cp);
- hci_update_page_scan(hdev);
+ hci_req_update_scan(hdev);
}
/* Set packet type for incoming connection */
@@ -2362,7 +2362,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
hci_remove_link_key(hdev, &conn->dst);
- hci_update_page_scan(hdev);
+ hci_req_update_scan(hdev);
}
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
@@ -2401,7 +2401,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
* is timed out due to Directed Advertising."
*/
if (type == LE_LINK)
- mgmt_reenable_advertising(hdev);
+ hci_req_reenable_advertising(hdev);
unlock:
hci_dev_unlock(hdev);
@@ -3833,9 +3833,9 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
data.ssp_mode = 0x01;
if (hci_dev_test_flag(hdev, HCI_MGMT))
- name_known = eir_has_data_type(info->data,
- sizeof(info->data),
- EIR_NAME_COMPLETE);
+ name_known = eir_get_data(info->data,
+ sizeof(info->data),
+ EIR_NAME_COMPLETE, NULL);
else
name_known = true;
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 981f8a202c27..41b5f3813f02 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -21,12 +21,19 @@
SOFTWARE IS DISCLAIMED.
*/
+#include <asm/unaligned.h>
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/mgmt.h>
#include "smp.h"
#include "hci_request.h"
+#define HCI_REQ_DONE 0
+#define HCI_REQ_PEND 1
+#define HCI_REQ_CANCELED 2
+
void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
{
skb_queue_head_init(&req->cmd_q);
@@ -56,8 +63,12 @@ static int req_run(struct hci_request *req, hci_req_complete_t complete,
return -ENODATA;
skb = skb_peek_tail(&req->cmd_q);
- bt_cb(skb)->hci.req_complete = complete;
- bt_cb(skb)->hci.req_complete_skb = complete_skb;
+ if (complete) {
+ bt_cb(skb)->hci.req_complete = complete;
+ } else if (complete_skb) {
+ bt_cb(skb)->hci.req_complete_skb = complete_skb;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_SKB;
+ }
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
@@ -78,6 +89,203 @@ int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
return req_run(req, NULL, complete);
}
+static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
+ struct sk_buff *skb)
+{
+ BT_DBG("%s result 0x%2.2x", hdev->name, result);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = result;
+ hdev->req_status = HCI_REQ_DONE;
+ if (skb)
+ hdev->req_skb = skb_get(skb);
+ wake_up_interruptible(&hdev->req_wait_q);
+ }
+}
+
+void hci_req_sync_cancel(struct hci_dev *hdev, int err)
+{
+ BT_DBG("%s err 0x%2.2x", hdev->name, err);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = err;
+ hdev->req_status = HCI_REQ_CANCELED;
+ wake_up_interruptible(&hdev->req_wait_q);
+ }
+}
+
+struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
+ const void *param, u8 event, u32 timeout)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct hci_request req;
+ struct sk_buff *skb;
+ int err = 0;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_req_init(&req, hdev);
+
+ hci_req_add_ev(&req, opcode, plen, param, event);
+
+ hdev->req_status = HCI_REQ_PEND;
+
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
+ if (err < 0) {
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+ return ERR_PTR(err);
+ }
+
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
+ if (signal_pending(current))
+ return ERR_PTR(-EINTR);
+
+ switch (hdev->req_status) {
+ case HCI_REQ_DONE:
+ err = -bt_to_errno(hdev->req_result);
+ break;
+
+ case HCI_REQ_CANCELED:
+ err = -hdev->req_result;
+ break;
+
+ default:
+ err = -ETIMEDOUT;
+ break;
+ }
+
+ hdev->req_status = hdev->req_result = 0;
+ skb = hdev->req_skb;
+ hdev->req_skb = NULL;
+
+ BT_DBG("%s end: err %d", hdev->name, err);
+
+ if (err < 0) {
+ kfree_skb(skb);
+ return ERR_PTR(err);
+ }
+
+ if (!skb)
+ return ERR_PTR(-ENODATA);
+
+ return skb;
+}
+EXPORT_SYMBOL(__hci_cmd_sync_ev);
+
+struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
+ const void *param, u32 timeout)
+{
+ return __hci_cmd_sync_ev(hdev, opcode, plen, param, 0, timeout);
+}
+EXPORT_SYMBOL(__hci_cmd_sync);
+
+/* Execute request and wait for completion. */
+int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status)
+{
+ struct hci_request req;
+ DECLARE_WAITQUEUE(wait, current);
+ int err = 0;
+
+ BT_DBG("%s start", hdev->name);
+
+ hci_req_init(&req, hdev);
+
+ hdev->req_status = HCI_REQ_PEND;
+
+ err = func(&req, opt);
+ if (err) {
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+ return err;
+ }
+
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
+ if (err < 0) {
+ hdev->req_status = 0;
+
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+
+ /* ENODATA means the HCI request command queue is empty.
+ * This can happen when a request with conditionals doesn't
+ * trigger any commands to be sent. This is normal behavior
+ * and should not trigger an error return.
+ */
+ if (err == -ENODATA) {
+ if (hci_status)
+ *hci_status = 0;
+ return 0;
+ }
+
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+
+ return err;
+ }
+
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ switch (hdev->req_status) {
+ case HCI_REQ_DONE:
+ err = -bt_to_errno(hdev->req_result);
+ if (hci_status)
+ *hci_status = hdev->req_result;
+ break;
+
+ case HCI_REQ_CANCELED:
+ err = -hdev->req_result;
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+ break;
+
+ default:
+ err = -ETIMEDOUT;
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+ break;
+ }
+
+ hdev->req_status = hdev->req_result = 0;
+
+ BT_DBG("%s end: err %d", hdev->name, err);
+
+ return err;
+}
+
+int hci_req_sync(struct hci_dev *hdev, int (*req)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status)
+{
+ int ret;
+
+ if (!test_bit(HCI_UP, &hdev->flags))
+ return -ENETDOWN;
+
+ /* Serialize all requests */
+ hci_req_sync_lock(hdev);
+ ret = __hci_req_sync(hdev, req, opt, timeout, hci_status);
+ hci_req_sync_unlock(hdev);
+
+ return ret;
+}
+
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param)
{
@@ -98,8 +306,8 @@ struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
BT_DBG("skb len %d", skb->len);
- bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
- bt_cb(skb)->hci.opcode = opcode;
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
+ hci_skb_opcode(skb) = opcode;
return skb;
}
@@ -128,7 +336,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
}
if (skb_queue_empty(&req->cmd_q))
- bt_cb(skb)->hci.req_start = true;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_START;
bt_cb(skb)->hci.req_event = event;
@@ -141,6 +349,311 @@ void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
hci_req_add_ev(req, opcode, plen, param, 0);
}
+void __hci_req_write_fast_connectable(struct hci_request *req, bool enable)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_page_scan_activity acp;
+ u8 type;
+
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+ return;
+
+ if (hdev->hci_ver < BLUETOOTH_VER_1_2)
+ return;
+
+ if (enable) {
+ type = PAGE_SCAN_TYPE_INTERLACED;
+
+ /* 160 msec page scan interval */
+ acp.interval = cpu_to_le16(0x0100);
+ } else {
+ type = PAGE_SCAN_TYPE_STANDARD; /* default */
+
+ /* default 1.28 sec page scan */
+ acp.interval = cpu_to_le16(0x0800);
+ }
+
+ acp.window = cpu_to_le16(0x0012);
+
+ if (__cpu_to_le16(hdev->page_scan_interval) != acp.interval ||
+ __cpu_to_le16(hdev->page_scan_window) != acp.window)
+ hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
+ sizeof(acp), &acp);
+
+ if (hdev->page_scan_type != type)
+ hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
+}
+
+/* This function controls the background scanning based on hdev->pend_le_conns
+ * list. If there are pending LE connection we start the background scanning,
+ * otherwise we stop it.
+ *
+ * This function requires the caller holds hdev->lock.
+ */
+static void __hci_update_background_scan(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+
+ if (!test_bit(HCI_UP, &hdev->flags) ||
+ test_bit(HCI_INIT, &hdev->flags) ||
+ hci_dev_test_flag(hdev, HCI_SETUP) ||
+ hci_dev_test_flag(hdev, HCI_CONFIG) ||
+ hci_dev_test_flag(hdev, HCI_AUTO_OFF) ||
+ hci_dev_test_flag(hdev, HCI_UNREGISTER))
+ return;
+
+ /* No point in doing scanning if LE support hasn't been enabled */
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ return;
+
+ /* If discovery is active don't interfere with it */
+ if (hdev->discovery.state != DISCOVERY_STOPPED)
+ return;
+
+ /* Reset RSSI and UUID filters when starting background scanning
+ * since these filters are meant for service discovery only.
+ *
+ * The Start Discovery and Start Service Discovery operations
+ * ensure to set proper values for RSSI threshold and UUID
+ * filter list. So it is safe to just reset them here.
+ */
+ hci_discovery_filter_clear(hdev);
+
+ if (list_empty(&hdev->pend_le_conns) &&
+ list_empty(&hdev->pend_le_reports)) {
+ /* If there is no pending LE connections or devices
+ * to be scanned for, we should stop the background
+ * scanning.
+ */
+
+ /* If controller is not scanning we are done. */
+ if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ return;
+
+ hci_req_add_le_scan_disable(req);
+
+ BT_DBG("%s stopping background scanning", hdev->name);
+ } else {
+ /* If there is at least one pending LE connection, we should
+ * keep the background scan running.
+ */
+
+ /* If controller is connecting, we should not start scanning
+ * since some controllers are not able to scan and connect at
+ * the same time.
+ */
+ if (hci_lookup_le_connect(hdev))
+ return;
+
+ /* If controller is currently scanning, we stop it to ensure we
+ * don't miss any advertising (due to duplicates filter).
+ */
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ hci_req_add_le_scan_disable(req);
+
+ hci_req_add_le_passive_scan(req);
+
+ BT_DBG("%s starting background scanning", hdev->name);
+ }
+}
+
+void __hci_req_update_name(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_local_name cp;
+
+ memcpy(cp.name, hdev->dev_name, sizeof(cp.name));
+
+ hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
+}
+
+#define PNP_INFO_SVCLASS_ID 0x1200
+
+static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+ u8 *ptr = data, *uuids_start = NULL;
+ struct bt_uuid *uuid;
+
+ if (len < 4)
+ return ptr;
+
+ list_for_each_entry(uuid, &hdev->uuids, list) {
+ u16 uuid16;
+
+ if (uuid->size != 16)
+ continue;
+
+ uuid16 = get_unaligned_le16(&uuid->uuid[12]);
+ if (uuid16 < 0x1100)
+ continue;
+
+ if (uuid16 == PNP_INFO_SVCLASS_ID)
+ continue;
+
+ if (!uuids_start) {
+ uuids_start = ptr;
+ uuids_start[0] = 1;
+ uuids_start[1] = EIR_UUID16_ALL;
+ ptr += 2;
+ }
+
+ /* Stop if not enough space to put next UUID */
+ if ((ptr - data) + sizeof(u16) > len) {
+ uuids_start[1] = EIR_UUID16_SOME;
+ break;
+ }
+
+ *ptr++ = (uuid16 & 0x00ff);
+ *ptr++ = (uuid16 & 0xff00) >> 8;
+ uuids_start[0] += sizeof(uuid16);
+ }
+
+ return ptr;
+}
+
+static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+ u8 *ptr = data, *uuids_start = NULL;
+ struct bt_uuid *uuid;
+
+ if (len < 6)
+ return ptr;
+
+ list_for_each_entry(uuid, &hdev->uuids, list) {
+ if (uuid->size != 32)
+ continue;
+
+ if (!uuids_start) {
+ uuids_start = ptr;
+ uuids_start[0] = 1;
+ uuids_start[1] = EIR_UUID32_ALL;
+ ptr += 2;
+ }
+
+ /* Stop if not enough space to put next UUID */
+ if ((ptr - data) + sizeof(u32) > len) {
+ uuids_start[1] = EIR_UUID32_SOME;
+ break;
+ }
+
+ memcpy(ptr, &uuid->uuid[12], sizeof(u32));
+ ptr += sizeof(u32);
+ uuids_start[0] += sizeof(u32);
+ }
+
+ return ptr;
+}
+
+static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+ u8 *ptr = data, *uuids_start = NULL;
+ struct bt_uuid *uuid;
+
+ if (len < 18)
+ return ptr;
+
+ list_for_each_entry(uuid, &hdev->uuids, list) {
+ if (uuid->size != 128)
+ continue;
+
+ if (!uuids_start) {
+ uuids_start = ptr;
+ uuids_start[0] = 1;
+ uuids_start[1] = EIR_UUID128_ALL;
+ ptr += 2;
+ }
+
+ /* Stop if not enough space to put next UUID */
+ if ((ptr - data) + 16 > len) {
+ uuids_start[1] = EIR_UUID128_SOME;
+ break;
+ }
+
+ memcpy(ptr, uuid->uuid, 16);
+ ptr += 16;
+ uuids_start[0] += 16;
+ }
+
+ return ptr;
+}
+
+static void create_eir(struct hci_dev *hdev, u8 *data)
+{
+ u8 *ptr = data;
+ size_t name_len;
+
+ name_len = strlen(hdev->dev_name);
+
+ if (name_len > 0) {
+ /* EIR Data type */
+ if (name_len > 48) {
+ name_len = 48;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ /* EIR Data length */
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ptr += (name_len + 2);
+ }
+
+ if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
+ ptr[0] = 2;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8) hdev->inq_tx_power;
+
+ ptr += 3;
+ }
+
+ if (hdev->devid_source > 0) {
+ ptr[0] = 9;
+ ptr[1] = EIR_DEVICE_ID;
+
+ put_unaligned_le16(hdev->devid_source, ptr + 2);
+ put_unaligned_le16(hdev->devid_vendor, ptr + 4);
+ put_unaligned_le16(hdev->devid_product, ptr + 6);
+ put_unaligned_le16(hdev->devid_version, ptr + 8);
+
+ ptr += 10;
+ }
+
+ ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+ ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+ ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+}
+
+void __hci_req_update_eir(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_eir cp;
+
+ if (!hdev_is_powered(hdev))
+ return;
+
+ if (!lmp_ext_inq_capable(hdev))
+ return;
+
+ if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
+ return;
+
+ if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ create_eir(hdev, cp.data);
+
+ if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
+ return;
+
+ memcpy(hdev->eir, cp.data, sizeof(cp.data));
+
+ hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+}
+
void hci_req_add_le_scan_disable(struct hci_request *req)
{
struct hci_cp_le_set_scan_enable cp;
@@ -302,6 +815,483 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
&enable_cp);
}
+static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
+{
+ u8 instance = hdev->cur_adv_instance;
+ struct adv_info *adv_instance;
+
+ /* Ignore instance 0 */
+ if (instance == 0x00)
+ return 0;
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return 0;
+
+ /* TODO: Take into account the "appearance" and "local-name" flags here.
+ * These are currently being ignored as they are not supported.
+ */
+ return adv_instance->scan_rsp_len;
+}
+
+void __hci_req_disable_advertising(struct hci_request *req)
+{
+ u8 enable = 0x00;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
+{
+ u32 flags;
+ struct adv_info *adv_instance;
+
+ if (instance == 0x00) {
+ /* Instance 0 always manages the "Tx Power" and "Flags"
+ * fields
+ */
+ flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
+
+ /* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
+ * corresponds to the "connectable" instance flag.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
+ flags |= MGMT_ADV_FLAG_CONNECTABLE;
+
+ return flags;
+ }
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+
+ /* Return 0 when we got an invalid instance identifier. */
+ if (!adv_instance)
+ return 0;
+
+ return adv_instance->flags;
+}
+
+void __hci_req_enable_advertising(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_adv_param cp;
+ u8 own_addr_type, enable = 0x01;
+ bool connectable;
+ u32 flags;
+
+ if (hci_conn_num(hdev, LE_LINK) > 0)
+ return;
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV))
+ __hci_req_disable_advertising(req);
+
+ /* Clear the HCI_LE_ADV bit temporarily so that the
+ * hci_update_random_address knows that it's safe to go ahead
+ * and write a new random address. The flag will be set back on
+ * as soon as the SET_ADV_ENABLE HCI command completes.
+ */
+ hci_dev_clear_flag(hdev, HCI_LE_ADV);
+
+ flags = get_adv_instance_flags(hdev, hdev->cur_adv_instance);
+
+ /* If the "connectable" instance flag was not set, then choose between
+ * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
+ */
+ connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
+ mgmt_get_connectable(hdev);
+
+ /* Set require_privacy to true only when non-connectable
+ * advertising is used. In that case it is fine to use a
+ * non-resolvable private address.
+ */
+ if (hci_update_random_address(req, !connectable, &own_addr_type) < 0)
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
+ cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
+
+ if (connectable)
+ cp.type = LE_ADV_IND;
+ else if (get_cur_adv_instance_scan_rsp_len(hdev))
+ cp.type = LE_ADV_SCAN_IND;
+ else
+ cp.type = LE_ADV_NONCONN_IND;
+
+ cp.own_address_type = own_addr_type;
+ cp.channel_map = hdev->le_adv_channel_map;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0;
+ size_t name_len;
+
+ name_len = strlen(hdev->dev_name);
+ if (name_len > 0) {
+ size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+ if (name_len > max_len) {
+ name_len = max_len;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ad_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ return ad_len;
+}
+
+static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
+ u8 *ptr)
+{
+ struct adv_info *adv_instance;
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return 0;
+
+ /* TODO: Set the appropriate entries based on advertising instance flags
+ * here once flags other than 0 are supported.
+ */
+ memcpy(ptr, adv_instance->scan_rsp_data,
+ adv_instance->scan_rsp_len);
+
+ return adv_instance->scan_rsp_len;
+}
+
+void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_rsp_data cp;
+ u8 len;
+
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (instance)
+ len = create_instance_scan_rsp_data(hdev, instance, cp.data);
+ else
+ len = create_default_scan_rsp_data(hdev, cp.data);
+
+ if (hdev->scan_rsp_data_len == len &&
+ !memcmp(cp.data, hdev->scan_rsp_data, len))
+ return;
+
+ memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
+ hdev->scan_rsp_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
+}
+
+static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
+{
+ struct adv_info *adv_instance = NULL;
+ u8 ad_len = 0, flags = 0;
+ u32 instance_flags;
+
+ /* Return 0 when the current instance identifier is invalid. */
+ if (instance) {
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return 0;
+ }
+
+ instance_flags = get_adv_instance_flags(hdev, instance);
+
+ /* The Add Advertising command allows userspace to set both the general
+ * and limited discoverable flags.
+ */
+ if (instance_flags & MGMT_ADV_FLAG_DISCOV)
+ flags |= LE_AD_GENERAL;
+
+ if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV)
+ flags |= LE_AD_LIMITED;
+
+ if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) {
+ /* If a discovery flag wasn't provided, simply use the global
+ * settings.
+ */
+ if (!flags)
+ flags |= mgmt_get_adv_discov_flags(hdev);
+
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+ flags |= LE_AD_NO_BREDR;
+
+ /* If flags would still be empty, then there is no need to
+ * include the "Flags" AD field".
+ */
+ if (flags) {
+ ptr[0] = 0x02;
+ ptr[1] = EIR_FLAGS;
+ ptr[2] = flags;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+ }
+
+ if (adv_instance) {
+ memcpy(ptr, adv_instance->adv_data,
+ adv_instance->adv_data_len);
+ ad_len += adv_instance->adv_data_len;
+ ptr += adv_instance->adv_data_len;
+ }
+
+ /* Provide Tx Power only if we can provide a valid value for it */
+ if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
+ (instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
+ ptr[0] = 0x02;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8)hdev->adv_tx_power;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ return ad_len;
+}
+
+void __hci_req_update_adv_data(struct hci_request *req, u8 instance)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_adv_data cp;
+ u8 len;
+
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_instance_adv_data(hdev, instance, cp.data);
+
+ /* There's nothing to do if the data hasn't changed */
+ if (hdev->adv_data_len == len &&
+ memcmp(cp.data, hdev->adv_data, len) == 0)
+ return;
+
+ memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+ hdev->adv_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
+int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance)
+{
+ struct hci_request req;
+
+ hci_req_init(&req, hdev);
+ __hci_req_update_adv_data(&req, instance);
+
+ return hci_req_run(&req, NULL);
+}
+
+static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+ BT_DBG("%s status %u", hdev->name, status);
+}
+
+void hci_req_reenable_advertising(struct hci_dev *hdev)
+{
+ struct hci_request req;
+
+ if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
+ list_empty(&hdev->adv_instances))
+ return;
+
+ hci_req_init(&req, hdev);
+
+ if (hdev->cur_adv_instance) {
+ __hci_req_schedule_adv_instance(&req, hdev->cur_adv_instance,
+ true);
+ } else {
+ __hci_req_update_adv_data(&req, 0x00);
+ __hci_req_update_scan_rsp_data(&req, 0x00);
+ __hci_req_enable_advertising(&req);
+ }
+
+ hci_req_run(&req, adv_enable_complete);
+}
+
+static void adv_timeout_expire(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ adv_instance_expire.work);
+
+ struct hci_request req;
+ u8 instance;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ hdev->adv_instance_timeout = 0;
+
+ instance = hdev->cur_adv_instance;
+ if (instance == 0x00)
+ goto unlock;
+
+ hci_req_init(&req, hdev);
+
+ hci_req_clear_adv_instance(hdev, &req, instance, false);
+
+ if (list_empty(&hdev->adv_instances))
+ __hci_req_disable_advertising(&req);
+
+ hci_req_run(&req, NULL);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
+ bool force)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct adv_info *adv_instance = NULL;
+ u16 timeout;
+
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ list_empty(&hdev->adv_instances))
+ return -EPERM;
+
+ if (hdev->adv_instance_timeout)
+ return -EBUSY;
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return -ENOENT;
+
+ /* A zero timeout means unlimited advertising. As long as there is
+ * only one instance, duration should be ignored. We still set a timeout
+ * in case further instances are being added later on.
+ *
+ * If the remaining lifetime of the instance is more than the duration
+ * then the timeout corresponds to the duration, otherwise it will be
+ * reduced to the remaining instance lifetime.
+ */
+ if (adv_instance->timeout == 0 ||
+ adv_instance->duration <= adv_instance->remaining_time)
+ timeout = adv_instance->duration;
+ else
+ timeout = adv_instance->remaining_time;
+
+ /* The remaining time is being reduced unless the instance is being
+ * advertised without time limit.
+ */
+ if (adv_instance->timeout)
+ adv_instance->remaining_time =
+ adv_instance->remaining_time - timeout;
+
+ hdev->adv_instance_timeout = timeout;
+ queue_delayed_work(hdev->req_workqueue,
+ &hdev->adv_instance_expire,
+ msecs_to_jiffies(timeout * 1000));
+
+ /* If we're just re-scheduling the same instance again then do not
+ * execute any HCI commands. This happens when a single instance is
+ * being advertised.
+ */
+ if (!force && hdev->cur_adv_instance == instance &&
+ hci_dev_test_flag(hdev, HCI_LE_ADV))
+ return 0;
+
+ hdev->cur_adv_instance = instance;
+ __hci_req_update_adv_data(req, instance);
+ __hci_req_update_scan_rsp_data(req, instance);
+ __hci_req_enable_advertising(req);
+
+ return 0;
+}
+
+static void cancel_adv_timeout(struct hci_dev *hdev)
+{
+ if (hdev->adv_instance_timeout) {
+ hdev->adv_instance_timeout = 0;
+ cancel_delayed_work(&hdev->adv_instance_expire);
+ }
+}
+
+/* For a single instance:
+ * - force == true: The instance will be removed even when its remaining
+ * lifetime is not zero.
+ * - force == false: the instance will be deactivated but kept stored unless
+ * the remaining lifetime is zero.
+ *
+ * For instance == 0x00:
+ * - force == true: All instances will be removed regardless of their timeout
+ * setting.
+ * - force == false: Only instances that have a timeout will be removed.
+ */
+void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
+ u8 instance, bool force)
+{
+ struct adv_info *adv_instance, *n, *next_instance = NULL;
+ int err;
+ u8 rem_inst;
+
+ /* Cancel any timeout concerning the removed instance(s). */
+ if (!instance || hdev->cur_adv_instance == instance)
+ cancel_adv_timeout(hdev);
+
+ /* Get the next instance to advertise BEFORE we remove
+ * the current one. This can be the same instance again
+ * if there is only one instance.
+ */
+ if (instance && hdev->cur_adv_instance == instance)
+ next_instance = hci_get_next_instance(hdev, instance);
+
+ if (instance == 0x00) {
+ list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
+ list) {
+ if (!(force || adv_instance->timeout))
+ continue;
+
+ rem_inst = adv_instance->instance;
+ err = hci_remove_adv_instance(hdev, rem_inst);
+ if (!err)
+ mgmt_advertising_removed(NULL, hdev, rem_inst);
+ }
+ } else {
+ adv_instance = hci_find_adv_instance(hdev, instance);
+
+ if (force || (adv_instance && adv_instance->timeout &&
+ !adv_instance->remaining_time)) {
+ /* Don't advertise a removed instance. */
+ if (next_instance &&
+ next_instance->instance == instance)
+ next_instance = NULL;
+
+ err = hci_remove_adv_instance(hdev, instance);
+ if (!err)
+ mgmt_advertising_removed(NULL, hdev, instance);
+ }
+ }
+
+ if (!req || !hdev_is_powered(hdev) ||
+ hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ return;
+
+ if (next_instance)
+ __hci_req_schedule_adv_instance(req, next_instance->instance,
+ false);
+}
+
static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
{
struct hci_dev *hdev = req->hdev;
@@ -432,7 +1422,7 @@ static bool disconnected_whitelist_entries(struct hci_dev *hdev)
return false;
}
-void __hci_update_page_scan(struct hci_request *req)
+void __hci_req_update_scan(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
u8 scan;
@@ -452,117 +1442,168 @@ void __hci_update_page_scan(struct hci_request *req)
else
scan = SCAN_DISABLED;
- if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
- return;
-
if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
scan |= SCAN_INQUIRY;
+ if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE) &&
+ test_bit(HCI_ISCAN, &hdev->flags) == !!(scan & SCAN_INQUIRY))
+ return;
+
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
-void hci_update_page_scan(struct hci_dev *hdev)
+static int update_scan(struct hci_request *req, unsigned long opt)
{
- struct hci_request req;
+ hci_dev_lock(req->hdev);
+ __hci_req_update_scan(req);
+ hci_dev_unlock(req->hdev);
+ return 0;
+}
- hci_req_init(&req, hdev);
- __hci_update_page_scan(&req);
- hci_req_run(&req, NULL);
+static void scan_update_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev, scan_update);
+
+ hci_req_sync(hdev, update_scan, 0, HCI_CMD_TIMEOUT, NULL);
}
-/* This function controls the background scanning based on hdev->pend_le_conns
- * list. If there are pending LE connection we start the background scanning,
- * otherwise we stop it.
- *
- * This function requires the caller holds hdev->lock.
- */
-void __hci_update_background_scan(struct hci_request *req)
+static int connectable_update(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
- if (!test_bit(HCI_UP, &hdev->flags) ||
- test_bit(HCI_INIT, &hdev->flags) ||
- hci_dev_test_flag(hdev, HCI_SETUP) ||
- hci_dev_test_flag(hdev, HCI_CONFIG) ||
- hci_dev_test_flag(hdev, HCI_AUTO_OFF) ||
- hci_dev_test_flag(hdev, HCI_UNREGISTER))
+ hci_dev_lock(hdev);
+
+ __hci_req_update_scan(req);
+
+ /* If BR/EDR is not enabled and we disable advertising as a
+ * by-product of disabling connectable, we need to update the
+ * advertising flags.
+ */
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+ __hci_req_update_adv_data(req, hdev->cur_adv_instance);
+
+ /* Update the advertising parameters if necessary */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ !list_empty(&hdev->adv_instances))
+ __hci_req_enable_advertising(req);
+
+ __hci_update_background_scan(req);
+
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static void connectable_update_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ connectable_update);
+ u8 status;
+
+ hci_req_sync(hdev, connectable_update, 0, HCI_CMD_TIMEOUT, &status);
+ mgmt_set_connectable_complete(hdev, status);
+}
+
+static u8 get_service_classes(struct hci_dev *hdev)
+{
+ struct bt_uuid *uuid;
+ u8 val = 0;
+
+ list_for_each_entry(uuid, &hdev->uuids, list)
+ val |= uuid->svc_hint;
+
+ return val;
+}
+
+void __hci_req_update_class(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ u8 cod[3];
+
+ BT_DBG("%s", hdev->name);
+
+ if (!hdev_is_powered(hdev))
return;
- /* No point in doing scanning if LE support hasn't been enabled */
- if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
return;
- /* If discovery is active don't interfere with it */
- if (hdev->discovery.state != DISCOVERY_STOPPED)
+ if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
return;
- /* Reset RSSI and UUID filters when starting background scanning
- * since these filters are meant for service discovery only.
- *
- * The Start Discovery and Start Service Discovery operations
- * ensure to set proper values for RSSI threshold and UUID
- * filter list. So it is safe to just reset them here.
- */
- hci_discovery_filter_clear(hdev);
+ cod[0] = hdev->minor_class;
+ cod[1] = hdev->major_class;
+ cod[2] = get_service_classes(hdev);
- if (list_empty(&hdev->pend_le_conns) &&
- list_empty(&hdev->pend_le_reports)) {
- /* If there is no pending LE connections or devices
- * to be scanned for, we should stop the background
- * scanning.
- */
+ if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
+ cod[1] |= 0x20;
- /* If controller is not scanning we are done. */
- if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
- return;
+ if (memcmp(cod, hdev->dev_class, 3) == 0)
+ return;
- hci_req_add_le_scan_disable(req);
+ hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+}
- BT_DBG("%s stopping background scanning", hdev->name);
+static void write_iac(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_current_iac_lap cp;
+
+ if (!hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
+ return;
+
+ if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) {
+ /* Limited discoverable mode */
+ cp.num_iac = min_t(u8, hdev->num_iac, 2);
+ cp.iac_lap[0] = 0x00; /* LIAC */
+ cp.iac_lap[1] = 0x8b;
+ cp.iac_lap[2] = 0x9e;
+ cp.iac_lap[3] = 0x33; /* GIAC */
+ cp.iac_lap[4] = 0x8b;
+ cp.iac_lap[5] = 0x9e;
} else {
- /* If there is at least one pending LE connection, we should
- * keep the background scan running.
- */
+ /* General discoverable mode */
+ cp.num_iac = 1;
+ cp.iac_lap[0] = 0x33; /* GIAC */
+ cp.iac_lap[1] = 0x8b;
+ cp.iac_lap[2] = 0x9e;
+ }
- /* If controller is connecting, we should not start scanning
- * since some controllers are not able to scan and connect at
- * the same time.
- */
- if (hci_lookup_le_connect(hdev))
- return;
+ hci_req_add(req, HCI_OP_WRITE_CURRENT_IAC_LAP,
+ (cp.num_iac * 3) + 1, &cp);
+}
- /* If controller is currently scanning, we stop it to ensure we
- * don't miss any advertising (due to duplicates filter).
- */
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
- hci_req_add_le_scan_disable(req);
+static int discoverable_update(struct hci_request *req, unsigned long opt)
+{
+ struct hci_dev *hdev = req->hdev;
- hci_req_add_le_passive_scan(req);
+ hci_dev_lock(hdev);
- BT_DBG("%s starting background scanning", hdev->name);
+ if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
+ write_iac(req);
+ __hci_req_update_scan(req);
+ __hci_req_update_class(req);
}
-}
-static void update_background_scan_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- if (status)
- BT_DBG("HCI request failed to update background scanning: "
- "status 0x%2.2x", status);
-}
+ /* Advertising instances don't use the global discoverable setting, so
+ * only update AD if advertising was enabled using Set Advertising.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ __hci_req_update_adv_data(req, 0x00);
-void hci_update_background_scan(struct hci_dev *hdev)
-{
- int err;
- struct hci_request req;
+ hci_dev_unlock(hdev);
- hci_req_init(&req, hdev);
+ return 0;
+}
- __hci_update_background_scan(&req);
+static void discoverable_update_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ discoverable_update);
+ u8 status;
- err = hci_req_run(&req, update_background_scan_complete);
- if (err && err != -ENODATA)
- BT_ERR("Failed to run HCI request: err %d", err);
+ hci_req_sync(hdev, discoverable_update, 0, HCI_CMD_TIMEOUT, &status);
+ mgmt_set_discoverable_complete(hdev, status);
}
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
@@ -657,3 +1698,574 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
return 0;
}
+
+static int update_bg_scan(struct hci_request *req, unsigned long opt)
+{
+ hci_dev_lock(req->hdev);
+ __hci_update_background_scan(req);
+ hci_dev_unlock(req->hdev);
+ return 0;
+}
+
+static void bg_scan_update(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ bg_scan_update);
+ struct hci_conn *conn;
+ u8 status;
+ int err;
+
+ err = hci_req_sync(hdev, update_bg_scan, 0, HCI_CMD_TIMEOUT, &status);
+ if (!err)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+ if (conn)
+ hci_le_conn_failed(conn, status);
+
+ hci_dev_unlock(hdev);
+}
+
+static int le_scan_disable(struct hci_request *req, unsigned long opt)
+{
+ hci_req_add_le_scan_disable(req);
+ return 0;
+}
+
+static int bredr_inquiry(struct hci_request *req, unsigned long opt)
+{
+ u8 length = opt;
+ const u8 giac[3] = { 0x33, 0x8b, 0x9e };
+ const u8 liac[3] = { 0x00, 0x8b, 0x9e };
+ struct hci_cp_inquiry cp;
+
+ BT_DBG("%s", req->hdev->name);
+
+ hci_dev_lock(req->hdev);
+ hci_inquiry_cache_flush(req->hdev);
+ hci_dev_unlock(req->hdev);
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (req->hdev->discovery.limited)
+ memcpy(&cp.lap, liac, sizeof(cp.lap));
+ else
+ memcpy(&cp.lap, giac, sizeof(cp.lap));
+
+ cp.length = length;
+
+ hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+ return 0;
+}
+
+static void le_scan_disable_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ le_scan_disable.work);
+ u8 status;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ return;
+
+ cancel_delayed_work(&hdev->le_scan_restart);
+
+ hci_req_sync(hdev, le_scan_disable, 0, HCI_CMD_TIMEOUT, &status);
+ if (status) {
+ BT_ERR("Failed to disable LE scan: status 0x%02x", status);
+ return;
+ }
+
+ hdev->discovery.scan_start = 0;
+
+ /* If we were running LE only scan, change discovery state. If
+ * we were running both LE and BR/EDR inquiry simultaneously,
+ * and BR/EDR inquiry is already finished, stop discovery,
+ * otherwise BR/EDR inquiry will stop discovery when finished.
+ * If we will resolve remote device name, do not change
+ * discovery state.
+ */
+
+ if (hdev->discovery.type == DISCOV_TYPE_LE)
+ goto discov_stopped;
+
+ if (hdev->discovery.type != DISCOV_TYPE_INTERLEAVED)
+ return;
+
+ if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks)) {
+ if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
+ hdev->discovery.state != DISCOVERY_RESOLVING)
+ goto discov_stopped;
+
+ return;
+ }
+
+ hci_req_sync(hdev, bredr_inquiry, DISCOV_INTERLEAVED_INQUIRY_LEN,
+ HCI_CMD_TIMEOUT, &status);
+ if (status) {
+ BT_ERR("Inquiry failed: status 0x%02x", status);
+ goto discov_stopped;
+ }
+
+ return;
+
+discov_stopped:
+ hci_dev_lock(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ hci_dev_unlock(hdev);
+}
+
+static int le_scan_restart(struct hci_request *req, unsigned long opt)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_enable cp;
+
+ /* If controller is not scanning we are done. */
+ if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ return 0;
+
+ hci_req_add_le_scan_disable(req);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.enable = LE_SCAN_ENABLE;
+ cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+
+ return 0;
+}
+
+static void le_scan_restart_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ le_scan_restart.work);
+ unsigned long timeout, duration, scan_start, now;
+ u8 status;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_req_sync(hdev, le_scan_restart, 0, HCI_CMD_TIMEOUT, &status);
+ if (status) {
+ BT_ERR("Failed to restart LE scan: status %d", status);
+ return;
+ }
+
+ hci_dev_lock(hdev);
+
+ if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
+ !hdev->discovery.scan_start)
+ goto unlock;
+
+ /* When the scan was started, hdev->le_scan_disable has been queued
+ * after duration from scan_start. During scan restart this job
+ * has been canceled, and we need to queue it again after proper
+ * timeout, to make sure that scan does not run indefinitely.
+ */
+ duration = hdev->discovery.scan_duration;
+ scan_start = hdev->discovery.scan_start;
+ now = jiffies;
+ if (now - scan_start <= duration) {
+ int elapsed;
+
+ if (now >= scan_start)
+ elapsed = now - scan_start;
+ else
+ elapsed = ULONG_MAX - scan_start + now;
+
+ timeout = duration - elapsed;
+ } else {
+ timeout = 0;
+ }
+
+ queue_delayed_work(hdev->req_workqueue,
+ &hdev->le_scan_disable, timeout);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static void disable_advertising(struct hci_request *req)
+{
+ u8 enable = 0x00;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static int active_scan(struct hci_request *req, unsigned long opt)
+{
+ uint16_t interval = opt;
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_param param_cp;
+ struct hci_cp_le_set_scan_enable enable_cp;
+ u8 own_addr_type;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
+ hci_dev_lock(hdev);
+
+ /* Don't let discovery abort an outgoing connection attempt
+ * that's using directed advertising.
+ */
+ if (hci_lookup_le_connect(hdev)) {
+ hci_dev_unlock(hdev);
+ return -EBUSY;
+ }
+
+ cancel_adv_timeout(hdev);
+ hci_dev_unlock(hdev);
+
+ disable_advertising(req);
+ }
+
+ /* If controller is scanning, it means the background scanning is
+ * running. Thus, we should temporarily stop it in order to set the
+ * discovery scanning parameters.
+ */
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ hci_req_add_le_scan_disable(req);
+
+ /* All active scans will be done with either a resolvable private
+ * address (when privacy feature has been enabled) or non-resolvable
+ * private address.
+ */
+ err = hci_update_random_address(req, true, &own_addr_type);
+ if (err < 0)
+ own_addr_type = ADDR_LE_DEV_PUBLIC;
+
+ memset(&param_cp, 0, sizeof(param_cp));
+ param_cp.type = LE_SCAN_ACTIVE;
+ param_cp.interval = cpu_to_le16(interval);
+ param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+ param_cp.own_address_type = own_addr_type;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+ &param_cp);
+
+ memset(&enable_cp, 0, sizeof(enable_cp));
+ enable_cp.enable = LE_SCAN_ENABLE;
+ enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+ &enable_cp);
+
+ return 0;
+}
+
+static int interleaved_discov(struct hci_request *req, unsigned long opt)
+{
+ int err;
+
+ BT_DBG("%s", req->hdev->name);
+
+ err = active_scan(req, opt);
+ if (err)
+ return err;
+
+ return bredr_inquiry(req, DISCOV_BREDR_INQUIRY_LEN);
+}
+
+static void start_discovery(struct hci_dev *hdev, u8 *status)
+{
+ unsigned long timeout;
+
+ BT_DBG("%s type %u", hdev->name, hdev->discovery.type);
+
+ switch (hdev->discovery.type) {
+ case DISCOV_TYPE_BREDR:
+ if (!hci_dev_test_flag(hdev, HCI_INQUIRY))
+ hci_req_sync(hdev, bredr_inquiry,
+ DISCOV_BREDR_INQUIRY_LEN, HCI_CMD_TIMEOUT,
+ status);
+ return;
+ case DISCOV_TYPE_INTERLEAVED:
+ /* When running simultaneous discovery, the LE scanning time
+ * should occupy the whole discovery time sine BR/EDR inquiry
+ * and LE scanning are scheduled by the controller.
+ *
+ * For interleaving discovery in comparison, BR/EDR inquiry
+ * and LE scanning are done sequentially with separate
+ * timeouts.
+ */
+ if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+ &hdev->quirks)) {
+ timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+ /* During simultaneous discovery, we double LE scan
+ * interval. We must leave some time for the controller
+ * to do BR/EDR inquiry.
+ */
+ hci_req_sync(hdev, interleaved_discov,
+ DISCOV_LE_SCAN_INT * 2, HCI_CMD_TIMEOUT,
+ status);
+ break;
+ }
+
+ timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
+ hci_req_sync(hdev, active_scan, DISCOV_LE_SCAN_INT,
+ HCI_CMD_TIMEOUT, status);
+ break;
+ case DISCOV_TYPE_LE:
+ timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+ hci_req_sync(hdev, active_scan, DISCOV_LE_SCAN_INT,
+ HCI_CMD_TIMEOUT, status);
+ break;
+ default:
+ *status = HCI_ERROR_UNSPECIFIED;
+ return;
+ }
+
+ if (*status)
+ return;
+
+ BT_DBG("%s timeout %u ms", hdev->name, jiffies_to_msecs(timeout));
+
+ /* When service discovery is used and the controller has a
+ * strict duplicate filter, it is important to remember the
+ * start and duration of the scan. This is required for
+ * restarting scanning during the discovery phase.
+ */
+ if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) &&
+ hdev->discovery.result_filtering) {
+ hdev->discovery.scan_start = jiffies;
+ hdev->discovery.scan_duration = timeout;
+ }
+
+ queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_disable,
+ timeout);
+}
+
+bool hci_req_stop_discovery(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct discovery_state *d = &hdev->discovery;
+ struct hci_cp_remote_name_req_cancel cp;
+ struct inquiry_entry *e;
+ bool ret = false;
+
+ BT_DBG("%s state %u", hdev->name, hdev->discovery.state);
+
+ if (d->state == DISCOVERY_FINDING || d->state == DISCOVERY_STOPPING) {
+ if (test_bit(HCI_INQUIRY, &hdev->flags))
+ hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
+ cancel_delayed_work(&hdev->le_scan_disable);
+ hci_req_add_le_scan_disable(req);
+ }
+
+ ret = true;
+ } else {
+ /* Passive scanning */
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
+ hci_req_add_le_scan_disable(req);
+ ret = true;
+ }
+ }
+
+ /* No further actions needed for LE-only discovery */
+ if (d->type == DISCOV_TYPE_LE)
+ return ret;
+
+ if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) {
+ e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
+ NAME_PENDING);
+ if (!e)
+ return ret;
+
+ bacpy(&cp.bdaddr, &e->data.bdaddr);
+ hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
+ &cp);
+ ret = true;
+ }
+
+ return ret;
+}
+
+static int stop_discovery(struct hci_request *req, unsigned long opt)
+{
+ hci_dev_lock(req->hdev);
+ hci_req_stop_discovery(req);
+ hci_dev_unlock(req->hdev);
+
+ return 0;
+}
+
+static void discov_update(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ discov_update);
+ u8 status = 0;
+
+ switch (hdev->discovery.state) {
+ case DISCOVERY_STARTING:
+ start_discovery(hdev, &status);
+ mgmt_start_discovery_complete(hdev, status);
+ if (status)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ else
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ break;
+ case DISCOVERY_STOPPING:
+ hci_req_sync(hdev, stop_discovery, 0, HCI_CMD_TIMEOUT, &status);
+ mgmt_stop_discovery_complete(hdev, status);
+ if (!status)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ break;
+ case DISCOVERY_STOPPED:
+ default:
+ return;
+ }
+}
+
+static void discov_off(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ discov_off.work);
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ /* When discoverable timeout triggers, then just make sure
+ * the limited discoverable flag is cleared. Even in the case
+ * of a timeout triggered from general discoverable, it is
+ * safe to unconditionally clear the flag.
+ */
+ hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
+ hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+ hdev->discov_timeout = 0;
+
+ hci_dev_unlock(hdev);
+
+ hci_req_sync(hdev, discoverable_update, 0, HCI_CMD_TIMEOUT, NULL);
+ mgmt_new_settings(hdev);
+}
+
+static int powered_update_hci(struct hci_request *req, unsigned long opt)
+{
+ struct hci_dev *hdev = req->hdev;
+ u8 link_sec;
+
+ hci_dev_lock(hdev);
+
+ if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
+ !lmp_host_ssp_capable(hdev)) {
+ u8 mode = 0x01;
+
+ hci_req_add(req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+
+ if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
+ u8 support = 0x01;
+
+ hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
+ sizeof(support), &support);
+ }
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
+ lmp_bredr_capable(hdev)) {
+ struct hci_cp_write_le_host_supported cp;
+
+ cp.le = 0x01;
+ cp.simul = 0x00;
+
+ /* Check first if we already have the right
+ * host state (host features set)
+ */
+ if (cp.le != lmp_host_le_capable(hdev) ||
+ cp.simul != lmp_host_le_br_capable(hdev))
+ hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
+ sizeof(cp), &cp);
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
+ /* Make sure the controller has a good default for
+ * advertising data. This also applies to the case
+ * where BR/EDR was toggled during the AUTO_OFF phase.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ list_empty(&hdev->adv_instances)) {
+ __hci_req_update_adv_data(req, 0x00);
+ __hci_req_update_scan_rsp_data(req, 0x00);
+
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ __hci_req_enable_advertising(req);
+ } else if (!list_empty(&hdev->adv_instances)) {
+ struct adv_info *adv_instance;
+
+ adv_instance = list_first_entry(&hdev->adv_instances,
+ struct adv_info, list);
+ __hci_req_schedule_adv_instance(req,
+ adv_instance->instance,
+ true);
+ }
+ }
+
+ link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
+ if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
+ hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE,
+ sizeof(link_sec), &link_sec);
+
+ if (lmp_bredr_capable(hdev)) {
+ if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
+ __hci_req_write_fast_connectable(req, true);
+ else
+ __hci_req_write_fast_connectable(req, false);
+ __hci_req_update_scan(req);
+ __hci_req_update_class(req);
+ __hci_req_update_name(req);
+ __hci_req_update_eir(req);
+ }
+
+ hci_dev_unlock(hdev);
+ return 0;
+}
+
+int __hci_req_hci_power_on(struct hci_dev *hdev)
+{
+ /* Register the available SMP channels (BR/EDR and LE) only when
+ * successfully powering on the controller. This late
+ * registration is required so that LE SMP can clearly decide if
+ * the public address or static address is used.
+ */
+ smp_register(hdev);
+
+ return __hci_req_sync(hdev, powered_update_hci, 0, HCI_CMD_TIMEOUT,
+ NULL);
+}
+
+void hci_request_setup(struct hci_dev *hdev)
+{
+ INIT_WORK(&hdev->discov_update, discov_update);
+ INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
+ INIT_WORK(&hdev->scan_update, scan_update_work);
+ INIT_WORK(&hdev->connectable_update, connectable_update_work);
+ INIT_WORK(&hdev->discoverable_update, discoverable_update_work);
+ INIT_DELAYED_WORK(&hdev->discov_off, discov_off);
+ INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
+ INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
+ INIT_DELAYED_WORK(&hdev->adv_instance_expire, adv_timeout_expire);
+}
+
+void hci_request_cancel_all(struct hci_dev *hdev)
+{
+ hci_req_sync_cancel(hdev, ENODEV);
+
+ cancel_work_sync(&hdev->discov_update);
+ cancel_work_sync(&hdev->bg_scan_update);
+ cancel_work_sync(&hdev->scan_update);
+ cancel_work_sync(&hdev->connectable_update);
+ cancel_work_sync(&hdev->discoverable_update);
+ cancel_delayed_work_sync(&hdev->discov_off);
+ cancel_delayed_work_sync(&hdev->le_scan_disable);
+ cancel_delayed_work_sync(&hdev->le_scan_restart);
+
+ if (hdev->adv_instance_timeout) {
+ cancel_delayed_work_sync(&hdev->adv_instance_expire);
+ hdev->adv_instance_timeout = 0;
+ }
+}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 25c7f1305dcb..64ff8c040d50 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -20,6 +20,9 @@
SOFTWARE IS DISCLAIMED.
*/
+#define hci_req_sync_lock(hdev) mutex_lock(&hdev->req_lock)
+#define hci_req_sync_unlock(hdev) mutex_unlock(&hdev->req_lock)
+
struct hci_request {
struct hci_dev *hdev;
struct sk_buff_head cmd_q;
@@ -41,21 +44,61 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
hci_req_complete_t *req_complete,
hci_req_complete_skb_t *req_complete_skb);
+int hci_req_sync(struct hci_dev *hdev, int (*req)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status);
+int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status);
+void hci_req_sync_cancel(struct hci_dev *hdev, int err);
+
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
+int __hci_req_hci_power_on(struct hci_dev *hdev);
+
+void __hci_req_write_fast_connectable(struct hci_request *req, bool enable);
+void __hci_req_update_name(struct hci_request *req);
+void __hci_req_update_eir(struct hci_request *req);
+
void hci_req_add_le_scan_disable(struct hci_request *req);
void hci_req_add_le_passive_scan(struct hci_request *req);
-void hci_update_page_scan(struct hci_dev *hdev);
-void __hci_update_page_scan(struct hci_request *req);
+void hci_req_reenable_advertising(struct hci_dev *hdev);
+void __hci_req_enable_advertising(struct hci_request *req);
+void __hci_req_disable_advertising(struct hci_request *req);
+void __hci_req_update_adv_data(struct hci_request *req, u8 instance);
+int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance);
+void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance);
+
+int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
+ bool force);
+void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
+ u8 instance, bool force);
+
+void __hci_req_update_class(struct hci_request *req);
+
+/* Returns true if HCI commands were queued */
+bool hci_req_stop_discovery(struct hci_request *req);
+
+static inline void hci_req_update_scan(struct hci_dev *hdev)
+{
+ queue_work(hdev->req_workqueue, &hdev->scan_update);
+}
+
+void __hci_req_update_scan(struct hci_request *req);
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type);
-void hci_update_background_scan(struct hci_dev *hdev);
-void __hci_update_background_scan(struct hci_request *req);
-
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
u8 reason);
+
+static inline void hci_update_background_scan(struct hci_dev *hdev)
+{
+ queue_work(hdev->req_workqueue, &hdev->bg_scan_update);
+}
+
+void hci_request_setup(struct hci_dev *hdev);
+void hci_request_cancel_all(struct hci_dev *hdev);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index b1eb8c09a660..1298d723c0e0 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -25,6 +25,7 @@
/* Bluetooth HCI sockets. */
#include <linux/export.h>
+#include <linux/utsname.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -120,13 +121,13 @@ static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb)
/* Apply filter */
flt = &hci_pi(sk)->filter;
- flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS;
+ flt_type = hci_skb_pkt_type(skb) & HCI_FLT_TYPE_BITS;
if (!test_bit(flt_type, &flt->type_mask))
return true;
/* Extra filter for event packets only */
- if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT)
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT)
return false;
flt_event = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
@@ -170,19 +171,19 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
continue;
if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) {
- if (bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
- bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
+ if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
+ hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
continue;
if (is_filtered_packet(sk, skb))
continue;
} else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
if (!bt_cb(skb)->incoming)
continue;
- if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
continue;
} else {
/* Don't send frame to other channel types */
@@ -196,7 +197,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
continue;
/* Put type byte before the data */
- memcpy(skb_push(skb_copy, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb_copy, 1), &hci_skb_pkt_type(skb), 1);
}
nskb = skb_clone(skb_copy, GFP_ATOMIC);
@@ -262,7 +263,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("hdev %p len %d", hdev, skb->len);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
opcode = cpu_to_le16(HCI_MON_COMMAND_PKT);
break;
@@ -294,7 +295,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
return;
/* Put header before the data */
- hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE);
+ hdr = (void *)skb_push(skb_copy, HCI_MON_HDR_SIZE);
hdr->opcode = opcode;
hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len);
@@ -375,7 +376,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
__net_timestamp(skb);
- hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE);
+ hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
hdr->opcode = opcode;
hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
@@ -383,6 +384,38 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
return skb;
}
+static void __printf(2, 3)
+send_monitor_note(struct sock *sk, const char *fmt, ...)
+{
+ size_t len;
+ struct hci_mon_hdr *hdr;
+ struct sk_buff *skb;
+ va_list args;
+
+ va_start(args, fmt);
+ len = vsnprintf(NULL, 0, fmt, args);
+ va_end(args);
+
+ skb = bt_skb_alloc(len + 1, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ va_start(args, fmt);
+ vsprintf(skb_put(skb, len), fmt, args);
+ *skb_put(skb, 1) = 0;
+ va_end(args);
+
+ __net_timestamp(skb);
+
+ hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
+ hdr->opcode = cpu_to_le16(HCI_MON_SYSTEM_NOTE);
+ hdr->index = cpu_to_le16(HCI_DEV_NONE);
+ hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
+
+ if (sock_queue_rcv_skb(sk, skb))
+ kfree_skb(skb);
+}
+
static void send_monitor_replay(struct sock *sk)
{
struct hci_dev *hdev;
@@ -436,18 +469,18 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
if (!skb)
return;
- hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE);
+ hdr = (void *)skb_put(skb, HCI_EVENT_HDR_SIZE);
hdr->evt = HCI_EV_STACK_INTERNAL;
hdr->plen = sizeof(*ev) + dlen;
- ev = (void *) skb_put(skb, sizeof(*ev) + dlen);
+ ev = (void *)skb_put(skb, sizeof(*ev) + dlen);
ev->type = type;
memcpy(ev->data, data, dlen);
bt_cb(skb)->incoming = 1;
__net_timestamp(skb);
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
hci_send_to_sock(hdev, skb);
kfree_skb(skb);
}
@@ -653,20 +686,20 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
return -EOPNOTSUPP;
case HCIGETCONNINFO:
- return hci_get_conn_info(hdev, (void __user *) arg);
+ return hci_get_conn_info(hdev, (void __user *)arg);
case HCIGETAUTHINFO:
- return hci_get_auth_info(hdev, (void __user *) arg);
+ return hci_get_auth_info(hdev, (void __user *)arg);
case HCIBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- return hci_sock_blacklist_add(hdev, (void __user *) arg);
+ return hci_sock_blacklist_add(hdev, (void __user *)arg);
case HCIUNBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- return hci_sock_blacklist_del(hdev, (void __user *) arg);
+ return hci_sock_blacklist_del(hdev, (void __user *)arg);
}
return -ENOIOCTLCMD;
@@ -675,7 +708,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
- void __user *argp = (void __user *) arg;
+ void __user *argp = (void __user *)arg;
struct sock *sk = sock->sk;
int err;
@@ -872,11 +905,28 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
*/
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
+ send_monitor_note(sk, "Linux version %s (%s)",
+ init_utsname()->release,
+ init_utsname()->machine);
+ send_monitor_note(sk, "Bluetooth subsystem version %s",
+ BT_SUBSYS_VERSION);
send_monitor_replay(sk);
atomic_inc(&monitor_promisc);
break;
+ case HCI_CHANNEL_LOGGING:
+ if (haddr.hci_dev != HCI_DEV_NONE) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (!capable(CAP_NET_ADMIN)) {
+ err = -EPERM;
+ goto done;
+ }
+ break;
+
default:
if (!hci_mgmt_chan_find(haddr.hci_channel)) {
err = -EINVAL;
@@ -926,7 +976,7 @@ done:
static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
int *addr_len, int peer)
{
- struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
+ struct sockaddr_hci *haddr = (struct sockaddr_hci *)addr;
struct sock *sk = sock->sk;
struct hci_dev *hdev;
int err = 0;
@@ -991,8 +1041,8 @@ static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
}
}
-static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
- int flags)
+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg,
+ size_t len, int flags)
{
int noblock = flags & MSG_DONTWAIT;
struct sock *sk = sock->sk;
@@ -1004,6 +1054,9 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
if (flags & MSG_OOB)
return -EOPNOTSUPP;
+ if (hci_pi(sk)->channel == HCI_CHANNEL_LOGGING)
+ return -EOPNOTSUPP;
+
if (sk->sk_state == BT_CLOSED)
return 0;
@@ -1150,6 +1203,90 @@ done:
return err;
}
+static int hci_logging_frame(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct hci_mon_hdr *hdr;
+ struct sk_buff *skb;
+ struct hci_dev *hdev;
+ u16 index;
+ int err;
+
+ /* The logging frame consists at minimum of the standard header,
+ * the priority byte, the ident length byte and at least one string
+ * terminator NUL byte. Anything shorter are invalid packets.
+ */
+ if (len < sizeof(*hdr) + 3)
+ return -EINVAL;
+
+ skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
+ if (!skb)
+ return err;
+
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
+ err = -EFAULT;
+ goto drop;
+ }
+
+ hdr = (void *)skb->data;
+
+ if (__le16_to_cpu(hdr->len) != len - sizeof(*hdr)) {
+ err = -EINVAL;
+ goto drop;
+ }
+
+ if (__le16_to_cpu(hdr->opcode) == 0x0000) {
+ __u8 priority = skb->data[sizeof(*hdr)];
+ __u8 ident_len = skb->data[sizeof(*hdr) + 1];
+
+ /* Only the priorities 0-7 are valid and with that any other
+ * value results in an invalid packet.
+ *
+ * The priority byte is followed by an ident length byte and
+ * the NUL terminated ident string. Check that the ident
+ * length is not overflowing the packet and also that the
+ * ident string itself is NUL terminated. In case the ident
+ * length is zero, the length value actually doubles as NUL
+ * terminator identifier.
+ *
+ * The message follows the ident string (if present) and
+ * must be NUL terminated. Otherwise it is not a valid packet.
+ */
+ if (priority > 7 || skb->data[len - 1] != 0x00 ||
+ ident_len > len - sizeof(*hdr) - 3 ||
+ skb->data[sizeof(*hdr) + ident_len + 1] != 0x00) {
+ err = -EINVAL;
+ goto drop;
+ }
+ } else {
+ err = -EINVAL;
+ goto drop;
+ }
+
+ index = __le16_to_cpu(hdr->index);
+
+ if (index != MGMT_INDEX_NONE) {
+ hdev = hci_dev_get(index);
+ if (!hdev) {
+ err = -ENODEV;
+ goto drop;
+ }
+ } else {
+ hdev = NULL;
+ }
+
+ hdr->opcode = cpu_to_le16(HCI_MON_USER_LOGGING);
+
+ hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, HCI_SOCK_TRUSTED, NULL);
+ err = len;
+
+ if (hdev)
+ hci_dev_put(hdev);
+
+drop:
+ kfree_skb(skb);
+ return err;
+}
+
static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len)
{
@@ -1179,6 +1316,9 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
case HCI_CHANNEL_MONITOR:
err = -EOPNOTSUPP;
goto done;
+ case HCI_CHANNEL_LOGGING:
+ err = hci_logging_frame(sk, msg, len);
+ goto done;
default:
mutex_lock(&mgmt_chan_list_lock);
chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
@@ -1211,7 +1351,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
goto drop;
}
- bt_cb(skb)->pkt_type = *((unsigned char *) skb->data);
+ hci_skb_pkt_type(skb) = skb->data[0];
skb_pull(skb, 1);
if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
@@ -1220,16 +1360,16 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
*
* However check that the packet type is valid.
*/
- if (bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
err = -EINVAL;
goto drop;
}
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
- } else if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ } else if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
u16 opcode = get_unaligned_le16(skb->data);
u16 ogf = hci_opcode_ogf(opcode);
u16 ocf = hci_opcode_ocf(opcode);
@@ -1242,6 +1382,11 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
goto drop;
}
+ /* Since the opcode has already been extracted here, store
+ * a copy of the value for later use by the drivers.
+ */
+ hci_skb_opcode(skb) = opcode;
+
if (ogf == 0x3f) {
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
@@ -1249,7 +1394,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->hci.req_start = true;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_START;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -1260,8 +1405,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
goto drop;
}
- if (bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
err = -EINVAL;
goto drop;
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 66e8b6ee19a5..39a5149f3010 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -6538,8 +6538,6 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff *skb)
{
- int err = 0;
-
BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
chan->rx_state);
@@ -6570,7 +6568,7 @@ static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
chan->last_acked_seq = control->txseq;
chan->expected_tx_seq = __next_seq(chan, control->txseq);
- return err;
+ return 0;
}
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -7113,8 +7111,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
chan->dcid = cid;
if (bdaddr_type_is_le(dst_type)) {
- u8 role;
-
/* Convert from L2CAP channel address type to HCI address type
*/
if (dst_type == BDADDR_LE_PUBLIC)
@@ -7123,14 +7119,15 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
dst_type = ADDR_LE_DEV_RANDOM;
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
- role = HCI_ROLE_SLAVE;
+ hcon = hci_connect_le(hdev, dst, dst_type,
+ chan->sec_level,
+ HCI_LE_CONN_TIMEOUT,
+ HCI_ROLE_SLAVE);
else
- role = HCI_ROLE_MASTER;
+ hcon = hci_connect_le_scan(hdev, dst, dst_type,
+ chan->sec_level,
+ HCI_LE_CONN_TIMEOUT);
- hcon = hci_connect_le_scan(hdev, dst, dst_type,
- chan->sec_level,
- HCI_LE_CONN_TIMEOUT,
- role);
} else {
u8 auth_type = l2cap_get_auth_type(chan);
hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7f22119276f3..5a5089cb6570 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -38,7 +38,7 @@
#include "mgmt_util.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 10
+#define MGMT_REVISION 11
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -102,6 +102,8 @@ static const u16 mgmt_commands[] = {
MGMT_OP_READ_ADV_FEATURES,
MGMT_OP_ADD_ADVERTISING,
MGMT_OP_REMOVE_ADVERTISING,
+ MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_OP_START_LIMITED_DISCOVERY,
};
static const u16 mgmt_events[] = {
@@ -718,116 +720,6 @@ static u32 get_current_settings(struct hci_dev *hdev)
return settings;
}
-#define PNP_INFO_SVCLASS_ID 0x1200
-
-static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
-{
- u8 *ptr = data, *uuids_start = NULL;
- struct bt_uuid *uuid;
-
- if (len < 4)
- return ptr;
-
- list_for_each_entry(uuid, &hdev->uuids, list) {
- u16 uuid16;
-
- if (uuid->size != 16)
- continue;
-
- uuid16 = get_unaligned_le16(&uuid->uuid[12]);
- if (uuid16 < 0x1100)
- continue;
-
- if (uuid16 == PNP_INFO_SVCLASS_ID)
- continue;
-
- if (!uuids_start) {
- uuids_start = ptr;
- uuids_start[0] = 1;
- uuids_start[1] = EIR_UUID16_ALL;
- ptr += 2;
- }
-
- /* Stop if not enough space to put next UUID */
- if ((ptr - data) + sizeof(u16) > len) {
- uuids_start[1] = EIR_UUID16_SOME;
- break;
- }
-
- *ptr++ = (uuid16 & 0x00ff);
- *ptr++ = (uuid16 & 0xff00) >> 8;
- uuids_start[0] += sizeof(uuid16);
- }
-
- return ptr;
-}
-
-static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
-{
- u8 *ptr = data, *uuids_start = NULL;
- struct bt_uuid *uuid;
-
- if (len < 6)
- return ptr;
-
- list_for_each_entry(uuid, &hdev->uuids, list) {
- if (uuid->size != 32)
- continue;
-
- if (!uuids_start) {
- uuids_start = ptr;
- uuids_start[0] = 1;
- uuids_start[1] = EIR_UUID32_ALL;
- ptr += 2;
- }
-
- /* Stop if not enough space to put next UUID */
- if ((ptr - data) + sizeof(u32) > len) {
- uuids_start[1] = EIR_UUID32_SOME;
- break;
- }
-
- memcpy(ptr, &uuid->uuid[12], sizeof(u32));
- ptr += sizeof(u32);
- uuids_start[0] += sizeof(u32);
- }
-
- return ptr;
-}
-
-static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
-{
- u8 *ptr = data, *uuids_start = NULL;
- struct bt_uuid *uuid;
-
- if (len < 18)
- return ptr;
-
- list_for_each_entry(uuid, &hdev->uuids, list) {
- if (uuid->size != 128)
- continue;
-
- if (!uuids_start) {
- uuids_start = ptr;
- uuids_start[0] = 1;
- uuids_start[1] = EIR_UUID128_ALL;
- ptr += 2;
- }
-
- /* Stop if not enough space to put next UUID */
- if ((ptr - data) + 16 > len) {
- uuids_start[1] = EIR_UUID128_SOME;
- break;
- }
-
- memcpy(ptr, uuid->uuid, 16);
- ptr += 16;
- uuids_start[0] += 16;
- }
-
- return ptr;
-}
-
static struct mgmt_pending_cmd *pending_find(u16 opcode, struct hci_dev *hdev)
{
return mgmt_pending_find(HCI_CHANNEL_CONTROL, opcode, hdev);
@@ -840,98 +732,7 @@ static struct mgmt_pending_cmd *pending_find_data(u16 opcode,
return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data);
}
-static u8 get_current_adv_instance(struct hci_dev *hdev)
-{
- /* The "Set Advertising" setting supersedes the "Add Advertising"
- * setting. Here we set the advertising data based on which
- * setting was set. When neither apply, default to the global settings,
- * represented by instance "0".
- */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
- !hci_dev_test_flag(hdev, HCI_ADVERTISING))
- return hdev->cur_adv_instance;
-
- return 0x00;
-}
-
-static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
-{
- u8 ad_len = 0;
- size_t name_len;
-
- name_len = strlen(hdev->dev_name);
- if (name_len > 0) {
- size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
-
- if (name_len > max_len) {
- name_len = max_len;
- ptr[1] = EIR_NAME_SHORT;
- } else
- ptr[1] = EIR_NAME_COMPLETE;
-
- ptr[0] = name_len + 1;
-
- memcpy(ptr + 2, hdev->dev_name, name_len);
-
- ad_len += (name_len + 2);
- ptr += (name_len + 2);
- }
-
- return ad_len;
-}
-
-static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
- u8 *ptr)
-{
- struct adv_info *adv_instance;
-
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return 0;
-
- /* TODO: Set the appropriate entries based on advertising instance flags
- * here once flags other than 0 are supported.
- */
- memcpy(ptr, adv_instance->scan_rsp_data,
- adv_instance->scan_rsp_len);
-
- return adv_instance->scan_rsp_len;
-}
-
-static void update_inst_scan_rsp_data(struct hci_request *req, u8 instance)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_scan_rsp_data cp;
- u8 len;
-
- if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- if (instance)
- len = create_instance_scan_rsp_data(hdev, instance, cp.data);
- else
- len = create_default_scan_rsp_data(hdev, cp.data);
-
- if (hdev->scan_rsp_data_len == len &&
- !memcmp(cp.data, hdev->scan_rsp_data, len))
- return;
-
- memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
- hdev->scan_rsp_data_len = len;
-
- cp.length = len;
-
- hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
-}
-
-static void update_scan_rsp_data(struct hci_request *req)
-{
- update_inst_scan_rsp_data(req, get_current_adv_instance(req->hdev));
-}
-
-static u8 get_adv_discov_flags(struct hci_dev *hdev)
+u8 mgmt_get_adv_discov_flags(struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
@@ -955,7 +756,7 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev)
return 0;
}
-static bool get_connectable(struct hci_dev *hdev)
+bool mgmt_get_connectable(struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
@@ -972,344 +773,6 @@ static bool get_connectable(struct hci_dev *hdev)
return hci_dev_test_flag(hdev, HCI_CONNECTABLE);
}
-static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
-{
- u32 flags;
- struct adv_info *adv_instance;
-
- if (instance == 0x00) {
- /* Instance 0 always manages the "Tx Power" and "Flags"
- * fields
- */
- flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
-
- /* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
- * corresponds to the "connectable" instance flag.
- */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
- flags |= MGMT_ADV_FLAG_CONNECTABLE;
-
- return flags;
- }
-
- adv_instance = hci_find_adv_instance(hdev, instance);
-
- /* Return 0 when we got an invalid instance identifier. */
- if (!adv_instance)
- return 0;
-
- return adv_instance->flags;
-}
-
-static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
-{
- u8 instance = get_current_adv_instance(hdev);
- struct adv_info *adv_instance;
-
- /* Ignore instance 0 */
- if (instance == 0x00)
- return 0;
-
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return 0;
-
- /* TODO: Take into account the "appearance" and "local-name" flags here.
- * These are currently being ignored as they are not supported.
- */
- return adv_instance->scan_rsp_len;
-}
-
-static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
-{
- struct adv_info *adv_instance = NULL;
- u8 ad_len = 0, flags = 0;
- u32 instance_flags;
-
- /* Return 0 when the current instance identifier is invalid. */
- if (instance) {
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return 0;
- }
-
- instance_flags = get_adv_instance_flags(hdev, instance);
-
- /* The Add Advertising command allows userspace to set both the general
- * and limited discoverable flags.
- */
- if (instance_flags & MGMT_ADV_FLAG_DISCOV)
- flags |= LE_AD_GENERAL;
-
- if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV)
- flags |= LE_AD_LIMITED;
-
- if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) {
- /* If a discovery flag wasn't provided, simply use the global
- * settings.
- */
- if (!flags)
- flags |= get_adv_discov_flags(hdev);
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- flags |= LE_AD_NO_BREDR;
-
- /* If flags would still be empty, then there is no need to
- * include the "Flags" AD field".
- */
- if (flags) {
- ptr[0] = 0x02;
- ptr[1] = EIR_FLAGS;
- ptr[2] = flags;
-
- ad_len += 3;
- ptr += 3;
- }
- }
-
- if (adv_instance) {
- memcpy(ptr, adv_instance->adv_data,
- adv_instance->adv_data_len);
- ad_len += adv_instance->adv_data_len;
- ptr += adv_instance->adv_data_len;
- }
-
- /* Provide Tx Power only if we can provide a valid value for it */
- if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
- (instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
- ptr[0] = 0x02;
- ptr[1] = EIR_TX_POWER;
- ptr[2] = (u8)hdev->adv_tx_power;
-
- ad_len += 3;
- ptr += 3;
- }
-
- return ad_len;
-}
-
-static void update_inst_adv_data(struct hci_request *req, u8 instance)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_adv_data cp;
- u8 len;
-
- if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- len = create_instance_adv_data(hdev, instance, cp.data);
-
- /* There's nothing to do if the data hasn't changed */
- if (hdev->adv_data_len == len &&
- memcmp(cp.data, hdev->adv_data, len) == 0)
- return;
-
- memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
- hdev->adv_data_len = len;
-
- cp.length = len;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
-}
-
-static void update_adv_data(struct hci_request *req)
-{
- update_inst_adv_data(req, get_current_adv_instance(req->hdev));
-}
-
-int mgmt_update_adv_data(struct hci_dev *hdev)
-{
- struct hci_request req;
-
- hci_req_init(&req, hdev);
- update_adv_data(&req);
-
- return hci_req_run(&req, NULL);
-}
-
-static void create_eir(struct hci_dev *hdev, u8 *data)
-{
- u8 *ptr = data;
- size_t name_len;
-
- name_len = strlen(hdev->dev_name);
-
- if (name_len > 0) {
- /* EIR Data type */
- if (name_len > 48) {
- name_len = 48;
- ptr[1] = EIR_NAME_SHORT;
- } else
- ptr[1] = EIR_NAME_COMPLETE;
-
- /* EIR Data length */
- ptr[0] = name_len + 1;
-
- memcpy(ptr + 2, hdev->dev_name, name_len);
-
- ptr += (name_len + 2);
- }
-
- if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
- ptr[0] = 2;
- ptr[1] = EIR_TX_POWER;
- ptr[2] = (u8) hdev->inq_tx_power;
-
- ptr += 3;
- }
-
- if (hdev->devid_source > 0) {
- ptr[0] = 9;
- ptr[1] = EIR_DEVICE_ID;
-
- put_unaligned_le16(hdev->devid_source, ptr + 2);
- put_unaligned_le16(hdev->devid_vendor, ptr + 4);
- put_unaligned_le16(hdev->devid_product, ptr + 6);
- put_unaligned_le16(hdev->devid_version, ptr + 8);
-
- ptr += 10;
- }
-
- ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
- ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
- ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
-}
-
-static void update_eir(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_write_eir cp;
-
- if (!hdev_is_powered(hdev))
- return;
-
- if (!lmp_ext_inq_capable(hdev))
- return;
-
- if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
- return;
-
- if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- create_eir(hdev, cp.data);
-
- if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
- return;
-
- memcpy(hdev->eir, cp.data, sizeof(cp.data));
-
- hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
-}
-
-static u8 get_service_classes(struct hci_dev *hdev)
-{
- struct bt_uuid *uuid;
- u8 val = 0;
-
- list_for_each_entry(uuid, &hdev->uuids, list)
- val |= uuid->svc_hint;
-
- return val;
-}
-
-static void update_class(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- u8 cod[3];
-
- BT_DBG("%s", hdev->name);
-
- if (!hdev_is_powered(hdev))
- return;
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- return;
-
- if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
- return;
-
- cod[0] = hdev->minor_class;
- cod[1] = hdev->major_class;
- cod[2] = get_service_classes(hdev);
-
- if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
- cod[1] |= 0x20;
-
- if (memcmp(cod, hdev->dev_class, 3) == 0)
- return;
-
- hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-}
-
-static void disable_advertising(struct hci_request *req)
-{
- u8 enable = 0x00;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-}
-
-static void enable_advertising(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_adv_param cp;
- u8 own_addr_type, enable = 0x01;
- bool connectable;
- u8 instance;
- u32 flags;
-
- if (hci_conn_num(hdev, LE_LINK) > 0)
- return;
-
- if (hci_dev_test_flag(hdev, HCI_LE_ADV))
- disable_advertising(req);
-
- /* Clear the HCI_LE_ADV bit temporarily so that the
- * hci_update_random_address knows that it's safe to go ahead
- * and write a new random address. The flag will be set back on
- * as soon as the SET_ADV_ENABLE HCI command completes.
- */
- hci_dev_clear_flag(hdev, HCI_LE_ADV);
-
- instance = get_current_adv_instance(hdev);
- flags = get_adv_instance_flags(hdev, instance);
-
- /* If the "connectable" instance flag was not set, then choose between
- * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
- */
- connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
- get_connectable(hdev);
-
- /* Set require_privacy to true only when non-connectable
- * advertising is used. In that case it is fine to use a
- * non-resolvable private address.
- */
- if (hci_update_random_address(req, !connectable, &own_addr_type) < 0)
- return;
-
- memset(&cp, 0, sizeof(cp));
- cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
- cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
-
- if (connectable)
- cp.type = LE_ADV_IND;
- else if (get_cur_adv_instance_scan_rsp_len(hdev))
- cp.type = LE_ADV_SCAN_IND;
- else
- cp.type = LE_ADV_NONCONN_IND;
-
- cp.own_address_type = own_addr_type;
- cp.channel_map = hdev->le_adv_channel_map;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-}
-
static void service_cache_off(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev,
@@ -1323,8 +786,8 @@ static void service_cache_off(struct work_struct *work)
hci_dev_lock(hdev);
- update_eir(&req);
- update_class(&req);
+ __hci_req_update_eir(&req);
+ __hci_req_update_class(&req);
hci_dev_unlock(hdev);
@@ -1345,10 +808,11 @@ static void rpa_expired(struct work_struct *work)
return;
/* The generation of a new RPA and programming it into the
- * controller happens in the enable_advertising() function.
+ * controller happens in the hci_req_enable_advertising()
+ * function.
*/
hci_req_init(&req, hdev);
- enable_advertising(&req);
+ __hci_req_enable_advertising(&req);
hci_req_run(&req, NULL);
}
@@ -1416,51 +880,7 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status, u16 opcode)
}
}
-static bool hci_stop_discovery(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_remote_name_req_cancel cp;
- struct inquiry_entry *e;
-
- switch (hdev->discovery.state) {
- case DISCOVERY_FINDING:
- if (test_bit(HCI_INQUIRY, &hdev->flags))
- hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
-
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- cancel_delayed_work(&hdev->le_scan_disable);
- hci_req_add_le_scan_disable(req);
- }
-
- return true;
-
- case DISCOVERY_RESOLVING:
- e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
- NAME_PENDING);
- if (!e)
- break;
-
- bacpy(&cp.bdaddr, &e->data.bdaddr);
- hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
- &cp);
-
- return true;
-
- default:
- /* Passive scanning */
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- hci_req_add_le_scan_disable(req);
- return true;
- }
-
- break;
- }
-
- return false;
-}
-
-static void advertising_added(struct sock *sk, struct hci_dev *hdev,
- u8 instance)
+void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev, u8 instance)
{
struct mgmt_ev_advertising_added ev;
@@ -1469,8 +889,8 @@ static void advertising_added(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk);
}
-static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
- u8 instance)
+void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
+ u8 instance)
{
struct mgmt_ev_advertising_removed ev;
@@ -1479,65 +899,6 @@ static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk);
}
-static int schedule_adv_instance(struct hci_request *req, u8 instance,
- bool force) {
- struct hci_dev *hdev = req->hdev;
- struct adv_info *adv_instance = NULL;
- u16 timeout;
-
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
- return -EPERM;
-
- if (hdev->adv_instance_timeout)
- return -EBUSY;
-
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return -ENOENT;
-
- /* A zero timeout means unlimited advertising. As long as there is
- * only one instance, duration should be ignored. We still set a timeout
- * in case further instances are being added later on.
- *
- * If the remaining lifetime of the instance is more than the duration
- * then the timeout corresponds to the duration, otherwise it will be
- * reduced to the remaining instance lifetime.
- */
- if (adv_instance->timeout == 0 ||
- adv_instance->duration <= adv_instance->remaining_time)
- timeout = adv_instance->duration;
- else
- timeout = adv_instance->remaining_time;
-
- /* The remaining time is being reduced unless the instance is being
- * advertised without time limit.
- */
- if (adv_instance->timeout)
- adv_instance->remaining_time =
- adv_instance->remaining_time - timeout;
-
- hdev->adv_instance_timeout = timeout;
- queue_delayed_work(hdev->workqueue,
- &hdev->adv_instance_expire,
- msecs_to_jiffies(timeout * 1000));
-
- /* If we're just re-scheduling the same instance again then do not
- * execute any HCI commands. This happens when a single instance is
- * being advertised.
- */
- if (!force && hdev->cur_adv_instance == instance &&
- hci_dev_test_flag(hdev, HCI_LE_ADV))
- return 0;
-
- hdev->cur_adv_instance = instance;
- update_adv_data(req);
- update_scan_rsp_data(req);
- enable_advertising(req);
-
- return 0;
-}
-
static void cancel_adv_timeout(struct hci_dev *hdev)
{
if (hdev->adv_instance_timeout) {
@@ -1546,76 +907,6 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
}
}
-/* For a single instance:
- * - force == true: The instance will be removed even when its remaining
- * lifetime is not zero.
- * - force == false: the instance will be deactivated but kept stored unless
- * the remaining lifetime is zero.
- *
- * For instance == 0x00:
- * - force == true: All instances will be removed regardless of their timeout
- * setting.
- * - force == false: Only instances that have a timeout will be removed.
- */
-static void clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
- u8 instance, bool force)
-{
- struct adv_info *adv_instance, *n, *next_instance = NULL;
- int err;
- u8 rem_inst;
-
- /* Cancel any timeout concerning the removed instance(s). */
- if (!instance || hdev->cur_adv_instance == instance)
- cancel_adv_timeout(hdev);
-
- /* Get the next instance to advertise BEFORE we remove
- * the current one. This can be the same instance again
- * if there is only one instance.
- */
- if (instance && hdev->cur_adv_instance == instance)
- next_instance = hci_get_next_instance(hdev, instance);
-
- if (instance == 0x00) {
- list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
- list) {
- if (!(force || adv_instance->timeout))
- continue;
-
- rem_inst = adv_instance->instance;
- err = hci_remove_adv_instance(hdev, rem_inst);
- if (!err)
- advertising_removed(NULL, hdev, rem_inst);
- }
- hdev->cur_adv_instance = 0x00;
- } else {
- adv_instance = hci_find_adv_instance(hdev, instance);
-
- if (force || (adv_instance && adv_instance->timeout &&
- !adv_instance->remaining_time)) {
- /* Don't advertise a removed instance. */
- if (next_instance &&
- next_instance->instance == instance)
- next_instance = NULL;
-
- err = hci_remove_adv_instance(hdev, instance);
- if (!err)
- advertising_removed(NULL, hdev, instance);
- }
- }
-
- if (list_empty(&hdev->adv_instances)) {
- hdev->cur_adv_instance = 0x00;
- hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
- }
-
- if (!req || !hdev_is_powered(hdev) ||
- hci_dev_test_flag(hdev, HCI_ADVERTISING))
- return;
-
- if (next_instance)
- schedule_adv_instance(req, next_instance->instance, false);
-}
-
static int clean_up_hci_state(struct hci_dev *hdev)
{
struct hci_request req;
@@ -1631,12 +922,12 @@ static int clean_up_hci_state(struct hci_dev *hdev)
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
- clear_adv_instance(hdev, NULL, 0x00, false);
+ hci_req_clear_adv_instance(hdev, NULL, 0x00, false);
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
- discov_stopped = hci_stop_discovery(&req);
+ discov_stopped = hci_req_stop_discovery(&req);
list_for_each_entry(conn, &hdev->conn_hash.list, list) {
/* 0x15 == Terminated due to Power Off */
@@ -1671,17 +962,6 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
- cancel_delayed_work(&hdev->power_off);
-
- if (cp->val) {
- mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev,
- data, len);
- err = mgmt_powered(hdev, 1);
- goto failed;
- }
- }
-
if (!!cp->val == hdev_is_powered(hdev)) {
err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
goto failed;
@@ -1805,13 +1085,9 @@ static u8 mgmt_le_support(struct hci_dev *hdev)
return MGMT_STATUS_SUCCESS;
}
-static void set_discoverable_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
+void mgmt_set_discoverable_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
- struct mgmt_mode *cp;
- struct hci_request req;
- bool changed;
BT_DBG("status 0x%02x", status);
@@ -1828,33 +1104,14 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status,
goto remove_cmd;
}
- cp = cmd->param;
- if (cp->val) {
- changed = !hci_dev_test_and_set_flag(hdev, HCI_DISCOVERABLE);
-
- if (hdev->discov_timeout > 0) {
- int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
- queue_delayed_work(hdev->workqueue, &hdev->discov_off,
- to);
- }
- } else {
- changed = hci_dev_test_and_clear_flag(hdev, HCI_DISCOVERABLE);
+ if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
+ hdev->discov_timeout > 0) {
+ int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+ queue_delayed_work(hdev->req_workqueue, &hdev->discov_off, to);
}
send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
-
- if (changed)
- new_settings(hdev, cmd->sk);
-
- /* When the discoverable mode gets changed, make sure
- * that class of device has the limited discoverable
- * bit correctly set. Also update page scan based on whitelist
- * entries.
- */
- hci_req_init(&req, hdev);
- __hci_update_page_scan(&req);
- update_class(&req);
- hci_req_run(&req, NULL);
+ new_settings(hdev, cmd->sk);
remove_cmd:
mgmt_pending_remove(cmd);
@@ -1868,9 +1125,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_cp_set_discoverable *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
u16 timeout;
- u8 scan;
int err;
BT_DBG("request for %s", hdev->name);
@@ -1949,8 +1204,8 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
if (cp->val && hdev->discov_timeout > 0) {
int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
- queue_delayed_work(hdev->workqueue, &hdev->discov_off,
- to);
+ queue_delayed_work(hdev->req_workqueue,
+ &hdev->discov_off, to);
}
err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -1970,105 +1225,28 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
cancel_delayed_work(&hdev->discov_off);
hdev->discov_timeout = timeout;
+ if (cp->val)
+ hci_dev_set_flag(hdev, HCI_DISCOVERABLE);
+ else
+ hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+
/* Limited discoverable mode */
if (cp->val == 0x02)
hci_dev_set_flag(hdev, HCI_LIMITED_DISCOVERABLE);
else
hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- hci_req_init(&req, hdev);
-
- /* The procedure for LE-only controllers is much simpler - just
- * update the advertising data.
- */
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- goto update_ad;
-
- scan = SCAN_PAGE;
-
- if (cp->val) {
- struct hci_cp_write_current_iac_lap hci_cp;
-
- if (cp->val == 0x02) {
- /* Limited discoverable mode */
- hci_cp.num_iac = min_t(u8, hdev->num_iac, 2);
- hci_cp.iac_lap[0] = 0x00; /* LIAC */
- hci_cp.iac_lap[1] = 0x8b;
- hci_cp.iac_lap[2] = 0x9e;
- hci_cp.iac_lap[3] = 0x33; /* GIAC */
- hci_cp.iac_lap[4] = 0x8b;
- hci_cp.iac_lap[5] = 0x9e;
- } else {
- /* General discoverable mode */
- hci_cp.num_iac = 1;
- hci_cp.iac_lap[0] = 0x33; /* GIAC */
- hci_cp.iac_lap[1] = 0x8b;
- hci_cp.iac_lap[2] = 0x9e;
- }
-
- hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP,
- (hci_cp.num_iac * 3) + 1, &hci_cp);
-
- scan |= SCAN_INQUIRY;
- } else {
- hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- }
-
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
-
-update_ad:
- update_adv_data(&req);
-
- err = hci_req_run(&req, set_discoverable_complete);
- if (err < 0)
- mgmt_pending_remove(cmd);
+ queue_work(hdev->req_workqueue, &hdev->discoverable_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
return err;
}
-static void write_fast_connectable(struct hci_request *req, bool enable)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_write_page_scan_activity acp;
- u8 type;
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- return;
-
- if (hdev->hci_ver < BLUETOOTH_VER_1_2)
- return;
-
- if (enable) {
- type = PAGE_SCAN_TYPE_INTERLACED;
-
- /* 160 msec page scan interval */
- acp.interval = cpu_to_le16(0x0100);
- } else {
- type = PAGE_SCAN_TYPE_STANDARD; /* default */
-
- /* default 1.28 sec page scan */
- acp.interval = cpu_to_le16(0x0800);
- }
-
- acp.window = cpu_to_le16(0x0012);
-
- if (__cpu_to_le16(hdev->page_scan_interval) != acp.interval ||
- __cpu_to_le16(hdev->page_scan_window) != acp.window)
- hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
- sizeof(acp), &acp);
-
- if (hdev->page_scan_type != type)
- hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
-}
-
-static void set_connectable_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
+void mgmt_set_connectable_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
- struct mgmt_mode *cp;
- bool conn_changed, discov_changed;
BT_DBG("status 0x%02x", status);
@@ -2084,27 +1262,8 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status,
goto remove_cmd;
}
- cp = cmd->param;
- if (cp->val) {
- conn_changed = !hci_dev_test_and_set_flag(hdev,
- HCI_CONNECTABLE);
- discov_changed = false;
- } else {
- conn_changed = hci_dev_test_and_clear_flag(hdev,
- HCI_CONNECTABLE);
- discov_changed = hci_dev_test_and_clear_flag(hdev,
- HCI_DISCOVERABLE);
- }
-
send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
-
- if (conn_changed || discov_changed) {
- new_settings(hdev, cmd->sk);
- hci_update_page_scan(hdev);
- if (discov_changed)
- mgmt_update_adv_data(hdev);
- hci_update_background_scan(hdev);
- }
+ new_settings(hdev, cmd->sk);
remove_cmd:
mgmt_pending_remove(cmd);
@@ -2134,7 +1293,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
return err;
if (changed) {
- hci_update_page_scan(hdev);
+ hci_req_update_scan(hdev);
hci_update_background_scan(hdev);
return new_settings(hdev, sk);
}
@@ -2147,8 +1306,6 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_mode *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
- u8 scan;
int err;
BT_DBG("request for %s", hdev->name);
@@ -2182,57 +1339,19 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- hci_req_init(&req, hdev);
-
- /* If BR/EDR is not enabled and we disable advertising as a
- * by-product of disabling connectable, we need to update the
- * advertising flags.
- */
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
- if (!cp->val) {
- hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
- }
- update_adv_data(&req);
- } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
- if (cp->val) {
- scan = SCAN_PAGE;
- } else {
- /* If we don't have any whitelist entries just
- * disable all scanning. If there are entries
- * and we had both page and inquiry scanning
- * enabled then fall back to only page scanning.
- * Otherwise no changes are needed.
- */
- if (list_empty(&hdev->whitelist))
- scan = SCAN_DISABLED;
- else if (test_bit(HCI_ISCAN, &hdev->flags))
- scan = SCAN_PAGE;
- else
- goto no_scan_update;
-
- if (test_bit(HCI_ISCAN, &hdev->flags) &&
- hdev->discov_timeout > 0)
- cancel_delayed_work(&hdev->discov_off);
- }
+ if (cp->val) {
+ hci_dev_set_flag(hdev, HCI_CONNECTABLE);
+ } else {
+ if (hdev->discov_timeout > 0)
+ cancel_delayed_work(&hdev->discov_off);
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
+ hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+ hci_dev_clear_flag(hdev, HCI_CONNECTABLE);
}
-no_scan_update:
- /* Update the advertising parameters if necessary */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
- enable_advertising(&req);
-
- err = hci_req_run(&req, set_connectable_complete);
- if (err < 0) {
- mgmt_pending_remove(cmd);
- if (err == -ENODATA)
- err = set_connectable_update_settings(hdev, sk,
- cp->val);
- goto failed;
- }
+ queue_work(hdev->req_workqueue, &hdev->connectable_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
@@ -2508,10 +1627,10 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
struct hci_request req;
hci_req_init(&req, hdev);
- update_adv_data(&req);
- update_scan_rsp_data(&req);
- __hci_update_background_scan(&req);
+ __hci_req_update_adv_data(&req, 0x00);
+ __hci_req_update_scan_rsp_data(&req, 0x00);
hci_req_run(&req, NULL);
+ hci_update_background_scan(hdev);
}
unlock:
@@ -2560,7 +1679,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
enabled = lmp_host_le_capable(hdev);
if (!val)
- clear_adv_instance(hdev, NULL, 0x00, true);
+ hci_req_clear_adv_instance(hdev, NULL, 0x00, true);
if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false;
@@ -2607,7 +1726,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_cp.simul = 0x00;
} else {
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
}
hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
@@ -2722,8 +1841,8 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_req_init(&req, hdev);
- update_class(&req);
- update_eir(&req);
+ __hci_req_update_class(&req);
+ __hci_req_update_eir(&req);
err = hci_req_run(&req, add_uuid_complete);
if (err < 0) {
@@ -2822,8 +1941,8 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
update_class:
hci_req_init(&req, hdev);
- update_class(&req);
- update_eir(&req);
+ __hci_req_update_class(&req);
+ __hci_req_update_eir(&req);
err = hci_req_run(&req, remove_uuid_complete);
if (err < 0) {
@@ -2898,10 +2017,10 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
cancel_delayed_work_sync(&hdev->service_cache);
hci_dev_lock(hdev);
- update_eir(&req);
+ __hci_req_update_eir(&req);
}
- update_class(&req);
+ __hci_req_update_class(&req);
err = hci_req_run(&req, set_class_complete);
if (err < 0) {
@@ -3561,8 +2680,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
conn = hci_connect_le_scan(hdev, &cp->addr.bdaddr,
addr_type, sec_level,
- HCI_LE_CONN_TIMEOUT,
- HCI_ROLE_MASTER);
+ HCI_LE_CONN_TIMEOUT);
}
if (IS_ERR(conn)) {
@@ -3803,16 +2921,6 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
}
-static void update_name(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_write_local_name cp;
-
- memcpy(cp.name, hdev->dev_name, sizeof(cp.name));
-
- hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
-}
-
static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
{
struct mgmt_cp_set_local_name *cp;
@@ -3891,15 +2999,15 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
hci_req_init(&req, hdev);
if (lmp_bredr_capable(hdev)) {
- update_name(&req);
- update_eir(&req);
+ __hci_req_update_name(&req);
+ __hci_req_update_eir(&req);
}
/* The name is stored in the scan response data and so
* no need to udpate the advertising data here.
*/
if (lmp_le_capable(hdev))
- update_scan_rsp_data(&req);
+ __hci_req_update_scan_rsp_data(&req, hdev->cur_adv_instance);
err = hci_req_run(&req, set_name_complete);
if (err < 0)
@@ -4164,145 +3272,9 @@ done:
return err;
}
-static bool trigger_bredr_inquiry(struct hci_request *req, u8 *status)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_inquiry cp;
- /* General inquiry access code (GIAC) */
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
-
- *status = mgmt_bredr_support(hdev);
- if (*status)
- return false;
-
- if (hci_dev_test_flag(hdev, HCI_INQUIRY)) {
- *status = MGMT_STATUS_BUSY;
- return false;
- }
-
- hci_inquiry_cache_flush(hdev);
-
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, sizeof(cp.lap));
- cp.length = DISCOV_BREDR_INQUIRY_LEN;
-
- hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
-
- return true;
-}
-
-static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_scan_param param_cp;
- struct hci_cp_le_set_scan_enable enable_cp;
- u8 own_addr_type;
- int err;
-
- *status = mgmt_le_support(hdev);
- if (*status)
- return false;
-
- if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
- /* Don't let discovery abort an outgoing connection attempt
- * that's using directed advertising.
- */
- if (hci_lookup_le_connect(hdev)) {
- *status = MGMT_STATUS_REJECTED;
- return false;
- }
-
- cancel_adv_timeout(hdev);
- disable_advertising(req);
- }
-
- /* If controller is scanning, it means the background scanning is
- * running. Thus, we should temporarily stop it in order to set the
- * discovery scanning parameters.
- */
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
- hci_req_add_le_scan_disable(req);
-
- /* All active scans will be done with either a resolvable private
- * address (when privacy feature has been enabled) or non-resolvable
- * private address.
- */
- err = hci_update_random_address(req, true, &own_addr_type);
- if (err < 0) {
- *status = MGMT_STATUS_FAILED;
- return false;
- }
-
- memset(&param_cp, 0, sizeof(param_cp));
- param_cp.type = LE_SCAN_ACTIVE;
- param_cp.interval = cpu_to_le16(interval);
- param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
- param_cp.own_address_type = own_addr_type;
-
- hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
- &param_cp);
-
- memset(&enable_cp, 0, sizeof(enable_cp));
- enable_cp.enable = LE_SCAN_ENABLE;
- enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-
- hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
- &enable_cp);
-
- return true;
-}
-
-static bool trigger_discovery(struct hci_request *req, u8 *status)
-{
- struct hci_dev *hdev = req->hdev;
-
- switch (hdev->discovery.type) {
- case DISCOV_TYPE_BREDR:
- if (!trigger_bredr_inquiry(req, status))
- return false;
- break;
-
- case DISCOV_TYPE_INTERLEAVED:
- if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
- &hdev->quirks)) {
- /* During simultaneous discovery, we double LE scan
- * interval. We must leave some time for the controller
- * to do BR/EDR inquiry.
- */
- if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT * 2,
- status))
- return false;
-
- if (!trigger_bredr_inquiry(req, status))
- return false;
-
- return true;
- }
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
- *status = MGMT_STATUS_NOT_SUPPORTED;
- return false;
- }
- /* fall through */
-
- case DISCOV_TYPE_LE:
- if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT, status))
- return false;
- break;
-
- default:
- *status = MGMT_STATUS_INVALID_PARAMS;
- return false;
- }
-
- return true;
-}
-
-static void start_discovery_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
+void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
- unsigned long timeout;
BT_DBG("status %d", status);
@@ -4312,75 +3284,49 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
if (!cmd)
cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
+ if (!cmd)
+ cmd = pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev);
+
if (cmd) {
cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
- if (status) {
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- goto unlock;
- }
-
- hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ hci_dev_unlock(hdev);
+}
- /* If the scan involves LE scan, pick proper timeout to schedule
- * hdev->le_scan_disable that will stop it.
- */
- switch (hdev->discovery.type) {
+static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type,
+ uint8_t *mgmt_status)
+{
+ switch (type) {
case DISCOV_TYPE_LE:
- timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+ *mgmt_status = mgmt_le_support(hdev);
+ if (*mgmt_status)
+ return false;
break;
case DISCOV_TYPE_INTERLEAVED:
- /* When running simultaneous discovery, the LE scanning time
- * should occupy the whole discovery time sine BR/EDR inquiry
- * and LE scanning are scheduled by the controller.
- *
- * For interleaving discovery in comparison, BR/EDR inquiry
- * and LE scanning are done sequentially with separate
- * timeouts.
- */
- if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
- timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
- else
- timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
- break;
+ *mgmt_status = mgmt_le_support(hdev);
+ if (*mgmt_status)
+ return false;
+ /* Intentional fall-through */
case DISCOV_TYPE_BREDR:
- timeout = 0;
+ *mgmt_status = mgmt_bredr_support(hdev);
+ if (*mgmt_status)
+ return false;
break;
default:
- BT_ERR("Invalid discovery type %d", hdev->discovery.type);
- timeout = 0;
- break;
- }
-
- if (timeout) {
- /* When service discovery is used and the controller has
- * a strict duplicate filter, it is important to remember
- * the start and duration of the scan. This is required
- * for restarting scanning during the discovery phase.
- */
- if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
- &hdev->quirks) &&
- hdev->discovery.result_filtering) {
- hdev->discovery.scan_start = jiffies;
- hdev->discovery.scan_duration = timeout;
- }
-
- queue_delayed_work(hdev->workqueue,
- &hdev->le_scan_disable, timeout);
+ *mgmt_status = MGMT_STATUS_INVALID_PARAMS;
+ return false;
}
-unlock:
- hci_dev_unlock(hdev);
+ return true;
}
-static int start_discovery(struct sock *sk, struct hci_dev *hdev,
- void *data, u16 len)
+static int start_discovery_internal(struct sock *sk, struct hci_dev *hdev,
+ u16 op, void *data, u16 len)
{
struct mgmt_cp_start_discovery *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
u8 status;
int err;
@@ -4389,7 +3335,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ err = mgmt_cmd_complete(sk, hdev->id, op,
MGMT_STATUS_NOT_POWERED,
&cp->type, sizeof(cp->type));
goto failed;
@@ -4397,20 +3343,17 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
if (hdev->discovery.state != DISCOVERY_STOPPED ||
hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY, &cp->type,
- sizeof(cp->type));
+ err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY,
+ &cp->type, sizeof(cp->type));
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
- if (!cmd) {
- err = -ENOMEM;
+ if (!discovery_type_is_valid(hdev, cp->type, &status)) {
+ err = mgmt_cmd_complete(sk, hdev->id, op, status,
+ &cp->type, sizeof(cp->type));
goto failed;
}
- cmd->cmd_complete = generic_cmd_complete;
-
/* Clear the discovery filter first to free any previously
* allocated memory for the UUID list.
*/
@@ -4418,29 +3361,43 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hdev->discovery.type = cp->type;
hdev->discovery.report_invalid_rssi = false;
+ if (op == MGMT_OP_START_LIMITED_DISCOVERY)
+ hdev->discovery.limited = true;
+ else
+ hdev->discovery.limited = false;
- hci_req_init(&req, hdev);
-
- if (!trigger_discovery(&req, &status)) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status, &cp->type, sizeof(cp->type));
- mgmt_pending_remove(cmd);
+ cmd = mgmt_pending_add(sk, op, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
goto failed;
}
- err = hci_req_run(&req, start_discovery_complete);
- if (err < 0) {
- mgmt_pending_remove(cmd);
- goto failed;
- }
+ cmd->cmd_complete = generic_cmd_complete;
hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+ queue_work(hdev->req_workqueue, &hdev->discov_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
return err;
}
+static int start_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ return start_discovery_internal(sk, hdev, MGMT_OP_START_DISCOVERY,
+ data, len);
+}
+
+static int start_limited_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ return start_discovery_internal(sk, hdev,
+ MGMT_OP_START_LIMITED_DISCOVERY,
+ data, len);
+}
+
static int service_discovery_cmd_complete(struct mgmt_pending_cmd *cmd,
u8 status)
{
@@ -4453,7 +3410,6 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
{
struct mgmt_cp_start_service_discovery *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
u16 uuid_count, expected_len;
u8 status;
@@ -4502,6 +3458,13 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
goto failed;
}
+ if (!discovery_type_is_valid(hdev, cp->type, &status)) {
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
+ goto failed;
+ }
+
cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY,
hdev, data, len);
if (!cmd) {
@@ -4534,30 +3497,16 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
}
}
- hci_req_init(&req, hdev);
-
- if (!trigger_discovery(&req, &status)) {
- err = mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_START_SERVICE_DISCOVERY,
- status, &cp->type, sizeof(cp->type));
- mgmt_pending_remove(cmd);
- goto failed;
- }
-
- err = hci_req_run(&req, start_discovery_complete);
- if (err < 0) {
- mgmt_pending_remove(cmd);
- goto failed;
- }
-
hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+ queue_work(hdev->req_workqueue, &hdev->discov_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
return err;
}
-static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
@@ -4571,9 +3520,6 @@ static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
mgmt_pending_remove(cmd);
}
- if (!status)
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-
hci_dev_unlock(hdev);
}
@@ -4582,7 +3528,6 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_cp_stop_discovery *mgmt_cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
@@ -4611,24 +3556,9 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
cmd->cmd_complete = generic_cmd_complete;
- hci_req_init(&req, hdev);
-
- hci_stop_discovery(&req);
-
- err = hci_req_run(&req, stop_discovery_complete);
- if (!err) {
- hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
- goto unlock;
- }
-
- mgmt_pending_remove(cmd);
-
- /* If no HCI commands were sent we're done */
- if (err == -ENODATA) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0,
- &mgmt_cp->type, sizeof(mgmt_cp->type));
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- }
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
+ queue_work(hdev->req_workqueue, &hdev->discov_update);
+ err = 0;
unlock:
hci_dev_unlock(hdev);
@@ -4776,7 +3706,7 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
NULL, 0);
hci_req_init(&req, hdev);
- update_eir(&req);
+ __hci_req_update_eir(&req);
hci_req_run(&req, NULL);
hci_dev_unlock(hdev);
@@ -4826,7 +3756,6 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
* set up earlier, then re-enable multi-instance advertising.
*/
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) ||
list_empty(&hdev->adv_instances))
goto unlock;
@@ -4842,7 +3771,7 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
hci_req_init(&req, hdev);
- err = schedule_adv_instance(&req, instance, true);
+ err = __hci_req_schedule_adv_instance(&req, instance, true);
if (!err)
err = hci_req_run(&req, enable_advertising_instance);
@@ -4892,6 +3821,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
bool changed;
if (cp->val) {
+ hdev->cur_adv_instance = 0x00;
changed = !hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING);
if (cp->val == 0x02)
hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
@@ -4939,11 +3869,12 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
* We cannot use update_[adv|scan_rsp]_data() here as the
* HCI_ADVERTISING flag is not yet set.
*/
- update_inst_adv_data(&req, 0x00);
- update_inst_scan_rsp_data(&req, 0x00);
- enable_advertising(&req);
+ hdev->cur_adv_instance = 0x00;
+ __hci_req_update_adv_data(&req, 0x00);
+ __hci_req_update_scan_rsp_data(&req, 0x00);
+ __hci_req_enable_advertising(&req);
} else {
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
}
err = hci_req_run(&req, set_advertising_complete);
@@ -5140,7 +4071,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
- write_fast_connectable(&req, cp->val);
+ __hci_req_write_fast_connectable(&req, cp->val);
err = hci_req_run(&req, fast_connectable_complete);
if (err < 0) {
@@ -5275,20 +4206,20 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto unlock;
}
- /* We need to flip the bit already here so that update_adv_data
- * generates the correct flags.
+ /* We need to flip the bit already here so that
+ * hci_req_update_adv_data generates the correct flags.
*/
hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
hci_req_init(&req, hdev);
- write_fast_connectable(&req, false);
- __hci_update_page_scan(&req);
+ __hci_req_write_fast_connectable(&req, false);
+ __hci_req_update_scan(&req);
/* Since only the advertising data flags will change, there
* is no need to update the scan response data.
*/
- update_adv_data(&req);
+ __hci_req_update_adv_data(&req, hdev->cur_adv_instance);
err = hci_req_run(&req, set_bredr_complete);
if (err < 0)
@@ -6076,10 +5007,9 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
}
/* This function requires the caller holds hdev->lock */
-static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
+static int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr,
u8 addr_type, u8 auto_connect)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
params = hci_conn_params_add(hdev, addr, addr_type);
@@ -6099,26 +5029,17 @@ static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
*/
if (params->explicit_connect)
list_add(&params->action, &hdev->pend_le_conns);
-
- __hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_REPORT:
if (params->explicit_connect)
list_add(&params->action, &hdev->pend_le_conns);
else
list_add(&params->action, &hdev->pend_le_reports);
- __hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
- if (!is_connected(hdev, addr, addr_type)) {
+ if (!is_connected(hdev, addr, addr_type))
list_add(&params->action, &hdev->pend_le_conns);
- /* If we are in scan phase of connecting, we were
- * already added to pend_le_conns and scanning.
- */
- if (params->auto_connect != HCI_AUTO_CONN_EXPLICIT)
- __hci_update_background_scan(req);
- }
break;
}
@@ -6142,31 +5063,10 @@ static void device_added(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk);
}
-static void add_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("status 0x%02x", status);
-
- hci_dev_lock(hdev);
-
- cmd = pending_find(MGMT_OP_ADD_DEVICE, hdev);
- if (!cmd)
- goto unlock;
-
- cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
-
-unlock:
- hci_dev_unlock(hdev);
-}
-
static int add_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_add_device *cp = data;
- struct mgmt_pending_cmd *cmd;
- struct hci_request req;
u8 auto_conn, addr_type;
int err;
@@ -6183,24 +5083,15 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
- hci_req_init(&req, hdev);
-
hci_dev_lock(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);
- if (!cmd) {
- err = -ENOMEM;
- goto unlock;
- }
-
- cmd->cmd_complete = addr_cmd_complete;
-
if (cp->addr.type == BDADDR_BREDR) {
/* Only incoming connections action is supported for now */
if (cp->action != 0x01) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6209,7 +5100,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
if (err)
goto unlock;
- __hci_update_page_scan(&req);
+ hci_req_update_scan(hdev);
goto added;
}
@@ -6229,33 +5120,31 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
* hci_conn_params_lookup.
*/
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
- err = cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
/* If the connection parameters don't exist for this device,
* they will be created and configured with defaults.
*/
- if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type,
+ if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
auto_conn) < 0) {
- err = cmd->cmd_complete(cmd, MGMT_STATUS_FAILED);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_FAILED, &cp->addr,
+ sizeof(cp->addr));
goto unlock;
}
+ hci_update_background_scan(hdev);
+
added:
device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
- err = hci_req_run(&req, add_device_complete);
- if (err < 0) {
- /* ENODATA means no HCI commands were needed (e.g. if
- * the adapter is powered off).
- */
- if (err == -ENODATA)
- err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
- mgmt_pending_remove(cmd);
- }
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_SUCCESS, &cp->addr,
+ sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
@@ -6273,55 +5162,25 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
}
-static void remove_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("status 0x%02x", status);
-
- hci_dev_lock(hdev);
-
- cmd = pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
- if (!cmd)
- goto unlock;
-
- cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
-
-unlock:
- hci_dev_unlock(hdev);
-}
-
static int remove_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_remove_device *cp = data;
- struct mgmt_pending_cmd *cmd;
- struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
- hci_req_init(&req, hdev);
-
hci_dev_lock(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len);
- if (!cmd) {
- err = -ENOMEM;
- goto unlock;
- }
-
- cmd->cmd_complete = addr_cmd_complete;
-
if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
struct hci_conn_params *params;
u8 addr_type;
if (!bdaddr_type_is_valid(cp->addr.type)) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6330,13 +5189,15 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
&cp->addr.bdaddr,
cp->addr.type);
if (err) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr,
+ sizeof(cp->addr));
goto unlock;
}
- __hci_update_page_scan(&req);
+ hci_req_update_scan(hdev);
device_removed(sk, hdev, &cp->addr.bdaddr,
cp->addr.type);
@@ -6351,33 +5212,36 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
* hci_conn_params_lookup.
*/
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
addr_type);
if (!params) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
list_del(&params->action);
list_del(&params->list);
kfree(params);
- __hci_update_background_scan(&req);
+ hci_update_background_scan(hdev);
device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
} else {
@@ -6385,9 +5249,10 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
struct bdaddr_list *b, *btmp;
if (cp->addr.type) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6397,7 +5262,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
kfree(b);
}
- __hci_update_page_scan(&req);
+ hci_req_update_scan(hdev);
list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -6414,20 +5279,13 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
BT_DBG("All LE connection parameters were removed");
- __hci_update_background_scan(&req);
+ hci_update_background_scan(hdev);
}
complete:
- err = hci_req_run(&req, remove_device_complete);
- if (err < 0) {
- /* ENODATA means no HCI commands were needed (e.g. if
- * the adapter is powered off).
- */
- if (err == -ENODATA)
- err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
- mgmt_pending_remove(cmd);
- }
-
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_SUCCESS, &cp->addr,
+ sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
return err;
@@ -6898,7 +5756,7 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
rand, sizeof(rand));
}
- flags = get_adv_discov_flags(hdev);
+ flags = mgmt_get_adv_discov_flags(hdev);
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
flags |= LE_AD_NO_BREDR;
@@ -6953,10 +5811,10 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
{
struct mgmt_rp_read_adv_features *rp;
size_t rp_len;
- int err, i;
- bool instance;
+ int err;
struct adv_info *adv_instance;
u32 supported_flags;
+ u8 *instance;
BT_DBG("%s", hdev->name);
@@ -6966,12 +5824,7 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
- rp_len = sizeof(*rp);
-
- instance = hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE);
- if (instance)
- rp_len += hdev->adv_instance_cnt;
-
+ rp_len = sizeof(*rp) + hdev->adv_instance_cnt;
rp = kmalloc(rp_len, GFP_ATOMIC);
if (!rp) {
hci_dev_unlock(hdev);
@@ -6984,19 +5837,12 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
rp->max_instances = HCI_MAX_ADV_INSTANCES;
+ rp->num_instances = hdev->adv_instance_cnt;
- if (instance) {
- i = 0;
- list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
- if (i >= hdev->adv_instance_cnt)
- break;
-
- rp->instance[i] = adv_instance->instance;
- i++;
- }
- rp->num_instances = hdev->adv_instance_cnt;
- } else {
- rp->num_instances = 0;
+ instance = rp->instance;
+ list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
+ *instance = adv_instance->instance;
+ instance++;
}
hci_dev_unlock(hdev);
@@ -7016,17 +5862,19 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
int i, cur_len;
bool flags_managed = false;
bool tx_power_managed = false;
- u32 flags_params = MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV |
- MGMT_ADV_FLAG_MANAGED_FLAGS;
- if (is_adv_data && (adv_flags & flags_params)) {
- flags_managed = true;
- max_len -= 3;
- }
+ if (is_adv_data) {
+ if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
+ MGMT_ADV_FLAG_LIMITED_DISCOV |
+ MGMT_ADV_FLAG_MANAGED_FLAGS)) {
+ flags_managed = true;
+ max_len -= 3;
+ }
- if (is_adv_data && (adv_flags & MGMT_ADV_FLAG_TX_POWER)) {
- tx_power_managed = true;
- max_len -= 3;
+ if (adv_flags & MGMT_ADV_FLAG_TX_POWER) {
+ tx_power_managed = true;
+ max_len -= 3;
+ }
}
if (len > max_len)
@@ -7067,9 +5915,6 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
- if (status)
- hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
-
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
if (!adv_instance->pending)
continue;
@@ -7085,7 +5930,7 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
cancel_adv_timeout(hdev);
hci_remove_adv_instance(hdev, instance);
- advertising_removed(cmd ? cmd->sk : NULL, hdev, instance);
+ mgmt_advertising_removed(cmd ? cmd->sk : NULL, hdev, instance);
}
if (!cmd)
@@ -7107,31 +5952,6 @@ unlock:
hci_dev_unlock(hdev);
}
-void mgmt_adv_timeout_expired(struct hci_dev *hdev)
-{
- u8 instance;
- struct hci_request req;
-
- hdev->adv_instance_timeout = 0;
-
- instance = get_current_adv_instance(hdev);
- if (instance == 0x00)
- return;
-
- hci_dev_lock(hdev);
- hci_req_init(&req, hdev);
-
- clear_adv_instance(hdev, &req, instance, false);
-
- if (list_empty(&hdev->adv_instances))
- disable_advertising(&req);
-
- if (!skb_queue_empty(&req.cmd_q))
- hci_req_run(&req, NULL);
-
- hci_dev_unlock(hdev);
-}
-
static int add_advertising(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
@@ -7155,6 +5975,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
status);
+ if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+ MGMT_STATUS_INVALID_PARAMS);
+
flags = __le32_to_cpu(cp->flags);
timeout = __le16_to_cpu(cp->timeout);
duration = __le16_to_cpu(cp->duration);
@@ -7206,9 +6030,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
* actually added.
*/
if (hdev->adv_instance_cnt > prev_instance_cnt)
- advertising_added(sk, hdev, cp->instance);
-
- hci_dev_set_flag(hdev, HCI_ADVERTISING_INSTANCE);
+ mgmt_advertising_added(sk, hdev, cp->instance);
if (hdev->cur_adv_instance == cp->instance) {
/* If the currently advertised instance is being changed then
@@ -7253,7 +6075,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
- err = schedule_adv_instance(&req, schedule_instance, true);
+ err = __hci_req_schedule_adv_instance(&req, schedule_instance, true);
if (!err)
err = hci_req_run(&req, add_advertising_complete);
@@ -7325,7 +6147,7 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
- if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) {
+ if (list_empty(&hdev->adv_instances)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
goto unlock;
@@ -7333,10 +6155,10 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
- clear_adv_instance(hdev, &req, cp->instance, true);
+ hci_req_clear_adv_instance(hdev, &req, cp->instance, true);
if (list_empty(&hdev->adv_instances))
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
/* If no HCI commands have been collected so far or the HCI_ADVERTISING
* flag is set or the device isn't powered then we have no HCI
@@ -7369,6 +6191,62 @@ unlock:
return err;
}
+static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
+{
+ u8 max_len = HCI_MAX_AD_LENGTH;
+
+ if (is_adv_data) {
+ if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
+ MGMT_ADV_FLAG_LIMITED_DISCOV |
+ MGMT_ADV_FLAG_MANAGED_FLAGS))
+ max_len -= 3;
+
+ if (adv_flags & MGMT_ADV_FLAG_TX_POWER)
+ max_len -= 3;
+ }
+
+ return max_len;
+}
+
+static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
+{
+ struct mgmt_cp_get_adv_size_info *cp = data;
+ struct mgmt_rp_get_adv_size_info rp;
+ u32 flags, supported_flags;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_REJECTED);
+
+ if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ flags = __le32_to_cpu(cp->flags);
+
+ /* The current implementation only supports a subset of the specified
+ * flags.
+ */
+ supported_flags = get_supported_adv_flags(hdev);
+ if (flags & ~supported_flags)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ rp.instance = cp->instance;
+ rp.flags = cp->flags;
+ rp.max_adv_data_len = tlv_data_max_len(flags, true);
+ rp.max_scan_rsp_len = tlv_data_max_len(flags, false);
+
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+
+ return err;
+}
+
static const struct hci_mgmt_handler mgmt_handlers[] = {
{ NULL }, /* 0x0000 (no command) */
{ read_version, MGMT_READ_VERSION_SIZE,
@@ -7456,6 +6334,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ add_advertising, MGMT_ADD_ADVERTISING_SIZE,
HCI_MGMT_VAR_LEN },
{ remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE },
+ { get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE },
+ { start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)
@@ -7526,9 +6406,8 @@ void mgmt_index_removed(struct hci_dev *hdev)
}
/* This function requires the caller holds hdev->lock */
-static void restart_le_actions(struct hci_request *req)
+static void restart_le_actions(struct hci_dev *hdev)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *p;
list_for_each_entry(p, &hdev->le_conn_params, list) {
@@ -7549,141 +6428,35 @@ static void restart_le_actions(struct hci_request *req)
break;
}
}
-
- __hci_update_background_scan(req);
}
-static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+void mgmt_power_on(struct hci_dev *hdev, int err)
{
struct cmd_lookup match = { NULL, hdev };
- BT_DBG("status 0x%02x", status);
-
- if (!status) {
- /* Register the available SMP channels (BR/EDR and LE) only
- * when successfully powering on the controller. This late
- * registration is required so that LE SMP can clearly
- * decide if the public address or static address is used.
- */
- smp_register(hdev);
- }
+ BT_DBG("err %d", err);
hci_dev_lock(hdev);
+ if (!err) {
+ restart_le_actions(hdev);
+ hci_update_background_scan(hdev);
+ }
+
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
new_settings(hdev, match.sk);
- hci_dev_unlock(hdev);
-
if (match.sk)
sock_put(match.sk);
-}
-
-static int powered_update_hci(struct hci_dev *hdev)
-{
- struct hci_request req;
- struct adv_info *adv_instance;
- u8 link_sec;
-
- hci_req_init(&req, hdev);
-
- if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
- !lmp_host_ssp_capable(hdev)) {
- u8 mode = 0x01;
-
- hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
-
- if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
- u8 support = 0x01;
-
- hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT,
- sizeof(support), &support);
- }
- }
-
- if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
- lmp_bredr_capable(hdev)) {
- struct hci_cp_write_le_host_supported cp;
-
- cp.le = 0x01;
- cp.simul = 0x00;
-
- /* Check first if we already have the right
- * host state (host features set)
- */
- if (cp.le != lmp_host_le_capable(hdev) ||
- cp.simul != lmp_host_le_br_capable(hdev))
- hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
- sizeof(cp), &cp);
- }
-
- if (lmp_le_capable(hdev)) {
- /* Make sure the controller has a good default for
- * advertising data. This also applies to the case
- * where BR/EDR was toggled during the AUTO_OFF phase.
- */
- if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
- (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))) {
- update_adv_data(&req);
- update_scan_rsp_data(&req);
- }
-
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
- hdev->cur_adv_instance == 0x00 &&
- !list_empty(&hdev->adv_instances)) {
- adv_instance = list_first_entry(&hdev->adv_instances,
- struct adv_info, list);
- hdev->cur_adv_instance = adv_instance->instance;
- }
-
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
- enable_advertising(&req);
- else if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
- hdev->cur_adv_instance)
- schedule_adv_instance(&req, hdev->cur_adv_instance,
- true);
- restart_le_actions(&req);
- }
-
- link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
- if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
- hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE,
- sizeof(link_sec), &link_sec);
-
- if (lmp_bredr_capable(hdev)) {
- if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
- write_fast_connectable(&req, true);
- else
- write_fast_connectable(&req, false);
- __hci_update_page_scan(&req);
- update_class(&req);
- update_name(&req);
- update_eir(&req);
- }
-
- return hci_req_run(&req, powered_complete);
+ hci_dev_unlock(hdev);
}
-int mgmt_powered(struct hci_dev *hdev, u8 powered)
+void __mgmt_power_off(struct hci_dev *hdev)
{
struct cmd_lookup match = { NULL, hdev };
u8 status, zero_cod[] = { 0, 0, 0 };
- int err;
-
- if (!hci_dev_test_flag(hdev, HCI_MGMT))
- return 0;
-
- if (powered) {
- if (powered_update_hci(hdev) == 0)
- return 0;
-
- mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp,
- &match);
- goto new_settings;
- }
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
@@ -7705,13 +6478,10 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
zero_cod, sizeof(zero_cod), NULL);
-new_settings:
- err = new_settings(hdev, match.sk);
+ new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
-
- return err;
}
void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
@@ -7733,43 +6503,6 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
mgmt_pending_remove(cmd);
}
-void mgmt_discoverable_timeout(struct hci_dev *hdev)
-{
- struct hci_request req;
-
- hci_dev_lock(hdev);
-
- /* When discoverable timeout triggers, then just make sure
- * the limited discoverable flag is cleared. Even in the case
- * of a timeout triggered from general discoverable, it is
- * safe to unconditionally clear the flag.
- */
- hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
-
- hci_req_init(&req, hdev);
- if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
- u8 scan = SCAN_PAGE;
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
- sizeof(scan), &scan);
- }
- update_class(&req);
-
- /* Advertising instances don't use the global discoverable setting, so
- * only update AD if advertising was enabled using Set Advertising.
- */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
- update_adv_data(&req);
-
- hci_req_run(&req, NULL);
-
- hdev->discov_timeout = 0;
-
- new_settings(hdev, NULL);
-
- hci_dev_unlock(hdev);
-}
-
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent)
{
@@ -8312,7 +7045,7 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS))
hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE,
sizeof(enable), &enable);
- update_eir(&req);
+ __hci_req_update_eir(&req);
} else {
clear_eir(&req);
}
@@ -8452,7 +7185,7 @@ static void restart_le_scan(struct hci_dev *hdev)
hdev->discovery.scan_duration))
return;
- queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
+ queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_restart,
DISCOV_LE_RESTART_DELAY);
}
@@ -8527,6 +7260,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
return;
}
+ if (hdev->discovery.limited) {
+ /* Check for limited discoverable bit */
+ if (dev_class) {
+ if (!(dev_class[1] & 0x20))
+ return;
+ } else {
+ u8 *flags = eir_get_data(eir, eir_len, EIR_FLAGS, NULL);
+ if (!flags || !(flags[0] & LE_AD_LIMITED))
+ return;
+ }
+ }
+
/* Make sure that the buffer is big enough. The 5 extra bytes
* are for the potential CoD field.
*/
@@ -8556,7 +7301,8 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
/* Copy EIR or advertising data into event */
memcpy(ev->eir, eir, eir_len);
- if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
+ if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
+ NULL))
eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
dev_class, 3);
@@ -8606,35 +7352,6 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
}
-static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- BT_DBG("%s status %u", hdev->name, status);
-}
-
-void mgmt_reenable_advertising(struct hci_dev *hdev)
-{
- struct hci_request req;
- u8 instance;
-
- if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
- return;
-
- instance = get_current_adv_instance(hdev);
-
- hci_req_init(&req, hdev);
-
- if (instance) {
- schedule_adv_instance(&req, instance, true);
- } else {
- update_adv_data(&req);
- update_scan_rsp_data(&req);
- enable_advertising(&req);
- }
-
- hci_req_run(&req, adv_enable_complete);
-}
-
static struct hci_mgmt_chan chan = {
.channel = HCI_CHANNEL_CONTROL,
.handler_count = ARRAY_SIZE(mgmt_handlers),
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 29709fbfd1f5..f7eb02f09b54 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -692,11 +692,9 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s)
static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
{
- struct rfcomm_session *s;
- struct list_head *p, *n;
+ struct rfcomm_session *s, *n;
struct l2cap_chan *chan;
- list_for_each_safe(p, n, &session_list) {
- s = list_entry(p, struct rfcomm_session, list);
+ list_for_each_entry_safe(s, n, &session_list, list) {
chan = l2cap_pi(s->sock->sk)->chan;
if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) &&
@@ -709,16 +707,14 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s,
int err)
{
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
s->state = BT_CLOSED;
BT_DBG("session %p state %ld err %d", s, s->state, err);
/* Close all dlcs */
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
d->state = BT_CLOSED;
__rfcomm_dlc_close(d, err);
}
@@ -1771,13 +1767,11 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
static void rfcomm_process_connect(struct rfcomm_session *s)
{
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
BT_DBG("session %p state %ld", s, s->state);
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (d->state == BT_CONFIG) {
d->mtu = s->mtu;
if (rfcomm_check_security(d)) {
@@ -1843,14 +1837,11 @@ static int rfcomm_process_tx(struct rfcomm_dlc *d)
static void rfcomm_process_dlcs(struct rfcomm_session *s)
{
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
BT_DBG("session %p state %ld", s, s->state);
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
-
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
__rfcomm_dlc_close(d, ETIMEDOUT);
continue;
@@ -1985,14 +1976,11 @@ static struct rfcomm_session *rfcomm_check_connection(struct rfcomm_session *s)
static void rfcomm_process_sessions(void)
{
- struct list_head *p, *n;
+ struct rfcomm_session *s, *n;
rfcomm_lock();
- list_for_each_safe(p, n, &session_list) {
- struct rfcomm_session *s;
- s = list_entry(p, struct rfcomm_session, list);
-
+ list_for_each_entry_safe(s, n, &session_list, list) {
if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
s->state = BT_DISCONN;
rfcomm_send_disc(s, 0);
@@ -2075,15 +2063,12 @@ failed:
static void rfcomm_kill_listener(void)
{
- struct rfcomm_session *s;
- struct list_head *p, *n;
+ struct rfcomm_session *s, *n;
BT_DBG("");
- list_for_each_safe(p, n, &session_list) {
- s = list_entry(p, struct rfcomm_session, list);
+ list_for_each_entry_safe(s, n, &session_list, list)
rfcomm_session_del(s);
- }
}
static int rfcomm_run(void *unused)
@@ -2113,8 +2098,7 @@ static int rfcomm_run(void *unused)
static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
{
struct rfcomm_session *s;
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
@@ -2122,9 +2106,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
if (!s)
return;
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
-
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
rfcomm_dlc_clear_timer(d);
if (status || encrypt == 0x00) {
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index a642bb829d09..82e3e9705017 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -135,6 +135,7 @@ static void fdb_del_external_learn(struct net_bridge_fdb_entry *f)
{
struct switchdev_obj_port_fdb fdb = {
.obj = {
+ .orig_dev = f->dst->dev,
.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.flags = SWITCHDEV_F_DEFER,
},
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index ec02f5869a78..c367b3e1b5ac 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -493,7 +493,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
dev->priv_flags |= IFF_BRIDGE_PORT;
- err = netdev_master_upper_dev_link(dev, br->dev);
+ err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL);
if (err)
goto err5;
@@ -511,8 +511,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n");
- if (nbp_vlan_init(p))
+ err = nbp_vlan_init(p);
+ if (err) {
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
+ goto err6;
+ }
spin_lock_bh(&br->lock);
changed_addr = br_stp_recalculate_bridge_id(br);
@@ -533,6 +536,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return 0;
+err6:
+ list_del_rcu(&p->list);
+ br_fdb_delete_by_port(br, p, 0, 1);
+ nbp_update_port_count(br);
+ netdev_upper_dev_unlink(dev, br->dev);
+
err5:
dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev);
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index cd8deea2d074..30e105f57f0d 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -7,6 +7,7 @@
#include <linux/if_ether.h>
#include <net/ip.h>
#include <net/netlink.h>
+#include <net/switchdev.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
#include <net/addrconf.h>
@@ -210,10 +211,32 @@ static inline size_t rtnl_mdb_nlmsg_size(void)
static void __br_mdb_notify(struct net_device *dev, struct br_mdb_entry *entry,
int type)
{
+ struct switchdev_obj_port_mdb mdb = {
+ .obj = {
+ .id = SWITCHDEV_OBJ_ID_PORT_MDB,
+ .flags = SWITCHDEV_F_DEFER,
+ },
+ .vid = entry->vid,
+ };
+ struct net_device *port_dev;
struct net *net = dev_net(dev);
struct sk_buff *skb;
int err = -ENOBUFS;
+ port_dev = __dev_get_by_index(net, entry->ifindex);
+ if (entry->addr.proto == htons(ETH_P_IP))
+ ip_eth_mc_map(entry->addr.u.ip4, mdb.addr);
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ ipv6_eth_mc_map(&entry->addr.u.ip6, mdb.addr);
+#endif
+
+ mdb.obj.orig_dev = port_dev;
+ if (port_dev && type == RTM_NEWMDB)
+ switchdev_port_obj_add(port_dev, &mdb.obj);
+ else if (port_dev && type == RTM_DELMDB)
+ switchdev_port_obj_del(port_dev, &mdb.obj);
+
skb = nlmsg_new(rtnl_mdb_nlmsg_size(), GFP_ATOMIC);
if (!skb)
goto errout;
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 5f3f64553179..b3cca126b103 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -40,6 +40,7 @@ void br_log_state(const struct net_bridge_port *p)
void br_set_state(struct net_bridge_port *p, unsigned int state)
{
struct switchdev_attr attr = {
+ .orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
.flags = SWITCHDEV_F_DEFER,
.u.stp_state = state,
@@ -570,6 +571,7 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)
int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
{
struct switchdev_attr attr = {
+ .orig_dev = br->dev,
.id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
.u.ageing_time = ageing_time,
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 8a7ada8bb947..a31ac6ad76a2 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -37,6 +37,7 @@ static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
void br_init_port(struct net_bridge_port *p)
{
struct switchdev_attr attr = {
+ .orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER,
.u.ageing_time = jiffies_to_clock_t(p->br->ageing_time),
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 8365bd53c421..6b8091407ca3 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -22,7 +22,6 @@
#include "br_private.h"
-#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_bridge(cd) ((struct net_bridge *)netdev_priv(to_net_dev(cd)))
/*
@@ -814,7 +813,7 @@ static ssize_t brforward_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct net_bridge *br = to_bridge(dev);
int n;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 1394da63614a..85e43af4af7a 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -73,6 +73,7 @@ static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
u16 vid, u16 flags)
{
struct switchdev_obj_port_vlan v = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
@@ -120,6 +121,7 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
u16 vid)
{
struct switchdev_obj_port_vlan v = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid_begin = vid,
.vid_end = vid,
@@ -624,9 +626,21 @@ void br_recalculate_fwd_mask(struct net_bridge *br)
int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
{
+ struct switchdev_attr attr = {
+ .orig_dev = br->dev,
+ .id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
+ .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
+ .u.vlan_filtering = val,
+ };
+ int err;
+
if (br->vlan_enabled == val)
return 0;
+ err = switchdev_port_attr_set(br->dev, &attr);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
br->vlan_enabled = val;
br_manage_promisc(br);
recalculate_group_addr(br);
@@ -637,13 +651,15 @@ int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
{
+ int err;
+
if (!rtnl_trylock())
return restart_syscall();
- __br_vlan_filter_toggle(br, val);
+ err = __br_vlan_filter_toggle(br, val);
rtnl_unlock();
- return 0;
+ return err;
}
int __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
@@ -891,6 +907,12 @@ err_rhtbl:
int nbp_vlan_init(struct net_bridge_port *p)
{
+ struct switchdev_attr attr = {
+ .orig_dev = p->br->dev,
+ .id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
+ .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
+ .u.vlan_filtering = p->br->vlan_enabled,
+ };
struct net_bridge_vlan_group *vg;
int ret = -ENOMEM;
@@ -898,6 +920,10 @@ int nbp_vlan_init(struct net_bridge_port *p)
if (!vg)
goto out;
+ ret = switchdev_port_attr_set(p->dev, &attr);
+ if (ret && ret != -EOPNOTSUPP)
+ goto err_vlan_enabled;
+
ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
if (ret)
goto err_rhtbl;
@@ -917,6 +943,7 @@ err_vlan_add:
RCU_INIT_POINTER(p->vlgrp, NULL);
synchronize_rcu();
rhashtable_destroy(&vg->vlan_hash);
+err_vlan_enabled:
err_rhtbl:
kfree(vg);
diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c
index 17fd5f2cb4b8..98de6e7fd86d 100644
--- a/net/bridge/netfilter/ebt_ip6.c
+++ b/net/bridge/netfilter/ebt_ip6.c
@@ -65,8 +65,8 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)
return false;
if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO))
return false;
- if (!(info->bitmask & ( EBT_IP6_DPORT |
- EBT_IP6_SPORT | EBT_IP6_ICMP6)))
+ if (!(info->bitmask & (EBT_IP6_DPORT |
+ EBT_IP6_SPORT | EBT_IP6_ICMP6)))
return true;
/* min icmpv6 headersize is 4, so sizeof(_pkthdr) is ok. */
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index 0ad639a96142..152300d164ac 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -36,14 +36,12 @@ static int ebt_log_tg_check(const struct xt_tgchk_param *par)
return 0;
}
-struct tcpudphdr
-{
+struct tcpudphdr {
__be16 src;
__be16 dst;
};
-struct arppayload
-{
+struct arppayload {
unsigned char mac_src[ETH_ALEN];
unsigned char ip_src[4];
unsigned char mac_dst[ETH_ALEN];
@@ -152,7 +150,8 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum,
ntohs(ah->ar_op));
/* If it's for Ethernet and the lengths are OK,
- * then log the ARP payload */
+ * then log the ARP payload
+ */
if (ah->ar_hrd == htons(1) &&
ah->ar_hln == ETH_ALEN &&
ah->ar_pln == sizeof(__be32)) {
diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c
index 0c40570069ba..6b731e12ecfa 100644
--- a/net/bridge/netfilter/ebt_stp.c
+++ b/net/bridge/netfilter/ebt_stp.c
@@ -41,7 +41,7 @@ struct stp_config_pdu {
#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
static bool ebt_filter_config(const struct ebt_stp_info *info,
- const struct stp_config_pdu *stpc)
+ const struct stp_config_pdu *stpc)
{
const struct ebt_stp_config_info *c;
uint16_t v16;
diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c
index 618568888128..98c221dbf059 100644
--- a/net/bridge/netfilter/ebt_vlan.c
+++ b/net/bridge/netfilter/ebt_vlan.c
@@ -66,7 +66,8 @@ ebt_vlan_mt(const struct sk_buff *skb, struct xt_action_param *par)
* - Canonical Format Indicator (CFI). The Canonical Format Indicator
* (CFI) is a single bit flag value. Currently ignored.
* - VLAN Identifier (VID). The VID is encoded as
- * an unsigned binary number. */
+ * an unsigned binary number.
+ */
id = TCI & VLAN_VID_MASK;
prio = (TCI >> 13) & 0x7;
@@ -98,7 +99,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
}
/* Check for bitmask range
- * True if even one bit is out of mask */
+ * True if even one bit is out of mask
+ */
if (info->bitmask & ~EBT_VLAN_MASK) {
pr_debug("bitmask %2X is out of mask (%2X)\n",
info->bitmask, EBT_VLAN_MASK);
@@ -117,7 +119,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
* 0 - The null VLAN ID.
* 1 - The default Port VID (PVID)
* 0x0FFF - Reserved for implementation use.
- * if_vlan.h: VLAN_N_VID 4096. */
+ * if_vlan.h: VLAN_N_VID 4096.
+ */
if (GET_BITMASK(EBT_VLAN_ID)) {
if (!!info->id) { /* if id!=0 => check vid range */
if (info->id > VLAN_N_VID) {
@@ -128,7 +131,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
/* Note: This is valid VLAN-tagged frame point.
* Any value of user_priority are acceptable,
* but should be ignored according to 802.1Q Std.
- * So we just drop the prio flag. */
+ * So we just drop the prio flag.
+ */
info->bitmask &= ~EBT_VLAN_PRIO;
}
/* Else, id=0 (null VLAN ID) => user_priority range (any?) */
@@ -143,7 +147,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
}
/* Check for encapsulated proto range - it is possible to be
* any value for u_short range.
- * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS */
+ * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS
+ */
if (GET_BITMASK(EBT_VLAN_ENCAP)) {
if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
pr_debug("encap frame length %d is less than "
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index 32eccd101f26..593a1bdc079e 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
- (1 << NF_BR_LOCAL_OUT))
+ (1 << NF_BR_LOCAL_OUT))
static struct ebt_entries initial_chains[] = {
{
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index ec55358f00c8..eb33919821ee 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
- (1 << NF_BR_POST_ROUTING))
+ (1 << NF_BR_POST_ROUTING))
static struct ebt_entries initial_chains[] = {
{
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f46ca417bf2d..67b2e27999aa 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -35,8 +35,7 @@
"report to author: "format, ## args)
/* #define BUGPRINT(format, args...) */
-/*
- * Each cpu has its own set of counters, so there is no need for write_lock in
+/* Each cpu has its own set of counters, so there is no need for write_lock in
* the softirq
* For reading or updating the counters, the user context needs to
* get a write_lock
@@ -46,7 +45,7 @@
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
- COUNTER_OFFSET(n) * cpu))
+ COUNTER_OFFSET(n) * cpu))
@@ -126,7 +125,7 @@ ebt_dev_check(const char *entry, const struct net_device *device)
/* process standard matches */
static inline int
ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out)
+ const struct net_device *in, const struct net_device *out)
{
const struct ethhdr *h = eth_hdr(skb);
const struct net_bridge_port *p;
@@ -162,7 +161,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
for (i = 0; i < 6; i++)
verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
e->sourcemsk[i];
- if (FWINV2(verdict != 0, EBT_ISOURCE) )
+ if (FWINV2(verdict != 0, EBT_ISOURCE))
return 1;
}
if (e->bitmask & EBT_DESTMAC) {
@@ -170,7 +169,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
for (i = 0; i < 6; i++)
verdict |= (h->h_dest[i] ^ e->destmac[i]) &
e->destmsk[i];
- if (FWINV2(verdict != 0, EBT_IDEST) )
+ if (FWINV2(verdict != 0, EBT_IDEST))
return 1;
}
return 0;
@@ -237,7 +236,8 @@ unsigned int ebt_do_table(struct sk_buff *skb,
(*(counter_base + i)).bcnt += skb->len;
/* these should only watch: not modify, nor tell us
- what to do with the packet */
+ * what to do with the packet
+ */
EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &acpar);
t = (struct ebt_entry_target *)
@@ -323,7 +323,7 @@ letscontinue:
/* If it succeeds, returns element and locks mutex */
static inline void *
find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
- struct mutex *mutex)
+ struct mutex *mutex)
{
struct {
struct list_head list;
@@ -342,7 +342,7 @@ find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
static void *
find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
- int *error, struct mutex *mutex)
+ int *error, struct mutex *mutex)
{
return try_then_request_module(
find_inlist_lock_noload(head, name, error, mutex),
@@ -451,7 +451,8 @@ static int ebt_verify_pointers(const struct ebt_replace *repl,
if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
if (e->bitmask != 0) {
/* we make userspace set this right,
- so there is no misunderstanding */
+ * so there is no misunderstanding
+ */
BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
"in distinguisher\n");
return -EINVAL;
@@ -487,15 +488,14 @@ static int ebt_verify_pointers(const struct ebt_replace *repl,
return 0;
}
-/*
- * this one is very careful, as it is the first function
+/* this one is very careful, as it is the first function
* to parse the userspace data
*/
static inline int
ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
- const struct ebt_table_info *newinfo,
- unsigned int *n, unsigned int *cnt,
- unsigned int *totalcnt, unsigned int *udc_cnt)
+ const struct ebt_table_info *newinfo,
+ unsigned int *n, unsigned int *cnt,
+ unsigned int *totalcnt, unsigned int *udc_cnt)
{
int i;
@@ -504,10 +504,12 @@ ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
break;
}
/* beginning of a new chain
- if i == NF_BR_NUMHOOKS it must be a user defined chain */
+ * if i == NF_BR_NUMHOOKS it must be a user defined chain
+ */
if (i != NF_BR_NUMHOOKS || !e->bitmask) {
/* this checks if the previous chain has as many entries
- as it said it has */
+ * as it said it has
+ */
if (*n != *cnt) {
BUGPRINT("nentries does not equal the nr of entries "
"in the chain\n");
@@ -549,20 +551,18 @@ ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
return 0;
}
-struct ebt_cl_stack
-{
+struct ebt_cl_stack {
struct ebt_chainstack cs;
int from;
unsigned int hookmask;
};
-/*
- * we need these positions to check that the jumps to a different part of the
+/* We need these positions to check that the jumps to a different part of the
* entries is a jump to the beginning of a new chain.
*/
static inline int
ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
- unsigned int *n, struct ebt_cl_stack *udc)
+ unsigned int *n, struct ebt_cl_stack *udc)
{
int i;
@@ -649,9 +649,9 @@ ebt_cleanup_entry(struct ebt_entry *e, struct net *net, unsigned int *cnt)
static inline int
ebt_check_entry(struct ebt_entry *e, struct net *net,
- const struct ebt_table_info *newinfo,
- const char *name, unsigned int *cnt,
- struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+ const struct ebt_table_info *newinfo,
+ const char *name, unsigned int *cnt,
+ struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
{
struct ebt_entry_target *t;
struct xt_target *target;
@@ -673,7 +673,7 @@ ebt_check_entry(struct ebt_entry *e, struct net *net,
BUGPRINT("Unknown flag for inv bitmask\n");
return -EINVAL;
}
- if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
+ if ((e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3)) {
BUGPRINT("NOPROTO & 802_3 not allowed\n");
return -EINVAL;
}
@@ -687,7 +687,8 @@ ebt_check_entry(struct ebt_entry *e, struct net *net,
break;
}
/* (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
- a base chain */
+ * a base chain
+ */
if (i < NF_BR_NUMHOOKS)
hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
else {
@@ -758,13 +759,12 @@ cleanup_matches:
return ret;
}
-/*
- * checks for loops and sets the hook mask for udc
+/* checks for loops and sets the hook mask for udc
* the hook mask for udc tells us from which base chains the udc can be
* accessed. This mask is a parameter to the check() functions of the extensions
*/
static int check_chainloops(const struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
- unsigned int udc_cnt, unsigned int hooknr, char *base)
+ unsigned int udc_cnt, unsigned int hooknr, char *base)
{
int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
const struct ebt_entry *e = (struct ebt_entry *)chain->data;
@@ -853,7 +853,8 @@ static int translate_table(struct net *net, const char *name,
return -EINVAL;
}
/* make sure chains are ordered after each other in same order
- as their corresponding hooks */
+ * as their corresponding hooks
+ */
for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
if (!newinfo->hook_entry[j])
continue;
@@ -868,7 +869,8 @@ static int translate_table(struct net *net, const char *name,
i = 0; /* holds the expected nr. of entries for the chain */
j = 0; /* holds the up to now counted entries for the chain */
k = 0; /* holds the total nr. of entries, should equal
- newinfo->nentries afterwards */
+ * newinfo->nentries afterwards
+ */
udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */
ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
ebt_check_entry_size_and_hooks, newinfo,
@@ -888,10 +890,12 @@ static int translate_table(struct net *net, const char *name,
}
/* get the location of the udc, put them in an array
- while we're at it, allocate the chainstack */
+ * while we're at it, allocate the chainstack
+ */
if (udc_cnt) {
/* this will get free'd in do_replace()/ebt_register_table()
- if an error occurs */
+ * if an error occurs
+ */
newinfo->chainstack =
vmalloc(nr_cpu_ids * sizeof(*(newinfo->chainstack)));
if (!newinfo->chainstack)
@@ -932,14 +936,15 @@ static int translate_table(struct net *net, const char *name,
}
/* we now know the following (along with E=mc²):
- - the nr of entries in each chain is right
- - the size of the allocated space is right
- - all valid hooks have a corresponding chain
- - there are no loops
- - wrong data can still be on the level of a single entry
- - could be there are jumps to places that are not the
- beginning of a chain. This can only occur in chains that
- are not accessible from any base chains, so we don't care. */
+ * - the nr of entries in each chain is right
+ * - the size of the allocated space is right
+ * - all valid hooks have a corresponding chain
+ * - there are no loops
+ * - wrong data can still be on the level of a single entry
+ * - could be there are jumps to places that are not the
+ * beginning of a chain. This can only occur in chains that
+ * are not accessible from any base chains, so we don't care.
+ */
/* used to know what we need to clean up if something goes wrong */
i = 0;
@@ -955,7 +960,7 @@ static int translate_table(struct net *net, const char *name,
/* called under write_lock */
static void get_counters(const struct ebt_counter *oldcounters,
- struct ebt_counter *counters, unsigned int nentries)
+ struct ebt_counter *counters, unsigned int nentries)
{
int i, cpu;
struct ebt_counter *counter_base;
@@ -986,7 +991,8 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
struct ebt_table *t;
/* the user wants counters back
- the check on the size is done later, when we have the lock */
+ * the check on the size is done later, when we have the lock
+ */
if (repl->num_counters) {
unsigned long size = repl->num_counters * sizeof(*counterstmp);
counterstmp = vmalloc(size);
@@ -1038,9 +1044,10 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
write_unlock_bh(&t->lock);
mutex_unlock(&ebt_mutex);
/* so, a user can change the chains while having messed up her counter
- allocation. Only reason why this is done is because this way the lock
- is held only once, while this doesn't bring the kernel into a
- dangerous state. */
+ * allocation. Only reason why this is done is because this way the lock
+ * is held only once, while this doesn't bring the kernel into a
+ * dangerous state.
+ */
if (repl->num_counters &&
copy_to_user(repl->counters, counterstmp,
repl->num_counters * sizeof(struct ebt_counter))) {
@@ -1342,13 +1349,14 @@ static int update_counters(struct net *net, const void __user *user,
}
static inline int ebt_make_matchname(const struct ebt_entry_match *m,
- const char *base, char __user *ubase)
+ const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)m - base);
char name[EBT_FUNCTION_MAXNAMELEN] = {};
/* ebtables expects 32 bytes long names but xt_match names are 29 bytes
- long. Copy 29 bytes and fill remaining bytes with zeroes. */
+ * long. Copy 29 bytes and fill remaining bytes with zeroes.
+ */
strlcpy(name, m->u.match->name, sizeof(name));
if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
@@ -1356,19 +1364,19 @@ static inline int ebt_make_matchname(const struct ebt_entry_match *m,
}
static inline int ebt_make_watchername(const struct ebt_entry_watcher *w,
- const char *base, char __user *ubase)
+ const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)w - base);
char name[EBT_FUNCTION_MAXNAMELEN] = {};
strlcpy(name, w->u.watcher->name, sizeof(name));
- if (copy_to_user(hlp , name, EBT_FUNCTION_MAXNAMELEN))
+ if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
-static inline int
-ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
+static inline int ebt_make_names(struct ebt_entry *e, const char *base,
+ char __user *ubase)
{
int ret;
char __user *hlp;
@@ -1394,9 +1402,9 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
}
static int copy_counters_to_user(struct ebt_table *t,
- const struct ebt_counter *oldcounters,
- void __user *user, unsigned int num_counters,
- unsigned int nentries)
+ const struct ebt_counter *oldcounters,
+ void __user *user, unsigned int num_counters,
+ unsigned int nentries)
{
struct ebt_counter *counterstmp;
int ret = 0;
@@ -1427,7 +1435,7 @@ static int copy_counters_to_user(struct ebt_table *t,
/* called with ebt_mutex locked */
static int copy_everything_to_user(struct ebt_table *t, void __user *user,
- const int *len, int cmd)
+ const int *len, int cmd)
{
struct ebt_replace tmp;
const struct ebt_counter *oldcounters;
@@ -1595,8 +1603,7 @@ static int ebt_compat_entry_padsize(void)
static int ebt_compat_match_offset(const struct xt_match *match,
unsigned int userlen)
{
- /*
- * ebt_among needs special handling. The kernel .matchsize is
+ /* ebt_among needs special handling. The kernel .matchsize is
* set to -1 at registration time; at runtime an EBT_ALIGN()ed
* value is expected.
* Example: userspace sends 4500, ebt_among.c wants 4504.
@@ -1966,8 +1973,7 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
return off + match_size;
}
-/*
- * return size of all matches, watchers or target, including necessary
+/* return size of all matches, watchers or target, including necessary
* alignment and padding.
*/
static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
@@ -2070,8 +2076,7 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
if (ret < 0)
return ret;
buf_start = (char *) entry;
- /*
- * 0: matches offset, always follows ebt_entry.
+ /* 0: matches offset, always follows ebt_entry.
* 1: watchers offset, from ebt_entry structure
* 2: target offset, from ebt_entry structure
* 3: next ebt_entry offset, from ebt_entry structure
@@ -2115,8 +2120,7 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
return 0;
}
-/*
- * repl->entries_size is the size of the ebt_entry blob in userspace.
+/* repl->entries_size is the size of the ebt_entry blob in userspace.
* It might need more memory when copied to a 64 bit kernel in case
* userspace is 32-bit. So, first task: find out how much memory is needed.
*
@@ -2305,7 +2309,7 @@ static int compat_do_ebt_set_ctl(struct sock *sk,
break;
default:
ret = -EINVAL;
- }
+ }
return ret;
}
@@ -2360,8 +2364,7 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
break;
case EBT_SO_GET_ENTRIES:
case EBT_SO_GET_INIT_ENTRIES:
- /*
- * try real handler first in case of userland-side padding.
+ /* try real handler first in case of userland-side padding.
* in case we are dealing with an 'ordinary' 32 bit binary
* without 64bit compatibility padding, this will fail right
* after copy_from_user when the *len argument is validated.
diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c
index 62f6b1b19589..7fcdd7261d88 100644
--- a/net/bridge/netfilter/nf_tables_bridge.c
+++ b/net/bridge/netfilter/nf_tables_bridge.c
@@ -141,7 +141,7 @@ err:
static void nf_tables_bridge_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.bridge);
+ nft_unregister_afinfo(net, net->nft.bridge);
kfree(net->nft.bridge);
}
diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c
index a21269b83f16..4b901d9f2e7c 100644
--- a/net/bridge/netfilter/nft_meta_bridge.c
+++ b/net/bridge/netfilter/nft_meta_bridge.c
@@ -84,6 +84,7 @@ static const struct nft_expr_ops nft_meta_bridge_set_ops = {
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval,
.init = nft_meta_set_init,
+ .destroy = nft_meta_set_destroy,
.dump = nft_meta_set_dump,
};
diff --git a/net/core/Makefile b/net/core/Makefile
index 086b01fbe1bd..0b835de04de3 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
- sock_diag.o dev_ioctl.o tso.o
+ sock_diag.o dev_ioctl.o tso.o sock_reuseport.o
obj-$(CONFIG_XFRM) += flow.o
obj-y += net-sysfs.o
diff --git a/net/core/datagram.c b/net/core/datagram.c
index d62af69ad844..fa9dc6450b08 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -83,8 +83,8 @@ static int receiver_wake_function(wait_queue_t *wait, unsigned int mode, int syn
/*
* Wait for the last received packet to be different from skb
*/
-static int wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
- const struct sk_buff *skb)
+int __skb_wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
+ const struct sk_buff *skb)
{
int error;
DEFINE_WAIT_FUNC(wait, receiver_wake_function);
@@ -130,6 +130,7 @@ out_noerr:
error = 1;
goto out;
}
+EXPORT_SYMBOL(__skb_wait_for_more_packets);
static struct sk_buff *skb_set_peeked(struct sk_buff *skb)
{
@@ -161,13 +162,15 @@ done:
}
/**
- * __skb_recv_datagram - Receive a datagram skbuff
+ * __skb_try_recv_datagram - Receive a datagram skbuff
* @sk: socket
* @flags: MSG_ flags
* @peeked: returns non-zero if this packet has been seen before
* @off: an offset in bytes to peek skb from. Returns an offset
* within an skb where data actually starts
* @err: error code returned
+ * @last: set to last peeked message to inform the wait function
+ * what to look for when peeking
*
* Get a datagram skbuff, understands the peeking, nonblocking wakeups
* and possible races. This replaces identical code in packet, raw and
@@ -175,9 +178,11 @@ done:
* the long standing peek and read race for datagram sockets. If you
* alter this routine remember it must be re-entrant.
*
- * This function will lock the socket if a skb is returned, so the caller
- * needs to unlock the socket in that case (usually by calling
- * skb_free_datagram)
+ * This function will lock the socket if a skb is returned, so
+ * the caller needs to unlock the socket in that case (usually by
+ * calling skb_free_datagram). Returns NULL with *err set to
+ * -EAGAIN if no data was available or to some other value if an
+ * error was detected.
*
* * It does not lock socket since today. This function is
* * free of race conditions. This measure should/can improve
@@ -191,13 +196,13 @@ done:
* quite explicitly by POSIX 1003.1g, don't change them without having
* the standard around please.
*/
-struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
- int *peeked, int *off, int *err)
+struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags,
+ int *peeked, int *off, int *err,
+ struct sk_buff **last)
{
struct sk_buff_head *queue = &sk->sk_receive_queue;
- struct sk_buff *skb, *last;
+ struct sk_buff *skb;
unsigned long cpu_flags;
- long timeo;
/*
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
*/
@@ -206,8 +211,6 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
if (error)
goto no_packet;
- timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
-
do {
/* Again only user level code calls this function, so nothing
* interrupt level will suddenly eat the receive_queue.
@@ -217,10 +220,10 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
*/
int _off = *off;
- last = (struct sk_buff *)queue;
+ *last = (struct sk_buff *)queue;
spin_lock_irqsave(&queue->lock, cpu_flags);
skb_queue_walk(queue, skb) {
- last = skb;
+ *last = skb;
*peeked = skb->peeked;
if (flags & MSG_PEEK) {
if (_off >= skb->len && (skb->len || _off ||
@@ -231,8 +234,11 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
skb = skb_set_peeked(skb);
error = PTR_ERR(skb);
- if (IS_ERR(skb))
- goto unlock_err;
+ if (IS_ERR(skb)) {
+ spin_unlock_irqrestore(&queue->lock,
+ cpu_flags);
+ goto no_packet;
+ }
atomic_inc(&skb->users);
} else
@@ -242,25 +248,38 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
*off = _off;
return skb;
}
+
spin_unlock_irqrestore(&queue->lock, cpu_flags);
+ } while (sk_can_busy_loop(sk) &&
+ sk_busy_loop(sk, flags & MSG_DONTWAIT));
- if (sk_can_busy_loop(sk) &&
- sk_busy_loop(sk, flags & MSG_DONTWAIT))
- continue;
+ error = -EAGAIN;
- /* User doesn't want to wait */
- error = -EAGAIN;
- if (!timeo)
- goto no_packet;
+no_packet:
+ *err = error;
+ return NULL;
+}
+EXPORT_SYMBOL(__skb_try_recv_datagram);
- } while (!wait_for_more_packets(sk, err, &timeo, last));
+struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
+ int *peeked, int *off, int *err)
+{
+ struct sk_buff *skb, *last;
+ long timeo;
- return NULL;
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
+ do {
+ skb = __skb_try_recv_datagram(sk, flags, peeked, off, err,
+ &last);
+ if (skb)
+ return skb;
+
+ if (*err != -EAGAIN)
+ break;
+ } while (timeo &&
+ !__skb_wait_for_more_packets(sk, err, &timeo, last));
-unlock_err:
- spin_unlock_irqrestore(&queue->lock, cpu_flags);
-no_packet:
- *err = error;
return NULL;
}
EXPORT_SYMBOL(__skb_recv_datagram);
diff --git a/net/core/dev.c b/net/core/dev.c
index ae00b894e675..0ca95d5d7af0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -96,6 +96,7 @@
#include <linux/skbuff.h>
#include <net/net_namespace.h>
#include <net/sock.h>
+#include <net/busy_poll.h>
#include <linux/rtnetlink.h>
#include <linux/stat.h>
#include <net/dst.h>
@@ -137,6 +138,7 @@
#include <linux/errqueue.h>
#include <linux/hrtimer.h>
#include <linux/netfilter_ingress.h>
+#include <linux/sctp.h>
#include "net-sysfs.h"
@@ -182,8 +184,8 @@ EXPORT_SYMBOL(dev_base_lock);
/* protects napi_hash addition/deletion and napi_gen_id */
static DEFINE_SPINLOCK(napi_hash_lock);
-static unsigned int napi_gen_id;
-static DEFINE_HASHTABLE(napi_hash, 8);
+static unsigned int napi_gen_id = NR_CPUS;
+static DEFINE_READ_MOSTLY_HASHTABLE(napi_hash, 8);
static seqcount_t devnet_rename_seq;
@@ -1674,6 +1676,22 @@ void net_dec_ingress_queue(void)
EXPORT_SYMBOL_GPL(net_dec_ingress_queue);
#endif
+#ifdef CONFIG_NET_EGRESS
+static struct static_key egress_needed __read_mostly;
+
+void net_inc_egress_queue(void)
+{
+ static_key_slow_inc(&egress_needed);
+}
+EXPORT_SYMBOL_GPL(net_inc_egress_queue);
+
+void net_dec_egress_queue(void)
+{
+ static_key_slow_dec(&egress_needed);
+}
+EXPORT_SYMBOL_GPL(net_dec_egress_queue);
+#endif
+
static struct static_key netstamp_needed __read_mostly;
#ifdef HAVE_JUMP_LABEL
/* We are not allowed to call static_key_slow_dec() from irq context
@@ -2470,6 +2488,141 @@ out:
}
EXPORT_SYMBOL(skb_checksum_help);
+/* skb_csum_offload_check - Driver helper function to determine if a device
+ * with limited checksum offload capabilities is able to offload the checksum
+ * for a given packet.
+ *
+ * Arguments:
+ * skb - sk_buff for the packet in question
+ * spec - contains the description of what device can offload
+ * csum_encapped - returns true if the checksum being offloaded is
+ * encpasulated. That is it is checksum for the transport header
+ * in the inner headers.
+ * checksum_help - when set indicates that helper function should
+ * call skb_checksum_help if offload checks fail
+ *
+ * Returns:
+ * true: Packet has passed the checksum checks and should be offloadable to
+ * the device (a driver may still need to check for additional
+ * restrictions of its device)
+ * false: Checksum is not offloadable. If checksum_help was set then
+ * skb_checksum_help was called to resolve checksum for non-GSO
+ * packets and when IP protocol is not SCTP
+ */
+bool __skb_csum_offload_chk(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec,
+ bool *csum_encapped,
+ bool csum_help)
+{
+ struct iphdr *iph;
+ struct ipv6hdr *ipv6;
+ void *nhdr;
+ int protocol;
+ u8 ip_proto;
+
+ if (skb->protocol == htons(ETH_P_8021Q) ||
+ skb->protocol == htons(ETH_P_8021AD)) {
+ if (!spec->vlan_okay)
+ goto need_help;
+ }
+
+ /* We check whether the checksum refers to a transport layer checksum in
+ * the outermost header or an encapsulated transport layer checksum that
+ * corresponds to the inner headers of the skb. If the checksum is for
+ * something else in the packet we need help.
+ */
+ if (skb_checksum_start_offset(skb) == skb_transport_offset(skb)) {
+ /* Non-encapsulated checksum */
+ protocol = eproto_to_ipproto(vlan_get_protocol(skb));
+ nhdr = skb_network_header(skb);
+ *csum_encapped = false;
+ if (spec->no_not_encapped)
+ goto need_help;
+ } else if (skb->encapsulation && spec->encap_okay &&
+ skb_checksum_start_offset(skb) ==
+ skb_inner_transport_offset(skb)) {
+ /* Encapsulated checksum */
+ *csum_encapped = true;
+ switch (skb->inner_protocol_type) {
+ case ENCAP_TYPE_ETHER:
+ protocol = eproto_to_ipproto(skb->inner_protocol);
+ break;
+ case ENCAP_TYPE_IPPROTO:
+ protocol = skb->inner_protocol;
+ break;
+ }
+ nhdr = skb_inner_network_header(skb);
+ } else {
+ goto need_help;
+ }
+
+ switch (protocol) {
+ case IPPROTO_IP:
+ if (!spec->ipv4_okay)
+ goto need_help;
+ iph = nhdr;
+ ip_proto = iph->protocol;
+ if (iph->ihl != 5 && !spec->ip_options_okay)
+ goto need_help;
+ break;
+ case IPPROTO_IPV6:
+ if (!spec->ipv6_okay)
+ goto need_help;
+ if (spec->no_encapped_ipv6 && *csum_encapped)
+ goto need_help;
+ ipv6 = nhdr;
+ nhdr += sizeof(*ipv6);
+ ip_proto = ipv6->nexthdr;
+ break;
+ default:
+ goto need_help;
+ }
+
+ip_proto_again:
+ switch (ip_proto) {
+ case IPPROTO_TCP:
+ if (!spec->tcp_okay ||
+ skb->csum_offset != offsetof(struct tcphdr, check))
+ goto need_help;
+ break;
+ case IPPROTO_UDP:
+ if (!spec->udp_okay ||
+ skb->csum_offset != offsetof(struct udphdr, check))
+ goto need_help;
+ break;
+ case IPPROTO_SCTP:
+ if (!spec->sctp_okay ||
+ skb->csum_offset != offsetof(struct sctphdr, checksum))
+ goto cant_help;
+ break;
+ case NEXTHDR_HOP:
+ case NEXTHDR_ROUTING:
+ case NEXTHDR_DEST: {
+ u8 *opthdr = nhdr;
+
+ if (protocol != IPPROTO_IPV6 || !spec->ext_hdrs_okay)
+ goto need_help;
+
+ ip_proto = opthdr[0];
+ nhdr += (opthdr[1] + 1) << 3;
+
+ goto ip_proto_again;
+ }
+ default:
+ goto need_help;
+ }
+
+ /* Passed the tests for offloading checksum */
+ return true;
+
+need_help:
+ if (csum_help && !skb_shinfo(skb)->gso_size)
+ skb_checksum_help(skb);
+cant_help:
+ return false;
+}
+EXPORT_SYMBOL(__skb_csum_offload_chk);
+
__be16 skb_network_protocol(struct sk_buff *skb, int *depth)
{
__be16 type = skb->protocol;
@@ -2644,7 +2797,7 @@ static netdev_features_t harmonize_features(struct sk_buff *skb,
if (skb->ip_summed != CHECKSUM_NONE &&
!can_checksum_protocol(features, type)) {
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
} else if (illegal_highdma(skb->dev, skb)) {
features &= ~NETIF_F_SG;
}
@@ -2791,7 +2944,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
else
skb_set_transport_header(skb,
skb_checksum_start_offset(skb));
- if (!(features & NETIF_F_ALL_CSUM) &&
+ if (!(features & NETIF_F_CSUM_MASK) &&
skb_checksum_help(skb))
goto out_kfree_skb;
}
@@ -2870,7 +3023,6 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
bool contended;
int rc;
- qdisc_pkt_len_init(skb);
qdisc_calculate_pkt_len(skb, q);
/*
* Heuristic to force contended enqueues to serialize on a
@@ -2928,7 +3080,8 @@ static void skb_update_prio(struct sk_buff *skb)
struct netprio_map *map = rcu_dereference_bh(skb->dev->priomap);
if (!skb->priority && skb->sk && map) {
- unsigned int prioidx = skb->sk->sk_cgrp_prioidx;
+ unsigned int prioidx =
+ sock_cgroup_prioidx(&skb->sk->sk_cgrp_data);
if (prioidx < map->priomap_len)
skb->priority = map->priomap[prioidx];
@@ -2962,6 +3115,49 @@ int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
}
EXPORT_SYMBOL(dev_loopback_xmit);
+#ifdef CONFIG_NET_EGRESS
+static struct sk_buff *
+sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev)
+{
+ struct tcf_proto *cl = rcu_dereference_bh(dev->egress_cl_list);
+ struct tcf_result cl_res;
+
+ if (!cl)
+ return skb;
+
+ /* skb->tc_verd and qdisc_skb_cb(skb)->pkt_len were already set
+ * earlier by the caller.
+ */
+ qdisc_bstats_cpu_update(cl->q, skb);
+
+ switch (tc_classify(skb, cl, &cl_res, false)) {
+ case TC_ACT_OK:
+ case TC_ACT_RECLASSIFY:
+ skb->tc_index = TC_H_MIN(cl_res.classid);
+ break;
+ case TC_ACT_SHOT:
+ qdisc_qstats_cpu_drop(cl->q);
+ *ret = NET_XMIT_DROP;
+ goto drop;
+ case TC_ACT_STOLEN:
+ case TC_ACT_QUEUED:
+ *ret = NET_XMIT_SUCCESS;
+drop:
+ kfree_skb(skb);
+ return NULL;
+ case TC_ACT_REDIRECT:
+ /* No need to push/pop skb's mac_header here on egress! */
+ skb_do_redirect(skb);
+ *ret = NET_XMIT_SUCCESS;
+ return NULL;
+ default:
+ break;
+ }
+
+ return skb;
+}
+#endif /* CONFIG_NET_EGRESS */
+
static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
{
#ifdef CONFIG_XPS
@@ -3021,7 +3217,9 @@ struct netdev_queue *netdev_pick_tx(struct net_device *dev,
int queue_index = 0;
#ifdef CONFIG_XPS
- if (skb->sender_cpu == 0)
+ u32 sender_cpu = skb->sender_cpu - 1;
+
+ if (sender_cpu >= (u32)NR_CPUS)
skb->sender_cpu = raw_smp_processor_id() + 1;
#endif
@@ -3086,6 +3284,17 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
skb_update_prio(skb);
+ qdisc_pkt_len_init(skb);
+#ifdef CONFIG_NET_CLS_ACT
+ skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
+# ifdef CONFIG_NET_EGRESS
+ if (static_key_false(&egress_needed)) {
+ skb = sch_handle_egress(skb, &rc, dev);
+ if (!skb)
+ goto out;
+ }
+# endif
+#endif
/* If device/qdisc don't need skb->dst, release it right now while
* its hot in this cpu cache.
*/
@@ -3107,9 +3316,6 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
txq = netdev_pick_tx(dev, skb, accel_priv);
q = rcu_dereference_bh(txq->qdisc);
-#ifdef CONFIG_NET_CLS_ACT
- skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
-#endif
trace_net_dev_queue(skb);
if (q->enqueue) {
rc = __dev_xmit_skb(skb, q, dev, txq);
@@ -3666,9 +3872,9 @@ int (*br_fdb_test_addr_hook)(struct net_device *dev,
EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook);
#endif
-static inline struct sk_buff *handle_ing(struct sk_buff *skb,
- struct packet_type **pt_prev,
- int *ret, struct net_device *orig_dev)
+static inline struct sk_buff *
+sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret,
+ struct net_device *orig_dev)
{
#ifdef CONFIG_NET_CLS_ACT
struct tcf_proto *cl = rcu_dereference_bh(skb->dev->ingress_cl_list);
@@ -3862,7 +4068,7 @@ another_round:
skip_taps:
#ifdef CONFIG_NET_INGRESS
if (static_key_false(&ingress_needed)) {
- skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
+ skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev);
if (!skb)
goto out;
@@ -4353,6 +4559,7 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
+ skb_mark_napi_id(skb, napi);
trace_napi_gro_receive_entry(skb);
skb_gro_reset_offset(skb);
@@ -4386,7 +4593,10 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi)
if (!skb) {
skb = napi_alloc_skb(napi, GRO_MAX_HEAD);
- napi->skb = skb;
+ if (skb) {
+ napi->skb = skb;
+ skb_mark_napi_id(skb, napi);
+ }
}
return skb;
}
@@ -4661,7 +4871,7 @@ void napi_complete_done(struct napi_struct *n, int work_done)
EXPORT_SYMBOL(napi_complete_done);
/* must be called under rcu_read_lock(), as we dont take a reference */
-struct napi_struct *napi_by_id(unsigned int napi_id)
+static struct napi_struct *napi_by_id(unsigned int napi_id)
{
unsigned int hash = napi_id % HASH_SIZE(napi_hash);
struct napi_struct *napi;
@@ -4672,43 +4882,101 @@ struct napi_struct *napi_by_id(unsigned int napi_id)
return NULL;
}
-EXPORT_SYMBOL_GPL(napi_by_id);
-void napi_hash_add(struct napi_struct *napi)
+#if defined(CONFIG_NET_RX_BUSY_POLL)
+#define BUSY_POLL_BUDGET 8
+bool sk_busy_loop(struct sock *sk, int nonblock)
{
- if (!test_and_set_bit(NAPI_STATE_HASHED, &napi->state)) {
+ unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0;
+ int (*busy_poll)(struct napi_struct *dev);
+ struct napi_struct *napi;
+ int rc = false;
- spin_lock(&napi_hash_lock);
+ rcu_read_lock();
- /* 0 is not a valid id, we also skip an id that is taken
- * we expect both events to be extremely rare
- */
- napi->napi_id = 0;
- while (!napi->napi_id) {
- napi->napi_id = ++napi_gen_id;
- if (napi_by_id(napi->napi_id))
- napi->napi_id = 0;
+ napi = napi_by_id(sk->sk_napi_id);
+ if (!napi)
+ goto out;
+
+ /* Note: ndo_busy_poll method is optional in linux-4.5 */
+ busy_poll = napi->dev->netdev_ops->ndo_busy_poll;
+
+ do {
+ rc = 0;
+ local_bh_disable();
+ if (busy_poll) {
+ rc = busy_poll(napi);
+ } else if (napi_schedule_prep(napi)) {
+ void *have = netpoll_poll_lock(napi);
+
+ if (test_bit(NAPI_STATE_SCHED, &napi->state)) {
+ rc = napi->poll(napi, BUSY_POLL_BUDGET);
+ trace_napi_poll(napi);
+ if (rc == BUSY_POLL_BUDGET) {
+ napi_complete_done(napi, rc);
+ napi_schedule(napi);
+ }
+ }
+ netpoll_poll_unlock(have);
}
+ if (rc > 0)
+ NET_ADD_STATS_BH(sock_net(sk),
+ LINUX_MIB_BUSYPOLLRXPACKETS, rc);
+ local_bh_enable();
- hlist_add_head_rcu(&napi->napi_hash_node,
- &napi_hash[napi->napi_id % HASH_SIZE(napi_hash)]);
+ if (rc == LL_FLUSH_FAILED)
+ break; /* permanent failure */
- spin_unlock(&napi_hash_lock);
- }
+ cpu_relax();
+ } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) &&
+ !need_resched() && !busy_loop_timeout(end_time));
+
+ rc = !skb_queue_empty(&sk->sk_receive_queue);
+out:
+ rcu_read_unlock();
+ return rc;
+}
+EXPORT_SYMBOL(sk_busy_loop);
+
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
+void napi_hash_add(struct napi_struct *napi)
+{
+ if (test_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state) ||
+ test_and_set_bit(NAPI_STATE_HASHED, &napi->state))
+ return;
+
+ spin_lock(&napi_hash_lock);
+
+ /* 0..NR_CPUS+1 range is reserved for sender_cpu use */
+ do {
+ if (unlikely(++napi_gen_id < NR_CPUS + 1))
+ napi_gen_id = NR_CPUS + 1;
+ } while (napi_by_id(napi_gen_id));
+ napi->napi_id = napi_gen_id;
+
+ hlist_add_head_rcu(&napi->napi_hash_node,
+ &napi_hash[napi->napi_id % HASH_SIZE(napi_hash)]);
+
+ spin_unlock(&napi_hash_lock);
}
EXPORT_SYMBOL_GPL(napi_hash_add);
/* Warning : caller is responsible to make sure rcu grace period
* is respected before freeing memory containing @napi
*/
-void napi_hash_del(struct napi_struct *napi)
+bool napi_hash_del(struct napi_struct *napi)
{
+ bool rcu_sync_needed = false;
+
spin_lock(&napi_hash_lock);
- if (test_and_clear_bit(NAPI_STATE_HASHED, &napi->state))
+ if (test_and_clear_bit(NAPI_STATE_HASHED, &napi->state)) {
+ rcu_sync_needed = true;
hlist_del_rcu(&napi->napi_hash_node);
-
+ }
spin_unlock(&napi_hash_lock);
+ return rcu_sync_needed;
}
EXPORT_SYMBOL_GPL(napi_hash_del);
@@ -4744,6 +5012,7 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
napi->poll_owner = -1;
#endif
set_bit(NAPI_STATE_SCHED, &napi->state);
+ napi_hash_add(napi);
}
EXPORT_SYMBOL(netif_napi_add);
@@ -4763,8 +5032,12 @@ void napi_disable(struct napi_struct *n)
}
EXPORT_SYMBOL(napi_disable);
+/* Must be called in process context */
void netif_napi_del(struct napi_struct *napi)
{
+ might_sleep();
+ if (napi_hash_del(napi))
+ synchronize_net();
list_del_init(&napi->dev_list);
napi_free_frags(napi);
@@ -5351,7 +5624,7 @@ static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev,
static int __netdev_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev, bool master,
- void *private)
+ void *upper_priv, void *upper_info)
{
struct netdev_notifier_changeupper_info changeupper_info;
struct netdev_adjacent *i, *j, *to_i, *to_j;
@@ -5375,6 +5648,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
changeupper_info.upper_dev = upper_dev;
changeupper_info.master = master;
changeupper_info.linking = true;
+ changeupper_info.upper_info = upper_info;
ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev,
&changeupper_info.info);
@@ -5382,7 +5656,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
if (ret)
return ret;
- ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, private,
+ ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, upper_priv,
master);
if (ret)
return ret;
@@ -5420,8 +5694,12 @@ static int __netdev_upper_dev_link(struct net_device *dev,
goto rollback_lower_mesh;
}
- call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev,
- &changeupper_info.info);
+ ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev,
+ &changeupper_info.info);
+ ret = notifier_to_errno(ret);
+ if (ret)
+ goto rollback_lower_mesh;
+
return 0;
rollback_lower_mesh:
@@ -5475,7 +5753,7 @@ rollback_mesh:
int netdev_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev)
{
- return __netdev_upper_dev_link(dev, upper_dev, false, NULL);
+ return __netdev_upper_dev_link(dev, upper_dev, false, NULL, NULL);
}
EXPORT_SYMBOL(netdev_upper_dev_link);
@@ -5483,6 +5761,8 @@ EXPORT_SYMBOL(netdev_upper_dev_link);
* netdev_master_upper_dev_link - Add a master link to the upper device
* @dev: device
* @upper_dev: new upper device
+ * @upper_priv: upper device private
+ * @upper_info: upper info to be passed down via notifier
*
* Adds a link to device which is upper to this one. In this case, only
* one master upper device can be linked, although other non-master devices
@@ -5491,20 +5771,14 @@ EXPORT_SYMBOL(netdev_upper_dev_link);
* counts are adjusted and the function returns zero.
*/
int netdev_master_upper_dev_link(struct net_device *dev,
- struct net_device *upper_dev)
+ struct net_device *upper_dev,
+ void *upper_priv, void *upper_info)
{
- return __netdev_upper_dev_link(dev, upper_dev, true, NULL);
+ return __netdev_upper_dev_link(dev, upper_dev, true,
+ upper_priv, upper_info);
}
EXPORT_SYMBOL(netdev_master_upper_dev_link);
-int netdev_master_upper_dev_link_private(struct net_device *dev,
- struct net_device *upper_dev,
- void *private)
-{
- return __netdev_upper_dev_link(dev, upper_dev, true, private);
-}
-EXPORT_SYMBOL(netdev_master_upper_dev_link_private);
-
/**
* netdev_upper_dev_unlink - Removes a link to upper device
* @dev: device
@@ -5663,7 +5937,7 @@ EXPORT_SYMBOL(netdev_lower_dev_get_private);
int dev_get_nest_level(struct net_device *dev,
- bool (*type_check)(struct net_device *dev))
+ bool (*type_check)(const struct net_device *dev))
{
struct net_device *lower = NULL;
struct list_head *iter;
@@ -5685,6 +5959,26 @@ int dev_get_nest_level(struct net_device *dev,
}
EXPORT_SYMBOL(dev_get_nest_level);
+/**
+ * netdev_lower_change - Dispatch event about lower device state change
+ * @lower_dev: device
+ * @lower_state_info: state to dispatch
+ *
+ * Send NETDEV_CHANGELOWERSTATE to netdev notifiers with info.
+ * The caller must hold the RTNL lock.
+ */
+void netdev_lower_state_changed(struct net_device *lower_dev,
+ void *lower_state_info)
+{
+ struct netdev_notifier_changelowerstate_info changelowerstate_info;
+
+ ASSERT_RTNL();
+ changelowerstate_info.lower_state_info = lower_state_info;
+ call_netdevice_notifiers_info(NETDEV_CHANGELOWERSTATE, lower_dev,
+ &changelowerstate_info.info);
+}
+EXPORT_SYMBOL(netdev_lower_state_changed);
+
static void dev_change_rx_flags(struct net_device *dev, int flags)
{
const struct net_device_ops *ops = dev->netdev_ops;
@@ -6375,9 +6669,9 @@ static netdev_features_t netdev_fix_features(struct net_device *dev,
/* UFO needs SG and checksumming */
if (features & NETIF_F_UFO) {
/* maybe split UFO into V4 and V6? */
- if (!((features & NETIF_F_GEN_CSUM) ||
- (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
- == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
+ if (!(features & NETIF_F_HW_CSUM) &&
+ ((features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) !=
+ (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))) {
netdev_dbg(dev,
"Dropping NETIF_F_UFO since no checksum offload features.\n");
features &= ~NETIF_F_UFO;
@@ -7164,11 +7458,13 @@ EXPORT_SYMBOL(alloc_netdev_mqs);
* This function does the last stage of destroying an allocated device
* interface. The reference to the device object is released.
* If this is the last reference then it will be freed.
+ * Must be called in process context.
*/
void free_netdev(struct net_device *dev)
{
struct napi_struct *p, *n;
+ might_sleep();
netif_free_tx_queues(dev);
#ifdef CONFIG_SYSFS
kvfree(dev->_rx);
@@ -7477,16 +7773,16 @@ static int dev_cpu_callback(struct notifier_block *nfb,
netdev_features_t netdev_increment_features(netdev_features_t all,
netdev_features_t one, netdev_features_t mask)
{
- if (mask & NETIF_F_GEN_CSUM)
- mask |= NETIF_F_ALL_CSUM;
+ if (mask & NETIF_F_HW_CSUM)
+ mask |= NETIF_F_CSUM_MASK;
mask |= NETIF_F_VLAN_CHALLENGED;
- all |= one & (NETIF_F_ONE_FOR_ALL|NETIF_F_ALL_CSUM) & mask;
+ all |= one & (NETIF_F_ONE_FOR_ALL | NETIF_F_CSUM_MASK) & mask;
all &= one | ~NETIF_F_ALL_FOR_ALL;
/* If one device supports hw checksumming, set for all. */
- if (all & NETIF_F_GEN_CSUM)
- all &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+ if (all & NETIF_F_HW_CSUM)
+ all &= ~(NETIF_F_CSUM_MASK & ~NETIF_F_HW_CSUM);
return all;
}
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 29edf74846fc..daf04709dd3c 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -87,7 +87,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
[NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
[NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
- [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp",
+ [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp",
[NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu",
[NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter",
[NETIF_F_RXHASH_BIT] = "rx-hashing",
@@ -191,6 +191,23 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
return ret;
}
+static int phy_get_sset_count(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->drv->get_sset_count &&
+ phydev->drv->get_strings &&
+ phydev->drv->get_stats) {
+ mutex_lock(&phydev->lock);
+ ret = phydev->drv->get_sset_count(phydev);
+ mutex_unlock(&phydev->lock);
+
+ return ret;
+ }
+
+ return -EOPNOTSUPP;
+}
+
static int __ethtool_get_sset_count(struct net_device *dev, int sset)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
@@ -204,6 +221,13 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
if (sset == ETH_SS_TUNABLES)
return ARRAY_SIZE(tunable_strings);
+ if (sset == ETH_SS_PHY_STATS) {
+ if (dev->phydev)
+ return phy_get_sset_count(dev->phydev);
+ else
+ return -EOPNOTSUPP;
+ }
+
if (ops->get_sset_count && ops->get_strings)
return ops->get_sset_count(dev, sset);
else
@@ -223,7 +247,17 @@ static void __ethtool_get_strings(struct net_device *dev,
sizeof(rss_hash_func_strings));
else if (stringset == ETH_SS_TUNABLES)
memcpy(data, tunable_strings, sizeof(tunable_strings));
- else
+ else if (stringset == ETH_SS_PHY_STATS) {
+ struct phy_device *phydev = dev->phydev;
+
+ if (phydev) {
+ mutex_lock(&phydev->lock);
+ phydev->drv->get_strings(phydev, data);
+ mutex_unlock(&phydev->lock);
+ } else {
+ return;
+ }
+ } else
/* ops->get_strings is valid because checked earlier */
ops->get_strings(dev, stringset, data);
}
@@ -235,7 +269,7 @@ static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd)
switch (eth_cmd) {
case ETHTOOL_GTXCSUM:
case ETHTOOL_STXCSUM:
- return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM;
+ return NETIF_F_CSUM_MASK | NETIF_F_SCTP_CRC;
case ETHTOOL_GRXCSUM:
case ETHTOOL_SRXCSUM:
return NETIF_F_RXCSUM;
@@ -1401,6 +1435,47 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
return ret;
}
+static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
+{
+ struct ethtool_stats stats;
+ struct phy_device *phydev = dev->phydev;
+ u64 *data;
+ int ret, n_stats;
+
+ if (!phydev)
+ return -EOPNOTSUPP;
+
+ n_stats = phy_get_sset_count(phydev);
+
+ if (n_stats < 0)
+ return n_stats;
+ WARN_ON(n_stats == 0);
+
+ if (copy_from_user(&stats, useraddr, sizeof(stats)))
+ return -EFAULT;
+
+ stats.n_stats = n_stats;
+ data = kmalloc_array(n_stats, sizeof(u64), GFP_USER);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_lock(&phydev->lock);
+ phydev->drv->get_stats(phydev, &stats, data);
+ mutex_unlock(&phydev->lock);
+
+ ret = -EFAULT;
+ if (copy_to_user(useraddr, &stats, sizeof(stats)))
+ goto out;
+ useraddr += sizeof(stats);
+ if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
+ goto out;
+ ret = 0;
+
+ out:
+ kfree(data);
+ return ret;
+}
+
static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
{
struct ethtool_perm_addr epaddr;
@@ -1779,6 +1854,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GSSET_INFO:
case ETHTOOL_GSTRINGS:
case ETHTOOL_GSTATS:
+ case ETHTOOL_GPHYSTATS:
case ETHTOOL_GTSO:
case ETHTOOL_GPERMADDR:
case ETHTOOL_GUFO:
@@ -1991,6 +2067,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_STUNABLE:
rc = ethtool_set_tunable(dev, useraddr);
break;
+ case ETHTOOL_GPHYSTATS:
+ rc = ethtool_get_phy_stats(dev, useraddr);
+ break;
default:
rc = -EOPNOTSUPP;
}
diff --git a/net/core/filter.c b/net/core/filter.c
index 672eefbfbe99..94d26201080d 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -50,6 +50,7 @@
#include <net/cls_cgroup.h>
#include <net/dst_metadata.h>
#include <net/dst.h>
+#include <net/sock_reuseport.h>
/**
* sk_filter - run a packet through a socket filter
@@ -348,12 +349,6 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
* jump offsets, 2nd pass remapping:
* new_prog = kmalloc(sizeof(struct bpf_insn) * new_len);
* bpf_convert_filter(old_prog, old_len, new_prog, &new_len);
- *
- * User BPF's register A is mapped to our BPF register 6, user BPF
- * register X is mapped to BPF register 7; frame pointer is always
- * register 10; Context 'void *ctx' is stored in register 1, that is,
- * for socket filters: ctx == 'struct sk_buff *', for seccomp:
- * ctx == 'struct seccomp_data *'.
*/
static int bpf_convert_filter(struct sock_filter *prog, int len,
struct bpf_insn *new_prog, int *new_len)
@@ -381,9 +376,22 @@ do_pass:
new_insn = new_prog;
fp = prog;
- if (new_insn)
- *new_insn = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);
- new_insn++;
+ /* Classic BPF related prologue emission. */
+ if (new_insn) {
+ /* Classic BPF expects A and X to be reset first. These need
+ * to be guaranteed to be the first two instructions.
+ */
+ *new_insn++ = BPF_ALU64_REG(BPF_XOR, BPF_REG_A, BPF_REG_A);
+ *new_insn++ = BPF_ALU64_REG(BPF_XOR, BPF_REG_X, BPF_REG_X);
+
+ /* All programs must keep CTX in callee saved BPF_REG_CTX.
+ * In eBPF case it's done by the compiler, here we need to
+ * do this ourself. Initial CTX is present in BPF_REG_ARG1.
+ */
+ *new_insn++ = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);
+ } else {
+ new_insn += 3;
+ }
for (i = 0; i < len; fp++, i++) {
struct bpf_insn tmp_insns[6] = { };
@@ -777,6 +785,11 @@ static int bpf_check_classic(const struct sock_filter *filter,
if (ftest->k == 0)
return -EINVAL;
break;
+ case BPF_ALU | BPF_LSH | BPF_K:
+ case BPF_ALU | BPF_RSH | BPF_K:
+ if (ftest->k >= 32)
+ return -EINVAL;
+ break;
case BPF_LD | BPF_MEM:
case BPF_LDX | BPF_MEM:
case BPF_ST:
@@ -1160,17 +1173,32 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
return 0;
}
-/**
- * sk_attach_filter - attach a socket filter
- * @fprog: the filter program
- * @sk: the socket to use
- *
- * Attach the user's filter code. We first run some sanity checks on
- * it to make sure it does not explode on us later. If an error
- * occurs or there is insufficient memory for the filter a negative
- * errno code is returned. On success the return is zero.
- */
-int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+static int __reuseport_attach_prog(struct bpf_prog *prog, struct sock *sk)
+{
+ struct bpf_prog *old_prog;
+ int err;
+
+ if (bpf_prog_size(prog->len) > sysctl_optmem_max)
+ return -ENOMEM;
+
+ if (sk_unhashed(sk)) {
+ err = reuseport_alloc(sk);
+ if (err)
+ return err;
+ } else if (!rcu_access_pointer(sk->sk_reuseport_cb)) {
+ /* The socket wasn't bound with SO_REUSEPORT */
+ return -EINVAL;
+ }
+
+ old_prog = reuseport_attach_prog(sk, prog);
+ if (old_prog)
+ bpf_prog_destroy(old_prog);
+
+ return 0;
+}
+
+static
+struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
{
unsigned int fsize = bpf_classic_proglen(fprog);
unsigned int bpf_fsize = bpf_prog_size(fprog->len);
@@ -1178,19 +1206,19 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
int err;
if (sock_flag(sk, SOCK_FILTER_LOCKED))
- return -EPERM;
+ return ERR_PTR(-EPERM);
/* Make sure new filter is there and in the right amounts. */
if (fprog->filter == NULL)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
prog = bpf_prog_alloc(bpf_fsize, 0);
if (!prog)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
if (copy_from_user(prog->insns, fprog->filter, fsize)) {
__bpf_prog_free(prog);
- return -EFAULT;
+ return ERR_PTR(-EFAULT);
}
prog->len = fprog->len;
@@ -1198,13 +1226,30 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
err = bpf_prog_store_orig_filter(prog, fprog);
if (err) {
__bpf_prog_free(prog);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
/* bpf_prepare_filter() already takes care of freeing
* memory in case something goes wrong.
*/
- prog = bpf_prepare_filter(prog, NULL);
+ return bpf_prepare_filter(prog, NULL);
+}
+
+/**
+ * sk_attach_filter - attach a socket filter
+ * @fprog: the filter program
+ * @sk: the socket to use
+ *
+ * Attach the user's filter code. We first run some sanity checks on
+ * it to make sure it does not explode on us later. If an error
+ * occurs or there is insufficient memory for the filter a negative
+ * errno code is returned. On success the return is zero.
+ */
+int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+{
+ struct bpf_prog *prog = __get_filter(fprog, sk);
+ int err;
+
if (IS_ERR(prog))
return PTR_ERR(prog);
@@ -1218,23 +1263,50 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
}
EXPORT_SYMBOL_GPL(sk_attach_filter);
-int sk_attach_bpf(u32 ufd, struct sock *sk)
+int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
{
- struct bpf_prog *prog;
+ struct bpf_prog *prog = __get_filter(fprog, sk);
int err;
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ err = __reuseport_attach_prog(prog, sk);
+ if (err < 0) {
+ __bpf_prog_release(prog);
+ return err;
+ }
+
+ return 0;
+}
+
+static struct bpf_prog *__get_bpf(u32 ufd, struct sock *sk)
+{
+ struct bpf_prog *prog;
+
if (sock_flag(sk, SOCK_FILTER_LOCKED))
- return -EPERM;
+ return ERR_PTR(-EPERM);
prog = bpf_prog_get(ufd);
if (IS_ERR(prog))
- return PTR_ERR(prog);
+ return prog;
if (prog->type != BPF_PROG_TYPE_SOCKET_FILTER) {
bpf_prog_put(prog);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
+ return prog;
+}
+
+int sk_attach_bpf(u32 ufd, struct sock *sk)
+{
+ struct bpf_prog *prog = __get_bpf(ufd, sk);
+ int err;
+
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
err = __sk_attach_prog(prog, sk);
if (err < 0) {
bpf_prog_put(prog);
@@ -1244,7 +1316,24 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
return 0;
}
-#define BPF_RECOMPUTE_CSUM(flags) ((flags) & 1)
+int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)
+{
+ struct bpf_prog *prog = __get_bpf(ufd, sk);
+ int err;
+
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ err = __reuseport_attach_prog(prog, sk);
+ if (err < 0) {
+ bpf_prog_put(prog);
+ return err;
+ }
+
+ return 0;
+}
+
+#define BPF_LDST_LEN 16U
static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
{
@@ -1252,9 +1341,12 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
int offset = (int) r2;
void *from = (void *) (long) r3;
unsigned int len = (unsigned int) r4;
- char buf[16];
+ char buf[BPF_LDST_LEN];
void *ptr;
+ if (unlikely(flags & ~(BPF_F_RECOMPUTE_CSUM)))
+ return -EINVAL;
+
/* bpf verifier guarantees that:
* 'from' pointer points to bpf program stack
* 'len' bytes of it were initialized
@@ -1274,7 +1366,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
if (unlikely(!ptr))
return -EFAULT;
- if (BPF_RECOMPUTE_CSUM(flags))
+ if (flags & BPF_F_RECOMPUTE_CSUM)
skb_postpull_rcsum(skb, ptr, len);
memcpy(ptr, from, len);
@@ -1283,8 +1375,9 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
/* skb_store_bits cannot return -EFAULT here */
skb_store_bits(skb, offset, ptr, len);
- if (BPF_RECOMPUTE_CSUM(flags) && skb->ip_summed == CHECKSUM_COMPLETE)
- skb->csum = csum_add(skb->csum, csum_partial(ptr, len, 0));
+ if (flags & BPF_F_RECOMPUTE_CSUM)
+ skb_postpush_rcsum(skb, ptr, len);
+
return 0;
}
@@ -1299,8 +1392,35 @@ const struct bpf_func_proto bpf_skb_store_bytes_proto = {
.arg5_type = ARG_ANYTHING,
};
-#define BPF_HEADER_FIELD_SIZE(flags) ((flags) & 0x0f)
-#define BPF_IS_PSEUDO_HEADER(flags) ((flags) & 0x10)
+static u64 bpf_skb_load_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+ const struct sk_buff *skb = (const struct sk_buff *)(unsigned long) r1;
+ int offset = (int) r2;
+ void *to = (void *)(unsigned long) r3;
+ unsigned int len = (unsigned int) r4;
+ void *ptr;
+
+ if (unlikely((u32) offset > 0xffff || len > BPF_LDST_LEN))
+ return -EFAULT;
+
+ ptr = skb_header_pointer(skb, offset, len, to);
+ if (unlikely(!ptr))
+ return -EFAULT;
+ if (ptr != to)
+ memcpy(to, ptr, len);
+
+ return 0;
+}
+
+const struct bpf_func_proto bpf_skb_load_bytes_proto = {
+ .func = bpf_skb_load_bytes,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_ANYTHING,
+ .arg3_type = ARG_PTR_TO_STACK,
+ .arg4_type = ARG_CONST_STACK_SIZE,
+};
static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
{
@@ -1308,6 +1428,8 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
int offset = (int) r2;
__sum16 sum, *ptr;
+ if (unlikely(flags & ~(BPF_F_HDR_FIELD_MASK)))
+ return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
@@ -1319,7 +1441,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
if (unlikely(!ptr))
return -EFAULT;
- switch (BPF_HEADER_FIELD_SIZE(flags)) {
+ switch (flags & BPF_F_HDR_FIELD_MASK) {
case 2:
csum_replace2(ptr, from, to);
break;
@@ -1351,10 +1473,12 @@ const struct bpf_func_proto bpf_l3_csum_replace_proto = {
static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
- bool is_pseudo = !!BPF_IS_PSEUDO_HEADER(flags);
+ bool is_pseudo = flags & BPF_F_PSEUDO_HDR;
int offset = (int) r2;
__sum16 sum, *ptr;
+ if (unlikely(flags & ~(BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK)))
+ return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
@@ -1366,7 +1490,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
if (unlikely(!ptr))
return -EFAULT;
- switch (BPF_HEADER_FIELD_SIZE(flags)) {
+ switch (flags & BPF_F_HDR_FIELD_MASK) {
case 2:
inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo);
break;
@@ -1395,13 +1519,14 @@ const struct bpf_func_proto bpf_l4_csum_replace_proto = {
.arg5_type = ARG_ANYTHING,
};
-#define BPF_IS_REDIRECT_INGRESS(flags) ((flags) & 1)
-
static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1, *skb2;
struct net_device *dev;
+ if (unlikely(flags & ~(BPF_F_INGRESS)))
+ return -EINVAL;
+
dev = dev_get_by_index_rcu(dev_net(skb->dev), ifindex);
if (unlikely(!dev))
return -EINVAL;
@@ -1410,8 +1535,12 @@ static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
if (unlikely(!skb2))
return -ENOMEM;
- if (BPF_IS_REDIRECT_INGRESS(flags))
+ if (flags & BPF_F_INGRESS) {
+ if (skb_at_tc_ingress(skb2))
+ skb_postpush_rcsum(skb2, skb_mac_header(skb2),
+ skb2->mac_len);
return dev_forward_skb(dev, skb2);
+ }
skb2->dev = dev;
skb_sender_cpu_clear(skb2);
@@ -1433,12 +1562,17 @@ struct redirect_info {
};
static DEFINE_PER_CPU(struct redirect_info, redirect_info);
+
static u64 bpf_redirect(u64 ifindex, u64 flags, u64 r3, u64 r4, u64 r5)
{
struct redirect_info *ri = this_cpu_ptr(&redirect_info);
+ if (unlikely(flags & ~(BPF_F_INGRESS)))
+ return TC_ACT_SHOT;
+
ri->ifindex = ifindex;
ri->flags = flags;
+
return TC_ACT_REDIRECT;
}
@@ -1454,8 +1588,12 @@ int skb_do_redirect(struct sk_buff *skb)
return -EINVAL;
}
- if (BPF_IS_REDIRECT_INGRESS(ri->flags))
+ if (ri->flags & BPF_F_INGRESS) {
+ if (skb_at_tc_ingress(skb))
+ skb_postpush_rcsum(skb, skb_mac_header(skb),
+ skb->mac_len);
return dev_forward_skb(dev, skb);
+ }
skb->dev = dev;
skb_sender_cpu_clear(skb);
@@ -1547,19 +1685,49 @@ bool bpf_helper_changes_skb_data(void *func)
return false;
}
+static unsigned short bpf_tunnel_key_af(u64 flags)
+{
+ return flags & BPF_F_TUNINFO_IPV6 ? AF_INET6 : AF_INET;
+}
+
static u64 bpf_skb_get_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
struct bpf_tunnel_key *to = (struct bpf_tunnel_key *) (long) r2;
- struct ip_tunnel_info *info = skb_tunnel_info(skb);
+ const struct ip_tunnel_info *info = skb_tunnel_info(skb);
+ u8 compat[sizeof(struct bpf_tunnel_key)];
- if (unlikely(size != sizeof(struct bpf_tunnel_key) || flags || !info))
- return -EINVAL;
- if (ip_tunnel_info_af(info) != AF_INET)
+ if (unlikely(!info || (flags & ~(BPF_F_TUNINFO_IPV6))))
return -EINVAL;
+ if (ip_tunnel_info_af(info) != bpf_tunnel_key_af(flags))
+ return -EPROTO;
+ if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
+ switch (size) {
+ case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
+ /* Fixup deprecated structure layouts here, so we have
+ * a common path later on.
+ */
+ if (ip_tunnel_info_af(info) != AF_INET)
+ return -EINVAL;
+ to = (struct bpf_tunnel_key *)compat;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
to->tunnel_id = be64_to_cpu(info->key.tun_id);
- to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
+ to->tunnel_tos = info->key.tos;
+ to->tunnel_ttl = info->key.ttl;
+
+ if (flags & BPF_F_TUNINFO_IPV6)
+ memcpy(to->remote_ipv6, &info->key.u.ipv6.src,
+ sizeof(to->remote_ipv6));
+ else
+ to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
+
+ if (unlikely(size != sizeof(struct bpf_tunnel_key)))
+ memcpy((void *)(long) r2, to, size);
return 0;
}
@@ -1581,10 +1749,25 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
struct sk_buff *skb = (struct sk_buff *) (long) r1;
struct bpf_tunnel_key *from = (struct bpf_tunnel_key *) (long) r2;
struct metadata_dst *md = this_cpu_ptr(md_dst);
+ u8 compat[sizeof(struct bpf_tunnel_key)];
struct ip_tunnel_info *info;
- if (unlikely(size != sizeof(struct bpf_tunnel_key) || flags))
+ if (unlikely(flags & ~(BPF_F_TUNINFO_IPV6)))
return -EINVAL;
+ if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
+ switch (size) {
+ case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
+ /* Fixup deprecated structure layouts here, so we have
+ * a common path later on.
+ */
+ memcpy(compat, from, size);
+ memset(compat + size, 0, sizeof(compat) - size);
+ from = (struct bpf_tunnel_key *)compat;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
skb_dst_drop(skb);
dst_hold((struct dst_entry *) md);
@@ -1592,9 +1775,19 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
info = &md->u.tun_info;
info->mode = IP_TUNNEL_INFO_TX;
+
info->key.tun_flags = TUNNEL_KEY;
info->key.tun_id = cpu_to_be64(from->tunnel_id);
- info->key.u.ipv4.dst = cpu_to_be32(from->remote_ipv4);
+ info->key.tos = from->tunnel_tos;
+ info->key.ttl = from->tunnel_ttl;
+
+ if (flags & BPF_F_TUNINFO_IPV6) {
+ info->mode |= IP_TUNNEL_INFO_IPV6;
+ memcpy(&info->key.u.ipv6.dst, from->remote_ipv6,
+ sizeof(from->remote_ipv6));
+ } else {
+ info->key.u.ipv4.dst = cpu_to_be32(from->remote_ipv4);
+ }
return 0;
}
@@ -1654,6 +1847,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
switch (func_id) {
case BPF_FUNC_skb_store_bytes:
return &bpf_skb_store_bytes_proto;
+ case BPF_FUNC_skb_load_bytes:
+ return &bpf_skb_load_bytes_proto;
case BPF_FUNC_l3_csum_replace:
return &bpf_l3_csum_replace_proto;
case BPF_FUNC_l4_csum_replace:
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index f88a62ab019d..b6c8a6629b39 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -471,6 +471,7 @@ static ssize_t phys_switch_id_show(struct device *dev,
if (dev_isalive(netdev)) {
struct switchdev_attr attr = {
+ .orig_dev = netdev,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
@@ -1452,8 +1453,8 @@ static void netdev_release(struct device *d)
static const void *net_namespace(struct device *d)
{
- struct net_device *dev;
- dev = container_of(d, struct net_device, dev);
+ struct net_device *dev = to_net_dev(d);
+
return dev_net(dev);
}
diff --git a/net/core/net-traces.c b/net/core/net-traces.c
index adef015b2f41..92da5e4ceb4f 100644
--- a/net/core/net-traces.c
+++ b/net/core/net-traces.c
@@ -32,6 +32,10 @@
#include <trace/events/sock.h>
#include <trace/events/udp.h>
#include <trace/events/fib.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <trace/events/fib6.h>
+EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
+#endif
EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb);
diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c
index d9ee8d08a3a6..0260c84ed83c 100644
--- a/net/core/netclassid_cgroup.c
+++ b/net/core/netclassid_cgroup.c
@@ -61,9 +61,12 @@ static int update_classid_sock(const void *v, struct file *file, unsigned n)
int err;
struct socket *sock = sock_from_file(file, &err);
- if (sock)
- sock->sk->sk_classid = (u32)(unsigned long)v;
-
+ if (sock) {
+ spin_lock(&cgroup_sk_update_lock);
+ sock_cgroup_set_classid(&sock->sk->sk_cgrp_data,
+ (unsigned long)v);
+ spin_unlock(&cgroup_sk_update_lock);
+ }
return 0;
}
@@ -100,6 +103,8 @@ static int write_classid(struct cgroup_subsys_state *css, struct cftype *cft,
{
struct cgroup_cls_state *cs = css_cls_state(css);
+ cgroup_sk_alloc_disable();
+
cs->classid = (u32)value;
update_classid(css, (void *)(unsigned long)cs->classid);
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index 40fd09fe06ae..f1efbc39ef6b 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -27,6 +27,12 @@
#include <linux/fdtable.h>
+/*
+ * netprio allocates per-net_device priomap array which is indexed by
+ * css->id. Limiting css ID to 16bits doesn't lose anything.
+ */
+#define NETPRIO_ID_MAX USHRT_MAX
+
#define PRIOMAP_MIN_SZ 128
/*
@@ -144,6 +150,9 @@ static int cgrp_css_online(struct cgroup_subsys_state *css)
struct net_device *dev;
int ret = 0;
+ if (css->id > NETPRIO_ID_MAX)
+ return -ENOSPC;
+
if (!parent_css)
return 0;
@@ -200,6 +209,8 @@ static ssize_t write_priomap(struct kernfs_open_file *of,
if (!dev)
return -ENODEV;
+ cgroup_sk_alloc_disable();
+
rtnl_lock();
ret = netprio_set_prio(of_css(of), dev, prio);
@@ -213,8 +224,12 @@ static int update_netprio(const void *v, struct file *file, unsigned n)
{
int err;
struct socket *sock = sock_from_file(file, &err);
- if (sock)
- sock->sk->sk_cgrp_prioidx = (u32)(unsigned long)v;
+ if (sock) {
+ spin_lock(&cgroup_sk_update_lock);
+ sock_cgroup_set_prioidx(&sock->sk->sk_cgrp_data,
+ (unsigned long)v);
+ spin_unlock(&cgroup_sk_update_lock);
+ }
return 0;
}
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index de8d5cc5eb24..1474cfd2dc1c 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2787,7 +2787,9 @@ static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
} else {
skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
}
- skb_reserve(skb, LL_RESERVED_SPACE(dev));
+
+ if (likely(skb))
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
return skb;
}
@@ -2898,7 +2900,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
if (!(pkt_dev->flags & F_UDPCSUM)) {
skb->ip_summed = CHECKSUM_NONE;
- } else if (odev->features & NETIF_F_V4_CSUM) {
+ } else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM)) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum = 0;
udp4_hwcsum(skb, iph->saddr, iph->daddr);
@@ -3032,7 +3034,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
if (!(pkt_dev->flags & F_UDPCSUM)) {
skb->ip_summed = CHECKSUM_NONE;
- } else if (odev->features & NETIF_F_V6_CSUM) {
+ } else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IPV6_CSUM)) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 34ba7a08876d..d735e854f916 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1027,6 +1027,7 @@ static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
{
int err;
struct switchdev_attr attr = {
+ .orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
@@ -2563,7 +2564,7 @@ static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
struct net_device *dev,
u8 *addr, u16 vid, u32 pid, u32 seq,
int type, unsigned int flags,
- int nlflags)
+ int nlflags, u16 ndm_state)
{
struct nlmsghdr *nlh;
struct ndmsg *ndm;
@@ -2579,7 +2580,7 @@ static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
ndm->ndm_flags = flags;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dev->ifindex;
- ndm->ndm_state = NUD_PERMANENT;
+ ndm->ndm_state = ndm_state;
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
@@ -2600,7 +2601,8 @@ static inline size_t rtnl_fdb_nlmsg_size(void)
return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN);
}
-static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type)
+static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type,
+ u16 ndm_state)
{
struct net *net = dev_net(dev);
struct sk_buff *skb;
@@ -2611,7 +2613,7 @@ static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type)
goto errout;
err = nlmsg_populate_fdb_fill(skb, dev, addr, vid,
- 0, 0, type, NTF_SELF, 0);
+ 0, 0, type, NTF_SELF, 0, ndm_state);
if (err < 0) {
kfree_skb(skb);
goto errout;
@@ -2746,7 +2748,8 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
nlh->nlmsg_flags);
if (!err) {
- rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH);
+ rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH,
+ ndm->ndm_state);
ndm->ndm_flags &= ~NTF_SELF;
}
}
@@ -2847,7 +2850,8 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
if (!err) {
- rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH);
+ rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH,
+ ndm->ndm_state);
ndm->ndm_flags &= ~NTF_SELF;
}
}
@@ -2875,7 +2879,7 @@ static int nlmsg_populate_fdb(struct sk_buff *skb,
err = nlmsg_populate_fdb_fill(skb, dev, ha->addr, 0,
portid, seq,
RTM_NEWNEIGH, NTF_SELF,
- NLM_F_MULTI);
+ NLM_F_MULTI, NUD_PERMANENT);
if (err < 0)
return err;
skip:
@@ -3347,7 +3351,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(skb->sk);
rtnl_doit_func doit;
- int sz_idx, kind;
+ int kind;
int family;
int type;
int err;
@@ -3363,7 +3367,6 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return 0;
family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
- sz_idx = type>>2;
kind = type&3;
if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
diff --git a/net/core/scm.c b/net/core/scm.c
index 8a1741b14302..14596fb37172 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -289,8 +289,8 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
/* Bump the usage count and install the file. */
sock = sock_from_file(fp[i], &err);
if (sock) {
- sock_update_netprioidx(sock->sk);
- sock_update_classid(sock->sk);
+ sock_update_netprioidx(&sock->sk->sk_cgrp_data);
+ sock_update_classid(&sock->sk->sk_cgrp_data);
}
fd_install(new_fd, get_file(fp[i]));
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 0d91f7dca751..51270238e269 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -134,6 +134,7 @@
#include <linux/sock_diag.h>
#include <linux/filter.h>
+#include <net/sock_reuseport.h>
#include <trace/events/sock.h>
@@ -932,6 +933,32 @@ set_rcvbuf:
}
break;
+ case SO_ATTACH_REUSEPORT_CBPF:
+ ret = -EINVAL;
+ if (optlen == sizeof(struct sock_fprog)) {
+ struct sock_fprog fprog;
+
+ ret = -EFAULT;
+ if (copy_from_user(&fprog, optval, sizeof(fprog)))
+ break;
+
+ ret = sk_reuseport_attach_filter(&fprog, sk);
+ }
+ break;
+
+ case SO_ATTACH_REUSEPORT_EBPF:
+ ret = -EINVAL;
+ if (optlen == sizeof(u32)) {
+ u32 ufd;
+
+ ret = -EFAULT;
+ if (copy_from_user(&ufd, optval, sizeof(ufd)))
+ break;
+
+ ret = sk_reuseport_attach_bpf(ufd, sk);
+ }
+ break;
+
case SO_DETACH_FILTER:
ret = sk_detach_filter(sk);
break;
@@ -1362,6 +1389,7 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
if (!try_module_get(prot->owner))
goto out_free_sec;
sk_tx_queue_clear(sk);
+ cgroup_sk_alloc(&sk->sk_cgrp_data);
}
return sk;
@@ -1384,6 +1412,7 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
owner = prot->owner;
slab = prot->slab;
+ cgroup_sk_free(&sk->sk_cgrp_data);
security_sk_free(sk);
if (slab != NULL)
kmem_cache_free(slab, sk);
@@ -1392,17 +1421,6 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
module_put(owner);
}
-#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
-void sock_update_netprioidx(struct sock *sk)
-{
- if (in_interrupt())
- return;
-
- sk->sk_cgrp_prioidx = task_netprioidx(current);
-}
-EXPORT_SYMBOL_GPL(sock_update_netprioidx);
-#endif
-
/**
* sk_alloc - All socket objects are allocated here
* @net: the applicable net namespace
@@ -1431,8 +1449,8 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
sock_net_set(sk, net);
atomic_set(&sk->sk_wmem_alloc, 1);
- sock_update_classid(sk);
- sock_update_netprioidx(sk);
+ sock_update_classid(&sk->sk_cgrp_data);
+ sock_update_netprioidx(&sk->sk_cgrp_data);
}
return sk;
@@ -1452,6 +1470,8 @@ void sk_destruct(struct sock *sk)
sk_filter_uncharge(sk, filter);
RCU_INIT_POINTER(sk->sk_filter, NULL);
}
+ if (rcu_access_pointer(sk->sk_reuseport_cb))
+ reuseport_detach_sock(sk);
sock_disable_timestamp(sk, SK_FLAGS_TIMESTAMP);
@@ -2281,7 +2301,7 @@ static void sock_def_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_all(&wq->wait);
rcu_read_unlock();
}
@@ -2292,7 +2312,7 @@ static void sock_def_error_report(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_poll(&wq->wait, POLLERR);
sk_wake_async(sk, SOCK_WAKE_IO, POLL_ERR);
rcu_read_unlock();
@@ -2304,7 +2324,7 @@ static void sock_def_readable(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN | POLLPRI |
POLLRDNORM | POLLRDBAND);
sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
@@ -2322,7 +2342,7 @@ static void sock_def_write_space(struct sock *sk)
*/
if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) {
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLWRNORM | POLLWRBAND);
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index 0c1d58d43f67..a996ce8c8fb2 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -214,7 +214,7 @@ void sock_diag_unregister(const struct sock_diag_handler *hnld)
}
EXPORT_SYMBOL_GPL(sock_diag_unregister);
-static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
{
int err;
struct sock_diag_req *req = nlmsg_data(nlh);
@@ -234,8 +234,12 @@ static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
hndl = sock_diag_handlers[req->sdiag_family];
if (hndl == NULL)
err = -ENOENT;
- else
+ else if (nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY)
err = hndl->dump(skb, nlh);
+ else if (nlh->nlmsg_type == SOCK_DESTROY && hndl->destroy)
+ err = hndl->destroy(skb, nlh);
+ else
+ err = -EOPNOTSUPP;
mutex_unlock(&sock_diag_table_mutex);
return err;
@@ -261,7 +265,8 @@ static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return ret;
case SOCK_DIAG_BY_FAMILY:
- return __sock_diag_rcv_msg(skb, nlh);
+ case SOCK_DESTROY:
+ return __sock_diag_cmd(skb, nlh);
default:
return -EINVAL;
}
@@ -295,6 +300,18 @@ static int sock_diag_bind(struct net *net, int group)
return 0;
}
+int sock_diag_destroy(struct sock *sk, int err)
+{
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (!sk->sk_prot->diag_destroy)
+ return -EOPNOTSUPP;
+
+ return sk->sk_prot->diag_destroy(sk, err);
+}
+EXPORT_SYMBOL_GPL(sock_diag_destroy);
+
static int __net_init diag_net_init(struct net *net)
{
struct netlink_kernel_cfg cfg = {
diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
new file mode 100644
index 000000000000..1df98c557440
--- /dev/null
+++ b/net/core/sock_reuseport.c
@@ -0,0 +1,251 @@
+/*
+ * To speed up listener socket lookup, create an array to store all sockets
+ * listening on the same port. This allows a decision to be made after finding
+ * the first socket. An optional BPF program can also be configured for
+ * selecting the socket index from the array of available sockets.
+ */
+
+#include <net/sock_reuseport.h>
+#include <linux/bpf.h>
+#include <linux/rcupdate.h>
+
+#define INIT_SOCKS 128
+
+static DEFINE_SPINLOCK(reuseport_lock);
+
+static struct sock_reuseport *__reuseport_alloc(u16 max_socks)
+{
+ size_t size = sizeof(struct sock_reuseport) +
+ sizeof(struct sock *) * max_socks;
+ struct sock_reuseport *reuse = kzalloc(size, GFP_ATOMIC);
+
+ if (!reuse)
+ return NULL;
+
+ reuse->max_socks = max_socks;
+
+ RCU_INIT_POINTER(reuse->prog, NULL);
+ return reuse;
+}
+
+int reuseport_alloc(struct sock *sk)
+{
+ struct sock_reuseport *reuse;
+
+ /* bh lock used since this function call may precede hlist lock in
+ * soft irq of receive path or setsockopt from process context
+ */
+ spin_lock_bh(&reuseport_lock);
+ WARN_ONCE(rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock)),
+ "multiple allocations for the same socket");
+ reuse = __reuseport_alloc(INIT_SOCKS);
+ if (!reuse) {
+ spin_unlock_bh(&reuseport_lock);
+ return -ENOMEM;
+ }
+
+ reuse->socks[0] = sk;
+ reuse->num_socks = 1;
+ rcu_assign_pointer(sk->sk_reuseport_cb, reuse);
+
+ spin_unlock_bh(&reuseport_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(reuseport_alloc);
+
+static struct sock_reuseport *reuseport_grow(struct sock_reuseport *reuse)
+{
+ struct sock_reuseport *more_reuse;
+ u32 more_socks_size, i;
+
+ more_socks_size = reuse->max_socks * 2U;
+ if (more_socks_size > U16_MAX)
+ return NULL;
+
+ more_reuse = __reuseport_alloc(more_socks_size);
+ if (!more_reuse)
+ return NULL;
+
+ more_reuse->max_socks = more_socks_size;
+ more_reuse->num_socks = reuse->num_socks;
+ more_reuse->prog = reuse->prog;
+
+ memcpy(more_reuse->socks, reuse->socks,
+ reuse->num_socks * sizeof(struct sock *));
+
+ for (i = 0; i < reuse->num_socks; ++i)
+ rcu_assign_pointer(reuse->socks[i]->sk_reuseport_cb,
+ more_reuse);
+
+ /* Note: we use kfree_rcu here instead of reuseport_free_rcu so
+ * that reuse and more_reuse can temporarily share a reference
+ * to prog.
+ */
+ kfree_rcu(reuse, rcu);
+ return more_reuse;
+}
+
+/**
+ * reuseport_add_sock - Add a socket to the reuseport group of another.
+ * @sk: New socket to add to the group.
+ * @sk2: Socket belonging to the existing reuseport group.
+ * May return ENOMEM and not add socket to group under memory pressure.
+ */
+int reuseport_add_sock(struct sock *sk, const struct sock *sk2)
+{
+ struct sock_reuseport *reuse;
+
+ spin_lock_bh(&reuseport_lock);
+ reuse = rcu_dereference_protected(sk2->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock)),
+ WARN_ONCE(rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock)),
+ "socket already in reuseport group");
+
+ if (reuse->num_socks == reuse->max_socks) {
+ reuse = reuseport_grow(reuse);
+ if (!reuse) {
+ spin_unlock_bh(&reuseport_lock);
+ return -ENOMEM;
+ }
+ }
+
+ reuse->socks[reuse->num_socks] = sk;
+ /* paired with smp_rmb() in reuseport_select_sock() */
+ smp_wmb();
+ reuse->num_socks++;
+ rcu_assign_pointer(sk->sk_reuseport_cb, reuse);
+
+ spin_unlock_bh(&reuseport_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(reuseport_add_sock);
+
+static void reuseport_free_rcu(struct rcu_head *head)
+{
+ struct sock_reuseport *reuse;
+
+ reuse = container_of(head, struct sock_reuseport, rcu);
+ if (reuse->prog)
+ bpf_prog_destroy(reuse->prog);
+ kfree(reuse);
+}
+
+void reuseport_detach_sock(struct sock *sk)
+{
+ struct sock_reuseport *reuse;
+ int i;
+
+ spin_lock_bh(&reuseport_lock);
+ reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock));
+ rcu_assign_pointer(sk->sk_reuseport_cb, NULL);
+
+ for (i = 0; i < reuse->num_socks; i++) {
+ if (reuse->socks[i] == sk) {
+ reuse->socks[i] = reuse->socks[reuse->num_socks - 1];
+ reuse->num_socks--;
+ if (reuse->num_socks == 0)
+ call_rcu(&reuse->rcu, reuseport_free_rcu);
+ break;
+ }
+ }
+ spin_unlock_bh(&reuseport_lock);
+}
+EXPORT_SYMBOL(reuseport_detach_sock);
+
+static struct sock *run_bpf(struct sock_reuseport *reuse, u16 socks,
+ struct bpf_prog *prog, struct sk_buff *skb,
+ int hdr_len)
+{
+ struct sk_buff *nskb = NULL;
+ u32 index;
+
+ if (skb_shared(skb)) {
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ return NULL;
+ skb = nskb;
+ }
+
+ /* temporarily advance data past protocol header */
+ if (!pskb_pull(skb, hdr_len)) {
+ kfree_skb(nskb);
+ return NULL;
+ }
+ index = bpf_prog_run_save_cb(prog, skb);
+ __skb_push(skb, hdr_len);
+
+ consume_skb(nskb);
+
+ if (index >= socks)
+ return NULL;
+
+ return reuse->socks[index];
+}
+
+/**
+ * reuseport_select_sock - Select a socket from an SO_REUSEPORT group.
+ * @sk: First socket in the group.
+ * @hash: When no BPF filter is available, use this hash to select.
+ * @skb: skb to run through BPF filter.
+ * @hdr_len: BPF filter expects skb data pointer at payload data. If
+ * the skb does not yet point at the payload, this parameter represents
+ * how far the pointer needs to advance to reach the payload.
+ * Returns a socket that should receive the packet (or NULL on error).
+ */
+struct sock *reuseport_select_sock(struct sock *sk,
+ u32 hash,
+ struct sk_buff *skb,
+ int hdr_len)
+{
+ struct sock_reuseport *reuse;
+ struct bpf_prog *prog;
+ struct sock *sk2 = NULL;
+ u16 socks;
+
+ rcu_read_lock();
+ reuse = rcu_dereference(sk->sk_reuseport_cb);
+
+ /* if memory allocation failed or add call is not yet complete */
+ if (!reuse)
+ goto out;
+
+ prog = rcu_dereference(reuse->prog);
+ socks = READ_ONCE(reuse->num_socks);
+ if (likely(socks)) {
+ /* paired with smp_wmb() in reuseport_add_sock() */
+ smp_rmb();
+
+ if (prog && skb)
+ sk2 = run_bpf(reuse, socks, prog, skb, hdr_len);
+ else
+ sk2 = reuse->socks[reciprocal_scale(hash, socks)];
+ }
+
+out:
+ rcu_read_unlock();
+ return sk2;
+}
+EXPORT_SYMBOL(reuseport_select_sock);
+
+struct bpf_prog *
+reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog)
+{
+ struct sock_reuseport *reuse;
+ struct bpf_prog *old_prog;
+
+ spin_lock_bh(&reuseport_lock);
+ reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock));
+ old_prog = rcu_dereference_protected(reuse->prog,
+ lockdep_is_held(&reuseport_lock));
+ rcu_assign_pointer(reuse->prog, prog);
+ spin_unlock_bh(&reuseport_lock);
+
+ return old_prog;
+}
+EXPORT_SYMBOL(reuseport_attach_prog);
diff --git a/net/core/stream.c b/net/core/stream.c
index b96f7a79e544..159516a11b7e 100644
--- a/net/core/stream.c
+++ b/net/core/stream.c
@@ -35,7 +35,7 @@ void sk_stream_write_space(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_poll(&wq->wait, POLLOUT |
POLLWRNORM | POLLWRBAND);
if (wq && wq->fasync_list && !(sk->sk_shutdown & SEND_SHUTDOWN))
diff --git a/net/dccp/output.c b/net/dccp/output.c
index 4ce912e691d0..b66c84db0766 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -201,7 +201,7 @@ void dccp_write_space(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible(&wq->wait);
/* Should agree with poll, otherwise some programs break */
if (sock_writeable(sk))
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 1eba07feb34a..fa4daba8db55 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -21,8 +21,10 @@
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/of_net.h>
+#include <linux/of_gpio.h>
#include <linux/sysfs.h>
#include <linux/phy_fixed.h>
+#include <linux/gpio/consumer.h>
#include "dsa_priv.h"
char dsa_driver_version[] = "0.1";
@@ -437,7 +439,7 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
if (of_phy_is_fixed_link(port_dn)) {
phydev = of_phy_find_device(port_dn);
if (phydev) {
- int addr = phydev->addr;
+ int addr = phydev->mdio.addr;
phy_device_free(phydev);
of_node_put(port_dn);
@@ -454,8 +456,7 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
if (!ds->ports[port])
continue;
- unregister_netdev(ds->ports[port]);
- free_netdev(ds->ports[port]);
+ dsa_slave_destroy(ds->ports[port]);
}
mdiobus_unregister(ds->slave_mii_bus);
@@ -506,33 +507,6 @@ static int dsa_switch_resume(struct dsa_switch *ds)
}
#endif
-
-/* link polling *************************************************************/
-static void dsa_link_poll_work(struct work_struct *ugly)
-{
- struct dsa_switch_tree *dst;
- int i;
-
- dst = container_of(ugly, struct dsa_switch_tree, link_poll_work);
-
- for (i = 0; i < dst->pd->nr_chips; i++) {
- struct dsa_switch *ds = dst->ds[i];
-
- if (ds != NULL && ds->drv->poll_link != NULL)
- ds->drv->poll_link(ds);
- }
-
- mod_timer(&dst->link_poll_timer, round_jiffies(jiffies + HZ));
-}
-
-static void dsa_link_poll_timer(unsigned long _dst)
-{
- struct dsa_switch_tree *dst = (void *)_dst;
-
- schedule_work(&dst->link_poll_work);
-}
-
-
/* platform driver init and cleanup *****************************************/
static int dev_is_class(struct device *dev, void *class)
{
@@ -688,6 +662,9 @@ static int dsa_of_probe(struct device *dev)
const char *port_name;
int chip_index, port_index;
const unsigned int *sw_addr, *port_reg;
+ int gpio;
+ enum of_gpio_flags of_flags;
+ unsigned long flags;
u32 eeprom_len;
int ret;
@@ -766,6 +743,19 @@ static int dsa_of_probe(struct device *dev)
put_device(cd->host_dev);
cd->host_dev = &mdio_bus_switch->dev;
}
+ gpio = of_get_named_gpio_flags(child, "reset-gpios", 0,
+ &of_flags);
+ if (gpio_is_valid(gpio)) {
+ flags = (of_flags == OF_GPIO_ACTIVE_LOW ?
+ GPIOF_ACTIVE_LOW : 0);
+ ret = devm_gpio_request_one(dev, gpio, flags,
+ "switch_reset");
+ if (ret)
+ goto out_free_chip;
+
+ cd->reset = gpio_to_desc(gpio);
+ gpiod_direction_output(cd->reset, 0);
+ }
for_each_available_child_of_node(child, port) {
port_reg = of_get_property(port, "reg", NULL);
@@ -859,8 +849,6 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,
}
dst->ds[i] = ds;
- if (ds->drv->poll_link != NULL)
- dst->link_poll_needed = 1;
++configured;
}
@@ -879,15 +867,6 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,
wmb();
dev->dsa_ptr = (void *)dst;
- if (dst->link_poll_needed) {
- INIT_WORK(&dst->link_poll_work, dsa_link_poll_work);
- init_timer(&dst->link_poll_timer);
- dst->link_poll_timer.data = (unsigned long)dst;
- dst->link_poll_timer.function = dsa_link_poll_timer;
- dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);
- add_timer(&dst->link_poll_timer);
- }
-
return 0;
}
@@ -939,8 +918,10 @@ static int dsa_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dst);
ret = dsa_setup_dst(dst, dev, &pdev->dev, pd);
- if (ret)
+ if (ret) {
+ dev_put(dev);
goto out;
+ }
return 0;
@@ -954,17 +935,14 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)
{
int i;
- if (dst->link_poll_needed)
- del_timer_sync(&dst->link_poll_timer);
-
- flush_work(&dst->link_poll_work);
-
for (i = 0; i < dst->pd->nr_chips; i++) {
struct dsa_switch *ds = dst->ds[i];
if (ds)
dsa_switch_destroy(ds);
}
+
+ dev_put(dst->master_netdev);
}
static int dsa_remove(struct platform_device *pdev)
@@ -1010,6 +988,14 @@ static int dsa_suspend(struct device *d)
struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
int i, ret = 0;
+ dst->master_netdev->dsa_ptr = NULL;
+
+ /* If we used a tagging format that doesn't have an ethertype
+ * field, make sure that all packets from this point get sent
+ * without the tag and go through the regular receive path.
+ */
+ wmb();
+
for (i = 0; i < dst->pd->nr_chips; i++) {
struct dsa_switch *ds = dst->ds[i];
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 311796c809af..1d1a54687e4a 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -61,6 +61,7 @@ extern const struct dsa_device_ops notag_netdev_ops;
void dsa_slave_mii_bus_init(struct dsa_switch *ds);
int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
int port, char *name);
+void dsa_slave_destroy(struct net_device *slave_dev);
int dsa_slave_suspend(struct net_device *slave_dev);
int dsa_slave_resume(struct net_device *slave_dev);
int dsa_slave_netdevice_event(struct notifier_block *unused,
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 7bc787b095c8..40b9ca72aae3 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -15,6 +15,7 @@
#include <linux/phy_fixed.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
+#include <linux/mdio.h>
#include <net/rtnetlink.h>
#include <net/switchdev.h>
#include <linux/if_bridge.h>
@@ -997,7 +998,7 @@ static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
{
struct dsa_switch *ds = p->parent;
- p->phy = ds->slave_mii_bus->phy_map[addr];
+ p->phy = mdiobus_get_phy(ds->slave_mii_bus, addr);
if (!p->phy) {
netdev_err(slave_dev, "no phy at %d\n", addr);
return -ENODEV;
@@ -1080,11 +1081,10 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret);
return ret;
}
- } else {
- netdev_info(slave_dev, "attached PHY at address %d [%s]\n",
- p->phy->addr, p->phy->drv->name);
}
+ phy_attached_info(p->phy);
+
return 0;
}
@@ -1189,13 +1189,6 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
p->old_link = -1;
p->old_duplex = -1;
- ret = dsa_slave_phy_setup(p, slave_dev);
- if (ret) {
- netdev_err(master, "error %d setting up slave phy\n", ret);
- free_netdev(slave_dev);
- return ret;
- }
-
ds->ports[port] = slave_dev;
ret = register_netdev(slave_dev);
if (ret) {
@@ -1209,9 +1202,27 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
netif_carrier_off(slave_dev);
+ ret = dsa_slave_phy_setup(p, slave_dev);
+ if (ret) {
+ netdev_err(master, "error %d setting up slave phy\n", ret);
+ free_netdev(slave_dev);
+ return ret;
+ }
+
return 0;
}
+void dsa_slave_destroy(struct net_device *slave_dev)
+{
+ struct dsa_slave_priv *p = netdev_priv(slave_dev);
+
+ netif_carrier_off(slave_dev);
+ if (p->phy)
+ phy_disconnect(p->phy);
+ unregister_netdev(slave_dev);
+ free_netdev(slave_dev);
+}
+
static bool dsa_slave_dev_check(struct net_device *dev)
{
return dev->netdev_ops == &dsa_slave_netdev_ops;
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 9e63f252a89e..103871784e50 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -52,6 +52,8 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/if_ether.h>
+#include <linux/of_net.h>
+#include <linux/pci.h>
#include <net/dst.h>
#include <net/arp.h>
#include <net/sock.h>
@@ -485,3 +487,32 @@ static int __init eth_offload_init(void)
}
fs_initcall(eth_offload_init);
+
+unsigned char * __weak arch_get_platform_mac_address(void)
+{
+ return NULL;
+}
+
+int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr)
+{
+ const unsigned char *addr;
+ struct device_node *dp;
+
+ if (dev_is_pci(dev))
+ dp = pci_device_to_OF_node(to_pci_dev(dev));
+ else
+ dp = dev->of_node;
+
+ addr = NULL;
+ if (dp)
+ addr = of_get_mac_address(dp);
+ if (!addr)
+ addr = arch_get_platform_mac_address();
+
+ if (!addr)
+ return -ENODEV;
+
+ ether_addr_copy(mac_addr, addr);
+ return 0;
+}
+EXPORT_SYMBOL(eth_platform_get_mac_address);
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 20c49c724ba0..737c87a2a41e 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -161,9 +161,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
wdev->needed_headroom;
ldev->needed_tailroom = wdev->needed_tailroom;
- lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154);
-
- ret = register_netdevice(ldev);
+ ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
if (ret < 0) {
dev_put(wdev);
return ret;
@@ -180,7 +178,7 @@ static void lowpan_dellink(struct net_device *ldev, struct list_head *head)
ASSERT_RTNL();
wdev->ieee802154_ptr->lowpan_dev = NULL;
- unregister_netdevice(ldev);
+ lowpan_unregister_netdevice(ldev);
dev_put(wdev);
}
diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index 6b437e8760d3..30d875dff6b5 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -624,7 +624,6 @@ int __init lowpan_net_frag_init(void)
lowpan_frags.hashfn = lowpan_hashfn;
lowpan_frags.constructor = lowpan_frag_init;
lowpan_frags.destructor = NULL;
- lowpan_frags.skb_free = NULL;
lowpan_frags.qsize = sizeof(struct frag_queue);
lowpan_frags.match = lowpan_frag_match;
lowpan_frags.frag_expire = lowpan_frag_expire;
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 416dfa004cfb..c22920525e5d 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -436,6 +436,19 @@ config INET_UDP_DIAG
Support for UDP socket monitoring interface used by the ss tool.
If unsure, say Y.
+config INET_DIAG_DESTROY
+ bool "INET: allow privileged process to administratively close sockets"
+ depends on INET_DIAG
+ default n
+ ---help---
+ Provides a SOCK_DESTROY operation that allows privileged processes
+ (e.g., a connection manager or a network administration tool such as
+ ss) to close sockets opened by other processes. Closing a socket in
+ this way interrupts any blocking read/write/connect operations on
+ the socket and causes future socket calls to behave as if the socket
+ had been disconnected.
+ If unsure, say N.
+
menuconfig TCP_CONG_ADVANCED
bool "TCP: advanced congestion control"
---help---
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index bd903fe0f750..976f0dcf6991 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -498,7 +498,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
sk->sk_allocation = GFP_ATOMIC;
if (cfg->udp_config.family == AF_INET) {
- err = udp_add_offload(&fou->udp_offloads);
+ err = udp_add_offload(net, &fou->udp_offloads);
if (err)
goto error;
}
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index ab9f8a66615d..8bb8e7ad8548 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -350,17 +350,12 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
nlmsg_flags, unlh);
}
-int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
- struct sk_buff *in_skb,
- const struct nlmsghdr *nlh,
- const struct inet_diag_req_v2 *req)
+struct sock *inet_diag_find_one_icsk(struct net *net,
+ struct inet_hashinfo *hashinfo,
+ const struct inet_diag_req_v2 *req)
{
- struct net *net = sock_net(in_skb->sk);
- struct sk_buff *rep;
struct sock *sk;
- int err;
- err = -EINVAL;
if (req->sdiag_family == AF_INET)
sk = inet_lookup(net, hashinfo, req->id.idiag_dst[0],
req->id.idiag_dport, req->id.idiag_src[0],
@@ -375,15 +370,33 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
req->id.idiag_if);
#endif
else
- goto out_nosk;
+ return ERR_PTR(-EINVAL);
- err = -ENOENT;
if (!sk)
- goto out_nosk;
+ return ERR_PTR(-ENOENT);
- err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
- if (err)
- goto out;
+ if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) {
+ sock_gen_put(sk);
+ return ERR_PTR(-ENOENT);
+ }
+
+ return sk;
+}
+EXPORT_SYMBOL_GPL(inet_diag_find_one_icsk);
+
+int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
+ struct sk_buff *in_skb,
+ const struct nlmsghdr *nlh,
+ const struct inet_diag_req_v2 *req)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct sk_buff *rep;
+ struct sock *sk;
+ int err;
+
+ sk = inet_diag_find_one_icsk(net, hashinfo, req);
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
rep = nlmsg_new(inet_sk_attr_size(), GFP_KERNEL);
if (!rep) {
@@ -409,12 +422,11 @@ out:
if (sk)
sock_gen_put(sk);
-out_nosk:
return err;
}
EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk);
-static int inet_diag_get_exact(struct sk_buff *in_skb,
+static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb,
const struct nlmsghdr *nlh,
const struct inet_diag_req_v2 *req)
{
@@ -424,8 +436,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
handler = inet_diag_lock_handler(req->sdiag_protocol);
if (IS_ERR(handler))
err = PTR_ERR(handler);
- else
+ else if (cmd == SOCK_DIAG_BY_FAMILY)
err = handler->dump_one(in_skb, nlh, req);
+ else if (cmd == SOCK_DESTROY && handler->destroy)
+ err = handler->destroy(in_skb, req);
+ else
+ err = -EOPNOTSUPP;
inet_diag_unlock_handler(handler);
return err;
@@ -938,7 +954,7 @@ static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
req.idiag_states = rc->idiag_states;
req.id = rc->id;
- return inet_diag_get_exact(in_skb, nlh, &req);
+ return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh, &req);
}
static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -972,7 +988,7 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
return inet_diag_get_exact_compat(skb, nlh);
}
-static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
+static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h)
{
int hdrlen = sizeof(struct inet_diag_req_v2);
struct net *net = sock_net(skb->sk);
@@ -980,7 +996,8 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
if (nlmsg_len(h) < hdrlen)
return -EINVAL;
- if (h->nlmsg_flags & NLM_F_DUMP) {
+ if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY &&
+ h->nlmsg_flags & NLM_F_DUMP) {
if (nlmsg_attrlen(h, hdrlen)) {
struct nlattr *attr;
@@ -999,7 +1016,7 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
}
}
- return inet_diag_get_exact(skb, h, nlmsg_data(h));
+ return inet_diag_cmd_exact(h->nlmsg_type, skb, h, nlmsg_data(h));
}
static
@@ -1050,14 +1067,16 @@ int inet_diag_handler_get_info(struct sk_buff *skb, struct sock *sk)
static const struct sock_diag_handler inet_diag_handler = {
.family = AF_INET,
- .dump = inet_diag_handler_dump,
+ .dump = inet_diag_handler_cmd,
.get_info = inet_diag_handler_get_info,
+ .destroy = inet_diag_handler_cmd,
};
static const struct sock_diag_handler inet6_diag_handler = {
.family = AF_INET6,
- .dump = inet_diag_handler_dump,
+ .dump = inet_diag_handler_cmd,
.get_info = inet_diag_handler_get_info,
+ .destroy = inet_diag_handler_cmd,
};
int inet_diag_register(const struct inet_diag_handler *h)
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index fe144dae7372..3a88b0c73797 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -285,14 +285,6 @@ void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f)
}
EXPORT_SYMBOL(inet_frag_kill);
-static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f,
- struct sk_buff *skb)
-{
- if (f->skb_free)
- f->skb_free(skb);
- kfree_skb(skb);
-}
-
void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f)
{
struct sk_buff *fp;
@@ -309,7 +301,7 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f)
struct sk_buff *xp = fp->next;
sum_truesize += fp->truesize;
- frag_kfree_skb(nf, f, fp);
+ kfree_skb(fp);
fp = xp;
}
sum = sum_truesize + f->qsize;
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 1fe55ae81781..3f00810b7288 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -891,7 +891,6 @@ void __init ipfrag_init(void)
ip4_frags.hashfn = ip4_hashfn;
ip4_frags.constructor = ip4_frag_init;
ip4_frags.destructor = ip4_frag_free;
- ip4_frags.skb_free = NULL;
ip4_frags.qsize = sizeof(struct ipq);
ip4_frags.match = ip4_frag_match;
ip4_frags.frag_expire = ip_expire;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 614521437e30..7c51c4e1661f 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -24,7 +24,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/in6.h>
@@ -562,10 +561,9 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
tunnel_id_to_key(tun_info->key.tun_id), 0);
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
- err = iptunnel_xmit(skb->sk, rt, skb, fl.saddr,
- key->u.ipv4.dst, IPPROTO_GRE,
- key->tos, key->ttl, df, false);
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+
+ iptunnel_xmit(skb->sk, rt, skb, fl.saddr, key->u.ipv4.dst, IPPROTO_GRE,
+ key->tos, key->ttl, df, false);
return;
err_free_rt:
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 4233cbe47052..512a44778cf2 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -76,7 +76,6 @@
#include <linux/igmp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
-#include <linux/mroute.h>
#include <linux/netlink.h>
#include <linux/tcp.h>
@@ -912,7 +911,7 @@ static int __ip_append_data(struct sock *sk,
*/
if (transhdrlen &&
length + fragheaderlen <= mtu &&
- rt->dst.dev->features & NETIF_F_V4_CSUM &&
+ rt->dst.dev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM) &&
!(flags & MSG_MORE) &&
!exthdrlen)
csummode = CHECKSUM_PARTIAL;
@@ -921,7 +920,7 @@ static int __ip_append_data(struct sock *sk,
if (((length > mtu) || (skb && skb_is_gso(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len &&
- (sk->sk_type == SOCK_DGRAM)) {
+ (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx) {
err = ip_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen, transhdrlen,
maxfraglen, flags);
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index cbb51f3fac06..c7bd72e9b544 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -30,7 +30,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
@@ -657,7 +656,6 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
struct rtable *rt; /* Route to the other host */
unsigned int max_headroom; /* The extra header space needed */
__be32 dst;
- int err;
bool connected;
inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
@@ -795,10 +793,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
return;
}
- err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol,
- tos, ttl, df, !net_eq(tunnel->net, dev_net(dev)));
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
-
+ iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl,
+ df, !net_eq(tunnel->net, dev_net(dev)));
return;
#if IS_ENABLED(CONFIG_IPV6)
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 6cb9009c3d96..859d415c0b2d 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -24,7 +24,6 @@
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
@@ -48,12 +47,13 @@
#include <net/rtnetlink.h>
#include <net/dst_metadata.h>
-int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
- __be32 src, __be32 dst, __u8 proto,
- __u8 tos, __u8 ttl, __be16 df, bool xnet)
+void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
+ __be32 src, __be32 dst, __u8 proto,
+ __u8 tos, __u8 ttl, __be16 df, bool xnet)
{
int pkt_len = skb->len - skb_inner_network_offset(skb);
struct net *net = dev_net(rt->dst.dev);
+ struct net_device *dev = skb->dev;
struct iphdr *iph;
int err;
@@ -82,7 +82,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
err = ip_local_out(net, sk, skb);
if (unlikely(net_xmit_eval(err)))
pkt_len = 0;
- return pkt_len;
+ iptunnel_xmit_stats(dev, pkt_len);
}
EXPORT_SYMBOL_GPL(iptunnel_xmit);
@@ -251,7 +251,7 @@ static int ip_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info = lwt_tun_info(new_state);
if (tb[LWTUNNEL_IP_ID])
- tun_info->key.tun_id = nla_get_u64(tb[LWTUNNEL_IP_ID]);
+ tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP_ID]);
if (tb[LWTUNNEL_IP_DST])
tun_info->key.u.ipv4.dst = nla_get_be32(tb[LWTUNNEL_IP_DST]);
@@ -266,7 +266,7 @@ static int ip_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info->key.tos = nla_get_u8(tb[LWTUNNEL_IP_TOS]);
if (tb[LWTUNNEL_IP_FLAGS])
- tun_info->key.tun_flags = nla_get_u16(tb[LWTUNNEL_IP_FLAGS]);
+ tun_info->key.tun_flags = nla_get_be16(tb[LWTUNNEL_IP_FLAGS]);
tun_info->mode = IP_TUNNEL_INFO_TX;
tun_info->options_len = 0;
@@ -281,12 +281,12 @@ static int ip_tun_fill_encap_info(struct sk_buff *skb,
{
struct ip_tunnel_info *tun_info = lwt_tun_info(lwtstate);
- if (nla_put_u64(skb, LWTUNNEL_IP_ID, tun_info->key.tun_id) ||
+ if (nla_put_be64(skb, LWTUNNEL_IP_ID, tun_info->key.tun_id) ||
nla_put_be32(skb, LWTUNNEL_IP_DST, tun_info->key.u.ipv4.dst) ||
nla_put_be32(skb, LWTUNNEL_IP_SRC, tun_info->key.u.ipv4.src) ||
nla_put_u8(skb, LWTUNNEL_IP_TOS, tun_info->key.tos) ||
nla_put_u8(skb, LWTUNNEL_IP_TTL, tun_info->key.ttl) ||
- nla_put_u16(skb, LWTUNNEL_IP_FLAGS, tun_info->key.tun_flags))
+ nla_put_be16(skb, LWTUNNEL_IP_FLAGS, tun_info->key.tun_flags))
return -ENOMEM;
return 0;
@@ -346,7 +346,7 @@ static int ip6_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info = lwt_tun_info(new_state);
if (tb[LWTUNNEL_IP6_ID])
- tun_info->key.tun_id = nla_get_u64(tb[LWTUNNEL_IP6_ID]);
+ tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP6_ID]);
if (tb[LWTUNNEL_IP6_DST])
tun_info->key.u.ipv6.dst = nla_get_in6_addr(tb[LWTUNNEL_IP6_DST]);
@@ -361,7 +361,7 @@ static int ip6_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info->key.tos = nla_get_u8(tb[LWTUNNEL_IP6_TC]);
if (tb[LWTUNNEL_IP6_FLAGS])
- tun_info->key.tun_flags = nla_get_u16(tb[LWTUNNEL_IP6_FLAGS]);
+ tun_info->key.tun_flags = nla_get_be16(tb[LWTUNNEL_IP6_FLAGS]);
tun_info->mode = IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_IPV6;
tun_info->options_len = 0;
@@ -376,12 +376,12 @@ static int ip6_tun_fill_encap_info(struct sk_buff *skb,
{
struct ip_tunnel_info *tun_info = lwt_tun_info(lwtstate);
- if (nla_put_u64(skb, LWTUNNEL_IP6_ID, tun_info->key.tun_id) ||
+ if (nla_put_be64(skb, LWTUNNEL_IP6_ID, tun_info->key.tun_id) ||
nla_put_in6_addr(skb, LWTUNNEL_IP6_DST, &tun_info->key.u.ipv6.dst) ||
nla_put_in6_addr(skb, LWTUNNEL_IP6_SRC, &tun_info->key.u.ipv6.src) ||
nla_put_u8(skb, LWTUNNEL_IP6_HOPLIMIT, tun_info->key.tos) ||
nla_put_u8(skb, LWTUNNEL_IP6_TC, tun_info->key.ttl) ||
- nla_put_u16(skb, LWTUNNEL_IP6_FLAGS, tun_info->key.tun_flags))
+ nla_put_be16(skb, LWTUNNEL_IP6_FLAGS, tun_info->key.tun_flags))
return -ENOMEM;
return 0;
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 4d8f0b698777..5cf10b777b7e 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -30,7 +30,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_ether.h>
@@ -200,7 +199,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
err = dst_output(tunnel->net, skb->sk, skb);
if (net_xmit_eval(err) == 0)
err = skb->len;
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+ iptunnel_xmit_stats(dev, err);
return NETDEV_TX_OK;
tx_error_icmp:
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 0bc7412d9e14..67f7c9de0b16 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -65,15 +65,6 @@
#include <net/checksum.h>
#include <asm/processor.h>
-/* Define this to allow debugging output */
-#undef IPCONFIG_DEBUG
-
-#ifdef IPCONFIG_DEBUG
-#define DBG(x) printk x
-#else
-#define DBG(x) do { } while(0)
-#endif
-
#if defined(CONFIG_IP_PNP_DHCP)
#define IPCONFIG_DHCP
#endif
@@ -227,7 +218,7 @@ static int __init ic_open_devs(void)
if (dev->mtu >= 364)
able |= IC_BOOTP;
else
- pr_warn("DHCP/BOOTP: Ignoring device %s, MTU %d too small",
+ pr_warn("DHCP/BOOTP: Ignoring device %s, MTU %d too small\n",
dev->name, dev->mtu);
if (!(dev->flags & IFF_NOARP))
able |= IC_RARP;
@@ -254,8 +245,8 @@ static int __init ic_open_devs(void)
else
d->xid = 0;
ic_proto_have_if |= able;
- DBG(("IP-Config: %s UP (able=%d, xid=%08x)\n",
- dev->name, able, d->xid));
+ pr_debug("IP-Config: %s UP (able=%d, xid=%08x)\n",
+ dev->name, able, d->xid);
}
}
@@ -311,7 +302,7 @@ static void __init ic_close_devs(void)
next = d->next;
dev = d->dev;
if (dev != ic_dev && !netdev_uses_dsa(dev)) {
- DBG(("IP-Config: Downing %s\n", dev->name));
+ pr_debug("IP-Config: Downing %s\n", dev->name);
dev_change_flags(dev, d->flags);
}
kfree(d);
@@ -464,7 +455,8 @@ static int __init ic_defaults(void)
&ic_myaddr);
return -1;
}
- printk("IP-Config: Guessing netmask %pI4\n", &ic_netmask);
+ pr_notice("IP-Config: Guessing netmask %pI4\n",
+ &ic_netmask);
}
return 0;
@@ -675,9 +667,7 @@ ic_dhcp_init_options(u8 *options)
u8 *e = options;
int len;
-#ifdef IPCONFIG_DEBUG
- printk("DHCP: Sending message type %d\n", mt);
-#endif
+ pr_debug("DHCP: Sending message type %d\n", mt);
memcpy(e, ic_bootp_cookie, 4); /* RFC1048 Magic Cookie */
e += 4;
@@ -847,7 +837,8 @@ static void __init ic_bootp_send_if(struct ic_device *d, unsigned long jiffies_d
else if (dev->type == ARPHRD_FDDI)
b->htype = ARPHRD_ETHER;
else {
- printk("Unknown ARP type 0x%04x for device %s\n", dev->type, dev->name);
+ pr_warn("Unknown ARP type 0x%04x for device %s\n", dev->type,
+ dev->name);
b->htype = dev->type; /* can cause undefined behavior */
}
@@ -904,14 +895,12 @@ static void __init ic_do_bootp_ext(u8 *ext)
int i;
__be16 mtu;
-#ifdef IPCONFIG_DEBUG
u8 *c;
- printk("DHCP/BOOTP: Got extension %d:",*ext);
+ pr_debug("DHCP/BOOTP: Got extension %d:", *ext);
for (c=ext+2; c<ext+2+ext[1]; c++)
- printk(" %02x", *c);
- printk("\n");
-#endif
+ pr_debug(" %02x", *c);
+ pr_debug("\n");
switch (*ext++) {
case 1: /* Subnet mask */
@@ -1080,9 +1069,7 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
}
}
-#ifdef IPCONFIG_DEBUG
- printk("DHCP: Got message type %d\n", mt);
-#endif
+ pr_debug("DHCP: Got message type %d\n", mt);
switch (mt) {
case DHCPOFFER:
@@ -1095,10 +1082,8 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
/* Let's accept that offer. */
ic_myaddr = b->your_ip;
ic_servaddr = server_id;
-#ifdef IPCONFIG_DEBUG
- printk("DHCP: Offered address %pI4 by server %pI4\n",
- &ic_myaddr, &b->iph.saddr);
-#endif
+ pr_debug("DHCP: Offered address %pI4 by server %pI4\n",
+ &ic_myaddr, &b->iph.saddr);
/* The DHCP indicated server address takes
* precedence over the bootp header one if
* they are different.
@@ -1295,11 +1280,10 @@ static int __init ic_dynamic(void)
return -1;
}
- printk("IP-Config: Got %s answer from %pI4, ",
+ pr_info("IP-Config: Got %s answer from %pI4, my address is %pI4\n",
((ic_got_reply & IC_RARP) ? "RARP"
- : (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
- &ic_addrservaddr);
- pr_cont("my address is %pI4\n", &ic_myaddr);
+ : (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
+ &ic_addrservaddr, &ic_myaddr);
return 0;
}
@@ -1426,7 +1410,7 @@ static int __init ip_auto_config(void)
if (!ic_enable)
return 0;
- DBG(("IP-Config: Entered.\n"));
+ pr_debug("IP-Config: Entered.\n");
#ifdef IPCONFIG_DYNAMIC
try_try_again:
#endif
@@ -1542,7 +1526,7 @@ static int __init ip_auto_config(void)
pr_cont(", mtu=%d", ic_dev_mtu);
for (i = 0; i < CONF_NAMESERVERS_MAX; i++)
if (ic_nameservers[i] != NONE) {
- pr_info(" nameserver%u=%pI4",
+ pr_cont(" nameserver%u=%pI4",
i, &ic_nameservers[i]);
break;
}
@@ -1585,7 +1569,7 @@ static int __init ic_proto_name(char *name)
return 1;
*v = 0;
if (kstrtou8(client_id, 0, dhcp_client_identifier))
- DBG("DHCP: Invalid client identifier type\n");
+ pr_debug("DHCP: Invalid client identifier type\n");
strncpy(dhcp_client_identifier + 1, v + 1, 251);
*v = ',';
}
@@ -1644,7 +1628,7 @@ static int __init ip_auto_config_setup(char *addrs)
if ((cp = strchr(ip, ':')))
*cp++ = '\0';
if (strlen(ip) > 0) {
- DBG(("IP-Config: Parameter #%d: `%s'\n", num, ip));
+ pr_debug("IP-Config: Parameter #%d: `%s'\n", num, ip);
switch (num) {
case 0:
if ((ic_myaddr = in_aton(ip)) == ANY)
@@ -1716,7 +1700,7 @@ static int __init vendor_class_identifier_setup(char *addrs)
if (strlcpy(vendor_class_identifier, addrs,
sizeof(vendor_class_identifier))
>= sizeof(vendor_class_identifier))
- pr_warn("DHCP: vendorclass too long, truncated to \"%s\"",
+ pr_warn("DHCP: vendorclass too long, truncated to \"%s\"\n",
vendor_class_identifier);
return 1;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index a09fb0dec725..4044da61e747 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -103,7 +103,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_ether.h>
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index c3a38353f5dc..395e2814a46d 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -66,28 +66,7 @@
#include <net/netlink.h>
#include <net/fib_rules.h>
#include <linux/netconf.h>
-
-#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
-#define CONFIG_IP_PIMSM 1
-#endif
-
-struct mr_table {
- struct list_head list;
- possible_net_t net;
- u32 id;
- struct sock __rcu *mroute_sk;
- struct timer_list ipmr_expire_timer;
- struct list_head mfc_unres_queue;
- struct list_head mfc_cache_array[MFC_LINES];
- struct vif_device vif_table[MAXVIFS];
- int maxvif;
- atomic_t cache_resolve_queue_len;
- bool mroute_do_assert;
- bool mroute_do_pim;
-#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
- int mroute_reg_vif_num;
-#endif
-};
+#include <net/nexthop.h>
struct ipmr_rule {
struct fib_rule common;
@@ -103,11 +82,7 @@ struct ipmr_result {
static DEFINE_RWLOCK(mrt_lock);
-/*
- * Multicast router control variables
- */
-
-#define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
+/* Multicast router control variables */
/* Special spinlock for queue of unresolved entries */
static DEFINE_SPINLOCK(mfc_unres_lock);
@@ -252,8 +227,8 @@ static int __net_init ipmr_rules_init(struct net *net)
INIT_LIST_HEAD(&net->ipv4.mr_tables);
mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
- if (!mrt) {
- err = -ENOMEM;
+ if (IS_ERR(mrt)) {
+ err = PTR_ERR(mrt);
goto err1;
}
@@ -301,8 +276,13 @@ static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
static int __net_init ipmr_rules_init(struct net *net)
{
- net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
- return net->ipv4.mrt ? 0 : -ENOMEM;
+ struct mr_table *mrt;
+
+ mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
+ if (IS_ERR(mrt))
+ return PTR_ERR(mrt);
+ net->ipv4.mrt = mrt;
+ return 0;
}
static void __net_exit ipmr_rules_exit(struct net *net)
@@ -319,13 +299,17 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
struct mr_table *mrt;
unsigned int i;
+ /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */
+ if (id != RT_TABLE_DEFAULT && id >= 1000000000)
+ return ERR_PTR(-EINVAL);
+
mrt = ipmr_get_table(net, id);
if (mrt)
return mrt;
mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
if (!mrt)
- return NULL;
+ return ERR_PTR(-ENOMEM);
write_pnet(&mrt->net, net);
mrt->id = id;
@@ -338,9 +322,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process,
(unsigned long)mrt);
-#ifdef CONFIG_IP_PIMSM
mrt->mroute_reg_vif_num = -1;
-#endif
#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables);
#endif
@@ -387,8 +369,24 @@ static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
}
}
-static
-struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
+/* Initialize ipmr pimreg/tunnel in_device */
+static bool ipmr_init_vif_indev(const struct net_device *dev)
+{
+ struct in_device *in_dev;
+
+ ASSERT_RTNL();
+
+ in_dev = __in_dev_get_rtnl(dev);
+ if (!in_dev)
+ return false;
+ ipv4_devconf_setall(in_dev);
+ neigh_parms_data_state_setall(in_dev->arp_parms);
+ IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
+
+ return true;
+}
+
+static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
{
struct net_device *dev;
@@ -399,7 +397,6 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
int err;
struct ifreq ifr;
struct ip_tunnel_parm p;
- struct in_device *in_dev;
memset(&p, 0, sizeof(p));
p.iph.daddr = v->vifc_rmt_addr.s_addr;
@@ -424,15 +421,8 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
if (err == 0 &&
(dev = __dev_get_by_name(net, p.name)) != NULL) {
dev->flags |= IFF_MULTICAST;
-
- in_dev = __in_dev_get_rtnl(dev);
- if (!in_dev)
+ if (!ipmr_init_vif_indev(dev))
goto failure;
-
- ipv4_devconf_setall(in_dev);
- neigh_parms_data_state_setall(in_dev->arp_parms);
- IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
-
if (dev_open(dev))
goto failure;
dev_hold(dev);
@@ -445,8 +435,7 @@ failure:
return NULL;
}
-#ifdef CONFIG_IP_PIMSM
-
+#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net *net = dev_net(dev);
@@ -496,7 +485,6 @@ static void reg_vif_setup(struct net_device *dev)
static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
{
struct net_device *dev;
- struct in_device *in_dev;
char name[IFNAMSIZ];
if (mrt->id == RT_TABLE_DEFAULT)
@@ -516,18 +504,8 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
return NULL;
}
- rcu_read_lock();
- in_dev = __in_dev_get_rcu(dev);
- if (!in_dev) {
- rcu_read_unlock();
+ if (!ipmr_init_vif_indev(dev))
goto failure;
- }
-
- ipv4_devconf_setall(in_dev);
- neigh_parms_data_state_setall(in_dev->arp_parms);
- IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
- rcu_read_unlock();
-
if (dev_open(dev))
goto failure;
@@ -539,13 +517,56 @@ failure:
unregister_netdevice(dev);
return NULL;
}
+
+/* called with rcu_read_lock() */
+static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
+ unsigned int pimlen)
+{
+ struct net_device *reg_dev = NULL;
+ struct iphdr *encap;
+
+ encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
+ /* Check that:
+ * a. packet is really sent to a multicast group
+ * b. packet is not a NULL-REGISTER
+ * c. packet is not truncated
+ */
+ if (!ipv4_is_multicast(encap->daddr) ||
+ encap->tot_len == 0 ||
+ ntohs(encap->tot_len) + pimlen > skb->len)
+ return 1;
+
+ read_lock(&mrt_lock);
+ if (mrt->mroute_reg_vif_num >= 0)
+ reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
+ read_unlock(&mrt_lock);
+
+ if (!reg_dev)
+ return 1;
+
+ skb->mac_header = skb->network_header;
+ skb_pull(skb, (u8 *)encap - skb->data);
+ skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IP);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
+
+ netif_rx(skb);
+
+ return NET_RX_SUCCESS;
+}
+#else
+static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
+{
+ return NULL;
+}
#endif
/**
* vif_delete - Delete a VIF entry
* @notify: Set to 1, if the caller is a notifier_call
*/
-
static int vif_delete(struct mr_table *mrt, int vifi, int notify,
struct list_head *head)
{
@@ -567,10 +588,8 @@ static int vif_delete(struct mr_table *mrt, int vifi, int notify,
return -EADDRNOTAVAIL;
}
-#ifdef CONFIG_IP_PIMSM
if (vifi == mrt->mroute_reg_vif_num)
mrt->mroute_reg_vif_num = -1;
-#endif
if (vifi + 1 == mrt->maxvif) {
int tmp;
@@ -617,7 +636,6 @@ static inline void ipmr_cache_free(struct mfc_cache *c)
/* Destroy an unresolved cache entry, killing queued skbs
* and reporting error to netlink readers.
*/
-
static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
{
struct net *net = read_pnet(&mrt->net);
@@ -645,9 +663,7 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
ipmr_cache_free(c);
}
-
/* Timer process for the unresolved queue. */
-
static void ipmr_expire_process(unsigned long arg)
{
struct mr_table *mrt = (struct mr_table *)arg;
@@ -687,7 +703,6 @@ out:
}
/* Fill oifs list. It is called under write locked mrt_lock. */
-
static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
unsigned char *ttls)
{
@@ -723,10 +738,10 @@ static int vif_add(struct net *net, struct mr_table *mrt,
return -EADDRINUSE;
switch (vifc->vifc_flags) {
-#ifdef CONFIG_IP_PIMSM
case VIFF_REGISTER:
- /*
- * Special Purpose VIF in PIM
+ if (!ipmr_pimsm_enabled())
+ return -EINVAL;
+ /* Special Purpose VIF in PIM
* All the packets will be sent to the daemon
*/
if (mrt->mroute_reg_vif_num >= 0)
@@ -741,7 +756,6 @@ static int vif_add(struct net *net, struct mr_table *mrt,
return err;
}
break;
-#endif
case VIFF_TUNNEL:
dev = ipmr_new_tunnel(net, vifc);
if (!dev)
@@ -753,7 +767,6 @@ static int vif_add(struct net *net, struct mr_table *mrt,
return err;
}
break;
-
case VIFF_USE_IFINDEX:
case 0:
if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
@@ -807,10 +820,8 @@ static int vif_add(struct net *net, struct mr_table *mrt,
/* And finish update writing critical data */
write_lock_bh(&mrt_lock);
v->dev = dev;
-#ifdef CONFIG_IP_PIMSM
if (v->flags & VIFF_REGISTER)
mrt->mroute_reg_vif_num = vifi;
-#endif
if (vifi+1 > mrt->maxvif)
mrt->maxvif = vifi+1;
write_unlock_bh(&mrt_lock);
@@ -875,9 +886,7 @@ skip:
return ipmr_cache_find_any_parent(mrt, vifi);
}
-/*
- * Allocate a multicast cache entry
- */
+/* Allocate a multicast cache entry */
static struct mfc_cache *ipmr_cache_alloc(void)
{
struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
@@ -898,10 +907,7 @@ static struct mfc_cache *ipmr_cache_alloc_unres(void)
return c;
}
-/*
- * A cache entry has gone into a resolved state from queued
- */
-
+/* A cache entry has gone into a resolved state from queued */
static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
struct mfc_cache *uc, struct mfc_cache *c)
{
@@ -909,7 +915,6 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
struct nlmsgerr *e;
/* Play the pending entries through our router */
-
while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
@@ -933,34 +938,29 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
}
}
-/*
- * Bounce a cache query up to mrouted. We could use netlink for this but mrouted
- * expects the following bizarre scheme.
+/* Bounce a cache query up to mrouted. We could use netlink for this but mrouted
+ * expects the following bizarre scheme.
*
- * Called under mrt_lock.
+ * Called under mrt_lock.
*/
-
static int ipmr_cache_report(struct mr_table *mrt,
struct sk_buff *pkt, vifi_t vifi, int assert)
{
- struct sk_buff *skb;
const int ihl = ip_hdrlen(pkt);
+ struct sock *mroute_sk;
struct igmphdr *igmp;
struct igmpmsg *msg;
- struct sock *mroute_sk;
+ struct sk_buff *skb;
int ret;
-#ifdef CONFIG_IP_PIMSM
if (assert == IGMPMSG_WHOLEPKT)
skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
else
-#endif
skb = alloc_skb(128, GFP_ATOMIC);
if (!skb)
return -ENOBUFS;
-#ifdef CONFIG_IP_PIMSM
if (assert == IGMPMSG_WHOLEPKT) {
/* Ugly, but we have no choice with this interface.
* Duplicate old header, fix ihl, length etc.
@@ -978,28 +978,23 @@ static int ipmr_cache_report(struct mr_table *mrt,
ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
sizeof(struct iphdr));
- } else
-#endif
- {
-
- /* Copy the IP header */
-
- skb_set_network_header(skb, skb->len);
- skb_put(skb, ihl);
- skb_copy_to_linear_data(skb, pkt->data, ihl);
- ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
- msg = (struct igmpmsg *)skb_network_header(skb);
- msg->im_vif = vifi;
- skb_dst_set(skb, dst_clone(skb_dst(pkt)));
-
- /* Add our header */
-
- igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
- igmp->type =
- msg->im_msgtype = assert;
- igmp->code = 0;
- ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
- skb->transport_header = skb->network_header;
+ } else {
+ /* Copy the IP header */
+ skb_set_network_header(skb, skb->len);
+ skb_put(skb, ihl);
+ skb_copy_to_linear_data(skb, pkt->data, ihl);
+ /* Flag to the kernel this is a route add */
+ ip_hdr(skb)->protocol = 0;
+ msg = (struct igmpmsg *)skb_network_header(skb);
+ msg->im_vif = vifi;
+ skb_dst_set(skb, dst_clone(skb_dst(pkt)));
+ /* Add our header */
+ igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
+ igmp->type = assert;
+ msg->im_msgtype = assert;
+ igmp->code = 0;
+ ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
+ skb->transport_header = skb->network_header;
}
rcu_read_lock();
@@ -1011,7 +1006,6 @@ static int ipmr_cache_report(struct mr_table *mrt,
}
/* Deliver to mrouted */
-
ret = sock_queue_rcv_skb(mroute_sk, skb);
rcu_read_unlock();
if (ret < 0) {
@@ -1022,12 +1016,9 @@ static int ipmr_cache_report(struct mr_table *mrt,
return ret;
}
-/*
- * Queue a packet for resolution. It gets locked cache entry!
- */
-
-static int
-ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
+/* Queue a packet for resolution. It gets locked cache entry! */
+static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
+ struct sk_buff *skb)
{
bool found = false;
int err;
@@ -1045,7 +1036,6 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
if (!found) {
/* Create a new entry if allowable */
-
if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
(c = ipmr_cache_alloc_unres()) == NULL) {
spin_unlock_bh(&mfc_unres_lock);
@@ -1055,13 +1045,11 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
}
/* Fill in the new cache entry */
-
c->mfc_parent = -1;
c->mfc_origin = iph->saddr;
c->mfc_mcastgrp = iph->daddr;
/* Reflect first query at mrouted. */
-
err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
if (err < 0) {
/* If the report failed throw the cache entry
@@ -1083,7 +1071,6 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
}
/* See if we can append the packet */
-
if (c->mfc_un.unres.unresolved.qlen > 3) {
kfree_skb(skb);
err = -ENOBUFS;
@@ -1096,9 +1083,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
return err;
}
-/*
- * MFC cache manipulation by user space mroute daemon
- */
+/* MFC cache manipulation by user space mroute daemon */
static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
{
@@ -1169,9 +1154,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
list_add_rcu(&c->list, &mrt->mfc_cache_array[line]);
- /*
- * Check to see if we resolved a queued list. If so we
- * need to send on the frames and tidy up.
+ /* Check to see if we resolved a queued list. If so we
+ * need to send on the frames and tidy up.
*/
found = false;
spin_lock_bh(&mfc_unres_lock);
@@ -1196,10 +1180,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
return 0;
}
-/*
- * Close the multicast socket, and clear the vif tables etc
- */
-
+/* Close the multicast socket, and clear the vif tables etc */
static void mroute_clean_tables(struct mr_table *mrt, bool all)
{
int i;
@@ -1207,7 +1188,6 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
struct mfc_cache *c, *next;
/* Shut down all active vif entries */
-
for (i = 0; i < mrt->maxvif; i++) {
if (!all && (mrt->vif_table[i].flags & VIFF_STATIC))
continue;
@@ -1216,7 +1196,6 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
unregister_netdevice_many(&list);
/* Wipe the cache */
-
for (i = 0; i < MFC_LINES; i++) {
list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
if (!all && (c->mfc_flags & MFC_STATIC))
@@ -1260,44 +1239,52 @@ static void mrtsock_destruct(struct sock *sk)
rtnl_unlock();
}
-/*
- * Socket options and virtual interface manipulation. The whole
- * virtual interface system is a complete heap, but unfortunately
- * that's how BSD mrouted happens to think. Maybe one day with a proper
- * MOSPF/PIM router set up we can clean this up.
+/* Socket options and virtual interface manipulation. The whole
+ * virtual interface system is a complete heap, but unfortunately
+ * that's how BSD mrouted happens to think. Maybe one day with a proper
+ * MOSPF/PIM router set up we can clean this up.
*/
-int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
+int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval,
+ unsigned int optlen)
{
- int ret, parent = 0;
- struct vifctl vif;
- struct mfcctl mfc;
struct net *net = sock_net(sk);
+ int val, ret = 0, parent = 0;
struct mr_table *mrt;
+ struct vifctl vif;
+ struct mfcctl mfc;
+ u32 uval;
+ /* There's one exception to the lock - MRT_DONE which needs to unlock */
+ rtnl_lock();
if (sk->sk_type != SOCK_RAW ||
- inet_sk(sk)->inet_num != IPPROTO_IGMP)
- return -EOPNOTSUPP;
+ inet_sk(sk)->inet_num != IPPROTO_IGMP) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
- if (!mrt)
- return -ENOENT;
-
+ if (!mrt) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
if (optname != MRT_INIT) {
if (sk != rcu_access_pointer(mrt->mroute_sk) &&
- !ns_capable(net->user_ns, CAP_NET_ADMIN))
- return -EACCES;
+ !ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+ ret = -EACCES;
+ goto out_unlock;
+ }
}
switch (optname) {
case MRT_INIT:
- if (optlen != sizeof(int))
- return -EINVAL;
-
- rtnl_lock();
+ if (optlen != sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
if (rtnl_dereference(mrt->mroute_sk)) {
- rtnl_unlock();
- return -EADDRINUSE;
+ ret = -EADDRINUSE;
+ break;
}
ret = ip_ra_control(sk, 1, mrtsock_destruct);
@@ -1308,129 +1295,133 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
NETCONFA_IFINDEX_ALL,
net->ipv4.devconf_all);
}
- rtnl_unlock();
- return ret;
+ break;
case MRT_DONE:
- if (sk != rcu_access_pointer(mrt->mroute_sk))
- return -EACCES;
- return ip_ra_control(sk, 0, NULL);
+ if (sk != rcu_access_pointer(mrt->mroute_sk)) {
+ ret = -EACCES;
+ } else {
+ /* We need to unlock here because mrtsock_destruct takes
+ * care of rtnl itself and we can't change that due to
+ * the IP_ROUTER_ALERT setsockopt which runs without it.
+ */
+ rtnl_unlock();
+ ret = ip_ra_control(sk, 0, NULL);
+ goto out;
+ }
+ break;
case MRT_ADD_VIF:
case MRT_DEL_VIF:
- if (optlen != sizeof(vif))
- return -EINVAL;
- if (copy_from_user(&vif, optval, sizeof(vif)))
- return -EFAULT;
- if (vif.vifc_vifi >= MAXVIFS)
- return -ENFILE;
- rtnl_lock();
+ if (optlen != sizeof(vif)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&vif, optval, sizeof(vif))) {
+ ret = -EFAULT;
+ break;
+ }
+ if (vif.vifc_vifi >= MAXVIFS) {
+ ret = -ENFILE;
+ break;
+ }
if (optname == MRT_ADD_VIF) {
ret = vif_add(net, mrt, &vif,
sk == rtnl_dereference(mrt->mroute_sk));
} else {
ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
}
- rtnl_unlock();
- return ret;
-
- /*
- * Manipulate the forwarding caches. These live
- * in a sort of kernel/user symbiosis.
- */
+ break;
+ /* Manipulate the forwarding caches. These live
+ * in a sort of kernel/user symbiosis.
+ */
case MRT_ADD_MFC:
case MRT_DEL_MFC:
parent = -1;
case MRT_ADD_MFC_PROXY:
case MRT_DEL_MFC_PROXY:
- if (optlen != sizeof(mfc))
- return -EINVAL;
- if (copy_from_user(&mfc, optval, sizeof(mfc)))
- return -EFAULT;
+ if (optlen != sizeof(mfc)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&mfc, optval, sizeof(mfc))) {
+ ret = -EFAULT;
+ break;
+ }
if (parent == 0)
parent = mfc.mfcc_parent;
- rtnl_lock();
if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
ret = ipmr_mfc_delete(mrt, &mfc, parent);
else
ret = ipmr_mfc_add(net, mrt, &mfc,
sk == rtnl_dereference(mrt->mroute_sk),
parent);
- rtnl_unlock();
- return ret;
- /*
- * Control PIM assert.
- */
+ break;
+ /* Control PIM assert. */
case MRT_ASSERT:
- {
- int v;
- if (optlen != sizeof(v))
- return -EINVAL;
- if (get_user(v, (int __user *)optval))
- return -EFAULT;
- mrt->mroute_do_assert = v;
- return 0;
- }
-#ifdef CONFIG_IP_PIMSM
+ if (optlen != sizeof(val)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(val, (int __user *)optval)) {
+ ret = -EFAULT;
+ break;
+ }
+ mrt->mroute_do_assert = val;
+ break;
case MRT_PIM:
- {
- int v;
-
- if (optlen != sizeof(v))
- return -EINVAL;
- if (get_user(v, (int __user *)optval))
- return -EFAULT;
- v = !!v;
+ if (!ipmr_pimsm_enabled()) {
+ ret = -ENOPROTOOPT;
+ break;
+ }
+ if (optlen != sizeof(val)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(val, (int __user *)optval)) {
+ ret = -EFAULT;
+ break;
+ }
- rtnl_lock();
- ret = 0;
- if (v != mrt->mroute_do_pim) {
- mrt->mroute_do_pim = v;
- mrt->mroute_do_assert = v;
+ val = !!val;
+ if (val != mrt->mroute_do_pim) {
+ mrt->mroute_do_pim = val;
+ mrt->mroute_do_assert = val;
}
- rtnl_unlock();
- return ret;
- }
-#endif
-#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
+ break;
case MRT_TABLE:
- {
- u32 v;
-
- if (optlen != sizeof(u32))
- return -EINVAL;
- if (get_user(v, (u32 __user *)optval))
- return -EFAULT;
-
- /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */
- if (v != RT_TABLE_DEFAULT && v >= 1000000000)
- return -EINVAL;
+ if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) {
+ ret = -ENOPROTOOPT;
+ break;
+ }
+ if (optlen != sizeof(uval)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(uval, (u32 __user *)optval)) {
+ ret = -EFAULT;
+ break;
+ }
- rtnl_lock();
- ret = 0;
if (sk == rtnl_dereference(mrt->mroute_sk)) {
ret = -EBUSY;
} else {
- if (!ipmr_new_table(net, v))
- ret = -ENOMEM;
+ mrt = ipmr_new_table(net, uval);
+ if (IS_ERR(mrt))
+ ret = PTR_ERR(mrt);
else
- raw_sk(sk)->ipmr_table = v;
+ raw_sk(sk)->ipmr_table = uval;
}
- rtnl_unlock();
- return ret;
- }
-#endif
- /*
- * Spurious command, or MRT_VERSION which you cannot
- * set.
- */
+ break;
+ /* Spurious command, or MRT_VERSION which you cannot set. */
default:
- return -ENOPROTOOPT;
+ ret = -ENOPROTOOPT;
}
+out_unlock:
+ rtnl_unlock();
+out:
+ return ret;
}
-/*
- * Getsock opt support for the multicast routing system.
- */
-
+/* Getsock opt support for the multicast routing system. */
int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
{
int olr;
@@ -1446,39 +1437,35 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int
if (!mrt)
return -ENOENT;
- if (optname != MRT_VERSION &&
-#ifdef CONFIG_IP_PIMSM
- optname != MRT_PIM &&
-#endif
- optname != MRT_ASSERT)
+ switch (optname) {
+ case MRT_VERSION:
+ val = 0x0305;
+ break;
+ case MRT_PIM:
+ if (!ipmr_pimsm_enabled())
+ return -ENOPROTOOPT;
+ val = mrt->mroute_do_pim;
+ break;
+ case MRT_ASSERT:
+ val = mrt->mroute_do_assert;
+ break;
+ default:
return -ENOPROTOOPT;
+ }
if (get_user(olr, optlen))
return -EFAULT;
-
olr = min_t(unsigned int, olr, sizeof(int));
if (olr < 0)
return -EINVAL;
-
if (put_user(olr, optlen))
return -EFAULT;
- if (optname == MRT_VERSION)
- val = 0x0305;
-#ifdef CONFIG_IP_PIMSM
- else if (optname == MRT_PIM)
- val = mrt->mroute_do_pim;
-#endif
- else
- val = mrt->mroute_do_assert;
if (copy_to_user(optval, &val, olr))
return -EFAULT;
return 0;
}
-/*
- * The IP multicast ioctl support routines.
- */
-
+/* The IP multicast ioctl support routines. */
int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
{
struct sioc_sg_req sr;
@@ -1611,7 +1598,6 @@ int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
}
#endif
-
static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
@@ -1633,17 +1619,14 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v
return NOTIFY_DONE;
}
-
static struct notifier_block ip_mr_notifier = {
.notifier_call = ipmr_device_event,
};
-/*
- * Encapsulate a packet by attaching a valid IPIP header to it.
- * This avoids tunnel drivers and other mess and gives us the speed so
- * important for multicast video.
+/* Encapsulate a packet by attaching a valid IPIP header to it.
+ * This avoids tunnel drivers and other mess and gives us the speed so
+ * important for multicast video.
*/
-
static void ip_encap(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr)
{
@@ -1685,9 +1668,7 @@ static inline int ipmr_forward_finish(struct net *net, struct sock *sk,
return dst_output(net, sk, skb);
}
-/*
- * Processing handlers for ipmr_forward
- */
+/* Processing handlers for ipmr_forward */
static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
struct sk_buff *skb, struct mfc_cache *c, int vifi)
@@ -1702,7 +1683,6 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
if (!vif->dev)
goto out_free;
-#ifdef CONFIG_IP_PIMSM
if (vif->flags & VIFF_REGISTER) {
vif->pkt_out++;
vif->bytes_out += skb->len;
@@ -1711,7 +1691,6 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
goto out_free;
}
-#endif
if (vif->flags & VIFF_TUNNEL) {
rt = ip_route_output_ports(net, &fl4, NULL,
@@ -1738,7 +1717,6 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
* allow to send ICMP, so that packets will disappear
* to blackhole.
*/
-
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
ip_rt_put(rt);
goto out_free;
@@ -1770,8 +1748,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
IPCB(skb)->flags |= IPSKB_FORWARDED;
- /*
- * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
+ /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
* not only before forwarding, but after forwarding on all output
* interfaces. It is clear, if mrouter runs a multicasting
* program, it should receive packets not depending to what interface
@@ -1802,7 +1779,6 @@ static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
}
/* "local" means that we should preserve one skb (for local delivery) */
-
static void ip_mr_forward(struct net *net, struct mr_table *mrt,
struct sk_buff *skb, struct mfc_cache *cache,
int local)
@@ -1827,9 +1803,7 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
goto forward;
}
- /*
- * Wrong interface: drop packet and (maybe) send PIM assert.
- */
+ /* Wrong interface: drop packet and (maybe) send PIM assert. */
if (mrt->vif_table[vif].dev != skb->dev) {
if (rt_is_output_route(skb_rtable(skb))) {
/* It is our own packet, looped back.
@@ -1868,9 +1842,7 @@ forward:
mrt->vif_table[vif].pkt_in++;
mrt->vif_table[vif].bytes_in += skb->len;
- /*
- * Forward the frame
- */
+ /* Forward the frame */
if (cache->mfc_origin == htonl(INADDR_ANY) &&
cache->mfc_mcastgrp == htonl(INADDR_ANY)) {
if (true_vifi >= 0 &&
@@ -1944,11 +1916,9 @@ static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb)
return mrt;
}
-/*
- * Multicast packets for forwarding arrive here
- * Called with rcu_read_lock();
+/* Multicast packets for forwarding arrive here
+ * Called with rcu_read_lock();
*/
-
int ip_mr_input(struct sk_buff *skb)
{
struct mfc_cache *cache;
@@ -1999,9 +1969,7 @@ int ip_mr_input(struct sk_buff *skb)
vif);
}
- /*
- * No usable cache entry
- */
+ /* No usable cache entry */
if (!cache) {
int vif;
@@ -2042,53 +2010,8 @@ dont_forward:
return 0;
}
-#ifdef CONFIG_IP_PIMSM
-/* called with rcu_read_lock() */
-static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
- unsigned int pimlen)
-{
- struct net_device *reg_dev = NULL;
- struct iphdr *encap;
-
- encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
- /*
- * Check that:
- * a. packet is really sent to a multicast group
- * b. packet is not a NULL-REGISTER
- * c. packet is not truncated
- */
- if (!ipv4_is_multicast(encap->daddr) ||
- encap->tot_len == 0 ||
- ntohs(encap->tot_len) + pimlen > skb->len)
- return 1;
-
- read_lock(&mrt_lock);
- if (mrt->mroute_reg_vif_num >= 0)
- reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
- read_unlock(&mrt_lock);
-
- if (!reg_dev)
- return 1;
-
- skb->mac_header = skb->network_header;
- skb_pull(skb, (u8 *)encap - skb->data);
- skb_reset_network_header(skb);
- skb->protocol = htons(ETH_P_IP);
- skb->ip_summed = CHECKSUM_NONE;
-
- skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
-
- netif_rx(skb);
-
- return NET_RX_SUCCESS;
-}
-#endif
-
#ifdef CONFIG_IP_PIMSM_V1
-/*
- * Handle IGMP messages of PIMv1
- */
-
+/* Handle IGMP messages of PIMv1 */
int pim_rcv_v1(struct sk_buff *skb)
{
struct igmphdr *pim;
@@ -2249,8 +2172,6 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
}
read_lock(&mrt_lock);
- if (!nowait && (rtm->rtm_flags & RTM_F_NOTIFY))
- cache->mfc_flags |= MFC_NOTIFY;
err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
read_unlock(&mrt_lock);
rcu_read_unlock();
@@ -2412,10 +2333,133 @@ done:
return skb->len;
}
+static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = {
+ [RTA_SRC] = { .type = NLA_U32 },
+ [RTA_DST] = { .type = NLA_U32 },
+ [RTA_IIF] = { .type = NLA_U32 },
+ [RTA_TABLE] = { .type = NLA_U32 },
+ [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
+};
+
+static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol)
+{
+ switch (rtm_protocol) {
+ case RTPROT_STATIC:
+ case RTPROT_MROUTED:
+ return true;
+ }
+ return false;
+}
+
+static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc)
+{
+ struct rtnexthop *rtnh = nla_data(nla);
+ int remaining = nla_len(nla), vifi = 0;
+
+ while (rtnh_ok(rtnh, remaining)) {
+ mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops;
+ if (++vifi == MAXVIFS)
+ break;
+ rtnh = rtnh_next(rtnh, &remaining);
+ }
+
+ return remaining > 0 ? -EINVAL : vifi;
+}
+
+/* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */
+static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh,
+ struct mfcctl *mfcc, int *mrtsock,
+ struct mr_table **mrtret)
+{
+ struct net_device *dev = NULL;
+ u32 tblid = RT_TABLE_DEFAULT;
+ struct mr_table *mrt;
+ struct nlattr *attr;
+ struct rtmsg *rtm;
+ int ret, rem;
+
+ ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy);
+ if (ret < 0)
+ goto out;
+ rtm = nlmsg_data(nlh);
+
+ ret = -EINVAL;
+ if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 ||
+ rtm->rtm_type != RTN_MULTICAST ||
+ rtm->rtm_scope != RT_SCOPE_UNIVERSE ||
+ !ipmr_rtm_validate_proto(rtm->rtm_protocol))
+ goto out;
+
+ memset(mfcc, 0, sizeof(*mfcc));
+ mfcc->mfcc_parent = -1;
+ ret = 0;
+ nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) {
+ switch (nla_type(attr)) {
+ case RTA_SRC:
+ mfcc->mfcc_origin.s_addr = nla_get_be32(attr);
+ break;
+ case RTA_DST:
+ mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr);
+ break;
+ case RTA_IIF:
+ dev = __dev_get_by_index(net, nla_get_u32(attr));
+ if (!dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+ break;
+ case RTA_MULTIPATH:
+ if (ipmr_nla_get_ttls(attr, mfcc) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ break;
+ case RTA_PREFSRC:
+ ret = 1;
+ break;
+ case RTA_TABLE:
+ tblid = nla_get_u32(attr);
+ break;
+ }
+ }
+ mrt = ipmr_get_table(net, tblid);
+ if (!mrt) {
+ ret = -ENOENT;
+ goto out;
+ }
+ *mrtret = mrt;
+ *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0;
+ if (dev)
+ mfcc->mfcc_parent = ipmr_find_vif(mrt, dev);
+
+out:
+ return ret;
+}
+
+/* takes care of both newroute and delroute */
+static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ struct net *net = sock_net(skb->sk);
+ int ret, mrtsock, parent;
+ struct mr_table *tbl;
+ struct mfcctl mfcc;
+
+ mrtsock = 0;
+ tbl = NULL;
+ ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl);
+ if (ret < 0)
+ return ret;
+
+ parent = ret ? mfcc.mfcc_parent : -1;
+ if (nlh->nlmsg_type == RTM_NEWROUTE)
+ return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent);
+ else
+ return ipmr_mfc_delete(tbl, &mfcc, parent);
+}
+
#ifdef CONFIG_PROC_FS
-/*
- * The /proc interfaces to multicast routing :
- * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif
+/* The /proc interfaces to multicast routing :
+ * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif
*/
struct ipmr_vif_iter {
struct seq_net_private p;
@@ -2699,10 +2743,7 @@ static const struct net_protocol pim_protocol = {
};
#endif
-
-/*
- * Setup for IP multicast routing
- */
+/* Setup for IP multicast routing */
static int __net_init ipmr_net_init(struct net *net)
{
int err;
@@ -2752,8 +2793,6 @@ int __init ip_mr_init(void)
sizeof(struct mfc_cache),
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
NULL);
- if (!mrt_cachep)
- return -ENOMEM;
err = register_pernet_subsys(&ipmr_net_ops);
if (err)
@@ -2771,6 +2810,10 @@ int __init ip_mr_init(void)
#endif
rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE,
NULL, ipmr_rtm_dumproute, NULL);
+ rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE,
+ ipmr_rtm_route, NULL, NULL);
+ rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE,
+ ipmr_rtm_route, NULL, NULL);
return 0;
#ifdef CONFIG_IP_PIMSM_V2
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 11dccba474b7..b488cac9c5ca 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -38,13 +38,13 @@ MODULE_DESCRIPTION("arptables core");
/*#define DEBUG_ARP_TABLES_USER*/
#ifdef DEBUG_ARP_TABLES
-#define dprintf(format, args...) printk(format , ## args)
+#define dprintf(format, args...) pr_debug(format, ## args)
#else
#define dprintf(format, args...)
#endif
#ifdef DEBUG_ARP_TABLES_USER
-#define duprintf(format, args...) printk(format , ## args)
+#define duprintf(format, args...) pr_debug(format, ## args)
#else
#define duprintf(format, args...)
#endif
@@ -1905,7 +1905,7 @@ static int __init arp_tables_init(void)
if (ret < 0)
goto err4;
- printk(KERN_INFO "arp_tables: (C) 2002 David S. Miller\n");
+ pr_info("arp_tables: (C) 2002 David S. Miller\n");
return 0;
err4:
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 461ca926fd39..e3c46e8e2762 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -451,7 +451,7 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
ret = nf_register_sockopt(&so_getorigdst);
if (ret < 0) {
- printk(KERN_ERR "Unable to register netfilter socket option\n");
+ pr_err("Unable to register netfilter socket option\n");
return ret;
}
diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
index 5075b7ecd26d..61c7cc22ea68 100644
--- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
@@ -132,7 +132,8 @@ static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
if (skb->ip_summed != CHECKSUM_PARTIAL) {
if (!(rt->rt_flags & RTCF_LOCAL) &&
- (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) {
+ (!skb->dev || skb->dev->features &
+ (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM))) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb) +
skb_network_offset(skb) +
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index ddb894ac1458..c9b52c361da2 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -1048,7 +1048,7 @@ static int snmp_parse_mangle(unsigned char *msg,
if (!asn1_uint_decode (&ctx, end, &vers))
return 0;
if (debug > 1)
- printk(KERN_DEBUG "bsalg: snmp version: %u\n", vers + 1);
+ pr_debug("bsalg: snmp version: %u\n", vers + 1);
if (vers > 1)
return 1;
@@ -1064,10 +1064,10 @@ static int snmp_parse_mangle(unsigned char *msg,
if (debug > 1) {
unsigned int i;
- printk(KERN_DEBUG "bsalg: community: ");
+ pr_debug("bsalg: community: ");
for (i = 0; i < comm.len; i++)
- printk("%c", comm.data[i]);
- printk("\n");
+ pr_cont("%c", comm.data[i]);
+ pr_cont("\n");
}
kfree(comm.data);
@@ -1091,9 +1091,9 @@ static int snmp_parse_mangle(unsigned char *msg,
};
if (pdutype > SNMP_PDU_TRAP2)
- printk(KERN_DEBUG "bsalg: bad pdu type %u\n", pdutype);
+ pr_debug("bsalg: bad pdu type %u\n", pdutype);
else
- printk(KERN_DEBUG "bsalg: pdu: %s\n", pdus[pdutype]);
+ pr_debug("bsalg: pdu: %s\n", pdus[pdutype]);
}
if (pdutype != SNMP_PDU_RESPONSE &&
pdutype != SNMP_PDU_TRAP1 && pdutype != SNMP_PDU_TRAP2)
@@ -1119,7 +1119,7 @@ static int snmp_parse_mangle(unsigned char *msg,
return 0;
if (debug > 1)
- printk(KERN_DEBUG "bsalg: request: id=0x%lx error_status=%u "
+ pr_debug("bsalg: request: id=0x%lx error_status=%u "
"error_index=%u\n", req.id, req.error_status,
req.error_index);
}
@@ -1145,13 +1145,13 @@ static int snmp_parse_mangle(unsigned char *msg,
}
if (debug > 1) {
- printk(KERN_DEBUG "bsalg: object: ");
+ pr_debug("bsalg: object: ");
for (i = 0; i < obj->id_len; i++) {
if (i > 0)
- printk(".");
- printk("%lu", obj->id[i]);
+ pr_cont(".");
+ pr_cont("%lu", obj->id[i]);
}
- printk(": type=%u\n", obj->type);
+ pr_cont(": type=%u\n", obj->type);
}
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index c747b2d9eb77..b6ea57ec5e14 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -14,7 +14,6 @@
#include <net/netfilter/ipv4/nf_reject.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
-#include <net/netfilter/ipv4/nf_reject.h>
const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *_oth, int hook)
diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c
index 9d09d4f59545..cd84d4295a20 100644
--- a/net/ipv4/netfilter/nf_tables_arp.c
+++ b/net/ipv4/netfilter/nf_tables_arp.c
@@ -57,7 +57,7 @@ err:
static void nf_tables_arp_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.arp);
+ nft_unregister_afinfo(net, net->nft.arp);
kfree(net->nft.arp);
}
diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c
index ca9dc3c46c4f..e44ba3b12fbb 100644
--- a/net/ipv4/netfilter/nf_tables_ipv4.c
+++ b/net/ipv4/netfilter/nf_tables_ipv4.c
@@ -78,7 +78,7 @@ err:
static void nf_tables_ipv4_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.ipv4);
+ nft_unregister_afinfo(net, net->nft.ipv4);
kfree(net->nft.ipv4);
}
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index e89094ab5ddb..c117b21b937d 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -1063,6 +1063,7 @@ static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos)
}
void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family)
+ __acquires(ping_table.lock)
{
struct ping_iter_state *state = seq->private;
state->bucket = 0;
@@ -1094,6 +1095,7 @@ void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos)
EXPORT_SYMBOL_GPL(ping_seq_next);
void ping_seq_stop(struct seq_file *seq, void *v)
+ __releases(ping_table.lock)
{
read_unlock_bh(&ping_table.lock);
}
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 4cbe9f0a4281..643a86c49020 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -351,7 +351,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
treq->snt_synack.v64 = 0;
treq->tfo_listener = false;
- ireq->ir_iif = sk->sk_bound_dev_if;
+ ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
/* We throwed the options of the initial SYN away, so we hope
* the ACK carries the same options again (see RFC1122 4.2.3.8)
@@ -371,7 +371,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
* hasn't changed since we received the original syn, but I see
* no easy way to do this.
*/
- flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark,
+ flowi4_init_output(&fl4, ireq->ir_iif, ireq->ir_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
inet_sk_flowi_flags(sk),
opt->srr ? opt->faddr : ireq->ir_rmt_addr,
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index a0bd7a55193e..46ce410703b1 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -337,27 +337,6 @@ static struct ctl_table ipv4_table[] = {
.proc_handler = proc_dointvec
},
{
- .procname = "tcp_keepalive_time",
- .data = &sysctl_tcp_keepalive_time,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- {
- .procname = "tcp_keepalive_probes",
- .data = &sysctl_tcp_keepalive_probes,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
- {
- .procname = "tcp_keepalive_intvl",
- .data = &sysctl_tcp_keepalive_intvl,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- {
.procname = "tcp_retries1",
.data = &sysctl_tcp_retries1,
.maxlen = sizeof(int),
@@ -915,6 +894,17 @@ static struct ctl_table ipv4_net_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
+#ifdef CONFIG_NET_L3_MASTER_DEV
+ {
+ .procname = "tcp_l3mdev_accept",
+ .data = &init_net.ipv4.sysctl_tcp_l3mdev_accept,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+#endif
{
.procname = "tcp_mtu_probing",
.data = &init_net.ipv4.sysctl_tcp_mtu_probing,
@@ -950,6 +940,27 @@ static struct ctl_table ipv4_net_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec
},
+ {
+ .procname = "tcp_keepalive_time",
+ .data = &init_net.ipv4.sysctl_tcp_keepalive_time,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ {
+ .procname = "tcp_keepalive_probes",
+ .data = &init_net.ipv4.sysctl_tcp_keepalive_probes,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .procname = "tcp_keepalive_intvl",
+ .data = &init_net.ipv4.sysctl_tcp_keepalive_intvl,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
{ }
};
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c82cca18c90f..7bb1b091efd1 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1018,7 +1018,7 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset,
ssize_t res;
if (!(sk->sk_route_caps & NETIF_F_SG) ||
- !(sk->sk_route_caps & NETIF_F_ALL_CSUM))
+ !sk_check_csum_caps(sk))
return sock_no_sendpage(sk->sk_socket, page, offset, size,
flags);
@@ -1175,7 +1175,7 @@ new_segment:
/*
* Check whether we can use HW checksum.
*/
- if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
+ if (sk_check_csum_caps(sk))
skb->ip_summed = CHECKSUM_PARTIAL;
skb_entail(sk, skb);
@@ -3080,6 +3080,52 @@ void tcp_done(struct sock *sk)
}
EXPORT_SYMBOL_GPL(tcp_done);
+int tcp_abort(struct sock *sk, int err)
+{
+ if (!sk_fullsock(sk)) {
+ if (sk->sk_state == TCP_NEW_SYN_RECV) {
+ struct request_sock *req = inet_reqsk(sk);
+
+ local_bh_disable();
+ inet_csk_reqsk_queue_drop_and_put(req->rsk_listener,
+ req);
+ local_bh_enable();
+ return 0;
+ }
+ sock_gen_put(sk);
+ return -EOPNOTSUPP;
+ }
+
+ /* Don't race with userspace socket closes such as tcp_close. */
+ lock_sock(sk);
+
+ if (sk->sk_state == TCP_LISTEN) {
+ tcp_set_state(sk, TCP_CLOSE);
+ inet_csk_listen_stop(sk);
+ }
+
+ /* Don't race with BH socket closes such as inet_csk_listen_stop. */
+ local_bh_disable();
+ bh_lock_sock(sk);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ sk->sk_err = err;
+ /* This barrier is coupled with smp_rmb() in tcp_poll() */
+ smp_wmb();
+ sk->sk_error_report(sk);
+ if (tcp_need_reset(sk->sk_state))
+ tcp_send_active_reset(sk, GFP_ATOMIC);
+ tcp_done(sk);
+ }
+
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ release_sock(sk);
+ sock_put(sk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tcp_abort);
+
extern struct tcp_congestion_ops tcp_reno;
static __initdata unsigned long thash_entries;
diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c
index b31604086edd..4d610934fb39 100644
--- a/net/ipv4/tcp_diag.c
+++ b/net/ipv4/tcp_diag.c
@@ -10,6 +10,8 @@
*/
#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
#include <linux/tcp.h>
@@ -46,12 +48,29 @@ static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req);
}
+#ifdef CONFIG_INET_DIAG_DESTROY
+static int tcp_diag_destroy(struct sk_buff *in_skb,
+ const struct inet_diag_req_v2 *req)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct sock *sk = inet_diag_find_one_icsk(net, &tcp_hashinfo, req);
+
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
+
+ return sock_diag_destroy(sk, ECONNABORTED);
+}
+#endif
+
static const struct inet_diag_handler tcp_diag_handler = {
.dump = tcp_diag_dump,
.dump_one = tcp_diag_dump_one,
.idiag_get_info = tcp_diag_get_info,
.idiag_type = IPPROTO_TCP,
.idiag_info_size = sizeof(struct tcp_info),
+#ifdef CONFIG_INET_DIAG_DESTROY
+ .destroy = tcp_diag_destroy,
+#endif
};
static int __init tcp_diag_init(void)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index d4c51158470f..0003d409fec5 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6207,7 +6207,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
tcp_openreq_init(req, &tmp_opt, skb, sk);
/* Note: tcp_v6_init_req() might override ir_iif for link locals */
- inet_rsk(req)->ir_iif = sk->sk_bound_dev_if;
+ inet_rsk(req)->ir_iif = inet_request_bound_dev_if(sk, skb);
af_ops->init_req(req, sk, skb);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index d8841a2f1569..65947c1f4733 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -587,7 +587,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
} rep;
struct ip_reply_arg arg;
#ifdef CONFIG_TCP_MD5SIG
- struct tcp_md5sig_key *key;
+ struct tcp_md5sig_key *key = NULL;
const __u8 *hash_location = NULL;
unsigned char newhash[16];
int genhash;
@@ -627,7 +627,10 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
#ifdef CONFIG_TCP_MD5SIG
hash_location = tcp_parse_md5sig_option(th);
- if (!sk && hash_location) {
+ if (sk && sk_fullsock(sk)) {
+ key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)
+ &ip_hdr(skb)->saddr, AF_INET);
+ } else if (hash_location) {
/*
* active side is lost. Try to find listening socket through
* source port, and then find md5 key through listening socket.
@@ -651,10 +654,6 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
if (genhash || memcmp(hash_location, newhash, 16) != 0)
goto release_sk1;
- } else {
- key = sk ? tcp_md5_do_lookup(sk, (union tcp_md5_addr *)
- &ip_hdr(skb)->saddr,
- AF_INET) : NULL;
}
if (key) {
@@ -675,7 +674,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
ip_hdr(skb)->saddr, /* XXX */
arg.iov[0].iov_len, IPPROTO_TCP, 0);
arg.csumoffset = offsetof(struct tcphdr, check) / 2;
- arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0;
+ arg.flags = (sk && inet_sk_transparent(sk)) ? IP_REPLY_ARG_NOSRCCHECK : 0;
+
/* When socket is gone, all binding information is lost.
* routing might fail in this case. No choice here, if we choose to force
* input interface, we will misroute in case of asymmetric route.
@@ -683,6 +683,9 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
if (sk)
arg.bound_dev_if = sk->sk_bound_dev_if;
+ BUILD_BUG_ON(offsetof(struct sock, sk_bound_dev_if) !=
+ offsetof(struct inet_timewait_sock, tw_bound_dev_if));
+
arg.tos = ip_hdr(skb)->tos;
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
@@ -1276,6 +1279,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
ireq = inet_rsk(req);
sk_daddr_set(newsk, ireq->ir_rmt_addr);
sk_rcv_saddr_set(newsk, ireq->ir_loc_addr);
+ newsk->sk_bound_dev_if = ireq->ir_iif;
newinet->inet_saddr = ireq->ir_loc_addr;
inet_opt = ireq->opt;
rcu_assign_pointer(newinet->inet_opt, inet_opt);
@@ -1705,7 +1709,9 @@ do_time_wait:
tcp_v4_timewait_ack(sk, skb);
break;
case TCP_TW_RST:
- goto no_tcp_socket;
+ tcp_v4_send_reset(sk, skb);
+ inet_twsk_deschedule_put(inet_twsk(sk));
+ goto discard_it;
case TCP_TW_SUCCESS:;
}
goto discard_it;
@@ -2341,6 +2347,7 @@ struct proto tcp_prot = {
.destroy_cgroup = tcp_destroy_cgroup,
.proto_cgroup = tcp_proto_cgroup,
#endif
+ .diag_destroy = tcp_abort,
};
EXPORT_SYMBOL(tcp_prot);
@@ -2378,6 +2385,10 @@ static int __net_init tcp_sk_init(struct net *net)
net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD;
net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL;
+ net->ipv4.sysctl_tcp_keepalive_time = TCP_KEEPALIVE_TIME;
+ net->ipv4.sysctl_tcp_keepalive_probes = TCP_KEEPALIVE_PROBES;
+ net->ipv4.sysctl_tcp_keepalive_intvl = TCP_KEEPALIVE_INTVL;
+
return 0;
fail:
tcp_sk_exit(net);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ac6b1961ffeb..75632a925824 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -131,7 +131,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
goto kill;
if (th->syn && !before(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt))
- goto kill_with_rst;
+ return TCP_TW_RST;
/* Dup ACK? */
if (!th->ack ||
@@ -145,11 +145,8 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
* reset.
*/
if (!th->fin ||
- TCP_SKB_CB(skb)->end_seq != tcptw->tw_rcv_nxt + 1) {
-kill_with_rst:
- inet_twsk_deschedule_put(tw);
+ TCP_SKB_CB(skb)->end_seq != tcptw->tw_rcv_nxt + 1)
return TCP_TW_RST;
- }
/* FIN arrived, enter true time-wait state. */
tw->tw_substate = TCP_TIME_WAIT;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 9bfc39ff2285..412a920fe0ec 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2296,7 +2296,7 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
return;
if (tcp_write_xmit(sk, cur_mss, nonagle, 0,
- sk_gfp_atomic(sk, GFP_ATOMIC)))
+ sk_gfp_mask(sk, GFP_ATOMIC)))
tcp_check_probe_timer(sk);
}
@@ -3353,8 +3353,9 @@ void tcp_send_ack(struct sock *sk)
* tcp_transmit_skb() will set the ownership to this
* sock.
*/
- buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
- if (!buff) {
+ buff = alloc_skb(MAX_TCP_HEADER,
+ sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN));
+ if (unlikely(!buff)) {
inet_csk_schedule_ack(sk);
inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
@@ -3376,7 +3377,7 @@ void tcp_send_ack(struct sock *sk)
/* Send it off, this clears delayed acks for us. */
skb_mstamp_get(&buff->skb_mstamp);
- tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
+ tcp_transmit_skb(sk, buff, 0, (__force gfp_t)0);
}
EXPORT_SYMBOL_GPL(tcp_send_ack);
@@ -3397,7 +3398,8 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent, int mib)
struct sk_buff *skb;
/* We don't queue it, tcp_transmit_skb() sets ownership. */
- skb = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
+ skb = alloc_skb(MAX_TCP_HEADER,
+ sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN));
if (!skb)
return -1;
@@ -3410,7 +3412,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent, int mib)
tcp_init_nondata_skb(skb, tp->snd_una - !urgent, TCPHDR_ACK);
skb_mstamp_get(&skb->skb_mstamp);
NET_INC_STATS(sock_net(sk), mib);
- return tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC);
+ return tcp_transmit_skb(sk, skb, 0, (__force gfp_t)0);
}
void tcp_send_window_probe(struct sock *sk)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 193ba1fa8a9a..a4730a28b220 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -24,9 +24,6 @@
int sysctl_tcp_syn_retries __read_mostly = TCP_SYN_RETRIES;
int sysctl_tcp_synack_retries __read_mostly = TCP_SYNACK_RETRIES;
-int sysctl_tcp_keepalive_time __read_mostly = TCP_KEEPALIVE_TIME;
-int sysctl_tcp_keepalive_probes __read_mostly = TCP_KEEPALIVE_PROBES;
-int sysctl_tcp_keepalive_intvl __read_mostly = TCP_KEEPALIVE_INTVL;
int sysctl_tcp_retries1 __read_mostly = TCP_RETR1;
int sysctl_tcp_retries2 __read_mostly = TCP_RETR2;
int sysctl_tcp_orphan_retries __read_mostly;
diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c
index 17d35662930d..3e6a472e6b88 100644
--- a/net/ipv4/tcp_yeah.c
+++ b/net/ipv4/tcp_yeah.c
@@ -219,7 +219,7 @@ static u32 tcp_yeah_ssthresh(struct sock *sk)
yeah->fast_count = 0;
yeah->reno_count = max(yeah->reno_count>>1, 2U);
- return tp->snd_cwnd - reduction;
+ return max_t(int, tp->snd_cwnd - reduction, 2);
}
static struct tcp_congestion_ops tcp_yeah __read_mostly = {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index c43890848641..dc45b538e237 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -113,6 +113,7 @@
#include <trace/events/skb.h>
#include <net/busy_poll.h>
#include "udp_impl.h"
+#include <net/sock_reuseport.h>
struct udp_table udp_table __read_mostly;
EXPORT_SYMBOL(udp_table);
@@ -137,7 +138,8 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
unsigned long *bitmap,
struct sock *sk,
int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2),
+ const struct sock *sk2,
+ bool match_wildcard),
unsigned int log)
{
struct sock *sk2;
@@ -152,8 +154,9 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
(!sk2->sk_reuseport || !sk->sk_reuseport ||
+ rcu_access_pointer(sk->sk_reuseport_cb) ||
!uid_eq(uid, sock_i_uid(sk2))) &&
- saddr_comp(sk, sk2)) {
+ saddr_comp(sk, sk2, true)) {
if (!bitmap)
return 1;
__set_bit(udp_sk(sk2)->udp_port_hash >> log, bitmap);
@@ -170,7 +173,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
struct udp_hslot *hslot2,
struct sock *sk,
int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2))
+ const struct sock *sk2,
+ bool match_wildcard))
{
struct sock *sk2;
struct hlist_nulls_node *node;
@@ -186,8 +190,9 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
(!sk2->sk_reuseport || !sk->sk_reuseport ||
+ rcu_access_pointer(sk->sk_reuseport_cb) ||
!uid_eq(uid, sock_i_uid(sk2))) &&
- saddr_comp(sk, sk2)) {
+ saddr_comp(sk, sk2, true)) {
res = 1;
break;
}
@@ -196,6 +201,35 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
return res;
}
+static int udp_reuseport_add_sock(struct sock *sk, struct udp_hslot *hslot,
+ int (*saddr_same)(const struct sock *sk1,
+ const struct sock *sk2,
+ bool match_wildcard))
+{
+ struct net *net = sock_net(sk);
+ struct hlist_nulls_node *node;
+ kuid_t uid = sock_i_uid(sk);
+ struct sock *sk2;
+
+ sk_nulls_for_each(sk2, node, &hslot->head) {
+ if (net_eq(sock_net(sk2), net) &&
+ sk2 != sk &&
+ sk2->sk_family == sk->sk_family &&
+ ipv6_only_sock(sk2) == ipv6_only_sock(sk) &&
+ (udp_sk(sk2)->udp_port_hash == udp_sk(sk)->udp_port_hash) &&
+ (sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+ sk2->sk_reuseport && uid_eq(uid, sock_i_uid(sk2)) &&
+ (*saddr_same)(sk, sk2, false)) {
+ return reuseport_add_sock(sk, sk2);
+ }
+ }
+
+ /* Initial allocation may have already happened via setsockopt */
+ if (!rcu_access_pointer(sk->sk_reuseport_cb))
+ return reuseport_alloc(sk);
+ return 0;
+}
+
/**
* udp_lib_get_port - UDP/-Lite port lookup for IPv4 and IPv6
*
@@ -207,7 +241,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
*/
int udp_lib_get_port(struct sock *sk, unsigned short snum,
int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2),
+ const struct sock *sk2,
+ bool match_wildcard),
unsigned int hash2_nulladdr)
{
struct udp_hslot *hslot, *hslot2;
@@ -290,6 +325,14 @@ found:
udp_sk(sk)->udp_port_hash = snum;
udp_sk(sk)->udp_portaddr_hash ^= snum;
if (sk_unhashed(sk)) {
+ if (sk->sk_reuseport &&
+ udp_reuseport_add_sock(sk, hslot, saddr_comp)) {
+ inet_sk(sk)->inet_num = 0;
+ udp_sk(sk)->udp_port_hash = 0;
+ udp_sk(sk)->udp_portaddr_hash ^= snum;
+ goto fail_unlock;
+ }
+
sk_nulls_add_node_rcu(sk, &hslot->head);
hslot->count++;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
@@ -309,13 +352,22 @@ fail:
}
EXPORT_SYMBOL(udp_lib_get_port);
-static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
+/* match_wildcard == true: 0.0.0.0 equals to any IPv4 addresses
+ * match_wildcard == false: addresses must be exactly the same, i.e.
+ * 0.0.0.0 only equals to 0.0.0.0
+ */
+static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2,
+ bool match_wildcard)
{
struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2);
- return (!ipv6_only_sock(sk2) &&
- (!inet1->inet_rcv_saddr || !inet2->inet_rcv_saddr ||
- inet1->inet_rcv_saddr == inet2->inet_rcv_saddr));
+ if (!ipv6_only_sock(sk2)) {
+ if (inet1->inet_rcv_saddr == inet2->inet_rcv_saddr)
+ return 1;
+ if (!inet1->inet_rcv_saddr || !inet2->inet_rcv_saddr)
+ return match_wildcard;
+ }
+ return 0;
}
static u32 udp4_portaddr_hash(const struct net *net, __be32 saddr,
@@ -441,7 +493,8 @@ static u32 udp_ehashfn(const struct net *net, const __be32 laddr,
static struct sock *udp4_lib_lookup2(struct net *net,
__be32 saddr, __be16 sport,
__be32 daddr, unsigned int hnum, int dif,
- struct udp_hslot *hslot2, unsigned int slot2)
+ struct udp_hslot *hslot2, unsigned int slot2,
+ struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -459,8 +512,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -478,6 +538,7 @@ begin:
if (get_nulls_value(node) != slot2)
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score2(result, net, saddr, sport,
@@ -494,7 +555,7 @@ begin:
*/
struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
__be16 sport, __be32 daddr, __be16 dport,
- int dif, struct udp_table *udptable)
+ int dif, struct udp_table *udptable, struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -514,7 +575,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
result = udp4_lib_lookup2(net, saddr, sport,
daddr, hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
if (!result) {
hash2 = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum);
slot2 = hash2 & udptable->mask;
@@ -524,7 +585,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
result = udp4_lib_lookup2(net, saddr, sport,
htonl(INADDR_ANY), hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
}
rcu_read_unlock();
return result;
@@ -540,8 +601,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -560,6 +628,7 @@ begin:
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score(result, net, saddr, hnum, sport,
@@ -581,13 +650,14 @@ static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb,
return __udp4_lib_lookup(dev_net(skb_dst(skb)->dev), iph->saddr, sport,
iph->daddr, dport, inet_iif(skb),
- udptable);
+ udptable, skb);
}
struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif)
{
- return __udp4_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table);
+ return __udp4_lib_lookup(net, saddr, sport, daddr, dport, dif,
+ &udp_table, NULL);
}
EXPORT_SYMBOL_GPL(udp4_lib_lookup);
@@ -635,7 +705,8 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
struct net *net = dev_net(skb->dev);
sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
- iph->saddr, uh->source, skb->dev->ifindex, udptable);
+ iph->saddr, uh->source, skb->dev->ifindex, udptable,
+ NULL);
if (!sk) {
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
return; /* No socket for error */
@@ -772,7 +843,8 @@ void udp_set_csum(bool nocheck, struct sk_buff *skb,
else if (skb_is_gso(skb))
uh->check = ~udp_v4_check(len, saddr, daddr, 0);
else if (skb_dst(skb) && skb_dst(skb)->dev &&
- (skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
+ (skb_dst(skb)->dev->features &
+ (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM))) {
BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
@@ -1273,6 +1345,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
int peeked, off = 0;
int err;
int is_udplite = IS_UDPLITE(sk);
+ bool checksum_valid = false;
bool slow;
if (flags & MSG_ERRQUEUE)
@@ -1298,11 +1371,12 @@ try_again:
*/
if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
- if (udp_lib_checksum_complete(skb))
+ checksum_valid = !udp_lib_checksum_complete(skb);
+ if (!checksum_valid)
goto csum_copy_err;
}
- if (skb_csum_unnecessary(skb))
+ if (checksum_valid || skb_csum_unnecessary(skb))
err = skb_copy_datagram_msg(skb, sizeof(struct udphdr),
msg, copied);
else {
@@ -1398,6 +1472,8 @@ void udp_lib_unhash(struct sock *sk)
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
spin_lock_bh(&hslot->lock);
+ if (rcu_access_pointer(sk->sk_reuseport_cb))
+ reuseport_detach_sock(sk);
if (sk_nulls_del_node_init_rcu(sk)) {
hslot->count--;
inet_sk(sk)->inet_num = 0;
@@ -1425,22 +1501,28 @@ void udp_lib_rehash(struct sock *sk, u16 newhash)
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
nhslot2 = udp_hashslot2(udptable, newhash);
udp_sk(sk)->udp_portaddr_hash = newhash;
- if (hslot2 != nhslot2) {
+
+ if (hslot2 != nhslot2 ||
+ rcu_access_pointer(sk->sk_reuseport_cb)) {
hslot = udp_hashslot(udptable, sock_net(sk),
udp_sk(sk)->udp_port_hash);
/* we must lock primary chain too */
spin_lock_bh(&hslot->lock);
-
- spin_lock(&hslot2->lock);
- hlist_nulls_del_init_rcu(&udp_sk(sk)->udp_portaddr_node);
- hslot2->count--;
- spin_unlock(&hslot2->lock);
-
- spin_lock(&nhslot2->lock);
- hlist_nulls_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,
- &nhslot2->head);
- nhslot2->count++;
- spin_unlock(&nhslot2->lock);
+ if (rcu_access_pointer(sk->sk_reuseport_cb))
+ reuseport_detach_sock(sk);
+
+ if (hslot2 != nhslot2) {
+ spin_lock(&hslot2->lock);
+ hlist_nulls_del_init_rcu(&udp_sk(sk)->udp_portaddr_node);
+ hslot2->count--;
+ spin_unlock(&hslot2->lock);
+
+ spin_lock(&nhslot2->lock);
+ hlist_nulls_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,
+ &nhslot2->head);
+ nhslot2->count++;
+ spin_unlock(&nhslot2->lock);
+ }
spin_unlock_bh(&hslot->lock);
}
diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c
index 6116604bf6e8..df1966f3b6ec 100644
--- a/net/ipv4/udp_diag.c
+++ b/net/ipv4/udp_diag.c
@@ -44,7 +44,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
sk = __udp4_lib_lookup(net,
req->id.idiag_src[0], req->id.idiag_sport,
req->id.idiag_dst[0], req->id.idiag_dport,
- req->id.idiag_if, tbl);
+ req->id.idiag_if, tbl, NULL);
#if IS_ENABLED(CONFIG_IPV6)
else if (req->sdiag_family == AF_INET6)
sk = __udp6_lib_lookup(net,
@@ -52,7 +52,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
req->id.idiag_sport,
(struct in6_addr *)req->id.idiag_dst,
req->id.idiag_dport,
- req->id.idiag_if, tbl);
+ req->id.idiag_if, tbl, NULL);
#endif
else
goto out_nosk;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index f9386160cbee..4c519c1dc161 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -21,6 +21,7 @@ static struct udp_offload_priv __rcu *udp_offload_base __read_mostly;
struct udp_offload_priv {
struct udp_offload *offload;
+ possible_net_t net;
struct rcu_head rcu;
struct udp_offload_priv __rcu *next;
};
@@ -60,8 +61,9 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
/* Try to offload checksum if possible */
offload_csum = !!(need_csum &&
- (skb->dev->features &
- (is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM)));
+ ((skb->dev->features & NETIF_F_HW_CSUM) ||
+ (skb->dev->features & (is_ipv6 ?
+ NETIF_F_IPV6_CSUM : NETIF_F_IP_CSUM))));
/* segment inner packet. */
enc_features = skb->dev->hw_enc_features & features;
@@ -241,13 +243,14 @@ out:
return segs;
}
-int udp_add_offload(struct udp_offload *uo)
+int udp_add_offload(struct net *net, struct udp_offload *uo)
{
struct udp_offload_priv *new_offload = kzalloc(sizeof(*new_offload), GFP_ATOMIC);
if (!new_offload)
return -ENOMEM;
+ write_pnet(&new_offload->net, net);
new_offload->offload = uo;
spin_lock(&udp_offload_lock);
@@ -311,7 +314,8 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
rcu_read_lock();
uo_priv = rcu_dereference(udp_offload_base);
for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
- if (uo_priv->offload->port == uh->dest &&
+ if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
+ uo_priv->offload->port == uh->dest &&
uo_priv->offload->callbacks.gro_receive)
goto unflush;
}
@@ -389,7 +393,8 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff)
uo_priv = rcu_dereference(udp_offload_base);
for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
- if (uo_priv->offload->port == uh->dest &&
+ if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
+ uo_priv->offload->port == uh->dest &&
uo_priv->offload->callbacks.gro_complete)
break;
}
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index aba428626b52..0ec08814f37d 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -74,10 +74,10 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
}
EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
- __be32 src, __be32 dst, __u8 tos, __u8 ttl,
- __be16 df, __be16 src_port, __be16 dst_port,
- bool xnet, bool nocheck)
+void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
+ __be32 src, __be32 dst, __u8 tos, __u8 ttl,
+ __be16 df, __be16 src_port, __be16 dst_port,
+ bool xnet, bool nocheck)
{
struct udphdr *uh;
@@ -91,8 +91,7 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
udp_set_csum(nocheck, skb, src, dst, skb->len);
- return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,
- tos, ttl, df, xnet);
+ iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP, tos, ttl, df, xnet);
}
EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 983bb999738c..bb7dabe2ebbf 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -94,6 +94,7 @@ config IPV6_MIP6
config IPV6_ILA
tristate "IPv6: Identifier Locator Addressing (ILA)"
+ depends on NETFILTER
select LWTUNNEL
---help---
Support for IPv6 Identifier Locator Addressing (ILA).
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 2c900c7b7eb1..2fbd90bf8d33 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -34,7 +34,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o
obj-$(CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION) += xfrm6_mode_ro.o
obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
obj-$(CONFIG_IPV6_MIP6) += mip6.o
-obj-$(CONFIG_IPV6_ILA) += ila.o
+obj-$(CONFIG_IPV6_ILA) += ila/
obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_IPV6_VTI) += ip6_vti.o
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 1f21087accab..38eeddedfc21 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -70,7 +70,7 @@
#include <net/sock.h>
#include <net/snmp.h>
-#include <net/af_ieee802154.h>
+#include <net/6lowpan.h>
#include <net/firewire.h>
#include <net/ipv6.h>
#include <net/protocol.h>
@@ -1772,12 +1772,13 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
{
+ if (dad_failed)
+ ifp->flags |= IFA_F_DADFAILED;
+
if (ifp->flags&IFA_F_PERMANENT) {
spin_lock_bh(&ifp->lock);
addrconf_del_dad_work(ifp);
ifp->flags |= IFA_F_TENTATIVE;
- if (dad_failed)
- ifp->flags |= IFA_F_DADFAILED;
spin_unlock_bh(&ifp->lock);
if (dad_failed)
ipv6_ifa_notify(0, ifp);
@@ -1953,9 +1954,9 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
{
- if (dev->addr_len != IEEE802154_ADDR_LEN)
+ if (dev->addr_len != EUI64_ADDR_LEN)
return -1;
- memcpy(eui, dev->dev_addr, 8);
+ memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
eui[0] ^= 2;
return 0;
}
@@ -2047,7 +2048,6 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
case ARPHRD_IPGRE:
return addrconf_ifid_gre(eui, dev);
case ARPHRD_6LOWPAN:
- case ARPHRD_IEEE802154:
return addrconf_ifid_eui64(eui, dev);
case ARPHRD_IEEE1394:
return addrconf_ifid_ieee1394(eui, dev);
@@ -2320,6 +2320,12 @@ static void manage_tempaddrs(struct inet6_dev *idev,
}
}
+static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
+{
+ return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
+ idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
+}
+
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
struct prefix_info *pinfo;
@@ -2433,8 +2439,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
in6_dev->token.s6_addr + 8, 8);
read_unlock_bh(&in6_dev->lock);
tokenized = true;
- } else if (in6_dev->addr_gen_mode ==
- IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+ } else if (is_addr_mode_generate_stable(in6_dev) &&
!ipv6_generate_stable_address(&addr, 0,
in6_dev)) {
addr_flags |= IFA_F_STABLE_PRIVACY;
@@ -3034,6 +3039,17 @@ retry:
return 0;
}
+static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
+{
+ struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
+
+ if (s->initialized)
+ return;
+ s = &idev->cnf.stable_secret;
+ get_random_bytes(&s->secret, sizeof(s->secret));
+ s->initialized = true;
+}
+
static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
{
struct in6_addr addr;
@@ -3044,13 +3060,18 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
- if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+ switch (idev->addr_gen_mode) {
+ case IN6_ADDR_GEN_MODE_RANDOM:
+ ipv6_gen_mode_random_init(idev);
+ /* fallthrough */
+ case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
if (!ipv6_generate_stable_address(&addr, 0, idev))
addrconf_add_linklocal(idev, &addr,
IFA_F_STABLE_PRIVACY);
else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
- } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
+ break;
+ case IN6_ADDR_GEN_MODE_EUI64:
/* addrconf_add_linklocal also adds a prefix_route and we
* only need to care about prefix routes if ipv6_generate_eui64
* couldn't generate one.
@@ -3059,6 +3080,11 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
addrconf_add_linklocal(idev, &addr, 0);
else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
+ break;
+ case IN6_ADDR_GEN_MODE_NONE:
+ default:
+ /* will not add any link local address */
+ break;
}
}
@@ -3072,10 +3098,10 @@ static void addrconf_dev_config(struct net_device *dev)
(dev->type != ARPHRD_FDDI) &&
(dev->type != ARPHRD_ARCNET) &&
(dev->type != ARPHRD_INFINIBAND) &&
- (dev->type != ARPHRD_IEEE802154) &&
(dev->type != ARPHRD_IEEE1394) &&
(dev->type != ARPHRD_TUNNEL6) &&
- (dev->type != ARPHRD_6LOWPAN)) {
+ (dev->type != ARPHRD_6LOWPAN) &&
+ (dev->type != ARPHRD_NONE)) {
/* Alas, we support only Ethernet autoconfiguration. */
return;
}
@@ -3084,6 +3110,11 @@ static void addrconf_dev_config(struct net_device *dev)
if (IS_ERR(idev))
return;
+ /* this device type has no EUI support */
+ if (dev->type == ARPHRD_NONE &&
+ idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
+ idev->addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
+
addrconf_addr_gen(idev, false);
}
@@ -3293,7 +3324,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
case NETDEV_PRE_TYPE_CHANGE:
case NETDEV_POST_TYPE_CHANGE:
- addrconf_type_change(dev, event);
+ if (idev)
+ addrconf_type_change(dev, event);
break;
}
@@ -4927,7 +4959,8 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
mode != IN6_ADDR_GEN_MODE_NONE &&
- mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
+ mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+ mode != IN6_ADDR_GEN_MODE_RANDOM)
return -EINVAL;
if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
@@ -5206,6 +5239,20 @@ int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
}
static
+int addrconf_sysctl_hop_limit(struct ctl_table *ctl, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table lctl;
+ int min_hl = 1, max_hl = 255;
+
+ lctl = *ctl;
+ lctl.extra1 = &min_hl;
+ lctl.extra2 = &max_hl;
+
+ return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos);
+}
+
+static
int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
@@ -5457,7 +5504,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.hop_limit,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec,
+ .proc_handler = addrconf_sysctl_hop_limit,
},
{
.procname = "mtu",
diff --git a/net/ipv6/ila.c b/net/ipv6/ila.c
deleted file mode 100644
index 1a6852e1ac69..000000000000
--- a/net/ipv6/ila.c
+++ /dev/null
@@ -1,229 +0,0 @@
-#include <linux/errno.h>
-#include <linux/ip.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/skbuff.h>
-#include <linux/socket.h>
-#include <linux/types.h>
-#include <net/checksum.h>
-#include <net/ip.h>
-#include <net/ip6_fib.h>
-#include <net/lwtunnel.h>
-#include <net/protocol.h>
-#include <uapi/linux/ila.h>
-
-struct ila_params {
- __be64 locator;
- __be64 locator_match;
- __wsum csum_diff;
-};
-
-static inline struct ila_params *ila_params_lwtunnel(
- struct lwtunnel_state *lwstate)
-{
- return (struct ila_params *)lwstate->data;
-}
-
-static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
-{
- __be32 diff[] = {
- ~from[0], ~from[1], to[0], to[1],
- };
-
- return csum_partial(diff, sizeof(diff), 0);
-}
-
-static inline __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
-{
- if (*(__be64 *)&ip6h->daddr == p->locator_match)
- return p->csum_diff;
- else
- return compute_csum_diff8((__be32 *)&ip6h->daddr,
- (__be32 *)&p->locator);
-}
-
-static void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
-{
- __wsum diff;
- struct ipv6hdr *ip6h = ipv6_hdr(skb);
- size_t nhoff = sizeof(struct ipv6hdr);
-
- /* First update checksum */
- switch (ip6h->nexthdr) {
- case NEXTHDR_TCP:
- if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
- struct tcphdr *th = (struct tcphdr *)
- (skb_network_header(skb) + nhoff);
-
- diff = get_csum_diff(ip6h, p);
- inet_proto_csum_replace_by_diff(&th->check, skb,
- diff, true);
- }
- break;
- case NEXTHDR_UDP:
- if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
- struct udphdr *uh = (struct udphdr *)
- (skb_network_header(skb) + nhoff);
-
- if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
- diff = get_csum_diff(ip6h, p);
- inet_proto_csum_replace_by_diff(&uh->check, skb,
- diff, true);
- if (!uh->check)
- uh->check = CSUM_MANGLED_0;
- }
- }
- break;
- case NEXTHDR_ICMP:
- if (likely(pskb_may_pull(skb,
- nhoff + sizeof(struct icmp6hdr)))) {
- struct icmp6hdr *ih = (struct icmp6hdr *)
- (skb_network_header(skb) + nhoff);
-
- diff = get_csum_diff(ip6h, p);
- inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
- diff, true);
- }
- break;
- }
-
- /* Now change destination address */
- *(__be64 *)&ip6h->daddr = p->locator;
-}
-
-static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
-{
- struct dst_entry *dst = skb_dst(skb);
-
- if (skb->protocol != htons(ETH_P_IPV6))
- goto drop;
-
- update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
-
- return dst->lwtstate->orig_output(net, sk, skb);
-
-drop:
- kfree_skb(skb);
- return -EINVAL;
-}
-
-static int ila_input(struct sk_buff *skb)
-{
- struct dst_entry *dst = skb_dst(skb);
-
- if (skb->protocol != htons(ETH_P_IPV6))
- goto drop;
-
- update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
-
- return dst->lwtstate->orig_input(skb);
-
-drop:
- kfree_skb(skb);
- return -EINVAL;
-}
-
-static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
- [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
-};
-
-static int ila_build_state(struct net_device *dev, struct nlattr *nla,
- unsigned int family, const void *cfg,
- struct lwtunnel_state **ts)
-{
- struct ila_params *p;
- struct nlattr *tb[ILA_ATTR_MAX + 1];
- size_t encap_len = sizeof(*p);
- struct lwtunnel_state *newts;
- const struct fib6_config *cfg6 = cfg;
- int ret;
-
- if (family != AF_INET6)
- return -EINVAL;
-
- ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla,
- ila_nl_policy);
- if (ret < 0)
- return ret;
-
- if (!tb[ILA_ATTR_LOCATOR])
- return -EINVAL;
-
- newts = lwtunnel_state_alloc(encap_len);
- if (!newts)
- return -ENOMEM;
-
- newts->len = encap_len;
- p = ila_params_lwtunnel(newts);
-
- p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
-
- if (cfg6->fc_dst_len > sizeof(__be64)) {
- /* Precompute checksum difference for translation since we
- * know both the old locator and the new one.
- */
- p->locator_match = *(__be64 *)&cfg6->fc_dst;
- p->csum_diff = compute_csum_diff8(
- (__be32 *)&p->locator_match, (__be32 *)&p->locator);
- }
-
- newts->type = LWTUNNEL_ENCAP_ILA;
- newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
- LWTUNNEL_STATE_INPUT_REDIRECT;
-
- *ts = newts;
-
- return 0;
-}
-
-static int ila_fill_encap_info(struct sk_buff *skb,
- struct lwtunnel_state *lwtstate)
-{
- struct ila_params *p = ila_params_lwtunnel(lwtstate);
-
- if (nla_put_u64(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator))
- goto nla_put_failure;
-
- return 0;
-
-nla_put_failure:
- return -EMSGSIZE;
-}
-
-static int ila_encap_nlsize(struct lwtunnel_state *lwtstate)
-{
- /* No encapsulation overhead */
- return 0;
-}
-
-static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
-{
- struct ila_params *a_p = ila_params_lwtunnel(a);
- struct ila_params *b_p = ila_params_lwtunnel(b);
-
- return (a_p->locator != b_p->locator);
-}
-
-static const struct lwtunnel_encap_ops ila_encap_ops = {
- .build_state = ila_build_state,
- .output = ila_output,
- .input = ila_input,
- .fill_encap = ila_fill_encap_info,
- .get_encap_size = ila_encap_nlsize,
- .cmp_encap = ila_encap_cmp,
-};
-
-static int __init ila_init(void)
-{
- return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
-}
-
-static void __exit ila_fini(void)
-{
- lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
-}
-
-module_init(ila_init);
-module_exit(ila_fini);
-MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
-MODULE_LICENSE("GPL");
diff --git a/net/ipv6/ila/Makefile b/net/ipv6/ila/Makefile
new file mode 100644
index 000000000000..4b32e5921e5c
--- /dev/null
+++ b/net/ipv6/ila/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for ILA module
+#
+
+obj-$(CONFIG_IPV6_ILA) += ila.o
+
+ila-objs := ila_common.o ila_lwt.o ila_xlat.o
diff --git a/net/ipv6/ila/ila.h b/net/ipv6/ila/ila.h
new file mode 100644
index 000000000000..28542cb2b387
--- /dev/null
+++ b/net/ipv6/ila/ila.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 Tom Herbert <tom@herbertland.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef __ILA_H
+#define __ILA_H
+
+#include <linux/errno.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <uapi/linux/ila.h>
+
+struct ila_params {
+ __be64 locator;
+ __be64 locator_match;
+ __wsum csum_diff;
+};
+
+static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
+{
+ __be32 diff[] = {
+ ~from[0], ~from[1], to[0], to[1],
+ };
+
+ return csum_partial(diff, sizeof(diff), 0);
+}
+
+void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p);
+
+int ila_lwt_init(void);
+void ila_lwt_fini(void);
+int ila_xlat_init(void);
+void ila_xlat_fini(void);
+
+#endif /* __ILA_H */
diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c
new file mode 100644
index 000000000000..32dc9aab7297
--- /dev/null
+++ b/net/ipv6/ila/ila_common.c
@@ -0,0 +1,103 @@
+#include <linux/errno.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ip.h>
+#include <net/ip6_fib.h>
+#include <net/lwtunnel.h>
+#include <net/protocol.h>
+#include <uapi/linux/ila.h>
+#include "ila.h"
+
+static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
+{
+ if (*(__be64 *)&ip6h->daddr == p->locator_match)
+ return p->csum_diff;
+ else
+ return compute_csum_diff8((__be32 *)&ip6h->daddr,
+ (__be32 *)&p->locator);
+}
+
+void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
+{
+ __wsum diff;
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ size_t nhoff = sizeof(struct ipv6hdr);
+
+ /* First update checksum */
+ switch (ip6h->nexthdr) {
+ case NEXTHDR_TCP:
+ if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
+ struct tcphdr *th = (struct tcphdr *)
+ (skb_network_header(skb) + nhoff);
+
+ diff = get_csum_diff(ip6h, p);
+ inet_proto_csum_replace_by_diff(&th->check, skb,
+ diff, true);
+ }
+ break;
+ case NEXTHDR_UDP:
+ if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
+ struct udphdr *uh = (struct udphdr *)
+ (skb_network_header(skb) + nhoff);
+
+ if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ diff = get_csum_diff(ip6h, p);
+ inet_proto_csum_replace_by_diff(&uh->check, skb,
+ diff, true);
+ if (!uh->check)
+ uh->check = CSUM_MANGLED_0;
+ }
+ }
+ break;
+ case NEXTHDR_ICMP:
+ if (likely(pskb_may_pull(skb,
+ nhoff + sizeof(struct icmp6hdr)))) {
+ struct icmp6hdr *ih = (struct icmp6hdr *)
+ (skb_network_header(skb) + nhoff);
+
+ diff = get_csum_diff(ip6h, p);
+ inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
+ diff, true);
+ }
+ break;
+ }
+
+ /* Now change destination address */
+ *(__be64 *)&ip6h->daddr = p->locator;
+}
+
+static int __init ila_init(void)
+{
+ int ret;
+
+ ret = ila_lwt_init();
+
+ if (ret)
+ goto fail_lwt;
+
+ ret = ila_xlat_init();
+ if (ret)
+ goto fail_xlat;
+
+ return 0;
+fail_xlat:
+ ila_lwt_fini();
+fail_lwt:
+ return ret;
+}
+
+static void __exit ila_fini(void)
+{
+ ila_xlat_fini();
+ ila_lwt_fini();
+}
+
+module_init(ila_init);
+module_exit(ila_fini);
+MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
+MODULE_LICENSE("GPL");
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
new file mode 100644
index 000000000000..2ae3c4fd8aab
--- /dev/null
+++ b/net/ipv6/ila/ila_lwt.c
@@ -0,0 +1,152 @@
+#include <linux/errno.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ip.h>
+#include <net/ip6_fib.h>
+#include <net/lwtunnel.h>
+#include <net/protocol.h>
+#include <uapi/linux/ila.h>
+#include "ila.h"
+
+static inline struct ila_params *ila_params_lwtunnel(
+ struct lwtunnel_state *lwstate)
+{
+ return (struct ila_params *)lwstate->data;
+}
+
+static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb_dst(skb);
+
+ if (skb->protocol != htons(ETH_P_IPV6))
+ goto drop;
+
+ update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
+
+ return dst->lwtstate->orig_output(net, sk, skb);
+
+drop:
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int ila_input(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb_dst(skb);
+
+ if (skb->protocol != htons(ETH_P_IPV6))
+ goto drop;
+
+ update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
+
+ return dst->lwtstate->orig_input(skb);
+
+drop:
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
+ [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
+};
+
+static int ila_build_state(struct net_device *dev, struct nlattr *nla,
+ unsigned int family, const void *cfg,
+ struct lwtunnel_state **ts)
+{
+ struct ila_params *p;
+ struct nlattr *tb[ILA_ATTR_MAX + 1];
+ size_t encap_len = sizeof(*p);
+ struct lwtunnel_state *newts;
+ const struct fib6_config *cfg6 = cfg;
+ int ret;
+
+ if (family != AF_INET6)
+ return -EINVAL;
+
+ ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla,
+ ila_nl_policy);
+ if (ret < 0)
+ return ret;
+
+ if (!tb[ILA_ATTR_LOCATOR])
+ return -EINVAL;
+
+ newts = lwtunnel_state_alloc(encap_len);
+ if (!newts)
+ return -ENOMEM;
+
+ newts->len = encap_len;
+ p = ila_params_lwtunnel(newts);
+
+ p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
+
+ if (cfg6->fc_dst_len > sizeof(__be64)) {
+ /* Precompute checksum difference for translation since we
+ * know both the old locator and the new one.
+ */
+ p->locator_match = *(__be64 *)&cfg6->fc_dst;
+ p->csum_diff = compute_csum_diff8(
+ (__be32 *)&p->locator_match, (__be32 *)&p->locator);
+ }
+
+ newts->type = LWTUNNEL_ENCAP_ILA;
+ newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
+ LWTUNNEL_STATE_INPUT_REDIRECT;
+
+ *ts = newts;
+
+ return 0;
+}
+
+static int ila_fill_encap_info(struct sk_buff *skb,
+ struct lwtunnel_state *lwtstate)
+{
+ struct ila_params *p = ila_params_lwtunnel(lwtstate);
+
+ if (nla_put_u64(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int ila_encap_nlsize(struct lwtunnel_state *lwtstate)
+{
+ /* No encapsulation overhead */
+ return 0;
+}
+
+static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
+{
+ struct ila_params *a_p = ila_params_lwtunnel(a);
+ struct ila_params *b_p = ila_params_lwtunnel(b);
+
+ return (a_p->locator != b_p->locator);
+}
+
+static const struct lwtunnel_encap_ops ila_encap_ops = {
+ .build_state = ila_build_state,
+ .output = ila_output,
+ .input = ila_input,
+ .fill_encap = ila_fill_encap_info,
+ .get_encap_size = ila_encap_nlsize,
+ .cmp_encap = ila_encap_cmp,
+};
+
+int ila_lwt_init(void)
+{
+ return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
+}
+
+void ila_lwt_fini(void)
+{
+ lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
+}
diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c
new file mode 100644
index 000000000000..295ca29a23c3
--- /dev/null
+++ b/net/ipv6/ila/ila_xlat.c
@@ -0,0 +1,680 @@
+#include <linux/jhash.h>
+#include <linux/netfilter.h>
+#include <linux/rcupdate.h>
+#include <linux/rhashtable.h>
+#include <linux/vmalloc.h>
+#include <net/genetlink.h>
+#include <net/ila.h>
+#include <net/netns/generic.h>
+#include <uapi/linux/genetlink.h>
+#include "ila.h"
+
+struct ila_xlat_params {
+ struct ila_params ip;
+ __be64 identifier;
+ int ifindex;
+ unsigned int dir;
+};
+
+struct ila_map {
+ struct ila_xlat_params p;
+ struct rhash_head node;
+ struct ila_map __rcu *next;
+ struct rcu_head rcu;
+};
+
+static unsigned int ila_net_id;
+
+struct ila_net {
+ struct rhashtable rhash_table;
+ spinlock_t *locks; /* Bucket locks for entry manipulation */
+ unsigned int locks_mask;
+ bool hooks_registered;
+};
+
+#define LOCKS_PER_CPU 10
+
+static int alloc_ila_locks(struct ila_net *ilan)
+{
+ unsigned int i, size;
+ unsigned int nr_pcpus = num_possible_cpus();
+
+ nr_pcpus = min_t(unsigned int, nr_pcpus, 32UL);
+ size = roundup_pow_of_two(nr_pcpus * LOCKS_PER_CPU);
+
+ if (sizeof(spinlock_t) != 0) {
+#ifdef CONFIG_NUMA
+ if (size * sizeof(spinlock_t) > PAGE_SIZE)
+ ilan->locks = vmalloc(size * sizeof(spinlock_t));
+ else
+#endif
+ ilan->locks = kmalloc_array(size, sizeof(spinlock_t),
+ GFP_KERNEL);
+ if (!ilan->locks)
+ return -ENOMEM;
+ for (i = 0; i < size; i++)
+ spin_lock_init(&ilan->locks[i]);
+ }
+ ilan->locks_mask = size - 1;
+
+ return 0;
+}
+
+static u32 hashrnd __read_mostly;
+static __always_inline void __ila_hash_secret_init(void)
+{
+ net_get_random_once(&hashrnd, sizeof(hashrnd));
+}
+
+static inline u32 ila_identifier_hash(__be64 identifier)
+{
+ u32 *v = (u32 *)&identifier;
+
+ return jhash_2words(v[0], v[1], hashrnd);
+}
+
+static inline spinlock_t *ila_get_lock(struct ila_net *ilan, __be64 identifier)
+{
+ return &ilan->locks[ila_identifier_hash(identifier) & ilan->locks_mask];
+}
+
+static inline int ila_cmp_wildcards(struct ila_map *ila, __be64 loc,
+ int ifindex, unsigned int dir)
+{
+ return (ila->p.ip.locator_match && ila->p.ip.locator_match != loc) ||
+ (ila->p.ifindex && ila->p.ifindex != ifindex) ||
+ !(ila->p.dir & dir);
+}
+
+static inline int ila_cmp_params(struct ila_map *ila, struct ila_xlat_params *p)
+{
+ return (ila->p.ip.locator_match != p->ip.locator_match) ||
+ (ila->p.ifindex != p->ifindex) ||
+ (ila->p.dir != p->dir);
+}
+
+static int ila_cmpfn(struct rhashtable_compare_arg *arg,
+ const void *obj)
+{
+ const struct ila_map *ila = obj;
+
+ return (ila->p.identifier != *(__be64 *)arg->key);
+}
+
+static inline int ila_order(struct ila_map *ila)
+{
+ int score = 0;
+
+ if (ila->p.ip.locator_match)
+ score += 1 << 0;
+
+ if (ila->p.ifindex)
+ score += 1 << 1;
+
+ return score;
+}
+
+static const struct rhashtable_params rht_params = {
+ .nelem_hint = 1024,
+ .head_offset = offsetof(struct ila_map, node),
+ .key_offset = offsetof(struct ila_map, p.identifier),
+ .key_len = sizeof(u64), /* identifier */
+ .max_size = 1048576,
+ .min_size = 256,
+ .automatic_shrinking = true,
+ .obj_cmpfn = ila_cmpfn,
+};
+
+static struct genl_family ila_nl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = ILA_GENL_NAME,
+ .version = ILA_GENL_VERSION,
+ .maxattr = ILA_ATTR_MAX,
+ .netnsok = true,
+ .parallel_ops = true,
+};
+
+static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
+ [ILA_ATTR_IDENTIFIER] = { .type = NLA_U64, },
+ [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
+ [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, },
+ [ILA_ATTR_IFINDEX] = { .type = NLA_U32, },
+ [ILA_ATTR_DIR] = { .type = NLA_U32, },
+};
+
+static int parse_nl_config(struct genl_info *info,
+ struct ila_xlat_params *p)
+{
+ memset(p, 0, sizeof(*p));
+
+ if (info->attrs[ILA_ATTR_IDENTIFIER])
+ p->identifier = (__force __be64)nla_get_u64(
+ info->attrs[ILA_ATTR_IDENTIFIER]);
+
+ if (info->attrs[ILA_ATTR_LOCATOR])
+ p->ip.locator = (__force __be64)nla_get_u64(
+ info->attrs[ILA_ATTR_LOCATOR]);
+
+ if (info->attrs[ILA_ATTR_LOCATOR_MATCH])
+ p->ip.locator_match = (__force __be64)nla_get_u64(
+ info->attrs[ILA_ATTR_LOCATOR_MATCH]);
+
+ if (info->attrs[ILA_ATTR_IFINDEX])
+ p->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
+
+ if (info->attrs[ILA_ATTR_DIR])
+ p->dir = nla_get_u32(info->attrs[ILA_ATTR_DIR]);
+
+ return 0;
+}
+
+/* Must be called with rcu readlock */
+static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc,
+ int ifindex,
+ unsigned int dir,
+ struct ila_net *ilan)
+{
+ struct ila_map *ila;
+
+ ila = rhashtable_lookup_fast(&ilan->rhash_table, &id, rht_params);
+ while (ila) {
+ if (!ila_cmp_wildcards(ila, loc, ifindex, dir))
+ return ila;
+ ila = rcu_access_pointer(ila->next);
+ }
+
+ return NULL;
+}
+
+/* Must be called with rcu readlock */
+static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *p,
+ struct ila_net *ilan)
+{
+ struct ila_map *ila;
+
+ ila = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier,
+ rht_params);
+ while (ila) {
+ if (!ila_cmp_params(ila, p))
+ return ila;
+ ila = rcu_access_pointer(ila->next);
+ }
+
+ return NULL;
+}
+
+static inline void ila_release(struct ila_map *ila)
+{
+ kfree_rcu(ila, rcu);
+}
+
+static void ila_free_cb(void *ptr, void *arg)
+{
+ struct ila_map *ila = (struct ila_map *)ptr, *next;
+
+ /* Assume rcu_readlock held */
+ while (ila) {
+ next = rcu_access_pointer(ila->next);
+ ila_release(ila);
+ ila = next;
+ }
+}
+
+static int ila_xlat_addr(struct sk_buff *skb, int dir);
+
+static unsigned int
+ila_nf_input(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ ila_xlat_addr(skb, ILA_DIR_IN);
+ return NF_ACCEPT;
+}
+
+static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = {
+ {
+ .hook = ila_nf_input,
+ .pf = NFPROTO_IPV6,
+ .hooknum = NF_INET_PRE_ROUTING,
+ .priority = -1,
+ },
+};
+
+static int ila_add_mapping(struct net *net, struct ila_xlat_params *p)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct ila_map *ila, *head;
+ spinlock_t *lock = ila_get_lock(ilan, p->identifier);
+ int err = 0, order;
+
+ if (!ilan->hooks_registered) {
+ /* We defer registering net hooks in the namespace until the
+ * first mapping is added.
+ */
+ err = nf_register_net_hooks(net, ila_nf_hook_ops,
+ ARRAY_SIZE(ila_nf_hook_ops));
+ if (err)
+ return err;
+
+ ilan->hooks_registered = true;
+ }
+
+ ila = kzalloc(sizeof(*ila), GFP_KERNEL);
+ if (!ila)
+ return -ENOMEM;
+
+ ila->p = *p;
+
+ if (p->ip.locator_match) {
+ /* Precompute checksum difference for translation since we
+ * know both the old identifier and the new one.
+ */
+ ila->p.ip.csum_diff = compute_csum_diff8(
+ (__be32 *)&p->ip.locator_match,
+ (__be32 *)&p->ip.locator);
+ }
+
+ order = ila_order(ila);
+
+ spin_lock(lock);
+
+ head = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier,
+ rht_params);
+ if (!head) {
+ /* New entry for the rhash_table */
+ err = rhashtable_lookup_insert_fast(&ilan->rhash_table,
+ &ila->node, rht_params);
+ } else {
+ struct ila_map *tila = head, *prev = NULL;
+
+ do {
+ if (!ila_cmp_params(tila, p)) {
+ err = -EEXIST;
+ goto out;
+ }
+
+ if (order > ila_order(tila))
+ break;
+
+ prev = tila;
+ tila = rcu_dereference_protected(tila->next,
+ lockdep_is_held(lock));
+ } while (tila);
+
+ if (prev) {
+ /* Insert in sub list of head */
+ RCU_INIT_POINTER(ila->next, tila);
+ rcu_assign_pointer(prev->next, ila);
+ } else {
+ /* Make this ila new head */
+ RCU_INIT_POINTER(ila->next, head);
+ err = rhashtable_replace_fast(&ilan->rhash_table,
+ &head->node,
+ &ila->node, rht_params);
+ if (err)
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock(lock);
+
+ if (err)
+ kfree(ila);
+
+ return err;
+}
+
+static int ila_del_mapping(struct net *net, struct ila_xlat_params *p)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct ila_map *ila, *head, *prev;
+ spinlock_t *lock = ila_get_lock(ilan, p->identifier);
+ int err = -ENOENT;
+
+ spin_lock(lock);
+
+ head = rhashtable_lookup_fast(&ilan->rhash_table,
+ &p->identifier, rht_params);
+ ila = head;
+
+ prev = NULL;
+
+ while (ila) {
+ if (ila_cmp_params(ila, p)) {
+ prev = ila;
+ ila = rcu_dereference_protected(ila->next,
+ lockdep_is_held(lock));
+ continue;
+ }
+
+ err = 0;
+
+ if (prev) {
+ /* Not head, just delete from list */
+ rcu_assign_pointer(prev->next, ila->next);
+ } else {
+ /* It is the head. If there is something in the
+ * sublist we need to make a new head.
+ */
+ head = rcu_dereference_protected(ila->next,
+ lockdep_is_held(lock));
+ if (head) {
+ /* Put first entry in the sublist into the
+ * table
+ */
+ err = rhashtable_replace_fast(
+ &ilan->rhash_table, &ila->node,
+ &head->node, rht_params);
+ if (err)
+ goto out;
+ } else {
+ /* Entry no longer used */
+ err = rhashtable_remove_fast(&ilan->rhash_table,
+ &ila->node,
+ rht_params);
+ }
+ }
+
+ ila_release(ila);
+
+ break;
+ }
+
+out:
+ spin_unlock(lock);
+
+ return err;
+}
+
+static int ila_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct ila_xlat_params p;
+ int err;
+
+ err = parse_nl_config(info, &p);
+ if (err)
+ return err;
+
+ return ila_add_mapping(net, &p);
+}
+
+static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct ila_xlat_params p;
+ int err;
+
+ err = parse_nl_config(info, &p);
+ if (err)
+ return err;
+
+ ila_del_mapping(net, &p);
+
+ return 0;
+}
+
+static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg)
+{
+ if (nla_put_u64(msg, ILA_ATTR_IDENTIFIER,
+ (__force u64)ila->p.identifier) ||
+ nla_put_u64(msg, ILA_ATTR_LOCATOR,
+ (__force u64)ila->p.ip.locator) ||
+ nla_put_u64(msg, ILA_ATTR_LOCATOR_MATCH,
+ (__force u64)ila->p.ip.locator_match) ||
+ nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->p.ifindex) ||
+ nla_put_u32(msg, ILA_ATTR_DIR, ila->p.dir))
+ return -1;
+
+ return 0;
+}
+
+static int ila_dump_info(struct ila_map *ila,
+ u32 portid, u32 seq, u32 flags,
+ struct sk_buff *skb, u8 cmd)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, portid, seq, &ila_nl_family, flags, cmd);
+ if (!hdr)
+ return -ENOMEM;
+
+ if (ila_fill_info(ila, skb) < 0)
+ goto nla_put_failure;
+
+ genlmsg_end(skb, hdr);
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct sk_buff *msg;
+ struct ila_xlat_params p;
+ struct ila_map *ila;
+ int ret;
+
+ ret = parse_nl_config(info, &p);
+ if (ret)
+ return ret;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ rcu_read_lock();
+
+ ila = ila_lookup_by_params(&p, ilan);
+ if (ila) {
+ ret = ila_dump_info(ila,
+ info->snd_portid,
+ info->snd_seq, 0, msg,
+ info->genlhdr->cmd);
+ }
+
+ rcu_read_unlock();
+
+ if (ret < 0)
+ goto out_free;
+
+ return genlmsg_reply(msg, info);
+
+out_free:
+ nlmsg_free(msg);
+ return ret;
+}
+
+struct ila_dump_iter {
+ struct rhashtable_iter rhiter;
+};
+
+static int ila_nl_dump_start(struct netlink_callback *cb)
+{
+ struct net *net = sock_net(cb->skb->sk);
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
+
+ return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter);
+}
+
+static int ila_nl_dump_done(struct netlink_callback *cb)
+{
+ struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
+
+ rhashtable_walk_exit(&iter->rhiter);
+
+ return 0;
+}
+
+static int ila_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
+ struct rhashtable_iter *rhiter = &iter->rhiter;
+ struct ila_map *ila;
+ int ret;
+
+ ret = rhashtable_walk_start(rhiter);
+ if (ret && ret != -EAGAIN)
+ goto done;
+
+ for (;;) {
+ ila = rhashtable_walk_next(rhiter);
+
+ if (IS_ERR(ila)) {
+ if (PTR_ERR(ila) == -EAGAIN)
+ continue;
+ ret = PTR_ERR(ila);
+ goto done;
+ } else if (!ila) {
+ break;
+ }
+
+ while (ila) {
+ ret = ila_dump_info(ila, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ skb, ILA_CMD_GET);
+ if (ret)
+ goto done;
+
+ ila = rcu_access_pointer(ila->next);
+ }
+ }
+
+ ret = skb->len;
+
+done:
+ rhashtable_walk_stop(rhiter);
+ return ret;
+}
+
+static const struct genl_ops ila_nl_ops[] = {
+ {
+ .cmd = ILA_CMD_ADD,
+ .doit = ila_nl_cmd_add_mapping,
+ .policy = ila_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = ILA_CMD_DEL,
+ .doit = ila_nl_cmd_del_mapping,
+ .policy = ila_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = ILA_CMD_GET,
+ .doit = ila_nl_cmd_get_mapping,
+ .start = ila_nl_dump_start,
+ .dumpit = ila_nl_dump,
+ .done = ila_nl_dump_done,
+ .policy = ila_nl_policy,
+ },
+};
+
+#define ILA_HASH_TABLE_SIZE 1024
+
+static __net_init int ila_init_net(struct net *net)
+{
+ int err;
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+
+ err = alloc_ila_locks(ilan);
+ if (err)
+ return err;
+
+ rhashtable_init(&ilan->rhash_table, &rht_params);
+
+ return 0;
+}
+
+static __net_exit void ila_exit_net(struct net *net)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+
+ rhashtable_free_and_destroy(&ilan->rhash_table, ila_free_cb, NULL);
+
+ kvfree(ilan->locks);
+
+ if (ilan->hooks_registered)
+ nf_unregister_net_hooks(net, ila_nf_hook_ops,
+ ARRAY_SIZE(ila_nf_hook_ops));
+}
+
+static struct pernet_operations ila_net_ops = {
+ .init = ila_init_net,
+ .exit = ila_exit_net,
+ .id = &ila_net_id,
+ .size = sizeof(struct ila_net),
+};
+
+static int ila_xlat_addr(struct sk_buff *skb, int dir)
+{
+ struct ila_map *ila;
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ struct net *net = dev_net(skb->dev);
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ __be64 identifier, locator_match;
+ size_t nhoff;
+
+ /* Assumes skb contains a valid IPv6 header that is pulled */
+
+ identifier = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[8];
+ locator_match = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[0];
+ nhoff = sizeof(struct ipv6hdr);
+
+ rcu_read_lock();
+
+ ila = ila_lookup_wildcards(identifier, locator_match,
+ skb->dev->ifindex, dir, ilan);
+ if (ila)
+ update_ipv6_locator(skb, &ila->p.ip);
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+int ila_xlat_incoming(struct sk_buff *skb)
+{
+ return ila_xlat_addr(skb, ILA_DIR_IN);
+}
+EXPORT_SYMBOL(ila_xlat_incoming);
+
+int ila_xlat_outgoing(struct sk_buff *skb)
+{
+ return ila_xlat_addr(skb, ILA_DIR_OUT);
+}
+EXPORT_SYMBOL(ila_xlat_outgoing);
+
+int ila_xlat_init(void)
+{
+ int ret;
+
+ ret = register_pernet_device(&ila_net_ops);
+ if (ret)
+ goto exit;
+
+ ret = genl_register_family_with_ops(&ila_nl_family,
+ ila_nl_ops);
+ if (ret < 0)
+ goto unregister;
+
+ return 0;
+
+unregister:
+ unregister_pernet_device(&ila_net_ops);
+exit:
+ return ret;
+}
+
+void ila_xlat_fini(void)
+{
+ genl_unregister_family(&ila_nl_family);
+ unregister_pernet_device(&ila_net_ops);
+}
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index a7ca2cde2ecb..36c3f0155010 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -51,12 +51,12 @@ int inet6_csk_bind_conflict(const struct sock *sk,
(sk2->sk_state != TCP_TIME_WAIT &&
!uid_eq(uid,
sock_i_uid((struct sock *)sk2))))) {
- if (ipv6_rcv_saddr_equal(sk, sk2))
+ if (ipv6_rcv_saddr_equal(sk, sk2, true))
break;
}
if (!relax && reuse && sk2->sk_reuse &&
sk2->sk_state != TCP_LISTEN &&
- ipv6_rcv_saddr_equal(sk, sk2))
+ ipv6_rcv_saddr_equal(sk, sk2, true))
break;
}
}
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index e5ea177d34c6..f37f18b6b40c 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -24,7 +24,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index e6a7bd15b9b7..23de98f976d5 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1322,7 +1322,7 @@ emsgsize:
headersize == sizeof(struct ipv6hdr) &&
length < mtu - headersize &&
!(flags & MSG_MORE) &&
- rt->dst.dev->features & NETIF_F_V6_CSUM)
+ rt->dst.dev->features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
csummode = CHECKSUM_PARTIAL;
if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) {
@@ -1353,7 +1353,7 @@ emsgsize:
(skb && skb_is_gso(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) &&
- (sk->sk_type == SOCK_DGRAM)) {
+ (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) {
err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen,
transhdrlen, mtu, flags, fl6);
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index bab4441ed4e4..e4347aeb2e65 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -56,7 +56,6 @@ struct nf_ct_frag6_skb_cb
{
struct inet6_skb_parm h;
int offset;
- struct sk_buff *orig;
};
#define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb *)((skb)->cb))
@@ -170,12 +169,6 @@ static unsigned int nf_hashfn(const struct inet_frag_queue *q)
return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr);
}
-static void nf_skb_free(struct sk_buff *skb)
-{
- if (NFCT_FRAG6_CB(skb)->orig)
- kfree_skb(NFCT_FRAG6_CB(skb)->orig);
-}
-
static void nf_ct_frag6_expire(unsigned long data)
{
struct frag_queue *fq;
@@ -369,17 +362,18 @@ err:
/*
* Check if this packet is complete.
- * Returns NULL on failure by any reason, and pointer
- * to current nexthdr field in reassembled frame.
*
* It is called with locked fq, and caller must check that
* queue is eligible for reassembly i.e. it is not COMPLETE,
* the last and the first frames arrived and all the bits are here.
+ *
+ * returns true if *prev skb has been transformed into the reassembled
+ * skb, false otherwise.
*/
-static struct sk_buff *
-nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
+static bool
+nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev)
{
- struct sk_buff *fp, *op, *head = fq->q.fragments;
+ struct sk_buff *fp, *head = fq->q.fragments;
int payload_len;
u8 ecn;
@@ -390,22 +384,21 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
ecn = ip_frag_ecn_table[fq->ecn];
if (unlikely(ecn == 0xff))
- goto out_fail;
+ return false;
/* Unfragmented part is taken from the first segment. */
payload_len = ((head->data - skb_network_header(head)) -
sizeof(struct ipv6hdr) + fq->q.len -
sizeof(struct frag_hdr));
if (payload_len > IPV6_MAXPLEN) {
- pr_debug("payload len is too large.\n");
- goto out_oversize;
+ net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n",
+ payload_len);
+ return false;
}
/* Head of list must not be cloned. */
- if (skb_unclone(head, GFP_ATOMIC)) {
- pr_debug("skb is cloned but can't expand head");
- goto out_oom;
- }
+ if (skb_unclone(head, GFP_ATOMIC))
+ return false;
/* If the first fragment is fragmented itself, we split
* it to two chunks: the first with data and paged part
@@ -416,7 +409,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
clone = alloc_skb(0, GFP_ATOMIC);
if (clone == NULL)
- goto out_oom;
+ return false;
clone->next = head->next;
head->next = clone;
@@ -430,10 +423,41 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
clone->csum = 0;
clone->ip_summed = head->ip_summed;
- NFCT_FRAG6_CB(clone)->orig = NULL;
add_frag_mem_limit(fq->q.net, clone->truesize);
}
+ /* morph head into last received skb: prev.
+ *
+ * This allows callers of ipv6 conntrack defrag to continue
+ * to use the last skb(frag) passed into the reasm engine.
+ * The last skb frag 'silently' turns into the full reassembled skb.
+ *
+ * Since prev is also part of q->fragments we have to clone it first.
+ */
+ if (head != prev) {
+ struct sk_buff *iter;
+
+ fp = skb_clone(prev, GFP_ATOMIC);
+ if (!fp)
+ return false;
+
+ fp->next = prev->next;
+
+ iter = head;
+ while (iter) {
+ if (iter->next == prev) {
+ iter->next = fp;
+ break;
+ }
+ iter = iter->next;
+ }
+
+ skb_morph(prev, head);
+ prev->next = head->next;
+ consume_skb(head);
+ head = prev;
+ }
+
/* We have to remove fragment header from datagram and to relocate
* header in order to calculate ICV correctly. */
skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0];
@@ -474,31 +498,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
fq->q.fragments = NULL;
fq->q.fragments_tail = NULL;
- /* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */
- fp = skb_shinfo(head)->frag_list;
- if (fp && NFCT_FRAG6_CB(fp)->orig == NULL)
- /* at above code, head skb is divided into two skbs. */
- fp = fp->next;
-
- op = NFCT_FRAG6_CB(head)->orig;
- for (; fp; fp = fp->next) {
- struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig;
-
- op->next = orig;
- op = orig;
- NFCT_FRAG6_CB(fp)->orig = NULL;
- }
-
- return head;
-
-out_oversize:
- net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n",
- payload_len);
- goto out_fail;
-out_oom:
- net_dbg_ratelimited("nf_ct_frag6_reasm: no memory for reassembly\n");
-out_fail:
- return NULL;
+ return true;
}
/*
@@ -564,89 +564,61 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
return 0;
}
-struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
+int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
{
- struct sk_buff *clone;
struct net_device *dev = skb->dev;
+ int fhoff, nhoff, ret;
struct frag_hdr *fhdr;
struct frag_queue *fq;
struct ipv6hdr *hdr;
- int fhoff, nhoff;
u8 prevhdr;
- struct sk_buff *ret_skb = NULL;
/* Jumbo payload inhibits frag. header */
if (ipv6_hdr(skb)->payload_len == 0) {
pr_debug("payload len = 0\n");
- return skb;
+ return -EINVAL;
}
if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0)
- return skb;
+ return -EINVAL;
- clone = skb_clone(skb, GFP_ATOMIC);
- if (clone == NULL) {
- pr_debug("Can't clone skb\n");
- return skb;
- }
+ if (!pskb_may_pull(skb, fhoff + sizeof(*fhdr)))
+ return -ENOMEM;
- NFCT_FRAG6_CB(clone)->orig = skb;
-
- if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) {
- pr_debug("message is too short.\n");
- goto ret_orig;
- }
-
- skb_set_transport_header(clone, fhoff);
- hdr = ipv6_hdr(clone);
- fhdr = (struct frag_hdr *)skb_transport_header(clone);
+ skb_set_transport_header(skb, fhoff);
+ hdr = ipv6_hdr(skb);
+ fhdr = (struct frag_hdr *)skb_transport_header(skb);
fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr,
skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr));
if (fq == NULL) {
pr_debug("Can't find and can't create new queue\n");
- goto ret_orig;
+ return -ENOMEM;
}
spin_lock_bh(&fq->q.lock);
- if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) {
- spin_unlock_bh(&fq->q.lock);
- pr_debug("Can't insert skb to queue\n");
- inet_frag_put(&fq->q, &nf_frags);
- goto ret_orig;
+ if (nf_ct_frag6_queue(fq, skb, fhdr, nhoff) < 0) {
+ ret = -EINVAL;
+ goto out_unlock;
}
+ /* after queue has assumed skb ownership, only 0 or -EINPROGRESS
+ * must be returned.
+ */
+ ret = -EINPROGRESS;
if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
- fq->q.meat == fq->q.len) {
- ret_skb = nf_ct_frag6_reasm(fq, dev);
- if (ret_skb == NULL)
- pr_debug("Can't reassemble fragmented packets\n");
- }
- spin_unlock_bh(&fq->q.lock);
+ fq->q.meat == fq->q.len &&
+ nf_ct_frag6_reasm(fq, skb, dev))
+ ret = 0;
+out_unlock:
+ spin_unlock_bh(&fq->q.lock);
inet_frag_put(&fq->q, &nf_frags);
- return ret_skb;
-
-ret_orig:
- kfree_skb(clone);
- return skb;
+ return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_frag6_gather);
-void nf_ct_frag6_consume_orig(struct sk_buff *skb)
-{
- struct sk_buff *s, *s2;
-
- for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
- s2 = s->next;
- s->next = NULL;
- consume_skb(s);
- s = s2;
- }
-}
-EXPORT_SYMBOL_GPL(nf_ct_frag6_consume_orig);
-
static int nf_ct_net_init(struct net *net)
{
int res;
@@ -681,7 +653,6 @@ int nf_ct_frag6_init(void)
nf_frags.hashfn = nf_hashfn;
nf_frags.constructor = ip6_frag_init;
nf_frags.destructor = NULL;
- nf_frags.skb_free = nf_skb_free;
nf_frags.qsize = sizeof(struct frag_queue);
nf_frags.match = ip6_frag_match;
nf_frags.frag_expire = nf_ct_frag6_expire;
diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
index 4fdbed5ebfb6..f7aab5ab93a5 100644
--- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
+++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
@@ -55,7 +55,7 @@ static unsigned int ipv6_defrag(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
- struct sk_buff *reasm;
+ int err;
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
/* Previously seen (loopback)? */
@@ -63,23 +63,13 @@ static unsigned int ipv6_defrag(void *priv,
return NF_ACCEPT;
#endif
- reasm = nf_ct_frag6_gather(state->net, skb,
- nf_ct6_defrag_user(state->hook, skb));
+ err = nf_ct_frag6_gather(state->net, skb,
+ nf_ct6_defrag_user(state->hook, skb));
/* queued */
- if (reasm == NULL)
+ if (err == -EINPROGRESS)
return NF_STOLEN;
- /* error occurred or not fragmented */
- if (reasm == skb)
- return NF_ACCEPT;
-
- nf_ct_frag6_consume_orig(reasm);
-
- NF_HOOK_THRESH(NFPROTO_IPV6, state->hook, state->net, state->sk, reasm,
- state->in, state->out,
- state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
-
- return NF_STOLEN;
+ return NF_ACCEPT;
}
static struct nf_hook_ops ipv6_defrag_ops[] = {
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index 238e70c3f7b7..6ce309928841 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -136,7 +136,8 @@ static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
if (skb->ip_summed != CHECKSUM_PARTIAL) {
if (!(rt->rt6i_flags & RTF_LOCAL) &&
- (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) {
+ (!skb->dev || skb->dev->features &
+ (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb) +
skb_network_offset(skb) +
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index e0f922b777e3..4709f657b7b6 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -14,7 +14,6 @@
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_bridge.h>
-#include <net/netfilter/ipv6/nf_reject.h>
const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *otcph,
diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c
index 120ea9131be0..30b22f4dff55 100644
--- a/net/ipv6/netfilter/nf_tables_ipv6.c
+++ b/net/ipv6/netfilter/nf_tables_ipv6.c
@@ -77,7 +77,7 @@ err:
static void nf_tables_ipv6_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.ipv6);
+ nft_unregister_afinfo(net, net->nft.ipv6);
kfree(net->nft.ipv6);
}
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 99140986e887..fa59dd7a427e 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -972,6 +972,11 @@ static int do_rawv6_setsockopt(struct sock *sk, int level, int optname,
return -EFAULT;
switch (optname) {
+ case IPV6_HDRINCL:
+ if (sk->sk_type != SOCK_RAW)
+ return -EINVAL;
+ inet_sk(sk)->hdrincl = !!val;
+ return 0;
case IPV6_CHECKSUM:
if (inet_sk(sk)->inet_num == IPPROTO_ICMPV6 &&
level == IPPROTO_IPV6) {
@@ -1016,7 +1021,8 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_seticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return ipv6_setsockopt(sk, level, optname, optval, optlen);
@@ -1037,7 +1043,8 @@ static int compat_rawv6_setsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_seticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return compat_ipv6_setsockopt(sk, level, optname,
@@ -1057,6 +1064,9 @@ static int do_rawv6_getsockopt(struct sock *sk, int level, int optname,
return -EFAULT;
switch (optname) {
+ case IPV6_HDRINCL:
+ val = inet_sk(sk)->hdrincl;
+ break;
case IPV6_CHECKSUM:
/*
* We allow getsockopt() for IPPROTO_IPV6-level
@@ -1094,7 +1104,8 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_geticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return ipv6_getsockopt(sk, level, optname, optval, optlen);
@@ -1115,7 +1126,8 @@ static int compat_rawv6_getsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_geticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return compat_ipv6_getsockopt(sk, level, optname,
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 45f5ae51de65..18f3498a6c80 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -755,7 +755,6 @@ int __init ipv6_frag_init(void)
ip6_frags.hashfn = ip6_hashfn;
ip6_frags.constructor = ip6_frag_init;
ip6_frags.destructor = NULL;
- ip6_frags.skb_free = NULL;
ip6_frags.qsize = sizeof(struct frag_queue);
ip6_frags.match = ip6_frag_match;
ip6_frags.frag_expire = ip6_frag_expire;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 826e6aa44f8d..3c8834bc822d 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -62,6 +62,7 @@
#include <net/lwtunnel.h>
#include <net/ip_tunnels.h>
#include <net/l3mdev.h>
+#include <trace/events/fib6.h>
#include <asm/uaccess.h>
@@ -865,6 +866,9 @@ restart:
}
dst_use(&rt->dst, jiffies);
read_unlock_bh(&table->tb6_lock);
+
+ trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
+
return rt;
}
@@ -1078,6 +1082,8 @@ redo_rt6_select:
read_unlock_bh(&table->tb6_lock);
rt6_dst_from_metrics_check(rt);
+
+ trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
return rt;
} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
!(rt->rt6i_flags & RTF_GATEWAY))) {
@@ -1101,6 +1107,8 @@ redo_rt6_select:
uncached_rt = net->ipv6.ip6_null_entry;
dst_hold(&uncached_rt->dst);
+
+ trace_fib6_table_lookup(net, uncached_rt, table->tb6_id, fl6);
return uncached_rt;
} else {
@@ -1125,6 +1133,7 @@ redo_rt6_select:
dst_release(&rt->dst);
}
+ trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6);
return pcpu_rt;
}
@@ -1474,6 +1483,7 @@ out:
read_unlock_bh(&table->tb6_lock);
+ trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
return rt;
};
@@ -2699,6 +2709,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
[RTA_PREF] = { .type = NLA_U8 },
[RTA_ENCAP_TYPE] = { .type = NLA_U16 },
[RTA_ENCAP] = { .type = NLA_NESTED },
+ [RTA_EXPIRES] = { .type = NLA_U32 },
};
static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -2799,6 +2810,15 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
if (tb[RTA_ENCAP_TYPE])
cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
+ if (tb[RTA_EXPIRES]) {
+ unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
+
+ if (addrconf_finite_timeout(timeout)) {
+ cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
+ cfg->fc_flags |= RTF_EXPIRES;
+ }
+ }
+
err = 0;
errout:
return err;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index dcccae86190f..e794ef66a401 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -820,7 +820,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
const struct in6_addr *addr6;
int addr_type;
u8 ttl;
- int err;
u8 protocol = IPPROTO_IPV6;
int t_hlen = tunnel->hlen + sizeof(struct iphdr);
@@ -983,10 +982,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
skb_set_inner_ipproto(skb, IPPROTO_IPV6);
- err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr,
- protocol, tos, ttl, df,
- !net_eq(tunnel->net, dev_net(dev)));
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+ iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl,
+ df, !net_eq(tunnel->net, dev_net(dev)));
return NETDEV_TX_OK;
tx_error_icmp:
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index eaf7ac496d50..2906ef20795e 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -193,7 +193,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
ireq->pktopts = skb;
}
- ireq->ir_iif = sk->sk_bound_dev_if;
+ ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
/* So that link locals have meaning */
if (!sk->sk_bound_dev_if &&
ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
@@ -224,7 +224,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
fl6.daddr = ireq->ir_v6_rmt_addr;
final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
fl6.saddr = ireq->ir_v6_loc_addr;
- fl6.flowi6_oif = sk->sk_bound_dev_if;
+ fl6.flowi6_oif = ireq->ir_iif;
fl6.flowi6_mark = ireq->ir_mark;
fl6.fl6_dport = ireq->ir_rmt_port;
fl6.fl6_sport = inet_sk(sk)->inet_sport;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6b8a8a9091fa..db9f1c318afc 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -462,8 +462,10 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,
if (np->repflow && ireq->pktopts)
fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts));
+ rcu_read_lock();
err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt),
np->tclass);
+ rcu_read_unlock();
err = net_xmit_eval(err);
}
@@ -854,7 +856,9 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
#ifdef CONFIG_TCP_MD5SIG
hash_location = tcp_parse_md5sig_option(th);
- if (!sk && hash_location) {
+ if (sk && sk_fullsock(sk)) {
+ key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr);
+ } else if (hash_location) {
/*
* active side is lost. Try to find listening socket through
* source port, and then find md5 key through listening socket.
@@ -877,8 +881,6 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb);
if (genhash || memcmp(hash_location, newhash, 16) != 0)
goto release_sk1;
- } else {
- key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL;
}
#endif
@@ -1135,7 +1137,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
*/
tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
AF_INET6, key->key, key->keylen,
- sk_gfp_atomic(sk, GFP_ATOMIC));
+ sk_gfp_mask(sk, GFP_ATOMIC));
}
#endif
@@ -1151,7 +1153,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
/* Clone pktoptions received with SYN, if we own the req */
if (ireq->pktopts) {
newnp->pktoptions = skb_clone(ireq->pktopts,
- sk_gfp_atomic(sk, GFP_ATOMIC));
+ sk_gfp_mask(sk, GFP_ATOMIC));
consume_skb(ireq->pktopts);
ireq->pktopts = NULL;
if (newnp->pktoptions)
@@ -1217,7 +1219,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
--ANK (980728)
*/
if (np->rxopt.all)
- opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC));
+ opt_skb = skb_clone(skb, sk_gfp_mask(sk, GFP_ATOMIC));
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
struct dst_entry *dst = sk->sk_rx_dst;
@@ -1516,7 +1518,9 @@ do_time_wait:
break;
case TCP_TW_RST:
tcp_v6_restore_cb(skb);
- goto no_tcp_socket;
+ tcp_v6_send_reset(sk, skb);
+ inet_twsk_deschedule_put(inet_twsk(sk));
+ goto discard_it;
case TCP_TW_SUCCESS:
;
}
@@ -1889,6 +1893,7 @@ struct proto tcpv6_prot = {
.proto_cgroup = tcp_proto_cgroup,
#endif
.clear_sk = tcp_v6_clear_sk,
+ .diag_destroy = tcp_abort,
};
static const struct inet6_protocol tcpv6_protocol = {
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 9da3287a3923..5d2c2afffe7b 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -47,6 +47,7 @@
#include <net/xfrm.h>
#include <net/inet6_hashtables.h>
#include <net/busy_poll.h>
+#include <net/sock_reuseport.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
@@ -76,7 +77,14 @@ static u32 udp6_ehashfn(const struct net *net,
udp_ipv6_hash_secret + net_hash_mix(net));
}
-int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
+/* match_wildcard == true: IPV6_ADDR_ANY equals to any IPv6 addresses if IPv6
+ * only, and any IPv4 addresses if not IPv6 only
+ * match_wildcard == false: addresses must be exactly the same, i.e.
+ * IPV6_ADDR_ANY only equals to IPV6_ADDR_ANY,
+ * and 0.0.0.0 equals to 0.0.0.0 only
+ */
+int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
+ bool match_wildcard)
{
const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2);
int sk2_ipv6only = inet_v6_ipv6only(sk2);
@@ -84,16 +92,24 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
/* if both are mapped, treat as IPv4 */
- if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED)
- return (!sk2_ipv6only &&
- (!sk->sk_rcv_saddr || !sk2->sk_rcv_saddr ||
- sk->sk_rcv_saddr == sk2->sk_rcv_saddr));
+ if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED) {
+ if (!sk2_ipv6only) {
+ if (sk->sk_rcv_saddr == sk2->sk_rcv_saddr)
+ return 1;
+ if (!sk->sk_rcv_saddr || !sk2->sk_rcv_saddr)
+ return match_wildcard;
+ }
+ return 0;
+ }
- if (addr_type2 == IPV6_ADDR_ANY &&
+ if (addr_type == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY)
+ return 1;
+
+ if (addr_type2 == IPV6_ADDR_ANY && match_wildcard &&
!(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
return 1;
- if (addr_type == IPV6_ADDR_ANY &&
+ if (addr_type == IPV6_ADDR_ANY && match_wildcard &&
!(ipv6_only_sock(sk) && addr_type2 == IPV6_ADDR_MAPPED))
return 1;
@@ -235,7 +251,8 @@ static inline int compute_score2(struct sock *sk, struct net *net,
static struct sock *udp6_lib_lookup2(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, unsigned int hnum, int dif,
- struct udp_hslot *hslot2, unsigned int slot2)
+ struct udp_hslot *hslot2, unsigned int slot2,
+ struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -253,8 +270,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp6_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -273,6 +297,7 @@ begin:
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score2(result, net, saddr, sport,
@@ -287,7 +312,8 @@ begin:
struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
- int dif, struct udp_table *udptable)
+ int dif, struct udp_table *udptable,
+ struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -307,7 +333,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
result = udp6_lib_lookup2(net, saddr, sport,
daddr, hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
if (!result) {
hash2 = udp6_portaddr_hash(net, &in6addr_any, hnum);
slot2 = hash2 & udptable->mask;
@@ -317,7 +343,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
result = udp6_lib_lookup2(net, saddr, sport,
&in6addr_any, hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
}
rcu_read_unlock();
return result;
@@ -332,8 +358,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp6_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -352,6 +385,7 @@ begin:
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score(result, net, hnum, saddr, sport,
@@ -377,13 +411,13 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
return sk;
return __udp6_lib_lookup(dev_net(skb_dst(skb)->dev), &iph->saddr, sport,
&iph->daddr, dport, inet6_iif(skb),
- udptable);
+ udptable, skb);
}
struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport, int dif)
{
- return __udp6_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table);
+ return __udp6_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table, NULL);
}
EXPORT_SYMBOL_GPL(udp6_lib_lookup);
@@ -402,6 +436,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int peeked, off = 0;
int err;
int is_udplite = IS_UDPLITE(sk);
+ bool checksum_valid = false;
int is_udp4;
bool slow;
@@ -433,11 +468,12 @@ try_again:
*/
if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
- if (udp_lib_checksum_complete(skb))
+ checksum_valid = !udp_lib_checksum_complete(skb);
+ if (!checksum_valid)
goto csum_copy_err;
}
- if (skb_csum_unnecessary(skb))
+ if (checksum_valid || skb_csum_unnecessary(skb))
err = skb_copy_datagram_msg(skb, sizeof(struct udphdr),
msg, copied);
else {
@@ -547,8 +583,8 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int err;
struct net *net = dev_net(skb->dev);
- sk = __udp6_lib_lookup(net, daddr, uh->dest,
- saddr, uh->source, inet6_iif(skb), udptable);
+ sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
+ inet6_iif(skb), udptable, skb);
if (!sk) {
ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
ICMP6_MIB_INERRORS);
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 435608c4306d..ef50a94d3eb7 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -303,7 +303,7 @@ static void iucv_sock_wake_msglim(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_all(&wq->wait);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
rcu_read_unlock();
@@ -1031,7 +1031,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
struct sock *sk = sock->sk;
struct iucv_sock *iucv = iucv_sk(sk);
struct sk_buff *skb;
- struct iucv_message txmsg;
+ struct iucv_message txmsg = {0};
struct cmsghdr *cmsg;
int cmsg_done;
long timeo;
@@ -2084,11 +2084,7 @@ static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
return NET_RX_SUCCESS;
}
- /* write stuff from iucv_msg to skb cb */
- if (skb->len < sizeof(struct af_iucv_trans_hdr)) {
- kfree_skb(skb);
- return NET_RX_SUCCESS;
- }
+ /* write stuff from iucv_msg to skb cb */
skb_pull(skb, sizeof(struct af_iucv_trans_hdr));
skb_reset_transport_header(skb);
skb_reset_network_header(skb);
@@ -2119,6 +2115,20 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
char nullstring[8];
int err = 0;
+ if (skb->len < (ETH_HLEN + sizeof(struct af_iucv_trans_hdr))) {
+ WARN_ONCE(1, "AF_IUCV too short skb, len=%d, min=%d",
+ (int)skb->len,
+ (int)(ETH_HLEN + sizeof(struct af_iucv_trans_hdr)));
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
+ if (skb_headlen(skb) < (ETH_HLEN + sizeof(struct af_iucv_trans_hdr)))
+ if (skb_linearize(skb)) {
+ WARN_ONCE(1, "AF_IUCV skb_linearize failed, len=%d",
+ (int)skb->len);
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
skb_pull(skb, ETH_HLEN);
trans_hdr = (struct af_iucv_trans_hdr *)skb->data;
EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 1ad18c55064c..652c250b9a3b 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -230,26 +230,11 @@ static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int
if (sk->sk_state & PPPOX_BOUND) {
struct pppox_sock *po;
+
l2tp_dbg(session, PPPOL2TP_MSG_DATA,
"%s: recv %d byte data frame, passing to ppp\n",
session->name, data_len);
- /* We need to forget all info related to the L2TP packet
- * gathered in the skb as we are going to reuse the same
- * skb for the inner packet.
- * Namely we need to:
- * - reset xfrm (IPSec) information as it applies to
- * the outer L2TP packet and not to the inner one
- * - release the dst to force a route lookup on the inner
- * IP packet since skb->dst currently points to the dst
- * of the UDP tunnel
- * - reset netfilter information as it doesn't apply
- * to the inner packet either
- */
- secpath_reset(skb);
- skb_dst_drop(skb);
- nf_reset(skb);
-
po = pppox_sk(sk);
ppp_input(&po->chan, skb);
} else {
@@ -1862,5 +1847,5 @@ MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("PPP over L2TP over UDP");
MODULE_LICENSE("GPL");
MODULE_VERSION(PPPOL2TP_DRV_VERSION);
-MODULE_ALIAS("pppox-proto-" __stringify(PX_PROTO_OL2TP));
+MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OL2TP);
MODULE_ALIAS_L2TP_PWTYPE(11);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c12f348138ac..166a29fe6c35 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1215,16 +1215,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOMEM;
- /*
- * defaults -- if userspace wants something else we'll
- * change it accordingly in sta_apply_parameters()
- */
- if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
- !(params->sta_flags_set & (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
- BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
- sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
- }
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
sta->sta.tdls = true;
@@ -1993,6 +1983,11 @@ static int ieee80211_scan(struct wiphy *wiphy,
return ieee80211_request_scan(sdata, req);
}
+static void ieee80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ ieee80211_scan_cancel(wiphy_priv(wiphy));
+}
+
static int
ieee80211_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev,
@@ -2508,294 +2503,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
return 0;
}
-static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
- struct ieee80211_roc_work *new_roc,
- struct ieee80211_roc_work *cur_roc)
-{
- unsigned long now = jiffies;
- unsigned long remaining = cur_roc->hw_start_time +
- msecs_to_jiffies(cur_roc->duration) -
- now;
-
- if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
- return false;
-
- /* if it doesn't fit entirely, schedule a new one */
- if (new_roc->duration > jiffies_to_msecs(remaining))
- return false;
-
- ieee80211_handle_roc_started(new_roc);
-
- /* add to dependents so we send the expired event properly */
- list_add_tail(&new_roc->list, &cur_roc->dependents);
- return true;
-}
-
-static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
-{
- lockdep_assert_held(&local->mtx);
-
- local->roc_cookie_counter++;
-
- /* wow, you wrapped 64 bits ... more likely a bug */
- if (WARN_ON(local->roc_cookie_counter == 0))
- local->roc_cookie_counter++;
-
- return local->roc_cookie_counter;
-}
-
-static int ieee80211_start_roc_work(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- unsigned int duration, u64 *cookie,
- struct sk_buff *txskb,
- enum ieee80211_roc_type type)
-{
- struct ieee80211_roc_work *roc, *tmp;
- bool queued = false;
- int ret;
-
- lockdep_assert_held(&local->mtx);
-
- if (local->use_chanctx && !local->ops->remain_on_channel)
- return -EOPNOTSUPP;
-
- roc = kzalloc(sizeof(*roc), GFP_KERNEL);
- if (!roc)
- return -ENOMEM;
-
- /*
- * If the duration is zero, then the driver
- * wouldn't actually do anything. Set it to
- * 10 for now.
- *
- * TODO: cancel the off-channel operation
- * when we get the SKB's TX status and
- * the wait time was zero before.
- */
- if (!duration)
- duration = 10;
-
- roc->chan = channel;
- roc->duration = duration;
- roc->req_duration = duration;
- roc->frame = txskb;
- roc->type = type;
- roc->sdata = sdata;
- INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
- INIT_LIST_HEAD(&roc->dependents);
-
- /*
- * cookie is either the roc cookie (for normal roc)
- * or the SKB (for mgmt TX)
- */
- if (!txskb) {
- roc->cookie = ieee80211_mgmt_tx_cookie(local);
- *cookie = roc->cookie;
- } else {
- roc->mgmt_tx_cookie = *cookie;
- }
-
- /* if there's one pending or we're scanning, queue this one */
- if (!list_empty(&local->roc_list) ||
- local->scanning || ieee80211_is_radar_required(local))
- goto out_check_combine;
-
- /* if not HW assist, just queue & schedule work */
- if (!local->ops->remain_on_channel) {
- ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
- goto out_queue;
- }
-
- /* otherwise actually kick it off here (for error handling) */
-
- ret = drv_remain_on_channel(local, sdata, channel, duration, type);
- if (ret) {
- kfree(roc);
- return ret;
- }
-
- roc->started = true;
- goto out_queue;
-
- out_check_combine:
- list_for_each_entry(tmp, &local->roc_list, list) {
- if (tmp->chan != channel || tmp->sdata != sdata)
- continue;
-
- /*
- * Extend this ROC if possible:
- *
- * If it hasn't started yet, just increase the duration
- * and add the new one to the list of dependents.
- * If the type of the new ROC has higher priority, modify the
- * type of the previous one to match that of the new one.
- */
- if (!tmp->started) {
- list_add_tail(&roc->list, &tmp->dependents);
- tmp->duration = max(tmp->duration, roc->duration);
- tmp->type = max(tmp->type, roc->type);
- queued = true;
- break;
- }
-
- /* If it has already started, it's more difficult ... */
- if (local->ops->remain_on_channel) {
- /*
- * In the offloaded ROC case, if it hasn't begun, add
- * this new one to the dependent list to be handled
- * when the master one begins. If it has begun,
- * check if it fits entirely within the existing one,
- * in which case it will just be dependent as well.
- * Otherwise, schedule it by itself.
- */
- if (!tmp->hw_begun) {
- list_add_tail(&roc->list, &tmp->dependents);
- queued = true;
- break;
- }
-
- if (ieee80211_coalesce_started_roc(local, roc, tmp))
- queued = true;
- } else if (del_timer_sync(&tmp->work.timer)) {
- unsigned long new_end;
-
- /*
- * In the software ROC case, cancel the timer, if
- * that fails then the finish work is already
- * queued/pending and thus we queue the new ROC
- * normally, if that succeeds then we can extend
- * the timer duration and TX the frame (if any.)
- */
-
- list_add_tail(&roc->list, &tmp->dependents);
- queued = true;
-
- new_end = jiffies + msecs_to_jiffies(roc->duration);
-
- /* ok, it was started & we canceled timer */
- if (time_after(new_end, tmp->work.timer.expires))
- mod_timer(&tmp->work.timer, new_end);
- else
- add_timer(&tmp->work.timer);
-
- ieee80211_handle_roc_started(roc);
- }
- break;
- }
-
- out_queue:
- if (!queued)
- list_add_tail(&roc->list, &local->roc_list);
-
- return 0;
-}
-
-static int ieee80211_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- struct ieee80211_channel *chan,
- unsigned int duration,
- u64 *cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
- int ret;
-
- mutex_lock(&local->mtx);
- ret = ieee80211_start_roc_work(local, sdata, chan,
- duration, cookie, NULL,
- IEEE80211_ROC_TYPE_NORMAL);
- mutex_unlock(&local->mtx);
-
- return ret;
-}
-
-static int ieee80211_cancel_roc(struct ieee80211_local *local,
- u64 cookie, bool mgmt_tx)
-{
- struct ieee80211_roc_work *roc, *tmp, *found = NULL;
- int ret;
-
- mutex_lock(&local->mtx);
- list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
- struct ieee80211_roc_work *dep, *tmp2;
-
- list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
- if (!mgmt_tx && dep->cookie != cookie)
- continue;
- else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
- continue;
- /* found dependent item -- just remove it */
- list_del(&dep->list);
- mutex_unlock(&local->mtx);
-
- ieee80211_roc_notify_destroy(dep, true);
- return 0;
- }
-
- if (!mgmt_tx && roc->cookie != cookie)
- continue;
- else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
- continue;
-
- found = roc;
- break;
- }
-
- if (!found) {
- mutex_unlock(&local->mtx);
- return -ENOENT;
- }
-
- /*
- * We found the item to cancel, so do that. Note that it
- * may have dependents, which we also cancel (and send
- * the expired signal for.) Not doing so would be quite
- * tricky here, but we may need to fix it later.
- */
-
- if (local->ops->remain_on_channel) {
- if (found->started) {
- ret = drv_cancel_remain_on_channel(local);
- if (WARN_ON_ONCE(ret)) {
- mutex_unlock(&local->mtx);
- return ret;
- }
- }
-
- list_del(&found->list);
-
- if (found->started)
- ieee80211_start_next_roc(local);
- mutex_unlock(&local->mtx);
-
- ieee80211_roc_notify_destroy(found, true);
- } else {
- /* work may be pending so use it all the time */
- found->abort = true;
- ieee80211_queue_delayed_work(&local->hw, &found->work, 0);
-
- mutex_unlock(&local->mtx);
-
- /* work will clean up etc */
- flush_delayed_work(&found->work);
- WARN_ON(!found->to_be_freed);
- kfree(found);
- }
-
- return 0;
-}
-
-static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u64 cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
-
- return ieee80211_cancel_roc(local, cookie, false);
-}
-
static int ieee80211_start_radar_detection(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_chan_def *chandef,
@@ -3266,9 +2973,21 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
return err;
}
-static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
- struct sk_buff *skb, u64 *cookie,
- gfp_t gfp)
+u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
+{
+ lockdep_assert_held(&local->mtx);
+
+ local->roc_cookie_counter++;
+
+ /* wow, you wrapped 64 bits ... more likely a bug */
+ if (WARN_ON(local->roc_cookie_counter == 0))
+ local->roc_cookie_counter++;
+
+ return local->roc_cookie_counter;
+}
+
+int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
+ u64 *cookie, gfp_t gfp)
{
unsigned long spin_flags;
struct sk_buff *ack_skb;
@@ -3276,7 +2995,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
ack_skb = skb_copy(skb, gfp);
if (!ack_skb)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
spin_lock_irqsave(&local->ack_status_lock, spin_flags);
id = idr_alloc(&local->ack_status_frames, ack_skb,
@@ -3285,7 +3004,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
if (id < 0) {
kfree_skb(ack_skb);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
IEEE80211_SKB_CB(skb)->ack_frame_id = id;
@@ -3293,204 +3012,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
*cookie = ieee80211_mgmt_tx_cookie(local);
IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
- return ack_skb;
-}
-
-static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct cfg80211_mgmt_tx_params *params,
- u64 *cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb, *ack_skb;
- struct sta_info *sta;
- const struct ieee80211_mgmt *mgmt = (void *)params->buf;
- bool need_offchan = false;
- u32 flags;
- int ret;
- u8 *data;
-
- if (params->dont_wait_for_ack)
- flags = IEEE80211_TX_CTL_NO_ACK;
- else
- flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
- IEEE80211_TX_CTL_REQ_TX_STATUS;
-
- if (params->no_cck)
- flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_ADHOC:
- if (!sdata->vif.bss_conf.ibss_joined)
- need_offchan = true;
- /* fall through */
-#ifdef CONFIG_MAC80211_MESH
- case NL80211_IFTYPE_MESH_POINT:
- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- !sdata->u.mesh.mesh_id_len)
- need_offchan = true;
- /* fall through */
-#endif
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_P2P_GO:
- if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- !ieee80211_vif_is_mesh(&sdata->vif) &&
- !rcu_access_pointer(sdata->bss->beacon))
- need_offchan = true;
- if (!ieee80211_is_action(mgmt->frame_control) ||
- mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
- mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
- mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
- break;
- rcu_read_lock();
- sta = sta_info_get(sdata, mgmt->da);
- rcu_read_unlock();
- if (!sta)
- return -ENOLINK;
- break;
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_CLIENT:
- sdata_lock(sdata);
- if (!sdata->u.mgd.associated ||
- (params->offchan && params->wait &&
- local->ops->remain_on_channel &&
- memcmp(sdata->u.mgd.associated->bssid,
- mgmt->bssid, ETH_ALEN)))
- need_offchan = true;
- sdata_unlock(sdata);
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- need_offchan = true;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- /* configurations requiring offchan cannot work if no channel has been
- * specified
- */
- if (need_offchan && !params->chan)
- return -EINVAL;
-
- mutex_lock(&local->mtx);
-
- /* Check if the operating channel is the requested channel */
- if (!need_offchan) {
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
- if (chanctx_conf) {
- need_offchan = params->chan &&
- (params->chan !=
- chanctx_conf->def.chan);
- } else if (!params->chan) {
- ret = -EINVAL;
- rcu_read_unlock();
- goto out_unlock;
- } else {
- need_offchan = true;
- }
- rcu_read_unlock();
- }
-
- if (need_offchan && !params->offchan) {
- ret = -EBUSY;
- goto out_unlock;
- }
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
- if (!skb) {
- ret = -ENOMEM;
- goto out_unlock;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- data = skb_put(skb, params->len);
- memcpy(data, params->buf, params->len);
-
- /* Update CSA counters */
- if (sdata->vif.csa_active &&
- (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
- params->n_csa_offsets) {
- int i;
- struct beacon_data *beacon = NULL;
-
- rcu_read_lock();
-
- if (sdata->vif.type == NL80211_IFTYPE_AP)
- beacon = rcu_dereference(sdata->u.ap.beacon);
- else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
- beacon = rcu_dereference(sdata->u.ibss.presp);
- else if (ieee80211_vif_is_mesh(&sdata->vif))
- beacon = rcu_dereference(sdata->u.mesh.beacon);
-
- if (beacon)
- for (i = 0; i < params->n_csa_offsets; i++)
- data[params->csa_offsets[i]] =
- beacon->csa_current_counter;
-
- rcu_read_unlock();
- }
-
- IEEE80211_SKB_CB(skb)->flags = flags;
-
- skb->dev = sdata->dev;
-
- if (!params->dont_wait_for_ack) {
- /* make a copy to preserve the frame contents
- * in case of encryption.
- */
- ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
- GFP_KERNEL);
- if (IS_ERR(ack_skb)) {
- ret = PTR_ERR(ack_skb);
- kfree_skb(skb);
- goto out_unlock;
- }
- } else {
- /* Assign a dummy non-zero cookie, it's not sent to
- * userspace in this case but we rely on its value
- * internally in the need_offchan case to distinguish
- * mgmt-tx from remain-on-channel.
- */
- *cookie = 0xffffffff;
- }
-
- if (!need_offchan) {
- ieee80211_tx_skb(sdata, skb);
- ret = 0;
- goto out_unlock;
- }
-
- IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
- IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
- if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
- IEEE80211_SKB_CB(skb)->hw_queue =
- local->hw.offchannel_tx_hw_queue;
-
- /* This will handle all kinds of coalescing and immediate TX */
- ret = ieee80211_start_roc_work(local, sdata, params->chan,
- params->wait, cookie, skb,
- IEEE80211_ROC_TYPE_MGMT_TX);
- if (ret)
- kfree_skb(skb);
- out_unlock:
- mutex_unlock(&local->mtx);
- return ret;
-}
-
-static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u64 cookie)
-{
- struct ieee80211_local *local = wiphy_priv(wiphy);
-
- return ieee80211_cancel_roc(local, cookie, true);
+ return 0;
}
static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
@@ -3568,7 +3090,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_qos_hdr *nullfunc;
- struct sk_buff *skb, *ack_skb;
+ struct sk_buff *skb;
int size = sizeof(*nullfunc);
__le16 fc;
bool qos;
@@ -3636,10 +3158,9 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
if (qos)
nullfunc->qos_ctrl = cpu_to_le16(7);
- ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
- if (IS_ERR(ack_skb)) {
+ ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_ATOMIC);
+ if (ret) {
kfree_skb(skb);
- ret = PTR_ERR(ack_skb);
goto unlock;
}
@@ -3841,6 +3362,7 @@ const struct cfg80211_ops mac80211_config_ops = {
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
+ .abort_scan = ieee80211_abort_scan,
.sched_scan_start = ieee80211_sched_scan_start,
.sched_scan_stop = ieee80211_sched_scan_stop,
.auth = ieee80211_auth,
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 4d2aaebd4f97..abbdff03ce92 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -125,6 +125,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
FLAG(TDLS_WIDER_BW),
FLAG(SUPPORTS_AMSDU_IN_AMPDU),
FLAG(BEACON_TX_STATUS),
+ FLAG(NEEDS_UNIQUE_STA_ADDR),
/* keep last for the build bug below */
(void *)0x1
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 337bb5d78003..f7fc0e00497f 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -428,6 +428,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
chandef.width = sdata->u.ibss.chandef.width;
break;
case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
chandef = sdata->u.ibss.chandef;
chandef.chan = cbss->channel;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5322b4c71630..b84f6aa32c08 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -325,19 +325,15 @@ struct mesh_preq_queue {
struct ieee80211_roc_work {
struct list_head list;
- struct list_head dependents;
-
- struct delayed_work work;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
bool started, abort, hw_begun, notified;
- bool to_be_freed;
bool on_channel;
- unsigned long hw_start_time;
+ unsigned long start_time;
u32 duration, req_duration;
struct sk_buff *frame;
@@ -1335,6 +1331,7 @@ struct ieee80211_local {
/*
* Remain-on-channel support
*/
+ struct delayed_work roc_work;
struct list_head roc_list;
struct work_struct hw_roc_start, hw_roc_done;
unsigned long hw_roc_start_time;
@@ -1483,6 +1480,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
void ieee80211_configure_filter(struct ieee80211_local *local);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
+u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local);
+int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
+ u64 *cookie, gfp_t gfp);
+
/* STA code */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
@@ -1577,16 +1578,22 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_local *local);
void ieee80211_sched_scan_end(struct ieee80211_local *local);
void ieee80211_sched_scan_stopped_work(struct work_struct *work);
-/* off-channel helpers */
+/* off-channel/mgmt-tx */
void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);
void ieee80211_offchannel_return(struct ieee80211_local *local);
void ieee80211_roc_setup(struct ieee80211_local *local);
void ieee80211_start_next_roc(struct ieee80211_local *local);
void ieee80211_roc_purge(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
-void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
-void ieee80211_sw_roc_work(struct work_struct *work);
-void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
+int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie);
+int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie);
+int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie);
+int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie);
/* channel switch handling */
void ieee80211_csa_finalize_work(struct work_struct *work);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 44388d6a1d8e..5e5bc599da4c 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -4,6 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015 Intel Deutschland GmbH
*
* 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
@@ -320,7 +321,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
return;
if (new)
- list_add_tail(&new->list, &sdata->key_list);
+ list_add_tail_rcu(&new->list, &sdata->key_list);
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
@@ -368,7 +369,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
}
if (old)
- list_del(&old->list);
+ list_del_rcu(&old->list);
}
struct ieee80211_key *
@@ -592,8 +593,8 @@ static void ieee80211_key_destroy(struct ieee80211_key *key,
return;
/*
- * Synchronize so the TX path can no longer be using
- * this key before we free/remove it.
+ * Synchronize so the TX path and rcu key iterators
+ * can no longer be using this key before we free/remove it.
*/
synchronize_net();
@@ -744,6 +745,53 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_iter_keys);
+static void
+_ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_sub_if_data *sdata,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_key *key;
+
+ list_for_each_entry_rcu(key, &sdata->key_list, list) {
+ /* skip keys of station in removal process */
+ if (key->sta && key->sta->removed)
+ continue;
+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+ continue;
+
+ iter(hw, &sdata->vif,
+ key->sta ? &key->sta->sta : NULL,
+ &key->conf, iter_data);
+ }
+}
+
+void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+
+ if (vif) {
+ sdata = vif_to_sdata(vif);
+ _ieee80211_iter_keys_rcu(hw, sdata, iter, iter_data);
+ } else {
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ _ieee80211_iter_keys_rcu(hw, sdata, iter, iter_data);
+ }
+}
+EXPORT_SYMBOL(ieee80211_iter_keys_rcu);
+
static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata,
struct list_head *keys)
{
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 175ffcf7fb06..6bcf0faa4a89 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -541,7 +541,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
NL80211_FEATURE_HT_IBSS |
NL80211_FEATURE_VIF_TXPOWER |
NL80211_FEATURE_MAC_ON_CREATE |
- NL80211_FEATURE_USERSPACE_MPM;
+ NL80211_FEATURE_USERSPACE_MPM |
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE;
if (!ops->hw_scan)
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -1148,6 +1149,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
rtnl_unlock();
+ cancel_delayed_work_sync(&local->roc_work);
cancel_work_sync(&local->restart_work);
cancel_work_sync(&local->reconfig_filter);
cancel_work_sync(&local->tdls_chsw_work);
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index b3b44a5dd375..dadf8dc6f1cf 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -968,8 +968,8 @@ int mesh_path_send_to_gates(struct mesh_path *mpath)
copy = true;
} else {
mpath_dbg(sdata,
- "Not forwarding %p (flags %#x)\n",
- gate->mpath, gate->mpath->flags);
+ "Not forwarding to %pM (flags %#x)\n",
+ gate->mpath->dst, gate->mpath->flags);
}
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 3aa04344942b..1c342e2592c4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1935,7 +1935,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
- if (sdata->vif.p2p) {
+ if (sdata->vif.p2p ||
+ sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
const struct cfg80211_bss_ies *ies;
rcu_read_lock();
@@ -3463,7 +3464,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
- if (sdata->vif.p2p) {
+ if (sdata->vif.p2p ||
+ sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
struct ieee80211_p2p_noa_attr noa = {};
int ret;
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 04401037140e..8b2f4eaac2ba 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -187,11 +187,80 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
false);
}
-void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
+static void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
{
- if (roc->notified)
+ /* was never transmitted */
+ if (roc->frame) {
+ cfg80211_mgmt_tx_status(&roc->sdata->wdev, roc->mgmt_tx_cookie,
+ roc->frame->data, roc->frame->len,
+ false, GFP_KERNEL);
+ ieee80211_free_txskb(&roc->sdata->local->hw, roc->frame);
+ }
+
+ if (!roc->mgmt_tx_cookie)
+ cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
+ roc->cookie, roc->chan,
+ GFP_KERNEL);
+
+ list_del(&roc->list);
+ kfree(roc);
+}
+
+static unsigned long ieee80211_end_finished_rocs(struct ieee80211_local *local,
+ unsigned long now)
+{
+ struct ieee80211_roc_work *roc, *tmp;
+ long remaining_dur_min = LONG_MAX;
+
+ lockdep_assert_held(&local->mtx);
+
+ list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+ long remaining;
+
+ if (!roc->started)
+ break;
+
+ remaining = roc->start_time +
+ msecs_to_jiffies(roc->duration) -
+ now;
+
+ /* In case of HW ROC, it is possible that the HW finished the
+ * ROC session before the actual requested time. In such a case
+ * end the ROC session (disregarding the remaining time).
+ */
+ if (roc->abort || roc->hw_begun || remaining <= 0)
+ ieee80211_roc_notify_destroy(roc);
+ else
+ remaining_dur_min = min(remaining_dur_min, remaining);
+ }
+
+ return remaining_dur_min;
+}
+
+static bool ieee80211_recalc_sw_work(struct ieee80211_local *local,
+ unsigned long now)
+{
+ long dur = ieee80211_end_finished_rocs(local, now);
+
+ if (dur == LONG_MAX)
+ return false;
+
+ mod_delayed_work(local->workqueue, &local->roc_work, dur);
+ return true;
+}
+
+static void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc,
+ unsigned long start_time)
+{
+ struct ieee80211_local *local = roc->sdata->local;
+
+ if (WARN_ON(roc->notified))
return;
+ roc->start_time = start_time;
+ roc->started = true;
+ roc->hw_begun = true;
+
if (roc->mgmt_tx_cookie) {
if (!WARN_ON(!roc->frame)) {
ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7,
@@ -205,40 +274,26 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
}
roc->notified = true;
+
+ if (!local->ops->remain_on_channel)
+ ieee80211_recalc_sw_work(local, start_time);
}
static void ieee80211_hw_roc_start(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, hw_roc_start);
- struct ieee80211_roc_work *roc, *dep, *tmp;
+ struct ieee80211_roc_work *roc;
mutex_lock(&local->mtx);
- if (list_empty(&local->roc_list))
- goto out_unlock;
-
- roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
- list);
-
- if (!roc->started)
- goto out_unlock;
-
- roc->hw_begun = true;
- roc->hw_start_time = local->hw_roc_start_time;
+ list_for_each_entry(roc, &local->roc_list, list) {
+ if (!roc->started)
+ break;
- ieee80211_handle_roc_started(roc);
- list_for_each_entry_safe(dep, tmp, &roc->dependents, list) {
- ieee80211_handle_roc_started(dep);
-
- if (dep->duration > roc->duration) {
- u32 dur = dep->duration;
- dep->duration = dur - roc->duration;
- roc->duration = dur;
- list_move(&dep->list, &roc->list);
- }
+ ieee80211_handle_roc_started(roc, local->hw_roc_start_time);
}
- out_unlock:
+
mutex_unlock(&local->mtx);
}
@@ -254,34 +309,40 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
-void ieee80211_start_next_roc(struct ieee80211_local *local)
+static void _ieee80211_start_next_roc(struct ieee80211_local *local)
{
- struct ieee80211_roc_work *roc;
+ struct ieee80211_roc_work *roc, *tmp;
+ enum ieee80211_roc_type type;
+ u32 min_dur, max_dur;
lockdep_assert_held(&local->mtx);
- if (list_empty(&local->roc_list)) {
- ieee80211_run_deferred_scan(local);
+ if (WARN_ON(list_empty(&local->roc_list)))
return;
- }
roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
list);
- if (WARN_ON_ONCE(roc->started))
+ if (WARN_ON(roc->started))
return;
- if (local->ops->remain_on_channel) {
- int ret, duration = roc->duration;
-
- /* XXX: duplicated, see ieee80211_start_roc_work() */
- if (!duration)
- duration = 10;
+ min_dur = roc->duration;
+ max_dur = roc->duration;
+ type = roc->type;
- ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
- duration, roc->type);
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp == roc)
+ continue;
+ if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+ break;
+ max_dur = max(tmp->duration, max_dur);
+ min_dur = min(tmp->duration, min_dur);
+ type = max(tmp->type, type);
+ }
- roc->started = true;
+ if (local->ops->remain_on_channel) {
+ int ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
+ max_dur, type);
if (ret) {
wiphy_warn(local->hw.wiphy,
@@ -290,74 +351,24 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
* queue the work struct again to avoid recursion
* when multiple failures occur
*/
- ieee80211_remain_on_channel_expired(&local->hw);
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->sdata != roc->sdata ||
+ tmp->chan != roc->chan)
+ break;
+ tmp->started = true;
+ tmp->abort = true;
+ }
+ ieee80211_queue_work(&local->hw, &local->hw_roc_done);
+ return;
}
- } else {
- /* delay it a bit */
- ieee80211_queue_delayed_work(&local->hw, &roc->work,
- round_jiffies_relative(HZ/2));
- }
-}
-
-void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free)
-{
- struct ieee80211_roc_work *dep, *tmp;
-
- if (WARN_ON(roc->to_be_freed))
- return;
-
- /* was never transmitted */
- if (roc->frame) {
- cfg80211_mgmt_tx_status(&roc->sdata->wdev,
- (unsigned long)roc->frame,
- roc->frame->data, roc->frame->len,
- false, GFP_KERNEL);
- kfree_skb(roc->frame);
- }
-
- if (!roc->mgmt_tx_cookie)
- cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
- roc->cookie, roc->chan,
- GFP_KERNEL);
-
- list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
- ieee80211_roc_notify_destroy(dep, true);
-
- if (free)
- kfree(roc);
- else
- roc->to_be_freed = true;
-}
-
-void ieee80211_sw_roc_work(struct work_struct *work)
-{
- struct ieee80211_roc_work *roc =
- container_of(work, struct ieee80211_roc_work, work.work);
- struct ieee80211_sub_if_data *sdata = roc->sdata;
- struct ieee80211_local *local = sdata->local;
- bool started, on_channel;
-
- mutex_lock(&local->mtx);
-
- if (roc->to_be_freed)
- goto out_unlock;
-
- if (roc->abort)
- goto finish;
-
- if (WARN_ON(list_empty(&local->roc_list)))
- goto out_unlock;
-
- if (WARN_ON(roc != list_first_entry(&local->roc_list,
- struct ieee80211_roc_work,
- list)))
- goto out_unlock;
-
- if (!roc->started) {
- struct ieee80211_roc_work *dep;
-
- WARN_ON(local->use_chanctx);
+ /* we'll notify about the start once the HW calls back */
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+ break;
+ tmp->started = true;
+ }
+ } else {
/* If actually operating on the desired channel (with at least
* 20 MHz channel width) don't stop all the operations but still
* treat it as though the ROC operation started properly, so
@@ -377,27 +388,72 @@ void ieee80211_sw_roc_work(struct work_struct *work)
ieee80211_hw_config(local, 0);
}
- /* tell userspace or send frame */
- ieee80211_handle_roc_started(roc);
- list_for_each_entry(dep, &roc->dependents, list)
- ieee80211_handle_roc_started(dep);
+ ieee80211_queue_delayed_work(&local->hw, &local->roc_work,
+ msecs_to_jiffies(min_dur));
- /* if it was pure TX, just finish right away */
- if (!roc->duration)
- goto finish;
+ /* tell userspace or send frame(s) */
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+ break;
- roc->started = true;
- ieee80211_queue_delayed_work(&local->hw, &roc->work,
- msecs_to_jiffies(roc->duration));
+ tmp->on_channel = roc->on_channel;
+ ieee80211_handle_roc_started(tmp, jiffies);
+ }
+ }
+}
+
+void ieee80211_start_next_roc(struct ieee80211_local *local)
+{
+ struct ieee80211_roc_work *roc;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (list_empty(&local->roc_list)) {
+ ieee80211_run_deferred_scan(local);
+ return;
+ }
+
+ roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
+ list);
+
+ if (WARN_ON_ONCE(roc->started))
+ return;
+
+ if (local->ops->remain_on_channel) {
+ _ieee80211_start_next_roc(local);
+ } else {
+ /* delay it a bit */
+ ieee80211_queue_delayed_work(&local->hw, &local->roc_work,
+ round_jiffies_relative(HZ/2));
+ }
+}
+
+static void __ieee80211_roc_work(struct ieee80211_local *local)
+{
+ struct ieee80211_roc_work *roc;
+ bool on_channel;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (WARN_ON(local->ops->remain_on_channel))
+ return;
+
+ roc = list_first_entry_or_null(&local->roc_list,
+ struct ieee80211_roc_work, list);
+ if (!roc)
+ return;
+
+ if (!roc->started) {
+ WARN_ON(local->use_chanctx);
+ _ieee80211_start_next_roc(local);
} else {
- /* finish this ROC */
- finish:
- list_del(&roc->list);
- started = roc->started;
on_channel = roc->on_channel;
- ieee80211_roc_notify_destroy(roc, !roc->abort);
+ if (ieee80211_recalc_sw_work(local, jiffies))
+ return;
+
+ /* careful - roc pointer became invalid during recalc */
- if (started && !on_channel) {
+ if (!on_channel) {
ieee80211_flush_queues(local, NULL, false);
local->tmp_channel = NULL;
@@ -407,14 +463,17 @@ void ieee80211_sw_roc_work(struct work_struct *work)
}
ieee80211_recalc_idle(local);
-
- if (started)
- ieee80211_start_next_roc(local);
- else if (list_empty(&local->roc_list))
- ieee80211_run_deferred_scan(local);
+ ieee80211_start_next_roc(local);
}
+}
- out_unlock:
+static void ieee80211_roc_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, roc_work.work);
+
+ mutex_lock(&local->mtx);
+ __ieee80211_roc_work(local);
mutex_unlock(&local->mtx);
}
@@ -422,27 +481,14 @@ static void ieee80211_hw_roc_done(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, hw_roc_done);
- struct ieee80211_roc_work *roc;
mutex_lock(&local->mtx);
- if (list_empty(&local->roc_list))
- goto out_unlock;
-
- roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
- list);
-
- if (!roc->started)
- goto out_unlock;
-
- list_del(&roc->list);
-
- ieee80211_roc_notify_destroy(roc, true);
+ ieee80211_end_finished_rocs(local, jiffies);
/* if there's another roc, start it now */
ieee80211_start_next_roc(local);
- out_unlock:
mutex_unlock(&local->mtx);
}
@@ -456,47 +502,500 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
-void ieee80211_roc_setup(struct ieee80211_local *local)
+static bool
+ieee80211_coalesce_hw_started_roc(struct ieee80211_local *local,
+ struct ieee80211_roc_work *new_roc,
+ struct ieee80211_roc_work *cur_roc)
{
- INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
- INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
- INIT_LIST_HEAD(&local->roc_list);
+ unsigned long now = jiffies;
+ unsigned long remaining;
+
+ if (WARN_ON(!cur_roc->started))
+ return false;
+
+ /* if it was scheduled in the hardware, but not started yet,
+ * we can only combine if the older one had a longer duration
+ */
+ if (!cur_roc->hw_begun && new_roc->duration > cur_roc->duration)
+ return false;
+
+ remaining = cur_roc->start_time +
+ msecs_to_jiffies(cur_roc->duration) -
+ now;
+
+ /* if it doesn't fit entirely, schedule a new one */
+ if (new_roc->duration > jiffies_to_msecs(remaining))
+ return false;
+
+ /* add just after the current one so we combine their finish later */
+ list_add(&new_roc->list, &cur_roc->list);
+
+ /* if the existing one has already begun then let this one also
+ * begin, otherwise they'll both be marked properly by the work
+ * struct that runs once the driver notifies us of the beginning
+ */
+ if (cur_roc->hw_begun)
+ ieee80211_handle_roc_started(new_roc, now);
+
+ return true;
}
-void ieee80211_roc_purge(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+static int ieee80211_start_roc_work(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *channel,
+ unsigned int duration, u64 *cookie,
+ struct sk_buff *txskb,
+ enum ieee80211_roc_type type)
{
struct ieee80211_roc_work *roc, *tmp;
- LIST_HEAD(tmp_list);
+ bool queued = false, combine_started = true;
+ int ret;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (local->use_chanctx && !local->ops->remain_on_channel)
+ return -EOPNOTSUPP;
+
+ roc = kzalloc(sizeof(*roc), GFP_KERNEL);
+ if (!roc)
+ return -ENOMEM;
+
+ /*
+ * If the duration is zero, then the driver
+ * wouldn't actually do anything. Set it to
+ * 10 for now.
+ *
+ * TODO: cancel the off-channel operation
+ * when we get the SKB's TX status and
+ * the wait time was zero before.
+ */
+ if (!duration)
+ duration = 10;
+
+ roc->chan = channel;
+ roc->duration = duration;
+ roc->req_duration = duration;
+ roc->frame = txskb;
+ roc->type = type;
+ roc->sdata = sdata;
+
+ /*
+ * cookie is either the roc cookie (for normal roc)
+ * or the SKB (for mgmt TX)
+ */
+ if (!txskb) {
+ roc->cookie = ieee80211_mgmt_tx_cookie(local);
+ *cookie = roc->cookie;
+ } else {
+ roc->mgmt_tx_cookie = *cookie;
+ }
+
+ /* if there's no need to queue, handle it immediately */
+ if (list_empty(&local->roc_list) &&
+ !local->scanning && !ieee80211_is_radar_required(local)) {
+ /* if not HW assist, just queue & schedule work */
+ if (!local->ops->remain_on_channel) {
+ list_add_tail(&roc->list, &local->roc_list);
+ ieee80211_queue_delayed_work(&local->hw,
+ &local->roc_work, 0);
+ } else {
+ /* otherwise actually kick it off here
+ * (for error handling)
+ */
+ ret = drv_remain_on_channel(local, sdata, channel,
+ duration, type);
+ if (ret) {
+ kfree(roc);
+ return ret;
+ }
+ roc->started = true;
+ list_add_tail(&roc->list, &local->roc_list);
+ }
+
+ return 0;
+ }
+
+ /* otherwise handle queueing */
+
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->chan != channel || tmp->sdata != sdata)
+ continue;
+
+ /*
+ * Extend this ROC if possible: If it hasn't started, add
+ * just after the new one to combine.
+ */
+ if (!tmp->started) {
+ list_add(&roc->list, &tmp->list);
+ queued = true;
+ break;
+ }
+
+ if (!combine_started)
+ continue;
+
+ if (!local->ops->remain_on_channel) {
+ /* If there's no hardware remain-on-channel, and
+ * doing so won't push us over the maximum r-o-c
+ * we allow, then we can just add the new one to
+ * the list and mark it as having started now.
+ * If it would push over the limit, don't try to
+ * combine with other started ones (that haven't
+ * been running as long) but potentially sort it
+ * with others that had the same fate.
+ */
+ unsigned long now = jiffies;
+ u32 elapsed = jiffies_to_msecs(now - tmp->start_time);
+ struct wiphy *wiphy = local->hw.wiphy;
+ u32 max_roc = wiphy->max_remain_on_channel_duration;
+
+ if (elapsed + roc->duration > max_roc) {
+ combine_started = false;
+ continue;
+ }
+
+ list_add(&roc->list, &tmp->list);
+ queued = true;
+ roc->on_channel = tmp->on_channel;
+ ieee80211_handle_roc_started(roc, now);
+ break;
+ }
+
+ queued = ieee80211_coalesce_hw_started_roc(local, roc, tmp);
+ if (queued)
+ break;
+ /* if it wasn't queued, perhaps it can be combined with
+ * another that also couldn't get combined previously,
+ * but no need to check for already started ones, since
+ * that can't work.
+ */
+ combine_started = false;
+ }
+
+ if (!queued)
+ list_add_tail(&roc->list, &local->roc_list);
+
+ return 0;
+}
+
+int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+
+ mutex_lock(&local->mtx);
+ ret = ieee80211_start_roc_work(local, sdata, chan,
+ duration, cookie, NULL,
+ IEEE80211_ROC_TYPE_NORMAL);
+ mutex_unlock(&local->mtx);
+
+ return ret;
+}
+
+static int ieee80211_cancel_roc(struct ieee80211_local *local,
+ u64 cookie, bool mgmt_tx)
+{
+ struct ieee80211_roc_work *roc, *tmp, *found = NULL;
+ int ret;
+
+ if (!cookie)
+ return -ENOENT;
mutex_lock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
- if (sdata && roc->sdata != sdata)
+ if (!mgmt_tx && roc->cookie != cookie)
continue;
+ else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
+ continue;
+
+ found = roc;
+ break;
+ }
+
+ if (!found) {
+ mutex_unlock(&local->mtx);
+ return -ENOENT;
+ }
+
+ if (!found->started) {
+ ieee80211_roc_notify_destroy(found);
+ goto out_unlock;
+ }
- if (roc->started && local->ops->remain_on_channel) {
- /* can race, so ignore return value */
- drv_cancel_remain_on_channel(local);
+ if (local->ops->remain_on_channel) {
+ ret = drv_cancel_remain_on_channel(local);
+ if (WARN_ON_ONCE(ret)) {
+ mutex_unlock(&local->mtx);
+ return ret;
+ }
+
+ /* TODO:
+ * if multiple items were combined here then we really shouldn't
+ * cancel them all - we should wait for as much time as needed
+ * for the longest remaining one, and only then cancel ...
+ */
+ list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+ if (!roc->started)
+ break;
+ if (roc == found)
+ found = NULL;
+ ieee80211_roc_notify_destroy(roc);
}
- list_move_tail(&roc->list, &tmp_list);
- roc->abort = true;
+ /* that really must not happen - it was started */
+ WARN_ON(found);
+
+ ieee80211_start_next_roc(local);
+ } else {
+ /* go through work struct to return to the operating channel */
+ found->abort = true;
+ mod_delayed_work(local->workqueue, &local->roc_work, 0);
}
+
+ out_unlock:
mutex_unlock(&local->mtx);
- list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
- if (local->ops->remain_on_channel) {
- list_del(&roc->list);
- ieee80211_roc_notify_destroy(roc, true);
+ return 0;
+}
+
+int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local = sdata->local;
+
+ return ieee80211_cancel_roc(local, cookie, false);
+}
+
+int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct sta_info *sta;
+ const struct ieee80211_mgmt *mgmt = (void *)params->buf;
+ bool need_offchan = false;
+ u32 flags;
+ int ret;
+ u8 *data;
+
+ if (params->dont_wait_for_ack)
+ flags = IEEE80211_TX_CTL_NO_ACK;
+ else
+ flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+ if (params->no_cck)
+ flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_ADHOC:
+ if (!sdata->vif.bss_conf.ibss_joined)
+ need_offchan = true;
+ /* fall through */
+#ifdef CONFIG_MAC80211_MESH
+ case NL80211_IFTYPE_MESH_POINT:
+ if (ieee80211_vif_is_mesh(&sdata->vif) &&
+ !sdata->u.mesh.mesh_id_len)
+ need_offchan = true;
+ /* fall through */
+#endif
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ !ieee80211_vif_is_mesh(&sdata->vif) &&
+ !rcu_access_pointer(sdata->bss->beacon))
+ need_offchan = true;
+ if (!ieee80211_is_action(mgmt->frame_control) ||
+ mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
+ mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
+ mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
+ break;
+ rcu_read_lock();
+ sta = sta_info_get(sdata, mgmt->da);
+ rcu_read_unlock();
+ if (!sta)
+ return -ENOLINK;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ sdata_lock(sdata);
+ if (!sdata->u.mgd.associated ||
+ (params->offchan && params->wait &&
+ local->ops->remain_on_channel &&
+ memcmp(sdata->u.mgd.associated->bssid,
+ mgmt->bssid, ETH_ALEN)))
+ need_offchan = true;
+ sdata_unlock(sdata);
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ need_offchan = true;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* configurations requiring offchan cannot work if no channel has been
+ * specified
+ */
+ if (need_offchan && !params->chan)
+ return -EINVAL;
+
+ mutex_lock(&local->mtx);
+
+ /* Check if the operating channel is the requested channel */
+ if (!need_offchan) {
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (chanctx_conf) {
+ need_offchan = params->chan &&
+ (params->chan !=
+ chanctx_conf->def.chan);
+ } else if (!params->chan) {
+ ret = -EINVAL;
+ rcu_read_unlock();
+ goto out_unlock;
} else {
- ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
+ need_offchan = true;
+ }
+ rcu_read_unlock();
+ }
+
+ if (need_offchan && !params->offchan) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ data = skb_put(skb, params->len);
+ memcpy(data, params->buf, params->len);
+
+ /* Update CSA counters */
+ if (sdata->vif.csa_active &&
+ (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
+ params->n_csa_offsets) {
+ int i;
+ struct beacon_data *beacon = NULL;
+
+ rcu_read_lock();
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ beacon = rcu_dereference(sdata->u.ap.beacon);
+ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ beacon = rcu_dereference(sdata->u.ibss.presp);
+ else if (ieee80211_vif_is_mesh(&sdata->vif))
+ beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+ if (beacon)
+ for (i = 0; i < params->n_csa_offsets; i++)
+ data[params->csa_offsets[i]] =
+ beacon->csa_current_counter;
+
+ rcu_read_unlock();
+ }
- /* work will clean up etc */
- flush_delayed_work(&roc->work);
- WARN_ON(!roc->to_be_freed);
- kfree(roc);
+ IEEE80211_SKB_CB(skb)->flags = flags;
+
+ skb->dev = sdata->dev;
+
+ if (!params->dont_wait_for_ack) {
+ /* make a copy to preserve the frame contents
+ * in case of encryption.
+ */
+ ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_KERNEL);
+ if (ret) {
+ kfree_skb(skb);
+ goto out_unlock;
}
+ } else {
+ /* Assign a dummy non-zero cookie, it's not sent to
+ * userspace in this case but we rely on its value
+ * internally in the need_offchan case to distinguish
+ * mgmt-tx from remain-on-channel.
+ */
+ *cookie = 0xffffffff;
}
- WARN_ON_ONCE(!list_empty(&tmp_list));
+ if (!need_offchan) {
+ ieee80211_tx_skb(sdata, skb);
+ ret = 0;
+ goto out_unlock;
+ }
+
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
+ if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
+ IEEE80211_SKB_CB(skb)->hw_queue =
+ local->hw.offchannel_tx_hw_queue;
+
+ /* This will handle all kinds of coalescing and immediate TX */
+ ret = ieee80211_start_roc_work(local, sdata, params->chan,
+ params->wait, cookie, skb,
+ IEEE80211_ROC_TYPE_MGMT_TX);
+ if (ret)
+ ieee80211_free_txskb(&local->hw, skb);
+ out_unlock:
+ mutex_unlock(&local->mtx);
+ return ret;
+}
+
+int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ return ieee80211_cancel_roc(local, cookie, true);
+}
+
+void ieee80211_roc_setup(struct ieee80211_local *local)
+{
+ INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
+ INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
+ INIT_DELAYED_WORK(&local->roc_work, ieee80211_roc_work);
+ INIT_LIST_HEAD(&local->roc_list);
+}
+
+void ieee80211_roc_purge(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_roc_work *roc, *tmp;
+ bool work_to_do = false;
+
+ mutex_lock(&local->mtx);
+ list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+ if (sdata && roc->sdata != sdata)
+ continue;
+
+ if (roc->started) {
+ if (local->ops->remain_on_channel) {
+ /* can race, so ignore return value */
+ drv_cancel_remain_on_channel(local);
+ ieee80211_roc_notify_destroy(roc);
+ } else {
+ roc->abort = true;
+ work_to_do = true;
+ }
+ } else {
+ ieee80211_roc_notify_destroy(roc);
+ }
+ }
+ if (work_to_do)
+ __ieee80211_roc_work(local);
+ mutex_unlock(&local->mtx);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 82af407fea7a..bc081850ac0e 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -661,8 +661,7 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx)
{
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- WARN_ONCE((unsigned long)rx->skb->data & 1,
- "unaligned packet at 0x%p\n", rx->skb->data);
+ WARN_ON_ONCE((unsigned long)rx->skb->data & 1);
#endif
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index f91d1873218c..4402ad5b27d1 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -2,6 +2,7 @@
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2015 Intel Deutschland GmbH
*
* 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
@@ -435,6 +436,19 @@ static int sta_info_insert_check(struct sta_info *sta)
is_multicast_ether_addr(sta->sta.addr)))
return -EINVAL;
+ /* Strictly speaking this isn't necessary as we hold the mutex, but
+ * the rhashtable code can't really deal with that distinction. We
+ * do require the mutex for correctness though.
+ */
+ rcu_read_lock();
+ lockdep_assert_held(&sdata->local->sta_mtx);
+ if (ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR) &&
+ ieee80211_find_sta_by_ifaddr(&sdata->local->hw, sta->addr, NULL)) {
+ rcu_read_unlock();
+ return -ENOTUNIQ;
+ }
+ rcu_read_unlock();
+
return 0;
}
@@ -554,14 +568,15 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
might_sleep();
+ mutex_lock(&local->sta_mtx);
+
err = sta_info_insert_check(sta);
if (err) {
+ mutex_unlock(&local->sta_mtx);
rcu_read_lock();
goto out_free;
}
- mutex_lock(&local->sta_mtx);
-
err = sta_info_insert_finish(sta);
if (err)
goto out_free;
@@ -868,6 +883,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
}
list_del_rcu(&sta->list);
+ sta->removed = true;
drv_sta_pre_rcu_remove(local, sta->sdata, sta);
@@ -1230,11 +1246,11 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
ieee80211_check_fast_xmit(sta);
}
-static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
- struct sta_info *sta, int tid,
+static void ieee80211_send_null_response(struct sta_info *sta, int tid,
enum ieee80211_frame_release_type reason,
- bool call_driver)
+ bool call_driver, bool more_data)
{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ieee80211_qos_hdr *nullfunc;
struct sk_buff *skb;
@@ -1274,9 +1290,13 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
if (qos) {
nullfunc->qos_ctrl = cpu_to_le16(tid);
- if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
+ if (reason == IEEE80211_FRAME_RELEASE_UAPSD) {
nullfunc->qos_ctrl |=
cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+ if (more_data)
+ nullfunc->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
}
info = IEEE80211_SKB_CB(skb);
@@ -1323,22 +1343,48 @@ static int find_highest_prio_tid(unsigned long tids)
return fls(tids) - 1;
}
+/* Indicates if the MORE_DATA bit should be set in the last
+ * frame obtained by ieee80211_sta_ps_get_frames.
+ * Note that driver_release_tids is relevant only if
+ * reason = IEEE80211_FRAME_RELEASE_PSPOLL
+ */
+static bool
+ieee80211_sta_ps_more_data(struct sta_info *sta, u8 ignored_acs,
+ enum ieee80211_frame_release_type reason,
+ unsigned long driver_release_tids)
+{
+ int ac;
+
+ /* If the driver has data on more than one TID then
+ * certainly there's more data if we release just a
+ * single frame now (from a single TID). This will
+ * only happen for PS-Poll.
+ */
+ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
+ hweight16(driver_release_tids) > 1)
+ return true;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ if (ignored_acs & BIT(ac))
+ continue;
+
+ if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
+ !skb_queue_empty(&sta->ps_tx_buf[ac]))
+ return true;
+ }
+
+ return false;
+}
+
static void
-ieee80211_sta_ps_deliver_response(struct sta_info *sta,
- int n_frames, u8 ignored_acs,
- enum ieee80211_frame_release_type reason)
+ieee80211_sta_ps_get_frames(struct sta_info *sta, int n_frames, u8 ignored_acs,
+ enum ieee80211_frame_release_type reason,
+ struct sk_buff_head *frames,
+ unsigned long *driver_release_tids)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
- bool more_data = false;
int ac;
- unsigned long driver_release_tids = 0;
- struct sk_buff_head frames;
-
- /* Service or PS-Poll period starts */
- set_sta_flag(sta, WLAN_STA_SP);
-
- __skb_queue_head_init(&frames);
/* Get response frame(s) and more data bit for the last one. */
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
@@ -1352,26 +1398,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* if we already have frames from software, then we can't also
* release from hardware queues
*/
- if (skb_queue_empty(&frames)) {
- driver_release_tids |= sta->driver_buffered_tids & tids;
- driver_release_tids |= sta->txq_buffered_tids & tids;
+ if (skb_queue_empty(frames)) {
+ *driver_release_tids |=
+ sta->driver_buffered_tids & tids;
+ *driver_release_tids |= sta->txq_buffered_tids & tids;
}
- if (driver_release_tids) {
- /* If the driver has data on more than one TID then
- * certainly there's more data if we release just a
- * single frame now (from a single TID). This will
- * only happen for PS-Poll.
- */
- if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
- hweight16(driver_release_tids) > 1) {
- more_data = true;
- driver_release_tids =
- BIT(find_highest_prio_tid(
- driver_release_tids));
- break;
- }
- } else {
+ if (!*driver_release_tids) {
struct sk_buff *skb;
while (n_frames > 0) {
@@ -1385,20 +1418,44 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
if (!skb)
break;
n_frames--;
- __skb_queue_tail(&frames, skb);
+ __skb_queue_tail(frames, skb);
}
}
- /* If we have more frames buffered on this AC, then set the
- * more-data bit and abort the loop since we can't send more
- * data from other ACs before the buffered frames from this.
+ /* If we have more frames buffered on this AC, then abort the
+ * loop since we can't send more data from other ACs before
+ * the buffered frames from this.
*/
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
- !skb_queue_empty(&sta->ps_tx_buf[ac])) {
- more_data = true;
+ !skb_queue_empty(&sta->ps_tx_buf[ac]))
break;
- }
}
+}
+
+static void
+ieee80211_sta_ps_deliver_response(struct sta_info *sta,
+ int n_frames, u8 ignored_acs,
+ enum ieee80211_frame_release_type reason)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ unsigned long driver_release_tids = 0;
+ struct sk_buff_head frames;
+ bool more_data;
+
+ /* Service or PS-Poll period starts */
+ set_sta_flag(sta, WLAN_STA_SP);
+
+ __skb_queue_head_init(&frames);
+
+ ieee80211_sta_ps_get_frames(sta, n_frames, ignored_acs, reason,
+ &frames, &driver_release_tids);
+
+ more_data = ieee80211_sta_ps_more_data(sta, ignored_acs, reason, driver_release_tids);
+
+ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL)
+ driver_release_tids =
+ BIT(find_highest_prio_tid(driver_release_tids));
if (skb_queue_empty(&frames) && !driver_release_tids) {
int tid;
@@ -1421,7 +1478,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* This will evaluate to 1, 3, 5 or 7. */
tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
- ieee80211_send_null_response(sdata, sta, tid, reason, true);
+ ieee80211_send_null_response(sta, tid, reason, true, false);
} else if (!driver_release_tids) {
struct sk_buff_head pending;
struct sk_buff *skb;
@@ -1521,8 +1578,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
if (need_null)
ieee80211_send_null_response(
- sdata, sta, find_highest_prio_tid(tids),
- reason, false);
+ sta, find_highest_prio_tid(tids),
+ reason, false, false);
sta_info_recalc_tim(sta);
} else {
@@ -1660,6 +1717,22 @@ void ieee80211_sta_eosp(struct ieee80211_sta *pubsta)
}
EXPORT_SYMBOL(ieee80211_sta_eosp);
+void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ enum ieee80211_frame_release_type reason;
+ bool more_data;
+
+ trace_api_send_eosp_nullfunc(sta->local, pubsta, tid);
+
+ reason = IEEE80211_FRAME_RELEASE_UAPSD;
+ more_data = ieee80211_sta_ps_more_data(sta, ~sta->sta.uapsd_queues,
+ reason, 0);
+
+ ieee80211_send_null_response(sta, tid, reason, false, more_data);
+}
+EXPORT_SYMBOL(ieee80211_send_eosp_nullfunc);
+
void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
u8 tid, bool buffered)
{
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 2cafb21b422f..d6051629ed15 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -367,6 +367,7 @@ DECLARE_EWMA(signal, 1024, 8)
* @mesh: mesh STA information
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
+ * @removed: set to true when sta is being removed from sta_list
* @uploaded: set to true when sta is uploaded to the driver
* @sta: station information we share with the driver
* @sta_state: duplicates information about station state (for debug)
@@ -412,6 +413,7 @@ struct sta_info {
u16 listen_interval;
bool dead;
+ bool removed;
bool uploaded;
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 56c6d6cfa5a1..a6b4442776a0 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -2027,6 +2027,31 @@ TRACE_EVENT(api_eosp,
)
);
+TRACE_EVENT(api_send_eosp_nullfunc,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sta *sta,
+ u8 tid),
+
+ TP_ARGS(local, sta, tid),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ STA_ENTRY
+ __field(u8, tid)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ STA_ASSIGN;
+ __entry->tid = tid;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT STA_PR_FMT " tid:%d",
+ LOCAL_PR_ARG, STA_PR_ARG, __entry->tid
+ )
+);
+
TRACE_EVENT(api_sta_set_buffered,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index bdc224d5053a..3311ce0f3d6c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1431,7 +1431,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
info->hw_queue =
vif->hw_queue[skb_get_queue_mapping(skb)];
} else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
- dev_kfree_skb(skb);
+ ieee80211_purge_tx_queue(&local->hw, skbs);
return true;
} else
vif = NULL;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 33344f5a66a8..3943d4bf289c 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -288,10 +288,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
if (!test_bit(reason, &local->queue_stop_reasons[queue]))
return;
- if (!refcounted)
+ if (!refcounted) {
local->q_stop_reasons[queue][reason] = 0;
- else
+ } else {
local->q_stop_reasons[queue][reason]--;
+ if (WARN_ON(local->q_stop_reasons[queue][reason] < 0))
+ local->q_stop_reasons[queue][reason] = 0;
+ }
if (local->q_stop_reasons[queue][reason] == 0)
__clear_bit(reason, &local->queue_stop_reasons[queue]);
diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h
index 0550f3365e33..fd9daf2ecec9 100644
--- a/net/mac802154/driver-ops.h
+++ b/net/mac802154/driver-ops.h
@@ -18,9 +18,6 @@ drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
static inline int
drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
{
- /* don't allow other operations while sync xmit */
- ASSERT_RTNL();
-
might_sleep();
return local->ops->xmit_sync(&local->hw, skb);
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index 8606da459ff3..3db16346cab3 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -126,7 +126,7 @@ static void mac802154_get_mac_params(struct net_device *dev,
params->lbt = wpan_dev->lbt;
}
-static struct ieee802154_llsec_ops mac802154_llsec_ops = {
+static const struct ieee802154_llsec_ops mac802154_llsec_ops = {
.get_params = mac802154_get_params,
.set_params = mac802154_set_params,
.add_key = mac802154_add_key,
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index 42e96729dae6..446e1300383e 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -217,8 +217,7 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local,
break;
}
- if (skb)
- kfree_skb(skb);
+ kfree_skb(skb);
}
static void
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index 3827f359b336..7e253455f9dd 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -38,12 +38,6 @@ void ieee802154_xmit_worker(struct work_struct *work)
struct net_device *dev = skb->dev;
int res;
- rtnl_lock();
-
- /* check if ifdown occurred while schedule */
- if (!netif_running(dev))
- goto err_tx;
-
res = drv_xmit_sync(local, skb);
if (res)
goto err_tx;
@@ -53,14 +47,11 @@ void ieee802154_xmit_worker(struct work_struct *work)
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
- rtnl_unlock();
-
return;
err_tx:
/* Restart the netif queue on each sub_if_data object. */
ieee802154_wake_queue(&local->hw);
- rtnl_unlock();
kfree_skb(skb);
netdev_dbg(dev, "transmission failed\n");
}
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index c32fc411a911..b18c5ed42d95 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -98,22 +98,15 @@ bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
}
EXPORT_SYMBOL_GPL(mpls_pkt_too_big);
-static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
- struct sk_buff *skb, bool bos)
+static u32 mpls_multipath_hash(struct mpls_route *rt,
+ struct sk_buff *skb, bool bos)
{
struct mpls_entry_decoded dec;
struct mpls_shim_hdr *hdr;
bool eli_seen = false;
int label_index;
- int nh_index = 0;
u32 hash = 0;
- /* No need to look further into packet if there's only
- * one path
- */
- if (rt->rt_nhn == 1)
- goto out;
-
for (label_index = 0; label_index < MAX_MP_SELECT_LABELS && !bos;
label_index++) {
if (!pskb_may_pull(skb, sizeof(*hdr) * label_index))
@@ -167,7 +160,38 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
}
}
- nh_index = hash % rt->rt_nhn;
+ return hash;
+}
+
+static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
+ struct sk_buff *skb, bool bos)
+{
+ int alive = ACCESS_ONCE(rt->rt_nhn_alive);
+ u32 hash = 0;
+ int nh_index = 0;
+ int n = 0;
+
+ /* No need to look further into packet if there's only
+ * one path
+ */
+ if (rt->rt_nhn == 1)
+ goto out;
+
+ if (alive <= 0)
+ return NULL;
+
+ hash = mpls_multipath_hash(rt, skb, bos);
+ nh_index = hash % alive;
+ if (alive == rt->rt_nhn)
+ goto out;
+ for_nexthops(rt) {
+ if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+ continue;
+ if (n == nh_index)
+ return nh;
+ n++;
+ } endfor_nexthops(rt);
+
out:
return &rt->rt_nh[nh_index];
}
@@ -373,6 +397,7 @@ static struct mpls_route *mpls_rt_alloc(int num_nh, u8 max_alen)
GFP_KERNEL);
if (rt) {
rt->rt_nhn = num_nh;
+ rt->rt_nhn_alive = num_nh;
rt->rt_max_alen = max_alen_aligned;
}
@@ -548,6 +573,16 @@ static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt,
RCU_INIT_POINTER(nh->nh_dev, dev);
+ if (!(dev->flags & IFF_UP)) {
+ nh->nh_flags |= RTNH_F_DEAD;
+ } else {
+ unsigned int flags;
+
+ flags = dev_get_flags(dev);
+ if (!(flags & (IFF_RUNNING | IFF_LOWER_UP)))
+ nh->nh_flags |= RTNH_F_LINKDOWN;
+ }
+
return 0;
errout:
@@ -582,6 +617,9 @@ static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
if (err)
goto errout;
+ if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+ rt->rt_nhn_alive--;
+
return 0;
errout:
@@ -589,8 +627,8 @@ errout:
}
static int mpls_nh_build(struct net *net, struct mpls_route *rt,
- struct mpls_nh *nh, int oif,
- struct nlattr *via, struct nlattr *newdst)
+ struct mpls_nh *nh, int oif, struct nlattr *via,
+ struct nlattr *newdst)
{
int err = -ENOMEM;
@@ -694,11 +732,13 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
}
err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh,
- rtnh->rtnh_ifindex, nla_via,
- nla_newdst);
+ rtnh->rtnh_ifindex, nla_via, nla_newdst);
if (err)
goto errout;
+ if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+ rt->rt_nhn_alive--;
+
rtnh = rtnh_next(rtnh, &remaining);
nhs++;
} endfor_nexthops(rt);
@@ -888,34 +928,74 @@ free:
return ERR_PTR(err);
}
-static void mpls_ifdown(struct net_device *dev)
+static void mpls_ifdown(struct net_device *dev, int event)
{
struct mpls_route __rcu **platform_label;
struct net *net = dev_net(dev);
- struct mpls_dev *mdev;
unsigned index;
platform_label = rtnl_dereference(net->mpls.platform_label);
for (index = 0; index < net->mpls.platform_labels; index++) {
struct mpls_route *rt = rtnl_dereference(platform_label[index]);
+
if (!rt)
continue;
- for_nexthops(rt) {
+
+ change_nexthops(rt) {
if (rtnl_dereference(nh->nh_dev) != dev)
continue;
- nh->nh_dev = NULL;
+ switch (event) {
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ nh->nh_flags |= RTNH_F_DEAD;
+ /* fall through */
+ case NETDEV_CHANGE:
+ nh->nh_flags |= RTNH_F_LINKDOWN;
+ ACCESS_ONCE(rt->rt_nhn_alive) = rt->rt_nhn_alive - 1;
+ break;
+ }
+ if (event == NETDEV_UNREGISTER)
+ RCU_INIT_POINTER(nh->nh_dev, NULL);
} endfor_nexthops(rt);
}
- mdev = mpls_dev_get(dev);
- if (!mdev)
- return;
- mpls_dev_sysctl_unregister(mdev);
+ return;
+}
+
+static void mpls_ifup(struct net_device *dev, unsigned int nh_flags)
+{
+ struct mpls_route __rcu **platform_label;
+ struct net *net = dev_net(dev);
+ unsigned index;
+ int alive;
+
+ platform_label = rtnl_dereference(net->mpls.platform_label);
+ for (index = 0; index < net->mpls.platform_labels; index++) {
+ struct mpls_route *rt = rtnl_dereference(platform_label[index]);
+
+ if (!rt)
+ continue;
+
+ alive = 0;
+ change_nexthops(rt) {
+ struct net_device *nh_dev =
+ rtnl_dereference(nh->nh_dev);
+
+ if (!(nh->nh_flags & nh_flags)) {
+ alive++;
+ continue;
+ }
+ if (nh_dev != dev)
+ continue;
+ alive++;
+ nh->nh_flags &= ~nh_flags;
+ } endfor_nexthops(rt);
- RCU_INIT_POINTER(dev->mpls_ptr, NULL);
+ ACCESS_ONCE(rt->rt_nhn_alive) = alive;
+ }
- kfree_rcu(mdev, rcu);
+ return;
}
static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
@@ -923,9 +1003,9 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct mpls_dev *mdev;
+ unsigned int flags;
- switch(event) {
- case NETDEV_REGISTER:
+ if (event == NETDEV_REGISTER) {
/* For now just support ethernet devices */
if ((dev->type == ARPHRD_ETHER) ||
(dev->type == ARPHRD_LOOPBACK)) {
@@ -933,10 +1013,39 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
if (IS_ERR(mdev))
return notifier_from_errno(PTR_ERR(mdev));
}
- break;
+ return NOTIFY_OK;
+ }
+ mdev = mpls_dev_get(dev);
+ if (!mdev)
+ return NOTIFY_OK;
+
+ switch (event) {
+ case NETDEV_DOWN:
+ mpls_ifdown(dev, event);
+ break;
+ case NETDEV_UP:
+ flags = dev_get_flags(dev);
+ if (flags & (IFF_RUNNING | IFF_LOWER_UP))
+ mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
+ else
+ mpls_ifup(dev, RTNH_F_DEAD);
+ break;
+ case NETDEV_CHANGE:
+ flags = dev_get_flags(dev);
+ if (flags & (IFF_RUNNING | IFF_LOWER_UP))
+ mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
+ else
+ mpls_ifdown(dev, event);
+ break;
case NETDEV_UNREGISTER:
- mpls_ifdown(dev);
+ mpls_ifdown(dev, event);
+ mdev = mpls_dev_get(dev);
+ if (mdev) {
+ mpls_dev_sysctl_unregister(mdev);
+ RCU_INIT_POINTER(dev->mpls_ptr, NULL);
+ kfree_rcu(mdev, rcu);
+ }
break;
case NETDEV_CHANGENAME:
mdev = mpls_dev_get(dev);
@@ -1252,9 +1361,15 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
dev = rtnl_dereference(nh->nh_dev);
if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
goto nla_put_failure;
+ if (nh->nh_flags & RTNH_F_LINKDOWN)
+ rtm->rtm_flags |= RTNH_F_LINKDOWN;
+ if (nh->nh_flags & RTNH_F_DEAD)
+ rtm->rtm_flags |= RTNH_F_DEAD;
} else {
struct rtnexthop *rtnh;
struct nlattr *mp;
+ int dead = 0;
+ int linkdown = 0;
mp = nla_nest_start(skb, RTA_MULTIPATH);
if (!mp)
@@ -1268,6 +1383,15 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
dev = rtnl_dereference(nh->nh_dev);
if (dev)
rtnh->rtnh_ifindex = dev->ifindex;
+ if (nh->nh_flags & RTNH_F_LINKDOWN) {
+ rtnh->rtnh_flags |= RTNH_F_LINKDOWN;
+ linkdown++;
+ }
+ if (nh->nh_flags & RTNH_F_DEAD) {
+ rtnh->rtnh_flags |= RTNH_F_DEAD;
+ dead++;
+ }
+
if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST,
nh->nh_labels,
nh->nh_label))
@@ -1282,6 +1406,11 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
} endfor_nexthops(rt);
+ if (linkdown == rt->rt_nhn)
+ rtm->rtm_flags |= RTNH_F_LINKDOWN;
+ if (dead == rt->rt_nhn)
+ rtm->rtm_flags |= RTNH_F_DEAD;
+
nla_nest_end(skb, mp);
}
diff --git a/net/mpls/internal.h b/net/mpls/internal.h
index bde52ce88c94..732a5c17e986 100644
--- a/net/mpls/internal.h
+++ b/net/mpls/internal.h
@@ -41,6 +41,7 @@ enum mpls_payload_type {
struct mpls_nh { /* next hop label forwarding entry */
struct net_device __rcu *nh_dev;
+ unsigned int nh_flags;
u32 nh_label[MAX_NEW_LABELS];
u8 nh_labels;
u8 nh_via_alen;
@@ -74,6 +75,7 @@ struct mpls_route { /* next hop label forwarding entry */
u8 rt_payload_type;
u8 rt_max_alen;
unsigned int rt_nhn;
+ unsigned int rt_nhn_alive;
struct mpls_nh rt_nh[0];
};
diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c
index 64afd3d0b144..fb31aa87de81 100644
--- a/net/mpls/mpls_iptunnel.c
+++ b/net/mpls/mpls_iptunnel.c
@@ -37,7 +37,7 @@ static unsigned int mpls_encap_size(struct mpls_iptunnel_encap *en)
return en->labels * sizeof(struct mpls_shim_hdr);
}
-int mpls_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+static int mpls_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct mpls_iptunnel_encap *tun_encap_info;
struct mpls_shim_hdr *hdr;
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 4692782b5280..8c067e6663a1 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -563,6 +563,28 @@ config NFT_COMPAT
x_tables match/target extensions over the nf_tables
framework.
+if NF_TABLES_NETDEV
+
+config NF_DUP_NETDEV
+ tristate "Netfilter packet duplication support"
+ help
+ This option enables the generic packet duplication infrastructure
+ for Netfilter.
+
+config NFT_DUP_NETDEV
+ tristate "Netfilter nf_tables netdev packet duplication support"
+ select NF_DUP_NETDEV
+ help
+ This option enables packet duplication for the "netdev" family.
+
+config NFT_FWD_NETDEV
+ tristate "Netfilter nf_tables netdev packet forwarding support"
+ select NF_DUP_NETDEV
+ help
+ This option enables packet forwarding for the "netdev" family.
+
+endif # NF_TABLES_NETDEV
+
endif # NF_TABLES
config NETFILTER_XTABLES
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 7638c36b498c..69134541d65b 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -66,8 +66,11 @@ obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o
# SYNPROXY
obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o
+# generic packet duplication from netdev family
+obj-$(CONFIG_NF_DUP_NETDEV) += nf_dup_netdev.o
+
# nf_tables
-nf_tables-objs += nf_tables_core.o nf_tables_api.o
+nf_tables-objs += nf_tables_core.o nf_tables_api.o nf_tables_trace.o
nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o
nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
@@ -90,6 +93,10 @@ obj-$(CONFIG_NFT_LOG) += nft_log.o
obj-$(CONFIG_NFT_MASQ) += nft_masq.o
obj-$(CONFIG_NFT_REDIR) += nft_redir.o
+# nf_tables netdev
+obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o
+obj-$(CONFIG_NFT_FWD_NETDEV) += nft_fwd_netdev.o
+
# generic X tables
obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 54f3d7cb23e6..95db43fc0303 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -825,20 +825,17 @@ find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index,
return 0;
}
-static int
-ip_set_none(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_none(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
return -EOPNOTSUPP;
}
-static int
-ip_set_create(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_create(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct net *net = sock_net(ctnl);
struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set, *clash = NULL;
ip_set_id_t index = IPSET_INVALID_ID;
@@ -976,12 +973,11 @@ ip_set_destroy_set(struct ip_set *set)
kfree(set);
}
-static int
-ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_destroy(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *s;
ip_set_id_t i;
int ret = 0;
@@ -1052,12 +1048,11 @@ ip_set_flush_set(struct ip_set *set)
spin_unlock_bh(&set->lock);
}
-static int
-ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_flush(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *s;
ip_set_id_t i;
@@ -1092,12 +1087,11 @@ ip_set_setname2_policy[IPSET_ATTR_CMD_MAX + 1] = {
.len = IPSET_MAXNAMELEN - 1 },
};
-static int
-ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_rename(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set, *s;
const char *name2;
ip_set_id_t i;
@@ -1142,12 +1136,11 @@ out:
* so the ip_set_list always contains valid pointers to the sets.
*/
-static int
-ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_swap(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *from, *to;
ip_set_id_t from_id, to_id;
char from_name[IPSET_MAXNAMELEN];
@@ -1413,10 +1406,9 @@ out:
return ret < 0 ? ret : skb->len;
}
-static int
-ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_dump(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
if (unlikely(protocol_failed(attr)))
return -IPSET_ERR_PROTOCOL;
@@ -1500,12 +1492,11 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
return ret;
}
-static int
-ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_uadd(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
const struct nlattr *nla;
@@ -1555,12 +1546,11 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
return ret;
}
-static int
-ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_udel(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
const struct nlattr *nla;
@@ -1610,12 +1600,11 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
return ret;
}
-static int
-ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_utest(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
int ret = 0;
@@ -1646,12 +1635,11 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
/* Get headed data of a set */
-static int
-ip_set_header(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_header(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
const struct ip_set *set;
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
@@ -1703,10 +1691,9 @@ static const struct nla_policy ip_set_type_policy[IPSET_ATTR_CMD_MAX + 1] = {
[IPSET_ATTR_FAMILY] = { .type = NLA_U8 },
};
-static int
-ip_set_type(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_type(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
@@ -1762,10 +1749,9 @@ ip_set_protocol_policy[IPSET_ATTR_CMD_MAX + 1] = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
};
-static int
-ip_set_protocol(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_protocol(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 010ddeec135f..d952d67f904d 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
/* Only update csum if we really have to */
if (sctph->dest != cp->dport || payload_csum ||
(skb->ip_summed == CHECKSUM_PARTIAL &&
- !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CSUM))) {
+ !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CRC))) {
sctph->dest = cp->dport;
sctp_nat_csum(skb, sctph, sctphoff);
} else if (skb->ip_summed != CHECKSUM_PARTIAL) {
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index acf5c7b3f378..278927ab0948 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -596,11 +596,18 @@ static int exp_proc_init(struct net *net)
{
#ifdef CONFIG_NF_CONNTRACK_PROCFS
struct proc_dir_entry *proc;
+ kuid_t root_uid;
+ kgid_t root_gid;
proc = proc_create("nf_conntrack_expect", 0440, net->proc_net,
&exp_file_ops);
if (!proc)
return -ENOMEM;
+
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
#endif /* CONFIG_NF_CONNTRACK_PROCFS */
return 0;
}
diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c
index b666959f17c0..883c691ec8d0 100644
--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -10,6 +10,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netfilter.h>
@@ -505,11 +507,11 @@ skip_nl_seq:
different IP address. Simply don't record it for
NAT. */
if (cmd.l3num == PF_INET) {
- pr_debug("conntrack_ftp: NOT RECORDING: %pI4 != %pI4\n",
+ pr_debug("NOT RECORDING: %pI4 != %pI4\n",
&cmd.u3.ip,
&ct->tuplehash[dir].tuple.src.u3.ip);
} else {
- pr_debug("conntrack_ftp: NOT RECORDING: %pI6 != %pI6\n",
+ pr_debug("NOT RECORDING: %pI6 != %pI6\n",
cmd.u3.ip6,
ct->tuplehash[dir].tuple.src.u3.ip6);
}
@@ -586,8 +588,7 @@ static void nf_conntrack_ftp_fini(void)
if (ftp[i][j].me == NULL)
continue;
- pr_debug("nf_ct_ftp: unregistering helper for pf: %d "
- "port: %d\n",
+ pr_debug("unregistering helper for pf: %d port: %d\n",
ftp[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_helper_unregister(&ftp[i][j]);
}
@@ -625,14 +626,12 @@ static int __init nf_conntrack_ftp_init(void)
else
sprintf(ftp[i][j].name, "ftp-%d", ports[i]);
- pr_debug("nf_ct_ftp: registering helper for pf: %d "
- "port: %d\n",
+ pr_debug("registering helper for pf: %d port: %d\n",
ftp[i][j].tuple.src.l3num, ports[i]);
ret = nf_conntrack_helper_register(&ftp[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_ftp: failed to register"
- " helper for pf: %d port: %d\n",
- ftp[i][j].tuple.src.l3num, ports[i]);
+ pr_err("failed to register helper for pf: %d port: %d\n",
+ ftp[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_ftp_fini();
return ret;
}
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
index 0fd2976db7ee..8b6da2719600 100644
--- a/net/netfilter/nf_conntrack_irc.c
+++ b/net/netfilter/nf_conntrack_irc.c
@@ -9,6 +9,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
@@ -237,7 +239,7 @@ static int __init nf_conntrack_irc_init(void)
int i, ret;
if (max_dcc_channels < 1) {
- printk(KERN_ERR "nf_ct_irc: max_dcc_channels must not be zero\n");
+ pr_err("max_dcc_channels must not be zero\n");
return -EINVAL;
}
@@ -267,8 +269,7 @@ static int __init nf_conntrack_irc_init(void)
ret = nf_conntrack_helper_register(&irc[i]);
if (ret) {
- printk(KERN_ERR "nf_ct_irc: failed to register helper "
- "for pf: %u port: %u\n",
+ pr_err("failed to register helper for pf: %u port: %u\n",
irc[i].tuple.src.l3num, ports[i]);
nf_conntrack_irc_fini();
return ret;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 9f5272968abb..dbb1bb3edb45 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1113,12 +1113,11 @@ static int ctnetlink_flush_conntrack(struct net *net,
return 0;
}
-static int
-ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_del_conntrack(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conn *ct;
@@ -1168,12 +1167,11 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
return 0;
}
-static int
-ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_conntrack(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conn *ct;
@@ -1330,10 +1328,10 @@ ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb)
return ctnetlink_dump_list(skb, cb, true);
}
-static int
-ctnetlink_get_ct_dying(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_ct_dying(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
@@ -1352,10 +1350,10 @@ ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb)
return ctnetlink_dump_list(skb, cb, false);
}
-static int
-ctnetlink_get_ct_unconfirmed(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_ct_unconfirmed(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
@@ -1865,12 +1863,11 @@ err1:
return ERR_PTR(err);
}
-static int
-ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_new_conntrack(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple otuple, rtuple;
struct nf_conntrack_tuple_hash *h = NULL;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -2034,10 +2031,10 @@ ctnetlink_ct_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static int
-ctnetlink_stat_ct_cpu(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_stat_ct_cpu(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
@@ -2080,10 +2077,9 @@ nlmsg_failure:
return -1;
}
-static int
-ctnetlink_stat_ct(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_stat_ct(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
struct sk_buff *skb2;
int err;
@@ -2729,12 +2725,12 @@ out:
return skb->len;
}
-static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb,
+static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
int err;
- struct net *net = sock_net(ctnl);
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int8_t u3 = nfmsg->nfgen_family;
struct nf_conntrack_tuple tuple;
@@ -2768,12 +2764,10 @@ static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb,
return err;
}
-static int
-ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_expect(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct sk_buff *skb2;
@@ -2784,7 +2778,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
if (nlh->nlmsg_flags & NLM_F_DUMP) {
if (cda[CTA_EXPECT_MASTER])
- return ctnetlink_dump_exp_ct(ctnl, skb, nlh, cda);
+ return ctnetlink_dump_exp_ct(net, ctnl, skb, nlh, cda);
else {
struct netlink_dump_control c = {
.dump = ctnetlink_exp_dump_table,
@@ -2850,12 +2844,10 @@ out:
return err == -EAGAIN ? -ENOBUFS : err;
}
-static int
-ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_del_expect(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_expect *exp;
struct nf_conntrack_tuple tuple;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -3136,12 +3128,10 @@ err_ct:
return err;
}
-static int
-ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_new_expect(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -3242,10 +3232,10 @@ ctnetlink_exp_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static int
-ctnetlink_stat_exp_cpu(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_stat_exp_cpu(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c
index 4a2134fd3fcb..7523a575f6d1 100644
--- a/net/netfilter/nf_conntrack_sane.c
+++ b/net/netfilter/nf_conntrack_sane.c
@@ -17,6 +17,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netfilter.h>
@@ -120,14 +122,14 @@ static int help(struct sk_buff *skb,
ct_sane_info->state = SANE_STATE_NORMAL;
if (datalen < sizeof(struct sane_reply_net_start)) {
- pr_debug("nf_ct_sane: NET_START reply too short\n");
+ pr_debug("NET_START reply too short\n");
goto out;
}
reply = sb_ptr;
if (reply->status != htonl(SANE_STATUS_SUCCESS)) {
/* saned refused the command */
- pr_debug("nf_ct_sane: unsuccessful SANE_STATUS = %u\n",
+ pr_debug("unsuccessful SANE_STATUS = %u\n",
ntohl(reply->status));
goto out;
}
@@ -148,7 +150,7 @@ static int help(struct sk_buff *skb,
&tuple->src.u3, &tuple->dst.u3,
IPPROTO_TCP, NULL, &reply->port);
- pr_debug("nf_ct_sane: expect: ");
+ pr_debug("expect: ");
nf_ct_dump_tuple(&exp->tuple);
/* Can't expect this? Best to drop packet now. */
@@ -178,8 +180,7 @@ static void nf_conntrack_sane_fini(void)
for (i = 0; i < ports_c; i++) {
for (j = 0; j < 2; j++) {
- pr_debug("nf_ct_sane: unregistering helper for pf: %d "
- "port: %d\n",
+ pr_debug("unregistering helper for pf: %d port: %d\n",
sane[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_helper_unregister(&sane[i][j]);
}
@@ -216,14 +217,12 @@ static int __init nf_conntrack_sane_init(void)
else
sprintf(sane[i][j].name, "sane-%d", ports[i]);
- pr_debug("nf_ct_sane: registering helper for pf: %d "
- "port: %d\n",
+ pr_debug("registering helper for pf: %d port: %d\n",
sane[i][j].tuple.src.l3num, ports[i]);
ret = nf_conntrack_helper_register(&sane[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_sane: failed to "
- "register helper for pf: %d port: %d\n",
- sane[i][j].tuple.src.l3num, ports[i]);
+ pr_err("failed to register helper for pf: %d port: %d\n",
+ sane[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_sane_fini();
return ret;
}
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 885b4aba3695..3e06402739e0 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -10,6 +10,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/skbuff.h>
@@ -1665,8 +1667,7 @@ static int __init nf_conntrack_sip_init(void)
ret = nf_conntrack_helper_register(&sip[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_sip: failed to register"
- " helper for pf: %u port: %u\n",
+ pr_err("failed to register helper for pf: %u port: %u\n",
sip[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_sip_fini();
return ret;
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 1fb3cacc04e1..0f1a45bcacb2 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -392,11 +392,18 @@ static const struct file_operations ct_cpu_seq_fops = {
static int nf_conntrack_standalone_init_proc(struct net *net)
{
struct proc_dir_entry *pde;
+ kuid_t root_uid;
+ kgid_t root_gid;
pde = proc_create("nf_conntrack", 0440, net->proc_net, &ct_file_ops);
if (!pde)
goto out_nf_conntrack;
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(pde, root_uid, root_gid);
+
pde = proc_create("nf_conntrack", S_IRUGO, net->proc_net_stat,
&ct_cpu_seq_fops);
if (!pde)
diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c
index e68ab4fbd71f..36f964066461 100644
--- a/net/netfilter/nf_conntrack_tftp.c
+++ b/net/netfilter/nf_conntrack_tftp.c
@@ -5,6 +5,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/in.h>
@@ -138,9 +140,8 @@ static int __init nf_conntrack_tftp_init(void)
ret = nf_conntrack_helper_register(&tftp[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_tftp: failed to register"
- " helper for pf: %u port: %u\n",
- tftp[i][j].tuple.src.l3num, ports[i]);
+ pr_err("failed to register helper for pf: %u port: %u\n",
+ tftp[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_tftp_fini();
return ret;
}
diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c
index 93da609d9d29..26e742006c48 100644
--- a/net/netfilter/nf_conntrack_timeout.c
+++ b/net/netfilter/nf_conntrack_timeout.c
@@ -25,7 +25,7 @@
#include <net/netfilter/nf_conntrack_timeout.h>
struct ctnl_timeout *
-(*nf_ct_timeout_find_get_hook)(const char *name) __read_mostly;
+(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name) __read_mostly;
EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook);
void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout) __read_mostly;
diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c
new file mode 100644
index 000000000000..8414ee1a0319
--- /dev/null
+++ b/net/netfilter/nf_dup_netdev.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif)
+{
+ struct net_device *dev;
+ struct sk_buff *skb;
+
+ dev = dev_get_by_index_rcu(pkt->net, oif);
+ if (dev == NULL)
+ return;
+
+ skb = skb_clone(pkt->skb, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ if (skb_mac_header_was_set(skb))
+ skb_push(skb, skb->mac_len);
+
+ skb->dev = dev;
+ skb_sender_cpu_clear(skb);
+ dev_queue_xmit(skb);
+}
+EXPORT_SYMBOL_GPL(nf_dup_netdev_egress);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 2cb429d34c03..2011977cd79d 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -41,6 +41,8 @@ int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
}
EXPORT_SYMBOL_GPL(nft_register_afinfo);
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi);
+
/**
* nft_unregister_afinfo - unregister nf_tables address family info
*
@@ -48,9 +50,10 @@ EXPORT_SYMBOL_GPL(nft_register_afinfo);
*
* Unregister the address family for use with nf_tables.
*/
-void nft_unregister_afinfo(struct nft_af_info *afi)
+void nft_unregister_afinfo(struct net *net, struct nft_af_info *afi)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
+ __nft_release_afinfo(net, afi);
list_del_rcu(&afi->list);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
@@ -128,8 +131,8 @@ static void nft_trans_destroy(struct nft_trans *trans)
kfree(trans);
}
-int nft_register_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops)
+static int nft_register_basechain(struct nft_base_chain *basechain,
+ unsigned int hook_nops)
{
struct net *net = read_pnet(&basechain->pnet);
@@ -138,10 +141,9 @@ int nft_register_basechain(struct nft_base_chain *basechain,
return nf_register_net_hooks(net, basechain->ops, hook_nops);
}
-EXPORT_SYMBOL_GPL(nft_register_basechain);
-void nft_unregister_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops)
+static void nft_unregister_basechain(struct nft_base_chain *basechain,
+ unsigned int hook_nops)
{
struct net *net = read_pnet(&basechain->pnet);
@@ -150,7 +152,6 @@ void nft_unregister_basechain(struct nft_base_chain *basechain,
nf_unregister_net_hooks(net, basechain->ops, hook_nops);
}
-EXPORT_SYMBOL_GPL(nft_unregister_basechain);
static int nf_tables_register_hooks(const struct nft_table *table,
struct nft_chain *chain,
@@ -542,15 +543,14 @@ done:
return skb->len;
}
-static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_gettable(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi;
const struct nft_table *table;
struct sk_buff *skb2;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
@@ -831,8 +831,6 @@ static int nf_tables_deltable(struct net *net, struct sock *nlsk,
table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
ctx.afi = afi;
ctx.table = table;
@@ -1098,8 +1096,8 @@ done:
return skb->len;
}
-static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getchain(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -1107,7 +1105,6 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
const struct nft_table *table;
const struct nft_chain *chain;
struct sk_buff *skb2;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
@@ -1492,14 +1489,10 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
if (IS_ERR(chain))
return PTR_ERR(chain);
- if (chain->flags & NFT_CHAIN_INACTIVE)
- return -ENOENT;
if (chain->use > 0)
return -EBUSY;
@@ -1928,8 +1921,8 @@ done:
return skb->len;
}
-static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getrule(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -1938,7 +1931,6 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
const struct nft_chain *chain;
const struct nft_rule *rule;
struct sk_buff *skb2;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
@@ -2191,8 +2183,6 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
if (nla[NFTA_RULE_CHAIN]) {
chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
@@ -2333,6 +2323,8 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
[NFTA_SET_ID] = { .type = NLA_U32 },
[NFTA_SET_TIMEOUT] = { .type = NLA_U64 },
[NFTA_SET_GC_INTERVAL] = { .type = NLA_U32 },
+ [NFTA_SET_USERDATA] = { .type = NLA_BINARY,
+ .len = NFT_USERDATA_MAXLEN },
};
static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
@@ -2361,8 +2353,6 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net,
table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
}
nft_ctx_init(ctx, net, skb, nlh, afi, table, NULL, nla);
@@ -2494,6 +2484,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
goto nla_put_failure;
}
+ if (nla_put(skb, NFTA_SET_USERDATA, set->udlen, set->udata))
+ goto nla_put_failure;
+
desc = nla_nest_start(skb, NFTA_SET_DESC);
if (desc == NULL)
goto nla_put_failure;
@@ -2613,11 +2606,10 @@ static int nf_tables_dump_sets_done(struct netlink_callback *cb)
return 0;
}
-static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getset(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
- struct net *net = sock_net(skb->sk);
const struct nft_set *set;
struct nft_ctx ctx;
struct sk_buff *skb2;
@@ -2704,6 +2696,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
u64 timeout;
u32 ktype, dtype, flags, policy, gc_int;
struct nft_set_desc desc;
+ unsigned char *udata;
+ u16 udlen;
int err;
if (nla[NFTA_SET_TABLE] == NULL ||
@@ -2816,12 +2810,16 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
if (IS_ERR(ops))
return PTR_ERR(ops);
+ udlen = 0;
+ if (nla[NFTA_SET_USERDATA])
+ udlen = nla_len(nla[NFTA_SET_USERDATA]);
+
size = 0;
if (ops->privsize != NULL)
size = ops->privsize(nla);
err = -ENOMEM;
- set = kzalloc(sizeof(*set) + size, GFP_KERNEL);
+ set = kzalloc(sizeof(*set) + size + udlen, GFP_KERNEL);
if (set == NULL)
goto err1;
@@ -2830,6 +2828,12 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
if (err < 0)
goto err2;
+ udata = NULL;
+ if (udlen) {
+ udata = set->data + size;
+ nla_memcpy(udata, nla[NFTA_SET_USERDATA], udlen);
+ }
+
INIT_LIST_HEAD(&set->bindings);
write_pnet(&set->pnet, net);
set->ops = ops;
@@ -2840,6 +2844,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
set->flags = flags;
set->size = desc.size;
set->policy = policy;
+ set->udlen = udlen;
+ set->udata = udata;
set->timeout = timeout;
set->gc_int = gc_int;
@@ -2897,8 +2903,6 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk,
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
if (IS_ERR(set))
return PTR_ERR(set);
- if (set->flags & NFT_SET_INACTIVE)
- return -ENOENT;
if (!list_empty(&set->bindings))
return -EBUSY;
@@ -3021,8 +3025,7 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
- const struct nlattr * const nla[],
- bool trans)
+ const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi;
@@ -3035,8 +3038,6 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (!trans && (table->flags & NFT_TABLE_INACTIVE))
- return -ENOENT;
nft_ctx_init(ctx, net, skb, nlh, afi, table, NULL, nla);
return 0;
@@ -3145,9 +3146,11 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
return err;
err = nft_ctx_init_from_elemattr(&ctx, net, cb->skb, cb->nlh,
- (void *)nla, false);
+ (void *)nla);
if (err < 0)
return err;
+ if (ctx.table->flags & NFT_TABLE_INACTIVE)
+ return -ENOENT;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
if (IS_ERR(set))
@@ -3202,18 +3205,19 @@ nla_put_failure:
return -ENOSPC;
}
-static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
- struct net *net = sock_net(skb->sk);
const struct nft_set *set;
struct nft_ctx ctx;
int err;
- err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, false);
+ err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
+ if (ctx.table->flags & NFT_TABLE_INACTIVE)
+ return -ENOENT;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
if (IS_ERR(set))
@@ -3535,7 +3539,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
return -EINVAL;
- err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, true);
+ err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
@@ -3629,7 +3633,7 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
return -EINVAL;
- err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, false);
+ err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
@@ -3733,11 +3737,10 @@ err:
return err;
}
-static int nf_tables_getgen(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getgen(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
- struct net *net = sock_net(skb->sk);
struct sk_buff *skb2;
int err;
@@ -3881,9 +3884,8 @@ static void nf_tables_commit_release(struct nft_trans *trans)
kfree(trans);
}
-static int nf_tables_commit(struct sk_buff *skb)
+static int nf_tables_commit(struct net *net, struct sk_buff *skb)
{
- struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
@@ -4018,9 +4020,8 @@ static void nf_tables_abort_release(struct nft_trans *trans)
kfree(trans);
}
-static int nf_tables_abort(struct sk_buff *skb)
+static int nf_tables_abort(struct net *net, struct sk_buff *skb)
{
- struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
@@ -4441,22 +4442,22 @@ static void nft_verdict_uninit(const struct nft_data *data)
}
}
-static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
+int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v)
{
struct nlattr *nest;
- nest = nla_nest_start(skb, NFTA_DATA_VERDICT);
+ nest = nla_nest_start(skb, type);
if (!nest)
goto nla_put_failure;
- if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict.code)))
+ if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(v->code)))
goto nla_put_failure;
- switch (data->verdict.code) {
+ switch (v->code) {
case NFT_JUMP:
case NFT_GOTO:
if (nla_put_string(skb, NFTA_VERDICT_CHAIN,
- data->verdict.chain->name))
+ v->chain->name))
goto nla_put_failure;
}
nla_nest_end(skb, nest);
@@ -4567,7 +4568,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
err = nft_value_dump(skb, data, len);
break;
case NFT_DATA_VERDICT:
- err = nft_verdict_dump(skb, data);
+ err = nft_verdict_dump(skb, NFTA_DATA_VERDICT, &data->verdict);
break;
default:
err = -EINVAL;
@@ -4579,7 +4580,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
}
EXPORT_SYMBOL_GPL(nft_data_dump);
-static int nf_tables_init_net(struct net *net)
+static int __net_init nf_tables_init_net(struct net *net)
{
INIT_LIST_HEAD(&net->nft.af_info);
INIT_LIST_HEAD(&net->nft.commit_list);
@@ -4587,6 +4588,67 @@ static int nf_tables_init_net(struct net *net)
return 0;
}
+int __nft_release_basechain(struct nft_ctx *ctx)
+{
+ struct nft_rule *rule, *nr;
+
+ BUG_ON(!(ctx->chain->flags & NFT_BASE_CHAIN));
+
+ nf_tables_unregister_hooks(ctx->chain->table, ctx->chain,
+ ctx->afi->nops);
+ list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
+ list_del(&rule->list);
+ ctx->chain->use--;
+ nf_tables_rule_destroy(ctx, rule);
+ }
+ list_del(&ctx->chain->list);
+ ctx->table->use--;
+ nf_tables_chain_destroy(ctx->chain);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__nft_release_basechain);
+
+/* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
+{
+ struct nft_table *table, *nt;
+ struct nft_chain *chain, *nc;
+ struct nft_rule *rule, *nr;
+ struct nft_set *set, *ns;
+ struct nft_ctx ctx = {
+ .net = net,
+ .afi = afi,
+ };
+
+ list_for_each_entry_safe(table, nt, &afi->tables, list) {
+ list_for_each_entry(chain, &table->chains, list)
+ nf_tables_unregister_hooks(table, chain, afi->nops);
+ /* No packets are walking on these chains anymore. */
+ ctx.table = table;
+ list_for_each_entry(chain, &table->chains, list) {
+ ctx.chain = chain;
+ list_for_each_entry_safe(rule, nr, &chain->rules, list) {
+ list_del(&rule->list);
+ chain->use--;
+ nf_tables_rule_destroy(&ctx, rule);
+ }
+ }
+ list_for_each_entry_safe(set, ns, &table->sets, list) {
+ list_del(&set->list);
+ table->use--;
+ nft_set_destroy(set);
+ }
+ list_for_each_entry_safe(chain, nc, &table->chains, list) {
+ list_del(&chain->list);
+ table->use--;
+ nf_tables_chain_destroy(chain);
+ }
+ list_del(&table->list);
+ nf_tables_table_destroy(&ctx);
+ }
+}
+
static struct pernet_operations nf_tables_net_ops = {
.init = nf_tables_init_net,
};
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c
index f3695a497408..e9f8dffcc244 100644
--- a/net/netfilter/nf_tables_core.c
+++ b/net/netfilter/nf_tables_core.c
@@ -16,22 +16,17 @@
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
+#include <linux/static_key.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_log.h>
-enum nft_trace {
- NFT_TRACE_RULE,
- NFT_TRACE_RETURN,
- NFT_TRACE_POLICY,
-};
-
-static const char *const comments[] = {
- [NFT_TRACE_RULE] = "rule",
- [NFT_TRACE_RETURN] = "return",
- [NFT_TRACE_POLICY] = "policy",
+static const char *const comments[__NFT_TRACETYPE_MAX] = {
+ [NFT_TRACETYPE_POLICY] = "policy",
+ [NFT_TRACETYPE_RETURN] = "return",
+ [NFT_TRACETYPE_RULE] = "rule",
};
static struct nf_loginfo trace_loginfo = {
@@ -44,22 +39,36 @@ static struct nf_loginfo trace_loginfo = {
},
};
-static void __nft_trace_packet(const struct nft_pktinfo *pkt,
- const struct nft_chain *chain,
- int rulenum, enum nft_trace type)
+static noinline void __nft_trace_packet(struct nft_traceinfo *info,
+ const struct nft_chain *chain,
+ int rulenum, enum nft_trace_types type)
{
+ const struct nft_pktinfo *pkt = info->pkt;
+
+ if (!info->trace || !pkt->skb->nf_trace)
+ return;
+
+ info->chain = chain;
+ info->type = type;
+
+ nft_trace_notify(info);
+
nf_log_trace(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in,
pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
chain->table->name, chain->name, comments[type],
rulenum);
}
-static inline void nft_trace_packet(const struct nft_pktinfo *pkt,
+static inline void nft_trace_packet(struct nft_traceinfo *info,
const struct nft_chain *chain,
- int rulenum, enum nft_trace type)
+ const struct nft_rule *rule,
+ int rulenum,
+ enum nft_trace_types type)
{
- if (unlikely(pkt->skb->nf_trace))
- __nft_trace_packet(pkt, chain, rulenum, type);
+ if (static_branch_unlikely(&nft_trace_enabled)) {
+ info->rule = rule;
+ __nft_trace_packet(info, chain, rulenum, type);
+ }
}
static void nft_cmp_fast_eval(const struct nft_expr *expr,
@@ -121,7 +130,11 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv)
struct nft_stats *stats;
int rulenum;
unsigned int gencursor = nft_genmask_cur(net);
+ struct nft_traceinfo info;
+ info.trace = false;
+ if (static_branch_unlikely(&nft_trace_enabled))
+ nft_trace_init(&info, pkt, &regs.verdict, basechain);
do_chain:
rulenum = 0;
rule = list_entry(&chain->rules, struct nft_rule, list);
@@ -151,7 +164,8 @@ next_rule:
regs.verdict.code = NFT_CONTINUE;
continue;
case NFT_CONTINUE:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RULE);
continue;
}
break;
@@ -161,7 +175,8 @@ next_rule:
case NF_ACCEPT:
case NF_DROP:
case NF_QUEUE:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RULE);
return regs.verdict.code;
}
@@ -174,7 +189,8 @@ next_rule:
stackptr++;
/* fall through */
case NFT_GOTO:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RULE);
chain = regs.verdict.chain;
goto do_chain;
@@ -182,7 +198,8 @@ next_rule:
rulenum++;
/* fall through */
case NFT_RETURN:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RETURN);
break;
default:
WARN_ON(1);
@@ -196,7 +213,8 @@ next_rule:
goto next_rule;
}
- nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY);
+ nft_trace_packet(&info, basechain, NULL, -1,
+ NFT_TRACETYPE_POLICY);
rcu_read_lock_bh();
stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats));
diff --git a/net/netfilter/nf_tables_inet.c b/net/netfilter/nf_tables_inet.c
index 9dd2d216cfc1..6b5f76295d3d 100644
--- a/net/netfilter/nf_tables_inet.c
+++ b/net/netfilter/nf_tables_inet.c
@@ -57,7 +57,7 @@ err:
static void __net_exit nf_tables_inet_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.inet);
+ nft_unregister_afinfo(net, net->nft.inet);
kfree(net->nft.inet);
}
diff --git a/net/netfilter/nf_tables_netdev.c b/net/netfilter/nf_tables_netdev.c
index edb3502f2016..b6605e000801 100644
--- a/net/netfilter/nf_tables_netdev.c
+++ b/net/netfilter/nf_tables_netdev.c
@@ -139,7 +139,7 @@ err:
static void nf_tables_netdev_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.netdev);
+ nft_unregister_afinfo(net, net->nft.netdev);
kfree(net->nft.netdev);
}
@@ -156,35 +156,17 @@ static const struct nf_chain_type nft_filter_chain_netdev = {
.hook_mask = (1 << NF_NETDEV_INGRESS),
};
-static void nft_netdev_event(unsigned long event, struct nft_af_info *afi,
- struct net_device *dev, struct nft_table *table,
- struct nft_base_chain *basechain)
+static void nft_netdev_event(unsigned long event, struct net_device *dev,
+ struct nft_ctx *ctx)
{
- switch (event) {
- case NETDEV_REGISTER:
- if (strcmp(basechain->dev_name, dev->name) != 0)
- return;
+ struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
- BUG_ON(!(basechain->flags & NFT_BASECHAIN_DISABLED));
-
- dev_hold(dev);
- basechain->ops[0].dev = dev;
- basechain->flags &= ~NFT_BASECHAIN_DISABLED;
- if (!(table->flags & NFT_TABLE_F_DORMANT))
- nft_register_basechain(basechain, afi->nops);
- break;
+ switch (event) {
case NETDEV_UNREGISTER:
if (strcmp(basechain->dev_name, dev->name) != 0)
return;
- BUG_ON(basechain->flags & NFT_BASECHAIN_DISABLED);
-
- if (!(table->flags & NFT_TABLE_F_DORMANT))
- nft_unregister_basechain(basechain, afi->nops);
-
- dev_put(basechain->ops[0].dev);
- basechain->ops[0].dev = NULL;
- basechain->flags |= NFT_BASECHAIN_DISABLED;
+ __nft_release_basechain(ctx);
break;
case NETDEV_CHANGENAME:
if (dev->ifindex != basechain->ops[0].dev->ifindex)
@@ -201,20 +183,29 @@ static int nf_tables_netdev_event(struct notifier_block *this,
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nft_af_info *afi;
struct nft_table *table;
- struct nft_chain *chain;
+ struct nft_chain *chain, *nr;
+ struct nft_ctx ctx = {
+ .net = dev_net(dev),
+ };
+
+ if (event != NETDEV_UNREGISTER &&
+ event != NETDEV_CHANGENAME)
+ return NOTIFY_DONE;
nfnl_lock(NFNL_SUBSYS_NFTABLES);
list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
+ ctx.afi = afi;
if (afi->family != NFPROTO_NETDEV)
continue;
list_for_each_entry(table, &afi->tables, list) {
- list_for_each_entry(chain, &table->chains, list) {
+ ctx.table = table;
+ list_for_each_entry_safe(chain, nr, &table->chains, list) {
if (!(chain->flags & NFT_BASE_CHAIN))
continue;
- nft_netdev_event(event, afi, dev, table,
- nft_base_chain(chain));
+ ctx.chain = chain;
+ nft_netdev_event(event, dev, &ctx);
}
}
}
diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c
new file mode 100644
index 000000000000..e9e959f65d91
--- /dev/null
+++ b/net/netfilter/nf_tables_trace.c
@@ -0,0 +1,275 @@
+/*
+ * (C) 2015 Red Hat GmbH
+ * Author: Florian Westphal <fw@strlen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/static_key.h>
+#include <linux/hash.h>
+#include <linux/jhash.h>
+#include <linux/if_vlan.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+#define NFT_TRACETYPE_LL_HSIZE 20
+#define NFT_TRACETYPE_NETWORK_HSIZE 40
+#define NFT_TRACETYPE_TRANSPORT_HSIZE 20
+
+DEFINE_STATIC_KEY_FALSE(nft_trace_enabled);
+EXPORT_SYMBOL_GPL(nft_trace_enabled);
+
+static int trace_fill_id(struct sk_buff *nlskb, struct sk_buff *skb)
+{
+ __be32 id;
+
+ /* using skb address as ID results in a limited number of
+ * values (and quick reuse).
+ *
+ * So we attempt to use as many skb members that will not
+ * change while skb is with netfilter.
+ */
+ id = (__be32)jhash_2words(hash32_ptr(skb), skb_get_hash(skb),
+ skb->skb_iif);
+
+ return nla_put_be32(nlskb, NFTA_TRACE_ID, id);
+}
+
+static int trace_fill_header(struct sk_buff *nlskb, u16 type,
+ const struct sk_buff *skb,
+ int off, unsigned int len)
+{
+ struct nlattr *nla;
+
+ if (len == 0)
+ return 0;
+
+ nla = nla_reserve(nlskb, type, len);
+ if (!nla || skb_copy_bits(skb, off, nla_data(nla), len))
+ return -1;
+
+ return 0;
+}
+
+static int nf_trace_fill_ll_header(struct sk_buff *nlskb,
+ const struct sk_buff *skb)
+{
+ struct vlan_ethhdr veth;
+ int off;
+
+ BUILD_BUG_ON(sizeof(veth) > NFT_TRACETYPE_LL_HSIZE);
+
+ off = skb_mac_header(skb) - skb->data;
+ if (off != -ETH_HLEN)
+ return -1;
+
+ if (skb_copy_bits(skb, off, &veth, ETH_HLEN))
+ return -1;
+
+ veth.h_vlan_proto = skb->vlan_proto;
+ veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
+ veth.h_vlan_encapsulated_proto = skb->protocol;
+
+ return nla_put(nlskb, NFTA_TRACE_LL_HEADER, sizeof(veth), &veth);
+}
+
+static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
+ const struct net_device *indev,
+ const struct net_device *outdev)
+{
+ if (indev) {
+ if (nla_put_be32(nlskb, NFTA_TRACE_IIF,
+ htonl(indev->ifindex)))
+ return -1;
+
+ if (nla_put_be16(nlskb, NFTA_TRACE_IIFTYPE,
+ htons(indev->type)))
+ return -1;
+ }
+
+ if (outdev) {
+ if (nla_put_be32(nlskb, NFTA_TRACE_OIF,
+ htonl(outdev->ifindex)))
+ return -1;
+
+ if (nla_put_be16(nlskb, NFTA_TRACE_OIFTYPE,
+ htons(outdev->type)))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
+ const struct nft_pktinfo *pkt)
+{
+ const struct sk_buff *skb = pkt->skb;
+ unsigned int len = min_t(unsigned int,
+ pkt->xt.thoff - skb_network_offset(skb),
+ NFT_TRACETYPE_NETWORK_HSIZE);
+ int off = skb_network_offset(skb);
+
+ if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
+ return -1;
+
+ len = min_t(unsigned int, skb->len - pkt->xt.thoff,
+ NFT_TRACETYPE_TRANSPORT_HSIZE);
+
+ if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
+ pkt->xt.thoff, len))
+ return -1;
+
+ if (!skb_mac_header_was_set(skb))
+ return 0;
+
+ if (skb_vlan_tag_get(skb))
+ return nf_trace_fill_ll_header(nlskb, skb);
+
+ off = skb_mac_header(skb) - skb->data;
+ len = min_t(unsigned int, -off, NFT_TRACETYPE_LL_HSIZE);
+ return trace_fill_header(nlskb, NFTA_TRACE_LL_HEADER,
+ skb, off, len);
+}
+
+static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
+ const struct nft_traceinfo *info)
+{
+ if (!info->rule)
+ return 0;
+
+ /* a continue verdict with ->type == RETURN means that this is
+ * an implicit return (end of chain reached).
+ *
+ * Since no rule matched, the ->rule pointer is invalid.
+ */
+ if (info->type == NFT_TRACETYPE_RETURN &&
+ info->verdict->code == NFT_CONTINUE)
+ return 0;
+
+ return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
+ cpu_to_be64(info->rule->handle));
+}
+
+void nft_trace_notify(struct nft_traceinfo *info)
+{
+ const struct nft_pktinfo *pkt = info->pkt;
+ struct nfgenmsg *nfmsg;
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ unsigned int size;
+ int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_TRACE;
+
+ if (!nfnetlink_has_listeners(pkt->net, NFNLGRP_NFTRACE))
+ return;
+
+ size = nlmsg_total_size(sizeof(struct nfgenmsg)) +
+ nla_total_size(NFT_TABLE_MAXNAMELEN) +
+ nla_total_size(NFT_CHAIN_MAXNAMELEN) +
+ nla_total_size(sizeof(__be64)) + /* rule handle */
+ nla_total_size(sizeof(__be32)) + /* trace type */
+ nla_total_size(0) + /* VERDICT, nested */
+ nla_total_size(sizeof(u32)) + /* verdict code */
+ nla_total_size(NFT_CHAIN_MAXNAMELEN) + /* jump target */
+ nla_total_size(sizeof(u32)) + /* id */
+ nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
+ nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
+ nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
+ nla_total_size(sizeof(u32)) + /* iif */
+ nla_total_size(sizeof(__be16)) + /* iiftype */
+ nla_total_size(sizeof(u32)) + /* oif */
+ nla_total_size(sizeof(__be16)) + /* oiftype */
+ nla_total_size(sizeof(u32)) + /* mark */
+ nla_total_size(sizeof(u32)) + /* nfproto */
+ nla_total_size(sizeof(u32)); /* policy */
+
+ skb = nlmsg_new(size, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0);
+ if (!nlh)
+ goto nla_put_failure;
+
+ nfmsg = nlmsg_data(nlh);
+ nfmsg->nfgen_family = info->basechain->type->family;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = 0;
+
+ if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(pkt->pf)))
+ goto nla_put_failure;
+
+ if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type)))
+ goto nla_put_failure;
+
+ if (trace_fill_id(skb, pkt->skb))
+ goto nla_put_failure;
+
+ if (info->chain) {
+ if (nla_put_string(skb, NFTA_TRACE_CHAIN,
+ info->chain->name))
+ goto nla_put_failure;
+ if (nla_put_string(skb, NFTA_TRACE_TABLE,
+ info->chain->table->name))
+ goto nla_put_failure;
+ }
+
+ if (nf_trace_fill_rule_info(skb, info))
+ goto nla_put_failure;
+
+ switch (info->type) {
+ case NFT_TRACETYPE_UNSPEC:
+ case __NFT_TRACETYPE_MAX:
+ break;
+ case NFT_TRACETYPE_RETURN:
+ case NFT_TRACETYPE_RULE:
+ if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict))
+ goto nla_put_failure;
+ break;
+ case NFT_TRACETYPE_POLICY:
+ if (nla_put_be32(skb, NFTA_TRACE_POLICY,
+ info->basechain->policy))
+ goto nla_put_failure;
+ break;
+ }
+
+ if (pkt->skb->mark &&
+ nla_put_be32(skb, NFTA_TRACE_MARK, htonl(pkt->skb->mark)))
+ goto nla_put_failure;
+
+ if (!info->packet_dumped) {
+ if (nf_trace_fill_dev_info(skb, pkt->in, pkt->out))
+ goto nla_put_failure;
+
+ if (nf_trace_fill_pkt_info(skb, pkt))
+ goto nla_put_failure;
+ info->packet_dumped = true;
+ }
+
+ nlmsg_end(skb, nlh);
+ nfnetlink_send(skb, pkt->net, 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC);
+ return;
+
+ nla_put_failure:
+ WARN_ON_ONCE(1);
+ kfree_skb(skb);
+}
+
+void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
+ const struct nft_verdict *verdict,
+ const struct nft_chain *chain)
+{
+ info->basechain = nft_base_chain(chain);
+ info->trace = true;
+ info->packet_dumped = false;
+ info->pkt = pkt;
+ info->verdict = verdict;
+}
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 77afe913d03d..a7ba23353dab 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -33,6 +33,10 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
+#define nfnl_dereference_protected(id) \
+ rcu_dereference_protected(table[(id)].subsys, \
+ lockdep_nfnl_is_held((id)))
+
static char __initdata nfversion[] = "0.30";
static struct {
@@ -49,6 +53,7 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
[NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES,
[NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT,
+ [NFNLGRP_NFTRACE] = NFNL_SUBSYS_NFTABLES,
};
void nfnl_lock(__u8 subsys_id)
@@ -201,19 +206,18 @@ replay:
}
if (nc->call_rcu) {
- err = nc->call_rcu(net->nfnl, skb, nlh,
+ err = nc->call_rcu(net, net->nfnl, skb, nlh,
(const struct nlattr **)cda);
rcu_read_unlock();
} else {
rcu_read_unlock();
nfnl_lock(subsys_id);
- if (rcu_dereference_protected(table[subsys_id].subsys,
- lockdep_is_held(&table[subsys_id].mutex)) != ss ||
+ if (nfnl_dereference_protected(subsys_id) != ss ||
nfnetlink_find_client(type, ss) != nc)
err = -EAGAIN;
else if (nc->call)
- err = nc->call(net->nfnl, skb, nlh,
- (const struct nlattr **)cda);
+ err = nc->call(net, net->nfnl, skb, nlh,
+ (const struct nlattr **)cda);
else
err = -EINVAL;
nfnl_unlock(subsys_id);
@@ -296,15 +300,13 @@ replay:
return netlink_ack(oskb, nlh, -ENOMEM);
nfnl_lock(subsys_id);
- ss = rcu_dereference_protected(table[subsys_id].subsys,
- lockdep_is_held(&table[subsys_id].mutex));
+ ss = nfnl_dereference_protected(subsys_id);
if (!ss) {
#ifdef CONFIG_MODULES
nfnl_unlock(subsys_id);
request_module("nfnetlink-subsys-%d", subsys_id);
nfnl_lock(subsys_id);
- ss = rcu_dereference_protected(table[subsys_id].subsys,
- lockdep_is_held(&table[subsys_id].mutex));
+ ss = nfnl_dereference_protected(subsys_id);
if (!ss)
#endif
{
@@ -423,15 +425,15 @@ next:
}
done:
if (status & NFNL_BATCH_REPLAY) {
- ss->abort(oskb);
+ ss->abort(net, oskb);
nfnl_err_reset(&err_list);
nfnl_unlock(subsys_id);
kfree_skb(skb);
goto replay;
} else if (status == NFNL_BATCH_DONE) {
- ss->commit(oskb);
+ ss->commit(net, oskb);
} else {
- ss->abort(oskb);
+ ss->abort(net, oskb);
}
nfnl_err_deliver(&err_list, oskb);
diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c
index fefbf5f0b28d..5274b04c42a6 100644
--- a/net/netfilter/nfnetlink_acct.c
+++ b/net/netfilter/nfnetlink_acct.c
@@ -46,12 +46,11 @@ struct nfacct_filter {
#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES)
#define NFACCT_OVERQUOTA_BIT 2 /* NFACCT_F_OVERQUOTA */
-static int
-nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_acct_new(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
struct nf_acct *nfacct, *matching = NULL;
- struct net *net = sock_net(nfnl);
char *acct_name;
unsigned int size = 0;
u32 flags = 0;
@@ -253,11 +252,10 @@ nfacct_filter_alloc(const struct nlattr * const attr)
return filter;
}
-static int
-nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_acct_get(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
- struct net *net = sock_net(nfnl);
int ret = -ENOENT;
struct nf_acct *cur;
char *acct_name;
@@ -333,11 +331,10 @@ static int nfnl_acct_try_del(struct nf_acct *cur)
return ret;
}
-static int
-nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_acct_del(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
- struct net *net = sock_net(nfnl);
char *acct_name;
struct nf_acct *cur;
int ret = -ENOENT;
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 54330fb5efaf..e924e95fcc7f 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -286,9 +286,9 @@ nfnl_cthelper_update(const struct nlattr * const tb[],
return 0;
}
-static int
-nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
const char *helper_name;
struct nf_conntrack_helper *cur, *helper = NULL;
@@ -498,9 +498,9 @@ out:
return skb->len;
}
-static int
-nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
int ret = -ENOENT, i;
struct nf_conntrack_helper *cur;
@@ -570,9 +570,9 @@ nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb,
return ret;
}
-static int
-nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
char *helper_name = NULL;
struct nf_conntrack_helper *cur;
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index c7a2d0e1c462..5d010f27ac01 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -38,8 +38,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning");
-static LIST_HEAD(cttimeout_list);
-
static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
[CTA_TIMEOUT_NAME] = { .type = NLA_NUL_STRING,
.len = CTNL_TIMEOUT_NAME_MAX - 1},
@@ -67,16 +65,15 @@ ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto,
return ret;
}
-static int
-cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_new_timeout(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
struct ctnl_timeout *timeout, *matching = NULL;
- struct net *net = sock_net(skb->sk);
char *name;
int ret;
@@ -90,7 +87,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
- list_for_each_entry(timeout, &cttimeout_list, head) {
+ list_for_each_entry(timeout, &net->nfct_timeout_list, head) {
if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
continue;
@@ -145,7 +142,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
timeout->l3num = l3num;
timeout->l4proto = l4proto;
atomic_set(&timeout->refcnt, 1);
- list_add_tail_rcu(&timeout->head, &cttimeout_list);
+ list_add_tail_rcu(&timeout->head, &net->nfct_timeout_list);
return 0;
err:
@@ -209,6 +206,7 @@ nla_put_failure:
static int
ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
+ struct net *net = sock_net(skb->sk);
struct ctnl_timeout *cur, *last;
if (cb->args[2])
@@ -219,7 +217,7 @@ ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
cb->args[1] = 0;
rcu_read_lock();
- list_for_each_entry_rcu(cur, &cttimeout_list, head) {
+ list_for_each_entry_rcu(cur, &net->nfct_timeout_list, head) {
if (last) {
if (cur != last)
continue;
@@ -240,10 +238,10 @@ ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static int
-cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_get_timeout(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
int ret = -ENOENT;
char *name;
@@ -260,7 +258,7 @@ cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
return -EINVAL;
name = nla_data(cda[CTA_TIMEOUT_NAME]);
- list_for_each_entry(cur, &cttimeout_list, head) {
+ list_for_each_entry(cur, &net->nfct_timeout_list, head) {
struct sk_buff *skb2;
if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
@@ -301,17 +299,17 @@ static void untimeout(struct nf_conntrack_tuple_hash *i,
RCU_INIT_POINTER(timeout_ext->timeout, NULL);
}
-static void ctnl_untimeout(struct ctnl_timeout *timeout)
+static void ctnl_untimeout(struct net *net, struct ctnl_timeout *timeout)
{
struct nf_conntrack_tuple_hash *h;
const struct hlist_nulls_node *nn;
int i;
local_bh_disable();
- for (i = 0; i < init_net.ct.htable_size; i++) {
+ for (i = 0; i < net->ct.htable_size; i++) {
spin_lock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
- if (i < init_net.ct.htable_size) {
- hlist_nulls_for_each_entry(h, nn, &init_net.ct.hash[i], hnnode)
+ if (i < net->ct.htable_size) {
+ hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
untimeout(h, timeout);
}
spin_unlock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
@@ -320,7 +318,7 @@ static void ctnl_untimeout(struct ctnl_timeout *timeout)
}
/* try to delete object, fail if it is still in use. */
-static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
+static int ctnl_timeout_try_del(struct net *net, struct ctnl_timeout *timeout)
{
int ret = 0;
@@ -329,7 +327,7 @@ static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
/* We are protected by nfnl mutex. */
list_del_rcu(&timeout->head);
nf_ct_l4proto_put(timeout->l4proto);
- ctnl_untimeout(timeout);
+ ctnl_untimeout(net, timeout);
kfree_rcu(timeout, rcu_head);
} else {
/* still in use, restore reference counter. */
@@ -339,28 +337,28 @@ static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
return ret;
}
-static int
-cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_del_timeout(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- char *name;
struct ctnl_timeout *cur;
int ret = -ENOENT;
+ char *name;
if (!cda[CTA_TIMEOUT_NAME]) {
- list_for_each_entry(cur, &cttimeout_list, head)
- ctnl_timeout_try_del(cur);
+ list_for_each_entry(cur, &net->nfct_timeout_list, head)
+ ctnl_timeout_try_del(net, cur);
return 0;
}
name = nla_data(cda[CTA_TIMEOUT_NAME]);
- list_for_each_entry(cur, &cttimeout_list, head) {
+ list_for_each_entry(cur, &net->nfct_timeout_list, head) {
if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
continue;
- ret = ctnl_timeout_try_del(cur);
+ ret = ctnl_timeout_try_del(net, cur);
if (ret < 0)
return ret;
@@ -369,15 +367,14 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
return ret;
}
-static int
-cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_default_set(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
- struct net *net = sock_net(skb->sk);
unsigned int *timeouts;
int ret;
@@ -459,14 +456,14 @@ nla_put_failure:
return -1;
}
-static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb,
+static int cttimeout_default_get(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
- struct net *net = sock_net(skb->sk);
struct sk_buff *skb2;
int ret, err;
@@ -511,12 +508,13 @@ err:
}
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
-static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
+static struct ctnl_timeout *
+ctnl_timeout_find_get(struct net *net, const char *name)
{
struct ctnl_timeout *timeout, *matching = NULL;
rcu_read_lock();
- list_for_each_entry_rcu(timeout, &cttimeout_list, head) {
+ list_for_each_entry_rcu(timeout, &net->nfct_timeout_list, head) {
if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
continue;
@@ -569,10 +567,39 @@ static const struct nfnetlink_subsystem cttimeout_subsys = {
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT);
+static int __net_init cttimeout_net_init(struct net *net)
+{
+ INIT_LIST_HEAD(&net->nfct_timeout_list);
+
+ return 0;
+}
+
+static void __net_exit cttimeout_net_exit(struct net *net)
+{
+ struct ctnl_timeout *cur, *tmp;
+
+ ctnl_untimeout(net, NULL);
+
+ list_for_each_entry_safe(cur, tmp, &net->nfct_timeout_list, head) {
+ list_del_rcu(&cur->head);
+ nf_ct_l4proto_put(cur->l4proto);
+ kfree_rcu(cur, rcu_head);
+ }
+}
+
+static struct pernet_operations cttimeout_ops = {
+ .init = cttimeout_net_init,
+ .exit = cttimeout_net_exit,
+};
+
static int __init cttimeout_init(void)
{
int ret;
+ ret = register_pernet_subsys(&cttimeout_ops);
+ if (ret < 0)
+ return ret;
+
ret = nfnetlink_subsys_register(&cttimeout_subsys);
if (ret < 0) {
pr_err("cttimeout_init: cannot register cttimeout with "
@@ -586,28 +613,17 @@ static int __init cttimeout_init(void)
return 0;
err_out:
+ unregister_pernet_subsys(&cttimeout_ops);
return ret;
}
static void __exit cttimeout_exit(void)
{
- struct ctnl_timeout *cur, *tmp;
-
pr_info("cttimeout: unregistering from nfnetlink.\n");
nfnetlink_subsys_unregister(&cttimeout_subsys);
- /* Make sure no conntrack objects refer to custom timeouts anymore. */
- ctnl_untimeout(NULL);
-
- list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) {
- list_del_rcu(&cur->head);
- /* We are sure that our objects have no clients at this point,
- * it's safe to release them all without checking refcnt.
- */
- nf_ct_l4proto_put(cur->l4proto);
- kfree_rcu(cur, rcu_head);
- }
+ unregister_pernet_subsys(&cttimeout_ops);
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 740cce4685ac..8ca932057c13 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -293,24 +293,20 @@ nfulnl_set_nlbufsiz(struct nfulnl_instance *inst, u_int32_t nlbufsiz)
return status;
}
-static int
+static void
nfulnl_set_timeout(struct nfulnl_instance *inst, u_int32_t timeout)
{
spin_lock_bh(&inst->lock);
inst->flushtimeout = timeout;
spin_unlock_bh(&inst->lock);
-
- return 0;
}
-static int
+static void
nfulnl_set_qthresh(struct nfulnl_instance *inst, u_int32_t qthresh)
{
spin_lock_bh(&inst->lock);
inst->qthreshold = qthresh;
spin_unlock_bh(&inst->lock);
-
- return 0;
}
static int
@@ -789,10 +785,9 @@ static struct notifier_block nfulnl_rtnl_notifier = {
.notifier_call = nfulnl_rcv_nl_event,
};
-static int
-nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfulnl_recv_unsupp(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
return -ENOTSUPP;
}
@@ -813,16 +808,14 @@ static const struct nla_policy nfula_cfg_policy[NFULA_CFG_MAX+1] = {
[NFULA_CFG_FLAGS] = { .type = NLA_U16 },
};
-static int
-nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfula[])
+static int nfulnl_recv_config(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfula[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int16_t group_num = ntohs(nfmsg->res_id);
struct nfulnl_instance *inst;
struct nfulnl_msg_config_cmd *cmd = NULL;
- struct net *net = sock_net(ctnl);
struct nfnl_log_net *log = nfnl_log_pernet(net);
int ret = 0;
u16 flags = 0;
@@ -895,7 +888,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
goto out_put;
default:
ret = -ENOTSUPP;
- break;
+ goto out_put;
}
} else if (!inst) {
ret = -ENODEV;
@@ -1064,15 +1057,26 @@ static int __net_init nfnl_log_net_init(struct net *net)
{
unsigned int i;
struct nfnl_log_net *log = nfnl_log_pernet(net);
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *proc;
+ kuid_t root_uid;
+ kgid_t root_gid;
+#endif
for (i = 0; i < INSTANCE_BUCKETS; i++)
INIT_HLIST_HEAD(&log->instance_table[i]);
spin_lock_init(&log->instances_lock);
#ifdef CONFIG_PROC_FS
- if (!proc_create("nfnetlink_log", 0440,
- net->nf.proc_netfilter, &nful_file_ops))
+ proc = proc_create("nfnetlink_log", 0440,
+ net->nf.proc_netfilter, &nful_file_ops);
+ if (!proc)
return -ENOMEM;
+
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
#endif
return 0;
}
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 861c6615253b..1d3936587ace 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -957,10 +957,10 @@ static int nfq_id_after(unsigned int id, unsigned int max)
return (int)(id - max) > 0;
}
-static int
-nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_verdict_batch(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nf_queue_entry *entry, *tmp;
@@ -969,8 +969,6 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
struct nfqnl_instance *queue;
LIST_HEAD(batch_list);
u16 queue_num = ntohs(nfmsg->res_id);
-
- struct net *net = sock_net(ctnl);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
queue = verdict_instance_lookup(q, queue_num,
@@ -1029,14 +1027,13 @@ static struct nf_conn *nfqnl_ct_parse(struct nfnl_ct_hook *nfnl_ct,
return ct;
}
-static int
-nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_verdict(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int16_t queue_num = ntohs(nfmsg->res_id);
-
struct nfqnl_msg_verdict_hdr *vhdr;
struct nfqnl_instance *queue;
unsigned int verdict;
@@ -1044,8 +1041,6 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
enum ip_conntrack_info uninitialized_var(ctinfo);
struct nfnl_ct_hook *nfnl_ct;
struct nf_conn *ct = NULL;
-
- struct net *net = sock_net(ctnl);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
queue = instance_lookup(q, queue_num);
@@ -1092,10 +1087,9 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
return 0;
}
-static int
-nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_unsupp(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
return -ENOTSUPP;
}
@@ -1110,17 +1104,16 @@ static const struct nf_queue_handler nfqh = {
.nf_hook_drop = &nfqnl_nf_hook_drop,
};
-static int
-nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_config(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int16_t queue_num = ntohs(nfmsg->res_id);
struct nfqnl_instance *queue;
struct nfqnl_msg_config_cmd *cmd = NULL;
- struct net *net = sock_net(ctnl);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+ __u32 flags = 0, mask = 0;
int ret = 0;
if (nfqa[NFQA_CFG_CMD]) {
@@ -1133,6 +1126,40 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
}
}
+ /* Check if we support these flags in first place, dependencies should
+ * be there too not to break atomicity.
+ */
+ if (nfqa[NFQA_CFG_FLAGS]) {
+ if (!nfqa[NFQA_CFG_MASK]) {
+ /* A mask is needed to specify which flags are being
+ * changed.
+ */
+ return -EINVAL;
+ }
+
+ flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS]));
+ mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK]));
+
+ if (flags >= NFQA_CFG_F_MAX)
+ return -EOPNOTSUPP;
+
+#if !IS_ENABLED(CONFIG_NETWORK_SECMARK)
+ if (flags & mask & NFQA_CFG_F_SECCTX)
+ return -EOPNOTSUPP;
+#endif
+ if ((flags & mask & NFQA_CFG_F_CONNTRACK) &&
+ !rcu_access_pointer(nfnl_ct_hook)) {
+#ifdef CONFIG_MODULES
+ nfnl_unlock(NFNL_SUBSYS_QUEUE);
+ request_module("ip_conntrack_netlink");
+ nfnl_lock(NFNL_SUBSYS_QUEUE);
+ if (rcu_access_pointer(nfnl_ct_hook))
+ return -EAGAIN;
+#endif
+ return -EOPNOTSUPP;
+ }
+ }
+
rcu_read_lock();
queue = instance_lookup(q, queue_num);
if (queue && queue->peer_portid != NETLINK_CB(skb).portid) {
@@ -1160,70 +1187,38 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
goto err_out_unlock;
}
instance_destroy(q, queue);
- break;
+ goto err_out_unlock;
case NFQNL_CFG_CMD_PF_BIND:
case NFQNL_CFG_CMD_PF_UNBIND:
break;
default:
ret = -ENOTSUPP;
- break;
+ goto err_out_unlock;
}
}
+ if (!queue) {
+ ret = -ENODEV;
+ goto err_out_unlock;
+ }
+
if (nfqa[NFQA_CFG_PARAMS]) {
- struct nfqnl_msg_config_params *params;
+ struct nfqnl_msg_config_params *params =
+ nla_data(nfqa[NFQA_CFG_PARAMS]);
- if (!queue) {
- ret = -ENODEV;
- goto err_out_unlock;
- }
- params = nla_data(nfqa[NFQA_CFG_PARAMS]);
nfqnl_set_mode(queue, params->copy_mode,
ntohl(params->copy_range));
}
if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) {
- __be32 *queue_maxlen;
+ __be32 *queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]);
- if (!queue) {
- ret = -ENODEV;
- goto err_out_unlock;
- }
- queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]);
spin_lock_bh(&queue->lock);
queue->queue_maxlen = ntohl(*queue_maxlen);
spin_unlock_bh(&queue->lock);
}
if (nfqa[NFQA_CFG_FLAGS]) {
- __u32 flags, mask;
-
- if (!queue) {
- ret = -ENODEV;
- goto err_out_unlock;
- }
-
- if (!nfqa[NFQA_CFG_MASK]) {
- /* A mask is needed to specify which flags are being
- * changed.
- */
- ret = -EINVAL;
- goto err_out_unlock;
- }
-
- flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS]));
- mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK]));
-
- if (flags >= NFQA_CFG_F_MAX) {
- ret = -EOPNOTSUPP;
- goto err_out_unlock;
- }
-#if !IS_ENABLED(CONFIG_NETWORK_SECMARK)
- if (flags & mask & NFQA_CFG_F_SECCTX) {
- ret = -EOPNOTSUPP;
- goto err_out_unlock;
- }
-#endif
spin_lock_bh(&queue->lock);
queue->flags &= ~mask;
queue->flags |= flags & mask;
diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c
index fde5145f2e36..383c17138399 100644
--- a/net/netfilter/nft_byteorder.c
+++ b/net/netfilter/nft_byteorder.c
@@ -8,6 +8,7 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <asm/unaligned.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -39,6 +40,27 @@ static void nft_byteorder_eval(const struct nft_expr *expr,
d = (void *)dst;
switch (priv->size) {
+ case 8: {
+ u64 src64;
+
+ switch (priv->op) {
+ case NFT_BYTEORDER_NTOH:
+ for (i = 0; i < priv->len / 8; i++) {
+ src64 = get_unaligned_be64(&src[i]);
+ src64 = be64_to_cpu((__force __be64)src64);
+ put_unaligned_be64(src64, &dst[i]);
+ }
+ break;
+ case NFT_BYTEORDER_HTON:
+ for (i = 0; i < priv->len / 8; i++) {
+ src64 = get_unaligned_be64(&src[i]);
+ src64 = (__force u64)cpu_to_be64(src64);
+ put_unaligned_be64(src64, &dst[i]);
+ }
+ break;
+ }
+ break;
+ }
case 4:
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
@@ -101,6 +123,7 @@ static int nft_byteorder_init(const struct nft_ctx *ctx,
switch (priv->size) {
case 2:
case 4:
+ case 8:
break;
default:
return -EINVAL;
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 9c8fab00164b..454841baa4d0 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -519,9 +519,9 @@ nla_put_failure:
return -1;
}
-static int
-nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_compat_get(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
int ret = 0, target;
struct nfgenmsg *nfmsg;
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 939921532764..a0eb2161e3ef 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -16,6 +16,7 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_acct.h>
#include <net/netfilter/nf_conntrack_tuple.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_ecache.h>
@@ -30,6 +31,18 @@ struct nft_ct {
};
};
+static u64 nft_ct_get_eval_counter(const struct nf_conn_counter *c,
+ enum nft_ct_keys k,
+ enum ip_conntrack_dir d)
+{
+ if (d < IP_CT_DIR_MAX)
+ return k == NFT_CT_BYTES ? atomic64_read(&c[d].bytes) :
+ atomic64_read(&c[d].packets);
+
+ return nft_ct_get_eval_counter(c, k, IP_CT_DIR_ORIGINAL) +
+ nft_ct_get_eval_counter(c, k, IP_CT_DIR_REPLY);
+}
+
static void nft_ct_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -114,6 +127,17 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
NF_CT_LABELS_MAX_SIZE - size);
return;
}
+ case NFT_CT_BYTES: /* fallthrough */
+ case NFT_CT_PKTS: {
+ const struct nf_conn_acct *acct = nf_conn_acct_find(ct);
+ u64 count = 0;
+
+ if (acct)
+ count = nft_ct_get_eval_counter(acct->counter,
+ priv->key, priv->dir);
+ memcpy(dest, &count, sizeof(count));
+ return;
+ }
#endif
default:
break;
@@ -291,6 +315,13 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
return -EINVAL;
len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u.all);
break;
+ case NFT_CT_BYTES:
+ case NFT_CT_PKTS:
+ /* no direction? return sum of original + reply */
+ if (tb[NFTA_CT_DIRECTION] == NULL)
+ priv->dir = IP_CT_DIR_MAX;
+ len = sizeof(u64);
+ break;
default:
return -EOPNOTSUPP;
}
@@ -374,6 +405,13 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
case NFT_CT_PROTO_DST:
if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
goto nla_put_failure;
+ break;
+ case NFT_CT_BYTES:
+ case NFT_CT_PKTS:
+ if (priv->dir < IP_CT_DIR_MAX &&
+ nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
+ goto nla_put_failure;
+ break;
default:
break;
}
diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c
new file mode 100644
index 000000000000..2cc1e0ef56e8
--- /dev/null
+++ b/net/netfilter/nft_dup_netdev.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_dup_netdev.h>
+
+struct nft_dup_netdev {
+ enum nft_registers sreg_dev:8;
+};
+
+static void nft_dup_netdev_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_dup_netdev *priv = nft_expr_priv(expr);
+ int oif = regs->data[priv->sreg_dev];
+
+ nf_dup_netdev_egress(pkt, oif);
+}
+
+static const struct nla_policy nft_dup_netdev_policy[NFTA_DUP_MAX + 1] = {
+ [NFTA_DUP_SREG_DEV] = { .type = NLA_U32 },
+};
+
+static int nft_dup_netdev_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_dup_netdev *priv = nft_expr_priv(expr);
+
+ if (tb[NFTA_DUP_SREG_DEV] == NULL)
+ return -EINVAL;
+
+ priv->sreg_dev = nft_parse_register(tb[NFTA_DUP_SREG_DEV]);
+ return nft_validate_register_load(priv->sreg_dev, sizeof(int));
+}
+
+static const struct nft_expr_ops nft_dup_netdev_ingress_ops;
+
+static int nft_dup_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ struct nft_dup_netdev *priv = nft_expr_priv(expr);
+
+ if (nft_dump_register(skb, NFTA_DUP_SREG_DEV, priv->sreg_dev))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static struct nft_expr_type nft_dup_netdev_type;
+static const struct nft_expr_ops nft_dup_netdev_ops = {
+ .type = &nft_dup_netdev_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_dup_netdev)),
+ .eval = nft_dup_netdev_eval,
+ .init = nft_dup_netdev_init,
+ .dump = nft_dup_netdev_dump,
+};
+
+static struct nft_expr_type nft_dup_netdev_type __read_mostly = {
+ .family = NFPROTO_NETDEV,
+ .name = "dup",
+ .ops = &nft_dup_netdev_ops,
+ .policy = nft_dup_netdev_policy,
+ .maxattr = NFTA_DUP_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_dup_netdev_module_init(void)
+{
+ return nft_register_expr(&nft_dup_netdev_type);
+}
+
+static void __exit nft_dup_netdev_module_exit(void)
+{
+ nft_unregister_expr(&nft_dup_netdev_type);
+}
+
+module_init(nft_dup_netdev_module_init);
+module_exit(nft_dup_netdev_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
+MODULE_ALIAS_NFT_AF_EXPR(5, "dup");
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
new file mode 100644
index 000000000000..763ebc3e0b2b
--- /dev/null
+++ b/net/netfilter/nft_fwd_netdev.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_dup_netdev.h>
+
+struct nft_fwd_netdev {
+ enum nft_registers sreg_dev:8;
+};
+
+static void nft_fwd_netdev_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+ int oif = regs->data[priv->sreg_dev];
+
+ nf_dup_netdev_egress(pkt, oif);
+ regs->verdict.code = NF_DROP;
+}
+
+static const struct nla_policy nft_fwd_netdev_policy[NFTA_FWD_MAX + 1] = {
+ [NFTA_FWD_SREG_DEV] = { .type = NLA_U32 },
+};
+
+static int nft_fwd_netdev_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+
+ if (tb[NFTA_FWD_SREG_DEV] == NULL)
+ return -EINVAL;
+
+ priv->sreg_dev = nft_parse_register(tb[NFTA_FWD_SREG_DEV]);
+ return nft_validate_register_load(priv->sreg_dev, sizeof(int));
+}
+
+static const struct nft_expr_ops nft_fwd_netdev_ingress_ops;
+
+static int nft_fwd_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+
+ if (nft_dump_register(skb, NFTA_FWD_SREG_DEV, priv->sreg_dev))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static struct nft_expr_type nft_fwd_netdev_type;
+static const struct nft_expr_ops nft_fwd_netdev_ops = {
+ .type = &nft_fwd_netdev_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev)),
+ .eval = nft_fwd_netdev_eval,
+ .init = nft_fwd_netdev_init,
+ .dump = nft_fwd_netdev_dump,
+};
+
+static struct nft_expr_type nft_fwd_netdev_type __read_mostly = {
+ .family = NFPROTO_NETDEV,
+ .name = "fwd",
+ .ops = &nft_fwd_netdev_ops,
+ .policy = nft_fwd_netdev_policy,
+ .maxattr = NFTA_FWD_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_fwd_netdev_module_init(void)
+{
+ return nft_register_expr(&nft_fwd_netdev_type);
+}
+
+static void __exit nft_fwd_netdev_module_exit(void)
+{
+ nft_unregister_expr(&nft_fwd_netdev_type);
+}
+
+module_init(nft_fwd_netdev_module_init);
+module_exit(nft_fwd_netdev_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
+MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");
diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c
index 5d67938f8b2f..99d18578afc6 100644
--- a/net/netfilter/nft_limit.c
+++ b/net/netfilter/nft_limit.c
@@ -26,6 +26,7 @@ struct nft_limit {
u64 rate;
u64 nsecs;
u32 burst;
+ bool invert;
};
static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost)
@@ -44,11 +45,11 @@ static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost)
if (delta >= 0) {
limit->tokens = delta;
spin_unlock_bh(&limit_lock);
- return false;
+ return limit->invert;
}
limit->tokens = tokens;
spin_unlock_bh(&limit_lock);
- return true;
+ return !limit->invert;
}
static int nft_limit_init(struct nft_limit *limit,
@@ -78,6 +79,12 @@ static int nft_limit_init(struct nft_limit *limit,
limit->rate = rate;
}
+ if (tb[NFTA_LIMIT_FLAGS]) {
+ u32 flags = ntohl(nla_get_be32(tb[NFTA_LIMIT_FLAGS]));
+
+ if (flags & NFT_LIMIT_F_INV)
+ limit->invert = true;
+ }
limit->last = ktime_get_ns();
return 0;
@@ -86,13 +93,15 @@ static int nft_limit_init(struct nft_limit *limit,
static int nft_limit_dump(struct sk_buff *skb, const struct nft_limit *limit,
enum nft_limit_type type)
{
+ u32 flags = limit->invert ? NFT_LIMIT_F_INV : 0;
u64 secs = div_u64(limit->nsecs, NSEC_PER_SEC);
u64 rate = limit->rate - limit->burst;
if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(rate)) ||
nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs)) ||
nla_put_be32(skb, NFTA_LIMIT_BURST, htonl(limit->burst)) ||
- nla_put_be32(skb, NFTA_LIMIT_TYPE, htonl(type)))
+ nla_put_be32(skb, NFTA_LIMIT_TYPE, htonl(type)) ||
+ nla_put_be32(skb, NFTA_LIMIT_FLAGS, htonl(flags)))
goto nla_put_failure;
return 0;
@@ -120,6 +129,7 @@ static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
[NFTA_LIMIT_UNIT] = { .type = NLA_U64 },
[NFTA_LIMIT_BURST] = { .type = NLA_U32 },
[NFTA_LIMIT_TYPE] = { .type = NLA_U32 },
+ [NFTA_LIMIT_FLAGS] = { .type = NLA_U32 },
};
static int nft_limit_pkts_init(const struct nft_ctx *ctx,
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 9dfaf4d55ee0..fe885bf271c5 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -18,12 +18,16 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/smp.h>
+#include <linux/static_key.h>
#include <net/dst.h>
#include <net/sock.h>
#include <net/tcp_states.h> /* for TCP_TIME_WAIT */
#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nft_meta.h>
+#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
+
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -174,7 +178,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
sk = skb_to_full_sk(skb);
if (!sk || !sk_fullsock(sk))
goto err;
- *dest = sk->sk_classid;
+ *dest = sock_cgroup_classid(&sk->sk_cgrp_data);
break;
#endif
default:
@@ -188,6 +192,13 @@ err:
}
EXPORT_SYMBOL_GPL(nft_meta_get_eval);
+/* don't change or set _LOOPBACK, _USER, etc. */
+static bool pkt_type_ok(u32 p)
+{
+ return p == PACKET_HOST || p == PACKET_BROADCAST ||
+ p == PACKET_MULTICAST || p == PACKET_OTHERHOST;
+}
+
void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -203,6 +214,11 @@ void nft_meta_set_eval(const struct nft_expr *expr,
case NFT_META_PRIORITY:
skb->priority = value;
break;
+ case NFT_META_PKTTYPE:
+ if (skb->pkt_type != value &&
+ pkt_type_ok(value) && pkt_type_ok(skb->pkt_type))
+ skb->pkt_type = value;
+ break;
case NFT_META_NFTRACE:
skb->nf_trace = 1;
break;
@@ -271,6 +287,24 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
}
EXPORT_SYMBOL_GPL(nft_meta_get_init);
+static int nft_meta_set_init_pkttype(const struct nft_ctx *ctx)
+{
+ unsigned int hooks;
+
+ switch (ctx->afi->family) {
+ case NFPROTO_BRIDGE:
+ hooks = 1 << NF_BR_PRE_ROUTING;
+ break;
+ case NFPROTO_NETDEV:
+ hooks = 1 << NF_NETDEV_INGRESS;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return nft_chain_validate_hooks(ctx->chain, hooks);
+}
+
int nft_meta_set_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
@@ -288,6 +322,12 @@ int nft_meta_set_init(const struct nft_ctx *ctx,
case NFT_META_NFTRACE:
len = sizeof(u8);
break;
+ case NFT_META_PKTTYPE:
+ err = nft_meta_set_init_pkttype(ctx);
+ if (err)
+ return err;
+ len = sizeof(u8);
+ break;
default:
return -EOPNOTSUPP;
}
@@ -297,6 +337,9 @@ int nft_meta_set_init(const struct nft_ctx *ctx,
if (err < 0)
return err;
+ if (priv->key == NFT_META_NFTRACE)
+ static_branch_inc(&nft_trace_enabled);
+
return 0;
}
EXPORT_SYMBOL_GPL(nft_meta_set_init);
@@ -334,6 +377,16 @@ nla_put_failure:
}
EXPORT_SYMBOL_GPL(nft_meta_set_dump);
+void nft_meta_set_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+{
+ const struct nft_meta *priv = nft_expr_priv(expr);
+
+ if (priv->key == NFT_META_NFTRACE)
+ static_branch_dec(&nft_trace_enabled);
+}
+EXPORT_SYMBOL_GPL(nft_meta_set_destroy);
+
static struct nft_expr_type nft_meta_type;
static const struct nft_expr_ops nft_meta_get_ops = {
.type = &nft_meta_type,
@@ -348,6 +401,7 @@ static const struct nft_expr_ops nft_meta_set_ops = {
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval,
.init = nft_meta_set_init,
+ .destroy = nft_meta_set_destroy,
.dump = nft_meta_set_dump,
};
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index 09b4b07eb676..12cd4bf16d17 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -107,10 +107,13 @@ err:
}
static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = {
- [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_SREG] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_CSUM_TYPE] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_CSUM_OFFSET] = { .type = NLA_U32 },
};
static int nft_payload_init(const struct nft_ctx *ctx,
@@ -160,6 +163,118 @@ const struct nft_expr_ops nft_payload_fast_ops = {
.dump = nft_payload_dump,
};
+static void nft_payload_set_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_payload_set *priv = nft_expr_priv(expr);
+ struct sk_buff *skb = pkt->skb;
+ const u32 *src = &regs->data[priv->sreg];
+ int offset, csum_offset;
+ __wsum fsum, tsum;
+ __sum16 sum;
+
+ switch (priv->base) {
+ case NFT_PAYLOAD_LL_HEADER:
+ if (!skb_mac_header_was_set(skb))
+ goto err;
+ offset = skb_mac_header(skb) - skb->data;
+ break;
+ case NFT_PAYLOAD_NETWORK_HEADER:
+ offset = skb_network_offset(skb);
+ break;
+ case NFT_PAYLOAD_TRANSPORT_HEADER:
+ offset = pkt->xt.thoff;
+ break;
+ default:
+ BUG();
+ }
+
+ csum_offset = offset + priv->csum_offset;
+ offset += priv->offset;
+
+ if (priv->csum_type == NFT_PAYLOAD_CSUM_INET &&
+ (priv->base != NFT_PAYLOAD_TRANSPORT_HEADER ||
+ skb->ip_summed != CHECKSUM_PARTIAL)) {
+ if (skb_copy_bits(skb, csum_offset, &sum, sizeof(sum)) < 0)
+ goto err;
+
+ fsum = skb_checksum(skb, offset, priv->len, 0);
+ tsum = csum_partial(src, priv->len, 0);
+ sum = csum_fold(csum_add(csum_sub(~csum_unfold(sum), fsum),
+ tsum));
+ if (sum == 0)
+ sum = CSUM_MANGLED_0;
+
+ if (!skb_make_writable(skb, csum_offset + sizeof(sum)) ||
+ skb_store_bits(skb, csum_offset, &sum, sizeof(sum)) < 0)
+ goto err;
+ }
+
+ if (!skb_make_writable(skb, max(offset + priv->len, 0)) ||
+ skb_store_bits(skb, offset, src, priv->len) < 0)
+ goto err;
+
+ return;
+err:
+ regs->verdict.code = NFT_BREAK;
+}
+
+static int nft_payload_set_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_payload_set *priv = nft_expr_priv(expr);
+
+ priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
+ priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
+ priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
+ priv->sreg = nft_parse_register(tb[NFTA_PAYLOAD_SREG]);
+
+ if (tb[NFTA_PAYLOAD_CSUM_TYPE])
+ priv->csum_type =
+ ntohl(nla_get_be32(tb[NFTA_PAYLOAD_CSUM_TYPE]));
+ if (tb[NFTA_PAYLOAD_CSUM_OFFSET])
+ priv->csum_offset =
+ ntohl(nla_get_be32(tb[NFTA_PAYLOAD_CSUM_OFFSET]));
+
+ switch (priv->csum_type) {
+ case NFT_PAYLOAD_CSUM_NONE:
+ case NFT_PAYLOAD_CSUM_INET:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return nft_validate_register_load(priv->sreg, priv->len);
+}
+
+static int nft_payload_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ const struct nft_payload_set *priv = nft_expr_priv(expr);
+
+ if (nft_dump_register(skb, NFTA_PAYLOAD_SREG, priv->sreg) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_BASE, htonl(priv->base)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_OFFSET, htonl(priv->offset)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_CSUM_TYPE, htonl(priv->csum_type)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_CSUM_OFFSET,
+ htonl(priv->csum_offset)))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static const struct nft_expr_ops nft_payload_set_ops = {
+ .type = &nft_payload_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_payload_set)),
+ .eval = nft_payload_set_eval,
+ .init = nft_payload_set_init,
+ .dump = nft_payload_set_dump,
+};
+
static const struct nft_expr_ops *
nft_payload_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
@@ -167,8 +282,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
enum nft_payload_bases base;
unsigned int offset, len;
- if (tb[NFTA_PAYLOAD_DREG] == NULL ||
- tb[NFTA_PAYLOAD_BASE] == NULL ||
+ if (tb[NFTA_PAYLOAD_BASE] == NULL ||
tb[NFTA_PAYLOAD_OFFSET] == NULL ||
tb[NFTA_PAYLOAD_LEN] == NULL)
return ERR_PTR(-EINVAL);
@@ -183,6 +297,15 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
return ERR_PTR(-EOPNOTSUPP);
}
+ if (tb[NFTA_PAYLOAD_SREG] != NULL) {
+ if (tb[NFTA_PAYLOAD_DREG] != NULL)
+ return ERR_PTR(-EINVAL);
+ return &nft_payload_set_ops;
+ }
+
+ if (tb[NFTA_PAYLOAD_DREG] == NULL)
+ return ERR_PTR(-EINVAL);
+
offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index d4aaad747ea9..c8a0b7da5ff4 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -26,6 +26,7 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/audit.h>
+#include <linux/user_namespace.h>
#include <net/net_namespace.h>
#include <linux/netfilter/x_tables.h>
@@ -1226,6 +1227,8 @@ int xt_proto_init(struct net *net, u_int8_t af)
#ifdef CONFIG_PROC_FS
char buf[XT_FUNCTION_MAXNAMELEN];
struct proc_dir_entry *proc;
+ kuid_t root_uid;
+ kgid_t root_gid;
#endif
if (af >= ARRAY_SIZE(xt_prefix))
@@ -1233,12 +1236,17 @@ int xt_proto_init(struct net *net, u_int8_t af)
#ifdef CONFIG_PROC_FS
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+
strlcpy(buf, xt_prefix[af], sizeof(buf));
strlcat(buf, FORMAT_TABLES, sizeof(buf));
proc = proc_create_data(buf, 0440, net->proc_net, &xt_table_ops,
(void *)(unsigned long)af);
if (!proc)
goto out;
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
strlcpy(buf, xt_prefix[af], sizeof(buf));
strlcat(buf, FORMAT_MATCHES, sizeof(buf));
@@ -1246,6 +1254,8 @@ int xt_proto_init(struct net *net, u_int8_t af)
(void *)(unsigned long)af);
if (!proc)
goto out_remove_tables;
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
strlcpy(buf, xt_prefix[af], sizeof(buf));
strlcat(buf, FORMAT_TARGETS, sizeof(buf));
@@ -1253,6 +1263,8 @@ int xt_proto_init(struct net *net, u_int8_t af)
(void *)(unsigned long)af);
if (!proc)
goto out_remove_matches;
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
#endif
return 0;
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index e7ac07e53b59..6669e68d589e 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -143,7 +143,7 @@ xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
goto out;
}
- timeout = timeout_find_get(timeout_name);
+ timeout = timeout_find_get(par->net, timeout_name);
if (timeout == NULL) {
ret = -ENOENT;
pr_info("No such timeout policy \"%s\"\n", timeout_name);
diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c
index a1d126f29463..a086a914865f 100644
--- a/net/netfilter/xt_cgroup.c
+++ b/net/netfilter/xt_cgroup.c
@@ -24,9 +24,9 @@ MODULE_DESCRIPTION("Xtables: process control group matching");
MODULE_ALIAS("ipt_cgroup");
MODULE_ALIAS("ip6t_cgroup");
-static int cgroup_mt_check(const struct xt_mtchk_param *par)
+static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
{
- struct xt_cgroup_info *info = par->matchinfo;
+ struct xt_cgroup_info_v0 *info = par->matchinfo;
if (info->invert & ~1)
return -EINVAL;
@@ -34,38 +34,110 @@ static int cgroup_mt_check(const struct xt_mtchk_param *par)
return 0;
}
+static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
+{
+ struct xt_cgroup_info_v1 *info = par->matchinfo;
+ struct cgroup *cgrp;
+
+ if ((info->invert_path & ~1) || (info->invert_classid & ~1))
+ return -EINVAL;
+
+ if (!info->has_path && !info->has_classid) {
+ pr_info("xt_cgroup: no path or classid specified\n");
+ return -EINVAL;
+ }
+
+ if (info->has_path && info->has_classid) {
+ pr_info("xt_cgroup: both path and classid specified\n");
+ return -EINVAL;
+ }
+
+ if (info->has_path) {
+ cgrp = cgroup_get_from_path(info->path);
+ if (IS_ERR(cgrp)) {
+ pr_info("xt_cgroup: invalid path, errno=%ld\n",
+ PTR_ERR(cgrp));
+ return -EINVAL;
+ }
+ info->priv = cgrp;
+ }
+
+ return 0;
+}
+
static bool
-cgroup_mt(const struct sk_buff *skb, struct xt_action_param *par)
+cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
{
- const struct xt_cgroup_info *info = par->matchinfo;
+ const struct xt_cgroup_info_v0 *info = par->matchinfo;
if (skb->sk == NULL || !sk_fullsock(skb->sk))
return false;
- return (info->id == skb->sk->sk_classid) ^ info->invert;
+ return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^
+ info->invert;
+}
+
+static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_cgroup_info_v1 *info = par->matchinfo;
+ struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
+ struct cgroup *ancestor = info->priv;
+
+ if (!skb->sk || !sk_fullsock(skb->sk))
+ return false;
+
+ if (ancestor)
+ return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
+ info->invert_path;
+ else
+ return (info->classid == sock_cgroup_classid(skcd)) ^
+ info->invert_classid;
+}
+
+static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par)
+{
+ struct xt_cgroup_info_v1 *info = par->matchinfo;
+
+ if (info->priv)
+ cgroup_put(info->priv);
}
-static struct xt_match cgroup_mt_reg __read_mostly = {
- .name = "cgroup",
- .revision = 0,
- .family = NFPROTO_UNSPEC,
- .checkentry = cgroup_mt_check,
- .match = cgroup_mt,
- .matchsize = sizeof(struct xt_cgroup_info),
- .me = THIS_MODULE,
- .hooks = (1 << NF_INET_LOCAL_OUT) |
- (1 << NF_INET_POST_ROUTING) |
- (1 << NF_INET_LOCAL_IN),
+static struct xt_match cgroup_mt_reg[] __read_mostly = {
+ {
+ .name = "cgroup",
+ .revision = 0,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = cgroup_mt_check_v0,
+ .match = cgroup_mt_v0,
+ .matchsize = sizeof(struct xt_cgroup_info_v0),
+ .me = THIS_MODULE,
+ .hooks = (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_POST_ROUTING) |
+ (1 << NF_INET_LOCAL_IN),
+ },
+ {
+ .name = "cgroup",
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = cgroup_mt_check_v1,
+ .match = cgroup_mt_v1,
+ .matchsize = sizeof(struct xt_cgroup_info_v1),
+ .destroy = cgroup_mt_destroy_v1,
+ .me = THIS_MODULE,
+ .hooks = (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_POST_ROUTING) |
+ (1 << NF_INET_LOCAL_IN),
+ },
};
static int __init cgroup_mt_init(void)
{
- return xt_register_match(&cgroup_mt_reg);
+ return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
}
static void __exit cgroup_mt_exit(void)
{
- xt_unregister_match(&cgroup_mt_reg);
+ xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
}
module_init(cgroup_mt_init);
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index df8801e02a32..4e3c3affd285 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -61,8 +61,8 @@ static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
[OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) },
};
-static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int xt_osf_add_callback(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[])
{
struct xt_osf_user_finger *f;
@@ -104,7 +104,8 @@ static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
return err;
}
-static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb,
+static int xt_osf_remove_callback(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[])
{
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 59651af8cc27..81dc1bb6e016 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2915,6 +2915,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
cb = &nlk->cb;
memset(cb, 0, sizeof(*cb));
+ cb->start = control->start;
cb->dump = control->dump;
cb->done = control->done;
cb->nlh = nlh;
@@ -2927,6 +2928,9 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
mutex_unlock(nlk->cb_mutex);
+ if (cb->start)
+ cb->start(cb);
+
ret = netlink_dump(sk);
sock_put(sk);
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index bc0e504f33a6..8e63662c6fb0 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -513,6 +513,20 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
}
EXPORT_SYMBOL(genlmsg_put);
+static int genl_lock_start(struct netlink_callback *cb)
+{
+ /* our ops are always const - netlink API doesn't propagate that */
+ const struct genl_ops *ops = cb->data;
+ int rc = 0;
+
+ if (ops->start) {
+ genl_lock();
+ rc = ops->start(cb);
+ genl_unlock();
+ }
+ return rc;
+}
+
static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
/* our ops are always const - netlink API doesn't propagate that */
@@ -577,6 +591,7 @@ static int genl_family_rcv_msg(struct genl_family *family,
.module = family->module,
/* we have const, but the netlink API doesn't */
.data = (void *)ops,
+ .start = genl_lock_start,
.dump = genl_lock_dumpit,
.done = genl_lock_done,
};
@@ -588,6 +603,7 @@ static int genl_family_rcv_msg(struct genl_family *family,
} else {
struct netlink_dump_control c = {
.module = family->module,
+ .start = ops->start,
.dump = ops->dumpit,
.done = ops->done,
};
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 1fe3d3b362c0..122bb81da918 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -953,6 +953,19 @@ out:
}
EXPORT_SYMBOL(nfc_se_transaction);
+int nfc_se_connectivity(struct nfc_dev *dev, u8 se_idx)
+{
+ int rc;
+
+ pr_debug("connectivity: %x\n", se_idx);
+
+ device_lock(&dev->dev);
+ rc = nfc_genl_se_connectivity(dev, se_idx);
+ device_unlock(&dev->dev);
+ return rc;
+}
+EXPORT_SYMBOL(nfc_se_connectivity);
+
static void nfc_release(struct device *d)
{
struct nfc_dev *dev = to_nfc_dev(d);
diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
index 23c2a118ac9f..dd9003f38822 100644
--- a/net/nfc/digital_core.c
+++ b/net/nfc/digital_core.c
@@ -20,7 +20,8 @@
#include "digital.h"
#define DIGITAL_PROTO_NFCA_RF_TECH \
- (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK)
+ (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | \
+ NFC_PROTO_NFC_DEP_MASK | NFC_PROTO_ISO14443_MASK)
#define DIGITAL_PROTO_NFCB_RF_TECH NFC_PROTO_ISO14443_B_MASK
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 10c99a578421..fbb7a2b57b44 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -610,14 +610,14 @@ int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
struct nci_core_conn_create_cmd *cmd;
struct core_conn_create_data data;
+ if (!number_destination_params)
+ return -EINVAL;
+
data.length = params_len + sizeof(struct nci_core_conn_create_cmd);
cmd = kzalloc(data.length, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
- if (!number_destination_params)
- return -EINVAL;
-
cmd->destination_type = destination_type;
cmd->number_destination_params = number_destination_params;
memcpy(cmd->params, params, params_len);
diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c
index 2aedac15cb59..a0ab26d535dc 100644
--- a/net/nfc/nci/hci.c
+++ b/net/nfc/nci/hci.c
@@ -676,7 +676,7 @@ int nci_hci_connect_gate(struct nci_dev *ndev,
break;
default:
pipe = nci_hci_create_pipe(ndev, dest_host, dest_gate, &r);
- if (pipe < 0)
+ if (pipe == NCI_HCI_INVALID_PIPE)
return r;
pipe_created = true;
break;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index f58c1fba1026..ea023b35f1c2 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -552,6 +552,43 @@ free_msg:
return -EMSGSIZE;
}
+int nfc_genl_se_connectivity(struct nfc_dev *dev, u8 se_idx)
+{
+ struct nfc_se *se;
+ 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_CONNECTIVITY);
+ if (!hdr)
+ goto free_msg;
+
+ se = nfc_find_se(dev, se_idx);
+ if (!se)
+ 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, se->type))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(&nfc_genl_family, msg, 0, 0, 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,
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index c20b784ad720..6c6f76b370b1 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -105,6 +105,7 @@ 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);
int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx,
struct nfc_evt_transaction *evt_transaction);
+int nfc_genl_se_connectivity(struct nfc_dev *dev, u8 se_idx);
struct nfc_dev *nfc_get_device(unsigned int idx);
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index e004067ec24a..ee6ff8ffc12d 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -305,10 +305,10 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
u16 zone, struct sk_buff *skb)
{
struct ovs_skb_cb ovs_cb = *OVS_CB(skb);
+ int err;
if (key->eth.type == htons(ETH_P_IP)) {
enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone;
- int err;
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
err = ip_defrag(net, skb, user);
@@ -319,28 +319,13 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
} else if (key->eth.type == htons(ETH_P_IPV6)) {
enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone;
- struct sk_buff *reasm;
memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
- reasm = nf_ct_frag6_gather(net, skb, user);
- if (!reasm)
- return -EINPROGRESS;
-
- if (skb == reasm) {
- kfree_skb(skb);
- return -EINVAL;
- }
-
- /* Don't free 'skb' even though it is one of the original
- * fragments, as we're going to morph it into the head.
- */
- skb_get(skb);
- nf_ct_frag6_consume_orig(reasm);
+ err = nf_ct_frag6_gather(net, skb, user);
+ if (err)
+ return err;
- key->ip.proto = ipv6_hdr(reasm)->nexthdr;
- skb_morph(skb, reasm);
- skb->next = reasm->next;
- consume_skb(reasm);
+ key->ip.proto = ipv6_hdr(skb)->nexthdr;
ovs_cb.mru = IP6CB(skb)->frag_max_size;
#endif
} else {
diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c
index e41cd12d9b2d..30ab8e127288 100644
--- a/net/openvswitch/vport-geneve.c
+++ b/net/openvswitch/vport-geneve.c
@@ -34,7 +34,7 @@ static struct vport_ops ovs_geneve_vport_ops;
* @dst_port: destination port.
*/
struct geneve_port {
- u16 port_no;
+ u16 dst_port;
};
static inline struct geneve_port *geneve_vport(const struct vport *vport)
@@ -47,7 +47,7 @@ static int geneve_get_options(const struct vport *vport,
{
struct geneve_port *geneve_port = geneve_vport(vport);
- if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, geneve_port->port_no))
+ if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, geneve_port->dst_port))
return -EMSGSIZE;
return 0;
}
@@ -83,7 +83,7 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms)
return vport;
geneve_port = geneve_vport(vport);
- geneve_port->port_no = dst_port;
+ geneve_port->dst_port = dst_port;
rtnl_lock();
dev = geneve_dev_create_fb(net, parms->name, NET_NAME_USER, dst_port);
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index 6b0190b987ec..6a6adf314363 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -105,7 +105,7 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name)
rtnl_lock();
err = netdev_master_upper_dev_link(vport->dev,
- get_dpdev(vport->dp));
+ get_dpdev(vport->dp), NULL, NULL);
if (err)
goto error_unlock;
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index 8ea3a96980ac..c10899cb9040 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -70,7 +70,7 @@ struct vport_portids {
/**
* struct vport - one port within a datapath
- * @rcu: RCU callback head for deferred destruction.
+ * @dev: Pointer to net_device.
* @dp: Datapath to which this port belongs.
* @upcall_portids: RCU protected 'struct vport_portids'.
* @port_no: Index into @dp's @ports array.
@@ -78,6 +78,7 @@ struct vport_portids {
* @dp_hash_node: Element in @datapath->ports hash table in datapath.c.
* @ops: Class structure.
* @detach_list: list used for detaching vport in net-exit call.
+ * @rcu: RCU callback head for deferred destruction.
*/
struct vport {
struct net_device *dev;
@@ -204,26 +205,6 @@ int __ovs_vport_ops_register(struct vport_ops *ops);
})
void ovs_vport_ops_unregister(struct vport_ops *ops);
-
-static inline struct rtable *ovs_tunnel_route_lookup(struct net *net,
- const struct ip_tunnel_key *key,
- u32 mark,
- struct flowi4 *fl,
- u8 protocol)
-{
- struct rtable *rt;
-
- memset(fl, 0, sizeof(*fl));
- fl->daddr = key->u.ipv4.dst;
- fl->saddr = key->u.ipv4.src;
- fl->flowi4_tos = RT_TOS(key->tos);
- fl->flowi4_mark = mark;
- fl->flowi4_proto = protocol;
-
- rt = ip_route_output_key(net, fl);
- return rt;
-}
-
void ovs_vport_send(struct vport *vport, struct sk_buff *skb);
#endif /* vport.h */
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 10d42f3220ab..f925753668a7 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -377,6 +377,10 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
struct sockaddr_pn sa;
u16 len;
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
/* check we have at least a full Phonet header */
if (!pskb_pull(skb, sizeof(struct phonethdr)))
goto out;
diff --git a/net/rds/page.c b/net/rds/page.c
index 9005a2c920ee..5a14e6d6a926 100644
--- a/net/rds/page.c
+++ b/net/rds/page.c
@@ -179,37 +179,18 @@ out:
}
EXPORT_SYMBOL_GPL(rds_page_remainder_alloc);
-static int rds_page_remainder_cpu_notify(struct notifier_block *self,
- unsigned long action, void *hcpu)
+void rds_page_exit(void)
{
- struct rds_page_remainder *rem;
- long cpu = (long)hcpu;
+ unsigned int cpu;
- rem = &per_cpu(rds_page_remainders, cpu);
+ for_each_possible_cpu(cpu) {
+ struct rds_page_remainder *rem;
- rdsdebug("cpu %ld action 0x%lx\n", cpu, action);
+ rem = &per_cpu(rds_page_remainders, cpu);
+ rdsdebug("cpu %u\n", cpu);
- switch (action) {
- case CPU_DEAD:
if (rem->r_page)
__free_page(rem->r_page);
rem->r_page = NULL;
- break;
}
-
- return 0;
-}
-
-static struct notifier_block rds_page_remainder_nb = {
- .notifier_call = rds_page_remainder_cpu_notify,
-};
-
-void rds_page_exit(void)
-{
- int i;
-
- for_each_possible_cpu(i)
- rds_page_remainder_cpu_notify(&rds_page_remainder_nb,
- (unsigned long)CPU_DEAD,
- (void *)(long)i);
}
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 93127220cb54..4b1e3f35f06c 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -163,10 +163,6 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
#ifdef CONFIG_ACPI
static const struct acpi_device_id rfkill_acpi_match[] = {
- { "BCM2E1A", RFKILL_TYPE_BLUETOOTH },
- { "BCM2E3D", RFKILL_TYPE_BLUETOOTH },
- { "BCM2E40", RFKILL_TYPE_BLUETOOTH },
- { "BCM2E64", RFKILL_TYPE_BLUETOOTH },
{ "BCM4752", RFKILL_TYPE_GPS },
{ "LNV4752", RFKILL_TYPE_GPS },
{ },
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 1f8a144a5dc2..7e2d1057d8bc 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -67,7 +67,7 @@ static void rxrpc_write_space(struct sock *sk)
if (rxrpc_writable(sk)) {
struct socket_wq *wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible(&wq->wait);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
}
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index daa33432b716..82830824fb1f 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -310,15 +310,21 @@ config NET_SCH_PIE
If unsure, say N.
config NET_SCH_INGRESS
- tristate "Ingress Qdisc"
+ tristate "Ingress/classifier-action Qdisc"
depends on NET_CLS_ACT
select NET_INGRESS
+ select NET_EGRESS
---help---
- Say Y here if you want to use classifiers for incoming packets.
+ Say Y here if you want to use classifiers for incoming and/or outgoing
+ packets. This qdisc doesn't do anything else besides running classifiers,
+ which can also have actions attached to them. In case of outgoing packets,
+ classifiers that this qdisc holds are executed in the transmit path
+ before real enqueuing to an egress qdisc happens.
+
If unsure, say Y.
- To compile this code as a module, choose M here: the
- module will be called sch_ingress.
+ To compile this code as a module, choose M here: the module will be
+ called sch_ingress with alias of sch_clsact.
config NET_SCH_PLUG
tristate "Plug network traffic until release (PLUG)"
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 5faaa5425f7b..8dc84300ee79 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -79,12 +79,8 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res)
{
struct cls_bpf_head *head = rcu_dereference_bh(tp->root);
+ bool at_ingress = skb_at_tc_ingress(skb);
struct cls_bpf_prog *prog;
-#ifdef CONFIG_NET_CLS_ACT
- bool at_ingress = G_TC_AT(skb->tc_verd) & AT_INGRESS;
-#else
- bool at_ingress = false;
-#endif
int ret = -1;
if (unlikely(!skb_mac_header_was_set(skb)))
@@ -295,7 +291,7 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,
prog->bpf_name = name;
prog->filter = fp;
- if (fp->dst_needed)
+ if (fp->dst_needed && !(tp->q->flags & TCQ_F_INGRESS))
netif_keep_dst(qdisc_dev(tp->q));
return 0;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 57692947ebbe..95b021243233 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -252,23 +252,28 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC,
mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK,
sizeof(key->eth.src));
+
fl_set_key_val(tb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE,
&mask->basic.n_proto, TCA_FLOWER_UNSPEC,
sizeof(key->basic.n_proto));
+
if (key->basic.n_proto == htons(ETH_P_IP) ||
key->basic.n_proto == htons(ETH_P_IPV6)) {
fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO,
&mask->basic.ip_proto, TCA_FLOWER_UNSPEC,
sizeof(key->basic.ip_proto));
}
- if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+
+ if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) {
+ key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC,
&mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK,
sizeof(key->ipv4.src));
fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST,
&mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK,
sizeof(key->ipv4.dst));
- } else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+ } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) {
+ key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC,
&mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK,
sizeof(key->ipv6.src));
@@ -276,6 +281,7 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
&mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK,
sizeof(key->ipv6.dst));
}
+
if (key->basic.ip_proto == IPPROTO_TCP) {
fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC,
&mask->tp.src, TCA_FLOWER_UNSPEC,
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index e7c648fa9dc3..10adbc617905 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -1,4 +1,5 @@
-/* net/sched/sch_ingress.c - Ingress qdisc
+/* net/sched/sch_ingress.c - Ingress and clsact qdisc
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
@@ -98,17 +99,100 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
.owner = THIS_MODULE,
};
+static unsigned long clsact_get(struct Qdisc *sch, u32 classid)
+{
+ switch (TC_H_MIN(classid)) {
+ case TC_H_MIN(TC_H_MIN_INGRESS):
+ case TC_H_MIN(TC_H_MIN_EGRESS):
+ return TC_H_MIN(classid);
+ default:
+ return 0;
+ }
+}
+
+static unsigned long clsact_bind_filter(struct Qdisc *sch,
+ unsigned long parent, u32 classid)
+{
+ return clsact_get(sch, classid);
+}
+
+static struct tcf_proto __rcu **clsact_find_tcf(struct Qdisc *sch,
+ unsigned long cl)
+{
+ struct net_device *dev = qdisc_dev(sch);
+
+ switch (cl) {
+ case TC_H_MIN(TC_H_MIN_INGRESS):
+ return &dev->ingress_cl_list;
+ case TC_H_MIN(TC_H_MIN_EGRESS):
+ return &dev->egress_cl_list;
+ default:
+ return NULL;
+ }
+}
+
+static int clsact_init(struct Qdisc *sch, struct nlattr *opt)
+{
+ net_inc_ingress_queue();
+ net_inc_egress_queue();
+
+ sch->flags |= TCQ_F_CPUSTATS;
+
+ return 0;
+}
+
+static void clsact_destroy(struct Qdisc *sch)
+{
+ struct net_device *dev = qdisc_dev(sch);
+
+ tcf_destroy_chain(&dev->ingress_cl_list);
+ tcf_destroy_chain(&dev->egress_cl_list);
+
+ net_dec_ingress_queue();
+ net_dec_egress_queue();
+}
+
+static const struct Qdisc_class_ops clsact_class_ops = {
+ .leaf = ingress_leaf,
+ .get = clsact_get,
+ .put = ingress_put,
+ .walk = ingress_walk,
+ .tcf_chain = clsact_find_tcf,
+ .bind_tcf = clsact_bind_filter,
+ .unbind_tcf = ingress_put,
+};
+
+static struct Qdisc_ops clsact_qdisc_ops __read_mostly = {
+ .cl_ops = &clsact_class_ops,
+ .id = "clsact",
+ .init = clsact_init,
+ .destroy = clsact_destroy,
+ .dump = ingress_dump,
+ .owner = THIS_MODULE,
+};
+
static int __init ingress_module_init(void)
{
- return register_qdisc(&ingress_qdisc_ops);
+ int ret;
+
+ ret = register_qdisc(&ingress_qdisc_ops);
+ if (!ret) {
+ ret = register_qdisc(&clsact_qdisc_ops);
+ if (ret)
+ unregister_qdisc(&ingress_qdisc_ops);
+ }
+
+ return ret;
}
static void __exit ingress_module_exit(void)
{
unregister_qdisc(&ingress_qdisc_ops);
+ unregister_qdisc(&clsact_qdisc_ops);
}
module_init(ingress_module_init);
module_exit(ingress_module_exit);
+MODULE_ALIAS("sch_clsact");
MODULE_LICENSE("GPL");
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 559afd0ee7de..2bf8ec92dde4 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -383,6 +383,7 @@ void sctp_association_free(struct sctp_association *asoc)
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
list_del_rcu(pos);
+ sctp_unhash_transport(transport);
sctp_transport_free(transport);
}
@@ -500,6 +501,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
/* Remove this peer from the list. */
list_del_rcu(&peer->transports);
+ /* Remove this peer from the transport hashtable */
+ sctp_unhash_transport(peer);
/* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next;
@@ -699,6 +702,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Attach the remote transport to our asoc. */
list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list);
asoc->peer.transport_count++;
+ /* Add this peer into the transport hashtable */
+ sctp_hash_transport(peer);
/* If we do not yet have a primary path, set one. */
if (!asoc->peer.primary_path) {
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index 9da76ba4d10f..52838eaa1582 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -314,21 +314,16 @@ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
}
/* Find the association that goes with this chunk.
- * We do a linear search of the associations for this endpoint.
- * We return the matching transport address too.
+ * We lookup the transport from hashtable at first, then get association
+ * through t->assoc.
*/
-static struct sctp_association *__sctp_endpoint_lookup_assoc(
+struct sctp_association *sctp_endpoint_lookup_assoc(
const struct sctp_endpoint *ep,
const union sctp_addr *paddr,
struct sctp_transport **transport)
{
struct sctp_association *asoc = NULL;
- struct sctp_association *tmp;
- struct sctp_transport *t = NULL;
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- int hash;
- int rport;
+ struct sctp_transport *t;
*transport = NULL;
@@ -337,45 +332,16 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
*/
if (!ep->base.bind_addr.port)
goto out;
+ t = sctp_epaddr_lookup_transport(ep, paddr);
+ if (!t || t->asoc->temp)
+ goto out;
- rport = ntohs(paddr->v4.sin_port);
-
- hash = sctp_assoc_hashfn(sock_net(ep->base.sk), ep->base.bind_addr.port,
- rport);
- head = &sctp_assoc_hashtable[hash];
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- tmp = sctp_assoc(epb);
- if (tmp->ep != ep || rport != tmp->peer.port)
- continue;
-
- t = sctp_assoc_lookup_paddr(tmp, paddr);
- if (t) {
- asoc = tmp;
- *transport = t;
- break;
- }
- }
- read_unlock(&head->lock);
+ *transport = t;
+ asoc = t->asoc;
out:
return asoc;
}
-/* Lookup association on an endpoint based on a peer address. BH-safe. */
-struct sctp_association *sctp_endpoint_lookup_assoc(
- const struct sctp_endpoint *ep,
- const union sctp_addr *paddr,
- struct sctp_transport **transport)
-{
- struct sctp_association *asoc;
-
- local_bh_disable();
- asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport);
- local_bh_enable();
-
- return asoc;
-}
-
/* Look for any peeled off association from the endpoint that matches the
* given peer address.
*/
diff --git a/net/sctp/input.c b/net/sctp/input.c
index b6493b3f11a9..d9a6e66c5c8a 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -782,65 +782,135 @@ hit:
return ep;
}
-/* Insert association into the hash table. */
-static void __sctp_hash_established(struct sctp_association *asoc)
+/* rhashtable for transport */
+struct sctp_hash_cmp_arg {
+ const union sctp_addr *laddr;
+ const union sctp_addr *paddr;
+ const struct net *net;
+};
+
+static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
+ const void *ptr)
{
- struct net *net = sock_net(asoc->base.sk);
- struct sctp_ep_common *epb;
- struct sctp_hashbucket *head;
+ const struct sctp_hash_cmp_arg *x = arg->key;
+ const struct sctp_transport *t = ptr;
+ struct sctp_association *asoc = t->asoc;
+ const struct net *net = x->net;
- epb = &asoc->base;
+ if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port))
+ return 1;
+ if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr))
+ return 1;
+ if (!net_eq(sock_net(asoc->base.sk), net))
+ return 1;
+ if (!sctp_bind_addr_match(&asoc->base.bind_addr,
+ x->laddr, sctp_sk(asoc->base.sk)))
+ return 1;
- /* Calculate which chain this entry will belong to. */
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+ return 0;
+}
- head = &sctp_assoc_hashtable[epb->hashent];
+static inline u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
+{
+ const struct sctp_transport *t = data;
+ const union sctp_addr *paddr = &t->ipaddr;
+ const struct net *net = sock_net(t->asoc->base.sk);
+ u16 lport = htons(t->asoc->base.bind_addr.port);
+ u32 addr;
+
+ if (paddr->sa.sa_family == AF_INET6)
+ addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+ else
+ addr = paddr->v4.sin_addr.s_addr;
- write_lock(&head->lock);
- hlist_add_head(&epb->node, &head->chain);
- write_unlock(&head->lock);
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Add an association to the hash. Local BH-safe. */
-void sctp_hash_established(struct sctp_association *asoc)
+static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
{
- if (asoc->temp)
- return;
+ const struct sctp_hash_cmp_arg *x = data;
+ const union sctp_addr *paddr = x->paddr;
+ const struct net *net = x->net;
+ u16 lport = x->laddr->v4.sin_port;
+ u32 addr;
+
+ if (paddr->sa.sa_family == AF_INET6)
+ addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+ else
+ addr = paddr->v4.sin_addr.s_addr;
- local_bh_disable();
- __sctp_hash_established(asoc);
- local_bh_enable();
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Remove association from the hash table. */
-static void __sctp_unhash_established(struct sctp_association *asoc)
+static const struct rhashtable_params sctp_hash_params = {
+ .head_offset = offsetof(struct sctp_transport, node),
+ .hashfn = sctp_hash_key,
+ .obj_hashfn = sctp_hash_obj,
+ .obj_cmpfn = sctp_hash_cmp,
+ .automatic_shrinking = true,
+};
+
+int sctp_transport_hashtable_init(void)
{
- struct net *net = sock_net(asoc->base.sk);
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
+}
- epb = &asoc->base;
+void sctp_transport_hashtable_destroy(void)
+{
+ rhashtable_destroy(&sctp_transport_hashtable);
+}
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+void sctp_hash_transport(struct sctp_transport *t)
+{
+ struct sctp_sockaddr_entry *addr;
+ struct sctp_hash_cmp_arg arg;
+
+ addr = list_entry(t->asoc->base.bind_addr.address_list.next,
+ struct sctp_sockaddr_entry, list);
+ arg.laddr = &addr->a;
+ arg.paddr = &t->ipaddr;
+ arg.net = sock_net(t->asoc->base.sk);
+
+reinsert:
+ if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg,
+ &t->node, sctp_hash_params) == -EBUSY)
+ goto reinsert;
+}
- head = &sctp_assoc_hashtable[epb->hashent];
+void sctp_unhash_transport(struct sctp_transport *t)
+{
+ rhashtable_remove_fast(&sctp_transport_hashtable, &t->node,
+ sctp_hash_params);
+}
- write_lock(&head->lock);
- hlist_del_init(&epb->node);
- write_unlock(&head->lock);
+struct sctp_transport *sctp_addrs_lookup_transport(
+ struct net *net,
+ const union sctp_addr *laddr,
+ const union sctp_addr *paddr)
+{
+ struct sctp_hash_cmp_arg arg = {
+ .laddr = laddr,
+ .paddr = paddr,
+ .net = net,
+ };
+
+ return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
+ sctp_hash_params);
}
-/* Remove association from the hash table. Local BH-safe. */
-void sctp_unhash_established(struct sctp_association *asoc)
+struct sctp_transport *sctp_epaddr_lookup_transport(
+ const struct sctp_endpoint *ep,
+ const union sctp_addr *paddr)
{
- if (asoc->temp)
- return;
+ struct sctp_sockaddr_entry *addr;
+ struct net *net = sock_net(ep->base.sk);
- local_bh_disable();
- __sctp_unhash_established(asoc);
- local_bh_enable();
+ addr = list_entry(ep->base.bind_addr.address_list.next,
+ struct sctp_sockaddr_entry, list);
+
+ return sctp_addrs_lookup_transport(net, &addr->a, paddr);
}
/* Look up an association. */
@@ -850,38 +920,19 @@ static struct sctp_association *__sctp_lookup_association(
const union sctp_addr *peer,
struct sctp_transport **pt)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- struct sctp_association *asoc;
- struct sctp_transport *transport;
- int hash;
+ struct sctp_transport *t;
- /* Optimize here for direct hit, only listening connections can
- * have wildcards anyways.
- */
- hash = sctp_assoc_hashfn(net, ntohs(local->v4.sin_port),
- ntohs(peer->v4.sin_port));
- head = &sctp_assoc_hashtable[hash];
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- asoc = sctp_assoc(epb);
- transport = sctp_assoc_is_match(asoc, net, local, peer);
- if (transport)
- goto hit;
- }
-
- read_unlock(&head->lock);
+ t = sctp_addrs_lookup_transport(net, local, peer);
+ if (!t || t->dead || t->asoc->temp)
+ return NULL;
- return NULL;
+ sctp_association_hold(t->asoc);
+ *pt = t;
-hit:
- *pt = transport;
- sctp_association_hold(asoc);
- read_unlock(&head->lock);
- return asoc;
+ return t->asoc;
}
-/* Look up an association. BH-safe. */
+/* Look up an association. protected by RCU read lock */
static
struct sctp_association *sctp_lookup_association(struct net *net,
const union sctp_addr *laddr,
@@ -890,9 +941,9 @@ struct sctp_association *sctp_lookup_association(struct net *net,
{
struct sctp_association *asoc;
- local_bh_disable();
+ rcu_read_lock();
asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
- local_bh_enable();
+ rcu_read_unlock();
return asoc;
}
diff --git a/net/sctp/output.c b/net/sctp/output.c
index abe7c2db2412..9d610eddd19e 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -534,7 +534,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/
if (!sctp_checksum_disable) {
- if (!(dst->dev->features & NETIF_F_SCTP_CSUM) ||
+ if (!(dst->dev->features & NETIF_F_SCTP_CRC) ||
(dst_xfrm(dst) != NULL) || packet->ipfragok) {
sh->checksum = sctp_compute_cksum(nskb, 0);
} else {
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 0697eda5aed8..dfa7eeccb537 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -281,88 +281,136 @@ void sctp_eps_proc_exit(struct net *net)
remove_proc_entry("eps", net->sctp.proc_net_sctp);
}
+struct sctp_ht_iter {
+ struct seq_net_private p;
+ struct rhashtable_iter hti;
+};
-static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
+static struct sctp_transport *sctp_transport_get_next(struct seq_file *seq)
{
- if (*pos >= sctp_assoc_hashsize)
- return NULL;
+ struct sctp_ht_iter *iter = seq->private;
+ struct sctp_transport *t;
- if (*pos < 0)
- *pos = 0;
+ t = rhashtable_walk_next(&iter->hti);
+ for (; t; t = rhashtable_walk_next(&iter->hti)) {
+ if (IS_ERR(t)) {
+ if (PTR_ERR(t) == -EAGAIN)
+ continue;
+ break;
+ }
- if (*pos == 0)
- seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
- "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
- "RPORT LADDRS <-> RADDRS "
- "HBINT INS OUTS MAXRT T1X T2X RTXC "
- "wmema wmemq sndbuf rcvbuf\n");
+ if (net_eq(sock_net(t->asoc->base.sk), seq_file_net(seq)) &&
+ t->asoc->peer.primary_path == t)
+ break;
+ }
- return (void *)pos;
+ return t;
}
-static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
+static struct sctp_transport *sctp_transport_get_idx(struct seq_file *seq,
+ loff_t pos)
+{
+ void *obj;
+
+ while (pos && (obj = sctp_transport_get_next(seq)) && !IS_ERR(obj))
+ pos--;
+
+ return obj;
+}
+
+static int sctp_transport_walk_start(struct seq_file *seq)
{
+ struct sctp_ht_iter *iter = seq->private;
+ int err;
+
+ err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti);
+ if (err)
+ return err;
+
+ err = rhashtable_walk_start(&iter->hti);
+
+ return err == -EAGAIN ? 0 : err;
}
+static void sctp_transport_walk_stop(struct seq_file *seq)
+{
+ struct sctp_ht_iter *iter = seq->private;
+
+ rhashtable_walk_stop(&iter->hti);
+ rhashtable_walk_exit(&iter->hti);
+}
+
+static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ int err = sctp_transport_walk_start(seq);
+
+ if (err)
+ return ERR_PTR(err);
+
+ return *pos ? sctp_transport_get_idx(seq, *pos) : SEQ_START_TOKEN;
+}
+
+static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
+{
+ sctp_transport_walk_stop(seq);
+}
static void *sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
/* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ struct sctp_transport *transport;
struct sctp_association *assoc;
+ struct sctp_ep_common *epb;
struct sock *sk;
- int hash = *(loff_t *)v;
-
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
- head = &sctp_assoc_hashtable[hash];
- local_bh_disable();
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- assoc = sctp_assoc(epb);
- sk = epb->sk;
- if (!net_eq(sock_net(sk), seq_file_net(seq)))
- continue;
- seq_printf(seq,
- "%8pK %8pK %-3d %-3d %-2d %-4d "
- "%4d %8d %8d %7u %5lu %-5d %5d ",
- assoc, sk, sctp_sk(sk)->type, sk->sk_state,
- assoc->state, hash,
- assoc->assoc_id,
- assoc->sndbuf_used,
- atomic_read(&assoc->rmem_alloc),
- from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
- sock_i_ino(sk),
- epb->bind_addr.port,
- assoc->peer.port);
- seq_printf(seq, " ");
- sctp_seq_dump_local_addrs(seq, epb);
- seq_printf(seq, "<-> ");
- sctp_seq_dump_remote_addrs(seq, assoc);
- seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
- "%8d %8d %8d %8d",
- assoc->hbinterval, assoc->c.sinit_max_instreams,
- assoc->c.sinit_num_ostreams, assoc->max_retrans,
- assoc->init_retries, assoc->shutdown_retries,
- assoc->rtx_data_chunks,
- atomic_read(&sk->sk_wmem_alloc),
- sk->sk_wmem_queued,
- sk->sk_sndbuf,
- sk->sk_rcvbuf);
- seq_printf(seq, "\n");
+ if (v == SEQ_START_TOKEN) {
+ seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
+ "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
+ "RPORT LADDRS <-> RADDRS "
+ "HBINT INS OUTS MAXRT T1X T2X RTXC "
+ "wmema wmemq sndbuf rcvbuf\n");
+ return 0;
}
- read_unlock(&head->lock);
- local_bh_enable();
+
+ transport = (struct sctp_transport *)v;
+ assoc = transport->asoc;
+ epb = &assoc->base;
+ sk = epb->sk;
+
+ seq_printf(seq,
+ "%8pK %8pK %-3d %-3d %-2d %-4d "
+ "%4d %8d %8d %7u %5lu %-5d %5d ",
+ assoc, sk, sctp_sk(sk)->type, sk->sk_state,
+ assoc->state, 0,
+ assoc->assoc_id,
+ assoc->sndbuf_used,
+ atomic_read(&assoc->rmem_alloc),
+ from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+ sock_i_ino(sk),
+ epb->bind_addr.port,
+ assoc->peer.port);
+ seq_printf(seq, " ");
+ sctp_seq_dump_local_addrs(seq, epb);
+ seq_printf(seq, "<-> ");
+ sctp_seq_dump_remote_addrs(seq, assoc);
+ seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
+ "%8d %8d %8d %8d",
+ assoc->hbinterval, assoc->c.sinit_max_instreams,
+ assoc->c.sinit_num_ostreams, assoc->max_retrans,
+ assoc->init_retries, assoc->shutdown_retries,
+ assoc->rtx_data_chunks,
+ atomic_read(&sk->sk_wmem_alloc),
+ sk->sk_wmem_queued,
+ sk->sk_sndbuf,
+ sk->sk_rcvbuf);
+ seq_printf(seq, "\n");
return 0;
}
@@ -378,7 +426,7 @@ static const struct seq_operations sctp_assoc_ops = {
static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &sctp_assoc_ops,
- sizeof(struct seq_net_private));
+ sizeof(struct sctp_ht_iter));
}
static const struct file_operations sctp_assocs_seq_fops = {
@@ -409,112 +457,94 @@ void sctp_assocs_proc_exit(struct net *net)
static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos)
{
- if (*pos >= sctp_assoc_hashsize)
- return NULL;
-
- if (*pos < 0)
- *pos = 0;
+ int err = sctp_transport_walk_start(seq);
- if (*pos == 0)
- seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
- "REM_ADDR_RTX START STATE\n");
+ if (err)
+ return ERR_PTR(err);
- return (void *)pos;
+ return *pos ? sctp_transport_get_idx(seq, *pos) : SEQ_START_TOKEN;
}
static void *sctp_remaddr_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v)
{
+ sctp_transport_walk_stop(seq);
}
static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
struct sctp_association *assoc;
struct sctp_transport *tsp;
- int hash = *(loff_t *)v;
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
+ if (v == SEQ_START_TOKEN) {
+ seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
+ "REM_ADDR_RTX START STATE\n");
+ return 0;
+ }
- head = &sctp_assoc_hashtable[hash];
- local_bh_disable();
- read_lock(&head->lock);
- rcu_read_lock();
- sctp_for_each_hentry(epb, &head->chain) {
- if (!net_eq(sock_net(epb->sk), seq_file_net(seq)))
+ tsp = (struct sctp_transport *)v;
+ assoc = tsp->asoc;
+
+ list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
+ transports) {
+ if (tsp->dead)
continue;
- assoc = sctp_assoc(epb);
- list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
- transports) {
- if (tsp->dead)
- continue;
+ /*
+ * The remote address (ADDR)
+ */
+ tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
+ seq_printf(seq, " ");
+ /*
+ * The association ID (ASSOC_ID)
+ */
+ seq_printf(seq, "%d ", tsp->asoc->assoc_id);
+
+ /*
+ * If the Heartbeat is active (HB_ACT)
+ * Note: 1 = Active, 0 = Inactive
+ */
+ seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
+
+ /*
+ * Retransmit time out (RTO)
+ */
+ seq_printf(seq, "%lu ", tsp->rto);
+
+ /*
+ * Maximum path retransmit count (PATH_MAX_RTX)
+ */
+ seq_printf(seq, "%d ", tsp->pathmaxrxt);
+
+ /*
+ * remote address retransmit count (REM_ADDR_RTX)
+ * Note: We don't have a way to tally this at the moment
+ * so lets just leave it as zero for the moment
+ */
+ seq_puts(seq, "0 ");
+
+ /*
+ * remote address start time (START). This is also not
+ * currently implemented, but we can record it with a
+ * jiffies marker in a subsequent patch
+ */
+ seq_puts(seq, "0 ");
+
+ /*
+ * The current state of this destination. I.e.
+ * SCTP_ACTIVE, SCTP_INACTIVE, ...
+ */
+ seq_printf(seq, "%d", tsp->state);
- /*
- * The remote address (ADDR)
- */
- tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
- seq_printf(seq, " ");
-
- /*
- * The association ID (ASSOC_ID)
- */
- seq_printf(seq, "%d ", tsp->asoc->assoc_id);
-
- /*
- * If the Heartbeat is active (HB_ACT)
- * Note: 1 = Active, 0 = Inactive
- */
- seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
-
- /*
- * Retransmit time out (RTO)
- */
- seq_printf(seq, "%lu ", tsp->rto);
-
- /*
- * Maximum path retransmit count (PATH_MAX_RTX)
- */
- seq_printf(seq, "%d ", tsp->pathmaxrxt);
-
- /*
- * remote address retransmit count (REM_ADDR_RTX)
- * Note: We don't have a way to tally this at the moment
- * so lets just leave it as zero for the moment
- */
- seq_puts(seq, "0 ");
-
- /*
- * remote address start time (START). This is also not
- * currently implemented, but we can record it with a
- * jiffies marker in a subsequent patch
- */
- seq_puts(seq, "0 ");
-
- /*
- * The current state of this destination. I.e.
- * SCTP_ACTIVE, SCTP_INACTIVE, ...
- */
- seq_printf(seq, "%d", tsp->state);
-
- seq_printf(seq, "\n");
- }
+ seq_printf(seq, "\n");
}
- rcu_read_unlock();
- read_unlock(&head->lock);
- local_bh_enable();
-
return 0;
-
}
static const struct seq_operations sctp_remaddr_ops = {
@@ -533,7 +563,7 @@ void sctp_remaddr_proc_exit(struct net *net)
static int sctp_remaddr_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &sctp_remaddr_ops,
- sizeof(struct seq_net_private));
+ sizeof(struct sctp_ht_iter));
}
static const struct file_operations sctp_remaddr_seq_fops = {
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 3d9ea9a48289..ab0d538a74ed 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1223,6 +1223,9 @@ static int __net_init sctp_defaults_init(struct net *net)
/* Max.Burst - 4 */
net->sctp.max_burst = SCTP_DEFAULT_MAX_BURST;
+ /* Enable pf state by default */
+ net->sctp.pf_enable = 1;
+
/* Association.Max.Retrans - 10 attempts
* Path.Max.Retrans - 5 attempts (per destination address)
* Max.Init.Retransmits - 8 attempts
@@ -1413,24 +1416,6 @@ static __init int sctp_init(void)
for (order = 0; (1UL << order) < goal; order++)
;
- do {
- sctp_assoc_hashsize = (1UL << order) * PAGE_SIZE /
- sizeof(struct sctp_hashbucket);
- if ((sctp_assoc_hashsize > (64 * 1024)) && order > 0)
- continue;
- sctp_assoc_hashtable = (struct sctp_hashbucket *)
- __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order);
- } while (!sctp_assoc_hashtable && --order > 0);
- if (!sctp_assoc_hashtable) {
- pr_err("Failed association hash alloc\n");
- status = -ENOMEM;
- goto err_ahash_alloc;
- }
- for (i = 0; i < sctp_assoc_hashsize; i++) {
- rwlock_init(&sctp_assoc_hashtable[i].lock);
- INIT_HLIST_HEAD(&sctp_assoc_hashtable[i].chain);
- }
-
/* Allocate and initialize the endpoint hash table. */
sctp_ep_hashsize = 64;
sctp_ep_hashtable =
@@ -1452,7 +1437,7 @@ static __init int sctp_init(void)
if ((sctp_port_hashsize > (64 * 1024)) && order > 0)
continue;
sctp_port_hashtable = (struct sctp_bind_hashbucket *)
- __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order);
+ __get_free_pages(GFP_KERNEL | __GFP_NOWARN, order);
} while (!sctp_port_hashtable && --order > 0);
if (!sctp_port_hashtable) {
pr_err("Failed bind hash alloc\n");
@@ -1464,8 +1449,10 @@ static __init int sctp_init(void)
INIT_HLIST_HEAD(&sctp_port_hashtable[i].chain);
}
- pr_info("Hash tables configured (established %d bind %d)\n",
- sctp_assoc_hashsize, sctp_port_hashsize);
+ if (sctp_transport_hashtable_init())
+ goto err_thash_alloc;
+
+ pr_info("Hash tables configured (bind %d)\n", sctp_port_hashsize);
sctp_sysctl_register();
@@ -1518,12 +1505,10 @@ err_register_defaults:
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
err_bhash_alloc:
+ sctp_transport_hashtable_destroy();
+err_thash_alloc:
kfree(sctp_ep_hashtable);
err_ehash_alloc:
- free_pages((unsigned long)sctp_assoc_hashtable,
- get_order(sctp_assoc_hashsize *
- sizeof(struct sctp_hashbucket)));
-err_ahash_alloc:
percpu_counter_destroy(&sctp_sockets_allocated);
err_percpu_counter_init:
kmem_cache_destroy(sctp_chunk_cachep);
@@ -1557,13 +1542,11 @@ static __exit void sctp_exit(void)
sctp_sysctl_unregister();
- free_pages((unsigned long)sctp_assoc_hashtable,
- get_order(sctp_assoc_hashsize *
- sizeof(struct sctp_hashbucket)));
- kfree(sctp_ep_hashtable);
free_pages((unsigned long)sctp_port_hashtable,
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
+ kfree(sctp_ep_hashtable);
+ sctp_transport_hashtable_destroy();
percpu_counter_destroy(&sctp_sockets_allocated);
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 6098d4c42fa9..2e21384697c2 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -63,7 +63,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
struct sctp_endpoint *ep,
- struct sctp_association *asoc,
+ struct sctp_association **asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
@@ -477,6 +477,8 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
struct sctp_transport *transport,
int is_hb)
{
+ struct net *net = sock_net(asoc->base.sk);
+
/* The check for association's overall error counter exceeding the
* threshold is done in the state function.
*/
@@ -503,7 +505,8 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
* is SCTP_ACTIVE, then mark this transport as Partially Failed,
* see SCTP Quick Failover Draft, section 5.1
*/
- if ((transport->state == SCTP_ACTIVE) &&
+ if (net->sctp.pf_enable &&
+ (transport->state == SCTP_ACTIVE) &&
(asoc->pf_retrans < transport->pathmaxrxt) &&
(transport->error_count > asoc->pf_retrans)) {
@@ -863,7 +866,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
(!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
return;
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
@@ -1123,7 +1125,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
debug_post_sfn();
error = sctp_side_effects(event_type, subtype, state,
- ep, asoc, event_arg, status,
+ ep, &asoc, event_arg, status,
&commands, gfp);
debug_post_sfx();
@@ -1136,7 +1138,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
struct sctp_endpoint *ep,
- struct sctp_association *asoc,
+ struct sctp_association **asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
@@ -1151,7 +1153,7 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
* disposition SCTP_DISPOSITION_CONSUME.
*/
if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state,
- ep, asoc,
+ ep, *asoc,
event_arg, status,
commands, gfp)))
goto bail;
@@ -1174,11 +1176,12 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_DISPOSITION_DELETE_TCB:
+ case SCTP_DISPOSITION_ABORT:
/* This should now be a command. */
+ *asoc = NULL;
break;
case SCTP_DISPOSITION_CONSUME:
- case SCTP_DISPOSITION_ABORT:
/*
* We should no longer have much work to do here as the
* real work has been done as explicit commands above.
@@ -1266,7 +1269,6 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
asoc = cmd->obj.asoc;
BUG_ON(asoc->peer.primary_path == NULL);
sctp_endpoint_add_asoc(ep, asoc);
- sctp_hash_established(asoc);
break;
case SCTP_CMD_UPDATE_ASSOC:
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 22c2bf367d7e..f1f08c8f277b 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -2976,7 +2976,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net,
SCTP_INC_STATS(net, SCTP_MIB_IN_DATA_CHUNK_DISCARDS);
goto discard_force;
case SCTP_IERROR_NO_DATA:
- goto consume;
+ return SCTP_DISPOSITION_ABORT;
case SCTP_IERROR_PROTO_VIOLATION:
return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
(u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
@@ -3043,9 +3043,6 @@ discard_noforce:
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, force);
return SCTP_DISPOSITION_DISCARD;
-consume:
- return SCTP_DISPOSITION_CONSUME;
-
}
/*
@@ -3093,7 +3090,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net,
case SCTP_IERROR_BAD_STREAM:
break;
case SCTP_IERROR_NO_DATA:
- goto consume;
+ return SCTP_DISPOSITION_ABORT;
case SCTP_IERROR_PROTO_VIOLATION:
return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
(u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
@@ -3119,7 +3116,6 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net,
SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
}
-consume:
return SCTP_DISPOSITION_CONSUME;
}
@@ -4825,9 +4821,6 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
* if necessary to fill gaps.
*/
struct sctp_chunk *abort = arg;
- sctp_disposition_t retval;
-
- retval = SCTP_DISPOSITION_CONSUME;
if (abort)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
@@ -4845,7 +4838,7 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
- return retval;
+ return SCTP_DISPOSITION_ABORT;
}
/* We tried an illegal operation on an association which is closed. */
@@ -4960,12 +4953,10 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
sctp_cmd_seq_t *commands)
{
struct sctp_chunk *abort = arg;
- sctp_disposition_t retval;
/* Stop T1-init timer */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
- retval = SCTP_DISPOSITION_CONSUME;
if (abort)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
@@ -4985,7 +4976,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_PERR(SCTP_ERROR_USER_ABORT));
- return retval;
+ return SCTP_DISPOSITION_ABORT;
}
/*
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index ef1d90fdc773..9bb80ec4c08f 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1228,7 +1228,6 @@ out_free:
* To the hash table, try to unhash it, just in case, its a noop
* if it wasn't hashed so we're safe
*/
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
return err;
@@ -1504,7 +1503,6 @@ static void sctp_close(struct sock *sk, long timeout)
* ABORT or SHUTDOWN based on the linger options.
*/
if (sctp_state(asoc, CLOSED)) {
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
continue;
}
@@ -1986,10 +1984,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
goto out_unlock;
out_free:
- if (new_asoc) {
- sctp_unhash_established(asoc);
+ if (new_asoc)
sctp_association_free(asoc);
- }
out_unlock:
release_sock(sk);
@@ -6980,7 +6976,7 @@ void sctp_data_ready(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM | POLLRDBAND);
sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 26d50c565f54..daf8554fd42a 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -308,6 +308,13 @@ static struct ctl_table sctp_net_table[] = {
.extra1 = &max_autoclose_min,
.extra2 = &max_autoclose_max,
},
+ {
+ .procname = "pf_enable",
+ .data = &init_net.sctp.pf_enable,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
{ /* sentinel */ }
};
@@ -320,7 +327,7 @@ static int proc_sctp_do_hmac_alg(struct ctl_table *ctl, int write,
struct ctl_table tbl;
bool changed = false;
char *none = "none";
- char tmp[8];
+ char tmp[8] = {0};
int ret;
memset(&tbl, 0, sizeof(struct ctl_table));
diff --git a/net/socket.c b/net/socket.c
index d730ef9dfbf0..91c2de6f5020 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2041,6 +2041,7 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
if (err)
break;
++datagrams;
+ cond_resched();
}
fput_light(sock->file, fput_needed);
@@ -2236,6 +2237,7 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
/* Out of band data, return right away */
if (msg_sys.msg_flags & MSG_OOB)
break;
+ cond_resched();
}
out_put:
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index f34e535e93bd..ebc661d3b6e3 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -345,6 +345,8 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
return sizeof(struct switchdev_obj_ipv4_fib);
case SWITCHDEV_OBJ_ID_PORT_FDB:
return sizeof(struct switchdev_obj_port_fdb);
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ return sizeof(struct switchdev_obj_port_mdb);
default:
BUG();
}
@@ -723,6 +725,7 @@ static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
u32 filter_mask)
{
struct switchdev_vlan_dump dump = {
+ .vlan.obj.orig_dev = dev,
.vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.skb = skb,
.filter_mask = filter_mask,
@@ -757,6 +760,7 @@ int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
int nlflags)
{
struct switchdev_attr attr = {
+ .orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u16 mode = BRIDGE_MODE_UNDEF;
@@ -778,6 +782,7 @@ static int switchdev_port_br_setflag(struct net_device *dev,
unsigned long brport_flag)
{
struct switchdev_attr attr = {
+ .orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u8 flag = nla_get_u8(nlattr);
@@ -853,6 +858,7 @@ static int switchdev_port_br_afspec(struct net_device *dev,
struct nlattr *attr;
struct bridge_vlan_info *vinfo;
struct switchdev_obj_port_vlan vlan = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
};
int rem;
@@ -975,6 +981,7 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
u16 vid, u16 nlm_flags)
{
struct switchdev_obj_port_fdb fdb = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
@@ -1000,6 +1007,7 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
u16 vid)
{
struct switchdev_obj_port_fdb fdb = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
@@ -1077,6 +1085,7 @@ int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *filter_dev, int idx)
{
struct switchdev_fdb_dump dump = {
+ .fdb.obj.orig_dev = dev,
.fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.dev = dev,
.skb = skb,
@@ -1135,6 +1144,7 @@ static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
if (!dev)
return NULL;
+ attr.orig_dev = dev;
if (switchdev_port_attr_get(dev, &attr))
return NULL;
@@ -1194,6 +1204,7 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
if (!dev)
return 0;
+ ipv4_fib.obj.orig_dev = dev;
err = switchdev_port_obj_add(dev, &ipv4_fib.obj);
if (!err)
fi->fib_flags |= RTNH_F_OFFLOAD;
@@ -1238,6 +1249,7 @@ int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
if (!dev)
return 0;
+ ipv4_fib.obj.orig_dev = dev;
err = switchdev_port_obj_del(dev, &ipv4_fib.obj);
if (!err)
fi->fib_flags &= ~RTNH_F_OFFLOAD;
@@ -1270,10 +1282,12 @@ static bool switchdev_port_same_parent_id(struct net_device *a,
struct net_device *b)
{
struct switchdev_attr a_attr = {
+ .orig_dev = a,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
struct switchdev_attr b_attr = {
+ .orig_dev = b,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index 9dc239dfe192..e401108360a2 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -332,131 +332,15 @@ void tipc_bcast_remove_peer(struct net *net, struct tipc_link *rcv_l)
tipc_sk_rcv(net, inputq);
}
-static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
- struct tipc_stats *stats)
-{
- int i;
- struct nlattr *nest;
-
- struct nla_map {
- __u32 key;
- __u32 val;
- };
-
- struct nla_map map[] = {
- {TIPC_NLA_STATS_RX_INFO, stats->recv_info},
- {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
- {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
- {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
- {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
- {TIPC_NLA_STATS_TX_INFO, stats->sent_info},
- {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
- {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
- {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
- {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
- {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
- {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
- {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
- {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
- {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
- {TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
- {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
- {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
- {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
- (stats->accu_queue_sz / stats->queue_sz_counts) : 0}
- };
-
- nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS);
- if (!nest)
- return -EMSGSIZE;
-
- for (i = 0; i < ARRAY_SIZE(map); i++)
- if (nla_put_u32(skb, map[i].key, map[i].val))
- goto msg_full;
-
- nla_nest_end(skb, nest);
-
- return 0;
-msg_full:
- nla_nest_cancel(skb, nest);
-
- return -EMSGSIZE;
-}
-
-int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)
-{
- int err;
- void *hdr;
- struct nlattr *attrs;
- struct nlattr *prop;
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *bcl = tn->bcl;
-
- if (!bcl)
- return 0;
-
- tipc_bcast_lock(net);
-
- hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
- NLM_F_MULTI, TIPC_NL_LINK_GET);
- if (!hdr)
- return -EMSGSIZE;
-
- attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK);
- if (!attrs)
- goto msg_full;
-
- /* The broadcast link is always up */
- if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
- goto attr_msg_full;
-
- if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
- goto attr_msg_full;
- if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
- goto attr_msg_full;
- if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->rcv_nxt))
- goto attr_msg_full;
- if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->snd_nxt))
- goto attr_msg_full;
-
- prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP);
- if (!prop)
- goto attr_msg_full;
- if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->window))
- goto prop_msg_full;
- nla_nest_end(msg->skb, prop);
-
- err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
- if (err)
- goto attr_msg_full;
-
- tipc_bcast_unlock(net);
- nla_nest_end(msg->skb, attrs);
- genlmsg_end(msg->skb, hdr);
-
- return 0;
-
-prop_msg_full:
- nla_nest_cancel(msg->skb, prop);
-attr_msg_full:
- nla_nest_cancel(msg->skb, attrs);
-msg_full:
- tipc_bcast_unlock(net);
- genlmsg_cancel(msg->skb, hdr);
-
- return -EMSGSIZE;
-}
-
int tipc_bclink_reset_stats(struct net *net)
{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *bcl = tn->bcl;
+ struct tipc_link *l = tipc_bc_sndlink(net);
- if (!bcl)
+ if (!l)
return -ENOPROTOOPT;
tipc_bcast_lock(net);
- memset(&bcl->stats, 0, sizeof(bcl->stats));
+ tipc_link_reset_stats(l);
tipc_bcast_unlock(net);
return 0;
}
@@ -530,9 +414,7 @@ enomem:
void tipc_bcast_reinit(struct net *net)
{
- struct tipc_bc_base *b = tipc_bc_base(net);
-
- msg_set_prevnode(b->link->pmsg, tipc_own_addr(net));
+ tipc_link_reinit(tipc_bc_sndlink(net), tipc_own_addr(net));
}
void tipc_bcast_stop(struct net *net)
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index 2855b9356a15..1944c6c00bb9 100644
--- a/net/tipc/bcast.h
+++ b/net/tipc/bcast.h
@@ -43,6 +43,7 @@ struct tipc_node;
struct tipc_msg;
struct tipc_nl_msg;
struct tipc_node_map;
+extern const char tipc_bclink_name[];
int tipc_bcast_init(struct net *net);
void tipc_bcast_reinit(struct net *net);
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 648f2a67f314..802ffad3200d 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -71,7 +71,7 @@ static const struct nla_policy tipc_nl_media_policy[TIPC_NLA_MEDIA_MAX + 1] = {
[TIPC_NLA_MEDIA_PROP] = { .type = NLA_NESTED }
};
-static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr);
+static void bearer_disable(struct net *net, struct tipc_bearer *b);
/**
* tipc_media_find - locates specified media object by name
@@ -107,13 +107,13 @@ static struct tipc_media *media_find_id(u8 type)
void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a)
{
char addr_str[MAX_ADDR_STR];
- struct tipc_media *m_ptr;
+ struct tipc_media *m;
int ret;
- m_ptr = media_find_id(a->media_id);
+ m = media_find_id(a->media_id);
- if (m_ptr && !m_ptr->addr2str(a, addr_str, sizeof(addr_str)))
- ret = scnprintf(buf, len, "%s(%s)", m_ptr->name, addr_str);
+ if (m && !m->addr2str(a, addr_str, sizeof(addr_str)))
+ ret = scnprintf(buf, len, "%s(%s)", m->name, addr_str);
else {
u32 i;
@@ -175,13 +175,13 @@ static int bearer_name_validate(const char *name,
struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
u32 i;
for (i = 0; i < MAX_BEARERS; i++) {
- b_ptr = rtnl_dereference(tn->bearer_list[i]);
- if (b_ptr && (!strcmp(b_ptr->name, name)))
- return b_ptr;
+ b = rtnl_dereference(tn->bearer_list[i]);
+ if (b && (!strcmp(b->name, name)))
+ return b;
}
return NULL;
}
@@ -189,24 +189,24 @@ struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name)
void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
rcu_read_lock();
- b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
- if (b_ptr)
- tipc_disc_add_dest(b_ptr->link_req);
+ b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+ if (b)
+ tipc_disc_add_dest(b->link_req);
rcu_read_unlock();
}
void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
rcu_read_lock();
- b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
- if (b_ptr)
- tipc_disc_remove_dest(b_ptr->link_req);
+ b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+ if (b)
+ tipc_disc_remove_dest(b->link_req);
rcu_read_unlock();
}
@@ -218,8 +218,8 @@ static int tipc_enable_bearer(struct net *net, const char *name,
struct nlattr *attr[])
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
- struct tipc_media *m_ptr;
+ struct tipc_bearer *b;
+ struct tipc_media *m;
struct tipc_bearer_names b_names;
char addr_string[16];
u32 bearer_id;
@@ -255,31 +255,31 @@ static int tipc_enable_bearer(struct net *net, const char *name,
return -EINVAL;
}
- m_ptr = tipc_media_find(b_names.media_name);
- if (!m_ptr) {
+ m = tipc_media_find(b_names.media_name);
+ if (!m) {
pr_warn("Bearer <%s> rejected, media <%s> not registered\n",
name, b_names.media_name);
return -EINVAL;
}
if (priority == TIPC_MEDIA_LINK_PRI)
- priority = m_ptr->priority;
+ priority = m->priority;
restart:
bearer_id = MAX_BEARERS;
with_this_prio = 1;
for (i = MAX_BEARERS; i-- != 0; ) {
- b_ptr = rtnl_dereference(tn->bearer_list[i]);
- if (!b_ptr) {
+ b = rtnl_dereference(tn->bearer_list[i]);
+ if (!b) {
bearer_id = i;
continue;
}
- if (!strcmp(name, b_ptr->name)) {
+ if (!strcmp(name, b->name)) {
pr_warn("Bearer <%s> rejected, already enabled\n",
name);
return -EINVAL;
}
- if ((b_ptr->priority == priority) &&
+ if ((b->priority == priority) &&
(++with_this_prio > 2)) {
if (priority-- == 0) {
pr_warn("Bearer <%s> rejected, duplicate priority\n",
@@ -297,35 +297,35 @@ restart:
return -EINVAL;
}
- b_ptr = kzalloc(sizeof(*b_ptr), GFP_ATOMIC);
- if (!b_ptr)
+ b = kzalloc(sizeof(*b), GFP_ATOMIC);
+ if (!b)
return -ENOMEM;
- strcpy(b_ptr->name, name);
- b_ptr->media = m_ptr;
- res = m_ptr->enable_media(net, b_ptr, attr);
+ strcpy(b->name, name);
+ b->media = m;
+ res = m->enable_media(net, b, attr);
if (res) {
pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
name, -res);
return -EINVAL;
}
- b_ptr->identity = bearer_id;
- b_ptr->tolerance = m_ptr->tolerance;
- b_ptr->window = m_ptr->window;
- b_ptr->domain = disc_domain;
- b_ptr->net_plane = bearer_id + 'A';
- b_ptr->priority = priority;
+ b->identity = bearer_id;
+ b->tolerance = m->tolerance;
+ b->window = m->window;
+ b->domain = disc_domain;
+ b->net_plane = bearer_id + 'A';
+ b->priority = priority;
- res = tipc_disc_create(net, b_ptr, &b_ptr->bcast_addr);
+ res = tipc_disc_create(net, b, &b->bcast_addr);
if (res) {
- bearer_disable(net, b_ptr);
+ bearer_disable(net, b);
pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
name);
return -EINVAL;
}
- rcu_assign_pointer(tn->bearer_list[bearer_id], b_ptr);
+ rcu_assign_pointer(tn->bearer_list[bearer_id], b);
pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
name,
@@ -336,11 +336,11 @@ restart:
/**
* tipc_reset_bearer - Reset all links established over this bearer
*/
-static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr)
+static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b)
{
- pr_info("Resetting bearer <%s>\n", b_ptr->name);
- tipc_node_delete_links(net, b_ptr->identity);
- tipc_disc_reset(net, b_ptr);
+ pr_info("Resetting bearer <%s>\n", b->name);
+ tipc_node_delete_links(net, b->identity);
+ tipc_disc_reset(net, b);
return 0;
}
@@ -349,26 +349,26 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr)
*
* Note: This routine assumes caller holds RTNL lock.
*/
-static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr)
+static void bearer_disable(struct net *net, struct tipc_bearer *b)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
u32 i;
- pr_info("Disabling bearer <%s>\n", b_ptr->name);
- b_ptr->media->disable_media(b_ptr);
+ pr_info("Disabling bearer <%s>\n", b->name);
+ b->media->disable_media(b);
- tipc_node_delete_links(net, b_ptr->identity);
- RCU_INIT_POINTER(b_ptr->media_ptr, NULL);
- if (b_ptr->link_req)
- tipc_disc_delete(b_ptr->link_req);
+ tipc_node_delete_links(net, b->identity);
+ RCU_INIT_POINTER(b->media_ptr, NULL);
+ if (b->link_req)
+ tipc_disc_delete(b->link_req);
for (i = 0; i < MAX_BEARERS; i++) {
- if (b_ptr == rtnl_dereference(tn->bearer_list[i])) {
+ if (b == rtnl_dereference(tn->bearer_list[i])) {
RCU_INIT_POINTER(tn->bearer_list[i], NULL);
break;
}
}
- kfree_rcu(b_ptr, rcu);
+ kfree_rcu(b, rcu);
}
int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b,
@@ -411,7 +411,7 @@ void tipc_disable_l2_media(struct tipc_bearer *b)
/**
* tipc_l2_send_msg - send a TIPC packet out over an L2 interface
* @buf: the packet to be sent
- * @b_ptr: the bearer through which the packet is to be sent
+ * @b: the bearer through which the packet is to be sent
* @dest: peer destination address
*/
int tipc_l2_send_msg(struct net *net, struct sk_buff *skb,
@@ -532,14 +532,14 @@ void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
rcu_read_lock();
- b_ptr = rcu_dereference_rtnl(dev->tipc_ptr);
- if (likely(b_ptr)) {
+ b = rcu_dereference_rtnl(dev->tipc_ptr);
+ if (likely(b)) {
if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
buf->next = NULL;
- tipc_rcv(dev_net(dev), buf, b_ptr);
+ tipc_rcv(dev_net(dev), buf, b);
rcu_read_unlock();
return NET_RX_SUCCESS;
}
@@ -564,13 +564,13 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(dev);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
- b_ptr = rtnl_dereference(dev->tipc_ptr);
- if (!b_ptr)
+ b = rtnl_dereference(dev->tipc_ptr);
+ if (!b)
return NOTIFY_DONE;
- b_ptr->mtu = dev->mtu;
+ b->mtu = dev->mtu;
switch (evt) {
case NETDEV_CHANGE:
@@ -578,16 +578,16 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
break;
case NETDEV_GOING_DOWN:
case NETDEV_CHANGEMTU:
- tipc_reset_bearer(net, b_ptr);
+ tipc_reset_bearer(net, b);
break;
case NETDEV_CHANGEADDR:
- b_ptr->media->raw2addr(b_ptr, &b_ptr->addr,
+ b->media->raw2addr(b, &b->addr,
(char *)dev->dev_addr);
- tipc_reset_bearer(net, b_ptr);
+ tipc_reset_bearer(net, b);
break;
case NETDEV_UNREGISTER:
case NETDEV_CHANGENAME:
- bearer_disable(dev_net(dev), b_ptr);
+ bearer_disable(dev_net(dev), b);
break;
}
return NOTIFY_OK;
@@ -623,13 +623,13 @@ void tipc_bearer_cleanup(void)
void tipc_bearer_stop(struct net *net)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
u32 i;
for (i = 0; i < MAX_BEARERS; i++) {
- b_ptr = rtnl_dereference(tn->bearer_list[i]);
- if (b_ptr) {
- bearer_disable(net, b_ptr);
+ b = rtnl_dereference(tn->bearer_list[i]);
+ if (b) {
+ bearer_disable(net, b);
tn->bearer_list[i] = NULL;
}
}
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index 552185bc4773..e31820516774 100644
--- a/net/tipc/bearer.h
+++ b/net/tipc/bearer.h
@@ -103,11 +103,11 @@ struct tipc_bearer;
*/
struct tipc_media {
int (*send_msg)(struct net *net, struct sk_buff *buf,
- struct tipc_bearer *b_ptr,
+ struct tipc_bearer *b,
struct tipc_media_addr *dest);
- int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr,
+ int (*enable_media)(struct net *net, struct tipc_bearer *b,
struct nlattr *attr[]);
- void (*disable_media)(struct tipc_bearer *b_ptr);
+ void (*disable_media)(struct tipc_bearer *b);
int (*addr2str)(struct tipc_media_addr *addr,
char *strbuf,
int bufsz);
@@ -176,7 +176,7 @@ struct tipc_bearer_names {
* TIPC routines available to supported media types
*/
-void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr);
+void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b);
/*
* Routines made available to TIPC by supported media types
diff --git a/net/tipc/core.h b/net/tipc/core.h
index 18e95a8020cd..5504d63503df 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -118,6 +118,11 @@ static inline int tipc_netid(struct net *net)
return tipc_net(net)->net_id;
}
+static inline struct list_head *tipc_nodes(struct net *net)
+{
+ return &tipc_net(net)->node_list;
+}
+
static inline u16 mod(u16 x)
{
return x & 0xffffu;
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index afe8c47c4085..f1e738e80535 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -75,14 +75,14 @@ struct tipc_link_req {
* tipc_disc_init_msg - initialize a link setup message
* @net: the applicable net namespace
* @type: message type (request or response)
- * @b_ptr: ptr to bearer issuing message
+ * @b: ptr to bearer issuing message
*/
static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type,
- struct tipc_bearer *b_ptr)
+ struct tipc_bearer *b)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
struct tipc_msg *msg;
- u32 dest_domain = b_ptr->domain;
+ u32 dest_domain = b->domain;
msg = buf_msg(buf);
tipc_msg_init(tn->own_addr, msg, LINK_CONFIG, type,
@@ -92,16 +92,16 @@ static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type,
msg_set_node_capabilities(msg, TIPC_NODE_CAPABILITIES);
msg_set_dest_domain(msg, dest_domain);
msg_set_bc_netid(msg, tn->net_id);
- b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr);
+ b->media->addr2msg(msg_media_addr(msg), &b->addr);
}
/**
* disc_dupl_alert - issue node address duplication alert
- * @b_ptr: pointer to bearer detecting duplication
+ * @b: pointer to bearer detecting duplication
* @node_addr: duplicated node address
* @media_addr: media address advertised by duplicated node
*/
-static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
+static void disc_dupl_alert(struct tipc_bearer *b, u32 node_addr,
struct tipc_media_addr *media_addr)
{
char node_addr_str[16];
@@ -111,7 +111,7 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
tipc_media_addr_printf(media_addr_str, sizeof(media_addr_str),
media_addr);
pr_warn("Duplicate %s using %s seen on <%s>\n", node_addr_str,
- media_addr_str, b_ptr->name);
+ media_addr_str, b->name);
}
/**
@@ -261,13 +261,13 @@ exit:
/**
* tipc_disc_create - create object to send periodic link setup requests
* @net: the applicable net namespace
- * @b_ptr: ptr to bearer issuing requests
+ * @b: ptr to bearer issuing requests
* @dest: destination address for request messages
* @dest_domain: network domain to which links can be established
*
* Returns 0 if successful, otherwise -errno.
*/
-int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr,
+int tipc_disc_create(struct net *net, struct tipc_bearer *b,
struct tipc_media_addr *dest)
{
struct tipc_link_req *req;
@@ -282,17 +282,17 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr,
return -ENOMEM;
}
- tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr);
+ tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b);
memcpy(&req->dest, dest, sizeof(*dest));
req->net = net;
- req->bearer_id = b_ptr->identity;
- req->domain = b_ptr->domain;
+ req->bearer_id = b->identity;
+ req->domain = b->domain;
req->num_nodes = 0;
req->timer_intv = TIPC_LINK_REQ_INIT;
spin_lock_init(&req->lock);
setup_timer(&req->timer, disc_timeout, (unsigned long)req);
mod_timer(&req->timer, jiffies + req->timer_intv);
- b_ptr->link_req = req;
+ b->link_req = req;
skb = skb_clone(req->buf, GFP_ATOMIC);
if (skb)
tipc_bearer_xmit_skb(net, req->bearer_id, skb, &req->dest);
@@ -313,19 +313,19 @@ void tipc_disc_delete(struct tipc_link_req *req)
/**
* tipc_disc_reset - reset object to send periodic link setup requests
* @net: the applicable net namespace
- * @b_ptr: ptr to bearer issuing requests
+ * @b: ptr to bearer issuing requests
* @dest_domain: network domain to which links can be established
*/
-void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr)
+void tipc_disc_reset(struct net *net, struct tipc_bearer *b)
{
- struct tipc_link_req *req = b_ptr->link_req;
+ struct tipc_link_req *req = b->link_req;
struct sk_buff *skb;
spin_lock_bh(&req->lock);
- tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr);
+ tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b);
req->net = net;
- req->bearer_id = b_ptr->identity;
- req->domain = b_ptr->domain;
+ req->bearer_id = b->identity;
+ req->domain = b->domain;
req->num_nodes = 0;
req->timer_intv = TIPC_LINK_REQ_INIT;
mod_timer(&req->timer, jiffies + req->timer_intv);
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 91aea071ab27..0c2944fb9ae0 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -45,28 +45,156 @@
#include <linux/pkt_sched.h>
+struct tipc_stats {
+ u32 sent_info; /* used in counting # sent packets */
+ u32 recv_info; /* used in counting # recv'd packets */
+ u32 sent_states;
+ u32 recv_states;
+ u32 sent_probes;
+ u32 recv_probes;
+ u32 sent_nacks;
+ u32 recv_nacks;
+ u32 sent_acks;
+ u32 sent_bundled;
+ u32 sent_bundles;
+ u32 recv_bundled;
+ u32 recv_bundles;
+ u32 retransmitted;
+ u32 sent_fragmented;
+ u32 sent_fragments;
+ u32 recv_fragmented;
+ u32 recv_fragments;
+ u32 link_congs; /* # port sends blocked by congestion */
+ u32 deferred_recv;
+ u32 duplicates;
+ u32 max_queue_sz; /* send queue size high water mark */
+ u32 accu_queue_sz; /* used for send queue size profiling */
+ u32 queue_sz_counts; /* used for send queue size profiling */
+ u32 msg_length_counts; /* used for message length profiling */
+ u32 msg_lengths_total; /* used for message length profiling */
+ u32 msg_length_profile[7]; /* used for msg. length profiling */
+};
+
+/**
+ * struct tipc_link - TIPC link data structure
+ * @addr: network address of link's peer node
+ * @name: link name character string
+ * @media_addr: media address to use when sending messages over link
+ * @timer: link timer
+ * @net: pointer to namespace struct
+ * @refcnt: reference counter for permanent references (owner node & timer)
+ * @peer_session: link session # being used by peer end of link
+ * @peer_bearer_id: bearer id used by link's peer endpoint
+ * @bearer_id: local bearer id used by link
+ * @tolerance: minimum link continuity loss needed to reset link [in ms]
+ * @keepalive_intv: link keepalive timer interval
+ * @abort_limit: # of unacknowledged continuity probes needed to reset link
+ * @state: current state of link FSM
+ * @peer_caps: bitmap describing capabilities of peer node
+ * @silent_intv_cnt: # of timer intervals without any reception from peer
+ * @proto_msg: template for control messages generated by link
+ * @pmsg: convenience pointer to "proto_msg" field
+ * @priority: current link priority
+ * @net_plane: current link network plane ('A' through 'H')
+ * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
+ * @exp_msg_count: # of tunnelled messages expected during link changeover
+ * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
+ * @mtu: current maximum packet size for this link
+ * @advertised_mtu: advertised own mtu when link is being established
+ * @transmitq: queue for sent, non-acked messages
+ * @backlogq: queue for messages waiting to be sent
+ * @snt_nxt: next sequence number to use for outbound messages
+ * @last_retransmitted: sequence number of most recently retransmitted message
+ * @stale_count: # of identical retransmit requests made by peer
+ * @ackers: # of peers that needs to ack each packet before it can be released
+ * @acked: # last packet acked by a certain peer. Used for broadcast.
+ * @rcv_nxt: next sequence number to expect for inbound messages
+ * @deferred_queue: deferred queue saved OOS b'cast message received from node
+ * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
+ * @inputq: buffer queue for messages to be delivered upwards
+ * @namedq: buffer queue for name table messages to be delivered upwards
+ * @next_out: ptr to first unsent outbound message in queue
+ * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
+ * @long_msg_seq_no: next identifier to use for outbound fragmented messages
+ * @reasm_buf: head of partially reassembled inbound message fragments
+ * @bc_rcvr: marks that this is a broadcast receiver link
+ * @stats: collects statistics regarding link activity
+ */
+struct tipc_link {
+ u32 addr;
+ char name[TIPC_MAX_LINK_NAME];
+ struct tipc_media_addr *media_addr;
+ struct net *net;
+
+ /* Management and link supervision data */
+ u32 peer_session;
+ u32 peer_bearer_id;
+ u32 bearer_id;
+ u32 tolerance;
+ unsigned long keepalive_intv;
+ u32 abort_limit;
+ u32 state;
+ u16 peer_caps;
+ bool active;
+ u32 silent_intv_cnt;
+ struct {
+ unchar hdr[INT_H_SIZE];
+ unchar body[TIPC_MAX_IF_NAME];
+ } proto_msg;
+ struct tipc_msg *pmsg;
+ u32 priority;
+ char net_plane;
+
+ /* Failover/synch */
+ u16 drop_point;
+ struct sk_buff *failover_reasm_skb;
+
+ /* Max packet negotiation */
+ u16 mtu;
+ u16 advertised_mtu;
+
+ /* Sending */
+ struct sk_buff_head transmq;
+ struct sk_buff_head backlogq;
+ struct {
+ u16 len;
+ u16 limit;
+ } backlog[5];
+ u16 snd_nxt;
+ u16 last_retransm;
+ u16 window;
+ u32 stale_count;
+
+ /* Reception */
+ u16 rcv_nxt;
+ u32 rcv_unacked;
+ struct sk_buff_head deferdq;
+ struct sk_buff_head *inputq;
+ struct sk_buff_head *namedq;
+
+ /* Congestion handling */
+ struct sk_buff_head wakeupq;
+
+ /* Fragmentation/reassembly */
+ struct sk_buff *reasm_buf;
+
+ /* Broadcast */
+ u16 ackers;
+ u16 acked;
+ struct tipc_link *bc_rcvlink;
+ struct tipc_link *bc_sndlink;
+ int nack_state;
+ bool bc_peer_is_up;
+
+ /* Statistics */
+ struct tipc_stats stats;
+};
+
/*
* Error message prefixes
*/
static const char *link_co_err = "Link tunneling error, ";
static const char *link_rst_msg = "Resetting link ";
-static const char tipc_bclink_name[] = "broadcast-link";
-
-static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
- [TIPC_NLA_LINK_UNSPEC] = { .type = NLA_UNSPEC },
- [TIPC_NLA_LINK_NAME] = {
- .type = NLA_STRING,
- .len = TIPC_MAX_LINK_NAME
- },
- [TIPC_NLA_LINK_MTU] = { .type = NLA_U32 },
- [TIPC_NLA_LINK_BROADCAST] = { .type = NLA_FLAG },
- [TIPC_NLA_LINK_UP] = { .type = NLA_FLAG },
- [TIPC_NLA_LINK_ACTIVE] = { .type = NLA_FLAG },
- [TIPC_NLA_LINK_PROP] = { .type = NLA_NESTED },
- [TIPC_NLA_LINK_STATS] = { .type = NLA_NESTED },
- [TIPC_NLA_LINK_RX] = { .type = NLA_U32 },
- [TIPC_NLA_LINK_TX] = { .type = NLA_U32 }
-};
/* Properties valid for media, bearar and link */
static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
@@ -117,8 +245,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
u16 rcvgap, int tolerance, int priority,
struct sk_buff_head *xmitq);
-static void link_reset_statistics(struct tipc_link *l_ptr);
-static void link_print(struct tipc_link *l_ptr, const char *str);
+static void link_print(struct tipc_link *l, const char *str);
static void tipc_link_build_nack_msg(struct tipc_link *l,
struct sk_buff_head *xmitq);
static void tipc_link_build_bc_init_msg(struct tipc_link *l,
@@ -183,6 +310,36 @@ void tipc_link_set_active(struct tipc_link *l, bool active)
l->active = active;
}
+u32 tipc_link_id(struct tipc_link *l)
+{
+ return l->peer_bearer_id << 16 | l->bearer_id;
+}
+
+int tipc_link_window(struct tipc_link *l)
+{
+ return l->window;
+}
+
+int tipc_link_prio(struct tipc_link *l)
+{
+ return l->priority;
+}
+
+unsigned long tipc_link_tolerance(struct tipc_link *l)
+{
+ return l->tolerance;
+}
+
+struct sk_buff_head *tipc_link_inputq(struct tipc_link *l)
+{
+ return l->inputq;
+}
+
+char tipc_link_plane(struct tipc_link *l)
+{
+ return l->net_plane;
+}
+
void tipc_link_add_bc_peer(struct tipc_link *snd_l,
struct tipc_link *uc_l,
struct sk_buff_head *xmitq)
@@ -227,11 +384,31 @@ int tipc_link_mtu(struct tipc_link *l)
return l->mtu;
}
+u16 tipc_link_rcv_nxt(struct tipc_link *l)
+{
+ return l->rcv_nxt;
+}
+
+u16 tipc_link_acked(struct tipc_link *l)
+{
+ return l->acked;
+}
+
+char *tipc_link_name(struct tipc_link *l)
+{
+ return l->name;
+}
+
static u32 link_own_addr(struct tipc_link *l)
{
return msg_prevnode(l->pmsg);
}
+void tipc_link_reinit(struct tipc_link *l, u32 addr)
+{
+ msg_set_prevnode(l->pmsg, addr);
+}
+
/**
* tipc_link_create - create a new link
* @n: pointer to associated node
@@ -694,7 +871,7 @@ void tipc_link_reset(struct tipc_link *l)
l->stats.recv_info = 0;
l->stale_count = 0;
l->bc_peer_is_up = false;
- link_reset_statistics(l);
+ tipc_link_reset_stats(l);
}
/**
@@ -1087,8 +1264,9 @@ drop:
/*
* Send protocol message to the other endpoint.
*/
-void tipc_link_proto_xmit(struct tipc_link *l, u32 msg_typ, int probe_msg,
- u32 gap, u32 tolerance, u32 priority)
+static void tipc_link_proto_xmit(struct tipc_link *l, u32 msg_typ,
+ int probe_msg, u32 gap, u32 tolerance,
+ u32 priority)
{
struct sk_buff *skb = NULL;
struct sk_buff_head xmitq;
@@ -1262,6 +1440,8 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
/* fall thru' */
case ACTIVATE_MSG:
+ skb_linearize(skb);
+ hdr = buf_msg(skb);
/* Complete own link name with peer's interface name */
if_name = strrchr(l->name, ':') + 1;
@@ -1527,53 +1707,17 @@ void tipc_link_set_queue_limits(struct tipc_link *l, u32 win)
l->backlog[TIPC_SYSTEM_IMPORTANCE].limit = max_bulk;
}
-/* tipc_link_find_owner - locate owner node of link by link's name
- * @net: the applicable net namespace
- * @name: pointer to link name string
- * @bearer_id: pointer to index in 'node->links' array where the link was found.
- *
- * Returns pointer to node owning the link, or 0 if no matching link is found.
- */
-static struct tipc_node *tipc_link_find_owner(struct net *net,
- const char *link_name,
- unsigned int *bearer_id)
-{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *l_ptr;
- struct tipc_node *n_ptr;
- struct tipc_node *found_node = NULL;
- int i;
-
- *bearer_id = 0;
- rcu_read_lock();
- list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
- tipc_node_lock(n_ptr);
- for (i = 0; i < MAX_BEARERS; i++) {
- l_ptr = n_ptr->links[i].link;
- if (l_ptr && !strcmp(l_ptr->name, link_name)) {
- *bearer_id = i;
- found_node = n_ptr;
- break;
- }
- }
- tipc_node_unlock(n_ptr);
- if (found_node)
- break;
- }
- rcu_read_unlock();
-
- return found_node;
-}
-
/**
- * link_reset_statistics - reset link statistics
- * @l_ptr: pointer to link
+ * link_reset_stats - reset link statistics
+ * @l: pointer to link
*/
-static void link_reset_statistics(struct tipc_link *l_ptr)
+void tipc_link_reset_stats(struct tipc_link *l)
{
- memset(&l_ptr->stats, 0, sizeof(l_ptr->stats));
- l_ptr->stats.sent_info = l_ptr->snd_nxt;
- l_ptr->stats.recv_info = l_ptr->rcv_nxt;
+ memset(&l->stats, 0, sizeof(l->stats));
+ if (!link_is_bc_sndlink(l)) {
+ l->stats.sent_info = l->snd_nxt;
+ l->stats.recv_info = l->rcv_nxt;
+ }
}
static void link_print(struct tipc_link *l, const char *str)
@@ -1626,84 +1770,6 @@ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])
return 0;
}
-int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)
-{
- int err;
- int res = 0;
- int bearer_id;
- char *name;
- struct tipc_link *link;
- struct tipc_node *node;
- struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
- struct net *net = sock_net(skb->sk);
-
- if (!info->attrs[TIPC_NLA_LINK])
- return -EINVAL;
-
- err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
- info->attrs[TIPC_NLA_LINK],
- tipc_nl_link_policy);
- if (err)
- return err;
-
- if (!attrs[TIPC_NLA_LINK_NAME])
- return -EINVAL;
-
- name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
-
- if (strcmp(name, tipc_bclink_name) == 0)
- return tipc_nl_bc_link_set(net, attrs);
-
- node = tipc_link_find_owner(net, name, &bearer_id);
- if (!node)
- return -EINVAL;
-
- tipc_node_lock(node);
-
- link = node->links[bearer_id].link;
- if (!link) {
- res = -EINVAL;
- goto out;
- }
-
- if (attrs[TIPC_NLA_LINK_PROP]) {
- struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
-
- err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP],
- props);
- if (err) {
- res = err;
- goto out;
- }
-
- if (props[TIPC_NLA_PROP_TOL]) {
- u32 tol;
-
- tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
- link->tolerance = tol;
- tipc_link_proto_xmit(link, STATE_MSG, 0, 0, tol, 0);
- }
- if (props[TIPC_NLA_PROP_PRIO]) {
- u32 prio;
-
- prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
- link->priority = prio;
- tipc_link_proto_xmit(link, STATE_MSG, 0, 0, 0, prio);
- }
- if (props[TIPC_NLA_PROP_WIN]) {
- u32 win;
-
- win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
- tipc_link_set_queue_limits(link, win);
- }
- }
-
-out:
- tipc_node_unlock(node);
-
- return res;
-}
-
static int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s)
{
int i;
@@ -1770,8 +1836,8 @@ msg_full:
}
/* Caller should hold appropriate locks to protect the link */
-static int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
- struct tipc_link *link, int nlflags)
+int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
+ struct tipc_link *link, int nlflags)
{
int err;
void *hdr;
@@ -1840,198 +1906,134 @@ msg_full:
return -EMSGSIZE;
}
-/* Caller should hold node lock */
-static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg,
- struct tipc_node *node, u32 *prev_link)
+static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
+ struct tipc_stats *stats)
{
- u32 i;
- int err;
-
- for (i = *prev_link; i < MAX_BEARERS; i++) {
- *prev_link = i;
-
- if (!node->links[i].link)
- continue;
+ int i;
+ struct nlattr *nest;
- err = __tipc_nl_add_link(net, msg,
- node->links[i].link, NLM_F_MULTI);
- if (err)
- return err;
- }
- *prev_link = 0;
+ struct nla_map {
+ __u32 key;
+ __u32 val;
+ };
- return 0;
-}
+ struct nla_map map[] = {
+ {TIPC_NLA_STATS_RX_INFO, stats->recv_info},
+ {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
+ {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
+ {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
+ {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
+ {TIPC_NLA_STATS_TX_INFO, stats->sent_info},
+ {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
+ {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
+ {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
+ {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
+ {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
+ {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
+ {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
+ {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
+ {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
+ {TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
+ {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
+ {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
+ {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
+ (stats->accu_queue_sz / stats->queue_sz_counts) : 0}
+ };
-int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
- struct net *net = sock_net(skb->sk);
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_node *node;
- struct tipc_nl_msg msg;
- u32 prev_node = cb->args[0];
- u32 prev_link = cb->args[1];
- int done = cb->args[2];
- int err;
+ nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS);
+ if (!nest)
+ return -EMSGSIZE;
- if (done)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(map); i++)
+ if (nla_put_u32(skb, map[i].key, map[i].val))
+ goto msg_full;
- msg.skb = skb;
- msg.portid = NETLINK_CB(cb->skb).portid;
- msg.seq = cb->nlh->nlmsg_seq;
-
- rcu_read_lock();
- if (prev_node) {
- node = tipc_node_find(net, prev_node);
- if (!node) {
- /* We never set seq or call nl_dump_check_consistent()
- * this means that setting prev_seq here will cause the
- * consistence check to fail in the netlink callback
- * handler. Resulting in the last NLMSG_DONE message
- * having the NLM_F_DUMP_INTR flag set.
- */
- cb->prev_seq = 1;
- goto out;
- }
- tipc_node_put(node);
-
- list_for_each_entry_continue_rcu(node, &tn->node_list,
- list) {
- tipc_node_lock(node);
- err = __tipc_nl_add_node_links(net, &msg, node,
- &prev_link);
- tipc_node_unlock(node);
- if (err)
- goto out;
-
- prev_node = node->addr;
- }
- } else {
- err = tipc_nl_add_bc_link(net, &msg);
- if (err)
- goto out;
-
- list_for_each_entry_rcu(node, &tn->node_list, list) {
- tipc_node_lock(node);
- err = __tipc_nl_add_node_links(net, &msg, node,
- &prev_link);
- tipc_node_unlock(node);
- if (err)
- goto out;
-
- prev_node = node->addr;
- }
- }
- done = 1;
-out:
- rcu_read_unlock();
+ nla_nest_end(skb, nest);
- cb->args[0] = prev_node;
- cb->args[1] = prev_link;
- cb->args[2] = done;
+ return 0;
+msg_full:
+ nla_nest_cancel(skb, nest);
- return skb->len;
+ return -EMSGSIZE;
}
-int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)
+int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)
{
- struct net *net = genl_info_net(info);
- struct tipc_nl_msg msg;
- char *name;
int err;
+ void *hdr;
+ struct nlattr *attrs;
+ struct nlattr *prop;
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct tipc_link *bcl = tn->bcl;
- msg.portid = info->snd_portid;
- msg.seq = info->snd_seq;
-
- if (!info->attrs[TIPC_NLA_LINK_NAME])
- return -EINVAL;
- name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]);
+ if (!bcl)
+ return 0;
- msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg.skb)
- return -ENOMEM;
+ tipc_bcast_lock(net);
- if (strcmp(name, tipc_bclink_name) == 0) {
- err = tipc_nl_add_bc_link(net, &msg);
- if (err) {
- nlmsg_free(msg.skb);
- return err;
- }
- } else {
- int bearer_id;
- struct tipc_node *node;
- struct tipc_link *link;
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
+ NLM_F_MULTI, TIPC_NL_LINK_GET);
+ if (!hdr)
+ return -EMSGSIZE;
- node = tipc_link_find_owner(net, name, &bearer_id);
- if (!node)
- return -EINVAL;
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK);
+ if (!attrs)
+ goto msg_full;
- tipc_node_lock(node);
- link = node->links[bearer_id].link;
- if (!link) {
- tipc_node_unlock(node);
- nlmsg_free(msg.skb);
- return -EINVAL;
- }
+ /* The broadcast link is always up */
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
+ goto attr_msg_full;
- err = __tipc_nl_add_link(net, &msg, link, 0);
- tipc_node_unlock(node);
- if (err) {
- nlmsg_free(msg.skb);
- return err;
- }
- }
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
+ goto attr_msg_full;
+ if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->rcv_nxt))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->snd_nxt))
+ goto attr_msg_full;
- return genlmsg_reply(msg.skb, info);
-}
+ prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP);
+ if (!prop)
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->window))
+ goto prop_msg_full;
+ nla_nest_end(msg->skb, prop);
-int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)
-{
- int err;
- char *link_name;
- unsigned int bearer_id;
- struct tipc_link *link;
- struct tipc_node *node;
- struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
- struct net *net = sock_net(skb->sk);
-
- if (!info->attrs[TIPC_NLA_LINK])
- return -EINVAL;
-
- err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
- info->attrs[TIPC_NLA_LINK],
- tipc_nl_link_policy);
+ err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
if (err)
- return err;
-
- if (!attrs[TIPC_NLA_LINK_NAME])
- return -EINVAL;
-
- link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+ goto attr_msg_full;
- if (strcmp(link_name, tipc_bclink_name) == 0) {
- err = tipc_bclink_reset_stats(net);
- if (err)
- return err;
- return 0;
- }
+ tipc_bcast_unlock(net);
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
- node = tipc_link_find_owner(net, link_name, &bearer_id);
- if (!node)
- return -EINVAL;
+ return 0;
- tipc_node_lock(node);
+prop_msg_full:
+ nla_nest_cancel(msg->skb, prop);
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ tipc_bcast_unlock(net);
+ genlmsg_cancel(msg->skb, hdr);
- link = node->links[bearer_id].link;
- if (!link) {
- tipc_node_unlock(node);
- return -EINVAL;
- }
+ return -EMSGSIZE;
+}
- link_reset_statistics(link);
+void tipc_link_set_tolerance(struct tipc_link *l, u32 tol)
+{
+ l->tolerance = tol;
+ tipc_link_proto_xmit(l, STATE_MSG, 0, 0, tol, 0);
+}
- tipc_node_unlock(node);
+void tipc_link_set_prio(struct tipc_link *l, u32 prio)
+{
+ l->priority = prio;
+ tipc_link_proto_xmit(l, STATE_MSG, 0, 0, 0, prio);
+}
- return 0;
+void tipc_link_set_abort_limit(struct tipc_link *l, u32 limit)
+{
+ l->abort_limit = limit;
}
diff --git a/net/tipc/link.h b/net/tipc/link.h
index 66d859b66c84..b2ae0f4276af 100644
--- a/net/tipc/link.h
+++ b/net/tipc/link.h
@@ -45,10 +45,6 @@
*/
#define ELINKCONG EAGAIN /* link congestion <=> resource unavailable */
-/* Out-of-range value for link sequence numbers
- */
-#define INVALID_LINK_SEQ 0x10000
-
/* Link FSM events:
*/
enum {
@@ -75,151 +71,6 @@ enum {
*/
#define MAX_PKT_DEFAULT 1500
-struct tipc_stats {
- u32 sent_info; /* used in counting # sent packets */
- u32 recv_info; /* used in counting # recv'd packets */
- u32 sent_states;
- u32 recv_states;
- u32 sent_probes;
- u32 recv_probes;
- u32 sent_nacks;
- u32 recv_nacks;
- u32 sent_acks;
- u32 sent_bundled;
- u32 sent_bundles;
- u32 recv_bundled;
- u32 recv_bundles;
- u32 retransmitted;
- u32 sent_fragmented;
- u32 sent_fragments;
- u32 recv_fragmented;
- u32 recv_fragments;
- u32 link_congs; /* # port sends blocked by congestion */
- u32 deferred_recv;
- u32 duplicates;
- u32 max_queue_sz; /* send queue size high water mark */
- u32 accu_queue_sz; /* used for send queue size profiling */
- u32 queue_sz_counts; /* used for send queue size profiling */
- u32 msg_length_counts; /* used for message length profiling */
- u32 msg_lengths_total; /* used for message length profiling */
- u32 msg_length_profile[7]; /* used for msg. length profiling */
-};
-
-/**
- * struct tipc_link - TIPC link data structure
- * @addr: network address of link's peer node
- * @name: link name character string
- * @media_addr: media address to use when sending messages over link
- * @timer: link timer
- * @net: pointer to namespace struct
- * @refcnt: reference counter for permanent references (owner node & timer)
- * @peer_session: link session # being used by peer end of link
- * @peer_bearer_id: bearer id used by link's peer endpoint
- * @bearer_id: local bearer id used by link
- * @tolerance: minimum link continuity loss needed to reset link [in ms]
- * @keepalive_intv: link keepalive timer interval
- * @abort_limit: # of unacknowledged continuity probes needed to reset link
- * @state: current state of link FSM
- * @peer_caps: bitmap describing capabilities of peer node
- * @silent_intv_cnt: # of timer intervals without any reception from peer
- * @proto_msg: template for control messages generated by link
- * @pmsg: convenience pointer to "proto_msg" field
- * @priority: current link priority
- * @net_plane: current link network plane ('A' through 'H')
- * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
- * @exp_msg_count: # of tunnelled messages expected during link changeover
- * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
- * @mtu: current maximum packet size for this link
- * @advertised_mtu: advertised own mtu when link is being established
- * @transmitq: queue for sent, non-acked messages
- * @backlogq: queue for messages waiting to be sent
- * @snt_nxt: next sequence number to use for outbound messages
- * @last_retransmitted: sequence number of most recently retransmitted message
- * @stale_count: # of identical retransmit requests made by peer
- * @ackers: # of peers that needs to ack each packet before it can be released
- * @acked: # last packet acked by a certain peer. Used for broadcast.
- * @rcv_nxt: next sequence number to expect for inbound messages
- * @deferred_queue: deferred queue saved OOS b'cast message received from node
- * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
- * @inputq: buffer queue for messages to be delivered upwards
- * @namedq: buffer queue for name table messages to be delivered upwards
- * @next_out: ptr to first unsent outbound message in queue
- * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
- * @long_msg_seq_no: next identifier to use for outbound fragmented messages
- * @reasm_buf: head of partially reassembled inbound message fragments
- * @bc_rcvr: marks that this is a broadcast receiver link
- * @stats: collects statistics regarding link activity
- */
-struct tipc_link {
- u32 addr;
- char name[TIPC_MAX_LINK_NAME];
- struct tipc_media_addr *media_addr;
- struct net *net;
-
- /* Management and link supervision data */
- u32 peer_session;
- u32 peer_bearer_id;
- u32 bearer_id;
- u32 tolerance;
- unsigned long keepalive_intv;
- u32 abort_limit;
- u32 state;
- u16 peer_caps;
- bool active;
- u32 silent_intv_cnt;
- struct {
- unchar hdr[INT_H_SIZE];
- unchar body[TIPC_MAX_IF_NAME];
- } proto_msg;
- struct tipc_msg *pmsg;
- u32 priority;
- char net_plane;
-
- /* Failover/synch */
- u16 drop_point;
- struct sk_buff *failover_reasm_skb;
-
- /* Max packet negotiation */
- u16 mtu;
- u16 advertised_mtu;
-
- /* Sending */
- struct sk_buff_head transmq;
- struct sk_buff_head backlogq;
- struct {
- u16 len;
- u16 limit;
- } backlog[5];
- u16 snd_nxt;
- u16 last_retransm;
- u16 window;
- u32 stale_count;
-
- /* Reception */
- u16 rcv_nxt;
- u32 rcv_unacked;
- struct sk_buff_head deferdq;
- struct sk_buff_head *inputq;
- struct sk_buff_head *namedq;
-
- /* Congestion handling */
- struct sk_buff_head wakeupq;
-
- /* Fragmentation/reassembly */
- struct sk_buff *reasm_buf;
-
- /* Broadcast */
- u16 ackers;
- u16 acked;
- struct tipc_link *bc_rcvlink;
- struct tipc_link *bc_sndlink;
- int nack_state;
- bool bc_peer_is_up;
-
- /* Statistics */
- struct tipc_stats stats;
-};
-
bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
int tolerance, char net_plane, u32 mtu, int priority,
int window, u32 session, u32 ownnode, u32 peer,
@@ -235,11 +86,11 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer,
struct sk_buff_head *namedq,
struct tipc_link *bc_sndlink,
struct tipc_link **link);
+void tipc_link_reinit(struct tipc_link *l, u32 addr);
void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
int mtyp, struct sk_buff_head *xmitq);
void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq);
int tipc_link_fsm_evt(struct tipc_link *l, int evt);
-void tipc_link_reset_fragments(struct tipc_link *l_ptr);
bool tipc_link_is_up(struct tipc_link *l);
bool tipc_link_peer_is_down(struct tipc_link *l);
bool tipc_link_is_reset(struct tipc_link *l);
@@ -248,15 +99,25 @@ bool tipc_link_is_synching(struct tipc_link *l);
bool tipc_link_is_failingover(struct tipc_link *l);
bool tipc_link_is_blocked(struct tipc_link *l);
void tipc_link_set_active(struct tipc_link *l, bool active);
-void tipc_link_reset(struct tipc_link *l_ptr);
-int tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list,
+void tipc_link_reset(struct tipc_link *l);
+void tipc_link_reset_stats(struct tipc_link *l);
+int tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list,
struct sk_buff_head *xmitq);
+struct sk_buff_head *tipc_link_inputq(struct tipc_link *l);
+u16 tipc_link_rcv_nxt(struct tipc_link *l);
+u16 tipc_link_acked(struct tipc_link *l);
+u32 tipc_link_id(struct tipc_link *l);
+char *tipc_link_name(struct tipc_link *l);
+char tipc_link_plane(struct tipc_link *l);
+int tipc_link_prio(struct tipc_link *l);
+int tipc_link_window(struct tipc_link *l);
+unsigned long tipc_link_tolerance(struct tipc_link *l);
+void tipc_link_set_tolerance(struct tipc_link *l, u32 tol);
+void tipc_link_set_prio(struct tipc_link *l, u32 prio);
+void tipc_link_set_abort_limit(struct tipc_link *l, u32 limit);
void tipc_link_set_queue_limits(struct tipc_link *l, u32 window);
-
-int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb);
-int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info);
-int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info);
-int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info);
+int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
+ struct tipc_link *link, int nlflags);
int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]);
int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq);
int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index c07612bab95c..ebe9d0ff6e9e 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -84,31 +84,6 @@ static struct sk_buff *named_prepare_buf(struct net *net, u32 type, u32 size,
return buf;
}
-void named_cluster_distribute(struct net *net, struct sk_buff *skb)
-{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct sk_buff *oskb;
- struct tipc_node *node;
- u32 dnode;
-
- rcu_read_lock();
- list_for_each_entry_rcu(node, &tn->node_list, list) {
- dnode = node->addr;
- if (in_own_node(net, dnode))
- continue;
- if (!tipc_node_is_up(node))
- continue;
- oskb = pskb_copy(skb, GFP_ATOMIC);
- if (!oskb)
- break;
- msg_set_destnode(buf_msg(oskb), dnode);
- tipc_node_xmit_skb(net, oskb, dnode, 0);
- }
- rcu_read_unlock();
-
- kfree_skb(skb);
-}
-
/**
* tipc_named_publish - tell other nodes about a new publication by this node
*/
@@ -226,42 +201,6 @@ void tipc_named_node_up(struct net *net, u32 dnode)
tipc_node_xmit(net, &head, dnode, 0);
}
-static void tipc_publ_subscribe(struct net *net, struct publication *publ,
- u32 addr)
-{
- struct tipc_node *node;
-
- if (in_own_node(net, addr))
- return;
-
- node = tipc_node_find(net, addr);
- if (!node) {
- pr_warn("Node subscription rejected, unknown node 0x%x\n",
- addr);
- return;
- }
-
- tipc_node_lock(node);
- list_add_tail(&publ->nodesub_list, &node->publ_list);
- tipc_node_unlock(node);
- tipc_node_put(node);
-}
-
-static void tipc_publ_unsubscribe(struct net *net, struct publication *publ,
- u32 addr)
-{
- struct tipc_node *node;
-
- node = tipc_node_find(net, addr);
- if (!node)
- return;
-
- tipc_node_lock(node);
- list_del_init(&publ->nodesub_list);
- tipc_node_unlock(node);
- tipc_node_put(node);
-}
-
/**
* tipc_publ_purge - remove publication associated with a failed node
*
@@ -277,7 +216,7 @@ static void tipc_publ_purge(struct net *net, struct publication *publ, u32 addr)
p = tipc_nametbl_remove_publ(net, publ->type, publ->lower,
publ->node, publ->ref, publ->key);
if (p)
- tipc_publ_unsubscribe(net, p, addr);
+ tipc_node_unsubscribe(net, &p->nodesub_list, addr);
spin_unlock_bh(&tn->nametbl_lock);
if (p != publ) {
@@ -317,7 +256,7 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i,
TIPC_CLUSTER_SCOPE, node,
ntohl(i->ref), ntohl(i->key));
if (publ) {
- tipc_publ_subscribe(net, publ, node);
+ tipc_node_subscribe(net, &publ->nodesub_list, node);
return true;
}
} else if (dtype == WITHDRAWAL) {
@@ -326,7 +265,7 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i,
node, ntohl(i->ref),
ntohl(i->key));
if (publ) {
- tipc_publ_unsubscribe(net, publ, node);
+ tipc_node_unsubscribe(net, &publ->nodesub_list, node);
kfree_rcu(publ, rcu);
return true;
}
@@ -397,6 +336,7 @@ void tipc_named_rcv(struct net *net, struct sk_buff_head *inputq)
spin_lock_bh(&tn->nametbl_lock);
for (skb = skb_dequeue(inputq); skb; skb = skb_dequeue(inputq)) {
+ skb_linearize(skb);
msg = buf_msg(skb);
mtype = msg_type(msg);
item = (struct distr_item *)msg_data(msg);
diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h
index dd2d9fd80da2..1264ba0af937 100644
--- a/net/tipc/name_distr.h
+++ b/net/tipc/name_distr.h
@@ -69,7 +69,6 @@ struct distr_item {
struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ);
struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ);
-void named_cluster_distribute(struct net *net, struct sk_buff *buf);
void tipc_named_node_up(struct net *net, u32 dnode);
void tipc_named_rcv(struct net *net, struct sk_buff_head *msg_queue);
void tipc_named_reinit(struct net *net);
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index 0f47f08bf38f..91fce70291a8 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -42,6 +42,7 @@
#include "subscr.h"
#include "bcast.h"
#include "addr.h"
+#include "node.h"
#include <net/genetlink.h>
#define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */
@@ -677,7 +678,7 @@ struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower,
spin_unlock_bh(&tn->nametbl_lock);
if (buf)
- named_cluster_distribute(net, buf);
+ tipc_node_broadcast(net, buf);
return publ;
}
@@ -709,7 +710,7 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref,
spin_unlock_bh(&tn->nametbl_lock);
if (skb) {
- named_cluster_distribute(net, skb);
+ tipc_node_broadcast(net, skb);
return 1;
}
return 0;
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 7f6475efc984..8975b0135b76 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -101,18 +101,18 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
},
{
.cmd = TIPC_NL_LINK_GET,
- .doit = tipc_nl_link_get,
- .dumpit = tipc_nl_link_dump,
+ .doit = tipc_nl_node_get_link,
+ .dumpit = tipc_nl_node_dump_link,
.policy = tipc_nl_policy,
},
{
.cmd = TIPC_NL_LINK_SET,
- .doit = tipc_nl_link_set,
+ .doit = tipc_nl_node_set_link,
.policy = tipc_nl_policy,
},
{
.cmd = TIPC_NL_LINK_RESET_STATS,
- .doit = tipc_nl_link_reset_stats,
+ .doit = tipc_nl_node_reset_link_stats,
.policy = tipc_nl_policy,
},
{
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index 1eadc95e1132..2c016fdefe97 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -1023,25 +1023,25 @@ static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
msg->req_type = TIPC_TLV_LINK_NAME;
msg->rep_size = ULTRA_STRING_MAX_LEN;
msg->rep_type = TIPC_TLV_ULTRA_STRING;
- dump.dumpit = tipc_nl_link_dump;
+ dump.dumpit = tipc_nl_node_dump_link;
dump.format = tipc_nl_compat_link_stat_dump;
return tipc_nl_compat_dumpit(&dump, msg);
case TIPC_CMD_GET_LINKS:
msg->req_type = TIPC_TLV_NET_ADDR;
msg->rep_size = ULTRA_STRING_MAX_LEN;
- dump.dumpit = tipc_nl_link_dump;
+ dump.dumpit = tipc_nl_node_dump_link;
dump.format = tipc_nl_compat_link_dump;
return tipc_nl_compat_dumpit(&dump, msg);
case TIPC_CMD_SET_LINK_TOL:
case TIPC_CMD_SET_LINK_PRI:
case TIPC_CMD_SET_LINK_WINDOW:
msg->req_type = TIPC_TLV_LINK_CONFIG;
- doit.doit = tipc_nl_link_set;
+ doit.doit = tipc_nl_node_set_link;
doit.transcode = tipc_nl_compat_link_set;
return tipc_nl_compat_doit(&doit, msg);
case TIPC_CMD_RESET_LINK_STATS:
msg->req_type = TIPC_TLV_LINK_NAME;
- doit.doit = tipc_nl_link_reset_stats;
+ doit.doit = tipc_nl_node_reset_link_stats;
doit.transcode = tipc_nl_compat_link_reset_stats;
return tipc_nl_compat_doit(&doit, msg);
case TIPC_CMD_SHOW_NAME_TABLE:
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 20cddec0a43c..fa97d9649a28 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -42,6 +42,84 @@
#include "bcast.h"
#include "discover.h"
+#define INVALID_NODE_SIG 0x10000
+
+/* Flags used to take different actions according to flag type
+ * TIPC_NOTIFY_NODE_DOWN: notify node is down
+ * TIPC_NOTIFY_NODE_UP: notify node is up
+ * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type
+ */
+enum {
+ TIPC_NOTIFY_NODE_DOWN = (1 << 3),
+ TIPC_NOTIFY_NODE_UP = (1 << 4),
+ TIPC_NOTIFY_LINK_UP = (1 << 6),
+ TIPC_NOTIFY_LINK_DOWN = (1 << 7)
+};
+
+struct tipc_link_entry {
+ struct tipc_link *link;
+ spinlock_t lock; /* per link */
+ u32 mtu;
+ struct sk_buff_head inputq;
+ struct tipc_media_addr maddr;
+};
+
+struct tipc_bclink_entry {
+ struct tipc_link *link;
+ struct sk_buff_head inputq1;
+ struct sk_buff_head arrvq;
+ struct sk_buff_head inputq2;
+ struct sk_buff_head namedq;
+};
+
+/**
+ * struct tipc_node - TIPC node structure
+ * @addr: network address of node
+ * @ref: reference counter to node object
+ * @lock: rwlock governing access to structure
+ * @net: the applicable net namespace
+ * @hash: links to adjacent nodes in unsorted hash chain
+ * @inputq: pointer to input queue containing messages for msg event
+ * @namedq: pointer to name table input queue with name table messages
+ * @active_links: bearer ids of active links, used as index into links[] array
+ * @links: array containing references to all links to node
+ * @action_flags: bit mask of different types of node actions
+ * @state: connectivity state vs peer node
+ * @sync_point: sequence number where synch/failover is finished
+ * @list: links to adjacent nodes in sorted list of cluster's nodes
+ * @working_links: number of working links to node (both active and standby)
+ * @link_cnt: number of links to node
+ * @capabilities: bitmap, indicating peer node's functional capabilities
+ * @signature: node instance identifier
+ * @link_id: local and remote bearer ids of changing link, if any
+ * @publ_list: list of publications
+ * @rcu: rcu struct for tipc_node
+ */
+struct tipc_node {
+ u32 addr;
+ struct kref kref;
+ rwlock_t lock;
+ struct net *net;
+ struct hlist_node hash;
+ int active_links[2];
+ struct tipc_link_entry links[MAX_BEARERS];
+ struct tipc_bclink_entry bc_entry;
+ int action_flags;
+ struct list_head list;
+ int state;
+ u16 sync_point;
+ int link_cnt;
+ u16 working_links;
+ u16 capabilities;
+ u32 signature;
+ u32 link_id;
+ struct list_head publ_list;
+ struct list_head conn_sks;
+ unsigned long keepalive_intv;
+ struct timer_list timer;
+ struct rcu_head rcu;
+};
+
/* Node FSM states and events:
*/
enum {
@@ -75,6 +153,9 @@ static void node_lost_contact(struct tipc_node *n, struct sk_buff_head *inputq);
static void tipc_node_delete(struct tipc_node *node);
static void tipc_node_timeout(unsigned long data);
static void tipc_node_fsm_evt(struct tipc_node *n, int evt);
+static struct tipc_node *tipc_node_find(struct net *net, u32 addr);
+static void tipc_node_put(struct tipc_node *node);
+static bool tipc_node_is_up(struct tipc_node *n);
struct tipc_sock_conn {
u32 port;
@@ -83,12 +164,54 @@ struct tipc_sock_conn {
struct list_head list;
};
+static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
+ [TIPC_NLA_LINK_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_LINK_NAME] = {
+ .type = NLA_STRING,
+ .len = TIPC_MAX_LINK_NAME
+ },
+ [TIPC_NLA_LINK_MTU] = { .type = NLA_U32 },
+ [TIPC_NLA_LINK_BROADCAST] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_UP] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_ACTIVE] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_PROP] = { .type = NLA_NESTED },
+ [TIPC_NLA_LINK_STATS] = { .type = NLA_NESTED },
+ [TIPC_NLA_LINK_RX] = { .type = NLA_U32 },
+ [TIPC_NLA_LINK_TX] = { .type = NLA_U32 }
+};
+
static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
[TIPC_NLA_NODE_UNSPEC] = { .type = NLA_UNSPEC },
[TIPC_NLA_NODE_ADDR] = { .type = NLA_U32 },
[TIPC_NLA_NODE_UP] = { .type = NLA_FLAG }
};
+static struct tipc_link *node_active_link(struct tipc_node *n, int sel)
+{
+ int bearer_id = n->active_links[sel & 1];
+
+ if (unlikely(bearer_id == INVALID_BEARER_ID))
+ return NULL;
+
+ return n->links[bearer_id].link;
+}
+
+int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel)
+{
+ struct tipc_node *n;
+ int bearer_id;
+ unsigned int mtu = MAX_MSG_SIZE;
+
+ n = tipc_node_find(net, addr);
+ if (unlikely(!n))
+ return mtu;
+
+ bearer_id = n->active_links[sel & 1];
+ if (likely(bearer_id != INVALID_BEARER_ID))
+ mtu = n->links[bearer_id].mtu;
+ tipc_node_put(n);
+ return mtu;
+}
/*
* A trivial power-of-two bitmask technique is used for speed, since this
* operation is done for every incoming TIPC packet. The number of hash table
@@ -107,7 +230,7 @@ static void tipc_node_kref_release(struct kref *kref)
tipc_node_delete(node);
}
-void tipc_node_put(struct tipc_node *node)
+static void tipc_node_put(struct tipc_node *node)
{
kref_put(&node->kref, tipc_node_kref_release);
}
@@ -120,7 +243,7 @@ static void tipc_node_get(struct tipc_node *node)
/*
* tipc_node_find - locate specified node object, if it exists
*/
-struct tipc_node *tipc_node_find(struct net *net, u32 addr)
+static struct tipc_node *tipc_node_find(struct net *net, u32 addr)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
struct tipc_node *node;
@@ -141,66 +264,122 @@ struct tipc_node *tipc_node_find(struct net *net, u32 addr)
return NULL;
}
+static void tipc_node_read_lock(struct tipc_node *n)
+{
+ read_lock_bh(&n->lock);
+}
+
+static void tipc_node_read_unlock(struct tipc_node *n)
+{
+ read_unlock_bh(&n->lock);
+}
+
+static void tipc_node_write_lock(struct tipc_node *n)
+{
+ write_lock_bh(&n->lock);
+}
+
+static void tipc_node_write_unlock(struct tipc_node *n)
+{
+ struct net *net = n->net;
+ u32 addr = 0;
+ u32 flags = n->action_flags;
+ u32 link_id = 0;
+ struct list_head *publ_list;
+
+ if (likely(!flags)) {
+ write_unlock_bh(&n->lock);
+ return;
+ }
+
+ addr = n->addr;
+ link_id = n->link_id;
+ publ_list = &n->publ_list;
+
+ n->action_flags &= ~(TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP |
+ TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP);
+
+ write_unlock_bh(&n->lock);
+
+ if (flags & TIPC_NOTIFY_NODE_DOWN)
+ tipc_publ_notify(net, publ_list, addr);
+
+ if (flags & TIPC_NOTIFY_NODE_UP)
+ tipc_named_node_up(net, addr);
+
+ if (flags & TIPC_NOTIFY_LINK_UP)
+ tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,
+ TIPC_NODE_SCOPE, link_id, addr);
+
+ if (flags & TIPC_NOTIFY_LINK_DOWN)
+ tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
+ link_id, addr);
+}
+
struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_node *n_ptr, *temp_node;
+ struct tipc_node *n, *temp_node;
+ int i;
spin_lock_bh(&tn->node_list_lock);
- n_ptr = tipc_node_find(net, addr);
- if (n_ptr)
+ n = tipc_node_find(net, addr);
+ if (n)
goto exit;
- n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC);
- if (!n_ptr) {
+ n = kzalloc(sizeof(*n), GFP_ATOMIC);
+ if (!n) {
pr_warn("Node creation failed, no memory\n");
goto exit;
}
- n_ptr->addr = addr;
- n_ptr->net = net;
- n_ptr->capabilities = capabilities;
- kref_init(&n_ptr->kref);
- spin_lock_init(&n_ptr->lock);
- INIT_HLIST_NODE(&n_ptr->hash);
- INIT_LIST_HEAD(&n_ptr->list);
- INIT_LIST_HEAD(&n_ptr->publ_list);
- INIT_LIST_HEAD(&n_ptr->conn_sks);
- skb_queue_head_init(&n_ptr->bc_entry.namedq);
- skb_queue_head_init(&n_ptr->bc_entry.inputq1);
- __skb_queue_head_init(&n_ptr->bc_entry.arrvq);
- skb_queue_head_init(&n_ptr->bc_entry.inputq2);
- hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]);
+ n->addr = addr;
+ n->net = net;
+ n->capabilities = capabilities;
+ kref_init(&n->kref);
+ rwlock_init(&n->lock);
+ INIT_HLIST_NODE(&n->hash);
+ INIT_LIST_HEAD(&n->list);
+ INIT_LIST_HEAD(&n->publ_list);
+ INIT_LIST_HEAD(&n->conn_sks);
+ skb_queue_head_init(&n->bc_entry.namedq);
+ skb_queue_head_init(&n->bc_entry.inputq1);
+ __skb_queue_head_init(&n->bc_entry.arrvq);
+ skb_queue_head_init(&n->bc_entry.inputq2);
+ for (i = 0; i < MAX_BEARERS; i++)
+ spin_lock_init(&n->links[i].lock);
+ hlist_add_head_rcu(&n->hash, &tn->node_htable[tipc_hashfn(addr)]);
list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
- if (n_ptr->addr < temp_node->addr)
+ if (n->addr < temp_node->addr)
break;
}
- list_add_tail_rcu(&n_ptr->list, &temp_node->list);
- n_ptr->state = SELF_DOWN_PEER_LEAVING;
- n_ptr->signature = INVALID_NODE_SIG;
- n_ptr->active_links[0] = INVALID_BEARER_ID;
- n_ptr->active_links[1] = INVALID_BEARER_ID;
- if (!tipc_link_bc_create(net, tipc_own_addr(net), n_ptr->addr,
- U16_MAX, tipc_bc_sndlink(net)->window,
- n_ptr->capabilities,
- &n_ptr->bc_entry.inputq1,
- &n_ptr->bc_entry.namedq,
+ list_add_tail_rcu(&n->list, &temp_node->list);
+ n->state = SELF_DOWN_PEER_LEAVING;
+ n->signature = INVALID_NODE_SIG;
+ n->active_links[0] = INVALID_BEARER_ID;
+ n->active_links[1] = INVALID_BEARER_ID;
+ if (!tipc_link_bc_create(net, tipc_own_addr(net), n->addr,
+ U16_MAX,
+ tipc_link_window(tipc_bc_sndlink(net)),
+ n->capabilities,
+ &n->bc_entry.inputq1,
+ &n->bc_entry.namedq,
tipc_bc_sndlink(net),
- &n_ptr->bc_entry.link)) {
+ &n->bc_entry.link)) {
pr_warn("Broadcast rcv link creation failed, no memory\n");
- kfree(n_ptr);
- n_ptr = NULL;
+ kfree(n);
+ n = NULL;
goto exit;
}
- tipc_node_get(n_ptr);
- setup_timer(&n_ptr->timer, tipc_node_timeout, (unsigned long)n_ptr);
- n_ptr->keepalive_intv = U32_MAX;
+ tipc_node_get(n);
+ setup_timer(&n->timer, tipc_node_timeout, (unsigned long)n);
+ n->keepalive_intv = U32_MAX;
exit:
spin_unlock_bh(&tn->node_list_lock);
- return n_ptr;
+ return n;
}
static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l)
{
- unsigned long tol = l->tolerance;
+ unsigned long tol = tipc_link_tolerance(l);
unsigned long intv = ((tol / 4) > 500) ? 500 : tol / 4;
unsigned long keepalive_intv = msecs_to_jiffies(intv);
@@ -209,7 +388,7 @@ static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l)
n->keepalive_intv = keepalive_intv;
/* Ensure link's abort limit corresponds to current interval */
- l->abort_limit = l->tolerance / jiffies_to_msecs(n->keepalive_intv);
+ tipc_link_set_abort_limit(l, tol / jiffies_to_msecs(n->keepalive_intv));
}
static void tipc_node_delete(struct tipc_node *node)
@@ -234,6 +413,42 @@ void tipc_node_stop(struct net *net)
spin_unlock_bh(&tn->node_list_lock);
}
+void tipc_node_subscribe(struct net *net, struct list_head *subscr, u32 addr)
+{
+ struct tipc_node *n;
+
+ if (in_own_node(net, addr))
+ return;
+
+ n = tipc_node_find(net, addr);
+ if (!n) {
+ pr_warn("Node subscribe rejected, unknown node 0x%x\n", addr);
+ return;
+ }
+ tipc_node_write_lock(n);
+ list_add_tail(subscr, &n->publ_list);
+ tipc_node_write_unlock(n);
+ tipc_node_put(n);
+}
+
+void tipc_node_unsubscribe(struct net *net, struct list_head *subscr, u32 addr)
+{
+ struct tipc_node *n;
+
+ if (in_own_node(net, addr))
+ return;
+
+ n = tipc_node_find(net, addr);
+ if (!n) {
+ pr_warn("Node unsubscribe rejected, unknown node 0x%x\n", addr);
+ return;
+ }
+ tipc_node_write_lock(n);
+ list_del_init(subscr);
+ tipc_node_write_unlock(n);
+ tipc_node_put(n);
+}
+
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
{
struct tipc_node *node;
@@ -257,9 +472,9 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
conn->port = port;
conn->peer_port = peer_port;
- tipc_node_lock(node);
+ tipc_node_write_lock(node);
list_add_tail(&conn->list, &node->conn_sks);
- tipc_node_unlock(node);
+ tipc_node_write_unlock(node);
exit:
tipc_node_put(node);
return err;
@@ -277,14 +492,14 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
if (!node)
return;
- tipc_node_lock(node);
+ tipc_node_write_lock(node);
list_for_each_entry_safe(conn, safe, &node->conn_sks, list) {
if (port != conn->port)
continue;
list_del(&conn->list);
kfree(conn);
}
- tipc_node_unlock(node);
+ tipc_node_write_unlock(node);
tipc_node_put(node);
}
@@ -301,14 +516,16 @@ static void tipc_node_timeout(unsigned long data)
__skb_queue_head_init(&xmitq);
for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {
- tipc_node_lock(n);
+ tipc_node_read_lock(n);
le = &n->links[bearer_id];
+ spin_lock_bh(&le->lock);
if (le->link) {
/* Link tolerance may change asynchronously: */
tipc_node_calculate_timer(n, le->link);
rc = tipc_link_timeout(le->link, &xmitq);
}
- tipc_node_unlock(n);
+ spin_unlock_bh(&le->lock);
+ tipc_node_read_unlock(n);
tipc_bearer_xmit(n->net, bearer_id, &xmitq, &le->maddr);
if (rc & TIPC_LINK_DOWN_EVT)
tipc_node_link_down(n, bearer_id, false);
@@ -340,16 +557,16 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
n->working_links++;
n->action_flags |= TIPC_NOTIFY_LINK_UP;
- n->link_id = nl->peer_bearer_id << 16 | bearer_id;
+ n->link_id = tipc_link_id(nl);
/* Leave room for tunnel header when returning 'mtu' to users: */
- n->links[bearer_id].mtu = nl->mtu - INT_H_SIZE;
+ n->links[bearer_id].mtu = tipc_link_mtu(nl) - INT_H_SIZE;
tipc_bearer_add_dest(n->net, bearer_id, n->addr);
tipc_bcast_inc_bearer_dst_cnt(n->net, bearer_id);
pr_debug("Established link <%s> on network plane %c\n",
- nl->name, nl->net_plane);
+ tipc_link_name(nl), tipc_link_plane(nl));
/* First link? => give it both slots */
if (!ol) {
@@ -362,17 +579,17 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
}
/* Second link => redistribute slots */
- if (nl->priority > ol->priority) {
- pr_debug("Old link <%s> becomes standby\n", ol->name);
+ if (tipc_link_prio(nl) > tipc_link_prio(ol)) {
+ pr_debug("Old link <%s> becomes standby\n", tipc_link_name(ol));
*slot0 = bearer_id;
*slot1 = bearer_id;
tipc_link_set_active(nl, true);
tipc_link_set_active(ol, false);
- } else if (nl->priority == ol->priority) {
+ } else if (tipc_link_prio(nl) == tipc_link_prio(ol)) {
tipc_link_set_active(nl, true);
*slot1 = bearer_id;
} else {
- pr_debug("New link <%s> is standby\n", nl->name);
+ pr_debug("New link <%s> is standby\n", tipc_link_name(nl));
}
/* Prepare synchronization with first link */
@@ -387,9 +604,9 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
static void tipc_node_link_up(struct tipc_node *n, int bearer_id,
struct sk_buff_head *xmitq)
{
- tipc_node_lock(n);
+ tipc_node_write_lock(n);
__tipc_node_link_up(n, bearer_id, xmitq);
- tipc_node_unlock(n);
+ tipc_node_write_unlock(n);
}
/**
@@ -402,7 +619,7 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
struct tipc_link_entry *le = &n->links[*bearer_id];
int *slot0 = &n->active_links[0];
int *slot1 = &n->active_links[1];
- int i, highest = 0;
+ int i, highest = 0, prio;
struct tipc_link *l, *_l, *tnl;
l = n->links[*bearer_id].link;
@@ -411,12 +628,12 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
n->working_links--;
n->action_flags |= TIPC_NOTIFY_LINK_DOWN;
- n->link_id = l->peer_bearer_id << 16 | *bearer_id;
+ n->link_id = tipc_link_id(l);
tipc_bearer_remove_dest(n->net, *bearer_id, n->addr);
pr_debug("Lost link <%s> on network plane %c\n",
- l->name, l->net_plane);
+ tipc_link_name(l), tipc_link_plane(l));
/* Select new active link if any available */
*slot0 = INVALID_BEARER_ID;
@@ -427,10 +644,11 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
continue;
if (_l == l)
continue;
- if (_l->priority < highest)
+ prio = tipc_link_prio(_l);
+ if (prio < highest)
continue;
- if (_l->priority > highest) {
- highest = _l->priority;
+ if (prio > highest) {
+ highest = prio;
*slot0 = i;
*slot1 = i;
continue;
@@ -453,17 +671,17 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
tipc_bcast_dec_bearer_dst_cnt(n->net, *bearer_id);
/* There is still a working link => initiate failover */
- tnl = node_active_link(n, 0);
+ *bearer_id = n->active_links[0];
+ tnl = n->links[*bearer_id].link;
tipc_link_fsm_evt(tnl, LINK_SYNCH_END_EVT);
tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);
- n->sync_point = tnl->rcv_nxt + (U16_MAX / 2 - 1);
+ n->sync_point = tipc_link_rcv_nxt(tnl) + (U16_MAX / 2 - 1);
tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, xmitq);
tipc_link_reset(l);
tipc_link_fsm_evt(l, LINK_RESET_EVT);
tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);
tipc_node_fsm_evt(n, NODE_FAILOVER_BEGIN_EVT);
- *maddr = &n->links[tnl->bearer_id].maddr;
- *bearer_id = tnl->bearer_id;
+ *maddr = &n->links[*bearer_id].maddr;
}
static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
@@ -478,7 +696,7 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
__skb_queue_head_init(&xmitq);
- tipc_node_lock(n);
+ tipc_node_write_lock(n);
if (!tipc_link_is_establishing(l)) {
__tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
if (delete) {
@@ -490,12 +708,12 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
/* Defuse pending tipc_node_link_up() */
tipc_link_fsm_evt(l, LINK_RESET_EVT);
}
- tipc_node_unlock(n);
+ tipc_node_write_unlock(n);
tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr);
tipc_sk_rcv(n->net, &le->inputq);
}
-bool tipc_node_is_up(struct tipc_node *n)
+static bool tipc_node_is_up(struct tipc_node *n)
{
return n->active_links[0] != INVALID_BEARER_ID;
}
@@ -523,7 +741,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
if (!n)
return;
- tipc_node_lock(n);
+ tipc_node_write_lock(n);
le = &n->links[b->identity];
@@ -626,7 +844,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
}
memcpy(&le->maddr, maddr, sizeof(*maddr));
exit:
- tipc_node_unlock(n);
+ tipc_node_write_unlock(n);
if (reset && !tipc_link_is_reset(l))
tipc_node_link_down(n, b->identity, false);
tipc_node_put(n);
@@ -834,24 +1052,6 @@ illegal_evt:
pr_err("Illegal node fsm evt %x in state %x\n", evt, state);
}
-bool tipc_node_filter_pkt(struct tipc_node *n, struct tipc_msg *hdr)
-{
- int state = n->state;
-
- if (likely(state == SELF_UP_PEER_UP))
- return true;
-
- if (state == SELF_LEAVING_PEER_DOWN)
- return false;
-
- if (state == SELF_DOWN_PEER_LEAVING) {
- if (msg_peer_node_is_up(hdr))
- return false;
- }
-
- return true;
-}
-
static void node_lost_contact(struct tipc_node *n,
struct sk_buff_head *inputq)
{
@@ -913,56 +1113,18 @@ int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
if (bearer_id >= MAX_BEARERS)
goto exit;
- tipc_node_lock(node);
+ tipc_node_read_lock(node);
link = node->links[bearer_id].link;
if (link) {
- strncpy(linkname, link->name, len);
+ strncpy(linkname, tipc_link_name(link), len);
err = 0;
}
exit:
- tipc_node_unlock(node);
+ tipc_node_read_unlock(node);
tipc_node_put(node);
return err;
}
-void tipc_node_unlock(struct tipc_node *node)
-{
- struct net *net = node->net;
- u32 addr = 0;
- u32 flags = node->action_flags;
- u32 link_id = 0;
- struct list_head *publ_list;
-
- if (likely(!flags)) {
- spin_unlock_bh(&node->lock);
- return;
- }
-
- addr = node->addr;
- link_id = node->link_id;
- publ_list = &node->publ_list;
-
- node->action_flags &= ~(TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP |
- TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP);
-
- spin_unlock_bh(&node->lock);
-
- if (flags & TIPC_NOTIFY_NODE_DOWN)
- tipc_publ_notify(net, publ_list, addr);
-
- if (flags & TIPC_NOTIFY_NODE_UP)
- tipc_named_node_up(net, addr);
-
- if (flags & TIPC_NOTIFY_LINK_UP)
- tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,
- TIPC_NODE_SCOPE, link_id, addr);
-
- if (flags & TIPC_NOTIFY_LINK_DOWN)
- tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
- link_id, addr);
-
-}
-
/* Caller should hold node lock for the passed node */
static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
{
@@ -997,20 +1159,6 @@ msg_full:
return -EMSGSIZE;
}
-static struct tipc_link *tipc_node_select_link(struct tipc_node *n, int sel,
- int *bearer_id,
- struct tipc_media_addr **maddr)
-{
- int id = n->active_links[sel & 1];
-
- if (unlikely(id < 0))
- return NULL;
-
- *bearer_id = id;
- *maddr = &n->links[id].maddr;
- return n->links[id].link;
-}
-
/**
* tipc_node_xmit() is the general link level function for message sending
* @net: the applicable net namespace
@@ -1023,29 +1171,32 @@ static struct tipc_link *tipc_node_select_link(struct tipc_node *n, int sel,
int tipc_node_xmit(struct net *net, struct sk_buff_head *list,
u32 dnode, int selector)
{
- struct tipc_link *l = NULL;
+ struct tipc_link_entry *le = NULL;
struct tipc_node *n;
struct sk_buff_head xmitq;
- struct tipc_media_addr *maddr;
- int bearer_id;
+ int bearer_id = -1;
int rc = -EHOSTUNREACH;
__skb_queue_head_init(&xmitq);
n = tipc_node_find(net, dnode);
if (likely(n)) {
- tipc_node_lock(n);
- l = tipc_node_select_link(n, selector, &bearer_id, &maddr);
- if (likely(l))
- rc = tipc_link_xmit(l, list, &xmitq);
- tipc_node_unlock(n);
- if (unlikely(rc == -ENOBUFS))
+ tipc_node_read_lock(n);
+ bearer_id = n->active_links[selector & 1];
+ if (bearer_id >= 0) {
+ le = &n->links[bearer_id];
+ spin_lock_bh(&le->lock);
+ rc = tipc_link_xmit(le->link, list, &xmitq);
+ spin_unlock_bh(&le->lock);
+ }
+ tipc_node_read_unlock(n);
+ if (likely(!rc))
+ tipc_bearer_xmit(net, bearer_id, &xmitq, &le->maddr);
+ else if (rc == -ENOBUFS)
tipc_node_link_down(n, bearer_id, false);
tipc_node_put(n);
+ return rc;
}
- if (likely(!rc)) {
- tipc_bearer_xmit(net, bearer_id, &xmitq, maddr);
- return 0;
- }
+
if (likely(in_own_node(net, dnode))) {
tipc_sk_rcv(net, list);
return 0;
@@ -1075,6 +1226,30 @@ int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode,
return 0;
}
+void tipc_node_broadcast(struct net *net, struct sk_buff *skb)
+{
+ struct sk_buff *txskb;
+ struct tipc_node *n;
+ u32 dst;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(n, tipc_nodes(net), list) {
+ dst = n->addr;
+ if (in_own_node(net, dst))
+ continue;
+ if (!tipc_node_is_up(n))
+ continue;
+ txskb = pskb_copy(skb, GFP_ATOMIC);
+ if (!txskb)
+ break;
+ msg_set_destnode(buf_msg(txskb), dst);
+ tipc_node_xmit_skb(net, txskb, dst, 0);
+ }
+ rcu_read_unlock();
+
+ kfree_skb(skb);
+}
+
/**
* tipc_node_bc_rcv - process TIPC broadcast packet arriving from off-node
* @net: the applicable net namespace
@@ -1116,9 +1291,9 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
/* Broadcast ACKs are sent on a unicast link */
if (rc & TIPC_LINK_SND_BC_ACK) {
- tipc_node_lock(n);
+ tipc_node_read_lock(n);
tipc_link_build_ack_msg(le->link, &xmitq);
- tipc_node_unlock(n);
+ tipc_node_read_unlock(n);
}
if (!skb_queue_empty(&xmitq))
@@ -1151,30 +1326,30 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
u16 oseqno = msg_seqno(hdr);
u16 iseqno = msg_seqno(msg_get_wrapped(hdr));
u16 exp_pkts = msg_msgcnt(hdr);
- u16 rcv_nxt, syncpt, dlv_nxt;
+ u16 rcv_nxt, syncpt, dlv_nxt, inputq_len;
int state = n->state;
struct tipc_link *l, *tnl, *pl = NULL;
struct tipc_media_addr *maddr;
- int i, pb_id;
+ int pb_id;
l = n->links[bearer_id].link;
if (!l)
return false;
- rcv_nxt = l->rcv_nxt;
+ rcv_nxt = tipc_link_rcv_nxt(l);
if (likely((state == SELF_UP_PEER_UP) && (usr != TUNNEL_PROTOCOL)))
return true;
/* Find parallel link, if any */
- for (i = 0; i < MAX_BEARERS; i++) {
- if ((i != bearer_id) && n->links[i].link) {
- pl = n->links[i].link;
+ for (pb_id = 0; pb_id < MAX_BEARERS; pb_id++) {
+ if ((pb_id != bearer_id) && n->links[pb_id].link) {
+ pl = n->links[pb_id].link;
break;
}
}
- /* Update node accesibility if applicable */
+ /* Check and update node accesibility if applicable */
if (state == SELF_UP_PEER_COMING) {
if (!tipc_link_is_up(l))
return true;
@@ -1187,8 +1362,12 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
if (msg_peer_node_is_up(hdr))
return false;
tipc_node_fsm_evt(n, PEER_LOST_CONTACT_EVT);
+ return true;
}
+ if (state == SELF_LEAVING_PEER_DOWN)
+ return false;
+
/* Ignore duplicate packets */
if ((usr != LINK_PROTOCOL) && less(oseqno, rcv_nxt))
return true;
@@ -1197,9 +1376,9 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
if ((usr == TUNNEL_PROTOCOL) && (mtyp == FAILOVER_MSG)) {
syncpt = oseqno + exp_pkts - 1;
if (pl && tipc_link_is_up(pl)) {
- pb_id = pl->bearer_id;
__tipc_node_link_down(n, &pb_id, xmitq, &maddr);
- tipc_skb_queue_splice_tail_init(pl->inputq, l->inputq);
+ tipc_skb_queue_splice_tail_init(tipc_link_inputq(pl),
+ tipc_link_inputq(l));
}
/* If pkts arrive out of order, use lowest calculated syncpt */
if (less(syncpt, n->sync_point))
@@ -1232,19 +1411,18 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
tipc_link_fsm_evt(l, LINK_SYNCH_BEGIN_EVT);
tipc_node_fsm_evt(n, NODE_SYNCH_BEGIN_EVT);
}
- if (less(syncpt, n->sync_point))
- n->sync_point = syncpt;
}
/* Open tunnel link when parallel link reaches synch point */
- if ((n->state == NODE_SYNCHING) && tipc_link_is_synching(l)) {
+ if (n->state == NODE_SYNCHING) {
if (tipc_link_is_synching(l)) {
tnl = l;
} else {
tnl = pl;
pl = l;
}
- dlv_nxt = pl->rcv_nxt - mod(skb_queue_len(pl->inputq));
+ inputq_len = skb_queue_len(tipc_link_inputq(pl));
+ dlv_nxt = tipc_link_rcv_nxt(pl) - inputq_len;
if (more(dlv_nxt, n->sync_point)) {
tipc_link_fsm_evt(tnl, LINK_SYNCH_END_EVT);
tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);
@@ -1304,22 +1482,32 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
/* Ensure broadcast reception is in synch with peer's send state */
if (unlikely(usr == LINK_PROTOCOL))
tipc_bcast_sync_rcv(net, n->bc_entry.link, hdr);
- else if (unlikely(n->bc_entry.link->acked != bc_ack))
+ else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack))
tipc_bcast_ack_rcv(net, n->bc_entry.link, bc_ack);
- tipc_node_lock(n);
-
- /* Is reception permitted at the moment ? */
- if (!tipc_node_filter_pkt(n, hdr))
- goto unlock;
-
- /* Check and if necessary update node state */
- if (likely(tipc_node_check_state(n, skb, bearer_id, &xmitq))) {
- rc = tipc_link_rcv(le->link, skb, &xmitq);
- skb = NULL;
+ /* Receive packet directly if conditions permit */
+ tipc_node_read_lock(n);
+ if (likely((n->state == SELF_UP_PEER_UP) && (usr != TUNNEL_PROTOCOL))) {
+ spin_lock_bh(&le->lock);
+ if (le->link) {
+ rc = tipc_link_rcv(le->link, skb, &xmitq);
+ skb = NULL;
+ }
+ spin_unlock_bh(&le->lock);
+ }
+ tipc_node_read_unlock(n);
+
+ /* Check/update node state before receiving */
+ if (unlikely(skb)) {
+ tipc_node_write_lock(n);
+ if (tipc_node_check_state(n, skb, bearer_id, &xmitq)) {
+ if (le->link) {
+ rc = tipc_link_rcv(le->link, skb, &xmitq);
+ skb = NULL;
+ }
+ }
+ tipc_node_write_unlock(n);
}
-unlock:
- tipc_node_unlock(n);
if (unlikely(rc & TIPC_LINK_UP_EVT))
tipc_node_link_up(n, bearer_id, &xmitq);
@@ -1384,15 +1572,15 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
continue;
}
- tipc_node_lock(node);
+ tipc_node_read_lock(node);
err = __tipc_nl_add_node(&msg, node);
if (err) {
last_addr = node->addr;
- tipc_node_unlock(node);
+ tipc_node_read_unlock(node);
goto out;
}
- tipc_node_unlock(node);
+ tipc_node_read_unlock(node);
}
done = 1;
out:
@@ -1402,3 +1590,314 @@ out:
return skb->len;
}
+
+/* tipc_node_find_by_name - locate owner node of link by link's name
+ * @net: the applicable net namespace
+ * @name: pointer to link name string
+ * @bearer_id: pointer to index in 'node->links' array where the link was found.
+ *
+ * Returns pointer to node owning the link, or 0 if no matching link is found.
+ */
+static struct tipc_node *tipc_node_find_by_name(struct net *net,
+ const char *link_name,
+ unsigned int *bearer_id)
+{
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct tipc_link *l;
+ struct tipc_node *n;
+ struct tipc_node *found_node = NULL;
+ int i;
+
+ *bearer_id = 0;
+ rcu_read_lock();
+ list_for_each_entry_rcu(n, &tn->node_list, list) {
+ tipc_node_read_lock(n);
+ for (i = 0; i < MAX_BEARERS; i++) {
+ l = n->links[i].link;
+ if (l && !strcmp(tipc_link_name(l), link_name)) {
+ *bearer_id = i;
+ found_node = n;
+ break;
+ }
+ }
+ tipc_node_read_unlock(n);
+ if (found_node)
+ break;
+ }
+ rcu_read_unlock();
+
+ return found_node;
+}
+
+int tipc_nl_node_set_link(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ int res = 0;
+ int bearer_id;
+ char *name;
+ struct tipc_link *link;
+ struct tipc_node *node;
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
+ struct net *net = sock_net(skb->sk);
+
+ if (!info->attrs[TIPC_NLA_LINK])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
+ info->attrs[TIPC_NLA_LINK],
+ tipc_nl_link_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+
+ name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+
+ if (strcmp(name, tipc_bclink_name) == 0)
+ return tipc_nl_bc_link_set(net, attrs);
+
+ node = tipc_node_find_by_name(net, name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ tipc_node_read_lock(node);
+
+ link = node->links[bearer_id].link;
+ if (!link) {
+ res = -EINVAL;
+ goto out;
+ }
+
+ if (attrs[TIPC_NLA_LINK_PROP]) {
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
+
+ err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP],
+ props);
+ if (err) {
+ res = err;
+ goto out;
+ }
+
+ if (props[TIPC_NLA_PROP_TOL]) {
+ u32 tol;
+
+ tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
+ tipc_link_set_tolerance(link, tol);
+ }
+ if (props[TIPC_NLA_PROP_PRIO]) {
+ u32 prio;
+
+ prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
+ tipc_link_set_prio(link, prio);
+ }
+ if (props[TIPC_NLA_PROP_WIN]) {
+ u32 win;
+
+ win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
+ tipc_link_set_queue_limits(link, win);
+ }
+ }
+
+out:
+ tipc_node_read_unlock(node);
+
+ return res;
+}
+
+int tipc_nl_node_get_link(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct tipc_nl_msg msg;
+ char *name;
+ int err;
+
+ msg.portid = info->snd_portid;
+ msg.seq = info->snd_seq;
+
+ if (!info->attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+ name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]);
+
+ msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg.skb)
+ return -ENOMEM;
+
+ if (strcmp(name, tipc_bclink_name) == 0) {
+ err = tipc_nl_add_bc_link(net, &msg);
+ if (err) {
+ nlmsg_free(msg.skb);
+ return err;
+ }
+ } else {
+ int bearer_id;
+ struct tipc_node *node;
+ struct tipc_link *link;
+
+ node = tipc_node_find_by_name(net, name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ tipc_node_read_lock(node);
+ link = node->links[bearer_id].link;
+ if (!link) {
+ tipc_node_read_unlock(node);
+ nlmsg_free(msg.skb);
+ return -EINVAL;
+ }
+
+ err = __tipc_nl_add_link(net, &msg, link, 0);
+ tipc_node_read_unlock(node);
+ if (err) {
+ nlmsg_free(msg.skb);
+ return err;
+ }
+ }
+
+ return genlmsg_reply(msg.skb, info);
+}
+
+int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *link_name;
+ unsigned int bearer_id;
+ struct tipc_link *link;
+ struct tipc_node *node;
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
+ struct net *net = sock_net(skb->sk);
+ struct tipc_link_entry *le;
+
+ if (!info->attrs[TIPC_NLA_LINK])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
+ info->attrs[TIPC_NLA_LINK],
+ tipc_nl_link_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+
+ link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+
+ if (strcmp(link_name, tipc_bclink_name) == 0) {
+ err = tipc_bclink_reset_stats(net);
+ if (err)
+ return err;
+ return 0;
+ }
+
+ node = tipc_node_find_by_name(net, link_name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ le = &node->links[bearer_id];
+ tipc_node_read_lock(node);
+ spin_lock_bh(&le->lock);
+ link = node->links[bearer_id].link;
+ if (!link) {
+ spin_unlock_bh(&le->lock);
+ tipc_node_read_unlock(node);
+ return -EINVAL;
+ }
+ tipc_link_reset_stats(link);
+ spin_unlock_bh(&le->lock);
+ tipc_node_read_unlock(node);
+ return 0;
+}
+
+/* Caller should hold node lock */
+static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg,
+ struct tipc_node *node, u32 *prev_link)
+{
+ u32 i;
+ int err;
+
+ for (i = *prev_link; i < MAX_BEARERS; i++) {
+ *prev_link = i;
+
+ if (!node->links[i].link)
+ continue;
+
+ err = __tipc_nl_add_link(net, msg,
+ node->links[i].link, NLM_F_MULTI);
+ if (err)
+ return err;
+ }
+ *prev_link = 0;
+
+ return 0;
+}
+
+int tipc_nl_node_dump_link(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct tipc_node *node;
+ struct tipc_nl_msg msg;
+ u32 prev_node = cb->args[0];
+ u32 prev_link = cb->args[1];
+ int done = cb->args[2];
+ int err;
+
+ if (done)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ rcu_read_lock();
+ if (prev_node) {
+ node = tipc_node_find(net, prev_node);
+ if (!node) {
+ /* We never set seq or call nl_dump_check_consistent()
+ * this means that setting prev_seq here will cause the
+ * consistence check to fail in the netlink callback
+ * handler. Resulting in the last NLMSG_DONE message
+ * having the NLM_F_DUMP_INTR flag set.
+ */
+ cb->prev_seq = 1;
+ goto out;
+ }
+ tipc_node_put(node);
+
+ list_for_each_entry_continue_rcu(node, &tn->node_list,
+ list) {
+ tipc_node_read_lock(node);
+ err = __tipc_nl_add_node_links(net, &msg, node,
+ &prev_link);
+ tipc_node_read_unlock(node);
+ if (err)
+ goto out;
+
+ prev_node = node->addr;
+ }
+ } else {
+ err = tipc_nl_add_bc_link(net, &msg);
+ if (err)
+ goto out;
+
+ list_for_each_entry_rcu(node, &tn->node_list, list) {
+ tipc_node_read_lock(node);
+ err = __tipc_nl_add_node_links(net, &msg, node,
+ &prev_link);
+ tipc_node_read_unlock(node);
+ if (err)
+ goto out;
+
+ prev_node = node->addr;
+ }
+ }
+ done = 1;
+out:
+ rcu_read_unlock();
+
+ cb->args[0] = prev_node;
+ cb->args[1] = prev_link;
+ cb->args[2] = done;
+
+ return skb->len;
+}
diff --git a/net/tipc/node.h b/net/tipc/node.h
index 6734562d3c6e..f39d9d06e8bb 100644
--- a/net/tipc/node.h
+++ b/net/tipc/node.h
@@ -42,23 +42,6 @@
#include "bearer.h"
#include "msg.h"
-/* Out-of-range value for node signature */
-#define INVALID_NODE_SIG 0x10000
-
-#define INVALID_BEARER_ID -1
-
-/* Flags used to take different actions according to flag type
- * TIPC_NOTIFY_NODE_DOWN: notify node is down
- * TIPC_NOTIFY_NODE_UP: notify node is up
- * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type
- */
-enum {
- TIPC_NOTIFY_NODE_DOWN = (1 << 3),
- TIPC_NOTIFY_NODE_UP = (1 << 4),
- TIPC_NOTIFY_LINK_UP = (1 << 6),
- TIPC_NOTIFY_LINK_DOWN = (1 << 7)
-};
-
/* Optional capabilities supported by this code version
*/
enum {
@@ -66,72 +49,8 @@ enum {
};
#define TIPC_NODE_CAPABILITIES TIPC_BCAST_SYNCH
+#define INVALID_BEARER_ID -1
-struct tipc_link_entry {
- struct tipc_link *link;
- u32 mtu;
- struct sk_buff_head inputq;
- struct tipc_media_addr maddr;
-};
-
-struct tipc_bclink_entry {
- struct tipc_link *link;
- struct sk_buff_head inputq1;
- struct sk_buff_head arrvq;
- struct sk_buff_head inputq2;
- struct sk_buff_head namedq;
-};
-
-/**
- * struct tipc_node - TIPC node structure
- * @addr: network address of node
- * @ref: reference counter to node object
- * @lock: spinlock governing access to structure
- * @net: the applicable net namespace
- * @hash: links to adjacent nodes in unsorted hash chain
- * @inputq: pointer to input queue containing messages for msg event
- * @namedq: pointer to name table input queue with name table messages
- * @active_links: bearer ids of active links, used as index into links[] array
- * @links: array containing references to all links to node
- * @action_flags: bit mask of different types of node actions
- * @state: connectivity state vs peer node
- * @sync_point: sequence number where synch/failover is finished
- * @list: links to adjacent nodes in sorted list of cluster's nodes
- * @working_links: number of working links to node (both active and standby)
- * @link_cnt: number of links to node
- * @capabilities: bitmap, indicating peer node's functional capabilities
- * @signature: node instance identifier
- * @link_id: local and remote bearer ids of changing link, if any
- * @publ_list: list of publications
- * @rcu: rcu struct for tipc_node
- */
-struct tipc_node {
- u32 addr;
- struct kref kref;
- spinlock_t lock;
- struct net *net;
- struct hlist_node hash;
- int active_links[2];
- struct tipc_link_entry links[MAX_BEARERS];
- struct tipc_bclink_entry bc_entry;
- int action_flags;
- struct list_head list;
- int state;
- u16 sync_point;
- int link_cnt;
- u16 working_links;
- u16 capabilities;
- u32 signature;
- u32 link_id;
- struct list_head publ_list;
- struct list_head conn_sks;
- unsigned long keepalive_intv;
- struct timer_list timer;
- struct rcu_head rcu;
-};
-
-struct tipc_node *tipc_node_find(struct net *net, u32 addr);
-void tipc_node_put(struct tipc_node *node);
void tipc_node_stop(struct net *net);
void tipc_node_check_dest(struct net *net, u32 onode,
struct tipc_bearer *bearer,
@@ -139,50 +58,22 @@ void tipc_node_check_dest(struct net *net, u32 onode,
struct tipc_media_addr *maddr,
bool *respond, bool *dupl_addr);
void tipc_node_delete_links(struct net *net, int bearer_id);
-void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr);
-void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr);
-bool tipc_node_is_up(struct tipc_node *n);
int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 node,
char *linkname, size_t len);
-void tipc_node_unlock(struct tipc_node *node);
int tipc_node_xmit(struct net *net, struct sk_buff_head *list, u32 dnode,
int selector);
int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dest,
u32 selector);
+void tipc_node_subscribe(struct net *net, struct list_head *subscr, u32 addr);
+void tipc_node_unsubscribe(struct net *net, struct list_head *subscr, u32 addr);
+void tipc_node_broadcast(struct net *net, struct sk_buff *skb);
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port);
void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port);
+int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel);
int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb);
-
-static inline void tipc_node_lock(struct tipc_node *node)
-{
- spin_lock_bh(&node->lock);
-}
-
-static inline struct tipc_link *node_active_link(struct tipc_node *n, int sel)
-{
- int bearer_id = n->active_links[sel & 1];
-
- if (unlikely(bearer_id == INVALID_BEARER_ID))
- return NULL;
-
- return n->links[bearer_id].link;
-}
-
-static inline unsigned int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel)
-{
- struct tipc_node *n;
- int bearer_id;
- unsigned int mtu = MAX_MSG_SIZE;
-
- n = tipc_node_find(net, addr);
- if (unlikely(!n))
- return mtu;
-
- bearer_id = n->active_links[sel & 1];
- if (likely(bearer_id != INVALID_BEARER_ID))
- mtu = n->links[bearer_id].mtu;
- tipc_node_put(n);
- return mtu;
-}
+int tipc_nl_node_dump_link(struct sk_buff *skb, struct netlink_callback *cb);
+int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_node_get_link(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_node_set_link(struct sk_buff *skb, struct genl_info *info);
#endif
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index b53246fb0412..69c29050f14a 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -1491,7 +1491,7 @@ static void tipc_write_space(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLWRNORM | POLLWRBAND);
rcu_read_unlock();
@@ -1508,7 +1508,7 @@ static void tipc_data_ready(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM | POLLRDBAND);
rcu_read_unlock();
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 70c03271b798..d63a911e7fe2 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -48,7 +48,6 @@
#include <linux/tipc_netlink.h>
#include "core.h"
#include "bearer.h"
-#include "msg.h"
/* IANA assigned UDP port */
#define UDP_PORT_DEFAULT 6118
@@ -183,15 +182,9 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
goto tx_error;
}
ttl = ip4_dst_hoplimit(&rt->dst);
- err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb,
- src->ipv4.s_addr,
- dst->ipv4.s_addr, 0, ttl, 0,
- src->udp_port, dst->udp_port,
- false, true);
- if (err < 0) {
- ip_rt_put(rt);
- goto tx_error;
- }
+ udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr,
+ dst->ipv4.s_addr, 0, ttl, 0, src->udp_port,
+ dst->udp_port, false, true);
#if IS_ENABLED(CONFIG_IPV6)
} else {
struct dst_entry *ndst;
@@ -224,10 +217,6 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
{
struct udp_bearer *ub;
struct tipc_bearer *b;
- int usr = msg_user(buf_msg(skb));
-
- if ((usr == LINK_PROTOCOL) || (usr == NAME_DISTRIBUTOR))
- skb_linearize(skb);
ub = rcu_dereference_sk_user_data(sk);
if (!ub) {
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index ef05cd9403d4..c5bf5ef2bf89 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -451,7 +451,7 @@ static void unix_write_space(struct sock *sk)
rcu_read_lock();
if (unix_writable(sk)) {
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait,
POLLOUT | POLLWRNORM | POLLWRBAND);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
@@ -1513,6 +1513,21 @@ static void unix_destruct_scm(struct sk_buff *skb)
sock_wfree(skb);
}
+/*
+ * The "user->unix_inflight" variable is protected by the garbage
+ * collection lock, and we just read it locklessly here. If you go
+ * over the limit, there might be a tiny race in actually noticing
+ * it across threads. Tough.
+ */
+static inline bool too_many_unix_fds(struct task_struct *p)
+{
+ struct user_struct *user = current_user();
+
+ if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE)))
+ return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN);
+ return false;
+}
+
#define MAX_RECURSION_LEVEL 4
static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
@@ -1521,6 +1536,9 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
unsigned char max_level = 0;
int unix_sock_count = 0;
+ if (too_many_unix_fds(current))
+ return -ETOOMANYREFS;
+
for (i = scm->fp->count - 1; i >= 0; i--) {
struct sock *sk = unix_get_socket(scm->fp->fp[i]);
@@ -1542,10 +1560,8 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
if (!UNIXCB(skb).fp)
return -ENOMEM;
- if (unix_sock_count) {
- for (i = scm->fp->count - 1; i >= 0; i--)
- unix_inflight(scm->fp->fp[i]);
- }
+ for (i = scm->fp->count - 1; i >= 0; i--)
+ unix_inflight(scm->fp->fp[i]);
return max_level;
}
@@ -2092,8 +2108,8 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
struct scm_cookie scm;
struct sock *sk = sock->sk;
struct unix_sock *u = unix_sk(sk);
- int noblock = flags & MSG_DONTWAIT;
- struct sk_buff *skb;
+ struct sk_buff *skb, *last;
+ long timeo;
int err;
int peeked, skip;
@@ -2101,30 +2117,38 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
if (flags&MSG_OOB)
goto out;
- err = mutex_lock_interruptible(&u->readlock);
- if (unlikely(err)) {
- /* recvmsg() in non blocking mode is supposed to return -EAGAIN
- * sk_rcvtimeo is not honored by mutex_lock_interruptible()
- */
- err = noblock ? -EAGAIN : -ERESTARTSYS;
- goto out;
- }
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
- skip = sk_peek_offset(sk, flags);
+ do {
+ mutex_lock(&u->readlock);
- skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err);
- if (!skb) {
+ skip = sk_peek_offset(sk, flags);
+ skb = __skb_try_recv_datagram(sk, flags, &peeked, &skip, &err,
+ &last);
+ if (skb)
+ break;
+
+ mutex_unlock(&u->readlock);
+
+ if (err != -EAGAIN)
+ break;
+ } while (timeo &&
+ !__skb_wait_for_more_packets(sk, &err, &timeo, last));
+
+ if (!skb) { /* implies readlock unlocked */
unix_state_lock(sk);
/* Signal EOF on disconnected non-blocking SEQPACKET socket. */
if (sk->sk_type == SOCK_SEQPACKET && err == -EAGAIN &&
(sk->sk_shutdown & RCV_SHUTDOWN))
err = 0;
unix_state_unlock(sk);
- goto out_unlock;
+ goto out;
}
- wake_up_interruptible_sync_poll(&u->peer_wait,
- POLLOUT | POLLWRNORM | POLLWRBAND);
+ if (wq_has_sleeper(&u->peer_wait))
+ wake_up_interruptible_sync_poll(&u->peer_wait,
+ POLLOUT | POLLWRNORM |
+ POLLWRBAND);
if (msg->msg_name)
unix_copy_addr(msg, skb->sk);
@@ -2176,7 +2200,6 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
out_free:
skb_free_datagram(sk, skb);
-out_unlock:
mutex_unlock(&u->readlock);
out:
return err;
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index a73a226f2d33..8fcdc2283af5 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -120,11 +120,11 @@ void unix_inflight(struct file *fp)
{
struct sock *s = unix_get_socket(fp);
+ spin_lock(&unix_gc_lock);
+
if (s) {
struct unix_sock *u = unix_sk(s);
- spin_lock(&unix_gc_lock);
-
if (atomic_long_inc_return(&u->inflight) == 1) {
BUG_ON(!list_empty(&u->link));
list_add_tail(&u->link, &gc_inflight_list);
@@ -132,25 +132,28 @@ void unix_inflight(struct file *fp)
BUG_ON(list_empty(&u->link));
}
unix_tot_inflight++;
- spin_unlock(&unix_gc_lock);
}
+ fp->f_cred->user->unix_inflight++;
+ spin_unlock(&unix_gc_lock);
}
void unix_notinflight(struct file *fp)
{
struct sock *s = unix_get_socket(fp);
+ spin_lock(&unix_gc_lock);
+
if (s) {
struct unix_sock *u = unix_sk(s);
- spin_lock(&unix_gc_lock);
BUG_ON(list_empty(&u->link));
if (atomic_long_dec_and_test(&u->inflight))
list_del_init(&u->link);
unix_tot_inflight--;
- spin_unlock(&unix_gc_lock);
}
+ fp->f_cred->user->unix_inflight--;
+ spin_unlock(&unix_gc_lock);
}
static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),
diff --git a/net/vmw_vsock/vmci_transport.h b/net/vmw_vsock/vmci_transport.h
index 2ad46f39649f..1820e74a5752 100644
--- a/net/vmw_vsock/vmci_transport.h
+++ b/net/vmw_vsock/vmci_transport.h
@@ -121,7 +121,7 @@ struct vmci_transport {
u64 queue_pair_max_size;
u32 detach_sub_id;
union vmci_transport_notify notify;
- struct vmci_transport_notify_ops *notify_ops;
+ const struct vmci_transport_notify_ops *notify_ops;
struct list_head elem;
struct sock *sk;
spinlock_t lock; /* protects sk. */
diff --git a/net/vmw_vsock/vmci_transport_notify.c b/net/vmw_vsock/vmci_transport_notify.c
index 9b7f207f2bee..fd8cf0214d51 100644
--- a/net/vmw_vsock/vmci_transport_notify.c
+++ b/net/vmw_vsock/vmci_transport_notify.c
@@ -661,7 +661,7 @@ static void vmci_transport_notify_pkt_process_negotiate(struct sock *sk)
}
/* Socket control packet based operations. */
-struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops = {
+const struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops = {
vmci_transport_notify_pkt_socket_init,
vmci_transport_notify_pkt_socket_destruct,
vmci_transport_notify_pkt_poll_in,
diff --git a/net/vmw_vsock/vmci_transport_notify.h b/net/vmw_vsock/vmci_transport_notify.h
index 7df793249b6c..3c464d394a8f 100644
--- a/net/vmw_vsock/vmci_transport_notify.h
+++ b/net/vmw_vsock/vmci_transport_notify.h
@@ -77,7 +77,8 @@ struct vmci_transport_notify_ops {
void (*process_negotiate) (struct sock *sk);
};
-extern struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops;
-extern struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops;
+extern const struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops;
+extern const
+struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops;
#endif /* __VMCI_TRANSPORT_NOTIFY_H__ */
diff --git a/net/vmw_vsock/vmci_transport_notify_qstate.c b/net/vmw_vsock/vmci_transport_notify_qstate.c
index dc9c7929a2f9..21e591dafb03 100644
--- a/net/vmw_vsock/vmci_transport_notify_qstate.c
+++ b/net/vmw_vsock/vmci_transport_notify_qstate.c
@@ -419,7 +419,7 @@ vmci_transport_notify_pkt_send_pre_enqueue(
}
/* Socket always on control packet based operations. */
-struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops = {
+const struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops = {
vmci_transport_notify_pkt_socket_init,
vmci_transport_notify_pkt_socket_destruct,
vmci_transport_notify_pkt_poll_in,
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a618b4b86fa4..022ccad06cbe 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -416,13 +416,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
void cfg80211_process_wdev_events(struct wireless_dev *wdev);
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- enum nl80211_iftype iftype,
- struct ieee80211_channel *chan,
- enum cfg80211_chan_mode chanmode,
- u8 radar_detect);
-
/**
* cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
* @wiphy: the wiphy to validate against
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c
index dc0e59e53dbf..6beab0cfcb99 100644
--- a/net/wireless/lib80211_crypt_ccmp.c
+++ b/net/wireless/lib80211_crypt_ccmp.c
@@ -311,8 +311,8 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
keyidx >>= 6;
if (key->key_idx != keyidx) {
- printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
- "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
+ net_dbg_ratelimited("CCMP: RX tkey->key_idx=%d frame keyidx=%d\n",
+ key->key_idx, keyidx);
return -6;
}
if (!key->key_set) {
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c
index 8c90ba79e56e..3cd819539241 100644
--- a/net/wireless/lib80211_crypt_tkip.c
+++ b/net/wireless/lib80211_crypt_tkip.c
@@ -434,8 +434,8 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
keyidx >>= 6;
if (tkey->key_idx != keyidx) {
- printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
- "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
+ net_dbg_ratelimited("TKIP: RX tkey->key_idx=%d frame keyidx=%d\n",
+ tkey->key_idx, keyidx);
return -6;
}
if (!tkey->key_set) {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 75b0d23ee882..d4786f2802aa 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4256,8 +4256,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
* station. Include these parameters here and will check them in
* cfg80211_check_station_change().
*/
- if (info->attrs[NL80211_ATTR_PEER_AID])
- params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+ if (info->attrs[NL80211_ATTR_STA_AID])
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
params.listen_interval =
@@ -4359,6 +4359,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
struct station_parameters params;
u8 *mac_addr = NULL;
+ u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED);
memset(&params, 0, sizeof(params));
@@ -4470,10 +4472,23 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* allow authenticated/associated only if driver handles it */
if (!(rdev->wiphy.features &
NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
- params.sta_flags_mask &
- (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
- BIT(NL80211_STA_FLAG_ASSOCIATED)))
- return -EINVAL;
+ params.sta_flags_mask & auth_assoc)
+ return -EINVAL;
+
+ /* Older userspace, or userspace wanting to be compatible with
+ * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth
+ * and assoc flags in the mask, but assumes the station will be
+ * added as associated anyway since this was the required driver
+ * behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was
+ * introduced.
+ * In order to not bother drivers with this quirk in the API
+ * set the flags in both the mask and set for new stations in
+ * this case.
+ */
+ if (!(params.sta_flags_mask & auth_assoc)) {
+ params.sta_flags_mask |= auth_assoc;
+ params.sta_flags_set |= auth_assoc;
+ }
/* must be last in here for error handling */
params.vlan = get_vlan(info, rdev);
@@ -5997,6 +6012,24 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (!rdev->ops->abort_scan)
+ return -EOPNOTSUPP;
+
+ if (rdev->scan_msg)
+ return 0;
+
+ if (!rdev->scan_req)
+ return -ENOENT;
+
+ rdev_abort_scan(rdev, wdev);
+ return 0;
+}
+
static int
nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
struct cfg80211_sched_scan_request *request,
@@ -6507,8 +6540,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (WARN_ON(!cac_time_ms))
cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
- err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef,
- cac_time_ms);
+ err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
if (!err) {
wdev->chandef = chandef;
wdev->cac_started = true;
@@ -7571,7 +7603,7 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
return -EINVAL;
- err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ err = rdev_set_mcast_rate(rdev, dev, mcast_rate);
return err;
}
@@ -9719,7 +9751,7 @@ static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
cfg80211_rdev_free_coalesce(rdev);
- rdev->ops->set_coalesce(&rdev->wiphy, NULL);
+ rdev_set_coalesce(rdev, NULL);
return 0;
}
@@ -9747,7 +9779,7 @@ static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
i++;
}
- err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
+ err = rdev_set_coalesce(rdev, &new_coalesce);
if (err)
goto error;
@@ -10949,6 +10981,14 @@ static const struct genl_ops nl80211_ops[] = {
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_ABORT_SCAN,
+ .doit = nl80211_abort_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_GET_SCAN,
.policy = nl80211_policy,
.dumpit = nl80211_dump_scan,
diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c
index c00d4a792319..e64dbf16330c 100644
--- a/net/wireless/ocb.c
+++ b/net/wireless/ocb.c
@@ -29,6 +29,9 @@ int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
return -EOPNOTSUPP;
+ if (!rdev->ops->join_ocb)
+ return -EOPNOTSUPP;
+
if (WARN_ON(!setup->chandef.chan))
return -EINVAL;
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index c23516d0f807..8ae0c04f9fc7 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -427,6 +427,14 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline void rdev_abort_scan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_abort_scan(&rdev->wiphy, wdev);
+ rdev->ops->abort_scan(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
static inline int rdev_auth(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_auth_request *req)
@@ -1020,4 +1028,47 @@ rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
trace_rdev_return_void(&rdev->wiphy);
}
+static inline int
+rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
+ cac_time_ms);
+ if (rdev->ops->start_radar_detection)
+ ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
+ chandef, cac_time_ms);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_mcast_rate(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ int mcast_rate[IEEE80211_NUM_BANDS])
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ if (rdev->ops->set_mcast_rate)
+ ret = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_coalesce(struct cfg80211_registered_device *rdev,
+ struct cfg80211_coalesce *coalesce)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_set_coalesce(&rdev->wiphy, coalesce);
+ if (rdev->ops->set_coalesce)
+ ret = rdev->ops->set_coalesce(&rdev->wiphy, coalesce);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 06d050da0d94..3b0ce1c484a3 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1052,7 +1052,7 @@ static u32 map_regdom_flags(u32 rd_flags)
}
static const struct ieee80211_reg_rule *
-freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
+freq_reg_info_regd(u32 center_freq,
const struct ieee80211_regdomain *regd, u32 bw)
{
int i;
@@ -1097,7 +1097,7 @@ __freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
u32 bw;
for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
- reg_rule = freq_reg_info_regd(wiphy, center_freq, regd, bw);
+ reg_rule = freq_reg_info_regd(center_freq, regd, bw);
if (!IS_ERR(reg_rule))
return reg_rule;
}
@@ -1166,6 +1166,41 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
#endif
}
+static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd,
+ const struct ieee80211_reg_rule *reg_rule,
+ const struct ieee80211_channel *chan)
+{
+ const struct ieee80211_freq_range *freq_range = NULL;
+ u32 max_bandwidth_khz, bw_flags = 0;
+
+ freq_range = &reg_rule->freq_range;
+
+ max_bandwidth_khz = freq_range->max_bandwidth_khz;
+ /* Check if auto calculation requested */
+ if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
+
+ /* If we get a reg_rule we can assume that at least 5Mhz fit */
+ if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(10)))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(20)))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+
+ if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(40))
+ bw_flags |= IEEE80211_CHAN_NO_HT40;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(80))
+ bw_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(160))
+ bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ return bw_flags;
+}
+
/*
* Note that right now we assume the desired channel bandwidth
* is always 20 MHz for each individual channel (HT40 uses 20 MHz
@@ -1178,11 +1213,9 @@ static void handle_channel(struct wiphy *wiphy,
u32 flags, bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
- const struct ieee80211_freq_range *freq_range = NULL;
struct wiphy *request_wiphy = NULL;
struct regulatory_request *lr = get_last_request();
const struct ieee80211_regdomain *regd;
- u32 max_bandwidth_khz;
request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
@@ -1223,31 +1256,7 @@ static void handle_channel(struct wiphy *wiphy,
chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
- freq_range = &reg_rule->freq_range;
-
- max_bandwidth_khz = freq_range->max_bandwidth_khz;
- /* Check if auto calculation requested */
- if (reg_rule->flags & NL80211_RRF_AUTO_BW)
- max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
-
- /* If we get a reg_rule we can assume that at least 5Mhz fit */
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(10)))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(20)))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
-
- if (max_bandwidth_khz < MHZ_TO_KHZ(10))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(20))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags |= IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
@@ -1760,13 +1769,10 @@ static void handle_channel_custom(struct wiphy *wiphy,
u32 bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
- const struct ieee80211_freq_range *freq_range = NULL;
- u32 max_bandwidth_khz;
u32 bw;
for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
- reg_rule = freq_reg_info_regd(wiphy,
- MHZ_TO_KHZ(chan->center_freq),
+ reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq),
regd, bw);
if (!IS_ERR(reg_rule))
break;
@@ -1787,31 +1793,7 @@ static void handle_channel_custom(struct wiphy *wiphy,
chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
- freq_range = &reg_rule->freq_range;
-
- max_bandwidth_khz = freq_range->max_bandwidth_khz;
- /* Check if auto calculation requested */
- if (reg_rule->flags & NL80211_RRF_AUTO_BW)
- max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
-
- /* If we get a reg_rule we can assume that at least 5Mhz fit */
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(10)))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(20)))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
-
- if (max_bandwidth_khz < MHZ_TO_KHZ(10))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(20))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags |= IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
chan->dfs_state_entered = jiffies;
chan->dfs_state = NL80211_DFS_USABLE;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 0c392d36781b..09b242b09bed 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -623,12 +623,24 @@ DECLARE_EVENT_CLASS(station_add_change,
__field(u32, sta_flags_set)
__field(u32, sta_modify_mask)
__field(int, listen_interval)
+ __field(u16, capability)
__field(u16, aid)
__field(u8, plink_action)
__field(u8, plink_state)
__field(u8, uapsd_queues)
+ __field(u8, max_sp)
+ __field(u8, opmode_notif)
+ __field(bool, opmode_notif_used)
__array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
+ __array(u8, vht_capa, (int)sizeof(struct ieee80211_vht_cap))
__array(char, vlan, IFNAMSIZ)
+ __dynamic_array(u8, supported_rates,
+ params->supported_rates_len)
+ __dynamic_array(u8, ext_capab, params->ext_capab_len)
+ __dynamic_array(u8, supported_channels,
+ params->supported_channels_len)
+ __dynamic_array(u8, supported_oper_classes,
+ params->supported_oper_classes_len)
),
TP_fast_assign(
WIPHY_ASSIGN;
@@ -646,9 +658,35 @@ DECLARE_EVENT_CLASS(station_add_change,
if (params->ht_capa)
memcpy(__entry->ht_capa, params->ht_capa,
sizeof(struct ieee80211_ht_cap));
+ memset(__entry->vht_capa, 0, sizeof(struct ieee80211_vht_cap));
+ if (params->vht_capa)
+ memcpy(__entry->vht_capa, params->vht_capa,
+ sizeof(struct ieee80211_vht_cap));
memset(__entry->vlan, 0, sizeof(__entry->vlan));
if (params->vlan)
memcpy(__entry->vlan, params->vlan->name, IFNAMSIZ);
+ if (params->supported_rates && params->supported_rates_len)
+ memcpy(__get_dynamic_array(supported_rates),
+ params->supported_rates,
+ params->supported_rates_len);
+ if (params->ext_capab && params->ext_capab_len)
+ memcpy(__get_dynamic_array(ext_capab),
+ params->ext_capab,
+ params->ext_capab_len);
+ if (params->supported_channels &&
+ params->supported_channels_len)
+ memcpy(__get_dynamic_array(supported_channels),
+ params->supported_channels,
+ params->supported_channels_len);
+ if (params->supported_oper_classes &&
+ params->supported_oper_classes_len)
+ memcpy(__get_dynamic_array(supported_oper_classes),
+ params->supported_oper_classes,
+ params->supported_oper_classes_len);
+ __entry->max_sp = params->max_sp;
+ __entry->capability = params->capability;
+ __entry->opmode_notif = params->opmode_notif;
+ __entry->opmode_notif_used = params->opmode_notif_used;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
", station flags mask: %u, station flags set: %u, "
@@ -2818,6 +2856,71 @@ TRACE_EVENT(cfg80211_stop_iface,
WIPHY_PR_ARG, WDEV_PR_ARG)
);
+TRACE_EVENT(rdev_start_radar_detection,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms),
+ TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(u32, cac_time_ms)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ __entry->cac_time_ms = cac_time_ms;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+ ", cac_time_ms=%u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+ __entry->cac_time_ms)
+);
+
+TRACE_EVENT(rdev_set_mcast_rate,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ int mcast_rate[IEEE80211_NUM_BANDS]),
+ TP_ARGS(wiphy, netdev, mcast_rate),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __array(int, mcast_rate, IEEE80211_NUM_BANDS)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ memcpy(__entry->mcast_rate, mcast_rate,
+ sizeof(int) * IEEE80211_NUM_BANDS);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", "
+ "mcast_rates [2.4GHz=0x%x, 5.2GHz=0x%x, 60GHz=0x%x]",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->mcast_rate[IEEE80211_BAND_2GHZ],
+ __entry->mcast_rate[IEEE80211_BAND_5GHZ],
+ __entry->mcast_rate[IEEE80211_BAND_60GHZ])
+);
+
+TRACE_EVENT(rdev_set_coalesce,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_coalesce *coalesce),
+ TP_ARGS(wiphy, coalesce),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, n_rules)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->n_rules = coalesce ? coalesce->n_rules : 0;
+ ),
+ TP_printk(WIPHY_PR_FMT ", n_rules=%d",
+ WIPHY_PR_ARG, __entry->n_rules)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_abort_scan,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
diff --git a/net/wireless/util.c b/net/wireless/util.c
index baf7218cec15..92770427b211 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1325,13 +1325,6 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
}
EXPORT_SYMBOL(ieee80211_ie_split_ric);
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset)
-{
- return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
-}
-EXPORT_SYMBOL(ieee80211_ie_split);
-
bool ieee80211_operating_class_to_band(u8 operating_class,
enum ieee80211_band *band)
{
@@ -1620,120 +1613,6 @@ int cfg80211_check_combinations(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_check_combinations);
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- enum nl80211_iftype iftype,
- struct ieee80211_channel *chan,
- enum cfg80211_chan_mode chanmode,
- u8 radar_detect)
-{
- struct wireless_dev *wdev_iter;
- int num[NUM_NL80211_IFTYPES];
- struct ieee80211_channel
- *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
- struct ieee80211_channel *ch;
- enum cfg80211_chan_mode chmode;
- int num_different_channels = 0;
- int total = 1;
- int i;
-
- ASSERT_RTNL();
-
- if (WARN_ON(hweight32(radar_detect) > 1))
- return -EINVAL;
-
- if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
- return -EINVAL;
-
- /* Always allow software iftypes */
- if (rdev->wiphy.software_iftypes & BIT(iftype)) {
- if (radar_detect)
- return -EINVAL;
- return 0;
- }
-
- memset(num, 0, sizeof(num));
- memset(used_channels, 0, sizeof(used_channels));
-
- num[iftype] = 1;
-
- /* TODO: We'll probably not need this anymore, since this
- * should only be called with CHAN_MODE_UNDEFINED. There are
- * still a couple of pending calls where other chanmodes are
- * used, but we should get rid of them.
- */
- switch (chanmode) {
- case CHAN_MODE_UNDEFINED:
- break;
- case CHAN_MODE_SHARED:
- WARN_ON(!chan);
- used_channels[0] = chan;
- num_different_channels++;
- break;
- case CHAN_MODE_EXCLUSIVE:
- num_different_channels++;
- break;
- }
-
- list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
- if (wdev_iter == wdev)
- continue;
- if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) {
- if (!wdev_iter->p2p_started)
- continue;
- } else if (wdev_iter->netdev) {
- if (!netif_running(wdev_iter->netdev))
- continue;
- } else {
- WARN_ON(1);
- }
-
- if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
- continue;
-
- /*
- * We may be holding the "wdev" mutex, but now need to lock
- * wdev_iter. This is OK because once we get here wdev_iter
- * is not wdev (tested above), but we need to use the nested
- * locking for lockdep.
- */
- mutex_lock_nested(&wdev_iter->mtx, 1);
- __acquire(wdev_iter->mtx);
- cfg80211_get_chan_state(wdev_iter, &ch, &chmode, &radar_detect);
- wdev_unlock(wdev_iter);
-
- switch (chmode) {
- case CHAN_MODE_UNDEFINED:
- break;
- case CHAN_MODE_SHARED:
- for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++)
- if (!used_channels[i] || used_channels[i] == ch)
- break;
-
- if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS)
- return -EBUSY;
-
- if (used_channels[i] == NULL) {
- used_channels[i] = ch;
- num_different_channels++;
- }
- break;
- case CHAN_MODE_EXCLUSIVE:
- num_different_channels++;
- break;
- }
-
- num[wdev_iter->iftype]++;
- total++;
- }
-
- if (total == 1 && !radar_detect)
- return 0;
-
- return cfg80211_check_combinations(&rdev->wiphy, num_different_channels,
- radar_detect, num);
-}
-
int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
const u8 *rates, unsigned int n_rates,
u32 *mask)
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 00326629d4af..6fb23366b258 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -1,3 +1,4 @@
socket
psock_fanout
psock_tpacket
+reuseport_bpf
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index fac4782c51d8..41449b5ad0a9 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -4,7 +4,7 @@ CFLAGS = -Wall -O2 -g
CFLAGS += -I../../../../usr/include/
-NET_PROGS = socket psock_fanout psock_tpacket
+NET_PROGS = socket psock_fanout psock_tpacket reuseport_bpf
all: $(NET_PROGS)
%: %.c
diff --git a/tools/testing/selftests/net/reuseport_bpf.c b/tools/testing/selftests/net/reuseport_bpf.c
new file mode 100644
index 000000000000..bec1b5dd2530
--- /dev/null
+++ b/tools/testing/selftests/net/reuseport_bpf.c
@@ -0,0 +1,514 @@
+/*
+ * Test functionality of BPF filters for SO_REUSEPORT. The tests below will use
+ * a BPF program (both classic and extended) to read the first word from an
+ * incoming packet (expected to be in network byte-order), calculate a modulus
+ * of that number, and then dispatch the packet to the Nth socket using the
+ * result. These tests are run for each supported address family and protocol.
+ * Additionally, a few edge cases in the implementation are tested.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/unistd.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+struct test_params {
+ int recv_family;
+ int send_family;
+ int protocol;
+ size_t recv_socks;
+ uint16_t recv_port;
+ uint16_t send_port_min;
+};
+
+static size_t sockaddr_size(void)
+{
+ return sizeof(struct sockaddr_storage);
+}
+
+static struct sockaddr *new_any_sockaddr(int family, uint16_t port)
+{
+ struct sockaddr_storage *addr;
+ struct sockaddr_in *addr4;
+ struct sockaddr_in6 *addr6;
+
+ addr = malloc(sizeof(struct sockaddr_storage));
+ memset(addr, 0, sizeof(struct sockaddr_storage));
+
+ switch (family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ addr4->sin_family = AF_INET;
+ addr4->sin_addr.s_addr = htonl(INADDR_ANY);
+ addr4->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)addr;
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_addr = in6addr_any;
+ addr6->sin6_port = htons(port);
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ }
+ return (struct sockaddr *)addr;
+}
+
+static struct sockaddr *new_loopback_sockaddr(int family, uint16_t port)
+{
+ struct sockaddr *addr = new_any_sockaddr(family, port);
+ struct sockaddr_in *addr4;
+ struct sockaddr_in6 *addr6;
+
+ switch (family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)addr;
+ addr6->sin6_addr = in6addr_loopback;
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ }
+ return addr;
+}
+
+static void attach_ebpf(int fd, uint16_t mod)
+{
+ static char bpf_log_buf[65536];
+ static const char bpf_license[] = "GPL";
+
+ int bpf_fd;
+ const struct bpf_insn prog[] = {
+ /* BPF_MOV64_REG(BPF_REG_6, BPF_REG_1) */
+ { BPF_ALU64 | BPF_MOV | BPF_X, BPF_REG_6, BPF_REG_1, 0, 0 },
+ /* BPF_LD_ABS(BPF_W, 0) R0 = (uint32_t)skb[0] */
+ { BPF_LD | BPF_ABS | BPF_W, 0, 0, 0, 0 },
+ /* BPF_ALU64_IMM(BPF_MOD, BPF_REG_0, mod) */
+ { BPF_ALU64 | BPF_MOD | BPF_K, BPF_REG_0, 0, 0, mod },
+ /* BPF_EXIT_INSN() */
+ { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 }
+ };
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ attr.insn_cnt = ARRAY_SIZE(prog);
+ attr.insns = (uint64_t)prog;
+ attr.license = (uint64_t)bpf_license;
+ attr.log_buf = (uint64_t)bpf_log_buf;
+ attr.log_size = sizeof(bpf_log_buf);
+ attr.log_level = 1;
+ attr.kern_version = 0;
+
+ bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (bpf_fd < 0)
+ error(1, errno, "ebpf error. log:\n%s\n", bpf_log_buf);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd,
+ sizeof(bpf_fd)))
+ error(1, errno, "failed to set SO_ATTACH_REUSEPORT_EBPF");
+
+ close(bpf_fd);
+}
+
+static void attach_cbpf(int fd, uint16_t mod)
+{
+ struct sock_filter code[] = {
+ /* A = (uint32_t)skb[0] */
+ { BPF_LD | BPF_W | BPF_ABS, 0, 0, 0 },
+ /* A = A % mod */
+ { BPF_ALU | BPF_MOD, 0, 0, mod },
+ /* return A */
+ { BPF_RET | BPF_A, 0, 0, 0 },
+ };
+ struct sock_fprog p = {
+ .len = ARRAY_SIZE(code),
+ .filter = code,
+ };
+
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p)))
+ error(1, errno, "failed to set SO_ATTACH_REUSEPORT_CBPF");
+}
+
+static void build_recv_group(const struct test_params p, int fd[], uint16_t mod,
+ void (*attach_bpf)(int, uint16_t))
+{
+ struct sockaddr * const addr =
+ new_any_sockaddr(p.recv_family, p.recv_port);
+ int i, opt;
+
+ for (i = 0; i < p.recv_socks; ++i) {
+ fd[i] = socket(p.recv_family, p.protocol, 0);
+ if (fd[i] < 0)
+ error(1, errno, "failed to create recv %d", i);
+
+ opt = 1;
+ if (setsockopt(fd[i], SOL_SOCKET, SO_REUSEPORT, &opt,
+ sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT on %d", i);
+
+ if (i == 0)
+ attach_bpf(fd[i], mod);
+
+ if (bind(fd[i], addr, sockaddr_size()))
+ error(1, errno, "failed to bind recv socket %d", i);
+
+ if (p.protocol == SOCK_STREAM)
+ if (listen(fd[i], p.recv_socks * 10))
+ error(1, errno, "failed to listen on socket");
+ }
+ free(addr);
+}
+
+static void send_from(struct test_params p, uint16_t sport, char *buf,
+ size_t len)
+{
+ struct sockaddr * const saddr = new_any_sockaddr(p.send_family, sport);
+ struct sockaddr * const daddr =
+ new_loopback_sockaddr(p.send_family, p.recv_port);
+ const int fd = socket(p.send_family, p.protocol, 0);
+
+ if (fd < 0)
+ error(1, errno, "failed to create send socket");
+
+ if (bind(fd, saddr, sockaddr_size()))
+ error(1, errno, "failed to bind send socket");
+ if (connect(fd, daddr, sockaddr_size()))
+ error(1, errno, "failed to connect");
+
+ if (send(fd, buf, len, 0) < 0)
+ error(1, errno, "failed to send message");
+
+ close(fd);
+ free(saddr);
+ free(daddr);
+}
+
+static void test_recv_order(const struct test_params p, int fd[], int mod)
+{
+ char recv_buf[8], send_buf[8];
+ struct msghdr msg;
+ struct iovec recv_io = { recv_buf, 8 };
+ struct epoll_event ev;
+ int epfd, conn, i, sport, expected;
+ uint32_t data, ndata;
+
+ epfd = epoll_create(1);
+ if (epfd < 0)
+ error(1, errno, "failed to create epoll");
+ for (i = 0; i < p.recv_socks; ++i) {
+ ev.events = EPOLLIN;
+ ev.data.fd = fd[i];
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i], &ev))
+ error(1, errno, "failed to register sock %d epoll", i);
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &recv_io;
+ msg.msg_iovlen = 1;
+
+ for (data = 0; data < p.recv_socks * 2; ++data) {
+ sport = p.send_port_min + data;
+ ndata = htonl(data);
+ memcpy(send_buf, &ndata, sizeof(ndata));
+ send_from(p, sport, send_buf, sizeof(ndata));
+
+ i = epoll_wait(epfd, &ev, 1, -1);
+ if (i < 0)
+ error(1, errno, "epoll wait failed");
+
+ if (p.protocol == SOCK_STREAM) {
+ conn = accept(ev.data.fd, NULL, NULL);
+ if (conn < 0)
+ error(1, errno, "error accepting");
+ i = recvmsg(conn, &msg, 0);
+ close(conn);
+ } else {
+ i = recvmsg(ev.data.fd, &msg, 0);
+ }
+ if (i < 0)
+ error(1, errno, "recvmsg error");
+ if (i != sizeof(ndata))
+ error(1, 0, "expected size %zd got %d",
+ sizeof(ndata), i);
+
+ for (i = 0; i < p.recv_socks; ++i)
+ if (ev.data.fd == fd[i])
+ break;
+ memcpy(&ndata, recv_buf, sizeof(ndata));
+ fprintf(stderr, "Socket %d: %d\n", i, ntohl(ndata));
+
+ expected = (sport % mod);
+ if (i != expected)
+ error(1, 0, "expected socket %d", expected);
+ }
+}
+
+static void test_reuseport_ebpf(const struct test_params p)
+{
+ int i, fd[p.recv_socks];
+
+ fprintf(stderr, "Testing EBPF mod %zd...\n", p.recv_socks);
+ build_recv_group(p, fd, p.recv_socks, attach_ebpf);
+ test_recv_order(p, fd, p.recv_socks);
+
+ fprintf(stderr, "Reprograming, testing mod %zd...\n", p.recv_socks / 2);
+ attach_ebpf(fd[0], p.recv_socks / 2);
+ test_recv_order(p, fd, p.recv_socks / 2);
+
+ for (i = 0; i < p.recv_socks; ++i)
+ close(fd[i]);
+}
+
+static void test_reuseport_cbpf(const struct test_params p)
+{
+ int i, fd[p.recv_socks];
+
+ fprintf(stderr, "Testing CBPF mod %zd...\n", p.recv_socks);
+ build_recv_group(p, fd, p.recv_socks, attach_cbpf);
+ test_recv_order(p, fd, p.recv_socks);
+
+ fprintf(stderr, "Reprograming, testing mod %zd...\n", p.recv_socks / 2);
+ attach_cbpf(fd[0], p.recv_socks / 2);
+ test_recv_order(p, fd, p.recv_socks / 2);
+
+ for (i = 0; i < p.recv_socks; ++i)
+ close(fd[i]);
+}
+
+static void test_extra_filter(const struct test_params p)
+{
+ struct sockaddr * const addr =
+ new_any_sockaddr(p.recv_family, p.recv_port);
+ int fd1, fd2, opt;
+
+ fprintf(stderr, "Testing too many filters...\n");
+ fd1 = socket(p.recv_family, p.protocol, 0);
+ if (fd1 < 0)
+ error(1, errno, "failed to create socket 1");
+ fd2 = socket(p.recv_family, p.protocol, 0);
+ if (fd2 < 0)
+ error(1, errno, "failed to create socket 2");
+
+ opt = 1;
+ if (setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT on socket 1");
+ if (setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT on socket 2");
+
+ attach_ebpf(fd1, 10);
+ attach_ebpf(fd2, 10);
+
+ if (bind(fd1, addr, sockaddr_size()))
+ error(1, errno, "failed to bind recv socket 1");
+
+ if (!bind(fd2, addr, sockaddr_size()) && errno != EADDRINUSE)
+ error(1, errno, "bind socket 2 should fail with EADDRINUSE");
+
+ free(addr);
+}
+
+static void test_filter_no_reuseport(const struct test_params p)
+{
+ struct sockaddr * const addr =
+ new_any_sockaddr(p.recv_family, p.recv_port);
+ const char bpf_license[] = "GPL";
+ struct bpf_insn ecode[] = {
+ { BPF_ALU64 | BPF_MOV | BPF_K, BPF_REG_0, 0, 0, 10 },
+ { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 }
+ };
+ struct sock_filter ccode[] = {{ BPF_RET | BPF_A, 0, 0, 0 }};
+ union bpf_attr eprog;
+ struct sock_fprog cprog;
+ int fd, bpf_fd;
+
+ fprintf(stderr, "Testing filters on non-SO_REUSEPORT socket...\n");
+
+ memset(&eprog, 0, sizeof(eprog));
+ eprog.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ eprog.insn_cnt = ARRAY_SIZE(ecode);
+ eprog.insns = (uint64_t)ecode;
+ eprog.license = (uint64_t)bpf_license;
+ eprog.kern_version = 0;
+
+ memset(&cprog, 0, sizeof(cprog));
+ cprog.len = ARRAY_SIZE(ccode);
+ cprog.filter = ccode;
+
+
+ bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &eprog, sizeof(eprog));
+ if (bpf_fd < 0)
+ error(1, errno, "ebpf error");
+ fd = socket(p.recv_family, p.protocol, 0);
+ if (fd < 0)
+ error(1, errno, "failed to create socket 1");
+
+ if (bind(fd, addr, sockaddr_size()))
+ error(1, errno, "failed to bind recv socket 1");
+
+ errno = 0;
+ if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd,
+ sizeof(bpf_fd)) || errno != EINVAL)
+ error(1, errno, "setsockopt should have returned EINVAL");
+
+ errno = 0;
+ if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &cprog,
+ sizeof(cprog)) || errno != EINVAL)
+ error(1, errno, "setsockopt should have returned EINVAL");
+
+ free(addr);
+}
+
+static void test_filter_without_bind(void)
+{
+ int fd1, fd2;
+
+ fprintf(stderr, "Testing filter add without bind...\n");
+ fd1 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd1 < 0)
+ error(1, errno, "failed to create socket 1");
+ fd2 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd2 < 0)
+ error(1, errno, "failed to create socket 2");
+
+ attach_ebpf(fd1, 10);
+ attach_cbpf(fd2, 10);
+
+ close(fd1);
+ close(fd2);
+}
+
+
+int main(void)
+{
+ fprintf(stderr, "---- IPv4 UDP ----\n");
+ /* NOTE: UDP socket lookups traverse a different code path when there
+ * are > 10 sockets in a group. Run the bpf test through both paths.
+ */
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8000,
+ .send_port_min = 9000});
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8000,
+ .send_port_min = 9000});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8001,
+ .send_port_min = 9020});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8001,
+ .send_port_min = 9020});
+ test_extra_filter((struct test_params) {
+ .recv_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8002});
+ test_filter_no_reuseport((struct test_params) {
+ .recv_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8008});
+
+ fprintf(stderr, "---- IPv6 UDP ----\n");
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8003,
+ .send_port_min = 9040});
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8003,
+ .send_port_min = 9040});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8004,
+ .send_port_min = 9060});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8004,
+ .send_port_min = 9060});
+ test_extra_filter((struct test_params) {
+ .recv_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8005});
+ test_filter_no_reuseport((struct test_params) {
+ .recv_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8009});
+
+ fprintf(stderr, "---- IPv6 UDP w/ mapped IPv4 ----\n");
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8006,
+ .send_port_min = 9080});
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8006,
+ .send_port_min = 9080});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8007,
+ .send_port_min = 9100});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8007,
+ .send_port_min = 9100});
+
+
+ test_filter_without_bind();
+
+ fprintf(stderr, "SUCCESS\n");
+ return 0;
+}